/
githubmirror
/
deno
Обзор
Документация
Войти
/
githubmirror
/
deno
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
ext/node/polyfills/inspector.js
345 строк
9 KB
Divy Srivastava
perf: startup time (22ms -> 15ms) (#34450)
18 июн 2026, 20:48
Не верифицирован
18 июн 2026, 20:48
7b54748
Код
Авторство
О чём код?
// Copyright 2018-2026 the Deno authors. MIT license. // Copyright Joyent and Node contributors. All rights reserved. MIT license. (function () { const { core, primordials } = __bootstrap; const { op_base64_encode_from_buffer, op_get_extras_binding_object, op_inspector_close, op_inspector_connect, op_inspector_disconnect, op_inspector_dispatch, op_inspector_emit_protocol_event, op_inspector_enabled, op_inspector_open, op_inspector_url, op_inspector_wait, } = core.ops; const lazyProcess = core.createLazyLoader("node:process"); const lazyWorkerThreads = core.createLazyLoader("node:worker_threads"); const { EventEmitter } = core.loadExtScript("ext:deno_node/_events.mjs"); const { isUint32, validateFunction, validateInt32, validateObject, validateString, } = core.loadExtScript("ext:deno_node/internal/validators.mjs"); const { ERR_INSPECTOR_ALREADY_ACTIVATED, ERR_INSPECTOR_ALREADY_CONNECTED, ERR_INSPECTOR_CLOSED, ERR_INSPECTOR_COMMAND, ERR_INSPECTOR_NOT_ACTIVE, ERR_INSPECTOR_NOT_CONNECTED, ERR_INSPECTOR_NOT_WORKER, } = core.loadExtScript("ext:deno_node/internal/errors.ts"); function isLoopback(host) { const hostLower = StringPrototypeToLowerCase(host); return ( hostLower === "localhost" || StringPrototypeStartsWith(hostLower, "127.") || hostLower === "[::1]" || hostLower === "[0:0:0:0:0:0:0:1]" ); } const { ArrayBufferPrototype, ArrayPrototypePush, ArrayPrototypeShift, ObjectAssign, ObjectPrototypeIsPrototypeOf, SymbolDispose, JSONParse, JSONStringify, SafeMap, SafeMapIterator, StringPrototypeStartsWith, StringPrototypeToLowerCase, TypeError, TypedArrayPrototypeGetByteLength, TypedArrayPrototypeGetSymbolToStringTag, Uint8Array, } = primordials; function encodeNetworkData(data) { if (data == null) return undefined; if (typeof data === "string") { // Encode UTF-8 string as base64. const buf = core.encode(data); return op_base64_encode_from_buffer( buf, 0, TypedArrayPrototypeGetByteLength(buf), ); } if (TypedArrayPrototypeGetSymbolToStringTag(data) === "Uint8Array") { return op_base64_encode_from_buffer( data, 0, TypedArrayPrototypeGetByteLength(data), ); } if (ObjectPrototypeIsPrototypeOf(ArrayBufferPrototype, data)) { const view = new Uint8Array(data); return op_base64_encode_from_buffer( view, 0, TypedArrayPrototypeGetByteLength(view), ); } throw new TypeError( "Expected data to be a string, Buffer, Uint8Array, or ArrayBuffer", ); } class Session extends EventEmitter { #connection = null; #nextId = 1; #messageCallbacks = new SafeMap(); #pendingMessages = []; #drainScheduled = false; #isDraining = false; connect() { if (this.#connection) { throw new ERR_INSPECTOR_ALREADY_CONNECTED("The inspector session"); } this.#connection = op_inspector_connect( false, (m) => this.#enqueueMessage(m), ); } connectToMainThread() { if (lazyWorkerThreads().isMainThread) { throw new ERR_INSPECTOR_NOT_WORKER(); } if (this.#connection) { throw new ERR_INSPECTOR_ALREADY_CONNECTED("The inspector session"); } this.#connection = op_inspector_connect( true, (m) => this.#enqueueMessage(m), ); } #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) { lazyProcess().default.emitWarning(error); } } #enqueueMessage(message) { ArrayPrototypePush(this.#pendingMessages, message); if (this.#isDraining) return; if (!this.#drainScheduled) { this.#drainScheduled = true; lazyProcess().default.nextTick(() => this.#drainMessages()); } } #drainMessages() { this.#drainScheduled = false; this.#isDraining = true; try { while (this.#pendingMessages.length > 0) { const nextMessage = ArrayPrototypeShift(this.#pendingMessages); this.#onMessage(nextMessage); } } finally { this.#isDraining = false; } } 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); } op_inspector_dispatch(this.#connection, JSONStringify(message)); } disconnect() { if (!this.#connection) { return; } op_inspector_disconnect(this.#connection); this.#connection = null; for ( const { 1: callback } of new SafeMapIterator(this.#messageCallbacks) ) { lazyProcess().default.nextTick(callback, new ERR_INSPECTOR_CLOSED()); } this.#messageCallbacks.clear(); this.#nextId = 1; this.#pendingMessages.length = 0; this.#drainScheduled = false; this.#isDraining = false; } } function open(port, host, wait) { if (op_inspector_enabled()) { 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); } else { // equiv of handling args[0]->IsUint32() port = undefined; } if (typeof host !== "string") { // equiv of handling args[1]->IsString() host = undefined; } if (host && !isLoopback(host)) { lazyProcess().default.emitWarning( "Binding the inspector to a public IP with an open port is insecure, " + "as it allows external hosts to connect to the inspector " + "and perform a remote code execution attack.", "SecurityWarning", ); } op_inspector_open(port, host, !!wait); return { __proto__: null, [SymbolDispose]() { op_inspector_close(); }, }; } function close() { op_inspector_close(); } function url() { const u = op_inspector_url(); if (u === null) { return undefined; } return u; } function waitForDebugger() { if (!op_inspector_wait()) { throw new ERR_INSPECTOR_NOT_ACTIVE(); } } function broadcastToFrontend(eventName, params) { validateString(eventName, "eventName"); if (params) { validateObject(params, "params"); } op_inspector_emit_protocol_event(eventName, JSONStringify(params ?? {})); } function broadcastNetworkData(eventName, params) { if (params && params.data !== undefined) { const encoded = encodeNetworkData(params.data); if (encoded !== params.data) { params = ObjectAssign({ __proto__: null }, params, { data: encoded }); } } broadcastToFrontend(eventName, 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), dataReceived: (params) => broadcastNetworkData("Network.dataReceived", params), dataSent: (params) => broadcastNetworkData("Network.dataSent", params), webSocketCreated: (params) => broadcastToFrontend("Network.webSocketCreated", params), webSocketHandshakeResponseReceived: (params) => broadcastToFrontend("Network.webSocketHandshakeResponseReceived", params), webSocketClosed: (params) => broadcastToFrontend("Network.webSocketClosed", params), }; const DOMStorage = { domStorageItemAdded: (params) => broadcastToFrontend("DOMStorage.domStorageItemAdded", params), domStorageItemRemoved: (params) => broadcastToFrontend("DOMStorage.domStorageItemRemoved", params), domStorageItemUpdated: (params) => broadcastToFrontend("DOMStorage.domStorageItemUpdated", params), domStorageItemsCleared: (params) => broadcastToFrontend("DOMStorage.domStorageItemsCleared", params), registerStorage: (params) => broadcastToFrontend("DOMStorage.registerStorage", params), }; const inspectorConsole = op_get_extras_binding_object().console; return { close, console: inspectorConsole, DOMStorage, Network, open, Session, url, waitForDebugger, }; })();