Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ls-proxy

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ls-proxy - npm Package Compare versions

Comparing version 0.4.0 to 0.5.0

157

lib/index.d.ts

@@ -0,7 +1,7 @@

import type { Keys } from './types';
export { default as Validations } from './validations';
/**
* Configuration for StoreObjectConfig
* @template O The stored object
* Configuration options used between both storeObject and storeSeparate
*/
export interface StoreObjectConfig<O extends Record<string, any>> {
interface CommonConfig {
/**

@@ -13,12 +13,2 @@ * 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 key validation:
* any keys that were not passed are ignored and not passed to validate or modify
* @default false
*/
partial?: boolean;
/**
* Called whenever a key should be set

@@ -36,4 +26,32 @@ * @param value The value being set

/**
* Function to parse object. Defaults to `JSON.parse`.
* Any validation should **NOT** be done here, but in the validate method
* @default JSON.parse
*/
parse?: (value: string) => any;
/**
* Function to stringify object. Defaults to `JSON.stringify`.
* Any validation should **NOT** be done here, but in the validate method
* @default JSON.stringify
*/
stringify?: (value: any) => string;
}
/**
* Configuration for StoreObjectConfig
* @template O The stored object
*/
export interface StoreObjectConfig<O extends Record<string, any>> extends CommonConfig {
/**
* 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 key validation:
* any keys that were not passed are ignored and not passed to validate or modify
* @default false
*/
partial?: boolean;
/**
* Validate an object before setting it in localStorage or reading it.
* Can confirm/deny if the object is valid, along with an optional error message if it is not
* Can confirm/deny if the object is valid, along with an optional error message if it is invalid
*

@@ -50,14 +68,2 @@ * @returns A boolean to confirm validity or false and optionally an Error instance to deny validity

modify?(value: O, action: 'get' | 'set'): O;
/**
* Function to parse object. Defaults to `JSON.parse`.
* Any validation should **NOT** be done here, but in the validate method
* @default JSON.parse
*/
parse?: (value: string) => any;
/**
* Function to stringify object. Defaults to `JSON.stringify`.
* Any validation should **NOT** be done here, but in the validate method
* @default JSON.stringify
*/
stringify?: (value: any) => string;
}

@@ -125,3 +131,3 @@ /**

*
* const myPerson = storeObject(
* const myPerson = storeObject<Person>(
* 'myPerson',

@@ -132,3 +138,3 @@ * {

* minor: true,
* } as Person,
* },
* {

@@ -139,3 +145,3 @@ * // If the person's age is 18 or greater, set minor to false.

* // and retrieved from it
* validate(value) {
* modify(value) {
* if (value.age >= 18) value.minor = false

@@ -155,4 +161,6 @@ * else value.minor = true

export declare function storeObject<O extends Record<string, any> = Record<string, any>>(lsKey: string, defaults: O, configuration?: StoreObjectConfig<O>): O;
/** Configuration for storeSeparate */
export interface StoreSeparateConfig {
/**
* Configuration for storeSeparate
*/
export interface StoreSeparateConfig<O extends Record<string, any>> extends CommonConfig {
/**

@@ -168,6 +176,26 @@ * An optional unique identifier. Prefixes all keys in localStorage

checkGets?: boolean;
/**
* Validate an object before setting it in localStorage or reading it.
* Can confirm/deny if the object is valid, along with an optional error message if it is invalid
*
* @param value A partial version of the originally passed object,
* **containing only the key being get/set**
* @param key The key being get/set
* @returns A boolean to confirm validity or false and optionally an Error instance to deny validity
*/
validate?(value: Partial<O>, action: 'get' | 'set', key: Keys<O>): 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
*
* @param value A partial version of the originally passed object,
* **containing only the key being get/set**
* @param key The key being get/set
* @returns A potentially modified version of the object originally passed.
* **Only the key used in the value param will be changed in localStorage**
*/
modify?(value: Partial<O>, action: 'get' | 'set', key: Keys<O>): Partial<O>;
}
/**
* Set multiple individual keys in localStorage with one object.
* Note that all values must be strings for this method
* Set multiple individual keys in localStorage with one object
*

@@ -180,2 +208,3 @@ * @param defaults The defaults values if they are undefined

* ```typescript
* // No validation
* import { storeSeparate } from 'ls-proxy'

@@ -185,2 +214,4 @@ *

* foo: 'bar',
* abc: 123,
* numbers: [1, 2, 3],
* })

@@ -190,6 +221,64 @@ *

* console.log(myObj.foo) // Checks localStorage if checkGets is true
* console.log(myObj.abc === localStorage.abc) // true
* ```
*
* @example
* ```typescript
* // Validating that the key being set/get is correct
* import { storeSeparate } from 'ls-proxy'
*
* const myObj = storeSeparate(
* {
* foo: 'bar',
* abc: 123,
* },
* {
* validate(value, action, key) {
* if (key !== 'foo' && key !== 'abc') return false
* return true
* },
* },
* )
* ```
*
* @example
* ```typescript
* // Using IDs to avoid conflicting names
* import { storeSeparate } from 'ls-proxy'
*
* const obj1 = storeSeparate({ foo: 'bar' }, { id: 'obj1' })
* const obj2 = storeSeparate({ foo: 123 }, { id: 'obj2' })
*
* console.log(obj1.foo) // bar
* console.log(obj2.foo) // 123
* console.log(localStorage['obj1.foo']) // "bar"
* console.log(localStorage['obj2.foo']) // 123
* ```
*
* @example
* ```typescript
* // Automatically change a key while being set/get
* import { storeSeparate } from 'ls-proxy'
*
* const myObj = storeSeparate(
* { base64Value: 'foo' },
* {
* modify(value, action, key) {
* if (key === 'base64Value') {
* // Decode base64 on get
* if (action === 'get') value[key] = window.btoa(value[key]!)
* // Encode base64 on set
* else value[key] = window.atob(value[key]!)
* }
* return value
* },
* },
* )
*
* myObj.base64Value = 'bar' // Encoded in localStorage
* console.log(myObj.base64Value) // Logs 'bar', decoded from localStorage
* ```
*/
export declare function storeSeparate<O extends Record<string, string> = Record<string, string>>(defaults: O, configuration?: StoreSeparateConfig): O;
export declare function storeSeparate<O extends Record<string, any> = Record<string, any>>(defaults: O, configuration?: StoreSeparateConfig<O>): O;
export as namespace LSProxy;

@@ -6,18 +6,18 @@ "use strict";

Object.defineProperty(exports, "Validations", { enumerable: true, get: function () { return validations_1.default; } });
const setObj = (target, newObj) => Object.entries(newObj).forEach(([k, v]) => (target[k] = v));
/**
* Fill in default values for CommonConfig
*/
const commonDefaults = ({ checkGets, set, get, parse, stringify, }) => ({
checkGets: checkGets !== null && checkGets !== void 0 ? checkGets : true,
set: set !== null && set !== void 0 ? set : ((key, value) => (localStorage[key] = value)),
get: get !== null && get !== void 0 ? get : (value => { var _a; return (_a = localStorage[value]) !== null && _a !== void 0 ? _a : null; }),
parse: parse !== null && parse !== void 0 ? parse : JSON.parse,
stringify: stringify !== null && stringify !== void 0 ? stringify : JSON.stringify,
});
/**
* Fill in default values for StoreObjectConfig
* @template O The stored object
*/
const defaultStoreObjectConfig = ({ checkGets, partial, set, get, validate, modify, parse, stringify, }) => {
return {
checkGets: checkGets !== null && checkGets !== void 0 ? checkGets : true,
partial: partial !== null && partial !== void 0 ? partial : false,
set: set !== null && set !== void 0 ? set : ((key, value) => (localStorage[key] = value)),
get: get !== null && get !== void 0 ? get : (value => { var _a; return (_a = localStorage[value]) !== null && _a !== void 0 ? _a : null; }),
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,
stringify: stringify !== null && stringify !== void 0 ? stringify : JSON.stringify,
};
};
const defaultStoreObjectConfig = ({ checkGets, partial, set, get, validate, modify, parse, stringify, }) => (Object.assign({ 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) }, commonDefaults({ checkGets, set, get, parse, stringify })));
const shouldObjectProxy = (object) =>

@@ -52,3 +52,42 @@ // Check that the target isn't falsey (primarily in case it's null, since typeof null === 'object')

};
const validateResult = (valid, defaultError) => {
// Throw error on failure
if (typeof valid === 'boolean') {
// Return is bool
if (!valid)
return [valid, defaultError];
return [valid];
}
else {
// Return is array
if (!valid[0]) {
if (valid.length === 2)
return [valid[0], valid[1]];
else
return [valid[0], defaultError];
}
return [valid[0]];
}
};
/**
* Validate and modify an object
*
* @param validate Function to validate the object
* @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
* @template O The stored object
* @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 = validateResult(validate(object, action), error);
if (!valid[0])
throw valid[1];
return modify(object, action);
};
/**
* Store a stringified JSON object in localStorage.

@@ -114,3 +153,3 @@ * This method can use any type that can be serialized.

*
* const myPerson = storeObject(
* const myPerson = storeObject<Person>(
* 'myPerson',

@@ -121,3 +160,3 @@ * {

* minor: true,
* } as Person,
* },
* {

@@ -128,3 +167,3 @@ * // If the person's age is 18 or greater, set minor to false.

* // and retrieved from it
* validate(value) {
* modify(value) {
* if (value.age >= 18) value.minor = false

@@ -215,44 +254,25 @@ * else value.minor = true

exports.storeObject = storeObject;
const defaultStoreSeparateConfig = ({ id, checkGets, set, get, validate, modify, parse, stringify, }) => (Object.assign({ id, validate: validate !== null && validate !== void 0 ? validate : (() => true), modify: modify !== null && modify !== void 0 ? modify : (value => value) }, commonDefaults({ checkGets, set, get, parse, stringify })));
/**
* Validate and modify an object
* Validate and modify a value
*
* @param validate Return from the validate function
* @param validate Function to validate the object
* @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
* @param key The key being validated/modified
* @template O The stored object
* @returns The object if valid
* @returns The value, if valid
*/
const validOrThrow = (validate, modify, object, action, lsKey) => {
const validOrThrowSeparate = (validate, modify, object, action, key) => {
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, action);
// Throw error on failure
if (typeof valid === 'boolean') {
// Return is bool
if (!valid)
throw error;
}
else if (Array.isArray(valid)) {
// Return is array
if (!valid[0]) {
if (valid.length === 2)
throw valid[1];
else
throw error;
}
}
return modify(object, action);
? `Validation failed while parsing ${key} from localStorage`
: `Validation failed while setting to ${key} in localStorage`);
const valid = validateResult(validate(object, action, key), error);
if (!valid[0])
throw valid[1];
return modify(object, action, key);
};
const defaultStoreSeparateConfig = ({ id, checkGets, }) => {
return {
id,
checkGets: checkGets !== null && checkGets !== void 0 ? checkGets : true,
};
};
/**
* Set multiple individual keys in localStorage with one object.
* Note that all values must be strings for this method
* Set multiple individual keys in localStorage with one object
*

@@ -265,2 +285,3 @@ * @param defaults The defaults values if they are undefined

* ```typescript
* // No validation
* import { storeSeparate } from 'ls-proxy'

@@ -270,2 +291,4 @@ *

* foo: 'bar',
* abc: 123,
* numbers: [1, 2, 3],
* })

@@ -275,24 +298,94 @@ *

* console.log(myObj.foo) // Checks localStorage if checkGets is true
* console.log(myObj.abc === localStorage.abc) // true
* ```
*
* @example
* ```typescript
* // Validating that the key being set/get is correct
* import { storeSeparate } from 'ls-proxy'
*
* const myObj = storeSeparate(
* {
* foo: 'bar',
* abc: 123,
* },
* {
* validate(value, action, key) {
* if (key !== 'foo' && key !== 'abc') return false
* return true
* },
* },
* )
* ```
*
* @example
* ```typescript
* // Using IDs to avoid conflicting names
* import { storeSeparate } from 'ls-proxy'
*
* const obj1 = storeSeparate({ foo: 'bar' }, { id: 'obj1' })
* const obj2 = storeSeparate({ foo: 123 }, { id: 'obj2' })
*
* console.log(obj1.foo) // bar
* console.log(obj2.foo) // 123
* console.log(localStorage['obj1.foo']) // "bar"
* console.log(localStorage['obj2.foo']) // 123
* ```
*
* @example
* ```typescript
* // Automatically change a key while being set/get
* import { storeSeparate } from 'ls-proxy'
*
* const myObj = storeSeparate(
* { base64Value: 'foo' },
* {
* modify(value, action, key) {
* if (key === 'base64Value') {
* // Decode base64 on get
* if (action === 'get') value[key] = window.btoa(value[key]!)
* // Encode base64 on set
* else value[key] = window.atob(value[key]!)
* }
* return value
* },
* },
* )
*
* myObj.base64Value = 'bar' // Encoded in localStorage
* console.log(myObj.base64Value) // Logs 'bar', decoded from localStorage
* ```
*/
function storeSeparate(defaults, configuration = {}) {
const { id, checkGets } = defaultStoreSeparateConfig(configuration);
const { id, checkGets, set, get, validate, modify, parse, stringify } = defaultStoreSeparateConfig(configuration);
const object = Object.assign({}, defaults);
/** Call validOrThrow with relevant parameters by default */
const vot = (key, value, action) => validOrThrowSeparate(validate, modify, { [key]: value }, action, key)[key];
// Set defaults
for (const [key, value] of Object.entries(defaults)) {
const keyPrefix = addId(key, id);
if (!localStorage[keyPrefix])
localStorage[keyPrefix] = value;
const lsValue = get(keyPrefix);
if (!lsValue) {
set(keyPrefix, stringify(vot(key, value, 'set')));
}
else
object[key] = localStorage[keyPrefix];
object[key] = vot(parse(lsValue), key, 'get');
}
return new Proxy(object, {
set(target, key, value) {
localStorage[addId(key, id)] = value;
return Reflect.set(target, key, value);
// Modify object
const modified = vot(key, value, 'set');
set(addId(key, id), stringify(modified));
return Reflect.set(target, key, modified);
},
get(target, key) {
var _a;
if (checkGets)
target[key] = (_a = localStorage[addId(key, id)]) !== null && _a !== void 0 ? _a : defaults[key];
if (checkGets) {
const valueUnparsed = get(addId(key, id));
const value = valueUnparsed ? parse(valueUnparsed) : defaults[key];
target[key] = vot(key, value, 'get');
}
if (shouldObjectProxy(target[key])) {
// Return a Proxy to the object to catch sets
return nestedProxyHandler(target, key, target[key], this.set);
}
return Reflect.get(target, key);

@@ -299,0 +392,0 @@ },

{
"$schema": "https://json.schemastore.org/package",
"name": "ls-proxy",
"version": "0.4.0",
"version": "0.5.0",
"description": "Wrapper around localStorage to easily store JSON objects",

@@ -6,0 +6,0 @@ "repository": "https://gitlab.com/MysteryBlokHed/ls-proxy",

@@ -21,4 +21,4 @@ # ls-proxy [![Build Badge]](https://gitlab.com/MysteryBlokHed/ls-proxy/-/pipelines) [![NPM Badge]](https://www.npmjs.com/package/ls-proxy) [![License Badge]](#license)

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.
You can also use it with vanilla JS with the Webpacked file (`ls-proxy.user.js` or `ls-proxy.min.user.js`),
which is useful to test it in the browser, when not using a JS bundler, or while writing UserScripts.

@@ -46,2 +46,25 @@ Here's all it takes to store a stringifed JSON object in localStorage and automatically change it:

The method above stores an entire serialized object in localStorage,
meaning the entire object has to be stringified/parsed whenever a single key
is affected. The method below stores each key individually instead:
```typescript
import { storeSeparate } from 'ls-proxy'
const someInfo = storeObject(
// The object to store
{
aString: 'Hello, World!',
aNumber: 123,
aBoolean: true,
aList: [1, '2', 3],
},
// Optional; prefixes the stored keys with this ID
{ id: 'someInfo' },
)
someInfo.aNumber = 42 // Updates localStorage
console.log(someInfo.aList) // Reads from localStorage
```
## Documentation

@@ -54,2 +77,12 @@

## storeObject vs storeSeparate
`storeSeparate` will generally be faster than `storeObject`
since there's less data being serialized/deserialized with each get/set,
but there are still reasons to use `storeObject`.
For example, if you want to use `validate` and `modify` (see documentation for config options),
and you need the entire object for context.
This can be the case if you need another key's value to validate the object,
or if you want to modify multiple keys with a single set/get.
## Use

@@ -56,0 +89,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc