@bounded-sh/client 0.0.110 → 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 (69) hide show
  1. package/README.md +1 -1
  2. package/dist/auth/index.d.ts +3 -1
  3. package/dist/auth/oidc-auth.d.ts +2 -0
  4. package/dist/auth/providers/email-auth-provider.d.ts +1 -1
  5. package/dist/auth/providers/guest-auth-provider.d.ts +1 -1
  6. package/dist/auth/providers/injected-evm-wallet-provider.d.ts +1 -1
  7. package/dist/auth/providers/injected-wallet-provider.d.ts +1 -1
  8. package/dist/auth/providers/offchain-auth-provider.d.ts +1 -1
  9. package/dist/auth/providers/privy-expo-provider.d.ts +1 -1
  10. package/dist/auth/solana-rpc.d.ts +9 -0
  11. package/dist/{email-auth-provider-D9SQu9c1.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-T-Go_9h9.js → email-auth-provider-CG5cxOj0.js} +4 -3
  14. package/dist/email-auth-provider-CG5cxOj0.js.map +1 -0
  15. package/dist/{email-auth-provider-Czo-xM6n.js → email-auth-provider-DTrCfOJd.js} +4 -3
  16. package/dist/email-auth-provider-DTrCfOJd.js.map +1 -0
  17. package/dist/{email-auth-provider-BPeQo6xL.mjs → email-auth-provider-eyi3Wpnx.mjs} +4 -3
  18. package/dist/email-auth-provider-eyi3Wpnx.mjs.map +1 -0
  19. package/dist/{guest-auth-provider-BIohNOHi.mjs → guest-auth-provider-BsW56oJg.mjs} +10 -5
  20. package/dist/guest-auth-provider-BsW56oJg.mjs.map +1 -0
  21. package/dist/{guest-auth-provider-DsFTB844.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-BDhwvzem.js → guest-auth-provider-Dtl1J-2F.js} +10 -5
  24. package/dist/guest-auth-provider-Dtl1J-2F.js.map +1 -0
  25. package/dist/{guest-auth-provider-DJ5nCGjq.js → guest-auth-provider-Tndt6tzJ.js} +10 -5
  26. package/dist/guest-auth-provider-Tndt6tzJ.js.map +1 -0
  27. package/dist/{index-Cs5nPIzA.js → index-Brdwqgqs.js} +256 -67
  28. package/dist/index-Brdwqgqs.js.map +1 -0
  29. package/dist/{index-DbXoOlIa.mjs → index-CRxls8Qc.mjs} +257 -68
  30. package/dist/index-CRxls8Qc.mjs.map +1 -0
  31. package/dist/index.js +1 -1
  32. package/dist/index.mjs +1 -1
  33. package/dist/{index.native-BSOqaKIz.mjs → index.native-CAH_ks0s.mjs} +245 -63
  34. package/dist/index.native-CAH_ks0s.mjs.map +1 -0
  35. package/dist/{index.native-BpcNwWQX.js → index.native-ingNhWPw.js} +244 -62
  36. package/dist/index.native-ingNhWPw.js.map +1 -0
  37. package/dist/index.native.js +1 -1
  38. package/dist/index.native.mjs +1 -1
  39. package/dist/{injected-evm-wallet-provider-BjqqLfPD.mjs → injected-evm-wallet-provider-BjkCeRBL.mjs} +4 -3
  40. package/dist/injected-evm-wallet-provider-BjkCeRBL.mjs.map +1 -0
  41. package/dist/{injected-evm-wallet-provider-_UNpc_hM.js → injected-evm-wallet-provider-Cao1U0NZ.js} +4 -3
  42. package/dist/{injected-evm-wallet-provider-_UNpc_hM.js.map → injected-evm-wallet-provider-Cao1U0NZ.js.map} +1 -1
  43. package/dist/{injected-evm-wallet-provider-CMl0Fyxk.js → injected-evm-wallet-provider-CwOGXCDt.js} +4 -3
  44. package/dist/injected-evm-wallet-provider-CwOGXCDt.js.map +1 -0
  45. package/dist/{injected-evm-wallet-provider-Xb2G-ZGI.mjs → injected-evm-wallet-provider-DXjwPjLW.mjs} +4 -3
  46. package/dist/{injected-evm-wallet-provider-Xb2G-ZGI.mjs.map → injected-evm-wallet-provider-DXjwPjLW.mjs.map} +1 -1
  47. package/dist/{injected-wallet-provider-D-Pq9QVK.mjs → injected-wallet-provider-BG6gGNch.mjs} +4 -3
  48. package/dist/{injected-wallet-provider-D-Pq9QVK.mjs.map → injected-wallet-provider-BG6gGNch.mjs.map} +1 -1
  49. package/dist/{injected-wallet-provider-DZOwvKgd.mjs → injected-wallet-provider-CfYNIIHd.mjs} +4 -3
  50. package/dist/{injected-wallet-provider-DZOwvKgd.mjs.map → injected-wallet-provider-CfYNIIHd.mjs.map} +1 -1
  51. package/dist/{injected-wallet-provider-C7Q0haGY.js → injected-wallet-provider-DSlHfgMU.js} +4 -3
  52. package/dist/{injected-wallet-provider-C7Q0haGY.js.map → injected-wallet-provider-DSlHfgMU.js.map} +1 -1
  53. package/dist/{injected-wallet-provider-CgHOrtmj.js → injected-wallet-provider-LTnqoaYD.js} +4 -3
  54. package/dist/{injected-wallet-provider-CgHOrtmj.js.map → injected-wallet-provider-LTnqoaYD.js.map} +1 -1
  55. package/package.json +2 -2
  56. package/dist/email-auth-provider-BPeQo6xL.mjs.map +0 -1
  57. package/dist/email-auth-provider-Czo-xM6n.js.map +0 -1
  58. package/dist/email-auth-provider-D9SQu9c1.mjs.map +0 -1
  59. package/dist/email-auth-provider-T-Go_9h9.js.map +0 -1
  60. package/dist/guest-auth-provider-BDhwvzem.js.map +0 -1
  61. package/dist/guest-auth-provider-BIohNOHi.mjs.map +0 -1
  62. package/dist/guest-auth-provider-DJ5nCGjq.js.map +0 -1
  63. package/dist/guest-auth-provider-DsFTB844.mjs.map +0 -1
  64. package/dist/index-Cs5nPIzA.js.map +0 -1
  65. package/dist/index-DbXoOlIa.mjs.map +0 -1
  66. package/dist/index.native-BSOqaKIz.mjs.map +0 -1
  67. package/dist/index.native-BpcNwWQX.js.map +0 -1
  68. package/dist/injected-evm-wallet-provider-BjqqLfPD.mjs.map +0 -1
  69. package/dist/injected-evm-wallet-provider-CMl0Fyxk.js.map +0 -1
