UNPKG

cdp-web

Version:

Provides a lightweight way to communicate with the Chrome DevTools Protocol (CDP).

155 lines (152 loc) 3.86 kB
// src/deferred.ts function isPromiseLike(value) { return typeof value === "object" && value !== null && "then" in value && typeof value.then === "function"; } var Deferred = class extends Promise { state = "pending"; value; reason; resolve; reject; constructor(executor) { let resolve; let reject; super((_resolve, _reject) => { resolve = (value) => { if (this.state === "pending" && !isPromiseLike(value)) { this.state = "fulfilled"; this.value = value; } return _resolve(value); }; reject = (reason) => { if (this.state === "pending") { this.state = "rejected"; this.reason = reason; } return _reject(reason); }; executor?.(resolve, reject); }); this.resolve = resolve; this.reject = reject; } }; // src/cdp.ts var DEFAULT_URL = "http://localhost:9222"; var CdpMessageEvent = class extends Event { method; params; constructor(init) { super("message", init); this.method = init.method; this.params = init.params; } }; var CdpError = class extends Error { code; constructor(code, message, options) { super(message, options); this.code = code; } }; var CdpConnection = class _CdpConnection extends EventTarget { static async create(options) { const cdpConnection = new _CdpConnection(options); await cdpConnection.#connect(); return cdpConnection; } #ws; #url; #target; #requestCount = 0; #requests = /* @__PURE__ */ new Map(); constructor(options) { super(); this.#url = new URL(options?.url ?? DEFAULT_URL); this.#target = options?.target ?? "page"; } async #getWsUrl() { const response = await fetch(new URL("/json/list", this.#url)); const targets = await response.json(); const wsUrl = targets.find( (target) => target.webSocketDebuggerUrl && target.type === this.#target ).webSocketDebuggerUrl; return wsUrl; } #onMessage(payload) { if ("id" in payload) { const deferred = this.#requests.get(payload.id); if (!deferred) { return; } if ("error" in payload) { deferred.reject( new CdpError(payload.error.code, payload.error.message) ); } else { deferred.resolve(payload.result); } return; } if ("method" in payload) { this.dispatchEvent( new CdpMessageEvent({ method: payload.method, params: payload.params }) ); return; } } async #connect() { const wsUrl = await this.#getWsUrl(); this.#ws = new WebSocket(wsUrl); this.#ws.addEventListener("message", (event) => { const text = typeof event.data === "string" ? event.data : new TextDecoder().decode(event.data); const payload = JSON.parse(text); this.#onMessage(payload); }); return new Promise((resolve, reject) => { this.#ws.addEventListener("open", () => { resolve(); }); this.#ws.addEventListener("close", (event) => { reject(new Error(event.reason)); }); this.#ws.addEventListener("error", () => { reject(new Error()); }); }); } send(method, params = {}) { const id = this.#requestCount++; const payload = { id, method, params }; this.#ws.send(JSON.stringify(payload)); const deferred = new Deferred(); this.#requests.set(id, deferred); return deferred; } close() { this.#ws.close(); } addEventListener(...args) { return super.addEventListener(...args); } removeEventListener(...args) { return super.removeEventListener(...args); } }; function createCdpConnection(options) { return CdpConnection.create(options); } export { CdpMessageEvent, CdpError, CdpConnection, createCdpConnection };