@raf-query/core 0.13.1 → 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 CHANGED
@@ -1,10 +1,26 @@
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
+
13
+ ## [0.13.2](https://github.com/KATT/raf-query/compare/0.13.1...0.13.2) (2026-11-10)
14
+
15
+ ### Bug Fixes
16
+
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))
18
+
3
19
  ## [0.13.1](https://github.com/KATT/raf-query/compare/0.13.0...0.13.1) (2026-11-10)
4
20
 
5
21
  ### Features
6
22
 
7
- * **core:** keepPages, for lists whose start stays out of the key ([#248](https://github.com/KATT/raf-query/issues/248)) ([164c88c](https://github.com/KATT/raf-query/commit/164c88c4509a34c9ec51f089c3b70c03ea13cac0))
23
+ - **core:** keepPages, for lists whose start stays out of the key ([#248](https://github.com/KATT/raf-query/issues/248)) ([164c88c](https://github.com/KATT/raf-query/commit/164c88c4509a34c9ec51f089c3b70c03ea13cac0))
8
24
 
9
25
  ## [0.13.0](https://github.com/KATT/raf-query/compare/0.12.0...0.13.0) (2026-11-10)
10
26
 
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 links, features and exports, so a client bundles only what it turns on.
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({ links: [retry()], staleTime: 5000 });
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 links and features, `group`, `dehydrate` and `hydrate` |
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 { QueryOptions } from "./options.mjs";
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 links see it. `TMeta` is what the link declares.
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#links-and-features
23
+ * @see https://raf-query.katt.dev/docs/links-and-features#parts
29
24
  */
30
- export interface Operation<TMeta extends object = object> {
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 `meta`, `{}` when it has none. */
40
- readonly meta: TMeta;
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
- * Wraps every request, like a tRPC link. Call `next(op)` to run the rest of
49
- * the chain; the last link is the query's own `queryFn`. `TDecl` is the
50
- * `meta` it reads: see {@link link}.
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#links-and-features
48
+ * @see https://raf-query.katt.dev/docs/links-and-features#parts
60
49
  */
61
- export type Feature<TDecl extends Decl = Decl> = FeatureFunction & Declares<TDecl>;
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 link that declares the `meta` fields it reads. A root's queries take
64
- * those fields only when the root has the link.
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
- * const traced = link<{ meta: { traceId?: string } }>((op, next) =>
68
- * withTrace(op.meta.traceId, () => next(op)),
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
- * @see https://raf-query.katt.dev/docs/links-and-features#links-and-features
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
- * ```ts
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 feature<TDecl extends Decl = Decl>(setup?: FeatureFunction): Feature<TDecl>;
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: links, features and the cache-wide defaults.
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
- /** Behaviors started while a provider has the client mounted. */
102
- readonly features?: ReadonlyArray<AnyFeature>;
103
- /** Default `gcTime`: 5 minutes. Server clients never gc. */
104
- readonly gcTime?: number;
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
- /** Wrap every request, outermost first. */
108
- readonly links?: ReadonlyArray<AnyLink>;
109
- /** Default `staleTime`: `0`. */
110
- readonly staleTime?: number;
111
- /** Default `structuralSharing`: `true`. */
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, which factories built with
116
- * `bindQueryOptions<typeof makeQueryClient>()` are checked against. The bare
117
- * `QueryClient` type takes a client of any root.
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 = AnyRoot> {
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, invariant. `undefined` at runtime. */
152
+ /** Phantom: the root's types. `undefined` at runtime. */
131
153
  readonly "~types"?: {
132
- readonly root: (types: TTypes) => TTypes;
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 `links` and `features`,
149
- * or spread `recommended()`. The client's `meta` type is what they declare.
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
- * const ownership = feature<{ meta: { owner?: "posts-team" | "users-team" } }>();
176
+ * interface Ownership {
177
+ * readonly options: { readonly owner?: "posts-team" | "users-team" };
178
+ * }
153
179
  * export const makeQueryClient = () =>
154
- * createQueryClient({ links: [retry()], features: [ownership], staleTime: 30_000 });
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 TLinks extends ReadonlyArray<AnyLink> = [], const TFeatures extends ReadonlyArray<AnyFeature> = []>(config?: QueryClientConfig & {
163
- readonly features?: TFeatures;
164
- readonly links?: TLinks;
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 features. Providers call it while mounted; the
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
- * A link that declares the `meta` fields it reads. A root's queries take
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 `links` and `features`,
42
- * or spread `recommended()`. The client's `meta` type is what they declare.
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
- * const ownership = feature<{ meta: { owner?: "posts-team" | "users-team" } }>();
18
+ * interface Ownership {
19
+ * readonly options: { readonly owner?: "posts-team" | "users-team" };
20
+ * }
46
21
  * export const makeQueryClient = () =>
47
- * createQueryClient({ links: [retry()], features: [ownership], staleTime: 30_000 });
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 features. Providers call it while mounted; the
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 start of client.config.features ?? []) start(client, current.controller.signal);
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, feature, link, mount, systemClock };
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.options?.gcTime ?? entry.client.config.gcTime ?? fiveMinutes;
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.options?.staleTime ?? entry.client.config.staleTime ?? 0;
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.options?.structuralSharing ?? entry.client.config.structuralSharing ?? true) && previous.hasData ? replaceEqualDeep(previous.data, data) : data;
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 links = op.client.config.links ?? [];
232
+ const parts = partsOf(op.client);
227
233
  const call = (index, current) => {
228
- const link = links[index];
229
- return link ? link(current, (next) => call(index + 1, next)) : terminal(current);
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
- meta: options.meta ?? {},
266
- options,
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 { Feature } from "../../client.mjs";
1
+ import { Part } from "../../client.mjs";
2
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;
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 `meta: { refetchOnWindowFocus: false }`.
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(): Feature<{
15
- readonly meta: FocusRefetchMeta;
16
- }>;
20
+ export declare function focusRefetch(): Part<FocusRefetch>;
17
21
  //#endregion
@@ -1,20 +1,20 @@
1
- import { feature } from "../../client.mjs";
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 `meta: { refetchOnWindowFocus: false }`.
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 feature((client, signal) => {
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 { Feature } from "../../client.mjs";
1
+ import { Part } from "../../client.mjs";
2
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;
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 `meta.refetchInterval` ms.
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(): Feature<{
14
- readonly meta: PollingMeta;
15
- }>;
19
+ export declare function polling(): Part<Polling>;
16
20
  //#endregion
@@ -1,22 +1,22 @@
1
- import { feature } from "../../client.mjs";
1
+ import { part } from "../../client.mjs";
2
2
  import { isNumber } from "../../guards.mjs";
3
3
  import { fetchEntry } from "../../entry.mjs";
4
- import { metaOf } from "../shared.mjs";
4
+ import { optionOf } from "../shared.mjs";
5
5
  //#region src/features/polling/polling.ts
6
6
  /**
7
- * Refetch read queries every `meta.refetchInterval` ms.
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 feature((client, signal) => {
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 = metaOf(entry, "refetchInterval");
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 { Feature } from "../../client.mjs";
1
+ import { Part } from "../../client.mjs";
2
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;
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 `meta: { refetchOnReconnect: false }`.
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(): Feature<{
15
- readonly meta: ReconnectRefetchMeta;
16
- }>;
20
+ export declare function reconnectRefetch(): Part<ReconnectRefetch>;
17
21
  //#endregion