https://github.com/jherax/proxy-storage
Provides an adapter for storage mechanisms (cookies, localStorage, sessionStorage, memoryStorage) and implements the Web Storage interface
https://github.com/jherax/proxy-storage
cookie interceptor javascript json localstorage proxy-storage sessionstorage storage umd webstorage
Last synced: 5 months ago
JSON representation
Provides an adapter for storage mechanisms (cookies, localStorage, sessionStorage, memoryStorage) and implements the Web Storage interface
- Host: GitHub
- URL: https://github.com/jherax/proxy-storage
- Owner: jherax
- License: mit
- Created: 2016-10-11T01:16:44.000Z (over 9 years ago)
- Default Branch: master
- Last Pushed: 2019-11-20T23:37:56.000Z (over 6 years ago)
- Last Synced: 2025-03-24T03:37:41.311Z (12 months ago)
- Topics: cookie, interceptor, javascript, json, localstorage, proxy-storage, sessionstorage, storage, umd, webstorage
- Language: JavaScript
- Homepage:
- Size: 325 KB
- Stars: 17
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# Proxy Storage
This library manages an adapter that implements an interface similar to
[Web Storage] to normalize the API for [document.cookie] to be as
[window.localStorage] and [window.sessionStorage].
One of the advantages of this library is that the adapter stores the data
as **JSON**, allowing to save `Object` and `Array` values, which
is not the default behavior when using the native `window.localStorage`,
`window.sessionStorage` or `document.cookie` storages.
It also provides a new mechanism -- [`memoryStorage`](#storage-or-default),
that persists the data in memory (for current browser-tab), even if a forced
refresh is done on the page. It is a mimic of `sessionStorage` and it could
be used as fallback when the other storage mechanisms are not available, for
example, some browsers navigating in private mode.
Read more about [window.sessionStorage].
Another advantage with **proxy-storage** is that you can register
[interceptors](#interceptors) as functions for the prototype methods of
[WebStorage](#webstorage) class: `setItem`, `getItem`, `removeItem`, and
`clear`, giving you the ability to intercept and modify the values to read,
write, or delete.
## Content
1. [Installing the library](#installing-the-library)
1. [Including the library](#including-the-library)
1. [API: storage/default](#storage-or-default)
1. [API: WebStorage](#webstorage)
1. [Handling cookies](#handling-cookies)
1. [Looping the storage](#looping-the-storage)
1. [Clearing data](#clearing-data)
1. [Interceptors](#interceptors)
1. [API: configStorage](#configstorage)
1. [API: isAvailable](#isavailable)
1. [Polyfills](#polyfills)
## Installing the library
To include this library into your package manager with `npm` or `yarn`, run:
```shell
# with npm
$ npm install proxy-storage --save
# with yarn
$ yarn add proxy-storage
```
## Including the library
`proxy-storage` can be included directly from a CDN in your page:
```html
```
In the above case, [`proxyStorage`](#api) is included as a global object
in the browser, and you can use it like this:
### Browser
```javascript
// gets the default storage mechanism (usually localStorage)
var storage = proxyStorage.default;
// or get an specific storage mechanism
var cookieStore = new proxyStorage.WebStorage('cookieStorage');
```
As `proxy-storage` is built as [UMD] _(Universal Module Definition)_,
it can be included from module loaders such as [CommonJS], [ES2015 Imports]
or [AMD RequireJS].
### CommonJS
```javascript
// gets the default storage mechanism (usually localStorage)
var storage = require('proxy-storage').default;
// or get an specific storage mechanism
var WebStorage = require('proxy-storage').WebStorage;
var cookieStore = new WebStorage('cookieStorage');
```
### ES2015 Imports
```javascript
// gets the default storage mechanism (usually localStorage)
import storage from 'proxy-storage';
// or get some API members
import storage, { WebStorage, configStorage } from 'proxy-storage';
const cookieStore = new WebStorage('cookieStorage');
```
### AMD
```javascript
// using RequireJS
requirejs.config({
paths: {
// remove the extension .js
'proxy-storage': '/proxy-storage.min'
}
});
require(['proxy-storage'], function(proxyStorage) {
// gets the default storage mechanism (usually localStorage)
var storage = proxyStorage.default;
// or get an specific storage mechanism
var sessionStore = new proxyStorage.WebStorage('sessionStorage');
});
```
See an example with RequireJS here: http://jsfiddle.net/FdKTn/77/
[☗ Back to Index](#content)
# API
The exposed API manages an adapter that stores the data
as **JSON**, allowing to save and retrieve **primitive**,
`Object` and `Array` values, thanks to `JSON.stringify`.
It also provides a new storage mechanism called **`memoryStorage`**
which persists the data in memory (current tab in the browser), even
if a forced refresh is done on the page, as `sessionStorage` does.
The [`WebStorage`](#webstorage) class has a static member called
[`interceptors`](#interceptors) which lets you to register callback
functions upon the prototype methods `setItem`, `getItem`, `removeItem`,
and `clear`, giving you the ability to intercept and modify the values
to read, write, or delete.
This library is exported as [UMD] _(Universal Module Definition)_ and
the API contains the following members:
## storage (or default)
**_@type_ `Object`**
This is the **default** member of the library and is an instance of
[`WebStorage`](#webstorage). It inherits the following members from
the prototype:
- **`setItem`**`(key, value [,options])`: stores a `value` given a `key` name.
The `options` parameter is used only with instances of `cookieStorage`.
Read more details [here](#handling-cookies).
- **`getItem`**`(key [, noParse])`: retrieves a value by its `key` name.
If `noParse` is `true` then the value retrieved is not parsed with `JSON.parse`.
- **`removeItem`**`(key [,options])`: deletes an item from the storage.
The `options` parameter is used only with instances of `cookieStorage`.
Read more details [here](#handling-cookies).
- **`clear`**`()`: removes all items from the storage instance.
- **`length`**: gets the number of items stored in the instance.
The `storage` object is a proxy for the first storage mechanism available,
usually `localStorage`, which is established when the library is initialized.
The availability of the storage mechanisms is determined in the following order:
1. **`localStorage`**: adapter of the [window.localStorage] object.
1. **`cookieStorage`**: adapter of the [document.cookie] object, and
normalized with the [`WebStorage`](#webstorage) interface.
1. **`sessionStorage`**: adapter of the [window.sessionStorage] object.
1. **`memoryStorage`**: internal storage mechanism that can be used as
_fallback_ when none of the above mechanisms are available. The behavior
of `memoryStorage` is similar to `sessionStorage`, which let you to persist
data in the current session (browser tab)
**Important**: As the `storage` object is a proxy for the first storage
mechanism available, that means if `localStorage` is available to read and
write data, it will be used, otherwise, if `localStorage` is not available,
then `cookieStorage` will be used, and finally if none of the above are
available, `memoryStorage` will be the fallback mechanism.
**Example**
```javascript
import storage from 'proxy-storage';
// for browser: storage = proxyStorage.default;
// use the default storage mechanism, usually localStorage
storage.setItem('qwerty', [{ garbage: true, some: 'object' }]);
console.log(storage.getItem('qwerty'));
// [{ garbage: true, some: 'object' }]
console.log(storage.getItem('qwerty', true));
// '[{ "garbage": true, "some": "object" }]'
storage.setItem('persisted', true);
storage.setItem('o-really', { status: 'saved' });
console.log(`items: ${storage.length}`);
storage.removeItem('qwerty');
console.log(storage.getItem('qwerty'));
// null
// removes all data in the current storage
storage.clear();
console.log(`items: ${storage.length}`);
// items: 0
```
**ProTip**: you can override the default storage mechanism by calling
the method [configStorage.set()](#configstorage)
[☗ Back to Index](#content)
## WebStorage
**_@type_ `Class`**
This constructor mimics the [Web Storage] interface and manages an adapter
that allows saving `Object` and `Array` values as **JSON**. It also
lets you register [interceptors](#interceptors) for the methods `setItem`,
`getItem`, `removeItem` and `clear`.
This is the usage:
```javascript
var instance = new WebStorage(storageType)
```
Where **`storageType`** is a `string` that describes the type of storage
to manage. It can be one of the following values:
- `"localStorage"`
- `"cookieStorage"`
- `"sessionStorage"`
- `"memoryStorage"`
Each instance inherits the following properties:
- **`setItem`**`(key, value [,options])`: stores a `value` given a `key` name.
The `options` parameter is used only with instances of `cookieStorage`.
Read more details [here](#handling-cookies).
- **`getItem`**`(key [, noParse])`: retrieves a value by its `key` name.
If `noParse` is `true` then the value retrieved is not parsed with `JSON.parse`.
- **`removeItem`**`(key [,options])`: deletes an item from the storage.
The `options` parameter is used only with instances of `cookieStorage`.
Read more details [here](#handling-cookies).
- **`clear`**`()`: removes all items from the storage instance.
- **`length`**: gets the number of items stored in the instance.
You can create multiple instances of `WebStorage` to handle different
storage mechanisms. For example, to store data in `cookies` and also in
`sessionStorage`, you can do as follow:
```javascript
import storage, { WebStorage } from 'proxy-storage';
// for browser:
// var storage = proxyStorage.default;
// var WebStorage = proxyStorage.WebStorage;
// use the default storage mechanism, usually localStorage
storage.setItem('tv-show', { name: 'Regular Show' });
// store in sessionStorage
const sessionStore = new WebStorage('sessionStorage');
sessionStore.setItem('character', { name: 'Mordecai' });
// store in cookies
const options = { expires: {days: 1} };
const cookieStore = new WebStorage('cookieStorage');
cookieStore.setItem('character', { name: 'Rigby' }, options);
```
**Important**: If you request an instance of a storage mechanism that is not
available, you will get an instance of the first storage mechanism available,
so you can continue storing data. It is useful when you rely on a
specific storage mechanism. Let's see an example:
```javascript
import { WebStorage, isAvailable } from 'proxy-storage';
// for browser:
// var WebStorage = proxyStorage.WebStorage;
// var isAvailable = proxyStorage.isAvailable;
// let's suppose the following storage is not available
isAvailable.sessionStorage = false;
const sessionStore = new WebStorage('sessionStorage');
// sessionStorage is not available. Falling back to memoryStorage
sessionStore.setItem('ulugrun', 3.1415926);
// as sessionStorage is not available, the instance
// obtained is the fallback mechanism: memoryStorage
console.dir(sessionStore);
```
[☗ Back to Index](#content)
### Handling cookies
When you create an instance of `WebStorage` with `cookieStorage`, the
method `setItem()` receives an optional argument as the last parameter
that configures the way how the cookie is stored.
Signature of `setItem`:
```javascript
instance.setItem(key: String, value: Any, options: Object) : void
```
Where the **`options`** parameter is an `object` with the following properties:
- `domain`_`{string}`_: the domain or subdomain where the cookie will be valid.
- `path`_`{string}`_: relative path where the cookie is valid. _Default `"/"`_
- `secure`_`{boolean}`_: if provided, creates a secure cookie over HTTPS.
- `expires`_`{Date, object}`_: the cookie expiration date.
You can pass an object describing the expiration:
- `date`_`{Date}`_: if provided, this date will be applied, otherwise the
current date is used.
- `minutes`_`{number}`_: minutes to add / subtract
- `hours`_`{number}`_: hours to add / subtract
- `days`_`{number}`_: days to add / subtract
- `months`_`{number}`_: months to add / subtract
- `years`_`{number}`_: years to add / subtract
**Example**
```javascript
import { WebStorage } from 'proxy-storage';
// for browser: WebStorage = proxyStorage.WebStorage;
const cookieStore = new WebStorage('cookieStorage');
let data = {
start: new Date().toISOString(),
sessionId: 'J34H5609-SG7ND98W3',
platform: 'Linux x86_64',
};
cookieStore.setItem('activity', data, {
expires: { minutes: 30 },
});
cookieStore.setItem('testing1', true, {
secure: true,
path: '/jherax',
expires: new Date('2017/12/31'),
});
cookieStore.setItem('testing2', [1,4,7], {
domain: '.github.com',
expires: { days: 1 },
});
cookieStore.setItem('testing3', 3, {
expires: {
date: new Date('2018/03/06'),
hours: -6,
},
});
```
**Important**: Take into account that if you want to **modify** or **remove**
a cookie that was created under specific `path` or `domain` (subdomain), you
need to specify the `domain` or `path` attributes in the `options` parameter
when calling `setItem(key, value, options)` or `removeItem(key, options)`.
If you have created the cookie with **proxyStorage**, it will handle the
metadata internally, so that you can call `removeItem(key)` with no more
arguments. Otherwise you will need to provide the metadata **`path`** or
**`domain`**:

```javascript
// change the value of an external cookie in /answers
cookieStore.setItem('landedAnswers', 999, {
path: '/answers',
});
// remove an external cookie in a subdomain
cookieStore.removeItem('optimizelyEndUserId', {
domain: '.healthcare.org',
});
```
[☗ Back to Index](#content)
### Looping the storage
You can loop over the items in the `storage` instance,
but it is not recommended to rely on it because navigable
items in the `storage` instance are not synchronized with
external changes, for example, a cookie has expired, or a
cookie/localStorage element was created/deleted from another
page with the same domain/subdomain.
```javascript
const sessionStore = new WebStorage('sessionStorage');
sessionStore.setItem('test1', 1);
sessionStore.setItem('test2', 2);
// loop over the storage object (not recommended)
Object.keys(sessionStore).forEach((key) => {
console.log(key, sessionStore[key]);
});
// or this way (not recommended either)
for (let key in sessionStore) {
console.log(key, sessionStore[key]);
}
```
It is also applied not only when reading, but also when writing to storage:
```javascript
// not recommended: not synchronized with the real storage
var title = cookieStorage['title'];
var session = cookieStorage.sessionId;
cookieStorage['sessionId'] = 'E6URTG5';
// good practice: it is synchronized for external changes
var title = cookieStorage.getItem('title');
var session = cookieStorage.getItem('sessionId');
cookieStorage.setItem('sessionId', 'E6URTG5');
```
[☗ Back to Index](#content)
### Clearing data
You can use the `removeItem(key)` method to delete a specific item in
the storage instance, or use the `clear()` method to remove all items
in the storage instance, e.g.
```javascript
import { WebStorage } from 'proxy-storage';
// for browser: WebStorage = proxyStorage.WebStorage;
function clearAllStorages() {
new WebStorage('localStorage').clear();
new WebStorage('sessionStorage').clear();
new WebStorage('cookieStorage').clear();
}
```
**Important**: When handling `cookieStorage`, the method `clear()` only will
remove the cookies with no metadata or those created through **proxyStorage**.
Take into account that if you want to remove a cookie that was created from
another page, you need to set the `domain` or `path` attributes in the
`options` parameter when calling `removeItem(key, options)`.
See [handling-cookies](#handling-cookies).
[☗ Back to Index](#content)
### Interceptors
The [`WebStorage`](#webstorage) class exposes the static member `interceptors`
which lets you to register callback functions upon the prototype methods
`setItem`, `getItem`, `removeItem`, and `clear`. It is very useful when you
need to perform an action to intercept the value to read, write, or delete.
- **`WebStorage.interceptors`**`(command, action)`: adds an interceptor to
the API method.
- `command`_`{string}`_: name of the API method to intercept. It can be
`setItem`, `getItem`, `removeItem`, `clear`.
- `action`_`{function}`_: callback executed when the API method is called.
It **must** return the value in order to be processed by the API method.
**ProTip**: interceptors are registered in chain, allowing you to transform
the _value_ passed and returned in each callback.
```javascript
import storage, { WebStorage } from 'proxy-storage';
// for browser:
// var storage = proxyStorage.default;
// var WebStorage = proxyStorage.WebStorage;
// adds first interceptor for 'setItem'
WebStorage.interceptors('setItem', (key, value/*, options*/) => {
if (key === 'storage-test') {
// encodes the 'id' to base64
value.id = btoa(value.id);
return value;
}
});
// adds second interceptor for 'setItem'
WebStorage.interceptors('setItem', (key, value) => {
// does not apply any transformation
console.info('setItem: See the application storage in your browser');
console.log(`${key}: ${JSON.stringify(value)}`);
});
// adds first interceptor for 'getItem'
WebStorage.interceptors('getItem', (key, value) => {
if (key === 'storage-test') {
// decodes from base64
value.id = +atob(value.id);
}
return value;
});
WebStorage.interceptors('removeItem', (key/*, options*/) => {
console.log(`removeItem: ${key}`);
});
// uses the default storage mechanism (usually localStorage)
storage.setItem('storage-test', {id: 1040, text: 'it works!'});
let data = storage.getItem('storage-test');
console.log(data);
// storage.removeItem('storage-test');
```
[☗ Back to Index](#content)
## configStorage
**_@type_ `Object`**
Sets the [default storage](##storage-or-default) mechanism, or get the current
default storage mechanism. This object contains the following methods:
- **`get`**`()`: returns a `string` with the name of the current storage mechanism.
- **`set`**`(storageType)`: sets the default storage mechanism. `storageType`
must be one of the following strings: `"localStorage"`, `"sessionStorage"`,
`"cookieStorage"`, or `"memoryStorage"`. If the storage type provided is
not valid, it will throw an exception.
**Example**
```javascript
import storage, { configStorage } from 'proxy-storage';
// for browser:
// var storage = proxyStorage.default;
// var configStorage = proxyStorage.configStorage;
// gets the default storage mechanism
let storageName = configStorage.get();
console.log('Default:', storageName);
storage.setItem('defaultStorage', storageName);
// sets the new default storage mechanism
configStorage.set('cookieStorage');
storageName = configStorage.get();
console.log('Current:', storageName);
// if you are running in the browser,
// you MUST update the alias of the storage:
// storage = proxyStorage.default;
storage.setItem('currentStorage', storageName);
```
[☗ Back to Index](#content)
## isAvailable
**_@type_ `Object`**
Determines which storage mechanisms are available to _read, write,_
or _delete_ data.
It contains the following flags:
- **`localStorage`**: is set to `true` if the local storage is available.
- **`cookieStorage`**: is set to `true` if the document cookies are available.
- **`sessionStorage`**: is set to `true` if the session storage is available.
- **`memoryStorage`**: always is set to `true`.
**Example**
```javascript
import storage, * as proxyStorage from 'proxy-storage';
// * imports the entire module's members into proxyStorage.
const flags = proxyStorage.isAvailable;
if (!flags.sessionStorage) {
// forces the storage mechanism to memoryStorage
proxyStorage.configStorage.set('memoryStorage');
}
let data = storage.getItem('hidden-data');
if (!data) {
storage.setItem('hidden-data', {
mechanism: 'memoryStorage',
availability: 'Current page: you can refresh the page, data still remain'
});
}
console.log('in memoryStorage', data);
```
[☗ Back to Index](#content)
## Polyfills
This library is written using some of the new ES5/ES6 features. If you have
to support Non-standard-compliant browsers like Internet Explorer, you can
polyfill some of the missing features with the following alternatives:
**Using [es6-shim](https://github.com/paulmillr/es6-shim)**
```html
```
**Using [polyfill.io](https://polyfill.io/v2/docs/)**
```html
```
[Polyfill.io](https://polyfill.io/v2/docs/examples) reads the `User-Agent`
header of each request and returns the polyfills that are suitable for the
requesting browser.
If you want to request specific polyfills, you can pass a query parameter
to the url, for example:
```html
```
Read the list of available features:
[Features and Browsers Supported](https://polyfill.io/v2/docs/features/).
[☗ Back to Index](#content)
## Versioning
This projects adopts the [Semantic Versioning](http://semver.org/)
(SemVer) guidelines:
```text
..
```
Given a version number MAJOR.MINOR.PATCH, increment the:
1. MAJOR version when you make incompatible API changes.
1. MINOR version when you add functionality in a backwards-compatible manner.
1. PATCH version when you make backwards-compatible bug fixes.
[☗ Back to Index](#content)
## Issues
To report an issue and keep traceability of bug-fixes, please report to:
- https://github.com/jherax/proxy-storage/issues
## Changelog
The change history for each version is documented [here](CHANGELOG.md).
## License
This project is released under the [MIT](https://opensource.org/licenses/MIT)
license. This license applies ONLY to the source of this repository and doesn't
extend to any other distribution, or any other 3rd party libraries used in a
repository. See [LICENSE](LICENSE) file for more information.
[Web Storage]: https://developer.mozilla.org/en-US/docs/Web/API/Storage
[document.cookie]: https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie
[window.localStorage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage
[window.sessionStorage]: https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage
[UMD]: http://davidbcalhoun.com/2014/what-is-amd-commonjs-and-umd/
[CommonJS]: https://blog.risingstack.com/node-js-at-scale-module-system-commonjs-require/
[ES2015 Imports]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
[AMD RequireJS]: http://requirejs.org/docs/api.html#jsfiles