@bounded-sh/client 0.0.58 → 0.0.60

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 (71) hide show
  1. package/dist/auth/index.d.ts +1 -1
  2. package/dist/{email-auth-provider-BHtR1H_E.js → email-auth-provider-B5yIZe1l.js} +3 -3
  3. package/dist/email-auth-provider-B5yIZe1l.js.map +1 -0
  4. package/dist/{email-auth-provider-BlJI-8w9.js → email-auth-provider-BCEdcnRu.js} +3 -3
  5. package/dist/email-auth-provider-BCEdcnRu.js.map +1 -0
  6. package/dist/{email-auth-provider-CfNSS8Du.mjs → email-auth-provider-BF-vvbAt.mjs} +3 -3
  7. package/dist/email-auth-provider-BF-vvbAt.mjs.map +1 -0
  8. package/dist/{email-auth-provider-CX7foEhA.mjs → email-auth-provider-nBv7mW9q.mjs} +3 -3
  9. package/dist/email-auth-provider-nBv7mW9q.mjs.map +1 -0
  10. package/dist/{guest-auth-provider-BPXDJoEQ.js → guest-auth-provider-CZ-CpNhD.js} +7 -7
  11. package/dist/guest-auth-provider-CZ-CpNhD.js.map +1 -0
  12. package/dist/{guest-auth-provider-BRjJV8Zq.js → guest-auth-provider-CrRreOyf.js} +7 -7
  13. package/dist/guest-auth-provider-CrRreOyf.js.map +1 -0
  14. package/dist/{guest-auth-provider-DxhZ5sWN.mjs → guest-auth-provider-DFNAHmtY.mjs} +7 -7
  15. package/dist/guest-auth-provider-DFNAHmtY.mjs.map +1 -0
  16. package/dist/{guest-auth-provider-DJmuReVa.mjs → guest-auth-provider-DGvQgIwm.mjs} +7 -7
  17. package/dist/guest-auth-provider-DGvQgIwm.mjs.map +1 -0
  18. package/dist/{index-DWodlTFG.mjs → index-CQtGkKVd.mjs} +284 -38
  19. package/dist/index-CQtGkKVd.mjs.map +1 -0
  20. package/dist/{index-MKE9tHpl.js → index-iu8vB4ye.js} +285 -36
  21. package/dist/index-iu8vB4ye.js.map +1 -0
  22. package/dist/index.d.ts +2 -0
  23. package/dist/index.js +4 -1
  24. package/dist/index.js.map +1 -1
  25. package/dist/index.mjs +1 -1
  26. package/dist/{index.native-hu3o3zXU.js → index.native-CDfx0N6p.js} +274 -25
  27. package/dist/index.native-CDfx0N6p.js.map +1 -0
  28. package/dist/{index.native-CQiw9baS.mjs → index.native-Crfn_NOB.mjs} +273 -27
  29. package/dist/index.native-Crfn_NOB.mjs.map +1 -0
  30. package/dist/index.native.d.ts +2 -0
  31. package/dist/index.native.js +4 -1
  32. package/dist/index.native.js.map +1 -1
  33. package/dist/index.native.mjs +1 -1
  34. package/dist/{injected-evm-wallet-provider-DCGnUedJ.mjs → injected-evm-wallet-provider-Bi9G3X35.mjs} +4 -4
  35. package/dist/injected-evm-wallet-provider-Bi9G3X35.mjs.map +1 -0
  36. package/dist/{injected-evm-wallet-provider-Xbt2CUPi.js → injected-evm-wallet-provider-CGa692L1.js} +4 -4
  37. package/dist/injected-evm-wallet-provider-CGa692L1.js.map +1 -0
  38. package/dist/{injected-evm-wallet-provider-CnJRLE8P.mjs → injected-evm-wallet-provider-D8lYGcM2.mjs} +4 -4
  39. package/dist/injected-evm-wallet-provider-D8lYGcM2.mjs.map +1 -0
  40. package/dist/{injected-evm-wallet-provider-BOPzdm7E.js → injected-evm-wallet-provider-LTWiACl5.js} +4 -4
  41. package/dist/injected-evm-wallet-provider-LTWiACl5.js.map +1 -0
  42. package/dist/{injected-wallet-provider-Bps6a9IP.mjs → injected-wallet-provider-CcyA5JBS.mjs} +3 -3
  43. package/dist/injected-wallet-provider-CcyA5JBS.mjs.map +1 -0
  44. package/dist/{injected-wallet-provider-BUvN5Y6_.js → injected-wallet-provider-DYZFxIdd.js} +3 -3
  45. package/dist/injected-wallet-provider-DYZFxIdd.js.map +1 -0
  46. package/dist/{injected-wallet-provider-7ep-jcal.js → injected-wallet-provider-TPx8Wzvq.js} +3 -3
  47. package/dist/injected-wallet-provider-TPx8Wzvq.js.map +1 -0
  48. package/dist/{injected-wallet-provider-Dg3MieTq.mjs → injected-wallet-provider-kx8tcMKM.mjs} +3 -3
  49. package/dist/injected-wallet-provider-kx8tcMKM.mjs.map +1 -0
  50. package/dist/onramp.d.ts +72 -0
  51. package/package.json +2 -2
  52. package/dist/email-auth-provider-BHtR1H_E.js.map +0 -1
  53. package/dist/email-auth-provider-BlJI-8w9.js.map +0 -1
  54. package/dist/email-auth-provider-CX7foEhA.mjs.map +0 -1
  55. package/dist/email-auth-provider-CfNSS8Du.mjs.map +0 -1
  56. package/dist/guest-auth-provider-BPXDJoEQ.js.map +0 -1
  57. package/dist/guest-auth-provider-BRjJV8Zq.js.map +0 -1
  58. package/dist/guest-auth-provider-DJmuReVa.mjs.map +0 -1
  59. package/dist/guest-auth-provider-DxhZ5sWN.mjs.map +0 -1
  60. package/dist/index-DWodlTFG.mjs.map +0 -1
  61. package/dist/index-MKE9tHpl.js.map +0 -1
  62. package/dist/index.native-CQiw9baS.mjs.map +0 -1
  63. package/dist/index.native-hu3o3zXU.js.map +0 -1
  64. package/dist/injected-evm-wallet-provider-BOPzdm7E.js.map +0 -1
  65. package/dist/injected-evm-wallet-provider-CnJRLE8P.mjs.map +0 -1
  66. package/dist/injected-evm-wallet-provider-DCGnUedJ.mjs.map +0 -1
  67. package/dist/injected-evm-wallet-provider-Xbt2CUPi.js.map +0 -1
  68. package/dist/injected-wallet-provider-7ep-jcal.js.map +0 -1
  69. package/dist/injected-wallet-provider-BUvN5Y6_.js.map +0 -1
  70. package/dist/injected-wallet-provider-Bps6a9IP.mjs.map +0 -1
  71. package/dist/injected-wallet-provider-Dg3MieTq.mjs.map +0 -1
