import { Type, type Static, type TSchema } from "typebox"; import { Compile } from "typebox/compile"; import { type Result, err, ok } from "../util/result"; import { type CdpError, cdpError } from "./errors"; const cmd =

(params: P, result: R) => ({ params, result, validate: Compile(result), }); const Empty = Type.Object({}); const TargetInfo = Type.Object( { targetId: Type.String(), type: Type.String(), title: Type.String(), url: Type.String(), attached: Type.Optional(Type.Boolean()), openerId: Type.Optional(Type.String()), browserContextId: Type.Optional(Type.String()), }, { additionalProperties: true }, ); const RemoteObject = Type.Object( { type: Type.String(), subtype: Type.Optional(Type.String()), className: Type.Optional(Type.String()), value: Type.Optional(Type.Unknown()), description: Type.Optional(Type.String()), objectId: Type.Optional(Type.String()), }, { additionalProperties: true }, ); const CallArgument = Type.Object( { value: Type.Optional(Type.Unknown()), objectId: Type.Optional(Type.String()), unserializableValue: Type.Optional(Type.String()), }, { additionalProperties: true }, ); const ExceptionDetails = Type.Object( { text: Type.String(), exception: Type.Optional(RemoteObject) }, { additionalProperties: true }, ); const AxValue = Type.Object({ value: Type.Optional(Type.Unknown()) }, { additionalProperties: true }); const AxNode = Type.Object( { nodeId: Type.String(), parentId: Type.Optional(Type.String()), childIds: Type.Optional(Type.Array(Type.String())), ignored: Type.Optional(Type.Boolean()), role: Type.Optional(AxValue), name: Type.Optional(AxValue), value: Type.Optional(AxValue), description: Type.Optional(AxValue), properties: Type.Optional( Type.Array( Type.Object( { name: Type.Optional(Type.String()), value: Type.Optional(AxValue) }, { additionalProperties: true }, ), ), ), backendDOMNodeId: Type.Optional(Type.Number()), }, { additionalProperties: true }, ); export type AxNodeResult = Static; export const COMMANDS = { "Target.attachToTarget": cmd( Type.Object({ targetId: Type.String(), flatten: Type.Optional(Type.Boolean()) }), Type.Object({ sessionId: Type.String() }), ), "Target.detachFromTarget": cmd( Type.Object({ sessionId: Type.Optional(Type.String()) }), Empty, ), "Target.createTarget": cmd( Type.Object({ url: Type.String(), newWindow: Type.Optional(Type.Boolean()), background: Type.Optional(Type.Boolean()), windowId: Type.Optional(Type.Number()), openerId: Type.Optional(Type.String()), }), Type.Object({ targetId: Type.String() }), ), "Target.closeTarget": cmd( Type.Object({ targetId: Type.String() }), Type.Object({ success: Type.Optional(Type.Boolean()) }), ), "Target.activateTarget": cmd(Type.Object({ targetId: Type.String() }), Empty), "Target.getTargetInfo": cmd( Type.Object({ targetId: Type.Optional(Type.String()) }), Type.Object({ targetInfo: TargetInfo }), ), "Target.getTargets": cmd( Type.Object({}), Type.Object({ targetInfos: Type.Array(TargetInfo) }), ), "Target.setDiscoverTargets": cmd(Type.Object({ discover: Type.Boolean() }), Empty), "Browser.getWindowForTarget": cmd( Type.Object({ targetId: Type.Optional(Type.String()) }), Type.Object({ windowId: Type.Number(), bounds: Type.Optional(Type.Unknown()) }), ), "Browser.setDownloadBehavior": cmd( Type.Object({ behavior: Type.String(), downloadPath: Type.Optional(Type.String()), eventsEnabled: Type.Optional(Type.Boolean()), }), Empty, ), "Page.enable": cmd(Type.Object({}), Empty), "Page.bringToFront": cmd(Type.Object({}), Empty), "Page.navigate": cmd( Type.Object({ url: Type.String(), referrer: Type.Optional(Type.String()) }), Type.Object({ frameId: Type.String(), loaderId: Type.Optional(Type.String()), errorText: Type.Optional(Type.String()), }), ), "Page.reload": cmd(Type.Object({ ignoreCache: Type.Optional(Type.Boolean()) }), Empty), "Page.getNavigationHistory": cmd( Type.Object({}), Type.Object({ currentIndex: Type.Number(), entries: Type.Array( Type.Object({ id: Type.Number(), url: Type.String(), title: Type.String() }), ), }), ), "Page.navigateToHistoryEntry": cmd(Type.Object({ entryId: Type.Number() }), Empty), "Page.captureScreenshot": cmd( Type.Object({ format: Type.Optional(Type.String()), quality: Type.Optional(Type.Number()), clip: Type.Optional(Type.Unknown()), captureBeyondViewport: Type.Optional(Type.Boolean()), }), Type.Object({ data: Type.String() }), ), "Page.printToPDF": cmd(Type.Object({}, { additionalProperties: true }), Type.Object({ data: Type.String() })), "Page.handleJavaScriptDialog": cmd( Type.Object({ accept: Type.Boolean(), promptText: Type.Optional(Type.String()) }), Empty, ), "Runtime.evaluate": cmd( Type.Object( { expression: Type.String(), returnByValue: Type.Optional(Type.Boolean()), awaitPromise: Type.Optional(Type.Boolean()), userGesture: Type.Optional(Type.Boolean()), }, { additionalProperties: true }, ), Type.Object({ result: RemoteObject, exceptionDetails: Type.Optional(ExceptionDetails) }), ), "Runtime.callFunctionOn": cmd( Type.Object( { functionDeclaration: Type.String(), objectId: Type.Optional(Type.String()), arguments: Type.Optional(Type.Array(CallArgument)), returnByValue: Type.Optional(Type.Boolean()), awaitPromise: Type.Optional(Type.Boolean()), userGesture: Type.Optional(Type.Boolean()), }, { additionalProperties: true }, ), Type.Object({ result: RemoteObject, exceptionDetails: Type.Optional(ExceptionDetails) }), ), "Runtime.releaseObject": cmd(Type.Object({ objectId: Type.String() }), Empty), "DOM.getDocument": cmd( Type.Object({ depth: Type.Optional(Type.Number()), pierce: Type.Optional(Type.Boolean()) }), Type.Object({ root: Type.Object({ nodeId: Type.Number() }, { additionalProperties: true }) }), ), "DOM.querySelector": cmd( Type.Object({ nodeId: Type.Number(), selector: Type.String() }), Type.Object({ nodeId: Type.Number() }), ), "DOM.getBoxModel": cmd( Type.Object({ nodeId: Type.Optional(Type.Number()), backendNodeId: Type.Optional(Type.Number()) }), Type.Object({ model: Type.Object( { content: Type.Array(Type.Number()), width: Type.Number(), height: Type.Number() }, { additionalProperties: true }, ), }), ), "DOM.resolveNode": cmd( Type.Object({ nodeId: Type.Optional(Type.Number()), backendNodeId: Type.Optional(Type.Number()) }), Type.Object({ object: RemoteObject }), ), "DOM.setFileInputFiles": cmd( Type.Object({ files: Type.Array(Type.String()), nodeId: Type.Optional(Type.Number()), backendNodeId: Type.Optional(Type.Number()), objectId: Type.Optional(Type.String()), }), Empty, ), "Input.dispatchMouseEvent": cmd(Type.Object({}, { additionalProperties: true }), Empty), "Input.dispatchKeyEvent": cmd(Type.Object({}, { additionalProperties: true }), Empty), "Input.dispatchDragEvent": cmd(Type.Object({}, { additionalProperties: true }), Empty), "Input.insertText": cmd(Type.Object({ text: Type.String() }), Empty), "Network.getResponseBody": cmd( Type.Object({ requestId: Type.String() }), Type.Object({ body: Type.String(), base64Encoded: Type.Optional(Type.Boolean()) }), ), "Emulation.setDeviceMetricsOverride": cmd(Type.Object({}, { additionalProperties: true }), Empty), "Accessibility.getFullAXTree": cmd( Type.Object({ depth: Type.Optional(Type.Number()) }, { additionalProperties: true }), Type.Object({ nodes: Type.Array(AxNode) }), ), }; export type CdpMethod = keyof typeof COMMANDS; export type ParamsOf = Static<(typeof COMMANDS)[M]["params"]>; export type ResultOf = Static<(typeof COMMANDS)[M]["result"]>; export const decodeResult = ( method: M, raw: unknown, ): Result, CdpError> => { const spec = COMMANDS[method]; if (spec === undefined) { return err(cdpError("invalid_response", `unknown CDP method`, method)); } if (!spec.validate.Check(raw)) { const why = spec.validate.Errors(raw).map((e) => e.message).join("; "); return err(cdpError("invalid_response", why, method)); } // Guarded assertion: the check above proves `raw` matches COMMANDS[method].result, which TS cannot correlate from a generic key. return ok(raw as ResultOf); };