orpc-fn 0.4.0 → 0.5.1

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.
Files changed (93) hide show
  1. package/README.md +146 -4
  2. package/dist/cli.js +37 -48
  3. package/dist/cli.js.map +1 -1
  4. package/dist/client.d.ts +29 -0
  5. package/dist/client.d.ts.map +1 -1
  6. package/dist/client.js +49 -0
  7. package/dist/client.js.map +1 -1
  8. package/dist/create-fn.d.ts.map +1 -1
  9. package/dist/create-fn.js +0 -1
  10. package/dist/create-fn.js.map +1 -1
  11. package/dist/hono.d.ts +12 -1
  12. package/dist/hono.d.ts.map +1 -1
  13. package/dist/hono.js +15 -1
  14. package/dist/hono.js.map +1 -1
  15. package/dist/live/fn-live.d.ts +1 -6
  16. package/dist/live/fn-live.d.ts.map +1 -1
  17. package/dist/live/fn-live.js +2 -18
  18. package/dist/live/fn-live.js.map +1 -1
  19. package/dist/live/index.d.ts +3 -1
  20. package/dist/live/index.d.ts.map +1 -1
  21. package/dist/live/index.js +2 -0
  22. package/dist/live/index.js.map +1 -1
  23. package/dist/live/invalidation-stream.d.ts +52 -0
  24. package/dist/live/invalidation-stream.d.ts.map +1 -0
  25. package/dist/live/invalidation-stream.js +98 -0
  26. package/dist/live/invalidation-stream.js.map +1 -0
  27. package/dist/live/ioredis.d.ts +1 -0
  28. package/dist/live/ioredis.d.ts.map +1 -1
  29. package/dist/live/ioredis.js +1 -0
  30. package/dist/live/ioredis.js.map +1 -1
  31. package/dist/live/memory.d.ts +2 -1
  32. package/dist/live/memory.d.ts.map +1 -1
  33. package/dist/live/memory.js +6 -1
  34. package/dist/live/memory.js.map +1 -1
  35. package/dist/live/pub-sub.d.ts +5 -0
  36. package/dist/live/pub-sub.d.ts.map +1 -1
  37. package/dist/live/pub-sub.js +16 -3
  38. package/dist/live/pub-sub.js.map +1 -1
  39. package/dist/live/redis-bun.d.ts +3 -0
  40. package/dist/live/redis-bun.d.ts.map +1 -1
  41. package/dist/live/redis-bun.js +1 -0
  42. package/dist/live/redis-bun.js.map +1 -1
  43. package/dist/live/redis.d.ts +4 -1
  44. package/dist/live/redis.d.ts.map +1 -1
  45. package/dist/live/redis.js +14 -2
  46. package/dist/live/redis.js.map +1 -1
  47. package/dist/live/scoped-change-bus.d.ts +45 -0
  48. package/dist/live/scoped-change-bus.d.ts.map +1 -0
  49. package/dist/live/scoped-change-bus.js +38 -0
  50. package/dist/live/scoped-change-bus.js.map +1 -0
  51. package/dist/live/transport.d.ts +11 -0
  52. package/dist/live/transport.d.ts.map +1 -1
  53. package/dist/live/transport.js.map +1 -1
  54. package/dist/procedure-cli.d.ts +25 -0
  55. package/dist/procedure-cli.d.ts.map +1 -0
  56. package/dist/procedure-cli.js +67 -0
  57. package/dist/procedure-cli.js.map +1 -0
  58. package/dist/react.d.ts +116 -0
  59. package/dist/react.d.ts.map +1 -0
  60. package/dist/react.js +156 -0
  61. package/dist/react.js.map +1 -0
  62. package/dist/realtime.d.ts +114 -0
  63. package/dist/realtime.d.ts.map +1 -0
  64. package/dist/realtime.js +316 -0
  65. package/dist/realtime.js.map +1 -0
  66. package/dist/vue/testing.d.ts +67 -0
  67. package/dist/vue/testing.d.ts.map +1 -0
  68. package/dist/vue/testing.js +225 -0
  69. package/dist/vue/testing.js.map +1 -0
  70. package/dist/vue.d.ts +211 -0
  71. package/dist/vue.d.ts.map +1 -0
  72. package/dist/vue.js +312 -0
  73. package/dist/vue.js.map +1 -0
  74. package/package.json +49 -3
  75. package/src/cli.ts +47 -57
  76. package/src/client.ts +57 -0
  77. package/src/create-fn.ts +0 -1
  78. package/src/hono.ts +22 -2
  79. package/src/live/fn-live.ts +3 -22
  80. package/src/live/index.ts +12 -1
  81. package/src/live/invalidation-stream.ts +142 -0
  82. package/src/live/ioredis.ts +2 -0
  83. package/src/live/memory.ts +11 -2
  84. package/src/live/pub-sub.ts +23 -2
  85. package/src/live/redis-bun.ts +3 -0
  86. package/src/live/redis.ts +33 -6
  87. package/src/live/scoped-change-bus.ts +98 -0
  88. package/src/live/transport.ts +12 -0
  89. package/src/procedure-cli.ts +99 -0
  90. package/src/react.ts +355 -0
  91. package/src/realtime.ts +416 -0
  92. package/src/vue/testing.ts +296 -0
  93. package/src/vue.ts +783 -0