package/dist/index.d.ts CHANGED
@@ -30,6 +30,8 @@ export type { TurnkeySignResult, TurnkeyWallet } from "./auth/turnkey-signer-bri
30
30
  export { startTurnkeyEmailLogin } from "./auth/turnkey-auth";
31
31
  export type { TurnkeyEmailLoginHandle } from "./auth/turnkey-auth";
32
32
  export { getIdToken } from "./utils";
33
+ export { onramp, onrampQuote, onrampStatus } from "./onramp";
34
+ export type { OnrampOptions, OnrampResult, OnrampQuoteInfo, OnrampTransaction } from "./onramp";
33
35
  export { setPlatform, getPlatform } from "./platform";
34
36
  export type { PlatformAdapter, StorageAdapter } from "./platform";
35
37
  export { OffchainAuthProvider, OffchainAuthProviderConfig, } from "./auth/providers/offchain-auth-provider";
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- var index = require('./index-MKE9tHpl.js');
3
+ var index = require('./index-iu8vB4ye.js');
4
4
  var core = require('@bounded-sh/core');
5
5
  require('react');
6
6
 
@@ -39,6 +39,9 @@ exports.loginWithWallet = index.loginWithWallet;
39
39
  exports.logout = index.logout;
40
40
  exports.onAuthLoadingChanged = index.onAuthLoadingChanged;
41
41
  exports.onAuthStateChanged = index.onAuthStateChanged;
42
+ exports.onramp = index.onramp;
43
+ exports.onrampQuote = index.onrampQuote;
44
+ exports.onrampStatus = index.onrampStatus;
42
45
  exports.openBoundedWidget = index.openBoundedWidget;
43
46
  exports.requestEip6963Providers = index.requestEip6963Providers;
44
47
  exports.setPlatform = index.setPlatform;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { $ as BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA, _ as BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA, Z as BUILD_REVIEW_CAPABILITY_SCHEMA, T as BuildsApiError, Y as FAILURE_REASONS, U as NON_TERMINAL_STATES, P as OffchainAuthProvider, X as PARK_REASONS, Q as PrivyExpoProvider, W as RUN_STATES, V as TERMINAL_STATES, I as apps, S as builds, A as completeLoginFromRedirect, C as completeLoginInPopup, k as defaultInjectedEvmProvider, d as defaultInjectedProvider, J as deserializeTransaction, q as getAuthLoading, v as getAuthProvider, m as getCurrentUser, N as getIdToken, L as getOrCreateTurnkeyWallet, g as getPlatform, l as init, t as login, B as loginWithPopup, x as loginWithPrivy, z as loginWithRedirect, y as loginWithWallet, u as logout, p as onAuthLoadingChanged, o as onAuthStateChanged, D as openBoundedWidget, R as requestEip6963Providers, O as setPlatform, w as signInAnonymously, K as signSolanaMessageViaTurnkey, M as startTurnkeyEmailLogin, F as useAuth, H as useBuildRun, G as useQuery } from './index-DWodlTFG.mjs';
