@bounded-sh/client 0.0.59 → 0.0.61

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 (54) hide show
  1. package/dist/{email-auth-provider-N-dfsNnE.js → email-auth-provider-9BwSziWj.js} +2 -2
  2. package/dist/{email-auth-provider-N-dfsNnE.js.map → email-auth-provider-9BwSziWj.js.map} +1 -1
  3. package/dist/{email-auth-provider-DhAMOILG.mjs → email-auth-provider-B3XaJw-G.mjs} +2 -2
  4. package/dist/{email-auth-provider-deB_ADc5.mjs.map → email-auth-provider-B3XaJw-G.mjs.map} +1 -1
  5. package/dist/{email-auth-provider-BSw9FRjg.js → email-auth-provider-BttT2sZK.js} +2 -2
  6. package/dist/{email-auth-provider-BSw9FRjg.js.map → email-auth-provider-BttT2sZK.js.map} +1 -1
  7. package/dist/{email-auth-provider-deB_ADc5.mjs → email-auth-provider-Cb4VKA1E.mjs} +2 -2
  8. package/dist/{email-auth-provider-DhAMOILG.mjs.map → email-auth-provider-Cb4VKA1E.mjs.map} +1 -1
  9. package/dist/{guest-auth-provider-D9wbogwi.mjs → guest-auth-provider-BBPpydtM.mjs} +2 -2
  10. package/dist/{guest-auth-provider-B78Ugfar.mjs.map → guest-auth-provider-BBPpydtM.mjs.map} +1 -1
  11. package/dist/{guest-auth-provider-B78Ugfar.mjs → guest-auth-provider-BBSFawAr.mjs} +2 -2
  12. package/dist/{guest-auth-provider-D9wbogwi.mjs.map → guest-auth-provider-BBSFawAr.mjs.map} +1 -1
  13. package/dist/{guest-auth-provider-Czl48mOP.js → guest-auth-provider-BcR-MQ0g.js} +2 -2
  14. package/dist/{guest-auth-provider-Czl48mOP.js.map → guest-auth-provider-BcR-MQ0g.js.map} +1 -1
  15. package/dist/{guest-auth-provider-BFodq9Rc.js → guest-auth-provider-Di0TRg-z.js} +2 -2
  16. package/dist/{guest-auth-provider-BFodq9Rc.js.map → guest-auth-provider-Di0TRg-z.js.map} +1 -1
  17. package/dist/{index-uu8hKP-p.js → index-C_L497w1.js} +191 -51
  18. package/dist/index-C_L497w1.js.map +1 -0
  19. package/dist/{index-SHt4VZZV.mjs → index-L2R6C5Vl.mjs} +190 -52
  20. package/dist/index-L2R6C5Vl.mjs.map +1 -0
  21. package/dist/index.d.ts +2 -2
  22. package/dist/index.js +3 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/index.mjs +1 -1
  25. package/dist/{index.native-D-8IAEy-.mjs → index.native-BIUs8t7F.mjs} +182 -51
  26. package/dist/index.native-BIUs8t7F.mjs.map +1 -0
  27. package/dist/{index.native-D48QQ0-q.js → index.native-BSheiGj2.js} +183 -50
  28. package/dist/index.native-BSheiGj2.js.map +1 -0
  29. package/dist/index.native.d.ts +2 -2
  30. package/dist/index.native.js +3 -1
  31. package/dist/index.native.js.map +1 -1
  32. package/dist/index.native.mjs +1 -1
  33. package/dist/{injected-evm-wallet-provider-HQMOL_mZ.js → injected-evm-wallet-provider-7XawIUdJ.js} +2 -2
  34. package/dist/{injected-evm-wallet-provider-HQMOL_mZ.js.map → injected-evm-wallet-provider-7XawIUdJ.js.map} +1 -1
  35. package/dist/{injected-evm-wallet-provider-UWvcy-LO.mjs → injected-evm-wallet-provider-DSFDC8fh.mjs} +2 -2
  36. package/dist/{injected-evm-wallet-provider-UWvcy-LO.mjs.map → injected-evm-wallet-provider-DSFDC8fh.mjs.map} +1 -1
  37. package/dist/{injected-evm-wallet-provider-CItxws1D.js → injected-evm-wallet-provider-DgWUaVtt.js} +2 -2
  38. package/dist/{injected-evm-wallet-provider-CItxws1D.js.map → injected-evm-wallet-provider-DgWUaVtt.js.map} +1 -1
  39. package/dist/{injected-evm-wallet-provider-CJsLX16m.mjs → injected-evm-wallet-provider-DnOrVMDX.mjs} +2 -2
  40. package/dist/{injected-evm-wallet-provider-CJsLX16m.mjs.map → injected-evm-wallet-provider-DnOrVMDX.mjs.map} +1 -1
  41. package/dist/{injected-wallet-provider-CmXqreEE.js → injected-wallet-provider-BMDJntMD.js} +2 -2
  42. package/dist/{injected-wallet-provider-CmXqreEE.js.map → injected-wallet-provider-BMDJntMD.js.map} +1 -1
  43. package/dist/{injected-wallet-provider-BihII94U.mjs → injected-wallet-provider-DfJvN3sV.mjs} +2 -2
  44. package/dist/{injected-wallet-provider-BihII94U.mjs.map → injected-wallet-provider-DfJvN3sV.mjs.map} +1 -1
  45. package/dist/{injected-wallet-provider-CNYAynIB.mjs → injected-wallet-provider-ZkPzVj5W.mjs} +2 -2
  46. package/dist/{injected-wallet-provider-CNYAynIB.mjs.map → injected-wallet-provider-ZkPzVj5W.mjs.map} +1 -1
  47. package/dist/{injected-wallet-provider-CVeZgfEt.js → injected-wallet-provider-oCRGuls5.js} +2 -2
  48. package/dist/{injected-wallet-provider-CVeZgfEt.js.map → injected-wallet-provider-oCRGuls5.js.map} +1 -1
  49. package/dist/onramp.d.ts +51 -9
  50. package/package.json +2 -2
  51. package/dist/index-SHt4VZZV.mjs.map +0 -1
  52. package/dist/index-uu8hKP-p.js.map +0 -1
  53. package/dist/index.native-D-8IAEy-.mjs.map +0 -1
  54. package/dist/index.native-D48QQ0-q.js.map +0 -1