@@ -236,8 +236,9 @@ class OffchainAuthProvider {
236
236
  async logout() {
237
237
  await this.wrappedProvider.logout();
238
238
  }
239
- async restoreSession() {
240
- const user = await this.wrappedProvider.restoreSession();
239
+ async restoreSession(beforeCommit) {
240
+ const provider = this.wrappedProvider;
241
+ const user = await provider.restoreSession(beforeCommit);
241
242
  if (user) {
242
243
  return Object.assign(Object.assign({}, user), { provider: this });
243
244
  }
@@ -707,6 +708,8 @@ async function clearSessionProviderForLogout() {
707
708
  setStoredAuthMethod(null);
708
709
  }
709
710
 
711
+ const SOLANA_DEVNET_RPC_URL = "https://idelle-8nxsep-fast-devnet.helius-rpc.com";
712
+ const SOLANA_MAINNET_RPC_URL = "https://celestia-cegncv-fast-mainnet.helius-rpc.com";
710
713
  const SUPPORTED_SOLANA_NETWORKS = new Set([
711
714
  "solana_devnet",
712
715
  "solana_mainnet",
@@ -736,15 +739,35 @@ function resolveSolanaWalletStandardChain(network, context) {
736
739
  `Expected one of: solana_devnet, solana_mainnet.`);
737
740
  }
738
741
  }
742
+ /**
743
+ * The platform RPC endpoint for a supported Solana network, or null when the
744
+ * network is missing or unsupported. Used when the app passes no rpcUrl.
745
+ */
746
+ function defaultSolanaRpcUrl(network) {
747
+ switch (network) {
748
+ case "solana_mainnet":
749
+ return SOLANA_MAINNET_RPC_URL;
750
+ case "solana_devnet":
751
+ return SOLANA_DEVNET_RPC_URL;
752
+ default:
753
+ return null;
754
+ }
755
+ }
756
+ /**
757
+ * An explicit rpcUrl always wins. Otherwise the platform RPC for the configured
758
+ * network is used. With neither, fail closed.
759
+ */
739
760
  function resolveSolanaRpcUrl(explicitRpcUrl, network, context) {
761
+ var _a;
740
762
  if (network != null && network !== "" && !isSupportedSolanaRpcNetwork(network)) {
741
763
  throw new Error(`${context} does not support Solana network "${network}". ` +
742
764
  `Expected one of: solana_devnet, solana_mainnet.`);
743
765
  }
744
- const rpcUrl = normalizeSolanaRpcUrl(explicitRpcUrl);
766
+ const rpcUrl = (_a = normalizeSolanaRpcUrl(explicitRpcUrl)) !== null && _a !== void 0 ? _a : defaultSolanaRpcUrl(network);
745
767
  if (rpcUrl)
746
768
  return rpcUrl;
747
- throw new Error(`${context} requires an explicit Solana RPC URL. Pass config.rpcUrl before signing or submitting onchain transactions.`);
769
+ throw new Error(`${context} requires a Solana network. Set init({ chain: "solana_mainnet" | "solana_devnet" }) ` +
770
+ `or pass config.rpcUrl before signing or submitting onchain transactions.`);
748
771
  }
749
772
 
750
773
  var __classPrivateFieldGet = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
@@ -5275,7 +5298,8 @@ async function loginWithRedirect(opts) {
5275
5298
  return runNativeLogin(opts);
5276
5299
  if (!hasDOM$1())
5277
5300
  throw new Error('loginWithRedirect requires a browser or a configured React Native platform');
5278
- const { url, pkce } = await buildAuthorize(opts);
5301
+ const { url, pkce } = await untilAborted(buildAuthorize(opts), opts.signal);
5302
+ throwIfCancelled(opts.signal);
5279
5303
  // Stash the verifier + state so the redirect_uri page can finish the exchange. sessionStorage
5280
5304
  // is per-tab and cleared on close — appropriate for a short-lived auth handshake. Keyed by
5281
5305
  // this attempt's own state so a second concurrent login cannot overwrite it.
@@ -5304,9 +5328,29 @@ async function completeLoginFromRedirect() {
5304
5328
  throw error;
5305
5329
  return result.user;
5306
5330
  }
5331
+ function throwIfCancelled(signal) {
5332
+ if (signal === null || signal === void 0 ? void 0 : signal.aborted)
5333
+ throw new Error('cancelled');
5334
+ }
5335
+ async function untilAborted(work, signal) {
5336
+ if (!signal)
5337
+ return work;
5338
+ throwIfCancelled(signal);
5339
+ let onAbort;
5340
+ try {
5341
+ return await Promise.race([work, new Promise((_, reject) => {
5342
+ onAbort = () => reject(new Error('cancelled'));
5343
+ signal.addEventListener('abort', onAbort, { once: true });
5344
+ })]);
5345
+ }
5346
+ finally {
5347
+ signal.removeEventListener('abort', onAbort);
5348
+ }
5349
+ }
5307
5350
  /** Popup variant: open the hosted login in a popup; resolves with the User once the popup's
5308
5351
  * redirect_uri page initializes the SDK, which hands the code back automatically. */
5309
5352
  async function loginWithPopup(opts) {
5353
+ throwIfCancelled(opts.signal);
5310
5354
  if (!hasDOM$1())
5311
5355
  throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
5312
5356
  // Open synchronously, before PKCE/config awaits consume the click's user-
@@ -5324,7 +5368,7 @@ async function loginWithPopup(opts) {
5324
5368
  throw new Error('Popup blocked — use loginWithRedirect() instead');
5325
5369
  let authorization;
5326
5370
  try {
5327
- authorization = await buildAuthorize(opts, 'popup');
5371
+ authorization = await untilAborted(buildAuthorize(opts, 'popup'), opts.signal);
5328
5372
  }
5329
5373
  catch (error) {
5330
5374
  try {
@@ -5341,6 +5385,7 @@ async function loginWithPopup(opts) {
5341
5385
  // window with no listener or watchdog attached.
5342
5386
  let expectedOrigin;
5343
5387
  try {
5388
+ throwIfCancelled(opts.signal);
5344
5389
  expectedOrigin = new URL(pkce.redirectUri).origin;
5345
5390
  sessionStorage.setItem(pkceKey(pkce.state), JSON.stringify(pkce));
5346
5391
  }
@@ -5372,20 +5417,42 @@ async function loginWithPopup(opts) {
5372
5417
  setAuthLoading(true); // reflect "signing in…" while the popup is open
5373
5418
  try {
5374
5419
  return await new Promise((resolve, reject) => {
5420
+ var _a, _b;
5375
5421
  let settled = false;
5422
+ let handedOff = false;
5423
+ let committing = false;
5376
5424
  let timer;
5377
5425
  let navigationTimer;
5378
5426
  let completionTimer;
5379
5427
  const cleanup = () => {
5380
- settled = true;
5381
5428
  clearInterval(timer);
5382
5429
  clearTimeout(navigationTimer);
5383
5430
  clearTimeout(completionTimer);
5384
5431
  window.removeEventListener('message', onMsg);
5385
5432
  removeAttempt(pkce.state);
5386
5433
  };
5434
+ const finish = (error, user) => {
5435
+ var _a;
5436
+ if (settled)
5437
+ return;
5438
+ settled = true;
5439
+ cleanup();
5440
+ (_a = opts.signal) === null || _a === void 0 ? void 0 : _a.removeEventListener('abort', onAbort);
5441
+ try {
5442
+ popup.close();
5443
+ }
5444
+ catch ( /* ignore */_b) { /* ignore */ }
5445
+ if (error)
5446
+ reject(error);
5447
+ else
5448
+ resolve(user);
5449
+ };
5450
+ // Provider adoption commits synchronously after its final guard. An auth
5451
+ // listener dismissing the card there cannot cancel an already signed-in user.
5452
+ const onAbort = () => { if (!committing)
5453
+ finish(new Error('cancelled')); };
5387
5454
  const onMsg = async (ev) => {
5388
- if (settled || ev.origin !== expectedOrigin || ev.source !== popup)
5455
+ if (settled || handedOff || ev.origin !== expectedOrigin || ev.source !== popup)
5389
5456
  return;
5390
5457
  const d = ev.data;
5391
5458
  if (!d || d.type !== 'bounded-oidc-code' ||
@@ -5399,35 +5466,30 @@ async function loginWithPopup(opts) {
5399
5466
  return;
5400
5467
  if (d.appId !== pkce.appId || d.issuer !== pkce.issuer)
5401
5468
  return;
5469
+ handedOff = true;
5402
5470
  cleanup();
5403
5471
  try {
5472
+ throwIfCancelled(opts.signal);
5404
5473
  await validateAttempt(pkce, new URL(d.callbackUrl));
5474
+ throwIfCancelled(opts.signal);
5405
5475
  popup.postMessage({ type: 'bounded-oidc-accepted', state: pkce.state }, expectedOrigin);
5406
5476
  if (d.error)
5407
5477
  throw new Error(typeof d.errorDescription === 'string' ? d.errorDescription : `Login failed: ${d.error}`);
5408
- const user = await exchangeCode(d.code, pkce);
5409
- try {
5410
- popup.close();
5411
- }
5412
- catch ( /* ignore */_a) { /* ignore */ }
5413
- resolve(user);
5478
+ const user = await exchangeCode(d.code, pkce, opts.signal, () => { committing = true; });
5479
+ finish(undefined, user);
5414
5480
  }
5415
5481
  catch (e) {
5416
- try {
5417
- popup.close();
5418
- }
5419
- catch ( /* ignore */_b) { /* ignore */ }
5420
- reject(e);
5482
+ finish(e);
5421
5483
  }
5422
5484
  };
5485
+ (_a = opts.signal) === null || _a === void 0 ? void 0 : _a.addEventListener('abort', onAbort, { once: true });
5486
+ if ((_b = opts.signal) === null || _b === void 0 ? void 0 : _b.aborted) {
5487
+ onAbort();
5488
+ return;
5489
+ }
5423
5490
  window.addEventListener('message', onMsg);
5424
5491
  completionTimer = setTimeout(() => {
5425
- cleanup();
5426
- try {
5427
- popup.close();
5428
- }
5429
- catch ( /* ignore */_a) { /* ignore */ }
5430
- reject(new Error('Login expired or its popup disconnected. Start sign-in again.'));
5492
+ finish(new Error('Login expired or its popup disconnected. Start sign-in again.'));
5431
5493
  }, LOGIN_TIMEOUT_MS);
5432
5494
  // Some browser/extension combinations can return a real popup handle but
5433
5495
  // strand the window at about:blank instead of navigating to the hosted
@@ -5448,23 +5510,18 @@ async function loginWithPopup(opts) {
5448
5510
  const NAVIGATION_RECHECK_MS = 2000;
5449
5511
  const navigationStartedAt = Date.now();
5450
5512
  const checkNavigation = () => {
5451
- if (settled || popup.closed)
5513
+ if (settled || handedOff || popup.closed)
5452
5514
  return;
5453
5515
  try {
5454
5516
  if (popup.location.href === 'about:blank') {
5455
5517
  if (Date.now() - navigationStartedAt >= NAVIGATION_GRACE_MS) {
5456
- cleanup();
5457
- try {
5458
- popup.close();
5459
- }
5460
- catch ( /* ignore */_a) { /* ignore */ }
5461
- reject(new Error('Popup stalled at about:blank - use loginWithRedirect() instead'));
5518
+ finish(new Error('Popup stalled at about:blank - use loginWithRedirect() instead'));
5462
5519
  return;
5463
5520
  }
5464
5521
  navigationTimer = setTimeout(checkNavigation, NAVIGATION_RECHECK_MS);
5465
5522
  }
5466
5523
  }
5467
- catch ( /* cross-origin hosted auth loaded normally */_b) { /* cross-origin hosted auth loaded normally */ }
5524
+ catch ( /* cross-origin hosted auth loaded normally */_a) { /* cross-origin hosted auth loaded normally */ }
5468
5525
  };
5469
5526
  navigationTimer = setTimeout(checkNavigation, NAVIGATION_RECHECK_MS);
5470
5527
  // The popup posts its code and then closes ITSELF, so a naive "popup.closed -> reject"
@@ -5474,10 +5531,8 @@ async function loginWithPopup(opts) {
5474
5531
  timer = setInterval(() => {
5475
5532
  if (popup.closed) {
5476
5533
  clearInterval(timer);
5477
- setTimeout(() => { if (!settled) {
5478
- cleanup();
5479
- reject(new Error('Login window closed'));
5480
- } }, 500);
5534
+ setTimeout(() => { if (!settled && !handedOff)
5535
+ finish(new Error('Login window closed')); }, 500);
5481
5536
  }
5482
5537
  }, 500);
5483
5538
  });
@@ -5486,9 +5541,10 @@ async function loginWithPopup(opts) {
5486
5541
  setAuthLoading(false);
5487
5542
  }
5488
5543
  }
5489
- async function exchangeCode(code, pkce) {
5544
+ async function exchangeCode(code, pkce, signal, onCommitting) {
5490
5545
  const generation = getInitializationGeneration();
5491
5546
  const beforeCommit = () => {
5547
+ throwIfCancelled(signal);
5492
5548
  if (generation !== getInitializationGeneration())
5493
5549
  throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
5494
5550
  };
@@ -5504,7 +5560,7 @@ async function exchangeCode(code, pkce) {
5504
5560
  let res;
5505
5561
  try {
5506
5562
  res = await fetch(`${base}/api/auth/oauth2/token`, {
5507
- method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString(),
5563
+ method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded' }, body: body.toString(), signal,
5508
5564
  });
5509
5565
  }
5510
5566
  catch (err) {
@@ -5530,7 +5586,7 @@ async function exchangeCode(code, pkce) {
5530
5586
  // Adopt on EVERY session surface (see establishInlineSession) - hosted
5531
5587
  // popup/redirect completions all land here, so this closes their gap too.
5532
5588
  try {
5533
- await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit });
5589
+ await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit: () => { beforeCommit(); onCommitting === null || onCommitting === void 0 ? void 0 : onCommitting(); } });
5534
5590
  }
5535
5591
  catch (error) {
5536
5592
  if (typeof rollback === 'function')
@@ -5599,6 +5655,31 @@ function idTokenIsGuest(idToken, expectedAddress) {
5599
5655
  function storedSessionIsGuest() {
5600
5656
  return idTokenIsGuest(getStoredIdToken());
5601
5657
  }
5658
+ /** Internal: select a cross-tab restore from bearer claims, never global hints. */
5659
+ function storedSessionAuthMethod(config) {
5660
+ try {
5661
+ const token = getStoredIdToken();
5662
+ if (!token)
5663
+ return null;
5664
+ let payload = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
5665
+ payload += '='.repeat((4 - payload.length % 4) % 4);
5666
+ const claims = JSON.parse(getPlatform().atob(payload));
5667
+ const method = claims.is_anonymous === true ? 'guest'
5668
+ : claims['custom:walletProvider'] === 'turnkey' || claims.email ? 'email'
5669
+ : claims['custom:evmWalletAddress'] ? 'evm-wallet'
5670
+ : claims['custom:walletAddress'] ? 'phantom' : 'email';
5671
+ if (config.authMethod != null && canonicalWalletAuthMethod(config.authMethod) !== method)
5672
+ return null;
5673
+ if (method === 'evm-wallet' && config.authMethod !== 'evm-wallet')
5674
+ return null;
5675
+ if (method === 'phantom' && (isWalletLoginDisabled(config) || isEmailRequired(config)))
5676
+ return null;
5677
+ return method;
5678
+ }
5679
+ catch (_a) {
5680
+ return null;
5681
+ }
5682
+ }
5602
5683
  /**
5603
5684
  * Pick the provider used for an ordinary `init({ appId })` restore.
5604
5685
  *
@@ -5845,9 +5926,10 @@ function optionalProviderError(method) {
5845
5926
  `loginWithRedirect(...) for Bounded Auth, signInAnonymously() for guests, ` +
5846
5927
  `or install/import an explicit wallet-provider entry once your app opts into "${method}".`);
5847
5928
  }
5848
- async function getAuthProvider(config) {
5929
+ async function getAuthProvider(config, fromStorage = false) {
5849
5930
  var _a;
5850
- const nextProviderConfigKey = config ? authProviderConfigKey(config) : null;
5931
+ const requestedAuthMethod = config ? (fromStorage ? storedSessionAuthMethod(config) : authMethodForInit(config)) : null;
5932
+ const nextProviderConfigKey = config ? authProviderConfigKey(Object.assign(Object.assign({}, config), { authMethod: requestedAuthMethod })) : null;
5851
5933
  if (currentAuthProvider && (!config || currentAuthProviderConfigKey === nextProviderConfigKey)) {
5852
5934
  return currentAuthProvider;
5853
5935
  }
@@ -5862,7 +5944,8 @@ async function getAuthProvider(config) {
5862
5944
  initConfig = config;
5863
5945
  // Keep what the app actually wrote for error text; everything else runs on
5864
5946
  // the canonical method so exactly one string reaches storage and the issuer.
5865
- const requestedAuthMethod = authMethodForInit(config);
5947
+ if (!requestedAuthMethod)
5948
+ throw new Error('The stored session is incompatible with this app authentication configuration.');
5866
5949
  const authMethod = canonicalWalletAuthMethod(requestedAuthMethod);
5867
5950
  const rpcUrl = normalizeSolanaRpcUrl(config.rpcUrl);
5868
5951
  const configuredNetwork = getConfiguredSolanaNetwork(config);
@@ -5887,7 +5970,7 @@ async function getAuthProvider(config) {
5887
5970
  // manager as legacy email sessions. Human login should be started
5888
5971
  // with loginWithRedirect(); this provider is kept as the restore and
5889
5972
  // logout surface for those sessions.
5890
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-Czo-xM6n.js'); })).EmailAuthProvider();
5973
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-DTrCfOJd.js'); })).EmailAuthProvider();
5891
5974
  break;
5892
5975
  case "phantom": {
5893
5976
  // First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
@@ -5929,7 +6012,7 @@ async function getAuthProvider(config) {
5929
6012
  // (bounded-login-widget.ts), so registration never counts as
5930
6013
  // evidence the user has a wallet.
5931
6014
  await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
5932
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-C7Q0haGY.js'); });
6015
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-DSlHfgMU.js'); });
5933
6016
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
5934
6017
  break;
5935
6018
  }
@@ -5942,7 +6025,7 @@ async function getAuthProvider(config) {
5942
6025
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
5943
6026
  // to point at a specific wallet or set chainId/domain.
5944
6027
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
5945
- const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-_UNpc_hM.js'); });
6028
+ const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-Cao1U0NZ.js'); });
5946
6029
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
5947
6030
  break;
5948
6031
  }
@@ -5967,7 +6050,7 @@ async function getAuthProvider(config) {
5967
6050
  break;
5968
6051
  case "guest":
5969
6052
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
5970
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-DJ5nCGjq.js'); })).GuestAuthProvider();
6053
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-Tndt6tzJ.js'); })).GuestAuthProvider();
5971
6054
  break;
5972
6055
  }
5973
6056
  if (!currentAuthProvider) {
@@ -6029,7 +6112,7 @@ async function signInAnonymously() {
6029
6112
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
6030
6113
  // the main bundle for data-only apps. Specifier is a static string literal
6031
6114
  // so bundle-splitting can see it.
6032
- const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-DJ5nCGjq.js'); })).GuestAuthProvider();
6115
+ const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-Tndt6tzJ.js'); })).GuestAuthProvider();
6033
6116
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
6034
6117
  // adoptSessionProvider awaits only getConfig() (already resolved here) and then
6035
6118
  // runs every write - provider/method/config/marker/user - in one synchronous
@@ -6200,7 +6283,7 @@ function ensureSharedWalletLoginReady() {
6200
6283
  if (!sharedWalletReady) {
6201
6284
  sharedWalletReady = (async () => {
6202
6285
  await core.getConfig();
6203
- await Promise.resolve().then(function () { return require('./injected-wallet-provider-C7Q0haGY.js'); });
6286
+ await Promise.resolve().then(function () { return require('./injected-wallet-provider-DSlHfgMU.js'); });
6204
6287
  })().catch((error) => {
6205
6288
  sharedWalletReady = null;
6206
6289
  throw error;
@@ -6421,6 +6504,17 @@ async function logout$1(options) {
6421
6504
  }
6422
6505
  }
6423
6506
 
6507
+ /**
6508
+ * An explicit rpcUrl wins; otherwise a Solana chain gets the platform RPC so
6509
+ * every onchain path (wallet signing, submission, reconcile) has one.
6510
+ */
6511
+ function withDefaultSolanaRpcUrl(config) {
6512
+ var _a;
6513
+ if (normalizeSolanaRpcUrl(config.rpcUrl))
6514
+ return config;
6515
+ const rpcUrl = defaultSolanaRpcUrl((_a = config.chain) === null || _a === void 0 ? void 0 : _a.trim());
6516
+ return rpcUrl ? Object.assign(Object.assign({}, config), { rpcUrl }) : config;
6517
+ }
6424
6518
  /**
6425
6519
  * Subscribed once per process, not per init(): re-registering on every init would
6426
6520
  * stack duplicate handlers on repeated initialization.
@@ -6485,11 +6579,80 @@ let isAuthLoading = false;
6485
6579
  let authReadyPromise = null;
6486
6580
  let initializingConfig = null;
6487
6581
  let initializationGeneration = 0;
6582
+ let initializedConfig = null;
6583
+ let storageBridgeRegistered = false;
6584
+ let observedIdToken = null;
6585
+ let observedIssuer = null;
6586
+ // Storage events carry no authority. Re-read the active manager's app/issuer
6587
+ // namespace, then use the ordinary initializer/provider restore and listeners.
6588
+ function registerStorageBridge() {
6589
+ if (storageBridgeRegistered || !getPlatform().hasDOM || typeof window === 'undefined')
6590
+ return;
6591
+ storageBridgeRegistered = true;
6592
+ window.addEventListener('storage', (event) => {
6593
+ if (event.storageArea !== window.localStorage || !initializedConfig)
6594
+ return;
6595
+ void syncStoredSession().catch((error) => console.warn('Bounded session synchronization failed:', error));
6596
+ });
6597
+ }
6598
+ async function syncStoredSession() {
6599
+ const config = initializedConfig;
6600
+ if (!config)
6601
+ return;
6602
+ const token = core.WebSessionManager.getIdToken();
6603
+ const issuer = core.WebSessionManager.getIssuer();
6604
+ if (token === observedIdToken && issuer === observedIssuer)
6605
+ return;
6606
+ observedIdToken = token;
6607
+ observedIssuer = issuer;
6608
+ const generation = ++initializationGeneration;
6609
+ // Hide the former principal while the replacement is validated. This also
6610
+ // reconnects live reads through the existing auth-state change path.
6611
+ setCurrentUser(null);
6612
+ if (authReadyPromise)
6613
+ await authReadyPromise.catch(() => { });
6614
+ if (generation !== initializationGeneration || config !== initializedConfig)
6615
+ return;
6616
+ const record = await core.WebSessionManager.readSessionWithGeneration();
6617
+ if (generation !== initializationGeneration || config !== initializedConfig)
6618
+ return;
6619
+ if (!record)
6620
+ return;
6621
+ const normalize = (url) => (url || '').replace(/\/+$/, '');
6622
+ const resolvedConfig = await core.getConfig();
6623
+ if (generation !== initializationGeneration || config !== initializedConfig)
6624
+ return;
6625
+ const allowedIssuers = [normalize(resolvedConfig.humanAuthApiUrl || 'https://auth.bounded.sh'), normalize(resolvedConfig.authApiUrl)].filter(Boolean);
6626
+ // A legacy shared blob can belong to another app. Never restore or clear
6627
+ // that app's credential; the current app remains signed out.
6628
+ if ((record.appId && record.appId !== config.appId)
6629
+ || (record.issuer && !allowedIssuers.includes(normalize(record.issuer))))
6630
+ return;
6631
+ if (!storedSessionAuthMethod(config))
6632
+ return;
6633
+ const restored = startInitialization(config, true);
6634
+ const restoreGeneration = initializationGeneration;
6635
+ try {
6636
+ await restored;
6637
+ }
6638
+ catch (error) {
6639
+ if (restoreGeneration === initializationGeneration && core.WebSessionManager.getIdToken())
6640
+ throw error;
6641
+ }
6642
+ }
6488
6643
  /** Internal guard for a login result arriving after the app reconfigured the SDK. */
6489
6644
  function getInitializationGeneration() { return initializationGeneration; }
6490
6645
  // This file acts as a middleware for the global state of the SDK
6491
6646
  // This mostly involves setting up the AuthProvider and managing the current user
6492
- async function initialize(newConfig) {
6647
+ async function initialize(requestedConfig, fromStorage) {
6648
+ const newConfig = withDefaultSolanaRpcUrl(requestedConfig);
6649
+ const generation = initializationGeneration;
6650
+ const validateStorage = fromStorage ? core.WebSessionManager.captureSessionEstablishment() : undefined;
6651
+ const beforeCommit = () => {
6652
+ if (generation !== initializationGeneration)
6653
+ throw new Error('Session restore was superseded.');
6654
+ validateStorage === null || validateStorage === void 0 ? void 0 : validateStorage();
6655
+ };
6493
6656
  // Mark auth as loading for the ENTIRE init/restore window so consumers render
6494
6657
  // a neutral/loading state instead of the signed-out UI while we rehydrate the
6495
6658
  // session — otherwise getCurrentUser() is null + loading is false during the
@@ -6508,8 +6671,9 @@ async function initialize(newConfig) {
6508
6671
  // onAuthStateChanged never fired, and every consumer UI went on showing a
6509
6672
  // connected wallet for a session the server no longer knew about.
6510
6673
  registerSessionClearedBridge();
6674
+ registerStorageBridge();
6511
6675
  // Get auth provider first
6512
- authProviderInstance = await getAuthProvider(newConfig);
6676
+ authProviderInstance = await getAuthProvider(newConfig, fromStorage);
6513
6677
  // Initialize config with auth provider
6514
6678
  await core.init(Object.assign(Object.assign({}, newConfig), { authProvider: authProviderInstance }));
6515
6679
  // A return establishes its own provider/session, or hands off a popup.
@@ -6534,7 +6698,8 @@ async function initialize(newConfig) {
6534
6698
  // Wait for restoreSession to complete
6535
6699
  // The appId check is done in SessionManager.getSession which is called by restoreSession
6536
6700
  // If there's an appId mismatch, the session will be cleared and restoreSession will return null
6537
- let user = await authProviderInstance.restoreSession();
6701
+ let user = await authProviderInstance.restoreSession(beforeCommit);
6702
+ beforeCommit();
6538
6703
  // If a session exists but was created before auth method tracking (legacy session),
6539
6704
  // clear it to avoid mismatches when the app switches auth methods (e.g. email → phantom)
6540
6705
  //
@@ -6542,9 +6707,10 @@ async function initialize(newConfig) {
6542
6707
  // unawaited call would return a Promise (always truthy) — nulling the user
6543
6708
  // unconditionally AND letting setCurrentUser run while the old credential
6544
6709
  // was still in storage.
6545
- if (user && await clearIncompatibleSession()) {
6710
+ if (user && !fromStorage && await clearIncompatibleSession()) {
6546
6711
  user = null;
6547
6712
  }
6713
+ beforeCommit();
6548
6714
  setCurrentUser(user);
6549
6715
  // Mark init as completed
6550
6716
  initCompleted = true;
@@ -6584,13 +6750,17 @@ function sameConfigValue(a, b, seen = new WeakMap()) {
6584
6750
  && sameConfigValue(a[key], b[key], seen));
6585
6751
  }
6586
6752
  function init(newConfig) {
6753
+ return startInitialization(newConfig, false);
6754
+ }
6755
+ function startInitialization(newConfig, fromStorage) {
6587
6756
  if (initializingConfig) {
6588
6757
  if (sameConfigValue(initializingConfig, newConfig)) {
6589
6758
  return authReadyPromise;
6590
6759
  }
6591
6760
  return Promise.reject(new Error('SDK initialization is in progress. Await init() before changing its configuration.'));
6592
6761
  }
6593
- initializingConfig = Object.assign({}, newConfig);
6762
+ initializedConfig = Object.assign({}, newConfig);
6763
+ initializingConfig = initializedConfig;
6594
6764
  initializationGeneration++;
6595
6765
  let resolveReady;
6596
6766
  let rejectReady;
@@ -6602,7 +6772,7 @@ function init(newConfig) {
6602
6772
  // auth-loading listener to call waitForAuthReady() synchronously when init()
6603
6773
  // announces that session restoration has begun.
6604
6774
  authReadyPromise = ready;
6605
- void initialize(initializingConfig).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
6775
+ void initialize(initializingConfig, fromStorage).then(() => { initializingConfig = null; resolveReady(); }, (error) => { initializingConfig = null; rejectReady(error); });
6606
6776
  return ready;
6607
6777
  }
6608
6778
  /**
@@ -6675,6 +6845,13 @@ function userPrincipalKey(user) {
6675
6845
  }
6676
6846
  // Update the currentUser and notify listeners
6677
6847
  function setCurrentUser(user) {
6848
+ observedIdToken = getCurrentIdToken();
6849
+ try {
6850
+ observedIssuer = core.getActiveSessionManager().getIssuer();
6851
+ }
6852
+ catch (_a) {
6853
+ observedIssuer = null;
6854
+ }
6678
6855
  // Enrich with the universal identity fields (id/email) derived from the
6679
6856
  // active session's idToken — central chokepoint so every provider's user
6680
6857
  // object gains { id, address, email } without each construction site
@@ -8417,7 +8594,7 @@ class PrivyExpoProvider {
8417
8594
  if (isLegacyTx) {
8418
8595
  const legacyTx = transaction;
8419
8596
  if (!legacyTx.recentBlockhash) {
8420
- const rpcUrl = this.getRpcUrl();
8597
+ const rpcUrl = await this.getRpcUrl();
8421
8598
  const connection = new Connection(rpcUrl, 'confirmed');
8422
8599
  const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
8423
8600
  legacyTx.recentBlockhash = blockhash;
@@ -8430,7 +8607,7 @@ class PrivyExpoProvider {
8430
8607
  else {
8431
8608
  const versionedTx = transaction;
8432
8609
  if (!versionedTx.message.recentBlockhash) {
8433
- const rpcUrl = this.getRpcUrl();
8610
+ const rpcUrl = await this.getRpcUrl();
8434
8611
  const connection = new Connection(rpcUrl, 'confirmed');
8435
8612
  const { blockhash } = await connection.getLatestBlockhash('confirmed');
8436
8613
  versionedTx.message.recentBlockhash = blockhash;
@@ -8450,7 +8627,7 @@ class PrivyExpoProvider {
8450
8627
  await this.ensureReady();
8451
8628
  const { Connection, PublicKey } = await import('@solana/web3.js');
8452
8629
  const wallet = await this.getWalletOrThrow('submitting this transaction');
8453
- const rpcUrl = this.getRpcUrl();
8630
+ const rpcUrl = await this.getRpcUrl();
8454
8631
  const connection = new Connection(rpcUrl, 'confirmed');
8455
8632
  // Ensure blockhash.
8456
8633
  // DS3-0414: a Solana signature covers the compiled message (recentBlockhash
@@ -8477,8 +8654,9 @@ class PrivyExpoProvider {
8477
8654
  const { signature } = await this.signAndSubmitInternal(transaction, wallet, connection);
8478
8655
  return signature;
8479
8656
  }
8480
- async restoreSession() {
8657
+ async restoreSession(beforeCommit) {
8481
8658
  const session = await core.getActiveSessionManager().getSession();
8659
+ beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit();
8482
8660
  if (session) {
8483
8661
  this.authenticatedAddress = session.address;
8484
8662
  return { provider: this, address: session.address };
@@ -8551,8 +8729,12 @@ class PrivyExpoProvider {
8551
8729
  const txInfo = await confirmAndCheckTransaction(connection, signature);
8552
8730
  return { signature, txInfo };
8553
8731
  }
8554
- getRpcUrl(network) {
8555
- return resolveSolanaRpcUrl(this.networkUrl, network, "Privy Expo Solana transaction");
8732
+ // The constructor rpcUrl wins, then init()'s rpcUrl, then the platform RPC
8733
+ // for init()'s chain.
8734
+ async getRpcUrl() {
8735
+ var _a;
8736
+ const config = await core.getConfig();
8737
+ return resolveSolanaRpcUrl((_a = this.networkUrl) !== null && _a !== void 0 ? _a : config.rpcUrl, isSupportedSolanaRpcNetwork(config.chain) ? config.chain : null, "Privy Expo Solana transaction");
8556
8738
  }
8557
8739
  async ensureReady(timeoutMs = 15000) {
8558
8740
  if (!this.privyMethods) {
@@ -8636,4 +8818,4 @@ exports.useAuth = useAuth;
8636
8818
  exports.useBuildRun = useBuildRun;
8637
8819
  exports.useQuery = useQuery;
8638
8820
  exports.waitForAuthReady = waitForAuthReady;
8639
- //# sourceMappingURL=index.native-BpcNwWQX.js.map
8821
+ //# sourceMappingURL=index.native-ingNhWPw.js.map