iobroker.javascript 10.2.2 → 10.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/README.md +2 -1
  2. package/admin/assets/{AiChatPanel-C5vTuhiL.js → AiChatPanel-AZko1BsN.js} +2 -2
  3. package/admin/assets/Debugger-CVDh3kgz.js +15 -0
  4. package/admin/assets/RulesEditor-D3kTbD_T.css +1 -0
  5. package/admin/assets/RulesEditor-DsO4cKIk.js +1 -0
  6. package/admin/assets/{ScriptEditor-COwzdxKl.js → ScriptEditor-BCGahS47.js} +1 -1
  7. package/admin/assets/{ScriptEditorVanillaMonaco-C0biTTli.js → ScriptEditorVanillaMonaco-BDAw2Bw6.js} +2 -2
  8. package/admin/assets/aiPromptBuilder-aJfHa-eU.js +1 -0
  9. package/admin/assets/{blocks_action-CycTOoDY.js → blocks_action-DS28sNfo.js} +1 -1
  10. package/admin/assets/{blocks_convert-BUmTT450.js → blocks_convert-CLb0DLyc.js} +1 -1
  11. package/admin/assets/{blocks_logic-BP_rkFPu.js → blocks_logic-Dpp2fwlu.js} +1 -1
  12. package/admin/assets/{blocks_number-DZPdNWcv.js → blocks_number-N4IfHvYD.js} +1 -1
  13. package/admin/assets/{blocks_object-CUKKvXdl.js → blocks_object-uK7FHVhS.js} +1 -1
  14. package/admin/assets/{blocks_procedures-D3LiaeLD.js → blocks_procedures-C_KD0Bm6.js} +1 -1
  15. package/admin/assets/{blocks_sendto-BWmeOFOr.js → blocks_sendto-BxCMg9X4.js} +1 -1
  16. package/admin/assets/{blocks_switch-B17LJ99U.js → blocks_switch-C8Rls5pK.js} +1 -1
  17. package/admin/assets/{blocks_system-DdvuotD5.js → blocks_system-CEQnj5fY.js} +1 -1
  18. package/admin/assets/{blocks_text-D35PPiiy.js → blocks_text-BDEWxu0P.js} +1 -1
  19. package/admin/assets/{blocks_time-CuI_GrE0.js → blocks_time-aHQQK1Uz.js} +1 -1
  20. package/admin/assets/{blocks_timeout-tbB0KBqO.js → blocks_timeout-C2L93TLJ.js} +1 -1
  21. package/admin/assets/{blocks_trigger-Bw3qbQRd.js → blocks_trigger-XivsIDE5.js} +1 -1
  22. package/admin/assets/{helpers-D5PdK4BS.js → helpers-DiwtLpXM.js} +1 -1
  23. package/admin/assets/{index-PLY-IJik.js → index-B0FfAYMN.js} +7 -7
  24. package/admin/assets/{mf-entry-bootstrap-0-f35ca6a1.js → mf-entry-bootstrap-0-019228a5.js} +1 -1
  25. package/admin/mf-stats.json +1 -1
  26. package/admin/tab.html +2 -2
  27. package/build/lib/inspect.js +547 -632
  28. package/build/lib/inspect.js.map +1 -1
  29. package/build/main.js +8 -13
  30. package/build/main.js.map +1 -1
  31. package/io-package.json +27 -27
  32. package/package.json +1 -1
  33. package/admin/assets/Debugger-DnrhfguX.js +0 -15
  34. package/admin/assets/RulesEditor-BwQ6Bo1V.css +0 -1
  35. package/admin/assets/RulesEditor-u3ppR2Xl.js +0 -1
  36. package/admin/assets/aiPromptBuilder-4yeoONDF.js +0 -1
  37. package/build/lib/debugger.js +0 -1135
  38. package/build/lib/debugger.js.map +0 -1
@@ -1,9 +1,4 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.NodeInspector = void 0;
7
2
  /*
8
3
  * Copyright Node.js contributors. All rights reserved.
9
4
  *
@@ -25,318 +20,202 @@ exports.NodeInspector = void 0;
25
20
  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
26
21
  * IN THE SOFTWARE.
27
22
  */
23
+ var __importDefault = (this && this.__importDefault) || function (mod) {
24
+ return (mod && mod.__esModule) ? mod : { "default": mod };
25
+ };
26
+ Object.defineProperty(exports, "__esModule", { value: true });
27
+ /*
28
+ * The inspector process. It is forked by the adapter (see `debugStart` in main.ts) and starts the process to debug -
29
+ * the javascript adapter itself with `--debugScript <scriptId>` or another adapter - with an enabled inspector.
30
+ * It connects to it via the DevTools protocol and translates the commands of the GUI into protocol calls and back.
31
+ *
32
+ * The communication with the host happens via IPC with JSON strings:
33
+ * - host -> inspector: `DebugCommand`
34
+ * - inspector -> host: `CommandToHost`
35
+ */
28
36
  const node_child_process_1 = require("node:child_process");
29
- const node_events_1 = require("node:events");
30
- const node_net_1 = require("node:net");
31
- const node_util_1 = require("node:util");
32
- const node_path_1 = require("node:path");
33
37
  const node_fs_1 = require("node:fs");
38
+ const node_path_1 = require("node:path");
39
+ const node_url_1 = require("node:url");
40
+ const node_util_1 = require("node:util");
34
41
  // @ts-expect-error no types available
35
42
  const inspect_client_1 = __importDefault(require("node-inspect/lib/internal/inspect_client"));
36
- const debugger_1 = __importDefault(require("./debugger"));
37
- const breakOnStart = process.argv.includes('--breakOnStart');
38
43
  const debuglog = (0, node_util_1.debuglog)('inspect');
39
- let inspector;
40
- let scriptToDebug = ''; // script.js.yy
41
- let instanceToDebug = ''; // adapter.X
42
- let alreadyPausedOnFirstLine = false;
44
+ /** Stay paused on the first line of the debugged process (used for the debugging of adapter instances) */
45
+ const breakOnStart = process.argv.includes('--breakOnStart');
46
+ /**
47
+ * By debugging of a script, stepping and "stop on exception" should not stop in the node internals,
48
+ * in 3rd party modules and in the adapter itself (e.g., in sandbox.js)
49
+ */
50
+ const SCRIPT_BLACKBOX_PATTERNS = ['^node:', '/node_modules/', '[iI]o[bB]roker\\.javascript/build/'];
43
51
  class StartupError extends Error {
44
52
  constructor(message) {
45
53
  super(message);
46
54
  this.name = 'StartupError';
47
55
  }
48
56
  }