@@ -3707,6 +3707,7 @@ const OIDC_NO_WALLET = 'This login has no embedded wallet. Enable `"auth": { "wa
3707
3707
  'then sign from a user gesture.';
3708
3708
  const PKCE_STORE_KEY = 'bounded.oidc.pkce';
3709
3709
  const STORED_AUTH_METHOD_KEY$1 = 'bounded_last_auth_method';
3710
+ const ISSUER_SESSION_KIND_KEY$1 = 'bounded_issuer_session_kind';
3710
3711
  // Active session store (Web on web, RN on React Native) — selected by config,
3711
3712
  // not by `typeof window` (truthy on RN). OIDC redirect/popup is web-only, but the
3712
3713
  // session it mints is read back through this same selector everywhere.
@@ -4213,6 +4214,13 @@ async function exchangeCode(code, verifier, redirectUri) {
4213
4214
  getPlatform().storage.setItem(STORED_AUTH_METHOD_KEY$1, 'email');
4214
4215
  }
4215
4216
  catch ( /* storage unavailable */_b) { /* storage unavailable */ }
4217
+ // OIDC login does establish the issuer's Better Auth browser cookie. Record
4218
+ // that fact separately from the provider name so logout clears the cookie
4219
+ // and returns through the registered app origin.
4220
+ try {
4221
+ getPlatform().storage.setItem(ISSUER_SESSION_KIND_KEY$1, 'hosted');
4222
+ }
4223
+ catch ( /* storage unavailable */_c) { /* storage unavailable */ }
4216
4224
  setCurrentUser(user);
4217
4225
  setAuthProviderInstance(oidcProviderSingleton);
4218
4226
  return user;
@@ -4229,6 +4237,7 @@ const objectIdentityKeys = new WeakMap();
4229
4237
  let nextObjectIdentityKey = 1;
4230
4238
  // --- localStorage helpers: track which auth method created the active session ---
4231
4239
  const STORED_AUTH_METHOD_KEY = 'bounded_last_auth_method';
