@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.
- package/dist/auth/index.d.ts +1 -1
- package/dist/{email-auth-provider-BHtR1H_E.js → email-auth-provider-B5yIZe1l.js} +3 -3
- package/dist/email-auth-provider-B5yIZe1l.js.map +1 -0
- package/dist/{email-auth-provider-BlJI-8w9.js → email-auth-provider-BCEdcnRu.js} +3 -3
- package/dist/email-auth-provider-BCEdcnRu.js.map +1 -0
- package/dist/{email-auth-provider-CfNSS8Du.mjs → email-auth-provider-BF-vvbAt.mjs} +3 -3
- package/dist/email-auth-provider-BF-vvbAt.mjs.map +1 -0
- package/dist/{email-auth-provider-CX7foEhA.mjs → email-auth-provider-nBv7mW9q.mjs} +3 -3
- package/dist/email-auth-provider-nBv7mW9q.mjs.map +1 -0
- package/dist/{guest-auth-provider-BPXDJoEQ.js → guest-auth-provider-CZ-CpNhD.js} +7 -7
- package/dist/guest-auth-provider-CZ-CpNhD.js.map +1 -0
- package/dist/{guest-auth-provider-BRjJV8Zq.js → guest-auth-provider-CrRreOyf.js} +7 -7
- package/dist/guest-auth-provider-CrRreOyf.js.map +1 -0
- package/dist/{guest-auth-provider-DxhZ5sWN.mjs → guest-auth-provider-DFNAHmtY.mjs} +7 -7
- package/dist/guest-auth-provider-DFNAHmtY.mjs.map +1 -0
- package/dist/{guest-auth-provider-DJmuReVa.mjs → guest-auth-provider-DGvQgIwm.mjs} +7 -7
- package/dist/guest-auth-provider-DGvQgIwm.mjs.map +1 -0
- package/dist/{index-DWodlTFG.mjs → index-CQtGkKVd.mjs} +284 -38
- package/dist/index-CQtGkKVd.mjs.map +1 -0
- package/dist/{index-MKE9tHpl.js → index-iu8vB4ye.js} +285 -36
- package/dist/index-iu8vB4ye.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{index.native-hu3o3zXU.js → index.native-CDfx0N6p.js} +274 -25
- package/dist/index.native-CDfx0N6p.js.map +1 -0
- package/dist/{index.native-CQiw9baS.mjs → index.native-Crfn_NOB.mjs} +273 -27
- package/dist/index.native-Crfn_NOB.mjs.map +1 -0
- package/dist/index.native.d.ts +2 -0
- package/dist/index.native.js +4 -1
- package/dist/index.native.js.map +1 -1
- package/dist/index.native.mjs +1 -1
- package/dist/{injected-evm-wallet-provider-DCGnUedJ.mjs → injected-evm-wallet-provider-Bi9G3X35.mjs} +4 -4
- package/dist/injected-evm-wallet-provider-Bi9G3X35.mjs.map +1 -0
- package/dist/{injected-evm-wallet-provider-Xbt2CUPi.js → injected-evm-wallet-provider-CGa692L1.js} +4 -4
- package/dist/injected-evm-wallet-provider-CGa692L1.js.map +1 -0
- package/dist/{injected-evm-wallet-provider-CnJRLE8P.mjs → injected-evm-wallet-provider-D8lYGcM2.mjs} +4 -4
- package/dist/injected-evm-wallet-provider-D8lYGcM2.mjs.map +1 -0
- package/dist/{injected-evm-wallet-provider-BOPzdm7E.js → injected-evm-wallet-provider-LTWiACl5.js} +4 -4
- package/dist/injected-evm-wallet-provider-LTWiACl5.js.map +1 -0
- package/dist/{injected-wallet-provider-Bps6a9IP.mjs → injected-wallet-provider-CcyA5JBS.mjs} +3 -3
- package/dist/injected-wallet-provider-CcyA5JBS.mjs.map +1 -0
- package/dist/{injected-wallet-provider-BUvN5Y6_.js → injected-wallet-provider-DYZFxIdd.js} +3 -3
- package/dist/injected-wallet-provider-DYZFxIdd.js.map +1 -0
- package/dist/{injected-wallet-provider-7ep-jcal.js → injected-wallet-provider-TPx8Wzvq.js} +3 -3
- package/dist/injected-wallet-provider-TPx8Wzvq.js.map +1 -0
- package/dist/{injected-wallet-provider-Dg3MieTq.mjs → injected-wallet-provider-kx8tcMKM.mjs} +3 -3
- package/dist/injected-wallet-provider-kx8tcMKM.mjs.map +1 -0
- package/dist/onramp.d.ts +72 -0
- package/package.json +2 -2
- package/dist/email-auth-provider-BHtR1H_E.js.map +0 -1
- package/dist/email-auth-provider-BlJI-8w9.js.map +0 -1
- package/dist/email-auth-provider-CX7foEhA.mjs.map +0 -1
- package/dist/email-auth-provider-CfNSS8Du.mjs.map +0 -1
- package/dist/guest-auth-provider-BPXDJoEQ.js.map +0 -1
- package/dist/guest-auth-provider-BRjJV8Zq.js.map +0 -1
- package/dist/guest-auth-provider-DJmuReVa.mjs.map +0 -1
- package/dist/guest-auth-provider-DxhZ5sWN.mjs.map +0 -1
- package/dist/index-DWodlTFG.mjs.map +0 -1
- package/dist/index-MKE9tHpl.js.map +0 -1
- package/dist/index.native-CQiw9baS.mjs.map +0 -1
- package/dist/index.native-hu3o3zXU.js.map +0 -1
- package/dist/injected-evm-wallet-provider-BOPzdm7E.js.map +0 -1
- package/dist/injected-evm-wallet-provider-CnJRLE8P.mjs.map +0 -1
- package/dist/injected-evm-wallet-provider-DCGnUedJ.mjs.map +0 -1
- package/dist/injected-evm-wallet-provider-Xbt2CUPi.js.map +0 -1
- package/dist/injected-wallet-provider-7ep-jcal.js.map +0 -1
- package/dist/injected-wallet-provider-BUvN5Y6_.js.map +0 -1
- package/dist/injected-wallet-provider-Bps6a9IP.mjs.map +0 -1
- package/dist/injected-wallet-provider-Dg3MieTq.mjs.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { assertCanonicalBoundedRuntimeWriteCalldata, getActiveSessionManager, getConfig, WebSessionManager, ReactNativeSessionManager, revokeSession, init as init$1, reconnectWithNewAuth, deriveUserIdentityFromIdToken, subscribe, getIdToken as getIdToken$1, convertRemainingAccounts, buildSetDocumentsTransaction, genAuthNonce, genSolanaMessage, createSessionWithSignature } from '@bounded-sh/core';
|
|
1
|
+
import { assertCanonicalBoundedRuntimeWriteCalldata, getActiveSessionManager, getConfig, WebSessionManager, ReactNativeSessionManager, revokeSession, init as init$1, onSessionCleared, reconnectWithNewAuth, deriveUserIdentityFromIdToken, subscribe, getIdToken as getIdToken$1, convertRemainingAccounts, buildSetDocumentsTransaction, genAuthNonce, genSolanaMessage, createSessionWithSignature } from '@bounded-sh/core';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
|
|
4
4
|
/**
|
|
@@ -3454,7 +3454,7 @@ const EMBEDDED_WALLET_UNSUPPORTED = 'Unsupported for embedded smart wallets —
|
|
|
3454
3454
|
const NO_EMBEDDED_WALLET = 'This login has no embedded wallet. Enable `"auth": { "wallets": true }` in ' +
|
|
3455
3455
|
'the app policy so email logins get a non-custodial wallet (@user.address), ' +
|
|
3456
3456
|
'then sign from a user gesture.';
|
|
3457
|
-
function hasDOM$
|
|
3457
|
+
function hasDOM$2() {
|
|
3458
3458
|
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
3459
3459
|
}
|
|
3460
3460
|
function decodeJwt$1(token) {
|
|
@@ -3514,7 +3514,7 @@ let requestSeq = 0;
|
|
|
3514
3514
|
* wallet's owner, and validates that the wallet address matches the account.
|
|
3515
3515
|
*/
|
|
3516
3516
|
async function signViaSignerPopup(action) {
|
|
3517
|
-
if (!hasDOM$
|
|
3517
|
+
if (!hasDOM$2()) {
|
|
3518
3518
|
throw new Error('Embedded-wallet signing is only available in the browser (popup-based).');
|
|
3519
3519
|
}
|
|
3520
3520
|
const isEvm = action.kind === 'evmTransaction';
|
|
@@ -3690,7 +3690,7 @@ const STORED_AUTH_METHOD_KEY$1 = 'bounded_last_auth_method';
|
|
|
3690
3690
|
// not by `typeof window` (truthy on RN). OIDC redirect/popup is web-only, but the
|
|
3691
3691
|
// session it mints is read back through this same selector everywhere.
|
|
3692
3692
|
const sessionManager = getActiveSessionManager;
|
|
3693
|
-
function hasDOM() {
|
|
3693
|
+
function hasDOM$1() {
|
|
3694
3694
|
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
3695
3695
|
}
|
|
3696
3696
|
function decodeJwt(token) {
|
|
@@ -3721,7 +3721,7 @@ const oidcProviderSingleton = {
|
|
|
3721
3721
|
const idToken = sessionManager().getIdToken();
|
|
3722
3722
|
return idToken ? userFromIdToken(idToken) : null;
|
|
3723
3723
|
},
|
|
3724
|
-
async logout() { sessionManager().clearSession(); },
|
|
3724
|
+
async logout() { await sessionManager().clearSession(); },
|
|
3725
3725
|
async signMessage(_message) {
|
|
3726
3726
|
throw new Error(hasEmbeddedWallet() ? EMBEDDED_WALLET_UNSUPPORTED : OIDC_NO_WALLET);
|
|
3727
3727
|
},
|
|
@@ -3830,7 +3830,7 @@ async function buildAuthorize(opts) {
|
|
|
3830
3830
|
throw new Error('appId is not configured (init the SDK with your appId)');
|
|
3831
3831
|
// Web: default to the current page so callers don't have to pass redirectUri. Native has no
|
|
3832
3832
|
// window — it MUST pass an https universal link (the system auth session needs a real URL).
|
|
3833
|
-
const redirectUri = opts.redirectUri || (hasDOM() ? window.location.origin + window.location.pathname : undefined);
|
|
3833
|
+
const redirectUri = opts.redirectUri || (hasDOM$1() ? window.location.origin + window.location.pathname : undefined);
|
|
3834
3834
|
if (!redirectUri)
|
|
3835
3835
|
throw new Error('redirectUri is required on React Native — pass the https universal link the app owner registered.');
|
|
3836
3836
|
const { verifier, challenge } = await makePkce();
|
|
@@ -3938,7 +3938,7 @@ async function runNativeLogin(opts) {
|
|
|
3938
3938
|
async function loginWithRedirect(opts) {
|
|
3939
3939
|
if (isNative())
|
|
3940
3940
|
return runNativeLogin(opts);
|
|
3941
|
-
if (!hasDOM())
|
|
3941
|
+
if (!hasDOM$1())
|
|
3942
3942
|
throw new Error('loginWithRedirect requires a browser or a configured React Native platform');
|
|
3943
3943
|
const { url, pkce } = await buildAuthorize(opts);
|
|
3944
3944
|
// Stash the verifier + state so the redirect_uri page can finish the exchange. sessionStorage
|
|
@@ -3954,7 +3954,7 @@ async function loginWithRedirect(opts) {
|
|
|
3954
3954
|
* loginWithRedirect() (which resolves with the User), so there is no separate redirect
|
|
3955
3955
|
* page to finish from. Calling it on RN is harmless. */
|
|
3956
3956
|
async function completeLoginFromRedirect() {
|
|
3957
|
-
if (!hasDOM())
|
|
3957
|
+
if (!hasDOM$1())
|
|
3958
3958
|
return null;
|
|
3959
3959
|
const url = new URL(window.location.href);
|
|
3960
3960
|
const code = url.searchParams.get('code');
|
|
@@ -4000,7 +4000,7 @@ async function completeLoginFromRedirect() {
|
|
|
4000
4000
|
* redirect_uri page posts the code back. The redirect_uri page just calls the usual
|
|
4001
4001
|
* completeLoginFromRedirect() (which auto-detects the popup) — no separate call needed. */
|
|
4002
4002
|
async function loginWithPopup(opts) {
|
|
4003
|
-
if (!hasDOM())
|
|
4003
|
+
if (!hasDOM$1())
|
|
4004
4004
|
throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
|
|
4005
4005
|
// Open synchronously, before PKCE/config awaits consume the click's user-
|
|
4006
4006
|
// activation window. Opening only after buildAuthorize() intermittently
|
|
@@ -4140,7 +4140,7 @@ async function loginWithPopup(opts) {
|
|
|
4140
4140
|
/** Call this on the redirect_uri page when login was opened via loginWithPopup(): posts the
|
|
4141
4141
|
* code back to the opener and closes. (For the redirect variant use completeLoginFromRedirect.) */
|
|
4142
4142
|
function completeLoginInPopup(openerOrigin) {
|
|
4143
|
-
if (!hasDOM() || !window.opener)
|
|
4143
|
+
if (!hasDOM$1() || !window.opener)
|
|
4144
4144
|
return;
|
|
4145
4145
|
const url = new URL(window.location.href);
|
|
4146
4146
|
const code = url.searchParams.get('code');
|
|
@@ -4341,15 +4341,20 @@ function authProviderConfigKey(config) {
|
|
|
4341
4341
|
* so both web (WebSessionManager) and RN (ReactNativeSessionManager) sessions
|
|
4342
4342
|
* are properly cleared.
|
|
4343
4343
|
*/
|
|
4344
|
-
function clearIncompatibleSession() {
|
|
4344
|
+
async function clearIncompatibleSession() {
|
|
4345
4345
|
if (getStoredAuthMethod() === currentAuthMethod) {
|
|
4346
4346
|
return false;
|
|
4347
4347
|
}
|
|
4348
4348
|
// Clear both managers: on web the RN manager is a no-op (not configured),
|
|
4349
4349
|
// and on RN the web manager is a no-op (no localStorage).
|
|
4350
|
-
|
|
4350
|
+
//
|
|
4351
|
+
// AWAITED. Session removal is serialized through a cross-tab lock now, so a
|
|
4352
|
+
// fire-and-forget call would let init() reach setCurrentUser while the old
|
|
4353
|
+
// credential was still readable — and a request issued in that gap would
|
|
4354
|
+
// authenticate as the session we are in the middle of discarding.
|
|
4355
|
+
await WebSessionManager.clearSession();
|
|
4351
4356
|
try {
|
|
4352
|
-
ReactNativeSessionManager.clearSession();
|
|
4357
|
+
await ReactNativeSessionManager.clearSession();
|
|
4353
4358
|
}
|
|
4354
4359
|
catch ( /* not configured on web */_a) { /* not configured on web */ }
|
|
4355
4360
|
setStoredAuthMethod(null);
|
|
@@ -4448,7 +4453,7 @@ async function getAuthProvider(config) {
|
|
|
4448
4453
|
// manager as legacy email sessions. Human login should be started
|
|
4449
4454
|
// with loginWithRedirect(); this provider is kept as the restore and
|
|
4450
4455
|
// logout surface for those sessions.
|
|
4451
|
-
currentAuthProvider = new (await import('./email-auth-provider-
|
|
4456
|
+
currentAuthProvider = new (await import('./email-auth-provider-BF-vvbAt.mjs')).EmailAuthProvider();
|
|
4452
4457
|
break;
|
|
4453
4458
|
case "wallet":
|
|
4454
4459
|
case "phantom": {
|
|
@@ -4467,7 +4472,7 @@ async function getAuthProvider(config) {
|
|
|
4467
4472
|
if (!isWalletLoginEnabled(config)) {
|
|
4468
4473
|
throw walletLoginNotEnabledError(authMethod);
|
|
4469
4474
|
}
|
|
4470
|
-
const { InjectedWalletProvider } = await import('./injected-wallet-provider-
|
|
4475
|
+
const { InjectedWalletProvider } = await import('./injected-wallet-provider-CcyA5JBS.mjs');
|
|
4471
4476
|
currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
|
|
4472
4477
|
break;
|
|
4473
4478
|
}
|
|
@@ -4480,7 +4485,7 @@ async function getAuthProvider(config) {
|
|
|
4480
4485
|
// Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
|
|
4481
4486
|
// to point at a specific wallet or set chainId/domain.
|
|
4482
4487
|
const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
|
|
4483
|
-
const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-
|
|
4488
|
+
const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-Bi9G3X35.mjs');
|
|
4484
4489
|
currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
|
|
4485
4490
|
break;
|
|
4486
4491
|
}
|
|
@@ -4508,7 +4513,7 @@ async function getAuthProvider(config) {
|
|
|
4508
4513
|
break;
|
|
4509
4514
|
case "guest":
|
|
4510
4515
|
// Anonymous auth: a device-local ed25519 keypair signs the challenge.
|
|
4511
|
-
currentAuthProvider = new (await import('./guest-auth-provider-
|
|
4516
|
+
currentAuthProvider = new (await import('./guest-auth-provider-DFNAHmtY.mjs')).GuestAuthProvider();
|
|
4512
4517
|
break;
|
|
4513
4518
|
}
|
|
4514
4519
|
if (!currentAuthProvider) {
|
|
@@ -4564,7 +4569,7 @@ async function signInAnonymously() {
|
|
|
4564
4569
|
// Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
|
|
4565
4570
|
// the main bundle for data-only apps. Specifier is a static string literal
|
|
4566
4571
|
// so bundle-splitting can see it.
|
|
4567
|
-
const provider = new (await import('./guest-auth-provider-
|
|
4572
|
+
const provider = new (await import('./guest-auth-provider-DFNAHmtY.mjs')).GuestAuthProvider();
|
|
4568
4573
|
setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
|
|
4569
4574
|
const commit = (user) => {
|
|
4570
4575
|
// No await/interleaving inside this block: provider/method/config/marker
|
|
@@ -4606,7 +4611,7 @@ async function signInAnonymously() {
|
|
|
4606
4611
|
}
|
|
4607
4612
|
catch (_b) {
|
|
4608
4613
|
try {
|
|
4609
|
-
getActiveSessionManager().clearSession();
|
|
4614
|
+
await getActiveSessionManager().clearSession();
|
|
4610
4615
|
}
|
|
4611
4616
|
catch ( /* fail closed below */_c) { /* fail closed below */ }
|
|
4612
4617
|
}
|
|
@@ -4749,7 +4754,7 @@ function getCurrentAuthMethod() {
|
|
|
4749
4754
|
// allowlist here: on a trusted origin we end the issuer session with a
|
|
4750
4755
|
// top-level bounce; anywhere else (custom domains, http dev) we keep the
|
|
4751
4756
|
// local-only logout instead of stranding the user on the issuer origin.
|
|
4752
|
-
function issuerLogoutBounceUrl(issuerBase) {
|
|
4757
|
+
function issuerLogoutBounceUrl(issuerBase, idTokenHint) {
|
|
4753
4758
|
if (typeof window === 'undefined' || typeof document === 'undefined')
|
|
4754
4759
|
return null; // RN/Node: no shared cookie jar to clear this way
|
|
4755
4760
|
try {
|
|
@@ -4772,12 +4777,27 @@ function issuerLogoutBounceUrl(issuerBase) {
|
|
|
4772
4777
|
if (!trusted)
|
|
4773
4778
|
return null;
|
|
4774
4779
|
const base = issuerBase.replace(/\/$/, '');
|
|
4775
|
-
|
|
4780
|
+
// #228: send the id_token_hint so the issuer can bind the post-logout redirect to this
|
|
4781
|
+
// session + app + registered origin. Without it the issuer safely falls back to its own
|
|
4782
|
+
// origin (it no longer trusts a bare suffix match), so the return-to-app bounce is a no-op.
|
|
4783
|
+
const hint = idTokenHint ? `&id_token_hint=${encodeURIComponent(idTokenHint)}` : '';
|
|
4784
|
+
return `${base}/logout?redirect_uri=${encodeURIComponent(here.href)}${hint}`;
|
|
4776
4785
|
}
|
|
4777
4786
|
catch (_b) {
|
|
4778
4787
|
return null;
|
|
4779
4788
|
}
|
|
4780
4789
|
}
|
|
4790
|
+
// #228 helper, deliberately OUTSIDE logout(): the logout body is source-guarded
|
|
4791
|
+
// against any `catch` so a revokeSession failure can never be swallowed, and a
|
|
4792
|
+
// missing id token is the one benign absence worth tolerating here.
|
|
4793
|
+
function idTokenHintOrNull(sm) {
|
|
4794
|
+
try {
|
|
4795
|
+
return sm.getIdToken();
|
|
4796
|
+
}
|
|
4797
|
+
catch (_a) {
|
|
4798
|
+
return null; /* no id token to hint with */
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4781
4801
|
async function logout$1(options) {
|
|
4782
4802
|
// Re-initialize the provider if it was cleared by a prior logout — the exact mirror
|
|
4783
4803
|
// of login()'s self-heal above. A hosted popup/redirect login (loginWithPopup /
|
|
@@ -4802,6 +4822,9 @@ async function logout$1(options) {
|
|
|
4802
4822
|
const sm = getActiveSessionManager();
|
|
4803
4823
|
const refreshToken = sm.getRefreshToken();
|
|
4804
4824
|
const sessionIssuer = sm.getIssuer();
|
|
4825
|
+
// #228: capture the id token NOW, before currentAuthProvider.logout() clears the session, so we
|
|
4826
|
+
// can hand it to the issuer /logout as an id_token_hint (which binds the post-logout redirect).
|
|
4827
|
+
const idTokenHint = idTokenHintOrNull(sm);
|
|
4805
4828
|
if (refreshToken)
|
|
4806
4829
|
await revokeSession(refreshToken, sessionIssuer !== null && sessionIssuer !== void 0 ? sessionIssuer : undefined);
|
|
4807
4830
|
// Hosted sessions ('email' covers the whole hosted surface: email OTP +
|
|
@@ -4822,7 +4845,7 @@ async function logout$1(options) {
|
|
|
4822
4845
|
const issuerBase = sessionIssuer
|
|
4823
4846
|
|| (await getConfig()).humanAuthApiUrl
|
|
4824
4847
|
|| 'https://auth.bounded.sh';
|
|
4825
|
-
const bounce = issuerLogoutBounceUrl(issuerBase);
|
|
4848
|
+
const bounce = issuerLogoutBounceUrl(issuerBase, idTokenHint);
|
|
4826
4849
|
// Local state is already cleared, so even if the navigation is stopped
|
|
4827
4850
|
// (tab close, extension) the app-side logout has fully happened.
|
|
4828
4851
|
if (bounce)
|
|
@@ -4830,6 +4853,22 @@ async function logout$1(options) {
|
|
|
4830
4853
|
}
|
|
4831
4854
|
}
|
|
4832
4855
|
|
|
4856
|
+
/**
|
|
4857
|
+
* Subscribed once per process, not per init(): re-registering on every init would
|
|
4858
|
+
* stack duplicate handlers on repeated initialization.
|
|
4859
|
+
*/
|
|
4860
|
+
let sessionClearedBridgeRegistered = false;
|
|
4861
|
+
function registerSessionClearedBridge() {
|
|
4862
|
+
if (sessionClearedBridgeRegistered)
|
|
4863
|
+
return;
|
|
4864
|
+
sessionClearedBridgeRegistered = true;
|
|
4865
|
+
onSessionCleared(() => {
|
|
4866
|
+
// Core fires this only when storage was ACTUALLY cleared, never on a
|
|
4867
|
+
// transient refresh failure that preserved the session — so this cannot
|
|
4868
|
+
// sign anyone out over a network blip.
|
|
4869
|
+
setCurrentUser(null);
|
|
4870
|
+
});
|
|
4871
|
+
}
|
|
4833
4872
|
/**
|
|
4834
4873
|
* Read the active session's idToken synchronously from whichever store is live
|
|
4835
4874
|
* for this runtime (WebSessionManager on web, ReactNativeSessionManager on RN —
|
|
@@ -4887,6 +4926,15 @@ async function init(newConfig) {
|
|
|
4887
4926
|
// setting it here makes "no flash" the SDK's default behavior for every consumer.
|
|
4888
4927
|
setAuthLoading(true);
|
|
4889
4928
|
try {
|
|
4929
|
+
// Bridge core's "the stored session is gone" signal into SDK auth state.
|
|
4930
|
+
//
|
|
4931
|
+
// Core cannot import this package, and a session can die deep inside a
|
|
4932
|
+
// session manager (dead refresh token, rejected refresh, app-id mismatch).
|
|
4933
|
+
// Before this, setCurrentUser(null) was only ever called by logout() and
|
|
4934
|
+
// init(), so storage could be wiped while `currentUser` stayed populated —
|
|
4935
|
+
// onAuthStateChanged never fired, and every consumer UI went on showing a
|
|
4936
|
+
// connected wallet for a session the server no longer knew about.
|
|
4937
|
+
registerSessionClearedBridge();
|
|
4890
4938
|
// Get auth provider first
|
|
4891
4939
|
authProviderInstance = await getAuthProvider(newConfig);
|
|
4892
4940
|
// Initialize config with auth provider
|
|
@@ -4903,7 +4951,12 @@ async function init(newConfig) {
|
|
|
4903
4951
|
let user = await authProviderInstance.restoreSession();
|
|
4904
4952
|
// If a session exists but was created before auth method tracking (legacy session),
|
|
4905
4953
|
// clear it to avoid mismatches when the app switches auth methods (e.g. email → phantom)
|
|
4906
|
-
|
|
4954
|
+
//
|
|
4955
|
+
// AWAITED: the clear is serialized through a cross-tab lock now, and an
|
|
4956
|
+
// unawaited call would return a Promise (always truthy) — nulling the user
|
|
4957
|
+
// unconditionally AND letting setCurrentUser run while the old credential
|
|
4958
|
+
// was still in storage.
|
|
4959
|
+
if (user && await clearIncompatibleSession()) {
|
|
4907
4960
|
user = null;
|
|
4908
4961
|
}
|
|
4909
4962
|
setCurrentUser(user);
|
|
@@ -5766,6 +5819,199 @@ async function deserializeTransaction(tx) {
|
|
|
5766
5819
|
}
|
|
5767
5820
|
}
|
|
5768
5821
|
|
|
5822
|
+
// bounded.onramp() - fund the signed-in user's wallet with Coinbase Onramp.
|
|
5823
|
+
//
|
|
5824
|
+
// Web-only. Opens Coinbase's hosted checkout (pay.coinbase.com) in a popup
|
|
5825
|
+
// (new-tab fallback) using a single-use session token minted by the Bounded
|
|
5826
|
+
// platform at POST <origin>/__bounded/onramp/session on the app's own origin.
|
|
5827
|
+
// The platform holds the Coinbase credential; the app never sees it. The
|
|
5828
|
+
// session is bound server-side to the caller's verified Bounded JWT, and the
|
|
5829
|
+
// destination defaults to that user's embedded wallet.
|
|
5830
|
+
//
|
|
5831
|
+
// Amount semantics:
|
|
5832
|
+
// amountUsd - exact fiat in; the fee/output breakdown comes back as `quote`.
|
|
5833
|
+
// minAmountOut - a FLOOR on crypto received. The platform quotes Coinbase's
|
|
5834
|
+
// worst-fee payment method (card) and pads by `slippagePad` (default 1%),
|
|
5835
|
+
// so cheaper methods overshoot the floor rather than undershooting it. The
|
|
5836
|
+
// floor holds unless the market moves more than the pad while the user sits
|
|
5837
|
+
// on the checkout screen - which is why completion is VERIFIED: after the
|
|
5838
|
+
// popup closes we poll the platform's status leg and resolve with the
|
|
5839
|
+
// actual filled amount when Coinbase reports it.
|
|
5840
|
+
//
|
|
5841
|
+
// Works on Bounded-served origins (<app>.bounded.page / custom domains). On a
|
|
5842
|
+
// local dev origin the session endpoint does not exist; we fail with a clear
|
|
5843
|
+
// error rather than guessing a host.
|
|
5844
|
+
function hasDOM() {
|
|
5845
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
5846
|
+
}
|
|
5847
|
+
async function authedFetch(path, init) {
|
|
5848
|
+
const idToken = await getIdToken();
|
|
5849
|
+
if (!idToken)
|
|
5850
|
+
throw new Error('onramp: no active session - call login() first');
|
|
5851
|
+
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}` }) }));
|
|
5852
|
+
}
|
|
5853
|
+
async function readError(res) {
|
|
5854
|
+
var _a;
|
|
5855
|
+
try {
|
|
5856
|
+
return (_a = (await res.json()).error) !== null && _a !== void 0 ? _a : '';
|
|
5857
|
+
}
|
|
5858
|
+
catch (_b) {
|
|
5859
|
+
return '';
|
|
5860
|
+
}
|
|
5861
|
+
}
|
|
5862
|
+
function sessionBody(options, extra) {
|
|
5863
|
+
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));
|
|
5864
|
+
}
|
|
5865
|
+
function resolveAddress(options) {
|
|
5866
|
+
var _a, _b;
|
|
5867
|
+
const user = getCurrentUser();
|
|
5868
|
+
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;
|
|
5869
|
+
if (!address) {
|
|
5870
|
+
throw new Error(user
|
|
5871
|
+
? 'onramp: the signed-in account has no wallet address - pass { address } explicitly'
|
|
5872
|
+
: 'onramp: no signed-in user - call login() first or pass { address }');
|
|
5873
|
+
}
|
|
5874
|
+
return address;
|
|
5875
|
+
}
|
|
5876
|
+
/**
|
|
5877
|
+
* Preview a quote (fees + exact/floor output) WITHOUT opening any window or
|
|
5878
|
+
* consuming a session. Show this to the user, then call onramp() to check out.
|
|
5879
|
+
*/
|
|
5880
|
+
async function onrampQuote(options = {}) {
|
|
5881
|
+
if (!hasDOM())
|
|
5882
|
+
throw new Error('onrampQuote is web-only');
|
|
5883
|
+
resolveAddress(options);
|
|
5884
|
+
const res = await authedFetch('/__bounded/onramp/session', {
|
|
5885
|
+
method: 'POST',
|
|
5886
|
+
headers: { 'content-type': 'application/json' },
|
|
5887
|
+
body: sessionBody(options, { quoteOnly: true }),
|
|
5888
|
+
});
|
|
5889
|
+
if (!res.ok)
|
|
5890
|
+
throw new Error(`onrampQuote: failed (${res.status}${await readError(res).then((e) => (e ? `: ${e}` : ''))})`);
|
|
5891
|
+
return (await res.json());
|
|
5892
|
+
}
|
|
5893
|
+
/** The signed-in user's recent onramp transactions (newest first). */
|
|
5894
|
+
async function onrampStatus(sinceMs) {
|
|
5895
|
+
if (!hasDOM())
|
|
5896
|
+
throw new Error('onrampStatus is web-only');
|
|
5897
|
+
const res = await authedFetch(`/__bounded/onramp/status${sinceMs ? `?since=${Math.floor(sinceMs)}` : ''}`);
|
|
5898
|
+
if (!res.ok)
|
|
5899
|
+
throw new Error(`onrampStatus: failed (${res.status})`);
|
|
5900
|
+
return (await res.json()).transactions;
|
|
5901
|
+
}
|
|
5902
|
+
function isSuccess(t) {
|
|
5903
|
+
return /SUCCESS|COMPLETED/i.test(t.status);
|
|
5904
|
+
}
|
|
5905
|
+
function isPending(t) {
|
|
5906
|
+
return /PROGRESS|PENDING|CREATED/i.test(t.status);
|
|
5907
|
+
}
|
|
5908
|
+
async function verifyCompletion(openedAt, asset, timeoutMs) {
|
|
5909
|
+
const deadline = Date.now() + timeoutMs;
|
|
5910
|
+
// 60s of clock-skew slack on `since`: Coinbase stamps server-side.
|
|
5911
|
+
const since = openedAt - 60000;
|
|
5912
|
+
for (;;) {
|
|
5913
|
+
let sawPending = false;
|
|
5914
|
+
try {
|
|
5915
|
+
const txs = await onrampStatus(since);
|
|
5916
|
+
const done = txs.find((t) => isSuccess(t) && (t.asset === null || t.asset === asset));
|
|
5917
|
+
if (done)
|
|
5918
|
+
return done;
|
|
5919
|
+
sawPending = txs.some(isPending);
|
|
5920
|
+
}
|
|
5921
|
+
catch ( /* transient - keep polling until the deadline */_a) { /* transient - keep polling until the deadline */ }
|
|
5922
|
+
// No completed fill and nothing in flight: one grace poll cycle still
|
|
5923
|
+
// runs (Coinbase's record can lag the redirect), but don't burn the
|
|
5924
|
+
// full window when the user plainly abandoned checkout.
|
|
5925
|
+
if (Date.now() >= deadline)
|
|
5926
|
+
return null;
|
|
5927
|
+
if (!sawPending && Date.now() >= openedAt + 12000 && Date.now() - openedAt > timeoutMs / 2)
|
|
5928
|
+
return null;
|
|
5929
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
5930
|
+
}
|
|
5931
|
+
}
|
|
5932
|
+
/**
|
|
5933
|
+
* Open Coinbase Onramp for the signed-in user. Resolves after the window
|
|
5934
|
+
* closes, with completion VERIFIED against Coinbase when possible.
|
|
5935
|
+
* Call from a click handler - popup blockers require user activation.
|
|
5936
|
+
*/
|
|
5937
|
+
async function onramp(options = {}) {
|
|
5938
|
+
var _a;
|
|
5939
|
+
if (!hasDOM())
|
|
5940
|
+
throw new Error('onramp() is web-only');
|
|
5941
|
+
resolveAddress(options);
|
|
5942
|
+
// Open the window synchronously so the click's user-activation window is not
|
|
5943
|
+
// consumed by the fetch below (same hard-won pattern as loginWithPopup).
|
|
5944
|
+
let popup = null;
|
|
5945
|
+
if (!options.newTab) {
|
|
5946
|
+
const w = options.width || 460;
|
|
5947
|
+
const h = options.height || 720;
|
|
5948
|
+
const left = window.screenX + (window.outerWidth - w) / 2;
|
|
5949
|
+
const top = window.screenY + (window.outerHeight - h) / 2;
|
|
5950
|
+
popup = window.open('', 'bounded-onramp', `width=${w},height=${h},left=${left},top=${top}`);
|
|
5951
|
+
}
|
|
5952
|
+
let session;
|
|
5953
|
+
try {
|
|
5954
|
+
const res = await authedFetch('/__bounded/onramp/session', {
|
|
5955
|
+
method: 'POST',
|
|
5956
|
+
headers: { 'content-type': 'application/json' },
|
|
5957
|
+
body: sessionBody(options),
|
|
5958
|
+
});
|
|
5959
|
+
if (!res.ok) {
|
|
5960
|
+
const detail = await readError(res);
|
|
5961
|
+
if (res.status === 404) {
|
|
5962
|
+
throw new Error('onramp: session endpoint not found - onramp works on Bounded-served origins (your deployed app URL), not local dev servers');
|
|
5963
|
+
}
|
|
5964
|
+
throw new Error(`onramp: session mint failed (${res.status}${detail ? `: ${detail}` : ''})`);
|
|
5965
|
+
}
|
|
5966
|
+
session = (await res.json());
|
|
5967
|
+
}
|
|
5968
|
+
catch (error) {
|
|
5969
|
+
try {
|
|
5970
|
+
popup === null || popup === void 0 ? void 0 : popup.close();
|
|
5971
|
+
}
|
|
5972
|
+
catch ( /* ignore */_b) { /* ignore */ }
|
|
5973
|
+
throw error;
|
|
5974
|
+
}
|
|
5975
|
+
const openedAt = Date.now();
|
|
5976
|
+
const base = Object.assign({ address: session.address, asset: session.asset }, (session.quote ? { quote: session.quote } : {}));
|
|
5977
|
+
if (popup) {
|
|
5978
|
+
try {
|
|
5979
|
+
popup.location.replace(session.url);
|
|
5980
|
+
}
|
|
5981
|
+
catch (_c) {
|
|
5982
|
+
try {
|
|
5983
|
+
popup.location.href = session.url;
|
|
5984
|
+
}
|
|
5985
|
+
catch (_d) {
|
|
5986
|
+
try {
|
|
5987
|
+
popup.close();
|
|
5988
|
+
}
|
|
5989
|
+
catch ( /* ignore */_e) { /* ignore */ }
|
|
5990
|
+
throw new Error('onramp: popup failed to navigate - retry with { newTab: true }');
|
|
5991
|
+
}
|
|
5992
|
+
}
|
|
5993
|
+
await new Promise((resolve) => {
|
|
5994
|
+
const timer = setInterval(() => {
|
|
5995
|
+
if (popup.closed) {
|
|
5996
|
+
clearInterval(timer);
|
|
5997
|
+
resolve();
|
|
5998
|
+
}
|
|
5999
|
+
}, 500);
|
|
6000
|
+
});
|
|
6001
|
+
const fill = await verifyCompletion(openedAt, session.asset, (_a = options.completionTimeoutMs) !== null && _a !== void 0 ? _a : 24000);
|
|
6002
|
+
if (fill) {
|
|
6003
|
+
return Object.assign(Object.assign(Object.assign(Object.assign({}, base), { status: 'completed' }), (fill.amountOut !== null ? { actualOut: fill.amountOut } : {})), (fill.txHash ? { txHash: fill.txHash } : {}));
|
|
6004
|
+
}
|
|
6005
|
+
return Object.assign(Object.assign({}, base), { status: 'closed' });
|
|
6006
|
+
}
|
|
6007
|
+
// New-tab path (popup was blocked or explicitly requested): cross-origin tab
|
|
6008
|
+
// close is not observable, so resolve immediately; verify via onrampStatus().
|
|
6009
|
+
const tab = window.open(session.url, '_blank');
|
|
6010
|
+
if (!tab)
|
|
6011
|
+
throw new Error('onramp: window.open was blocked - call onramp() from a click handler');
|
|
6012
|
+
return Object.assign(Object.assign({}, base), { status: 'opened' });
|
|
6013
|
+
}
|
|
6014
|
+
|
|
5769
6015
|
/**
|
|
5770
6016
|
* Extracts a human-readable error message from Solana transaction logs.
|
|
5771
6017
|
*/
|
|
@@ -5895,7 +6141,7 @@ class PrivyExpoProvider {
|
|
|
5895
6141
|
return { provider: this, address: session.address };
|
|
5896
6142
|
}
|
|
5897
6143
|
// Stale session — clear and continue to fresh login flow
|
|
5898
|
-
getActiveSessionManager().clearSession();
|
|
6144
|
+
await getActiveSessionManager().clearSession();
|
|
5899
6145
|
}
|
|
5900
6146
|
// Trigger Privy login
|
|
5901
6147
|
const privyUser = await this.privyMethods.login();
|
|
@@ -6076,7 +6322,7 @@ class PrivyExpoProvider {
|
|
|
6076
6322
|
if ((_a = this.privyMethods) === null || _a === void 0 ? void 0 : _a.isAuthenticated) {
|
|
6077
6323
|
await this.privyMethods.logout();
|
|
6078
6324
|
}
|
|
6079
|
-
getActiveSessionManager().clearSession();
|
|
6325
|
+
await getActiveSessionManager().clearSession();
|
|
6080
6326
|
}
|
|
6081
6327
|
async getNativeMethods() {
|
|
6082
6328
|
return this.privyMethods;
|
|
@@ -6149,5 +6395,5 @@ class PrivyExpoProvider {
|
|
|
6149
6395
|
}
|
|
6150
6396
|
}
|
|
6151
6397
|
|
|
6152
|
-
export { useAuth as A, useQuery as B, useBuildRun as C, apps as D, EMBEDDED_WALLET_UNSUPPORTED as E, deserializeTransaction as F, getIdToken as G,
|
|
6153
|
-
//# sourceMappingURL=index.native-
|
|
6398
|
+
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 };
|
|
6399
|
+
//# sourceMappingURL=index.native-Crfn_NOB.mjs.map
|