@saptools/cf-inspector 0.4.11 → 0.5.0
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.
- package/README.md +169 -31
- package/dist/cli.js +1220 -429
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +44 -3
- package/dist/index.js +665 -116
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -152,9 +152,12 @@ var init_wsTransport = __esm({
|
|
|
152
152
|
});
|
|
153
153
|
|
|
154
154
|
// src/cli.ts
|
|
155
|
-
import
|
|
155
|
+
import process11 from "process";
|
|
156
156
|
|
|
157
157
|
// src/cli/program.ts
|
|
158
|
+
import { readFileSync } from "fs";
|
|
159
|
+
import { dirname, join } from "path";
|
|
160
|
+
import { fileURLToPath } from "url";
|
|
158
161
|
import { Command } from "commander";
|
|
159
162
|
|
|
160
163
|
// src/cli/commands/attach.ts
|
|
@@ -163,42 +166,93 @@ import process3 from "process";
|
|
|
163
166
|
// src/inspector/discovery.ts
|
|
164
167
|
init_types();
|
|
165
168
|
import { request } from "http";
|
|
169
|
+
import { performance } from "perf_hooks";
|
|
170
|
+
var InvalidDiscoveryPayloadError = class extends CfInspectorError {
|
|
171
|
+
};
|
|
166
172
|
async function fetchJson(url, timeoutMs) {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
173
|
+
const deadline = performance.now() + timeoutMs;
|
|
174
|
+
let lastError;
|
|
175
|
+
while (performance.now() < deadline) {
|
|
176
|
+
try {
|
|
177
|
+
const remainingMs = deadline - performance.now();
|
|
178
|
+
if (remainingMs <= 0) {
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
return await new Promise((resolve, reject) => {
|
|
182
|
+
const req = request(url, { method: "GET" }, (res) => {
|
|
183
|
+
const chunks = [];
|
|
184
|
+
res.on("data", (chunk) => {
|
|
185
|
+
chunks.push(chunk);
|
|
186
|
+
});
|
|
187
|
+
res.on("end", () => {
|
|
188
|
+
try {
|
|
189
|
+
resolve(parseJsonResponse(chunks));
|
|
190
|
+
} catch (err) {
|
|
191
|
+
reject(parseDiscoveryError(url, err));
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
res.on("error", (err) => {
|
|
195
|
+
reject(newDiscoveryError(`Inspector discovery response error: ${err.message}`));
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
const attemptTimeoutMs = Math.min(2e3, remainingMs);
|
|
199
|
+
req.setTimeout(attemptTimeoutMs, () => {
|
|
200
|
+
req.destroy(
|
|
201
|
+
new CfInspectorError(
|
|
202
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
203
|
+
`Inspector discovery at ${url} timed out after ${timeoutMs.toString()}ms`
|
|
204
|
+
)
|
|
205
|
+
);
|
|
206
|
+
});
|
|
207
|
+
req.on("error", (err) => {
|
|
208
|
+
reject(err instanceof CfInspectorError ? err : formatDiscoveryRequestError(url, err));
|
|
209
|
+
});
|
|
210
|
+
req.end();
|
|
182
211
|
});
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
212
|
+
} catch (err) {
|
|
213
|
+
if (err instanceof InvalidDiscoveryPayloadError) {
|
|
214
|
+
throw err;
|
|
215
|
+
}
|
|
216
|
+
lastError = err;
|
|
217
|
+
const now = performance.now();
|
|
218
|
+
if (now < deadline) {
|
|
219
|
+
const sleepMs = Math.min(1e3, deadline - now);
|
|
220
|
+
await new Promise((r) => setTimeout(r, sleepMs));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (lastError instanceof Error) {
|
|
225
|
+
throw lastError;
|
|
226
|
+
}
|
|
227
|
+
throw new CfInspectorError("INSPECTOR_DISCOVERY_FAILED", `Inspector discovery at ${url} timed out after ${timeoutMs.toString()}ms`);
|
|
228
|
+
}
|
|
229
|
+
function isNodeSystemError(err) {
|
|
230
|
+
return err instanceof Error;
|
|
231
|
+
}
|
|
232
|
+
function isConnectionRefusedOrUnreachable(code) {
|
|
233
|
+
return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "ETIMEDOUT" || code === "EHOSTUNREACH" || code === "ENETUNREACH";
|
|
234
|
+
}
|
|
235
|
+
function formatEndpoint(url, err) {
|
|
236
|
+
if (typeof err.address === "string" && typeof err.port === "number") {
|
|
237
|
+
return `${err.address}:${err.port.toString()}`;
|
|
238
|
+
}
|
|
239
|
+
const parsed = new URL(url);
|
|
240
|
+
return parsed.host;
|
|
241
|
+
}
|
|
242
|
+
function formatDiscoveryRequestError(url, err) {
|
|
243
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
244
|
+
if (!isNodeSystemError(err) || !isConnectionRefusedOrUnreachable(err.code)) {
|
|
245
|
+
return new CfInspectorError(
|
|
246
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
247
|
+
`Inspector discovery at ${url} failed: ${detail}`
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const endpoint = formatEndpoint(url, err);
|
|
251
|
+
return new CfInspectorError(
|
|
252
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
253
|
+
`Cannot reach Node inspector discovery at ${url}. Nothing is listening on ${endpoint}, or the inspector tunnel is stale/closed. Restart the local inspector or tunnel and retry. If this port came from cf-debugger, stop the stale session and start a fresh tunnel, or run cf-inspector with --app/--region/--org/--space so it can open a tunnel.`,
|
|
254
|
+
detail
|
|
255
|
+
);
|
|
202
256
|
}
|
|
203
257
|
function parseJsonResponse(chunks) {
|
|
204
258
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
@@ -206,7 +260,10 @@ function parseJsonResponse(chunks) {
|
|
|
206
260
|
}
|
|
207
261
|
function parseDiscoveryError(url, err) {
|
|
208
262
|
const message = err instanceof Error ? err.message : String(err);
|
|
209
|
-
return
|
|
263
|
+
return new InvalidDiscoveryPayloadError(
|
|
264
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
265
|
+
`Failed to parse inspector discovery response from ${url}: ${message}`
|
|
266
|
+
);
|
|
210
267
|
}
|
|
211
268
|
function newDiscoveryError(message) {
|
|
212
269
|
return new CfInspectorError("INSPECTOR_DISCOVERY_FAILED", message);
|
|
@@ -306,7 +363,7 @@ function writeHumanSnapshot(snapshot) {
|
|
|
306
363
|
lines.push(" captures:");
|
|
307
364
|
for (const capture of snapshot.captures) {
|
|
308
365
|
const detail = capture.error ?? capture.value ?? "undefined";
|
|
309
|
-
lines.push(` ${capture.expression} = ${detail}`);
|
|
366
|
+
lines.push(` ${capture.expression} = ${renderTruncated(detail, capture)}`);
|
|
310
367
|
}
|
|
311
368
|
}
|
|
312
369
|
if (snapshot.stack !== void 0 && snapshot.stack.length > 0) {
|
|
@@ -324,13 +381,17 @@ function appendFrameLines(lines, frame) {
|
|
|
324
381
|
lines.push(
|
|
325
382
|
` frame: ${fnName} ${sourceUrl}:${frame.line.toString()}:${frame.column.toString()}`
|
|
326
383
|
);
|
|
384
|
+
if (frame.truncated === true) {
|
|
385
|
+
lines.push(` scopes: ${truncationLabel(frame)}`);
|
|
386
|
+
}
|
|
327
387
|
if (frame.scopes === void 0) {
|
|
328
388
|
return;
|
|
329
389
|
}
|
|
330
390
|
for (const scope of frame.scopes) {
|
|
331
|
-
|
|
391
|
+
const scopeSuffix = scope.truncated === true ? `; ${truncationLabel(scope)}` : "";
|
|
392
|
+
lines.push(` scope ${scope.type} (${scope.variables.length.toString()} vars${scopeSuffix}):`);
|
|
332
393
|
for (const variable of scope.variables) {
|
|
333
|
-
lines.push(` ${variable.name} = ${variable.value}`);
|
|
394
|
+
lines.push(` ${variable.name} = ${renderTruncated(variable.value, variable)}`);
|
|
334
395
|
}
|
|
335
396
|
}
|
|
336
397
|
}
|
|
@@ -341,7 +402,7 @@ function appendStackFrameLine(lines, frame) {
|
|
|
341
402
|
if (frame.captures !== void 0) {
|
|
342
403
|
for (const capture of frame.captures) {
|
|
343
404
|
const detail = capture.error ?? capture.value ?? "undefined";
|
|
344
|
-
lines.push(` ${capture.expression} = ${detail}`);
|
|
405
|
+
lines.push(` ${capture.expression} = ${renderTruncated(detail, capture)}`);
|
|
345
406
|
}
|
|
346
407
|
}
|
|
347
408
|
}
|
|
@@ -350,8 +411,7 @@ function appendExceptionLines(lines, exception) {
|
|
|
350
411
|
lines.push(` exception: !err ${exception.error}`);
|
|
351
412
|
return;
|
|
352
413
|
}
|
|
353
|
-
|
|
354
|
-
lines.push(` exception: ${detail}`);
|
|
414
|
+
lines.push(` exception: ${renderExceptionDetail(exception)}`);
|
|
355
415
|
}
|
|
356
416
|
function writeLogEvent(event, json) {
|
|
357
417
|
if (json) {
|
|
@@ -360,11 +420,11 @@ function writeLogEvent(event, json) {
|
|
|
360
420
|
return;
|
|
361
421
|
}
|
|
362
422
|
if (event.error !== void 0) {
|
|
363
|
-
process.stdout.write(`[${event.ts}] ${event.at} !err ${event.error}
|
|
423
|
+
process.stdout.write(`[${event.ts}] ${event.at} !err ${renderTruncated(event.error, event)}
|
|
364
424
|
`);
|
|
365
425
|
return;
|
|
366
426
|
}
|
|
367
|
-
process.stdout.write(`[${event.ts}] ${event.at} ${event.value ?? ""}
|
|
427
|
+
process.stdout.write(`[${event.ts}] ${event.at} ${renderTruncated(event.value ?? "", event)}
|
|
368
428
|
`);
|
|
369
429
|
}
|
|
370
430
|
function writeWatchEvent(event, json) {
|
|
@@ -376,23 +436,53 @@ function writeWatchEvent(event, json) {
|
|
|
376
436
|
process.stdout.write(`[${event.ts}] hit#${event.hit.toString()} ${event.at}
|
|
377
437
|
`);
|
|
378
438
|
if (event.exception !== void 0) {
|
|
379
|
-
|
|
380
|
-
process.stdout.write(` exception: ${detail}
|
|
439
|
+
process.stdout.write(` exception: ${renderExceptionDetail(event.exception)}
|
|
381
440
|
`);
|
|
382
441
|
}
|
|
383
442
|
for (const capture of event.captures) {
|
|
384
443
|
const detail = capture.error ?? capture.value ?? "undefined";
|
|
385
|
-
process.stdout.write(` ${capture.expression} = ${detail}
|
|
444
|
+
process.stdout.write(` ${capture.expression} = ${renderTruncated(detail, capture)}
|
|
386
445
|
`);
|
|
387
446
|
}
|
|
388
447
|
}
|
|
448
|
+
function renderExceptionDetail(exception) {
|
|
449
|
+
if (exception.description !== void 0) {
|
|
450
|
+
const originalLength = exception.descriptionOriginalLength;
|
|
451
|
+
return renderTruncated(
|
|
452
|
+
exception.description,
|
|
453
|
+
originalLength === void 0 ? {} : { truncated: true, originalLength }
|
|
454
|
+
);
|
|
455
|
+
}
|
|
456
|
+
if (exception.value !== void 0) {
|
|
457
|
+
const originalLength = exception.valueOriginalLength ?? exception.originalLength;
|
|
458
|
+
const summary = {
|
|
459
|
+
...originalLength === void 0 ? {} : { truncated: true, originalLength },
|
|
460
|
+
...exception.omittedCount === void 0 ? {} : { truncated: true, omittedCount: exception.omittedCount }
|
|
461
|
+
};
|
|
462
|
+
return renderTruncated(exception.value, summary);
|
|
463
|
+
}
|
|
464
|
+
return exception.error ?? "(unknown)";
|
|
465
|
+
}
|
|
466
|
+
function renderTruncated(value, summary) {
|
|
467
|
+
if (summary.truncated !== true) {
|
|
468
|
+
return value;
|
|
469
|
+
}
|
|
470
|
+
const visualValue = summary.originalLength === void 0 ? value : `${value}\u2026`;
|
|
471
|
+
return `${visualValue} [${truncationLabel(summary)}]`;
|
|
472
|
+
}
|
|
473
|
+
function truncationLabel(summary) {
|
|
474
|
+
const details = [];
|
|
475
|
+
if (summary.originalLength !== void 0) {
|
|
476
|
+
details.push(`original ${summary.originalLength.toString()} chars`);
|
|
477
|
+
}
|
|
478
|
+
if (summary.omittedCount !== void 0) {
|
|
479
|
+
details.push(`${summary.omittedCount.toString()} omitted`);
|
|
480
|
+
}
|
|
481
|
+
return details.length === 0 ? "truncated" : `truncated: ${details.join(", ")}`;
|
|
482
|
+
}
|
|
389
483
|
|
|
390
484
|
// src/cli/target.ts
|
|
391
|
-
import
|
|
392
|
-
import {
|
|
393
|
-
readCurrentCfTarget,
|
|
394
|
-
requireCurrentCfRegion
|
|
395
|
-
} from "@saptools/cf-debugger";
|
|
485
|
+
import "@saptools/cf-debugger";
|
|
396
486
|
|
|
397
487
|
// src/cf/tunnel.ts
|
|
398
488
|
import { startDebugger } from "@saptools/cf-debugger";
|
|
@@ -459,7 +549,7 @@ function extractExistingTunnelPort(message) {
|
|
|
459
549
|
}
|
|
460
550
|
|
|
461
551
|
// src/inspector/session.ts
|
|
462
|
-
import { performance } from "perf_hooks";
|
|
552
|
+
import { performance as performance2 } from "perf_hooks";
|
|
463
553
|
|
|
464
554
|
// src/cdp/client.ts
|
|
465
555
|
init_types();
|
|
@@ -701,6 +791,102 @@ var CdpClient = class _CdpClient {
|
|
|
701
791
|
this.emitter.removeAllListeners();
|
|
702
792
|
}
|
|
703
793
|
};
|
|
794
|
+
var NodeWorkerTransport = class {
|
|
795
|
+
constructor(parent, sessionId) {
|
|
796
|
+
this.parent = parent;
|
|
797
|
+
this.sessionId = sessionId;
|
|
798
|
+
this.detachParentListeners = [
|
|
799
|
+
parent.on("NodeWorker.receivedMessageFromWorker", (raw) => {
|
|
800
|
+
this.forwardWorkerMessage(raw);
|
|
801
|
+
}),
|
|
802
|
+
parent.on("NodeWorker.detachedFromWorker", (raw) => {
|
|
803
|
+
this.handleWorkerDetach(raw);
|
|
804
|
+
}),
|
|
805
|
+
parent.onClose((error) => {
|
|
806
|
+
this.closeWithError(error);
|
|
807
|
+
})
|
|
808
|
+
];
|
|
809
|
+
}
|
|
810
|
+
parent;
|
|
811
|
+
sessionId;
|
|
812
|
+
emitter = new EventEmitter();
|
|
813
|
+
detachParentListeners;
|
|
814
|
+
readyState = 1;
|
|
815
|
+
send(payload) {
|
|
816
|
+
if (this.readyState !== 1) {
|
|
817
|
+
throw new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Worker inspector session is closed");
|
|
818
|
+
}
|
|
819
|
+
void this.parent.send("NodeWorker.sendMessageToWorker", {
|
|
820
|
+
sessionId: this.sessionId,
|
|
821
|
+
message: payload
|
|
822
|
+
}).catch((error) => {
|
|
823
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
824
|
+
this.closeWithError(normalized);
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
close() {
|
|
828
|
+
this.finishClose();
|
|
829
|
+
}
|
|
830
|
+
on(event, listener) {
|
|
831
|
+
this.emitter.on(event, listener);
|
|
832
|
+
}
|
|
833
|
+
off(event, listener) {
|
|
834
|
+
this.emitter.off(event, listener);
|
|
835
|
+
}
|
|
836
|
+
forwardWorkerMessage(raw) {
|
|
837
|
+
const params = asNodeWorkerEventParams(raw);
|
|
838
|
+
if (params.sessionId !== this.sessionId || typeof params.message !== "string") {
|
|
839
|
+
return;
|
|
840
|
+
}
|
|
841
|
+
this.emitter.emit("message", params.message);
|
|
842
|
+
}
|
|
843
|
+
handleWorkerDetach(raw) {
|
|
844
|
+
const params = asNodeWorkerEventParams(raw);
|
|
845
|
+
if (params.sessionId === this.sessionId) {
|
|
846
|
+
this.finishClose();
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
closeWithError(error) {
|
|
850
|
+
if (this.readyState !== 1) {
|
|
851
|
+
return;
|
|
852
|
+
}
|
|
853
|
+
this.emitter.emit("error", error);
|
|
854
|
+
this.finishClose();
|
|
855
|
+
}
|
|
856
|
+
finishClose() {
|
|
857
|
+
if (this.readyState !== 1) {
|
|
858
|
+
return;
|
|
859
|
+
}
|
|
860
|
+
this.readyState = 3;
|
|
861
|
+
for (const detach of this.detachParentListeners) {
|
|
862
|
+
detach();
|
|
863
|
+
}
|
|
864
|
+
this.emitter.emit("close");
|
|
865
|
+
this.emitter.removeAllListeners();
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
function asNodeWorkerEventParams(raw) {
|
|
869
|
+
if (!isUnknownRecord(raw)) {
|
|
870
|
+
return {};
|
|
871
|
+
}
|
|
872
|
+
const sessionId = raw["sessionId"];
|
|
873
|
+
const message = raw["message"];
|
|
874
|
+
return {
|
|
875
|
+
...typeof sessionId === "string" ? { sessionId } : {},
|
|
876
|
+
...typeof message === "string" ? { message } : {}
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function isUnknownRecord(value) {
|
|
880
|
+
return typeof value === "object" && value !== null;
|
|
881
|
+
}
|
|
882
|
+
async function createNodeWorkerClient(parent, sessionId, requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
|
|
883
|
+
const transport = new NodeWorkerTransport(parent, sessionId);
|
|
884
|
+
return await CdpClient.connect({
|
|
885
|
+
url: `node-worker://${sessionId}`,
|
|
886
|
+
transportFactory: () => Promise.resolve(transport),
|
|
887
|
+
requestTimeoutMs
|
|
888
|
+
});
|
|
889
|
+
}
|
|
704
890
|
|
|
705
891
|
// src/inspector/session.ts
|
|
706
892
|
init_types();
|
|
@@ -817,6 +1003,101 @@ function pauseDetail(pause) {
|
|
|
817
1003
|
var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
|
|
818
1004
|
var DEFAULT_HOST = "127.0.0.1";
|
|
819
1005
|
var PAUSE_BUFFER_LIMIT = 32;
|
|
1006
|
+
var NodeWorkerDiscovery = class {
|
|
1007
|
+
constructor(client) {
|
|
1008
|
+
this.client = client;
|
|
1009
|
+
this.detachListeners = [
|
|
1010
|
+
client.on("NodeWorker.attachedToWorker", (raw) => {
|
|
1011
|
+
const worker = toInspectorWorkerTarget(raw);
|
|
1012
|
+
if (worker !== void 0) {
|
|
1013
|
+
this.workers.set(worker.sessionId, worker);
|
|
1014
|
+
}
|
|
1015
|
+
}),
|
|
1016
|
+
client.on("NodeWorker.detachedFromWorker", (raw) => {
|
|
1017
|
+
const sessionId = readField(raw, "sessionId");
|
|
1018
|
+
if (typeof sessionId === "string") {
|
|
1019
|
+
this.workers.delete(sessionId);
|
|
1020
|
+
}
|
|
1021
|
+
})
|
|
1022
|
+
];
|
|
1023
|
+
}
|
|
1024
|
+
client;
|
|
1025
|
+
workers = /* @__PURE__ */ new Map();
|
|
1026
|
+
detachListeners;
|
|
1027
|
+
supported = false;
|
|
1028
|
+
disposed = false;
|
|
1029
|
+
async enable() {
|
|
1030
|
+
try {
|
|
1031
|
+
await this.client.send("NodeWorker.enable", { waitForDebuggerOnStart: false });
|
|
1032
|
+
this.supported = true;
|
|
1033
|
+
} catch (error) {
|
|
1034
|
+
if (!isUnsupportedNodeWorkerDomain(error)) {
|
|
1035
|
+
throw error;
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
list() {
|
|
1040
|
+
return [...this.workers.values()].sort(compareWorkers);
|
|
1041
|
+
}
|
|
1042
|
+
async dispose() {
|
|
1043
|
+
if (this.disposed) {
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
this.disposed = true;
|
|
1047
|
+
if (this.supported && !this.client.isClosed) {
|
|
1048
|
+
try {
|
|
1049
|
+
await this.client.send("NodeWorker.disable");
|
|
1050
|
+
} catch {
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
for (const detach of this.detachListeners) {
|
|
1054
|
+
detach();
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
function isUnsupportedNodeWorkerDomain(error) {
|
|
1059
|
+
if (!(error instanceof CfInspectorError) || error.code !== "CDP_REQUEST_FAILED") {
|
|
1060
|
+
return false;
|
|
1061
|
+
}
|
|
1062
|
+
return error.detail?.includes('"code":-32601') === true;
|
|
1063
|
+
}
|
|
1064
|
+
function compareWorkers(left, right) {
|
|
1065
|
+
const leftId = Number.parseInt(left.workerId, 10);
|
|
1066
|
+
const rightId = Number.parseInt(right.workerId, 10);
|
|
1067
|
+
if (!Number.isNaN(leftId) && !Number.isNaN(rightId) && leftId !== rightId) {
|
|
1068
|
+
return leftId - rightId;
|
|
1069
|
+
}
|
|
1070
|
+
return left.workerId.localeCompare(right.workerId);
|
|
1071
|
+
}
|
|
1072
|
+
function toInspectorWorkerTarget(raw) {
|
|
1073
|
+
const sessionId = readField(raw, "sessionId");
|
|
1074
|
+
const info = readField(raw, "workerInfo");
|
|
1075
|
+
if (typeof sessionId !== "string" || !isUnknownRecord2(info)) {
|
|
1076
|
+
return void 0;
|
|
1077
|
+
}
|
|
1078
|
+
const workerId = asString(info["workerId"]);
|
|
1079
|
+
if (workerId.length === 0) {
|
|
1080
|
+
return void 0;
|
|
1081
|
+
}
|
|
1082
|
+
return {
|
|
1083
|
+
sessionId,
|
|
1084
|
+
workerId,
|
|
1085
|
+
type: asString(info["type"]),
|
|
1086
|
+
title: asString(info["title"]),
|
|
1087
|
+
url: asString(info["url"])
|
|
1088
|
+
};
|
|
1089
|
+
}
|
|
1090
|
+
function readField(value, name) {
|
|
1091
|
+
return isUnknownRecord2(value) ? value[name] : void 0;
|
|
1092
|
+
}
|
|
1093
|
+
function isUnknownRecord2(value) {
|
|
1094
|
+
return typeof value === "object" && value !== null;
|
|
1095
|
+
}
|
|
1096
|
+
async function startNodeWorkerDiscovery(client) {
|
|
1097
|
+
const discovery = new NodeWorkerDiscovery(client);
|
|
1098
|
+
await discovery.enable();
|
|
1099
|
+
return discovery;
|
|
1100
|
+
}
|
|
820
1101
|
async function connectInspector(options) {
|
|
821
1102
|
const host = options.host ?? DEFAULT_HOST;
|
|
822
1103
|
const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
@@ -833,13 +1114,81 @@ async function connectInspector(options) {
|
|
|
833
1114
|
url: target.webSocketDebuggerUrl,
|
|
834
1115
|
connectTimeoutMs
|
|
835
1116
|
});
|
|
1117
|
+
let workerDiscovery;
|
|
836
1118
|
try {
|
|
837
|
-
|
|
1119
|
+
workerDiscovery = await startNodeWorkerDiscovery(client);
|
|
1120
|
+
if (options.workerIndex === void 0) {
|
|
1121
|
+
const session = await initSession(client, target);
|
|
1122
|
+
return withWorkerMetadata(session, workerDiscovery, targetIndex, targets.length);
|
|
1123
|
+
}
|
|
1124
|
+
return await initWorkerSession(
|
|
1125
|
+
client,
|
|
1126
|
+
workerDiscovery,
|
|
1127
|
+
options.workerIndex,
|
|
1128
|
+
targetIndex,
|
|
1129
|
+
targets.length
|
|
1130
|
+
);
|
|
838
1131
|
} catch (err) {
|
|
1132
|
+
await workerDiscovery?.dispose();
|
|
839
1133
|
client.dispose();
|
|
840
1134
|
throw err;
|
|
841
1135
|
}
|
|
842
1136
|
}
|
|
1137
|
+
async function initWorkerSession(parent, discovery, workerIndex, targetIndex, targetCount) {
|
|
1138
|
+
const workers = discovery.list();
|
|
1139
|
+
if (!discovery.supported) {
|
|
1140
|
+
throw new CfInspectorError(
|
|
1141
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
1142
|
+
"This runtime does not expose the NodeWorker CDP domain; --worker cannot be used. Run list-targets for available raw targets."
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
const worker = workers[workerIndex];
|
|
1146
|
+
if (worker === void 0) {
|
|
1147
|
+
throw new CfInspectorError(
|
|
1148
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
1149
|
+
`No NodeWorker sub-session at index ${workerIndex.toString()} (available: ${workers.length.toString()}). Ensure the worker is alive, then rerun list-targets.`
|
|
1150
|
+
);
|
|
1151
|
+
}
|
|
1152
|
+
const client = await createNodeWorkerClient(parent, worker.sessionId);
|
|
1153
|
+
const session = await initSession(client, workerToInspectorTarget(worker));
|
|
1154
|
+
return withWorkerMetadata(session, discovery, targetIndex, targetCount, workerIndex, parent);
|
|
1155
|
+
}
|
|
1156
|
+
function withWorkerMetadata(session, discovery, targetIndex, targetCount, workerIndex, parent) {
|
|
1157
|
+
return {
|
|
1158
|
+
...session,
|
|
1159
|
+
targetIndex,
|
|
1160
|
+
targetCount,
|
|
1161
|
+
...workerIndex === void 0 ? {} : { workerIndex },
|
|
1162
|
+
workerTargets: discovery.list(),
|
|
1163
|
+
workerDiscoverySupported: discovery.supported,
|
|
1164
|
+
dispose: async () => {
|
|
1165
|
+
await session.dispose();
|
|
1166
|
+
await discovery.dispose();
|
|
1167
|
+
parent?.dispose();
|
|
1168
|
+
}
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
function workerToInspectorTarget(worker) {
|
|
1172
|
+
return {
|
|
1173
|
+
description: "Node worker sub-session",
|
|
1174
|
+
id: worker.workerId,
|
|
1175
|
+
title: worker.title,
|
|
1176
|
+
type: worker.type,
|
|
1177
|
+
url: worker.url,
|
|
1178
|
+
webSocketDebuggerUrl: `node-worker://${worker.sessionId}`
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
async function discoverNodeWorkerTargets(target, connectTimeoutMs = DEFAULT_CONNECT_TIMEOUT_MS) {
|
|
1182
|
+
const client = await CdpClient.connect({ url: target.webSocketDebuggerUrl, connectTimeoutMs });
|
|
1183
|
+
let discovery;
|
|
1184
|
+
try {
|
|
1185
|
+
discovery = await startNodeWorkerDiscovery(client);
|
|
1186
|
+
return { supported: discovery.supported, workers: discovery.list() };
|
|
1187
|
+
} finally {
|
|
1188
|
+
await discovery?.dispose();
|
|
1189
|
+
client.dispose();
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
843
1192
|
async function initSession(client, target) {
|
|
844
1193
|
const scripts = /* @__PURE__ */ new Map();
|
|
845
1194
|
client.on("Debugger.scriptParsed", (raw) => {
|
|
@@ -859,14 +1208,14 @@ async function initSession(client, target) {
|
|
|
859
1208
|
return;
|
|
860
1209
|
}
|
|
861
1210
|
const params = raw;
|
|
862
|
-
const event = toPauseEvent(params,
|
|
1211
|
+
const event = toPauseEvent(params, performance2.now(), scripts);
|
|
863
1212
|
if (pauseBuffer.length >= PAUSE_BUFFER_LIMIT) {
|
|
864
1213
|
pauseBuffer.shift();
|
|
865
1214
|
}
|
|
866
1215
|
pauseBuffer.push(event);
|
|
867
1216
|
});
|
|
868
1217
|
client.on("Debugger.resumed", () => {
|
|
869
|
-
debuggerState.lastResumedAtMs =
|
|
1218
|
+
debuggerState.lastResumedAtMs = performance2.now();
|
|
870
1219
|
});
|
|
871
1220
|
await client.send("Runtime.enable");
|
|
872
1221
|
await client.send("Debugger.enable");
|
|
@@ -895,6 +1244,223 @@ var DEFAULT_BREAKPOINT_TIMEOUT_SEC = 30;
|
|
|
895
1244
|
var DEFAULT_CF_TIMEOUT_SEC = 180;
|
|
896
1245
|
var DEFAULT_EXCEPTION_TIMEOUT_SEC = 30;
|
|
897
1246
|
|
|
1247
|
+
// src/cli/warnings.ts
|
|
1248
|
+
init_types();
|
|
1249
|
+
import process2 from "process";
|
|
1250
|
+
|
|
1251
|
+
// src/cli/captureParser.ts
|
|
1252
|
+
function parseCaptureList(raw) {
|
|
1253
|
+
if (raw === void 0 || raw.trim().length === 0) {
|
|
1254
|
+
return [];
|
|
1255
|
+
}
|
|
1256
|
+
return splitCaptureExpressions(raw);
|
|
1257
|
+
}
|
|
1258
|
+
function isQuoteChar(value) {
|
|
1259
|
+
return value === "'" || value === '"' || value === "`";
|
|
1260
|
+
}
|
|
1261
|
+
function consumeQuotedChar(state, char) {
|
|
1262
|
+
if (state.quote === void 0) {
|
|
1263
|
+
return false;
|
|
1264
|
+
}
|
|
1265
|
+
if (state.escaped) {
|
|
1266
|
+
state.escaped = false;
|
|
1267
|
+
return true;
|
|
1268
|
+
}
|
|
1269
|
+
if (char === "\\") {
|
|
1270
|
+
state.escaped = true;
|
|
1271
|
+
return true;
|
|
1272
|
+
}
|
|
1273
|
+
if (char === state.quote) {
|
|
1274
|
+
state.quote = void 0;
|
|
1275
|
+
}
|
|
1276
|
+
return true;
|
|
1277
|
+
}
|
|
1278
|
+
function stripQuotedText(expression) {
|
|
1279
|
+
const state = { quote: void 0, escaped: false };
|
|
1280
|
+
let stripped = "";
|
|
1281
|
+
for (const char of expression) {
|
|
1282
|
+
if (consumeQuotedChar(state, char)) {
|
|
1283
|
+
stripped += " ";
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
if (isQuoteChar(char)) {
|
|
1287
|
+
state.quote = char;
|
|
1288
|
+
stripped += " ";
|
|
1289
|
+
continue;
|
|
1290
|
+
}
|
|
1291
|
+
stripped += char;
|
|
1292
|
+
}
|
|
1293
|
+
return stripped;
|
|
1294
|
+
}
|
|
1295
|
+
function looksLikeMutation(expression) {
|
|
1296
|
+
const stripped = stripQuotedText(expression);
|
|
1297
|
+
const hasUpdate = /(?:\+\+|--)/u.test(stripped);
|
|
1298
|
+
const hasAssignment = /(?:\*\*=|&&=|\|\|=|\?\?=|[+\-*/%&|^]=|(?:^|[^=!<>])=(?!=|>))/u.test(stripped);
|
|
1299
|
+
const hasDelete = /\bdelete\b/u.test(stripped);
|
|
1300
|
+
const hasMutatingMethod = /\.\s*(?:push|pop|shift|unshift|splice|sort|reverse|fill|copyWithin|set|add|delete|clear)\s*\(/u.test(stripped);
|
|
1301
|
+
const hasObjectMutation = /\bObject\s*\.\s*(?:assign|defineProperty|defineProperties)\s*\(/u.test(stripped);
|
|
1302
|
+
return hasUpdate || hasAssignment || hasDelete || hasMutatingMethod || hasObjectMutation;
|
|
1303
|
+
}
|
|
1304
|
+
function updateCaptureDepth(state, char) {
|
|
1305
|
+
if (char === "(") {
|
|
1306
|
+
state.parenDepth += 1;
|
|
1307
|
+
} else if (char === ")") {
|
|
1308
|
+
state.parenDepth = Math.max(0, state.parenDepth - 1);
|
|
1309
|
+
} else if (char === "[") {
|
|
1310
|
+
state.bracketDepth += 1;
|
|
1311
|
+
} else if (char === "]") {
|
|
1312
|
+
state.bracketDepth = Math.max(0, state.bracketDepth - 1);
|
|
1313
|
+
} else if (char === "{") {
|
|
1314
|
+
state.braceDepth += 1;
|
|
1315
|
+
} else if (char === "}") {
|
|
1316
|
+
state.braceDepth = Math.max(0, state.braceDepth - 1);
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
function isTopLevel(state) {
|
|
1320
|
+
return state.parenDepth === 0 && state.bracketDepth === 0 && state.braceDepth === 0;
|
|
1321
|
+
}
|
|
1322
|
+
function appendCapturePiece(raw, state, end) {
|
|
1323
|
+
const piece = raw.slice(state.start, end).trim();
|
|
1324
|
+
if (piece.length > 0) {
|
|
1325
|
+
state.pieces.push(piece);
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
function splitCaptureExpressions(raw) {
|
|
1329
|
+
const state = {
|
|
1330
|
+
escaped: false,
|
|
1331
|
+
parenDepth: 0,
|
|
1332
|
+
bracketDepth: 0,
|
|
1333
|
+
braceDepth: 0,
|
|
1334
|
+
quote: void 0,
|
|
1335
|
+
start: 0,
|
|
1336
|
+
pieces: []
|
|
1337
|
+
};
|
|
1338
|
+
for (let idx = 0; idx < raw.length; idx += 1) {
|
|
1339
|
+
const char = raw.charAt(idx);
|
|
1340
|
+
if (consumeQuotedChar(state, char)) {
|
|
1341
|
+
continue;
|
|
1342
|
+
}
|
|
1343
|
+
if (isQuoteChar(char)) {
|
|
1344
|
+
state.quote = char;
|
|
1345
|
+
continue;
|
|
1346
|
+
}
|
|
1347
|
+
updateCaptureDepth(state, char);
|
|
1348
|
+
if (char === "," && isTopLevel(state)) {
|
|
1349
|
+
appendCapturePiece(raw, state, idx);
|
|
1350
|
+
state.start = idx + 1;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
appendCapturePiece(raw, state, raw.length);
|
|
1354
|
+
return state.pieces;
|
|
1355
|
+
}
|
|
1356
|
+
|
|
1357
|
+
// src/cli/warnings.ts
|
|
1358
|
+
function warnOnCaptureMutationRisk(expressions, allowMutation) {
|
|
1359
|
+
const riskyCount = expressions.filter(looksLikeMutation).length;
|
|
1360
|
+
if (riskyCount === 0) {
|
|
1361
|
+
return;
|
|
1362
|
+
}
|
|
1363
|
+
const suffix = allowMutation ? "will run without the V8 side-effect guard because --allow-mutation was passed." : "will be checked by the V8 side-effect guard and blocked unless V8 proves them safe; pass --allow-mutation to run them unrestricted.";
|
|
1364
|
+
process2.stderr.write(
|
|
1365
|
+
`[cf-inspector] warning: ${riskyCount.toString()} capture ${riskyCount === 1 ? "expression looks" : "expressions look"} mutation-capable and ${suffix}
|
|
1366
|
+
`
|
|
1367
|
+
);
|
|
1368
|
+
}
|
|
1369
|
+
function enforceNativeConditionMutationPolicy(expression, allowMutation, context) {
|
|
1370
|
+
if (!looksLikeMutation(expression)) {
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
if (!allowMutation) {
|
|
1374
|
+
throw new CfInspectorError(
|
|
1375
|
+
"MUTATION_NOT_ALLOWED",
|
|
1376
|
+
`${context} looks mutation-capable. Native breakpoint conditions cannot be protected by V8's side-effect guard; pass --allow-mutation to arm it explicitly.`
|
|
1377
|
+
);
|
|
1378
|
+
}
|
|
1379
|
+
process2.stderr.write(
|
|
1380
|
+
`[cf-inspector] warning: ${context} looks mutation-capable and will run as a native breakpoint condition; native conditions cannot be side-effect-gated.
|
|
1381
|
+
`
|
|
1382
|
+
);
|
|
1383
|
+
}
|
|
1384
|
+
function warnOnMutationRisk(expression, context) {
|
|
1385
|
+
if (!looksLikeMutation(expression)) {
|
|
1386
|
+
return;
|
|
1387
|
+
}
|
|
1388
|
+
process2.stderr.write(
|
|
1389
|
+
`[cf-inspector] warning: ${context} looks mutation-capable and will execute against the live inspectee without a side-effect guard.
|
|
1390
|
+
`
|
|
1391
|
+
);
|
|
1392
|
+
}
|
|
1393
|
+
function warnOnUnboundBreakpoints(handles) {
|
|
1394
|
+
for (const handle of handles) {
|
|
1395
|
+
if (handle.resolvedLocations.length === 0) {
|
|
1396
|
+
const tsHint = handle.file.endsWith(".ts") ? " Hint: Source TS breakpoints may not bind. Try inspecting loaded scripts with list-scripts and target the compiled .js file instead." : "";
|
|
1397
|
+
process2.stderr.write(
|
|
1398
|
+
`[cf-inspector] warning: breakpoint ${handle.file}:${handle.line.toString()} did not bind to any loaded script. Check the path or pass --remote-root. Use 'list-scripts' to inspect what V8 currently has loaded.${tsHint}
|
|
1399
|
+
`
|
|
1400
|
+
);
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
function warnOnImplicitInspectorSelection(session, targetWasExplicit, workerWasExplicit) {
|
|
1405
|
+
const targetCount = session.targetCount ?? 1;
|
|
1406
|
+
const targetIndex = session.targetIndex ?? 0;
|
|
1407
|
+
if (!targetWasExplicit && targetCount > 1) {
|
|
1408
|
+
process2.stderr.write(
|
|
1409
|
+
`[cf-inspector] notice: attached to inspector target ${targetIndex.toString()} of ${targetCount.toString()}; pass --target <index> to pick another.
|
|
1410
|
+
`
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
const workerCount = session.workerTargets?.length ?? 0;
|
|
1414
|
+
if (!workerWasExplicit && workerCount > 0) {
|
|
1415
|
+
process2.stderr.write(
|
|
1416
|
+
`[cf-inspector] notice: attached to the main isolate; ${workerCount.toString()} Node ${workerCount === 1 ? "worker is" : "workers are"} available. Run list-targets and pass --worker <index> to inspect one.
|
|
1417
|
+
`
|
|
1418
|
+
);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
function warnOnBoundBreakpointWithoutHit(handles) {
|
|
1422
|
+
const boundCount = handles.reduce((count, handle) => {
|
|
1423
|
+
return count + handle.resolvedLocations.length;
|
|
1424
|
+
}, 0);
|
|
1425
|
+
if (boundCount === 0) {
|
|
1426
|
+
return;
|
|
1427
|
+
}
|
|
1428
|
+
process2.stderr.write(
|
|
1429
|
+
`[cf-inspector] warning: ${boundCount.toString()} breakpoint ${boundCount === 1 ? "location bound" : "locations bound"}, but no hit was observed. The code may be running in another worker isolate. Run list-targets and retry with --worker <index> for a NodeWorker sub-session or --target <index> for a raw target.
|
|
1430
|
+
`
|
|
1431
|
+
);
|
|
1432
|
+
}
|
|
1433
|
+
function roundDurationMs(durationMs) {
|
|
1434
|
+
return Math.round(durationMs * 1e3) / 1e3;
|
|
1435
|
+
}
|
|
1436
|
+
function warnOnUnmatchedPause(pause) {
|
|
1437
|
+
const reason = pause.reason.length > 0 ? pause.reason : "unknown";
|
|
1438
|
+
process2.stderr.write(
|
|
1439
|
+
`[cf-inspector] warning: target is paused by another debugger event (${reason} at ${formatPauseLocation(pause)}); waiting for it to resume...
|
|
1440
|
+
`
|
|
1441
|
+
);
|
|
1442
|
+
}
|
|
1443
|
+
function withPausedDuration(snapshot, pausedDurationMs) {
|
|
1444
|
+
const base = {
|
|
1445
|
+
reason: snapshot.reason,
|
|
1446
|
+
hitBreakpoints: snapshot.hitBreakpoints,
|
|
1447
|
+
capturedAt: snapshot.capturedAt,
|
|
1448
|
+
pausedDurationMs,
|
|
1449
|
+
captures: snapshot.captures
|
|
1450
|
+
};
|
|
1451
|
+
const withFrame = snapshot.topFrame === void 0 ? base : { ...base, topFrame: snapshot.topFrame };
|
|
1452
|
+
const withStack = snapshot.stack === void 0 ? withFrame : { ...withFrame, stack: snapshot.stack };
|
|
1453
|
+
return snapshot.exception === void 0 ? withStack : { ...withStack, exception: snapshot.exception };
|
|
1454
|
+
}
|
|
1455
|
+
function formatPauseLocation(pause) {
|
|
1456
|
+
const top = pause.callFrames[0];
|
|
1457
|
+
if (top === void 0) {
|
|
1458
|
+
return "(no call frame)";
|
|
1459
|
+
}
|
|
1460
|
+
const url = top.url !== void 0 && top.url.length > 0 ? top.url : "(unknown)";
|
|
1461
|
+
return `${url}:${(top.lineNumber + 1).toString()}:${(top.columnNumber + 1).toString()}`;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
898
1464
|
// src/cli/target.ts
|
|
899
1465
|
var CF_TUNNEL_STATUS_MESSAGES = {
|
|
900
1466
|
starting: "Preparing the Cloud Foundry debugger...",
|
|
@@ -922,41 +1488,48 @@ function parsePositiveInt(raw, label) {
|
|
|
922
1488
|
}
|
|
923
1489
|
return value;
|
|
924
1490
|
}
|
|
925
|
-
|
|
1491
|
+
function resolveTarget(opts, options = {}) {
|
|
926
1492
|
const port = parsePositiveInt(opts.port, "--port");
|
|
927
1493
|
const targetIndex = parseTargetIndex(opts.target);
|
|
1494
|
+
const workerIndex = parseSelectionIndex(opts.worker, "--worker");
|
|
928
1495
|
if (port !== void 0) {
|
|
929
|
-
return {
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
1496
|
+
return {
|
|
1497
|
+
kind: "port",
|
|
1498
|
+
port,
|
|
1499
|
+
host: opts.host ?? "127.0.0.1",
|
|
1500
|
+
...selectionOptions(targetIndex, workerIndex)
|
|
1501
|
+
};
|
|
934
1502
|
}
|
|
935
|
-
const tunnelTimeoutSec = parseTunnelTimeout(opts, options);
|
|
936
1503
|
const region = optionalText(opts.region);
|
|
937
|
-
const apiEndpoint = optionalText(opts.apiEndpoint);
|
|
938
1504
|
const org = optionalText(opts.org);
|
|
939
1505
|
const space = optionalText(opts.space);
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
1506
|
+
const app = optionalText(opts.app);
|
|
1507
|
+
const missingFlags = [
|
|
1508
|
+
...region === void 0 ? ["--region"] : [],
|
|
1509
|
+
...org === void 0 ? ["--org"] : [],
|
|
1510
|
+
...space === void 0 ? ["--space"] : [],
|
|
1511
|
+
...app === void 0 ? ["--app"] : []
|
|
1512
|
+
];
|
|
1513
|
+
if (region === void 0 || org === void 0 || space === void 0 || app === void 0) {
|
|
945
1514
|
throw new CfInspectorError(
|
|
946
1515
|
"MISSING_TARGET",
|
|
947
|
-
|
|
1516
|
+
`Cloud Foundry targeting requires explicit selectors. Missing: ${missingFlags.join(", ")}. cf-inspector does not consult ambient \`cf target\` because it can silently change between runs.`
|
|
948
1517
|
);
|
|
949
1518
|
}
|
|
950
1519
|
return buildCfTarget(
|
|
951
|
-
region
|
|
952
|
-
|
|
953
|
-
org
|
|
954
|
-
space
|
|
1520
|
+
region,
|
|
1521
|
+
optionalText(opts.apiEndpoint),
|
|
1522
|
+
org,
|
|
1523
|
+
space,
|
|
955
1524
|
app,
|
|
956
|
-
|
|
957
|
-
targetIndex
|
|
1525
|
+
parseTunnelTimeout(opts, options),
|
|
1526
|
+
targetIndex,
|
|
1527
|
+
workerIndex
|
|
958
1528
|
);
|
|
959
1529
|
}
|
|
1530
|
+
async function resolveTargetWithCurrentCfTarget(opts, options = {}) {
|
|
1531
|
+
return await Promise.resolve(resolveTarget(opts, options));
|
|
1532
|
+
}
|
|
960
1533
|
function parseTunnelTimeout(opts, options) {
|
|
961
1534
|
if (options.useTimeoutForTunnel === false) {
|
|
962
1535
|
return DEFAULT_CF_TIMEOUT_SEC;
|
|
@@ -964,19 +1537,31 @@ function parseTunnelTimeout(opts, options) {
|
|
|
964
1537
|
return parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_CF_TIMEOUT_SEC;
|
|
965
1538
|
}
|
|
966
1539
|
function parseTargetIndex(raw) {
|
|
1540
|
+
return parseSelectionIndex(raw, "--target");
|
|
1541
|
+
}
|
|
1542
|
+
function parseSelectionIndex(raw, label) {
|
|
967
1543
|
if (raw === void 0) {
|
|
968
1544
|
return void 0;
|
|
969
1545
|
}
|
|
970
1546
|
const value = Number.parseInt(raw, 10);
|
|
971
1547
|
if (Number.isNaN(value) || value < 0 || value.toString() !== raw.trim()) {
|
|
972
|
-
throw new CfInspectorError(
|
|
1548
|
+
throw new CfInspectorError(
|
|
1549
|
+
"INVALID_ARGUMENT",
|
|
1550
|
+
`Invalid ${label}: "${raw}" \u2014 expected a non-negative integer`
|
|
1551
|
+
);
|
|
973
1552
|
}
|
|
974
1553
|
return value;
|
|
975
1554
|
}
|
|
976
1555
|
function targetIndexOption(targetIndex) {
|
|
977
1556
|
return targetIndex === void 0 ? {} : { targetIndex };
|
|
978
1557
|
}
|
|
979
|
-
function
|
|
1558
|
+
function selectionOptions(targetIndex, workerIndex) {
|
|
1559
|
+
return {
|
|
1560
|
+
...targetIndexOption(targetIndex),
|
|
1561
|
+
...workerIndex === void 0 ? {} : { workerIndex }
|
|
1562
|
+
};
|
|
1563
|
+
}
|
|
1564
|
+
function buildCfTarget(region, apiEndpoint, org, space, app, tunnelTimeoutSec, targetIndex, workerIndex) {
|
|
980
1565
|
return {
|
|
981
1566
|
kind: "cf",
|
|
982
1567
|
region,
|
|
@@ -985,42 +1570,13 @@ function buildCfTarget(region, apiEndpoint, org, space, app, tunnelTimeoutSec, t
|
|
|
985
1570
|
space,
|
|
986
1571
|
app,
|
|
987
1572
|
tunnelTimeoutMs: tunnelTimeoutSec * 1e3,
|
|
988
|
-
...
|
|
1573
|
+
...selectionOptions(targetIndex, workerIndex)
|
|
989
1574
|
};
|
|
990
1575
|
}
|
|
991
1576
|
function optionalText(value) {
|
|
992
1577
|
const trimmed = value?.trim();
|
|
993
1578
|
return trimmed === void 0 || trimmed.length === 0 ? void 0 : trimmed;
|
|
994
1579
|
}
|
|
995
|
-
function currentCfOptions() {
|
|
996
|
-
const command = process2.env["CF_DEBUGGER_CF_BIN"];
|
|
997
|
-
return command === void 0 ? void 0 : { command };
|
|
998
|
-
}
|
|
999
|
-
async function readCurrentTarget() {
|
|
1000
|
-
try {
|
|
1001
|
-
return await readCurrentCfTarget(currentCfOptions());
|
|
1002
|
-
} catch (error) {
|
|
1003
|
-
throw new CfInspectorError(
|
|
1004
|
-
"MISSING_TARGET",
|
|
1005
|
-
"No current CF target found. Run `cf target -o <org> -s <space>` or pass --region/--org/--space.",
|
|
1006
|
-
error instanceof Error ? error.message : String(error)
|
|
1007
|
-
);
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
function currentRegion(current) {
|
|
1011
|
-
try {
|
|
1012
|
-
return requireCurrentCfRegion(current, "Pass --region explicitly.");
|
|
1013
|
-
} catch (error) {
|
|
1014
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
1015
|
-
throw new CfInspectorError("MISSING_TARGET", message);
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
function missingTargetError() {
|
|
1019
|
-
return new CfInspectorError(
|
|
1020
|
-
"MISSING_TARGET",
|
|
1021
|
-
"Provide either --port (and optionally --host), an --app with current cf target, or all of --region, --org, --space, --app."
|
|
1022
|
-
);
|
|
1023
|
-
}
|
|
1024
1580
|
async function withSession(target, fn, reportProgress) {
|
|
1025
1581
|
const tunnel = await openTarget(target, reportProgress);
|
|
1026
1582
|
let session;
|
|
@@ -1031,8 +1587,13 @@ async function withSession(target, fn, reportProgress) {
|
|
|
1031
1587
|
session = await connectInspector({
|
|
1032
1588
|
port: tunnel.port,
|
|
1033
1589
|
host: tunnel.host,
|
|
1034
|
-
...
|
|
1590
|
+
...selectionOptions(target.targetIndex, target.workerIndex)
|
|
1035
1591
|
});
|
|
1592
|
+
warnOnImplicitInspectorSelection(
|
|
1593
|
+
session,
|
|
1594
|
+
target.targetIndex !== void 0,
|
|
1595
|
+
target.workerIndex !== void 0
|
|
1596
|
+
);
|
|
1036
1597
|
reportProgress?.("Inspector session is ready.");
|
|
1037
1598
|
return await fn(session, tunnel.port);
|
|
1038
1599
|
} finally {
|
|
@@ -1107,15 +1668,30 @@ async function resume(session) {
|
|
|
1107
1668
|
async function setPauseOnExceptions(session, state) {
|
|
1108
1669
|
await session.client.send("Debugger.setPauseOnExceptions", { state });
|
|
1109
1670
|
}
|
|
1110
|
-
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
1671
|
+
async function evaluateOnFrame(session, callFrameId, expression, options = {}) {
|
|
1111
1672
|
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
1112
1673
|
callFrameId,
|
|
1113
1674
|
expression,
|
|
1114
1675
|
returnByValue: false,
|
|
1115
1676
|
generatePreview: true,
|
|
1116
|
-
silent: true
|
|
1677
|
+
silent: true,
|
|
1678
|
+
...options.throwOnSideEffect === void 0 ? {} : { throwOnSideEffect: options.throwOnSideEffect }
|
|
1117
1679
|
});
|
|
1118
1680
|
}
|
|
1681
|
+
function isSideEffectRefusal(result) {
|
|
1682
|
+
const classNames = [
|
|
1683
|
+
result.result?.className,
|
|
1684
|
+
result.exceptionDetails?.exception?.className
|
|
1685
|
+
];
|
|
1686
|
+
const descriptions = [
|
|
1687
|
+
result.result?.description,
|
|
1688
|
+
result.exceptionDetails?.exception?.description
|
|
1689
|
+
];
|
|
1690
|
+
const isEvalError = classNames.includes("EvalError");
|
|
1691
|
+
return isEvalError && descriptions.some(
|
|
1692
|
+
(description) => typeof description === "string" && description.toLowerCase().includes("possible side-effect in debug-evaluate")
|
|
1693
|
+
);
|
|
1694
|
+
}
|
|
1119
1695
|
async function evaluateGlobal(session, expression) {
|
|
1120
1696
|
return await session.client.send("Runtime.evaluate", {
|
|
1121
1697
|
expression,
|
|
@@ -1168,6 +1744,7 @@ async function getProperties(session, objectId) {
|
|
|
1168
1744
|
|
|
1169
1745
|
// src/cli/commands/eval.ts
|
|
1170
1746
|
async function handleEval(opts) {
|
|
1747
|
+
warnOnMutationRisk(opts.expr, "eval --expr");
|
|
1171
1748
|
const target = await resolveTargetWithCurrentCfTarget(opts);
|
|
1172
1749
|
const result = await withSession(target, async (session) => {
|
|
1173
1750
|
return await evaluateGlobal(session, opts.expr);
|
|
@@ -1209,8 +1786,8 @@ function writeHumanEvalResult(result) {
|
|
|
1209
1786
|
}
|
|
1210
1787
|
|
|
1211
1788
|
// src/cli/commands/exception.ts
|
|
1212
|
-
import { performance as
|
|
1213
|
-
import
|
|
1789
|
+
import { performance as performance4 } from "perf_hooks";
|
|
1790
|
+
import process5 from "process";
|
|
1214
1791
|
|
|
1215
1792
|
// src/pathMapper.ts
|
|
1216
1793
|
init_types();
|
|
@@ -1422,7 +1999,7 @@ async function removeBreakpoint(session, breakpointId) {
|
|
|
1422
1999
|
|
|
1423
2000
|
// src/inspector/pause.ts
|
|
1424
2001
|
init_types();
|
|
1425
|
-
import { performance as
|
|
2002
|
+
import { performance as performance3 } from "perf_hooks";
|
|
1426
2003
|
function pauseMatches(pause, breakpointIds, pauseReasons) {
|
|
1427
2004
|
if (pauseReasons !== void 0 && pauseReasons.length > 0) {
|
|
1428
2005
|
return pauseReasons.includes(pause.reason);
|
|
@@ -1433,7 +2010,7 @@ function pauseMatches(pause, breakpointIds, pauseReasons) {
|
|
|
1433
2010
|
return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
|
|
1434
2011
|
}
|
|
1435
2012
|
function remainingUntil(deadlineMs) {
|
|
1436
|
-
return Math.max(0, deadlineMs -
|
|
2013
|
+
return Math.max(0, deadlineMs - performance3.now());
|
|
1437
2014
|
}
|
|
1438
2015
|
function hasResumedSincePause(session, pause) {
|
|
1439
2016
|
const pauseAt = pause.receivedAtMs;
|
|
@@ -1463,7 +2040,7 @@ async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, timeout
|
|
|
1463
2040
|
}
|
|
1464
2041
|
try {
|
|
1465
2042
|
await session.client.waitFor("Debugger.resumed", { timeoutMs: remainingMs });
|
|
1466
|
-
session.debuggerState.lastResumedAtMs =
|
|
2043
|
+
session.debuggerState.lastResumedAtMs = performance3.now();
|
|
1467
2044
|
} catch (err) {
|
|
1468
2045
|
if (err instanceof CfInspectorError && err.code === "BREAKPOINT_NOT_HIT") {
|
|
1469
2046
|
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
@@ -1486,7 +2063,7 @@ async function handleUnmatchedPause(session, pause, options, deadlineMs) {
|
|
|
1486
2063
|
await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
|
|
1487
2064
|
}
|
|
1488
2065
|
async function waitForPause(session, options) {
|
|
1489
|
-
const deadlineMs =
|
|
2066
|
+
const deadlineMs = performance3.now() + options.timeoutMs;
|
|
1490
2067
|
const buffer = session.pauseBuffer;
|
|
1491
2068
|
while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
|
|
1492
2069
|
while (buffer.length > 0) {
|
|
@@ -1519,19 +2096,23 @@ async function waitForLivePause(session, options, deadlineMs) {
|
|
|
1519
2096
|
params = await session.client.waitFor("Debugger.paused", {
|
|
1520
2097
|
timeoutMs: remainingMs,
|
|
1521
2098
|
predicate: () => {
|
|
1522
|
-
receivedAtMs =
|
|
2099
|
+
receivedAtMs = performance3.now();
|
|
1523
2100
|
return true;
|
|
1524
2101
|
}
|
|
1525
2102
|
});
|
|
1526
2103
|
} finally {
|
|
1527
2104
|
session.pauseWaitGate.active = false;
|
|
1528
2105
|
}
|
|
1529
|
-
return toPauseEvent(params, receivedAtMs ??
|
|
2106
|
+
return toPauseEvent(params, receivedAtMs ?? performance3.now(), session.scripts);
|
|
1530
2107
|
}
|
|
1531
2108
|
|
|
2109
|
+
// src/snapshot/evaluation.ts
|
|
2110
|
+
init_types();
|
|
2111
|
+
|
|
1532
2112
|
// src/snapshot/values.ts
|
|
1533
2113
|
init_types();
|
|
1534
|
-
var DEFAULT_MAX_VALUE_LENGTH =
|
|
2114
|
+
var DEFAULT_MAX_VALUE_LENGTH = 131072;
|
|
2115
|
+
var DEFAULT_STREAM_MAX_VALUE_LENGTH = 4096;
|
|
1535
2116
|
function isPrimitive(value) {
|
|
1536
2117
|
const t = typeof value;
|
|
1537
2118
|
return t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol";
|
|
@@ -1559,9 +2140,16 @@ function resolveMaxValueLength(value) {
|
|
|
1559
2140
|
}
|
|
1560
2141
|
function limitValueLength(raw, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
1561
2142
|
if (raw.length <= maxValueLength) {
|
|
1562
|
-
return raw;
|
|
2143
|
+
return { text: raw, truncated: false };
|
|
1563
2144
|
}
|
|
1564
|
-
return
|
|
2145
|
+
return {
|
|
2146
|
+
text: raw.slice(0, maxValueLength),
|
|
2147
|
+
truncated: true,
|
|
2148
|
+
originalLength: raw.length
|
|
2149
|
+
};
|
|
2150
|
+
}
|
|
2151
|
+
function textTruncationFields(limited) {
|
|
2152
|
+
return limited.truncated ? { truncated: true, originalLength: limited.originalLength } : {};
|
|
1565
2153
|
}
|
|
1566
2154
|
function parseQuotedString(value) {
|
|
1567
2155
|
try {
|
|
@@ -1646,7 +2234,12 @@ function toStructuredValue(variable) {
|
|
|
1646
2234
|
// src/snapshot/evaluation.ts
|
|
1647
2235
|
function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
1648
2236
|
if (result.exceptionDetails !== void 0) {
|
|
1649
|
-
|
|
2237
|
+
const limited = readEvalError(result, maxValueLength);
|
|
2238
|
+
return {
|
|
2239
|
+
expression,
|
|
2240
|
+
error: limited.text,
|
|
2241
|
+
...textTruncationFields(limited)
|
|
2242
|
+
};
|
|
1650
2243
|
}
|
|
1651
2244
|
const inner = result.result;
|
|
1652
2245
|
if (!inner) {
|
|
@@ -1654,8 +2247,12 @@ function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_V
|
|
|
1654
2247
|
}
|
|
1655
2248
|
const type = typeof inner.type === "string" ? inner.type : void 0;
|
|
1656
2249
|
const buildCaptured = (rendered) => {
|
|
1657
|
-
const
|
|
1658
|
-
const base = {
|
|
2250
|
+
const limited = limitValueLength(rendered, maxValueLength);
|
|
2251
|
+
const base = {
|
|
2252
|
+
expression,
|
|
2253
|
+
value: limited.text,
|
|
2254
|
+
...textTruncationFields(limited)
|
|
2255
|
+
};
|
|
1659
2256
|
return type === void 0 ? base : { ...base, type };
|
|
1660
2257
|
};
|
|
1661
2258
|
if (type === "string" && typeof inner.value === "string") {
|
|
@@ -1672,6 +2269,18 @@ function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_V
|
|
|
1672
2269
|
}
|
|
1673
2270
|
return buildCaptured("undefined");
|
|
1674
2271
|
}
|
|
2272
|
+
function sideEffectRefusalToCaptured(expression) {
|
|
2273
|
+
const error = new CfInspectorError(
|
|
2274
|
+
"MUTATION_NOT_ALLOWED",
|
|
2275
|
+
`V8 blocked the capture expression "${expression}" because it may have side effects. Pass --allow-mutation to run it explicitly.`
|
|
2276
|
+
);
|
|
2277
|
+
return {
|
|
2278
|
+
expression,
|
|
2279
|
+
error: `${error.code}: ${error.message}`,
|
|
2280
|
+
mutationRisk: true,
|
|
2281
|
+
blocked: true
|
|
2282
|
+
};
|
|
2283
|
+
}
|
|
1675
2284
|
function readEvalError(result, maxValueLength) {
|
|
1676
2285
|
const text = typeof result.exceptionDetails?.exception?.description === "string" ? result.exceptionDetails.exception.description : typeof result.exceptionDetails?.text === "string" ? result.exceptionDetails.text : "evaluation failed";
|
|
1677
2286
|
return limitValueLength(text, maxValueLength);
|
|
@@ -1729,56 +2338,88 @@ async function captureProperties(session, objectId, limit, depth, maxValueLength
|
|
|
1729
2338
|
return await captureProperty(session, prop, depth, maxValueLength);
|
|
1730
2339
|
})
|
|
1731
2340
|
);
|
|
1732
|
-
|
|
2341
|
+
const omittedCount = Math.max(properties.length - limited.length, 0);
|
|
2342
|
+
return omittedCount === 0 ? { variables } : { variables, omittedCount };
|
|
1733
2343
|
}
|
|
1734
2344
|
async function captureProperty(session, prop, depth, maxValueLength) {
|
|
1735
2345
|
const name = typeof prop.name === "string" ? prop.name : "?";
|
|
1736
2346
|
const described = describeProperty(prop);
|
|
1737
|
-
const
|
|
1738
|
-
|
|
1739
|
-
|
|
2347
|
+
const capturedChildren = await capturePropertyChildren(
|
|
2348
|
+
session,
|
|
2349
|
+
described,
|
|
2350
|
+
depth,
|
|
2351
|
+
maxValueLength
|
|
2352
|
+
);
|
|
2353
|
+
const limited = limitValueLength(described.value, maxValueLength);
|
|
2354
|
+
const base = {
|
|
2355
|
+
name,
|
|
2356
|
+
value: limited.text,
|
|
2357
|
+
...textTruncationFields(limited)
|
|
2358
|
+
};
|
|
1740
2359
|
const withType = described.type === void 0 ? base : { ...base, type: described.type };
|
|
1741
|
-
|
|
2360
|
+
const children = capturedChildren?.variables;
|
|
2361
|
+
const withChildren = children === void 0 || children.length === 0 ? withType : { ...withType, children };
|
|
2362
|
+
const omittedCount = capturedChildren?.omittedCount ?? 0;
|
|
2363
|
+
return omittedCount === 0 ? withChildren : { ...withChildren, truncated: true, omittedCount };
|
|
1742
2364
|
}
|
|
1743
2365
|
async function capturePropertyChildren(session, described, depth, maxValueLength) {
|
|
1744
|
-
if (
|
|
2366
|
+
if (described.objectId === void 0 || !isExpandable(described.type)) {
|
|
1745
2367
|
return void 0;
|
|
1746
2368
|
}
|
|
2369
|
+
if (depth <= 0) {
|
|
2370
|
+
return await countDepthOmissions(session, described.objectId);
|
|
2371
|
+
}
|
|
1747
2372
|
try {
|
|
1748
|
-
|
|
2373
|
+
return await captureProperties(
|
|
1749
2374
|
session,
|
|
1750
2375
|
described.objectId,
|
|
1751
2376
|
MAX_CHILD_VARIABLES,
|
|
1752
2377
|
depth - 1,
|
|
1753
2378
|
maxValueLength
|
|
1754
2379
|
);
|
|
1755
|
-
return nested.length > 0 ? nested : void 0;
|
|
1756
2380
|
} catch {
|
|
1757
2381
|
return void 0;
|
|
1758
2382
|
}
|
|
1759
2383
|
}
|
|
2384
|
+
async function countDepthOmissions(session, objectId) {
|
|
2385
|
+
try {
|
|
2386
|
+
const properties = await getProperties(session, objectId);
|
|
2387
|
+
return properties.length === 0 ? void 0 : { variables: [], omittedCount: properties.length };
|
|
2388
|
+
} catch {
|
|
2389
|
+
return void 0;
|
|
2390
|
+
}
|
|
2391
|
+
}
|
|
2392
|
+
function countPropertyOmissions(captured) {
|
|
2393
|
+
return (captured.omittedCount ?? 0) + captured.variables.reduce((total, variable) => {
|
|
2394
|
+
const childOmissions = variable.children === void 0 ? 0 : countPropertyOmissions({ variables: variable.children });
|
|
2395
|
+
return total + (variable.omittedCount ?? 0) + childOmissions;
|
|
2396
|
+
}, 0);
|
|
2397
|
+
}
|
|
1760
2398
|
|
|
1761
2399
|
// src/snapshot/exception.ts
|
|
1762
2400
|
function asString2(value) {
|
|
1763
2401
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1764
2402
|
}
|
|
1765
|
-
async function materializeObject(session, objectId
|
|
2403
|
+
async function materializeObject(session, objectId) {
|
|
1766
2404
|
try {
|
|
1767
|
-
const
|
|
2405
|
+
const captured = await captureProperties(
|
|
1768
2406
|
session,
|
|
1769
2407
|
objectId,
|
|
1770
2408
|
MAX_SCOPE_VARIABLES,
|
|
1771
2409
|
MAX_VARIABLE_DEPTH,
|
|
1772
|
-
|
|
2410
|
+
Number.MAX_SAFE_INTEGER
|
|
1773
2411
|
);
|
|
1774
|
-
if (
|
|
2412
|
+
if (captured.variables.length === 0) {
|
|
1775
2413
|
return void 0;
|
|
1776
2414
|
}
|
|
1777
2415
|
const structured = {};
|
|
1778
|
-
for (const variable of
|
|
2416
|
+
for (const variable of captured.variables) {
|
|
1779
2417
|
structured[variable.name] = toStructuredValue(variable);
|
|
1780
2418
|
}
|
|
1781
|
-
return
|
|
2419
|
+
return {
|
|
2420
|
+
value: JSON.stringify(structured),
|
|
2421
|
+
omittedCount: countPropertyOmissions(captured)
|
|
2422
|
+
};
|
|
1782
2423
|
} catch {
|
|
1783
2424
|
return void 0;
|
|
1784
2425
|
}
|
|
@@ -1831,18 +2472,44 @@ async function captureException(session, pause, maxValueLength) {
|
|
|
1831
2472
|
return { error: "exception data has no objectId or value" };
|
|
1832
2473
|
}
|
|
1833
2474
|
const message = await readPropertyDescription(session, objectId, "message");
|
|
1834
|
-
const rendered = await materializeObject(session, objectId
|
|
2475
|
+
const rendered = await materializeObject(session, objectId);
|
|
1835
2476
|
if (rendered !== void 0) {
|
|
1836
|
-
|
|
1837
|
-
|
|
2477
|
+
return buildResult(
|
|
2478
|
+
type,
|
|
2479
|
+
message ?? description,
|
|
2480
|
+
rendered.value,
|
|
2481
|
+
maxValueLength,
|
|
2482
|
+
rendered.omittedCount
|
|
2483
|
+
);
|
|
1838
2484
|
}
|
|
1839
2485
|
return buildResult(type, description, description ?? "[exception]", maxValueLength);
|
|
1840
2486
|
}
|
|
1841
|
-
function buildResult(type, description, value, maxValueLength) {
|
|
1842
|
-
const
|
|
1843
|
-
const
|
|
2487
|
+
function buildResult(type, description, value, maxValueLength, omittedCount = 0) {
|
|
2488
|
+
const limitedValue = limitValueLength(value, maxValueLength);
|
|
2489
|
+
const limitedDescription = description === void 0 ? void 0 : limitValueLength(description, maxValueLength);
|
|
2490
|
+
const base = {
|
|
2491
|
+
value: limitedValue.text,
|
|
2492
|
+
...exceptionTruncationFields(limitedValue, limitedDescription)
|
|
2493
|
+
};
|
|
1844
2494
|
const withType = type === void 0 ? base : { ...base, type };
|
|
1845
|
-
|
|
2495
|
+
const withDescription = limitedDescription === void 0 ? withType : { ...withType, description: limitedDescription.text };
|
|
2496
|
+
return omittedCount === 0 ? withDescription : { ...withDescription, truncated: true, omittedCount };
|
|
2497
|
+
}
|
|
2498
|
+
function exceptionTruncationFields(value, description) {
|
|
2499
|
+
const valueLength = value.truncated ? value.originalLength : void 0;
|
|
2500
|
+
const descriptionLength = description?.truncated === true ? description.originalLength : void 0;
|
|
2501
|
+
const lengths = [valueLength, descriptionLength].filter(
|
|
2502
|
+
(length) => length !== void 0
|
|
2503
|
+
);
|
|
2504
|
+
if (lengths.length === 0) {
|
|
2505
|
+
return {};
|
|
2506
|
+
}
|
|
2507
|
+
return {
|
|
2508
|
+
truncated: true,
|
|
2509
|
+
originalLength: Math.max(...lengths),
|
|
2510
|
+
...valueLength === void 0 ? {} : { valueOriginalLength: valueLength },
|
|
2511
|
+
...descriptionLength === void 0 ? {} : { descriptionOriginalLength: descriptionLength }
|
|
2512
|
+
};
|
|
1846
2513
|
}
|
|
1847
2514
|
|
|
1848
2515
|
// src/snapshot/objects.ts
|
|
@@ -1857,20 +2524,23 @@ function objectIdFromEvalResult(result) {
|
|
|
1857
2524
|
}
|
|
1858
2525
|
return objectId;
|
|
1859
2526
|
}
|
|
1860
|
-
async function renderObjectCapture(session, objectId
|
|
2527
|
+
async function renderObjectCapture(session, objectId) {
|
|
1861
2528
|
try {
|
|
1862
|
-
const
|
|
2529
|
+
const captured = await captureProperties(
|
|
1863
2530
|
session,
|
|
1864
2531
|
objectId,
|
|
1865
2532
|
MAX_SCOPE_VARIABLES,
|
|
1866
2533
|
MAX_VARIABLE_DEPTH,
|
|
1867
|
-
|
|
2534
|
+
Number.MAX_SAFE_INTEGER
|
|
1868
2535
|
);
|
|
1869
2536
|
const structured = {};
|
|
1870
|
-
for (const variable of
|
|
2537
|
+
for (const variable of captured.variables) {
|
|
1871
2538
|
structured[variable.name] = toStructuredValue(variable);
|
|
1872
2539
|
}
|
|
1873
|
-
return
|
|
2540
|
+
return {
|
|
2541
|
+
value: JSON.stringify(structured),
|
|
2542
|
+
omittedCount: countPropertyOmissions(captured)
|
|
2543
|
+
};
|
|
1874
2544
|
} catch {
|
|
1875
2545
|
return void 0;
|
|
1876
2546
|
}
|
|
@@ -1892,16 +2562,22 @@ async function withSerializedObjectCapture(session, expression, evalResult, capt
|
|
|
1892
2562
|
if (objectId === void 0) {
|
|
1893
2563
|
return captured;
|
|
1894
2564
|
}
|
|
1895
|
-
const rendered = await renderObjectCapture(session, objectId
|
|
2565
|
+
const rendered = await renderObjectCapture(session, objectId);
|
|
1896
2566
|
if (rendered === void 0) {
|
|
1897
2567
|
return captured;
|
|
1898
2568
|
}
|
|
1899
|
-
const normalized = normalizeRenderedObjectCapture(rendered, captured.value);
|
|
2569
|
+
const normalized = normalizeRenderedObjectCapture(rendered.value, captured.value);
|
|
1900
2570
|
if (normalized === void 0) {
|
|
1901
2571
|
return captured;
|
|
1902
2572
|
}
|
|
1903
|
-
const
|
|
1904
|
-
|
|
2573
|
+
const limited = limitValueLength(normalized, maxValueLength);
|
|
2574
|
+
const base = {
|
|
2575
|
+
expression,
|
|
2576
|
+
value: limited.text,
|
|
2577
|
+
...textTruncationFields(limited),
|
|
2578
|
+
...captured.type === void 0 ? {} : { type: captured.type }
|
|
2579
|
+
};
|
|
2580
|
+
return rendered.omittedCount === 0 ? base : { ...base, truncated: true, omittedCount: rendered.omittedCount };
|
|
1905
2581
|
}
|
|
1906
2582
|
|
|
1907
2583
|
// src/snapshot/scopes.ts
|
|
@@ -1916,35 +2592,39 @@ var PRIORITY_BY_TYPE = {
|
|
|
1916
2592
|
module: 6,
|
|
1917
2593
|
script: 7
|
|
1918
2594
|
};
|
|
1919
|
-
function
|
|
2595
|
+
function rankedScopes(scopeChain) {
|
|
1920
2596
|
const eligible = scopeChain.filter((scope) => scope.objectId !== void 0 && scope.type !== "global");
|
|
1921
|
-
return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type))
|
|
2597
|
+
return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type));
|
|
1922
2598
|
}
|
|
1923
2599
|
function priorityOf(type) {
|
|
1924
2600
|
return PRIORITY_BY_TYPE[type] ?? Number.MAX_SAFE_INTEGER;
|
|
1925
2601
|
}
|
|
1926
2602
|
async function captureScopes(session, frame, maxValueLength) {
|
|
1927
|
-
const
|
|
1928
|
-
|
|
2603
|
+
const ranked = rankedScopes(frame.scopeChain);
|
|
2604
|
+
const scopes = ranked.slice(0, MAX_SCOPES);
|
|
2605
|
+
const capturedScopes = await Promise.all(
|
|
1929
2606
|
scopes.map(async (scope) => {
|
|
1930
2607
|
const objectId = scope.objectId;
|
|
1931
2608
|
if (objectId === void 0) {
|
|
1932
2609
|
return { type: scope.type, variables: [] };
|
|
1933
2610
|
}
|
|
1934
2611
|
try {
|
|
1935
|
-
const
|
|
2612
|
+
const captured = await captureProperties(
|
|
1936
2613
|
session,
|
|
1937
2614
|
objectId,
|
|
1938
2615
|
MAX_SCOPE_VARIABLES,
|
|
1939
2616
|
MAX_VARIABLE_DEPTH,
|
|
1940
2617
|
maxValueLength
|
|
1941
2618
|
);
|
|
1942
|
-
|
|
2619
|
+
const base = { type: scope.type, variables: captured.variables };
|
|
2620
|
+
return captured.omittedCount === void 0 ? base : { ...base, truncated: true, omittedCount: captured.omittedCount };
|
|
1943
2621
|
} catch {
|
|
1944
2622
|
return { type: scope.type, variables: [] };
|
|
1945
2623
|
}
|
|
1946
2624
|
})
|
|
1947
2625
|
);
|
|
2626
|
+
const omittedCount = Math.max(ranked.length - capturedScopes.length, 0);
|
|
2627
|
+
return omittedCount === 0 ? { scopes: capturedScopes } : { scopes: capturedScopes, omittedCount };
|
|
1948
2628
|
}
|
|
1949
2629
|
|
|
1950
2630
|
// src/snapshot/stack.ts
|
|
@@ -1964,23 +2644,48 @@ function buildBaseFrame(frame) {
|
|
|
1964
2644
|
};
|
|
1965
2645
|
return frame.url === void 0 ? base : { ...base, url: frame.url };
|
|
1966
2646
|
}
|
|
1967
|
-
async function captureFrameExpression(session, callFrameId, expression, maxValueLength) {
|
|
2647
|
+
async function captureFrameExpression(session, callFrameId, expression, maxValueLength, throwOnSideEffect) {
|
|
2648
|
+
const mutationRisk = throwOnSideEffect === false && looksLikeMutation(expression);
|
|
1968
2649
|
try {
|
|
1969
|
-
const result = await evaluateOnFrame(session, callFrameId, expression
|
|
2650
|
+
const result = await evaluateOnFrame(session, callFrameId, expression, {
|
|
2651
|
+
...throwOnSideEffect === void 0 ? {} : { throwOnSideEffect }
|
|
2652
|
+
});
|
|
2653
|
+
if (isSideEffectRefusal(result)) {
|
|
2654
|
+
return sideEffectRefusalToCaptured(expression);
|
|
2655
|
+
}
|
|
1970
2656
|
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1971
|
-
|
|
2657
|
+
const serialized = await withSerializedObjectCapture(
|
|
2658
|
+
session,
|
|
2659
|
+
expression,
|
|
2660
|
+
result,
|
|
2661
|
+
captured,
|
|
2662
|
+
maxValueLength
|
|
2663
|
+
);
|
|
2664
|
+
return mutationRisk ? { ...serialized, mutationRisk: true } : serialized;
|
|
1972
2665
|
} catch (err) {
|
|
1973
2666
|
const message = err instanceof Error ? err.message : String(err);
|
|
1974
|
-
|
|
2667
|
+
const limited = limitValueLength(message, maxValueLength);
|
|
2668
|
+
const captured = {
|
|
2669
|
+
expression,
|
|
2670
|
+
error: limited.text,
|
|
2671
|
+
...textTruncationFields(limited)
|
|
2672
|
+
};
|
|
2673
|
+
return mutationRisk ? { ...captured, mutationRisk: true } : captured;
|
|
1975
2674
|
}
|
|
1976
2675
|
}
|
|
1977
|
-
async function captureFrameExpressions(session, frame, expressions, maxValueLength) {
|
|
2676
|
+
async function captureFrameExpressions(session, frame, expressions, maxValueLength, throwOnSideEffect) {
|
|
1978
2677
|
if (expressions.length === 0) {
|
|
1979
2678
|
return [];
|
|
1980
2679
|
}
|
|
1981
2680
|
return await Promise.all(
|
|
1982
2681
|
expressions.map(
|
|
1983
|
-
(expression) => captureFrameExpression(
|
|
2682
|
+
(expression) => captureFrameExpression(
|
|
2683
|
+
session,
|
|
2684
|
+
frame.callFrameId,
|
|
2685
|
+
expression,
|
|
2686
|
+
maxValueLength,
|
|
2687
|
+
throwOnSideEffect
|
|
2688
|
+
)
|
|
1984
2689
|
)
|
|
1985
2690
|
);
|
|
1986
2691
|
}
|
|
@@ -2000,7 +2705,8 @@ async function walkStack(session, callFrames, options) {
|
|
|
2000
2705
|
session,
|
|
2001
2706
|
frame,
|
|
2002
2707
|
options.stackCaptures,
|
|
2003
|
-
options.maxValueLength
|
|
2708
|
+
options.maxValueLength,
|
|
2709
|
+
options.throwOnSideEffect
|
|
2004
2710
|
);
|
|
2005
2711
|
return { ...base, captures };
|
|
2006
2712
|
})
|
|
@@ -2022,14 +2728,25 @@ async function captureSnapshot(session, pause, options = {}) {
|
|
|
2022
2728
|
column: top.columnNumber + 1
|
|
2023
2729
|
};
|
|
2024
2730
|
if (options.includeScopes === true) {
|
|
2025
|
-
const
|
|
2026
|
-
topFrame = {
|
|
2731
|
+
const capturedScopes = await captureScopes(session, top, maxValueLength);
|
|
2732
|
+
topFrame = {
|
|
2733
|
+
...topFrame,
|
|
2734
|
+
scopes: capturedScopes.scopes,
|
|
2735
|
+
...capturedScopes.omittedCount === void 0 ? {} : { truncated: true, omittedCount: capturedScopes.omittedCount }
|
|
2736
|
+
};
|
|
2027
2737
|
}
|
|
2028
|
-
captures = await captureExpressions(
|
|
2738
|
+
captures = await captureExpressions(
|
|
2739
|
+
session,
|
|
2740
|
+
top.callFrameId,
|
|
2741
|
+
options.captures,
|
|
2742
|
+
maxValueLength,
|
|
2743
|
+
options.throwOnSideEffect
|
|
2744
|
+
);
|
|
2029
2745
|
stack = await walkStack(session, pause.callFrames, {
|
|
2030
2746
|
stackDepth: options.stackDepth ?? DEFAULT_STACK_DEPTH,
|
|
2031
2747
|
stackCaptures: options.stackCaptures ?? [],
|
|
2032
|
-
maxValueLength
|
|
2748
|
+
maxValueLength,
|
|
2749
|
+
...options.throwOnSideEffect === void 0 ? {} : { throwOnSideEffect: options.throwOnSideEffect }
|
|
2033
2750
|
});
|
|
2034
2751
|
}
|
|
2035
2752
|
const exception = await captureException(session, pause, maxValueLength);
|
|
@@ -2052,159 +2769,62 @@ function buildResult2(input) {
|
|
|
2052
2769
|
const withStack = input.stack.length > 0 ? { ...withFrame, stack: input.stack } : withFrame;
|
|
2053
2770
|
return input.exception === void 0 ? withStack : { ...withStack, exception: input.exception };
|
|
2054
2771
|
}
|
|
2055
|
-
async function captureExpressions(session, callFrameId, captures, maxValueLength) {
|
|
2772
|
+
async function captureExpressions(session, callFrameId, captures, maxValueLength, throwOnSideEffect) {
|
|
2056
2773
|
if (captures === void 0 || captures.length === 0) {
|
|
2057
2774
|
return [];
|
|
2058
2775
|
}
|
|
2059
2776
|
return await Promise.all(
|
|
2060
2777
|
captures.map(async (expression) => {
|
|
2061
|
-
return await captureExpression(
|
|
2778
|
+
return await captureExpression(
|
|
2779
|
+
session,
|
|
2780
|
+
callFrameId,
|
|
2781
|
+
expression,
|
|
2782
|
+
maxValueLength,
|
|
2783
|
+
throwOnSideEffect
|
|
2784
|
+
);
|
|
2062
2785
|
})
|
|
2063
2786
|
);
|
|
2064
2787
|
}
|
|
2065
|
-
async function captureExpression(session, callFrameId, expression, maxValueLength) {
|
|
2788
|
+
async function captureExpression(session, callFrameId, expression, maxValueLength, throwOnSideEffect) {
|
|
2789
|
+
const mutationRisk = throwOnSideEffect === false && looksLikeMutation(expression);
|
|
2066
2790
|
try {
|
|
2067
|
-
const result = await evaluateOnFrame(session, callFrameId, expression
|
|
2791
|
+
const result = await evaluateOnFrame(session, callFrameId, expression, {
|
|
2792
|
+
...throwOnSideEffect === void 0 ? {} : { throwOnSideEffect }
|
|
2793
|
+
});
|
|
2794
|
+
if (isSideEffectRefusal(result)) {
|
|
2795
|
+
return sideEffectRefusalToCaptured(expression);
|
|
2796
|
+
}
|
|
2068
2797
|
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
2069
|
-
|
|
2798
|
+
const serialized = await withSerializedObjectCapture(
|
|
2799
|
+
session,
|
|
2800
|
+
expression,
|
|
2801
|
+
result,
|
|
2802
|
+
captured,
|
|
2803
|
+
maxValueLength
|
|
2804
|
+
);
|
|
2805
|
+
return mutationRisk ? { ...serialized, mutationRisk: true } : serialized;
|
|
2070
2806
|
} catch (err) {
|
|
2071
2807
|
const message = err instanceof Error ? err.message : String(err);
|
|
2072
|
-
|
|
2808
|
+
const limited = limitValueLength(message, maxValueLength);
|
|
2809
|
+
const captured = {
|
|
2810
|
+
expression,
|
|
2811
|
+
error: limited.text,
|
|
2812
|
+
...textTruncationFields(limited)
|
|
2813
|
+
};
|
|
2814
|
+
return mutationRisk ? { ...captured, mutationRisk: true } : captured;
|
|
2073
2815
|
}
|
|
2074
2816
|
}
|
|
2075
2817
|
|
|
2076
2818
|
// src/cli/commands/exception.ts
|
|
2077
2819
|
init_types();
|
|
2078
|
-
|
|
2079
|
-
// src/cli/captureParser.ts
|
|
2080
|
-
function parseCaptureList(raw) {
|
|
2081
|
-
if (raw === void 0 || raw.trim().length === 0) {
|
|
2082
|
-
return [];
|
|
2083
|
-
}
|
|
2084
|
-
return splitCaptureExpressions(raw);
|
|
2085
|
-
}
|
|
2086
|
-
function isQuoteChar(value) {
|
|
2087
|
-
return value === "'" || value === '"' || value === "`";
|
|
2088
|
-
}
|
|
2089
|
-
function consumeQuotedChar(state, char) {
|
|
2090
|
-
if (state.quote === void 0) {
|
|
2091
|
-
return false;
|
|
2092
|
-
}
|
|
2093
|
-
if (state.escaped) {
|
|
2094
|
-
state.escaped = false;
|
|
2095
|
-
return true;
|
|
2096
|
-
}
|
|
2097
|
-
if (char === "\\") {
|
|
2098
|
-
state.escaped = true;
|
|
2099
|
-
return true;
|
|
2100
|
-
}
|
|
2101
|
-
if (char === state.quote) {
|
|
2102
|
-
state.quote = void 0;
|
|
2103
|
-
}
|
|
2104
|
-
return true;
|
|
2105
|
-
}
|
|
2106
|
-
function updateCaptureDepth(state, char) {
|
|
2107
|
-
if (char === "(") {
|
|
2108
|
-
state.parenDepth += 1;
|
|
2109
|
-
} else if (char === ")") {
|
|
2110
|
-
state.parenDepth = Math.max(0, state.parenDepth - 1);
|
|
2111
|
-
} else if (char === "[") {
|
|
2112
|
-
state.bracketDepth += 1;
|
|
2113
|
-
} else if (char === "]") {
|
|
2114
|
-
state.bracketDepth = Math.max(0, state.bracketDepth - 1);
|
|
2115
|
-
} else if (char === "{") {
|
|
2116
|
-
state.braceDepth += 1;
|
|
2117
|
-
} else if (char === "}") {
|
|
2118
|
-
state.braceDepth = Math.max(0, state.braceDepth - 1);
|
|
2119
|
-
}
|
|
2120
|
-
}
|
|
2121
|
-
function isTopLevel(state) {
|
|
2122
|
-
return state.parenDepth === 0 && state.bracketDepth === 0 && state.braceDepth === 0;
|
|
2123
|
-
}
|
|
2124
|
-
function appendCapturePiece(raw, state, end) {
|
|
2125
|
-
const piece = raw.slice(state.start, end).trim();
|
|
2126
|
-
if (piece.length > 0) {
|
|
2127
|
-
state.pieces.push(piece);
|
|
2128
|
-
}
|
|
2129
|
-
}
|
|
2130
|
-
function splitCaptureExpressions(raw) {
|
|
2131
|
-
const state = {
|
|
2132
|
-
escaped: false,
|
|
2133
|
-
parenDepth: 0,
|
|
2134
|
-
bracketDepth: 0,
|
|
2135
|
-
braceDepth: 0,
|
|
2136
|
-
quote: void 0,
|
|
2137
|
-
start: 0,
|
|
2138
|
-
pieces: []
|
|
2139
|
-
};
|
|
2140
|
-
for (let idx = 0; idx < raw.length; idx += 1) {
|
|
2141
|
-
const char = raw.charAt(idx);
|
|
2142
|
-
if (consumeQuotedChar(state, char)) {
|
|
2143
|
-
continue;
|
|
2144
|
-
}
|
|
2145
|
-
if (isQuoteChar(char)) {
|
|
2146
|
-
state.quote = char;
|
|
2147
|
-
continue;
|
|
2148
|
-
}
|
|
2149
|
-
updateCaptureDepth(state, char);
|
|
2150
|
-
if (char === "," && isTopLevel(state)) {
|
|
2151
|
-
appendCapturePiece(raw, state, idx);
|
|
2152
|
-
state.start = idx + 1;
|
|
2153
|
-
}
|
|
2154
|
-
}
|
|
2155
|
-
appendCapturePiece(raw, state, raw.length);
|
|
2156
|
-
return state.pieces;
|
|
2157
|
-
}
|
|
2158
|
-
|
|
2159
|
-
// src/cli/warnings.ts
|
|
2160
|
-
import process5 from "process";
|
|
2161
|
-
function warnOnUnboundBreakpoints(handles) {
|
|
2162
|
-
for (const handle of handles) {
|
|
2163
|
-
if (handle.resolvedLocations.length === 0) {
|
|
2164
|
-
const tsHint = handle.file.endsWith(".ts") ? " Hint: Source TS breakpoints may not bind. Try inspecting loaded scripts with list-scripts and target the compiled .js file instead." : "";
|
|
2165
|
-
process5.stderr.write(
|
|
2166
|
-
`[cf-inspector] warning: breakpoint ${handle.file}:${handle.line.toString()} did not bind to any loaded script. Check the path or pass --remote-root. Use 'list-scripts' to inspect what V8 currently has loaded.${tsHint}
|
|
2167
|
-
`
|
|
2168
|
-
);
|
|
2169
|
-
}
|
|
2170
|
-
}
|
|
2171
|
-
}
|
|
2172
|
-
function roundDurationMs(durationMs) {
|
|
2173
|
-
return Math.round(durationMs * 1e3) / 1e3;
|
|
2174
|
-
}
|
|
2175
|
-
function warnOnUnmatchedPause(pause) {
|
|
2176
|
-
const reason = pause.reason.length > 0 ? pause.reason : "unknown";
|
|
2177
|
-
process5.stderr.write(
|
|
2178
|
-
`[cf-inspector] warning: target is paused by another debugger event (${reason} at ${formatPauseLocation(pause)}); waiting for it to resume...
|
|
2179
|
-
`
|
|
2180
|
-
);
|
|
2181
|
-
}
|
|
2182
|
-
function withPausedDuration(snapshot, pausedDurationMs) {
|
|
2183
|
-
const base = {
|
|
2184
|
-
reason: snapshot.reason,
|
|
2185
|
-
hitBreakpoints: snapshot.hitBreakpoints,
|
|
2186
|
-
capturedAt: snapshot.capturedAt,
|
|
2187
|
-
pausedDurationMs,
|
|
2188
|
-
captures: snapshot.captures
|
|
2189
|
-
};
|
|
2190
|
-
const withFrame = snapshot.topFrame === void 0 ? base : { ...base, topFrame: snapshot.topFrame };
|
|
2191
|
-
const withStack = snapshot.stack === void 0 ? withFrame : { ...withFrame, stack: snapshot.stack };
|
|
2192
|
-
return snapshot.exception === void 0 ? withStack : { ...withStack, exception: snapshot.exception };
|
|
2193
|
-
}
|
|
2194
|
-
function formatPauseLocation(pause) {
|
|
2195
|
-
const top = pause.callFrames[0];
|
|
2196
|
-
if (top === void 0) {
|
|
2197
|
-
return "(no call frame)";
|
|
2198
|
-
}
|
|
2199
|
-
const url = top.url !== void 0 && top.url.length > 0 ? top.url : "(unknown)";
|
|
2200
|
-
return `${url}:${(top.lineNumber + 1).toString()}:${(top.columnNumber + 1).toString()}`;
|
|
2201
|
-
}
|
|
2202
|
-
|
|
2203
|
-
// src/cli/commands/exception.ts
|
|
2204
2820
|
var VALID_PAUSE_TYPES = ["uncaught", "caught", "all"];
|
|
2205
2821
|
async function handleException(opts) {
|
|
2206
2822
|
const target = await resolveTargetWithCurrentCfTarget(opts, { useTimeoutForTunnel: false });
|
|
2207
2823
|
const prepared = prepareExceptionCommand(opts, target);
|
|
2824
|
+
warnOnCaptureMutationRisk(
|
|
2825
|
+
[...prepared.captures, ...prepared.stackCaptures],
|
|
2826
|
+
opts.allowMutation === true
|
|
2827
|
+
);
|
|
2208
2828
|
const result = await runExceptionCommand(prepared, opts);
|
|
2209
2829
|
if (opts.json) {
|
|
2210
2830
|
writeJson(result);
|
|
@@ -2221,7 +2841,7 @@ function prepareExceptionCommand(opts, target) {
|
|
|
2221
2841
|
);
|
|
2222
2842
|
}
|
|
2223
2843
|
const timeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_EXCEPTION_TIMEOUT_SEC;
|
|
2224
|
-
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
2844
|
+
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length") ?? DEFAULT_MAX_VALUE_LENGTH;
|
|
2225
2845
|
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
2226
2846
|
return {
|
|
2227
2847
|
target,
|
|
@@ -2229,9 +2849,10 @@ function prepareExceptionCommand(opts, target) {
|
|
|
2229
2849
|
captures: parseCaptureList(opts.capture),
|
|
2230
2850
|
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
2231
2851
|
timeoutMs: timeoutSec * 1e3,
|
|
2232
|
-
|
|
2852
|
+
maxValueLength,
|
|
2233
2853
|
...stackDepth === void 0 ? {} : { stackDepth },
|
|
2234
|
-
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
2854
|
+
stackCaptures: parseCaptureList(opts.stackCaptures),
|
|
2855
|
+
throwOnSideEffect: opts.allowMutation !== true
|
|
2235
2856
|
};
|
|
2236
2857
|
}
|
|
2237
2858
|
async function runExceptionCommand(command, opts) {
|
|
@@ -2243,13 +2864,14 @@ async function runExceptionCommand(command, opts) {
|
|
|
2243
2864
|
pauseReasons: ["exception", "promiseRejection"],
|
|
2244
2865
|
unmatchedPausePolicy: "wait-for-resume"
|
|
2245
2866
|
});
|
|
2246
|
-
const pausedStartedAt = pause.receivedAtMs ??
|
|
2867
|
+
const pausedStartedAt = pause.receivedAtMs ?? performance4.now();
|
|
2247
2868
|
const snapshot = await captureSnapshot(session, pause, {
|
|
2248
2869
|
captures: command.captures,
|
|
2249
2870
|
includeScopes: opts.includeScopes === true,
|
|
2250
|
-
|
|
2871
|
+
maxValueLength: command.maxValueLength,
|
|
2251
2872
|
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
2252
|
-
stackCaptures: command.stackCaptures
|
|
2873
|
+
stackCaptures: command.stackCaptures,
|
|
2874
|
+
throwOnSideEffect: command.throwOnSideEffect
|
|
2253
2875
|
});
|
|
2254
2876
|
if (opts.keepPaused === true) {
|
|
2255
2877
|
return withPausedDuration(snapshot, null);
|
|
@@ -2263,9 +2885,9 @@ async function runExceptionCommand(command, opts) {
|
|
|
2263
2885
|
async function resumeAfterException(session, snapshot, pausedStartedAt) {
|
|
2264
2886
|
try {
|
|
2265
2887
|
await resume(session);
|
|
2266
|
-
return withPausedDuration(snapshot, roundDurationMs(
|
|
2888
|
+
return withPausedDuration(snapshot, roundDurationMs(performance4.now() - pausedStartedAt));
|
|
2267
2889
|
} catch {
|
|
2268
|
-
|
|
2890
|
+
process5.stderr.write(
|
|
2269
2891
|
"[cf-inspector] warning: Debugger.resume failed after exception capture; pausedDurationMs is unknown.\n"
|
|
2270
2892
|
);
|
|
2271
2893
|
return withPausedDuration(snapshot, null);
|
|
@@ -2279,7 +2901,7 @@ async function disablePauseOnExceptionsBestEffort(session) {
|
|
|
2279
2901
|
}
|
|
2280
2902
|
|
|
2281
2903
|
// src/cli/commands/listScripts.ts
|
|
2282
|
-
import
|
|
2904
|
+
import process6 from "process";
|
|
2283
2905
|
async function handleListScripts(opts) {
|
|
2284
2906
|
const target = await resolveTargetWithCurrentCfTarget(opts);
|
|
2285
2907
|
const filter = compileScriptUrlFilter(opts.filter);
|
|
@@ -2289,7 +2911,7 @@ async function handleListScripts(opts) {
|
|
|
2289
2911
|
return;
|
|
2290
2912
|
}
|
|
2291
2913
|
for (const script of scripts) {
|
|
2292
|
-
|
|
2914
|
+
process6.stdout.write(`${script.scriptId} ${script.url}
|
|
2293
2915
|
`);
|
|
2294
2916
|
}
|
|
2295
2917
|
}
|
|
@@ -2298,19 +2920,86 @@ async function handleListTargets(opts) {
|
|
|
2298
2920
|
const tunnel = await openTarget(target);
|
|
2299
2921
|
try {
|
|
2300
2922
|
const targets = await discoverInspectorTargets(tunnel.host, tunnel.port, 5e3);
|
|
2301
|
-
const indexedTargets =
|
|
2923
|
+
const indexedTargets = await buildListedTargets(targets);
|
|
2924
|
+
const workerCount = indexedTargets.reduce((count, targetEntry) => {
|
|
2925
|
+
return count + targetEntry.workers.length;
|
|
2926
|
+
}, 0);
|
|
2927
|
+
writeTargetCountSummary(indexedTargets.length, workerCount);
|
|
2928
|
+
warnOnMissingWorkers(indexedTargets.length, workerCount, indexedTargets);
|
|
2302
2929
|
if (opts.json) {
|
|
2303
2930
|
writeJson(indexedTargets);
|
|
2304
2931
|
return;
|
|
2305
2932
|
}
|
|
2306
|
-
|
|
2307
|
-
process7.stdout.write(`${entry.index.toString()} ${entry.type} ${entry.title} ${entry.url}
|
|
2308
|
-
`);
|
|
2309
|
-
}
|
|
2933
|
+
writeHumanTargets(indexedTargets);
|
|
2310
2934
|
} finally {
|
|
2311
2935
|
await tunnel.dispose();
|
|
2312
2936
|
}
|
|
2313
2937
|
}
|
|
2938
|
+
async function buildListedTargets(targets) {
|
|
2939
|
+
return await Promise.all(targets.map(async (target, index) => {
|
|
2940
|
+
try {
|
|
2941
|
+
const workerResult = await discoverNodeWorkerTargets(target);
|
|
2942
|
+
return buildListedTarget(target, index, workerResult.supported, workerResult.workers);
|
|
2943
|
+
} catch (error) {
|
|
2944
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2945
|
+
process6.stderr.write(
|
|
2946
|
+
`[cf-inspector] warning: worker discovery failed for raw target ${index.toString()}: ${message}
|
|
2947
|
+
`
|
|
2948
|
+
);
|
|
2949
|
+
return buildListedTarget(target, index, false, []);
|
|
2950
|
+
}
|
|
2951
|
+
}));
|
|
2952
|
+
}
|
|
2953
|
+
function buildListedTarget(target, index, workerDiscoverySupported, workers) {
|
|
2954
|
+
return {
|
|
2955
|
+
index,
|
|
2956
|
+
...target,
|
|
2957
|
+
likelyWorker: looksLikeWorkerTarget(target),
|
|
2958
|
+
workerDiscoverySupported,
|
|
2959
|
+
workers: workers.map((worker, workerIndex) => ({
|
|
2960
|
+
index: workerIndex,
|
|
2961
|
+
workerId: worker.workerId,
|
|
2962
|
+
type: worker.type,
|
|
2963
|
+
title: worker.title,
|
|
2964
|
+
url: worker.url
|
|
2965
|
+
}))
|
|
2966
|
+
};
|
|
2967
|
+
}
|
|
2968
|
+
function looksLikeWorkerTarget(target) {
|
|
2969
|
+
return `${target.type} ${target.title} ${target.url}`.toLowerCase().includes("worker");
|
|
2970
|
+
}
|
|
2971
|
+
function writeTargetCountSummary(targetCount, workerCount) {
|
|
2972
|
+
process6.stderr.write(
|
|
2973
|
+
`[cf-inspector] ${targetCount.toString()} raw inspector ${targetCount === 1 ? "target" : "targets"}; ${workerCount.toString()} ${workerCount === 1 ? "worker" : "workers"}.
|
|
2974
|
+
`
|
|
2975
|
+
);
|
|
2976
|
+
}
|
|
2977
|
+
function warnOnMissingWorkers(targetCount, workerCount, targets) {
|
|
2978
|
+
if (targetCount !== 1 || workerCount !== 0) {
|
|
2979
|
+
return;
|
|
2980
|
+
}
|
|
2981
|
+
const supported = targets[0]?.workerDiscoverySupported === true;
|
|
2982
|
+
const supportHint = supported ? "NodeWorker discovery is available, but no live worker attached." : "This runtime did not expose NodeWorker discovery.";
|
|
2983
|
+
process6.stderr.write(
|
|
2984
|
+
`[cf-inspector] warning: only the main inspector target is reachable. ${supportHint} If worker code is expected, ensure the worker is alive and rerun list-targets. A worker on a separate inspector port is not carried by a single Cloud Foundry tunnel.
|
|
2985
|
+
`
|
|
2986
|
+
);
|
|
2987
|
+
}
|
|
2988
|
+
function writeHumanTargets(targets) {
|
|
2989
|
+
for (const target of targets) {
|
|
2990
|
+
const workerLabel = target.likelyWorker ? " likely-worker" : "";
|
|
2991
|
+
process6.stdout.write(
|
|
2992
|
+
`${target.index.toString()} target ${target.type} ${target.title} ${target.url}${workerLabel}
|
|
2993
|
+
`
|
|
2994
|
+
);
|
|
2995
|
+
for (const worker of target.workers) {
|
|
2996
|
+
process6.stdout.write(
|
|
2997
|
+
` ${worker.index.toString()} worker ${worker.type} ${worker.title} ${worker.url}
|
|
2998
|
+
`
|
|
2999
|
+
);
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
}
|
|
2314
3003
|
function compileScriptUrlFilter(pattern) {
|
|
2315
3004
|
if (pattern === void 0 || pattern.length === 0) {
|
|
2316
3005
|
return void 0;
|
|
@@ -2381,7 +3070,7 @@ function matchesFilterTokens(value, tokens) {
|
|
|
2381
3070
|
}
|
|
2382
3071
|
|
|
2383
3072
|
// src/cli/commands/log.ts
|
|
2384
|
-
import
|
|
3073
|
+
import process8 from "process";
|
|
2385
3074
|
|
|
2386
3075
|
// src/logpoint/stream.ts
|
|
2387
3076
|
init_types();
|
|
@@ -2467,7 +3156,7 @@ function readArg(arg, index) {
|
|
|
2467
3156
|
}
|
|
2468
3157
|
return index === 0 ? void 0 : "";
|
|
2469
3158
|
}
|
|
2470
|
-
function parseLogEvent(rawArgs, sentinel, location, timestamp) {
|
|
3159
|
+
function parseLogEvent(rawArgs, sentinel, location, timestamp, maxValueLength = DEFAULT_STREAM_MAX_VALUE_LENGTH) {
|
|
2471
3160
|
if (!Array.isArray(rawArgs) || rawArgs.length < 2) {
|
|
2472
3161
|
return void 0;
|
|
2473
3162
|
}
|
|
@@ -2479,21 +3168,37 @@ function parseLogEvent(rawArgs, sentinel, location, timestamp) {
|
|
|
2479
3168
|
const ts = new Date(typeof timestamp === "number" ? timestamp : Date.now()).toISOString();
|
|
2480
3169
|
const at = `${location.file}:${location.line.toString()}`;
|
|
2481
3170
|
if (payload.startsWith("!err:")) {
|
|
2482
|
-
|
|
3171
|
+
const limited = limitValueLength(payload.slice("!err:".length), maxValueLength);
|
|
3172
|
+
return {
|
|
3173
|
+
ts,
|
|
3174
|
+
at,
|
|
3175
|
+
error: limited.text,
|
|
3176
|
+
...textTruncationFields(limited)
|
|
3177
|
+
};
|
|
2483
3178
|
}
|
|
2484
|
-
return parsePayload(ts, at, payload);
|
|
3179
|
+
return parsePayload(ts, at, payload, maxValueLength);
|
|
2485
3180
|
}
|
|
2486
|
-
function parsePayload(ts, at, payload) {
|
|
3181
|
+
function parsePayload(ts, at, payload, maxValueLength) {
|
|
2487
3182
|
try {
|
|
2488
3183
|
const parsed = JSON.parse(payload);
|
|
2489
3184
|
if (typeof parsed === "string") {
|
|
2490
|
-
return
|
|
3185
|
+
return buildValueEvent(ts, at, parsed, maxValueLength);
|
|
2491
3186
|
}
|
|
2492
|
-
return
|
|
3187
|
+
return buildValueEvent(ts, at, JSON.stringify(parsed), maxValueLength);
|
|
2493
3188
|
} catch {
|
|
2494
|
-
return
|
|
3189
|
+
return buildValueEvent(ts, at, payload, maxValueLength, true);
|
|
2495
3190
|
}
|
|
2496
3191
|
}
|
|
3192
|
+
function buildValueEvent(ts, at, value, maxValueLength, includeRaw = false) {
|
|
3193
|
+
const limited = limitValueLength(value, maxValueLength);
|
|
3194
|
+
return {
|
|
3195
|
+
ts,
|
|
3196
|
+
at,
|
|
3197
|
+
value: limited.text,
|
|
3198
|
+
...includeRaw ? { raw: limited.text } : {},
|
|
3199
|
+
...textTruncationFields(limited)
|
|
3200
|
+
};
|
|
3201
|
+
}
|
|
2497
3202
|
|
|
2498
3203
|
// src/logpoint/stream.ts
|
|
2499
3204
|
function validateMaxEvents(maxEvents) {
|
|
@@ -2523,6 +3228,9 @@ function validateHitCount2(hitCount) {
|
|
|
2523
3228
|
async function streamLogpoint(session, options) {
|
|
2524
3229
|
const maxEvents = validateMaxEvents(options.maxEvents);
|
|
2525
3230
|
const hitCount = validateHitCount2(options.hitCount);
|
|
3231
|
+
const maxValueLength = resolveMaxValueLength(
|
|
3232
|
+
options.maxValueLength ?? DEFAULT_STREAM_MAX_VALUE_LENGTH
|
|
3233
|
+
);
|
|
2526
3234
|
const sentinel = generateSentinel();
|
|
2527
3235
|
const condition = buildLogpointCondition(sentinel, options.expression, {
|
|
2528
3236
|
...options.condition === void 0 ? {} : { predicate: options.condition },
|
|
@@ -2535,7 +3243,7 @@ async function streamLogpoint(session, options) {
|
|
|
2535
3243
|
if (maxEventsReached) {
|
|
2536
3244
|
return;
|
|
2537
3245
|
}
|
|
2538
|
-
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
3246
|
+
const event = toLogpointEvent(raw, sentinel, options.location, maxValueLength);
|
|
2539
3247
|
if (event === void 0) {
|
|
2540
3248
|
return;
|
|
2541
3249
|
}
|
|
@@ -2575,13 +3283,13 @@ async function streamLogpoint(session, options) {
|
|
|
2575
3283
|
await removeBreakpointBestEffort(session, handle.breakpointId);
|
|
2576
3284
|
}
|
|
2577
3285
|
}
|
|
2578
|
-
function toLogpointEvent(raw, sentinel, location) {
|
|
3286
|
+
function toLogpointEvent(raw, sentinel, location, maxValueLength) {
|
|
2579
3287
|
const params = raw;
|
|
2580
3288
|
if (asString3(params.type) !== "log") {
|
|
2581
3289
|
return void 0;
|
|
2582
3290
|
}
|
|
2583
3291
|
const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
|
|
2584
|
-
return parseLogEvent(params.args, sentinel, location, ts);
|
|
3292
|
+
return parseLogEvent(params.args, sentinel, location, ts, maxValueLength);
|
|
2585
3293
|
}
|
|
2586
3294
|
async function removeBreakpointBestEffort(session, breakpointId) {
|
|
2587
3295
|
try {
|
|
@@ -2630,19 +3338,19 @@ async function waitForStop(session, options, registerMaxEventsSignal) {
|
|
|
2630
3338
|
init_types();
|
|
2631
3339
|
|
|
2632
3340
|
// src/cli/signals.ts
|
|
2633
|
-
import
|
|
3341
|
+
import process7 from "process";
|
|
2634
3342
|
async function withTerminationSignal(fn) {
|
|
2635
3343
|
const abort = new AbortController();
|
|
2636
3344
|
const onSignal = () => {
|
|
2637
3345
|
abort.abort();
|
|
2638
3346
|
};
|
|
2639
|
-
|
|
2640
|
-
|
|
3347
|
+
process7.once("SIGINT", onSignal);
|
|
3348
|
+
process7.once("SIGTERM", onSignal);
|
|
2641
3349
|
try {
|
|
2642
3350
|
return await fn(abort.signal);
|
|
2643
3351
|
} finally {
|
|
2644
|
-
|
|
2645
|
-
|
|
3352
|
+
process7.off("SIGINT", onSignal);
|
|
3353
|
+
process7.off("SIGTERM", onSignal);
|
|
2646
3354
|
}
|
|
2647
3355
|
}
|
|
2648
3356
|
|
|
@@ -2654,11 +3362,16 @@ async function handleLog(opts) {
|
|
|
2654
3362
|
const durationSec = parsePositiveInt(opts.duration, "--duration");
|
|
2655
3363
|
const maxEvents = parsePositiveInt(opts.maxEvents, "--max-events");
|
|
2656
3364
|
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
3365
|
+
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length") ?? DEFAULT_STREAM_MAX_VALUE_LENGTH;
|
|
2657
3366
|
const expression = opts.expr.trim();
|
|
2658
3367
|
if (expression.length === 0) {
|
|
2659
3368
|
throw new CfInspectorError("INVALID_EXPRESSION", "--expr must not be empty");
|
|
2660
3369
|
}
|
|
2661
3370
|
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
3371
|
+
warnOnMutationRisk(expression, "log --expr");
|
|
3372
|
+
if (condition !== void 0) {
|
|
3373
|
+
warnOnMutationRisk(condition, "log --condition");
|
|
3374
|
+
}
|
|
2662
3375
|
await withTerminationSignal(async (signal) => {
|
|
2663
3376
|
await withSession(target, async (session) => {
|
|
2664
3377
|
await validateExpression(session, expression);
|
|
@@ -2673,6 +3386,7 @@ async function handleLog(opts) {
|
|
|
2673
3386
|
...maxEvents === void 0 ? {} : { maxEvents },
|
|
2674
3387
|
...hitCount === void 0 ? {} : { hitCount },
|
|
2675
3388
|
...condition === void 0 ? {} : { condition },
|
|
3389
|
+
maxValueLength,
|
|
2676
3390
|
signal,
|
|
2677
3391
|
onEvent: (event) => {
|
|
2678
3392
|
writeLogEvent(event, opts.json);
|
|
@@ -2681,29 +3395,39 @@ async function handleLog(opts) {
|
|
|
2681
3395
|
warnOnUnboundBreakpoints([handle]);
|
|
2682
3396
|
}
|
|
2683
3397
|
});
|
|
3398
|
+
if (result.emitted === 0 && (result.stoppedReason === "duration" || result.stoppedReason === "signal")) {
|
|
3399
|
+
warnOnBoundBreakpointWithoutHit([result.handle]);
|
|
3400
|
+
}
|
|
2684
3401
|
writeLogSummary(result.stoppedReason, result.emitted, opts.json);
|
|
2685
3402
|
});
|
|
2686
3403
|
});
|
|
2687
3404
|
}
|
|
2688
3405
|
function writeLogSummary(stoppedReason, emitted, json) {
|
|
2689
3406
|
if (json) {
|
|
2690
|
-
|
|
3407
|
+
process8.stderr.write(`${JSON.stringify({ stopped: stoppedReason, emitted })}
|
|
2691
3408
|
`);
|
|
2692
3409
|
return;
|
|
2693
3410
|
}
|
|
2694
|
-
|
|
3411
|
+
process8.stderr.write(
|
|
2695
3412
|
`Stopped (${stoppedReason}); emitted ${emitted.toString()} log ${emitted === 1 ? "entry" : "entries"}.
|
|
2696
3413
|
`
|
|
2697
3414
|
);
|
|
2698
3415
|
}
|
|
2699
3416
|
|
|
2700
3417
|
// src/cli/commands/snapshot.ts
|
|
2701
|
-
import { performance as
|
|
2702
|
-
import
|
|
3418
|
+
import { performance as performance5 } from "perf_hooks";
|
|
3419
|
+
import process9 from "process";
|
|
2703
3420
|
init_types();
|
|
2704
3421
|
async function handleSnapshot(opts) {
|
|
2705
3422
|
const target = await resolveTargetWithCurrentCfTarget(opts, { useTimeoutForTunnel: false });
|
|
2706
3423
|
const prepared = prepareSnapshotCommand(opts, target);
|
|
3424
|
+
warnOnCaptureMutationRisk(
|
|
3425
|
+
[...prepared.captures, ...prepared.stackCaptures],
|
|
3426
|
+
opts.allowMutation === true
|
|
3427
|
+
);
|
|
3428
|
+
for (const expression of prepared.setupEvals) {
|
|
3429
|
+
warnOnMutationRisk(expression, "snapshot --setup-eval");
|
|
3430
|
+
}
|
|
2707
3431
|
const reportProgress = opts.quiet === true ? void 0 : writeProgress;
|
|
2708
3432
|
const result = await runSnapshotCommand(prepared, opts, reportProgress);
|
|
2709
3433
|
if (opts.json) {
|
|
@@ -2721,11 +3445,16 @@ function prepareSnapshotCommand(opts, target) {
|
|
|
2721
3445
|
);
|
|
2722
3446
|
}
|
|
2723
3447
|
const timeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_BREAKPOINT_TIMEOUT_SEC;
|
|
2724
|
-
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
3448
|
+
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length") ?? DEFAULT_MAX_VALUE_LENGTH;
|
|
2725
3449
|
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2726
3450
|
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
2727
3451
|
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
2728
3452
|
const setupEvals = parseSetupEvals(opts.setupEval);
|
|
3453
|
+
enforceNativeConditionMutationPolicy(
|
|
3454
|
+
condition ?? "",
|
|
3455
|
+
opts.allowMutation === true,
|
|
3456
|
+
"snapshot --condition"
|
|
3457
|
+
);
|
|
2729
3458
|
return {
|
|
2730
3459
|
target,
|
|
2731
3460
|
setupEvals,
|
|
@@ -2734,62 +3463,67 @@ function prepareSnapshotCommand(opts, target) {
|
|
|
2734
3463
|
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
2735
3464
|
timeoutMs: timeoutSec * 1e3,
|
|
2736
3465
|
...condition === void 0 ? {} : { condition },
|
|
2737
|
-
|
|
3466
|
+
maxValueLength,
|
|
2738
3467
|
...hitCount === void 0 ? {} : { hitCount },
|
|
2739
3468
|
...stackDepth === void 0 ? {} : { stackDepth },
|
|
2740
|
-
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
3469
|
+
stackCaptures: parseCaptureList(opts.stackCaptures),
|
|
3470
|
+
throwOnSideEffect: opts.allowMutation !== true
|
|
2741
3471
|
};
|
|
2742
3472
|
}
|
|
2743
3473
|
async function runSnapshotCommand(command, opts, reportProgress) {
|
|
2744
3474
|
return await withSession(command.target, async (session) => {
|
|
2745
|
-
|
|
2746
|
-
const setupCount = command.setupEvals.length;
|
|
2747
|
-
reportProgress?.(`Running ${setupCount.toString()} setup ${setupCount === 1 ? "evaluation" : "evaluations"}...`);
|
|
2748
|
-
await runSetupEvals(session, command.setupEvals);
|
|
2749
|
-
reportProgress?.("Setup evaluation complete.");
|
|
2750
|
-
}
|
|
2751
|
-
if (command.condition !== void 0) {
|
|
2752
|
-
reportProgress?.("Validating the breakpoint condition...");
|
|
2753
|
-
await validateExpression(session, command.condition);
|
|
2754
|
-
reportProgress?.("Breakpoint condition is valid.");
|
|
2755
|
-
}
|
|
2756
|
-
const breakpointCount = command.breakpoints.length;
|
|
2757
|
-
reportProgress?.(
|
|
2758
|
-
`Setting ${breakpointCount.toString()} ${breakpointCount === 1 ? "breakpoint" : "breakpoints"}...`
|
|
2759
|
-
);
|
|
2760
|
-
const handles = await setCommandBreakpoints(session, command);
|
|
2761
|
-
const resolvedCount = handles.reduce(
|
|
2762
|
-
(total, handle) => total + handle.resolvedLocations.length,
|
|
2763
|
-
0
|
|
2764
|
-
);
|
|
2765
|
-
reportProgress?.(
|
|
2766
|
-
`Breakpoint setup complete: ${resolvedCount.toString()} resolved ${resolvedCount === 1 ? "location" : "locations"}.`
|
|
2767
|
-
);
|
|
2768
|
-
warnOnUnboundBreakpoints(handles);
|
|
2769
|
-
reportProgress?.(
|
|
2770
|
-
`Waiting up to ${(command.timeoutMs / 1e3).toString()}s for a breakpoint hit...`
|
|
2771
|
-
);
|
|
2772
|
-
const pause = await waitForCommandPause(session, opts, handles, command.timeoutMs);
|
|
2773
|
-
const captureCount = command.captures.length;
|
|
2774
|
-
reportProgress?.(
|
|
2775
|
-
`Breakpoint hit; capturing ${captureCount.toString()} ${captureCount === 1 ? "expression" : "expressions"}...`
|
|
2776
|
-
);
|
|
2777
|
-
const pausedStartedAt = pause.receivedAtMs ?? performance4.now();
|
|
2778
|
-
const snapshot = await captureSnapshot(session, pause, {
|
|
2779
|
-
captures: command.captures,
|
|
2780
|
-
includeScopes: opts.includeScopes === true,
|
|
2781
|
-
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength },
|
|
2782
|
-
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
2783
|
-
stackCaptures: command.stackCaptures
|
|
2784
|
-
});
|
|
2785
|
-
if (opts.keepPaused === true) {
|
|
2786
|
-
reportProgress?.("Snapshot captured; leaving the target paused as requested.");
|
|
2787
|
-
return withPausedDuration(snapshot, null);
|
|
2788
|
-
}
|
|
2789
|
-
reportProgress?.("Snapshot captured; resuming the target...");
|
|
2790
|
-
return await resumeAfterSnapshot(session, snapshot, pausedStartedAt, reportProgress);
|
|
3475
|
+
return await runSnapshotOnSession(session, command, opts, reportProgress);
|
|
2791
3476
|
}, reportProgress);
|
|
2792
3477
|
}
|
|
3478
|
+
async function runSnapshotOnSession(session, command, opts, reportProgress) {
|
|
3479
|
+
if (command.setupEvals.length > 0) {
|
|
3480
|
+
const setupCount = command.setupEvals.length;
|
|
3481
|
+
reportProgress?.(`Running ${setupCount.toString()} setup ${setupCount === 1 ? "evaluation" : "evaluations"}...`);
|
|
3482
|
+
await runSetupEvals(session, command.setupEvals);
|
|
3483
|
+
reportProgress?.("Setup evaluation complete.");
|
|
3484
|
+
}
|
|
3485
|
+
if (command.condition !== void 0) {
|
|
3486
|
+
reportProgress?.("Validating the breakpoint condition...");
|
|
3487
|
+
await validateExpression(session, command.condition);
|
|
3488
|
+
reportProgress?.("Breakpoint condition is valid.");
|
|
3489
|
+
}
|
|
3490
|
+
const breakpointCount = command.breakpoints.length;
|
|
3491
|
+
reportProgress?.(
|
|
3492
|
+
`Setting ${breakpointCount.toString()} ${breakpointCount === 1 ? "breakpoint" : "breakpoints"}...`
|
|
3493
|
+
);
|
|
3494
|
+
const handles = await setCommandBreakpoints(session, command);
|
|
3495
|
+
const resolvedCount = handles.reduce(
|
|
3496
|
+
(total, handle) => total + handle.resolvedLocations.length,
|
|
3497
|
+
0
|
|
3498
|
+
);
|
|
3499
|
+
reportProgress?.(
|
|
3500
|
+
`Breakpoint setup complete: ${resolvedCount.toString()} resolved ${resolvedCount === 1 ? "location" : "locations"}.`
|
|
3501
|
+
);
|
|
3502
|
+
warnOnUnboundBreakpoints(handles);
|
|
3503
|
+
reportProgress?.(
|
|
3504
|
+
`Waiting up to ${(command.timeoutMs / 1e3).toString()}s for a breakpoint hit...`
|
|
3505
|
+
);
|
|
3506
|
+
const pause = await waitForCommandPause(session, opts, handles, command.timeoutMs);
|
|
3507
|
+
const captureCount = command.captures.length;
|
|
3508
|
+
reportProgress?.(
|
|
3509
|
+
`Breakpoint hit; capturing ${captureCount.toString()} ${captureCount === 1 ? "expression" : "expressions"}...`
|
|
3510
|
+
);
|
|
3511
|
+
const pausedStartedAt = pause.receivedAtMs ?? performance5.now();
|
|
3512
|
+
const snapshot = await captureSnapshot(session, pause, {
|
|
3513
|
+
captures: command.captures,
|
|
3514
|
+
includeScopes: opts.includeScopes === true,
|
|
3515
|
+
maxValueLength: command.maxValueLength,
|
|
3516
|
+
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
3517
|
+
stackCaptures: command.stackCaptures,
|
|
3518
|
+
throwOnSideEffect: command.throwOnSideEffect
|
|
3519
|
+
});
|
|
3520
|
+
if (opts.keepPaused === true) {
|
|
3521
|
+
reportProgress?.("Snapshot captured; leaving the target paused as requested.");
|
|
3522
|
+
return withPausedDuration(snapshot, null);
|
|
3523
|
+
}
|
|
3524
|
+
reportProgress?.("Snapshot captured; resuming the target...");
|
|
3525
|
+
return await resumeAfterSnapshot(session, snapshot, pausedStartedAt, reportProgress);
|
|
3526
|
+
}
|
|
2793
3527
|
async function setCommandBreakpoints(session, command) {
|
|
2794
3528
|
return await Promise.all(
|
|
2795
3529
|
command.breakpoints.map(
|
|
@@ -2805,26 +3539,33 @@ async function setCommandBreakpoints(session, command) {
|
|
|
2805
3539
|
}
|
|
2806
3540
|
async function waitForCommandPause(session, opts, handles, timeoutMs) {
|
|
2807
3541
|
let warnedUnmatchedPause = false;
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
3542
|
+
try {
|
|
3543
|
+
return await waitForPause(session, {
|
|
3544
|
+
timeoutMs,
|
|
3545
|
+
breakpointIds: handles.map((h) => h.breakpointId),
|
|
3546
|
+
unmatchedPausePolicy: opts.failOnUnmatchedPause === true ? "fail" : "wait-for-resume",
|
|
3547
|
+
onUnmatchedPause: (unmatchedPause) => {
|
|
3548
|
+
if (warnedUnmatchedPause || opts.failOnUnmatchedPause === true) {
|
|
3549
|
+
return;
|
|
3550
|
+
}
|
|
3551
|
+
warnedUnmatchedPause = true;
|
|
3552
|
+
warnOnUnmatchedPause(unmatchedPause);
|
|
2815
3553
|
}
|
|
2816
|
-
|
|
2817
|
-
|
|
3554
|
+
});
|
|
3555
|
+
} catch (error) {
|
|
3556
|
+
if (error instanceof CfInspectorError && (error.code === "BREAKPOINT_NOT_HIT" || error.code === "UNRELATED_PAUSE_TIMEOUT")) {
|
|
3557
|
+
warnOnBoundBreakpointWithoutHit(handles);
|
|
2818
3558
|
}
|
|
2819
|
-
|
|
3559
|
+
throw error;
|
|
3560
|
+
}
|
|
2820
3561
|
}
|
|
2821
3562
|
async function resumeAfterSnapshot(session, snapshot, pausedStartedAt, reportProgress) {
|
|
2822
3563
|
try {
|
|
2823
3564
|
await resume(session);
|
|
2824
3565
|
reportProgress?.("Target resumed.");
|
|
2825
|
-
return withPausedDuration(snapshot, roundDurationMs(
|
|
3566
|
+
return withPausedDuration(snapshot, roundDurationMs(performance5.now() - pausedStartedAt));
|
|
2826
3567
|
} catch {
|
|
2827
|
-
|
|
3568
|
+
process9.stderr.write(
|
|
2828
3569
|
"[cf-inspector] warning: Debugger.resume failed after snapshot; pausedDurationMs is unknown.\n"
|
|
2829
3570
|
);
|
|
2830
3571
|
return withPausedDuration(snapshot, null);
|
|
@@ -2836,12 +3577,19 @@ function parseSetupEvals(raw) {
|
|
|
2836
3577
|
}
|
|
2837
3578
|
|
|
2838
3579
|
// src/cli/commands/watch.ts
|
|
2839
|
-
import { performance as
|
|
2840
|
-
import
|
|
3580
|
+
import { performance as performance6 } from "perf_hooks";
|
|
3581
|
+
import process10 from "process";
|
|
2841
3582
|
init_types();
|
|
2842
3583
|
async function handleWatch(opts) {
|
|
2843
3584
|
const target = await resolveTargetWithCurrentCfTarget(opts, { useTimeoutForTunnel: false });
|
|
2844
3585
|
const prepared = prepareWatchCommand(opts, target);
|
|
3586
|
+
warnOnCaptureMutationRisk(
|
|
3587
|
+
[...prepared.captures, ...prepared.stackCaptures],
|
|
3588
|
+
opts.allowMutation === true
|
|
3589
|
+
);
|
|
3590
|
+
for (const expression of prepared.setupEvals) {
|
|
3591
|
+
warnOnMutationRisk(expression, "watch --setup-eval");
|
|
3592
|
+
}
|
|
2845
3593
|
let stoppedReason = "signal";
|
|
2846
3594
|
let emitted = 0;
|
|
2847
3595
|
await withTerminationSignal(async (signal) => {
|
|
@@ -2863,11 +3611,16 @@ function prepareWatchCommand(opts, target) {
|
|
|
2863
3611
|
const perHitTimeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_BREAKPOINT_TIMEOUT_SEC;
|
|
2864
3612
|
const durationSec = parsePositiveInt(opts.duration, "--duration");
|
|
2865
3613
|
const maxEvents = parsePositiveInt(opts.maxEvents, "--max-events");
|
|
2866
|
-
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
3614
|
+
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length") ?? DEFAULT_STREAM_MAX_VALUE_LENGTH;
|
|
2867
3615
|
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
2868
3616
|
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
2869
3617
|
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2870
3618
|
const setupEvals = parseSetupEvals2(opts.setupEval);
|
|
3619
|
+
enforceNativeConditionMutationPolicy(
|
|
3620
|
+
condition ?? "",
|
|
3621
|
+
opts.allowMutation === true,
|
|
3622
|
+
"watch --condition"
|
|
3623
|
+
);
|
|
2871
3624
|
return {
|
|
2872
3625
|
target,
|
|
2873
3626
|
setupEvals,
|
|
@@ -2877,11 +3630,12 @@ function prepareWatchCommand(opts, target) {
|
|
|
2877
3630
|
perHitTimeoutMs: perHitTimeoutSec * 1e3,
|
|
2878
3631
|
...durationSec === void 0 ? {} : { durationMs: durationSec * 1e3 },
|
|
2879
3632
|
...maxEvents === void 0 ? {} : { maxEvents },
|
|
2880
|
-
|
|
3633
|
+
maxValueLength,
|
|
2881
3634
|
...condition === void 0 ? {} : { condition },
|
|
2882
3635
|
...hitCount === void 0 ? {} : { hitCount },
|
|
2883
3636
|
...stackDepth === void 0 ? {} : { stackDepth },
|
|
2884
|
-
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
3637
|
+
stackCaptures: parseCaptureList(opts.stackCaptures),
|
|
3638
|
+
throwOnSideEffect: opts.allowMutation !== true
|
|
2885
3639
|
};
|
|
2886
3640
|
}
|
|
2887
3641
|
async function runWatchLoop(session, command, opts, signal) {
|
|
@@ -2935,7 +3689,7 @@ async function runWatchLoop(session, command, opts, signal) {
|
|
|
2935
3689
|
break;
|
|
2936
3690
|
}
|
|
2937
3691
|
if (pause === "timeout") {
|
|
2938
|
-
if (deadline !== void 0 &&
|
|
3692
|
+
if (deadline !== void 0 && performance6.now() >= deadline) {
|
|
2939
3693
|
setStop("duration");
|
|
2940
3694
|
break;
|
|
2941
3695
|
}
|
|
@@ -2947,7 +3701,7 @@ async function runWatchLoop(session, command, opts, signal) {
|
|
|
2947
3701
|
try {
|
|
2948
3702
|
await resume(session);
|
|
2949
3703
|
} catch {
|
|
2950
|
-
|
|
3704
|
+
process10.stderr.write("[cf-inspector] warning: Debugger.resume failed during watch.\n");
|
|
2951
3705
|
setStop("transport-closed");
|
|
2952
3706
|
break;
|
|
2953
3707
|
}
|
|
@@ -2959,19 +3713,22 @@ async function runWatchLoop(session, command, opts, signal) {
|
|
|
2959
3713
|
} finally {
|
|
2960
3714
|
transportClosed.cancel();
|
|
2961
3715
|
}
|
|
3716
|
+
if (emitted === 0 && (state.reason === "duration" || state.reason === "signal")) {
|
|
3717
|
+
warnOnBoundBreakpointWithoutHit(handles);
|
|
3718
|
+
}
|
|
2962
3719
|
return { emitted, stoppedReason: state.reason };
|
|
2963
3720
|
}
|
|
2964
3721
|
function computeDeadline(durationMs) {
|
|
2965
3722
|
if (durationMs === void 0) {
|
|
2966
3723
|
return void 0;
|
|
2967
3724
|
}
|
|
2968
|
-
return
|
|
3725
|
+
return performance6.now() + durationMs;
|
|
2969
3726
|
}
|
|
2970
3727
|
function remainingForLoop(deadline, perHitTimeoutMs) {
|
|
2971
3728
|
if (deadline === void 0) {
|
|
2972
3729
|
return perHitTimeoutMs;
|
|
2973
3730
|
}
|
|
2974
|
-
const remaining = deadline -
|
|
3731
|
+
const remaining = deadline - performance6.now();
|
|
2975
3732
|
if (remaining <= 0) {
|
|
2976
3733
|
return 0;
|
|
2977
3734
|
}
|
|
@@ -3023,9 +3780,10 @@ async function captureWatchEvent(session, command, pause, hit, opts) {
|
|
|
3023
3780
|
const snapshot = await captureSnapshot(session, pause, {
|
|
3024
3781
|
captures: command.captures,
|
|
3025
3782
|
includeScopes: opts.includeScopes === true,
|
|
3026
|
-
|
|
3783
|
+
maxValueLength: command.maxValueLength,
|
|
3027
3784
|
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
3028
|
-
stackCaptures: command.stackCaptures
|
|
3785
|
+
stackCaptures: command.stackCaptures,
|
|
3786
|
+
throwOnSideEffect: command.throwOnSideEffect
|
|
3029
3787
|
});
|
|
3030
3788
|
const at = formatLocation(command, snapshot.topFrame);
|
|
3031
3789
|
const base = {
|
|
@@ -3052,11 +3810,11 @@ function formatLocation(command, topFrame) {
|
|
|
3052
3810
|
}
|
|
3053
3811
|
function writeWatchSummary(reason, emitted, json) {
|
|
3054
3812
|
if (json) {
|
|
3055
|
-
|
|
3813
|
+
process10.stderr.write(`${JSON.stringify({ stopped: reason, emitted })}
|
|
3056
3814
|
`);
|
|
3057
3815
|
return;
|
|
3058
3816
|
}
|
|
3059
|
-
|
|
3817
|
+
process10.stderr.write(
|
|
3060
3818
|
`Stopped (${reason}); emitted ${emitted.toString()} watch ${emitted === 1 ? "event" : "events"}.
|
|
3061
3819
|
`
|
|
3062
3820
|
);
|
|
@@ -3068,16 +3826,42 @@ function parseSetupEvals2(raw) {
|
|
|
3068
3826
|
|
|
3069
3827
|
// src/cli/program.ts
|
|
3070
3828
|
function applyTargetOptions(cmd, options = {}) {
|
|
3071
|
-
const
|
|
3072
|
-
|
|
3829
|
+
const withEndpointOptions = cmd.option("--port <number>", "Local port the inspector or tunnel listens on").option("--host <host>", "Hostname (default: 127.0.0.1)", "127.0.0.1").option("--region <key>", "CF region key (required with --app)").option("--api-endpoint <url>", "CF API endpoint override for --region").option("--org <name>", "CF org name (required with --app)").option("--space <name>", "CF space name (required with --app)").option(
|
|
3830
|
+
"--app <name>",
|
|
3831
|
+
"CF app name; requires explicit --region/--org/--space (ambient cf target is ignored)"
|
|
3832
|
+
);
|
|
3833
|
+
const withTargetOption = options.includeTarget === false ? withEndpointOptions : withEndpointOptions.option(
|
|
3834
|
+
"--target <index>",
|
|
3835
|
+
"Inspector target index from /json/list (default: 0)"
|
|
3836
|
+
);
|
|
3837
|
+
const withWorkerOption = options.includeWorker === false ? withTargetOption : withTargetOption.option("--worker <index>", "NodeWorker sub-session index listed by list-targets");
|
|
3838
|
+
return options.includeTimeout === false ? withWorkerOption : withWorkerOption.option("--timeout <seconds>", "Timeout for CF tunnel readiness in seconds (default: 180)");
|
|
3073
3839
|
}
|
|
3074
3840
|
var collectStrings = (value, prev = []) => [
|
|
3075
3841
|
...prev,
|
|
3076
3842
|
value
|
|
3077
3843
|
];
|
|
3844
|
+
function readPackageVersion() {
|
|
3845
|
+
let current = dirname(fileURLToPath(import.meta.url));
|
|
3846
|
+
for (let depth = 0; depth < 4; depth += 1) {
|
|
3847
|
+
const candidate = join(current, "package.json");
|
|
3848
|
+
try {
|
|
3849
|
+
const parsed = JSON.parse(readFileSync(candidate, "utf8"));
|
|
3850
|
+
if (typeof parsed === "object" && parsed !== null) {
|
|
3851
|
+
const record = parsed;
|
|
3852
|
+
if (record["name"] === "@saptools/cf-inspector" && typeof record["version"] === "string") {
|
|
3853
|
+
return record["version"];
|
|
3854
|
+
}
|
|
3855
|
+
}
|
|
3856
|
+
} catch {
|
|
3857
|
+
}
|
|
3858
|
+
current = dirname(current);
|
|
3859
|
+
}
|
|
3860
|
+
throw new Error("Unable to read @saptools/cf-inspector package version");
|
|
3861
|
+
}
|
|
3078
3862
|
async function main(argv) {
|
|
3079
3863
|
const program = new Command();
|
|
3080
|
-
program.name("cf-inspector").description("Drive a Node.js inspector from the command line \u2014 set breakpoints, capture snapshots, evaluate expressions");
|
|
3864
|
+
program.name("cf-inspector").version(readPackageVersion()).description("Drive a Node.js inspector from the command line \u2014 set breakpoints, capture snapshots, evaluate expressions");
|
|
3081
3865
|
registerSnapshot(program);
|
|
3082
3866
|
registerLog(program);
|
|
3083
3867
|
registerWatch(program);
|
|
@@ -3092,14 +3876,14 @@ function registerSnapshot(program) {
|
|
|
3092
3876
|
applyTargetOptions(
|
|
3093
3877
|
program.command("snapshot").description("Set a breakpoint, wait for it to hit, capture expressions, and resume"),
|
|
3094
3878
|
{ includeTimeout: false }
|
|
3095
|
-
).option("--bp <file:line>", "Breakpoint location (repeatable; first hit wins), e.g. src/handler.ts:42", collectStrings, []).option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate in the paused frame").option("--setup-eval <expr>", "Evaluate a global setup expression before breakpoint setup (repeatable)", collectStrings, []).option("--timeout <seconds>", "How long to wait for the breakpoint to hit (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default:
|
|
3879
|
+
).option("--bp <file:line>", "Breakpoint location (repeatable; first hit wins), e.g. src/handler.ts:42", collectStrings, []).option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate in the paused frame").option("--setup-eval <expr>", "Evaluate a global setup expression before breakpoint setup (repeatable)", collectStrings, []).option("--timeout <seconds>", "How long to wait for the breakpoint to hit (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default: 131072)").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--condition <expr>", "Only pause when this JS expression evaluates truthy in the paused frame").option("--hit-count <n>", "Only pause after the breakpoint has been hit N or more times").option("--stack-depth <n>", "Walk this many call frames when capturing (default: 1, only top frame)").option("--stack-captures <expr,\u2026>", "Expressions to evaluate on each call frame in the stack").option("--allow-mutation", "Allow mutation-capable captures and native breakpoint conditions to run").option("--include-scopes", "Include expanded paused-frame scopes in the snapshot").option("--no-json", "Print a human-readable summary instead of JSON").option("--quiet", "Suppress progress messages on stderr").option("--keep-paused", "Skip Debugger.resume after capture; Node may resume when this CLI disconnects").option("--fail-on-unmatched-pause", "Fail immediately if the target pauses somewhere else").action(async (opts) => {
|
|
3096
3880
|
await handleSnapshot(opts);
|
|
3097
3881
|
});
|
|
3098
3882
|
}
|
|
3099
3883
|
function registerLog(program) {
|
|
3100
3884
|
applyTargetOptions(
|
|
3101
3885
|
program.command("log").description("Stream a non-pausing logpoint: log an expression each time a line executes")
|
|
3102
|
-
).requiredOption("--at <file:line>", "Logpoint location, e.g. src/handler.ts:42").requiredOption("--expr <expression>", "JavaScript expression to log on each hit").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--duration <seconds>", "Stop streaming after N seconds (default: run until SIGINT)").option("--max-events <n>", "Stop streaming after emitting N log events").option("--hit-count <n>", "Start logging once the line has been hit N or more times").option("--condition <expr>", "Only log when this JS expression evaluates truthy on the inspectee").option("--no-json", "Print human-readable lines instead of JSON Lines").action(async (opts) => {
|
|
3886
|
+
).requiredOption("--at <file:line>", "Logpoint location, e.g. src/handler.ts:42").requiredOption("--expr <expression>", "JavaScript expression to log on each hit").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--duration <seconds>", "Stop streaming after N seconds (default: run until SIGINT)").option("--max-events <n>", "Stop streaming after emitting N log events").option("--hit-count <n>", "Start logging once the line has been hit N or more times").option("--condition <expr>", "Only log when this JS expression evaluates truthy on the inspectee").option("--max-value-length <chars>", "Maximum characters per log value before truncation (default: 4096)").option("--no-json", "Print human-readable lines instead of JSON Lines").action(async (opts) => {
|
|
3103
3887
|
await handleLog(opts);
|
|
3104
3888
|
});
|
|
3105
3889
|
}
|
|
@@ -3107,7 +3891,7 @@ function registerWatch(program) {
|
|
|
3107
3891
|
applyTargetOptions(
|
|
3108
3892
|
program.command("watch").description("Stream a snapshot per breakpoint hit (multi-shot watch); resume between hits"),
|
|
3109
3893
|
{ includeTimeout: false }
|
|
3110
|
-
).option("--bp <file:line>", "Breakpoint location (repeatable), e.g. src/handler.ts:42", collectStrings, []).option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate per hit").option("--setup-eval <expr>", "Evaluate a global setup expression before breakpoint setup (repeatable)", collectStrings, []).option("--condition <expr>", "Only emit hits where this JS expression evaluates truthy").option("--hit-count <n>", "Start emitting after the line has been hit N or more times").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--duration <seconds>", "Stop streaming after N seconds (default: run until SIGINT)").option("--max-events <n>", "Stop streaming after emitting N watch events").option("--timeout <seconds>", "How long to wait for the next hit before giving up (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default: 4096)").option("--stack-depth <n>", "Walk this many call frames per hit (default: 1)").option("--stack-captures <expr,\u2026>", "Expressions to evaluate on each call frame").option("--include-scopes", "Include expanded paused-frame scopes per hit").option("--no-json", "Print human-readable lines instead of JSON Lines").action(async (opts) => {
|
|
3894
|
+
).option("--bp <file:line>", "Breakpoint location (repeatable), e.g. src/handler.ts:42", collectStrings, []).option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate per hit").option("--setup-eval <expr>", "Evaluate a global setup expression before breakpoint setup (repeatable)", collectStrings, []).option("--condition <expr>", "Only emit hits where this JS expression evaluates truthy").option("--hit-count <n>", "Start emitting after the line has been hit N or more times").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--duration <seconds>", "Stop streaming after N seconds (default: run until SIGINT)").option("--max-events <n>", "Stop streaming after emitting N watch events").option("--timeout <seconds>", "How long to wait for the next hit before giving up (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default: 4096)").option("--stack-depth <n>", "Walk this many call frames per hit (default: 1)").option("--stack-captures <expr,\u2026>", "Expressions to evaluate on each call frame").option("--allow-mutation", "Allow mutation-capable captures and native breakpoint conditions to run").option("--include-scopes", "Include expanded paused-frame scopes per hit").option("--no-json", "Print human-readable lines instead of JSON Lines").action(async (opts) => {
|
|
3111
3895
|
await handleWatch(opts);
|
|
3112
3896
|
});
|
|
3113
3897
|
}
|
|
@@ -3115,7 +3899,7 @@ function registerException(program) {
|
|
|
3115
3899
|
applyTargetOptions(
|
|
3116
3900
|
program.command("exception").description("Pause on a thrown exception, capture the value and frame, then resume"),
|
|
3117
3901
|
{ includeTimeout: false }
|
|
3118
|
-
).option("--type <state>", "Pause type: uncaught (default), caught, or all").option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate in the paused frame").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--timeout <seconds>", "How long to wait for an exception (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default:
|
|
3902
|
+
).option("--type <state>", "Pause type: uncaught (default), caught, or all").option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate in the paused frame").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--timeout <seconds>", "How long to wait for an exception (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default: 131072)").option("--stack-depth <n>", "Walk this many call frames when capturing (default: 1)").option("--stack-captures <expr,\u2026>", "Expressions to evaluate on each call frame in the stack").option("--allow-mutation", "Allow mutation-capable capture expressions to run").option("--include-scopes", "Include expanded paused-frame scopes in the snapshot").option("--keep-paused", "Skip Debugger.resume after capture; Node may resume when this CLI disconnects").option("--no-json", "Print a human-readable summary instead of JSON").action(async (opts) => {
|
|
3119
3903
|
await handleException(opts);
|
|
3120
3904
|
});
|
|
3121
3905
|
}
|
|
@@ -3135,14 +3919,21 @@ function registerListScripts(program) {
|
|
|
3135
3919
|
}
|
|
3136
3920
|
function registerListTargets(program) {
|
|
3137
3921
|
applyTargetOptions(
|
|
3138
|
-
program.command("list-targets").description(
|
|
3139
|
-
|
|
3922
|
+
program.command("list-targets").description(
|
|
3923
|
+
"List raw /json/list targets and nested workers; use --target or --worker on other commands"
|
|
3924
|
+
),
|
|
3925
|
+
{ includeTarget: false, includeWorker: false }
|
|
3926
|
+
).option(
|
|
3927
|
+
"--no-json",
|
|
3928
|
+
"Print tab-separated target/worker rows: index, kind, type, title, and URL"
|
|
3929
|
+
).action(async (opts) => {
|
|
3140
3930
|
await handleListTargets(opts);
|
|
3141
3931
|
});
|
|
3142
3932
|
}
|
|
3143
3933
|
function registerAttach(program) {
|
|
3144
3934
|
applyTargetOptions(
|
|
3145
|
-
program.command("attach").description("Connect, fetch the inspector version, and disconnect (smoke-test)")
|
|
3935
|
+
program.command("attach").description("Connect, fetch the inspector version, and disconnect (smoke-test)"),
|
|
3936
|
+
{ includeTarget: false, includeWorker: false }
|
|
3146
3937
|
).option("--no-json", "Print a multi-line summary instead of JSON").action(async (opts) => {
|
|
3147
3938
|
await handleAttach(opts);
|
|
3148
3939
|
});
|
|
@@ -3151,20 +3942,20 @@ function registerAttach(program) {
|
|
|
3151
3942
|
// src/cli.ts
|
|
3152
3943
|
init_types();
|
|
3153
3944
|
try {
|
|
3154
|
-
await main(
|
|
3945
|
+
await main(process11.argv);
|
|
3155
3946
|
} catch (err) {
|
|
3156
3947
|
if (err instanceof CfInspectorError) {
|
|
3157
|
-
|
|
3948
|
+
process11.stderr.write(`Error [${err.code}]: ${err.message}
|
|
3158
3949
|
`);
|
|
3159
3950
|
if (err.detail !== void 0) {
|
|
3160
|
-
|
|
3951
|
+
process11.stderr.write(` detail: ${err.detail}
|
|
3161
3952
|
`);
|
|
3162
3953
|
}
|
|
3163
|
-
|
|
3954
|
+
process11.exit(1);
|
|
3164
3955
|
}
|
|
3165
3956
|
const message = err instanceof Error ? err.message : String(err);
|
|
3166
|
-
|
|
3957
|
+
process11.stderr.write(`Error: ${message}
|
|
3167
3958
|
`);
|
|
3168
|
-
|
|
3959
|
+
process11.exit(1);
|
|
3169
3960
|
}
|
|
3170
3961
|
//# sourceMappingURL=cli.js.map
|