@ic-reactor/react 3.12.5 → 4.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +236 -423
  2. package/dist/index.d.ts +259 -12
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +470 -17
  5. package/dist/index.js.map +1 -1
  6. package/llms.txt +82 -54
  7. package/package.json +12 -28
  8. package/src/index.tsx +611 -0
  9. package/dist/auth/authentication-manager.d.ts +0 -123
  10. package/dist/auth/authentication-manager.d.ts.map +0 -1
  11. package/dist/auth/authentication-manager.js +0 -767
  12. package/dist/auth/authentication-manager.js.map +0 -1
  13. package/dist/auth/constants.d.ts +0 -24
  14. package/dist/auth/constants.d.ts.map +0 -1
  15. package/dist/auth/constants.js +0 -24
  16. package/dist/auth/constants.js.map +0 -1
  17. package/dist/auth/createIdentityAttributeHooks.d.ts +0 -14
  18. package/dist/auth/createIdentityAttributeHooks.d.ts.map +0 -1
  19. package/dist/auth/createIdentityAttributeHooks.js +0 -106
  20. package/dist/auth/createIdentityAttributeHooks.js.map +0 -1
  21. package/dist/auth/identity-attributes-manager.d.ts +0 -26
  22. package/dist/auth/identity-attributes-manager.d.ts.map +0 -1
  23. package/dist/auth/identity-attributes-manager.js +0 -107
  24. package/dist/auth/identity-attributes-manager.js.map +0 -1
  25. package/dist/auth/identity-attributes.d.ts +0 -19
  26. package/dist/auth/identity-attributes.d.ts.map +0 -1
  27. package/dist/auth/identity-attributes.js +0 -170
  28. package/dist/auth/identity-attributes.js.map +0 -1
  29. package/dist/auth/index.d.ts +0 -8
  30. package/dist/auth/index.d.ts.map +0 -1
  31. package/dist/auth/index.js +0 -8
  32. package/dist/auth/index.js.map +0 -1
  33. package/dist/auth/local-ii-probe.d.ts +0 -46
  34. package/dist/auth/local-ii-probe.d.ts.map +0 -1
  35. package/dist/auth/local-ii-probe.js +0 -102
  36. package/dist/auth/local-ii-probe.js.map +0 -1
  37. package/dist/auth/types.d.ts +0 -179
  38. package/dist/auth/types.d.ts.map +0 -1
  39. package/dist/auth/types.js +0 -2
  40. package/dist/auth/types.js.map +0 -1
  41. package/dist/createActorHooks.d.ts +0 -52
  42. package/dist/createActorHooks.d.ts.map +0 -1
  43. package/dist/createActorHooks.js +0 -17
  44. package/dist/createActorHooks.js.map +0 -1
  45. package/dist/createInfiniteQuery.d.ts +0 -144
  46. package/dist/createInfiniteQuery.d.ts.map +0 -1
  47. package/dist/createInfiniteQuery.js +0 -174
  48. package/dist/createInfiniteQuery.js.map +0 -1
  49. package/dist/createMutation.d.ts +0 -30
  50. package/dist/createMutation.d.ts.map +0 -1
  51. package/dist/createMutation.js +0 -192
  52. package/dist/createMutation.js.map +0 -1
  53. package/dist/createQuery.d.ts +0 -30
  54. package/dist/createQuery.d.ts.map +0 -1
  55. package/dist/createQuery.js +0 -117
  56. package/dist/createQuery.js.map +0 -1
  57. package/dist/createSuspenseInfiniteQuery.d.ts +0 -147
  58. package/dist/createSuspenseInfiniteQuery.d.ts.map +0 -1
  59. package/dist/createSuspenseInfiniteQuery.js +0 -177
  60. package/dist/createSuspenseInfiniteQuery.js.map +0 -1
  61. package/dist/createSuspenseQuery.d.ts +0 -25
  62. package/dist/createSuspenseQuery.d.ts.map +0 -1
  63. package/dist/createSuspenseQuery.js +0 -111
  64. package/dist/createSuspenseQuery.js.map +0 -1
  65. package/dist/defineReactor.d.ts +0 -125
  66. package/dist/defineReactor.d.ts.map +0 -1
  67. package/dist/defineReactor.js +0 -180
  68. package/dist/defineReactor.js.map +0 -1
  69. package/dist/hooks/createAuthHooks.d.ts +0 -43
  70. package/dist/hooks/createAuthHooks.d.ts.map +0 -1
  71. package/dist/hooks/createAuthHooks.js +0 -131
  72. package/dist/hooks/createAuthHooks.js.map +0 -1
  73. package/dist/hooks/index.d.ts +0 -21
  74. package/dist/hooks/index.d.ts.map +0 -1
  75. package/dist/hooks/index.js +0 -24
  76. package/dist/hooks/index.js.map +0 -1
  77. package/dist/hooks/useActorInfiniteQuery.d.ts +0 -39
  78. package/dist/hooks/useActorInfiniteQuery.d.ts.map +0 -1
  79. package/dist/hooks/useActorInfiniteQuery.js +0 -56
  80. package/dist/hooks/useActorInfiniteQuery.js.map +0 -1
  81. package/dist/hooks/useActorMethod.d.ts +0 -115
  82. package/dist/hooks/useActorMethod.d.ts.map +0 -1
  83. package/dist/hooks/useActorMethod.js +0 -250
  84. package/dist/hooks/useActorMethod.js.map +0 -1
  85. package/dist/hooks/useActorMutation.d.ts +0 -48
  86. package/dist/hooks/useActorMutation.d.ts.map +0 -1
  87. package/dist/hooks/useActorMutation.js +0 -69
  88. package/dist/hooks/useActorMutation.js.map +0 -1
  89. package/dist/hooks/useActorQuery.d.ts +0 -32
  90. package/dist/hooks/useActorQuery.d.ts.map +0 -1
  91. package/dist/hooks/useActorQuery.js +0 -46
  92. package/dist/hooks/useActorQuery.js.map +0 -1
  93. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +0 -39
  94. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +0 -1
  95. package/dist/hooks/useActorSuspenseInfiniteQuery.js +0 -56
  96. package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +0 -1
  97. package/dist/hooks/useActorSuspenseQuery.d.ts +0 -32
  98. package/dist/hooks/useActorSuspenseQuery.d.ts.map +0 -1
  99. package/dist/hooks/useActorSuspenseQuery.js +0 -47
  100. package/dist/hooks/useActorSuspenseQuery.js.map +0 -1
  101. package/dist/types.d.ts +0 -270
  102. package/dist/types.d.ts.map +0 -1
  103. package/dist/types.js +0 -5
  104. package/dist/types.js.map +0 -1
  105. package/dist/utils.d.ts +0 -51
  106. package/dist/utils.d.ts.map +0 -1
  107. package/dist/utils.js +0 -105
  108. package/dist/utils.js.map +0 -1
  109. package/dist/validation.d.ts +0 -131
  110. package/dist/validation.d.ts.map +0 -1
  111. package/dist/validation.js +0 -125
  112. package/dist/validation.js.map +0 -1
  113. package/src/auth/authentication-manager.ts +0 -860
  114. package/src/auth/constants.ts +0 -32
  115. package/src/auth/createIdentityAttributeHooks.ts +0 -143
  116. package/src/auth/identity-attributes-manager.ts +0 -131
  117. package/src/auth/identity-attributes.ts +0 -270
  118. package/src/auth/index.ts +0 -7
  119. package/src/auth/local-ii-probe.ts +0 -147
  120. package/src/auth/types.ts +0 -200
  121. package/src/createActorHooks.ts +0 -220
  122. package/src/createInfiniteQuery.ts +0 -578
  123. package/src/createMutation.ts +0 -341
  124. package/src/createQuery.ts +0 -237
  125. package/src/createSuspenseInfiniteQuery.ts +0 -601
  126. package/src/createSuspenseQuery.ts +0 -234
  127. package/src/defineReactor.ts +0 -324
  128. package/src/hooks/createAuthHooks.ts +0 -189
  129. package/src/hooks/index.ts +0 -103
  130. package/src/hooks/useActorInfiniteQuery.ts +0 -205
  131. package/src/hooks/useActorMethod.ts +0 -507
  132. package/src/hooks/useActorMutation.ts +0 -205
  133. package/src/hooks/useActorQuery.ts +0 -124
  134. package/src/hooks/useActorSuspenseInfiniteQuery.ts +0 -205
  135. package/src/hooks/useActorSuspenseQuery.ts +0 -132
  136. package/src/index.ts +0 -23
  137. package/src/types.ts +0 -499
  138. package/src/utils.ts +0 -121
  139. package/src/validation.ts +0 -202
