@ic-reactor/react 3.12.5 → 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.
Files changed (152) hide show
  1. package/README.md +410 -44
  2. package/dist/auth/auth-client-compat.d.ts +122 -0
  3. package/dist/auth/auth-client-compat.d.ts.map +1 -0
  4. package/dist/auth/auth-client-compat.js +162 -0
  5. package/dist/auth/auth-client-compat.js.map +1 -0
  6. package/dist/auth/authentication-manager.d.ts +287 -5
  7. package/dist/auth/authentication-manager.d.ts.map +1 -1
  8. package/dist/auth/authentication-manager.js +920 -150
  9. package/dist/auth/authentication-manager.js.map +1 -1
  10. package/dist/auth/createIdentityAttributeHooks.d.ts.map +1 -1
  11. package/dist/auth/createIdentityAttributeHooks.js +36 -20
  12. package/dist/auth/createIdentityAttributeHooks.js.map +1 -1
  13. package/dist/auth/identity-attributes-manager.d.ts +2 -1
  14. package/dist/auth/identity-attributes-manager.d.ts.map +1 -1
  15. package/dist/auth/identity-attributes-manager.js +90 -6
  16. package/dist/auth/identity-attributes-manager.js.map +1 -1
  17. package/dist/auth/identity-attributes.d.ts.map +1 -1
  18. package/dist/auth/identity-attributes.js +57 -0
  19. package/dist/auth/identity-attributes.js.map +1 -1
  20. package/dist/auth/local-ii-probe.d.ts +12 -1
  21. package/dist/auth/local-ii-probe.d.ts.map +1 -1
  22. package/dist/auth/local-ii-probe.js +22 -3
  23. package/dist/auth/local-ii-probe.js.map +1 -1
  24. package/dist/auth/types.d.ts +48 -5
  25. package/dist/auth/types.d.ts.map +1 -1
  26. package/dist/createActorHooks.d.ts +7 -18
  27. package/dist/createActorHooks.d.ts.map +1 -1
  28. package/dist/createActorHooks.js.map +1 -1
  29. package/dist/createInfiniteQuery.d.ts +51 -10
  30. package/dist/createInfiniteQuery.d.ts.map +1 -1
  31. package/dist/createInfiniteQuery.js +39 -15
  32. package/dist/createInfiniteQuery.js.map +1 -1
  33. package/dist/createMutation.d.ts +3 -0
  34. package/dist/createMutation.d.ts.map +1 -1
  35. package/dist/createMutation.js +87 -80
  36. package/dist/createMutation.js.map +1 -1
  37. package/dist/createQuery.d.ts +35 -2
  38. package/dist/createQuery.d.ts.map +1 -1
  39. package/dist/createQuery.js +104 -17
  40. package/dist/createQuery.js.map +1 -1
  41. package/dist/createReactorProvider.d.ts +158 -0
  42. package/dist/createReactorProvider.d.ts.map +1 -0
  43. package/dist/createReactorProvider.js +256 -0
  44. package/dist/createReactorProvider.js.map +1 -0
  45. package/dist/createSuspenseInfiniteQuery.d.ts +16 -9
  46. package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
  47. package/dist/createSuspenseInfiniteQuery.js +59 -27
  48. package/dist/createSuspenseInfiniteQuery.js.map +1 -1
  49. package/dist/createSuspenseQuery.d.ts +23 -2
  50. package/dist/createSuspenseQuery.d.ts.map +1 -1
  51. package/dist/createSuspenseQuery.js +68 -21
  52. package/dist/createSuspenseQuery.js.map +1 -1
  53. package/dist/defineDisplayReactor.d.ts +43 -0
  54. package/dist/defineDisplayReactor.d.ts.map +1 -0
  55. package/dist/defineDisplayReactor.js +42 -0
  56. package/dist/defineDisplayReactor.js.map +1 -0
  57. package/dist/defineReactor.d.ts +46 -72
  58. package/dist/defineReactor.d.ts.map +1 -1
  59. package/dist/defineReactor.js +11 -176
  60. package/dist/defineReactor.js.map +1 -1
  61. package/dist/defineReactorShared.d.ts +84 -0
  62. package/dist/defineReactorShared.d.ts.map +1 -0
  63. package/dist/defineReactorShared.js +139 -0
  64. package/dist/defineReactorShared.js.map +1 -0
  65. package/dist/hooks/createAuthHooks.d.ts +7 -0
  66. package/dist/hooks/createAuthHooks.d.ts.map +1 -1
  67. package/dist/hooks/createAuthHooks.js +180 -20
  68. package/dist/hooks/createAuthHooks.js.map +1 -1
  69. package/dist/hooks/useActorInfiniteQuery.d.ts +34 -6
  70. package/dist/hooks/useActorInfiniteQuery.d.ts.map +1 -1
  71. package/dist/hooks/useActorInfiniteQuery.js +54 -21
  72. package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
  73. package/dist/hooks/useActorMethod.d.ts +37 -4
  74. package/dist/hooks/useActorMethod.d.ts.map +1 -1
  75. package/dist/hooks/useActorMethod.js +201 -57
  76. package/dist/hooks/useActorMethod.js.map +1 -1
  77. package/dist/hooks/useActorMutation.d.ts +10 -7
  78. package/dist/hooks/useActorMutation.d.ts.map +1 -1
  79. package/dist/hooks/useActorMutation.js +14 -13
  80. package/dist/hooks/useActorMutation.js.map +1 -1
  81. package/dist/hooks/useActorQuery.d.ts +17 -4
  82. package/dist/hooks/useActorQuery.d.ts.map +1 -1
  83. package/dist/hooks/useActorQuery.js +30 -9
  84. package/dist/hooks/useActorQuery.js.map +1 -1
  85. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts +15 -3
  86. package/dist/hooks/useActorSuspenseInfiniteQuery.d.ts.map +1 -1
  87. package/dist/hooks/useActorSuspenseInfiniteQuery.js +37 -17
  88. package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
  89. package/dist/hooks/useActorSuspenseQuery.d.ts +2 -2
  90. package/dist/hooks/useActorSuspenseQuery.d.ts.map +1 -1
  91. package/dist/hooks/useActorSuspenseQuery.js +20 -9
  92. package/dist/hooks/useActorSuspenseQuery.js.map +1 -1
  93. package/dist/index.d.ts +4 -0
  94. package/dist/index.d.ts.map +1 -1
  95. package/dist/index.js +6 -0
  96. package/dist/index.js.map +1 -1
  97. package/dist/ownedAuthentication.d.ts +52 -0
  98. package/dist/ownedAuthentication.d.ts.map +1 -0
  99. package/dist/ownedAuthentication.js +49 -0
  100. package/dist/ownedAuthentication.js.map +1 -0
  101. package/dist/server.d.ts +21 -0
  102. package/dist/server.d.ts.map +1 -0
  103. package/dist/server.js +23 -0
  104. package/dist/server.js.map +1 -0
  105. package/dist/testing.d.ts +19 -0
  106. package/dist/testing.d.ts.map +1 -0
  107. package/dist/testing.js +19 -0
  108. package/dist/testing.js.map +1 -0
  109. package/dist/types.d.ts +416 -15
  110. package/dist/types.d.ts.map +1 -1
  111. package/dist/types.js +1 -1
  112. package/dist/utils.d.ts +159 -3
  113. package/dist/utils.d.ts.map +1 -1
  114. package/dist/utils.js +301 -1
  115. package/dist/utils.js.map +1 -1
  116. package/dist/validation.d.ts +12 -7
  117. package/dist/validation.d.ts.map +1 -1
  118. package/dist/validation.js +34 -15
  119. package/dist/validation.js.map +1 -1
  120. package/llms.txt +259 -33
  121. package/package.json +17 -5
  122. package/src/auth/auth-client-compat.ts +273 -0
  123. package/src/auth/authentication-manager.ts +918 -96
  124. package/src/auth/createIdentityAttributeHooks.ts +47 -21
  125. package/src/auth/identity-attributes-manager.ts +100 -5
  126. package/src/auth/identity-attributes.ts +75 -0
  127. package/src/auth/local-ii-probe.ts +29 -3
  128. package/src/auth/types.ts +49 -6
  129. package/src/createActorHooks.ts +20 -32
  130. package/src/createInfiniteQuery.ts +120 -28
  131. package/src/createMutation.ts +161 -178
  132. package/src/createQuery.ts +164 -32
  133. package/src/createReactorProvider.ts +365 -0
  134. package/src/createSuspenseInfiniteQuery.ts +93 -43
  135. package/src/createSuspenseQuery.ts +102 -32
  136. package/src/defineDisplayReactor.ts +62 -0
  137. package/src/defineReactor.ts +81 -263
  138. package/src/defineReactorShared.ts +268 -0
  139. package/src/hooks/createAuthHooks.ts +206 -24
  140. package/src/hooks/useActorInfiniteQuery.ts +122 -49
  141. package/src/hooks/useActorMethod.ts +295 -92
  142. package/src/hooks/useActorMutation.ts +23 -23
  143. package/src/hooks/useActorQuery.ts +43 -10
  144. package/src/hooks/useActorSuspenseInfiniteQuery.ts +93 -50
  145. package/src/hooks/useActorSuspenseQuery.ts +30 -15
  146. package/src/index.ts +8 -0
  147. package/src/ownedAuthentication.ts +81 -0
  148. package/src/server.ts +23 -0
  149. package/src/testing.ts +18 -0
  150. package/src/types.ts +463 -14
  151. package/src/utils.ts +387 -3
  152. package/src/validation.ts +43 -19
