@ic-reactor/react 3.12.5 → 4.0.0-beta.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 (139) hide show
  1. package/README.md +236 -423
  2. package/dist/index.d.ts +259 -12
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +470 -17
  5. package/dist/index.js.map +1 -1
  6. package/llms.txt +82 -54
  7. package/package.json +12 -28
  8. package/src/index.tsx +611 -0
  9. package/dist/auth/authentication-manager.d.ts +0 -123
  10. package/dist/auth/authentication-manager.d.ts.map +0 -1
  11. package/dist/auth/authentication-manager.js +0 -767
  12. package/dist/auth/authentication-manager.js.map +0 -1
  13. package/dist/auth/constants.d.ts +0 -24
  14. package/dist/auth/constants.d.ts.map +0 -1
  15. package/dist/auth/constants.js +0 -24
  16. package/dist/auth/constants.js.map +0 -1
  17. package/dist/auth/createIdentityAttributeHooks.d.ts +0 -14
  18. package/dist/auth/createIdentityAttributeHooks.d.ts.map +0 -1
  19. package/dist/auth/createIdentityAttributeHooks.js +0 -106
  20. package/dist/auth/createIdentityAttributeHooks.js.map +0 -1
  21. package/dist/auth/identity-attributes-manager.d.ts +0 -26
  22. package/dist/auth/identity-attributes-manager.d.ts.map +0 -1
  23. package/dist/auth/identity-attributes-manager.js +0 -107
  24. package/dist/auth/identity-attributes-manager.js.map +0 -1
  25. package/dist/auth/identity-attributes.d.ts +0 -19
  26. package/dist/auth/identity-attributes.d.ts.map +0 -1
  27. package/dist/auth/identity-attributes.js +0 -170
  28. package/dist/auth/identity-attributes.js.map +0 -1
  29. package/dist/auth/index.d.ts +0 -8
  30. package/dist/auth/index.d.ts.map +0 -1
  31. package/dist/auth/index.js +0 -8
  32. package/dist/auth/index.js.map +0 -1
  33. package/dist/auth/local-ii-probe.d.ts +0 -46
  34. package/dist/auth/local-ii-probe.d.ts.map +0 -1
  35. package/dist/auth/local-ii-probe.js +0 -102
  36. package/dist/auth/local-ii-probe.js.map +0 -1
  37. package/dist/auth/types.d.ts +0 -179
  38. package/dist/auth/types.d.ts.map +0 -1
  39. package/dist/auth/types.js +0 -2
  40. package/dist/auth/types.js.map +0 -1
  41. package/dist/createActorHooks.d.ts +0 -52
  42. package/dist/createActorHooks.d.ts.map +0 -1
  43. package/dist/createActorHooks.js +0 -17
  44. package/dist/createActorHooks.js.map +0 -1
  45. package/dist/createInfiniteQuery.d.ts +0 -144
  46. package/dist/createInfiniteQuery.d.ts.map +0 -1
  47. package/dist/createInfiniteQuery.js +0 -174
  48. package/dist/createInfiniteQuery.js.map +0 -1
  49. package/dist/createMutation.d.ts +0 -30
  50. package/dist/createMutation.d.ts.map +0 -1
  51. package/dist/createMutation.js +0 -192
  52. package/dist/createMutation.js.map +0 -1
  53. package/dist/createQuery.d.ts +0 -30
  54. package/dist/createQuery.d.ts.map +0 -1
  55. package/dist/createQuery.js +0 -117
  56. package/dist/createQuery.js.map +0 -1
  57. package/dist/createSuspenseInfiniteQuery.d.ts +0 -147
  58. package/dist/createSuspenseInfiniteQuery.d.ts.map +0 -1
  59. package/dist/createSuspenseInfiniteQuery.js +0 -177
  60. package/dist/createSuspenseInfiniteQuery.js.map +0 -1
  61. package/dist/createSuspenseQuery.d.ts +0 -25
  62. package/dist/createSuspenseQuery.d.ts.map +0 -1
  63. package/dist/createSuspenseQuery.js +0 -111
  64. package/dist/createSuspenseQuery.js.map +0 -1
  65. package/dist/defineReactor.d.ts +0 -125
  66. package/dist/defineReactor.d.ts.map +0 -1
  67. package/dist/defineReactor.js +0 -180
  68. package/dist/defineReactor.js.map +0 -1
  69. package/dist/hooks/createAuthHooks.d.ts +0 -43
  70. package/dist/hooks/createAuthHooks.d.ts.map +0 -1
  71. package/dist/hooks/createAuthHooks.js +0 -131
  72. package/dist/hooks/createAuthHooks.js.map +0 -1
  73. package/dist/hooks/index.d.ts +0 -21
  74. package/dist/hooks/index.d.ts.map +0 -1
  75. package/dist/hooks/index.js +0 -24
  76. package/dist/hooks/index.js.map +0 -1
  77. package/dist/hooks/useActorInfiniteQuery.d.ts +0 -39
  78. package/dist/hooks/useActorInfiniteQuery.d.ts.map +0 -1
  79. package/dist/hooks/useActorInfiniteQuery.js +0 -56
  80. package/dist/hooks/useActorInfiniteQuery.js.map +0 -1
  81. package/dist/hooks/useActorMethod.d.ts +0 -115
  82. package/dist/hooks/useActorMethod.d.ts.map +0 -1
  83. package/dist/hooks/useActorMethod.js +0 -250
  84. package/dist/hooks/useActorMethod.js.map +0 -1
  85. package/dist/hooks/useActorMutation.d.ts +0 -48
  86. package/dist/hooks/useActorMutation.d.ts.map +0 -1
  87. package/dist/hooks/useActorMutation.js +0 -69
  88. package/dist/hooks/useActorMutation.js.map +0 -1
  89. package/dist/hooks/useActorQuery.d.ts +0 -32
  90. package/dist/hooks/useActorQuery.d.ts.map +0 -1
  91. package/dist/hooks/useActorQuery.js +0 -46
  92. package/dist/hooks/useActorQuery.js.map +0 -1
  93. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +0 -39
  94. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +0 -1
  95. package/dist/hooks/useActorSuspenseInfiniteQuery.js +0 -56
  96. package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +0 -1
  97. package/dist/hooks/useActorSuspenseQuery.d.ts +0 -32
  98. package/dist/hooks/useActorSuspenseQuery.d.ts.map +0 -1
  99. package/dist/hooks/useActorSuspenseQuery.js +0 -47
  100. package/dist/hooks/useActorSuspenseQuery.js.map +0 -1
  101. package/dist/types.d.ts +0 -270
  102. package/dist/types.d.ts.map +0 -1
  103. package/dist/types.js +0 -5
  104. package/dist/types.js.map +0 -1
  105. package/dist/utils.d.ts +0 -51
  106. package/dist/utils.d.ts.map +0 -1
  107. package/dist/utils.js +0 -105
  108. package/dist/utils.js.map +0 -1
  109. package/dist/validation.d.ts +0 -131
  110. package/dist/validation.d.ts.map +0 -1
  111. package/dist/validation.js +0 -125
  112. package/dist/validation.js.map +0 -1
  113. package/src/auth/authentication-manager.ts +0 -860
  114. package/src/auth/constants.ts +0 -32
  115. package/src/auth/createIdentityAttributeHooks.ts +0 -143
  116. package/src/auth/identity-attributes-manager.ts +0 -131
  117. package/src/auth/identity-attributes.ts +0 -270
  118. package/src/auth/index.ts +0 -7
  119. package/src/auth/local-ii-probe.ts +0 -147
  120. package/src/auth/types.ts +0 -200
  121. package/src/createActorHooks.ts +0 -220
  122. package/src/createInfiniteQuery.ts +0 -578
  123. package/src/createMutation.ts +0 -341
  124. package/src/createQuery.ts +0 -237
  125. package/src/createSuspenseInfiniteQuery.ts +0 -601
  126. package/src/createSuspenseQuery.ts +0 -234
  127. package/src/defineReactor.ts +0 -324
  128. package/src/hooks/createAuthHooks.ts +0 -189
  129. package/src/hooks/index.ts +0 -103
  130. package/src/hooks/useActorInfiniteQuery.ts +0 -205
  131. package/src/hooks/useActorMethod.ts +0 -507
  132. package/src/hooks/useActorMutation.ts +0 -205
  133. package/src/hooks/useActorQuery.ts +0 -124
  134. package/src/hooks/useActorSuspenseInfiniteQuery.ts +0 -205
  135. package/src/hooks/useActorSuspenseQuery.ts +0 -132
  136. package/src/index.ts +0 -23
  137. package/src/types.ts +0 -499
  138. package/src/utils.ts +0 -121
  139. package/src/validation.ts +0 -202