package/dist/vue.js ADDED
@@ -0,0 +1,312 @@
1
+ import { focusManager, hashKey, keepPreviousData, useInfiniteQuery as useVueInfiniteQuery, useMutation as useVueMutation, useQuery as useVueQuery, useQueryClient } from '@tanstack/vue-query';
2
+ import { computed, getCurrentScope, hasInjectionContext, inject, onScopeDispose, ref, shallowRef, toValue, watch } from 'vue';
3
+ import { userFacingErrorMessage } from './client.js';
4
+ import { createInvalidationCoordinator, createStreamScope, createSubscriptionRunner, seedQueryFn } from './realtime.js';
5
+ const orpcKey = Symbol('orpc-fn');
6
+ /**
7
+ * Make an oRPC client and its TanStack Query utils the app's default. A
8
+ * surface that talks to another API (an embed with its own auth) passes
9
+ * `orpc` to each composable instead and never needs this.
10
+ */
11
+ export function provideOrpc(app, orpc) {
12
+ app.provide(orpcKey, orpc);
13
+ }
14
+ const missingOrpc = () => new Error('orpc-fn/vue: no oRPC client provided. Call provideOrpc(app, { client, utils }) or pass `orpc` to the composable.');
15
+ /** The client and utils from {@link provideOrpc}. */
16
+ export function useOrpc() {
17
+ const provided = hasInjectionContext() ? inject(orpcKey, null) : null;
18
+ if (!provided)
19
+ throw missingOrpc();
20
+ return provided;
21
+ }
22
+ /** Resolve the provided utils now; fail only when they are needed and missing. */
23
+ function useUtilsGetter(explicit) {
24
+ if (explicit)
25
+ return () => toValue(explicit);
26
+ const provided = hasInjectionContext() ? inject(orpcKey, null) : null;
27
+ return () => {
28
+ if (!provided)
29
+ throw missingOrpc();
30
+ return provided.utils;
31
+ };
32
+ }
33
+ let documentVisible = null;
34
+ /**
35
+ * Whether the page is visible, from TanStack's `focusManager` (which follows
36
+ * `visibilitychange` in browsers). Live streams pause while it is false.
37
+ */
38
+ function useDocumentVisible() {
39
+ if (!documentVisible) {
40
+ const visible = ref(focusManager.isFocused());
41
+ focusManager.subscribe((focused) => {
42
+ visible.value = focused;
43
+ });
44
+ documentVisible = visible;
45
+ }
46
+ return documentVisible;
47
+ }
48
+ function resolveQueryEnabled(enabled) {
49
+ if (enabled === undefined)
50
+ return true;
51
+ if (typeof enabled === 'function')
52
+ return Boolean(enabled());
53
+ return Boolean(toValue(enabled));
54
+ }
55
+ /**
56
+ * An oRPC event iterator as reactive state, with backoff reconnects and a
57
+ * pause while the page is hidden. Closed when the owning effect scope stops.
58
+ */
59
+ export function useOrpcSubscription(input, subscribeFn, options = {}) {
60
+ const { immediate = true, maxEvents = 100, autoReconnectOnInputChange = true, autoReconnectOnClose = true, respectVisibility = true } = options;
61
+ const getUtils = useUtilsGetter(options.orpc);
62
+ const visible = useDocumentVisible();
63
+ // May a stream run at all? This gates a manual connect() too.
64
+ const isStreamAllowed = computed(() => !respectVisibility || visible.value);
65
+ // Should it start on its own? That is what `immediate` means.
66
+ const shouldAutoConnect = computed(() => toValue(immediate) && isStreamAllowed.value);
67
+ // Shallow: event payloads are replaced, never mutated, so deep proxies are wasted work.
68
+ const events = shallowRef([]);
69
+ const latestEvent = shallowRef(undefined);
70
+ const status = ref('idle');
71
+ const error = ref(null);
72
+ const runner = createSubscriptionRunner({
73
+ open: (signal) => subscribeFn(getUtils())(toValue(input), { signal }),
74
+ canConnect: () => isStreamAllowed.value,
75
+ shouldReconnect: () => autoReconnectOnClose && shouldAutoConnect.value,
76
+ onStatus: (next) => {
77
+ status.value = next;
78
+ if (next === 'connecting')
79
+ error.value = null;
80
+ },
81
+ onEvent: (event, connectionId) => {
82
+ latestEvent.value = event;
83
+ if (maxEvents > 1) {
84
+ events.value = [...events.value.slice(-(maxEvents - 1)), event];
85
+ }
86
+ options.onEvent?.(event, { connectionId });
87
+ },
88
+ onError: (err) => {
89
+ error.value = err;
90
+ options.onError?.(err);
91
+ },
92
+ onConnect: options.onConnect,
93
+ onClose: options.onClose
94
+ });
95
+ function clearEvents() {
96
+ events.value = [];
97
+ latestEvent.value = undefined;
98
+ }
99
+ if (autoReconnectOnInputChange) {
100
+ watch(() => toValue(input), async () => {
101
+ const wasLive = runner.isLive();
102
+ if (runner.getStatus() !== 'idle') {
103
+ await runner.disconnect();
104
+ clearEvents();
105
+ }
106
+ // Reconnect in auto mode, or in manual mode when it was running.
107
+ if (shouldAutoConnect.value || (wasLive && isStreamAllowed.value)) {
108
+ await runner.start();
109
+ }
110
+ }, { deep: true });
111
+ }
112
+ // Visibility closes the stream in both modes; only the re-open waits for `immediate`.
113
+ watch(isStreamAllowed, (allowed) => {
114
+ if (!allowed)
115
+ void runner.disconnect();
116
+ else if (toValue(immediate)) {
117
+ error.value = null;
118
+ void runner.start();
119
+ }
120
+ }, { immediate: true });
121
+ watch(() => toValue(immediate), (auto) => {
122
+ if (!auto)
123
+ void runner.disconnect();
124
+ else if (isStreamAllowed.value) {
125
+ error.value = null;
126
+ void runner.start();
127
+ }
128
+ });
129
+ // A detached effect scope may own the stream, not just a component.
130
+ if (getCurrentScope())
131
+ onScopeDispose(() => void runner.dispose());
132
+ return {
133
+ events,
134
+ latestEvent,
135
+ status,
136
+ error,
137
+ isConnected: computed(() => status.value === 'connected'),
138
+ connect: runner.connect,
139
+ disconnect: runner.disconnect,
140
+ clearEvents
141
+ };
142
+ }
143
+ /**
144
+ * TanStack Query composables that receive the oRPC utils, so call sites read
145
+ * `useQuery((o) => o.order.get.queryOptions({ input }))`. Pass `orpc` to bind
146
+ * another client than the provided one.
147
+ */
148
+ export function useApiClient(clientOptions) {
149
+ const queryClient = useQueryClient();
150
+ const getO = useUtilsGetter(clientOptions?.orpc);
151
+ // Each wrapper forwards TanStack's own generics, so `select`, typed errors,
152
+ // page params and oRPC's tagged keys infer exactly as with vue-query itself.
153
+ function useQuery(optionsFn) {
154
+ // A computed is a ref at runtime; TypeScript cannot relate a computed of
155
+ // the generic options to vue-query's MaybeRef parameter, so it is asserted.
156
+ const options = computed(() => optionsFn(getO()));
157
+ return useVueQuery(options);
158
+ }
159
+ /** `useInfiniteQuery((o) => o.order.list.infiniteOptions({ input, initialPageParam, getNextPageParam }))` */
160
+ function useInfiniteQuery(optionsFn) {
161
+ const options = computed(() => optionsFn(getO()));
162
+ return useVueInfiniteQuery(options);
163
+ }
164
+ function useMutation(optionsFn, config) {
165
+ const mutation = useVueMutation(computed(() => optionsFn(getO())));
166
+ if (config?.initialInput === undefined)
167
+ return mutation;
168
+ const inputData = ref(config.initialInput);
169
+ return {
170
+ ...mutation,
171
+ inputData,
172
+ mutate: (variables) => mutation.mutate(variables ?? inputData.value),
173
+ mutateAsync: (variables) => mutation.mutateAsync(variables ?? inputData.value)
174
+ };
175
+ }
176
+ function useSubscription(input, subscribeFn, options = {}) {
177
+ return useOrpcSubscription(input, subscribeFn, {
178
+ ...options,
179
+ orpc: getO
180
+ });
181
+ }
182
+ const keyOf = (keyFn) => keyFn(getO());
183
+ return {
184
+ useQuery,
185
+ useInfiniteQuery,
186
+ useMutation,
187
+ useSubscription,
188
+ invalidate: (keyFn, filters) => queryClient.invalidateQueries({ queryKey: keyOf(keyFn), ...filters }),
189
+ /** Route preloading and hover-to-prefetch. */
190
+ prefetch: (optionsFn) => queryClient.prefetchQuery(optionsFn(getO())),
191
+ /** Data is typed from oRPC's tagged key; pass `<TData>` for an untagged one. */
192
+ getQueryData: (keyFn) => queryClient.getQueryData(keyOf(keyFn)),
193
+ /** Optimistic updates, typed like `getQueryData`. */
194
+ setQueryData: (keyFn, updater) => queryClient.setQueryData(keyOf(keyFn), updater),
195
+ cancelQueries: (keyFn) => queryClient.cancelQueries({ queryKey: keyOf(keyFn) }),
196
+ removeQueries: (keyFn) => queryClient.removeQueries({ queryKey: keyOf(keyFn) }),
197
+ isFetching: (keyFn) => queryClient.isFetching(keyFn ? { queryKey: keyOf(keyFn) } : {}),
198
+ queryClient
199
+ };
200
+ }
201
+ export function useLiveQuery(input, queryOptionsFn, subscribeFn, options = {}) {
202
+ const queryClient = useQueryClient();
203
+ const getO = useUtilsGetter(options.orpc);
204
+ const visible = useDocumentVisible();
205
+ // Without `applyEvent` the stream event is the cache type (the first overload).
206
+ const applyEvent = options.applyEvent ?? ((event) => event);
207
+ // Recomputed when the input changes, not once per stream event.
208
+ const queryKey = computed(() => queryOptionsFn(toValue(input), getO()).queryKey);
209
+ const queryHash = computed(() => hashKey(queryKey.value));
210
+ const scope = createStreamScope();
211
+ const subscription = useOrpcSubscription(computed(() => (options.subscriptionInput === undefined
212
+ ? toValue(input)
213
+ : toValue(options.subscriptionInput))), subscribeFn, {
214
+ immediate: options.immediate,
215
+ respectVisibility: options.respectVisibility,
216
+ onError: options.onError,
217
+ maxEvents: 1,
218
+ orpc: getO,
219
+ onConnect: (connectionId) => scope.open(connectionId, queryHash.value),
220
+ onEvent: (event, meta) => {
221
+ // The consumer callback must not see an event for a scope no longer shown.
222
+ if (!meta || !scope.owns(meta.connectionId, queryHash.value))
223
+ return;
224
+ const key = queryKey.value;
225
+ const next = applyEvent(event, queryClient.getQueryData(key));
226
+ if (next !== undefined)
227
+ queryClient.setQueryData(key, next);
228
+ options.onEvent?.(event, meta);
229
+ }
230
+ });
231
+ const query = useVueQuery(computed(() => {
232
+ const inputValue = toValue(input);
233
+ // oRPC query options are plain objects at runtime; vue-query's type
234
+ // wraps fields in MaybeRefDeep, so `queryFn` is read through one shape.
235
+ const raw = queryOptionsFn(inputValue, getO());
236
+ const wrapsSeed = options.select || options.mergeSeed;
237
+ const queryFn = raw.queryFn && wrapsSeed
238
+ ? seedQueryFn(raw.queryFn, {
239
+ input: inputValue,
240
+ readCache: () => queryClient.getQueryData(raw.queryKey),
241
+ select: options.select,
242
+ onSeed: options.onSeed,
243
+ mergeSeed: options.mergeSeed
244
+ })
245
+ : raw.queryFn;
246
+ return {
247
+ ...raw,
248
+ ...(queryFn ? { queryFn } : {}),
249
+ enabled: visible.value && resolveQueryEnabled(raw.enabled),
250
+ ...(options.keepPreviousData === false
251
+ ? {}
252
+ : { placeholderData: keepPreviousData }),
253
+ staleTime: Number.POSITIVE_INFINITY,
254
+ meta: { live: true }
255
+ };
256
+ }));
257
+ return Object.assign(query, { liveStatus: subscription.status });
258
+ }
259
+ /**
260
+ * Treat a stream as a "something changed" ping: invalidate `keyFn` on each
261
+ * event. Prefer {@link useLiveQuery} when the stream carries the data itself.
262
+ */
263
+ export function useSubscriptionInvalidates(input, subscribeFn, keyFn, options = {}) {
264
+ const { onEvent, shouldInvalidate, ...subscriptionOptions } = options;
265
+ const { useSubscription, invalidate } = useApiClient({
266
+ orpc: options.orpc
267
+ });
268
+ return useSubscription(input, subscribeFn, {
269
+ ...subscriptionOptions,
270
+ onEvent: (event) => {
271
+ onEvent?.(event);
272
+ if (shouldInvalidate?.(event) === false)
273
+ return;
274
+ void invalidate(keyFn);
275
+ }
276
+ });
277
+ }
278
+ /**
279
+ * One app-wide stream of `{ domain }` change events that keeps list and count
280
+ * queries fresh: each event invalidates its domain's query keys (debounced),
281
+ * and a reconnect that proves healthy invalidates every domain to cover
282
+ * events lost while it was down. Call once, in the signed-in shell.
283
+ *
284
+ * ```ts
285
+ * useInvalidationStream({}, (o) => o.realtime.subscribeChanges.call, {
286
+ * domains: { order: { debounceMs: 100, queryKeys: [orpc.order.key()] } }
287
+ * })
288
+ * ```
289
+ */
290
+ export function useInvalidationStream(input, subscribe, options) {
291
+ const coordinator = createInvalidationCoordinator(useQueryClient(), options);
292
+ if (getCurrentScope())
293
+ onScopeDispose(coordinator.dispose);
294
+ return useOrpcSubscription(input, subscribe, {
295
+ immediate: options.immediate,
296
+ respectVisibility: options.respectVisibility,
297
+ orpc: options.orpc,
298
+ maxEvents: 1,
299
+ onEvent: coordinator.event,
300
+ onConnect: coordinator.connect,
301
+ onError: coordinator.lost,
302
+ onClose: coordinator.lost
303
+ });
304
+ }
305
+ /** Localized text for a failed query: your fallback unless the server marked its message user-safe. */
306
+ export function useQueryErrorMessage(error, fallback) {
307
+ return computed(() => {
308
+ const err = toValue(error);
309
+ return err ? userFacingErrorMessage(err, toValue(fallback)) : null;
310
+ });
311
+ }
312
+ //# sourceMappingURL=vue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vue.js","sourceRoot":"","sources":["../src/vue.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\ttype DefaultError,\n\ttype FetchQueryOptions,\n\tfocusManager,\n\thashKey,\n\ttype InferDataFromTag,\n\ttype InfiniteData,\n\ttype InvalidateQueryFilters,\n\ttype MutationObserverOptions,\n\ttype UseInfiniteQueryOptions,\n\ttype UseInfiniteQueryReturnType,\n\ttype UseMutationReturnType,\n\ttype UseQueryReturnType,\n\ttype Updater,\n\tkeepPreviousData,\n\ttype QueryKey,\n\ttype UseQueryOptions,\n\tuseInfiniteQuery as useVueInfiniteQuery,\n\tuseMutation as useVueMutation,\n\tuseQuery as useVueQuery,\n\tuseQueryClient\n} from '@tanstack/vue-query'\nimport {\n\ttype App,\n\tcomputed,\n\tgetCurrentScope,\n\thasInjectionContext,\n\ttype InjectionKey,\n\tinject,\n\ttype MaybeRefOrGetter,\n\tonScopeDispose,\n\ttype Ref,\n\tref,\n\tshallowRef,\n\ttoValue,\n\twatch\n} from 'vue'\nimport { userFacingErrorMessage } from './client.js'\nimport {\n\tcreateInvalidationCoordinator,\n\tcreateStreamScope,\n\tcreateSubscriptionRunner,\n\ttype InvalidationStreamOptions,\n\ttype LiveConnectionStatus,\n\tseedQueryFn\n} from './realtime.js'\n\nexport type { InvalidationDomain, LiveConnectionStatus } from './realtime.js'\n\n/**\n * Register your client types once so every composable infers them:\n *\n * ```ts\n * declare module 'orpc-fn/vue' {\n * interface Register { utils: typeof orpc; client: typeof client }\n * }\n * ```\n */\n// biome-ignore lint/suspicious/noEmptyInterface: filled in by app module augmentation\nexport interface Register {}\n\nexport type RegisteredOrpcUtils = Register extends { utils: infer T }\n\t? T\n\t: {\n\t\t\t'orpc-fn/vue: register your utils with `declare module \"orpc-fn/vue\" { interface Register { utils: typeof orpc } }`, or pass `orpc`': never\n\t\t}\nexport type RegisteredOrpcClient = Register extends { client: infer T }\n\t? T\n\t: unknown\n\n/** An explicit `<TData>`, else the data type oRPC tagged on the key. */\ntype CachedData<TData, TKey extends QueryKey> = [TData] extends [never]\n\t? InferDataFromTag<unknown, TKey>\n\t: TData\n\ntype ProvidedOrpc = { client: unknown; utils: unknown }\nconst orpcKey: InjectionKey<ProvidedOrpc> = Symbol('orpc-fn')\n\n/**\n * Make an oRPC client and its TanStack Query utils the app's default. A\n * surface that talks to another API (an embed with its own auth) passes\n * `orpc` to each composable instead and never needs this.\n */\nexport function provideOrpc<TClient, TUtils>(\n\tapp: App,\n\torpc: { client: TClient; utils: TUtils }\n): void {\n\tapp.provide(orpcKey, orpc)\n}\n\nconst missingOrpc = () =>\n\tnew Error(\n\t\t'orpc-fn/vue: no oRPC client provided. Call provideOrpc(app, { client, utils }) or pass `orpc` to the composable.'\n\t)\n\n/** The client and utils from {@link provideOrpc}. */\nexport function useOrpc(): {\n\tclient: RegisteredOrpcClient\n\tutils: RegisteredOrpcUtils\n} {\n\tconst provided = hasInjectionContext() ? inject(orpcKey, null) : null\n\tif (!provided) throw missingOrpc()\n\treturn provided as {\n\t\tclient: RegisteredOrpcClient\n\t\tutils: RegisteredOrpcUtils\n\t}\n}\n\n/** Resolve the provided utils now; fail only when they are needed and missing. */\nfunction useUtilsGetter<TOrpc>(\n\texplicit: MaybeRefOrGetter<TOrpc> | undefined\n): () => TOrpc {\n\tif (explicit) return () => toValue(explicit)\n\tconst provided = hasInjectionContext() ? inject(orpcKey, null) : null\n\treturn () => {\n\t\tif (!provided) throw missingOrpc()\n\t\treturn provided.utils as TOrpc\n\t}\n}\n\nlet documentVisible: Ref<boolean> | null = null\n/**\n * Whether the page is visible, from TanStack's `focusManager` (which follows\n * `visibilitychange` in browsers). Live streams pause while it is false.\n */\nfunction useDocumentVisible(): Ref<boolean> {\n\tif (!documentVisible) {\n\t\tconst visible = ref(focusManager.isFocused())\n\t\tfocusManager.subscribe((focused) => {\n\t\t\tvisible.value = focused\n\t\t})\n\t\tdocumentVisible = visible\n\t}\n\treturn documentVisible\n}\n\nfunction resolveQueryEnabled(enabled: unknown): boolean {\n\tif (enabled === undefined) return true\n\tif (typeof enabled === 'function') return Boolean(enabled())\n\treturn Boolean(toValue(enabled))\n}\n\n/**\n * Picks the stream to open from the utils. A procedure's `call` fits as is:\n * `(o) => o.order.watch.call`. The composable passes the input and an abort signal.\n */\nexport type OrpcSubscribeFn<TInput, TEvent, TOrpc = RegisteredOrpcUtils> = (\n\torpc: TOrpc\n) => (\n\tinput: TInput,\n\toptions: { signal: AbortSignal }\n) => Promise<AsyncIterable<TEvent>>\n\nexport interface UseSubscriptionOptions<TEvent, TOrpc = RegisteredOrpcUtils> {\n\t/** Start the stream on its own (default: true). With false, call `connect()`. */\n\timmediate?: MaybeRefOrGetter<boolean> | undefined\n\tonEvent?: (event: TEvent, meta?: { connectionId: number }) => void\n\tonError?: ((error: Error) => void) | undefined\n\t/** Every successful connect; `connectionId > 1` is a reconnect. */\n\tonConnect?: (connectionId: number) => void\n\tonClose?: () => void\n\t/** Events kept in `events` (default: 100). 1 keeps only `latestEvent`. */\n\tmaxEvents?: number\n\t/** Reconnect when the input changes (default: true). */\n\tautoReconnectOnInputChange?: boolean\n\t/**\n\t * After a connect, reconnect with backoff when the stream ends or errors\n\t * unexpectedly (proxy idle timeout, network blip). Deliberate teardown\n\t * does not retry. Default: true.\n\t */\n\tautoReconnectOnClose?: boolean\n\torpc?: MaybeRefOrGetter<TOrpc> | undefined\n\t/**\n\t * Close the stream while the page is hidden (default: true), so a\n\t * background tab holds no server connection. It reconnects, and a live\n\t * route re-sends its snapshot, when the page is visible again.\n\t */\n\trespectVisibility?: boolean | undefined\n}\n\nexport interface UseSubscriptionReturn<TEvent> {\n\tevents: Ref<TEvent[]>\n\tlatestEvent: Ref<TEvent | undefined>\n\tstatus: Ref<LiveConnectionStatus>\n\terror: Ref<Error | null>\n\tisConnected: Ref<boolean>\n\tconnect: () => Promise<void>\n\tdisconnect: () => Promise<void>\n\tclearEvents: () => void\n}\n\n/**\n * An oRPC event iterator as reactive state, with backoff reconnects and a\n * pause while the page is hidden. Closed when the owning effect scope stops.\n */\nexport function useOrpcSubscription<\n\tTInput,\n\tTEvent,\n\tTOrpc = RegisteredOrpcUtils\n>(\n\tinput: MaybeRefOrGetter<TInput>,\n\tsubscribeFn: OrpcSubscribeFn<TInput, TEvent, TOrpc>,\n\toptions: UseSubscriptionOptions<TEvent, TOrpc> = {}\n): UseSubscriptionReturn<TEvent> {\n\tconst {\n\t\timmediate = true,\n\t\tmaxEvents = 100,\n\t\tautoReconnectOnInputChange = true,\n\t\tautoReconnectOnClose = true,\n\t\trespectVisibility = true\n\t} = options\n\tconst getUtils = useUtilsGetter(options.orpc)\n\tconst visible = useDocumentVisible()\n\n\t// May a stream run at all? This gates a manual connect() too.\n\tconst isStreamAllowed = computed(() => !respectVisibility || visible.value)\n\t// Should it start on its own? That is what `immediate` means.\n\tconst shouldAutoConnect = computed(\n\t\t() => toValue(immediate) && isStreamAllowed.value\n\t)\n\n\t// Shallow: event payloads are replaced, never mutated, so deep proxies are wasted work.\n\tconst events = shallowRef<TEvent[]>([])\n\tconst latestEvent = shallowRef<TEvent | undefined>(undefined)\n\tconst status = ref<LiveConnectionStatus>('idle')\n\tconst error = ref<Error | null>(null)\n\n\tconst runner = createSubscriptionRunner<TEvent>({\n\t\topen: (signal) => subscribeFn(getUtils())(toValue(input), { signal }),\n\t\tcanConnect: () => isStreamAllowed.value,\n\t\tshouldReconnect: () => autoReconnectOnClose && shouldAutoConnect.value,\n\t\tonStatus: (next) => {\n\t\t\tstatus.value = next\n\t\t\tif (next === 'connecting') error.value = null\n\t\t},\n\t\tonEvent: (event, connectionId) => {\n\t\t\tlatestEvent.value = event\n\t\t\tif (maxEvents > 1) {\n\t\t\t\tevents.value = [...events.value.slice(-(maxEvents - 1)), event]\n\t\t\t}\n\t\t\toptions.onEvent?.(event, { connectionId })\n\t\t},\n\t\tonError: (err) => {\n\t\t\terror.value = err\n\t\t\toptions.onError?.(err)\n\t\t},\n\t\tonConnect: options.onConnect,\n\t\tonClose: options.onClose\n\t})\n\n\tfunction clearEvents() {\n\t\tevents.value = []\n\t\tlatestEvent.value = undefined\n\t}\n\n\tif (autoReconnectOnInputChange) {\n\t\twatch(\n\t\t\t() => toValue(input),\n\t\t\tasync () => {\n\t\t\t\tconst wasLive = runner.isLive()\n\t\t\t\tif (runner.getStatus() !== 'idle') {\n\t\t\t\t\tawait runner.disconnect()\n\t\t\t\t\tclearEvents()\n\t\t\t\t}\n\t\t\t\t// Reconnect in auto mode, or in manual mode when it was running.\n\t\t\t\tif (shouldAutoConnect.value || (wasLive && isStreamAllowed.value)) {\n\t\t\t\t\tawait runner.start()\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ deep: true }\n\t\t)\n\t}\n\n\t// Visibility closes the stream in both modes; only the re-open waits for `immediate`.\n\twatch(\n\t\tisStreamAllowed,\n\t\t(allowed) => {\n\t\t\tif (!allowed) void runner.disconnect()\n\t\t\telse if (toValue(immediate)) {\n\t\t\t\terror.value = null\n\t\t\t\tvoid runner.start()\n\t\t\t}\n\t\t},\n\t\t{ immediate: true }\n\t)\n\n\twatch(\n\t\t() => toValue(immediate),\n\t\t(auto) => {\n\t\t\tif (!auto) void runner.disconnect()\n\t\t\telse if (isStreamAllowed.value) {\n\t\t\t\terror.value = null\n\t\t\t\tvoid runner.start()\n\t\t\t}\n\t\t}\n\t)\n\n\t// A detached effect scope may own the stream, not just a component.\n\tif (getCurrentScope()) onScopeDispose(() => void runner.dispose())\n\n\treturn {\n\t\tevents,\n\t\tlatestEvent,\n\t\tstatus,\n\t\terror,\n\t\tisConnected: computed(() => status.value === 'connected'),\n\t\tconnect: runner.connect,\n\t\tdisconnect: runner.disconnect,\n\t\tclearEvents\n\t}\n}\n\n/**\n * TanStack Query composables that receive the oRPC utils, so call sites read\n * `useQuery((o) => o.order.get.queryOptions({ input }))`. Pass `orpc` to bind\n * another client than the provided one.\n */\nexport function useApiClient<TOrpc = RegisteredOrpcUtils>(clientOptions?: {\n\torpc?: MaybeRefOrGetter<TOrpc> | undefined\n}) {\n\tconst queryClient = useQueryClient()\n\tconst getO = useUtilsGetter(clientOptions?.orpc)\n\n\t// Each wrapper forwards TanStack's own generics, so `select`, typed errors,\n\t// page params and oRPC's tagged keys infer exactly as with vue-query itself.\n\tfunction useQuery<\n\t\tTQueryFnData,\n\t\tTError = DefaultError,\n\t\tTData = TQueryFnData,\n\t\tTQueryKey extends QueryKey = QueryKey\n\t>(\n\t\toptionsFn: (\n\t\t\torpc: TOrpc\n\t\t) => UseQueryOptions<TQueryFnData, TError, TData, TQueryFnData, TQueryKey>\n\t): UseQueryReturnType<TData, TError> {\n\t\t// A computed is a ref at runtime; TypeScript cannot relate a computed of\n\t\t// the generic options to vue-query's MaybeRef parameter, so it is asserted.\n\t\tconst options = computed(() => optionsFn(getO()))\n\t\treturn useVueQuery<TQueryFnData, TError, TData, TQueryKey>(\n\t\t\toptions as unknown as UseQueryOptions<\n\t\t\t\tTQueryFnData,\n\t\t\t\tTError,\n\t\t\t\tTData,\n\t\t\t\tTQueryFnData,\n\t\t\t\tTQueryKey\n\t\t\t>\n\t\t)\n\t}\n\n\t/** `useInfiniteQuery((o) => o.order.list.infiniteOptions({ input, initialPageParam, getNextPageParam }))` */\n\tfunction useInfiniteQuery<\n\t\tTQueryFnData,\n\t\tTError = DefaultError,\n\t\tTData = InfiniteData<TQueryFnData>,\n\t\tTQueryKey extends QueryKey = QueryKey,\n\t\tTPageParam = unknown\n\t>(\n\t\toptionsFn: (\n\t\t\torpc: TOrpc\n\t\t) => UseInfiniteQueryOptions<\n\t\t\tTQueryFnData,\n\t\t\tTError,\n\t\t\tTData,\n\t\t\tTQueryKey,\n\t\t\tTPageParam\n\t\t>\n\t): UseInfiniteQueryReturnType<TData, TError> {\n\t\tconst options = computed(() => optionsFn(getO()))\n\t\treturn useVueInfiniteQuery<\n\t\t\tTQueryFnData,\n\t\t\tTError,\n\t\t\tTData,\n\t\t\tTQueryKey,\n\t\t\tTPageParam\n\t\t>(\n\t\t\toptions as unknown as UseInfiniteQueryOptions<\n\t\t\t\tTQueryFnData,\n\t\t\t\tTError,\n\t\t\t\tTData,\n\t\t\t\tTQueryKey,\n\t\t\t\tTPageParam\n\t\t\t>\n\t\t)\n\t}\n\n\t/**\n\t * With `initialInput`, also returns `inputData`, a ref that `mutate()` and\n\t * `mutateAsync()` use when called without variables (form state).\n\t */\n\tfunction useMutation<\n\t\tTData = unknown,\n\t\tTError = DefaultError,\n\t\tTVariables = void,\n\t\tTContext = unknown\n\t>(\n\t\toptionsFn: (\n\t\t\torpc: TOrpc\n\t\t) => MutationObserverOptions<TData, TError, TVariables, TContext>,\n\t\tconfig: { initialInput: TVariables }\n\t): UseMutationReturnType<TData, TError, TVariables, TContext> & {\n\t\tinputData: Ref<TVariables>\n\t\tmutate: (variables?: TVariables) => void\n\t\tmutateAsync: (variables?: TVariables) => Promise<TData>\n\t}\n\tfunction useMutation<\n\t\tTData = unknown,\n\t\tTError = DefaultError,\n\t\tTVariables = void,\n\t\tTContext = unknown\n\t>(\n\t\toptionsFn: (\n\t\t\torpc: TOrpc\n\t\t) => MutationObserverOptions<TData, TError, TVariables, TContext>\n\t): UseMutationReturnType<TData, TError, TVariables, TContext>\n\tfunction useMutation<TData, TError, TVariables, TContext>(\n\t\toptionsFn: (\n\t\t\torpc: TOrpc\n\t\t) => MutationObserverOptions<TData, TError, TVariables, TContext>,\n\t\tconfig?: { initialInput: TVariables }\n\t) {\n\t\tconst mutation = useVueMutation(computed(() => optionsFn(getO())))\n\t\tif (config?.initialInput === undefined) return mutation\n\t\tconst inputData = ref(config.initialInput) as Ref<TVariables>\n\t\treturn {\n\t\t\t...mutation,\n\t\t\tinputData,\n\t\t\tmutate: (variables?: TVariables) =>\n\t\t\t\tmutation.mutate(variables ?? inputData.value),\n\t\t\tmutateAsync: (variables?: TVariables) =>\n\t\t\t\tmutation.mutateAsync(variables ?? inputData.value)\n\t\t}\n\t}\n\n\tfunction useSubscription<TInput, TEvent>(\n\t\tinput: MaybeRefOrGetter<TInput>,\n\t\tsubscribeFn: OrpcSubscribeFn<TInput, TEvent, TOrpc>,\n\t\toptions: UseSubscriptionOptions<TEvent, TOrpc> = {}\n\t) {\n\t\treturn useOrpcSubscription<TInput, TEvent, TOrpc>(input, subscribeFn, {\n\t\t\t...options,\n\t\t\torpc: getO\n\t\t})\n\t}\n\n\tconst keyOf = <TKey extends QueryKey>(keyFn: (orpc: TOrpc) => TKey) =>\n\t\tkeyFn(getO())\n\n\treturn {\n\t\tuseQuery,\n\t\tuseInfiniteQuery,\n\t\tuseMutation,\n\t\tuseSubscription,\n\t\tinvalidate: (\n\t\t\tkeyFn: (orpc: TOrpc) => QueryKey,\n\t\t\tfilters?: Omit<InvalidateQueryFilters, 'queryKey'>\n\t\t) => queryClient.invalidateQueries({ queryKey: keyOf(keyFn), ...filters }),\n\t\t/** Route preloading and hover-to-prefetch. */\n\t\tprefetch: <\n\t\t\tTQueryFnData,\n\t\t\tTError = DefaultError,\n\t\t\tTData = TQueryFnData,\n\t\t\tTQueryKey extends QueryKey = QueryKey\n\t\t>(\n\t\t\toptionsFn: (\n\t\t\t\torpc: TOrpc\n\t\t\t) => FetchQueryOptions<TQueryFnData, TError, TData, TQueryKey>\n\t\t) => queryClient.prefetchQuery(optionsFn(getO())),\n\t\t/** Data is typed from oRPC's tagged key; pass `<TData>` for an untagged one. */\n\t\tgetQueryData: <TData = never, TKey extends QueryKey = QueryKey>(\n\t\t\tkeyFn: (orpc: TOrpc) => TKey\n\t\t): CachedData<TData, TKey> | undefined =>\n\t\t\tqueryClient.getQueryData<CachedData<TData, TKey>>(keyOf(keyFn)),\n\t\t/** Optimistic updates, typed like `getQueryData`. */\n\t\tsetQueryData: <TData = never, TKey extends QueryKey = QueryKey>(\n\t\t\tkeyFn: (orpc: TOrpc) => TKey,\n\t\t\tupdater: Updater<\n\t\t\t\tCachedData<TData, TKey> | undefined,\n\t\t\t\tCachedData<TData, TKey> | undefined\n\t\t\t>\n\t\t) =>\n\t\t\tqueryClient.setQueryData<CachedData<TData, TKey>>(keyOf(keyFn), updater),\n\t\tcancelQueries: (keyFn: (orpc: TOrpc) => QueryKey) =>\n\t\t\tqueryClient.cancelQueries({ queryKey: keyOf(keyFn) }),\n\t\tremoveQueries: (keyFn: (orpc: TOrpc) => QueryKey) =>\n\t\t\tqueryClient.removeQueries({ queryKey: keyOf(keyFn) }),\n\t\tisFetching: (keyFn?: (orpc: TOrpc) => QueryKey) =>\n\t\t\tqueryClient.isFetching(keyFn ? { queryKey: keyOf(keyFn) } : {}),\n\t\tqueryClient\n\t}\n}\n\ntype ApplyEvent<TStreamEvent, TData> = (\n\tevent: TStreamEvent,\n\tprevious: TData | undefined\n) => TData | undefined\n\ntype LiveQueryOptions<\n\tTInput,\n\tTRouteData,\n\tTStreamEvent,\n\tTData,\n\tTOrpc,\n\tTSubscriptionInput\n> = {\n\timmediate?: MaybeRefOrGetter<boolean>\n\torpc?: MaybeRefOrGetter<TOrpc>\n\t/** Input for the stream when it differs from the query's. */\n\tsubscriptionInput?: MaybeRefOrGetter<TSubscriptionInput>\n\t/**\n\t * Convert the route output once at fetch time into a differently typed\n\t * cache; `applyEvent` then reduces stream events into that cache.\n\t */\n\tselect?: (data: TRouteData) => TData\n\t/**\n\t * The raw route output of a seed before `select` drops fields. `input` is\n\t * what the seed was fetched for, so a seed that resolves after the input\n\t * moved on can be told apart.\n\t */\n\tonSeed?: (data: TRouteData, input: TInput) => void\n\t/**\n\t * Fold a stream event into the cached data. Required when the stream sends\n\t * something other than the cached type (an `fnLive` patch route, or a\n\t * `select`ed cache). Return `undefined` to skip the write.\n\t */\n\tapplyEvent?: ApplyEvent<TStreamEvent, NoInfer<TData>>\n\tonEvent?: (event: TStreamEvent, meta?: { connectionId: number }) => void\n\tonError?: (error: Error) => void\n\t/**\n\t * Reconcile a fetched seed with what the stream already wrote. The HTTP\n\t * request can resolve last, so a plain overwrite would drop events that\n\t * arrived while it was in flight. Default: take the fetched value.\n\t */\n\tmergeSeed?: (\n\t\tfetched: NoInfer<TData>,\n\t\tcached: NoInfer<TData> | undefined\n\t) => NoInfer<TData>\n\t/**\n\t * Show the previous key's data while a new key loads (default: true). Pass\n\t * false when the key says which entity is shown, not just how it is\n\t * filtered: another conversation's timeline must not stand in for this one.\n\t */\n\tkeepPreviousData?: boolean\n\t/** Pause the stream while the page is hidden (default: true). */\n\trespectVisibility?: boolean\n}\n\ntype LiveQueryReturn<TData, TError> = ReturnType<\n\ttypeof useVueQuery<TData, TError>\n> & { liveStatus: Ref<LiveConnectionStatus> }\n\n/**\n * The client half of `fnLive`: the HTTP query seeds the cache, and the live\n * route's stream writes each snapshot into the same cache entry. Marked\n * `meta.live`, so invalidation streams leave it alone.\n *\n * One signature, not overloads: TypeScript fixes an untyped callback's\n * parameters at the first overload it tries, which made valid calls fail.\n */\nexport function useLiveQuery<\n\tTInput,\n\tTRouteData,\n\tTStreamEvent,\n\tTData = TRouteData,\n\tTError = Error,\n\tTOrpc = RegisteredOrpcUtils,\n\tTSubscriptionInput = TInput\n>(\n\tinput: MaybeRefOrGetter<TInput>,\n\tqueryOptionsFn: (\n\t\tinput: TInput,\n\t\torpc: TOrpc\n\t) => UseQueryOptions<TRouteData, TError> & { queryKey: QueryKey },\n\tsubscribeFn: OrpcSubscribeFn<\n\t\tNoInfer<TSubscriptionInput>,\n\t\tTStreamEvent,\n\t\tTOrpc\n\t>,\n\t...options: [TStreamEvent] extends [TData]\n\t\t? [\n\t\t\t\toptions?: LiveQueryOptions<\n\t\t\t\t\tTInput,\n\t\t\t\t\tTRouteData,\n\t\t\t\t\tTStreamEvent,\n\t\t\t\t\tTData,\n\t\t\t\t\tTOrpc,\n\t\t\t\t\tTSubscriptionInput\n\t\t\t\t>\n\t\t\t]\n\t\t: [\n\t\t\t\toptions: LiveQueryOptions<\n\t\t\t\t\tTInput,\n\t\t\t\t\tTRouteData,\n\t\t\t\t\tTStreamEvent,\n\t\t\t\t\tTData,\n\t\t\t\t\tTOrpc,\n\t\t\t\t\tTSubscriptionInput\n\t\t\t\t> & { applyEvent: ApplyEvent<TStreamEvent, NoInfer<TData>> }\n\t\t\t]\n): LiveQueryReturn<TData, TError>\nexport function useLiveQuery<\n\tTInput,\n\tTRouteData,\n\tTStreamEvent,\n\tTData,\n\tTError,\n\tTOrpc,\n\tTSubscriptionInput\n>(\n\tinput: MaybeRefOrGetter<TInput>,\n\tqueryOptionsFn: (\n\t\tinput: TInput,\n\t\torpc: TOrpc\n\t) => UseQueryOptions<TRouteData, TError> & { queryKey: QueryKey },\n\tsubscribeFn: OrpcSubscribeFn<TSubscriptionInput, TStreamEvent, TOrpc>,\n\toptions: LiveQueryOptions<\n\t\tTInput,\n\t\tTRouteData,\n\t\tTStreamEvent,\n\t\tTData,\n\t\tTOrpc,\n\t\tTSubscriptionInput\n\t> = {}\n): LiveQueryReturn<TData, TError> {\n\tconst queryClient = useQueryClient()\n\tconst getO = useUtilsGetter(options.orpc)\n\tconst visible = useDocumentVisible()\n\t// Without `applyEvent` the stream event is the cache type (the first overload).\n\tconst applyEvent =\n\t\toptions.applyEvent ?? ((event: TStreamEvent) => event as unknown as TData)\n\t// Recomputed when the input changes, not once per stream event.\n\tconst queryKey = computed(\n\t\t() => queryOptionsFn(toValue(input), getO()).queryKey\n\t)\n\tconst queryHash = computed(() => hashKey(queryKey.value))\n\tconst scope = createStreamScope()\n\n\tconst subscription = useOrpcSubscription(\n\t\tcomputed(\n\t\t\t() =>\n\t\t\t\t(options.subscriptionInput === undefined\n\t\t\t\t\t? toValue(input)\n\t\t\t\t\t: toValue(options.subscriptionInput)) as TSubscriptionInput\n\t\t),\n\t\tsubscribeFn,\n\t\t{\n\t\t\timmediate: options.immediate,\n\t\t\trespectVisibility: options.respectVisibility,\n\t\t\tonError: options.onError,\n\t\t\tmaxEvents: 1,\n\t\t\torpc: getO,\n\t\t\tonConnect: (connectionId) => scope.open(connectionId, queryHash.value),\n\t\t\tonEvent: (event, meta) => {\n\t\t\t\t// The consumer callback must not see an event for a scope no longer shown.\n\t\t\t\tif (!meta || !scope.owns(meta.connectionId, queryHash.value)) return\n\t\t\t\tconst key = queryKey.value\n\t\t\t\tconst next = applyEvent(event, queryClient.getQueryData<TData>(key))\n\t\t\t\tif (next !== undefined) queryClient.setQueryData(key, next)\n\t\t\t\toptions.onEvent?.(event, meta)\n\t\t\t}\n\t\t}\n\t)\n\n\tconst query = useVueQuery<TData, TError>(\n\t\tcomputed(() => {\n\t\t\tconst inputValue = toValue(input)\n\t\t\t// oRPC query options are plain objects at runtime; vue-query's type\n\t\t\t// wraps fields in MaybeRefDeep, so `queryFn` is read through one shape.\n\t\t\tconst raw = queryOptionsFn(inputValue, getO()) as unknown as {\n\t\t\t\tqueryKey: QueryKey\n\t\t\t\tqueryFn?: (context: unknown) => Promise<TRouteData>\n\t\t\t\tenabled?: unknown\n\t\t\t} & Record<string, unknown>\n\t\t\tconst wrapsSeed = options.select || options.mergeSeed\n\t\t\tconst queryFn =\n\t\t\t\traw.queryFn && wrapsSeed\n\t\t\t\t\t? seedQueryFn(raw.queryFn, {\n\t\t\t\t\t\t\tinput: inputValue,\n\t\t\t\t\t\t\treadCache: () => queryClient.getQueryData<TData>(raw.queryKey),\n\t\t\t\t\t\t\tselect: options.select,\n\t\t\t\t\t\t\tonSeed: options.onSeed,\n\t\t\t\t\t\t\tmergeSeed: options.mergeSeed\n\t\t\t\t\t\t})\n\t\t\t\t\t: raw.queryFn\n\t\t\treturn {\n\t\t\t\t...raw,\n\t\t\t\t...(queryFn ? { queryFn } : {}),\n\t\t\t\tenabled: visible.value && resolveQueryEnabled(raw.enabled),\n\t\t\t\t...(options.keepPreviousData === false\n\t\t\t\t\t? {}\n\t\t\t\t\t: { placeholderData: keepPreviousData }),\n\t\t\t\tstaleTime: Number.POSITIVE_INFINITY,\n\t\t\t\tmeta: { live: true }\n\t\t\t} as unknown as UseQueryOptions<TData, TError>\n\t\t})\n\t)\n\n\treturn Object.assign(query, { liveStatus: subscription.status })\n}\n\n/**\n * Treat a stream as a \"something changed\" ping: invalidate `keyFn` on each\n * event. Prefer {@link useLiveQuery} when the stream carries the data itself.\n */\nexport function useSubscriptionInvalidates<\n\tTInput,\n\tTEvent,\n\tTOrpc = RegisteredOrpcUtils\n>(\n\tinput: MaybeRefOrGetter<TInput>,\n\tsubscribeFn: OrpcSubscribeFn<TInput, TEvent, TOrpc>,\n\tkeyFn: (orpc: TOrpc) => QueryKey,\n\toptions: Pick<\n\t\tUseSubscriptionOptions<TEvent, TOrpc>,\n\t\t'immediate' | 'maxEvents' | 'respectVisibility' | 'orpc'\n\t> & {\n\t\tonEvent?: (event: TEvent) => void\n\t\t/** Return false to skip invalidation for this event. */\n\t\tshouldInvalidate?: (event: TEvent) => boolean\n\t} = {}\n): UseSubscriptionReturn<TEvent> {\n\tconst { onEvent, shouldInvalidate, ...subscriptionOptions } = options\n\tconst { useSubscription, invalidate } = useApiClient<TOrpc>({\n\t\torpc: options.orpc\n\t})\n\treturn useSubscription(input, subscribeFn, {\n\t\t...subscriptionOptions,\n\t\tonEvent: (event) => {\n\t\t\tonEvent?.(event)\n\t\t\tif (shouldInvalidate?.(event) === false) return\n\t\t\tvoid invalidate(keyFn)\n\t\t}\n\t})\n}\n\n/**\n * One app-wide stream of `{ domain }` change events that keeps list and count\n * queries fresh: each event invalidates its domain's query keys (debounced),\n * and a reconnect that proves healthy invalidates every domain to cover\n * events lost while it was down. Call once, in the signed-in shell.\n *\n * ```ts\n * useInvalidationStream({}, (o) => o.realtime.subscribeChanges.call, {\n * domains: { order: { debounceMs: 100, queryKeys: [orpc.order.key()] } }\n * })\n * ```\n */\nexport function useInvalidationStream<\n\tTInput,\n\tTEvent extends { domain: string },\n\tTOrpc = RegisteredOrpcUtils\n>(\n\tinput: MaybeRefOrGetter<TInput>,\n\tsubscribe: OrpcSubscribeFn<TInput, TEvent, TOrpc>,\n\toptions: InvalidationStreamOptions<TEvent> &\n\t\tPick<\n\t\t\tUseSubscriptionOptions<TEvent, TOrpc>,\n\t\t\t'immediate' | 'respectVisibility' | 'orpc'\n\t\t>\n): UseSubscriptionReturn<TEvent> {\n\tconst coordinator = createInvalidationCoordinator(useQueryClient(), options)\n\tif (getCurrentScope()) onScopeDispose(coordinator.dispose)\n\treturn useOrpcSubscription<TInput, TEvent, TOrpc>(input, subscribe, {\n\t\timmediate: options.immediate,\n\t\trespectVisibility: options.respectVisibility,\n\t\torpc: options.orpc,\n\t\tmaxEvents: 1,\n\t\tonEvent: coordinator.event,\n\t\tonConnect: coordinator.connect,\n\t\tonError: coordinator.lost,\n\t\tonClose: coordinator.lost\n\t})\n}\n\n/** Localized text for a failed query: your fallback unless the server marked its message user-safe. */\nexport function useQueryErrorMessage(\n\terror: MaybeRefOrGetter<unknown>,\n\tfallback: MaybeRefOrGetter<string>\n) {\n\treturn computed(() => {\n\t\tconst err = toValue(error)\n\t\treturn err ? userFacingErrorMessage(err, toValue(fallback)) : null\n\t})\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orpc-fn",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "Define a backend function once: a typed oRPC procedure with tracing, logging, guards, injected handler extras, nested calls and live queries over Redis.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -21,11 +21,14 @@
21
21
  "live-query",