@@ -1,9 +1,64 @@
1
+ // DisplayReactor is only for the deprecated `display: true` path. It is what
2
+ // puts DisplayReactor and zod in every bundle that uses defineReactor; drop it
3
+ // with that path.
4
+ import { DisplayReactor, Reactor } from "@ic-reactor/core"
5
+ import type { BaseActor } from "@ic-reactor/core"
6
+ import { defineReactorWith } from "./defineReactorShared.js"
7
+ import type {
8
+ DefineReactorSharedParameters,
9
+ DefineReactorResult,
10
+ } from "./defineReactorShared.js"
11
+ import type { DefineDisplayReactorOptions } from "./defineDisplayReactor.js"
12
+
13
+ export type {
14
+ DefineReactorSharedParameters,
15
+ DefineReactorResult,
16
+ } from "./defineReactorShared.js"
17
+
18
+ /** Parameters for a standard (raw Candid types) reactor. */
19
+ export interface DefineReactorParameters extends DefineReactorSharedParameters {
20
+ display?: false
21
+ }
22
+
23
+ /**
24
+ * Parameters for `defineReactor({ display: true })`.
25
+ *
26
+ * @deprecated Call `defineDisplayReactor` with the same options, without
27
+ * `display`: see {@link DefineDisplayReactorOptions}.
28
+ */
29
+ export interface DefineDisplayReactorParameters<
30
+ Service,
31
+ > extends DefineDisplayReactorOptions<Service> {
32
+ /**
33
+ * @deprecated Use `defineDisplayReactor(...)` instead of
34
+ * `defineReactor({ display: true, ... })`. The flag keeps working until the
35
+ * next major, but it makes every bundle that calls `defineReactor` carry
36
+ * `DisplayReactor` and zod, even when it never sets the flag.
37
+ */
38
+ display: true
39
+ }
40
+
41
+ /**
42
+ * The `display: true` form: builds a DisplayReactor and its hooks.
43
+ *
44
+ * @deprecated `defineReactor({ display: true })` is deprecated: pass the same
45
+ * options, without `display`, to `defineDisplayReactor(...)`. Only this form
46
+ * is; `defineReactor` without `display` builds a plain `Reactor` and is not
47
+ * deprecated. The flag keeps working until the next major, but it makes every
48
+ * bundle that calls `defineReactor` carry `DisplayReactor` and zod, even when
49
+ * it never sets the flag.
50
+ */
51
+ export function defineReactor<Service = BaseActor>(
52
+ params: DefineDisplayReactorParameters<Service>
53
+ ): DefineReactorResult<Service, "display", DisplayReactor<Service>>
54
+
1
55
  /**
2
- * defineReactor - One-call bootstrap for a canister reactor + its React hooks.
56
+ * One-call bootstrap for a canister reactor + its React hooks.
3
57
  *
4
58
  * 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.
59
+ * createActorHooks) into a single call. It builds a `Reactor`, which takes and
60
+ * returns raw Candid values; `defineDisplayReactor` takes the same options and
61
+ * builds a `DisplayReactor` for UI-friendly ones.
7
62
  * Use this when one-call bootstrap is enough. If you need reusable
8
63
  * method-specific operations outside React, compose with query/mutation
9
64
  * factories after setup.
@@ -17,7 +72,6 @@
17
72
  * name: "backend",
18
73
  * idlFactory,
19
74
  * canisterId: "rrkah-fqaaa-aaaaa-aaaaq-cai",
20
- * display: true, // ⇒ DisplayReactor (string principals/bigints)
21
75
  * })
22
76
  *
23
77
  * function Profile() {
@@ -28,10 +82,15 @@
28
82
  *
29
83
  * @example Reuse an existing ClientManager (multiple canisters share one agent)
30
84
  * ```typescript
31
- * const ledger = defineReactor<_LEDGER>({ name: "ledger", idlFactory: ledgerIdl })
85
+ * const ledger = defineReactor<_LEDGER>({
86
+ * name: "ledger",
87
+ * idlFactory: ledgerIdl,
88
+ * canisterId: "ryjl3-tyaaa-aaaaa-aaaba-cai",
89
+ * })
32
90
  * const index = defineReactor<_INDEX>({
33
91
  * name: "index",
34
92
  * idlFactory: indexIdl,
93
+ * canisterId: "qhbym-qaaaa-aaaaa-aaafq-cai",
35
94
  * clientManager: ledger.clientManager,
36
95
  * authentication: ledger.authentication, // one Internet Identity session
37
96
  * })
@@ -42,6 +101,7 @@
42
101
  * const { useAuth, useIdentityAttributes } = defineReactor<_SERVICE>({
43
102
  * name: "backend",
44
103
  * idlFactory,
104
+ * canisterId: "rrkah-fqaaa-aaaaa-aaaaq-cai",
45
105
  * // Needed when the app is served from more than one origin.
46
106
  * auth: { derivationOrigin: "https://app.example.com" },
47
107
  * })
@@ -54,271 +114,29 @@
54
114
  * }
55
115
  * ```
56
116
  */
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
117
  export function defineReactor<Service = BaseActor>(
173
118
  params: DefineReactorParameters
174
119
  ): DefineReactorResult<Service, "candid", Reactor<Service, "candid">>
