@volter/browser-cdp 0.1.48 → 0.1.50

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.
@@ -0,0 +1,24 @@
1
+ import type { ProtocolPeer } from "@volter/almostcdp/core";
2
+ import type { BrowserProgramRunner } from "@volter/browser-runtime/browser-process-host.js";
3
+ import type { BrowserFileSystem } from "@volter/browser-runtime/browser-filesystem.js";
4
+ /** The program names a placed executable resolves to (a path resolves to its basename). */
5
+ export declare const CHROMIUM_FACE_PROGRAMS: readonly ["chrome", "chrome-headless-shell"];
6
+ /**
7
+ * Chromium as Playwright launches it: `--remote-debugging-pipe`, CDP as
8
+ * NUL-terminated JSON read from fd 3 and written to fd 4. The face owns no
9
+ * protocol behaviour of its own; each launch is one browser-level client of
10
+ * the tab's CDP endpoint, and `Browser.close` ends the process as it ends
11
+ * Chromium's.
12
+ */
13
+ export declare function chromiumFaceRunner(attachClient: (peer: ProtocolPeer) => void): BrowserProgramRunner;
14
+ /** The Chromium revisions pinned by each `playwright-core` installed under the given roots. */
15
+ export declare function playwrightChromiumRevisions(filesystem: BrowserFileSystem, roots: readonly string[]): Promise<string[]>;
16
+ /**
17
+ * Places the face where Playwright's registry looks for each revision, marked
18
+ * installed and host-validated as the registry marks what it downloads, so
19
+ * `playwright install` finds it present and a launch resolves the path to the
20
+ * registered program: the file is the engine's registered-program stub, which
21
+ * it relays to the host rather than running.
22
+ */
23
+ export declare function placeChromiumFace(filesystem: BrowserFileSystem, cacheDirectory: string, revisions: readonly string[]): Promise<void>;
24
+ //# sourceMappingURL=chromium-face.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chromium-face.d.ts","sourceRoot":"","sources":["../src/chromium-face.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iDAAiD,CAAC;AAC5F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAQvF,2FAA2F;AAC3F,eAAO,MAAM,sBAAsB,8CAA+C,CAAC;AAInF;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,YAAY,KAAK,IAAI,GAAG,oBAAoB,CAqDnG;AAUD,+FAA+F;AAC/F,wBAAsB,2BAA2B,CAAC,UAAU,EAAE,iBAAiB,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAa5H;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAqB1I"}
@@ -0,0 +1,144 @@
1
+ /** Where Playwright's registry keeps each Chromium it launches, under its cache directory. */
2
+ const PLACEMENTS = [
3
+ { browser: "chromium", directory: "chromium", executable: "chrome-linux64/chrome" },
4
+ { browser: "chromium-headless-shell", directory: "chromium_headless_shell", executable: "chrome-headless-shell-linux64/chrome-headless-shell" },
5
+ ];
6
+ /** The program names a placed executable resolves to (a path resolves to its basename). */
7
+ export const CHROMIUM_FACE_PROGRAMS = ["chrome", "chrome-headless-shell"];
8
+ const encoder = new TextEncoder();
9
+ /**
10
+ * Chromium as Playwright launches it: `--remote-debugging-pipe`, CDP as
11
+ * NUL-terminated JSON read from fd 3 and written to fd 4. The face owns no
12
+ * protocol behaviour of its own; each launch is one browser-level client of
13
+ * the tab's CDP endpoint, and `Browser.close` ends the process as it ends
14
+ * Chromium's.
15
+ */
16
+ export function chromiumFaceRunner(attachClient) {
17
+ return async (argv, options) => {
18
+ const name = argv[0]?.slice(argv[0].lastIndexOf("/") + 1) ?? "chrome";
19
+ if (!argv.includes("--remote-debugging-pipe")) {
20
+ return { stdout: "", stderr: `${name}: the tab's Chromium is driven over --remote-debugging-pipe only\n`, exitCode: 1 };
21
+ }
22
+ const commands = options.descriptors?.[3];
23
+ const replies = options.descriptors?.[4];
24
+ if (!commands || !replies) {
25
+ return { stdout: "", stderr: `${name}: --remote-debugging-pipe needs pipes on fds 3 and 4\n`, exitCode: 1 };
26
+ }
27
+ const messageListeners = [];
28
+ const closeListeners = [];
29
+ let open = true;
30
+ let finished;
31
+ const done = new Promise((resolve) => { finished = resolve; });
32
+ const end = () => {
33
+ if (!open)
34
+ return;
35
+ open = false;
36
+ replies.end();
37
+ for (const listener of closeListeners)
38
+ listener();
39
+ finished();
40
+ };
41
+ const peer = {
42
+ get readyState() { return open ? 1 : 3; },
43
+ send: (data) => { if (open)
44
+ replies.write(encoder.encode(`${data}\0`)); },
45
+ close: () => end(),
46
+ on: ((event, listener) => {
47
+ if (event === "message")
48
+ messageListeners.push(listener);
49
+ else
50
+ closeListeners.push(listener);
51
+ }),
52
+ };
53
+ options.signal?.addEventListener("abort", end, { once: true });
54
+ attachClient(peer);
55
+ void (async () => {
56
+ const decoder = new TextDecoder();
57
+ let buffered = "";
58
+ for await (const chunk of commands.input) {
59
+ buffered += decoder.decode(chunk, { stream: true });
60
+ for (let boundary = buffered.indexOf("\0"); boundary >= 0; boundary = buffered.indexOf("\0")) {
61
+ const message = buffered.slice(0, boundary);
62
+ buffered = buffered.slice(boundary + 1);
63
+ const closing = browserClose(message);
64
+ if (closing !== undefined) {
65
+ peer.send(JSON.stringify({ id: closing, result: {} }));
66
+ end();
67
+ return;
68
+ }
69
+ for (const listener of messageListeners)
70
+ listener(message);
71
+ if (!open)
72
+ return;
73
+ }
74
+ }
75
+ end();
76
+ })().catch(end);
77
+ await done;
78
+ return { stdout: "", stderr: "", exitCode: 0 };
79
+ };
80
+ }
81
+ /** The id of a browser-level `Browser.close`, which ends the process rather than reaching the endpoint. */
82
+ function browserClose(message) {
83
+ try {
84
+ const parsed = JSON.parse(message);
85
+ return parsed.method === "Browser.close" && parsed.sessionId === undefined && typeof parsed.id === "number" ? parsed.id : undefined;
86
+ }
87
+ catch {
88
+ return undefined;
89
+ }
90
+ }
91
+ /** The Chromium revisions pinned by each `playwright-core` installed under the given roots. */
92
+ export async function playwrightChromiumRevisions(filesystem, roots) {
93
+ const revisions = new Set();
94
+ for (const root of roots) {
95
+ const path = `${root.replace(/\/$/u, "")}/node_modules/playwright-core/browsers.json`;
96
+ if (!filesystem.existsSync(path))
97
+ continue;
98
+ try {
99
+ const pinned = JSON.parse(await filesystem.readFile(path, "utf8"));
100
+ for (const entry of pinned.browsers ?? []) {
101
+ if ((entry.name === "chromium" || entry.name === "chromium-headless-shell") && typeof entry.revision === "string")
102
+ revisions.add(entry.revision);
103
+ }
104
+ }
105
+ catch { /* A browsers.json that does not parse pins nothing. */ }
106
+ }
107
+ return [...revisions];
108
+ }
109
+ /**
110
+ * Places the face where Playwright's registry looks for each revision, marked
111
+ * installed and host-validated as the registry marks what it downloads, so
112
+ * `playwright install` finds it present and a launch resolves the path to the
113
+ * registered program: the file is the engine's registered-program stub, which
114
+ * it relays to the host rather than running.
115
+ */
116
+ export async function placeChromiumFace(filesystem, cacheDirectory, revisions) {
117
+ for (const revision of revisions) {
118
+ for (const placement of PLACEMENTS) {
119
+ const root = `${cacheDirectory}/${placement.directory}-${revision}`;
120
+ const executable = `${root}/${placement.executable}`;
121
+ filesystem.mkdirSync(executable.slice(0, executable.lastIndexOf("/")), { recursive: true });
122
+ // The registered-program stub the engine relays to the host, which maps
123
+ // the pathname to the face by its basename.
124
+ const name = placement.executable.slice(placement.executable.lastIndexOf("/") + 1);
125
+ const stub = `#!/bin/sh\n# ${name}: runtime-registered program\n`;
126
+ let current;
127
+ try {
128
+ current = filesystem.existsSync(executable) ? await filesystem.readFile(executable, "utf8") : undefined;
129
+ }
130
+ catch {
131
+ current = undefined;
132
+ }
133
+ if (current !== stub) {
134
+ filesystem.writeFileSync(executable, stub);
135
+ filesystem.chmodSync?.(executable, 0o755);
136
+ }
137
+ for (const marker of ["INSTALLATION_COMPLETE", "DEPENDENCIES_VALIDATED"]) {
138
+ if (!filesystem.existsSync(`${root}/${marker}`))
139
+ filesystem.writeFileSync(`${root}/${marker}`, "");
140
+ }
141
+ }
142
+ }
143
+ }
144
+ //# sourceMappingURL=chromium-face.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chromium-face.js","sourceRoot":"","sources":["../src/chromium-face.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { DispatcherOptions, ProtocolPeer } from "@volter/almostcdp/core";
2
+ import { type BrowserSandboxBridgeOptions } from "@volter/browser-runtime/browser-sandbox-bridge.js";
3
+ type BrowserContextHost = NonNullable<DispatcherOptions["contextHost"]>;
4
+ type BrowserPageHost = NonNullable<DispatcherOptions["pageHost"]>;
5
+ type SharedBridgeOptions = Omit<BrowserSandboxBridgeOptions, "preview" | "origin" | "nonce" | "port" | "onReady" | "onPreviewLoad">;
6
+ export interface PreviewBrowserHostOptions {
7
+ /** Where page frames are mounted; the embedding decides how they are shown. */
8
+ mount: HTMLElement;
9
+ /** The deployment's sandbox origin: the default context's pages live on it. */
10
+ sandboxOrigin: string;
11
+ /** The sandbox bootstrap document every page frame loads. */
12
+ sandboxPath?: string;
13
+ /**
14
+ * The origin a context's label is served at, where the deployment's sandbox
15
+ * has its own zone of hosts. By default the label is a subdomain of the
16
+ * sandbox host, which a loopback name (`c-1.localhost`) or a zone apex serves.
17
+ */
18
+ contextOrigin?(label: string): string;
19
+ bridge: SharedBridgeOptions;
20
+ /** Registers a created page's surface with the endpoint; resolves once the endpoint lists it. */
21
+ attachSurface(peer: ProtocolPeer, id: string, signal: AbortSignal): Promise<void>;
22
+ }
23
+ export interface PreviewBrowserHost {
24
+ readonly contextHost: BrowserContextHost;
25
+ readonly pageHost: BrowserPageHost;
26
+ dispose(): Promise<void>;
27
+ }
28
+ /**
29
+ * Browser contexts and pages for the tab's CDP endpoint. A context is its own
30
+ * origin, a sibling of the sandbox origin, so its storage and cookies are the
31
+ * browser's own partition; its pages are sandbox frames on that origin, each
32
+ * lent to the endpoint like the preview. A closed context's origin is emptied
33
+ * and given to the next context, keeping its service worker: a fresh origin
34
+ * per context registered a worker per context, and a browser with a debugger
35
+ * attached keeps every one of them running (a suite run reached about 985
36
+ * renderers in ten minutes). Every context shares the tab's container: the
37
+ * servers a page reaches are the project's, as they are for a real browser's
38
+ * contexts.
39
+ */
40
+ export declare function createPreviewBrowserHost(options: PreviewBrowserHostOptions): PreviewBrowserHost;
41
+ export {};
42
+ //# sourceMappingURL=preview-browser-host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-browser-host.d.ts","sourceRoot":"","sources":["../src/preview-browser-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAC9E,OAAO,EAAwB,KAAK,2BAA2B,EAAE,MAAM,mDAAmD,CAAC;AAI3H,KAAK,kBAAkB,GAAG,WAAW,CAAC,iBAAiB,CAAC,aAAa,CAAC,CAAC,CAAC;AACxE,KAAK,eAAe,GAAG,WAAW,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC;AAClE,KAAK,mBAAmB,GAAG,IAAI,CAAC,2BAA2B,EAAE,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,eAAe,CAAC,CAAC;AAEpI,MAAM,WAAW,yBAAyB;IACxC,+EAA+E;IAC/E,KAAK,EAAE,WAAW,CAAC;IACnB,+EAA+E;IAC/E,aAAa,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,aAAa,CAAC,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IACtC,MAAM,EAAE,mBAAmB,CAAC;IAC5B,iGAAiG;IACjG,aAAa,CAAC,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnF;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,WAAW,EAAE,kBAAkB,CAAC;IACzC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1B;AAgBD;;;;;;;;;;;GAWG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,kBAAkB,CAoM/F"}
@@ -0,0 +1,270 @@
1
+ import { BrowserSandboxBridge } from "@volter/browser-runtime/browser-sandbox-bridge.js";
2
+ import { SANDBOX_CHANNEL } from "@volter/browser-runtime/sandbox-protocol.js";
3
+ import { MessagePortTransport } from "@volter/almostcdp/message-port";
4
+ const CLEAR_TIMEOUT_MS = 15_000;
5
+ /** The page's record of the context origins it made, in its own storage. */
6
+ const ORIGINS_RECORD = "browser-substrate.context-origins";
7
+ /** A context origin in use is held under this lock by the tab using it. */
8
+ const ORIGIN_LOCK = "browser-substrate.context-origin:";
9
+ /** Emptied origins kept for the next contexts; past this, one is emptied for good. */
10
+ const SPARE_ORIGINS = 4;
11
+ /**
12
+ * Browser contexts and pages for the tab's CDP endpoint. A context is its own
13
+ * origin, a sibling of the sandbox origin, so its storage and cookies are the
14
+ * browser's own partition; its pages are sandbox frames on that origin, each
15
+ * lent to the endpoint like the preview. A closed context's origin is emptied
16
+ * and given to the next context, keeping its service worker: a fresh origin
17
+ * per context registered a worker per context, and a browser with a debugger
18
+ * attached keeps every one of them running (a suite run reached about 985
19
+ * renderers in ten minutes). Every context shares the tab's container: the
20
+ * servers a page reaches are the project's, as they are for a real browser's
21
+ * contexts.
22
+ */
23
+ export function createPreviewBrowserHost(options) {
24
+ const sandboxPath = options.sandboxPath ?? "/sandbox.html";
25
+ const contexts = new Map();
26
+ /** Closed contexts' origins, each usable once its emptying has succeeded. */
27
+ const emptied = [];
28
+ const held = new Map();
29
+ // A tab that ends before its contexts are closed leaves their origins, each
30
+ // with its worker registered and its storage, and no page knew them again
31
+ // (a morning's work left eight). The page records every origin it makes and
32
+ // holds a lock on each while it lives: a recorded origin whose lock is free
33
+ // belongs to no tab, and is taken for the next context or emptied for good.
34
+ const recorded = () => {
35
+ try {
36
+ const value = JSON.parse(localStorage.getItem(ORIGINS_RECORD) ?? "[]");
37
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
38
+ }
39
+ catch {
40
+ return [];
41
+ }
42
+ };
43
+ const record = (origins) => {
44
+ try {
45
+ localStorage.setItem(ORIGINS_RECORD, JSON.stringify([...new Set(origins)]));
46
+ }
47
+ catch { /* storage refused: the origin is still usable */ }
48
+ };
49
+ /** Takes an origin's lock, or with `ifFree` only if no tab holds it; resolves whether it is held. */
50
+ const hold = (origin, ifFree) => new Promise((acquired) => {
51
+ if (!("locks" in navigator)) {
52
+ acquired(!ifFree);
53
+ return;
54
+ }
55
+ void navigator.locks.request(`${ORIGIN_LOCK}${origin}`, { ifAvailable: ifFree }, (lock) => {
56
+ if (!lock) {
57
+ acquired(false);
58
+ return undefined;
59
+ }
60
+ acquired(true);
61
+ return new Promise((release) => held.set(origin, release));
62
+ });
63
+ });
64
+ /** Empties an origin for good: its worker too. It leaves the record once that succeeded. */
65
+ const retire = (origin) => clearOrigin(origin).then(() => { record(recorded().filter((item) => item !== origin)); }, () => undefined).finally(() => { held.get(origin)?.(); held.delete(origin); });
66
+ /** A closed context's origin, emptied for the next context, or for good past the spares. */
67
+ const release = (origin) => {
68
+ if (emptied.length >= SPARE_ORIGINS) {
69
+ void retire(origin);
70
+ return;
71
+ }
72
+ emptied.push({ origin, ready: clearOrigin(origin, "reuse").then(() => true, () => false) });
73
+ };
74
+ let adopting = Promise.resolve();
75
+ for (const origin of recorded()) {
76
+ // One at a time: each is a frame of its origin loaded to empty it.
77
+ adopting = adopting.then(async () => { if (await hold(origin, true))
78
+ release(origin); });
79
+ }
80
+ const pages = new Map();
81
+ const contextOrigin = () => {
82
+ const label = `c-${[...crypto.getRandomValues(new Uint8Array(8))].map((byte) => byte.toString(16).padStart(2, "0")).join("")}`;
83
+ if (options.contextOrigin)
84
+ return new URL(options.contextOrigin(label)).origin;
85
+ const base = new URL(options.sandboxOrigin);
86
+ if (/^[0-9.]+$/u.test(base.hostname) || base.hostname.startsWith("[")) {
87
+ throw new Error(`Isolated browser contexts need a sandbox host name; ${base.hostname} is an address.`);
88
+ }
89
+ // workers.dev serves one level of names: a context host under the
90
+ // sandbox's own name does not exist there.
91
+ if (base.hostname.endsWith(".workers.dev")) {
92
+ throw new Error(`Isolated browser contexts need the sandbox on a zone of its own; ${base.hostname} is a workers.dev host.`);
93
+ }
94
+ return `${base.protocol}//${label}.${base.host}`;
95
+ };
96
+ const frameFor = (origin, query) => {
97
+ const frame = document.createElement("iframe");
98
+ frame.style.cssText = "border:0;width:1280px;height:720px;display:block";
99
+ const url = new URL(sandboxPath, origin);
100
+ for (const [key, value] of Object.entries(query))
101
+ url.searchParams.set(key, value);
102
+ frame.src = url.href;
103
+ options.mount.append(frame);
104
+ return frame;
105
+ };
106
+ const closePage = async (id) => {
107
+ const page = pages.get(id);
108
+ if (!page)
109
+ return;
110
+ pages.delete(id);
111
+ page.bridge.dispose();
112
+ page.frame.remove();
113
+ };
114
+ /** Empties an origin's storage from a frame of that origin, then removes the frame. */
115
+ const clearOrigin = async (origin, clear = "1") => {
116
+ const nonce = crypto.randomUUID();
117
+ const frame = frameFor(origin, { parentOrigin: location.origin, nonce, clear });
118
+ try {
119
+ await new Promise((resolve, reject) => {
120
+ const timer = setTimeout(() => { cleanup(); reject(new Error(`Clearing ${origin} timed out.`)); }, CLEAR_TIMEOUT_MS);
121
+ const receive = (event) => {
122
+ if (event.origin !== origin || event.source !== frame.contentWindow)
123
+ return;
124
+ const data = event.data;
125
+ if (data?.channel !== SANDBOX_CHANNEL || data.nonce !== nonce)
126
+ return;
127
+ if (data.type === "cleared") {
128
+ cleanup();
129
+ resolve();
130
+ }
131
+ else if (data.type === "error") {
132
+ cleanup();
133
+ reject(new Error(data.payload?.message ?? `Clearing ${origin} failed.`));
134
+ }
135
+ };
136
+ const cleanup = () => { clearTimeout(timer); window.removeEventListener("message", receive); };
137
+ window.addEventListener("message", receive);
138
+ });
139
+ }
140
+ finally {
141
+ frame.remove();
142
+ }
143
+ };
144
+ const contextHost = {
145
+ async createContext(context, signal) {
146
+ if (context.proxyServer !== undefined || context.proxyBypassList !== undefined) {
147
+ throw new Error("A context's network is the tab's; a per-context proxy is unavailable.");
148
+ }
149
+ if (context.originsWithUniversalNetworkAccess?.length) {
150
+ throw new Error("Universal network access for named origins is unavailable in the tab.");
151
+ }
152
+ signal.throwIfAborted();
153
+ let origin;
154
+ while (!origin && emptied.length > 0) {
155
+ const candidate = emptied.shift();
156
+ if (await candidate.ready)
157
+ origin = candidate.origin;
158
+ // Not emptied: it stays recorded, and a later page tries again.
159
+ else {
160
+ held.get(candidate.origin)?.();
161
+ held.delete(candidate.origin);
162
+ }
163
+ signal.throwIfAborted();
164
+ }
165
+ if (!origin) {
166
+ origin = contextOrigin();
167
+ await hold(origin, false);
168
+ record([...recorded(), origin]);
169
+ }
170
+ contexts.set(context.id, { origin });
171
+ },
172
+ async disposeContext(id) {
173
+ const context = contexts.get(id);
174
+ if (!context)
175
+ return;
176
+ await Promise.all([...pages].filter(([, page]) => page.contextId === id).map(([pageId]) => closePage(pageId)));
177
+ contexts.delete(id);
178
+ // The context is closed once its pages are, so emptying its storage
179
+ // (a frame of the origin loaded to do it) goes on after the answer, as
180
+ // Chromium's closing does not wait on disk: awaited, it held
181
+ // Playwright's context teardown past a test's timeout. The next context
182
+ // waits for it instead; an origin that could not be emptied is never
183
+ // given out again.
184
+ release(context.origin);
185
+ },
186
+ };
187
+ const pageHost = {
188
+ async createPage(page, signal) {
189
+ const context = page.browserContextId === undefined ? undefined : contexts.get(page.browserContextId);
190
+ if (page.browserContextId !== undefined && !context)
191
+ throw new Error("No browser context with given id");
192
+ const origin = context?.origin ?? new URL(options.sandboxOrigin).origin;
193
+ const nonce = crypto.randomUUID();
194
+ // A page opens blank, is attached, and is then taken to the guest
195
+ // server it names under that port's prefix, the way a client's own
196
+ // first navigation takes it there, as Chromium's page starts blank and
197
+ // then loads its URL. Opened on the guest's port and navigated before
198
+ // attaching, a page never attached (a window.open's popup,
199
+ // Target.createTarget with a URL): creation hung.
200
+ const opening = guestAddress(page.url);
201
+ const port = 0;
202
+ const frame = frameFor(origin, { parentOrigin: location.origin, nonce, port: String(port) });
203
+ const bridge = new BrowserSandboxBridge({ ...options.bridge, preview: frame, origin, nonce, port });
204
+ pages.set(page.id, { frame, bridge, ...(page.browserContextId !== undefined ? { contextId: page.browserContextId } : {}) });
205
+ const abort = () => { void closePage(page.id); };
206
+ signal.addEventListener("abort", abort, { once: true });
207
+ try {
208
+ // The surface registers under the target id before creation resolves.
209
+ const channel = await bridge.connectProtocol(page.id);
210
+ const transport = new MessagePortTransport(channel.port);
211
+ void channel.closed.then((reason) => transport.close(1001, reason));
212
+ // A page is handed over once its first document has loaded, as
213
+ // Chromium's is committed before `Target.createTarget` answers: a
214
+ // client's first navigation otherwise interrupts the frame's own.
215
+ await bridge.initialPreviewReady;
216
+ signal.throwIfAborted();
217
+ await options.attachSurface(transport, page.id, signal);
218
+ // Attached first, then taken to its URL: a navigation while the
219
+ // surface attached kept it from ever attaching.
220
+ if (opening)
221
+ await bridge.navigate(`${origin}/__virtual__/${opening.port}${opening.path}`);
222
+ }
223
+ catch (cause) {
224
+ await closePage(page.id);
225
+ throw cause;
226
+ }
227
+ finally {
228
+ signal.removeEventListener("abort", abort);
229
+ }
230
+ },
231
+ closePage,
232
+ };
233
+ return {
234
+ contextHost,
235
+ pageHost,
236
+ async dispose() {
237
+ await Promise.allSettled([...pages.keys()].map(closePage));
238
+ const origins = [...contexts.values()].map((context) => context.origin);
239
+ contexts.clear();
240
+ await adopting;
241
+ for (const entry of emptied.splice(0))
242
+ if (await entry.ready)
243
+ origins.push(entry.origin);
244
+ await Promise.allSettled(origins.map(retire));
245
+ },
246
+ };
247
+ }
248
+ /**
249
+ * The guest server a page is opened on: `about:blank` opens the default port,
250
+ * a loopback URL opens its own port and path. Anything else is not served in
251
+ * the tab.
252
+ */
253
+ function guestAddress(url) {
254
+ if (url === "" || url === "about:blank")
255
+ return undefined;
256
+ let parsed;
257
+ try {
258
+ parsed = new URL(url);
259
+ }
260
+ catch {
261
+ throw new Error(`Invalid page URL: ${url}`);
262
+ }
263
+ const loopback = parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1" || parsed.hostname === "[::1]";
264
+ if ((parsed.protocol !== "http:" && parsed.protocol !== "https:") || !loopback) {
265
+ throw new Error(`A page opens on a guest server of the tab; ${url} is not one.`);
266
+ }
267
+ const port = Number(parsed.port || (parsed.protocol === "https:" ? 443 : 80));
268
+ return { port, path: `${parsed.pathname}${parsed.search}${parsed.hash}` };
269
+ }
270
+ //# sourceMappingURL=preview-browser-host.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-browser-host.js","sourceRoot":"","sources":["../src/preview-browser-host.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import { type ByteSocket } from "@volter/almostcdp/socket";
2
+ import type { ProtocolPeer } from "@volter/almostcdp/core";
3
+ import type { BrowserSandboxBridge } from "@volter/browser-runtime/browser-sandbox-bridge.js";
4
+ import type { BrowserServerBridge } from "@volter/browser-runtime/browser-http.js";
5
+ import type { BrowserVirtualWebSocketRegistry } from "@volter/browser-runtime/browser-virtual-websocket.js";
6
+ import { type PreviewBrowserHostOptions } from "./preview-browser-host.js";
7
+ /** A loopback listener of the tab: WALI's `listenTcp`, a Node container's `listenNet`. */
8
+ export type PreviewCdpListener = (port: number, accept: (socket: ByteSocket) => void) => () => void;
9
+ export interface PreviewCdpPublication {
10
+ readonly port: number;
11
+ /** What a client inside the tab passes to `connectOverCDP`. */
12
+ readonly url: string;
13
+ /** A browser-level client over a transport the host decodes, such as a launch pipe. */
14
+ attachClient(peer: ProtocolPeer): void;
15
+ close(): Promise<void>;
16
+ }
17
+ /**
18
+ * The preview as a Chrome DevTools Protocol endpoint on a loopback port of the
19
+ * tab. AlmostCDP serves discovery and the DevTools WebSockets on each accepted
20
+ * connection; the preview's surface is lent through the authenticated sandbox
21
+ * bridge and lent again whenever the sandbox document is replaced, which a
22
+ * client sees as its page target closing and a new one appearing.
23
+ */
24
+ export declare function publishPreviewCdp(options: {
25
+ /**
26
+ * The preview's bridge, which lends the preview as a page target. Without
27
+ * one the endpoint still serves contexts and pages a client creates, which
28
+ * is all a launched browser needs.
29
+ */
30
+ bridge?: BrowserSandboxBridge;
31
+ port: number;
32
+ listen: PreviewCdpListener;
33
+ targetId?: string;
34
+ /**
35
+ * Browser contexts and pages beyond the preview: `Target.createBrowserContext`
36
+ * and `Target.createTarget` open sandbox frames here. Without it they refuse.
37
+ */
38
+ browser?: Omit<PreviewBrowserHostOptions, "attachSurface">;
39
+ /**
40
+ * The same endpoint as a server in the tab's port table, so a page of the
41
+ * tab reaches it and a live tunnel of that port carries it off the machine.
42
+ */
43
+ portTable?: {
44
+ port: number;
45
+ ports: Pick<BrowserServerBridge, "registerServer" | "unregisterServer">;
46
+ webSockets: BrowserVirtualWebSocketRegistry;
47
+ };
48
+ /**
49
+ * The endpoint's discovery documents alone (`/json/version`, `/json/list`)
50
+ * in the tab's port table, where a program's `fetch` to a loopback port
51
+ * goes: Puppeteer reads `browserURL` so. The WebSocket they name stays on
52
+ * the listener, which the tab's programs reach and a page of the tab does
53
+ * not, so a page learns the address and cannot drive the browser.
54
+ */
55
+ discoveryTable?: {
56
+ port: number;
57
+ ports: Pick<BrowserServerBridge, "registerServer" | "unregisterServer">;
58
+ };
59
+ /**
60
+ * Where a download a page makes is written: the path Playwright names in
61
+ * its client's filesystem, which is the container's. Without it the
62
+ * endpoint refuses download policy by name.
63
+ */
64
+ downloads?: {
65
+ write(path: string, bytes: Uint8Array): Promise<void>;
66
+ };
67
+ /**
68
+ * Reads a file a client names by path for a page's file input
69
+ * (DOM.setFileInputFiles): the client's filesystem, the container's. The
70
+ * page is given those files' bytes alone. Without it the endpoint refuses
71
+ * file paths by name.
72
+ */
73
+ files?: {
74
+ read(path: string): Promise<{
75
+ data: Uint8Array;
76
+ name?: string;
77
+ type?: string;
78
+ lastModified?: number;
79
+ }>;
80
+ /** A directory's files by their paths relative to it (a folder upload); undefined for a file. */
81
+ list?(path: string): Promise<string[] | undefined>;
82
+ };
83
+ }): PreviewCdpPublication;
84
+ //# sourceMappingURL=preview-cdp-endpoint.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preview-cdp-endpoint.d.ts","sourceRoot":"","sources":["../src/preview-cdp-endpoint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAwB,KAAK,UAAU,EAAuB,MAAM,0BAA0B,CAAC;AACtG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAE3D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mDAAmD,CAAC;AAC9F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAC;AACnF,OAAO,KAAK,EAAE,+BAA+B,EAA4B,MAAM,sDAAsD,CAAC;AACtI,OAAO,EAA4B,KAAK,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AAErG,0FAA0F;AAC1F,MAAM,MAAM,kBAAkB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,IAAI,KAAK,MAAM,IAAI,CAAC;AAEpG,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,+DAA+D;IAC/D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,YAAY,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAAC;IACvC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAID;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC;;;;OAIG;IACH,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,kBAAkB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,OAAO,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,eAAe,CAAC,CAAC;IAC3D;;;OAGG;IACH,SAAS,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,gBAAgB,GAAG,kBAAkB,CAAC,CAAC;QAAC,UAAU,EAAE,+BAA+B,CAAA;KAAE,CAAC;IACnJ;;;;;;OAMG;IACH,cAAc,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,IAAI,CAAC,mBAAmB,EAAE,gBAAgB,GAAG,kBAAkB,CAAC,CAAA;KAAE,CAAC;IAC3G;;;;OAIG;IACH,SAAS,CAAC,EAAE;QAAE,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,CAAC;IACtE;;;;;OAKG;IACH,KAAK,CAAC,EAAE;QACN,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;YAAE,IAAI,EAAE,UAAU,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,YAAY,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACvG,iGAAiG;QACjG,IAAI,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;KACpD,CAAC;CACH,GAAG,qBAAqB,CAmDxB"}