@choksheak/react-utils 0.1.6 → 0.1.8

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.
Files changed (58) hide show
  1. package/docs/assets/navigation.js +1 -1
  2. package/docs/assets/search.js +1 -1
  3. package/docs/classes/sharedQuery.SharedQuery.html +15 -0
  4. package/docs/classes/sharedState.SharedState.html +8 -0
  5. package/docs/classes/sharedState.SharedStateConfig.html +5 -5
  6. package/docs/classes/utils_storage.InMemoryStorageAdapter.html +9 -0
  7. package/docs/classes/utils_storage.IndexedDbAdapter.html +9 -0
  8. package/docs/classes/utils_storage.LocalStorageAdapter.html +9 -0
  9. package/docs/functions/sharedQuery.sharedQuery.html +5 -0
  10. package/docs/functions/sharedQuery.useQuery.html +4 -0
  11. package/docs/functions/sharedState.sharedState.html +2 -2
  12. package/docs/functions/sharedState.useSharedState.html +2 -2
  13. package/docs/functions/sharedState.useSharedStateSetter.html +2 -2
  14. package/docs/functions/sharedState.useSharedStateValue.html +2 -2
  15. package/docs/functions/utils_storage.getStorageAdapter.html +1 -0
  16. package/docs/functions/utils_stringify.stringifyDeterministicForKeys.html +7 -0
  17. package/docs/modules/sharedQuery.html +1 -0
  18. package/docs/modules/sharedState.html +1 -1
  19. package/docs/modules/utils_storage.html +1 -0
  20. package/docs/modules/utils_stringify.html +1 -0
  21. package/docs/modules.html +1 -1
  22. package/docs/types/sharedQuery.FetchFn.html +1 -0
  23. package/docs/types/sharedQuery.FetchedData.html +3 -0
  24. package/docs/types/sharedQuery.QueryStateValue.html +4 -0
  25. package/docs/types/sharedQuery.SharedQueryOptions.html +1 -0
  26. package/docs/types/sharedQuery.SharedQueryState.html +3 -0
  27. package/docs/types/sharedState.SharedStateOptions.html +1 -0
  28. package/docs/types/utils_storage.StorageAdapter.html +5 -0
  29. package/docs/types/utils_storage.StorageOptions.html +5 -0
  30. package/docs/variables/fetcher.fetcher.html +1 -1
  31. package/docs/variables/fetcher.fetcherWithRetries.html +1 -1
  32. package/package.json +9 -6
  33. package/sharedQuery.d.mts +61 -0
  34. package/sharedQuery.d.ts +58 -0
  35. package/sharedQuery.js +471 -0
  36. package/sharedQuery.mjs +444 -0
  37. package/sharedState.d.mts +21 -16
  38. package/sharedState.d.ts +19 -15
  39. package/sharedState.js +159 -88
  40. package/sharedState.mjs +158 -88
  41. package/src/sharedQuery.ts +303 -0
  42. package/src/sharedState.ts +77 -140
  43. package/src/utils/storage.ts +197 -0
  44. package/src/utils/stringify.test.ts +38 -0
  45. package/src/utils/stringify.ts +49 -0
  46. package/utils/storage.d.mts +46 -0
  47. package/utils/storage.d.ts +44 -0
  48. package/utils/storage.js +162 -0
  49. package/utils/storage.mjs +134 -0
  50. package/utils/stringify.d.mts +12 -0
  51. package/utils/stringify.d.ts +10 -0
  52. package/utils/stringify.js +58 -0
  53. package/utils/stringify.mjs +33 -0
  54. package/utils/stringify.test.d.mts +2 -0
  55. package/utils/stringify.test.d.ts +2 -0
  56. package/utils/stringify.test.js +64 -0
  57. package/utils/stringify.test.mjs +81 -0
  58. package/docs/types/sharedState.SharedState.html +0 -6
