@orpc/tanstack-query 2.0.0-beta.3 → 2.0.0-beta.30

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,16 +1,8 @@
1
- import { getPathMeta } from '@orpc/contract';
2
- import { sortPlugins, stringifyJSON, resolveMaybeOptionalOptions, intercept, toArray, isAsyncIteratorObject, bindMethods, getOrBind, get, isTypescriptObject } from '@orpc/shared';
1
+ import { resolveBasePathMeta, getRouterContract, ProcedureContract } from '@orpc/contract';
2
+ import { sortPlugins, stringifyJSON, resolveMaybeOptionalOptions, intercept, toArray, isAsyncIteratorObject, isTypescriptObject, bindMethods, get } from '@orpc/shared';
3
3
  import { RECURSIVE_CLIENT_UNWRAP_KEYS } from '@orpc/client';
4
4
  import { skipToken } from '@tanstack/query-core';
5
5
 
6
- function generateOperationKey(path, state = {}) {
7
- return [path, {
8
- ...state.input !== void 0 ? { input: state.input } : {},
9
- ...state.type !== void 0 ? { type: state.type } : {},
10
- ...state.fnOptions !== void 0 ? { fnOptions: state.fnOptions } : {}
11
- }];
12
- }
13
-
14
6
  class CompositeRouterUtilsPlugin {
15
7
  constructor(plugins = []) {
16
8
  this.plugins = plugins;
@@ -35,6 +27,18 @@ class CompositeRouterUtilsPlugin {
35
27
  }
36
28
  }
37
29
 
30
+ function generateOperationKey(path, options = {}) {
31
+ return [
32
+ ...options.prefix !== void 0 ? [options.prefix] : [],
33
+ path,
34
+ {
35
+ ...options.input !== void 0 ? { input: options.input } : {},
36
+ ...options.type !== void 0 ? { type: options.type } : {},
37
+ ...options.fnOptions !== void 0 ? { fnOptions: options.fnOptions } : {}
38
+ }
39
+ ];
40
+ }
41
+
38
42
  function liveQuery(queryFn) {
39
43
  return async (context) => {
40
44
  const stream = await queryFn(context);
@@ -53,6 +57,19 @@ function liveQuery(queryFn) {
53
57
  };
54
58
  }
55
59
 
60
+ class SharedUtils {
61
+ constructor(path, options) {
62
+ this.path = path;
63
+ this.options = options;
64
+ }
65
+ /**
66
+ * Generate a **partial matching** key for actions like revalidating queries, checking mutation status, etc.
67
+ */
68
+ key(options = {}) {
69
+ return generateOperationKey(this.path, { ...options, prefix: this.options.prefix });
70
+ }
71
+ }
72
+
56
73
  function serializableStreamedQuery(queryFn, { refetchMode = "reset", maxChunks = Number.POSITIVE_INFINITY } = {}) {
57
74
  return async (context) => {
58
75
  const query = context.client.getQueryCache().find({ queryKey: context.queryKey, exact: true });
@@ -111,16 +128,84 @@ function limitArraySize(items, maxSize) {
111
128
 
112
129
  const OPERATION_CONTEXT_SYMBOL = Symbol.for("ORPC_TANSTACK_QUERY_OPERATION_CONTEXT");
113
130
 
114
- class ProcedureUtils {
115
- constructor(path, client, options = {}) {
116
- this.path = path;
117
- this.options = options;
118
- this.call = client;
131
+ const PROCEDURE_UTILS_INTERCEPTOR_KEYS = [
132
+ "queryInterceptors",
133
+ "streamedInterceptors",
134
+ "liveInterceptors",
135
+ "infiniteInterceptors",
136
+ "mutationInterceptors"
137
+ ];
138
+ const PROCEDURE_UTILS_MODIFIER_KEYS = [
139
+ "queryKey",
140
+ "queryOptions",
141
+ "streamedKey",
142
+ "streamedOptions",
143
+ "liveKey",
144
+ "liveOptions",
145
+ "infiniteKey",
146
+ "infiniteOptions",
147
+ "mutationKey",
148
+ "mutationOptions"
149
+ ];
150
+ function isProcedureUtilsOptions(value) {
151
+ if (!isTypescriptObject(value)) {
152
+ return false;
153
+ }
154
+ for (const key in value) {
155
+ if (value[key] === void 0) {
156
+ continue;
157
+ }
158
+ if (key === "prefix") {
159
+ if (typeof value[key] !== "string") {
160
+ return false;
161
+ }
162
+ } else if (PROCEDURE_UTILS_INTERCEPTOR_KEYS.includes(key)) {
163
+ if (!Array.isArray(value[key]) || value[key].some((i) => typeof i !== "function")) {
164
+ return false;
165
+ }
166
+ } else if (PROCEDURE_UTILS_MODIFIER_KEYS.includes(key)) {
167
+ if (!isTypescriptObject(value[key])) {
168
+ return false;
169
+ }
170
+ }
171
+ }
172
+ return true;
173
+ }
174
+ function mergeProcedureUtilsModifier(base, override) {
175
+ if (typeof base !== "function" && typeof override !== "function") {
176
+ return { ...base, ...override };
119
177
  }
178
+ const applyBase = typeof base === "function" ? base : (options) => ({ ...base, ...options });
179
+ const applyOverride = typeof override === "function" ? override : (options) => ({ ...override, ...options });
180
+ return (options) => applyOverride(applyBase(options));
181
+ }
182
+ function mergeProcedureUtilsOptions(base, override) {
183
+ const merged = { ...base, ...override };
184
+ for (const key of PROCEDURE_UTILS_INTERCEPTOR_KEYS) {
185
+ const baseValue = base[key];
186
+ const overrideValue = override[key];
187
+ if (baseValue && overrideValue) {
188
+ merged[key] = [...baseValue, ...overrideValue];
189
+ }
190
+ }
191
+ for (const key of PROCEDURE_UTILS_MODIFIER_KEYS) {
192
+ const baseValue = base[key];
193
+ const overrideValue = override[key];
194
+ if (baseValue && overrideValue) {
195
+ merged[key] = mergeProcedureUtilsModifier(baseValue, overrideValue);
196
+ }
197
+ }
198
+ return merged;
199
+ }
200
+ class ProcedureUtils extends SharedUtils {
120
201
  /**
121
202
  * Calling corresponding procedure client
122
203
  */
123
204
  call;
205
+ constructor(path, client, options = {}) {
206
+ super(path, options);
207
+ this.call = client;
208
+ }
124
209
  /**
125
210
  * Generate a **full matching** key for [Query Options](https://orpc.dev/docs/integrations/tanstack-query#query-options).
126
211
  */
@@ -131,7 +216,7 @@ class ProcedureUtils {
131
216
  } else if (this.options.queryKey) {
132
217
  optionsIn = { ...this.options.queryKey, ...optionsIn };
133
218
  }
134
- const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, { type: "query", input: optionsIn.input });
219
+ const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, { prefix: this.options.prefix, type: "query", input: optionsIn.input });
135
220
  return queryKey;
136
221
  }
137
222
  /**
@@ -187,11 +272,11 @@ class ProcedureUtils {
187
272
  } else if (this.options.streamedKey) {
188
273
  optionsIn = { ...this.options.streamedKey, ...optionsIn };
189
274
  }
190
- const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, { type: "streamed", input: optionsIn.input, fnOptions: optionsIn.queryFnOptions });
275
+ const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, { prefix: this.options.prefix, type: "streamed", input: optionsIn.input, fnOptions: optionsIn.queryFnOptions });
191
276
  return queryKey;
192
277
  }
193
278
  /**
194
- * Configure queries for [Event Iterator](https://orpc.dev/docs/event-iterator).
279
+ * Configure queries for [AsyncIteratorObject](https://orpc.dev/docs/async-iterator-object).
195
280
  * This is built on [TanStack Query streamedQuery](https://tanstack.com/query/latest/docs/reference/streamedQuery)
196
281
  * and works with hooks like `useQuery`, `useSuspenseQuery`, or `prefetchQuery`.
197
282
  */
@@ -232,7 +317,7 @@ class ProcedureUtils {
232
317
  async (queryContext) => {
233
318
  const output = await this.call(input, { signal: queryContext.signal, context });
234
319
  if (!isAsyncIteratorObject(output)) {
235
- throw new Error("streamedQuery requires an event iterator output");
320
+ throw new Error("streamedQuery requires an AsyncIteratorObject output");
236
321
  }
237
322
  return output;
238
323
  },
@@ -254,11 +339,11 @@ class ProcedureUtils {
254
339
  } else if (this.options.liveKey) {
255
340
  optionsIn = { ...this.options.liveKey, ...optionsIn };
256
341
  }
257
- const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, { type: "live", input: optionsIn.input });
342
+ const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, { prefix: this.options.prefix, type: "live", input: optionsIn.input });
258
343
  return queryKey;
259
344
  }
260
345
  /**
261
- * Configure live queries for [Event Iterator](https://orpc.dev/docs/event-iterator).
346
+ * Configure live queries for [AsyncIteratorObject](https://orpc.dev/docs/async-iterator-object).
262
347
  * Unlike `.streamedOptions` which accumulates chunks, live queries replace the entire result with each new chunk received.
263
348
  * Works with hooks like `useQuery`, `useSuspenseQuery`, or `prefetchQuery`.
264
349
  */
@@ -301,7 +386,7 @@ class ProcedureUtils {
301
386
  context
302
387
  });
303
388
  if (!isAsyncIteratorObject(output)) {
304
- throw new Error("liveQuery requires an event iterator output");
389
+ throw new Error("liveQuery requires an AsyncIteratorObject output");
305
390
  }
306
391
  return output;
307
392
  })(fnContext2);
