sliccy 5.5.0 → 5.6.0
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/node-server/_shared/base64.d.ts +32 -0
- package/dist/node-server/_shared/base64.d.ts.map +1 -0
- package/dist/node-server/_shared/base64.js +90 -0
- package/dist/node-server/_shared/base64.js.map +1 -0
- package/dist/node-server/_shared/index.d.ts +1 -0
- package/dist/node-server/_shared/index.d.ts.map +1 -1
- package/dist/node-server/_shared/index.js +1 -0
- package/dist/node-server/_shared/index.js.map +1 -1
- package/dist/node-server/_shared/sign-and-forward.d.ts.map +1 -1
- package/dist/node-server/_shared/sign-and-forward.js +6 -36
- package/dist/node-server/_shared/sign-and-forward.js.map +1 -1
- package/dist/node-server/electron-controller.d.ts +41 -0
- package/dist/node-server/electron-controller.js +141 -2
- package/dist/ui/assets/{account-store-ek-8-vad.js → account-store-ChMOtlUz.js} +2 -2
- package/dist/ui/assets/{account-store-uVmUnqVi.js → account-store-NnrV4ZgP.js} +3 -3
- package/dist/ui/assets/{adobe-B53UUHRQ.js → adobe-C8Q-i1ER.js} +2 -2
- package/dist/ui/assets/{adobe-CUUUdCzq.js → adobe-DkHCumOt.js} +1 -1
- package/dist/ui/assets/adobe-oauth-state---RmeoFk.js +1 -0
- package/dist/ui/assets/adobe-oauth-state-BWBscKit.js +1 -0
- package/dist/ui/assets/{agent-message-to-chat-EX33cDse.js → agent-message-to-chat-DBY6kl9u.js} +1 -1
- package/dist/ui/assets/{apps-CAEkkK-h.js → apps-DTJS_0DO.js} +1 -1
- package/dist/ui/assets/{azure-openai-BKN7-_r9.js → azure-openai-DiIYHOhW.js} +1 -1
- package/dist/ui/assets/{azure-openai-DFKLOD9O.js → azure-openai-Dwl8MBqc.js} +1 -1
- package/dist/ui/assets/{composer-speech-BJF-32Qm.js → composer-speech-D1okaecE.js} +1 -1
- package/dist/ui/assets/{connect-surface-D1ylnSSy.js → connect-surface-B2SGC9nj.js} +1 -1
- package/dist/ui/assets/dip-D84UtYDl.js +1 -0
- package/dist/ui/assets/{dist-DoIT4K9j.js → dist-D2QIqGam.js} +1 -1
- package/dist/ui/assets/{dist-BykthAQO.js → dist-DEgj051I.js} +1 -1
- package/dist/ui/assets/{ensure-speech-assets-BAMSo5xy.js → ensure-speech-assets-DHIjah7x.js} +1 -1
- package/dist/ui/assets/esp8266-BK1kLRwZ.js +1 -0
- package/dist/ui/assets/{esptool-operations-Dqz4H39j.js → esptool-operations-B5h37g_j.js} +3 -3
- package/dist/ui/assets/fs-C7XtIDid.js +1 -0
- package/dist/ui/assets/fs-D1mT9P9B.js +2 -0
- package/dist/ui/assets/{github-CEb6Ukrf.js → github-CWmsFzDG.js} +2 -2
- package/dist/ui/assets/{github-Djvp2KJc.js → github-D0QzxwXi.js} +1 -1
- package/dist/ui/assets/{github-copilot-CwlFBczV.js → github-copilot-CPLiUa96.js} +1 -1
- package/dist/ui/assets/{github-copilot-D_nIJ9c2.js → github-copilot-CozINCYs.js} +1 -1
- package/dist/ui/assets/{hear-Ca-dJ63I.js → hear-CbGiJcVU.js} +1 -1
- package/dist/ui/assets/{hear-CwRsBtKX.js → hear-Cz0P4OJa.js} +1 -1
- package/dist/ui/assets/{kernel-worker-BvRAudrx.js → kernel-worker-DUSeFCHs.js} +1102 -1097
- package/dist/ui/assets/{kokoro-engine-BI-6sX56.js → kokoro-engine-Cc_3FxqR.js} +1 -1
- package/dist/ui/assets/{lick-ws-bridge-CzKvgnBL.js → lick-ws-bridge-BS8lOfvh.js} +1 -1
- package/dist/ui/assets/{lick-ws-bridge-mMPXekwg.js → lick-ws-bridge-C9mSkmfA.js} +1 -1
- package/dist/ui/assets/{local-llm-fqZ0fO9H.js → local-llm-CLoX0X2a.js} +1 -1
- package/dist/ui/assets/main-nYB4sXGf.js +24 -0
- package/dist/ui/assets/mount-DwaNjxHO.js +1 -0
- package/dist/ui/assets/mount-Dyf6fUXm.js +2 -0
- package/dist/ui/assets/{mount-bridge-client-DDnAgZEL.js → mount-bridge-client-tEnjiHyh.js} +1 -1
- package/dist/ui/assets/new-session-BL7SxGTK.js +2 -0
- package/dist/ui/assets/{oauth-bootstrap-BTpmul3n.js → oauth-bootstrap-cjq89qUf.js} +2 -2
- package/dist/ui/assets/{oauth-service-D4iuqFv6.js → oauth-service-D-lmxdpH.js} +1 -1
- package/dist/ui/assets/{oauth-service-YGl8VFS-.js → oauth-service-ahaKd8Mt.js} +1 -1
- package/dist/ui/assets/{openai-codex-Di_Wkz8P.js → openai-codex-BfGBBXcA.js} +1 -1
- package/dist/ui/assets/{openai-codex-BiuU54bP.js → openai-codex-DHaiaCAy.js} +1 -1
- package/dist/ui/assets/page-follower-tray-CgwN8jiT.js +4 -0
- package/dist/ui/assets/panel-rpc-handlers-zv2QzW38.js +2 -0
- package/dist/ui/assets/{provider-LWa4i1ss.js → provider-BkRwgYP9.js} +2 -2
- package/dist/ui/assets/{provider-BG2yHao6.js → provider-k5CLuyyK.js} +1 -1
- package/dist/ui/assets/provider-store-access-DvdszTSf.js +1 -0
- package/dist/ui/assets/provider-store-access-ZwyKgM4x.js +1 -0
- package/dist/ui/assets/{providers-InId80tM.js → providers-C1GOyEPo.js} +1 -1
- package/dist/ui/assets/proxied-fetch-B5vr_AP8.js +2 -0
- package/dist/ui/assets/proxied-fetch-I4cndszg.js +1 -0
- package/dist/ui/assets/quick-llm-CsIp5dLo.js +1 -0
- package/dist/ui/assets/{secret-env-DncfFSea.js → secret-env-Bs2du0RF.js} +1 -1
- package/dist/ui/assets/{secret-topology-bwXIzzAK.js → secret-topology-Bp0wH2ni.js} +1 -1
- package/dist/ui/assets/{secrets-bridge-client-BSipj-5f.js → secrets-bridge-client-DXw32cXb.js} +1 -1
- package/dist/ui/assets/session-freezer-CSO9MFMa.js +1 -0
- package/dist/ui/assets/{speak-D9WnbzxD.js → speak-D7I9w_gp.js} +1 -1
- package/dist/ui/assets/{speak-De8bZX3X.js → speak-DZmFOAZi.js} +1 -1
- package/dist/ui/assets/{sprinkle-manager-HrjQYffk.js → sprinkle-manager-DP5TcQ2O.js} +1 -1
- package/dist/ui/assets/src-x0cJ68Fe.js +1 -0
- package/dist/ui/assets/{store-BcukSq0d.js → store-DDctRXHY.js} +1 -1
- package/dist/ui/assets/{sudo-DTuVh1ES.js → sudo-Cn26f2pW.js} +1 -1
- package/dist/ui/assets/{transformers-env-CLlJbteI.js → transformers-env-BBUrqDgh.js} +1 -1
- package/dist/ui/assets/{transformers-env-CgaV7qpU.js → transformers-env-BLIAM-E9.js} +1 -1
- package/dist/ui/assets/{tray-leader-C0t_8UvQ.js → tray-leader-BcR176TP.js} +1 -1
- package/dist/ui/assets/{tray-leave-runtime-C0haAPQt.js → tray-leave-runtime-DH_-gySV.js} +1 -1
- package/dist/ui/assets/{upgrade-detection-Cf5EhzTd.js → upgrade-detection-130VJubQ.js} +1 -1
- package/dist/ui/assets/{voice-reply-DfKSbOma.js → voice-reply-CPoLpDd6.js} +1 -1
- package/dist/ui/assets/{wc-attach-C9_tu3yw.js → wc-attach-Dd9KyQkG.js} +3 -3
- package/dist/ui/assets/{wc-detached--tazk6dE.js → wc-detached-BPin8oEW.js} +1 -1
- package/dist/ui/assets/{wc-extension-0ostIpBU.js → wc-extension-D_L9ZoSH.js} +2 -2
- package/dist/ui/assets/{wc-follower-HC4qvoDl.js → wc-follower-bas3jMk1.js} +2 -2
- package/dist/ui/assets/wc-live-D9eNcQec.js +253 -0
- package/dist/ui/assets/{wc-nav-BGRbM4u_.js → wc-nav-CFRdJtq4.js} +2 -2
- package/dist/ui/assets/wc-onboarding-Bs6g6bXY.js +2 -0
- package/dist/ui/assets/{wc-placeholder-DLEqGc0p.js → wc-placeholder-BWpBvXKI.js} +3 -3
- package/dist/ui/assets/wc-settings-Da2cSSGg.js +24 -0
- package/dist/ui/assets/{wc-shell-k8zT_CCv.js → wc-shell-DFghXbe8.js} +35 -35
- package/dist/ui/assets/{wc-sprinkles-BtPIBmNQ.js → wc-sprinkles-hynRobBE.js} +2 -2
- package/dist/ui/assets/wc-tray-C-LtjhoL.js +2 -0
- package/dist/ui/assets/{whisper-engine-DCpr74_i.js → whisper-engine-Bp-XPxDQ.js} +1 -1
- package/dist/ui/assets/whisper-session-DmSNDpbR.js +2 -0
- package/dist/ui/assets/{xai-grok-frRBdi_g.js → xai-grok-Cx1F2hjj.js} +1 -1
- package/dist/ui/assets/{xai-grok-BecRhwSG.js → xai-grok-KqWZ-MII.js} +1 -1
- package/dist/ui/index.html +6 -6
- package/dist/ui/packages/webapp/index.html +6 -6
- package/package.json +1 -1
- package/dist/ui/assets/adobe-oauth-state-Cv45ZC7F.js +0 -1
- package/dist/ui/assets/adobe-oauth-state-fjk_YXgL.js +0 -1
- package/dist/ui/assets/dip-B7G-yFNh.js +0 -1
- package/dist/ui/assets/esp8266-qZDXfwS_.js +0 -1
- package/dist/ui/assets/fs-DUiVyDAh.js +0 -1
- package/dist/ui/assets/fs-DhW0sB4P.js +0 -2
- package/dist/ui/assets/main-FJNMV_71.js +0 -24
- package/dist/ui/assets/mount-CuXSlUe4.js +0 -1
- package/dist/ui/assets/mount-DU8rtYg8.js +0 -2
- package/dist/ui/assets/new-session-RQLW9UpV.js +0 -2
- package/dist/ui/assets/page-follower-tray-eZi5RMQE.js +0 -4
- package/dist/ui/assets/panel-rpc-handlers-D11yFCCd.js +0 -2
- package/dist/ui/assets/provider-store-access-BiIwB6XY.js +0 -1
- package/dist/ui/assets/provider-store-access-mtb0ZoHI.js +0 -1
- package/dist/ui/assets/proxied-fetch-CKDBmEMl.js +0 -2
- package/dist/ui/assets/proxied-fetch-CM0jk_Mr.js +0 -1
- package/dist/ui/assets/quick-llm-I3lG0JAs.js +0 -1
- package/dist/ui/assets/session-freezer-BBOMQDZu.js +0 -1
- package/dist/ui/assets/src-CpdsjHiA.js +0 -1
- package/dist/ui/assets/src-DlEHAkLd.js +0 -1
- package/dist/ui/assets/wc-live-CC1IJ9qx.js +0 -253
- package/dist/ui/assets/wc-onboarding-CJV5ViMw.js +0 -2
- package/dist/ui/assets/wc-settings-zWM-erHv.js +0 -24
- package/dist/ui/assets/wc-tray-BJZGo5tr.js +0 -2
- package/dist/ui/assets/whisper-session-Dt-B3KmT.js +0 -2
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decode a base64 string to `Uint8Array`.
|
|
3
|
+
*
|
|
4
|
+
* Prefers Node's `Buffer.from(b64, 'base64')` when available — measurably
|
|
5
|
+
* faster than the per-byte `atob` loop for the multi-MB S3 mount and
|
|
6
|
+
* federated-VFS payloads the CLI float moves. The browser / extension
|
|
7
|
+
* service-worker fallback round-trips through `atob`, which is a
|
|
8
|
+
* universal global.
|
|
9
|
+
*
|
|
10
|
+
* Two normalizations bridge the platform gap so both paths are
|
|
11
|
+
* observationally identical:
|
|
12
|
+
* - the Node path copies the `Buffer` into a fresh `Uint8Array` so the
|
|
13
|
+
* caller gets a plain prototype backed by a standalone `ArrayBuffer`
|
|
14
|
+
* (not Node's slab pool, which `Buffer.from` draws from for small
|
|
15
|
+
* inputs) — callers that read `.buffer` downstream and tests that
|
|
16
|
+
* deep-equal the bytes both need this;
|
|
17
|
+
* - the Node path validates against {@link STRICT_BASE64_RE} first and
|
|
18
|
+
* throws on invalid input. `atob` already throws; `Buffer.from`
|
|
19
|
+
* silently strips invalid characters, which would let bad payloads
|
|
20
|
+
* through callers that rely on the decoder rejecting them.
|
|
21
|
+
*/
|
|
22
|
+
export declare function base64ToUint8(b64: string): Uint8Array<ArrayBuffer>;
|
|
23
|
+
/**
|
|
24
|
+
* Encode a `Uint8Array` to a base64 string.
|
|
25
|
+
*
|
|
26
|
+
* Prefers Node's `Buffer.from(bytes).toString('base64')` when available.
|
|
27
|
+
* Otherwise builds the binary string in {@link CHUNK_SIZE}-byte chunks
|
|
28
|
+
* through `String.fromCharCode.apply` — the unchunked spread overflows
|
|
29
|
+
* the call stack on inputs larger than ~64 KiB.
|
|
30
|
+
*/
|
|
31
|
+
export declare function uint8ToBase64(bytes: Uint8Array): string;
|
|
32
|
+
//# sourceMappingURL=base64.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAiDA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAclE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAWvD"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Universal Uint8Array ↔ base64 codec.
|
|
2
|
+
//
|
|
3
|
+
// One implementation shared across the browser, the extension service
|
|
4
|
+
// worker, and Node 22+ — same correctness, same chunk size, same Node
|
|
5
|
+
// fast-path. Replaces 13+ hand-rolled copies that had drifted on chunk
|
|
6
|
+
// size (8192 vs 0x8000) and on whether they used `Buffer` at all; see
|
|
7
|
+
// the inventory in issue #1087.
|
|
8
|
+
//
|
|
9
|
+
// Conforms to `packages/shared-ts/CLAUDE.md` "universal globals" rule:
|
|
10
|
+
// `atob` / `btoa` are global in every runtime we target; the Node
|
|
11
|
+
// `Buffer` fast-path is feature-detected via `globalThis` and the
|
|
12
|
+
// structural type is declared locally so this package keeps building
|
|
13
|
+
// without `@types/node` (the same pattern `sign-and-forward.ts` uses).
|
|
14
|
+
// `0x8000` (32 KiB) matches the chunk size in
|
|
15
|
+
// `kernel/transport-binary-codec.ts` and the rationale comment there:
|
|
16
|
+
// the naive `String.fromCharCode(...bytes)` overflows the call stack on
|
|
17
|
+
// inputs larger than ~64 KiB; chunking keeps the worst case bounded.
|
|
18
|
+
const CHUNK_SIZE = 0x8000;
|
|
19
|
+
function nodeBuffer() {
|
|
20
|
+
return globalThis.Buffer;
|
|
21
|
+
}
|
|
22
|
+
// Strict base64 grammar (no URL-safe variants — matches `atob`). Used to
|
|
23
|
+
// gate Node's lenient `Buffer.from('base64')` so a malformed input throws
|
|
24
|
+
// here instead of being silently stripped: the signed-fetch transport
|
|
25
|
+
// surfaces a malformed reply as a clean "decode failed" EIO and relies
|
|
26
|
+
// on the decoder being strict.
|
|
27
|
+
//
|
|
28
|
+
// Alphabet alone is not enough — `Buffer.from('abcde', 'base64')` and
|
|
29
|
+
// `Buffer.from('abcd=', 'base64')` both decode successfully (and silently
|
|
30
|
+
// drop or pad the trailing junk), while `atob` throws. Enforce the full
|
|
31
|
+
// shape: a run of 4-char groups, optionally followed by one final group
|
|
32
|
+
// of 2+`==`, 3+`=`, or 4 alphabet chars. Empty string is valid.
|
|
33
|
+
const STRICT_BASE64_RE = /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4})?$/;
|
|
34
|
+
/**
|
|
35
|
+
* Decode a base64 string to `Uint8Array`.
|
|
36
|
+
*
|
|
37
|
+
* Prefers Node's `Buffer.from(b64, 'base64')` when available — measurably
|
|
38
|
+
* faster than the per-byte `atob` loop for the multi-MB S3 mount and
|
|
39
|
+
* federated-VFS payloads the CLI float moves. The browser / extension
|
|
40
|
+
* service-worker fallback round-trips through `atob`, which is a
|
|
41
|
+
* universal global.
|
|
42
|
+
*
|
|
43
|
+
* Two normalizations bridge the platform gap so both paths are
|
|
44
|
+
* observationally identical:
|
|
45
|
+
* - the Node path copies the `Buffer` into a fresh `Uint8Array` so the
|
|
46
|
+
* caller gets a plain prototype backed by a standalone `ArrayBuffer`
|
|
47
|
+
* (not Node's slab pool, which `Buffer.from` draws from for small
|
|
48
|
+
* inputs) — callers that read `.buffer` downstream and tests that
|
|
49
|
+
* deep-equal the bytes both need this;
|
|
50
|
+
* - the Node path validates against {@link STRICT_BASE64_RE} first and
|
|
51
|
+
* throws on invalid input. `atob` already throws; `Buffer.from`
|
|
52
|
+
* silently strips invalid characters, which would let bad payloads
|
|
53
|
+
* through callers that rely on the decoder rejecting them.
|
|
54
|
+
*/
|
|
55
|
+
export function base64ToUint8(b64) {
|
|
56
|
+
const B = nodeBuffer();
|
|
57
|
+
if (B) {
|
|
58
|
+
if (!STRICT_BASE64_RE.test(b64)) {
|
|
59
|
+
throw new Error('Invalid base64 string');
|
|
60
|
+
}
|
|
61
|
+
return new Uint8Array(B.from(b64, 'base64'));
|
|
62
|
+
}
|
|
63
|
+
const binary = atob(b64);
|
|
64
|
+
const bytes = new Uint8Array(binary.length);
|
|
65
|
+
for (let i = 0; i < binary.length; i++) {
|
|
66
|
+
bytes[i] = binary.charCodeAt(i);
|
|
67
|
+
}
|
|
68
|
+
return bytes;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Encode a `Uint8Array` to a base64 string.
|
|
72
|
+
*
|
|
73
|
+
* Prefers Node's `Buffer.from(bytes).toString('base64')` when available.
|
|
74
|
+
* Otherwise builds the binary string in {@link CHUNK_SIZE}-byte chunks
|
|
75
|
+
* through `String.fromCharCode.apply` — the unchunked spread overflows
|
|
76
|
+
* the call stack on inputs larger than ~64 KiB.
|
|
77
|
+
*/
|
|
78
|
+
export function uint8ToBase64(bytes) {
|
|
79
|
+
const B = nodeBuffer();
|
|
80
|
+
if (B) {
|
|
81
|
+
return B.from(bytes).toString('base64');
|
|
82
|
+
}
|
|
83
|
+
let binary = '';
|
|
84
|
+
for (let i = 0; i < bytes.byteLength; i += CHUNK_SIZE) {
|
|
85
|
+
const slice = bytes.subarray(i, Math.min(i + CHUNK_SIZE, bytes.byteLength));
|
|
86
|
+
binary += String.fromCharCode.apply(null, slice);
|
|
87
|
+
}
|
|
88
|
+
return btoa(binary);
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=base64.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base64.js","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,EAAE;AACF,sEAAsE;AACtE,sEAAsE;AACtE,uEAAuE;AACvE,sEAAsE;AACtE,gCAAgC;AAChC,EAAE;AACF,uEAAuE;AACvE,kEAAkE;AAClE,kEAAkE;AAClE,qEAAqE;AACrE,uEAAuE;AAEvE,8CAA8C;AAC9C,sEAAsE;AACtE,wEAAwE;AACxE,qEAAqE;AACrE,MAAM,UAAU,GAAG,MAAM,CAAC;AAa1B,SAAS,UAAU;IACjB,OAAQ,UAA0C,CAAC,MAAM,CAAC;AAC5D,CAAC;AAED,yEAAyE;AACzE,0EAA0E;AAC1E,sEAAsE;AACtE,uEAAuE;AACvE,+BAA+B;AAC/B,EAAE;AACF,sEAAsE;AACtE,0EAA0E;AAC1E,wEAAwE;AACxE,wEAAwE;AACxE,gEAAgE;AAChE,MAAM,gBAAgB,GACpB,mFAAmF,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,CAAC;QACN,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,KAAK,CAAC,uBAAuB,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;IACzB,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,MAAM,CAAC,GAAG,UAAU,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,CAAC;QACN,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,UAAU,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;QACtD,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;QAC5E,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,KAA4B,CAAC,CAAC;IAC1E,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,uBAAuB,CAAC;AACtC,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iFAAiF;AAEjF,cAAc,uBAAuB,CAAC;AACtC,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iFAAiF;AAEjF,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC;AACpC,cAAc,uBAAuB,CAAC;AACtC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-and-forward.d.ts","sourceRoot":"","sources":["../src/sign-and-forward.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;
|
|
1
|
+
{"version":3,"file":"sign-and-forward.d.ts","sourceRoot":"","sources":["../src/sign-and-forward.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,QAAA,MAAM,eAAe,mDAAoD,CAAC;AAC1E,KAAK,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAiBrD,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,uEAAuE;IACvE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,MAAM,uBAAuB,GAC/B,iBAAiB,GACjB,iBAAiB,GACjB,wBAAwB,GACxB,cAAc,GACd,UAAU,CAAC;AAEf,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,IAAI,CAAC;IACT,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,uBAAuB,CAAC;CACpC;AAED,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,GAAG,qBAAqB,CAAC;AAEhF;;;GAGG;AACH,MAAM,WAAW,YAAY;IAC3B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;CAC/C;AA+FD;;;;;;GAMG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAAG,SAAS,EAClD,KAAK,EAAE,YAAY,EACnB,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,mBAAmB,CAAC,CAsF9B;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAC3C,GAAG,EAAE,OAAO,CAAC,wBAAwB,CAAC,GAAG,SAAS,EAClD,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,mBAAmB,CAAC,CA6D9B"}
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
* wraps these in Express handlers via a SecretStore adapter)
|
|
17
17
|
* - tests in `packages/shared-ts/tests/sign-and-forward.test.ts`
|
|
18
18
|
*/
|
|
19
|
+
import { base64ToUint8, uint8ToBase64 } from './base64.js';
|
|
19
20
|
import { signSigV4 } from './sigv4.js';
|
|
20
21
|
// ---------------- envelope contract ----------------
|
|
21
22
|
const ALLOWED_METHODS = ['GET', 'PUT', 'POST', 'DELETE', 'HEAD'];
|
|
@@ -31,38 +32,7 @@ const HOP_BY_HOP = new Set([
|
|
|
31
32
|
'upgrade',
|
|
32
33
|
]);
|
|
33
34
|
const DA_ORIGIN = 'https://admin.da.live';
|
|
34
|
-
|
|
35
|
-
return globalThis.Buffer;
|
|
36
|
-
}
|
|
37
|
-
function decodeBase64(b64) {
|
|
38
|
-
// Node fast-path: Buffer.from decodes the multi-MB S3 mount payloads the CLI
|
|
39
|
-
// float moves far faster than the per-byte atob loop. Feature-detected so the
|
|
40
|
-
// browser and extension service-worker bundles (no Buffer global) fall back to
|
|
41
|
-
// the universal path. Buffer extends Uint8Array, so the return type holds.
|
|
42
|
-
const B = nodeBuffer();
|
|
43
|
-
if (B) {
|
|
44
|
-
return B.from(b64, 'base64');
|
|
45
|
-
}
|
|
46
|
-
// atob is a global in browsers, extension service workers, and Node 22+.
|
|
47
|
-
const binary = atob(b64);
|
|
48
|
-
const bytes = new Uint8Array(binary.length);
|
|
49
|
-
for (let i = 0; i < binary.length; i++) {
|
|
50
|
-
bytes[i] = binary.charCodeAt(i);
|
|
51
|
-
}
|
|
52
|
-
return bytes;
|
|
53
|
-
}
|
|
54
|
-
function encodeBase64(bytes) {
|
|
55
|
-
// Node fast-path — see decodeBase64.
|
|
56
|
-
const B = nodeBuffer();
|
|
57
|
-
if (B) {
|
|
58
|
-
return B.from(bytes).toString('base64');
|
|
59
|
-
}
|
|
60
|
-
let binary = '';
|
|
61
|
-
for (let i = 0; i < bytes.length; i++) {
|
|
62
|
-
binary += String.fromCharCode(bytes[i]);
|
|
63
|
-
}
|
|
64
|
-
return btoa(binary);
|
|
65
|
-
}
|
|
35
|
+
// ---------------- helpers ----------------
|
|
66
36
|
function isAllowedMethod(m) {
|
|
67
37
|
return typeof m === 'string' && ALLOWED_METHODS.includes(m);
|
|
68
38
|
}
|
|
@@ -175,7 +145,7 @@ export async function executeS3SignAndForward(env, store, fetchImpl = fetch) {
|
|
|
175
145
|
};
|
|
176
146
|
}
|
|
177
147
|
const body = typeof env.bodyBase64 === 'string' && env.bodyBase64.length > 0
|
|
178
|
-
?
|
|
148
|
+
? base64ToUint8(env.bodyBase64)
|
|
179
149
|
: undefined;
|
|
180
150
|
const signed = await signSigV4({
|
|
181
151
|
method: env.method,
|
|
@@ -207,7 +177,7 @@ export async function executeS3SignAndForward(env, store, fetchImpl = fetch) {
|
|
|
207
177
|
ok: true,
|
|
208
178
|
status: upstream.status,
|
|
209
179
|
headers: passthroughHeaders(upstream),
|
|
210
|
-
bodyBase64:
|
|
180
|
+
bodyBase64: uint8ToBase64(upstreamBody),
|
|
211
181
|
};
|
|
212
182
|
}
|
|
213
183
|
/**
|
|
@@ -247,7 +217,7 @@ export async function executeDaSignAndForward(env, fetchImpl = fetch) {
|
|
|
247
217
|
};
|
|
248
218
|
}
|
|
249
219
|
const body = typeof env.bodyBase64 === 'string' && env.bodyBase64.length > 0
|
|
250
|
-
?
|
|
220
|
+
? base64ToUint8(env.bodyBase64)
|
|
251
221
|
: undefined;
|
|
252
222
|
let upstream;
|
|
253
223
|
try {
|
|
@@ -272,7 +242,7 @@ export async function executeDaSignAndForward(env, fetchImpl = fetch) {
|
|
|
272
242
|
ok: true,
|
|
273
243
|
status: upstream.status,
|
|
274
244
|
headers: passthroughHeaders(upstream),
|
|
275
|
-
bodyBase64:
|
|
245
|
+
bodyBase64: uint8ToBase64(upstreamBody),
|
|
276
246
|
};
|
|
277
247
|
}
|
|
278
248
|
//# sourceMappingURL=sign-and-forward.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sign-and-forward.js","sourceRoot":"","sources":["../src/sign-and-forward.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,sDAAsD;AAEtD,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAU,CAAC;AAG1E,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAE/C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,YAAY;IACZ,YAAY;IACZ,oBAAoB;IACpB,qBAAqB;IACrB,IAAI;IACJ,SAAS;IACT,mBAAmB;IACnB,SAAS;CACV,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"sign-and-forward.js","sourceRoot":"","sources":["../src/sign-and-forward.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,sDAAsD;AAEtD,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAU,CAAC;AAG1E,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAE/C,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC;IACzB,YAAY;IACZ,YAAY;IACZ,oBAAoB;IACpB,qBAAqB;IACrB,IAAI;IACJ,SAAS;IACT,mBAAmB;IACnB,SAAS;CACV,CAAC,CAAC;AAEH,MAAM,SAAS,GAAG,uBAAuB,CAAC;AA6D1C,4CAA4C;AAE5C,SAAS,eAAe,CAAC,CAAU;IACjC,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAK,eAAqC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;AACrF,CAAC;AAED,MAAM,yBAA0B,SAAQ,KAAK;IAC3C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,2BAA2B,CAAC;IAC1C,CAAC;CACF;AAED,KAAK,UAAU,gBAAgB,CAAC,IAAY,EAAE,KAAmB;IAC/D,MAAM,WAAW,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,gBAAgB,CAAC,CAAC;IAChE,MAAM,eAAe,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,oBAAoB,CAAC,CAAC;IAExE,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,MAAM,IAAI,yBAAyB,CACjC,YAAY,IAAI,4CAA4C;YAC1D,6BAA6B,IAAI,wBAAwB,CAC5D,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,eAAe,EAAE,CAAC;QACrB,MAAM,IAAI,yBAAyB,CACjC,YAAY,IAAI,gDAAgD;YAC9D,6BAA6B,IAAI,4BAA4B,CAChE,CAAC;IACJ,CAAC;IAED,OAAO;QACL,WAAW;QACX,eAAe;QACf,YAAY,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,gBAAgB,CAAC;QACzD,MAAM,EAAE,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC,IAAI,WAAW;QAC7D,QAAQ,EAAE,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,WAAW,CAAC;QAChD,SAAS,EAAE,CAAC,MAAM,KAAK,CAAC,GAAG,CAAC,MAAM,IAAI,aAAa,CAAC,CAAC,KAAK,MAAM;KACjE,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CACjB,OAAkB,EAClB,MAAc,EACd,GAAW,EACX,KAA8B;IAE9B,IAAI,IAAY,CAAC;IACjB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC;QACxC,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,wCAAwC,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC9E,CAAC;IACH,CAAC;SAAM,CAAC;QACN,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,gBAAgB,CAAC;IAC9C,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACpE,MAAM,aAAa,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;IAEjD,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,aAAa,IAAI,UAAU,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC;IACnF,MAAM,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,aAAa,IAAI,IAAI,EAAE,CAAC;IACvE,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,WAAW,QAAQ,IAAI,QAAQ,EAAE,CAAC,CAAC;IAEvD,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAS,kBAAkB,CAAC,QAAkB;IAC5C,MAAM,GAAG,GAA2B,EAAE,CAAC;IACvC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;YACvC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACnB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC;AAED,kDAAkD;AAElD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,GAAkD,EAClD,KAAmB,EACnB,YAA0B,KAAK;IAE/B,IACE,OAAO,GAAG,EAAE,OAAO,KAAK,QAAQ;QAChC,GAAG,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;QACxB,CAAC,kBAAkB,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EACrC,CAAC;QACD,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,uEAAuE;YAC9E,SAAS,EAAE,iBAAiB;SAC7B,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC9E,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9D,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC9E,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;QAChC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC3E,CAAC;IAED,IAAI,OAAkB,CAAC;IACvB,IAAI,CAAC;QACH,OAAO,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACvD,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,yBAAyB,EAAE,CAAC;YAC7C,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,wBAAwB,EAAE,CAAC;QAChF,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;IAED,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IAC5D,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB;YACjE,SAAS,EAAE,iBAAiB;SAC7B,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GACR,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QAC7D,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC;QAC/B,CAAC,CAAC,SAAS,CAAC;IAEhB,MAAM,MAAM,GAAG,MAAM,SAAS,CAC5B;QACE,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,GAAG;QACH,OAAO,EAAE,EAAE,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,EAAE;QACnD,IAAI;KACL,EACD;QACE,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,eAAe,EAAE,OAAO,CAAC,eAAe;QACxC,YAAY,EAAE,OAAO,CAAC,YAAY;KACnC,EACD,OAAO,CAAC,MAAM,EACd,IAAI,CACL,CAAC;IAEF,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YACzC,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,IAAI,EAAE,MAAM,CAAC,IAA2B;SACzC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;YAC7E,SAAS,EAAE,cAAc;SAC1B,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IAClE,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC;QACrC,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC;KACxC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,GAAkD,EAClD,YAA0B,KAAK;IAE/B,IAAI,OAAO,GAAG,EAAE,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACnE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,sBAAsB,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC;IACpF,CAAC;IACD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,gBAAgB,EAAE,SAAS,EAAE,iBAAiB,EAAE,CAAC;IAC9E,CAAC;IACD,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QAC9D,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,uCAAuC;YAC9C,SAAS,EAAE,iBAAiB;SAC7B,CAAC;IACJ,CAAC;IAED,IAAI,GAAQ,CAAC;IACb,IAAI,CAAC;QACH,GAAG,GAAG,IAAI,GAAG,CAAC,SAAS,GAAG,GAAG,CAAC,IAAI,CAAC,CAAC;QACpC,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;YACd,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC/C,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB;YACjE,SAAS,EAAE,iBAAiB;SAC7B,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GACR,OAAO,GAAG,CAAC,UAAU,KAAK,QAAQ,IAAI,GAAG,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC;QAC7D,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC;QAC/B,CAAC,CAAC,SAAS,CAAC;IAEhB,IAAI,QAAkB,CAAC;IACvB,IAAI,CAAC;QACH,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE;YACzC,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,OAAO,EAAE;gBACP,GAAG,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC;gBACtB,aAAa,EAAE,UAAU,GAAG,CAAC,QAAQ,EAAE;aACxC;YACD,IAAI,EAAE,IAA2B;SAClC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,EAAE,EAAE,KAAK;YACT,KAAK,EAAE,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;YAC7E,SAAS,EAAE,cAAc;SAC1B,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAG,IAAI,UAAU,CAAC,MAAM,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC;IAClE,OAAO;QACL,EAAE,EAAE,IAAI;QACR,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,OAAO,EAAE,kBAAkB,CAAC,QAAQ,CAAC;QACrC,UAAU,EAAE,aAAa,CAAC,YAAY,CAAC;KACxC,CAAC;AACJ,CAAC"}
|
|
@@ -116,12 +116,25 @@ export interface ThinBootstrapSet {
|
|
|
116
116
|
* 'no-src' / 'blank:<href>'`.
|
|
117
117
|
*/
|
|
118
118
|
export declare const OVERLAY_LOADED_PROBE_EXPRESSION = "(function() {\n var host = document.getElementById('slicc-electron-overlay-root');\n if (!host || !host.shadowRoot) return 'no-host';\n var iframe = host.shadowRoot.querySelector('iframe');\n if (!iframe) return 'no-iframe';\n if (!iframe.src) return 'no-src';\n try {\n // Thin-bridge overlay is ALWAYS cross-origin (hosted webapp) vs the app\n // document. A committed cross-origin navigation makes this access THROW.\n // Any READABLE href means the cross-origin nav did NOT commit \u2014 still\n // about:blank, or swapped to chrome-error://chromewebdata/ by a CSP block \u2014\n // so the overlay did NOT load and the setBypassCSP escalation must fire.\n var href = iframe.contentWindow && iframe.contentWindow.location ? iframe.contentWindow.location.href : '';\n return 'blank:' + href;\n } catch (e) {\n return 'ok';\n }\n })()";
|
|
119
|
+
/**
|
|
120
|
+
* JS probe that reports whether the overlay was *evicted* — i.e. the
|
|
121
|
+
* `window.__SLICC_ELECTRON_OVERLAY__` marker is still present (the bootstrap
|
|
122
|
+
* ran at least once on this document) but the `#slicc-electron-overlay-root`
|
|
123
|
+
* host element is gone, which happens when an SPA framework (React/Vue)
|
|
124
|
+
* re-renders the DOM root out from under it on an in-page route change.
|
|
125
|
+
* Returns `'evicted'` only in that exact state so re-injection is gated to the
|
|
126
|
+
* genuine eviction case and never loops while the host element is still
|
|
127
|
+
* attached. A full document replacement wipes the marker too, so that case
|
|
128
|
+
* reports `'ok'` here and is covered by the new-document hook instead.
|
|
129
|
+
*/
|
|
130
|
+
export declare const OVERLAY_EVICTED_PROBE_EXPRESSION = "(function() {\n try {\n var hasMarker = typeof window.__SLICC_ELECTRON_OVERLAY__ !== 'undefined';\n var hasRoot = !!document.getElementById('slicc-electron-overlay-root');\n return (hasMarker && !hasRoot) ? 'evicted' : 'ok';\n } catch (e) {\n return 'ok';\n }\n })()";
|
|
119
131
|
export declare class ElectronOverlayInjector {
|
|
120
132
|
private readonly cdpPort;
|
|
121
133
|
private readonly servePort;
|
|
122
134
|
/** Thin-mode bootstrap pair — the only overlay path. */
|
|
123
135
|
private readonly thinBootstraps;
|
|
124
136
|
private readonly probeDelayMs;
|
|
137
|
+
private readonly presenceCheckIntervalMs;
|
|
125
138
|
private readonly connections;
|
|
126
139
|
private readonly cspBypassedTargets;
|
|
127
140
|
/**
|
|
@@ -156,6 +169,7 @@ export declare class ElectronOverlayInjector {
|
|
|
156
169
|
servePort: number;
|
|
157
170
|
thinBootstraps?: ThinBootstrapSet;
|
|
158
171
|
probeDelayMs?: number;
|
|
172
|
+
presenceCheckIntervalMs?: number;
|
|
159
173
|
}): ElectronOverlayInjector;
|
|
160
174
|
/** Test-only: snapshot the elected leader target URL (null when no leader). */
|
|
161
175
|
_testingLeaderTargetUrl(): string | null;
|
|
@@ -199,6 +213,33 @@ export declare class ElectronOverlayInjector {
|
|
|
199
213
|
* bootstrap is target-specific (leader vs. follower).
|
|
200
214
|
*/
|
|
201
215
|
private buildThemedBootstrap;
|
|
216
|
+
/**
|
|
217
|
+
* Wrap the target's role bootstrap in a top-frame guard for use as a
|
|
218
|
+
* `Page.addScriptToEvaluateOnNewDocument` source. The hook fires in every
|
|
219
|
+
* frame of a new document, so without the guard the overlay iframe (the
|
|
220
|
+
* hosted webapp, which also ships `__SLICC_ELECTRON_OVERLAY__`) would re-run
|
|
221
|
+
* the bootstrap inside itself and recurse. Re-using `resolveBootstrapForTarget`
|
|
222
|
+
* keeps the re-injected overlay's leader/follower role stable.
|
|
223
|
+
*/
|
|
224
|
+
private buildNewDocumentBootstrap;
|
|
225
|
+
/**
|
|
226
|
+
* Evaluate {@link OVERLAY_EVICTED_PROBE_EXPRESSION} on the target and resolve
|
|
227
|
+
* `true` only when the overlay marker is present but the host element is gone
|
|
228
|
+
* — the SPA-DOM-root eviction case that re-injection must repair. Mirrors
|
|
229
|
+
* {@link probeOverlayIframeLoaded}'s one-shot message-listener pattern.
|
|
230
|
+
*/
|
|
231
|
+
private probeOverlayEvicted;
|
|
232
|
+
/**
|
|
233
|
+
* Re-inject the overlay if (and only if) it was evicted from an
|
|
234
|
+
* already-connected target — an in-page SPA route change or DOM-root
|
|
235
|
+
* re-render that removed `#slicc-electron-overlay-root` while the
|
|
236
|
+
* `__SLICC_ELECTRON_OVERLAY__` marker persists. Gated on the eviction probe
|
|
237
|
+
* so it is idempotent and never loops while the host element is still
|
|
238
|
+
* attached, and skipped while the CSP-bypass reload / Fetch-proxy escalation
|
|
239
|
+
* owns injection (`pendingReload`). Re-uses the target's existing role
|
|
240
|
+
* bootstrap, so no leader/follower re-election occurs.
|
|
241
|
+
*/
|
|
242
|
+
private reinjectIfEvicted;
|
|
202
243
|
/**
|
|
203
244
|
* Handle the initial CDP `ws.on('open', ...)` event for a target: enable
|
|
204
245
|
* Runtime/Page, set CSP bypass, detect theme, inject the overlay, and (on a
|
|
@@ -10,6 +10,12 @@ import { BRIDGE_TOKEN_QUERY_PARAM, BRIDGE_WS_QUERY_PARAM } from './bridge-securi
|
|
|
10
10
|
import { buildElectronAppLaunchSpec, buildElectronOverlayBootstrapScript, buildElectronOverlayEntryUrl, ELECTRON_OVERLAY_APP_PATH, getElectronOverlayEntryDistPath, getElectronServeOrigin, selectBestOverlayTargets, } from './electron-runtime.js';
|
|
11
11
|
const execFile = promisify(nodeExecFile);
|
|
12
12
|
const ELECTRON_OVERLAY_SYNC_INTERVAL_MS = 1500;
|
|
13
|
+
/**
|
|
14
|
+
* Cadence of the per-connection overlay presence re-check. Covers SPAs that
|
|
15
|
+
* re-render their DOM root (evicting `#slicc-electron-overlay-root`) without
|
|
16
|
+
* emitting any navigation event for the event-driven re-injection to hook.
|
|
17
|
+
*/
|
|
18
|
+
const ELECTRON_OVERLAY_PRESENCE_CHECK_INTERVAL_MS = 2000;
|
|
13
19
|
/**
|
|
14
20
|
* Query param name used to mark the role of an overlay tab on the hosted
|
|
15
21
|
* launcher URL. The pinned leader carries `role=leader`; auto-follow
|
|
@@ -516,12 +522,33 @@ export const OVERLAY_LOADED_PROBE_EXPRESSION = `(function() {
|
|
|
516
522
|
return 'ok';
|
|
517
523
|
}
|
|
518
524
|
})()`;
|
|
525
|
+
/**
|
|
526
|
+
* JS probe that reports whether the overlay was *evicted* — i.e. the
|
|
527
|
+
* `window.__SLICC_ELECTRON_OVERLAY__` marker is still present (the bootstrap
|
|
528
|
+
* ran at least once on this document) but the `#slicc-electron-overlay-root`
|
|
529
|
+
* host element is gone, which happens when an SPA framework (React/Vue)
|
|
530
|
+
* re-renders the DOM root out from under it on an in-page route change.
|
|
531
|
+
* Returns `'evicted'` only in that exact state so re-injection is gated to the
|
|
532
|
+
* genuine eviction case and never loops while the host element is still
|
|
533
|
+
* attached. A full document replacement wipes the marker too, so that case
|
|
534
|
+
* reports `'ok'` here and is covered by the new-document hook instead.
|
|
535
|
+
*/
|
|
536
|
+
export const OVERLAY_EVICTED_PROBE_EXPRESSION = `(function() {
|
|
537
|
+
try {
|
|
538
|
+
var hasMarker = typeof window.__SLICC_ELECTRON_OVERLAY__ !== 'undefined';
|
|
539
|
+
var hasRoot = !!document.getElementById('slicc-electron-overlay-root');
|
|
540
|
+
return (hasMarker && !hasRoot) ? 'evicted' : 'ok';
|
|
541
|
+
} catch (e) {
|
|
542
|
+
return 'ok';
|
|
543
|
+
}
|
|
544
|
+
})()`;
|
|
519
545
|
export class ElectronOverlayInjector {
|
|
520
546
|
cdpPort;
|
|
521
547
|
servePort;
|
|
522
548
|
/** Thin-mode bootstrap pair — the only overlay path. */
|
|
523
549
|
thinBootstraps;
|
|
524
550
|
probeDelayMs;
|
|
551
|
+
presenceCheckIntervalMs;
|
|
525
552
|
connections = new Map();
|
|
526
553
|
cspBypassedTargets = new Set();
|
|
527
554
|
/**
|
|
@@ -532,11 +559,12 @@ export class ElectronOverlayInjector {
|
|
|
532
559
|
leaderTargetUrl = null;
|
|
533
560
|
syncTimer = null;
|
|
534
561
|
syncing = false;
|
|
535
|
-
constructor(cdpPort, servePort, thinBootstraps, probeDelayMs = 1500) {
|
|
562
|
+
constructor(cdpPort, servePort, thinBootstraps, probeDelayMs = 1500, presenceCheckIntervalMs = ELECTRON_OVERLAY_PRESENCE_CHECK_INTERVAL_MS) {
|
|
536
563
|
this.cdpPort = cdpPort;
|
|
537
564
|
this.servePort = servePort;
|
|
538
565
|
this.thinBootstraps = thinBootstraps;
|
|
539
566
|
this.probeDelayMs = probeDelayMs;
|
|
567
|
+
this.presenceCheckIntervalMs = presenceCheckIntervalMs;
|
|
540
568
|
}
|
|
541
569
|
static async create(options) {
|
|
542
570
|
const bundleSource = await loadElectronOverlayBundleSource(options);
|
|
@@ -558,7 +586,7 @@ export class ElectronOverlayInjector {
|
|
|
558
586
|
* `_testing_*` hooks on `ElectronOverlayInjector`.
|
|
559
587
|
*/
|
|
560
588
|
static _createForTesting(options) {
|
|
561
|
-
return new ElectronOverlayInjector(options.cdpPort ?? 9223, options.servePort, options.thinBootstraps ?? { leader: '/* test-leader */', follower: '/* test-follower */' }, options.probeDelayMs ?? 1500);
|
|
589
|
+
return new ElectronOverlayInjector(options.cdpPort ?? 9223, options.servePort, options.thinBootstraps ?? { leader: '/* test-leader */', follower: '/* test-follower */' }, options.probeDelayMs ?? 1500, options.presenceCheckIntervalMs ?? ELECTRON_OVERLAY_PRESENCE_CHECK_INTERVAL_MS);
|
|
562
590
|
}
|
|
563
591
|
/** Test-only: snapshot the elected leader target URL (null when no leader). */
|
|
564
592
|
_testingLeaderTargetUrl() {
|
|
@@ -739,6 +767,76 @@ export class ElectronOverlayInjector {
|
|
|
739
767
|
const themeScript = `try{localStorage.setItem('slicc-theme',${JSON.stringify(theme)})}catch(e){}`;
|
|
740
768
|
return `${themeScript}\n${this.resolveBootstrapForTarget(target)}`;
|
|
741
769
|
}
|
|
770
|
+
/**
|
|
771
|
+
* Wrap the target's role bootstrap in a top-frame guard for use as a
|
|
772
|
+
* `Page.addScriptToEvaluateOnNewDocument` source. The hook fires in every
|
|
773
|
+
* frame of a new document, so without the guard the overlay iframe (the
|
|
774
|
+
* hosted webapp, which also ships `__SLICC_ELECTRON_OVERLAY__`) would re-run
|
|
775
|
+
* the bootstrap inside itself and recurse. Re-using `resolveBootstrapForTarget`
|
|
776
|
+
* keeps the re-injected overlay's leader/follower role stable.
|
|
777
|
+
*/
|
|
778
|
+
buildNewDocumentBootstrap(target) {
|
|
779
|
+
const bootstrap = this.resolveBootstrapForTarget(target);
|
|
780
|
+
return `(function(){try{if(window.top!==window.self)return;}catch(e){return;}\n${bootstrap}\n})();`;
|
|
781
|
+
}
|
|
782
|
+
/**
|
|
783
|
+
* Evaluate {@link OVERLAY_EVICTED_PROBE_EXPRESSION} on the target and resolve
|
|
784
|
+
* `true` only when the overlay marker is present but the host element is gone
|
|
785
|
+
* — the SPA-DOM-root eviction case that re-injection must repair. Mirrors
|
|
786
|
+
* {@link probeOverlayIframeLoaded}'s one-shot message-listener pattern.
|
|
787
|
+
*/
|
|
788
|
+
async probeOverlayEvicted(ws, send) {
|
|
789
|
+
return new Promise((resolve) => {
|
|
790
|
+
const probeId = send('Runtime.evaluate', {
|
|
791
|
+
expression: OVERLAY_EVICTED_PROBE_EXPRESSION,
|
|
792
|
+
awaitPromise: false,
|
|
793
|
+
returnByValue: true,
|
|
794
|
+
});
|
|
795
|
+
const timeout = setTimeout(() => {
|
|
796
|
+
cleanup();
|
|
797
|
+
resolve(false);
|
|
798
|
+
}, 3000);
|
|
799
|
+
const onMessage = (data) => {
|
|
800
|
+
try {
|
|
801
|
+
const msg = JSON.parse(data.toString());
|
|
802
|
+
if (msg.id === probeId) {
|
|
803
|
+
cleanup();
|
|
804
|
+
resolve(msg.result?.result?.value === 'evicted');
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
catch {
|
|
808
|
+
/* ignore */
|
|
809
|
+
}
|
|
810
|
+
};
|
|
811
|
+
const cleanup = () => {
|
|
812
|
+
clearTimeout(timeout);
|
|
813
|
+
ws.off('message', onMessage);
|
|
814
|
+
};
|
|
815
|
+
ws.on('message', onMessage);
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
/**
|
|
819
|
+
* Re-inject the overlay if (and only if) it was evicted from an
|
|
820
|
+
* already-connected target — an in-page SPA route change or DOM-root
|
|
821
|
+
* re-render that removed `#slicc-electron-overlay-root` while the
|
|
822
|
+
* `__SLICC_ELECTRON_OVERLAY__` marker persists. Gated on the eviction probe
|
|
823
|
+
* so it is idempotent and never loops while the host element is still
|
|
824
|
+
* attached, and skipped while the CSP-bypass reload / Fetch-proxy escalation
|
|
825
|
+
* owns injection (`pendingReload`). Re-uses the target's existing role
|
|
826
|
+
* bootstrap, so no leader/follower re-election occurs.
|
|
827
|
+
*/
|
|
828
|
+
async reinjectIfEvicted(ws, send, target, state) {
|
|
829
|
+
if (ws.readyState !== WebSocket.OPEN || state.pendingReload)
|
|
830
|
+
return;
|
|
831
|
+
const evicted = await this.probeOverlayEvicted(ws, send);
|
|
832
|
+
if (!evicted || ws.readyState !== WebSocket.OPEN || state.pendingReload)
|
|
833
|
+
return;
|
|
834
|
+
console.log(`[electron-float] Overlay evicted, re-injecting: ${target.url}`);
|
|
835
|
+
send('Runtime.evaluate', {
|
|
836
|
+
expression: this.resolveBootstrapForTarget(target),
|
|
837
|
+
awaitPromise: false,
|
|
838
|
+
});
|
|
839
|
+
}
|
|
742
840
|
/**
|
|
743
841
|
* Handle the initial CDP `ws.on('open', ...)` event for a target: enable
|
|
744
842
|
* Runtime/Page, set CSP bypass, detect theme, inject the overlay, and (on a
|
|
@@ -755,6 +853,16 @@ export class ElectronOverlayInjector {
|
|
|
755
853
|
console.log(`[electron-float] Connected to target, bypassed=${alreadyBypassed}, url=${target.url}`);
|
|
756
854
|
send('Runtime.enable');
|
|
757
855
|
send('Page.enable');
|
|
856
|
+
// Install the role bootstrap as a permanent new-document hook (parity with
|
|
857
|
+
// swift-server) so a full document reload / load-driven navigation of this
|
|
858
|
+
// already-connected target re-injects the overlay automatically. Without
|
|
859
|
+
// this, `syncTargets` only injects into brand-new CDP targets, so a reload
|
|
860
|
+
// of an existing target would lose the overlay permanently (the marker is
|
|
861
|
+
// wiped along with the host element, so the eviction re-check below can't
|
|
862
|
+
// recover it).
|
|
863
|
+
send('Page.addScriptToEvaluateOnNewDocument', {
|
|
864
|
+
source: this.buildNewDocumentBootstrap(target),
|
|
865
|
+
});
|
|
758
866
|
// Set CSP bypass — affects future resource loads on the current page
|
|
759
867
|
send('Page.setBypassCSP', { enabled: true });
|
|
760
868
|
if (alreadyBypassed) {
|
|
@@ -939,8 +1047,24 @@ export class ElectronOverlayInjector {
|
|
|
939
1047
|
pendingCspEscalation: false,
|
|
940
1048
|
fetchProxyActive: false,
|
|
941
1049
|
};
|
|
1050
|
+
// Periodic presence re-check: covers SPAs that re-render their DOM root
|
|
1051
|
+
// (evicting the overlay) without firing a navigation event. Cleared on
|
|
1052
|
+
// close/error so it never outlives the connection.
|
|
1053
|
+
let presenceTimer = null;
|
|
1054
|
+
const clearPresenceTimer = () => {
|
|
1055
|
+
if (presenceTimer) {
|
|
1056
|
+
clearInterval(presenceTimer);
|
|
1057
|
+
presenceTimer = null;
|
|
1058
|
+
}
|
|
1059
|
+
};
|
|
942
1060
|
ws.on('open', () => {
|
|
943
1061
|
this.handleSocketOpen(ws, send, target, state);
|
|
1062
|
+
presenceTimer = setInterval(() => {
|
|
1063
|
+
void this.reinjectIfEvicted(ws, send, target, state).catch((error) => {
|
|
1064
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1065
|
+
console.warn(`[electron-float] Presence-check re-injection failed: ${message}`);
|
|
1066
|
+
});
|
|
1067
|
+
}, this.presenceCheckIntervalMs);
|
|
944
1068
|
});
|
|
945
1069
|
// Handle CDP events: lifecycle events and Fetch interception
|
|
946
1070
|
ws.on('message', (data) => {
|
|
@@ -950,6 +1074,19 @@ export class ElectronOverlayInjector {
|
|
|
950
1074
|
if (msg.method === 'Page.loadEventFired' && state.pendingReload) {
|
|
951
1075
|
this.handlePageLoadAfterReload(ws, send, target, state);
|
|
952
1076
|
}
|
|
1077
|
+
// In-page SPA route change (history.pushState / hashchange) — no new
|
|
1078
|
+
// document is created, so the new-document hook never fires. Re-inject
|
|
1079
|
+
// the role bootstrap if the host element was evicted. `Page.frameNavigated`
|
|
1080
|
+
// is handled only for the main frame (subframe navs never touch the
|
|
1081
|
+
// top-level overlay), and the eviction probe keeps the main-frame full
|
|
1082
|
+
// navigation a no-op (its marker is wiped, so the new-document hook owns it).
|
|
1083
|
+
const isMainFrameNavigated = msg.method === 'Page.frameNavigated' && !msg.params?.frame?.parentId;
|
|
1084
|
+
if (msg.method === 'Page.navigatedWithinDocument' || isMainFrameNavigated) {
|
|
1085
|
+
void this.reinjectIfEvicted(ws, send, target, state).catch((error) => {
|
|
1086
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1087
|
+
console.warn(`[electron-float] Navigation re-injection failed: ${message}`);
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
953
1090
|
if (msg.method === 'Fetch.requestPaused' && state.fetchProxyActive) {
|
|
954
1091
|
this.handleFetchRequestPaused(ws, send, msg);
|
|
955
1092
|
}
|
|
@@ -959,11 +1096,13 @@ export class ElectronOverlayInjector {
|
|
|
959
1096
|
}
|
|
960
1097
|
});
|
|
961
1098
|
ws.on('close', () => {
|
|
1099
|
+
clearPresenceTimer();
|
|
962
1100
|
if (this.connections.get(targetId) === ws) {
|
|
963
1101
|
this.connections.delete(targetId);
|
|
964
1102
|
}
|
|
965
1103
|
});
|
|
966
1104
|
ws.on('error', (error) => {
|
|
1105
|
+
clearPresenceTimer();
|
|
967
1106
|
const message = error instanceof Error ? error.message : String(error);
|
|
968
1107
|
console.error(`[electron-float] Overlay target connection failed for ${target.url}:`, message);
|
|
969
1108
|
if (this.connections.get(targetId) === ws) {
|