@solid-primitives/storage
Advanced tools
Comparing version 1.0.4 to 1.0.7
@@ -1,78 +0,13 @@ | ||
import { addClearMethod } from "./tools"; | ||
import { createStorage, createStorageSignal } from "./storage"; | ||
const serializeCookieOptions = (options) => { | ||
if (!options) { | ||
return ""; | ||
} | ||
let memo = ""; | ||
for (const key in options) { | ||
if (!options.hasOwnProperty(key)) { | ||
continue; | ||
} | ||
const value = options[key]; | ||
memo += | ||
value instanceof Date | ||
? `; ${key}=${value.toUTCString()}` | ||
: typeof value === "boolean" | ||
? `; ${key}` | ||
: `; ${key}=${value}`; | ||
} | ||
return memo; | ||
import { | ||
cookieStorage, | ||
createCookieStorage, | ||
createCookieStorageSignal | ||
} from "./chunk-INHFJDUZ.js"; | ||
import "./chunk-V53AARZS.js"; | ||
import "./chunk-UHHZE4FR.js"; | ||
import "./chunk-JUWXSDKJ.js"; | ||
export { | ||
cookieStorage, | ||
createCookieStorage, | ||
createCookieStorageSignal | ||
}; | ||
/** | ||
* handle cookies exactly like you would handle localStorage | ||
* | ||
* the main change is that setItem accepts the following options: | ||
* ```typescript | ||
* export type CookieOptions = { | ||
* domain?: string; | ||
* expires?: Date | number | String; | ||
* path?: string; | ||
* secure?: boolean; | ||
* httpOnly?: boolean; | ||
* maxAge?: number; | ||
* sameSite?: "None" | "Lax" | "Strict"; | ||
* }; | ||
* ``` | ||
*/ | ||
export const cookieStorage = addClearMethod({ | ||
_cookies: [globalThis.document, "cookie"], | ||
getItem: (key) => cookieStorage._cookies[0][cookieStorage._cookies[1]] | ||
.match("(^|;)\\s*" + key + "\\s*=\\s*([^;]+)") | ||
?.pop() ?? null, | ||
setItem: (key, value, options) => { | ||
cookieStorage._cookies[0][cookieStorage._cookies[1]] = `${key}=${value}${serializeCookieOptions(options)}`; | ||
}, | ||
removeItem: (key) => { | ||
cookieStorage._cookies[0][cookieStorage._cookies[1]] = `${key}=deleted${serializeCookieOptions({ | ||
expires: new Date(0) | ||
})}`; | ||
}, | ||
key: (index) => { | ||
let key = null; | ||
let count = 0; | ||
cookieStorage._cookies[0][cookieStorage._cookies[1]].replace(/(?:^|;)\s*(.+?)\s*=\s*[^;]+/g, (_, found) => { | ||
if (!key && found && count++ === index) { | ||
key = found; | ||
} | ||
return ""; | ||
}); | ||
return key; | ||
}, | ||
get length() { | ||
let length = 0; | ||
cookieStorage._cookies[0][cookieStorage._cookies[1]].replace(/(?:^|;)\s*.+?\s*=\s*[^;]+/g, (found) => { | ||
length += found ? 1 : 0; | ||
return ""; | ||
}); | ||
return length; | ||
} | ||
}); | ||
/** | ||
* creates a reactive store but bound to document.cookie | ||
*/ | ||
export const createCookieStorage = (props) => createStorage({ ...props, api: cookieStorage }); | ||
/** | ||
* creates a reactive signal, but bound to document.cookie | ||
*/ | ||
export const createCookieStorageSignal = (key, initialValue, props) => createStorageSignal(key, initialValue, { ...props, api: cookieStorage }); |
@@ -1,4 +0,25 @@ | ||
import { createStorage, createAsyncStorage, createStorageSignal, createLocalStorage, createSessionStorage } from "./storage"; | ||
import { cookieStorage, createCookieStorage } from "./cookies"; | ||
import { addClearMethod } from "./tools"; | ||
export { createStorage, createAsyncStorage, createStorageSignal, createLocalStorage, createCookieStorage, createSessionStorage, cookieStorage, addClearMethod }; | ||
import { | ||
cookieStorage, | ||
createCookieStorage | ||
} from "./chunk-INHFJDUZ.js"; | ||
import { | ||
createAsyncStorage, | ||
createLocalStorage, | ||
createSessionStorage, | ||
createStorage, | ||
createStorageSignal | ||
} from "./chunk-V53AARZS.js"; | ||
import { | ||
addClearMethod | ||
} from "./chunk-UHHZE4FR.js"; | ||
import "./chunk-JUWXSDKJ.js"; | ||
export { | ||
addClearMethod, | ||
cookieStorage, | ||
createAsyncStorage, | ||
createCookieStorage, | ||
createLocalStorage, | ||
createSessionStorage, | ||
createStorage, | ||
createStorageSignal | ||
}; |
@@ -1,224 +0,15 @@ | ||
import { createEffect, createSignal } from "solid-js"; | ||
export function createStorage(props) { | ||
const apis = props?.api | ||
? Array.isArray(props.api) | ||
? props.api | ||
: [props.api] | ||
: [globalThis.localStorage].filter(Boolean); | ||
const prefix = props?.prefix ? `${props.prefix}.` : ""; | ||
const signals = new Map(); | ||
const store = new Proxy({}, { | ||
get(_, key) { | ||
let node = signals.get(key); | ||
if (!node) { | ||
node = createSignal(undefined, { equals: false }); | ||
signals.set(key, node); | ||
} | ||
node[0](); | ||
const value = apis.reduce((result, api) => { | ||
if (result !== null || !api) { | ||
return result; | ||
} | ||
return api.getItem(`${prefix}${key}`); | ||
}, null); | ||
if (value !== null && props?.deserializer) { | ||
return props.deserializer(value, key, props?.options); | ||
} | ||
return value; | ||
} | ||
}); | ||
const setter = (key, value, options) => { | ||
const filteredValue = props?.serializer | ||
? props.serializer(value, key, options ?? props?.options) | ||
: value; | ||
apis.forEach(api => api.setItem(`${prefix}${key}`, filteredValue)); | ||
const node = signals.get(key); | ||
node && node[1](); | ||
}; | ||
const remove = (key) => apis.forEach(api => api.removeItem(key)); | ||
const clear = () => apis.forEach(api => api?.clear?.()); | ||
const toJSON = () => { | ||
const result = {}; | ||
const addValue = (key, value) => { | ||
if (!result.hasOwnProperty(key)) { | ||
const filteredValue = value && props?.deserializer | ||
? props.deserializer(value, key, props?.options) | ||
: value; | ||
if (filteredValue) { | ||
result[key] = filteredValue; | ||
} | ||
} | ||
}; | ||
apis.forEach(api => { | ||
if (typeof api.getAll === "function") { | ||
const values = api.getAll(); | ||
for (const key of values) { | ||
addValue(key, values[key]); | ||
} | ||
} | ||
else { | ||
let index = 0, key; | ||
while ((key = api.key(index++))) { | ||
if (!result.hasOwnProperty(key)) { | ||
addValue(key, api.getItem(key)); | ||
} | ||
} | ||
} | ||
}); | ||
return result; | ||
}; | ||
return [ | ||
store, | ||
setter, | ||
{ | ||
clear, | ||
remove, | ||
toJSON | ||
} | ||
]; | ||
} | ||
export function createAsyncStorage(props) { | ||
const apis = props?.api ? (Array.isArray(props.api) ? props.api : [props.api]) : []; | ||
const prefix = props?.prefix ? `${props.prefix}.` : ""; | ||
const signals = new Map(); | ||
const store = new Proxy({}, { | ||
get(_, key) { | ||
let node = signals.get(key); | ||
if (!node) { | ||
node = createSignal(undefined, { equals: false }); | ||
signals.set(key, node); | ||
} | ||
node[0](); | ||
return apis.reduce((result, api) => { | ||
if (result !== null || !api) { | ||
return result; | ||
} | ||
const value = api.getItem(`${prefix}${key}`); | ||
if (value instanceof Promise) { | ||
return value.then((value) => value && props?.deserializer ? props.deserializer(value, key, props?.options) : value); | ||
} | ||
return value !== null && props?.deserializer | ||
? Promise.resolve(props.deserializer(value, key, props?.options)) | ||
: Promise.resolve(value); | ||
}, null); | ||
} | ||
}); | ||
const setter = (key, value, options) => { | ||
const filteredValue = props?.serializer | ||
? props.serializer(value, key, options ?? props?.options) | ||
: value; | ||
return Promise.all(apis.map(api => api.setItem(`${prefix}${key}`, filteredValue, options ?? props?.options))).then(() => { | ||
const node = signals.get(key); | ||
node && node[1](); | ||
}); | ||
}; | ||
const remove = (key) => Promise.all(apis.map(api => api.removeItem(`${prefix}${key}`))).then(() => { | ||
const node = signals.get(key); | ||
node && node[1](); | ||
}); | ||
const clear = () => Promise.all(apis.map(async (api) => { | ||
let index = 0, key; | ||
while ((key = await api.key(index++))) { | ||
await api.removeItem(key); | ||
} | ||
})).then(() => { | ||
return; | ||
}); | ||
const toJSON = async () => { | ||
const result = {}; | ||
const addValue = (key, value) => { | ||
if (!result.hasOwnProperty(key)) { | ||
const filteredValue = value && props?.deserializer | ||
? props.deserializer(value, key, props?.options) | ||
: value; | ||
if (filteredValue) { | ||
result[key] = filteredValue; | ||
} | ||
} | ||
}; | ||
await Promise.all(apis.map(async (api) => { | ||
if (typeof api.getAll === "function") { | ||
const values = await api.getAll(); | ||
for (const key of values) { | ||
addValue(key, values[key]); | ||
} | ||
} | ||
else { | ||
let index = 0, key; | ||
while ((key = await api.key(index++))) { | ||
addValue(key, await api.getItem(key)); | ||
} | ||
} | ||
})); | ||
return result; | ||
}; | ||
return [ | ||
store, | ||
setter, | ||
{ | ||
remove, | ||
clear, | ||
toJSON | ||
} | ||
]; | ||
} | ||
/** | ||
* like createSignal, but bound to a localStorage-like API | ||
* ```typescript | ||
* type StorageWithOptions<O> = Storage; // but with options added to setItem | ||
* type StorageProps<T extends string, O> = { | ||
* api?: Storage | StorageWithOptions; | ||
* // or an array thereof, default will be localStorage | ||
* deserializer?: (value: string, key: string, options?: O) => T; | ||
* serializer?: (value: T, key: string, options?: O) => string; | ||
* options?: O; // options | ||
* prefix?: string // will be prefixed to the key | ||
* }; | ||
* createStorage<T extends string>(key: string, props?: StorageProps<T>) => [ | ||
* accessor: Accessor<T>, // basically like `value()` | ||
* setter: Setter<T>, // like `setValue()` | ||
* refetch: () => void // reloads from storage | ||
* ] | ||
* ``` | ||
*/ | ||
export function createStorageSignal(key, initialValue, props) { | ||
const apis = props?.api | ||
? Array.isArray(props.api) | ||
? props.api | ||
: [props.api] | ||
: [globalThis.localStorage].filter(Boolean); | ||
const prefix = props?.prefix ? `${props.prefix}.` : ""; | ||
const read = () => apis.reduce((result, api) => { | ||
if (result !== null || !api) { | ||
return result; | ||
} | ||
const value = api.getItem(`${prefix}${key}`); | ||
if (value !== null && props?.deserializer) { | ||
return props.deserializer(value, key, props?.options); | ||
} | ||
return value; | ||
}, null); | ||
const [accessor, setter] = createSignal(read() ?? initialValue); | ||
createEffect(() => { | ||
const value = accessor(); | ||
const filteredValue = props?.serializer | ||
? props.serializer(value, key, props?.options) | ||
: value; | ||
if (value === null) { | ||
apis.forEach(api => api.removeItem(`${prefix}${key}`)); | ||
} | ||
else { | ||
apis.forEach(api => api.setItem(`${prefix}${key}`, filteredValue, props?.options)); | ||
} | ||
}); | ||
return [ | ||
accessor, | ||
setter, | ||
() => { | ||
const value = read(); | ||
setter(value); | ||
} | ||
]; | ||
} | ||
export const createLocalStorage = createStorage; | ||
export const createSessionStorage = (props) => createStorage({ ...props, api: globalThis.sessionStorage }); | ||
import { | ||
createAsyncStorage, | ||
createLocalStorage, | ||
createSessionStorage, | ||
createStorage, | ||
createStorageSignal | ||
} from "./chunk-V53AARZS.js"; | ||
import "./chunk-JUWXSDKJ.js"; | ||
export { | ||
createAsyncStorage, | ||
createLocalStorage, | ||
createSessionStorage, | ||
createStorage, | ||
createStorageSignal | ||
}; |
@@ -1,15 +0,7 @@ | ||
/** | ||
* adds a `.clear` method to a Storage without one only using `.key`/`.removeItem` | ||
*/ | ||
export const addClearMethod = (storage) => { | ||
if (typeof storage.clear === "function") { | ||
return storage; | ||
} | ||
storage.clear = () => { | ||
let key; | ||
while ((key = storage.key(0))) { | ||
storage.removeItem(key); | ||
} | ||
}; | ||
return storage; | ||
import { | ||
addClearMethod | ||
} from "./chunk-UHHZE4FR.js"; | ||
import "./chunk-JUWXSDKJ.js"; | ||
export { | ||
addClearMethod | ||
}; |
@@ -1,2 +0,2 @@ | ||
export declare type StorageWithOptions<O> = { | ||
declare type StorageWithOptions<O> = { | ||
clear: () => void; | ||
@@ -13,5 +13,5 @@ getItem: (key: string, options?: O) => string | null; | ||
}; | ||
export declare type StorageDeserializer<T, O> = (value: string, key: string, options?: O) => T; | ||
export declare type StorageSerializer<T, O> = (value: T, key: string, options?: O) => string; | ||
export declare type StringStorageProps<A, O, T = string> = { | ||
declare type StorageDeserializer<T, O> = (value: string, key: string, options?: O) => T; | ||
declare type StorageSerializer<T, O> = (value: T, key: string, options?: O) => string; | ||
declare type StringStorageProps<A, O, T = string> = { | ||
api?: A | A[]; | ||
@@ -23,3 +23,3 @@ deserializer?: StorageDeserializer<T, O>; | ||
}; | ||
export declare type AnyStorageProps<A, O, T> = { | ||
declare type AnyStorageProps<A, O, T> = { | ||
api?: A | A[]; | ||
@@ -31,6 +31,6 @@ deserializer: StorageDeserializer<T, O>; | ||
}; | ||
export declare type StorageProps<T, A, O> = T extends string ? StringStorageProps<A, O> : AnyStorageProps<A, O, T>; | ||
export declare type StorageObject<T> = Record<string, T>; | ||
export declare type StorageSetter<T, O> = (key: string, value: T, options?: O) => void; | ||
export declare type StorageActions<T> = { | ||
declare type StorageProps<T, A, O> = T extends string ? StringStorageProps<A, O> : AnyStorageProps<A, O, T>; | ||
declare type StorageObject<T> = Record<string, T>; | ||
declare type StorageSetter<T, O> = (key: string, value: T, options?: O) => void; | ||
declare type StorageActions<T> = { | ||
remove: (key: string) => void; | ||
@@ -42,3 +42,3 @@ clear: () => void; | ||
}; | ||
export declare type AsyncStorage = { | ||
declare type AsyncStorage = { | ||
clear?: () => Promise<void> | void; | ||
@@ -53,3 +53,3 @@ getItem: (key: string) => Promise<string | null> | string | null; | ||
}; | ||
export declare type AsyncStorageWithOptions<O> = { | ||
declare type AsyncStorageWithOptions<O> = { | ||
clear?: () => Promise<void> | void; | ||
@@ -64,5 +64,5 @@ getItem: (key: string, options?: O) => Promise<string | null> | string | null; | ||
}; | ||
export declare type AsyncStorageObject<T> = Record<string, Promise<T | null>>; | ||
export declare type AsyncStorageSetter<T, O> = (key: string, value: T, options?: O) => Promise<void> | void; | ||
export declare type AsyncStorageActions<T> = { | ||
declare type AsyncStorageObject<T> = Record<string, Promise<T | null>>; | ||
declare type AsyncStorageSetter<T, O> = (key: string, value: T, options?: O) => Promise<void> | void; | ||
declare type AsyncStorageActions<T> = { | ||
remove: (key: string) => Promise<void> | void; | ||
@@ -74,3 +74,3 @@ clear: () => Promise<void> | void; | ||
}; | ||
export declare type StorageSignalProps<T, A, O> = StorageProps<T, A, O> & { | ||
declare type StorageSignalProps<T, A, O> = StorageProps<T, A, O> & { | ||
equals?: false | ((prev: T, next: T) => boolean) | undefined; | ||
@@ -80,1 +80,3 @@ name?: string | undefined; | ||
}; | ||
export { AnyStorageProps, AsyncStorage, AsyncStorageActions, AsyncStorageObject, AsyncStorageSetter, AsyncStorageWithOptions, StorageActions, StorageDeserializer, StorageObject, StorageProps, StorageSerializer, StorageSetter, StorageSignalProps, StorageWithOptions, StringStorageProps }; |
{ | ||
"name": "@solid-primitives/storage", | ||
"version": "1.0.4", | ||
"version": "1.0.7", | ||
"description": "Primitive that provides reactive wrappers for storage access", | ||
@@ -8,20 +8,30 @@ "author": "Alex Lohr <alex.lohr@logmein.com>", | ||
"homepage": "https://github.com/davedbase/solid-primitives/tree/main/packages/storage", | ||
"private": false, | ||
"main": "dist/cjs/index.cjs", | ||
"module": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"type": "module", | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/davedbase/solid-primitives.git" | ||
}, | ||
"primitive": { | ||
"name": "storage", | ||
"stage": 3, | ||
"list": [ | ||
"createStorage", | ||
"createCookieStorage", | ||
"createAsyncStorage", | ||
"createStorageSignal", | ||
"createLocalStorage", | ||
"createSessionStorage" | ||
], | ||
"category": "Browser APIs" | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"exports": { | ||
"require": "./dist/cjs/index.cjs", | ||
"import": "./dist/index.js", | ||
"default": "./dist/index.js" | ||
}, | ||
"sideEffects": "false", | ||
"private": false, | ||
"sideEffects": false, | ||
"type": "module", | ||
"main": "./dist/index.cjs", | ||
"module": "./dist/index.js", | ||
"types": "./dist/index.d.ts", | ||
"scripts": { | ||
"prebuild": "npm run clean", | ||
"clean": "rimraf dist/", | ||
"build": "tsc && tsc --target es5 --module commonjs --declaration false --outDir ./dist/cjs", | ||
"build": "tsup", | ||
"test": "uvu -r solid-register" | ||
@@ -40,6 +50,7 @@ }, | ||
"solid-register": "^0.0.18", | ||
"tsup": "^5.10.1", | ||
"typescript": "^4.5.2", | ||
"uvu": "^0.5.2" | ||
}, | ||
"dependencies": { | ||
"peerDependencies": { | ||
"solid-js": "^1.2.5" | ||
@@ -46,0 +57,0 @@ }, |
@@ -1,9 +0,1 @@ | ||
--- | ||
Name: storage | ||
Stage: 3 | ||
Package: "@solid-primitives/storage" | ||
Primitives: createStorage, createCookieStorage, createAsyncStorage, createStorageSignal, createLocalStorage, createSessionStorage | ||
Category: Browser APIs | ||
--- | ||
# @solid-primitives/storage | ||
@@ -17,4 +9,14 @@ | ||
## Installation | ||
``` | ||
npm install @solid-primitives/storage | ||
# or | ||
yarn add @solid-primitives/storage | ||
``` | ||
## How to use it | ||
### Basic Usage | ||
`createStorage` is meant to wrap any localStorage-like API to be as accessible as a [Solid Store](https://www.solidjs.com/docs/latest/api#createstore). The main differences are | ||
@@ -167,3 +169,3 @@ | ||
1.0.4 | ||
1.0.7 | ||
@@ -170,0 +172,0 @@ Patch CJS support. |
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
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
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
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
63127
1558
173
0
6
15
1
- Removedsolid-js@^1.2.5