@@ -1,341 +0,0 @@
1
- /**
2
- * Mutation Factory - Generic wrapper for mutating canister data
3
- *
4
- * Creates unified mutation hooks for any canister method.
5
- * Works with any Reactor instance.
6
- * Use this when one mutation should support both React hooks and imperative
7
- * execution outside React.
8
- *
9
- * @example
10
- * const transferMutation = createMutation(reactor, {
11
- * functionName: "transfer",
12
- * onSuccess: () => console.log("Success!"),
13
- * })
14
- *
15
- * // In component
16
- * const { mutate, isPending } = transferMutation.useMutation()
17
- *
18
- * @example
19
- * const transferMutation = createMutation(reactor, {
20
- * functionName: "transfer",
21
- * onCanisterError: (err) => console.error(err.code),
22
- * })
23
- *
24
- * // Outside React (loader/service/script)
25
- * await transferMutation.execute([{ to: "aaaaa-aa", amount: "1000" }])
26
- */
27
-
28
- import {
29
- useMutation,
30
- type MutationFunctionContext,
31
- } from "@tanstack/react-query"
32
- import type {
33
- Reactor,
34
- FunctionName,
35
- ReactorArgs,
36
- TransformKey,
37
- ReactorReturnOk,
38
- ReactorReturnErr,
39
- } from "@ic-reactor/core"
40
- import { isCanisterError } from "@ic-reactor/core"
41
- import type {
42
- MutationConfig,
43
- MutationResult,
44
- MutationHookOptions,
45
- NoInfer,
46
- } from "./types.js"
47
-
48
- // ============================================================================
49
- // Internal helpers
50
- // ============================================================================
51
-
52
- /** Invalidate a list of query keys in parallel, filtering out undefineds. */
53
- async function invalidateAll(
54
- queryClient: Reactor<any, any>["queryClient"],
55
- keys: (import("@tanstack/react-query").QueryKey | undefined)[]
56
- ): Promise<void> {
57
- await Promise.all(
58
- keys.map((queryKey) =>
59
- queryKey ? queryClient.invalidateQueries({ queryKey }) : Promise.resolve()
60
- )
61
- )
62
- }
63
-
64
- // ============================================================================
65
- // Internal Implementation
66
- // ============================================================================
67
-
68
- const createMutationImpl = <
69
- Service,
70
- Method extends FunctionName<Service> = FunctionName<Service>,
71
- Transform extends TransformKey = "candid",
72
- TOnMutateResult = unknown,
73
- >(
74
- reactor: Reactor<Service, Transform>,
75
- config: MutationConfig<
76
- Service,
77
- Method,
78
- Transform,
79
- TOnMutateResult | undefined
80
- >
81
- ): MutationResult<Service, Method, Transform> => {
82
- const {
83
- functionName,
84
- callConfig,
85
- invalidateQueries: factoryInvalidateQueries,
86
- onSuccess: factoryOnSuccess,
87
- onCanisterError: factoryOnCanisterError,
88
- onError: factoryOnError,
89
- onMutate: factoryOnMutate,
90
- onSettled: factoryOnSettled,
91
- ...factoryOptions
92
- } = config
93
-
94
- /**
95
- * Raw call without any invalidation logic.
96
- * Used as mutationFn so that onSuccess handles all post-mutation work
97
- * and there is no double-invalidation.
98
- */
99
- const callFn = (
100
- args: ReactorArgs<Service, Method, Transform>
101
- ): Promise<ReactorReturnOk<Service, Method, Transform>> =>
102
- reactor.callMethod({ functionName, args, callConfig })
103
-
104
- /**
105
- * The factory's own `onMutate` result for each call through the hook.
106
- *
107
- * TanStack Query stores one `onMutate` result per mutation and hands it to
108
- * every callback. That slot holds the hook's result, which the mutation also
109
- * exposes as `context`, so this map holds the factory's. Each key is the
110
- * context object TanStack Query creates for a call and passes to every
111
- * callback of that call.
112
- */
113
- const factoryOnMutateResults = new WeakMap<
114
- MutationFunctionContext,
115
- TOnMutateResult | undefined
116
- >()
117
-
118
- /**
119
- * The `onMutate` result a factory callback receives. A factory without its
120
- * own `onMutate` gets the hook's, as it always has. TanStack Query before
121
- * 5.89 passes no context, so there a factory callback gets the hook's result
122
- * too.
123
- */
124
- const factoryOnMutateResult = (
125
- onMutateResult: unknown,
126
- context: MutationFunctionContext | undefined
127
- ) =>
128
- (factoryOnMutate && context
129
- ? factoryOnMutateResults.get(context)
130
- : onMutateResult) as TOnMutateResult | undefined
131
-
132
- /**
133
- * Imperative execution for non-React usage.
134
- *
135
- * Runs the same factory-level chain the hook does — invalidation, then
136
- * `onSuccess`, or `onCanisterError`/`onError` on failure — so a mutation
137
- * object behaves the same through both call paths. Only hook-level callbacks
138
- * are absent, because there is no hook here to supply them.
139
- *
140
- * The callbacks get the same `{ client, meta, mutationKey }` context
141
- * TanStack Query passes them on the hook path.
142
- *
143
- * The error is rethrown after the callbacks run, so `await execute(...)`
144
- * still rejects for the caller.
145
- *
146
- * Use this in route loaders, scripts, or server-side code.
147
- */
148
- const execute = async (
149
- args: ReactorArgs<Service, Method, Transform>
150
- ): Promise<ReactorReturnOk<Service, Method, Transform>> => {
151
- // TanStack Query builds this for the hook path, and the callback types
152
- // declare it as always present. None of it is mutation state, so this path
153
- // builds the same object. Passing `undefined` made a callback that reads
154
- // `context.client` throw here and nowhere else.
155
- //
156
- // The hook path takes `meta` and `mutationKey` from the client's defaulted
157
- // options, so this runs the same spread through `defaultMutationOptions`.
158
- // Reading `factoryOptions` alone dropped mutation defaults registered on
159
- // the QueryClient (`defaultOptions.mutations`, `setMutationDefaults`).
160
- const effectiveOptions = reactor.queryClient.defaultMutationOptions({
161
- mutationKey: reactor.getQueryOptions({ functionName }).queryKey,
162
- ...factoryOptions,
163
- })
164
- const context: MutationFunctionContext = {
165
- client: reactor.queryClient,
166
- meta: effectiveOptions.meta,
167
- mutationKey: effectiveOptions.mutationKey,
168
- }
169
-
170
- let result: ReactorReturnOk<Service, Method, Transform>
171
- try {
172
- result = await callFn(args)
173
- } catch (error) {
174
- if (isCanisterError(error)) {
175
- // `catch` drops the type. `callMethod` rejects with this method's
176
- // CanisterError, the one the hook path narrows to.
177
- factoryOnCanisterError?.(
178
- error as Parameters<NonNullable<typeof factoryOnCanisterError>>[0],
179
- args
180
- )
181
- }
182
- // `onMutate` does not run on this path, so there is no result to pass.
183
- // Awaited like `onSuccess` below and like the hook path, so an async
184
- // rollback or report finishes before `execute()` rejects.
185
- await factoryOnError?.(
186
- error as Parameters<NonNullable<typeof factoryOnError>>[0],
187
- args,
188
- undefined,
189
- context
190
- )
191
- throw error
192
- }
193
-
194
- if (factoryInvalidateQueries) {
195
- await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
196
- }
197
- await factoryOnSuccess?.(result, args, undefined, context)
198
- return result
199
- }
200
-
201
- // Hook implementation
202
- const useMutationHook = <THookOnMutateResult = unknown>(
203
- options?: MutationHookOptions<
204
- Service,
205
- Method,
206
- Transform,
207
- THookOnMutateResult
208
- >
209
- ) => {
210
- const baseOptions = reactor.getQueryOptions({ functionName })
211
- const {
212
- invalidateQueries: hookInvalidateQueries,
213
- onCanisterError: hookOnCanisterError,
214
- ...restOptions
215
- } = options ?? {}
216
-
217
- return useMutation<
218
- ReactorReturnOk<Service, Method, Transform>,
219
- ReactorReturnErr<Service, Method, Transform>,
220
- ReactorArgs<Service, Method, Transform>,
221
- THookOnMutateResult
222
- >(
223
- {
224
- mutationKey: baseOptions.queryKey,
225
- ...factoryOptions,
226
- ...restOptions,
227
- // Use callFn (not execute) to avoid double-invalidation:
228
- // factoryInvalidateQueries are handled in onSuccess below.
229
- mutationFn: callFn,
230
- onSuccess: async (data, variables, onMutateResult, context) => {
231
- // 1. Factory-level invalidation
232
- if (factoryInvalidateQueries) {
233
- await invalidateAll(reactor.queryClient, factoryInvalidateQueries)
234
- }
235
- // 2. Hook-level invalidation
236
- if (hookInvalidateQueries) {
237
- await invalidateAll(reactor.queryClient, hookInvalidateQueries)
238
- }
239
- // 3. Factory onSuccess
240
- await factoryOnSuccess?.(
241
- data,
242
- variables,
243
- factoryOnMutateResult(onMutateResult, context),
244
- context
245
- )
246
- // 4. Hook onSuccess
247
- await restOptions.onSuccess?.(
248
- data,
249
- variables,
250
- onMutateResult,
251
- context
252
- )
253
- },
254
- onError: async (error, variables, onMutateResult, context) => {
255
- if (isCanisterError(error)) {
256
- factoryOnCanisterError?.(error, variables)
257
- hookOnCanisterError?.(error, variables)
258
- }
259
- // Awaited in order, like `onSuccess`. TanStack Query holds
260
- // `onSettled` and the settled state until this promise resolves, so
261
- // an async `onError` must be part of it.
262
- await factoryOnError?.(
263
- error,
264
- variables,
265
- factoryOnMutateResult(onMutateResult, context),
266
- context
267
- )
268
- await restOptions.onError?.(error, variables, onMutateResult, context)
269
- },
270
- // `onMutate` and `onSettled` are composed like `onSuccess`/`onError`
271
- // above. They used to arrive through the `...restOptions` spread, so a
272
- // hook-level one silently replaced the factory's — factory teardown,
273
- // telemetry or logging simply vanished the moment any call site passed
274
- // its own, with no warning and no type error. Chaining is what a
275
- // reader who has seen `onSuccess` chain already expects.
276
- onMutate: async (variables, context) => {
277
- if (factoryOnMutate) {
278
- const result = await factoryOnMutate(variables, context)
279
- if (context) factoryOnMutateResults.set(context, result)
280
- }
281
- // Without a hook-level `onMutate` this is `undefined`, and
282
- // THookOnMutateResult is then `unknown`.
283
- return (await restOptions.onMutate?.(
284
- variables,
285
- context
286
- )) as THookOnMutateResult
287
- },
288
- onSettled: async (data, error, variables, onMutateResult, context) => {
289
- await factoryOnSettled?.(
290
- data,
291
- error,
292
- variables,
293
- factoryOnMutateResult(onMutateResult, context),
294
- context
295
- )
296
- await restOptions.onSettled?.(
297
- data,
298
- error,
299
- variables,
300
- onMutateResult,
301
- context
302
- )
303
- },
304
- },
305
- reactor.queryClient
306
- )
307
- }
308
-
309
- return { useMutation: useMutationHook, execute }
310
- }
311
-
312
- // ============================================================================
313
- // Public Factory Function
314
- // ============================================================================
315
-
316
- export function createMutation<
317
- Service,
318
- Transform extends TransformKey,
319
- Method extends FunctionName<Service> = FunctionName<Service>,
320
- TOnMutateResult = unknown,
321
- >(
322
- reactor: Reactor<Service, Transform>,
323
- // `execute()` runs the factory's `onSuccess` and `onError` without running
324
- // its `onMutate`, so their `onMutate` result can also be `undefined`.
325
- config: MutationConfig<
326
- NoInfer<Service>,
327
- Method,
328
- Transform,
329
- TOnMutateResult | undefined
330
- >
331
- ): MutationResult<Service, Method, Transform> {
332
- return createMutationImpl(
333
- reactor,
334
- config as MutationConfig<
335
- Service,
336
- Method,
337
- Transform,
338
- TOnMutateResult | undefined
339
- >
340
- )
341
- }
@@ -1,237 +0,0 @@
1
- /**
2
- * Query Factory - Generic wrapper for React Query-based canister data
3
- *
4
- * Creates unified fetch/hook/invalidate functions for any canister method.
5
- * Works with any Reactor instance.
6
- * Use this when the same read operation must work both inside React components
7
- * and outside React (loaders, actions, services, tests).
8
- *
9
- * @example
10
- * const userQuery = createQuery(todoManager, {
11
- * functionName: "get_user",
12
- * select: (result) => result.user,
13
- * })
14
- *
15
- * // In component
16
- * const { data: user } = userQuery.useQuery()
17
- *
18
- * @example
19
- * const userQuery = createQuery(todoManager, { functionName: "get_user", args: ["alice"] })
20
- *
21
- * // Outside React (loader/service/script)
22
- * await userQuery.fetch()
23
- * const cached = userQuery.getCacheData()
24
- * await userQuery.invalidate()
25
- */
26
-
27
- import type {
28
- Reactor,
29
- FunctionName,
30
- ReactorArgs,
31
- TransformKey,
32
- } from "@ic-reactor/core"
33
- import { useMemo } from "react"
34
- import {
35
- QueryKey,
36
- useQuery,
37
- type UseQueryOptions,
38
- type Updater,
39
- } from "@tanstack/react-query"
40
- import type {
41
- QueryFnData,
42
- QueryError,
43
- QueryConfig,
44
- UseQueryWithSelect,
45
- QueryResult,
46
- QueryFactoryConfig,
47
- NoInfer,
48
- } from "./types.js"
49
- import { buildChainedSelect, createBoundedCache } from "./utils.js"
50
-
51
- // ============================================================================
52
- // Internal Implementation
53
- // ============================================================================
54
-
55
- const createQueryImpl = <
56
- Service,
57
- Method extends FunctionName<Service> = FunctionName<Service>,
58
- Transform extends TransformKey = "candid",
59
- Selected = QueryFnData<Service, Method, Transform>,
60
- >(
61
- reactor: Reactor<Service, Transform>,
62
- config: QueryConfig<Service, Method, Transform, Selected>
63
- ): QueryResult<
64
- QueryFnData<Service, Method, Transform>,
65
- Selected,
66
- QueryError<Service, Method, Transform>
67
- > => {
68
- type TData = QueryFnData<Service, Method, Transform>
69
- type TError = QueryError<Service, Method, Transform>
70
-
71
- const {
72
- functionName,
73
- args,
74
- staleTime = 5 * 60 * 1000,
75
- select,
76
- queryKey: customQueryKey,
77
- ...rest
78
- } = config
79
-
80
- const params = { functionName, args, queryKey: customQueryKey }
81
-
82
- const getQueryKey = (): QueryKey => reactor.generateQueryKey(params)
83
-
84
- // Apply config.select to raw data (shared by fetch, getCacheData, and the hook)
85
- const applySelect = (raw: TData): Selected =>
86
- select ? select(raw) : (raw as unknown as Selected)
87
-
88
- /** Cache-first fetch for use in loaders / route preloading. */
89
- const fetch = async (): Promise<Selected> => {
90
- const result = await reactor.fetchQuery(params)
91
- return applySelect(result)
92
- }
93
-
94
- /** Fire-and-forget prefetch — warms the cache without blocking. */
95
- const prefetch = (): Promise<void> => {
96
- const baseOptions = reactor.getQueryOptions(params)
97
- return reactor.queryClient.prefetchQuery({
98
- queryKey: baseOptions.queryKey,
99
- queryFn: baseOptions.queryFn,
100
- staleTime,
101
- })
102
- }
103
-
104
- // The hook publicly exposes the overloaded UseQueryWithSelect signature.
105
- // Internally it takes a single broad options object, so we type the
106
- // implementation explicitly and cast once to the public overloaded type.
107
- type UseQueryHookOptions = Omit<
108
- UseQueryOptions<TData, TError, unknown>,
109
- "queryKey" | "queryFn"
110
- > & { select?: (data: Selected) => unknown }
111
-
112
- const useQueryHook = ((options?: UseQueryHookOptions) => {
113
- const baseOptions = reactor.getQueryOptions(params)
114
- // Memoized so the observer's select-result cache can hit; see
115
- // buildChainedSelect. `select` comes from the factory config and is stable.
116
- const chainedSelect = useMemo(
117
- () => buildChainedSelect(select, options?.select),
118
- [options?.select]
119
- )
120
- return useQuery(
121
- {
122
- queryKey: baseOptions.queryKey,
123
- staleTime,
124
- ...rest,
125
- ...options,
126
- queryFn: baseOptions.queryFn,
127
- select: chainedSelect,
128
- },
129
- reactor.queryClient
130
- )
131
- }) as UseQueryWithSelect<TData, Selected, TError>
132
-
133
- const invalidate = async (): Promise<void> => {
134
- await reactor.queryClient.invalidateQueries({ queryKey: getQueryKey() })
135
- }
136
-
137
- const getCacheData = ((
138
- selectFn?: (data: Selected) => unknown
139
- ): Selected | unknown => {
140
- const raw = reactor.getQueryData(params)
141
- if (raw === undefined) return undefined
142
- const selected = applySelect(raw)
143
- return selectFn ? selectFn(selected) : selected
144
- }) as QueryResult<TData, Selected, TError>["getCacheData"]
145
-
146
- const setData: QueryResult<TData, Selected, TError>["setData"] = (
147
- updater
148
- ) => {
149
- return reactor.queryClient.setQueryData(
150
- getQueryKey(),
151
- updater as Updater<TData | undefined, TData | undefined>
152
- ) as TData | undefined
153
- }
154
-
155
- return {
156
- fetch,
157
- prefetch,
158
- useQuery: useQueryHook,
159
- invalidate,
160
- getQueryKey,
161
- getCacheData,
162
- setData,
163
- }
164
- }
165
-
166
- // ============================================================================
167
- // Public Factory Function
168
- // ============================================================================
169
-
170
- export function createQuery<
171
- Service,
172
- Transform extends TransformKey,
173
- Method extends FunctionName<Service> = FunctionName<Service>,
174
- Selected = QueryFnData<Service, Method, Transform>,
175
- >(
176
- reactor: Reactor<Service, Transform>,
177
- config: QueryConfig<NoInfer<Service>, Method, Transform, Selected>
178
- ): QueryResult<
179
- QueryFnData<Service, Method, Transform>,
180
- Selected,
181
- QueryError<Service, Method, Transform>
182
- > {
183
- return createQueryImpl(
184
- reactor,
185
- config as QueryConfig<Service, Method, Transform, Selected>
186
- )
187
- }
188
-
189
- // ============================================================================
190
- // Convenience: Create query with dynamic args
191
- // ============================================================================
192
-
193
- export function createQueryFactory<
194
- Service,
195
- Transform extends TransformKey,
196
- Method extends FunctionName<Service> = FunctionName<Service>,
197
- Selected = QueryFnData<Service, Method, Transform>,
198
- >(
199
- reactor: Reactor<Service, Transform>,
200
- config: QueryFactoryConfig<NoInfer<Service>, Method, Transform, Selected>
201
- ): (
202
- args: ReactorArgs<Service, Method, Transform>
203
- ) => QueryResult<
204
- QueryFnData<Service, Method, Transform>,
205
- Selected,
206
- QueryError<Service, Method, Transform>
207
- > {
208
- const cache =
209
- createBoundedCache<
210
- QueryResult<
211
- QueryFnData<Service, Method, Transform>,
212
- Selected,
213
- QueryError<Service, Method, Transform>
214
- >
215
- >()
216
-
217
- return (args: ReactorArgs<Service, Method, Transform>) => {
218
- const key = reactor.generateQueryKey({
219
- functionName: config.functionName as Method,
220
- args,
221
- })
222
- const cacheKey = JSON.stringify(key)
223
-
224
- const existing = cache.get(cacheKey)
225
- if (existing) return existing
226
-
227
- const result = createQueryImpl<Service, Method, Transform, Selected>(
228
- reactor,
229
- {
230
- ...config,
231
- args,
232
- }
233
- )
234
- cache.set(cacheKey, result)
235
- return result
236
- }
237
- }