4240
+ const ISSUER_SESSION_KIND_KEY = 'bounded_issuer_session_kind';
4232
4241
  function getStoredAuthMethod() {
4233
4242
  try {
4234
4243
  return getPlatform().storage.getItem(STORED_AUTH_METHOD_KEY);
@@ -4245,11 +4254,25 @@ function setStoredAuthMethod(method) {
4245
4254
  else {
4246
4255
  getPlatform().storage.removeItem(STORED_AUTH_METHOD_KEY);
4247
4256
  }
4257
+ // Non-email providers cannot own a Better Auth browser session. Clear
4258
+ // any prior hosted/inline classification when the active provider is
4259
+ // replaced or the session is removed.
4260
+ if (method !== 'email') {
4261
+ getPlatform().storage.removeItem(ISSUER_SESSION_KIND_KEY);
4262
+ }
4248
4263
  }
4249
4264
  catch (_a) {
4250
4265
  // storage might not be available
4251
4266
  }
4252
4267
  }
4268
+ function getIssuerSessionKind() {
4269
+ try {
4270
+ return getPlatform().storage.getItem(ISSUER_SESSION_KIND_KEY);
4271
+ }
4272
+ catch (_a) {
4273
+ return null;
4274
+ }
4275
+ }
4253
4276
  function getStoredIdToken() {
4254
4277
  try {
4255
4278
  return core.getActiveSessionManager().getIdToken();
@@ -4474,7 +4497,7 @@ async function getAuthProvider(config) {
4474
4497
  // manager as legacy email sessions. Human login should be started
4475
4498
  // with loginWithRedirect(); this provider is kept as the restore and
4476
4499
  // logout surface for those sessions.
4477
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-BSw9FRjg.js'); })).EmailAuthProvider();
4500
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-BttT2sZK.js'); })).EmailAuthProvider();
4478
4501
  break;
4479
4502
  case "wallet":
4480
4503
  case "phantom": {
@@ -4493,7 +4516,7 @@ async function getAuthProvider(config) {
4493
4516
  if (!isWalletLoginEnabled(config)) {
4494
4517
  throw walletLoginNotEnabledError(authMethod);
4495
4518
  }
4496
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-CmXqreEE.js'); });
4519
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-BMDJntMD.js'); });
4497
4520
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
4498
4521
  break;
4499
4522
  }
@@ -4506,7 +4529,7 @@ async function getAuthProvider(config) {
4506
4529
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
4507
4530
  // to point at a specific wallet or set chainId/domain.
4508
4531
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
4509
- const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-HQMOL_mZ.js'); });
4532
+ const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-7XawIUdJ.js'); });
4510
4533
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
4511
4534
  break;
4512
4535
  }
@@ -4534,7 +4557,7 @@ async function getAuthProvider(config) {
4534
4557
  break;
4535
4558
  case "guest":
4536
4559
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
4537
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-Czl48mOP.js'); })).GuestAuthProvider();
4560
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BcR-MQ0g.js'); })).GuestAuthProvider();
4538
4561
  break;
4539
4562
  }
4540
4563
  if (!currentAuthProvider) {
@@ -4590,7 +4613,7 @@ async function signInAnonymously() {
4590
4613
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
4591
4614
  // the main bundle for data-only apps. Specifier is a static string literal
4592
4615
  // so bundle-splitting can see it.
4593
- const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-Czl48mOP.js'); })).GuestAuthProvider();
4616
+ const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BcR-MQ0g.js'); })).GuestAuthProvider();
4594
4617
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
4595
4618
  const commit = (user) => {
4596
4619
  // No await/interleaving inside this block: provider/method/config/marker
@@ -4775,7 +4798,7 @@ function getCurrentAuthMethod() {
4775
4798
  // allowlist here: on a trusted origin we end the issuer session with a
4776
4799
  // top-level bounce; anywhere else (custom domains, http dev) we keep the
4777
4800
  // local-only logout instead of stranding the user on the issuer origin.
4778
- function issuerLogoutBounceUrl(issuerBase) {
4801
+ function issuerLogoutBounceUrl(issuerBase, idTokenHint) {
4779
4802
  if (typeof window === 'undefined' || typeof document === 'undefined')
4780
4803
  return null; // RN/Node: no shared cookie jar to clear this way
4781
4804
  try {
@@ -4798,12 +4821,27 @@ function issuerLogoutBounceUrl(issuerBase) {
4798
4821
  if (!trusted)
4799
4822
  return null;
4800
4823
  const base = issuerBase.replace(/\/$/, '');
4801
- return `${base}/logout?redirect_uri=${encodeURIComponent(here.href)}`;
4824
+ // #228: send the id_token_hint so the issuer can bind the post-logout redirect to this
4825
+ // session + app + registered origin. Without it the issuer safely falls back to its own
4826
+ // origin (it no longer trusts a bare suffix match), so the return-to-app bounce is a no-op.
4827
+ const hint = idTokenHint ? `&id_token_hint=${encodeURIComponent(idTokenHint)}` : '';
4828
+ return `${base}/logout?redirect_uri=${encodeURIComponent(here.href)}${hint}`;
4802
4829
  }
4803
4830
  catch (_b) {
4804
4831
  return null;
4805
4832
  }
4806
4833
  }
4834
+ // #228 helper, deliberately OUTSIDE logout(): the logout body is source-guarded
4835
+ // against any `catch` so a revokeSession failure can never be swallowed, and a
4836
+ // missing id token is the one benign absence worth tolerating here.
4837
+ function idTokenHintOrNull(sm) {
4838
+ try {
4839
+ return sm.getIdToken();
4840
+ }
4841
+ catch (_a) {
4842
+ return null; /* no id token to hint with */
4843
+ }
4844
+ }
4807
4845
  async function logout$1(options) {
4808
4846
  // Re-initialize the provider if it was cleared by a prior logout — the exact mirror
4809
4847
  // of login()'s self-heal above. A hosted popup/redirect login (loginWithPopup /
@@ -4828,17 +4866,20 @@ async function logout$1(options) {
4828
4866
  const sm = core.getActiveSessionManager();
4829
4867
  const refreshToken = sm.getRefreshToken();
4830
4868
  const sessionIssuer = sm.getIssuer();
4869
+ // #228: capture the id token NOW, before currentAuthProvider.logout() clears the session, so we
4870
+ // can hand it to the issuer /logout as an id_token_hint (which binds the post-logout redirect).
4871
+ const idTokenHint = idTokenHintOrNull(sm);
4831
4872
  if (refreshToken)
4832
4873
  await core.revokeSession(refreshToken, sessionIssuer !== null && sessionIssuer !== void 0 ? sessionIssuer : undefined);
4833
- // Hosted sessions ('email' covers the whole hosted surface: email OTP +
4834
- // social + the OIDC redirect/popup flows) are the ones with an issuer
4835
- // cookie; wallet and guest sessions have nothing hosted to end.
4836
- // Check the STORED marker as well as the in-memory method: the hosted
4837
- // OIDC flows (loginWithPopup / completeLoginFromRedirect) write the
4838
- // 'email' marker via exchangeCode() but never touch currentAuthMethod,
4839
- // which stays 'guest'/explicit even after a hosted upgrade. Worst case of
4840
- // a false positive is a harmless idempotent issuer bounce.
4841
- const wasHosted = currentAuthMethod === 'email' || getStoredAuthMethod() === 'email';
4874
+ // Only OIDC-hosted sessions own an issuer browser cookie. Turnkey-native
4875
+ // email OTP is also an email auth method, but it mints the app session
4876
+ // inline and has no issuer cookie to clear. Use the explicit session-kind
4877
+ // marker when present. The auth-method fallback preserves correct logout
4878
+ // behavior for hosted sessions created by older SDK versions.
4879
+ const issuerSessionKind = getIssuerSessionKind();
4880
+ const wasHosted = issuerSessionKind === 'hosted'
4881
+ || (issuerSessionKind === null
4882
+ && (currentAuthMethod === 'email' || getStoredAuthMethod() === 'email'));
4842
4883
  await currentAuthProvider.logout();
4843
4884
  setStoredAuthMethod(null);
4844
4885
  currentAuthProvider = null;
@@ -4848,7 +4889,7 @@ async function logout$1(options) {
4848
4889
  const issuerBase = sessionIssuer
4849
4890
  || (await core.getConfig()).humanAuthApiUrl
4850
4891
  || 'https://auth.bounded.sh';
4851
- const bounce = issuerLogoutBounceUrl(issuerBase);
4892
+ const bounce = issuerLogoutBounceUrl(issuerBase, idTokenHint);
4852
4893
  // Local state is already cleared, so even if the navigation is stopped
4853
4894
  // (tab close, extension) the app-side logout has fully happened.
4854
4895
  if (bounce)
@@ -5831,30 +5872,117 @@ async function deserializeTransaction(tx) {
5831
5872
  // session is bound server-side to the caller's verified Bounded JWT, and the
5832
5873
  // destination defaults to that user's embedded wallet.
5833
5874
  //
5875
+ // Amount semantics:
5876
+ // amountUsd - exact fiat in; the fee/output breakdown comes back as `quote`.
5877
+ // minAmountOut - a FLOOR on crypto received. The platform quotes Coinbase's
5878
+ // worst-fee payment method (card) and pads by `slippagePad` (default 1%),
5879
+ // so cheaper methods overshoot the floor rather than undershooting it. The
5880
+ // floor holds unless the market moves more than the pad while the user sits
5881
+ // on the checkout screen - which is why completion is VERIFIED: after the
5882
+ // popup closes we poll the platform's status leg and resolve with the
5883
+ // actual filled amount when Coinbase reports it.
5884
+ //
5834
5885
  // Works on Bounded-served origins (<app>.bounded.page / custom domains). On a
5835
5886
  // local dev origin the session endpoint does not exist; we fail with a clear
5836
5887
  // error rather than guessing a host.
5837
5888
  function hasDOM() {
5838
5889
  return typeof window !== 'undefined' && typeof document !== 'undefined';
5839
5890
  }
5891
+ async function authedFetch(path, init) {
5892
+ const idToken = await getIdToken();
5893
+ if (!idToken)
5894
+ throw new Error('onramp: no active session - call login() first');
5895
+ 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}` }) }));
5896
+ }
5897
+ async function readError(res) {
5898
+ var _a;
5899
+ try {
5900
+ return (_a = (await res.json()).error) !== null && _a !== void 0 ? _a : '';
5901
+ }
5902
+ catch (_b) {
5903
+ return '';
5904
+ }
5905
+ }
5906
+ function sessionBody(options, extra) {
5907
+ 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));
5908
+ }
5909
+ function resolveAddress(options) {
5910
+ var _a, _b;
5911
+ const user = getCurrentUser();
5912
+ 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;
5913
+ if (!address) {
5914
+ throw new Error(user
5915
+ ? 'onramp: the signed-in account has no wallet address - pass { address } explicitly'
5916
+ : 'onramp: no signed-in user - call login() first or pass { address }');
5917
+ }
5918
+ return address;
5919
+ }
5840
5920
  /**
5841
- * Open Coinbase Onramp for the signed-in user. Resolves when the popup closes.
5921
+ * Preview a quote (fees + exact/floor output) WITHOUT opening any window or
5922
+ * consuming a session. Show this to the user, then call onramp() to check out.
5923
+ */
5924
+ async function onrampQuote(options = {}) {
5925
+ if (!hasDOM())
5926
+ throw new Error('onrampQuote is web-only');
5927
+ resolveAddress(options);
5928
+ const res = await authedFetch('/__bounded/onramp/session', {
5929
+ method: 'POST',
5930
+ headers: { 'content-type': 'application/json' },
5931
+ body: sessionBody(options, { quoteOnly: true }),
5932
+ });
5933
+ if (!res.ok)
5934
+ throw new Error(`onrampQuote: failed (${res.status}${await readError(res).then((e) => (e ? `: ${e}` : ''))})`);
5935
+ return (await res.json());
5936
+ }
5937
+ /** The signed-in user's recent onramp transactions (newest first). */
5938
+ async function onrampStatus(sinceMs) {
5939
+ if (!hasDOM())
5940
+ throw new Error('onrampStatus is web-only');
5941
+ const res = await authedFetch(`/__bounded/onramp/status${sinceMs ? `?since=${Math.floor(sinceMs)}` : ''}`);
5942
+ if (!res.ok)
5943
+ throw new Error(`onrampStatus: failed (${res.status})`);
5944
+ return (await res.json()).transactions;
5945
+ }
5946
+ function isSuccess(t) {
5947
+ return /SUCCESS|COMPLETED/i.test(t.status);
5948
+ }
5949
+ function isPending(t) {
5950
+ return /PROGRESS|PENDING|CREATED/i.test(t.status);
5951
+ }
5952
+ async function verifyCompletion(openedAt, asset, timeoutMs) {
5953
+ const deadline = Date.now() + timeoutMs;
5954
+ // 60s of clock-skew slack on `since`: Coinbase stamps server-side.
5955
+ const since = openedAt - 60000;
5956
+ for (;;) {
5957
+ let sawPending = false;
5958
+ try {
5959
+ const txs = await onrampStatus(since);
5960
+ const done = txs.find((t) => isSuccess(t) && (t.asset === null || t.asset === asset));
5961
+ if (done)
5962
+ return done;
5963
+ sawPending = txs.some(isPending);
5964
+ }
5965
+ catch ( /* transient - keep polling until the deadline */_a) { /* transient - keep polling until the deadline */ }
5966
+ // No completed fill and nothing in flight: one grace poll cycle still
5967
+ // runs (Coinbase's record can lag the redirect), but don't burn the
5968
+ // full window when the user plainly abandoned checkout.
5969
+ if (Date.now() >= deadline)
5970
+ return null;
5971
+ if (!sawPending && Date.now() >= openedAt + 12000 && Date.now() - openedAt > timeoutMs / 2)
5972
+ return null;
5973
+ await new Promise((r) => setTimeout(r, 3000));
5974
+ }
5975
+ }
5976
+ /**
5977
+ * Open Coinbase Onramp for the signed-in user. Resolves after the window
5978
+ * closes, with completion VERIFIED against Coinbase when possible.
5842
5979
  * Call from a click handler - popup blockers require user activation.
5843
5980
  */
5844
5981
  async function onramp(options = {}) {
5845
- var _a, _b, _c;
5982
+ var _a;
5846
5983
  if (!hasDOM())
5847
5984
  throw new Error('onramp() is web-only');
5848
- const user = getCurrentUser();
5849
- 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;
5850
- if (!address) {
5851
- throw new Error(user
5852
- ? 'onramp(): the signed-in account has no wallet address - pass { address } explicitly'
5853
- : 'onramp(): no signed-in user - call login() first or pass { address }');
5854
- }
5855
- const idToken = await getIdToken();
5856
- if (!idToken)
5857
- throw new Error('onramp(): no active session - call login() first');
5985
+ resolveAddress(options);
5858
5986
  // Open the window synchronously so the click's user-activation window is not
5859
5987
  // consumed by the fetch below (same hard-won pattern as loginWithPopup).
5860
5988
  let popup = null;
@@ -5867,21 +5995,17 @@ async function onramp(options = {}) {
5867
5995
  }
5868
5996
  let session;
5869
5997
  try {
5870
- const res = await fetch(`${window.location.origin}/__bounded/onramp/session`, {
5998
+ const res = await authedFetch('/__bounded/onramp/session', {
5871
5999
  method: 'POST',
5872
- headers: { Authorization: `Bearer ${idToken}`, 'content-type': 'application/json' },
5873
- body: JSON.stringify(Object.assign(Object.assign(Object.assign({}, (options.asset ? { asset: options.asset } : {})), (options.address ? { address: options.address } : {})), (options.amountUsd !== undefined ? { amountUsd: options.amountUsd } : {}))),
6000
+ headers: { 'content-type': 'application/json' },
6001
+ body: sessionBody(options),
5874
6002
  });
5875
6003
  if (!res.ok) {
5876
- let detail = '';
5877
- try {
5878
- detail = (_c = (await res.json()).error) !== null && _c !== void 0 ? _c : '';
5879
- }
5880
- catch ( /* keep status only */_d) { /* keep status only */ }
6004
+ const detail = await readError(res);
5881
6005
  if (res.status === 404) {
5882
- throw new Error('onramp(): session endpoint not found - onramp works on Bounded-served origins (your deployed app URL), not local dev servers');
6006
+ throw new Error('onramp: session endpoint not found - onramp works on Bounded-served origins (your deployed app URL), not local dev servers');
5883
6007
  }
5884
- throw new Error(`onramp(): session mint failed (${res.status}${detail ? `: ${detail}` : ''})`);
6008
+ throw new Error(`onramp: session mint failed (${res.status}${detail ? `: ${detail}` : ''})`);
5885
6009
  }
5886
6010
  session = (await res.json());
5887
6011
  }
@@ -5889,40 +6013,47 @@ async function onramp(options = {}) {
5889
6013
  try {
5890
6014
  popup === null || popup === void 0 ? void 0 : popup.close();
5891
6015
  }
5892
- catch ( /* ignore */_e) { /* ignore */ }
6016
+ catch ( /* ignore */_b) { /* ignore */ }
5893
6017
  throw error;
5894
6018
  }
6019
+ const openedAt = Date.now();
6020
+ const base = Object.assign({ address: session.address, asset: session.asset }, (session.quote ? { quote: session.quote } : {}));
5895
6021
  if (popup) {
5896
6022
  try {
5897
6023
  popup.location.replace(session.url);
5898
6024
  }
5899
- catch (_f) {
6025
+ catch (_c) {
5900
6026
  try {
5901
6027
  popup.location.href = session.url;
5902
6028
  }
5903
- catch (_g) {
6029
+ catch (_d) {
5904
6030
  try {
5905
6031
  popup.close();
5906
6032
  }
5907
- catch ( /* ignore */_h) { /* ignore */ }
5908
- throw new Error('onramp(): popup failed to navigate - retry with { newTab: true }');
6033
+ catch ( /* ignore */_e) { /* ignore */ }
6034
+ throw new Error('onramp: popup failed to navigate - retry with { newTab: true }');
5909
6035
  }
5910
6036
  }
5911
- return await new Promise((resolve) => {
6037
+ await new Promise((resolve) => {
5912
6038
  const timer = setInterval(() => {
5913
6039
  if (popup.closed) {
5914
6040
  clearInterval(timer);
5915
- resolve({ status: 'closed', address: session.address, asset: session.asset });
6041
+ resolve();
5916
6042
  }
5917
6043
  }, 500);
5918
6044
  });
6045
+ const fill = await verifyCompletion(openedAt, session.asset, (_a = options.completionTimeoutMs) !== null && _a !== void 0 ? _a : 24000);
6046
+ if (fill) {
6047
+ return Object.assign(Object.assign(Object.assign(Object.assign({}, base), { status: 'completed' }), (fill.amountOut !== null ? { actualOut: fill.amountOut } : {})), (fill.txHash ? { txHash: fill.txHash } : {}));
6048
+ }
6049
+ return Object.assign(Object.assign({}, base), { status: 'closed' });
5919
6050
  }
5920
6051
  // New-tab path (popup was blocked or explicitly requested): cross-origin tab
5921
- // close is not observable, so resolve immediately.
6052
+ // close is not observable, so resolve immediately; verify via onrampStatus().
5922
6053
  const tab = window.open(session.url, '_blank');
5923
6054
  if (!tab)
5924
- throw new Error('onramp(): window.open was blocked - call onramp() from a click handler');
5925
- return { status: 'opened', address: session.address, asset: session.asset };
6055
+ throw new Error('onramp: window.open was blocked - call onramp() from a click handler');
6056
+ return Object.assign(Object.assign({}, base), { status: 'opened' });
5926
6057
  }
5927
6058
 
5928
6059
  /**
@@ -6347,6 +6478,8 @@ exports.normalizeSolanaRpcUrl = normalizeSolanaRpcUrl;
6347
6478
  exports.onAuthLoadingChanged = onAuthLoadingChanged;
6348
6479
  exports.onAuthStateChanged = onAuthStateChanged;
6349
6480
  exports.onramp = onramp;
6481
+ exports.onrampQuote = onrampQuote;
6482
+ exports.onrampStatus = onrampStatus;
6350
6483
  exports.resolveSolanaRpcUrl = resolveSolanaRpcUrl;
6351
6484
  exports.setAuthLoading = setAuthLoading;
6352
6485
  exports.setCurrentUser = setCurrentUser;
@@ -6355,4 +6488,4 @@ exports.signInAnonymously = signInAnonymously;
6355
6488
  exports.useAuth = useAuth;
6356
6489
  exports.useBuildRun = useBuildRun;
6357
6490
  exports.useQuery = useQuery;
6358
- //# sourceMappingURL=index.native-D48QQ0-q.js.map
6491
+ //# sourceMappingURL=index.native-BSheiGj2.js.map