@bounded-sh/client 0.0.111 → 0.0.112-ux-audit.2

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 (67) hide show
  1. package/dist/auth/index.d.ts +3 -1
  2. package/dist/auth/oidc-auth.d.ts +3 -1
  3. package/dist/auth/providers/email-auth-provider.d.ts +1 -1
  4. package/dist/auth/providers/guest-auth-provider.d.ts +1 -1
  5. package/dist/auth/providers/injected-evm-wallet-provider.d.ts +1 -1
  6. package/dist/auth/providers/injected-wallet-provider.d.ts +1 -1
  7. package/dist/auth/providers/offchain-auth-provider.d.ts +1 -1
  8. package/dist/auth/providers/privy-expo-provider.d.ts +1 -1
  9. package/dist/{email-auth-provider-DxS2WYxM.js → email-auth-provider-53sA5_i2.js} +4 -3
  10. package/dist/email-auth-provider-53sA5_i2.js.map +1 -0
  11. package/dist/{email-auth-provider-CVp6hSCo.mjs → email-auth-provider-BfCH7K6N.mjs} +4 -3
  12. package/dist/email-auth-provider-BfCH7K6N.mjs.map +1 -0
  13. package/dist/{email-auth-provider-BfBvF_4M.mjs → email-auth-provider-C7RXLVd9.mjs} +4 -3
  14. package/dist/email-auth-provider-C7RXLVd9.mjs.map +1 -0
  15. package/dist/{email-auth-provider-CiqhQ1c2.js → email-auth-provider-DTrCfOJd.js} +4 -3
  16. package/dist/email-auth-provider-DTrCfOJd.js.map +1 -0
  17. package/dist/{guest-auth-provider-DDBXeHoo.js → guest-auth-provider-00KkSNpE.js} +10 -5
  18. package/dist/guest-auth-provider-00KkSNpE.js.map +1 -0
  19. package/dist/{guest-auth-provider-Bm_yqQF6.mjs → guest-auth-provider-2J4fnTX5.mjs} +10 -5
  20. package/dist/guest-auth-provider-2J4fnTX5.mjs.map +1 -0
  21. package/dist/{guest-auth-provider-Cc8y4Qd1.mjs → guest-auth-provider-DjOFjtnk.mjs} +10 -5
  22. package/dist/guest-auth-provider-DjOFjtnk.mjs.map +1 -0
  23. package/dist/{guest-auth-provider-D9FGNuP9.js → guest-auth-provider-Tndt6tzJ.js} +10 -5
  24. package/dist/guest-auth-provider-Tndt6tzJ.js.map +1 -0
  25. package/dist/{index-B9__gwTi.mjs → index-D3kdhCpH.mjs} +313 -84
  26. package/dist/index-D3kdhCpH.mjs.map +1 -0
  27. package/dist/{index-BghQ8jX0.js → index-DJeEMevo.js} +312 -83
  28. package/dist/index-DJeEMevo.js.map +1 -0
  29. package/dist/index.js +1 -1
  30. package/dist/index.mjs +1 -1
  31. package/dist/{index.native-BHjpXkKQ.mjs → index.native-CAH_ks0s.mjs} +200 -56
  32. package/dist/index.native-CAH_ks0s.mjs.map +1 -0
  33. package/dist/{index.native-B_AFa0GD.js → index.native-ingNhWPw.js} +199 -55
  34. package/dist/index.native-ingNhWPw.js.map +1 -0
  35. package/dist/index.native.js +1 -1
  36. package/dist/index.native.mjs +1 -1
  37. package/dist/{injected-evm-wallet-provider-CIeCSOPL.mjs → injected-evm-wallet-provider-BtyG1HOX.mjs} +4 -3
  38. package/dist/injected-evm-wallet-provider-BtyG1HOX.mjs.map +1 -0
  39. package/dist/{injected-evm-wallet-provider-DeLUwvGs.js → injected-evm-wallet-provider-Cao1U0NZ.js} +4 -3
  40. package/dist/{injected-evm-wallet-provider-DeLUwvGs.js.map → injected-evm-wallet-provider-Cao1U0NZ.js.map} +1 -1
  41. package/dist/{injected-evm-wallet-provider-BUjqxQxp.mjs → injected-evm-wallet-provider-DXjwPjLW.mjs} +4 -3
  42. package/dist/{injected-evm-wallet-provider-BUjqxQxp.mjs.map → injected-evm-wallet-provider-DXjwPjLW.mjs.map} +1 -1
  43. package/dist/{injected-evm-wallet-provider-Ckb85p6a.js → injected-evm-wallet-provider-IlkuKffc.js} +4 -3
  44. package/dist/injected-evm-wallet-provider-IlkuKffc.js.map +1 -0
  45. package/dist/{injected-wallet-provider-BlzA0J_V.mjs → injected-wallet-provider-0SSONh1d.mjs} +4 -3
  46. package/dist/{injected-wallet-provider-BlzA0J_V.mjs.map → injected-wallet-provider-0SSONh1d.mjs.map} +1 -1
  47. package/dist/{injected-wallet-provider-Cfb4ayCP.js → injected-wallet-provider-BEYwsn6M.js} +4 -3
  48. package/dist/{injected-wallet-provider-Cfb4ayCP.js.map → injected-wallet-provider-BEYwsn6M.js.map} +1 -1
  49. package/dist/{injected-wallet-provider-BEuj4sM3.mjs → injected-wallet-provider-CfYNIIHd.mjs} +4 -3
  50. package/dist/{injected-wallet-provider-BEuj4sM3.mjs.map → injected-wallet-provider-CfYNIIHd.mjs.map} +1 -1
  51. package/dist/{injected-wallet-provider-CUKdAuFu.js → injected-wallet-provider-DSlHfgMU.js} +4 -3
  52. package/dist/{injected-wallet-provider-CUKdAuFu.js.map → injected-wallet-provider-DSlHfgMU.js.map} +1 -1
  53. package/package.json +2 -2
  54. package/dist/email-auth-provider-BfBvF_4M.mjs.map +0 -1
  55. package/dist/email-auth-provider-CVp6hSCo.mjs.map +0 -1
  56. package/dist/email-auth-provider-CiqhQ1c2.js.map +0 -1
  57. package/dist/email-auth-provider-DxS2WYxM.js.map +0 -1
  58. package/dist/guest-auth-provider-Bm_yqQF6.mjs.map +0 -1
  59. package/dist/guest-auth-provider-Cc8y4Qd1.mjs.map +0 -1
  60. package/dist/guest-auth-provider-D9FGNuP9.js.map +0 -1
  61. package/dist/guest-auth-provider-DDBXeHoo.js.map +0 -1
  62. package/dist/index-B9__gwTi.mjs.map +0 -1
  63. package/dist/index-BghQ8jX0.js.map +0 -1
  64. package/dist/index.native-BHjpXkKQ.mjs.map +0 -1
  65. package/dist/index.native-B_AFa0GD.js.map +0 -1
  66. package/dist/injected-evm-wallet-provider-CIeCSOPL.mjs.map +0 -1
  67. package/dist/injected-evm-wallet-provider-Ckb85p6a.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-BghQ8jX0.js');
