Fixed some bugs

This commit is contained in:
Zakary Timson 2021-01-28 15:18:32 -05:00
parent 954049d992
commit 42fa3722fa
15 changed files with 137 additions and 121 deletions

View File

@ -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

View File

@ -1 +0,0 @@
export * from './src/index';

2
lib/index.d.ts vendored
View File

@ -1 +1 @@
export * from './src/index';
export * from './webstorage';

View File

@ -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

1
lib/index.js.map Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC"}

1
lib/src/index.d.ts vendored
View File

@ -1 +0,0 @@
export * from './webstorage';

View File

@ -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);

View File

@ -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));
}
});
};
}

View File

@ -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
*/

97
lib/webstorage.js Normal file
View File

@ -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

1
lib/webstorage.js.map Normal file
View File

@ -0,0 +1 @@
{"version":3,"file":"webstorage.js","sourceRoot":"","sources":["../src/webstorage.ts"],"names":[],"mappings":"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"}

View File

@ -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",

View File

@ -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(<string>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(<string>storage.getItem(<string>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;
}
/**

View File

@ -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;

View File

@ -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"
]
}