49
- function portIsFree(host, port, timeout = 9999) {
50
- if (port === 0) {
51
- // Binding to a random port.
52
- return Promise.resolve();
57
+ let inspector;
58
+ /** ID of the debugged script, like `script.js.myScript` */
59
+ let scriptToDebug = '';
60
+ /** Main file of the debugged adapter */
61
+ let instanceToDebug = '';
62
+ function sendToHost(data) {
63
+ if (data.cmd === 'error') {
64
+ console.error(`[DEBUGGER] ${data.error}`);
53
65
  }
54
- const retryDelay = 150;
55
- let didTimeOut = false;
56
- return new Promise((resolve, reject) => {
57
- setTimeout(() => {
58
- didTimeOut = true;
59
- reject(new StartupError(`Timeout (${timeout}) waiting for ${host}:${port} to be free`));
60
- }, timeout);
61
- function pingPort() {
62
- if (didTimeOut) {
63
- return;
64
- }
65
- const socket = (0, node_net_1.connect)(port, host);
66
- let didRetry = false;
67
- function retry() {
68
- if (!didRetry && !didTimeOut) {
69
- didRetry = true;
70
- setTimeout(pingPort, retryDelay);
71
- }
72
- }
73
- socket.on('error', (error) => {
74
- if (error.code === 'ECONNREFUSED') {
75
- resolve();
76
- }
77
- else {
78
- retry();
79
- }
80
- });
81
- socket.on('connect', () => {
82
- socket.destroy();
83
- retry();
84
- });
66
+ try {
67
+ process.send?.(JSON.stringify(data));
68
+ }
69
+ catch (e) {
70
+ // The host is not reachable anymore
71
+ console.error(`[DEBUGGER] Cannot send to host: ${e}`);
72
+ }
73
+ }
74
+ function errorToString(e) {
75
+ if (e instanceof Error) {
76
+ return e.message;
77
+ }
78
+ return typeof e === 'string' ? e : JSON.stringify(e);
79
+ }
80
+ function reportError(text, e) {
81
+ sendToHost({ cmd: 'error', error: e === undefined ? text : `${text}: ${errorToString(e)}` });
82
+ }
83
+ function remoteObjectToString(obj) {
84
+ if (!obj) {
85
+ return '';
86
+ }
87
+ if (obj.value !== undefined) {
88
+ return typeof obj.value === 'string' ? obj.value : JSON.stringify(obj.value);
89
+ }
90
+ return obj.unserializableValue || obj.description || obj.type;
91
+ }
92
+ function consoleTypeToSeverity(type) {
93
+ switch (type) {
94
+ case 'warning':
95
+ return 'warn';
96
+ case 'error':
97
+ case 'assert':
98
+ return 'error';
99
+ case 'debug':
100
+ return 'debug';
101
+ default:
102
+ return 'info';
103
+ }
104
+ }
105
+ /** Values like NaN, Infinity, -0 or BigInt cannot be transferred as JSON value */
106
+ function toCallArgument(value) {
107
+ if (typeof value === 'number' && (!Number.isFinite(value) || Object.is(value, -0))) {
108
+ return { unserializableValue: Object.is(value, -0) ? '-0' : String(value) };
109
+ }
110
+ if (typeof value === 'bigint') {
111
+ return { unserializableValue: `${value}n` };
112
+ }
113
+ return value === undefined ? {} : { value };
114
+ }
115
+ function isUrlOfFile(url, file) {
116
+ let path = url;
117
+ if (url.startsWith('file://')) {
118
+ try {
119
+ path = (0, node_url_1.fileURLToPath)(url);
85
120
  }
86
- pingPort();
87
- });
121
+ catch {
122
+ return false;
123
+ }
124
+ }
125
+ path = (0, node_path_1.normalize)(path);
126
+ file = (0, node_path_1.normalize)(file);
127
+ return process.platform === 'win32' ? path.toLowerCase() === file.toLowerCase() : path === file;
88
128
  }
89
- function runScript(script, scriptArgs, inspectHost, inspectPort, childPrint) {
90
- script = (0, node_path_1.normalize)(script);
91
- return portIsFree(inspectHost, inspectPort).then(() => {
92
- return new Promise(resolve => {
93
- const args = [breakOnStart ? `--inspect-brk=${inspectPort}` : `--inspect=${inspectPort}`].concat([script], scriptArgs || []);
94
- const child = (0, node_child_process_1.spawn)(process.execPath, args);
95
- child.stdout.setEncoding('utf8');
96
- child.stderr.setEncoding('utf8');
97
- child.stdout.on('data', childPrint);
98
- child.stderr.on('data', text => childPrint(text, true));
99
- let output = '';
100
- function waitForListenHint(text) {
101
- output += text;
102
- const res = /Debugger listening on ws:\/\/\[?(.+?)]?:(\d+)\//.exec(output);
103
- if (res) {
104
- const host = res[1];
105
- const port = Number.parseInt(res[2]);
106
- child.stderr.removeListener('data', waitForListenHint);
107
- resolve([child, port, host]);
108
- }
109
- }
110
- child.stderr.on('data', waitForListenHint);
111
- });
129
+ function createDomain(domain, client) {
130
+ return new Proxy({}, {
131
+ get: (_target, method) =>
132
+ // the object must not look like a promise
133
+ typeof method === 'string' && method !== 'then'
134
+ ? (params) => client.callMethod(`${domain}.${method}`, params)
135
+ : undefined,
112
136
  });
113
137
  }
114
- function createAgentProxy(domain, client) {
115
- const agent = new node_events_1.EventEmitter();
116
- agent.then = (...args) => {
117
- // TODO: potentially fetch the protocol and pretty-print it here.
118
- const descriptor = {
119
- [node_util_1.inspect.custom](_depth, { stylize }) {
120
- return stylize(`[Agent ${domain}]`, 'special');
121
- },
122
- };
123
- return Promise.resolve(descriptor).then(...args);
124
- };
125
- return new Proxy(agent, {
126
- get(target, name) {
127
- if (name in target) {
128
- return target[name];
138
+ function runScript(script, scriptArgs, childPrint) {
139
+ return new Promise((resolve, reject) => {
140
+ // Always break on start: the process must not run before the debugger is connected, else it could
141
+ // execute the `debugger;` statement of the script before. Port 0 means any free port.
142
+ const child = (0, node_child_process_1.spawn)(process.execPath, ['--inspect-brk=127.0.0.1:0', (0, node_path_1.normalize)(script), ...scriptArgs]);
143
+ child.stdout.setEncoding('utf8');
144
+ child.stderr.setEncoding('utf8');
145
+ child.stdout.on('data', (text) => childPrint(text));
146
+ child.stderr.on('data', (text) => childPrint(text, true));
147
+ let output = '';
148
+ const timeout = setTimeout(() => {
149
+ child.stderr.removeListener('data', waitForListenHint);
150
+ child.kill();
151
+ reject(new StartupError('Timeout by starting of the process to debug'));
152
+ }, 10_000);
153
+ function waitForListenHint(text) {
154
+ output += text;
155
+ const res = /Debugger listening on ws:\/\/\[?(.+?)]?:(\d+)\//.exec(output);
156
+ if (res) {
157
+ clearTimeout(timeout);
158
+ child.stderr.removeListener('data', waitForListenHint);
159
+ resolve([child, parseInt(res[2], 10), res[1]]);
129
160
  }
130
- return function callVirtualMethod(params) {
131
- return client.callMethod(`${domain}.${name}`, params);
132
- };
133
- },
161
+ }
162
+ child.stderr.on('data', waitForListenHint);
163
+ child.on('error', error => {
164
+ clearTimeout(timeout);
165
+ reject(new StartupError(`Cannot start the process to debug: ${error.message}`));
166
+ });
134
167
  });
135
168
  }
136
169
  class NodeInspector {
137
- options;
138
- stdin;
139
- stdout;
140
- scripts;
141
- delayedContext;
142
- paused;
143
- child;
144
- _runScript;
145
- client;
146
170
  Debugger;
147
- HeapProfiler;
148
- Profiler;
149
171
  Runtime;
150
- handleDebugEvent;
172
+ client = new inspect_client_1.default();
173
+ file;
174
+ args;
175
+ child = null;
176
+ /** The protocol does not deliver the URLs in the call frames anymore, so remember them */
177
+ scriptUrls = {};
151
178
  mainScriptId;
152
- mainFile;
153
- repl;
154
- constructor(options, stdin, stdout) {
155
- this.options = options;
156
- this.stdin = stdin;
157
- this.stdout = stdout;
158
- this.scripts = {};
159
- this.delayedContext = null;
160
- this.paused = true;
161
- this.child = null;
162
- if (options.script) {
163
- this._runScript = runScript.bind(null, options.script, options.scriptArgs, options.host, options.port, this.childPrint.bind(this));
164
- }
165
- else {
166
- this._runScript = () => Promise.resolve([null, options.port, options.host]);
167
- }
168
- this.client = new inspect_client_1.default();
169
- this.Debugger = createAgentProxy('Debugger', this.client);
170
- this.HeapProfiler = createAgentProxy('HeapProfiler', this.client);
171
- this.Profiler = createAgentProxy('Profiler', this.client);
172
- this.Runtime = createAgentProxy('Runtime', this.client);
173
- this.handleDebugEvent = (fullName, params) => {
174
- const [domain, name] = fullName.split('.');
175
- if (domain === 'Debugger' && name === 'scriptParsed') {
176
- // console.log(`Parsed: ${params.url}`);
177
- if ((scriptToDebug && params.url.includes(scriptToDebug)) ||
178
- (instanceToDebug && params.url.includes(instanceToDebug))) {
179
- console.log(`My scriptID: ${params.scriptId}`);
180
- this.mainScriptId = params.scriptId;
181
- this.mainFile = params.url.replace('file:///', '');
182
- // load text of a script
183
- this.scripts[this.mainScriptId] = this.Debugger.getScriptSource({
184
- scriptId: this.mainScriptId,
185
- }).then((script) => ({
186
- script: script.scriptSource,
187
- scriptId: this.mainScriptId,
188
- }));
189
- // sometimes the pause event comes before scriptParsed
190
- if (this.delayedContext) {
191
- console.log('Send to debugger: readyToDebug');
192
- void this.scripts[this.mainScriptId]?.then(data => {
193
- // console.log(`Send to debugger: readyToDebug ${JSON.stringify(data)}`);
194
- sendToHost({
195
- cmd: 'readyToDebug',
196
- scriptId: this.mainScriptId,
197
- script: data.script,
198
- context: this.delayedContext,
199
- url: this.mainFile,
200
- });
201
- this.delayedContext = null;
202
- });
203
- }
204
- }
205
- return;
206
- }
207
- else if (domain === 'Debugger' && name === 'resumed') {
208
- this.Debugger.emit(name, params);
209
- sendToHost({ cmd: 'resumed', context: params });
210
- return;
211
- }
212
- else if (domain === 'Debugger' && name === 'paused') {
213
- console.log(`PAUSED!! ${alreadyPausedOnFirstLine}`);
214
- if (!alreadyPausedOnFirstLine && params.reason === 'exception') {
215
- // ignore all exceptions by start
216
- this.Debugger.resume();
217
- return;
218
- }
219
- //console.warn(`${fullName}: => \n${JSON.stringify(params, null, 2)}`);
220
- this.Debugger.emit(name, params);
221
- if (!alreadyPausedOnFirstLine) {
222
- alreadyPausedOnFirstLine = true;
223
- // sometimes the pause event comes before scriptParsed
224
- if (this.mainScriptId && this.scripts[this.mainScriptId]) {
225
- void this.scripts[this.mainScriptId]?.then(data => sendToHost({
226
- cmd: 'readyToDebug',
227
- scriptId: data.scriptId,
228
- script: data.script,
229
- context: params,
230
- url: this.mainFile,
231
- }));
232
- }
233
- else {
234
- // store context to send it later when script ID will be known
235
- this.delayedContext = params;
236
- console.log('PAUSED, but no scriptId');
237
- }
238
- }
239
- else {
240
- // this.scripts[params.loca]
241
- // .then(data =>
242
- sendToHost({ cmd: 'paused', context: params });
243
- }
244
- return;
245
- }
246
- if (domain === 'Runtime') {
247
- //console.warn(`${fullName}: => \n${JSON.stringify(params, null, 2)}`);
248
- }
249
- if (domain === 'Runtime' && name === 'consoleAPICalled') {
250
- const text = params.args[0].value;
251
- if (instanceToDebug) {
252
- sendToHost({
253
- cmd: 'log',
254
- severity: params.type === 'warning' ? 'warn' : 'error',
255
- text,
256
- ts: Date.now(),
257
- });
258
- }
259
- else if (text.includes(`$$${scriptToDebug}$$`)) {
260
- console.log(`${fullName} [${params.executionContextId}]: => ${text}`);
261
- const [severity, _text] = text.split(`$$${scriptToDebug}$$`);
262
- sendToHost({
263
- cmd: 'log',
264
- severity: severity,
265
- text: _text,
266
- ts: params.args[1] && params.args[1].value ? params.args[1].value : Date.now(),
267
- });
268
- }
269
- else if (params.type === 'warning' || params.type === 'error') {
270
- sendToHost({
271
- cmd: 'log',
272
- severity: params.type === 'warning' ? 'warn' : 'error',
273
- text,
274
- ts: Date.now(),
275
- });
276
- }
277
- return;
179
+ mainFile = '';
180
+ mainScriptSource = null;
181
+ /** Sometimes the first pause comes before the main script is parsed */
182
+ delayedContext = null;
183
+ pausedOnFirstLine = false;
184
+ /** Execution context of the debugged script */
185
+ scriptContextId;
186
+ finished = false;
187
+ constructor(file, args) {
188
+ this.file = file;
189
+ this.args = args;
190
+ this.Debugger = createDomain('Debugger', this.client);
191
+ this.Runtime = createDomain('Runtime', this.client);
192
+ this.client.on('debugEvent', (fullName, params) => this.handleDebugEvent(fullName, params || {}));
193
+ }
194
+ async start() {
195
+ const [child, port, host] = await runScript(this.file, this.args, (text, isError) => this.childPrint(text, isError));
196
+ this.child = child;
197
+ child.on('exit', code => this.finish(`Process exited with code ${code}`));
198
+ for (let attempt = 1;; attempt++) {
199
+ try {
200
+ await this.client.connect(port, host);
201
+ break;
278
202
  }
279
- else if (domain === 'Runtime' && (params.id === 2 || params.executionContextId === 2)) {
280
- if (name === 'executionContextCreated') {
281
- console.warn(`${fullName}: =>\n${JSON.stringify(params, null, 2)}`);
282
- }
283
- else if (name === 'executionContextDestroyed') {
284
- console.warn(`${fullName}: =>\n${JSON.stringify(params, null, 2)}`);
285
- sendToHost({ cmd: 'finished', context: params });
203
+ catch (e) {
204
+ debuglog(`Connection attempt #${attempt} failed: ${e}`);
205
+ if (attempt >= 10) {
206
+ throw new StartupError(`Cannot connect to the debugger on ${host}:${port}`);
286
207
  }
287
- return;
288
- }
289
- else if (domain === 'Runtime' &&
290
- name === 'executionContextDestroyed' &&
291
- params.executionContextId === 1) {
292
- sendToHost({ cmd: 'finished', context: params });
293
- console.log('Exited!');
294
- setTimeout(() => process.exit(125), 200);
208
+ await new Promise(resolve => setTimeout(resolve, 500));
295
209
  }
296
- else if (domain === 'Debugger' && name === 'scriptFailedToParse') {
297
- // ignore
298
- return;
299
- }
300
- console.warn(`${fullName}: =>\n${JSON.stringify(params, null, 2)}`);
301
- /*if (domain in this) {
302
- this[domain].emit(name, params);
303
- }*/
304
- };
305
- this.client.on('debugEvent', this.handleDebugEvent);
306
- const startRepl = (0, debugger_1.default)(this);
307
- // Handle all possible exits
308
- process.on('exit', () => this.killChild());
309
- process.once('SIGTERM', process.exit.bind(process, 0));
310
- process.once('SIGHUP', process.exit.bind(process, 0));
311
- this.run()
312
- .then(() => startRepl())
313
- .then(repl => {
314
- this.repl = repl;
315
- this.repl.on('exit', () => process.exit(0));
316
- this.paused = false;
317
- })
318
- .then(null, error => process.nextTick(() => {
319
- throw error;
320
- }));
321
- }
322
- suspendReplWhile(fn) {
323
- if (this.repl) {
324
- this.repl.pause();
325
210
  }
326
- this.stdin.pause();
327
- this.paused = true;
328
- return new Promise(resolve => resolve(fn()))
329
- .then(() => {
330
- this.paused = false;
331
- if (this.repl) {
332
- this.repl.resume();
333
- this.repl.displayPrompt();
334
- }
335
- this.stdin.resume();
336
- })
337
- .then(null, error => process.nextTick(() => {
338
- throw error;
339
- }));
211
+ await Promise.all([
212
+ this.Runtime.enable(),
213
+ this.Debugger.enable(),
214
+ this.Debugger.setPauseOnExceptions({ state: 'none' }),
215
+ this.Debugger.setAsyncCallStackDepth({ maxDepth: 1 }),
216
+ this.Debugger.setBlackboxPatterns({ patterns: scriptToDebug ? SCRIPT_BLACKBOX_PATTERNS : [] }),
217
+ this.Runtime.runIfWaitingForDebugger(),
218
+ ]);
340
219
  }
341
220
  killChild() {
342
221
  this.client.reset();
@@ -345,384 +224,420 @@ class NodeInspector {
345
224
  this.child = null;
346
225
  }
347
226
  }
348
- run() {
349
- this.killChild();
350
- return this._runScript().then(([child, port, host]) => {
351
- this.child = child;
352
- let connectionAttempts = 0;
353
- const attemptConnect = () => {
354
- ++connectionAttempts;
355
- debuglog('connection attempt #%d', connectionAttempts);
356
- this.stdout.write('.');
357
- return this.client.connect(port, host).then(() => {
358
- debuglog('connection established');
359
- this.stdout.write(' ok');
360
- }, (error) => {
361
- debuglog('connect failed', error);
362
- // If it's failed to connect 10 times, then print a failed message
363
- if (connectionAttempts >= 10) {
364
- this.stdout.write(' failed to connect, please retry\n');
365
- process.exit(1);
366
- }
367
- return new Promise(resolve => setTimeout(resolve, 500)).then(attemptConnect);
368
- });
369
- };
370
- this.print(`connecting to ${host}:${port} ..`, true);
371
- return attemptConnect();
372
- });
373
- }
374
- clearLine() {
375
- if (this.stdout.isTTY) {
376
- this.stdout.cursorTo(0);
377
- this.stdout.clearLine(1);
378
- }
379
- else {
380
- this.stdout.write('\b');
227
+ finish(reason) {
228
+ if (this.finished) {
229
+ return;
381
230
  }
382
- }
383
- print(text, oneLine = false) {
384
- this.clearLine();
385
- this.stdout.write(oneLine ? text : `${text}\n`);
231
+ this.finished = true;
232
+ debuglog(reason);
233
+ sendToHost({ cmd: 'finished', text: reason });
234
+ this.killChild();
235
+ setTimeout(() => process.exit(0), 200);
386
236
  }
387
237
  childPrint(text, isError) {
388
- isError &&
389
- this.print(text
390
- .toString()
238
+ if (isError) {
239
+ const lines = text
391
240
  .split(/\r\n|\r|\n/g)
392
- .filter((chunk) => !!chunk)
393
- .map((chunk) => `< ${chunk}`)
394
- .join('\n'));
395
- if (!this.paused) {
396
- this.repl?.displayPrompt(true);
241
+ .filter(line => !!line)
242
+ .map(line => `< ${line}`);
243
+ lines.length && process.stdout.write(`${lines.join('\n')}\n`);
397
244
  }
398
- if (/Waiting for the debugger to disconnect\.\.\.\n$/.test(text)) {
399
- this.killChild();
400
- sendToHost({ cmd: 'finished', text });
245
+ // The debugged process ended, but it waits till the debugger disconnects
246
+ if (text.includes('Waiting for the debugger to disconnect')) {
247
+ this.finish('Process finished');
401
248
  }
402
249
  }
403
- }
404
- exports.NodeInspector = NodeInspector;
405
- function parseArgv([target, ...args]) {
406
- let host = '127.0.0.1';
407
- let port = 9229;
408
- let isRemote = false;
409
- let script = target;
410
- let scriptArgs = args;
411
- const hostMatch = target.match(/^([^:]+):(\d+)$/);
412
- const portMatch = target.match(/^--port=(\d+)$/);
413
- if (hostMatch) {
414
- // Connecting to remote debugger
415
- // `node-inspect localhost:9229`
416
- host = hostMatch[1];
417
- port = parseInt(hostMatch[2], 10);
418
- isRemote = true;
419
- script = null;
420
- }
421
- else if (portMatch) {
422
- // start debugee on custom port
423
- // `node inspect --port=9230 script.js`
424
- port = parseInt(portMatch[1], 10);
425
- script = args[0];
426
- scriptArgs = args.slice(1);
427
- }
428
- else if (args.length === 1 && /^\d+$/.test(args[0]) && target === '-p') {
429
- // Start debugger against a given pid
430
- const pid = parseInt(args[0], 10);
431
- try {
432
- // Windows does not support UNIX signals. To enable debugging, you can use an undocumented API function
433
- // https://github.com/node-inspector/node-inspector?tab=readme-ov-file#windows
434
- // @ts-expect-error undocumented function
435
- process._debugProcess(pid);
250
+ handleDebugEvent(fullName, params) {
251
+ switch (fullName) {
252
+ case 'Debugger.scriptParsed':
253
+ this.onScriptParsed(params);
254
+ break;
255
+ case 'Debugger.paused':
256
+ this.onPaused(params);
257
+ break;
258
+ case 'Debugger.resumed':
259
+ sendToHost({ cmd: 'resumed', context: params });
260
+ break;
261
+ case 'Runtime.consoleAPICalled':
262
+ this.onConsoleApiCalled(params);
263
+ break;
264
+ case 'Runtime.executionContextCreated':
265
+ // main.ts gives the context of a script the name of the script
266
+ if (scriptToDebug && params.context?.name === scriptToDebug) {
267
+ this.scriptContextId = params.context.id;
268
+ }
269
+ break;
270
+ case 'Runtime.executionContextDestroyed':
271
+ if (params.executionContextId === 1) {
272
+ // the main context of the debugged process is gone
273
+ this.finish('Process finished');
274
+ }
275
+ else if (this.scriptContextId !== undefined && params.executionContextId === this.scriptContextId) {
276
+ // the script was stopped, but the process is still running
277
+ sendToHost({ cmd: 'finished', context: params });
278
+ }
279
+ break;
280
+ default:
281
+ debuglog(`${fullName}: ${JSON.stringify(params)}`);
282
+ break;
436
283
  }
437
- catch (e) {
438
- if (e && e.code === 'ESRCH') {
439
- console.error(`Target process: ${pid} doesn't exist.`);
440
- process.exit(1);
441
- }
442
- throw e;
284
+ }
285
+ onScriptParsed(params) {
286
+ const { scriptId, url } = params;
287
+ if (!scriptId || !url) {
288
+ return;
443
289
  }
444
- script = null;
445
- isRemote = true;
446
- }
447
- return {
448
- host,
449
- port,
450
- isRemote,
451
- script,
452
- scriptArgs,
453
- };
454
- }
455
- function startInspect(argv = process.argv.slice(2), stdin = process.stdin, stdout = process.stdout) {
456
- /*
457
- if (argv.length < 1) {
458
- const invokedAs = runAsStandalone ? 'node-inspect' : `${process.argv0} ${process.argv[1]}`;
459
-
460
- console.error(`Usage: ${invokedAs} script.js`);
461
- console.error(` ${invokedAs} <host>:<port>`);
462
- console.error(` ${invokedAs} -p <pid>`);
463
- process.exit(1);
464
- }
465
- */
466
- const options = parseArgv(argv);
467
- inspector = new NodeInspector(options, stdin, stdout);
468
- stdin.resume();
469
- function handleUnexpectedError(e) {
470
- if (!(e instanceof StartupError)) {
471
- console.error('There was an internal error in node-inspect. Please report this bug.');
472
- console.error(e.message);
473
- console.error(e.stack);
290
+ this.scriptUrls[scriptId] = url;
291
+ const isMain = scriptToDebug ? url === scriptToDebug : !!instanceToDebug && isUrlOfFile(url, instanceToDebug);
292
+ if (!isMain) {
293
+ return;
474
294
  }
475
- else {
476
- console.error(e.message);
295
+ debuglog(`Script to debug parsed: ${scriptId}`);
296
+ this.mainScriptId = scriptId;
297
+ this.mainFile = url.replace('file:///', '');
298
+ this.mainScriptSource = this.Debugger.getScriptSource({ scriptId }).then((result) => result.scriptSource);
299
+ if (this.delayedContext) {
300
+ const context = this.delayedContext;
301
+ this.delayedContext = null;
302
+ void this.sendReadyToDebug(context);
477
303
  }
478
- inspector.child?.kill();
479
- process.exit(1);
480
- }
481
- process.on('uncaughtException', handleUnexpectedError);
482
- }
483
- function extractErrorMessage(stack) {
484
- if (!stack) {
485
- return '<unknown>';
486
304
  }
487
- const m = stack.match(/^\w+: ([^\n]+)/);
488
- return m ? m[1] : stack;
489
- }
490
- function convertResultToError(result) {
491
- const { className, description } = result;
492
- const err = new Error(extractErrorMessage(description));
493
- err.stack = description;
494
- Object.defineProperty(err, 'name', { value: className });
495
- return err;
496
- }
497
- process.on('message', (message) => {
498
- if (typeof message === 'string') {
499
- try {
500
- message = JSON.parse(message);
305
+ onPaused(params) {
306
+ if (!this.pausedOnFirstLine) {
307
+ if (params.reason === 'Break on start' && !breakOnStart) {
308
+ // the process was started with --inspect-brk only to have time to set up the debugger
309
+ this.Debugger.resume().catch(e => reportError('Cannot resume', e));
310
+ return;
311
+ }
312
+ if (params.reason === 'exception') {
313
+ // ignore all exceptions till the start of the script
314
+ this.Debugger.resume().catch(e => reportError('Cannot resume', e));
315
+ return;
316
+ }
501
317
  }
502
- catch {
503
- return console.error(`Cannot parse: ${JSON.stringify(message)}`);
318
+ this.enrichCallFrames(params);
319
+ if (this.pausedOnFirstLine &&
320
+ scriptToDebug &&
321
+ params.reason === 'step' &&
322
+ !params.callFrames?.[0]?.url?.startsWith('script.js.')) {
323
+ // Stepping left the script, e.g., at the end of a callback. The blackbox patterns do not cover
324
+ // all internals of node.js, so continue the execution instead of stopping there
325
+ this.Debugger.resume().catch(e => reportError('Cannot resume', e));
326
+ return;
504
327
  }
505
- }
506
- processCommand(message);
507
- });
508
- sendToHost({ cmd: 'ready' });
509
- function processCommand(data) {
510
- console.log(`[DEBUGGER]: processCommand: ${JSON.stringify(data)}`);
511
- if (data.cmd === 'start') {
512
- scriptToDebug = data.scriptName;
513
- // we can request the script by name or iobroker instance to debug
514
- if (scriptToDebug) {
515
- startInspect([
516
- `${__dirname}/../main.js`,
517
- (data.instance || 0).toString(),
518
- '--debug',
519
- '--debugScript',
520
- scriptToDebug,
521
- ]);
328
+ if (this.pausedOnFirstLine) {
329
+ sendToHost({ cmd: 'paused', context: params });
522
330
  }
523
331
  else {
524
- instanceToDebug = data.adapterInstance;
525
- const [adapter, instance] = instanceToDebug.split('.');
526
- let file;
527
- try {
528
- file = require.resolve(`iobroker.${adapter}`);
332
+ this.pausedOnFirstLine = true;
333
+ if (this.mainScriptSource) {
334
+ void this.sendReadyToDebug(params);
529
335
  }
530
- catch (e) {
531
- // try to locate in the same dir
532
- const dir = (0, node_path_1.normalize)((0, node_path_1.join)(__dirname, '..', `iobroker.${adapter}`));
533
- if ((0, node_fs_1.existsSync)(dir)) {
534
- const pack = require((0, node_path_1.join)(dir, 'package.json'));
535
- if ((0, node_fs_1.existsSync)((0, node_path_1.join)(dir, pack.main || `${adapter}.js`))) {
536
- file = (0, node_path_1.join)(dir, pack.main || `${adapter}.js`);
537
- }
538
- }
539
- if (!file) {
540
- sendToHost({ cmd: 'error', error: `Cannot locate iobroker.${adapter}`, errorContext: e });
541
- setTimeout(() => {
542
- sendToHost({ cmd: 'finished', context: `Cannot locate iobroker.${adapter}` });
543
- setTimeout(() => process.exit(124), 500);
544
- }, 200);
545
- return;
546
- }
336
+ else {
337
+ // store the context to send it, when the main script is parsed
338
+ this.delayedContext = params;
547
339
  }
548
- file = file.replace(/\\/g, '/');
549
- instanceToDebug = file;
550
- console.log(`Start ${file} ${instance} --debug`);
551
- startInspect([file, instance, '--debug']);
552
340
  }
553
341
  }
554
- else if (data.cmd === 'end') {
555
- process.exit();
556
- }
557
- else if (data.cmd === 'source') {
558
- inspector.Debugger.getScriptSource({ scriptId: data.scriptId }).then((script) => sendToHost({ cmd: 'script', scriptId: data.scriptId, text: script.scriptSource }));
559
- }
560
- else if (data.cmd === 'cont') {
561
- inspector.Debugger.resume().catch((e) => sendToHost({ cmd: 'error', error: e }));
562
- }
563
- else if (data.cmd === 'next') {
564
- inspector.Debugger.stepOver().catch((e) => sendToHost({ cmd: 'error', error: e }));
565
- }
566
- else if (data.cmd === 'pause') {
567
- inspector.Debugger.pause().catch((e) => sendToHost({ cmd: 'error', error: e }));
568
- }
569
- else if (data.cmd === 'step') {
570
- inspector.Debugger.stepInto().catch((e) => sendToHost({ cmd: 'error', error: e }));
571
- }
572
- else if (data.cmd === 'out') {
573
- inspector.Debugger.stepOut().catch((e) => sendToHost({ cmd: 'error', error: e }));
574
- }
575
- else if (data.cmd === 'sb') {
576
- console.log(JSON.stringify(data));
577
- void Promise.all(data.breakpoints?.map((bp) => inspector.Debugger.setBreakpoint({
578
- location: {
579
- scriptId: bp.scriptId,
580
- lineNumber: bp.lineNumber,
581
- columnNumber: bp.columnNumber,
582
- },
583
- })
584
- .then((result) => ({
585
- id: result.breakpointId,
586
- location: result.actualLocation,
587
- }))
588
- .catch((e) => sendToHost({
589
- cmd: 'error',
590
- error: `Cannot set breakpoint: ${e}`,
591
- errorContext: e,
592
- bp,
593
- }))) || []).then(breakpoints => sendToHost({ cmd: 'sb', breakpoints }));
594
- }
595
- else if (data.cmd === 'cb') {
596
- void Promise.all(data.breakpoints?.map(breakpointId => inspector.Debugger.removeBreakpoint({ breakpointId })
597
- .then(() => breakpointId)
598
- .catch((e) => {
599
- console.error(`[DEBUGGER]: Cannot clear breakpoint: ${e}`);
342
+ async sendReadyToDebug(context) {
343
+ try {
344
+ const script = await this.mainScriptSource;
345
+ this.enrichCallFrames(context);
600
346
  sendToHost({
601
- cmd: 'error',
602
- error: `Cannot clear breakpoint: ${e}`,
603
- errorContext: e,
604
- id: breakpointId,
347
+ cmd: 'readyToDebug',
348
+ scriptId: this.mainScriptId,
349
+ script,
350
+ context,
351
+ url: this.mainFile,
605
352
  });
606
- })) || []).then(breakpoints => {
607
- console.error(`[DEBUGGER]: sent breakpoints ${JSON.stringify(breakpoints)}`);
608
- sendToHost({ cmd: 'cb', breakpoints });
353
+ }
354
+ catch (e) {
355
+ reportError('Cannot read the source of the script to debug', e);
356
+ }
357
+ }
358
+ enrichCallFrames(context) {
359
+ context.callFrames?.forEach(frame => {
360
+ if (!frame.url) {
361
+ frame.url = this.scriptUrls[frame.location.scriptId] || '';
362
+ }
609
363
  });
610
364
  }
611
- else if (data.cmd === 'watch') {
612
- void Promise.all(data.expressions?.map(expr => inspector.Debugger.watch(expr).catch((e) => sendToHost({
613
- cmd: 'error',
614
- error: `Cannot watch expr: ${e}`,
615
- errorContext: e,
616
- expr,
617
- }))) || []).then(() => console.log('Watch done'));
618
- }
619
- else if (data.cmd === 'unwatch') {
620
- void Promise.all(data.expressions?.map(expr => inspector.Debugger.unwatch(expr).catch((e) => sendToHost({
621
- cmd: 'error',
622
- error: `Cannot unwatch expr: ${e}`,
623
- errorContext: e,
624
- expr,
625
- }))) || []).then(() => console.log('Watch done'));
626
- }
627
- else if (data.cmd === 'scope') {
628
- void Promise.all(data.scopes
629
- ?.filter(scope => scope?.object?.objectId)
630
- .map(scope => inspector.Runtime.getProperties({
631
- objectId: scope.object.objectId,
632
- generatePreview: true,
633
- })
634
- .then((result) => {
635
- return { type: scope.type, properties: result };
636
- })
637
- .catch((e) => sendToHost({
638
- cmd: 'error',
639
- error: `Cannot get scopes expr: ${e}`,
640
- errorContext: e,
641
- }))) || []).then(scopes => sendToHost({ cmd: 'scope', scopes }));
642
- }
643
- else if (data.cmd === 'setValue') {
644
- inspector.Debugger.setVariableValue({
645
- variableName: data.variableName,
646
- scopeNumber: data.scopeNumber,
647
- newValue: data.newValue,
648
- callFrameId: data.callFrameId,
649
- })
650
- .catch((e) => {
651
- console.error(`Cannot setValue "${data.variableName}": ${e}`);
365
+ onConsoleApiCalled(params) {
366
+ const args = params.args || [];
367
+ if (instanceToDebug) {
652
368
  sendToHost({
653
- cmd: 'setValue',
654
- variableName: `Cannot setValue: ${e}`,
655
- errorContext: e,
369
+ cmd: 'log',
370
+ severity: consoleTypeToSeverity(params.type),
371
+ text: args.map(arg => remoteObjectToString(arg)).join(' '),
372
+ ts: Date.now(),
656
373
  });
657
- })
658
- .then(() => {
659
- console.log(`setValue "${data.variableName}" successful`);
374
+ return;
375
+ }
376
+ // The sandbox logs in debug mode as `console.log(`${severity}$$${scriptName}$$${message}`, Date.now())`
377
+ const text = remoteObjectToString(args[0]);
378
+ const marker = `$$${scriptToDebug}$$`;
379
+ const pos = text.indexOf(marker);
380
+ if (pos !== -1) {
660
381
  sendToHost({
661
- cmd: 'setValue',
662
- variableName: data.variableName,
663
- scopeNumber: data.scopeNumber,
664
- newValue: data.newValue,
665
- callFrameId: data.callFrameId,
382
+ cmd: 'log',
383
+ severity: text.substring(0, pos),
384
+ text: text.substring(pos + marker.length),
385
+ ts: typeof args[1]?.value === 'number' ? args[1].value : Date.now(),
666
386
  });
667
- });
387
+ }
388
+ else if (params.type === 'warning' || params.type === 'error') {
389
+ sendToHost({
390
+ cmd: 'log',
391
+ severity: consoleTypeToSeverity(params.type),
392
+ text: args.map(arg => remoteObjectToString(arg)).join(' '),
393
+ ts: Date.now(),
394
+ });
395
+ }
668
396
  }
669
- else if (data.cmd === 'expressions') {
670
- void Promise.all(data.expressions?.map(item => inspector.Debugger.evaluateOnCallFrame({
671
- callFrameId: data.callFrameId,
672
- expression: item.name,
673
- objectGroup: 'node-inspect',
674
- returnByValue: true,
675
- generatePreview: true,
676
- })
677
- // @ts-expect-error fix later
678
- .then(({ result, wasThrown }) => {
679
- if (wasThrown) {
680
- return { name: item.name, result: convertResultToError(result) };
397
+ }
398
+ function resolveAdapterMainFile(adapter) {
399
+ try {
400
+ return require.resolve(`iobroker.${adapter}`);
401
+ }
402
+ catch {
403
+ // try to locate it in the same node_modules directory as this adapter
404
+ const dir = (0, node_path_1.normalize)((0, node_path_1.join)(__dirname, '..', '..', '..', `iobroker.${adapter}`));
405
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(dir, 'package.json'))) {
406
+ try {
407
+ const pack = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(dir, 'package.json'), 'utf8'));
408
+ const main = (0, node_path_1.join)(dir, pack.main || `${adapter}.js`);
409
+ if ((0, node_fs_1.existsSync)(main)) {
410
+ return main;
411
+ }
681
412
  }
682
- return { name: item.name, result };
683
- })
684
- .catch((e) => sendToHost({
685
- cmd: 'expressions',
686
- variableName: `Cannot setValue: ${e}`,
687
- errorContext: e,
688
- }))) || []).then(expressions => {
689
- sendToHost({ cmd: 'expressions', expressions });
690
- });
413
+ catch {
414
+ // ignore
415
+ }
416
+ }
691
417
  }
692
- else if (data.cmd === 'stopOnException') {
693
- inspector.Debugger.setPauseOnExceptions({
694
- state: data.state ? 'all' : 'none',
695
- }).catch((e) => sendToHost({
696
- cmd: 'stopOnException',
697
- variableName: `Cannot stopOnException: ${e}`,
698
- errorContext: e,
699
- }));
700
- }
701
- else if (data.cmd === 'getPossibleBreakpoints') {
702
- inspector.Debugger.getPossibleBreakpoints({ start: data.start, end: data.end })
703
- .then((breakpoints) => sendToHost({ cmd: 'getPossibleBreakpoints', breakpoints: breakpoints.locations }))
704
- .catch((e) => sendToHost({
705
- cmd: 'getPossibleBreakpoints',
706
- variableName: `Cannot getPossibleBreakpoints: ${e}`,
707
- errorContext: e,
708
- }));
418
+ return undefined;
419
+ }
420
+ function startDebugging(data) {
421
+ if (inspector) {
422
+ reportError('The debugger is already started');
423
+ return;
709
424
  }
710
- else {
711
- console.error(`Unknown command: ${JSON.stringify(data)}`);
425
+ if (data.scriptName) {
426
+ scriptToDebug = data.scriptName;
427
+ inspector = new NodeInspector((0, node_path_1.join)(__dirname, '..', 'main.js'), [
428
+ (data.instance || 0).toString(),
429
+ '--debug',
430
+ '--debugScript',
431
+ scriptToDebug,
432
+ ]);
712
433
  }
434
+ else {
435
+ const [adapter, instance] = (data.adapterInstance || '').split('.');
436
+ const file = resolveAdapterMainFile(adapter);
437
+ if (!file) {
438
+ reportError(`Cannot locate iobroker.${adapter}`);
439
+ setTimeout(() => {
440
+ sendToHost({ cmd: 'finished', context: `Cannot locate iobroker.${adapter}` });
441
+ setTimeout(() => process.exit(0), 500);
442
+ }, 200);
443
+ return;
444
+ }
445
+ instanceToDebug = file;
446
+ inspector = new NodeInspector(file, [instance || '0', '--debug']);
447
+ }
448
+ inspector.start().catch((e) => {
449
+ reportError(e instanceof StartupError
450
+ ? e.message
451
+ : `Internal error in inspector: ${e instanceof Error ? e.stack : errorToString(e)}`);
452
+ inspector?.killChild();
453
+ setTimeout(() => process.exit(1), 200);
454
+ });
713
455
  }
714
- function sendToHost(data) {
715
- if (data.cmd === 'error') {
716
- console.error(data.text);
717
- if (data.expr) {
718
- console.error(`[EXPRESSION] ${JSON.stringify(data.expr)}`);
456
+ async function processCommand(data) {
457
+ debuglog(`processCommand: ${JSON.stringify(data)}`);
458
+ if (data.cmd === 'start') {
459
+ startDebugging(data);
460
+ return;
461
+ }
462
+ if (data.cmd === 'end') {
463
+ process.exit(0);
464
+ }
465
+ if (!inspector) {
466
+ reportError(`Cannot process "${data.cmd}": the debugger is not started`);
467
+ return;
468
+ }
469
+ const { Debugger, Runtime } = inspector;
470
+ switch (data.cmd) {
471
+ case 'source':
472
+ try {
473
+ const { scriptSource } = (await Debugger.getScriptSource({
474
+ scriptId: data.scriptId,
475
+ }));
476
+ sendToHost({ cmd: 'script', scriptId: data.scriptId, text: scriptSource });
477
+ }
478
+ catch (e) {
479
+ reportError(`Cannot read the source of script ${data.scriptId}`, e);
480
+ }
481
+ break;
482
+ case 'cont':
483
+ case 'next':
484
+ case 'step':
485
+ case 'out':
486
+ case 'pause': {
487
+ const method = { cont: 'resume', next: 'stepOver', step: 'stepInto', out: 'stepOut', pause: 'pause' }[data.cmd];
488
+ try {
489
+ await Debugger[method]();
490
+ }
491
+ catch (e) {
492
+ reportError(`Cannot execute "${data.cmd}"`, e);
493
+ }
494
+ break;
719
495
  }
720
- if (data.bp) {
721
- console.error(`[BP] ${JSON.stringify(data.bp)}`);
496
+ case 'sb': {
497
+ const breakpoints = await Promise.all((data.breakpoints || []).map(async (bp) => {
498
+ try {
499
+ const result = (await Debugger.setBreakpoint({
500
+ location: {
501
+ scriptId: bp.scriptId,
502
+ lineNumber: bp.lineNumber,
503
+ columnNumber: bp.columnNumber,
504
+ },
505
+ }));
506
+ return { id: result.breakpointId, location: result.actualLocation };
507
+ }
508
+ catch (e) {
509
+ reportError(`Cannot set breakpoint on line ${bp.lineNumber + 1}`, e);
510
+ return null;
511
+ }
512
+ }));
513
+ sendToHost({ cmd: 'sb', breakpoints: breakpoints.filter(bp => bp) });
514
+ break;
722
515
  }
723
- }
724
- if (process.send) {
725
- process.send(JSON.stringify(data));
516
+ case 'cb': {
517
+ const breakpointIds = await Promise.all((data.breakpoints || []).map(async (breakpointId) => {
518
+ try {
519
+ await Debugger.removeBreakpoint({ breakpointId });
520
+ }
521
+ catch (e) {
522
+ // The breakpoint does not exist (anymore), so it is removed anyway
523
+ debuglog(`Cannot clear breakpoint ${breakpointId}: ${e}`);
524
+ }
525
+ return breakpointId;
526
+ }));
527
+ sendToHost({ cmd: 'cb', breakpoints: breakpointIds });
528
+ break;
529
+ }
530
+ case 'scope': {
531
+ const scopes = await Promise.all((data.scopes || [])
532
+ .filter(scope => scope?.object?.objectId)
533
+ .map(async (scope) => {
534
+ try {
535
+ const properties = (await Runtime.getProperties({
536
+ objectId: scope.object.objectId,
537
+ generatePreview: true,
538
+ }));
539
+ return { type: scope.type, name: scope.name, index: scope.index, properties };
540
+ }
541
+ catch (e) {
542
+ // e.g., the execution was resumed in the meantime
543
+ debuglog(`Cannot read scope: ${e}`);
544
+ return null;
545
+ }
546
+ }));
547
+ sendToHost({ cmd: 'scope', scopes: scopes.filter(scope => scope) });
548
+ break;
549
+ }
550
+ case 'setValue':
551
+ try {
552
+ await Debugger.setVariableValue({
553
+ variableName: data.variableName,
554
+ scopeNumber: data.scopeNumber,
555
+ newValue: toCallArgument(data.newValue?.value),
556
+ callFrameId: data.callFrameId,
557
+ });
558
+ sendToHost({
559
+ cmd: 'setValue',
560
+ variableName: data.variableName,
561
+ scopeNumber: data.scopeNumber,
562
+ newValue: data.newValue,
563
+ callFrameId: data.callFrameId,
564
+ });
565
+ }
566
+ catch (e) {
567
+ reportError(`Cannot set value of "${data.variableName}"`, e);
568
+ }
569
+ break;
570
+ case 'expressions': {
571
+ const expressions = await Promise.all((data.expressions || []).map(async (item) => {
572
+ try {
573
+ const { result } = (await Debugger.evaluateOnCallFrame({
574
+ callFrameId: data.callFrameId,
575
+ expression: item.name,
576
+ objectGroup: 'node-inspect',
577
+ generatePreview: true,
578
+ }));
579
+ return { name: item.name, result };
580
+ }
581
+ catch (e) {
582
+ return {
583
+ name: item.name,
584
+ result: {
585
+ type: 'object',
586
+ subtype: 'error',
587
+ className: 'Error',
588
+ description: errorToString(e),
589
+ },
590
+ };
591
+ }
592
+ }));
593
+ sendToHost({ cmd: 'expressions', expressions });
594
+ break;
595
+ }
596
+ case 'stopOnException':
597
+ try {
598
+ await Debugger.setPauseOnExceptions({ state: data.state ? 'all' : 'none' });
599
+ }
600
+ catch (e) {
601
+ reportError('Cannot change "stop on exception"', e);
602
+ }
603
+ break;
604
+ case 'getPossibleBreakpoints':
605
+ try {
606
+ const { locations } = (await Debugger.getPossibleBreakpoints({
607
+ start: data.start,
608
+ end: data.end,
609
+ }));
610
+ sendToHost({ cmd: 'getPossibleBreakpoints', breakpoints: locations });
611
+ }
612
+ catch (e) {
613
+ reportError('Cannot get possible breakpoints', e);
614
+ }
615
+ break;
616
+ default:
617
+ console.error(`[DEBUGGER] Unknown command: ${JSON.stringify(data)}`);
618
+ break;
726
619
  }
727
620
  }
621
+ process.on('message', (message) => {
622
+ let command;
623
+ try {
624
+ command = typeof message === 'string' ? JSON.parse(message) : message;
625
+ }
626
+ catch {
627
+ console.error(`[DEBUGGER] Cannot parse: ${JSON.stringify(message)}`);
628
+ return;
629
+ }
630
+ processCommand(command).catch(e => reportError(`Cannot process "${command?.cmd}"`, e));
631
+ });
632
+ // Handle all possible exits and never leave the debugged process behind
633
+ process.on('exit', () => inspector?.killChild());
634
+ process.on('disconnect', () => process.exit(0));
635
+ process.once('SIGTERM', () => process.exit(0));
636
+ process.once('SIGHUP', () => process.exit(0));
637
+ process.on('uncaughtException', (e) => {
638
+ reportError(`Internal error in inspector: ${e.stack || e.message}`);
639
+ inspector?.killChild();
640
+ process.exit(1);
641
+ });
642
+ sendToHost({ cmd: 'ready' });
728
643
  //# sourceMappingURL=inspect.js.map