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