@@ -0,0 +1,444 @@
1
+ // src/sharedQuery.ts
2
+ import { DEFAULT_EXPIRY_DELTA_MS as DEFAULT_EXPIRY_DELTA_MS2 } from "@choksheak/ts-utils/kvStore";
3
+ import { useCallback, useEffect, useRef } from "react";
4
+
5
+ // src/sharedState.ts
6
+ import { DEFAULT_EXPIRY_DELTA_MS } from "@choksheak/ts-utils/kvStore";
7
+ import { MS_PER_DAY } from "@choksheak/ts-utils/timeConstants";
8
+ import { useSyncExternalStore } from "react";
9
+
10
+ // src/utils/storage.ts
11
+ import { kvStore } from "@choksheak/ts-utils/kvStore";
12
+ function getStorageAdapter(options, defaultStoreExpiryMs) {
13
+ const { localStorageKey, indexedDbKey, storeExpiryMs, store } = options ?? {};
14
+ if (store) {
15
+ return store;
16
+ }
17
+ const expiryMs = storeExpiryMs || defaultStoreExpiryMs;
18
+ if (localStorageKey) {
19
+ return new LocalStorageAdapter(localStorageKey, expiryMs);
20
+ }
21
+ if (indexedDbKey) {
22
+ return new IndexedDbAdapter(indexedDbKey, expiryMs);
23
+ }
24
+ return null;
25
+ }
26
+ var LocalStorageAdapter = class {
27
+ constructor(keyPrefix, expiryMs) {
28
+ this.keyPrefix = keyPrefix;
29
+ this.expiryMs = expiryMs;
30
+ }
31
+ save(key, value) {
32
+ if (typeof window === "undefined") return;
33
+ const fullKey = this.keyPrefix + ":" + key;
34
+ window.localStorage.setItem(
35
+ fullKey,
36
+ JSON.stringify({
37
+ value,
38
+ expiresAt: this.expiryMs ? Date.now() + this.expiryMs : 0
39
+ })
40
+ );
41
+ }
42
+ load(key) {
43
+ if (typeof window === "undefined") return void 0;
44
+ const fullKey = this.keyPrefix + ":" + key;
45
+ const raw = window.localStorage.getItem(fullKey);
46
+ if (!raw) return void 0;
47
+ try {
48
+ const box = JSON.parse(raw);
49
+ if (!box || box.expiresAt && Date.now() >= box.expiresAt) {
50
+ window.localStorage.removeItem(fullKey);
51
+ return void 0;
52
+ }
53
+ return box.value;
54
+ } catch {
55
+ window.localStorage.removeItem(fullKey);
56
+ return void 0;
57
+ }
58
+ }
59
+ delete(key) {
60
+ if (typeof window === "undefined") return;
61
+ const fullKey = this.keyPrefix + ":" + key;
62
+ window.localStorage.removeItem(fullKey);
63
+ }
64
+ clear() {
65
+ if (typeof window === "undefined") return;
66
+ for (const key of Object.keys(window.localStorage)) {
67
+ if (key.startsWith(this.keyPrefix)) {
68
+ window.localStorage.removeItem(key);
69
+ }
70
+ }
71
+ }
72
+ };
73
+ var IndexedDbAdapter = class {
74
+ constructor(keyPrefix, expiryMs) {
75
+ this.keyPrefix = keyPrefix;
76
+ this.expiryMs = expiryMs;
77
+ }
78
+ async save(key, value) {
79
+ if (typeof window === "undefined") return;
80
+ const fullKey = this.keyPrefix + ":" + key;
81
+ await kvStore.set(fullKey, JSON.stringify(value), this.expiryMs);
82
+ }
83
+ async load(key) {
84
+ if (typeof window === "undefined") return void 0;
85
+ const fullKey = this.keyPrefix + ":" + key;
86
+ const raw = await kvStore.get(fullKey);
87
+ if (raw === void 0) return void 0;
88
+ try {
89
+ return JSON.parse(raw);
90
+ } catch {
91
+ await kvStore.delete(fullKey);
92
+ return void 0;
93
+ }
94
+ }
95
+ async delete(key) {
96
+ if (typeof window === "undefined") return;
97
+ const fullKey = this.keyPrefix + ":" + key;
98
+ await kvStore.delete(fullKey);
99
+ }
100
+ async clear() {
101
+ if (typeof window === "undefined") return;
102
+ await kvStore.forEach(async (k) => {
103
+ if (k.startsWith(this.keyPrefix)) {
104
+ await kvStore.delete(k);
105
+ }
106
+ });
107
+ }
108
+ };
109
+
110
+ // src/sharedState.ts
111
+ var SharedStateConfig = class {
112
+ /**
113
+ * Default duration for storage expiration in milliseconds. Initial value
114
+ * is 30 days, but can be changed to anything.
115
+ */
116
+ static storeExpiryMs = MS_PER_DAY * 30;
117
+ /**
118
+ * True to load from storage only on mount, false (default) to load in
119
+ * the top level scope when the shared state is defined.
120
+ */
121
+ static lazyLoad = false;
122
+ };
123
+ var PubSubStore = class {
124
+ dataByKey = /* @__PURE__ */ new Map();
125
+ subscribersByKey = /* @__PURE__ */ new Map();
126
+ // This could be undefined if T includes undefined, else it should never
127
+ // return undefined because the user has to always specify the default value.
128
+ get(key) {
129
+ return this.dataByKey.get(key);
130
+ }
131
+ set(key, value) {
132
+ const prev = this.dataByKey.get(key);
133
+ if (prev === value) return;
134
+ this.dataByKey.set(key, value);
135
+ this.notify(key, value, prev);
136
+ }
137
+ delete(key) {
138
+ this.dataByKey.delete(key);
139
+ }
140
+ setNoNotify(key, value) {
141
+ this.dataByKey.set(key, value);
142
+ }
143
+ // Subscribe to changes of a specific key
144
+ subscribe(key, subscriber) {
145
+ let subscribers = this.subscribersByKey.get(key);
146
+ if (!subscribers) {
147
+ subscribers = /* @__PURE__ */ new Set();
148
+ this.subscribersByKey.set(key, subscribers);
149
+ }
150
+ subscribers.add(subscriber);
151
+ return () => {
152
+ subscribers.delete(subscriber);
153
+ if (subscribers.size === 0) this.subscribersByKey.delete(key);
154
+ };
155
+ }
156
+ notify(key, next, prev) {
157
+ const subscribers = this.subscribersByKey.get(key);
158
+ if (!subscribers) return;
159
+ const copy = Array.from(subscribers.values());
160
+ for (const subscriber of copy) {
161
+ try {
162
+ subscriber(next, prev);
163
+ } catch (e) {
164
+ console.error(`Error invoking subscriber:`, e);
165
+ }
166
+ }
167
+ }
168
+ };
169
+ var pubSubStore = new PubSubStore();
170
+ var STORAGE_KEY = "state";
171
+ var stateKey = 0;
172
+ var SharedState = class {
173
+ constructor(defaultValue, options) {
174
+ this.defaultValue = defaultValue;
175
+ this.pubSubKey = String(stateKey++);
176
+ this.storageAdapter = getStorageAdapter(options, DEFAULT_EXPIRY_DELTA_MS);
177
+ pubSubStore.setNoNotify(this.pubSubKey, defaultValue);
178
+ const lazyLoad = options?.lazyLoad;
179
+ const lazy = lazyLoad !== void 0 ? lazyLoad : SharedStateConfig.lazyLoad;
180
+ if (!lazy) {
181
+ void this.initDefaultValueOnce();
182
+ }
183
+ }
184
+ pubSubKey;
185
+ storageAdapter;
186
+ initDone = false;
187
+ // `subscribe` function required by useSyncExternalStore
188
+ subscribe(subscriber) {
189
+ return pubSubStore.subscribe(this.pubSubKey, () => subscriber());
190
+ }
191
+ // Get the value of the shared state.
192
+ // `getSnapshot` function — must return same ref if value unchanged
193
+ getSnapshot() {
194
+ return pubSubStore.get(this.pubSubKey);
195
+ }
196
+ // Set the value of the shared state.
197
+ setValue(next) {
198
+ if (typeof next === "function") {
199
+ const prev = pubSubStore.get(this.pubSubKey);
200
+ next = next(prev);
201
+ }
202
+ pubSubStore.set(this.pubSubKey, next);
203
+ void this.storageAdapter?.save(STORAGE_KEY, next);
204
+ }
205
+ // Remove the value of the shared state. Actually this just sets the value
206
+ // back to the given default value.
207
+ delete() {
208
+ pubSubStore.set(this.pubSubKey, this.defaultValue);
209
+ void this.storageAdapter?.save(STORAGE_KEY, this.defaultValue);
210
+ }
211
+ // Initial default handling
212
+ async initDefaultValueOnce() {
213
+ if (this.initDone) return;
214
+ this.initDone = true;
215
+ const v = await this.storageAdapter?.load(STORAGE_KEY);
216
+ pubSubStore.set(this.pubSubKey, v !== void 0 ? v : this.defaultValue);
217
+ }
218
+ };
219
+ function sharedState(defaultValue, options) {
220
+ return new SharedState(defaultValue, options);
221
+ }
222
+ function useSharedState(state) {
223
+ state.initDefaultValueOnce();
224
+ const value = useSyncExternalStore(
225
+ (onStoreChange) => state.subscribe(onStoreChange),
226
+ () => state.getSnapshot(),
227
+ // getServerSnapshot is needed to prevent SSR dev errors in nextjs.
228
+ () => state.getSnapshot()
229
+ );
230
+ return [value, state.setValue];
231
+ }
232
+
233
+ // src/utils/stringify.ts
234
+ function stringifyDeterministicForKeys(params) {
235
+ const cache = /* @__PURE__ */ new Set();
236
+ return JSON.stringify(params, (_, value) => {
237
+ if (typeof value === "function") {
238
+ throw new Error(`Functions should not be passed into query parameters`);
239
+ }
240
+ if (!value || typeof value !== "object") {
241
+ return value;
242
+ }
243
+ if (cache.has(value)) {
244
+ return "[Circular]";
245
+ }
246
+ cache.add(value);
247
+ if (value !== params) {
248
+ try {
249
+ JSON.stringify(value);
250
+ } catch {
251
+ return "[Unserializable]";
252
+ }
253
+ }
254
+ return Object.keys(value).sort().reduce(
255
+ (result, key) => {
256
+ result[key] = value[key];
257
+ return result;
258
+ },
259
+ {}
260
+ );
261
+ });
262
+ }
263
+
264
+ // src/sharedQuery.ts
265
+ var DEFAULT_STALE_MS = 0;
266
+ var SharedQuery = class {
267
+ constructor(queryName, fetchFn, options) {
268
+ this.queryName = queryName;
269
+ this.fetchFn = fetchFn;
270
+ this.expiryMs = options?.expiryMs !== void 0 ? options?.expiryMs : DEFAULT_EXPIRY_DELTA_MS2;
271
+ this.staleMs = options?.staleMs !== void 0 ? options?.staleMs : DEFAULT_STALE_MS;
272
+ this.revalidateOnStale = Boolean(options?.revalidateOnStale);
273
+ this.queryState = sharedState(
274
+ {},
275
+ {
276
+ ...options,
277
+ // Fallback to use expiryMs for the storeExpiryMs.
278
+ storeExpiryMs: options?.storeExpiryMs || this.expiryMs
279
+ }
280
+ );
281
+ }
282
+ inflightPromises = /* @__PURE__ */ new Map();
283
+ queryState;
284
+ expiryMs;
285
+ staleMs;
286
+ revalidateOnStale;
287
+ getQueryKey(args) {
288
+ return this.queryName + ":" + stringifyDeterministicForKeys(args);
289
+ }
290
+ isStale(entry) {
291
+ if (this.staleMs === 0) return true;
292
+ const ageMs = Date.now() - entry.updatedMs;
293
+ return ageMs > this.staleMs;
294
+ }
295
+ isExpired(entry) {
296
+ if (this.expiryMs === 0) return false;
297
+ const ageMs = Date.now() - entry.updatedMs;
298
+ return ageMs > this.expiryMs;
299
+ }
300
+ fetchNoCaching(queryKey, ...args) {
301
+ const inflightPromise = this.inflightPromises.get(queryKey);
302
+ if (inflightPromise) {
303
+ console.log(`Deduplicating inflight fetch for ${queryKey}`);
304
+ return inflightPromise;
305
+ }
306
+ console.log(`Start fetching ${queryKey}`);
307
+ const promise = this.fetchFn(...args).then((result) => {
308
+ console.log(`Successfully fetched ${queryKey}`);
309
+ this.setCache(queryKey, result);
310
+ return result;
311
+ }).catch((e) => {
312
+ console.error(`Failed to fetch ${queryKey}: ${e}`);
313
+ throw e;
314
+ }).finally(() => {
315
+ this.inflightPromises.delete(queryKey);
316
+ });
317
+ this.inflightPromises.set(queryKey, promise);
318
+ return promise;
319
+ }
320
+ async getCachedOrFetch(...args) {
321
+ const queryKey = this.getQueryKey(args);
322
+ const cached = this.queryState.getSnapshot()?.[queryKey];
323
+ if (cached?.data && !this.isExpired(cached.data)) {
324
+ if (!this.isStale(cached.data)) {
325
+ console.log(
326
+ `Return fresh data ${queryKey} from cache without fetching`
327
+ );
328
+ return cached.data.value;
329
+ }
330
+ if (this.revalidateOnStale) {
331
+ console.log(`revalidateOnStale for ${queryKey}`);
332
+ void this.fetchNoCaching(queryKey, ...args);
333
+ }
334
+ console.log(`Returning stale data for ${queryKey}`);
335
+ return cached.data.value;
336
+ }
337
+ return await this.fetchNoCaching(queryKey, ...args);
338
+ }
339
+ async setCache(queryKey, value) {
340
+ const entry = {
341
+ data: { value, updatedMs: Date.now() },
342
+ loading: false,
343
+ error: null
344
+ };
345
+ this.queryState.setValue((prev) => ({ ...prev, [queryKey]: entry }));
346
+ }
347
+ /** Do a new fetch even when the data is already cached. */
348
+ async updateFromSource(...args) {
349
+ const key = this.getQueryKey(args);
350
+ return await this.fetchNoCaching(key, ...args);
351
+ }
352
+ /** Delete the currently cached data. */
353
+ invalidate(args) {
354
+ if (args) {
355
+ const queryKey = this.getQueryKey(args);
356
+ const record = this.queryState.getSnapshot();
357
+ const clone = { ...record };
358
+ delete clone[queryKey];
359
+ this.queryState.setValue(clone);
360
+ this.inflightPromises.delete(queryKey);
361
+ } else {
362
+ this.clearAll();
363
+ }
364
+ }
365
+ /** Delete the currently cached data & all inflight promises. */
366
+ clearAll() {
367
+ this.queryState.delete();
368
+ this.inflightPromises.clear();
369
+ }
370
+ };
371
+ var seenQueryNames = /* @__PURE__ */ new Set();
372
+ function sharedQuery(queryName, fetchFn, options) {
373
+ if (seenQueryNames.has(queryName)) {
374
+ throw new Error(`Duplicate shared query "${queryName}"`);
375
+ }
376
+ seenQueryNames.add(queryName);
377
+ return new SharedQuery(queryName, fetchFn, options);
378
+ }
379
+ var DEFAULT_QUERY_STATE_ENTRY = {
380
+ data: null,
381
+ loading: true,
382
+ error: null
383
+ };
384
+ function useQuery(query, args) {
385
+ const [queryState, setQueryState] = useSharedState(query.queryState);
386
+ const isMounted = useRef(true);
387
+ const queryKey = query.getQueryKey(args);
388
+ const execute = useCallback(async () => {
389
+ console.log(`Begin executing shared query ${queryKey}`);
390
+ setQueryState((prev) => {
391
+ const clone = { ...prev };
392
+ clone[queryKey] = {
393
+ data: clone[queryKey]?.data ?? null,
394
+ loading: true,
395
+ error: null
396
+ };
397
+ return clone;
398
+ });
399
+ try {
400
+ const value = await query.getCachedOrFetch(...args);
401
+ if (isMounted.current) {
402
+ setQueryState((prev) => {
403
+ const clone = { ...prev };
404
+ const now = Date.now();
405
+ clone[queryKey] = {
406
+ data: {
407
+ value,
408
+ updatedMs: now
409
+ },
410
+ loading: false,
411
+ error: null
412
+ };
413
+ return clone;
414
+ });
415
+ }
416
+ } catch (e) {
417
+ const error = e instanceof Error ? e : new Error(`An unknown error occurred during fetch: ${e}`);
418
+ if (isMounted.current) {
419
+ setQueryState((prev) => {
420
+ const clone = { ...prev };
421
+ clone[queryKey] = {
422
+ data: clone[queryKey]?.data ?? null,
423
+ loading: false,
424
+ error
425
+ };
426
+ return clone;
427
+ });
428
+ }
429
+ }
430
+ }, [queryKey]);
431
+ useEffect(() => {
432
+ isMounted.current = true;
433
+ void execute();
434
+ return () => {
435
+ isMounted.current = false;
436
+ };
437
+ }, [execute]);
438
+ return queryState[queryKey] ?? DEFAULT_QUERY_STATE_ENTRY;
439
+ }
440
+ export {
441
+ SharedQuery,
442
+ sharedQuery,
443
+ useQuery
444
+ };
package/sharedState.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ import { StorageOptions } from './utils/storage.mjs';
2
+
1
3
  /**
2
4
  * The "shared state" is a React state that shares the same value across the
3
5
  * entire app. It is largely similar to the jotai library, but is much smaller
@@ -15,34 +17,37 @@
15
17
  */
