@ic-reactor/core 3.5.1 → 3.7.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 (75) hide show
  1. package/README.md +6 -11
  2. package/dist/client.d.ts +17 -79
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +49 -527
  5. package/dist/client.js.map +1 -1
  6. package/dist/display/visitor.d.ts.map +1 -1
  7. package/dist/display/visitor.js +6 -1
  8. package/dist/display/visitor.js.map +1 -1
  9. package/dist/display-reactor.d.ts +3 -0
  10. package/dist/display-reactor.d.ts.map +1 -1
  11. package/dist/display-reactor.js +3 -0
  12. package/dist/display-reactor.js.map +1 -1
  13. package/dist/index.d.ts +0 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +0 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/reactor.d.ts +4 -4
  18. package/dist/reactor.d.ts.map +1 -1
  19. package/dist/reactor.js +27 -10
  20. package/dist/reactor.js.map +1 -1
  21. package/dist/types/client.d.ts +1 -120
  22. package/dist/types/client.d.ts.map +1 -1
  23. package/dist/types/display-reactor.d.ts.map +1 -1
  24. package/dist/types/reactor.d.ts +8 -0
  25. package/dist/types/reactor.d.ts.map +1 -1
  26. package/dist/types/variant.d.ts.map +1 -1
  27. package/dist/utils/agent.d.ts +3 -2
  28. package/dist/utils/agent.d.ts.map +1 -1
  29. package/dist/utils/agent.js +9 -3
  30. package/dist/utils/agent.js.map +1 -1
  31. package/dist/utils/constants.d.ts +0 -2
  32. package/dist/utils/constants.d.ts.map +1 -1
  33. package/dist/utils/constants.js +0 -2
  34. package/dist/utils/constants.js.map +1 -1
  35. package/dist/utils/helper.d.ts +14 -1
  36. package/dist/utils/helper.d.ts.map +1 -1
  37. package/dist/utils/helper.js +48 -9
  38. package/dist/utils/helper.js.map +1 -1
  39. package/dist/utils/index.d.ts +1 -0
  40. package/dist/utils/index.d.ts.map +1 -1
  41. package/dist/utils/index.js +1 -0
  42. package/dist/utils/index.js.map +1 -1
  43. package/dist/utils/polling.d.ts.map +1 -1
  44. package/dist/utils/polling.js +2 -2
  45. package/dist/utils/polling.js.map +1 -1
  46. package/dist/utils/query-data.d.ts +4 -0
  47. package/dist/utils/query-data.d.ts.map +1 -0
  48. package/dist/utils/query-data.js +3 -0
  49. package/dist/utils/query-data.js.map +1 -0
  50. package/dist/version.d.ts.map +1 -1
  51. package/dist/version.js +2 -2
  52. package/dist/version.js.map +1 -1
  53. package/llms.txt +43 -0
  54. package/package.json +16 -18
  55. package/src/client.ts +53 -627
  56. package/src/display/visitor.ts +10 -1
  57. package/src/display-reactor.ts +3 -0
  58. package/src/index.ts +0 -1
  59. package/src/reactor.ts +48 -18
  60. package/src/types/client.ts +1 -138
  61. package/src/types/display-reactor.ts +1 -2
  62. package/src/types/reactor.ts +15 -0
  63. package/src/types/variant.ts +8 -6
  64. package/src/utils/agent.ts +12 -3
  65. package/src/utils/constants.ts +0 -5
  66. package/src/utils/helper.ts +52 -7
  67. package/src/utils/index.ts +1 -0
  68. package/src/utils/polling.ts +3 -3
  69. package/src/utils/query-data.ts +5 -0
  70. package/src/version.ts +2 -2
  71. package/dist/identity-attributes.d.ts +0 -14
  72. package/dist/identity-attributes.d.ts.map +0 -1
  73. package/dist/identity-attributes.js +0 -161
  74. package/dist/identity-attributes.js.map +0 -1
  75. package/src/identity-attributes.ts +0 -256
package/src/client.ts CHANGED
@@ -1,68 +1,50 @@
1
1
  import type { Identity } from "@icp-sdk/core/agent"
