@cascivo/storage 0.1.17 → 0.1.19

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.
@@ -1,6 +1,5 @@
1
1
  import { ReadonlySignal, Signal } from "@cascivo/core";
2
2
 
3
- //#region src/drivers.d.ts
4
3
  interface StorageDriver {
5
4
  get(key: string): string | null | Promise<string | null>;
6
5
  set(key: string, value: string): void;
@@ -10,8 +9,6 @@ interface StorageDriver {
10
9
  }
11
10
  declare function memoryDriver(): StorageDriver;
12
11
  declare function localStorageDriver(): StorageDriver;
13
- //#endregion
14
- //#region src/persisted-signal.d.ts
15
12
  interface PersistedSignalOptions<T> {
16
13
  driver?: StorageDriver;
17
14
  /** Bump together with `migrate` when the stored shape changes. */
@@ -22,10 +19,7 @@ type PersistedSignal<T> = Signal<T> & {
22
19
  ready: ReadonlySignal<boolean>;
23
20
  };
24
21
  declare function persistedSignal<T>(key: string, initial: T, options?: PersistedSignalOptions<T>): PersistedSignal<T>;
25
- //#endregion
26
- //#region src/indexed-db.d.ts
27
22
  /** Minimal key-value driver over indexedDB — async hydration, no `idb` dependency. */
28
23
  declare function indexedDBDriver(dbName?: string, storeName?: string): StorageDriver;
29
- //#endregion
30
24
  export { type PersistedSignal, type PersistedSignalOptions, type StorageDriver, indexedDBDriver, localStorageDriver, memoryDriver, persistedSignal };
31
25
  //# sourceMappingURL=index.d.mts.map
package/dist/index.js ADDED
@@ -0,0 +1,101 @@
1
+ import { effect as e, signal as t } from "@cascivo/core";
2
+ //#region src/drivers.ts
3
+ function n() {
4
+ let e = /* @__PURE__ */ new Map();
5
+ return {
6
+ get: (t) => e.get(t) ?? null,
7
+ set: (t, n) => {
8
+ e.set(t, n);
9
+ },
10
+ remove: (t) => {
11
+ e.delete(t);
12
+ }
13
+ };
14
+ }
15
+ function r() {
16
+ return typeof window > "u" ? n() : {
17
+ get: (e) => window.localStorage.getItem(e),
18
+ set: (e, t) => {
19
+ try {
20
+ window.localStorage.setItem(e, t);
21
+ } catch {}
22
+ },
23
+ remove: (e) => {
24
+ window.localStorage.removeItem(e);
25
+ },
26
+ subscribe(e, t) {
27
+ let n = (n) => {
28
+ n.storageArea === window.localStorage && n.key === e && t(n.newValue);
29
+ };
30
+ return window.addEventListener("storage", n), () => window.removeEventListener("storage", n);
31
+ }
32
+ };
33
+ }
34
+ //#endregion
35
+ //#region src/persisted-signal.ts
36
+ function i(n, i, a = {}) {
37
+ let o = a.driver ?? r(), s = a.version ?? 1, c = t(i), l = t(!1), u = !1;
38
+ function d(e) {
39
+ if (e !== null) try {
40
+ let t = JSON.parse(e);
41
+ if (typeof t != "object" || !t || typeof t.v != "number") return;
42
+ if (t.v !== s) {
43
+ if (!a.migrate) return;
44
+ let e = a.migrate(t.value, t.v);
45
+ return o.set(n, JSON.stringify({
46
+ v: s,
47
+ value: e
48
+ })), e;
49
+ }
50
+ return t.value;
51
+ } catch {
52
+ return;
53
+ }
54
+ }
55
+ function f(e) {
56
+ let t = d(e);
57
+ t !== void 0 && (u = !0, c.value = t, u = !1);
58
+ }
59
+ let p = o.get(n);
60
+ p instanceof Promise ? p.then((e) => {
61
+ f(e), l.value = !0;
62
+ }) : (f(p), l.value = !0), e(() => {
63
+ let e = c.value;
64
+ !l.value || u || o.set(n, JSON.stringify({
65
+ v: s,
66
+ value: e
67
+ }));
68
+ }), o.subscribe?.(n, f);
69
+ let m = c;
70
+ return m.ready = l, m;
71
+ }
72
+ //#endregion
73
+ //#region src/indexed-db.ts
74
+ function a(e = "cascivo", t = "kv") {
75
+ if (typeof indexedDB > "u") return n();
76
+ let r;
77
+ function i() {
78
+ return r ??= new Promise((n, r) => {
79
+ let i = indexedDB.open(e, 1);
80
+ i.onupgradeneeded = () => i.result.createObjectStore(t), i.onsuccess = () => n(i.result), i.onerror = () => r(i.error ?? /* @__PURE__ */ Error(`indexedDB open failed: ${e}`));
81
+ }), r;
82
+ }
83
+ async function a(e, n) {
84
+ let r = await i();
85
+ return new Promise((i, a) => {
86
+ let o = n(r.transaction(t, e).objectStore(t));
87
+ o.onsuccess = () => i(o.result), o.onerror = () => a(o.error ?? /* @__PURE__ */ Error("indexedDB request failed"));
88
+ });
89
+ }
90
+ return {
91
+ get: async (e) => await a("readonly", (t) => t.get(e)) ?? null,
92
+ set: (e, t) => {
93
+ a("readwrite", (n) => n.put(t, e));
94
+ },
95
+ remove: (e) => {
96
+ a("readwrite", (t) => t.delete(e));
97
+ }
98
+ };
99
+ }
100
+ //#endregion
101
+ export { a as indexedDBDriver, r as localStorageDriver, n as memoryDriver, i as persistedSignal };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cascivo/storage",
3
- "version": "0.1.17",
3
+ "version": "0.1.19",
4
4
  "private": false,
5
5
  "description": "Persisted signals over localStorage/IndexedDB for cascivo — SSR-safe",
6
6
  "keywords": [
@@ -26,13 +26,13 @@
26
26
  "dist"
27
27
  ],
28
28
  "type": "module",
29
- "types": "./dist/index.d.mts",
29
+ "types": "./dist/index.d.ts",
30
30
  "exports": {
31
31
  "./package.json": "./package.json",
32
32
  ".": {
33
- "types": "./dist/index.d.mts",
34
- "import": "./dist/index.mjs",
35
- "default": "./dist/index.mjs"
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.js",
35
+ "default": "./dist/index.js"
36
36
  }
37
37
  },
38
38
  "publishConfig": {
@@ -40,7 +40,7 @@
40
40
  "provenance": true
41
41
  },
42
42
  "dependencies": {
43
- "@cascivo/core": "^0.6.0"
43
+ "@cascivo/core": "^0.7.1"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@preact/signals-react": "^3",
@@ -52,7 +52,7 @@
52
52
  "@preact/signals-react": ">=3.0.0"
53
53
  },
54
54
  "scripts": {
55
- "build": "vp pack",
55
+ "build": "vp build && node scripts/flatten-types.mjs && node scripts/check-types-flat.mjs",
56
56
  "check": "tsc --noEmit",
57
57
  "test": "vp test",
58
58
  "dev": "vp pack --watch"
@@ -1 +0,0 @@
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 DELETED
@@ -1,129 +0,0 @@
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
@@ -1 +0,0 @@
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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"}