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

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 +2 -0
  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-CVp6hSCo.mjs → email-auth-provider-BfCH7K6N.mjs} +4 -3
  10. package/dist/email-auth-provider-BfCH7K6N.mjs.map +1 -0
  11. package/dist/{email-auth-provider-DxS2WYxM.js → email-auth-provider-CG5cxOj0.js} +4 -3
  12. package/dist/email-auth-provider-CG5cxOj0.js.map +1 -0
  13. package/dist/{email-auth-provider-CiqhQ1c2.js → email-auth-provider-DTrCfOJd.js} +4 -3
  14. package/dist/email-auth-provider-DTrCfOJd.js.map +1 -0
  15. package/dist/{email-auth-provider-BfBvF_4M.mjs → email-auth-provider-eyi3Wpnx.mjs} +4 -3
  16. package/dist/email-auth-provider-eyi3Wpnx.mjs.map +1 -0
  17. package/dist/{guest-auth-provider-Bm_yqQF6.mjs → guest-auth-provider-BsW56oJg.mjs} +10 -5
  18. package/dist/guest-auth-provider-BsW56oJg.mjs.map +1 -0
  19. package/dist/{guest-auth-provider-Cc8y4Qd1.mjs → guest-auth-provider-DjOFjtnk.mjs} +10 -5
  20. package/dist/guest-auth-provider-DjOFjtnk.mjs.map +1 -0
  21. package/dist/{guest-auth-provider-DDBXeHoo.js → guest-auth-provider-Dtl1J-2F.js} +10 -5
  22. package/dist/guest-auth-provider-Dtl1J-2F.js.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-BghQ8jX0.js → index-Brdwqgqs.js} +213 -60
  26. package/dist/index-Brdwqgqs.js.map +1 -0
  27. package/dist/{index-B9__gwTi.mjs → index-CRxls8Qc.mjs} +214 -61
  28. package/dist/index-CRxls8Qc.mjs.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-BjkCeRBL.mjs} +4 -3
  38. package/dist/injected-evm-wallet-provider-BjkCeRBL.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-Ckb85p6a.js → injected-evm-wallet-provider-CwOGXCDt.js} +4 -3
  42. package/dist/injected-evm-wallet-provider-CwOGXCDt.js.map +1 -0
  43. package/dist/{injected-evm-wallet-provider-BUjqxQxp.mjs → injected-evm-wallet-provider-DXjwPjLW.mjs} +4 -3
  44. package/dist/{injected-evm-wallet-provider-BUjqxQxp.mjs.map → injected-evm-wallet-provider-DXjwPjLW.mjs.map} +1 -1
  45. package/dist/{injected-wallet-provider-BlzA0J_V.mjs → injected-wallet-provider-BG6gGNch.mjs} +4 -3
  46. package/dist/{injected-wallet-provider-BlzA0J_V.mjs.map → injected-wallet-provider-BG6gGNch.mjs.map} +1 -1
  47. package/dist/{injected-wallet-provider-BEuj4sM3.mjs → injected-wallet-provider-CfYNIIHd.mjs} +4 -3
  48. package/dist/{injected-wallet-provider-BEuj4sM3.mjs.map → injected-wallet-provider-CfYNIIHd.mjs.map} +1 -1
  49. package/dist/{injected-wallet-provider-CUKdAuFu.js → injected-wallet-provider-DSlHfgMU.js} +4 -3
  50. package/dist/{injected-wallet-provider-CUKdAuFu.js.map → injected-wallet-provider-DSlHfgMU.js.map} +1 -1
  51. package/dist/{injected-wallet-provider-Cfb4ayCP.js → injected-wallet-provider-LTnqoaYD.js} +4 -3
  52. package/dist/{injected-wallet-provider-Cfb4ayCP.js.map → injected-wallet-provider-LTnqoaYD.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
@@ -277,8 +277,9 @@ class OffchainAuthProvider {
277
277
  async logout() {
278
278
  await this.wrappedProvider.logout();
279
279
  }
280
- async restoreSession() {
281
- const user = await this.wrappedProvider.restoreSession();
280
+ async restoreSession(beforeCommit) {
281
+ const provider = this.wrappedProvider;
282
+ const user = await provider.restoreSession(beforeCommit);
282
283
  if (user) {
283
284
  return Object.assign(Object.assign({}, user), { provider: this });
284
285
  }
@@ -5532,7 +5533,8 @@ async function loginWithRedirect(opts) {
5532
5533
  return runNativeLogin(opts);
5533
5534
  if (!hasDOM$2())
5534
5535
  throw new Error('loginWithRedirect requires a browser or a configured React Native platform');
5535
- const { url, pkce } = await buildAuthorize(opts);
5536
+ const { url, pkce } = await untilAborted(buildAuthorize(opts), opts.signal);
5537
+ throwIfCancelled(opts.signal);
5536
5538
  // Stash the verifier + state so the redirect_uri page can finish the exchange. sessionStorage
5537
5539
  // is per-tab and cleared on close — appropriate for a short-lived auth handshake. Keyed by
5538
5540
  // this attempt's own state so a second concurrent login cannot overwrite it.
@@ -5561,9 +5563,29 @@ async function completeLoginFromRedirect() {
5561
5563
  throw error;
5562
5564
  return result.user;
5563
5565
  }
5566
+ function throwIfCancelled(signal) {
5567
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
5568
+ throw new Error('cancelled');
5569
+ }
5570
+ async function untilAborted(work, signal) {
5571
+ if (!signal)
5572
+ return work;
5573
+ throwIfCancelled(signal);
5574
+ let onAbort;
5575
+ try {
5576
+ return await Promise.race([work, new Promise((_, reject) => {
5577
+ onAbort = () => reject(new Error('cancelled'));
5578
+ signal.addEventListener('abort', onAbort, { once: true });
5579
+ })]);
5580
+ }
5581
+ finally {
5582
+ signal.removeEventListener('abort', onAbort);
5583
+ }
5584
+ }
5564
5585
  /** Popup variant: open the hosted login in a popup; resolves with the User once the popup's
5565
5586
  * redirect_uri page initializes the SDK, which hands the code back automatically. */
5566
5587
  async function loginWithPopup(opts) {
5588
+ throwIfCancelled(opts.signal);
5567
5589
  if (!hasDOM$2())
5568
5590
  throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
5569
5591
  // Open synchronously, before PKCE/config awaits consume the click's user-
@@ -5581,7 +5603,7 @@ async function loginWithPopup(opts) {
5581
5603
  throw new Error('Popup blocked — use loginWithRedirect() instead');
5582
5604
  let authorization;
5583
5605
  try {
5584
- authorization = await buildAuthorize(opts, 'popup');
5606
+ authorization = await untilAborted(buildAuthorize(opts, 'popup'), opts.signal);
5585
5607
  }
5586
5608
  catch (error) {
5587
5609
  try {
@@ -5598,6 +5620,7 @@ async function loginWithPopup(opts) {
5598
5620
  // window with no listener or watchdog attached.
5599
5621
  let expectedOrigin;
5600
5622
  try {
5623
+ throwIfCancelled(opts.signal);
5601
5624
  expectedOrigin = new URL(pkce.redirectUri).origin;
5602
5625
  sessionStorage.setItem(pkceKey(pkce.state), JSON.stringify(pkce));
5603
5626
  }
@@ -5629,20 +5652,42 @@ async function loginWithPopup(opts) {
5629
5652
  setAuthLoading(true); // reflect "signing in…" while the popup is open
5630
5653
  try {
5631
5654
  return await new Promise((resolve, reject) => {
5655
+ var _a, _b;
5632
5656
  let settled = false;
5657
+ let handedOff = false;
5658
+ let committing = false;
5633
5659
  let timer;
5634
5660
  let navigationTimer;
5635
5661
  let completionTimer;
5636
5662
  const cleanup = () => {
5637
- settled = true;
5638
5663
  clearInterval(timer);
5639
5664
  clearTimeout(navigationTimer);
5640
5665
  clearTimeout(completionTimer);
5641
5666
  window.removeEventListener('message', onMsg);
5642
5667
  removeAttempt(pkce.state);
5643
5668
  };
5669
+ const finish = (error, user) => {
5670
+ var _a;
5671
+ if (settled)
5672
+ return;
5673
+ settled = true;
5674
+ cleanup();
5675
+ (_a = opts.signal) === null || _a === void 0 ? void 0 : _a.removeEventListener('abort', onAbort);
5676
+ try {
5677
+ popup.close();
5678
+ }
5679
+ catch ( /* ignore */_b) { /* ignore */ }
5680
+ if (error)
5681
+ reject(error);
5682
+ else
5683
+ resolve(user);
5684
+ };
5685
+ // Provider adoption commits synchronously after its final guard. An auth
5686
+ // listener dismissing the card there cannot cancel an already signed-in user.
5687
+ const onAbort = () => { if (!committing)
5688
+ finish(new Error('cancelled')); };
5644
5689
  const onMsg = async (ev) => {
5645
- if (settled || ev.origin !== expectedOrigin || ev.source !== popup)
5690
+ if (settled || handedOff || ev.origin !== expectedOrigin || ev.source !== popup)
5646
5691
  return;
5647
5692
  const d = ev.data;
5648
5693
  if (!d || d.type !== 'bounded-oidc-code' ||
@@ -5656,35 +5701,30 @@ async function loginWithPopup(opts) {
5656
5701
  return;
5657
5702
  if (d.appId !== pkce.appId || d.issuer !== pkce.issuer)
5658
5703
  return;
5704
+ handedOff = true;
5659
5705
  cleanup();
5660
5706
  try {
5707
+ throwIfCancelled(opts.signal);
5661
5708
  await validateAttempt(pkce, new URL(d.callbackUrl));
5709
+ throwIfCancelled(opts.signal);
5662
5710
  popup.postMessage({ type: 'bounded-oidc-accepted', state: pkce.state }, expectedOrigin);
5663
5711
  if (d.error)
5664
5712
  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);
5713
+ const user = await exchangeCode(d.code, pkce, opts.signal, () => { committing = true; });
5714
+ finish(undefined, user);
5671
5715
  }
5672
5716
  catch (e) {
5673
- try {
5674
- popup.close();
5675
- }
5676
- catch ( /* ignore */_b) { /* ignore */ }
5677
- reject(e);
5717
+ finish(e);
5678
5718
  }
5679
5719
  };
5720
+ (_a = opts.signal) === null || _a === void 0 ? void 0 : _a.addEventListener('abort', onAbort, { once: true });
5721
+ if ((_b = opts.signal) === null || _b === void 0 ? void 0 : _b.aborted) {
5722
+ onAbort();
5723
+ return;
5724
+ }
5680
5725
  window.addEventListener('message', onMsg);
5681
5726
  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.'));
5727
+ finish(new Error('Login expired or its popup disconnected. Start sign-in again.'));
5688
5728
  }, LOGIN_TIMEOUT_MS);
5689
5729
  // Some browser/extension combinations can return a real popup handle but
5690
5730
  // strand the window at about:blank instead of navigating to the hosted
@@ -5705,23 +5745,18 @@ async function loginWithPopup(opts) {
5705
5745
  const NAVIGATION_RECHECK_MS = 2000;
5706
5746
  const navigationStartedAt = Date.now();
5707
5747
  const checkNavigation = () => {
5708
- if (settled || popup.closed)
5748
+ if (settled || handedOff || popup.closed)
5709
5749
  return;
5710
5750
  try {
5711
5751
  if (popup.location.href === 'about:blank') {
5712
5752
  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'));
5753
+ finish(new Error('Popup stalled at about:blank - use loginWithRedirect() instead'));
5719
5754
  return;
5720
5755
  }
5721
5756
  navigationTimer = setTimeout(checkNavigation, NAVIGATION_RECHECK_MS);
5722
5757
  }
5723
5758
  }
5724
- catch ( /* cross-origin hosted auth loaded normally */_b) { /* cross-origin hosted auth loaded normally */ }
5759
+ catch ( /* cross-origin hosted auth loaded normally */_a) { /* cross-origin hosted auth loaded normally */ }
5725
5760
  };
5726
5761
  navigationTimer = setTimeout(checkNavigation, NAVIGATION_RECHECK_MS);
5727
5762
  // The popup posts its code and then closes ITSELF, so a naive "popup.closed -> reject"
@@ -5731,10 +5766,8 @@ async function loginWithPopup(opts) {
5731
5766
  timer = setInterval(() => {
5732
5767
  if (popup.closed) {
5733
5768
  clearInterval(timer);
5734
- setTimeout(() => { if (!settled) {
5735
- cleanup();
5736
- reject(new Error('Login window closed'));
5737
- } }, 500);
5769
+ setTimeout(() => { if (!settled && !handedOff)
5770
+ finish(new Error('Login window closed')); }, 500);
5738
5771
  }
5739
5772
  }, 500);
5740
5773
  });
@@ -5785,9 +5818,10 @@ async function inlineAuthEndpoint() {
5785
5818
  const cfg = await core.getConfig();
5786
5819
  return { base: await issuerBase(), appId: cfg.appId };
5787
5820
  }
5788
- async function exchangeCode(code, pkce) {
5821
+ async function exchangeCode(code, pkce, signal, onCommitting) {
5789
5822
  const generation = getInitializationGeneration();
5790
5823
  const beforeCommit = () => {
5824
+ throwIfCancelled(signal);
5791
5825
  if (generation !== getInitializationGeneration())
5792
5826
  throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
5793
5827
  };
@@ -5803,7 +5837,7 @@ async function exchangeCode(code, pkce) {
5803
5837
  let res;
5804
5838
  try {
5805
5839
  res = await fetch(`${base}/api/auth/oauth2/token`, {
5806
- method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString(),
5840
+ method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString(), signal,
5807
5841
  });
5808
5842
  }
5809
5843
  catch (err) {
@@ -5829,7 +5863,7 @@ async function exchangeCode(code, pkce) {
5829
5863
  // Adopt on EVERY session surface (see establishInlineSession) - hosted
5830
5864
  // popup/redirect completions all land here, so this closes their gap too.
5831
5865
  try {
5832
- await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit });
5866
+ await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit: () => { beforeCommit(); onCommitting === null || onCommitting === void 0 ? void 0 : onCommitting(); } });
5833
5867
  }
5834
5868
  catch (error) {
5835
5869
  if (typeof rollback === 'function')
@@ -5898,6 +5932,31 @@ function idTokenIsGuest(idToken, expectedAddress) {
5898
5932
  function storedSessionIsGuest() {
5899
5933
  return idTokenIsGuest(getStoredIdToken());
5900
5934
  }
5935
+ /** Internal: select a cross-tab restore from bearer claims, never global hints. */
5936
+ function storedSessionAuthMethod(config) {
5937
+ try {
5938
+ const token = getStoredIdToken();
5939
+ if (!token)
5940
+ return null;
5941
+ let payload = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
5942
+ payload += '='.repeat((4 - payload.length % 4) % 4);
5943
+ const claims = JSON.parse(getPlatform().atob(payload));
5944
+ const method = claims.is_anonymous === true ? 'guest'
5945
+ : claims['custom:walletProvider'] === 'turnkey' || claims.email ? 'email'
5946
+ : claims['custom:evmWalletAddress'] ? 'evm-wallet'
5947
+ : claims['custom:walletAddress'] ? 'phantom' : 'email';
5948
+ if (config.authMethod != null && canonicalWalletAuthMethod(config.authMethod) !== method)
5949
+ return null;
5950
+ if (method === 'evm-wallet' && config.authMethod !== 'evm-wallet')
5951
+ return null;
5952
+ if (method === 'phantom' && (isWalletLoginDisabled(config) || isEmailRequired(config)))
5953
+ return null;
5954
+ return method;
5955
+ }
5956
+ catch (_a) {
5957
+ return null;
5958
+ }
5959
+ }
5901
5960
  /**
5902
5961
  * Pick the provider used for an ordinary `init({ appId })` restore.
5903
5962
  *
@@ -6159,9 +6218,10 @@ function optionalProviderError(method) {
6159
6218
  `loginWithRedirect(...) for Bounded Auth, signInAnonymously() for guests, ` +
6160
6219
  `or install/import an explicit wallet-provider entry once your app opts into "${method}".`);
6161
6220
  }
6162
- async function getAuthProvider(config) {
6221
+ async function getAuthProvider(config, fromStorage = false) {
6163
6222
  var _a;
6164
- const nextProviderConfigKey = config ? authProviderConfigKey(config) : null;
6223
+ const requestedAuthMethod = config ? (fromStorage ? storedSessionAuthMethod(config) : authMethodForInit(config)) : null;
6224
+ const nextProviderConfigKey = config ? authProviderConfigKey(Object.assign(Object.assign({}, config), { authMethod: requestedAuthMethod })) : null;
6165
6225
  if (currentAuthProvider && (!config || currentAuthProviderConfigKey === nextProviderConfigKey)) {
6166
6226
  return currentAuthProvider;
6167
6227
  }
@@ -6176,7 +6236,8 @@ async function getAuthProvider(config) {
6176
6236
  initConfig = config;
6177
6237
  // Keep what the app actually wrote for error text; everything else runs on
6178
6238
  // the canonical method so exactly one string reaches storage and the issuer.
6179
- const requestedAuthMethod = authMethodForInit(config);
6239
+ if (!requestedAuthMethod)
6240
+ throw new Error('The stored session is incompatible with this app authentication configuration.');
6180
6241
  const authMethod = canonicalWalletAuthMethod(requestedAuthMethod);
6181
6242
  const rpcUrl = normalizeSolanaRpcUrl(config.rpcUrl);
6182
6243
  const configuredNetwork = getConfiguredSolanaNetwork(config);
@@ -6201,7 +6262,7 @@ async function getAuthProvider(config) {
6201
6262
  // manager as legacy email sessions. Human login should be started
6202
6263
  // with loginWithRedirect(); this provider is kept as the restore and
6203
6264
  // logout surface for those sessions.
6204
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-DxS2WYxM.js'); })).EmailAuthProvider();
6265
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-CG5cxOj0.js'); })).EmailAuthProvider();
6205
6266
  break;
6206
6267
  case "phantom": {
6207
6268
  // First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
@@ -6243,7 +6304,7 @@ async function getAuthProvider(config) {
6243
6304
  // (bounded-login-widget.ts), so registration never counts as
6244
6305
  // evidence the user has a wallet.
6245
6306
  await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
6246
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-Cfb4ayCP.js'); });
6307
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-LTnqoaYD.js'); });
6247
6308
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
6248
6309
  break;
6249
6310
  }
@@ -6256,7 +6317,7 @@ async function getAuthProvider(config) {
6256
6317
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
6257
6318
  // to point at a specific wallet or set chainId/domain.
6258
6319
  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'); });
6320
+ const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-CwOGXCDt.js'); });
6260
6321
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
6261
6322
  break;
6262
6323
  }
@@ -6281,7 +6342,7 @@ async function getAuthProvider(config) {
6281
6342
  break;
6282
6343
  case "guest":
6283
6344
  // 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();
6345
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-Dtl1J-2F.js'); })).GuestAuthProvider();
6285
6346
  break;
6286
6347
  }
6287
6348
  if (!currentAuthProvider) {
@@ -6343,7 +6404,7 @@ async function signInAnonymously() {
6343
6404
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
6344
6405
  // the main bundle for data-only apps. Specifier is a static string literal
6345
6406
  // so bundle-splitting can see it.
6346
- const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-DDBXeHoo.js'); })).GuestAuthProvider();
6407
+ const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-Dtl1J-2F.js'); })).GuestAuthProvider();
6347
6408
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
6348
6409
  // adoptSessionProvider awaits only getConfig() (already resolved here) and then
6349
6410
  // runs every write - provider/method/config/marker/user - in one synchronous
@@ -6514,7 +6575,7 @@ function ensureSharedWalletLoginReady() {
6514
6575
  if (!sharedWalletReady) {
6515
6576
  sharedWalletReady = (async () => {
6516
6577
  await core.getConfig();
6517
- await Promise.resolve().then(function () { return require('./injected-wallet-provider-Cfb4ayCP.js'); });
6578
+ await Promise.resolve().then(function () { return require('./injected-wallet-provider-LTnqoaYD.js'); });
6518
6579
  })().catch((error) => {
6519
6580
  sharedWalletReady = null;
6520
6581
  throw error;
@@ -6617,7 +6678,7 @@ async function loginWithWallet(options) {
6617
6678
  await ensureSharedWalletLoginReady();
6618
6679
  else
6619
6680
  await ensureWalletLoginReady();
6620
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-Cfb4ayCP.js'); });
6681
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-LTnqoaYD.js'); });
6621
6682
  // Keep the RAW wallet provider: the offchain wrapper forwards only a fixed
6622
6683
  // set of methods, so cleanup below must speak to the provider that actually
6623
6684
  // holds the config rather than to whatever is wrapping it.
@@ -6889,7 +6950,8 @@ var index = /*#__PURE__*/Object.freeze({
6889
6950
  logout: logout$1,
6890
6951
  preloadWalletLogin: preloadWalletLogin,
6891
6952
  resolveAuthMode: resolveAuthMode,
6892
- signInAnonymously: signInAnonymously
6953
+ signInAnonymously: signInAnonymously,
6954
+ storedSessionAuthMethod: storedSessionAuthMethod
6893
6955
  });
6894
6956
 
6895
6957
  /**
@@ -6967,12 +7029,80 @@ let isAuthLoading = false;
6967
7029
  let authReadyPromise = null;
6968
7030
  let initializingConfig = null;
6969
7031
  let initializationGeneration = 0;
7032
+ let initializedConfig = null;
7033
+ let storageBridgeRegistered = false;
7034
+ let observedIdToken = null;
7035
+ let observedIssuer = null;
7036
+ // Storage events carry no authority. Re-read the active manager's app/issuer
7037
+ // namespace, then use the ordinary initializer/provider restore and listeners.
7038
+ function registerStorageBridge() {
7039
+ if (storageBridgeRegistered || !getPlatform().hasDOM || typeof window === 'undefined')
7040
+ return;
7041
+ storageBridgeRegistered = true;
7042
+ window.addEventListener('storage', (event) => {
7043
+ if (event.storageArea !== window.localStorage || !initializedConfig)
7044
+ return;
7045
+ void syncStoredSession().catch((error) => console.warn('Bounded session synchronization failed:', error));
7046
+ });
7047
+ }
7048
+ async function syncStoredSession() {
7049
+ const config = initializedConfig;
7050
+ if (!config)
7051
+ return;
7052
+ const token = core.WebSessionManager.getIdToken();
7053
+ const issuer = core.WebSessionManager.getIssuer();
7054
+ if (token === observedIdToken && issuer === observedIssuer)
7055
+ return;
7056
+ observedIdToken = token;
7057
+ observedIssuer = issuer;
7058
+ const generation = ++initializationGeneration;
7059
+ // Hide the former principal while the replacement is validated. This also
7060
+ // reconnects live reads through the existing auth-state change path.
7061
+ setCurrentUser(null);
7062
+ if (authReadyPromise)
7063
+ await authReadyPromise.catch(() => { });
7064
+ if (generation !== initializationGeneration || config !== initializedConfig)
7065
+ return;
7066
+ const record = await core.WebSessionManager.readSessionWithGeneration();
7067
+ if (generation !== initializationGeneration || config !== initializedConfig)
7068
+ return;
7069
+ if (!record)
7070
+ return;
7071
+ const normalize = (url) => (url || '').replace(/\/+$/, '');
7072
+ const resolvedConfig = await core.getConfig();
7073
+ if (generation !== initializationGeneration || config !== initializedConfig)
7074
+ return;
7075
+ const allowedIssuers = [normalize(resolvedConfig.humanAuthApiUrl || 'https://auth.bounded.sh'), normalize(resolvedConfig.authApiUrl)].filter(Boolean);
7076
+ // A legacy shared blob can belong to another app. Never restore or clear
7077
+ // that app's credential; the current app remains signed out.
7078
+ if ((record.appId && record.appId !== config.appId)
7079
+ || (record.issuer && !allowedIssuers.includes(normalize(record.issuer))))
7080
+ return;
7081
+ if (!storedSessionAuthMethod(config))
7082
+ return;
7083
+ const restored = startInitialization(config, true);
7084
+ const restoreGeneration = initializationGeneration;
7085
+ try {
7086
+ await restored;
7087
+ }
7088
+ catch (error) {
7089
+ if (restoreGeneration === initializationGeneration && core.WebSessionManager.getIdToken())
7090
+ throw error;
7091
+ }
7092
+ }
6970
7093
  /** Internal guard for a login result arriving after the app reconfigured the SDK. */
6971
7094
  function getInitializationGeneration() { return initializationGeneration; }
6972
7095
  // This file acts as a middleware for the global state of the SDK
6973
7096
  // This mostly involves setting up the AuthProvider and managing the current user
6974
- async function initialize(requestedConfig) {
7097
+ async function initialize(requestedConfig, fromStorage) {
6975
7098
  const newConfig = withDefaultSolanaRpcUrl(requestedConfig);
7099
+ const generation = initializationGeneration;
7100
+ const validateStorage = fromStorage ? core.WebSessionManager.captureSessionEstablishment() : undefined;
7101
+ const beforeCommit = () => {
7102
+ if (generation !== initializationGeneration)
7103
+ throw new Error('Session restore was superseded.');
7104
+ validateStorage === null || validateStorage === void 0 ? void 0 : validateStorage();
7105
+ };
6976
7106
  // Mark auth as loading for the ENTIRE init/restore window so consumers render
6977
7107
  // a neutral/loading state instead of the signed-out UI while we rehydrate the
6978
7108
  // session — otherwise getCurrentUser() is null + loading is false during the
@@ -6991,8 +7121,9 @@ async function initialize(requestedConfig) {
6991
7121
  // onAuthStateChanged never fired, and every consumer UI went on showing a
6992
7122
  // connected wallet for a session the server no longer knew about.
6993
7123
  registerSessionClearedBridge();
7124
+ registerStorageBridge();
6994
7125
  // Get auth provider first
6995
- authProviderInstance = await getAuthProvider(newConfig);
7126
+ authProviderInstance = await getAuthProvider(newConfig, fromStorage);
6996
7127
  // Initialize config with auth provider
6997
7128
  await core.init(Object.assign(Object.assign({}, newConfig), { authProvider: authProviderInstance }));
6998
7129
  // A return establishes its own provider/session, or hands off a popup.
@@ -7017,7 +7148,8 @@ async function initialize(requestedConfig) {
7017
7148
  // Wait for restoreSession to complete
7018
7149
  // The appId check is done in SessionManager.getSession which is called by restoreSession
7019
7150
  // If there's an appId mismatch, the session will be cleared and restoreSession will return null
7020
- let user = await authProviderInstance.restoreSession();
7151
+ let user = await authProviderInstance.restoreSession(beforeCommit);
7152
+ beforeCommit();
7021
7153
  // If a session exists but was created before auth method tracking (legacy session),
7022
7154
  // clear it to avoid mismatches when the app switches auth methods (e.g. email → phantom)
7023
7155
  //
@@ -7025,9 +7157,10 @@ async function initialize(requestedConfig) {
7025
7157
  // unawaited call would return a Promise (always truthy) — nulling the user
7026
7158
  // unconditionally AND letting setCurrentUser run while the old credential
7027
7159
  // was still in storage.
7028
- if (user && await clearIncompatibleSession()) {
7160
+ if (user && !fromStorage && await clearIncompatibleSession()) {
7029
7161
  user = null;
7030
7162
  }
7163
+ beforeCommit();
7031
7164
  setCurrentUser(user);
7032
7165
  // Mark init as completed
7033
7166
  initCompleted = true;
@@ -7067,13 +7200,17 @@ function sameConfigValue(a, b, seen = new WeakMap()) {
7067
7200
  && sameConfigValue(a[key], b[key], seen));
7068
7201
  }
7069
7202
  function init$1(newConfig) {
7203
+ return startInitialization(newConfig, false);
7204
+ }
7205
+ function startInitialization(newConfig, fromStorage) {
7070
7206
  if (initializingConfig) {
7071
7207
  if (sameConfigValue(initializingConfig, newConfig)) {
7072
7208
  return authReadyPromise;
7073
7209
  }
7074
7210
  return Promise.reject(new Error('SDK initialization is in progress. Await init() before changing its configuration.'));
7075
7211
  }
7076
- initializingConfig = Object.assign({}, newConfig);
7212
+ initializedConfig = Object.assign({}, newConfig);
7213
+ initializingConfig = initializedConfig;
7077
7214
  initializationGeneration++;
7078
7215
  let resolveReady;
7079
7216
  let rejectReady;
@@ -7085,7 +7222,7 @@ function init$1(newConfig) {
7085
7222
  // auth-loading listener to call waitForAuthReady() synchronously when init()
7086
7223
  // announces that session restoration has begun.
7087
7224
  authReadyPromise = ready;
7088
- void initialize(initializingConfig).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
7225
+ void initialize(initializingConfig, fromStorage).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
7089
7226
  return ready;
7090
7227
  }
7091
7228
  /**
@@ -7158,6 +7295,13 @@ function userPrincipalKey(user) {
7158
7295
  }
7159
7296
  // Update the currentUser and notify listeners
7160
7297
  function setCurrentUser(user) {
7298
+ observedIdToken = getCurrentIdToken();
7299
+ try {
7300
+ observedIssuer = core.getActiveSessionManager().getIssuer();
7301
+ }
7302
+ catch (_a) {
7303
+ observedIssuer = null;
7304
+ }
7161
7305
  // Enrich with the universal identity fields (id/email) derived from the
7162
7306
  // active session's idToken — central chokepoint so every provider's user
7163
7307
  // object gains { id, address, email } without each construction site
@@ -8105,7 +8249,8 @@ async function prepareAndOpen(opts, pending) {
8105
8249
  return authApi.loginWithRedirect(options);
8106
8250
  // Invoke immediately inside the click handler to retain popup activation.
8107
8251
  return authApi.loginWithPopup(options).catch((error) => {
8108
- if (shouldFallbackToRedirect(error))
8252
+ var _a;
8253
+ if (!((_a = options.signal) === null || _a === void 0 ? void 0 : _a.aborted) && shouldFallbackToRedirect(error))
8109
8254
  return authApi.loginWithRedirect(options);
8110
8255
  throw error;
8111
8256
  });
@@ -8243,6 +8388,12 @@ async function prepareAndOpen(opts, pending) {
8243
8388
  let activeAttempt = 0;
8244
8389
  let abortScopedLogin = null;
8245
8390
  let abortGesture = null;
8391
+ const startHostedLogin = (options) => {
8392
+ const controller = new AbortController();
8393
+ const abort = () => controller.abort();
8394
+ abortScopedLogin = abort;
8395
+ return hostedLogin(Object.assign(Object.assign({}, options), { signal: controller.signal }));
8396
+ };
8246
8397
  /** Reject a gesture promise already waiting on a view that is going away. */
8247
8398
  const abortPendingGesture = () => {
8248
8399
  const abort = abortGesture;
@@ -8268,6 +8419,7 @@ async function prepareAndOpen(opts, pending) {
8268
8419
  if (!attemptIsCurrent(id))
8269
8420
  return false;
8270
8421
  activeAttempt = 0;
8422
+ abortScopedLogin = null;
8271
8423
  return true;
8272
8424
  };
8273
8425
  /** Take the card back (Back, a lane switch, settling the modal). Deliberately
@@ -8422,7 +8574,7 @@ async function prepareAndOpen(opts, pending) {
8422
8574
  loginHint: input.value,
8423
8575
  redirectUri: opts.redirectUri,
8424
8576
  };
8425
- hostedLogin(emailOpts)
8577
+ startHostedLogin(emailOpts)
8426
8578
  // A hosted login that RESOLVES minted a real session, so it settles
8427
8579
  // the card even if another lane has since taken it - dropping it
8428
8580
  // would leave the app signed in behind a widget that says it is not.
@@ -8472,7 +8624,7 @@ async function prepareAndOpen(opts, pending) {
8472
8624
  }
8473
8625
  setBusy(b, "Opening secure sign-in\u2026");
8474
8626
  const socialOpts = { provider: method, redirectUri: opts.redirectUri };
8475
- hostedLogin(socialOpts)
8627
+ startHostedLogin(socialOpts)
8476
8628
  .then((u) => {
8477
8629
  if (u) {
8478
8630
  settleAttempt(attempt);
@@ -10544,8 +10696,9 @@ class PrivyExpoProvider {
10544
10696
  const { signature } = await this.signAndSubmitInternal(transaction, wallet, connection);
10545
10697
  return signature;
10546
10698
  }
10547
- async restoreSession() {
10699
+ async restoreSession(beforeCommit) {
10548
10700
  const session = await core.getActiveSessionManager().getSession();
10701
+ beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit();
10549
10702
  if (session) {
10550
10703
  this.authenticatedAddress = session.address;
10551
10704
  return { provider: this, address: session.address };
@@ -11130,4 +11283,4 @@ exports.useAuth = useAuth;
11130
11283
  exports.useBuildRun = useBuildRun;
11131
11284
  exports.useQuery = useQuery;
11132
11285
  exports.waitForAuthReady = waitForAuthReady;
11133
- //# sourceMappingURL=index-BghQ8jX0.js.map
11286
+ //# sourceMappingURL=index-Brdwqgqs.js.map