sandboxedjs 0.1.28 → 0.1.30
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/LICENSE +1 -7
- package/README.md +104 -7
- package/dist/index.cjs +1014 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +321 -8
- package/dist/index.d.ts +321 -8
- package/dist/index.js +1010 -54
- package/dist/index.js.map +1 -1
- package/dist/service-worker.js +112 -0
- package/dist/service-worker.js.map +1 -0
- package/dist/worker-entry.js +30825 -0
- package/dist/worker-entry.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// src/preview/router.ts
|
|
2
|
+
var CLAIM = /(?:^|\/)__sbx__\/(\d+)(?:\/|$)/;
|
|
3
|
+
var NOT_CONNECTED = 503;
|
|
4
|
+
var PreviewRouter = class {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
options;
|
|
9
|
+
clientPorts = /* @__PURE__ */ new Map();
|
|
10
|
+
pending = /* @__PURE__ */ new Map();
|
|
11
|
+
nextId = 1;
|
|
12
|
+
connected = false;
|
|
13
|
+
/** The page that answers requests has arrived. */
|
|
14
|
+
connect() {
|
|
15
|
+
this.connected = true;
|
|
16
|
+
}
|
|
17
|
+
/** Read a port out of a claim path, or null when this is not one. */
|
|
18
|
+
claimedPort(pathname) {
|
|
19
|
+
const match = CLAIM.exec(pathname);
|
|
20
|
+
return match ? Number(match[1]) : null;
|
|
21
|
+
}
|
|
22
|
+
/** Remember that everything from `clientId` belongs to `port`. */
|
|
23
|
+
bind(clientId, port) {
|
|
24
|
+
if (clientId) this.clientPorts.set(clientId, port);
|
|
25
|
+
}
|
|
26
|
+
/** Which port this client is previewing, or undefined for ordinary traffic. */
|
|
27
|
+
portFor(clientId) {
|
|
28
|
+
return this.clientPorts.get(clientId);
|
|
29
|
+
}
|
|
30
|
+
/** A response has come back from the page. */
|
|
31
|
+
settle(id, response) {
|
|
32
|
+
const resolve = this.pending.get(id);
|
|
33
|
+
if (!resolve) return;
|
|
34
|
+
this.pending.delete(id);
|
|
35
|
+
resolve(response);
|
|
36
|
+
}
|
|
37
|
+
/** Forget a client that has gone away. */
|
|
38
|
+
release(clientId) {
|
|
39
|
+
this.clientPorts.delete(clientId);
|
|
40
|
+
}
|
|
41
|
+
/** Ask the page for one response and rebuild it as a `Response`. */
|
|
42
|
+
async fetch(port, path, request) {
|
|
43
|
+
if (!this.connected) {
|
|
44
|
+
return new Response("The sandbox preview is not connected yet.", { status: NOT_CONNECTED });
|
|
45
|
+
}
|
|
46
|
+
const body = request.method === "GET" || request.method === "HEAD" ? void 0 : await request.arrayBuffer();
|
|
47
|
+
const id = this.nextId++;
|
|
48
|
+
const answered = new Promise((resolve) => this.pending.set(id, resolve));
|
|
49
|
+
this.options.send(
|
|
50
|
+
{ id, port, path, method: request.method, headers: headerRecord(request.headers), ...body ? { body } : {} },
|
|
51
|
+
body ? [body] : []
|
|
52
|
+
);
|
|
53
|
+
const timeoutMs = this.options.timeoutMs ?? 3e4;
|
|
54
|
+
const result = await Promise.race([
|
|
55
|
+
answered,
|
|
56
|
+
new Promise(
|
|
57
|
+
(resolve) => setTimeout(
|
|
58
|
+
() => resolve({ status: 504, statusText: "Gateway Timeout", headers: {}, body: new ArrayBuffer(0) }),
|
|
59
|
+
timeoutMs
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
]);
|
|
63
|
+
this.pending.delete(id);
|
|
64
|
+
const headers = new Headers(result.headers);
|
|
65
|
+
headers.set("Cross-Origin-Resource-Policy", "cross-origin");
|
|
66
|
+
headers.set("Cross-Origin-Embedder-Policy", "require-corp");
|
|
67
|
+
return new Response(result.body, { status: result.status, statusText: result.statusText, headers });
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
function headerRecord(headers) {
|
|
71
|
+
const out = {};
|
|
72
|
+
headers.forEach((value, key) => {
|
|
73
|
+
out[key] = value;
|
|
74
|
+
});
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// src/preview/service-worker.ts
|
|
79
|
+
var worker = globalThis;
|
|
80
|
+
var host = null;
|
|
81
|
+
var router = new PreviewRouter({
|
|
82
|
+
send: (request, transfer) => host?.postMessage(request, transfer)
|
|
83
|
+
});
|
|
84
|
+
worker.addEventListener("install", () => void worker.skipWaiting());
|
|
85
|
+
worker.addEventListener("activate", (event) => event.waitUntil(worker.clients.claim()));
|
|
86
|
+
worker.addEventListener("message", (event) => {
|
|
87
|
+
const data = event.data;
|
|
88
|
+
if (data?.type !== "sandboxedjs:connect") return;
|
|
89
|
+
host = event.ports[0] ?? null;
|
|
90
|
+
if (!host) return;
|
|
91
|
+
host.onmessage = (message) => {
|
|
92
|
+
const reply = message.data;
|
|
93
|
+
router.settle(reply.id, reply.response);
|
|
94
|
+
};
|
|
95
|
+
host.start();
|
|
96
|
+
router.connect();
|
|
97
|
+
});
|
|
98
|
+
worker.addEventListener("fetch", (event) => {
|
|
99
|
+
const url = new URL(event.request.url);
|
|
100
|
+
if (url.origin !== worker.location.origin) return;
|
|
101
|
+
const claimed = router.claimedPort(url.pathname);
|
|
102
|
+
if (claimed !== null && event.request.mode === "navigate") {
|
|
103
|
+
router.bind(event.resultingClientId || event.clientId, claimed);
|
|
104
|
+
event.respondWith(router.fetch(claimed, "/", event.request));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
const port = router.portFor(event.clientId);
|
|
108
|
+
if (port === void 0) return;
|
|
109
|
+
event.respondWith(router.fetch(port, url.pathname + url.search, event.request));
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=service-worker.js.map
|
|
112
|
+
//# sourceMappingURL=service-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/preview/router.ts","../src/preview/service-worker.ts"],"names":[],"mappings":";AA2BA,IAAM,KAAA,GAAQ,gCAAA;AA0Bd,IAAM,aAAA,GAAgB,GAAA;AAEf,IAAM,gBAAN,MAAoB;AAAA,EAMzB,YAA6B,OAAA,EAA+B;AAA/B,IAAA,IAAA,CAAA,OAAA,GAAA,OAAA;AAAA,EAAgC;AAAA,EAAhC,OAAA;AAAA,EALZ,WAAA,uBAAkB,GAAA,EAAoB;AAAA,EACtC,OAAA,uBAAc,GAAA,EAA8C;AAAA,EACrE,MAAA,GAAS,CAAA;AAAA,EACT,SAAA,GAAY,KAAA;AAAA;AAAA,EAKpB,OAAA,GAAgB;AACd,IAAA,IAAA,CAAK,SAAA,GAAY,IAAA;AAAA,EACnB;AAAA;AAAA,EAGA,YAAY,QAAA,EAAiC;AAC3C,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,CAAK,QAAQ,CAAA;AACjC,IAAA,OAAO,KAAA,GAAQ,MAAA,CAAO,KAAA,CAAM,CAAC,CAAC,CAAA,GAAI,IAAA;AAAA,EACpC;AAAA;AAAA,EAGA,IAAA,CAAK,UAAkB,IAAA,EAAoB;AACzC,IAAA,IAAI,QAAA,EAAU,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,UAAU,IAAI,CAAA;AAAA,EACnD;AAAA;AAAA,EAGA,QAAQ,QAAA,EAAsC;AAC5C,IAAA,OAAO,IAAA,CAAK,WAAA,CAAY,GAAA,CAAI,QAAQ,CAAA;AAAA,EACtC;AAAA;AAAA,EAGA,MAAA,CAAO,IAAY,QAAA,EAAiC;AAClD,IAAA,MAAM,OAAA,GAAU,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,EAAE,CAAA;AACnC,IAAA,IAAI,CAAC,OAAA,EAAS;AACd,IAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,EAAE,CAAA;AACtB,IAAA,OAAA,CAAQ,QAAQ,CAAA;AAAA,EAClB;AAAA;AAAA,EAGA,QAAQ,QAAA,EAAwB;AAC9B,IAAA,IAAA,CAAK,WAAA,CAAY,OAAO,QAAQ,CAAA;AAAA,EAClC;AAAA;AAAA,EAGA,MAAM,KAAA,CAAM,IAAA,EAAc,IAAA,EAAc,OAAA,EAAqC;AAC3E,IAAA,IAAI,CAAC,KAAK,SAAA,EAAW;AACnB,MAAA,OAAO,IAAI,QAAA,CAAS,2CAAA,EAA6C,EAAE,MAAA,EAAQ,eAAe,CAAA;AAAA,IAC5F;AACA,IAAA,MAAM,IAAA,GAAO,OAAA,CAAQ,MAAA,KAAW,KAAA,IAAS,OAAA,CAAQ,WAAW,MAAA,GAAS,MAAA,GAAY,MAAM,OAAA,CAAQ,WAAA,EAAY;AAC3G,IAAA,MAAM,KAAK,IAAA,CAAK,MAAA,EAAA;AAChB,IAAA,MAAM,QAAA,GAAW,IAAI,OAAA,CAAyB,CAAC,OAAA,KAAY,KAAK,OAAA,CAAQ,GAAA,CAAI,EAAA,EAAI,OAAO,CAAC,CAAA;AAExF,IAAA,IAAA,CAAK,OAAA,CAAQ,IAAA;AAAA,MACX,EAAE,EAAA,EAAI,IAAA,EAAM,MAAM,MAAA,EAAQ,OAAA,CAAQ,QAAQ,OAAA,EAAS,YAAA,CAAa,OAAA,CAAQ,OAAO,GAAG,GAAI,IAAA,GAAO,EAAE,IAAA,EAAK,GAAI,EAAC,EAAG;AAAA,MAC5G,IAAA,GAAO,CAAC,IAAI,CAAA,GAAI;AAAC,KACnB;AAEA,IAAA,MAAM,SAAA,GAAY,IAAA,CAAK,OAAA,CAAQ,SAAA,IAAa,GAAA;AAC5C,IAAA,MAAM,MAAA,GAAS,MAAM,OAAA,CAAQ,IAAA,CAAK;AAAA,MAChC,QAAA;AAAA,MACA,IAAI,OAAA;AAAA,QAAyB,CAAC,OAAA,KAC5B,UAAA;AAAA,UACE,MAAM,OAAA,CAAQ,EAAE,MAAA,EAAQ,KAAK,UAAA,EAAY,iBAAA,EAAmB,OAAA,EAAS,IAAI,IAAA,EAAM,IAAI,WAAA,CAAY,CAAC,GAAG,CAAA;AAAA,UACnG;AAAA;AACF;AACF,KACD,CAAA;AACD,IAAA,IAAA,CAAK,OAAA,CAAQ,OAAO,EAAE,CAAA;AAEtB,IAAA,MAAM,OAAA,GAAU,IAAI,OAAA,CAAQ,MAAA,CAAO,OAAO,CAAA;AAY1C,IAAA,OAAA,CAAQ,GAAA,CAAI,gCAAgC,cAAc,CAAA;AAC1D,IAAA,OAAA,CAAQ,GAAA,CAAI,gCAAgC,cAAc,CAAA;AAC1D,IAAA,OAAO,IAAI,QAAA,CAAS,MAAA,CAAO,IAAA,EAAM,EAAE,MAAA,EAAQ,MAAA,CAAO,MAAA,EAAQ,UAAA,EAAY,MAAA,CAAO,UAAA,EAAY,OAAA,EAAS,CAAA;AAAA,EACpG;AACF,CAAA;AAGA,SAAS,aAAa,OAAA,EAA0C;AAC9D,EAAA,MAAM,MAA8B,EAAC;AACrC,EAAC,OAAA,CACE,OAAA,CAAQ,CAAC,KAAA,EAAO,GAAA,KAAQ;AAAE,IAAA,GAAA,CAAI,GAAG,CAAA,GAAI,KAAA;AAAA,EAAO,CAAC,CAAA;AAChD,EAAA,OAAO,GAAA;AACT;;;ACvGA,IAAM,MAAA,GAAS,UAAA;AAEf,IAAI,IAAA,GAA2B,IAAA;AAC/B,IAAM,MAAA,GAAS,IAAI,aAAA,CAAc;AAAA,EAC/B,MAAM,CAAC,OAAA,EAAS,aAAa,IAAA,EAAM,WAAA,CAAY,SAAS,QAAQ;AAClE,CAAC,CAAA;AAED,MAAA,CAAO,iBAAiB,SAAA,EAAW,MAAM,KAAK,MAAA,CAAO,aAAa,CAAA;AAClE,MAAA,CAAO,gBAAA,CAAiB,UAAA,EAAY,CAAC,KAAA,KAAU,KAAA,CAAM,UAAU,MAAA,CAAO,OAAA,CAAQ,KAAA,EAAO,CAAC,CAAA;AAEtF,MAAA,CAAO,gBAAA,CAAiB,SAAA,EAAW,CAAC,KAAA,KAAU;AAC5C,EAAA,MAAM,OAAO,KAAA,CAAM,IAAA;AACnB,EAAA,IAAI,IAAA,EAAM,SAAS,qBAAA,EAAuB;AAC1C,EAAA,IAAA,GAAO,KAAA,CAAM,KAAA,CAAM,CAAC,CAAA,IAAK,IAAA;AACzB,EAAA,IAAI,CAAC,IAAA,EAAM;AACX,EAAA,IAAA,CAAK,SAAA,GAAY,CAAC,OAAA,KAAY;AAC5B,IAAA,MAAM,QAAQ,OAAA,CAAQ,IAAA;AACtB,IAAA,MAAA,CAAO,MAAA,CAAO,KAAA,CAAM,EAAA,EAAI,KAAA,CAAM,QAAQ,CAAA;AAAA,EACxC,CAAA;AACA,EAAA,IAAA,CAAK,KAAA,EAAM;AACX,EAAA,MAAA,CAAO,OAAA,EAAQ;AACjB,CAAC,CAAA;AAED,MAAA,CAAO,gBAAA,CAAiB,OAAA,EAAS,CAAC,KAAA,KAAU;AAC1C,EAAA,MAAM,GAAA,GAAM,IAAI,GAAA,CAAI,KAAA,CAAM,QAAQ,GAAG,CAAA;AACrC,EAAA,IAAI,GAAA,CAAI,MAAA,KAAW,MAAA,CAAO,QAAA,CAAS,MAAA,EAAQ;AAE3C,EAAA,MAAM,OAAA,GAAU,MAAA,CAAO,WAAA,CAAY,GAAA,CAAI,QAAQ,CAAA;AAC/C,EAAA,IAAI,OAAA,KAAY,IAAA,IAAQ,KAAA,CAAM,OAAA,CAAQ,SAAS,UAAA,EAAY;AAIzD,IAAA,MAAA,CAAO,IAAA,CAAK,KAAA,CAAM,iBAAA,IAAqB,KAAA,CAAM,UAAU,OAAO,CAAA;AAC9D,IAAA,KAAA,CAAM,YAAY,MAAA,CAAO,KAAA,CAAM,SAAS,GAAA,EAAK,KAAA,CAAM,OAAO,CAAC,CAAA;AAC3D,IAAA;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,MAAA,CAAO,OAAA,CAAQ,KAAA,CAAM,QAAQ,CAAA;AAC1C,EAAA,IAAI,SAAS,MAAA,EAAW;AACxB,EAAA,KAAA,CAAM,WAAA,CAAY,MAAA,CAAO,KAAA,CAAM,IAAA,EAAM,GAAA,CAAI,WAAW,GAAA,CAAI,MAAA,EAAQ,KAAA,CAAM,OAAO,CAAC,CAAA;AAChF,CAAC,CAAA","file":"service-worker.js","sourcesContent":["/**\n * The routing a preview service worker does, separated from the worker itself.\n *\n * A service worker is awkward to test — it needs a browser, a registration and\n * a secure context — but almost none of what makes this correct is about being\n * a service worker. The interesting part is the bookkeeping: which client is\n * previewing which port, which requests belong to a preview at all, and how a\n * response is rebuilt on the way back. That lives here, where it can be\n * exercised directly.\n *\n * The rule this implements, and the reason it exists: requests are routed by\n * **who is asking**, not by what the path looks like. An iframe navigates once\n * to `/__sbx__/<port>/`; every later request from that client — however\n * absolute its path — is recognised by its client id. Nothing has to rewrite\n * `/src/main.js` or `/@vite/client`, which is what makes a real dev server\n * work rather than only a single page.\n */\n\n/**\n * The one-time path an iframe navigates to in order to claim a port.\n *\n * Deliberately not anchored to the start of the path. A service worker's scope\n * is the directory its script was served from, so in a published package the\n * claim arrives under something like\n * `/node_modules/sandboxedjs/dist/__sbx__/5173/` — anchoring here would match\n * only when the worker happened to be served from the site root.\n */\nconst CLAIM = /(?:^|\\/)__sbx__\\/(\\d+)(?:\\/|$)/;\n\nexport interface ProxiedRequest {\n id: number;\n port: number;\n path: string;\n method: string;\n headers: Record<string, string>;\n body?: ArrayBuffer;\n}\n\nexport interface ProxiedResponse {\n status: number;\n statusText: string;\n headers: Record<string, string>;\n body: ArrayBuffer;\n}\n\nexport interface PreviewRouterOptions {\n /** Hand a request to whichever page owns the container. */\n send(request: ProxiedRequest, transfer: ArrayBuffer[]): void;\n /** How long to wait for that page before giving up. */\n timeoutMs?: number;\n}\n\n/** The port to answer with when nothing has connected yet. */\nconst NOT_CONNECTED = 503;\n\nexport class PreviewRouter {\n private readonly clientPorts = new Map<string, number>();\n private readonly pending = new Map<number, (value: ProxiedResponse) => void>();\n private nextId = 1;\n private connected = false;\n\n constructor(private readonly options: PreviewRouterOptions) {}\n\n /** The page that answers requests has arrived. */\n connect(): void {\n this.connected = true;\n }\n\n /** Read a port out of a claim path, or null when this is not one. */\n claimedPort(pathname: string): number | null {\n const match = CLAIM.exec(pathname);\n return match ? Number(match[1]) : null;\n }\n\n /** Remember that everything from `clientId` belongs to `port`. */\n bind(clientId: string, port: number): void {\n if (clientId) this.clientPorts.set(clientId, port);\n }\n\n /** Which port this client is previewing, or undefined for ordinary traffic. */\n portFor(clientId: string): number | undefined {\n return this.clientPorts.get(clientId);\n }\n\n /** A response has come back from the page. */\n settle(id: number, response: ProxiedResponse): void {\n const resolve = this.pending.get(id);\n if (!resolve) return;\n this.pending.delete(id);\n resolve(response);\n }\n\n /** Forget a client that has gone away. */\n release(clientId: string): void {\n this.clientPorts.delete(clientId);\n }\n\n /** Ask the page for one response and rebuild it as a `Response`. */\n async fetch(port: number, path: string, request: Request): Promise<Response> {\n if (!this.connected) {\n return new Response(\"The sandbox preview is not connected yet.\", { status: NOT_CONNECTED });\n }\n const body = request.method === \"GET\" || request.method === \"HEAD\" ? undefined : await request.arrayBuffer();\n const id = this.nextId++;\n const answered = new Promise<ProxiedResponse>((resolve) => this.pending.set(id, resolve));\n\n this.options.send(\n { id, port, path, method: request.method, headers: headerRecord(request.headers), ...(body ? { body } : {}) },\n body ? [body] : [],\n );\n\n const timeoutMs = this.options.timeoutMs ?? 30_000;\n const result = await Promise.race([\n answered,\n new Promise<ProxiedResponse>((resolve) =>\n setTimeout(\n () => resolve({ status: 504, statusText: \"Gateway Timeout\", headers: {}, body: new ArrayBuffer(0) }),\n timeoutMs,\n ),\n ),\n ]);\n this.pending.delete(id);\n\n const headers = new Headers(result.headers);\n /*\n * Two headers, for two different refusals, both of which a cross-origin\n * isolated page makes — and pages that run Rolldown have to be isolated.\n *\n * `Cross-Origin-Resource-Policy` lets the response be loaded as a\n * subresource at all. `Cross-Origin-Embedder-Policy` lets it be *framed*:\n * without it the document is not isolation-compatible, so the iframe is\n * replaced by an error page and the parent sees an opaque, cross-origin\n * frame rather than the site it asked for. Everything served here carries\n * CORP, so the stricter policy is one this preview can satisfy.\n */\n headers.set(\"Cross-Origin-Resource-Policy\", \"cross-origin\");\n headers.set(\"Cross-Origin-Embedder-Policy\", \"require-corp\");\n return new Response(result.body, { status: result.status, statusText: result.statusText, headers });\n }\n}\n\n/** `Headers` is iterable at runtime; the DOM lib used here does not say so. */\nfunction headerRecord(headers: Headers): Record<string, string> {\n const out: Record<string, string> = {};\n (headers as unknown as { forEach(fn: (value: string, key: string) => void): void })\n .forEach((value, key) => { out[key] = value; });\n return out;\n}\n","/**\n * A service worker that gives a container's HTTP servers real URLs.\n *\n * Without one, a page can reach a server inside the container only through\n * `box.request(port, …)`, which hands back a single response. That is enough to\n * read an API or drop some HTML into an element, but not to *run* a site: a\n * dev server emits absolute URLs like `/src/main.js` and `/@vite/client`, and\n * nothing in the page resolves those to the sandbox.\n *\n * This file is deliberately only the glue. The routing lives in\n * {@link PreviewRouter}, where it can be tested without a browser — a service\n * worker otherwise needs a registration, a secure context and a real page\n * before any of its behaviour can be observed at all.\n */\n\nimport { PreviewRouter, type ProxiedResponse } from \"./router.js\";\n\n/*\n * Typed locally rather than through `lib.webworker`: this package compiles with\n * the DOM lib, and the two declare a conflicting `self`. Only the handful of\n * members actually used are described here.\n */\ninterface FetchLikeEvent extends Event {\n request: Request;\n clientId: string;\n resultingClientId: string;\n respondWith(response: Promise<Response> | Response): void;\n}\ninterface ExtendableLikeEvent extends Event {\n waitUntil(promise: Promise<unknown>): void;\n}\ninterface MessageLikeEvent extends Event {\n data: unknown;\n ports: readonly MessagePort[];\n}\ninterface WorkerScope {\n location: { origin: string };\n clients: { claim(): Promise<void> };\n skipWaiting(): Promise<void>;\n addEventListener(type: \"install\" | \"activate\", listener: (event: ExtendableLikeEvent) => void): void;\n addEventListener(type: \"fetch\", listener: (event: FetchLikeEvent) => void): void;\n addEventListener(type: \"message\", listener: (event: MessageLikeEvent) => void): void;\n}\n\nconst worker = globalThis as unknown as WorkerScope;\n\nlet host: MessagePort | null = null;\nconst router = new PreviewRouter({\n send: (request, transfer) => host?.postMessage(request, transfer),\n});\n\nworker.addEventListener(\"install\", () => void worker.skipWaiting());\nworker.addEventListener(\"activate\", (event) => event.waitUntil(worker.clients.claim()));\n\nworker.addEventListener(\"message\", (event) => {\n const data = event.data as { type?: string } | null;\n if (data?.type !== \"sandboxedjs:connect\") return;\n host = event.ports[0] ?? null;\n if (!host) return;\n host.onmessage = (message) => {\n const reply = message.data as { id: number; response: ProxiedResponse };\n router.settle(reply.id, reply.response);\n };\n host.start();\n router.connect();\n});\n\nworker.addEventListener(\"fetch\", (event) => {\n const url = new URL(event.request.url);\n if (url.origin !== worker.location.origin) return;\n\n const claimed = router.claimedPort(url.pathname);\n if (claimed !== null && event.request.mode === \"navigate\") {\n /* A navigation's client does not exist yet, so it is named by\n * `resultingClientId`. Binding it here is what lets every later request\n * from that document be recognised without touching its URLs. */\n router.bind(event.resultingClientId || event.clientId, claimed);\n event.respondWith(router.fetch(claimed, \"/\", event.request));\n return;\n }\n\n const port = router.portFor(event.clientId);\n if (port === undefined) return; // not a preview: the host page's own traffic\n event.respondWith(router.fetch(port, url.pathname + url.search, event.request));\n});\n"]}
|