From 42fa3722fabf28ee5f3b963563fe563f5018e061 Mon Sep 17 00:00:00 2001 From: ztimson Date: Thu, 28 Jan 2021 15:18:32 -0500 Subject: [PATCH] Fixed some bugs --- README.md | 7 +++ index.ts | 1 - lib/index.d.ts | 2 +- lib/index.js | 15 +----- lib/index.js.map | 1 + lib/src/index.d.ts | 1 - lib/src/index.js | 13 ----- lib/src/webstorage.js | 77 --------------------------- lib/{src => }/webstorage.d.ts | 3 -- lib/webstorage.js | 97 +++++++++++++++++++++++++++++++++++ lib/webstorage.js.map | 1 + package.json | 2 +- src/webstorage.ts | 10 ++-- tests/webstorage.spec.ts | 13 ++++- tsconfig.json | 15 +++--- 15 files changed, 137 insertions(+), 121 deletions(-) delete mode 100644 index.ts create mode 100644 lib/index.js.map delete mode 100644 lib/src/index.d.ts delete mode 100644 lib/src/index.js delete mode 100644 lib/src/webstorage.js rename lib/{src => }/webstorage.d.ts (94%) create mode 100644 lib/webstorage.js create mode 100644 lib/webstorage.js.map diff --git a/README.md b/README.md index a96cae6..ae8cef3 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,13 @@ export class MyCustomClass { ``` ## Caveats + +### Custom Functions +You can technically store anything inside local/session storage however everything is serialized using javascript's JSON, +so any prototypes will be stripped causing you to lose any extra functions you may have defined on your class. However +if you provide a default value, + +### Impure Functions Impure functions don't use the Object's setter preventing the storage from being updated. To prevent this use a pure function or save it manually by reading the variable. (Reading triggers change detection & save if there are differences) ```typescript diff --git a/index.ts b/index.ts deleted file mode 100644 index f3c80e0..0000000 --- a/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './src/index'; \ No newline at end of file diff --git a/lib/index.d.ts b/lib/index.d.ts index cba1843..71f4dc3 100644 --- a/lib/index.d.ts +++ b/lib/index.d.ts @@ -1 +1 @@ -export * from './src/index'; +export * from './webstorage'; diff --git a/lib/index.js b/lib/index.js index 9289462..8b307bf 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,13 +1,2 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -__exportStar(require("./src/index"), exports); +export * from './webstorage'; +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/lib/index.js.map b/lib/index.js.map new file mode 100644 index 0000000..d8d9dd2 --- /dev/null +++ b/lib/index.js.map @@ -0,0 +1 @@ +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"} \ No newline at end of file diff --git a/lib/src/index.d.ts b/lib/src/index.d.ts deleted file mode 100644 index 71f4dc3..0000000 --- a/lib/src/index.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './webstorage'; diff --git a/lib/src/index.js b/lib/src/index.js deleted file mode 100644 index 10fc4a3..0000000 --- a/lib/src/index.js +++ /dev/null @@ -1,13 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -__exportStar(require("./webstorage"), exports); diff --git a/lib/src/webstorage.js b/lib/src/webstorage.js deleted file mode 100644 index b179d34..0000000 --- a/lib/src/webstorage.js +++ /dev/null @@ -1,77 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SessionStorage = exports.LocalStorage = void 0; -const crypto = require("crypto-js"); -/** - * Automatically syncs localStorage with the decorated property. - * - * **Example** - * ``` - * class Example { - * @LocalStorage() lastLogin: string; - * @LocalStorage(false, {key: '_hideMenu'}) hideMenu: boolean; - * } - * ``` - * - * @category WebStorage - * @param defaultValue Default value to return if property does no exist inside localStorage. - * @param opts Any additional options - */ -function LocalStorage(defaultValue, opts = {}) { - opts.default = defaultValue; - return storage(localStorage, opts); -} -exports.LocalStorage = LocalStorage; -/** - * Automatically syncs sessionStorage with the decorated property. - * - * **Example** - * ``` - * class Example { - * @SessionStorage() lastLogin: string; - * @SessionStorage(false, {key: '_hideMenu'}) hideMenu: boolean; - * } - * ``` - * - * @category WebStorage - * @param defaultValue Default value to return if property does no exist inside sessionStorage. - * @param opts Any additional options - */ -function SessionStorage(defaultValue, opts = {}) { - opts.default = defaultValue; - return storage(sessionStorage, opts); -} -exports.SessionStorage = SessionStorage; -/** - * **Internal use only** - * - * Overrides the properties getter/setter methods to read/write from the provided storage endpoint. - * - * @hidden - * @category WebStorage - * @param storage Web Storage API - * @param opts Any additional options - */ -function storage(storage, opts) { - return function (target, key) { - if (!opts.key) - opts.key = key; - Object.defineProperty(target, key, { - get: function () { - let storageVal = storage.getItem(opts.key); - if (storageVal == null || storageVal == 'null' || storageVal == 'undefined') - return opts.default || null; - if (opts.encryptWith != null) - storageVal = crypto.AES.decrypt(JSON.parse(storageVal), opts.encryptWith).toString(crypto.enc.Utf8); - return JSON.parse(storageVal); - }, - set: function (value) { - if (value == null) - storage.removeItem(opts.key); - if (opts.encryptWith != null) - value = crypto.AES.encrypt(JSON.stringify(value), opts.encryptWith).toString(); - storage.setItem(opts.key, JSON.stringify(value)); - } - }); - }; -} diff --git a/lib/src/webstorage.d.ts b/lib/webstorage.d.ts similarity index 94% rename from lib/src/webstorage.d.ts rename to lib/webstorage.d.ts index 232ea20..aa527e0 100644 --- a/lib/src/webstorage.d.ts +++ b/lib/webstorage.d.ts @@ -1,6 +1,5 @@ /** * Options to be used with WebStorage decorators - * @category WebStorage */ export interface WebStorageOptions { /** Default value to provide if storage is empty */ @@ -21,7 +20,6 @@ export interface WebStorageOptions { * } * ``` * - * @category WebStorage * @param defaultValue Default value to return if property does no exist inside localStorage. * @param opts Any additional options */ @@ -37,7 +35,6 @@ export declare function LocalStorage(defaultValue?: any, opts?: WebStorageOption * } * ``` * - * @category WebStorage * @param defaultValue Default value to return if property does no exist inside sessionStorage. * @param opts Any additional options */ diff --git a/lib/webstorage.js b/lib/webstorage.js new file mode 100644 index 0000000..8256e64 --- /dev/null +++ b/lib/webstorage.js @@ -0,0 +1,97 @@ +import * as crypto from 'crypto-js'; +/** + * Automatically syncs localStorage with the decorated property. + * + * **Example** + * ``` + * class Example { + * @LocalStorage() lastLogin: string; + * @LocalStorage(false, {key: '_hideMenu'}) hideMenu: boolean; + * } + * ``` + * + * @param defaultValue Default value to return if property does no exist inside localStorage. + * @param opts Any additional options + */ +export function LocalStorage(defaultValue, opts = {}) { + opts.default = defaultValue; + return decoratorBuilder(localStorage, opts); +} +/** + * Automatically syncs sessionStorage with the decorated property. + * + * **Example** + * ``` + * class Example { + * @SessionStorage() lastLogin: string; + * @SessionStorage(false, {key: '_hideMenu'}) hideMenu: boolean; + * } + * ``` + * + * @param defaultValue Default value to return if property does no exist inside sessionStorage. + * @param opts Any additional options + */ +export function SessionStorage(defaultValue, opts = {}) { + opts.default = defaultValue; + return decoratorBuilder(sessionStorage, opts); +} +/** + * **Internal use only** + * + * Fetch variable from storage & take care of any defaults, object definitions, encryption & serialization + * + * @param storage Web Storage API + * @param opts Any additional options + */ +function fromStorage(storage, opts) { + let storedVal = JSON.parse(storage.getItem(opts.key)); + if (storedVal == null) { + if (opts.default == null) + return null; + if (opts.default != 'object') + return opts.default; + if (opts.default.constructor != null) + return Object.assign(new opts.default.constructor(), opts.default); + return Object.assign({}, opts.default); + } + if (opts.encryptWith != null) + storedVal = JSON.parse(crypto.AES.decrypt(storedVal, opts.encryptWith).toString(crypto.enc.Utf8)); + if (typeof storedVal != 'object' || !Array.isArray(storedVal)) + return storedVal; + if (opts.default != null && opts.default.constructor != null) + return Object.assign(new opts.default.constructor(), opts.default, storedVal); + return Object.assign({}, opts.default, storedVal); +} +/** + * **Internal use only** + * + * Overrides the properties getter/setter methods to read/write from the provided storage endpoint. + * + * @param storage Web Storage API + * @param opts Any additional options + */ +function decoratorBuilder(storage, opts) { + return function (target, key) { + if (!opts.key) + opts.key = key; + let field = fromStorage(storage, opts); + Object.defineProperty(target, key, { + get: function () { + if (field != fromStorage(storage, opts)) { + console.log(typeof opts.default, field, fromStorage(storage, opts)); + target[key] = field; + } + return field; + }, + set: function (value) { + field = value; + if (value == null) + storage.removeItem(opts.key); + if (opts.encryptWith != null) + value = crypto.AES.encrypt(JSON.stringify(value), opts.encryptWith).toString(); + storage.setItem(opts.key, JSON.stringify(value)); + } + }); + }; +} +//# sourceMappingURL=webstorage.js.map \ No newline at end of file diff --git a/lib/webstorage.js.map b/lib/webstorage.js.map new file mode 100644 index 0000000..32cbabb --- /dev/null +++ b/lib/webstorage.js.map @@ -0,0 +1 @@ +{"version":3,"file":"webstorage.js","sourceRoot":"","sources":["../src/webstorage.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/package.json b/package.json index 964819f..a5a69dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "webstorage-decorators", - "version": "3.0.0", + "version": "3.1.2", "description": "Decorators to sync class properties to Local/Session storage", "main": "./lib/index.js", "typings": "./lib/index.d.ts", diff --git a/src/webstorage.ts b/src/webstorage.ts index c98bbfc..8ee2cd7 100644 --- a/src/webstorage.ts +++ b/src/webstorage.ts @@ -59,10 +59,12 @@ export function SessionStorage(defaultValue?, opts: WebStorageOptions = {}) { * @param opts Any additional options */ function fromStorage(storage: Storage, opts: WebStorageOptions) { - let storedVal = storage.getItem(opts.key); - if(storedVal == null) return opts.default != null ? opts.default : null; - if(opts.encryptWith != null) storedVal = JSON.parse(crypto.AES.decrypt(JSON.parse(storedVal), opts.encryptWith).toString(crypto.enc.Utf8)); - return typeof storedVal == 'object' && !Array.isArray(storedVal) ? Object.assign(opts.default, storedVal) : storedVal; + let storedVal = JSON.parse(storage.getItem(opts.key)); + if(storedVal == null && opts.default == null) return null; + if(storedVal != null && opts.encryptWith != null) storedVal = JSON.parse(crypto.AES.decrypt(storedVal, opts.encryptWith).toString(crypto.enc.Utf8)); + if(opts.default != null && opts.default.constructor != null) return Object.assign(new opts.default.constructor(), opts.default, storedVal); + if(typeof storedVal == 'object' && !Array.isArray(storedVal)) return Object.assign({}, opts.default, storedVal); + return storedVal; } /** diff --git a/tests/webstorage.spec.ts b/tests/webstorage.spec.ts index c848d77..6ff7ecf 100644 --- a/tests/webstorage.spec.ts +++ b/tests/webstorage.spec.ts @@ -3,7 +3,6 @@ import {LocalStorage, SessionStorage} from "../src"; const CUSTOM_KEY = '_MY_KEY' const ENCRYPTION_KEY = 'abc123'; - class TestType { constructor(public first: string, public last: string) { } fullName() { return `${this.last}, ${this.first}`; } @@ -44,6 +43,12 @@ describe('Webstorage Decorators', () => { expect(localStorage.getItem('localStorage')).toBe(JSON.stringify(testValue)); expect(testComponent.localStorage).toBe(testValue); }); + test('Arrays', () => { + const testValue = [Math.random(), Math.random(), Math.random()]; + testComponent.localStorage = testValue; + expect(localStorage.getItem('localStorage')).toBe(JSON.stringify(testValue)); + expect(testComponent.localStorage).toBe(testValue); + }); test('String Value', () => { const testValue = 'SOMETHING_RANDOM'; testComponent.localStorage = testValue; @@ -99,6 +104,12 @@ describe('Webstorage Decorators', () => { expect(sessionStorage.getItem('sessionStorage')).toBe(JSON.stringify(testValue)); expect(testComponent.sessionStorage).toBe(testValue); }); + test('Arrays', () => { + const testValue = [Math.random(), Math.random(), Math.random()]; + testComponent.sessionStorage = testValue; + expect(sessionStorage.getItem('sessionStorage')).toBe(JSON.stringify(testValue)); + expect(testComponent.sessionStorage).toBe(testValue); + }); test('String Value', () => { const testValue = 'SOMETHING_RANDOM'; testComponent.sessionStorage = testValue; diff --git a/tsconfig.json b/tsconfig.json index 263051b..9a0037f 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,18 +1,21 @@ { "compilerOptions": { - "target": "ES2015", - "module": "commonjs", + "target": "es2015", + "module": "es2020", "declaration": true, + "sourceMap": true, "outDir": "lib", + "strict": true, + "noImplicitAny": false, + "moduleResolution": "node", "typeRoots": [ "./node_modules/@types" ], - "strict": true, - "noImplicitAny": false, + "esModuleInterop": true, "experimentalDecorators": true, + "emitDecoratorMetadata": true }, "include": [ - "index.ts", - "src/**/*" + "./src" ] }