Skip to content

Commit

Permalink
Added the 'options' parameter to the API method 'removeItem()', in or…
Browse files Browse the repository at this point in the history
…der to allow passing metadata to the cookie to delete.

- Checks if the cookie was created on 'setItem()', or delete it if the domain or path are not valid.
  • Loading branch information
jherax committed Apr 21, 2017
1 parent 42ea67e commit 902427e
Show file tree
Hide file tree
Showing 9 changed files with 125 additions and 55 deletions.
13 changes: 11 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@

<!-- markdownlint-disable MD024 MD033 -->

## 2.1.2

### Improvements

1. Added the `options` parameter to the API method `removeItem()`, in order to allow passing metadata to the cookie to delete.
When using `"cookieStorage"` the new signature is: `instance.removeItem(key, options)`
1. Checks if the cookie was created on `setItem()`, or delete it if the domain or path are not valid.

---

## 2.1.1

### Fixes

1. [#4](https://github.com/jherax/proxy-storage/issues/4):
Removing cookies are failing when the domain or path were set in the cookie.
1. [#4](https://github.com/jherax/proxy-storage/issues/4): Removing cookies are failing when the `domain` or `path` were set in the cookie.
1. `setItem`: prevents converting strings values to JSON to avoid extra quotes.

---
Expand Down
49 changes: 31 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ $ yarn add proxy-storage
<script src="https://unpkg.com/proxy-storage/dist/proxy-storage.min.js"></script>

<!-- or from rawgit.com -->
<script src="https://cdn.rawgit.com/jherax/proxy-storage/2.1.1/dist/proxy-storage.min.js"></script>
<script src="https://cdn.rawgit.com/jherax/proxy-storage/2.1.2/dist/proxy-storage.min.js"></script>
```

In the above case, [`proxyStorage`](#api) is included as a global object
Expand Down Expand Up @@ -153,7 +153,9 @@ It inherits the following members from the `WebStorage` prototype:
<br>The `options` parameter is used only when you set `"cookieStorage"`.
Read more details [here](#handling-cookies).
- **`getItem`**`(key)`: retrieves a value by its `key` name.
- **`removeItem`**`(key)`: deletes an item from the storage.
- **`removeItem`**`(key [,options])`: deletes an item from the storage.
<br>The `options` parameter is used only when you set `"cookieStorage"`.
Read more details [here](#handling-cookies).
- **`clear`**`()`: removes all items from the storage instance.
- **`length`**: gets the number of items stored in the storage instance.

Expand Down Expand Up @@ -234,7 +236,9 @@ Each instance handles an adapter with the following API:
<br>The `options` parameter is used only when you set `"cookieStorage"`.
Read more details [here](#handling-cookies).
- **`getItem`**`(key)`: retrieves a value by its `key` name.
- **`removeItem`**`(key)`: deletes an item from the storage.
- **`removeItem`**`(key [,options])`: deletes an item from the storage.
<br>The `options` parameter is used only when you set `"cookieStorage"`.
Read more details [here](#handling-cookies).
- **`clear`**`()`: removes all items from the storage instance.
- **`length`**: gets the number of items stored in the storage instance.

Expand All @@ -253,7 +257,7 @@ const sessionStore = new WebStorage('sessionStorage');
sessionStore.setItem('character', { name: 'Mordecai' });

// store in cookies
const options = { expires: {days:1} };
const options = { expires: {days: 1} };
const cookieStore = new WebStorage('cookieStorage');
cookieStore.setItem('character', { name: 'Rigby' }, options);
```
Expand Down Expand Up @@ -341,7 +345,8 @@ cookieStore.setItem('testing3', 3, {

**Important**: Take into account that if you want to modify or remove a cookie
that was created with a specific `path` or `domain` / subdomain, you need to
explicitate the domain attribute in `setItem(key, value, options)`.
explicitate the domain attribute in the `options` when calling
`setItem(key, value, options)` or `removeItem(key, options)`.

![cookies](https://www.dropbox.com/s/wlvgm0t8xc07me1/cookies-metadata.gif?dl=1)

Expand All @@ -359,9 +364,8 @@ cookieStore.setItem('landedAnswers', 999, {
});

// remove an external cookie in a subdomain
cookieStore.setItem('optimizelyEndUserId', '', {
cookieStore.removeItem('optimizelyEndUserId', {
domain: '.healthcare.org',
expires: {days: -1}, // trick!
});
```

Expand Down Expand Up @@ -443,7 +447,7 @@ the _value_ passed and returned in each callback.
import storage, { WebStorage } from 'proxy-storage';

// adds first interceptor for 'setItem'
WebStorage.interceptors('setItem', (key, value) => {
WebStorage.interceptors('setItem', (key, value/*, options*/) => {
if (key === 'storage-test') {
// transform the 'id' property by encoding it to base64
value.id = btoa(value.id);
Expand All @@ -454,7 +458,7 @@ WebStorage.interceptors('setItem', (key, value) => {
// adds second interceptor for 'setItem'
WebStorage.interceptors('setItem', (key, value) => {
// does not apply any transformation
console.info('setItem: See the localStorage in your browser');
console.info('setItem: See the application storage in your browser');
console.log(`${key}: ${JSON.stringify(value)}`);
});

Expand All @@ -467,7 +471,7 @@ WebStorage.interceptors('getItem', (key, value) => {
return value;
});

WebStorage.interceptors('removeItem', (key) => {
WebStorage.interceptors('removeItem', (key/*, options*/) => {
console.log(`removeItem: ${key}`);
});

Expand Down Expand Up @@ -552,20 +556,29 @@ console.log('in memoryStorage', data);

## Shimming-polyfills

<!-- TODO: Add polyfill.io -->

This library is written using some of the new ES6 features, e.g.
`Object.assign()`. If your target browsers does not have support,
you can polyfill some of the ES2015 features with the next alternatives:
`Object.assign()`. If you have to support Non-standard-compliant browsers
(e.g. Internet Explorer), you can polyfill some of the ES2015 features with
the following alternatives:

The reason is because this library use some of the new features in ES5-ES6.
To overcome this problem, you may include in your project the
[es6-shim](https://github.com/paulmillr/es6-shim) script before all scripts.
**[es6-shim](https://github.com/paulmillr/es6-shim)**

```html
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.1/es6-shim.min.js"></script>
<!-- put this script FIRST, before all other scripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.35.3/es6-shim.min.js"></script>
```

**[polyfill.io](https://polyfill.io/v2/docs/)**

```html
<!-- put this script FIRST, before all other scripts -->
<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
```

[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.

## Running the project

If you want to fork or build your own, you must run this project.
Expand Down
56 changes: 40 additions & 16 deletions dist/proxy-storage.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/*! proxyStorage@v2.1.1. Jherax 2017. Visit https://github.com/jherax/proxy-storage */
/*! proxyStorage@v2.1.2. Jherax 2017. Visit https://github.com/jherax/proxy-storage */
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
Expand Down Expand Up @@ -150,7 +150,7 @@ function setProperty(obj, name, value) {

/**
* Validates if the key is not empty.
* (null, undefined either empty string)
* (null, undefined or empty string)
*
* @param {string} key: keyname of an element in the storage mechanism
* @return {void}
Expand Down Expand Up @@ -231,6 +231,15 @@ var _interceptors = {
clear: []
};

/**
* @private
*
* Keys not allowed for cookies.
*
* @type {RegExp}
*/
var bannedKeys = /^(?:expires|max-age|path|domain|secure)$/i;

/**
* @private
*
Expand Down Expand Up @@ -376,14 +385,20 @@ var WebStorage = function () {
key: 'setItem',
value: function setItem(key, value, options) {
(0, _utils.checkEmpty)(key);
var storageType = this.__storage__;
if (storageType === 'cookieStorage' && bannedKeys.test(key)) {
throw new Error('The key is a reserved word, therefore not allowed');
}
var v = executeInterceptors('setItem', key, value, options);
if (v !== undefined) value = v;
this[key] = value;
// prevents converting strings to JSON to avoid extra quotes
if (typeof value !== 'string') value = JSON.stringify(value);
// TODO: should add setTimeout for options.expires?
// TODO: prevent adding cookies when the domain or path are not valid?
_proxyMechanism.proxy[this.__storage__].setItem(key, value, options);
_proxyMechanism.proxy[storageType].setItem(key, value, options);
// checks if the cookie was created, or delete it if the domain or path are not valid
if (storageType === 'cookieStorage' && _proxyMechanism.proxy[storageType].getItem(key) === null) {
delete this[key];
}
}

/**
Expand Down Expand Up @@ -416,18 +431,19 @@ var WebStorage = function () {
* Deletes a key from the storage.
*
* @param {string} key: keyname of the storage
* @param {object} options: additional options for cookieStorage
* @return {void}
*
* @memberOf WebStorage
*/

}, {
key: 'removeItem',
value: function removeItem(key) {
value: function removeItem(key, options) {
(0, _utils.checkEmpty)(key);
executeInterceptors('removeItem', key);
executeInterceptors('removeItem', key, options);
delete this[key];
_proxyMechanism.proxy[this.__storage__].removeItem(key);
_proxyMechanism.proxy[this.__storage__].removeItem(key, options);
}

/**
Expand Down Expand Up @@ -542,11 +558,19 @@ function buildExpirationString(date) {
return expires.toUTCString();
}

// @private
var buildStringFor = function buildStringFor(key, data) {
/**
* @private
*
* Builds the string for the cookie's metadata.
*
* @param {string} key: name of the metadata
* @param {object} data: metadata of the cookie
* @return {string}
*/
function buildMetadataFor(key, data) {
if (!data[key]) return '';
return '; ' + key + '=' + data[key];
};
}

/**
* @private
Expand Down Expand Up @@ -583,9 +607,9 @@ function cookieStorage() {
if (options.domain && typeof options.domain === 'string') {
metadata.domain = options.domain.trim();
}
var expires = buildStringFor('expires', metadata);
var domain = buildStringFor('domain', metadata);
var path = buildStringFor('path', metadata);
var expires = buildMetadataFor('expires', metadata);
var domain = buildMetadataFor('domain', metadata);
var path = buildMetadataFor('path', metadata);
var cookie = key + '=' + encodeURIComponent(value) + expires + domain + path;
$cookie.set(cookie);
},
Expand All @@ -601,8 +625,8 @@ function cookieStorage() {
if (value === null) delete $cookie.data[key];
return value;
},
removeItem: function removeItem(key) {
var metadata = Object.assign({}, $cookie.data[key]);
removeItem: function removeItem(key, options) {
var metadata = Object.assign({}, $cookie.data[key], options);
metadata.expires = { days: -1 };
api.setItem(key, '', metadata);
delete $cookie.data[key];
Expand Down
4 changes: 2 additions & 2 deletions dist/proxy-storage.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/proxy-storage.min.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "proxy-storage",
"version": "2.1.1",
"version": "2.1.2",
"description": "Storage mechanism that implements the Web Storage interface",
"author": "David Rivera <jherax@gmail.com>",
"main": "dist/proxy-storage.js",
Expand Down
24 changes: 16 additions & 8 deletions src/cookie-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,19 @@ function buildExpirationString(date) {
return expires.toUTCString();
}

// @private
const buildStringFor = (key, data) => {
/**
* @private
*
* Builds the string for the cookie's metadata.
*
* @param {string} key: name of the metadata
* @param {object} data: metadata of the cookie
* @return {string}
*/
function buildMetadataFor(key, data) {
if (!data[key]) return '';
return `; ${key}=${data[key]}`;
};
}

/**
* @private
Expand Down Expand Up @@ -78,9 +86,9 @@ export default function cookieStorage() {
if (options.domain && typeof options.domain === 'string') {
metadata.domain = options.domain.trim();
}
const expires = buildStringFor('expires', metadata);
const domain = buildStringFor('domain', metadata);
const path = buildStringFor('path', metadata);
const expires = buildMetadataFor('expires', metadata);
const domain = buildMetadataFor('domain', metadata);
const path = buildMetadataFor('path', metadata);
const cookie = `${key}=${encodeURIComponent(value)}${expires}${domain}${path}`;
$cookie.set(cookie);
},
Expand All @@ -98,8 +106,8 @@ export default function cookieStorage() {
return value;
},

removeItem(key) {
const metadata = Object.assign({}, $cookie.data[key]);
removeItem(key, options) {
const metadata = Object.assign({}, $cookie.data[key], options);
metadata.expires = {days: -1};
api.setItem(key, '', metadata);
delete $cookie.data[key];
Expand Down
2 changes: 1 addition & 1 deletion src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export function setProperty(obj, name, value) {

/**
* Validates if the key is not empty.
* (null, undefined either empty string)
* (null, undefined or empty string)
*
* @param {string} key: keyname of an element in the storage mechanism
* @return {void}
Expand Down
28 changes: 22 additions & 6 deletions src/web-storage.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@ const _interceptors = {
clear: [],
};

/**
* @private
*
* Keys not allowed for cookies.
*
* @type {RegExp}
*/
const bannedKeys = /^(?:expires|max-age|path|domain|secure)$/i;

/**
* @private
*
Expand Down Expand Up @@ -159,14 +168,20 @@ class WebStorage {
*/
setItem(key, value, options) {
checkEmpty(key);
const storageType = this.__storage__;
if (storageType === 'cookieStorage' && bannedKeys.test(key)) {
throw new Error('The key is a reserved word, therefore not allowed');
}
const v = executeInterceptors('setItem', key, value, options);
if (v !== undefined) value = v;
this[key] = value;
// prevents converting strings to JSON to avoid extra quotes
if (typeof value !== 'string') value = JSON.stringify(value);
// TODO: should add setTimeout for options.expires?
// TODO: prevent adding cookies when the domain or path are not valid?
proxy[this.__storage__].setItem(key, value, options);
proxy[storageType].setItem(key, value, options);
// checks if the cookie was created, or delete it if the domain or path are not valid
if (storageType === 'cookieStorage' && proxy[storageType].getItem(key) === null) {
delete this[key];
}
}

/**
Expand Down Expand Up @@ -196,15 +211,16 @@ class WebStorage {
* Deletes a key from the storage.
*
* @param {string} key: keyname of the storage
* @param {object} options: additional options for cookieStorage
* @return {void}
*
* @memberOf WebStorage
*/
removeItem(key) {
removeItem(key, options) {
checkEmpty(key);
executeInterceptors('removeItem', key);
executeInterceptors('removeItem', key, options);
delete this[key];
proxy[this.__storage__].removeItem(key);
proxy[this.__storage__].removeItem(key, options);
}

/**
Expand Down

0 comments on commit 902427e

Please sign in to comment.