@ic-reactor/react 3.7.0 → 3.8.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 (86) hide show
  1. package/README.md +93 -9
  2. package/dist/auth/authentication-manager.d.ts +37 -5
  3. package/dist/auth/authentication-manager.d.ts.map +1 -1
  4. package/dist/auth/authentication-manager.js +111 -37
  5. package/dist/auth/authentication-manager.js.map +1 -1
  6. package/dist/auth/createIdentityAttributeHooks.d.ts +2 -2
  7. package/dist/auth/createIdentityAttributeHooks.d.ts.map +1 -1
  8. package/dist/auth/identity-attributes-manager.d.ts +12 -5
  9. package/dist/auth/identity-attributes-manager.d.ts.map +1 -1
  10. package/dist/auth/identity-attributes-manager.js +42 -28
  11. package/dist/auth/identity-attributes-manager.js.map +1 -1
  12. package/dist/auth/identity-attributes.d.ts +1 -1
  13. package/dist/auth/identity-attributes.d.ts.map +1 -1
  14. package/dist/auth/identity-attributes.js +1 -1
  15. package/dist/auth/identity-attributes.js.map +1 -1
  16. package/dist/auth/index.d.ts +7 -7
  17. package/dist/auth/index.d.ts.map +1 -1
  18. package/dist/auth/index.js +7 -7
  19. package/dist/auth/index.js.map +1 -1
  20. package/dist/auth/types.d.ts +123 -16
  21. package/dist/auth/types.d.ts.map +1 -1
  22. package/dist/createActorHooks.d.ts +4 -4
  23. package/dist/createActorHooks.d.ts.map +1 -1
  24. package/dist/createActorHooks.js +6 -6
  25. package/dist/createActorHooks.js.map +1 -1
  26. package/dist/createInfiniteQuery.d.ts +1 -1
  27. package/dist/createInfiniteQuery.d.ts.map +1 -1
  28. package/dist/createInfiniteQuery.js +1 -1
  29. package/dist/createInfiniteQuery.js.map +1 -1
  30. package/dist/createMutation.d.ts +1 -1
  31. package/dist/createMutation.d.ts.map +1 -1
  32. package/dist/createQuery.d.ts +1 -1
  33. package/dist/createQuery.d.ts.map +1 -1
  34. package/dist/createQuery.js +1 -1
  35. package/dist/createQuery.js.map +1 -1
  36. package/dist/createSuspenseInfiniteQuery.d.ts +1 -1
  37. package/dist/createSuspenseInfiniteQuery.d.ts.map +1 -1
  38. package/dist/createSuspenseInfiniteQuery.js +1 -1
  39. package/dist/createSuspenseInfiniteQuery.js.map +1 -1
  40. package/dist/createSuspenseQuery.d.ts +1 -1
  41. package/dist/createSuspenseQuery.d.ts.map +1 -1
  42. package/dist/createSuspenseQuery.js +1 -1
  43. package/dist/createSuspenseQuery.js.map +1 -1
  44. package/dist/defineReactor.d.ts +54 -4
  45. package/dist/defineReactor.d.ts.map +1 -1
  46. package/dist/defineReactor.js +77 -7
  47. package/dist/defineReactor.js.map +1 -1
  48. package/dist/hooks/createAuthHooks.d.ts +2 -2
  49. package/dist/hooks/createAuthHooks.d.ts.map +1 -1
  50. package/dist/hooks/createAuthHooks.js +7 -2
  51. package/dist/hooks/createAuthHooks.js.map +1 -1
  52. package/dist/hooks/useActorInfiniteQuery.js +1 -1
  53. package/dist/hooks/useActorInfiniteQuery.js.map +1 -1
  54. package/dist/hooks/useActorSuspenseInfiniteQuery.js +1 -1
  55. package/dist/hooks/useActorSuspenseInfiniteQuery.js.map +1 -1
  56. package/dist/index.d.ts +11 -11
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +11 -11
  59. package/dist/index.js.map +1 -1
  60. package/llms.txt +1 -1
  61. package/package.json +14 -9
  62. package/src/auth/authentication-manager.ts +121 -49
  63. package/src/auth/createIdentityAttributeHooks.ts +2 -2
  64. package/src/auth/identity-attributes-manager.ts +53 -44
  65. package/src/auth/identity-attributes.ts +2 -2
  66. package/src/auth/index.ts +7 -7
  67. package/src/auth/types.ts +134 -18
  68. package/src/createActorHooks.ts +9 -9
  69. package/src/createInfiniteQuery.ts +2 -2
  70. package/src/createMutation.ts +1 -1
  71. package/src/createQuery.ts +2 -2
  72. package/src/createSuspenseInfiniteQuery.ts +2 -2
  73. package/src/createSuspenseQuery.ts +2 -2
  74. package/src/defineReactor.ts +136 -14
  75. package/src/hooks/createAuthHooks.ts +8 -3
  76. package/src/hooks/useActorInfiniteQuery.ts +1 -1
  77. package/src/hooks/useActorSuspenseInfiniteQuery.ts +1 -1
  78. package/src/index.ts +11 -11
  79. package/src/defineReactor.test.ts +0 -70
  80. package/src/hooks/useActorInfiniteQuery.test.tsx +0 -457
  81. package/src/hooks/useActorMethod.test.tsx +0 -798
  82. package/src/hooks/useActorMutation.test.tsx +0 -220
  83. package/src/hooks/useActorQuery.test.tsx +0 -287
  84. package/src/hooks/useActorSuspenseInfiniteQuery.test.tsx +0 -472
  85. package/src/hooks/useActorSuspenseQuery.test.tsx +0 -254
  86. package/src/utils.test.ts +0 -14
