Comparing version 0.1.0 to 0.2.0
@@ -0,3 +1,4 @@ | ||
export { default as Validations } from './validations'; | ||
/** Configuration for storeObject */ | ||
export interface StoreObjectConfig<Object extends Record<string, any>> { | ||
export interface StoreObjectConfig<O extends Record<string, any>> { | ||
/** | ||
@@ -9,12 +10,27 @@ * Whether or not to check localStorage when an object key is retrieved | ||
/** | ||
* Whether the stored object only contains/stores *some* of the keys on the serialized object. | ||
* This is useful if you want an object to look at only some keys of a localStorage object | ||
* without overwriting the other ones. | ||
* | ||
* It's important to note that passing this option effectively enables a key validation of sorts; | ||
* any keys that were not passed are ignored and not passed to validate or modify (if these methods are defined) | ||
* @default false | ||
*/ | ||
partial?: boolean; | ||
/** | ||
* Validate an object before setting it in localStorage or reading it. | ||
* Can confirm/deny if the object is valid, or modify the object before passing it on. | ||
* See validation examples in the examples/ directory or in the documentation for `storeObject` | ||
* Can confirm/deny if the object is valid, along with an optional error message if it is not | ||
* | ||
* @returns A boolean to confirm validity, false and an Error instance to deny validity, | ||
* or return true alongside an object to pass on instead of the original | ||
* @default () => true | ||
*/ | ||
validate?: (value: any) => Object | boolean | readonly [boolean] | readonly [false, Error]; | ||
validate?(value: Readonly<any>): boolean | readonly [boolean] | readonly [false, Error]; | ||
/** | ||
* Modify an object before setting it in localStorage or reading it. | ||
* Called after validate. Any valiation should be done in validate and not here | ||
* | ||
* @returns A potentially modified version of the object originally passed | ||
*/ | ||
modify?(value: O): O; | ||
/** | ||
* Function to parse object. Defaults to `JSON.parse`. | ||
@@ -62,2 +78,4 @@ * Any validation should **NOT** be done here, but in the validate method | ||
* // Validating that the expected keys exist and are the correct type | ||
* import { storeObject, validateKeys } from 'ls-proxy' | ||
* | ||
* const myObj = storeObject( | ||
@@ -71,2 +89,3 @@ * 'myObj', | ||
* validate(value) { | ||
* if (!validateKeys(value, ['someString', 'someNumber'])) return false | ||
* if (typeof value.someString !== 'string') return false | ||
@@ -82,4 +101,5 @@ * if (typeof value.someNumber !== 'number') return false | ||
* ```typescript | ||
* // Validation to automatically change a key based on another | ||
* // Automatically change a key based on another | ||
* import { storeObject } from 'ls-proxy' | ||
* | ||
* interface Person { | ||
@@ -117,3 +137,3 @@ * name: string | ||
*/ | ||
export declare function storeObject<Keys extends string = string, Object extends Record<Keys, any> = Record<Keys, any>>(lsKey: string, defaults: Readonly<Object>, configuration?: StoreObjectConfig<Object>): Object; | ||
export declare function storeObject<O extends Record<string, any> = Record<string, any>>(lsKey: string, defaults: O, configuration?: StoreObjectConfig<O>): O; | ||
/** Configuration for storeSeparate */ | ||
@@ -151,4 +171,4 @@ export interface StoreSeparateConfig { | ||
*/ | ||
export declare function storeSeparate<Keys extends string = string, Object extends Record<Keys, string> = Record<Keys, string>>(defaults: Readonly<Object>, configuration?: StoreSeparateConfig): Object; | ||
export declare function storeSeparate<O extends Record<string, string> = Record<string, string>>(defaults: O, configuration?: StoreSeparateConfig): O; | ||
export as namespace LSProxy; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.storeSeparate = exports.storeObject = void 0; | ||
const defaultStoreObjectConfig = ({ checkGets, validate, parse, stringify, }) => { | ||
exports.storeSeparate = exports.storeObject = exports.Validations = void 0; | ||
var validations_1 = require("./validations"); | ||
Object.defineProperty(exports, "Validations", { enumerable: true, get: function () { return validations_1.default; } }); | ||
const defaultStoreObjectConfig = ({ checkGets, partial, validate, modify, parse, stringify, }) => { | ||
return { | ||
checkGets: checkGets !== null && checkGets !== void 0 ? checkGets : true, | ||
partial: partial !== null && partial !== void 0 ? partial : false, | ||
validate: validate !== null && validate !== void 0 ? validate : (() => true), | ||
modify: modify !== null && modify !== void 0 ? modify : (value => value), | ||
parse: parse !== null && parse !== void 0 ? parse : JSON.parse, | ||
@@ -42,2 +46,4 @@ stringify: stringify !== null && stringify !== void 0 ? stringify : JSON.stringify, | ||
* // Validating that the expected keys exist and are the correct type | ||
* import { storeObject, validateKeys } from 'ls-proxy' | ||
* | ||
* const myObj = storeObject( | ||
@@ -51,2 +57,3 @@ * 'myObj', | ||
* validate(value) { | ||
* if (!validateKeys(value, ['someString', 'someNumber'])) return false | ||
* if (typeof value.someString !== 'string') return false | ||
@@ -62,4 +69,5 @@ * if (typeof value.someNumber !== 'number') return false | ||
* ```typescript | ||
* // Validation to automatically change a key based on another | ||
* // Automatically change a key based on another | ||
* import { storeObject } from 'ls-proxy' | ||
* | ||
* interface Person { | ||
@@ -98,20 +106,48 @@ * name: string | ||
function storeObject(lsKey, defaults, configuration = {}) { | ||
const { checkGets, validate, parse, stringify } = defaultStoreObjectConfig(configuration); | ||
const { checkGets, partial, validate, modify, parse, stringify } = defaultStoreObjectConfig(configuration); | ||
/** Call validOrThrow with relevant parameters by default */ | ||
const vot = (value, action = 'set') => validOrThrow(validate, modify, value, action, lsKey); | ||
const checkParse = (value) => { | ||
const parsed = parse(value); | ||
const valid = validOrThrow(validate(parsed), parsed, 'get', lsKey); | ||
const valid = vot(parsed, 'get'); | ||
return valid; | ||
}; | ||
const checkStringify = (value) => stringify(validOrThrow(validate(value), value, 'set', lsKey)); | ||
const checkStringify = (value) => stringify(vot(value)); | ||
const filterWanted = (obj, defaultIfUndefined = true) => { | ||
let desiredObject = {}; | ||
Object.keys(defaults).forEach( | ||
// Set to value found in localStorage if it exists, otherwise use provided default | ||
key => { | ||
var _a; | ||
return (desiredObject[key] = defaultIfUndefined | ||
? // Use default if defaultInDefined | ||
(_a = obj[key]) !== null && _a !== void 0 ? _a : defaults[key] | ||
: // Use given value even if undefined | ||
obj[key]); | ||
}); | ||
return desiredObject; | ||
}; | ||
let object = Object.assign({}, defaults); | ||
// Update localStorage value | ||
// Update localStorage value or read existing values | ||
if (!localStorage[lsKey]) { | ||
localStorage[lsKey] = checkStringify(defaults); | ||
} | ||
else | ||
else if (partial) { | ||
const current = parse(localStorage[lsKey]); | ||
object = filterWanted(current); | ||
const validModified = vot(object); | ||
localStorage[lsKey] = stringify(Object.assign(Object.assign({}, current), validModified)); | ||
} | ||
else { | ||
object = checkParse(localStorage[lsKey]); | ||
} | ||
return new Proxy(object, { | ||
set(target, key, value, receiver) { | ||
const setResult = Reflect.set(target, key, value, receiver); | ||
localStorage[lsKey] = checkStringify(target); | ||
if (partial) { | ||
const validModified = vot(target); | ||
localStorage[lsKey] = stringify(Object.assign(Object.assign({}, parse(localStorage[lsKey])), validModified)); | ||
} | ||
else | ||
localStorage[lsKey] = checkStringify(target); | ||
return setResult; | ||
@@ -121,4 +157,11 @@ }, | ||
var _a; | ||
if (checkGets) | ||
target[key] = (_a = checkParse(localStorage[lsKey])[key]) !== null && _a !== void 0 ? _a : defaults[key]; | ||
if (checkGets) { | ||
if (partial) { | ||
target[key] = vot(filterWanted(parse(localStorage[lsKey]), false), 'get')[key]; | ||
vot(target, 'get'); | ||
} | ||
else { | ||
target[key] = (_a = checkParse(localStorage[lsKey])[key]) !== null && _a !== void 0 ? _a : defaults[key]; | ||
} | ||
} | ||
return Reflect.get(target, key, receiver); | ||
@@ -129,6 +172,17 @@ }, | ||
exports.storeObject = storeObject; | ||
const validOrThrow = (valid, object, action, lsKey) => { | ||
/** | ||
* Validate and modify an object | ||
* | ||
* @param validate Return from the validate function | ||
* @param modify Function to modify the object | ||
* @param object The object to modify | ||
* @param action Whether the object is being get or set | ||
* @param lsKey The key in localStorage | ||
* @returns The object if valid | ||
*/ | ||
const validOrThrow = (validate, modify, object, action, lsKey) => { | ||
const error = new TypeError(action === 'get' | ||
? `Validation failed while parsing ${lsKey} from localStorage` | ||
: `Validation failed while setting to ${lsKey} in localStorage`); | ||
const valid = validate(object); | ||
// Throw error on failure | ||
@@ -149,7 +203,3 @@ if (typeof valid === 'boolean') { | ||
} | ||
else { | ||
// Return is a new object | ||
return valid; | ||
} | ||
return object; | ||
return modify(object); | ||
}; | ||
@@ -156,0 +206,0 @@ const defaultStoreSeparateConfig = ({ id, checkGets, }) => { |
{ | ||
"$schema": "https://json.schemastore.org/package", | ||
"name": "ls-proxy", | ||
"version": "0.1.0", | ||
"version": "0.2.0", | ||
"description": "Wrapper around localStorage to easily store JSON objects", | ||
@@ -20,2 +20,3 @@ "repository": "https://gitlab.com/MysteryBlokHed/ls-proxy", | ||
"dev": "nodemon -w src -e ts --exec \"yarn build || exit 1\"", | ||
"test": "jest", | ||
"lint": "prettier \"**/*.{js,ts,json,md,yml}\"", | ||
@@ -25,10 +26,16 @@ "doc": "typedoc", | ||
}, | ||
"dependencies": { | ||
"@types/greasemonkey": "^4.0.2" | ||
"jest": { | ||
"verbose": true, | ||
"preset": "ts-jest", | ||
"testEnvironment": "jsdom" | ||
}, | ||
"devDependencies": { | ||
"@types/greasemonkey": "^4.0.2", | ||
"@types/jest": "^27.4.0", | ||
"greasetools": "^0.2.0", | ||
"jest": "^27.5.1", | ||
"nodemon": "^2.0.13", | ||
"prettier": "^2.4.1", | ||
"terser-webpack-plugin": "^5.2.4", | ||
"ts-jest": "^27.1.3", | ||
"typedoc": "^0.22.11", | ||
@@ -35,0 +42,0 @@ "typescript": "^4.4.4", |
@@ -10,2 +10,36 @@ # ls-proxy [![Build Badge]](https://gitlab.com/MysteryBlokHed/ls-proxy/-/pipelines) [![NPM Badge]](https://www.npmjs.com/package/ls-proxy) [![License Badge]](#license) | ||
## Why to use? | ||
If you want to store client-side data for your website, the way to do it is with localStorage. | ||
However, there is at least one siginificant downside: you can only store strings in localStorage keys. | ||
The best way to get around this is by storing a stringified JSON object in a key, | ||
but doing this manually or having to call a function that does it for you any time you change an object would be annoying. | ||
This library solves these problems using JS proxies. | ||
It also has great IDE support thanks to it being written in TypeScript. | ||
You can also use it with vanilla JS with the Webpacked file (`ls-proxy.user.js`), | ||
which is useful to test it in the browser or while writing UserScripts. | ||
Here's all it takes to store a stringifed JSON object in localStorage and automatically change it: | ||
```typescript | ||
import { storeObject } from 'ls-proxy' | ||
const someInfo = storeObject( | ||
// The localStorage key to save data under | ||
'someInfo', | ||
// The object to store | ||
{ | ||
aString: 'Hello, World!', | ||
aNumber: 123, | ||
aBoolean: true, | ||
aList: [1, '2', 3], | ||
}, | ||
) | ||
someInfo.aNumber = 42 // Updates localStorage | ||
console.log(someInfo.aList) // Reads from localStorage | ||
``` | ||
## Documentation | ||
@@ -16,3 +50,3 @@ | ||
Examples are located in [`examples/`](examples/). | ||
Examples are located in [`examples`](https://gitlab.com/MysteryBlokHed/ls-proxy/-/tree/main/examples). | ||
@@ -98,3 +132,3 @@ ## Use | ||
### Building files | ||
### Build | ||
@@ -117,2 +151,12 @@ To build the project, run: | ||
### Test | ||
To test the project, run: | ||
```sh | ||
yarn test | ||
``` | ||
This project uses Jest for tests. | ||
## License | ||
@@ -119,0 +163,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
25139
0
9
524
172
12
- Removed@types/greasemonkey@^4.0.2
- Removed@types/greasemonkey@4.0.7(transitive)