@bounded-sh/client 0.0.98 → 0.0.99

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 (67) hide show
  1. package/dist/auth/bounded-widget-modal.d.ts +7 -2
  2. package/dist/auth/oidc-auth.d.ts +1 -1
  3. package/dist/auth/session-provider.d.ts +1 -0
  4. package/dist/auth/turnkey-auth.d.ts +7 -1
  5. package/dist/auth/turnkey-signer-bridge.d.ts +8 -5
  6. package/dist/auth/turnkey-signer-frame.d.ts +4 -1
  7. package/dist/auth/turnkey-wallet-context.d.ts +24 -0
  8. package/dist/auth/turnkey-wallet-widget.d.ts +2 -0
  9. package/dist/builds/client.d.ts +5 -2
  10. package/dist/builds/types.d.ts +108 -1
  11. package/dist/{email-auth-provider-DxB_pJKO.js → email-auth-provider-62OK5X30.js} +2 -2
  12. package/dist/{email-auth-provider-DxB_pJKO.js.map → email-auth-provider-62OK5X30.js.map} +1 -1
  13. package/dist/{email-auth-provider-DF7Urh8i.mjs → email-auth-provider-BKYVi3dl.mjs} +2 -2
  14. package/dist/{email-auth-provider-CiPAD3vz.mjs.map → email-auth-provider-BKYVi3dl.mjs.map} +1 -1
  15. package/dist/{email-auth-provider-CiPAD3vz.mjs → email-auth-provider-CIRbOSM3.mjs} +2 -2
  16. package/dist/{email-auth-provider-DF7Urh8i.mjs.map → email-auth-provider-CIRbOSM3.mjs.map} +1 -1
  17. package/dist/{email-auth-provider-BhYzRN0r.js → email-auth-provider-Cpj8hIFu.js} +2 -2
  18. package/dist/{email-auth-provider-BhYzRN0r.js.map → email-auth-provider-Cpj8hIFu.js.map} +1 -1
  19. package/dist/{guest-auth-provider-BiM35KHb.js → guest-auth-provider-0PjGFGDm.js} +2 -2
  20. package/dist/{guest-auth-provider-BiM35KHb.js.map → guest-auth-provider-0PjGFGDm.js.map} +1 -1
  21. package/dist/{guest-auth-provider-B9C3Iub_.js → guest-auth-provider-B6dJixRm.js} +2 -2
  22. package/dist/{guest-auth-provider-B9C3Iub_.js.map → guest-auth-provider-B6dJixRm.js.map} +1 -1
  23. package/dist/{guest-auth-provider-Dtsq4QaY.mjs → guest-auth-provider-C23vWrOT.mjs} +2 -2
  24. package/dist/{guest-auth-provider-DVw-tITC.mjs.map → guest-auth-provider-C23vWrOT.mjs.map} +1 -1
  25. package/dist/{guest-auth-provider-DVw-tITC.mjs → guest-auth-provider-E7Fg7-E2.mjs} +2 -2
  26. package/dist/{guest-auth-provider-Dtsq4QaY.mjs.map → guest-auth-provider-E7Fg7-E2.mjs.map} +1 -1
  27. package/dist/{index-CYdYsf8s.js → index-BrU_tGd-.js} +1148 -597
  28. package/dist/index-BrU_tGd-.js.map +1 -0
  29. package/dist/{index-Cflemdaf.mjs → index-l5E4pjpt.mjs} +1145 -598
  30. package/dist/index-l5E4pjpt.mjs.map +1 -0
  31. package/dist/index.d.ts +6 -2
  32. package/dist/index.js +5 -1
  33. package/dist/index.js.map +1 -1
  34. package/dist/index.mjs +1 -1
  35. package/dist/{index.native-BZOWS_sR.js → index.native-CllNqdoy.js} +442 -81
  36. package/dist/index.native-CllNqdoy.js.map +1 -0
  37. package/dist/{index.native-gVbAsohv.mjs → index.native-r1I-9E8e.mjs} +442 -81
  38. package/dist/index.native-r1I-9E8e.mjs.map +1 -0
  39. package/dist/index.native.js +1 -1
  40. package/dist/index.native.mjs +1 -1
  41. package/dist/{injected-evm-wallet-provider-C3MwUTRd.js → injected-evm-wallet-provider-BXM8TfS3.js} +2 -2
  42. package/dist/{injected-evm-wallet-provider-C3MwUTRd.js.map → injected-evm-wallet-provider-BXM8TfS3.js.map} +1 -1
  43. package/dist/{injected-evm-wallet-provider-CMqUqqxN.mjs → injected-evm-wallet-provider-BmDPRV5L.mjs} +2 -2
  44. package/dist/{injected-evm-wallet-provider-CMqUqqxN.mjs.map → injected-evm-wallet-provider-BmDPRV5L.mjs.map} +1 -1
  45. package/dist/{injected-evm-wallet-provider-Bv8VLuB1.js → injected-evm-wallet-provider-CO_aC4TY.js} +2 -2
  46. package/dist/{injected-evm-wallet-provider-Bv8VLuB1.js.map → injected-evm-wallet-provider-CO_aC4TY.js.map} +1 -1
  47. package/dist/{injected-evm-wallet-provider-K0j697jt.mjs → injected-evm-wallet-provider-CkhN5u3d.mjs} +2 -2
  48. package/dist/{injected-evm-wallet-provider-K0j697jt.mjs.map → injected-evm-wallet-provider-CkhN5u3d.mjs.map} +1 -1
  49. package/dist/{injected-wallet-provider-BDt4CN0b.js → injected-wallet-provider-B9eU7t8o.js} +2 -2
  50. package/dist/{injected-wallet-provider-BDt4CN0b.js.map → injected-wallet-provider-B9eU7t8o.js.map} +1 -1
  51. package/dist/{injected-wallet-provider-CG3oKdp_.js → injected-wallet-provider-Bi_mB_q3.js} +2 -2
  52. package/dist/{injected-wallet-provider-CG3oKdp_.js.map → injected-wallet-provider-Bi_mB_q3.js.map} +1 -1
  53. package/dist/{injected-wallet-provider-DH8yLpyf.mjs → injected-wallet-provider-BlSdRbgq.mjs} +2 -2
  54. package/dist/{injected-wallet-provider-DH8yLpyf.mjs.map → injected-wallet-provider-BlSdRbgq.mjs.map} +1 -1
  55. package/dist/{injected-wallet-provider-Dlo96aV9.mjs → injected-wallet-provider-yl51pLBa.mjs} +2 -2
  56. package/dist/{injected-wallet-provider-Dlo96aV9.mjs.map → injected-wallet-provider-yl51pLBa.mjs.map} +1 -1
  57. package/dist/services/broadcast.d.ts +28 -0
  58. package/dist/services/session.d.ts +28 -0
  59. package/package.json +7 -3
  60. package/dist/bounded-widget-modal-C0Z7gmxH.mjs +0 -31
  61. package/dist/bounded-widget-modal-C0Z7gmxH.mjs.map +0 -1
  62. package/dist/bounded-widget-modal-s0PL86Y-.js +0 -33
  63. package/dist/bounded-widget-modal-s0PL86Y-.js.map +0 -1
  64. package/dist/index-CYdYsf8s.js.map +0 -1
  65. package/dist/index-Cflemdaf.mjs.map +0 -1
  66. package/dist/index.native-BZOWS_sR.js.map +0 -1
  67. package/dist/index.native-gVbAsohv.mjs.map +0 -1
