@ic-reactor/react 3.12.4 → 3.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +410 -44
- package/dist/auth/auth-client-compat.d.ts +122 -0
- package/dist/auth/auth-client-compat.d.ts.map +1 -0
- package/dist/auth/auth-client-compat.js +162 -0
- package/dist/auth/auth-client-compat.js.map +1 -0
- package/dist/auth/authentication-manager.d.ts +287 -5
- package/dist/auth/authentication-manager.d.ts.map +1 -1
- package/dist/auth/authentication-manager.js +920 -150
- package/dist/auth/authentication-manager.js.map +1 -1
- package/dist/auth/createIdentityAttributeHooks.d.ts.map +1 -1
- package/dist/auth/createIdentityAttributeHooks.js +36 -20
- package/dist/auth/createIdentityAttributeHooks.js.map +1 -1
- package/dist/auth/identity-attributes-manager.d.ts +2 -1
- package/dist/auth/identity-attributes-manager.d.ts.map +1 -1
- package/dist/auth/identity-attributes-manager.js +90 -6
- package/dist/auth/identity-attributes-manager.js.map +1 -1
- package/dist/auth/identity-attributes.d.ts.map +1 -1
- package/dist/auth/identity-attributes.js +57 -0
- package/dist/auth/identity-attributes.js.map +1 -1
- package/dist/auth/local-ii-probe.d.ts +12 -1
- package/dist/auth/local-ii-probe.d.ts.map +1 -1
- package/dist/auth/local-ii-probe.js +22 -3
- package/dist/auth/local-ii-probe.js.map +1 -1
- package/dist/auth/types.d.ts +48 -5
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/createActorHooks.d.ts +9 -20
- package/dist/createActorHooks.d.ts.map +1 -1
- package/dist/createActorHooks.js.map +1 -1
- package/dist/createInfiniteQuery.d.ts +51 -10
- package/dist/createInfiniteQuery.d.ts.map +1 -1
- package/dist/createInfiniteQuery.js +39 -15
- package/dist/createInfiniteQuery.js.map +1 -1
- package/dist/createMutation.d.ts +4 -1
- package/dist/createMutation.d.ts.map +1 -1
- package/dist/createMutation.js +121 -84
- package/dist/createMutation.js.map +1 -1
- package/dist/createQuery.d.ts +35 -2
- package/dist/createQuery.d.ts.map +1 -1
- package/dist/createQuery.js +104 -17
- package/dist/createQuery.js.map +1 -1
- package/dist/createReactorProvider.d.ts +158 -0
- package/dist/createReactorProvider.d.ts.map +1 -0
- package/dist/createReactorProvider.js +256 -0
- package/dist/createReactorProvider.js.map +1 -0
- package/dist/createSuspenseInfiniteQuery.d.ts +16 -9
- package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/createSuspenseInfiniteQuery.js +59 -27
- package/dist/createSuspenseInfiniteQuery.js.map +1 -1
- package/dist/createSuspenseQuery.d.ts +23 -2
- package/dist/createSuspenseQuery.d.ts.map +1 -1
- package/dist/createSuspenseQuery.js +68 -21
- package/dist/createSuspenseQuery.js.map +1 -1
- package/dist/defineDisplayReactor.d.ts +43 -0
- package/dist/defineDisplayReactor.d.ts.map +1 -0
- package/dist/defineDisplayReactor.js +42 -0
- package/dist/defineDisplayReactor.js.map +1 -0
- package/dist/defineReactor.d.ts +46 -72
- package/dist/defineReactor.d.ts.map +1 -1
- package/dist/defineReactor.js +11 -176
- package/dist/defineReactor.js.map +1 -1
- package/dist/defineReactorShared.d.ts +84 -0
- package/dist/defineReactorShared.d.ts.map +1 -0
- package/dist/defineReactorShared.js +139 -0
- package/dist/defineReactorShared.js.map +1 -0
- package/dist/hooks/createAuthHooks.d.ts +9 -2
- package/dist/hooks/createAuthHooks.d.ts.map +1 -1
- package/dist/hooks/createAuthHooks.js +184 -24
- package/dist/hooks/createAuthHooks.js.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.d.ts +36 -8
- package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorInfiniteQuery.js +54 -21
- package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorMethod.d.ts +37 -4
- package/dist/hooks/useActorMethod.d.ts.map +1 -1
- package/dist/hooks/useActorMethod.js +201 -57
- package/dist/hooks/useActorMethod.js.map +1 -1
- package/dist/hooks/useActorMutation.d.ts +15 -12
- package/dist/hooks/useActorMutation.d.ts.map +1 -1
- package/dist/hooks/useActorMutation.js +14 -13
- package/dist/hooks/useActorMutation.js.map +1 -1
- package/dist/hooks/useActorQuery.d.ts +17 -4
- package/dist/hooks/useActorQuery.d.ts.map +1 -1
- package/dist/hooks/useActorQuery.js +30 -9
- package/dist/hooks/useActorQuery.js.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +17 -5
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js +37 -17
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
- package/dist/hooks/useActorSuspenseQuery.d.ts +2 -2
- package/dist/hooks/useActorSuspenseQuery.d.ts.map +1 -1
- package/dist/hooks/useActorSuspenseQuery.js +20 -9
- package/dist/hooks/useActorSuspenseQuery.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/ownedAuthentication.d.ts +52 -0
- package/dist/ownedAuthentication.d.ts.map +1 -0
- package/dist/ownedAuthentication.js +49 -0
- package/dist/ownedAuthentication.js.map +1 -0
- package/dist/server.d.ts +21 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +23 -0
- package/dist/server.js.map +1 -0
- package/dist/testing.d.ts +19 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +19 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +428 -21
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/utils.d.ts +159 -3
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +301 -1
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts +12 -7
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +34 -15
- package/dist/validation.js.map +1 -1
- package/llms.txt +259 -33
- package/package.json +17 -5
- package/src/auth/auth-client-compat.ts +273 -0
- package/src/auth/authentication-manager.ts +918 -96
- package/src/auth/createIdentityAttributeHooks.ts +47 -21
- package/src/auth/identity-attributes-manager.ts +100 -5
- package/src/auth/identity-attributes.ts +75 -0
- package/src/auth/local-ii-probe.ts +29 -3
- package/src/auth/types.ts +49 -6
- package/src/createActorHooks.ts +50 -42
- package/src/createInfiniteQuery.ts +120 -28
- package/src/createMutation.ts +213 -132
- package/src/createQuery.ts +164 -32
- package/src/createReactorProvider.ts +365 -0
- package/src/createSuspenseInfiniteQuery.ts +93 -43
- package/src/createSuspenseQuery.ts +102 -32
- package/src/defineDisplayReactor.ts +62 -0
- package/src/defineReactor.ts +81 -263
- package/src/defineReactorShared.ts +268 -0
- package/src/hooks/createAuthHooks.ts +210 -28
- package/src/hooks/useActorInfiniteQuery.ts +156 -55
- package/src/hooks/useActorMethod.ts +295 -92
- package/src/hooks/useActorMutation.ts +42 -30
- package/src/hooks/useActorQuery.ts +43 -10
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +110 -54
- package/src/hooks/useActorSuspenseQuery.ts +30 -15
- package/src/index.ts +8 -0
- package/src/ownedAuthentication.ts +81 -0
- package/src/server.ts +23 -0
- package/src/testing.ts +18 -0
- package/src/types.ts +492 -22
- package/src/utils.ts +387 -3
- package/src/validation.ts +43 -19
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* const postsQuery = createSuspenseInfiniteQuery(reactor, {
|
|
14
14
|
* functionName: "get_posts",
|
|
15
15
|
* initialPageParam: 0,
|
|
16
|
-
* getArgs: (cursor) => [{ cursor, limit: 10 }],
|
|
16
|
+
* getArgs: (cursor) => [{ cursor, limit: 10 }] as const,
|
|
17
17
|
* getNextPageParam: (lastPage) => lastPage.nextCursor,
|
|
18
18
|
* })
|
|
19
19
|
*
|
|
@@ -46,11 +46,22 @@ import {
|
|
|
46
46
|
InfiniteQueryObserverOptions,
|
|
47
47
|
} from "@tanstack/react-query"
|
|
48
48
|
import { CallConfig } from "@icp-sdk/core/agent"
|
|
49
|
-
import {
|
|
49
|
+
import type {
|
|
50
|
+
NoInfer,
|
|
51
|
+
QueryCacheControls,
|
|
52
|
+
QueryFactoryMethods,
|
|
53
|
+
} from "./types.js"
|
|
50
54
|
import {
|
|
51
55
|
buildChainedSelect,
|
|
56
|
+
callConfigForKey,
|
|
52
57
|
mergeFactoryQueryKey,
|
|
58
|
+
mountWhileSuspended,
|
|
53
59
|
normalizeQueryData,
|
|
60
|
+
pickFetchOptions,
|
|
61
|
+
queryCacheControls,
|
|
62
|
+
retryOption,
|
|
63
|
+
useMountQueryClient,
|
|
64
|
+
withQueryFactoryMethods,
|
|
54
65
|
} from "./utils.js"
|
|
55
66
|
|
|
56
67
|
type SuspenseInfiniteFactoryCallOptions = {
|
|
@@ -63,7 +74,7 @@ type SuspenseInfiniteQueryFactoryFn<
|
|
|
63
74
|
Transform extends TransformKey,
|
|
64
75
|
TPageParam,
|
|
65
76
|
Selected,
|
|
66
|
-
> = {
|
|
77
|
+
> = QueryFactoryMethods & {
|
|
67
78
|
(
|
|
68
79
|
getArgs: (pageParam: TPageParam) => ReactorArgs<Service, Method, Transform>
|
|
69
80
|
): SuspenseInfiniteQueryResult<
|
|
@@ -106,7 +117,7 @@ export type SuspenseInfiniteQueryError<
|
|
|
106
117
|
// ============================================================================
|
|
107
118
|
|
|
108
119
|
/**
|
|
109
|
-
* Configuration for
|
|
120
|
+
* Configuration for createSuspenseInfiniteQuery.
|
|
110
121
|
* Extends InfiniteQueryObserverOptions to accept all React Query options at the create level.
|
|
111
122
|
*
|
|
112
123
|
* @template Service - The actor interface type
|
|
@@ -154,7 +165,7 @@ export interface SuspenseInfiniteQueryConfig<
|
|
|
154
165
|
}
|
|
155
166
|
|
|
156
167
|
/**
|
|
157
|
-
* Configuration for
|
|
168
|
+
* Configuration for createSuspenseInfiniteQueryFactory (without getArgs; provided at call time).
|
|
158
169
|
*/
|
|
159
170
|
export type SuspenseInfiniteQueryFactoryConfig<
|
|
160
171
|
Service = BaseActor,
|
|
@@ -231,7 +242,11 @@ export interface UseSuspenseInfiniteQueryWithSelect<
|
|
|
231
242
|
// ============================================================================
|
|
232
243
|
|
|
233
244
|
/**
|
|
234
|
-
* Result from
|
|
245
|
+
* Result from createSuspenseInfiniteQuery
|
|
246
|
+
*
|
|
247
|
+
* `cancel()`, `reset()` and `optimisticUpdate()` act on the whole page set:
|
|
248
|
+
* `optimisticUpdate` gets and returns the raw `InfiniteData`, `{ pages,
|
|
249
|
+
* pageParams }`.
|
|
235
250
|
*
|
|
236
251
|
* @template TPageData - The raw page data type
|
|
237
252
|
* @template TPageParam - The page parameter type
|
|
@@ -243,7 +258,7 @@ export interface SuspenseInfiniteQueryResult<
|
|
|
243
258
|
TPageParam,
|
|
244
259
|
Selected = InfiniteData<TPageData, TPageParam>,
|
|
245
260
|
TError = Error,
|
|
246
|
-
> {
|
|
261
|
+
> extends QueryCacheControls<InfiniteData<TPageData, TPageParam>> {
|
|
247
262
|
/** Fetch first page in loader (uses ensureInfiniteQueryData for cache-first) */
|
|
248
263
|
fetch: () => Promise<Selected>
|
|
249
264
|
|
|
@@ -345,7 +360,7 @@ const createSuspenseInfiniteQueryImpl = <
|
|
|
345
360
|
const result = await reactor.callMethod({
|
|
346
361
|
functionName,
|
|
347
362
|
args,
|
|
348
|
-
callConfig,
|
|
363
|
+
callConfig: callConfigForKey(context.queryKey, callConfig),
|
|
349
364
|
})
|
|
350
365
|
return normalizeQueryData<ReactorReturnOk<Service, Method, Transform>>(
|
|
351
366
|
result as ReactorReturnOk<Service, Method, Transform>
|
|
@@ -359,19 +374,30 @@ const createSuspenseInfiniteQueryImpl = <
|
|
|
359
374
|
TPageData,
|
|
360
375
|
QueryKey,
|
|
361
376
|
TPageParam
|
|
362
|
-
> =>
|
|
363
|
-
queryKey
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
377
|
+
> => {
|
|
378
|
+
const queryKey = getQueryKey()
|
|
379
|
+
return {
|
|
380
|
+
// How the query function runs, shared with the hook; see
|
|
381
|
+
// pickFetchOptions. An update method's `retry` defaults as
|
|
382
|
+
// `Reactor.getQueryRetry` says.
|
|
383
|
+
...pickFetchOptions(rest),
|
|
384
|
+
...retryOption(rest.retry, reactor.getQueryRetry(functionName, queryKey)),
|
|
385
|
+
queryKey,
|
|
386
|
+
queryFn,
|
|
387
|
+
initialPageParam,
|
|
388
|
+
getNextPageParam,
|
|
389
|
+
staleTime,
|
|
390
|
+
}
|
|
391
|
+
}
|
|
369
392
|
|
|
370
393
|
// Fetch function for loaders (cache-first, fetches first page)
|
|
371
394
|
const fetch = async (): Promise<Selected> => {
|
|
372
|
-
// Use ensureInfiniteQueryData to get cached data or fetch if stale
|
|
373
|
-
|
|
374
|
-
|
|
395
|
+
// Use ensureInfiniteQueryData to get cached data or fetch if stale. A
|
|
396
|
+
// sign-in or sign-out while it is in flight cancels it; it then runs again
|
|
397
|
+
// for the new identity rather than rejecting with TanStack's
|
|
398
|
+
// CancelledError, as `reactor.fetchQuery` does.
|
|
399
|
+
const result = await reactor.clientManager.fetchAcrossIdentitySwitch(() =>
|
|
400
|
+
reactor.queryClient.ensureInfiniteQueryData(getInfiniteQueryOptions())
|
|
375
401
|
)
|
|
376
402
|
|
|
377
403
|
// Result is already InfiniteData format
|
|
@@ -385,6 +411,8 @@ const createSuspenseInfiniteQueryImpl = <
|
|
|
385
411
|
Selected,
|
|
386
412
|
TError
|
|
387
413
|
> = (options: any): any => {
|
|
414
|
+
useMountQueryClient(reactor.queryClient)
|
|
415
|
+
|
|
388
416
|
// Memoized and identity-preserving; see buildChainedSelect for why the
|
|
389
417
|
// function identity matters to the observer's select-result cache.
|
|
390
418
|
const chainedSelect = useMemo(
|
|
@@ -392,21 +420,34 @@ const createSuspenseInfiniteQueryImpl = <
|
|
|
392
420
|
[options?.select]
|
|
393
421
|
)
|
|
394
422
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
423
|
+
const queryKey = getQueryKey()
|
|
424
|
+
|
|
425
|
+
try {
|
|
426
|
+
return useSuspenseInfiniteQuery(
|
|
427
|
+
{
|
|
428
|
+
queryKey,
|
|
429
|
+
queryFn,
|
|
430
|
+
initialPageParam,
|
|
431
|
+
getNextPageParam,
|
|
432
|
+
getPreviousPageParam,
|
|
433
|
+
maxPages,
|
|
434
|
+
staleTime,
|
|
435
|
+
...rest,
|
|
436
|
+
...options,
|
|
437
|
+
select: chainedSelect,
|
|
438
|
+
// The hook's `retry`, else the config's, else an update method's
|
|
439
|
+
// default; see `Reactor.getQueryRetry`.
|
|
440
|
+
...retryOption(
|
|
441
|
+
options?.retry ?? rest.retry,
|
|
442
|
+
reactor.getQueryRetry(functionName, queryKey)
|
|
443
|
+
),
|
|
444
|
+
},
|
|
445
|
+
reactor.queryClient
|
|
446
|
+
)
|
|
447
|
+
} catch (thrown) {
|
|
448
|
+
mountWhileSuspended(reactor.queryClient, thrown)
|
|
449
|
+
throw thrown
|
|
450
|
+
}
|
|
410
451
|
}
|
|
411
452
|
|
|
412
453
|
// Invalidate function
|
|
@@ -445,6 +486,7 @@ const createSuspenseInfiniteQueryImpl = <
|
|
|
445
486
|
invalidate,
|
|
446
487
|
getQueryKey,
|
|
447
488
|
getCacheData,
|
|
489
|
+
...queryCacheControls<TInfiniteData>(reactor, getQueryKey),
|
|
448
490
|
}
|
|
449
491
|
}
|
|
450
492
|
|
|
@@ -504,10 +546,10 @@ export function createSuspenseInfiniteQuery<
|
|
|
504
546
|
*
|
|
505
547
|
* @param reactor - The Reactor instance
|
|
506
548
|
* @param config - Suspense infinite query configuration (without getArgs)
|
|
507
|
-
* @returns A function that accepts getArgs and returns
|
|
549
|
+
* @returns A function that accepts getArgs and returns a SuspenseInfiniteQueryResult
|
|
508
550
|
*
|
|
509
551
|
* @example
|
|
510
|
-
* const getPostsQuery =
|
|
552
|
+
* const getPostsQuery = createSuspenseInfiniteQueryFactory(reactor, {
|
|
511
553
|
* functionName: "get_posts",
|
|
512
554
|
* initialPageParam: 0,
|
|
513
555
|
* getKeyArgs: (args) => {
|
|
@@ -526,6 +568,9 @@ export function createSuspenseInfiniteQuery<
|
|
|
526
568
|
* (cursor) => [{ userId, cursor, limit: 10 }],
|
|
527
569
|
* { queryKey: ["v2"] }
|
|
528
570
|
* )
|
|
571
|
+
*
|
|
572
|
+
* // Every list this factory made, whatever its args and pages
|
|
573
|
+
* await getPostsQuery.invalidate()
|
|
529
574
|
*/
|
|
530
575
|
|
|
531
576
|
export function createSuspenseInfiniteQueryFactory<
|
|
@@ -553,13 +598,7 @@ export function createSuspenseInfiniteQueryFactory<
|
|
|
553
598
|
TPageParam,
|
|
554
599
|
Selected
|
|
555
600
|
> {
|
|
556
|
-
const factory
|
|
557
|
-
Service,
|
|
558
|
-
Method,
|
|
559
|
-
Transform,
|
|
560
|
-
TPageParam,
|
|
561
|
-
Selected
|
|
562
|
-
> = (
|
|
601
|
+
const factory = (
|
|
563
602
|
getArgs: (pageParam: TPageParam) => ReactorArgs<Service, Method, Transform>,
|
|
564
603
|
options?: SuspenseInfiniteFactoryCallOptions
|
|
565
604
|
) => {
|
|
@@ -597,5 +636,16 @@ export function createSuspenseInfiniteQueryFactory<
|
|
|
597
636
|
>)
|
|
598
637
|
}
|
|
599
638
|
|
|
600
|
-
|
|
639
|
+
// What every instance's key starts with: the method, the `callConfig`
|
|
640
|
+
// segments and the config `queryKey`. A per-call `queryKey` and the args
|
|
641
|
+
// segment come after them.
|
|
642
|
+
return withQueryFactoryMethods(factory, reactor, () =>
|
|
643
|
+
reactor.generateQueryKey(
|
|
644
|
+
{
|
|
645
|
+
functionName: config.functionName as Method,
|
|
646
|
+
queryKey: config.queryKey,
|
|
647
|
+
},
|
|
648
|
+
config.callConfig
|
|
649
|
+
)
|
|
650
|
+
)
|
|
601
651
|
}
|
|
@@ -34,9 +34,19 @@ import type {
|
|
|
34
34
|
UseSuspenseQueryWithSelect,
|
|
35
35
|
SuspenseQueryResult,
|
|
36
36
|
SuspenseQueryFactoryConfig,
|
|
37
|
+
QueryFactoryFn,
|
|
37
38
|
NoInfer,
|
|
38
39
|
} from "./types.js"
|
|
39
|
-
import {
|
|
40
|
+
import {
|
|
41
|
+
buildChainedSelect,
|
|
42
|
+
createBoundedCache,
|
|
43
|
+
mountWhileSuspended,
|
|
44
|
+
pickFetchOptions,
|
|
45
|
+
queryCacheControls,
|
|
46
|
+
retryOption,
|
|
47
|
+
useMountQueryClient,
|
|
48
|
+
withQueryFactoryMethods,
|
|
49
|
+
} from "./utils.js"
|
|
40
50
|
|
|
41
51
|
// ============================================================================
|
|
42
52
|
// Internal Implementation
|
|
@@ -61,33 +71,51 @@ const createSuspenseQueryImpl = <
|
|
|
61
71
|
const {
|
|
62
72
|
functionName,
|
|
63
73
|
args,
|
|
74
|
+
callConfig,
|
|
64
75
|
staleTime = 5 * 60 * 1000,
|
|
65
76
|
select,
|
|
66
77
|
queryKey: customQueryKey,
|
|
67
78
|
...rest
|
|
68
79
|
} = config
|
|
69
80
|
|
|
70
|
-
|
|
81
|
+
// `callConfig` goes wherever the hooks send it: to the call and into the
|
|
82
|
+
// key, so a query of another canister or agent has an entry of its own.
|
|
83
|
+
const params = { functionName, args, queryKey: customQueryKey, callConfig }
|
|
71
84
|
|
|
72
|
-
const getQueryKey = () => reactor.generateQueryKey(params)
|
|
85
|
+
const getQueryKey = () => reactor.generateQueryKey(params, callConfig)
|
|
73
86
|
|
|
74
87
|
const applySelect = (raw: TData): Selected =>
|
|
75
88
|
select ? select(raw) : (raw as unknown as Selected)
|
|
76
89
|
|
|
90
|
+
// How the query function runs, shared with the hook; see pickFetchOptions.
|
|
91
|
+
const fetchOptions = pickFetchOptions(rest)
|
|
92
|
+
|
|
77
93
|
/** Cache-first fetch for use in loaders / route preloading. */
|
|
78
94
|
const fetch = async (): Promise<Selected> => {
|
|
79
|
-
|
|
95
|
+
// Through the reactor rather than straight to the QueryClient: overriding
|
|
96
|
+
// `fetchQuery` in a Reactor subclass is a documented way to add logic to
|
|
97
|
+
// every factory fetch.
|
|
98
|
+
const result = await reactor.fetchQuery(params, fetchOptions)
|
|
80
99
|
return applySelect(result)
|
|
81
100
|
}
|
|
82
101
|
|
|
83
102
|
/** Fire-and-forget prefetch — warms the cache without blocking. */
|
|
84
103
|
const prefetch = (): Promise<void> => {
|
|
85
104
|
const baseOptions = reactor.getQueryOptions(params)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
105
|
+
// Runs again when a sign-in or sign-out cancels it, and never rejects;
|
|
106
|
+
// see `prefetch` in createQuery.
|
|
107
|
+
return reactor.clientManager
|
|
108
|
+
.fetchAcrossIdentitySwitch(() =>
|
|
109
|
+
reactor.queryClient.prefetchQuery({
|
|
110
|
+
...fetchOptions,
|
|
111
|
+
// An update method's default `retry`; see `Reactor.getQueryRetry`.
|
|
112
|
+
...retryOption(fetchOptions.retry, baseOptions.retry),
|
|
113
|
+
queryKey: baseOptions.queryKey,
|
|
114
|
+
queryFn: baseOptions.queryFn,
|
|
115
|
+
staleTime,
|
|
116
|
+
})
|
|
117
|
+
)
|
|
118
|
+
.catch(() => undefined)
|
|
91
119
|
}
|
|
92
120
|
|
|
93
121
|
const useSuspenseQueryHook: UseSuspenseQueryWithSelect<
|
|
@@ -95,6 +123,7 @@ const createSuspenseQueryImpl = <
|
|
|
95
123
|
Selected,
|
|
96
124
|
TError
|
|
97
125
|
> = (options: any): any => {
|
|
126
|
+
useMountQueryClient(reactor.queryClient)
|
|
98
127
|
const baseOptions = reactor.getQueryOptions(params)
|
|
99
128
|
// Memoized so the observer's select-result cache can hit; see
|
|
100
129
|
// buildChainedSelect. `select` comes from the factory config and is stable.
|
|
@@ -102,17 +131,25 @@ const createSuspenseQueryImpl = <
|
|
|
102
131
|
() => buildChainedSelect(select, options?.select),
|
|
103
132
|
[options?.select]
|
|
104
133
|
)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
134
|
+
try {
|
|
135
|
+
return useSuspenseQuery(
|
|
136
|
+
{
|
|
137
|
+
queryKey: baseOptions.queryKey,
|
|
138
|
+
staleTime,
|
|
139
|
+
...rest,
|
|
140
|
+
...options,
|
|
141
|
+
queryFn: baseOptions.queryFn,
|
|
142
|
+
select: chainedSelect,
|
|
143
|
+
// The hook's `retry`, else the config's, else an update method's
|
|
144
|
+
// default; see `Reactor.getQueryRetry`.
|
|
145
|
+
...retryOption(options?.retry ?? rest.retry, baseOptions.retry),
|
|
146
|
+
},
|
|
147
|
+
reactor.queryClient
|
|
148
|
+
)
|
|
149
|
+
} catch (thrown) {
|
|
150
|
+
mountWhileSuspended(reactor.queryClient, thrown)
|
|
151
|
+
throw thrown
|
|
152
|
+
}
|
|
116
153
|
}
|
|
117
154
|
|
|
118
155
|
const invalidate = async (): Promise<void> => {
|
|
@@ -124,7 +161,7 @@ const createSuspenseQueryImpl = <
|
|
|
124
161
|
Selected,
|
|
125
162
|
TError
|
|
126
163
|
>["getCacheData"] = (selectFn?: (data: Selected) => unknown): any => {
|
|
127
|
-
const raw = reactor.getQueryData(params)
|
|
164
|
+
const raw = reactor.getQueryData(params, callConfig)
|
|
128
165
|
if (raw === undefined) return undefined
|
|
129
166
|
const selected = applySelect(raw)
|
|
130
167
|
return selectFn ? selectFn(selected) : selected
|
|
@@ -145,6 +182,7 @@ const createSuspenseQueryImpl = <
|
|
|
145
182
|
getQueryKey,
|
|
146
183
|
getCacheData,
|
|
147
184
|
setData,
|
|
185
|
+
...queryCacheControls<TData>(reactor, getQueryKey),
|
|
148
186
|
}
|
|
149
187
|
}
|
|
150
188
|
|
|
@@ -175,6 +213,27 @@ export function createSuspenseQuery<
|
|
|
175
213
|
// Convenience: Create suspense query with dynamic args
|
|
176
214
|
// ============================================================================
|
|
177
215
|
|
|
216
|
+
/**
|
|
217
|
+
* Create a suspense query factory: a function that takes the method's args
|
|
218
|
+
* and returns the suspense query object for them, the same object for the
|
|
219
|
+
* same args.
|
|
220
|
+
*
|
|
221
|
+
* The function also has `getQueryKey()`, the key prefix every query it
|
|
222
|
+
* returns shares, and `invalidate()`, which invalidates all of them whatever
|
|
223
|
+
* their args. Pass the function itself to a mutation's `invalidateQueries` to
|
|
224
|
+
* refresh every instance after the mutation.
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* const getBalance = createSuspenseQueryFactory(ledger, {
|
|
228
|
+
* functionName: "icrc1_balance_of",
|
|
229
|
+
* })
|
|
230
|
+
*
|
|
231
|
+
* // In a component under <Suspense>
|
|
232
|
+
* const { data } = getBalance([{ owner, subaccount: [] }]).useSuspenseQuery()
|
|
233
|
+
*
|
|
234
|
+
* // Refetch every account's balance
|
|
235
|
+
* await getBalance.invalidate()
|
|
236
|
+
*/
|
|
178
237
|
export function createSuspenseQueryFactory<
|
|
179
238
|
Service,
|
|
180
239
|
Transform extends TransformKey,
|
|
@@ -188,12 +247,13 @@ export function createSuspenseQueryFactory<
|
|
|
188
247
|
Transform,
|
|
189
248
|
Selected
|
|
190
249
|
>
|
|
191
|
-
):
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
250
|
+
): QueryFactoryFn<
|
|
251
|
+
ReactorArgs<Service, Method, Transform>,
|
|
252
|
+
SuspenseQueryResult<
|
|
253
|
+
QueryFnData<Service, Method, Transform>,
|
|
254
|
+
Selected,
|
|
255
|
+
QueryError<Service, Method, Transform>
|
|
256
|
+
>
|
|
197
257
|
> {
|
|
198
258
|
const cache =
|
|
199
259
|
createBoundedCache<
|
|
@@ -204,11 +264,11 @@ export function createSuspenseQueryFactory<
|
|
|
204
264
|
>
|
|
205
265
|
>()
|
|
206
266
|
|
|
207
|
-
|
|
208
|
-
const key = reactor.generateQueryKey(
|
|
209
|
-
functionName: config.functionName as Method,
|
|
210
|
-
|
|
211
|
-
|
|
267
|
+
const factory = (args: ReactorArgs<Service, Method, Transform>) => {
|
|
268
|
+
const key = reactor.generateQueryKey(
|
|
269
|
+
{ functionName: config.functionName as Method, args },
|
|
270
|
+
config.callConfig
|
|
271
|
+
)
|
|
212
272
|
const cacheKey = JSON.stringify(key)
|
|
213
273
|
|
|
214
274
|
const existing = cache.get(cacheKey)
|
|
@@ -231,4 +291,14 @@ export function createSuspenseQueryFactory<
|
|
|
231
291
|
cache.set(cacheKey, result)
|
|
232
292
|
return result
|
|
233
293
|
}
|
|
294
|
+
|
|
295
|
+
// The method's own prefix, at the canister and agent the config's
|
|
296
|
+
// `callConfig` names. A config `queryKey` follows the args segment in every
|
|
297
|
+
// instance's key, so it cannot narrow the prefix.
|
|
298
|
+
return withQueryFactoryMethods(factory, reactor, () =>
|
|
299
|
+
reactor.generateQueryKey(
|
|
300
|
+
{ functionName: config.functionName as Method },
|
|
301
|
+
config.callConfig
|
|
302
|
+
)
|
|
303
|
+
)
|
|
234
304
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { DisplayReactor } from "@ic-reactor/core"
|
|
2
|
+
import type { BaseActor, DisplayReactorParameters } from "@ic-reactor/core"
|
|
3
|
+
import { defineReactorWith } from "./defineReactorShared.js"
|
|
4
|
+
import type {
|
|
5
|
+
DefineReactorSharedParameters,
|
|
6
|
+
DefineReactorResult,
|
|
7
|
+
} from "./defineReactorShared.js"
|
|
8
|
+
|
|
9
|
+
/** Options for {@link defineDisplayReactor}: `defineReactor`'s, plus validators. */
|
|
10
|
+
export interface DefineDisplayReactorOptions<
|
|
11
|
+
Service,
|
|
12
|
+
> extends DefineReactorSharedParameters {
|
|
13
|
+
/** Optional initial argument validators (receive display types). */
|
|
14
|
+
validators?: DisplayReactorParameters<Service>["validators"]
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* One-call bootstrap for a {@link DisplayReactor} and its React hooks: the
|
|
19
|
+
* display counterpart of `defineReactor`, taking the same options.
|
|
20
|
+
*
|
|
21
|
+
* A DisplayReactor takes and returns UI-friendly values — text for
|
|
22
|
+
* `Principal`, `nat`, `int` and 64-bit integers, hex for blobs — which suits
|
|
23
|
+
* forms and components. Its codecs are built on zod, so this adds zod to the
|
|
24
|
+
* bundle. `defineReactor`, which builds a plain `Reactor` with raw Candid
|
|
25
|
+
* values, does not need zod, but until its deprecated `display` flag is removed
|
|
26
|
+
* at the next major it bundles zod anyway; `createActorHooks(new Reactor(...))`
|
|
27
|
+
* is the setup without it.
|
|
28
|
+
*
|
|
29
|
+
* Replaces `defineReactor({ display: true })`, which is deprecated.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* import { defineDisplayReactor } from "@ic-reactor/react"
|
|
34
|
+
* import { idlFactory, type _SERVICE } from "./declarations/backend"
|
|
35
|
+
*
|
|
36
|
+
* export const { reactor, useActorQuery, useActorMutation, useAuth } =
|
|
37
|
+
* defineDisplayReactor<_SERVICE>({
|
|
38
|
+
* name: "backend",
|
|
39
|
+
* idlFactory,
|
|
40
|
+
* canisterId: "rrkah-fqaaa-aaaaa-aaaaq-cai",
|
|
41
|
+
* })
|
|
42
|
+
*
|
|
43
|
+
* function Balance() {
|
|
44
|
+
* // `nat` arrives as text, ready to render.
|
|
45
|
+
* const { data } = useActorQuery({ functionName: "get_balance" })
|
|
46
|
+
* return <span>{data}</span>
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export function defineDisplayReactor<Service = BaseActor>(
|
|
51
|
+
params: DefineDisplayReactorOptions<Service>
|
|
52
|
+
): DefineReactorResult<Service, "display", DisplayReactor<Service>> {
|
|
53
|
+
return defineReactorWith<Service, "display", DisplayReactor<Service>>(
|
|
54
|
+
"defineDisplayReactor",
|
|
55
|
+
params,
|
|
56
|
+
(config) =>
|
|
57
|
+
new DisplayReactor<Service>({
|
|
58
|
+
...config,
|
|
59
|
+
validators: params.validators,
|
|
60
|
+
})
|
|
61
|
+
)
|
|
62
|
+
}
|