@raf-query/core 0.13.2 → 0.15.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 +21 -1
- package/README.md +8 -5
- package/dist/client.d.mts +141 -69
- package/dist/client.mjs +19 -36
- package/dist/entry.mjs +19 -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,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.15.0](https://github.com/KATT/raf-query/compare/0.14.0...0.15.0) (2026-11-11)
|
|
4
|
+
|
|
5
|
+
### ⚠ BREAKING CHANGES
|
|
6
|
+
|
|
7
|
+
* **core:** a part's link can give or change the queryFn context per request ([#251](https://github.com/KATT/raf-query/issues/251))
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* **core:** a part's link can give or change the queryFn context per request ([#251](https://github.com/KATT/raf-query/issues/251)) ([3358dde](https://github.com/KATT/raf-query/commit/3358ddeb1a0a19b0459af55d70b6e23a05a902d5))
|
|
12
|
+
|
|
13
|
+
## [0.14.0](https://github.com/KATT/raf-query/compare/0.13.2...0.14.0) (2026-11-11)
|
|
14
|
+
|
|
15
|
+
### ⚠ BREAKING CHANGES
|
|
16
|
+
|
|
17
|
+
- **core:** parts declare the options and queryFn context a client's queries get ([#250](https://github.com/KATT/raf-query/issues/250))
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
- **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))
|
|
22
|
+
|
|
3
23
|
## [0.13.2](https://github.com/KATT/raf-query/compare/0.13.1...0.13.2) (2026-11-10)
|
|
4
24
|
|
|
5
25
|
### Bug Fixes
|
|
6
26
|
|
|
7
|
-
|
|
27
|
+
- **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
28
|
|
|
9
29
|
## [0.13.1](https://github.com/KATT/raf-query/compare/0.13.0...0.13.1) (2026-11-10)
|
|
10
30
|
|
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, ContextOfDecl, 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,12 +17,19 @@ 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;
|
|
27
|
+
/**
|
|
28
|
+
* What the `queryFn` gets beside the client, key and signal: the parts'
|
|
29
|
+
* context, as the links before this one left it. Pass `next` an
|
|
30
|
+
* operation with another `context` to change it for this request.
|
|
31
|
+
*/
|
|
32
|
+
readonly context: Partial<ContextOfDecl<TDecl>>;
|
|
32
33
|
/** The entry's data when the request started (paged queries extend it). */
|
|
33
34
|
readonly data: unknown;
|
|
34
35
|
/** For paged queries: which page to add. */
|
|
@@ -36,54 +37,119 @@ export interface Operation<TMeta extends object = object> {
|
|
|
36
37
|
readonly hasData: boolean;
|
|
37
38
|
readonly hash: string;
|
|
38
39
|
readonly input: unknown;
|
|
39
|
-
/** The query's
|
|
40
|
-
readonly
|
|
41
|
-
readonly options: QueryOptions;
|
|
40
|
+
/** The query's options, over the client's `defaults`. */
|
|
41
|
+
readonly options: Partial<OptionsOfDecl<TDecl>> & QueryOptions;
|
|
42
42
|
/** For paged queries: how many pages a first load fetches. */
|
|
43
43
|
readonly pages: number | undefined;
|
|
44
44
|
readonly queryKey: QueryKey;
|
|
45
45
|
readonly signal: AbortSignal;
|
|
46
46
|
}
|
|
47
|
+
type LinkFunction = (op: Operation, next: (op: Operation) => Promise<unknown>) => Promise<unknown>;
|
|
48
|
+
type SetupFunction = (client: QueryClient, signal: AbortSignal) => void;
|
|
47
49
|
/**
|
|
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}.
|
|
50
|
+
* One piece of a client's behavior: any of a link that wraps every request,
|
|
51
|
+
* a feature's `setup`, and values for every `queryFn`'s context. `TDecl` is
|
|
52
|
+
* what it declares: see {@link part}.
|
|
58
53
|
*
|
|
59
|
-
* @see https://raf-query.katt.dev/docs/links-and-features#
|
|
54
|
+
* @see https://raf-query.katt.dev/docs/links-and-features#parts
|
|
60
55
|
*/
|
|
61
|
-
export
|
|
56
|
+
export interface Part<TDecl extends Decl = NoDecl> extends Declares<TDecl> {
|
|
57
|
+
readonly context?: object;
|
|
58
|
+
readonly link?: LinkFunction;
|
|
59
|
+
readonly setup?: SetupFunction;
|
|
60
|
+
}
|
|
61
|
+
/** Any part, whatever it declares. */
|
|
62
|
+
export interface AnyPart extends AnyDeclares {
|
|
63
|
+
readonly context?: object;
|
|
64
|
+
readonly link?: LinkFunction;
|
|
65
|
+
readonly setup?: SetupFunction;
|
|
66
|
+
}
|
|
67
|
+
type LinkOf<TDecl extends Decl, TNext> = (op: Operation<TDecl>, next: (op: TNext) => Promise<unknown>) => Promise<unknown>;
|
|
68
|
+
interface PartSetup<TDecl extends Decl> {
|
|
69
|
+
/**
|
|
70
|
+
* A cache-wide behavior, started when a provider mounts the client and
|
|
71
|
+
* stopped by `signal`. The client is typed with this part, so it runs
|
|
72
|
+
* the factories of a builder bound to it.
|
|
73
|
+
*/
|
|
74
|
+
readonly setup?: (client: QueryClient<Root<TDecl>>, signal: AbortSignal) => void;
|
|
75
|
+
}
|
|
76
|
+
/** The context a declaration says its part gives. */
|
|
77
|
+
type DeclaredContext<TDecl extends ContextDecl> = TDecl["context"];
|
|
78
|
+
/** A declaration with a `context`: its part gives it. */
|
|
79
|
+
interface ContextDecl extends Decl {
|
|
80
|
+
readonly context: object;
|
|
81
|
+
}
|
|
82
|
+
/** What {@link part} takes for a part that declares no `context`. */
|
|
83
|
+
interface PlainDefinition<TDecl extends Decl> extends PartSetup<TDecl> {
|
|
84
|
+
readonly context?: never;
|
|
85
|
+
/**
|
|
86
|
+
* Wraps every request, like a tRPC link. Call `next(op)` to run the
|
|
87
|
+
* rest of the chain; the last link is the query's own `queryFn`.
|
|
88
|
+
*/
|
|
89
|
+
readonly link?: LinkOf<TDecl, Operation<TDecl>>;
|
|
90
|
+
}
|
|
91
|
+
/** What {@link part} takes for a part that gives its `context` for the client. */
|
|
92
|
+
interface ClientContextDefinition<TDecl extends ContextDecl> extends PartSetup<TDecl> {
|
|
93
|
+
/** Merged into the context of every `queryFn` the client runs. */
|
|
94
|
+
readonly context: DeclaredContext<TDecl>;
|
|
95
|
+
/**
|
|
96
|
+
* Wraps every request, like a tRPC link. Call `next(op)` to run the
|
|
97
|
+
* rest of the chain; the last link is the query's own `queryFn`. An
|
|
98
|
+
* operation with another `context` changes it for that request.
|
|
99
|
+
*/
|
|
100
|
+
readonly link?: LinkOf<TDecl, Operation<TDecl>>;
|
|
101
|
+
}
|
|
102
|
+
/** What {@link part} takes for a part that gives its `context` per request. */
|
|
103
|
+
interface RequestContextDefinition<TDecl extends ContextDecl> extends PartSetup<TDecl> {
|
|
104
|
+
readonly context?: never;
|
|
105
|
+
/**
|
|
106
|
+
* Wraps every request, and gives each the context the part declares:
|
|
107
|
+
* `next` takes an operation whose `context` has it.
|
|
108
|
+
*/
|
|
109
|
+
readonly link: LinkOf<TDecl, Operation<TDecl> & {
|
|
110
|
+
readonly context: DeclaredContext<TDecl>;
|
|
111
|
+
}>;
|
|
112
|
+
}
|
|
113
|
+
/** A declaration without a `context`: its part needs no definition. */
|
|
114
|
+
interface OptionsDecl {
|
|
115
|
+
readonly context?: never;
|
|
116
|
+
readonly options?: object;
|
|
117
|
+
}
|
|
62
118
|
/**
|
|
63
|
-
* A
|
|
64
|
-
*
|
|
119
|
+
* A part of a client, with what it declares: the per-query `options` it
|
|
120
|
+
* reads and the `context` it gives every `queryFn`. A client's queries take
|
|
121
|
+
* those options, and get that context, only when the client has the part.
|
|
65
122
|
*
|
|
66
123
|
* ```ts
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
124
|
+
* interface Traced {
|
|
125
|
+
* readonly options: { readonly traceId?: string };
|
|
126
|
+
* }
|
|
127
|
+
* const traced = part<Traced>({
|
|
128
|
+
* link: (op, next) => withTrace(op.options.traceId, () => next(op)),
|
|
129
|
+
* });
|
|
70
130
|
* ```
|
|
71
131
|
*
|
|
72
|
-
*
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
*
|
|
77
|
-
* only declares them: a root's own fields, such as an `owner` its targets
|
|
78
|
-
* match on.
|
|
132
|
+
* A part without a link or `setup` only declares: an app's own options, such
|
|
133
|
+
* as an `owner` its targets match on.
|
|
134
|
+
*
|
|
135
|
+
* A part gives the `context` it declares for the client, as `context`, or
|
|
136
|
+
* per request, from its link:
|
|
79
137
|
*
|
|
80
138
|
* ```ts
|
|
81
|
-
*
|
|
139
|
+
* interface Session {
|
|
140
|
+
* readonly context: { readonly token: string };
|
|
141
|
+
* }
|
|
142
|
+
* const session = part<Session>({
|
|
143
|
+
* link: async (op, next) =>
|
|
144
|
+
* next({ ...op, context: { ...op.context, token: await freshToken() } }),
|
|
145
|
+
* });
|
|
82
146
|
* ```
|
|
83
147
|
*
|
|
84
|
-
* @see https://raf-query.katt.dev/docs/links-and-features#
|
|
148
|
+
* @see https://raf-query.katt.dev/docs/links-and-features#parts
|
|
85
149
|
*/
|
|
86
|
-
export declare function
|
|
150
|
+
export declare function part<TDecl extends OptionsDecl = NoDecl>(definition?: PlainDefinition<NoInfer<TDecl>>): Part<NoInfer<TDecl>>;
|
|
151
|
+
export declare function part<TDecl extends ContextDecl>(definition: ClientContextDefinition<NoInfer<TDecl>>): Part<NoInfer<TDecl>>;
|
|
152
|
+
export declare function part<TDecl extends ContextDecl>(definition: RequestContextDefinition<NoInfer<TDecl>>): Part<NoInfer<TDecl>>;
|
|
87
153
|
export type ClientEvent = {
|
|
88
154
|
readonly entry: Entry;
|
|
89
155
|
readonly type: "added" | "removed" | "updated";
|
|
@@ -92,44 +158,47 @@ export type ClientEvent = {
|
|
|
92
158
|
readonly type: "readers";
|
|
93
159
|
};
|
|
94
160
|
/**
|
|
95
|
-
* What `createQueryClient` takes:
|
|
161
|
+
* What `createQueryClient` takes: its parts and the cache-wide defaults.
|
|
96
162
|
*
|
|
97
163
|
* @see https://raf-query.katt.dev/docs/important-defaults#changing-the-defaults
|
|
98
164
|
*/
|
|
99
165
|
export interface QueryClientConfig {
|
|
100
166
|
readonly clock?: Clock;
|
|
101
|
-
/**
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
167
|
+
/**
|
|
168
|
+
* Client-wide defaults for per-query options: the core's (`staleTime`,
|
|
169
|
+
* `gcTime`, `structuralSharing`) and those the parts declare. `ssr` is a
|
|
170
|
+
* query's own.
|
|
171
|
+
*/
|
|
172
|
+
readonly defaults?: object;
|
|
105
173
|
/** Default: no `window`. Server clients never gc and never revalidate cached data. */
|
|
106
174
|
readonly isServer?: boolean;
|
|
107
|
-
/**
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
readonly structuralSharing?: boolean;
|
|
175
|
+
/**
|
|
176
|
+
* The client's behaviors. Links wrap every request, outermost first;
|
|
177
|
+
* features start while a provider has the client mounted.
|
|
178
|
+
*/
|
|
179
|
+
readonly parts?: ReadonlyArray<AnyPart>;
|
|
113
180
|
}
|
|
114
181
|
/**
|
|
115
|
-
* A query cache. `TTypes` is its root's types
|
|
116
|
-
*
|
|
117
|
-
* `QueryClient` type takes
|
|
182
|
+
* A query cache. `TTypes` is its root's types: what its parts declare. It
|
|
183
|
+
* runs the factories of builders bound to those parts, or to fewer of them,
|
|
184
|
+
* so the bare `QueryClient` type takes any client.
|
|
118
185
|
*
|
|
119
186
|
* @see https://raf-query.katt.dev/docs/api-reference#the-client
|
|
120
187
|
*/
|
|
121
|
-
export interface QueryClient<TTypes extends RootTypes =
|
|
188
|
+
export interface QueryClient<TTypes extends RootTypes = NoParts> {
|
|
122
189
|
/** Entries changed since the last notification. */
|
|
123
190
|
readonly "~changed": Set<Entry>;
|
|
191
|
+
/** What the parts give every `queryFn` for the client; links can change it per request. */
|
|
192
|
+
readonly "~context": object;
|
|
124
193
|
readonly "~entries": Map<string, Entry>;
|
|
125
194
|
readonly "~events": Listeners<ClientEvent>;
|
|
126
195
|
/** Orders requests and invalidations. */
|
|
127
196
|
"~seq": number;
|
|
128
197
|
/** Invalidations not yet older than every entry's data. */
|
|
129
198
|
"~stamps": Array<Stamp>;
|
|
130
|
-
/** Phantom: the root's types
|
|
199
|
+
/** Phantom: the root's types. `undefined` at runtime. */
|
|
131
200
|
readonly "~types"?: {
|
|
132
|
-
readonly root:
|
|
201
|
+
readonly root: TTypes;
|
|
133
202
|
};
|
|
134
203
|
readonly clock: Clock;
|
|
135
204
|
readonly config: QueryClientConfig;
|
|
@@ -145,13 +214,18 @@ export interface QueryClient<TTypes extends RootTypes = AnyRoot> {
|
|
|
145
214
|
}): () => void;
|
|
146
215
|
}
|
|
147
216
|
/**
|
|
148
|
-
* Create a query client. Behaviors are opt-in: pass `
|
|
149
|
-
*
|
|
217
|
+
* Create a query client. Behaviors are opt-in: pass `parts`, such as
|
|
218
|
+
* `recommended()`. The client's root is what they declare.
|
|
150
219
|
*
|
|
151
220
|
* ```ts
|
|
152
|
-
*
|
|
221
|
+
* interface Ownership {
|
|
222
|
+
* readonly options: { readonly owner?: "posts-team" | "users-team" };
|
|
223
|
+
* }
|
|
153
224
|
* export const makeQueryClient = () =>
|
|
154
|
-
* createQueryClient({
|
|
225
|
+
* createQueryClient({
|
|
226
|
+
* parts: [retry(), part<Ownership>()],
|
|
227
|
+
* defaults: { staleTime: 30_000 },
|
|
228
|
+
* });
|
|
155
229
|
* ```
|
|
156
230
|
*
|
|
157
231
|
* Make one per request on the server (in a loader or a request-scoped
|
|
@@ -159,14 +233,12 @@ export interface QueryClient<TTypes extends RootTypes = AnyRoot> {
|
|
|
159
233
|
*
|
|
160
234
|
* @see https://raf-query.katt.dev/docs/important-defaults
|
|
161
235
|
*/
|
|
162
|
-
export declare function createQueryClient<const
|
|
163
|
-
readonly
|
|
164
|
-
readonly
|
|
165
|
-
}): QueryClient<
|
|
166
|
-
readonly meta: MetaOf<[...TLinks, ...TFeatures]>;
|
|
167
|
-
}>;
|
|
236
|
+
export declare function createQueryClient<const TParts extends ReadonlyArray<AnyPart> = []>(config?: Omit<QueryClientConfig, "defaults" | "parts"> & {
|
|
237
|
+
readonly defaults?: NoInfer<Omit<CoreOptions, "ssr"> & Partial<RootOfParts<TParts>["options"]>>;
|
|
238
|
+
readonly parts?: TParts;
|
|
239
|
+
}): QueryClient<RootOfParts<TParts>>;
|
|
168
240
|
/**
|
|
169
|
-
* Start the client's
|
|
241
|
+
* Start the features of the client's parts. Providers call it while mounted; the
|
|
170
242
|
* returned function stops them. Mounts are counted, so features start once
|
|
171
243
|
* however many providers share the client, and stop with the last.
|
|
172
244
|
*/
|
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
|
|
@@ -57,6 +35,7 @@ function createQueryClient(config = {}) {
|
|
|
57
35
|
const entries = /* @__PURE__ */ new Map();
|
|
58
36
|
return {
|
|
59
37
|
"~changed": /* @__PURE__ */ new Set(),
|
|
38
|
+
"~context": Object.assign({}, ...(config.parts ?? []).map((one) => one.context)),
|
|
60
39
|
"~entries": entries,
|
|
61
40
|
"~events": events,
|
|
62
41
|
"~seq": 0,
|
|
@@ -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.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
|
}
|
|
@@ -257,13 +264,16 @@ function fetchEntry(entry, request = {}) {
|
|
|
257
264
|
const state = entry.state;
|
|
258
265
|
chain({
|
|
259
266
|
client,
|
|
267
|
+
context: client["~context"],
|
|
260
268
|
data: state.data,
|
|
261
269
|
direction: request.direction,
|
|
262
270
|
hasData: state.hasData,
|
|
263
271
|
hash: entry.hash,
|
|
264
272
|
input: entry.input,
|
|
265
|
-
|
|
266
|
-
|
|
273
|
+
options: {
|
|
274
|
+
...client.config.defaults,
|
|
275
|
+
...options
|
|
276
|
+
},
|
|
267
277
|
pages: request.pages,
|
|
268
278
|
queryKey: entry.key,
|
|
269
279
|
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
|