22
22
  "mastra",
23
23
  "mcp",
24
+ "cli",
24
25
  "hono",
25
26
  "typescript",
26
27
  "tanstack-query",
27
28
  "expo",
28
- "react-native"
29
+ "react-native",
30
+ "vue",
31
+ "react"
29
32
  ],
30
33
  "engines": {
31
34
  "node": ">=20"
@@ -71,6 +74,18 @@
71
74
  "types": "./dist/expo.d.ts",
72
75
  "import": "./dist/expo.js"
73
76
  },
77
+ "./vue": {
78
+ "types": "./dist/vue.d.ts",
79
+ "import": "./dist/vue.js"
80
+ },
81
+ "./vue/testing": {
82
+ "types": "./dist/vue/testing.d.ts",
83
+ "import": "./dist/vue/testing.js"
84
+ },
85
+ "./react": {
86
+ "types": "./dist/react.d.ts",
87
+ "import": "./dist/react.js"
88
+ },
74
89
  "./mcp/sdk": {
75
90
  "types": "./dist/mcp/sdk.d.ts",
76
91
  "import": "./dist/mcp/sdk.js"
@@ -78,6 +93,10 @@
78
93
  "./mcp/server": {
79
94
  "types": "./dist/mcp/server.d.ts",
80
95
  "import": "./dist/mcp/server.js"
96
+ },
97
+ "./cli": {
98
+ "types": "./dist/procedure-cli.d.ts",
99
+ "import": "./dist/procedure-cli.js"
81
100
  }
82
101
  },