16
18
  declare class SharedStateConfig {
17
19
  /**
18
- * Default duration for persistence expiration in milliseconds. Initial value
20
+ * Default duration for storage expiration in milliseconds. Initial value
19
21
  * is 30 days, but can be changed to anything.
20
22
  */
21
- static persistExpiryMs: number;
23
+ static storeExpiryMs: number;
22
24
  /**
23
- * True to load from persistence only on mount, false (default) to load in
25
+ * True to load from storage only on mount, false (default) to load in
24
26
  * the top level scope when the shared state is defined.
25
27
  */
26
28
  static lazyLoad: boolean;
27
29
  }
28
- type SharedState<T> = {
29
- subscribe: (subscriber: () => void) => () => void;
30
- getSnapshot: () => T;
31
- initDefaultValueOnce: () => Promise<void> | void;
30
+ type SharedStateOptions<T> = {
31
+ lazyLoad?: boolean;
32
+ } & StorageOptions<T>;
33
+ declare class SharedState<T> {
34
+ private readonly defaultValue;
35
+ private readonly pubSubKey;
36
+ private readonly storageAdapter;
32
37
  initDone: boolean;
33
- setValue: (next: T | ((prev: T) => T)) => void;
34
- };
38
+ constructor(defaultValue: T, options?: SharedStateOptions<T>);
39
+ subscribe(subscriber: () => void): () => void;
40
+ getSnapshot(): T;
41
+ setValue(next: T | ((prev: T) => T)): void;
42
+ delete(): void;
43
+ initDefaultValueOnce(): Promise<void>;
44
+ }
35
45
  /**
36
46
  * Create a new shared state object. Put this code at the top level scope:
37
47
  *
38
48
  * const myState = sharedState<number>(0);
39
49
  */