@@ -321,6 +406,7 @@ class ProcedureUtils {
321
406
  optionsIn = { ...this.options.infiniteKey, ...optionsIn };
322
407
  }
323
408
  const queryKey = optionsIn.queryKey ?? generateOperationKey(this.path, {
409
+ prefix: this.options.prefix,
324
410
  type: "infinite",
325
411
  input: optionsIn.input === skipToken ? skipToken : optionsIn.input(optionsIn.initialPageParam)
326
412
  });
@@ -377,7 +463,7 @@ class ProcedureUtils {
377
463
  } else if (this.options.mutationKey) {
378
464
  optionsIn = { ...this.options.mutationKey, ...optionsIn };
379
465
  }
380
- const mutationKey = optionsIn.mutationKey ?? generateOperationKey(this.path, { type: "mutation" });
466
+ const mutationKey = optionsIn.mutationKey ?? generateOperationKey(this.path, { prefix: this.options.prefix, type: "mutation" });
381
467
  return mutationKey;
382
468
  }
383
469
  /**
@@ -421,113 +507,107 @@ class ProcedureUtils {
421
507
  }
422
508
  }
423
509
 
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
- }
435
510
  function createRouterUtils(client, options = {}) {
436
511
  const plugin = new CompositeRouterUtilsPlugin(options.plugins);
437
512
  options = plugin.init(options);
438
513
  return createRouterUtilsInternal(client, options, plugin);
439
514
  }
440
- function createRouterUtilsInternal(client, options = {}, plugin) {
515
+ function createRouterUtilsInternal(client, options, plugin) {
441
516
  const path = toArray(options.path);
442
- const sharedUtils = bindMethods(new SharedRouterUtils(path));
443
- const procedureUtils = typeof client === "function" && (options.scoped === void 0 || isProcedureUtilsOptions(options.scoped)) ? bindMethods(new ProcedureUtils(
517
+ const utils = typeof client === "function" && (options.scoped === void 0 || isProcedureUtilsOptions(options.scoped)) ? bindMethods(new ProcedureUtils(
444
518
  path,
445
519
  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;
455
- const recursive = new Proxy({
456
- ...sharedUtils,
457
- ...procedureUtils
458
- }, {
520
+ plugin.initProcedureOptions(path, mergeProcedureUtilsOptions(
521
+ {
522
+ prefix: options.prefix,
523
+ queryInterceptors: options.queryInterceptors,
524
+ streamedInterceptors: options.streamedInterceptors,
525
+ liveInterceptors: options.liveInterceptors,
526
+ infiniteInterceptors: options.infiniteInterceptors,
527
+ mutationInterceptors: options.mutationInterceptors
528
+ },
529
+ options.scoped ?? {}
530
+ ))
531
+ ), { unbound: ["call"] }) : bindMethods(new SharedUtils(path, options));
532
+ const cache = /* @__PURE__ */ new Map();
533
+ const recursive = new Proxy(utils, {
459
534
  get(target, prop) {
460
- const value = getOrBind(target, prop);
461
- const nextClient = get(client, [prop]);
535
+ const value = Reflect.get(target, prop);
536
+ const nextClient = Reflect.get(client, prop);
462
537
  if (typeof prop !== "string" || RECURSIVE_CLIENT_UNWRAP_KEYS.has(prop) || !isTypescriptObject(nextClient)) {
463
538
  return value;
464
539
  }
465
- const nextUtils = createRouterUtilsInternal(nextClient, {
466
- ...options,
467
- path: [...path, prop],
468
- scoped: get(options.scoped, [prop])
469
- }, plugin);
470
- if (typeof value !== "function") {
471
- return nextUtils;
472
- }
473
- return new Proxy(value, {
474
- get(target2, prop2) {
475
- if (typeof prop2 !== "string" || RECURSIVE_CLIENT_UNWRAP_KEYS.has(prop2)) {
476
- return getOrBind(target2, prop2);
540
+ let result = cache.get(prop);
541
+ if (result === void 0) {
542
+ const nextUtils = createRouterUtilsInternal(nextClient, {
543
+ ...options,
544
+ path: [...path, prop],
545
+ scoped: options.scoped?.[prop]
546
+ }, plugin);
547
+ result = typeof value !== "function" ? nextUtils : new Proxy(value, {
548
+ get(target2, prop2) {
549
+ if (typeof prop2 !== "string" || RECURSIVE_CLIENT_UNWRAP_KEYS.has(prop2)) {
550
+ return Reflect.get(target2, prop2);
551
+ }
552
+ return Reflect.get(nextUtils, prop2);
477
553
  }
478
- return getOrBind(nextUtils, prop2);
479
- }
480
- });
554
+ });
555
+ cache.set(prop, result);
556
+ }
557
+ return result;
481
558
  }
482
559
  });
483
560
  return recursive;
484
561
  }
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
562
 
