@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
package/dist/index.d.ts CHANGED
@@ -30,8 +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 } from "./onramp";
34
- export type { OnrampOptions, OnrampResult } from "./onramp";
33
+ export { onramp, onrampQuote, onrampStatus } from "./onramp";
34
+ export type { OnrampOptions, OnrampResult, OnrampQuoteInfo, OnrampTransaction } from "./onramp";
35
35
  export { setPlatform, getPlatform } from "./platform";
36
36
  export type { PlatformAdapter, StorageAdapter } from "./platform";
37
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-uu8hKP-p.js');
3
+ var index = require('./index-C_L497w1.js');
4
4
  var core = require('@bounded-sh/core');
5
5
  require('react');
6
6
 
@@ -40,6 +40,8 @@ exports.logout = index.logout;
40
40
  exports.onAuthLoadingChanged = index.onAuthLoadingChanged;
41
41
  exports.onAuthStateChanged = index.onAuthStateChanged;
42
42
  exports.onramp = index.onramp;
43
+ exports.onrampQuote = index.onrampQuote;
44
+ exports.onrampStatus = index.onrampStatus;
43
45
  exports.openBoundedWidget = index.openBoundedWidget;
44
46
  exports.requestEip6963Providers = index.requestEip6963Providers;
45
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 { a0 as BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA, $ as BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA, _ as BUILD_REVIEW_CAPABILITY_SCHEMA, U as BuildsApiError, Z as FAILURE_REASONS, V as NON_TERMINAL_STATES, Q as OffchainAuthProvider, Y as PARK_REASONS, R as PrivyExpoProvider, X as RUN_STATES, W as TERMINAL_STATES, I as apps, T 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, D as openBoundedWidget, S as requestEip6963Providers, P as setPlatform, w as signInAnonymously, K as signSolanaMessageViaTurnkey, M as startTurnkeyEmailLogin, F as useAuth, H as useBuildRun, G as useQuery } from './index-SHt4VZZV.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-L2R6C5Vl.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';
@@ -3686,6 +3686,7 @@ const OIDC_NO_WALLET = 'This login has no embedded wallet. Enable `"auth": { "wa
3686
3686
  'then sign from a user gesture.';
3687
3687
  const PKCE_STORE_KEY = 'bounded.oidc.pkce';
3688
3688
  const STORED_AUTH_METHOD_KEY$1 = 'bounded_last_auth_method';
3689
+ const ISSUER_SESSION_KIND_KEY$1 = 'bounded_issuer_session_kind';
3689
3690
  // Active session store (Web on web, RN on React Native) — selected by config,
3690
3691
  // not by `typeof window` (truthy on RN). OIDC redirect/popup is web-only, but the
3691
3692
  // session it mints is read back through this same selector everywhere.
@@ -4192,6 +4193,13 @@ async function exchangeCode(code, verifier, redirectUri) {
4192
4193
  getPlatform().storage.setItem(STORED_AUTH_METHOD_KEY$1, 'email');
4193
4194
  }
4194
4195
  catch ( /* storage unavailable */_b) { /* storage unavailable */ }
4196
+ // OIDC login does establish the issuer's Better Auth browser cookie. Record
4197
+ // that fact separately from the provider name so logout clears the cookie
4198
+ // and returns through the registered app origin.
4199
+ try {
4200
+ getPlatform().storage.setItem(ISSUER_SESSION_KIND_KEY$1, 'hosted');
4201
+ }
4202
+ catch ( /* storage unavailable */_c) { /* storage unavailable */ }
4195
4203
  setCurrentUser(user);
4196
4204
  setAuthProviderInstance(oidcProviderSingleton);
4197
4205
  return user;
@@ -4208,6 +4216,7 @@ const objectIdentityKeys = new WeakMap();
4208
4216
  let nextObjectIdentityKey = 1;
4209
4217
  // --- localStorage helpers: track which auth method created the active session ---