1
+ export { a2 as BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA, a1 as BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA, a0 as BUILD_REVIEW_CAPABILITY_SCHEMA, W as BuildsApiError, $ as FAILURE_REASONS, X as NON_TERMINAL_STATES, S as OffchainAuthProvider, _ as PARK_REASONS, T as PrivyExpoProvider, Z as RUN_STATES, Y as TERMINAL_STATES, I as apps, V as builds, A as completeLoginFromRedirect, C as completeLoginInPopup, k as defaultInjectedEvmProvider, d as defaultInjectedProvider, J as deserializeTransaction, q as getAuthLoading, v as getAuthProvider, m as getCurrentUser, N as getIdToken, L as getOrCreateTurnkeyWallet, g as getPlatform, l as init, t as login, B as loginWithPopup, x as loginWithPrivy, z as loginWithRedirect, y as loginWithWallet, u as logout, p as onAuthLoadingChanged, o as onAuthStateChanged, O as onramp, P as onrampQuote, Q as onrampStatus, D as openBoundedWidget, U as requestEip6963Providers, R as setPlatform, w as signInAnonymously, K as signSolanaMessageViaTurnkey, M as startTurnkeyEmailLogin, F as useAuth, H as useBuildRun, G as useQuery } from './index-CQtGkKVd.mjs';
2
2
  export * from '@bounded-sh/core';
3
3
  export { BoundedDeclineError, DECLINE_EVENT_NAME, RATE_LIMIT_EVENT_NAME, ScopedClientReadOnlyError, aggregate, count, createClient, deriveDecline, dispatchDeclineEvent, dispatchRateLimitEvent, docId, get, getConfig, getFiles, getMany, queryAggregate, runExpression, runExpressionMany, runQuery, runQueryMany, search, set, setFile, setMany, signAndSubmitTransaction, signMessage, signTransaction, subscribe } from '@bounded-sh/core';
4
4
  import 'react';
@@ -3475,7 +3475,7 @@ const EMBEDDED_WALLET_UNSUPPORTED = 'Unsupported for embedded smart wallets —
3475
3475
  const NO_EMBEDDED_WALLET = 'This login has no embedded wallet. Enable `"auth": { "wallets": true }` in ' +
3476
3476
  'the app policy so email logins get a non-custodial wallet (@user.address), ' +
3477
3477
  'then sign from a user gesture.';
