@cascivo/storage 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +19 -0
- package/dist/index.d.mts +31 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +129 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
- package/readme.body.md +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 urbanisierung
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<!-- generated by scripts/readme/generate.ts — edit readme.body.md, not this file -->
|
|
2
|
+
|
|
3
|
+
# @cascivo/storage
|
|
4
|
+
|
|
5
|
+
> Persisted signals over localStorage/IndexedDB for cascade — SSR-safe
|
|
6
|
+
|
|
7
|
+
[cascivo.com](https://cascivo.com) · [Docs](https://docs.cascivo.com) · [Storybook](https://storybook.cascivo.com) · [GitHub](https://github.com/urbanisierung/cascivo)
|
|
8
|
+
|
|
9
|
+
Persisted signals for cascade — sync signal state to `localStorage` or `IndexedDB` with SSR-safe initialisation. Drop-in replacement for plain signals when persistence is needed.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm add @cascivo/storage
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
[cascivo.com](https://cascivo.com) · [Docs](https://docs.cascivo.com) · [Storybook](https://storybook.cascivo.com) · [GitHub](https://github.com/urbanisierung/cascivo) · AI agents: use [`@cascivo/mcp`](https://github.com/urbanisierung/cascivo/tree/main/packages/mcp) and [`registry.json`](https://github.com/urbanisierung/cascivo/blob/main/registry.json) · MIT
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ReadonlySignal, Signal } from "@cascivo/core";
|
|
2
|
+
|
|
3
|
+
//#region src/drivers.d.ts
|
|
4
|
+
interface StorageDriver {
|
|
5
|
+
get(key: string): string | null | Promise<string | null>;
|
|
6
|
+
set(key: string, value: string): void;
|
|
7
|
+
remove(key: string): void;
|
|
8
|
+
/** Optional change feed (e.g. the cross-tab `storage` event). */
|
|
9
|
+
subscribe?(key: string, onChange: (raw: string | null) => void): () => void;
|
|
10
|
+
}
|
|
11
|
+
declare function memoryDriver(): StorageDriver;
|
|
12
|
+
declare function localStorageDriver(): StorageDriver;
|
|
13
|
+
//#endregion
|
|
14
|
+
//#region src/persisted-signal.d.ts
|
|
15
|
+
interface PersistedSignalOptions<T> {
|
|
16
|
+
driver?: StorageDriver;
|
|
17
|
+
/** Bump together with `migrate` when the stored shape changes. */
|
|
18
|
+
version?: number;
|
|
19
|
+
migrate?: (value: unknown, fromVersion: number) => T;
|
|
20
|
+
}
|
|
21
|
+
type PersistedSignal<T> = Signal<T> & {
|
|
22
|
+
ready: ReadonlySignal<boolean>;
|
|
23
|
+
};
|
|
24
|
+
declare function persistedSignal<T>(key: string, initial: T, options?: PersistedSignalOptions<T>): PersistedSignal<T>;
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/indexed-db.d.ts
|
|
27
|
+
/** Minimal key-value driver over indexedDB — async hydration, no `idb` dependency. */
|
|
28
|
+
declare function indexedDBDriver(dbName?: string, storeName?: string): StorageDriver;
|
|
29
|
+
//#endregion
|
|
30
|
+
export { type PersistedSignal, type PersistedSignalOptions, type StorageDriver, indexedDBDriver, localStorageDriver, memoryDriver, persistedSignal };
|
|
31
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/drivers.ts","../src/persisted-signal.ts","../src/indexed-db.ts"],"mappings":";;;UAAiB,aAAA;EACf,GAAA,CAAI,GAAA,2BAA8B,OAAO;EACzC,GAAA,CAAI,GAAA,UAAa,KAAA;EACjB,MAAA,CAAO,GAAA;EAHqB;EAK5B,SAAA,EAAW,GAAA,UAAa,QAAA,GAAW,GAAA;AAAA;AAAA,iBAGrB,YAAA,IAAgB,aAAa;AAAA,iBAa7B,kBAAA,IAAsB,aAAa;;;UCZlC,sBAAA;EACf,MAAA,GAAS,aAAA;EDVmB;ECY5B,OAAA;EACA,OAAA,IAAW,KAAA,WAAgB,WAAA,aAAwB,CAAC;AAAA;AAAA,KAG1C,eAAA,MAAqB,MAAA,CAAO,CAAA;EAAO,KAAA,EAAO,cAAA;AAAA;AAAA,iBAEtC,eAAA,IACd,GAAA,UACA,OAAA,EAAS,CAAA,EACT,OAAA,GAAS,sBAAA,CAAuB,CAAA,IAC/B,eAAA,CAAgB,CAAA;;;;iBCnBH,eAAA,CAAgB,MAAA,WAAoB,SAAA,YAAmB,aAAa"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { effect, signal } from "@cascivo/core";
|
|
2
|
+
//#region src/drivers.ts
|
|
3
|
+
function memoryDriver() {
|
|
4
|
+
const store = /* @__PURE__ */ new Map();
|
|
5
|
+
return {
|
|
6
|
+
get: (key) => store.get(key) ?? null,
|
|
7
|
+
set: (key, value) => {
|
|
8
|
+
store.set(key, value);
|
|
9
|
+
},
|
|
10
|
+
remove: (key) => {
|
|
11
|
+
store.delete(key);
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function localStorageDriver() {
|
|
16
|
+
if (typeof window === "undefined") return memoryDriver();
|
|
17
|
+
return {
|
|
18
|
+
get: (key) => window.localStorage.getItem(key),
|
|
19
|
+
set: (key, value) => {
|
|
20
|
+
try {
|
|
21
|
+
window.localStorage.setItem(key, value);
|
|
22
|
+
} catch {}
|
|
23
|
+
},
|
|
24
|
+
remove: (key) => {
|
|
25
|
+
window.localStorage.removeItem(key);
|
|
26
|
+
},
|
|
27
|
+
subscribe(key, onChange) {
|
|
28
|
+
const listener = (event) => {
|
|
29
|
+
if (event.storageArea === window.localStorage && event.key === key) onChange(event.newValue);
|
|
30
|
+
};
|
|
31
|
+
window.addEventListener("storage", listener);
|
|
32
|
+
return () => window.removeEventListener("storage", listener);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
//#region src/persisted-signal.ts
|
|
38
|
+
function persistedSignal(key, initial, options = {}) {
|
|
39
|
+
const driver = options.driver ?? localStorageDriver();
|
|
40
|
+
const version = options.version ?? 1;
|
|
41
|
+
const value = signal(initial);
|
|
42
|
+
const ready = signal(false);
|
|
43
|
+
let adopting = false;
|
|
44
|
+
function decode(raw) {
|
|
45
|
+
if (raw === null) return void 0;
|
|
46
|
+
try {
|
|
47
|
+
const envelope = JSON.parse(raw);
|
|
48
|
+
if (typeof envelope !== "object" || envelope === null || typeof envelope.v !== "number") return;
|
|
49
|
+
if (envelope.v !== version) {
|
|
50
|
+
if (!options.migrate) return void 0;
|
|
51
|
+
const migrated = options.migrate(envelope.value, envelope.v);
|
|
52
|
+
driver.set(key, JSON.stringify({
|
|
53
|
+
v: version,
|
|
54
|
+
value: migrated
|
|
55
|
+
}));
|
|
56
|
+
return migrated;
|
|
57
|
+
}
|
|
58
|
+
return envelope.value;
|
|
59
|
+
} catch {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function adopt(raw) {
|
|
64
|
+
const decoded = decode(raw);
|
|
65
|
+
if (decoded !== void 0) {
|
|
66
|
+
adopting = true;
|
|
67
|
+
value.value = decoded;
|
|
68
|
+
adopting = false;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const raw = driver.get(key);
|
|
72
|
+
if (raw instanceof Promise) raw.then((resolved) => {
|
|
73
|
+
adopt(resolved);
|
|
74
|
+
ready.value = true;
|
|
75
|
+
});
|
|
76
|
+
else {
|
|
77
|
+
adopt(raw);
|
|
78
|
+
ready.value = true;
|
|
79
|
+
}
|
|
80
|
+
effect(() => {
|
|
81
|
+
const next = value.value;
|
|
82
|
+
if (!ready.value || adopting) return;
|
|
83
|
+
driver.set(key, JSON.stringify({
|
|
84
|
+
v: version,
|
|
85
|
+
value: next
|
|
86
|
+
}));
|
|
87
|
+
});
|
|
88
|
+
driver.subscribe?.(key, adopt);
|
|
89
|
+
const persisted = value;
|
|
90
|
+
persisted.ready = ready;
|
|
91
|
+
return persisted;
|
|
92
|
+
}
|
|
93
|
+
//#endregion
|
|
94
|
+
//#region src/indexed-db.ts
|
|
95
|
+
/** Minimal key-value driver over indexedDB — async hydration, no `idb` dependency. */
|
|
96
|
+
function indexedDBDriver(dbName = "cascivo", storeName = "kv") {
|
|
97
|
+
if (typeof indexedDB === "undefined") return memoryDriver();
|
|
98
|
+
let dbPromise;
|
|
99
|
+
function open() {
|
|
100
|
+
dbPromise ??= new Promise((resolve, reject) => {
|
|
101
|
+
const request = indexedDB.open(dbName, 1);
|
|
102
|
+
request.onupgradeneeded = () => request.result.createObjectStore(storeName);
|
|
103
|
+
request.onsuccess = () => resolve(request.result);
|
|
104
|
+
request.onerror = () => reject(request.error ?? /* @__PURE__ */ new Error(`indexedDB open failed: ${dbName}`));
|
|
105
|
+
});
|
|
106
|
+
return dbPromise;
|
|
107
|
+
}
|
|
108
|
+
async function withStore(mode, run) {
|
|
109
|
+
const db = await open();
|
|
110
|
+
return new Promise((resolve, reject) => {
|
|
111
|
+
const request = run(db.transaction(storeName, mode).objectStore(storeName));
|
|
112
|
+
request.onsuccess = () => resolve(request.result);
|
|
113
|
+
request.onerror = () => reject(request.error ?? /* @__PURE__ */ new Error("indexedDB request failed"));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
get: async (key) => await withStore("readonly", (store) => store.get(key)) ?? null,
|
|
118
|
+
set: (key, value) => {
|
|
119
|
+
withStore("readwrite", (store) => store.put(value, key));
|
|
120
|
+
},
|
|
121
|
+
remove: (key) => {
|
|
122
|
+
withStore("readwrite", (store) => store.delete(key));
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//#endregion
|
|
127
|
+
export { indexedDBDriver, localStorageDriver, memoryDriver, persistedSignal };
|
|
128
|
+
|
|
129
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/drivers.ts","../src/persisted-signal.ts","../src/indexed-db.ts"],"sourcesContent":["export interface StorageDriver {\n get(key: string): string | null | Promise<string | null>\n set(key: string, value: string): void\n remove(key: string): void\n /** Optional change feed (e.g. the cross-tab `storage` event). */\n subscribe?(key: string, onChange: (raw: string | null) => void): () => void\n}\n\nexport function memoryDriver(): StorageDriver {\n const store = new Map<string, string>()\n return {\n get: (key) => store.get(key) ?? null,\n set: (key, value) => {\n store.set(key, value)\n },\n remove: (key) => {\n store.delete(key)\n },\n }\n}\n\nexport function localStorageDriver(): StorageDriver {\n // SSR: no window — persistence becomes a no-op, the signal still works.\n if (typeof window === 'undefined') return memoryDriver()\n return {\n get: (key) => window.localStorage.getItem(key),\n set: (key, value) => {\n try {\n window.localStorage.setItem(key, value)\n } catch {\n // Quota exceeded / private mode: persistence is best-effort.\n }\n },\n remove: (key) => {\n window.localStorage.removeItem(key)\n },\n subscribe(key, onChange) {\n const listener = (event: StorageEvent) => {\n if (event.storageArea === window.localStorage && event.key === key) {\n onChange(event.newValue)\n }\n }\n window.addEventListener('storage', listener)\n return () => window.removeEventListener('storage', listener)\n },\n }\n}\n","import { effect, signal } from '@cascivo/core'\nimport type { ReadonlySignal, Signal } from '@cascivo/core'\nimport { localStorageDriver, type StorageDriver } from './drivers'\n\ninterface Envelope {\n v: number\n value: unknown\n}\n\nexport interface PersistedSignalOptions<T> {\n driver?: StorageDriver\n /** Bump together with `migrate` when the stored shape changes. */\n version?: number\n migrate?: (value: unknown, fromVersion: number) => T\n}\n\nexport type PersistedSignal<T> = Signal<T> & { ready: ReadonlySignal<boolean> }\n\nexport function persistedSignal<T>(\n key: string,\n initial: T,\n options: PersistedSignalOptions<T> = {},\n): PersistedSignal<T> {\n const driver = options.driver ?? localStorageDriver()\n const version = options.version ?? 1\n const value = signal(initial)\n const ready = signal(false)\n // Suppresses the write-through effect while adopting a value FROM storage.\n let adopting = false\n\n function decode(raw: string | null): T | undefined {\n if (raw === null) return undefined\n try {\n const envelope = JSON.parse(raw) as Envelope\n if (typeof envelope !== 'object' || envelope === null || typeof envelope.v !== 'number') {\n return undefined\n }\n if (envelope.v !== version) {\n if (!options.migrate) return undefined\n const migrated = options.migrate(envelope.value, envelope.v)\n // Persist the migration immediately so old-format data doesn't linger.\n driver.set(key, JSON.stringify({ v: version, value: migrated } satisfies Envelope))\n return migrated\n }\n return envelope.value as T\n } catch {\n return undefined\n }\n }\n\n function adopt(raw: string | null): void {\n const decoded = decode(raw)\n if (decoded !== undefined) {\n adopting = true\n value.value = decoded\n adopting = false\n }\n }\n\n const raw = driver.get(key)\n if (raw instanceof Promise) {\n void raw.then((resolved) => {\n adopt(resolved)\n ready.value = true\n })\n } else {\n adopt(raw)\n ready.value = true\n }\n\n effect(() => {\n const next = value.value\n if (!ready.value || adopting) return\n driver.set(key, JSON.stringify({ v: version, value: next } satisfies Envelope))\n })\n\n driver.subscribe?.(key, adopt)\n\n const persisted = value as PersistedSignal<T>\n ;(persisted as { ready: ReadonlySignal<boolean> }).ready = ready\n return persisted\n}\n","import { memoryDriver, type StorageDriver } from './drivers'\n\n/** Minimal key-value driver over indexedDB — async hydration, no `idb` dependency. */\nexport function indexedDBDriver(dbName = 'cascivo', storeName = 'kv'): StorageDriver {\n if (typeof indexedDB === 'undefined') return memoryDriver()\n\n let dbPromise: Promise<IDBDatabase> | undefined\n function open(): Promise<IDBDatabase> {\n dbPromise ??= new Promise((resolve, reject) => {\n const request = indexedDB.open(dbName, 1)\n request.onupgradeneeded = () => request.result.createObjectStore(storeName)\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error ?? new Error(`indexedDB open failed: ${dbName}`))\n })\n return dbPromise\n }\n\n async function withStore<T>(\n mode: IDBTransactionMode,\n run: (store: IDBObjectStore) => IDBRequest<T>,\n ): Promise<T> {\n const db = await open()\n return new Promise<T>((resolve, reject) => {\n const request = run(db.transaction(storeName, mode).objectStore(storeName))\n request.onsuccess = () => resolve(request.result)\n request.onerror = () => reject(request.error ?? new Error('indexedDB request failed'))\n })\n }\n\n return {\n get: async (key) =>\n ((await withStore('readonly', (store) => store.get(key))) as string | undefined) ?? null,\n set: (key, value) => {\n void withStore('readwrite', (store) => store.put(value, key))\n },\n remove: (key) => {\n void withStore('readwrite', (store) => store.delete(key))\n },\n }\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cascivo/storage",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "Persisted signals over localStorage/IndexedDB for cascade — SSR-safe",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"cascivo",
|
|
8
|
+
"css",
|
|
9
|
+
"design-system",
|
|
10
|
+
"indexeddb",
|
|
11
|
+
"localstorage",
|
|
12
|
+
"react",
|
|
13
|
+
"signals",
|
|
14
|
+
"storage"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/urbanisierung/cascivo/tree/main/packages/storage#readme",
|
|
17
|
+
"bugs": "https://github.com/urbanisierung/cascivo/issues",
|
|
18
|
+
"license": "MIT",
|
|
19
|
+
"author": "urbanisierung",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/urbanisierung/cascivo.git",
|
|
23
|
+
"directory": "packages/storage"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"type": "module",
|
|
29
|
+
"types": "./dist/index.d.mts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"import": "./dist/index.mjs",
|
|
33
|
+
"types": "./dist/index.d.mts",
|
|
34
|
+
"default": "./dist/index.mjs"
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
"publishConfig": {
|
|
38
|
+
"access": "public",
|
|
39
|
+
"provenance": true
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@cascivo/core": "^0.1.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@preact/signals-react": "^3",
|
|
46
|
+
"jsdom": "^29",
|
|
47
|
+
"typescript": "^5",
|
|
48
|
+
"vite-plus": "^0.1.24"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"@preact/signals-react": ">=2.0.0"
|
|
52
|
+
},
|
|
53
|
+
"scripts": {
|
|
54
|
+
"build": "vp pack",
|
|
55
|
+
"check": "tsc --noEmit",
|
|
56
|
+
"test": "vp test",
|
|
57
|
+
"dev": "vp pack --watch"
|
|
58
|
+
}
|
|
59
|
+
}
|
package/readme.body.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Persisted signals for cascade — sync signal state to `localStorage` or `IndexedDB` with SSR-safe initialisation. Drop-in replacement for plain signals when persistence is needed.
|