2
- import type {
3
- ClientManagerParameters,
4
- AgentState,
5
- AuthState,
6
- AuthClientSignInOptions,
7
- AuthClientLike,
8
- ClientManagerAuthClientOptions,
9
- ClientManagerSignInOptions,
10
- IdentityAttributeResult,
11
- RequestIdentityAttributesParameters,
12
- RequestOpenIdIdentityAttributesParameters,
13
- } from "./types/client"
2
+ import type { ClientManagerParameters, AgentState } from "./types/client"
14
3
  import type { Principal } from "@icp-sdk/core/principal"
15
- import type { QueryClient } from "@tanstack/react-query"
4
+ import type { QueryClient } from "@tanstack/query-core"
16
5
 
17
6
  import { HttpAgent } from "@icp-sdk/core/agent"
18
7
  import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"
19
- import {
20
- IC_HOST_NETWORK_URI,
21
- IC_INTERNET_IDENTITY_PROVIDER,
22
- LOCAL_INTERNET_IDENTITY_PROVIDER,
23
- } from "./utils/constants"
8
+ import { IC_HOST_NETWORK_URI } from "./utils/constants"
24
9
  import {
25
10
  getNetworkByHostname,
26
11
  getProcessEnvNetwork,
27
12
  isDev,
13
+ isMainnetHost,
28
14
  } from "./utils/helper"
29
- import {
30
- decodeIdentityAttributeValues,
31
- IDENTITY_ATTRIBUTES_BETA_PROVIDER,
32
- identityAttributeKeys,
33
- normalizeSignedIdentityAttributes,
34
- } from "./identity-attributes"
35
-
36
- type AuthClientConstructor = {
37
- new (options?: ClientManagerAuthClientOptions): AuthClientLike
38
- }
39
15
 
40
16
  /**
41
- * ClientManager is a central class for managing the Internet Computer (IC) agent and authentication state.
17
+ * ClientManager is a central class for managing the Internet Computer (IC) agent.
42
18
  *
43
- * It initializes the agent (connecting to local or mainnet), handles authentication via AuthClient,
44
- * and integrates with TanStack Query's QueryClient for state management.
19
+ * It initializes the agent (connecting to local or mainnet) and integrates
20
+ * with TanStack Query's QueryClient for state management.
21
+ * Use this as a singleton shared by all reactors in an app.
45
22
  *
46
23
  * @example
47
24
  * ```typescript
48
25
  * import { ClientManager } from "@ic-reactor/core";
49
- * import { QueryClient } from "@tanstack/react-query";
26
+ * import { QueryClient } from "@tanstack/query-core";
50
27
  *
51
28
  * const queryClient = new QueryClient();
52
29
  * const clientManager = new ClientManager({
53
30
  * queryClient,
54
- * withLocalEnv: true, // Use local replica
31
+ * agentOptions: { host: "http://127.0.0.1:4943" },
55
32
  * });
56
33
  *
57
34
  * await clientManager.initialize();
58
35
  * ```
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * // Reuse the same ClientManager across multiple canisters
40
+ * const backend = new Reactor<BackendService>({ clientManager, idlFactory: backendIdl, name: "backend" })
41
+ * const ledger = new Reactor<LedgerService>({ clientManager, idlFactory: ledgerIdl, name: "ledger" })
42
+ * ```
59
43
  */
