@bounded-sh/client 0.0.62 → 0.0.64
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/providers/email-auth-provider.d.ts +4 -4
- package/dist/auth/turnkey-signer-bridge.d.ts +5 -0
- package/dist/{email-auth-provider-BxV1zZA_.js → email-auth-provider-97CluDlZ.js} +27 -30
- package/dist/email-auth-provider-97CluDlZ.js.map +1 -0
- package/dist/{email-auth-provider-BttT2sZK.js → email-auth-provider-CKM4O5Z1.js} +27 -30
- package/dist/email-auth-provider-CKM4O5Z1.js.map +1 -0
- package/dist/{email-auth-provider-vWv0rnbd.mjs → email-auth-provider-h0fPnU76.mjs} +27 -30
- package/dist/email-auth-provider-h0fPnU76.mjs.map +1 -0
- package/dist/{email-auth-provider-Cb4VKA1E.mjs → email-auth-provider-v9xvY0Ky.mjs} +27 -30
- package/dist/email-auth-provider-v9xvY0Ky.mjs.map +1 -0
- package/dist/{guest-auth-provider-CeIZw5x6.mjs → guest-auth-provider-BLRbjAv2.mjs} +2 -2
- package/dist/{guest-auth-provider-CeIZw5x6.mjs.map → guest-auth-provider-BLRbjAv2.mjs.map} +1 -1
- package/dist/{guest-auth-provider-peD5Zasl.js → guest-auth-provider-BvIf5W5c.js} +2 -2
- package/dist/{guest-auth-provider-peD5Zasl.js.map → guest-auth-provider-BvIf5W5c.js.map} +1 -1
- package/dist/{guest-auth-provider-BcR-MQ0g.js → guest-auth-provider-CLnFuodt.js} +2 -2
- package/dist/{guest-auth-provider-BcR-MQ0g.js.map → guest-auth-provider-CLnFuodt.js.map} +1 -1
- package/dist/{guest-auth-provider-BBPpydtM.mjs → guest-auth-provider-Dmup2WIL.mjs} +2 -2
- package/dist/{guest-auth-provider-BBPpydtM.mjs.map → guest-auth-provider-Dmup2WIL.mjs.map} +1 -1
- package/dist/{index-DpJl_Cpa.mjs → index-7yz-Olrm.mjs} +1366 -1519
- package/dist/index-7yz-Olrm.mjs.map +1 -0
- package/dist/{index-DEDYfO2p.js → index-Bk0nEjB6.js} +1368 -1522
- package/dist/index-Bk0nEjB6.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{index.native-BIUs8t7F.mjs → index.native-CWcQMPZd.mjs} +518 -221
- package/dist/index.native-CWcQMPZd.mjs.map +1 -0
- package/dist/{index.native-BSheiGj2.js → index.native-Do-VhPUG.js} +520 -224
- package/dist/index.native-Do-VhPUG.js.map +1 -0
- package/dist/index.native.js +1 -1
- package/dist/index.native.mjs +1 -1
- package/dist/{injected-evm-wallet-provider-B-9VXQcH.js → injected-evm-wallet-provider-B5MA0w8I.js} +2 -2
- package/dist/{injected-evm-wallet-provider-B-9VXQcH.js.map → injected-evm-wallet-provider-B5MA0w8I.js.map} +1 -1
- package/dist/{injected-evm-wallet-provider-7XawIUdJ.js → injected-evm-wallet-provider-BMiT8I-4.js} +2 -2
- package/dist/{injected-evm-wallet-provider-7XawIUdJ.js.map → injected-evm-wallet-provider-BMiT8I-4.js.map} +1 -1
- package/dist/{injected-evm-wallet-provider-BVsxOzYr.mjs → injected-evm-wallet-provider-DlPsOmik.mjs} +2 -2
- package/dist/{injected-evm-wallet-provider-BVsxOzYr.mjs.map → injected-evm-wallet-provider-DlPsOmik.mjs.map} +1 -1
- package/dist/{injected-evm-wallet-provider-DnOrVMDX.mjs → injected-evm-wallet-provider-xvZq9Zli.mjs} +2 -2
- package/dist/{injected-evm-wallet-provider-DnOrVMDX.mjs.map → injected-evm-wallet-provider-xvZq9Zli.mjs.map} +1 -1
- package/dist/{injected-wallet-provider-D574oPzH.js → injected-wallet-provider-CSj0lY9H.js} +2 -2
- package/dist/{injected-wallet-provider-D574oPzH.js.map → injected-wallet-provider-CSj0lY9H.js.map} +1 -1
- package/dist/{injected-wallet-provider-BMDJntMD.js → injected-wallet-provider-CoE5XQQn.js} +2 -2
- package/dist/{injected-wallet-provider-BMDJntMD.js.map → injected-wallet-provider-CoE5XQQn.js.map} +1 -1
- package/dist/{injected-wallet-provider-ZkPzVj5W.mjs → injected-wallet-provider-Dd7ij6bc.mjs} +2 -2
- package/dist/{injected-wallet-provider-ZkPzVj5W.mjs.map → injected-wallet-provider-Dd7ij6bc.mjs.map} +1 -1
- package/dist/{injected-wallet-provider-Bqk33sir.mjs → injected-wallet-provider-YHhhFzsj.mjs} +2 -2
- package/dist/{injected-wallet-provider-Bqk33sir.mjs.map → injected-wallet-provider-YHhhFzsj.mjs.map} +1 -1
- package/package.json +2 -2
- package/dist/auth/crossmint-signer-bridge.d.ts +0 -45
- package/dist/email-auth-provider-BttT2sZK.js.map +0 -1
- package/dist/email-auth-provider-BxV1zZA_.js.map +0 -1
- package/dist/email-auth-provider-Cb4VKA1E.mjs.map +0 -1
- package/dist/email-auth-provider-vWv0rnbd.mjs.map +0 -1
- package/dist/index-DEDYfO2p.js.map +0 -1
- package/dist/index-DpJl_Cpa.mjs.map +0 -1
- package/dist/index.native-BIUs8t7F.mjs.map +0 -1
- package/dist/index.native-BSheiGj2.js.map +0 -1
|
@@ -868,6 +868,8 @@ function getDefaultExportFromCjs (x) {
|
|
|
868
868
|
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
|
|
869
869
|
}
|
|
870
870
|
|
|
871
|
+
var safeBuffer = {exports: {}};
|
|
872
|
+
|
|
871
873
|
var buffer = {};
|
|
872
874
|
|
|
873
875
|
var base64Js = {};
|
|
@@ -3237,10 +3239,6 @@ function requireBuffer () {
|
|
|
3237
3239
|
return buffer;
|
|
3238
3240
|
}
|
|
3239
3241
|
|
|
3240
|
-
var bufferExports = requireBuffer();
|
|
3241
|
-
|
|
3242
|
-
var safeBuffer = {exports: {}};
|
|
3243
|
-
|
|
3244
3242
|
/*! safe-buffer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
|
|
3245
3243
|
|
|
3246
3244
|
var hasRequiredSafeBuffer;
|
|
@@ -3454,327 +3452,725 @@ var basex = /*@__PURE__*/getDefaultExportFromCjs(srcExports);
|
|
|
3454
3452
|
var ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
3455
3453
|
var bs58 = basex(ALPHABET);
|
|
3456
3454
|
|
|
3457
|
-
|
|
3458
|
-
|
|
3455
|
+
var bufferExports = requireBuffer();
|
|
3456
|
+
|
|
3457
|
+
// bounded-widget-modal.ts - the shared in-app WIDGET shell for Bounded's login
|
|
3458
|
+
// and signing surfaces. One Shadow-DOM overlay + panel that matches the Bounded
|
|
3459
|
+
// widget (Seal) look and MOTION: a backdrop fade, a panel rise on desktop, a
|
|
3460
|
+
// bottom-sheet on mobile, a height tween as content swaps, and a clean close.
|
|
3459
3461
|
//
|
|
3460
|
-
//
|
|
3461
|
-
//
|
|
3462
|
-
//
|
|
3463
|
-
//
|
|
3464
|
-
// ONLY on the Bounded signer page (one whitelisted origin) and talking to it via
|
|
3465
|
-
// postMessage.
|
|
3462
|
+
// It hosts EITHER:
|
|
3463
|
+
// - NATIVE content (the login card the SDK renders itself), or
|
|
3464
|
+
// - an IFRAME (the Bounded-owned signer page, which must run on the single
|
|
3465
|
+
// signer origin because a WebAuthn passkey is bound to one origin).
|
|
3466
3466
|
//
|
|
3467
|
-
//
|
|
3468
|
-
//
|
|
3469
|
-
//
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
const EMBEDDED_WALLET_UNSUPPORTED = 'Unsupported for embedded smart wallets — Crossmint Solana smart wallets sign ' +
|
|
3473
|
-
'and submit atomically. Use signAndSubmitTransaction() (or bounded.sendWalletToken) ' +
|
|
3474
|
-
'instead of signMessage/signTransaction.';
|
|
3475
|
-
const NO_EMBEDDED_WALLET = 'This login has no embedded wallet. Enable `"auth": { "wallets": true }` in ' +
|
|
3476
|
-
'the app policy so email logins get a non-custodial wallet (@user.address), ' +
|
|
3477
|
-
'then sign from a user gesture.';
|
|
3478
|
-
function hasDOM$5() {
|
|
3479
|
-
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
3467
|
+
// The modal is a warm singleton: it stays mounted (hidden) between uses so a
|
|
3468
|
+
// second open - or a signature right after login - is instant and, when it is
|
|
3469
|
+
// already open, a clean in-place view swap rather than a reopen.
|
|
3470
|
+
function hasDOM$4() {
|
|
3471
|
+
return typeof window !== "undefined" && typeof document !== "undefined";
|
|
3480
3472
|
}
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3493
|
-
|
|
3473
|
+
const OVERLAY_CSS = `
|
|
3474
|
+
:host{all:initial}
|
|
3475
|
+
*{box-sizing:border-box}
|
|
3476
|
+
.bw-ov{position:fixed;inset:0;z-index:2147483647;display:none;
|
|
3477
|
+
font-family:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
|
|
3478
|
+
.bw-ov.mounted{display:block}
|
|
3479
|
+
.bw-back{position:fixed;inset:0;background:rgba(4,4,5,.62);opacity:0;
|
|
3480
|
+
transition:opacity .24s ease;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}
|
|
3481
|
+
.bw-wrap{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:20px}
|
|
3482
|
+
.bw-panel{position:relative;width:420px;max-width:calc(100vw - 28px);height:280px;
|
|
3483
|
+
border-radius:14px;overflow:hidden;background:#0e0f11;border:1px solid rgba(232,230,222,.08);
|
|
3484
|
+
box-shadow:0 1px 0 rgba(232,230,222,.03) inset,0 40px 90px -45px rgba(0,0,0,.85);
|
|
3485
|
+
opacity:0;transform:translateY(14px) scale(.98);will-change:transform,opacity,height;
|
|
3486
|
+
transition:opacity .28s cubic-bezier(.2,.8,.2,1),transform .28s cubic-bezier(.2,.8,.2,1),
|
|
3487
|
+
height .3s cubic-bezier(.2,.8,.2,1)}
|
|
3488
|
+
.bw-ov.show .bw-back{opacity:1}
|
|
3489
|
+
.bw-ov.show .bw-panel{opacity:1;transform:none}
|
|
3490
|
+
.bw-ov.closing .bw-back{opacity:0}
|
|
3491
|
+
.bw-ov.closing .bw-panel{opacity:0;transform:translateY(12px) scale(.99)}
|
|
3492
|
+
.bw-content{position:absolute;inset:0;overflow:hidden}
|
|
3493
|
+
.bw-frame{display:block;width:100%;height:100%;border:0;background:transparent;color-scheme:dark}
|
|
3494
|
+
/* editorial corner crop-marks, matching the reference frame */
|
|
3495
|
+
.bw-mk{position:absolute;width:13px;height:13px;pointer-events:none;z-index:2;
|
|
3496
|
+
border-color:rgba(232,230,222,.22);border-style:solid;border-width:0}
|
|
3497
|
+
.bw-mk.tl{top:11px;left:11px;border-top-width:1px;border-left-width:1px}
|
|
3498
|
+
.bw-mk.tr{top:11px;right:11px;border-top-width:1px;border-right-width:1px}
|
|
3499
|
+
.bw-mk.bl{bottom:11px;left:11px;border-bottom-width:1px;border-left-width:1px}
|
|
3500
|
+
.bw-mk.br{bottom:11px;right:11px;border-bottom-width:1px;border-right-width:1px}
|
|
3501
|
+
@media(max-width:560px){
|
|
3502
|
+
.bw-wrap{align-items:flex-end;padding:0}
|
|
3503
|
+
.bw-panel{width:100%;max-width:100%;border-radius:18px 18px 0 0;border-bottom:0}
|
|
3504
|
+
.bw-content{overflow-y:auto}
|
|
3505
|
+
.bw-ov.show .bw-panel{transform:none}
|
|
3506
|
+
.bw-ov:not(.show) .bw-panel,.bw-ov.closing .bw-panel{transform:translateY(18%)}
|
|
3494
3507
|
}
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
function
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
|
|
3508
|
+
@media(prefers-reduced-motion:reduce){.bw-back,.bw-panel{transition:opacity .12s linear!important}}
|
|
3509
|
+
`;
|
|
3510
|
+
let modal = null;
|
|
3511
|
+
function getModal() {
|
|
3512
|
+
if (modal)
|
|
3513
|
+
return modal;
|
|
3514
|
+
const root = document.createElement("div");
|
|
3515
|
+
root.setAttribute("data-bounded-widget", "");
|
|
3516
|
+
const shadow = root.attachShadow({ mode: "open" });
|
|
3517
|
+
const style = document.createElement("style");
|
|
3518
|
+
style.textContent = OVERLAY_CSS;
|
|
3519
|
+
shadow.appendChild(style);
|
|
3520
|
+
const overlay = document.createElement("div");
|
|
3521
|
+
overlay.className = "bw-ov";
|
|
3522
|
+
const back = document.createElement("div");
|
|
3523
|
+
back.className = "bw-back";
|
|
3524
|
+
const wrap = document.createElement("div");
|
|
3525
|
+
wrap.className = "bw-wrap";
|
|
3526
|
+
const panel = document.createElement("div");
|
|
3527
|
+
panel.className = "bw-panel";
|
|
3528
|
+
const content = document.createElement("div");
|
|
3529
|
+
content.className = "bw-content";
|
|
3530
|
+
panel.appendChild(content);
|
|
3531
|
+
for (const corner of ["tl", "tr", "bl", "br"]) {
|
|
3532
|
+
const mk = document.createElement("span");
|
|
3533
|
+
mk.className = "bw-mk " + corner;
|
|
3534
|
+
panel.appendChild(mk);
|
|
3505
3535
|
}
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
const
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
const cfg = await core.getConfig();
|
|
3524
|
-
const base = (cfg.humanAuthApiUrl || 'https://auth.bounded.sh').replace(/\/$/, '');
|
|
3525
|
-
return { url: `${base}/wallet/signer`, origin: new URL(base).origin };
|
|
3526
|
-
}
|
|
3527
|
-
let requestSeq$1 = 0;
|
|
3528
|
-
/**
|
|
3529
|
-
* Open the Bounded signer popup, send `action` to it, and resolve with the
|
|
3530
|
-
* submitted transaction result. Must be called from a USER GESTURE (click/tap) or
|
|
3531
|
-
* the browser will block the popup — in which case this rejects with a clear
|
|
3532
|
-
* "Popup blocked" error telling the app to call from a user gesture.
|
|
3533
|
-
*
|
|
3534
|
-
* Non-custodial: the popup gates signing behind an email OTP delivered to the
|
|
3535
|
-
* wallet's owner, and validates that the wallet address matches the account.
|
|
3536
|
-
*/
|
|
3537
|
-
async function signViaSignerPopup(action) {
|
|
3538
|
-
if (!hasDOM$5()) {
|
|
3539
|
-
throw new Error('Embedded-wallet signing is only available in the browser (popup-based).');
|
|
3540
|
-
}
|
|
3541
|
-
const isEvm = action.kind === 'evmTransaction';
|
|
3542
|
-
const claims = walletClaimsFromSession(isEvm ? 'evm' : 'solana');
|
|
3543
|
-
if (!claims)
|
|
3544
|
-
throw new Error(NO_EMBEDDED_WALLET);
|
|
3545
|
-
const { url, origin: signerOrigin } = await signerPageUrl();
|
|
3546
|
-
const requestId = `bsign_${Date.now()}_${++requestSeq$1}`;
|
|
3547
|
-
// Centered popup, sized for the OTP form (matches the OIDC login popup shape).
|
|
3548
|
-
const w = 420, h = 600;
|
|
3549
|
-
const left = window.screenX + Math.max(0, (window.outerWidth - w) / 2);
|
|
3550
|
-
const top = window.screenY + Math.max(0, (window.outerHeight - h) / 2);
|
|
3551
|
-
const popup = window.open(url, 'bounded-wallet-signer', `width=${w},height=${h},left=${left},top=${top}`);
|
|
3552
|
-
if (!popup) {
|
|
3553
|
-
throw new Error('Popup blocked — call signAndSubmitTransaction() from a user gesture (e.g. a click handler) so the wallet approval window can open.');
|
|
3554
|
-
}
|
|
3555
|
-
const request = {
|
|
3556
|
-
type: 'bounded:sign',
|
|
3557
|
-
requestId,
|
|
3558
|
-
environment: claims.environment, // may be undefined → page auto-detects
|
|
3559
|
-
email: claims.email,
|
|
3560
|
-
address: isEvm ? claims.evmAddress : claims.address,
|
|
3561
|
-
action,
|
|
3536
|
+
wrap.appendChild(panel);
|
|
3537
|
+
overlay.appendChild(back);
|
|
3538
|
+
overlay.appendChild(wrap);
|
|
3539
|
+
shadow.appendChild(overlay);
|
|
3540
|
+
(document.body || document.documentElement).appendChild(root);
|
|
3541
|
+
const m = {
|
|
3542
|
+
root,
|
|
3543
|
+
overlay,
|
|
3544
|
+
panel,
|
|
3545
|
+
content,
|
|
3546
|
+
iframe: null,
|
|
3547
|
+
iframeOrigin: "",
|
|
3548
|
+
iframeReady: false,
|
|
3549
|
+
onBackdrop: null,
|
|
3550
|
+
onIframeMessage: null,
|
|
3551
|
+
chain: Promise.resolve(),
|
|
3552
|
+
fitObserver: null,
|
|
3562
3553
|
};
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
if (ev.origin !== signerOrigin)
|
|
3584
|
-
return; // only trust the signer page origin
|
|
3585
|
-
if (ev.source !== popup)
|
|
3586
|
-
return; // only trust this signer popup window
|
|
3587
|
-
const d = ev.data;
|
|
3588
|
-
if (!d || typeof d !== 'object')
|
|
3589
|
-
return;
|
|
3590
|
-
if (d.type === 'bounded:signer:ready') {
|
|
3591
|
-
sendRequest();
|
|
3592
|
-
return;
|
|
3593
|
-
}
|
|
3594
|
-
if (d.type === 'bounded:signer:result' && d.requestId === requestId) {
|
|
3595
|
-
cleanup();
|
|
3596
|
-
try {
|
|
3597
|
-
popup.close();
|
|
3598
|
-
}
|
|
3599
|
-
catch ( /* ignore */_a) { /* ignore */ }
|
|
3600
|
-
if (d.ok)
|
|
3601
|
-
resolve({ hash: d.hash, explorerLink: d.explorerLink, transactionId: d.transactionId });
|
|
3602
|
-
else
|
|
3603
|
-
reject(new Error(String(d.error || 'Wallet signing failed')));
|
|
3604
|
-
}
|
|
3605
|
-
};
|
|
3606
|
-
window.addEventListener('message', onMsg);
|
|
3607
|
-
// Fallback: if the ready ping was missed (fast load / focus race), push the
|
|
3608
|
-
// request shortly after opening. The page ignores it until a bounded:sign
|
|
3609
|
-
// arrives and only acts on the first one, so a duplicate is harmless.
|
|
3610
|
-
setTimeout(sendRequest, 1200);
|
|
3611
|
-
// If the user closes the popup before completing, reject (unless a result
|
|
3612
|
-
// just arrived — give any in-flight message a beat to settle first).
|
|
3613
|
-
closeTimer = setInterval(() => {
|
|
3614
|
-
if (popup.closed) {
|
|
3615
|
-
clearInterval(closeTimer);
|
|
3616
|
-
setTimeout(() => { if (!settled) {
|
|
3617
|
-
cleanup();
|
|
3618
|
-
reject(new Error('Wallet approval window was closed before signing completed.'));
|
|
3619
|
-
} }, 400);
|
|
3620
|
-
}
|
|
3621
|
-
}, 500);
|
|
3622
|
-
// Overall safety timeout (OTP + signing). Generous — the user may take a
|
|
3623
|
-
// while to fetch the code from their email.
|
|
3624
|
-
setTimeout(() => { if (!settled) {
|
|
3625
|
-
cleanup();
|
|
3626
|
-
try {
|
|
3627
|
-
popup.close();
|
|
3628
|
-
}
|
|
3629
|
-
catch ( /* ignore */_a) { /* ignore */ }
|
|
3630
|
-
reject(new Error('Wallet approval timed out.'));
|
|
3631
|
-
} }, 5 * 60 * 1000);
|
|
3554
|
+
// The dismiss handler lives on `.bw-wrap`: it is the full-viewport flex layer
|
|
3555
|
+
// that sits ABOVE `.bw-back`, so it (not the backdrop element) receives clicks
|
|
3556
|
+
// outside the panel. Fire only when the click lands on the wrap itself, never
|
|
3557
|
+
// when it bubbles up from the panel or its content.
|
|
3558
|
+
wrap.addEventListener("click", (event) => {
|
|
3559
|
+
if (event.target === wrap && m.onBackdrop)
|
|
3560
|
+
m.onBackdrop();
|
|
3561
|
+
});
|
|
3562
|
+
window.addEventListener("message", (event) => {
|
|
3563
|
+
if (!m.iframe)
|
|
3564
|
+
return;
|
|
3565
|
+
if (event.origin !== m.iframeOrigin)
|
|
3566
|
+
return;
|
|
3567
|
+
if (event.source !== m.iframe.contentWindow)
|
|
3568
|
+
return;
|
|
3569
|
+
const d = event.data;
|
|
3570
|
+
if (!d || typeof d !== "object")
|
|
3571
|
+
return;
|
|
3572
|
+
if (m.onIframeMessage)
|
|
3573
|
+
m.onIframeMessage(d);
|
|
3632
3574
|
});
|
|
3575
|
+
modal = m;
|
|
3576
|
+
return m;
|
|
3633
3577
|
}
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
//
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
|
|
3641
|
-
|
|
3642
|
-
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3578
|
+
function showModal(m) {
|
|
3579
|
+
m.overlay.classList.remove("closing");
|
|
3580
|
+
m.overlay.classList.add("mounted");
|
|
3581
|
+
// Force a reflow so the .show transition runs from the hidden state every open.
|
|
3582
|
+
void m.panel.offsetWidth;
|
|
3583
|
+
m.overlay.classList.add("show");
|
|
3584
|
+
}
|
|
3585
|
+
function hideModal(m, after) {
|
|
3586
|
+
m.overlay.classList.remove("show");
|
|
3587
|
+
m.overlay.classList.add("closing");
|
|
3588
|
+
window.setTimeout(() => {
|
|
3589
|
+
m.overlay.classList.remove("mounted", "closing");
|
|
3590
|
+
}, 260);
|
|
3591
|
+
}
|
|
3592
|
+
function setPanelHeight(m, px) {
|
|
3593
|
+
const h = Math.max(140, Math.min(680, Math.round(px)));
|
|
3594
|
+
m.panel.style.height = h + "px";
|
|
3595
|
+
}
|
|
3596
|
+
// True outer height of the mounted card: the max of its padding-box scroll height
|
|
3597
|
+
// (catches overflowing descendants) and its border-box height plus own vertical
|
|
3598
|
+
// margins (catches border + margins, which scrollHeight omits). Ceiled so a
|
|
3599
|
+
// sub-pixel row is never clipped under `overflow:hidden`.
|
|
3600
|
+
function measureOuterHeight(target) {
|
|
3601
|
+
var _a;
|
|
3602
|
+
const cs = (_a = target.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.getComputedStyle(target);
|
|
3603
|
+
const marginY = cs ? parseFloat(cs.marginTop) + parseFloat(cs.marginBottom) : 0;
|
|
3604
|
+
const borderBox = target.offsetHeight + (Number.isFinite(marginY) ? marginY : 0);
|
|
3605
|
+
const rect = target.getBoundingClientRect().height;
|
|
3606
|
+
return Math.ceil(Math.max(target.scrollHeight, borderBox, rect));
|
|
3650
3607
|
}
|
|
3651
3608
|
/**
|
|
3652
|
-
*
|
|
3653
|
-
*
|
|
3609
|
+
* Measure the currently mounted native content and tween the panel to fit it.
|
|
3610
|
+
* Re-fits automatically when the card's intrinsic height changes - notably after
|
|
3611
|
+
* the Newsreader/Schibsted web fonts swap in (which grows line boxes a few px
|
|
3612
|
+
* after first paint) and on inline view swaps (email -> OTP code -> success).
|
|
3654
3613
|
*/
|
|
3655
|
-
function
|
|
3656
|
-
return walletClaimsFromSession() != null;
|
|
3657
|
-
}
|
|
3658
|
-
/** Whether the active email/social session carries an embedded Crossmint EVM wallet. */
|
|
3659
|
-
function hasEmbeddedEvmWallet() {
|
|
3660
|
-
return walletClaimsFromSession('evm') != null;
|
|
3661
|
-
}
|
|
3662
|
-
/** signAndSubmitTransaction for embedded wallets: serialize + route through the
|
|
3663
|
-
* signer popup, resolve with the on-chain transaction signature/hash. */
|
|
3664
|
-
async function embeddedSignAndSubmit(transaction) {
|
|
3665
|
-
const serializedTransaction = serializeTransaction(transaction);
|
|
3666
|
-
const result = await signViaSignerPopup({ kind: 'transaction', serializedTransaction });
|
|
3667
|
-
const hash = result.hash || result.transactionId;
|
|
3668
|
-
if (!hash)
|
|
3669
|
-
throw new Error('Signer returned no transaction hash.');
|
|
3670
|
-
return hash;
|
|
3671
|
-
}
|
|
3672
|
-
/** Submit a validated BoundedRuntime EVM write through the user's email signer. */
|
|
3673
|
-
async function embeddedRunEvmTransaction(transaction) {
|
|
3614
|
+
function fitPanelToContent(m) {
|
|
3674
3615
|
var _a, _b;
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3616
|
+
const first = m.content.firstElementChild;
|
|
3617
|
+
const target = first || m.content;
|
|
3618
|
+
const apply = () => {
|
|
3619
|
+
// Only fit while this exact node is still the mounted card.
|
|
3620
|
+
if (m.content.firstElementChild !== first && first)
|
|
3621
|
+
return;
|
|
3622
|
+
const h = measureOuterHeight(target);
|
|
3623
|
+
if (h > 0)
|
|
3624
|
+
setPanelHeight(m, h);
|
|
3625
|
+
};
|
|
3626
|
+
apply();
|
|
3627
|
+
const view = target.ownerDocument.defaultView;
|
|
3628
|
+
// Re-fit after fonts load (one-shot) and once the ResizeObserver-less browsers
|
|
3629
|
+
// have painted, so the height never lags a late reflow.
|
|
3630
|
+
(_a = view === null || view === void 0 ? void 0 : view.requestAnimationFrame) === null || _a === void 0 ? void 0 : _a.call(view, apply);
|
|
3631
|
+
const fonts = target.ownerDocument.fonts;
|
|
3632
|
+
if (fonts === null || fonts === void 0 ? void 0 : fonts.ready)
|
|
3633
|
+
fonts.ready.then(apply).catch(() => { });
|
|
3634
|
+
(_b = m.fitObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
3635
|
+
m.fitObserver = null;
|
|
3636
|
+
if (first && typeof (view === null || view === void 0 ? void 0 : view.ResizeObserver) === "function") {
|
|
3637
|
+
const ro = new view.ResizeObserver(() => apply());
|
|
3638
|
+
ro.observe(first);
|
|
3639
|
+
m.fitObserver = ro;
|
|
3692
3640
|
}
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3641
|
+
}
|
|
3642
|
+
/** Switch the modal into NATIVE-content mode: detach any iframe, mount `node`. */
|
|
3643
|
+
function mountContent(m, node) {
|
|
3644
|
+
var _a;
|
|
3645
|
+
detachIframe(m);
|
|
3646
|
+
(_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
3647
|
+
m.fitObserver = null;
|
|
3648
|
+
m.content.replaceChildren(node);
|
|
3649
|
+
m.content.style.display = "block";
|
|
3650
|
+
}
|
|
3651
|
+
/** Get (or lazily create) the warm iframe for signer/hosted-page mode. */
|
|
3652
|
+
function ensureIframe(m, url, origin) {
|
|
3653
|
+
var _a;
|
|
3654
|
+
if (m.iframe && m.iframeOrigin === origin && m.iframe.src === url)
|
|
3655
|
+
return m.iframe;
|
|
3656
|
+
detachIframe(m);
|
|
3657
|
+
(_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
3658
|
+
m.fitObserver = null;
|
|
3659
|
+
m.content.style.display = "none";
|
|
3660
|
+
const iframe = document.createElement("iframe");
|
|
3661
|
+
iframe.className = "bw-frame";
|
|
3662
|
+
iframe.setAttribute("title", "Bounded");
|
|
3663
|
+
// Grant the framed origin WebAuthn get + create so passkey enroll/sign run
|
|
3664
|
+
// inside the frame from the user's click (transient activation stays in-frame).
|
|
3665
|
+
iframe.setAttribute("allow", "publickey-credentials-get; publickey-credentials-create; clipboard-write");
|
|
3666
|
+
iframe.src = url;
|
|
3667
|
+
m.iframeOrigin = origin;
|
|
3668
|
+
m.iframeReady = false;
|
|
3669
|
+
m.panel.appendChild(iframe);
|
|
3670
|
+
m.iframe = iframe;
|
|
3671
|
+
return iframe;
|
|
3672
|
+
}
|
|
3673
|
+
function detachIframe(m) {
|
|
3674
|
+
if (m.iframe) {
|
|
3675
|
+
try {
|
|
3676
|
+
m.iframe.remove();
|
|
3677
|
+
}
|
|
3678
|
+
catch (_a) {
|
|
3679
|
+
/* ignore */
|
|
3680
|
+
}
|
|
3681
|
+
}
|
|
3682
|
+
m.iframe = null;
|
|
3683
|
+
m.iframeReady = false;
|
|
3684
|
+
m.iframeOrigin = "";
|
|
3685
|
+
m.onIframeMessage = null;
|
|
3686
|
+
}
|
|
3687
|
+
// Load the Bounded editorial typefaces (Newsreader display + Schibsted Grotesk body +
|
|
3688
|
+
// JetBrains Mono) once at the document level so they apply inside the shadow root. A
|
|
3689
|
+
// no-op if already present or off-DOM. Fonts loaded here match the hosted auth page.
|
|
3690
|
+
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";
|
|
3691
|
+
function ensureFonts() {
|
|
3692
|
+
if (!hasDOM$4())
|
|
3693
|
+
return;
|
|
3694
|
+
if (document.querySelector('link[data-bounded-fonts="1"]'))
|
|
3695
|
+
return;
|
|
3696
|
+
const pre1 = document.createElement("link");
|
|
3697
|
+
pre1.rel = "preconnect";
|
|
3698
|
+
pre1.href = "https://fonts.googleapis.com";
|
|
3699
|
+
const pre2 = document.createElement("link");
|
|
3700
|
+
pre2.rel = "preconnect";
|
|
3701
|
+
pre2.href = "https://fonts.gstatic.com";
|
|
3702
|
+
pre2.crossOrigin = "anonymous";
|
|
3703
|
+
const link = document.createElement("link");
|
|
3704
|
+
link.rel = "stylesheet";
|
|
3705
|
+
link.href = FONTS_HREF;
|
|
3706
|
+
link.setAttribute("data-bounded-fonts", "1");
|
|
3707
|
+
document.head.append(pre1, pre2, link);
|
|
3701
3708
|
}
|
|
3702
3709
|
|
|
3703
|
-
//
|
|
3704
|
-
//
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
//
|
|
3712
|
-
//
|
|
3713
|
-
//
|
|
3714
|
-
|
|
3715
|
-
|
|
3710
|
+
// turnkey-wallet-widget.ts - runs one Turnkey signer exchange inside the shared
|
|
3711
|
+
// in-app WIDGET (bounded-widget-modal.ts). The Bounded-owned signer page is loaded
|
|
3712
|
+
// in a warm iframe behind the modal so signing feels native: an in-app card plus
|
|
3713
|
+
// the OS Face ID / Touch ID sheet, never a jarring popup.
|
|
3714
|
+
//
|
|
3715
|
+
// WHY AN IFRAME: a WebAuthn passkey is bound to ONE origin, so passkey create/sign
|
|
3716
|
+
// MUST run on the single Bounded signer origin. The iframe carries the
|
|
3717
|
+
// publickey-credentials-{get,create} permission policy and the user's click lands
|
|
3718
|
+
// INSIDE it, giving WebAuthn the transient activation it needs.
|
|
3719
|
+
//
|
|
3720
|
+
// SAFARI / restricted embedders: cross-origin iframe passkey CREATE is unreliable
|
|
3721
|
+
// in Safari. The signer detects that and posts `fallbackPopup`; we then rerun the
|
|
3722
|
+
// SAME request through `popupFallback` (a real window). Callers pass a popup
|
|
3723
|
+
// implementation and never see the difference.
|
|
3724
|
+
/**
|
|
3725
|
+
* Run one signer exchange through the in-app widget. Resolves when `onResult`
|
|
3726
|
+
* accepts a result; falls back to `popupFallback` if the frame cannot run WebAuthn
|
|
3727
|
+
* (Safari cross-origin create) or cannot be mounted.
|
|
3728
|
+
*/
|
|
3729
|
+
async function runWidget(url, signerOrigin, ex, popupFallback) {
|
|
3730
|
+
if (!hasDOM$4())
|
|
3731
|
+
return popupFallback();
|
|
3732
|
+
const m = getModal();
|
|
3733
|
+
const requestId = String(ex.request.requestId);
|
|
3734
|
+
// Serialize: WebAuthn is modal, and one warm frame handles one request at a time.
|
|
3735
|
+
const run = m.chain.then(() => new Promise((resolve, reject) => {
|
|
3736
|
+
let settled = false;
|
|
3737
|
+
let usedFallback = false;
|
|
3738
|
+
const iframe = ensureIframe(m, url, signerOrigin);
|
|
3739
|
+
const send = () => {
|
|
3740
|
+
if (!iframe.contentWindow)
|
|
3741
|
+
return;
|
|
3742
|
+
try {
|
|
3743
|
+
iframe.contentWindow.postMessage(ex.request, signerOrigin);
|
|
3744
|
+
}
|
|
3745
|
+
catch (_a) {
|
|
3746
|
+
/* frame gone */
|
|
3747
|
+
}
|
|
3748
|
+
};
|
|
3749
|
+
let loadTimer = 0;
|
|
3750
|
+
let ceilingTimer = 0;
|
|
3751
|
+
const finish = (fn) => {
|
|
3752
|
+
if (settled)
|
|
3753
|
+
return;
|
|
3754
|
+
settled = true;
|
|
3755
|
+
m.onIframeMessage = null;
|
|
3756
|
+
m.onBackdrop = null;
|
|
3757
|
+
window.clearTimeout(loadTimer);
|
|
3758
|
+
window.clearTimeout(ceilingTimer);
|
|
3759
|
+
fn();
|
|
3760
|
+
};
|
|
3761
|
+
const goFallback = () => {
|
|
3762
|
+
if (usedFallback || settled)
|
|
3763
|
+
return;
|
|
3764
|
+
usedFallback = true;
|
|
3765
|
+
hideModal(m);
|
|
3766
|
+
finish(() => popupFallback().then(resolve, reject));
|
|
3767
|
+
};
|
|
3768
|
+
m.onBackdrop = () => {
|
|
3769
|
+
hideModal(m);
|
|
3770
|
+
finish(() => reject(new Error("cancelled")));
|
|
3771
|
+
};
|
|
3772
|
+
m.onIframeMessage = (d) => {
|
|
3773
|
+
if (d.type === "bounded:turnkey:ready") {
|
|
3774
|
+
m.iframeReady = true;
|
|
3775
|
+
send();
|
|
3776
|
+
return;
|
|
3777
|
+
}
|
|
3778
|
+
if (d.type === "bounded:turnkey:resize" && typeof d.height === "number") {
|
|
3779
|
+
setPanelHeight(m, d.height);
|
|
3780
|
+
return;
|
|
3781
|
+
}
|
|
3782
|
+
if (d.type === "bounded:turnkey:fallbackPopup" && d.requestId === requestId) {
|
|
3783
|
+
goFallback();
|
|
3784
|
+
return;
|
|
3785
|
+
}
|
|
3786
|
+
if (d.type === "bounded:turnkey:result" && d.requestId === requestId) {
|
|
3787
|
+
const outcome = ex.onResult(d);
|
|
3788
|
+
if ("error" in outcome) {
|
|
3789
|
+
hideModal(m);
|
|
3790
|
+
finish(() => reject(new Error(outcome.error)));
|
|
3791
|
+
return;
|
|
3792
|
+
}
|
|
3793
|
+
if (outcome.done) {
|
|
3794
|
+
hideModal(m);
|
|
3795
|
+
finish(() => resolve(outcome.value));
|
|
3796
|
+
}
|
|
3797
|
+
}
|
|
3798
|
+
};
|
|
3799
|
+
showModal(m);
|
|
3800
|
+
if (m.iframeReady)
|
|
3801
|
+
send();
|
|
3802
|
+
// If the frame never loads, degrade to the popup (best-effort - a post-load
|
|
3803
|
+
// popup may be blocked without a gesture, in which case the fallback rejects).
|
|
3804
|
+
loadTimer = window.setTimeout(() => {
|
|
3805
|
+
if (!m.iframeReady && !settled)
|
|
3806
|
+
goFallback();
|
|
3807
|
+
}, 8000);
|
|
3808
|
+
// Absolute ceiling so a stuck flow cannot hang forever.
|
|
3809
|
+
ceilingTimer = window.setTimeout(() => {
|
|
3810
|
+
if (!settled) {
|
|
3811
|
+
hideModal(m);
|
|
3812
|
+
finish(() => reject(new Error("Wallet approval timed out.")));
|
|
3813
|
+
}
|
|
3814
|
+
}, 5 * 60 * 1000);
|
|
3815
|
+
}));
|
|
3816
|
+
m.chain = run.then(() => undefined, () => undefined);
|
|
3817
|
+
return run;
|
|
3818
|
+
}
|
|
3819
|
+
|
|
3820
|
+
// turnkey-signer-bridge.ts - routes NON-CUSTODIAL Turnkey embedded-wallet signing
|
|
3821
|
+
// through the Bounded-owned signer page (auth.bounded.sh/wallet/turnkey/signer).
|
|
3822
|
+
//
|
|
3823
|
+
// TRANSPORT: by default the signer runs inside the in-app WIDGET (turnkey-wallet-
|
|
3824
|
+
// widget.ts) - an iframe behind a Shadow-DOM modal that matches the Bounded widget
|
|
3825
|
+
// look + motion, so signing feels native. Safari (unreliable at iframe passkey
|
|
3826
|
+
// CREATE) and any environment where the frame cannot mount fall back to a real
|
|
3827
|
+
// popup window; both transports speak the same postMessage protocol.
|
|
3828
|
+
//
|
|
3829
|
+
// Turnkey wallets produce raw detached signatures. The signer runs the Turnkey
|
|
3830
|
+
// browser SDK on the single Bounded origin and stamps an ed25519 signRawPayload
|
|
3831
|
+
// with the user's passkey. The app retains the transaction itself.
|
|
3832
|
+
//
|
|
3833
|
+
// ONE ORIGIN, ONE PASSKEY: a WebAuthn passkey is bound to a single origin, so ALL
|
|
3834
|
+
// Bounded apps (any subdomain / custom domain) sign through this one origin - giving
|
|
3835
|
+
// every user one wallet + one passkey that roams across every app.
|
|
3836
|
+
function hasDOM$3() {
|
|
3716
3837
|
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
3717
3838
|
}
|
|
3718
|
-
|
|
3839
|
+
// Safari (incl. iOS WebViews) is unreliable at running a WebAuthn CREATE inside a
|
|
3840
|
+
// cross-origin iframe, so the widget's automatic in-iframe passkey enrollment can
|
|
3841
|
+
// fail - and a post-failure popup would no longer have a user gesture to open with.
|
|
3842
|
+
// There we pick the popup up front, from the original click, so enrollment is solid.
|
|
3843
|
+
// Everywhere else the inline widget is used (buttery in-app modal).
|
|
3844
|
+
function preferPopup() {
|
|
3845
|
+
if (!hasDOM$3() || typeof navigator === 'undefined')
|
|
3846
|
+
return true;
|
|
3847
|
+
const ua = navigator.userAgent || '';
|
|
3848
|
+
return /^((?!chrome|android|crios|fxios|edg|edga|opr).)*safari/i.test(ua);
|
|
3849
|
+
}
|
|
3850
|
+
// Dispatch one signer exchange through the in-app WIDGET, degrading to the POPUP
|
|
3851
|
+
// when we cannot embed (Safari enrollment, or the frame never loads).
|
|
3852
|
+
async function deliver(url, origin, ex) {
|
|
3853
|
+
const popup = () => runPopup(url, origin, ex);
|
|
3854
|
+
if (preferPopup())
|
|
3855
|
+
return popup();
|
|
3856
|
+
return runWidget(url, origin, { request: ex.request, onResult: ex.onResult }, popup);
|
|
3857
|
+
}
|
|
3858
|
+
async function turnkeySignerPage() {
|
|
3859
|
+
const cfg = await core.getConfig();
|
|
3860
|
+
const base = (cfg.humanAuthApiUrl || 'https://auth.bounded.sh').replace(/\/$/, '');
|
|
3861
|
+
return { url: `${base}/wallet/turnkey/signer`, origin: new URL(base).origin };
|
|
3862
|
+
}
|
|
3863
|
+
function toHex(bytes) {
|
|
3864
|
+
let h = '';
|
|
3865
|
+
for (const b of bytes)
|
|
3866
|
+
h += b.toString(16).padStart(2, '0');
|
|
3867
|
+
return h;
|
|
3868
|
+
}
|
|
3869
|
+
function fromHex(hex) {
|
|
3870
|
+
const clean = hex.startsWith('0x') ? hex.slice(2) : hex;
|
|
3871
|
+
const out = new Uint8Array(clean.length / 2);
|
|
3872
|
+
for (let i = 0; i < out.length; i++)
|
|
3873
|
+
out[i] = parseInt(clean.substr(i * 2, 2), 16);
|
|
3874
|
+
return out;
|
|
3875
|
+
}
|
|
3876
|
+
function decodeJwt$1(token) {
|
|
3719
3877
|
try {
|
|
3720
|
-
let
|
|
3721
|
-
const
|
|
3722
|
-
if (
|
|
3723
|
-
|
|
3724
|
-
else if (
|
|
3725
|
-
|
|
3726
|
-
return JSON.parse(getPlatform().atob(
|
|
3878
|
+
let value = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
3879
|
+
const padding = value.length % 4;
|
|
3880
|
+
if (padding === 2)
|
|
3881
|
+
value += '==';
|
|
3882
|
+
else if (padding === 3)
|
|
3883
|
+
value += '=';
|
|
3884
|
+
return JSON.parse(getPlatform().atob(value));
|
|
3727
3885
|
}
|
|
3728
3886
|
catch (_a) {
|
|
3729
3887
|
return {};
|
|
3730
3888
|
}
|
|
3731
3889
|
}
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3890
|
+
function activeTurnkeyAddress() {
|
|
3891
|
+
let token = null;
|
|
3892
|
+
try {
|
|
3893
|
+
token = core.getActiveSessionManager().getIdToken();
|
|
3894
|
+
}
|
|
3895
|
+
catch (_a) {
|
|
3896
|
+
token = null;
|
|
3897
|
+
}
|
|
3898
|
+
if (!token)
|
|
3899
|
+
return null;
|
|
3900
|
+
const claims = decodeJwt$1(token);
|
|
3901
|
+
const address = claims['custom:walletAddress'];
|
|
3902
|
+
const provider = claims['custom:walletProvider'];
|
|
3903
|
+
return provider === 'turnkey' && typeof address === 'string' && address
|
|
3904
|
+
? address
|
|
3905
|
+
: null;
|
|
3906
|
+
}
|
|
3907
|
+
let requestSeq = 0;
|
|
3908
|
+
// Shared popup driver: open the signer page, do the ready/postMessage handshake, and
|
|
3909
|
+
// resolve when `onResult` accepts a result message. Origin + source are pinned.
|
|
3910
|
+
async function runPopup(url, signerOrigin, ex) {
|
|
3911
|
+
var _a, _b;
|
|
3912
|
+
const w = (_a = ex.width) !== null && _a !== void 0 ? _a : 420, h = (_b = ex.height) !== null && _b !== void 0 ? _b : 620;
|
|
3913
|
+
const left = window.screenX + Math.max(0, (window.outerWidth - w) / 2);
|
|
3914
|
+
const top = window.screenY + Math.max(0, (window.outerHeight - h) / 2);
|
|
3915
|
+
const popup = window.open(url, 'bounded-turnkey-signer', `width=${w},height=${h},left=${left},top=${top}`);
|
|
3916
|
+
if (!popup) {
|
|
3917
|
+
throw new Error('Popup blocked - call this from a user gesture (e.g. a click handler) so the passkey approval window can open.');
|
|
3918
|
+
}
|
|
3919
|
+
return await new Promise((resolve, reject) => {
|
|
3920
|
+
let settled = false;
|
|
3921
|
+
let sentRequest = false;
|
|
3922
|
+
let closeTimer;
|
|
3923
|
+
const cleanup = () => { settled = true; window.removeEventListener('message', onMsg); if (closeTimer)
|
|
3924
|
+
clearInterval(closeTimer); };
|
|
3925
|
+
const sendRequest = () => { if (sentRequest)
|
|
3926
|
+
return; sentRequest = true; try {
|
|
3927
|
+
popup.postMessage(ex.request, signerOrigin);
|
|
3928
|
+
}
|
|
3929
|
+
catch ( /* popup gone */_a) { /* popup gone */ } };
|
|
3930
|
+
const onMsg = (event) => {
|
|
3931
|
+
if (event.origin !== signerOrigin)
|
|
3932
|
+
return;
|
|
3933
|
+
if (event.source !== popup)
|
|
3934
|
+
return;
|
|
3935
|
+
const d = event.data;
|
|
3936
|
+
if (!d || typeof d !== 'object')
|
|
3937
|
+
return;
|
|
3938
|
+
if (d.type === 'bounded:turnkey:ready') {
|
|
3939
|
+
sendRequest();
|
|
3940
|
+
return;
|
|
3941
|
+
}
|
|
3942
|
+
if (d.type === 'bounded:turnkey:result' && d.requestId === ex.request.requestId) {
|
|
3943
|
+
const outcome = ex.onResult(d);
|
|
3944
|
+
if ('error' in outcome) {
|
|
3945
|
+
cleanup();
|
|
3946
|
+
try {
|
|
3947
|
+
popup.close();
|
|
3948
|
+
}
|
|
3949
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
3950
|
+
reject(new Error(outcome.error));
|
|
3951
|
+
return;
|
|
3952
|
+
}
|
|
3953
|
+
if (outcome.done) {
|
|
3954
|
+
cleanup();
|
|
3955
|
+
try {
|
|
3956
|
+
popup.close();
|
|
3957
|
+
}
|
|
3958
|
+
catch ( /* ignore */_b) { /* ignore */ }
|
|
3959
|
+
resolve(outcome.value);
|
|
3960
|
+
}
|
|
3961
|
+
}
|
|
3962
|
+
};
|
|
3963
|
+
window.addEventListener('message', onMsg);
|
|
3964
|
+
setTimeout(sendRequest, 1200);
|
|
3965
|
+
closeTimer = setInterval(() => {
|
|
3966
|
+
if (popup.closed) {
|
|
3967
|
+
clearInterval(closeTimer);
|
|
3968
|
+
setTimeout(() => { if (!settled) {
|
|
3969
|
+
cleanup();
|
|
3970
|
+
reject(new Error('Wallet approval window was closed before it completed.'));
|
|
3971
|
+
} }, 400);
|
|
3972
|
+
}
|
|
3973
|
+
}, 500);
|
|
3974
|
+
setTimeout(() => { if (!settled) {
|
|
3975
|
+
cleanup();
|
|
3976
|
+
try {
|
|
3977
|
+
popup.close();
|
|
3978
|
+
}
|
|
3979
|
+
catch ( /* ignore */_a) { /* ignore */ }
|
|
3980
|
+
reject(new Error('Wallet approval timed out.'));
|
|
3981
|
+
} }, 5 * 60 * 1000);
|
|
3982
|
+
});
|
|
3983
|
+
}
|
|
3984
|
+
function hasTurnkeyWallet() {
|
|
3985
|
+
return activeTurnkeyAddress() !== null;
|
|
3986
|
+
}
|
|
3987
|
+
/**
|
|
3988
|
+
* Get-or-create the user's non-custodial Turnkey wallet, resolving with its stable
|
|
3989
|
+
* Solana address. On first use the popup enrolls a device passkey (Face ID / Touch ID)
|
|
3990
|
+
* and creates the wallet (lazy) with no email code; if the wallet already exists it
|
|
3991
|
+
* resolves immediately without a prompt. Call from a USER GESTURE.
|
|
3992
|
+
*
|
|
3993
|
+
* Use this to learn the fee-payer address BEFORE building a transaction to sign.
|
|
3994
|
+
*/
|
|
3995
|
+
async function getOrCreateTurnkeyWallet() {
|
|
3996
|
+
if (!hasDOM$3())
|
|
3997
|
+
throw new Error('Turnkey embedded wallets are only available in the browser (popup-based).');
|
|
3998
|
+
const { url, origin } = await turnkeySignerPage();
|
|
3999
|
+
const requestId = `tkcreate_${Date.now()}_${++requestSeq}`;
|
|
4000
|
+
return await deliver(url, origin, {
|
|
4001
|
+
request: { type: 'bounded:turnkey:createWallet', requestId },
|
|
4002
|
+
onResult: (d) => {
|
|
4003
|
+
if (d.ok && typeof d.address === 'string')
|
|
4004
|
+
return { done: true, value: { address: d.address, subOrgId: String(d.subOrgId || '') } };
|
|
4005
|
+
return { error: String(d.error || 'Wallet setup failed') };
|
|
4006
|
+
},
|
|
4007
|
+
});
|
|
4008
|
+
}
|
|
4009
|
+
/**
|
|
4010
|
+
* Open the Bounded Turnkey signer popup and sign `message` (raw bytes) with the
|
|
4011
|
+
* user's non-custodial Turnkey wallet, resolving with the ed25519 signature +
|
|
4012
|
+
* the wallet address. MUST be called from a USER GESTURE (click/tap) or the
|
|
4013
|
+
* browser blocks the popup.
|
|
4014
|
+
*
|
|
4015
|
+
* On the user's FIRST signature the popup transparently enrolls a device passkey
|
|
4016
|
+
* (Face ID / Touch ID) and creates their wallet - no email code. Subsequent
|
|
4017
|
+
* signatures are just a passkey approval.
|
|
4018
|
+
*/
|
|
4019
|
+
async function signSolanaMessageViaTurnkey(message) {
|
|
4020
|
+
if (!hasDOM$3()) {
|
|
4021
|
+
throw new Error('Turnkey embedded-wallet signing is only available in the browser (popup-based).');
|
|
4022
|
+
}
|
|
4023
|
+
const { url, origin } = await turnkeySignerPage();
|
|
4024
|
+
const requestId = `tksign_${Date.now()}_${++requestSeq}`;
|
|
4025
|
+
const messageHex = toHex(message);
|
|
4026
|
+
return await deliver(url, origin, {
|
|
4027
|
+
request: { type: 'bounded:turnkey:sign', requestId, messageHex },
|
|
4028
|
+
onResult: (d) => {
|
|
4029
|
+
if (d.ok && typeof d.signatureHex === 'string' && typeof d.address === 'string') {
|
|
4030
|
+
return { done: true, value: { signature: fromHex(d.signatureHex), address: d.address, subOrgId: String(d.subOrgId || '') } };
|
|
4031
|
+
}
|
|
4032
|
+
return { error: String(d.error || 'Turnkey signing failed') };
|
|
4033
|
+
},
|
|
4034
|
+
});
|
|
4035
|
+
}
|
|
4036
|
+
function requireMatchingAddress(result) {
|
|
4037
|
+
const expected = activeTurnkeyAddress();
|
|
4038
|
+
if (!expected)
|
|
4039
|
+
throw new Error('This session has no Turnkey wallet. Sign in again to provision one.');
|
|
4040
|
+
if (result.address !== expected)
|
|
4041
|
+
throw new Error('Turnkey signer returned a different wallet address than the active session.');
|
|
4042
|
+
return expected;
|
|
4043
|
+
}
|
|
4044
|
+
async function signMessageWithTurnkey(message) {
|
|
4045
|
+
const result = await signSolanaMessageViaTurnkey(getPlatform().textEncode(message));
|
|
4046
|
+
requireMatchingAddress(result);
|
|
4047
|
+
return bs58.encode(result.signature);
|
|
4048
|
+
}
|
|
4049
|
+
async function signTransactionWithTurnkey(transaction) {
|
|
4050
|
+
const isLegacy = 'serializeMessage' in transaction;
|
|
4051
|
+
const message = isLegacy
|
|
4052
|
+
? transaction.serializeMessage()
|
|
4053
|
+
: transaction.message.serialize();
|
|
4054
|
+
const result = await signSolanaMessageViaTurnkey(new Uint8Array(message));
|
|
4055
|
+
const address = requireMatchingAddress(result);
|
|
4056
|
+
const { PublicKey } = await import('@solana/web3.js');
|
|
4057
|
+
if (isLegacy) {
|
|
4058
|
+
transaction.addSignature(new PublicKey(address), bufferExports.Buffer.from(result.signature));
|
|
4059
|
+
}
|
|
4060
|
+
else {
|
|
4061
|
+
transaction.addSignature(new PublicKey(address), result.signature);
|
|
4062
|
+
}
|
|
4063
|
+
return transaction;
|
|
4064
|
+
}
|
|
4065
|
+
async function signAndSubmitTransactionWithTurnkey(transaction, feePayer) {
|
|
4066
|
+
var _a;
|
|
4067
|
+
const config = await core.getConfig();
|
|
4068
|
+
const rpcUrl = (_a = config.rpcUrl) === null || _a === void 0 ? void 0 : _a.trim();
|
|
4069
|
+
if (!rpcUrl)
|
|
4070
|
+
throw new Error('Turnkey transaction submission requires init({ rpcUrl }).');
|
|
4071
|
+
const { Connection, PublicKey } = await import('@solana/web3.js');
|
|
4072
|
+
const connection = new Connection(rpcUrl, 'confirmed');
|
|
4073
|
+
const { blockhash, lastValidBlockHeight } = await connection.getLatestBlockhash('confirmed');
|
|
4074
|
+
const isLegacy = 'serializeMessage' in transaction;
|
|
4075
|
+
if (isLegacy) {
|
|
4076
|
+
const legacy = transaction;
|
|
4077
|
+
legacy.recentBlockhash = blockhash;
|
|
4078
|
+
legacy.lastValidBlockHeight = lastValidBlockHeight;
|
|
4079
|
+
if (!legacy.feePayer) {
|
|
4080
|
+
const address = activeTurnkeyAddress();
|
|
4081
|
+
if (!address)
|
|
4082
|
+
throw new Error('This session has no Turnkey wallet. Sign in again to provision one.');
|
|
4083
|
+
legacy.feePayer = feePayer !== null && feePayer !== void 0 ? feePayer : new PublicKey(address);
|
|
4084
|
+
}
|
|
4085
|
+
}
|
|
4086
|
+
else {
|
|
4087
|
+
transaction.message.recentBlockhash = blockhash;
|
|
4088
|
+
}
|
|
4089
|
+
const signed = await signTransactionWithTurnkey(transaction);
|
|
4090
|
+
const signature = await connection.sendRawTransaction(signed.serialize(), {
|
|
4091
|
+
skipPreflight: false,
|
|
4092
|
+
maxRetries: 3,
|
|
4093
|
+
});
|
|
4094
|
+
const confirmation = await connection.confirmTransaction({ signature, blockhash, lastValidBlockHeight }, 'confirmed');
|
|
4095
|
+
if (confirmation.value.err) {
|
|
4096
|
+
throw new Error(`Turnkey transaction failed: ${JSON.stringify(confirmation.value.err)}`);
|
|
4097
|
+
}
|
|
4098
|
+
return signature;
|
|
4099
|
+
}
|
|
4100
|
+
|
|
4101
|
+
// Same message the email provider uses when a hosted (OIDC) login has no
|
|
4102
|
+
// embedded wallet — enable `auth.wallets` in policy to get one.
|
|
4103
|
+
const OIDC_NO_WALLET = 'This session has no Turnkey wallet. Sign in again to provision the default ' +
|
|
4104
|
+
'embedded wallet, or check whether the app explicitly sets auth.wallets:false.';
|
|
4105
|
+
const PKCE_STORE_KEY = 'bounded.oidc.pkce';
|
|
4106
|
+
const STORED_AUTH_METHOD_KEY$1 = 'bounded_last_auth_method';
|
|
4107
|
+
const ISSUER_SESSION_KIND_KEY$1 = 'bounded_issuer_session_kind';
|
|
4108
|
+
// Active session store (Web on web, RN on React Native) — selected by config,
|
|
4109
|
+
// not by `typeof window` (truthy on RN). OIDC redirect/popup is web-only, but the
|
|
4110
|
+
// session it mints is read back through this same selector everywhere.
|
|
4111
|
+
const sessionManager = core.getActiveSessionManager;
|
|
4112
|
+
function hasDOM$2() {
|
|
4113
|
+
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
4114
|
+
}
|
|
4115
|
+
function decodeJwt(token) {
|
|
4116
|
+
try {
|
|
4117
|
+
let b64 = token.split('.')[1].replace(/-/g, '+').replace(/_/g, '/');
|
|
4118
|
+
const pad = b64.length % 4;
|
|
4119
|
+
if (pad === 2)
|
|
4120
|
+
b64 += '==';
|
|
4121
|
+
else if (pad === 3)
|
|
4122
|
+
b64 += '=';
|
|
4123
|
+
return JSON.parse(getPlatform().atob(b64));
|
|
4124
|
+
}
|
|
4125
|
+
catch (_a) {
|
|
4126
|
+
return {};
|
|
4127
|
+
}
|
|
4128
|
+
}
|
|
4129
|
+
// A minimal AuthProvider so getSession()/logout() work for OIDC sessions, mirroring email.
|
|
4130
|
+
// Turnkey supplies the default Solana wallet and supports raw message and transaction
|
|
4131
|
+
// signatures through the Bounded signer window.
|
|
4132
|
+
const oidcProviderSingleton = {
|
|
4133
|
+
async login() {
|
|
4134
|
+
const idToken = sessionManager().getIdToken();
|
|
4135
|
+
return idToken ? userFromIdToken(idToken) : null;
|
|
4136
|
+
},
|
|
4137
|
+
async getUser() {
|
|
4138
|
+
const idToken = sessionManager().getIdToken();
|
|
4139
|
+
return idToken ? userFromIdToken(idToken) : null;
|
|
3745
4140
|
},
|
|
3746
4141
|
async logout() { await sessionManager().clearSession(); },
|
|
3747
|
-
async signMessage(
|
|
3748
|
-
|
|
4142
|
+
async signMessage(message) {
|
|
4143
|
+
if (!hasTurnkeyWallet())
|
|
4144
|
+
throw new Error(OIDC_NO_WALLET);
|
|
4145
|
+
return signMessageWithTurnkey(message);
|
|
3749
4146
|
},
|
|
3750
|
-
async signTransaction(
|
|
3751
|
-
|
|
4147
|
+
async signTransaction(transaction) {
|
|
4148
|
+
if (!hasTurnkeyWallet())
|
|
4149
|
+
throw new Error(OIDC_NO_WALLET);
|
|
4150
|
+
return signTransactionWithTurnkey(transaction);
|
|
3752
4151
|
},
|
|
3753
|
-
async runTransaction(evm, sol,
|
|
4152
|
+
async runTransaction(evm, sol, _options) {
|
|
3754
4153
|
if (evm && sol)
|
|
3755
4154
|
throw new Error('A transaction cannot target EVM and Solana at the same time.');
|
|
3756
|
-
if (!
|
|
4155
|
+
if (!hasTurnkeyWallet())
|
|
3757
4156
|
throw new Error(OIDC_NO_WALLET);
|
|
3758
|
-
if (
|
|
3759
|
-
throw new Error('
|
|
3760
|
-
|
|
3761
|
-
throw new Error('This login has no embedded EVM wallet. Enable auth.wallets on an app with an EVM collection and sign in again.');
|
|
3762
|
-
return embeddedRunEvmTransaction(evm);
|
|
4157
|
+
if (evm)
|
|
4158
|
+
throw new Error('Turnkey embedded wallets currently support Solana only. Connect an EVM wallet for EVM transactions.');
|
|
4159
|
+
throw new Error('This Solana transaction shape is not supported by the Turnkey provider. Use a pre-built transaction.');
|
|
3763
4160
|
},
|
|
3764
|
-
async signAndSubmitTransaction(transaction,
|
|
3765
|
-
if (!
|
|
4161
|
+
async signAndSubmitTransaction(transaction, feePayer) {
|
|
4162
|
+
if (!hasTurnkeyWallet())
|
|
3766
4163
|
throw new Error(OIDC_NO_WALLET);
|
|
3767
|
-
return
|
|
4164
|
+
return signAndSubmitTransactionWithTurnkey(transaction, feePayer);
|
|
3768
4165
|
},
|
|
3769
4166
|
};
|
|
3770
4167
|
function userFromIdToken(idToken) {
|
|
3771
4168
|
const c = decodeJwt(idToken);
|
|
3772
4169
|
const id = c['custom:userId'] || c.sub || c['custom:walletAddress'] || '';
|
|
3773
4170
|
return {
|
|
3774
|
-
id,
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
: null,
|
|
4171
|
+
id,
|
|
4172
|
+
address: (c['custom:walletProvider'] === 'turnkey' && c['custom:walletAddress'] || null),
|
|
4173
|
+
evmAddress: null,
|
|
3778
4174
|
email: (c.email || null), provider: oidcProviderSingleton,
|
|
3779
4175
|
};
|
|
3780
4176
|
}
|
|
@@ -3852,7 +4248,7 @@ async function buildAuthorize(opts) {
|
|
|
3852
4248
|
throw new Error('appId is not configured (init the SDK with your appId)');
|
|
3853
4249
|
// Web: default to the current page so callers don't have to pass redirectUri. Native has no
|
|
3854
4250
|
// window — it MUST pass an https universal link (the system auth session needs a real URL).
|
|
3855
|
-
const redirectUri = opts.redirectUri || (hasDOM$
|
|
4251
|
+
const redirectUri = opts.redirectUri || (hasDOM$2() ? window.location.origin + window.location.pathname : undefined);
|
|
3856
4252
|
if (!redirectUri)
|
|
3857
4253
|
throw new Error('redirectUri is required on React Native — pass the https universal link the app owner registered.');
|
|
3858
4254
|
const { verifier, challenge } = await makePkce();
|
|
@@ -3960,7 +4356,7 @@ async function runNativeLogin(opts) {
|
|
|
3960
4356
|
async function loginWithRedirect(opts) {
|
|
3961
4357
|
if (isNative())
|
|
3962
4358
|
return runNativeLogin(opts);
|
|
3963
|
-
if (!hasDOM$
|
|
4359
|
+
if (!hasDOM$2())
|
|
3964
4360
|
throw new Error('loginWithRedirect requires a browser or a configured React Native platform');
|
|
3965
4361
|
const { url, pkce } = await buildAuthorize(opts);
|
|
3966
4362
|
// Stash the verifier + state so the redirect_uri page can finish the exchange. sessionStorage
|
|
@@ -3976,7 +4372,7 @@ async function loginWithRedirect(opts) {
|
|
|
3976
4372
|
* loginWithRedirect() (which resolves with the User), so there is no separate redirect
|
|
3977
4373
|
* page to finish from. Calling it on RN is harmless. */
|
|
3978
4374
|
async function completeLoginFromRedirect() {
|
|
3979
|
-
if (!hasDOM$
|
|
4375
|
+
if (!hasDOM$2())
|
|
3980
4376
|
return null;
|
|
3981
4377
|
const url = new URL(window.location.href);
|
|
3982
4378
|
const code = url.searchParams.get('code');
|
|
@@ -4022,7 +4418,7 @@ async function completeLoginFromRedirect() {
|
|
|
4022
4418
|
* redirect_uri page posts the code back. The redirect_uri page just calls the usual
|
|
4023
4419
|
* completeLoginFromRedirect() (which auto-detects the popup) — no separate call needed. */
|
|
4024
4420
|
async function loginWithPopup(opts) {
|
|
4025
|
-
if (!hasDOM$
|
|
4421
|
+
if (!hasDOM$2())
|
|
4026
4422
|
throw new Error('loginWithPopup is web-only — on React Native use loginWithRedirect()');
|
|
4027
4423
|
// Open synchronously, before PKCE/config awaits consume the click's user-
|
|
4028
4424
|
// activation window. Opening only after buildAuthorize() intermittently
|
|
@@ -4162,7 +4558,7 @@ async function loginWithPopup(opts) {
|
|
|
4162
4558
|
/** Call this on the redirect_uri page when login was opened via loginWithPopup(): posts the
|
|
4163
4559
|
* code back to the opener and closes. (For the redirect variant use completeLoginFromRedirect.) */
|
|
4164
4560
|
function completeLoginInPopup(openerOrigin) {
|
|
4165
|
-
if (!hasDOM$
|
|
4561
|
+
if (!hasDOM$2() || !window.opener)
|
|
4166
4562
|
return;
|
|
4167
4563
|
const url = new URL(window.location.href);
|
|
4168
4564
|
const code = url.searchParams.get('code');
|
|
@@ -4565,7 +4961,7 @@ async function getAuthProvider(config) {
|
|
|
4565
4961
|
// manager as legacy email sessions. Human login should be started
|
|
4566
4962
|
// with loginWithRedirect(); this provider is kept as the restore and
|
|
4567
4963
|
// logout surface for those sessions.
|
|
4568
|
-
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-
|
|
4964
|
+
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./email-auth-provider-97CluDlZ.js'); })).EmailAuthProvider();
|
|
4569
4965
|
break;
|
|
4570
4966
|
case "wallet":
|
|
4571
4967
|
case "phantom": {
|
|
@@ -4584,7 +4980,7 @@ async function getAuthProvider(config) {
|
|
|
4584
4980
|
if (!isWalletLoginEnabled(config)) {
|
|
4585
4981
|
throw walletLoginNotEnabledError(authMethod);
|
|
4586
4982
|
}
|
|
4587
|
-
const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-
|
|
4983
|
+
const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-CSj0lY9H.js'); });
|
|
4588
4984
|
currentAuthProvider = new InjectedWalletProvider(resolveInjectedWalletConfig(config), rpcUrl, configuredNetwork);
|
|
4589
4985
|
break;
|
|
4590
4986
|
}
|
|
@@ -4597,7 +4993,7 @@ async function getAuthProvider(config) {
|
|
|
4597
4993
|
// Selecting authMethod:'evm-wallet' IS the opt-in — pass evmWalletConfig
|
|
4598
4994
|
// to point at a specific wallet or set chainId/domain.
|
|
4599
4995
|
const evmWalletConfig = ((_a = config.evmWalletConfig) !== null && _a !== void 0 ? _a : {});
|
|
4600
|
-
const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-
|
|
4996
|
+
const { InjectedEvmWalletProvider } = await Promise.resolve().then(function () { return require('./injected-evm-wallet-provider-B5MA0w8I.js'); });
|
|
4601
4997
|
currentAuthProvider = new InjectedEvmWalletProvider(evmWalletConfig);
|
|
4602
4998
|
break;
|
|
4603
4999
|
}
|
|
@@ -4625,7 +5021,7 @@ async function getAuthProvider(config) {
|
|
|
4625
5021
|
break;
|
|
4626
5022
|
case "guest":
|
|
4627
5023
|
// Anonymous auth: a device-local ed25519 keypair signs the challenge.
|
|
4628
|
-
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-
|
|
5024
|
+
currentAuthProvider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BvIf5W5c.js'); })).GuestAuthProvider();
|
|
4629
5025
|
break;
|
|
4630
5026
|
}
|
|
4631
5027
|
if (!currentAuthProvider) {
|
|
@@ -4681,7 +5077,7 @@ async function signInAnonymously() {
|
|
|
4681
5077
|
// Lazy-load the guest provider so its ed25519/web3 dependencies stay out of
|
|
4682
5078
|
// the main bundle for data-only apps. Specifier is a static string literal
|
|
4683
5079
|
// so bundle-splitting can see it.
|
|
4684
|
-
const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-
|
|
5080
|
+
const provider = new (await Promise.resolve().then(function () { return require('./guest-auth-provider-BvIf5W5c.js'); })).GuestAuthProvider();
|
|
4685
5081
|
setAuthLoading(true); // reflect "signing in…" while the guest keypair session is minted
|
|
4686
5082
|
const commit = (user) => {
|
|
4687
5083
|
// No await/interleaving inside this block: provider/method/config/marker
|
|
@@ -4832,7 +5228,7 @@ async function loginWithWallet(options) {
|
|
|
4832
5228
|
? Object.assign(Object.assign({}, base), { getProvider: options.getProvider }) : base;
|
|
4833
5229
|
const rpcUrl = normalizeSolanaRpcUrl(cfg.rpcUrl);
|
|
4834
5230
|
const network = getConfiguredSolanaNetwork(cfg);
|
|
4835
|
-
const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-
|
|
5231
|
+
const { InjectedWalletProvider } = await Promise.resolve().then(function () { return require('./injected-wallet-provider-CSj0lY9H.js'); });
|
|
4836
5232
|
let provider = new InjectedWalletProvider(injectedConfig, rpcUrl, network);
|
|
4837
5233
|
if (coreConfig.chain === 'offchain')
|
|
4838
5234
|
provider = new OffchainAuthProvider(provider);
|
|
@@ -5232,276 +5628,23 @@ function setAuthProviderInstance(provider) {
|
|
|
5232
5628
|
authProviderInstance = provider;
|
|
5233
5629
|
}
|
|
5234
5630
|
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
:host{all:initial}
|
|
5253
|
-
*{box-sizing:border-box}
|
|
5254
|
-
.bw-ov{position:fixed;inset:0;z-index:2147483647;display:none;
|
|
5255
|
-
font-family:"Schibsted Grotesk","Helvetica Neue",Arial,sans-serif}
|
|
5256
|
-
.bw-ov.mounted{display:block}
|
|
5257
|
-
.bw-back{position:fixed;inset:0;background:rgba(4,4,5,.62);opacity:0;
|
|
5258
|
-
transition:opacity .24s ease;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px)}
|
|
5259
|
-
.bw-wrap{position:fixed;inset:0;display:flex;align-items:center;justify-content:center;padding:20px}
|
|
5260
|
-
.bw-panel{position:relative;width:420px;max-width:calc(100vw - 28px);height:280px;
|
|
5261
|
-
border-radius:14px;overflow:hidden;background:#0e0f11;border:1px solid rgba(232,230,222,.08);
|
|
5262
|
-
box-shadow:0 1px 0 rgba(232,230,222,.03) inset,0 40px 90px -45px rgba(0,0,0,.85);
|
|
5263
|
-
opacity:0;transform:translateY(14px) scale(.98);will-change:transform,opacity,height;
|
|
5264
|
-
transition:opacity .28s cubic-bezier(.2,.8,.2,1),transform .28s cubic-bezier(.2,.8,.2,1),
|
|
5265
|
-
height .3s cubic-bezier(.2,.8,.2,1)}
|
|
5266
|
-
.bw-ov.show .bw-back{opacity:1}
|
|
5267
|
-
.bw-ov.show .bw-panel{opacity:1;transform:none}
|
|
5268
|
-
.bw-ov.closing .bw-back{opacity:0}
|
|
5269
|
-
.bw-ov.closing .bw-panel{opacity:0;transform:translateY(12px) scale(.99)}
|
|
5270
|
-
.bw-content{position:absolute;inset:0;overflow:hidden}
|
|
5271
|
-
.bw-frame{display:block;width:100%;height:100%;border:0;background:transparent;color-scheme:dark}
|
|
5272
|
-
/* editorial corner crop-marks, matching the reference frame */
|
|
5273
|
-
.bw-mk{position:absolute;width:13px;height:13px;pointer-events:none;z-index:2;
|
|
5274
|
-
border-color:rgba(232,230,222,.22);border-style:solid;border-width:0}
|
|
5275
|
-
.bw-mk.tl{top:11px;left:11px;border-top-width:1px;border-left-width:1px}
|
|
5276
|
-
.bw-mk.tr{top:11px;right:11px;border-top-width:1px;border-right-width:1px}
|
|
5277
|
-
.bw-mk.bl{bottom:11px;left:11px;border-bottom-width:1px;border-left-width:1px}
|
|
5278
|
-
.bw-mk.br{bottom:11px;right:11px;border-bottom-width:1px;border-right-width:1px}
|
|
5279
|
-
@media(max-width:560px){
|
|
5280
|
-
.bw-wrap{align-items:flex-end;padding:0}
|
|
5281
|
-
.bw-panel{width:100%;max-width:100%;border-radius:18px 18px 0 0;border-bottom:0}
|
|
5282
|
-
.bw-content{overflow-y:auto}
|
|
5283
|
-
.bw-ov.show .bw-panel{transform:none}
|
|
5284
|
-
.bw-ov:not(.show) .bw-panel,.bw-ov.closing .bw-panel{transform:translateY(18%)}
|
|
5285
|
-
}
|
|
5286
|
-
@media(prefers-reduced-motion:reduce){.bw-back,.bw-panel{transition:opacity .12s linear!important}}
|
|
5287
|
-
`;
|
|
5288
|
-
let modal = null;
|
|
5289
|
-
function getModal() {
|
|
5290
|
-
if (modal)
|
|
5291
|
-
return modal;
|
|
5292
|
-
const root = document.createElement("div");
|
|
5293
|
-
root.setAttribute("data-bounded-widget", "");
|
|
5294
|
-
const shadow = root.attachShadow({ mode: "open" });
|
|
5295
|
-
const style = document.createElement("style");
|
|
5296
|
-
style.textContent = OVERLAY_CSS;
|
|
5297
|
-
shadow.appendChild(style);
|
|
5298
|
-
const overlay = document.createElement("div");
|
|
5299
|
-
overlay.className = "bw-ov";
|
|
5300
|
-
const back = document.createElement("div");
|
|
5301
|
-
back.className = "bw-back";
|
|
5302
|
-
const wrap = document.createElement("div");
|
|
5303
|
-
wrap.className = "bw-wrap";
|
|
5304
|
-
const panel = document.createElement("div");
|
|
5305
|
-
panel.className = "bw-panel";
|
|
5306
|
-
const content = document.createElement("div");
|
|
5307
|
-
content.className = "bw-content";
|
|
5308
|
-
panel.appendChild(content);
|
|
5309
|
-
for (const corner of ["tl", "tr", "bl", "br"]) {
|
|
5310
|
-
const mk = document.createElement("span");
|
|
5311
|
-
mk.className = "bw-mk " + corner;
|
|
5312
|
-
panel.appendChild(mk);
|
|
5313
|
-
}
|
|
5314
|
-
wrap.appendChild(panel);
|
|
5315
|
-
overlay.appendChild(back);
|
|
5316
|
-
overlay.appendChild(wrap);
|
|
5317
|
-
shadow.appendChild(overlay);
|
|
5318
|
-
(document.body || document.documentElement).appendChild(root);
|
|
5319
|
-
const m = {
|
|
5320
|
-
root,
|
|
5321
|
-
overlay,
|
|
5322
|
-
panel,
|
|
5323
|
-
content,
|
|
5324
|
-
iframe: null,
|
|
5325
|
-
iframeOrigin: "",
|
|
5326
|
-
iframeReady: false,
|
|
5327
|
-
onBackdrop: null,
|
|
5328
|
-
onIframeMessage: null,
|
|
5329
|
-
chain: Promise.resolve(),
|
|
5330
|
-
fitObserver: null,
|
|
5331
|
-
};
|
|
5332
|
-
// The dismiss handler lives on `.bw-wrap`: it is the full-viewport flex layer
|
|
5333
|
-
// that sits ABOVE `.bw-back`, so it (not the backdrop element) receives clicks
|
|
5334
|
-
// outside the panel. Fire only when the click lands on the wrap itself, never
|
|
5335
|
-
// when it bubbles up from the panel or its content.
|
|
5336
|
-
wrap.addEventListener("click", (event) => {
|
|
5337
|
-
if (event.target === wrap && m.onBackdrop)
|
|
5338
|
-
m.onBackdrop();
|
|
5339
|
-
});
|
|
5340
|
-
window.addEventListener("message", (event) => {
|
|
5341
|
-
if (!m.iframe)
|
|
5342
|
-
return;
|
|
5343
|
-
if (event.origin !== m.iframeOrigin)
|
|
5344
|
-
return;
|
|
5345
|
-
if (event.source !== m.iframe.contentWindow)
|
|
5346
|
-
return;
|
|
5347
|
-
const d = event.data;
|
|
5348
|
-
if (!d || typeof d !== "object")
|
|
5349
|
-
return;
|
|
5350
|
-
if (m.onIframeMessage)
|
|
5351
|
-
m.onIframeMessage(d);
|
|
5352
|
-
});
|
|
5353
|
-
modal = m;
|
|
5354
|
-
return m;
|
|
5355
|
-
}
|
|
5356
|
-
function showModal(m) {
|
|
5357
|
-
m.overlay.classList.remove("closing");
|
|
5358
|
-
m.overlay.classList.add("mounted");
|
|
5359
|
-
// Force a reflow so the .show transition runs from the hidden state every open.
|
|
5360
|
-
void m.panel.offsetWidth;
|
|
5361
|
-
m.overlay.classList.add("show");
|
|
5362
|
-
}
|
|
5363
|
-
function hideModal(m, after) {
|
|
5364
|
-
m.overlay.classList.remove("show");
|
|
5365
|
-
m.overlay.classList.add("closing");
|
|
5366
|
-
window.setTimeout(() => {
|
|
5367
|
-
m.overlay.classList.remove("mounted", "closing");
|
|
5368
|
-
}, 260);
|
|
5369
|
-
}
|
|
5370
|
-
function setPanelHeight(m, px) {
|
|
5371
|
-
const h = Math.max(140, Math.min(680, Math.round(px)));
|
|
5372
|
-
m.panel.style.height = h + "px";
|
|
5373
|
-
}
|
|
5374
|
-
// True outer height of the mounted card: the max of its padding-box scroll height
|
|
5375
|
-
// (catches overflowing descendants) and its border-box height plus own vertical
|
|
5376
|
-
// margins (catches border + margins, which scrollHeight omits). Ceiled so a
|
|
5377
|
-
// sub-pixel row is never clipped under `overflow:hidden`.
|
|
5378
|
-
function measureOuterHeight(target) {
|
|
5379
|
-
var _a;
|
|
5380
|
-
const cs = (_a = target.ownerDocument.defaultView) === null || _a === void 0 ? void 0 : _a.getComputedStyle(target);
|
|
5381
|
-
const marginY = cs ? parseFloat(cs.marginTop) + parseFloat(cs.marginBottom) : 0;
|
|
5382
|
-
const borderBox = target.offsetHeight + (Number.isFinite(marginY) ? marginY : 0);
|
|
5383
|
-
const rect = target.getBoundingClientRect().height;
|
|
5384
|
-
return Math.ceil(Math.max(target.scrollHeight, borderBox, rect));
|
|
5385
|
-
}
|
|
5386
|
-
/**
|
|
5387
|
-
* Measure the currently mounted native content and tween the panel to fit it.
|
|
5388
|
-
* Re-fits automatically when the card's intrinsic height changes - notably after
|
|
5389
|
-
* the Newsreader/Schibsted web fonts swap in (which grows line boxes a few px
|
|
5390
|
-
* after first paint) and on inline view swaps (email -> OTP code -> success).
|
|
5391
|
-
*/
|
|
5392
|
-
function fitPanelToContent(m) {
|
|
5393
|
-
var _a, _b;
|
|
5394
|
-
const first = m.content.firstElementChild;
|
|
5395
|
-
const target = first || m.content;
|
|
5396
|
-
const apply = () => {
|
|
5397
|
-
// Only fit while this exact node is still the mounted card.
|
|
5398
|
-
if (m.content.firstElementChild !== first && first)
|
|
5399
|
-
return;
|
|
5400
|
-
const h = measureOuterHeight(target);
|
|
5401
|
-
if (h > 0)
|
|
5402
|
-
setPanelHeight(m, h);
|
|
5403
|
-
};
|
|
5404
|
-
apply();
|
|
5405
|
-
const view = target.ownerDocument.defaultView;
|
|
5406
|
-
// Re-fit after fonts load (one-shot) and once the ResizeObserver-less browsers
|
|
5407
|
-
// have painted, so the height never lags a late reflow.
|
|
5408
|
-
(_a = view === null || view === void 0 ? void 0 : view.requestAnimationFrame) === null || _a === void 0 ? void 0 : _a.call(view, apply);
|
|
5409
|
-
const fonts = target.ownerDocument.fonts;
|
|
5410
|
-
if (fonts === null || fonts === void 0 ? void 0 : fonts.ready)
|
|
5411
|
-
fonts.ready.then(apply).catch(() => { });
|
|
5412
|
-
(_b = m.fitObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
5413
|
-
m.fitObserver = null;
|
|
5414
|
-
if (first && typeof (view === null || view === void 0 ? void 0 : view.ResizeObserver) === "function") {
|
|
5415
|
-
const ro = new view.ResizeObserver(() => apply());
|
|
5416
|
-
ro.observe(first);
|
|
5417
|
-
m.fitObserver = ro;
|
|
5418
|
-
}
|
|
5419
|
-
}
|
|
5420
|
-
/** Switch the modal into NATIVE-content mode: detach any iframe, mount `node`. */
|
|
5421
|
-
function mountContent(m, node) {
|
|
5422
|
-
var _a;
|
|
5423
|
-
detachIframe(m);
|
|
5424
|
-
(_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
5425
|
-
m.fitObserver = null;
|
|
5426
|
-
m.content.replaceChildren(node);
|
|
5427
|
-
m.content.style.display = "block";
|
|
5428
|
-
}
|
|
5429
|
-
/** Get (or lazily create) the warm iframe for signer/hosted-page mode. */
|
|
5430
|
-
function ensureIframe(m, url, origin) {
|
|
5431
|
-
var _a;
|
|
5432
|
-
if (m.iframe && m.iframeOrigin === origin && m.iframe.src === url)
|
|
5433
|
-
return m.iframe;
|
|
5434
|
-
detachIframe(m);
|
|
5435
|
-
(_a = m.fitObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
5436
|
-
m.fitObserver = null;
|
|
5437
|
-
m.content.style.display = "none";
|
|
5438
|
-
const iframe = document.createElement("iframe");
|
|
5439
|
-
iframe.className = "bw-frame";
|
|
5440
|
-
iframe.setAttribute("title", "Bounded");
|
|
5441
|
-
// Grant the framed origin WebAuthn get + create so passkey enroll/sign run
|
|
5442
|
-
// inside the frame from the user's click (transient activation stays in-frame).
|
|
5443
|
-
iframe.setAttribute("allow", "publickey-credentials-get; publickey-credentials-create; clipboard-write");
|
|
5444
|
-
iframe.src = url;
|
|
5445
|
-
m.iframeOrigin = origin;
|
|
5446
|
-
m.iframeReady = false;
|
|
5447
|
-
m.panel.appendChild(iframe);
|
|
5448
|
-
m.iframe = iframe;
|
|
5449
|
-
return iframe;
|
|
5450
|
-
}
|
|
5451
|
-
function detachIframe(m) {
|
|
5452
|
-
if (m.iframe) {
|
|
5453
|
-
try {
|
|
5454
|
-
m.iframe.remove();
|
|
5455
|
-
}
|
|
5456
|
-
catch (_a) {
|
|
5457
|
-
/* ignore */
|
|
5458
|
-
}
|
|
5459
|
-
}
|
|
5460
|
-
m.iframe = null;
|
|
5461
|
-
m.iframeReady = false;
|
|
5462
|
-
m.iframeOrigin = "";
|
|
5463
|
-
m.onIframeMessage = null;
|
|
5464
|
-
}
|
|
5465
|
-
// Load the Bounded editorial typefaces (Newsreader display + Schibsted Grotesk body +
|
|
5466
|
-
// JetBrains Mono) once at the document level so they apply inside the shadow root. A
|
|
5467
|
-
// no-op if already present or off-DOM. Fonts loaded here match the hosted auth page.
|
|
5468
|
-
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";
|
|
5469
|
-
function ensureFonts() {
|
|
5470
|
-
if (!hasDOM$3())
|
|
5471
|
-
return;
|
|
5472
|
-
if (document.querySelector('link[data-bounded-fonts="1"]'))
|
|
5473
|
-
return;
|
|
5474
|
-
const pre1 = document.createElement("link");
|
|
5475
|
-
pre1.rel = "preconnect";
|
|
5476
|
-
pre1.href = "https://fonts.googleapis.com";
|
|
5477
|
-
const pre2 = document.createElement("link");
|
|
5478
|
-
pre2.rel = "preconnect";
|
|
5479
|
-
pre2.href = "https://fonts.gstatic.com";
|
|
5480
|
-
pre2.crossOrigin = "anonymous";
|
|
5481
|
-
const link = document.createElement("link");
|
|
5482
|
-
link.rel = "stylesheet";
|
|
5483
|
-
link.href = FONTS_HREF;
|
|
5484
|
-
link.setAttribute("data-bounded-fonts", "1");
|
|
5485
|
-
document.head.append(pre1, pre2, link);
|
|
5486
|
-
}
|
|
5487
|
-
|
|
5488
|
-
var __classPrivateFieldGet = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
5489
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
5490
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5491
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5492
|
-
};
|
|
5493
|
-
var __classPrivateFieldSet = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
5494
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
5495
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5496
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5497
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
5498
|
-
};
|
|
5499
|
-
var _AppReadyEvent_detail;
|
|
5500
|
-
let wallets = undefined;
|
|
5501
|
-
const registeredWalletsSet = new Set();
|
|
5502
|
-
function addRegisteredWallet(wallet) {
|
|
5503
|
-
cachedWalletsArray = undefined;
|
|
5504
|
-
registeredWalletsSet.add(wallet);
|
|
5631
|
+
var __classPrivateFieldGet = (undefined && undefined.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
5632
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
5633
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
5634
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5635
|
+
};
|
|
5636
|
+
var __classPrivateFieldSet = (undefined && undefined.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
5637
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
5638
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5639
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5640
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
5641
|
+
};
|
|
5642
|
+
var _AppReadyEvent_detail;
|
|
5643
|
+
let wallets = undefined;
|
|
5644
|
+
const registeredWalletsSet = new Set();
|
|
5645
|
+
function addRegisteredWallet(wallet) {
|
|
5646
|
+
cachedWalletsArray = undefined;
|
|
5647
|
+
registeredWalletsSet.add(wallet);
|
|
5505
5648
|
}
|
|
5506
5649
|
function removeRegisteredWallet(wallet) {
|
|
5507
5650
|
cachedWalletsArray = undefined;
|
|
@@ -5773,7 +5916,7 @@ function discoverStandardWallets() {
|
|
|
5773
5916
|
return out;
|
|
5774
5917
|
}
|
|
5775
5918
|
|
|
5776
|
-
function hasDOM$
|
|
5919
|
+
function hasDOM$1() {
|
|
5777
5920
|
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
5778
5921
|
}
|
|
5779
5922
|
async function signerOrigin() {
|
|
@@ -5850,7 +5993,7 @@ function sealOtpInIframe(code, otpEncryptionTargetBundle) {
|
|
|
5850
5993
|
* step needs the signer iframe); on React Native use loginWithRedirect() instead.
|
|
5851
5994
|
*/
|
|
5852
5995
|
async function startTurnkeyEmailLogin(email) {
|
|
5853
|
-
if (!hasDOM$
|
|
5996
|
+
if (!hasDOM$1()) {
|
|
5854
5997
|
throw new Error('Turnkey-native email login is web-only - on React Native use loginWithRedirect()');
|
|
5855
5998
|
}
|
|
5856
5999
|
const trimmed = email.trim();
|
|
@@ -6039,7 +6182,7 @@ function el(tag, cls, text) {
|
|
|
6039
6182
|
*/
|
|
6040
6183
|
async function openBoundedWidget(opts = {}) {
|
|
6041
6184
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
6042
|
-
if (!hasDOM$
|
|
6185
|
+
if (!hasDOM$4()) {
|
|
6043
6186
|
throw new Error("openBoundedWidget is web-only - on React Native use loginWithRedirect()");
|
|
6044
6187
|
}
|
|
6045
6188
|
// Imported lazily to avoid a static cycle with the auth index barrel.
|
|
@@ -6412,1014 +6555,718 @@ function useAuth() {
|
|
|
6412
6555
|
// Only log errors that aren't user-initiated cancellations
|
|
6413
6556
|
if (error !== 'exited_auth_flow' && (error === null || error === void 0 ? void 0 : error.message) !== 'exited_auth_flow') {
|
|
6414
6557
|
console.error('Error logging in:', error);
|
|
6415
|
-
}
|
|
6416
|
-
setUser(null);
|
|
6417
|
-
}
|
|
6418
|
-
finally {
|
|
6419
|
-
setLoading(false);
|
|
6420
|
-
}
|
|
6421
|
-
};
|
|
6422
|
-
const logout$1 = async () => {
|
|
6423
|
-
try {
|
|
6424
|
-
setLoading(true);
|
|
6425
|
-
await logout();
|
|
6426
|
-
setUser(null);
|
|
6427
|
-
}
|
|
6428
|
-
catch (error) {
|
|
6429
|
-
console.error('Error logging out:', error);
|
|
6430
|
-
}
|
|
6431
|
-
finally {
|
|
6432
|
-
setLoading(false);
|
|
6433
|
-
}
|
|
6434
|
-
};
|
|
6435
|
-
return {
|
|
6436
|
-
login: login$1,
|
|
6437
|
-
logout: logout$1,
|
|
6438
|
-
loading: loading || sdkLoading,
|
|
6439
|
-
user,
|
|
6440
|
-
};
|
|
6441
|
-
}
|
|
6442
|
-
|
|
6443
|
-
var _a$1;
|
|
6444
|
-
// SSR guard (mirrors useAuth): no hooks/subscriptions during Node server render.
|
|
6445
|
-
// React Native has no `window` but DOES support hooks, so detect Node SSR
|
|
6446
|
-
// specifically via process.versions.node.
|
|
6447
|
-
const isSSR$1 = typeof window === 'undefined'
|
|
6448
|
-
&& typeof process !== 'undefined'
|
|
6449
|
-
&& !!((_a$1 = process.versions) === null || _a$1 === void 0 ? void 0 : _a$1.node);
|
|
6450
|
-
function useQuery(path, options) {
|
|
6451
|
-
if (isSSR$1) {
|
|
6452
|
-
return { data: undefined, loading: true, error: null };
|
|
6453
|
-
}
|
|
6454
|
-
const [data, setData] = React__namespace.useState(undefined);
|
|
6455
|
-
const [loading, setLoading] = React__namespace.useState(true);
|
|
6456
|
-
const [error, setError] = React__namespace.useState(null);
|
|
6457
|
-
// Re-subscribe only when the query actually changes (not on every render).
|
|
6458
|
-
const optionsKey = options ? JSON.stringify(options) : '';
|
|
6459
|
-
React__namespace.useEffect(() => {
|
|
6460
|
-
if (!path) {
|
|
6461
|
-
setData(undefined);
|
|
6462
|
-
setLoading(false);
|
|
6463
|
-
setError(null);
|
|
6464
|
-
return;
|
|
6465
|
-
}
|
|
6466
|
-
let active = true;
|
|
6467
|
-
let unsubscribe;
|
|
6468
|
-
setLoading(true);
|
|
6469
|
-
setError(null);
|
|
6470
|
-
core.subscribe(path, Object.assign(Object.assign({}, options), { onData: (d) => {
|
|
6471
|
-
if (!active)
|
|
6472
|
-
return;
|
|
6473
|
-
// A reconnect can legitimately surface a transport error before the
|
|
6474
|
-
// socket re-authenticates and sends a new authorized snapshot. That
|
|
6475
|
-
// fresh snapshot is the recovery boundary: keep the error visible
|
|
6476
|
-
// until data arrives, then clear it so React does not remain stuck on
|
|
6477
|
-
// a failure page after realtime has recovered.
|
|
6478
|
-
setError(null);
|
|
6479
|
-
setData(d);
|
|
6480
|
-
setLoading(false);
|
|
6481
|
-
}, onError: (e) => {
|
|
6482
|
-
if (!active)
|
|
6483
|
-
return;
|
|
6484
|
-
setError(e instanceof Error ? e : new Error(String(e)));
|
|
6485
|
-
setLoading(false);
|
|
6486
|
-
} }))
|
|
6487
|
-
.then((unsub) => {
|
|
6488
|
-
if (active)
|
|
6489
|
-
unsubscribe = unsub;
|
|
6490
|
-
else
|
|
6491
|
-
void unsub(); // unmounted before subscribe resolved
|
|
6492
|
-
})
|
|
6493
|
-
.catch((e) => {
|
|
6494
|
-
if (!active)
|
|
6495
|
-
return;
|
|
6496
|
-
setError(e instanceof Error ? e : new Error(String(e)));
|
|
6497
|
-
setLoading(false);
|
|
6498
|
-
});
|
|
6499
|
-
return () => {
|
|
6500
|
-
active = false;
|
|
6501
|
-
void (unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe());
|
|
6502
|
-
};
|
|
6503
|
-
// optionsKey captures the option object's contents; path is the other input.
|
|
6504
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
6505
|
-
}, [path, optionsKey]);
|
|
6506
|
-
return { data, loading, error };
|
|
6507
|
-
}
|
|
6508
|
-
|
|
6509
|
-
// Wrapper for getIdToken - passes isServer=false for client-side usage
|
|
6510
|
-
async function getIdToken() {
|
|
6511
|
-
return core.getIdToken(false);
|
|
6512
|
-
}
|
|
6513
|
-
|
|
6514
|
-
var __rest = (undefined && undefined.__rest) || function (s, e) {
|
|
6515
|
-
var t = {};
|
|
6516
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
6517
|
-
t[p] = s[p];
|
|
6518
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6519
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
6520
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
6521
|
-
t[p[i]] = s[p[i]];
|
|
6522
|
-
}
|
|
6523
|
-
return t;
|
|
6524
|
-
};
|
|
6525
|
-
/** A structured non-2xx response from the developer API. */
|
|
6526
|
-
class BuildsApiError extends Error {
|
|
6527
|
-
constructor(init) {
|
|
6528
|
-
super(init.message);
|
|
6529
|
-
this.name = 'BuildsApiError';
|
|
6530
|
-
this.code = init.code;
|
|
6531
|
-
this.retryable = init.retryable;
|
|
6532
|
-
this.status = init.status;
|
|
6533
|
-
this.details = init.details;
|
|
6534
|
-
this.retryAfterMs = init.retryAfterMs;
|
|
6535
|
-
}
|
|
6536
|
-
}
|
|
6537
|
-
const memoryIdempotencyKeys = new Map();
|
|
6538
|
-
const IDEMPOTENCY_STORAGE_PREFIX = 'bounded:build:idempotency:';
|
|
6539
|
-
function retryAfterMs(response) {
|
|
6540
|
-
var _a, _b;
|
|
6541
|
-
const value = (_b = (_a = response.headers) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(_a, 'Retry-After');
|
|
6542
|
-
if (!value)
|
|
6543
|
-
return undefined;
|
|
6544
|
-
const seconds = Number(value);
|
|
6545
|
-
if (Number.isFinite(seconds) && seconds >= 0)
|
|
6546
|
-
return Math.ceil(seconds * 1000);
|
|
6547
|
-
const at = Date.parse(value);
|
|
6548
|
-
if (!Number.isFinite(at))
|
|
6549
|
-
return undefined;
|
|
6550
|
-
return Math.max(0, at - Date.now());
|
|
6551
|
-
}
|
|
6552
|
-
async function responseBody(response) {
|
|
6553
|
-
const text = await response.text();
|
|
6554
|
-
if (!text)
|
|
6555
|
-
return {};
|
|
6556
|
-
try {
|
|
6557
|
-
return JSON.parse(text);
|
|
6558
|
-
}
|
|
6559
|
-
catch (_a) {
|
|
6560
|
-
return { raw: text };
|
|
6561
|
-
}
|
|
6562
|
-
}
|
|
6563
|
-
function apiError(response, body) {
|
|
6564
|
-
const envelope = body;
|
|
6565
|
-
const nested = (envelope === null || envelope === void 0 ? void 0 : envelope.error) && typeof envelope.error === 'object'
|
|
6566
|
-
? envelope.error
|
|
6567
|
-
: undefined;
|
|
6568
|
-
const hasNestedDetails = !!nested && Object.prototype.hasOwnProperty.call(nested, 'details');
|
|
6569
|
-
const hasTopLevelDetails = !!body
|
|
6570
|
-
&& typeof body === 'object'
|
|
6571
|
-
&& Object.prototype.hasOwnProperty.call(body, 'details');
|
|
6572
|
-
const legacyCode = typeof (body === null || body === void 0 ? void 0 : body.error) === 'string' ? body.error : undefined;
|
|
6573
|
-
const code = typeof (nested === null || nested === void 0 ? void 0 : nested.code) === 'string'
|
|
6574
|
-
? nested.code
|
|
6575
|
-
: legacyCode !== null && legacyCode !== void 0 ? legacyCode : `http_${response.status}`;
|
|
6576
|
-
const message = typeof (nested === null || nested === void 0 ? void 0 : nested.message) === 'string'
|
|
6577
|
-
? nested.message
|
|
6578
|
-
: typeof (body === null || body === void 0 ? void 0 : body.message) === 'string'
|
|
6579
|
-
? body.message
|
|
6580
|
-
: `Developer API request failed with HTTP ${response.status}`;
|
|
6581
|
-
const retryable = typeof (nested === null || nested === void 0 ? void 0 : nested.retryable) === 'boolean'
|
|
6582
|
-
? nested.retryable
|
|
6583
|
-
: response.status === 429 || response.status >= 500;
|
|
6584
|
-
return new BuildsApiError(Object.assign(Object.assign({ code,
|
|
6585
|
-
message,
|
|
6586
|
-
retryable, status: response.status }, (hasNestedDetails
|
|
6587
|
-
? { details: nested === null || nested === void 0 ? void 0 : nested.details }
|
|
6588
|
-
: hasTopLevelDetails
|
|
6589
|
-
? { details: body.details }
|
|
6590
|
-
: {})), (response.status === 429 ? { retryAfterMs: retryAfterMs(response) } : {})));
|
|
6591
|
-
}
|
|
6592
|
-
/** @internal Shared authenticated developer-api transport for builds and apps. */
|
|
6593
|
-
async function requestDevApi(path, request = {}) {
|
|
6594
|
-
var _a, _b;
|
|
6595
|
-
const config = await core.getConfig();
|
|
6596
|
-
const base = (_a = config.devApiUrl) === null || _a === void 0 ? void 0 : _a.replace(/\/$/, '');
|
|
6597
|
-
if (!base) {
|
|
6598
|
-
throw new Error('Bounded developer API is not configured. Pass devApiUrl or a Bounded network to init().');
|
|
6599
|
-
}
|
|
6600
|
-
// Reuse the exact accessor exposed publicly as getIdToken().
|
|
6601
|
-
const token = await getIdToken();
|
|
6602
|
-
if (!token) {
|
|
6603
|
-
throw new BuildsApiError({
|
|
6604
|
-
code: 'auth_required',
|
|
6605
|
-
message: 'Sign in before calling the Bounded developer API.',
|
|
6606
|
-
retryable: false,
|
|
6607
|
-
status: 401,
|
|
6608
|
-
});
|
|
6609
|
-
}
|
|
6610
|
-
const hasBody = request.body !== undefined;
|
|
6611
|
-
const response = await fetch(`${base}${path}`, Object.assign({ method: (_b = request.method) !== null && _b !== void 0 ? _b : 'GET', headers: Object.assign({ Authorization: `Bearer ${token}` }, (hasBody ? { 'Content-Type': 'application/json' } : {})) }, (hasBody ? { body: JSON.stringify(request.body) } : {})));
|
|
6612
|
-
const body = await responseBody(response);
|
|
6613
|
-
if (!response.ok)
|
|
6614
|
-
throw apiError(response, body);
|
|
6615
|
-
return body;
|
|
6616
|
-
}
|
|
6617
|
-
function intentStorageKey(intentKey) {
|
|
6618
|
-
return `${IDEMPOTENCY_STORAGE_PREFIX}${intentKey}`;
|
|
6619
|
-
}
|
|
6620
|
-
function isValidIdempotencyKey(value) {
|
|
6621
|
-
return typeof value === 'string' && value.length > 0 && value.length <= 256;
|
|
6622
|
-
}
|
|
6623
|
-
function readPersistedIdempotencyKey(intentKey) {
|
|
6624
|
-
var _a;
|
|
6625
|
-
const storageKey = intentStorageKey(intentKey);
|
|
6626
|
-
try {
|
|
6627
|
-
const stored = (_a = globalThis.sessionStorage) === null || _a === void 0 ? void 0 : _a.getItem(storageKey);
|
|
6628
|
-
if (isValidIdempotencyKey(stored))
|
|
6629
|
-
return stored;
|
|
6630
|
-
}
|
|
6631
|
-
catch (_b) {
|
|
6632
|
-
// Storage can be disabled by browser privacy policy; use module memory.
|
|
6633
|
-
}
|
|
6634
|
-
const inMemory = memoryIdempotencyKeys.get(storageKey);
|
|
6635
|
-
return isValidIdempotencyKey(inMemory) ? inMemory : undefined;
|
|
6636
|
-
}
|
|
6637
|
-
function persistIdempotencyKey(intentKey, idempotencyKey) {
|
|
6638
|
-
var _a;
|
|
6639
|
-
const storageKey = intentStorageKey(intentKey);
|
|
6640
|
-
memoryIdempotencyKeys.set(storageKey, idempotencyKey);
|
|
6641
|
-
try {
|
|
6642
|
-
(_a = globalThis.sessionStorage) === null || _a === void 0 ? void 0 : _a.setItem(storageKey, idempotencyKey);
|
|
6643
|
-
}
|
|
6644
|
-
catch (_b) {
|
|
6645
|
-
// The in-memory write above is the React Native / disabled-storage fallback.
|
|
6646
|
-
}
|
|
6647
|
-
}
|
|
6648
|
-
function randomHex(bytes) {
|
|
6649
|
-
const cryptoObject = globalThis.crypto;
|
|
6650
|
-
if (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.getRandomValues) {
|
|
6651
|
-
const values = new Uint8Array(bytes);
|
|
6652
|
-
cryptoObject.getRandomValues(values);
|
|
6653
|
-
return Array.from(values, (value) => value.toString(16).padStart(2, '0')).join('');
|
|
6654
|
-
}
|
|
6655
|
-
let value = '';
|
|
6656
|
-
for (let index = 0; index < bytes; index += 1) {
|
|
6657
|
-
value += Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
|
|
6658
|
-
}
|
|
6659
|
-
return value;
|
|
6660
|
-
}
|
|
6661
|
-
function mintIdempotencyKey() {
|
|
6662
|
-
const cryptoObject = globalThis.crypto;
|
|
6663
|
-
if (typeof (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.randomUUID) === 'function') {
|
|
6664
|
-
return `build_${cryptoObject.randomUUID()}`;
|
|
6665
|
-
}
|
|
6666
|
-
return `build_${Date.now().toString(36)}_${randomHex(16)}`;
|
|
6667
|
-
}
|
|
6668
|
-
function actionIdempotencyKey(options, action) {
|
|
6669
|
-
const supplied = options.idempotencyKey;
|
|
6670
|
-
if (supplied !== undefined) {
|
|
6671
|
-
if (!isValidIdempotencyKey(supplied)) {
|
|
6672
|
-
throw new TypeError('idempotencyKey must be between 1 and 256 characters.');
|
|
6673
|
-
}
|
|
6674
|
-
if (options.intentKey)
|
|
6675
|
-
persistIdempotencyKey(options.intentKey, supplied);
|
|
6676
|
-
return supplied;
|
|
6677
|
-
}
|
|
6678
|
-
if (!options.intentKey) {
|
|
6679
|
-
throw new TypeError(`intentKey is required when ${action} auto-mints an idempotency key.`);
|
|
6680
|
-
}
|
|
6681
|
-
const existing = readPersistedIdempotencyKey(options.intentKey);
|
|
6682
|
-
if (existing)
|
|
6683
|
-
return existing;
|
|
6684
|
-
const minted = mintIdempotencyKey();
|
|
6685
|
-
// This is deliberately synchronous and happens before requestDevApi can fetch.
|
|
6686
|
-
persistIdempotencyKey(options.intentKey, minted);
|
|
6687
|
-
return minted;
|
|
6688
|
-
}
|
|
6689
|
-
function appPath(appId) {
|
|
6690
|
-
return `/app/${encodeURIComponent(appId)}/builds`;
|
|
6691
|
-
}
|
|
6692
|
-
async function submit(appId, options) {
|
|
6693
|
-
const idempotencyKey = actionIdempotencyKey(options, 'submit()');
|
|
6694
|
-
const { intentKey: _intentKey, idempotencyKey: _suppliedKey } = options, submission = __rest(options, ["intentKey", "idempotencyKey"]);
|
|
6695
|
-
return requestDevApi(appPath(appId), {
|
|
6696
|
-
method: 'POST',
|
|
6697
|
-
body: Object.assign(Object.assign({}, submission), { idempotencyKey }),
|
|
6698
|
-
});
|
|
6699
|
-
}
|
|
6700
|
-
async function get(appId, runId) {
|
|
6701
|
-
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}`);
|
|
6702
|
-
}
|
|
6703
|
-
async function events(appId, runId, after = 0) {
|
|
6704
|
-
const cursor = Number.isFinite(after) ? Math.max(0, Math.floor(after)) : 0;
|
|
6705
|
-
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/events?after=${cursor}`);
|
|
6706
|
-
}
|
|
6707
|
-
async function cancel(appId, runId) {
|
|
6708
|
-
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/cancel`, { method: 'POST' });
|
|
6709
|
-
}
|
|
6710
|
-
async function decideGate(appId, runId, gateId, options) {
|
|
6711
|
-
const idempotencyKey = actionIdempotencyKey(options, 'decideGate()');
|
|
6712
|
-
const { intentKey: _intentKey, idempotencyKey: _suppliedKey } = options, decision = __rest(options, ["intentKey", "idempotencyKey"]);
|
|
6713
|
-
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/gates/${encodeURIComponent(gateId)}/decision`, { method: 'POST', body: Object.assign(Object.assign({}, decision), { idempotencyKey }) });
|
|
6714
|
-
}
|
|
6715
|
-
async function refreshReviewCapability(appId, runId, gateId, options) {
|
|
6716
|
-
const idempotencyKey = actionIdempotencyKey(options, 'refreshReviewCapability()');
|
|
6717
|
-
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/gates/${encodeURIComponent(gateId)}/review-capability/refresh`, {
|
|
6718
|
-
method: 'POST',
|
|
6719
|
-
body: {
|
|
6720
|
-
expectedGeneration: options.expectedGeneration,
|
|
6721
|
-
expectedExpiresAtMs: options.expectedExpiresAtMs,
|
|
6722
|
-
expectedRecoveryUntilMs: options.expectedRecoveryUntilMs,
|
|
6723
|
-
expectedAudience: options.expectedAudience,
|
|
6724
|
-
expectedReviewerBindingHash: options.expectedReviewerBindingHash,
|
|
6725
|
-
idempotencyKey,
|
|
6726
|
-
},
|
|
6727
|
-
});
|
|
6728
|
-
}
|
|
6729
|
-
/**
|
|
6730
|
-
* Read one exact delegated-review capability and its narrow lifecycle result.
|
|
6731
|
-
* This does not refresh or extend the window and exposes no generic run-view
|
|
6732
|
-
* authority.
|
|
6733
|
-
*/
|
|
6734
|
-
async function getReviewCapability(appId, runId, gateId, options) {
|
|
6735
|
-
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/gates/${encodeURIComponent(gateId)}/review-capability`, {
|
|
6736
|
-
method: 'POST',
|
|
6737
|
-
body: { expectedPreviewUrl: options.expectedPreviewUrl },
|
|
6738
|
-
});
|
|
6739
|
-
}
|
|
6740
|
-
async function topup(appId, runId, options) {
|
|
6741
|
-
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/topup`, { method: 'POST', body: options });
|
|
6742
|
-
}
|
|
6743
|
-
async function list$1(appId, limit = 20) {
|
|
6744
|
-
const requestedLimit = Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 20;
|
|
6745
|
-
return requestDevApi(`${appPath(appId)}?limit=${requestedLimit}`);
|
|
6746
|
-
}
|
|
6747
|
-
|
|
6748
|
-
// Source of truth for these strings:
|
|
6749
|
-
// packages/cdk/cloudflare/bounded-build/src/types.ts. Keep them string-pinned;
|
|
6750
|
-
// wire:1 remains additive and callers must tolerate unknown response strings.
|
|
6751
|
-
const NON_TERMINAL_STATES = [
|
|
6752
|
-
'queued',
|
|
6753
|
-
'admitted',
|
|
6754
|
-
'executing',
|
|
6755
|
-
'preview_ready',
|
|
6756
|
-
'parked',
|
|
6757
|
-
'resuming',
|
|
6758
|
-
'promoting',
|
|
6759
|
-
'quarantined',
|
|
6760
|
-
];
|
|
6761
|
-
const TERMINAL_STATES = [
|
|
6762
|
-
'promoted',
|
|
6763
|
-
'failed',
|
|
6764
|
-
'canceled',
|
|
6765
|
-
'rejected',
|
|
6766
|
-
'expired',
|
|
6767
|
-
'rebase_required',
|
|
6768
|
-
'reported',
|
|
6769
|
-
];
|
|
6770
|
-
const RUN_STATES = [
|
|
6771
|
-
...NON_TERMINAL_STATES,
|
|
6772
|
-
...TERMINAL_STATES,
|
|
6773
|
-
];
|
|
6774
|
-
const PARK_REASONS = ['gate', 'funding'];
|
|
6775
|
-
const FAILURE_REASONS = [
|
|
6776
|
-
'admission_rejected',
|
|
6777
|
-
'dispatch_timeout',
|
|
6778
|
-
'auth_revoked',
|
|
6779
|
-
'owner_changed',
|
|
6780
|
-
'policy_changed',
|
|
6781
|
-
'quarantine_forced',
|
|
6782
|
-
'bind_failed',
|
|
6783
|
-
'internal_error',
|
|
6784
|
-
];
|
|
6785
|
-
const BUILD_REVIEW_CAPABILITY_SCHEMA = 'bounded.build-review-capability.v1';
|
|
6786
|
-
const BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA = 'bounded.build-review-capability-refresh.v1';
|
|
6787
|
-
const BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA = 'bounded.build-delegated-review-capability.v1';
|
|
6788
|
-
|
|
6789
|
-
const FAST_POLL_MS = 1000;
|
|
6790
|
-
const NORMAL_POLL_MS = 2000;
|
|
6791
|
-
const PARKED_POLL_MS = 5000;
|
|
6792
|
-
const LONG_RUNNING_POLL_MS = 10000;
|
|
6793
|
-
const THIRTY_SECONDS_MS = 30000;
|
|
6794
|
-
const TWO_MINUTES_MS = 2 * 60000;
|
|
6795
|
-
const TEN_MINUTES_MS = 10 * 60000;
|
|
6796
|
-
const terminalStates = new Set(TERMINAL_STATES);
|
|
6797
|
-
function pollCadence(state, elapsedMs) {
|
|
6798
|
-
if (elapsedMs >= TEN_MINUTES_MS)
|
|
6799
|
-
return LONG_RUNNING_POLL_MS;
|
|
6800
|
-
if ((state === null || state === void 0 ? void 0 : state.startsWith('parked')) || elapsedMs >= TWO_MINUTES_MS)
|
|
6801
|
-
return PARKED_POLL_MS;
|
|
6802
|
-
// The first 30s is where the user is staring at "waiting for a build slot" /
|
|
6803
|
-
// "building" and every transition matters most; poll fast, then relax.
|
|
6804
|
-
if (elapsedMs < THIRTY_SECONDS_MS)
|
|
6805
|
-
return FAST_POLL_MS;
|
|
6806
|
-
return NORMAL_POLL_MS;
|
|
6807
|
-
}
|
|
6808
|
-
function retryCadence(error, state, elapsedMs) {
|
|
6809
|
-
const cadence = pollCadence(state, elapsedMs);
|
|
6810
|
-
if (error instanceof BuildsApiError && error.status === 429 && error.retryAfterMs !== undefined) {
|
|
6811
|
-
return Math.max(cadence, error.retryAfterMs);
|
|
6812
|
-
}
|
|
6813
|
-
return cadence;
|
|
6814
|
-
}
|
|
6815
|
-
function hasRetryAfter(error) {
|
|
6816
|
-
return error instanceof BuildsApiError
|
|
6817
|
-
&& error.status === 429
|
|
6818
|
-
&& error.retryAfterMs !== undefined;
|
|
6819
|
-
}
|
|
6820
|
-
// A permanent API refusal (expired session, revoked access, unknown run) will
|
|
6821
|
-
// never heal by re-polling; the loop must stop and surface it instead of
|
|
6822
|
-
// retrying forever. 429 stays retryable regardless of the server envelope.
|
|
6823
|
-
function isPermanentError(error) {
|
|
6824
|
-
return error instanceof BuildsApiError
|
|
6825
|
-
&& error.status !== 429
|
|
6826
|
-
&& error.retryable === false;
|
|
6827
|
-
}
|
|
6828
|
-
function newEvents(rows, lastSeq, seen) {
|
|
6829
|
-
const cursorBeforePoll = lastSeq;
|
|
6830
|
-
const fresh = [];
|
|
6831
|
-
let nextCursor = lastSeq;
|
|
6832
|
-
for (const event of rows) {
|
|
6833
|
-
if (!Number.isFinite(event === null || event === void 0 ? void 0 : event.seq))
|
|
6834
|
-
continue;
|
|
6835
|
-
nextCursor = Math.max(nextCursor, event.seq);
|
|
6836
|
-
if (event.seq <= cursorBeforePoll || seen.has(event.seq))
|
|
6837
|
-
continue;
|
|
6838
|
-
seen.add(event.seq);
|
|
6839
|
-
fresh.push(event);
|
|
6840
|
-
}
|
|
6841
|
-
return { events: fresh, lastSeq: nextCursor };
|
|
6842
|
-
}
|
|
6843
|
-
/**
|
|
6844
|
-
* Poll a build view and its event cursor until stopped or the run reaches a
|
|
6845
|
-
* known terminal state. Unknown state strings remain untouched and nonterminal.
|
|
6846
|
-
*/
|
|
6847
|
-
function watch(appId, runId, onUpdate, options = {}) {
|
|
6848
|
-
const startedAt = Date.now();
|
|
6849
|
-
const seenSeqs = new Set();
|
|
6850
|
-
let lastSeq = Number.isFinite(options.after)
|
|
6851
|
-
? Math.max(0, Math.floor(options.after))
|
|
6852
|
-
: 0;
|
|
6853
|
-
let timer;
|
|
6854
|
-
let active = true;
|
|
6855
|
-
let lastState;
|
|
6856
|
-
const stop = () => {
|
|
6857
|
-
active = false;
|
|
6858
|
-
if (timer !== undefined) {
|
|
6859
|
-
clearTimeout(timer);
|
|
6860
|
-
timer = undefined;
|
|
6861
|
-
}
|
|
6862
|
-
};
|
|
6863
|
-
const schedule = (delayMs) => {
|
|
6864
|
-
if (!active)
|
|
6865
|
-
return;
|
|
6866
|
-
timer = setTimeout(() => {
|
|
6867
|
-
timer = undefined;
|
|
6868
|
-
void poll();
|
|
6869
|
-
}, delayMs);
|
|
6870
|
-
};
|
|
6871
|
-
const reportError = (error) => {
|
|
6872
|
-
var _a;
|
|
6873
|
-
try {
|
|
6874
|
-
(_a = options.onError) === null || _a === void 0 ? void 0 : _a.call(options, error);
|
|
6875
|
-
}
|
|
6876
|
-
catch (_b) {
|
|
6877
|
-
// A consumer error callback must not break the transport loop.
|
|
6878
|
-
}
|
|
6879
|
-
};
|
|
6880
|
-
const publish = (run, events) => {
|
|
6881
|
-
try {
|
|
6882
|
-
onUpdate({ run, events });
|
|
6883
|
-
}
|
|
6884
|
-
catch (_a) {
|
|
6885
|
-
// Polling is transport state; consumer render errors must not strand it.
|
|
6886
|
-
}
|
|
6887
|
-
};
|
|
6888
|
-
const poll = async () => {
|
|
6889
|
-
if (!active)
|
|
6890
|
-
return;
|
|
6891
|
-
// Fire the view and the events page concurrently: the events cursor (lastSeq)
|
|
6892
|
-
// does not depend on the run view, so there is no reason to pay two serial
|
|
6893
|
-
// round-trips per tick. Error semantics below are unchanged — the run result
|
|
6894
|
-
// still gates, and a failed events page still publishes the run with no rows.
|
|
6895
|
-
const runPromise = get(appId, runId);
|
|
6896
|
-
const eventsPromise = events(appId, runId, lastSeq);
|
|
6897
|
-
// Keep the in-flight events page from becoming an unhandled rejection if the
|
|
6898
|
-
// run result makes us bail before awaiting it; the real await below still sees it.
|
|
6899
|
-
eventsPromise.catch(() => { });
|
|
6900
|
-
let run;
|
|
6901
|
-
try {
|
|
6902
|
-
const view = await runPromise;
|
|
6903
|
-
if (!active)
|
|
6904
|
-
return;
|
|
6905
|
-
run = view.run;
|
|
6906
|
-
lastState = run.state;
|
|
6907
|
-
}
|
|
6908
|
-
catch (error) {
|
|
6909
|
-
if (!active)
|
|
6910
|
-
return;
|
|
6911
|
-
reportError(error);
|
|
6912
|
-
if (isPermanentError(error)) {
|
|
6913
|
-
stop();
|
|
6914
|
-
return;
|
|
6915
|
-
}
|
|
6916
|
-
schedule(retryCadence(error, lastState, Date.now() - startedAt));
|
|
6917
|
-
return;
|
|
6918
|
-
}
|
|
6919
|
-
try {
|
|
6920
|
-
const response = await eventsPromise;
|
|
6921
|
-
if (!active)
|
|
6922
|
-
return;
|
|
6923
|
-
const deduped = newEvents(Array.isArray(response.events) ? response.events : [], lastSeq, seenSeqs);
|
|
6924
|
-
lastSeq = deduped.lastSeq;
|
|
6925
|
-
publish(run, deduped.events);
|
|
6926
|
-
}
|
|
6927
|
-
catch (error) {
|
|
6928
|
-
if (!active)
|
|
6929
|
-
return;
|
|
6930
|
-
// The view still advances UI state even when event replay is rate-limited.
|
|
6931
|
-
publish(run, []);
|
|
6932
|
-
reportError(error);
|
|
6933
|
-
if (terminalStates.has(run.state)) {
|
|
6934
|
-
// The run itself is done, but a rate-limited final event replay still
|
|
6935
|
-
// owes the caller its unseen seq rows. Honor Retry-After, retry the
|
|
6936
|
-
// piggyback once the limiter permits it, then stop after a successful
|
|
6937
|
-
// replay. Other terminal replay failures stop normally.
|
|
6938
|
-
if (hasRetryAfter(error)) {
|
|
6939
|
-
schedule(retryCadence(error, run.state, Date.now() - startedAt));
|
|
6940
|
-
return;
|
|
6941
|
-
}
|
|
6942
|
-
stop();
|
|
6943
|
-
return;
|
|
6944
|
-
}
|
|
6945
|
-
if (isPermanentError(error)) {
|
|
6946
|
-
stop();
|
|
6947
|
-
return;
|
|
6948
|
-
}
|
|
6949
|
-
schedule(retryCadence(error, run.state, Date.now() - startedAt));
|
|
6950
|
-
return;
|
|
6951
|
-
}
|
|
6952
|
-
if (!active)
|
|
6953
|
-
return;
|
|
6954
|
-
if (terminalStates.has(run.state)) {
|
|
6955
|
-
// Drain the remaining event pages before stopping: a resumed terminal
|
|
6956
|
-
// watch may owe the caller more rows than one page returns. Each page
|
|
6957
|
-
// advances the cursor, so the loop terminates; the cap is a backstop.
|
|
6958
|
-
for (let page = 0; page < 50 && active; page += 1) {
|
|
6959
|
-
let drained;
|
|
6960
|
-
try {
|
|
6961
|
-
const response = await events(appId, runId, lastSeq);
|
|
6962
|
-
if (!active)
|
|
6963
|
-
return;
|
|
6964
|
-
drained = newEvents(Array.isArray(response.events) ? response.events : [], lastSeq, seenSeqs);
|
|
6965
|
-
}
|
|
6966
|
-
catch (error) {
|
|
6967
|
-
reportError(error);
|
|
6968
|
-
break;
|
|
6969
|
-
}
|
|
6970
|
-
if (drained.events.length === 0)
|
|
6971
|
-
break;
|
|
6972
|
-
lastSeq = drained.lastSeq;
|
|
6973
|
-
publish(run, drained.events);
|
|
6974
|
-
}
|
|
6975
|
-
stop();
|
|
6976
|
-
return;
|
|
6558
|
+
}
|
|
6559
|
+
setUser(null);
|
|
6560
|
+
}
|
|
6561
|
+
finally {
|
|
6562
|
+
setLoading(false);
|
|
6977
6563
|
}
|
|
6978
|
-
schedule(pollCadence(run.state, Date.now() - startedAt));
|
|
6979
6564
|
};
|
|
6980
|
-
|
|
6981
|
-
|
|
6565
|
+
const logout$1 = async () => {
|
|
6566
|
+
try {
|
|
6567
|
+
setLoading(true);
|
|
6568
|
+
await logout();
|
|
6569
|
+
setUser(null);
|
|
6570
|
+
}
|
|
6571
|
+
catch (error) {
|
|
6572
|
+
console.error('Error logging out:', error);
|
|
6573
|
+
}
|
|
6574
|
+
finally {
|
|
6575
|
+
setLoading(false);
|
|
6576
|
+
}
|
|
6577
|
+
};
|
|
6578
|
+
return {
|
|
6579
|
+
login: login$1,
|
|
6580
|
+
logout: logout$1,
|
|
6581
|
+
loading: loading || sdkLoading,
|
|
6582
|
+
user,
|
|
6583
|
+
};
|
|
6982
6584
|
}
|
|
6983
6585
|
|
|
6984
|
-
var _a;
|
|
6586
|
+
var _a$1;
|
|
6985
6587
|
// SSR guard (mirrors useAuth): no hooks/subscriptions during Node server render.
|
|
6986
6588
|
// React Native has no `window` but DOES support hooks, so detect Node SSR
|
|
6987
6589
|
// specifically via process.versions.node.
|
|
6988
|
-
const isSSR = typeof window === 'undefined'
|
|
6590
|
+
const isSSR$1 = typeof window === 'undefined'
|
|
6989
6591
|
&& typeof process !== 'undefined'
|
|
6990
|
-
&& !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
|
|
6991
|
-
function
|
|
6992
|
-
if (isSSR) {
|
|
6993
|
-
return {
|
|
6592
|
+
&& !!((_a$1 = process.versions) === null || _a$1 === void 0 ? void 0 : _a$1.node);
|
|
6593
|
+
function useQuery(path, options) {
|
|
6594
|
+
if (isSSR$1) {
|
|
6595
|
+
return { data: undefined, loading: true, error: null };
|
|
6994
6596
|
}
|
|
6995
|
-
const [
|
|
6996
|
-
const [events, setEvents] = React__namespace.useState([]);
|
|
6597
|
+
const [data, setData] = React__namespace.useState(undefined);
|
|
6997
6598
|
const [loading, setLoading] = React__namespace.useState(true);
|
|
6998
6599
|
const [error, setError] = React__namespace.useState(null);
|
|
6600
|
+
// Re-subscribe only when the query actually changes (not on every render).
|
|
6999
6601
|
const optionsKey = options ? JSON.stringify(options) : '';
|
|
7000
6602
|
React__namespace.useEffect(() => {
|
|
7001
|
-
|
|
7002
|
-
|
|
7003
|
-
setError(null);
|
|
7004
|
-
if (!appId || !runId) {
|
|
6603
|
+
if (!path) {
|
|
6604
|
+
setData(undefined);
|
|
7005
6605
|
setLoading(false);
|
|
6606
|
+
setError(null);
|
|
7006
6607
|
return;
|
|
7007
6608
|
}
|
|
7008
6609
|
let active = true;
|
|
6610
|
+
let unsubscribe;
|
|
7009
6611
|
setLoading(true);
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
return;
|
|
7013
|
-
setRun(nextRun);
|
|
7014
|
-
if (nextEvents.length > 0) {
|
|
7015
|
-
setEvents((current) => {
|
|
7016
|
-
const seen = new Set(current.map((event) => event.seq));
|
|
7017
|
-
return [...current, ...nextEvents.filter((event) => !seen.has(event.seq))];
|
|
7018
|
-
});
|
|
7019
|
-
}
|
|
7020
|
-
setError(null);
|
|
7021
|
-
setLoading(false);
|
|
7022
|
-
}, Object.assign(Object.assign({}, options), { onError: (value) => {
|
|
6612
|
+
setError(null);
|
|
6613
|
+
core.subscribe(path, Object.assign(Object.assign({}, options), { onData: (d) => {
|
|
7023
6614
|
if (!active)
|
|
7024
6615
|
return;
|
|
7025
|
-
|
|
6616
|
+
// A reconnect can legitimately surface a transport error before the
|
|
6617
|
+
// socket re-authenticates and sends a new authorized snapshot. That
|
|
6618
|
+
// fresh snapshot is the recovery boundary: keep the error visible
|
|
6619
|
+
// until data arrives, then clear it so React does not remain stuck on
|
|
6620
|
+
// a failure page after realtime has recovered.
|
|
6621
|
+
setError(null);
|
|
6622
|
+
setData(d);
|
|
7026
6623
|
setLoading(false);
|
|
7027
|
-
}
|
|
6624
|
+
}, onError: (e) => {
|
|
6625
|
+
if (!active)
|
|
6626
|
+
return;
|
|
6627
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
6628
|
+
setLoading(false);
|
|
6629
|
+
} }))
|
|
6630
|
+
.then((unsub) => {
|
|
6631
|
+
if (active)
|
|
6632
|
+
unsubscribe = unsub;
|
|
6633
|
+
else
|
|
6634
|
+
void unsub(); // unmounted before subscribe resolved
|
|
6635
|
+
})
|
|
6636
|
+
.catch((e) => {
|
|
6637
|
+
if (!active)
|
|
6638
|
+
return;
|
|
6639
|
+
setError(e instanceof Error ? e : new Error(String(e)));
|
|
6640
|
+
setLoading(false);
|
|
6641
|
+
});
|
|
7028
6642
|
return () => {
|
|
7029
6643
|
active = false;
|
|
7030
|
-
|
|
6644
|
+
void (unsubscribe === null || unsubscribe === void 0 ? void 0 : unsubscribe());
|
|
7031
6645
|
};
|
|
7032
|
-
// optionsKey captures the option object's contents;
|
|
6646
|
+
// optionsKey captures the option object's contents; path is the other input.
|
|
7033
6647
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7034
|
-
}, [
|
|
7035
|
-
return {
|
|
6648
|
+
}, [path, optionsKey]);
|
|
6649
|
+
return { data, loading, error };
|
|
6650
|
+
}
|
|
6651
|
+
|
|
6652
|
+
// Wrapper for getIdToken - passes isServer=false for client-side usage
|
|
6653
|
+
async function getIdToken() {
|
|
6654
|
+
return core.getIdToken(false);
|
|
6655
|
+
}
|
|
6656
|
+
|
|
6657
|
+
var __rest = (undefined && undefined.__rest) || function (s, e) {
|
|
6658
|
+
var t = {};
|
|
6659
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
6660
|
+
t[p] = s[p];
|
|
6661
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
6662
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
6663
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
6664
|
+
t[p[i]] = s[p[i]];
|
|
6665
|
+
}
|
|
6666
|
+
return t;
|
|
6667
|
+
};
|
|
6668
|
+
/** A structured non-2xx response from the developer API. */
|
|
6669
|
+
class BuildsApiError extends Error {
|
|
6670
|
+
constructor(init) {
|
|
6671
|
+
super(init.message);
|
|
6672
|
+
this.name = 'BuildsApiError';
|
|
6673
|
+
this.code = init.code;
|
|
6674
|
+
this.retryable = init.retryable;
|
|
6675
|
+
this.status = init.status;
|
|
6676
|
+
this.details = init.details;
|
|
6677
|
+
this.retryAfterMs = init.retryAfterMs;
|
|
6678
|
+
}
|
|
6679
|
+
}
|
|
6680
|
+
const memoryIdempotencyKeys = new Map();
|
|
6681
|
+
const IDEMPOTENCY_STORAGE_PREFIX = 'bounded:build:idempotency:';
|
|
6682
|
+
function retryAfterMs(response) {
|
|
6683
|
+
var _a, _b;
|
|
6684
|
+
const value = (_b = (_a = response.headers) === null || _a === void 0 ? void 0 : _a.get) === null || _b === void 0 ? void 0 : _b.call(_a, 'Retry-After');
|
|
6685
|
+
if (!value)
|
|
6686
|
+
return undefined;
|
|
6687
|
+
const seconds = Number(value);
|
|
6688
|
+
if (Number.isFinite(seconds) && seconds >= 0)
|
|
6689
|
+
return Math.ceil(seconds * 1000);
|
|
6690
|
+
const at = Date.parse(value);
|
|
6691
|
+
if (!Number.isFinite(at))
|
|
6692
|
+
return undefined;
|
|
6693
|
+
return Math.max(0, at - Date.now());
|
|
6694
|
+
}
|
|
6695
|
+
async function responseBody(response) {
|
|
6696
|
+
const text = await response.text();
|
|
6697
|
+
if (!text)
|
|
6698
|
+
return {};
|
|
6699
|
+
try {
|
|
6700
|
+
return JSON.parse(text);
|
|
6701
|
+
}
|
|
6702
|
+
catch (_a) {
|
|
6703
|
+
return { raw: text };
|
|
6704
|
+
}
|
|
6705
|
+
}
|
|
6706
|
+
function apiError(response, body) {
|
|
6707
|
+
const envelope = body;
|
|
6708
|
+
const nested = (envelope === null || envelope === void 0 ? void 0 : envelope.error) && typeof envelope.error === 'object'
|
|
6709
|
+
? envelope.error
|
|
6710
|
+
: undefined;
|
|
6711
|
+
const hasNestedDetails = !!nested && Object.prototype.hasOwnProperty.call(nested, 'details');
|
|
6712
|
+
const hasTopLevelDetails = !!body
|
|
6713
|
+
&& typeof body === 'object'
|
|
6714
|
+
&& Object.prototype.hasOwnProperty.call(body, 'details');
|
|
6715
|
+
const legacyCode = typeof (body === null || body === void 0 ? void 0 : body.error) === 'string' ? body.error : undefined;
|
|
6716
|
+
const code = typeof (nested === null || nested === void 0 ? void 0 : nested.code) === 'string'
|
|
6717
|
+
? nested.code
|
|
6718
|
+
: legacyCode !== null && legacyCode !== void 0 ? legacyCode : `http_${response.status}`;
|
|
6719
|
+
const message = typeof (nested === null || nested === void 0 ? void 0 : nested.message) === 'string'
|
|
6720
|
+
? nested.message
|
|
6721
|
+
: typeof (body === null || body === void 0 ? void 0 : body.message) === 'string'
|
|
6722
|
+
? body.message
|
|
6723
|
+
: `Developer API request failed with HTTP ${response.status}`;
|
|
6724
|
+
const retryable = typeof (nested === null || nested === void 0 ? void 0 : nested.retryable) === 'boolean'
|
|
6725
|
+
? nested.retryable
|
|
6726
|
+
: response.status === 429 || response.status >= 500;
|
|
6727
|
+
return new BuildsApiError(Object.assign(Object.assign({ code,
|
|
6728
|
+
message,
|
|
6729
|
+
retryable, status: response.status }, (hasNestedDetails
|
|
6730
|
+
? { details: nested === null || nested === void 0 ? void 0 : nested.details }
|
|
6731
|
+
: hasTopLevelDetails
|
|
6732
|
+
? { details: body.details }
|
|
6733
|
+
: {})), (response.status === 429 ? { retryAfterMs: retryAfterMs(response) } : {})));
|
|
6734
|
+
}
|
|
6735
|
+
/** @internal Shared authenticated developer-api transport for builds and apps. */
|
|
6736
|
+
async function requestDevApi(path, request = {}) {
|
|
6737
|
+
var _a, _b;
|
|
6738
|
+
const config = await core.getConfig();
|
|
6739
|
+
const base = (_a = config.devApiUrl) === null || _a === void 0 ? void 0 : _a.replace(/\/$/, '');
|
|
6740
|
+
if (!base) {
|
|
6741
|
+
throw new Error('Bounded developer API is not configured. Pass devApiUrl or a Bounded network to init().');
|
|
6742
|
+
}
|
|
6743
|
+
// Reuse the exact accessor exposed publicly as getIdToken().
|
|
6744
|
+
const token = await getIdToken();
|
|
6745
|
+
if (!token) {
|
|
6746
|
+
throw new BuildsApiError({
|
|
6747
|
+
code: 'auth_required',
|
|
6748
|
+
message: 'Sign in before calling the Bounded developer API.',
|
|
6749
|
+
retryable: false,
|
|
6750
|
+
status: 401,
|
|
6751
|
+
});
|
|
6752
|
+
}
|
|
6753
|
+
const hasBody = request.body !== undefined;
|
|
6754
|
+
const response = await fetch(`${base}${path}`, Object.assign({ method: (_b = request.method) !== null && _b !== void 0 ? _b : 'GET', headers: Object.assign({ Authorization: `Bearer ${token}` }, (hasBody ? { 'Content-Type': 'application/json' } : {})) }, (hasBody ? { body: JSON.stringify(request.body) } : {})));
|
|
6755
|
+
const body = await responseBody(response);
|
|
6756
|
+
if (!response.ok)
|
|
6757
|
+
throw apiError(response, body);
|
|
6758
|
+
return body;
|
|
6759
|
+
}
|
|
6760
|
+
function intentStorageKey(intentKey) {
|
|
6761
|
+
return `${IDEMPOTENCY_STORAGE_PREFIX}${intentKey}`;
|
|
6762
|
+
}
|
|
6763
|
+
function isValidIdempotencyKey(value) {
|
|
6764
|
+
return typeof value === 'string' && value.length > 0 && value.length <= 256;
|
|
6765
|
+
}
|
|
6766
|
+
function readPersistedIdempotencyKey(intentKey) {
|
|
6767
|
+
var _a;
|
|
6768
|
+
const storageKey = intentStorageKey(intentKey);
|
|
6769
|
+
try {
|
|
6770
|
+
const stored = (_a = globalThis.sessionStorage) === null || _a === void 0 ? void 0 : _a.getItem(storageKey);
|
|
6771
|
+
if (isValidIdempotencyKey(stored))
|
|
6772
|
+
return stored;
|
|
6773
|
+
}
|
|
6774
|
+
catch (_b) {
|
|
6775
|
+
// Storage can be disabled by browser privacy policy; use module memory.
|
|
6776
|
+
}
|
|
6777
|
+
const inMemory = memoryIdempotencyKeys.get(storageKey);
|
|
6778
|
+
return isValidIdempotencyKey(inMemory) ? inMemory : undefined;
|
|
6779
|
+
}
|
|
6780
|
+
function persistIdempotencyKey(intentKey, idempotencyKey) {
|
|
6781
|
+
var _a;
|
|
6782
|
+
const storageKey = intentStorageKey(intentKey);
|
|
6783
|
+
memoryIdempotencyKeys.set(storageKey, idempotencyKey);
|
|
6784
|
+
try {
|
|
6785
|
+
(_a = globalThis.sessionStorage) === null || _a === void 0 ? void 0 : _a.setItem(storageKey, idempotencyKey);
|
|
6786
|
+
}
|
|
6787
|
+
catch (_b) {
|
|
6788
|
+
// The in-memory write above is the React Native / disabled-storage fallback.
|
|
6789
|
+
}
|
|
6790
|
+
}
|
|
6791
|
+
function randomHex(bytes) {
|
|
6792
|
+
const cryptoObject = globalThis.crypto;
|
|
6793
|
+
if (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.getRandomValues) {
|
|
6794
|
+
const values = new Uint8Array(bytes);
|
|
6795
|
+
cryptoObject.getRandomValues(values);
|
|
6796
|
+
return Array.from(values, (value) => value.toString(16).padStart(2, '0')).join('');
|
|
6797
|
+
}
|
|
6798
|
+
let value = '';
|
|
6799
|
+
for (let index = 0; index < bytes; index += 1) {
|
|
6800
|
+
value += Math.floor(Math.random() * 256).toString(16).padStart(2, '0');
|
|
6801
|
+
}
|
|
6802
|
+
return value;
|
|
6803
|
+
}
|
|
6804
|
+
function mintIdempotencyKey() {
|
|
6805
|
+
const cryptoObject = globalThis.crypto;
|
|
6806
|
+
if (typeof (cryptoObject === null || cryptoObject === void 0 ? void 0 : cryptoObject.randomUUID) === 'function') {
|
|
6807
|
+
return `build_${cryptoObject.randomUUID()}`;
|
|
6808
|
+
}
|
|
6809
|
+
return `build_${Date.now().toString(36)}_${randomHex(16)}`;
|
|
7036
6810
|
}
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
|
|
6811
|
+
function actionIdempotencyKey(options, action) {
|
|
6812
|
+
const supplied = options.idempotencyKey;
|
|
6813
|
+
if (supplied !== undefined) {
|
|
6814
|
+
if (!isValidIdempotencyKey(supplied)) {
|
|
6815
|
+
throw new TypeError('idempotencyKey must be between 1 and 256 characters.');
|
|
6816
|
+
}
|
|
6817
|
+
if (options.intentKey)
|
|
6818
|
+
persistIdempotencyKey(options.intentKey, supplied);
|
|
6819
|
+
return supplied;
|
|
6820
|
+
}
|
|
6821
|
+
if (!options.intentKey) {
|
|
6822
|
+
throw new TypeError(`intentKey is required when ${action} auto-mints an idempotency key.`);
|
|
6823
|
+
}
|
|
6824
|
+
const existing = readPersistedIdempotencyKey(options.intentKey);
|
|
6825
|
+
if (existing)
|
|
6826
|
+
return existing;
|
|
6827
|
+
const minted = mintIdempotencyKey();
|
|
6828
|
+
// This is deliberately synchronous and happens before requestDevApi can fetch.
|
|
6829
|
+
persistIdempotencyKey(options.intentKey, minted);
|
|
6830
|
+
return minted;
|
|
6831
|
+
}
|
|
6832
|
+
function appPath(appId) {
|
|
6833
|
+
return `/app/${encodeURIComponent(appId)}/builds`;
|
|
6834
|
+
}
|
|
6835
|
+
async function submit(appId, options) {
|
|
6836
|
+
const idempotencyKey = actionIdempotencyKey(options, 'submit()');
|
|
6837
|
+
const { intentKey: _intentKey, idempotencyKey: _suppliedKey } = options, submission = __rest(options, ["intentKey", "idempotencyKey"]);
|
|
6838
|
+
return requestDevApi(appPath(appId), {
|
|
7059
6839
|
method: 'POST',
|
|
7060
|
-
body,
|
|
6840
|
+
body: Object.assign(Object.assign({}, submission), { idempotencyKey }),
|
|
7061
6841
|
});
|
|
7062
|
-
return Object.assign(Object.assign(Object.assign(Object.assign({ appId: app.appId }, (typeof app.slug === 'string' && app.slug ? { slug: app.slug } : {})), { url: app.url }), (typeof app.prompt === 'string' && app.prompt ? { prompt: app.prompt } : {})), (typeof app.handoff === 'string' && app.handoff ? { handoff: app.handoff } : {}));
|
|
7063
6842
|
}
|
|
7064
|
-
|
|
7065
|
-
|
|
7066
|
-
* prompt and handoff carried in the URL FRAGMENT (never the query - fragments never reach
|
|
7067
|
-
* servers, logs, or Referer). Encoding is byte-exact so the widget's `URLSearchParams`
|
|
7068
|
-
* parse + `.trim()` reproduces the SAME prompt string dev-api hashed into the handoff;
|
|
7069
|
-
* getting this wrong would make the widget reject a genuine token and degrade to prefill.
|
|
7070
|
-
* Callers (bounded.sh, oapps.fun) should use this instead of hand-building the URL.
|
|
7071
|
-
*
|
|
7072
|
-
* Returns `${url}#bounded_prompt=<prompt>` with no `&bounded_handoff=` when the server
|
|
7073
|
-
* minted no handoff (HANDOFF_SECRET unset) - the widget then prefills without auto-running.
|
|
7074
|
-
*/
|
|
7075
|
-
function buildCreateRedirectUrl(app) {
|
|
7076
|
-
const base = app.url;
|
|
7077
|
-
const prompt = typeof app.prompt === 'string' ? app.prompt.trim() : '';
|
|
7078
|
-
if (!prompt)
|
|
7079
|
-
return base;
|
|
7080
|
-
const params = `bounded_prompt=${encodeURIComponent(prompt)}`
|
|
7081
|
-
+ (app.handoff ? `&bounded_handoff=${encodeURIComponent(app.handoff)}` : '');
|
|
7082
|
-
return `${base}#${params}`;
|
|
6843
|
+
async function get(appId, runId) {
|
|
6844
|
+
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}`);
|
|
7083
6845
|
}
|
|
7084
|
-
async function
|
|
7085
|
-
|
|
7086
|
-
|
|
6846
|
+
async function events(appId, runId, after = 0) {
|
|
6847
|
+
const cursor = Number.isFinite(after) ? Math.max(0, Math.floor(after)) : 0;
|
|
6848
|
+
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/events?after=${cursor}`);
|
|
6849
|
+
}
|
|
6850
|
+
async function cancel(appId, runId) {
|
|
6851
|
+
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/cancel`, { method: 'POST' });
|
|
6852
|
+
}
|
|
6853
|
+
async function decideGate(appId, runId, gateId, options) {
|
|
6854
|
+
const idempotencyKey = actionIdempotencyKey(options, 'decideGate()');
|
|
6855
|
+
const { intentKey: _intentKey, idempotencyKey: _suppliedKey } = options, decision = __rest(options, ["intentKey", "idempotencyKey"]);
|
|
6856
|
+
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/gates/${encodeURIComponent(gateId)}/decision`, { method: 'POST', body: Object.assign(Object.assign({}, decision), { idempotencyKey }) });
|
|
6857
|
+
}
|
|
6858
|
+
async function refreshReviewCapability(appId, runId, gateId, options) {
|
|
6859
|
+
const idempotencyKey = actionIdempotencyKey(options, 'refreshReviewCapability()');
|
|
6860
|
+
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/gates/${encodeURIComponent(gateId)}/review-capability/refresh`, {
|
|
7087
6861
|
method: 'POST',
|
|
7088
|
-
body: {
|
|
6862
|
+
body: {
|
|
6863
|
+
expectedGeneration: options.expectedGeneration,
|
|
6864
|
+
expectedExpiresAtMs: options.expectedExpiresAtMs,
|
|
6865
|
+
expectedRecoveryUntilMs: options.expectedRecoveryUntilMs,
|
|
6866
|
+
expectedAudience: options.expectedAudience,
|
|
6867
|
+
expectedReviewerBindingHash: options.expectedReviewerBindingHash,
|
|
6868
|
+
idempotencyKey,
|
|
6869
|
+
},
|
|
7089
6870
|
});
|
|
7090
|
-
const rows = Array.isArray(response.apps) ? response.apps : [];
|
|
7091
|
-
return rows
|
|
7092
|
-
.map((value) => {
|
|
7093
|
-
var _a, _b, _c, _d;
|
|
7094
|
-
const row = value && typeof value === 'object'
|
|
7095
|
-
? value
|
|
7096
|
-
: {};
|
|
7097
|
-
const appId = String((_c = (_b = (_a = row._id) !== null && _a !== void 0 ? _a : row.id) !== null && _b !== void 0 ? _b : row.appId) !== null && _c !== void 0 ? _c : '');
|
|
7098
|
-
if (!/^[a-f0-9]{24}$/i.test(appId))
|
|
7099
|
-
return null;
|
|
7100
|
-
const name = String((_d = row.name) !== null && _d !== void 0 ? _d : '').trim() || '(unnamed app)';
|
|
7101
|
-
const slug = typeof row.slug === 'string' && row.slug ? row.slug : undefined;
|
|
7102
|
-
const url = typeof row.url === 'string' && row.url.trim() ? row.url.trim() : undefined;
|
|
7103
|
-
const buildStatus = row.buildStatus === 'building' || row.buildStatus === 'build_failed'
|
|
7104
|
-
? row.buildStatus
|
|
7105
|
-
: undefined;
|
|
7106
|
-
return Object.assign(Object.assign(Object.assign({ appId,
|
|
7107
|
-
name }, (slug ? { slug } : {})), (url ? { url } : {})), (buildStatus ? { buildStatus } : {}));
|
|
7108
|
-
})
|
|
7109
|
-
.filter((app) => app !== null);
|
|
7110
|
-
}
|
|
7111
|
-
/**
|
|
7112
|
-
* Platform first-party surfaces only (bounded.sh, oapps.fun, localhost) —
|
|
7113
|
-
* dev-api CORS blocks calls from generated-app origins. Server-side callers unaffected.
|
|
7114
|
-
*/
|
|
7115
|
-
const apps = { create, list, buildCreateRedirectUrl };
|
|
7116
|
-
|
|
7117
|
-
async function deserializeTransaction(tx) {
|
|
7118
|
-
// Literal specifier so bundlers can code-split the lazy chunk.
|
|
7119
|
-
const { Transaction, VersionedTransaction } = await import('@solana/web3.js');
|
|
7120
|
-
const buf = Buffer.isBuffer(tx) ? tx : Buffer.from(tx, 'base64');
|
|
7121
|
-
if ((buf[0] & 0x80) !== 0) {
|
|
7122
|
-
return VersionedTransaction.deserialize(buf);
|
|
7123
|
-
}
|
|
7124
|
-
else {
|
|
7125
|
-
return Transaction.from(buf);
|
|
7126
|
-
}
|
|
7127
6871
|
}
|
|
7128
|
-
|
|
7129
|
-
// turnkey-wallet-widget.ts - runs one Turnkey signer exchange inside the shared
|
|
7130
|
-
// in-app WIDGET (bounded-widget-modal.ts). The Bounded-owned signer page is loaded
|
|
7131
|
-
// in a warm iframe behind the modal so signing feels native: an in-app card plus
|
|
7132
|
-
// the OS Face ID / Touch ID sheet, never a jarring popup.
|
|
7133
|
-
//
|
|
7134
|
-
// WHY AN IFRAME: a WebAuthn passkey is bound to ONE origin, so passkey create/sign
|
|
7135
|
-
// MUST run on the single Bounded signer origin. The iframe carries the
|
|
7136
|
-
// publickey-credentials-{get,create} permission policy and the user's click lands
|
|
7137
|
-
// INSIDE it, giving WebAuthn the transient activation it needs.
|
|
7138
|
-
//
|
|
7139
|
-
// SAFARI / restricted embedders: cross-origin iframe passkey CREATE is unreliable
|
|
7140
|
-
// in Safari. The signer detects that and posts `fallbackPopup`; we then rerun the
|
|
7141
|
-
// SAME request through `popupFallback` (a real window). Callers pass a popup
|
|
7142
|
-
// implementation and never see the difference.
|
|
7143
6872
|
/**
|
|
7144
|
-
*
|
|
7145
|
-
*
|
|
7146
|
-
*
|
|
6873
|
+
* Read one exact delegated-review capability and its narrow lifecycle result.
|
|
6874
|
+
* This does not refresh or extend the window and exposes no generic run-view
|
|
6875
|
+
* authority.
|
|
7147
6876
|
*/
|
|
7148
|
-
async function
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
// Serialize: WebAuthn is modal, and one warm frame handles one request at a time.
|
|
7154
|
-
const run = m.chain.then(() => new Promise((resolve, reject) => {
|
|
7155
|
-
let settled = false;
|
|
7156
|
-
let usedFallback = false;
|
|
7157
|
-
const iframe = ensureIframe(m, url, signerOrigin);
|
|
7158
|
-
const send = () => {
|
|
7159
|
-
if (!iframe.contentWindow)
|
|
7160
|
-
return;
|
|
7161
|
-
try {
|
|
7162
|
-
iframe.contentWindow.postMessage(ex.request, signerOrigin);
|
|
7163
|
-
}
|
|
7164
|
-
catch (_a) {
|
|
7165
|
-
/* frame gone */
|
|
7166
|
-
}
|
|
7167
|
-
};
|
|
7168
|
-
let loadTimer = 0;
|
|
7169
|
-
let ceilingTimer = 0;
|
|
7170
|
-
const finish = (fn) => {
|
|
7171
|
-
if (settled)
|
|
7172
|
-
return;
|
|
7173
|
-
settled = true;
|
|
7174
|
-
m.onIframeMessage = null;
|
|
7175
|
-
m.onBackdrop = null;
|
|
7176
|
-
window.clearTimeout(loadTimer);
|
|
7177
|
-
window.clearTimeout(ceilingTimer);
|
|
7178
|
-
fn();
|
|
7179
|
-
};
|
|
7180
|
-
const goFallback = () => {
|
|
7181
|
-
if (usedFallback || settled)
|
|
7182
|
-
return;
|
|
7183
|
-
usedFallback = true;
|
|
7184
|
-
hideModal(m);
|
|
7185
|
-
finish(() => popupFallback().then(resolve, reject));
|
|
7186
|
-
};
|
|
7187
|
-
m.onBackdrop = () => {
|
|
7188
|
-
hideModal(m);
|
|
7189
|
-
finish(() => reject(new Error("cancelled")));
|
|
7190
|
-
};
|
|
7191
|
-
m.onIframeMessage = (d) => {
|
|
7192
|
-
if (d.type === "bounded:turnkey:ready") {
|
|
7193
|
-
m.iframeReady = true;
|
|
7194
|
-
send();
|
|
7195
|
-
return;
|
|
7196
|
-
}
|
|
7197
|
-
if (d.type === "bounded:turnkey:resize" && typeof d.height === "number") {
|
|
7198
|
-
setPanelHeight(m, d.height);
|
|
7199
|
-
return;
|
|
7200
|
-
}
|
|
7201
|
-
if (d.type === "bounded:turnkey:fallbackPopup" && d.requestId === requestId) {
|
|
7202
|
-
goFallback();
|
|
7203
|
-
return;
|
|
7204
|
-
}
|
|
7205
|
-
if (d.type === "bounded:turnkey:result" && d.requestId === requestId) {
|
|
7206
|
-
const outcome = ex.onResult(d);
|
|
7207
|
-
if ("error" in outcome) {
|
|
7208
|
-
hideModal(m);
|
|
7209
|
-
finish(() => reject(new Error(outcome.error)));
|
|
7210
|
-
return;
|
|
7211
|
-
}
|
|
7212
|
-
if (outcome.done) {
|
|
7213
|
-
hideModal(m);
|
|
7214
|
-
finish(() => resolve(outcome.value));
|
|
7215
|
-
}
|
|
7216
|
-
}
|
|
7217
|
-
};
|
|
7218
|
-
showModal(m);
|
|
7219
|
-
if (m.iframeReady)
|
|
7220
|
-
send();
|
|
7221
|
-
// If the frame never loads, degrade to the popup (best-effort - a post-load
|
|
7222
|
-
// popup may be blocked without a gesture, in which case the fallback rejects).
|
|
7223
|
-
loadTimer = window.setTimeout(() => {
|
|
7224
|
-
if (!m.iframeReady && !settled)
|
|
7225
|
-
goFallback();
|
|
7226
|
-
}, 8000);
|
|
7227
|
-
// Absolute ceiling so a stuck flow cannot hang forever.
|
|
7228
|
-
ceilingTimer = window.setTimeout(() => {
|
|
7229
|
-
if (!settled) {
|
|
7230
|
-
hideModal(m);
|
|
7231
|
-
finish(() => reject(new Error("Wallet approval timed out.")));
|
|
7232
|
-
}
|
|
7233
|
-
}, 5 * 60 * 1000);
|
|
7234
|
-
}));
|
|
7235
|
-
m.chain = run.then(() => undefined, () => undefined);
|
|
7236
|
-
return run;
|
|
6877
|
+
async function getReviewCapability(appId, runId, gateId, options) {
|
|
6878
|
+
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/gates/${encodeURIComponent(gateId)}/review-capability`, {
|
|
6879
|
+
method: 'POST',
|
|
6880
|
+
body: { expectedPreviewUrl: options.expectedPreviewUrl },
|
|
6881
|
+
});
|
|
7237
6882
|
}
|
|
7238
|
-
|
|
7239
|
-
|
|
7240
|
-
// through the Bounded-owned signer page (auth.bounded.sh/wallet/turnkey/signer).
|
|
7241
|
-
//
|
|
7242
|
-
// TRANSPORT: by default the signer runs inside the in-app WIDGET (turnkey-wallet-
|
|
7243
|
-
// widget.ts) - an iframe behind a Shadow-DOM modal that matches the Bounded widget
|
|
7244
|
-
// look + motion, so signing feels native. Safari (unreliable at iframe passkey
|
|
7245
|
-
// CREATE) and any environment where the frame cannot mount fall back to a real
|
|
7246
|
-
// popup window; both transports speak the same postMessage protocol.
|
|
7247
|
-
//
|
|
7248
|
-
// Unlike the Crossmint bridge (crossmint-signer-bridge.ts), Turnkey wallets DO
|
|
7249
|
-
// produce raw detached signatures: the signer runs the Turnkey browser SDK on the
|
|
7250
|
-
// single Bounded origin, enrolls the user's passkey on first use (lazy - the
|
|
7251
|
-
// sub-org is created with the passkey + email attached at that moment), and stamps
|
|
7252
|
-
// an ed25519 signRawPayload with that passkey. So this bridge exposes a
|
|
7253
|
-
// `signSolanaMessage` primitive; the app builds + submits the transaction itself.
|
|
7254
|
-
//
|
|
7255
|
-
// ONE ORIGIN, ONE PASSKEY: a WebAuthn passkey is bound to a single origin, so ALL
|
|
7256
|
-
// Bounded apps (any subdomain / custom domain) sign through this one origin - giving
|
|
7257
|
-
// every user one wallet + one passkey that roams across every app.
|
|
7258
|
-
function hasDOM$1() {
|
|
7259
|
-
return typeof window !== 'undefined' && typeof document !== 'undefined';
|
|
6883
|
+
async function topup(appId, runId, options) {
|
|
6884
|
+
return requestDevApi(`${appPath(appId)}/${encodeURIComponent(runId)}/topup`, { method: 'POST', body: options });
|
|
7260
6885
|
}
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
// There we pick the popup up front, from the original click, so enrollment is solid.
|
|
7265
|
-
// Everywhere else the inline widget is used (buttery in-app modal).
|
|
7266
|
-
function preferPopup() {
|
|
7267
|
-
if (!hasDOM$1() || typeof navigator === 'undefined')
|
|
7268
|
-
return true;
|
|
7269
|
-
const ua = navigator.userAgent || '';
|
|
7270
|
-
return /^((?!chrome|android|crios|fxios|edg|edga|opr).)*safari/i.test(ua);
|
|
6886
|
+
async function list$1(appId, limit = 20) {
|
|
6887
|
+
const requestedLimit = Number.isFinite(limit) ? Math.max(1, Math.floor(limit)) : 20;
|
|
6888
|
+
return requestDevApi(`${appPath(appId)}?limit=${requestedLimit}`);
|
|
7271
6889
|
}
|
|
7272
|
-
|
|
7273
|
-
//
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
6890
|
+
|
|
6891
|
+
// Source of truth for these strings:
|
|
6892
|
+
// packages/cdk/cloudflare/bounded-build/src/types.ts. Keep them string-pinned;
|
|
6893
|
+
// wire:1 remains additive and callers must tolerate unknown response strings.
|
|
6894
|
+
const NON_TERMINAL_STATES = [
|
|
6895
|
+
'queued',
|
|
6896
|
+
'admitted',
|
|
6897
|
+
'executing',
|
|
6898
|
+
'preview_ready',
|
|
6899
|
+
'parked',
|
|
6900
|
+
'resuming',
|
|
6901
|
+
'promoting',
|
|
6902
|
+
'quarantined',
|
|
6903
|
+
];
|
|
6904
|
+
const TERMINAL_STATES = [
|
|
6905
|
+
'promoted',
|
|
6906
|
+
'failed',
|
|
6907
|
+
'canceled',
|
|
6908
|
+
'rejected',
|
|
6909
|
+
'expired',
|
|
6910
|
+
'rebase_required',
|
|
6911
|
+
'reported',
|
|
6912
|
+
];
|
|
6913
|
+
const RUN_STATES = [
|
|
6914
|
+
...NON_TERMINAL_STATES,
|
|
6915
|
+
...TERMINAL_STATES,
|
|
6916
|
+
];
|
|
6917
|
+
const PARK_REASONS = ['gate', 'funding'];
|
|
6918
|
+
const FAILURE_REASONS = [
|
|
6919
|
+
'admission_rejected',
|
|
6920
|
+
'dispatch_timeout',
|
|
6921
|
+
'auth_revoked',
|
|
6922
|
+
'owner_changed',
|
|
6923
|
+
'policy_changed',
|
|
6924
|
+
'quarantine_forced',
|
|
6925
|
+
'bind_failed',
|
|
6926
|
+
'internal_error',
|
|
6927
|
+
];
|
|
6928
|
+
const BUILD_REVIEW_CAPABILITY_SCHEMA = 'bounded.build-review-capability.v1';
|
|
6929
|
+
const BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA = 'bounded.build-review-capability-refresh.v1';
|
|
6930
|
+
const BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA = 'bounded.build-delegated-review-capability.v1';
|
|
6931
|
+
|
|
6932
|
+
const FAST_POLL_MS = 1000;
|
|
6933
|
+
const NORMAL_POLL_MS = 2000;
|
|
6934
|
+
const PARKED_POLL_MS = 5000;
|
|
6935
|
+
const LONG_RUNNING_POLL_MS = 10000;
|
|
6936
|
+
const THIRTY_SECONDS_MS = 30000;
|
|
6937
|
+
const TWO_MINUTES_MS = 2 * 60000;
|
|
6938
|
+
const TEN_MINUTES_MS = 10 * 60000;
|
|
6939
|
+
const terminalStates = new Set(TERMINAL_STATES);
|
|
6940
|
+
function pollCadence(state, elapsedMs) {
|
|
6941
|
+
if (elapsedMs >= TEN_MINUTES_MS)
|
|
6942
|
+
return LONG_RUNNING_POLL_MS;
|
|
6943
|
+
if ((state === null || state === void 0 ? void 0 : state.startsWith('parked')) || elapsedMs >= TWO_MINUTES_MS)
|
|
6944
|
+
return PARKED_POLL_MS;
|
|
6945
|
+
// The first 30s is where the user is staring at "waiting for a build slot" /
|
|
6946
|
+
// "building" and every transition matters most; poll fast, then relax.
|
|
6947
|
+
if (elapsedMs < THIRTY_SECONDS_MS)
|
|
6948
|
+
return FAST_POLL_MS;
|
|
6949
|
+
return NORMAL_POLL_MS;
|
|
7279
6950
|
}
|
|
7280
|
-
|
|
7281
|
-
const
|
|
7282
|
-
|
|
7283
|
-
|
|
6951
|
+
function retryCadence(error, state, elapsedMs) {
|
|
6952
|
+
const cadence = pollCadence(state, elapsedMs);
|
|
6953
|
+
if (error instanceof BuildsApiError && error.status === 429 && error.retryAfterMs !== undefined) {
|
|
6954
|
+
return Math.max(cadence, error.retryAfterMs);
|
|
6955
|
+
}
|
|
6956
|
+
return cadence;
|
|
7284
6957
|
}
|
|
7285
|
-
function
|
|
7286
|
-
|
|
7287
|
-
|
|
7288
|
-
|
|
7289
|
-
return h;
|
|
6958
|
+
function hasRetryAfter(error) {
|
|
6959
|
+
return error instanceof BuildsApiError
|
|
6960
|
+
&& error.status === 429
|
|
6961
|
+
&& error.retryAfterMs !== undefined;
|
|
7290
6962
|
}
|
|
7291
|
-
|
|
7292
|
-
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
6963
|
+
// A permanent API refusal (expired session, revoked access, unknown run) will
|
|
6964
|
+
// never heal by re-polling; the loop must stop and surface it instead of
|
|
6965
|
+
// retrying forever. 429 stays retryable regardless of the server envelope.
|
|
6966
|
+
function isPermanentError(error) {
|
|
6967
|
+
return error instanceof BuildsApiError
|
|
6968
|
+
&& error.status !== 429
|
|
6969
|
+
&& error.retryable === false;
|
|
7297
6970
|
}
|
|
7298
|
-
|
|
7299
|
-
|
|
7300
|
-
|
|
7301
|
-
|
|
7302
|
-
|
|
7303
|
-
|
|
7304
|
-
|
|
7305
|
-
|
|
7306
|
-
|
|
7307
|
-
|
|
7308
|
-
|
|
6971
|
+
function newEvents(rows, lastSeq, seen) {
|
|
6972
|
+
const cursorBeforePoll = lastSeq;
|
|
6973
|
+
const fresh = [];
|
|
6974
|
+
let nextCursor = lastSeq;
|
|
6975
|
+
for (const event of rows) {
|
|
6976
|
+
if (!Number.isFinite(event === null || event === void 0 ? void 0 : event.seq))
|
|
6977
|
+
continue;
|
|
6978
|
+
nextCursor = Math.max(nextCursor, event.seq);
|
|
6979
|
+
if (event.seq <= cursorBeforePoll || seen.has(event.seq))
|
|
6980
|
+
continue;
|
|
6981
|
+
seen.add(event.seq);
|
|
6982
|
+
fresh.push(event);
|
|
7309
6983
|
}
|
|
7310
|
-
return
|
|
7311
|
-
|
|
7312
|
-
|
|
7313
|
-
|
|
7314
|
-
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
6984
|
+
return { events: fresh, lastSeq: nextCursor };
|
|
6985
|
+
}
|
|
6986
|
+
/**
|
|
6987
|
+
* Poll a build view and its event cursor until stopped or the run reaches a
|
|
6988
|
+
* known terminal state. Unknown state strings remain untouched and nonterminal.
|
|
6989
|
+
*/
|
|
6990
|
+
function watch(appId, runId, onUpdate, options = {}) {
|
|
6991
|
+
const startedAt = Date.now();
|
|
6992
|
+
const seenSeqs = new Set();
|
|
6993
|
+
let lastSeq = Number.isFinite(options.after)
|
|
6994
|
+
? Math.max(0, Math.floor(options.after))
|
|
6995
|
+
: 0;
|
|
6996
|
+
let timer;
|
|
6997
|
+
let active = true;
|
|
6998
|
+
let lastState;
|
|
6999
|
+
const stop = () => {
|
|
7000
|
+
active = false;
|
|
7001
|
+
if (timer !== undefined) {
|
|
7002
|
+
clearTimeout(timer);
|
|
7003
|
+
timer = undefined;
|
|
7319
7004
|
}
|
|
7320
|
-
|
|
7321
|
-
|
|
7322
|
-
|
|
7005
|
+
};
|
|
7006
|
+
const schedule = (delayMs) => {
|
|
7007
|
+
if (!active)
|
|
7008
|
+
return;
|
|
7009
|
+
timer = setTimeout(() => {
|
|
7010
|
+
timer = undefined;
|
|
7011
|
+
void poll();
|
|
7012
|
+
}, delayMs);
|
|
7013
|
+
};
|
|
7014
|
+
const reportError = (error) => {
|
|
7015
|
+
var _a;
|
|
7016
|
+
try {
|
|
7017
|
+
(_a = options.onError) === null || _a === void 0 ? void 0 : _a.call(options, error);
|
|
7018
|
+
}
|
|
7019
|
+
catch (_b) {
|
|
7020
|
+
// A consumer error callback must not break the transport loop.
|
|
7021
|
+
}
|
|
7022
|
+
};
|
|
7023
|
+
const publish = (run, events) => {
|
|
7024
|
+
try {
|
|
7025
|
+
onUpdate({ run, events });
|
|
7026
|
+
}
|
|
7027
|
+
catch (_a) {
|
|
7028
|
+
// Polling is transport state; consumer render errors must not strand it.
|
|
7029
|
+
}
|
|
7030
|
+
};
|
|
7031
|
+
const poll = async () => {
|
|
7032
|
+
if (!active)
|
|
7033
|
+
return;
|
|
7034
|
+
// Fire the view and the events page concurrently: the events cursor (lastSeq)
|
|
7035
|
+
// does not depend on the run view, so there is no reason to pay two serial
|
|
7036
|
+
// round-trips per tick. Error semantics below are unchanged — the run result
|
|
7037
|
+
// still gates, and a failed events page still publishes the run with no rows.
|
|
7038
|
+
const runPromise = get(appId, runId);
|
|
7039
|
+
const eventsPromise = events(appId, runId, lastSeq);
|
|
7040
|
+
// Keep the in-flight events page from becoming an unhandled rejection if the
|
|
7041
|
+
// run result makes us bail before awaiting it; the real await below still sees it.
|
|
7042
|
+
eventsPromise.catch(() => { });
|
|
7043
|
+
let run;
|
|
7044
|
+
try {
|
|
7045
|
+
const view = await runPromise;
|
|
7046
|
+
if (!active)
|
|
7323
7047
|
return;
|
|
7324
|
-
|
|
7048
|
+
run = view.run;
|
|
7049
|
+
lastState = run.state;
|
|
7050
|
+
}
|
|
7051
|
+
catch (error) {
|
|
7052
|
+
if (!active)
|
|
7325
7053
|
return;
|
|
7326
|
-
|
|
7327
|
-
if (
|
|
7054
|
+
reportError(error);
|
|
7055
|
+
if (isPermanentError(error)) {
|
|
7056
|
+
stop();
|
|
7057
|
+
return;
|
|
7058
|
+
}
|
|
7059
|
+
schedule(retryCadence(error, lastState, Date.now() - startedAt));
|
|
7060
|
+
return;
|
|
7061
|
+
}
|
|
7062
|
+
try {
|
|
7063
|
+
const response = await eventsPromise;
|
|
7064
|
+
if (!active)
|
|
7065
|
+
return;
|
|
7066
|
+
const deduped = newEvents(Array.isArray(response.events) ? response.events : [], lastSeq, seenSeqs);
|
|
7067
|
+
lastSeq = deduped.lastSeq;
|
|
7068
|
+
publish(run, deduped.events);
|
|
7069
|
+
}
|
|
7070
|
+
catch (error) {
|
|
7071
|
+
if (!active)
|
|
7072
|
+
return;
|
|
7073
|
+
// The view still advances UI state even when event replay is rate-limited.
|
|
7074
|
+
publish(run, []);
|
|
7075
|
+
reportError(error);
|
|
7076
|
+
if (terminalStates.has(run.state)) {
|
|
7077
|
+
// The run itself is done, but a rate-limited final event replay still
|
|
7078
|
+
// owes the caller its unseen seq rows. Honor Retry-After, retry the
|
|
7079
|
+
// piggyback once the limiter permits it, then stop after a successful
|
|
7080
|
+
// replay. Other terminal replay failures stop normally.
|
|
7081
|
+
if (hasRetryAfter(error)) {
|
|
7082
|
+
schedule(retryCadence(error, run.state, Date.now() - startedAt));
|
|
7083
|
+
return;
|
|
7084
|
+
}
|
|
7085
|
+
stop();
|
|
7328
7086
|
return;
|
|
7329
|
-
|
|
7330
|
-
|
|
7087
|
+
}
|
|
7088
|
+
if (isPermanentError(error)) {
|
|
7089
|
+
stop();
|
|
7331
7090
|
return;
|
|
7332
7091
|
}
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
|
|
7336
|
-
|
|
7337
|
-
|
|
7338
|
-
|
|
7339
|
-
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
|
|
7092
|
+
schedule(retryCadence(error, run.state, Date.now() - startedAt));
|
|
7093
|
+
return;
|
|
7094
|
+
}
|
|
7095
|
+
if (!active)
|
|
7096
|
+
return;
|
|
7097
|
+
if (terminalStates.has(run.state)) {
|
|
7098
|
+
// Drain the remaining event pages before stopping: a resumed terminal
|
|
7099
|
+
// watch may owe the caller more rows than one page returns. Each page
|
|
7100
|
+
// advances the cursor, so the loop terminates; the cap is a backstop.
|
|
7101
|
+
for (let page = 0; page < 50 && active; page += 1) {
|
|
7102
|
+
let drained;
|
|
7103
|
+
try {
|
|
7104
|
+
const response = await events(appId, runId, lastSeq);
|
|
7105
|
+
if (!active)
|
|
7106
|
+
return;
|
|
7107
|
+
drained = newEvents(Array.isArray(response.events) ? response.events : [], lastSeq, seenSeqs);
|
|
7343
7108
|
}
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
popup.close();
|
|
7348
|
-
}
|
|
7349
|
-
catch ( /* ignore */_b) { /* ignore */ }
|
|
7350
|
-
resolve(outcome.value);
|
|
7109
|
+
catch (error) {
|
|
7110
|
+
reportError(error);
|
|
7111
|
+
break;
|
|
7351
7112
|
}
|
|
7113
|
+
if (drained.events.length === 0)
|
|
7114
|
+
break;
|
|
7115
|
+
lastSeq = drained.lastSeq;
|
|
7116
|
+
publish(run, drained.events);
|
|
7352
7117
|
}
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7118
|
+
stop();
|
|
7119
|
+
return;
|
|
7120
|
+
}
|
|
7121
|
+
schedule(pollCadence(run.state, Date.now() - startedAt));
|
|
7122
|
+
};
|
|
7123
|
+
void poll();
|
|
7124
|
+
return { stop };
|
|
7125
|
+
}
|
|
7126
|
+
|
|
7127
|
+
var _a;
|
|
7128
|
+
// SSR guard (mirrors useAuth): no hooks/subscriptions during Node server render.
|
|
7129
|
+
// React Native has no `window` but DOES support hooks, so detect Node SSR
|
|
7130
|
+
// specifically via process.versions.node.
|
|
7131
|
+
const isSSR = typeof window === 'undefined'
|
|
7132
|
+
&& typeof process !== 'undefined'
|
|
7133
|
+
&& !!((_a = process.versions) === null || _a === void 0 ? void 0 : _a.node);
|
|
7134
|
+
function useBuildRun(appId, runId, options) {
|
|
7135
|
+
if (isSSR) {
|
|
7136
|
+
return { run: null, events: [], loading: true, error: null };
|
|
7137
|
+
}
|
|
7138
|
+
const [run, setRun] = React__namespace.useState(null);
|
|
7139
|
+
const [events, setEvents] = React__namespace.useState([]);
|
|
7140
|
+
const [loading, setLoading] = React__namespace.useState(true);
|
|
7141
|
+
const [error, setError] = React__namespace.useState(null);
|
|
7142
|
+
const optionsKey = options ? JSON.stringify(options) : '';
|
|
7143
|
+
React__namespace.useEffect(() => {
|
|
7144
|
+
setRun(null);
|
|
7145
|
+
setEvents([]);
|
|
7146
|
+
setError(null);
|
|
7147
|
+
if (!appId || !runId) {
|
|
7148
|
+
setLoading(false);
|
|
7149
|
+
return;
|
|
7150
|
+
}
|
|
7151
|
+
let active = true;
|
|
7152
|
+
setLoading(true);
|
|
7153
|
+
const handle = watch(appId, runId, ({ run: nextRun, events: nextEvents }) => {
|
|
7154
|
+
if (!active)
|
|
7155
|
+
return;
|
|
7156
|
+
setRun(nextRun);
|
|
7157
|
+
if (nextEvents.length > 0) {
|
|
7158
|
+
setEvents((current) => {
|
|
7159
|
+
const seen = new Set(current.map((event) => event.seq));
|
|
7160
|
+
return [...current, ...nextEvents.filter((event) => !seen.has(event.seq))];
|
|
7161
|
+
});
|
|
7369
7162
|
}
|
|
7370
|
-
|
|
7371
|
-
|
|
7372
|
-
} },
|
|
7163
|
+
setError(null);
|
|
7164
|
+
setLoading(false);
|
|
7165
|
+
}, Object.assign(Object.assign({}, options), { onError: (value) => {
|
|
7166
|
+
if (!active)
|
|
7167
|
+
return;
|
|
7168
|
+
setError(value instanceof Error ? value : new Error(String(value)));
|
|
7169
|
+
setLoading(false);
|
|
7170
|
+
} }));
|
|
7171
|
+
return () => {
|
|
7172
|
+
active = false;
|
|
7173
|
+
handle.stop();
|
|
7174
|
+
};
|
|
7175
|
+
// optionsKey captures the option object's contents; ids are the other inputs.
|
|
7176
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
7177
|
+
}, [appId, runId, optionsKey]);
|
|
7178
|
+
return { run, events, loading, error };
|
|
7179
|
+
}
|
|
7180
|
+
|
|
7181
|
+
/**
|
|
7182
|
+
* Platform first-party surfaces only (bounded.sh, oapps.fun, localhost), with
|
|
7183
|
+
* one exact Drafted exception for delegated review/decision routes. Other
|
|
7184
|
+
* generated-app origins remain blocked; server-side callers are unaffected.
|
|
7185
|
+
*/
|
|
7186
|
+
const builds = {
|
|
7187
|
+
submit,
|
|
7188
|
+
get,
|
|
7189
|
+
getReviewCapability,
|
|
7190
|
+
events,
|
|
7191
|
+
cancel,
|
|
7192
|
+
decideGate,
|
|
7193
|
+
refreshReviewCapability,
|
|
7194
|
+
topup,
|
|
7195
|
+
list: list$1,
|
|
7196
|
+
watch,
|
|
7197
|
+
};
|
|
7198
|
+
|
|
7199
|
+
async function create(options = {}) {
|
|
7200
|
+
const body = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, (options.name !== undefined ? { name: options.name } : {})), (options.prompt !== undefined ? { prompt: options.prompt } : {})), (options.idempotencyKey !== undefined ? { idempotencyKey: options.idempotencyKey } : {})), (options.intent !== undefined ? { intent: options.intent } : {})), (options.oapp === true ? { oapp: true } : {}));
|
|
7201
|
+
const app = await requestDevApi('/apps/new', {
|
|
7202
|
+
method: 'POST',
|
|
7203
|
+
body,
|
|
7373
7204
|
});
|
|
7205
|
+
return Object.assign(Object.assign(Object.assign(Object.assign({ appId: app.appId }, (typeof app.slug === 'string' && app.slug ? { slug: app.slug } : {})), { url: app.url }), (typeof app.prompt === 'string' && app.prompt ? { prompt: app.prompt } : {})), (typeof app.handoff === 'string' && app.handoff ? { handoff: app.handoff } : {}));
|
|
7374
7206
|
}
|
|
7375
7207
|
/**
|
|
7376
|
-
*
|
|
7377
|
-
*
|
|
7378
|
-
*
|
|
7379
|
-
*
|
|
7208
|
+
* F-1200: build the create-flow redirect the owner is sent to after `create()`, with the
|
|
7209
|
+
* prompt and handoff carried in the URL FRAGMENT (never the query - fragments never reach
|
|
7210
|
+
* servers, logs, or Referer). Encoding is byte-exact so the widget's `URLSearchParams`
|
|
7211
|
+
* parse + `.trim()` reproduces the SAME prompt string dev-api hashed into the handoff;
|
|
7212
|
+
* getting this wrong would make the widget reject a genuine token and degrade to prefill.
|
|
7213
|
+
* Callers (bounded.sh, oapps.fun) should use this instead of hand-building the URL.
|
|
7380
7214
|
*
|
|
7381
|
-
*
|
|
7215
|
+
* Returns `${url}#bounded_prompt=<prompt>` with no `&bounded_handoff=` when the server
|
|
7216
|
+
* minted no handoff (HANDOFF_SECRET unset) - the widget then prefills without auto-running.
|
|
7382
7217
|
*/
|
|
7383
|
-
|
|
7384
|
-
|
|
7385
|
-
|
|
7386
|
-
|
|
7387
|
-
|
|
7388
|
-
|
|
7389
|
-
|
|
7390
|
-
|
|
7391
|
-
|
|
7392
|
-
|
|
7393
|
-
|
|
7394
|
-
|
|
7218
|
+
function buildCreateRedirectUrl(app) {
|
|
7219
|
+
const base = app.url;
|
|
7220
|
+
const prompt = typeof app.prompt === 'string' ? app.prompt.trim() : '';
|
|
7221
|
+
if (!prompt)
|
|
7222
|
+
return base;
|
|
7223
|
+
const params = `bounded_prompt=${encodeURIComponent(prompt)}`
|
|
7224
|
+
+ (app.handoff ? `&bounded_handoff=${encodeURIComponent(app.handoff)}` : '');
|
|
7225
|
+
return `${base}#${params}`;
|
|
7226
|
+
}
|
|
7227
|
+
async function list() {
|
|
7228
|
+
// This legacy-compatible action shape is also used by oapps.fun listMyApps.
|
|
7229
|
+
const response = await requestDevApi('/', {
|
|
7230
|
+
method: 'POST',
|
|
7231
|
+
body: { action: 'listApps' },
|
|
7395
7232
|
});
|
|
7233
|
+
const rows = Array.isArray(response.apps) ? response.apps : [];
|
|
7234
|
+
return rows
|
|
7235
|
+
.map((value) => {
|
|
7236
|
+
var _a, _b, _c, _d;
|
|
7237
|
+
const row = value && typeof value === 'object'
|
|
7238
|
+
? value
|
|
7239
|
+
: {};
|
|
7240
|
+
const appId = String((_c = (_b = (_a = row._id) !== null && _a !== void 0 ? _a : row.id) !== null && _b !== void 0 ? _b : row.appId) !== null && _c !== void 0 ? _c : '');
|
|
7241
|
+
if (!/^[a-f0-9]{24}$/i.test(appId))
|
|
7242
|
+
return null;
|
|
7243
|
+
const name = String((_d = row.name) !== null && _d !== void 0 ? _d : '').trim() || '(unnamed app)';
|
|
7244
|
+
const slug = typeof row.slug === 'string' && row.slug ? row.slug : undefined;
|
|
7245
|
+
const url = typeof row.url === 'string' && row.url.trim() ? row.url.trim() : undefined;
|
|
7246
|
+
const buildStatus = row.buildStatus === 'building' || row.buildStatus === 'build_failed'
|
|
7247
|
+
? row.buildStatus
|
|
7248
|
+
: undefined;
|
|
7249
|
+
return Object.assign(Object.assign(Object.assign({ appId,
|
|
7250
|
+
name }, (slug ? { slug } : {})), (url ? { url } : {})), (buildStatus ? { buildStatus } : {}));
|
|
7251
|
+
})
|
|
7252
|
+
.filter((app) => app !== null);
|
|
7396
7253
|
}
|
|
7397
7254
|
/**
|
|
7398
|
-
*
|
|
7399
|
-
*
|
|
7400
|
-
* the wallet address. MUST be called from a USER GESTURE (click/tap) or the
|
|
7401
|
-
* browser blocks the popup.
|
|
7402
|
-
*
|
|
7403
|
-
* On the user's FIRST signature the popup transparently enrolls a device passkey
|
|
7404
|
-
* (Face ID / Touch ID) and creates their wallet - no email code. Subsequent
|
|
7405
|
-
* signatures are just a passkey approval.
|
|
7255
|
+
* Platform first-party surfaces only (bounded.sh, oapps.fun, localhost) —
|
|
7256
|
+
* dev-api CORS blocks calls from generated-app origins. Server-side callers unaffected.
|
|
7406
7257
|
*/
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7258
|
+
const apps = { create, list, buildCreateRedirectUrl };
|
|
7259
|
+
|
|
7260
|
+
async function deserializeTransaction(tx) {
|
|
7261
|
+
// Literal specifier so bundlers can code-split the lazy chunk.
|
|
7262
|
+
const { Transaction, VersionedTransaction } = await import('@solana/web3.js');
|
|
7263
|
+
const buf = Buffer.isBuffer(tx) ? tx : Buffer.from(tx, 'base64');
|
|
7264
|
+
if ((buf[0] & 0x80) !== 0) {
|
|
7265
|
+
return VersionedTransaction.deserialize(buf);
|
|
7266
|
+
}
|
|
7267
|
+
else {
|
|
7268
|
+
return Transaction.from(buf);
|
|
7410
7269
|
}
|
|
7411
|
-
const { url, origin } = await turnkeySignerPage();
|
|
7412
|
-
const requestId = `tksign_${Date.now()}_${++requestSeq}`;
|
|
7413
|
-
const messageHex = toHex(message);
|
|
7414
|
-
return await deliver(url, origin, {
|
|
7415
|
-
request: { type: 'bounded:turnkey:sign', requestId, messageHex },
|
|
7416
|
-
onResult: (d) => {
|
|
7417
|
-
if (d.ok && typeof d.signatureHex === 'string' && typeof d.address === 'string') {
|
|
7418
|
-
return { done: true, value: { signature: fromHex(d.signatureHex), address: d.address, subOrgId: String(d.subOrgId || '') } };
|
|
7419
|
-
}
|
|
7420
|
-
return { error: String(d.error || 'Turnkey signing failed') };
|
|
7421
|
-
},
|
|
7422
|
-
});
|
|
7423
7270
|
}
|
|
7424
7271
|
|
|
7425
7272
|
// bounded.onramp() - fund the signed-in user's wallet with Coinbase Onramp.
|
|
@@ -8087,7 +7934,6 @@ exports.BUILD_DELEGATED_REVIEW_CAPABILITY_SCHEMA = BUILD_DELEGATED_REVIEW_CAPABI
|
|
|
8087
7934
|
exports.BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA = BUILD_REVIEW_CAPABILITY_REFRESH_SCHEMA;
|
|
8088
7935
|
exports.BUILD_REVIEW_CAPABILITY_SCHEMA = BUILD_REVIEW_CAPABILITY_SCHEMA;
|
|
8089
7936
|
exports.BuildsApiError = BuildsApiError;
|
|
8090
|
-
exports.EMBEDDED_WALLET_UNSUPPORTED = EMBEDDED_WALLET_UNSUPPORTED;
|
|
8091
7937
|
exports.FAILURE_REASONS = FAILURE_REASONS;
|
|
8092
7938
|
exports.NON_TERMINAL_STATES = NON_TERMINAL_STATES;
|
|
8093
7939
|
exports.OffchainAuthProvider = OffchainAuthProvider;
|
|
@@ -8105,16 +7951,13 @@ exports.confirmAndCheckTransaction = confirmAndCheckTransaction;
|
|
|
8105
7951
|
exports.defaultInjectedEvmProvider = defaultInjectedEvmProvider;
|
|
8106
7952
|
exports.defaultInjectedProvider = defaultInjectedProvider;
|
|
8107
7953
|
exports.deserializeTransaction = deserializeTransaction;
|
|
8108
|
-
exports.embeddedRunEvmTransaction = embeddedRunEvmTransaction;
|
|
8109
|
-
exports.embeddedSignAndSubmit = embeddedSignAndSubmit;
|
|
8110
7954
|
exports.getAuthLoading = getAuthLoading;
|
|
8111
7955
|
exports.getAuthProvider = getAuthProvider;
|
|
8112
7956
|
exports.getCurrentUser = getCurrentUser;
|
|
8113
7957
|
exports.getIdToken = getIdToken;
|
|
8114
7958
|
exports.getOrCreateTurnkeyWallet = getOrCreateTurnkeyWallet;
|
|
8115
7959
|
exports.getPlatform = getPlatform;
|
|
8116
|
-
exports.
|
|
8117
|
-
exports.hasEmbeddedWallet = hasEmbeddedWallet;
|
|
7960
|
+
exports.hasTurnkeyWallet = hasTurnkeyWallet;
|
|
8118
7961
|
exports.init = init;
|
|
8119
7962
|
exports.login = login;
|
|
8120
7963
|
exports.loginWithPopup = loginWithPopup;
|
|
@@ -8134,10 +7977,13 @@ exports.resolveSolanaRpcUrl = resolveSolanaRpcUrl;
|
|
|
8134
7977
|
exports.setAuthLoading = setAuthLoading;
|
|
8135
7978
|
exports.setCurrentUser = setCurrentUser;
|
|
8136
7979
|
exports.setPlatform = setPlatform;
|
|
7980
|
+
exports.signAndSubmitTransactionWithTurnkey = signAndSubmitTransactionWithTurnkey;
|
|
8137
7981
|
exports.signInAnonymously = signInAnonymously;
|
|
7982
|
+
exports.signMessageWithTurnkey = signMessageWithTurnkey;
|
|
8138
7983
|
exports.signSolanaMessageViaTurnkey = signSolanaMessageViaTurnkey;
|
|
7984
|
+
exports.signTransactionWithTurnkey = signTransactionWithTurnkey;
|
|
8139
7985
|
exports.startTurnkeyEmailLogin = startTurnkeyEmailLogin;
|
|
8140
7986
|
exports.useAuth = useAuth;
|
|
8141
7987
|
exports.useBuildRun = useBuildRun;
|
|
8142
7988
|
exports.useQuery = useQuery;
|
|
8143
|
-
//# sourceMappingURL=index-
|
|
7989
|
+
//# sourceMappingURL=index-Bk0nEjB6.js.map
|