@orkestrel/browser 0.0.2 → 0.0.5
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/src/core/index.cjs +422 -162
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +11 -2
- package/dist/src/core/index.d.ts +11 -2
- package/dist/src/core/index.js +422 -162
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/server/index.cjs +588 -411
- package/dist/src/server/index.cjs.map +1 -1
- package/dist/src/server/index.d.cts +52 -53
- package/dist/src/server/index.d.ts +52 -53
- package/dist/src/server/index.js +586 -410
- package/dist/src/server/index.js.map +1 -1
- package/package.json +11 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["#transport","#timeout","#connected","#connecting","#closeRequested","#wired","#onMessage","#onClose","#onError","#nextId","#pending","#sessionSubscriptions","#subscriptions","#messageId","#emitter","#client","#sessionId","#started","#destroyed","#onBindingCalled","#onFrameNavigated","#actions","#client","#targetId","#sessionId","#writer","#url","#destroyHandler","#closed","#evaluate","#waitForLoadEvent","#stopLoading","#waitForSelector","#flattenFrameTree","#codegen","#client","#id","#viewport","#writer","#pages"],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/helpers.ts","../../../src/core/CDPClient.ts","../../../src/core/BrowserCodegen.ts","../../../src/core/BrowserPage.ts","../../../src/core/BrowserContext.ts","../../../src/core/factories.ts"],"sourcesContent":["// === Base64\n\n/** Base64 alphabet, index-ordered, used to build {@link BASE64_LOOKUP}. */\nexport const BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\n\n/** Base64 character to 6-bit value lookup table, derived from {@link BASE64_CHARS}. */\nexport const BASE64_LOOKUP: Readonly<Record<string, number>> = Object.freeze(\n\tObject.fromEntries(BASE64_CHARS.split('').map((char, index) => [char, index])),\n)\n\n// === Browser\n\n/** Default timeout in milliseconds for browser connection, requests, and navigation. */\nexport const BROWSER_DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Maximum serialized-character length for an `evaluate()`/`content()` result,\n * enforced IN-PAGE before the result is returned to CDP.\n *\n * @remarks\n * This counts UTF-16 STRING LENGTH (`String#length`), not transport BYTES —\n * the actual CDP frame is UTF-8 encoded (up to 3 bytes/char for common\n * multibyte content) and carries additional JSON/CDP framing overhead on top\n * of the raw content. A `Runtime.evaluate` result above this size, once\n * framed as a CDP JSON response, overflows the native WebSocket inbound frame\n * limit and closes the whole CDP connection — a page-level failure with no\n * clean error. The limit is set well under the observed ~3-4MB transport\n * ceiling (rather than at it) to leave headroom for the length-vs-bytes gap\n * and framing overhead. The cap is enforced by stringifying the candidate\n * result in-page and throwing a recognizable sentinel error (see\n * {@link BROWSER_RESULT_LIMIT_SENTINEL_PREFIX}) before the oversized frame is\n * ever produced, so the caller gets a coded error instead of a dropped\n * connection.\n */\nexport const BROWSER_RESULT_LIMIT = 2_500_000\n\n/**\n * Distinctive prefix for the in-page result-limit sentinel error, immediately\n * followed by the serialized length.\n *\n * @remarks\n * Deliberately unlikely to appear in a page's own thrown error text (unlike\n * a plain `BROWSER_RESULT_LIMIT: ` label), so {@link BROWSER_RESULT_LIMIT_PATTERN}\n * can distinguish the guard's own throw from a page error that merely\n * mentions similar text.\n */\nexport const BROWSER_RESULT_LIMIT_SENTINEL_PREFIX = '[[ORKESTREL_BROWSER_RESULT_LIMIT]]'\n\n/**\n * Matches the in-page result-limit sentinel error message, anchored\n * immediately after the `Error: ` (optionally `Uncaught Error: `) prefix that\n * Chromium prepends to a thrown error's description — so the guard's own\n * throw is recognized only at the message START, not wherever the substring\n * happens to occur.\n */\nexport const BROWSER_RESULT_LIMIT_PATTERN = new RegExp(\n\t`(?:Uncaught )?Error: \\\\[\\\\[ORKESTREL_BROWSER_RESULT_LIMIT\\\\]\\\\](\\\\d+)`,\n)\n\n/** Poll interval in milliseconds while waiting for a selector to appear. */\nexport const BROWSER_WAIT_POLL_INTERVAL_MS = 100\n\n/**\n * Bound (in milliseconds) on the best-effort `Page.stopLoading` call issued\n * after a failed `navigate()`.\n *\n * @remarks\n * A wedged renderer can make the underlying CDP call hang for the full\n * per-call timeout; capping it to a small fixed bound keeps a navigate\n * failure's total latency close to the original timeout instead of doubling\n * it. Best-effort only — never masks the original navigate error.\n */\nexport const BROWSER_STOP_LOADING_TIMEOUT_MS = 1_000\n\n/** Default viewport width in pixels. */\nexport const BROWSER_DEFAULT_VIEWPORT_WIDTH = 1280\n\n/** Default viewport height in pixels. */\nexport const BROWSER_DEFAULT_VIEWPORT_HEIGHT = 720\n\n// === Browser codegen\n\n/** Name of the CDP runtime binding the codegen recorder script calls into. */\nexport const BROWSER_CODEGEN_BINDING_NAME = '__orkestrelBrowserCodegen'\n\n/**\n * In-page recorder script injected via `Page.addScriptToEvaluateOnNewDocument`\n * and `Runtime.evaluate`.\n *\n * @remarks\n * Attaches capturing-phase listeners for `click`, `input` (fill), and\n * `change` (select) on `document`, builds a stable CSS selector for the\n * target element, and forwards each action to the CDP binding\n * ({@link BROWSER_CODEGEN_BINDING_NAME}) as a JSON string payload. Guarded to\n * install exactly once per document (`window[name]` sentinel) so repeated\n * injection on every new document is idempotent.\n */\nexport const BROWSER_CODEGEN_SOURCE = `(() => {\n\tconst bindingName = ${JSON.stringify(BROWSER_CODEGEN_BINDING_NAME)}\n\tif (window[bindingName + '__installed']) return\n\twindow[bindingName + '__installed'] = true\n\n\tconst selectorFor = (el) => {\n\t\tif (el.id) return '#' + CSS.escape(el.id)\n\t\tconst parts = []\n\t\tlet node = el\n\t\twhile (node && node.nodeType === 1 && parts.length < 8) {\n\t\t\tlet part = node.tagName.toLowerCase()\n\t\t\tif (node.classList && node.classList.length > 0) {\n\t\t\t\tpart += '.' + Array.from(node.classList).map((cls) => CSS.escape(cls)).join('.')\n\t\t\t}\n\t\t\tconst parent = node.parentElement\n\t\t\tif (parent) {\n\t\t\t\tconst siblings = Array.from(parent.children).filter((c) => c.tagName === node.tagName)\n\t\t\t\tif (siblings.length > 1) {\n\t\t\t\t\tpart += ':nth-of-type(' + (siblings.indexOf(node) + 1) + ')'\n\t\t\t\t}\n\t\t\t}\n\t\t\tparts.unshift(part)\n\t\t\tnode = parent\n\t\t}\n\t\treturn parts.join(' > ')\n\t}\n\n\tconst send = (payload) => {\n\t\tif (typeof window[bindingName] === 'function') {\n\t\t\twindow[bindingName](JSON.stringify(payload))\n\t\t}\n\t}\n\n\tdocument.addEventListener(\n\t\t'click',\n\t\t(event) => {\n\t\t\tconst target = event.target\n\t\t\tif (!target || target.nodeType !== 1) return\n\t\t\tsend({ action: 'click', selector: selectorFor(target) })\n\t\t},\n\t\ttrue,\n\t)\n\n\tconst fillableTypes = new Set([\n\t\t'text',\n\t\t'search',\n\t\t'url',\n\t\t'tel',\n\t\t'email',\n\t\t'password',\n\t\t'number',\n\t])\n\n\tdocument.addEventListener(\n\t\t'input',\n\t\t(event) => {\n\t\t\tconst target = event.target\n\t\t\tif (!target || target.nodeType !== 1) return\n\n\t\t\tif (target.isContentEditable) {\n\t\t\t\tsend({ action: 'fill', selector: selectorFor(target), value: target.textContent || '' })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (typeof target.value !== 'string') return\n\t\t\tconst tag = target.tagName\n\t\t\tif (tag === 'TEXTAREA') {\n\t\t\t\tsend({ action: 'fill', selector: selectorFor(target), value: target.value })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (tag === 'INPUT' && fillableTypes.has((target.type || 'text').toLowerCase())) {\n\t\t\t\tsend({ action: 'fill', selector: selectorFor(target), value: target.value })\n\t\t\t}\n\t\t},\n\t\ttrue,\n\t)\n\n\tdocument.addEventListener(\n\t\t'change',\n\t\t(event) => {\n\t\t\tconst target = event.target\n\t\t\tif (!target || target.tagName !== 'SELECT') return\n\t\t\tconst values = Array.from(target.selectedOptions || []).map((option) => option.value)\n\t\t\tsend({ action: 'select', selector: selectorFor(target), values })\n\t\t},\n\t\ttrue,\n\t)\n})()`\n","// === Browser errors\n\n/**\n * Base error for all browser automation operations.\n *\n * @remarks\n * Carries a machine-readable `code` and optional `context` (AGENTS §12) so\n * callers can branch in a `catch` without parsing message strings.\n */\nexport class BrowserError extends Error {\n\treadonly code: string\n\treadonly context: Readonly<Record<string, unknown>> | undefined\n\n\tconstructor(\n\t\tmessage: string,\n\t\tcode = 'BROWSER_ERROR',\n\t\tcontext?: Readonly<Record<string, unknown>>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'BrowserError'\n\t\tthis.code = code\n\t\tthis.context = context\n\t}\n}\n\n/**\n * A selector-based lookup or wait timed out without the element appearing.\n */\nexport class BrowserSelectorError extends BrowserError {\n\tconstructor(message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message, 'BROWSER_SELECTOR_ERROR', context)\n\t\tthis.name = 'BrowserSelectorError'\n\t}\n}\n\n/**\n * A CDP request received an error response from the remote endpoint.\n *\n * @remarks\n * Carries the originating `method` plus the CDP error's own `code`,\n * `message`, and `data` (when present) in `context`, so callers can branch\n * on the protocol-level error instead of parsing the message string.\n */\nexport class CDPError extends BrowserError {\n\tconstructor(message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message, 'BROWSER_CDP_ERROR', context)\n\t\tthis.name = 'CDPError'\n\t}\n}\n\n/**\n * A CDP request could not be sent or completed because the client was not in\n * a connectable state (not connected, closed while connecting, or the\n * connection dropped mid-request).\n */\nexport class CDPConnectionError extends BrowserError {\n\tconstructor(message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message, 'BROWSER_CDP_CONNECTION_ERROR', context)\n\t\tthis.name = 'CDPConnectionError'\n\t}\n}\n\n/**\n * A pending CDP request was not answered within its timeout window.\n */\nexport class CDPTimeoutError extends BrowserError {\n\tconstructor(message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message, 'BROWSER_CDP_TIMEOUT_ERROR', context)\n\t\tthis.name = 'CDPTimeoutError'\n\t}\n}\n\n/**\n * An `evaluate()`/`content()` result exceeded {@link BROWSER_RESULT_LIMIT}\n * and was rejected in-page before it could overflow the CDP transport frame.\n */\nexport class BrowserResultLimitError extends BrowserError {\n\tconstructor(message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message, 'BROWSER_RESULT_LIMIT_ERROR', context)\n\t\tthis.name = 'BrowserResultLimitError'\n\t}\n}\n\n// === Browser type guards\n\n/**\n * Narrow an unknown value to BrowserError.\n *\n * @param value - Value to check\n * @returns True when value is a BrowserError instance\n */\nexport function isBrowserError(value: unknown): value is BrowserError {\n\treturn value instanceof BrowserError\n}\n\n/**\n * Narrow an unknown value to BrowserSelectorError.\n *\n * @param value - Value to check\n * @returns True when value is a BrowserSelectorError instance\n */\nexport function isBrowserSelectorError(value: unknown): value is BrowserSelectorError {\n\treturn value instanceof BrowserSelectorError\n}\n\n/**\n * Narrow an unknown value to CDPError.\n *\n * @param value - Value to check\n * @returns True when value is a CDPError instance\n */\nexport function isCDPError(value: unknown): value is CDPError {\n\treturn value instanceof CDPError\n}\n\n/**\n * Narrow an unknown value to CDPConnectionError.\n *\n * @param value - Value to check\n * @returns True when value is a CDPConnectionError instance\n */\nexport function isCDPConnectionError(value: unknown): value is CDPConnectionError {\n\treturn value instanceof CDPConnectionError\n}\n\n/**\n * Narrow an unknown value to CDPTimeoutError.\n *\n * @param value - Value to check\n * @returns True when value is a CDPTimeoutError instance\n */\nexport function isCDPTimeoutError(value: unknown): value is CDPTimeoutError {\n\treturn value instanceof CDPTimeoutError\n}\n\n/**\n * Narrow an unknown value to BrowserResultLimitError.\n *\n * @param value - Value to check\n * @returns True when value is a BrowserResultLimitError instance\n */\nexport function isBrowserResultLimitError(value: unknown): value is BrowserResultLimitError {\n\treturn value instanceof BrowserResultLimitError\n}\n","import type { BrowserCodegenAction, BrowserCodegenScriptOptions } from './types.js'\nimport { isRecord, isString } from '@orkestrel/contract'\nimport { BASE64_LOOKUP, BROWSER_RESULT_LIMIT_SENTINEL_PREFIX } from './constants.js'\n\n/**\n * Decode a base64-encoded string into raw bytes.\n *\n * @remarks\n * Pure JS implementation — no `Buffer`, no `atob` (DOM-only) — so it runs\n * identically in Node and browser environments. Whitespace and `=` padding\n * are ignored; invalid characters are skipped.\n *\n * @param text - Base64-encoded input string\n * @returns Decoded bytes\n *\n * @example\n * ```ts\n * decodeBase64('aGVsbG8=') // Uint8Array [104, 101, 108, 108, 111]\n * ```\n */\nexport function decodeBase64(text: string): Uint8Array {\n\tconst clean = text.replace(/[^A-Za-z0-9+/]/g, '')\n\tconst bytes: number[] = []\n\n\tlet buffer = 0\n\tlet bits = 0\n\n\tfor (const char of clean) {\n\t\tconst value = BASE64_LOOKUP[char]\n\t\tif (value === undefined) continue\n\n\t\tbuffer = (buffer << 6) | value\n\t\tbits += 6\n\n\t\tif (bits >= 8) {\n\t\t\tbits -= 8\n\t\t\tbytes.push((buffer >> bits) & 0xff)\n\t\t}\n\t}\n\n\treturn new Uint8Array(bytes)\n}\n\n/**\n * Wrap a `Runtime.evaluate` expression so the IN-PAGE code stringifies its\n * own result and throws a recognizable error before an oversized result\n * would overflow the CDP transport frame.\n *\n * @remarks\n * A result whose `JSON.stringify` length exceeds `limit` throws\n * `Error('BROWSER_RESULT_LIMIT: <length>')` inside the page instead of being\n * returned — the caller maps that sentinel to a coded\n * {@link BrowserResultLimitError}. A non-serializable result (`undefined`,\n * a function, a symbol) makes `JSON.stringify` return `undefined`, so the\n * length check is skipped and today's undefined-passthrough behavior is\n * unchanged.\n *\n * The expression is placed on its own line inside the wrapper (rather than\n * inline with the guard code) so a trailing `//` line comment in the\n * expression cannot swallow the closing guard syntax that follows it.\n *\n * @param expression - The candidate JavaScript expression to evaluate\n * @param limit - Maximum serialized-character length (see {@link BROWSER_RESULT_LIMIT})\n * @returns The wrapped, guarded expression\n */\nexport function guardEvaluateExpression(expression: string, limit: number): string {\n\treturn `(() => { const r = (\n${expression}\n); const s = JSON.stringify(r); if (typeof s === 'string' && s.length > ${limit}) throw new Error(${JSON.stringify(BROWSER_RESULT_LIMIT_SENTINEL_PREFIX)} + s.length); return r })()`\n}\n\n/**\n * Normalize a raw list of recorded codegen actions.\n *\n * @remarks\n * Collapses consecutive `fill` actions on the same selector into the latest\n * value (a text input fires one `input` event per keystroke) so the\n * compiled script reflects the final typed value rather than every\n * intermediate keystroke.\n *\n * @param actions - Raw recorded actions, in capture order\n * @returns Normalized actions, in the same order\n */\nexport function normalizeCodegenActions(\n\tactions: readonly BrowserCodegenAction[],\n): readonly BrowserCodegenAction[] {\n\tconst result: BrowserCodegenAction[] = []\n\n\tfor (const action of actions) {\n\t\tconst previous = result[result.length - 1]\n\t\tif (\n\t\t\tprevious !== undefined &&\n\t\t\tprevious.action === 'fill' &&\n\t\t\taction.action === 'fill' &&\n\t\t\tprevious.selector === action.selector\n\t\t) {\n\t\t\tresult[result.length - 1] = action\n\t\t\tcontinue\n\t\t}\n\t\tresult.push(action)\n\t}\n\n\treturn result\n}\n\n/**\n * Parse a codegen binding payload into a typed action.\n *\n * @remarks\n * The in-page recorder script calls the CDP binding with a JSON string\n * payload shaped like a {@link BrowserCodegenAction}. Returns `undefined`\n * when the payload is not valid JSON or does not match a known action shape.\n *\n * @param payload - Raw binding call payload (the CDP `payload` param)\n * @returns The parsed action, or `undefined` when the payload is malformed\n */\nexport function parseCodegenActionPayload(payload: unknown): BrowserCodegenAction | undefined {\n\tif (!isString(payload)) return undefined\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(payload)\n\t} catch {\n\t\treturn undefined\n\t}\n\n\tif (!isRecord(parsed)) return undefined\n\n\tconst action = parsed['action']\n\tconst selector = parsed['selector']\n\n\tif (action === 'click' && isString(selector)) {\n\t\treturn { action: 'click', selector }\n\t}\n\n\tif (action === 'fill' && isString(selector) && isString(parsed['value'])) {\n\t\treturn { action: 'fill', selector, value: parsed['value'] }\n\t}\n\n\tif (action === 'select' && isString(selector) && Array.isArray(parsed['values'])) {\n\t\tconst values = parsed['values'].filter(isString)\n\t\tif (values.length === parsed['values'].length) {\n\t\t\treturn { action: 'select', selector, values }\n\t\t}\n\t}\n\n\treturn undefined\n}\n\n/**\n * Derive a `navigate` codegen action from a `Page.frameNavigated` CDP event.\n *\n * @remarks\n * Only the top-level (main) frame's navigation is recorded — a frame\n * carrying a `parentId` is a sub-frame and is ignored.\n *\n * @param params - The CDP `Page.frameNavigated` event params\n * @returns A `navigate` action, or `undefined` when the event is not a\n * top-level navigation with a resolvable URL\n */\nexport function readCodegenNavigateAction(\n\tparams: Readonly<Record<string, unknown>>,\n): BrowserCodegenAction | undefined {\n\tconst frame = params['frame']\n\tif (!isRecord(frame)) return undefined\n\tif ('parentId' in frame) return undefined\n\tif (!isString(frame['url'])) return undefined\n\n\treturn { action: 'navigate', url: frame['url'] }\n}\n\n/**\n * Compile recorded codegen actions into a replayable script.\n *\n * @remarks\n * Emits one statement per action against a `page` object shaped like\n * {@link BrowserPageInterface} (`page.navigate(...)`, `page.click(...)`, …).\n * Both target languages emit an `async function run(page)` body whose\n * statements are `await`-ed; `language` only toggles whether the `page`\n * parameter carries a TypeScript type annotation (default `'javascript'`).\n *\n * @param actions - Normalized actions to compile\n * @param options - Compilation options (target language)\n * @returns The compiled script source\n */\nexport function compileCodegenScript(\n\tactions: readonly BrowserCodegenAction[],\n\toptions?: BrowserCodegenScriptOptions,\n): string {\n\tconst language = options?.language ?? 'javascript'\n\n\tconst lines = actions.map((action) => {\n\t\tswitch (action.action) {\n\t\t\tcase 'navigate':\n\t\t\t\treturn `await page.navigate(${JSON.stringify(action.url)})`\n\t\t\tcase 'click':\n\t\t\t\treturn `await page.click(${JSON.stringify(action.selector)})`\n\t\t\tcase 'fill':\n\t\t\t\treturn `await page.fill(${JSON.stringify(action.selector)}, ${JSON.stringify(action.value)})`\n\t\t\tcase 'select':\n\t\t\t\treturn `await page.select(${JSON.stringify(action.selector)}, ${JSON.stringify(action.values)})`\n\t\t}\n\t})\n\n\tif (language === 'typescript') {\n\t\treturn [\n\t\t\t`async function run(page: import('@orkestrel/browser').BrowserPageInterface): Promise<void> {`,\n\t\t\t...lines.map((line) => `\\t${line}`),\n\t\t\t`}`,\n\t\t].join('\\n')\n\t}\n\n\treturn [`async function run(page) {`, ...lines.map((line) => `\\t${line}`), `}`].join('\\n')\n}\n","import type {\n\tCDPClientInterface,\n\tCDPClientOptions,\n\tCDPHandler,\n\tCDPTransportInterface,\n} from './types.js'\nimport { BROWSER_DEFAULT_TIMEOUT_MS } from './constants.js'\nimport { CDPConnectionError, CDPError, CDPTimeoutError } from './errors.js'\nimport { isRecord, isString } from '@orkestrel/contract'\n\n// === CDPClient\n\n/**\n * Lightweight Chrome DevTools Protocol client over a {@link CDPTransportInterface}.\n *\n * @remarks\n * Sends JSON-RPC messages over the injected transport and dispatches\n * responses and events. The transport owns the connection itself\n * (WebSocket, pipe, or any other duplex channel); this class owns only\n * the JSON-RPC framing and id/session correlation.\n *\n * Supports session-scoped event subscriptions: when a sessionId is\n * provided to subscribe/unsubscribe, the handler only fires for CDP\n * events carrying that sessionId. Global subscriptions (no sessionId)\n * continue to see ALL events for backwards compatibility.\n */\nexport class CDPClient implements CDPClientInterface {\n\t#transport: CDPTransportInterface\n\t#messageId = 0\n\t#pending: Map<\n\t\tnumber,\n\t\t{\n\t\t\tmethod: string\n\t\t\tresolve: (value: unknown) => void\n\t\t\treject: (reason: unknown) => void\n\t\t\ttimer: ReturnType<typeof setTimeout>\n\t\t}\n\t> = new Map()\n\t#subscriptions: Map<string, Set<CDPHandler>> = new Map()\n\t#sessionSubscriptions: Map<string, Map<string, Set<CDPHandler>>> = new Map()\n\t#connected = false\n\t#wired = false\n\t#timeout: number\n\t#connecting: Promise<void> | undefined\n\t#closeRequested = false\n\n\tconstructor(options: CDPClientOptions) {\n\t\tthis.#transport = options.transport\n\t\tthis.#timeout = options.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t}\n\n\tget connected(): boolean {\n\t\treturn this.#connected\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (this.#connected) return\n\t\tif (this.#connecting !== undefined) return this.#connecting\n\n\t\tthis.#closeRequested = false\n\n\t\tconst attempt = (async (): Promise<void> => {\n\t\t\tif (!this.#wired) {\n\t\t\t\tthis.#transport.emitter.on('message', (data) => this.#onMessage(data))\n\t\t\t\tthis.#transport.emitter.on('close', () => this.#onClose())\n\t\t\t\tthis.#transport.emitter.on('error', (error) => this.#onError(error))\n\t\t\t\tthis.#wired = true\n\t\t\t}\n\n\t\t\tawait this.#transport.start()\n\n\t\t\tif (this.#closeRequested) {\n\t\t\t\tthis.#closeRequested = false\n\t\t\t\tawait this.#transport.close()\n\t\t\t\tthrow new CDPConnectionError('CDP client was closed while connecting', {\n\t\t\t\t\tmethod: 'connect',\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tthis.#connected = true\n\t\t})()\n\n\t\tthis.#connecting = attempt\n\t\ttry {\n\t\t\tawait attempt\n\t\t} finally {\n\t\t\tthis.#connecting = undefined\n\t\t}\n\t}\n\n\t/**\n\t * Close the transport and re-establish a fresh connection.\n\t *\n\t * @remarks\n\t * Client-level subscriptions (`subscribe`/`unsubscribe` registrations)\n\t * survive `close()` and remain active after the reconnect — only pending\n\t * in-flight requests are rejected. This lets a caller reconnect a dropped\n\t * transport without re-registering every handler.\n\t */\n\tasync reconnect(): Promise<void> {\n\t\tawait this.close()\n\t\tawait this.connect()\n\t}\n\n\tasync send(\n\t\tmethod: string,\n\t\tparams?: Readonly<Record<string, unknown>>,\n\t\tsessionId?: string,\n\t\ttimeout?: number,\n\t): Promise<unknown> {\n\t\tif (!this.#connected) {\n\t\t\tthrow new CDPConnectionError('CDP client is not connected', { method })\n\t\t}\n\n\t\tconst id = this.#nextId()\n\t\tconst message: Record<string, unknown> = { id, method }\n\n\t\tif (params !== undefined) {\n\t\t\tmessage['params'] = params\n\t\t}\n\t\tif (sessionId !== undefined) {\n\t\t\tmessage['sessionId'] = sessionId\n\t\t}\n\n\t\tconst serialized = JSON.stringify(message)\n\t\tconst effectiveTimeout = timeout ?? this.#timeout\n\n\t\treturn new Promise<unknown>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.#pending.delete(id)\n\t\t\t\treject(\n\t\t\t\t\tnew CDPTimeoutError(`CDP request timed out: ${method}`, {\n\t\t\t\t\t\tmethod,\n\t\t\t\t\t\ttimeout: effectiveTimeout,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t}, effectiveTimeout)\n\n\t\t\tthis.#pending.set(id, { method, resolve, reject, timer })\n\n\t\t\tthis.#transport.send(serialized).catch((thrown: unknown) => {\n\t\t\t\tthis.#pending.delete(id)\n\t\t\t\tclearTimeout(timer)\n\t\t\t\treject(thrown)\n\t\t\t})\n\t\t})\n\t}\n\n\tsubscribe(method: string, handler: CDPHandler, sessionId?: string): void {\n\t\tif (sessionId !== undefined) {\n\t\t\tlet sessionMap = this.#sessionSubscriptions.get(sessionId)\n\t\t\tif (sessionMap === undefined) {\n\t\t\t\tsessionMap = new Map()\n\t\t\t\tthis.#sessionSubscriptions.set(sessionId, sessionMap)\n\t\t\t}\n\t\t\tlet handlers = sessionMap.get(method)\n\t\t\tif (handlers === undefined) {\n\t\t\t\thandlers = new Set()\n\t\t\t\tsessionMap.set(method, handlers)\n\t\t\t}\n\t\t\thandlers.add(handler)\n\t\t} else {\n\t\t\tlet handlers = this.#subscriptions.get(method)\n\t\t\tif (handlers === undefined) {\n\t\t\t\thandlers = new Set()\n\t\t\t\tthis.#subscriptions.set(method, handlers)\n\t\t\t}\n\t\t\thandlers.add(handler)\n\t\t}\n\t}\n\n\tunsubscribe(method: string, handler: CDPHandler, sessionId?: string): void {\n\t\tif (sessionId !== undefined) {\n\t\t\tconst sessionMap = this.#sessionSubscriptions.get(sessionId)\n\t\t\tif (sessionMap !== undefined) {\n\t\t\t\tconst handlers = sessionMap.get(method)\n\t\t\t\tif (handlers !== undefined) {\n\t\t\t\t\thandlers.delete(handler)\n\t\t\t\t\tif (handlers.size === 0) {\n\t\t\t\t\t\tsessionMap.delete(method)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (sessionMap.size === 0) {\n\t\t\t\t\tthis.#sessionSubscriptions.delete(sessionId)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst handlers = this.#subscriptions.get(method)\n\t\t\tif (handlers !== undefined) {\n\t\t\t\thandlers.delete(handler)\n\t\t\t\tif (handlers.size === 0) {\n\t\t\t\t\tthis.#subscriptions.delete(method)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Close the transport and reject all pending requests.\n\t *\n\t * @remarks\n\t * Subscriptions registered via `subscribe()` are client-level\n\t * registrations, not connection-level state — they survive `close()`\n\t * (and a subsequent `reconnect()`/`connect()`) and keep firing once the\n\t * transport is active again. Only pending requests are rejected here.\n\t * If `close()` is called while `connect()` is still awaiting\n\t * `transport.start()`, the in-flight connect is rejected and the\n\t * transport is closed deterministically once `start()` resolves.\n\t */\n\tasync close(): Promise<void> {\n\t\tif (this.#connecting !== undefined) {\n\t\t\tthis.#closeRequested = true\n\t\t\tawait this.#connecting.catch(() => undefined)\n\t\t\t// The in-flight attempt may have already flipped #connected to\n\t\t\t// true before observing #closeRequested (race between the\n\t\t\t// attempt's final steps and #closeRequested being set here).\n\t\t\t// Fall through to the normal connected-close path instead of\n\t\t\t// returning early, or the close would be silently lost.\n\t\t}\n\n\t\tif (!this.#connected) return\n\n\t\tthis.#connected = false\n\n\t\t// Reject all pending requests\n\t\tfor (const [id, entry] of this.#pending) {\n\t\t\tclearTimeout(entry.timer)\n\t\t\tentry.reject(new CDPConnectionError('CDP connection closed', { method: entry.method }))\n\t\t\tthis.#pending.delete(id)\n\t\t}\n\n\t\tawait this.#transport.close()\n\t}\n\n\t// === Private helpers\n\n\t#nextId(): number {\n\t\tthis.#messageId += 1\n\t\treturn this.#messageId\n\t}\n\n\t#onClose(): void {\n\t\tthis.#connected = false\n\n\t\t// Reject all pending requests\n\t\tfor (const [id, entry] of this.#pending) {\n\t\t\tclearTimeout(entry.timer)\n\t\t\tentry.reject(new CDPConnectionError('CDP connection closed', { method: entry.method }))\n\t\t\tthis.#pending.delete(id)\n\t\t}\n\t}\n\n\t#onError(error: unknown): void {\n\t\tif (!this.#connected) {\n\t\t\tfor (const [id, entry] of this.#pending) {\n\t\t\t\tclearTimeout(entry.timer)\n\t\t\t\tentry.reject(\n\t\t\t\t\tnew CDPConnectionError(`CDP connection failed: ${String(error)}`, {\n\t\t\t\t\t\tmethod: entry.method,\n\t\t\t\t\t\terror,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t\tthis.#pending.delete(id)\n\t\t\t}\n\t\t}\n\t}\n\n\t#onMessage(data: string): void {\n\t\tlet parsed: unknown\n\t\ttry {\n\t\t\tparsed = JSON.parse(data)\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\n\t\tif (!isRecord(parsed)) return\n\n\t\t// Response to a pending request\n\t\tif (typeof parsed['id'] === 'number') {\n\t\t\tconst id = parsed['id']\n\t\t\tconst entry = this.#pending.get(id)\n\t\t\tif (entry !== undefined) {\n\t\t\t\tthis.#pending.delete(id)\n\t\t\t\tclearTimeout(entry.timer)\n\n\t\t\t\tconst errorValue = parsed['error']\n\t\t\t\tif (isRecord(errorValue)) {\n\t\t\t\t\tconst message = isString(errorValue['message']) ? errorValue['message'] : 'CDP error'\n\t\t\t\t\tconst context: Record<string, unknown> = { method: entry.method }\n\t\t\t\t\tif ('code' in errorValue) context['code'] = errorValue['code']\n\t\t\t\t\tcontext['message'] = message\n\t\t\t\t\tif ('data' in errorValue) context['data'] = errorValue['data']\n\t\t\t\t\tentry.reject(new CDPError(message, context))\n\t\t\t\t} else {\n\t\t\t\t\tentry.resolve('result' in parsed ? parsed['result'] : undefined)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// CDP event\n\t\tif (isString(parsed['method'])) {\n\t\t\tconst method = parsed['method']\n\t\t\tconst rawParams = parsed['params']\n\t\t\tconst params: Readonly<Record<string, unknown>> = isRecord(rawParams)\n\t\t\t\t? rawParams\n\t\t\t\t: Object.freeze({})\n\n\t\t\t// Fire global handlers (backwards compatible — see ALL events)\n\t\t\tconst globalHandlers = this.#subscriptions.get(method)\n\t\t\tif (globalHandlers !== undefined) {\n\t\t\t\tfor (const handler of [...globalHandlers]) {\n\t\t\t\t\thandler(params)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Fire session-scoped handlers\n\t\t\tconst eventSessionId = parsed['sessionId']\n\t\t\tif (isString(eventSessionId)) {\n\t\t\t\tconst sessionMap = this.#sessionSubscriptions.get(eventSessionId)\n\t\t\t\tif (sessionMap !== undefined) {\n\t\t\t\t\tconst sessionHandlers = sessionMap.get(method)\n\t\t\t\t\tif (sessionHandlers !== undefined) {\n\t\t\t\t\t\tfor (const handler of [...sessionHandlers]) {\n\t\t\t\t\t\t\thandler(params)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","import type {\n\tBrowserCodegenAction,\n\tBrowserCodegenEventMap,\n\tBrowserCodegenInterface,\n\tBrowserCodegenOptions,\n\tBrowserCodegenScriptOptions,\n\tCDPClientInterface,\n} from './types.js'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport { BROWSER_CODEGEN_BINDING_NAME, BROWSER_CODEGEN_SOURCE } from './constants.js'\nimport {\n\tcompileCodegenScript,\n\tnormalizeCodegenActions,\n\tparseCodegenActionPayload,\n\treadCodegenNavigateAction,\n} from './helpers.js'\nimport { Emitter } from '@orkestrel/emitter'\n\n// === BrowserCodegen\n\nexport class BrowserCodegen implements BrowserCodegenInterface {\n\t#client: CDPClientInterface\n\t#sessionId: string\n\t#actions: BrowserCodegenAction[] = []\n\t#started = false\n\t#destroyed = false\n\n\treadonly #emitter: Emitter<BrowserCodegenEventMap>\n\n\tconstructor(client: CDPClientInterface, sessionId: string, options?: BrowserCodegenOptions) {\n\t\tthis.#client = client\n\t\tthis.#sessionId = sessionId\n\t\tthis.#emitter = new Emitter({ on: options?.on })\n\t}\n\n\t// === Property accessors\n\n\tget emitter(): EmitterInterface<BrowserCodegenEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget started(): boolean {\n\t\treturn this.#started\n\t}\n\n\t// === Public API\n\n\tasync start(): Promise<void> {\n\t\tif (this.#destroyed) return\n\t\tif (this.#started) return\n\t\tthis.#started = true\n\n\t\tthis.#client.subscribe('Runtime.bindingCalled', this.#onBindingCalled, this.#sessionId)\n\t\tthis.#client.subscribe('Page.frameNavigated', this.#onFrameNavigated, this.#sessionId)\n\n\t\ttry {\n\t\t\tawait this.#client.send('Runtime.enable', undefined, this.#sessionId)\n\t\t\tawait this.#client.send(\n\t\t\t\t'Runtime.addBinding',\n\t\t\t\t{ name: BROWSER_CODEGEN_BINDING_NAME },\n\t\t\t\tthis.#sessionId,\n\t\t\t)\n\t\t\tawait this.#client.send(\n\t\t\t\t'Page.addScriptToEvaluateOnNewDocument',\n\t\t\t\t{ source: BROWSER_CODEGEN_SOURCE },\n\t\t\t\tthis.#sessionId,\n\t\t\t)\n\t\t\tawait this.#client.send(\n\t\t\t\t'Runtime.evaluate',\n\t\t\t\t{ expression: BROWSER_CODEGEN_SOURCE, awaitPromise: true },\n\t\t\t\tthis.#sessionId,\n\t\t\t)\n\t\t} catch (error) {\n\t\t\tthis.#client.unsubscribe('Runtime.bindingCalled', this.#onBindingCalled, this.#sessionId)\n\t\t\tthis.#client.unsubscribe('Page.frameNavigated', this.#onFrameNavigated, this.#sessionId)\n\t\t\tthis.#started = false\n\t\t\tthrow error\n\t\t}\n\n\t\tthis.#emitter.emit('start')\n\t}\n\n\tasync stop(): Promise<readonly BrowserCodegenAction[]> {\n\t\tif (!this.#started) return this.actions()\n\n\t\tthis.#client.unsubscribe('Runtime.bindingCalled', this.#onBindingCalled, this.#sessionId)\n\t\tthis.#client.unsubscribe('Page.frameNavigated', this.#onFrameNavigated, this.#sessionId)\n\n\t\tthis.#started = false\n\n\t\tconst snapshot = this.actions()\n\t\tthis.#emitter.emit('stop', snapshot)\n\t\treturn snapshot\n\t}\n\n\tactions(): readonly BrowserCodegenAction[] {\n\t\treturn normalizeCodegenActions(this.#actions)\n\t}\n\n\tscript(options?: BrowserCodegenScriptOptions): string {\n\t\treturn compileCodegenScript(this.actions(), options)\n\t}\n\n\tclear(): void {\n\t\tthis.#actions = []\n\t\tthis.#emitter.emit('clear')\n\t}\n\n\tasync destroy(): Promise<void> {\n\t\tif (this.#destroyed) return\n\t\tthis.#destroyed = true\n\n\t\ttry {\n\t\t\tawait this.stop()\n\t\t} catch {\n\t\t\t// Swallow teardown errors — the underlying session may already be gone\n\t\t}\n\n\t\tthis.#actions = []\n\t\tthis.#emitter.destroy()\n\t}\n\n\t// === Private helpers\n\n\t#onBindingCalled = (params: Readonly<Record<string, unknown>>): void => {\n\t\tif (params['name'] !== BROWSER_CODEGEN_BINDING_NAME) return\n\n\t\tconst action = parseCodegenActionPayload(params['payload'])\n\t\tif (action === undefined) return\n\n\t\tthis.#actions.push(action)\n\t\tthis.#emitter.emit('action', action)\n\t}\n\n\t#onFrameNavigated = (params: Readonly<Record<string, unknown>>): void => {\n\t\tconst action = readCodegenNavigateAction(params)\n\t\tif (action === undefined) return\n\n\t\tthis.#actions.push(action)\n\t\tthis.#emitter.emit('action', action)\n\t}\n}\n","import type {\n\tBrowserCodegenInterface,\n\tBrowserCodegenOptions,\n\tBrowserFrame,\n\tBrowserPageInterface,\n\tBrowserNavigationOptions,\n\tBrowserActionOptions,\n\tBrowserScreenshotOptions,\n\tBrowserContentResult,\n\tBrowserScreenshotResult,\n\tCDPClientInterface,\n\tScreenshotWriterInterface,\n} from './types.js'\nimport { BrowserCodegen } from './BrowserCodegen.js'\nimport { BrowserError, BrowserResultLimitError, BrowserSelectorError } from './errors.js'\nimport {\n\tBROWSER_DEFAULT_TIMEOUT_MS,\n\tBROWSER_RESULT_LIMIT,\n\tBROWSER_RESULT_LIMIT_PATTERN,\n\tBROWSER_STOP_LOADING_TIMEOUT_MS,\n\tBROWSER_WAIT_POLL_INTERVAL_MS,\n} from './constants.js'\nimport { decodeBase64, guardEvaluateExpression } from './helpers.js'\nimport { isRecord, isString } from '@orkestrel/contract'\n\n// === BrowserPage\n\nexport class BrowserPage implements BrowserPageInterface {\n\t#client: CDPClientInterface\n\t#targetId: string\n\t#sessionId: string\n\t#writer: ScreenshotWriterInterface | undefined\n\t#url = 'about:blank'\n\t#closed = false\n\t#codegen: BrowserCodegen | undefined\n\t#destroyHandler: (params: Readonly<Record<string, unknown>>) => void\n\n\tconstructor(\n\t\tclient: CDPClientInterface,\n\t\ttargetId: string,\n\t\tsessionId: string,\n\t\twriter?: ScreenshotWriterInterface,\n\t\turl?: string,\n\t) {\n\t\tthis.#client = client\n\t\tthis.#targetId = targetId\n\t\tthis.#sessionId = sessionId\n\t\tthis.#writer = writer\n\t\tif (url !== undefined) this.#url = url\n\n\t\t// Subscribe to external close detection (browser-level event, global subscription)\n\t\tthis.#destroyHandler = (params) => {\n\t\t\tif (isString(params['targetId']) && params['targetId'] === this.#targetId) {\n\t\t\t\tthis.#closed = true\n\t\t\t}\n\t\t}\n\t\tthis.#client.subscribe('Target.targetDestroyed', this.#destroyHandler)\n\t}\n\n\t// === Property accessors\n\n\tget url(): string {\n\t\treturn this.#url\n\t}\n\n\tget closed(): boolean {\n\t\treturn this.#closed\n\t}\n\n\t// === Public API\n\n\tasync title(): Promise<string> {\n\t\tconst result = await this.#evaluate('document.title')\n\t\treturn typeof result === 'string' ? result : ''\n\t}\n\n\tasync navigate(url: string, options?: BrowserNavigationOptions): Promise<void> {\n\t\tconst timeout = options?.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t\tconst condition = options?.condition ?? 'load'\n\n\t\t// Set up load event listener before navigating\n\t\tconst wait = this.#waitForLoadEvent(condition, timeout)\n\n\t\t// Never let wait.promise reject unobserved — convert to an outcome\n\t\tconst waitOutcome = wait.promise.then(\n\t\t\t() => ({ ok: true as const }),\n\t\t\t(error: unknown) => ({ ok: false as const, error }),\n\t\t)\n\n\t\ttry {\n\t\t\tconst result: unknown = await this.#client.send(\n\t\t\t\t'Page.navigate',\n\t\t\t\t{ url },\n\t\t\t\tthis.#sessionId,\n\t\t\t\ttimeout,\n\t\t\t)\n\n\t\t\tif (isRecord(result) && isString(result['errorText'])) {\n\t\t\t\tthrow new BrowserError(`Navigation failed: ${result['errorText']}`)\n\t\t\t}\n\n\t\t\t// Wait for the load condition\n\t\t\tconst outcome = await waitOutcome\n\t\t\tif (!outcome.ok) throw outcome.error\n\t\t} catch (error) {\n\t\t\twait.cancel()\n\t\t\tawait this.#stopLoading(Math.min(timeout, BROWSER_STOP_LOADING_TIMEOUT_MS))\n\t\t\tthrow error\n\t\t}\n\n\t\tconst currentUrl = await this.#evaluate('location.href')\n\t\tthis.#url = typeof currentUrl === 'string' ? currentUrl : url\n\t}\n\n\tasync content(): Promise<BrowserContentResult> {\n\t\tconst [title, html, text, currentUrl] = await Promise.all([\n\t\t\tthis.#evaluate('document.title'),\n\t\t\tthis.#evaluate(\n\t\t\t\tguardEvaluateExpression('document.documentElement.outerHTML', BROWSER_RESULT_LIMIT),\n\t\t\t),\n\t\t\tthis.#evaluate(\n\t\t\t\tguardEvaluateExpression(\n\t\t\t\t\t'document.body ? document.body.innerText : \"\"',\n\t\t\t\t\tBROWSER_RESULT_LIMIT,\n\t\t\t\t),\n\t\t\t),\n\t\t\tthis.#evaluate('location.href'),\n\t\t])\n\n\t\tif (typeof currentUrl === 'string') {\n\t\t\tthis.#url = currentUrl\n\t\t}\n\n\t\treturn {\n\t\t\turl: this.#url,\n\t\t\ttitle: typeof title === 'string' ? title : '',\n\t\t\thtml: typeof html === 'string' ? html : '',\n\t\t\ttext: typeof text === 'string' ? text : '',\n\t\t}\n\t}\n\n\tasync screenshot(options?: BrowserScreenshotOptions): Promise<BrowserScreenshotResult> {\n\t\tconst format = options?.type ?? 'png'\n\t\tconst params: Record<string, unknown> = { format }\n\n\t\tif (options?.quality !== undefined && format === 'jpeg') {\n\t\t\tparams['quality'] = options.quality\n\t\t}\n\n\t\tif (options?.full === true) {\n\t\t\t// Get full page dimensions for full-page screenshot\n\t\t\tconst metrics: unknown = await this.#client.send(\n\t\t\t\t'Page.getLayoutMetrics',\n\t\t\t\tundefined,\n\t\t\t\tthis.#sessionId,\n\t\t\t)\n\n\t\t\tif (isRecord(metrics) && isRecord(metrics['contentSize'])) {\n\t\t\t\tconst contentSize = metrics['contentSize']\n\t\t\t\tconst width = typeof contentSize['width'] === 'number' ? contentSize['width'] : 0\n\t\t\t\tconst height = typeof contentSize['height'] === 'number' ? contentSize['height'] : 0\n\n\t\t\t\tif (width > 0 && height > 0) {\n\t\t\t\t\tparams['clip'] = { x: 0, y: 0, width, height, scale: 1 }\n\t\t\t\t\tparams['captureBeyondViewport'] = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst result: unknown = await this.#client.send(\n\t\t\t'Page.captureScreenshot',\n\t\t\tparams,\n\t\t\tthis.#sessionId,\n\t\t)\n\n\t\tif (!isRecord(result) || !isString(result['data'])) {\n\t\t\tthrow new BrowserError('Screenshot failed: no data returned')\n\t\t}\n\n\t\tconst bytes = decodeBase64(result['data'])\n\n\t\tif (options?.path !== undefined && this.#writer !== undefined) {\n\t\t\tawait this.#writer.write(options.path, bytes)\n\t\t}\n\n\t\treturn { bytes, path: options?.path }\n\t}\n\n\tasync click(selector: string, options?: BrowserActionOptions): Promise<void> {\n\t\tconst timeout = options?.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t\tawait this.#waitForSelector(selector, timeout)\n\t\tawait this.#evaluate(\n\t\t\t`(() => { const el = document.querySelector(${JSON.stringify(selector)}); if (el) { el.scrollIntoView({ block: 'center' }); el.click(); } else { throw new Error('Element not found: ' + ${JSON.stringify(selector)}); } })()`,\n\t\t)\n\t}\n\n\tasync fill(selector: string, value: string, options?: BrowserActionOptions): Promise<void> {\n\t\tconst timeout = options?.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t\tawait this.#waitForSelector(selector, timeout)\n\t\tawait this.#evaluate(\n\t\t\t`(() => {\n const el = document.querySelector(${JSON.stringify(selector)});\n if (!el) throw new Error('Element not found: ' + ${JSON.stringify(selector)});\n el.focus();\n if (el.isContentEditable) {\n el.textContent = ${JSON.stringify(value)};\n el.dispatchEvent(new Event('input', { bubbles: true }));\n } else {\n el.value = ${JSON.stringify(value)};\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n }\n })()`,\n\t\t)\n\t}\n\n\tasync select(\n\t\tselector: string,\n\t\tvalues: readonly string[],\n\t\toptions?: BrowserActionOptions,\n\t): Promise<void> {\n\t\tconst timeout = options?.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t\tawait this.#waitForSelector(selector, timeout)\n\t\tconst valuesJson = JSON.stringify([...values])\n\t\tawait this.#evaluate(\n\t\t\t`(() => {\n const el = document.querySelector(${JSON.stringify(selector)});\n if (!el) throw new Error('Element not found: ' + ${JSON.stringify(selector)});\n const vals = ${valuesJson};\n for (const opt of el.options) {\n opt.selected = vals.includes(opt.value);\n }\n el.dispatchEvent(new Event('change', { bubbles: true }));\n })()`,\n\t\t)\n\t}\n\n\tasync evaluate(expression: string): Promise<unknown> {\n\t\treturn this.#evaluate(guardEvaluateExpression(expression, BROWSER_RESULT_LIMIT))\n\t}\n\n\tasync wait(selector: string, options?: BrowserActionOptions): Promise<void> {\n\t\tconst timeout = options?.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t\tawait this.#waitForSelector(selector, timeout)\n\t}\n\n\tasync frame(name: string): Promise<BrowserFrame | undefined> {\n\t\tconst frames = await this.frames()\n\t\treturn frames.find((frame) => frame.name === name || frame.url === name)\n\t}\n\n\tasync frames(): Promise<readonly BrowserFrame[]> {\n\t\tconst result: unknown = await this.#client.send('Page.getFrameTree', undefined, this.#sessionId)\n\n\t\tif (!isRecord(result) || !isRecord(result['frameTree'])) return []\n\n\t\tconst frames: BrowserFrame[] = []\n\t\tthis.#flattenFrameTree(result['frameTree'], frames)\n\t\treturn frames\n\t}\n\n\tasync codegen(options?: BrowserCodegenOptions): Promise<BrowserCodegenInterface> {\n\t\tif (this.#codegen !== undefined) return this.#codegen\n\n\t\tconst codegen = new BrowserCodegen(this.#client, this.#sessionId, options)\n\t\tawait codegen.start()\n\t\tthis.#codegen = codegen\n\t\treturn codegen\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (this.#closed) return\n\t\tthis.#closed = true\n\n\t\t// Tear down the recorder first so it can detach CDP listeners cleanly\n\t\tif (this.#codegen !== undefined) {\n\t\t\ttry {\n\t\t\t\tawait this.#codegen.destroy()\n\t\t\t} catch {\n\t\t\t\t// Swallow recorder teardown errors during page close\n\t\t\t}\n\t\t\tthis.#codegen = undefined\n\t\t}\n\n\t\t// Unsubscribe from external close detection before sending close command\n\t\tthis.#client.unsubscribe('Target.targetDestroyed', this.#destroyHandler)\n\n\t\ttry {\n\t\t\tawait this.#client.send('Target.closeTarget', { targetId: this.#targetId })\n\t\t} catch {\n\t\t\t// Target may already be closed\n\t\t}\n\t}\n\n\t// === Private helpers\n\n\t#flattenFrameTree(node: Readonly<Record<string, unknown>>, out: BrowserFrame[]): void {\n\t\tconst frame = node['frame']\n\n\t\tif (isRecord(frame) && isString(frame['id']) && isString(frame['url'])) {\n\t\t\tconst parent = frame['parentId']\n\t\t\tconst name = frame['name']\n\n\t\t\tout.push({\n\t\t\t\tid: frame['id'],\n\t\t\t\t...(isString(parent) ? { parent } : {}),\n\t\t\t\t...(isString(name) && name !== '' ? { name } : {}),\n\t\t\t\turl: frame['url'],\n\t\t\t})\n\t\t}\n\n\t\tconst children = node['childFrames']\n\t\tif (Array.isArray(children)) {\n\t\t\tfor (const child of children) {\n\t\t\t\tif (isRecord(child)) this.#flattenFrameTree(child, out)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Best-effort: tells Chromium to abandon an in-flight navigation so the\n\t// renderer is not left wedged behind it. Bounded by a short cap (see\n\t// BROWSER_STOP_LOADING_TIMEOUT_MS) rather than the full per-call timeout,\n\t// so a wedged renderer cannot add up to the whole timeout to the failure\n\t// path; any failure here is swallowed so it never masks the original\n\t// navigate error.\n\tasync #stopLoading(timeout: number): Promise<void> {\n\t\ttry {\n\t\t\tawait this.#client.send('Page.stopLoading', undefined, this.#sessionId, timeout)\n\t\t} catch {\n\t\t\t// Best-effort only — the original navigate error is what the caller receives\n\t\t}\n\t}\n\n\tasync #evaluate(expression: string): Promise<unknown> {\n\t\tconst result: unknown = await this.#client.send(\n\t\t\t'Runtime.evaluate',\n\t\t\t{\n\t\t\t\texpression,\n\t\t\t\treturnByValue: true,\n\t\t\t\tawaitPromise: true,\n\t\t\t},\n\t\t\tthis.#sessionId,\n\t\t)\n\n\t\tif (!isRecord(result)) return undefined\n\n\t\t// Check for exceptions\n\t\tif (isRecord(result['exceptionDetails'])) {\n\t\t\tconst details = result['exceptionDetails']\n\t\t\tif (isRecord(details['exception']) && isString(details['exception']['description'])) {\n\t\t\t\tconst description = details['exception']['description']\n\t\t\t\tconst limitMatch = BROWSER_RESULT_LIMIT_PATTERN.exec(description)\n\t\t\t\tif (limitMatch !== null) {\n\t\t\t\t\tthrow new BrowserResultLimitError('Evaluation result exceeds BROWSER_RESULT_LIMIT', {\n\t\t\t\t\t\tlength: Number(limitMatch[1]),\n\t\t\t\t\t\tlimit: BROWSER_RESULT_LIMIT,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tthrow new BrowserError(description)\n\t\t\t}\n\t\t\tthrow new BrowserError('JavaScript evaluation failed')\n\t\t}\n\n\t\t// Extract the value from the result\n\t\tconst remoteObject = result['result']\n\t\tif (!isRecord(remoteObject)) return undefined\n\n\t\tif ('value' in remoteObject) {\n\t\t\treturn remoteObject['value']\n\t\t}\n\n\t\treturn undefined\n\t}\n\n\tasync #waitForSelector(selector: string, timeout: number): Promise<void> {\n\t\tconst deadline = Date.now() + timeout\n\t\tconst selectorJson = JSON.stringify(selector)\n\n\t\twhile (Date.now() < deadline) {\n\t\t\tconst found = await this.#evaluate(`document.querySelector(${selectorJson}) !== null`)\n\t\t\tif (found === true) return\n\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, BROWSER_WAIT_POLL_INTERVAL_MS))\n\t\t}\n\n\t\tthrow new BrowserSelectorError(`Timeout waiting for selector: ${selector}`)\n\t}\n\n\t#waitForLoadEvent(\n\t\tcondition: string,\n\t\ttimeout: number,\n\t): { promise: Promise<void>; cancel: () => void } {\n\t\tconst eventName =\n\t\t\tcondition === 'domcontentloaded' ? 'Page.domContentEventFired' : 'Page.loadEventFired'\n\n\t\tlet settled = false\n\t\tlet timer: ReturnType<typeof setTimeout>\n\t\tlet handler: () => void\n\n\t\tconst promise = new Promise<void>((resolve, reject) => {\n\t\t\ttimer = setTimeout(() => {\n\t\t\t\tif (settled) return\n\t\t\t\tsettled = true\n\t\t\t\tthis.#client.unsubscribe(eventName, handler, this.#sessionId)\n\t\t\t\treject(new BrowserError(`Navigation timeout after ${timeout}ms`))\n\t\t\t}, timeout)\n\n\t\t\thandler = (): void => {\n\t\t\t\tif (settled) return\n\t\t\t\tsettled = true\n\t\t\t\tclearTimeout(timer)\n\t\t\t\tthis.#client.unsubscribe(eventName, handler, this.#sessionId)\n\t\t\t\tresolve()\n\t\t\t}\n\n\t\t\tthis.#client.subscribe(eventName, handler, this.#sessionId)\n\t\t})\n\n\t\tconst cancel = (): void => {\n\t\t\tif (settled) return\n\t\t\tsettled = true\n\t\t\tclearTimeout(timer)\n\t\t\tthis.#client.unsubscribe(eventName, handler, this.#sessionId)\n\t\t}\n\n\t\treturn { promise, cancel }\n\t}\n}\n","import type {\n\tBrowserContextInterface,\n\tBrowserPageInterface,\n\tBrowserPageOptions,\n\tBrowserViewport,\n\tCDPClientInterface,\n\tCDPTarget,\n\tScreenshotWriterInterface,\n} from './types.js'\nimport { BrowserPage } from './BrowserPage.js'\nimport { isRecord, isString } from '@orkestrel/contract'\n\n// === BrowserContext\n\nexport class BrowserContext implements BrowserContextInterface {\n\t#client: CDPClientInterface\n\t#id: string | undefined\n\t#viewport: BrowserViewport | undefined\n\t#writer: ScreenshotWriterInterface | undefined\n\t#pages: Map<string, BrowserPage> = new Map()\n\n\tconstructor(\n\t\tclient: CDPClientInterface,\n\t\tid?: string,\n\t\tviewport?: BrowserViewport,\n\t\twriter?: ScreenshotWriterInterface,\n\t) {\n\t\tthis.#client = client\n\t\tthis.#id = id\n\t\tthis.#viewport = viewport\n\t\tthis.#writer = writer\n\t}\n\n\t// === Property accessors\n\n\tget id(): string | undefined {\n\t\treturn this.#id\n\t}\n\n\t// === Public API\n\n\tpage(index?: number): BrowserPageInterface | undefined {\n\t\tconst i = index ?? 0\n\t\tconst values = [...this.#pages.values()]\n\t\treturn i >= 0 && i < values.length ? values[i] : undefined\n\t}\n\n\tpages(): readonly BrowserPageInterface[] {\n\t\treturn [...this.#pages.values()]\n\t}\n\n\tasync create(options?: BrowserPageOptions): Promise<BrowserPageInterface> {\n\t\t// Build createTarget params — include browserContextId if we have a real CDP context\n\t\tconst createParams: Record<string, unknown> = { url: 'about:blank' }\n\t\tif (this.#id !== undefined) {\n\t\t\tcreateParams['browserContextId'] = this.#id\n\t\t}\n\n\t\tconst result: unknown = await this.#client.send('Target.createTarget', createParams)\n\n\t\tif (!isRecord(result) || !isString(result['targetId'])) {\n\t\t\tthrow new Error('Failed to create new browser target')\n\t\t}\n\n\t\tconst targetId = result['targetId']\n\n\t\t// Attach to the target to get a session\n\t\tconst attachResult: unknown = await this.#client.send('Target.attachToTarget', {\n\t\t\ttargetId,\n\t\t\tflatten: true,\n\t\t})\n\n\t\tif (!isRecord(attachResult) || !isString(attachResult['sessionId'])) {\n\t\t\tthrow new Error('Failed to attach to browser target')\n\t\t}\n\n\t\tconst sessionId = attachResult['sessionId']\n\n\t\t// Enable required CDP domains on the session\n\t\tawait this.#client.send('Page.enable', undefined, sessionId)\n\t\tawait this.#client.send('Runtime.enable', undefined, sessionId)\n\n\t\t// Apply viewport (from page options, context default, or neither)\n\t\tconst viewport = options?.viewport ?? this.#viewport\n\t\tif (viewport !== undefined) {\n\t\t\tawait this.#client.send(\n\t\t\t\t'Emulation.setDeviceMetricsOverride',\n\t\t\t\t{\n\t\t\t\t\twidth: viewport.width,\n\t\t\t\t\theight: viewport.height,\n\t\t\t\t\tdeviceScaleFactor: 1,\n\t\t\t\t\tmobile: false,\n\t\t\t\t},\n\t\t\t\tsessionId,\n\t\t\t)\n\t\t}\n\n\t\tconst page = new BrowserPage(this.#client, targetId, sessionId, this.#writer, options?.url)\n\n\t\t// Navigate if url was provided\n\t\tif (options?.url !== undefined && options.url !== 'about:blank') {\n\t\t\tawait page.navigate(options.url, { timeout: options.timeout })\n\t\t}\n\n\t\tthis.#pages.set(targetId, page)\n\t\treturn page\n\t}\n\n\tasync sync(targets: readonly CDPTarget[]): Promise<void> {\n\t\tconst pageTargets = targets.filter((t) => t.type === 'page')\n\t\tconst targetIds = new Set(pageTargets.map((t) => t.id))\n\n\t\t// Close and drop pages whose targets are no longer present\n\t\tfor (const [id, page] of this.#pages) {\n\t\t\tif (targetIds.has(id)) continue\n\t\t\ttry {\n\t\t\t\tawait page.close()\n\t\t\t} catch {\n\t\t\t\t// Swallow errors during teardown of a removed target\n\t\t\t}\n\t\t\tthis.#pages.delete(id)\n\t\t}\n\n\t\t// Attach only to targets we don't already have a page for\n\t\tfor (const target of pageTargets) {\n\t\t\tif (this.#pages.has(target.id)) continue\n\n\t\t\ttry {\n\t\t\t\t// Attach to existing target\n\t\t\t\tconst attachResult: unknown = await this.#client.send('Target.attachToTarget', {\n\t\t\t\t\ttargetId: target.id,\n\t\t\t\t\tflatten: true,\n\t\t\t\t})\n\n\t\t\t\tif (!isRecord(attachResult) || !isString(attachResult['sessionId'])) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tconst sessionId = attachResult['sessionId']\n\n\t\t\t\t// Enable required CDP domains\n\t\t\t\tawait this.#client.send('Page.enable', undefined, sessionId)\n\t\t\t\tawait this.#client.send('Runtime.enable', undefined, sessionId)\n\n\t\t\t\t// Apply viewport if configured\n\t\t\t\tif (this.#viewport !== undefined) {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tawait this.#client.send(\n\t\t\t\t\t\t\t'Emulation.setDeviceMetricsOverride',\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\twidth: this.#viewport.width,\n\t\t\t\t\t\t\t\theight: this.#viewport.height,\n\t\t\t\t\t\t\t\tdeviceScaleFactor: 1,\n\t\t\t\t\t\t\t\tmobile: false,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\tsessionId,\n\t\t\t\t\t\t)\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// Non-fatal — viewport may not be supported on this target\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tthis.#pages.set(\n\t\t\t\t\ttarget.id,\n\t\t\t\t\tnew BrowserPage(this.#client, target.id, sessionId, this.#writer, target.url),\n\t\t\t\t)\n\t\t\t} catch {\n\t\t\t\t// Skip targets we cannot attach to\n\t\t\t}\n\t\t}\n\t}\n\n\tasync close(): Promise<void> {\n\t\tfor (const page of this.#pages.values()) {\n\t\t\ttry {\n\t\t\t\tawait page.close()\n\t\t\t} catch {\n\t\t\t\t// Swallow errors during teardown\n\t\t\t}\n\t\t}\n\t\tthis.#pages.clear()\n\n\t\t// Dispose real CDP browser context if we have one\n\t\tif (this.#id !== undefined) {\n\t\t\ttry {\n\t\t\t\tawait this.#client.send('Target.disposeBrowserContext', {\n\t\t\t\t\tbrowserContextId: this.#id,\n\t\t\t\t})\n\t\t\t} catch {\n\t\t\t\t// Context may already be disposed\n\t\t\t}\n\t\t}\n\t}\n}\n","import type { CDPClientInterface, CDPClientOptions } from './types.js'\nimport { CDPClient } from './CDPClient.js'\n\n/**\n * Create a CDP client bound to the given transport.\n *\n * @param options - The transport (and optional timeout) the client uses\n * @returns A {@link CDPClientInterface}\n *\n * @example\n * ```ts\n * import { createCDPClient } from '@src/core'\n *\n * const client = createCDPClient({ transport })\n * await client.connect()\n * ```\n */\nexport function createCDPClient(options: CDPClientOptions): CDPClientInterface {\n\treturn new CDPClient(options)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["#transport","#pending","#subscriptions","#sessionSubscriptions","#timeout","#connected","#closing","#connecting","#closeRequested","#start","#nextId","#stop","#wired","#onMessage","#onClose","#onError","#active","#messageId","#dispatch","#client","#sessionId","#emitter","#started","#destroyed","#stopping","#starting","#begin","#end","#actions","#shutdown","#destroy","#onBindingCalled","#onFrameNavigated","#unsubscribe","#client","#targetId","#sessionId","#writer","#closed","#release","#resolveLoad","#url","#destroyHandler","#assertOpen","#evaluate","#requireString","#navigation","#navigate","#waitForSelector","#flattenFrameTree","#codegen","#codegenStart","#startCodegen","#closing","#destroy","#close","#waitForLoadEvent","#cancelLoad","#stopLoading","#releasing","#releaseResources","#loadEvent","#loadResolve","#loadReject","#loadTimer","#rejectLoad","#loadHandler","#clearLoad","#client","#id","#viewport","#writer","#pages","#creating","#closed","#create","#syncing","#sync","#shutdown","#destroyResources","#closeResources","#attach","#closeTarget","#reattach","#openSession","#enableSession","#applyViewport","#detachSession","#tryViewport","#settle"],"sources":["../../../src/core/constants.ts","../../../src/core/errors.ts","../../../src/core/helpers.ts","../../../src/core/CDPClient.ts","../../../src/core/BrowserCodegen.ts","../../../src/core/BrowserPage.ts","../../../src/core/BrowserContext.ts","../../../src/core/factories.ts"],"sourcesContent":["// === Base64\n\n/** Base64 alphabet, index-ordered, used to build {@link BASE64_LOOKUP}. */\nexport const BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'\n\n/** Base64 character to 6-bit value lookup table, derived from {@link BASE64_CHARS}. */\nexport const BASE64_LOOKUP: Readonly<Record<string, number>> = Object.freeze(\n\tObject.fromEntries(BASE64_CHARS.split('').map((char, index) => [char, index])),\n)\n\n// === Browser\n\n/** Default timeout in milliseconds for browser connection, requests, and navigation. */\nexport const BROWSER_DEFAULT_TIMEOUT_MS = 30_000\n\n/**\n * Maximum serialized-character length for an `evaluate()`/`content()` result,\n * enforced IN-PAGE before the result is returned to CDP.\n *\n * @remarks\n * This counts UTF-16 STRING LENGTH (`String#length`), not transport BYTES —\n * the actual CDP frame is UTF-8 encoded (up to 3 bytes/char for common\n * multibyte content) and carries additional JSON/CDP framing overhead on top\n * of the raw content. A `Runtime.evaluate` result above this size, once\n * framed as a CDP JSON response, overflows the native WebSocket inbound frame\n * limit and closes the whole CDP connection — a page-level failure with no\n * clean error. The limit is set well under the observed ~3-4MB transport\n * ceiling (rather than at it) to leave headroom for the length-vs-bytes gap\n * and framing overhead. The cap is enforced by stringifying the candidate\n * result in-page and throwing a recognizable sentinel error (see\n * {@link BROWSER_RESULT_LIMIT_SENTINEL_PREFIX}) before the oversized frame is\n * ever produced, so the caller gets a coded error instead of a dropped\n * connection.\n */\nexport const BROWSER_RESULT_LIMIT = 2_500_000\n\n/**\n * Distinctive prefix for the in-page result-limit sentinel error, immediately\n * followed by the serialized length.\n *\n * @remarks\n * Deliberately unlikely to appear in a page's own thrown error text (unlike\n * a plain `BROWSER_RESULT_LIMIT: ` label), so {@link BROWSER_RESULT_LIMIT_PATTERN}\n * can distinguish the guard's own throw from a page error that merely\n * mentions similar text.\n */\nexport const BROWSER_RESULT_LIMIT_SENTINEL_PREFIX = '[[ORKESTREL_BROWSER_RESULT_LIMIT]]'\n\n/**\n * Matches the in-page result-limit sentinel error message, anchored\n * immediately after the `Error: ` (optionally `Uncaught Error: `) prefix that\n * Chromium prepends to a thrown error's description — so the guard's own\n * throw is recognized only at the message START, not wherever the substring\n * happens to occur.\n */\nexport const BROWSER_RESULT_LIMIT_PATTERN = new RegExp(\n\t`^(?:Uncaught )?Error: \\\\[\\\\[ORKESTREL_BROWSER_RESULT_LIMIT\\\\]\\\\](\\\\d+)`,\n)\n\n/** Poll interval in milliseconds while waiting for a selector to appear. */\nexport const BROWSER_WAIT_POLL_INTERVAL_MS = 100\n\n/**\n * Bound (in milliseconds) on the best-effort `Page.stopLoading` call issued\n * after a failed `navigate()`.\n *\n * @remarks\n * A wedged renderer can make the underlying CDP call hang for the full\n * per-call timeout; capping it to a small fixed bound keeps a navigate\n * failure's total latency close to the original timeout instead of doubling\n * it. Best-effort only — never masks the original navigate error.\n */\nexport const BROWSER_STOP_LOADING_TIMEOUT_MS = 1_000\n\n/** Default viewport width in pixels. */\nexport const BROWSER_DEFAULT_VIEWPORT_WIDTH = 1280\n\n/** Default viewport height in pixels. */\nexport const BROWSER_DEFAULT_VIEWPORT_HEIGHT = 720\n\n// === Browser codegen\n\n/** Name of the CDP runtime binding the codegen recorder script calls into. */\nexport const BROWSER_CODEGEN_BINDING_NAME = '__orkestrelBrowserCodegen'\n\n/**\n * In-page recorder script injected via `Page.addScriptToEvaluateOnNewDocument`\n * and `Runtime.evaluate`.\n *\n * @remarks\n * Attaches capturing-phase listeners for `click`, `input` (fill), and\n * `change` (select) on `document`, builds a stable CSS selector for the\n * target element, and forwards each action to the CDP binding\n * ({@link BROWSER_CODEGEN_BINDING_NAME}) as a JSON string payload. Guarded to\n * install exactly once per document (`window[name]` sentinel) so repeated\n * injection on every new document is idempotent.\n */\nexport const BROWSER_CODEGEN_SOURCE = `(() => {\n\tconst bindingName = ${JSON.stringify(BROWSER_CODEGEN_BINDING_NAME)}\n\tif (window[bindingName + '__installed']) return\n\twindow[bindingName + '__installed'] = true\n\n\tconst selectorFor = (el) => {\n\t\tif (el.id) return '#' + CSS.escape(el.id)\n\t\tconst parts = []\n\t\tlet node = el\n\t\twhile (node && node.nodeType === 1 && parts.length < 8) {\n\t\t\tlet part = node.tagName.toLowerCase()\n\t\t\tif (node.classList && node.classList.length > 0) {\n\t\t\t\tpart += '.' + Array.from(node.classList).map((cls) => CSS.escape(cls)).join('.')\n\t\t\t}\n\t\t\tconst parent = node.parentElement\n\t\t\tif (parent) {\n\t\t\t\tconst siblings = Array.from(parent.children).filter((c) => c.tagName === node.tagName)\n\t\t\t\tif (siblings.length > 1) {\n\t\t\t\t\tpart += ':nth-of-type(' + (siblings.indexOf(node) + 1) + ')'\n\t\t\t\t}\n\t\t\t}\n\t\t\tparts.unshift(part)\n\t\t\tnode = parent\n\t\t}\n\t\treturn parts.join(' > ')\n\t}\n\n\tconst send = (payload) => {\n\t\tif (typeof window[bindingName] === 'function') {\n\t\t\twindow[bindingName](JSON.stringify(payload))\n\t\t}\n\t}\n\n\tdocument.addEventListener(\n\t\t'click',\n\t\t(event) => {\n\t\t\tconst target = event.target\n\t\t\tif (!target || target.nodeType !== 1) return\n\t\t\tsend({ action: 'click', selector: selectorFor(target) })\n\t\t},\n\t\ttrue,\n\t)\n\n\tconst fillableTypes = new Set([\n\t\t'text',\n\t\t'search',\n\t\t'url',\n\t\t'tel',\n\t\t'email',\n\t\t'password',\n\t\t'number',\n\t])\n\n\tdocument.addEventListener(\n\t\t'input',\n\t\t(event) => {\n\t\t\tconst target = event.target\n\t\t\tif (!target || target.nodeType !== 1) return\n\n\t\t\tif (target.isContentEditable) {\n\t\t\t\tsend({ action: 'fill', selector: selectorFor(target), value: target.textContent || '' })\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif (typeof target.value !== 'string') return\n\t\t\tconst tag = target.tagName\n\t\t\tif (tag === 'TEXTAREA') {\n\t\t\t\tsend({ action: 'fill', selector: selectorFor(target), value: target.value })\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif (tag === 'INPUT' && fillableTypes.has((target.type || 'text').toLowerCase())) {\n\t\t\t\tsend({ action: 'fill', selector: selectorFor(target), value: target.value })\n\t\t\t}\n\t\t},\n\t\ttrue,\n\t)\n\n\tdocument.addEventListener(\n\t\t'change',\n\t\t(event) => {\n\t\t\tconst target = event.target\n\t\t\tif (!target || target.tagName !== 'SELECT') return\n\t\t\tconst values = Array.from(target.selectedOptions || []).map((option) => option.value)\n\t\t\tsend({ action: 'select', selector: selectorFor(target), values })\n\t\t},\n\t\ttrue,\n\t)\n})()`\n","// === Browser errors\n\n/**\n * Base error for all browser automation operations.\n *\n * @remarks\n * Carries a machine-readable `code` and optional `context` (AGENTS §12) so\n * callers can branch in a `catch` without parsing message strings.\n */\nexport class BrowserError extends Error {\n\treadonly code: string\n\treadonly context: Readonly<Record<string, unknown>> | undefined\n\n\tconstructor(\n\t\tmessage: string,\n\t\tcode = 'BROWSER_ERROR',\n\t\tcontext?: Readonly<Record<string, unknown>>,\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'BrowserError'\n\t\tthis.code = code\n\t\tthis.context = context\n\t}\n}\n\n/**\n * A selector-based lookup or wait timed out without the element appearing.\n */\nexport class BrowserSelectorError extends BrowserError {\n\tconstructor(message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message, 'BROWSER_SELECTOR_ERROR', context)\n\t\tthis.name = 'BrowserSelectorError'\n\t}\n}\n\n/**\n * A CDP request received an error response from the remote endpoint.\n *\n * @remarks\n * Carries the originating `method` plus the CDP error's own `code`,\n * `message`, and `data` (when present) in `context`, so callers can branch\n * on the protocol-level error instead of parsing the message string.\n */\nexport class CDPError extends BrowserError {\n\tconstructor(message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message, 'BROWSER_CDP_ERROR', context)\n\t\tthis.name = 'CDPError'\n\t}\n}\n\n/**\n * A CDP request could not be sent or completed because the client was not in\n * a connectable state (not connected, closed while connecting, or the\n * connection dropped mid-request).\n */\nexport class CDPConnectionError extends BrowserError {\n\tconstructor(message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message, 'BROWSER_CDP_CONNECTION_ERROR', context)\n\t\tthis.name = 'CDPConnectionError'\n\t}\n}\n\n/**\n * A pending CDP request was not answered within its timeout window.\n */\nexport class CDPTimeoutError extends BrowserError {\n\tconstructor(message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message, 'BROWSER_CDP_TIMEOUT_ERROR', context)\n\t\tthis.name = 'CDPTimeoutError'\n\t}\n}\n\n/**\n * An `evaluate()`/`content()` result exceeded {@link BROWSER_RESULT_LIMIT}\n * and was rejected in-page before it could overflow the CDP transport frame.\n */\nexport class BrowserResultLimitError extends BrowserError {\n\tconstructor(message: string, context?: Readonly<Record<string, unknown>>) {\n\t\tsuper(message, 'BROWSER_RESULT_LIMIT_ERROR', context)\n\t\tthis.name = 'BrowserResultLimitError'\n\t}\n}\n\n// === Browser type guards\n\n/**\n * Narrow an unknown value to BrowserError.\n *\n * @param value - Value to check\n * @returns True when value is a BrowserError instance\n */\nexport function isBrowserError(value: unknown): value is BrowserError {\n\treturn value instanceof BrowserError\n}\n\n/**\n * Narrow an unknown value to BrowserSelectorError.\n *\n * @param value - Value to check\n * @returns True when value is a BrowserSelectorError instance\n */\nexport function isBrowserSelectorError(value: unknown): value is BrowserSelectorError {\n\treturn value instanceof BrowserSelectorError\n}\n\n/**\n * Narrow an unknown value to CDPError.\n *\n * @param value - Value to check\n * @returns True when value is a CDPError instance\n */\nexport function isCDPError(value: unknown): value is CDPError {\n\treturn value instanceof CDPError\n}\n\n/**\n * Narrow an unknown value to CDPConnectionError.\n *\n * @param value - Value to check\n * @returns True when value is a CDPConnectionError instance\n */\nexport function isCDPConnectionError(value: unknown): value is CDPConnectionError {\n\treturn value instanceof CDPConnectionError\n}\n\n/**\n * Narrow an unknown value to CDPTimeoutError.\n *\n * @param value - Value to check\n * @returns True when value is a CDPTimeoutError instance\n */\nexport function isCDPTimeoutError(value: unknown): value is CDPTimeoutError {\n\treturn value instanceof CDPTimeoutError\n}\n\n/**\n * Narrow an unknown value to BrowserResultLimitError.\n *\n * @param value - Value to check\n * @returns True when value is a BrowserResultLimitError instance\n */\nexport function isBrowserResultLimitError(value: unknown): value is BrowserResultLimitError {\n\treturn value instanceof BrowserResultLimitError\n}\n","import type { BrowserCodegenAction, BrowserCodegenScriptOptions } from './types.js'\nimport { isRecord, isString } from '@orkestrel/contract'\nimport { BASE64_LOOKUP, BROWSER_RESULT_LIMIT_SENTINEL_PREFIX } from './constants.js'\n\n/**\n * Decode a base64-encoded string into raw bytes.\n *\n * @remarks\n * Pure JS implementation — no `Buffer`, no `atob` (DOM-only) — so it runs\n * identically in Node and browser environments. Whitespace and `=` padding\n * are ignored; invalid characters are skipped.\n *\n * @param text - Base64-encoded input string\n * @returns Decoded bytes\n *\n * @example\n * ```ts\n * decodeBase64('aGVsbG8=') // Uint8Array [104, 101, 108, 108, 111]\n * ```\n */\nexport function decodeBase64(text: string): Uint8Array {\n\tconst clean = text.replace(/[^A-Za-z0-9+/]/g, '')\n\tconst bytes: number[] = []\n\n\tlet buffer = 0\n\tlet bits = 0\n\n\tfor (const char of clean) {\n\t\tconst value = BASE64_LOOKUP[char]\n\t\tif (value === undefined) continue\n\n\t\tbuffer = (buffer << 6) | value\n\t\tbits += 6\n\n\t\tif (bits >= 8) {\n\t\t\tbits -= 8\n\t\t\tbytes.push((buffer >> bits) & 0xff)\n\t\t}\n\t}\n\n\treturn new Uint8Array(bytes)\n}\n\n/**\n * Wrap a `Runtime.evaluate` expression so the IN-PAGE code stringifies its\n * own result and throws a recognizable error before an oversized result\n * would overflow the CDP transport frame.\n *\n * @remarks\n * A result whose `JSON.stringify` length exceeds `limit` throws\n * `Error('BROWSER_RESULT_LIMIT: <length>')` inside the page instead of being\n * returned — the caller maps that sentinel to a coded\n * {@link BrowserResultLimitError}. A non-serializable result (`undefined`,\n * a function, a symbol) makes `JSON.stringify` return `undefined`, so the\n * length check is skipped and today's undefined-passthrough behavior is\n * unchanged.\n *\n * The expression is placed on its own line inside the wrapper (rather than\n * inline with the guard code) so a trailing `//` line comment in the\n * expression cannot swallow the closing guard syntax that follows it.\n *\n * @param expression - The candidate JavaScript expression to evaluate\n * @param limit - Maximum serialized-character length (see {@link BROWSER_RESULT_LIMIT})\n * @returns The wrapped, guarded expression\n */\nexport function guardEvaluateExpression(expression: string, limit: number): string {\n\treturn `(() => { const r = (\n${expression}\n); const s = JSON.stringify(r); if (typeof s === 'string' && s.length > ${limit}) throw new Error(${JSON.stringify(BROWSER_RESULT_LIMIT_SENTINEL_PREFIX)} + s.length); return r })()`\n}\n\n/**\n * Normalize a raw list of recorded codegen actions.\n *\n * @remarks\n * Collapses consecutive `fill` actions on the same selector into the latest\n * value (a text input fires one `input` event per keystroke) so the\n * compiled script reflects the final typed value rather than every\n * intermediate keystroke.\n *\n * @param actions - Raw recorded actions, in capture order\n * @returns Normalized actions, in the same order\n */\nexport function normalizeCodegenActions(\n\tactions: readonly BrowserCodegenAction[],\n): readonly BrowserCodegenAction[] {\n\tconst result: BrowserCodegenAction[] = []\n\n\tfor (const action of actions) {\n\t\tconst previous = result[result.length - 1]\n\t\tif (\n\t\t\tprevious !== undefined &&\n\t\t\tprevious.action === 'fill' &&\n\t\t\taction.action === 'fill' &&\n\t\t\tprevious.selector === action.selector\n\t\t) {\n\t\t\tresult[result.length - 1] = action\n\t\t\tcontinue\n\t\t}\n\t\tresult.push(action)\n\t}\n\n\treturn result\n}\n\n/**\n * Parse a codegen binding payload into a typed action.\n *\n * @remarks\n * The in-page recorder script calls the CDP binding with a JSON string\n * payload shaped like a {@link BrowserCodegenAction}. Returns `undefined`\n * when the payload is not valid JSON or does not match a known action shape.\n *\n * @param payload - Raw binding call payload (the CDP `payload` param)\n * @returns The parsed action, or `undefined` when the payload is malformed\n */\nexport function parseCodegenActionPayload(payload: unknown): BrowserCodegenAction | undefined {\n\tif (!isString(payload)) return undefined\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(payload)\n\t} catch {\n\t\treturn undefined\n\t}\n\n\tif (!isRecord(parsed)) return undefined\n\n\tconst action = parsed['action']\n\tconst selector = parsed['selector']\n\n\tif (action === 'click' && isString(selector)) {\n\t\treturn { action: 'click', selector }\n\t}\n\n\tif (action === 'fill' && isString(selector) && isString(parsed['value'])) {\n\t\treturn { action: 'fill', selector, value: parsed['value'] }\n\t}\n\n\tif (action === 'select' && isString(selector) && Array.isArray(parsed['values'])) {\n\t\tconst values = parsed['values'].filter(isString)\n\t\tif (values.length === parsed['values'].length) {\n\t\t\treturn { action: 'select', selector, values }\n\t\t}\n\t}\n\n\treturn undefined\n}\n\n/**\n * Derive a `navigate` codegen action from a `Page.frameNavigated` CDP event.\n *\n * @remarks\n * Only the top-level (main) frame's navigation is recorded — a frame\n * carrying a `parentId` is a sub-frame and is ignored.\n *\n * @param params - The CDP `Page.frameNavigated` event params\n * @returns A `navigate` action, or `undefined` when the event is not a\n * top-level navigation with a resolvable URL\n */\nexport function readCodegenNavigateAction(\n\tparams: Readonly<Record<string, unknown>>,\n): BrowserCodegenAction | undefined {\n\tconst frame = params['frame']\n\tif (!isRecord(frame)) return undefined\n\tif ('parentId' in frame) return undefined\n\tif (!isString(frame['url'])) return undefined\n\n\treturn { action: 'navigate', url: frame['url'] }\n}\n\n/**\n * Compile recorded codegen actions into a replayable script.\n *\n * @remarks\n * Emits one statement per action against a `page` object shaped like\n * {@link BrowserPageInterface} (`page.navigate(...)`, `page.click(...)`, …).\n * Both target languages emit an `async function run(page)` body whose\n * statements are `await`-ed; `language` only toggles whether the `page`\n * parameter carries a TypeScript type annotation (default `'javascript'`).\n *\n * @param actions - Normalized actions to compile\n * @param options - Compilation options (target language)\n * @returns The compiled script source\n */\nexport function compileCodegenScript(\n\tactions: readonly BrowserCodegenAction[],\n\toptions?: BrowserCodegenScriptOptions,\n): string {\n\tconst language = options?.language ?? 'javascript'\n\n\tconst lines = actions.map((action) => {\n\t\tswitch (action.action) {\n\t\t\tcase 'navigate':\n\t\t\t\treturn `await page.navigate(${JSON.stringify(action.url)})`\n\t\t\tcase 'click':\n\t\t\t\treturn `await page.click(${JSON.stringify(action.selector)})`\n\t\t\tcase 'fill':\n\t\t\t\treturn `await page.fill(${JSON.stringify(action.selector)}, ${JSON.stringify(action.value)})`\n\t\t\tcase 'select':\n\t\t\t\treturn `await page.select(${JSON.stringify(action.selector)}, ${JSON.stringify(action.values)})`\n\t\t}\n\t})\n\n\tif (language === 'typescript') {\n\t\treturn [\n\t\t\t`async function run(page: import('@orkestrel/browser').BrowserPageInterface): Promise<void> {`,\n\t\t\t...lines.map((line) => `\\t${line}`),\n\t\t\t`}`,\n\t\t].join('\\n')\n\t}\n\n\treturn [`async function run(page) {`, ...lines.map((line) => `\\t${line}`), `}`].join('\\n')\n}\n","import type {\n\tCDPClientInterface,\n\tCDPClientOptions,\n\tCDPHandler,\n\tCDPTransportInterface,\n} from './types.js'\nimport { BROWSER_DEFAULT_TIMEOUT_MS } from './constants.js'\nimport { CDPConnectionError, CDPError, CDPTimeoutError } from './errors.js'\nimport { isRecord, isString } from '@orkestrel/contract'\n\n// === CDPClient\n\n/**\n * Lightweight Chrome DevTools Protocol client over a {@link CDPTransportInterface}.\n *\n * @remarks\n * Sends JSON-RPC messages over the injected transport and dispatches\n * responses and events. The transport owns the connection itself\n * (WebSocket, pipe, or any other duplex channel); this class owns only\n * the JSON-RPC framing and id/session correlation.\n *\n * Supports session-scoped event subscriptions: when a sessionId is\n * provided to subscribe/unsubscribe, the handler only fires for CDP\n * events carrying that sessionId. Global subscriptions (no sessionId)\n * continue to see ALL events for backwards compatibility.\n */\nexport class CDPClient implements CDPClientInterface {\n\treadonly #transport: CDPTransportInterface\n\t#messageId = 0\n\treadonly #pending: Map<\n\t\tnumber,\n\t\t{\n\t\t\tmethod: string\n\t\t\tresolve: (value: unknown) => void\n\t\t\treject: (reason: unknown) => void\n\t\t\ttimer: ReturnType<typeof setTimeout>\n\t\t}\n\t> = new Map()\n\treadonly #subscriptions: Map<string, Set<CDPHandler>> = new Map()\n\treadonly #sessionSubscriptions: Map<string, Map<string, Set<CDPHandler>>> = new Map()\n\t#connected = false\n\t#active = false\n\t#wired = false\n\treadonly #timeout: number\n\t#connecting: Promise<void> | undefined\n\t#closing: Promise<void> | undefined\n\t#closeRequested = false\n\n\tconstructor(options: CDPClientOptions) {\n\t\tthis.#transport = options.transport\n\t\tthis.#timeout = options.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t}\n\n\tget connected(): boolean {\n\t\treturn this.#connected\n\t}\n\n\tasync connect(): Promise<void> {\n\t\tif (this.#closing !== undefined) await this.#closing\n\t\tif (this.#connected) return\n\t\tif (this.#connecting !== undefined) return this.#connecting\n\n\t\tthis.#closeRequested = false\n\t\tconst attempt = this.#start()\n\t\tthis.#connecting = attempt\n\t\ttry {\n\t\t\tawait attempt\n\t\t} finally {\n\t\t\tif (this.#connecting === attempt) this.#connecting = undefined\n\t\t}\n\t}\n\n\t/**\n\t * Close the transport and re-establish a fresh connection.\n\t *\n\t * @remarks\n\t * Client-level subscriptions (`subscribe`/`unsubscribe` registrations)\n\t * survive `close()` and remain active after the reconnect — only pending\n\t * in-flight requests are rejected. This lets a caller reconnect a dropped\n\t * transport without re-registering every handler.\n\t */\n\tasync reconnect(): Promise<void> {\n\t\tawait this.close()\n\t\tawait this.connect()\n\t}\n\n\tasync send(\n\t\tmethod: string,\n\t\tparams?: Readonly<Record<string, unknown>>,\n\t\tsessionId?: string,\n\t\ttimeout?: number,\n\t): Promise<unknown> {\n\t\tif (!this.#connected) {\n\t\t\tthrow new CDPConnectionError('CDP client is not connected', { method })\n\t\t}\n\n\t\tconst id = this.#nextId()\n\t\tconst message: Record<string, unknown> = { id, method }\n\n\t\tif (params !== undefined) {\n\t\t\tmessage['params'] = params\n\t\t}\n\t\tif (sessionId !== undefined) {\n\t\t\tmessage['sessionId'] = sessionId\n\t\t}\n\n\t\tconst serialized = JSON.stringify(message)\n\t\tconst effectiveTimeout = timeout ?? this.#timeout\n\n\t\treturn new Promise<unknown>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.#pending.delete(id)\n\t\t\t\treject(\n\t\t\t\t\tnew CDPTimeoutError(`CDP request timed out: ${method}`, {\n\t\t\t\t\t\tmethod,\n\t\t\t\t\t\ttimeout: effectiveTimeout,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t\t}, effectiveTimeout)\n\n\t\t\tthis.#pending.set(id, { method, resolve, reject, timer })\n\n\t\t\tthis.#transport.send(serialized).catch((thrown: unknown) => {\n\t\t\t\tthis.#pending.delete(id)\n\t\t\t\tclearTimeout(timer)\n\t\t\t\treject(thrown)\n\t\t\t})\n\t\t})\n\t}\n\n\tsubscribe(method: string, handler: CDPHandler, sessionId?: string): void {\n\t\tif (sessionId !== undefined) {\n\t\t\tlet sessionMap = this.#sessionSubscriptions.get(sessionId)\n\t\t\tif (sessionMap === undefined) {\n\t\t\t\tsessionMap = new Map()\n\t\t\t\tthis.#sessionSubscriptions.set(sessionId, sessionMap)\n\t\t\t}\n\t\t\tlet handlers = sessionMap.get(method)\n\t\t\tif (handlers === undefined) {\n\t\t\t\thandlers = new Set()\n\t\t\t\tsessionMap.set(method, handlers)\n\t\t\t}\n\t\t\thandlers.add(handler)\n\t\t} else {\n\t\t\tlet handlers = this.#subscriptions.get(method)\n\t\t\tif (handlers === undefined) {\n\t\t\t\thandlers = new Set()\n\t\t\t\tthis.#subscriptions.set(method, handlers)\n\t\t\t}\n\t\t\thandlers.add(handler)\n\t\t}\n\t}\n\n\tunsubscribe(method: string, handler: CDPHandler, sessionId?: string): void {\n\t\tif (sessionId !== undefined) {\n\t\t\tconst sessionMap = this.#sessionSubscriptions.get(sessionId)\n\t\t\tif (sessionMap !== undefined) {\n\t\t\t\tconst handlers = sessionMap.get(method)\n\t\t\t\tif (handlers !== undefined) {\n\t\t\t\t\thandlers.delete(handler)\n\t\t\t\t\tif (handlers.size === 0) {\n\t\t\t\t\t\tsessionMap.delete(method)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (sessionMap.size === 0) {\n\t\t\t\t\tthis.#sessionSubscriptions.delete(sessionId)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst handlers = this.#subscriptions.get(method)\n\t\t\tif (handlers !== undefined) {\n\t\t\t\thandlers.delete(handler)\n\t\t\t\tif (handlers.size === 0) {\n\t\t\t\t\tthis.#subscriptions.delete(method)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Close the transport and reject all pending requests.\n\t *\n\t * @remarks\n\t * Subscriptions registered via `subscribe()` are client-level\n\t * registrations, not connection-level state — they survive `close()`\n\t * (and a subsequent `reconnect()`/`connect()`) and keep firing once the\n\t * transport is active again. Only pending requests are rejected here.\n\t * If `close()` is called while `connect()` is still awaiting\n\t * `transport.start()`, the in-flight connect is rejected and the\n\t * transport is closed deterministically once `start()` resolves.\n\t */\n\tasync close(): Promise<void> {\n\t\tconst active = this.#closing\n\t\tif (active !== undefined) {\n\t\t\tawait active\n\t\t\treturn\n\t\t}\n\n\t\tconst attempt = this.#stop()\n\t\tthis.#closing = attempt\n\t\ttry {\n\t\t\tawait attempt\n\t\t} finally {\n\t\t\tif (this.#closing === attempt) this.#closing = undefined\n\t\t}\n\t}\n\n\t// === Private helpers\n\n\tasync #start(): Promise<void> {\n\t\tif (!this.#wired) {\n\t\t\tthis.#transport.emitter.on('message', (data) => this.#onMessage(data))\n\t\t\tthis.#transport.emitter.on('close', () => this.#onClose())\n\t\t\tthis.#transport.emitter.on('error', (error) => this.#onError(error))\n\t\t\tthis.#wired = true\n\t\t}\n\n\t\tawait this.#transport.start()\n\t\tthis.#active = true\n\n\t\tif (this.#closeRequested) {\n\t\t\tthis.#closeRequested = false\n\t\t\ttry {\n\t\t\t\tawait this.#transport.close()\n\t\t\t} finally {\n\t\t\t\tthis.#active = false\n\t\t\t}\n\t\t\tthrow new CDPConnectionError('CDP client was closed while connecting', {\n\t\t\t\tmethod: 'connect',\n\t\t\t})\n\t\t}\n\n\t\tthis.#connected = true\n\t}\n\n\tasync #stop(): Promise<void> {\n\t\tif (this.#connecting !== undefined) {\n\t\t\tthis.#closeRequested = true\n\t\t\tawait this.#connecting.catch(() => undefined)\n\t\t\t// The in-flight attempt may have already flipped #connected to\n\t\t\t// true before observing #closeRequested (race between the\n\t\t\t// attempt's final steps and #closeRequested being set here).\n\t\t\t// Fall through to the normal connected-close path instead of\n\t\t\t// returning early, or the close would be silently lost.\n\t\t}\n\n\t\tif (!this.#connected && !this.#active) return\n\n\t\tthis.#connected = false\n\n\t\t// Reject all pending requests\n\t\tfor (const [id, entry] of this.#pending) {\n\t\t\tclearTimeout(entry.timer)\n\t\t\tentry.reject(new CDPConnectionError('CDP connection closed', { method: entry.method }))\n\t\t\tthis.#pending.delete(id)\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.#transport.close()\n\t\t} finally {\n\t\t\tthis.#active = false\n\t\t}\n\t}\n\n\t#nextId(): number {\n\t\tthis.#messageId += 1\n\t\treturn this.#messageId\n\t}\n\n\t#onClose(): void {\n\t\tthis.#connected = false\n\t\tthis.#active = false\n\n\t\t// Reject all pending requests\n\t\tfor (const [id, entry] of this.#pending) {\n\t\t\tclearTimeout(entry.timer)\n\t\t\tentry.reject(new CDPConnectionError('CDP connection closed', { method: entry.method }))\n\t\t\tthis.#pending.delete(id)\n\t\t}\n\t}\n\n\t#onError(error: unknown): void {\n\t\tthis.#connected = false\n\t\tfor (const [id, entry] of this.#pending) {\n\t\t\tclearTimeout(entry.timer)\n\t\t\tentry.reject(\n\t\t\t\tnew CDPConnectionError(`CDP connection failed: ${String(error)}`, {\n\t\t\t\t\tmethod: entry.method,\n\t\t\t\t\terror,\n\t\t\t\t}),\n\t\t\t)\n\t\t\tthis.#pending.delete(id)\n\t\t}\n\t}\n\n\t#onMessage(data: string): void {\n\t\tlet parsed: unknown\n\t\ttry {\n\t\t\tparsed = JSON.parse(data)\n\t\t} catch {\n\t\t\treturn\n\t\t}\n\n\t\tif (!isRecord(parsed)) return\n\n\t\t// Response to a pending request\n\t\tif (typeof parsed['id'] === 'number') {\n\t\t\tconst id = parsed['id']\n\t\t\tconst entry = this.#pending.get(id)\n\t\t\tif (entry !== undefined) {\n\t\t\t\tthis.#pending.delete(id)\n\t\t\t\tclearTimeout(entry.timer)\n\n\t\t\t\tconst errorValue = parsed['error']\n\t\t\t\tif (isRecord(errorValue)) {\n\t\t\t\t\tconst message = isString(errorValue['message']) ? errorValue['message'] : 'CDP error'\n\t\t\t\t\tconst context: Record<string, unknown> = { method: entry.method }\n\t\t\t\t\tif ('code' in errorValue) context['code'] = errorValue['code']\n\t\t\t\t\tcontext['message'] = message\n\t\t\t\t\tif ('data' in errorValue) context['data'] = errorValue['data']\n\t\t\t\t\tentry.reject(new CDPError(message, context))\n\t\t\t\t} else {\n\t\t\t\t\tentry.resolve('result' in parsed ? parsed['result'] : undefined)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\t// CDP event\n\t\tif (isString(parsed['method'])) {\n\t\t\tconst method = parsed['method']\n\t\t\tconst rawParams = parsed['params']\n\t\t\tconst params: Readonly<Record<string, unknown>> = isRecord(rawParams)\n\t\t\t\t? rawParams\n\t\t\t\t: Object.freeze({})\n\n\t\t\t// Fire global handlers (backwards compatible — see ALL events)\n\t\t\tconst globalHandlers = this.#subscriptions.get(method)\n\t\t\tif (globalHandlers !== undefined) {\n\t\t\t\tthis.#dispatch(globalHandlers, params)\n\t\t\t}\n\n\t\t\t// Fire session-scoped handlers\n\t\t\tconst eventSessionId = parsed['sessionId']\n\t\t\tif (isString(eventSessionId)) {\n\t\t\t\tconst sessionMap = this.#sessionSubscriptions.get(eventSessionId)\n\t\t\t\tif (sessionMap !== undefined) {\n\t\t\t\t\tconst sessionHandlers = sessionMap.get(method)\n\t\t\t\t\tif (sessionHandlers !== undefined) {\n\t\t\t\t\t\tthis.#dispatch(sessionHandlers, params)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t#dispatch(handlers: ReadonlySet<CDPHandler>, params: Readonly<Record<string, unknown>>): void {\n\t\tfor (const handler of [...handlers]) {\n\t\t\ttry {\n\t\t\t\thandler(params)\n\t\t\t} catch {\n\t\t\t\t// One observer must not prevent sibling observers from receiving the event.\n\t\t\t}\n\t\t}\n\t}\n}\n","import type {\n\tBrowserCodegenAction,\n\tBrowserCodegenEventMap,\n\tBrowserCodegenInterface,\n\tBrowserCodegenOptions,\n\tBrowserCodegenScriptOptions,\n\tCDPClientInterface,\n} from './types.js'\nimport type { EmitterInterface } from '@orkestrel/emitter'\nimport { BROWSER_CODEGEN_BINDING_NAME, BROWSER_CODEGEN_SOURCE } from './constants.js'\nimport {\n\tcompileCodegenScript,\n\tnormalizeCodegenActions,\n\tparseCodegenActionPayload,\n\treadCodegenNavigateAction,\n} from './helpers.js'\nimport { Emitter } from '@orkestrel/emitter'\n\n// === BrowserCodegen\n\nexport class BrowserCodegen implements BrowserCodegenInterface {\n\treadonly #client: CDPClientInterface\n\treadonly #sessionId: string\n\t#actions: BrowserCodegenAction[] = []\n\t#started = false\n\t#destroyed = false\n\t#starting: Promise<void> | undefined\n\t#stopping: Promise<readonly BrowserCodegenAction[]> | undefined\n\t#shutdown: Promise<void> | undefined\n\n\treadonly #emitter: Emitter<BrowserCodegenEventMap>\n\n\tconstructor(client: CDPClientInterface, sessionId: string, options?: BrowserCodegenOptions) {\n\t\tthis.#client = client\n\t\tthis.#sessionId = sessionId\n\t\tthis.#emitter = new Emitter({ on: options?.on, error: options?.error })\n\t}\n\n\t// === Property accessors\n\n\tget emitter(): EmitterInterface<BrowserCodegenEventMap> {\n\t\treturn this.#emitter\n\t}\n\n\tget started(): boolean {\n\t\treturn this.#started\n\t}\n\n\t// === Public API\n\n\tasync start(): Promise<void> {\n\t\tif (this.#destroyed) return\n\t\tif (this.#stopping !== undefined) await this.#stopping\n\t\tif (this.#destroyed) return\n\t\tif (this.#started) return\n\t\tconst active = this.#starting\n\t\tif (active !== undefined) {\n\t\t\tawait active\n\t\t\treturn\n\t\t}\n\n\t\tconst attempt = this.#begin()\n\t\tthis.#starting = attempt\n\t\ttry {\n\t\t\tawait attempt\n\t\t} finally {\n\t\t\tif (this.#starting === attempt) this.#starting = undefined\n\t\t}\n\t}\n\n\tasync stop(): Promise<readonly BrowserCodegenAction[]> {\n\t\tawait this.#starting?.catch(() => undefined)\n\t\tif (!this.#started) return this.actions()\n\t\tconst active = this.#stopping\n\t\tif (active !== undefined) return await active\n\n\t\tconst attempt = this.#end()\n\t\tthis.#stopping = attempt\n\t\ttry {\n\t\t\treturn await attempt\n\t\t} finally {\n\t\t\tif (this.#stopping === attempt) this.#stopping = undefined\n\t\t}\n\t}\n\n\tactions(): readonly BrowserCodegenAction[] {\n\t\treturn normalizeCodegenActions(this.#actions)\n\t}\n\n\tscript(options?: BrowserCodegenScriptOptions): string {\n\t\treturn compileCodegenScript(this.actions(), options)\n\t}\n\n\tclear(): void {\n\t\tthis.#actions = []\n\t\tthis.#emitter.emit('clear')\n\t}\n\n\tdestroy(): Promise<void> {\n\t\tconst active = this.#shutdown\n\t\tif (active !== undefined) return active\n\t\tif (this.#destroyed) return Promise.resolve()\n\n\t\tthis.#destroyed = true\n\t\tconst shutdown = this.#destroy()\n\t\tthis.#shutdown = shutdown\n\t\treturn shutdown\n\t}\n\n\t// === Private helpers\n\n\tasync #begin(): Promise<void> {\n\t\tthis.#client.subscribe('Runtime.bindingCalled', this.#onBindingCalled, this.#sessionId)\n\t\tthis.#client.subscribe('Page.frameNavigated', this.#onFrameNavigated, this.#sessionId)\n\n\t\ttry {\n\t\t\tawait this.#client.send('Runtime.enable', undefined, this.#sessionId)\n\t\t\tawait this.#client.send(\n\t\t\t\t'Runtime.addBinding',\n\t\t\t\t{ name: BROWSER_CODEGEN_BINDING_NAME },\n\t\t\t\tthis.#sessionId,\n\t\t\t)\n\t\t\tawait this.#client.send(\n\t\t\t\t'Page.addScriptToEvaluateOnNewDocument',\n\t\t\t\t{ source: BROWSER_CODEGEN_SOURCE },\n\t\t\t\tthis.#sessionId,\n\t\t\t)\n\t\t\tawait this.#client.send(\n\t\t\t\t'Runtime.evaluate',\n\t\t\t\t{ expression: BROWSER_CODEGEN_SOURCE, awaitPromise: true },\n\t\t\t\tthis.#sessionId,\n\t\t\t)\n\t\t\tif (this.#destroyed) {\n\t\t\t\tthis.#unsubscribe()\n\t\t\t\treturn\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tthis.#unsubscribe()\n\t\t\tthrow error\n\t\t}\n\n\t\tthis.#started = true\n\t\tthis.#emitter.emit('start')\n\t}\n\n\tasync #end(): Promise<readonly BrowserCodegenAction[]> {\n\t\tthis.#unsubscribe()\n\t\tthis.#started = false\n\n\t\tconst snapshot = this.actions()\n\t\tthis.#emitter.emit('stop', snapshot)\n\t\treturn snapshot\n\t}\n\n\tasync #destroy(): Promise<void> {\n\t\ttry {\n\t\t\tawait this.stop()\n\t\t} catch {\n\t\t\t// Swallow teardown errors — the underlying session may already be gone\n\t\t}\n\n\t\tthis.#actions = []\n\t\tthis.#emitter.destroy()\n\t}\n\n\t#onBindingCalled = (params: Readonly<Record<string, unknown>>): void => {\n\t\tif (params['name'] !== BROWSER_CODEGEN_BINDING_NAME) return\n\n\t\tconst action = parseCodegenActionPayload(params['payload'])\n\t\tif (action === undefined) return\n\n\t\tthis.#actions.push(action)\n\t\tthis.#emitter.emit('action', action)\n\t}\n\n\t#onFrameNavigated = (params: Readonly<Record<string, unknown>>): void => {\n\t\tconst action = readCodegenNavigateAction(params)\n\t\tif (action === undefined) return\n\n\t\tthis.#actions.push(action)\n\t\tthis.#emitter.emit('action', action)\n\t}\n\n\t#unsubscribe(): void {\n\t\tthis.#client.unsubscribe('Runtime.bindingCalled', this.#onBindingCalled, this.#sessionId)\n\t\tthis.#client.unsubscribe('Page.frameNavigated', this.#onFrameNavigated, this.#sessionId)\n\t}\n}\n","import type {\n\tBrowserCodegenInterface,\n\tBrowserCodegenOptions,\n\tBrowserFrame,\n\tBrowserPageInterface,\n\tBrowserNavigationOptions,\n\tBrowserActionOptions,\n\tBrowserScreenshotOptions,\n\tBrowserContentResult,\n\tBrowserScreenshotResult,\n\tBrowserWaitUntil,\n\tCDPClientInterface,\n\tScreenshotWriterInterface,\n} from './types.js'\nimport { BrowserCodegen } from './BrowserCodegen.js'\nimport { BrowserError, BrowserResultLimitError, BrowserSelectorError } from './errors.js'\nimport {\n\tBROWSER_DEFAULT_TIMEOUT_MS,\n\tBROWSER_RESULT_LIMIT,\n\tBROWSER_RESULT_LIMIT_PATTERN,\n\tBROWSER_STOP_LOADING_TIMEOUT_MS,\n\tBROWSER_WAIT_POLL_INTERVAL_MS,\n} from './constants.js'\nimport { decodeBase64, guardEvaluateExpression } from './helpers.js'\nimport { isRecord, isString } from '@orkestrel/contract'\n\n// === BrowserPage\n\nexport class BrowserPage implements BrowserPageInterface {\n\treadonly #client: CDPClientInterface\n\treadonly #targetId: string\n\treadonly #sessionId: string\n\treadonly #writer: ScreenshotWriterInterface | undefined\n\t#url = 'about:blank'\n\t#closed = false\n\t#codegen: BrowserCodegen | undefined\n\t#codegenStart: Promise<BrowserCodegen> | undefined\n\t#navigation: Promise<void> | undefined\n\t#closing: Promise<void> | undefined\n\t#releasing: Promise<void> | undefined\n\t#loadEvent: string | undefined\n\t#loadTimer: ReturnType<typeof setTimeout> | undefined\n\t#loadResolve: (() => void) | undefined\n\t#loadReject: ((error: unknown) => void) | undefined\n\t#destroyHandler = (params: Readonly<Record<string, unknown>>): void => {\n\t\tif (!isString(params['targetId']) || params['targetId'] !== this.#targetId) return\n\t\tthis.#closed = true\n\t\tvoid this.#release().catch(() => undefined)\n\t}\n\t#loadHandler = (): void => this.#resolveLoad()\n\n\tconstructor(\n\t\tclient: CDPClientInterface,\n\t\ttargetId: string,\n\t\tsessionId: string,\n\t\twriter?: ScreenshotWriterInterface,\n\t\turl?: string,\n\t) {\n\t\tthis.#client = client\n\t\tthis.#targetId = targetId\n\t\tthis.#sessionId = sessionId\n\t\tthis.#writer = writer\n\t\tif (url !== undefined) this.#url = url\n\n\t\t// Subscribe to external close detection (browser-level event, global subscription)\n\t\tthis.#client.subscribe('Target.targetDestroyed', this.#destroyHandler)\n\t}\n\n\t// === Property accessors\n\n\tget url(): string {\n\t\treturn this.#url\n\t}\n\n\tget closed(): boolean {\n\t\treturn this.#closed\n\t}\n\n\t// === Public API\n\n\tasync title(): Promise<string> {\n\t\tthis.#assertOpen()\n\t\tconst result = await this.#evaluate('document.title')\n\t\treturn this.#requireString(result, 'Document title')\n\t}\n\n\tasync navigate(url: string, options?: BrowserNavigationOptions): Promise<void> {\n\t\tthis.#assertOpen()\n\t\twhile (this.#navigation !== undefined) {\n\t\t\tawait this.#navigation.catch(() => undefined)\n\t\t}\n\t\tthis.#assertOpen()\n\t\tconst navigation = this.#navigate(url, options)\n\t\tthis.#navigation = navigation\n\n\t\ttry {\n\t\t\tawait navigation\n\t\t} finally {\n\t\t\tif (this.#navigation === navigation) this.#navigation = undefined\n\t\t}\n\t}\n\n\tasync content(): Promise<BrowserContentResult> {\n\t\tthis.#assertOpen()\n\t\tconst [title, html, text, currentUrl] = await Promise.all([\n\t\t\tthis.#evaluate('document.title'),\n\t\t\tthis.#evaluate(\n\t\t\t\tguardEvaluateExpression('document.documentElement.outerHTML', BROWSER_RESULT_LIMIT),\n\t\t\t),\n\t\t\tthis.#evaluate(\n\t\t\t\tguardEvaluateExpression(\n\t\t\t\t\t'document.body ? document.body.innerText : \"\"',\n\t\t\t\t\tBROWSER_RESULT_LIMIT,\n\t\t\t\t),\n\t\t\t),\n\t\t\tthis.#evaluate('location.href'),\n\t\t])\n\n\t\tconst url = this.#requireString(currentUrl, 'Document URL')\n\t\tthis.#url = url\n\n\t\treturn {\n\t\t\turl,\n\t\t\ttitle: this.#requireString(title, 'Document title'),\n\t\t\thtml: this.#requireString(html, 'Document HTML'),\n\t\t\ttext: this.#requireString(text, 'Document text'),\n\t\t}\n\t}\n\n\tasync screenshot(options?: BrowserScreenshotOptions): Promise<BrowserScreenshotResult> {\n\t\tthis.#assertOpen()\n\t\tconst format = options?.type ?? 'png'\n\t\tconst params: Record<string, unknown> = { format }\n\n\t\tif (options?.quality !== undefined && format === 'jpeg') {\n\t\t\tparams['quality'] = options.quality\n\t\t}\n\n\t\tif (options?.full === true) {\n\t\t\t// Get full page dimensions for full-page screenshot\n\t\t\tconst metrics: unknown = await this.#client.send(\n\t\t\t\t'Page.getLayoutMetrics',\n\t\t\t\tundefined,\n\t\t\t\tthis.#sessionId,\n\t\t\t)\n\n\t\t\tif (isRecord(metrics) && isRecord(metrics['contentSize'])) {\n\t\t\t\tconst contentSize = metrics['contentSize']\n\t\t\t\tconst width = contentSize['width']\n\t\t\t\tconst height = contentSize['height']\n\n\t\t\t\tif (typeof width === 'number' && typeof height === 'number' && width > 0 && height > 0) {\n\t\t\t\t\tparams['clip'] = { x: 0, y: 0, width, height, scale: 1 }\n\t\t\t\t\tparams['captureBeyondViewport'] = true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst result: unknown = await this.#client.send(\n\t\t\t'Page.captureScreenshot',\n\t\t\tparams,\n\t\t\tthis.#sessionId,\n\t\t)\n\n\t\tif (!isRecord(result) || !isString(result['data'])) {\n\t\t\tthrow new BrowserError('Screenshot failed: no data returned')\n\t\t}\n\n\t\tconst bytes = decodeBase64(result['data'])\n\n\t\tif (options?.path !== undefined && this.#writer !== undefined) {\n\t\t\tawait this.#writer.write(options.path, bytes)\n\t\t}\n\n\t\treturn { bytes, path: options?.path }\n\t}\n\n\tasync click(selector: string, options?: BrowserActionOptions): Promise<void> {\n\t\tthis.#assertOpen()\n\t\tconst timeout = options?.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t\tawait this.#waitForSelector(selector, timeout)\n\t\tawait this.#evaluate(\n\t\t\t`(() => { const el = document.querySelector(${JSON.stringify(selector)}); if (el) { el.scrollIntoView({ block: 'center' }); el.click(); } else { throw new Error('Element not found: ' + ${JSON.stringify(selector)}); } })()`,\n\t\t)\n\t}\n\n\tasync fill(selector: string, value: string, options?: BrowserActionOptions): Promise<void> {\n\t\tthis.#assertOpen()\n\t\tconst timeout = options?.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t\tawait this.#waitForSelector(selector, timeout)\n\t\tawait this.#evaluate(\n\t\t\t`(() => {\n const el = document.querySelector(${JSON.stringify(selector)});\n if (!el) throw new Error('Element not found: ' + ${JSON.stringify(selector)});\n el.focus();\n if (el.isContentEditable) {\n el.textContent = ${JSON.stringify(value)};\n el.dispatchEvent(new Event('input', { bubbles: true }));\n } else {\n el.value = ${JSON.stringify(value)};\n el.dispatchEvent(new Event('input', { bubbles: true }));\n el.dispatchEvent(new Event('change', { bubbles: true }));\n }\n })()`,\n\t\t)\n\t}\n\n\tasync select(\n\t\tselector: string,\n\t\tvalues: readonly string[],\n\t\toptions?: BrowserActionOptions,\n\t): Promise<void> {\n\t\tthis.#assertOpen()\n\t\tconst timeout = options?.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t\tawait this.#waitForSelector(selector, timeout)\n\t\tconst valuesJson = JSON.stringify([...values])\n\t\tawait this.#evaluate(\n\t\t\t`(() => {\n const el = document.querySelector(${JSON.stringify(selector)});\n if (!el) throw new Error('Element not found: ' + ${JSON.stringify(selector)});\n const vals = ${valuesJson};\n for (const opt of el.options) {\n opt.selected = vals.includes(opt.value);\n }\n el.dispatchEvent(new Event('change', { bubbles: true }));\n })()`,\n\t\t)\n\t}\n\n\tasync evaluate(expression: string): Promise<unknown> {\n\t\tthis.#assertOpen()\n\t\treturn this.#evaluate(guardEvaluateExpression(expression, BROWSER_RESULT_LIMIT))\n\t}\n\n\tasync wait(selector: string, options?: BrowserActionOptions): Promise<void> {\n\t\tthis.#assertOpen()\n\t\tconst timeout = options?.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t\tawait this.#waitForSelector(selector, timeout)\n\t}\n\n\tasync frame(name: string): Promise<BrowserFrame | undefined> {\n\t\tconst frames = await this.frames()\n\t\treturn frames.find((frame) => frame.name === name || frame.url === name)\n\t}\n\n\tasync frames(): Promise<readonly BrowserFrame[]> {\n\t\tthis.#assertOpen()\n\t\tconst result: unknown = await this.#client.send('Page.getFrameTree', undefined, this.#sessionId)\n\n\t\tif (!isRecord(result) || !isRecord(result['frameTree'])) return []\n\n\t\tconst frames: BrowserFrame[] = []\n\t\tthis.#flattenFrameTree(result['frameTree'], frames)\n\t\treturn frames\n\t}\n\n\tasync codegen(options?: BrowserCodegenOptions): Promise<BrowserCodegenInterface> {\n\t\tthis.#assertOpen()\n\t\tif (this.#codegen !== undefined) return this.#codegen\n\t\tconst active = this.#codegenStart\n\t\tif (active !== undefined) return await active\n\n\t\tconst attempt = this.#startCodegen(options)\n\t\tthis.#codegenStart = attempt\n\t\ttry {\n\t\t\treturn await attempt\n\t\t} finally {\n\t\t\tif (this.#codegenStart === attempt) this.#codegenStart = undefined\n\t\t}\n\t}\n\n\tdestroy(): Promise<void> {\n\t\tconst active = this.#closing\n\t\tif (active !== undefined) return active\n\n\t\tthis.#closed = true\n\t\tconst closing = this.#destroy()\n\t\tthis.#closing = closing\n\t\treturn closing\n\t}\n\n\tclose(): Promise<void> {\n\t\tconst active = this.#closing\n\t\tif (active !== undefined) return active\n\n\t\tif (this.#closed) {\n\t\t\tconst closing = this.#release()\n\t\t\tthis.#closing = closing\n\t\t\treturn closing\n\t\t}\n\n\t\tthis.#closed = true\n\t\tconst closing = this.#close()\n\t\tthis.#closing = closing\n\t\treturn closing\n\t}\n\n\t// === Private helpers\n\n\tasync #navigate(url: string, options?: BrowserNavigationOptions): Promise<void> {\n\t\tconst timeout = options?.timeout ?? BROWSER_DEFAULT_TIMEOUT_MS\n\t\tconst condition = options?.condition ?? 'load'\n\n\t\tconst wait = this.#waitForLoadEvent(condition, timeout)\n\t\tvoid wait.catch(() => undefined)\n\n\t\ttry {\n\t\t\tconst result: unknown = await this.#client.send(\n\t\t\t\t'Page.navigate',\n\t\t\t\t{ url },\n\t\t\t\tthis.#sessionId,\n\t\t\t\ttimeout,\n\t\t\t)\n\n\t\t\tif (isRecord(result) && isString(result['errorText'])) {\n\t\t\t\tthrow new BrowserError(`Navigation failed: ${result['errorText']}`)\n\t\t\t}\n\n\t\t\tawait wait\n\t\t} catch (error) {\n\t\t\tthis.#cancelLoad()\n\t\t\tawait this.#stopLoading(Math.min(timeout, BROWSER_STOP_LOADING_TIMEOUT_MS))\n\t\t\tthrow error\n\t\t}\n\n\t\tconst currentUrl = await this.#evaluate('location.href')\n\t\tthis.#url = this.#requireString(currentUrl, 'Navigation URL')\n\t}\n\n\tasync #startCodegen(options?: BrowserCodegenOptions): Promise<BrowserCodegen> {\n\t\tconst codegen = new BrowserCodegen(this.#client, this.#sessionId, options)\n\t\tawait codegen.start()\n\t\tif (this.#closed) {\n\t\t\tawait codegen.destroy()\n\t\t\tthrow new BrowserError('Browser page is closed')\n\t\t}\n\t\tthis.#codegen = codegen\n\t\treturn codegen\n\t}\n\n\tasync #destroy(): Promise<void> {\n\t\tawait this.#release()\n\t\ttry {\n\t\t\tawait this.#client.send('Target.detachFromTarget', { sessionId: this.#sessionId })\n\t\t} catch {\n\t\t\t// The session may already be detached.\n\t\t}\n\t}\n\n\tasync #close(): Promise<void> {\n\t\tawait this.#release()\n\n\t\ttry {\n\t\t\tawait this.#client.send('Target.closeTarget', { targetId: this.#targetId })\n\t\t} catch {\n\t\t\t// The target may already be closed.\n\t\t}\n\t}\n\n\t#release(): Promise<void> {\n\t\tconst active = this.#releasing\n\t\tif (active !== undefined) return active\n\n\t\tconst release = this.#releaseResources()\n\t\tthis.#releasing = release\n\t\treturn release\n\t}\n\n\tasync #releaseResources(): Promise<void> {\n\t\tthis.#cancelLoad()\n\t\tawait this.#codegenStart?.catch(() => undefined)\n\n\t\tif (this.#codegen !== undefined) {\n\t\t\ttry {\n\t\t\t\tawait this.#codegen.destroy()\n\t\t\t} catch {\n\t\t\t\t// The recorder's session may already be unavailable.\n\t\t\t}\n\t\t\tthis.#codegen = undefined\n\t\t}\n\n\t\tthis.#client.unsubscribe('Target.targetDestroyed', this.#destroyHandler)\n\t}\n\n\t#flattenFrameTree(node: Readonly<Record<string, unknown>>, out: BrowserFrame[]): void {\n\t\tconst frame = node['frame']\n\n\t\tif (isRecord(frame) && isString(frame['id']) && isString(frame['url'])) {\n\t\t\tconst parent = frame['parentId']\n\t\t\tconst name = frame['name']\n\n\t\t\tout.push({\n\t\t\t\tid: frame['id'],\n\t\t\t\t...(isString(parent) ? { parent } : {}),\n\t\t\t\t...(isString(name) && name !== '' ? { name } : {}),\n\t\t\t\turl: frame['url'],\n\t\t\t})\n\t\t}\n\n\t\tconst children = node['childFrames']\n\t\tif (Array.isArray(children)) {\n\t\t\tfor (const child of children) {\n\t\t\t\tif (isRecord(child)) this.#flattenFrameTree(child, out)\n\t\t\t}\n\t\t}\n\t}\n\n\t// Best-effort: tells Chromium to abandon an in-flight navigation so the\n\t// renderer is not left wedged behind it. Bounded by a short cap (see\n\t// BROWSER_STOP_LOADING_TIMEOUT_MS) rather than the full per-call timeout,\n\t// so a wedged renderer cannot add up to the whole timeout to the failure\n\t// path; any failure here is swallowed so it never masks the original\n\t// navigate error.\n\tasync #stopLoading(timeout: number): Promise<void> {\n\t\ttry {\n\t\t\tawait this.#client.send('Page.stopLoading', undefined, this.#sessionId, timeout)\n\t\t} catch {\n\t\t\t// Best-effort only — the original navigate error is what the caller receives\n\t\t}\n\t}\n\n\tasync #evaluate(expression: string): Promise<unknown> {\n\t\tconst result: unknown = await this.#client.send(\n\t\t\t'Runtime.evaluate',\n\t\t\t{\n\t\t\t\texpression,\n\t\t\t\treturnByValue: true,\n\t\t\t\tawaitPromise: true,\n\t\t\t},\n\t\t\tthis.#sessionId,\n\t\t)\n\n\t\tif (!isRecord(result)) return undefined\n\n\t\t// Check for exceptions\n\t\tif (isRecord(result['exceptionDetails'])) {\n\t\t\tconst details = result['exceptionDetails']\n\t\t\tif (isRecord(details['exception']) && isString(details['exception']['description'])) {\n\t\t\t\tconst description = details['exception']['description']\n\t\t\t\tconst limitMatch = BROWSER_RESULT_LIMIT_PATTERN.exec(description)\n\t\t\t\tif (limitMatch !== null) {\n\t\t\t\t\tthrow new BrowserResultLimitError('Evaluation result exceeds BROWSER_RESULT_LIMIT', {\n\t\t\t\t\t\tlength: Number(limitMatch[1]),\n\t\t\t\t\t\tlimit: BROWSER_RESULT_LIMIT,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tthrow new BrowserError(description)\n\t\t\t}\n\t\t\tthrow new BrowserError('JavaScript evaluation failed')\n\t\t}\n\n\t\t// Extract the value from the result\n\t\tconst remoteObject = result['result']\n\t\tif (!isRecord(remoteObject)) return undefined\n\n\t\tif ('value' in remoteObject) {\n\t\t\treturn remoteObject['value']\n\t\t}\n\n\t\treturn undefined\n\t}\n\n\tasync #waitForSelector(selector: string, timeout: number): Promise<void> {\n\t\tconst deadline = Date.now() + timeout\n\t\tconst selectorJson = JSON.stringify(selector)\n\n\t\twhile (Date.now() < deadline) {\n\t\t\tconst found = await this.#evaluate(`document.querySelector(${selectorJson}) !== null`)\n\t\t\tif (found === true) return\n\n\t\t\tawait new Promise((resolve) => setTimeout(resolve, BROWSER_WAIT_POLL_INTERVAL_MS))\n\t\t}\n\n\t\tthrow new BrowserSelectorError(`Timeout waiting for selector: ${selector}`)\n\t}\n\n\t#waitForLoadEvent(condition: BrowserWaitUntil, timeout: number): Promise<void> {\n\t\tconst eventName =\n\t\t\tcondition === 'domcontentloaded' ? 'Page.domContentEventFired' : 'Page.loadEventFired'\n\t\tconst deferred = Promise.withResolvers<void>()\n\t\tthis.#loadEvent = eventName\n\t\tthis.#loadResolve = deferred.resolve\n\t\tthis.#loadReject = deferred.reject\n\t\tthis.#loadTimer = setTimeout(() => {\n\t\t\tthis.#rejectLoad(new BrowserError(`Navigation timeout after ${timeout}ms`))\n\t\t}, timeout)\n\t\tthis.#client.subscribe(eventName, this.#loadHandler, this.#sessionId)\n\t\treturn deferred.promise\n\t}\n\n\t#resolveLoad(): void {\n\t\tconst resolve = this.#loadResolve\n\t\tthis.#clearLoad()\n\t\tresolve?.()\n\t}\n\n\t#rejectLoad(error: unknown): void {\n\t\tconst reject = this.#loadReject\n\t\tthis.#clearLoad()\n\t\treject?.(error)\n\t}\n\n\t#cancelLoad(): void {\n\t\tthis.#rejectLoad(new BrowserError('Navigation cancelled'))\n\t}\n\n\t#clearLoad(): void {\n\t\tif (this.#loadTimer !== undefined) clearTimeout(this.#loadTimer)\n\t\tif (this.#loadEvent !== undefined) {\n\t\t\tthis.#client.unsubscribe(this.#loadEvent, this.#loadHandler, this.#sessionId)\n\t\t}\n\t\tthis.#loadEvent = undefined\n\t\tthis.#loadTimer = undefined\n\t\tthis.#loadResolve = undefined\n\t\tthis.#loadReject = undefined\n\t}\n\n\t#requireString(value: unknown, field: string): string {\n\t\tif (typeof value === 'string') return value\n\t\tthrow new BrowserError(`${field} failed: no string value returned`)\n\t}\n\n\t#assertOpen(): void {\n\t\tif (this.#closed) throw new BrowserError('Browser page is closed')\n\t}\n}\n","import type {\n\tBrowserContextInterface,\n\tBrowserPageInterface,\n\tBrowserPageOptions,\n\tBrowserViewport,\n\tCDPClientInterface,\n\tCDPTarget,\n\tScreenshotWriterInterface,\n} from './types.js'\nimport { BrowserPage } from './BrowserPage.js'\nimport { BrowserError } from './errors.js'\nimport { isRecord, isString } from '@orkestrel/contract'\n\n// === BrowserContext\n\nexport class BrowserContext implements BrowserContextInterface {\n\treadonly #client: CDPClientInterface\n\treadonly #id: string | undefined\n\treadonly #viewport: BrowserViewport | undefined\n\treadonly #writer: ScreenshotWriterInterface | undefined\n\treadonly #pages: Map<string, BrowserPage> = new Map()\n\treadonly #creating: Set<Promise<BrowserPage>> = new Set()\n\t#syncing: Promise<void> | undefined\n\t#shutdown: Promise<void> | undefined\n\t#closed = false\n\n\tconstructor(\n\t\tclient: CDPClientInterface,\n\t\tid?: string,\n\t\tviewport?: BrowserViewport,\n\t\twriter?: ScreenshotWriterInterface,\n\t) {\n\t\tthis.#client = client\n\t\tthis.#id = id\n\t\tthis.#viewport = viewport\n\t\tthis.#writer = writer\n\t}\n\n\tget id(): string | undefined {\n\t\treturn this.#id\n\t}\n\n\tpage(index?: number): BrowserPageInterface | undefined {\n\t\tconst i = index ?? 0\n\t\tconst pages = [...this.#pages.values()]\n\t\treturn i >= 0 && i < pages.length ? pages[i] : undefined\n\t}\n\n\tpages(): readonly BrowserPageInterface[] {\n\t\treturn [...this.#pages.values()]\n\t}\n\n\tasync create(options?: BrowserPageOptions): Promise<BrowserPageInterface> {\n\t\tif (this.#closed) throw new BrowserError('Browser context is closed')\n\n\t\tconst attempt = this.#create(options)\n\t\tthis.#creating.add(attempt)\n\t\ttry {\n\t\t\treturn await attempt\n\t\t} finally {\n\t\t\tthis.#creating.delete(attempt)\n\t\t}\n\t}\n\n\tasync sync(targets: readonly CDPTarget[]): Promise<void> {\n\t\twhile (this.#syncing !== undefined) {\n\t\t\tawait this.#syncing\n\t\t}\n\t\tif (this.#closed) throw new BrowserError('Browser context is closed')\n\n\t\tconst attempt = this.#sync(targets)\n\t\tthis.#syncing = attempt\n\t\ttry {\n\t\t\tawait attempt\n\t\t} finally {\n\t\t\tif (this.#syncing === attempt) this.#syncing = undefined\n\t\t}\n\t}\n\n\tdestroy(): Promise<void> {\n\t\tconst active = this.#shutdown\n\t\tif (active !== undefined) return active\n\n\t\tthis.#closed = true\n\t\tconst shutdown = this.#destroyResources()\n\t\tthis.#shutdown = shutdown\n\t\treturn shutdown\n\t}\n\n\tclose(): Promise<void> {\n\t\tconst active = this.#shutdown\n\t\tif (active !== undefined) return active\n\n\t\tthis.#closed = true\n\t\tconst shutdown = this.#closeResources()\n\t\tthis.#shutdown = shutdown\n\t\treturn shutdown\n\t}\n\n\t// === Private helpers\n\n\tasync #create(options?: BrowserPageOptions): Promise<BrowserPage> {\n\t\tconst result: unknown = await this.#client.send('Target.createTarget', {\n\t\t\turl: 'about:blank',\n\t\t\t...(this.#id === undefined ? {} : { browserContextId: this.#id }),\n\t\t})\n\n\t\tif (!isRecord(result) || !isString(result['targetId'])) {\n\t\t\tthrow new BrowserError('Failed to create new browser target')\n\t\t}\n\n\t\tconst targetId = result['targetId']\n\t\tlet page: BrowserPage | undefined\n\n\t\ttry {\n\t\t\tconst viewport = options?.viewport ?? this.#viewport\n\t\t\tpage = await this.#attach(targetId, options?.url ?? 'about:blank', viewport)\n\n\t\t\tif (options?.url !== undefined && options.url !== 'about:blank') {\n\t\t\t\tawait page.navigate(options.url, { timeout: options.timeout })\n\t\t\t}\n\t\t\tif (this.#closed) throw new BrowserError('Browser context closed during page creation')\n\n\t\t\tthis.#pages.set(targetId, page)\n\t\t\treturn page\n\t\t} catch (error) {\n\t\t\tif (page !== undefined) {\n\t\t\t\tawait page.close()\n\t\t\t} else {\n\t\t\t\tawait this.#closeTarget(targetId)\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tasync #sync(targets: readonly CDPTarget[]): Promise<void> {\n\t\tconst pageTargets = targets.filter((target) => target.type === 'page')\n\t\tconst targetIds = new Set(pageTargets.map((target) => target.id))\n\n\t\tfor (const [id, page] of this.#pages) {\n\t\t\tif (targetIds.has(id)) continue\n\t\t\tawait page.destroy()\n\t\t\tthis.#pages.delete(id)\n\t\t}\n\n\t\tfor (const target of pageTargets) {\n\t\t\tif (this.#closed || this.#pages.has(target.id)) continue\n\n\t\t\ttry {\n\t\t\t\tconst page = await this.#reattach(target.id, target.url, this.#viewport)\n\t\t\t\tif (this.#closed) {\n\t\t\t\t\tawait page.destroy()\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tthis.#pages.set(target.id, page)\n\t\t\t} catch {\n\t\t\t\t// A disappearing or unsupported target does not invalidate its siblings.\n\t\t\t}\n\t\t}\n\t}\n\n\tasync #attach(\n\t\ttargetId: string,\n\t\turl: string,\n\t\tviewport: BrowserViewport | undefined,\n\t): Promise<BrowserPage> {\n\t\tlet sessionId: string | undefined\n\n\t\ttry {\n\t\t\tsessionId = await this.#openSession(targetId)\n\t\t\tawait this.#enableSession(sessionId)\n\t\t\tif (viewport !== undefined) await this.#applyViewport(sessionId, viewport)\n\n\t\t\treturn new BrowserPage(this.#client, targetId, sessionId, this.#writer, url)\n\t\t} catch (error) {\n\t\t\tif (sessionId !== undefined) await this.#detachSession(sessionId)\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tasync #reattach(\n\t\ttargetId: string,\n\t\turl: string,\n\t\tviewport: BrowserViewport | undefined,\n\t): Promise<BrowserPage> {\n\t\tlet sessionId: string | undefined\n\n\t\ttry {\n\t\t\tsessionId = await this.#openSession(targetId)\n\t\t\tawait this.#enableSession(sessionId)\n\t\t\tif (viewport !== undefined) await this.#tryViewport(sessionId, viewport)\n\n\t\t\treturn new BrowserPage(this.#client, targetId, sessionId, this.#writer, url)\n\t\t} catch (error) {\n\t\t\tif (sessionId !== undefined) await this.#detachSession(sessionId)\n\t\t\tthrow error\n\t\t}\n\t}\n\n\tasync #destroyResources(): Promise<void> {\n\t\tawait this.#settle()\n\t\tfor (const page of this.#pages.values()) {\n\t\t\tawait page.destroy()\n\t\t}\n\t\tthis.#pages.clear()\n\t}\n\n\tasync #closeResources(): Promise<void> {\n\t\tawait this.#settle()\n\t\tfor (const page of this.#pages.values()) {\n\t\t\tawait page.close()\n\t\t}\n\t\tthis.#pages.clear()\n\n\t\tif (this.#id === undefined) return\n\t\ttry {\n\t\t\tawait this.#client.send('Target.disposeBrowserContext', {\n\t\t\t\tbrowserContextId: this.#id,\n\t\t\t})\n\t\t} catch {\n\t\t\t// The context may already be disposed.\n\t\t}\n\t}\n\n\tasync #settle(): Promise<void> {\n\t\tawait Promise.allSettled([...this.#creating])\n\t\tawait this.#syncing?.catch(() => undefined)\n\t}\n\n\tasync #openSession(targetId: string): Promise<string> {\n\t\tconst result: unknown = await this.#client.send('Target.attachToTarget', {\n\t\t\ttargetId,\n\t\t\tflatten: true,\n\t\t})\n\t\tif (!isRecord(result) || !isString(result['sessionId'])) {\n\t\t\tthrow new BrowserError('Failed to attach to browser target')\n\t\t}\n\t\treturn result['sessionId']\n\t}\n\n\tasync #enableSession(sessionId: string): Promise<void> {\n\t\tawait this.#client.send('Page.enable', undefined, sessionId)\n\t\tawait this.#client.send('Runtime.enable', undefined, sessionId)\n\t}\n\n\tasync #applyViewport(sessionId: string, viewport: BrowserViewport): Promise<void> {\n\t\tawait this.#client.send(\n\t\t\t'Emulation.setDeviceMetricsOverride',\n\t\t\t{\n\t\t\t\twidth: viewport.width,\n\t\t\t\theight: viewport.height,\n\t\t\t\tdeviceScaleFactor: 1,\n\t\t\t\tmobile: false,\n\t\t\t},\n\t\t\tsessionId,\n\t\t)\n\t}\n\n\tasync #tryViewport(sessionId: string, viewport: BrowserViewport): Promise<void> {\n\t\ttry {\n\t\t\tawait this.#applyViewport(sessionId, viewport)\n\t\t} catch {\n\t\t\t// Reattached targets may not support viewport emulation.\n\t\t}\n\t}\n\n\tasync #closeTarget(targetId: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.#client.send('Target.closeTarget', { targetId })\n\t\t} catch {\n\t\t\t// The target may already be gone.\n\t\t}\n\t}\n\n\tasync #detachSession(sessionId: string): Promise<void> {\n\t\ttry {\n\t\t\tawait this.#client.send('Target.detachFromTarget', { sessionId })\n\t\t} catch {\n\t\t\t// The session may already be detached.\n\t\t}\n\t}\n}\n","import type { CDPClientInterface, CDPClientOptions } from './types.js'\nimport { CDPClient } from './CDPClient.js'\n\n/**\n * Create a CDP client bound to the given transport.\n *\n * @param options - The transport (and optional timeout) the client uses\n * @returns A {@link CDPClientInterface}\n *\n * @example\n * ```ts\n * import { createCDPClient } from '@src/core'\n *\n * const client = createCDPClient({ transport })\n * await client.connect()\n * ```\n */\nexport function createCDPClient(options: CDPClientOptions): CDPClientInterface {\n\treturn new CDPClient(options)\n}\n"],"mappings":"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"}
|