60
44
  export class ClientManager {
61
45
  #agent: HttpAgent
62
- #authClient?: AuthClientLike
63
46
  #identitySubscribers: Array<(identity: Identity) => void> = []
64
47
  #agentStateSubscribers: Array<(state: AgentState) => void> = []
65
- #authStateSubscribers: Array<(state: AuthState) => void> = []
66
48
  #targetCanisterIds: Set<string> = new Set()
67
49
 
68
50
  /**
@@ -73,36 +55,14 @@ export class ClientManager {
73
55
  * Current state of the HttpAgent, including initialization status, network, and error information.
74
56
  */
75
57
  public agentState: AgentState
76
- /**
77
- * Current authentication state, including the active identity, authentication progress, and errors.
78
- */
79
- public authState: AuthState
80
-
81
58
  private initPromise?: Promise<void>
82
- private authPromise?: Promise<Identity | undefined>
83
- private authClientConstructor?: AuthClientConstructor
84
- private authClientConstructorPromise?: Promise<
85
- AuthClientConstructor | undefined
86
- >
87
- private authStateRevision = 0
88
- private authClientWasProvided = false
89
- private port: number
90
- private internetIdentityId?: string
91
59
 
92
60
  /**
93
61
  * Creates a new instance of ClientManager.
94
62
  *
95
63
  * @param parameters - Configuration options for the agent and network environment.
96
64
  */
97
- constructor({
98
- port = 4943,
99
- withLocalEnv,
100
- withProcessEnv,
101
- withCanisterEnv,
102
- agentOptions = {},
103
- queryClient,
104
- authClient,
105
- }: ClientManagerParameters) {
65
+ constructor({ agentOptions = {}, queryClient }: ClientManagerParameters) {
106
66
  this.queryClient = queryClient
107
67
 
108
68
  this.agentState = {
@@ -113,70 +73,49 @@ export class ClientManager {
113
73
  isLocalhost: false,
114
74
  }
115
75
 
116
- this.authState = {
117
- identity: null,
118
- isAuthenticating: false,
119
- isAuthenticated: false,
120
- error: undefined,
121
- }
122
-
123
- this.port = port
76
+ const canisterEnv =
77
+ typeof window !== "undefined" ? safeGetCanisterEnv() : undefined
124
78
 
125
- // EXPERIMENTAL: Use canister environment from ic_env cookie when enabled
126
- // ⚠️ This may cause issues with update calls on localhost development
127
- if (withCanisterEnv) {
128
- const canisterEnv =
129
- typeof window !== "undefined" ? safeGetCanisterEnv() : undefined
130
-
131
- if (canisterEnv) {
132
- this.internetIdentityId =
133
- canisterEnv["internet_identity"] ||
134
- canisterEnv["PUBLIC_CANISTER_ID:internet_identity"] ||
135
- canisterEnv["CANISTER_ID_INTERNET_IDENTITY"]
136
- }
137
-
138
- if (isDev() && typeof window !== "undefined") {
139
- agentOptions.host = agentOptions.host ?? window.location.origin
140
- if (agentOptions.verifyQuerySignatures == null) {
141
- agentOptions.verifyQuerySignatures = false
142
- console.warn(
143
- "[ic-reactor] Query signature verification is DISABLED in development. " +
144
- "Never use withCanisterEnv in production without explicitly setting verifyQuerySignatures: true."
145
- )
146
- }
147
- } else {
148
- agentOptions.verifyQuerySignatures =
149
- agentOptions.verifyQuerySignatures ?? true
79
+ // Locally deployed asset-canister pages and IC boundary domains can route
80
+ // agent traffic through their serving origin. Ordinary web hosts (Vercel,
81
+ // Cloudflare, etc.) cannot, so they retain the default IC API fallback.
82
+ if (typeof window !== "undefined") {
83
+ const browserOrigin = window.location.origin
84
+ const browserNetwork = getNetworkByHostname(
85
+ new URL(browserOrigin).hostname
86
+ )
87
+ if (browserNetwork === "local" || isMainnetHost(browserOrigin)) {
88
+ agentOptions.host = agentOptions.host ?? browserOrigin
150
89
  }
90
+ }
151
91
 
152
- // Root key must NOT be sourced from the ic_env cookie: cookies are
153
- // user-controllable and accepting a root key from them would allow an
154
- // attacker to bypass all IC certificate verification.
155
- // If a custom root key is required (e.g. local replica), pass it via
156
- // agentOptions.rootKey in your application code instead.
157
- if (canisterEnv?.IC_ROOT_KEY) {
158
- console.error(
159
- "[ic-reactor] IC_ROOT_KEY in the ic_env cookie is ignored for security reasons. " +
160
- "Pass agentOptions.rootKey explicitly if you need a custom root key."
161
- )
92
+ if (isDev() && typeof window !== "undefined") {
93
+ if (agentOptions.verifyQuerySignatures == null) {
94
+ agentOptions.verifyQuerySignatures = false
162
95
  }
96
+ } else {
97
+ agentOptions.verifyQuerySignatures =
98
+ agentOptions.verifyQuerySignatures ?? true
163
99
  }
164
100
 
165
- if (withProcessEnv) {
101
+ if (!agentOptions.host) {
166
102
  const processNetwork = getProcessEnvNetwork()
167
- if (processNetwork === "ic") {
103
+ if (processNetwork === "local") {
104
+ const envHost =
105
+ typeof process !== "undefined"
106
+ ? process.env.ICP_HOST || process.env.IC_HOST
107
+ : undefined
108
+ agentOptions.host = envHost ?? "http://127.0.0.1:4943"
109
+ } else {
168
110
  agentOptions.host = IC_HOST_NETWORK_URI
169
- } else if (processNetwork === "local") {
170
- agentOptions.host =
171
- typeof process !== "undefined" && process.env.IC_HOST
172
- ? process.env.IC_HOST
173
- : `http://127.0.0.1:${port}`
174
111
  }
175
- } else if (withLocalEnv) {
176
- agentOptions.host = `http://127.0.0.1:${port}`
177
- } else if (!withCanisterEnv) {
178
- // Only set default host if withCanisterEnv hasn't already configured it
179
- agentOptions.host = agentOptions.host ?? IC_HOST_NETWORK_URI
112
+ }
113
+
114
+ // For security reasons, the root key from the ic_env cookie is only accepted
115
+ // on non-mainnet environments (e.g. local replicas or custom testnets).
116
+ // On the mainnet ("ic"), we must use the pinned root key inside the agent.
117
+ if (!isMainnetHost(agentOptions.host) && canisterEnv?.IC_ROOT_KEY) {
118
+ agentOptions.rootKey = agentOptions.rootKey ?? canisterEnv.IC_ROOT_KEY
180
119
  }
181
120
 
182
121
  this.#agent = HttpAgent.createSync(agentOptions)
@@ -184,52 +123,20 @@ export class ClientManager {
184
123
  isLocalhost: this.isLocal,
185
124
  network: this.network,
186
125
  })
187
-
188
- if (authClient) {
189
- this.authClientWasProvided = true
190
- this.#authClient = authClient
191
- this.syncAuthStateFromClient(this.authStateRevision).catch((error) => {
192
- this.updateAuthState({ error: error as Error, isAuthenticating: false })
193
- })
194
- } else if (typeof window !== "undefined") {
195
- this.loadAuthClientConstructor().catch(() => {
196
- // Optional auth support is reported when an auth method is used.
197
- })
198
- }
199
126
  }
200
127
 
201
128
  /**
202
129
  * Orchestrates the complete initialization of the ClientManager.
203
130
  * This method awaits the agent's core initialization (e.g., fetching root keys)
204
- * and triggers the authentication (session restoration) in the background.
131
+ * Authentication session restoration is handled by AuthenticationManager.
205
132
  *
206
133
  * @returns A promise that resolves to the ClientManager instance when core initialization is complete.
207
134
  */
208
135
  public async initialize() {
209
136
  await this.initializeAgent()
210
- this.authenticate()
211
137
  return this
212
138
  }
213
139
 
214
- /**
215
- * Preloads and creates an AuthClient before a user gesture is needed.
216
- *
217
- * Browser signer transports must open their channel directly from a click
218
- * handler. Apps that pass dynamic auth options, such as OpenID provider
219
- * aliases, can call this from hover/focus/effect code so the later click path
220
- * can call signIn/requestAttributes without first awaiting a dynamic import.
221
- */
222
- public async prepareAuthClient(options?: ClientManagerAuthClientOptions) {
223
- if (
224
- this.#authClient &&
225
- (!this.shouldRecreateAuthClient(options) || this.authClientWasProvided)
226
- ) {
227
- return this.#authClient
228
- }
229
-
230
- return this.initializeAuthClient(options)
231
- }
232
-
233
140
  /**
234
141
  * Specifically initializes the HttpAgent.
235
142
  * On local networks, this includes fetching the root key for certificate verification.
@@ -275,267 +182,6 @@ export class ClientManager {
275
182
  return this.initPromise
276
183
  }
277
184
 
278
- private authModuleMissing = false
279
-
280
- /**
281
- * Attempts to initialize the authentication client and restore a previous session.
282
- *
283
- * If an `AuthClient` is already initialized (passed in constructor or previously created),
284
- * it uses that instance. Otherwise, it dynamically imports the `@icp-sdk/auth` module
285
- * and creates a new AuthClient.
286
- *
287
- * If the module is missing and no client is provided, it fails gracefully by marking authentication as unavailable.
288
- *
289
- * @returns A promise that resolves to the restored Identity, or undefined if auth fails or is unavailable.
290
- */
291
- public authenticate = async (): Promise<Identity | undefined> => {
292
- if (this.authState.isAuthenticated) {
293
- return this.authState.identity || undefined
294
- }
295
- if (this.authPromise) {
296
- return this.authPromise
297
- }
298
-
299
- if (this.authModuleMissing) {
300
- return undefined
301
- }
302
-
303
- this.authPromise = (async () => {
304
- if (isDev() && typeof window !== "undefined") {
305
- console.info(
306
- `%cic-reactor:%c Authenticating...`,
307
- "color: #3b82f6; font-weight: bold",
308
- "color: inherit",
309
- {
310
- network: this.network,
311
- authClient: this.#authClient ? "Shared Instance" : "Dynamic Import",
312
- }
313
- )
314
- }
315
- this.updateAuthState({ isAuthenticating: true })
316
- try {
317
- if (!this.#authClient) {
318
- const authClient = await this.initializeAuthClient()
319
- if (!authClient) {
320
- this.updateAuthState({ isAuthenticating: false })
321
- return undefined
322
- }
323
- }
324
- const identity = await this.#authClient!.getIdentity()
325
- const isAuthenticated = await this.#authClient!.isAuthenticated()
326
- this.updateAgent(identity)
327
- this.updateAuthState({
328
- identity,
329
- isAuthenticated,
330
- isAuthenticating: false,
331
- })
332
- return identity
333
- } catch (error) {
334
- this.updateAuthState({ error: error as Error, isAuthenticating: false })
335
- console.error("Authentication failed:", error)
336
- throw error
337
- } finally {
338
- this.authPromise = undefined
339
- }
340
- })()
341
-
342
- return this.authPromise
343
- }
344
-
345
- /**
346
- * Triggers the login flow using the Internet Identity provider.
347
- *
348
- * @param loginOptions - Options for the login flow, including identity provider and callbacks.
349
- * @throws An error if the authentication module is not installed.
350
- */
351
- public login = async (loginOptions?: ClientManagerSignInOptions) => {
352
- let didCompleteSignIn = false
353
-
354
- try {
355
- const identityProvider =
356
- loginOptions?.identityProvider || this.getDefaultIdentityProvider()
357
- const authClientOptions = getAuthClientOptions({
358
- ...loginOptions,
359
- identityProvider,
360
- })
361
-
362
- if (!this.ensurePreparedAuthClient(authClientOptions)) {
363
- await this.initializeAuthClient(authClientOptions)
364
- }
365
-
366
- if (!this.#authClient) {
367
- await this.authenticate()
368
- }
369
-
370
- if (!this.#authClient) {
371
- throw new Error(
372
- "Authentication module is missing or failed to initialize. To use login, please install the auth package: npm install @icp-sdk/auth"
373
- )
374
- }
375
-
376
- this.updateAuthState({ isAuthenticating: true, error: undefined })
377
-
378
- const identity = await this.signInOrRecoverAuthenticatedIdentity(
379
- getSignInOptions(loginOptions)
380
- )
381
-
382
- if (!this.agentState.isInitialized) {
383
- await this.initializeAgent()
384
- }
385
-
386
- this.updateAgent(identity)
387
- this.updateAuthState({
388
- identity,
389
- isAuthenticated: true,
390
- isAuthenticating: false,
391
- })
392
- didCompleteSignIn = true
393
-
394
- try {
395
- await (
396
- loginOptions?.onSuccess as (() => void | Promise<void>) | undefined
397
- )?.()
398
- } catch (callbackError) {
399
- this.updateAuthState({ error: callbackError as Error })
400
- await loginOptions?.onError?.((callbackError as Error).message)
401
- throw callbackError
402
- }
403
- } catch (error) {
404
- if (!didCompleteSignIn) {
405
- await loginOptions?.onError?.((error as Error).message)
406
- this.updateAuthState({
407
- error: error as Error,
408
- isAuthenticating: false,
409
- })
410
- }
411
- throw error
412
- }
413
- }
414
-
415
- /**
416
- * Logs out the user and reverts the agent to an anonymous identity.
417
- *
418
- * @throws An error if the authentication module is not installed.
419
- */
420
- public logout = async (options?: { returnTo?: string }) => {
421
- if (!this.#authClient) {
422
- throw new Error(
423
- "Authentication module is missing or failed to initialize. To use logout, please install the auth package: npm install @icp-sdk/auth"
424
- )
425
- }
426
- this.updateAuthState({ isAuthenticating: true, error: undefined })
427
- await this.#authClient.logout(options)
428
- const identity = await this.#authClient.getIdentity()
429
- this.updateAgent(identity)
430
- this.updateAuthState({
431
- identity,
432
- isAuthenticated: false,
433
- isAuthenticating: false,
434
- })
435
- }
436
-
437
- public requestIdentityAttributes = async ({
438
- keys,
439
- nonce,
440
- identityProvider = IDENTITY_ATTRIBUTES_BETA_PROVIDER,
441
- openIdProvider,
442
- windowOpenerFeatures,
443
- signIn = true,
444
- maxTimeToLive,
445
- targets,
446
- }: RequestIdentityAttributesParameters): Promise<IdentityAttributeResult> => {
447
- const authClientOptions = getAuthClientOptions({
448
- identityProvider,
449
- windowOpenerFeatures,
450
- openIdProvider,
451
- })
452
-
453
- if (!this.ensurePreparedAuthClient(authClientOptions)) {
454
- await this.initializeAuthClient(authClientOptions)
455
- }
456
-
457
- if (!this.#authClient) {
458
- await this.authenticate()
459
- }
460
-
461
- if (!this.#authClient) {
462
- throw new Error(
463
- "Authentication module is missing or failed to initialize. To request identity attributes, please install @icp-sdk/auth v6 or provide a compatible authClient."
464
- )
465
- }
466
-
467
- this.updateAuthState({ isAuthenticating: true, error: undefined })
468
-
469
- try {
470
- const identityPromise = signIn
471
- ? this.signInOrRecoverAuthenticatedIdentity({
472
- maxTimeToLive,
473
- targets,
474
- })
475
- : Promise.resolve(this.#authClient.getIdentity())
476
- const requestPromise = this.#authClient.requestAttributes({
477
- keys,
478
- nonce,
479
- })
480
-
481
- const [signedAttributes, identity] = await Promise.all([
482
- requestPromise,
483
- identityPromise,
484
- ])
485
-
486
- const finalIdentity = identity ?? (await this.#authClient.getIdentity())
487
- const isAuthenticated = await this.#authClient.isAuthenticated()
488
- if (!this.agentState.isInitialized) {
489
- await this.initializeAgent()
490
- }
491
- this.updateAgent(finalIdentity)
492
- this.updateAuthState({
493
- identity: finalIdentity,
494
- isAuthenticated,
495
- isAuthenticating: false,
496
- })
497
-
498
- const normalizedSignedAttributes =
499
- normalizeSignedIdentityAttributes(signedAttributes)
500
-
501
- return {
502
- principal: finalIdentity.getPrincipal().toText(),
503
- requestedKeys: keys,
504
- signedAttributes: normalizedSignedAttributes,
505
- decodedAttributes: decodeIdentityAttributeValues(
506
- normalizedSignedAttributes.data,
507
- keys
508
- ),
509
- completedAt: new Date().toISOString(),
510
- }
511
- } catch (error) {
512
- this.updateAuthState({ error: error as Error, isAuthenticating: false })
513
- throw error
514
- }
515
- }
516
-
517
- public requestOpenIdIdentityAttributes = async ({
518
- nonce,
519
- openIdProvider,
520
- keys,
521
- identityProvider = IDENTITY_ATTRIBUTES_BETA_PROVIDER,
522
- windowOpenerFeatures,
523
- signIn,
524
- maxTimeToLive,
525
- targets,
526
- }: RequestOpenIdIdentityAttributesParameters): Promise<IdentityAttributeResult> => {
527
- return this.requestIdentityAttributes({
528
- keys: identityAttributeKeys({ openIdProvider, keys }),
529
- nonce,
530
- identityProvider,
531
- openIdProvider,
532
- windowOpenerFeatures,
533
- signIn,
534
- maxTimeToLive,
535
- targets,
536
- })
537
- }
538
-
539
185
  /**
540
186
  * The underlying HttpAgent managed by this class.
541
187
  */
@@ -543,143 +189,6 @@ export class ClientManager {
543
189
  return this.#agent
544
190
  }
545
191
 
546
- /**
547
- * The AuthClient instance used for authentication, if available.
548
- */
549
- get authClient() {
550
- return this.#authClient
551
- }
552
-
553
- private async initializeAuthClient(
554
- options?: ClientManagerAuthClientOptions
555
- ): Promise<AuthClientLike | undefined> {
556
- const AuthClient = await this.loadAuthClientConstructor()
557
-
558
- if (!AuthClient) {
559
- return undefined
560
- }
561
-
562
- this.#authClient = this.createAuthClient(AuthClient, options)
563
- return this.#authClient
564
- }
565
-
566
- private async signInOrRecoverAuthenticatedIdentity(
567
- options?: AuthClientSignInOptions
568
- ): Promise<Identity> {
569
- if (!this.#authClient) {
570
- throw new Error(
571
- "Authentication module is missing or failed to initialize. To use login, please install the auth package: npm install @icp-sdk/auth"
572
- )
573
- }
574
-
575
- try {
576
- return await this.#authClient.signIn(options)
577
- } catch (error) {
578
- const identity = await Promise.resolve(
579
- this.#authClient.getIdentity()
580
- ).catch(() => null)
581
- const isAuthenticated = await Promise.resolve(
582
- this.#authClient.isAuthenticated()
583
- ).catch(() => false)
584
-
585
- if (identity && isAuthenticated) {
586
- return identity
587
- }
588
-
589
- throw error
590
- }
591
- }
592
-
593
- private async loadAuthClientConstructor(): Promise<
594
- AuthClientConstructor | undefined
595
- > {
596
- if (this.authClientConstructor) {
597
- return this.authClientConstructor
598
- }
599
-
600
- if (!this.authClientConstructorPromise) {
601
- this.authClientConstructorPromise = import("@icp-sdk/auth/client")
602
- .then((authModule) => {
603
- const AuthClient = (
604
- authModule as { AuthClient?: AuthClientConstructor }
605
- ).AuthClient
606
-
607
- if (!AuthClient) {
608
- throw new Error("@icp-sdk/auth/client did not export AuthClient")
609
- }
610
-
611
- this.authClientConstructor = AuthClient
612
- return AuthClient
613
- })
614
- .catch((error) => {
615
- this.authModuleMissing = true
616
- this.authClientConstructorPromise = undefined
617
- if (
618
- error instanceof Error &&
619
- error.message.includes("did not export AuthClient")
620
- ) {
621
- throw error
622
- }
623
- return undefined
624
- })
625
- }
626
-
627
- return this.authClientConstructorPromise
628
- }
629
-
630
- private createAuthClient(
631
- AuthClient: AuthClientConstructor,
632
- options?: ClientManagerAuthClientOptions
633
- ): AuthClientLike {
634
- return new AuthClient(options)
635
- }
636
-
637
- private ensurePreparedAuthClient(
638
- options?: ClientManagerAuthClientOptions
639
- ): AuthClientLike | undefined {
640
- if (
641
- this.#authClient &&
642
- (!this.shouldRecreateAuthClient(options) || this.authClientWasProvided)
643
- ) {
644
- return this.#authClient
645
- }
646
-
647
- if (!this.authClientConstructor || this.authClientWasProvided) {
648
- return undefined
649
- }
650
-
651
- this.#authClient = this.createAuthClient(
652
- this.authClientConstructor,
653
- options
654
- )
655
- return this.#authClient
656
- }
657
-
658
- private shouldRecreateAuthClient(
659
- options?: ClientManagerAuthClientOptions
660
- ): boolean {
661
- return !this.authClientWasProvided && hasAuthClientOptions(options)
662
- }
663
-
664
- private async syncAuthStateFromClient(revision = this.authStateRevision) {
665
- if (!this.#authClient) {
666
- return
667
- }
668
-
669
- const identity = await this.#authClient.getIdentity()
670
- const isAuthenticated = await this.#authClient.isAuthenticated()
671
- if (revision !== this.authStateRevision) {
672
- return
673
- }
674
- this.updateAgent(identity)
675
- this.updateAuthState({
676
- identity,
677
- isAuthenticated,
678
- isAuthenticating: false,
679
- error: undefined,
680
- })
681
- }
682
-
683
192
  /**
684
193
  * The host URL of the current IC agent.
685
194
  */
@@ -758,17 +267,6 @@ export class ClientManager {
758
267
  return this.#agent.syncTimeWithSubnet(subnetId)
759
268
  }
760
269
 
761
- private getDefaultIdentityProvider(): string {
762
- if (this.isLocal) {
763
- if (this.internetIdentityId) {
764
- return `http://${this.internetIdentityId}.localhost:${this.port}`
765
- }
766
- return LOCAL_INTERNET_IDENTITY_PROVIDER
767
- } else {
768
- return IC_INTERNET_IDENTITY_PROVIDER
769
- }
770
- }
771
-
772
270
  /**
773
271
  * Subscribes to identity changes (e.g., after login/logout).
774
272
  * @param callback - Function called with the new identity.
@@ -797,20 +295,6 @@ export class ClientManager {
797
295
  }
798
296
  }
799
297
 
800
- /**
801
- * Subscribes to changes in the authentication state.
802
- * @param callback - Function called with the updated authentication state.
803
- * @returns An unsubscribe function.
804
- */
805
- public subscribeAuthState(callback: (state: AuthState) => void) {
806
- this.#authStateSubscribers.push(callback)
807
- return () => {
808
- this.#authStateSubscribers = this.#authStateSubscribers.filter(
809
- (sub) => sub !== callback
810
- )
811
- }
812
- }
813
-
814
298
  /**
815
299
  * Replaces the current agent's identity and invalidates TanStack queries.
816
300
  * @param identity - The new identity to use.
@@ -847,66 +331,8 @@ export class ClientManager {
847
331
  this.#agentStateSubscribers.forEach((sub) => sub(state))
848
332
  }
849
333
 
850
- private notifyAuthStateSubscribers(state: AuthState) {
851
- this.#authStateSubscribers.forEach((sub) => sub(state))
852
- }
853
-
854
334
  private updateAgentState(newState: Partial<AgentState>) {
855
335
  this.agentState = { ...this.agentState, ...newState }
856
336
  this.notifyAgentStateSubscribers(this.agentState)
857
337
  }
858
-
859
- private updateAuthState(newState: Partial<AuthState>) {
860
- if (isDev()) console.debug("[ic-reactor] Updating Auth State:", newState)
861
- this.authStateRevision += 1
862
- this.authState = { ...this.authState, ...newState }
863
- this.notifyAuthStateSubscribers(this.authState)
864
- }
865
- }
866
-
867
- function getAuthClientOptions(
868
- options?: ClientManagerAuthClientOptions
869
- ): ClientManagerAuthClientOptions | undefined {
870
- if (!options) {
871
- return undefined
872
- }
873
-
874
- return {
875
- identityProvider: options.identityProvider,
876
- windowOpenerFeatures: options.windowOpenerFeatures,
877
- openIdProvider: getAuthClientOpenIdProvider(options.openIdProvider),
878
- }
879
- }
880
-
881
- function getAuthClientOpenIdProvider(
882
- openIdProvider?: ClientManagerAuthClientOptions["openIdProvider"]
883
- ): ClientManagerAuthClientOptions["openIdProvider"] | undefined {
884
- return openIdProvider === "google" ||
885
- openIdProvider === "apple" ||
886
- openIdProvider === "microsoft"
887
- ? openIdProvider
888
- : undefined
889
- }
890
-
891
- function hasAuthClientOptions(
892
- options?: ClientManagerAuthClientOptions
893
- ): boolean {
894
- return Boolean(
895
- options?.identityProvider ||
896
- options?.windowOpenerFeatures ||
897
- options?.openIdProvider
898
- )
899
- }
900
-
901
- function getSignInOptions(
902
- options?: ClientManagerSignInOptions
903
- ): AuthClientSignInOptions | undefined {
904
- if (!options) {
905
- return undefined
906
- }
907
-
908
- return {
909
- maxTimeToLive: options.maxTimeToLive,
910
- targets: options.targets,
911
- }
912
338
  }