steddy 0.1.2 → 0.1.3
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 +57 -6
- package/dist/index.cjs +112 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +26 -5
- package/dist/index.d.ts +26 -5
- package/dist/index.js +112 -30
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,6 +10,34 @@ Explainer: [https://cincinnatus101010.github.io/steddyweb/](https://cincinnatus1
|
|
|
10
10
|
npm install steddy
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
+
## App root
|
|
14
|
+
|
|
15
|
+
Wire an isolated runtime once. On the server, create a **new** runtime per request — do not rely on the module singleton.
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { useEffect } from "react";
|
|
19
|
+
import {
|
|
20
|
+
attachDefaults,
|
|
21
|
+
createRuntime,
|
|
22
|
+
SteddyProvider,
|
|
23
|
+
} from "steddy";
|
|
24
|
+
|
|
25
|
+
const runtime = createRuntime();
|
|
26
|
+
|
|
27
|
+
export function AppProviders({ children }: { children: React.ReactNode }) {
|
|
28
|
+
useEffect(() => attachDefaults(runtime.coordinator, runtime.store), []);
|
|
29
|
+
return (
|
|
30
|
+
<SteddyProvider store={runtime.store} coordinator={runtime.coordinator}>
|
|
31
|
+
{children}
|
|
32
|
+
</SteddyProvider>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For Next.js / RSC: fetch on the server, `dump(runtime.store)`, pass `cache={payload}` into `SteddyProvider` on the client. `hydrateAll` preserves timestamps from `dump` so dedup can skip an immediate refetch.
|
|
38
|
+
|
|
39
|
+
## Hook
|
|
40
|
+
|
|
13
41
|
```ts
|
|
14
42
|
import { useSteddy } from "steddy";
|
|
15
43
|
|
|
@@ -21,6 +49,7 @@ function Profile({ id }: { id: string }) {
|
|
|
21
49
|
if (!response.ok) throw new Error("failed");
|
|
22
50
|
return response.json();
|
|
23
51
|
},
|
|
52
|
+
{ keepPreviousData: true, staleTime: 60_000 },
|
|
24
53
|
);
|
|
25
54
|
|
|
26
55
|
if (error) return <p>Failed to load</p>;
|
|
@@ -35,17 +64,39 @@ function Profile({ id }: { id: string }) {
|
|
|
35
64
|
}
|
|
36
65
|
```
|
|
37
66
|
|
|
38
|
-
`key === null` skips fetching.
|
|
67
|
+
`key === null` skips fetching. Options: `{ suspense: true }`, `{ keepPreviousData: true }`, `{ staleTime }` (default 2000ms dedup window).
|
|
68
|
+
|
|
69
|
+
## Helpers
|
|
39
70
|
|
|
40
71
|
```ts
|
|
41
|
-
import {
|
|
72
|
+
import {
|
|
73
|
+
createRuntime,
|
|
74
|
+
dump,
|
|
75
|
+
hydrateAll,
|
|
76
|
+
prefetch,
|
|
77
|
+
clear,
|
|
78
|
+
} from "steddy";
|
|
79
|
+
|
|
80
|
+
// Route loader / link hover — no mounted hook required
|
|
81
|
+
await prefetch(["user", id], fetchUser, runtime);
|
|
82
|
+
|
|
83
|
+
clear("user", runtime);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Plugins (opt-in)
|
|
42
87
|
|
|
43
|
-
|
|
44
|
-
focusRevalidate
|
|
45
|
-
|
|
88
|
+
```ts
|
|
89
|
+
import { attachDefaults, focusRevalidate, measurePerf } from "steddy";
|
|
90
|
+
|
|
91
|
+
// Or wire individually — pass store so focus/reconnect only hit subscribed keys
|
|
92
|
+
focusRevalidate(coordinator, store);
|
|
93
|
+
|
|
94
|
+
const perf = measurePerf(coordinator);
|
|
46
95
|
```
|
|
47
96
|
|
|
48
|
-
|
|
97
|
+
Also: `reconnectRevalidate`, `pollingRevalidate`, `retryOnError` (fetcher wrapper), `ttlEvict`, `useSteddyInfinite`.
|
|
98
|
+
|
|
99
|
+
`measurePerf(coordinator)` returns `{ starts, aborts, dedups, writes, errors, totalMs }`. Run `npm run bench` for coordinator microbenchmarks.
|
|
49
100
|
|
|
50
101
|
## Architecture
|
|
51
102
|
|
package/dist/index.cjs
CHANGED
|
@@ -57,9 +57,13 @@ function createCoordinator(store) {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
const coordinator = {
|
|
60
|
-
register(serializedKey, key, fetcher) {
|
|
60
|
+
register(serializedKey, key, fetcher, options) {
|
|
61
61
|
cancelRelease(serializedKey);
|
|
62
|
-
registered.set(serializedKey, {
|
|
62
|
+
registered.set(serializedKey, {
|
|
63
|
+
key,
|
|
64
|
+
fetcher,
|
|
65
|
+
staleTime: options?.staleTime ?? DEDUP_WINDOW_MS
|
|
66
|
+
});
|
|
63
67
|
},
|
|
64
68
|
unregister(serializedKey) {
|
|
65
69
|
cancelRelease(serializedKey);
|
|
@@ -80,7 +84,8 @@ function createCoordinator(store) {
|
|
|
80
84
|
async revalidate(serializedKey, fetcher, options) {
|
|
81
85
|
if (!options?.force) {
|
|
82
86
|
const entry = store.get(serializedKey);
|
|
83
|
-
|
|
87
|
+
const staleTime = registered.get(serializedKey)?.staleTime ?? DEDUP_WINDOW_MS;
|
|
88
|
+
if (entry && !entry.isValidating && entry.error == null && entry.hasData && Date.now() - entry.timestamp < staleTime) {
|
|
84
89
|
emit({ type: "dedup", key: serializedKey });
|
|
85
90
|
return;
|
|
86
91
|
}
|
|
@@ -117,6 +122,7 @@ function createCoordinator(store) {
|
|
|
117
122
|
const previous = store.get(serializedKey);
|
|
118
123
|
store.set(serializedKey, {
|
|
119
124
|
data: previous?.data,
|
|
125
|
+
hasData: previous?.hasData ?? false,
|
|
120
126
|
error: previous?.error,
|
|
121
127
|
timestamp: previous?.timestamp ?? 0,
|
|
122
128
|
isValidating: true
|
|
@@ -130,6 +136,7 @@ function createCoordinator(store) {
|
|
|
130
136
|
inflight.delete(serializedKey);
|
|
131
137
|
store.set(serializedKey, {
|
|
132
138
|
data,
|
|
139
|
+
hasData: true,
|
|
133
140
|
error: void 0,
|
|
134
141
|
timestamp: Date.now(),
|
|
135
142
|
isValidating: false
|
|
@@ -148,6 +155,7 @@ function createCoordinator(store) {
|
|
|
148
155
|
const current = store.get(serializedKey);
|
|
149
156
|
store.set(serializedKey, {
|
|
150
157
|
data: current?.data,
|
|
158
|
+
hasData: current?.hasData ?? false,
|
|
151
159
|
error,
|
|
152
160
|
timestamp: current?.timestamp ?? 0,
|
|
153
161
|
isValidating: false
|
|
@@ -240,6 +248,7 @@ function createCoordinator(store) {
|
|
|
240
248
|
// src/store.ts
|
|
241
249
|
var EMPTY_SNAPSHOT = Object.freeze({
|
|
242
250
|
data: void 0,
|
|
251
|
+
hasData: false,
|
|
243
252
|
error: void 0,
|
|
244
253
|
timestamp: 0,
|
|
245
254
|
isValidating: false
|
|
@@ -261,6 +270,10 @@ function createStore() {
|
|
|
261
270
|
return entries.get(key);
|
|
262
271
|
},
|
|
263
272
|
set(key, entry) {
|
|
273
|
+
const existing = entries.get(key);
|
|
274
|
+
if (existing && Object.is(existing.data, entry.data) && existing.hasData === entry.hasData && existing.error === entry.error && existing.timestamp === entry.timestamp && existing.isValidating === entry.isValidating) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
264
277
|
entries.set(key, entry);
|
|
265
278
|
emit(key);
|
|
266
279
|
},
|
|
@@ -362,6 +375,7 @@ function createMutate(store, coordinator) {
|
|
|
362
375
|
if (isThenable(next)) {
|
|
363
376
|
store.set(serialized, {
|
|
364
377
|
data: currentData,
|
|
378
|
+
hasData: previous?.hasData ?? false,
|
|
365
379
|
error: previous?.error,
|
|
366
380
|
timestamp: previous?.timestamp ?? 0,
|
|
367
381
|
isValidating: true
|
|
@@ -369,6 +383,7 @@ function createMutate(store, coordinator) {
|
|
|
369
383
|
const resolved = await next;
|
|
370
384
|
store.set(serialized, {
|
|
371
385
|
data: resolved,
|
|
386
|
+
hasData: true,
|
|
372
387
|
error: void 0,
|
|
373
388
|
timestamp: Date.now(),
|
|
374
389
|
isValidating: revalidate
|
|
@@ -376,6 +391,7 @@ function createMutate(store, coordinator) {
|
|
|
376
391
|
} else {
|
|
377
392
|
store.set(serialized, {
|
|
378
393
|
data: next,
|
|
394
|
+
hasData: true,
|
|
379
395
|
error: void 0,
|
|
380
396
|
timestamp: Date.now(),
|
|
381
397
|
isValidating: revalidate
|
|
@@ -392,6 +408,7 @@ function createMutate(store, coordinator) {
|
|
|
392
408
|
const current = store.get(serialized);
|
|
393
409
|
store.set(serialized, {
|
|
394
410
|
data: current?.data,
|
|
411
|
+
hasData: current?.hasData ?? false,
|
|
395
412
|
error,
|
|
396
413
|
timestamp: current?.timestamp ?? Date.now(),
|
|
397
414
|
isValidating: false
|
|
@@ -423,24 +440,44 @@ function SteddyProvider({
|
|
|
423
440
|
cache,
|
|
424
441
|
children
|
|
425
442
|
}) {
|
|
426
|
-
const
|
|
427
|
-
|
|
428
|
-
|
|
443
|
+
const hydratedFingerprint = react.useRef(null);
|
|
444
|
+
react.useEffect(() => {
|
|
445
|
+
if (!cache) {
|
|
446
|
+
hydratedFingerprint.current = null;
|
|
447
|
+
return;
|
|
429
448
|
}
|
|
430
|
-
|
|
449
|
+
const fingerprint = JSON.stringify(cache);
|
|
450
|
+
if (hydratedFingerprint.current === fingerprint) {
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
hydratedFingerprint.current = fingerprint;
|
|
454
|
+
hydrateAll(cache, store);
|
|
455
|
+
}, [cache, store]);
|
|
456
|
+
const value = react.useMemo(
|
|
457
|
+
() => ({
|
|
431
458
|
store,
|
|
432
459
|
coordinator,
|
|
433
460
|
mutate: createMutate(store, coordinator)
|
|
434
|
-
}
|
|
435
|
-
|
|
461
|
+
}),
|
|
462
|
+
[store, coordinator]
|
|
463
|
+
);
|
|
436
464
|
return /* @__PURE__ */ jsxRuntime.jsx(SteddyContext.Provider, { value, children });
|
|
437
465
|
}
|
|
466
|
+
var warnedDefaultOnServer = false;
|
|
438
467
|
function useSteddyRuntime() {
|
|
439
|
-
|
|
468
|
+
const runtime = react.useContext(SteddyContext);
|
|
469
|
+
if (process.env.NODE_ENV !== "production" && typeof window === "undefined" && runtime === defaultRuntime && !warnedDefaultOnServer) {
|
|
470
|
+
warnedDefaultOnServer = true;
|
|
471
|
+
console.warn(
|
|
472
|
+
"[steddy] useSteddy on the server without SteddyProvider shares one cache across requests. Use createRuntime() per request."
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
return runtime;
|
|
440
476
|
}
|
|
441
477
|
function hydrate(key, data, store = defaultStore) {
|
|
442
478
|
store.set(serializeKey(key), {
|
|
443
479
|
data,
|
|
480
|
+
hasData: true,
|
|
444
481
|
error: void 0,
|
|
445
482
|
timestamp: 0,
|
|
446
483
|
isValidating: false
|
|
@@ -450,7 +487,7 @@ function dump(store = defaultStore) {
|
|
|
450
487
|
const snapshot = {};
|
|
451
488
|
for (const key of store.keys()) {
|
|
452
489
|
const entry = store.get(key);
|
|
453
|
-
if (!entry
|
|
490
|
+
if (!entry?.hasData) {
|
|
454
491
|
continue;
|
|
455
492
|
}
|
|
456
493
|
snapshot[key] = { data: entry.data, timestamp: entry.timestamp };
|
|
@@ -461,12 +498,23 @@ function hydrateAll(snapshot, store = defaultStore) {
|
|
|
461
498
|
for (const [key, payload] of Object.entries(snapshot)) {
|
|
462
499
|
store.set(key, {
|
|
463
500
|
data: payload.data,
|
|
501
|
+
hasData: true,
|
|
464
502
|
error: void 0,
|
|
465
|
-
timestamp:
|
|
503
|
+
timestamp: payload.timestamp,
|
|
466
504
|
isValidating: false
|
|
467
505
|
});
|
|
468
506
|
}
|
|
469
507
|
}
|
|
508
|
+
async function prefetch(key, fetcher, runtime = defaultRuntime) {
|
|
509
|
+
const serialized = serializeKey(key);
|
|
510
|
+
runtime.coordinator.register(serialized, key, fetcher);
|
|
511
|
+
try {
|
|
512
|
+
await runtime.coordinator.revalidate(serialized);
|
|
513
|
+
} catch {
|
|
514
|
+
} finally {
|
|
515
|
+
runtime.coordinator.unregister(serialized);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
470
518
|
function clear(key, runtime = defaultRuntime) {
|
|
471
519
|
if (key === void 0) {
|
|
472
520
|
for (const active of runtime.coordinator.getRegisteredKeys()) {
|
|
@@ -505,11 +553,15 @@ function useSteddy(key, fetcher, options) {
|
|
|
505
553
|
fetcherRef.current = fetcher;
|
|
506
554
|
const keyRef = react.useRef(key);
|
|
507
555
|
keyRef.current = key;
|
|
556
|
+
const staleTime = options?.staleTime ?? DEDUP_WINDOW_MS;
|
|
557
|
+
const staleTimeRef = react.useRef(staleTime);
|
|
558
|
+
staleTimeRef.current = staleTime;
|
|
508
559
|
if (serialized != null && key != null) {
|
|
509
560
|
coordinator.register(
|
|
510
561
|
serialized,
|
|
511
562
|
key,
|
|
512
|
-
(k, ctx) => fetcherRef.current(k, ctx)
|
|
563
|
+
(k, ctx) => fetcherRef.current(k, ctx),
|
|
564
|
+
{ staleTime: staleTimeRef.current }
|
|
513
565
|
);
|
|
514
566
|
}
|
|
515
567
|
const subscribe = react.useCallback(
|
|
@@ -522,7 +574,8 @@ function useSteddy(key, fetcher, options) {
|
|
|
522
574
|
coordinator.register(
|
|
523
575
|
serialized,
|
|
524
576
|
originalKey,
|
|
525
|
-
(k, ctx) => fetcherRef.current(k, ctx)
|
|
577
|
+
(k, ctx) => fetcherRef.current(k, ctx),
|
|
578
|
+
{ staleTime: staleTimeRef.current }
|
|
526
579
|
);
|
|
527
580
|
const unsubscribe = store.subscribe(serialized, onStoreChange);
|
|
528
581
|
if (!coordinator.isInFlight(serialized)) {
|
|
@@ -558,15 +611,15 @@ function useSteddy(key, fetcher, options) {
|
|
|
558
611
|
const previousRef = react.useRef(
|
|
559
612
|
void 0
|
|
560
613
|
);
|
|
561
|
-
if (serialized != null && snapshot.
|
|
614
|
+
if (serialized != null && snapshot.hasData) {
|
|
562
615
|
previousRef.current = { serialized, data: snapshot.data };
|
|
563
616
|
}
|
|
564
|
-
const data = keepPreviousData && serialized != null && snapshot.
|
|
617
|
+
const data = keepPreviousData && serialized != null && !snapshot.hasData && snapshot.error == null && previousRef.current != null && previousRef.current.serialized !== serialized ? previousRef.current.data : snapshot.data;
|
|
565
618
|
if (options?.suspense && serialized != null) {
|
|
566
619
|
if (snapshot.error != null) {
|
|
567
620
|
throw snapshot.error;
|
|
568
621
|
}
|
|
569
|
-
if (snapshot.
|
|
622
|
+
if (!snapshot.hasData && data === void 0) {
|
|
570
623
|
const waiter = coordinator.getInFlightPromise(serialized) ?? coordinator.revalidate(serialized).catch(() => {
|
|
571
624
|
});
|
|
572
625
|
throw waiter;
|
|
@@ -575,7 +628,7 @@ function useSteddy(key, fetcher, options) {
|
|
|
575
628
|
return {
|
|
576
629
|
data,
|
|
577
630
|
error: snapshot.error,
|
|
578
|
-
isLoading: serialized != null &&
|
|
631
|
+
isLoading: serialized != null && !snapshot.hasData && snapshot.error == null && data === void 0,
|
|
579
632
|
isValidating: snapshot.isValidating,
|
|
580
633
|
mutate: boundMutate
|
|
581
634
|
};
|
|
@@ -666,7 +719,7 @@ function useSteddyInfinite(getKey, fetcher, options) {
|
|
|
666
719
|
);
|
|
667
720
|
const fingerprint = currentPages.map((page) => {
|
|
668
721
|
const entry = store.getSnapshot(page.serialized);
|
|
669
|
-
return `${page.serialized}:${entry.timestamp}:${entry.isValidating ? 1 : 0}:${entry.error == null ? 0 : 1}:${entry.
|
|
722
|
+
return `${page.serialized}:${entry.timestamp}:${entry.isValidating ? 1 : 0}:${entry.error == null ? 0 : 1}:${entry.hasData ? 1 : 0}`;
|
|
670
723
|
}).join("|");
|
|
671
724
|
if (snapshotRef.current.fingerprint === fingerprint) {
|
|
672
725
|
return snapshotRef.current;
|
|
@@ -683,7 +736,7 @@ function useSteddyInfinite(getKey, fetcher, options) {
|
|
|
683
736
|
if (entry.error != null && error === void 0) {
|
|
684
737
|
error = entry.error;
|
|
685
738
|
}
|
|
686
|
-
if (entry.
|
|
739
|
+
if (!entry.hasData) {
|
|
687
740
|
missing = true;
|
|
688
741
|
break;
|
|
689
742
|
}
|
|
@@ -750,6 +803,7 @@ function useSteddyInfinite(getKey, fetcher, options) {
|
|
|
750
803
|
}
|
|
751
804
|
store.set(page.serialized, {
|
|
752
805
|
data: pagesValue[index],
|
|
806
|
+
hasData: true,
|
|
753
807
|
error: void 0,
|
|
754
808
|
timestamp: Date.now(),
|
|
755
809
|
isValidating: revalidate
|
|
@@ -779,7 +833,7 @@ function useSteddyInfinite(getKey, fetcher, options) {
|
|
|
779
833
|
return {
|
|
780
834
|
data: snapshot.data,
|
|
781
835
|
error: snapshot.error,
|
|
782
|
-
isLoading: first != null && firstEntry?.
|
|
836
|
+
isLoading: first != null && !firstEntry?.hasData && firstEntry?.error == null,
|
|
783
837
|
isValidating: snapshot.isValidating,
|
|
784
838
|
size,
|
|
785
839
|
setSize,
|
|
@@ -787,23 +841,37 @@ function useSteddyInfinite(getKey, fetcher, options) {
|
|
|
787
841
|
};
|
|
788
842
|
}
|
|
789
843
|
|
|
844
|
+
// src/plugins/subscribedKeys.ts
|
|
845
|
+
function keysWithSubscribers(keys, store) {
|
|
846
|
+
return keys.filter((key) => store.subscriberCount(key) > 0);
|
|
847
|
+
}
|
|
848
|
+
|
|
790
849
|
// src/plugins/focus.ts
|
|
791
|
-
function focusRevalidate(coordinator) {
|
|
792
|
-
const
|
|
793
|
-
|
|
850
|
+
function focusRevalidate(coordinator, store) {
|
|
851
|
+
const run = () => {
|
|
852
|
+
if (typeof document !== "undefined" && document.visibilityState !== "visible") {
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
const keys = store ? keysWithSubscribers(coordinator.getRegisteredKeys(), store) : coordinator.getRegisteredKeys();
|
|
856
|
+
for (const key of keys) {
|
|
794
857
|
void coordinator.revalidate(key);
|
|
795
858
|
}
|
|
796
859
|
};
|
|
797
|
-
|
|
860
|
+
if (typeof document === "undefined") {
|
|
861
|
+
return () => {
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
document.addEventListener("visibilitychange", run);
|
|
798
865
|
return () => {
|
|
799
|
-
|
|
866
|
+
document.removeEventListener("visibilitychange", run);
|
|
800
867
|
};
|
|
801
868
|
}
|
|
802
869
|
|
|
803
870
|
// src/plugins/reconnect.ts
|
|
804
|
-
function reconnectRevalidate(coordinator) {
|
|
871
|
+
function reconnectRevalidate(coordinator, store) {
|
|
805
872
|
const onOnline = () => {
|
|
806
|
-
|
|
873
|
+
const keys = store ? keysWithSubscribers(coordinator.getRegisteredKeys(), store) : coordinator.getRegisteredKeys();
|
|
874
|
+
for (const key of keys) {
|
|
807
875
|
void coordinator.revalidate(key);
|
|
808
876
|
}
|
|
809
877
|
};
|
|
@@ -927,9 +995,24 @@ function measurePerf(coordinator, onEvent) {
|
|
|
927
995
|
};
|
|
928
996
|
}
|
|
929
997
|
|
|
998
|
+
// src/plugins/attachDefaults.ts
|
|
999
|
+
function attachDefaults(coordinator, store, options) {
|
|
1000
|
+
const stops = [
|
|
1001
|
+
focusRevalidate(coordinator, store),
|
|
1002
|
+
reconnectRevalidate(coordinator, store),
|
|
1003
|
+
ttlEvict(coordinator, options?.ttl ?? { maxAge: 3e5 })
|
|
1004
|
+
];
|
|
1005
|
+
return () => {
|
|
1006
|
+
for (const stop of stops) {
|
|
1007
|
+
stop();
|
|
1008
|
+
}
|
|
1009
|
+
};
|
|
1010
|
+
}
|
|
1011
|
+
|
|
930
1012
|
exports.DEDUP_WINDOW_MS = DEDUP_WINDOW_MS;
|
|
931
1013
|
exports.SteddyProvider = SteddyProvider;
|
|
932
1014
|
exports.UNSUBSCRIBE_GRACE_MS = UNSUBSCRIBE_GRACE_MS;
|
|
1015
|
+
exports.attachDefaults = attachDefaults;
|
|
933
1016
|
exports.clear = clear;
|
|
934
1017
|
exports.createCoordinator = createCoordinator;
|
|
935
1018
|
exports.createMutate = createMutate;
|
|
@@ -944,6 +1027,7 @@ exports.hydrateAll = hydrateAll;
|
|
|
944
1027
|
exports.measurePerf = measurePerf;
|
|
945
1028
|
exports.mutate = mutate;
|
|
946
1029
|
exports.pollingRevalidate = pollingRevalidate;
|
|
1030
|
+
exports.prefetch = prefetch;
|
|
947
1031
|
exports.reconnectRevalidate = reconnectRevalidate;
|
|
948
1032
|
exports.retryOnError = retryOnError;
|
|
949
1033
|
exports.serializeKey = serializeKey;
|