UNPKG

nstdlib-nightly

Version:

Node.js standard library converted to runtime-agnostic ES modules.

214 lines (196 loc) 5.76 kB
// Source: https://github.com/nodejs/node/blob/65eff1eb/lib/inspector.js import { codes as __codes__ } from "nstdlib/lib/internal/errors"; import { hasInspector } from "nstdlib/stub/binding/config"; import EventEmitter from "nstdlib/lib/events"; import { queueMicrotask } from "nstdlib/lib/internal/process/task_queues"; import { isUint32, validateFunction, validateInt32, validateObject, validateString, } from "nstdlib/lib/internal/validators"; import { isMainThread } from "nstdlib/lib/worker_threads"; import { _debugEnd } from "nstdlib/stub/binding/process_methods"; import { Connection, MainThreadConnection, open, url, isEnabled, waitForDebugger, console, emitProtocolEvent, } from "nstdlib/stub/binding/inspector"; const { ERR_INSPECTOR_ALREADY_ACTIVATED, ERR_INSPECTOR_ALREADY_CONNECTED, ERR_INSPECTOR_CLOSED, ERR_INSPECTOR_COMMAND, ERR_INSPECTOR_NOT_AVAILABLE, ERR_INSPECTOR_NOT_CONNECTED, ERR_INSPECTOR_NOT_ACTIVE, ERR_INSPECTOR_NOT_WORKER, } = __codes__; if (!hasInspector) throw new ERR_INSPECTOR_NOT_AVAILABLE(); class Session extends EventEmitter { #connection = null; #nextId = 1; #messageCallbacks = new Map(); /** * Connects the session to the inspector back-end. * @returns {void} */ connect() { if (this.#connection) throw new ERR_INSPECTOR_ALREADY_CONNECTED("The inspector session"); this.#connection = new Connection((message) => this.#onMessage(message)); } /** * Connects the session to the main thread * inspector back-end. * @returns {void} */ connectToMainThread() { if (isMainThread) throw new ERR_INSPECTOR_NOT_WORKER(); if (this.#connection) throw new ERR_INSPECTOR_ALREADY_CONNECTED("The inspector session"); this.#connection = new MainThreadConnection((message) => queueMicrotask(() => this.#onMessage(message)), ); } #onMessage(message) { const parsed = JSONParse(message); try { if (parsed.id) { const callback = this.#messageCallbacks.get(parsed.id); this.#messageCallbacks.delete(parsed.id); if (callback) { if (parsed.error) { return callback( new ERR_INSPECTOR_COMMAND( parsed.error.code, parsed.error.message, ), ); } callback(null, parsed.result); } } else { this.emit(parsed.method, parsed); this.emit("inspectorNotification", parsed); } } catch (error) { process.emitWarning(error); } } /** * Posts a message to the inspector back-end. * @param {string} method * @param {Record<unknown, unknown>} [params] * @param {Function} [callback] * @returns {void} */ post(method, params, callback) { validateString(method, "method"); if (!callback && typeof params === "function") { callback = params; params = null; } if (params) { validateObject(params, "params"); } if (callback) { validateFunction(callback, "callback"); } if (!this.#connection) { throw new ERR_INSPECTOR_NOT_CONNECTED(); } const id = this.#nextId++; const message = { id, method }; if (params) { message.params = params; } if (callback) { this.#messageCallbacks.set(id, callback); } this.#connection.dispatch(JSONStringify(message)); } /** * Immediately closes the session, all pending * message callbacks will be called with an * error. * @returns {void} */ disconnect() { if (!this.#connection) return; this.#connection.disconnect(); this.#connection = null; const remainingCallbacks = this.#messageCallbacks.values(); for (const callback of remainingCallbacks) { process.nextTick(callback, new ERR_INSPECTOR_CLOSED()); } this.#messageCallbacks.clear(); this.#nextId = 1; } } /** * Activates inspector on host and port. * @param {number} [port] * @param {string} [host] * @param {boolean} [wait] * @returns {void} */ function inspectorOpen(port, host, wait) { if (isEnabled()) { throw new ERR_INSPECTOR_ALREADY_ACTIVATED(); } // inspectorOpen() currently does not typecheck its arguments and adding // such checks would be a potentially breaking change. However, the native // open() function requires the port to fit into a 16-bit unsigned integer, // causing an integer overflow otherwise, so we at least need to prevent that. if (isUint32(port)) { validateInt32(port, "port", 0, 65535); } open(port, host); if (wait) waitForDebugger(); return { __proto__: null, [Symbol.for("nodejs.dispose")]() { _debugEnd(); }, }; } /** * Blocks until a client (existing or connected later) * has sent the `Runtime.runIfWaitingForDebugger` * command. * @returns {void} */ function inspectorWaitForDebugger() { if (!waitForDebugger()) throw new ERR_INSPECTOR_NOT_ACTIVE(); } function broadcastToFrontend(eventName, params) { validateString(eventName, "eventName"); if (params) { validateObject(params, "params"); } emitProtocolEvent(eventName, JSONStringify(params ?? {})); } const Network = { requestWillBeSent: (params) => broadcastToFrontend("Network.requestWillBeSent", params), responseReceived: (params) => broadcastToFrontend("Network.responseReceived", params), loadingFinished: (params) => broadcastToFrontend("Network.loadingFinished", params), loadingFailed: (params) => broadcastToFrontend("Network.loadingFailed", params), }; export { inspectorOpen as open }; export { _debugEnd as close }; export { url }; export { inspectorWaitForDebugger as waitForDebugger }; export { console }; export { Session }; export { Network };