@echojs-ecosystem/persist 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/README.md ADDED
@@ -0,0 +1,83 @@
1
+ <div align="center">
2
+
3
+ # @echojs-ecosystem/persist
4
+
5
+ **Storage extensions for stores and form fields — localStorage, cookies, IndexedDB, and more.**
6
+
7
+ [![npm](https://img.shields.io/npm/v/@echojs-ecosystem/persist)](https://www.npmjs.com/package/@echojs-ecosystem/persist)
8
+ [![docs](https://img.shields.io/badge/docs-echojs.dev-blue)](https://echojs.dev/docs/packages/persist)
9
+
10
+ </div>
11
+
12
+ ---
13
+
14
+ Universal persistence via **`.extend()`**. Works with [`@echojs-ecosystem/store`](https://www.npmjs.com/package/@echojs-ecosystem/store) and any object matching the persistable contract (`value` / `set` / `subscribe`).
15
+
16
+ ## Features
17
+
18
+ - **Adapters** — `localStorage`, `sessionStorage`, `cookie`, `IndexedDB`, in-memory
19
+ - **`.extend(withLocalStorage(...))`** — plug-and-play for stores & fields
20
+ - **Versioning & migration** — schema version with `migrate` callback
21
+ - **TTL** — auto-expire stale records on hydrate
22
+ - **Select / merge** — persist slices of state (e.g. token only)
23
+ - **SSR-safe** — memory fallback when `window` is unavailable
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ npm install @echojs-ecosystem/persist @echojs-ecosystem/store
29
+ ```
30
+
31
+ ## Quick start
32
+
33
+ ```ts
34
+ import { createStore } from "@echojs-ecosystem/store";
35
+ import { withLocalStorage } from "@echojs-ecosystem/persist";
36
+
37
+ export const themeStore = createStore("dark", { name: "theme" }).extend(
38
+ withLocalStorage({ key: "app-theme", version: 1 }),
39
+ );
40
+
41
+ // Imperative control
42
+ await themeStore.persist.hydrate();
43
+ await themeStore.persist.save();
44
+ themeStore.persist.$hydrated.value();
45
+ ```
46
+
47
+ ## Adapters
48
+
49
+ ```ts
50
+ import { withSessionStorage, withCookie, withIndexedDB } from "@echojs-ecosystem/persist";
51
+
52
+ createStore(filters).extend(withSessionStorage({ key: "filters", version: 1 }));
53
+ createStore(token).extend(withCookie({ key: "token", path: "/", sameSite: "lax" }));
54
+ createStore(cache).extend(withIndexedDB({ key: "cache", dbName: "echojs", storeName: "kv" }));
55
+ ```
56
+
57
+ ## Persist record
58
+
59
+ ```ts
60
+ type PersistRecord<T> = {
61
+ version: number;
62
+ createdAt: number;
63
+ updatedAt: number;
64
+ expiresAt?: number;
65
+ data: T;
66
+ };
67
+ ```
68
+
69
+ ## API
70
+
71
+ | Export | Description |
72
+ |--------|-------------|
73
+ | `withLocalStorage` | Browser localStorage (+ optional tab sync) |
74
+ | `withSessionStorage` | sessionStorage |
75
+ | `withCookie` | document.cookie |
76
+ | `withIndexedDB` | Async IndexedDB KV |
77
+ | `withMemoryStorage` | Tests & SSR |
78
+ | `withStorage` | Custom adapter |
79
+ | `persist` | Attach without `.extend()` |
80
+
81
+ ## Documentation
82
+
83
+ [echojs.dev/docs/packages/persist](https://echojs.dev/docs/packages/persist)
@@ -0,0 +1,148 @@
1
+ import { Signal } from '@echojs-ecosystem/reactivity';
2
+
3
+ type Persistable<Value> = {
4
+ value(): Value;
5
+ set(value: Value): void;
6
+ subscribe(listener: (value: Value, prevValue: Value) => void): () => void;
7
+ };
8
+ type Serializer<T> = {
9
+ serialize(value: T): string;
10
+ deserialize(value: string): T;
11
+ };
12
+ type PersistRecord<Snapshot> = {
13
+ version: number;
14
+ createdAt: number;
15
+ updatedAt: number;
16
+ expiresAt?: number;
17
+ data: Snapshot;
18
+ };
19
+ type MigrateContext = {
20
+ data: unknown;
21
+ version: number;
22
+ currentVersion: number;
23
+ };
24
+ type PersistOptions<Value, Snapshot = Value> = {
25
+ key: string;
26
+ version?: number;
27
+ ttl?: number;
28
+ hydrate?: boolean;
29
+ saveInitial?: boolean;
30
+ debounce?: number;
31
+ syncTabs?: boolean;
32
+ serializer?: Serializer<PersistRecord<Snapshot>>;
33
+ select?: (value: Value) => Snapshot;
34
+ merge?: (currentValue: Value, snapshot: Snapshot) => Value;
35
+ migrate?: (ctx: MigrateContext) => Snapshot;
36
+ validate?: (data: unknown) => data is Snapshot;
37
+ onHydrate?: (ctx: {
38
+ value: Value;
39
+ snapshot: Snapshot;
40
+ }) => void;
41
+ onSave?: (ctx: {
42
+ value: Value;
43
+ snapshot: Snapshot;
44
+ }) => void;
45
+ onError?: (error: unknown) => void;
46
+ };
47
+ type StorageAdapter = {
48
+ kind: string;
49
+ getItem(key: string): string | null | Promise<string | null>;
50
+ setItem(key: string, value: string): void | Promise<void>;
51
+ removeItem(key: string): void | Promise<void>;
52
+ subscribe?(key: string, listener: (value: string | null) => void): () => void;
53
+ };
54
+ type PersistController<Value, Snapshot = Value> = {
55
+ key: string;
56
+ hydrate(): Promise<void> | void;
57
+ save(): Promise<void> | void;
58
+ clear(): Promise<void> | void;
59
+ pause(): void;
60
+ resume(): void;
61
+ $hydrated: Signal<boolean>;
62
+ $pending: Signal<boolean>;
63
+ $error: Signal<unknown | null>;
64
+ };
65
+ type PersistExtensionResult<Value, Snapshot = Value> = {
66
+ persist: PersistController<Value, Snapshot>;
67
+ };
68
+ type PersistExtension<Value, Snapshot = Value> = (target: Persistable<Value>) => PersistExtensionResult<Value, Snapshot>;
69
+ type CookiePersistOptions<Value, Snapshot = Value> = PersistOptions<Value, Snapshot> & {
70
+ path?: string;
71
+ domain?: string;
72
+ sameSite?: "strict" | "lax" | "none";
73
+ secure?: boolean;
74
+ maxAge?: number;
75
+ };
76
+ type IndexedDBPersistOptions<Value, Snapshot = Value> = PersistOptions<Value, Snapshot> & {
77
+ dbName?: string;
78
+ storeName?: string;
79
+ };
80
+ type FieldLike<Value> = Persistable<Value> & {
81
+ kind?: "field";
82
+ name?: string;
83
+ reset?(): void;
84
+ };
85
+ type FieldArrayLike<Item> = Persistable<Item[]> & {
86
+ kind?: "field-array";
87
+ name?: string;
88
+ push?(item: Item): void;
89
+ remove?(index: number): void;
90
+ reset?(): void;
91
+ };
92
+ type PersistableForm<Value extends object> = Persistable<Value> & {
93
+ kind?: "form";
94
+ reset?(): void;
95
+ };
96
+
97
+ declare const withStorage: <Value, Snapshot = Value>(adapter: StorageAdapter, options: PersistOptions<Value, Snapshot>) => PersistExtension<Value, Snapshot>;
98
+
99
+ declare const persist: <T extends Persistable<Value>, Value, Snapshot = Value>(target: T, extension: PersistExtension<Value, Snapshot>) => T & PersistExtensionResult<Value, Snapshot>;
100
+ declare const persistField: <Value, Snapshot = Value>(field: Persistable<Value>, extension: PersistExtension<Value, Snapshot>) => Persistable<Value> & PersistExtensionResult<Value, Snapshot>;
101
+ declare const persistFieldArray: <Item, Snapshot = Item[]>(fieldArray: Persistable<Item[]>, extension: PersistExtension<Item[], Snapshot>) => Persistable<Item[]> & PersistExtensionResult<Item[], Snapshot>;
102
+
103
+ declare const withLocalStorage: <Value, Snapshot = Value>(options: PersistOptions<Value, Snapshot>) => PersistExtension<Value, Snapshot>;
104
+
105
+ declare const withSessionStorage: <Value, Snapshot = Value>(options: PersistOptions<Value, Snapshot>) => PersistExtension<Value, Snapshot>;
106
+
107
+ declare const withCookie: <Value, Snapshot = Value>(options: CookiePersistOptions<Value, Snapshot>) => PersistExtension<Value, Snapshot>;
108
+
109
+ declare const withIndexedDB: <Value, Snapshot = Value>(options: IndexedDBPersistOptions<Value, Snapshot>) => PersistExtension<Value, Snapshot>;
110
+
111
+ declare const withMemoryStorage: <Value, Snapshot = Value>(options: PersistOptions<Value, Snapshot>) => PersistExtension<Value, Snapshot>;
112
+
113
+ type MemoryStorageAdapter = StorageAdapter & {
114
+ clear(): void;
115
+ entries(): ReadonlyMap<string, string>;
116
+ };
117
+ declare const createMemoryStorageAdapter: () => MemoryStorageAdapter;
118
+
119
+ declare const createLocalStorageAdapter: () => StorageAdapter;
120
+
121
+ declare const createSessionStorageAdapter: () => StorageAdapter;
122
+
123
+ type CookieAdapterOptions = {
124
+ path?: string;
125
+ domain?: string;
126
+ sameSite?: "strict" | "lax" | "none";
127
+ secure?: boolean;
128
+ maxAge?: number;
129
+ };
130
+ declare const createCookieStorageAdapter: (cookieOptions?: CookieAdapterOptions) => StorageAdapter;
131
+
132
+ type IndexedDBAdapterOptions = {
133
+ dbName?: string;
134
+ storeName?: string;
135
+ };
136
+ declare const createIndexedDBStorageAdapter: (options?: IndexedDBAdapterOptions) => StorageAdapter;
137
+
138
+ declare const jsonSerializer: Serializer<unknown>;
139
+ declare const createJsonSerializer: <T>() => Serializer<T>;
140
+
141
+ declare const createPersistRecord: <Snapshot>(data: Snapshot, options: {
142
+ version: number;
143
+ ttl?: number;
144
+ createdAt?: number;
145
+ }) => PersistRecord<Snapshot>;
146
+ declare const isRecordExpired: (record: PersistRecord<unknown>) => boolean;
147
+
148
+ export { type CookiePersistOptions, type FieldArrayLike, type FieldLike, type IndexedDBPersistOptions, type MigrateContext, type PersistController, type PersistExtension, type PersistExtensionResult, type PersistOptions, type PersistRecord, type Persistable, type PersistableForm, type Serializer, type StorageAdapter, createCookieStorageAdapter, createIndexedDBStorageAdapter, createJsonSerializer, createLocalStorageAdapter, createMemoryStorageAdapter, createPersistRecord, createSessionStorageAdapter, isRecordExpired, jsonSerializer, persist, persistField, persistFieldArray, withCookie, withIndexedDB, withLocalStorage, withMemoryStorage, withSessionStorage, withStorage };
package/dist/index.js ADDED
@@ -0,0 +1,660 @@
1
+ import { signal } from '@echojs-ecosystem/reactivity';
2
+
3
+ // src/core/persist.ts
4
+
5
+ // src/core/debounce.ts
6
+ var debounce = (fn, ms) => {
7
+ let timer;
8
+ const debounced = ((...args) => {
9
+ if (timer != null) {
10
+ clearTimeout(timer);
11
+ }
12
+ timer = setTimeout(() => {
13
+ timer = void 0;
14
+ fn(...args);
15
+ }, ms);
16
+ });
17
+ debounced.cancel = () => {
18
+ if (timer != null) {
19
+ clearTimeout(timer);
20
+ timer = void 0;
21
+ }
22
+ };
23
+ debounced.flush = () => {
24
+ if (timer == null) {
25
+ return;
26
+ }
27
+ clearTimeout(timer);
28
+ timer = void 0;
29
+ fn();
30
+ };
31
+ return debounced;
32
+ };
33
+
34
+ // src/core/record.ts
35
+ var createPersistRecord = (data, options) => {
36
+ const now = Date.now();
37
+ const createdAt = options.createdAt ?? now;
38
+ return {
39
+ version: options.version,
40
+ createdAt,
41
+ updatedAt: now,
42
+ expiresAt: options.ttl != null ? now + options.ttl : void 0,
43
+ data
44
+ };
45
+ };
46
+ var isRecordExpired = (record) => {
47
+ if (record.expiresAt == null) {
48
+ return false;
49
+ }
50
+ return Date.now() >= record.expiresAt;
51
+ };
52
+ var touchPersistRecord = (record, data, options) => {
53
+ const now = Date.now();
54
+ return {
55
+ version: options.version,
56
+ createdAt: record.createdAt,
57
+ updatedAt: now,
58
+ expiresAt: options.ttl != null ? now + options.ttl : record.expiresAt,
59
+ data
60
+ };
61
+ };
62
+
63
+ // src/core/utils.ts
64
+ var isDev = () => {
65
+ if (typeof import.meta !== "undefined" && "env" in import.meta) {
66
+ const meta = import.meta;
67
+ return meta.env?.DEV === true || meta.env?.MODE === "development";
68
+ }
69
+ return false;
70
+ };
71
+ var warnDev = (message) => {
72
+ if (!isDev()) {
73
+ return;
74
+ }
75
+ if (typeof console !== "undefined" && typeof console.warn === "function") {
76
+ console.warn(`[@echojs-ecosystem/persist] ${message}`);
77
+ }
78
+ };
79
+ var resolveAsync = async (value) => {
80
+ return await Promise.resolve(value);
81
+ };
82
+ var hasWindow = () => {
83
+ return typeof window !== "undefined";
84
+ };
85
+ var hasDocument = () => {
86
+ return typeof document !== "undefined";
87
+ };
88
+ var hasLocalStorage = () => {
89
+ if (!hasWindow()) {
90
+ return false;
91
+ }
92
+ try {
93
+ const storage = window.localStorage;
94
+ const key = "__echojs_persist_test__";
95
+ storage.setItem(key, "1");
96
+ storage.removeItem(key);
97
+ return true;
98
+ } catch {
99
+ return false;
100
+ }
101
+ };
102
+ var hasSessionStorage = () => {
103
+ if (!hasWindow()) {
104
+ return false;
105
+ }
106
+ try {
107
+ const storage = window.sessionStorage;
108
+ const key = "__echojs_persist_test__";
109
+ storage.setItem(key, "1");
110
+ storage.removeItem(key);
111
+ return true;
112
+ } catch {
113
+ return false;
114
+ }
115
+ };
116
+ var hasIndexedDB = () => {
117
+ return hasWindow() && "indexedDB" in window;
118
+ };
119
+
120
+ // src/core/hydration.ts
121
+ var readPersistRecord = async (adapter, key, serializer) => {
122
+ const raw = await resolveAsync(adapter.getItem(key));
123
+ if (raw == null || raw === "") {
124
+ return null;
125
+ }
126
+ const parsed = serializer.deserialize(raw);
127
+ if (parsed == null || typeof parsed !== "object" || typeof parsed.version !== "number" || typeof parsed.createdAt !== "number" || typeof parsed.updatedAt !== "number" || !("data" in parsed)) {
128
+ throw new Error(`Invalid persist record for key "${key}"`);
129
+ }
130
+ return parsed;
131
+ };
132
+ var resolveSnapshot = (record, options) => {
133
+ const currentVersion = options.version ?? 1;
134
+ let snapshot = record.data;
135
+ if (record.version !== currentVersion) {
136
+ if (!options.migrate) {
137
+ return null;
138
+ }
139
+ const ctx = {
140
+ data: snapshot,
141
+ version: record.version,
142
+ currentVersion
143
+ };
144
+ snapshot = options.migrate(ctx);
145
+ }
146
+ if (options.validate && !options.validate(snapshot)) {
147
+ return null;
148
+ }
149
+ return snapshot;
150
+ };
151
+ var hydrateFromStorage = async (target, adapter, options) => {
152
+ const record = await readPersistRecord(adapter, options.key, options.serializer);
153
+ if (record == null) {
154
+ return { kind: "empty" };
155
+ }
156
+ if (isRecordExpired(record)) {
157
+ await resolveAsync(adapter.removeItem(options.key));
158
+ return { kind: "expired" };
159
+ }
160
+ const snapshot = resolveSnapshot(record, options);
161
+ if (snapshot == null) {
162
+ return { kind: "invalid" };
163
+ }
164
+ const value = options.merge(target.value(), snapshot);
165
+ target.set(value);
166
+ options.onHydrate?.({ value, snapshot });
167
+ return { kind: "applied", value, snapshot };
168
+ };
169
+
170
+ // src/core/serializer.ts
171
+ var jsonSerializer = {
172
+ serialize(value) {
173
+ return JSON.stringify(value);
174
+ },
175
+ deserialize(value) {
176
+ try {
177
+ return JSON.parse(value);
178
+ } catch (error) {
179
+ throw new Error("jsonSerializer.deserialize: invalid JSON", { cause: error });
180
+ }
181
+ }
182
+ };
183
+ var createJsonSerializer = () => {
184
+ return jsonSerializer;
185
+ };
186
+
187
+ // src/core/save.ts
188
+ var saveToStorage = async (target, adapter, options) => {
189
+ const value = target.value();
190
+ const snapshot = options.select(value);
191
+ if (snapshot == null) {
192
+ await resolveAsync(adapter.removeItem(options.key));
193
+ options.onSave?.({ value, snapshot });
194
+ return;
195
+ }
196
+ const existing = await readPersistRecord(adapter, options.key, options.serializer).catch(
197
+ () => null
198
+ );
199
+ const record = existing == null ? createPersistRecord(snapshot, {
200
+ version: options.version,
201
+ ttl: options.ttl
202
+ }) : touchPersistRecord(existing, snapshot, {
203
+ version: options.version,
204
+ ttl: options.ttl
205
+ });
206
+ const serialized = options.serializer.serialize(record);
207
+ await resolveAsync(adapter.setItem(options.key, serialized));
208
+ options.onSave?.({ value, snapshot });
209
+ };
210
+
211
+ // src/core/persist.ts
212
+ var createPersist = (target, adapter, options) => {
213
+ const version = options.version ?? 1;
214
+ const serializer = options.serializer ?? createJsonSerializer();
215
+ const select = options.select ?? ((value) => value);
216
+ const merge = options.merge ?? ((currentValue, snapshot) => snapshot);
217
+ let paused = false;
218
+ let isHydrating = false;
219
+ let targetUnsubscribe = null;
220
+ let storageUnsubscribe = null;
221
+ const $hydrated = signal(false);
222
+ const $pending = signal(false);
223
+ const $error = signal(null);
224
+ const runSave = async (force = false) => {
225
+ if (paused || !force && isHydrating) {
226
+ return;
227
+ }
228
+ $pending.set(true);
229
+ try {
230
+ await saveToStorage(target, adapter, {
231
+ ...options,
232
+ serializer,
233
+ select,
234
+ version
235
+ });
236
+ $error.set(null);
237
+ } catch (error) {
238
+ $error.set(error);
239
+ options.onError?.(error);
240
+ throw error;
241
+ } finally {
242
+ $pending.set(false);
243
+ }
244
+ };
245
+ const debouncedSave = options.debounce != null && options.debounce > 0 ? debounce(() => {
246
+ void runSave();
247
+ }, options.debounce) : null;
248
+ const scheduleSave = () => {
249
+ if (paused || isHydrating) {
250
+ return;
251
+ }
252
+ if (debouncedSave) {
253
+ debouncedSave();
254
+ return;
255
+ }
256
+ void runSave();
257
+ };
258
+ const attachTargetSubscription = () => {
259
+ if (targetUnsubscribe) {
260
+ return;
261
+ }
262
+ targetUnsubscribe = target.subscribe(() => {
263
+ scheduleSave();
264
+ });
265
+ };
266
+ const applyStorageValue = async (raw) => {
267
+ if (raw == null) {
268
+ return;
269
+ }
270
+ isHydrating = true;
271
+ try {
272
+ const record = serializer.deserialize(raw);
273
+ if (record == null || typeof record !== "object") {
274
+ return;
275
+ }
276
+ if (isRecordExpired(record)) {
277
+ await resolveAsync(adapter.removeItem(options.key));
278
+ return;
279
+ }
280
+ const snapshot = resolveSnapshot(record, {
281
+ version,
282
+ migrate: options.migrate,
283
+ validate: options.validate
284
+ });
285
+ if (snapshot == null) {
286
+ return;
287
+ }
288
+ const value = merge(target.value(), snapshot);
289
+ target.set(value);
290
+ options.onHydrate?.({ value, snapshot });
291
+ } finally {
292
+ isHydrating = false;
293
+ }
294
+ };
295
+ const attachStorageSubscription = () => {
296
+ if (!options.syncTabs || !adapter.subscribe || storageUnsubscribe) {
297
+ return;
298
+ }
299
+ storageUnsubscribe = adapter.subscribe(options.key, (value) => {
300
+ if (paused) {
301
+ return;
302
+ }
303
+ void applyStorageValue(value);
304
+ });
305
+ };
306
+ const hydrate = async () => {
307
+ isHydrating = true;
308
+ $pending.set(true);
309
+ try {
310
+ const result = await hydrateFromStorage(target, adapter, {
311
+ ...options,
312
+ serializer,
313
+ select,
314
+ merge,
315
+ version
316
+ });
317
+ if (result.kind === "empty" && options.saveInitial) {
318
+ await runSave(true);
319
+ }
320
+ $error.set(null);
321
+ } catch (error) {
322
+ $error.set(error);
323
+ options.onError?.(error);
324
+ } finally {
325
+ isHydrating = false;
326
+ $hydrated.set(true);
327
+ $pending.set(false);
328
+ attachTargetSubscription();
329
+ attachStorageSubscription();
330
+ }
331
+ };
332
+ const clear = async () => {
333
+ debouncedSave?.cancel();
334
+ const wasPaused = paused;
335
+ paused = true;
336
+ await resolveAsync(adapter.removeItem(options.key));
337
+ paused = wasPaused;
338
+ };
339
+ return {
340
+ key: options.key,
341
+ $hydrated,
342
+ $pending,
343
+ $error,
344
+ hydrate,
345
+ save: runSave,
346
+ clear,
347
+ pause() {
348
+ paused = true;
349
+ debouncedSave?.cancel();
350
+ },
351
+ resume() {
352
+ paused = false;
353
+ }
354
+ };
355
+ };
356
+
357
+ // src/core/with-storage.ts
358
+ var withStorage = (adapter, options) => {
359
+ return (target) => {
360
+ const persist2 = createPersist(target, adapter, options);
361
+ if (options.hydrate !== false) {
362
+ void persist2.hydrate();
363
+ }
364
+ return { persist: persist2 };
365
+ };
366
+ };
367
+
368
+ // src/helpers/persist-target.ts
369
+ var persist = (target, extension) => {
370
+ const extendable = target;
371
+ if (typeof extendable.extend === "function") {
372
+ return extendable.extend(extension);
373
+ }
374
+ const result = extension(target);
375
+ Object.assign(target, result);
376
+ return target;
377
+ };
378
+ var persistField = (field, extension) => {
379
+ return persist(field, extension);
380
+ };
381
+ var persistFieldArray = (fieldArray, extension) => {
382
+ return persist(fieldArray, extension);
383
+ };
384
+
385
+ // src/adapters/memory.ts
386
+ var createMemoryStorageAdapter = () => {
387
+ const storage = /* @__PURE__ */ new Map();
388
+ const listeners = /* @__PURE__ */ new Map();
389
+ const notify = (key) => {
390
+ const value = storage.get(key) ?? null;
391
+ const keyListeners = listeners.get(key);
392
+ if (!keyListeners) {
393
+ return;
394
+ }
395
+ for (const listener of keyListeners) {
396
+ listener(value);
397
+ }
398
+ };
399
+ return {
400
+ kind: "memory",
401
+ getItem(key) {
402
+ return storage.get(key) ?? null;
403
+ },
404
+ setItem(key, value) {
405
+ storage.set(key, value);
406
+ notify(key);
407
+ },
408
+ removeItem(key) {
409
+ storage.delete(key);
410
+ notify(key);
411
+ },
412
+ subscribe(key, listener) {
413
+ const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
414
+ set.add(listener);
415
+ listeners.set(key, set);
416
+ return () => {
417
+ set.delete(listener);
418
+ if (set.size === 0) {
419
+ listeners.delete(key);
420
+ }
421
+ };
422
+ },
423
+ clear() {
424
+ storage.clear();
425
+ for (const key of [...listeners.keys()]) {
426
+ notify(key);
427
+ }
428
+ },
429
+ entries() {
430
+ return storage;
431
+ }
432
+ };
433
+ };
434
+
435
+ // src/adapters/local-storage.ts
436
+ var createLocalStorageAdapter = () => {
437
+ if (!hasLocalStorage()) {
438
+ warnDev("localStorage is unavailable; falling back to in-memory storage.");
439
+ return createMemoryStorageAdapter();
440
+ }
441
+ const storage = window.localStorage;
442
+ return {
443
+ kind: "localStorage",
444
+ getItem(key) {
445
+ return storage.getItem(key);
446
+ },
447
+ setItem(key, value) {
448
+ storage.setItem(key, value);
449
+ },
450
+ removeItem(key) {
451
+ storage.removeItem(key);
452
+ },
453
+ subscribe(key, listener) {
454
+ if (typeof window === "undefined") {
455
+ return () => {
456
+ };
457
+ }
458
+ const handler = (event) => {
459
+ if (event.key !== key) {
460
+ return;
461
+ }
462
+ if (event.storageArea !== storage) {
463
+ return;
464
+ }
465
+ listener(event.newValue);
466
+ };
467
+ window.addEventListener("storage", handler);
468
+ return () => {
469
+ window.removeEventListener("storage", handler);
470
+ };
471
+ }
472
+ };
473
+ };
474
+
475
+ // src/extensions/with-local-storage.ts
476
+ var withLocalStorage = (options) => {
477
+ const adapter = createLocalStorageAdapter();
478
+ return withStorage(adapter, options);
479
+ };
480
+
481
+ // src/adapters/session-storage.ts
482
+ var createSessionStorageAdapter = () => {
483
+ if (!hasSessionStorage()) {
484
+ warnDev("sessionStorage is unavailable; falling back to in-memory storage.");
485
+ return createMemoryStorageAdapter();
486
+ }
487
+ const storage = window.sessionStorage;
488
+ return {
489
+ kind: "sessionStorage",
490
+ getItem(key) {
491
+ return storage.getItem(key);
492
+ },
493
+ setItem(key, value) {
494
+ storage.setItem(key, value);
495
+ },
496
+ removeItem(key) {
497
+ storage.removeItem(key);
498
+ }
499
+ };
500
+ };
501
+
502
+ // src/extensions/with-session-storage.ts
503
+ var withSessionStorage = (options) => {
504
+ const adapter = createSessionStorageAdapter();
505
+ return withStorage(adapter, options);
506
+ };
507
+
508
+ // src/adapters/cookie.ts
509
+ var encodeCookieValue = (value) => {
510
+ return encodeURIComponent(value);
511
+ };
512
+ var decodeCookieValue = (value) => {
513
+ return decodeURIComponent(value);
514
+ };
515
+ var readCookie = (key) => {
516
+ if (!hasDocument()) {
517
+ return null;
518
+ }
519
+ const doc = document;
520
+ const prefix = `${encodeURIComponent(key)}=`;
521
+ const parts = doc.cookie.split(";");
522
+ for (const part of parts) {
523
+ const trimmed = part.trim();
524
+ if (trimmed.startsWith(prefix)) {
525
+ return decodeCookieValue(trimmed.slice(prefix.length));
526
+ }
527
+ }
528
+ return null;
529
+ };
530
+ var writeCookie = (key, value, options) => {
531
+ if (!hasDocument()) {
532
+ return;
533
+ }
534
+ const doc = document;
535
+ const segments = [`${encodeURIComponent(key)}=${value == null ? "" : encodeCookieValue(value)}`];
536
+ if (options.maxAge != null) {
537
+ segments.push(`Max-Age=${options.maxAge}`);
538
+ }
539
+ if (options.path) {
540
+ segments.push(`Path=${options.path}`);
541
+ }
542
+ if (options.domain) {
543
+ segments.push(`Domain=${options.domain}`);
544
+ }
545
+ if (options.sameSite) {
546
+ segments.push(`SameSite=${options.sameSite}`);
547
+ }
548
+ if (options.secure) {
549
+ segments.push("Secure");
550
+ }
551
+ if (value == null) {
552
+ segments.push("Max-Age=0");
553
+ }
554
+ doc.cookie = segments.join("; ");
555
+ };
556
+ var createCookieStorageAdapter = (cookieOptions = {}) => {
557
+ if (!hasDocument()) {
558
+ warnDev("document.cookie is unavailable; falling back to in-memory storage.");
559
+ return createMemoryStorageAdapter();
560
+ }
561
+ return {
562
+ kind: "cookie",
563
+ getItem(key) {
564
+ return readCookie(key);
565
+ },
566
+ setItem(key, value) {
567
+ writeCookie(key, value, cookieOptions);
568
+ },
569
+ removeItem(key) {
570
+ writeCookie(key, null, cookieOptions);
571
+ }
572
+ };
573
+ };
574
+
575
+ // src/extensions/with-cookie.ts
576
+ var withCookie = (options) => {
577
+ const { path, domain, sameSite, secure, maxAge, ...persistOptions } = options;
578
+ const adapter = createCookieStorageAdapter({ path, domain, sameSite, secure, maxAge });
579
+ return withStorage(adapter, persistOptions);
580
+ };
581
+
582
+ // src/adapters/indexed-db.ts
583
+ var openDatabase = (dbName, storeName) => {
584
+ return new Promise((resolve, reject) => {
585
+ const request = indexedDB.open(dbName, 1);
586
+ request.onupgradeneeded = () => {
587
+ const db = request.result;
588
+ if (!db.objectStoreNames.contains(storeName)) {
589
+ db.createObjectStore(storeName);
590
+ }
591
+ };
592
+ request.onsuccess = () => {
593
+ resolve(request.result);
594
+ };
595
+ request.onerror = () => {
596
+ reject(request.error ?? new Error("indexedDB.open failed"));
597
+ };
598
+ });
599
+ };
600
+ var withStore = async (dbName, storeName, mode, run) => {
601
+ const db = await openDatabase(dbName, storeName);
602
+ return await new Promise((resolve, reject) => {
603
+ const transaction = db.transaction(storeName, mode);
604
+ const store = transaction.objectStore(storeName);
605
+ const request = run(store);
606
+ request.onsuccess = () => {
607
+ resolve(request.result);
608
+ };
609
+ request.onerror = () => {
610
+ reject(request.error ?? new Error("indexedDB request failed"));
611
+ };
612
+ transaction.oncomplete = () => {
613
+ db.close();
614
+ };
615
+ });
616
+ };
617
+ var createIndexedDBStorageAdapter = (options = {}) => {
618
+ if (!hasIndexedDB()) {
619
+ warnDev("indexedDB is unavailable; falling back to in-memory storage.");
620
+ return createMemoryStorageAdapter();
621
+ }
622
+ const dbName = options.dbName ?? "echojs";
623
+ const storeName = options.storeName ?? "persist";
624
+ return {
625
+ kind: "indexedDB",
626
+ async getItem(key) {
627
+ const value = await withStore(dbName, storeName, "readonly", (store) => {
628
+ return store.get(key);
629
+ });
630
+ return value ?? null;
631
+ },
632
+ async setItem(key, value) {
633
+ await withStore(dbName, storeName, "readwrite", (store) => {
634
+ return store.put(value, key);
635
+ });
636
+ },
637
+ async removeItem(key) {
638
+ await withStore(dbName, storeName, "readwrite", (store) => {
639
+ return store.delete(key);
640
+ });
641
+ }
642
+ };
643
+ };
644
+
645
+ // src/extensions/with-indexed-db.ts
646
+ var withIndexedDB = (options) => {
647
+ const { dbName, storeName, ...persistOptions } = options;
648
+ const adapter = createIndexedDBStorageAdapter({ dbName, storeName });
649
+ return withStorage(adapter, persistOptions);
650
+ };
651
+
652
+ // src/extensions/with-memory-storage.ts
653
+ var withMemoryStorage = (options) => {
654
+ const adapter = createMemoryStorageAdapter();
655
+ return withStorage(adapter, options);
656
+ };
657
+
658
+ export { createCookieStorageAdapter, createIndexedDBStorageAdapter, createJsonSerializer, createLocalStorageAdapter, createMemoryStorageAdapter, createPersistRecord, createSessionStorageAdapter, isRecordExpired, jsonSerializer, persist, persistField, persistFieldArray, withCookie, withIndexedDB, withLocalStorage, withMemoryStorage, withSessionStorage, withStorage };
659
+ //# sourceMappingURL=index.js.map
660
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/debounce.ts","../src/core/record.ts","../src/core/utils.ts","../src/core/hydration.ts","../src/core/serializer.ts","../src/core/save.ts","../src/core/persist.ts","../src/core/with-storage.ts","../src/helpers/persist-target.ts","../src/adapters/memory.ts","../src/adapters/local-storage.ts","../src/extensions/with-local-storage.ts","../src/adapters/session-storage.ts","../src/extensions/with-session-storage.ts","../src/adapters/cookie.ts","../src/extensions/with-cookie.ts","../src/adapters/indexed-db.ts","../src/extensions/with-indexed-db.ts","../src/extensions/with-memory-storage.ts"],"names":["persist"],"mappings":"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","file":"index.js","sourcesContent":["export type DebouncedFn<T extends (...args: never[]) => void> = T & {\n cancel(): void;\n flush(): void;\n};\n\nexport const debounce = <T extends (...args: never[]) => void>(\n fn: T,\n ms: number,\n): DebouncedFn<T> => {\n let timer: ReturnType<typeof setTimeout> | undefined;\n\n const debounced = ((...args: Parameters<T>) => {\n if (timer != null) {\n clearTimeout(timer);\n }\n timer = setTimeout(() => {\n timer = undefined;\n fn(...args);\n }, ms);\n }) as DebouncedFn<T>;\n\n debounced.cancel = () => {\n if (timer != null) {\n clearTimeout(timer);\n timer = undefined;\n }\n };\n\n debounced.flush = () => {\n if (timer == null) {\n return;\n }\n clearTimeout(timer);\n timer = undefined;\n fn();\n };\n\n return debounced;\n};\n","import type { PersistRecord } from \"./types\";\n\nexport const createPersistRecord = <Snapshot>(\n data: Snapshot,\n options: {\n version: number;\n ttl?: number;\n createdAt?: number;\n },\n): PersistRecord<Snapshot> => {\n const now = Date.now();\n const createdAt = options.createdAt ?? now;\n\n return {\n version: options.version,\n createdAt,\n updatedAt: now,\n expiresAt: options.ttl != null ? now + options.ttl : undefined,\n data,\n };\n};\n\nexport const isRecordExpired = (record: PersistRecord<unknown>): boolean => {\n if (record.expiresAt == null) {\n return false;\n }\n return Date.now() >= record.expiresAt;\n};\n\nexport const touchPersistRecord = <Snapshot>(\n record: PersistRecord<Snapshot>,\n data: Snapshot,\n options: { version: number; ttl?: number },\n): PersistRecord<Snapshot> => {\n const now = Date.now();\n return {\n version: options.version,\n createdAt: record.createdAt,\n updatedAt: now,\n expiresAt: options.ttl != null ? now + options.ttl : record.expiresAt,\n data,\n };\n};\n","export const isDev = (): boolean => {\n if (typeof import.meta !== \"undefined\" && \"env\" in import.meta) {\n const meta = import.meta as ImportMeta & { env?: { DEV?: boolean; MODE?: string } };\n return meta.env?.DEV === true || meta.env?.MODE === \"development\";\n }\n return false;\n};\n\nexport const warnDev = (message: string): void => {\n if (!isDev()) {\n return;\n }\n if (typeof console !== \"undefined\" && typeof console.warn === \"function\") {\n console.warn(`[@echojs-ecosystem/persist] ${message}`);\n }\n};\n\nexport const resolveAsync = async <T>(value: T | Promise<T>): Promise<T> => {\n return await Promise.resolve(value);\n};\n\nexport const hasWindow = (): boolean => {\n return typeof window !== \"undefined\";\n};\n\nexport const hasDocument = (): boolean => {\n return typeof document !== \"undefined\";\n};\n\nexport const hasLocalStorage = (): boolean => {\n if (!hasWindow()) {\n return false;\n }\n try {\n const storage = window.localStorage;\n const key = \"__echojs_persist_test__\";\n storage.setItem(key, \"1\");\n storage.removeItem(key);\n return true;\n } catch {\n return false;\n }\n};\n\nexport const hasSessionStorage = (): boolean => {\n if (!hasWindow()) {\n return false;\n }\n try {\n const storage = window.sessionStorage;\n const key = \"__echojs_persist_test__\";\n storage.setItem(key, \"1\");\n storage.removeItem(key);\n return true;\n } catch {\n return false;\n }\n};\n\nexport const hasIndexedDB = (): boolean => {\n return hasWindow() && \"indexedDB\" in window;\n};\n","import { createPersistRecord, isRecordExpired } from \"./record\";\nimport type {\n MigrateContext,\n PersistOptions,\n PersistRecord,\n Persistable,\n Serializer,\n StorageAdapter,\n} from \"./types\";\nimport { resolveAsync } from \"./utils\";\n\nexport type HydrationResult<Value, Snapshot> =\n | { kind: \"empty\" }\n | { kind: \"expired\" }\n | { kind: \"invalid\" }\n | { kind: \"applied\"; value: Value; snapshot: Snapshot };\n\nexport const readPersistRecord = async <Snapshot>(\n adapter: StorageAdapter,\n key: string,\n serializer: Serializer<PersistRecord<Snapshot>>,\n): Promise<PersistRecord<Snapshot> | null> => {\n const raw = await resolveAsync(adapter.getItem(key));\n if (raw == null || raw === \"\") {\n return null;\n }\n\n const parsed = serializer.deserialize(raw) as PersistRecord<Snapshot>;\n if (\n parsed == null ||\n typeof parsed !== \"object\" ||\n typeof parsed.version !== \"number\" ||\n typeof parsed.createdAt !== \"number\" ||\n typeof parsed.updatedAt !== \"number\" ||\n !(\"data\" in parsed)\n ) {\n throw new Error(`Invalid persist record for key \"${key}\"`);\n }\n\n return parsed;\n};\n\nexport const resolveSnapshot = <Value, Snapshot>(\n record: PersistRecord<Snapshot>,\n options: Pick<PersistOptions<Value, Snapshot>, \"version\" | \"migrate\" | \"validate\">,\n): Snapshot | null => {\n const currentVersion = options.version ?? 1;\n\n let snapshot: unknown = record.data;\n\n if (record.version !== currentVersion) {\n if (!options.migrate) {\n return null;\n }\n const ctx: MigrateContext = {\n data: snapshot,\n version: record.version,\n currentVersion,\n };\n snapshot = options.migrate(ctx);\n }\n\n if (options.validate && !options.validate(snapshot)) {\n return null;\n }\n\n return snapshot as Snapshot;\n};\n\nexport const hydrateFromStorage = async <Value, Snapshot>(\n target: Persistable<Value>,\n adapter: StorageAdapter,\n options: PersistOptions<Value, Snapshot> & {\n serializer: Serializer<PersistRecord<Snapshot>>;\n select: (value: Value) => Snapshot;\n merge: (currentValue: Value, snapshot: Snapshot) => Value;\n },\n): Promise<HydrationResult<Value, Snapshot>> => {\n const record = await readPersistRecord<Snapshot>(adapter, options.key, options.serializer);\n\n if (record == null) {\n return { kind: \"empty\" };\n }\n\n if (isRecordExpired(record)) {\n await resolveAsync(adapter.removeItem(options.key));\n return { kind: \"expired\" };\n }\n\n const snapshot = resolveSnapshot(record, options);\n if (snapshot == null) {\n return { kind: \"invalid\" };\n }\n\n const value = options.merge(target.value(), snapshot);\n target.set(value);\n options.onHydrate?.({ value, snapshot });\n\n return { kind: \"applied\", value, snapshot };\n};\n\nexport const createInitialRecord = <Snapshot>(\n snapshot: Snapshot,\n options: { version: number; ttl?: number },\n): PersistRecord<Snapshot> => {\n return createPersistRecord(snapshot, options);\n};\n","import type { Serializer } from \"./types\";\n\nexport const jsonSerializer: Serializer<unknown> = {\n serialize(value) {\n return JSON.stringify(value);\n },\n deserialize(value) {\n try {\n return JSON.parse(value) as unknown;\n } catch (error) {\n throw new Error(\"jsonSerializer.deserialize: invalid JSON\", { cause: error });\n }\n },\n};\n\nexport const createJsonSerializer = <T>(): Serializer<T> => {\n return jsonSerializer as Serializer<T>;\n};\n","import { createPersistRecord, touchPersistRecord } from \"./record\";\nimport { readPersistRecord } from \"./hydration\";\nimport type { PersistOptions, PersistRecord, Persistable, Serializer, StorageAdapter } from \"./types\";\nimport { resolveAsync } from \"./utils\";\n\nexport const saveToStorage = async <Value, Snapshot>(\n target: Persistable<Value>,\n adapter: StorageAdapter,\n options: PersistOptions<Value, Snapshot> & {\n serializer: Serializer<PersistRecord<Snapshot>>;\n select: (value: Value) => Snapshot;\n version: number;\n },\n): Promise<void> => {\n const value = target.value();\n const snapshot = options.select(value);\n\n if (snapshot == null) {\n await resolveAsync(adapter.removeItem(options.key));\n options.onSave?.({ value, snapshot });\n return;\n }\n\n const existing = await readPersistRecord<Snapshot>(adapter, options.key, options.serializer).catch(\n () => null,\n );\n\n const record =\n existing == null\n ? createPersistRecord(snapshot, {\n version: options.version,\n ttl: options.ttl,\n })\n : touchPersistRecord(existing, snapshot, {\n version: options.version,\n ttl: options.ttl,\n });\n\n const serialized = options.serializer.serialize(record);\n await resolveAsync(adapter.setItem(options.key, serialized));\n options.onSave?.({ value, snapshot });\n};\n","import { signal } from \"@echojs-ecosystem/reactivity\";\nimport { debounce } from \"./debounce\";\nimport { hydrateFromStorage, resolveSnapshot } from \"./hydration\";\nimport { isRecordExpired } from \"./record\";\nimport { createJsonSerializer } from \"./serializer\";\nimport { saveToStorage } from \"./save\";\nimport type {\n PersistController,\n PersistOptions,\n PersistRecord,\n Persistable,\n Serializer,\n StorageAdapter,\n} from \"./types\";\nimport { resolveAsync } from \"./utils\";\n\nexport const createPersist = <Value, Snapshot = Value>(\n target: Persistable<Value>,\n adapter: StorageAdapter,\n options: PersistOptions<Value, Snapshot>,\n): PersistController<Value, Snapshot> => {\n const version = options.version ?? 1;\n const serializer =\n options.serializer ?? (createJsonSerializer<PersistRecord<Snapshot>>() as Serializer<PersistRecord<Snapshot>>);\n const select = options.select ?? ((value: Value) => value as unknown as Snapshot);\n const merge =\n options.merge ?? ((currentValue: Value, snapshot: Snapshot) => snapshot as unknown as Value);\n\n let paused = false;\n let isHydrating = false;\n let targetUnsubscribe: (() => void) | null = null;\n let storageUnsubscribe: (() => void) | null = null;\n\n const $hydrated = signal(false);\n const $pending = signal(false);\n const $error = signal<unknown | null>(null);\n\n const runSave = async (force = false): Promise<void> => {\n if (paused || (!force && isHydrating)) {\n return;\n }\n\n $pending.set(true);\n try {\n await saveToStorage(target, adapter, {\n ...options,\n serializer,\n select,\n version,\n });\n $error.set(null);\n } catch (error) {\n $error.set(error);\n options.onError?.(error);\n throw error;\n } finally {\n $pending.set(false);\n }\n };\n\n const debouncedSave =\n options.debounce != null && options.debounce > 0\n ? debounce(() => {\n void runSave();\n }, options.debounce)\n : null;\n\n const scheduleSave = (): void => {\n if (paused || isHydrating) {\n return;\n }\n if (debouncedSave) {\n debouncedSave();\n return;\n }\n void runSave();\n };\n\n const attachTargetSubscription = (): void => {\n if (targetUnsubscribe) {\n return;\n }\n targetUnsubscribe = target.subscribe(() => {\n scheduleSave();\n });\n };\n\n const applyStorageValue = async (raw: string | null): Promise<void> => {\n if (raw == null) {\n return;\n }\n\n isHydrating = true;\n try {\n const record = serializer.deserialize(raw) as PersistRecord<Snapshot>;\n if (record == null || typeof record !== \"object\") {\n return;\n }\n\n if (isRecordExpired(record)) {\n await resolveAsync(adapter.removeItem(options.key));\n return;\n }\n\n const snapshot = resolveSnapshot(record, {\n version,\n migrate: options.migrate,\n validate: options.validate,\n });\n if (snapshot == null) {\n return;\n }\n\n const value = merge(target.value(), snapshot);\n target.set(value);\n options.onHydrate?.({ value, snapshot });\n } finally {\n isHydrating = false;\n }\n };\n\n const attachStorageSubscription = (): void => {\n if (!options.syncTabs || !adapter.subscribe || storageUnsubscribe) {\n return;\n }\n\n storageUnsubscribe = adapter.subscribe(options.key, (value) => {\n if (paused) {\n return;\n }\n void applyStorageValue(value);\n });\n };\n\n const hydrate = async (): Promise<void> => {\n isHydrating = true;\n $pending.set(true);\n\n try {\n const result = await hydrateFromStorage(target, adapter, {\n ...options,\n serializer,\n select,\n merge,\n version,\n });\n\n if (result.kind === \"empty\" && options.saveInitial) {\n await runSave(true);\n }\n\n $error.set(null);\n } catch (error) {\n $error.set(error);\n options.onError?.(error);\n } finally {\n isHydrating = false;\n $hydrated.set(true);\n $pending.set(false);\n attachTargetSubscription();\n attachStorageSubscription();\n }\n };\n\n const clear = async (): Promise<void> => {\n debouncedSave?.cancel();\n const wasPaused = paused;\n paused = true;\n await resolveAsync(adapter.removeItem(options.key));\n paused = wasPaused;\n };\n\n return {\n key: options.key,\n $hydrated,\n $pending,\n $error,\n\n hydrate,\n\n save: runSave,\n\n clear,\n\n pause() {\n paused = true;\n debouncedSave?.cancel();\n },\n\n resume() {\n paused = false;\n },\n };\n};\n","import { createPersist } from \"./persist\";\nimport type {\n PersistExtension,\n PersistOptions,\n StorageAdapter,\n} from \"./types\";\n\nexport const withStorage = <Value, Snapshot = Value>(\n adapter: StorageAdapter,\n options: PersistOptions<Value, Snapshot>,\n): PersistExtension<Value, Snapshot> => {\n return (target) => {\n const persist = createPersist(target, adapter, options);\n\n if (options.hydrate !== false) {\n void persist.hydrate();\n }\n\n return { persist };\n };\n};\n","import type {\n Extendable,\n Persistable,\n PersistExtension,\n PersistExtensionResult,\n} from \"../core/types\";\n\nexport const persist = <T extends Persistable<Value>, Value, Snapshot = Value>(\n target: T,\n extension: PersistExtension<Value, Snapshot>,\n): T & PersistExtensionResult<Value, Snapshot> => {\n const extendable = target as T & Extendable<T, PersistExtensionResult<Value, Snapshot>>;\n\n if (typeof extendable.extend === \"function\") {\n return extendable.extend(extension);\n }\n\n const result = extension(target);\n Object.assign(target, result);\n return target as T & PersistExtensionResult<Value, Snapshot>;\n};\n\nexport const persistField = <Value, Snapshot = Value>(\n field: Persistable<Value>,\n extension: PersistExtension<Value, Snapshot>,\n) => {\n return persist(field, extension);\n};\n\nexport const persistFieldArray = <Item, Snapshot = Item[]>(\n fieldArray: Persistable<Item[]>,\n extension: PersistExtension<Item[], Snapshot>,\n) => {\n return persist(fieldArray, extension);\n};\n","import type { StorageAdapter } from \"../core/types\";\n\nexport type MemoryStorageAdapter = StorageAdapter & {\n clear(): void;\n entries(): ReadonlyMap<string, string>;\n};\n\nexport const createMemoryStorageAdapter = (): MemoryStorageAdapter => {\n const storage = new Map<string, string>();\n const listeners = new Map<string, Set<(value: string | null) => void>>();\n\n const notify = (key: string): void => {\n const value = storage.get(key) ?? null;\n const keyListeners = listeners.get(key);\n if (!keyListeners) {\n return;\n }\n for (const listener of keyListeners) {\n listener(value);\n }\n };\n\n return {\n kind: \"memory\",\n\n getItem(key) {\n return storage.get(key) ?? null;\n },\n\n setItem(key, value) {\n storage.set(key, value);\n notify(key);\n },\n\n removeItem(key) {\n storage.delete(key);\n notify(key);\n },\n\n subscribe(key, listener) {\n const set = listeners.get(key) ?? new Set();\n set.add(listener);\n listeners.set(key, set);\n return () => {\n set.delete(listener);\n if (set.size === 0) {\n listeners.delete(key);\n }\n };\n },\n\n clear() {\n storage.clear();\n for (const key of [...listeners.keys()]) {\n notify(key);\n }\n },\n\n entries() {\n return storage;\n },\n };\n};\n","import type { StorageAdapter } from \"../core/types\";\nimport { hasLocalStorage, warnDev } from \"../core/utils\";\nimport { createMemoryStorageAdapter } from \"./memory\";\n\nexport const createLocalStorageAdapter = (): StorageAdapter => {\n if (!hasLocalStorage()) {\n warnDev(\"localStorage is unavailable; falling back to in-memory storage.\");\n return createMemoryStorageAdapter();\n }\n\n const storage = window.localStorage;\n\n return {\n kind: \"localStorage\",\n\n getItem(key) {\n return storage.getItem(key);\n },\n\n setItem(key, value) {\n storage.setItem(key, value);\n },\n\n removeItem(key) {\n storage.removeItem(key);\n },\n\n subscribe(key, listener) {\n if (typeof window === \"undefined\") {\n return () => {};\n }\n\n const handler = (event: StorageEvent) => {\n if (event.key !== key) {\n return;\n }\n if (event.storageArea !== storage) {\n return;\n }\n listener(event.newValue);\n };\n\n window.addEventListener(\"storage\", handler);\n return () => {\n window.removeEventListener(\"storage\", handler);\n };\n },\n };\n};\n","import { createLocalStorageAdapter } from \"../adapters/local-storage\";\nimport { withStorage } from \"../core/with-storage\";\nimport type { PersistExtension, PersistOptions } from \"../core/types\";\n\nexport const withLocalStorage = <Value, Snapshot = Value>(\n options: PersistOptions<Value, Snapshot>,\n): PersistExtension<Value, Snapshot> => {\n const adapter = createLocalStorageAdapter();\n return withStorage(adapter, options);\n};\n","import type { StorageAdapter } from \"../core/types\";\nimport { hasSessionStorage, warnDev } from \"../core/utils\";\nimport { createMemoryStorageAdapter } from \"./memory\";\n\nexport const createSessionStorageAdapter = (): StorageAdapter => {\n if (!hasSessionStorage()) {\n warnDev(\"sessionStorage is unavailable; falling back to in-memory storage.\");\n return createMemoryStorageAdapter();\n }\n\n const storage = window.sessionStorage;\n\n return {\n kind: \"sessionStorage\",\n\n getItem(key) {\n return storage.getItem(key);\n },\n\n setItem(key, value) {\n storage.setItem(key, value);\n },\n\n removeItem(key) {\n storage.removeItem(key);\n },\n };\n};\n","import { createSessionStorageAdapter } from \"../adapters/session-storage\";\nimport { withStorage } from \"../core/with-storage\";\nimport type { PersistExtension, PersistOptions } from \"../core/types\";\n\nexport const withSessionStorage = <Value, Snapshot = Value>(\n options: PersistOptions<Value, Snapshot>,\n): PersistExtension<Value, Snapshot> => {\n const adapter = createSessionStorageAdapter();\n return withStorage(adapter, options);\n};\n","import type { StorageAdapter } from \"../core/types\";\nimport { hasDocument, warnDev } from \"../core/utils\";\nimport { createMemoryStorageAdapter } from \"./memory\";\n\nexport type CookieAdapterOptions = {\n path?: string;\n domain?: string;\n sameSite?: \"strict\" | \"lax\" | \"none\";\n secure?: boolean;\n maxAge?: number;\n};\n\nconst encodeCookieValue = (value: string): string => {\n return encodeURIComponent(value);\n};\n\nconst decodeCookieValue = (value: string): string => {\n return decodeURIComponent(value);\n};\n\nconst readCookie = (key: string): string | null => {\n if (!hasDocument()) {\n return null;\n }\n\n const doc = document;\n const prefix = `${encodeURIComponent(key)}=`;\n const parts = doc.cookie.split(\";\");\n\n for (const part of parts) {\n const trimmed = part.trim();\n if (trimmed.startsWith(prefix)) {\n return decodeCookieValue(trimmed.slice(prefix.length));\n }\n }\n\n return null;\n};\n\nconst writeCookie = (key: string, value: string | null, options: CookieAdapterOptions): void => {\n if (!hasDocument()) {\n return;\n }\n\n const doc = document;\n const segments = [`${encodeURIComponent(key)}=${value == null ? \"\" : encodeCookieValue(value)}`];\n\n if (options.maxAge != null) {\n segments.push(`Max-Age=${options.maxAge}`);\n }\n if (options.path) {\n segments.push(`Path=${options.path}`);\n }\n if (options.domain) {\n segments.push(`Domain=${options.domain}`);\n }\n if (options.sameSite) {\n segments.push(`SameSite=${options.sameSite}`);\n }\n if (options.secure) {\n segments.push(\"Secure\");\n }\n\n if (value == null) {\n segments.push(\"Max-Age=0\");\n }\n\n doc.cookie = segments.join(\"; \");\n};\n\nexport const createCookieStorageAdapter = (\n cookieOptions: CookieAdapterOptions = {},\n): StorageAdapter => {\n if (!hasDocument()) {\n warnDev(\"document.cookie is unavailable; falling back to in-memory storage.\");\n return createMemoryStorageAdapter();\n }\n\n return {\n kind: \"cookie\",\n\n getItem(key) {\n return readCookie(key);\n },\n\n setItem(key, value) {\n writeCookie(key, value, cookieOptions);\n },\n\n removeItem(key) {\n writeCookie(key, null, cookieOptions);\n },\n };\n};\n","import { createCookieStorageAdapter } from \"../adapters/cookie\";\nimport { withStorage } from \"../core/with-storage\";\nimport type { CookiePersistOptions, PersistExtension } from \"../core/types\";\n\nexport const withCookie = <Value, Snapshot = Value>(\n options: CookiePersistOptions<Value, Snapshot>,\n): PersistExtension<Value, Snapshot> => {\n const { path, domain, sameSite, secure, maxAge, ...persistOptions } = options;\n const adapter = createCookieStorageAdapter({ path, domain, sameSite, secure, maxAge });\n return withStorage(adapter, persistOptions);\n};\n","import type { StorageAdapter } from \"../core/types\";\nimport { hasIndexedDB, warnDev } from \"../core/utils\";\nimport { createMemoryStorageAdapter } from \"./memory\";\n\nexport type IndexedDBAdapterOptions = {\n dbName?: string;\n storeName?: string;\n};\n\nconst openDatabase = (\n dbName: string,\n storeName: string,\n): Promise<IDBDatabase> => {\n return new Promise((resolve, reject) => {\n const request = indexedDB.open(dbName, 1);\n\n request.onupgradeneeded = () => {\n const db = request.result;\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n };\n\n request.onsuccess = () => {\n resolve(request.result);\n };\n\n request.onerror = () => {\n reject(request.error ?? new Error(\"indexedDB.open failed\"));\n };\n });\n};\n\nconst withStore = async <T>(\n dbName: string,\n storeName: string,\n mode: IDBTransactionMode,\n run: (store: IDBObjectStore) => IDBRequest<T>,\n): Promise<T> => {\n const db = await openDatabase(dbName, storeName);\n\n return await new Promise<T>((resolve, reject) => {\n const transaction = db.transaction(storeName, mode);\n const store = transaction.objectStore(storeName);\n const request = run(store);\n\n request.onsuccess = () => {\n resolve(request.result as T);\n };\n\n request.onerror = () => {\n reject(request.error ?? new Error(\"indexedDB request failed\"));\n };\n\n transaction.oncomplete = () => {\n db.close();\n };\n });\n};\n\nexport const createIndexedDBStorageAdapter = (\n options: IndexedDBAdapterOptions = {},\n): StorageAdapter => {\n if (!hasIndexedDB()) {\n warnDev(\"indexedDB is unavailable; falling back to in-memory storage.\");\n return createMemoryStorageAdapter();\n }\n\n const dbName = options.dbName ?? \"echojs\";\n const storeName = options.storeName ?? \"persist\";\n\n return {\n kind: \"indexedDB\",\n\n async getItem(key) {\n const value = await withStore<string | undefined>(dbName, storeName, \"readonly\", (store) => {\n return store.get(key) as IDBRequest<string | undefined>;\n });\n return value ?? null;\n },\n\n async setItem(key, value) {\n await withStore(dbName, storeName, \"readwrite\", (store) => {\n return store.put(value, key);\n });\n },\n\n async removeItem(key) {\n await withStore(dbName, storeName, \"readwrite\", (store) => {\n return store.delete(key);\n });\n },\n };\n};\n","import { createIndexedDBStorageAdapter } from \"../adapters/indexed-db\";\nimport { withStorage } from \"../core/with-storage\";\nimport type { IndexedDBPersistOptions, PersistExtension } from \"../core/types\";\n\nexport const withIndexedDB = <Value, Snapshot = Value>(\n options: IndexedDBPersistOptions<Value, Snapshot>,\n): PersistExtension<Value, Snapshot> => {\n const { dbName, storeName, ...persistOptions } = options;\n const adapter = createIndexedDBStorageAdapter({ dbName, storeName });\n return withStorage(adapter, persistOptions);\n};\n","import { createMemoryStorageAdapter } from \"../adapters/memory\";\nimport { withStorage } from \"../core/with-storage\";\nimport type { PersistExtension, PersistOptions } from \"../core/types\";\n\nexport const withMemoryStorage = <Value, Snapshot = Value>(\n options: PersistOptions<Value, Snapshot>,\n): PersistExtension<Value, Snapshot> => {\n const adapter = createMemoryStorageAdapter();\n return withStorage(adapter, options);\n};\n"]}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@echojs-ecosystem/persist",
3
+ "version": "0.1.0",
4
+ "files": [
5
+ "dist"
6
+ ],
7
+ "type": "module",
8
+ "sideEffects": false,
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "scripts": {
17
+ "build": "tsup",
18
+ "check-types": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.typing.json --noEmit",
19
+ "typecheck": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.typing.json --noEmit",
20
+ "test:types": "tsc -p tsconfig.typing.json --noEmit",
21
+ "lint": "oxlint .",
22
+ "lint:fix": "oxlint . --fix",
23
+ "format": "oxfmt --check .",
24
+ "format:fix": "oxfmt .",
25
+ "test": "vitest run",
26
+ "test:watch": "vitest",
27
+ "test:coverage": "vitest run --coverage"
28
+ },
29
+ "dependencies": {
30
+ "@echojs-ecosystem/reactivity": "workspace:*"
31
+ },
32
+ "devDependencies": {
33
+ "@echojs-ecosystem/oxc-config": "workspace:*",
34
+ "@echojs-ecosystem/store": "workspace:*",
35
+ "fake-indexeddb": "^6.0.0",
36
+ "jsdom": "^26.1.0",
37
+ "typescript": "5.9.2",
38
+ "vitest": "^4.1.4"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/echojs-ecosystem/echojs-core.git",
46
+ "directory": "packages/persist"
47
+ }
48
+ }