@bounded-sh/client 0.0.112-ux-audit.2 → 0.0.113

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 (53) hide show
  1. package/dist/auth/bounded-widget-modal.d.ts +24 -2
  2. package/dist/auth/providers/injected-wallet-provider.d.ts +7 -0
  3. package/dist/auth/providers/injected-wallet-types.d.ts +2 -0
  4. package/dist/auth/providers/solana-mobile-registration.d.ts +18 -0
  5. package/dist/{email-auth-provider-BfCH7K6N.mjs → email-auth-provider-Bk5GSpYT.mjs} +2 -2
  6. package/dist/{email-auth-provider-C7RXLVd9.mjs.map → email-auth-provider-Bk5GSpYT.mjs.map} +1 -1
  7. package/dist/{email-auth-provider-DTrCfOJd.js → email-auth-provider-Cr66GaKw.js} +2 -2
  8. package/dist/{email-auth-provider-DTrCfOJd.js.map → email-auth-provider-Cr66GaKw.js.map} +1 -1
  9. package/dist/{email-auth-provider-53sA5_i2.js → email-auth-provider-DeORQbTK.js} +2 -2
  10. package/dist/{email-auth-provider-53sA5_i2.js.map → email-auth-provider-DeORQbTK.js.map} +1 -1
  11. package/dist/{email-auth-provider-C7RXLVd9.mjs → email-auth-provider-nDZjRDpj.mjs} +2 -2
  12. package/dist/{email-auth-provider-BfCH7K6N.mjs.map → email-auth-provider-nDZjRDpj.mjs.map} +1 -1
  13. package/dist/{guest-auth-provider-00KkSNpE.js → guest-auth-provider-5bX-xt6K.js} +2 -2
  14. package/dist/{guest-auth-provider-00KkSNpE.js.map → guest-auth-provider-5bX-xt6K.js.map} +1 -1
  15. package/dist/{guest-auth-provider-Tndt6tzJ.js → guest-auth-provider-CUNkZVFK.js} +2 -2
  16. package/dist/{guest-auth-provider-Tndt6tzJ.js.map → guest-auth-provider-CUNkZVFK.js.map} +1 -1
  17. package/dist/{guest-auth-provider-DjOFjtnk.mjs → guest-auth-provider-DDudqqxY.mjs} +2 -2
  18. package/dist/{guest-auth-provider-DjOFjtnk.mjs.map → guest-auth-provider-DDudqqxY.mjs.map} +1 -1
  19. package/dist/{guest-auth-provider-2J4fnTX5.mjs → guest-auth-provider-ejUTbjCC.mjs} +2 -2
  20. package/dist/{guest-auth-provider-2J4fnTX5.mjs.map → guest-auth-provider-ejUTbjCC.mjs.map} +1 -1
  21. package/dist/{index-D3kdhCpH.mjs → index-BAiLpNEQ.mjs} +190 -53
  22. package/dist/index-BAiLpNEQ.mjs.map +1 -0
  23. package/dist/{index-DJeEMevo.js → index-BZ0X1Z9X.js} +190 -53
  24. package/dist/index-BZ0X1Z9X.js.map +1 -0
  25. package/dist/index.js +1 -1
  26. package/dist/index.mjs +1 -1
  27. package/dist/{index.native-ingNhWPw.js → index.native-DJSOqyKQ.js} +101 -14
  28. package/dist/index.native-DJSOqyKQ.js.map +1 -0
  29. package/dist/{index.native-CAH_ks0s.mjs → index.native-DcW2klyn.mjs} +100 -15
  30. package/dist/index.native-DcW2klyn.mjs.map +1 -0
  31. package/dist/index.native.js +1 -1
  32. package/dist/index.native.mjs +1 -1
  33. package/dist/{injected-evm-wallet-provider-Cao1U0NZ.js → injected-evm-wallet-provider-BDYWjIOQ.js} +2 -2
  34. package/dist/{injected-evm-wallet-provider-Cao1U0NZ.js.map → injected-evm-wallet-provider-BDYWjIOQ.js.map} +1 -1
  35. package/dist/{injected-evm-wallet-provider-DXjwPjLW.mjs → injected-evm-wallet-provider-BQ_kG1vI.mjs} +2 -2
  36. package/dist/{injected-evm-wallet-provider-DXjwPjLW.mjs.map → injected-evm-wallet-provider-BQ_kG1vI.mjs.map} +1 -1
  37. package/dist/{injected-evm-wallet-provider-BtyG1HOX.mjs → injected-evm-wallet-provider-C4aKa2v1.mjs} +2 -2
  38. package/dist/{injected-evm-wallet-provider-BtyG1HOX.mjs.map → injected-evm-wallet-provider-C4aKa2v1.mjs.map} +1 -1
  39. package/dist/{injected-evm-wallet-provider-IlkuKffc.js → injected-evm-wallet-provider-D06arDQD.js} +2 -2
  40. package/dist/{injected-evm-wallet-provider-IlkuKffc.js.map → injected-evm-wallet-provider-D06arDQD.js.map} +1 -1
  41. package/dist/{injected-wallet-provider-BEYwsn6M.js → injected-wallet-provider-B9jvzDM5.js} +51 -22
  42. package/dist/{injected-wallet-provider-BEYwsn6M.js.map → injected-wallet-provider-B9jvzDM5.js.map} +1 -1
  43. package/dist/{injected-wallet-provider-0SSONh1d.mjs → injected-wallet-provider-Dj-F69gb.mjs} +51 -22
  44. package/dist/{injected-wallet-provider-0SSONh1d.mjs.map → injected-wallet-provider-Dj-F69gb.mjs.map} +1 -1
  45. package/dist/{injected-wallet-provider-DSlHfgMU.js → injected-wallet-provider-Dqmk-m4V.js} +67 -26
  46. package/dist/{injected-wallet-provider-DSlHfgMU.js.map → injected-wallet-provider-Dqmk-m4V.js.map} +1 -1
  47. package/dist/{injected-wallet-provider-CfYNIIHd.mjs → injected-wallet-provider-DxPcYxP-.mjs} +67 -26
  48. package/dist/{injected-wallet-provider-CfYNIIHd.mjs.map → injected-wallet-provider-DxPcYxP-.mjs.map} +1 -1
  49. package/package.json +2 -2
  50. package/dist/index-D3kdhCpH.mjs.map +0 -1
  51. package/dist/index-DJeEMevo.js.map +0 -1
  52. package/dist/index.native-CAH_ks0s.mjs.map +0 -1
  53. package/dist/index.native-ingNhWPw.js.map +0 -1
