@choksheak/react-utils 0.4.6 → 0.4.7
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/NoSSR.cjs +7 -33
- package/NoSSR.min.cjs +1 -1
- package/NoSSR.min.cjs.map +1 -1
- package/NoSSR.min.mjs +1 -1
- package/NoSSR.min.mjs.map +1 -1
- package/NoSSR.mjs +1 -2
- package/index.cjs +5 -985
- package/index.d.cts +2 -2
- package/index.d.ts +2 -2
- package/index.min.cjs +1 -1
- package/index.min.cjs.map +1 -1
- package/index.min.mjs +1 -1
- package/index.min.mjs.map +1 -1
- package/index.mjs +5 -940
- package/package.json +1 -1
- package/sharedQuery.cjs +58 -425
- package/sharedQuery.min.cjs +1 -1
- package/sharedQuery.min.cjs.map +1 -1
- package/sharedQuery.min.mjs +1 -1
- package/sharedQuery.min.mjs.map +1 -1
- package/sharedQuery.mjs +11 -346
- package/sharedState.cjs +23 -154
- package/sharedState.min.cjs +1 -1
- package/sharedState.min.cjs.map +1 -1
- package/sharedState.min.mjs +1 -1
- package/sharedState.min.mjs.map +1 -1
- package/sharedState.mjs +5 -106
- package/storage.cjs +15 -43
- package/storage.min.cjs +1 -1
- package/storage.min.cjs.map +1 -1
- package/storage.min.mjs +1 -1
- package/storage.min.mjs.map +1 -1
- package/storage.mjs +1 -2
- package/stringify.cjs +5 -31
- package/stringify.min.cjs +1 -1
- package/stringify.min.cjs.map +1 -1
- package/stringify.min.mjs +1 -1
- package/stringify.min.mjs.map +1 -1
- package/stringify.mjs +0 -1
- package/useDeepMemo.cjs +8 -34
- package/useDeepMemo.min.cjs +1 -1
- package/useDeepMemo.min.cjs.map +1 -1
- package/useDeepMemo.min.mjs +1 -1
- package/useDeepMemo.min.mjs.map +1 -1
- package/useDeepMemo.mjs +0 -1
package/sharedQuery.mjs
CHANGED
|
@@ -1,353 +1,18 @@
|
|
|
1
|
-
// src/sharedQuery.ts
|
|
2
1
|
import { http, retries } from "@choksheak/ts-utils/http";
|
|
3
2
|
import { nonNil } from "@choksheak/ts-utils/nonNil";
|
|
4
3
|
import getByteSize from "object-sizeof";
|
|
5
4
|
import {
|
|
6
5
|
useCallback,
|
|
7
6
|
useEffect,
|
|
8
|
-
useMemo
|
|
7
|
+
useMemo,
|
|
9
8
|
useRef,
|
|
10
|
-
useState
|
|
9
|
+
useState
|
|
11
10
|
} from "react";
|
|
12
11
|
import XXH from "xxhashjs";
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
// src/storage.ts
|
|
19
|
-
import { kvStore } from "@choksheak/ts-utils/kvStore";
|
|
20
|
-
import { createLocalStore } from "@choksheak/ts-utils/localStore";
|
|
21
|
-
var IS_NOT_BROWSER = typeof window === "undefined";
|
|
22
|
-
function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
23
|
-
const { store } = options ?? {};
|
|
24
|
-
if (!store) {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
if ("persistTo" in store && typeof store.persistTo === "string" && "key" in store && typeof store.key === "string") {
|
|
28
|
-
const expiryMs = store.expiryMs || defaultStoreExpiryMs;
|
|
29
|
-
const config = { ...store, expiryMs };
|
|
30
|
-
switch (store.persistTo) {
|
|
31
|
-
case "localStorage":
|
|
32
|
-
return localStoreAdapter(config);
|
|
33
|
-
case "indexedDb":
|
|
34
|
-
return kvStoreAdapter(config);
|
|
35
|
-
default:
|
|
36
|
-
store.persistTo;
|
|
37
|
-
throw new Error(`Unknown store.adapter ${store.persistTo}`);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return store;
|
|
41
|
-
}
|
|
42
|
-
function localStoreAdapter({
|
|
43
|
-
key,
|
|
44
|
-
expiryMs,
|
|
45
|
-
isValid
|
|
46
|
-
}) {
|
|
47
|
-
const STORE_NAME = "react-utils";
|
|
48
|
-
const adapter = createLocalStore(STORE_NAME, {
|
|
49
|
-
defaultExpiryMs: expiryMs
|
|
50
|
-
});
|
|
51
|
-
const keyPrefix = key + ":";
|
|
52
|
-
return {
|
|
53
|
-
set: (key2, value) => {
|
|
54
|
-
if (IS_NOT_BROWSER) return;
|
|
55
|
-
adapter.set(keyPrefix + key2, value);
|
|
56
|
-
},
|
|
57
|
-
get: (key2) => {
|
|
58
|
-
if (IS_NOT_BROWSER) return void 0;
|
|
59
|
-
const fullKey = keyPrefix + key2;
|
|
60
|
-
const value = adapter.get(fullKey);
|
|
61
|
-
if (value !== void 0 && isValid && !isValid(value)) {
|
|
62
|
-
console.warn(
|
|
63
|
-
`localStorage adapter: Auto-discard invalid value for ${fullKey}: ${JSON.stringify(value)}`
|
|
64
|
-
);
|
|
65
|
-
adapter.delete(fullKey);
|
|
66
|
-
return void 0;
|
|
67
|
-
}
|
|
68
|
-
return value;
|
|
69
|
-
},
|
|
70
|
-
delete: (key2) => {
|
|
71
|
-
if (IS_NOT_BROWSER) return;
|
|
72
|
-
adapter.delete(keyPrefix + key2);
|
|
73
|
-
},
|
|
74
|
-
/** Note that this will clear all keys in all shared states. */
|
|
75
|
-
clear: () => {
|
|
76
|
-
if (IS_NOT_BROWSER) return;
|
|
77
|
-
adapter.clear();
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
}
|
|
81
|
-
function kvStoreAdapter({
|
|
82
|
-
key,
|
|
83
|
-
expiryMs,
|
|
84
|
-
isValid
|
|
85
|
-
}) {
|
|
86
|
-
const keyPrefix = key + ":";
|
|
87
|
-
return {
|
|
88
|
-
set: async (key2, value) => {
|
|
89
|
-
if (IS_NOT_BROWSER) return;
|
|
90
|
-
await kvStore.set(keyPrefix + key2, value, expiryMs);
|
|
91
|
-
},
|
|
92
|
-
get: async (key2) => {
|
|
93
|
-
if (IS_NOT_BROWSER) return void 0;
|
|
94
|
-
const fullKey = keyPrefix + key2;
|
|
95
|
-
const value = await kvStore.get(fullKey);
|
|
96
|
-
if (value !== void 0 && isValid && !isValid(value)) {
|
|
97
|
-
console.warn(
|
|
98
|
-
`indexedDb adapter: Auto-discard invalid value for ${key2}:`,
|
|
99
|
-
value
|
|
100
|
-
);
|
|
101
|
-
await kvStore.delete(fullKey);
|
|
102
|
-
return void 0;
|
|
103
|
-
}
|
|
104
|
-
return value;
|
|
105
|
-
},
|
|
106
|
-
delete: async (key2) => {
|
|
107
|
-
if (IS_NOT_BROWSER) return;
|
|
108
|
-
await kvStore.delete(keyPrefix + key2);
|
|
109
|
-
},
|
|
110
|
-
clear: async () => {
|
|
111
|
-
if (IS_NOT_BROWSER) return;
|
|
112
|
-
await kvStore.clear();
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// src/sharedState.ts
|
|
118
|
-
var SharedStateConfig = {
|
|
119
|
-
/**
|
|
120
|
-
* Default duration for storage expiration in milliseconds. Initial value
|
|
121
|
-
* is 30 days, but can be changed to anything.
|
|
122
|
-
*/
|
|
123
|
-
storeExpiryMs: 30 * MS_PER_DAY,
|
|
124
|
-
/**
|
|
125
|
-
* True to load from storage only on mount, false (default) to load in
|
|
126
|
-
* the top level scope when the shared state is defined.
|
|
127
|
-
*/
|
|
128
|
-
lazyLoad: false
|
|
129
|
-
};
|
|
130
|
-
function createPubSubStore() {
|
|
131
|
-
const dataByKey = /* @__PURE__ */ new Map();
|
|
132
|
-
const subscribersByKey = /* @__PURE__ */ new Map();
|
|
133
|
-
function notify(key, next, prev) {
|
|
134
|
-
const subscribers = subscribersByKey.get(key);
|
|
135
|
-
if (!subscribers) return;
|
|
136
|
-
const copy = Array.from(subscribers.values());
|
|
137
|
-
for (const subscriber of copy) {
|
|
138
|
-
try {
|
|
139
|
-
subscriber(next, prev);
|
|
140
|
-
} catch (e) {
|
|
141
|
-
console.error(`Error invoking subscriber:`, e);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
return {
|
|
146
|
-
// This could be undefined if T includes undefined, else it should never
|
|
147
|
-
// return undefined because the user has to always specify the default value.
|
|
148
|
-
get(key) {
|
|
149
|
-
return dataByKey.get(key);
|
|
150
|
-
},
|
|
151
|
-
set(key, value) {
|
|
152
|
-
const prev = dataByKey.get(key);
|
|
153
|
-
if (prev === value) return;
|
|
154
|
-
dataByKey.set(key, value);
|
|
155
|
-
notify(key, value, prev);
|
|
156
|
-
},
|
|
157
|
-
delete(key) {
|
|
158
|
-
dataByKey.delete(key);
|
|
159
|
-
},
|
|
160
|
-
setNoNotify(key, value) {
|
|
161
|
-
dataByKey.set(key, value);
|
|
162
|
-
},
|
|
163
|
-
// Subscribe to changes of a specific key
|
|
164
|
-
subscribe(key, subscriber) {
|
|
165
|
-
let subscribers = subscribersByKey.get(key);
|
|
166
|
-
if (!subscribers) {
|
|
167
|
-
subscribers = /* @__PURE__ */ new Set();
|
|
168
|
-
subscribersByKey.set(key, subscribers);
|
|
169
|
-
}
|
|
170
|
-
subscribers.add(subscriber);
|
|
171
|
-
return () => {
|
|
172
|
-
subscribers.delete(subscriber);
|
|
173
|
-
if (subscribers.size === 0) subscribersByKey.delete(key);
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
|
-
function getOrCreatePubSubStore() {
|
|
179
|
-
if (typeof window !== "undefined") {
|
|
180
|
-
const t = window.globalThis;
|
|
181
|
-
return t.reactUtilsPubSubStore || (t.reactUtilsPubSubStore = createPubSubStore());
|
|
182
|
-
}
|
|
183
|
-
return createPubSubStore();
|
|
184
|
-
}
|
|
185
|
-
var pubSubStore = getOrCreatePubSubStore();
|
|
186
|
-
var STORAGE_KEY = "state";
|
|
187
|
-
var stateKey = 0;
|
|
188
|
-
function getNextStateKey() {
|
|
189
|
-
if (typeof window !== "undefined") {
|
|
190
|
-
const t = window.globalThis;
|
|
191
|
-
t.reactUtilsStateKey = (t.reactUtilsStateKey ?? 0) + 1;
|
|
192
|
-
return t.reactUtilsStateKey;
|
|
193
|
-
}
|
|
194
|
-
return ++stateKey;
|
|
195
|
-
}
|
|
196
|
-
function sharedState(defaultValue, options) {
|
|
197
|
-
let readyResolve = () => {
|
|
198
|
-
};
|
|
199
|
-
const readyPromise = new Promise((resolve) => {
|
|
200
|
-
readyResolve = resolve;
|
|
201
|
-
});
|
|
202
|
-
const resolveReadyPromise = readyResolve;
|
|
203
|
-
const pubSubKey = String(getNextStateKey());
|
|
204
|
-
const storageAdapter = getStorageAdapter(
|
|
205
|
-
options,
|
|
206
|
-
SharedStateConfig.storeExpiryMs
|
|
207
|
-
);
|
|
208
|
-
pubSubStore.setNoNotify(pubSubKey, defaultValue);
|
|
209
|
-
const lazyLoad = options?.lazyLoad;
|
|
210
|
-
const lazy = lazyLoad !== void 0 ? lazyLoad : SharedStateConfig.lazyLoad;
|
|
211
|
-
function completeInit(stored) {
|
|
212
|
-
const value = stored !== void 0 ? stored : defaultValue;
|
|
213
|
-
pubSubStore.set(pubSubKey, value);
|
|
214
|
-
obj.initDone = true;
|
|
215
|
-
resolveReadyPromise();
|
|
216
|
-
}
|
|
217
|
-
const obj = {
|
|
218
|
-
defaultValue,
|
|
219
|
-
/** Allow users to await for the completion of data init. */
|
|
220
|
-
readyPromise,
|
|
221
|
-
lazy,
|
|
222
|
-
initStarted: false,
|
|
223
|
-
initDone: false,
|
|
224
|
-
/** `subscribe` function required by useSyncExternalStore. */
|
|
225
|
-
subscribe(subscriber) {
|
|
226
|
-
return pubSubStore.subscribe(pubSubKey, () => subscriber());
|
|
227
|
-
},
|
|
228
|
-
/** Returns true if there is a backing store. */
|
|
229
|
-
hasStore() {
|
|
230
|
-
return Boolean(storageAdapter);
|
|
231
|
-
},
|
|
232
|
-
/**
|
|
233
|
-
* Set a value directly into the store. Does nothing if there is no
|
|
234
|
-
* backing store.
|
|
235
|
-
*
|
|
236
|
-
* Warning: If you want to set the value properly, you should use
|
|
237
|
-
* `setValue()` instead which also updates the in-memory copy.
|
|
238
|
-
*/
|
|
239
|
-
setInStore(value) {
|
|
240
|
-
return storageAdapter?.set(STORAGE_KEY, value);
|
|
241
|
-
},
|
|
242
|
-
/**
|
|
243
|
-
* Get a value directly from the store. Returns undefined if there is no
|
|
244
|
-
* backing store.
|
|
245
|
-
*/
|
|
246
|
-
getFromStore() {
|
|
247
|
-
return storageAdapter?.get(STORAGE_KEY);
|
|
248
|
-
},
|
|
249
|
-
/**
|
|
250
|
-
* Get the value of the shared state.
|
|
251
|
-
* `getSnapshot` function — must return same ref if value unchanged.
|
|
252
|
-
* Note that this would return the default value if data init from store is
|
|
253
|
-
* not completed yet.
|
|
254
|
-
*/
|
|
255
|
-
getSnapshot() {
|
|
256
|
-
return pubSubStore.get(pubSubKey);
|
|
257
|
-
},
|
|
258
|
-
/** Set the value of the shared state. */
|
|
259
|
-
setValue(next) {
|
|
260
|
-
if (typeof next === "function") {
|
|
261
|
-
const prev = pubSubStore.get(pubSubKey);
|
|
262
|
-
next = next(prev);
|
|
263
|
-
}
|
|
264
|
-
pubSubStore.set(pubSubKey, next);
|
|
265
|
-
void obj.setInStore(next);
|
|
266
|
-
},
|
|
267
|
-
/**
|
|
268
|
-
* Remove the value of the shared state. Actually this just sets the value
|
|
269
|
-
* back to the given default value.
|
|
270
|
-
*/
|
|
271
|
-
delete() {
|
|
272
|
-
pubSubStore.set(pubSubKey, defaultValue);
|
|
273
|
-
return storageAdapter?.set(STORAGE_KEY, defaultValue);
|
|
274
|
-
},
|
|
275
|
-
/** Initial default handling. */
|
|
276
|
-
initDefaultValueOnce() {
|
|
277
|
-
if (obj.initDone || obj.initStarted) return;
|
|
278
|
-
obj.initStarted = true;
|
|
279
|
-
const maybePromise = obj.getFromStore();
|
|
280
|
-
if (maybePromise instanceof Promise) {
|
|
281
|
-
return maybePromise.then(completeInit);
|
|
282
|
-
} else {
|
|
283
|
-
const stored = maybePromise;
|
|
284
|
-
completeInit(stored);
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
};
|
|
288
|
-
if (!lazy) {
|
|
289
|
-
obj.initDefaultValueOnce();
|
|
290
|
-
}
|
|
291
|
-
return obj;
|
|
292
|
-
}
|
|
293
|
-
function useSharedState(state) {
|
|
294
|
-
void state.initDefaultValueOnce();
|
|
295
|
-
const value = useSyncExternalStore(
|
|
296
|
-
(onStoreChange) => state.subscribe(onStoreChange),
|
|
297
|
-
() => state.getSnapshot(),
|
|
298
|
-
// getServerSnapshot is needed to prevent SSR dev errors in nextjs.
|
|
299
|
-
() => state.getSnapshot()
|
|
300
|
-
);
|
|
301
|
-
return [value, state.setValue];
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// src/stringify.ts
|
|
305
|
-
function stringifyDeterministicForKeys(params) {
|
|
306
|
-
const cache = /* @__PURE__ */ new Set();
|
|
307
|
-
return JSON.stringify(params, (_, value) => {
|
|
308
|
-
if (typeof value === "function") {
|
|
309
|
-
throw new Error(`Functions should not be passed into query parameters`);
|
|
310
|
-
}
|
|
311
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
312
|
-
return value;
|
|
313
|
-
}
|
|
314
|
-
if (cache.has(value)) {
|
|
315
|
-
return "[Circular]";
|
|
316
|
-
}
|
|
317
|
-
cache.add(value);
|
|
318
|
-
if (value !== params) {
|
|
319
|
-
try {
|
|
320
|
-
JSON.stringify(value);
|
|
321
|
-
} catch {
|
|
322
|
-
return "[Unserializable]";
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
return Object.keys(value).sort().reduce(
|
|
326
|
-
(result, key) => {
|
|
327
|
-
result[key] = value[key];
|
|
328
|
-
return result;
|
|
329
|
-
},
|
|
330
|
-
{}
|
|
331
|
-
);
|
|
332
|
-
});
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// src/useDeepMemo.ts
|
|
336
|
-
import { isEqual } from "lodash-es";
|
|
337
|
-
import { useMemo, useState } from "react";
|
|
338
|
-
function useDeepMemo(value) {
|
|
339
|
-
const [state, setState] = useState(value);
|
|
340
|
-
return useMemo(() => {
|
|
341
|
-
if (isEqual(state, value)) {
|
|
342
|
-
return state;
|
|
343
|
-
}
|
|
344
|
-
setState(value);
|
|
345
|
-
return value;
|
|
346
|
-
}, [state, value]);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// src/sharedQuery.ts
|
|
350
|
-
var SharedQueryConfig = {
|
|
12
|
+
import { sharedState, useSharedState } from "./sharedState";
|
|
13
|
+
import { stringifyDeterministicForKeys } from "./stringify";
|
|
14
|
+
import { useDeepMemo } from "./useDeepMemo";
|
|
15
|
+
const SharedQueryConfig = {
|
|
351
16
|
/** Refetch on every page load. */
|
|
352
17
|
staleMs: 0,
|
|
353
18
|
/** Keep in cache for a long time. */
|
|
@@ -366,7 +31,7 @@ var SharedQueryConfig = {
|
|
|
366
31
|
function configureSharedQuery(config) {
|
|
367
32
|
Object.assign(SharedQueryConfig, config);
|
|
368
33
|
}
|
|
369
|
-
|
|
34
|
+
const moduleQueryRegistry = /* @__PURE__ */ new Map();
|
|
370
35
|
function getOrCreateQueryRegistry() {
|
|
371
36
|
if (typeof window !== "undefined") {
|
|
372
37
|
const g = window.globalThis;
|
|
@@ -788,7 +453,7 @@ function sharedQuery2(options) {
|
|
|
788
453
|
function sharedQuery(options) {
|
|
789
454
|
return sharedQuery1(options);
|
|
790
455
|
}
|
|
791
|
-
|
|
456
|
+
const DEFAULT_QUERY_STATE_ENTRY = {
|
|
792
457
|
lastUpdatedMs: 0,
|
|
793
458
|
loading: true
|
|
794
459
|
};
|
|
@@ -796,7 +461,7 @@ function useSharedQuery(query, ...args) {
|
|
|
796
461
|
const stableArgs = useDeepMemo(args);
|
|
797
462
|
const [queryState, setQueryState] = useSharedState(query.queryState);
|
|
798
463
|
const isMounted = useRef(true);
|
|
799
|
-
const queryKey =
|
|
464
|
+
const queryKey = useMemo(
|
|
800
465
|
() => query.getQueryKey(stableArgs),
|
|
801
466
|
[query, stableArgs]
|
|
802
467
|
);
|
|
@@ -811,7 +476,7 @@ function useSharedQuery(query, ...args) {
|
|
|
811
476
|
},
|
|
812
477
|
[queryKey, setQueryState]
|
|
813
478
|
);
|
|
814
|
-
const [lastNonEmptyData, setLastNonEmptyData] =
|
|
479
|
+
const [lastNonEmptyData, setLastNonEmptyData] = useState();
|
|
815
480
|
useEffect(() => {
|
|
816
481
|
if (query.keepLastNonEmptyData && queryStateValue.data !== void 0) {
|
|
817
482
|
setLastNonEmptyData(queryStateValue.data);
|
|
@@ -871,7 +536,7 @@ function useSharedQuery(query, ...args) {
|
|
|
871
536
|
query.unmount(queryKey);
|
|
872
537
|
};
|
|
873
538
|
}, [executeQuery, query, queryKey]);
|
|
874
|
-
return
|
|
539
|
+
return useMemo(() => {
|
|
875
540
|
return {
|
|
876
541
|
...queryStateValue,
|
|
877
542
|
// Fill with lastNonEmptyData when data is undefined.
|
package/sharedState.cjs
CHANGED
|
@@ -1,142 +1,12 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
|
|
5
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
-
for (let key of __getOwnPropNames(from))
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
-
}
|
|
16
|
-
return to;
|
|
17
|
-
};
|
|
18
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
-
|
|
20
|
-
// src/sharedState.ts
|
|
21
|
-
var sharedState_exports = {};
|
|
22
|
-
__export(sharedState_exports, {
|
|
23
|
-
SharedStateConfig: () => SharedStateConfig,
|
|
24
|
-
configureSharedState: () => configureSharedState,
|
|
25
|
-
sharedState: () => sharedState,
|
|
26
|
-
useSharedState: () => useSharedState,
|
|
27
|
-
useSharedStateSetter: () => useSharedStateSetter,
|
|
28
|
-
useSharedStateValue: () => useSharedStateValue
|
|
29
|
-
});
|
|
30
|
-
module.exports = __toCommonJS(sharedState_exports);
|
|
31
|
-
var import_timeConstants = require("@choksheak/ts-utils/timeConstants");
|
|
32
|
-
var import_react = require("react");
|
|
33
|
-
|
|
34
|
-
// src/storage.ts
|
|
35
|
-
var import_kvStore = require("@choksheak/ts-utils/kvStore");
|
|
36
|
-
var import_localStore = require("@choksheak/ts-utils/localStore");
|
|
37
|
-
var IS_NOT_BROWSER = typeof window === "undefined";
|
|
38
|
-
function getStorageAdapter(options, defaultStoreExpiryMs) {
|
|
39
|
-
const { store } = options ?? {};
|
|
40
|
-
if (!store) {
|
|
41
|
-
return null;
|
|
42
|
-
}
|
|
43
|
-
if ("persistTo" in store && typeof store.persistTo === "string" && "key" in store && typeof store.key === "string") {
|
|
44
|
-
const expiryMs = store.expiryMs || defaultStoreExpiryMs;
|
|
45
|
-
const config = { ...store, expiryMs };
|
|
46
|
-
switch (store.persistTo) {
|
|
47
|
-
case "localStorage":
|
|
48
|
-
return localStoreAdapter(config);
|
|
49
|
-
case "indexedDb":
|
|
50
|
-
return kvStoreAdapter(config);
|
|
51
|
-
default:
|
|
52
|
-
store.persistTo;
|
|
53
|
-
throw new Error(`Unknown store.adapter ${store.persistTo}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return store;
|
|
57
|
-
}
|
|
58
|
-
function localStoreAdapter({
|
|
59
|
-
key,
|
|
60
|
-
expiryMs,
|
|
61
|
-
isValid
|
|
62
|
-
}) {
|
|
63
|
-
const STORE_NAME = "react-utils";
|
|
64
|
-
const adapter = (0, import_localStore.createLocalStore)(STORE_NAME, {
|
|
65
|
-
defaultExpiryMs: expiryMs
|
|
66
|
-
});
|
|
67
|
-
const keyPrefix = key + ":";
|
|
68
|
-
return {
|
|
69
|
-
set: (key2, value) => {
|
|
70
|
-
if (IS_NOT_BROWSER) return;
|
|
71
|
-
adapter.set(keyPrefix + key2, value);
|
|
72
|
-
},
|
|
73
|
-
get: (key2) => {
|
|
74
|
-
if (IS_NOT_BROWSER) return void 0;
|
|
75
|
-
const fullKey = keyPrefix + key2;
|
|
76
|
-
const value = adapter.get(fullKey);
|
|
77
|
-
if (value !== void 0 && isValid && !isValid(value)) {
|
|
78
|
-
console.warn(
|
|
79
|
-
`localStorage adapter: Auto-discard invalid value for ${fullKey}: ${JSON.stringify(value)}`
|
|
80
|
-
);
|
|
81
|
-
adapter.delete(fullKey);
|
|
82
|
-
return void 0;
|
|
83
|
-
}
|
|
84
|
-
return value;
|
|
85
|
-
},
|
|
86
|
-
delete: (key2) => {
|
|
87
|
-
if (IS_NOT_BROWSER) return;
|
|
88
|
-
adapter.delete(keyPrefix + key2);
|
|
89
|
-
},
|
|
90
|
-
/** Note that this will clear all keys in all shared states. */
|
|
91
|
-
clear: () => {
|
|
92
|
-
if (IS_NOT_BROWSER) return;
|
|
93
|
-
adapter.clear();
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
function kvStoreAdapter({
|
|
98
|
-
key,
|
|
99
|
-
expiryMs,
|
|
100
|
-
isValid
|
|
101
|
-
}) {
|
|
102
|
-
const keyPrefix = key + ":";
|
|
103
|
-
return {
|
|
104
|
-
set: async (key2, value) => {
|
|
105
|
-
if (IS_NOT_BROWSER) return;
|
|
106
|
-
await import_kvStore.kvStore.set(keyPrefix + key2, value, expiryMs);
|
|
107
|
-
},
|
|
108
|
-
get: async (key2) => {
|
|
109
|
-
if (IS_NOT_BROWSER) return void 0;
|
|
110
|
-
const fullKey = keyPrefix + key2;
|
|
111
|
-
const value = await import_kvStore.kvStore.get(fullKey);
|
|
112
|
-
if (value !== void 0 && isValid && !isValid(value)) {
|
|
113
|
-
console.warn(
|
|
114
|
-
`indexedDb adapter: Auto-discard invalid value for ${key2}:`,
|
|
115
|
-
value
|
|
116
|
-
);
|
|
117
|
-
await import_kvStore.kvStore.delete(fullKey);
|
|
118
|
-
return void 0;
|
|
119
|
-
}
|
|
120
|
-
return value;
|
|
121
|
-
},
|
|
122
|
-
delete: async (key2) => {
|
|
123
|
-
if (IS_NOT_BROWSER) return;
|
|
124
|
-
await import_kvStore.kvStore.delete(keyPrefix + key2);
|
|
125
|
-
},
|
|
126
|
-
clear: async () => {
|
|
127
|
-
if (IS_NOT_BROWSER) return;
|
|
128
|
-
await import_kvStore.kvStore.clear();
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// src/sharedState.ts
|
|
134
|
-
var SharedStateConfig = {
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _timeConstants = require('@choksheak/ts-utils/timeConstants');
|
|
2
|
+
var _react = require('react');
|
|
3
|
+
var _storage = require('./storage');
|
|
4
|
+
const SharedStateConfig = {
|
|
135
5
|
/**
|
|
136
6
|
* Default duration for storage expiration in milliseconds. Initial value
|
|
137
7
|
* is 30 days, but can be changed to anything.
|
|
138
8
|
*/
|
|
139
|
-
storeExpiryMs: 30 *
|
|
9
|
+
storeExpiryMs: 30 * _timeConstants.MS_PER_DAY,
|
|
140
10
|
/**
|
|
141
11
|
* True to load from storage only on mount, false (default) to load in
|
|
142
12
|
* the top level scope when the shared state is defined.
|
|
@@ -201,13 +71,13 @@ function getOrCreatePubSubStore() {
|
|
|
201
71
|
}
|
|
202
72
|
return createPubSubStore();
|
|
203
73
|
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
74
|
+
const pubSubStore = getOrCreatePubSubStore();
|
|
75
|
+
const STORAGE_KEY = "state";
|
|
76
|
+
let stateKey = 0;
|
|
207
77
|
function getNextStateKey() {
|
|
208
78
|
if (typeof window !== "undefined") {
|
|
209
79
|
const t = window.globalThis;
|
|
210
|
-
t.reactUtilsStateKey = (t.reactUtilsStateKey
|
|
80
|
+
t.reactUtilsStateKey = (_nullishCoalesce(t.reactUtilsStateKey, () => ( 0))) + 1;
|
|
211
81
|
return t.reactUtilsStateKey;
|
|
212
82
|
}
|
|
213
83
|
return ++stateKey;
|
|
@@ -220,12 +90,12 @@ function sharedState(defaultValue, options) {
|
|
|
220
90
|
});
|
|
221
91
|
const resolveReadyPromise = readyResolve;
|
|
222
92
|
const pubSubKey = String(getNextStateKey());
|
|
223
|
-
const storageAdapter = getStorageAdapter(
|
|
93
|
+
const storageAdapter = _storage.getStorageAdapter.call(void 0,
|
|
224
94
|
options,
|
|
225
95
|
SharedStateConfig.storeExpiryMs
|
|
226
96
|
);
|
|
227
97
|
pubSubStore.setNoNotify(pubSubKey, defaultValue);
|
|
228
|
-
const lazyLoad = options
|
|
98
|
+
const lazyLoad = _optionalChain([options, 'optionalAccess', _ => _.lazyLoad]);
|
|
229
99
|
const lazy = lazyLoad !== void 0 ? lazyLoad : SharedStateConfig.lazyLoad;
|
|
230
100
|
function completeInit(stored) {
|
|
231
101
|
const value = stored !== void 0 ? stored : defaultValue;
|
|
@@ -256,14 +126,14 @@ function sharedState(defaultValue, options) {
|
|
|
256
126
|
* `setValue()` instead which also updates the in-memory copy.
|
|
257
127
|
*/
|
|
258
128
|
setInStore(value) {
|
|
259
|
-
return storageAdapter
|
|
129
|
+
return _optionalChain([storageAdapter, 'optionalAccess', _2 => _2.set, 'call', _3 => _3(STORAGE_KEY, value)]);
|
|
260
130
|
},
|
|
261
131
|
/**
|
|
262
132
|
* Get a value directly from the store. Returns undefined if there is no
|
|
263
133
|
* backing store.
|
|
264
134
|
*/
|
|
265
135
|
getFromStore() {
|
|
266
|
-
return storageAdapter
|
|
136
|
+
return _optionalChain([storageAdapter, 'optionalAccess', _4 => _4.get, 'call', _5 => _5(STORAGE_KEY)]);
|
|
267
137
|
},
|
|
268
138
|
/**
|
|
269
139
|
* Get the value of the shared state.
|
|
@@ -289,7 +159,7 @@ function sharedState(defaultValue, options) {
|
|
|
289
159
|
*/
|
|
290
160
|
delete() {
|
|
291
161
|
pubSubStore.set(pubSubKey, defaultValue);
|
|
292
|
-
return storageAdapter
|
|
162
|
+
return _optionalChain([storageAdapter, 'optionalAccess', _6 => _6.set, 'call', _7 => _7(STORAGE_KEY, defaultValue)]);
|
|
293
163
|
},
|
|
294
164
|
/** Initial default handling. */
|
|
295
165
|
initDefaultValueOnce() {
|
|
@@ -311,7 +181,7 @@ function sharedState(defaultValue, options) {
|
|
|
311
181
|
}
|
|
312
182
|
function useSharedState(state) {
|
|
313
183
|
void state.initDefaultValueOnce();
|
|
314
|
-
const value = (0,
|
|
184
|
+
const value = _react.useSyncExternalStore.call(void 0,
|
|
315
185
|
(onStoreChange) => state.subscribe(onStoreChange),
|
|
316
186
|
() => state.getSnapshot(),
|
|
317
187
|
// getServerSnapshot is needed to prevent SSR dev errors in nextjs.
|
|
@@ -326,12 +196,11 @@ function useSharedStateSetter(state) {
|
|
|
326
196
|
void state.initDefaultValueOnce();
|
|
327
197
|
return state.setValue;
|
|
328
198
|
}
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
});
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
exports.SharedStateConfig = SharedStateConfig; exports.configureSharedState = configureSharedState; exports.sharedState = sharedState; exports.useSharedState = useSharedState; exports.useSharedStateSetter = useSharedStateSetter; exports.useSharedStateValue = useSharedStateValue;
|
package/sharedState.min.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _timeConstants = require('@choksheak/ts-utils/timeConstants');var _react = require('react');var _storage = require('./storage');const p={storeExpiryMs:30*_timeConstants.MS_PER_DAY,lazyLoad:!1};function E(e){Object.assign(p,e)}function l(){const e=new Map,i=new Map;function S(o,n,t){const a=i.get(o);if(!a)return;const c=Array.from(a.values());for(const d of c)try{d(n,t)}catch(f){console.error("Error invoking subscriber:",f)}}return{get(o){return e.get(o)},set(o,n){const t=e.get(o);t!==n&&(e.set(o,n),S(o,n,t))},delete(o){e.delete(o)},setNoNotify(o,n){e.set(o,n)},subscribe(o,n){let t=i.get(o);return t||(t=new Set,i.set(o,t)),t.add(n),()=>{t.delete(n),t.size===0&&i.delete(o)}}}}function h(){if(typeof window<"u"){const e=window.globalThis;return e.reactUtilsPubSubStore||(e.reactUtilsPubSubStore=l())}return l()}const u=h(),T="state";let m=0;function P(){if(typeof window<"u"){const e=window.globalThis;return e.reactUtilsStateKey=(_nullishCoalesce(e.reactUtilsStateKey, () => (0)))+1,e.reactUtilsStateKey}return++m}function U(e,i){let S=()=>{};const o=new Promise(r=>{S=r}),n=S,t=String(P()),a=_storage.getStorageAdapter.call(void 0, i,p.storeExpiryMs);u.setNoNotify(t,e);const c=_optionalChain([i, 'optionalAccess', _ => _.lazyLoad]),d=c!==void 0?c:p.lazyLoad;function f(r){const b=r!==void 0?r:e;u.set(t,b),s.initDone=!0,n()}const s={defaultValue:e,readyPromise:o,lazy:d,initStarted:!1,initDone:!1,subscribe(r){return u.subscribe(t,()=>r())},hasStore(){return!!a},setInStore(r){return _optionalChain([a, 'optionalAccess', _2 => _2.set, 'call', _3 => _3(T,r)])},getFromStore(){return _optionalChain([a, 'optionalAccess', _4 => _4.get, 'call', _5 => _5(T)])},getSnapshot(){return u.get(t)},setValue(r){if(typeof r=="function"){const b=u.get(t);r=r(b)}u.set(t,r),s.setInStore(r)},delete(){return u.set(t,e),_optionalChain([a, 'optionalAccess', _6 => _6.set, 'call', _7 => _7(T,e)])},initDefaultValueOnce(){if(s.initDone||s.initStarted)return;s.initStarted=!0;const r=s.getFromStore();if(r instanceof Promise)return r.then(f);f(r)}};return d||s.initDefaultValueOnce(),s}function w(e){return e.initDefaultValueOnce(),[_react.useSyncExternalStore.call(void 0, S=>e.subscribe(S),()=>e.getSnapshot(),()=>e.getSnapshot()),e.setValue]}function R(e){return w(e)[0]}function C(e){return e.initDefaultValueOnce(),e.setValue}exports.SharedStateConfig = p; exports.configureSharedState = E; exports.sharedState = U; exports.useSharedState = w; exports.useSharedStateSetter = C; exports.useSharedStateValue = R;
|
|
2
2
|
//# sourceMappingURL=sharedState.min.cjs.map
|