@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
@@ -56,6 +56,13 @@ function walletOperationGuard() {
56
56
  throw new Error('wallet_session_changed');
57
57
  };
58
58
  }
59
+ function walletLoginGuard(signal) {
60
+ const generation = ++loginGeneration;
61
+ return () => {
62
+ if ((signal === null || signal === void 0 ? void 0 : signal.aborted) || generation !== loginGeneration)
63
+ throw new Error('wallet_login_cancelled');
64
+ };
65
+ }
59
66
  // This realm owns one active SDK session and one retained signer frame.
60
67
  let activeWalletState;
61
68
  function setWalletSessionState(key, state) { activeWalletState = { key, state }; }
@@ -277,8 +284,9 @@ class OffchainAuthProvider {
277
284
  async logout() {
278
285
  await this.wrappedProvider.logout();
279
286
  }
280
- async restoreSession() {
281
- const user = await this.wrappedProvider.restoreSession();
287
+ async restoreSession(beforeCommit) {
288
+ const provider = this.wrappedProvider;
289
+ const user = await provider.restoreSession(beforeCommit);
282
290
  if (user) {
283
291
  return Object.assign(Object.assign({}, user), { provider: this });
284
292
  }
@@ -5532,7 +5540,8 @@ async function loginWithRedirect(opts) {
5532
5540
  return runNativeLogin(opts);
5533
5541
  if (!hasDOM$2())
5534
5542
  throw new Error('loginWithRedirect requires a browser or a configured React Native platform');
5535
- const { url, pkce } = await buildAuthorize(opts);
5543
+ const { url, pkce } = await untilAborted(buildAuthorize(opts), opts.signal);
5544
+ throwIfCancelled(opts.signal);
5536
5545
  // Stash the verifier + state so the redirect_uri page can finish the exchange. sessionStorage
5537
5546
  // is per-tab and cleared on close — appropriate for a short-lived auth handshake. Keyed by
5538
5547
  // this attempt's own state so a second concurrent login cannot overwrite it.
@@ -5561,9 +5570,29 @@ async function completeLoginFromRedirect() {
5561
5570
  throw error;
5562
5571
  return result.user;
5563
5572
  }
5573
+ function throwIfCancelled(signal) {
5574
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
5575
+ throw new Error('cancelled');
5576
+ }
5577
+ async function untilAborted(work, signal) {
5578
+ if (!signal)
5579
+ return work;
5580
+ throwIfCancelled(signal);
5581
+ let onAbort;
5582
+ try {
5583
+ return await Promise.race([work, new Promise((_, reject) => {
5584
+ onAbort = () => reject(new Error('cancelled'));
5585
+ signal.addEventListener('abort', onAbort, { once: true });
5586
+ })]);
5587
+ }
5588
+ finally {
5589
+ signal.removeEventListener('abort', onAbort);
5590
+ }
5591
+ }
5564
5592
  /** Popup variant: open the hosted login in a popup; resolves with the User once the popup's
5565
5593
  * redirect_uri page initializes the SDK, which hands the code back automatically. */
5566
5594
  async function loginWithPopup(opts) {
5595
+ throwIfCancelled(opts.signal);
5567
5596
  if (!hasDOM$2())
5568
5597
  throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
5569
5598
  // Open synchronously, before PKCE/config awaits consume the click's user-
@@ -5581,7 +5610,7 @@ async function loginWithPopup(opts) {
5581
5610
  throw new Error('Popup blocked — use loginWithRedirect() instead');
5582
5611
  let authorization;
5583
5612
  try {
5584
- authorization = await buildAuthorize(opts, 'popup');
5613
+ authorization = await untilAborted(buildAuthorize(opts, 'popup'), opts.signal);
5585
5614
  }
5586
5615
  catch (error) {
5587
5616
  try {
@@ -5598,6 +5627,7 @@ async function loginWithPopup(opts) {
5598
5627
  // window with no listener or watchdog attached.
5599
5628
  let expectedOrigin;
5600
5629
  try {
5630
+ throwIfCancelled(opts.signal);
5601
5631
  expectedOrigin = new URL(pkce.redirectUri).origin;
5602
5632
  sessionStorage.setItem(pkceKey(pkce.state), JSON.stringify(pkce));
5603
5633
  }
@@ -5629,20 +5659,42 @@ async function loginWithPopup(opts) {
5629
5659
  setAuthLoading(true); // reflect "signing in…" while the popup is open
5630
5660
  try {
5631
5661
  return await new Promise((resolve, reject) => {
5662
+ var _a, _b;
5632
5663
  let settled = false;
5664
+ let handedOff = false;
5665
+ let committing = false;
5633
5666
  let timer;
5634
5667
  let navigationTimer;
5635
5668
  let completionTimer;
5636
5669
  const cleanup = () => {
5637
- settled = true;
5638
5670
  clearInterval(timer);
5639
5671
  clearTimeout(navigationTimer);
5640
5672
  clearTimeout(completionTimer);
5641
5673
  window.removeEventListener('message', onMsg);
5642
5674
  removeAttempt(pkce.state);
5643
5675
  };
5676
+ const finish = (error, user) => {
5677
+ var _a;
5678
+ if (settled)
5679
+ return;
5680
+ settled = true;
5681
+ cleanup();
5682
+ (_a = opts.signal) === null || _a === void 0 ? void 0 : _a.removeEventListener('abort', onAbort);
5683
+ try {
5684
+ popup.close();
5685
+ }
5686
+ catch ( /* ignore */_b) { /* ignore */ }
5687
+ if (error)
5688
+ reject(error);
5689
+ else
5690
+ resolve(user);
5691
+ };
5692
+ // Provider adoption commits synchronously after its final guard. An auth
5693
+ // listener dismissing the card there cannot cancel an already signed-in user.
5694
+ const onAbort = () => { if (!committing)
5695
+ finish(new Error('cancelled')); };
5644
5696
  const onMsg = async (ev) => {
5645
- if (settled || ev.origin !== expectedOrigin || ev.source !== popup)
5697
+ if (settled || handedOff || ev.origin !== expectedOrigin || ev.source !== popup)
5646
5698
  return;
5647
5699
  const d = ev.data;
5648
5700
  if (!d || d.type !== 'bounded-oidc-code' ||
@@ -5656,35 +5708,30 @@ async function loginWithPopup(opts) {
5656
5708
  return;
5657
5709
  if (d.appId !== pkce.appId || d.issuer !== pkce.issuer)
5658
5710
  return;
5711
+ handedOff = true;
5659
5712
  cleanup();
5660
5713
  try {
5714
+ throwIfCancelled(opts.signal);
5661
5715
  await validateAttempt(pkce, new URL(d.callbackUrl));
5716
+ throwIfCancelled(opts.signal);
5662
5717
  popup.postMessage({ type: 'bounded-oidc-accepted', state: pkce.state }, expectedOrigin);
5663
5718
  if (d.error)
5664
5719
  throw new Error(typeof d.errorDescription === 'string' ? d.errorDescription : `Login failed: ${d.error}`);
5665
- const user = await exchangeCode(d.code, pkce);
5666
- try {
5667
- popup.close();
5668
- }
5669
- catch ( /* ignore */_a) { /* ignore */ }
5670
- resolve(user);
5720
+ const user = await exchangeCode(d.code, pkce, opts.signal, () => { committing = true; });
5721
+ finish(undefined, user);
5671
5722
  }
5672
5723
  catch (e) {
5673
- try {
5674
- popup.close();
5675
- }
5676
- catch ( /* ignore */_b) { /* ignore */ }
5677
- reject(e);
5724
+ finish(e);
5678
5725
  }
5679
5726
  };
5727
+ (_a = opts.signal) === null || _a === void 0 ? void 0 : _a.addEventListener('abort', onAbort, { once: true });
5728
+ if ((_b = opts.signal) === null || _b === void 0 ? void 0 : _b.aborted) {
5729
+ onAbort();
5730
+ return;
5731
+ }
5680
5732
  window.addEventListener('message', onMsg);
5681
5733
  completionTimer = setTimeout(() => {
5682
- cleanup();
5683
- try {
5684
- popup.close();
5685
- }
5686
- catch ( /* ignore */_a) { /* ignore */ }
5687
- reject(new Error('Login expired or its popup disconnected. Start sign-in again.'));
5734
+ finish(new Error('Login expired or its popup disconnected. Start sign-in again.'));
5688
5735
  }, LOGIN_TIMEOUT_MS);
5689
5736
  // Some browser/extension combinations can return a real popup handle but
5690
5737
  // strand the window at about:blank instead of navigating to the hosted
@@ -5705,23 +5752,18 @@ async function loginWithPopup(opts) {
5705
5752
  const NAVIGATION_RECHECK_MS = 2000;
5706
5753
  const navigationStartedAt = Date.now();
5707
5754
  const checkNavigation = () => {
5708
- if (settled || popup.closed)
5755
+ if (settled || handedOff || popup.closed)
5709
5756
  return;
5710
5757
  try {
5711
5758
  if (popup.location.href === 'about:blank') {
5712
5759
  if (Date.now() - navigationStartedAt >= NAVIGATION_GRACE_MS) {
5713
- cleanup();
5714
- try {
5715
- popup.close();
5716
- }
5717
- catch ( /* ignore */_a) { /* ignore */ }
5718
- reject(new Error('Popup stalled at about:blank - use loginWithRedirect() instead'));
5760
+ finish(new Error('Popup stalled at about:blank - use loginWithRedirect() instead'));
5719
5761
  return;
5720
5762
  }
5721
5763
  navigationTimer = setTimeout(checkNavigation, NAVIGATION_RECHECK_MS);
5722
5764
  }
5723
5765
  }
5724
- catch ( /* cross-origin hosted auth loaded normally */_b) { /* cross-origin hosted auth loaded normally */ }
5766
+ catch ( /* cross-origin hosted auth loaded normally */_a) { /* cross-origin hosted auth loaded normally */ }
5725
5767
  };
5726
5768
  navigationTimer = setTimeout(checkNavigation, NAVIGATION_RECHECK_MS);
5727
5769
  // The popup posts its code and then closes ITSELF, so a naive "popup.closed -> reject"
@@ -5731,10 +5773,8 @@ async function loginWithPopup(opts) {
5731
5773
  timer = setInterval(() => {
5732
5774
  if (popup.closed) {
5733
5775
  clearInterval(timer);
5734
- setTimeout(() => { if (!settled) {
5735
- cleanup();
5736
- reject(new Error('Login window closed'));
5737
- } }, 500);
5776
+ setTimeout(() => { if (!settled && !handedOff)
5777
+ finish(new Error('Login window closed')); }, 500);
5738
5778
  }
5739
5779
  }, 500);
5740
5780
  });
@@ -5748,20 +5788,21 @@ async function loginWithPopup(opts) {
5748
5788
  // Turnkey-native login path (authMode:"turnkey"), which mints the session server-side
5749
5789
  // after Turnkey verifies the email OTP - there is no OIDC code to exchange. Wiring is
5750
5790
  // identical to exchangeCode's tail so the realtime worker cannot tell them apart.
5751
- async function establishInlineSession(data, beforeCommit, beforeStore) {
5791
+ async function establishInlineSession(data, beforeCommit, beforeStore, onCommitting) {
5752
5792
  const base = await issuerBase();
5793
+ beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit();
5753
5794
  const idToken = tokenField(data, 'id_token', 'idToken');
5754
5795
  if (!idToken)
5755
5796
  throw new Error('No id_token in the inline session response');
5756
5797
  const accessToken = tokenField(data, 'access_token', 'accessToken') || idToken;
5757
5798
  const refreshToken = boundedRefreshTokenField(data);
5758
5799
  const user = userFromIdToken(idToken);
5759
- const rollback = await sessionManager().storeSession(String(user.address || ''), accessToken, idToken, refreshToken, base, () => { });
5800
+ const rollback = await sessionManager().storeSession(String(user.address || ''), accessToken, idToken, refreshToken, base, () => { beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit(); });
5760
5801
  // Adopt the OIDC provider on EVERY session surface - including core's signing
5761
5802
  // pointer (getConfig().authProvider), which this path used to skip, leaving
5762
5803
  // onchain writes routed through a previous (e.g. logged-out wallet) provider.
5763
5804
  try {
5764
- await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit });
5805
+ await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit: () => { beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit(); onCommitting === null || onCommitting === void 0 ? void 0 : onCommitting(); } });
5765
5806
  }
5766
5807
  catch (error) {
5767
5808
  if (typeof rollback === 'function')
@@ -5785,9 +5826,10 @@ async function inlineAuthEndpoint() {
5785
5826
  const cfg = await core.getConfig();
5786
5827
  return { base: await issuerBase(), appId: cfg.appId };
5787
5828
  }
5788
- async function exchangeCode(code, pkce) {
5829
+ async function exchangeCode(code, pkce, signal, onCommitting) {
5789
5830
  const generation = getInitializationGeneration();
5790
5831
  const beforeCommit = () => {
5832
+ throwIfCancelled(signal);
5791
5833
  if (generation !== getInitializationGeneration())
5792
5834
  throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
5793
5835
  };
@@ -5803,7 +5845,7 @@ async function exchangeCode(code, pkce) {
5803
5845
  let res;
5804
5846
  try {
5805
5847
  res = await fetch(`${base}/api/auth/oauth2/token`, {
5806
- method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString(),
5848
+ method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString(), signal,
5807
5849
  });
5808
5850
  }
5809
5851
  catch (err) {
@@ -5829,7 +5871,7 @@ async function exchangeCode(code, pkce) {
5829
5871
  // Adopt on EVERY session surface (see establishInlineSession) - hosted
5830
5872
  // popup/redirect completions all land here, so this closes their gap too.
5831
5873
  try {
5832
- await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit });
5874
+ await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit: () => { beforeCommit(); onCommitting === null || onCommitting === void 0 ? void 0 : onCommitting(); } });
5833
5875
  }
5834
5876
  catch (error) {
5835
5877
  if (typeof rollback === 'function')
@@ -5898,6 +5940,31 @@ function idTokenIsGuest(idToken, expectedAddress) {
5898
5940
  function storedSessionIsGuest() {
5899
5941
  return idTokenIsGuest(getStoredIdToken());
5900
5942
  }
5943
+ /** Internal: select a cross-tab restore from bearer claims, never global hints. */
5944
+ function storedSessionAuthMethod(config) {
5945
+ try {
5946
+ const token = getStoredIdToken();
5947
+ if (!token)
5948
+ return null;
5949
+ let payload = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
5950
+ payload += '='.repeat((4 - payload.length % 4) % 4);
5951
+ const claims = JSON.parse(getPlatform().atob(payload));
5952
+ const method = claims.is_anonymous === true ? 'guest'
5953
+ : claims['custom:walletProvider'] === 'turnkey' || claims.email ? 'email'
5954
+ : claims['custom:evmWalletAddress'] ? 'evm-wallet'
5955
+ : claims['custom:walletAddress'] ? 'phantom' : 'email';
5956
+ if (config.authMethod != null && canonicalWalletAuthMethod(config.authMethod) !== method)
5957
+ return null;
5958
+ if (method === 'evm-wallet' && config.authMethod !== 'evm-wallet')
5959
+ return null;
5960
+ if (method === 'phantom' && (isWalletLoginDisabled(config) || isEmailRequired(config)))
5961
+ return null;
5962
+ return method;
5963
+ }
5964
+ catch (_a) {
5965
+ return null;
5966
+ }
5967
+ }
5901
5968
  /**
5902
5969
  * Pick the provider used for an ordinary `init({ appId })` restore.
5903
5970
  *
@@ -6159,9 +6226,10 @@ function optionalProviderError(method) {
6159
6226
  `loginWithRedirect(...) for Bounded Auth, signInAnonymously() for guests, ` +
6160
6227
  `or install/import an explicit wallet-provider entry once your app opts into "${method}".`);
6161
6228
  }
6162
- async function getAuthProvider(config) {
6229
+ async function getAuthProvider(config, fromStorage = false) {
6163
6230
  var _a;
6164
- const nextProviderConfigKey = config ? authProviderConfigKey(config) : null;
6231
+ const requestedAuthMethod = config ? (fromStorage ? storedSessionAuthMethod(config) : authMethodForInit(config)) : null;
6232
+ const nextProviderConfigKey = config ? authProviderConfigKey(Object.assign(Object.assign({}, config), { authMethod: requestedAuthMethod })) : null;
6165
6233
  if (currentAuthProvider && (!config || currentAuthProviderConfigKey === nextProviderConfigKey)) {
6166
6234
  return currentAuthProvider;
6167
6235
  }
@@ -6176,7 +6244,8 @@ async function getAuthProvider(config) {
6176
6244
  initConfig = config;
6177
6245
  // Keep what the app actually wrote for error text; everything else runs on
6178
6246
  // the canonical method so exactly one string reaches storage and the issuer.
6179
- const requestedAuthMethod = authMethodForInit(config);
6247
+ if (!requestedAuthMethod)
6248
+ throw new Error('The stored session is incompatible with this app authentication configuration.');
6180
6249
  const authMethod = canonicalWalletAuthMethod(requestedAuthMethod);
6181
6250
  const rpcUrl = normalizeSolanaRpcUrl(config.rpcUrl);
6182
6251
  const configuredNetwork = getConfiguredSolanaNetwork(config);
@@ -6201,7 +6270,7 @@ async function getAuthProvider(config) {
6201
6270
  // manager as legacy email sessions. Human login should be started
6202
6271
  // with loginWithRedirect(); this provider is kept as the restore and
6203
6272
  // logout surface for those sessions.
6204
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-DxS2WYxM.js'); })).EmailAuthProvider();
6273
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-53sA5_i2.js'); })).EmailAuthProvider();
6205
6274
  break;
6206
6275
  case "phantom": {
6207
6276
  // First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
@@ -6243,7 +6312,7 @@ async function getAuthProvider(config) {
6243
6312
  // (bounded-login-widget.ts), so registration never counts as
6244
6313
  // evidence the user has a wallet.
6245
6314
  await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
6246
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-Cfb4ayCP.js'); });
6315
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-BEYwsn6M.js'); });
6247
6316
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
6248
6317
  break;
6249
6318
  }
@@ -6256,7 +6325,7 @@ async function getAuthProvider(config) {
6256
6325
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
6257
6326
  // to point at a specific wallet or set chainId/domain.
6258
6327
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
6259
- const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-Ckb85p6a.js'); });
6328
+ const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-IlkuKffc.js'); });
6260
6329
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
6261
6330
  break;
6262
6331
  }
@@ -6281,7 +6350,7 @@ async function getAuthProvider(config) {
6281
6350
  break;
6282
6351
  case "guest":
6283
6352
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
6284
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-DDBXeHoo.js'); })).GuestAuthProvider();
6353
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-00KkSNpE.js'); })).GuestAuthProvider();
6285
6354
  break;
6286
6355
  }
6287
6356
  if (!currentAuthProvider) {
@@ -6343,7 +6412,7 @@ async function signInAnonymously() {
6343
6412
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
6344
6413
  // the main bundle for data-only apps. Specifier is a static string literal
6345
6414
  // so bundle-splitting can see it.
6346
- const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-DDBXeHoo.js'); })).GuestAuthProvider();
6415
+ const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-00KkSNpE.js'); })).GuestAuthProvider();
6347
6416
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
6348
6417
  // adoptSessionProvider awaits only getConfig() (already resolved here) and then
6349
6418
  // runs every write - provider/method/config/marker/user - in one synchronous
@@ -6514,7 +6583,7 @@ function ensureSharedWalletLoginReady() {
6514
6583
  if (!sharedWalletReady) {
6515
6584
  sharedWalletReady = (async () => {
6516
6585
  await core.getConfig();
6517
- await Promise.resolve().then(function () { return require('./injected-wallet-provider-Cfb4ayCP.js'); });
6586
+ await Promise.resolve().then(function () { return require('./injected-wallet-provider-BEYwsn6M.js'); });
6518
6587
  })().catch((error) => {
6519
6588
  sharedWalletReady = null;
6520
6589
  throw error;
@@ -6617,7 +6686,7 @@ async function loginWithWallet(options) {
6617
6686
  await ensureSharedWalletLoginReady();
6618
6687
  else
6619
6688
  await ensureWalletLoginReady();
6620
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-Cfb4ayCP.js'); });
6689
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-BEYwsn6M.js'); });
6621
6690
  // Keep the RAW wallet provider: the offchain wrapper forwards only a fixed
6622
6691
  // set of methods, so cleanup below must speak to the provider that actually
6623
6692
  // holds the config rather than to whatever is wrapping it.
@@ -6889,7 +6958,8 @@ var index = /*#__PURE__*/Object.freeze({
6889
6958
  logout: logout$1,
6890
6959
  preloadWalletLogin: preloadWalletLogin,
6891
6960
  resolveAuthMode: resolveAuthMode,
6892
- signInAnonymously: signInAnonymously
6961
+ signInAnonymously: signInAnonymously,
6962
+ storedSessionAuthMethod: storedSessionAuthMethod
6893
6963
  });
6894
6964
 
6895
6965
  /**
@@ -6967,12 +7037,80 @@ let isAuthLoading = false;
6967
7037
  let authReadyPromise = null;
6968
7038
  let initializingConfig = null;
6969
7039
  let initializationGeneration = 0;
7040
+ let initializedConfig = null;
7041
+ let storageBridgeRegistered = false;
7042
+ let observedIdToken = null;
7043
+ let observedIssuer = null;
7044
+ // Storage events carry no authority. Re-read the active manager's app/issuer
7045
+ // namespace, then use the ordinary initializer/provider restore and listeners.
7046
+ function registerStorageBridge() {
7047
+ if (storageBridgeRegistered || !getPlatform().hasDOM || typeof window === 'undefined')
7048
+ return;
7049
+ storageBridgeRegistered = true;
7050
+ window.addEventListener('storage', (event) => {
7051
+ if (event.storageArea !== window.localStorage || !initializedConfig)
7052
+ return;
7053
+ void syncStoredSession().catch((error) => console.warn('Bounded session synchronization failed:', error));
7054
+ });
7055
+ }
7056
+ async function syncStoredSession() {
7057
+ const config = initializedConfig;
7058
+ if (!config)
7059
+ return;
7060
+ const token = core.WebSessionManager.getIdToken();
7061
+ const issuer = core.WebSessionManager.getIssuer();
7062
+ if (token === observedIdToken && issuer === observedIssuer)
7063
+ return;
7064
+ observedIdToken = token;
7065
+ observedIssuer = issuer;
7066
+ const generation = ++initializationGeneration;
7067
+ // Hide the former principal while the replacement is validated. This also
7068
+ // reconnects live reads through the existing auth-state change path.
7069
+ setCurrentUser(null);
7070
+ if (authReadyPromise)
7071
+ await authReadyPromise.catch(() => { });
7072
+ if (generation !== initializationGeneration || config !== initializedConfig)
7073
+ return;
7074
+ const record = await core.WebSessionManager.readSessionWithGeneration();
7075
+ if (generation !== initializationGeneration || config !== initializedConfig)
7076
+ return;
7077
+ if (!record)
7078
+ return;
7079
+ const normalize = (url) => (url || '').replace(/\/+$/, '');
7080
+ const resolvedConfig = await core.getConfig();
7081
+ if (generation !== initializationGeneration || config !== initializedConfig)
7082
+ return;
7083
+ const allowedIssuers = [normalize(resolvedConfig.humanAuthApiUrl || 'https://auth.bounded.sh'), normalize(resolvedConfig.authApiUrl)].filter(Boolean);
7084
+ // A legacy shared blob can belong to another app. Never restore or clear
7085
+ // that app's credential; the current app remains signed out.
7086
+ if ((record.appId && record.appId !== config.appId)
7087
+ || (record.issuer && !allowedIssuers.includes(normalize(record.issuer))))
7088
+ return;
7089
+ if (!storedSessionAuthMethod(config))
7090
+ return;
7091
+ const restored = startInitialization(config, true);
7092
+ const restoreGeneration = initializationGeneration;
7093
+ try {
7094
+ await restored;
7095
+ }
7096
+ catch (error) {
7097
+ if (restoreGeneration === initializationGeneration && core.WebSessionManager.getIdToken())
7098
+ throw error;
7099
+ }
7100
+ }
6970
7101
  /** Internal guard for a login result arriving after the app reconfigured the SDK. */
6971
7102
  function getInitializationGeneration() { return initializationGeneration; }
6972
7103
  // This file acts as a middleware for the global state of the SDK
6973
7104
  // This mostly involves setting up the AuthProvider and managing the current user
6974
- async function initialize(requestedConfig) {
7105
+ async function initialize(requestedConfig, fromStorage) {
6975
7106
  const newConfig = withDefaultSolanaRpcUrl(requestedConfig);
7107
+ const generation = initializationGeneration;
7108
+ const validateStorage = fromStorage ? core.WebSessionManager.captureSessionEstablishment() : undefined;
7109
+ const beforeCommit = () => {
7110
+ if (generation !== initializationGeneration)
7111
+ throw new Error('Session restore was superseded.');
7112
+ validateStorage === null || validateStorage === void 0 ? void 0 : validateStorage();
7113
+ };
6976
7114
  // Mark auth as loading for the ENTIRE init/restore window so consumers render
6977
7115
  // a neutral/loading state instead of the signed-out UI while we rehydrate the
6978
7116
  // session — otherwise getCurrentUser() is null + loading is false during the
@@ -6991,8 +7129,9 @@ async function initialize(requestedConfig) {
6991
7129
  // onAuthStateChanged never fired, and every consumer UI went on showing a
6992
7130
  // connected wallet for a session the server no longer knew about.
6993
7131
  registerSessionClearedBridge();
7132
+ registerStorageBridge();
6994
7133
  // Get auth provider first
6995
- authProviderInstance = await getAuthProvider(newConfig);
7134
+ authProviderInstance = await getAuthProvider(newConfig, fromStorage);
6996
7135
  // Initialize config with auth provider
6997
7136
  await core.init(Object.assign(Object.assign({}, newConfig), { authProvider: authProviderInstance }));
6998
7137
  // A return establishes its own provider/session, or hands off a popup.
@@ -7017,7 +7156,8 @@ async function initialize(requestedConfig) {
7017
7156
  // Wait for restoreSession to complete
7018
7157
  // The appId check is done in SessionManager.getSession which is called by restoreSession
7019
7158
  // If there's an appId mismatch, the session will be cleared and restoreSession will return null
7020
- let user = await authProviderInstance.restoreSession();
7159
+ let user = await authProviderInstance.restoreSession(beforeCommit);
7160
+ beforeCommit();
7021
7161
  // If a session exists but was created before auth method tracking (legacy session),
7022
7162
  // clear it to avoid mismatches when the app switches auth methods (e.g. email → phantom)
7023
7163
  //
@@ -7025,9 +7165,10 @@ async function initialize(requestedConfig) {
7025
7165
  // unawaited call would return a Promise (always truthy) — nulling the user
7026
7166
  // unconditionally AND letting setCurrentUser run while the old credential
7027
7167
  // was still in storage.
7028
- if (user && await clearIncompatibleSession()) {
7168
+ if (user && !fromStorage && await clearIncompatibleSession()) {
7029
7169
  user = null;
7030
7170
  }
7171
+ beforeCommit();
7031
7172
  setCurrentUser(user);
7032
7173
  // Mark init as completed
7033
7174
  initCompleted = true;
@@ -7067,13 +7208,17 @@ function sameConfigValue(a, b, seen = new WeakMap()) {
7067
7208
  && sameConfigValue(a[key], b[key], seen));
7068
7209
  }
7069
7210
  function init$1(newConfig) {
7211
+ return startInitialization(newConfig, false);
7212
+ }
7213
+ function startInitialization(newConfig, fromStorage) {
7070
7214
  if (initializingConfig) {
7071
7215
  if (sameConfigValue(initializingConfig, newConfig)) {
7072
7216
  return authReadyPromise;
7073
7217
  }
7074
7218
  return Promise.reject(new Error('SDK initialization is in progress. Await init() before changing its configuration.'));
7075
7219
  }
7076
- initializingConfig = Object.assign({}, newConfig);
7220
+ initializedConfig = Object.assign({}, newConfig);
7221
+ initializingConfig = initializedConfig;
7077
7222
  initializationGeneration++;
7078
7223
  let resolveReady;
7079
7224
  let rejectReady;
@@ -7085,7 +7230,7 @@ function init$1(newConfig) {
7085
7230
  // auth-loading listener to call waitForAuthReady() synchronously when init()
7086
7231
  // announces that session restoration has begun.
7087
7232
  authReadyPromise = ready;
7088
- void initialize(initializingConfig).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
7233
+ void initialize(initializingConfig, fromStorage).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
7089
7234
  return ready;
7090
7235
  }
7091
7236
  /**
@@ -7158,6 +7303,13 @@ function userPrincipalKey(user) {
7158
7303
  }
7159
7304
  // Update the currentUser and notify listeners
7160
7305
  function setCurrentUser(user) {
7306
+ observedIdToken = getCurrentIdToken();
7307
+ try {
7308
+ observedIssuer = core.getActiveSessionManager().getIssuer();
7309
+ }
7310
+ catch (_a) {
7311
+ observedIssuer = null;
7312
+ }
7161
7313
  // Enrich with the universal identity fields (id/email) derived from the
7162
7314
  // active session's idToken — central chokepoint so every provider's user
7163
7315
  // object gains { id, address, email } without each construction site
@@ -7703,10 +7855,18 @@ async function startTurnkeyEmailLogin(email) {
7703
7855
  if (!hasDOM$1()) {
7704
7856
  throw new Error('Turnkey-native email login is web-only - on React Native use loginWithRedirect()');
7705
7857
  }
7858
+ const generation = getInitializationGeneration();
7859
+ const walletGuard = walletOperationGuard();
7860
+ const assertContext = () => {
7861
+ walletGuard();
7862
+ if (generation !== getInitializationGeneration())
7863
+ throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
7864
+ };
7706
7865
  const trimmed = email.trim();
7707
7866
  if (!trimmed)
7708
7867
  throw new Error('Enter your email to continue.');
7709
7868
  const { base, appId } = await inlineAuthEndpoint();
7869
+ assertContext();
7710
7870
  if (!appId)
7711
7871
  throw new Error('appId is not configured (init the SDK with your appId)');
7712
7872
  const res = await fetch(`${base}/wallet/turnkey/auth/init`, {
@@ -7718,6 +7878,7 @@ async function startTurnkeyEmailLogin(email) {
7718
7878
  if (!res.ok || !data || typeof data.otpId !== 'string' || typeof data.otpEncryptionTargetBundle !== 'string') {
7719
7879
  throw new Error(String((data && data.error) || 'Could not send a code. Please try again.'));
7720
7880
  }
7881
+ assertContext();
7721
7882
  const otpId = data.otpId;
7722
7883
  const otpEncryptionTargetBundle = data.otpEncryptionTargetBundle;
7723
7884
  // A tenant-origin login always gets a ceremony id; without one the OTP frame refuses to run
@@ -7733,14 +7894,20 @@ async function startTurnkeyEmailLogin(email) {
7733
7894
  : undefined;
7734
7895
  return {
7735
7896
  email: trimmed, frameUrl, signerOrigin,
7736
- run(iframe, _signal) {
7897
+ run(iframe, signal) {
7898
+ try {
7899
+ assertContext();
7900
+ }
7901
+ catch (error) {
7902
+ return Promise.reject(error);
7903
+ }
7737
7904
  if (!ceremonyId) {
7738
7905
  return Promise.reject(new Error('This app is not configured for in-frame wallet login. Please try again.'));
7739
7906
  }
7740
- const run = { iframe, base, signerOrigin, appId, email: trimmed, otpId, otpEncryptionTargetBundle, ceremonyId };
7907
+ const run = { iframe, base, signerOrigin, appId, email: trimmed, otpId, otpEncryptionTargetBundle, ceremonyId, generation };
7741
7908
  // BSA-2026-004: the login code is typed only in /wallet/turnkey/otp-frame.
7742
7909
  // The scoped signer URL is for post-login signing, not OTP entry.
7743
- return runOtpFrame(run);
7910
+ return runOtpFrame(run, signal);
7744
7911
  },
7745
7912
  };
7746
7913
  }
@@ -7749,7 +7916,7 @@ async function startTurnkeyEmailLogin(email) {
7749
7916
  // frame 'result' {session} -> we establish the inline session and resolve the User.
7750
7917
  // When the frame asks for the signing-session authorization ('need-authorization') we mint a
7751
7918
  // createWallet action authorization on THIS (registered) origin and post it in - never a key.
7752
- function runOtpFrame(run) {
7919
+ function runOtpFrame(run, signal) {
7753
7920
  const tenantOrigin = window.location.origin;
7754
7921
  const url = `${run.base.replace(/\/$/, '')}/wallet/turnkey/otp-frame`
7755
7922
  + `?appId=${encodeURIComponent(run.appId)}`
@@ -7759,13 +7926,48 @@ function runOtpFrame(run) {
7759
7926
  return new Promise((resolve, reject) => {
7760
7927
  let settled = false;
7761
7928
  let started = false;
7762
- const cleanup = () => { settled = true; window.removeEventListener('message', onMsg); window.clearTimeout(timer); };
7929
+ let receivingResult = false;
7930
+ let committing = false;
7931
+ const walletGuard = walletLoginGuard(signal);
7932
+ const beforeCommit = () => {
7933
+ if (settled)
7934
+ throw new Error('wallet_login_cancelled');
7935
+ walletGuard();
7936
+ if (run.generation !== getInitializationGeneration())
7937
+ throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
7938
+ };
7939
+ const finish = (error, user) => {
7940
+ if (settled)
7941
+ return;
7942
+ settled = true;
7943
+ window.removeEventListener('message', onMsg);
7944
+ window.clearTimeout(timer);
7945
+ signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
7946
+ if (error) {
7947
+ // Destroy the expired frame's code/key realm as well as its parent listener.
7948
+ run.iframe.src = 'about:blank';
7949
+ reject(error);
7950
+ }
7951
+ else
7952
+ resolve(user);
7953
+ };
7954
+ const onAbort = () => { if (!committing)
7955
+ finish(new Error('cancelled')); };
7763
7956
  const onMsg = async (event) => {
7764
7957
  if (settled)
7765
7958
  return;
7766
7959
  // Trust ONLY the signer origin and the frame's own window.
7767
7960
  if (event.origin !== run.signerOrigin || event.source !== run.iframe.contentWindow)
7768
7961
  return;
7962
+ if (!committing) {
7963
+ try {
7964
+ beforeCommit();
7965
+ }
7966
+ catch (error) {
7967
+ finish(error instanceof Error ? error : new Error(String(error)));
7968
+ return;
7969
+ }
7970
+ }
7769
7971
  const d = event.data;
7770
7972
  if (!d || typeof d !== 'object')
7771
7973
  return;
@@ -7808,40 +8010,56 @@ function runOtpFrame(run) {
7808
8010
  return;
7809
8011
  }
7810
8012
  if (d.type === 'bounded:otp:result') {
8013
+ if (receivingResult)
8014
+ return;
8015
+ receivingResult = true;
7811
8016
  if (d.ok && d.session && typeof d.session === 'object') {
7812
8017
  try {
7813
- const user = await establishInlineSession(d.session);
7814
- cleanup();
7815
- resolve(user);
8018
+ const user = await establishInlineSession(d.session, beforeCommit, undefined, () => { committing = true; });
8019
+ finish(undefined, user);
7816
8020
  }
7817
8021
  catch (error) {
7818
- cleanup();
7819
- reject(error instanceof Error ? error : new Error(String(error)));
8022
+ finish(error instanceof Error ? error : new Error(String(error)));
7820
8023
  }
7821
8024
  return;
7822
8025
  }
7823
- cleanup();
7824
- reject(new Error(String((d && d.error) || 'Invalid or expired code.')));
8026
+ finish(new Error(String((d && d.error) || 'Invalid or expired code.')));
7825
8027
  return;
7826
8028
  }
7827
8029
  // 'bounded:otp:registered' is advisory; login does not depend on it.
7828
8030
  };
7829
8031
  const post = (msg) => {
7830
8032
  var _a;
8033
+ if (settled)
8034
+ return;
8035
+ if (!committing) {
8036
+ try {
8037
+ beforeCommit();
8038
+ }
8039
+ catch (_b) {
8040
+ return;
8041
+ }
8042
+ }
7831
8043
  try {
7832
8044
  (_a = run.iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(msg, run.signerOrigin);
7833
8045
  }
7834
- catch ( /* frame gone */_b) { /* frame gone */ }
8046
+ catch ( /* frame gone */_c) { /* frame gone */ }
7835
8047
  };
7836
8048
  window.addEventListener('message', onMsg);
7837
8049
  // 3 minutes: the human must read the email and type the code. A frame that never reports is a
7838
8050
  // failed login the user can retry.
7839
8051
  const timer = window.setTimeout(() => {
7840
- if (settled)
7841
- return;
7842
- cleanup();
7843
- reject(new Error('The sign-in code entry timed out. Please try again.'));
8052
+ if (!committing)
8053
+ finish(new Error('The sign-in code entry timed out. Please try again.'));
7844
8054
  }, 3 * 60 * 1000);
8055
+ signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
8056
+ try {
8057
+ beforeCommit();
8058
+ }
8059
+ catch (error) {
8060
+ finish(error instanceof Error ? error : new Error(String(error)));
8061
+ return;
8062
+ }
7845
8063
  run.iframe.src = url;
7846
8064
  });
7847
8065
  }
@@ -8105,7 +8323,8 @@ async function prepareAndOpen(opts, pending) {
8105
8323
  return authApi.loginWithRedirect(options);
8106
8324
  // Invoke immediately inside the click handler to retain popup activation.
8107
8325
  return authApi.loginWithPopup(options).catch((error) => {
8108
- if (shouldFallbackToRedirect(error))
8326
+ var _a;
8327
+ if (!((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) && shouldFallbackToRedirect(error))
8109
8328
  return authApi.loginWithRedirect(options);
8110
8329
  throw error;
8111
8330
  });
@@ -8243,6 +8462,12 @@ async function prepareAndOpen(opts, pending) {
8243
8462
  let activeAttempt = 0;
8244
8463
  let abortScopedLogin = null;
8245
8464
  let abortGesture = null;
8465
+ const startHostedLogin = (options) => {
8466
+ const controller = new AbortController();
8467
+ const abort = () => controller.abort();
8468
+ abortScopedLogin = abort;
8469
+ return hostedLogin(Object.assign(Object.assign({}, options), { signal: controller.signal }));
8470
+ };
8246
8471
  /** Reject a gesture promise already waiting on a view that is going away. */
8247
8472
  const abortPendingGesture = () => {
8248
8473
  const abort = abortGesture;
@@ -8268,6 +8493,7 @@ async function prepareAndOpen(opts, pending) {
8268
8493
  if (!attemptIsCurrent(id))
8269
8494
  return false;
8270
8495
  activeAttempt = 0;
8496
+ abortScopedLogin = null;
8271
8497
  return true;
8272
8498
  };
8273
8499
  /** Take the card back (Back, a lane switch, settling the modal). Deliberately
@@ -8422,7 +8648,7 @@ async function prepareAndOpen(opts, pending) {
8422
8648
  loginHint: input.value,
8423
8649
  redirectUri: opts.redirectUri,
8424
8650
  };
8425
- hostedLogin(emailOpts)
8651
+ startHostedLogin(emailOpts)
8426
8652
  // A hosted login that RESOLVES minted a real session, so it settles
8427
8653
  // the card even if another lane has since taken it - dropping it
8428
8654
  // would leave the app signed in behind a widget that says it is not.
@@ -8472,7 +8698,7 @@ async function prepareAndOpen(opts, pending) {
8472
8698
  }
8473
8699
  setBusy(b, "Opening secure sign-in\u2026");
8474
8700
  const socialOpts = { provider: method, redirectUri: opts.redirectUri };
8475
- hostedLogin(socialOpts)
8701
+ startHostedLogin(socialOpts)
8476
8702
  .then((u) => {
8477
8703
  if (u) {
8478
8704
  settleAttempt(attempt);
@@ -8722,14 +8948,16 @@ async function prepareAndOpen(opts, pending) {
8722
8948
  setErr(BUSY_MSG);
8723
8949
  return card;
8724
8950
  }
8725
- back.addEventListener("click", () => { settleAttempt(attempt); invalidateAttempts(); swap(renderChoose()); });
8951
+ back.addEventListener("click", () => { invalidateAttempts(); swap(renderChoose()); });
8952
+ const controller = new AbortController();
8953
+ abortScopedLogin = () => controller.abort();
8726
8954
  handle
8727
- .run(frame)
8955
+ .run(frame, controller.signal)
8728
8956
  .then((u) => { settleAttempt(attempt); succeed(u); })
8729
8957
  .catch((err) => {
8730
8958
  if (!settleAttempt(attempt))
8731
8959
  return;
8732
- setErr(errText(err));
8960
+ swap(renderChoose(errText(err)));
8733
8961
  });
8734
8962
  return card;
8735
8963
  };
@@ -10544,8 +10772,9 @@ class PrivyExpoProvider {
10544
10772
  const { signature } = await this.signAndSubmitInternal(transaction, wallet, connection);
10545
10773
  return signature;
10546
10774
  }
10547
- async restoreSession() {
10775
+ async restoreSession(beforeCommit) {
10548
10776
  const session = await core.getActiveSessionManager().getSession();
10777
+ beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit();
10549
10778
  if (session) {
10550
10779
  this.authenticatedAddress = session.address;
10551
10780
  return { provider: this, address: session.address };
@@ -11130,4 +11359,4 @@ exports.useAuth = useAuth;
11130
11359
  exports.useBuildRun = useBuildRun;
11131
11360
  exports.useQuery = useQuery;
11132
11361
  exports.waitForAuthReady = waitForAuthReady;
11133
- //# sourceMappingURL=index-BghQ8jX0.js.map
11362
+ //# sourceMappingURL=index-DJeEMevo.js.map