@raf-query/core 0.14.0 → 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 CHANGED
@@ -1,14 +1,24 @@
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
+
3
13
  ## [0.14.0](https://github.com/KATT/raf-query/compare/0.13.2...0.14.0) (2026-11-11)
4
14
 
5
15
  ### ⚠ BREAKING CHANGES
6
16
 
7
- * **core:** parts declare the options and queryFn context a client's queries get ([#250](https://github.com/KATT/raf-query/issues/250))
17
+ - **core:** parts declare the options and queryFn context a client's queries get ([#250](https://github.com/KATT/raf-query/issues/250))
8
18
 
9
19
  ### Features
10
20
 
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))
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))
12
22
 
13
23
  ## [0.13.2](https://github.com/KATT/raf-query/compare/0.13.1...0.13.2) (2026-11-10)
14
24
 
package/dist/client.d.mts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { QueryKey } from "./keys/keys.mjs";
2
2
  import { Stamp } from "./keys/targets.mjs";
3
- import { AnyDeclares, Decl, Declares, NoDecl, NoParts, OptionsOfDecl, Root, RootOfParts, RootTypes } from "./root.mjs";
3
+ import { AnyDeclares, ContextOfDecl, Decl, Declares, NoDecl, NoParts, OptionsOfDecl, Root, RootOfParts, RootTypes } from "./root.mjs";
4
4
  import { CoreOptions, QueryOptions } from "./options.mjs";
5
5
  import { Listeners, SubscribeOptions } from "./subscribe.mjs";
6
6
  import { Entry } from "./entry.mjs";
@@ -24,6 +24,12 @@ export declare const systemClock: Clock;
24
24
  */
25
25
  export interface Operation<TDecl extends Decl = Decl> {
26
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>>;
27
33
  /** The entry's data when the request started (paged queries extend it). */
28
34
  readonly data: unknown;
29
35
  /** For paged queries: which page to add. */
@@ -58,27 +64,52 @@ export interface AnyPart extends AnyDeclares {
58
64
  readonly link?: LinkFunction;
59
65
  readonly setup?: SetupFunction;
60
66
  }
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
- } : {
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> {
68
84
  readonly context?: never;
69
- }) & {
70
85
  /**
71
86
  * Wraps every request, like a tRPC link. Call `next(op)` to run the
72
87
  * rest of the chain; the last link is the query's own `queryFn`.
73
88
  */
74
- readonly link?: (op: Operation<TDecl>, next: (op: Operation) => Promise<unknown>) => Promise<unknown>;
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>;
75
95
  /**
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.
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.
79
99
  */
80
- readonly setup?: (client: QueryClient<Root<TDecl>>, signal: AbortSignal) => void;
81
- };
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
+ }
82
113
  /** A declaration without a `context`: its part needs no definition. */
83
114
  interface OptionsDecl {
84
115
  readonly context?: never;
@@ -101,10 +132,24 @@ interface OptionsDecl {
101
132
  * A part without a link or `setup` only declares: an app's own options, such
102
133
  * as an `owner` its targets match on.
103
134
  *
135
+ * A part gives the `context` it declares for the client, as `context`, or
136
+ * per request, from its link:
137
+ *
138
+ * ```ts
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
+ * });
146
+ * ```
147
+ *
104
148
  * @see https://raf-query.katt.dev/docs/links-and-features#parts
105
149
  */
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>>;
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>>;
108
153
  export type ClientEvent = {
109
154
  readonly entry: Entry;
110
155
  readonly type: "added" | "removed" | "updated";
@@ -143,6 +188,8 @@ export interface QueryClientConfig {
143
188
  export interface QueryClient<TTypes extends RootTypes = NoParts> {
144
189
  /** Entries changed since the last notification. */
145
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;
146
193
  readonly "~entries": Map<string, Entry>;
147
194
  readonly "~events": Listeners<ClientEvent>;
148
195
  /** Orders requests and invalidations. */
@@ -155,8 +202,6 @@ export interface QueryClient<TTypes extends RootTypes = NoParts> {
155
202
  };
156
203
  readonly clock: Clock;
157
204
  readonly config: QueryClientConfig;
158
- /** What the parts give every `queryFn`, beside the client, key and signal. */
159
- readonly context: TTypes["context"];
160
205
  readonly isServer: boolean;
161
206
  /**
162
207
  * Cache events: entries added, updated, removed, or gaining and losing
package/dist/client.mjs CHANGED
@@ -35,13 +35,13 @@ function createQueryClient(config = {}) {
35
35
  const entries = /* @__PURE__ */ new Map();
36
36
  return {
37
37
  "~changed": /* @__PURE__ */ new Set(),
38
+ "~context": Object.assign({}, ...(config.parts ?? []).map((one) => one.context)),
38
39
  "~entries": entries,
39
40
  "~events": events,
40
41
  "~seq": 0,
41
42
  "~stamps": [],
42
43
  clock: config.clock ?? systemClock,
43
44
  config,
44
- context: Object.assign({}, ...(config.parts ?? []).map((one) => one.context)),
45
45
  isServer: config.isServer ?? !("window" in globalThis),
46
46
  subscribe: (listener, options) => {
47
47
  const unsubscribe = events.subscribe(listener, options);
package/dist/entry.mjs CHANGED
@@ -221,7 +221,7 @@ function terminal(op) {
221
221
  const options = op.options;
222
222
  const paged = options["~run"];
223
223
  resolve(paged ? paged(op) : options.queryFn({
224
- ...op.client.context,
224
+ ...op.context,
225
225
  client: op.client,
226
226
  queryKey: op.queryKey,
227
227
  signal: op.signal
@@ -264,6 +264,7 @@ function fetchEntry(entry, request = {}) {
264
264
  const state = entry.state;
265
265
  chain({
266
266
  client,
267
+ context: client["~context"],
267
268
  data: state.data,
268
269
  direction: request.direction,
269
270
  hasData: state.hasData,
@@ -66,7 +66,7 @@ function pageFor(data, pageParam) {
66
66
  }
67
67
  async function runPages(op, source) {
68
68
  const fetchPage = (pageParam, direction) => Promise.resolve(source.queryFn({
69
- ...op.client.context,
69
+ ...op.context,
70
70
  client: op.client,
71
71
  direction,
72
72
  pageParam,
package/dist/root.d.mts CHANGED
@@ -9,7 +9,7 @@
9
9
  */
10
10
  /**
11
11
  * What a part declares: the per-query `options` it reads, and the `context`
12
- * it gives every `queryFn`.
12
+ * it gives every `queryFn`, for the client or per request from its link.
13
13
  *
14
14
  * @see https://raf-query.katt.dev/docs/links-and-features#parts
15
15
  */
@@ -63,7 +63,7 @@ export type OptionsOfDecl<TDecl> = TDecl extends {
63
63
  readonly options?: infer TOptions extends object;
64
64
  } ? TOptions : unknown;
65
65
  /** The `queryFn` context a declaration adds to its root. */
66
- type ContextOfDecl<TDecl> = TDecl extends {
66
+ export type ContextOfDecl<TDecl> = TDecl extends {
67
67
  readonly context?: infer TContext extends object;
68
68
  } ? TContext : unknown;
69
69
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@raf-query/core",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "description": "Framework-agnostic core of raf-query, a render-as-you-fetch query library",
5
5
  "keywords": [
6
6
  "query",