├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── babel.config.js ├── dist ├── README.md ├── index.d.ts ├── index.js ├── index.js.map ├── lib │ ├── vault.d.ts │ ├── vault.js │ ├── vault.js.map │ ├── vault.min.js │ └── vault.min.js.map └── package.json ├── package-lock.json ├── package.json ├── src ├── index.ts └── lib │ ├── vault.test.ts │ └── vault.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["@typescript-eslint"], 5 | "extends": [ 6 | "eslint:recommended", 7 | "plugin:@typescript-eslint/eslint-recommended", 8 | "plugin:@typescript-eslint/recommended" 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | npm-debug.log 3 | .DS_Store 4 | Thumbs.db 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | LICENSE 2 | 3 | The MIT License 4 | 5 | Copyright (c) 2022 Ultimate Courses 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 🔒 @ultimate/vault 3 |

4 |

5 | 6 | 1KB typed localStorage and sessionStorage utility with data structure and prefix support. 7 |

8 | 9 | 10 | 11 | 12 | 13 | ## Installation 14 | 15 | Install via `npm i @ultimate/vault`. 16 | 17 | ## Documentation 18 | 19 | _ESModule_: Import `Vault` into your TypeScript or JavaScript project and create a new instance: 20 | 21 | ```ts 22 | import { Vault } from '@ultimate/vault'; 23 | 24 | const localStorage = new Vault(); 25 | ``` 26 | 27 | _Global_: Access `window.Vault` if you are not using a module system: 28 | 29 | ```html 30 | 31 | 35 | ``` 36 | 37 | ### Local or Session Storage 38 | 39 | By default `new Vault()` will use `localStorage`. You may specify the type of storage: 40 | 41 | ```ts 42 | const localStorage = new Vault({ type: 'local' }); 43 | const sessionStorage = new Vault({ type: 'session' }); 44 | ``` 45 | 46 | As `Vault` is a `class` each instance works independently. 47 | 48 | ### Key Prefixes 49 | 50 | Create a prefix for each `Vault` instance: 51 | 52 | ```ts 53 | const localStorage = new Vault({ prefix: 'x9ea45' }); 54 | ``` 55 | 56 | All keys set into storage via this instance will be stored as `x9ea45-`. 57 | 58 | ### isSupported property 59 | 60 | Browser support is IE8+ so this shouldn't be wildly needed, but it's there anyway: 61 | 62 | ```ts 63 | const localStorage = new Vault(); 64 | 65 | if (localStorage.isSupported) { 66 | // initialize... 67 | } 68 | ``` 69 | 70 | ### `set(key: string, value: T): void` 71 | 72 | Set a key and value into storage using the typed `set` method: 73 | 74 | ```ts 75 | // TypeScript 76 | const localStorage = new Vault(); 77 | 78 | interface User { 79 | name: string 80 | } 81 | 82 | localStorage.set('user', { name: 'Todd Motto' }); 83 | ``` 84 | 85 | All methods are available to use without TypeScript: 86 | 87 | ```js 88 | const localStorage = new Vault(); 89 | 90 | localStorage.set('user', { name: 'Todd Motto' }); 91 | ``` 92 | 93 | ### `get(key: string): T | undefined` 94 | 95 | Get a value from storage using the typed `get` method: 96 | 97 | ```ts 98 | const localStorage = new Vault(); 99 | 100 | interface User { 101 | name: string 102 | } 103 | 104 | localStorage.get('user'); 105 | ``` 106 | 107 | ### `remove(key: string): void` 108 | 109 | Remove an item from storage using the `remove` method: 110 | 111 | ```ts 112 | const localStorage = new Vault(); 113 | 114 | localStorage.remove('user'); 115 | ``` 116 | 117 | ### `removeAll(): void` 118 | 119 | Remove _all_ items from storage: 120 | 121 | ```ts 122 | const localStorage = new Vault(); 123 | 124 | localStorage.removeAll(); 125 | ``` 126 | 127 | ### `onChange(key: string, fn: (e: StorageEvent) => void): () => void` 128 | 129 | Listen to the `storage` change event from another tab, which is emitted when any storage value is changed. Here we can specify to only listen to specific property changes: 130 | 131 | ```ts 132 | const localStorage = new Vault(); 133 | 134 | const unsubscribe = localStorage.onChange('user', (e: StorageEvent) => { 135 | // `user` was changed in another tab 136 | // we could use this new data to sync our UI 137 | console.log(e); 138 | }); 139 | 140 | // remove the event listener when you're ready 141 | unsubscribe(); 142 | ``` 143 | 144 | ### Get all values 145 | 146 | Obtain all storage values by accessing the `value` getter: 147 | 148 | ```ts 149 | const localStorage = new Vault(); 150 | 151 | console.log(localStorage.value); // { "user": "Todd Motto", ... } 152 | ``` 153 | 154 | Returns an object with all keys and values. Values will remain a `string` type and will need parsing with `JSON.parse()` if you need to access the value. 155 | 156 | ### Length of Storage 157 | 158 | Access how many items are currently in storage with `length`: 159 | 160 | ```ts 161 | const localStorage = new Vault(); 162 | 163 | console.log(localStorage.length); // 3 164 | ``` -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line 2 | module.exports = { 3 | presets: [ 4 | ["@babel/preset-env", { targets: { node: "current" } }], 5 | "@babel/preset-typescript", 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 |

2 | 🔒 @ultimate/vault 3 |

4 |

5 | 6 | 1KB typed localStorage and sessionStorage utility with data structure and prefix support. 7 |

8 | 9 | 10 | 11 | 12 | 13 | ## Installation 14 | 15 | Install via `npm i @ultimate/vault`. 16 | 17 | ## Documentation 18 | 19 | _ESModule_: Import `Vault` into your TypeScript or JavaScript project and create a new instance: 20 | 21 | ```ts 22 | import { Vault } from '@ultimate/vault'; 23 | 24 | const localStorage = new Vault(); 25 | ``` 26 | 27 | _Global_: Access `window.Vault` if you are not using a module system: 28 | 29 | ```html 30 | 31 | 35 | ``` 36 | 37 | ### Local or Session Storage 38 | 39 | By default `new Vault()` will use `localStorage`. You may specify the type of storage: 40 | 41 | ```ts 42 | const localStorage = new Vault({ type: 'local' }); 43 | const sessionStorage = new Vault({ type: 'session' }); 44 | ``` 45 | 46 | As `Vault` is a `class` each instance works independently. 47 | 48 | ### Key Prefixes 49 | 50 | Create a prefix for each `Vault` instance: 51 | 52 | ```ts 53 | const localStorage = new Vault({ prefix: 'x9ea45' }); 54 | ``` 55 | 56 | All keys set into storage via this instance will be stored as `x9ea45-`. 57 | 58 | ### isSupported property 59 | 60 | Browser support is IE8+ so this shouldn't be wildly needed, but it's there anyway: 61 | 62 | ```ts 63 | const localStorage = new Vault(); 64 | 65 | if (localStorage.isSupported) { 66 | // initialize... 67 | } 68 | ``` 69 | 70 | ### `set(key: string, value: T): void` 71 | 72 | Set a key and value into storage using the typed `set` method: 73 | 74 | ```ts 75 | // TypeScript 76 | const localStorage = new Vault(); 77 | 78 | interface User { 79 | name: string 80 | } 81 | 82 | localStorage.set('user', { name: 'Todd Motto' }); 83 | ``` 84 | 85 | All methods are available to use without TypeScript: 86 | 87 | ```js 88 | const localStorage = new Vault(); 89 | 90 | localStorage.set('user', { name: 'Todd Motto' }); 91 | ``` 92 | 93 | ### `get(key: string): T | undefined` 94 | 95 | Get a value from storage using the typed `get` method: 96 | 97 | ```ts 98 | const localStorage = new Vault(); 99 | 100 | interface User { 101 | name: string 102 | } 103 | 104 | localStorage.get('user'); 105 | ``` 106 | 107 | ### `remove(key: string): void` 108 | 109 | Remove an item from storage using the `remove` method: 110 | 111 | ```ts 112 | const localStorage = new Vault(); 113 | 114 | localStorage.remove('user'); 115 | ``` 116 | 117 | ### `removeAll(): void` 118 | 119 | Remove _all_ items from storage: 120 | 121 | ```ts 122 | const localStorage = new Vault(); 123 | 124 | localStorage.removeAll(); 125 | ``` 126 | 127 | ### `onChange(key: string, fn: (e: StorageEvent) => void): () => void` 128 | 129 | Listen to the `storage` change event from another tab, which is emitted when any storage value is changed. Here we can specify to only listen to specific property changes: 130 | 131 | ```ts 132 | const localStorage = new Vault(); 133 | 134 | const unsubscribe = localStorage.onChange('user', (e: StorageEvent) => { 135 | // `user` was changed in another tab 136 | // we could use this new data to sync our UI 137 | console.log(e); 138 | }); 139 | 140 | // remove the event listener when you're ready 141 | unsubscribe(); 142 | ``` 143 | 144 | ### Get all values 145 | 146 | Obtain all storage values by accessing the `value` getter: 147 | 148 | ```ts 149 | const localStorage = new Vault(); 150 | 151 | console.log(localStorage.value); // { "user": "Todd Motto", ... } 152 | ``` 153 | 154 | Returns an object with all keys and values. Values will remain a `string` type and will need parsing with `JSON.parse()` if you need to access the value. 155 | 156 | ### Length of Storage 157 | 158 | Access how many items are currently in storage with `length`: 159 | 160 | ```ts 161 | const localStorage = new Vault(); 162 | 163 | console.log(localStorage.length); // 3 164 | ``` -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | export * from './lib/vault'; 2 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { 3 | if (k2 === undefined) k2 = k; 4 | var desc = Object.getOwnPropertyDescriptor(m, k); 5 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { 6 | desc = { enumerable: true, get: function() { return m[k]; } }; 7 | } 8 | Object.defineProperty(o, k2, desc); 9 | }) : (function(o, m, k, k2) { 10 | if (k2 === undefined) k2 = k; 11 | o[k2] = m[k]; 12 | })); 13 | var __exportStar = (this && this.__exportStar) || function(m, exports) { 14 | for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); 15 | }; 16 | Object.defineProperty(exports, "__esModule", { value: true }); 17 | __exportStar(require("./lib/vault"), exports); 18 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,8CAA4B"} -------------------------------------------------------------------------------- /dist/lib/vault.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Vault instance options 3 | * `type`: Either localStorage or sessionStorage 4 | * `prefix`: Set all keys of the instance with a prefix 5 | */ 6 | export interface VaultOptions { 7 | type?: 'local' | 'session'; 8 | prefix?: string; 9 | } 10 | /** 11 | * Vault class to manage storage 12 | */ 13 | export declare class Vault { 14 | /** 15 | * Internal store, either localStorage or sessionStorage 16 | */ 17 | private store; 18 | /** 19 | * Default options for each instance 20 | */ 21 | private options; 22 | /** 23 | * Specify a `type` or `prefix` when instantiating 24 | */ 25 | constructor(options: VaultOptions); 26 | /** 27 | * isSupported getter that returns a boolean 28 | * @returns boolean 29 | */ 30 | get isSupported(): boolean; 31 | /** 32 | * An object of all storage items 33 | * @returns { [name: string]: any } 34 | */ 35 | get value(): { 36 | [name: string]: any; 37 | }; 38 | /** 39 | * How many items are in the storage 40 | * @returns number 41 | */ 42 | get length(): number; 43 | /** 44 | * Returns the prefixed key if required 45 | * @returns string 46 | */ 47 | private getKey; 48 | /** 49 | * Set a data structure to storage 50 | * @param key The key name to set, excluding the prefix 51 | * @param value Any non-function value (strings, numbers, booleans, arrays, objects) 52 | * @returns void 53 | */ 54 | set(key: string, value: T): void; 55 | /** 56 | * Get an item from storage 57 | * @param key The key name to get, excluding the prefix 58 | * @returns The typed item or undefined if not found 59 | */ 60 | get(key: string): T | undefined; 61 | /** 62 | * Remove an item from storaage 63 | * @param key The key name to get, excluding the prefix 64 | * @returns void 65 | */ 66 | remove(key: string): void; 67 | /** 68 | * Remove all items from storage 69 | * @returns void 70 | */ 71 | removeAll(): void; 72 | /** 73 | * 74 | * @param key The key name to listen for, excluding the prefix 75 | * @param fn Callback function on key's value change 76 | * @returns function to remove the event listener 77 | */ 78 | onChange(key: string, fn: (e: StorageEvent) => void): () => void; 79 | } 80 | -------------------------------------------------------------------------------- /dist/lib/vault.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | var __assign = (this && this.__assign) || function () { 3 | __assign = Object.assign || function(t) { 4 | for (var s, i = 1, n = arguments.length; i < n; i++) { 5 | s = arguments[i]; 6 | for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) 7 | t[p] = s[p]; 8 | } 9 | return t; 10 | }; 11 | return __assign.apply(this, arguments); 12 | }; 13 | Object.defineProperty(exports, "__esModule", { value: true }); 14 | exports.Vault = void 0; 15 | /** 16 | * Vault class to manage storage 17 | */ 18 | var Vault = /** @class */ (function () { 19 | /** 20 | * Specify a `type` or `prefix` when instantiating 21 | */ 22 | function Vault(options) { 23 | /** 24 | * Default options for each instance 25 | */ 26 | this.options = { 27 | type: 'local', 28 | prefix: '', 29 | }; 30 | this.options = __assign(__assign({}, this.options), options); 31 | /* eslint-disable @typescript-eslint/no-explicit-any */ 32 | this.store = window["".concat(this.options.type, "Storage")]; 33 | } 34 | Object.defineProperty(Vault.prototype, "isSupported", { 35 | /** 36 | * isSupported getter that returns a boolean 37 | * @returns boolean 38 | */ 39 | get: function () { 40 | return typeof Storage === 'function'; 41 | }, 42 | enumerable: false, 43 | configurable: true 44 | }); 45 | Object.defineProperty(Vault.prototype, "value", { 46 | /** 47 | * An object of all storage items 48 | * @returns { [name: string]: any } 49 | */ 50 | get: function () { 51 | return __assign({}, this.store); 52 | }, 53 | enumerable: false, 54 | configurable: true 55 | }); 56 | Object.defineProperty(Vault.prototype, "length", { 57 | /** 58 | * How many items are in the storage 59 | * @returns number 60 | */ 61 | get: function () { 62 | return this.store.length; 63 | }, 64 | enumerable: false, 65 | configurable: true 66 | }); 67 | /** 68 | * Returns the prefixed key if required 69 | * @returns string 70 | */ 71 | Vault.prototype.getKey = function (key) { 72 | if (this.options.prefix) { 73 | return "".concat(this.options.prefix, "-").concat(key); 74 | } 75 | return key; 76 | }; 77 | /** 78 | * Set a data structure to storage 79 | * @param key The key name to set, excluding the prefix 80 | * @param value Any non-function value (strings, numbers, booleans, arrays, objects) 81 | * @returns void 82 | */ 83 | Vault.prototype.set = function (key, value) { 84 | try { 85 | this.store.setItem(this.getKey(key), JSON.stringify(value)); 86 | } 87 | catch (e) { 88 | if (e instanceof DOMException) { 89 | throw new Error("[Vault] Storage limit exceeded: ".concat(e)); 90 | } 91 | else { 92 | throw new Error("[Vault] Unknown error: ".concat(e)); 93 | } 94 | } 95 | }; 96 | /** 97 | * Get an item from storage 98 | * @param key The key name to get, excluding the prefix 99 | * @returns The typed item or undefined if not found 100 | */ 101 | Vault.prototype.get = function (key) { 102 | try { 103 | var value = this.store.getItem(this.getKey(key)); 104 | if (value) { 105 | return JSON.parse(value); 106 | } 107 | } 108 | catch (e) { 109 | throw new Error("[Vault] Error parsing key \"".concat(this.getKey(key), "\": ").concat(e)); 110 | } 111 | }; 112 | /** 113 | * Remove an item from storaage 114 | * @param key The key name to get, excluding the prefix 115 | * @returns void 116 | */ 117 | Vault.prototype.remove = function (key) { 118 | this.store.removeItem(this.getKey(key)); 119 | }; 120 | /** 121 | * Remove all items from storage 122 | * @returns void 123 | */ 124 | Vault.prototype.removeAll = function () { 125 | this.store.clear(); 126 | }; 127 | /** 128 | * 129 | * @param key The key name to listen for, excluding the prefix 130 | * @param fn Callback function on key's value change 131 | * @returns function to remove the event listener 132 | */ 133 | Vault.prototype.onChange = function (key, fn) { 134 | var prop = this.getKey(key); 135 | var onChange = function (e) { return prop === e[prop] && fn(e); }; 136 | window.addEventListener('storage', onChange); 137 | return function () { return window.removeEventListener('storage', onChange); }; 138 | }; 139 | return Vault; 140 | }()); 141 | exports.Vault = Vault; 142 | /* eslint-disable @typescript-eslint/no-explicit-any */ 143 | window.Vault = Vault; 144 | //# sourceMappingURL=vault.js.map -------------------------------------------------------------------------------- /dist/lib/vault.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"vault.js","sourceRoot":"","sources":["../../src/lib/vault.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/lib/vault.min.js: -------------------------------------------------------------------------------- 1 | "use strict";var __assign=this&&this.__assign||function(){return(__assign=Object.assign||function(t){for(var e,o=1,r=arguments.length;o { 9 | let vault: Vault; 10 | 11 | beforeEach(() => { 12 | vault = new Vault(); 13 | }); 14 | 15 | test('Vault creates a new instance', () => { 16 | // .toBe versus .toEqual 17 | expect(vault instanceof Vault).toEqual(true); 18 | }); 19 | 20 | test('Vault reports browser support', () => { 21 | expect(vault.isSupported).toEqual(true); 22 | }); 23 | }); 24 | 25 | describe('Storage Types and Prefixes', () => { 26 | test('Defaults to window.localStorage', () => { 27 | const spy = jest.spyOn(Object.getPrototypeOf(localStorage), 'setItem'); // Storage.prototype 28 | 29 | const vault = new Vault({ type: 'local' }); 30 | vault.set('x', 'y'); 31 | 32 | expect(JSON.parse(localStorage.getItem('x') as string)).toEqual('y'); 33 | expect(spy).toHaveBeenCalledWith('x', JSON.stringify('y')); 34 | }); 35 | 36 | test('Defaults to window.sessionStorage', () => { 37 | const spy = jest.spyOn(Object.getPrototypeOf(sessionStorage), 'setItem'); // Storage.prototype 38 | 39 | const vault = new Vault({ type: 'session' }); 40 | vault.set('x', 'y'); 41 | 42 | expect(JSON.parse(sessionStorage.getItem('x') as string)).toEqual('y'); 43 | expect(spy).toHaveBeenCalledWith('x', JSON.stringify('y')); 44 | }); 45 | 46 | test('Prefixes all Storage set and get', () => { 47 | const spy = jest.spyOn(Object.getPrototypeOf(localStorage), 'setItem'); // Storage.prototype 48 | 49 | const vault = new Vault({ prefix: 'x8k0zae' }); 50 | vault.set('prefix', 1234); 51 | 52 | // raw localStorage 53 | expect(JSON.parse(localStorage.getItem('prefix') as string)).toBeNull(); 54 | expect( 55 | JSON.parse(localStorage.getItem('x8k0zae-prefix') as string) 56 | ).toEqual(1234); 57 | expect(spy).toHaveBeenCalledWith('x8k0zae-prefix', JSON.stringify(1234)); 58 | 59 | // vault abstraction 60 | expect(vault.get('prefix')).toEqual(1234); 61 | expect(vault.get('x8k0zae-prefix')).not.toBeDefined(); 62 | }); 63 | }); 64 | 65 | describe('Public Methods and Properties', () => { 66 | let vault: Vault; 67 | 68 | beforeEach(() => { 69 | vault = new Vault(); 70 | vault.removeAll(); 71 | }); 72 | 73 | describe('Value Property', () => { 74 | test('Returns all unserialized values as an object', () => { 75 | vault.set('a', 1); 76 | vault.set('b', 2); 77 | vault.set('c', 3); 78 | // toEqual vs toStrictEqual: 79 | // https://dev.to/thejaredwilcurt/why-you-should-never-use-tobe-in-jest-48ca 80 | expect(vault.value).toEqual({ a: '1', b: '2', c: '3' }); 81 | }); 82 | }); 83 | 84 | describe('Set and Get API', () => { 85 | test('Handles basic data types', () => { 86 | vault.set('string', 'y'); 87 | vault.set('number', 88); 88 | vault.set('boolean', true); 89 | vault.set('array', [1, 2, 3, 4, 5]); 90 | vault.set('object', { a: 123, b: [] }); 91 | 92 | expect(vault.get('string')).toEqual('y'); 93 | expect(vault.get('number')).toEqual(88); 94 | expect(vault.get('boolean')).toEqual(true); 95 | expect(vault.get('array')).toEqual([1, 2, 3, 4, 5]); 96 | expect(vault.get('object')).toEqual({ a: 123, b: [] }); 97 | }); 98 | 99 | test('Should reject and throw Function values', () => { 100 | // expect(() => vault.set('function', () => 1234)).toThrow(); 101 | expect(() => vault.set('function', () => 1234)).toThrowError( 102 | 'Cannot set functions to Storage - "() => 1234"' 103 | ); 104 | }); 105 | }); 106 | 107 | describe('Remove API', () => { 108 | test('Removes a single property', () => { 109 | vault.set('x', 1); 110 | vault.set('y', 2); 111 | vault.set('z', 3); 112 | 113 | vault.remove('y'); 114 | 115 | expect(vault.get('x')).toEqual(1); 116 | expect(vault.get('y')).toBeUndefined(); 117 | expect(vault.get('z')).toEqual(3); 118 | }); 119 | 120 | test('Removes all properties', () => { 121 | vault.set('x', 1); 122 | vault.set('y', 2); 123 | vault.set('z', 3); 124 | 125 | vault.removeAll(); 126 | 127 | expect(vault.get('x')).toBeUndefined(); 128 | expect(vault.get('y')).toBeUndefined(); 129 | expect(vault.get('z')).toBeUndefined(); 130 | }); 131 | }); 132 | 133 | describe('Remove API', () => { 134 | beforeEach(() => { 135 | vault.removeAll(); 136 | }); 137 | 138 | test('Tracks the length of items stored', () => { 139 | vault.set('x', 1); 140 | expect(vault.length).toEqual(1); 141 | vault.set('y', 2); 142 | expect(vault.length).toEqual(2); 143 | vault.set('z', 3); 144 | expect(vault.length).toEqual(3); 145 | }); 146 | }); 147 | }); 148 | -------------------------------------------------------------------------------- /src/lib/vault.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Vault instance options 3 | * `type`: Either localStorage or sessionStorage 4 | * `prefix`: Set all keys of the instance with a prefix 5 | */ 6 | export interface VaultOptions { 7 | type?: 'local' | 'session'; 8 | prefix?: string; 9 | } 10 | 11 | /** 12 | * Vault class to manage storage 13 | */ 14 | export class Vault { 15 | /** 16 | * Internal store, either localStorage or sessionStorage 17 | */ 18 | private store: Storage; 19 | 20 | /** 21 | * Default options for each instance 22 | */ 23 | private options: Required = { 24 | type: 'local', 25 | prefix: '', 26 | }; 27 | 28 | /** 29 | * Specify a `type` or `prefix` when instantiating 30 | */ 31 | constructor(options?: VaultOptions) { 32 | this.options = { ...this.options, ...options }; 33 | /* eslint-disable @typescript-eslint/no-explicit-any */ 34 | this.store = (window as any)[`${this.options.type}Storage`]; 35 | } 36 | 37 | /** 38 | * isSupported getter that returns a boolean 39 | * @returns boolean 40 | */ 41 | get isSupported(): boolean { 42 | return typeof Storage === 'function'; 43 | } 44 | 45 | /** 46 | * An object of all storage items 47 | * @returns { [name: string]: any } 48 | */ 49 | get value(): { [name: string]: any } { 50 | return { ...this.store }; 51 | } 52 | 53 | /** 54 | * How many items are in the storage 55 | * @returns number 56 | */ 57 | get length(): number { 58 | return this.store.length; 59 | } 60 | 61 | /** 62 | * Returns the prefixed key if required 63 | * @returns string 64 | */ 65 | private getKey(key: string): string { 66 | if (this.options.prefix) { 67 | return `${this.options.prefix}-${key}`; 68 | } 69 | return key; 70 | } 71 | 72 | /** 73 | * Set a data structure to storage 74 | * @param key The key name to set, excluding the prefix 75 | * @param value Any non-function value (strings, numbers, booleans, arrays, objects) 76 | * @returns void 77 | */ 78 | set(key: string, value: T): void { 79 | if (typeof value === 'function') { 80 | throw new Error(`Cannot set functions to Storage - "${value}"`); 81 | } 82 | try { 83 | this.store.setItem(this.getKey(key), JSON.stringify(value)); 84 | } catch (e) { 85 | if (e instanceof DOMException) { 86 | throw new Error(`[Vault] Storage limit exceeded: ${e}`); 87 | } else { 88 | throw new Error(`[Vault] Unknown error: ${e}`); 89 | } 90 | } 91 | } 92 | 93 | /** 94 | * Get an item from storage 95 | * @param key The key name to get, excluding the prefix 96 | * @returns The typed item or undefined if not found 97 | */ 98 | get(key: string): T | undefined { 99 | try { 100 | const value = this.store.getItem(this.getKey(key)); 101 | if (value) { 102 | return JSON.parse(value) as T; 103 | } 104 | } catch (e) { 105 | throw new Error(`[Vault] Error parsing key "${this.getKey(key)}": ${e}`); 106 | } 107 | } 108 | 109 | /** 110 | * Remove an item from storaage 111 | * @param key The key name to get, excluding the prefix 112 | * @returns void 113 | */ 114 | remove(key: string): void { 115 | this.store.removeItem(this.getKey(key)); 116 | } 117 | 118 | /** 119 | * Remove all items from storage 120 | * @returns void 121 | */ 122 | removeAll(): void { 123 | this.store.clear(); 124 | } 125 | 126 | /** 127 | * 128 | * @param key The key name to listen for, excluding the prefix 129 | * @param fn Callback function on key's value change 130 | * @returns function to remove the event listener 131 | */ 132 | onChange(key: string, fn: (e: StorageEvent) => void): () => void { 133 | const prop = this.getKey(key); 134 | const onChange = (e: StorageEvent) => prop === (e as any)[prop] && fn(e); 135 | window.addEventListener('storage', onChange); 136 | return () => window.removeEventListener('storage', onChange); 137 | } 138 | } 139 | 140 | /* eslint-disable @typescript-eslint/no-explicit-any */ 141 | (window as any).Vault = Vault; 142 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES5", 4 | "lib": ["DOM", "ES5"], 5 | "module": "CommonJS", 6 | "moduleResolution": "node", 7 | "declaration": true, 8 | "sourceMap": true, 9 | "outDir": "./dist", 10 | "esModuleInterop": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "strict": true, 13 | "skipLibCheck": true 14 | } 15 | } 16 | --------------------------------------------------------------------------------