83
102
  "types": "./dist/index.d.ts",
@@ -108,6 +127,9 @@
108
127
  "test:types-performance": "bun scripts/check-types-performance.ts",
109
128
  "test:client-bundle": "bun scripts/check-client-bundle.ts"
110
129
  },
130
+ "dependencies": {
131
+ "hanzio": "^3.1.0"
132
+ },
111
133
  "peerDependencies": {
112
134
  "@mastra/core": "^1.51.0",
113
135
  "@opentelemetry/api": "^1.9.0",
@@ -121,7 +143,11 @@
121
143
  "ioredis": "^5.0.0",
122
144
  "zod": "^4.0.0",
123
145
  "@modelcontextprotocol/sdk": "^1.32.0",
124
- "@modelcontextprotocol/server": "^2.0.0"
146
+ "@modelcontextprotocol/server": "^2.0.0",
147
+ "@tanstack/react-query": "^5.80.0",
148
+ "@tanstack/vue-query": "^5.80.0",
149
+ "react": "^18.0.0 || ^19.0.0",
150
+ "vue": "^3.3.0"
125
151
  },
126
152
  "peerDependenciesMeta": {
127
153
  "@mastra/core": {
@@ -150,6 +176,18 @@
150
176
  },
151
177
  "@modelcontextprotocol/server": {
152
178
  "optional": true
179
+ },
180
+ "@tanstack/react-query": {
181
+ "optional": true
182
+ },
183
+ "@tanstack/vue-query": {
184
+ "optional": true
185
+ },
186
+ "react": {
187
+ "optional": true
188
+ },
189
+ "vue": {
190
+ "optional": true
153
191
  }
154
192
  },
