@ic-reactor/react 3.13.0 → 4.0.0-beta.2
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 +237 -787
- package/dist/index.d.ts +260 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +471 -23
- package/dist/index.js.map +1 -1
- package/llms.txt +82 -278
- package/package.json +11 -39
- package/src/index.tsx +612 -0
- package/dist/auth/auth-client-compat.d.ts +0 -122
- package/dist/auth/auth-client-compat.d.ts.map +0 -1
- package/dist/auth/auth-client-compat.js +0 -162
- package/dist/auth/auth-client-compat.js.map +0 -1
- package/dist/auth/authentication-manager.d.ts +0 -405
- package/dist/auth/authentication-manager.d.ts.map +0 -1
- package/dist/auth/authentication-manager.js +0 -1537
- package/dist/auth/authentication-manager.js.map +0 -1
- package/dist/auth/constants.d.ts +0 -24
- package/dist/auth/constants.d.ts.map +0 -1
- package/dist/auth/constants.js +0 -24
- package/dist/auth/constants.js.map +0 -1
- package/dist/auth/createIdentityAttributeHooks.d.ts +0 -14
- package/dist/auth/createIdentityAttributeHooks.d.ts.map +0 -1
- package/dist/auth/createIdentityAttributeHooks.js +0 -122
- package/dist/auth/createIdentityAttributeHooks.js.map +0 -1
- package/dist/auth/identity-attributes-manager.d.ts +0 -27
- package/dist/auth/identity-attributes-manager.d.ts.map +0 -1
- package/dist/auth/identity-attributes-manager.js +0 -191
- package/dist/auth/identity-attributes-manager.js.map +0 -1
- package/dist/auth/identity-attributes.d.ts +0 -19
- package/dist/auth/identity-attributes.d.ts.map +0 -1
- package/dist/auth/identity-attributes.js +0 -227
- package/dist/auth/identity-attributes.js.map +0 -1
- package/dist/auth/index.d.ts +0 -8
- package/dist/auth/index.d.ts.map +0 -1
- package/dist/auth/index.js +0 -8
- package/dist/auth/index.js.map +0 -1
- package/dist/auth/local-ii-probe.d.ts +0 -57
- package/dist/auth/local-ii-probe.d.ts.map +0 -1
- package/dist/auth/local-ii-probe.js +0 -121
- package/dist/auth/local-ii-probe.js.map +0 -1
- package/dist/auth/types.d.ts +0 -222
- package/dist/auth/types.d.ts.map +0 -1
- package/dist/auth/types.js +0 -2
- package/dist/auth/types.js.map +0 -1
- package/dist/createActorHooks.d.ts +0 -41
- package/dist/createActorHooks.d.ts.map +0 -1
- package/dist/createActorHooks.js +0 -17
- package/dist/createActorHooks.js.map +0 -1
- package/dist/createInfiniteQuery.d.ts +0 -185
- package/dist/createInfiniteQuery.d.ts.map +0 -1
- package/dist/createInfiniteQuery.js +0 -198
- package/dist/createInfiniteQuery.js.map +0 -1
- package/dist/createMutation.d.ts +0 -33
- package/dist/createMutation.d.ts.map +0 -1
- package/dist/createMutation.js +0 -199
- package/dist/createMutation.js.map +0 -1
- package/dist/createQuery.d.ts +0 -63
- package/dist/createQuery.d.ts.map +0 -1
- package/dist/createQuery.js +0 -204
- package/dist/createQuery.js.map +0 -1
- package/dist/createReactorProvider.d.ts +0 -158
- package/dist/createReactorProvider.d.ts.map +0 -1
- package/dist/createReactorProvider.js +0 -256
- package/dist/createReactorProvider.js.map +0 -1
- package/dist/createSuspenseInfiniteQuery.d.ts +0 -154
- package/dist/createSuspenseInfiniteQuery.d.ts.map +0 -1
- package/dist/createSuspenseInfiniteQuery.js +0 -209
- package/dist/createSuspenseInfiniteQuery.js.map +0 -1
- package/dist/createSuspenseQuery.d.ts +0 -46
- package/dist/createSuspenseQuery.d.ts.map +0 -1
- package/dist/createSuspenseQuery.js +0 -158
- package/dist/createSuspenseQuery.js.map +0 -1
- package/dist/defineDisplayReactor.d.ts +0 -43
- package/dist/defineDisplayReactor.d.ts.map +0 -1
- package/dist/defineDisplayReactor.js +0 -42
- package/dist/defineDisplayReactor.js.map +0 -1
- package/dist/defineReactor.d.ts +0 -99
- package/dist/defineReactor.d.ts.map +0 -1
- package/dist/defineReactor.js +0 -15
- package/dist/defineReactor.js.map +0 -1
- package/dist/defineReactorShared.d.ts +0 -84
- package/dist/defineReactorShared.d.ts.map +0 -1
- package/dist/defineReactorShared.js +0 -139
- package/dist/defineReactorShared.js.map +0 -1
- package/dist/hooks/createAuthHooks.d.ts +0 -50
- package/dist/hooks/createAuthHooks.d.ts.map +0 -1
- package/dist/hooks/createAuthHooks.js +0 -291
- package/dist/hooks/createAuthHooks.js.map +0 -1
- package/dist/hooks/index.d.ts +0 -21
- package/dist/hooks/index.d.ts.map +0 -1
- package/dist/hooks/index.js +0 -24
- package/dist/hooks/index.js.map +0 -1
- package/dist/hooks/useActorInfiniteQuery.d.ts +0 -67
- package/dist/hooks/useActorInfiniteQuery.d.ts.map +0 -1
- package/dist/hooks/useActorInfiniteQuery.js +0 -89
- package/dist/hooks/useActorInfiniteQuery.js.map +0 -1
- package/dist/hooks/useActorMethod.d.ts +0 -148
- package/dist/hooks/useActorMethod.d.ts.map +0 -1
- package/dist/hooks/useActorMethod.js +0 -394
- package/dist/hooks/useActorMethod.js.map +0 -1
- package/dist/hooks/useActorMutation.d.ts +0 -51
- package/dist/hooks/useActorMutation.d.ts.map +0 -1
- package/dist/hooks/useActorMutation.js +0 -70
- package/dist/hooks/useActorMutation.js.map +0 -1
- package/dist/hooks/useActorQuery.d.ts +0 -45
- package/dist/hooks/useActorQuery.d.ts.map +0 -1
- package/dist/hooks/useActorQuery.js +0 -67
- package/dist/hooks/useActorQuery.js.map +0 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +0 -51
- package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +0 -1
- package/dist/hooks/useActorSuspenseInfiniteQuery.js +0 -76
- package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +0 -1
- package/dist/hooks/useActorSuspenseQuery.d.ts +0 -32
- package/dist/hooks/useActorSuspenseQuery.d.ts.map +0 -1
- package/dist/hooks/useActorSuspenseQuery.js +0 -58
- package/dist/hooks/useActorSuspenseQuery.js.map +0 -1
- package/dist/ownedAuthentication.d.ts +0 -52
- package/dist/ownedAuthentication.d.ts.map +0 -1
- package/dist/ownedAuthentication.js +0 -49
- package/dist/ownedAuthentication.js.map +0 -1
- package/dist/server.d.ts +0 -21
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js +0 -23
- package/dist/server.js.map +0 -1
- package/dist/testing.d.ts +0 -19
- package/dist/testing.d.ts.map +0 -1
- package/dist/testing.js +0 -19
- package/dist/testing.js.map +0 -1
- package/dist/types.d.ts +0 -671
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js +0 -5
- package/dist/types.js.map +0 -1
- package/dist/utils.d.ts +0 -207
- package/dist/utils.d.ts.map +0 -1
- package/dist/utils.js +0 -405
- package/dist/utils.js.map +0 -1
- package/dist/validation.d.ts +0 -136
- package/dist/validation.d.ts.map +0 -1
- package/dist/validation.js +0 -144
- package/dist/validation.js.map +0 -1
- package/src/auth/auth-client-compat.ts +0 -273
- package/src/auth/authentication-manager.ts +0 -1682
- package/src/auth/constants.ts +0 -32
- package/src/auth/createIdentityAttributeHooks.ts +0 -169
- package/src/auth/identity-attributes-manager.ts +0 -226
- package/src/auth/identity-attributes.ts +0 -345
- package/src/auth/index.ts +0 -7
- package/src/auth/local-ii-probe.ts +0 -173
- package/src/auth/types.ts +0 -243
- package/src/createActorHooks.ts +0 -208
- package/src/createInfiniteQuery.ts +0 -670
- package/src/createMutation.ts +0 -324
- package/src/createQuery.ts +0 -369
- package/src/createReactorProvider.ts +0 -365
- package/src/createSuspenseInfiniteQuery.ts +0 -651
- package/src/createSuspenseQuery.ts +0 -304
- package/src/defineDisplayReactor.ts +0 -62
- package/src/defineReactor.ts +0 -142
- package/src/defineReactorShared.ts +0 -268
- package/src/hooks/createAuthHooks.ts +0 -371
- package/src/hooks/index.ts +0 -103
- package/src/hooks/useActorInfiniteQuery.ts +0 -278
- package/src/hooks/useActorMethod.ts +0 -710
- package/src/hooks/useActorMutation.ts +0 -205
- package/src/hooks/useActorQuery.ts +0 -157
- package/src/hooks/useActorSuspenseInfiniteQuery.ts +0 -248
- package/src/hooks/useActorSuspenseQuery.ts +0 -147
- package/src/index.ts +0 -31
- package/src/ownedAuthentication.ts +0 -81
- package/src/server.ts +0 -23
- package/src/testing.ts +0 -18
- package/src/types.ts +0 -948
- package/src/utils.ts +0 -505
- package/src/validation.ts +0 -226
package/dist/createQuery.js
DELETED
|
@@ -1,204 +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
|
-
import { useMemo } from "react";
|
|
27
|
-
import { useQuery, skipToken, } from "@tanstack/react-query";
|
|
28
|
-
import { buildChainedSelect, createBoundedCache, pickFetchOptions, queryCacheControls, retryOption, skippedQueryKey, useMountQueryClient, withQueryFactoryMethods, } from "./utils.js";
|
|
29
|
-
// ============================================================================
|
|
30
|
-
// Internal Implementation
|
|
31
|
-
// ============================================================================
|
|
32
|
-
const createQueryImpl = (reactor,
|
|
33
|
-
// `args` is `skipToken` only for a factory's skipped query, which exposes
|
|
34
|
-
// nothing but `useQuery`; see createQueryFactory.
|
|
35
|
-
config) => {
|
|
36
|
-
const { functionName, args, callConfig, staleTime = 5 * 60 * 1000, select, queryKey: customQueryKey, ...rest } = config;
|
|
37
|
-
const skipped = args === skipToken;
|
|
38
|
-
// `callConfig` goes wherever the hooks send it: to the call and into the
|
|
39
|
-
// key, so a query of another canister or agent has an entry of its own.
|
|
40
|
-
const params = {
|
|
41
|
-
functionName,
|
|
42
|
-
args: skipped ? undefined : args,
|
|
43
|
-
queryKey: customQueryKey,
|
|
44
|
-
callConfig,
|
|
45
|
-
};
|
|
46
|
-
// What the hook observes: the call, or for a query still waiting for its
|
|
47
|
-
// args, an entry of its own under the method's key, with nothing to run;
|
|
48
|
-
// see skippedQueryKey.
|
|
49
|
-
const queryOptions = () => skipped
|
|
50
|
-
? {
|
|
51
|
-
queryKey: skippedQueryKey(reactor.generateQueryKey({ functionName }, callConfig), "query"),
|
|
52
|
-
// Kept as the unique symbol, which an object literal widens.
|
|
53
|
-
queryFn: skipToken,
|
|
54
|
-
retry: undefined,
|
|
55
|
-
}
|
|
56
|
-
: reactor.getQueryOptions(params);
|
|
57
|
-
const getQueryKey = () => reactor.generateQueryKey(params, callConfig);
|
|
58
|
-
// Apply config.select to raw data (shared by fetch, getCacheData, and the hook)
|
|
59
|
-
const applySelect = (raw) => select ? select(raw) : raw;
|
|
60
|
-
// How the query function runs, shared with the hook; see pickFetchOptions.
|
|
61
|
-
const fetchOptions = pickFetchOptions(rest);
|
|
62
|
-
/** Cache-first fetch for use in loaders / route preloading. */
|
|
63
|
-
const fetch = async () => {
|
|
64
|
-
// Through the reactor rather than straight to the QueryClient: overriding
|
|
65
|
-
// `fetchQuery` in a Reactor subclass is a documented way to add logic to
|
|
66
|
-
// every factory fetch.
|
|
67
|
-
const result = await reactor.fetchQuery(params, fetchOptions);
|
|
68
|
-
return applySelect(result);
|
|
69
|
-
};
|
|
70
|
-
/** Fire-and-forget prefetch — warms the cache without blocking. */
|
|
71
|
-
const prefetch = () => {
|
|
72
|
-
const baseOptions = reactor.getQueryOptions(params);
|
|
73
|
-
// A sign-in or sign-out cancels a prefetch in flight, and TanStack
|
|
74
|
-
// resolves it all the same: an entry nothing observes was left empty, and
|
|
75
|
-
// one a mounted query shows still held the previous principal's data
|
|
76
|
-
// until that query's refetch landed. It now runs again for the principal
|
|
77
|
-
// signed in, as `fetch()` does. Like `prefetchQuery` it never rejects, so
|
|
78
|
-
// the CallError for a principal that keeps switching is dropped too.
|
|
79
|
-
return reactor.clientManager
|
|
80
|
-
.fetchAcrossIdentitySwitch(() => reactor.queryClient.prefetchQuery({
|
|
81
|
-
...fetchOptions,
|
|
82
|
-
// An update method's default `retry`; see `Reactor.getQueryRetry`.
|
|
83
|
-
...retryOption(fetchOptions.retry, baseOptions.retry),
|
|
84
|
-
queryKey: baseOptions.queryKey,
|
|
85
|
-
queryFn: baseOptions.queryFn,
|
|
86
|
-
staleTime,
|
|
87
|
-
}))
|
|
88
|
-
.catch(() => undefined);
|
|
89
|
-
};
|
|
90
|
-
const useQueryHook = ((options) => {
|
|
91
|
-
useMountQueryClient(reactor.queryClient);
|
|
92
|
-
const baseOptions = queryOptions();
|
|
93
|
-
// Memoized so the observer's select-result cache can hit; see
|
|
94
|
-
// buildChainedSelect. `select` comes from the factory config and is stable.
|
|
95
|
-
const chainedSelect = useMemo(() => buildChainedSelect(select, options?.select), [options?.select]);
|
|
96
|
-
return useQuery({
|
|
97
|
-
queryKey: baseOptions.queryKey,
|
|
98
|
-
staleTime,
|
|
99
|
-
...rest,
|
|
100
|
-
...options,
|
|
101
|
-
queryFn: baseOptions.queryFn,
|
|
102
|
-
select: chainedSelect,
|
|
103
|
-
// The hook's `retry`, else the config's, else an update method's
|
|
104
|
-
// default; see `Reactor.getQueryRetry`.
|
|
105
|
-
...retryOption(options?.retry ?? rest.retry, baseOptions.retry),
|
|
106
|
-
}, reactor.queryClient);
|
|
107
|
-
});
|
|
108
|
-
const invalidate = async () => {
|
|
109
|
-
await reactor.queryClient.invalidateQueries({ queryKey: getQueryKey() });
|
|
110
|
-
};
|
|
111
|
-
const getCacheData = ((selectFn) => {
|
|
112
|
-
const raw = reactor.getQueryData(params, callConfig);
|
|
113
|
-
if (raw === undefined)
|
|
114
|
-
return undefined;
|
|
115
|
-
const selected = applySelect(raw);
|
|
116
|
-
return selectFn ? selectFn(selected) : selected;
|
|
117
|
-
});
|
|
118
|
-
const setData = (updater) => {
|
|
119
|
-
return reactor.queryClient.setQueryData(getQueryKey(), updater);
|
|
120
|
-
};
|
|
121
|
-
return {
|
|
122
|
-
fetch,
|
|
123
|
-
prefetch,
|
|
124
|
-
useQuery: useQueryHook,
|
|
125
|
-
invalidate,
|
|
126
|
-
getQueryKey,
|
|
127
|
-
getCacheData,
|
|
128
|
-
setData,
|
|
129
|
-
...queryCacheControls(reactor, getQueryKey),
|
|
130
|
-
};
|
|
131
|
-
};
|
|
132
|
-
// ============================================================================
|
|
133
|
-
// Public Factory Function
|
|
134
|
-
// ============================================================================
|
|
135
|
-
export function createQuery(reactor, config) {
|
|
136
|
-
return createQueryImpl(reactor, config);
|
|
137
|
-
}
|
|
138
|
-
// ============================================================================
|
|
139
|
-
// Convenience: Create query with dynamic args
|
|
140
|
-
// ============================================================================
|
|
141
|
-
/**
|
|
142
|
-
* Create a query factory: a function that takes the method's args and returns
|
|
143
|
-
* the query object for them, the same object for the same args.
|
|
144
|
-
*
|
|
145
|
-
* The function also has `getQueryKey()`, the key prefix every query it
|
|
146
|
-
* returns shares, and `invalidate()`, which invalidates all of them whatever
|
|
147
|
-
* their args. Pass the function itself to a mutation's `invalidateQueries` to
|
|
148
|
-
* refresh every instance after the mutation.
|
|
149
|
-
*
|
|
150
|
-
* It also takes TanStack Query's `skipToken` in place of args, for a
|
|
151
|
-
* component whose args are not known yet, and returns a query with only
|
|
152
|
-
* `useQuery()`, which waits without fetching; see
|
|
153
|
-
* {@link SkippableQueryFactoryFn}.
|
|
154
|
-
*
|
|
155
|
-
* @example
|
|
156
|
-
* const getBalance = createQueryFactory(ledger, {
|
|
157
|
-
* functionName: "icrc1_balance_of",
|
|
158
|
-
* })
|
|
159
|
-
*
|
|
160
|
-
* // In a component
|
|
161
|
-
* const { data } = getBalance([{ owner, subaccount: [] }]).useQuery()
|
|
162
|
-
*
|
|
163
|
-
* // In a component whose owner may not be known yet
|
|
164
|
-
* const { data: maybe } = getBalance(
|
|
165
|
-
* owner ? [{ owner, subaccount: [] }] : skipToken
|
|
166
|
-
* ).useQuery()
|
|
167
|
-
*
|
|
168
|
-
* // Refetch every account's balance after a transfer
|
|
169
|
-
* const transfer = createMutation(ledger, {
|
|
170
|
-
* functionName: "icrc1_transfer",
|
|
171
|
-
* invalidateQueries: [getBalance],
|
|
172
|
-
* })
|
|
173
|
-
*/
|
|
174
|
-
export function createQueryFactory(reactor, config) {
|
|
175
|
-
const cache = createBoundedCache();
|
|
176
|
-
// One skipped query per factory, built on first use. Only its hook is
|
|
177
|
-
// handed out: until the args are known there is no call to make and no
|
|
178
|
-
// entry of their own to read or write.
|
|
179
|
-
let skippedQuery;
|
|
180
|
-
function factory(args) {
|
|
181
|
-
if (args === skipToken) {
|
|
182
|
-
skippedQuery ?? (skippedQuery = {
|
|
183
|
-
useQuery: createQueryImpl(reactor, { ...config, args: skipToken }).useQuery,
|
|
184
|
-
});
|
|
185
|
-
return skippedQuery;
|
|
186
|
-
}
|
|
187
|
-
const key = reactor.generateQueryKey({ functionName: config.functionName, args }, config.callConfig);
|
|
188
|
-
const cacheKey = JSON.stringify(key);
|
|
189
|
-
const existing = cache.get(cacheKey);
|
|
190
|
-
if (existing)
|
|
191
|
-
return existing;
|
|
192
|
-
const result = createQueryImpl(reactor, {
|
|
193
|
-
...config,
|
|
194
|
-
args,
|
|
195
|
-
});
|
|
196
|
-
cache.set(cacheKey, result);
|
|
197
|
-
return result;
|
|
198
|
-
}
|
|
199
|
-
// The method's own prefix, at the canister and agent the config's
|
|
200
|
-
// `callConfig` names. A config `queryKey` follows the args segment in every
|
|
201
|
-
// instance's key, so it cannot narrow the prefix.
|
|
202
|
-
return withQueryFactoryMethods(factory, reactor, () => reactor.generateQueryKey({ functionName: config.functionName }, config.callConfig));
|
|
203
|
-
}
|
|
204
|
-
//# sourceMappingURL=createQuery.js.map
|
package/dist/createQuery.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createQuery.js","sourceRoot":"","sources":["../src/createQuery.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAQH,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,EAEL,QAAQ,EACR,SAAS,GAIV,MAAM,uBAAuB,CAAA;AAa9B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,uBAAuB,GACxB,MAAM,YAAY,CAAA;AAEnB,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E,MAAM,eAAe,GAAG,CAMtB,OAAoC;AACpC,0EAA0E;AAC1E,kDAAkD;AAClD,MAAkE,EAKlE,EAAE;IAIF,MAAM,EACJ,YAAY,EACZ,IAAI,EACJ,UAAU,EACV,SAAS,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,EACzB,MAAM,EACN,QAAQ,EAAE,cAAc,EACxB,GAAG,IAAI,EACR,GAAG,MAAM,CAAA;IAEV,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS,CAAA;IAElC,yEAAyE;IACzE,wEAAwE;IACxE,MAAM,MAAM,GAAG;QACb,YAAY;QACZ,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI;QAChC,QAAQ,EAAE,cAAc;QACxB,UAAU;KACX,CAAA;IAED,yEAAyE;IACzE,yEAAyE;IACzE,uBAAuB;IACvB,MAAM,YAAY,GAAG,GAAG,EAAE,CACxB,OAAO;QACL,CAAC,CAAC;YACE,QAAQ,EAAE,eAAe,CACvB,OAAO,CAAC,gBAAgB,CAAC,EAAE,YAAY,EAAE,EAAE,UAAU,CAAC,EACtD,OAAO,CACR;YACD,6DAA6D;YAC7D,OAAO,EAAE,SAAsB;YAC/B,KAAK,EAAE,SAAS;SACjB;QACH,CAAC,CAAC,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAA;IAErC,MAAM,WAAW,GAAG,GAAa,EAAE,CACjC,OAAO,CAAC,gBAAgB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAE9C,gFAAgF;IAChF,MAAM,WAAW,GAAG,CAAC,GAAU,EAAY,EAAE,CAC3C,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAE,GAA2B,CAAA;IAErD,2EAA2E;IAC3E,MAAM,YAAY,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAA;IAE3C,+DAA+D;IAC/D,MAAM,KAAK,GAAG,KAAK,IAAuB,EAAE;QAC1C,0EAA0E;QAC1E,yEAAyE;QACzE,uBAAuB;QACvB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;QAC7D,OAAO,WAAW,CAAC,MAAM,CAAC,CAAA;IAC5B,CAAC,CAAA;IAED,mEAAmE;IACnE,MAAM,QAAQ,GAAG,GAAkB,EAAE;QACnC,MAAM,WAAW,GAAG,OAAO,CAAC,eAAe,CAAC,MAAM,CAAC,CAAA;QACnD,mEAAmE;QACnE,0EAA0E;QAC1E,qEAAqE;QACrE,yEAAyE;QACzE,0EAA0E;QAC1E,qEAAqE;QACrE,OAAO,OAAO,CAAC,aAAa;aACzB,yBAAyB,CAAC,GAAG,EAAE,CAC9B,OAAO,CAAC,WAAW,CAAC,aAAa,CAAC;YAChC,GAAG,YAAY;YACf,mEAAmE;YACnE,GAAG,WAAW,CAAC,YAAY,CAAC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;YACrD,QAAQ,EAAE,WAAW,CAAC,QAAQ;YAC9B,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,SAAS;SACV,CAAC,CACH;aACA,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;IAC3B,CAAC,CAAA;IAUD,MAAM,YAAY,GAAG,CAAC,CAAC,OAA6B,EAAE,EAAE;QACtD,mBAAmB,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QACxC,MAAM,WAAW,GAAG,YAAY,EAAE,CAAA;QAClC,8DAA8D;QAC9D,4EAA4E;QAC5E,MAAM,aAAa,GAAG,OAAO,CAC3B,GAAG,EAAE,CAAC,kBAAkB,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,EACjD,CAAC,OAAO,EAAE,MAAM,CAAC,CAClB,CAAA;QACD,OAAO,QAAQ,CACb;YACE,QAAQ,EAAE,WAAW,CAAC,QAAQ;YAC9B,SAAS;YACT,GAAG,IAAI;YACP,GAAG,OAAO;YACV,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,MAAM,EAAE,aAAa;YACrB,iEAAiE;YACjE,wCAAwC;YACxC,GAAG,WAAW,CAAC,OAAO,EAAE,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;SAChE,EACD,OAAO,CAAC,WAAW,CACpB,CAAA;IACH,CAAC,CAAgD,CAAA;IAEjD,MAAM,UAAU,GAAG,KAAK,IAAmB,EAAE;QAC3C,MAAM,OAAO,CAAC,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,WAAW,EAAE,EAAE,CAAC,CAAA;IAC1E,CAAC,CAAA;IAED,MAAM,YAAY,GAAG,CAAC,CACpB,QAAsC,EAClB,EAAE;QACtB,MAAM,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;QACpD,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,SAAS,CAAA;QACvC,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;QACjC,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;IACjD,CAAC,CAAyD,CAAA;IAE1D,MAAM,OAAO,GAAoD,CAC/D,OAAO,EACP,EAAE;QACF,OAAO,OAAO,CAAC,WAAW,CAAC,YAAY,CACrC,WAAW,EAAE,EACb,OAAwD,CACpC,CAAA;IACxB,CAAC,CAAA;IAED,OAAO;QACL,KAAK;QACL,QAAQ;QACR,QAAQ,EAAE,YAAY;QACtB,UAAU;QACV,WAAW;QACX,YAAY;QACZ,OAAO;QACP,GAAG,kBAAkB,CAAQ,OAAO,EAAE,WAAW,CAAC;KACnD,CAAA;AACH,CAAC,CAAA;AAED,+EAA+E;AAC/E,0BAA0B;AAC1B,+EAA+E;AAE/E,MAAM,UAAU,WAAW,CAMzB,OAAoC,EACpC,MAAkE;IAMlE,OAAO,eAAe,CACpB,OAAO,EACP,MAA2D,CAC5D,CAAA;AACH,CAAC;AAED,+EAA+E;AAC/E,8CAA8C;AAC9C,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,MAAM,UAAU,kBAAkB,CAMhC,OAAoC,EACpC,MAAyE;IAezE,MAAM,KAAK,GAAG,kBAAkB,EAAS,CAAA;IAEzC,sEAAsE;IACtE,uEAAuE;IACvE,uCAAuC;IACvC,IAAI,YAA6C,CAAA;IAOjD,SAAS,OAAO,CACd,IAAyD;QAEzD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,YAAY,KAAZ,YAAY,GAAK;gBACf,QAAQ,EAAE,eAAe,CACvB,OAAO,EACP,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,CAC/B,CAAC,QAAQ;aACX,EAAA;YACD,OAAO,YAAY,CAAA;QACrB,CAAC;QAED,MAAM,GAAG,GAAG,OAAO,CAAC,gBAAgB,CAClC,EAAE,YAAY,EAAE,MAAM,CAAC,YAAsB,EAAE,IAAI,EAAE,EACrD,MAAM,CAAC,UAAU,CAClB,CAAA;QACD,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAA;QAEpC,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACpC,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAA;QAE7B,MAAM,MAAM,GAAG,eAAe,CAC5B,OAAO,EACP;YACE,GAAG,MAAM;YACT,IAAI;SACL,CACF,CAAA;QACD,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QAC3B,OAAO,MAAM,CAAA;IACf,CAAC;IAED,kEAAkE;IAClE,4EAA4E;IAC5E,kDAAkD;IAClD,OAAO,uBAAuB,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,CACpD,OAAO,CAAC,gBAAgB,CACtB,EAAE,YAAY,EAAE,MAAM,CAAC,YAAsB,EAAE,EAC/C,MAAM,CAAC,UAAU,CAClB,CACF,CAAA;AACH,CAAC"}
|
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import type { ReactElement, ReactNode } from "react";
|
|
2
|
-
/**
|
|
3
|
-
* Props of the provider {@link createReactorProvider} returns: `children`, and
|
|
4
|
-
* the props its factory takes.
|
|
5
|
-
*/
|
|
6
|
-
export type ReactorProviderProps<TProps extends object = {}> = TProps & {
|
|
7
|
-
children?: ReactNode;
|
|
8
|
-
};
|
|
9
|
-
/** Options for {@link createReactorProvider}. */
|
|
10
|
-
export interface CreateReactorProviderOptions {
|
|
11
|
-
/**
|
|
12
|
-
* Render a `QueryClientProvider` for the QueryClient the built value uses,
|
|
13
|
-
* so that `useQueryClient()`, React Query Devtools and `HydrationBoundary`
|
|
14
|
-
* below the provider read the cache the reactor hooks fill. The reactor
|
|
15
|
-
* hooks do not need it: each binds to its reactor's own QueryClient.
|
|
16
|
-
*
|
|
17
|
-
* Rendered only when the value holds exactly one QueryClient, found on the
|
|
18
|
-
* value and on its own properties: a `defineReactor` result, a reactor or a
|
|
19
|
-
* `ClientManager` brings its manager's, and a `QueryClient` counts as
|
|
20
|
-
* itself. Below the provider it takes the place of an outer
|
|
21
|
-
* `QueryClientProvider`, so a plain `useQuery` there caches in the
|
|
22
|
-
* reactors' QueryClient too. Pass `false` to leave that context to a
|
|
23
|
-
* provider of your own.
|
|
24
|
-
*
|
|
25
|
-
* @default true
|
|
26
|
-
*/
|
|
27
|
-
queryClientProvider?: boolean;
|
|
28
|
-
}
|
|
29
|
-
/** What {@link createReactorProvider} returns. */
|
|
30
|
-
export interface CreateReactorProviderReturn<TValue extends object, TProps extends object = {}> {
|
|
31
|
-
/**
|
|
32
|
-
* Builds the value once per mount and provides it to `useReactor`.
|
|
33
|
-
*
|
|
34
|
-
* Its props, other than `children`, are passed to the factory when it
|
|
35
|
-
* mounts and are not read again. To build a new value, give the provider a
|
|
36
|
-
* new `key`: React then unmounts the old tree, which releases the old value,
|
|
37
|
-
* and mounts a new one.
|
|
38
|
-
*/
|
|
39
|
-
ReactorProvider: (props: ReactorProviderProps<TProps>) => ReactElement;
|
|
40
|
-
/**
|
|
41
|
-
* The value the nearest `ReactorProvider` built, or one of its properties
|
|
42
|
-
* when given that property's name. A hook: call it in a component or a
|
|
43
|
-
* custom hook below the provider. It throws when there is none.
|
|
44
|
-
*/
|
|
45
|
-
useReactor: {
|
|
46
|
-
<TKey extends keyof TValue>(key: TKey): TValue[TKey];
|
|
47
|
-
(): TValue;
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Creates a provider that builds an app's reactors once per mounted tree, and
|
|
52
|
-
* a `useReactor()` hook that returns what it built, fully typed.
|
|
53
|
-
*
|
|
54
|
-
* This is the setup a server-rendered app needs. The factory runs in a
|
|
55
|
-
* `useState` initializer, once per mounted tree, and a server render is a tree
|
|
56
|
-
* of its own: each request builds its own `ClientManager`, `QueryClient`,
|
|
57
|
-
* reactors and `AuthenticationManager`, so no cache or identity is shared
|
|
58
|
-
* between visitors. The first render needs no effect, so the server's HTML is
|
|
59
|
-
* complete, and the browser builds a fresh value from the same props to
|
|
60
|
-
* hydrate it. In a client-only app it works the same, one value per mount.
|
|
61
|
-
*
|
|
62
|
-
* The factory returns anything: a `defineReactor` or `defineDisplayReactor`
|
|
63
|
-
* result, a record of them, reactors and managers built by hand, query and
|
|
64
|
-
* mutation objects from `createQuery` or `createMutation`. `useReactor()`
|
|
65
|
-
* returns it with the factory's own return type, so its hooks keep their
|
|
66
|
-
* generic signatures. `useReactor("todo")` returns one property of it.
|
|
67
|
-
*
|
|
68
|
-
* When the tree unmounts, the provider disposes each `AuthenticationManager`
|
|
69
|
-
* built for the value, releasing the Internet Identity client it built: those
|
|
70
|
-
* constructed while the factory ran, and the one a `defineReactor` result in
|
|
71
|
-
* the value, or among its own properties, builds on first use (a result that
|
|
72
|
-
* never used authentication builds none). A manager built elsewhere and
|
|
73
|
-
* handed in, such as an app-wide one passed as `authentication` or as a
|
|
74
|
-
* prop, is left to whoever built it. `dispose()` only forgets the client, and
|
|
75
|
-
* the next sign-in builds a new one, so this is safe under StrictMode, which
|
|
76
|
-
* runs the cleanup and then the effect again on the same value.
|
|
77
|
-
*
|
|
78
|
-
* It also renders a `QueryClientProvider` for the value's QueryClient; see
|
|
79
|
-
* {@link CreateReactorProviderOptions.queryClientProvider}.
|
|
80
|
-
*
|
|
81
|
-
* A suspense hook below the provider may suspend its first render: React
|
|
82
|
-
* renders the same element again when the data arrives, and the provider
|
|
83
|
-
* reuses the value that first render built, whether the Suspense boundary
|
|
84
|
-
* sits above the provider or, while hydrating, there is none. A provider that
|
|
85
|
-
* a transition mounts (`startTransition`, a client-side navigation) can be
|
|
86
|
-
* rendered from a new element on each retry and build a new value each time,
|
|
87
|
-
* so give its suspending components a `<Suspense>` boundary inside it.
|
|
88
|
-
*
|
|
89
|
-
* Call `createReactorProvider` at module scope: it builds nothing itself,
|
|
90
|
-
* only the context, and every mounted provider builds its own value. In the
|
|
91
|
-
* Next.js App Router that module needs `"use client"`, like any module with
|
|
92
|
-
* hooks; a server component renders the provider from there.
|
|
93
|
-
*
|
|
94
|
-
* @param factory - Builds the value. It receives the provider's props other
|
|
95
|
-
* than `children`, and runs once per mounted provider.
|
|
96
|
-
* @param options - See {@link CreateReactorProviderOptions}.
|
|
97
|
-
*
|
|
98
|
-
* @example One canister, with Internet Identity
|
|
99
|
-
* ```tsx
|
|
100
|
-
* "use client"
|
|
101
|
-
* import { createReactorProvider, defineReactor } from "@ic-reactor/react"
|
|
102
|
-
* import { canisterId, idlFactory, type _SERVICE } from "./declarations/todo"
|
|
103
|
-
*
|
|
104
|
-
* export const { ReactorProvider, useReactor } = createReactorProvider(() =>
|
|
105
|
-
* defineReactor<_SERVICE>({ name: "todo", idlFactory, canisterId })
|
|
106
|
-
* )
|
|
107
|
-
*
|
|
108
|
-
* function Todos() {
|
|
109
|
-
* const { useActorQuery, useAuth } = useReactor()
|
|
110
|
-
* const { isAuthenticated } = useAuth()
|
|
111
|
-
* const { data } = useActorQuery({ functionName: "getAllTodos" })
|
|
112
|
-
* return <p>{isAuthenticated ? data?.length : "Sign in"}</p>
|
|
113
|
-
* }
|
|
114
|
-
*
|
|
115
|
-
* // app/layout.tsx (a server component)
|
|
116
|
-
* // <ReactorProvider>{children}</ReactorProvider>
|
|
117
|
-
* ```
|
|
118
|
-
*
|
|
119
|
-
* @example Several canisters sharing one agent and one sign-in
|
|
120
|
-
* ```tsx
|
|
121
|
-
* export const { ReactorProvider, useReactor } = createReactorProvider(() => {
|
|
122
|
-
* const backend = defineReactor<Backend>({
|
|
123
|
-
* name: "backend",
|
|
124
|
-
* idlFactory: backendIdl,
|
|
125
|
-
* canisterId: backendId,
|
|
126
|
-
* })
|
|
127
|
-
* const ledger = defineDisplayReactor<Ledger>({
|
|
128
|
-
* name: "ledger",
|
|
129
|
-
* idlFactory: ledgerIdl,
|
|
130
|
-
* canisterId: ledgerId,
|
|
131
|
-
* authentication: backend.authentication,
|
|
132
|
-
* })
|
|
133
|
-
* return { backend, ledger }
|
|
134
|
-
* })
|
|
135
|
-
*
|
|
136
|
-
* function Balance() {
|
|
137
|
-
* const { data } = useReactor("ledger").useActorQuery({
|
|
138
|
-
* functionName: "icrc1_total_supply",
|
|
139
|
-
* })
|
|
140
|
-
* return <span>{data}</span>
|
|
141
|
-
* }
|
|
142
|
-
* ```
|
|
143
|
-
*
|
|
144
|
-
* @example Props for the factory, and a new key to build again
|
|
145
|
-
* ```tsx
|
|
146
|
-
* export const { ReactorProvider: LedgerProvider, useReactor: useLedger } =
|
|
147
|
-
* createReactorProvider(({ canisterId }: { canisterId: string }) =>
|
|
148
|
-
* defineDisplayReactor<Ledger>({ name: "ledger", idlFactory, canisterId })
|
|
149
|
-
* )
|
|
150
|
-
*
|
|
151
|
-
* // Read once per mount: the key makes a new canister a new tree.
|
|
152
|
-
* <LedgerProvider key={canisterId} canisterId={canisterId}>
|
|
153
|
-
* <TokenPage />
|
|
154
|
-
* </LedgerProvider>
|
|
155
|
-
* ```
|
|
156
|
-
*/
|
|
157
|
-
export declare function createReactorProvider<TValue extends object, TProps extends object | undefined = {}>(factory: (props: TProps) => TValue, options?: CreateReactorProviderOptions): CreateReactorProviderReturn<TValue, NonNullable<TProps>>;
|
|
158
|
-
//# sourceMappingURL=createReactorProvider.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createReactorProvider.d.ts","sourceRoot":"","sources":["../src/createReactorProvider.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AASpD;;;GAGG;AACH,MAAM,MAAM,oBAAoB,CAAC,MAAM,SAAS,MAAM,GAAG,EAAE,IAAI,MAAM,GAAG;IACtE,QAAQ,CAAC,EAAE,SAAS,CAAA;CACrB,CAAA;AAED,iDAAiD;AACjD,MAAM,WAAW,4BAA4B;IAC3C;;;;;;;;;;;;;;;OAeG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC9B;AAED,kDAAkD;AAClD,MAAM,WAAW,2BAA2B,CAC1C,MAAM,SAAS,MAAM,EACrB,MAAM,SAAS,MAAM,GAAG,EAAE;IAE1B;;;;;;;OAOG;IACH,eAAe,EAAE,CAAC,KAAK,EAAE,oBAAoB,CAAC,MAAM,CAAC,KAAK,YAAY,CAAA;IACtE;;;;OAIG;IACH,UAAU,EAAE;QACV,CAAC,IAAI,SAAS,MAAM,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;QAEpD,IAAI,MAAM,CAAA;KACX,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0GG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,SAAS,MAAM,EAGrB,MAAM,SAAS,MAAM,GAAG,SAAS,GAAG,EAAE,EAEtC,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,EAClC,OAAO,GAAE,4BAAiC,GACzC,2BAA2B,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAwF1D"}
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Reactor Provider Factory - builds an app's reactors once per mounted tree.
|
|
3
|
-
*
|
|
4
|
-
* A server renders every request through the same module graph, so a reactor
|
|
5
|
-
* built at module scope is one cache shared by every visitor: query keys carry
|
|
6
|
-
* no caller principal, and one request's caller-scoped result can be served to
|
|
7
|
-
* the next. The setup belongs inside the render tree instead, built in a
|
|
8
|
-
* `useState` initializer and handed down through context. Apps wrote that
|
|
9
|
-
* provider by hand (examples/nextjs had one of 93 lines) and then forwarded
|
|
10
|
-
* each hook out of the context with an `as any` cast, because a hook that is
|
|
11
|
-
* generic over the method name has no parameter tuple a typed wrapper could
|
|
12
|
-
* name.
|
|
13
|
-
*
|
|
14
|
-
* `createReactorProvider` is that provider. `useReactor()` returns what the
|
|
15
|
-
* factory built, typed as the factory's return, so every hook on it keeps its
|
|
16
|
-
* own generic signature and needs no forwarder.
|
|
17
|
-
*/
|
|
18
|
-
import { createContext, createElement, useContext, useEffect, useState, } from "react";
|
|
19
|
-
import { QueryClientProvider } from "@tanstack/react-query";
|
|
20
|
-
import { authenticationOf, collectAuthentication, } from "./ownedAuthentication.js";
|
|
21
|
-
/**
|
|
22
|
-
* Creates a provider that builds an app's reactors once per mounted tree, and
|
|
23
|
-
* a `useReactor()` hook that returns what it built, fully typed.
|
|
24
|
-
*
|
|
25
|
-
* This is the setup a server-rendered app needs. The factory runs in a
|
|
26
|
-
* `useState` initializer, once per mounted tree, and a server render is a tree
|
|
27
|
-
* of its own: each request builds its own `ClientManager`, `QueryClient`,
|
|
28
|
-
* reactors and `AuthenticationManager`, so no cache or identity is shared
|
|
29
|
-
* between visitors. The first render needs no effect, so the server's HTML is
|
|
30
|
-
* complete, and the browser builds a fresh value from the same props to
|
|
31
|
-
* hydrate it. In a client-only app it works the same, one value per mount.
|
|
32
|
-
*
|
|
33
|
-
* The factory returns anything: a `defineReactor` or `defineDisplayReactor`
|
|
34
|
-
* result, a record of them, reactors and managers built by hand, query and
|
|
35
|
-
* mutation objects from `createQuery` or `createMutation`. `useReactor()`
|
|
36
|
-
* returns it with the factory's own return type, so its hooks keep their
|
|
37
|
-
* generic signatures. `useReactor("todo")` returns one property of it.
|
|
38
|
-
*
|
|
39
|
-
* When the tree unmounts, the provider disposes each `AuthenticationManager`
|
|
40
|
-
* built for the value, releasing the Internet Identity client it built: those
|
|
41
|
-
* constructed while the factory ran, and the one a `defineReactor` result in
|
|
42
|
-
* the value, or among its own properties, builds on first use (a result that
|
|
43
|
-
* never used authentication builds none). A manager built elsewhere and
|
|
44
|
-
* handed in, such as an app-wide one passed as `authentication` or as a
|
|
45
|
-
* prop, is left to whoever built it. `dispose()` only forgets the client, and
|
|
46
|
-
* the next sign-in builds a new one, so this is safe under StrictMode, which
|
|
47
|
-
* runs the cleanup and then the effect again on the same value.
|
|
48
|
-
*
|
|
49
|
-
* It also renders a `QueryClientProvider` for the value's QueryClient; see
|
|
50
|
-
* {@link CreateReactorProviderOptions.queryClientProvider}.
|
|
51
|
-
*
|
|
52
|
-
* A suspense hook below the provider may suspend its first render: React
|
|
53
|
-
* renders the same element again when the data arrives, and the provider
|
|
54
|
-
* reuses the value that first render built, whether the Suspense boundary
|
|
55
|
-
* sits above the provider or, while hydrating, there is none. A provider that
|
|
56
|
-
* a transition mounts (`startTransition`, a client-side navigation) can be
|
|
57
|
-
* rendered from a new element on each retry and build a new value each time,
|
|
58
|
-
* so give its suspending components a `<Suspense>` boundary inside it.
|
|
59
|
-
*
|
|
60
|
-
* Call `createReactorProvider` at module scope: it builds nothing itself,
|
|
61
|
-
* only the context, and every mounted provider builds its own value. In the
|
|
62
|
-
* Next.js App Router that module needs `"use client"`, like any module with
|
|
63
|
-
* hooks; a server component renders the provider from there.
|
|
64
|
-
*
|
|
65
|
-
* @param factory - Builds the value. It receives the provider's props other
|
|
66
|
-
* than `children`, and runs once per mounted provider.
|
|
67
|
-
* @param options - See {@link CreateReactorProviderOptions}.
|
|
68
|
-
*
|
|
69
|
-
* @example One canister, with Internet Identity
|
|
70
|
-
* ```tsx
|
|
71
|
-
* "use client"
|
|
72
|
-
* import { createReactorProvider, defineReactor } from "@ic-reactor/react"
|
|
73
|
-
* import { canisterId, idlFactory, type _SERVICE } from "./declarations/todo"
|
|
74
|
-
*
|
|
75
|
-
* export const { ReactorProvider, useReactor } = createReactorProvider(() =>
|
|
76
|
-
* defineReactor<_SERVICE>({ name: "todo", idlFactory, canisterId })
|
|
77
|
-
* )
|
|
78
|
-
*
|
|
79
|
-
* function Todos() {
|
|
80
|
-
* const { useActorQuery, useAuth } = useReactor()
|
|
81
|
-
* const { isAuthenticated } = useAuth()
|
|
82
|
-
* const { data } = useActorQuery({ functionName: "getAllTodos" })
|
|
83
|
-
* return <p>{isAuthenticated ? data?.length : "Sign in"}</p>
|
|
84
|
-
* }
|
|
85
|
-
*
|
|
86
|
-
* // app/layout.tsx (a server component)
|
|
87
|
-
* // <ReactorProvider>{children}</ReactorProvider>
|
|
88
|
-
* ```
|
|
89
|
-
*
|
|
90
|
-
* @example Several canisters sharing one agent and one sign-in
|
|
91
|
-
* ```tsx
|
|
92
|
-
* export const { ReactorProvider, useReactor } = createReactorProvider(() => {
|
|
93
|
-
* const backend = defineReactor<Backend>({
|
|
94
|
-
* name: "backend",
|
|
95
|
-
* idlFactory: backendIdl,
|
|
96
|
-
* canisterId: backendId,
|
|
97
|
-
* })
|
|
98
|
-
* const ledger = defineDisplayReactor<Ledger>({
|
|
99
|
-
* name: "ledger",
|
|
100
|
-
* idlFactory: ledgerIdl,
|
|
101
|
-
* canisterId: ledgerId,
|
|
102
|
-
* authentication: backend.authentication,
|
|
103
|
-
* })
|
|
104
|
-
* return { backend, ledger }
|
|
105
|
-
* })
|
|
106
|
-
*
|
|
107
|
-
* function Balance() {
|
|
108
|
-
* const { data } = useReactor("ledger").useActorQuery({
|
|
109
|
-
* functionName: "icrc1_total_supply",
|
|
110
|
-
* })
|
|
111
|
-
* return <span>{data}</span>
|
|
112
|
-
* }
|
|
113
|
-
* ```
|
|
114
|
-
*
|
|
115
|
-
* @example Props for the factory, and a new key to build again
|
|
116
|
-
* ```tsx
|
|
117
|
-
* export const { ReactorProvider: LedgerProvider, useReactor: useLedger } =
|
|
118
|
-
* createReactorProvider(({ canisterId }: { canisterId: string }) =>
|
|
119
|
-
* defineDisplayReactor<Ledger>({ name: "ledger", idlFactory, canisterId })
|
|
120
|
-
* )
|
|
121
|
-
*
|
|
122
|
-
* // Read once per mount: the key makes a new canister a new tree.
|
|
123
|
-
* <LedgerProvider key={canisterId} canisterId={canisterId}>
|
|
124
|
-
* <TokenPage />
|
|
125
|
-
* </LedgerProvider>
|
|
126
|
-
* ```
|
|
127
|
-
*/
|
|
128
|
-
export function createReactorProvider(factory, options = {}) {
|
|
129
|
-
const { queryClientProvider = true } = options;
|
|
130
|
-
const ReactorContext = createContext(null);
|
|
131
|
-
// What a browser render built and has not committed yet, by the props
|
|
132
|
-
// object that render received. React throws away the state of a tree that
|
|
133
|
-
// suspends before it first commits, and renders it again, from the same
|
|
134
|
-
// element, once the promise settles. A value built afresh for that render
|
|
135
|
-
// came with an empty QueryClient, so a suspense query below, with no
|
|
136
|
-
// Suspense boundary between it and this provider, fetched again, suspended
|
|
137
|
-
// again and rebuilt the value in an endless loop of canister calls. The
|
|
138
|
-
// render that follows reuses the value instead, and so does StrictMode's
|
|
139
|
-
// second call of the initializer, which would otherwise build a value only
|
|
140
|
-
// to drop it. A server keeps nothing here: it never commits, and a
|
|
141
|
-
// module-scope element rendered for each request would hand one request's
|
|
142
|
-
// value to the next.
|
|
143
|
-
const uncommitted = new WeakMap();
|
|
144
|
-
function ReactorProvider(providerProps) {
|
|
145
|
-
// Once per mounted tree. A server renders each request as a tree of its
|
|
146
|
-
// own, so each request builds its own value and nothing it caches is seen
|
|
147
|
-
// by another. The props are read here only; a new `key` builds again.
|
|
148
|
-
const [built] = useState(() => {
|
|
149
|
-
const pending = uncommitted.get(providerProps);
|
|
150
|
-
if (pending)
|
|
151
|
-
return pending;
|
|
152
|
-
const { children: _children, ...props } = providerProps;
|
|
153
|
-
const { value, built: authentication } = collectAuthentication(() => factory(props));
|
|
154
|
-
const next = {
|
|
155
|
-
value,
|
|
156
|
-
authentication,
|
|
157
|
-
queryClient: queryClientProvider ? soleQueryClient(value) : undefined,
|
|
158
|
-
};
|
|
159
|
-
if (!isServer()) {
|
|
160
|
-
next.pendingProps = providerProps;
|
|
161
|
-
uncommitted.set(providerProps, next);
|
|
162
|
-
}
|
|
163
|
-
return next;
|
|
164
|
-
});
|
|
165
|
-
// Releases the Internet Identity clients of the managers built for this
|
|
166
|
-
// value once this tree unmounts. A v10 client listens to the page until
|
|
167
|
-
// it is disposed, so each remount would otherwise leave one behind.
|
|
168
|
-
// dispose() only forgets the client: StrictMode runs this cleanup and then
|
|
169
|
-
// the effect again on the same value, and the next sign-in builds a new
|
|
170
|
-
// one. A server runs no effects, and its managers build no client.
|
|
171
|
-
useEffect(() => {
|
|
172
|
-
// Committed: the value is this mount's, and a later mount of the same
|
|
173
|
-
// element builds its own.
|
|
174
|
-
if (built.pendingProps) {
|
|
175
|
-
uncommitted.delete(built.pendingProps);
|
|
176
|
-
built.pendingProps = undefined;
|
|
177
|
-
}
|
|
178
|
-
return () => disposeAuthentication(built);
|
|
179
|
-
}, [built]);
|
|
180
|
-
const provided = createElement(ReactorContext.Provider, { value: built.value }, providerProps.children);
|
|
181
|
-
return built.queryClient
|
|
182
|
-
? createElement(QueryClientProvider, { client: built.queryClient }, provided)
|
|
183
|
-
: provided;
|
|
184
|
-
}
|
|
185
|
-
function useReactor(key) {
|
|
186
|
-
const value = useContext(ReactorContext);
|
|
187
|
-
if (value === null) {
|
|
188
|
-
throw new Error("[ic-reactor] useReactor() was called outside its <ReactorProvider>. " +
|
|
189
|
-
"Render the component below the ReactorProvider returned by the " +
|
|
190
|
-
"same createReactorProvider() call: each call has a context of its own.");
|
|
191
|
-
}
|
|
192
|
-
return key === undefined ? value : value[key];
|
|
193
|
-
}
|
|
194
|
-
return { ReactorProvider, useReactor };
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Whether this render runs on a server, as TanStack Query decides it: Deno
|
|
198
|
-
* defines `window` without being a browser.
|
|
199
|
-
*/
|
|
200
|
-
const isServer = () => typeof window === "undefined" || "Deno" in globalThis;
|
|
201
|
-
/**
|
|
202
|
-
* The value and each of its own data properties: where a built value keeps
|
|
203
|
-
* what it built. Accessors are skipped rather than read, because reading
|
|
204
|
-
* one can build what it returns: a `defineReactor` result's `authentication`
|
|
205
|
-
* builds the manager on first read.
|
|
206
|
-
*/
|
|
207
|
-
function partsOf(value) {
|
|
208
|
-
const parts = [value];
|
|
209
|
-
for (const descriptor of Object.values(Object.getOwnPropertyDescriptors(value))) {
|
|
210
|
-
const part = descriptor.value;
|
|
211
|
-
if (typeof part === "object" && part !== null)
|
|
212
|
-
parts.push(part);
|
|
213
|
-
}
|
|
214
|
-
return parts;
|
|
215
|
-
}
|
|
216
|
-
function isQueryClient(candidate) {
|
|
217
|
-
const client = candidate;
|
|
218
|
-
return (typeof client?.getQueryCache === "function" &&
|
|
219
|
-
typeof client.mount === "function");
|
|
220
|
-
}
|
|
221
|
-
/**
|
|
222
|
-
* The QueryClient the value uses, when it uses exactly one. A reactor, a
|
|
223
|
-
* `ClientManager` and a `defineReactor` result each bring theirs as
|
|
224
|
-
* `queryClient`.
|
|
225
|
-
*/
|
|
226
|
-
function soleQueryClient(value) {
|
|
227
|
-
const clients = new Set();
|
|
228
|
-
for (const part of partsOf(value)) {
|
|
229
|
-
const client = isQueryClient(part)
|
|
230
|
-
? part
|
|
231
|
-
: part.queryClient;
|
|
232
|
-
if (isQueryClient(client))
|
|
233
|
-
clients.add(client);
|
|
234
|
-
}
|
|
235
|
-
if (clients.size !== 1)
|
|
236
|
-
return undefined;
|
|
237
|
-
const [client] = clients;
|
|
238
|
-
return client;
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Disposes each `AuthenticationManager` built for the value, once each: those
|
|
242
|
-
* constructed while the factory ran, and the one each `defineReactor` result
|
|
243
|
-
* in the value or among its own properties has built since. A manager built
|
|
244
|
-
* elsewhere and handed in is left to whoever built it.
|
|
245
|
-
*/
|
|
246
|
-
function disposeAuthentication({ value, authentication }) {
|
|
247
|
-
const managers = new Set(authentication);
|
|
248
|
-
for (const part of partsOf(value)) {
|
|
249
|
-
const owned = authenticationOf(part);
|
|
250
|
-
if (owned)
|
|
251
|
-
managers.add(owned);
|
|
252
|
-
}
|
|
253
|
-
for (const manager of managers)
|
|
254
|
-
manager.dispose();
|
|
255
|
-
}
|
|
256
|
-
//# sourceMappingURL=createReactorProvider.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"createReactorProvider.js","sourceRoot":"","sources":["../src/createReactorProvider.ts"],"names":[],"mappings":"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"}
|