@@ -5,7 +5,7 @@ import type {
5
5
  AuthState,
6
6
  AuthenticationClientOptions,
7
7
  AuthenticationSignInOptions,
8
- } from "./types"
8
+ } from "./types.js"
9
9
  import { ClientManager, isDev } from "@ic-reactor/core"
10
10
 
11
11
  import { safeGetCanisterEnv } from "@icp-sdk/core/agent/canister-env"
@@ -13,12 +13,16 @@ import {
13
13
  IC_INTERNET_IDENTITY_PROVIDER,
14
14
  INTERNET_IDENTITY_PROVIDER_ENV_KEY,
15
15
  localInternetIdentityProvider,
16
- } from "./constants"
16
+ } from "./constants.js"
17
17
 
18
- export interface AuthenticationManagerParameters {
18
+ export interface AuthenticationManagerParameters extends AuthenticationClientOptions {
19
19
  clientManager: ClientManager
20
+ /**
21
+ * Bring your own client instance. When provided, IC Reactor never constructs
22
+ * one and never applies the options below to it.
23
+ */
20
24
  authClient?: AuthClientLike
21
- identityProvider?: string | URL
25
+ /** Canister ID of a locally deployed Internet Identity. */
22
26
  internetIdentityId?: string
23
27
  }
24
28
 
@@ -26,8 +30,6 @@ type AuthClientConstructor = {
26
30
  new (options?: AuthenticationClientOptions): AuthClientLike
27
31
  }
28
32
 
29
- const AUTH_CLIENT_MODULE = "@icp-sdk/auth/client"
30
-
31
33
  /**
32
34
  * Manages Internet Identity sign-in, session restoration, and authentication
33
35
  * state for a {@link ClientManager}.
@@ -49,6 +51,7 @@ export class AuthenticationManager {
49
51
  AuthClientConstructor | undefined
50
52
  >
51
53
  private authModuleMissing = false
54
+ private authClientOptions?: AuthenticationClientOptions
52
55
  private authStateValue: AuthState = {
53
56
  identity: null,
54
57
  isAuthenticating: false,
@@ -57,6 +60,7 @@ export class AuthenticationManager {
57
60
  }
58
61
  private readonly identityProvider?: string | URL
59
62
  private readonly internetIdentityId?: string
63
+ private readonly defaultClientOptions: AuthenticationClientOptions
60
64
  public readonly clientManager: ClientManager
61
65
 
62
66
  /** The current authentication state. */
@@ -69,6 +73,7 @@ export class AuthenticationManager {
69
73
  authClient,
70
74
  identityProvider,
71
75
  internetIdentityId,
76
+ ...clientOptions
72
77
  }: AuthenticationManagerParameters) {
73
78
  this.clientManager = clientManager
74
79
  const canisterEnv =
@@ -82,6 +87,7 @@ export class AuthenticationManager {
82
87
  canisterEnv?.["internet_identity"] ||
83
88
  canisterEnv?.["PUBLIC_CANISTER_ID:internet_identity"] ||
84
89
  canisterEnv?.["CANISTER_ID_INTERNET_IDENTITY"]
90
+ this.defaultClientOptions = clientOptions
85
91
 
86
92
  if (authClient) {
87
93
  this.authClientWasProvided = true
@@ -111,25 +117,37 @@ export class AuthenticationManager {
111
117
  }
112
118
 
113
119
  /**
114
- * Preloads and creates an AuthClient before a user gesture is needed.
120
+ * Preloads the auth module and creates an AuthClient ahead of time.
121
+ *
122
+ * Call (and await) this before wiring up a login button: it makes
123
+ * {@link login} and {@link IdentityAttributesManager.request} able to open
124
+ * the identity provider synchronously inside the click handler, which is
125
+ * what browser popup blockers and the ICRC-29 transport require.
115
126
  */
116
127
  public async prepareClient(options?: AuthenticationClientOptions) {
117
- const clientOptions = getAuthClientOptions({
118
- ...options,
119
- identityProvider:
120
- options?.identityProvider ?? this.getDefaultIdentityProvider(),
121
- })
128
+ const clientOptions = this.resolveClientOptions(options)
122
129
 
123
- if (
124
- this.authClient &&
125
- (!this.shouldRecreateClient(clientOptions) || this.authClientWasProvided)
126
- ) {
130
+ if (this.authClient && !this.shouldRecreateClient(clientOptions)) {
127
131
  return this.authClient
128
132
  }
129
133
 
130
134
  return this.initializeClient(clientOptions)
131
135
  }
132
136
 
137
+ /**
138
+ * Returns an AuthClient without awaiting, or `undefined` when the auth
139
+ * module has not been loaded yet.
140
+ *
141
+ * Callers that must stay inside a user gesture use this instead of
142
+ * {@link ensureClient}; awaiting anything before opening the identity
143
+ * provider window loses the gesture.
144
+ */
145
+ public getPreparedClient(
146
+ options?: AuthenticationClientOptions
147
+ ): AuthClientLike | undefined {
148
+ return this.ensurePreparedClient(this.resolveClientOptions(options))
149
+ }
150
+
133
151
  public authenticate = async (): Promise<Identity | undefined> => {
134
152
  if (this.authState.isAuthenticated) {
135
153
  return this.authState.identity || undefined
@@ -157,9 +175,7 @@ export class AuthenticationManager {
157
175
  try {
158
176
  if (!this.authClient) {
159
177
  const authClient = await this.initializeClient(
160
- getAuthClientOptions({
161
- identityProvider: this.getDefaultIdentityProvider(),
162
- })
178
+ this.resolveClientOptions()
163
179
  )
164
180
  if (!authClient) {
165
181
  this.updateState({ isAuthenticating: false })
@@ -168,7 +184,12 @@ export class AuthenticationManager {
168
184
  }
169
185
  const identity = await this.authClient!.getIdentity()
170
186
  const isAuthenticated = await this.authClient!.isAuthenticated()
171
- this.clientManager.updateAgent(identity)
187
+ // Restoring an anonymous session is the common first-load case; pushing
188
+ // it through updateAgent would invalidate the whole query cache on
189
+ // every mount for nothing.
190
+ if (isAuthenticated || !identity.getPrincipal().isAnonymous()) {
191
+ this.clientManager.updateAgent(identity)
192
+ }
172
193
  this.updateState({
173
194
  identity,
174
195
  isAuthenticated,
@@ -191,13 +212,10 @@ export class AuthenticationManager {
191
212
  let didCompleteSignIn = false
192
213
 
193
214
  try {
194
- const identityProvider =
195
- loginOptions?.identityProvider || this.getDefaultIdentityProvider()
196
- const authClientOptions = getAuthClientOptions({
197
- ...loginOptions,
198
- identityProvider,
199
- })
215
+ const authClientOptions = this.resolveClientOptions(loginOptions)
200
216
 
217
+ // Stays synchronous when `prepareClient()` has already loaded the auth
218
+ // module, so `signIn()` still runs inside the caller's click handler.
201
219
  if (!this.ensurePreparedClient(authClientOptions)) {
202
220
  await this.initializeClient(authClientOptions)
203
221
  }
@@ -208,7 +226,7 @@ export class AuthenticationManager {
208
226
 
209
227
  if (!this.authClient) {
210
228
  throw new Error(
211
- "Authentication module is missing or failed to initialize. To use login, please install the auth package: npm install @icp-sdk/auth"
229
+ "Authentication module is missing or failed to initialize. To use login, install the optional auth peer: npm install @icp-sdk/auth. If it is already installed and your bundler could not resolve it, pass a pre-constructed client instead: new AuthenticationManager({ clientManager, authClient: new AuthClient(...) })"
212
230
  )
213
231
  }
214
232
 
@@ -251,7 +269,7 @@ export class AuthenticationManager {
251
269
  public logout = async (options?: { returnTo?: string }) => {
252
270
  if (!this.authClient) {
253
271
  throw new Error(
254
- "Authentication module is missing or failed to initialize. To use logout, please install the auth package: npm install @icp-sdk/auth"
272
+ "Authentication module is missing or failed to initialize. To use logout, install the optional auth peer: npm install @icp-sdk/auth. If it is already installed and your bundler could not resolve it, pass a pre-constructed client instead: new AuthenticationManager({ clientManager, authClient: new AuthClient(...) })"
255
273
  )
256
274
  }
257
275
  this.updateState({ isAuthenticating: true, error: undefined })
@@ -275,6 +293,7 @@ export class AuthenticationManager {
275
293
  }
276
294
 
277
295
  this.authClient = new AuthClient(options)
296
+ this.authClientOptions = options
278
297
  return this.authClient
279
298
  }
280
299
 
@@ -284,7 +303,7 @@ export class AuthenticationManager {
284
303
  ): Promise<Identity> {
285
304
  if (!this.authClient) {
286
305
  throw new Error(
287
- "Authentication module is missing or failed to initialize. To use login, please install the auth package: npm install @icp-sdk/auth"
306
+ "Authentication module is missing or failed to initialize. To use login, install the optional auth peer: npm install @icp-sdk/auth. If it is already installed and your bundler could not resolve it, pass a pre-constructed client instead: new AuthenticationManager({ clientManager, authClient: new AuthClient(...) })"
288
307
  )
289
308
  }
290
309
 
@@ -309,10 +328,7 @@ export class AuthenticationManager {
309
328
  private ensurePreparedClient(
310
329
  options?: AuthenticationClientOptions
311
330
  ): AuthClientLike | undefined {
312
- if (
313
- this.authClient &&
314
- (!this.shouldRecreateClient(options) || this.authClientWasProvided)
315
- ) {
331
+ if (this.authClient && !this.shouldRecreateClient(options)) {
316
332
  return this.authClient
317
333
  }
318
334
 
@@ -322,11 +338,41 @@ export class AuthenticationManager {
322
338
  }
323
339
 
324
340
  this.authClient = new AuthClient(options)
341
+ this.authClientOptions = options
325
342
  return this.authClient
326
343
  }
327
344
 
345
+ /**
346
+ * Only rebuild the client when the effective options actually changed.
347
+ *
348
+ * Recreating on every call would discard the client warmed up by
349
+ * `prepareClient()` and register a duplicate sign-out callback on the
350
+ * shared IdleManager singleton each time.
351
+ */
328
352
  private shouldRecreateClient(options?: AuthenticationClientOptions): boolean {
329
- return !this.authClientWasProvided && hasAuthClientOptions(options)
353
+ if (this.authClientWasProvided) {
354
+ return false
355
+ }
356
+ return !isSameAuthClientOptions(this.authClientOptions, options)
357
+ }
358
+
359
+ /**
360
+ * Merges constructor-level defaults with per-call overrides and fills in the
361
+ * network-appropriate identity provider.
362
+ */
363
+ private resolveClientOptions(
364
+ options?: AuthenticationClientOptions
365
+ ): AuthenticationClientOptions {
366
+ const merged = getAuthClientOptions({
367
+ ...this.defaultClientOptions,
368
+ ...options,
369
+ })
370
+
371
+ return {
372
+ ...merged,
373
+ identityProvider:
374
+ merged?.identityProvider ?? this.getDefaultIdentityProvider(),
375
+ }
330
376
  }
331
377
 
332
378
  private async syncStateFromClient(revision = this.authStateRevision) {
@@ -350,11 +396,7 @@ export class AuthenticationManager {
350
396
 
351
397
  /** @internal Used by IdentityAttributesManager. */
352
398
  public async ensureClient(options?: AuthenticationClientOptions) {
353
- const clientOptions = getAuthClientOptions({
354
- ...options,
355
- identityProvider:
356
- options?.identityProvider ?? this.getDefaultIdentityProvider(),
357
- })
399
+ const clientOptions = this.resolveClientOptions(options)
358
400
  if (!this.ensurePreparedClient(clientOptions)) {
359
401
  await this.initializeClient(clientOptions)
360
402
  }
@@ -407,11 +449,12 @@ export class AuthenticationManager {
407
449
  }
408
450
 
409
451
  if (!this.authClientConstructorPromise) {
410
- // Keep the optional auth peer out of non-auth bundles. Vite/Rollup/esbuild
411
- // can otherwise try to resolve this package even when auth APIs are unused.
412
- this.authClientConstructorPromise = import(
413
- /* @vite-ignore */ AUTH_CLIENT_MODULE
414
- )
452
+ // The specifier must stay a literal: a variable specifier (or /* @vite-ignore */)
453
+ // makes bundlers skip this import entirely, so the bare specifier survives into
454
+ // the browser, where it cannot be resolved without an import map and every login
455
+ // path fails. A literal lets Vite/Rollup/webpack code-split the optional peer,
456
+ // and lets them tree-shake it away for apps that never reference this class.
457
+ this.authClientConstructorPromise = import("@icp-sdk/auth/client")
415
458
  .then((authModule) => {
416
459
  const AuthClient = (
417
460
  authModule as { AuthClient?: AuthClientConstructor }
@@ -452,6 +495,12 @@ function getAuthClientOptions(
452
495
  identityProvider: options.identityProvider,
453
496
  windowOpenerFeatures: options.windowOpenerFeatures,
454
497
  openIdProvider: getAuthClientOpenIdProvider(options.openIdProvider),
498
+ derivationOrigin: options.derivationOrigin,
499
+ storage: options.storage,
500
+ keyType: options.keyType,
501
+ idleOptions: options.idleOptions,
502
+ identity: options.identity,
503
+ transport: options.transport,
455
504
  }
456
505
  }
457
506
 
@@ -465,11 +514,34 @@ function getAuthClientOpenIdProvider(
465
514
  : undefined
466
515
  }
467
516
 
468
- function hasAuthClientOptions(options?: AuthenticationClientOptions): boolean {
469
- return Boolean(
470
- options?.identityProvider ||
471
- options?.windowOpenerFeatures ||
472
- options?.openIdProvider
517
+ /**
518
+ * Compares the options two AuthClients would be built from. Object-valued
519
+ * options (`storage`, `identity`, `idleOptions`) are compared by reference,
520
+ * which is what module-scoped configuration produces.
521
+ */
522
+ function isSameAuthClientOptions(
523
+ current?: AuthenticationClientOptions,
524
+ next?: AuthenticationClientOptions
525
+ ): boolean {
526
+ if (current === next) {
527
+ return true
528
+ }
529
+ if (!current || !next) {
530
+ return false
531
+ }
532
+
533
+ return (
534
+ String(current.identityProvider ?? "") ===
535
+ String(next.identityProvider ?? "") &&
536
+ current.windowOpenerFeatures === next.windowOpenerFeatures &&
537
+ current.openIdProvider === next.openIdProvider &&
538
+ String(current.derivationOrigin ?? "") ===
539
+ String(next.derivationOrigin ?? "") &&
540
+ current.storage === next.storage &&
541
+ current.keyType === next.keyType &&
542
+ current.idleOptions === next.idleOptions &&
543
+ current.identity === next.identity &&
544
+ current.transport === next.transport
473
545
  )
474
546
  }
475
547
 
@@ -1,10 +1,10 @@
1
1
  import { useCallback, useState } from "react"
2
- import type { IdentityAttributesManager } from "./identity-attributes-manager"
2
+ import type { IdentityAttributesManager } from "./identity-attributes-manager.js"
3
3
  import type {
4
4
  IdentityAttributeResult,
5
5
  RequestIdentityAttributesParameters,
6
6
  RequestOpenIdIdentityAttributesParameters,
7
- } from "./types"
7
+ } from "./types.js"
8
8
 
9
9
  export interface UseIdentityAttributesReturn {
10
10
  requestAttributes: (
@@ -1,25 +1,33 @@
1
1
  import type {
2
- AuthenticationClientOptions,
2
+ AuthClientLike,
3
+ IdentityAttributeNonce,
3
4
  IdentityAttributeResult,
4
5
  RequestIdentityAttributesParameters,
5
6
  RequestOpenIdIdentityAttributesParameters,
6
- } from "./types"
7
+ } from "./types.js"
7
8
 
8
- import { AuthenticationManager } from "./authentication-manager"
9
+ import { AuthenticationManager } from "./authentication-manager.js"
9
10
  import {
10
11
  decodeIdentityAttributeValues,
11
12
  identityAttributeKeys,
12
13
  normalizeSignedIdentityAttributes,
13
- } from "./identity-attributes"
14
+ } from "./identity-attributes.js"
14
15
 
15
16
  /**
16
17
  * Requests and decodes signed identity attributes for an authenticated
17
18
  * Internet Identity session.
18
19
  *
20
+ * Await `authentication.prepareClient()` once during startup (the `useAuth`
21
+ * hook already does) so that {@link request} can open the identity provider
22
+ * window synchronously from a click handler.
23
+ *
19
24
  * @example
20
25
  * ```ts
21
26
  * const attributes = new IdentityAttributesManager(authentication)
22
- * const result = await attributes.request({ keys: ["name"] })
27
+ * const result = await attributes.request({
28
+ * keys: ["name"],
29
+ * nonce: () => fetchNonceFromCanister(),
30
+ * })
23
31
  * ```
24
32
  */
25
33
  export class IdentityAttributesManager {
@@ -28,37 +36,40 @@ export class IdentityAttributesManager {
28
36
  public request = async ({
29
37
  keys,
30
38
  nonce,
31
- identityProvider,
32
- openIdProvider,
33
- windowOpenerFeatures,
34
39
  signIn = true,
35
40
  maxTimeToLive,
36
41
  targets,
42
+ ...clientOptions
37
43
  }: RequestIdentityAttributesParameters): Promise<IdentityAttributeResult> => {
38
- const authClient = await this.authentication.ensureClient(
39
- getAuthClientOptions({
40
- identityProvider,
41
- windowOpenerFeatures,
42
- openIdProvider,
43
- })
44
- )
44
+ // Nothing may be awaited before `requestAttributes` / `signIn` are invoked:
45
+ // the ICRC-29 transport only opens the identity provider window while the
46
+ // browser still considers us inside the user gesture. `getPreparedClient`
47
+ // is synchronous whenever `prepareClient()` has run.
48
+ const preparedClient = this.authentication.getPreparedClient(clientOptions)
49
+ const authClient =
50
+ preparedClient ?? (await this.authentication.ensureClient(clientOptions))
45
51
 
46
52
  if (!authClient) {
47
53
  throw new Error(
48
- "Authentication module is missing or failed to initialize. To request identity attributes, please install @icp-sdk/auth v7 or provide a compatible authClient."
54
+ "Authentication module is missing or failed to initialize. To request identity attributes, please install @icp-sdk/auth v7 or later, or provide a compatible authClient."
49
55
  )
50
56
  }
51
57
 
52
58
  this.authentication.setAuthenticating()
53
59
 
54
60
  try {
61
+ // `signIn` first: it opens the transport channel synchronously, and the
62
+ // attribute request then reuses that same channel and window.
55
63
  const identityPromise = signIn
56
64
  ? this.authentication.signInOrRecoverIdentity({
57
65
  maxTimeToLive,
58
66
  targets,
59
67
  })
60
68
  : Promise.resolve(authClient.getIdentity())
61
- const requestPromise = authClient.requestAttributes({ keys, nonce })
69
+ const requestPromise = authClient.requestAttributes({
70
+ keys,
71
+ nonce: toAuthClientNonce(authClient, nonce),
72
+ })
62
73
 
63
74
  const [signedAttributes, identity] = await Promise.all([
64
75
  requestPromise,
@@ -88,40 +99,38 @@ export class IdentityAttributesManager {
88
99
  }
89
100
  }
90
101
 
91
- public requestOpenId = async ({
92
- nonce,
93
- openIdProvider,
102
+ public requestOpenId = ({
94
103
  keys,
95
- identityProvider,
96
- windowOpenerFeatures,
97
- signIn,
98
- maxTimeToLive,
99
- targets,
104
+ openIdProvider,
105
+ ...rest
100
106
  }: RequestOpenIdIdentityAttributesParameters): Promise<IdentityAttributeResult> => {
107
+ // Called (not awaited) synchronously so the user gesture is preserved.
101
108
  return this.request({
102
- keys: identityAttributeKeys({ openIdProvider, keys }),
103
- nonce,
104
- identityProvider,
109
+ ...rest,
105
110
  openIdProvider,
106
- windowOpenerFeatures,
107
- signIn,
108
- maxTimeToLive,
109
- targets,
111
+ keys: identityAttributeKeys({ openIdProvider, keys }),
110
112
  })
111
113
  }
112
114
  }
113
115
 
114
- function getAuthClientOptions(
115
- options: AuthenticationClientOptions
116
- ): AuthenticationClientOptions {
117
- return {
118
- identityProvider: options.identityProvider,
119
- windowOpenerFeatures: options.windowOpenerFeatures,
120
- openIdProvider:
121
- options.openIdProvider === "google" ||
122
- options.openIdProvider === "apple" ||
123
- options.openIdProvider === "microsoft"
124
- ? options.openIdProvider
125
- : undefined,
116
+ /**
117
+ * Bridges the two `@icp-sdk/auth` nonce contracts.
118
+ *
119
+ * v8 takes `() => Promise<Uint8Array>` (it journals the value so a redirect
120
+ * flow replays the same bytes); v7 takes `Uint8Array | Promise<Uint8Array>`.
121
+ * Both open the transport channel synchronously for the deferred forms, so we
122
+ * always hand over the deferred shape the installed client understands.
123
+ * `memoize` only exists from v8 onwards, which makes it a reliable marker.
124
+ */
125
+ function toAuthClientNonce(
126
+ authClient: AuthClientLike,
127
+ nonce: IdentityAttributeNonce
128
+ ): Uint8Array | Promise<Uint8Array> | (() => Promise<Uint8Array>) {
129
+ const supportsNonceThunk = typeof authClient.memoize === "function"
130
+
131
+ if (supportsNonceThunk) {
132
+ return () => Promise.resolve(typeof nonce === "function" ? nonce() : nonce)
126
133
  }
134
+
135
+ return Promise.resolve(typeof nonce === "function" ? nonce() : nonce)
127
136
  }
@@ -4,8 +4,8 @@ import type {
4
4
  IdentityAttributeOpenIdProvider,
5
5
  IdentityAttributeValues,
6
6
  SignedIdentityAttributes,
7
- } from "./types"
8
- import { IC_INTERNET_IDENTITY_PROVIDER } from "./constants"
7
+ } from "./types.js"
8
+ import { IC_INTERNET_IDENTITY_PROVIDER } from "./constants.js"
9
9
 
10
10
  export const IDENTITY_ATTRIBUTES_PROVIDER = IC_INTERNET_IDENTITY_PROVIDER
11
11
 
package/src/auth/index.ts CHANGED
@@ -1,7 +1,7 @@
1
- export * from "./authentication-manager"
2
- export * from "./identity-attributes-manager"
3
- export * from "./identity-attributes"
4
- export * from "./constants"
5
- export * from "./types"
6
- export * from "../hooks/createAuthHooks"
7
- export * from "./createIdentityAttributeHooks"
1
+ export * from "./authentication-manager.js"
2
+ export * from "./identity-attributes-manager.js"
3
+ export * from "./identity-attributes.js"
4
+ export * from "./constants.js"
5
+ export * from "./types.js"
6
+ export * from "../hooks/createAuthHooks.js"
7
+ export * from "./createIdentityAttributeHooks.js"