@saptools/cf-inspector 0.3.19 → 0.4.1
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 +105 -23
- package/dist/cli.js +1515 -735
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +56 -5
- package/dist/index.js +446 -53
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/cli.d.ts +0 -2
package/dist/cli.js
CHANGED
|
@@ -35,10 +35,19 @@ __export(wsTransport_exports, {
|
|
|
35
35
|
wsTransportFactory: () => wsTransportFactory
|
|
36
36
|
});
|
|
37
37
|
import { WebSocket } from "ws";
|
|
38
|
-
async function wsTransportFactory(url) {
|
|
38
|
+
async function wsTransportFactory(url, options = {}) {
|
|
39
39
|
const socket = new WebSocket(url, { perMessageDeflate: false });
|
|
40
|
-
await waitForOpen(socket, url);
|
|
41
|
-
const wrappers = /* @__PURE__ */ new
|
|
40
|
+
await waitForOpen(socket, url, options.connectTimeoutMs);
|
|
41
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
42
|
+
function wrappersFor(event) {
|
|
43
|
+
const existing = wrappers.get(event);
|
|
44
|
+
if (existing !== void 0) {
|
|
45
|
+
return existing;
|
|
46
|
+
}
|
|
47
|
+
const created = /* @__PURE__ */ new WeakMap();
|
|
48
|
+
wrappers.set(event, created);
|
|
49
|
+
return created;
|
|
50
|
+
}
|
|
42
51
|
return {
|
|
43
52
|
send(payload) {
|
|
44
53
|
socket.send(payload);
|
|
@@ -50,11 +59,12 @@ async function wsTransportFactory(url) {
|
|
|
50
59
|
return socket.readyState;
|
|
51
60
|
},
|
|
52
61
|
on(event, listener) {
|
|
53
|
-
const wrapped = wrapListener(event, listener
|
|
62
|
+
const wrapped = wrapListener(event, listener);
|
|
63
|
+
wrappersFor(event).set(listener, wrapped);
|
|
54
64
|
socket.on(event, wrapped);
|
|
55
65
|
},
|
|
56
66
|
off(event, listener) {
|
|
57
|
-
const wrapped =
|
|
67
|
+
const wrapped = wrappersFor(event).get(listener);
|
|
58
68
|
if (!wrapped) {
|
|
59
69
|
return;
|
|
60
70
|
}
|
|
@@ -62,14 +72,30 @@ async function wsTransportFactory(url) {
|
|
|
62
72
|
}
|
|
63
73
|
};
|
|
64
74
|
}
|
|
65
|
-
async function waitForOpen(socket, url) {
|
|
75
|
+
async function waitForOpen(socket, url, timeoutMs) {
|
|
66
76
|
await new Promise((resolve, reject) => {
|
|
67
|
-
|
|
77
|
+
let settled = false;
|
|
78
|
+
const cleanup = () => {
|
|
79
|
+
socket.off("open", onOpen);
|
|
68
80
|
socket.off("error", onError);
|
|
81
|
+
if (timer !== void 0) {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const onOpen = () => {
|
|
86
|
+
if (settled) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
settled = true;
|
|
90
|
+
cleanup();
|
|
69
91
|
resolve();
|
|
70
92
|
};
|
|
71
93
|
const onError = (err) => {
|
|
72
|
-
|
|
94
|
+
if (settled) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
settled = true;
|
|
98
|
+
cleanup();
|
|
73
99
|
reject(
|
|
74
100
|
new CfInspectorError(
|
|
75
101
|
"INSPECTOR_CONNECTION_FAILED",
|
|
@@ -77,29 +103,45 @@ async function waitForOpen(socket, url) {
|
|
|
77
103
|
)
|
|
78
104
|
);
|
|
79
105
|
};
|
|
106
|
+
const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
107
|
+
if (settled) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
settled = true;
|
|
111
|
+
cleanup();
|
|
112
|
+
socket.on("error", () => {
|
|
113
|
+
});
|
|
114
|
+
try {
|
|
115
|
+
socket.terminate();
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
reject(
|
|
119
|
+
new CfInspectorError(
|
|
120
|
+
"INSPECTOR_CONNECTION_FAILED",
|
|
121
|
+
`WebSocket handshake to ${url} timed out after ${timeoutMs.toString()}ms`
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
}, timeoutMs);
|
|
80
125
|
socket.once("open", onOpen);
|
|
81
126
|
socket.once("error", onError);
|
|
82
127
|
});
|
|
83
128
|
}
|
|
84
|
-
function wrapListener(event, listener
|
|
129
|
+
function wrapListener(event, listener) {
|
|
85
130
|
if (event === "message") {
|
|
86
131
|
const wrapped2 = (data) => {
|
|
87
132
|
listener(data.toString("utf8"));
|
|
88
133
|
};
|
|
89
|
-
wrappers.set(listener, wrapped2);
|
|
90
134
|
return wrapped2;
|
|
91
135
|
}
|
|
92
136
|
if (event === "close") {
|
|
93
137
|
const wrapped2 = () => {
|
|
94
138
|
listener();
|
|
95
139
|
};
|
|
96
|
-
wrappers.set(listener, wrapped2);
|
|
97
140
|
return wrapped2;
|
|
98
141
|
}
|
|
99
142
|
const wrapped = (err) => {
|
|
100
143
|
listener(err);
|
|
101
144
|
};
|
|
102
|
-
wrappers.set(listener, wrapped);
|
|
103
145
|
return wrapped;
|
|
104
146
|
}
|
|
105
147
|
var init_wsTransport = __esm({
|
|
@@ -110,7 +152,7 @@ var init_wsTransport = __esm({
|
|
|
110
152
|
});
|
|
111
153
|
|
|
112
154
|
// src/cli.ts
|
|
113
|
-
import
|
|
155
|
+
import process11 from "process";
|
|
114
156
|
|
|
115
157
|
// src/cli/program.ts
|
|
116
158
|
import { Command } from "commander";
|
|
@@ -250,8 +292,11 @@ function writeHumanSnapshot(snapshot) {
|
|
|
250
292
|
` reason: ${snapshot.reason}`,
|
|
251
293
|
` paused: ${pausedDuration}`
|
|
252
294
|
);
|
|
295
|
+
if (snapshot.exception !== void 0) {
|
|
296
|
+
appendExceptionLines(lines, snapshot.exception);
|
|
297
|
+
}
|
|
253
298
|
if (snapshot.topFrame) {
|
|
254
|
-
appendFrameLines(lines, snapshot);
|
|
299
|
+
appendFrameLines(lines, snapshot.topFrame);
|
|
255
300
|
}
|
|
256
301
|
if (snapshot.captures.length > 0) {
|
|
257
302
|
lines.push(" captures:");
|
|
@@ -260,14 +305,16 @@ function writeHumanSnapshot(snapshot) {
|
|
|
260
305
|
lines.push(` ${capture.expression} = ${detail}`);
|
|
261
306
|
}
|
|
262
307
|
}
|
|
308
|
+
if (snapshot.stack !== void 0 && snapshot.stack.length > 0) {
|
|
309
|
+
lines.push(" stack:");
|
|
310
|
+
for (const frame of snapshot.stack) {
|
|
311
|
+
appendStackFrameLine(lines, frame);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
263
314
|
process.stdout.write(`${lines.join("\n")}
|
|
264
315
|
`);
|
|
265
316
|
}
|
|
266
|
-
function appendFrameLines(lines,
|
|
267
|
-
const frame = snapshot.topFrame;
|
|
268
|
-
if (frame === void 0) {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
317
|
+
function appendFrameLines(lines, frame) {
|
|
271
318
|
const fnName = frame.functionName.length === 0 ? "(anonymous)" : frame.functionName;
|
|
272
319
|
const sourceUrl = frame.url !== void 0 && frame.url.length > 0 ? frame.url : "(unknown)";
|
|
273
320
|
lines.push(
|
|
@@ -283,6 +330,25 @@ function appendFrameLines(lines, snapshot) {
|
|
|
283
330
|
}
|
|
284
331
|
}
|
|
285
332
|
}
|
|
333
|
+
function appendStackFrameLine(lines, frame) {
|
|
334
|
+
const fnName = frame.functionName.length === 0 ? "(anonymous)" : frame.functionName;
|
|
335
|
+
const sourceUrl = frame.url !== void 0 && frame.url.length > 0 ? frame.url : "(unknown)";
|
|
336
|
+
lines.push(` ${fnName} ${sourceUrl}:${frame.line.toString()}:${frame.column.toString()}`);
|
|
337
|
+
if (frame.captures !== void 0) {
|
|
338
|
+
for (const capture of frame.captures) {
|
|
339
|
+
const detail = capture.error ?? capture.value ?? "undefined";
|
|
340
|
+
lines.push(` ${capture.expression} = ${detail}`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
function appendExceptionLines(lines, exception) {
|
|
345
|
+
if (exception.error !== void 0) {
|
|
346
|
+
lines.push(` exception: !err ${exception.error}`);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
const detail = exception.description ?? exception.value ?? "(unknown)";
|
|
350
|
+
lines.push(` exception: ${detail}`);
|
|
351
|
+
}
|
|
286
352
|
function writeLogEvent(event, json) {
|
|
287
353
|
if (json) {
|
|
288
354
|
process.stdout.write(`${JSON.stringify(event)}
|
|
@@ -297,6 +363,25 @@ function writeLogEvent(event, json) {
|
|
|
297
363
|
process.stdout.write(`[${event.ts}] ${event.at} ${event.value ?? ""}
|
|
298
364
|
`);
|
|
299
365
|
}
|
|
366
|
+
function writeWatchEvent(event, json) {
|
|
367
|
+
if (json) {
|
|
368
|
+
process.stdout.write(`${JSON.stringify(event)}
|
|
369
|
+
`);
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
process.stdout.write(`[${event.ts}] hit#${event.hit.toString()} ${event.at}
|
|
373
|
+
`);
|
|
374
|
+
if (event.exception !== void 0) {
|
|
375
|
+
const detail = event.exception.description ?? event.exception.value ?? event.exception.error ?? "(unknown)";
|
|
376
|
+
process.stdout.write(` exception: ${detail}
|
|
377
|
+
`);
|
|
378
|
+
}
|
|
379
|
+
for (const capture of event.captures) {
|
|
380
|
+
const detail = capture.error ?? capture.value ?? "undefined";
|
|
381
|
+
process.stdout.write(` ${capture.expression} = ${detail}
|
|
382
|
+
`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
300
385
|
|
|
301
386
|
// src/cf/tunnel.ts
|
|
302
387
|
import { startDebugger } from "@saptools/cf-debugger";
|
|
@@ -361,10 +446,19 @@ var CdpClient = class _CdpClient {
|
|
|
361
446
|
return;
|
|
362
447
|
}
|
|
363
448
|
if (typeof parsed.method === "string") {
|
|
364
|
-
this.
|
|
365
|
-
this.
|
|
449
|
+
this.safeEmit(parsed.method, parsed.params);
|
|
450
|
+
this.safeEmit("event", { method: parsed.method, params: parsed.params });
|
|
366
451
|
}
|
|
367
452
|
};
|
|
453
|
+
safeEmit(event, payload) {
|
|
454
|
+
const listeners = this.emitter.listeners(event);
|
|
455
|
+
for (const listener of listeners) {
|
|
456
|
+
try {
|
|
457
|
+
listener(payload);
|
|
458
|
+
} catch {
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}
|
|
368
462
|
handleClose = () => {
|
|
369
463
|
this.markClosed(new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Inspector connection closed"));
|
|
370
464
|
};
|
|
@@ -382,7 +476,8 @@ var CdpClient = class _CdpClient {
|
|
|
382
476
|
}
|
|
383
477
|
static async connect(options) {
|
|
384
478
|
const factory = options.transportFactory ?? await loadDefaultFactory();
|
|
385
|
-
const
|
|
479
|
+
const factoryOptions = options.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: options.connectTimeoutMs };
|
|
480
|
+
const transport = await factory(options.url, factoryOptions);
|
|
386
481
|
return new _CdpClient(transport, options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
|
|
387
482
|
}
|
|
388
483
|
async send(method, params = {}) {
|
|
@@ -432,8 +527,16 @@ var CdpClient = class _CdpClient {
|
|
|
432
527
|
return;
|
|
433
528
|
}
|
|
434
529
|
const params = raw;
|
|
435
|
-
if (options.predicate
|
|
436
|
-
|
|
530
|
+
if (options.predicate) {
|
|
531
|
+
let accepted;
|
|
532
|
+
try {
|
|
533
|
+
accepted = options.predicate(params);
|
|
534
|
+
} catch {
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
if (!accepted) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
437
540
|
}
|
|
438
541
|
finish(params);
|
|
439
542
|
});
|
|
@@ -634,12 +737,13 @@ function toCallFrames(value, scripts) {
|
|
|
634
737
|
});
|
|
635
738
|
}
|
|
636
739
|
function toPauseEvent(params, receivedAtMs, scripts) {
|
|
637
|
-
|
|
740
|
+
const base = {
|
|
638
741
|
reason: asString(params.reason),
|
|
639
742
|
hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
|
|
640
743
|
callFrames: toCallFrames(params.callFrames, scripts),
|
|
641
744
|
receivedAtMs
|
|
642
745
|
};
|
|
746
|
+
return params.data === void 0 ? base : { ...base, data: params.data };
|
|
643
747
|
}
|
|
644
748
|
function topFrameLocation(pause) {
|
|
645
749
|
const top = pause.callFrames[0];
|
|
@@ -672,7 +776,18 @@ async function connectInspector(options) {
|
|
|
672
776
|
`No inspector targets available on ${host}:${options.port.toString()}`
|
|
673
777
|
);
|
|
674
778
|
}
|
|
675
|
-
const client = await CdpClient.connect({
|
|
779
|
+
const client = await CdpClient.connect({
|
|
780
|
+
url: target.webSocketDebuggerUrl,
|
|
781
|
+
connectTimeoutMs
|
|
782
|
+
});
|
|
783
|
+
try {
|
|
784
|
+
return await initSession(client, target);
|
|
785
|
+
} catch (err) {
|
|
786
|
+
client.dispose();
|
|
787
|
+
throw err;
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
async function initSession(client, target) {
|
|
676
791
|
const scripts = /* @__PURE__ */ new Map();
|
|
677
792
|
client.on("Debugger.scriptParsed", (raw) => {
|
|
678
793
|
const params = raw;
|
|
@@ -725,6 +840,7 @@ init_types();
|
|
|
725
840
|
// src/cli/commandTypes.ts
|
|
726
841
|
var DEFAULT_BREAKPOINT_TIMEOUT_SEC = 30;
|
|
727
842
|
var DEFAULT_CF_TIMEOUT_SEC = 60;
|
|
843
|
+
var DEFAULT_EXCEPTION_TIMEOUT_SEC = 30;
|
|
728
844
|
|
|
729
845
|
// src/cli/target.ts
|
|
730
846
|
function parsePositiveInt(raw, label) {
|
|
@@ -827,6 +943,9 @@ init_types();
|
|
|
827
943
|
async function resume(session) {
|
|
828
944
|
await session.client.send("Debugger.resume");
|
|
829
945
|
}
|
|
946
|
+
async function setPauseOnExceptions(session, state) {
|
|
947
|
+
await session.client.send("Debugger.setPauseOnExceptions", { state });
|
|
948
|
+
}
|
|
830
949
|
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
831
950
|
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
832
951
|
callFrameId,
|
|
@@ -914,23 +1033,9 @@ function writeHumanEvalResult(result) {
|
|
|
914
1033
|
`);
|
|
915
1034
|
}
|
|
916
1035
|
|
|
917
|
-
// src/cli/commands/
|
|
918
|
-
import
|
|
919
|
-
|
|
920
|
-
const target = resolveTarget(opts);
|
|
921
|
-
const scripts = await withSession(target, (session) => Promise.resolve(listScripts(session)));
|
|
922
|
-
if (opts.json) {
|
|
923
|
-
writeJson(scripts);
|
|
924
|
-
return;
|
|
925
|
-
}
|
|
926
|
-
for (const script of scripts) {
|
|
927
|
-
process4.stdout.write(`${script.scriptId} ${script.url}
|
|
928
|
-
`);
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
|
|
932
|
-
// src/cli/commands/log.ts
|
|
933
|
-
import process6 from "process";
|
|
1036
|
+
// src/cli/commands/exception.ts
|
|
1037
|
+
import { performance as performance3 } from "perf_hooks";
|
|
1038
|
+
import process5 from "process";
|
|
934
1039
|
|
|
935
1040
|
// src/pathMapper.ts
|
|
936
1041
|
init_types();
|
|
@@ -1032,9 +1137,11 @@ function normalizeRegexRootPattern(pattern) {
|
|
|
1032
1137
|
const withoutEndAnchor = withoutStartAnchor.endsWith("$") && !isEscaped(withoutStartAnchor, withoutStartAnchor.length - 1) ? withoutStartAnchor.slice(0, -1) : withoutStartAnchor;
|
|
1033
1138
|
return stripTrailingSlash(withoutEndAnchor);
|
|
1034
1139
|
}
|
|
1035
|
-
function buildFileUrlRegex(
|
|
1036
|
-
|
|
1037
|
-
|
|
1140
|
+
function buildFileUrlRegex(embeddedRoot, tail, separator) {
|
|
1141
|
+
return `^file://${embeddedRoot}${separator}${tail}$`;
|
|
1142
|
+
}
|
|
1143
|
+
function rootSeparator(rawPattern) {
|
|
1144
|
+
return rawPattern.endsWith("/") ? "" : "/";
|
|
1038
1145
|
}
|
|
1039
1146
|
function escapeRegExp(value) {
|
|
1040
1147
|
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
@@ -1061,18 +1168,58 @@ function buildBreakpointUrlRegex(input) {
|
|
|
1061
1168
|
return `(?:^|/)${tail}$`;
|
|
1062
1169
|
}
|
|
1063
1170
|
case "literal": {
|
|
1064
|
-
const
|
|
1065
|
-
return buildFileUrlRegex(
|
|
1171
|
+
const root = input.remoteRoot.value;
|
|
1172
|
+
return buildFileUrlRegex(escapeRegExp(root), tail, rootSeparator(root));
|
|
1066
1173
|
}
|
|
1067
1174
|
case "regex": {
|
|
1068
|
-
const
|
|
1069
|
-
return buildFileUrlRegex(
|
|
1175
|
+
const root = normalizeRegexRootPattern(input.remoteRoot.pattern);
|
|
1176
|
+
return buildFileUrlRegex(`(?:${root})`, tail, rootSeparator(root));
|
|
1070
1177
|
}
|
|
1071
1178
|
}
|
|
1072
1179
|
}
|
|
1073
1180
|
|
|
1074
1181
|
// src/inspector/breakpoints.ts
|
|
1075
1182
|
init_types();
|
|
1183
|
+
var HITS_GLOBAL = "globalThis.__CFI_HITS";
|
|
1184
|
+
var counterKeyCounter = 0;
|
|
1185
|
+
function nextCounterKey(file, line) {
|
|
1186
|
+
counterKeyCounter += 1;
|
|
1187
|
+
return `${file}:${line.toString()}:${counterKeyCounter.toString()}`;
|
|
1188
|
+
}
|
|
1189
|
+
function validateHitCount(hitCount) {
|
|
1190
|
+
if (hitCount === void 0) {
|
|
1191
|
+
return void 0;
|
|
1192
|
+
}
|
|
1193
|
+
if (!Number.isInteger(hitCount) || hitCount <= 0) {
|
|
1194
|
+
throw new CfInspectorError(
|
|
1195
|
+
"INVALID_HIT_COUNT",
|
|
1196
|
+
`hitCount must be a positive integer, received: ${hitCount.toString()}`
|
|
1197
|
+
);
|
|
1198
|
+
}
|
|
1199
|
+
return hitCount;
|
|
1200
|
+
}
|
|
1201
|
+
function buildHitCountedCondition(hitCount, counterKey, userCondition) {
|
|
1202
|
+
const keyLiteral = JSON.stringify(counterKey);
|
|
1203
|
+
const baseCondition = userCondition !== void 0 && userCondition.trim().length > 0 ? `(${userCondition})` : "true";
|
|
1204
|
+
return [
|
|
1205
|
+
"(function(){",
|
|
1206
|
+
`var m=(${HITS_GLOBAL}=${HITS_GLOBAL}||{});`,
|
|
1207
|
+
`var k=${keyLiteral};`,
|
|
1208
|
+
"m[k]=(m[k]||0)+1;",
|
|
1209
|
+
`if(m[k]<${hitCount.toString()})return false;`,
|
|
1210
|
+
`return ${baseCondition};`,
|
|
1211
|
+
"})()"
|
|
1212
|
+
].join("");
|
|
1213
|
+
}
|
|
1214
|
+
function resolveCondition(input) {
|
|
1215
|
+
const condition = input.condition;
|
|
1216
|
+
const hitCount = validateHitCount(input.hitCount);
|
|
1217
|
+
if (hitCount === void 0) {
|
|
1218
|
+
return condition !== void 0 && condition.length > 0 ? condition : void 0;
|
|
1219
|
+
}
|
|
1220
|
+
const counterKey = nextCounterKey(input.file, input.line);
|
|
1221
|
+
return buildHitCountedCondition(hitCount, counterKey, condition);
|
|
1222
|
+
}
|
|
1076
1223
|
async function setBreakpoint(session, input) {
|
|
1077
1224
|
const remoteRoot = input.remoteRoot ?? { kind: "none" };
|
|
1078
1225
|
const urlRegex = buildBreakpointUrlRegex({ file: input.file, remoteRoot });
|
|
@@ -1080,8 +1227,9 @@ async function setBreakpoint(session, input) {
|
|
|
1080
1227
|
lineNumber: input.line - 1,
|
|
1081
1228
|
urlRegex
|
|
1082
1229
|
};
|
|
1083
|
-
|
|
1084
|
-
|
|
1230
|
+
const condition = resolveCondition(input);
|
|
1231
|
+
if (condition !== void 0) {
|
|
1232
|
+
params["condition"] = condition;
|
|
1085
1233
|
}
|
|
1086
1234
|
const result = await session.client.send(
|
|
1087
1235
|
"Debugger.setBreakpointByUrl",
|
|
@@ -1106,813 +1254,1199 @@ async function removeBreakpoint(session, breakpointId) {
|
|
|
1106
1254
|
await session.client.send("Debugger.removeBreakpoint", { breakpointId });
|
|
1107
1255
|
}
|
|
1108
1256
|
|
|
1109
|
-
// src/
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
"console.log(s, r);",
|
|
1121
|
-
"}catch(e){",
|
|
1122
|
-
"console.log(s, '!err:'+(e&&e.message?e.message:String(e)));",
|
|
1123
|
-
"}",
|
|
1124
|
-
"return false;",
|
|
1125
|
-
"})()"
|
|
1126
|
-
].join("");
|
|
1257
|
+
// src/inspector/pause.ts
|
|
1258
|
+
init_types();
|
|
1259
|
+
import { performance as performance2 } from "perf_hooks";
|
|
1260
|
+
function pauseMatches(pause, breakpointIds, pauseReasons) {
|
|
1261
|
+
if (pauseReasons !== void 0 && pauseReasons.length > 0) {
|
|
1262
|
+
return pauseReasons.includes(pause.reason);
|
|
1263
|
+
}
|
|
1264
|
+
if (breakpointIds === void 0 || breakpointIds.length === 0) {
|
|
1265
|
+
return true;
|
|
1266
|
+
}
|
|
1267
|
+
return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
|
|
1127
1268
|
}
|
|
1128
|
-
function
|
|
1129
|
-
return
|
|
1269
|
+
function remainingUntil(deadlineMs) {
|
|
1270
|
+
return Math.max(0, deadlineMs - performance2.now());
|
|
1130
1271
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
return
|
|
1272
|
+
function hasResumedSincePause(session, pause) {
|
|
1273
|
+
const pauseAt = pause.receivedAtMs;
|
|
1274
|
+
const resumedAt = session.debuggerState.lastResumedAtMs;
|
|
1275
|
+
return pauseAt !== void 0 && resumedAt !== void 0 && resumedAt >= pauseAt;
|
|
1135
1276
|
}
|
|
1136
|
-
function
|
|
1137
|
-
|
|
1138
|
-
|
|
1277
|
+
function throwBreakpointTimeout(timeoutMs) {
|
|
1278
|
+
throw new CfInspectorError(
|
|
1279
|
+
"BREAKPOINT_NOT_HIT",
|
|
1280
|
+
`Timed out waiting for matching Debugger.paused after ${timeoutMs.toString()}ms`
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1283
|
+
function throwUnrelatedPauseTimeout(pause, timeoutMs) {
|
|
1284
|
+
throw new CfInspectorError(
|
|
1285
|
+
"UNRELATED_PAUSE_TIMEOUT",
|
|
1286
|
+
`Target stayed paused by another debugger event before this command's breakpoint could hit within ${timeoutMs.toString()}ms`,
|
|
1287
|
+
pauseDetail(pause)
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, timeoutMs) {
|
|
1291
|
+
if (hasResumedSincePause(session, pause)) {
|
|
1292
|
+
return;
|
|
1139
1293
|
}
|
|
1140
|
-
const
|
|
1141
|
-
if (
|
|
1142
|
-
|
|
1294
|
+
const remainingMs = remainingUntil(deadlineMs);
|
|
1295
|
+
if (remainingMs <= 0) {
|
|
1296
|
+
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1143
1297
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1298
|
+
try {
|
|
1299
|
+
await session.client.waitFor("Debugger.resumed", { timeoutMs: remainingMs });
|
|
1300
|
+
session.debuggerState.lastResumedAtMs = performance2.now();
|
|
1301
|
+
} catch (err) {
|
|
1302
|
+
if (err instanceof CfInspectorError && err.code === "BREAKPOINT_NOT_HIT") {
|
|
1303
|
+
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1304
|
+
}
|
|
1305
|
+
throw err;
|
|
1148
1306
|
}
|
|
1149
|
-
return index === 0 ? void 0 : "";
|
|
1150
1307
|
}
|
|
1151
|
-
function
|
|
1152
|
-
if (
|
|
1153
|
-
|
|
1308
|
+
async function handleUnmatchedPause(session, pause, options, deadlineMs) {
|
|
1309
|
+
if (options.unmatchedPausePolicy === "fail") {
|
|
1310
|
+
throw new CfInspectorError(
|
|
1311
|
+
"UNRELATED_PAUSE",
|
|
1312
|
+
"Target paused before this command's breakpoint was reached",
|
|
1313
|
+
pauseDetail(pause)
|
|
1314
|
+
);
|
|
1154
1315
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
return void 0;
|
|
1316
|
+
if (hasResumedSincePause(session, pause)) {
|
|
1317
|
+
return;
|
|
1158
1318
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1319
|
+
options.onUnmatchedPause?.(pause);
|
|
1320
|
+
await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
|
|
1321
|
+
}
|
|
1322
|
+
async function waitForPause(session, options) {
|
|
1323
|
+
const deadlineMs = performance2.now() + options.timeoutMs;
|
|
1324
|
+
const buffer = session.pauseBuffer;
|
|
1325
|
+
while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
|
|
1326
|
+
while (buffer.length > 0) {
|
|
1327
|
+
const buffered = buffer.shift();
|
|
1328
|
+
if (buffered === void 0) {
|
|
1329
|
+
continue;
|
|
1330
|
+
}
|
|
1331
|
+
if (pauseMatches(buffered, options.breakpointIds, options.pauseReasons)) {
|
|
1332
|
+
return buffered;
|
|
1333
|
+
}
|
|
1334
|
+
await handleUnmatchedPause(session, buffered, options, deadlineMs);
|
|
1335
|
+
}
|
|
1336
|
+
const pause = await waitForLivePause(session, options, deadlineMs);
|
|
1337
|
+
if (pauseMatches(pause, options.breakpointIds, options.pauseReasons)) {
|
|
1338
|
+
return pause;
|
|
1339
|
+
}
|
|
1340
|
+
await handleUnmatchedPause(session, pause, options, deadlineMs);
|
|
1164
1341
|
}
|
|
1165
|
-
|
|
1342
|
+
throwBreakpointTimeout(options.timeoutMs);
|
|
1166
1343
|
}
|
|
1167
|
-
function
|
|
1344
|
+
async function waitForLivePause(session, options, deadlineMs) {
|
|
1345
|
+
const remainingMs = remainingUntil(deadlineMs);
|
|
1346
|
+
if (remainingMs <= 0) {
|
|
1347
|
+
throwBreakpointTimeout(options.timeoutMs);
|
|
1348
|
+
}
|
|
1349
|
+
session.pauseWaitGate.active = true;
|
|
1350
|
+
let receivedAtMs;
|
|
1351
|
+
let params;
|
|
1168
1352
|
try {
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1353
|
+
params = await session.client.waitFor("Debugger.paused", {
|
|
1354
|
+
timeoutMs: remainingMs,
|
|
1355
|
+
predicate: () => {
|
|
1356
|
+
receivedAtMs = performance2.now();
|
|
1357
|
+
return true;
|
|
1358
|
+
}
|
|
1359
|
+
});
|
|
1360
|
+
} finally {
|
|
1361
|
+
session.pauseWaitGate.active = false;
|
|
1176
1362
|
}
|
|
1363
|
+
return toPauseEvent(params, receivedAtMs ?? performance2.now(), session.scripts);
|
|
1177
1364
|
}
|
|
1178
1365
|
|
|
1179
|
-
// src/
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
emitted += 1;
|
|
1190
|
-
options.onEvent(event);
|
|
1191
|
-
});
|
|
1192
|
-
let handle;
|
|
1193
|
-
try {
|
|
1194
|
-
handle = await setBreakpoint(session, {
|
|
1195
|
-
file: options.location.file,
|
|
1196
|
-
line: options.location.line,
|
|
1197
|
-
...options.remoteRoot === void 0 ? {} : { remoteRoot: options.remoteRoot },
|
|
1198
|
-
condition
|
|
1199
|
-
});
|
|
1200
|
-
} catch (err) {
|
|
1201
|
-
offEvent();
|
|
1202
|
-
throw err;
|
|
1366
|
+
// src/snapshot/values.ts
|
|
1367
|
+
init_types();
|
|
1368
|
+
var DEFAULT_MAX_VALUE_LENGTH = 4096;
|
|
1369
|
+
function isPrimitive(value) {
|
|
1370
|
+
const t = typeof value;
|
|
1371
|
+
return t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol";
|
|
1372
|
+
}
|
|
1373
|
+
function formatPrimitive(value) {
|
|
1374
|
+
if (typeof value === "symbol") {
|
|
1375
|
+
return value.toString();
|
|
1203
1376
|
}
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
const reason = await waitForStop(session, options);
|
|
1207
|
-
return { handle, sentinel, emitted, stoppedReason: reason };
|
|
1208
|
-
} finally {
|
|
1209
|
-
offEvent();
|
|
1210
|
-
await removeBreakpointBestEffort(session, handle.breakpointId);
|
|
1377
|
+
if (typeof value === "bigint") {
|
|
1378
|
+
return `${value.toString()}n`;
|
|
1211
1379
|
}
|
|
1380
|
+
return String(value);
|
|
1212
1381
|
}
|
|
1213
|
-
function
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
return void 0;
|
|
1382
|
+
function resolveMaxValueLength(value) {
|
|
1383
|
+
if (value === void 0) {
|
|
1384
|
+
return DEFAULT_MAX_VALUE_LENGTH;
|
|
1217
1385
|
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1386
|
+
if (!Number.isInteger(value) || value <= 0) {
|
|
1387
|
+
throw new CfInspectorError(
|
|
1388
|
+
"INVALID_ARGUMENT",
|
|
1389
|
+
`Invalid maxValueLength: ${value.toString()} \u2014 expected a positive integer`
|
|
1390
|
+
);
|
|
1391
|
+
}
|
|
1392
|
+
return value;
|
|
1220
1393
|
}
|
|
1221
|
-
|
|
1394
|
+
function limitValueLength(raw, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
1395
|
+
if (raw.length <= maxValueLength) {
|
|
1396
|
+
return raw;
|
|
1397
|
+
}
|
|
1398
|
+
return `${raw.slice(0, maxValueLength)}...`;
|
|
1399
|
+
}
|
|
1400
|
+
function parseQuotedString(value) {
|
|
1222
1401
|
try {
|
|
1223
|
-
|
|
1402
|
+
const parsed = JSON.parse(value);
|
|
1403
|
+
return typeof parsed === "string" ? parsed : value;
|
|
1224
1404
|
} catch {
|
|
1405
|
+
return value;
|
|
1225
1406
|
}
|
|
1226
1407
|
}
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
};
|
|
1247
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
1248
|
-
if (options.signal?.aborted === true) {
|
|
1249
|
-
finish("signal");
|
|
1408
|
+
function parseNumericIndex(name) {
|
|
1409
|
+
const parsed = Number.parseInt(name, 10);
|
|
1410
|
+
if (!Number.isInteger(parsed) || parsed < 0 || parsed.toString() !== name) {
|
|
1411
|
+
return void 0;
|
|
1412
|
+
}
|
|
1413
|
+
return parsed;
|
|
1414
|
+
}
|
|
1415
|
+
function scalarFromVariable(variable) {
|
|
1416
|
+
const value = variable.value;
|
|
1417
|
+
if (variable.type === "string") {
|
|
1418
|
+
return parseQuotedString(value);
|
|
1419
|
+
}
|
|
1420
|
+
if (variable.type === "number") {
|
|
1421
|
+
const parsed = Number(value);
|
|
1422
|
+
return Number.isFinite(parsed) ? parsed : value;
|
|
1423
|
+
}
|
|
1424
|
+
if (variable.type === "boolean") {
|
|
1425
|
+
if (value === "true") {
|
|
1426
|
+
return true;
|
|
1250
1427
|
}
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1428
|
+
if (value === "false") {
|
|
1429
|
+
return false;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
if (variable.type === "undefined") {
|
|
1433
|
+
return "[undefined]";
|
|
1434
|
+
}
|
|
1435
|
+
if (variable.type === "bigint") {
|
|
1436
|
+
return value;
|
|
1437
|
+
}
|
|
1438
|
+
return value === "null" ? null : value;
|
|
1439
|
+
}
|
|
1440
|
+
function isArrayLikeChildren(children) {
|
|
1441
|
+
let hasNumeric = false;
|
|
1442
|
+
for (const child of children) {
|
|
1443
|
+
if (child.name === "length") {
|
|
1444
|
+
continue;
|
|
1445
|
+
}
|
|
1446
|
+
if (parseNumericIndex(child.name) === void 0) {
|
|
1447
|
+
return false;
|
|
1448
|
+
}
|
|
1449
|
+
hasNumeric = true;
|
|
1450
|
+
}
|
|
1451
|
+
return hasNumeric;
|
|
1452
|
+
}
|
|
1453
|
+
function toStructuredValue(variable) {
|
|
1454
|
+
const children = variable.children;
|
|
1455
|
+
if (children === void 0 || children.length === 0) {
|
|
1456
|
+
return scalarFromVariable(variable);
|
|
1457
|
+
}
|
|
1458
|
+
if (isArrayLikeChildren(children)) {
|
|
1459
|
+
const indexed = children.flatMap((child) => {
|
|
1460
|
+
const index = parseNumericIndex(child.name);
|
|
1461
|
+
if (index === void 0) {
|
|
1462
|
+
return [];
|
|
1254
1463
|
}
|
|
1255
|
-
|
|
1256
|
-
|
|
1464
|
+
return [[index, toStructuredValue(child)]];
|
|
1465
|
+
});
|
|
1466
|
+
const maxIndex = Math.max(...indexed.map(([index]) => index));
|
|
1467
|
+
const out2 = Array.from({ length: maxIndex + 1 }, () => null);
|
|
1468
|
+
for (const [index, entry] of indexed) {
|
|
1469
|
+
out2[index] = entry;
|
|
1257
1470
|
}
|
|
1258
|
-
|
|
1471
|
+
return out2;
|
|
1472
|
+
}
|
|
1473
|
+
const out = {};
|
|
1474
|
+
for (const child of children) {
|
|
1475
|
+
out[child.name] = toStructuredValue(child);
|
|
1476
|
+
}
|
|
1477
|
+
return out;
|
|
1259
1478
|
}
|
|
1260
1479
|
|
|
1261
|
-
// src/
|
|
1262
|
-
|
|
1480
|
+
// src/snapshot/evaluation.ts
|
|
1481
|
+
function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
1482
|
+
if (result.exceptionDetails !== void 0) {
|
|
1483
|
+
return { expression, error: readEvalError(result, maxValueLength) };
|
|
1484
|
+
}
|
|
1485
|
+
const inner = result.result;
|
|
1486
|
+
if (!inner) {
|
|
1487
|
+
return { expression, error: "no result returned" };
|
|
1488
|
+
}
|
|
1489
|
+
const type = typeof inner.type === "string" ? inner.type : void 0;
|
|
1490
|
+
const buildCaptured = (rendered) => {
|
|
1491
|
+
const sanitized = limitValueLength(rendered, maxValueLength);
|
|
1492
|
+
const base = { expression, value: sanitized };
|
|
1493
|
+
return type === void 0 ? base : { ...base, type };
|
|
1494
|
+
};
|
|
1495
|
+
if (type === "string" && typeof inner.value === "string") {
|
|
1496
|
+
return buildCaptured(JSON.stringify(inner.value));
|
|
1497
|
+
}
|
|
1498
|
+
if ((type === "number" || type === "boolean" || type === "bigint") && isPrimitive(inner.value)) {
|
|
1499
|
+
return buildCaptured(formatPrimitive(inner.value));
|
|
1500
|
+
}
|
|
1501
|
+
if (typeof inner.description === "string") {
|
|
1502
|
+
return buildCaptured(inner.description);
|
|
1503
|
+
}
|
|
1504
|
+
if (isPrimitive(inner.value)) {
|
|
1505
|
+
return buildCaptured(formatPrimitive(inner.value));
|
|
1506
|
+
}
|
|
1507
|
+
return buildCaptured("undefined");
|
|
1508
|
+
}
|
|
1509
|
+
function readEvalError(result, maxValueLength) {
|
|
1510
|
+
const text = typeof result.exceptionDetails?.exception?.description === "string" ? result.exceptionDetails.exception.description : typeof result.exceptionDetails?.text === "string" ? result.exceptionDetails.text : "evaluation failed";
|
|
1511
|
+
return limitValueLength(text, maxValueLength);
|
|
1512
|
+
}
|
|
1263
1513
|
|
|
1264
|
-
// src/
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1514
|
+
// src/snapshot/properties.ts
|
|
1515
|
+
var MAX_SCOPE_VARIABLES = 20;
|
|
1516
|
+
var MAX_CHILD_VARIABLES = 8;
|
|
1517
|
+
var MAX_VARIABLE_DEPTH = 2;
|
|
1518
|
+
function buildDescribed(value, type, objectId) {
|
|
1519
|
+
const base = { value };
|
|
1520
|
+
if (type !== void 0) {
|
|
1521
|
+
base.type = type;
|
|
1522
|
+
}
|
|
1523
|
+
if (objectId !== void 0) {
|
|
1524
|
+
base.objectId = objectId;
|
|
1274
1525
|
}
|
|
1526
|
+
return base;
|
|
1275
1527
|
}
|
|
1276
|
-
function
|
|
1277
|
-
|
|
1528
|
+
function describeProperty(prop) {
|
|
1529
|
+
const value = prop.value;
|
|
1530
|
+
if (value === void 0) {
|
|
1531
|
+
return { value: "undefined" };
|
|
1532
|
+
}
|
|
1533
|
+
const type = typeof value.type === "string" ? value.type : void 0;
|
|
1534
|
+
const objectId = typeof value.objectId === "string" ? value.objectId : void 0;
|
|
1535
|
+
if (type === "undefined") {
|
|
1536
|
+
return buildDescribed("undefined", type);
|
|
1537
|
+
}
|
|
1538
|
+
if (type === "string" && typeof value.value === "string") {
|
|
1539
|
+
return buildDescribed(JSON.stringify(value.value), type);
|
|
1540
|
+
}
|
|
1541
|
+
if ((type === "number" || type === "boolean" || type === "bigint" || type === "symbol") && isPrimitive(value.value)) {
|
|
1542
|
+
return buildDescribed(formatPrimitive(value.value), type);
|
|
1543
|
+
}
|
|
1544
|
+
if (typeof value.description === "string") {
|
|
1545
|
+
return buildDescribed(value.description, type, objectId);
|
|
1546
|
+
}
|
|
1547
|
+
if (isPrimitive(value.value)) {
|
|
1548
|
+
return buildDescribed(formatPrimitive(value.value), type);
|
|
1549
|
+
}
|
|
1550
|
+
if (objectId === void 0) {
|
|
1551
|
+
return buildDescribed("undefined", type);
|
|
1552
|
+
}
|
|
1553
|
+
return buildDescribed("[object]", type, objectId);
|
|
1278
1554
|
}
|
|
1279
|
-
function
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1555
|
+
function isExpandable(type) {
|
|
1556
|
+
return type === "object" || type === "function";
|
|
1557
|
+
}
|
|
1558
|
+
async function captureProperties(session, objectId, limit, depth, maxValueLength) {
|
|
1559
|
+
const properties = await getProperties(session, objectId);
|
|
1560
|
+
const limited = properties.slice(0, limit);
|
|
1561
|
+
const variables = await Promise.all(
|
|
1562
|
+
limited.map(async (prop) => {
|
|
1563
|
+
return await captureProperty(session, prop, depth, maxValueLength);
|
|
1564
|
+
})
|
|
1284
1565
|
);
|
|
1566
|
+
return variables;
|
|
1285
1567
|
}
|
|
1286
|
-
function
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
};
|
|
1568
|
+
async function captureProperty(session, prop, depth, maxValueLength) {
|
|
1569
|
+
const name = typeof prop.name === "string" ? prop.name : "?";
|
|
1570
|
+
const described = describeProperty(prop);
|
|
1571
|
+
const children = await capturePropertyChildren(session, described, depth, maxValueLength);
|
|
1572
|
+
const sanitizedValue = limitValueLength(described.value, maxValueLength);
|
|
1573
|
+
const base = { name, value: sanitizedValue };
|
|
1574
|
+
const withType = described.type === void 0 ? base : { ...base, type: described.type };
|
|
1575
|
+
return children === void 0 ? withType : { ...withType, children };
|
|
1295
1576
|
}
|
|
1296
|
-
function
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1577
|
+
async function capturePropertyChildren(session, described, depth, maxValueLength) {
|
|
1578
|
+
if (depth <= 0 || described.objectId === void 0 || !isExpandable(described.type)) {
|
|
1579
|
+
return void 0;
|
|
1580
|
+
}
|
|
1581
|
+
try {
|
|
1582
|
+
const nested = await captureProperties(
|
|
1583
|
+
session,
|
|
1584
|
+
described.objectId,
|
|
1585
|
+
MAX_CHILD_VARIABLES,
|
|
1586
|
+
depth - 1,
|
|
1587
|
+
maxValueLength
|
|
1588
|
+
);
|
|
1589
|
+
return nested.length > 0 ? nested : void 0;
|
|
1590
|
+
} catch {
|
|
1591
|
+
return void 0;
|
|
1300
1592
|
}
|
|
1301
|
-
const url = top.url !== void 0 && top.url.length > 0 ? top.url : "(unknown)";
|
|
1302
|
-
return `${url}:${(top.lineNumber + 1).toString()}:${(top.columnNumber + 1).toString()}`;
|
|
1303
1593
|
}
|
|
1304
1594
|
|
|
1305
|
-
// src/
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1595
|
+
// src/snapshot/exception.ts
|
|
1596
|
+
function asString2(value) {
|
|
1597
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1598
|
+
}
|
|
1599
|
+
async function materializeObject(session, objectId, maxValueLength) {
|
|
1600
|
+
try {
|
|
1601
|
+
const properties = await captureProperties(
|
|
1602
|
+
session,
|
|
1603
|
+
objectId,
|
|
1604
|
+
MAX_SCOPE_VARIABLES,
|
|
1605
|
+
MAX_VARIABLE_DEPTH,
|
|
1606
|
+
maxValueLength
|
|
1607
|
+
);
|
|
1608
|
+
if (properties.length === 0) {
|
|
1609
|
+
return void 0;
|
|
1610
|
+
}
|
|
1611
|
+
const structured = {};
|
|
1612
|
+
for (const variable of properties) {
|
|
1613
|
+
structured[variable.name] = toStructuredValue(variable);
|
|
1614
|
+
}
|
|
1615
|
+
return JSON.stringify(structured);
|
|
1616
|
+
} catch {
|
|
1617
|
+
return void 0;
|
|
1314
1618
|
}
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
abort.abort();
|
|
1318
|
-
};
|
|
1319
|
-
process6.once("SIGINT", onSig);
|
|
1320
|
-
process6.once("SIGTERM", onSig);
|
|
1619
|
+
}
|
|
1620
|
+
async function readPropertyDescription(session, objectId, name) {
|
|
1321
1621
|
try {
|
|
1322
|
-
await
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
}
|
|
1340
|
-
|
|
1341
|
-
|
|
1622
|
+
const properties = await getProperties(session, objectId);
|
|
1623
|
+
for (const prop of properties) {
|
|
1624
|
+
if (prop.name !== name) {
|
|
1625
|
+
continue;
|
|
1626
|
+
}
|
|
1627
|
+
const value = prop.value;
|
|
1628
|
+
if (value === void 0) {
|
|
1629
|
+
continue;
|
|
1630
|
+
}
|
|
1631
|
+
if (typeof value.value === "string") {
|
|
1632
|
+
return value.value;
|
|
1633
|
+
}
|
|
1634
|
+
if (typeof value.description === "string") {
|
|
1635
|
+
return value.description;
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
return void 0;
|
|
1639
|
+
} catch {
|
|
1640
|
+
return void 0;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
async function captureException(session, pause, maxValueLength) {
|
|
1644
|
+
if (pause.reason !== "exception" && pause.reason !== "promiseRejection") {
|
|
1645
|
+
return void 0;
|
|
1646
|
+
}
|
|
1647
|
+
const data = pause.data;
|
|
1648
|
+
if (typeof data !== "object" || data === null) {
|
|
1649
|
+
return { error: "no exception data attached" };
|
|
1650
|
+
}
|
|
1651
|
+
const candidate = data;
|
|
1652
|
+
const type = asString2(candidate.type);
|
|
1653
|
+
const description = asString2(candidate.description);
|
|
1654
|
+
if (typeof candidate.value === "string") {
|
|
1655
|
+
return buildResult(type, description, JSON.stringify(candidate.value), maxValueLength);
|
|
1656
|
+
}
|
|
1657
|
+
if (typeof candidate.value === "number" || typeof candidate.value === "boolean") {
|
|
1658
|
+
return buildResult(type, description, String(candidate.value), maxValueLength);
|
|
1659
|
+
}
|
|
1660
|
+
const objectId = asString2(candidate.objectId);
|
|
1661
|
+
if (objectId === void 0) {
|
|
1662
|
+
if (description !== void 0) {
|
|
1663
|
+
return buildResult(type, description, description, maxValueLength);
|
|
1664
|
+
}
|
|
1665
|
+
return { error: "exception data has no objectId or value" };
|
|
1666
|
+
}
|
|
1667
|
+
const message = await readPropertyDescription(session, objectId, "message");
|
|
1668
|
+
const rendered = await materializeObject(session, objectId, maxValueLength);
|
|
1669
|
+
if (rendered !== void 0) {
|
|
1670
|
+
const result = buildResult(type, description, rendered, maxValueLength);
|
|
1671
|
+
return message === void 0 ? result : { ...result, description: limitValueLength(message, maxValueLength) };
|
|
1672
|
+
}
|
|
1673
|
+
return buildResult(type, description, description ?? "[exception]", maxValueLength);
|
|
1674
|
+
}
|
|
1675
|
+
function buildResult(type, description, value, maxValueLength) {
|
|
1676
|
+
const safeValue = limitValueLength(value, maxValueLength);
|
|
1677
|
+
const base = { value: safeValue };
|
|
1678
|
+
const withType = type === void 0 ? base : { ...base, type };
|
|
1679
|
+
return description === void 0 ? withType : { ...withType, description: limitValueLength(description, maxValueLength) };
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
// src/snapshot/objects.ts
|
|
1683
|
+
function objectIdFromEvalResult(result) {
|
|
1684
|
+
const inner = result.result;
|
|
1685
|
+
if (inner?.type !== "object") {
|
|
1686
|
+
return void 0;
|
|
1687
|
+
}
|
|
1688
|
+
const objectId = inner.objectId;
|
|
1689
|
+
if (typeof objectId !== "string" || objectId.length === 0) {
|
|
1690
|
+
return void 0;
|
|
1691
|
+
}
|
|
1692
|
+
return objectId;
|
|
1693
|
+
}
|
|
1694
|
+
async function renderObjectCapture(session, objectId, maxValueLength) {
|
|
1695
|
+
try {
|
|
1696
|
+
const properties = await captureProperties(
|
|
1697
|
+
session,
|
|
1698
|
+
objectId,
|
|
1699
|
+
MAX_SCOPE_VARIABLES,
|
|
1700
|
+
MAX_VARIABLE_DEPTH,
|
|
1701
|
+
maxValueLength
|
|
1702
|
+
);
|
|
1703
|
+
const structured = {};
|
|
1704
|
+
for (const variable of properties) {
|
|
1705
|
+
structured[variable.name] = toStructuredValue(variable);
|
|
1706
|
+
}
|
|
1707
|
+
return JSON.stringify(structured);
|
|
1708
|
+
} catch {
|
|
1709
|
+
return void 0;
|
|
1342
1710
|
}
|
|
1343
1711
|
}
|
|
1344
|
-
function
|
|
1345
|
-
if (
|
|
1346
|
-
|
|
1347
|
-
`);
|
|
1348
|
-
return;
|
|
1712
|
+
function normalizeRenderedObjectCapture(rendered, original) {
|
|
1713
|
+
if (rendered === "{}" && original !== "Object") {
|
|
1714
|
+
return void 0;
|
|
1349
1715
|
}
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1716
|
+
if (original.startsWith("Array(") && rendered === '{"length":0}') {
|
|
1717
|
+
return "[]";
|
|
1718
|
+
}
|
|
1719
|
+
return rendered;
|
|
1354
1720
|
}
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
import process7 from "process";
|
|
1359
|
-
|
|
1360
|
-
// src/inspector/pause.ts
|
|
1361
|
-
init_types();
|
|
1362
|
-
import { performance as performance2 } from "perf_hooks";
|
|
1363
|
-
function pauseMatches(pause, breakpointIds) {
|
|
1364
|
-
if (breakpointIds === void 0 || breakpointIds.length === 0) {
|
|
1365
|
-
return true;
|
|
1721
|
+
async function withSerializedObjectCapture(session, expression, evalResult, captured, maxValueLength) {
|
|
1722
|
+
if (captured.error !== void 0 || captured.value === void 0) {
|
|
1723
|
+
return captured;
|
|
1366
1724
|
}
|
|
1367
|
-
|
|
1725
|
+
const objectId = objectIdFromEvalResult(evalResult);
|
|
1726
|
+
if (objectId === void 0) {
|
|
1727
|
+
return captured;
|
|
1728
|
+
}
|
|
1729
|
+
const rendered = await renderObjectCapture(session, objectId, maxValueLength);
|
|
1730
|
+
if (rendered === void 0) {
|
|
1731
|
+
return captured;
|
|
1732
|
+
}
|
|
1733
|
+
const normalized = normalizeRenderedObjectCapture(rendered, captured.value);
|
|
1734
|
+
if (normalized === void 0) {
|
|
1735
|
+
return captured;
|
|
1736
|
+
}
|
|
1737
|
+
const value = limitValueLength(normalized, maxValueLength);
|
|
1738
|
+
return captured.type === void 0 ? { expression, value } : { expression, value, type: captured.type };
|
|
1368
1739
|
}
|
|
1369
|
-
|
|
1370
|
-
|
|
1740
|
+
|
|
1741
|
+
// src/snapshot/scopes.ts
|
|
1742
|
+
var MAX_SCOPES = 3;
|
|
1743
|
+
var PRIORITY_BY_TYPE = {
|
|
1744
|
+
local: 0,
|
|
1745
|
+
arguments: 1,
|
|
1746
|
+
block: 2,
|
|
1747
|
+
closure: 3,
|
|
1748
|
+
catch: 4,
|
|
1749
|
+
with: 5,
|
|
1750
|
+
module: 6,
|
|
1751
|
+
script: 7
|
|
1752
|
+
};
|
|
1753
|
+
function selectScopes(scopeChain) {
|
|
1754
|
+
const eligible = scopeChain.filter((scope) => scope.objectId !== void 0 && scope.type !== "global");
|
|
1755
|
+
return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type)).slice(0, MAX_SCOPES);
|
|
1371
1756
|
}
|
|
1372
|
-
function
|
|
1373
|
-
|
|
1374
|
-
const resumedAt = session.debuggerState.lastResumedAtMs;
|
|
1375
|
-
return pauseAt !== void 0 && resumedAt !== void 0 && resumedAt >= pauseAt;
|
|
1757
|
+
function priorityOf(type) {
|
|
1758
|
+
return PRIORITY_BY_TYPE[type] ?? Number.MAX_SAFE_INTEGER;
|
|
1376
1759
|
}
|
|
1377
|
-
function
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1760
|
+
async function captureScopes(session, frame, maxValueLength) {
|
|
1761
|
+
const scopes = selectScopes(frame.scopeChain);
|
|
1762
|
+
return await Promise.all(
|
|
1763
|
+
scopes.map(async (scope) => {
|
|
1764
|
+
const objectId = scope.objectId;
|
|
1765
|
+
if (objectId === void 0) {
|
|
1766
|
+
return { type: scope.type, variables: [] };
|
|
1767
|
+
}
|
|
1768
|
+
try {
|
|
1769
|
+
const variables = await captureProperties(
|
|
1770
|
+
session,
|
|
1771
|
+
objectId,
|
|
1772
|
+
MAX_SCOPE_VARIABLES,
|
|
1773
|
+
MAX_VARIABLE_DEPTH,
|
|
1774
|
+
maxValueLength
|
|
1775
|
+
);
|
|
1776
|
+
return { type: scope.type, variables };
|
|
1777
|
+
} catch {
|
|
1778
|
+
return { type: scope.type, variables: [] };
|
|
1779
|
+
}
|
|
1780
|
+
})
|
|
1381
1781
|
);
|
|
1382
1782
|
}
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
)
|
|
1783
|
+
|
|
1784
|
+
// src/snapshot/stack.ts
|
|
1785
|
+
var DEFAULT_STACK_DEPTH = 1;
|
|
1786
|
+
var MAX_STACK_DEPTH = 64;
|
|
1787
|
+
function clampDepth(depth, frameCount) {
|
|
1788
|
+
if (depth <= 0) {
|
|
1789
|
+
return 0;
|
|
1790
|
+
}
|
|
1791
|
+
return Math.min(depth, frameCount, MAX_STACK_DEPTH);
|
|
1792
|
+
}
|
|
1793
|
+
function buildBaseFrame(frame) {
|
|
1794
|
+
const base = {
|
|
1795
|
+
functionName: frame.functionName,
|
|
1796
|
+
line: frame.lineNumber + 1,
|
|
1797
|
+
column: frame.columnNumber + 1
|
|
1798
|
+
};
|
|
1799
|
+
return frame.url === void 0 ? base : { ...base, url: frame.url };
|
|
1389
1800
|
}
|
|
1390
|
-
async function
|
|
1391
|
-
if (hasResumedSincePause(session, pause)) {
|
|
1392
|
-
return;
|
|
1393
|
-
}
|
|
1394
|
-
const remainingMs = remainingUntil(deadlineMs);
|
|
1395
|
-
if (remainingMs <= 0) {
|
|
1396
|
-
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1397
|
-
}
|
|
1801
|
+
async function captureFrameExpression(session, callFrameId, expression, maxValueLength) {
|
|
1398
1802
|
try {
|
|
1399
|
-
await session
|
|
1400
|
-
|
|
1803
|
+
const result = await evaluateOnFrame(session, callFrameId, expression);
|
|
1804
|
+
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1805
|
+
return await withSerializedObjectCapture(session, expression, result, captured, maxValueLength);
|
|
1401
1806
|
} catch (err) {
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
}
|
|
1405
|
-
throw err;
|
|
1406
|
-
}
|
|
1407
|
-
}
|
|
1408
|
-
async function handleUnmatchedPause(session, pause, options, deadlineMs) {
|
|
1409
|
-
if (options.unmatchedPausePolicy === "fail") {
|
|
1410
|
-
throw new CfInspectorError(
|
|
1411
|
-
"UNRELATED_PAUSE",
|
|
1412
|
-
"Target paused before this command's breakpoint was reached",
|
|
1413
|
-
pauseDetail(pause)
|
|
1414
|
-
);
|
|
1415
|
-
}
|
|
1416
|
-
if (hasResumedSincePause(session, pause)) {
|
|
1417
|
-
return;
|
|
1807
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1808
|
+
return { expression, error: limitValueLength(message, maxValueLength) };
|
|
1418
1809
|
}
|
|
1419
|
-
options.onUnmatchedPause?.(pause);
|
|
1420
|
-
await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
|
|
1421
1810
|
}
|
|
1422
|
-
async function
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
|
|
1426
|
-
while (buffer.length > 0) {
|
|
1427
|
-
const buffered = buffer.shift();
|
|
1428
|
-
if (buffered === void 0) {
|
|
1429
|
-
continue;
|
|
1430
|
-
}
|
|
1431
|
-
if (pauseMatches(buffered, options.breakpointIds)) {
|
|
1432
|
-
return buffered;
|
|
1433
|
-
}
|
|
1434
|
-
await handleUnmatchedPause(session, buffered, options, deadlineMs);
|
|
1435
|
-
}
|
|
1436
|
-
const pause = await waitForLivePause(session, options, deadlineMs);
|
|
1437
|
-
if (pauseMatches(pause, options.breakpointIds)) {
|
|
1438
|
-
return pause;
|
|
1439
|
-
}
|
|
1440
|
-
await handleUnmatchedPause(session, pause, options, deadlineMs);
|
|
1811
|
+
async function captureFrameExpressions(session, frame, expressions, maxValueLength) {
|
|
1812
|
+
if (expressions.length === 0) {
|
|
1813
|
+
return [];
|
|
1441
1814
|
}
|
|
1442
|
-
|
|
1815
|
+
return await Promise.all(
|
|
1816
|
+
expressions.map(
|
|
1817
|
+
(expression) => captureFrameExpression(session, frame.callFrameId, expression, maxValueLength)
|
|
1818
|
+
)
|
|
1819
|
+
);
|
|
1443
1820
|
}
|
|
1444
|
-
async function
|
|
1445
|
-
const
|
|
1446
|
-
if (
|
|
1447
|
-
|
|
1821
|
+
async function walkStack(session, callFrames, options) {
|
|
1822
|
+
const depth = clampDepth(options.stackDepth, callFrames.length);
|
|
1823
|
+
if (depth <= 1) {
|
|
1824
|
+
return [];
|
|
1448
1825
|
}
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
predicate: () => {
|
|
1456
|
-
receivedAtMs = performance2.now();
|
|
1457
|
-
return true;
|
|
1826
|
+
const slice = callFrames.slice(0, depth);
|
|
1827
|
+
return await Promise.all(
|
|
1828
|
+
slice.map(async (frame) => {
|
|
1829
|
+
const base = buildBaseFrame(frame);
|
|
1830
|
+
if (options.stackCaptures.length === 0) {
|
|
1831
|
+
return base;
|
|
1458
1832
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1833
|
+
const captures = await captureFrameExpressions(
|
|
1834
|
+
session,
|
|
1835
|
+
frame,
|
|
1836
|
+
options.stackCaptures,
|
|
1837
|
+
options.maxValueLength
|
|
1838
|
+
);
|
|
1839
|
+
return { ...base, captures };
|
|
1840
|
+
})
|
|
1841
|
+
);
|
|
1464
1842
|
}
|
|
1465
1843
|
|
|
1466
|
-
// src/snapshot/
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1844
|
+
// src/snapshot/capture.ts
|
|
1845
|
+
async function captureSnapshot(session, pause, options = {}) {
|
|
1846
|
+
const maxValueLength = resolveMaxValueLength(options.maxValueLength);
|
|
1847
|
+
const top = pause.callFrames[0];
|
|
1848
|
+
let topFrame;
|
|
1849
|
+
let captures = [];
|
|
1850
|
+
let stack = [];
|
|
1851
|
+
if (top) {
|
|
1852
|
+
topFrame = {
|
|
1853
|
+
functionName: top.functionName,
|
|
1854
|
+
...top.url === void 0 ? {} : { url: top.url },
|
|
1855
|
+
line: top.lineNumber + 1,
|
|
1856
|
+
column: top.columnNumber + 1
|
|
1857
|
+
};
|
|
1858
|
+
if (options.includeScopes === true) {
|
|
1859
|
+
const scopes = await captureScopes(session, top, maxValueLength);
|
|
1860
|
+
topFrame = { ...topFrame, scopes };
|
|
1861
|
+
}
|
|
1862
|
+
captures = await captureExpressions(session, top.callFrameId, options.captures, maxValueLength);
|
|
1863
|
+
stack = await walkStack(session, pause.callFrames, {
|
|
1864
|
+
stackDepth: options.stackDepth ?? DEFAULT_STACK_DEPTH,
|
|
1865
|
+
stackCaptures: options.stackCaptures ?? [],
|
|
1866
|
+
maxValueLength
|
|
1867
|
+
});
|
|
1479
1868
|
}
|
|
1480
|
-
|
|
1869
|
+
const exception = await captureException(session, pause, maxValueLength);
|
|
1870
|
+
return buildResult2({
|
|
1871
|
+
pause,
|
|
1872
|
+
topFrame,
|
|
1873
|
+
captures,
|
|
1874
|
+
stack,
|
|
1875
|
+
exception
|
|
1876
|
+
});
|
|
1481
1877
|
}
|
|
1482
|
-
function
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1878
|
+
function buildResult2(input) {
|
|
1879
|
+
const base = {
|
|
1880
|
+
reason: input.pause.reason,
|
|
1881
|
+
hitBreakpoints: input.pause.hitBreakpoints,
|
|
1882
|
+
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1883
|
+
captures: input.captures
|
|
1884
|
+
};
|
|
1885
|
+
const withFrame = input.topFrame === void 0 ? base : { ...base, topFrame: input.topFrame };
|
|
1886
|
+
const withStack = input.stack.length > 0 ? { ...withFrame, stack: input.stack } : withFrame;
|
|
1887
|
+
return input.exception === void 0 ? withStack : { ...withStack, exception: input.exception };
|
|
1888
|
+
}
|
|
1889
|
+
async function captureExpressions(session, callFrameId, captures, maxValueLength) {
|
|
1890
|
+
if (captures === void 0 || captures.length === 0) {
|
|
1891
|
+
return [];
|
|
1491
1892
|
}
|
|
1492
|
-
return
|
|
1893
|
+
return await Promise.all(
|
|
1894
|
+
captures.map(async (expression) => {
|
|
1895
|
+
return await captureExpression(session, callFrameId, expression, maxValueLength);
|
|
1896
|
+
})
|
|
1897
|
+
);
|
|
1493
1898
|
}
|
|
1494
|
-
function
|
|
1495
|
-
|
|
1496
|
-
|
|
1899
|
+
async function captureExpression(session, callFrameId, expression, maxValueLength) {
|
|
1900
|
+
try {
|
|
1901
|
+
const result = await evaluateOnFrame(session, callFrameId, expression);
|
|
1902
|
+
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1903
|
+
return await withSerializedObjectCapture(session, expression, result, captured, maxValueLength);
|
|
1904
|
+
} catch (err) {
|
|
1905
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1906
|
+
return { expression, error: limitValueLength(message, maxValueLength) };
|
|
1497
1907
|
}
|
|
1498
|
-
return `${raw.slice(0, maxValueLength)}...`;
|
|
1499
1908
|
}
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1909
|
+
|
|
1910
|
+
// src/cli/commands/exception.ts
|
|
1911
|
+
init_types();
|
|
1912
|
+
|
|
1913
|
+
// src/cli/captureParser.ts
|
|
1914
|
+
function parseCaptureList(raw) {
|
|
1915
|
+
if (raw === void 0 || raw.trim().length === 0) {
|
|
1916
|
+
return [];
|
|
1506
1917
|
}
|
|
1918
|
+
return splitCaptureExpressions(raw);
|
|
1507
1919
|
}
|
|
1508
|
-
function
|
|
1509
|
-
|
|
1510
|
-
if (!Number.isInteger(parsed) || parsed < 0 || parsed.toString() !== name) {
|
|
1511
|
-
return void 0;
|
|
1512
|
-
}
|
|
1513
|
-
return parsed;
|
|
1920
|
+
function isQuoteChar(value) {
|
|
1921
|
+
return value === "'" || value === '"' || value === "`";
|
|
1514
1922
|
}
|
|
1515
|
-
function
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
return parseQuotedString(value);
|
|
1923
|
+
function consumeQuotedChar(state, char) {
|
|
1924
|
+
if (state.quote === void 0) {
|
|
1925
|
+
return false;
|
|
1519
1926
|
}
|
|
1520
|
-
if (
|
|
1521
|
-
|
|
1522
|
-
return
|
|
1927
|
+
if (state.escaped) {
|
|
1928
|
+
state.escaped = false;
|
|
1929
|
+
return true;
|
|
1523
1930
|
}
|
|
1524
|
-
if (
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
}
|
|
1528
|
-
if (value === "false") {
|
|
1529
|
-
return false;
|
|
1530
|
-
}
|
|
1931
|
+
if (char === "\\") {
|
|
1932
|
+
state.escaped = true;
|
|
1933
|
+
return true;
|
|
1531
1934
|
}
|
|
1532
|
-
if (
|
|
1533
|
-
|
|
1935
|
+
if (char === state.quote) {
|
|
1936
|
+
state.quote = void 0;
|
|
1534
1937
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1938
|
+
return true;
|
|
1939
|
+
}
|
|
1940
|
+
function updateCaptureDepth(state, char) {
|
|
1941
|
+
if (char === "(") {
|
|
1942
|
+
state.parenDepth += 1;
|
|
1943
|
+
} else if (char === ")") {
|
|
1944
|
+
state.parenDepth = Math.max(0, state.parenDepth - 1);
|
|
1945
|
+
} else if (char === "[") {
|
|
1946
|
+
state.bracketDepth += 1;
|
|
1947
|
+
} else if (char === "]") {
|
|
1948
|
+
state.bracketDepth = Math.max(0, state.bracketDepth - 1);
|
|
1949
|
+
} else if (char === "{") {
|
|
1950
|
+
state.braceDepth += 1;
|
|
1951
|
+
} else if (char === "}") {
|
|
1952
|
+
state.braceDepth = Math.max(0, state.braceDepth - 1);
|
|
1537
1953
|
}
|
|
1538
|
-
return value === "null" ? null : value;
|
|
1539
1954
|
}
|
|
1540
|
-
function
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1955
|
+
function isTopLevel(state) {
|
|
1956
|
+
return state.parenDepth === 0 && state.bracketDepth === 0 && state.braceDepth === 0;
|
|
1957
|
+
}
|
|
1958
|
+
function appendCapturePiece(raw, state, end) {
|
|
1959
|
+
const piece = raw.slice(state.start, end).trim();
|
|
1960
|
+
if (piece.length > 0) {
|
|
1961
|
+
state.pieces.push(piece);
|
|
1544
1962
|
}
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1963
|
+
}
|
|
1964
|
+
function splitCaptureExpressions(raw) {
|
|
1965
|
+
const state = {
|
|
1966
|
+
escaped: false,
|
|
1967
|
+
parenDepth: 0,
|
|
1968
|
+
bracketDepth: 0,
|
|
1969
|
+
braceDepth: 0,
|
|
1970
|
+
quote: void 0,
|
|
1971
|
+
start: 0,
|
|
1972
|
+
pieces: []
|
|
1973
|
+
};
|
|
1974
|
+
for (let idx = 0; idx < raw.length; idx += 1) {
|
|
1975
|
+
const char = raw.charAt(idx);
|
|
1976
|
+
if (consumeQuotedChar(state, char)) {
|
|
1977
|
+
continue;
|
|
1549
1978
|
}
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1979
|
+
if (isQuoteChar(char)) {
|
|
1980
|
+
state.quote = char;
|
|
1981
|
+
continue;
|
|
1982
|
+
}
|
|
1983
|
+
updateCaptureDepth(state, char);
|
|
1984
|
+
if (char === "," && isTopLevel(state)) {
|
|
1985
|
+
appendCapturePiece(raw, state, idx);
|
|
1986
|
+
state.start = idx + 1;
|
|
1557
1987
|
}
|
|
1558
|
-
return out2;
|
|
1559
|
-
}
|
|
1560
|
-
const out = {};
|
|
1561
|
-
for (const child of children) {
|
|
1562
|
-
out[child.name] = toStructuredValue(child);
|
|
1563
1988
|
}
|
|
1564
|
-
|
|
1989
|
+
appendCapturePiece(raw, state, raw.length);
|
|
1990
|
+
return state.pieces;
|
|
1565
1991
|
}
|
|
1566
1992
|
|
|
1567
|
-
// src/
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1993
|
+
// src/cli/warnings.ts
|
|
1994
|
+
import process4 from "process";
|
|
1995
|
+
function warnOnUnboundBreakpoints(handles) {
|
|
1996
|
+
for (const handle of handles) {
|
|
1997
|
+
if (handle.resolvedLocations.length === 0) {
|
|
1998
|
+
process4.stderr.write(
|
|
1999
|
+
`[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.
|
|
2000
|
+
`
|
|
2001
|
+
);
|
|
2002
|
+
}
|
|
1575
2003
|
}
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
2004
|
+
}
|
|
2005
|
+
function roundDurationMs(durationMs) {
|
|
2006
|
+
return Math.round(durationMs * 1e3) / 1e3;
|
|
2007
|
+
}
|
|
2008
|
+
function warnOnUnmatchedPause(pause) {
|
|
2009
|
+
const reason = pause.reason.length > 0 ? pause.reason : "unknown";
|
|
2010
|
+
process4.stderr.write(
|
|
2011
|
+
`[cf-inspector] warning: target is paused by another debugger event (${reason} at ${formatPauseLocation(pause)}); waiting for it to resume...
|
|
2012
|
+
`
|
|
2013
|
+
);
|
|
2014
|
+
}
|
|
2015
|
+
function withPausedDuration(snapshot, pausedDurationMs) {
|
|
2016
|
+
const base = {
|
|
2017
|
+
reason: snapshot.reason,
|
|
2018
|
+
hitBreakpoints: snapshot.hitBreakpoints,
|
|
2019
|
+
capturedAt: snapshot.capturedAt,
|
|
2020
|
+
pausedDurationMs,
|
|
2021
|
+
captures: snapshot.captures
|
|
1581
2022
|
};
|
|
1582
|
-
|
|
1583
|
-
|
|
2023
|
+
const withFrame = snapshot.topFrame === void 0 ? base : { ...base, topFrame: snapshot.topFrame };
|
|
2024
|
+
const withStack = snapshot.stack === void 0 ? withFrame : { ...withFrame, stack: snapshot.stack };
|
|
2025
|
+
return snapshot.exception === void 0 ? withStack : { ...withStack, exception: snapshot.exception };
|
|
2026
|
+
}
|
|
2027
|
+
function formatPauseLocation(pause) {
|
|
2028
|
+
const top = pause.callFrames[0];
|
|
2029
|
+
if (top === void 0) {
|
|
2030
|
+
return "(no call frame)";
|
|
1584
2031
|
}
|
|
1585
|
-
|
|
1586
|
-
|
|
2032
|
+
const url = top.url !== void 0 && top.url.length > 0 ? top.url : "(unknown)";
|
|
2033
|
+
return `${url}:${(top.lineNumber + 1).toString()}:${(top.columnNumber + 1).toString()}`;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
// src/cli/commands/exception.ts
|
|
2037
|
+
var VALID_PAUSE_TYPES = ["uncaught", "caught", "all"];
|
|
2038
|
+
async function handleException(opts) {
|
|
2039
|
+
const prepared = prepareExceptionCommand(opts);
|
|
2040
|
+
const result = await runExceptionCommand(prepared, opts);
|
|
2041
|
+
if (opts.json) {
|
|
2042
|
+
writeJson(result);
|
|
2043
|
+
} else {
|
|
2044
|
+
writeHumanSnapshot(result);
|
|
1587
2045
|
}
|
|
1588
|
-
|
|
1589
|
-
|
|
2046
|
+
}
|
|
2047
|
+
function prepareExceptionCommand(opts) {
|
|
2048
|
+
const target = resolveTarget(opts);
|
|
2049
|
+
const stateRaw = (opts.type ?? "uncaught").trim().toLowerCase();
|
|
2050
|
+
if (!VALID_PAUSE_TYPES.includes(stateRaw)) {
|
|
2051
|
+
throw new CfInspectorError(
|
|
2052
|
+
"INVALID_PAUSE_TYPE",
|
|
2053
|
+
`--type must be one of ${VALID_PAUSE_TYPES.join(", ")} (received "${stateRaw}")`
|
|
2054
|
+
);
|
|
1590
2055
|
}
|
|
1591
|
-
|
|
1592
|
-
|
|
2056
|
+
const timeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_EXCEPTION_TIMEOUT_SEC;
|
|
2057
|
+
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
2058
|
+
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
2059
|
+
return {
|
|
2060
|
+
target,
|
|
2061
|
+
state: stateRaw,
|
|
2062
|
+
captures: parseCaptureList(opts.capture),
|
|
2063
|
+
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
2064
|
+
timeoutMs: timeoutSec * 1e3,
|
|
2065
|
+
...maxValueLength === void 0 ? {} : { maxValueLength },
|
|
2066
|
+
...stackDepth === void 0 ? {} : { stackDepth },
|
|
2067
|
+
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
2068
|
+
};
|
|
2069
|
+
}
|
|
2070
|
+
async function runExceptionCommand(command, opts) {
|
|
2071
|
+
return await withSession(command.target, async (session) => {
|
|
2072
|
+
await setPauseOnExceptions(session, command.state);
|
|
2073
|
+
try {
|
|
2074
|
+
const pause = await waitForPause(session, {
|
|
2075
|
+
timeoutMs: command.timeoutMs,
|
|
2076
|
+
pauseReasons: ["exception", "promiseRejection"],
|
|
2077
|
+
unmatchedPausePolicy: "wait-for-resume"
|
|
2078
|
+
});
|
|
2079
|
+
const pausedStartedAt = pause.receivedAtMs ?? performance3.now();
|
|
2080
|
+
const snapshot = await captureSnapshot(session, pause, {
|
|
2081
|
+
captures: command.captures,
|
|
2082
|
+
includeScopes: opts.includeScopes === true,
|
|
2083
|
+
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength },
|
|
2084
|
+
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
2085
|
+
stackCaptures: command.stackCaptures
|
|
2086
|
+
});
|
|
2087
|
+
if (opts.keepPaused === true) {
|
|
2088
|
+
return withPausedDuration(snapshot, null);
|
|
2089
|
+
}
|
|
2090
|
+
return await resumeAfterException(session, snapshot, pausedStartedAt);
|
|
2091
|
+
} finally {
|
|
2092
|
+
await disablePauseOnExceptionsBestEffort(session);
|
|
2093
|
+
}
|
|
2094
|
+
});
|
|
2095
|
+
}
|
|
2096
|
+
async function resumeAfterException(session, snapshot, pausedStartedAt) {
|
|
2097
|
+
try {
|
|
2098
|
+
await resume(session);
|
|
2099
|
+
return withPausedDuration(snapshot, roundDurationMs(performance3.now() - pausedStartedAt));
|
|
2100
|
+
} catch {
|
|
2101
|
+
process5.stderr.write(
|
|
2102
|
+
"[cf-inspector] warning: Debugger.resume failed after exception capture; pausedDurationMs is unknown.\n"
|
|
2103
|
+
);
|
|
2104
|
+
return withPausedDuration(snapshot, null);
|
|
1593
2105
|
}
|
|
1594
|
-
return buildCaptured("undefined");
|
|
1595
2106
|
}
|
|
1596
|
-
function
|
|
1597
|
-
|
|
1598
|
-
|
|
2107
|
+
async function disablePauseOnExceptionsBestEffort(session) {
|
|
2108
|
+
try {
|
|
2109
|
+
await setPauseOnExceptions(session, "none");
|
|
2110
|
+
} catch {
|
|
2111
|
+
}
|
|
1599
2112
|
}
|
|
1600
2113
|
|
|
1601
|
-
// src/
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
2114
|
+
// src/cli/commands/listScripts.ts
|
|
2115
|
+
import process6 from "process";
|
|
2116
|
+
async function handleListScripts(opts) {
|
|
2117
|
+
const target = resolveTarget(opts);
|
|
2118
|
+
const scripts = await withSession(target, (session) => Promise.resolve(listScripts(session)));
|
|
2119
|
+
if (opts.json) {
|
|
2120
|
+
writeJson(scripts);
|
|
2121
|
+
return;
|
|
1609
2122
|
}
|
|
1610
|
-
|
|
1611
|
-
|
|
2123
|
+
for (const script of scripts) {
|
|
2124
|
+
process6.stdout.write(`${script.scriptId} ${script.url}
|
|
2125
|
+
`);
|
|
1612
2126
|
}
|
|
1613
|
-
return base;
|
|
1614
2127
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
2128
|
+
|
|
2129
|
+
// src/cli/commands/log.ts
|
|
2130
|
+
import process8 from "process";
|
|
2131
|
+
|
|
2132
|
+
// src/logpoint/stream.ts
|
|
2133
|
+
init_types();
|
|
2134
|
+
|
|
2135
|
+
// src/logpoint/condition.ts
|
|
2136
|
+
import { randomBytes } from "crypto";
|
|
2137
|
+
var SENTINEL_PREFIX = "__CFI_LOG_";
|
|
2138
|
+
var SENTINEL_SUFFIX = "__";
|
|
2139
|
+
var HITS_GLOBAL2 = "globalThis.__CFI_LOG_HITS";
|
|
2140
|
+
function buildLoggingIife(sentinel, expression) {
|
|
2141
|
+
return [
|
|
2142
|
+
"(function(){",
|
|
2143
|
+
`var s=${JSON.stringify(sentinel)};`,
|
|
2144
|
+
"try{",
|
|
2145
|
+
`var v=(${expression});`,
|
|
2146
|
+
"var r=typeof v==='string'?v:JSON.stringify(v);",
|
|
2147
|
+
"console.log(s, r);",
|
|
2148
|
+
"}catch(e){",
|
|
2149
|
+
"console.log(s, '!err:'+(e&&e.message?e.message:String(e)));",
|
|
2150
|
+
"}",
|
|
2151
|
+
"return false;",
|
|
2152
|
+
"})()"
|
|
2153
|
+
].join("");
|
|
2154
|
+
}
|
|
2155
|
+
function buildHitGate(hitCount, counterKey) {
|
|
2156
|
+
const keyLiteral = JSON.stringify(counterKey);
|
|
2157
|
+
return [
|
|
2158
|
+
"(function(){",
|
|
2159
|
+
`var m=(${HITS_GLOBAL2}=${HITS_GLOBAL2}||{});`,
|
|
2160
|
+
`var k=${keyLiteral};`,
|
|
2161
|
+
"m[k]=(m[k]||0)+1;",
|
|
2162
|
+
`return m[k]>=${hitCount.toString()};`,
|
|
2163
|
+
"})()"
|
|
2164
|
+
].join("");
|
|
2165
|
+
}
|
|
2166
|
+
function combineGuards(guards) {
|
|
2167
|
+
const filtered = guards.filter((guard) => guard.length > 0);
|
|
2168
|
+
if (filtered.length === 0) {
|
|
2169
|
+
return void 0;
|
|
1627
2170
|
}
|
|
1628
|
-
if (
|
|
1629
|
-
return
|
|
2171
|
+
if (filtered.length === 1) {
|
|
2172
|
+
return filtered[0];
|
|
1630
2173
|
}
|
|
1631
|
-
|
|
1632
|
-
|
|
2174
|
+
return filtered.map((guard) => `(${guard})`).join("&&");
|
|
2175
|
+
}
|
|
2176
|
+
function buildLogpointCondition(sentinel, expression, options = {}) {
|
|
2177
|
+
const guards = [];
|
|
2178
|
+
if (options.hitCount !== void 0) {
|
|
2179
|
+
const counterKey = options.counterKey ?? sentinel;
|
|
2180
|
+
guards.push(buildHitGate(options.hitCount, counterKey));
|
|
1633
2181
|
}
|
|
1634
|
-
|
|
1635
|
-
|
|
2182
|
+
const userPredicate = options.predicate?.trim();
|
|
2183
|
+
if (userPredicate !== void 0 && userPredicate.length > 0) {
|
|
2184
|
+
guards.push(`(${userPredicate})`);
|
|
1636
2185
|
}
|
|
1637
|
-
|
|
1638
|
-
|
|
2186
|
+
const iife = buildLoggingIife(sentinel, expression);
|
|
2187
|
+
const guard = combineGuards(guards);
|
|
2188
|
+
if (guard === void 0) {
|
|
2189
|
+
return iife;
|
|
1639
2190
|
}
|
|
1640
|
-
return
|
|
2191
|
+
return `(${guard})?${iife}:false`;
|
|
1641
2192
|
}
|
|
1642
|
-
function
|
|
1643
|
-
return
|
|
1644
|
-
}
|
|
1645
|
-
async function captureProperties(session, objectId, limit, depth, maxValueLength) {
|
|
1646
|
-
const properties = await getProperties(session, objectId);
|
|
1647
|
-
const limited = properties.slice(0, limit);
|
|
1648
|
-
const variables = await Promise.all(
|
|
1649
|
-
limited.map(async (prop) => {
|
|
1650
|
-
return await captureProperty(session, prop, depth, maxValueLength);
|
|
1651
|
-
})
|
|
1652
|
-
);
|
|
1653
|
-
return variables;
|
|
2193
|
+
function generateSentinel() {
|
|
2194
|
+
return `${SENTINEL_PREFIX}${randomBytes(8).toString("hex")}${SENTINEL_SUFFIX}`;
|
|
1654
2195
|
}
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
const sanitizedValue = limitValueLength(described.value, maxValueLength);
|
|
1660
|
-
const base = { name, value: sanitizedValue };
|
|
1661
|
-
const withType = described.type === void 0 ? base : { ...base, type: described.type };
|
|
1662
|
-
return children === void 0 ? withType : { ...withType, children };
|
|
2196
|
+
|
|
2197
|
+
// src/logpoint/events.ts
|
|
2198
|
+
function asString3(value) {
|
|
2199
|
+
return typeof value === "string" ? value : void 0;
|
|
1663
2200
|
}
|
|
1664
|
-
|
|
1665
|
-
if (
|
|
2201
|
+
function readArg(arg, index) {
|
|
2202
|
+
if (typeof arg !== "object" || arg === null) {
|
|
1666
2203
|
return void 0;
|
|
1667
2204
|
}
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
);
|
|
1676
|
-
return nested.length > 0 ? nested : void 0;
|
|
1677
|
-
} catch {
|
|
1678
|
-
return void 0;
|
|
2205
|
+
const candidate = arg;
|
|
2206
|
+
if (candidate.type === "string" && typeof candidate.value === "string") {
|
|
2207
|
+
return candidate.value;
|
|
2208
|
+
}
|
|
2209
|
+
const isPrimitiveType = candidate.type === "number" || candidate.type === "boolean" || candidate.type === "bigint";
|
|
2210
|
+
const isPrimitiveValue = typeof candidate.value === "number" || typeof candidate.value === "boolean" || typeof candidate.value === "bigint";
|
|
2211
|
+
if (isPrimitiveType && isPrimitiveValue) {
|
|
2212
|
+
return String(candidate.value);
|
|
1679
2213
|
}
|
|
2214
|
+
return index === 0 ? void 0 : "";
|
|
1680
2215
|
}
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
function objectIdFromEvalResult(result) {
|
|
1684
|
-
const inner = result.result;
|
|
1685
|
-
if (inner?.type !== "object") {
|
|
2216
|
+
function parseLogEvent(rawArgs, sentinel, location, timestamp) {
|
|
2217
|
+
if (!Array.isArray(rawArgs) || rawArgs.length < 2) {
|
|
1686
2218
|
return void 0;
|
|
1687
2219
|
}
|
|
1688
|
-
const
|
|
1689
|
-
if (
|
|
2220
|
+
const tag = readArg(rawArgs[0], 0);
|
|
2221
|
+
if (tag !== sentinel) {
|
|
1690
2222
|
return void 0;
|
|
1691
2223
|
}
|
|
1692
|
-
|
|
2224
|
+
const payload = readArg(rawArgs[1], 1) ?? "";
|
|
2225
|
+
const ts = new Date(typeof timestamp === "number" ? timestamp : Date.now()).toISOString();
|
|
2226
|
+
const at = `${location.file}:${location.line.toString()}`;
|
|
2227
|
+
if (payload.startsWith("!err:")) {
|
|
2228
|
+
return { ts, at, error: payload.slice("!err:".length) };
|
|
2229
|
+
}
|
|
2230
|
+
return parsePayload(ts, at, payload);
|
|
1693
2231
|
}
|
|
1694
|
-
|
|
2232
|
+
function parsePayload(ts, at, payload) {
|
|
1695
2233
|
try {
|
|
1696
|
-
const
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
MAX_SCOPE_VARIABLES,
|
|
1700
|
-
MAX_VARIABLE_DEPTH,
|
|
1701
|
-
maxValueLength
|
|
1702
|
-
);
|
|
1703
|
-
const structured = {};
|
|
1704
|
-
for (const variable of properties) {
|
|
1705
|
-
structured[variable.name] = toStructuredValue(variable);
|
|
2234
|
+
const parsed = JSON.parse(payload);
|
|
2235
|
+
if (typeof parsed === "string") {
|
|
2236
|
+
return { ts, at, value: parsed };
|
|
1706
2237
|
}
|
|
1707
|
-
return JSON.stringify(
|
|
2238
|
+
return { ts, at, value: JSON.stringify(parsed) };
|
|
1708
2239
|
} catch {
|
|
1709
|
-
return
|
|
2240
|
+
return { ts, at, value: payload, raw: payload };
|
|
1710
2241
|
}
|
|
1711
2242
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
2243
|
+
|
|
2244
|
+
// src/logpoint/stream.ts
|
|
2245
|
+
function validateMaxEvents(maxEvents) {
|
|
2246
|
+
if (maxEvents === void 0) {
|
|
1714
2247
|
return void 0;
|
|
1715
2248
|
}
|
|
1716
|
-
if (
|
|
1717
|
-
|
|
2249
|
+
if (!Number.isInteger(maxEvents) || maxEvents <= 0) {
|
|
2250
|
+
throw new CfInspectorError(
|
|
2251
|
+
"INVALID_ARGUMENT",
|
|
2252
|
+
`maxEvents must be a positive integer, received: ${maxEvents.toString()}`
|
|
2253
|
+
);
|
|
1718
2254
|
}
|
|
1719
|
-
return
|
|
2255
|
+
return maxEvents;
|
|
1720
2256
|
}
|
|
1721
|
-
|
|
1722
|
-
if (
|
|
1723
|
-
return
|
|
1724
|
-
}
|
|
1725
|
-
const objectId = objectIdFromEvalResult(evalResult);
|
|
1726
|
-
if (objectId === void 0) {
|
|
1727
|
-
return captured;
|
|
1728
|
-
}
|
|
1729
|
-
const rendered = await renderObjectCapture(session, objectId, maxValueLength);
|
|
1730
|
-
if (rendered === void 0) {
|
|
1731
|
-
return captured;
|
|
2257
|
+
function validateHitCount2(hitCount) {
|
|
2258
|
+
if (hitCount === void 0) {
|
|
2259
|
+
return void 0;
|
|
1732
2260
|
}
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
2261
|
+
if (!Number.isInteger(hitCount) || hitCount <= 0) {
|
|
2262
|
+
throw new CfInspectorError(
|
|
2263
|
+
"INVALID_HIT_COUNT",
|
|
2264
|
+
`hitCount must be a positive integer, received: ${hitCount.toString()}`
|
|
2265
|
+
);
|
|
1736
2266
|
}
|
|
1737
|
-
|
|
1738
|
-
return captured.type === void 0 ? { expression, value } : { expression, value, type: captured.type };
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
// src/snapshot/scopes.ts
|
|
1742
|
-
var MAX_SCOPES = 3;
|
|
1743
|
-
var PRIORITY_BY_TYPE = {
|
|
1744
|
-
local: 0,
|
|
1745
|
-
arguments: 1,
|
|
1746
|
-
block: 2,
|
|
1747
|
-
closure: 3,
|
|
1748
|
-
catch: 4,
|
|
1749
|
-
with: 5,
|
|
1750
|
-
module: 6,
|
|
1751
|
-
script: 7
|
|
1752
|
-
};
|
|
1753
|
-
function selectScopes(scopeChain) {
|
|
1754
|
-
const eligible = scopeChain.filter((scope) => scope.objectId !== void 0 && scope.type !== "global");
|
|
1755
|
-
return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type)).slice(0, MAX_SCOPES);
|
|
1756
|
-
}
|
|
1757
|
-
function priorityOf(type) {
|
|
1758
|
-
return PRIORITY_BY_TYPE[type] ?? Number.MAX_SAFE_INTEGER;
|
|
1759
|
-
}
|
|
1760
|
-
async function captureScopes(session, frame, maxValueLength) {
|
|
1761
|
-
const scopes = selectScopes(frame.scopeChain);
|
|
1762
|
-
return await Promise.all(
|
|
1763
|
-
scopes.map(async (scope) => {
|
|
1764
|
-
const objectId = scope.objectId;
|
|
1765
|
-
if (objectId === void 0) {
|
|
1766
|
-
return { type: scope.type, variables: [] };
|
|
1767
|
-
}
|
|
1768
|
-
try {
|
|
1769
|
-
const variables = await captureProperties(
|
|
1770
|
-
session,
|
|
1771
|
-
objectId,
|
|
1772
|
-
MAX_SCOPE_VARIABLES,
|
|
1773
|
-
MAX_VARIABLE_DEPTH,
|
|
1774
|
-
maxValueLength
|
|
1775
|
-
);
|
|
1776
|
-
return { type: scope.type, variables };
|
|
1777
|
-
} catch {
|
|
1778
|
-
return { type: scope.type, variables: [] };
|
|
1779
|
-
}
|
|
1780
|
-
})
|
|
1781
|
-
);
|
|
2267
|
+
return hitCount;
|
|
1782
2268
|
}
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
const
|
|
1787
|
-
const
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
if (options.includeScopes === true) {
|
|
1798
|
-
const scopes = await captureScopes(session, top, maxValueLength);
|
|
1799
|
-
topFrame = { ...topFrame, scopes };
|
|
2269
|
+
async function streamLogpoint(session, options) {
|
|
2270
|
+
const maxEvents = validateMaxEvents(options.maxEvents);
|
|
2271
|
+
const hitCount = validateHitCount2(options.hitCount);
|
|
2272
|
+
const sentinel = generateSentinel();
|
|
2273
|
+
const condition = buildLogpointCondition(sentinel, options.expression, {
|
|
2274
|
+
...options.condition === void 0 ? {} : { predicate: options.condition },
|
|
2275
|
+
...hitCount === void 0 ? {} : { hitCount }
|
|
2276
|
+
});
|
|
2277
|
+
let emitted = 0;
|
|
2278
|
+
let maxEventsReached = false;
|
|
2279
|
+
let stopMaxEvents;
|
|
2280
|
+
const offEvent = session.client.on("Runtime.consoleAPICalled", (raw) => {
|
|
2281
|
+
if (maxEventsReached) {
|
|
2282
|
+
return;
|
|
1800
2283
|
}
|
|
1801
|
-
|
|
2284
|
+
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
2285
|
+
if (event === void 0) {
|
|
2286
|
+
return;
|
|
2287
|
+
}
|
|
2288
|
+
emitted += 1;
|
|
2289
|
+
try {
|
|
2290
|
+
options.onEvent(event);
|
|
2291
|
+
} catch {
|
|
2292
|
+
}
|
|
2293
|
+
if (maxEvents !== void 0 && emitted >= maxEvents) {
|
|
2294
|
+
maxEventsReached = true;
|
|
2295
|
+
stopMaxEvents?.();
|
|
2296
|
+
}
|
|
2297
|
+
});
|
|
2298
|
+
let handle;
|
|
2299
|
+
try {
|
|
2300
|
+
handle = await setBreakpoint(session, {
|
|
2301
|
+
file: options.location.file,
|
|
2302
|
+
line: options.location.line,
|
|
2303
|
+
...options.remoteRoot === void 0 ? {} : { remoteRoot: options.remoteRoot },
|
|
2304
|
+
condition
|
|
2305
|
+
});
|
|
2306
|
+
} catch (err) {
|
|
2307
|
+
offEvent();
|
|
2308
|
+
throw err;
|
|
1802
2309
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
}
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
2310
|
+
options.onBreakpointSet?.(handle);
|
|
2311
|
+
try {
|
|
2312
|
+
const reason = await waitForStop(session, options, (signal) => {
|
|
2313
|
+
stopMaxEvents = signal;
|
|
2314
|
+
if (maxEventsReached) {
|
|
2315
|
+
signal();
|
|
2316
|
+
}
|
|
2317
|
+
});
|
|
2318
|
+
return { handle, sentinel, emitted, stoppedReason: reason };
|
|
2319
|
+
} finally {
|
|
2320
|
+
offEvent();
|
|
2321
|
+
await removeBreakpointBestEffort(session, handle.breakpointId);
|
|
1814
2322
|
}
|
|
1815
|
-
return await Promise.all(
|
|
1816
|
-
captures.map(async (expression) => {
|
|
1817
|
-
return await captureExpression(session, callFrameId, expression, maxValueLength);
|
|
1818
|
-
})
|
|
1819
|
-
);
|
|
1820
2323
|
}
|
|
1821
|
-
|
|
2324
|
+
function toLogpointEvent(raw, sentinel, location) {
|
|
2325
|
+
const params = raw;
|
|
2326
|
+
if (asString3(params.type) !== "log") {
|
|
2327
|
+
return void 0;
|
|
2328
|
+
}
|
|
2329
|
+
const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
|
|
2330
|
+
return parseLogEvent(params.args, sentinel, location, ts);
|
|
2331
|
+
}
|
|
2332
|
+
async function removeBreakpointBestEffort(session, breakpointId) {
|
|
1822
2333
|
try {
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
return await withSerializedObjectCapture(session, expression, result, captured, maxValueLength);
|
|
1826
|
-
} catch (err) {
|
|
1827
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
1828
|
-
return { expression, error: limitValueLength(message, maxValueLength) };
|
|
2334
|
+
await removeBreakpoint(session, breakpointId);
|
|
2335
|
+
} catch {
|
|
1829
2336
|
}
|
|
1830
2337
|
}
|
|
2338
|
+
async function waitForStop(session, options, registerMaxEventsSignal) {
|
|
2339
|
+
return await new Promise((resolve) => {
|
|
2340
|
+
let settled = false;
|
|
2341
|
+
const finish = (reason) => {
|
|
2342
|
+
if (settled) {
|
|
2343
|
+
return;
|
|
2344
|
+
}
|
|
2345
|
+
settled = true;
|
|
2346
|
+
cleanup();
|
|
2347
|
+
resolve(reason);
|
|
2348
|
+
};
|
|
2349
|
+
const timer = options.durationMs === void 0 ? void 0 : setTimeout(() => {
|
|
2350
|
+
finish("duration");
|
|
2351
|
+
}, options.durationMs);
|
|
2352
|
+
const offClose = session.client.onClose(() => {
|
|
2353
|
+
finish("transport-closed");
|
|
2354
|
+
});
|
|
2355
|
+
const onAbort = () => {
|
|
2356
|
+
finish("signal");
|
|
2357
|
+
};
|
|
2358
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
2359
|
+
if (options.signal?.aborted === true) {
|
|
2360
|
+
finish("signal");
|
|
2361
|
+
}
|
|
2362
|
+
registerMaxEventsSignal(() => {
|
|
2363
|
+
finish("max-events");
|
|
2364
|
+
});
|
|
2365
|
+
function cleanup() {
|
|
2366
|
+
if (timer !== void 0) {
|
|
2367
|
+
clearTimeout(timer);
|
|
2368
|
+
}
|
|
2369
|
+
offClose();
|
|
2370
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
2371
|
+
}
|
|
2372
|
+
});
|
|
2373
|
+
}
|
|
1831
2374
|
|
|
1832
|
-
// src/cli/commands/
|
|
2375
|
+
// src/cli/commands/log.ts
|
|
1833
2376
|
init_types();
|
|
1834
2377
|
|
|
1835
|
-
// src/cli/
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
}
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
if (state.escaped) {
|
|
1850
|
-
state.escaped = false;
|
|
1851
|
-
return true;
|
|
1852
|
-
}
|
|
1853
|
-
if (char === "\\") {
|
|
1854
|
-
state.escaped = true;
|
|
1855
|
-
return true;
|
|
1856
|
-
}
|
|
1857
|
-
if (char === state.quote) {
|
|
1858
|
-
state.quote = void 0;
|
|
1859
|
-
}
|
|
1860
|
-
return true;
|
|
1861
|
-
}
|
|
1862
|
-
function updateCaptureDepth(state, char) {
|
|
1863
|
-
if (char === "(") {
|
|
1864
|
-
state.parenDepth += 1;
|
|
1865
|
-
} else if (char === ")") {
|
|
1866
|
-
state.parenDepth = Math.max(0, state.parenDepth - 1);
|
|
1867
|
-
} else if (char === "[") {
|
|
1868
|
-
state.bracketDepth += 1;
|
|
1869
|
-
} else if (char === "]") {
|
|
1870
|
-
state.bracketDepth = Math.max(0, state.bracketDepth - 1);
|
|
1871
|
-
} else if (char === "{") {
|
|
1872
|
-
state.braceDepth += 1;
|
|
1873
|
-
} else if (char === "}") {
|
|
1874
|
-
state.braceDepth = Math.max(0, state.braceDepth - 1);
|
|
2378
|
+
// src/cli/signals.ts
|
|
2379
|
+
import process7 from "process";
|
|
2380
|
+
async function withTerminationSignal(fn) {
|
|
2381
|
+
const abort = new AbortController();
|
|
2382
|
+
const onSignal = () => {
|
|
2383
|
+
abort.abort();
|
|
2384
|
+
};
|
|
2385
|
+
process7.once("SIGINT", onSignal);
|
|
2386
|
+
process7.once("SIGTERM", onSignal);
|
|
2387
|
+
try {
|
|
2388
|
+
return await fn(abort.signal);
|
|
2389
|
+
} finally {
|
|
2390
|
+
process7.off("SIGINT", onSignal);
|
|
2391
|
+
process7.off("SIGTERM", onSignal);
|
|
1875
2392
|
}
|
|
1876
2393
|
}
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
const
|
|
1882
|
-
|
|
1883
|
-
|
|
2394
|
+
|
|
2395
|
+
// src/cli/commands/log.ts
|
|
2396
|
+
async function handleLog(opts) {
|
|
2397
|
+
const target = resolveTarget(opts);
|
|
2398
|
+
const location = parseBreakpointSpec(opts.at);
|
|
2399
|
+
const remoteRoot = parseRemoteRoot(opts.remoteRoot);
|
|
2400
|
+
const durationSec = parsePositiveInt(opts.duration, "--duration");
|
|
2401
|
+
const maxEvents = parsePositiveInt(opts.maxEvents, "--max-events");
|
|
2402
|
+
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
2403
|
+
const expression = opts.expr.trim();
|
|
2404
|
+
if (expression.length === 0) {
|
|
2405
|
+
throw new CfInspectorError("INVALID_EXPRESSION", "--expr must not be empty");
|
|
1884
2406
|
}
|
|
2407
|
+
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2408
|
+
await withTerminationSignal(async (signal) => {
|
|
2409
|
+
await withSession(target, async (session) => {
|
|
2410
|
+
await validateExpression(session, expression);
|
|
2411
|
+
if (condition !== void 0) {
|
|
2412
|
+
await validateExpression(session, condition);
|
|
2413
|
+
}
|
|
2414
|
+
const result = await streamLogpoint(session, {
|
|
2415
|
+
location,
|
|
2416
|
+
expression,
|
|
2417
|
+
remoteRoot,
|
|
2418
|
+
...durationSec === void 0 ? {} : { durationMs: durationSec * 1e3 },
|
|
2419
|
+
...maxEvents === void 0 ? {} : { maxEvents },
|
|
2420
|
+
...hitCount === void 0 ? {} : { hitCount },
|
|
2421
|
+
...condition === void 0 ? {} : { condition },
|
|
2422
|
+
signal,
|
|
2423
|
+
onEvent: (event) => {
|
|
2424
|
+
writeLogEvent(event, opts.json);
|
|
2425
|
+
},
|
|
2426
|
+
onBreakpointSet: (handle) => {
|
|
2427
|
+
warnOnUnboundBreakpoints([handle]);
|
|
2428
|
+
}
|
|
2429
|
+
});
|
|
2430
|
+
writeLogSummary(result.stoppedReason, result.emitted, opts.json);
|
|
2431
|
+
});
|
|
2432
|
+
});
|
|
1885
2433
|
}
|
|
1886
|
-
function
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
braceDepth: 0,
|
|
1892
|
-
quote: void 0,
|
|
1893
|
-
start: 0,
|
|
1894
|
-
pieces: []
|
|
1895
|
-
};
|
|
1896
|
-
for (let idx = 0; idx < raw.length; idx += 1) {
|
|
1897
|
-
const char = raw.charAt(idx);
|
|
1898
|
-
if (consumeQuotedChar(state, char)) {
|
|
1899
|
-
continue;
|
|
1900
|
-
}
|
|
1901
|
-
if (isQuoteChar(char)) {
|
|
1902
|
-
state.quote = char;
|
|
1903
|
-
continue;
|
|
1904
|
-
}
|
|
1905
|
-
updateCaptureDepth(state, char);
|
|
1906
|
-
if (char === "," && isTopLevel(state)) {
|
|
1907
|
-
appendCapturePiece(raw, state, idx);
|
|
1908
|
-
state.start = idx + 1;
|
|
1909
|
-
}
|
|
2434
|
+
function writeLogSummary(stoppedReason, emitted, json) {
|
|
2435
|
+
if (json) {
|
|
2436
|
+
process8.stderr.write(`${JSON.stringify({ stopped: stoppedReason, emitted })}
|
|
2437
|
+
`);
|
|
2438
|
+
return;
|
|
1910
2439
|
}
|
|
1911
|
-
|
|
1912
|
-
|
|
2440
|
+
process8.stderr.write(
|
|
2441
|
+
`Stopped (${stoppedReason}); emitted ${emitted.toString()} log ${emitted === 1 ? "entry" : "entries"}.
|
|
2442
|
+
`
|
|
2443
|
+
);
|
|
1913
2444
|
}
|
|
1914
2445
|
|
|
1915
2446
|
// src/cli/commands/snapshot.ts
|
|
2447
|
+
import { performance as performance4 } from "perf_hooks";
|
|
2448
|
+
import process9 from "process";
|
|
2449
|
+
init_types();
|
|
1916
2450
|
async function handleSnapshot(opts) {
|
|
1917
2451
|
const prepared = prepareSnapshotCommand(opts);
|
|
1918
2452
|
const result = await runSnapshotCommand(prepared, opts);
|
|
@@ -1933,6 +2467,8 @@ function prepareSnapshotCommand(opts) {
|
|
|
1933
2467
|
const timeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_BREAKPOINT_TIMEOUT_SEC;
|
|
1934
2468
|
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
1935
2469
|
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2470
|
+
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
2471
|
+
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
1936
2472
|
return {
|
|
1937
2473
|
target,
|
|
1938
2474
|
breakpoints: opts.bp.map((spec) => parseBreakpointSpec(spec)),
|
|
@@ -1940,7 +2476,10 @@ function prepareSnapshotCommand(opts) {
|
|
|
1940
2476
|
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
1941
2477
|
timeoutMs: timeoutSec * 1e3,
|
|
1942
2478
|
...condition === void 0 ? {} : { condition },
|
|
1943
|
-
...maxValueLength === void 0 ? {} : { maxValueLength }
|
|
2479
|
+
...maxValueLength === void 0 ? {} : { maxValueLength },
|
|
2480
|
+
...hitCount === void 0 ? {} : { hitCount },
|
|
2481
|
+
...stackDepth === void 0 ? {} : { stackDepth },
|
|
2482
|
+
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
1944
2483
|
};
|
|
1945
2484
|
}
|
|
1946
2485
|
async function runSnapshotCommand(command, opts) {
|
|
@@ -1954,17 +2493,20 @@ async function runSnapshotCommand(command, opts) {
|
|
|
1954
2493
|
file: bp.file,
|
|
1955
2494
|
line: bp.line,
|
|
1956
2495
|
remoteRoot: command.remoteRoot,
|
|
1957
|
-
...command.condition === void 0 ? {} : { condition: command.condition }
|
|
2496
|
+
...command.condition === void 0 ? {} : { condition: command.condition },
|
|
2497
|
+
...command.hitCount === void 0 ? {} : { hitCount: command.hitCount }
|
|
1958
2498
|
})
|
|
1959
2499
|
)
|
|
1960
2500
|
);
|
|
1961
2501
|
warnOnUnboundBreakpoints(handles);
|
|
1962
2502
|
const pause = await waitForCommandPause(session, opts, handles, command.timeoutMs);
|
|
1963
|
-
const pausedStartedAt = pause.receivedAtMs ??
|
|
2503
|
+
const pausedStartedAt = pause.receivedAtMs ?? performance4.now();
|
|
1964
2504
|
const snapshot = await captureSnapshot(session, pause, {
|
|
1965
2505
|
captures: command.captures,
|
|
1966
2506
|
includeScopes: opts.includeScopes === true,
|
|
1967
|
-
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength }
|
|
2507
|
+
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength },
|
|
2508
|
+
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
2509
|
+
stackCaptures: command.stackCaptures
|
|
1968
2510
|
});
|
|
1969
2511
|
if (opts.keepPaused === true) {
|
|
1970
2512
|
return withPausedDuration(snapshot, null);
|
|
@@ -1990,47 +2532,285 @@ async function waitForCommandPause(session, opts, handles, timeoutMs) {
|
|
|
1990
2532
|
async function resumeAfterSnapshot(session, snapshot, pausedStartedAt) {
|
|
1991
2533
|
try {
|
|
1992
2534
|
await resume(session);
|
|
1993
|
-
return withPausedDuration(snapshot, roundDurationMs(
|
|
2535
|
+
return withPausedDuration(snapshot, roundDurationMs(performance4.now() - pausedStartedAt));
|
|
1994
2536
|
} catch {
|
|
1995
|
-
|
|
2537
|
+
process9.stderr.write(
|
|
1996
2538
|
"[cf-inspector] warning: Debugger.resume failed after snapshot; pausedDurationMs is unknown.\n"
|
|
1997
2539
|
);
|
|
1998
2540
|
return withPausedDuration(snapshot, null);
|
|
1999
2541
|
}
|
|
2000
2542
|
}
|
|
2001
2543
|
|
|
2544
|
+
// src/cli/commands/watch.ts
|
|
2545
|
+
import { performance as performance5 } from "perf_hooks";
|
|
2546
|
+
import process10 from "process";
|
|
2547
|
+
init_types();
|
|
2548
|
+
async function handleWatch(opts) {
|
|
2549
|
+
const prepared = prepareWatchCommand(opts);
|
|
2550
|
+
let stoppedReason = "signal";
|
|
2551
|
+
let emitted = 0;
|
|
2552
|
+
await withTerminationSignal(async (signal) => {
|
|
2553
|
+
await withSession(prepared.target, async (session) => {
|
|
2554
|
+
const result = await runWatchLoop(session, prepared, opts, signal);
|
|
2555
|
+
stoppedReason = result.stoppedReason;
|
|
2556
|
+
emitted = result.emitted;
|
|
2557
|
+
});
|
|
2558
|
+
});
|
|
2559
|
+
writeWatchSummary(stoppedReason, emitted, opts.json);
|
|
2560
|
+
}
|
|
2561
|
+
function prepareWatchCommand(opts) {
|
|
2562
|
+
const target = resolveTarget(opts);
|
|
2563
|
+
if (opts.bp.length === 0) {
|
|
2564
|
+
throw new CfInspectorError(
|
|
2565
|
+
"INVALID_BREAKPOINT",
|
|
2566
|
+
"At least one --bp <file:line> is required."
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2569
|
+
const perHitTimeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_BREAKPOINT_TIMEOUT_SEC;
|
|
2570
|
+
const durationSec = parsePositiveInt(opts.duration, "--duration");
|
|
2571
|
+
const maxEvents = parsePositiveInt(opts.maxEvents, "--max-events");
|
|
2572
|
+
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
2573
|
+
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
2574
|
+
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
2575
|
+
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2576
|
+
return {
|
|
2577
|
+
target,
|
|
2578
|
+
breakpoints: opts.bp.map((spec) => parseBreakpointSpec(spec)),
|
|
2579
|
+
captures: parseCaptureList(opts.capture),
|
|
2580
|
+
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
2581
|
+
perHitTimeoutMs: perHitTimeoutSec * 1e3,
|
|
2582
|
+
...durationSec === void 0 ? {} : { durationMs: durationSec * 1e3 },
|
|
2583
|
+
...maxEvents === void 0 ? {} : { maxEvents },
|
|
2584
|
+
...maxValueLength === void 0 ? {} : { maxValueLength },
|
|
2585
|
+
...condition === void 0 ? {} : { condition },
|
|
2586
|
+
...hitCount === void 0 ? {} : { hitCount },
|
|
2587
|
+
...stackDepth === void 0 ? {} : { stackDepth },
|
|
2588
|
+
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
2589
|
+
};
|
|
2590
|
+
}
|
|
2591
|
+
async function runWatchLoop(session, command, opts, signal) {
|
|
2592
|
+
if (command.condition !== void 0) {
|
|
2593
|
+
await validateExpression(session, command.condition);
|
|
2594
|
+
}
|
|
2595
|
+
const handles = await Promise.all(
|
|
2596
|
+
command.breakpoints.map(
|
|
2597
|
+
(bp) => setBreakpoint(session, {
|
|
2598
|
+
file: bp.file,
|
|
2599
|
+
line: bp.line,
|
|
2600
|
+
remoteRoot: command.remoteRoot,
|
|
2601
|
+
...command.condition === void 0 ? {} : { condition: command.condition },
|
|
2602
|
+
...command.hitCount === void 0 ? {} : { hitCount: command.hitCount }
|
|
2603
|
+
})
|
|
2604
|
+
)
|
|
2605
|
+
);
|
|
2606
|
+
warnOnUnboundBreakpoints(handles);
|
|
2607
|
+
const deadline = computeDeadline(command.durationMs);
|
|
2608
|
+
let emitted = 0;
|
|
2609
|
+
const state = { stopped: false, reason: "signal" };
|
|
2610
|
+
const setStop = (reason) => {
|
|
2611
|
+
if (state.stopped) {
|
|
2612
|
+
return;
|
|
2613
|
+
}
|
|
2614
|
+
state.reason = reason;
|
|
2615
|
+
state.stopped = true;
|
|
2616
|
+
};
|
|
2617
|
+
const transportClosed = waitForTransportClose(session);
|
|
2618
|
+
transportClosed.promise.then(() => {
|
|
2619
|
+
setStop("transport-closed");
|
|
2620
|
+
}).catch(() => {
|
|
2621
|
+
});
|
|
2622
|
+
try {
|
|
2623
|
+
while (!state.stopped) {
|
|
2624
|
+
if (signal.aborted) {
|
|
2625
|
+
setStop("signal");
|
|
2626
|
+
break;
|
|
2627
|
+
}
|
|
2628
|
+
const remainingMs = remainingForLoop(deadline, command.perHitTimeoutMs);
|
|
2629
|
+
if (remainingMs <= 0) {
|
|
2630
|
+
setStop("duration");
|
|
2631
|
+
break;
|
|
2632
|
+
}
|
|
2633
|
+
const pause = await waitForNextWatchPause(session, handles, remainingMs, signal);
|
|
2634
|
+
if (pause === "signal") {
|
|
2635
|
+
setStop("signal");
|
|
2636
|
+
break;
|
|
2637
|
+
}
|
|
2638
|
+
if (pause === "timeout") {
|
|
2639
|
+
if (deadline !== void 0 && performance5.now() >= deadline) {
|
|
2640
|
+
setStop("duration");
|
|
2641
|
+
break;
|
|
2642
|
+
}
|
|
2643
|
+
continue;
|
|
2644
|
+
}
|
|
2645
|
+
const event = await captureWatchEvent(session, command, pause, emitted + 1, opts);
|
|
2646
|
+
emitted += 1;
|
|
2647
|
+
writeWatchEvent(event, opts.json);
|
|
2648
|
+
try {
|
|
2649
|
+
await resume(session);
|
|
2650
|
+
} catch {
|
|
2651
|
+
process10.stderr.write("[cf-inspector] warning: Debugger.resume failed during watch.\n");
|
|
2652
|
+
setStop("transport-closed");
|
|
2653
|
+
break;
|
|
2654
|
+
}
|
|
2655
|
+
if (command.maxEvents !== void 0 && emitted >= command.maxEvents) {
|
|
2656
|
+
setStop("max-events");
|
|
2657
|
+
break;
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
} finally {
|
|
2661
|
+
transportClosed.cancel();
|
|
2662
|
+
}
|
|
2663
|
+
return { emitted, stoppedReason: state.reason };
|
|
2664
|
+
}
|
|
2665
|
+
function computeDeadline(durationMs) {
|
|
2666
|
+
if (durationMs === void 0) {
|
|
2667
|
+
return void 0;
|
|
2668
|
+
}
|
|
2669
|
+
return performance5.now() + durationMs;
|
|
2670
|
+
}
|
|
2671
|
+
function remainingForLoop(deadline, perHitTimeoutMs) {
|
|
2672
|
+
if (deadline === void 0) {
|
|
2673
|
+
return perHitTimeoutMs;
|
|
2674
|
+
}
|
|
2675
|
+
const remaining = deadline - performance5.now();
|
|
2676
|
+
if (remaining <= 0) {
|
|
2677
|
+
return 0;
|
|
2678
|
+
}
|
|
2679
|
+
return Math.min(remaining, perHitTimeoutMs);
|
|
2680
|
+
}
|
|
2681
|
+
function waitForTransportClose(session) {
|
|
2682
|
+
let cancelled = false;
|
|
2683
|
+
let resolve;
|
|
2684
|
+
const promise = new Promise((res) => {
|
|
2685
|
+
resolve = res;
|
|
2686
|
+
});
|
|
2687
|
+
const off = session.client.onClose(() => {
|
|
2688
|
+
if (!cancelled) {
|
|
2689
|
+
resolve?.();
|
|
2690
|
+
}
|
|
2691
|
+
});
|
|
2692
|
+
return {
|
|
2693
|
+
promise,
|
|
2694
|
+
cancel: () => {
|
|
2695
|
+
cancelled = true;
|
|
2696
|
+
off();
|
|
2697
|
+
resolve?.();
|
|
2698
|
+
}
|
|
2699
|
+
};
|
|
2700
|
+
}
|
|
2701
|
+
async function waitForNextWatchPause(session, handles, timeoutMs, signal) {
|
|
2702
|
+
if (signal.aborted) {
|
|
2703
|
+
return "signal";
|
|
2704
|
+
}
|
|
2705
|
+
try {
|
|
2706
|
+
return await waitForPause(session, {
|
|
2707
|
+
timeoutMs,
|
|
2708
|
+
breakpointIds: handles.map((h) => h.breakpointId),
|
|
2709
|
+
unmatchedPausePolicy: "wait-for-resume"
|
|
2710
|
+
});
|
|
2711
|
+
} catch (err) {
|
|
2712
|
+
if (err instanceof CfInspectorError) {
|
|
2713
|
+
if (err.code === "BREAKPOINT_NOT_HIT") {
|
|
2714
|
+
return "timeout";
|
|
2715
|
+
}
|
|
2716
|
+
if (err.code === "UNRELATED_PAUSE_TIMEOUT") {
|
|
2717
|
+
return "timeout";
|
|
2718
|
+
}
|
|
2719
|
+
}
|
|
2720
|
+
throw err;
|
|
2721
|
+
}
|
|
2722
|
+
}
|
|
2723
|
+
async function captureWatchEvent(session, command, pause, hit, opts) {
|
|
2724
|
+
const snapshot = await captureSnapshot(session, pause, {
|
|
2725
|
+
captures: command.captures,
|
|
2726
|
+
includeScopes: opts.includeScopes === true,
|
|
2727
|
+
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength },
|
|
2728
|
+
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
2729
|
+
stackCaptures: command.stackCaptures
|
|
2730
|
+
});
|
|
2731
|
+
const at = formatLocation(command, snapshot.topFrame);
|
|
2732
|
+
const base = {
|
|
2733
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2734
|
+
at,
|
|
2735
|
+
hit,
|
|
2736
|
+
reason: snapshot.reason,
|
|
2737
|
+
hitBreakpoints: snapshot.hitBreakpoints,
|
|
2738
|
+
captures: snapshot.captures
|
|
2739
|
+
};
|
|
2740
|
+
const withFrame = snapshot.topFrame === void 0 ? base : { ...base, topFrame: snapshot.topFrame };
|
|
2741
|
+
const withStack = snapshot.stack === void 0 ? withFrame : { ...withFrame, stack: snapshot.stack };
|
|
2742
|
+
return snapshot.exception === void 0 ? withStack : { ...withStack, exception: snapshot.exception };
|
|
2743
|
+
}
|
|
2744
|
+
function formatLocation(command, topFrame) {
|
|
2745
|
+
if (topFrame?.url !== void 0) {
|
|
2746
|
+
return `${topFrame.url}:${topFrame.line.toString()}`;
|
|
2747
|
+
}
|
|
2748
|
+
const first = command.breakpoints[0];
|
|
2749
|
+
if (first === void 0) {
|
|
2750
|
+
return "(unknown)";
|
|
2751
|
+
}
|
|
2752
|
+
return `${first.file}:${first.line.toString()}`;
|
|
2753
|
+
}
|
|
2754
|
+
function writeWatchSummary(reason, emitted, json) {
|
|
2755
|
+
if (json) {
|
|
2756
|
+
process10.stderr.write(`${JSON.stringify({ stopped: reason, emitted })}
|
|
2757
|
+
`);
|
|
2758
|
+
return;
|
|
2759
|
+
}
|
|
2760
|
+
process10.stderr.write(
|
|
2761
|
+
`Stopped (${reason}); emitted ${emitted.toString()} watch ${emitted === 1 ? "event" : "events"}.
|
|
2762
|
+
`
|
|
2763
|
+
);
|
|
2764
|
+
}
|
|
2765
|
+
|
|
2002
2766
|
// src/cli/program.ts
|
|
2003
2767
|
function applyTargetOptions(cmd) {
|
|
2004
2768
|
return 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 (e.g. eu10)").option("--org <name>", "CF org name").option("--space <name>", "CF space name").option("--app <name>", "CF app name").option("--cf-timeout <seconds>", "Timeout for CF tunnel readiness in seconds");
|
|
2005
2769
|
}
|
|
2770
|
+
var collectStrings = (value, prev = []) => [
|
|
2771
|
+
...prev,
|
|
2772
|
+
value
|
|
2773
|
+
];
|
|
2006
2774
|
async function main(argv) {
|
|
2007
2775
|
const program = new Command();
|
|
2008
2776
|
program.name("cf-inspector").description("Drive a Node.js inspector from the command line \u2014 set breakpoints, capture snapshots, evaluate expressions");
|
|
2009
2777
|
registerSnapshot(program);
|
|
2010
2778
|
registerLog(program);
|
|
2779
|
+
registerWatch(program);
|
|
2780
|
+
registerException(program);
|
|
2011
2781
|
registerEval(program);
|
|
2012
2782
|
registerListScripts(program);
|
|
2013
2783
|
registerAttach(program);
|
|
2014
2784
|
await program.parseAsync([...argv]);
|
|
2015
2785
|
}
|
|
2016
2786
|
function registerSnapshot(program) {
|
|
2017
|
-
const collectStrings = (value, prev = []) => [
|
|
2018
|
-
...prev,
|
|
2019
|
-
value
|
|
2020
|
-
];
|
|
2021
2787
|
applyTargetOptions(
|
|
2022
2788
|
program.command("snapshot").description("Set a breakpoint, wait for it to hit, capture expressions, and resume")
|
|
2023
|
-
).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("--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: 4096)").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("--include-scopes", "Include expanded paused-frame scopes in the snapshot").option("--no-json", "Print a human-readable summary instead of JSON").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) => {
|
|
2789
|
+
).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("--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: 4096)").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("--include-scopes", "Include expanded paused-frame scopes in the snapshot").option("--no-json", "Print a human-readable summary instead of JSON").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) => {
|
|
2024
2790
|
await handleSnapshot(opts);
|
|
2025
2791
|
});
|
|
2026
2792
|
}
|
|
2027
2793
|
function registerLog(program) {
|
|
2028
2794
|
applyTargetOptions(
|
|
2029
2795
|
program.command("log").description("Stream a non-pausing logpoint: log an expression each time a line executes")
|
|
2030
|
-
).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("--no-json", "Print human-readable lines instead of JSON Lines").action(async (opts) => {
|
|
2796
|
+
).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) => {
|
|
2031
2797
|
await handleLog(opts);
|
|
2032
2798
|
});
|
|
2033
2799
|
}
|
|
2800
|
+
function registerWatch(program) {
|
|
2801
|
+
applyTargetOptions(
|
|
2802
|
+
program.command("watch").description("Stream a snapshot per breakpoint hit (multi-shot watch); resume between hits")
|
|
2803
|
+
).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("--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) => {
|
|
2804
|
+
await handleWatch(opts);
|
|
2805
|
+
});
|
|
2806
|
+
}
|
|
2807
|
+
function registerException(program) {
|
|
2808
|
+
applyTargetOptions(
|
|
2809
|
+
program.command("exception").description("Pause on a thrown exception, capture the value and frame, then resume")
|
|
2810
|
+
).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: 4096)").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("--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) => {
|
|
2811
|
+
await handleException(opts);
|
|
2812
|
+
});
|
|
2813
|
+
}
|
|
2034
2814
|
function registerEval(program) {
|
|
2035
2815
|
applyTargetOptions(
|
|
2036
2816
|
program.command("eval").description("Evaluate an expression against the global Runtime")
|
|
@@ -2056,20 +2836,20 @@ function registerAttach(program) {
|
|
|
2056
2836
|
// src/cli.ts
|
|
2057
2837
|
init_types();
|
|
2058
2838
|
try {
|
|
2059
|
-
await main(
|
|
2839
|
+
await main(process11.argv);
|
|
2060
2840
|
} catch (err) {
|
|
2061
2841
|
if (err instanceof CfInspectorError) {
|
|
2062
|
-
|
|
2842
|
+
process11.stderr.write(`Error [${err.code}]: ${err.message}
|
|
2063
2843
|
`);
|
|
2064
2844
|
if (err.detail !== void 0) {
|
|
2065
|
-
|
|
2845
|
+
process11.stderr.write(` detail: ${err.detail}
|
|
2066
2846
|
`);
|
|
2067
2847
|
}
|
|
2068
|
-
|
|
2848
|
+
process11.exit(1);
|
|
2069
2849
|
}
|
|
2070
2850
|
const message = err instanceof Error ? err.message : String(err);
|
|
2071
|
-
|
|
2851
|
+
process11.stderr.write(`Error: ${message}
|
|
2072
2852
|
`);
|
|
2073
|
-
|
|
2853
|
+
process11.exit(1);
|
|
2074
2854
|
}
|
|
2075
2855
|
//# sourceMappingURL=cli.js.map
|