@orpc/tanstack-query 1.14.6 → 2.0.0-beta.10

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/dist/index.mjs CHANGED
@@ -1,4 +1,6 @@
1
- import { stringifyJSON, isAsyncIteratorObject, toArray, get } from '@orpc/shared';
1
+ import { getPathMeta } from '@orpc/contract';
2
+ import { sortPlugins, stringifyJSON, resolveMaybeOptionalOptions, intercept, toArray, isAsyncIteratorObject, bindMethods, getOrBind, get, isTypescriptObject } from '@orpc/shared';
3
+ import { RECURSIVE_CLIENT_UNWRAP_KEYS } from '@orpc/client';
2
4
  import { skipToken } from '@tanstack/query-core';
3
5
 
4
6
  function generateOperationKey(path, state = {}) {
@@ -9,27 +11,41 @@ function generateOperationKey(path, state = {}) {
9
11
  }];
10
12
  }
11
13
 
12
- function createGeneralUtils(path) {
13
- return {
14
- key(options) {
15
- return generateOperationKey(path, options);
14
+ class CompositeRouterUtilsPlugin {
15
+ constructor(plugins = []) {
16
+ this.plugins = plugins;
17
+ this.plugins = sortPlugins(plugins);
18
+ }
19
+ name = "~composite";
20
+ init(options) {
21
+ for (const plugin of this.plugins) {
22
+ if (plugin.init) {
23
+ options = plugin.init(options);
24
+ }
16
25
  }
17
- };
26
+ return options;
27
+ }
28
+ initProcedureOptions(path, options) {
29
+ for (const plugin of this.plugins) {
30
+ if (plugin.initProcedureOptions) {
31
+ options = plugin.initProcedureOptions(path, options);
32
+ }
33
+ }
34
+ return options;
35
+ }
18
36
  }
19
37
 
20
- function experimental_liveQuery(queryFn) {
38
+ function liveQuery(queryFn) {
21
39
  return async (context) => {
22
40
  const stream = await queryFn(context);
23
41
  let last;
24
42
  for await (const chunk of stream) {
25
- if (context.signal.aborted) {
26
- throw context.signal.reason;
27
- }
43
+ context.signal?.throwIfAborted();
28
44
  last = { chunk };
29
45
  context.client.setQueryData(context.queryKey, chunk);
30
46
  }
31
47
  if (!last) {
32
- throw new Error(
48
+ throw new TypeError(
33
49
  `Live query for ${stringifyJSON(context.queryKey)} did not yield any data. Ensure the query function returns an AsyncIterable with at least one chunk.`
34
50
  );
35
51
  }
@@ -37,7 +53,7 @@ function experimental_liveQuery(queryFn) {
37
53
  };
38
54
  }
39
55
 
40
- function experimental_serializableStreamedQuery(queryFn, { refetchMode = "reset", maxChunks = Number.POSITIVE_INFINITY } = {}) {
56
+ function serializableStreamedQuery(queryFn, { refetchMode = "reset", maxChunks = Number.POSITIVE_INFINITY } = {}) {
41
57
  return async (context) => {
42
58
  const query = context.client.getQueryCache().find({ queryKey: context.queryKey, exact: true });
43
59
  const hasPreviousData = !!query && query.state.data !== void 0;
@@ -93,199 +109,425 @@ function limitArraySize(items, maxSize) {
93
109
  return items.slice(items.length - maxSize);
94
110
  }
95
111
 
96
- const OPERATION_CONTEXT_SYMBOL = Symbol("ORPC_OPERATION_CONTEXT");
112
+ const OPERATION_CONTEXT_SYMBOL = Symbol.for("ORPC_TANSTACK_QUERY_OPERATION_CONTEXT");
97
113
 
98
- function createProcedureUtils(client, options) {
99
- const utils = {
100
- call: client,
101
- queryKey(...[optionsIn = {}]) {
102
- optionsIn = { ...options.experimental_defaults?.queryKey, ...optionsIn };
103
- const queryKey = optionsIn.queryKey ?? generateOperationKey(options.path, { type: "query", input: optionsIn.input });
104
- return queryKey;
105
- },
106
- queryOptions(...[optionsIn = {}]) {
107
- optionsIn = { ...options.experimental_defaults?.queryOptions, ...optionsIn };
108
- const queryKey = utils.queryKey(optionsIn);
109
- return {
110
- queryFn: ({ signal }) => {
111
- if (optionsIn.input === skipToken) {
112
- throw new Error("queryFn should not be called with skipToken used as input");
113
- }
114
- return client(optionsIn.input, {
115
- signal,
114
+ class ProcedureUtils {
115
+ constructor(path, client, options = {}) {
116
+ this.path = path;
117
+ this.options = options;
118
+ this.call = client;
119
+ }
120
+ /**
121
+ * Calling corresponding procedure client
122
+ */
123
+ call;
124
+ /**
125
+ * Generate a **full matching** key for [Query Options](https://orpc.dev/docs/integrations/tanstack-query#query-options).
126
+ */
127
+ queryKey(...rest) {
128
+ let optionsIn = resolveMaybeOptionalOptions(rest);
129
+ if (typeof this.options.queryKey === "function") {
130
+ optionsIn = this.options.queryKey(optionsIn);
131
+ } else if (this.options.queryKey) {
132
+ optionsIn = { ...this.options.queryKey, ...optionsIn };
133
+ }
134
+ const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, { type: "query", input: optionsIn.input });
135
+ return queryKey;
136
+ }
137
+ /**
138
+ * Generate options used for useQuery/useSuspenseQuery/prefetchQuery/...
139
+ */
140
+ queryOptions(...rest) {
141
+ let optionsIn = resolveMaybeOptionalOptions(rest);
142
+ if (typeof this.options.queryOptions === "function") {
143
+ optionsIn = this.options.queryOptions(optionsIn);
144
+ } else if (this.options.queryOptions) {
145
+ optionsIn = { ...this.options.queryOptions, ...optionsIn };
146
+ }
147
+ const queryKey = this.queryKey(optionsIn);
148
+ return {
149
+ ...optionsIn,
150
+ queryKey,
151
+ queryFn: (fnContext) => {
152
+ return intercept(
153
+ this.options.queryInterceptors,
154
+ {
155
+ path: this.path,
116
156
  context: {
117
157
  [OPERATION_CONTEXT_SYMBOL]: {
118
158
  key: queryKey,
119
159
  type: "query"
120
160
  },
121
161
  ...optionsIn.context
162
+ },
163
+ input: optionsIn.input,
164
+ fnContext
165
+ },
166
+ ({ context, input, fnContext: fnContext2 }) => {
167
+ if (input === skipToken || optionsIn.queryFn === skipToken) {
168
+ throw new Error("queryFn should not be called when skipToken used for skipping");
122
169
  }
123
- });
124
- },
125
- ...optionsIn.input === skipToken ? { enabled: false } : {},
126
- ...optionsIn,
127
- queryKey
128
- };
129
- },
130
- experimental_streamedKey(...[optionsIn = {}]) {
131
- optionsIn = { ...options.experimental_defaults?.experimental_streamedKey, ...optionsIn };
132
- const queryKey = optionsIn.queryKey ?? generateOperationKey(options.path, { type: "streamed", input: optionsIn.input, fnOptions: optionsIn.queryFnOptions });
133
- return queryKey;
134
- },
135
- experimental_streamedOptions(...[optionsIn = {}]) {
136
- optionsIn = { ...options.experimental_defaults?.experimental_streamedOptions, ...optionsIn };
137
- const queryKey = utils.experimental_streamedKey(optionsIn);
138
- return {
139
- queryFn: experimental_serializableStreamedQuery(
140
- async ({ signal }) => {
141
- if (optionsIn.input === skipToken) {
142
- throw new Error("queryFn should not be called with skipToken used as input");
170
+ if (optionsIn.queryFn) {
171
+ return optionsIn.queryFn(fnContext2);
143
172
  }
144
- const output = await client(optionsIn.input, {
145
- signal,
146
- context: {
147
- [OPERATION_CONTEXT_SYMBOL]: {
148
- key: queryKey,
149
- type: "streamed"
150
- },
151
- ...optionsIn.context
152
- }
153
- });
154
- if (!isAsyncIteratorObject(output)) {
155
- throw new Error("streamedQuery requires an event iterator output");
156
- }
157
- return output;
173
+ return this.call(input, { signal: fnContext2.signal, context });
174
+ }
175
+ );
176
+ },
177
+ ...optionsIn.input === skipToken || optionsIn.queryFn === skipToken ? { enabled: false } : {}
178
+ };
179
+ }
180
+ /**
181
+ * Generate a **full matching** key for [Streamed Query Options](https://orpc.dev/docs/integrations/tanstack-query#streamed-query-options).
182
+ */
183
+ streamedKey(...rest) {
184
+ let optionsIn = resolveMaybeOptionalOptions(rest);
185
+ if (typeof this.options.streamedKey === "function") {
186
+ optionsIn = this.options.streamedKey(optionsIn);
187
+ } else if (this.options.streamedKey) {
188
+ optionsIn = { ...this.options.streamedKey, ...optionsIn };
189
+ }
190
+ const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, { type: "streamed", input: optionsIn.input, fnOptions: optionsIn.queryFnOptions });
191
+ return queryKey;
192
+ }
193
+ /**
194
+ * Configure queries for [Event Iterator](https://orpc.dev/docs/event-iterator).
195
+ * This is built on [TanStack Query streamedQuery](https://tanstack.com/query/latest/docs/reference/streamedQuery)
196
+ * and works with hooks like `useQuery`, `useSuspenseQuery`, or `prefetchQuery`.
197
+ */
198
+ streamedOptions(...rest) {
199
+ let optionsIn = resolveMaybeOptionalOptions(rest);
200
+ if (typeof this.options.streamedOptions === "function") {
201
+ optionsIn = this.options.streamedOptions(optionsIn);
202
+ } else if (this.options.streamedOptions) {
203
+ optionsIn = { ...this.options.streamedOptions, ...optionsIn };
204
+ }
205
+ const queryKey = this.streamedKey(optionsIn);
206
+ return {
207
+ ...optionsIn,
208
+ queryKey,
209
+ queryFn: (fnContext) => {
210
+ return intercept(
211
+ toArray(this.options.streamedInterceptors),
212
+ {
213
+ path: this.path,
214
+ context: {
215
+ [OPERATION_CONTEXT_SYMBOL]: {
216
+ key: queryKey,
217
+ type: "streamed"
218
+ },
219
+ ...optionsIn.context
220
+ },
221
+ input: optionsIn.input,
222
+ fnContext
158
223
  },
159
- optionsIn.queryFnOptions
160
- ),
161
- ...optionsIn.input === skipToken ? { enabled: false } : {},
162
- ...optionsIn,
163
- queryKey
164
- };
165
- },
166
- experimental_liveKey(...[optionsIn = {}]) {
167
- optionsIn = { ...options.experimental_defaults?.experimental_liveKey, ...optionsIn };
168
- const queryKey = optionsIn.queryKey ?? generateOperationKey(options.path, { type: "live", input: optionsIn.input });
169
- return queryKey;
170
- },
171
- experimental_liveOptions(...[optionsIn = {}]) {
172
- optionsIn = { ...options.experimental_defaults?.experimental_liveOptions, ...optionsIn };
173
- const queryKey = utils.experimental_liveKey(optionsIn);
174
- return {
175
- queryFn: experimental_liveQuery(async ({ signal }) => {
176
- if (optionsIn.input === skipToken) {
177
- throw new Error("queryFn should not be called with skipToken used as input");
224
+ ({ context, fnContext: fnContext2, input }) => {
225
+ if (input === skipToken || optionsIn.queryFn === skipToken) {
226
+ throw new Error("queryFn should not be called when skipToken used for skipping");
227
+ }
228
+ if (optionsIn.queryFn) {
229
+ return optionsIn.queryFn(fnContext2);
230
+ }
231
+ return serializableStreamedQuery(
232
+ async (queryContext) => {
233
+ const output = await this.call(input, { signal: queryContext.signal, context });
234
+ if (!isAsyncIteratorObject(output)) {
235
+ throw new Error("streamedQuery requires an event iterator output");
236
+ }
237
+ return output;
238
+ },
239
+ optionsIn.queryFnOptions
240
+ )(fnContext2);
178
241
  }
179
- const output = await client(optionsIn.input, {
180
- signal,
242
+ );
243
+ },
244
+ ...optionsIn.input === skipToken || optionsIn.queryFn === skipToken ? { enabled: false } : {}
245
+ };
246
+ }
247
+ /**
248
+ * Generate a **full matching** key for [Live Query Options](https://orpc.dev/docs/integrations/tanstack-query#live-query-options).
249
+ */
250
+ liveKey(...rest) {
251
+ let optionsIn = resolveMaybeOptionalOptions(rest);
252
+ if (typeof this.options.liveKey === "function") {
253
+ optionsIn = this.options.liveKey(optionsIn);
254
+ } else if (this.options.liveKey) {
255
+ optionsIn = { ...this.options.liveKey, ...optionsIn };
256
+ }
257
+ const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, { type: "live", input: optionsIn.input });
258
+ return queryKey;
259
+ }
260
+ /**
261
+ * Configure live queries for [Event Iterator](https://orpc.dev/docs/event-iterator).
262
+ * Unlike `.streamedOptions` which accumulates chunks, live queries replace the entire result with each new chunk received.
263
+ * Works with hooks like `useQuery`, `useSuspenseQuery`, or `prefetchQuery`.
264
+ */
265
+ liveOptions(...rest) {
266
+ let optionsIn = resolveMaybeOptionalOptions(rest);
267
+ if (typeof this.options.liveOptions === "function") {
268
+ optionsIn = this.options.liveOptions(optionsIn);
269
+ } else if (this.options.liveOptions) {
270
+ optionsIn = { ...this.options.liveOptions, ...optionsIn };
271
+ }
272
+ const queryKey = this.liveKey(optionsIn);
273
+ return {
274
+ ...optionsIn,
275
+ queryKey,
276
+ queryFn: (fnContext) => {
277
+ return intercept(
278
+ toArray(this.options.liveInterceptors),
279
+ {
280
+ path: this.path,
181
281
  context: {
182
282
  [OPERATION_CONTEXT_SYMBOL]: {
183
283
  key: queryKey,
184
284
  type: "live"
185
285
  },
186
286
  ...optionsIn.context
287
+ },
288
+ input: optionsIn.input,
289
+ fnContext
290
+ },
291
+ ({ fnContext: fnContext2, input, context }) => {
292
+ if (input === skipToken || optionsIn.queryFn === skipToken) {
293
+ throw new Error("queryFn should not be called when skipToken used for skipping");
187
294
  }
188
- });
189
- if (!isAsyncIteratorObject(output)) {
190
- throw new Error("liveQuery requires an event iterator output");
191
- }
192
- return output;
193
- }),
194
- ...optionsIn.input === skipToken ? { enabled: false } : {},
195
- ...optionsIn,
196
- queryKey
197
- };
198
- },
199
- infiniteKey(optionsIn) {
200
- optionsIn = { ...options.experimental_defaults?.infiniteKey, ...optionsIn };
201
- const queryKey = optionsIn.queryKey ?? generateOperationKey(options.path, {
202
- type: "infinite",
203
- input: optionsIn.input === skipToken ? skipToken : optionsIn.input(optionsIn.initialPageParam)
204
- });
205
- return queryKey;
206
- },
207
- infiniteOptions(optionsIn) {
208
- optionsIn = { ...options.experimental_defaults?.infiniteOptions, ...optionsIn };
209
- const queryKey = utils.infiniteKey(optionsIn);
210
- return {
211
- queryFn: ({ pageParam, signal }) => {
212
- if (optionsIn.input === skipToken) {
213
- throw new Error("queryFn should not be called with skipToken used as input");
295
+ if (optionsIn.queryFn) {
296
+ return optionsIn.queryFn(fnContext2);
297
+ }
298
+ return liveQuery(async (queryContext) => {
299
+ const output = await this.call(input, {
300
+ signal: queryContext.signal,
301
+ context
302
+ });
303
+ if (!isAsyncIteratorObject(output)) {
304
+ throw new Error("liveQuery requires an event iterator output");
305
+ }
306
+ return output;
307
+ })(fnContext2);
214
308
  }
215
- return client(optionsIn.input(pageParam), {
216
- signal,
309
+ );
310
+ },
311
+ ...optionsIn.input === skipToken || optionsIn.queryFn === skipToken ? { enabled: false } : {}
312
+ };
313
+ }
314
+ /**
315
+ * Generate a **full matching** key for [Infinite Query Options](https://orpc.dev/docs/integrations/tanstack-query#infinite-query-options).
316
+ */
317
+ infiniteKey(optionsIn) {
318
+ if (typeof this.options.infiniteKey === "function") {
319
+ optionsIn = this.options.infiniteKey(optionsIn);
320
+ } else if (this.options.infiniteKey) {
321
+ optionsIn = { ...this.options.infiniteKey, ...optionsIn };
322
+ }
323
+ const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, {
324
+ type: "infinite",
325
+ input: optionsIn.input === skipToken ? skipToken : optionsIn.input(optionsIn.initialPageParam)
326
+ });
327
+ return queryKey;
328
+ }
329
+ /**
330
+ * Generate options used for useInfiniteQuery/useSuspenseInfiniteQuery/prefetchInfiniteQuery/...
331
+ */
332
+ infiniteOptions(optionsIn) {
333
+ if (typeof this.options.infiniteOptions === "function") {
334
+ optionsIn = this.options.infiniteOptions(optionsIn);
335
+ } else if (this.options.infiniteOptions) {
336
+ optionsIn = { ...this.options.infiniteOptions, ...optionsIn };
337
+ }
338
+ const queryKey = this.infiniteKey(optionsIn);
339
+ return {
340
+ ...optionsIn,
341
+ queryKey,
342
+ queryFn: (fnContext) => {
343
+ return intercept(
344
+ toArray(this.options.infiniteInterceptors),
345
+ {
346
+ path: this.path,
217
347
  context: {
218
348
  [OPERATION_CONTEXT_SYMBOL]: {
219
349
  key: queryKey,
220
350
  type: "infinite"
221
351
  },
222
352
  ...optionsIn.context
223
- }
224
- });
225
- },
226
- ...optionsIn.input === skipToken ? { enabled: false } : {},
227
- ...optionsIn,
228
- queryKey
229
- };
230
- },
231
- mutationKey(...[optionsIn = {}]) {
232
- optionsIn = { ...options.experimental_defaults?.mutationKey, ...optionsIn };
233
- const mutationKey = optionsIn.mutationKey ?? generateOperationKey(options.path, { type: "mutation" });
234
- return mutationKey;
235
- },
236
- mutationOptions(...[optionsIn = {}]) {
237
- optionsIn = { ...options.experimental_defaults?.mutationOptions, ...optionsIn };
238
- const mutationKey = utils.mutationKey(optionsIn);
239
- return {
240
- mutationFn: (input) => client(input, {
241
- context: {
242
- [OPERATION_CONTEXT_SYMBOL]: {
243
- key: mutationKey,
244
- type: "mutation"
245
353
  },
246
- ...optionsIn.context
354
+ input: optionsIn.input === skipToken ? skipToken : optionsIn.input(fnContext.pageParam),
355
+ fnContext
356
+ },
357
+ ({ context, input, fnContext: fnContext2 }) => {
358
+ if (input === skipToken || optionsIn.queryFn === skipToken) {
359
+ throw new Error("queryFn should not be called when skipToken used for skipping");
360
+ }
361
+ if (optionsIn.queryFn) {
362
+ return optionsIn.queryFn(fnContext2);
363
+ }
364
+ return this.call(input, { signal: fnContext2.signal, context });
247
365
  }
248
- }),
249
- ...optionsIn,
250
- mutationKey
251
- };
366
+ );
367
+ },
368
+ ...optionsIn.input === skipToken || optionsIn.queryFn === skipToken ? { enabled: false } : {}
369
+ };
370
+ }
371
+ /**
372
+ * Generate a **full matching** key for [Mutation Options](https://orpc.dev/docs/integrations/tanstack-query#mutation-options).
373
+ */
374
+ mutationKey(optionsIn = {}) {
375
+ if (typeof this.options.mutationKey === "function") {
376
+ optionsIn = this.options.mutationKey(optionsIn);
377
+ } else if (this.options.mutationKey) {
378
+ optionsIn = { ...this.options.mutationKey, ...optionsIn };
252
379
  }
253
- };
254
- return utils;
380
+ const mutationKey = optionsIn.mutationKey ?? generateOperationKey(this.path, { type: "mutation" });
381
+ return mutationKey;
382
+ }
383
+ /**
384
+ * Generate options used for useMutation/...
385
+ */
386
+ mutationOptions(...rest) {
387
+ let optionsIn = resolveMaybeOptionalOptions(rest);
388
+ if (typeof this.options.mutationOptions === "function") {
389
+ optionsIn = this.options.mutationOptions(optionsIn);
390
+ } else if (this.options.mutationOptions) {
391
+ optionsIn = { ...this.options.mutationOptions, ...optionsIn };
392
+ }
393
+ const mutationKey = this.mutationKey(optionsIn);
394
+ return {
395
+ ...optionsIn,
396
+ mutationKey,
397
+ mutationFn: (input, fnContext) => {
398
+ return intercept(
399
+ toArray(this.options.mutationInterceptors),
400
+ {
401
+ path: this.path,
402
+ context: {
403
+ [OPERATION_CONTEXT_SYMBOL]: {
404
+ key: mutationKey,
405
+ type: "mutation"
406
+ },
407
+ ...optionsIn.context
408
+ },
409
+ fnContext,
410
+ input
411
+ },
412
+ ({ input: input2, fnContext: fnContext2, context }) => {
413
+ if (optionsIn.mutationFn) {
414
+ return optionsIn.mutationFn(input2, fnContext2);
415
+ }
416
+ return this.call(input2, { context });
417
+ }
418
+ );
419
+ }
420
+ };
421
+ }
255
422
  }
256
423
 
424
+ class SharedRouterUtils {
425
+ constructor(path) {
426
+ this.path = path;
427
+ }
428
+ /**
429
+ * Generate a **partial matching** key for actions like revalidating queries, checking mutation status, etc.
430
+ */
431
+ key(options = {}) {
432
+ return generateOperationKey(this.path, options);
433
+ }
434
+ }
257
435
  function createRouterUtils(client, options = {}) {
436
+ const plugin = new CompositeRouterUtilsPlugin(options.plugins);
437
+ options = plugin.init(options);
438
+ return createRouterUtilsInternal(client, options, plugin);
439
+ }
440
+ function createRouterUtilsInternal(client, options = {}, plugin) {
258
441
  const path = toArray(options.path);
259
- const generalUtils = createGeneralUtils(path);
260
- const procedureUtils = createProcedureUtils(client, {
442
+ const sharedUtils = bindMethods(new SharedRouterUtils(path));
443
+ const procedureUtils = typeof client === "function" && (options.scoped === void 0 || isProcedureUtilsOptions(options.scoped)) ? bindMethods(new ProcedureUtils(
261
444
  path,
262
- experimental_defaults: options.experimental_defaults
263
- });
445
+ client,
446
+ plugin.initProcedureOptions(path, {
447
+ ...options.scoped,
448
+ queryInterceptors: [...toArray(options.queryInterceptors), ...toArray(options.scoped?.queryInterceptors)],
449
+ streamedInterceptors: [...toArray(options.streamedInterceptors), ...toArray(options.scoped?.streamedInterceptors)],
450
+ liveInterceptors: [...toArray(options.liveInterceptors), ...toArray(options.scoped?.liveInterceptors)],
451
+ infiniteInterceptors: [...toArray(options.infiniteInterceptors), ...toArray(options.scoped?.infiniteInterceptors)],
452
+ mutationInterceptors: [...toArray(options.mutationInterceptors), ...toArray(options.scoped?.mutationInterceptors)]
453
+ })
454
+ )) : void 0;
264
455
  const recursive = new Proxy({
265
- ...generalUtils,
456
+ ...sharedUtils,
266
457
  ...procedureUtils
267
458
  }, {
268
459
  get(target, prop) {
269
- const value = Reflect.get(target, prop);
270
- if (typeof prop !== "string") {
460
+ const value = getOrBind(target, prop);
461
+ const nextClient = get(client, [prop]);
462
+ if (typeof prop !== "string" || RECURSIVE_CLIENT_UNWRAP_KEYS.has(prop) || !isTypescriptObject(nextClient)) {
271
463
  return value;
272
464
  }
273
- const nextUtils = createRouterUtils(client[prop], {
465
+ const nextUtils = createRouterUtilsInternal(nextClient, {
274
466
  ...options,
275
467
  path: [...path, prop],
276
- experimental_defaults: get(options.experimental_defaults, [prop])
277
- });
468
+ scoped: get(options.scoped, [prop])
469
+ }, plugin);
278
470
  if (typeof value !== "function") {
279
471
  return nextUtils;
280
472
  }
281
473
  return new Proxy(value, {
282
- get(_, prop2) {
283
- return Reflect.get(nextUtils, prop2);
474
+ get(target2, prop2) {
475
+ if (typeof prop2 !== "string" || RECURSIVE_CLIENT_UNWRAP_KEYS.has(prop2)) {
476
+ return getOrBind(target2, prop2);
477
+ }
478
+ return getOrBind(nextUtils, prop2);
284
479
  }
285
480
  });
286
481
  }
287
482
  });
288
483
  return recursive;
289
484
  }
485
+ function isProcedureUtilsOptions(value) {
486
+ if (!isTypescriptObject(value)) {
487
+ return false;
488
+ }
489
+ if (value.queryInterceptors !== void 0 && !Array.isArray(value.queryInterceptors)) {
490
+ return false;
491
+ }
492
+ if (value.streamedInterceptors !== void 0 && !Array.isArray(value.streamedInterceptors)) {
493
+ return false;
494
+ }
495
+ if (value.liveInterceptors !== void 0 && !Array.isArray(value.liveInterceptors)) {
496
+ return false;
497
+ }
498
+ if (value.infiniteInterceptors !== void 0 && !Array.isArray(value.infiniteInterceptors)) {
499
+ return false;
500
+ }
501
+ if (value.mutationInterceptors !== void 0 && !Array.isArray(value.mutationInterceptors)) {
502
+ return false;
503
+ }
504
+ return true;
505
+ }
506
+
507
+ function createContractUtilsFactory(caller, options) {
508
+ return (procedure) => {
509
+ const path = getPathMeta(procedure);
510
+ if (!path) {
511
+ throw new TypeError(
512
+ "ContractUtilsFactory: procedure contract must define `meta.path` that matches its path in the root router contract."
513
+ );
514
+ }
515
+ const scoped = get(options.scoped, path);
516
+ if (scoped !== void 0 && (scoped === null || typeof scoped !== "object")) {
517
+ throw new TypeError(
518
+ `ContractUtilsFactory: "scoped" at path "${path.join(".")}" must be an object or undefined, got "${scoped}".`
519
+ );
520
+ }
521
+ const client = (...rest) => caller(procedure, ...rest);
522
+ return createRouterUtils(client, {
523
+ ...options,
524
+ scoped,
525
+ path: [...toArray(options.path), ...path]
526
+ });
527
+ };
528
+ }
529
+ function createContractJsonifiedUtilsFactory(caller, options) {
530
+ return createContractUtilsFactory(caller, options);
531
+ }
290
532
 
291
- export { OPERATION_CONTEXT_SYMBOL, OPERATION_CONTEXT_SYMBOL as TANSTACK_QUERY_OPERATION_CONTEXT_SYMBOL, createGeneralUtils, createProcedureUtils, createRouterUtils, createRouterUtils as createTanstackQueryUtils, experimental_serializableStreamedQuery, generateOperationKey };
533
+ export { OPERATION_CONTEXT_SYMBOL, ProcedureUtils, SharedRouterUtils, OPERATION_CONTEXT_SYMBOL as TANSTACK_QUERY_OPERATION_CONTEXT_SYMBOL, createContractJsonifiedUtilsFactory, createContractUtilsFactory, createRouterUtils, createRouterUtils as createTanstackQueryUtils, generateOperationKey, serializableStreamedQuery };