507
- function createContractUtilsFactory(caller, options) {
508
- return (procedure) => {
509
- const path = getPathMeta(procedure);
510
- if (!path) {
563
+ function createContractUtilsFactory(clientFactory, options) {
564
+ const factory = (contract) => {
565
+ const client = clientFactory(contract);
566
+ const path = resolveBasePathMeta(contract);
567
+ if (path === void 0) {
511
568
  throw new TypeError(
512
569
  "ContractUtilsFactory: procedure contract must define `meta.path` that matches its path in the root router contract."
513
570
  );
514
571
  }
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
- );
572
+ return createRouterUtils(client, { ...options, path, scoped: get(options.scoped, path) });
573
+ };
574
+ return factory;
575
+ }
576
+ function createContractJsonifiedUtilsFactory(clientFactory, options) {
577
+ return createContractUtilsFactory(clientFactory, options);
578
+ }
579
+
580
+ function tanstackQuery(options) {
581
+ return {
582
+ name: "~tanstack-query",
583
+ init(meta) {
584
+ const current = meta["~tanstack-query"];
585
+ return {
586
+ ...meta,
587
+ "~tanstack-query": current ? mergeProcedureUtilsOptions(current, options) : options
588
+ };
520
589
  }
521
- const client = (...rest) => caller(procedure, ...rest);
522
- return createRouterUtils(client, {
523
- ...options,
524
- scoped,
525
- path: [...toArray(options.path), ...path]
526
- });
527
590
  };
528
591
  }
529
- function createContractJsonifiedUtilsFactory(caller, options) {
530
- return createContractUtilsFactory(caller, options);
592
+ function getTanstackQueryMeta(procedureOrLazy) {
593
+ return procedureOrLazy["~orpc"].meta["~tanstack-query"];
594
+ }
595
+ class ContractOptionsUtilsPlugin {
596
+ constructor(contract) {
597
+ this.contract = contract;
598
+ }
599
+ name = "~contract-options";
600
+ initProcedureOptions(path, options) {
601
+ const procedure = getRouterContract(this.contract, path);
602
+ if (!(procedure instanceof ProcedureContract)) {
603
+ return options;
604
+ }
605
+ const base = getTanstackQueryMeta(procedure);
606
+ if (!base) {
607
+ return options;
608
+ }
609
+ return mergeProcedureUtilsOptions(base, options);
610
+ }
531
611
  }
532
612
 
533
- export { OPERATION_CONTEXT_SYMBOL, ProcedureUtils, SharedRouterUtils, OPERATION_CONTEXT_SYMBOL as TANSTACK_QUERY_OPERATION_CONTEXT_SYMBOL, createContractJsonifiedUtilsFactory, createContractUtilsFactory, createRouterUtils, createRouterUtils as createTanstackQueryUtils, generateOperationKey, serializableStreamedQuery };
613
+ export { CompositeRouterUtilsPlugin, ContractOptionsUtilsPlugin, OPERATION_CONTEXT_SYMBOL, ProcedureUtils, SharedUtils, OPERATION_CONTEXT_SYMBOL as TANSTACK_QUERY_OPERATION_CONTEXT_SYMBOL, createContractJsonifiedUtilsFactory, createContractUtilsFactory, createRouterUtils, createRouterUtils as createTanstackQueryUtils, generateOperationKey, getTanstackQueryMeta, isProcedureUtilsOptions, mergeProcedureUtilsOptions, serializableStreamedQuery, tanstackQuery };
package/package.json CHANGED
@@ -1,8 +1,13 @@
1
1
  {
2
2
  "name": "@orpc/tanstack-query",
3
3
  "type": "module",
4
- "version": "2.0.0-beta.3",
4
+ "version": "2.0.0-beta.30",
5
+ "description": "TanStack Query integration for oRPC: typesafe queries, mutations, streaming, and SSR for React, Vue, Solid, Svelte, and Angular",
5
6
  "license": "MIT",
7
+ "funding": [
8
+ "https://github.com/sponsors/dinwwwh",
9
+ "https://opencollective.com/middleapi"
10
+ ],
6
11
  "homepage": "https://orpc.dev",
7
12
  "repository": {
8
13
  "type": "git",
@@ -11,7 +16,17 @@
11
16
  },
12
17
  "keywords": [
13
18
  "orpc",
14
- "tanstack query"
19
+ "tanstack-query",
20
+ "react-query",
21
+ "vue-query",
22
+ "solid-query",
23
+ "svelte-query",
24
+ "angular-query",
25
+ "query",
26
+ "mutation",
27
+ "ssr",
28
+ "typescript",
29
+ "typesafe"
15
30
  ],
16
31
  "sideEffects": false,
17
32
  "exports": {
@@ -29,21 +44,21 @@
29
44
  "@tanstack/query-core": ">=5.80.2"
30
45
  },
31
46
  "dependencies": {
32
- "@orpc/client": "2.0.0-beta.3",
33
- "@orpc/contract": "2.0.0-beta.3",
34
- "@orpc/shared": "2.0.0-beta.3",
35
- "@orpc/openapi": "2.0.0-beta.3"
47
+ "@orpc/client": "2.0.0-beta.30",
48
+ "@orpc/contract": "2.0.0-beta.30",
49
+ "@orpc/openapi": "2.0.0-beta.30",
50
+ "@orpc/shared": "2.0.0-beta.30"
36
51
  },
37
52
  "devDependencies": {
38
- "@angular/core": "^22.0.1",
39
- "@tanstack/angular-query-experimental": "^5.101.0",
40
- "@tanstack/query-core": "^5.101.0",
41
- "@tanstack/react-query": "^5.101.0",
42
- "@tanstack/solid-query": "^5.101.0",
43
- "@tanstack/svelte-query": "^6.1.34",
44
- "@tanstack/vue-query": "^5.101.0",
45
- "svelte": "^5.56.3",
46
- "vue": "^3.5.38",
53
+ "@angular/core": "^22.1.3",
54
+ "@tanstack/angular-query-experimental": "^5.101.4",
55
+ "@tanstack/query-core": "^5.101.4",
56
+ "@tanstack/react-query": "^5.101.4",
57
+ "@tanstack/solid-query": "^5.101.4",
58
+ "@tanstack/svelte-query": "^6.1.38",
59
+ "@tanstack/vue-query": "^5.101.4",
60
+ "svelte": "^5.56.7",
61
+ "vue": "^3.5.41",
47
62
  "zod": "^4.4.3"
48
63
  },
49
64
  "scripts": {