├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── index.js ├── package-lock.json ├── package.json ├── src ├── node-cookies-wrapper.js └── redux-persist-cookie-storage.js └── test └── redux-persist-cookie-storage-test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - v9 4 | script: 5 | - npm test 6 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | ## [0.1.1] - 2016-12-05 3 | ### Added 4 | - Initial release 5 | 6 | ## [0.2.0] - 2017-01-05 7 | ### Added 8 | - Add option for cookie expiration date (Thanks @40818419) 9 | 10 | ## [0.3.0] - 2017-01-18 11 | ### Added 12 | - Support custom cookie jars on server side (Thanks @modosc) 13 | 14 | ## [0.4.0] - 2017-11-15 15 | ### Added 16 | - Add options for domain and path (Thanks @tfoster) 17 | - Support redux-persist version 5 (Thanks @elgubbo) 18 | 19 | ### Changed 20 | - Require Promises to be available 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Oliver Spindler 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Redux Persist Cookie Storage Adapter 2 | 3 | [![Travis branch](https://img.shields.io/travis/abersager/redux-persist-cookie-storage/master.svg)](https://travis-ci.org/abersager/redux-persist-cookie-storage) 4 | 5 | [Redux Persist](https://github.com/rt2zz/redux-persist) storage adapter for cookies. Works in the browser and in Node.js with [cookie-parser](https://github.com/expressjs/cookie-parser) output. This makes it suitable for universal / isomorphic applications. 6 | 7 | ## Installation 8 | 9 | `npm install --save redux-persist-cookie-storage` 10 | 11 | ## Usage 12 | 13 | ### Browser 14 | 15 | #### Pure Cookie mode 16 | 17 | ```js 18 | import { persistStore, persistCombineReducers } from 'redux-persist' 19 | import { CookieStorage } from 'redux-persist-cookie-storage' 20 | import Cookies from 'cookies-js' 21 | 22 | // Cookies.defaults.domain = ... 23 | 24 | const persistConfig = { 25 | key: "root", 26 | storage: new CookieStorage(Cookies/*, options */) 27 | } 28 | 29 | const rootReducer = persistCombineReducers(persistConfig, reducers) 30 | 31 | const store = createStore(rootReducer, undefined) 32 | 33 | const persistor = persistStore(store, {}) 34 | ``` 35 | 36 | #### Bootstrap from preloaded state in window object 37 | 38 | ```js 39 | import { persistStore, persistCombineReducers } from 'redux-persist' 40 | import { CookieStorage } from 'redux-persist-cookie-storage' 41 | import Cookies from 'cookies-js' 42 | 43 | const persistConfig = { 44 | key: "root", 45 | storage: new CookieStorage(Cookies/*, options */), 46 | stateReconciler(inboundState, originalState) { 47 | // Ignore state from cookies, only use preloadedState from window object 48 | return originalState 49 | } 50 | } 51 | 52 | const rootReducer = persistCombineReducers(persistConfig, reducers) 53 | 54 | const store = createStore(rootReducer) 55 | 56 | const persistor = persistStore(store, window.PRELOADED_STATE) 57 | ``` 58 | 59 | ### Server 60 | 61 | ```js 62 | // Read-only mode: Use getStoredState method 63 | import { persistStore, getStoredState } from 'redux-persist' 64 | import { CookieStorage, NodeCookiesWrapper } from 'redux-persist-cookie-storage' 65 | import Cookies from 'cookies' 66 | 67 | const app = new Express() 68 | 69 | app.use(Cookies.express()) 70 | 71 | app.use(async (req, res) => { 72 | const cookieJar = new NodeCookiesWrapper(new Cookies(req, res)) 73 | 74 | const persistConfig = { 75 | key: "root", 76 | storage: new CookieStorage(cookieJar/*, options */), 77 | stateReconciler(inboundState, originalState) { 78 | // Ignore state from cookies, only use preloadedState from window object 79 | return originalState 80 | } 81 | } 82 | 83 | let preloadedState 84 | try { 85 | preloadedState = await getStoredState(persistConfig) 86 | } 87 | catch (e) { 88 | // getStoredState implementation fails when index storage item is not set. 89 | preloadedState = {} 90 | } 91 | 92 | const rootReducer = persistCombineReducers(persistConfig, reducers) 93 | 94 | const store = createStore(rootReducer, preloadedState) 95 | }) 96 | 97 | 98 | // Read-write mode: Create persistor 99 | import { persistStore, getStoredState } from 'redux-persist' 100 | import { CookieStorage, NodeCookiesWrapper } from 'redux-persist-cookie-storage' 101 | import Cookies from 'cookies' 102 | 103 | const configurePersistor = async (store) => { 104 | return new Promise((resolve) => { 105 | const persistor = persistStore(store, {}, () => { 106 | resolve(persistor) 107 | }) 108 | }) 109 | } 110 | 111 | const app = new Express() 112 | 113 | app.use(Cookies.express()) 114 | 115 | app.use(async (req, res) => { 116 | const cookieJar = new NodeCookiesWrapper(new Cookies(req, res)) 117 | 118 | const persistConfig = { 119 | key: "root", 120 | storage: new CookieStorage(cookieJar/*, options */), 121 | stateReconciler(inboundState, originalState) { 122 | // Ignore state from cookies, only use preloadedState from window object 123 | return originalState 124 | } 125 | } 126 | 127 | const rootReducer = persistCombineReducers(persistConfig, reducers) 128 | 129 | // Initialize store without preloaded state 130 | const store = createStore(rootReducer) 131 | 132 | // Wait until persistor has completed deserialization 133 | const persistor = await configurePersistor(store) 134 | 135 | // Force cookies to be set 136 | await persistor.flush() 137 | 138 | res.send(200, 'Done!') 139 | }) 140 | ``` 141 | 142 | ### Options 143 | 144 | ```js 145 | // By default, session cookies are used 146 | persistStore(store, { storage: new CookieStorage(Cookies) }) 147 | 148 | // Expiration time can be set via options 149 | persistStore(store, { storage: new CookieStorage(Cookies, { 150 | expiration: { 151 | 'default': 365 * 86400 // Cookies expire after one year 152 | } 153 | }) 154 | }) 155 | 156 | // Default expiration time can be overridden for specific parts of the store: 157 | persistStore(store, { storage: new CookieStorage(Cookies, { 158 | expiration: { 159 | 'default': null, // Session cookies used by default 160 | 'storeKey': 600 // State in key `storeKey` expires after 10 minutes 161 | } 162 | }) 163 | }) 164 | 165 | // Other cookie options like domain, path and secure: 166 | persistStore(store, { storage: new CookieStorage(Cookies, { 167 | setCookieOptions: { 168 | path: '/mypath' 169 | } 170 | }) 171 | }) 172 | ``` 173 | 174 | 175 | ## Development 176 | 177 | ### Running tests 178 | 179 | `npm test` 180 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports.CookieStorage = require('./src/redux-persist-cookie-storage'); 2 | module.exports.NodeCookiesWrapper = require('./src/node-cookies-wrapper'); 3 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-persist-cookie-storage", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "abab": { 8 | "version": "1.0.4", 9 | "resolved": "https://registry.npmjs.org/abab/-/abab-1.0.4.tgz", 10 | "integrity": "sha1-X6rZwsB/YN12dw9xzwJbYqY8/U4=", 11 | "dev": true 12 | }, 13 | "accepts": { 14 | "version": "1.3.5", 15 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz", 16 | "integrity": "sha1-63d99gEXI6OxTopywIBcjoZ0a9I=", 17 | "requires": { 18 | "mime-types": "2.1.18", 19 | "negotiator": "0.6.1" 20 | } 21 | }, 22 | "acorn": { 23 | "version": "5.5.3", 24 | "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.5.3.tgz", 25 | "integrity": "sha512-jd5MkIUlbbmb07nXH0DT3y7rDVtkzDi4XZOUVWAer8ajmF/DTSSbl5oNFyDOl/OXA33Bl79+ypHhl2pN20VeOQ==", 26 | "dev": true 27 | }, 28 | "acorn-globals": { 29 | "version": "4.1.0", 30 | "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.1.0.tgz", 31 | "integrity": "sha512-KjZwU26uG3u6eZcfGbTULzFcsoz6pegNKtHPksZPOUsiKo5bUmiBPa38FuHZ/Eun+XYh/JCCkS9AS3Lu4McQOQ==", 32 | "dev": true, 33 | "requires": { 34 | "acorn": "5.5.3" 35 | } 36 | }, 37 | "ajv": { 38 | "version": "5.5.2", 39 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", 40 | "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", 41 | "dev": true, 42 | "requires": { 43 | "co": "4.6.0", 44 | "fast-deep-equal": "1.1.0", 45 | "fast-json-stable-stringify": "2.0.0", 46 | "json-schema-traverse": "0.3.1" 47 | } 48 | }, 49 | "array-equal": { 50 | "version": "1.0.0", 51 | "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", 52 | "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", 53 | "dev": true 54 | }, 55 | "asn1": { 56 | "version": "0.2.3", 57 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", 58 | "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=", 59 | "dev": true 60 | }, 61 | "assert-plus": { 62 | "version": "1.0.0", 63 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", 64 | "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", 65 | "dev": true 66 | }, 67 | "assertion-error": { 68 | "version": "1.1.0", 69 | "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", 70 | "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", 71 | "dev": true 72 | }, 73 | "async-limiter": { 74 | "version": "1.0.0", 75 | "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", 76 | "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", 77 | "dev": true 78 | }, 79 | "asynckit": { 80 | "version": "0.4.0", 81 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 82 | "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", 83 | "dev": true 84 | }, 85 | "aws-sign2": { 86 | "version": "0.7.0", 87 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", 88 | "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", 89 | "dev": true 90 | }, 91 | "aws4": { 92 | "version": "1.7.0", 93 | "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.7.0.tgz", 94 | "integrity": "sha512-32NDda82rhwD9/JBCCkB+MRYDp0oSvlo2IL6rQWA10PQi7tDUM3eqMSltXmY+Oyl/7N3P3qNtAlv7X0d9bI28w==", 95 | "dev": true 96 | }, 97 | "balanced-match": { 98 | "version": "1.0.0", 99 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 100 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 101 | "dev": true 102 | }, 103 | "bcrypt-pbkdf": { 104 | "version": "1.0.1", 105 | "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", 106 | "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", 107 | "dev": true, 108 | "optional": true, 109 | "requires": { 110 | "tweetnacl": "0.14.5" 111 | } 112 | }, 113 | "brace-expansion": { 114 | "version": "1.1.11", 115 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 116 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 117 | "dev": true, 118 | "requires": { 119 | "balanced-match": "1.0.0", 120 | "concat-map": "0.0.1" 121 | } 122 | }, 123 | "browser-process-hrtime": { 124 | "version": "0.1.2", 125 | "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.2.tgz", 126 | "integrity": "sha1-Ql1opY00R/AqBKqJQYf86K+Le44=", 127 | "dev": true 128 | }, 129 | "browser-stdout": { 130 | "version": "1.3.0", 131 | "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz", 132 | "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=", 133 | "dev": true 134 | }, 135 | "caseless": { 136 | "version": "0.12.0", 137 | "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", 138 | "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", 139 | "dev": true 140 | }, 141 | "chai": { 142 | "version": "3.5.0", 143 | "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", 144 | "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", 145 | "dev": true, 146 | "requires": { 147 | "assertion-error": "1.1.0", 148 | "deep-eql": "0.1.3", 149 | "type-detect": "1.0.0" 150 | } 151 | }, 152 | "chai-spies": { 153 | "version": "0.7.1", 154 | "resolved": "https://registry.npmjs.org/chai-spies/-/chai-spies-0.7.1.tgz", 155 | "integrity": "sha1-ND2Z9RJEIS6LF+ZLk5lv97LCqbE=", 156 | "dev": true 157 | }, 158 | "co": { 159 | "version": "4.6.0", 160 | "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", 161 | "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", 162 | "dev": true 163 | }, 164 | "combined-stream": { 165 | "version": "1.0.6", 166 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", 167 | "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", 168 | "dev": true, 169 | "requires": { 170 | "delayed-stream": "1.0.0" 171 | } 172 | }, 173 | "commander": { 174 | "version": "2.9.0", 175 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.9.0.tgz", 176 | "integrity": "sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q=", 177 | "dev": true, 178 | "requires": { 179 | "graceful-readlink": "1.0.1" 180 | } 181 | }, 182 | "concat-map": { 183 | "version": "0.0.1", 184 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 185 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 186 | "dev": true 187 | }, 188 | "content-disposition": { 189 | "version": "0.5.2", 190 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz", 191 | "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=" 192 | }, 193 | "content-type": { 194 | "version": "1.0.4", 195 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", 196 | "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" 197 | }, 198 | "cookie": { 199 | "version": "0.3.1", 200 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz", 201 | "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=" 202 | }, 203 | "cookie-signature": { 204 | "version": "1.1.0", 205 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.1.0.tgz", 206 | "integrity": "sha512-Alvs19Vgq07eunykd3Xy2jF0/qSNv2u7KDbAek9H5liV1UMijbqFs5cycZvv5dVsvseT/U4H8/7/w8Koh35C4A==" 207 | }, 208 | "cookies": { 209 | "version": "0.7.1", 210 | "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.7.1.tgz", 211 | "integrity": "sha1-fIphX1SBxhq58WyDNzG8uPZjuZs=", 212 | "dev": true, 213 | "requires": { 214 | "depd": "1.1.2", 215 | "keygrip": "1.0.2" 216 | } 217 | }, 218 | "cookies-js": { 219 | "version": "1.2.3", 220 | "resolved": "https://registry.npmjs.org/cookies-js/-/cookies-js-1.2.3.tgz", 221 | "integrity": "sha1-AzFQSefFK+4/cxhqaRZ+qw3bLTE=", 222 | "dev": true 223 | }, 224 | "core-util-is": { 225 | "version": "1.0.2", 226 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 227 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 228 | "dev": true 229 | }, 230 | "cssom": { 231 | "version": "0.3.2", 232 | "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.2.tgz", 233 | "integrity": "sha1-uANhcMefB6kP8vFuIihAJ6JDhIs=", 234 | "dev": true 235 | }, 236 | "cssstyle": { 237 | "version": "0.3.1", 238 | "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-0.3.1.tgz", 239 | "integrity": "sha512-tNvaxM5blOnxanyxI6panOsnfiyLRj3HV4qjqqS45WPNS1usdYWRUQjqTEEELK73lpeP/1KoIGYUwrBn/VcECA==", 240 | "dev": true, 241 | "requires": { 242 | "cssom": "0.3.2" 243 | } 244 | }, 245 | "dashdash": { 246 | "version": "1.14.1", 247 | "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", 248 | "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", 249 | "dev": true, 250 | "requires": { 251 | "assert-plus": "1.0.0" 252 | } 253 | }, 254 | "data-urls": { 255 | "version": "1.0.0", 256 | "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.0.tgz", 257 | "integrity": "sha512-ai40PPQR0Fn1lD2PPie79CibnlMN2AYiDhwFX/rZHVsxbs5kNJSjegqXIprhouGXlRdEnfybva7kqRGnB6mypA==", 258 | "dev": true, 259 | "requires": { 260 | "abab": "1.0.4", 261 | "whatwg-mimetype": "2.1.0", 262 | "whatwg-url": "6.4.1" 263 | } 264 | }, 265 | "debug": { 266 | "version": "2.6.8", 267 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.8.tgz", 268 | "integrity": "sha1-5zFTHKLt4n0YgiJCfaF4IdaP9Pw=", 269 | "dev": true, 270 | "requires": { 271 | "ms": "2.0.0" 272 | } 273 | }, 274 | "deep-eql": { 275 | "version": "0.1.3", 276 | "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", 277 | "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", 278 | "dev": true, 279 | "requires": { 280 | "type-detect": "0.1.1" 281 | }, 282 | "dependencies": { 283 | "type-detect": { 284 | "version": "0.1.1", 285 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", 286 | "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", 287 | "dev": true 288 | } 289 | } 290 | }, 291 | "deep-is": { 292 | "version": "0.1.3", 293 | "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", 294 | "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", 295 | "dev": true 296 | }, 297 | "delayed-stream": { 298 | "version": "1.0.0", 299 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 300 | "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", 301 | "dev": true 302 | }, 303 | "depd": { 304 | "version": "1.1.2", 305 | "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", 306 | "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" 307 | }, 308 | "destroy": { 309 | "version": "1.0.4", 310 | "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", 311 | "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" 312 | }, 313 | "diff": { 314 | "version": "3.2.0", 315 | "resolved": "https://registry.npmjs.org/diff/-/diff-3.2.0.tgz", 316 | "integrity": "sha1-yc45Okt8vQsFinJck98pkCeGj/k=", 317 | "dev": true 318 | }, 319 | "domexception": { 320 | "version": "1.0.1", 321 | "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", 322 | "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", 323 | "dev": true, 324 | "requires": { 325 | "webidl-conversions": "4.0.2" 326 | } 327 | }, 328 | "ecc-jsbn": { 329 | "version": "0.1.1", 330 | "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", 331 | "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", 332 | "dev": true, 333 | "optional": true, 334 | "requires": { 335 | "jsbn": "0.1.1" 336 | } 337 | }, 338 | "ee-first": { 339 | "version": "1.1.1", 340 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 341 | "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" 342 | }, 343 | "encodeurl": { 344 | "version": "1.0.2", 345 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", 346 | "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" 347 | }, 348 | "escape-html": { 349 | "version": "1.0.3", 350 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 351 | "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" 352 | }, 353 | "escape-string-regexp": { 354 | "version": "1.0.5", 355 | "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", 356 | "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", 357 | "dev": true 358 | }, 359 | "escodegen": { 360 | "version": "1.9.1", 361 | "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.9.1.tgz", 362 | "integrity": "sha512-6hTjO1NAWkHnDk3OqQ4YrCuwwmGHL9S3nPlzBOUG/R44rda3wLNrfvQ5fkSGjyhHFKM7ALPKcKGrwvCLe0lC7Q==", 363 | "dev": true, 364 | "requires": { 365 | "esprima": "3.1.3", 366 | "estraverse": "4.2.0", 367 | "esutils": "2.0.2", 368 | "optionator": "0.8.2", 369 | "source-map": "0.6.1" 370 | } 371 | }, 372 | "esprima": { 373 | "version": "3.1.3", 374 | "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", 375 | "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", 376 | "dev": true 377 | }, 378 | "estraverse": { 379 | "version": "4.2.0", 380 | "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", 381 | "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", 382 | "dev": true 383 | }, 384 | "esutils": { 385 | "version": "2.0.2", 386 | "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", 387 | "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", 388 | "dev": true 389 | }, 390 | "etag": { 391 | "version": "1.8.1", 392 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 393 | "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" 394 | }, 395 | "extend": { 396 | "version": "3.0.1", 397 | "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", 398 | "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=", 399 | "dev": true 400 | }, 401 | "extsprintf": { 402 | "version": "1.3.0", 403 | "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", 404 | "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", 405 | "dev": true 406 | }, 407 | "fast-deep-equal": { 408 | "version": "1.1.0", 409 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", 410 | "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=", 411 | "dev": true 412 | }, 413 | "fast-json-stable-stringify": { 414 | "version": "2.0.0", 415 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", 416 | "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", 417 | "dev": true 418 | }, 419 | "fast-levenshtein": { 420 | "version": "2.0.6", 421 | "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", 422 | "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", 423 | "dev": true 424 | }, 425 | "forever-agent": { 426 | "version": "0.6.1", 427 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", 428 | "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", 429 | "dev": true 430 | }, 431 | "form-data": { 432 | "version": "2.3.2", 433 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", 434 | "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", 435 | "dev": true, 436 | "requires": { 437 | "asynckit": "0.4.0", 438 | "combined-stream": "1.0.6", 439 | "mime-types": "2.1.18" 440 | } 441 | }, 442 | "forwarded": { 443 | "version": "0.1.2", 444 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", 445 | "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" 446 | }, 447 | "fresh": { 448 | "version": "0.5.2", 449 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", 450 | "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" 451 | }, 452 | "fs.realpath": { 453 | "version": "1.0.0", 454 | "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", 455 | "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", 456 | "dev": true 457 | }, 458 | "getpass": { 459 | "version": "0.1.7", 460 | "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", 461 | "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", 462 | "dev": true, 463 | "requires": { 464 | "assert-plus": "1.0.0" 465 | } 466 | }, 467 | "glob": { 468 | "version": "7.1.1", 469 | "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", 470 | "integrity": "sha1-gFIR3wT6rxxjo2ADBs31reULLsg=", 471 | "dev": true, 472 | "requires": { 473 | "fs.realpath": "1.0.0", 474 | "inflight": "1.0.6", 475 | "inherits": "2.0.3", 476 | "minimatch": "3.0.4", 477 | "once": "1.4.0", 478 | "path-is-absolute": "1.0.1" 479 | } 480 | }, 481 | "graceful-readlink": { 482 | "version": "1.0.1", 483 | "resolved": "https://registry.npmjs.org/graceful-readlink/-/graceful-readlink-1.0.1.tgz", 484 | "integrity": "sha1-TK+tdrxi8C+gObL5Tpo906ORpyU=", 485 | "dev": true 486 | }, 487 | "growl": { 488 | "version": "1.9.2", 489 | "resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz", 490 | "integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=", 491 | "dev": true 492 | }, 493 | "har-schema": { 494 | "version": "2.0.0", 495 | "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", 496 | "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", 497 | "dev": true 498 | }, 499 | "har-validator": { 500 | "version": "5.0.3", 501 | "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", 502 | "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", 503 | "dev": true, 504 | "requires": { 505 | "ajv": "5.5.2", 506 | "har-schema": "2.0.0" 507 | } 508 | }, 509 | "has-flag": { 510 | "version": "1.0.0", 511 | "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", 512 | "integrity": "sha1-nZ55MWXOAXoA8AQYxD+UKnsdEfo=", 513 | "dev": true 514 | }, 515 | "he": { 516 | "version": "1.1.1", 517 | "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", 518 | "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", 519 | "dev": true 520 | }, 521 | "html-encoding-sniffer": { 522 | "version": "1.0.2", 523 | "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", 524 | "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", 525 | "dev": true, 526 | "requires": { 527 | "whatwg-encoding": "1.0.3" 528 | } 529 | }, 530 | "http-errors": { 531 | "version": "1.6.3", 532 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", 533 | "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=", 534 | "requires": { 535 | "depd": "1.1.2", 536 | "inherits": "2.0.3", 537 | "setprototypeof": "1.1.0", 538 | "statuses": "1.4.0" 539 | } 540 | }, 541 | "http-signature": { 542 | "version": "1.2.0", 543 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", 544 | "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", 545 | "dev": true, 546 | "requires": { 547 | "assert-plus": "1.0.0", 548 | "jsprim": "1.4.1", 549 | "sshpk": "1.14.1" 550 | } 551 | }, 552 | "iconv-lite": { 553 | "version": "0.4.19", 554 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.19.tgz", 555 | "integrity": "sha512-oTZqweIP51xaGPI4uPa56/Pri/480R+mo7SeU+YETByQNhDG55ycFyNLIgta9vXhILrxXDmF7ZGhqZIcuN0gJQ==", 556 | "dev": true 557 | }, 558 | "inflight": { 559 | "version": "1.0.6", 560 | "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", 561 | "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", 562 | "dev": true, 563 | "requires": { 564 | "once": "1.4.0", 565 | "wrappy": "1.0.2" 566 | } 567 | }, 568 | "inherits": { 569 | "version": "2.0.3", 570 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", 571 | "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" 572 | }, 573 | "ipaddr.js": { 574 | "version": "1.6.0", 575 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.6.0.tgz", 576 | "integrity": "sha1-4/o1e3c9phnybpXwSdBVxyeW+Gs=" 577 | }, 578 | "is-typedarray": { 579 | "version": "1.0.0", 580 | "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", 581 | "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", 582 | "dev": true 583 | }, 584 | "isstream": { 585 | "version": "0.1.2", 586 | "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", 587 | "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", 588 | "dev": true 589 | }, 590 | "jsbn": { 591 | "version": "0.1.1", 592 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", 593 | "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", 594 | "dev": true, 595 | "optional": true 596 | }, 597 | "jsdom": { 598 | "version": "11.11.0", 599 | "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.11.0.tgz", 600 | "integrity": "sha512-ou1VyfjwsSuWkudGxb03FotDajxAto6USAlmMZjE2lc0jCznt7sBWkhfRBRaWwbnmDqdMSTKTLT5d9sBFkkM7A==", 601 | "dev": true, 602 | "requires": { 603 | "abab": "1.0.4", 604 | "acorn": "5.5.3", 605 | "acorn-globals": "4.1.0", 606 | "array-equal": "1.0.0", 607 | "cssom": "0.3.2", 608 | "cssstyle": "0.3.1", 609 | "data-urls": "1.0.0", 610 | "domexception": "1.0.1", 611 | "escodegen": "1.9.1", 612 | "html-encoding-sniffer": "1.0.2", 613 | "left-pad": "1.3.0", 614 | "nwsapi": "2.0.0", 615 | "parse5": "4.0.0", 616 | "pn": "1.1.0", 617 | "request": "2.87.0", 618 | "request-promise-native": "1.0.5", 619 | "sax": "1.2.4", 620 | "symbol-tree": "3.2.2", 621 | "tough-cookie": "2.3.4", 622 | "w3c-hr-time": "1.0.1", 623 | "webidl-conversions": "4.0.2", 624 | "whatwg-encoding": "1.0.3", 625 | "whatwg-mimetype": "2.1.0", 626 | "whatwg-url": "6.4.1", 627 | "ws": "4.1.0", 628 | "xml-name-validator": "3.0.0" 629 | } 630 | }, 631 | "json-schema": { 632 | "version": "0.2.3", 633 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", 634 | "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", 635 | "dev": true 636 | }, 637 | "json-schema-traverse": { 638 | "version": "0.3.1", 639 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", 640 | "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=", 641 | "dev": true 642 | }, 643 | "json-stringify-safe": { 644 | "version": "5.0.1", 645 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 646 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", 647 | "dev": true 648 | }, 649 | "json3": { 650 | "version": "3.3.2", 651 | "resolved": "https://registry.npmjs.org/json3/-/json3-3.3.2.tgz", 652 | "integrity": "sha1-PAQ0dD35Pi9cQq7nsZvLSDV19OE=", 653 | "dev": true 654 | }, 655 | "jsprim": { 656 | "version": "1.4.1", 657 | "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", 658 | "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", 659 | "dev": true, 660 | "requires": { 661 | "assert-plus": "1.0.0", 662 | "extsprintf": "1.3.0", 663 | "json-schema": "0.2.3", 664 | "verror": "1.10.0" 665 | } 666 | }, 667 | "keygrip": { 668 | "version": "1.0.2", 669 | "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.0.2.tgz", 670 | "integrity": "sha1-rTKXxVcGneqLz+ek+kkbdcXd65E=", 671 | "dev": true 672 | }, 673 | "left-pad": { 674 | "version": "1.3.0", 675 | "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", 676 | "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", 677 | "dev": true 678 | }, 679 | "levn": { 680 | "version": "0.3.0", 681 | "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", 682 | "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", 683 | "dev": true, 684 | "requires": { 685 | "prelude-ls": "1.1.2", 686 | "type-check": "0.3.2" 687 | } 688 | }, 689 | "lodash": { 690 | "version": "4.17.10", 691 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz", 692 | "integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==" 693 | }, 694 | "lodash._baseassign": { 695 | "version": "3.2.0", 696 | "resolved": "https://registry.npmjs.org/lodash._baseassign/-/lodash._baseassign-3.2.0.tgz", 697 | "integrity": "sha1-jDigmVAPIVrQnlnxci/QxSv+Ck4=", 698 | "dev": true, 699 | "requires": { 700 | "lodash._basecopy": "3.0.1", 701 | "lodash.keys": "3.1.2" 702 | } 703 | }, 704 | "lodash._basecopy": { 705 | "version": "3.0.1", 706 | "resolved": "https://registry.npmjs.org/lodash._basecopy/-/lodash._basecopy-3.0.1.tgz", 707 | "integrity": "sha1-jaDmqHbPNEwK2KVIghEd08XHyjY=", 708 | "dev": true 709 | }, 710 | "lodash._basecreate": { 711 | "version": "3.0.3", 712 | "resolved": "https://registry.npmjs.org/lodash._basecreate/-/lodash._basecreate-3.0.3.tgz", 713 | "integrity": "sha1-G8ZhYU2qf8MRt9A78WgGoCE8+CE=", 714 | "dev": true 715 | }, 716 | "lodash._getnative": { 717 | "version": "3.9.1", 718 | "resolved": "https://registry.npmjs.org/lodash._getnative/-/lodash._getnative-3.9.1.tgz", 719 | "integrity": "sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U=", 720 | "dev": true 721 | }, 722 | "lodash._isiterateecall": { 723 | "version": "3.0.9", 724 | "resolved": "https://registry.npmjs.org/lodash._isiterateecall/-/lodash._isiterateecall-3.0.9.tgz", 725 | "integrity": "sha1-UgOte6Ql+uhCRg5pbbnPPmqsBXw=", 726 | "dev": true 727 | }, 728 | "lodash.create": { 729 | "version": "3.1.1", 730 | "resolved": "https://registry.npmjs.org/lodash.create/-/lodash.create-3.1.1.tgz", 731 | "integrity": "sha1-1/KEnw29p+BGgruM1yqwIkYd6+c=", 732 | "dev": true, 733 | "requires": { 734 | "lodash._baseassign": "3.2.0", 735 | "lodash._basecreate": "3.0.3", 736 | "lodash._isiterateecall": "3.0.9" 737 | } 738 | }, 739 | "lodash.isarguments": { 740 | "version": "3.1.0", 741 | "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", 742 | "integrity": "sha1-L1c9hcaiQon/AGY7SRwdM4/zRYo=", 743 | "dev": true 744 | }, 745 | "lodash.isarray": { 746 | "version": "3.0.4", 747 | "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz", 748 | "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U=", 749 | "dev": true 750 | }, 751 | "lodash.keys": { 752 | "version": "3.1.2", 753 | "resolved": "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz", 754 | "integrity": "sha1-TbwEcrFWvlCgsoaFXRvQsMZWCYo=", 755 | "dev": true, 756 | "requires": { 757 | "lodash._getnative": "3.9.1", 758 | "lodash.isarguments": "3.1.0", 759 | "lodash.isarray": "3.0.4" 760 | } 761 | }, 762 | "lodash.sortby": { 763 | "version": "4.7.0", 764 | "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", 765 | "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", 766 | "dev": true 767 | }, 768 | "media-typer": { 769 | "version": "0.3.0", 770 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", 771 | "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" 772 | }, 773 | "mime": { 774 | "version": "1.4.1", 775 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", 776 | "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==" 777 | }, 778 | "mime-db": { 779 | "version": "1.33.0", 780 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", 781 | "integrity": "sha512-BHJ/EKruNIqJf/QahvxwQZXKygOQ256myeN/Ew+THcAa5q+PjyTTMMeNQC4DZw5AwfvelsUrA6B67NKMqXDbzQ==" 782 | }, 783 | "mime-types": { 784 | "version": "2.1.18", 785 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.18.tgz", 786 | "integrity": "sha512-lc/aahn+t4/SWV/qcmumYjymLsWfN3ELhpmVuUFjgsORruuZPVSwAQryq+HHGvO/SI2KVX26bx+En+zhM8g8hQ==", 787 | "requires": { 788 | "mime-db": "1.33.0" 789 | } 790 | }, 791 | "minimatch": { 792 | "version": "3.0.4", 793 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 794 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 795 | "dev": true, 796 | "requires": { 797 | "brace-expansion": "1.1.11" 798 | } 799 | }, 800 | "minimist": { 801 | "version": "0.0.8", 802 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 803 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 804 | "dev": true 805 | }, 806 | "mkdirp": { 807 | "version": "0.5.1", 808 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 809 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 810 | "dev": true, 811 | "requires": { 812 | "minimist": "0.0.8" 813 | } 814 | }, 815 | "mocha": { 816 | "version": "3.5.3", 817 | "resolved": "https://registry.npmjs.org/mocha/-/mocha-3.5.3.tgz", 818 | "integrity": "sha512-/6na001MJWEtYxHOV1WLfsmR4YIynkUEhBwzsb+fk2qmQ3iqsi258l/Q2MWHJMImAcNpZ8DEdYAK72NHoIQ9Eg==", 819 | "dev": true, 820 | "requires": { 821 | "browser-stdout": "1.3.0", 822 | "commander": "2.9.0", 823 | "debug": "2.6.8", 824 | "diff": "3.2.0", 825 | "escape-string-regexp": "1.0.5", 826 | "glob": "7.1.1", 827 | "growl": "1.9.2", 828 | "he": "1.1.1", 829 | "json3": "3.3.2", 830 | "lodash.create": "3.1.1", 831 | "mkdirp": "0.5.1", 832 | "supports-color": "3.1.2" 833 | } 834 | }, 835 | "mock-express-request": { 836 | "version": "0.2.2", 837 | "resolved": "https://registry.npmjs.org/mock-express-request/-/mock-express-request-0.2.2.tgz", 838 | "integrity": "sha512-EymHjY1k1jWIsaVaCsPdFterWO18gcNwQMb99OryhSBtIA33SZJujOLeOe03Rf2DTV997xLPyl2I098WCFm/mA==", 839 | "requires": { 840 | "accepts": "1.3.5", 841 | "fresh": "0.5.2", 842 | "lodash": "4.17.10", 843 | "mock-req": "0.2.0", 844 | "parseurl": "1.3.2", 845 | "range-parser": "1.2.0", 846 | "type-is": "1.6.16" 847 | } 848 | }, 849 | "mock-express-response": { 850 | "version": "0.2.2", 851 | "resolved": "https://registry.npmjs.org/mock-express-response/-/mock-express-response-0.2.2.tgz", 852 | "integrity": "sha512-+pRUv25LhyKZVSRCOzoZp8TW0nqvT7UlH7vpVzCibjjEucXL72gTgwEhmqbyTO2LI9IlyIeBBdflR9Ml5KQWMQ==", 853 | "requires": { 854 | "content-disposition": "0.5.2", 855 | "content-type": "1.0.4", 856 | "cookie": "0.3.1", 857 | "cookie-signature": "1.1.0", 858 | "depd": "1.1.2", 859 | "escape-html": "1.0.3", 860 | "etag": "1.8.1", 861 | "mock-express-request": "0.2.2", 862 | "mock-res": "0.5.0", 863 | "on-finished": "2.3.0", 864 | "proxy-addr": "2.0.3", 865 | "qs": "6.5.2", 866 | "send": "0.16.2", 867 | "utils-merge": "1.0.1", 868 | "vary": "1.1.2" 869 | } 870 | }, 871 | "mock-req": { 872 | "version": "0.2.0", 873 | "resolved": "https://registry.npmjs.org/mock-req/-/mock-req-0.2.0.tgz", 874 | "integrity": "sha1-dJRGgE0sAGFpNC7nvmu6HP/VNMI=" 875 | }, 876 | "mock-res": { 877 | "version": "0.5.0", 878 | "resolved": "https://registry.npmjs.org/mock-res/-/mock-res-0.5.0.tgz", 879 | "integrity": "sha1-mDaL6wnfdT9k9m2U5VNql7NqJDA=" 880 | }, 881 | "ms": { 882 | "version": "2.0.0", 883 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", 884 | "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" 885 | }, 886 | "negotiator": { 887 | "version": "0.6.1", 888 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz", 889 | "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=" 890 | }, 891 | "nwsapi": { 892 | "version": "2.0.0", 893 | "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.0.tgz", 894 | "integrity": "sha512-9kj1oCEDNq+LHDAVPGDPg9+qRcBcpXb1IYC8q89jR8xJvOC2byQwEVsM3W1qQcSPVyzGGaXN7wZHnXORCiZl4w==", 895 | "dev": true 896 | }, 897 | "oauth-sign": { 898 | "version": "0.8.2", 899 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", 900 | "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=", 901 | "dev": true 902 | }, 903 | "on-finished": { 904 | "version": "2.3.0", 905 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", 906 | "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", 907 | "requires": { 908 | "ee-first": "1.1.1" 909 | } 910 | }, 911 | "once": { 912 | "version": "1.4.0", 913 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 914 | "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", 915 | "dev": true, 916 | "requires": { 917 | "wrappy": "1.0.2" 918 | } 919 | }, 920 | "optionator": { 921 | "version": "0.8.2", 922 | "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", 923 | "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", 924 | "dev": true, 925 | "requires": { 926 | "deep-is": "0.1.3", 927 | "fast-levenshtein": "2.0.6", 928 | "levn": "0.3.0", 929 | "prelude-ls": "1.1.2", 930 | "type-check": "0.3.2", 931 | "wordwrap": "1.0.0" 932 | } 933 | }, 934 | "parse5": { 935 | "version": "4.0.0", 936 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", 937 | "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", 938 | "dev": true 939 | }, 940 | "parseurl": { 941 | "version": "1.3.2", 942 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz", 943 | "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=" 944 | }, 945 | "path-is-absolute": { 946 | "version": "1.0.1", 947 | "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", 948 | "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", 949 | "dev": true 950 | }, 951 | "performance-now": { 952 | "version": "2.1.0", 953 | "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", 954 | "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", 955 | "dev": true 956 | }, 957 | "pn": { 958 | "version": "1.1.0", 959 | "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", 960 | "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", 961 | "dev": true 962 | }, 963 | "prelude-ls": { 964 | "version": "1.1.2", 965 | "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", 966 | "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", 967 | "dev": true 968 | }, 969 | "proxy-addr": { 970 | "version": "2.0.3", 971 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.3.tgz", 972 | "integrity": "sha512-jQTChiCJteusULxjBp8+jftSQE5Obdl3k4cnmLA6WXtK6XFuWRnvVL7aCiBqaLPM8c4ph0S4tKna8XvmIwEnXQ==", 973 | "requires": { 974 | "forwarded": "0.1.2", 975 | "ipaddr.js": "1.6.0" 976 | } 977 | }, 978 | "punycode": { 979 | "version": "2.1.1", 980 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 981 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 982 | "dev": true 983 | }, 984 | "qs": { 985 | "version": "6.5.2", 986 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", 987 | "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" 988 | }, 989 | "range-parser": { 990 | "version": "1.2.0", 991 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz", 992 | "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=" 993 | }, 994 | "request": { 995 | "version": "2.87.0", 996 | "resolved": "https://registry.npmjs.org/request/-/request-2.87.0.tgz", 997 | "integrity": "sha512-fcogkm7Az5bsS6Sl0sibkbhcKsnyon/jV1kF3ajGmF0c8HrttdKTPRT9hieOaQHA5HEq6r8OyWOo/o781C1tNw==", 998 | "dev": true, 999 | "requires": { 1000 | "aws-sign2": "0.7.0", 1001 | "aws4": "1.7.0", 1002 | "caseless": "0.12.0", 1003 | "combined-stream": "1.0.6", 1004 | "extend": "3.0.1", 1005 | "forever-agent": "0.6.1", 1006 | "form-data": "2.3.2", 1007 | "har-validator": "5.0.3", 1008 | "http-signature": "1.2.0", 1009 | "is-typedarray": "1.0.0", 1010 | "isstream": "0.1.2", 1011 | "json-stringify-safe": "5.0.1", 1012 | "mime-types": "2.1.18", 1013 | "oauth-sign": "0.8.2", 1014 | "performance-now": "2.1.0", 1015 | "qs": "6.5.2", 1016 | "safe-buffer": "5.1.2", 1017 | "tough-cookie": "2.3.4", 1018 | "tunnel-agent": "0.6.0", 1019 | "uuid": "3.2.1" 1020 | } 1021 | }, 1022 | "request-promise-core": { 1023 | "version": "1.1.1", 1024 | "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", 1025 | "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", 1026 | "dev": true, 1027 | "requires": { 1028 | "lodash": "4.17.10" 1029 | } 1030 | }, 1031 | "request-promise-native": { 1032 | "version": "1.0.5", 1033 | "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", 1034 | "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", 1035 | "dev": true, 1036 | "requires": { 1037 | "request-promise-core": "1.1.1", 1038 | "stealthy-require": "1.1.1", 1039 | "tough-cookie": "2.3.4" 1040 | } 1041 | }, 1042 | "safe-buffer": { 1043 | "version": "5.1.2", 1044 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", 1045 | "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", 1046 | "dev": true 1047 | }, 1048 | "sax": { 1049 | "version": "1.2.4", 1050 | "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", 1051 | "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", 1052 | "dev": true 1053 | }, 1054 | "send": { 1055 | "version": "0.16.2", 1056 | "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", 1057 | "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", 1058 | "requires": { 1059 | "debug": "2.6.9", 1060 | "depd": "1.1.2", 1061 | "destroy": "1.0.4", 1062 | "encodeurl": "1.0.2", 1063 | "escape-html": "1.0.3", 1064 | "etag": "1.8.1", 1065 | "fresh": "0.5.2", 1066 | "http-errors": "1.6.3", 1067 | "mime": "1.4.1", 1068 | "ms": "2.0.0", 1069 | "on-finished": "2.3.0", 1070 | "range-parser": "1.2.0", 1071 | "statuses": "1.4.0" 1072 | }, 1073 | "dependencies": { 1074 | "debug": { 1075 | "version": "2.6.9", 1076 | "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", 1077 | "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", 1078 | "requires": { 1079 | "ms": "2.0.0" 1080 | } 1081 | } 1082 | } 1083 | }, 1084 | "setprototypeof": { 1085 | "version": "1.1.0", 1086 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", 1087 | "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==" 1088 | }, 1089 | "source-map": { 1090 | "version": "0.6.1", 1091 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", 1092 | "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", 1093 | "dev": true, 1094 | "optional": true 1095 | }, 1096 | "sshpk": { 1097 | "version": "1.14.1", 1098 | "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.1.tgz", 1099 | "integrity": "sha1-Ew9Zde3a2WPx1W+SuaxsUfqfg+s=", 1100 | "dev": true, 1101 | "requires": { 1102 | "asn1": "0.2.3", 1103 | "assert-plus": "1.0.0", 1104 | "bcrypt-pbkdf": "1.0.1", 1105 | "dashdash": "1.14.1", 1106 | "ecc-jsbn": "0.1.1", 1107 | "getpass": "0.1.7", 1108 | "jsbn": "0.1.1", 1109 | "tweetnacl": "0.14.5" 1110 | } 1111 | }, 1112 | "statuses": { 1113 | "version": "1.4.0", 1114 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", 1115 | "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==" 1116 | }, 1117 | "stealthy-require": { 1118 | "version": "1.1.1", 1119 | "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", 1120 | "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", 1121 | "dev": true 1122 | }, 1123 | "supports-color": { 1124 | "version": "3.1.2", 1125 | "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.1.2.tgz", 1126 | "integrity": "sha1-cqJiiU2dQIuVbKBf83su2KbiotU=", 1127 | "dev": true, 1128 | "requires": { 1129 | "has-flag": "1.0.0" 1130 | } 1131 | }, 1132 | "symbol-tree": { 1133 | "version": "3.2.2", 1134 | "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", 1135 | "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", 1136 | "dev": true 1137 | }, 1138 | "tough-cookie": { 1139 | "version": "2.3.4", 1140 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", 1141 | "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", 1142 | "dev": true, 1143 | "requires": { 1144 | "punycode": "1.4.1" 1145 | }, 1146 | "dependencies": { 1147 | "punycode": { 1148 | "version": "1.4.1", 1149 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", 1150 | "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", 1151 | "dev": true 1152 | } 1153 | } 1154 | }, 1155 | "tr46": { 1156 | "version": "1.0.1", 1157 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", 1158 | "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", 1159 | "dev": true, 1160 | "requires": { 1161 | "punycode": "2.1.1" 1162 | } 1163 | }, 1164 | "tunnel-agent": { 1165 | "version": "0.6.0", 1166 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", 1167 | "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", 1168 | "dev": true, 1169 | "requires": { 1170 | "safe-buffer": "5.1.2" 1171 | } 1172 | }, 1173 | "tweetnacl": { 1174 | "version": "0.14.5", 1175 | "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", 1176 | "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", 1177 | "dev": true, 1178 | "optional": true 1179 | }, 1180 | "type-check": { 1181 | "version": "0.3.2", 1182 | "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", 1183 | "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", 1184 | "dev": true, 1185 | "requires": { 1186 | "prelude-ls": "1.1.2" 1187 | } 1188 | }, 1189 | "type-detect": { 1190 | "version": "1.0.0", 1191 | "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", 1192 | "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", 1193 | "dev": true 1194 | }, 1195 | "type-is": { 1196 | "version": "1.6.16", 1197 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz", 1198 | "integrity": "sha512-HRkVv/5qY2G6I8iab9cI7v1bOIdhm94dVjQCPFElW9W+3GeDOSHmy2EBYe4VTApuzolPcmgFTN3ftVJRKR2J9Q==", 1199 | "requires": { 1200 | "media-typer": "0.3.0", 1201 | "mime-types": "2.1.18" 1202 | } 1203 | }, 1204 | "utils-merge": { 1205 | "version": "1.0.1", 1206 | "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", 1207 | "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" 1208 | }, 1209 | "uuid": { 1210 | "version": "3.2.1", 1211 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", 1212 | "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==", 1213 | "dev": true 1214 | }, 1215 | "vary": { 1216 | "version": "1.1.2", 1217 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 1218 | "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" 1219 | }, 1220 | "verror": { 1221 | "version": "1.10.0", 1222 | "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", 1223 | "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", 1224 | "dev": true, 1225 | "requires": { 1226 | "assert-plus": "1.0.0", 1227 | "core-util-is": "1.0.2", 1228 | "extsprintf": "1.3.0" 1229 | } 1230 | }, 1231 | "w3c-hr-time": { 1232 | "version": "1.0.1", 1233 | "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", 1234 | "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", 1235 | "dev": true, 1236 | "requires": { 1237 | "browser-process-hrtime": "0.1.2" 1238 | } 1239 | }, 1240 | "webidl-conversions": { 1241 | "version": "4.0.2", 1242 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", 1243 | "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", 1244 | "dev": true 1245 | }, 1246 | "whatwg-encoding": { 1247 | "version": "1.0.3", 1248 | "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.3.tgz", 1249 | "integrity": "sha512-jLBwwKUhi8WtBfsMQlL4bUUcT8sMkAtQinscJAe/M4KHCkHuUJAF6vuB0tueNIw4c8ziO6AkRmgY+jL3a0iiPw==", 1250 | "dev": true, 1251 | "requires": { 1252 | "iconv-lite": "0.4.19" 1253 | } 1254 | }, 1255 | "whatwg-mimetype": { 1256 | "version": "2.1.0", 1257 | "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz", 1258 | "integrity": "sha512-FKxhYLytBQiUKjkYteN71fAUA3g6KpNXoho1isLiLSB3N1G4F35Q5vUxWfKFhBwi5IWF27VE6WxhrnnC+m0Mew==", 1259 | "dev": true 1260 | }, 1261 | "whatwg-url": { 1262 | "version": "6.4.1", 1263 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.4.1.tgz", 1264 | "integrity": "sha512-FwygsxsXx27x6XXuExA/ox3Ktwcbf+OAvrKmLulotDAiO1Q6ixchPFaHYsis2zZBZSJTR0+dR+JVtf7MlbqZjw==", 1265 | "dev": true, 1266 | "requires": { 1267 | "lodash.sortby": "4.7.0", 1268 | "tr46": "1.0.1", 1269 | "webidl-conversions": "4.0.2" 1270 | } 1271 | }, 1272 | "wordwrap": { 1273 | "version": "1.0.0", 1274 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", 1275 | "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", 1276 | "dev": true 1277 | }, 1278 | "wrappy": { 1279 | "version": "1.0.2", 1280 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 1281 | "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", 1282 | "dev": true 1283 | }, 1284 | "ws": { 1285 | "version": "4.1.0", 1286 | "resolved": "https://registry.npmjs.org/ws/-/ws-4.1.0.tgz", 1287 | "integrity": "sha512-ZGh/8kF9rrRNffkLFV4AzhvooEclrOH0xaugmqGsIfFgOE/pIz4fMc4Ef+5HSQqTEug2S9JZIWDR47duDSLfaA==", 1288 | "dev": true, 1289 | "requires": { 1290 | "async-limiter": "1.0.0", 1291 | "safe-buffer": "5.1.2" 1292 | } 1293 | }, 1294 | "xml-name-validator": { 1295 | "version": "3.0.0", 1296 | "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", 1297 | "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", 1298 | "dev": true 1299 | } 1300 | } 1301 | } 1302 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-persist-cookie-storage", 3 | "version": "1.0.0", 4 | "description": "redux-persist storage adapter for cookies", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test --timeout 15000" 8 | }, 9 | "author": "Oliver Spindler ", 10 | "license": "MIT", 11 | "repository": "abersager/redux-persist-cookie-storage", 12 | "files": [ 13 | "src", 14 | "index.js" 15 | ], 16 | "devDependencies": { 17 | "chai": "^3.5.0", 18 | "chai-spies": "^0.7.1", 19 | "cookies-js": "^1.2.3", 20 | "cookies": "^0.7.1", 21 | "jsdom": "^11.11.0", 22 | "mocha": "^3.0.2" 23 | }, 24 | "dependencies": { 25 | "mock-express-request": "^0.2.2", 26 | "mock-express-response": "^0.2.2" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/node-cookies-wrapper.js: -------------------------------------------------------------------------------- 1 | /* 2 | Wraps instance of Node.js cookie implementation [1] to match API of Cookies.js 3 | browser library [2]. 4 | 5 | [1] https://github.com/pillarjs/cookies 6 | [2] https://github.com/ScottHamper/Cookies 7 | */ 8 | 9 | function NodeCookiesWrapper(cookies) { 10 | this.cookies = cookies; 11 | } 12 | 13 | NodeCookiesWrapper.prototype.get = function (key) { 14 | key = encodeKey(key); 15 | var encodedValue = this.cookies.get(key); 16 | if (encodedValue) { 17 | return decodeURIComponent(encodedValue); 18 | } 19 | return undefined; 20 | } 21 | 22 | NodeCookiesWrapper.prototype.set = function (key, value, options) { 23 | key = encodeKey(key); 24 | value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent); 25 | this.cookies.set(key, value, options); 26 | } 27 | 28 | NodeCookiesWrapper.prototype.expire = function (key) { 29 | this.set(key, undefined); 30 | } 31 | 32 | function encodeKey (key) { 33 | key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent); 34 | key = key.replace(/\(/g, '%28').replace(/\)/g, '%29'); 35 | return key; 36 | } 37 | 38 | module.exports = NodeCookiesWrapper; 39 | -------------------------------------------------------------------------------- /src/redux-persist-cookie-storage.js: -------------------------------------------------------------------------------- 1 | function CookieStorage(cookies, options) { 2 | options = options || {}; 3 | 4 | this.cookies = cookies; 5 | 6 | this.keyPrefix = options.keyPrefix || ''; 7 | this.indexKey = options.indexKey || 'reduxPersistIndex'; 8 | this.expiration = options.expiration || {}; 9 | if (!this.expiration.default) { 10 | this.expiration.default = null; 11 | } 12 | 13 | this.setCookieOptions = options.setCookieOptions; 14 | } 15 | 16 | CookieStorage.prototype.getItem = function (key, callback) { 17 | var item = this.cookies.get(this.keyPrefix + key) || null; 18 | if (callback) { 19 | callback(null, item); 20 | } 21 | return Promise.resolve(item); 22 | } 23 | 24 | CookieStorage.prototype.setItem = function (key, value, callback) { 25 | var options = Object.assign({}, this.setCookieOptions); 26 | 27 | var expires = this.expiration.default; 28 | if (typeof this.expiration[key] !== 'undefined') { 29 | expires = this.expiration[key] 30 | } 31 | if (expires) { 32 | options["expires"] = expires; 33 | } 34 | 35 | this.cookies.set(this.keyPrefix + key, value, options); 36 | 37 | // Update key index 38 | var indexOptions = Object.assign({}, this.setCookieOptions); 39 | if (this.expiration.default) { 40 | indexOptions["expires"] = this.expiration.default; 41 | } 42 | 43 | return this.getAllKeys().then(function(allKeys) { 44 | if (allKeys.indexOf(key) === -1) { 45 | allKeys.push(key); 46 | this.cookies.set(this.indexKey, JSON.stringify(allKeys), indexOptions); 47 | } 48 | if (callback) { 49 | callback(null); 50 | } 51 | return Promise.resolve(null); 52 | }.bind(this)); 53 | } 54 | 55 | CookieStorage.prototype.removeItem = function (key, callback) { 56 | this.cookies.expire(this.keyPrefix + key); 57 | 58 | return this.getAllKeys().then(function (allKeys) { 59 | allKeys = allKeys.filter(function (k) { 60 | return k !== key; 61 | }); 62 | 63 | this.cookies.set(this.indexKey, JSON.stringify(allKeys)); 64 | if (callback) { 65 | callback(null); 66 | } 67 | return Promise.resolve(null); 68 | }.bind(this)); 69 | } 70 | 71 | CookieStorage.prototype.getAllKeys = function (callback) { 72 | var cookie = this.cookies.get(this.indexKey); 73 | 74 | var result = []; 75 | if (cookie) { 76 | result = JSON.parse(cookie); 77 | } 78 | 79 | if (callback) { 80 | callback(null, result); 81 | } 82 | return Promise.resolve(result); 83 | } 84 | 85 | module.exports = CookieStorage 86 | -------------------------------------------------------------------------------- /test/redux-persist-cookie-storage-test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai') 2 | const spies = require('chai-spies') 3 | const CookiesJS = require('cookies-js') 4 | const Cookies = require('cookies') 5 | const JSDOM = require('jsdom').JSDOM 6 | const MockExpressRequest = require('mock-express-request') 7 | const MockExpressResponse = require('mock-express-response') 8 | 9 | const CookieStorage = require('../src/redux-persist-cookie-storage') 10 | const NodeCookiesWrapper = require('../src/node-cookies-wrapper') 11 | 12 | chai.use(spies) 13 | const expect = chai.expect 14 | 15 | function delay (milliseconds) { 16 | return new Promise((resolve) => { 17 | setTimeout(() => resolve(), milliseconds) 18 | }) 19 | } 20 | 21 | function isSpy(cookies) { 22 | return cookies && typeof cookies['get'] === 'function' && '__spy' in cookies['get'] 23 | } 24 | 25 | describe('CookieStorage', () => { 26 | describe('browser behaviour', () => { 27 | describe('setItem', () => { 28 | it('stores item as session cookies by default', async () => { 29 | const Cookies = CookiesJS(new JSDOM(``).window) 30 | chai.spy.on(Cookies, 'set') 31 | 32 | const storage = new CookieStorage(Cookies, { expiration: { default: null } }) 33 | 34 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 35 | 36 | expect(Cookies.set).to.have.been.called.with('test', '{"foo":"bar"}', ) 37 | expect(Cookies.set).to.have.been.called.with('reduxPersistIndex', '["test"]', ) 38 | }) 39 | 40 | it('stores item as session cookies by default with custom key prefix', async () => { 41 | const Cookies = CookiesJS(new JSDOM(``).window) 42 | chai.spy.on(Cookies, 'set') 43 | 44 | const storage = new CookieStorage(Cookies, { keyPrefix: 'prefix', expiration: { default: null } }) 45 | 46 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 47 | 48 | expect(Cookies.set).to.have.been.called.with('prefixtest', '{"foo":"bar"}', {}) 49 | expect(Cookies.set).to.have.been.called.with('reduxPersistIndex', '["test"]', {}) 50 | }) 51 | 52 | it('stores an item as a cookie with expiration time', async () => { 53 | const Cookies = CookiesJS(new JSDOM(``).window) 54 | chai.spy.on(Cookies, 'set') 55 | 56 | const storage = new CookieStorage(Cookies, { expiration: { default: 1 } }) 57 | 58 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 59 | 60 | expect(Cookies.set).to.have.been.called.with('test', '{"foo":"bar"}', { expires: 1 }) 61 | expect(Cookies.set).to.have.been.called.with('reduxPersistIndex', '["test"]', { expires: 1 }) 62 | }) 63 | 64 | it('stores an item as a cookie with expiration time and custom key prefix', async () => { 65 | const Cookies = CookiesJS(new JSDOM(``).window) 66 | chai.spy.on(Cookies, 'set') 67 | 68 | const storage = new CookieStorage(Cookies, { keyPrefix: 'prefix', expiration: { default: 1 } }) 69 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 70 | 71 | expect(Cookies.set).to.have.been.called.with('prefixtest', '{"foo":"bar"}', { expires: 1 }) 72 | expect(Cookies.set).to.have.been.called.with('reduxPersistIndex', '["test"]', { expires: 1 }) 73 | }) 74 | 75 | it('stores an item with custom expiration overriding default time', async () => { 76 | const Cookies = CookiesJS(new JSDOM(``).window) 77 | chai.spy.on(Cookies, 'set') 78 | 79 | const storage = new CookieStorage(Cookies, { expiration: { 80 | default: 3, 81 | test: 1 82 | } }) 83 | 84 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 85 | await storage.setItem('test2', JSON.stringify({ foo: 'bar' })) 86 | const keys = await storage.getAllKeys() 87 | 88 | expect(Cookies.set).to.have.been.called.with('test', '{"foo":"bar"}', { expires: 1 }) 89 | expect(Cookies.set).to.have.been.called.with('test2', '{"foo":"bar"}', { expires: 3 }) 90 | expect(Cookies.set).to.have.been.called.with('reduxPersistIndex', '["test","test2"]', { expires: 3 }) 91 | }) 92 | 93 | it('stores an item with custom expiration overriding default session option', async () => { 94 | const Cookies = CookiesJS(new JSDOM(``).window) 95 | chai.spy.on(Cookies, 'set') 96 | 97 | const storage = new CookieStorage(Cookies, { expiration: { 98 | default: null, 99 | test: 1 100 | } }) 101 | 102 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 103 | 104 | expect(Cookies.set).to.have.been.called.with('test', '{"foo":"bar"}', { expires: 1 }) 105 | expect(Cookies.set).to.have.been.called.with('reduxPersistIndex', '["test"]', {}) 106 | }) 107 | 108 | it('stores an item as a session cookie overriding default time', async () => { 109 | const Cookies = CookiesJS(new JSDOM(``).window) 110 | chai.spy.on(Cookies, 'set') 111 | 112 | const storage = new CookieStorage(Cookies, { expiration: { 113 | default: 3, 114 | test: null 115 | } }) 116 | 117 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 118 | 119 | expect(Cookies.set).to.have.been.called.with('test', '{"foo":"bar"}', {}) 120 | expect(Cookies.set).to.have.been.called.with('reduxPersistIndex', '["test"]', { expires: 3 }) 121 | }) 122 | 123 | it('stores an item as a session cookie in specified domain', async () => { 124 | const Cookies = CookiesJS(new JSDOM(``).window) 125 | chai.spy.on(Cookies, 'set') 126 | 127 | const storage = new CookieStorage(Cookies, { 128 | setCookieOptions: { domain: 'example.com' } 129 | }) 130 | 131 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 132 | 133 | expect(Cookies.set).to.have.been.called.with('test', '{"foo":"bar"}', { domain: 'example.com' }) 134 | expect(Cookies.set).to.have.been.called.with('reduxPersistIndex', '["test"]', { domain: 'example.com' }) 135 | }) 136 | 137 | it('stores an item as a session cookie in specified path', async () => { 138 | const Cookies = CookiesJS(new JSDOM(``).window) 139 | chai.spy.on(Cookies, 'set') 140 | 141 | const storage = new CookieStorage(Cookies, { 142 | setCookieOptions: { path: '/123' } 143 | }) 144 | 145 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 146 | 147 | expect(Cookies.set).to.have.been.called.with('test', '{"foo":"bar"}', { path: '/123' }) 148 | expect(Cookies.set).to.have.been.called.with('reduxPersistIndex', '["test"]', { path: '/123' }) 149 | }) 150 | 151 | it('updates the list of keys', async () => { 152 | const Cookies = CookiesJS(new JSDOM(``).window) 153 | chai.spy.on(Cookies, 'set') 154 | 155 | const storage = new CookieStorage(Cookies, { expiration: { default: null } }) 156 | 157 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 158 | await storage.setItem('test2', JSON.stringify({ foo: 'bar' })) 159 | const keys = await storage.getAllKeys() 160 | expect(keys).to.eql(['test', 'test2']) 161 | }) 162 | 163 | it('stores list of keys with expiration time', async () => { 164 | const Cookies = CookiesJS(new JSDOM(``).window) 165 | 166 | const storage = new CookieStorage(Cookies, { expiration: { 167 | default: 1 168 | } }) 169 | 170 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 171 | let keys = await storage.getAllKeys() 172 | expect(keys).to.eql(['test']) 173 | 174 | await delay(1000) 175 | keys = await storage.getAllKeys() 176 | expect(keys).to.eql([]) 177 | }) 178 | }) 179 | 180 | describe('getItem', () => { 181 | it('gets an item stored as cookie', (done) => { 182 | const Cookies = CookiesJS(new JSDOM(``).window) 183 | 184 | const storage = new CookieStorage(Cookies) 185 | 186 | storage.cookies.set('test', JSON.stringify({ foo: 'bar' })) 187 | 188 | storage.getItem('test', (error, result) => { 189 | expect(JSON.parse(result)).to.eql({ foo: 'bar' }) 190 | done() 191 | }) 192 | }) 193 | 194 | it('gets an item stored as cookie and returns a promise', async () => { 195 | const Cookies = CookiesJS(new JSDOM(``).window) 196 | 197 | const storage = new CookieStorage(Cookies) 198 | 199 | storage.cookies.set('test', JSON.stringify({ foo: 'bar' })) 200 | 201 | const result = await storage.getItem('test') 202 | expect(JSON.parse(result)).to.eql({ foo: 'bar' }) 203 | }) 204 | 205 | it('returns null when the item isn\'t available', async () => { 206 | const Cookies = CookiesJS(new JSDOM(``).window) 207 | 208 | const storage = new CookieStorage(Cookies) 209 | 210 | const result = await storage.getItem('test') 211 | expect(JSON.parse(result)).to.be.null 212 | }) 213 | }) 214 | 215 | describe('removeItem', () => { 216 | it('removes the item\'s cookie', function (done) { 217 | const Cookies = CookiesJS(new JSDOM(``).window) 218 | 219 | const storage = new CookieStorage(Cookies) 220 | 221 | storage.cookies.set('test', JSON.stringify({ foo: 'bar' })) 222 | 223 | storage.removeItem('test', (error, result) => { 224 | expect(storage.cookies.get('reduxPersist_test')).not.to.be.defined 225 | done() 226 | }) 227 | }) 228 | 229 | it('removes the item\'s cookie and returns a promise', async () => { 230 | const Cookies = CookiesJS(new JSDOM(``).window) 231 | 232 | const storage = new CookieStorage(Cookies) 233 | 234 | storage.cookies.set('test', JSON.stringify({ foo: 'bar' })) 235 | 236 | await storage.removeItem('test') 237 | 238 | expect(storage.cookies.get('reduxPersist_test')).not.to.be.defined 239 | }) 240 | 241 | it('removes the item from the list of keys', async () => { 242 | const Cookies = CookiesJS(new JSDOM(``).window) 243 | 244 | const storage = new CookieStorage(Cookies) 245 | 246 | storage.cookies.set('reduxPersist_test', JSON.stringify({ foo: 'bar' })) 247 | 248 | await storage.setItem('test', { foo: 'bar' }) 249 | await storage.removeItem('test') 250 | 251 | let keys = await storage.getAllKeys() 252 | expect(keys).to.eql([]) 253 | }) 254 | }) 255 | 256 | describe('getAllKeys', () => { 257 | it('returns a list of persisted keys', (done) => { 258 | const Cookies = CookiesJS(new JSDOM(``).window) 259 | 260 | const storage = new CookieStorage(Cookies) 261 | 262 | storage.cookies.set('reduxPersistIndex', JSON.stringify(['foo', 'bar'])) 263 | 264 | storage.getAllKeys(function (error, result) { 265 | expect(result).to.eql(['foo', 'bar']) 266 | done() 267 | }) 268 | }) 269 | 270 | it('returns a list of persisted keys and returns a promise', async () => { 271 | const Cookies = CookiesJS(new JSDOM(``).window) 272 | 273 | const storage = new CookieStorage(Cookies) 274 | 275 | storage.cookies.set('reduxPersistIndex', JSON.stringify(['foo', 'bar'])) 276 | 277 | const keys = await storage.getAllKeys() 278 | expect(keys).to.eql(['foo', 'bar']) 279 | }) 280 | }) 281 | }) 282 | 283 | describe('server-side behaviour', function () { 284 | describe('setItem', () => { 285 | it('stores an item as a cookie', async () => { 286 | const fakeCookies = { 287 | set: chai.spy(), 288 | get: chai.spy(), 289 | } 290 | 291 | const cookieJar = new NodeCookiesWrapper(fakeCookies) 292 | const storage = new CookieStorage(cookieJar, { expiration: { default: null } }) 293 | 294 | await storage.setItem('test', JSON.stringify({ foo: 'bar' })) 295 | 296 | expect(fakeCookies.set).to.have.been.called.with('test', '{%22foo%22:%22bar%22}', {}) 297 | expect(fakeCookies.set).to.have.been.called.with('reduxPersistIndex', '[%22test%22]', {}) 298 | }) 299 | }) 300 | 301 | describe('getItem', function () { 302 | it('gets an item stored as cookie', async () => { 303 | const fakeCookies = { 304 | set: chai.spy(), 305 | get: chai.spy(returns => ('{"foo":"bar"}')), 306 | } 307 | 308 | const cookieJar = new NodeCookiesWrapper(fakeCookies) 309 | const storage = new CookieStorage(cookieJar, { expiration: { default: null } }) 310 | 311 | const result = await storage.getItem('test') 312 | 313 | expect(JSON.parse(result)).to.eql({ foo: 'bar' }) 314 | expect(fakeCookies.get).to.have.been.called.with('test') 315 | }) 316 | 317 | it('returns null when the item isn\'t available', async () => { 318 | const fakeCookies = { 319 | set: chai.spy(), 320 | get: chai.spy(), 321 | } 322 | 323 | const cookieJar = new NodeCookiesWrapper(fakeCookies) 324 | const storage = new CookieStorage(cookieJar, { expiration: { default: null } }) 325 | 326 | const result = await storage.getItem('test') 327 | 328 | expect(JSON.parse(result)).to.null 329 | expect(fakeCookies.get).to.have.been.called.with('test') 330 | }) 331 | }) 332 | 333 | describe('removeItem', function () { 334 | it('removes the item\'s cookie', async () => { 335 | const fakeCookies = { 336 | set: chai.spy(), 337 | get: chai.spy(), 338 | } 339 | 340 | const cookieJar = new NodeCookiesWrapper(fakeCookies) 341 | const storage = new CookieStorage(cookieJar, { expiration: { default: null } }) 342 | 343 | await storage.removeItem('test') 344 | 345 | expect(fakeCookies.set).to.have.been.called.with('test', undefined) 346 | }) 347 | 348 | it('removes the item from the list of keys', async () => { 349 | const fakeCookies = { 350 | set: chai.spy(), 351 | get: chai.spy(), 352 | } 353 | 354 | const cookieJar = new NodeCookiesWrapper(fakeCookies) 355 | const storage = new CookieStorage(cookieJar, { expiration: { default: null } }) 356 | storage.getAllKeys = chai.spy(returns => Promise.resolve(['test', 'test2', 'test3'])) 357 | 358 | await storage.removeItem('test2') 359 | 360 | expect(fakeCookies.set).to.have.been.called.with('test2', undefined) 361 | expect(fakeCookies.set).to.have.been.called.with('reduxPersistIndex', '[%22test%22%2C%22test3%22]') 362 | }) 363 | }) 364 | 365 | describe('getAllKeys', function () { 366 | it('returns a list of persisted keys', async () => { 367 | const fakeCookies = { 368 | get: chai.spy(returns => ('["test","test2"]')), 369 | } 370 | 371 | const cookieJar = new NodeCookiesWrapper(fakeCookies) 372 | const storage = new CookieStorage(cookieJar, { expiration: { default: null } }) 373 | 374 | const keys = await storage.getAllKeys() 375 | 376 | expect(keys).to.eql(['test', 'test2']) 377 | }) 378 | }) 379 | }) 380 | }) 381 | --------------------------------------------------------------------------------