@raf-query/core 0.13.2 → 0.14.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 +11 -1
- package/README.md +8 -5
- package/dist/client.d.mts +99 -72
- package/dist/client.mjs +19 -36
- package/dist/entry.mjs +18 -9
- package/dist/features/focus-refetch/focus-refetch.d.mts +13 -9
- package/dist/features/focus-refetch/focus-refetch.mjs +4 -4
- package/dist/features/polling/polling.d.mts +13 -9
- package/dist/features/polling/polling.mjs +6 -6
- package/dist/features/reconnect-refetch/reconnect-refetch.d.mts +13 -9
- package/dist/features/reconnect-refetch/reconnect-refetch.mjs +4 -4
- package/dist/features/shared.mjs +8 -6
- package/dist/index.d.mts +8 -8
- package/dist/index.mjs +2 -2
- package/dist/infinite/infinite.d.mts +17 -15
- package/dist/infinite/infinite.mjs +5 -4
- package/dist/internal.d.mts +3 -7
- package/dist/keys/targets.d.mts +7 -8
- package/dist/keys/targets.mjs +6 -5
- package/dist/links/retry/retry.d.mts +14 -10
- package/dist/links/retry/retry.mjs +5 -5
- package/dist/links/timeout/timeout.d.mts +2 -2
- package/dist/links/timeout/timeout.mjs +3 -2
- package/dist/load.d.mts +9 -2
- package/dist/options.d.mts +61 -39
- package/dist/options.mjs +15 -6
- package/dist/presets.d.mts +9 -20
- package/dist/presets.mjs +8 -10
- package/dist/root.d.mts +86 -40
- package/dist/verbs.d.mts +8 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.14.0](https://github.com/KATT/raf-query/compare/0.13.2...0.14.0) (2026-11-11)
|
|
4
|
+
|
|
5
|
+
### ⚠ BREAKING CHANGES
|
|
6
|
+
|
|
7
|
+
* **core:** parts declare the options and queryFn context a client's queries get ([#250](https://github.com/KATT/raf-query/issues/250))
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **core:** parts declare the options and queryFn context a client's queries get ([#250](https://github.com/KATT/raf-query/issues/250)) ([931b7ed](https://github.com/KATT/raf-query/commit/931b7ed83837576c607b63e2dfdd1c795129cd41))
|
|
12
|
+
|
|
3
13
|
## [0.13.2](https://github.com/KATT/raf-query/compare/0.13.1...0.13.2) (2026-11-10)
|
|
4
14
|
|
|
5
15
|
### Bug Fixes
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
- **core:** a prefetch never drops pages keepPages turns down ([#249](https://github.com/KATT/raf-query/issues/249)) ([3c66020](https://github.com/KATT/raf-query/commit/3c66020ee7562c6e2592017a1b11af94bc89e340))
|
|
8
18
|
|
|
9
19
|
## [0.13.1](https://github.com/KATT/raf-query/compare/0.13.0...0.13.1) (2026-11-10)
|
|
10
20
|
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @raf-query/core
|
|
2
2
|
|
|
3
|
-
The framework-agnostic query client behind [raf-query](https://raf-query.katt.dev): a small cache that dedupes requests, keeps entries fresh, and hands out serializable handles. Retries, timeouts, refetch on focus or reconnect, polling and hydration are opt-in
|
|
3
|
+
The framework-agnostic query client behind [raf-query](https://raf-query.katt.dev): a small cache that dedupes requests, keeps entries fresh, and hands out serializable handles. Retries, timeouts, refetch on focus or reconnect, polling and hydration are opt-in, as parts you give the client or exports you import, so a client bundles only what it turns on.
|
|
4
4
|
|
|
5
5
|
**[Documentation](https://raf-query.katt.dev)** · [API reference](https://raf-query.katt.dev/docs/api-reference#raf-querycore) · [GitHub](https://github.com/KATT/raf-query)
|
|
6
6
|
|
|
@@ -37,7 +37,10 @@ export function issueQuery(input: { id: number }) {
|
|
|
37
37
|
});
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const queryClient = createQueryClient({
|
|
40
|
+
const queryClient = createQueryClient({
|
|
41
|
+
parts: [retry()],
|
|
42
|
+
defaults: { staleTime: 5000 },
|
|
43
|
+
});
|
|
41
44
|
|
|
42
45
|
const [, data] = await load(queryClient, {
|
|
43
46
|
issue: revalidate(issueQuery, { id: 1 }),
|
|
@@ -49,9 +52,9 @@ await invalidateOne(queryClient, issueQuery, { id: 1 });
|
|
|
49
52
|
|
|
50
53
|
## Entry points
|
|
51
54
|
|
|
52
|
-
| Import | Use
|
|
53
|
-
| ----------------- |
|
|
54
|
-
| `@raf-query/core` | `createQueryClient`, query factories, `load`, the verbs, the built-in
|
|
55
|
+
| Import | Use |
|
|
56
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
57
|
+
| `@raf-query/core` | `createQueryClient`, query factories, `load`, the verbs, the built-in parts (`retry()`, `polling()` and so on), `group`, `dehydrate` and `hydrate` |
|
|
55
58
|
|
|
56
59
|
## License
|
|
57
60
|
|
package/dist/client.d.mts
CHANGED
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import { QueryKey } from "./keys/keys.mjs";
|
|
2
|
-
import { AnyPart, AnyRoot, Decl, Declares, MetaOf, MetaOfDecl, RootTypes } from "./root.mjs";
|
|
3
2
|
import { Stamp } from "./keys/targets.mjs";
|
|
4
|
-
import {
|
|
3
|
+
import { AnyDeclares, Decl, Declares, NoDecl, NoParts, OptionsOfDecl, Root, RootOfParts, RootTypes } from "./root.mjs";
|
|
4
|
+
import { CoreOptions, QueryOptions } from "./options.mjs";
|
|
5
5
|
import { Listeners, SubscribeOptions } from "./subscribe.mjs";
|
|
6
6
|
import { Entry } from "./entry.mjs";
|
|
7
7
|
//#region src/client.d.ts
|
|
8
|
-
type LinkFunction = (op: Operation, next: (op: Operation) => Promise<unknown>) => Promise<unknown>;
|
|
9
|
-
type FeatureFunction = (client: QueryClient, signal: AbortSignal) => void;
|
|
10
|
-
/** Any link, whatever `meta` it reads. */
|
|
11
|
-
export type AnyLink = LinkFunction & AnyPart;
|
|
12
|
-
/** Any feature, whatever `meta` it reads. */
|
|
13
|
-
export type AnyFeature = FeatureFunction & AnyPart;
|
|
14
8
|
/**
|
|
15
9
|
* Time and timers, injectable so tests control them without patching globals.
|
|
16
10
|
*
|
|
@@ -23,11 +17,12 @@ export interface Clock {
|
|
|
23
17
|
}
|
|
24
18
|
export declare const systemClock: Clock;
|
|
25
19
|
/**
|
|
26
|
-
* One request, as
|
|
20
|
+
* One request, as a part's link sees it. `TDecl` is what the part declares:
|
|
21
|
+
* `options` has the fields it reads.
|
|
27
22
|
*
|
|
28
|
-
* @see https://raf-query.katt.dev/docs/links-and-features#
|
|
23
|
+
* @see https://raf-query.katt.dev/docs/links-and-features#parts
|
|
29
24
|
*/
|
|
30
|
-
export interface Operation<
|
|
25
|
+
export interface Operation<TDecl extends Decl = Decl> {
|
|
31
26
|
readonly client: QueryClient;
|
|
32
27
|
/** The entry's data when the request started (paged queries extend it). */
|
|
33
28
|
readonly data: unknown;
|
|
@@ -36,54 +31,80 @@ export interface Operation<TMeta extends object = object> {
|
|
|
36
31
|
readonly hasData: boolean;
|
|
37
32
|
readonly hash: string;
|
|
38
33
|
readonly input: unknown;
|
|
39
|
-
/** The query's
|
|
40
|
-
readonly
|
|
41
|
-
readonly options: QueryOptions;
|
|
34
|
+
/** The query's options, over the client's `defaults`. */
|
|
35
|
+
readonly options: Partial<OptionsOfDecl<TDecl>> & QueryOptions;
|
|
42
36
|
/** For paged queries: how many pages a first load fetches. */
|
|
43
37
|
readonly pages: number | undefined;
|
|
44
38
|
readonly queryKey: QueryKey;
|
|
45
39
|
readonly signal: AbortSignal;
|
|
46
40
|
}
|
|
41
|
+
type LinkFunction = (op: Operation, next: (op: Operation) => Promise<unknown>) => Promise<unknown>;
|
|
42
|
+
type SetupFunction = (client: QueryClient, signal: AbortSignal) => void;
|
|
47
43
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
* @see https://raf-query.katt.dev/docs/links-and-features#links-and-features
|
|
53
|
-
*/
|
|
54
|
-
export type Link<TDecl extends Decl = Decl> = LinkFunction & Declares<TDecl>;
|
|
55
|
-
/**
|
|
56
|
-
* A cache-wide behavior, started when a provider mounts the client and
|
|
57
|
-
* stopped by `signal`. `TDecl` is the `meta` it reads: see {@link feature}.
|
|
44
|
+
* One piece of a client's behavior: any of a link that wraps every request,
|
|
45
|
+
* a feature's `setup`, and values for every `queryFn`'s context. `TDecl` is
|
|
46
|
+
* what it declares: see {@link part}.
|
|
58
47
|
*
|
|
59
|
-
* @see https://raf-query.katt.dev/docs/links-and-features#
|
|
48
|
+
* @see https://raf-query.katt.dev/docs/links-and-features#parts
|
|
60
49
|
*/
|
|
61
|
-
export
|
|
50
|
+
export interface Part<TDecl extends Decl = NoDecl> extends Declares<TDecl> {
|
|
51
|
+
readonly context?: object;
|
|
52
|
+
readonly link?: LinkFunction;
|
|
53
|
+
readonly setup?: SetupFunction;
|
|
54
|
+
}
|
|
55
|
+
/** Any part, whatever it declares. */
|
|
56
|
+
export interface AnyPart extends AnyDeclares {
|
|
57
|
+
readonly context?: object;
|
|
58
|
+
readonly link?: LinkFunction;
|
|
59
|
+
readonly setup?: SetupFunction;
|
|
60
|
+
}
|
|
61
|
+
/** What {@link part} takes: `context` is required when the part declares one. */
|
|
62
|
+
export type PartDefinition<TDecl extends Decl> = (TDecl extends {
|
|
63
|
+
readonly context: infer TContext extends object;
|
|
64
|
+
} ? {
|
|
65
|
+
/** Merged into the context of every `queryFn` the client runs. */
|
|
66
|
+
readonly context: TContext;
|
|
67
|
+
} : {
|
|
68
|
+
readonly context?: never;
|
|
69
|
+
}) & {
|
|
70
|
+
/**
|
|
71
|
+
* Wraps every request, like a tRPC link. Call `next(op)` to run the
|
|
72
|
+
* rest of the chain; the last link is the query's own `queryFn`.
|
|
73
|
+
*/
|
|
74
|
+
readonly link?: (op: Operation<TDecl>, next: (op: Operation) => Promise<unknown>) => Promise<unknown>;
|
|
75
|
+
/**
|
|
76
|
+
* A cache-wide behavior, started when a provider mounts the client and
|
|
77
|
+
* stopped by `signal`. The client is typed with this part, so it runs
|
|
78
|
+
* the factories of a builder bound to it.
|
|
79
|
+
*/
|
|
80
|
+
readonly setup?: (client: QueryClient<Root<TDecl>>, signal: AbortSignal) => void;
|
|
81
|
+
};
|
|
82
|
+
/** A declaration without a `context`: its part needs no definition. */
|
|
83
|
+
interface OptionsDecl {
|
|
84
|
+
readonly context?: never;
|
|
85
|
+
readonly options?: object;
|
|
86
|
+
}
|
|
62
87
|
/**
|
|
63
|
-
* A
|
|
64
|
-
*
|
|
88
|
+
* A part of a client, with what it declares: the per-query `options` it
|
|
89
|
+
* reads and the `context` it gives every `queryFn`. A client's queries take
|
|
90
|
+
* those options, and get that context, only when the client has the part.
|
|
65
91
|
*
|
|
66
92
|
* ```ts
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
93
|
+
* interface Traced {
|
|
94
|
+
* readonly options: { readonly traceId?: string };
|
|
95
|
+
* }
|
|
96
|
+
* const traced = part<Traced>({
|
|
97
|
+
* link: (op, next) => withTrace(op.options.traceId, () => next(op)),
|
|
98
|
+
* });
|
|
70
99
|
* ```
|
|
71
100
|
*
|
|
72
|
-
*
|
|
73
|
-
|
|
74
|
-
export declare function link<TDecl extends Decl = Decl>(run: (op: Operation<MetaOfDecl<TDecl> & object>, next: (op: Operation) => Promise<unknown>) => Promise<unknown>): Link<TDecl>;
|
|
75
|
-
/**
|
|
76
|
-
* A feature that declares the `meta` fields it reads. Without `setup`, it
|
|
77
|
-
* only declares them: a root's own fields, such as an `owner` its targets
|
|
78
|
-
* match on.
|
|
101
|
+
* A part without a link or `setup` only declares: an app's own options, such
|
|
102
|
+
* as an `owner` its targets match on.
|
|
79
103
|
*
|
|
80
|
-
*
|
|
81
|
-
* const ownership = feature<{ meta: { owner?: "posts-team" | "users-team" } }>();
|
|
82
|
-
* ```
|
|
83
|
-
*
|
|
84
|
-
* @see https://raf-query.katt.dev/docs/links-and-features#links-and-features
|
|
104
|
+
* @see https://raf-query.katt.dev/docs/links-and-features#parts
|
|
85
105
|
*/
|
|
86
|
-
export declare function
|
|
106
|
+
export declare function part<TDecl extends OptionsDecl = NoDecl>(definition?: PartDefinition<NoInfer<TDecl>>): Part<NoInfer<TDecl>>;
|
|
107
|
+
export declare function part<TDecl extends Decl>(definition: PartDefinition<NoInfer<TDecl>>): Part<NoInfer<TDecl>>;
|
|
87
108
|
export type ClientEvent = {
|
|
88
109
|
readonly entry: Entry;
|
|
89
110
|
readonly type: "added" | "removed" | "updated";
|
|
@@ -92,33 +113,34 @@ export type ClientEvent = {
|
|
|
92
113
|
readonly type: "readers";
|
|
93
114
|
};
|
|
94
115
|
/**
|
|
95
|
-
* What `createQueryClient` takes:
|
|
116
|
+
* What `createQueryClient` takes: its parts and the cache-wide defaults.
|
|
96
117
|
*
|
|
97
118
|
* @see https://raf-query.katt.dev/docs/important-defaults#changing-the-defaults
|
|
98
119
|
*/
|
|
99
120
|
export interface QueryClientConfig {
|
|
100
121
|
readonly clock?: Clock;
|
|
101
|
-
/**
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
122
|
+
/**
|
|
123
|
+
* Client-wide defaults for per-query options: the core's (`staleTime`,
|
|
124
|
+
* `gcTime`, `structuralSharing`) and those the parts declare. `ssr` is a
|
|
125
|
+
* query's own.
|
|
126
|
+
*/
|
|
127
|
+
readonly defaults?: object;
|
|
105
128
|
/** Default: no `window`. Server clients never gc and never revalidate cached data. */
|
|
106
129
|
readonly isServer?: boolean;
|
|
107
|
-
/**
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
readonly structuralSharing?: boolean;
|
|
130
|
+
/**
|
|
131
|
+
* The client's behaviors. Links wrap every request, outermost first;
|
|
132
|
+
* features start while a provider has the client mounted.
|
|
133
|
+
*/
|
|
134
|
+
readonly parts?: ReadonlyArray<AnyPart>;
|
|
113
135
|
}
|
|
114
136
|
/**
|
|
115
|
-
* A query cache. `TTypes` is its root's types
|
|
116
|
-
*
|
|
117
|
-
* `QueryClient` type takes
|
|
137
|
+
* A query cache. `TTypes` is its root's types: what its parts declare. It
|
|
138
|
+
* runs the factories of builders bound to those parts, or to fewer of them,
|
|
139
|
+
* so the bare `QueryClient` type takes any client.
|
|
118
140
|
*
|
|
119
141
|
* @see https://raf-query.katt.dev/docs/api-reference#the-client
|
|
120
142
|
*/
|
|
121
|
-
export interface QueryClient<TTypes extends RootTypes =
|
|
143
|
+
export interface QueryClient<TTypes extends RootTypes = NoParts> {
|
|
122
144
|
/** Entries changed since the last notification. */
|
|
123
145
|
readonly "~changed": Set<Entry>;
|
|
124
146
|
readonly "~entries": Map<string, Entry>;
|
|
@@ -127,12 +149,14 @@ export interface QueryClient<TTypes extends RootTypes = AnyRoot> {
|
|
|
127
149
|
"~seq": number;
|
|
128
150
|
/** Invalidations not yet older than every entry's data. */
|
|
129
151
|
"~stamps": Array<Stamp>;
|
|
130
|
-
/** Phantom: the root's types
|
|
152
|
+
/** Phantom: the root's types. `undefined` at runtime. */
|
|
131
153
|
readonly "~types"?: {
|
|
132
|
-
readonly root:
|
|
154
|
+
readonly root: TTypes;
|
|
133
155
|
};
|
|
134
156
|
readonly clock: Clock;
|
|
135
157
|
readonly config: QueryClientConfig;
|
|
158
|
+
/** What the parts give every `queryFn`, beside the client, key and signal. */
|
|
159
|
+
readonly context: TTypes["context"];
|
|
136
160
|
readonly isServer: boolean;
|
|
137
161
|
/**
|
|
138
162
|
* Cache events: entries added, updated, removed, or gaining and losing
|
|
@@ -145,13 +169,18 @@ export interface QueryClient<TTypes extends RootTypes = AnyRoot> {
|
|
|
145
169
|
}): () => void;
|
|
146
170
|
}
|
|
147
171
|
/**
|
|
148
|
-
* Create a query client. Behaviors are opt-in: pass `
|
|
149
|
-
*
|
|
172
|
+
* Create a query client. Behaviors are opt-in: pass `parts`, such as
|
|
173
|
+
* `recommended()`. The client's root is what they declare.
|
|
150
174
|
*
|
|
151
175
|
* ```ts
|
|
152
|
-
*
|
|
176
|
+
* interface Ownership {
|
|
177
|
+
* readonly options: { readonly owner?: "posts-team" | "users-team" };
|
|
178
|
+
* }
|
|
153
179
|
* export const makeQueryClient = () =>
|
|
154
|
-
* createQueryClient({
|
|
180
|
+
* createQueryClient({
|
|
181
|
+
* parts: [retry(), part<Ownership>()],
|
|
182
|
+
* defaults: { staleTime: 30_000 },
|
|
183
|
+
* });
|
|
155
184
|
* ```
|
|
156
185
|
*
|
|
157
186
|
* Make one per request on the server (in a loader or a request-scoped
|
|
@@ -159,14 +188,12 @@ export interface QueryClient<TTypes extends RootTypes = AnyRoot> {
|
|
|
159
188
|
*
|
|
160
189
|
* @see https://raf-query.katt.dev/docs/important-defaults
|
|
161
190
|
*/
|
|
162
|
-
export declare function createQueryClient<const
|
|
163
|
-
readonly
|
|
164
|
-
readonly
|
|
165
|
-
}): QueryClient<
|
|
166
|
-
readonly meta: MetaOf<[...TLinks, ...TFeatures]>;
|
|
167
|
-
}>;
|
|
191
|
+
export declare function createQueryClient<const TParts extends ReadonlyArray<AnyPart> = []>(config?: Omit<QueryClientConfig, "defaults" | "parts"> & {
|
|
192
|
+
readonly defaults?: NoInfer<Omit<CoreOptions, "ssr"> & Partial<RootOfParts<TParts>["options"]>>;
|
|
193
|
+
readonly parts?: TParts;
|
|
194
|
+
}): QueryClient<RootOfParts<TParts>>;
|
|
168
195
|
/**
|
|
169
|
-
* Start the client's
|
|
196
|
+
* Start the features of the client's parts. Providers call it while mounted; the
|
|
170
197
|
* returned function stops them. Mounts are counted, so features start once
|
|
171
198
|
* however many providers share the client, and stop with the last.
|
|
172
199
|
*/
|
package/dist/client.mjs
CHANGED
|
@@ -7,44 +7,22 @@ const systemClock = {
|
|
|
7
7
|
now: () => Date.now(),
|
|
8
8
|
setTimeout: (callback, ms) => setTimeout(callback, ms)
|
|
9
9
|
};
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* those fields only when the root has the link.
|
|
13
|
-
*
|
|
14
|
-
* ```ts
|
|
15
|
-
* const traced = link<{ meta: { traceId?: string } }>((op, next) =>
|
|
16
|
-
* withTrace(op.meta.traceId, () => next(op)),
|
|
17
|
-
* );
|
|
18
|
-
* ```
|
|
19
|
-
*
|
|
20
|
-
* @see https://raf-query.katt.dev/docs/links-and-features#links-and-features
|
|
21
|
-
*/
|
|
22
|
-
function link(run) {
|
|
23
|
-
return run;
|
|
24
|
-
}
|
|
25
|
-
const noop = () => {};
|
|
26
|
-
/**
|
|
27
|
-
* A feature that declares the `meta` fields it reads. Without `setup`, it
|
|
28
|
-
* only declares them: a root's own fields, such as an `owner` its targets
|
|
29
|
-
* match on.
|
|
30
|
-
*
|
|
31
|
-
* ```ts
|
|
32
|
-
* const ownership = feature<{ meta: { owner?: "posts-team" | "users-team" } }>();
|
|
33
|
-
* ```
|
|
34
|
-
*
|
|
35
|
-
* @see https://raf-query.katt.dev/docs/links-and-features#links-and-features
|
|
36
|
-
*/
|
|
37
|
-
function feature(setup = noop) {
|
|
38
|
-
return setup;
|
|
10
|
+
function part(definition = {}) {
|
|
11
|
+
return definition;
|
|
39
12
|
}
|
|
40
13
|
/**
|
|
41
|
-
* Create a query client. Behaviors are opt-in: pass `
|
|
42
|
-
*
|
|
14
|
+
* Create a query client. Behaviors are opt-in: pass `parts`, such as
|
|
15
|
+
* `recommended()`. The client's root is what they declare.
|
|
43
16
|
*
|
|
44
17
|
* ```ts
|
|
45
|
-
*
|
|
18
|
+
* interface Ownership {
|
|
19
|
+
* readonly options: { readonly owner?: "posts-team" | "users-team" };
|
|
20
|
+
* }
|
|
46
21
|
* export const makeQueryClient = () =>
|
|
47
|
-
* createQueryClient({
|
|
22
|
+
* createQueryClient({
|
|
23
|
+
* parts: [retry(), part<Ownership>()],
|
|
24
|
+
* defaults: { staleTime: 30_000 },
|
|
25
|
+
* });
|
|
48
26
|
* ```
|
|
49
27
|
*
|
|
50
28
|
* Make one per request on the server (in a loader or a request-scoped
|
|
@@ -63,6 +41,7 @@ function createQueryClient(config = {}) {
|
|
|
63
41
|
"~stamps": [],
|
|
64
42
|
clock: config.clock ?? systemClock,
|
|
65
43
|
config,
|
|
44
|
+
context: Object.assign({}, ...(config.parts ?? []).map((one) => one.context)),
|
|
66
45
|
isServer: config.isServer ?? !("window" in globalThis),
|
|
67
46
|
subscribe: (listener, options) => {
|
|
68
47
|
const unsubscribe = events.subscribe(listener, options);
|
|
@@ -74,9 +53,13 @@ function createQueryClient(config = {}) {
|
|
|
74
53
|
}
|
|
75
54
|
};
|
|
76
55
|
}
|
|
56
|
+
/** The client's parts, as the kernel runs them. */
|
|
57
|
+
function partsOf(client) {
|
|
58
|
+
return client.config.parts ?? [];
|
|
59
|
+
}
|
|
77
60
|
const mounted = /* @__PURE__ */ new WeakMap();
|
|
78
61
|
/**
|
|
79
|
-
* Start the client's
|
|
62
|
+
* Start the features of the client's parts. Providers call it while mounted; the
|
|
80
63
|
* returned function stops them. Mounts are counted, so features start once
|
|
81
64
|
* however many providers share the client, and stop with the last.
|
|
82
65
|
*/
|
|
@@ -88,7 +71,7 @@ function mount(client) {
|
|
|
88
71
|
count: 0
|
|
89
72
|
};
|
|
90
73
|
mounted.set(client, current);
|
|
91
|
-
for (const
|
|
74
|
+
for (const one of partsOf(client)) one.setup?.(client, current.controller.signal);
|
|
92
75
|
}
|
|
93
76
|
const shared = current;
|
|
94
77
|
shared.count += 1;
|
|
@@ -101,4 +84,4 @@ function mount(client) {
|
|
|
101
84
|
};
|
|
102
85
|
}
|
|
103
86
|
//#endregion
|
|
104
|
-
export { createQueryClient,
|
|
87
|
+
export { createQueryClient, mount, part, partsOf, systemClock };
|
package/dist/entry.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { listeners } from "./subscribe.mjs";
|
|
2
|
+
import { partsOf } from "./client.mjs";
|
|
2
3
|
import { isObject } from "./guards.mjs";
|
|
3
4
|
import { hashKey } from "./keys/keys.mjs";
|
|
4
5
|
import { matches } from "./keys/targets.mjs";
|
|
@@ -52,8 +53,13 @@ function newPending(entry) {
|
|
|
52
53
|
entry.pendingThenable = next;
|
|
53
54
|
entry.thenable = next.promise;
|
|
54
55
|
}
|
|
56
|
+
/** One of the core's options for the entry: the query's, or else the client's default. */
|
|
57
|
+
function optionOf(entry, name) {
|
|
58
|
+
const defaults = entry.client.config.defaults;
|
|
59
|
+
return entry.options?.[name] ?? defaults?.[name];
|
|
60
|
+
}
|
|
55
61
|
function gcTime(entry) {
|
|
56
|
-
return entry
|
|
62
|
+
return optionOf(entry, "gcTime") ?? fiveMinutes;
|
|
57
63
|
}
|
|
58
64
|
function scheduleGc(entry) {
|
|
59
65
|
const client = entry.client;
|
|
@@ -136,13 +142,13 @@ function isBrowserOnly(entry) {
|
|
|
136
142
|
function isStale(entry) {
|
|
137
143
|
const state = entry.state;
|
|
138
144
|
if (!state.hasData) return true;
|
|
139
|
-
const staleTime = entry
|
|
145
|
+
const staleTime = optionOf(entry, "staleTime") ?? 0;
|
|
140
146
|
return entry.client.clock.now() - state.updatedAt >= staleTime || isInvalidated(entry);
|
|
141
147
|
}
|
|
142
148
|
/** Write data. `startedAt` orders it against invalidations. */
|
|
143
149
|
function write(entry, data, updatedAt, startedAt) {
|
|
144
150
|
const previous = entry.state;
|
|
145
|
-
const next = (entry
|
|
151
|
+
const next = (optionOf(entry, "structuralSharing") ?? true) && previous.hasData ? replaceEqualDeep(previous.data, data) : data;
|
|
146
152
|
entry.requestedAt = Math.max(entry.requestedAt, startedAt);
|
|
147
153
|
if (entry.pendingThenable) {
|
|
148
154
|
entry.pendingThenable.resolve(next);
|
|
@@ -215,18 +221,19 @@ function terminal(op) {
|
|
|
215
221
|
const options = op.options;
|
|
216
222
|
const paged = options["~run"];
|
|
217
223
|
resolve(paged ? paged(op) : options.queryFn({
|
|
224
|
+
...op.client.context,
|
|
218
225
|
client: op.client,
|
|
219
|
-
meta: op.meta,
|
|
220
226
|
queryKey: op.queryKey,
|
|
221
227
|
signal: op.signal
|
|
222
228
|
}));
|
|
223
229
|
});
|
|
224
230
|
}
|
|
225
231
|
function chain(op) {
|
|
226
|
-
const
|
|
232
|
+
const parts = partsOf(op.client);
|
|
227
233
|
const call = (index, current) => {
|
|
228
|
-
const
|
|
229
|
-
|
|
234
|
+
const one = parts[index];
|
|
235
|
+
if (!one) return terminal(current);
|
|
236
|
+
return one.link ? one.link(current, (next) => call(index + 1, next)) : call(index + 1, current);
|
|
230
237
|
};
|
|
231
238
|
return call(0, op);
|
|
232
239
|
}
|
|
@@ -262,8 +269,10 @@ function fetchEntry(entry, request = {}) {
|
|
|
262
269
|
hasData: state.hasData,
|
|
263
270
|
hash: entry.hash,
|
|
264
271
|
input: entry.input,
|
|
265
|
-
|
|
266
|
-
|
|
272
|
+
options: {
|
|
273
|
+
...client.config.defaults,
|
|
274
|
+
...options
|
|
275
|
+
},
|
|
267
276
|
pages: request.pages,
|
|
268
277
|
queryKey: entry.key,
|
|
269
278
|
signal: run.controller.signal
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Part } from "../../client.mjs";
|
|
2
2
|
//#region src/features/focus-refetch/focus-refetch.d.ts
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* What `focusRefetch()` declares.
|
|
5
|
+
*
|
|
6
|
+
* @see https://raf-query.katt.dev/docs/window-focus-refetching
|
|
7
|
+
*/
|
|
8
|
+
export interface FocusRefetch {
|
|
9
|
+
readonly options: {
|
|
10
|
+
/** `false`: don't refetch this query on focus. */
|
|
11
|
+
readonly refetchOnWindowFocus?: boolean;
|
|
12
|
+
};
|
|
7
13
|
}
|
|
8
14
|
/**
|
|
9
15
|
* Refetch read, stale queries when the window regains focus, except those
|
|
10
|
-
* with `
|
|
16
|
+
* with `refetchOnWindowFocus: false`.
|
|
11
17
|
*
|
|
12
18
|
* @see https://raf-query.katt.dev/docs/window-focus-refetching
|
|
13
19
|
*/
|
|
14
|
-
export declare function focusRefetch():
|
|
15
|
-
readonly meta: FocusRefetchMeta;
|
|
16
|
-
}>;
|
|
20
|
+
export declare function focusRefetch(): Part<FocusRefetch>;
|
|
17
21
|
//#endregion
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { part } from "../../client.mjs";
|
|
2
2
|
import { refetchStaleReads } from "../shared.mjs";
|
|
3
3
|
//#region src/features/focus-refetch/focus-refetch.ts
|
|
4
4
|
/**
|
|
5
5
|
* Refetch read, stale queries when the window regains focus, except those
|
|
6
|
-
* with `
|
|
6
|
+
* with `refetchOnWindowFocus: false`.
|
|
7
7
|
*
|
|
8
8
|
* @see https://raf-query.katt.dev/docs/window-focus-refetching
|
|
9
9
|
*/
|
|
10
10
|
function focusRefetch() {
|
|
11
|
-
return
|
|
11
|
+
return part({ setup: (client, signal) => {
|
|
12
12
|
const onFocus = () => {
|
|
13
13
|
if (document.visibilityState === "visible") refetchStaleReads(client, "refetchOnWindowFocus");
|
|
14
14
|
};
|
|
15
15
|
document.addEventListener("visibilitychange", onFocus, { signal });
|
|
16
16
|
window.addEventListener("focus", onFocus, { signal });
|
|
17
|
-
});
|
|
17
|
+
} });
|
|
18
18
|
}
|
|
19
19
|
//#endregion
|
|
20
20
|
export { focusRefetch };
|
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Part } from "../../client.mjs";
|
|
2
2
|
//#region src/features/polling/polling.d.ts
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* What `polling()` declares.
|
|
5
|
+
*
|
|
6
|
+
* @see https://raf-query.katt.dev/docs/polling
|
|
7
|
+
*/
|
|
8
|
+
export interface Polling {
|
|
9
|
+
readonly options: {
|
|
10
|
+
/** Refetch every `n` ms while the query is read. */
|
|
11
|
+
readonly refetchInterval?: number | false;
|
|
12
|
+
};
|
|
7
13
|
}
|
|
8
14
|
/**
|
|
9
|
-
* Refetch read queries every `
|
|
15
|
+
* Refetch read queries every `refetchInterval` ms.
|
|
10
16
|
*
|
|
11
17
|
* @see https://raf-query.katt.dev/docs/polling
|
|
12
18
|
*/
|
|
13
|
-
export declare function polling():
|
|
14
|
-
readonly meta: PollingMeta;
|
|
15
|
-
}>;
|
|
19
|
+
export declare function polling(): Part<Polling>;
|
|
16
20
|
//#endregion
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { part } from "../../client.mjs";
|
|
2
2
|
import { isNumber } from "../../guards.mjs";
|
|
3
3
|
import { fetchEntry } from "../../entry.mjs";
|
|
4
|
-
import {
|
|
4
|
+
import { optionOf } from "../shared.mjs";
|
|
5
5
|
//#region src/features/polling/polling.ts
|
|
6
6
|
/**
|
|
7
|
-
* Refetch read queries every `
|
|
7
|
+
* Refetch read queries every `refetchInterval` ms.
|
|
8
8
|
*
|
|
9
9
|
* @see https://raf-query.katt.dev/docs/polling
|
|
10
10
|
*/
|
|
11
11
|
function polling() {
|
|
12
|
-
return
|
|
12
|
+
return part({ setup: (client, signal) => {
|
|
13
13
|
const timers = /* @__PURE__ */ new Map();
|
|
14
14
|
const stop = (entry) => {
|
|
15
15
|
client.clock.clearTimeout(timers.get(entry));
|
|
16
16
|
timers.delete(entry);
|
|
17
17
|
};
|
|
18
18
|
const sync = (entry) => {
|
|
19
|
-
const interval =
|
|
19
|
+
const interval = optionOf(entry, "refetchInterval");
|
|
20
20
|
if (!(entry.readers.size > 0 && isNumber(interval) && interval > 0 && client["~entries"].get(entry.hash) === entry)) stop(entry);
|
|
21
21
|
else if (!timers.has(entry)) {
|
|
22
22
|
const tick = () => {
|
|
@@ -33,7 +33,7 @@ function polling() {
|
|
|
33
33
|
signal.addEventListener("abort", () => {
|
|
34
34
|
for (const entry of timers.keys()) stop(entry);
|
|
35
35
|
});
|
|
36
|
-
});
|
|
36
|
+
} });
|
|
37
37
|
}
|
|
38
38
|
//#endregion
|
|
39
39
|
export { polling };
|
|
@@ -1,17 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Part } from "../../client.mjs";
|
|
2
2
|
//#region src/features/reconnect-refetch/reconnect-refetch.d.ts
|
|
3
|
-
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
/**
|
|
4
|
+
* What `reconnectRefetch()` declares.
|
|
5
|
+
*
|
|
6
|
+
* @see https://raf-query.katt.dev/docs/testing#focus-and-the-network
|
|
7
|
+
*/
|
|
8
|
+
export interface ReconnectRefetch {
|
|
9
|
+
readonly options: {
|
|
10
|
+
/** `false`: don't refetch this query when the browser comes back online. */
|
|
11
|
+
readonly refetchOnReconnect?: boolean;
|
|
12
|
+
};
|
|
7
13
|
}
|
|
8
14
|
/**
|
|
9
15
|
* Refetch read, stale queries when the browser comes back online, except
|
|
10
|
-
* those with `
|
|
16
|
+
* those with `refetchOnReconnect: false`.
|
|
11
17
|
*
|
|
12
18
|
* @see https://raf-query.katt.dev/docs/testing#focus-and-the-network
|
|
13
19
|
*/
|
|
14
|
-
export declare function reconnectRefetch():
|
|
15
|
-
readonly meta: ReconnectRefetchMeta;
|
|
16
|
-
}>;
|
|
20
|
+
export declare function reconnectRefetch(): Part<ReconnectRefetch>;
|
|
17
21
|
//#endregion
|