155
193
  "devDependencies": {
@@ -165,13 +203,21 @@
165
203
  "@orpc/json-schema": "1.14.8",
166
204
  "@orpc/openapi": "1.14.8",
167
205
  "@orpc/server": "1.14.8",
206
+ "@orpc/tanstack-query": "1.14.8",
168
207
  "@orpc/zod": "1.14.8",
208
+ "@tanstack/react-query": "5.101.2",
209
+ "@tanstack/vue-query": "5.101.2",
169
210
  "@types/bun": "^1.2.0",
170
211
  "@types/node": "^22.0.0",
212
+ "@types/react": "19",
213
+ "@types/react-test-renderer": "19",
171
214
  "hono": "^4.12.31",
172
215
  "ioredis": "5.8.2",
173
216
  "publint": "^0.3.12",
217
+ "react": "19.2.3",
218
+ "react-test-renderer": "19.2.3",
174
219
  "typescript": "^7.0.2",
220
+ "vue": "3.5.40",
175
221
  "zod": "4.4.3"
176
222
  },
177
223
  "bugs": {
package/src/cli.ts CHANGED
@@ -1,68 +1,58 @@
1
1
  #!/usr/bin/env node
2
- // biome-ignore-all lint/suspicious/noConsole: a CLI reports on the console
3
2
  import { readFile, writeFile } from 'node:fs/promises'
4
3
  import { resolve } from 'node:path'
5
4
  import { pathToFileURL } from 'node:url'
6
- import { parseArgs } from 'node:util'
7
5
  import type { AnyRouter } from '@orpc/server'
6
+ import { CliError, defineCli, defineCommand } from 'hanzio/cli'
7
+ import { z } from 'zod'
8
8
  import { createStreamManifestAsync, renderStreamManifest } from './router.js'
9
9
 
10
- const usage = `Usage: orpc-fn stream-manifest <module>[#export] --out <file.ts|file.json> [--name streamPaths] [--check]
11
-
12
- Imports the router (export "default" unless #export is given) and writes the
13
- paths of its streaming routes for createRpcLink({ streamPaths }). The file is
14
- only written when its content changes. --check writes nothing and exits 1 when
15
- the file is missing or stale. Importing the router runs its module code; run
16
- TypeScript routers with a runtime that loads .ts (bun, tsx, node >= 22.18).`
17
-
18
- async function run(argv: readonly string[]): Promise<number> {
19
- const { positionals, values } = parseArgs({
20
- args: [...argv],
21
- allowPositionals: true,
22
- options: {
23
- out: { type: 'string' },
24
- name: { type: 'string' },
25
- check: { type: 'boolean' },
26
- help: { type: 'boolean', short: 'h' }
27
- }
28
- })
29
- const [command, target] = positionals
30
- if (values.help || command !== 'stream-manifest' || !target || !values.out) {
31
- console.error(usage)
32
- return values.help ? 0 : 2
33
- }
34
- const [modulePath = '', exportName = 'default'] = target.split('#')
35
- const loaded: Record<string, unknown> = await import(
36
- pathToFileURL(resolve(modulePath)).href
37
- )
38
- const router = loaded[exportName]
39
- if (!router || typeof router !== 'object') {
40
- console.error(`orpc-fn: ${modulePath} has no router export "${exportName}"`)
41
- return 2
42
- }
43
- const out = resolve(values.out)
44
- const source = renderStreamManifest(
45
- await createStreamManifestAsync(router as AnyRouter),
46
- { format: out.endsWith('.json') ? 'json' : 'ts', name: values.name }
47
- )
48
- const current = await readFile(out, 'utf8').catch(() => undefined)
49
- if (current === source) return 0
50
- if (values.check) {
51
- console.error(
52
- `orpc-fn: ${values.out} is stale; rerun without --check to update it`
10
+ const streamManifest = defineCommand({
11
+ description:
12
+ "Write the paths of a router's streaming routes for createRpcLink({ streamPaths }), only when they change. Importing the router runs its module code: load .ts routers with bun, tsx or node >= 22.18.",
13
+ input: z.object({
14
+ module: z
15
+ .string()
16
+ .describe('Router module, with #export for a named export')
17
+ .meta({ positional: true }),
18
+ out: z.string().describe('Manifest to write: file.ts or file.json'),
19
+ name: z
20
+ .string()
21
+ .optional()
22
+ .describe('Exported constant name in a .ts file'),
23
+ check: z.boolean().describe('Exit 1 when the file is missing or stale')
24
+ }),
25
+ run: async (input) => {
26
+ const [modulePath = '', exportName = 'default'] = input.module.split('#')
27
+ const loaded: Record<string, unknown> = await import(
28
+ pathToFileURL(resolve(modulePath)).href
53
29
  )
54
- return 1
30
+ const router = loaded[exportName]
31
+ if (!router || typeof router !== 'object')
32
+ throw new CliError(
33
+ `orpc-fn: ${modulePath} has no router export "${exportName}"`,
34
+ { exitCode: 2 }
35
+ )
36
+ const out = resolve(input.out)
37
+ const source = renderStreamManifest(
38
+ await createStreamManifestAsync(router as AnyRouter),
39
+ { format: out.endsWith('.json') ? 'json' : 'ts', name: input.name }
40
+ )
41
+ const current = await readFile(out, 'utf8').catch(() => undefined)
42
+ if (current === source) return
43
+ if (input.check)
44
+ throw new CliError(
45
+ `orpc-fn: ${input.out} is stale; rerun without --check to update it`
46
+ )
47
+ await writeFile(out, source)
48
+ return `orpc-fn: wrote ${input.out}`
55
49
  }
56
- await writeFile(out, source)
57
- console.log(`orpc-fn: wrote ${values.out}`)
58
- return 0
59
- }
50
+ })
51
+
52
+ const cli = defineCli({
53
+ name: 'orpc-fn',
54
+ commands: { 'stream-manifest': streamManifest }
55
+ })
60
56
 
61
57
  // Exit explicitly: the imported router may hold open database or broker handles.
62
- run(process.argv.slice(2)).then(
63
- (code) => process.exit(code),
64
- (error) => {
65
- console.error(error)
66
- process.exit(1)
67
- }
68
- )
58
+ process.exit(await cli.run())