4210
4218
  const STORED_AUTH_METHOD_KEY = 'bounded_last_auth_method';
4219
+ const ISSUER_SESSION_KIND_KEY = 'bounded_issuer_session_kind';
4211
4220
  function getStoredAuthMethod() {
4212
4221
  try {
4213
4222
  return getPlatform().storage.getItem(STORED_AUTH_METHOD_KEY);
@@ -4224,11 +4233,25 @@ function setStoredAuthMethod(method) {
4224
4233
  else {
4225
4234
  getPlatform().storage.removeItem(STORED_AUTH_METHOD_KEY);
4226
4235
  }
4236
+ // Non-email providers cannot own a Better Auth browser session. Clear
4237
+ // any prior hosted/inline classification when the active provider is
4238
+ // replaced or the session is removed.
4239
+ if (method !== 'email') {
4240
+ getPlatform().storage.removeItem(ISSUER_SESSION_KIND_KEY);
4241
+ }
4227
4242
  }
4228
4243
  catch (_a) {
4229
4244
  // storage might not be available
4230
4245
  }
4231
4246
  }
4247
+ function getIssuerSessionKind() {
4248
+ try {
4249
+ return getPlatform().storage.getItem(ISSUER_SESSION_KIND_KEY);
4250
+ }
4251
+ catch (_a) {
4252
+ return null;
4253
+ }
4254
+ }
4232
4255
  function getStoredIdToken() {
4233
4256
  try {
4234
4257
  return getActiveSessionManager().getIdToken();
@@ -4453,7 +4476,7 @@ async function getAuthProvider(config) {
4453
4476
  // manager as legacy email sessions. Human login should be started
4454
4477
  // with loginWithRedirect(); this provider is kept as the restore and
4455
4478
  // logout surface for those sessions.
4456
- currentAuthProvider = new (await import('./email-auth-provider-deB_ADc5.mjs')).EmailAuthProvider();
4479
+ currentAuthProvider = new (await import('./email-auth-provider-Cb4VKA1E.mjs')).EmailAuthProvider();
4457
4480
  break;
4458
4481
  case "wallet":
4459
4482
  case "phantom": {
@@ -4472,7 +4495,7 @@ async function getAuthProvider(config) {
4472
4495
  if (!isWalletLoginEnabled(config)) {
4473
4496
  throw walletLoginNotEnabledError(authMethod);
4474
4497
  }
4475
- const { InjectedWalletProvider } = await import('./injected-wallet-provider-CNYAynIB.mjs');
4498
+ const { InjectedWalletProvider } = await import('./injected-wallet-provider-ZkPzVj5W.mjs');
4476
4499
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
4477
4500
  break;
4478
4501
  }
@@ -4485,7 +4508,7 @@ async function getAuthProvider(config) {
4485
4508
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
4486
4509
  // to point at a specific wallet or set chainId/domain.
4487
4510
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
4488
- const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-CJsLX16m.mjs');
4511
+ const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-DnOrVMDX.mjs');
4489
4512
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
4490
4513
  break;
4491
4514
  }
@@ -4513,7 +4536,7 @@ async function getAuthProvider(config) {
4513
4536
  break;
4514
4537
  case "guest":
4515
4538
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
4516
- currentAuthProvider = new (await import('./guest-auth-provider-D9wbogwi.mjs')).GuestAuthProvider();
4539
+ currentAuthProvider = new (await import('./guest-auth-provider-BBPpydtM.mjs')).GuestAuthProvider();
4517
4540
  break;
4518
4541
  }
4519
4542
  if (!currentAuthProvider) {
@@ -4569,7 +4592,7 @@ async function signInAnonymously() {
4569
4592
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
4570
4593
  // the main bundle for data-only apps. Specifier is a static string literal
4571
4594
  // so bundle-splitting can see it.
4572
- const provider = new (await import('./guest-auth-provider-D9wbogwi.mjs')).GuestAuthProvider();
4595
+ const provider = new (await import('./guest-auth-provider-BBPpydtM.mjs')).GuestAuthProvider();
4573
4596
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
4574
4597
  const commit = (user) => {
4575
4598
  // No await/interleaving inside this block: provider/method/config/marker
@@ -4754,7 +4777,7 @@ function getCurrentAuthMethod() {
4754
4777
  // allowlist here: on a trusted origin we end the issuer session with a
4755
4778
  // top-level bounce; anywhere else (custom domains, http dev) we keep the
4756
4779
  // local-only logout instead of stranding the user on the issuer origin.
4757
- function issuerLogoutBounceUrl(issuerBase) {
4780
+ function issuerLogoutBounceUrl(issuerBase, idTokenHint) {
4758
4781
  if (typeof window === 'undefined' || typeof document === 'undefined')
4759
4782
  return null; // RN/Node: no shared cookie jar to clear this way
4760
4783
  try {
@@ -4777,12 +4800,27 @@ function issuerLogoutBounceUrl(issuerBase) {
4777
4800
  if (!trusted)
4778
4801
  return null;
4779
4802
  const base = issuerBase.replace(/\/$/, '');
4780
- return `${base}/logout?redirect_uri=${encodeURIComponent(here.href)}`;
4803
+ // #228: send the id_token_hint so the issuer can bind the post-logout redirect to this
4804
+ // session + app + registered origin. Without it the issuer safely falls back to its own
4805
+ // origin (it no longer trusts a bare suffix match), so the return-to-app bounce is a no-op.
4806
+ const hint = idTokenHint ? `&id_token_hint=${encodeURIComponent(idTokenHint)}` : '';
4807
+ return `${base}/logout?redirect_uri=${encodeURIComponent(here.href)}${hint}`;
4781
4808
  }
4782
4809
  catch (_b) {
4783
4810
  return null;
4784
4811
  }
4785
4812
  }
4813
+ // #228 helper, deliberately OUTSIDE logout(): the logout body is source-guarded
4814
+ // against any `catch` so a revokeSession failure can never be swallowed, and a
4815
+ // missing id token is the one benign absence worth tolerating here.
4816
+ function idTokenHintOrNull(sm) {
4817
+ try {
4818
+ return sm.getIdToken();
4819
+ }
4820
+ catch (_a) {
4821
+ return null; /* no id token to hint with */
4822
+ }
4823
+ }
4786
4824
  async function logout$1(options) {
4787
4825
  // Re-initialize the provider if it was cleared by a prior logout — the exact mirror
4788
4826
  // of login()'s self-heal above. A hosted popup/redirect login (loginWithPopup /
@@ -4807,17 +4845,20 @@ async function logout$1(options) {
4807
4845
  const sm = getActiveSessionManager();
4808
4846
  const refreshToken = sm.getRefreshToken();
4809
4847
  const sessionIssuer = sm.getIssuer();
4848
+ // #228: capture the id token NOW, before currentAuthProvider.logout() clears the session, so we
4849
+ // can hand it to the issuer /logout as an id_token_hint (which binds the post-logout redirect).
4850
+ const idTokenHint = idTokenHintOrNull(sm);
4810
4851
  if (refreshToken)
4811
4852
  await revokeSession(refreshToken, sessionIssuer !== null && sessionIssuer !== void 0 ? sessionIssuer : undefined);
4812
- // Hosted sessions ('email' covers the whole hosted surface: email OTP +
4813
- // social + the OIDC redirect/popup flows) are the ones with an issuer
4814
- // cookie; wallet and guest sessions have nothing hosted to end.
4815
- // Check the STORED marker as well as the in-memory method: the hosted
4816
- // OIDC flows (loginWithPopup / completeLoginFromRedirect) write the
4817
- // 'email' marker via exchangeCode() but never touch currentAuthMethod,
4818
- // which stays 'guest'/explicit even after a hosted upgrade. Worst case of
4819
- // a false positive is a harmless idempotent issuer bounce.
4820
- const wasHosted = currentAuthMethod === 'email' || getStoredAuthMethod() === 'email';
4853
+ // Only OIDC-hosted sessions own an issuer browser cookie. Turnkey-native
4854
+ // email OTP is also an email auth method, but it mints the app session
4855
+ // inline and has no issuer cookie to clear. Use the explicit session-kind
4856
+ // marker when present. The auth-method fallback preserves correct logout
4857
+ // behavior for hosted sessions created by older SDK versions.
4858
+ const issuerSessionKind = getIssuerSessionKind();
4859
+ const wasHosted = issuerSessionKind === 'hosted'
4860
+ || (issuerSessionKind === null
4861
+ && (currentAuthMethod === 'email' || getStoredAuthMethod() === 'email'));
4821
4862
  await currentAuthProvider.logout();
4822
4863
  setStoredAuthMethod(null);
4823
4864
  currentAuthProvider = null;
@@ -4827,7 +4868,7 @@ async function logout$1(options) {
4827
4868
  const issuerBase = sessionIssuer
4828
4869
  || (await getConfig()).humanAuthApiUrl
4829
4870
  || 'https://auth.bounded.sh';
4830
- const bounce = issuerLogoutBounceUrl(issuerBase);
4871
+ const bounce = issuerLogoutBounceUrl(issuerBase, idTokenHint);
4831
4872
  // Local state is already cleared, so even if the navigation is stopped
4832
4873
  // (tab close, extension) the app-side logout has fully happened.
4833
4874
  if (bounce)
@@ -5810,30 +5851,117 @@ async function deserializeTransaction(tx) {
5810
5851
  // session is bound server-side to the caller's verified Bounded JWT, and the
5811
5852
  // destination defaults to that user's embedded wallet.
5812
5853
  //
5854
+ // Amount semantics:
5855
+ // amountUsd - exact fiat in; the fee/output breakdown comes back as `quote`.
5856
+ // minAmountOut - a FLOOR on crypto received. The platform quotes Coinbase's
5857
+ // worst-fee payment method (card) and pads by `slippagePad` (default 1%),
5858
+ // so cheaper methods overshoot the floor rather than undershooting it. The
5859
+ // floor holds unless the market moves more than the pad while the user sits
5860
+ // on the checkout screen - which is why completion is VERIFIED: after the
5861
+ // popup closes we poll the platform's status leg and resolve with the
5862
+ // actual filled amount when Coinbase reports it.
5863
+ //
5813
5864
  // Works on Bounded-served origins (<app>.bounded.page / custom domains). On a
5814
5865
  // local dev origin the session endpoint does not exist; we fail with a clear
5815
5866
  // error rather than guessing a host.
5816
5867
  function hasDOM() {
5817
5868
  return typeof window !== 'undefined' && typeof document !== 'undefined';
5818
5869
  }
5870
+ async function authedFetch(path, init) {
5871
+ const idToken = await getIdToken();
5872
+ if (!idToken)
5873
+ throw new Error('onramp: no active session - call login() first');
5874
+ 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}` }) }));
5875
+ }
5876
+ async function readError(res) {
5877
+ var _a;
5878
+ try {
5879
+ return (_a = (await res.json()).error) !== null && _a !== void 0 ? _a : '';
5880
+ }
5881
+ catch (_b) {
5882
+ return '';
5883
+ }
5884
+ }
5885
+ function sessionBody(options, extra) {
5886
+ 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));
5887
+ }
5888
+ function resolveAddress(options) {
5889
+ var _a, _b;
5890
+ const user = getCurrentUser();
5891
+ 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;
5892
+ if (!address) {
5893
+ throw new Error(user
5894
+ ? 'onramp: the signed-in account has no wallet address - pass { address } explicitly'
5895
+ : 'onramp: no signed-in user - call login() first or pass { address }');
5896
+ }
5897
+ return address;
5898
+ }
5819
5899
  /**
5820
- * Open Coinbase Onramp for the signed-in user. Resolves when the popup closes.
5900
+ * Preview a quote (fees + exact/floor output) WITHOUT opening any window or
5901
+ * consuming a session. Show this to the user, then call onramp() to check out.
5902
+ */
5903
+ async function onrampQuote(options = {}) {
5904
+ if (!hasDOM())
5905
+ throw new Error('onrampQuote is web-only');
5906
+ resolveAddress(options);
5907
+ const res = await authedFetch('/__bounded/onramp/session', {
5908
+ method: 'POST',
5909
+ headers: { 'content-type': 'application/json' },
5910
+ body: sessionBody(options, { quoteOnly: true }),
5911
+ });
5912
+ if (!res.ok)
5913
+ throw new Error(`onrampQuote: failed (${res.status}${await readError(res).then((e) => (e ? `: ${e}` : ''))})`);
5914
+ return (await res.json());
5915
+ }
5916
+ /** The signed-in user's recent onramp transactions (newest first). */
5917
+ async function onrampStatus(sinceMs) {
5918
+ if (!hasDOM())
5919
+ throw new Error('onrampStatus is web-only');
5920
+ const res = await authedFetch(`/__bounded/onramp/status${sinceMs ? `?since=${Math.floor(sinceMs)}` : ''}`);
5921
+ if (!res.ok)
5922
+ throw new Error(`onrampStatus: failed (${res.status})`);
5923
+ return (await res.json()).transactions;
5924
+ }
5925
+ function isSuccess(t) {
5926
+ return /SUCCESS|COMPLETED/i.test(t.status);
5927
+ }
5928
+ function isPending(t) {
5929
+ return /PROGRESS|PENDING|CREATED/i.test(t.status);
5930
+ }
5931
+ async function verifyCompletion(openedAt, asset, timeoutMs) {
5932
+ const deadline = Date.now() + timeoutMs;
5933
+ // 60s of clock-skew slack on `since`: Coinbase stamps server-side.
5934
+ const since = openedAt - 60000;
5935
+ for (;;) {
5936
+ let sawPending = false;
5937
+ try {
5938
+ const txs = await onrampStatus(since);
5939
+ const done = txs.find((t) => isSuccess(t) && (t.asset === null || t.asset === asset));
5940
+ if (done)
5941
+ return done;
5942
+ sawPending = txs.some(isPending);
5943
+ }
5944
+ catch ( /* transient - keep polling until the deadline */_a) { /* transient - keep polling until the deadline */ }
5945
+ // No completed fill and nothing in flight: one grace poll cycle still
5946
+ // runs (Coinbase's record can lag the redirect), but don't burn the
5947
+ // full window when the user plainly abandoned checkout.
5948
+ if (Date.now() >= deadline)
5949
+ return null;
5950
+ if (!sawPending && Date.now() >= openedAt + 12000 && Date.now() - openedAt > timeoutMs / 2)
5951
+ return null;
5952
+ await new Promise((r) => setTimeout(r, 3000));
5953
+ }
5954
+ }
5955
+ /**
5956
+ * Open Coinbase Onramp for the signed-in user. Resolves after the window
5957
+ * closes, with completion VERIFIED against Coinbase when possible.
5821
5958
  * Call from a click handler - popup blockers require user activation.
5822
5959
  */
5823
5960
  async function onramp(options = {}) {
5824
- var _a, _b, _c;
5961
+ var _a;
5825
5962
  if (!hasDOM())
5826
5963
  throw new Error('onramp() is web-only');
5827
- const user = getCurrentUser();
5828
- 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;
5829
- if (!address) {
5830
- throw new Error(user
5831
- ? 'onramp(): the signed-in account has no wallet address - pass { address } explicitly'
5832
- : 'onramp(): no signed-in user - call login() first or pass { address }');
5833
- }
5834
- const idToken = await getIdToken();
5835
- if (!idToken)
5836
- throw new Error('onramp(): no active session - call login() first');
5964
+ resolveAddress(options);
5837
5965
  // Open the window synchronously so the click's user-activation window is not
5838
5966
  // consumed by the fetch below (same hard-won pattern as loginWithPopup).
5839
5967
  let popup = null;
@@ -5846,21 +5974,17 @@ async function onramp(options = {}) {
5846
5974
  }
5847
5975
  let session;
5848
5976
  try {
5849
- const res = await fetch(`${window.location.origin}/__bounded/onramp/session`, {
5977
+ const res = await authedFetch('/__bounded/onramp/session', {
5850
5978
  method: 'POST',
5851
- headers: { Authorization: `Bearer ${idToken}`, 'content-type': 'application/json' },
5852
- 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 } : {}))),
5979
+ headers: { 'content-type': 'application/json' },
5980
+ body: sessionBody(options),
5853
5981
  });
5854
5982
  if (!res.ok) {
5855
- let detail = '';
5856
- try {
5857
- detail = (_c = (await res.json()).error) !== null && _c !== void 0 ? _c : '';
5858
- }
5859
- catch ( /* keep status only */_d) { /* keep status only */ }
5983
+ const detail = await readError(res);
5860
5984
  if (res.status === 404) {
5861
- throw new Error('onramp(): session endpoint not found - onramp works on Bounded-served origins (your deployed app URL), not local dev servers');
5985
+ throw new Error('onramp: session endpoint not found - onramp works on Bounded-served origins (your deployed app URL), not local dev servers');
5862
5986
  }
5863
- throw new Error(`onramp(): session mint failed (${res.status}${detail ? `: ${detail}` : ''})`);
5987
+ throw new Error(`onramp: session mint failed (${res.status}${detail ? `: ${detail}` : ''})`);
5864
5988
  }
5865
5989
  session = (await res.json());
5866
5990
  }
@@ -5868,40 +5992,47 @@ async function onramp(options = {}) {
5868
5992
  try {
5869
5993
  popup === null || popup === void 0 ? void 0 : popup.close();
5870
5994
  }
5871
- catch ( /* ignore */_e) { /* ignore */ }
5995
+ catch ( /* ignore */_b) { /* ignore */ }
5872
5996
  throw error;
5873
5997
  }
5998
+ const openedAt = Date.now();
5999
+ const base = Object.assign({ address: session.address, asset: session.asset }, (session.quote ? { quote: session.quote } : {}));
5874
6000
  if (popup) {
5875
6001
  try {
5876
6002
  popup.location.replace(session.url);
5877
6003
  }
5878
- catch (_f) {
6004
+ catch (_c) {
5879
6005
  try {
5880
6006
  popup.location.href = session.url;
5881
6007
  }
5882
- catch (_g) {
6008
+ catch (_d) {
5883
6009
  try {
5884
6010
  popup.close();
5885
6011
  }
5886
- catch ( /* ignore */_h) { /* ignore */ }
5887
- throw new Error('onramp(): popup failed to navigate - retry with { newTab: true }');
6012
+ catch ( /* ignore */_e) { /* ignore */ }
6013
+ throw new Error('onramp: popup failed to navigate - retry with { newTab: true }');
5888
6014
  }
5889
6015
  }
5890
- return await new Promise((resolve) => {
6016
+ await new Promise((resolve) => {
5891
6017
  const timer = setInterval(() => {
5892
6018
  if (popup.closed) {
5893
6019
  clearInterval(timer);
5894
- resolve({ status: 'closed', address: session.address, asset: session.asset });
6020
+ resolve();
5895
6021
  }
5896
6022
  }, 500);
5897
6023
  });
6024
+ const fill = await verifyCompletion(openedAt, session.asset, (_a = options.completionTimeoutMs) !== null && _a !== void 0 ? _a : 24000);
6025
+ if (fill) {
6026
+ return Object.assign(Object.assign(Object.assign(Object.assign({}, base), { status: 'completed' }), (fill.amountOut !== null ? { actualOut: fill.amountOut } : {})), (fill.txHash ? { txHash: fill.txHash } : {}));
6027
+ }
6028
+ return Object.assign(Object.assign({}, base), { status: 'closed' });
5898
6029
  }
5899
6030
  // New-tab path (popup was blocked or explicitly requested): cross-origin tab
5900
- // close is not observable, so resolve immediately.
6031
+ // close is not observable, so resolve immediately; verify via onrampStatus().
5901
6032
  const tab = window.open(session.url, '_blank');
5902
6033
  if (!tab)
5903
- throw new Error('onramp(): window.open was blocked - call onramp() from a click handler');
5904
- return { status: 'opened', address: session.address, asset: session.asset };
6034
+ throw new Error('onramp: window.open was blocked - call onramp() from a click handler');
6035
+ return Object.assign(Object.assign({}, base), { status: 'opened' });
5905
6036
  }
5906
6037
 
5907
6038
  /**
@@ -6287,5 +6418,5 @@ class PrivyExpoProvider {
6287
6418
  }
6288
6419
  }
6289
6420
 
6290
- export { useAuth as A, useQuery as B, useBuildRun as C, apps as D, EMBEDDED_WALLET_UNSUPPORTED as E, deserializeTransaction as F, getIdToken as G, onramp as H, setPlatform as I, builds as J, BuildsApiError as K, PARK_REASONS as L, FAILURE_REASONS as M, NON_TERMINAL_STATES as N, OffchainAuthProvider as O, PrivyExpoProvider as P, BUILD_REVIEW_CAPABILITY_SCHEMA as Q, RUN_STATES as R, BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA as S, TERMINAL_STATES as T, BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA as U, hasEmbeddedEvmWallet as a, embeddedSignAndSubmit as b, setAuthLoading as c, bufferExports as d, embeddedRunEvmTransaction as e, bs58 as f, getPlatform as g, hasEmbeddedWallet as h, confirmAndCheckTransaction as i, init as j, getCurrentUser as k, onAuthLoadingChanged as l, getAuthLoading as m, normalizeSolanaRpcUrl as n, onAuthStateChanged as o, login as p, logout as q, resolveSolanaRpcUrl as r, setCurrentUser as s, getAuthProvider as t, signInAnonymously as u, loginWithPrivy as v, loginWithRedirect as w, completeLoginFromRedirect as x, loginWithPopup as y, completeLoginInPopup as z };
6291
- //# sourceMappingURL=index.native-D-8IAEy-.mjs.map
6421
+ export { useAuth as A, useQuery as B, useBuildRun as C, apps as D, EMBEDDED_WALLET_UNSUPPORTED as E, deserializeTransaction as F, getIdToken as G, onramp as H, onrampQuote as I, onrampStatus as J, setPlatform as K, builds as L, BuildsApiError as M, NON_TERMINAL_STATES as N, OffchainAuthProvider as O, PrivyExpoProvider as P, PARK_REASONS as Q, RUN_STATES as R, FAILURE_REASONS as S, TERMINAL_STATES as T, BUILD_REVIEW_CAPABILITY_SCHEMA as U, BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA as V, BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA as W, hasEmbeddedEvmWallet as a, embeddedSignAndSubmit as b, setAuthLoading as c, bufferExports as d, embeddedRunEvmTransaction as e, bs58 as f, getPlatform as g, hasEmbeddedWallet as h, confirmAndCheckTransaction as i, init as j, getCurrentUser as k, onAuthLoadingChanged as l, getAuthLoading as m, normalizeSolanaRpcUrl as n, onAuthStateChanged as o, login as p, logout as q, resolveSolanaRpcUrl as r, setCurrentUser as s, getAuthProvider as t, signInAnonymously as u, loginWithPrivy as v, loginWithRedirect as w, completeLoginFromRedirect as x, loginWithPopup as y, completeLoginInPopup as z };
6422
+ //# sourceMappingURL=index.native-BIUs8t7F.mjs.map