1'use strict'; 2 3const { 4 JSONParse, 5 JSONStringify, 6 SafeMap, 7 Symbol, 8} = primordials; 9 10const { 11 ERR_INSPECTOR_ALREADY_ACTIVATED, 12 ERR_INSPECTOR_ALREADY_CONNECTED, 13 ERR_INSPECTOR_CLOSED, 14 ERR_INSPECTOR_COMMAND, 15 ERR_INSPECTOR_NOT_AVAILABLE, 16 ERR_INSPECTOR_NOT_CONNECTED, 17 ERR_INSPECTOR_NOT_ACTIVE, 18 ERR_INSPECTOR_NOT_WORKER, 19 ERR_INVALID_ARG_TYPE, 20 ERR_INVALID_CALLBACK 21} = require('internal/errors').codes; 22 23const { hasInspector } = internalBinding('config'); 24if (!hasInspector) 25 throw new ERR_INSPECTOR_NOT_AVAILABLE(); 26 27const EventEmitter = require('events'); 28const { queueMicrotask } = require('internal/process/task_queues'); 29const { validateString } = require('internal/validators'); 30const { isMainThread } = require('worker_threads'); 31 32const { 33 Connection, 34 MainThreadConnection, 35 open, 36 url, 37 isEnabled, 38 waitForDebugger 39} = internalBinding('inspector'); 40 41const connectionSymbol = Symbol('connectionProperty'); 42const messageCallbacksSymbol = Symbol('messageCallbacks'); 43const nextIdSymbol = Symbol('nextId'); 44const onMessageSymbol = Symbol('onMessage'); 45 46class Session extends EventEmitter { 47 constructor() { 48 super(); 49 this[connectionSymbol] = null; 50 this[nextIdSymbol] = 1; 51 this[messageCallbacksSymbol] = new SafeMap(); 52 } 53 54 connect() { 55 if (this[connectionSymbol]) 56 throw new ERR_INSPECTOR_ALREADY_CONNECTED('The inspector session'); 57 this[connectionSymbol] = 58 new Connection((message) => this[onMessageSymbol](message)); 59 } 60 61 connectToMainThread() { 62 if (isMainThread) 63 throw new ERR_INSPECTOR_NOT_WORKER(); 64 if (this[connectionSymbol]) 65 throw new ERR_INSPECTOR_ALREADY_CONNECTED('The inspector session'); 66 this[connectionSymbol] = 67 new MainThreadConnection( 68 (message) => queueMicrotask(() => this[onMessageSymbol](message))); 69 } 70 71 [onMessageSymbol](message) { 72 const parsed = JSONParse(message); 73 try { 74 if (parsed.id) { 75 const callback = this[messageCallbacksSymbol].get(parsed.id); 76 this[messageCallbacksSymbol].delete(parsed.id); 77 if (callback) { 78 if (parsed.error) { 79 return callback(new ERR_INSPECTOR_COMMAND(parsed.error.code, 80 parsed.error.message)); 81 } 82 83 callback(null, parsed.result); 84 } 85 } else { 86 this.emit(parsed.method, parsed); 87 this.emit('inspectorNotification', parsed); 88 } 89 } catch (error) { 90 process.emitWarning(error); 91 } 92 } 93 94 post(method, params, callback) { 95 validateString(method, 'method'); 96 if (!callback && typeof params === 'function') { 97 callback = params; 98 params = null; 99 } 100 if (params && typeof params !== 'object') { 101 throw new ERR_INVALID_ARG_TYPE('params', 'Object', params); 102 } 103 if (callback && typeof callback !== 'function') { 104 throw new ERR_INVALID_CALLBACK(callback); 105 } 106 107 if (!this[connectionSymbol]) { 108 throw new ERR_INSPECTOR_NOT_CONNECTED(); 109 } 110 const id = this[nextIdSymbol]++; 111 const message = { id, method }; 112 if (params) { 113 message.params = params; 114 } 115 if (callback) { 116 this[messageCallbacksSymbol].set(id, callback); 117 } 118 this[connectionSymbol].dispatch(JSONStringify(message)); 119 } 120 121 disconnect() { 122 if (!this[connectionSymbol]) 123 return; 124 this[connectionSymbol].disconnect(); 125 this[connectionSymbol] = null; 126 const remainingCallbacks = this[messageCallbacksSymbol].values(); 127 for (const callback of remainingCallbacks) { 128 process.nextTick(callback, new ERR_INSPECTOR_CLOSED()); 129 } 130 this[messageCallbacksSymbol].clear(); 131 this[nextIdSymbol] = 1; 132 } 133} 134 135function inspectorOpen(port, host, wait) { 136 if (isEnabled()) { 137 throw new ERR_INSPECTOR_ALREADY_ACTIVATED(); 138 } 139 open(port, host); 140 if (wait) 141 waitForDebugger(); 142} 143 144function inspectorWaitForDebugger() { 145 if (!waitForDebugger()) 146 throw new ERR_INSPECTOR_NOT_ACTIVE(); 147} 148 149module.exports = { 150 open: inspectorOpen, 151 close: process._debugEnd, 152 url: url, 153 waitForDebugger: inspectorWaitForDebugger, 154 // This is dynamically added during bootstrap, 155 // where the console from the VM is still available 156 console: require('internal/util/inspector').consoleFromVM, 157 Session 158}; 159