3478
- function hasDOM$1() {
3478
+ function hasDOM$2() {
3479
3479
  return typeof window !== 'undefined' && typeof document !== 'undefined';
3480
3480
  }
3481
3481
  function decodeJwt$1(token) {
@@ -3535,7 +3535,7 @@ let requestSeq = 0;
3535
3535
  * wallet's owner, and validates that the wallet address matches the account.
3536
3536
  */
3537
3537
  async function signViaSignerPopup(action) {
3538
- if (!hasDOM$1()) {
3538
+ if (!hasDOM$2()) {
3539
3539
  throw new Error('Embedded-wallet signing is only available in the browser (popup-based).');
3540
3540
  }
3541
3541
  const isEvm = action.kind === 'evmTransaction';
@@ -3711,7 +3711,7 @@ const STORED_AUTH_METHOD_KEY$1 = 'bounded_last_auth_method';
3711
3711
  // not by `typeof window` (truthy on RN). OIDC redirect/popup is web-only, but the
3712
3712
  // session it mints is read back through this same selector everywhere.
3713
3713
  const sessionManager = core.getActiveSessionManager;
3714
- function hasDOM() {
3714
+ function hasDOM$1() {
3715
3715
  return typeof window !== 'undefined' && typeof document !== 'undefined';
3716
3716
  }
3717
3717
  function decodeJwt(token) {
@@ -3742,7 +3742,7 @@ const oidcProviderSingleton = {
3742
3742
  const idToken = sessionManager().getIdToken();
3743
3743
  return idToken ? userFromIdToken(idToken) : null;
3744
3744
  },
3745
- async logout() { sessionManager().clearSession(); },
3745
+ async logout() { await sessionManager().clearSession(); },
3746
3746
  async signMessage(_message) {
3747
3747
  throw new Error(hasEmbeddedWallet() ? EMBEDDED_WALLET_UNSUPPORTED : OIDC_NO_WALLET);
3748
3748
  },
@@ -3851,7 +3851,7 @@ async function buildAuthorize(opts) {
3851
3851
  throw new Error('appId is not configured (init the SDK with your appId)');
3852
3852
  // Web: default to the current page so callers don't have to pass redirectUri. Native has no
3853
3853
  // window — it MUST pass an https universal link (the system auth session needs a real URL).
3854
- const redirectUri = opts.redirectUri || (hasDOM() ? window.location.origin + window.location.pathname : undefined);
3854
+ const redirectUri = opts.redirectUri || (hasDOM$1() ? window.location.origin + window.location.pathname : undefined);
3855
3855
  if (!redirectUri)
3856
3856
  throw new Error('redirectUri is required on React Native — pass the https universal link the app owner registered.');
3857
3857
  const { verifier, challenge } = await makePkce();
@@ -3959,7 +3959,7 @@ async function runNativeLogin(opts) {
3959
3959
  async function loginWithRedirect(opts) {
3960
3960
  if (isNative())
3961
3961
  return runNativeLogin(opts);
3962
- if (!hasDOM())
3962
+ if (!hasDOM$1())
3963
3963
  throw new Error('loginWithRedirect requires a browser or a configured React Native platform');
3964
3964
  const { url, pkce } = await buildAuthorize(opts);
3965
3965
  // Stash the verifier + state so the redirect_uri page can finish the exchange. sessionStorage
@@ -3975,7 +3975,7 @@ async function loginWithRedirect(opts) {
3975
3975
  * loginWithRedirect() (which resolves with the User), so there is no separate redirect
3976
3976
  * page to finish from. Calling it on RN is harmless. */
3977
3977
  async function completeLoginFromRedirect() {
3978
- if (!hasDOM())
3978
+ if (!hasDOM$1())
3979
3979
  return null;
3980
3980
  const url = new URL(window.location.href);
3981
3981
  const code = url.searchParams.get('code');
@@ -4021,7 +4021,7 @@ async function completeLoginFromRedirect() {
4021
4021
  * redirect_uri page posts the code back. The redirect_uri page just calls the usual
4022
4022
  * completeLoginFromRedirect() (which auto-detects the popup) — no separate call needed. */
4023
4023
  async function loginWithPopup(opts) {
4024
- if (!hasDOM())
4024
+ if (!hasDOM$1())
4025
4025
  throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
4026
4026
  // Open synchronously, before PKCE/config awaits consume the click's user-
4027
4027
  // activation window. Opening only after buildAuthorize() intermittently
@@ -4161,7 +4161,7 @@ async function loginWithPopup(opts) {
4161
4161
  /** Call this on the redirect_uri page when login was opened via loginWithPopup(): posts the
4162
4162
  * code back to the opener and closes. (For the redirect variant use completeLoginFromRedirect.) */
4163
4163
  function completeLoginInPopup(openerOrigin) {
4164
- if (!hasDOM() || !window.opener)
4164
+ if (!hasDOM$1() || !window.opener)
4165
4165
  return;
4166
4166
  const url = new URL(window.location.href);
4167
4167
  const code = url.searchParams.get('code');
@@ -4362,15 +4362,20 @@ function authProviderConfigKey(config) {
4362
4362
  * so both web (WebSessionManager) and RN (ReactNativeSessionManager) sessions
4363
4363
  * are properly cleared.
4364
4364
  */
4365
- function clearIncompatibleSession() {
4365
+ async function clearIncompatibleSession() {
4366
4366
  if (getStoredAuthMethod() === currentAuthMethod) {
4367
4367
  return false;
4368
4368
  }
4369
4369
  // Clear both managers: on web the RN manager is a no-op (not configured),
4370
4370
  // and on RN the web manager is a no-op (no localStorage).
4371
- core.WebSessionManager.clearSession();
4371
+ //
4372
+ // AWAITED. Session removal is serialized through a cross-tab lock now, so a
4373
+ // fire-and-forget call would let init() reach setCurrentUser while the old
4374
+ // credential was still readable — and a request issued in that gap would
4375
+ // authenticate as the session we are in the middle of discarding.
4376
+ await core.WebSessionManager.clearSession();
4372
4377
  try {
4373
- core.ReactNativeSessionManager.clearSession();
4378
+ await core.ReactNativeSessionManager.clearSession();
4374
4379
  }
4375
4380
  catch ( /* not configured on web */_a) { /* not configured on web */ }
4376
4381
  setStoredAuthMethod(null);
@@ -4469,7 +4474,7 @@ async function getAuthProvider(config) {
4469
4474
  // manager as legacy email sessions. Human login should be started
4470
4475
  // with loginWithRedirect(); this provider is kept as the restore and
4471
4476
  // logout surface for those sessions.
4472
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-BlJI-8w9.js'); })).EmailAuthProvider();
4477
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-BCEdcnRu.js'); })).EmailAuthProvider();
4473
4478
  break;
4474
4479
  case "wallet":
4475
4480
  case "phantom": {
@@ -4488,7 +4493,7 @@ async function getAuthProvider(config) {
4488
4493
  if (!isWalletLoginEnabled(config)) {
4489
4494
  throw walletLoginNotEnabledError(authMethod);
4490
4495
  }
4491
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-7ep-jcal.js'); });
4496
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-TPx8Wzvq.js'); });
4492
4497
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
4493
4498
  break;
4494
4499
  }
@@ -4501,7 +4506,7 @@ async function getAuthProvider(config) {
4501
4506
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
4502
4507
  // to point at a specific wallet or set chainId/domain.
4503
4508
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
4504
- const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-Xbt2CUPi.js'); });
4509
+ const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-CGa692L1.js'); });
4505
4510
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
4506
4511
  break;
4507
4512
  }
@@ -4529,7 +4534,7 @@ async function getAuthProvider(config) {
4529
4534
  break;
4530
4535
  case "guest":
4531
4536
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
4532
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BRjJV8Zq.js'); })).GuestAuthProvider();
4537
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-CrRreOyf.js'); })).GuestAuthProvider();
4533
4538
  break;
4534
4539
  }
4535
4540
  if (!currentAuthProvider) {
@@ -4585,7 +4590,7 @@ async function signInAnonymously() {
4585
4590
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
4586
4591
  // the main bundle for data-only apps. Specifier is a static string literal
4587
4592
  // so bundle-splitting can see it.
4588
- const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BRjJV8Zq.js'); })).GuestAuthProvider();
4593
+ const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-CrRreOyf.js'); })).GuestAuthProvider();
4589
4594
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
4590
4595
  const commit = (user) => {
4591
4596
  // No await/interleaving inside this block: provider/method/config/marker
@@ -4627,7 +4632,7 @@ async function signInAnonymously() {
4627
4632
  }
4628
4633
  catch (_b) {
4629
4634
  try {
4630
- core.getActiveSessionManager().clearSession();
4635
+ await core.getActiveSessionManager().clearSession();
4631
4636
  }
4632
4637
  catch ( /* fail closed below */_c) { /* fail closed below */ }
4633
4638
  }
@@ -4770,7 +4775,7 @@ function getCurrentAuthMethod() {
4770
4775
  // allowlist here: on a trusted origin we end the issuer session with a
4771
4776
  // top-level bounce; anywhere else (custom domains, http dev) we keep the
4772
4777
  // local-only logout instead of stranding the user on the issuer origin.
4773
- function issuerLogoutBounceUrl(issuerBase) {
4778
+ function issuerLogoutBounceUrl(issuerBase, idTokenHint) {
4774
4779
  if (typeof window === 'undefined' || typeof document === 'undefined')
4775
4780
  return null; // RN/Node: no shared cookie jar to clear this way
4776
4781
  try {
@@ -4793,12 +4798,27 @@ function issuerLogoutBounceUrl(issuerBase) {
4793
4798
  if (!trusted)
4794
4799
  return null;
4795
4800
  const base = issuerBase.replace(/\/$/, '');
4796
- return `${base}/logout?redirect_uri=${encodeURIComponent(here.href)}`;
4801
+ // #228: send the id_token_hint so the issuer can bind the post-logout redirect to this
4802
+ // session + app + registered origin. Without it the issuer safely falls back to its own
4803
+ // origin (it no longer trusts a bare suffix match), so the return-to-app bounce is a no-op.
4804
+ const hint = idTokenHint ? `&id_token_hint=${encodeURIComponent(idTokenHint)}` : '';
4805
+ return `${base}/logout?redirect_uri=${encodeURIComponent(here.href)}${hint}`;
4797
4806
  }
4798
4807
  catch (_b) {
4799
4808
  return null;
4800
4809
  }
4801
4810
  }
4811
+ // #228 helper, deliberately OUTSIDE logout(): the logout body is source-guarded
4812
+ // against any `catch` so a revokeSession failure can never be swallowed, and a
4813
+ // missing id token is the one benign absence worth tolerating here.
4814
+ function idTokenHintOrNull(sm) {
4815
+ try {
4816
+ return sm.getIdToken();
4817
+ }
4818
+ catch (_a) {
4819
+ return null; /* no id token to hint with */
4820
+ }
4821
+ }
4802
4822
  async function logout$1(options) {
4803
4823
  // Re-initialize the provider if it was cleared by a prior logout — the exact mirror
4804
4824
  // of login()'s self-heal above. A hosted popup/redirect login (loginWithPopup /
@@ -4823,6 +4843,9 @@ async function logout$1(options) {
4823
4843
  const sm = core.getActiveSessionManager();
4824
4844
  const refreshToken = sm.getRefreshToken();
4825
4845
  const sessionIssuer = sm.getIssuer();
4846
+ // #228: capture the id token NOW, before currentAuthProvider.logout() clears the session, so we
4847
+ // can hand it to the issuer /logout as an id_token_hint (which binds the post-logout redirect).
4848
+ const idTokenHint = idTokenHintOrNull(sm);
4826
4849
  if (refreshToken)
4827
4850
  await core.revokeSession(refreshToken, sessionIssuer !== null && sessionIssuer !== void 0 ? sessionIssuer : undefined);
4828
4851
  // Hosted sessions ('email' covers the whole hosted surface: email OTP +
@@ -4843,7 +4866,7 @@ async function logout$1(options) {
4843
4866
  const issuerBase = sessionIssuer
4844
4867
  || (await core.getConfig()).humanAuthApiUrl
4845
4868
  || 'https://auth.bounded.sh';
4846
- const bounce = issuerLogoutBounceUrl(issuerBase);
4869
+ const bounce = issuerLogoutBounceUrl(issuerBase, idTokenHint);
4847
4870
  // Local state is already cleared, so even if the navigation is stopped
4848
4871
  // (tab close, extension) the app-side logout has fully happened.
4849
4872
  if (bounce)
@@ -4851,6 +4874,22 @@ async function logout$1(options) {
4851
4874
  }
4852
4875
  }
4853
4876
 
4877
+ /**
4878
+ * Subscribed once per process, not per init(): re-registering on every init would
4879
+ * stack duplicate handlers on repeated initialization.
4880
+ */
4881
+ let sessionClearedBridgeRegistered = false;
4882
+ function registerSessionClearedBridge() {
4883
+ if (sessionClearedBridgeRegistered)
4884
+ return;
4885
+ sessionClearedBridgeRegistered = true;
4886
+ core.onSessionCleared(() => {
4887
+ // Core fires this only when storage was ACTUALLY cleared, never on a
4888
+ // transient refresh failure that preserved the session — so this cannot
4889
+ // sign anyone out over a network blip.
4890
+ setCurrentUser(null);
4891
+ });
4892
+ }
4854
4893
  /**
4855
4894
  * Read the active session's idToken synchronously from whichever store is live
4856
4895
  * for this runtime (WebSessionManager on web, ReactNativeSessionManager on RN —
@@ -4908,6 +4947,15 @@ async function init(newConfig) {
4908
4947
  // setting it here makes "no flash" the SDK's default behavior for every consumer.
4909
4948
  setAuthLoading(true);
4910
4949
  try {
4950
+ // Bridge core's "the stored session is gone" signal into SDK auth state.
4951
+ //
4952
+ // Core cannot import this package, and a session can die deep inside a
4953
+ // session manager (dead refresh token, rejected refresh, app-id mismatch).
4954
+ // Before this, setCurrentUser(null) was only ever called by logout() and
4955
+ // init(), so storage could be wiped while `currentUser` stayed populated —
4956
+ // onAuthStateChanged never fired, and every consumer UI went on showing a
4957
+ // connected wallet for a session the server no longer knew about.
4958
+ registerSessionClearedBridge();
4911
4959
  // Get auth provider first
4912
4960
  authProviderInstance = await getAuthProvider(newConfig);
4913
4961
  // Initialize config with auth provider
@@ -4924,7 +4972,12 @@ async function init(newConfig) {
4924
4972
  let user = await authProviderInstance.restoreSession();
4925
4973
  // If a session exists but was created before auth method tracking (legacy session),
4926
4974
  // clear it to avoid mismatches when the app switches auth methods (e.g. email → phantom)
4927
- if (user && clearIncompatibleSession()) {
4975
+ //
4976
+ // AWAITED: the clear is serialized through a cross-tab lock now, and an
4977
+ // unawaited call would return a Promise (always truthy) — nulling the user
4978
+ // unconditionally AND letting setCurrentUser run while the old credential
4979
+ // was still in storage.
4980
+ if (user && await clearIncompatibleSession()) {
4928
4981
  user = null;
4929
4982
  }
4930
4983
  setCurrentUser(user);
@@ -5787,6 +5840,199 @@ async function deserializeTransaction(tx) {
5787
5840
  }
5788
5841
  }
5789
5842
 
5843
+ // bounded.onramp() - fund the signed-in user's wallet with Coinbase Onramp.
5844
+ //
5845
+ // Web-only. Opens Coinbase's hosted checkout (pay.coinbase.com) in a popup
5846
+ // (new-tab fallback) using a single-use session token minted by the Bounded
5847
+ // platform at POST <origin>/__bounded/onramp/session on the app's own origin.
5848
+ // The platform holds the Coinbase credential; the app never sees it. The
5849
+ // session is bound server-side to the caller's verified Bounded JWT, and the
5850
+ // destination defaults to that user's embedded wallet.
5851
+ //
5852
+ // Amount semantics:
5853
+ // amountUsd - exact fiat in; the fee/output breakdown comes back as `quote`.
5854
+ // minAmountOut - a FLOOR on crypto received. The platform quotes Coinbase's
5855
+ // worst-fee payment method (card) and pads by `slippagePad` (default 1%),
5856
+ // so cheaper methods overshoot the floor rather than undershooting it. The
5857
+ // floor holds unless the market moves more than the pad while the user sits
5858
+ // on the checkout screen - which is why completion is VERIFIED: after the
5859
+ // popup closes we poll the platform's status leg and resolve with the
5860
+ // actual filled amount when Coinbase reports it.
5861
+ //
5862
+ // Works on Bounded-served origins (<app>.bounded.page / custom domains). On a
5863
+ // local dev origin the session endpoint does not exist; we fail with a clear
5864
+ // error rather than guessing a host.
5865
+ function hasDOM() {
5866
+ return typeof window !== 'undefined' && typeof document !== 'undefined';
5867
+ }
5868
+ async function authedFetch(path, init) {
5869
+ const idToken = await getIdToken();
5870
+ if (!idToken)
5871
+ throw new Error('onramp: no active session - call login() first');
5872
+ return fetch(`${window.location.origin}${path}`, Object.assign(Object.assign({}, init), { headers: Object.assign(Object.assign({}, init === null || init === void 0 ? void 0 : init.headers), { Authorization: `Bearer ${idToken}` }) }));
5873
+ }
5874
+ async function readError(res) {
5875
+ var _a;
5876
+ try {
5877
+ return (_a = (await res.json()).error) !== null && _a !== void 0 ? _a : '';
5878
+ }
5879
+ catch (_b) {
5880
+ return '';
5881
+ }
5882
+ }
5883
+ function sessionBody(options, extra) {
5884
+ return JSON.stringify(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (options.asset ? { asset: options.asset } : {})), (options.address ? { address: options.address } : {})), (options.amountUsd !== undefined ? { amountUsd: options.amountUsd } : {})), (options.minAmountOut !== undefined ? { minAmountOut: options.minAmountOut } : {})), (options.slippagePad !== undefined ? { slippagePad: options.slippagePad } : {})), extra));
5885
+ }
5886
+ function resolveAddress(options) {
5887
+ var _a, _b;
5888
+ const user = getCurrentUser();
5889
+ const address = (_b = (_a = options.address) !== null && _a !== void 0 ? _a : user === null || user === void 0 ? void 0 : user.address) !== null && _b !== void 0 ? _b : undefined;
5890
+ if (!address) {
5891
+ throw new Error(user
5892
+ ? 'onramp: the signed-in account has no wallet address - pass { address } explicitly'
5893
+ : 'onramp: no signed-in user - call login() first or pass { address }');
5894
+ }
5895
+ return address;
5896
+ }
5897
+ /**
5898
+ * Preview a quote (fees + exact/floor output) WITHOUT opening any window or
5899
+ * consuming a session. Show this to the user, then call onramp() to check out.
5900
+ */
5901
+ async function onrampQuote(options = {}) {
5902
+ if (!hasDOM())
5903
+ throw new Error('onrampQuote is web-only');
5904
+ resolveAddress(options);
5905
+ const res = await authedFetch('/__bounded/onramp/session', {
5906
+ method: 'POST',
5907
+ headers: { 'content-type': 'application/json' },
5908
+ body: sessionBody(options, { quoteOnly: true }),
5909
+ });
5910
+ if (!res.ok)
5911
+ throw new Error(`onrampQuote: failed (${res.status}${await readError(res).then((e) => (e ? `: ${e}` : ''))})`);
5912
+ return (await res.json());
5913
+ }
5914
+ /** The signed-in user's recent onramp transactions (newest first). */
5915
+ async function onrampStatus(sinceMs) {
5916
+ if (!hasDOM())
5917
+ throw new Error('onrampStatus is web-only');
5918
+ const res = await authedFetch(`/__bounded/onramp/status${sinceMs ? `?since=${Math.floor(sinceMs)}` : ''}`);
5919
+ if (!res.ok)
5920
+ throw new Error(`onrampStatus: failed (${res.status})`);
5921
+ return (await res.json()).transactions;
5922
+ }
5923
+ function isSuccess(t) {
5924
+ return /SUCCESS|COMPLETED/i.test(t.status);
5925
+ }
5926
+ function isPending(t) {
5927
+ return /PROGRESS|PENDING|CREATED/i.test(t.status);
5928
+ }
5929
+ async function verifyCompletion(openedAt, asset, timeoutMs) {
5930
+ const deadline = Date.now() + timeoutMs;
5931
+ // 60s of clock-skew slack on `since`: Coinbase stamps server-side.
5932
+ const since = openedAt - 60000;
5933
+ for (;;) {
5934
+ let sawPending = false;
5935
+ try {
5936
+ const txs = await onrampStatus(since);
5937
+ const done = txs.find((t) => isSuccess(t) && (t.asset === null || t.asset === asset));
5938
+ if (done)
5939
+ return done;
5940
+ sawPending = txs.some(isPending);
5941
+ }
5942
+ catch ( /* transient - keep polling until the deadline */_a) { /* transient - keep polling until the deadline */ }
5943
+ // No completed fill and nothing in flight: one grace poll cycle still
5944
+ // runs (Coinbase's record can lag the redirect), but don't burn the
5945
+ // full window when the user plainly abandoned checkout.
5946
+ if (Date.now() >= deadline)
5947
+ return null;
5948
+ if (!sawPending && Date.now() >= openedAt + 12000 && Date.now() - openedAt > timeoutMs / 2)
5949
+ return null;
5950
+ await new Promise((r) => setTimeout(r, 3000));
5951
+ }
5952
+ }
5953
+ /**
5954
+ * Open Coinbase Onramp for the signed-in user. Resolves after the window
5955
+ * closes, with completion VERIFIED against Coinbase when possible.
5956
+ * Call from a click handler - popup blockers require user activation.
5957
+ */
5958
+ async function onramp(options = {}) {
5959
+ var _a;
5960
+ if (!hasDOM())
5961
+ throw new Error('onramp() is web-only');
5962
+ resolveAddress(options);
5963
+ // Open the window synchronously so the click's user-activation window is not
5964
+ // consumed by the fetch below (same hard-won pattern as loginWithPopup).
5965
+ let popup = null;
5966
+ if (!options.newTab) {
5967
+ const w = options.width || 460;
5968
+ const h = options.height || 720;
5969
+ const left = window.screenX + (window.outerWidth - w) / 2;
5970
+ const top = window.screenY + (window.outerHeight - h) / 2;
5971
+ popup = window.open('', 'bounded-onramp', `width=${w},height=${h},left=${left},top=${top}`);
5972
+ }
5973
+ let session;
5974
+ try {
5975
+ const res = await authedFetch('/__bounded/onramp/session', {
5976
+ method: 'POST',
5977
+ headers: { 'content-type': 'application/json' },
5978
+ body: sessionBody(options),
5979
+ });
5980
+ if (!res.ok) {
5981
+ const detail = await readError(res);
5982
+ if (res.status === 404) {
5983
+ throw new Error('onramp: session endpoint not found - onramp works on Bounded-served origins (your deployed app URL), not local dev servers');
5984
+ }
5985
+ throw new Error(`onramp: session mint failed (${res.status}${detail ? `: ${detail}` : ''})`);
5986
+ }
5987
+ session = (await res.json());
5988
+ }
5989
+ catch (error) {
5990
+ try {
5991
+ popup === null || popup === void 0 ? void 0 : popup.close();
5992
+ }
5993
+ catch ( /* ignore */_b) { /* ignore */ }
5994
+ throw error;
5995
+ }
5996
+ const openedAt = Date.now();
5997
+ const base = Object.assign({ address: session.address, asset: session.asset }, (session.quote ? { quote: session.quote } : {}));
5998
+ if (popup) {
5999
+ try {
6000
+ popup.location.replace(session.url);
6001
+ }
6002
+ catch (_c) {
6003
+ try {
6004
+ popup.location.href = session.url;
6005
+ }
6006
+ catch (_d) {
6007
+ try {
6008
+ popup.close();
6009
+ }
6010
+ catch ( /* ignore */_e) { /* ignore */ }
6011
+ throw new Error('onramp: popup failed to navigate - retry with { newTab: true }');
6012
+ }
6013
+ }
6014
+ await new Promise((resolve) => {
6015
+ const timer = setInterval(() => {
6016
+ if (popup.closed) {
6017
+ clearInterval(timer);
6018
+ resolve();
6019
+ }
6020
+ }, 500);
6021
+ });
6022
+ const fill = await verifyCompletion(openedAt, session.asset, (_a = options.completionTimeoutMs) !== null && _a !== void 0 ? _a : 24000);
6023
+ if (fill) {
6024
+ return Object.assign(Object.assign(Object.assign(Object.assign({}, base), { status: 'completed' }), (fill.amountOut !== null ? { actualOut: fill.amountOut } : {})), (fill.txHash ? { txHash: fill.txHash } : {}));
6025
+ }
6026
+ return Object.assign(Object.assign({}, base), { status: 'closed' });
6027
+ }
6028
+ // New-tab path (popup was blocked or explicitly requested): cross-origin tab
6029
+ // close is not observable, so resolve immediately; verify via onrampStatus().
6030
+ const tab = window.open(session.url, '_blank');
6031
+ if (!tab)
6032
+ throw new Error('onramp: window.open was blocked - call onramp() from a click handler');
6033
+ return Object.assign(Object.assign({}, base), { status: 'opened' });
6034
+ }
6035
+
5790
6036
  /**
5791
6037
  * Extracts a human-readable error message from Solana transaction logs.
5792
6038
  */
@@ -5916,7 +6162,7 @@ class PrivyExpoProvider {
5916
6162
  return { provider: this, address: session.address };
5917
6163
  }
5918
6164
  // Stale session — clear and continue to fresh login flow
5919
- core.getActiveSessionManager().clearSession();
6165
+ await core.getActiveSessionManager().clearSession();
5920
6166
  }
5921
6167
  // Trigger Privy login
5922
6168
  const privyUser = await this.privyMethods.login();
@@ -6097,7 +6343,7 @@ class PrivyExpoProvider {
6097
6343
  if ((_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.isAuthenticated) {
6098
6344
  await this.privyMethods.logout();
6099
6345
  }
6100
- core.getActiveSessionManager().clearSession();
6346
+ await core.getActiveSessionManager().clearSession();
6101
6347
  }
6102
6348
  async getNativeMethods() {
6103
6349
  return this.privyMethods;
@@ -6208,6 +6454,9 @@ exports.logout = logout;
6208
6454
  exports.normalizeSolanaRpcUrl = normalizeSolanaRpcUrl;
6209
6455
  exports.onAuthLoadingChanged = onAuthLoadingChanged;
6210
6456
  exports.onAuthStateChanged = onAuthStateChanged;
6457
+ exports.onramp = onramp;
6458
+ exports.onrampQuote = onrampQuote;
6459
+ exports.onrampStatus = onrampStatus;
6211
6460
  exports.resolveSolanaRpcUrl = resolveSolanaRpcUrl;
6212
6461
  exports.setAuthLoading = setAuthLoading;
6213
6462
  exports.setCurrentUser = setCurrentUser;
@@ -6216,4 +6465,4 @@ exports.signInAnonymously = signInAnonymously;
6216
6465
  exports.useAuth = useAuth;
6217
6466
  exports.useBuildRun = useBuildRun;
6218
6467
  exports.useQuery = useQuery;
6219
- //# sourceMappingURL=index.native-hu3o3zXU.js.map
6468
+ //# sourceMappingURL=index.native-CDfx0N6p.js.map