175
120
 
176
121
  export function defineReactor<Service = BaseActor>(
177
122
  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.`
123
+ ):
124
+ | DefineReactorResult<Service, "display", DisplayReactor<Service>>
125
+ | DefineReactorResult<Service, "candid", Reactor<Service, "candid">> {
126
+ if (params.display) {
127
+ // What defineDisplayReactor builds, but under this function's name, so an
128
+ // error names the call the app actually made.
129
+ const { validators } = params
130
+ return defineReactorWith<Service, "display", DisplayReactor<Service>>(
131
+ "defineReactor",
132
+ params,
133
+ (config) => new DisplayReactor<Service>({ ...config, validators })
217
134
  )
218
135
  }
219
136
 
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
- }
137
+ return defineReactorWith<Service, "candid", Reactor<Service, "candid">>(
138
+ "defineReactor",
139
+ params,
140
+ (config) => new Reactor<Service>(config)
141
+ )
324
142
  }
@@ -0,0 +1,268 @@
1
+ /**
2
+ * The body `defineReactor` and `defineDisplayReactor` share.
3
+ *
4
+ * It takes the reactor's construction as a callback, so this module imports
5
+ * neither reactor class. That matters for bundle size: `DisplayReactor` builds
6
+ * its codecs on zod, and a module that imported it here would put zod in every
7
+ * app that calls `defineReactor`, whether or not it ever builds a
8
+ * DisplayReactor. Keep it that way — no `DisplayReactor` import in this file.
9
+ */
10
+ import { ClientManager, reactorRetry } from "@ic-reactor/core"
11
+ import type {
12
+ Reactor,
13
+ TransformKey,
14
+ ReactorParameters,
15
+ ClientManagerParameters,
16
+ } from "@ic-reactor/core"
17
+ import { QueryClient } from "@tanstack/react-query"
18
+ import { createActorHooks, ActorHooks } from "./createActorHooks.js"
19
+ import { AuthenticationManager } from "./auth/authentication-manager.js"
20
+ import { IdentityAttributesManager } from "./auth/identity-attributes-manager.js"
21
+ import { createIdentityAttributeHooks } from "./auth/createIdentityAttributeHooks.js"
22
+ import type { UseIdentityAttributesReturn } from "./auth/createIdentityAttributeHooks.js"
23
+ import { createAuthHooks } from "./hooks/createAuthHooks.js"
24
+ import type { CreateAuthHooksReturn } from "./hooks/createAuthHooks.js"
25
+ import type { AuthenticationManagerParameters } from "./auth/authentication-manager.js"
26
+ import { registerAuthentication } from "./ownedAuthentication.js"
27
+
28
+ /** Options shared by both the standard and display variants of defineReactor. */
29
+ export interface DefineReactorSharedParameters
30
+ extends
31
+ Omit<ReactorParameters, "clientManager">,
32
+ Omit<ClientManagerParameters, "queryClient"> {
33
+ /**
34
+ * Reuse an existing ClientManager (e.g. to share one agent across canisters).
35
+ * When omitted, a ClientManager is created from the agent options below.
36
+ *
37
+ * A supplied or adopted manager brings its own agent and QueryClient, so
38
+ * `agentOptions`, `queryClient` and `allowEnvConfig` apply only when this
39
+ * call creates one.
40
+ */
41
+ clientManager?: ClientManager
42
+ /**
43
+ * QueryClient for a ClientManager created by this call.
44
+ *
45
+ * Ignored when `clientManager` or `authentication` is supplied — queries run
46
+ * against that manager's own QueryClient, which is what is returned.
47
+ */
48
+ queryClient?: QueryClient
49
+ /**
50
+ * Reuse an existing AuthenticationManager, so several reactors share one
51
+ * Internet Identity session. When omitted, one is created for this reactor.
52
+ *
53
+ * Its `clientManager` is adopted for this reactor, so sign-in updates the
54
+ * same agent the reactor calls through. Supplying a different `clientManager`
55
+ * alongside it is rejected.
56
+ */
57
+ authentication?: AuthenticationManager
58
+ /**
59
+ * Internet Identity options forwarded to the AuthenticationManager
60
+ * (`identityProvider`, `derivationOrigin`, `idleOptions`, `storage`, …).
61
+ *
62
+ * Not every option reaches both `@icp-sdk/auth` majors. `idleOptions`,
63
+ * `storage`, `keyType` and `identity` are honoured only by v8: v10 has no
64
+ * equivalent, so IC Reactor drops each with a one-time warning. For idle
65
+ * handling on v10, pass `maxTimeToIdle` to `login()` and set
66
+ * `disableBrowserActivity` here. `disableBrowserActivity` is v10-only.
67
+ *
68
+ * Mutually exclusive with `authentication`: a manager built elsewhere is
69
+ * already configured, so these could not be applied to it.
70
+ */
71
+ auth?: Omit<AuthenticationManagerParameters, "clientManager">
72
+ }
73
+
74
+ /** The reactor instance plus its bound hooks and shared infrastructure. */
75
+ export type DefineReactorResult<
76
+ Service,
77
+ Transform extends TransformKey,
78
+ R extends Reactor<Service, Transform>,
79
+ > = ActorHooks<Service, Transform> &
80
+ CreateAuthHooksReturn & {
81
+ reactor: R
82
+ clientManager: ClientManager
83
+ queryClient: QueryClient
84
+ /** Internet Identity session manager backing `useAuth`. */
85
+ authentication: AuthenticationManager
86
+ /** Signed identity attribute requests backing `useIdentityAttributes`. */
87
+ identityAttributes: IdentityAttributesManager
88
+ useIdentityAttributes: () => UseIdentityAttributesReturn
89
+ }
90
+
91
+ /**
92
+ * The QueryClient this module creates when the caller does not supply one.
93
+ *
94
+ * React Query retries every failure three times by default, which for canister
95
+ * calls means four attempts and several seconds of backoff on outcomes that
96
+ * cannot change — a canister `Err`, a validation failure, a Candid encode
97
+ * error that never reached the network. `reactorRetry` keeps the same three
98
+ * attempts for transport failures and stops immediately on the rest.
99
+ *
100
+ * A caller-supplied `queryClient` is left exactly as given; opt in there with
101
+ * `defaultOptions: { queries: { retry: reactorRetry } }`.
102
+ */
103
+ const createDefaultQueryClient = () =>
104
+ new QueryClient({
105
+ defaultOptions: { queries: { retry: reactorRetry } },
106
+ })
107
+
108
+ /**
109
+ * Builds the ClientManager, the reactor `createReactor` returns, its hooks and
110
+ * the lazily created auth managers, for `defineReactor` and
111
+ * `defineDisplayReactor`. Not part of the public API.
112
+ *
113
+ * `caller` is the function the app called; the errors below name it, so an
114
+ * app that called `defineDisplayReactor` is not sent looking for a
115
+ * `defineReactor` call it never made.
116
+ *
117
+ * @internal
118
+ */
119
+ export function defineReactorWith<
120
+ Service,
121
+ Transform extends TransformKey,
122
+ R extends Reactor<Service, Transform>,
123
+ >(
124
+ caller: "defineReactor" | "defineDisplayReactor",
125
+ params: DefineReactorSharedParameters,
126
+ createReactor: (config: ReactorParameters) => R
127
+ ): DefineReactorResult<Service, Transform, R> {
128
+ const {
129
+ clientManager: providedClientManager,
130
+ queryClient: providedQueryClient,
131
+ authentication: providedAuthentication,
132
+ auth,
133
+ agentOptions,
134
+ allowEnvConfig,
135
+ allowEnvRootKey,
136
+ name,
137
+ idlFactory,
138
+ canisterId,
139
+ pollingOptions,
140
+ } = params
141
+
142
+ // A shared AuthenticationManager updates the identity on its own
143
+ // ClientManager. Giving this reactor a different one would leave its calls
144
+ // anonymous after sign-in, so adopt the manager's rather than building a new
145
+ // one, and refuse an explicit mismatch instead of splitting them silently.
146
+ if (
147
+ providedClientManager &&
148
+ providedAuthentication &&
149
+ providedAuthentication.clientManager !== providedClientManager
150
+ ) {
151
+ throw new Error(
152
+ `[ic-reactor] ${caller}("${name}") received an \`authentication\` manager bound to a different \`clientManager\`. ` +
153
+ `Sign-in would update the authentication manager's agent while this reactor calls through another one, ` +
154
+ `leaving its calls anonymous. Pass \`clientManager: authentication.clientManager\`, or omit \`clientManager\` to adopt it.`
155
+ )
156
+ }
157
+
158
+ // `auth` configures a manager this call would build; an existing one is
159
+ // already constructed, so these options could only be dropped on the floor.
160
+ if (providedAuthentication && auth) {
161
+ throw new Error(
162
+ `[ic-reactor] ${caller}("${name}") received both \`authentication\` and \`auth\`. ` +
163
+ `The supplied manager is already configured, so \`auth\` (${Object.keys(auth).join(", ")}) would be ignored. ` +
164
+ `Pass those options where that AuthenticationManager is created, or drop \`authentication\` to build one here.`
165
+ )
166
+ }
167
+
168
+ // Same reasoning as `auth`, and it matters more here: an ignored
169
+ // `allowEnvConfig: false` reads as "I locked the cookie out" while the
170
+ // supplied manager carries whatever decision it was built with.
171
+ if (
172
+ (providedClientManager || providedAuthentication) &&
173
+ (allowEnvConfig !== undefined || allowEnvRootKey !== undefined)
174
+ ) {
175
+ const passed = [
176
+ allowEnvConfig !== undefined && "allowEnvConfig",
177
+ allowEnvRootKey !== undefined && "allowEnvRootKey",
178
+ ]
179
+ .filter(Boolean)
180
+ .join(", ")
181
+ throw new Error(
182
+ `[ic-reactor] ${caller}("${name}") received both a ClientManager and \`${passed}\`. ` +
183
+ `That option is resolved when a ClientManager is constructed, so the supplied one already carries its own ` +
184
+ `decision and this would be ignored — silently changing nothing about whether the ic_env cookie is trusted. ` +
185
+ `Pass it where that ClientManager is created, or drop \`clientManager\` to build one here.`
186
+ )
187
+ }
188
+
189
+ const clientManager =
190
+ providedClientManager ??
191
+ providedAuthentication?.clientManager ??
192
+ new ClientManager({
193
+ queryClient: providedQueryClient ?? createDefaultQueryClient(),
194
+ agentOptions,
195
+ // Forwarded, not dropped: the type has always accepted these (it extends
196
+ // ClientManagerParameters) while the call ignored them, so the one
197
+ // documented setup path silently discarded the ic_env opt-in it advertised.
198
+ allowEnvConfig,
199
+ allowEnvRootKey,
200
+ })
201
+
202
+ // Always report the QueryClient actually in use: when a ClientManager is
203
+ // supplied or adopted, its own QueryClient is the one queries run against.
204
+ const queryClient = clientManager.queryClient
205
+
206
+ const reactor = createReactor({
207
+ clientManager,
208
+ name,
209
+ idlFactory,
210
+ canisterId,
211
+ pollingOptions,
212
+ })
213
+
214
+ const hooks = createActorHooks<Service, Transform>(reactor)
215
+
216
+ // Auth is built on first use. `AuthenticationManager` dynamically imports the
217
+ // optional `@icp-sdk/auth` peer as soon as it is constructed, and reactors
218
+ // that never touch authentication should not pay for that.
219
+ let authenticationInstance: AuthenticationManager | undefined
220
+ const getAuthentication = () =>
221
+ (authenticationInstance ??=
222
+ providedAuthentication ??
223
+ new AuthenticationManager({ ...auth, clientManager }))
224
+
225
+ let identityAttributesInstance: IdentityAttributesManager | undefined
226
+ const getIdentityAttributes = () =>
227
+ (identityAttributesInstance ??= new IdentityAttributesManager(
228
+ getAuthentication()
229
+ ))
230
+
231
+ let authHooks: CreateAuthHooksReturn | undefined
232
+ const getAuthHooks = () =>
233
+ (authHooks ??= createAuthHooks(getAuthentication()))
234
+
235
+ let attributeHooks:
236
+ ReturnType<typeof createIdentityAttributeHooks> | undefined
237
+ const getAttributeHooks = () =>
238
+ (attributeHooks ??= createIdentityAttributeHooks(getIdentityAttributes()))
239
+
240
+ const result: DefineReactorResult<Service, Transform, R> = {
241
+ ...hooks,
242
+ reactor,
243
+ clientManager,
244
+ queryClient,
245
+ // Stable wrappers: the hook call order inside them never changes, so the
246
+ // rules of hooks still hold.
247
+ useAuth: () => getAuthHooks().useAuth(),
248
+ useAgentState: () => getAuthHooks().useAgentState(),
249
+ useUserPrincipal: () => getAuthHooks().useUserPrincipal(),
250
+ useIdentityAttributes: () => getAttributeHooks().useIdentityAttributes(),
251
+ get authentication() {
252
+ return getAuthentication()
253
+ },
254
+ get identityAttributes() {
255
+ return getIdentityAttributes()
256
+ },
257
+ }
258
+
259
+ // For `createReactorProvider`, which disposes the manager this result
260
+ // builds when its tree unmounts. It reads the manager without the getter
261
+ // above, so a tree that never touched authentication does not build one
262
+ // just to release it. A supplied manager is not this result's to release.
263
+ registerAuthentication(result, () =>
264
+ providedAuthentication ? undefined : authenticationInstance
265
+ )
266
+
267
+ return result
268
+ }