@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
package/src/types.ts DELETED
@@ -1,499 +0,0 @@
1
- /**
2
- * Shared type definitions for query factories (createActorQuery, createActorSuspenseQuery, etc.)
3
- */
4
-
5
- import type {
6
- FunctionName,
7
- ReactorReturnOk,
8
- ReactorQueryData,
9
- ReactorReturnErr,
10
- ReactorArgs,
11
- BaseActor,
12
- TransformKey,
13
- TransformReturnRegistry,
14
- ErrResult,
15
- ActorMethodReturnType,
16
- } from "@ic-reactor/core"
17
- import { CanisterError } from "@ic-reactor/core"
18
- import { CallConfig } from "@icp-sdk/core/agent"
19
- import {
20
- QueryKey,
21
- QueryObserverOptions,
22
- UseQueryOptions,
23
- UseQueryResult,
24
- UseSuspenseQueryOptions,
25
- UseSuspenseQueryResult,
26
- UseMutationOptions,
27
- UseMutationResult,
28
- } from "@tanstack/react-query"
29
-
30
- // ============================================================================
31
- // Utility Types
32
- // ============================================================================
33
-
34
- // NoInfer prevents TypeScript from inferring a type parameter from a particular position
35
- // This is available in TypeScript 5.4+ natively, but we define it for compatibility
36
- export type NoInfer<T> = [T][T extends any ? 0 : never]
37
- // ============================================================================
38
- // Base Query Data Types
39
- // ============================================================================
40
-
41
- /** The raw data type returned by the query function (before select) */
42
- export type QueryFnData<
43
- Service = BaseActor,
44
- Method extends FunctionName<Service> = FunctionName<Service>,
45
- Transform extends TransformKey = "candid",
46
- > = ReactorQueryData<ReactorReturnOk<Service, Method, Transform>>
47
-
48
- /** The error type for queries */
49
- export type QueryError<
50
- Service = BaseActor,
51
- Method extends FunctionName<Service> = FunctionName<Service>,
52
- Transform extends TransformKey = "candid",
53
- > = ReactorReturnErr<Service, Method, Transform>
54
-
55
- // ============================================================================
56
- // Base Query Configuration
57
- // ============================================================================
58
-
59
- /**
60
- * Base configuration for query wrappers (shared between regular and suspense).
61
- *
62
- * @template Service - The actor interface type
63
- * @template Method - The method name on the actor
64
- * @template Transform - The transformation key (identity, display, etc.)
65
- * @template Selected - The type returned after select transformation
66
- */
67
- export interface BaseQueryConfig<
68
- Service = BaseActor,
69
- Method extends FunctionName<Service> = FunctionName<Service>,
70
- Transform extends TransformKey = "candid",
71
- Selected = QueryFnData<Service, Method, Transform>,
72
- > extends Omit<
73
- QueryObserverOptions<
74
- QueryFnData<Service, Method, Transform>,
75
- ReactorReturnErr<Service, Method, Transform>,
76
- Selected,
77
- QueryFnData<Service, Method, Transform>,
78
- QueryKey
79
- >,
80
- "queryFn" | "queryKey"
81
- > {
82
- /** The method to call on the canister */
83
- functionName: Method
84
- /** Arguments to pass to the method (if any) */
85
- args?: ReactorArgs<Service, Method, Transform>
86
- /** The query key to use for this query */
87
- queryKey?: QueryKey
88
- /** How long data stays fresh before refetching (default: 5 min) */
89
- staleTime?: number
90
- /** Transform the raw result before returning */
91
- select?: (data: QueryFnData<Service, Method, Transform>) => Selected
92
- }
93
-
94
- /**
95
- * Configuration for createQuery (regular useQuery).
96
- * Alias for BaseQueryConfig for clarity.
97
- */
98
- export type QueryConfig<
99
- Service = BaseActor,
100
- Method extends FunctionName<Service> = FunctionName<Service>,
101
- Transform extends TransformKey = "candid",
102
- Selected = QueryFnData<Service, Method, Transform>,
103
- > = BaseQueryConfig<Service, Method, Transform, Selected>
104
-
105
- /**
106
- * Configuration for createSuspenseQuery (useSuspenseQuery).
107
- * Alias for BaseQueryConfig for clarity.
108
- */
109
- export type SuspenseQueryConfig<
110
- Service = BaseActor,
111
- Method extends FunctionName<Service> = FunctionName<Service>,
112
- Transform extends TransformKey = "candid",
113
- Selected = QueryFnData<Service, Method, Transform>,
114
- > = BaseQueryConfig<Service, Method, Transform, Selected>
115
-
116
- // ============================================================================
117
- // Factory Configuration (without args)
118
- // ============================================================================
119
-
120
- /**
121
- * Configuration for createQueryFactory (args are provided at call time).
122
- */
123
- export type QueryFactoryConfig<
124
- Service = BaseActor,
125
- Method extends FunctionName<Service> = FunctionName<Service>,
126
- Transform extends TransformKey = "candid",
127
- Selected = QueryFnData<Service, Method, Transform>,
128
- > = Omit<QueryConfig<Service, Method, Transform, Selected>, "args">
129
-
130
- /**
131
- * Configuration for createSuspenseQueryFactory (args are provided at call time).
132
- */
133
- export type SuspenseQueryFactoryConfig<
134
- Service = BaseActor,
135
- Method extends FunctionName<Service> = FunctionName<Service>,
136
- Transform extends TransformKey = "candid",
137
- Selected = QueryFnData<Service, Method, Transform>,
138
- > = Omit<SuspenseQueryConfig<Service, Method, Transform, Selected>, "args">
139
-
140
- // ============================================================================
141
- // Hook Interfaces with Chained Select Support
142
- // ============================================================================
143
-
144
- /**
145
- * useQuery hook with chained select support.
146
- * - Without select: returns TSelected (from config.select)
147
- * - With select: chains on top and returns TFinal
148
- *
149
- * Accepts all useQuery options from React Query documentation.
150
- * Select is special: it chains on top of config.select.
151
- */
152
- export interface UseQueryWithSelect<
153
- TQueryFnData,
154
- TSelected = TQueryFnData,
155
- TError = Error,
156
- > {
157
- // Overload 1: Without select - returns TSelected
158
- // Note: select is included as optional (never type) to enable autocomplete suggestions
159
- (
160
- options?: Omit<
161
- UseQueryOptions<TQueryFnData, TError, TSelected>,
162
- "queryKey" | "queryFn"
163
- > & {
164
- select?: undefined
165
- }
166
- ): UseQueryResult<TSelected, TError>
167
-
168
- // Overload 2: With select - chains on top of config.select and returns TFinal
169
- <TFinal = TSelected>(
170
- options: Omit<
171
- UseQueryOptions<TQueryFnData, TError, TFinal>,
172
- "queryKey" | "queryFn" | "select"
173
- > & {
174
- select: (data: TSelected) => TFinal
175
- }
176
- ): UseQueryResult<TFinal, TError>
177
- }
178
-
179
- /**
180
- * useSuspenseQuery hook with chained select support.
181
- * - Without select: returns TSelected (from config.select)
182
- * - With select: chains on top and returns TFinal
183
- *
184
- * Accepts all useSuspenseQuery options from React Query documentation.
185
- * Select is special: it chains on top of config.select.
186
- * Data is always defined (never undefined).
187
- * Does NOT support `enabled` option.
188
- */
189
- export interface UseSuspenseQueryWithSelect<
190
- TQueryFnData,
191
- TSelected = TQueryFnData,
192
- TError = Error,
193
- > {
194
- // Overload 1: Without select - returns TSelected
195
- // Note: select is included as optional (never type) to enable autocomplete suggestions
196
- (
197
- options?: Omit<
198
- UseSuspenseQueryOptions<TQueryFnData, TError, TSelected>,
199
- "queryKey" | "queryFn"
200
- > & {
201
- select?: undefined
202
- }
203
- ): UseSuspenseQueryResult<TSelected, TError>
204
-
205
- // Overload 2: With select - chains on top of config.select and returns TFinal
206
- <TFinal = TSelected>(
207
- options: Omit<
208
- UseSuspenseQueryOptions<TQueryFnData, TError, TFinal>,
209
- "queryKey" | "queryFn" | "select"
210
- > & {
211
- select: (data: TSelected) => TFinal
212
- }
213
- ): UseSuspenseQueryResult<TFinal, TError>
214
- }
215
-
216
- // ============================================================================
217
- // Result Interfaces
218
- // ============================================================================
219
-
220
- /**
221
- * Base result interface shared between createQuery and createSuspenseQuery.
222
- *
223
- * @template TQueryFnData - The raw data type
224
- * @template TSelected - The type after select transformation
225
- * @template TError - The error type
226
- */
227
- export interface BaseQueryResult<
228
- TQueryFnData,
229
- TSelected = TQueryFnData,
230
- _TError = Error,
231
- > {
232
- /** Fetch data in loader (uses ensureQueryData for cache-first) */
233
- fetch: () => Promise<TSelected>
234
-
235
- /**
236
- * Eagerly prefetch data into the cache without blocking.
237
- * Useful for preloading data before navigating to a route.
238
- *
239
- * Unlike `fetch()`, this returns a void promise so it can be fire-and-forget.
240
- *
241
- * @example
242
- * // In a route hover handler
243
- * button.addEventListener("mouseenter", () => userQuery.prefetch())
244
- */
245
- prefetch: () => Promise<void>
246
-
247
- /** Invalidate the cache (refetches if query is active) */
248
- invalidate: () => Promise<void>
249
-
250
- /** Get query key (for advanced React Query usage) */
251
- getQueryKey: () => QueryKey
252
-
253
- /**
254
- * Read data directly from cache without fetching.
255
- * Returns undefined if data is not in cache.
256
- *
257
- * @template TFinal - Type returned after optional select transformation
258
- * @param select - Optional select function to transform cached data further
259
- * @returns Cached data with select applied, or undefined if not in cache
260
- *
261
- * @example
262
- * // Just get the cached data
263
- * const user = userQuery.getCacheData()
264
- *
265
- * // With additional select transformation
266
- * const name = userQuery.getCacheData((user) => user.name)
267
- */
268
- getCacheData: {
269
- (): TSelected | undefined
270
- <TFinal>(select: (data: TSelected) => TFinal): TFinal | undefined
271
- }
272
-
273
- /**
274
- * Write raw data directly into the cache (useful for optimistic updates).
275
- * Accepts a new value or an updater function that receives the current cached raw data.
276
- *
277
- * Note: The value is stored as raw (pre-select) data. Any active `select`
278
- * transformations are automatically re-applied by React Query on the next render.
279
- *
280
- * @example
281
- * // Optimistic update before a mutation
282
- * userQuery.setData({ id: "1", name: "Alice" })
283
- *
284
- * // Functional update
285
- * counterQuery.setData((prev) => (prev ?? 0) + 1)
286
- */
287
- setData: (
288
- updater:
289
- | TQueryFnData
290
- | ((old: TQueryFnData | undefined) => TQueryFnData | undefined)
291
- ) => TQueryFnData | undefined
292
- }
293
-
294
- /**
295
- * Result from createQuery
296
- *
297
- * Includes useQuery hook that:
298
- * - Supports `enabled` option for conditional fetching
299
- * - Data may be `undefined` during loading
300
- * - Uses regular `useQuery` with manual loading state handling
301
- *
302
- * @template TQueryFnData - The raw data type
303
- * @template TSelected - The type after select transformation
304
- * @template TError - The error type
305
- */
306
- export interface QueryResult<
307
- TQueryFnData,
308
- TSelected = TQueryFnData,
309
- TError = Error,
310
- > extends BaseQueryResult<TQueryFnData, TSelected, TError> {
311
- /** React hook for components - supports chained select and enabled option */
312
- useQuery: UseQueryWithSelect<TQueryFnData, TSelected, TError>
313
- }
314
-
315
- /**
316
- * Result from createSuspenseQuery
317
- *
318
- * Includes useSuspenseQuery hook that:
319
- * - Requires wrapping in <Suspense> boundary
320
- * - Data is always defined (no undefined checks)
321
- * - Does NOT support `enabled` option
322
- *
323
- * @template TQueryFnData - The raw data type
324
- * @template TSelected - The type after select transformation
325
- * @template TError - The error type
326
- */
327
- export interface SuspenseQueryResult<
328
- TQueryFnData,
329
- TSelected = TQueryFnData,
330
- TError = Error,
331
- > extends BaseQueryResult<TQueryFnData, TSelected, TError> {
332
- /** React hook for components - data is always defined (wrap in Suspense) */
333
- useSuspenseQuery: UseSuspenseQueryWithSelect<TQueryFnData, TSelected, TError>
334
- }
335
-
336
- // ============================================================================
337
- // Actor Mutation Types
338
- // ============================================================================
339
-
340
- /**
341
- * Configuration for createMutation and useActorMutation.
342
- *
343
- * @template TOnMutateResult - The value `onMutate` returns, which `onSuccess`,
344
- * `onError` and `onSettled` receive. TypeScript infers it from `onMutate`.
345
- */
346
- export interface MutationConfig<
347
- Service = BaseActor,
348
- Method extends FunctionName<Service> = FunctionName<Service>,
349
- Transform extends TransformKey = "candid",
350
- TOnMutateResult = unknown,
351
- > extends Omit<
352
- UseMutationOptions<
353
- ReactorReturnOk<Service, Method, Transform>,
354
- ReactorReturnErr<Service, Method, Transform>,
355
- ReactorArgs<Service, Method, Transform>,
356
- TOnMutateResult
357
- >,
358
- "mutationFn"
359
- > {
360
- /** The method to call on the canister */
361
- functionName: Method
362
- /** Call configuration for the actor method */
363
- callConfig?: CallConfig
364
- /**
365
- * Queries to invalidate upon successful mutation.
366
- *
367
- * `undefined` entries are skipped, so the common
368
- * `[maybeQuery?.getQueryKey()]` idiom is safe when the optional query object
369
- * is absent.
370
- */
371
- invalidateQueries?: (QueryKey | undefined)[]
372
- /**
373
- * Callback for canister-level business logic errors.
374
- * Called when the canister returns a Result { Err: E } variant.
375
- *
376
- * This is separate from `onError` which handles all errors including
377
- * network failures, agent errors, etc.
378
- *
379
- * @param error - The CanisterError containing the typed error value
380
- * @param variables - The arguments passed to the mutation
381
- *
382
- * @example
383
- * ```typescript
384
- * createMutation(reactor, {
385
- * functionName: "transfer",
386
- * onCanisterError: (error, variables) => {
387
- * // error.err contains the typed Err value
388
- * // error.code contains the variant key (e.g., "InsufficientFunds")
389
- * console.error(`Transfer failed: ${error.code}`, error.err)
390
- * },
391
- * })
392
- * ```
393
- */
394
- onCanisterError?: (
395
- error: CanisterError<
396
- TransformReturnRegistry<
397
- ErrResult<ActorMethodReturnType<Service[Method]>>,
398
- Service
399
- >[Transform]
400
- >,
401
- variables: ReactorArgs<Service, Method, Transform>
402
- ) => void
403
- }
404
-
405
- /**
406
- * Configuration for createMutationFactory.
407
- */
408
- export type MutationFactoryConfig<
409
- Service = BaseActor,
410
- Method extends FunctionName<Service> = FunctionName<Service>,
411
- Transform extends TransformKey = "candid",
412
- TOnMutateResult = unknown,
413
- > = Omit<
414
- MutationConfig<Service, Method, Transform, TOnMutateResult>,
415
- "onSuccess"
416
- >
417
-
418
- /**
419
- * Options for useMutation hook.
420
- * Extends React Query's UseMutationOptions with invalidateQueries support.
421
- *
422
- * @template TOnMutateResult - The value `onMutate` returns, which `onSuccess`,
423
- * `onError` and `onSettled` receive. TypeScript infers it from `onMutate`.
424
- */
425
- export interface MutationHookOptions<
426
- Service = BaseActor,
427
- Method extends FunctionName<Service> = FunctionName<Service>,
428
- Transform extends TransformKey = "candid",
429
- TOnMutateResult = unknown,
430
- > extends Omit<
431
- UseMutationOptions<
432
- ReactorReturnOk<Service, Method, Transform>,
433
- ReactorReturnErr<Service, Method, Transform>,
434
- ReactorArgs<Service, Method, Transform>,
435
- TOnMutateResult
436
- >,
437
- "mutationFn"
438
- > {
439
- /**
440
- * Query keys to invalidate upon successful mutation.
441
- * Use query.getQueryKey() to get the key from a query result.
442
- *
443
- * @example
444
- * const balanceQuery = getIcpBalance(account)
445
- * useMutation({
446
- * invalidateQueries: [balanceQuery.getQueryKey()],
447
- * })
448
- */
449
- invalidateQueries?: (QueryKey | undefined)[]
450
- /**
451
- * Callback for canister-level business logic errors.
452
- * Called when the canister returns a Result { Err: E } variant.
453
- *
454
- * @param error - The CanisterError containing the typed error value
455
- * @param variables - The arguments passed to the mutation
456
- */
457
- onCanisterError?: (
458
- error: CanisterError<
459
- TransformReturnRegistry<
460
- ErrResult<ActorMethodReturnType<Service[Method]>>,
461
- Service
462
- >[Transform]
463
- >,
464
- variables: ReactorArgs<Service, Method, Transform>
465
- ) => void
466
- }
467
-
468
- /**
469
- * Result from createMutation.
470
- */
471
- export interface MutationResult<
472
- Service = BaseActor,
473
- Method extends FunctionName<Service> = FunctionName<Service>,
474
- Transform extends TransformKey = "candid",
475
- > {
476
- /**
477
- * React hook for the mutation.
478
- * Accepts options to override/extend the factory config.
479
- *
480
- * @example
481
- * // With invalidateQueries to auto-update balance after transfer
482
- * const { mutate } = icpTransferMutation.useMutation({
483
- * invalidateQueries: [userBalanceQuery], // Auto-invalidate after success!
484
- * })
485
- */
486
- useMutation: <TOnMutateResult = unknown>(
487
- options?: MutationHookOptions<Service, Method, Transform, TOnMutateResult>
488
- ) => UseMutationResult<
489
- ReactorReturnOk<Service, Method, Transform>,
490
- ReactorReturnErr<Service, Method, Transform>,
491
- ReactorArgs<Service, Method, Transform>,
492
- TOnMutateResult
493
- >
494
-
495
- /** Execute the update call directly (outside of React) */
496
- execute: (
497
- args: ReactorArgs<Service, Method, Transform>
498
- ) => Promise<ReactorReturnOk<Service, Method, Transform>>
499
- }
package/src/utils.ts DELETED
@@ -1,121 +0,0 @@
1
- /**
2
- * Shared internal utilities for query and mutation factories.
3
- */
4
-
5
- import type { QueryKey } from "@tanstack/react-query"
6
- import type { ReactorQueryData } from "@ic-reactor/core"
7
- import { generateKey } from "@ic-reactor/core"
8
-
9
- /**
10
- * Internal query-key segment used to distinguish per-call factory args
11
- * from the base query key. Not part of the public API.
12
- */
13
- export const FACTORY_KEY_ARGS_QUERY_KEY = "__ic_reactor_factory_key_args"
14
-
15
- /** Convert a direct reactor result into a value TanStack Query can cache. */
16
- export const normalizeQueryData = <T>(value: T): ReactorQueryData<T> =>
17
- (value === undefined ? null : value) as ReactorQueryData<T>
18
-
19
- /**
20
- * Merge a base query key, optional per-call query key, and optional key-args
21
- * into a single query key array.
22
- *
23
- * Used by createInfiniteQueryFactory and createSuspenseInfiniteQueryFactory to
24
- * ensure each unique set of factory args produces a distinct cache entry.
25
- */
26
- export function mergeFactoryQueryKey(
27
- baseQueryKey?: QueryKey,
28
- callQueryKey?: QueryKey,
29
- keyArgs?: unknown
30
- ): QueryKey | undefined {
31
- const merged: unknown[] = []
32
-
33
- if (baseQueryKey) merged.push(...baseQueryKey)
34
- if (callQueryKey) merged.push(...callQueryKey)
35
- if (keyArgs !== undefined) {
36
- // Serialize keyArgs through generateKey so BigInt values (and other
37
- // non-JSON-serializable Candid types such as Principal) are safely
38
- // converted to strings before React Query hashes the key.
39
- const safeKeyArgs = generateKey(
40
- Array.isArray(keyArgs) ? keyArgs : [keyArgs]
41
- )
42
- merged.push({ [FACTORY_KEY_ARGS_QUERY_KEY]: safeKeyArgs })
43
- }
44
-
45
- return merged.length > 0 ? merged : undefined
46
- }
47
-
48
- /**
49
- * Build a chained select that applies the config-level select (if any) and then
50
- * the hook-level select (if any).
51
- *
52
- * Returns the caller's own function untouched when only one of the two is
53
- * present, and `undefined` when neither is — allocating a wrapper only for the
54
- * genuinely chained case. Identity matters here: `QueryObserver` memoizes a
55
- * select result on `options.select === previousSelectFn`, so a wrapper rebuilt
56
- * on every render defeats that check. The select then re-runs each render, and
57
- * for a select returning a non-plain value (a `Map`, a `Date`, a `Principal`)
58
- * `replaceEqualDeep` cannot structurally share the result either, so `data`
59
- * gets a fresh reference every render and a `useEffect([data])` that sets state
60
- * becomes an unbounded loop.
61
- *
62
- * Callers should still memoize the result, since the chained case allocates.
63
- */
64
- export function buildChainedSelect<TData, TSelected, TFinal = TSelected>(
65
- configSelect: ((data: TData) => TSelected) | undefined,
66
- hookSelect: ((data: TSelected) => TFinal) | undefined
67
- ): ((rawData: TData) => TSelected | TFinal) | undefined {
68
- if (!configSelect) {
69
- // `hookSelect` receives the raw data when there is no config-level select,
70
- // matching the previous behaviour.
71
- return hookSelect as unknown as
72
- ((rawData: TData) => TSelected | TFinal) | undefined
73
- }
74
- if (!hookSelect) return configSelect
75
- return (rawData: TData) => hookSelect(configSelect(rawData))
76
- }
77
-
78
- /**
79
- * How many memoized query objects an args-late factory keeps.
80
- *
81
- * The memo exists so `getBalance(sameArgs)` returns the same object twice; it
82
- * was unbounded, so an open-ended argument space — a per-principal balance in a
83
- * long-lived SPA, a search-as-you-type filter, a cursor-keyed list — grew it
84
- * forever (measured at ~2.9 kB per entry, 55.8 MB for 20k). A few hundred
85
- * covers any realistic working set while capping the worst case.
86
- */
87
- const FACTORY_CACHE_LIMIT = 256
88
-
89
- /**
90
- * Smallest useful LRU: a `Map` already iterates in insertion order, so
91
- * re-inserting on read is enough to track recency.
92
- *
93
- * Eviction is safe — it costs memoization, never correctness. A query object is
94
- * a closure over the reactor and config, so one rebuilt after eviction behaves
95
- * identically; only its reference identity differs.
96
- */
97
- export function createBoundedCache<V>(limit: number = FACTORY_CACHE_LIMIT) {
98
- const entries = new Map<string, V>()
99
-
100
- return {
101
- get(key: string): V | undefined {
102
- const value = entries.get(key)
103
- if (value === undefined) return undefined
104
- // Touch: move to the most-recent end.
105
- entries.delete(key)
106
- entries.set(key, value)
107
- return value
108
- },
109
- set(key: string, value: V): void {
110
- if (entries.has(key)) entries.delete(key)
111
- else if (entries.size >= limit) {
112
- const oldest = entries.keys().next().value
113
- if (oldest !== undefined) entries.delete(oldest)
114
- }
115
- entries.set(key, value)
116
- },
117
- get size(): number {
118
- return entries.size
119
- },
120
- }
121
- }