@bounded-sh/client 0.0.112-ux-audit.1 → 0.0.112
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/bounded-widget-modal.d.ts +24 -2
- package/dist/auth/oidc-auth.d.ts +1 -1
- package/dist/{email-auth-provider-eyi3Wpnx.mjs → email-auth-provider-BV1Iea4A.mjs} +2 -2
- package/dist/{email-auth-provider-BfCH7K6N.mjs.map → email-auth-provider-BV1Iea4A.mjs.map} +1 -1
- package/dist/{email-auth-provider-CG5cxOj0.js → email-auth-provider-CyFsnSzx.js} +2 -2
- package/dist/{email-auth-provider-CG5cxOj0.js.map → email-auth-provider-CyFsnSzx.js.map} +1 -1
- package/dist/{email-auth-provider-BfCH7K6N.mjs → email-auth-provider-D0_iXVYk.mjs} +2 -2
- package/dist/{email-auth-provider-eyi3Wpnx.mjs.map → email-auth-provider-D0_iXVYk.mjs.map} +1 -1
- package/dist/{email-auth-provider-DTrCfOJd.js → email-auth-provider-OVzQ63dn.js} +2 -2
- package/dist/{email-auth-provider-DTrCfOJd.js.map → email-auth-provider-OVzQ63dn.js.map} +1 -1
- package/dist/{guest-auth-provider-Tndt6tzJ.js → guest-auth-provider-0WlOutqU.js} +2 -2
- package/dist/{guest-auth-provider-Tndt6tzJ.js.map → guest-auth-provider-0WlOutqU.js.map} +1 -1
- package/dist/{guest-auth-provider-DjOFjtnk.mjs → guest-auth-provider-AP80a700.mjs} +2 -2
- package/dist/{guest-auth-provider-BsW56oJg.mjs.map → guest-auth-provider-AP80a700.mjs.map} +1 -1
- package/dist/{guest-auth-provider-Dtl1J-2F.js → guest-auth-provider-B4WIWOjV.js} +2 -2
- package/dist/{guest-auth-provider-Dtl1J-2F.js.map → guest-auth-provider-B4WIWOjV.js.map} +1 -1
- package/dist/{guest-auth-provider-BsW56oJg.mjs → guest-auth-provider-Cqx1zH23.mjs} +2 -2
- package/dist/{guest-auth-provider-DjOFjtnk.mjs.map → guest-auth-provider-Cqx1zH23.mjs.map} +1 -1
- package/dist/{index-Brdwqgqs.js → index-BmNNZlTl.js} +226 -67
- package/dist/index-BmNNZlTl.js.map +1 -0
- package/dist/{index-CRxls8Qc.mjs → index-D76-cZcD.mjs} +226 -67
- package/dist/index-D76-cZcD.mjs.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{index.native-ingNhWPw.js → index.native-C5glqiTU.js} +59 -14
- package/dist/index.native-C5glqiTU.js.map +1 -0
- package/dist/{index.native-CAH_ks0s.mjs → index.native-DyR93n8K.mjs} +59 -14
- package/dist/index.native-DyR93n8K.mjs.map +1 -0
- package/dist/index.native.js +1 -1
- package/dist/index.native.mjs +1 -1
- package/dist/{injected-evm-wallet-provider-Cao1U0NZ.js → injected-evm-wallet-provider-4ICYUEU4.js} +2 -2
- package/dist/{injected-evm-wallet-provider-Cao1U0NZ.js.map → injected-evm-wallet-provider-4ICYUEU4.js.map} +1 -1
- package/dist/{injected-evm-wallet-provider-CwOGXCDt.js → injected-evm-wallet-provider-C8YItSiB.js} +2 -2
- package/dist/{injected-evm-wallet-provider-CwOGXCDt.js.map → injected-evm-wallet-provider-C8YItSiB.js.map} +1 -1
- package/dist/{injected-evm-wallet-provider-DXjwPjLW.mjs → injected-evm-wallet-provider-CS4R8c51.mjs} +2 -2
- package/dist/{injected-evm-wallet-provider-DXjwPjLW.mjs.map → injected-evm-wallet-provider-CS4R8c51.mjs.map} +1 -1
- package/dist/{injected-evm-wallet-provider-BjkCeRBL.mjs → injected-evm-wallet-provider-WxUbyMZL.mjs} +2 -2
- package/dist/{injected-evm-wallet-provider-BjkCeRBL.mjs.map → injected-evm-wallet-provider-WxUbyMZL.mjs.map} +1 -1
- package/dist/{injected-wallet-provider-CfYNIIHd.mjs → injected-wallet-provider-D1ygndjX.mjs} +2 -2
- package/dist/{injected-wallet-provider-CfYNIIHd.mjs.map → injected-wallet-provider-D1ygndjX.mjs.map} +1 -1
- package/dist/{injected-wallet-provider-LTnqoaYD.js → injected-wallet-provider-D8vIsUHf.js} +2 -2
- package/dist/{injected-wallet-provider-LTnqoaYD.js.map → injected-wallet-provider-D8vIsUHf.js.map} +1 -1
- package/dist/{injected-wallet-provider-BG6gGNch.mjs → injected-wallet-provider-UuGrzdNW.mjs} +2 -2
- package/dist/{injected-wallet-provider-BG6gGNch.mjs.map → injected-wallet-provider-UuGrzdNW.mjs.map} +1 -1
- package/dist/{injected-wallet-provider-DSlHfgMU.js → injected-wallet-provider-nwd3ialR.js} +2 -2
- package/dist/{injected-wallet-provider-DSlHfgMU.js.map → injected-wallet-provider-nwd3ialR.js.map} +1 -1
- package/package.json +2 -2
- package/dist/index-Brdwqgqs.js.map +0 -1
- package/dist/index-CRxls8Qc.mjs.map +0 -1
- package/dist/index.native-CAH_ks0s.mjs.map +0 -1
- package/dist/index.native-ingNhWPw.js.map +0 -1
|
@@ -35,6 +35,13 @@ function walletOperationGuard() {
|
|
|
35
35
|
throw new Error('wallet_session_changed');
|
|
36
36
|
};
|
|
37
37
|
}
|
|
38
|
+
function walletLoginGuard(signal) {
|
|
39
|
+
const generation = ++loginGeneration;
|
|
40
|
+
return () => {
|
|
41
|
+
if ((signal === null || signal === void 0 ? void 0 : signal.aborted) || generation !== loginGeneration)
|
|
42
|
+
throw new Error('wallet_login_cancelled');
|
|
43
|
+
};
|
|
44
|
+
}
|
|
38
45
|
// This realm owns one active SDK session and one retained signer frame.
|
|
39
46
|
let activeWalletState;
|
|
40
47
|
function setWalletSessionState(key, state) { activeWalletState = { key, state }; }
|
|
@@ -3869,9 +3876,13 @@ async function ensureSolanaMobileWalletRegistered(config, network) {
|
|
|
3869
3876
|
}
|
|
3870
3877
|
catch (_e) {
|
|
3871
3878
|
throw new WalletConfigError(`Bounded: Solana Mobile wallet support needs the optional peer dependency ` +
|
|
3872
|
-
`'@solana-mobile/wallet-standard-mobile', which is not installed.
|
|
3873
|
-
`
|
|
3874
|
-
`
|
|
3879
|
+
`'@solana-mobile/wallet-standard-mobile', which is not installed. Until it is, ` +
|
|
3880
|
+
`Android users (Seeker, Saga, any phone browser) get NO wallet to connect: ` +
|
|
3881
|
+
`installed wallet apps are never injected into a page there, the mobile adapter ` +
|
|
3882
|
+
`is the only route. Fix: npm install @solana-mobile/wallet-standard-mobile ` +
|
|
3883
|
+
`(a web app also needs legacy-peer-deps=true in its .npmrc, because the adapter's ` +
|
|
3884
|
+
`protocol package declares react-native as a peer). Or leave the Solana Mobile ` +
|
|
3885
|
+
`wallet out of this app's wallet configuration on purpose.`);
|
|
3875
3886
|
}
|
|
3876
3887
|
const { registerMwa, createDefaultChainSelector, SolanaMobileWalletAdapterWalletName, SolanaMobileWalletAdapterRemoteWalletName, } = mwa;
|
|
3877
3888
|
const before = new Set(getWallets().get());
|
|
@@ -3958,7 +3969,7 @@ const OVERLAY_CSS = `
|
|
|
3958
3969
|
:host{all:initial}
|
|
3959
3970
|
*{box-sizing:border-box}
|
|
3960
3971
|
.bw-ov{position:fixed;inset:0;z-index:2147483647;display:none;
|
|
3961
|
-
font-family:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
|
|
3972
|
+
font-family:"Bounded Schibsted Grotesk","Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
|
|
3962
3973
|
.bw-ov.mounted{display:block}
|
|
3963
3974
|
.bw-back{position:fixed;inset:0;background:rgba(4,4,5,.62);opacity:0;
|
|
3964
3975
|
transition:opacity .24s ease;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}
|
|
@@ -3991,6 +4002,29 @@ const OVERLAY_CSS = `
|
|
|
3991
4002
|
}
|
|
3992
4003
|
@media(prefers-reduced-motion:reduce){.bw-back,.bw-panel{transition:opacity .12s linear!important}}
|
|
3993
4004
|
`;
|
|
4005
|
+
/**
|
|
4006
|
+
* Style the shadow tree with `css` as constructed stylesheets, and say whether it could. A host
|
|
4007
|
+
* CSP whose style-src lacks 'unsafe-inline' refuses an inline <style> even inside a shadow root,
|
|
4008
|
+
* which left the widget unstyled (the Bounded router compiles exactly `style-src 'self'` for an
|
|
4009
|
+
* app that declares browser boundaries and names no style sources); CSP does not govern
|
|
4010
|
+
* constructed sheets. A browser without them (they shipped with adoptedStyleSheets everywhere)
|
|
4011
|
+
* gets <style> elements instead (styleTag).
|
|
4012
|
+
*/
|
|
4013
|
+
function adoptStyles(shadow, css) {
|
|
4014
|
+
if (!("adoptedStyleSheets" in shadow))
|
|
4015
|
+
return false;
|
|
4016
|
+
shadow.adoptedStyleSheets = css.map((text) => {
|
|
4017
|
+
const sheet = new CSSStyleSheet();
|
|
4018
|
+
sheet.replaceSync(text);
|
|
4019
|
+
return sheet;
|
|
4020
|
+
});
|
|
4021
|
+
return true;
|
|
4022
|
+
}
|
|
4023
|
+
function styleTag(css) {
|
|
4024
|
+
const style = document.createElement("style");
|
|
4025
|
+
style.textContent = css;
|
|
4026
|
+
return style;
|
|
4027
|
+
}
|
|
3994
4028
|
let modal = null;
|
|
3995
4029
|
function getModal() {
|
|
3996
4030
|
if (modal)
|
|
@@ -3998,9 +4032,8 @@ function getModal() {
|
|
|
3998
4032
|
const root = document.createElement("div");
|
|
3999
4033
|
root.setAttribute("data-bounded-widget", "");
|
|
4000
4034
|
const shadow = root.attachShadow({ mode: "open" });
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
shadow.appendChild(style);
|
|
4035
|
+
if (!adoptStyles(shadow, [OVERLAY_CSS]))
|
|
4036
|
+
shadow.appendChild(styleTag(OVERLAY_CSS));
|
|
4004
4037
|
const overlay = document.createElement("div");
|
|
4005
4038
|
overlay.className = "bw-ov";
|
|
4006
4039
|
const back = document.createElement("div");
|
|
@@ -4135,12 +4168,15 @@ function fitPanelToContent(m) {
|
|
|
4135
4168
|
m.fitObserver = ro;
|
|
4136
4169
|
}
|
|
4137
4170
|
}
|
|
4138
|
-
/** Switch the modal into NATIVE-content mode: detach any iframe, mount `node
|
|
4139
|
-
function mountContent(m, node) {
|
|
4171
|
+
/** Switch the modal into NATIVE-content mode: detach any iframe, mount `node`, styled by `css` until the next mount. */
|
|
4172
|
+
function mountContent(m, node, css) {
|
|
4140
4173
|
var _a;
|
|
4141
4174
|
detachIframe(m);
|
|
4142
4175
|
(_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
4143
4176
|
m.fitObserver = null;
|
|
4177
|
+
const shadow = m.root.shadowRoot;
|
|
4178
|
+
if (!(shadow && adoptStyles(shadow, [OVERLAY_CSS, css] )) && css)
|
|
4179
|
+
node.prepend(styleTag(css));
|
|
4144
4180
|
m.content.replaceChildren(node);
|
|
4145
4181
|
m.content.style.display = "block";
|
|
4146
4182
|
}
|
|
@@ -4202,27 +4238,39 @@ function detachIframe(m) {
|
|
|
4202
4238
|
m.iframeContext = "";
|
|
4203
4239
|
m.onIframeMessage = null;
|
|
4204
4240
|
}
|
|
4205
|
-
|
|
4206
|
-
|
|
4207
|
-
|
|
4208
|
-
|
|
4241
|
+
/**
|
|
4242
|
+
* The Bounded editorial typefaces the hosted auth page uses (Newsreader display, Schibsted
|
|
4243
|
+
* Grotesk body), registered once at the document level so they apply inside the shadow root.
|
|
4244
|
+
*
|
|
4245
|
+
* The Bounded router serves them same-origin at /__bounded/fonts/*.woff2 on every site it
|
|
4246
|
+
* serves (bounded-router scripts/build-widget-styles.cjs), and they load through the FontFace
|
|
4247
|
+
* API: no stylesheet, so a host CSP's style-src never applies, and the only fetch is same-origin,
|
|
4248
|
+
* which font-src 'self' admits. Google Fonts CSS injected here was refused by strict hosts such
|
|
4249
|
+
* as the OpenApps venue (style-src 'self'), with a CSP error on every page. A page the router
|
|
4250
|
+
* does not serve has none of its /__bounded/ scripts: nothing is fetched there, and the widget's
|
|
4251
|
+
* font stacks fall back to the page's own faces or system ones. The Bounded-prefixed family
|
|
4252
|
+
* names never restyle the host page's own text.
|
|
4253
|
+
*/
|
|
4254
|
+
const WIDGET_FONT_FACES = [
|
|
4255
|
+
{ family: "Bounded Newsreader", url: "/__bounded/fonts/newsreader.woff2", weight: "200 800" },
|
|
4256
|
+
{ family: "Bounded Schibsted Grotesk", url: "/__bounded/fonts/schibsted.woff2", weight: "400 900" },
|
|
4257
|
+
];
|
|
4209
4258
|
function ensureFonts() {
|
|
4210
|
-
if (!hasDOM$5())
|
|
4259
|
+
if (!hasDOM$5() || typeof FontFace === "undefined" || !document.fonts)
|
|
4211
4260
|
return;
|
|
4212
|
-
if (document.querySelector('
|
|
4261
|
+
if (!document.querySelector('script[src^="/__bounded/"]'))
|
|
4213
4262
|
return;
|
|
4214
|
-
const
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4219
|
-
|
|
4220
|
-
|
|
4221
|
-
|
|
4222
|
-
|
|
4223
|
-
|
|
4224
|
-
|
|
4225
|
-
document.head.append(pre1, pre2, link);
|
|
4263
|
+
const registered = new Set();
|
|
4264
|
+
document.fonts.forEach((face) => registered.add(face.family.replace(/^["']|["']$/g, "")));
|
|
4265
|
+
for (const { family, url, weight } of WIDGET_FONT_FACES) {
|
|
4266
|
+
if (registered.has(family))
|
|
4267
|
+
continue;
|
|
4268
|
+
// Against the page's origin, not its base URL: a <base href> elsewhere must not move the fetch off this origin.
|
|
4269
|
+
const face = new FontFace(family, `url("${location.origin}${url}") format("woff2")`, { weight, display: "swap" });
|
|
4270
|
+
document.fonts.add(face);
|
|
4271
|
+
// A face that cannot load is taken back out; the font stack falls back to the next face.
|
|
4272
|
+
face.load().catch(() => { document.fonts.delete(face); });
|
|
4273
|
+
}
|
|
4226
4274
|
}
|
|
4227
4275
|
|
|
4228
4276
|
// turnkey-wallet-widget.ts - runs one Turnkey signer exchange inside the shared
|
|
@@ -5760,20 +5808,21 @@ async function loginWithPopup(opts) {
|
|
|
5760
5808
|
// Turnkey-native login path (authMode:"turnkey"), which mints the session server-side
|
|
5761
5809
|
// after Turnkey verifies the email OTP - there is no OIDC code to exchange. Wiring is
|
|
5762
5810
|
// identical to exchangeCode's tail so the realtime worker cannot tell them apart.
|
|
5763
|
-
async function establishInlineSession(data, beforeCommit, beforeStore) {
|
|
5811
|
+
async function establishInlineSession(data, beforeCommit, beforeStore, onCommitting) {
|
|
5764
5812
|
const base = await issuerBase();
|
|
5813
|
+
beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit();
|
|
5765
5814
|
const idToken = tokenField(data, 'id_token', 'idToken');
|
|
5766
5815
|
if (!idToken)
|
|
5767
5816
|
throw new Error('No id_token in the inline session response');
|
|
5768
5817
|
const accessToken = tokenField(data, 'access_token', 'accessToken') || idToken;
|
|
5769
5818
|
const refreshToken = boundedRefreshTokenField(data);
|
|
5770
5819
|
const user = userFromIdToken(idToken);
|
|
5771
|
-
const rollback = await sessionManager().storeSession(String(user.address || ''), accessToken, idToken, refreshToken, base, () => { });
|
|
5820
|
+
const rollback = await sessionManager().storeSession(String(user.address || ''), accessToken, idToken, refreshToken, base, () => { beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit(); });
|
|
5772
5821
|
// Adopt the OIDC provider on EVERY session surface - including core's signing
|
|
5773
5822
|
// pointer (getConfig().authProvider), which this path used to skip, leaving
|
|
5774
5823
|
// onchain writes routed through a previous (e.g. logged-out wallet) provider.
|
|
5775
5824
|
try {
|
|
5776
|
-
await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit });
|
|
5825
|
+
await adoptSessionProvider({ provider: oidcProviderSingleton, method: 'email', configKey: 'direct:oidc', user, beforeCommit: () => { beforeCommit === null || beforeCommit === void 0 ? void 0 : beforeCommit(); onCommitting === null || onCommitting === void 0 ? void 0 : onCommitting(); } });
|
|
5777
5826
|
}
|
|
5778
5827
|
catch (error) {
|
|
5779
5828
|
if (typeof rollback === 'function')
|
|
@@ -6241,7 +6290,7 @@ async function getAuthProvider(config, fromStorage = false) {
|
|
|
6241
6290
|
// manager as legacy email sessions. Human login should be started
|
|
6242
6291
|
// with loginWithRedirect(); this provider is kept as the restore and
|
|
6243
6292
|
// logout surface for those sessions.
|
|
6244
|
-
currentAuthProvider = new (await import('./email-auth-provider-
|
|
6293
|
+
currentAuthProvider = new (await import('./email-auth-provider-BV1Iea4A.mjs')).EmailAuthProvider();
|
|
6245
6294
|
break;
|
|
6246
6295
|
case "phantom": {
|
|
6247
6296
|
// First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
|
|
@@ -6283,7 +6332,7 @@ async function getAuthProvider(config, fromStorage = false) {
|
|
|
6283
6332
|
// (bounded-login-widget.ts), so registration never counts as
|
|
6284
6333
|
// evidence the user has a wallet.
|
|
6285
6334
|
await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
|
|
6286
|
-
const { InjectedWalletProvider } = await import('./injected-wallet-provider-
|
|
6335
|
+
const { InjectedWalletProvider } = await import('./injected-wallet-provider-UuGrzdNW.mjs');
|
|
6287
6336
|
currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
|
|
6288
6337
|
break;
|
|
6289
6338
|
}
|
|
@@ -6296,7 +6345,7 @@ async function getAuthProvider(config, fromStorage = false) {
|
|
|
6296
6345
|
// Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
|
|
6297
6346
|
// to point at a specific wallet or set chainId/domain.
|
|
6298
6347
|
const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
|
|
6299
|
-
const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-
|
|
6348
|
+
const { InjectedEvmWalletProvider } = await import('./injected-evm-wallet-provider-WxUbyMZL.mjs');
|
|
6300
6349
|
currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
|
|
6301
6350
|
break;
|
|
6302
6351
|
}
|
|
@@ -6321,7 +6370,7 @@ async function getAuthProvider(config, fromStorage = false) {
|
|
|
6321
6370
|
break;
|
|
6322
6371
|
case "guest":
|
|
6323
6372
|
// Anonymous auth: a device-local ed25519 keypair signs the challenge.
|
|
6324
|
-
currentAuthProvider = new (await import('./guest-auth-provider-
|
|
6373
|
+
currentAuthProvider = new (await import('./guest-auth-provider-Cqx1zH23.mjs')).GuestAuthProvider();
|
|
6325
6374
|
break;
|
|
6326
6375
|
}
|
|
6327
6376
|
if (!currentAuthProvider) {
|
|
@@ -6383,7 +6432,7 @@ async function signInAnonymously() {
|
|
|
6383
6432
|
// Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
|
|
6384
6433
|
// the main bundle for data-only apps. Specifier is a static string literal
|
|
6385
6434
|
// so bundle-splitting can see it.
|
|
6386
|
-
const provider = new (await import('./guest-auth-provider-
|
|
6435
|
+
const provider = new (await import('./guest-auth-provider-Cqx1zH23.mjs')).GuestAuthProvider();
|
|
6387
6436
|
setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
|
|
6388
6437
|
// adoptSessionProvider awaits only getConfig() (already resolved here) and then
|
|
6389
6438
|
// runs every write - provider/method/config/marker/user - in one synchronous
|
|
@@ -6554,7 +6603,7 @@ function ensureSharedWalletLoginReady() {
|
|
|
6554
6603
|
if (!sharedWalletReady) {
|
|
6555
6604
|
sharedWalletReady = (async () => {
|
|
6556
6605
|
await getConfig();
|
|
6557
|
-
await import('./injected-wallet-provider-
|
|
6606
|
+
await import('./injected-wallet-provider-UuGrzdNW.mjs');
|
|
6558
6607
|
})().catch((error) => {
|
|
6559
6608
|
sharedWalletReady = null;
|
|
6560
6609
|
throw error;
|
|
@@ -6657,7 +6706,7 @@ async function loginWithWallet(options) {
|
|
|
6657
6706
|
await ensureSharedWalletLoginReady();
|
|
6658
6707
|
else
|
|
6659
6708
|
await ensureWalletLoginReady();
|
|
6660
|
-
const { InjectedWalletProvider } = await import('./injected-wallet-provider-
|
|
6709
|
+
const { InjectedWalletProvider } = await import('./injected-wallet-provider-UuGrzdNW.mjs');
|
|
6661
6710
|
// Keep the RAW wallet provider: the offchain wrapper forwards only a fixed
|
|
6662
6711
|
// set of methods, so cleanup below must speak to the provider that actually
|
|
6663
6712
|
// holds the config rather than to whatever is wrapping it.
|
|
@@ -7024,6 +7073,13 @@ function registerStorageBridge() {
|
|
|
7024
7073
|
void syncStoredSession().catch((error) => console.warn('Bounded session synchronization failed:', error));
|
|
7025
7074
|
});
|
|
7026
7075
|
}
|
|
7076
|
+
function sameSessionIdentity(next, previous) {
|
|
7077
|
+
if (!next || !previous)
|
|
7078
|
+
return false;
|
|
7079
|
+
const a = deriveUserIdentityFromIdToken(next);
|
|
7080
|
+
const b = deriveUserIdentityFromIdToken(previous);
|
|
7081
|
+
return !!a.id && a.id === b.id && a.address === b.address && a.email === b.email && a.evmAddress === b.evmAddress;
|
|
7082
|
+
}
|
|
7027
7083
|
async function syncStoredSession() {
|
|
7028
7084
|
const config = initializedConfig;
|
|
7029
7085
|
if (!config)
|
|
@@ -7032,6 +7088,18 @@ async function syncStoredSession() {
|
|
|
7032
7088
|
const issuer = WebSessionManager.getIssuer();
|
|
7033
7089
|
if (token === observedIdToken && issuer === observedIssuer)
|
|
7034
7090
|
return;
|
|
7091
|
+
// Another tab refreshed this same session (tokens rotate every few minutes):
|
|
7092
|
+
// who is signed in did not change, so keep the user and its live reads, whose
|
|
7093
|
+
// sockets take the new token on their own, instead of hiding and restoring it.
|
|
7094
|
+
if (currentUser && issuer === observedIssuer && sameSessionIdentity(token, observedIdToken)) {
|
|
7095
|
+
const record = await WebSessionManager.readSessionWithGeneration();
|
|
7096
|
+
if (config !== initializedConfig || WebSessionManager.getIdToken() !== token)
|
|
7097
|
+
return;
|
|
7098
|
+
if ((record === null || record === void 0 ? void 0 : record.idToken) === token && (!record.appId || record.appId === config.appId)) {
|
|
7099
|
+
observedIdToken = token;
|
|
7100
|
+
return;
|
|
7101
|
+
}
|
|
7102
|
+
}
|
|
7035
7103
|
observedIdToken = token;
|
|
7036
7104
|
observedIssuer = issuer;
|
|
7037
7105
|
const generation = ++initializationGeneration;
|
|
@@ -7826,10 +7894,18 @@ async function startTurnkeyEmailLogin(email) {
|
|
|
7826
7894
|
if (!hasDOM$1()) {
|
|
7827
7895
|
throw new Error('Turnkey-native email login is web-only - on React Native use loginWithRedirect()');
|
|
7828
7896
|
}
|
|
7897
|
+
const generation = getInitializationGeneration();
|
|
7898
|
+
const walletGuard = walletOperationGuard();
|
|
7899
|
+
const assertContext = () => {
|
|
7900
|
+
walletGuard();
|
|
7901
|
+
if (generation !== getInitializationGeneration())
|
|
7902
|
+
throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
|
|
7903
|
+
};
|
|
7829
7904
|
const trimmed = email.trim();
|
|
7830
7905
|
if (!trimmed)
|
|
7831
7906
|
throw new Error('Enter your email to continue.');
|
|
7832
7907
|
const { base, appId } = await inlineAuthEndpoint();
|
|
7908
|
+
assertContext();
|
|
7833
7909
|
if (!appId)
|
|
7834
7910
|
throw new Error('appId is not configured (init the SDK with your appId)');
|
|
7835
7911
|
const res = await fetch(`${base}/wallet/turnkey/auth/init`, {
|
|
@@ -7841,6 +7917,7 @@ async function startTurnkeyEmailLogin(email) {
|
|
|
7841
7917
|
if (!res.ok || !data || typeof data.otpId !== 'string' || typeof data.otpEncryptionTargetBundle !== 'string') {
|
|
7842
7918
|
throw new Error(String((data && data.error) || 'Could not send a code. Please try again.'));
|
|
7843
7919
|
}
|
|
7920
|
+
assertContext();
|
|
7844
7921
|
const otpId = data.otpId;
|
|
7845
7922
|
const otpEncryptionTargetBundle = data.otpEncryptionTargetBundle;
|
|
7846
7923
|
// A tenant-origin login always gets a ceremony id; without one the OTP frame refuses to run
|
|
@@ -7856,14 +7933,20 @@ async function startTurnkeyEmailLogin(email) {
|
|
|
7856
7933
|
: undefined;
|
|
7857
7934
|
return {
|
|
7858
7935
|
email: trimmed, frameUrl, signerOrigin,
|
|
7859
|
-
run(iframe,
|
|
7936
|
+
run(iframe, signal) {
|
|
7937
|
+
try {
|
|
7938
|
+
assertContext();
|
|
7939
|
+
}
|
|
7940
|
+
catch (error) {
|
|
7941
|
+
return Promise.reject(error);
|
|
7942
|
+
}
|
|
7860
7943
|
if (!ceremonyId) {
|
|
7861
7944
|
return Promise.reject(new Error('This app is not configured for in-frame wallet login. Please try again.'));
|
|
7862
7945
|
}
|
|
7863
|
-
const run = { iframe, base, signerOrigin, appId, email: trimmed, otpId, otpEncryptionTargetBundle, ceremonyId };
|
|
7946
|
+
const run = { iframe, base, signerOrigin, appId, email: trimmed, otpId, otpEncryptionTargetBundle, ceremonyId, generation };
|
|
7864
7947
|
// BSA-2026-004: the login code is typed only in /wallet/turnkey/otp-frame.
|
|
7865
7948
|
// The scoped signer URL is for post-login signing, not OTP entry.
|
|
7866
|
-
return runOtpFrame(run);
|
|
7949
|
+
return runOtpFrame(run, signal);
|
|
7867
7950
|
},
|
|
7868
7951
|
};
|
|
7869
7952
|
}
|
|
@@ -7872,7 +7955,7 @@ async function startTurnkeyEmailLogin(email) {
|
|
|
7872
7955
|
// frame 'result' {session} -> we establish the inline session and resolve the User.
|
|
7873
7956
|
// When the frame asks for the signing-session authorization ('need-authorization') we mint a
|
|
7874
7957
|
// createWallet action authorization on THIS (registered) origin and post it in - never a key.
|
|
7875
|
-
function runOtpFrame(run) {
|
|
7958
|
+
function runOtpFrame(run, signal) {
|
|
7876
7959
|
const tenantOrigin = window.location.origin;
|
|
7877
7960
|
const url = `${run.base.replace(/\/$/, '')}/wallet/turnkey/otp-frame`
|
|
7878
7961
|
+ `?appId=${encodeURIComponent(run.appId)}`
|
|
@@ -7882,13 +7965,48 @@ function runOtpFrame(run) {
|
|
|
7882
7965
|
return new Promise((resolve, reject) => {
|
|
7883
7966
|
let settled = false;
|
|
7884
7967
|
let started = false;
|
|
7885
|
-
|
|
7968
|
+
let receivingResult = false;
|
|
7969
|
+
let committing = false;
|
|
7970
|
+
const walletGuard = walletLoginGuard(signal);
|
|
7971
|
+
const beforeCommit = () => {
|
|
7972
|
+
if (settled)
|
|
7973
|
+
throw new Error('wallet_login_cancelled');
|
|
7974
|
+
walletGuard();
|
|
7975
|
+
if (run.generation !== getInitializationGeneration())
|
|
7976
|
+
throw new Error('Login was superseded by SDK reinitialization. Start sign-in again.');
|
|
7977
|
+
};
|
|
7978
|
+
const finish = (error, user) => {
|
|
7979
|
+
if (settled)
|
|
7980
|
+
return;
|
|
7981
|
+
settled = true;
|
|
7982
|
+
window.removeEventListener('message', onMsg);
|
|
7983
|
+
window.clearTimeout(timer);
|
|
7984
|
+
signal === null || signal === void 0 ? void 0 : signal.removeEventListener('abort', onAbort);
|
|
7985
|
+
if (error) {
|
|
7986
|
+
// Destroy the expired frame's code/key realm as well as its parent listener.
|
|
7987
|
+
run.iframe.src = 'about:blank';
|
|
7988
|
+
reject(error);
|
|
7989
|
+
}
|
|
7990
|
+
else
|
|
7991
|
+
resolve(user);
|
|
7992
|
+
};
|
|
7993
|
+
const onAbort = () => { if (!committing)
|
|
7994
|
+
finish(new Error('cancelled')); };
|
|
7886
7995
|
const onMsg = async (event) => {
|
|
7887
7996
|
if (settled)
|
|
7888
7997
|
return;
|
|
7889
7998
|
// Trust ONLY the signer origin and the frame's own window.
|
|
7890
7999
|
if (event.origin !== run.signerOrigin || event.source !== run.iframe.contentWindow)
|
|
7891
8000
|
return;
|
|
8001
|
+
if (!committing) {
|
|
8002
|
+
try {
|
|
8003
|
+
beforeCommit();
|
|
8004
|
+
}
|
|
8005
|
+
catch (error) {
|
|
8006
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
8007
|
+
return;
|
|
8008
|
+
}
|
|
8009
|
+
}
|
|
7892
8010
|
const d = event.data;
|
|
7893
8011
|
if (!d || typeof d !== 'object')
|
|
7894
8012
|
return;
|
|
@@ -7931,40 +8049,56 @@ function runOtpFrame(run) {
|
|
|
7931
8049
|
return;
|
|
7932
8050
|
}
|
|
7933
8051
|
if (d.type === 'bounded:otp:result') {
|
|
8052
|
+
if (receivingResult)
|
|
8053
|
+
return;
|
|
8054
|
+
receivingResult = true;
|
|
7934
8055
|
if (d.ok && d.session && typeof d.session === 'object') {
|
|
7935
8056
|
try {
|
|
7936
|
-
const user = await establishInlineSession(d.session);
|
|
7937
|
-
|
|
7938
|
-
resolve(user);
|
|
8057
|
+
const user = await establishInlineSession(d.session, beforeCommit, undefined, () => { committing = true; });
|
|
8058
|
+
finish(undefined, user);
|
|
7939
8059
|
}
|
|
7940
8060
|
catch (error) {
|
|
7941
|
-
|
|
7942
|
-
reject(error instanceof Error ? error : new Error(String(error)));
|
|
8061
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
7943
8062
|
}
|
|
7944
8063
|
return;
|
|
7945
8064
|
}
|
|
7946
|
-
|
|
7947
|
-
reject(new Error(String((d && d.error) || 'Invalid or expired code.')));
|
|
8065
|
+
finish(new Error(String((d && d.error) || 'Invalid or expired code.')));
|
|
7948
8066
|
return;
|
|
7949
8067
|
}
|
|
7950
8068
|
// 'bounded:otp:registered' is advisory; login does not depend on it.
|
|
7951
8069
|
};
|
|
7952
8070
|
const post = (msg) => {
|
|
7953
8071
|
var _a;
|
|
8072
|
+
if (settled)
|
|
8073
|
+
return;
|
|
8074
|
+
if (!committing) {
|
|
8075
|
+
try {
|
|
8076
|
+
beforeCommit();
|
|
8077
|
+
}
|
|
8078
|
+
catch (_b) {
|
|
8079
|
+
return;
|
|
8080
|
+
}
|
|
8081
|
+
}
|
|
7954
8082
|
try {
|
|
7955
8083
|
(_a = run.iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(msg, run.signerOrigin);
|
|
7956
8084
|
}
|
|
7957
|
-
catch ( /* frame gone */
|
|
8085
|
+
catch ( /* frame gone */_c) { /* frame gone */ }
|
|
7958
8086
|
};
|
|
7959
8087
|
window.addEventListener('message', onMsg);
|
|
7960
8088
|
// 3 minutes: the human must read the email and type the code. A frame that never reports is a
|
|
7961
8089
|
// failed login the user can retry.
|
|
7962
8090
|
const timer = window.setTimeout(() => {
|
|
7963
|
-
if (
|
|
7964
|
-
|
|
7965
|
-
cleanup();
|
|
7966
|
-
reject(new Error('The sign-in code entry timed out. Please try again.'));
|
|
8091
|
+
if (!committing)
|
|
8092
|
+
finish(new Error('The sign-in code entry timed out. Please try again.'));
|
|
7967
8093
|
}, 3 * 60 * 1000);
|
|
8094
|
+
signal === null || signal === void 0 ? void 0 : signal.addEventListener('abort', onAbort, { once: true });
|
|
8095
|
+
try {
|
|
8096
|
+
beforeCommit();
|
|
8097
|
+
}
|
|
8098
|
+
catch (error) {
|
|
8099
|
+
finish(error instanceof Error ? error : new Error(String(error)));
|
|
8100
|
+
return;
|
|
8101
|
+
}
|
|
7968
8102
|
run.iframe.src = url;
|
|
7969
8103
|
});
|
|
7970
8104
|
}
|
|
@@ -7993,8 +8127,8 @@ const CARD_CSS = `
|
|
|
7993
8127
|
--line:rgba(232,230,222,.08);--line-strong:rgba(232,230,222,.16);
|
|
7994
8128
|
--text:#e8e6de;--dim:#9b9991;--faint:#85837c;
|
|
7995
8129
|
--green:#58c98f;--green-bg:rgba(88,201,143,.09);--green-line:rgba(88,201,143,.28);--red:#ec8488;
|
|
7996
|
-
--disp:"Newsreader","Iowan Old Style",Georgia,serif;
|
|
7997
|
-
--body:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif;
|
|
8130
|
+
--disp:"Bounded Newsreader","Newsreader","Iowan Old Style",Georgia,serif;
|
|
8131
|
+
--body:"Bounded Schibsted Grotesk","Schibsted Grotesk","Helvetica Neue",Arial,sans-serif;
|
|
7998
8132
|
position:relative;padding:34px 32px 20px;color:var(--text);font-family:var(--body);
|
|
7999
8133
|
-webkit-font-smoothing:antialiased}
|
|
8000
8134
|
.x{position:absolute;top:20px;right:24px;border:0;background:transparent;color:var(--faint);
|
|
@@ -8115,6 +8249,17 @@ function discoverWallets(configuredNetwork) {
|
|
|
8115
8249
|
// beats a click that does nothing, which is what "one at a time" looks like from
|
|
8116
8250
|
// the outside.
|
|
8117
8251
|
const BUSY_MSG = "A sign-in is already in progress - finish or cancel it first.";
|
|
8252
|
+
// Shown instead of an empty card when no lane can be offered (wallet-only app,
|
|
8253
|
+
// no wallet in this browser). A phone browser never sees installed wallet apps
|
|
8254
|
+
// as injected providers, so the honest advice there is a different browser,
|
|
8255
|
+
// not "install a wallet".
|
|
8256
|
+
function noLaneCopy(mobile) {
|
|
8257
|
+
return mobile
|
|
8258
|
+
? "No wallet is available in this browser. Open this page in your wallet app's " +
|
|
8259
|
+
"browser, or on a desktop browser with a wallet extension, then try again."
|
|
8260
|
+
: "No wallet was found in this browser. Install a Solana wallet extension such as " +
|
|
8261
|
+
"Phantom or Solflare, reload the page, then try again.";
|
|
8262
|
+
}
|
|
8118
8263
|
const METHOD_LABEL = {
|
|
8119
8264
|
google: "Continue with Google",
|
|
8120
8265
|
apple: "Continue with Apple",
|
|
@@ -8456,12 +8601,9 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8456
8601
|
cancelPendingOpens();
|
|
8457
8602
|
};
|
|
8458
8603
|
m.onBackdrop = cancel;
|
|
8459
|
-
const style = el("style");
|
|
8460
|
-
style.textContent = CARD_CSS;
|
|
8461
8604
|
let emailDraft = "";
|
|
8462
8605
|
const renderChoose = (initialError) => {
|
|
8463
8606
|
const card = el("div", "c");
|
|
8464
|
-
card.appendChild(style.cloneNode(true));
|
|
8465
8607
|
const x = el("button", "x", "Close");
|
|
8466
8608
|
x.type = "button";
|
|
8467
8609
|
x.setAttribute("aria-label", "Close");
|
|
@@ -8678,6 +8820,24 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8678
8820
|
});
|
|
8679
8821
|
};
|
|
8680
8822
|
}
|
|
8823
|
+
// A card with no lane at all. It happens when an app asks for wallet-only
|
|
8824
|
+
// (`methods: []`, `wallet: true`) and this browser has no wallet to offer:
|
|
8825
|
+
// no injected wallet, and no Solana Mobile wallet (off-Android, or the app
|
|
8826
|
+
// never installed the adapter). A blank card between the title and the
|
|
8827
|
+
// footer was the shipped behaviour, and it reads as "the site is broken"
|
|
8828
|
+
// to the person and tells the developer nothing. Say what is missing on
|
|
8829
|
+
// screen, and name the usual cause once in the console, where the
|
|
8830
|
+
// developer's own preview run will show it.
|
|
8831
|
+
if (!hasEmail && social.length === 0 && !walletEnabled) {
|
|
8832
|
+
card.appendChild(el("p", "sub", noLaneCopy(detectMobile())));
|
|
8833
|
+
console.warn("[Bounded] openBoundedWidget has no sign-in lane to show: the app asked for " +
|
|
8834
|
+
"no email or social method and no wallet is available in this browser. " +
|
|
8835
|
+
(offerMobile
|
|
8836
|
+
? "On Android the Solana Mobile wallet is offered only when the app installs " +
|
|
8837
|
+
"'@solana-mobile/wallet-standard-mobile' (see the warning above if registration failed); "
|
|
8838
|
+
: "") +
|
|
8839
|
+
"Add a method, or make sure wallet login is enabled and the mobile adapter is installed.");
|
|
8840
|
+
}
|
|
8681
8841
|
card.appendChild(status);
|
|
8682
8842
|
const foot = el("div", "foot");
|
|
8683
8843
|
const poweredBy = el("a", undefined, "Powered by Bounded");
|
|
@@ -8724,7 +8884,6 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8724
8884
|
const renderSignInGesture = (choice, done) => {
|
|
8725
8885
|
var _a;
|
|
8726
8886
|
const card = el("div", "c");
|
|
8727
|
-
card.appendChild(style.cloneNode(true));
|
|
8728
8887
|
const x = el("button", "x", "Close");
|
|
8729
8888
|
x.type = "button";
|
|
8730
8889
|
x.setAttribute("aria-label", "Close");
|
|
@@ -8745,7 +8904,6 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8745
8904
|
};
|
|
8746
8905
|
const renderWallets = (initialError) => {
|
|
8747
8906
|
const card = el("div", "c");
|
|
8748
|
-
card.appendChild(style.cloneNode(true));
|
|
8749
8907
|
const back = el("button", "back", "\u2190 Back");
|
|
8750
8908
|
back.type = "button";
|
|
8751
8909
|
// Leaving the wallet list supersedes any attempt still preparing, so it
|
|
@@ -8825,7 +8983,6 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8825
8983
|
// experience still feels native; the code input, sealing, and verify all live behind the frame.
|
|
8826
8984
|
const renderOtpFrame = (handle) => {
|
|
8827
8985
|
const card = el("div", "c");
|
|
8828
|
-
card.appendChild(style.cloneNode(true));
|
|
8829
8986
|
const back = el("button", "back", "\u2190 Back");
|
|
8830
8987
|
back.type = "button";
|
|
8831
8988
|
card.appendChild(back);
|
|
@@ -8853,14 +9010,16 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8853
9010
|
setErr(BUSY_MSG);
|
|
8854
9011
|
return card;
|
|
8855
9012
|
}
|
|
8856
|
-
back.addEventListener("click", () => {
|
|
9013
|
+
back.addEventListener("click", () => { invalidateAttempts(); swap(renderChoose()); });
|
|
9014
|
+
const controller = new AbortController();
|
|
9015
|
+
abortScopedLogin = () => controller.abort();
|
|
8857
9016
|
handle
|
|
8858
|
-
.run(frame)
|
|
9017
|
+
.run(frame, controller.signal)
|
|
8859
9018
|
.then((u) => { settleAttempt(attempt); succeed(u); })
|
|
8860
9019
|
.catch((err) => {
|
|
8861
9020
|
if (!settleAttempt(attempt))
|
|
8862
9021
|
return;
|
|
8863
|
-
|
|
9022
|
+
swap(renderChoose(errText(err)));
|
|
8864
9023
|
});
|
|
8865
9024
|
return card;
|
|
8866
9025
|
};
|
|
@@ -8870,7 +9029,7 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8870
9029
|
// land after a newer open has taken over the DOM.
|
|
8871
9030
|
if (!owner.owns())
|
|
8872
9031
|
return;
|
|
8873
|
-
mountContent(m, node);
|
|
9032
|
+
mountContent(m, node, CARD_CSS);
|
|
8874
9033
|
requestAnimationFrame(() => {
|
|
8875
9034
|
if (owner.owns())
|
|
8876
9035
|
fitPanelToContent(m);
|
|
@@ -11192,4 +11351,4 @@ function init(config = {}) {
|
|
|
11192
11351
|
}
|
|
11193
11352
|
|
|
11194
11353
|
export { onrampStatus as $, getAuthProvider as A, signInAnonymously as B, loginWithPrivy as C, loginWithWallet as D, ensureWalletLoginReady as E, ensureSharedWalletLoginReady as F, preloadWalletLogin as G, loginWithRedirect as H, completeLoginFromRedirect as I, getLoginReturnError as J, loginWithPopup as K, openBoundedWidget as L, ensureSignedIn as M, useAuth as N, useQuery as O, useBuildRun as P, apps as Q, signSolanaMessageViaTurnkey as R, getOrCreateTurnkeyWallet as S, openTurnkeyKeyExport as T, getTurnkeyWalletSessionState as U, getTurnkeyWalletRecoveryUrl as V, WalletConfigError as W, startTurnkeyEmailLogin as X, getIdToken as Y, onramp as Z, onrampQuote as _, signMessageWithTurnkey as a, setPlatform as a0, OffchainAuthProvider as a1, PrivyExpoProvider as a2, requestEip6963Providers as a3, connectBroadcast as a4, connectServiceSession as a5, builds as a6, BuildsApiError as a7, mediaAgentTools as a8, NON_TERMINAL_STATES as a9, TERMINAL_STATES as aa, RUN_STATES as ab, PARK_REASONS as ac, FAILURE_REASONS as ad, BUILD_REVIEW_CAPABILITY_SCHEMA as ae, BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA as af, BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA as ag, signTransactionWithTurnkey as b, signTransactionV1WithTurnkey as c, signAndSubmitTransactionWithTurnkey as d, bs58 as e, defaultInjectedProvider as f, getPlatform as g, hasTurnkeyWallet as h, firstStandardWalletProvider as i, setAuthLoading as j, bufferExports as k, setStoredAuthMethod as l, confirmAndCheckTransaction as m, normalizeSolanaRpcUrl as n, defaultInjectedEvmProvider as o, init as p, getCurrentUser as q, resolveSolanaRpcUrl as r, setCurrentUser as s, transactionHasSignature as t, onAuthStateChanged as u, onAuthLoadingChanged as v, getAuthLoading as w, waitForAuthReady as x, login as y, logout as z };
|
|
11195
|
-
//# sourceMappingURL=index-
|
|
11354
|
+
//# sourceMappingURL=index-D76-cZcD.mjs.map
|