@raf-query/core 0.11.1 → 0.13.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/CHANGELOG.md +39 -1
- package/README.md +28 -18
- package/dist/client.d.mts +163 -119
- package/dist/client.mjs +90 -162
- package/dist/entry.d.mts +73 -147
- package/dist/entry.mjs +314 -471
- package/dist/features/focus-refetch/focus-refetch.d.mts +17 -0
- package/dist/features/focus-refetch/focus-refetch.mjs +20 -0
- package/dist/features/polling/polling.d.mts +16 -0
- package/dist/features/polling/polling.mjs +39 -0
- package/dist/features/reconnect-refetch/reconnect-refetch.d.mts +17 -0
- package/dist/features/reconnect-refetch/reconnect-refetch.mjs +16 -0
- package/dist/features/shared.mjs +13 -0
- package/dist/guards.mjs +23 -0
- package/dist/hydration/hydration.d.mts +108 -0
- package/dist/hydration/hydration.mjs +142 -0
- package/dist/index.d.mts +19 -13
- package/dist/index.mjs +16 -13
- package/dist/infinite/infinite.d.mts +83 -0
- package/dist/infinite/infinite.mjs +120 -0
- package/dist/infinite/paging.d.mts +71 -0
- package/dist/infinite/paging.mjs +62 -0
- package/dist/internal.d.mts +13 -0
- package/dist/internal.mjs +7 -0
- package/dist/keys/keys.d.mts +54 -0
- package/dist/keys/keys.mjs +53 -0
- package/dist/keys/targets.d.mts +62 -0
- package/dist/keys/targets.mjs +85 -0
- package/dist/links/retry/retry.d.mts +33 -0
- package/dist/links/retry/retry.mjs +53 -0
- package/dist/links/timeout/timeout.d.mts +17 -0
- package/dist/links/timeout/timeout.mjs +35 -0
- package/dist/load.d.mts +149 -0
- package/dist/load.mjs +179 -0
- package/dist/options.d.mts +161 -332
- package/dist/options.mjs +28 -40
- package/dist/presets.d.mts +35 -0
- package/dist/presets.mjs +27 -0
- package/dist/root.d.mts +86 -0
- package/dist/structural-sharing/structural-sharing.mjs +32 -0
- package/dist/subscribe.d.mts +15 -0
- package/dist/subscribe.mjs +29 -0
- package/dist/thenable.d.mts +15 -17
- package/dist/thenable.mjs +25 -6
- package/dist/verbs.d.mts +150 -0
- package/dist/verbs.mjs +115 -0
- package/package.json +13 -9
- package/dist/_virtual/_rolldown/runtime.mjs +0 -13
- package/dist/all-keyed.d.mts +0 -18
- package/dist/all-keyed.mjs +0 -19
- package/dist/binding.d.mts +0 -92
- package/dist/binding.mjs +0 -186
- package/dist/cache.d.mts +0 -164
- package/dist/cache.mjs +0 -184
- package/dist/controls.d.mts +0 -95
- package/dist/controls.mjs +0 -70
- package/dist/hydration.d.mts +0 -84
- package/dist/hydration.mjs +0 -148
- package/dist/infinite.d.mts +0 -5
- package/dist/infinite.mjs +0 -103
- package/dist/key.d.mts +0 -18
- package/dist/key.mjs +0 -36
- package/dist/listen.mjs +0 -19
- package/dist/notify.mjs +0 -21
- package/dist/observer.d.mts +0 -110
- package/dist/observer.mjs +0 -300
- package/dist/platform.d.mts +0 -27
- package/dist/platform.mjs +0 -44
- package/dist/query.d.mts +0 -137
- package/dist/query.mjs +0 -147
- package/dist/request.mjs +0 -47
- package/dist/retry.mjs +0 -25
- package/dist/share.mjs +0 -40
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Feature } from "../../client.mjs";
|
|
2
|
+
//#region src/features/focus-refetch/focus-refetch.d.ts
|
|
3
|
+
/** What `focusRefetch()` reads from a query's `meta`. */
|
|
4
|
+
export interface FocusRefetchMeta {
|
|
5
|
+
/** `false`: don't refetch this query on focus. */
|
|
6
|
+
readonly refetchOnWindowFocus?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Refetch read, stale queries when the window regains focus, except those
|
|
10
|
+
* with `meta: { refetchOnWindowFocus: false }`.
|
|
11
|
+
*
|
|
12
|
+
* @see https://raf-query.katt.dev/docs/window-focus-refetching
|
|
13
|
+
*/
|
|
14
|
+
export declare function focusRefetch(): Feature<{
|
|
15
|
+
readonly meta: FocusRefetchMeta;
|
|
16
|
+
}>;
|
|
17
|
+
//#endregion
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { feature } from "../../client.mjs";
|
|
2
|
+
import { refetchStaleReads } from "../shared.mjs";
|
|
3
|
+
//#region src/features/focus-refetch/focus-refetch.ts
|
|
4
|
+
/**
|
|
5
|
+
* Refetch read, stale queries when the window regains focus, except those
|
|
6
|
+
* with `meta: { refetchOnWindowFocus: false }`.
|
|
7
|
+
*
|
|
8
|
+
* @see https://raf-query.katt.dev/docs/window-focus-refetching
|
|
9
|
+
*/
|
|
10
|
+
function focusRefetch() {
|
|
11
|
+
return feature((client, signal) => {
|
|
12
|
+
const onFocus = () => {
|
|
13
|
+
if (document.visibilityState === "visible") refetchStaleReads(client, "refetchOnWindowFocus");
|
|
14
|
+
};
|
|
15
|
+
document.addEventListener("visibilitychange", onFocus, { signal });
|
|
16
|
+
window.addEventListener("focus", onFocus, { signal });
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { focusRefetch };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Feature } from "../../client.mjs";
|
|
2
|
+
//#region src/features/polling/polling.d.ts
|
|
3
|
+
/** What `polling()` reads from a query's `meta`. */
|
|
4
|
+
export interface PollingMeta {
|
|
5
|
+
/** Refetch every `n` ms while the query is read. */
|
|
6
|
+
readonly refetchInterval?: number | false;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Refetch read queries every `meta.refetchInterval` ms.
|
|
10
|
+
*
|
|
11
|
+
* @see https://raf-query.katt.dev/docs/polling
|
|
12
|
+
*/
|
|
13
|
+
export declare function polling(): Feature<{
|
|
14
|
+
readonly meta: PollingMeta;
|
|
15
|
+
}>;
|
|
16
|
+
//#endregion
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { feature } from "../../client.mjs";
|
|
2
|
+
import { isNumber } from "../../guards.mjs";
|
|
3
|
+
import { fetchEntry } from "../../entry.mjs";
|
|
4
|
+
import { metaOf } from "../shared.mjs";
|
|
5
|
+
//#region src/features/polling/polling.ts
|
|
6
|
+
/**
|
|
7
|
+
* Refetch read queries every `meta.refetchInterval` ms.
|
|
8
|
+
*
|
|
9
|
+
* @see https://raf-query.katt.dev/docs/polling
|
|
10
|
+
*/
|
|
11
|
+
function polling() {
|
|
12
|
+
return feature((client, signal) => {
|
|
13
|
+
const timers = /* @__PURE__ */ new Map();
|
|
14
|
+
const stop = (entry) => {
|
|
15
|
+
client.clock.clearTimeout(timers.get(entry));
|
|
16
|
+
timers.delete(entry);
|
|
17
|
+
};
|
|
18
|
+
const sync = (entry) => {
|
|
19
|
+
const interval = metaOf(entry, "refetchInterval");
|
|
20
|
+
if (!(entry.readers.size > 0 && isNumber(interval) && interval > 0 && client["~entries"].get(entry.hash) === entry)) stop(entry);
|
|
21
|
+
else if (!timers.has(entry)) {
|
|
22
|
+
const tick = () => {
|
|
23
|
+
timers.set(entry, client.clock.setTimeout(tick, interval));
|
|
24
|
+
fetchEntry(entry);
|
|
25
|
+
};
|
|
26
|
+
timers.set(entry, client.clock.setTimeout(tick, interval));
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
client.subscribe((event) => {
|
|
30
|
+
if (event.type === "readers" || event.type === "removed") sync(event.entry);
|
|
31
|
+
}, { signal });
|
|
32
|
+
for (const entry of client["~entries"].values()) sync(entry);
|
|
33
|
+
signal.addEventListener("abort", () => {
|
|
34
|
+
for (const entry of timers.keys()) stop(entry);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { polling };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Feature } from "../../client.mjs";
|
|
2
|
+
//#region src/features/reconnect-refetch/reconnect-refetch.d.ts
|
|
3
|
+
/** What `reconnectRefetch()` reads from a query's `meta`. */
|
|
4
|
+
export interface ReconnectRefetchMeta {
|
|
5
|
+
/** `false`: don't refetch this query when the browser comes back online. */
|
|
6
|
+
readonly refetchOnReconnect?: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Refetch read, stale queries when the browser comes back online, except
|
|
10
|
+
* those with `meta: { refetchOnReconnect: false }`.
|
|
11
|
+
*
|
|
12
|
+
* @see https://raf-query.katt.dev/docs/testing#focus-and-the-network
|
|
13
|
+
*/
|
|
14
|
+
export declare function reconnectRefetch(): Feature<{
|
|
15
|
+
readonly meta: ReconnectRefetchMeta;
|
|
16
|
+
}>;
|
|
17
|
+
//#endregion
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { feature } from "../../client.mjs";
|
|
2
|
+
import { refetchStaleReads } from "../shared.mjs";
|
|
3
|
+
//#region src/features/reconnect-refetch/reconnect-refetch.ts
|
|
4
|
+
/**
|
|
5
|
+
* Refetch read, stale queries when the browser comes back online, except
|
|
6
|
+
* those with `meta: { refetchOnReconnect: false }`.
|
|
7
|
+
*
|
|
8
|
+
* @see https://raf-query.katt.dev/docs/testing#focus-and-the-network
|
|
9
|
+
*/
|
|
10
|
+
function reconnectRefetch() {
|
|
11
|
+
return feature((client, signal) => {
|
|
12
|
+
window.addEventListener("online", () => refetchStaleReads(client, "refetchOnReconnect"), { signal });
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
export { reconnectRefetch };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { isObject } from "../guards.mjs";
|
|
2
|
+
import { fetchEntry, isStale } from "../entry.mjs";
|
|
3
|
+
//#region src/features/shared.ts
|
|
4
|
+
function metaOf(entry, field) {
|
|
5
|
+
const meta = entry.options?.meta;
|
|
6
|
+
if (!isObject(meta) || !(field in meta)) return;
|
|
7
|
+
return meta[field];
|
|
8
|
+
}
|
|
9
|
+
function refetchStaleReads(client, optOut) {
|
|
10
|
+
for (const entry of client["~entries"].values()) if (entry.readers.size > 0 && isStale(entry) && metaOf(entry, optOut) !== false) fetchEntry(entry);
|
|
11
|
+
}
|
|
12
|
+
//#endregion
|
|
13
|
+
export { metaOf, refetchStaleReads };
|
package/dist/guards.mjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
//#region src/guards.ts
|
|
2
|
+
function isFunction(value) {
|
|
3
|
+
return typeof value === "function";
|
|
4
|
+
}
|
|
5
|
+
/** `Array.isArray` that narrows readonly arrays and tuples too. */
|
|
6
|
+
function isArray(value) {
|
|
7
|
+
return Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
function isNumber(value) {
|
|
10
|
+
return typeof value === "number";
|
|
11
|
+
}
|
|
12
|
+
function isObject(value) {
|
|
13
|
+
return typeof value === "object" && value !== null;
|
|
14
|
+
}
|
|
15
|
+
function isString(value) {
|
|
16
|
+
return typeof value === "string";
|
|
17
|
+
}
|
|
18
|
+
/** A `Response`, also one from another realm or polyfill. */
|
|
19
|
+
function isResponse(value) {
|
|
20
|
+
return Object.prototype.toString.call(value) === "[object Response]";
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { isArray, isFunction, isNumber, isObject, isResponse, isString };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { QueryKey } from "../keys/keys.mjs";
|
|
2
|
+
import { Entry } from "../entry.mjs";
|
|
3
|
+
import { QueryClient } from "../client.mjs";
|
|
4
|
+
//#region src/hydration/hydration.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* What a dehydrated pending entry's promise resolves to.
|
|
7
|
+
*
|
|
8
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
9
|
+
*/
|
|
10
|
+
export interface DehydratedResult {
|
|
11
|
+
readonly data?: unknown;
|
|
12
|
+
/** When the sender got the result (ms since epoch). */
|
|
13
|
+
readonly updatedAt: number;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* One cache entry in transit. Settled entries carry `data` and
|
|
17
|
+
* `updatedAt > 0`; entries still loading carry their request's `promise`,
|
|
18
|
+
* so the receiver resumes it instead of fetching again.
|
|
19
|
+
*
|
|
20
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
21
|
+
*/
|
|
22
|
+
export interface DehydratedEntry {
|
|
23
|
+
readonly data?: unknown;
|
|
24
|
+
readonly promise?: Promise<DehydratedResult>;
|
|
25
|
+
readonly queryKey: QueryKey;
|
|
26
|
+
/** When the data was fetched; `0` while pending. */
|
|
27
|
+
readonly updatedAt: number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* A cache, serialized by {@link dehydrate} for {@link hydrate}.
|
|
31
|
+
*
|
|
32
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
33
|
+
*/
|
|
34
|
+
export interface DehydratedState {
|
|
35
|
+
readonly entries: ReadonlyArray<DehydratedEntry>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Options for {@link dehydrate} and {@link dehydrateStream}.
|
|
39
|
+
*
|
|
40
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
41
|
+
*/
|
|
42
|
+
export interface DehydrateOptions {
|
|
43
|
+
/**
|
|
44
|
+
* Which entries to send. Default: entries with data or a request in
|
|
45
|
+
* flight. An entry with neither has nothing to send and stays behind.
|
|
46
|
+
*/
|
|
47
|
+
readonly filter?: (entry: Entry) => boolean;
|
|
48
|
+
/** Send a pending entry's error as is, instead of a `RedactedError`. */
|
|
49
|
+
readonly keepErrors?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* Hashes already sent in this transfer (one request). Those entries are
|
|
52
|
+
* skipped, and the ones sent are added.
|
|
53
|
+
*/
|
|
54
|
+
readonly sent?: Set<string>;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* What a streamed pending entry rejects with when its server request threw:
|
|
58
|
+
* the browser learns which query failed, not why.
|
|
59
|
+
*
|
|
60
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration#errors
|
|
61
|
+
*/
|
|
62
|
+
export declare class RedactedError extends Error {
|
|
63
|
+
readonly name = "RedactedError";
|
|
64
|
+
readonly hash: string;
|
|
65
|
+
constructor(hash: string);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* The default filter: entries with data or a request in flight.
|
|
69
|
+
*
|
|
70
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
71
|
+
*/
|
|
72
|
+
export declare function canDehydrate(entry: Entry): boolean;
|
|
73
|
+
/**
|
|
74
|
+
* Snapshot the client's entries for transfer.
|
|
75
|
+
*
|
|
76
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
77
|
+
*/
|
|
78
|
+
export declare function dehydrate(client: QueryClient, options?: DehydrateOptions): DehydratedState;
|
|
79
|
+
/**
|
|
80
|
+
* What {@link dehydrateStream} returns: the entries to send first, then their updates.
|
|
81
|
+
*
|
|
82
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
83
|
+
*/
|
|
84
|
+
export interface DehydratedStream {
|
|
85
|
+
/** Stop: send what changed, then end `updates`. */
|
|
86
|
+
close(): void;
|
|
87
|
+
/** The entries to send first. */
|
|
88
|
+
readonly initial: DehydratedState;
|
|
89
|
+
/** Entries as they start or settle, batched per microtask. */
|
|
90
|
+
readonly updates: ReadableStream<DehydratedState>;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The cache now, then entries as they start, until `close()`. Snapshots and
|
|
94
|
+
* subscribes in one synchronous step, so no change falls between the two.
|
|
95
|
+
*
|
|
96
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
97
|
+
*/
|
|
98
|
+
export declare function dehydrateStream(client: QueryClient, options?: DehydrateOptions): DehydratedStream;
|
|
99
|
+
/**
|
|
100
|
+
* Write dehydrated entries into a client. Existing entries only take newer
|
|
101
|
+
* data. A pending entry the client has nothing for resumes the sender's
|
|
102
|
+
* request; one that has data or a request of its own waits for the
|
|
103
|
+
* sender's, takes it if newer, and revalidates if the sender's failed.
|
|
104
|
+
*
|
|
105
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
106
|
+
*/
|
|
107
|
+
export declare function hydrate(client: QueryClient, state: DehydratedState): void;
|
|
108
|
+
//#endregion
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { adopt, entryForKey, fetchEntry, isStale, write } from "../entry.mjs";
|
|
2
|
+
//#region src/hydration/hydration.ts
|
|
3
|
+
/**
|
|
4
|
+
* What a streamed pending entry rejects with when its server request threw:
|
|
5
|
+
* the browser learns which query failed, not why.
|
|
6
|
+
*
|
|
7
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration#errors
|
|
8
|
+
*/
|
|
9
|
+
var RedactedError = class extends Error {
|
|
10
|
+
name = "RedactedError";
|
|
11
|
+
hash;
|
|
12
|
+
constructor(hash) {
|
|
13
|
+
super(`Query ${hash} failed on the server`);
|
|
14
|
+
this.hash = hash;
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* The default filter: entries with data or a request in flight.
|
|
19
|
+
*
|
|
20
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
21
|
+
*/
|
|
22
|
+
function canDehydrate(entry) {
|
|
23
|
+
const state = entry.state;
|
|
24
|
+
return state.hasData || state.isFetching;
|
|
25
|
+
}
|
|
26
|
+
function dehydrateEntry(entry, options) {
|
|
27
|
+
const state = entry.state;
|
|
28
|
+
const base = { queryKey: entry.key };
|
|
29
|
+
if (state.hasData) return {
|
|
30
|
+
...base,
|
|
31
|
+
data: state.data,
|
|
32
|
+
updatedAt: state.updatedAt
|
|
33
|
+
};
|
|
34
|
+
const run = entry.run;
|
|
35
|
+
if (!run) return;
|
|
36
|
+
const promise = run.done.promise.then((outcome) => {
|
|
37
|
+
if (outcome.ok) return {
|
|
38
|
+
data: entry.state.data,
|
|
39
|
+
updatedAt: entry.state.updatedAt
|
|
40
|
+
};
|
|
41
|
+
throw options.keepErrors ? outcome.error : new RedactedError(entry.hash);
|
|
42
|
+
});
|
|
43
|
+
promise.catch(() => {});
|
|
44
|
+
return {
|
|
45
|
+
...base,
|
|
46
|
+
promise,
|
|
47
|
+
updatedAt: 0
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function dehydrateEntries(entries, options) {
|
|
51
|
+
const filter = options.filter ?? canDehydrate;
|
|
52
|
+
const sent = options.sent;
|
|
53
|
+
const dehydrated = [];
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
const dehydratedEntry = !sent?.has(entry.hash) && filter(entry) ? dehydrateEntry(entry, options) : void 0;
|
|
56
|
+
if (dehydratedEntry) {
|
|
57
|
+
sent?.add(entry.hash);
|
|
58
|
+
dehydrated.push(dehydratedEntry);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { entries: dehydrated };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Snapshot the client's entries for transfer.
|
|
65
|
+
*
|
|
66
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
67
|
+
*/
|
|
68
|
+
function dehydrate(client, options = {}) {
|
|
69
|
+
return dehydrateEntries(client["~entries"].values(), options);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The cache now, then entries as they start, until `close()`. Snapshots and
|
|
73
|
+
* subscribes in one synchronous step, so no change falls between the two.
|
|
74
|
+
*
|
|
75
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
76
|
+
*/
|
|
77
|
+
function dehydrateStream(client, options = {}) {
|
|
78
|
+
const send = {
|
|
79
|
+
...options,
|
|
80
|
+
sent: options.sent ?? /* @__PURE__ */ new Set()
|
|
81
|
+
};
|
|
82
|
+
const unsubscribe = new AbortController();
|
|
83
|
+
let controller;
|
|
84
|
+
const updates = new ReadableStream({ start(streamController) {
|
|
85
|
+
controller = streamController;
|
|
86
|
+
} });
|
|
87
|
+
let batch;
|
|
88
|
+
const flush = () => {
|
|
89
|
+
const changed = batch;
|
|
90
|
+
batch = void 0;
|
|
91
|
+
if (!changed) return;
|
|
92
|
+
const state = dehydrateEntries(changed, send);
|
|
93
|
+
if (state.entries.length > 0) controller?.enqueue(state);
|
|
94
|
+
};
|
|
95
|
+
client.subscribe((event) => {
|
|
96
|
+
if (event.type === "removed" || send.sent.has(event.entry.hash)) return;
|
|
97
|
+
if (!batch) {
|
|
98
|
+
batch = /* @__PURE__ */ new Set();
|
|
99
|
+
queueMicrotask(flush);
|
|
100
|
+
}
|
|
101
|
+
batch.add(event.entry);
|
|
102
|
+
}, { signal: unsubscribe.signal });
|
|
103
|
+
return {
|
|
104
|
+
close() {
|
|
105
|
+
if (unsubscribe.signal.aborted) return;
|
|
106
|
+
flush();
|
|
107
|
+
unsubscribe.abort();
|
|
108
|
+
controller?.close();
|
|
109
|
+
},
|
|
110
|
+
initial: dehydrate(client, send),
|
|
111
|
+
updates
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function revalidate(entry) {
|
|
115
|
+
if (entry.options && entry.readers.size > 0 && isStale(entry)) fetchEntry(entry);
|
|
116
|
+
}
|
|
117
|
+
/** Take the sender's data if it's newer than the entry's. */
|
|
118
|
+
function takeNewer(entry, result) {
|
|
119
|
+
if (!entry.state.hasData || entry.state.updatedAt < result.updatedAt) write(entry, result.data, result.updatedAt, entry.client["~seq"]);
|
|
120
|
+
else revalidate(entry);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Write dehydrated entries into a client. Existing entries only take newer
|
|
124
|
+
* data. A pending entry the client has nothing for resumes the sender's
|
|
125
|
+
* request; one that has data or a request of its own waits for the
|
|
126
|
+
* sender's, takes it if newer, and revalidates if the sender's failed.
|
|
127
|
+
*
|
|
128
|
+
* @see https://raf-query.katt.dev/docs/ssr-and-hydration
|
|
129
|
+
*/
|
|
130
|
+
function hydrate(client, state) {
|
|
131
|
+
for (const sent of state.entries) {
|
|
132
|
+
const entry = entryForKey(client, sent.queryKey);
|
|
133
|
+
if (sent.promise === void 0) takeNewer(entry, sent);
|
|
134
|
+
else {
|
|
135
|
+
const result = Promise.resolve(sent.promise);
|
|
136
|
+
if (!entry.state.hasData && !entry.run) adopt(entry, result);
|
|
137
|
+
else result.then((settled) => takeNewer(entry, settled), () => revalidate(entry));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//#endregion
|
|
142
|
+
export { RedactedError, canDehydrate, dehydrate, dehydrateStream, hydrate };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
1
|
+
import { Group, QueryKey, group } from "./keys/keys.mjs";
|
|
2
|
+
import { QueryMeta } from "./root.mjs";
|
|
3
|
+
import { QueryInfo, Target } from "./keys/targets.mjs";
|
|
4
|
+
import { DataOf, InfiniteQueryFactory, InputOf, QueryFactory, QueryFunctionContext, QueryOptions, QueryOptionsBuilder, SingleQueryFactory, StreamOptions, bindQueryOptions, queryOptions } from "./options.mjs";
|
|
5
|
+
import { Thenable } from "./thenable.mjs";
|
|
6
|
+
import { abortError } from "./entry.mjs";
|
|
7
|
+
import { Clock, Feature, Link, Operation, QueryClient, QueryClientConfig, createQueryClient, feature, link } from "./client.mjs";
|
|
8
|
+
import { focusRefetch } from "./features/focus-refetch/focus-refetch.mjs";
|
|
9
|
+
import { reconnectRefetch } from "./features/reconnect-refetch/reconnect-refetch.mjs";
|
|
10
|
+
import { polling } from "./features/polling/polling.mjs";
|
|
11
|
+
import { DehydrateOptions, DehydratedEntry, DehydratedResult, DehydratedState, DehydratedStream, RedactedError, canDehydrate, dehydrate, dehydrateStream, hydrate } from "./hydration/hydration.mjs";
|
|
12
|
+
import { InfiniteData, InfiniteQueryFunctionContext, InfiniteQueryOptions, InfiniteQueryOptionsBuilder, bindInfiniteQueryOptions, infiniteQueryOptions } from "./infinite/infinite.mjs";
|
|
13
|
+
import { ClientFor, PartialInput, getData, invalidateAll, invalidateMany, invalidateOne, isFetching, removeAll, removeMany, removeOne, resetAll, resetMany, resetOne, setData } from "./verbs.mjs";
|
|
14
|
+
import { PageControls, PageDirection, PageState, fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage } from "./infinite/paging.mjs";
|
|
15
|
+
import { RetryOptions, retry } from "./links/retry/retry.mjs";
|
|
16
|
+
import { QueryTimeoutError, timeout } from "./links/timeout/timeout.mjs";
|
|
17
|
+
import { LoadEntry, LoadEntryOptions, Loaded, QueryHandle, load, prefetch, revalidate, skipToken, swr, waterfallHandle } from "./load.mjs";
|
|
18
|
+
import { Recommended, recommended } from "./presets.mjs";
|
|
19
|
+
export { type ClientFor, type Clock, type DataOf, type DehydrateOptions, type DehydratedEntry, type DehydratedResult, type DehydratedState, type DehydratedStream, type Feature, type Group, type InfiniteData, type InfiniteQueryFactory, type InfiniteQueryFunctionContext, type InfiniteQueryOptions, type InfiniteQueryOptionsBuilder, type InputOf, type Link, type LoadEntry, type LoadEntryOptions, type Loaded, type Operation, type PageControls, type PageDirection, type PageState, type PartialInput, type QueryClient, type QueryClientConfig, type QueryFactory, type QueryFunctionContext, type QueryHandle, type QueryInfo, type QueryKey, type QueryMeta, type QueryOptions, type QueryOptionsBuilder, QueryTimeoutError, type Recommended, RedactedError, type RetryOptions, type SingleQueryFactory, type StreamOptions, type Target, type Thenable, abortError, bindInfiniteQueryOptions, bindQueryOptions, canDehydrate, createQueryClient, dehydrate, dehydrateStream, feature, fetchNextPage, fetchPreviousPage, focusRefetch, getData, group, hasNextPage, hasPreviousPage, hydrate, infiniteQueryOptions, invalidateAll, invalidateMany, invalidateOne, isFetching, link, load, polling, prefetch, queryOptions, recommended, reconnectRefetch, removeAll, removeMany, removeOne, resetAll, resetMany, resetOne, retry, revalidate, setData, skipToken, swr, timeout, waterfallHandle };
|
package/dist/index.mjs
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
1
|
+
import { createQueryClient, feature, link } from "./client.mjs";
|
|
2
|
+
import { group } from "./keys/keys.mjs";
|
|
3
|
+
import { bindQueryOptions, queryOptions } from "./options.mjs";
|
|
4
|
+
import { abortError } from "./entry.mjs";
|
|
5
|
+
import { focusRefetch } from "./features/focus-refetch/focus-refetch.mjs";
|
|
6
|
+
import { reconnectRefetch } from "./features/reconnect-refetch/reconnect-refetch.mjs";
|
|
7
|
+
import { polling } from "./features/polling/polling.mjs";
|
|
8
|
+
import { RedactedError, canDehydrate, dehydrate, dehydrateStream, hydrate } from "./hydration/hydration.mjs";
|
|
9
|
+
import { bindInfiniteQueryOptions, infiniteQueryOptions } from "./infinite/infinite.mjs";
|
|
10
|
+
import { fetchNextPage, fetchPreviousPage, hasNextPage, hasPreviousPage } from "./infinite/paging.mjs";
|
|
11
|
+
import { retry } from "./links/retry/retry.mjs";
|
|
12
|
+
import { QueryTimeoutError, timeout } from "./links/timeout/timeout.mjs";
|
|
13
|
+
import { load, prefetch, revalidate, skipToken, swr, waterfallHandle } from "./load.mjs";
|
|
14
|
+
import { recommended } from "./presets.mjs";
|
|
15
|
+
import { getData, invalidateAll, invalidateMany, invalidateOne, isFetching, removeAll, removeMany, removeOne, resetAll, resetMany, resetOne, setData } from "./verbs.mjs";
|
|
16
|
+
export { QueryTimeoutError, RedactedError, abortError, bindInfiniteQueryOptions, bindQueryOptions, canDehydrate, createQueryClient, dehydrate, dehydrateStream, feature, fetchNextPage, fetchPreviousPage, focusRefetch, getData, group, hasNextPage, hasPreviousPage, hydrate, infiniteQueryOptions, invalidateAll, invalidateMany, invalidateOne, isFetching, link, load, polling, prefetch, queryOptions, recommended, reconnectRefetch, removeAll, removeMany, removeOne, resetAll, resetMany, resetOne, retry, revalidate, setData, skipToken, swr, timeout, waterfallHandle };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { QueryKey } from "../keys/keys.mjs";
|
|
2
|
+
import { DefaultTypes, RootOf, RootTypes } from "../root.mjs";
|
|
3
|
+
import { BaseOptions, FromQueryFn, QueryFunctionContext, QueryOptions } from "../options.mjs";
|
|
4
|
+
import { Operation } from "../client.mjs";
|
|
5
|
+
//#region src/infinite/infinite.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* A paged query's data.
|
|
8
|
+
*
|
|
9
|
+
* @see https://raf-query.katt.dev/docs/infinite-queries
|
|
10
|
+
*/
|
|
11
|
+
export interface InfiniteData<TPage, TParam = unknown> {
|
|
12
|
+
readonly pageParams: ReadonlyArray<TParam>;
|
|
13
|
+
readonly pages: ReadonlyArray<TPage>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* What a paged `queryFn` gets: the query function context, plus the page param and the direction it loads in.
|
|
17
|
+
*
|
|
18
|
+
* @see https://raf-query.katt.dev/docs/infinite-queries
|
|
19
|
+
*/
|
|
20
|
+
export interface InfiniteQueryFunctionContext<TKey extends QueryKey, TParam> extends QueryFunctionContext<TKey> {
|
|
21
|
+
readonly direction: "backward" | "forward";
|
|
22
|
+
readonly pageParam: TParam;
|
|
23
|
+
}
|
|
24
|
+
type PageParamFn<TPage, TParam> = (page: TPage, pages: ReadonlyArray<TPage>, pageParam: TParam) => TParam | null | undefined;
|
|
25
|
+
interface InfiniteOptions<TKey extends QueryKey, TPage, TParam, TData, TTypes extends RootTypes = RootTypes> extends BaseOptions<TKey, TTypes> {
|
|
26
|
+
readonly getNextPageParam: PageParamFn<TPage, TParam>;
|
|
27
|
+
readonly getPreviousPageParam?: PageParamFn<TPage, TParam>;
|
|
28
|
+
readonly initialPageParam: TParam;
|
|
29
|
+
/** Keep at most this many pages, dropping from the other end. */
|
|
30
|
+
readonly maxPages?: number;
|
|
31
|
+
readonly select?: (data: InfiniteData<TPage, TParam>) => TData;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* What `infiniteQueryOptions` returns.
|
|
35
|
+
*
|
|
36
|
+
* @see https://raf-query.katt.dev/docs/infinite-queries
|
|
37
|
+
*/
|
|
38
|
+
export type InfiniteQueryOptions<TKey extends QueryKey = QueryKey, TPage = unknown, TParam = unknown, TData = InfiniteData<TPage, TParam>, TTypes extends RootTypes = RootTypes> = QueryOptions<TKey, InfiniteData<TPage, TParam>, TData, TTypes> & Pick<InfiniteOptions<TKey, TPage, TParam, TData, TTypes>, "getNextPageParam" | "getPreviousPageParam" | "initialPageParam" | "maxPages"> & {
|
|
39
|
+
readonly "~run": (op: Operation) => Promise<unknown>;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* `infiniteQueryOptions`, with `meta` checked against the root `TTypes`.
|
|
43
|
+
*
|
|
44
|
+
* @see https://raf-query.katt.dev/docs/links-and-features#typing-meta-by-its-root
|
|
45
|
+
*/
|
|
46
|
+
export interface InfiniteQueryOptionsBuilder<TTypes extends RootTypes> {
|
|
47
|
+
<const TKey extends QueryKey, TPage, TParam, TData = InfiniteData<TPage, TParam>>(options: InfiniteOptions<TKey, FromQueryFn<TPage>, TParam, TData, TTypes> & {
|
|
48
|
+
readonly queryFn: (context: InfiniteQueryFunctionContext<TKey, TParam>) => PromiseLike<TPage> | TPage;
|
|
49
|
+
}): InfiniteQueryOptions<TKey, TPage, TParam, TData, TTypes>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Typed options for a paged query factory. Readers get `{ pages, pageParams }`
|
|
53
|
+
* and, on their state, `fetchNextPage` and `fetchPreviousPage`:
|
|
54
|
+
*
|
|
55
|
+
* ```ts
|
|
56
|
+
* export const commentsQuery = (input: { slug: string }) =>
|
|
57
|
+
* infiniteQueryOptions({
|
|
58
|
+
* queryKey: comments(input.slug),
|
|
59
|
+
* queryFn: ({ pageParam, signal }) => listComments({ ...input, cursor: pageParam }, { signal }),
|
|
60
|
+
* initialPageParam: 0,
|
|
61
|
+
* getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
62
|
+
* });
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* Its `meta` is any record. To check `meta` against a client's links and
|
|
66
|
+
* features, use the builder {@link bindInfiniteQueryOptions} returns.
|
|
67
|
+
*
|
|
68
|
+
* @see https://raf-query.katt.dev/docs/infinite-queries
|
|
69
|
+
*/
|
|
70
|
+
export declare const infiniteQueryOptions: InfiniteQueryOptionsBuilder<DefaultTypes>;
|
|
71
|
+
/**
|
|
72
|
+
* `infiniteQueryOptions` bound to a root, like `bindQueryOptions`. Bind it
|
|
73
|
+
* next to `queryOptions`, in apps that page:
|
|
74
|
+
*
|
|
75
|
+
* ```ts
|
|
76
|
+
* // raf.ts
|
|
77
|
+
* export const infiniteQueryOptions = bindInfiniteQueryOptions<typeof makeQueryClient>();
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @see https://raf-query.katt.dev/docs/links-and-features#typing-meta-by-its-root
|
|
81
|
+
*/
|
|
82
|
+
export declare function bindInfiniteQueryOptions<T>(): InfiniteQueryOptionsBuilder<RootOf<T>>;
|
|
83
|
+
//#endregion
|