3
+ var index = require('./index-DJeEMevo.js');
4
4
  var core = require('@bounded-sh/core');
5
5
  require('react');
6
6
 
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { ag as BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA, af as BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA, ae as BUILD_REVIEW_CAPABILITY_SCHEMA, a7 as BuildsApiError, ad as FAILURE_REASONS, a9 as NON_TERMINAL_STATES, a1 as OffchainAuthProvider, ac as PARK_REASONS, a2 as PrivyExpoProvider, ab as RUN_STATES, aa as TERMINAL_STATES, W as WalletConfigError, Q as apps, a6 as builds, I as completeLoginFromRedirect, a4 as connectBroadcast, a5 as connectServiceSession, o as defaultInjectedEvmProvider, f as defaultInjectedProvider, F as ensureSharedWalletLoginReady, M as ensureSignedIn, E as ensureWalletLoginReady, w as getAuthLoading, A as getAuthProvider, q as getCurrentUser, Y as getIdToken, J as getLoginReturnError, S as getOrCreateTurnkeyWallet, g as getPlatform, V as getTurnkeyWalletRecoveryUrl, U as getTurnkeyWalletSessionState, p as init, y as login, K as loginWithPopup, C as loginWithPrivy, H as loginWithRedirect, D as loginWithWallet, z as logout, a8 as mediaAgentTools, v as onAuthLoadingChanged, u as onAuthStateChanged, Z as onramp, _ as onrampQuote, $ as onrampStatus, L as openBoundedWidget, T as openTurnkeyKeyExport, G as preloadWalletLogin, a3 as requestEip6963Providers, a0 as setPlatform, B as signInAnonymously, R as signSolanaMessageViaTurnkey, X as startTurnkeyEmailLogin, N as useAuth, P as useBuildRun, O as useQuery, x as waitForAuthReady } from './index-B9__gwTi.mjs';
1
+ export { ag as BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA, af as BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA, ae as BUILD_REVIEW_CAPABILITY_SCHEMA, a7 as BuildsApiError, ad as FAILURE_REASONS, a9 as NON_TERMINAL_STATES, a1 as OffchainAuthProvider, ac as PARK_REASONS, a2 as PrivyExpoProvider, ab as RUN_STATES, aa as TERMINAL_STATES, W as WalletConfigError, Q as apps, a6 as builds, I as completeLoginFromRedirect, a4 as connectBroadcast, a5 as connectServiceSession, o as defaultInjectedEvmProvider, f as defaultInjectedProvider, F as ensureSharedWalletLoginReady, M as ensureSignedIn, E as ensureWalletLoginReady, w as getAuthLoading, A as getAuthProvider, q as getCurrentUser, Y as getIdToken, J as getLoginReturnError, S as getOrCreateTurnkeyWallet, g as getPlatform, V as getTurnkeyWalletRecoveryUrl, U as getTurnkeyWalletSessionState, p as init, y as login, K as loginWithPopup, C as loginWithPrivy, H as loginWithRedirect, D as loginWithWallet, z as logout, a8 as mediaAgentTools, v as onAuthLoadingChanged, u as onAuthStateChanged, Z as onramp, _ as onrampQuote, $ as onrampStatus, L as openBoundedWidget, T as openTurnkeyKeyExport, G as preloadWalletLogin, a3 as requestEip6963Providers, a0 as setPlatform, B as signInAnonymously, R as signSolanaMessageViaTurnkey, X as startTurnkeyEmailLogin, N as useAuth, P as useBuildRun, O as useQuery, x as waitForAuthReady } from './index-D3kdhCpH.mjs';
2
2
  export * from '@bounded-sh/core';