@@ -3817,6 +3817,46 @@ function looksLikeSolanaMobileWallet(name) {
3817
3817
  function solanaMobileWalletLeavesPage(name) {
3818
3818
  return name === LOCAL_MOBILE_WALLET_NAME;
3819
3819
  }
3820
+ /**
3821
+ * Whether this wallet answers a sign-in from its authorization cache, so the
3822
+ * cached record must be dropped before asking it to sign in.
3823
+ *
3824
+ * Both Mobile Wallet Adapter transports consult their persisted authorization
3825
+ * before EVERY authorize call, including the one that carries the Sign In With
3826
+ * Solana payload (@solana-mobile/wallet-standard-mobile 0.5.3
3827
+ * lib/esm/index.browser.js:1291). A record left by a plain connect holds no
3828
+ * sign-in result, so that sign-in fails with "no sign in result returned by
3829
+ * wallet" without the wallet app ever being asked; a record left by an earlier
3830
+ * sign-in holds that OLD proof, so a later sign-in replays a spent nonce. The
3831
+ * wallet's own `standard:disconnect` clears the record locally - it opens
3832
+ * nothing - and the sign-in that follows then authorizes afresh, with its
3833
+ * payload, in the wallet app. A name check, like leavesPage: this describes the
3834
+ * transport, and the same adapter registered by another SDK behaves the same.
3835
+ */
3836
+ function solanaMobileWalletAnswersSignInFromCache(name) {
3837
+ return mobileWalletNames.has(name);
3838
+ }
3839
+ /**
3840
+ * The Wallet Standard name of a feature a Mobile Wallet Adapter account reports.
3841
+ *
3842
+ * The adapter copies a wallet app's per-account `features` into the Wallet
3843
+ * Standard account untranslated (0.5.3 lib/esm/index.browser.js:1421), and the
3844
+ * MWA protocol names them differently from the Wallet Standard (spec feature
3845
+ * identifiers: `solana:signMessages`, `solana:signInWithSolana`,
3846
+ * `solana:signTransactions`). Filtered by the Wallet Standard names, such an
3847
+ * account looks unable to sign a message or sign in, and the page sees no
3848
+ * connected account after a successful authorization. Wallet Standard names pass
3849
+ * through unchanged, so this applies to every account.
3850
+ */
3851
+ const MWA_FEATURE_NAMES = {
3852
+ 'solana:signMessages': 'solana:signMessage',
3853
+ 'solana:signInWithSolana': 'solana:signIn',
3854
+ 'solana:signTransactions': 'solana:signTransaction',
3855
+ };
3856
+ function walletStandardFeatureName(feature) {
3857
+ var _a;
3858
+ return (_a = MWA_FEATURE_NAMES[feature]) !== null && _a !== void 0 ? _a : feature;
3859
+ }
3820
3860
  /**
3821
3861
  * Register Solana Mobile's MWA wallet with the Wallet Standard registry so
3822
3862
  * wallet discovery surfaces it. Idempotent - the first wallet-lane activation
@@ -3897,9 +3937,13 @@ async function ensureSolanaMobileWalletRegistered(config, network) {
3897
3937
  }
3898
3938
  catch (_e) {
3899
3939
  throw new WalletConfigError(`Bounded: Solana Mobile wallet support needs the optional peer dependency ` +
3900
- `'@solana-mobile/wallet-standard-mobile', which is not installed. Install it ` +
3901
- `in your app to enable the mobile wallet, or leave the Solana Mobile wallet ` +
3902
- `out of this app's wallet configuration.`);
3940
+ `'@solana-mobile/wallet-standard-mobile', which is not installed. Until it is, ` +
3941
+ `Android users (Seeker, Saga, any phone browser) get NO wallet to connect: ` +
3942
+ `installed wallet apps are never injected into a page there, the mobile adapter ` +
3943
+ `is the only route. Fix: npm install @solana-mobile/wallet-standard-mobile ` +
3944
+ `(a web app also needs legacy-peer-deps=true in its .npmrc, because the adapter's ` +
3945
+ `protocol package declares react-native as a peer). Or leave the Solana Mobile ` +
3946
+ `wallet out of this app's wallet configuration on purpose.`);
3903
3947
  }
3904
3948
  const { registerMwa, createDefaultChainSelector, SolanaMobileWalletAdapterWalletName, SolanaMobileWalletAdapterRemoteWalletName, } = mwa;
3905
3949
  const before = new Set(getWallets().get());
@@ -3986,7 +4030,7 @@ const OVERLAY_CSS = `
3986
4030
  :host{all:initial}
3987
4031
  *{box-sizing:border-box}
3988
4032
  .bw-ov{position:fixed;inset:0;z-index:2147483647;display:none;
3989
- font-family:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
4033
+ font-family:"Bounded Schibsted Grotesk","Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
3990
4034
  .bw-ov.mounted{display:block}
3991
4035
  .bw-back{position:fixed;inset:0;background:rgba(4,4,5,.62);opacity:0;
3992
4036
  transition:opacity .24s ease;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}
@@ -4019,6 +4063,29 @@ const OVERLAY_CSS = `
4019
4063
  }
4020
4064
  @media(prefers-reduced-motion:reduce){.bw-back,.bw-panel{transition:opacity .12s linear!important}}
4021
4065
  `;
4066
+ /**
4067
+ * Style the shadow tree with `css` as constructed stylesheets, and say whether it could. A host
4068
+ * CSP whose style-src lacks 'unsafe-inline' refuses an inline <style> even inside a shadow root,
4069
+ * which left the widget unstyled (the Bounded router compiles exactly `style-src 'self'` for an
4070
+ * app that declares browser boundaries and names no style sources); CSP does not govern
4071
+ * constructed sheets. A browser without them (they shipped with adoptedStyleSheets everywhere)
4072
+ * gets <style> elements instead (styleTag).
4073
+ */
4074
+ function adoptStyles(shadow, css) {
4075
+ if (!("adoptedStyleSheets" in shadow))
4076
+ return false;
4077
+ shadow.adoptedStyleSheets = css.map((text) => {
4078
+ const sheet = new CSSStyleSheet();
4079
+ sheet.replaceSync(text);
4080
+ return sheet;
4081
+ });
4082
+ return true;
4083
+ }
4084
+ function styleTag(css) {
4085
+ const style = document.createElement("style");
4086
+ style.textContent = css;
4087
+ return style;
4088
+ }
4022
4089
  let modal = null;
4023
4090
  function getModal() {
4024
4091
  if (modal)
@@ -4026,9 +4093,8 @@ function getModal() {
4026
4093
  const root = document.createElement("div");
4027
4094
  root.setAttribute("data-bounded-widget", "");
4028
4095
  const shadow = root.attachShadow({ mode: "open" });
4029
- const style = document.createElement("style");
4030
- style.textContent = OVERLAY_CSS;
4031
- shadow.appendChild(style);
4096
+ if (!adoptStyles(shadow, [OVERLAY_CSS]))
4097
+ shadow.appendChild(styleTag(OVERLAY_CSS));
4032
4098
  const overlay = document.createElement("div");
4033
4099
  overlay.className = "bw-ov";
4034
4100
  const back = document.createElement("div");
@@ -4163,12 +4229,15 @@ function fitPanelToContent(m) {
4163
4229
  m.fitObserver = ro;
4164
4230
  }
4165
4231
  }
4166
- /** Switch the modal into NATIVE-content mode: detach any iframe, mount `node`. */
4167
- function mountContent(m, node) {
4232
+ /** Switch the modal into NATIVE-content mode: detach any iframe, mount `node`, styled by `css` until the next mount. */
4233
+ function mountContent(m, node, css) {
4168
4234
  var _a;
4169
4235
  detachIframe(m);
4170
4236
  (_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
4171
4237
  m.fitObserver = null;
4238
+ const shadow = m.root.shadowRoot;
4239
+ if (!(shadow && adoptStyles(shadow, [OVERLAY_CSS, css] )) && css)
4240
+ node.prepend(styleTag(css));
4172
4241
  m.content.replaceChildren(node);
4173
4242
  m.content.style.display = "block";
4174
4243
  }
@@ -4230,27 +4299,39 @@ function detachIframe(m) {
4230
4299
  m.iframeContext = "";
4231
4300
  m.onIframeMessage = null;
4232
4301
  }
4233
- // Load the Bounded editorial typefaces (Newsreader display + Schibsted Grotesk body +
4234
- // JetBrains Mono) once at the document level so they apply inside the shadow root. A
4235
- // no-op if already present or off-DOM. Fonts loaded here match the hosted auth page.
4236
- const FONTS_HREF = "https://fonts.googleapis.com/css2?family=Newsreader:opsz,wght@6..72,400;6..72,500&family=Schibsted+Grotesk:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap";
4302
+ /**
4303
+ * The Bounded editorial typefaces the hosted auth page uses (Newsreader display, Schibsted
4304
+ * Grotesk body), registered once at the document level so they apply inside the shadow root.
4305
+ *
4306
+ * The Bounded router serves them same-origin at /__bounded/fonts/*.woff2 on every site it
4307
+ * serves (bounded-router scripts/build-widget-styles.cjs), and they load through the FontFace
4308
+ * API: no stylesheet, so a host CSP's style-src never applies, and the only fetch is same-origin,
4309
+ * which font-src 'self' admits. Google Fonts CSS injected here was refused by strict hosts such
4310
+ * as the OpenApps venue (style-src 'self'), with a CSP error on every page. A page the router
4311
+ * does not serve has none of its /__bounded/ scripts: nothing is fetched there, and the widget's
4312
+ * font stacks fall back to the page's own faces or system ones. The Bounded-prefixed family
4313
+ * names never restyle the host page's own text.
4314
+ */
4315
+ const WIDGET_FONT_FACES = [
4316
+ { family: "Bounded Newsreader", url: "/__bounded/fonts/newsreader.woff2", weight: "200 800" },
4317
+ { family: "Bounded Schibsted Grotesk", url: "/__bounded/fonts/schibsted.woff2", weight: "400 900" },
4318
+ ];
4237
4319
  function ensureFonts() {
4238
- if (!hasDOM$5())
4320
+ if (!hasDOM$5() || typeof FontFace === "undefined" || !document.fonts)
4239
4321
  return;
4240
- if (document.querySelector('link[data-bounded-fonts="1"]'))
4322
+ if (!document.querySelector('script[src^="/__bounded/"]'))
4241
4323
  return;
4242
- const pre1 = document.createElement("link");
4243
- pre1.rel = "preconnect";
4244
- pre1.href = "https://fonts.googleapis.com";
4245
- const pre2 = document.createElement("link");
4246
- pre2.rel = "preconnect";
4247
- pre2.href = "https://fonts.gstatic.com";
4248
- pre2.crossOrigin = "anonymous";
4249
- const link = document.createElement("link");
4250
- link.rel = "stylesheet";
4251
- link.href = FONTS_HREF;
4252
- link.setAttribute("data-bounded-fonts", "1");
4253
- document.head.append(pre1, pre2, link);
4324
+ const registered = new Set();
4325
+ document.fonts.forEach((face) => registered.add(face.family.replace(/^["']|["']$/g, "")));
4326
+ for (const { family, url, weight } of WIDGET_FONT_FACES) {
4327
+ if (registered.has(family))
4328
+ continue;
4329
+ // Against the page's origin, not its base URL: a <base href> elsewhere must not move the fetch off this origin.
4330
+ const face = new FontFace(family, `url("${location.origin}${url}") format("woff2")`, { weight, display: "swap" });
4331
+ document.fonts.add(face);
4332
+ // A face that cannot load is taken back out; the font stack falls back to the next face.
4333
+ face.load().catch(() => { document.fonts.delete(face); });
4334
+ }
4254
4335
  }
4255
4336
 
4256
4337
  // turnkey-wallet-widget.ts - runs one Turnkey signer exchange inside the shared
@@ -6270,7 +6351,7 @@ async function getAuthProvider(config, fromStorage = false) {
6270
6351
  // manager as legacy email sessions. Human login should be started
6271
6352
  // with loginWithRedirect(); this provider is kept as the restore and
6272
6353
  // logout surface for those sessions.
6273
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-53sA5_i2.js'); })).EmailAuthProvider();
6354
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-DeORQbTK.js'); })).EmailAuthProvider();
6274
6355
  break;
6275
6356
  case "phantom": {
6276
6357
  // First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
@@ -6312,7 +6393,7 @@ async function getAuthProvider(config, fromStorage = false) {
6312
6393
  // (bounded-login-widget.ts), so registration never counts as
6313
6394
  // evidence the user has a wallet.
6314
6395
  await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
6315
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-BEYwsn6M.js'); });
6396
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-B9jvzDM5.js'); });
6316
6397
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
6317
6398
  break;
6318
6399
  }
@@ -6325,7 +6406,7 @@ async function getAuthProvider(config, fromStorage = false) {
6325
6406
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
6326
6407
  // to point at a specific wallet or set chainId/domain.
6327
6408
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
6328
- const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-IlkuKffc.js'); });
6409
+ const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-D06arDQD.js'); });
6329
6410
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
6330
6411
  break;
6331
6412
  }
@@ -6350,7 +6431,7 @@ async function getAuthProvider(config, fromStorage = false) {
6350
6431
  break;
6351
6432
  case "guest":
6352
6433
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
6353
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-00KkSNpE.js'); })).GuestAuthProvider();
6434
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-5bX-xt6K.js'); })).GuestAuthProvider();
6354
6435
  break;
6355
6436
  }
6356
6437
  if (!currentAuthProvider) {
@@ -6412,7 +6493,7 @@ async function signInAnonymously() {
6412
6493
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
6413
6494
  // the main bundle for data-only apps. Specifier is a static string literal
6414
6495
  // so bundle-splitting can see it.
6415
- const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-00KkSNpE.js'); })).GuestAuthProvider();
6496
+ const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-5bX-xt6K.js'); })).GuestAuthProvider();
6416
6497
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
6417
6498
  // adoptSessionProvider awaits only getConfig() (already resolved here) and then
6418
6499
  // runs every write - provider/method/config/marker/user - in one synchronous
@@ -6583,7 +6664,7 @@ function ensureSharedWalletLoginReady() {
6583
6664
  if (!sharedWalletReady) {
6584
6665
  sharedWalletReady = (async () => {
6585
6666
  await core.getConfig();
6586
- await Promise.resolve().then(function () { return require('./injected-wallet-provider-BEYwsn6M.js'); });
6667
+ await Promise.resolve().then(function () { return require('./injected-wallet-provider-B9jvzDM5.js'); });
6587
6668
  })().catch((error) => {
6588
6669
  sharedWalletReady = null;
6589
6670
  throw error;
@@ -6686,7 +6767,7 @@ async function loginWithWallet(options) {
6686
6767
  await ensureSharedWalletLoginReady();
6687
6768
  else
6688
6769
  await ensureWalletLoginReady();
6689
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-BEYwsn6M.js'); });
6770
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-B9jvzDM5.js'); });
6690
6771
  // Keep the RAW wallet provider: the offchain wrapper forwards only a fixed
6691
6772
  // set of methods, so cleanup below must speak to the provider that actually
6692
6773
  // holds the config rather than to whatever is wrapping it.
@@ -7053,6 +7134,13 @@ function registerStorageBridge() {
7053
7134
  void syncStoredSession().catch((error) => console.warn('Bounded session synchronization failed:', error));
7054
7135
  });
7055
7136
  }
7137
+ function sameSessionIdentity(next, previous) {
7138
+ if (!next || !previous)
7139
+ return false;
7140
+ const a = core.deriveUserIdentityFromIdToken(next);
7141
+ const b = core.deriveUserIdentityFromIdToken(previous);
7142
+ return !!a.id && a.id === b.id && a.address === b.address && a.email === b.email && a.evmAddress === b.evmAddress;
7143
+ }
7056
7144
  async function syncStoredSession() {
7057
7145
  const config = initializedConfig;
7058
7146
  if (!config)
@@ -7061,6 +7149,18 @@ async function syncStoredSession() {
7061
7149
  const issuer = core.WebSessionManager.getIssuer();
7062
7150
  if (token === observedIdToken && issuer === observedIssuer)
7063
7151
  return;
7152
+ // Another tab refreshed this same session (tokens rotate every few minutes):
7153
+ // who is signed in did not change, so keep the user and its live reads, whose
7154
+ // sockets take the new token on their own, instead of hiding and restoring it.
7155
+ if (currentUser && issuer === observedIssuer && sameSessionIdentity(token, observedIdToken)) {
7156
+ const record = await core.WebSessionManager.readSessionWithGeneration();
7157
+ if (config !== initializedConfig || core.WebSessionManager.getIdToken() !== token)
7158
+ return;
7159
+ if ((record === null || record === void 0 ? void 0 : record.idToken) === token && (!record.appId || record.appId === config.appId)) {
7160
+ observedIdToken = token;
7161
+ return;
7162
+ }
7163
+ }
7064
7164
  observedIdToken = token;
7065
7165
  observedIssuer = issuer;
7066
7166
  const generation = ++initializationGeneration;
@@ -7557,7 +7657,9 @@ function standardWalletToInjectedProvider(wallet, configuredNetwork) {
7557
7657
  // mobile adapter fills DEFAULT_FEATURES in exactly that case. So an empty list
7558
7658
  // means none, and reading it as "everything" picks an account that cannot sign
7559
7659
  // and fails at the wallet instead of here.
7560
- const accountSupports = (account, feature) => account.features.includes(feature);
7660
+ // The mobile adapter hands a wallet app's own feature names through
7661
+ // untranslated, so each name is read as its Wallet Standard equivalent.
7662
+ const accountSupports = (account, feature) => account.features.some((f) => walletStandardFeatureName(f) === feature);
7561
7663
  const pickSolanaAccount = (accounts) => {
7562
7664
  // With a network configured, an account that does not support it is not a
7563
7665
  // usable account: `WalletAccount.chains` is what that account can sign for,
@@ -7632,13 +7734,23 @@ function standardWalletToInjectedProvider(wallet, configuredNetwork) {
7632
7734
  } }, (hasFeature(wallet, SolanaSignIn)
7633
7735
  ? {
7634
7736
  async signIn(input) {
7635
- const acc = requireAccount();
7636
- if (!accountSupports(acc, SolanaSignIn))
7637
- throw new Error("Wallet account does not support solana:signIn");
7737
+ var _a;
7638
7738
  const sign = feature(wallet, SolanaSignIn);
7639
7739
  if (!sign)
7640
7740
  throw new Error("Wallet does not support solana:signIn");
7741
+ const open = currentAccount();
7742
+ if (open && !accountSupports(open, SolanaSignIn))
7743
+ throw new Error("Wallet account does not support solana:signIn");
7744
+ if (solanaMobileWalletAnswersSignInFromCache(wallet.name)) {
7745
+ // Drop the cached authorization so this sign-in is performed by the
7746
+ // wallet app, with its payload, rather than answered from a record
7747
+ // that holds no sign-in result or an earlier sign-in's spent nonce.
7748
+ // Local to the page: the adapter's disconnect opens nothing.
7749
+ await ((_a = feature(wallet, StandardDisconnect)) === null || _a === void 0 ? void 0 : _a.disconnect());
7750
+ }
7641
7751
  const [out] = await sign.signIn(input);
7752
+ if (!out)
7753
+ throw new Error("Wallet returned no sign-in result");
7642
7754
  return { account: { address: out.account.address }, signedMessage: out.signedMessage, signature: out.signature };
7643
7755
  },
7644
7756
  }
@@ -8088,8 +8200,8 @@ const CARD_CSS = `
8088
8200
  --line:rgba(232,230,222,.08);--line-strong:rgba(232,230,222,.16);
8089
8201
  --text:#e8e6de;--dim:#9b9991;--faint:#85837c;
8090
8202
  --green:#58c98f;--green-bg:rgba(88,201,143,.09);--green-line:rgba(88,201,143,.28);--red:#ec8488;
8091
- --disp:"Newsreader","Iowan Old Style",Georgia,serif;
8092
- --body:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif;
8203
+ --disp:"Bounded Newsreader","Newsreader","Iowan Old Style",Georgia,serif;
8204
+ --body:"Bounded Schibsted Grotesk","Schibsted Grotesk","Helvetica Neue",Arial,sans-serif;
8093
8205
  position:relative;padding:34px 32px 20px;color:var(--text);font-family:var(--body);
8094
8206
  -webkit-font-smoothing:antialiased}
8095
8207
  .x{position:absolute;top:20px;right:24px;border:0;background:transparent;color:var(--faint);
@@ -8210,6 +8322,17 @@ function discoverWallets(configuredNetwork) {
8210
8322
  // beats a click that does nothing, which is what "one at a time" looks like from
8211
8323
  // the outside.
8212
8324
  const BUSY_MSG = "A sign-in is already in progress - finish or cancel it first.";
8325
+ // Shown instead of an empty card when no lane can be offered (wallet-only app,
8326
+ // no wallet in this browser). A phone browser never sees installed wallet apps
8327
+ // as injected providers, so the honest advice there is a different browser,
8328
+ // not "install a wallet".
8329
+ function noLaneCopy(mobile) {
8330
+ return mobile
8331
+ ? "No wallet is available in this browser. Open this page in your wallet app's " +
8332
+ "browser, or on a desktop browser with a wallet extension, then try again."
8333
+ : "No wallet was found in this browser. Install a Solana wallet extension such as " +
8334
+ "Phantom or Solflare, reload the page, then try again.";
8335
+ }
8213
8336
  const METHOD_LABEL = {
8214
8337
  google: "Continue with Google",
8215
8338
  apple: "Continue with Apple",
@@ -8551,12 +8674,9 @@ async function prepareAndOpen(opts, pending) {
8551
8674
  cancelPendingOpens();
8552
8675
  };
8553
8676
  m.onBackdrop = cancel;
8554
- const style = el("style");
8555
- style.textContent = CARD_CSS;
8556
8677
  let emailDraft = "";
8557
8678
  const renderChoose = (initialError) => {
8558
8679
  const card = el("div", "c");
8559
- card.appendChild(style.cloneNode(true));
8560
8680
  const x = el("button", "x", "Close");
8561
8681
  x.type = "button";
8562
8682
  x.setAttribute("aria-label", "Close");
@@ -8773,6 +8893,24 @@ async function prepareAndOpen(opts, pending) {
8773
8893
  });
8774
8894
  };
8775
8895
  }
8896
+ // A card with no lane at all. It happens when an app asks for wallet-only
8897
+ // (`methods: []`, `wallet: true`) and this browser has no wallet to offer:
8898
+ // no injected wallet, and no Solana Mobile wallet (off-Android, or the app
8899
+ // never installed the adapter). A blank card between the title and the
8900
+ // footer was the shipped behaviour, and it reads as "the site is broken"
8901
+ // to the person and tells the developer nothing. Say what is missing on
8902
+ // screen, and name the usual cause once in the console, where the
8903
+ // developer's own preview run will show it.
8904
+ if (!hasEmail && social.length === 0 && !walletEnabled) {
8905
+ card.appendChild(el("p", "sub", noLaneCopy(detectMobile())));
8906
+ console.warn("[Bounded] openBoundedWidget has no sign-in lane to show: the app asked for " +
8907
+ "no email or social method and no wallet is available in this browser. " +
8908
+ (offerMobile
8909
+ ? "On Android the Solana Mobile wallet is offered only when the app installs " +
8910
+ "'@solana-mobile/wallet-standard-mobile' (see the warning above if registration failed); "
8911
+ : "") +
8912
+ "Add a method, or make sure wallet login is enabled and the mobile adapter is installed.");
8913
+ }
8776
8914
  card.appendChild(status);
8777
8915
  const foot = el("div", "foot");
8778
8916
  const poweredBy = el("a", undefined, "Powered by Bounded");
@@ -8783,11 +8921,13 @@ async function prepareAndOpen(opts, pending) {
8783
8921
  card.appendChild(foot);
8784
8922
  return card;
8785
8923
  };
8786
- // Solana Mobile leaves the page to sign: connecting spends the tap that
8787
- // started login, so requesting the signature straight afterwards runs with
8788
- // no transient activation left, Chrome blocks the `solana-wallet:` intent
8789
- // navigation, and the protocol reports the wallet as missing. Collect a
8790
- // second tap on its OWN view - never by adding a listener to a button that
8924
+ // Solana Mobile leaves the page to sign in: the nonce fetch that precedes
8925
+ // the sign-in outlives the tap that started login, so requesting the
8926
+ // sign-in straight afterwards runs with no transient activation left,
8927
+ // Chrome blocks the `solana-wallet:` intent navigation, and the protocol
8928
+ // reports the wallet as missing. Collect a fresh tap on its OWN view (one
8929
+ // wallet trip: sign-in authorizes and signs in the same step, there is no
8930
+ // connect before it) - never by adding a listener to a button that
8791
8931
  // already starts a login, which would run both handlers on that tap and
8792
8932
  // kick off a duplicate login beside the one waiting for the gesture.
8793
8933
  // Injected wallets sign in-page and get no extra step.
@@ -8819,7 +8959,6 @@ async function prepareAndOpen(opts, pending) {
8819
8959
  const renderSignInGesture = (choice, done) => {
8820
8960
  var _a;
8821
8961
  const card = el("div", "c");
8822
- card.appendChild(style.cloneNode(true));
8823
8962
  const x = el("button", "x", "Close");
8824
8963
  x.type = "button";
8825
8964
  x.setAttribute("aria-label", "Close");
@@ -8840,7 +8979,6 @@ async function prepareAndOpen(opts, pending) {
8840
8979
  };
8841
8980
  const renderWallets = (initialError) => {
8842
8981
  const card = el("div", "c");
8843
- card.appendChild(style.cloneNode(true));
8844
8982
  const back = el("button", "back", "\u2190 Back");
8845
8983
  back.type = "button";
8846
8984
  // Leaving the wallet list supersedes any attempt still preparing, so it
@@ -8920,7 +9058,6 @@ async function prepareAndOpen(opts, pending) {
8920
9058
  // experience still feels native; the code input, sealing, and verify all live behind the frame.
8921
9059
  const renderOtpFrame = (handle) => {
8922
9060
  const card = el("div", "c");
8923
- card.appendChild(style.cloneNode(true));
8924
9061
  const back = el("button", "back", "\u2190 Back");
8925
9062
  back.type = "button";
8926
9063
  card.appendChild(back);
@@ -8967,7 +9104,7 @@ async function prepareAndOpen(opts, pending) {
8967
9104
  // land after a newer open has taken over the DOM.
8968
9105
  if (!owner.owns())
8969
9106
  return;
8970
- mountContent(m, node);
9107
+ mountContent(m, node, CARD_CSS);
8971
9108
  requestAnimationFrame(() => {
8972
9109
  if (owner.owns())
8973
9110
  fitPanelToContent(m);
@@ -11359,4 +11496,4 @@ exports.useAuth = useAuth;
11359
11496
  exports.useBuildRun = useBuildRun;
11360
11497
  exports.useQuery = useQuery;
11361
11498
  exports.waitForAuthReady = waitForAuthReady;
11362
- //# sourceMappingURL=index-DJeEMevo.js.map
11499
+ //# sourceMappingURL=index-BZ0X1Z9X.js.map