@bounded-sh/client 0.0.95 → 0.0.97
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/turnkey-auth.d.ts +9 -7
- package/dist/bounded-widget-modal-C0Z7gmxH.mjs +31 -0
- package/dist/bounded-widget-modal-C0Z7gmxH.mjs.map +1 -0
- package/dist/bounded-widget-modal-s0PL86Y-.js +33 -0
- package/dist/bounded-widget-modal-s0PL86Y-.js.map +1 -0
- package/dist/builds/client.d.ts +31 -0
- package/dist/builds/index.d.ts +3 -2
- package/dist/{email-auth-provider-mNpHuk1i.js → email-auth-provider-BhYzRN0r.js} +2 -2
- package/dist/{email-auth-provider-mNpHuk1i.js.map → email-auth-provider-BhYzRN0r.js.map} +1 -1
- package/dist/{email-auth-provider-H6Dw5Z9x.mjs → email-auth-provider-CiPAD3vz.mjs} +2 -2
- package/dist/{email-auth-provider-H6Dw5Z9x.mjs.map → email-auth-provider-CiPAD3vz.mjs.map} +1 -1
- package/dist/{email-auth-provider-D7wnHbb3.mjs → email-auth-provider-DbBN1MXS.mjs} +2 -2
- package/dist/{email-auth-provider-D7wnHbb3.mjs.map → email-auth-provider-DbBN1MXS.mjs.map} +1 -1
- package/dist/{email-auth-provider-DqXtds8G.js → email-auth-provider-SqE5H8jv.js} +2 -2
- package/dist/{email-auth-provider-DqXtds8G.js.map → email-auth-provider-SqE5H8jv.js.map} +1 -1
- package/dist/{guest-auth-provider-DwgEfPWe.js → guest-auth-provider-BiM35KHb.js} +2 -2
- package/dist/{guest-auth-provider-DwgEfPWe.js.map → guest-auth-provider-BiM35KHb.js.map} +1 -1
- package/dist/{guest-auth-provider-CenCVVnc.mjs → guest-auth-provider-Dtsq4QaY.mjs} +2 -2
- package/dist/{guest-auth-provider-CenCVVnc.mjs.map → guest-auth-provider-Dtsq4QaY.mjs.map} +1 -1
- package/dist/{guest-auth-provider-CKMU9n1w.js → guest-auth-provider-JE0zdREA.js} +2 -2
- package/dist/{guest-auth-provider-CKMU9n1w.js.map → guest-auth-provider-JE0zdREA.js.map} +1 -1
- package/dist/{guest-auth-provider-CJjhV1b0.mjs → guest-auth-provider-KCalcsKu.mjs} +2 -2
- package/dist/{guest-auth-provider-CJjhV1b0.mjs.map → guest-auth-provider-KCalcsKu.mjs.map} +1 -1
- package/dist/{index-BiytVNMk.js → index-Bb7I-AoO.js} +524 -661
- package/dist/index-Bb7I-AoO.js.map +1 -0
- package/dist/{index-D3mp2wpM.mjs → index-C01Q7k1A.mjs} +524 -661
- package/dist/index-C01Q7k1A.mjs.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{index.native-vfNe1Pga.js → index.native-BZOWS_sR.js} +35 -332
- package/dist/index.native-BZOWS_sR.js.map +1 -0
- package/dist/{index.native-DHFDc9Vy.mjs → index.native-gVbAsohv.mjs} +35 -332
- package/dist/index.native-gVbAsohv.mjs.map +1 -0
- package/dist/index.native.js +1 -1
- package/dist/index.native.mjs +1 -1
- package/dist/{injected-evm-wallet-provider-BLg6H50t.js → injected-evm-wallet-provider-C3MwUTRd.js} +2 -2
- package/dist/{injected-evm-wallet-provider-BLg6H50t.js.map → injected-evm-wallet-provider-C3MwUTRd.js.map} +1 -1
- package/dist/{injected-evm-wallet-provider-BsxKY7bi.mjs → injected-evm-wallet-provider-DIMM0HdQ.mjs} +2 -2
- package/dist/{injected-evm-wallet-provider-BsxKY7bi.mjs.map → injected-evm-wallet-provider-DIMM0HdQ.mjs.map} +1 -1
- package/dist/{injected-evm-wallet-provider-B8_1qfbX.mjs → injected-evm-wallet-provider-K0j697jt.mjs} +2 -2
- package/dist/{injected-evm-wallet-provider-B8_1qfbX.mjs.map → injected-evm-wallet-provider-K0j697jt.mjs.map} +1 -1
- package/dist/{injected-evm-wallet-provider-B7lMl8NN.js → injected-evm-wallet-provider-ZrxW8UES.js} +2 -2
- package/dist/{injected-evm-wallet-provider-B7lMl8NN.js.map → injected-evm-wallet-provider-ZrxW8UES.js.map} +1 -1
- package/dist/{injected-wallet-provider-zLWZ_vKC.js → injected-wallet-provider-CDl-az6P.js} +2 -2
- package/dist/{injected-wallet-provider-zLWZ_vKC.js.map → injected-wallet-provider-CDl-az6P.js.map} +1 -1
- package/dist/{injected-wallet-provider-DNU7LBlK.js → injected-wallet-provider-CG3oKdp_.js} +2 -2
- package/dist/{injected-wallet-provider-DNU7LBlK.js.map → injected-wallet-provider-CG3oKdp_.js.map} +1 -1
- package/dist/{injected-wallet-provider-D8Y_sP0c.mjs → injected-wallet-provider-DCaT1pKl.mjs} +2 -2
- package/dist/{injected-wallet-provider-D8Y_sP0c.mjs.map → injected-wallet-provider-DCaT1pKl.mjs.map} +1 -1
- package/dist/{injected-wallet-provider-DE_Kce-V.mjs → injected-wallet-provider-DH8yLpyf.mjs} +2 -2
- package/dist/{injected-wallet-provider-DE_Kce-V.mjs.map → injected-wallet-provider-DH8yLpyf.mjs.map} +1 -1
- package/package.json +2 -2
- package/dist/index-BiytVNMk.js.map +0 -1
- package/dist/index-D3mp2wpM.mjs.map +0 -1
- package/dist/index.native-DHFDc9Vy.mjs.map +0 -1
- package/dist/index.native-vfNe1Pga.js.map +0 -1
|
@@ -4199,402 +4199,6 @@ async function ensureSolanaMobileWalletRegistered(config, network) {
|
|
|
4199
4199
|
|
|
4200
4200
|
var bufferExports = requireBuffer();
|
|
4201
4201
|
|
|
4202
|
-
// bounded-widget-modal.ts - the shared in-app WIDGET shell for Bounded's login
|
|
4203
|
-
// and signing surfaces. One Shadow-DOM overlay + panel that matches the Bounded
|
|
4204
|
-
// widget (Seal) look and MOTION: a backdrop fade, a panel rise on desktop, a
|
|
4205
|
-
// bottom-sheet on mobile, a height tween as content swaps, and a clean close.
|
|
4206
|
-
//
|
|
4207
|
-
// It hosts EITHER:
|
|
4208
|
-
// - NATIVE content (the login card the SDK renders itself), or
|
|
4209
|
-
// - an IFRAME (the Bounded-owned signer page, which must run on the single
|
|
4210
|
-
// signer origin because the wallet's 24h signing session lives in that
|
|
4211
|
-
// origin's storage, out of every app's reach).
|
|
4212
|
-
//
|
|
4213
|
-
// The modal is a warm singleton: it stays mounted (hidden) between uses so a
|
|
4214
|
-
// second open - or a signature right after login - is instant and, when it is
|
|
4215
|
-
// already open, a clean in-place view swap rather than a reopen.
|
|
4216
|
-
function hasDOM$5() {
|
|
4217
|
-
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
4218
|
-
}
|
|
4219
|
-
const OVERLAY_CSS = `
|
|
4220
|
-
:host{all:initial}
|
|
4221
|
-
*{box-sizing:border-box}
|
|
4222
|
-
.bw-ov{position:fixed;inset:0;z-index:2147483647;display:none;
|
|
4223
|
-
font-family:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
|
|
4224
|
-
.bw-ov.mounted{display:block}
|
|
4225
|
-
.bw-back{position:fixed;inset:0;background:rgba(4,4,5,.62);opacity:0;
|
|
4226
|
-
transition:opacity .24s ease;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}
|
|
4227
|
-
.bw-wrap{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:20px}
|
|
4228
|
-
.bw-panel{position:relative;width:420px;max-width:calc(100vw - 28px);height:280px;
|
|
4229
|
-
border-radius:14px;overflow:hidden;background:#0e0f11;border:1px solid rgba(232,230,222,.08);
|
|
4230
|
-
box-shadow:0 1px 0 rgba(232,230,222,.03) inset,0 40px 90px -45px rgba(0,0,0,.85);
|
|
4231
|
-
opacity:0;transform:translateY(14px) scale(.98);will-change:transform,opacity,height;
|
|
4232
|
-
transition:opacity .28s cubic-bezier(.2,.8,.2,1),transform .28s cubic-bezier(.2,.8,.2,1),
|
|
4233
|
-
height .3s cubic-bezier(.2,.8,.2,1)}
|
|
4234
|
-
.bw-ov.show .bw-back{opacity:1}
|
|
4235
|
-
.bw-ov.show .bw-panel{opacity:1;transform:none}
|
|
4236
|
-
.bw-ov.closing .bw-back{opacity:0}
|
|
4237
|
-
.bw-ov.closing .bw-panel{opacity:0;transform:translateY(12px) scale(.99)}
|
|
4238
|
-
.bw-content{position:absolute;inset:0;overflow:hidden}
|
|
4239
|
-
.bw-frame{display:block;width:100%;height:100%;border:0;background:transparent;color-scheme:dark}
|
|
4240
|
-
/* editorial corner crop-marks, matching the reference frame */
|
|
4241
|
-
.bw-mk{position:absolute;width:13px;height:13px;pointer-events:none;z-index:2;
|
|
4242
|
-
border-color:rgba(232,230,222,.22);border-style:solid;border-width:0}
|
|
4243
|
-
.bw-mk.tl{top:11px;left:11px;border-top-width:1px;border-left-width:1px}
|
|
4244
|
-
.bw-mk.tr{top:11px;right:11px;border-top-width:1px;border-right-width:1px}
|
|
4245
|
-
.bw-mk.bl{bottom:11px;left:11px;border-bottom-width:1px;border-left-width:1px}
|
|
4246
|
-
.bw-mk.br{bottom:11px;right:11px;border-bottom-width:1px;border-right-width:1px}
|
|
4247
|
-
@media(max-width:560px){
|
|
4248
|
-
.bw-wrap{align-items:flex-end;padding:0}
|
|
4249
|
-
.bw-panel{width:100%;max-width:100%;border-radius:18px 18px 0 0;border-bottom:0}
|
|
4250
|
-
.bw-content{overflow-y:auto}
|
|
4251
|
-
.bw-ov.show .bw-panel{transform:none}
|
|
4252
|
-
.bw-ov:not(.show) .bw-panel,.bw-ov.closing .bw-panel{transform:translateY(18%)}
|
|
4253
|
-
}
|
|
4254
|
-
@media(prefers-reduced-motion:reduce){.bw-back,.bw-panel{transition:opacity .12s linear!important}}
|
|
4255
|
-
`;
|
|
4256
|
-
let modal = null;
|
|
4257
|
-
function getModal() {
|
|
4258
|
-
if (modal)
|
|
4259
|
-
return modal;
|
|
4260
|
-
const root = document.createElement("div");
|
|
4261
|
-
root.setAttribute("data-bounded-widget", "");
|
|
4262
|
-
const shadow = root.attachShadow({ mode: "open" });
|
|
4263
|
-
const style = document.createElement("style");
|
|
4264
|
-
style.textContent = OVERLAY_CSS;
|
|
4265
|
-
shadow.appendChild(style);
|
|
4266
|
-
const overlay = document.createElement("div");
|
|
4267
|
-
overlay.className = "bw-ov";
|
|
4268
|
-
const back = document.createElement("div");
|
|
4269
|
-
back.className = "bw-back";
|
|
4270
|
-
const wrap = document.createElement("div");
|
|
4271
|
-
wrap.className = "bw-wrap";
|
|
4272
|
-
const panel = document.createElement("div");
|
|
4273
|
-
panel.className = "bw-panel";
|
|
4274
|
-
const content = document.createElement("div");
|
|
4275
|
-
content.className = "bw-content";
|
|
4276
|
-
panel.appendChild(content);
|
|
4277
|
-
for (const corner of ["tl", "tr", "bl", "br"]) {
|
|
4278
|
-
const mk = document.createElement("span");
|
|
4279
|
-
mk.className = "bw-mk " + corner;
|
|
4280
|
-
panel.appendChild(mk);
|
|
4281
|
-
}
|
|
4282
|
-
wrap.appendChild(panel);
|
|
4283
|
-
overlay.appendChild(back);
|
|
4284
|
-
overlay.appendChild(wrap);
|
|
4285
|
-
shadow.appendChild(overlay);
|
|
4286
|
-
(document.body || document.documentElement).appendChild(root);
|
|
4287
|
-
const m = {
|
|
4288
|
-
root,
|
|
4289
|
-
overlay,
|
|
4290
|
-
panel,
|
|
4291
|
-
content,
|
|
4292
|
-
iframe: null,
|
|
4293
|
-
iframeOrigin: "",
|
|
4294
|
-
iframeReady: false,
|
|
4295
|
-
onBackdrop: null,
|
|
4296
|
-
onIframeMessage: null,
|
|
4297
|
-
chain: Promise.resolve(),
|
|
4298
|
-
fitObserver: null,
|
|
4299
|
-
generation: 0,
|
|
4300
|
-
onSupersede: null,
|
|
4301
|
-
};
|
|
4302
|
-
// The dismiss handler lives on `.bw-wrap`: it is the full-viewport flex layer
|
|
4303
|
-
// that sits ABOVE `.bw-back`, so it (not the backdrop element) receives clicks
|
|
4304
|
-
// outside the panel. Fire only when the click lands on the wrap itself, never
|
|
4305
|
-
// when it bubbles up from the panel or its content.
|
|
4306
|
-
wrap.addEventListener("click", (event) => {
|
|
4307
|
-
if (event.target === wrap && m.onBackdrop)
|
|
4308
|
-
m.onBackdrop();
|
|
4309
|
-
});
|
|
4310
|
-
window.addEventListener("message", (event) => {
|
|
4311
|
-
if (!m.iframe)
|
|
4312
|
-
return;
|
|
4313
|
-
if (event.origin !== m.iframeOrigin)
|
|
4314
|
-
return;
|
|
4315
|
-
if (event.source !== m.iframe.contentWindow)
|
|
4316
|
-
return;
|
|
4317
|
-
const d = event.data;
|
|
4318
|
-
if (!d || typeof d !== "object")
|
|
4319
|
-
return;
|
|
4320
|
-
if (m.onIframeMessage)
|
|
4321
|
-
m.onIframeMessage(d);
|
|
4322
|
-
});
|
|
4323
|
-
modal = m;
|
|
4324
|
-
return m;
|
|
4325
|
-
}
|
|
4326
|
-
function showModal(m) {
|
|
4327
|
-
m.overlay.classList.remove("closing");
|
|
4328
|
-
m.overlay.classList.add("mounted");
|
|
4329
|
-
// Force a reflow so the .show transition runs from the hidden state every open.
|
|
4330
|
-
void m.panel.offsetWidth;
|
|
4331
|
-
m.overlay.classList.add("show");
|
|
4332
|
-
}
|
|
4333
|
-
function hideModal(m, after) {
|
|
4334
|
-
m.overlay.classList.remove("show");
|
|
4335
|
-
m.overlay.classList.add("closing");
|
|
4336
|
-
// Capture the generation at hide time. If a newer open re-shows the modal within
|
|
4337
|
-
// the close tween, this stale timer must NOT strip `.mounted`/`.closing` off the
|
|
4338
|
-
// freshly reopened widget - only tear down when we are still the same open.
|
|
4339
|
-
const gen = m.generation;
|
|
4340
|
-
window.setTimeout(() => {
|
|
4341
|
-
if (m.generation === gen)
|
|
4342
|
-
m.overlay.classList.remove("mounted", "closing");
|
|
4343
|
-
}, 260);
|
|
4344
|
-
}
|
|
4345
|
-
function setPanelHeight(m, px) {
|
|
4346
|
-
const h = Math.max(140, Math.min(680, Math.round(px)));
|
|
4347
|
-
m.panel.style.height = h + "px";
|
|
4348
|
-
}
|
|
4349
|
-
// True outer height of the mounted card: the max of its padding-box scroll height
|
|
4350
|
-
// (catches overflowing descendants) and its border-box height plus own vertical
|
|
4351
|
-
// margins (catches border + margins, which scrollHeight omits). Ceiled so a
|
|
4352
|
-
// sub-pixel row is never clipped under `overflow:hidden`.
|
|
4353
|
-
function measureOuterHeight(target) {
|
|
4354
|
-
var _a;
|
|
4355
|
-
const cs = (_a = target.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.getComputedStyle(target);
|
|
4356
|
-
const marginY = cs ? parseFloat(cs.marginTop) + parseFloat(cs.marginBottom) : 0;
|
|
4357
|
-
const borderBox = target.offsetHeight + (Number.isFinite(marginY) ? marginY : 0);
|
|
4358
|
-
const rect = target.getBoundingClientRect().height;
|
|
4359
|
-
return Math.ceil(Math.max(target.scrollHeight, borderBox, rect));
|
|
4360
|
-
}
|
|
4361
|
-
/**
|
|
4362
|
-
* Measure the currently mounted native content and tween the panel to fit it.
|
|
4363
|
-
* Re-fits automatically when the card's intrinsic height changes - notably after
|
|
4364
|
-
* the Newsreader/Schibsted web fonts swap in (which grows line boxes a few px
|
|
4365
|
-
* after first paint) and on inline view swaps (email -> OTP code -> success).
|
|
4366
|
-
*/
|
|
4367
|
-
function fitPanelToContent(m) {
|
|
4368
|
-
var _a, _b;
|
|
4369
|
-
const first = m.content.firstElementChild;
|
|
4370
|
-
const target = first || m.content;
|
|
4371
|
-
const apply = () => {
|
|
4372
|
-
// Only fit while this exact node is still the mounted card.
|
|
4373
|
-
if (m.content.firstElementChild !== first && first)
|
|
4374
|
-
return;
|
|
4375
|
-
const h = measureOuterHeight(target);
|
|
4376
|
-
if (h > 0)
|
|
4377
|
-
setPanelHeight(m, h);
|
|
4378
|
-
};
|
|
4379
|
-
apply();
|
|
4380
|
-
const view = target.ownerDocument.defaultView;
|
|
4381
|
-
// Re-fit after fonts load (one-shot) and once the ResizeObserver-less browsers
|
|
4382
|
-
// have painted, so the height never lags a late reflow.
|
|
4383
|
-
(_a = view === null || view === void 0 ? void 0 : view.requestAnimationFrame) === null || _a === void 0 ? void 0 : _a.call(view, apply);
|
|
4384
|
-
const fonts = target.ownerDocument.fonts;
|
|
4385
|
-
if (fonts === null || fonts === void 0 ? void 0 : fonts.ready)
|
|
4386
|
-
fonts.ready.then(apply).catch(() => { });
|
|
4387
|
-
(_b = m.fitObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
4388
|
-
m.fitObserver = null;
|
|
4389
|
-
if (first && typeof (view === null || view === void 0 ? void 0 : view.ResizeObserver) === "function") {
|
|
4390
|
-
const ro = new view.ResizeObserver(() => apply());
|
|
4391
|
-
ro.observe(first);
|
|
4392
|
-
m.fitObserver = ro;
|
|
4393
|
-
}
|
|
4394
|
-
}
|
|
4395
|
-
/** Switch the modal into NATIVE-content mode: detach any iframe, mount `node`. */
|
|
4396
|
-
function mountContent(m, node) {
|
|
4397
|
-
var _a;
|
|
4398
|
-
detachIframe(m);
|
|
4399
|
-
(_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
4400
|
-
m.fitObserver = null;
|
|
4401
|
-
m.content.replaceChildren(node);
|
|
4402
|
-
m.content.style.display = "block";
|
|
4403
|
-
}
|
|
4404
|
-
/** Get (or lazily create) the warm iframe for signer/hosted-page mode. */
|
|
4405
|
-
function ensureIframe(m, url, origin) {
|
|
4406
|
-
var _a;
|
|
4407
|
-
if (m.iframe && m.iframeOrigin === origin && m.iframe.src === url)
|
|
4408
|
-
return m.iframe;
|
|
4409
|
-
detachIframe(m);
|
|
4410
|
-
(_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
4411
|
-
m.fitObserver = null;
|
|
4412
|
-
m.content.style.display = "none";
|
|
4413
|
-
const iframe = document.createElement("iframe");
|
|
4414
|
-
iframe.className = "bw-frame";
|
|
4415
|
-
iframe.setAttribute("title", "Bounded");
|
|
4416
|
-
iframe.setAttribute("allow", "clipboard-write");
|
|
4417
|
-
iframe.src = url;
|
|
4418
|
-
m.iframeOrigin = origin;
|
|
4419
|
-
m.iframeReady = false;
|
|
4420
|
-
m.panel.appendChild(iframe);
|
|
4421
|
-
m.iframe = iframe;
|
|
4422
|
-
return iframe;
|
|
4423
|
-
}
|
|
4424
|
-
/**
|
|
4425
|
-
* Destroy the warm signer iframe if one is currently mounted for `origin`, and report
|
|
4426
|
-
* whether there was one. Logout needs this: a signing session can live in that document's
|
|
4427
|
-
* MEMORY (the signer keeps an in-memory mirror when the browser blocks its storage), and a
|
|
4428
|
-
* throwaway iframe opened by the sweep can only ever clear the shared store, never another
|
|
4429
|
-
* document's memory. Removing the iframe discards its browsing context, which takes the
|
|
4430
|
-
* mirror with it.
|
|
4431
|
-
*
|
|
4432
|
-
* Deliberately does NOT call getModal(): that would BUILD a modal just to tear it down, on
|
|
4433
|
-
* every logout of every app, most of which never mounted one.
|
|
4434
|
-
*/
|
|
4435
|
-
function detachWarmSignerIframe(origin) {
|
|
4436
|
-
if (!modal || !modal.iframe || modal.iframeOrigin !== origin)
|
|
4437
|
-
return false;
|
|
4438
|
-
detachIframe(modal);
|
|
4439
|
-
return true;
|
|
4440
|
-
}
|
|
4441
|
-
function detachIframe(m) {
|
|
4442
|
-
if (m.iframe) {
|
|
4443
|
-
try {
|
|
4444
|
-
m.iframe.remove();
|
|
4445
|
-
}
|
|
4446
|
-
catch (_a) {
|
|
4447
|
-
/* ignore */
|
|
4448
|
-
}
|
|
4449
|
-
}
|
|
4450
|
-
m.iframe = null;
|
|
4451
|
-
m.iframeReady = false;
|
|
4452
|
-
m.iframeOrigin = "";
|
|
4453
|
-
m.onIframeMessage = null;
|
|
4454
|
-
}
|
|
4455
|
-
// Load the Bounded editorial typefaces (Newsreader display + Schibsted Grotesk body +
|
|
4456
|
-
// JetBrains Mono) once at the document level so they apply inside the shadow root. A
|
|
4457
|
-
// no-op if already present or off-DOM. Fonts loaded here match the hosted auth page.
|
|
4458
|
-
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";
|
|
4459
|
-
function ensureFonts() {
|
|
4460
|
-
if (!hasDOM$5())
|
|
4461
|
-
return;
|
|
4462
|
-
if (document.querySelector('link[data-bounded-fonts="1"]'))
|
|
4463
|
-
return;
|
|
4464
|
-
const pre1 = document.createElement("link");
|
|
4465
|
-
pre1.rel = "preconnect";
|
|
4466
|
-
pre1.href = "https://fonts.googleapis.com";
|
|
4467
|
-
const pre2 = document.createElement("link");
|
|
4468
|
-
pre2.rel = "preconnect";
|
|
4469
|
-
pre2.href = "https://fonts.gstatic.com";
|
|
4470
|
-
pre2.crossOrigin = "anonymous";
|
|
4471
|
-
const link = document.createElement("link");
|
|
4472
|
-
link.rel = "stylesheet";
|
|
4473
|
-
link.href = FONTS_HREF;
|
|
4474
|
-
link.setAttribute("data-bounded-fonts", "1");
|
|
4475
|
-
document.head.append(pre1, pre2, link);
|
|
4476
|
-
}
|
|
4477
|
-
|
|
4478
|
-
var boundedWidgetModal = /*#__PURE__*/Object.freeze({
|
|
4479
|
-
__proto__: null,
|
|
4480
|
-
detachIframe: detachIframe,
|
|
4481
|
-
detachWarmSignerIframe: detachWarmSignerIframe,
|
|
4482
|
-
ensureFonts: ensureFonts,
|
|
4483
|
-
ensureIframe: ensureIframe,
|
|
4484
|
-
fitPanelToContent: fitPanelToContent,
|
|
4485
|
-
getModal: getModal,
|
|
4486
|
-
hasDOM: hasDOM$5,
|
|
4487
|
-
hideModal: hideModal,
|
|
4488
|
-
mountContent: mountContent,
|
|
4489
|
-
setPanelHeight: setPanelHeight,
|
|
4490
|
-
showModal: showModal
|
|
4491
|
-
});
|
|
4492
|
-
|
|
4493
|
-
// turnkey-wallet-widget.ts - runs one Turnkey signer exchange inside the shared
|
|
4494
|
-
// in-app WIDGET (bounded-widget-modal.ts). The Bounded-owned signer page is loaded
|
|
4495
|
-
// in a warm iframe behind the modal so signing feels native: an in-app approve
|
|
4496
|
-
// card (with inline code entry when the 24h signing session must first be
|
|
4497
|
-
// established), never a jarring popup.
|
|
4498
|
-
//
|
|
4499
|
-
// WHY AN IFRAME: the wallet's signing session lives in the single Bounded signer
|
|
4500
|
-
// origin's storage, out of every app's reach, so signing MUST run on that origin;
|
|
4501
|
-
// the iframe brings it in-app.
|
|
4502
|
-
//
|
|
4503
|
-
// Restricted embedders: if the frame never loads, the SAME request reruns through
|
|
4504
|
-
// `popupFallback` (a real window). Callers pass a popup implementation and never
|
|
4505
|
-
// see the difference.
|
|
4506
|
-
/**
|
|
4507
|
-
* Run one signer exchange through the in-app widget. Resolves when `onResult`
|
|
4508
|
-
* accepts a result; falls back to `popupFallback` if the frame cannot be mounted
|
|
4509
|
-
* or never loads.
|
|
4510
|
-
*/
|
|
4511
|
-
async function runWidget(url, signerOrigin, ex, popupFallback) {
|
|
4512
|
-
if (!hasDOM$5())
|
|
4513
|
-
return popupFallback();
|
|
4514
|
-
const m = getModal();
|
|
4515
|
-
const requestId = String(ex.request.requestId);
|
|
4516
|
-
// Serialize: the approve card is modal, and one warm frame handles one request at a time.
|
|
4517
|
-
const run = m.chain.then(() => new Promise((resolve, reject) => {
|
|
4518
|
-
let settled = false;
|
|
4519
|
-
let usedFallback = false;
|
|
4520
|
-
const iframe = ensureIframe(m, url, signerOrigin);
|
|
4521
|
-
const send = () => {
|
|
4522
|
-
if (!iframe.contentWindow)
|
|
4523
|
-
return;
|
|
4524
|
-
try {
|
|
4525
|
-
iframe.contentWindow.postMessage(ex.request, signerOrigin);
|
|
4526
|
-
}
|
|
4527
|
-
catch (_a) {
|
|
4528
|
-
/* frame gone */
|
|
4529
|
-
}
|
|
4530
|
-
};
|
|
4531
|
-
let loadTimer = 0;
|
|
4532
|
-
let ceilingTimer = 0;
|
|
4533
|
-
const finish = (fn) => {
|
|
4534
|
-
if (settled)
|
|
4535
|
-
return;
|
|
4536
|
-
settled = true;
|
|
4537
|
-
m.onIframeMessage = null;
|
|
4538
|
-
m.onBackdrop = null;
|
|
4539
|
-
window.clearTimeout(loadTimer);
|
|
4540
|
-
window.clearTimeout(ceilingTimer);
|
|
4541
|
-
fn();
|
|
4542
|
-
};
|
|
4543
|
-
const goFallback = () => {
|
|
4544
|
-
if (usedFallback || settled)
|
|
4545
|
-
return;
|
|
4546
|
-
usedFallback = true;
|
|
4547
|
-
hideModal(m);
|
|
4548
|
-
finish(() => popupFallback().then(resolve, reject));
|
|
4549
|
-
};
|
|
4550
|
-
m.onBackdrop = () => {
|
|
4551
|
-
hideModal(m);
|
|
4552
|
-
finish(() => reject(new Error("cancelled")));
|
|
4553
|
-
};
|
|
4554
|
-
m.onIframeMessage = (d) => {
|
|
4555
|
-
if (d.type === "bounded:turnkey:ready") {
|
|
4556
|
-
m.iframeReady = true;
|
|
4557
|
-
send();
|
|
4558
|
-
return;
|
|
4559
|
-
}
|
|
4560
|
-
if (d.type === "bounded:turnkey:resize" && typeof d.height === "number") {
|
|
4561
|
-
setPanelHeight(m, d.height);
|
|
4562
|
-
return;
|
|
4563
|
-
}
|
|
4564
|
-
if (d.type === "bounded:turnkey:result" && d.requestId === requestId) {
|
|
4565
|
-
const outcome = ex.onResult(d);
|
|
4566
|
-
if ("error" in outcome) {
|
|
4567
|
-
hideModal(m);
|
|
4568
|
-
finish(() => reject(new Error(outcome.error)));
|
|
4569
|
-
return;
|
|
4570
|
-
}
|
|
4571
|
-
if (outcome.done) {
|
|
4572
|
-
hideModal(m);
|
|
4573
|
-
finish(() => resolve(outcome.value));
|
|
4574
|
-
}
|
|
4575
|
-
}
|
|
4576
|
-
};
|
|
4577
|
-
showModal(m);
|
|
4578
|
-
if (m.iframeReady)
|
|
4579
|
-
send();
|
|
4580
|
-
// If the frame never loads, degrade to the popup (best-effort - a post-load
|
|
4581
|
-
// popup may be blocked without a gesture, in which case the fallback rejects).
|
|
4582
|
-
loadTimer = window.setTimeout(() => {
|
|
4583
|
-
if (!m.iframeReady && !settled)
|
|
4584
|
-
goFallback();
|
|
4585
|
-
}, 8000);
|
|
4586
|
-
// Absolute ceiling so a stuck flow cannot hang forever.
|
|
4587
|
-
ceilingTimer = window.setTimeout(() => {
|
|
4588
|
-
if (!settled) {
|
|
4589
|
-
hideModal(m);
|
|
4590
|
-
finish(() => reject(new Error("Wallet approval timed out.")));
|
|
4591
|
-
}
|
|
4592
|
-
}, 5 * 60 * 1000);
|
|
4593
|
-
}));
|
|
4594
|
-
m.chain = run.then(() => undefined, () => undefined);
|
|
4595
|
-
return run;
|
|
4596
|
-
}
|
|
4597
|
-
|
|
4598
4202
|
// turnkey-signer-frame.ts - a HIDDEN, offscreen iframe of the Bounded signer page
|
|
4599
4203
|
// for UI-less signer exchanges: sealing a login code (bounded:turnkey:encryptOtp),
|
|
4600
4204
|
// establishing the 24h signing session right after login
|
|
@@ -4605,7 +4209,7 @@ async function runWidget(url, signerOrigin, ex, popupFallback) {
|
|
|
4605
4209
|
// exchanges (seal a code, then establish the session with the keypair the SEAL
|
|
4606
4210
|
// left in that exact page instance) talk to the same page. Origin + source +
|
|
4607
4211
|
// requestId are all pinned; dispose() tears everything down.
|
|
4608
|
-
function hasDOM$
|
|
4212
|
+
function hasDOM$5() {
|
|
4609
4213
|
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
4610
4214
|
}
|
|
4611
4215
|
async function signerPage() {
|
|
@@ -4615,7 +4219,7 @@ async function signerPage() {
|
|
|
4615
4219
|
}
|
|
4616
4220
|
/** Mount the hidden signer frame. Callers MUST dispose() when done. */
|
|
4617
4221
|
async function openHiddenSignerFrame() {
|
|
4618
|
-
if (!hasDOM$
|
|
4222
|
+
if (!hasDOM$5())
|
|
4619
4223
|
throw new Error('The Bounded signer frame is only available in the browser.');
|
|
4620
4224
|
const { url, origin } = await signerPage();
|
|
4621
4225
|
const frame = document.createElement('iframe');
|
|
@@ -4820,17 +4424,17 @@ function buildTurnkeyActionRequest(opts) {
|
|
|
4820
4424
|
// storage, out of every app's reach, so ALL Bounded apps (any subdomain / custom
|
|
4821
4425
|
// domain) sign through this one origin - giving every user one wallet that roams
|
|
4822
4426
|
// across every app.
|
|
4823
|
-
function hasDOM$
|
|
4427
|
+
function hasDOM$4() {
|
|
4824
4428
|
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
4825
4429
|
}
|
|
4826
|
-
//
|
|
4827
|
-
//
|
|
4828
|
-
//
|
|
4829
|
-
// click
|
|
4830
|
-
//
|
|
4430
|
+
// BSA-2026-006: dispatch every signing / get-or-create exchange through a TOP-LEVEL
|
|
4431
|
+
// POPUP the caller opened from the user gesture - NEVER a framed widget. An authorized
|
|
4432
|
+
// tenant that embedded the signer could overlay its predictable Approve control and steal
|
|
4433
|
+
// one concealed click into a fund-moving signature; a top-level window it cannot reach or
|
|
4434
|
+
// visually redress closes that. A blocked popup surfaces `popup_blocked` (runPopup) and we
|
|
4435
|
+
// never silently fall back to an iframe.
|
|
4831
4436
|
async function deliver(url, origin, ex) {
|
|
4832
|
-
|
|
4833
|
-
return runWidget(url, origin, { request: ex.request, onResult: ex.onResult }, popup);
|
|
4437
|
+
return runPopup(url, origin, ex);
|
|
4834
4438
|
}
|
|
4835
4439
|
async function turnkeySignerPage() {
|
|
4836
4440
|
const cfg = await core.getConfig();
|
|
@@ -4881,7 +4485,7 @@ function activeTurnkeyAddress() {
|
|
|
4881
4485
|
? address
|
|
4882
4486
|
: null;
|
|
4883
4487
|
}
|
|
4884
|
-
let requestSeq
|
|
4488
|
+
let requestSeq = 0;
|
|
4885
4489
|
// Shared popup driver: open the signer page, do the ready/postMessage handshake, and
|
|
4886
4490
|
// resolve when `onResult` accepts a result message. Origin + source are pinned.
|
|
4887
4491
|
async function runPopup(url, signerOrigin, ex) {
|
|
@@ -4891,7 +4495,7 @@ async function runPopup(url, signerOrigin, ex) {
|
|
|
4891
4495
|
const top = window.screenY + Math.max(0, (window.outerHeight - h) / 2);
|
|
4892
4496
|
const popup = window.open(url, 'bounded-turnkey-signer', `width=${w},height=${h},left=${left},top=${top}`);
|
|
4893
4497
|
if (!popup) {
|
|
4894
|
-
throw new Error('
|
|
4498
|
+
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.');
|
|
4895
4499
|
}
|
|
4896
4500
|
return await new Promise((resolve, reject) => {
|
|
4897
4501
|
let settled = false;
|
|
@@ -5035,7 +4639,7 @@ async function fetchActionAuthorization(action) {
|
|
|
5035
4639
|
* it just costs its owner nothing and a stranger nothing.
|
|
5036
4640
|
*/
|
|
5037
4641
|
async function clearTurnkeySigningSession() {
|
|
5038
|
-
if (!hasDOM$
|
|
4642
|
+
if (!hasDOM$4())
|
|
5039
4643
|
return;
|
|
5040
4644
|
// The session can live in TWO places, and the sweep has to reach both:
|
|
5041
4645
|
// 1. the signer origin's store, shared by every signer frame in this app's partition -
|
|
@@ -5055,7 +4659,7 @@ async function clearTurnkeySigningSession() {
|
|
|
5055
4659
|
let frame = null;
|
|
5056
4660
|
try {
|
|
5057
4661
|
frame = await openHiddenSignerFrame();
|
|
5058
|
-
await frame.request({ type: 'bounded:turnkey:clearSigningSession', requestId: `tkclear_${Date.now()}_${++requestSeq
|
|
4662
|
+
await frame.request({ type: 'bounded:turnkey:clearSigningSession', requestId: `tkclear_${Date.now()}_${++requestSeq}` }, 4000);
|
|
5059
4663
|
}
|
|
5060
4664
|
catch (_b) {
|
|
5061
4665
|
/* best-effort: never block or fail logout on the sweep */
|
|
@@ -5076,10 +4680,10 @@ function hasTurnkeyWallet() {
|
|
|
5076
4680
|
* Use this to learn the fee-payer address BEFORE building a transaction to sign.
|
|
5077
4681
|
*/
|
|
5078
4682
|
async function getOrCreateTurnkeyWallet() {
|
|
5079
|
-
if (!hasDOM$
|
|
4683
|
+
if (!hasDOM$4())
|
|
5080
4684
|
throw new Error('Turnkey embedded wallets are only available in the browser (popup-based).');
|
|
5081
4685
|
const { url, origin } = await turnkeySignerPage();
|
|
5082
|
-
const requestId = `tkcreate_${Date.now()}_${++requestSeq
|
|
4686
|
+
const requestId = `tkcreate_${Date.now()}_${++requestSeq}`;
|
|
5083
4687
|
const authorization = await fetchActionAuthorization('createWallet');
|
|
5084
4688
|
return await deliver(url, origin, {
|
|
5085
4689
|
request: buildTurnkeyActionRequest({ requestId, action: 'createWallet', authorization }),
|
|
@@ -5101,7 +4705,7 @@ async function getOrCreateTurnkeyWallet() {
|
|
|
5101
4705
|
* that tab - it never transits Bounded.
|
|
5102
4706
|
*/
|
|
5103
4707
|
async function openTurnkeyKeyExport() {
|
|
5104
|
-
if (!hasDOM$
|
|
4708
|
+
if (!hasDOM$4())
|
|
5105
4709
|
throw new Error('Turnkey key export is only available in the browser.');
|
|
5106
4710
|
if (!hasTurnkeyWallet())
|
|
5107
4711
|
throw new Error('This session has no Turnkey wallet to export.');
|
|
@@ -5149,11 +4753,11 @@ async function openTurnkeyKeyExport() {
|
|
|
5149
4753
|
* completes the signature.
|
|
5150
4754
|
*/
|
|
5151
4755
|
async function signSolanaMessageViaTurnkey(message) {
|
|
5152
|
-
if (!hasDOM$
|
|
4756
|
+
if (!hasDOM$4()) {
|
|
5153
4757
|
throw new Error('Turnkey embedded-wallet signing is only available in the browser (popup-based).');
|
|
5154
4758
|
}
|
|
5155
4759
|
const { url, origin } = await turnkeySignerPage();
|
|
5156
|
-
const requestId = `tksign_${Date.now()}_${++requestSeq
|
|
4760
|
+
const requestId = `tksign_${Date.now()}_${++requestSeq}`;
|
|
5157
4761
|
const messageHex = toHex(message);
|
|
5158
4762
|
// AS-02: obtain the issuer-signed capability for THESE exact bytes first; the signer will
|
|
5159
4763
|
// refuse (capability_required) without it. Fetched per signature (each is single-payload).
|
|
@@ -5307,7 +4911,7 @@ const ISSUER_SESSION_KIND_KEY = 'bounded_issuer_session_kind';
|
|
|
5307
4911
|
// not by `typeof window` (truthy on RN). OIDC redirect/popup is web-only, but the
|
|
5308
4912
|
// session it mints is read back through this same selector everywhere.
|
|
5309
4913
|
const sessionManager = core.getActiveSessionManager;
|
|
5310
|
-
function hasDOM$
|
|
4914
|
+
function hasDOM$3() {
|
|
5311
4915
|
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
5312
4916
|
}
|
|
5313
4917
|
function decodeJwt(token) {
|
|
@@ -5458,7 +5062,7 @@ async function buildAuthorize(opts) {
|
|
|
5458
5062
|
throw new Error('appId is not configured (init the SDK with your appId)');
|
|
5459
5063
|
// Web: default to the current page so callers don't have to pass redirectUri. Native has no
|
|
5460
5064
|
// window — it MUST pass an https universal link (the system auth session needs a real URL).
|
|
5461
|
-
const redirectUri = opts.redirectUri || (hasDOM$
|
|
5065
|
+
const redirectUri = opts.redirectUri || (hasDOM$3() ? window.location.origin + window.location.pathname : undefined);
|
|
5462
5066
|
if (!redirectUri)
|
|
5463
5067
|
throw new Error('redirectUri is required on React Native — pass the https universal link the app owner registered.');
|
|
5464
5068
|
const { verifier, challenge } = await makePkce();
|
|
@@ -5566,7 +5170,7 @@ async function runNativeLogin(opts) {
|
|
|
5566
5170
|
async function loginWithRedirect(opts) {
|
|
5567
5171
|
if (isNative())
|
|
5568
5172
|
return runNativeLogin(opts);
|
|
5569
|
-
if (!hasDOM$
|
|
5173
|
+
if (!hasDOM$3())
|
|
5570
5174
|
throw new Error('loginWithRedirect requires a browser or a configured React Native platform');
|
|
5571
5175
|
const { url, pkce } = await buildAuthorize(opts);
|
|
5572
5176
|
// Stash the verifier + state so the redirect_uri page can finish the exchange. sessionStorage
|
|
@@ -5583,7 +5187,7 @@ async function loginWithRedirect(opts) {
|
|
|
5583
5187
|
* loginWithRedirect() (which resolves with the User), so there is no separate redirect
|
|
5584
5188
|
* page to finish from. Calling it on RN is harmless. */
|
|
5585
5189
|
async function completeLoginFromRedirect() {
|
|
5586
|
-
if (!hasDOM$
|
|
5190
|
+
if (!hasDOM$3())
|
|
5587
5191
|
return null;
|
|
5588
5192
|
const url = new URL(window.location.href);
|
|
5589
5193
|
const code = url.searchParams.get('code');
|
|
@@ -5631,7 +5235,7 @@ async function completeLoginFromRedirect() {
|
|
|
5631
5235
|
* redirect_uri page posts the code back. The redirect_uri page just calls the usual
|
|
5632
5236
|
* completeLoginFromRedirect() (which auto-detects the popup) — no separate call needed. */
|
|
5633
5237
|
async function loginWithPopup(opts) {
|
|
5634
|
-
if (!hasDOM$
|
|
5238
|
+
if (!hasDOM$3())
|
|
5635
5239
|
throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
|
|
5636
5240
|
// Open synchronously, before PKCE/config awaits consume the click's user-
|
|
5637
5241
|
// activation window. Opening only after buildAuthorize() intermittently
|
|
@@ -5791,7 +5395,7 @@ async function loginWithPopup(opts) {
|
|
|
5791
5395
|
/** Call this on the redirect_uri page when login was opened via loginWithPopup(): posts the
|
|
5792
5396
|
* code back to the opener and closes. (For the redirect variant use completeLoginFromRedirect.) */
|
|
5793
5397
|
function completeLoginInPopup(openerOrigin) {
|
|
5794
|
-
if (!hasDOM$
|
|
5398
|
+
if (!hasDOM$3() || !window.opener)
|
|
5795
5399
|
return;
|
|
5796
5400
|
const url = new URL(window.location.href);
|
|
5797
5401
|
const code = url.searchParams.get('code');
|
|
@@ -6245,7 +5849,7 @@ async function getAuthProvider(config) {
|
|
|
6245
5849
|
// manager as legacy email sessions. Human login should be started
|
|
6246
5850
|
// with loginWithRedirect(); this provider is kept as the restore and
|
|
6247
5851
|
// logout surface for those sessions.
|
|
6248
|
-
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-
|
|
5852
|
+
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-SqE5H8jv.js'); })).EmailAuthProvider();
|
|
6249
5853
|
break;
|
|
6250
5854
|
case "phantom": {
|
|
6251
5855
|
// First-class Solana WALLET LOGIN (the poof/TaroBase-style "connect
|
|
@@ -6287,7 +5891,7 @@ async function getAuthProvider(config) {
|
|
|
6287
5891
|
// (bounded-login-widget.ts), so registration never counts as
|
|
6288
5892
|
// evidence the user has a wallet.
|
|
6289
5893
|
await ensureSolanaMobileWalletRegistered(config, configuredNetwork);
|
|
6290
|
-
const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-
|
|
5894
|
+
const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-CDl-az6P.js'); });
|
|
6291
5895
|
currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
|
|
6292
5896
|
break;
|
|
6293
5897
|
}
|
|
@@ -6300,7 +5904,7 @@ async function getAuthProvider(config) {
|
|
|
6300
5904
|
// Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
|
|
6301
5905
|
// to point at a specific wallet or set chainId/domain.
|
|
6302
5906
|
const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
|
|
6303
|
-
const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-
|
|
5907
|
+
const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-ZrxW8UES.js'); });
|
|
6304
5908
|
currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
|
|
6305
5909
|
break;
|
|
6306
5910
|
}
|
|
@@ -6325,7 +5929,7 @@ async function getAuthProvider(config) {
|
|
|
6325
5929
|
break;
|
|
6326
5930
|
case "guest":
|
|
6327
5931
|
// Anonymous auth: a device-local ed25519 keypair signs the challenge.
|
|
6328
|
-
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-
|
|
5932
|
+
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-JE0zdREA.js'); })).GuestAuthProvider();
|
|
6329
5933
|
break;
|
|
6330
5934
|
}
|
|
6331
5935
|
if (!currentAuthProvider) {
|
|
@@ -6387,7 +5991,7 @@ async function signInAnonymously() {
|
|
|
6387
5991
|
// Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
|
|
6388
5992
|
// the main bundle for data-only apps. Specifier is a static string literal
|
|
6389
5993
|
// so bundle-splitting can see it.
|
|
6390
|
-
const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-
|
|
5994
|
+
const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-JE0zdREA.js'); })).GuestAuthProvider();
|
|
6391
5995
|
setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
|
|
6392
5996
|
// adoptSessionProvider awaits only getConfig() (already resolved here) and then
|
|
6393
5997
|
// runs every write - provider/method/config/marker/user - in one synchronous
|
|
@@ -6558,7 +6162,7 @@ function ensureSharedWalletLoginReady() {
|
|
|
6558
6162
|
if (!sharedWalletReady) {
|
|
6559
6163
|
sharedWalletReady = (async () => {
|
|
6560
6164
|
await core.getConfig();
|
|
6561
|
-
await Promise.resolve().then(function () { return require('./injected-wallet-provider-
|
|
6165
|
+
await Promise.resolve().then(function () { return require('./injected-wallet-provider-CDl-az6P.js'); });
|
|
6562
6166
|
})().catch((error) => {
|
|
6563
6167
|
sharedWalletReady = null;
|
|
6564
6168
|
throw error;
|
|
@@ -6661,7 +6265,7 @@ async function loginWithWallet(options) {
|
|
|
6661
6265
|
await ensureSharedWalletLoginReady();
|
|
6662
6266
|
else
|
|
6663
6267
|
await ensureWalletLoginReady();
|
|
6664
|
-
const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-
|
|
6268
|
+
const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-CDl-az6P.js'); });
|
|
6665
6269
|
// Keep the RAW wallet provider: the offchain wrapper forwards only a fixed
|
|
6666
6270
|
// set of methods, so cleanup below must speak to the provider that actually
|
|
6667
6271
|
// holds the config rather than to whatever is wrapping it.
|
|
@@ -7112,80 +6716,350 @@ function setAuthLoading(loading) {
|
|
|
7112
6716
|
function getAuthLoading() {
|
|
7113
6717
|
return isAuthLoading;
|
|
7114
6718
|
}
|
|
7115
|
-
async function login(options) {
|
|
7116
|
-
if (!authProviderInstance) {
|
|
7117
|
-
throw new Error('SDK not initialized. Please call init() first.');
|
|
6719
|
+
async function login(options) {
|
|
6720
|
+
if (!authProviderInstance) {
|
|
6721
|
+
throw new Error('SDK not initialized. Please call init() first.');
|
|
6722
|
+
}
|
|
6723
|
+
const loggedInUser = await login$1(options);
|
|
6724
|
+
setCurrentUser(loggedInUser);
|
|
6725
|
+
return currentUser;
|
|
6726
|
+
}
|
|
6727
|
+
async function logout(options) {
|
|
6728
|
+
if (!authProviderInstance) {
|
|
6729
|
+
throw new Error('SDK not initialized. Please call init() first.');
|
|
6730
|
+
}
|
|
6731
|
+
await logout$1(options);
|
|
6732
|
+
setCurrentUser(null);
|
|
6733
|
+
}
|
|
6734
|
+
var hasRunFirstTime = false;
|
|
6735
|
+
function userPrincipalKey(user) {
|
|
6736
|
+
var _a, _b, _c;
|
|
6737
|
+
if (!user)
|
|
6738
|
+
return null;
|
|
6739
|
+
return (_c = (_b = (_a = user.id) !== null && _a !== void 0 ? _a : user.address) !== null && _b !== void 0 ? _b : user.email) !== null && _c !== void 0 ? _c : null;
|
|
6740
|
+
}
|
|
6741
|
+
// Update the currentUser and notify listeners
|
|
6742
|
+
function setCurrentUser(user) {
|
|
6743
|
+
// Enrich with the universal identity fields (id/email) derived from the
|
|
6744
|
+
// active session's idToken — central chokepoint so every provider's user
|
|
6745
|
+
// object gains { id, address, email } without each construction site
|
|
6746
|
+
// having to set them.
|
|
6747
|
+
user = withUserIdentity(user);
|
|
6748
|
+
const lastUser = currentUser;
|
|
6749
|
+
const wasInitializedBefore = hasRunFirstTime;
|
|
6750
|
+
const lastPrincipal = userPrincipalKey(currentUser);
|
|
6751
|
+
const nextPrincipal = userPrincipalKey(user);
|
|
6752
|
+
const shouldRunListeners = !hasRunFirstTime || lastPrincipal !== nextPrincipal;
|
|
6753
|
+
hasRunFirstTime = true;
|
|
6754
|
+
currentUser = user;
|
|
6755
|
+
if (shouldRunListeners) {
|
|
6756
|
+
// Isolate each listener: a consumer callback that throws (during logout in
|
|
6757
|
+
// particular) must not abort the notification loop OR skip the socket reopen
|
|
6758
|
+
// below. Before this, a single throwing listener propagated out of
|
|
6759
|
+
// setCurrentUser, so the realtime socket kept streaming as the signed-out
|
|
6760
|
+
// user and the other listeners were never told the principal changed.
|
|
6761
|
+
authStateListeners.forEach((callback) => {
|
|
6762
|
+
try {
|
|
6763
|
+
callback(user);
|
|
6764
|
+
}
|
|
6765
|
+
catch (error) {
|
|
6766
|
+
console.error('Error in onAuthStateChanged listener:', error);
|
|
6767
|
+
}
|
|
6768
|
+
});
|
|
6769
|
+
// Reconnect WebSocket connections with new auth when user changes.
|
|
6770
|
+
// Skip on the first setCurrentUser call during init() — there are no
|
|
6771
|
+
// existing connections to reconnect at that point.
|
|
6772
|
+
if (wasInitializedBefore && userPrincipalKey(lastUser) !== nextPrincipal) {
|
|
6773
|
+
core.reconnectWithNewAuth().catch((error) => {
|
|
6774
|
+
console.error('Error reconnecting WebSocket after auth change:', error);
|
|
6775
|
+
});
|
|
6776
|
+
}
|
|
6777
|
+
}
|
|
6778
|
+
// Return this commit's normalized snapshot. A listener can start another
|
|
6779
|
+
// login, so the mutable currentUser may already belong to a newer session.
|
|
6780
|
+
return user;
|
|
6781
|
+
}
|
|
6782
|
+
function getCurrentUser() {
|
|
6783
|
+
return currentUser;
|
|
6784
|
+
}
|
|
6785
|
+
/** @internal Snapshot/restore surface for transactional auth-provider switches. */
|
|
6786
|
+
function getAuthProviderInstance() {
|
|
6787
|
+
return authProviderInstance;
|
|
6788
|
+
}
|
|
6789
|
+
function setAuthProviderInstance(provider) {
|
|
6790
|
+
authProviderInstance = provider;
|
|
6791
|
+
}
|
|
6792
|
+
|
|
6793
|
+
// bounded-widget-modal.ts - the shared in-app WIDGET shell for Bounded's login
|
|
6794
|
+
// and signing surfaces. One Shadow-DOM overlay + panel that matches the Bounded
|
|
6795
|
+
// widget (Seal) look and MOTION: a backdrop fade, a panel rise on desktop, a
|
|
6796
|
+
// bottom-sheet on mobile, a height tween as content swaps, and a clean close.
|
|
6797
|
+
//
|
|
6798
|
+
// It hosts EITHER:
|
|
6799
|
+
// - NATIVE content (the login card the SDK renders itself), or
|
|
6800
|
+
// - an IFRAME (the Bounded-owned signer page, which must run on the single
|
|
6801
|
+
// signer origin because the wallet's 24h signing session lives in that
|
|
6802
|
+
// origin's storage, out of every app's reach).
|
|
6803
|
+
//
|
|
6804
|
+
// The modal is a warm singleton: it stays mounted (hidden) between uses so a
|
|
6805
|
+
// second open - or a signature right after login - is instant and, when it is
|
|
6806
|
+
// already open, a clean in-place view swap rather than a reopen.
|
|
6807
|
+
function hasDOM$2() {
|
|
6808
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
6809
|
+
}
|
|
6810
|
+
const OVERLAY_CSS = `
|
|
6811
|
+
:host{all:initial}
|
|
6812
|
+
*{box-sizing:border-box}
|
|
6813
|
+
.bw-ov{position:fixed;inset:0;z-index:2147483647;display:none;
|
|
6814
|
+
font-family:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
|
|
6815
|
+
.bw-ov.mounted{display:block}
|
|
6816
|
+
.bw-back{position:fixed;inset:0;background:rgba(4,4,5,.62);opacity:0;
|
|
6817
|
+
transition:opacity .24s ease;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}
|
|
6818
|
+
.bw-wrap{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:20px}
|
|
6819
|
+
.bw-panel{position:relative;width:420px;max-width:calc(100vw - 28px);height:280px;
|
|
6820
|
+
border-radius:14px;overflow:hidden;background:#0e0f11;border:1px solid rgba(232,230,222,.08);
|
|
6821
|
+
box-shadow:0 1px 0 rgba(232,230,222,.03) inset,0 40px 90px -45px rgba(0,0,0,.85);
|
|
6822
|
+
opacity:0;transform:translateY(14px) scale(.98);will-change:transform,opacity,height;
|
|
6823
|
+
transition:opacity .28s cubic-bezier(.2,.8,.2,1),transform .28s cubic-bezier(.2,.8,.2,1),
|
|
6824
|
+
height .3s cubic-bezier(.2,.8,.2,1)}
|
|
6825
|
+
.bw-ov.show .bw-back{opacity:1}
|
|
6826
|
+
.bw-ov.show .bw-panel{opacity:1;transform:none}
|
|
6827
|
+
.bw-ov.closing .bw-back{opacity:0}
|
|
6828
|
+
.bw-ov.closing .bw-panel{opacity:0;transform:translateY(12px) scale(.99)}
|
|
6829
|
+
.bw-content{position:absolute;inset:0;overflow:hidden}
|
|
6830
|
+
.bw-frame{display:block;width:100%;height:100%;border:0;background:transparent;color-scheme:dark}
|
|
6831
|
+
/* editorial corner crop-marks, matching the reference frame */
|
|
6832
|
+
.bw-mk{position:absolute;width:13px;height:13px;pointer-events:none;z-index:2;
|
|
6833
|
+
border-color:rgba(232,230,222,.22);border-style:solid;border-width:0}
|
|
6834
|
+
.bw-mk.tl{top:11px;left:11px;border-top-width:1px;border-left-width:1px}
|
|
6835
|
+
.bw-mk.tr{top:11px;right:11px;border-top-width:1px;border-right-width:1px}
|
|
6836
|
+
.bw-mk.bl{bottom:11px;left:11px;border-bottom-width:1px;border-left-width:1px}
|
|
6837
|
+
.bw-mk.br{bottom:11px;right:11px;border-bottom-width:1px;border-right-width:1px}
|
|
6838
|
+
@media(max-width:560px){
|
|
6839
|
+
.bw-wrap{align-items:flex-end;padding:0}
|
|
6840
|
+
.bw-panel{width:100%;max-width:100%;border-radius:18px 18px 0 0;border-bottom:0}
|
|
6841
|
+
.bw-content{overflow-y:auto}
|
|
6842
|
+
.bw-ov.show .bw-panel{transform:none}
|
|
6843
|
+
.bw-ov:not(.show) .bw-panel,.bw-ov.closing .bw-panel{transform:translateY(18%)}
|
|
6844
|
+
}
|
|
6845
|
+
@media(prefers-reduced-motion:reduce){.bw-back,.bw-panel{transition:opacity .12s linear!important}}
|
|
6846
|
+
`;
|
|
6847
|
+
let modal = null;
|
|
6848
|
+
function getModal() {
|
|
6849
|
+
if (modal)
|
|
6850
|
+
return modal;
|
|
6851
|
+
const root = document.createElement("div");
|
|
6852
|
+
root.setAttribute("data-bounded-widget", "");
|
|
6853
|
+
const shadow = root.attachShadow({ mode: "open" });
|
|
6854
|
+
const style = document.createElement("style");
|
|
6855
|
+
style.textContent = OVERLAY_CSS;
|
|
6856
|
+
shadow.appendChild(style);
|
|
6857
|
+
const overlay = document.createElement("div");
|
|
6858
|
+
overlay.className = "bw-ov";
|
|
6859
|
+
const back = document.createElement("div");
|
|
6860
|
+
back.className = "bw-back";
|
|
6861
|
+
const wrap = document.createElement("div");
|
|
6862
|
+
wrap.className = "bw-wrap";
|
|
6863
|
+
const panel = document.createElement("div");
|
|
6864
|
+
panel.className = "bw-panel";
|
|
6865
|
+
const content = document.createElement("div");
|
|
6866
|
+
content.className = "bw-content";
|
|
6867
|
+
panel.appendChild(content);
|
|
6868
|
+
for (const corner of ["tl", "tr", "bl", "br"]) {
|
|
6869
|
+
const mk = document.createElement("span");
|
|
6870
|
+
mk.className = "bw-mk " + corner;
|
|
6871
|
+
panel.appendChild(mk);
|
|
6872
|
+
}
|
|
6873
|
+
wrap.appendChild(panel);
|
|
6874
|
+
overlay.appendChild(back);
|
|
6875
|
+
overlay.appendChild(wrap);
|
|
6876
|
+
shadow.appendChild(overlay);
|
|
6877
|
+
(document.body || document.documentElement).appendChild(root);
|
|
6878
|
+
const m = {
|
|
6879
|
+
root,
|
|
6880
|
+
overlay,
|
|
6881
|
+
panel,
|
|
6882
|
+
content,
|
|
6883
|
+
iframe: null,
|
|
6884
|
+
iframeOrigin: "",
|
|
6885
|
+
iframeReady: false,
|
|
6886
|
+
onBackdrop: null,
|
|
6887
|
+
onIframeMessage: null,
|
|
6888
|
+
chain: Promise.resolve(),
|
|
6889
|
+
fitObserver: null,
|
|
6890
|
+
generation: 0,
|
|
6891
|
+
onSupersede: null,
|
|
6892
|
+
};
|
|
6893
|
+
// The dismiss handler lives on `.bw-wrap`: it is the full-viewport flex layer
|
|
6894
|
+
// that sits ABOVE `.bw-back`, so it (not the backdrop element) receives clicks
|
|
6895
|
+
// outside the panel. Fire only when the click lands on the wrap itself, never
|
|
6896
|
+
// when it bubbles up from the panel or its content.
|
|
6897
|
+
wrap.addEventListener("click", (event) => {
|
|
6898
|
+
if (event.target === wrap && m.onBackdrop)
|
|
6899
|
+
m.onBackdrop();
|
|
6900
|
+
});
|
|
6901
|
+
window.addEventListener("message", (event) => {
|
|
6902
|
+
if (!m.iframe)
|
|
6903
|
+
return;
|
|
6904
|
+
if (event.origin !== m.iframeOrigin)
|
|
6905
|
+
return;
|
|
6906
|
+
if (event.source !== m.iframe.contentWindow)
|
|
6907
|
+
return;
|
|
6908
|
+
const d = event.data;
|
|
6909
|
+
if (!d || typeof d !== "object")
|
|
6910
|
+
return;
|
|
6911
|
+
if (m.onIframeMessage)
|
|
6912
|
+
m.onIframeMessage(d);
|
|
6913
|
+
});
|
|
6914
|
+
modal = m;
|
|
6915
|
+
return m;
|
|
6916
|
+
}
|
|
6917
|
+
function showModal(m) {
|
|
6918
|
+
m.overlay.classList.remove("closing");
|
|
6919
|
+
m.overlay.classList.add("mounted");
|
|
6920
|
+
// Force a reflow so the .show transition runs from the hidden state every open.
|
|
6921
|
+
void m.panel.offsetWidth;
|
|
6922
|
+
m.overlay.classList.add("show");
|
|
6923
|
+
}
|
|
6924
|
+
function hideModal(m, after) {
|
|
6925
|
+
m.overlay.classList.remove("show");
|
|
6926
|
+
m.overlay.classList.add("closing");
|
|
6927
|
+
// Capture the generation at hide time. If a newer open re-shows the modal within
|
|
6928
|
+
// the close tween, this stale timer must NOT strip `.mounted`/`.closing` off the
|
|
6929
|
+
// freshly reopened widget - only tear down when we are still the same open.
|
|
6930
|
+
const gen = m.generation;
|
|
6931
|
+
window.setTimeout(() => {
|
|
6932
|
+
if (m.generation === gen)
|
|
6933
|
+
m.overlay.classList.remove("mounted", "closing");
|
|
6934
|
+
}, 260);
|
|
6935
|
+
}
|
|
6936
|
+
function setPanelHeight(m, px) {
|
|
6937
|
+
const h = Math.max(140, Math.min(680, Math.round(px)));
|
|
6938
|
+
m.panel.style.height = h + "px";
|
|
6939
|
+
}
|
|
6940
|
+
// True outer height of the mounted card: the max of its padding-box scroll height
|
|
6941
|
+
// (catches overflowing descendants) and its border-box height plus own vertical
|
|
6942
|
+
// margins (catches border + margins, which scrollHeight omits). Ceiled so a
|
|
6943
|
+
// sub-pixel row is never clipped under `overflow:hidden`.
|
|
6944
|
+
function measureOuterHeight(target) {
|
|
6945
|
+
var _a;
|
|
6946
|
+
const cs = (_a = target.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.getComputedStyle(target);
|
|
6947
|
+
const marginY = cs ? parseFloat(cs.marginTop) + parseFloat(cs.marginBottom) : 0;
|
|
6948
|
+
const borderBox = target.offsetHeight + (Number.isFinite(marginY) ? marginY : 0);
|
|
6949
|
+
const rect = target.getBoundingClientRect().height;
|
|
6950
|
+
return Math.ceil(Math.max(target.scrollHeight, borderBox, rect));
|
|
6951
|
+
}
|
|
6952
|
+
/**
|
|
6953
|
+
* Measure the currently mounted native content and tween the panel to fit it.
|
|
6954
|
+
* Re-fits automatically when the card's intrinsic height changes - notably after
|
|
6955
|
+
* the Newsreader/Schibsted web fonts swap in (which grows line boxes a few px
|
|
6956
|
+
* after first paint) and on inline view swaps (email -> OTP code -> success).
|
|
6957
|
+
*/
|
|
6958
|
+
function fitPanelToContent(m) {
|
|
6959
|
+
var _a, _b;
|
|
6960
|
+
const first = m.content.firstElementChild;
|
|
6961
|
+
const target = first || m.content;
|
|
6962
|
+
const apply = () => {
|
|
6963
|
+
// Only fit while this exact node is still the mounted card.
|
|
6964
|
+
if (m.content.firstElementChild !== first && first)
|
|
6965
|
+
return;
|
|
6966
|
+
const h = measureOuterHeight(target);
|
|
6967
|
+
if (h > 0)
|
|
6968
|
+
setPanelHeight(m, h);
|
|
6969
|
+
};
|
|
6970
|
+
apply();
|
|
6971
|
+
const view = target.ownerDocument.defaultView;
|
|
6972
|
+
// Re-fit after fonts load (one-shot) and once the ResizeObserver-less browsers
|
|
6973
|
+
// have painted, so the height never lags a late reflow.
|
|
6974
|
+
(_a = view === null || view === void 0 ? void 0 : view.requestAnimationFrame) === null || _a === void 0 ? void 0 : _a.call(view, apply);
|
|
6975
|
+
const fonts = target.ownerDocument.fonts;
|
|
6976
|
+
if (fonts === null || fonts === void 0 ? void 0 : fonts.ready)
|
|
6977
|
+
fonts.ready.then(apply).catch(() => { });
|
|
6978
|
+
(_b = m.fitObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
6979
|
+
m.fitObserver = null;
|
|
6980
|
+
if (first && typeof (view === null || view === void 0 ? void 0 : view.ResizeObserver) === "function") {
|
|
6981
|
+
const ro = new view.ResizeObserver(() => apply());
|
|
6982
|
+
ro.observe(first);
|
|
6983
|
+
m.fitObserver = ro;
|
|
7118
6984
|
}
|
|
7119
|
-
const loggedInUser = await login$1(options);
|
|
7120
|
-
setCurrentUser(loggedInUser);
|
|
7121
|
-
return currentUser;
|
|
7122
6985
|
}
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
6986
|
+
/** Switch the modal into NATIVE-content mode: detach any iframe, mount `node`. */
|
|
6987
|
+
function mountContent(m, node) {
|
|
6988
|
+
var _a;
|
|
6989
|
+
detachIframe(m);
|
|
6990
|
+
(_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
6991
|
+
m.fitObserver = null;
|
|
6992
|
+
m.content.replaceChildren(node);
|
|
6993
|
+
m.content.style.display = "block";
|
|
7129
6994
|
}
|
|
7130
|
-
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
6995
|
+
/**
|
|
6996
|
+
* Destroy the warm signer iframe if one is currently mounted for `origin`, and report
|
|
6997
|
+
* whether there was one. Logout needs this: a signing session can live in that document's
|
|
6998
|
+
* MEMORY (the signer keeps an in-memory mirror when the browser blocks its storage), and a
|
|
6999
|
+
* throwaway iframe opened by the sweep can only ever clear the shared store, never another
|
|
7000
|
+
* document's memory. Removing the iframe discards its browsing context, which takes the
|
|
7001
|
+
* mirror with it.
|
|
7002
|
+
*
|
|
7003
|
+
* Deliberately does NOT call getModal(): that would BUILD a modal just to tear it down, on
|
|
7004
|
+
* every logout of every app, most of which never mounted one.
|
|
7005
|
+
*/
|
|
7006
|
+
function detachWarmSignerIframe(origin) {
|
|
7007
|
+
if (!modal || !modal.iframe || modal.iframeOrigin !== origin)
|
|
7008
|
+
return false;
|
|
7009
|
+
detachIframe(modal);
|
|
7010
|
+
return true;
|
|
7136
7011
|
}
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
|
|
7144
|
-
const lastUser = currentUser;
|
|
7145
|
-
const wasInitializedBefore = hasRunFirstTime;
|
|
7146
|
-
const lastPrincipal = userPrincipalKey(currentUser);
|
|
7147
|
-
const nextPrincipal = userPrincipalKey(user);
|
|
7148
|
-
const shouldRunListeners = !hasRunFirstTime || lastPrincipal !== nextPrincipal;
|
|
7149
|
-
hasRunFirstTime = true;
|
|
7150
|
-
currentUser = user;
|
|
7151
|
-
if (shouldRunListeners) {
|
|
7152
|
-
// Isolate each listener: a consumer callback that throws (during logout in
|
|
7153
|
-
// particular) must not abort the notification loop OR skip the socket reopen
|
|
7154
|
-
// below. Before this, a single throwing listener propagated out of
|
|
7155
|
-
// setCurrentUser, so the realtime socket kept streaming as the signed-out
|
|
7156
|
-
// user and the other listeners were never told the principal changed.
|
|
7157
|
-
authStateListeners.forEach((callback) => {
|
|
7158
|
-
try {
|
|
7159
|
-
callback(user);
|
|
7160
|
-
}
|
|
7161
|
-
catch (error) {
|
|
7162
|
-
console.error('Error in onAuthStateChanged listener:', error);
|
|
7163
|
-
}
|
|
7164
|
-
});
|
|
7165
|
-
// Reconnect WebSocket connections with new auth when user changes.
|
|
7166
|
-
// Skip on the first setCurrentUser call during init() — there are no
|
|
7167
|
-
// existing connections to reconnect at that point.
|
|
7168
|
-
if (wasInitializedBefore && userPrincipalKey(lastUser) !== nextPrincipal) {
|
|
7169
|
-
core.reconnectWithNewAuth().catch((error) => {
|
|
7170
|
-
console.error('Error reconnecting WebSocket after auth change:', error);
|
|
7171
|
-
});
|
|
7012
|
+
function detachIframe(m) {
|
|
7013
|
+
if (m.iframe) {
|
|
7014
|
+
try {
|
|
7015
|
+
m.iframe.remove();
|
|
7016
|
+
}
|
|
7017
|
+
catch (_a) {
|
|
7018
|
+
/* ignore */
|
|
7172
7019
|
}
|
|
7173
7020
|
}
|
|
7174
|
-
|
|
7175
|
-
|
|
7176
|
-
|
|
7177
|
-
|
|
7178
|
-
function getCurrentUser() {
|
|
7179
|
-
return currentUser;
|
|
7180
|
-
}
|
|
7181
|
-
/** @internal Snapshot/restore surface for transactional auth-provider switches. */
|
|
7182
|
-
function getAuthProviderInstance() {
|
|
7183
|
-
return authProviderInstance;
|
|
7021
|
+
m.iframe = null;
|
|
7022
|
+
m.iframeReady = false;
|
|
7023
|
+
m.iframeOrigin = "";
|
|
7024
|
+
m.onIframeMessage = null;
|
|
7184
7025
|
}
|
|
7185
|
-
|
|
7186
|
-
|
|
7026
|
+
// Load the Bounded editorial typefaces (Newsreader display + Schibsted Grotesk body +
|
|
7027
|
+
// JetBrains Mono) once at the document level so they apply inside the shadow root. A
|
|
7028
|
+
// no-op if already present or off-DOM. Fonts loaded here match the hosted auth page.
|
|
7029
|
+
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";
|
|
7030
|
+
function ensureFonts() {
|
|
7031
|
+
if (!hasDOM$2())
|
|
7032
|
+
return;
|
|
7033
|
+
if (document.querySelector('link[data-bounded-fonts="1"]'))
|
|
7034
|
+
return;
|
|
7035
|
+
const pre1 = document.createElement("link");
|
|
7036
|
+
pre1.rel = "preconnect";
|
|
7037
|
+
pre1.href = "https://fonts.googleapis.com";
|
|
7038
|
+
const pre2 = document.createElement("link");
|
|
7039
|
+
pre2.rel = "preconnect";
|
|
7040
|
+
pre2.href = "https://fonts.gstatic.com";
|
|
7041
|
+
pre2.crossOrigin = "anonymous";
|
|
7042
|
+
const link = document.createElement("link");
|
|
7043
|
+
link.rel = "stylesheet";
|
|
7044
|
+
link.href = FONTS_HREF;
|
|
7045
|
+
link.setAttribute("data-bounded-fonts", "1");
|
|
7046
|
+
document.head.append(pre1, pre2, link);
|
|
7187
7047
|
}
|
|
7188
7048
|
|
|
7049
|
+
var boundedWidgetModal = /*#__PURE__*/Object.freeze({
|
|
7050
|
+
__proto__: null,
|
|
7051
|
+
detachIframe: detachIframe,
|
|
7052
|
+
detachWarmSignerIframe: detachWarmSignerIframe,
|
|
7053
|
+
ensureFonts: ensureFonts,
|
|
7054
|
+
fitPanelToContent: fitPanelToContent,
|
|
7055
|
+
getModal: getModal,
|
|
7056
|
+
hasDOM: hasDOM$2,
|
|
7057
|
+
hideModal: hideModal,
|
|
7058
|
+
mountContent: mountContent,
|
|
7059
|
+
setPanelHeight: setPanelHeight,
|
|
7060
|
+
showModal: showModal
|
|
7061
|
+
});
|
|
7062
|
+
|
|
7189
7063
|
// bounded-widget-ownership.ts - per-open ownership generation for the singleton
|
|
7190
7064
|
// login/signing modal (bounded-widget-modal.ts).
|
|
7191
7065
|
//
|
|
@@ -7626,72 +7500,11 @@ function shouldOfferSolanaMobileWallet(input) {
|
|
|
7626
7500
|
function hasDOM$1() {
|
|
7627
7501
|
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
7628
7502
|
}
|
|
7629
|
-
let requestSeq = 0;
|
|
7630
|
-
// Seal the OTP in the given hidden signer iframe on the Bounded origin. The signer page
|
|
7631
|
-
// runs the vendored @turnkey/crypto encryptOtpCodeToBundle (which verifies the enclave's
|
|
7632
|
-
// signature on the target bundle first) and KEEPS the sealing keypair for the follow-up
|
|
7633
|
-
// establishSigningSession exchange below.
|
|
7634
|
-
async function sealOtpInFrame(frame, code, otpEncryptionTargetBundle) {
|
|
7635
|
-
const d = await frame.request({
|
|
7636
|
-
type: 'bounded:turnkey:encryptOtp',
|
|
7637
|
-
requestId: `tkotp_${Date.now()}_${++requestSeq}`,
|
|
7638
|
-
otpCode: code,
|
|
7639
|
-
otpEncryptionTargetBundle,
|
|
7640
|
-
}, 30000).catch(() => { throw new Error('Securing the code timed out. Please try again.'); });
|
|
7641
|
-
if (d.ok && typeof d.encryptedOtpBundle === 'string' && d.encryptedOtpBundle) {
|
|
7642
|
-
return d.encryptedOtpBundle;
|
|
7643
|
-
}
|
|
7644
|
-
throw new Error(String(d.error || 'Could not secure the code. Please try again.'));
|
|
7645
|
-
}
|
|
7646
|
-
// Best-effort login-time signing-session establishment. Runs AFTER the login session is
|
|
7647
|
-
// live (the action authorization is minted from the freshly-established id token) against
|
|
7648
|
-
// the SAME frame that sealed the code - only that page instance holds the keypair the
|
|
7649
|
-
// verificationToken is bound to. Owns the frame: disposes it whatever happens. Never
|
|
7650
|
-
// throws; a failure only means the first signature costs one inline code.
|
|
7651
|
-
async function establishSigningSessionInBackground(frame, baseUrl, signingSession) {
|
|
7652
|
-
// The moment this work begins - captured before ANY await, because verify() has already
|
|
7653
|
-
// resolved and the user can hit logout throughout the authorization mint below. The
|
|
7654
|
-
// signer drops a session whose work began at-or-before the newest logout, so a stamp
|
|
7655
|
-
// taken later (in the frame, after the mint) would let this establishment out-run a
|
|
7656
|
-
// logout and resurrect the session.
|
|
7657
|
-
const startedAtMs = Date.now();
|
|
7658
|
-
try {
|
|
7659
|
-
let idToken = null;
|
|
7660
|
-
try {
|
|
7661
|
-
idToken = core.getActiveSessionManager().getIdToken();
|
|
7662
|
-
}
|
|
7663
|
-
catch (_a) {
|
|
7664
|
-
idToken = null;
|
|
7665
|
-
}
|
|
7666
|
-
if (!idToken)
|
|
7667
|
-
return;
|
|
7668
|
-
const authorization = await requestTurnkeyActionAuthorization({
|
|
7669
|
-
baseUrl,
|
|
7670
|
-
idToken,
|
|
7671
|
-
action: 'createWallet',
|
|
7672
|
-
fetchImpl: (url, init) => fetch(url, init),
|
|
7673
|
-
});
|
|
7674
|
-
const d = await frame.request({
|
|
7675
|
-
type: 'bounded:turnkey:establishSigningSession',
|
|
7676
|
-
requestId: `tkestablish_${Date.now()}_${++requestSeq}`,
|
|
7677
|
-
verificationToken: signingSession.verificationToken,
|
|
7678
|
-
authorization,
|
|
7679
|
-
startedAtMs,
|
|
7680
|
-
}, 20000);
|
|
7681
|
-
if (!d.ok)
|
|
7682
|
-
throw new Error(String(d.error || 'establish_failed'));
|
|
7683
|
-
}
|
|
7684
|
-
catch (_b) {
|
|
7685
|
-
/* best-effort: the signer's inline code flow covers the first signature */
|
|
7686
|
-
}
|
|
7687
|
-
finally {
|
|
7688
|
-
frame.dispose();
|
|
7689
|
-
}
|
|
7690
|
-
}
|
|
7691
7503
|
/**
|
|
7692
|
-
* Begin authMode:"turnkey" login: ask Turnkey to email a one-time code to `email
|
|
7693
|
-
* Resolves with a handle whose `
|
|
7694
|
-
*
|
|
7504
|
+
* Begin authMode:"turnkey" login: ask Turnkey to email a one-time code to `email`, and open a
|
|
7505
|
+
* server-bound login ceremony. Resolves with a handle whose `run(iframe)` finishes the login inside
|
|
7506
|
+
* the Bounded OTP frame. Web-only (the sealing runs in the frame); on React Native use
|
|
7507
|
+
* loginWithRedirect() instead.
|
|
7695
7508
|
*/
|
|
7696
7509
|
async function startTurnkeyEmailLogin(email) {
|
|
7697
7510
|
if (!hasDOM$1()) {
|
|
@@ -7714,51 +7527,115 @@ async function startTurnkeyEmailLogin(email) {
|
|
|
7714
7527
|
}
|
|
7715
7528
|
const otpId = data.otpId;
|
|
7716
7529
|
const otpEncryptionTargetBundle = data.otpEncryptionTargetBundle;
|
|
7530
|
+
// A tenant-origin login always gets a ceremony id; without one the OTP frame refuses to run
|
|
7531
|
+
// (there is nothing to bind the frame key to), which is the correct fail-closed posture.
|
|
7532
|
+
const ceremonyId = typeof data.ceremonyId === 'string' ? data.ceremonyId : '';
|
|
7533
|
+
const signerOrigin = new URL(base).origin;
|
|
7717
7534
|
return {
|
|
7718
7535
|
email: trimmed,
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
throw new Error('Enter the code from your email.');
|
|
7723
|
-
// One frame per attempt: the sealing keypair lives in this exact page instance,
|
|
7724
|
-
// and a failed attempt's frame (and keypair) is discarded with it.
|
|
7725
|
-
const frame = await openHiddenSignerFrame();
|
|
7726
|
-
let vdata = null;
|
|
7727
|
-
try {
|
|
7728
|
-
const encryptedOtpBundle = await sealOtpInFrame(frame, trimmedCode, otpEncryptionTargetBundle);
|
|
7729
|
-
const vres = await fetch(`${base}/wallet/turnkey/auth/verify`, {
|
|
7730
|
-
method: 'POST',
|
|
7731
|
-
headers: { 'content-type': 'application/json' },
|
|
7732
|
-
body: JSON.stringify({ email: trimmed, appId, otpId, encryptedOtpBundle }),
|
|
7733
|
-
});
|
|
7734
|
-
vdata = await vres.json().catch(() => null);
|
|
7735
|
-
if (!vres.ok || !vdata) {
|
|
7736
|
-
throw new Error(String((vdata && vdata.error) || 'Invalid or expired code.'));
|
|
7737
|
-
}
|
|
7536
|
+
run(iframe) {
|
|
7537
|
+
if (!ceremonyId) {
|
|
7538
|
+
return Promise.reject(new Error('This app is not configured for in-frame wallet login. Please try again.'));
|
|
7738
7539
|
}
|
|
7739
|
-
|
|
7740
|
-
|
|
7741
|
-
|
|
7540
|
+
return runOtpFrame({ iframe, base, signerOrigin, appId, email: trimmed, otpId, otpEncryptionTargetBundle, ceremonyId });
|
|
7541
|
+
},
|
|
7542
|
+
};
|
|
7543
|
+
}
|
|
7544
|
+
// Point the iframe at the Bounded OTP frame and run the postMessage handshake:
|
|
7545
|
+
// frame 'ready' -> we post {otpId, targetBundle}; frame registers its key + shows the input;
|
|
7546
|
+
// frame 'result' {session} -> we establish the inline session and resolve the User.
|
|
7547
|
+
// When the frame asks for the signing-session authorization ('need-authorization') we mint a
|
|
7548
|
+
// createWallet action authorization on THIS (registered) origin and post it in - never a key.
|
|
7549
|
+
function runOtpFrame(run) {
|
|
7550
|
+
const tenantOrigin = window.location.origin;
|
|
7551
|
+
const url = `${run.base.replace(/\/$/, '')}/wallet/turnkey/otp-frame`
|
|
7552
|
+
+ `?appId=${encodeURIComponent(run.appId)}`
|
|
7553
|
+
+ `&origin=${encodeURIComponent(tenantOrigin)}`
|
|
7554
|
+
+ `&email=${encodeURIComponent(run.email)}`
|
|
7555
|
+
+ `&ceremonyId=${encodeURIComponent(run.ceremonyId)}`;
|
|
7556
|
+
return new Promise((resolve, reject) => {
|
|
7557
|
+
let settled = false;
|
|
7558
|
+
let started = false;
|
|
7559
|
+
const cleanup = () => { settled = true; window.removeEventListener('message', onMsg); window.clearTimeout(timer); };
|
|
7560
|
+
const onMsg = async (event) => {
|
|
7561
|
+
if (settled)
|
|
7562
|
+
return;
|
|
7563
|
+
// Trust ONLY the signer origin and the frame's own window.
|
|
7564
|
+
if (event.origin !== run.signerOrigin || event.source !== run.iframe.contentWindow)
|
|
7565
|
+
return;
|
|
7566
|
+
const d = event.data;
|
|
7567
|
+
if (!d || typeof d !== 'object')
|
|
7568
|
+
return;
|
|
7569
|
+
if (d.type === 'bounded:otp:ready') {
|
|
7570
|
+
if (started)
|
|
7571
|
+
return;
|
|
7572
|
+
started = true;
|
|
7573
|
+
post({ type: 'bounded:otp:start', otpId: run.otpId, otpEncryptionTargetBundle: run.otpEncryptionTargetBundle });
|
|
7574
|
+
return;
|
|
7742
7575
|
}
|
|
7743
|
-
|
|
7744
|
-
|
|
7745
|
-
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
|
|
7751
|
-
|
|
7752
|
-
|
|
7753
|
-
|
|
7754
|
-
|
|
7576
|
+
if (d.type === 'bounded:otp:need-authorization') {
|
|
7577
|
+
// Best-effort: mint the createWallet authorization on our own registered origin and hand it
|
|
7578
|
+
// to the frame so it can complete the 24h signing session with the key it holds. Never throws.
|
|
7579
|
+
try {
|
|
7580
|
+
let idToken = null;
|
|
7581
|
+
try {
|
|
7582
|
+
idToken = core.getActiveSessionManager().getIdToken();
|
|
7583
|
+
}
|
|
7584
|
+
catch (_a) {
|
|
7585
|
+
idToken = null;
|
|
7586
|
+
}
|
|
7587
|
+
if (!idToken) {
|
|
7588
|
+
post({ type: 'bounded:otp:authorization', authorization: '' });
|
|
7589
|
+
return;
|
|
7590
|
+
}
|
|
7591
|
+
const authorization = await requestTurnkeyActionAuthorization({
|
|
7592
|
+
baseUrl: run.base, idToken, action: 'createWallet', fetchImpl: (u, i) => fetch(u, i),
|
|
7593
|
+
});
|
|
7594
|
+
post({ type: 'bounded:otp:authorization', authorization });
|
|
7595
|
+
}
|
|
7596
|
+
catch (_b) {
|
|
7597
|
+
post({ type: 'bounded:otp:authorization', authorization: '' });
|
|
7598
|
+
}
|
|
7599
|
+
return;
|
|
7755
7600
|
}
|
|
7756
|
-
|
|
7757
|
-
|
|
7601
|
+
if (d.type === 'bounded:otp:result') {
|
|
7602
|
+
if (d.ok && d.session && typeof d.session === 'object') {
|
|
7603
|
+
try {
|
|
7604
|
+
const user = await establishInlineSession(d.session);
|
|
7605
|
+
cleanup();
|
|
7606
|
+
resolve(user);
|
|
7607
|
+
}
|
|
7608
|
+
catch (error) {
|
|
7609
|
+
cleanup();
|
|
7610
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
7611
|
+
}
|
|
7612
|
+
return;
|
|
7613
|
+
}
|
|
7614
|
+
cleanup();
|
|
7615
|
+
reject(new Error(String((d && d.error) || 'Invalid or expired code.')));
|
|
7616
|
+
return;
|
|
7758
7617
|
}
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7618
|
+
// 'bounded:otp:resize' / 'bounded:otp:registered' are advisory; the widget host may size the
|
|
7619
|
+
// frame from resize, but login does not depend on them.
|
|
7620
|
+
};
|
|
7621
|
+
const post = (msg) => {
|
|
7622
|
+
var _a;
|
|
7623
|
+
try {
|
|
7624
|
+
(_a = run.iframe.contentWindow) === null || _a === void 0 ? void 0 : _a.postMessage(msg, run.signerOrigin);
|
|
7625
|
+
}
|
|
7626
|
+
catch ( /* frame gone */_b) { /* frame gone */ }
|
|
7627
|
+
};
|
|
7628
|
+
window.addEventListener('message', onMsg);
|
|
7629
|
+
// 3 minutes: the human must read the email and type the code. A frame that never reports is a
|
|
7630
|
+
// failed login the user can retry.
|
|
7631
|
+
const timer = window.setTimeout(() => {
|
|
7632
|
+
if (settled)
|
|
7633
|
+
return;
|
|
7634
|
+
cleanup();
|
|
7635
|
+
reject(new Error('The sign-in code entry timed out. Please try again.'));
|
|
7636
|
+
}, 3 * 60 * 1000);
|
|
7637
|
+
run.iframe.src = url;
|
|
7638
|
+
});
|
|
7762
7639
|
}
|
|
7763
7640
|
|
|
7764
7641
|
// bounded-login-widget.ts - the UNIFIED Bounded login surface: one in-app card
|
|
@@ -7983,7 +7860,7 @@ function untilCancelled(work, pending) {
|
|
|
7983
7860
|
* run first; call from a user gesture so social/wallet popups are allowed.
|
|
7984
7861
|
*/
|
|
7985
7862
|
async function openBoundedWidget(opts = {}) {
|
|
7986
|
-
if (!hasDOM$
|
|
7863
|
+
if (!hasDOM$2()) {
|
|
7987
7864
|
throw new Error("openBoundedWidget is web-only - on React Native use loginWithRedirect()");
|
|
7988
7865
|
}
|
|
7989
7866
|
// Registered before the first await - see PendingOpen.
|
|
@@ -8286,7 +8163,7 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8286
8163
|
setBusy(submit, "Sending code\u2026");
|
|
8287
8164
|
startTurnkeyEmailLogin(input.value)
|
|
8288
8165
|
.then((handle) => { if (settleAttempt(attempt))
|
|
8289
|
-
swap(
|
|
8166
|
+
swap(renderOtpFrame(handle)); })
|
|
8290
8167
|
.catch((err) => {
|
|
8291
8168
|
if (!settleAttempt(attempt))
|
|
8292
8169
|
return;
|
|
@@ -8570,75 +8447,27 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8570
8447
|
card.appendChild(status);
|
|
8571
8448
|
return card;
|
|
8572
8449
|
};
|
|
8573
|
-
// authMode:"turnkey"
|
|
8574
|
-
//
|
|
8575
|
-
|
|
8450
|
+
// BSA-2026-004: authMode:"turnkey" code entry runs ENTIRELY inside a Bounded-origin OTP frame,
|
|
8451
|
+
// never in this tenant DOM. The widget only embeds that frame and reacts to its result - it
|
|
8452
|
+
// never sees the plaintext code or the sealing key. The editorial card wraps the frame so the
|
|
8453
|
+
// experience still feels native; the code input, sealing, and verify all live behind the frame.
|
|
8454
|
+
const renderOtpFrame = (handle) => {
|
|
8576
8455
|
const card = el("div", "c");
|
|
8577
8456
|
card.appendChild(style.cloneNode(true));
|
|
8578
8457
|
const back = el("button", "back", "\u2190 Back");
|
|
8579
8458
|
back.type = "button";
|
|
8580
|
-
// Abandoning the code entry settles the email attempt that opened it, so
|
|
8581
|
-
// the next lane the user picks is not refused by a claim nobody owns.
|
|
8582
|
-
back.addEventListener("click", () => { invalidateAttempts(); swap(renderChoose()); });
|
|
8583
8459
|
card.appendChild(back);
|
|
8584
8460
|
card.appendChild(el("h1", undefined, "Enter your code"));
|
|
8585
8461
|
card.appendChild(el("p", "sub", `We sent a 6-digit code to ${handle.email}.`));
|
|
8586
8462
|
const status = el("div", "status");
|
|
8587
8463
|
const setErr = (msg) => { status.textContent = msg; status.classList.add("err"); };
|
|
8588
|
-
|
|
8589
|
-
|
|
8590
|
-
|
|
8591
|
-
|
|
8592
|
-
|
|
8593
|
-
|
|
8594
|
-
|
|
8595
|
-
input.inputMode = "numeric";
|
|
8596
|
-
input.autocomplete = "one-time-code";
|
|
8597
|
-
input.maxLength = 6;
|
|
8598
|
-
input.placeholder = "000000";
|
|
8599
|
-
input.required = true;
|
|
8600
|
-
const submit = el("button", "btn primary");
|
|
8601
|
-
submit.type = "submit";
|
|
8602
|
-
submit.textContent = "Verify";
|
|
8603
|
-
form.append(label, input, submit);
|
|
8604
|
-
const verify = () => {
|
|
8605
|
-
const code = input.value.replace(/\D+/g, "");
|
|
8606
|
-
if (code.length < 6) {
|
|
8607
|
-
setErr("Enter the 6-digit code.");
|
|
8608
|
-
return;
|
|
8609
|
-
}
|
|
8610
|
-
// Verifying MINTS a session, so it takes the lock like every other lane.
|
|
8611
|
-
const attempt = beginAttempt();
|
|
8612
|
-
if (attempt === null) {
|
|
8613
|
-
setErr(BUSY_MSG);
|
|
8614
|
-
return;
|
|
8615
|
-
}
|
|
8616
|
-
status.classList.remove("err");
|
|
8617
|
-
status.textContent = "";
|
|
8618
|
-
submit.disabled = true;
|
|
8619
|
-
input.disabled = true;
|
|
8620
|
-
submit.replaceChildren(el("span", "spin"), document.createTextNode("Verifying\u2026"));
|
|
8621
|
-
handle
|
|
8622
|
-
.verify(code)
|
|
8623
|
-
.then((u) => { settleAttempt(attempt); succeed(u); })
|
|
8624
|
-
.catch((err) => {
|
|
8625
|
-
if (!settleAttempt(attempt))
|
|
8626
|
-
return;
|
|
8627
|
-
submit.disabled = false;
|
|
8628
|
-
input.disabled = false;
|
|
8629
|
-
submit.textContent = "Verify";
|
|
8630
|
-
input.value = "";
|
|
8631
|
-
input.focus();
|
|
8632
|
-
setErr(errText(err));
|
|
8633
|
-
});
|
|
8634
|
-
};
|
|
8635
|
-
form.addEventListener("submit", (e) => { e.preventDefault(); verify(); });
|
|
8636
|
-
// Auto-submit once six digits are in - the buttery one-shot paste/type path.
|
|
8637
|
-
input.addEventListener("input", () => {
|
|
8638
|
-
if (input.value.replace(/\D+/g, "").length === 6 && !submit.disabled)
|
|
8639
|
-
verify();
|
|
8640
|
-
});
|
|
8641
|
-
card.appendChild(form);
|
|
8464
|
+
// The Bounded-origin OTP frame. Its route restricts frame-ancestors to this exact origin, so
|
|
8465
|
+
// only this app may embed it; the code + the P-256 key never leave it into this realm. A fixed
|
|
8466
|
+
// height comfortably fits the frame's code card (heading + input + button + status).
|
|
8467
|
+
const frame = document.createElement("iframe");
|
|
8468
|
+
frame.setAttribute("title", "Enter your code");
|
|
8469
|
+
frame.style.cssText = "width:100%;height:210px;border:0;background:transparent;color-scheme:dark";
|
|
8470
|
+
card.appendChild(frame);
|
|
8642
8471
|
card.appendChild(status);
|
|
8643
8472
|
const foot = el("div", "foot");
|
|
8644
8473
|
const poweredBy = el("a", undefined, "Powered by Bounded");
|
|
@@ -8647,7 +8476,22 @@ async function prepareAndOpen(opts, pending) {
|
|
|
8647
8476
|
poweredBy.rel = "noopener";
|
|
8648
8477
|
foot.appendChild(poweredBy);
|
|
8649
8478
|
card.appendChild(foot);
|
|
8650
|
-
|
|
8479
|
+
// Running the frame MINTS a session, so it takes the lock like every other lane. Abandoning
|
|
8480
|
+
// via Back settles it so the next lane the user picks is not refused by a claim nobody owns.
|
|
8481
|
+
const attempt = beginAttempt();
|
|
8482
|
+
if (attempt === null) {
|
|
8483
|
+
setErr(BUSY_MSG);
|
|
8484
|
+
return card;
|
|
8485
|
+
}
|
|
8486
|
+
back.addEventListener("click", () => { settleAttempt(attempt); invalidateAttempts(); swap(renderChoose()); });
|
|
8487
|
+
handle
|
|
8488
|
+
.run(frame)
|
|
8489
|
+
.then((u) => { settleAttempt(attempt); succeed(u); })
|
|
8490
|
+
.catch((err) => {
|
|
8491
|
+
if (!settleAttempt(attempt))
|
|
8492
|
+
return;
|
|
8493
|
+
setErr(errText(err));
|
|
8494
|
+
});
|
|
8651
8495
|
return card;
|
|
8652
8496
|
};
|
|
8653
8497
|
const swap = (node) => {
|
|
@@ -8858,6 +8702,24 @@ var __rest = (undefined && undefined.__rest) || function (s, e) {
|
|
|
8858
8702
|
}
|
|
8859
8703
|
return t;
|
|
8860
8704
|
};
|
|
8705
|
+
/** Source is a safe tar.gz tree; attachments remain model input, never executable files. */
|
|
8706
|
+
async function uploadInput(appId, options) {
|
|
8707
|
+
const data = options.data instanceof Uint8Array ? new Uint8Array(options.data).buffer : await options.data.arrayBuffer();
|
|
8708
|
+
const sha256 = Array.from(new Uint8Array(await getPlatform().sha256(new Uint8Array(data))), (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
8709
|
+
const path = `/app/${encodeURIComponent(appId)}/build-inputs`;
|
|
8710
|
+
const ticket = await requestDevApi(path, {
|
|
8711
|
+
method: 'POST', body: { kind: options.kind, name: options.name, contentType: options.contentType, bytes: data.byteLength, sha256 },
|
|
8712
|
+
});
|
|
8713
|
+
const destination = new URL(ticket.uploadUrl);
|
|
8714
|
+
if (destination.protocol !== 'https:' && !(destination.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(destination.hostname)))
|
|
8715
|
+
throw new Error('Build input upload requires a secure endpoint.');
|
|
8716
|
+
const response = await fetch(destination.href, {
|
|
8717
|
+
method: 'PUT', redirect: 'error', headers: { Authorization: `Bearer ${ticket.uploadToken}`, 'Content-Type': options.contentType }, body: data,
|
|
8718
|
+
});
|
|
8719
|
+
if (!response.ok)
|
|
8720
|
+
throw apiError(response, await responseBody(response));
|
|
8721
|
+
return requestDevApi(`${path}/${encodeURIComponent(ticket.artifactId)}/finalize`, { method: 'POST' });
|
|
8722
|
+
}
|
|
8861
8723
|
/** A structured non-2xx response from the developer API. */
|
|
8862
8724
|
class BuildsApiError extends Error {
|
|
8863
8725
|
constructor(init) {
|
|
@@ -9463,6 +9325,7 @@ const builds = {
|
|
|
9463
9325
|
decideGate,
|
|
9464
9326
|
refreshReviewCapability,
|
|
9465
9327
|
topup,
|
|
9328
|
+
uploadInput,
|
|
9466
9329
|
list: list$1,
|
|
9467
9330
|
watch,
|
|
9468
9331
|
};
|
|
@@ -10335,4 +10198,4 @@ exports.useAuth = useAuth;
|
|
|
10335
10198
|
exports.useBuildRun = useBuildRun;
|
|
10336
10199
|
exports.useQuery = useQuery;
|
|
10337
10200
|
exports.waitForAuthReady = waitForAuthReady;
|
|
10338
|
-
//# sourceMappingURL=index-
|
|
10201
|
+
//# sourceMappingURL=index-Bb7I-AoO.js.map
|