3
3
  export { AppTokenExchangeError, BoundedDeclineError, DECLINE_EVENT_NAME, RATE_LIMIT_EVENT_NAME, ScopedClientReadOnlyError, aggregate, clearAppTokenCache, count, createAppTokenAuthHeaders, createClient, deriveDecline, dispatchDeclineEvent, dispatchRateLimitEvent, docId, exchangeAppToken, get, getConfig, getFiles, getMany, queryAggregate, reconcileSetResult, runExpression, runExpressionMany, runQuery, runQueryMany, search, set, setFile, setMany, signAndSubmitTransaction, signMessage, signTransaction, subscribe, waitForSetResult } from '@bounded-sh/core';
4
4
  import 'react';
@@ -1,4 +1,4 @@
1
- import { getConfig, getActiveSessionManager, WebSessionManager, ReactNativeSessionManager, revokeSession, init as init$1, reconnectWithNewAuth, onSessionCleared, deriveUserIdentityFromIdToken, subscribe, getFreshAuthToken, getSessionGeneration, getIdToken as getIdToken$1, genAuthNonce, genSolanaMessage, createSessionWithSignature } from '@bounded-sh/core';
1
+ import { getConfig, getActiveSessionManager, WebSessionManager, ReactNativeSessionManager, revokeSession, reconnectWithNewAuth, init as init$1, deriveUserIdentityFromIdToken, onSessionCleared, subscribe, getFreshAuthToken, getSessionGeneration, getIdToken as getIdToken$1, genAuthNonce, genSolanaMessage, createSessionWithSignature } from '@bounded-sh/core';
2
2
  import * as React from 'react';
3
3
 
