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/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createContext,
|
|
1
|
+
import { createContext, useRef, useEffect, useMemo, useCallback, useSyncExternalStore, useState, useContext } from 'react';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
|
|
4
4
|
// src/useSteddy.ts
|
|
@@ -55,9 +55,13 @@ function createCoordinator(store) {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
const coordinator = {
|
|
58
|
-
register(serializedKey, key, fetcher) {
|
|
58
|
+
register(serializedKey, key, fetcher, options) {
|
|
59
59
|
cancelRelease(serializedKey);
|
|
60
|
-
registered.set(serializedKey, {
|
|
60
|
+
registered.set(serializedKey, {
|
|
61
|
+
key,
|
|
62
|
+
fetcher,
|
|
63
|
+
staleTime: options?.staleTime ?? DEDUP_WINDOW_MS
|
|
64
|
+
});
|
|
61
65
|
},
|
|
62
66
|
unregister(serializedKey) {
|
|
63
67
|
cancelRelease(serializedKey);
|
|
@@ -78,7 +82,8 @@ function createCoordinator(store) {
|
|
|
78
82
|
async revalidate(serializedKey, fetcher, options) {
|
|
79
83
|
if (!options?.force) {
|
|
80
84
|
const entry = store.get(serializedKey);
|
|
81
|
-
|
|
85
|
+
const staleTime = registered.get(serializedKey)?.staleTime ?? DEDUP_WINDOW_MS;
|
|
86
|
+
if (entry && !entry.isValidating && entry.error == null && entry.hasData && Date.now() - entry.timestamp < staleTime) {
|
|
82
87
|
emit({ type: "dedup", key: serializedKey });
|
|
83
88
|
return;
|
|
84
89
|
}
|
|
@@ -115,6 +120,7 @@ function createCoordinator(store) {
|
|
|
115
120
|
const previous = store.get(serializedKey);
|
|
116
121
|
store.set(serializedKey, {
|
|
117
122
|
data: previous?.data,
|
|
123
|
+
hasData: previous?.hasData ?? false,
|
|
118
124
|
error: previous?.error,
|
|
119
125
|
timestamp: previous?.timestamp ?? 0,
|
|
120
126
|
isValidating: true
|
|
@@ -128,6 +134,7 @@ function createCoordinator(store) {
|
|
|
128
134
|
inflight.delete(serializedKey);
|
|
129
135
|
store.set(serializedKey, {
|
|
130
136
|
data,
|
|
137
|
+
hasData: true,
|
|
131
138
|
error: void 0,
|
|
132
139
|
timestamp: Date.now(),
|
|
133
140
|
isValidating: false
|
|
@@ -146,6 +153,7 @@ function createCoordinator(store) {
|
|
|
146
153
|
const current = store.get(serializedKey);
|
|
147
154
|
store.set(serializedKey, {
|
|
148
155
|
data: current?.data,
|
|
156
|
+
hasData: current?.hasData ?? false,
|
|
149
157
|
error,
|
|
150
158
|
timestamp: current?.timestamp ?? 0,
|
|
151
159
|
isValidating: false
|
|
@@ -238,6 +246,7 @@ function createCoordinator(store) {
|
|
|
238
246
|
// src/store.ts
|
|
239
247
|
var EMPTY_SNAPSHOT = Object.freeze({
|
|
240
248
|
data: void 0,
|
|
249
|
+
hasData: false,
|
|
241
250
|
error: void 0,
|
|
242
251
|
timestamp: 0,
|
|
243
252
|
isValidating: false
|
|
@@ -259,6 +268,10 @@ function createStore() {
|
|
|
259
268
|
return entries.get(key);
|
|
260
269
|
},
|
|
261
270
|
set(key, entry) {
|
|
271
|
+
const existing = entries.get(key);
|
|
272
|
+
if (existing && Object.is(existing.data, entry.data) && existing.hasData === entry.hasData && existing.error === entry.error && existing.timestamp === entry.timestamp && existing.isValidating === entry.isValidating) {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
262
275
|
entries.set(key, entry);
|
|
263
276
|
emit(key);
|
|
264
277
|
},
|
|
@@ -360,6 +373,7 @@ function createMutate(store, coordinator) {
|
|
|
360
373
|
if (isThenable(next)) {
|
|
361
374
|
store.set(serialized, {
|
|
362
375
|
data: currentData,
|
|
376
|
+
hasData: previous?.hasData ?? false,
|
|
363
377
|
error: previous?.error,
|
|
364
378
|
timestamp: previous?.timestamp ?? 0,
|
|
365
379
|
isValidating: true
|
|
@@ -367,6 +381,7 @@ function createMutate(store, coordinator) {
|
|
|
367
381
|
const resolved = await next;
|
|
368
382
|
store.set(serialized, {
|
|
369
383
|
data: resolved,
|
|
384
|
+
hasData: true,
|
|
370
385
|
error: void 0,
|
|
371
386
|
timestamp: Date.now(),
|
|
372
387
|
isValidating: revalidate
|
|
@@ -374,6 +389,7 @@ function createMutate(store, coordinator) {
|
|
|
374
389
|
} else {
|
|
375
390
|
store.set(serialized, {
|
|
376
391
|
data: next,
|
|
392
|
+
hasData: true,
|
|
377
393
|
error: void 0,
|
|
378
394
|
timestamp: Date.now(),
|
|
379
395
|
isValidating: revalidate
|
|
@@ -390,6 +406,7 @@ function createMutate(store, coordinator) {
|
|
|
390
406
|
const current = store.get(serialized);
|
|
391
407
|
store.set(serialized, {
|
|
392
408
|
data: current?.data,
|
|
409
|
+
hasData: current?.hasData ?? false,
|
|
393
410
|
error,
|
|
394
411
|
timestamp: current?.timestamp ?? Date.now(),
|
|
395
412
|
isValidating: false
|
|
@@ -421,24 +438,44 @@ function SteddyProvider({
|
|
|
421
438
|
cache,
|
|
422
439
|
children
|
|
423
440
|
}) {
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
441
|
+
const hydratedFingerprint = useRef(null);
|
|
442
|
+
useEffect(() => {
|
|
443
|
+
if (!cache) {
|
|
444
|
+
hydratedFingerprint.current = null;
|
|
445
|
+
return;
|
|
427
446
|
}
|
|
428
|
-
|
|
447
|
+
const fingerprint = JSON.stringify(cache);
|
|
448
|
+
if (hydratedFingerprint.current === fingerprint) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
hydratedFingerprint.current = fingerprint;
|
|
452
|
+
hydrateAll(cache, store);
|
|
453
|
+
}, [cache, store]);
|
|
454
|
+
const value = useMemo(
|
|
455
|
+
() => ({
|
|
429
456
|
store,
|
|
430
457
|
coordinator,
|
|
431
458
|
mutate: createMutate(store, coordinator)
|
|
432
|
-
}
|
|
433
|
-
|
|
459
|
+
}),
|
|
460
|
+
[store, coordinator]
|
|
461
|
+
);
|
|
434
462
|
return /* @__PURE__ */ jsx(SteddyContext.Provider, { value, children });
|
|
435
463
|
}
|
|
464
|
+
var warnedDefaultOnServer = false;
|
|
436
465
|
function useSteddyRuntime() {
|
|
437
|
-
|
|
466
|
+
const runtime = useContext(SteddyContext);
|
|
467
|
+
if (process.env.NODE_ENV !== "production" && typeof window === "undefined" && runtime === defaultRuntime && !warnedDefaultOnServer) {
|
|
468
|
+
warnedDefaultOnServer = true;
|
|
469
|
+
console.warn(
|
|
470
|
+
"[steddy] useSteddy on the server without SteddyProvider shares one cache across requests. Use createRuntime() per request."
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
return runtime;
|
|
438
474
|
}
|
|
439
475
|
function hydrate(key, data, store = defaultStore) {
|
|
440
476
|
store.set(serializeKey(key), {
|
|
441
477
|
data,
|
|
478
|
+
hasData: true,
|
|
442
479
|
error: void 0,
|
|
443
480
|
timestamp: 0,
|
|
444
481
|
isValidating: false
|
|
@@ -448,7 +485,7 @@ function dump(store = defaultStore) {
|
|
|
448
485
|
const snapshot = {};
|
|
449
486
|
for (const key of store.keys()) {
|
|
450
487
|
const entry = store.get(key);
|
|
451
|
-
if (!entry
|
|
488
|
+
if (!entry?.hasData) {
|
|
452
489
|
continue;
|
|
453
490
|
}
|
|
454
491
|
snapshot[key] = { data: entry.data, timestamp: entry.timestamp };
|
|
@@ -459,12 +496,23 @@ function hydrateAll(snapshot, store = defaultStore) {
|
|
|
459
496
|
for (const [key, payload] of Object.entries(snapshot)) {
|
|
460
497
|
store.set(key, {
|
|
461
498
|
data: payload.data,
|
|
499
|
+
hasData: true,
|
|
462
500
|
error: void 0,
|
|
463
|
-
timestamp:
|
|
501
|
+
timestamp: payload.timestamp,
|
|
464
502
|
isValidating: false
|
|
465
503
|
});
|
|
466
504
|
}
|
|
467
505
|
}
|
|
506
|
+
async function prefetch(key, fetcher, runtime = defaultRuntime) {
|
|
507
|
+
const serialized = serializeKey(key);
|
|
508
|
+
runtime.coordinator.register(serialized, key, fetcher);
|
|
509
|
+
try {
|
|
510
|
+
await runtime.coordinator.revalidate(serialized);
|
|
511
|
+
} catch {
|
|
512
|
+
} finally {
|
|
513
|
+
runtime.coordinator.unregister(serialized);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
468
516
|
function clear(key, runtime = defaultRuntime) {
|
|
469
517
|
if (key === void 0) {
|
|
470
518
|
for (const active of runtime.coordinator.getRegisteredKeys()) {
|
|
@@ -503,11 +551,15 @@ function useSteddy(key, fetcher, options) {
|
|
|
503
551
|
fetcherRef.current = fetcher;
|
|
504
552
|
const keyRef = useRef(key);
|
|
505
553
|
keyRef.current = key;
|
|
554
|
+
const staleTime = options?.staleTime ?? DEDUP_WINDOW_MS;
|
|
555
|
+
const staleTimeRef = useRef(staleTime);
|
|
556
|
+
staleTimeRef.current = staleTime;
|
|
506
557
|
if (serialized != null && key != null) {
|
|
507
558
|
coordinator.register(
|
|
508
559
|
serialized,
|
|
509
560
|
key,
|
|
510
|
-
(k, ctx) => fetcherRef.current(k, ctx)
|
|
561
|
+
(k, ctx) => fetcherRef.current(k, ctx),
|
|
562
|
+
{ staleTime: staleTimeRef.current }
|
|
511
563
|
);
|
|
512
564
|
}
|
|
513
565
|
const subscribe = useCallback(
|
|
@@ -520,7 +572,8 @@ function useSteddy(key, fetcher, options) {
|
|
|
520
572
|
coordinator.register(
|
|
521
573
|
serialized,
|
|
522
574
|
originalKey,
|
|
523
|
-
(k, ctx) => fetcherRef.current(k, ctx)
|
|
575
|
+
(k, ctx) => fetcherRef.current(k, ctx),
|
|
576
|
+
{ staleTime: staleTimeRef.current }
|
|
524
577
|
);
|
|
525
578
|
const unsubscribe = store.subscribe(serialized, onStoreChange);
|
|
526
579
|
if (!coordinator.isInFlight(serialized)) {
|
|
@@ -556,15 +609,15 @@ function useSteddy(key, fetcher, options) {
|
|
|
556
609
|
const previousRef = useRef(
|
|
557
610
|
void 0
|
|
558
611
|
);
|
|
559
|
-
if (serialized != null && snapshot.
|
|
612
|
+
if (serialized != null && snapshot.hasData) {
|
|
560
613
|
previousRef.current = { serialized, data: snapshot.data };
|
|
561
614
|
}
|
|
562
|
-
const data = keepPreviousData && serialized != null && snapshot.
|
|
615
|
+
const data = keepPreviousData && serialized != null && !snapshot.hasData && snapshot.error == null && previousRef.current != null && previousRef.current.serialized !== serialized ? previousRef.current.data : snapshot.data;
|
|
563
616
|
if (options?.suspense && serialized != null) {
|
|
564
617
|
if (snapshot.error != null) {
|
|
565
618
|
throw snapshot.error;
|
|
566
619
|
}
|
|
567
|
-
if (snapshot.
|
|
620
|
+
if (!snapshot.hasData && data === void 0) {
|
|
568
621
|
const waiter = coordinator.getInFlightPromise(serialized) ?? coordinator.revalidate(serialized).catch(() => {
|
|
569
622
|
});
|
|
570
623
|
throw waiter;
|
|
@@ -573,7 +626,7 @@ function useSteddy(key, fetcher, options) {
|
|
|
573
626
|
return {
|
|
574
627
|
data,
|
|
575
628
|
error: snapshot.error,
|
|
576
|
-
isLoading: serialized != null &&
|
|
629
|
+
isLoading: serialized != null && !snapshot.hasData && snapshot.error == null && data === void 0,
|
|
577
630
|
isValidating: snapshot.isValidating,
|
|
578
631
|
mutate: boundMutate
|
|
579
632
|
};
|
|
@@ -664,7 +717,7 @@ function useSteddyInfinite(getKey, fetcher, options) {
|
|
|
664
717
|
);
|
|
665
718
|
const fingerprint = currentPages.map((page) => {
|
|
666
719
|
const entry = store.getSnapshot(page.serialized);
|
|
667
|
-
return `${page.serialized}:${entry.timestamp}:${entry.isValidating ? 1 : 0}:${entry.error == null ? 0 : 1}:${entry.
|
|
720
|
+
return `${page.serialized}:${entry.timestamp}:${entry.isValidating ? 1 : 0}:${entry.error == null ? 0 : 1}:${entry.hasData ? 1 : 0}`;
|
|
668
721
|
}).join("|");
|
|
669
722
|
if (snapshotRef.current.fingerprint === fingerprint) {
|
|
670
723
|
return snapshotRef.current;
|
|
@@ -681,7 +734,7 @@ function useSteddyInfinite(getKey, fetcher, options) {
|
|
|
681
734
|
if (entry.error != null && error === void 0) {
|
|
682
735
|
error = entry.error;
|
|
683
736
|
}
|
|
684
|
-
if (entry.
|
|
737
|
+
if (!entry.hasData) {
|
|
685
738
|
missing = true;
|
|
686
739
|
break;
|
|
687
740
|
}
|
|
@@ -748,6 +801,7 @@ function useSteddyInfinite(getKey, fetcher, options) {
|
|
|
748
801
|
}
|
|
749
802
|
store.set(page.serialized, {
|
|
750
803
|
data: pagesValue[index],
|
|
804
|
+
hasData: true,
|
|
751
805
|
error: void 0,
|
|
752
806
|
timestamp: Date.now(),
|
|
753
807
|
isValidating: revalidate
|
|
@@ -777,7 +831,7 @@ function useSteddyInfinite(getKey, fetcher, options) {
|
|
|
777
831
|
return {
|
|
778
832
|
data: snapshot.data,
|
|
779
833
|
error: snapshot.error,
|
|
780
|
-
isLoading: first != null && firstEntry?.
|
|
834
|
+
isLoading: first != null && !firstEntry?.hasData && firstEntry?.error == null,
|
|
781
835
|
isValidating: snapshot.isValidating,
|
|
782
836
|
size,
|
|
783
837
|
setSize,
|
|
@@ -785,23 +839,37 @@ function useSteddyInfinite(getKey, fetcher, options) {
|
|
|
785
839
|
};
|
|
786
840
|
}
|
|
787
841
|
|
|
842
|
+
// src/plugins/subscribedKeys.ts
|
|
843
|
+
function keysWithSubscribers(keys, store) {
|
|
844
|
+
return keys.filter((key) => store.subscriberCount(key) > 0);
|
|
845
|
+
}
|
|
846
|
+
|
|
788
847
|
// src/plugins/focus.ts
|
|
789
|
-
function focusRevalidate(coordinator) {
|
|
790
|
-
const
|
|
791
|
-
|
|
848
|
+
function focusRevalidate(coordinator, store) {
|
|
849
|
+
const run = () => {
|
|
850
|
+
if (typeof document !== "undefined" && document.visibilityState !== "visible") {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
const keys = store ? keysWithSubscribers(coordinator.getRegisteredKeys(), store) : coordinator.getRegisteredKeys();
|
|
854
|
+
for (const key of keys) {
|
|
792
855
|
void coordinator.revalidate(key);
|
|
793
856
|
}
|
|
794
857
|
};
|
|
795
|
-
|
|
858
|
+
if (typeof document === "undefined") {
|
|
859
|
+
return () => {
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
document.addEventListener("visibilitychange", run);
|
|
796
863
|
return () => {
|
|
797
|
-
|
|
864
|
+
document.removeEventListener("visibilitychange", run);
|
|
798
865
|
};
|
|
799
866
|
}
|
|
800
867
|
|
|
801
868
|
// src/plugins/reconnect.ts
|
|
802
|
-
function reconnectRevalidate(coordinator) {
|
|
869
|
+
function reconnectRevalidate(coordinator, store) {
|
|
803
870
|
const onOnline = () => {
|
|
804
|
-
|
|
871
|
+
const keys = store ? keysWithSubscribers(coordinator.getRegisteredKeys(), store) : coordinator.getRegisteredKeys();
|
|
872
|
+
for (const key of keys) {
|
|
805
873
|
void coordinator.revalidate(key);
|
|
806
874
|
}
|
|
807
875
|
};
|
|
@@ -925,6 +993,20 @@ function measurePerf(coordinator, onEvent) {
|
|
|
925
993
|
};
|
|
926
994
|
}
|
|
927
995
|
|
|
928
|
-
|
|
996
|
+
// src/plugins/attachDefaults.ts
|
|
997
|
+
function attachDefaults(coordinator, store, options) {
|
|
998
|
+
const stops = [
|
|
999
|
+
focusRevalidate(coordinator, store),
|
|
1000
|
+
reconnectRevalidate(coordinator, store),
|
|
1001
|
+
ttlEvict(coordinator, options?.ttl ?? { maxAge: 3e5 })
|
|
1002
|
+
];
|
|
1003
|
+
return () => {
|
|
1004
|
+
for (const stop of stops) {
|
|
1005
|
+
stop();
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
export { DEDUP_WINDOW_MS, SteddyProvider, UNSUBSCRIBE_GRACE_MS, attachDefaults, clear, createCoordinator, createMutate, createRuntime, createStore, defaultCoordinator, defaultStore, dump, focusRevalidate, hydrate, hydrateAll, measurePerf, mutate, pollingRevalidate, prefetch, reconnectRevalidate, retryOnError, serializeKey, ttlEvict, useSteddy, useSteddyInfinite };
|
|
929
1011
|
//# sourceMappingURL=index.js.map
|
|
930
1012
|
//# sourceMappingURL=index.js.map
|