40
- declare function sharedState<T>(defaultValue: T, options?: {
41
- localStorageKey?: string;
42
- indexedDbKey?: string;
43
- persistExpiryMs?: number;
44
- lazyLoad?: boolean;
45
- }): SharedState<T>;
50
+ declare function sharedState<T>(defaultValue: T, options?: SharedStateOptions<T>): SharedState<T>;
46
51
  /**
47
52
  * React hook for subscribing to a key in the global store.
48
53
  * const [my, setMy] = useSharedState(myState);
@@ -53,4 +58,4 @@ declare function useSharedStateValue<T>(state: SharedState<T>): T;
53
58
  /** E.g. const setMy = useSharedStateSetter(myState) */
54
59
  declare function useSharedStateSetter<T>(state: SharedState<T>): (next: T | ((prev: T) => T)) => void;
55
60
 
56
- export { type SharedState, SharedStateConfig, sharedState, useSharedState, useSharedStateSetter, useSharedStateValue };
61
+ export { SharedState, SharedStateConfig, type SharedStateOptions, sharedState, useSharedState, useSharedStateSetter, useSharedStateValue };
package/sharedState.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { StorageOptions } from "./utils/storage";
1
2
  /**
2
3
  * The "shared state" is a React state that shares the same value across the
3
4
  * entire app. It is largely similar to the jotai library, but is much smaller
@@ -15,34 +16,37 @@
15
16
  */