@@ -1,234 +0,0 @@
1
- /**
2
- * Suspense Query Factory - Generic wrapper for React Suspense-based canister data
3
- *
4
- * Creates unified fetch/hook/invalidate functions for any canister method.
5
- * Works with any Reactor instance.
6
- *
7
- * Uses `useSuspenseQuery` which:
8
- * - Requires wrapping in <Suspense> boundary
9
- * - Data is always defined (no undefined checks)
10
- * - Does NOT support `enabled` option
11
- *
12
- * @example
13
- * const userQuery = createSuspenseQuery(todoManager, {
14
- * functionName: "get_user",
15
- * select: (result) => result.user,
16
- * })
17
- *
18
- * // In component (wrap in Suspense)
19
- * const { data: user } = userQuery.useSuspenseQuery() // data is never undefined!
20
- */
21
-
22
- import type {
23
- Reactor,
24
- FunctionName,
25
- ReactorArgs,
26
- TransformKey,
27
- } from "@ic-reactor/core"
28
- import { useMemo } from "react"
29
- import { useSuspenseQuery } from "@tanstack/react-query"
30
- import type {
31
- QueryFnData,
32
- QueryError,
33
- SuspenseQueryConfig,
34
- UseSuspenseQueryWithSelect,
35
- SuspenseQueryResult,
36
- SuspenseQueryFactoryConfig,
37
- NoInfer,
38
- } from "./types.js"
39
- import { buildChainedSelect, createBoundedCache } from "./utils.js"
40
-
41
- // ============================================================================
42
- // Internal Implementation
43
- // ============================================================================
44
-
45
- const createSuspenseQueryImpl = <
46
- Service,
47
- Method extends FunctionName<Service> = FunctionName<Service>,
48
- Transform extends TransformKey = "candid",
49
- Selected = QueryFnData<Service, Method, Transform>,
50
- >(
51
- reactor: Reactor<Service, Transform>,
52
- config: SuspenseQueryConfig<Service, Method, Transform, Selected>
53
- ): SuspenseQueryResult<
54
- QueryFnData<Service, Method, Transform>,
55
- Selected,
56
- QueryError<Service, Method, Transform>
57
- > => {
58
- type TData = QueryFnData<Service, Method, Transform>
59
- type TError = QueryError<Service, Method, Transform>
60
-
61
- const {
62
- functionName,
63
- args,
64
- staleTime = 5 * 60 * 1000,
65
- select,
66
- queryKey: customQueryKey,
67
- ...rest
68
- } = config
69
-
70
- const params = { functionName, args, queryKey: customQueryKey }
71
-
72
- const getQueryKey = () => reactor.generateQueryKey(params)
73
-
74
- const applySelect = (raw: TData): Selected =>
75
- select ? select(raw) : (raw as unknown as Selected)
76
-
77
- /** Cache-first fetch for use in loaders / route preloading. */
78
- const fetch = async (): Promise<Selected> => {
79
- const result = await reactor.fetchQuery(params)
80
- return applySelect(result)
81
- }
82
-
83
- /** Fire-and-forget prefetch — warms the cache without blocking. */
84
- const prefetch = (): Promise<void> => {
85
- const baseOptions = reactor.getQueryOptions(params)
86
- return reactor.queryClient.prefetchQuery({
87
- queryKey: baseOptions.queryKey,
88
- queryFn: baseOptions.queryFn,
89
- staleTime,
90
- })
91
- }
92
-
93
- const useSuspenseQueryHook: UseSuspenseQueryWithSelect<
94
- TData,
95
- Selected,
96
- TError
97
- > = (options: any): any => {
98
- const baseOptions = reactor.getQueryOptions(params)
99
- // Memoized so the observer's select-result cache can hit; see
100
- // buildChainedSelect. `select` comes from the factory config and is stable.
101
- const chainedSelect = useMemo(
102
- () => buildChainedSelect(select, options?.select),
103
- [options?.select]
104
- )
105
- return useSuspenseQuery(
106
- {
107
- queryKey: baseOptions.queryKey,
108
- staleTime,
109
- ...rest,
110
- ...options,
111
- queryFn: baseOptions.queryFn,
112
- select: chainedSelect,
113
- },
114
- reactor.queryClient
115
- )
116
- }
117
-
118
- const invalidate = async (): Promise<void> => {
119
- await reactor.queryClient.invalidateQueries({ queryKey: getQueryKey() })
120
- }
121
-
122
- const getCacheData: SuspenseQueryResult<
123
- TData,
124
- Selected,
125
- TError
126
- >["getCacheData"] = (selectFn?: (data: Selected) => unknown): any => {
127
- const raw = reactor.getQueryData(params)
128
- if (raw === undefined) return undefined
129
- const selected = applySelect(raw)
130
- return selectFn ? selectFn(selected) : selected
131
- }
132
-
133
- const setData: SuspenseQueryResult<TData, Selected, TError>["setData"] = (
134
- updater
135
- ) => {
136
- return reactor.queryClient.setQueryData(getQueryKey(), updater as any) as
137
- TData | undefined
138
- }
139
-
140
- return {
141
- fetch,
142
- prefetch,
143
- useSuspenseQuery: useSuspenseQueryHook,
144
- invalidate,
145
- getQueryKey,
146
- getCacheData,
147
- setData,
148
- }
149
- }
150
-
151
- // ============================================================================
152
- // Public Factory Function
153
- // ============================================================================
154
-
155
- export function createSuspenseQuery<
156
- Service,
157
- Transform extends TransformKey,
158
- Method extends FunctionName<Service> = FunctionName<Service>,
159
- Selected = QueryFnData<Service, Method, Transform>,
160
- >(
161
- reactor: Reactor<Service, Transform>,
162
- config: SuspenseQueryConfig<NoInfer<Service>, Method, Transform, Selected>
163
- ): SuspenseQueryResult<
164
- QueryFnData<Service, Method, Transform>,
165
- Selected,
166
- QueryError<Service, Method, Transform>
167
- > {
168
- return createSuspenseQueryImpl(
169
- reactor,
170
- config as SuspenseQueryConfig<Service, Method, Transform, Selected>
171
- )
172
- }
173
-
174
- // ============================================================================
175
- // Convenience: Create suspense query with dynamic args
176
- // ============================================================================
177
-
178
- export function createSuspenseQueryFactory<
179
- Service,
180
- Transform extends TransformKey,
181
- Method extends FunctionName<Service> = FunctionName<Service>,
182
- Selected = QueryFnData<Service, Method, Transform>,
183
- >(
184
- reactor: Reactor<Service, Transform>,
185
- config: SuspenseQueryFactoryConfig<
186
- NoInfer<Service>,
187
- Method,
188
- Transform,
189
- Selected
190
- >
191
- ): (
192
- args: ReactorArgs<Service, Method, Transform>
193
- ) => SuspenseQueryResult<
194
- QueryFnData<Service, Method, Transform>,
195
- Selected,
196
- QueryError<Service, Method, Transform>
197
- > {
198
- const cache =
199
- createBoundedCache<
200
- SuspenseQueryResult<
201
- QueryFnData<Service, Method, Transform>,
202
- Selected,
203
- QueryError<Service, Method, Transform>
204
- >
205
- >()
206
-
207
- return (args: ReactorArgs<Service, Method, Transform>) => {
208
- const key = reactor.generateQueryKey({
209
- functionName: config.functionName as Method,
210
- args,
211
- })
212
- const cacheKey = JSON.stringify(key)
213
-
214
- const existing = cache.get(cacheKey)
215
- if (existing) return existing
216
-
217
- const result = createSuspenseQueryImpl<
218
- Service,
219
- Method,
220
- Transform,
221
- Selected
222
- >(reactor, {
223
- ...(config as SuspenseQueryFactoryConfig<
224
- Service,
225
- Method,
226
- Transform,
227
- Selected
228
- >),
229
- args,
230
- })
231
- cache.set(cacheKey, result)
232
- return result
233
- }
234
- }
@@ -1,324 +0,0 @@
1
- /**
2
- * defineReactor - One-call bootstrap for a canister reactor + its React hooks.
3
- *
4
- * Collapses the four manual steps (QueryClient → ClientManager → Reactor →
5
- * createActorHooks) into a single call, and turns the Reactor / DisplayReactor
6
- * choice into a `display` flag.
7
- * Use this when one-call bootstrap is enough. If you need reusable
8
- * method-specific operations outside React, compose with query/mutation
9
- * factories after setup.
10
- *
11
- * @example
12
- * ```typescript
13
- * import { defineReactor } from "@ic-reactor/react"
14
- * import { idlFactory, type _SERVICE } from "./declarations/backend"
15
- *
16
- * const { reactor, useActorQuery, useActorMutation } = defineReactor<_SERVICE>({
17
- * name: "backend",
18
- * idlFactory,
19
- * canisterId: "rrkah-fqaaa-aaaaa-aaaaq-cai",
20
- * display: true, // ⇒ DisplayReactor (string principals/bigints)
21
- * })
22
- *
23
- * function Profile() {
24
- * const { data } = useActorQuery({ functionName: "get_profile" })
25
- * return <div>{data?.name}</div>
26
- * }
27
- * ```
28
- *
29
- * @example Reuse an existing ClientManager (multiple canisters share one agent)
30
- * ```typescript
31
- * const ledger = defineReactor<_LEDGER>({ name: "ledger", idlFactory: ledgerIdl })
32
- * const index = defineReactor<_INDEX>({
33
- * name: "index",
34
- * idlFactory: indexIdl,
35
- * clientManager: ledger.clientManager,
36
- * authentication: ledger.authentication, // one Internet Identity session
37
- * })
38
- * ```
39
- *
40
- * @example Internet Identity is wired up out of the box
41
- * ```typescript
42
- * const { useAuth, useIdentityAttributes } = defineReactor<_SERVICE>({
43
- * name: "backend",
44
- * idlFactory,
45
- * // Needed when the app is served from more than one origin.
46
- * auth: { derivationOrigin: "https://app.example.com" },
47
- * })
48
- *
49
- * function LoginButton() {
50
- * const { login, logout, isAuthenticated, principal } = useAuth()
51
- * return isAuthenticated
52
- * ? <button onClick={() => logout()}>{principal?.toText()}</button>
53
- * : <button onClick={() => login()}>Sign in</button>
54
- * }
55
- * ```
56
- */
57
- import {
58
- ClientManager,
59
- Reactor,
60
- DisplayReactor,
61
- reactorRetry,
62
- } from "@ic-reactor/core"
63
- import type {
64
- BaseActor,
65
- TransformKey,
66
- ReactorParameters,
67
- ClientManagerParameters,
68
- DisplayReactorParameters,
69
- } from "@ic-reactor/core"
70
- import { QueryClient } from "@tanstack/react-query"
71
- import { createActorHooks, ActorHooks } from "./createActorHooks.js"
72
- import { AuthenticationManager } from "./auth/authentication-manager.js"
73
- import { IdentityAttributesManager } from "./auth/identity-attributes-manager.js"
74
- import { createIdentityAttributeHooks } from "./auth/createIdentityAttributeHooks.js"
75
- import type { UseIdentityAttributesReturn } from "./auth/createIdentityAttributeHooks.js"
76
- import { createAuthHooks } from "./hooks/createAuthHooks.js"
77
- import type { CreateAuthHooksReturn } from "./hooks/createAuthHooks.js"
78
- import type { AuthenticationManagerParameters } from "./auth/authentication-manager.js"
79
-
80
- /** Options shared by both the standard and display variants of defineReactor. */
81
- export interface DefineReactorSharedParameters
82
- extends
83
- Omit<ReactorParameters, "clientManager">,
84
- Omit<ClientManagerParameters, "queryClient"> {
85
- /**
86
- * Reuse an existing ClientManager (e.g. to share one agent across canisters).
87
- * When omitted, a ClientManager is created from the agent options below.
88
- *
89
- * A supplied or adopted manager brings its own agent and QueryClient, so
90
- * `agentOptions`, `queryClient` and `allowEnvConfig` apply only when this
91
- * call creates one.
92
- */
93
- clientManager?: ClientManager
94
- /**
95
- * QueryClient for a ClientManager created by this call.
96
- *
97
- * Ignored when `clientManager` or `authentication` is supplied — queries run
98
- * against that manager's own QueryClient, which is what is returned.
99
- */
100
- queryClient?: QueryClient
101
- /**
102
- * Reuse an existing AuthenticationManager, so several reactors share one
103
- * Internet Identity session. When omitted, one is created for this reactor.
104
- *
105
- * Its `clientManager` is adopted for this reactor, so sign-in updates the
106
- * same agent the reactor calls through. Supplying a different `clientManager`
107
- * alongside it is rejected.
108
- */
109
- authentication?: AuthenticationManager
110
- /**
111
- * Internet Identity options forwarded to the AuthenticationManager
112
- * (`identityProvider`, `derivationOrigin`, `idleOptions`, `storage`, …).
113
- *
114
- * Mutually exclusive with `authentication`: a manager built elsewhere is
115
- * already configured, so these could not be applied to it.
116
- */
117
- auth?: Omit<AuthenticationManagerParameters, "clientManager">
118
- }
119
-
120
- /** Parameters for a standard (raw Candid types) reactor. */
121
- export interface DefineReactorParameters extends DefineReactorSharedParameters {
122
- display?: false
123
- }
124
-
125
- /** Parameters for a DisplayReactor (UI-friendly string principals/bigints). */
126
- export interface DefineDisplayReactorParameters<
127
- Service,
128
- > extends DefineReactorSharedParameters {
129
- display: true
130
- /** Optional initial argument validators (receive display types). */
131
- validators?: DisplayReactorParameters<Service>["validators"]
132
- }
133
-
134
- /** The reactor instance plus its bound hooks and shared infrastructure. */
135
- export type DefineReactorResult<
136
- Service,
137
- Transform extends TransformKey,
138
- R extends Reactor<Service, Transform>,
139
- > = ActorHooks<Service, Transform> &
140
- CreateAuthHooksReturn & {
141
- reactor: R
142
- clientManager: ClientManager
143
- queryClient: QueryClient
144
- /** Internet Identity session manager backing `useAuth`. */
145
- authentication: AuthenticationManager
146
- /** Signed identity attribute requests backing `useIdentityAttributes`. */
147
- identityAttributes: IdentityAttributesManager
148
- useIdentityAttributes: () => UseIdentityAttributesReturn
149
- }
150
-
151
- /**
152
- * The QueryClient this module creates when the caller does not supply one.
153
- *
154
- * React Query retries every failure three times by default, which for canister
155
- * calls means four attempts and several seconds of backoff on outcomes that
156
- * cannot change — a canister `Err`, a validation failure, a Candid encode
157
- * error that never reached the network. `reactorRetry` keeps the same three
158
- * attempts for transport failures and stops immediately on the rest.
159
- *
160
- * A caller-supplied `queryClient` is left exactly as given; opt in there with
161
- * `defaultOptions: { queries: { retry: reactorRetry } }`.
162
- */
163
- const createDefaultQueryClient = () =>
164
- new QueryClient({
165
- defaultOptions: { queries: { retry: reactorRetry } },
166
- })
167
-
168
- export function defineReactor<Service = BaseActor>(
169
- params: DefineDisplayReactorParameters<Service>
170
- ): DefineReactorResult<Service, "display", DisplayReactor<Service>>
171
-
172
- export function defineReactor<Service = BaseActor>(
173
- params: DefineReactorParameters
174
- ): DefineReactorResult<Service, "candid", Reactor<Service, "candid">>
175
-
176
- export function defineReactor<Service = BaseActor>(
177
- params: DefineReactorParameters | DefineDisplayReactorParameters<Service>
178
- ): DefineReactorResult<Service, any, any> {
179
- const {
180
- clientManager: providedClientManager,
181
- queryClient: providedQueryClient,
182
- authentication: providedAuthentication,
183
- auth,
184
- display,
185
- agentOptions,
186
- allowEnvConfig,
187
- allowEnvRootKey,
188
- name,
189
- idlFactory,
190
- canisterId,
191
- pollingOptions,
192
- } = params as DefineDisplayReactorParameters<Service>
193
-
194
- // A shared AuthenticationManager updates the identity on its own
195
- // ClientManager. Giving this reactor a different one would leave its calls
196
- // anonymous after sign-in, so adopt the manager's rather than building a new
197
- // one, and refuse an explicit mismatch instead of splitting them silently.
198
- if (
199
- providedClientManager &&
200
- providedAuthentication &&
201
- providedAuthentication.clientManager !== providedClientManager
202
- ) {
203
- throw new Error(
204
- `[ic-reactor] defineReactor("${name}") received an \`authentication\` manager bound to a different \`clientManager\`. ` +
205
- `Sign-in would update the authentication manager's agent while this reactor calls through another one, ` +
206
- `leaving its calls anonymous. Pass \`clientManager: authentication.clientManager\`, or omit \`clientManager\` to adopt it.`
207
- )
208
- }
209
-
210
- // `auth` configures a manager this call would build; an existing one is
211
- // already constructed, so these options could only be dropped on the floor.
212
- if (providedAuthentication && auth) {
213
- throw new Error(
214
- `[ic-reactor] defineReactor("${name}") received both \`authentication\` and \`auth\`. ` +
215
- `The supplied manager is already configured, so \`auth\` (${Object.keys(auth).join(", ")}) would be ignored. ` +
216
- `Pass those options where that AuthenticationManager is created, or drop \`authentication\` to build one here.`
217
- )
218
- }
219
-
220
- // Same reasoning as `auth`, and it matters more here: an ignored
221
- // `allowEnvConfig: false` reads as "I locked the cookie out" while the
222
- // supplied manager carries whatever decision it was built with.
223
- if (
224
- (providedClientManager || providedAuthentication) &&
225
- (allowEnvConfig !== undefined || allowEnvRootKey !== undefined)
226
- ) {
227
- const passed = [
228
- allowEnvConfig !== undefined && "allowEnvConfig",
229
- allowEnvRootKey !== undefined && "allowEnvRootKey",
230
- ]
231
- .filter(Boolean)
232
- .join(", ")
233
- throw new Error(
234
- `[ic-reactor] defineReactor("${name}") received both a ClientManager and \`${passed}\`. ` +
235
- `That option is resolved when a ClientManager is constructed, so the supplied one already carries its own ` +
236
- `decision and this would be ignored — silently changing nothing about whether the ic_env cookie is trusted. ` +
237
- `Pass it where that ClientManager is created, or drop \`clientManager\` to build one here.`
238
- )
239
- }
240
-
241
- const clientManager =
242
- providedClientManager ??
243
- providedAuthentication?.clientManager ??
244
- new ClientManager({
245
- queryClient: providedQueryClient ?? createDefaultQueryClient(),
246
- agentOptions,
247
- // Forwarded, not dropped: the type has always accepted these (it extends
248
- // ClientManagerParameters) while the call ignored them, so the one
249
- // documented setup path silently discarded the ic_env opt-in it advertised.
250
- allowEnvConfig,
251
- allowEnvRootKey,
252
- })
253
-
254
- // Always report the QueryClient actually in use: when a ClientManager is
255
- // supplied or adopted, its own QueryClient is the one queries run against.
256
- const queryClient = clientManager.queryClient
257
-
258
- const reactorConfig = {
259
- clientManager,
260
- name,
261
- idlFactory,
262
- canisterId,
263
- pollingOptions,
264
- }
265
-
266
- // The Reactor / DisplayReactor union cannot be expressed through the shared
267
- // implementation signature, so the body is intentionally untyped here; the
268
- // public overloads above carry the precise types for callers.
269
-
270
- const reactor: any = display
271
- ? new DisplayReactor<Service>({
272
- ...reactorConfig,
273
- validators: (params as DefineDisplayReactorParameters<Service>)
274
- .validators,
275
- })
276
- : new Reactor<Service>(reactorConfig)
277
-
278
- // `reactor` is `any` above, which would let the overloads infer `Service`
279
- // as `unknown` and type every hook's arguments as `never`.
280
- const hooks = createActorHooks<Service, any>(reactor)
281
-
282
- // Auth is built on first use. `AuthenticationManager` dynamically imports the
283
- // optional `@icp-sdk/auth` peer as soon as it is constructed, and reactors
284
- // that never touch authentication should not pay for that.
285
- let authenticationInstance: AuthenticationManager | undefined
286
- const getAuthentication = () =>
287
- (authenticationInstance ??=
288
- providedAuthentication ??
289
- new AuthenticationManager({ ...auth, clientManager }))
290
-
291
- let identityAttributesInstance: IdentityAttributesManager | undefined
292
- const getIdentityAttributes = () =>
293
- (identityAttributesInstance ??= new IdentityAttributesManager(
294
- getAuthentication()
295
- ))
296
-
297
- let authHooks: CreateAuthHooksReturn | undefined
298
- const getAuthHooks = () =>
299
- (authHooks ??= createAuthHooks(getAuthentication()))
300
-
301
- let attributeHooks:
302
- ReturnType<typeof createIdentityAttributeHooks> | undefined
303
- const getAttributeHooks = () =>
304
- (attributeHooks ??= createIdentityAttributeHooks(getIdentityAttributes()))
305
-
306
- return {
307
- ...hooks,
308
- reactor,
309
- clientManager,
310
- queryClient,
311
- // Stable wrappers: the hook call order inside them never changes, so the
312
- // rules of hooks still hold.
313
- useAuth: () => getAuthHooks().useAuth(),
314
- useAgentState: () => getAuthHooks().useAgentState(),
315
- useUserPrincipal: () => getAuthHooks().useUserPrincipal(),
316
- useIdentityAttributes: () => getAttributeHooks().useIdentityAttributes(),
317
- get authentication() {
318
- return getAuthentication()
319
- },
320
- get identityAttributes() {
321
- return getIdentityAttributes()
322
- },
323
- }
324
- }