@@ -22,6 +22,42 @@ function _interopNamespaceDefault(e) {
22
22
 
23
23
  var React__namespace = /*#__PURE__*/_interopNamespaceDefault(React);
24
24
 
25
+ function walletContextFromClaims(claims, baseUrl, expectedAppId, tenantOrigin) {
26
+ const base = baseUrl.replace(/\/$/, '');
27
+ const scope = claims['custom:walletScope'];
28
+ const protocol = claims['custom:walletProtocol'];
29
+ if (scope !== undefined && scope !== 'legacy' && scope !== 'app')
30
+ throw new Error('wallet_scope_invalid');
31
+ if ((scope === 'app' && protocol !== 2) || (scope === 'legacy' && protocol !== 1)
32
+ || (scope === undefined && protocol !== undefined))
33
+ throw new Error('wallet_protocol_invalid');
34
+ const appId = claims['custom:appId'];
35
+ const userId = claims['custom:userId'];
36
+ const address = claims['custom:walletAddress'];
37
+ if (scope === 'app' && (appId !== expectedAppId || typeof appId !== 'string'
38
+ || !/^[a-f0-9]{24}$/.test(appId) || typeof userId !== 'string' || !userId
39
+ || claims.iss !== base))
40
+ throw new Error('wallet_session_context_invalid');
41
+ const resolvedScope = scope === 'app' ? 'app' : 'legacy';
42
+ const route = resolvedScope === 'app' ? `/wallet/turnkey/app/${appId}` : '/wallet/turnkey';
43
+ return {
44
+ scope: resolvedScope, appId: String(appId || expectedAppId), userId: String(userId || ''),
45
+ address: typeof address === 'string' ? address : '', base, origin: new URL(base).origin,
46
+ url: `${base}${route}/signer${resolvedScope === 'app' ? `?origin=${encodeURIComponent(tenantOrigin)}` : ''}`,
47
+ key: JSON.stringify([base, appId || expectedAppId, userId || '', address || '', resolvedScope]),
48
+ };
49
+ }
50
+ let loginGeneration = 0;
51
+ function invalidatePendingWalletLogin() { ++loginGeneration; }
52
+ function walletOperationGuard() {
53
+ const generation = loginGeneration;
54
+ return () => {
55
+ if (generation !== loginGeneration)
56
+ throw new Error('wallet_session_changed');
57
+ };
58
+ }
59
+ function setWalletSessionState(key, state) { }
60
+
25
61
  /**
26
62
  * Platform abstraction layer for React Native compatibility.
27
63
  *
@@ -965,7 +1001,10 @@ function registerAuthRegistrySync(fn) {
965
1001
  * Returns the normalized user published by this commit, or null when omitted.
966
1002
  */
967
1003
  async function adoptSessionProvider(opts) {
1004
+ var _a;
968
1005
  const coreConfig = await core.getConfig();
1006
+ (_a = opts.beforeCommit) === null || _a === void 0 ? void 0 : _a.call(opts);
1007
+ invalidatePendingWalletLogin();
969
1008
  coreConfig.authProvider = opts.provider;
970
1009
  coreConfig.authMethod = opts.method;
971
1010
  setAuthProviderInstance(opts.provider);
@@ -4188,6 +4227,336 @@ async function ensureSolanaMobileWalletRegistered(config, network) {
4188
4227
 
4189
4228
  var bufferExports = requireBuffer();
4190
4229
 
4230
+ // bounded-widget-modal.ts - the shared in-app WIDGET shell for Bounded's login
4231
+ // and signing surfaces. One Shadow-DOM overlay + panel that matches the Bounded
4232
+ // widget (Seal) look and MOTION: a backdrop fade, a panel rise on desktop, a
4233
+ // bottom-sheet on mobile, a height tween as content swaps, and a clean close.
4234
+ //
4235
+ // It hosts EITHER:
4236
+ // - NATIVE content (the login card the SDK renders itself), or
4237
+ // - an IFRAME (the Bounded-owned signer page, which must run on the single
4238
+ // signer origin because the wallet's 24h signing session lives in that
4239
+ // origin's storage, out of every app's reach).
4240
+ //
4241
+ // The modal is a warm singleton: it stays mounted (hidden) between uses so a
4242
+ // second open - or a signature right after login - is instant and, when it is
4243
+ // already open, a clean in-place view swap rather than a reopen.
4244
+ function hasDOM$4() {
4245
+ return typeof window !== "undefined" && typeof document !== "undefined";
4246
+ }
4247
+ const OVERLAY_CSS = `
4248
+ :host{all:initial}
4249
+ *{box-sizing:border-box}
4250
+ .bw-ov{position:fixed;inset:0;z-index:2147483647;display:none;
4251
+ font-family:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
4252
+ .bw-ov.mounted{display:block}
4253
+ .bw-back{position:fixed;inset:0;background:rgba(4,4,5,.62);opacity:0;
4254
+ transition:opacity .24s ease;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}
4255
+ .bw-wrap{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:20px}
4256
+ .bw-panel{position:relative;width:420px;max-width:calc(100vw - 28px);height:280px;
4257
+ border-radius:14px;overflow:hidden;background:#0e0f11;border:1px solid rgba(232,230,222,.08);
4258
+ box-shadow:0 1px 0 rgba(232,230,222,.03) inset,0 40px 90px -45px rgba(0,0,0,.85);
4259
+ opacity:0;transform:translateY(14px) scale(.98);will-change:transform,opacity,height;
4260
+ transition:opacity .28s cubic-bezier(.2,.8,.2,1),transform .28s cubic-bezier(.2,.8,.2,1),
4261
+ height .3s cubic-bezier(.2,.8,.2,1)}
4262
+ .bw-ov.show .bw-back{opacity:1}
4263
+ .bw-ov.show .bw-panel{opacity:1;transform:none}
4264
+ .bw-ov.closing .bw-back{opacity:0}
4265
+ .bw-ov.closing .bw-panel{opacity:0;transform:translateY(12px) scale(.99)}
4266
+ .bw-content{position:absolute;inset:0;overflow:hidden}
4267
+ .bw-frame{display:block;width:100%;height:100%;border:0;background:transparent;color-scheme:dark}
4268
+ /* editorial corner crop-marks, matching the reference frame */
4269
+ .bw-mk{position:absolute;width:13px;height:13px;pointer-events:none;z-index:2;
4270
+ border-color:rgba(232,230,222,.22);border-style:solid;border-width:0}
4271
+ .bw-mk.tl{top:11px;left:11px;border-top-width:1px;border-left-width:1px}
4272
+ .bw-mk.tr{top:11px;right:11px;border-top-width:1px;border-right-width:1px}
4273
+ .bw-mk.bl{bottom:11px;left:11px;border-bottom-width:1px;border-left-width:1px}
4274
+ .bw-mk.br{bottom:11px;right:11px;border-bottom-width:1px;border-right-width:1px}
4275
+ @media(max-width:560px){
4276
+ .bw-wrap{align-items:flex-end;padding:0}
4277
+ .bw-panel{width:100%;max-width:100%;border-radius:18px 18px 0 0;border-bottom:0}
4278
+ .bw-content{overflow-y:auto}
4279
+ .bw-ov.show .bw-panel{transform:none}
4280
+ .bw-ov:not(.show) .bw-panel,.bw-ov.closing .bw-panel{transform:translateY(18%)}
4281
+ }
4282
+ @media(prefers-reduced-motion:reduce){.bw-back,.bw-panel{transition:opacity .12s linear!important}}
4283
+ `;
4284
+ let modal = null;
4285
+ function getModal() {
4286
+ if (modal)
4287
+ return modal;
4288
+ const root = document.createElement("div");
4289
+ root.setAttribute("data-bounded-widget", "");
4290
+ const shadow = root.attachShadow({ mode: "open" });
4291
+ const style = document.createElement("style");
4292
+ style.textContent = OVERLAY_CSS;
4293
+ shadow.appendChild(style);
4294
+ const overlay = document.createElement("div");
4295
+ overlay.className = "bw-ov";
4296
+ const back = document.createElement("div");
4297
+ back.className = "bw-back";
4298
+ const wrap = document.createElement("div");
4299
+ wrap.className = "bw-wrap";
4300
+ const panel = document.createElement("div");
4301
+ panel.className = "bw-panel";
4302
+ const content = document.createElement("div");
4303
+ content.className = "bw-content";
4304
+ panel.appendChild(content);
4305
+ for (const corner of ["tl", "tr", "bl", "br"]) {
4306
+ const mk = document.createElement("span");
4307
+ mk.className = "bw-mk " + corner;
4308
+ panel.appendChild(mk);
4309
+ }
4310
+ wrap.appendChild(panel);
4311
+ overlay.appendChild(back);
4312
+ overlay.appendChild(wrap);
4313
+ shadow.appendChild(overlay);
4314
+ (document.body || document.documentElement).appendChild(root);
4315
+ const m = {
4316
+ root,
4317
+ overlay,
4318
+ panel,
4319
+ content,
4320
+ iframe: null,
4321
+ iframeOrigin: "",
4322
+ iframeReady: false,
4323
+ iframeContext: "",
4324
+ onBackdrop: null,
4325
+ onIframeMessage: null,
4326
+ chain: Promise.resolve(),
4327
+ fitObserver: null,
4328
+ generation: 0,
4329
+ onSupersede: null,
4330
+ };
4331
+ // The dismiss handler lives on `.bw-wrap`: it is the full-viewport flex layer
4332
+ // that sits ABOVE `.bw-back`, so it (not the backdrop element) receives clicks
4333
+ // outside the panel. Fire only when the click lands on the wrap itself, never
4334
+ // when it bubbles up from the panel or its content.
4335
+ wrap.addEventListener("click", (event) => {
4336
+ if (event.target === wrap && m.onBackdrop)
4337
+ m.onBackdrop();
4338
+ });
4339
+ window.addEventListener("message", (event) => {
4340
+ if (!m.iframe)
4341
+ return;
4342
+ if (event.origin !== m.iframeOrigin)
4343
+ return;
4344
+ if (event.source !== m.iframe.contentWindow)
4345
+ return;
4346
+ const d = event.data;
4347
+ if (!d || typeof d !== "object")
4348
+ return;
4349
+ // Readiness belongs to the document, even while login owns the modal and no
4350
+ // signing exchange is listening. The retained frame will not load again.
4351
+ if (d.type === "bounded:turnkey:ready")
4352
+ m.iframeReady = true;
4353
+ if (m.onIframeMessage)
4354
+ m.onIframeMessage(d);
4355
+ });
4356
+ modal = m;
4357
+ return m;
4358
+ }
4359
+ function showModal(m) {
4360
+ m.overlay.classList.remove("closing");
4361
+ m.overlay.classList.add("mounted");
4362
+ // Force a reflow so the .show transition runs from the hidden state every open.
4363
+ void m.panel.offsetWidth;
4364
+ m.overlay.classList.add("show");
4365
+ }
4366
+ function hideModal(m, after) {
4367
+ m.overlay.classList.remove("show");
4368
+ m.overlay.classList.add("closing");
4369
+ // Capture the generation at hide time. If a newer open re-shows the modal within
4370
+ // the close tween, this stale timer must NOT strip `.mounted`/`.closing` off the
4371
+ // freshly reopened widget - only tear down when we are still the same open.
4372
+ const gen = m.generation;
4373
+ window.setTimeout(() => {
4374
+ if (m.generation === gen)
4375
+ m.overlay.classList.remove("mounted", "closing");
4376
+ }, 260);
4377
+ }
4378
+ function setPanelHeight(m, px) {
4379
+ const h = Math.max(140, Math.min(680, Math.round(px)));
4380
+ m.panel.style.height = h + "px";
4381
+ }
4382
+ /** Get (or lazily create) the warm iframe for signer/hosted-page mode. */
4383
+ function ensureIframe(m, url, origin, context = "") {
4384
+ var _a;
4385
+ if (m.iframe && m.iframeOrigin === origin && m.iframe.src === url && m.iframeContext === context)
4386
+ return m.iframe;
4387
+ detachIframe(m);
4388
+ (_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
4389
+ m.fitObserver = null;
4390
+ m.content.style.display = "none";
4391
+ const iframe = document.createElement("iframe");
4392
+ iframe.className = "bw-frame";
4393
+ iframe.setAttribute("title", "Bounded");
4394
+ iframe.setAttribute("allow", "clipboard-write");
4395
+ iframe.src = url;
4396
+ m.iframeOrigin = origin;
4397
+ m.iframeContext = context;
4398
+ m.iframeReady = false;
4399
+ m.panel.appendChild(iframe);
4400
+ m.iframe = iframe;
4401
+ return iframe;
4402
+ }
4403
+ /**
4404
+ * Destroy the warm signer iframe if one is currently mounted for `origin`, and report
4405
+ * whether there was one. Logout needs this: a signing session can live in that document's
4406
+ * MEMORY (the signer keeps an in-memory mirror when the browser blocks its storage), and a
4407
+ * throwaway iframe opened by the sweep can only ever clear the shared store, never another
4408
+ * document's memory. Removing the iframe discards its browsing context, which takes the
4409
+ * mirror with it.
4410
+ *
4411
+ * Deliberately does NOT call getModal(): that would BUILD a modal just to tear it down, on
4412
+ * every logout of every app, most of which never mounted one.
4413
+ */
4414
+ function detachWarmSignerIframe(origin) {
4415
+ if (!modal || !modal.iframe || (origin))
4416
+ return false;
4417
+ detachIframe(modal);
4418
+ return true;
4419
+ }
4420
+ function detachIframe(m) {
4421
+ if (m.iframe) {
4422
+ try {
4423
+ m.iframe.remove();
4424
+ }
4425
+ catch (_a) {
4426
+ /* ignore */
4427
+ }
4428
+ }
4429
+ m.iframe = null;
4430
+ m.iframeReady = false;
4431
+ m.iframeOrigin = "";
4432
+ m.iframeContext = "";
4433
+ m.onIframeMessage = null;
4434
+ }
4435
+
4436
+ // turnkey-wallet-widget.ts - runs one Turnkey signer exchange inside the shared
4437
+ // in-app WIDGET (bounded-widget-modal.ts). The Bounded-owned signer page is loaded
4438
+ // in a warm iframe behind the modal so signing feels native: an in-app approve
4439
+ // card (with inline code entry when the 24h signing session must first be
4440
+ // established), never a jarring popup.
4441
+ //
4442
+ // WHY AN IFRAME: the wallet's signing session lives in the single Bounded signer
4443
+ // origin's storage, out of every app's reach, so signing MUST run on that origin;
4444
+ // the iframe brings it in-app.
4445
+ //
4446
+ // Restricted embedders: if the frame never loads, the SAME request reruns through
4447
+ // `popupFallback` (a real window). Callers pass a popup implementation and never
4448
+ // see the difference.
4449
+ /**
4450
+ * Run one signer exchange through the in-app widget. Resolves when `onResult`
4451
+ * accepts a result; falls back to `popupFallback` if the frame cannot be mounted
4452
+ * or never loads.
4453
+ */
4454
+ async function runWidget(url, signerOrigin, ex, popupFallback) {
4455
+ if (!hasDOM$4())
4456
+ return popupFallback();
4457
+ const m = getModal();
4458
+ const requestId = String(ex.request.requestId);
4459
+ // Serialize: the approve card is modal, and one warm frame handles one request at a time.
4460
+ const run = m.chain.then(() => new Promise((resolve, reject) => {
4461
+ var _a;
4462
+ let settled = false;
4463
+ let usedFallback = false;
4464
+ (_a = ex.assertCurrent) === null || _a === void 0 ? void 0 : _a.call(ex);
4465
+ const iframe = ensureIframe(m, url, signerOrigin, ex.context);
4466
+ const send = () => {
4467
+ var _a;
4468
+ if (!iframe.contentWindow)
4469
+ return;
4470
+ try {
4471
+ (_a = ex.assertCurrent) === null || _a === void 0 ? void 0 : _a.call(ex);
4472
+ iframe.contentWindow.postMessage(ex.request, signerOrigin);
4473
+ }
4474
+ catch (_b) {
4475
+ finish(() => reject(new Error('wallet_session_changed')));
4476
+ }
4477
+ };
4478
+ let loadTimer = 0;
4479
+ let ceilingTimer = 0;
4480
+ const finish = (fn) => {
4481
+ if (settled)
4482
+ return;
4483
+ settled = true;
4484
+ m.onIframeMessage = null;
4485
+ m.onBackdrop = null;
4486
+ window.clearTimeout(loadTimer);
4487
+ window.clearTimeout(ceilingTimer);
4488
+ fn();
4489
+ };
4490
+ // The frame is WARM across exchanges, so an abandoned approval must be told to drop its
4491
+ // in-flight ceremony. Otherwise the signer keeps `approving` set for a card nobody is
4492
+ // looking at and refuses every later create/sign as `signer_request_superseded` (the
4493
+ // BSA-2026-012 guard) until the page reloads.
4494
+ const cancelInFrame = () => {
4495
+ if (!iframe.contentWindow)
4496
+ return;
4497
+ try {
4498
+ iframe.contentWindow.postMessage({ type: "bounded:turnkey:cancel", requestId }, signerOrigin);
4499
+ }
4500
+ catch (_a) {
4501
+ /* frame gone */
4502
+ }
4503
+ };
4504
+ const goFallback = () => {
4505
+ if (usedFallback || settled)
4506
+ return;
4507
+ usedFallback = true;
4508
+ hideModal(m);
4509
+ finish(() => popupFallback().then(resolve, reject));
4510
+ };
4511
+ m.onBackdrop = () => {
4512
+ cancelInFrame();
4513
+ hideModal(m);
4514
+ finish(() => reject(new Error("cancelled")));
4515
+ };
4516
+ m.onIframeMessage = (d) => {
4517
+ if (d.type === "bounded:turnkey:ready") {
4518
+ send();
4519
+ return;
4520
+ }
4521
+ if (d.type === "bounded:turnkey:resize" && typeof d.height === "number") {
4522
+ setPanelHeight(m, d.height);
4523
+ return;
4524
+ }
4525
+ if (d.type === "bounded:turnkey:result" && d.requestId === requestId) {
4526
+ const outcome = ex.onResult(d);
4527
+ if ("error" in outcome) {
4528
+ hideModal(m);
4529
+ finish(() => reject(new Error(outcome.error)));
4530
+ return;
4531
+ }
4532
+ if (outcome.done) {
4533
+ hideModal(m);
4534
+ finish(() => resolve(outcome.value));
4535
+ }
4536
+ }
4537
+ };
4538
+ showModal(m);
4539
+ if (m.iframeReady)
4540
+ send();
4541
+ // If the frame never loads, degrade to the popup (best-effort - a post-load
4542
+ // popup may be blocked without a gesture, in which case the fallback rejects).
4543
+ loadTimer = window.setTimeout(() => {
4544
+ if (!m.iframeReady && !settled)
4545
+ goFallback();
4546
+ }, 8000);
4547
+ // Absolute ceiling so a stuck flow cannot hang forever.
4548
+ ceilingTimer = window.setTimeout(() => {
4549
+ if (!settled) {
4550
+ cancelInFrame();
4551
+ hideModal(m);
4552
+ finish(() => reject(new Error("Wallet approval timed out.")));
4553
+ }
4554
+ }, 5 * 60 * 1000);
4555
+ }));
4556
+ m.chain = run.then(() => undefined, () => undefined);
4557
+ return run;
4558
+ }
4559
+
4191
4560
  // turnkey-signer-frame.ts - a HIDDEN, offscreen iframe of the Bounded signer page
4192
4561
  // for UI-less signer exchanges: sealing a login code (bounded:turnkey:encryptOtp),
4193
4562
  // establishing the 24h signing session right after login
@@ -4207,10 +4576,10 @@ async function signerPage() {
4207
4576
  return { url: `${base}/wallet/turnkey/signer`, origin: new URL(base).origin };
4208
4577
  }
4209
4578
  /** Mount the hidden signer frame. Callers MUST dispose() when done. */
4210
- async function openHiddenSignerFrame() {
4579
+ async function openHiddenSignerFrame(page) {
4211
4580
  if (!hasDOM$3())
4212
4581
  throw new Error('The Bounded signer frame is only available in the browser.');
4213
- const { url, origin } = await signerPage();
4582
+ const { url, origin } = page !== null && page !== void 0 ? page : await signerPage();
4214
4583
  const frame = document.createElement('iframe');
4215
4584
  frame.setAttribute('aria-hidden', 'true');
4216
4585
  frame.style.cssText =
@@ -4305,7 +4674,7 @@ async function requestTurnkeySigningCapability(opts) {
4305
4674
  res = await opts.fetchImpl(`${base}/wallet/turnkey/capability`, {
4306
4675
  method: 'POST',
4307
4676
  headers: { 'content-type': 'application/json' },
4308
- body: JSON.stringify({ idToken: opts.idToken, messageHex: opts.messageHex }),
4677
+ body: JSON.stringify({ idToken: opts.idToken, messageHex: opts.messageHex, walletProtocol: 2 }),
4309
4678
  });
4310
4679
  }
4311
4680
  catch (_a) {
@@ -4357,7 +4726,7 @@ async function requestTurnkeyActionAuthorization(opts) {
4357
4726
  res = await opts.fetchImpl(`${base}/wallet/turnkey/action-authorization`, {
4358
4727
  method: 'POST',
4359
4728
  headers: { 'content-type': 'application/json' },
4360
- body: JSON.stringify({ idToken: opts.idToken, action: opts.action }),
4729
+ body: JSON.stringify({ idToken: opts.idToken, action: opts.action, walletProtocol: 2 }),
4361
4730
  });
4362
4731
  }
4363
4732
  catch (_a) {
@@ -4397,26 +4766,39 @@ async function requestTurnkeyActionAuthorization(opts) {
4397
4766
  // code is the sole signing authority; within a live session every signature is one
4398
4767
  // approval click). The app retains the transaction itself.
4399
4768
  //
4400
- // ONE ORIGIN, ONE SESSION: the signing session lives in the signer origin's own
4401
- // storage, out of every app's reach, so ALL Bounded apps (any subdomain / custom
4402
- // domain) sign through this one origin - giving every user one wallet that roams
4403
- // across every app.
4769
+ // Signing keys remain on the trusted signer origin. New apps use independently
4770
+ // bound wallets and storage slots; legacy apps retain their shared wallet.
4404
4771
  function hasDOM$2() {
4405
4772
  return typeof window !== 'undefined' && typeof document !== 'undefined';
4406
4773
  }
4407
- // BSA-2026-006: dispatch every signing / get-or-create exchange through a TOP-LEVEL
4408
- // POPUP the caller opened from the user gesture - NEVER a framed widget. An authorized
4409
- // tenant that embedded the signer could overlay its predictable Approve control and steal
4410
- // one concealed click into a fund-moving signature; a top-level window it cannot reach or
4411
- // visually redress closes that. A blocked popup surfaces `popup_blocked` (runPopup) and we
4412
- // never silently fall back to an iframe.
4774
+ // Dispatch one signer exchange through the in-app WIDGET, degrading to the POPUP
4775
+ // when we cannot embed (no DOM, or the frame never mounts/loads). Nothing in the
4776
+ // email-session flow needs a popup by preference: the code entry and the approval
4777
+ // click both run fine inside a cross-origin iframe on every engine (the old
4778
+ // Safari popup preference existed only for in-iframe WebAuthn CREATE).
4413
4779
  async function deliver(url, origin, ex) {
4414
- return runPopup(url, origin, ex);
4415
- }
4416
- async function turnkeySignerPage() {
4780
+ var _a, _b;
4781
+ const popup = () => runPopup(url, origin, ex);
4782
+ (_a = ex.assertCurrent) === null || _a === void 0 ? void 0 : _a.call(ex);
4783
+ const result = await runWidget(url, origin, { request: ex.request, onResult: ex.onResult, context: ex.context, assertCurrent: ex.assertCurrent }, popup);
4784
+ (_b = ex.assertCurrent) === null || _b === void 0 ? void 0 : _b.call(ex);
4785
+ return result;
4786
+ }
4787
+ async function turnkeySignerPage(token) {
4788
+ const assertGeneration = walletOperationGuard();
4789
+ const idToken = token === undefined ? core.getActiveSessionManager().getIdToken() : token;
4417
4790
  const cfg = await core.getConfig();
4418
4791
  const base = (cfg.humanAuthApiUrl || 'https://auth.bounded.sh').replace(/\/$/, '');
4419
- return { url: `${base}/wallet/turnkey/signer`, origin: new URL(base).origin };
4792
+ const context = walletContextFromClaims(idToken ? decodeJwt$1(idToken) : {}, base, cfg.appId, window.location.origin);
4793
+ return Object.assign(Object.assign({}, context), { idToken: idToken || '', assertCurrent() {
4794
+ assertGeneration();
4795
+ const current = core.getActiveSessionManager().getIdToken();
4796
+ if (!current)
4797
+ throw new Error('wallet_session_changed');
4798
+ const next = walletContextFromClaims(decodeJwt$1(current), base, cfg.appId, window.location.origin);
4799
+ if (next.key !== context.key)
4800
+ throw new Error('wallet_session_changed');
4801
+ } });
4420
4802
  }
4421
4803
  function toHex(bytes) {
4422
4804
  let h = '';
@@ -4472,7 +4854,7 @@ async function runPopup(url, signerOrigin, ex) {
4472
4854
  const top = window.screenY + Math.max(0, (window.outerHeight - h) / 2);
4473
4855
  const popup = window.open(url, 'bounded-turnkey-signer', `width=${w},height=${h},left=${left},top=${top}`);
4474
4856
  if (!popup) {
4475
- throw new Error('popup_blocked - the wallet approval window was blocked. Call this from a user gesture (e.g. a click handler) so it can open; we never fall back to an in-page frame.');
4857
+ throw new Error('Popup blocked - call this from a user gesture (e.g. a click handler) so the wallet approval window can open.');
4476
4858
  }
4477
4859
  return await new Promise((resolve, reject) => {
4478
4860
  let settled = false;
@@ -4488,11 +4870,12 @@ async function runPopup(url, signerOrigin, ex) {
4488
4870
  let closeTimer;
4489
4871
  const cleanup = () => { settled = true; window.removeEventListener('message', onMsg); if (closeTimer)
4490
4872
  clearInterval(closeTimer); };
4491
- const sendRequest = () => { if (settled || acked)
4873
+ const sendRequest = () => { var _a; if (settled || acked)
4492
4874
  return; try {
4875
+ (_a = ex.assertCurrent) === null || _a === void 0 ? void 0 : _a.call(ex);
4493
4876
  popup.postMessage(ex.request, signerOrigin);
4494
4877
  }
4495
- catch ( /* popup gone */_a) { /* popup gone */ } };
4878
+ catch ( /* popup gone */_b) { /* popup gone */ } };
4496
4879
  const onMsg = (event) => {
4497
4880
  if (event.origin !== signerOrigin)
4498
4881
  return;
@@ -4562,16 +4945,9 @@ async function runPopup(url, signerOrigin, ex) {
4562
4945
  // identity with the Bounded id token WE minted (a body secret, RS256-verified server-side), not
4563
4946
  // an ambient cookie - the issuer's session cookie is SameSite-scoped to its own origin and is
4564
4947
  // not sent on this cross-origin fetch, so the token is what carries the session's wallet.
4565
- async function fetchSigningCapability(messageHex) {
4566
- const cfg = await core.getConfig();
4567
- const base = (cfg.humanAuthApiUrl || 'https://auth.bounded.sh').replace(/\/$/, '');
4568
- let idToken = null;
4569
- try {
4570
- idToken = core.getActiveSessionManager().getIdToken();
4571
- }
4572
- catch (_a) {
4573
- idToken = null;
4574
- }
4948
+ async function fetchSigningCapability(messageHex, context) {
4949
+ context.assertCurrent();
4950
+ const { base, idToken } = context;
4575
4951
  if (!idToken) {
4576
4952
  throw new Error('This session has no Turnkey wallet. Sign in again to provision one.');
4577
4953
  }
@@ -4586,16 +4962,9 @@ async function fetchSigningCapability(messageHex) {
4586
4962
  // requires an issuer-signed authorization bound to this app origin and exact action. Fetch it
4587
4963
  // before posting to the signer. Walletless sessions are valid for createWallet because that is
4588
4964
  // the operation that provisions their first wallet.
4589
- async function fetchActionAuthorization(action) {
4590
- const cfg = await core.getConfig();
4591
- const base = (cfg.humanAuthApiUrl || 'https://auth.bounded.sh').replace(/\/$/, '');
4592
- let idToken = null;
4593
- try {
4594
- idToken = core.getActiveSessionManager().getIdToken();
4595
- }
4596
- catch (_a) {
4597
- idToken = null;
4598
- }
4965
+ async function fetchActionAuthorization(action, context) {
4966
+ context.assertCurrent();
4967
+ const { base, idToken } = context;
4599
4968
  if (!idToken) {
4600
4969
  throw new Error('Sign in again to set up your Turnkey wallet.');
4601
4970
  }
@@ -4615,31 +4984,23 @@ async function fetchActionAuthorization(action) {
4615
4984
  * an unswept session is account-bound and unusable without a live authorization,
4616
4985
  * it just costs its owner nothing and a stranger nothing.
4617
4986
  */
4618
- async function clearTurnkeySigningSession() {
4987
+ async function clearTurnkeySigningSession(idToken) {
4619
4988
  if (!hasDOM$2())
4620
4989
  return;
4621
- // The session can live in TWO places, and the sweep has to reach both:
4622
- // 1. the signer origin's store, shared by every signer frame in this app's partition -
4623
- // cleared by posting into a throwaway frame below;
4624
- // 2. the WARM widget iframe's memory. The signer keeps an in-memory mirror when the
4625
- // browser blocks its storage, and hideModal deliberately leaves that iframe mounted
4626
- // between signatures, so the mirror outlives logout. A throwaway frame cannot see
4627
- // another document's memory - the only way to take it is to destroy that document.
4628
- // Done first, and synchronously, so it lands even if the frame exchange below fails.
4629
- try {
4630
- const { origin } = await turnkeySignerPage();
4631
- (await Promise.resolve().then(function () { return require('./bounded-widget-modal-s0PL86Y-.js'); })).detachWarmSignerIframe(origin);
4632
- }
4633
- catch (_a) {
4634
- /* no modal module / no warm frame: nothing to take */
4635
- }
4990
+ // Destroy the trusted in-memory key even if configuration or token parsing fails.
4991
+ // Do it synchronously, before a different login can mount a replacement frame.
4992
+ detachWarmSignerIframe();
4993
+ // Capture the session before asynchronous teardown clears the SDK's token.
4994
+ const contextPromise = turnkeySignerPage(idToken);
4636
4995
  let frame = null;
4637
4996
  try {
4638
- frame = await openHiddenSignerFrame();
4997
+ const context = await contextPromise;
4998
+ setWalletSessionState(context.key, { status: 'verification-required', reason: 'logged_out' });
4999
+ frame = await openHiddenSignerFrame(context);
4639
5000
  await frame.request({ type: 'bounded:turnkey:clearSigningSession', requestId: `tkclear_${Date.now()}_${++requestSeq}` }, 4000);
4640
5001
  }
4641
- catch (_b) {
4642
- /* best-effort: never block or fail logout on the sweep */
5002
+ catch (_a) {
5003
+ // The warm frame was destroyed; the bounded store sweep is best effort.
4643
5004
  }
4644
5005
  finally {
4645
5006
  frame === null || frame === void 0 ? void 0 : frame.dispose();
@@ -4649,10 +5010,9 @@ function hasTurnkeyWallet() {
4649
5010
  return activeTurnkeyAddress() !== null;
4650
5011
  }
4651
5012
  /**
4652
- * Open the Bounded Turnkey signer popup and sign `message` (raw bytes) with the
4653
- * user's non-custodial Turnkey wallet, resolving with the ed25519 signature +
4654
- * the wallet address. MUST be called from a USER GESTURE (click/tap) or the
4655
- * browser blocks the popup.
5013
+ * Open the Bounded Turnkey approval card and sign `message` (raw bytes) with the
5014
+ * user's Turnkey wallet, resolving with the ed25519 signature and wallet address.
5015
+ * Call from a user gesture so a deliberate browser fallback can open if needed.
4656
5016
  *
4657
5017
  * The signer shows an approve card (wallet address, requesting origin, Approve /
4658
5018
  * Cancel). With a live 24-hour signing session (normally established by the login
@@ -4662,23 +5022,26 @@ function hasTurnkeyWallet() {
4662
5022
  */
4663
5023
  async function signSolanaMessageViaTurnkey(message) {
4664
5024
  if (!hasDOM$2()) {
4665
- throw new Error('Turnkey embedded-wallet signing is only available in the browser (popup-based).');
5025
+ throw new Error('Turnkey embedded-wallet signing is only available in the browser (trusted wallet UI).');
4666
5026
  }
4667
- const { url, origin } = await turnkeySignerPage();
5027
+ const context = await turnkeySignerPage();
5028
+ const { url, origin } = context;
4668
5029
  const requestId = `tksign_${Date.now()}_${++requestSeq}`;
4669
5030
  const messageHex = toHex(message);
4670
5031
  // AS-02: obtain the issuer-signed capability for THESE exact bytes first; the signer will
4671
5032
  // refuse (capability_required) without it. Fetched per signature (each is single-payload).
4672
- const capability = await fetchSigningCapability(messageHex);
5033
+ const capability = await fetchSigningCapability(messageHex, context);
4673
5034
  // Inline (authMode:"turnkey") sessions plant no Better Auth cookie on the issuer, so the
4674
5035
  // signer's same-origin status/create fetches need a bearer session proof: attach the
4675
5036
  // createWallet action authorization (it covers the sign flow's get-or-create too). Minted
4676
5037
  // from the same id token as the capability, so a session that minted one can mint both.
4677
- const authorization = await fetchActionAuthorization('createWallet');
5038
+ const authorization = await fetchActionAuthorization('createWallet', context);
4678
5039
  return await deliver(url, origin, {
5040
+ context: context.key, assertCurrent: context.assertCurrent,
4679
5041
  request: buildTurnkeySignRequest({ requestId, messageHex, capability, authorization }),
4680
5042
  onResult: (d) => {
4681
5043
  if (d.ok && typeof d.signatureHex === 'string' && typeof d.address === 'string') {
5044
+ setWalletSessionState(context.key, { expiresAt: Number(d.signingSessionExpiresAt) });
4682
5045
  return { done: true, value: { signature: fromHex(d.signatureHex), address: d.address, subOrgId: String(d.subOrgId || '') } };
4683
5046
  }
4684
5047
  return { error: String(d.error || 'Turnkey signing failed') };
@@ -5363,9 +5726,6 @@ async function exchangeCode(code, verifier, redirectUri) {
5363
5726
  return user;
5364
5727
  }
5365
5728
 
5366
- // NOTE: Provider class re-exports have been moved to index.ts (web) and
5367
- // index.native.ts (RN) to avoid eager resolution of web-only dependencies
5368
- // (react-dom) when this module is imported on RN.
5369
5729
  let currentAuthProvider = null;
5370
5730
  let currentAuthMethod = null;
5371
5731
  let currentAuthProviderConfigKey = null;
@@ -5705,7 +6065,7 @@ async function getAuthProvider(config) {
5705
6065
  // manager as legacy email sessions. Human login should be started
5706
6066
  // with loginWithRedirect(); this provider is kept as the restore and
5707
6067
  // logout surface for those sessions.
5708
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-BhYzRN0r.js'); })).EmailAuthProvider();
6068
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-Cpj8hIFu.js'); })).EmailAuthProvider();
5709
6069
  break;
5710
6070
  case "phantom": {
5711
6071
  // First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
@@ -5747,7 +6107,7 @@ async function getAuthProvider(config) {
5747
6107
  // (bounded-login-widget.ts), so registration never counts as
5748
6108
  // evidence the user has a wallet.
5749
6109
  await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
5750
- const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-CG3oKdp_.js'); });
6110
+ const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-Bi_mB_q3.js'); });
5751
6111
  currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
5752
6112
  break;
5753
6113
  }
@@ -5760,7 +6120,7 @@ async function getAuthProvider(config) {
5760
6120
  // Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
5761
6121
  // to point at a specific wallet or set chainId/domain.
5762
6122
  const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
5763
- const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-C3MwUTRd.js'); });
6123
+ const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-BXM8TfS3.js'); });
5764
6124
  currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
5765
6125
  break;
5766
6126
  }
@@ -5785,7 +6145,7 @@ async function getAuthProvider(config) {
5785
6145
  break;
5786
6146
  case "guest":
5787
6147
  // Anonymous auth: a device-local ed25519 keypair signs the challenge.
5788
- currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BiM35KHb.js'); })).GuestAuthProvider();
6148
+ currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-0PjGFGDm.js'); })).GuestAuthProvider();
5789
6149
  break;
5790
6150
  }
5791
6151
  if (!currentAuthProvider) {
@@ -5847,7 +6207,7 @@ async function signInAnonymously() {
5847
6207
  // Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
5848
6208
  // the main bundle for data-only apps. Specifier is a static string literal
5849
6209
  // so bundle-splitting can see it.
5850
- const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BiM35KHb.js'); })).GuestAuthProvider();
6210
+ const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-0PjGFGDm.js'); })).GuestAuthProvider();
5851
6211
  setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
5852
6212
  // adoptSessionProvider awaits only getConfig() (already resolved here) and then
5853
6213
  // runs every write - provider/method/config/marker/user - in one synchronous
@@ -6018,7 +6378,7 @@ function ensureSharedWalletLoginReady() {
6018
6378
  if (!sharedWalletReady) {
6019
6379
  sharedWalletReady = (async () => {
6020
6380
  await core.getConfig();
6021
- await Promise.resolve().then(function () { return require('./injected-wallet-provider-CG3oKdp_.js'); });
6381
+ await Promise.resolve().then(function () { return require('./injected-wallet-provider-Bi_mB_q3.js'); });
6022
6382
  })().catch((error) => {
6023
6383
  sharedWalletReady = null;
6024
6384
  throw error;
@@ -6155,13 +6515,14 @@ async function exchangeLogoutHint(issuerBase, idToken) {
6155
6515
  // which can be blocked or offline), so its failures are the one thing logout may
6156
6516
  // tolerate - never revokeSession's. Lazy import per this module's rule (the signer
6157
6517
  // bridge is browser-only machinery). Never rejects.
6158
- async function sweepTurnkeySigningSession() {
6518
+ async function sweepTurnkeySigningSession(idToken) {
6159
6519
  try {
6160
- await (await Promise.resolve().then(function () { return turnkeySignerBridge; })).clearTurnkeySigningSession();
6520
+ await (await Promise.resolve().then(function () { return turnkeySignerBridge; })).clearTurnkeySigningSession(idToken);
6161
6521
  }
6162
6522
  catch ( /* best-effort: never block or fail logout on the sweep */_a) { /* best-effort: never block or fail logout on the sweep */ }
6163
6523
  }
6164
6524
  async function logout$1(options) {
6525
+ invalidatePendingWalletLogin();
6165
6526
  // Re-initialize the provider if it was cleared by a prior logout — the exact mirror
6166
6527
  // of login()'s self-heal above. Every session-establishment path now adopts through
6167
6528
  // session-provider.ts, so this is a REPAIR (the registry was cleared by a previous
@@ -6193,7 +6554,7 @@ async function logout$1(options) {
6193
6554
  // sweep is a hidden signer-iframe exchange; start it now (after revocation is
6194
6555
  // committed - a failed logout leaves the still-logged-in user their session) and
6195
6556
  // let it overlap the provider teardown. sweepTurnkeySigningSession never rejects.
6196
- const signingSessionSweep = sweepTurnkeySigningSession();
6557
+ const signingSessionSweep = sweepTurnkeySigningSession(idTokenHint);
6197
6558
  // Only OIDC-hosted sessions own an issuer browser cookie. Turnkey-native
6198
6559
  // email OTP is also an email auth method, but it mints the app session
6199
6560
  // inline and has no issuer cookie to clear. Use the explicit session-kind
@@ -8039,4 +8400,4 @@ exports.useAuth = useAuth;
8039
8400
  exports.useBuildRun = useBuildRun;
8040
8401
  exports.useQuery = useQuery;
8041
8402
  exports.waitForAuthReady = waitForAuthReady;
8042
- //# sourceMappingURL=index.native-BZOWS_sR.js.map
8403
+ //# sourceMappingURL=index.native-CllNqdoy.js.map