4
4
  function walletContextFromClaims(claims, baseUrl, expectedAppId, tenantOrigin) {
@@ -215,8 +215,9 @@ class OffchainAuthProvider {
215
215
  async logout() {
216
216
  await this.wrappedProvider.logout();
217
217
  }
218
- async restoreSession() {
219
- const user = await this.wrappedProvider.restoreSession();
218
+ async restoreSession(beforeCommit) {
219
+ const provider = this.wrappedProvider;
220
+ const user = await provider.restoreSession(beforeCommit);
220
221
  if (user) {
221
222
  return Object.assign(Object.assign({}, user), { provider: this });
222
223
  }
@@ -5276,7 +5277,8 @@ async function loginWithRedirect(opts) {
5276
5277
  return runNativeLogin(opts);
5277
5278
  if (!hasDOM$1())
5278
5279
  throw new Error('loginWithRedirect requires a browser or a configured React Native platform');
5279
- const { url, pkce } = await buildAuthorize(opts);
5280
+ const { url, pkce } = await untilAborted(buildAuthorize(opts), opts.signal);
5281
+ throwIfCancelled(opts.signal);
5280
5282
  // Stash the verifier + state so the redirect_uri page can finish the exchange. sessionStorage
5281
5283
  // is per-tab and cleared on close — appropriate for a short-lived auth handshake. Keyed by
5282
5284
  // this attempt's own state so a second concurrent login cannot overwrite it.
@@ -5305,9 +5307,29 @@ async function completeLoginFromRedirect() {
5305
5307
  throw error;
5306
5308
  return result.user;
5307
5309
  }
5310
+ function throwIfCancelled(signal) {
5311
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
5312
+ throw new Error('cancelled');
5313
+ }
5314
+ async function untilAborted(work, signal) {
5315
+ if (!signal)
5316
+ return work;
5317
+ throwIfCancelled(signal);
5318
+ let onAbort;
5319
+ try {
5320
+ return await Promise.race([work, new Promise((_, reject) => {
5321
+ onAbort = () => reject(new Error('cancelled'));
5322
+ signal.addEventListener('abort', onAbort, { once: true });
5323
+ })]);
5324
+ }
5325
+ finally {
5326
+ signal.removeEventListener('abort', onAbort);
5327
+ }
5328
+ }
5308
5329
  /** Popup variant: open the hosted login in a popup; resolves with the User once the popup's
5309
5330
  * redirect_uri page initializes the SDK, which hands the code back automatically. */
5310
5331
  async function loginWithPopup(opts) {
5332
+ throwIfCancelled(opts.signal);
5311
5333
  if (!hasDOM$1())
5312
5334
  throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
5313
5335
  // Open synchronously, before PKCE/config awaits consume the click's user-
@@ -5325,7 +5347,7 @@ async function loginWithPopup(opts) {
5325
5347
  throw new Error('Popup blocked — use loginWithRedirect() instead');
5326
5348
  let authorization;
5327
5349
  try {
5328
- authorization = await buildAuthorize(opts, 'popup');
5350
+ authorization = await untilAborted(buildAuthorize(opts, 'popup'), opts.signal);
5329
5351
  }
5330
5352
  catch (error) {
5331
5353
  try {
@@ -5342,6 +5364,7 @@ async function loginWithPopup(opts) {
5342
5364
  // window with no listener or watchdog attached.
5343
5365
  let expectedOrigin;
5344
5366
  try {
5367
+ throwIfCancelled(opts.signal);
5345
5368
  expectedOrigin = new URL(pkce.redirectUri).origin;
5346
5369
  sessionStorage.setItem(pkceKey(pkce.state), JSON.stringify(pkce));
5347
5370
  }
@@ -5373,20 +5396,42 @@ async function loginWithPopup(opts) {
5373
5396
  setAuthLoading(true); // reflect "signing in…" while the popup is open
5374
5397
  try {
5375
5398
  return await new Promise((resolve, reject) => {
5399
+ var _a, _b;
5376
5400
  let settled = false;
5401
+ let handedOff = false;
5402
+ let committing = false;
5377
5403
  let timer;
5378
5404
  let navigationTimer;
5379
5405
  let completionTimer;
5380
5406
  const cleanup = () => {
5381
- settled = true;
5382
5407
  clearInterval(timer);
5383
5408
  clearTimeout(navigationTimer);
5384
5409
  clearTimeout(completionTimer);
5385
5410
  window.removeEventListener('message', onMsg);
5386
5411
  removeAttempt(pkce.state);
5387
5412
  };
5413
+ const finish = (error, user) => {
5414
+ var _a;
5415
+ if (settled)
5416
+ return;
5417
+ settled = true;
5418
+ cleanup();
5419
+ (_a = opts.signal) === null || _a === void 0 ? void 0 : _a.removeEventListener('abort', onAbort);
5420
+ try {
5421
+ popup.close();
5422
+ }
5423
+ catch ( /* ignore */_b) { /* ignore */ }
5424
+ if (error)
5425
+ reject(error);
5426
+ else
5427
+ resolve(user);
5428
+ };
5429
+ // Provider adoption commits synchronously after its final guard. An auth
5430
+ // listener dismissing the card there cannot cancel an already signed-in user.
5431
+ const onAbort = () => { if (!committing)
5432
+ finish(new Error('cancelled')); };
5388
5433
  const onMsg = async (ev) => {
5389
- if (settled || ev.origin !== expectedOrigin || ev.source !== popup)
5434
+ if (settled || handedOff || ev.origin !== expectedOrigin || ev.source !== popup)
5390
5435
  return;
5391
5436
  const d = ev.data;
5392
5437
  if (!d || d.type !== 'bounded-oidc-code' ||
@@ -5400,35 +5445,30 @@ async function loginWithPopup(opts) {
5400
5445
  return;
5401
5446
  if (d.appId !== pkce.appId || d.issuer !== pkce.issuer)
5402
5447
  return;
5448
+ handedOff = true;
5403
5449
  cleanup();
5404
5450
  try {
5451
+ throwIfCancelled(opts.signal);
5405
5452
  await validateAttempt(pkce, new URL(d.callbackUrl));
5453
+ throwIfCancelled(opts.signal);
5406
5454
  popup.postMessage({ type: 'bounded-oidc-accepted', state: pkce.state }, expectedOrigin);
5407
5455
  if (d.error)
5408
5456
  throw new Error(typeof d.errorDescription === 'string' ? d.errorDescription : `Login failed: ${d.error}`);
5409
- const user = await exchangeCode(d.code, pkce);
5410
- try {
5411
- popup.close();
5412
- }
5413
- catch ( /* ignore */_a) { /* ignore */ }
5414
- resolve(user);
5457
+ const user = await exchangeCode(d.code, pkce, opts.signal, () => { committing = true; });
5458
+ finish(undefined, user);
5415
5459
  }
5416
5460
  catch (e) {
5417
- try {
5418
- popup.close();
5419
- }
5420
- catch ( /* ignore */_b) { /* ignore */ }
5421
- reject(e);
5461
+ finish(e);
5422
5462
  }
5423
5463
  };
5464
+ (_a = opts.signal) === null || _a === void 0 ? void 0 : _a.addEventListener('abort', onAbort, { once: true });
5465
+ if ((_b = opts.signal) === null || _b === void 0 ? void 0 : _b.aborted) {
5466
+ onAbort();
5467
+ return;
5468
+ }
5424
5469
  window.addEventListener('message', onMsg);
5425
5470
  completionTimer = setTimeout(() => {
5426
- cleanup();
5427
- try {
5428
- popup.close();
5429
- }
5430
- catch ( /* ignore */_a) { /* ignore */ }
5431
- reject(new Error('Login expired or its popup disconnected. Start sign-in again.'));
5471
+ finish(new Error('Login expired or its popup disconnected. Start sign-in again.'));
5432
5472
  }, LOGIN_TIMEOUT_MS);
5433
5473
  // Some browser/extension combinations can return a real popup handle but
5434
5474
  // strand the window at about:blank instead of navigating to the hosted
@@ -5449,23 +5489,18 @@ async function loginWithPopup(opts) {
5449
5489
  const NAVIGATION_RECHECK_MS = 2000;
5450
5490
  const navigationStartedAt = Date.now();
5451
5491
  const checkNavigation = () => {
5452
- if (settled || popup.closed)
5492
+ if (settled || handedOff || popup.closed)
5453
5493
  return;
5454
5494
  try {
5455
5495
  if (popup.location.href === 'about:blank') {
5456
5496
  if (Date.now() - navigationStartedAt >= NAVIGATION_GRACE_MS) {
5457
- cleanup();
5458
- try {
5459
- popup.close();
5460
- }
5461
- catch ( /* ignore */_a) { /* ignore */ }
5462
- reject(new Error('Popup stalled at about:blank - use loginWithRedirect() instead'));
5497
+ finish(new Error('Popup stalled at about:blank - use loginWithRedirect() instead'));
5463
5498
  return;
5464
5499
  }
5465
5500
  navigationTimer = setTimeout(checkNavigation, NAVIGATION_RECHECK_MS);
5466
5501
  }
5467
5502
  }
5468
- catch ( /* cross-origin hosted auth loaded normally */_b) { /* cross-origin hosted auth loaded normally */ }
5503
+ catch ( /* cross-origin hosted auth loaded normally */_a) { /* cross-origin hosted auth loaded normally */ }
5469
5504
  };
5470
5505
  navigationTimer = setTimeout(checkNavigation, NAVIGATION_RECHECK_MS);
5471
5506
  // The popup posts its code and then closes ITSELF, so a naive "popup.closed -> reject"
@@ -5475,10 +5510,8 @@ async function loginWithPopup(opts) {
5475
5510
  timer = setInterval(() => {
5476
5511
  if (popup.closed) {
5477
5512
  clearInterval(timer);
5478
- setTimeout(() => { if (!settled) {
5479
- cleanup();
5480
- reject(new Error('Login window closed'));
5481
- } }, 500);
5513
+ setTimeout(() => { if (!settled && !handedOff)
5514
+ finish(new Error('Login window closed')); }, 500);
5482
5515
  }
5483
5516
  }, 500);
5484
5517
  });
@@ -5487,9 +5520,10 @@ async function loginWithPopup(opts) {
5487
5520
  setAuthLoading(false);
5488
5521
  }
5489
5522
  }
5490
- async function exchangeCode(code, pkce) {
5523
+ async function exchangeCode(code, pkce, signal, onCommitting) {
5491
5524
  const generation = getInitializationGeneration();
5492
5525
  const beforeCommit = () => {
5526
+ throwIfCancelled(signal);
5493
5527
  if (generation !== getInitializationGeneration())
5494
5528
  throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
5495
5529
  };
@@ -5505,7 +5539,7 @@ async function exchangeCode(code, pkce) {
5505
5539
  let res;
5506
5540
  try {
5507
5541
  res = await fetch(`${base}/api/auth/oauth2/token`, {
5508
- method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString(),
5542
+ method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString(), signal,
5509
5543
  });
5510
5544
  }
5511
5545
  catch (err) {
@@ -5531,7 +5565,7 @@ async function exchangeCode(code, pkce) {
5531
5565
  // Adopt on EVERY session surface (see establishInlineSession) - hosted
5532
5566
  // popup/redirect completions all land here, so this closes their gap too.
5533
5567
  try {
5534
- await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit });
5568
+ await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit: () => { beforeCommit(); onCommitting === null || onCommitting === void 0 ? void 0 : onCommitting(); } });
5535
5569
  }
5536
5570
  catch (error) {
5537
5571
  if (typeof rollback === 'function')
@@ -5600,6 +5634,31 @@ function idTokenIsGuest(idToken, expectedAddress) {
5600
5634
  function storedSessionIsGuest() {
5601
5635
  return idTokenIsGuest(getStoredIdToken());
5602
5636
  }
5637
+ /** Internal: select a cross-tab restore from bearer claims, never global hints. */
5638
+ function storedSessionAuthMethod(config) {
5639
+ try {
5640
+ const token = getStoredIdToken();
5641
+ if (!token)
5642
+ return null;
5643
+ let payload = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
5644
+ payload += '='.repeat((4 - payload.length % 4) % 4);
5645
+ const claims = JSON.parse(getPlatform().atob(payload));
5646
+ const method = claims.is_anonymous === true ? 'guest'
5647
+ : claims['custom:walletProvider'] === 'turnkey' || claims.email ? 'email'
5648
+ : claims['custom:evmWalletAddress'] ? 'evm-wallet'
5649
+ : claims['custom:walletAddress'] ? 'phantom' : 'email';
5650
+ if (config.authMethod != null && canonicalWalletAuthMethod(config.authMethod) !== method)
5651
+ return null;
5652
+ if (method === 'evm-wallet' && config.authMethod !== 'evm-wallet')
5653
+ return null;
5654
+ if (method === 'phantom' && (isWalletLoginDisabled(config) || isEmailRequired(config)))
5655
+ return null;
5656
+ return method;
5657
+ }
5658
+ catch (_a) {
5659
+ return null;
5660
+ }
5661
+ }
5603
5662
  /**
5604
5663
  * Pick the provider used for an ordinary `init({ appId })` restore.
5605
5664
  *
@@ -5846,9 +5905,10 @@ function optionalProviderError(method) {
5846
5905
  `loginWithRedirect(...) for Bounded Auth, signInAnonymously() for guests, ` +
5847
5906
  `or install/import an explicit wallet-provider entry once your app opts into "${method}".`);
5848
5907
  }
5849
- async function getAuthProvider(config) {
5908
+ async function getAuthProvider(config, fromStorage = false) {
5850
5909
  var _a;
5851
- const nextProviderConfigKey = config ? authProviderConfigKey(config) : null;
5910
+ const requestedAuthMethod = config ? (fromStorage ? storedSessionAuthMethod(config) : authMethodForInit(config)) : null;
5911
+ const nextProviderConfigKey = config ? authProviderConfigKey(Object.assign(Object.assign({}, config), { authMethod: requestedAuthMethod })) : null;
5852
5912
  if (currentAuthProvider && (!config || currentAuthProviderConfigKey === nextProviderConfigKey)) {
5853
5913
  return currentAuthProvider;
5854
5914
  }
@@ -5863,7 +5923,8 @@ async function getAuthProvider(config) {
5863
5923
  initConfig = config;
5864
5924
  // Keep what the app actually wrote for error text; everything else runs on
5865
5925
  // the canonical method so exactly one string reaches storage and the issuer.
5866
- const requestedAuthMethod = authMethodForInit(config);
5926
+ if (!requestedAuthMethod)
5927
+ throw new Error('The stored session is incompatible with this app authentication configuration.');
5867
5928
  const authMethod = canonicalWalletAuthMethod(requestedAuthMethod);
5868
5929
  const rpcUrl = normalizeSolanaRpcUrl(config.rpcUrl);
5869
5930
  const configuredNetwork = getConfiguredSolanaNetwork(config);
@@ -5888,7 +5949,7 @@ async function getAuthProvider(config) {
5888
5949
  // manager as legacy email sessions. Human login should be started
5889
5950
  // with loginWithRedirect(); this provider is kept as the restore and
5890
5951
  // logout surface for those sessions.
5891
- currentAuthProvider = new (await import('./email-auth-provider-CVp6hSCo.mjs')).EmailAuthProvider();
5952
+ currentAuthProvider = new (await import('./email-auth-provider-BfCH7K6N.mjs')).EmailAuthProvider();
5892
5953
  break;
5893
5954
  case "phantom": {
5894
5955
  // First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
@@ -5930,7 +5991,7 @@ async function getAuthProvider(config) {
5930
5991
  // (bounded-login-widget.ts), so registration never counts as
5931
5992
  // evidence the user has a wallet.
5932
5993
  await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
5933
- const { InjectedWalletProvider } = await import('./injected-wallet-provider-BEuj4sM3.mjs');
5994
+ const { InjectedWalletProvider } = await import('./injected-wallet-provider-CfYNIIHd.mjs');
5934
5995
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
5935
5996
  break;
5936
5997
  }
@@ -5943,7 +6004,7 @@ async function getAuthProvider(config) {
5943
6004
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
5944
6005
  // to point at a specific wallet or set chainId/domain.
5945
6006
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
5946
- const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-BUjqxQxp.mjs');
6007
+ const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-DXjwPjLW.mjs');
5947
6008
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
5948
6009
  break;
5949
6010
  }
@@ -5968,7 +6029,7 @@ async function getAuthProvider(config) {
5968
6029
  break;
5969
6030
  case "guest":
5970
6031
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
5971
- currentAuthProvider = new (await import('./guest-auth-provider-Cc8y4Qd1.mjs')).GuestAuthProvider();
6032
+ currentAuthProvider = new (await import('./guest-auth-provider-DjOFjtnk.mjs')).GuestAuthProvider();
5972
6033
  break;
5973
6034
  }
5974
6035
  if (!currentAuthProvider) {
@@ -6030,7 +6091,7 @@ async function signInAnonymously() {
6030
6091
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
6031
6092
  // the main bundle for data-only apps. Specifier is a static string literal
6032
6093
  // so bundle-splitting can see it.
6033
- const provider = new (await import('./guest-auth-provider-Cc8y4Qd1.mjs')).GuestAuthProvider();
6094
+ const provider = new (await import('./guest-auth-provider-DjOFjtnk.mjs')).GuestAuthProvider();
6034
6095
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
6035
6096
  // adoptSessionProvider awaits only getConfig() (already resolved here) and then
6036
6097
  // runs every write - provider/method/config/marker/user - in one synchronous
@@ -6201,7 +6262,7 @@ function ensureSharedWalletLoginReady() {
6201
6262
  if (!sharedWalletReady) {
6202
6263
  sharedWalletReady = (async () => {
6203
6264
  await getConfig();
6204
- await import('./injected-wallet-provider-BEuj4sM3.mjs');
6265
+ await import('./injected-wallet-provider-CfYNIIHd.mjs');
6205
6266
  })().catch((error) => {
6206
6267
  sharedWalletReady = null;
6207
6268
  throw error;
@@ -6497,12 +6558,80 @@ let isAuthLoading = false;
6497
6558
  let authReadyPromise = null;
6498
6559
  let initializingConfig = null;
6499
6560
  let initializationGeneration = 0;
6561
+ let initializedConfig = null;
6562
+ let storageBridgeRegistered = false;
6563
+ let observedIdToken = null;
6564
+ let observedIssuer = null;
6565
+ // Storage events carry no authority. Re-read the active manager's app/issuer
6566
+ // namespace, then use the ordinary initializer/provider restore and listeners.
6567
+ function registerStorageBridge() {
6568
+ if (storageBridgeRegistered || !getPlatform().hasDOM || typeof window === 'undefined')
6569
+ return;
6570
+ storageBridgeRegistered = true;
6571
+ window.addEventListener('storage', (event) => {
6572
+ if (event.storageArea !== window.localStorage || !initializedConfig)
6573
+ return;
6574
+ void syncStoredSession().catch((error) => console.warn('Bounded session synchronization failed:', error));
6575
+ });
6576
+ }
6577
+ async function syncStoredSession() {
6578
+ const config = initializedConfig;
6579
+ if (!config)
6580
+ return;
6581
+ const token = WebSessionManager.getIdToken();
6582
+ const issuer = WebSessionManager.getIssuer();
6583
+ if (token === observedIdToken && issuer === observedIssuer)
6584
+ return;
6585
+ observedIdToken = token;
6586
+ observedIssuer = issuer;
6587
+ const generation = ++initializationGeneration;
6588
+ // Hide the former principal while the replacement is validated. This also
6589
+ // reconnects live reads through the existing auth-state change path.
6590
+ setCurrentUser(null);
6591
+ if (authReadyPromise)
6592
+ await authReadyPromise.catch(() => { });
6593
+ if (generation !== initializationGeneration || config !== initializedConfig)
6594
+ return;
6595
+ const record = await WebSessionManager.readSessionWithGeneration();
6596
+ if (generation !== initializationGeneration || config !== initializedConfig)
6597
+ return;
6598
+ if (!record)
6599
+ return;
6600
+ const normalize = (url) => (url || '').replace(/\/+$/, '');
6601
+ const resolvedConfig = await getConfig();
6602
+ if (generation !== initializationGeneration || config !== initializedConfig)
6603
+ return;
6604
+ const allowedIssuers = [normalize(resolvedConfig.humanAuthApiUrl || 'https://auth.bounded.sh'), normalize(resolvedConfig.authApiUrl)].filter(Boolean);
6605
+ // A legacy shared blob can belong to another app. Never restore or clear
6606
+ // that app's credential; the current app remains signed out.
6607
+ if ((record.appId && record.appId !== config.appId)
6608
+ || (record.issuer && !allowedIssuers.includes(normalize(record.issuer))))
6609
+ return;
6610
+ if (!storedSessionAuthMethod(config))
6611
+ return;
6612
+ const restored = startInitialization(config, true);
6613
+ const restoreGeneration = initializationGeneration;
6614
+ try {
6615
+ await restored;
6616
+ }
6617
+ catch (error) {
6618
+ if (restoreGeneration === initializationGeneration && WebSessionManager.getIdToken())
6619
+ throw error;
6620
+ }
6621
+ }
6500
6622
  /** Internal guard for a login result arriving after the app reconfigured the SDK. */
6501
6623
  function getInitializationGeneration() { return initializationGeneration; }
6502
6624
  // This file acts as a middleware for the global state of the SDK
6503
6625
  // This mostly involves setting up the AuthProvider and managing the current user
6504
- async function initialize(requestedConfig) {
6626
+ async function initialize(requestedConfig, fromStorage) {
6505
6627
  const newConfig = withDefaultSolanaRpcUrl(requestedConfig);
6628
+ const generation = initializationGeneration;
6629
+ const validateStorage = fromStorage ? WebSessionManager.captureSessionEstablishment() : undefined;
6630
+ const beforeCommit = () => {
6631
+ if (generation !== initializationGeneration)
6632
+ throw new Error('Session restore was superseded.');
6633
+ validateStorage === null || validateStorage === void 0 ? void 0 : validateStorage();
6634
+ };
6506
6635
  // Mark auth as loading for the ENTIRE init/restore window so consumers render
6507
6636
  // a neutral/loading state instead of the signed-out UI while we rehydrate the
6508
6637
  // session — otherwise getCurrentUser() is null + loading is false during the
@@ -6521,8 +6650,9 @@ async function initialize(requestedConfig) {
6521
6650
  // onAuthStateChanged never fired, and every consumer UI went on showing a
6522
6651
  // connected wallet for a session the server no longer knew about.
6523
6652
  registerSessionClearedBridge();
6653
+ registerStorageBridge();
6524
6654
  // Get auth provider first
6525
- authProviderInstance = await getAuthProvider(newConfig);
6655
+ authProviderInstance = await getAuthProvider(newConfig, fromStorage);
6526
6656
  // Initialize config with auth provider
6527
6657
  await init$1(Object.assign(Object.assign({}, newConfig), { authProvider: authProviderInstance }));
6528
6658
  // A return establishes its own provider/session, or hands off a popup.
@@ -6547,7 +6677,8 @@ async function initialize(requestedConfig) {
6547
6677
  // Wait for restoreSession to complete
6548
6678
  // The appId check is done in SessionManager.getSession which is called by restoreSession
6549
6679
  // If there's an appId mismatch, the session will be cleared and restoreSession will return null
6550
- let user = await authProviderInstance.restoreSession();
6680
+ let user = await authProviderInstance.restoreSession(beforeCommit);
6681
+ beforeCommit();
6551
6682
  // If a session exists but was created before auth method tracking (legacy session),
6552
6683
  // clear it to avoid mismatches when the app switches auth methods (e.g. email → phantom)
6553
6684
  //
@@ -6555,9 +6686,10 @@ async function initialize(requestedConfig) {
6555
6686
  // unawaited call would return a Promise (always truthy) — nulling the user
6556
6687
  // unconditionally AND letting setCurrentUser run while the old credential
6557
6688
  // was still in storage.
6558
- if (user && await clearIncompatibleSession()) {
6689
+ if (user && !fromStorage && await clearIncompatibleSession()) {
6559
6690
  user = null;
6560
6691
  }
6692
+ beforeCommit();
6561
6693
  setCurrentUser(user);
6562
6694
  // Mark init as completed
6563
6695
  initCompleted = true;
@@ -6597,13 +6729,17 @@ function sameConfigValue(a, b, seen = new WeakMap()) {
6597
6729
  && sameConfigValue(a[key], b[key], seen));
6598
6730
  }
6599
6731
  function init(newConfig) {
6732
+ return startInitialization(newConfig, false);
6733
+ }
6734
+ function startInitialization(newConfig, fromStorage) {
6600
6735
  if (initializingConfig) {
6601
6736
  if (sameConfigValue(initializingConfig, newConfig)) {
6602
6737
  return authReadyPromise;
6603
6738
  }
6604
6739
  return Promise.reject(new Error('SDK initialization is in progress. Await init() before changing its configuration.'));
6605
6740
  }
6606
- initializingConfig = Object.assign({}, newConfig);
6741
+ initializedConfig = Object.assign({}, newConfig);
6742
+ initializingConfig = initializedConfig;
6607
6743
  initializationGeneration++;
6608
6744
  let resolveReady;
6609
6745
  let rejectReady;
@@ -6615,7 +6751,7 @@ function init(newConfig) {
6615
6751
  // auth-loading listener to call waitForAuthReady() synchronously when init()
6616
6752
  // announces that session restoration has begun.
6617
6753
  authReadyPromise = ready;
6618
- void initialize(initializingConfig).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
6754
+ void initialize(initializingConfig, fromStorage).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
6619
6755
  return ready;
6620
6756
  }
6621
6757
  /**
@@ -6688,6 +6824,13 @@ function userPrincipalKey(user) {
6688
6824
  }
6689
6825
  // Update the currentUser and notify listeners
6690
6826
  function setCurrentUser(user) {
6827
+ observedIdToken = getCurrentIdToken();
6828
+ try {
6829
+ observedIssuer = getActiveSessionManager().getIssuer();
6830
+ }
6831
+ catch (_a) {
6832
+ observedIssuer = null;
6833
+ }
6691
6834
  // Enrich with the universal identity fields (id/email) derived from the
6692
6835
  // active session's idToken — central chokepoint so every provider's user
6693
6836
  // object gains { id, address, email } without each construction site
@@ -8490,8 +8633,9 @@ class PrivyExpoProvider {
8490
8633
  const { signature } = await this.signAndSubmitInternal(transaction, wallet, connection);
8491
8634
  return signature;
8492
8635
  }
8493
- async restoreSession() {
8636
+ async restoreSession(beforeCommit) {
8494
8637
  const session = await getActiveSessionManager().getSession();
8638
+ beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit();
8495
8639
  if (session) {
8496
8640
  this.authenticatedAddress = session.address;
8497
8641
  return { provider: this, address: session.address };
@@ -8598,4 +8742,4 @@ class PrivyExpoProvider {
8598
8742
  }
8599
8743
 
8600
8744
  export { BUILD_REVIEW_CAPABILITY_SCHEMA as $, logout as A, getAuthProvider as B, signInAnonymously as C, loginWithPrivy as D, loginWithRedirect as E, completeLoginFromRedirect as F, getLoginReturnError as G, loginWithPopup as H, useAuth as I, useQuery as J, useBuildRun as K, apps as L, getIdToken as M, onramp as N, onrampQuote as O, onrampStatus as P, setPlatform as Q, OffchainAuthProvider as R, PrivyExpoProvider as S, builds as T, BuildsApiError as U, mediaAgentTools as V, NON_TERMINAL_STATES as W, TERMINAL_STATES as X, RUN_STATES as Y, PARK_REASONS as Z, FAILURE_REASONS as _, signMessageWithTurnkey as a, BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA as a0, BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA as a1, signTransactionWithTurnkey as b, signTransactionV1WithTurnkey as c, signAndSubmitTransactionWithTurnkey as d, bs58 as e, getWallets as f, getPlatform as g, hasTurnkeyWallet as h, isSolanaMobileWallet as i, solanaMobileWalletLeavesPage as j, setAuthLoading as k, bufferExports as l, setStoredAuthMethod as m, normalizeSolanaRpcUrl as n, confirmAndCheckTransaction as o, resolveSolanaRpcUrl as p, init as q, resolveSolanaWalletStandardChain as r, setCurrentUser as s, transactionHasSignature as t, getCurrentUser as u, onAuthStateChanged as v, onAuthLoadingChanged as w, getAuthLoading as x, waitForAuthReady as y, login as z };
8601
- //# sourceMappingURL=index.native-BHjpXkKQ.mjs.map
8745
+ //# sourceMappingURL=index.native-CAH_ks0s.mjs.map