16
17
  export declare class SharedStateConfig {
17
18
  /**
18
- * Default duration for persistence expiration in milliseconds. Initial value
19
+ * Default duration for storage expiration in milliseconds. Initial value
19
20
  * is 30 days, but can be changed to anything.
20
21
  */
21
- static persistExpiryMs: number;
22
+ static storeExpiryMs: number;
22
23
  /**
23
- * True to load from persistence only on mount, false (default) to load in
24
+ * True to load from storage only on mount, false (default) to load in
24
25
  * the top level scope when the shared state is defined.
25
26
  */
26
27
  static lazyLoad: boolean;
27
28
  }
28
- export type SharedState<T> = {
29
- subscribe: (subscriber: () => void) => () => void;
30
- getSnapshot: () => T;
31
- initDefaultValueOnce: () => Promise<void> | void;
29
+ export type SharedStateOptions<T> = {
30
+ lazyLoad?: boolean;
31
+ } & StorageOptions<T>;
32
+ export declare class SharedState<T> {
33
+ private readonly defaultValue;
34
+ private readonly pubSubKey;
35
+ private readonly storageAdapter;
32
36
  initDone: boolean;
33
- setValue: (next: T | ((prev: T) => T)) => void;
34
- };
37
+ constructor(defaultValue: T, options?: SharedStateOptions<T>);
38
+ subscribe(subscriber: () => void): () => void;
39
+ getSnapshot(): T;
40
+ setValue(next: T | ((prev: T) => T)): void;
41
+ delete(): void;
42
+ initDefaultValueOnce(): Promise<void>;
43
+ }
35
44
  /**
36
45
  * Create a new shared state object. Put this code at the top level scope:
37
46
  *
38
47
  * const myState = sharedState<number>(0);
39
48
  */
40
- export declare function sharedState<T>(defaultValue: T, options?: {
41
- localStorageKey?: string;
42
- indexedDbKey?: string;
43
- persistExpiryMs?: number;
44
- lazyLoad?: boolean;
45
- }): SharedState<T>;
49
+ export declare function sharedState<T>(defaultValue: T, options?: SharedStateOptions<T>): SharedState<T>;
46
50
  /**
47
51
  * React hook for subscribing to a key in the global store.
48
52
  * const [my, setMy] = useSharedState(myState);