@saptools/cf-inspector 0.3.18 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -23
- package/dist/cli.js +1124 -421
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +50 -4
- package/dist/index.js +343 -24
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -110,7 +110,7 @@ var init_wsTransport = __esm({
|
|
|
110
110
|
});
|
|
111
111
|
|
|
112
112
|
// src/cli.ts
|
|
113
|
-
import
|
|
113
|
+
import process10 from "process";
|
|
114
114
|
|
|
115
115
|
// src/cli/program.ts
|
|
116
116
|
import { Command } from "commander";
|
|
@@ -250,8 +250,11 @@ function writeHumanSnapshot(snapshot) {
|
|
|
250
250
|
` reason: ${snapshot.reason}`,
|
|
251
251
|
` paused: ${pausedDuration}`
|
|
252
252
|
);
|
|
253
|
+
if (snapshot.exception !== void 0) {
|
|
254
|
+
appendExceptionLines(lines, snapshot.exception);
|
|
255
|
+
}
|
|
253
256
|
if (snapshot.topFrame) {
|
|
254
|
-
appendFrameLines(lines, snapshot);
|
|
257
|
+
appendFrameLines(lines, snapshot.topFrame);
|
|
255
258
|
}
|
|
256
259
|
if (snapshot.captures.length > 0) {
|
|
257
260
|
lines.push(" captures:");
|
|
@@ -260,14 +263,16 @@ function writeHumanSnapshot(snapshot) {
|
|
|
260
263
|
lines.push(` ${capture.expression} = ${detail}`);
|
|
261
264
|
}
|
|
262
265
|
}
|
|
266
|
+
if (snapshot.stack !== void 0 && snapshot.stack.length > 0) {
|
|
267
|
+
lines.push(" stack:");
|
|
268
|
+
for (const frame of snapshot.stack) {
|
|
269
|
+
appendStackFrameLine(lines, frame);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
263
272
|
process.stdout.write(`${lines.join("\n")}
|
|
264
273
|
`);
|
|
265
274
|
}
|
|
266
|
-
function appendFrameLines(lines,
|
|
267
|
-
const frame = snapshot.topFrame;
|
|
268
|
-
if (frame === void 0) {
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
275
|
+
function appendFrameLines(lines, frame) {
|
|
271
276
|
const fnName = frame.functionName.length === 0 ? "(anonymous)" : frame.functionName;
|
|
272
277
|
const sourceUrl = frame.url !== void 0 && frame.url.length > 0 ? frame.url : "(unknown)";
|
|
273
278
|
lines.push(
|
|
@@ -283,6 +288,25 @@ function appendFrameLines(lines, snapshot) {
|
|
|
283
288
|
}
|
|
284
289
|
}
|
|
285
290
|
}
|
|
291
|
+
function appendStackFrameLine(lines, frame) {
|
|
292
|
+
const fnName = frame.functionName.length === 0 ? "(anonymous)" : frame.functionName;
|
|
293
|
+
const sourceUrl = frame.url !== void 0 && frame.url.length > 0 ? frame.url : "(unknown)";
|
|
294
|
+
lines.push(` ${fnName} ${sourceUrl}:${frame.line.toString()}:${frame.column.toString()}`);
|
|
295
|
+
if (frame.captures !== void 0) {
|
|
296
|
+
for (const capture of frame.captures) {
|
|
297
|
+
const detail = capture.error ?? capture.value ?? "undefined";
|
|
298
|
+
lines.push(` ${capture.expression} = ${detail}`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function appendExceptionLines(lines, exception) {
|
|
303
|
+
if (exception.error !== void 0) {
|
|
304
|
+
lines.push(` exception: !err ${exception.error}`);
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
const detail = exception.description ?? exception.value ?? "(unknown)";
|
|
308
|
+
lines.push(` exception: ${detail}`);
|
|
309
|
+
}
|
|
286
310
|
function writeLogEvent(event, json) {
|
|
287
311
|
if (json) {
|
|
288
312
|
process.stdout.write(`${JSON.stringify(event)}
|
|
@@ -297,6 +321,25 @@ function writeLogEvent(event, json) {
|
|
|
297
321
|
process.stdout.write(`[${event.ts}] ${event.at} ${event.value ?? ""}
|
|
298
322
|
`);
|
|
299
323
|
}
|
|
324
|
+
function writeWatchEvent(event, json) {
|
|
325
|
+
if (json) {
|
|
326
|
+
process.stdout.write(`${JSON.stringify(event)}
|
|
327
|
+
`);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
process.stdout.write(`[${event.ts}] hit#${event.hit.toString()} ${event.at}
|
|
331
|
+
`);
|
|
332
|
+
if (event.exception !== void 0) {
|
|
333
|
+
const detail = event.exception.description ?? event.exception.value ?? event.exception.error ?? "(unknown)";
|
|
334
|
+
process.stdout.write(` exception: ${detail}
|
|
335
|
+
`);
|
|
336
|
+
}
|
|
337
|
+
for (const capture of event.captures) {
|
|
338
|
+
const detail = capture.error ?? capture.value ?? "undefined";
|
|
339
|
+
process.stdout.write(` ${capture.expression} = ${detail}
|
|
340
|
+
`);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
300
343
|
|
|
301
344
|
// src/cf/tunnel.ts
|
|
302
345
|
import { startDebugger } from "@saptools/cf-debugger";
|
|
@@ -557,6 +600,9 @@ function asString(value, fallback = "") {
|
|
|
557
600
|
function asNumber(value, fallback = 0) {
|
|
558
601
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
559
602
|
}
|
|
603
|
+
function nonEmptyString(value) {
|
|
604
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
605
|
+
}
|
|
560
606
|
function toResolvedLocations(value) {
|
|
561
607
|
if (!Array.isArray(value)) {
|
|
562
608
|
return [];
|
|
@@ -595,7 +641,18 @@ function toScopeChain(value) {
|
|
|
595
641
|
return [objectId === void 0 ? base : { ...base, objectId }];
|
|
596
642
|
});
|
|
597
643
|
}
|
|
598
|
-
function
|
|
644
|
+
function resolveCallFrameUrl(frame, scripts) {
|
|
645
|
+
const direct = nonEmptyString(frame.url);
|
|
646
|
+
if (direct !== void 0) {
|
|
647
|
+
return direct;
|
|
648
|
+
}
|
|
649
|
+
const scriptId = nonEmptyString(frame.location?.scriptId);
|
|
650
|
+
if (scriptId === void 0) {
|
|
651
|
+
return void 0;
|
|
652
|
+
}
|
|
653
|
+
return nonEmptyString(scripts?.get(scriptId)?.url);
|
|
654
|
+
}
|
|
655
|
+
function toCallFrames(value, scripts) {
|
|
599
656
|
if (!Array.isArray(value)) {
|
|
600
657
|
return [];
|
|
601
658
|
}
|
|
@@ -608,7 +665,7 @@ function toCallFrames(value) {
|
|
|
608
665
|
if (callFrameId.length === 0) {
|
|
609
666
|
return [];
|
|
610
667
|
}
|
|
611
|
-
const url =
|
|
668
|
+
const url = resolveCallFrameUrl(candidate, scripts);
|
|
612
669
|
const base = {
|
|
613
670
|
callFrameId,
|
|
614
671
|
functionName: asString(candidate.functionName),
|
|
@@ -619,13 +676,14 @@ function toCallFrames(value) {
|
|
|
619
676
|
return [url === void 0 ? base : { ...base, url }];
|
|
620
677
|
});
|
|
621
678
|
}
|
|
622
|
-
function toPauseEvent(params, receivedAtMs) {
|
|
623
|
-
|
|
679
|
+
function toPauseEvent(params, receivedAtMs, scripts) {
|
|
680
|
+
const base = {
|
|
624
681
|
reason: asString(params.reason),
|
|
625
682
|
hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
|
|
626
|
-
callFrames: toCallFrames(params.callFrames),
|
|
683
|
+
callFrames: toCallFrames(params.callFrames, scripts),
|
|
627
684
|
receivedAtMs
|
|
628
685
|
};
|
|
686
|
+
return params.data === void 0 ? base : { ...base, data: params.data };
|
|
629
687
|
}
|
|
630
688
|
function topFrameLocation(pause) {
|
|
631
689
|
const top = pause.callFrames[0];
|
|
@@ -677,7 +735,7 @@ async function connectInspector(options) {
|
|
|
677
735
|
return;
|
|
678
736
|
}
|
|
679
737
|
const params = raw;
|
|
680
|
-
const event = toPauseEvent(params, performance.now());
|
|
738
|
+
const event = toPauseEvent(params, performance.now(), scripts);
|
|
681
739
|
if (pauseBuffer.length >= PAUSE_BUFFER_LIMIT) {
|
|
682
740
|
pauseBuffer.shift();
|
|
683
741
|
}
|
|
@@ -711,6 +769,7 @@ init_types();
|
|
|
711
769
|
// src/cli/commandTypes.ts
|
|
712
770
|
var DEFAULT_BREAKPOINT_TIMEOUT_SEC = 30;
|
|
713
771
|
var DEFAULT_CF_TIMEOUT_SEC = 60;
|
|
772
|
+
var DEFAULT_EXCEPTION_TIMEOUT_SEC = 30;
|
|
714
773
|
|
|
715
774
|
// src/cli/target.ts
|
|
716
775
|
function parsePositiveInt(raw, label) {
|
|
@@ -813,6 +872,9 @@ init_types();
|
|
|
813
872
|
async function resume(session) {
|
|
814
873
|
await session.client.send("Debugger.resume");
|
|
815
874
|
}
|
|
875
|
+
async function setPauseOnExceptions(session, state) {
|
|
876
|
+
await session.client.send("Debugger.setPauseOnExceptions", { state });
|
|
877
|
+
}
|
|
816
878
|
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
817
879
|
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
818
880
|
callFrameId,
|
|
@@ -900,23 +962,9 @@ function writeHumanEvalResult(result) {
|
|
|
900
962
|
`);
|
|
901
963
|
}
|
|
902
964
|
|
|
903
|
-
// src/cli/commands/
|
|
904
|
-
import
|
|
905
|
-
|
|
906
|
-
const target = resolveTarget(opts);
|
|
907
|
-
const scripts = await withSession(target, (session) => Promise.resolve(listScripts(session)));
|
|
908
|
-
if (opts.json) {
|
|
909
|
-
writeJson(scripts);
|
|
910
|
-
return;
|
|
911
|
-
}
|
|
912
|
-
for (const script of scripts) {
|
|
913
|
-
process4.stdout.write(`${script.scriptId} ${script.url}
|
|
914
|
-
`);
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
// src/cli/commands/log.ts
|
|
919
|
-
import process6 from "process";
|
|
965
|
+
// src/cli/commands/exception.ts
|
|
966
|
+
import { performance as performance3 } from "perf_hooks";
|
|
967
|
+
import process5 from "process";
|
|
920
968
|
|
|
921
969
|
// src/pathMapper.ts
|
|
922
970
|
init_types();
|
|
@@ -1059,6 +1107,46 @@ function buildBreakpointUrlRegex(input) {
|
|
|
1059
1107
|
|
|
1060
1108
|
// src/inspector/breakpoints.ts
|
|
1061
1109
|
init_types();
|
|
1110
|
+
var HITS_GLOBAL = "globalThis.__CFI_HITS";
|
|
1111
|
+
var counterKeyCounter = 0;
|
|
1112
|
+
function nextCounterKey(file, line) {
|
|
1113
|
+
counterKeyCounter += 1;
|
|
1114
|
+
return `${file}:${line.toString()}:${counterKeyCounter.toString()}`;
|
|
1115
|
+
}
|
|
1116
|
+
function validateHitCount(hitCount) {
|
|
1117
|
+
if (hitCount === void 0) {
|
|
1118
|
+
return void 0;
|
|
1119
|
+
}
|
|
1120
|
+
if (!Number.isInteger(hitCount) || hitCount <= 0) {
|
|
1121
|
+
throw new CfInspectorError(
|
|
1122
|
+
"INVALID_HIT_COUNT",
|
|
1123
|
+
`hitCount must be a positive integer, received: ${hitCount.toString()}`
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
return hitCount;
|
|
1127
|
+
}
|
|
1128
|
+
function buildHitCountedCondition(hitCount, counterKey, userCondition) {
|
|
1129
|
+
const keyLiteral = JSON.stringify(counterKey);
|
|
1130
|
+
const baseCondition = userCondition !== void 0 && userCondition.trim().length > 0 ? `(${userCondition})` : "true";
|
|
1131
|
+
return [
|
|
1132
|
+
"(function(){",
|
|
1133
|
+
`var m=(${HITS_GLOBAL}=${HITS_GLOBAL}||{});`,
|
|
1134
|
+
`var k=${keyLiteral};`,
|
|
1135
|
+
"m[k]=(m[k]||0)+1;",
|
|
1136
|
+
`if(m[k]<${hitCount.toString()})return false;`,
|
|
1137
|
+
`return ${baseCondition};`,
|
|
1138
|
+
"})()"
|
|
1139
|
+
].join("");
|
|
1140
|
+
}
|
|
1141
|
+
function resolveCondition(input) {
|
|
1142
|
+
const condition = input.condition;
|
|
1143
|
+
const hitCount = validateHitCount(input.hitCount);
|
|
1144
|
+
if (hitCount === void 0) {
|
|
1145
|
+
return condition !== void 0 && condition.length > 0 ? condition : void 0;
|
|
1146
|
+
}
|
|
1147
|
+
const counterKey = nextCounterKey(input.file, input.line);
|
|
1148
|
+
return buildHitCountedCondition(hitCount, counterKey, condition);
|
|
1149
|
+
}
|
|
1062
1150
|
async function setBreakpoint(session, input) {
|
|
1063
1151
|
const remoteRoot = input.remoteRoot ?? { kind: "none" };
|
|
1064
1152
|
const urlRegex = buildBreakpointUrlRegex({ file: input.file, remoteRoot });
|
|
@@ -1066,8 +1154,9 @@ async function setBreakpoint(session, input) {
|
|
|
1066
1154
|
lineNumber: input.line - 1,
|
|
1067
1155
|
urlRegex
|
|
1068
1156
|
};
|
|
1069
|
-
|
|
1070
|
-
|
|
1157
|
+
const condition = resolveCondition(input);
|
|
1158
|
+
if (condition !== void 0) {
|
|
1159
|
+
params["condition"] = condition;
|
|
1071
1160
|
}
|
|
1072
1161
|
const result = await session.client.send(
|
|
1073
1162
|
"Debugger.setBreakpointByUrl",
|
|
@@ -1092,378 +1181,130 @@ async function removeBreakpoint(session, breakpointId) {
|
|
|
1092
1181
|
await session.client.send("Debugger.removeBreakpoint", { breakpointId });
|
|
1093
1182
|
}
|
|
1094
1183
|
|
|
1095
|
-
// src/
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
"console.log(s, r);",
|
|
1107
|
-
"}catch(e){",
|
|
1108
|
-
"console.log(s, '!err:'+(e&&e.message?e.message:String(e)));",
|
|
1109
|
-
"}",
|
|
1110
|
-
"return false;",
|
|
1111
|
-
"})()"
|
|
1112
|
-
].join("");
|
|
1184
|
+
// src/inspector/pause.ts
|
|
1185
|
+
init_types();
|
|
1186
|
+
import { performance as performance2 } from "perf_hooks";
|
|
1187
|
+
function pauseMatches(pause, breakpointIds, pauseReasons) {
|
|
1188
|
+
if (pauseReasons !== void 0 && pauseReasons.length > 0) {
|
|
1189
|
+
return pauseReasons.includes(pause.reason);
|
|
1190
|
+
}
|
|
1191
|
+
if (breakpointIds === void 0 || breakpointIds.length === 0) {
|
|
1192
|
+
return true;
|
|
1193
|
+
}
|
|
1194
|
+
return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
|
|
1113
1195
|
}
|
|
1114
|
-
function
|
|
1115
|
-
return
|
|
1196
|
+
function remainingUntil(deadlineMs) {
|
|
1197
|
+
return Math.max(0, deadlineMs - performance2.now());
|
|
1116
1198
|
}
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
return
|
|
1199
|
+
function hasResumedSincePause(session, pause) {
|
|
1200
|
+
const pauseAt = pause.receivedAtMs;
|
|
1201
|
+
const resumedAt = session.debuggerState.lastResumedAtMs;
|
|
1202
|
+
return pauseAt !== void 0 && resumedAt !== void 0 && resumedAt >= pauseAt;
|
|
1121
1203
|
}
|
|
1122
|
-
function
|
|
1123
|
-
|
|
1124
|
-
|
|
1204
|
+
function throwBreakpointTimeout(timeoutMs) {
|
|
1205
|
+
throw new CfInspectorError(
|
|
1206
|
+
"BREAKPOINT_NOT_HIT",
|
|
1207
|
+
`Timed out waiting for matching Debugger.paused after ${timeoutMs.toString()}ms`
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
function throwUnrelatedPauseTimeout(pause, timeoutMs) {
|
|
1211
|
+
throw new CfInspectorError(
|
|
1212
|
+
"UNRELATED_PAUSE_TIMEOUT",
|
|
1213
|
+
`Target stayed paused by another debugger event before this command's breakpoint could hit within ${timeoutMs.toString()}ms`,
|
|
1214
|
+
pauseDetail(pause)
|
|
1215
|
+
);
|
|
1216
|
+
}
|
|
1217
|
+
async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, timeoutMs) {
|
|
1218
|
+
if (hasResumedSincePause(session, pause)) {
|
|
1219
|
+
return;
|
|
1125
1220
|
}
|
|
1126
|
-
const
|
|
1127
|
-
if (
|
|
1128
|
-
|
|
1221
|
+
const remainingMs = remainingUntil(deadlineMs);
|
|
1222
|
+
if (remainingMs <= 0) {
|
|
1223
|
+
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1129
1224
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1225
|
+
try {
|
|
1226
|
+
await session.client.waitFor("Debugger.resumed", { timeoutMs: remainingMs });
|
|
1227
|
+
session.debuggerState.lastResumedAtMs = performance2.now();
|
|
1228
|
+
} catch (err) {
|
|
1229
|
+
if (err instanceof CfInspectorError && err.code === "BREAKPOINT_NOT_HIT") {
|
|
1230
|
+
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1231
|
+
}
|
|
1232
|
+
throw err;
|
|
1134
1233
|
}
|
|
1135
|
-
return index === 0 ? void 0 : "";
|
|
1136
1234
|
}
|
|
1137
|
-
function
|
|
1138
|
-
if (
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1235
|
+
async function handleUnmatchedPause(session, pause, options, deadlineMs) {
|
|
1236
|
+
if (options.unmatchedPausePolicy === "fail") {
|
|
1237
|
+
throw new CfInspectorError(
|
|
1238
|
+
"UNRELATED_PAUSE",
|
|
1239
|
+
"Target paused before this command's breakpoint was reached",
|
|
1240
|
+
pauseDetail(pause)
|
|
1241
|
+
);
|
|
1144
1242
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
const at = `${location.file}:${location.line.toString()}`;
|
|
1148
|
-
if (payload.startsWith("!err:")) {
|
|
1149
|
-
return { ts, at, error: payload.slice("!err:".length) };
|
|
1243
|
+
if (hasResumedSincePause(session, pause)) {
|
|
1244
|
+
return;
|
|
1150
1245
|
}
|
|
1151
|
-
|
|
1246
|
+
options.onUnmatchedPause?.(pause);
|
|
1247
|
+
await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
|
|
1152
1248
|
}
|
|
1153
|
-
function
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1249
|
+
async function waitForPause(session, options) {
|
|
1250
|
+
const deadlineMs = performance2.now() + options.timeoutMs;
|
|
1251
|
+
const buffer = session.pauseBuffer;
|
|
1252
|
+
while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
|
|
1253
|
+
while (buffer.length > 0) {
|
|
1254
|
+
const buffered = buffer.shift();
|
|
1255
|
+
if (buffered === void 0) {
|
|
1256
|
+
continue;
|
|
1257
|
+
}
|
|
1258
|
+
if (pauseMatches(buffered, options.breakpointIds, options.pauseReasons)) {
|
|
1259
|
+
return buffered;
|
|
1260
|
+
}
|
|
1261
|
+
await handleUnmatchedPause(session, buffered, options, deadlineMs);
|
|
1158
1262
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1263
|
+
const pause = await waitForLivePause(session, options, deadlineMs);
|
|
1264
|
+
if (pauseMatches(pause, options.breakpointIds, options.pauseReasons)) {
|
|
1265
|
+
return pause;
|
|
1266
|
+
}
|
|
1267
|
+
await handleUnmatchedPause(session, pause, options, deadlineMs);
|
|
1162
1268
|
}
|
|
1269
|
+
throwBreakpointTimeout(options.timeoutMs);
|
|
1163
1270
|
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
const condition = buildLogpointCondition(sentinel, options.expression);
|
|
1169
|
-
let emitted = 0;
|
|
1170
|
-
const offEvent = session.client.on("Runtime.consoleAPICalled", (raw) => {
|
|
1171
|
-
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
1172
|
-
if (event === void 0) {
|
|
1173
|
-
return;
|
|
1174
|
-
}
|
|
1175
|
-
emitted += 1;
|
|
1176
|
-
options.onEvent(event);
|
|
1177
|
-
});
|
|
1178
|
-
let handle;
|
|
1179
|
-
try {
|
|
1180
|
-
handle = await setBreakpoint(session, {
|
|
1181
|
-
file: options.location.file,
|
|
1182
|
-
line: options.location.line,
|
|
1183
|
-
...options.remoteRoot === void 0 ? {} : { remoteRoot: options.remoteRoot },
|
|
1184
|
-
condition
|
|
1185
|
-
});
|
|
1186
|
-
} catch (err) {
|
|
1187
|
-
offEvent();
|
|
1188
|
-
throw err;
|
|
1271
|
+
async function waitForLivePause(session, options, deadlineMs) {
|
|
1272
|
+
const remainingMs = remainingUntil(deadlineMs);
|
|
1273
|
+
if (remainingMs <= 0) {
|
|
1274
|
+
throwBreakpointTimeout(options.timeoutMs);
|
|
1189
1275
|
}
|
|
1190
|
-
|
|
1276
|
+
session.pauseWaitGate.active = true;
|
|
1277
|
+
let receivedAtMs;
|
|
1278
|
+
let params;
|
|
1191
1279
|
try {
|
|
1192
|
-
|
|
1193
|
-
|
|
1280
|
+
params = await session.client.waitFor("Debugger.paused", {
|
|
1281
|
+
timeoutMs: remainingMs,
|
|
1282
|
+
predicate: () => {
|
|
1283
|
+
receivedAtMs = performance2.now();
|
|
1284
|
+
return true;
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1194
1287
|
} finally {
|
|
1195
|
-
|
|
1196
|
-
await removeBreakpointBestEffort(session, handle.breakpointId);
|
|
1288
|
+
session.pauseWaitGate.active = false;
|
|
1197
1289
|
}
|
|
1290
|
+
return toPauseEvent(params, receivedAtMs ?? performance2.now(), session.scripts);
|
|
1198
1291
|
}
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
const
|
|
1205
|
-
return
|
|
1292
|
+
|
|
1293
|
+
// src/snapshot/values.ts
|
|
1294
|
+
init_types();
|
|
1295
|
+
var DEFAULT_MAX_VALUE_LENGTH = 4096;
|
|
1296
|
+
function isPrimitive(value) {
|
|
1297
|
+
const t = typeof value;
|
|
1298
|
+
return t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol";
|
|
1206
1299
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
} catch {
|
|
1300
|
+
function formatPrimitive(value) {
|
|
1301
|
+
if (typeof value === "symbol") {
|
|
1302
|
+
return value.toString();
|
|
1211
1303
|
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
const finish = (reason) => {
|
|
1217
|
-
if (settled) {
|
|
1218
|
-
return;
|
|
1219
|
-
}
|
|
1220
|
-
settled = true;
|
|
1221
|
-
cleanup();
|
|
1222
|
-
resolve(reason);
|
|
1223
|
-
};
|
|
1224
|
-
const timer = options.durationMs === void 0 ? void 0 : setTimeout(() => {
|
|
1225
|
-
finish("duration");
|
|
1226
|
-
}, options.durationMs);
|
|
1227
|
-
const offClose = session.client.onClose(() => {
|
|
1228
|
-
finish("transport-closed");
|
|
1229
|
-
});
|
|
1230
|
-
const onAbort = () => {
|
|
1231
|
-
finish("signal");
|
|
1232
|
-
};
|
|
1233
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
1234
|
-
if (options.signal?.aborted === true) {
|
|
1235
|
-
finish("signal");
|
|
1236
|
-
}
|
|
1237
|
-
function cleanup() {
|
|
1238
|
-
if (timer !== void 0) {
|
|
1239
|
-
clearTimeout(timer);
|
|
1240
|
-
}
|
|
1241
|
-
offClose();
|
|
1242
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
1243
|
-
}
|
|
1244
|
-
});
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
// src/cli/commands/log.ts
|
|
1248
|
-
init_types();
|
|
1249
|
-
|
|
1250
|
-
// src/cli/warnings.ts
|
|
1251
|
-
import process5 from "process";
|
|
1252
|
-
function warnOnUnboundBreakpoints(handles) {
|
|
1253
|
-
for (const handle of handles) {
|
|
1254
|
-
if (handle.resolvedLocations.length === 0) {
|
|
1255
|
-
process5.stderr.write(
|
|
1256
|
-
`[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.
|
|
1257
|
-
`
|
|
1258
|
-
);
|
|
1259
|
-
}
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
function roundDurationMs(durationMs) {
|
|
1263
|
-
return Math.round(durationMs * 1e3) / 1e3;
|
|
1264
|
-
}
|
|
1265
|
-
function warnOnUnmatchedPause(pause) {
|
|
1266
|
-
const reason = pause.reason.length > 0 ? pause.reason : "unknown";
|
|
1267
|
-
process5.stderr.write(
|
|
1268
|
-
`[cf-inspector] warning: target is paused by another debugger event (${reason} at ${formatPauseLocation(pause)}); waiting for it to resume...
|
|
1269
|
-
`
|
|
1270
|
-
);
|
|
1271
|
-
}
|
|
1272
|
-
function withPausedDuration(snapshot, pausedDurationMs) {
|
|
1273
|
-
return {
|
|
1274
|
-
reason: snapshot.reason,
|
|
1275
|
-
hitBreakpoints: snapshot.hitBreakpoints,
|
|
1276
|
-
capturedAt: snapshot.capturedAt,
|
|
1277
|
-
pausedDurationMs,
|
|
1278
|
-
...snapshot.topFrame === void 0 ? {} : { topFrame: snapshot.topFrame },
|
|
1279
|
-
captures: snapshot.captures
|
|
1280
|
-
};
|
|
1281
|
-
}
|
|
1282
|
-
function formatPauseLocation(pause) {
|
|
1283
|
-
const top = pause.callFrames[0];
|
|
1284
|
-
if (top === void 0) {
|
|
1285
|
-
return "(no call frame)";
|
|
1286
|
-
}
|
|
1287
|
-
const url = top.url !== void 0 && top.url.length > 0 ? top.url : "(unknown)";
|
|
1288
|
-
return `${url}:${(top.lineNumber + 1).toString()}:${(top.columnNumber + 1).toString()}`;
|
|
1289
|
-
}
|
|
1290
|
-
|
|
1291
|
-
// src/cli/commands/log.ts
|
|
1292
|
-
async function handleLog(opts) {
|
|
1293
|
-
const target = resolveTarget(opts);
|
|
1294
|
-
const location = parseBreakpointSpec(opts.at);
|
|
1295
|
-
const remoteRoot = parseRemoteRoot(opts.remoteRoot);
|
|
1296
|
-
const durationSec = parsePositiveInt(opts.duration, "--duration");
|
|
1297
|
-
const expression = opts.expr.trim();
|
|
1298
|
-
if (expression.length === 0) {
|
|
1299
|
-
throw new CfInspectorError("INVALID_BREAKPOINT", "--expr must not be empty");
|
|
1300
|
-
}
|
|
1301
|
-
const abort = new AbortController();
|
|
1302
|
-
const onSig = () => {
|
|
1303
|
-
abort.abort();
|
|
1304
|
-
};
|
|
1305
|
-
process6.once("SIGINT", onSig);
|
|
1306
|
-
process6.once("SIGTERM", onSig);
|
|
1307
|
-
try {
|
|
1308
|
-
await withSession(target, async (session) => {
|
|
1309
|
-
await validateExpression(session, expression);
|
|
1310
|
-
const result = await streamLogpoint(session, {
|
|
1311
|
-
location,
|
|
1312
|
-
expression,
|
|
1313
|
-
remoteRoot,
|
|
1314
|
-
...durationSec === void 0 ? {} : { durationMs: durationSec * 1e3 },
|
|
1315
|
-
signal: abort.signal,
|
|
1316
|
-
onEvent: (event) => {
|
|
1317
|
-
writeLogEvent(event, opts.json);
|
|
1318
|
-
},
|
|
1319
|
-
onBreakpointSet: (handle) => {
|
|
1320
|
-
warnOnUnboundBreakpoints([handle]);
|
|
1321
|
-
}
|
|
1322
|
-
});
|
|
1323
|
-
writeLogSummary(result.stoppedReason, result.emitted, opts.json);
|
|
1324
|
-
});
|
|
1325
|
-
} finally {
|
|
1326
|
-
process6.off("SIGINT", onSig);
|
|
1327
|
-
process6.off("SIGTERM", onSig);
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
function writeLogSummary(stoppedReason, emitted, json) {
|
|
1331
|
-
if (json) {
|
|
1332
|
-
process6.stderr.write(`${JSON.stringify({ stopped: stoppedReason, emitted })}
|
|
1333
|
-
`);
|
|
1334
|
-
return;
|
|
1335
|
-
}
|
|
1336
|
-
process6.stderr.write(
|
|
1337
|
-
`Stopped (${stoppedReason}); emitted ${emitted.toString()} log ${emitted === 1 ? "entry" : "entries"}.
|
|
1338
|
-
`
|
|
1339
|
-
);
|
|
1340
|
-
}
|
|
1341
|
-
|
|
1342
|
-
// src/cli/commands/snapshot.ts
|
|
1343
|
-
import { performance as performance3 } from "perf_hooks";
|
|
1344
|
-
import process7 from "process";
|
|
1345
|
-
|
|
1346
|
-
// src/inspector/pause.ts
|
|
1347
|
-
init_types();
|
|
1348
|
-
import { performance as performance2 } from "perf_hooks";
|
|
1349
|
-
function pauseMatches(pause, breakpointIds) {
|
|
1350
|
-
if (breakpointIds === void 0 || breakpointIds.length === 0) {
|
|
1351
|
-
return true;
|
|
1352
|
-
}
|
|
1353
|
-
return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
|
|
1354
|
-
}
|
|
1355
|
-
function remainingUntil(deadlineMs) {
|
|
1356
|
-
return Math.max(0, deadlineMs - performance2.now());
|
|
1357
|
-
}
|
|
1358
|
-
function hasResumedSincePause(session, pause) {
|
|
1359
|
-
const pauseAt = pause.receivedAtMs;
|
|
1360
|
-
const resumedAt = session.debuggerState.lastResumedAtMs;
|
|
1361
|
-
return pauseAt !== void 0 && resumedAt !== void 0 && resumedAt >= pauseAt;
|
|
1362
|
-
}
|
|
1363
|
-
function throwBreakpointTimeout(timeoutMs) {
|
|
1364
|
-
throw new CfInspectorError(
|
|
1365
|
-
"BREAKPOINT_NOT_HIT",
|
|
1366
|
-
`Timed out waiting for matching Debugger.paused after ${timeoutMs.toString()}ms`
|
|
1367
|
-
);
|
|
1368
|
-
}
|
|
1369
|
-
function throwUnrelatedPauseTimeout(pause, timeoutMs) {
|
|
1370
|
-
throw new CfInspectorError(
|
|
1371
|
-
"UNRELATED_PAUSE_TIMEOUT",
|
|
1372
|
-
`Target stayed paused by another debugger event before this command's breakpoint could hit within ${timeoutMs.toString()}ms`,
|
|
1373
|
-
pauseDetail(pause)
|
|
1374
|
-
);
|
|
1375
|
-
}
|
|
1376
|
-
async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, timeoutMs) {
|
|
1377
|
-
if (hasResumedSincePause(session, pause)) {
|
|
1378
|
-
return;
|
|
1379
|
-
}
|
|
1380
|
-
const remainingMs = remainingUntil(deadlineMs);
|
|
1381
|
-
if (remainingMs <= 0) {
|
|
1382
|
-
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1383
|
-
}
|
|
1384
|
-
try {
|
|
1385
|
-
await session.client.waitFor("Debugger.resumed", { timeoutMs: remainingMs });
|
|
1386
|
-
session.debuggerState.lastResumedAtMs = performance2.now();
|
|
1387
|
-
} catch (err) {
|
|
1388
|
-
if (err instanceof CfInspectorError && err.code === "BREAKPOINT_NOT_HIT") {
|
|
1389
|
-
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1390
|
-
}
|
|
1391
|
-
throw err;
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
async function handleUnmatchedPause(session, pause, options, deadlineMs) {
|
|
1395
|
-
if (options.unmatchedPausePolicy === "fail") {
|
|
1396
|
-
throw new CfInspectorError(
|
|
1397
|
-
"UNRELATED_PAUSE",
|
|
1398
|
-
"Target paused before this command's breakpoint was reached",
|
|
1399
|
-
pauseDetail(pause)
|
|
1400
|
-
);
|
|
1401
|
-
}
|
|
1402
|
-
if (hasResumedSincePause(session, pause)) {
|
|
1403
|
-
return;
|
|
1404
|
-
}
|
|
1405
|
-
options.onUnmatchedPause?.(pause);
|
|
1406
|
-
await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
|
|
1407
|
-
}
|
|
1408
|
-
async function waitForPause(session, options) {
|
|
1409
|
-
const deadlineMs = performance2.now() + options.timeoutMs;
|
|
1410
|
-
const buffer = session.pauseBuffer;
|
|
1411
|
-
while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
|
|
1412
|
-
while (buffer.length > 0) {
|
|
1413
|
-
const buffered = buffer.shift();
|
|
1414
|
-
if (buffered === void 0) {
|
|
1415
|
-
continue;
|
|
1416
|
-
}
|
|
1417
|
-
if (pauseMatches(buffered, options.breakpointIds)) {
|
|
1418
|
-
return buffered;
|
|
1419
|
-
}
|
|
1420
|
-
await handleUnmatchedPause(session, buffered, options, deadlineMs);
|
|
1421
|
-
}
|
|
1422
|
-
const pause = await waitForLivePause(session, options, deadlineMs);
|
|
1423
|
-
if (pauseMatches(pause, options.breakpointIds)) {
|
|
1424
|
-
return pause;
|
|
1425
|
-
}
|
|
1426
|
-
await handleUnmatchedPause(session, pause, options, deadlineMs);
|
|
1427
|
-
}
|
|
1428
|
-
throwBreakpointTimeout(options.timeoutMs);
|
|
1429
|
-
}
|
|
1430
|
-
async function waitForLivePause(session, options, deadlineMs) {
|
|
1431
|
-
const remainingMs = remainingUntil(deadlineMs);
|
|
1432
|
-
if (remainingMs <= 0) {
|
|
1433
|
-
throwBreakpointTimeout(options.timeoutMs);
|
|
1434
|
-
}
|
|
1435
|
-
session.pauseWaitGate.active = true;
|
|
1436
|
-
let receivedAtMs;
|
|
1437
|
-
let params;
|
|
1438
|
-
try {
|
|
1439
|
-
params = await session.client.waitFor("Debugger.paused", {
|
|
1440
|
-
timeoutMs: remainingMs,
|
|
1441
|
-
predicate: () => {
|
|
1442
|
-
receivedAtMs = performance2.now();
|
|
1443
|
-
return true;
|
|
1444
|
-
}
|
|
1445
|
-
});
|
|
1446
|
-
} finally {
|
|
1447
|
-
session.pauseWaitGate.active = false;
|
|
1448
|
-
}
|
|
1449
|
-
return toPauseEvent(params, receivedAtMs ?? performance2.now());
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
// src/snapshot/values.ts
|
|
1453
|
-
init_types();
|
|
1454
|
-
var DEFAULT_MAX_VALUE_LENGTH = 4096;
|
|
1455
|
-
function isPrimitive(value) {
|
|
1456
|
-
const t = typeof value;
|
|
1457
|
-
return t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol";
|
|
1458
|
-
}
|
|
1459
|
-
function formatPrimitive(value) {
|
|
1460
|
-
if (typeof value === "symbol") {
|
|
1461
|
-
return value.toString();
|
|
1462
|
-
}
|
|
1463
|
-
if (typeof value === "bigint") {
|
|
1464
|
-
return `${value.toString()}n`;
|
|
1465
|
-
}
|
|
1466
|
-
return String(value);
|
|
1304
|
+
if (typeof value === "bigint") {
|
|
1305
|
+
return `${value.toString()}n`;
|
|
1306
|
+
}
|
|
1307
|
+
return String(value);
|
|
1467
1308
|
}
|
|
1468
1309
|
function resolveMaxValueLength(value) {
|
|
1469
1310
|
if (value === void 0) {
|
|
@@ -1665,19 +1506,11 @@ async function capturePropertyChildren(session, described, depth, maxValueLength
|
|
|
1665
1506
|
}
|
|
1666
1507
|
}
|
|
1667
1508
|
|
|
1668
|
-
// src/snapshot/
|
|
1669
|
-
function
|
|
1670
|
-
|
|
1671
|
-
if (inner?.type !== "object") {
|
|
1672
|
-
return void 0;
|
|
1673
|
-
}
|
|
1674
|
-
const objectId = inner.objectId;
|
|
1675
|
-
if (typeof objectId !== "string" || objectId.length === 0) {
|
|
1676
|
-
return void 0;
|
|
1677
|
-
}
|
|
1678
|
-
return objectId;
|
|
1509
|
+
// src/snapshot/exception.ts
|
|
1510
|
+
function asString2(value) {
|
|
1511
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1679
1512
|
}
|
|
1680
|
-
async function
|
|
1513
|
+
async function materializeObject(session, objectId, maxValueLength) {
|
|
1681
1514
|
try {
|
|
1682
1515
|
const properties = await captureProperties(
|
|
1683
1516
|
session,
|
|
@@ -1686,6 +1519,9 @@ async function renderObjectCapture(session, objectId, maxValueLength) {
|
|
|
1686
1519
|
MAX_VARIABLE_DEPTH,
|
|
1687
1520
|
maxValueLength
|
|
1688
1521
|
);
|
|
1522
|
+
if (properties.length === 0) {
|
|
1523
|
+
return void 0;
|
|
1524
|
+
}
|
|
1689
1525
|
const structured = {};
|
|
1690
1526
|
for (const variable of properties) {
|
|
1691
1527
|
structured[variable.name] = toStructuredValue(variable);
|
|
@@ -1695,7 +1531,99 @@ async function renderObjectCapture(session, objectId, maxValueLength) {
|
|
|
1695
1531
|
return void 0;
|
|
1696
1532
|
}
|
|
1697
1533
|
}
|
|
1698
|
-
function
|
|
1534
|
+
async function readPropertyDescription(session, objectId, name) {
|
|
1535
|
+
try {
|
|
1536
|
+
const properties = await getProperties(session, objectId);
|
|
1537
|
+
for (const prop of properties) {
|
|
1538
|
+
if (prop.name !== name) {
|
|
1539
|
+
continue;
|
|
1540
|
+
}
|
|
1541
|
+
const value = prop.value;
|
|
1542
|
+
if (value === void 0) {
|
|
1543
|
+
continue;
|
|
1544
|
+
}
|
|
1545
|
+
if (typeof value.value === "string") {
|
|
1546
|
+
return value.value;
|
|
1547
|
+
}
|
|
1548
|
+
if (typeof value.description === "string") {
|
|
1549
|
+
return value.description;
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
return void 0;
|
|
1553
|
+
} catch {
|
|
1554
|
+
return void 0;
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
async function captureException(session, pause, maxValueLength) {
|
|
1558
|
+
if (pause.reason !== "exception" && pause.reason !== "promiseRejection") {
|
|
1559
|
+
return void 0;
|
|
1560
|
+
}
|
|
1561
|
+
const data = pause.data;
|
|
1562
|
+
if (typeof data !== "object" || data === null) {
|
|
1563
|
+
return { error: "no exception data attached" };
|
|
1564
|
+
}
|
|
1565
|
+
const candidate = data;
|
|
1566
|
+
const type = asString2(candidate.type);
|
|
1567
|
+
const description = asString2(candidate.description);
|
|
1568
|
+
if (typeof candidate.value === "string") {
|
|
1569
|
+
return buildResult(type, description, JSON.stringify(candidate.value), maxValueLength);
|
|
1570
|
+
}
|
|
1571
|
+
if (typeof candidate.value === "number" || typeof candidate.value === "boolean") {
|
|
1572
|
+
return buildResult(type, description, String(candidate.value), maxValueLength);
|
|
1573
|
+
}
|
|
1574
|
+
const objectId = asString2(candidate.objectId);
|
|
1575
|
+
if (objectId === void 0) {
|
|
1576
|
+
if (description !== void 0) {
|
|
1577
|
+
return buildResult(type, description, description, maxValueLength);
|
|
1578
|
+
}
|
|
1579
|
+
return { error: "exception data has no objectId or value" };
|
|
1580
|
+
}
|
|
1581
|
+
const message = await readPropertyDescription(session, objectId, "message");
|
|
1582
|
+
const rendered = await materializeObject(session, objectId, maxValueLength);
|
|
1583
|
+
if (rendered !== void 0) {
|
|
1584
|
+
const result = buildResult(type, description, rendered, maxValueLength);
|
|
1585
|
+
return message === void 0 ? result : { ...result, description: limitValueLength(message, maxValueLength) };
|
|
1586
|
+
}
|
|
1587
|
+
return buildResult(type, description, description ?? "[exception]", maxValueLength);
|
|
1588
|
+
}
|
|
1589
|
+
function buildResult(type, description, value, maxValueLength) {
|
|
1590
|
+
const safeValue = limitValueLength(value, maxValueLength);
|
|
1591
|
+
const base = { value: safeValue };
|
|
1592
|
+
const withType = type === void 0 ? base : { ...base, type };
|
|
1593
|
+
return description === void 0 ? withType : { ...withType, description: limitValueLength(description, maxValueLength) };
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// src/snapshot/objects.ts
|
|
1597
|
+
function objectIdFromEvalResult(result) {
|
|
1598
|
+
const inner = result.result;
|
|
1599
|
+
if (inner?.type !== "object") {
|
|
1600
|
+
return void 0;
|
|
1601
|
+
}
|
|
1602
|
+
const objectId = inner.objectId;
|
|
1603
|
+
if (typeof objectId !== "string" || objectId.length === 0) {
|
|
1604
|
+
return void 0;
|
|
1605
|
+
}
|
|
1606
|
+
return objectId;
|
|
1607
|
+
}
|
|
1608
|
+
async function renderObjectCapture(session, objectId, maxValueLength) {
|
|
1609
|
+
try {
|
|
1610
|
+
const properties = await captureProperties(
|
|
1611
|
+
session,
|
|
1612
|
+
objectId,
|
|
1613
|
+
MAX_SCOPE_VARIABLES,
|
|
1614
|
+
MAX_VARIABLE_DEPTH,
|
|
1615
|
+
maxValueLength
|
|
1616
|
+
);
|
|
1617
|
+
const structured = {};
|
|
1618
|
+
for (const variable of properties) {
|
|
1619
|
+
structured[variable.name] = toStructuredValue(variable);
|
|
1620
|
+
}
|
|
1621
|
+
return JSON.stringify(structured);
|
|
1622
|
+
} catch {
|
|
1623
|
+
return void 0;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
function normalizeRenderedObjectCapture(rendered, original) {
|
|
1699
1627
|
if (rendered === "{}" && original !== "Object") {
|
|
1700
1628
|
return void 0;
|
|
1701
1629
|
}
|
|
@@ -1767,12 +1695,73 @@ async function captureScopes(session, frame, maxValueLength) {
|
|
|
1767
1695
|
);
|
|
1768
1696
|
}
|
|
1769
1697
|
|
|
1698
|
+
// src/snapshot/stack.ts
|
|
1699
|
+
var DEFAULT_STACK_DEPTH = 1;
|
|
1700
|
+
var MAX_STACK_DEPTH = 64;
|
|
1701
|
+
function clampDepth(depth, frameCount) {
|
|
1702
|
+
if (depth <= 0) {
|
|
1703
|
+
return 0;
|
|
1704
|
+
}
|
|
1705
|
+
return Math.min(depth, frameCount, MAX_STACK_DEPTH);
|
|
1706
|
+
}
|
|
1707
|
+
function buildBaseFrame(frame) {
|
|
1708
|
+
const base = {
|
|
1709
|
+
functionName: frame.functionName,
|
|
1710
|
+
line: frame.lineNumber + 1,
|
|
1711
|
+
column: frame.columnNumber + 1
|
|
1712
|
+
};
|
|
1713
|
+
return frame.url === void 0 ? base : { ...base, url: frame.url };
|
|
1714
|
+
}
|
|
1715
|
+
async function captureFrameExpression(session, callFrameId, expression, maxValueLength) {
|
|
1716
|
+
try {
|
|
1717
|
+
const result = await evaluateOnFrame(session, callFrameId, expression);
|
|
1718
|
+
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1719
|
+
return await withSerializedObjectCapture(session, expression, result, captured, maxValueLength);
|
|
1720
|
+
} catch (err) {
|
|
1721
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1722
|
+
return { expression, error: limitValueLength(message, maxValueLength) };
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
async function captureFrameExpressions(session, frame, expressions, maxValueLength) {
|
|
1726
|
+
if (expressions.length === 0) {
|
|
1727
|
+
return [];
|
|
1728
|
+
}
|
|
1729
|
+
return await Promise.all(
|
|
1730
|
+
expressions.map(
|
|
1731
|
+
(expression) => captureFrameExpression(session, frame.callFrameId, expression, maxValueLength)
|
|
1732
|
+
)
|
|
1733
|
+
);
|
|
1734
|
+
}
|
|
1735
|
+
async function walkStack(session, callFrames, options) {
|
|
1736
|
+
const depth = clampDepth(options.stackDepth, callFrames.length);
|
|
1737
|
+
if (depth <= 1) {
|
|
1738
|
+
return [];
|
|
1739
|
+
}
|
|
1740
|
+
const slice = callFrames.slice(0, depth);
|
|
1741
|
+
return await Promise.all(
|
|
1742
|
+
slice.map(async (frame) => {
|
|
1743
|
+
const base = buildBaseFrame(frame);
|
|
1744
|
+
if (options.stackCaptures.length === 0) {
|
|
1745
|
+
return base;
|
|
1746
|
+
}
|
|
1747
|
+
const captures = await captureFrameExpressions(
|
|
1748
|
+
session,
|
|
1749
|
+
frame,
|
|
1750
|
+
options.stackCaptures,
|
|
1751
|
+
options.maxValueLength
|
|
1752
|
+
);
|
|
1753
|
+
return { ...base, captures };
|
|
1754
|
+
})
|
|
1755
|
+
);
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1770
1758
|
// src/snapshot/capture.ts
|
|
1771
1759
|
async function captureSnapshot(session, pause, options = {}) {
|
|
1772
1760
|
const maxValueLength = resolveMaxValueLength(options.maxValueLength);
|
|
1773
1761
|
const top = pause.callFrames[0];
|
|
1774
1762
|
let topFrame;
|
|
1775
1763
|
let captures = [];
|
|
1764
|
+
let stack = [];
|
|
1776
1765
|
if (top) {
|
|
1777
1766
|
topFrame = {
|
|
1778
1767
|
functionName: top.functionName,
|
|
@@ -1785,14 +1774,31 @@ async function captureSnapshot(session, pause, options = {}) {
|
|
|
1785
1774
|
topFrame = { ...topFrame, scopes };
|
|
1786
1775
|
}
|
|
1787
1776
|
captures = await captureExpressions(session, top.callFrameId, options.captures, maxValueLength);
|
|
1777
|
+
stack = await walkStack(session, pause.callFrames, {
|
|
1778
|
+
stackDepth: options.stackDepth ?? DEFAULT_STACK_DEPTH,
|
|
1779
|
+
stackCaptures: options.stackCaptures ?? [],
|
|
1780
|
+
maxValueLength
|
|
1781
|
+
});
|
|
1788
1782
|
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1783
|
+
const exception = await captureException(session, pause, maxValueLength);
|
|
1784
|
+
return buildResult2({
|
|
1785
|
+
pause,
|
|
1786
|
+
topFrame,
|
|
1787
|
+
captures,
|
|
1788
|
+
stack,
|
|
1789
|
+
exception
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1792
|
+
function buildResult2(input) {
|
|
1793
|
+
const base = {
|
|
1794
|
+
reason: input.pause.reason,
|
|
1795
|
+
hitBreakpoints: input.pause.hitBreakpoints,
|
|
1792
1796
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1793
|
-
|
|
1794
|
-
captures
|
|
1797
|
+
captures: input.captures
|
|
1795
1798
|
};
|
|
1799
|
+
const withFrame = input.topFrame === void 0 ? base : { ...base, topFrame: input.topFrame };
|
|
1800
|
+
const withStack = input.stack.length > 0 ? { ...withFrame, stack: input.stack } : withFrame;
|
|
1801
|
+
return input.exception === void 0 ? withStack : { ...withStack, exception: input.exception };
|
|
1796
1802
|
}
|
|
1797
1803
|
async function captureExpressions(session, callFrameId, captures, maxValueLength) {
|
|
1798
1804
|
if (captures === void 0 || captures.length === 0) {
|
|
@@ -1815,7 +1821,7 @@ async function captureExpression(session, callFrameId, expression, maxValueLengt
|
|
|
1815
1821
|
}
|
|
1816
1822
|
}
|
|
1817
1823
|
|
|
1818
|
-
// src/cli/commands/
|
|
1824
|
+
// src/cli/commands/exception.ts
|
|
1819
1825
|
init_types();
|
|
1820
1826
|
|
|
1821
1827
|
// src/cli/captureParser.ts
|
|
@@ -1898,7 +1904,450 @@ function splitCaptureExpressions(raw) {
|
|
|
1898
1904
|
return state.pieces;
|
|
1899
1905
|
}
|
|
1900
1906
|
|
|
1907
|
+
// src/cli/warnings.ts
|
|
1908
|
+
import process4 from "process";
|
|
1909
|
+
function warnOnUnboundBreakpoints(handles) {
|
|
1910
|
+
for (const handle of handles) {
|
|
1911
|
+
if (handle.resolvedLocations.length === 0) {
|
|
1912
|
+
process4.stderr.write(
|
|
1913
|
+
`[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.
|
|
1914
|
+
`
|
|
1915
|
+
);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
function roundDurationMs(durationMs) {
|
|
1920
|
+
return Math.round(durationMs * 1e3) / 1e3;
|
|
1921
|
+
}
|
|
1922
|
+
function warnOnUnmatchedPause(pause) {
|
|
1923
|
+
const reason = pause.reason.length > 0 ? pause.reason : "unknown";
|
|
1924
|
+
process4.stderr.write(
|
|
1925
|
+
`[cf-inspector] warning: target is paused by another debugger event (${reason} at ${formatPauseLocation(pause)}); waiting for it to resume...
|
|
1926
|
+
`
|
|
1927
|
+
);
|
|
1928
|
+
}
|
|
1929
|
+
function withPausedDuration(snapshot, pausedDurationMs) {
|
|
1930
|
+
const base = {
|
|
1931
|
+
reason: snapshot.reason,
|
|
1932
|
+
hitBreakpoints: snapshot.hitBreakpoints,
|
|
1933
|
+
capturedAt: snapshot.capturedAt,
|
|
1934
|
+
pausedDurationMs,
|
|
1935
|
+
captures: snapshot.captures
|
|
1936
|
+
};
|
|
1937
|
+
const withFrame = snapshot.topFrame === void 0 ? base : { ...base, topFrame: snapshot.topFrame };
|
|
1938
|
+
const withStack = snapshot.stack === void 0 ? withFrame : { ...withFrame, stack: snapshot.stack };
|
|
1939
|
+
return snapshot.exception === void 0 ? withStack : { ...withStack, exception: snapshot.exception };
|
|
1940
|
+
}
|
|
1941
|
+
function formatPauseLocation(pause) {
|
|
1942
|
+
const top = pause.callFrames[0];
|
|
1943
|
+
if (top === void 0) {
|
|
1944
|
+
return "(no call frame)";
|
|
1945
|
+
}
|
|
1946
|
+
const url = top.url !== void 0 && top.url.length > 0 ? top.url : "(unknown)";
|
|
1947
|
+
return `${url}:${(top.lineNumber + 1).toString()}:${(top.columnNumber + 1).toString()}`;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
// src/cli/commands/exception.ts
|
|
1951
|
+
var VALID_PAUSE_TYPES = ["uncaught", "caught", "all"];
|
|
1952
|
+
async function handleException(opts) {
|
|
1953
|
+
const prepared = prepareExceptionCommand(opts);
|
|
1954
|
+
const result = await runExceptionCommand(prepared, opts);
|
|
1955
|
+
if (opts.json) {
|
|
1956
|
+
writeJson(result);
|
|
1957
|
+
} else {
|
|
1958
|
+
writeHumanSnapshot(result);
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1961
|
+
function prepareExceptionCommand(opts) {
|
|
1962
|
+
const target = resolveTarget(opts);
|
|
1963
|
+
const stateRaw = (opts.type ?? "uncaught").trim().toLowerCase();
|
|
1964
|
+
if (!VALID_PAUSE_TYPES.includes(stateRaw)) {
|
|
1965
|
+
throw new CfInspectorError(
|
|
1966
|
+
"INVALID_PAUSE_TYPE",
|
|
1967
|
+
`--type must be one of ${VALID_PAUSE_TYPES.join(", ")} (received "${stateRaw}")`
|
|
1968
|
+
);
|
|
1969
|
+
}
|
|
1970
|
+
const timeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_EXCEPTION_TIMEOUT_SEC;
|
|
1971
|
+
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
1972
|
+
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
1973
|
+
return {
|
|
1974
|
+
target,
|
|
1975
|
+
state: stateRaw,
|
|
1976
|
+
captures: parseCaptureList(opts.capture),
|
|
1977
|
+
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
1978
|
+
timeoutMs: timeoutSec * 1e3,
|
|
1979
|
+
...maxValueLength === void 0 ? {} : { maxValueLength },
|
|
1980
|
+
...stackDepth === void 0 ? {} : { stackDepth },
|
|
1981
|
+
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
1982
|
+
};
|
|
1983
|
+
}
|
|
1984
|
+
async function runExceptionCommand(command, opts) {
|
|
1985
|
+
return await withSession(command.target, async (session) => {
|
|
1986
|
+
await setPauseOnExceptions(session, command.state);
|
|
1987
|
+
try {
|
|
1988
|
+
const pause = await waitForPause(session, {
|
|
1989
|
+
timeoutMs: command.timeoutMs,
|
|
1990
|
+
pauseReasons: ["exception", "promiseRejection"],
|
|
1991
|
+
unmatchedPausePolicy: "wait-for-resume"
|
|
1992
|
+
});
|
|
1993
|
+
const pausedStartedAt = pause.receivedAtMs ?? performance3.now();
|
|
1994
|
+
const snapshot = await captureSnapshot(session, pause, {
|
|
1995
|
+
captures: command.captures,
|
|
1996
|
+
includeScopes: opts.includeScopes === true,
|
|
1997
|
+
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength },
|
|
1998
|
+
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
1999
|
+
stackCaptures: command.stackCaptures
|
|
2000
|
+
});
|
|
2001
|
+
if (opts.keepPaused === true) {
|
|
2002
|
+
return withPausedDuration(snapshot, null);
|
|
2003
|
+
}
|
|
2004
|
+
return await resumeAfterException(session, snapshot, pausedStartedAt);
|
|
2005
|
+
} finally {
|
|
2006
|
+
await disablePauseOnExceptionsBestEffort(session);
|
|
2007
|
+
}
|
|
2008
|
+
});
|
|
2009
|
+
}
|
|
2010
|
+
async function resumeAfterException(session, snapshot, pausedStartedAt) {
|
|
2011
|
+
try {
|
|
2012
|
+
await resume(session);
|
|
2013
|
+
return withPausedDuration(snapshot, roundDurationMs(performance3.now() - pausedStartedAt));
|
|
2014
|
+
} catch {
|
|
2015
|
+
process5.stderr.write(
|
|
2016
|
+
"[cf-inspector] warning: Debugger.resume failed after exception capture; pausedDurationMs is unknown.\n"
|
|
2017
|
+
);
|
|
2018
|
+
return withPausedDuration(snapshot, null);
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
async function disablePauseOnExceptionsBestEffort(session) {
|
|
2022
|
+
try {
|
|
2023
|
+
await setPauseOnExceptions(session, "none");
|
|
2024
|
+
} catch {
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
// src/cli/commands/listScripts.ts
|
|
2029
|
+
import process6 from "process";
|
|
2030
|
+
async function handleListScripts(opts) {
|
|
2031
|
+
const target = resolveTarget(opts);
|
|
2032
|
+
const scripts = await withSession(target, (session) => Promise.resolve(listScripts(session)));
|
|
2033
|
+
if (opts.json) {
|
|
2034
|
+
writeJson(scripts);
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
for (const script of scripts) {
|
|
2038
|
+
process6.stdout.write(`${script.scriptId} ${script.url}
|
|
2039
|
+
`);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
|
|
2043
|
+
// src/cli/commands/log.ts
|
|
2044
|
+
import process7 from "process";
|
|
2045
|
+
|
|
2046
|
+
// src/logpoint/stream.ts
|
|
2047
|
+
init_types();
|
|
2048
|
+
|
|
2049
|
+
// src/logpoint/condition.ts
|
|
2050
|
+
import { randomBytes } from "crypto";
|
|
2051
|
+
var SENTINEL_PREFIX = "__CFI_LOG_";
|
|
2052
|
+
var SENTINEL_SUFFIX = "__";
|
|
2053
|
+
var HITS_GLOBAL2 = "globalThis.__CFI_LOG_HITS";
|
|
2054
|
+
function buildLoggingIife(sentinel, expression) {
|
|
2055
|
+
return [
|
|
2056
|
+
"(function(){",
|
|
2057
|
+
`var s=${JSON.stringify(sentinel)};`,
|
|
2058
|
+
"try{",
|
|
2059
|
+
`var v=(${expression});`,
|
|
2060
|
+
"var r=typeof v==='string'?v:JSON.stringify(v);",
|
|
2061
|
+
"console.log(s, r);",
|
|
2062
|
+
"}catch(e){",
|
|
2063
|
+
"console.log(s, '!err:'+(e&&e.message?e.message:String(e)));",
|
|
2064
|
+
"}",
|
|
2065
|
+
"return false;",
|
|
2066
|
+
"})()"
|
|
2067
|
+
].join("");
|
|
2068
|
+
}
|
|
2069
|
+
function buildHitGate(hitCount, counterKey) {
|
|
2070
|
+
const keyLiteral = JSON.stringify(counterKey);
|
|
2071
|
+
return [
|
|
2072
|
+
"(function(){",
|
|
2073
|
+
`var m=(${HITS_GLOBAL2}=${HITS_GLOBAL2}||{});`,
|
|
2074
|
+
`var k=${keyLiteral};`,
|
|
2075
|
+
"m[k]=(m[k]||0)+1;",
|
|
2076
|
+
`return m[k]>=${hitCount.toString()};`,
|
|
2077
|
+
"})()"
|
|
2078
|
+
].join("");
|
|
2079
|
+
}
|
|
2080
|
+
function combineGuards(guards) {
|
|
2081
|
+
const filtered = guards.filter((guard) => guard.length > 0);
|
|
2082
|
+
if (filtered.length === 0) {
|
|
2083
|
+
return void 0;
|
|
2084
|
+
}
|
|
2085
|
+
if (filtered.length === 1) {
|
|
2086
|
+
return filtered[0];
|
|
2087
|
+
}
|
|
2088
|
+
return filtered.map((guard) => `(${guard})`).join("&&");
|
|
2089
|
+
}
|
|
2090
|
+
function buildLogpointCondition(sentinel, expression, options = {}) {
|
|
2091
|
+
const guards = [];
|
|
2092
|
+
if (options.hitCount !== void 0) {
|
|
2093
|
+
const counterKey = options.counterKey ?? sentinel;
|
|
2094
|
+
guards.push(buildHitGate(options.hitCount, counterKey));
|
|
2095
|
+
}
|
|
2096
|
+
const userPredicate = options.predicate?.trim();
|
|
2097
|
+
if (userPredicate !== void 0 && userPredicate.length > 0) {
|
|
2098
|
+
guards.push(`(${userPredicate})`);
|
|
2099
|
+
}
|
|
2100
|
+
const iife = buildLoggingIife(sentinel, expression);
|
|
2101
|
+
const guard = combineGuards(guards);
|
|
2102
|
+
if (guard === void 0) {
|
|
2103
|
+
return iife;
|
|
2104
|
+
}
|
|
2105
|
+
return `(${guard})?${iife}:false`;
|
|
2106
|
+
}
|
|
2107
|
+
function generateSentinel() {
|
|
2108
|
+
return `${SENTINEL_PREFIX}${randomBytes(8).toString("hex")}${SENTINEL_SUFFIX}`;
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
// src/logpoint/events.ts
|
|
2112
|
+
function asString3(value) {
|
|
2113
|
+
return typeof value === "string" ? value : void 0;
|
|
2114
|
+
}
|
|
2115
|
+
function readArg(arg, index) {
|
|
2116
|
+
if (typeof arg !== "object" || arg === null) {
|
|
2117
|
+
return void 0;
|
|
2118
|
+
}
|
|
2119
|
+
const candidate = arg;
|
|
2120
|
+
if (candidate.type === "string" && typeof candidate.value === "string") {
|
|
2121
|
+
return candidate.value;
|
|
2122
|
+
}
|
|
2123
|
+
const isPrimitiveType = candidate.type === "number" || candidate.type === "boolean" || candidate.type === "bigint";
|
|
2124
|
+
const isPrimitiveValue = typeof candidate.value === "number" || typeof candidate.value === "boolean" || typeof candidate.value === "bigint";
|
|
2125
|
+
if (isPrimitiveType && isPrimitiveValue) {
|
|
2126
|
+
return String(candidate.value);
|
|
2127
|
+
}
|
|
2128
|
+
return index === 0 ? void 0 : "";
|
|
2129
|
+
}
|
|
2130
|
+
function parseLogEvent(rawArgs, sentinel, location, timestamp) {
|
|
2131
|
+
if (!Array.isArray(rawArgs) || rawArgs.length < 2) {
|
|
2132
|
+
return void 0;
|
|
2133
|
+
}
|
|
2134
|
+
const tag = readArg(rawArgs[0], 0);
|
|
2135
|
+
if (tag !== sentinel) {
|
|
2136
|
+
return void 0;
|
|
2137
|
+
}
|
|
2138
|
+
const payload = readArg(rawArgs[1], 1) ?? "";
|
|
2139
|
+
const ts = new Date(typeof timestamp === "number" ? timestamp : Date.now()).toISOString();
|
|
2140
|
+
const at = `${location.file}:${location.line.toString()}`;
|
|
2141
|
+
if (payload.startsWith("!err:")) {
|
|
2142
|
+
return { ts, at, error: payload.slice("!err:".length) };
|
|
2143
|
+
}
|
|
2144
|
+
return parsePayload(ts, at, payload);
|
|
2145
|
+
}
|
|
2146
|
+
function parsePayload(ts, at, payload) {
|
|
2147
|
+
try {
|
|
2148
|
+
const parsed = JSON.parse(payload);
|
|
2149
|
+
if (typeof parsed === "string") {
|
|
2150
|
+
return { ts, at, value: parsed };
|
|
2151
|
+
}
|
|
2152
|
+
return { ts, at, value: JSON.stringify(parsed) };
|
|
2153
|
+
} catch {
|
|
2154
|
+
return { ts, at, value: payload, raw: payload };
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
// src/logpoint/stream.ts
|
|
2159
|
+
function validateMaxEvents(maxEvents) {
|
|
2160
|
+
if (maxEvents === void 0) {
|
|
2161
|
+
return void 0;
|
|
2162
|
+
}
|
|
2163
|
+
if (!Number.isInteger(maxEvents) || maxEvents <= 0) {
|
|
2164
|
+
throw new CfInspectorError(
|
|
2165
|
+
"INVALID_ARGUMENT",
|
|
2166
|
+
`maxEvents must be a positive integer, received: ${maxEvents.toString()}`
|
|
2167
|
+
);
|
|
2168
|
+
}
|
|
2169
|
+
return maxEvents;
|
|
2170
|
+
}
|
|
2171
|
+
function validateHitCount2(hitCount) {
|
|
2172
|
+
if (hitCount === void 0) {
|
|
2173
|
+
return void 0;
|
|
2174
|
+
}
|
|
2175
|
+
if (!Number.isInteger(hitCount) || hitCount <= 0) {
|
|
2176
|
+
throw new CfInspectorError(
|
|
2177
|
+
"INVALID_HIT_COUNT",
|
|
2178
|
+
`hitCount must be a positive integer, received: ${hitCount.toString()}`
|
|
2179
|
+
);
|
|
2180
|
+
}
|
|
2181
|
+
return hitCount;
|
|
2182
|
+
}
|
|
2183
|
+
async function streamLogpoint(session, options) {
|
|
2184
|
+
const maxEvents = validateMaxEvents(options.maxEvents);
|
|
2185
|
+
const hitCount = validateHitCount2(options.hitCount);
|
|
2186
|
+
const sentinel = generateSentinel();
|
|
2187
|
+
const condition = buildLogpointCondition(sentinel, options.expression, {
|
|
2188
|
+
...options.condition === void 0 ? {} : { predicate: options.condition },
|
|
2189
|
+
...hitCount === void 0 ? {} : { hitCount }
|
|
2190
|
+
});
|
|
2191
|
+
let emitted = 0;
|
|
2192
|
+
let maxEventsReached = false;
|
|
2193
|
+
let stopMaxEvents;
|
|
2194
|
+
const offEvent = session.client.on("Runtime.consoleAPICalled", (raw) => {
|
|
2195
|
+
if (maxEventsReached) {
|
|
2196
|
+
return;
|
|
2197
|
+
}
|
|
2198
|
+
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
2199
|
+
if (event === void 0) {
|
|
2200
|
+
return;
|
|
2201
|
+
}
|
|
2202
|
+
emitted += 1;
|
|
2203
|
+
options.onEvent(event);
|
|
2204
|
+
if (maxEvents !== void 0 && emitted >= maxEvents) {
|
|
2205
|
+
maxEventsReached = true;
|
|
2206
|
+
stopMaxEvents?.();
|
|
2207
|
+
}
|
|
2208
|
+
});
|
|
2209
|
+
let handle;
|
|
2210
|
+
try {
|
|
2211
|
+
handle = await setBreakpoint(session, {
|
|
2212
|
+
file: options.location.file,
|
|
2213
|
+
line: options.location.line,
|
|
2214
|
+
...options.remoteRoot === void 0 ? {} : { remoteRoot: options.remoteRoot },
|
|
2215
|
+
condition
|
|
2216
|
+
});
|
|
2217
|
+
} catch (err) {
|
|
2218
|
+
offEvent();
|
|
2219
|
+
throw err;
|
|
2220
|
+
}
|
|
2221
|
+
options.onBreakpointSet?.(handle);
|
|
2222
|
+
try {
|
|
2223
|
+
const reason = await waitForStop(session, options, (signal) => {
|
|
2224
|
+
stopMaxEvents = signal;
|
|
2225
|
+
if (maxEventsReached) {
|
|
2226
|
+
signal();
|
|
2227
|
+
}
|
|
2228
|
+
});
|
|
2229
|
+
return { handle, sentinel, emitted, stoppedReason: reason };
|
|
2230
|
+
} finally {
|
|
2231
|
+
offEvent();
|
|
2232
|
+
await removeBreakpointBestEffort(session, handle.breakpointId);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
function toLogpointEvent(raw, sentinel, location) {
|
|
2236
|
+
const params = raw;
|
|
2237
|
+
if (asString3(params.type) !== "log") {
|
|
2238
|
+
return void 0;
|
|
2239
|
+
}
|
|
2240
|
+
const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
|
|
2241
|
+
return parseLogEvent(params.args, sentinel, location, ts);
|
|
2242
|
+
}
|
|
2243
|
+
async function removeBreakpointBestEffort(session, breakpointId) {
|
|
2244
|
+
try {
|
|
2245
|
+
await removeBreakpoint(session, breakpointId);
|
|
2246
|
+
} catch {
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
async function waitForStop(session, options, registerMaxEventsSignal) {
|
|
2250
|
+
return await new Promise((resolve) => {
|
|
2251
|
+
let settled = false;
|
|
2252
|
+
const finish = (reason) => {
|
|
2253
|
+
if (settled) {
|
|
2254
|
+
return;
|
|
2255
|
+
}
|
|
2256
|
+
settled = true;
|
|
2257
|
+
cleanup();
|
|
2258
|
+
resolve(reason);
|
|
2259
|
+
};
|
|
2260
|
+
const timer = options.durationMs === void 0 ? void 0 : setTimeout(() => {
|
|
2261
|
+
finish("duration");
|
|
2262
|
+
}, options.durationMs);
|
|
2263
|
+
const offClose = session.client.onClose(() => {
|
|
2264
|
+
finish("transport-closed");
|
|
2265
|
+
});
|
|
2266
|
+
const onAbort = () => {
|
|
2267
|
+
finish("signal");
|
|
2268
|
+
};
|
|
2269
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
2270
|
+
if (options.signal?.aborted === true) {
|
|
2271
|
+
finish("signal");
|
|
2272
|
+
}
|
|
2273
|
+
registerMaxEventsSignal(() => {
|
|
2274
|
+
finish("max-events");
|
|
2275
|
+
});
|
|
2276
|
+
function cleanup() {
|
|
2277
|
+
if (timer !== void 0) {
|
|
2278
|
+
clearTimeout(timer);
|
|
2279
|
+
}
|
|
2280
|
+
offClose();
|
|
2281
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
2282
|
+
}
|
|
2283
|
+
});
|
|
2284
|
+
}
|
|
2285
|
+
|
|
2286
|
+
// src/cli/commands/log.ts
|
|
2287
|
+
init_types();
|
|
2288
|
+
async function handleLog(opts) {
|
|
2289
|
+
const target = resolveTarget(opts);
|
|
2290
|
+
const location = parseBreakpointSpec(opts.at);
|
|
2291
|
+
const remoteRoot = parseRemoteRoot(opts.remoteRoot);
|
|
2292
|
+
const durationSec = parsePositiveInt(opts.duration, "--duration");
|
|
2293
|
+
const maxEvents = parsePositiveInt(opts.maxEvents, "--max-events");
|
|
2294
|
+
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
2295
|
+
const expression = opts.expr.trim();
|
|
2296
|
+
if (expression.length === 0) {
|
|
2297
|
+
throw new CfInspectorError("INVALID_EXPRESSION", "--expr must not be empty");
|
|
2298
|
+
}
|
|
2299
|
+
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2300
|
+
const abort = new AbortController();
|
|
2301
|
+
const onSig = () => {
|
|
2302
|
+
abort.abort();
|
|
2303
|
+
};
|
|
2304
|
+
process7.once("SIGINT", onSig);
|
|
2305
|
+
process7.once("SIGTERM", onSig);
|
|
2306
|
+
try {
|
|
2307
|
+
await withSession(target, async (session) => {
|
|
2308
|
+
await validateExpression(session, expression);
|
|
2309
|
+
if (condition !== void 0) {
|
|
2310
|
+
await validateExpression(session, condition);
|
|
2311
|
+
}
|
|
2312
|
+
const result = await streamLogpoint(session, {
|
|
2313
|
+
location,
|
|
2314
|
+
expression,
|
|
2315
|
+
remoteRoot,
|
|
2316
|
+
...durationSec === void 0 ? {} : { durationMs: durationSec * 1e3 },
|
|
2317
|
+
...maxEvents === void 0 ? {} : { maxEvents },
|
|
2318
|
+
...hitCount === void 0 ? {} : { hitCount },
|
|
2319
|
+
...condition === void 0 ? {} : { condition },
|
|
2320
|
+
signal: abort.signal,
|
|
2321
|
+
onEvent: (event) => {
|
|
2322
|
+
writeLogEvent(event, opts.json);
|
|
2323
|
+
},
|
|
2324
|
+
onBreakpointSet: (handle) => {
|
|
2325
|
+
warnOnUnboundBreakpoints([handle]);
|
|
2326
|
+
}
|
|
2327
|
+
});
|
|
2328
|
+
writeLogSummary(result.stoppedReason, result.emitted, opts.json);
|
|
2329
|
+
});
|
|
2330
|
+
} finally {
|
|
2331
|
+
process7.off("SIGINT", onSig);
|
|
2332
|
+
process7.off("SIGTERM", onSig);
|
|
2333
|
+
}
|
|
2334
|
+
}
|
|
2335
|
+
function writeLogSummary(stoppedReason, emitted, json) {
|
|
2336
|
+
if (json) {
|
|
2337
|
+
process7.stderr.write(`${JSON.stringify({ stopped: stoppedReason, emitted })}
|
|
2338
|
+
`);
|
|
2339
|
+
return;
|
|
2340
|
+
}
|
|
2341
|
+
process7.stderr.write(
|
|
2342
|
+
`Stopped (${stoppedReason}); emitted ${emitted.toString()} log ${emitted === 1 ? "entry" : "entries"}.
|
|
2343
|
+
`
|
|
2344
|
+
);
|
|
2345
|
+
}
|
|
2346
|
+
|
|
1901
2347
|
// src/cli/commands/snapshot.ts
|
|
2348
|
+
import { performance as performance4 } from "perf_hooks";
|
|
2349
|
+
import process8 from "process";
|
|
2350
|
+
init_types();
|
|
1902
2351
|
async function handleSnapshot(opts) {
|
|
1903
2352
|
const prepared = prepareSnapshotCommand(opts);
|
|
1904
2353
|
const result = await runSnapshotCommand(prepared, opts);
|
|
@@ -1919,6 +2368,8 @@ function prepareSnapshotCommand(opts) {
|
|
|
1919
2368
|
const timeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_BREAKPOINT_TIMEOUT_SEC;
|
|
1920
2369
|
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
1921
2370
|
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2371
|
+
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
2372
|
+
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
1922
2373
|
return {
|
|
1923
2374
|
target,
|
|
1924
2375
|
breakpoints: opts.bp.map((spec) => parseBreakpointSpec(spec)),
|
|
@@ -1926,7 +2377,10 @@ function prepareSnapshotCommand(opts) {
|
|
|
1926
2377
|
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
1927
2378
|
timeoutMs: timeoutSec * 1e3,
|
|
1928
2379
|
...condition === void 0 ? {} : { condition },
|
|
1929
|
-
...maxValueLength === void 0 ? {} : { maxValueLength }
|
|
2380
|
+
...maxValueLength === void 0 ? {} : { maxValueLength },
|
|
2381
|
+
...hitCount === void 0 ? {} : { hitCount },
|
|
2382
|
+
...stackDepth === void 0 ? {} : { stackDepth },
|
|
2383
|
+
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
1930
2384
|
};
|
|
1931
2385
|
}
|
|
1932
2386
|
async function runSnapshotCommand(command, opts) {
|
|
@@ -1940,17 +2394,20 @@ async function runSnapshotCommand(command, opts) {
|
|
|
1940
2394
|
file: bp.file,
|
|
1941
2395
|
line: bp.line,
|
|
1942
2396
|
remoteRoot: command.remoteRoot,
|
|
1943
|
-
...command.condition === void 0 ? {} : { condition: command.condition }
|
|
2397
|
+
...command.condition === void 0 ? {} : { condition: command.condition },
|
|
2398
|
+
...command.hitCount === void 0 ? {} : { hitCount: command.hitCount }
|
|
1944
2399
|
})
|
|
1945
2400
|
)
|
|
1946
2401
|
);
|
|
1947
2402
|
warnOnUnboundBreakpoints(handles);
|
|
1948
2403
|
const pause = await waitForCommandPause(session, opts, handles, command.timeoutMs);
|
|
1949
|
-
const pausedStartedAt = pause.receivedAtMs ??
|
|
2404
|
+
const pausedStartedAt = pause.receivedAtMs ?? performance4.now();
|
|
1950
2405
|
const snapshot = await captureSnapshot(session, pause, {
|
|
1951
2406
|
captures: command.captures,
|
|
1952
2407
|
includeScopes: opts.includeScopes === true,
|
|
1953
|
-
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength }
|
|
2408
|
+
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength },
|
|
2409
|
+
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
2410
|
+
stackCaptures: command.stackCaptures
|
|
1954
2411
|
});
|
|
1955
2412
|
if (opts.keepPaused === true) {
|
|
1956
2413
|
return withPausedDuration(snapshot, null);
|
|
@@ -1976,47 +2433,293 @@ async function waitForCommandPause(session, opts, handles, timeoutMs) {
|
|
|
1976
2433
|
async function resumeAfterSnapshot(session, snapshot, pausedStartedAt) {
|
|
1977
2434
|
try {
|
|
1978
2435
|
await resume(session);
|
|
1979
|
-
return withPausedDuration(snapshot, roundDurationMs(
|
|
2436
|
+
return withPausedDuration(snapshot, roundDurationMs(performance4.now() - pausedStartedAt));
|
|
1980
2437
|
} catch {
|
|
1981
|
-
|
|
2438
|
+
process8.stderr.write(
|
|
1982
2439
|
"[cf-inspector] warning: Debugger.resume failed after snapshot; pausedDurationMs is unknown.\n"
|
|
1983
2440
|
);
|
|
1984
2441
|
return withPausedDuration(snapshot, null);
|
|
1985
2442
|
}
|
|
1986
2443
|
}
|
|
1987
2444
|
|
|
2445
|
+
// src/cli/commands/watch.ts
|
|
2446
|
+
import process9 from "process";
|
|
2447
|
+
init_types();
|
|
2448
|
+
async function handleWatch(opts) {
|
|
2449
|
+
const prepared = prepareWatchCommand(opts);
|
|
2450
|
+
const abort = new AbortController();
|
|
2451
|
+
const onSig = () => {
|
|
2452
|
+
abort.abort();
|
|
2453
|
+
};
|
|
2454
|
+
process9.once("SIGINT", onSig);
|
|
2455
|
+
process9.once("SIGTERM", onSig);
|
|
2456
|
+
let stoppedReason = "signal";
|
|
2457
|
+
let emitted = 0;
|
|
2458
|
+
try {
|
|
2459
|
+
await withSession(prepared.target, async (session) => {
|
|
2460
|
+
const result = await runWatchLoop(session, prepared, opts, abort.signal);
|
|
2461
|
+
stoppedReason = result.stoppedReason;
|
|
2462
|
+
emitted = result.emitted;
|
|
2463
|
+
});
|
|
2464
|
+
} finally {
|
|
2465
|
+
process9.off("SIGINT", onSig);
|
|
2466
|
+
process9.off("SIGTERM", onSig);
|
|
2467
|
+
}
|
|
2468
|
+
writeWatchSummary(stoppedReason, emitted, opts.json);
|
|
2469
|
+
}
|
|
2470
|
+
function prepareWatchCommand(opts) {
|
|
2471
|
+
const target = resolveTarget(opts);
|
|
2472
|
+
if (opts.bp.length === 0) {
|
|
2473
|
+
throw new CfInspectorError(
|
|
2474
|
+
"INVALID_BREAKPOINT",
|
|
2475
|
+
"At least one --bp <file:line> is required."
|
|
2476
|
+
);
|
|
2477
|
+
}
|
|
2478
|
+
const perHitTimeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_BREAKPOINT_TIMEOUT_SEC;
|
|
2479
|
+
const durationSec = parsePositiveInt(opts.duration, "--duration");
|
|
2480
|
+
const maxEvents = parsePositiveInt(opts.maxEvents, "--max-events");
|
|
2481
|
+
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
2482
|
+
const hitCount = parsePositiveInt(opts.hitCount, "--hit-count");
|
|
2483
|
+
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
2484
|
+
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2485
|
+
return {
|
|
2486
|
+
target,
|
|
2487
|
+
breakpoints: opts.bp.map((spec) => parseBreakpointSpec(spec)),
|
|
2488
|
+
captures: parseCaptureList(opts.capture),
|
|
2489
|
+
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
2490
|
+
perHitTimeoutMs: perHitTimeoutSec * 1e3,
|
|
2491
|
+
...durationSec === void 0 ? {} : { durationMs: durationSec * 1e3 },
|
|
2492
|
+
...maxEvents === void 0 ? {} : { maxEvents },
|
|
2493
|
+
...maxValueLength === void 0 ? {} : { maxValueLength },
|
|
2494
|
+
...condition === void 0 ? {} : { condition },
|
|
2495
|
+
...hitCount === void 0 ? {} : { hitCount },
|
|
2496
|
+
...stackDepth === void 0 ? {} : { stackDepth },
|
|
2497
|
+
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
2498
|
+
};
|
|
2499
|
+
}
|
|
2500
|
+
async function runWatchLoop(session, command, opts, signal) {
|
|
2501
|
+
if (command.condition !== void 0) {
|
|
2502
|
+
await validateExpression(session, command.condition);
|
|
2503
|
+
}
|
|
2504
|
+
const handles = await Promise.all(
|
|
2505
|
+
command.breakpoints.map(
|
|
2506
|
+
(bp) => setBreakpoint(session, {
|
|
2507
|
+
file: bp.file,
|
|
2508
|
+
line: bp.line,
|
|
2509
|
+
remoteRoot: command.remoteRoot,
|
|
2510
|
+
...command.condition === void 0 ? {} : { condition: command.condition },
|
|
2511
|
+
...command.hitCount === void 0 ? {} : { hitCount: command.hitCount }
|
|
2512
|
+
})
|
|
2513
|
+
)
|
|
2514
|
+
);
|
|
2515
|
+
warnOnUnboundBreakpoints(handles);
|
|
2516
|
+
const deadline = computeDeadline(command.durationMs);
|
|
2517
|
+
let emitted = 0;
|
|
2518
|
+
const state = { stopped: false, reason: "signal" };
|
|
2519
|
+
const setStop = (reason) => {
|
|
2520
|
+
if (state.stopped) {
|
|
2521
|
+
return;
|
|
2522
|
+
}
|
|
2523
|
+
state.reason = reason;
|
|
2524
|
+
state.stopped = true;
|
|
2525
|
+
};
|
|
2526
|
+
const transportClosed = waitForTransportClose(session);
|
|
2527
|
+
transportClosed.promise.then(() => {
|
|
2528
|
+
setStop("transport-closed");
|
|
2529
|
+
}).catch(() => {
|
|
2530
|
+
});
|
|
2531
|
+
try {
|
|
2532
|
+
while (!state.stopped) {
|
|
2533
|
+
if (signal.aborted) {
|
|
2534
|
+
setStop("signal");
|
|
2535
|
+
break;
|
|
2536
|
+
}
|
|
2537
|
+
const remainingMs = remainingForLoop(deadline, command.perHitTimeoutMs);
|
|
2538
|
+
if (remainingMs <= 0) {
|
|
2539
|
+
setStop("duration");
|
|
2540
|
+
break;
|
|
2541
|
+
}
|
|
2542
|
+
const pause = await waitForNextWatchPause(session, handles, remainingMs, signal);
|
|
2543
|
+
if (pause === "signal") {
|
|
2544
|
+
setStop("signal");
|
|
2545
|
+
break;
|
|
2546
|
+
}
|
|
2547
|
+
if (pause === "timeout") {
|
|
2548
|
+
if (deadline !== void 0 && Date.now() >= deadline) {
|
|
2549
|
+
setStop("duration");
|
|
2550
|
+
break;
|
|
2551
|
+
}
|
|
2552
|
+
continue;
|
|
2553
|
+
}
|
|
2554
|
+
const event = await captureWatchEvent(session, command, pause, emitted + 1, opts);
|
|
2555
|
+
emitted += 1;
|
|
2556
|
+
writeWatchEvent(event, opts.json);
|
|
2557
|
+
try {
|
|
2558
|
+
await resume(session);
|
|
2559
|
+
} catch {
|
|
2560
|
+
process9.stderr.write("[cf-inspector] warning: Debugger.resume failed during watch.\n");
|
|
2561
|
+
setStop("transport-closed");
|
|
2562
|
+
break;
|
|
2563
|
+
}
|
|
2564
|
+
if (command.maxEvents !== void 0 && emitted >= command.maxEvents) {
|
|
2565
|
+
setStop("max-events");
|
|
2566
|
+
break;
|
|
2567
|
+
}
|
|
2568
|
+
}
|
|
2569
|
+
} finally {
|
|
2570
|
+
transportClosed.cancel();
|
|
2571
|
+
}
|
|
2572
|
+
return { emitted, stoppedReason: state.reason };
|
|
2573
|
+
}
|
|
2574
|
+
function computeDeadline(durationMs) {
|
|
2575
|
+
if (durationMs === void 0) {
|
|
2576
|
+
return void 0;
|
|
2577
|
+
}
|
|
2578
|
+
return Date.now() + durationMs;
|
|
2579
|
+
}
|
|
2580
|
+
function remainingForLoop(deadline, perHitTimeoutMs) {
|
|
2581
|
+
if (deadline === void 0) {
|
|
2582
|
+
return perHitTimeoutMs;
|
|
2583
|
+
}
|
|
2584
|
+
const remaining = deadline - Date.now();
|
|
2585
|
+
if (remaining <= 0) {
|
|
2586
|
+
return 0;
|
|
2587
|
+
}
|
|
2588
|
+
return Math.min(remaining, perHitTimeoutMs);
|
|
2589
|
+
}
|
|
2590
|
+
function waitForTransportClose(session) {
|
|
2591
|
+
let cancelled = false;
|
|
2592
|
+
let resolve;
|
|
2593
|
+
const promise = new Promise((res) => {
|
|
2594
|
+
resolve = res;
|
|
2595
|
+
});
|
|
2596
|
+
const off = session.client.onClose(() => {
|
|
2597
|
+
if (!cancelled) {
|
|
2598
|
+
resolve?.();
|
|
2599
|
+
}
|
|
2600
|
+
});
|
|
2601
|
+
return {
|
|
2602
|
+
promise,
|
|
2603
|
+
cancel: () => {
|
|
2604
|
+
cancelled = true;
|
|
2605
|
+
off();
|
|
2606
|
+
resolve?.();
|
|
2607
|
+
}
|
|
2608
|
+
};
|
|
2609
|
+
}
|
|
2610
|
+
async function waitForNextWatchPause(session, handles, timeoutMs, signal) {
|
|
2611
|
+
if (signal.aborted) {
|
|
2612
|
+
return "signal";
|
|
2613
|
+
}
|
|
2614
|
+
try {
|
|
2615
|
+
return await waitForPause(session, {
|
|
2616
|
+
timeoutMs,
|
|
2617
|
+
breakpointIds: handles.map((h) => h.breakpointId),
|
|
2618
|
+
unmatchedPausePolicy: "wait-for-resume"
|
|
2619
|
+
});
|
|
2620
|
+
} catch (err) {
|
|
2621
|
+
if (err instanceof CfInspectorError) {
|
|
2622
|
+
if (err.code === "BREAKPOINT_NOT_HIT") {
|
|
2623
|
+
return "timeout";
|
|
2624
|
+
}
|
|
2625
|
+
if (err.code === "UNRELATED_PAUSE_TIMEOUT") {
|
|
2626
|
+
return "timeout";
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
throw err;
|
|
2630
|
+
}
|
|
2631
|
+
}
|
|
2632
|
+
async function captureWatchEvent(session, command, pause, hit, opts) {
|
|
2633
|
+
const snapshot = await captureSnapshot(session, pause, {
|
|
2634
|
+
captures: command.captures,
|
|
2635
|
+
includeScopes: opts.includeScopes === true,
|
|
2636
|
+
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength },
|
|
2637
|
+
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
2638
|
+
stackCaptures: command.stackCaptures
|
|
2639
|
+
});
|
|
2640
|
+
const at = formatLocation(command, snapshot.topFrame);
|
|
2641
|
+
const base = {
|
|
2642
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2643
|
+
at,
|
|
2644
|
+
hit,
|
|
2645
|
+
reason: snapshot.reason,
|
|
2646
|
+
hitBreakpoints: snapshot.hitBreakpoints,
|
|
2647
|
+
captures: snapshot.captures
|
|
2648
|
+
};
|
|
2649
|
+
const withFrame = snapshot.topFrame === void 0 ? base : { ...base, topFrame: snapshot.topFrame };
|
|
2650
|
+
const withStack = snapshot.stack === void 0 ? withFrame : { ...withFrame, stack: snapshot.stack };
|
|
2651
|
+
return snapshot.exception === void 0 ? withStack : { ...withStack, exception: snapshot.exception };
|
|
2652
|
+
}
|
|
2653
|
+
function formatLocation(command, topFrame) {
|
|
2654
|
+
if (topFrame?.url !== void 0) {
|
|
2655
|
+
return `${topFrame.url}:${topFrame.line.toString()}`;
|
|
2656
|
+
}
|
|
2657
|
+
const first = command.breakpoints[0];
|
|
2658
|
+
if (first === void 0) {
|
|
2659
|
+
return "(unknown)";
|
|
2660
|
+
}
|
|
2661
|
+
return `${first.file}:${first.line.toString()}`;
|
|
2662
|
+
}
|
|
2663
|
+
function writeWatchSummary(reason, emitted, json) {
|
|
2664
|
+
if (json) {
|
|
2665
|
+
process9.stderr.write(`${JSON.stringify({ stopped: reason, emitted })}
|
|
2666
|
+
`);
|
|
2667
|
+
return;
|
|
2668
|
+
}
|
|
2669
|
+
process9.stderr.write(
|
|
2670
|
+
`Stopped (${reason}); emitted ${emitted.toString()} watch ${emitted === 1 ? "event" : "events"}.
|
|
2671
|
+
`
|
|
2672
|
+
);
|
|
2673
|
+
}
|
|
2674
|
+
|
|
1988
2675
|
// src/cli/program.ts
|
|
1989
2676
|
function applyTargetOptions(cmd) {
|
|
1990
2677
|
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");
|
|
1991
2678
|
}
|
|
2679
|
+
var collectStrings = (value, prev = []) => [
|
|
2680
|
+
...prev,
|
|
2681
|
+
value
|
|
2682
|
+
];
|
|
1992
2683
|
async function main(argv) {
|
|
1993
2684
|
const program = new Command();
|
|
1994
2685
|
program.name("cf-inspector").description("Drive a Node.js inspector from the command line \u2014 set breakpoints, capture snapshots, evaluate expressions");
|
|
1995
2686
|
registerSnapshot(program);
|
|
1996
2687
|
registerLog(program);
|
|
2688
|
+
registerWatch(program);
|
|
2689
|
+
registerException(program);
|
|
1997
2690
|
registerEval(program);
|
|
1998
2691
|
registerListScripts(program);
|
|
1999
2692
|
registerAttach(program);
|
|
2000
2693
|
await program.parseAsync([...argv]);
|
|
2001
2694
|
}
|
|
2002
2695
|
function registerSnapshot(program) {
|
|
2003
|
-
const collectStrings = (value, prev = []) => [
|
|
2004
|
-
...prev,
|
|
2005
|
-
value
|
|
2006
|
-
];
|
|
2007
2696
|
applyTargetOptions(
|
|
2008
2697
|
program.command("snapshot").description("Set a breakpoint, wait for it to hit, capture expressions, and resume")
|
|
2009
|
-
).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) => {
|
|
2698
|
+
).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) => {
|
|
2010
2699
|
await handleSnapshot(opts);
|
|
2011
2700
|
});
|
|
2012
2701
|
}
|
|
2013
2702
|
function registerLog(program) {
|
|
2014
2703
|
applyTargetOptions(
|
|
2015
2704
|
program.command("log").description("Stream a non-pausing logpoint: log an expression each time a line executes")
|
|
2016
|
-
).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) => {
|
|
2705
|
+
).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) => {
|
|
2017
2706
|
await handleLog(opts);
|
|
2018
2707
|
});
|
|
2019
2708
|
}
|
|
2709
|
+
function registerWatch(program) {
|
|
2710
|
+
applyTargetOptions(
|
|
2711
|
+
program.command("watch").description("Stream a snapshot per breakpoint hit (multi-shot watch); resume between hits")
|
|
2712
|
+
).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) => {
|
|
2713
|
+
await handleWatch(opts);
|
|
2714
|
+
});
|
|
2715
|
+
}
|
|
2716
|
+
function registerException(program) {
|
|
2717
|
+
applyTargetOptions(
|
|
2718
|
+
program.command("exception").description("Pause on a thrown exception, capture the value and frame, then resume")
|
|
2719
|
+
).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) => {
|
|
2720
|
+
await handleException(opts);
|
|
2721
|
+
});
|
|
2722
|
+
}
|
|
2020
2723
|
function registerEval(program) {
|
|
2021
2724
|
applyTargetOptions(
|
|
2022
2725
|
program.command("eval").description("Evaluate an expression against the global Runtime")
|
|
@@ -2042,20 +2745,20 @@ function registerAttach(program) {
|
|
|
2042
2745
|
// src/cli.ts
|
|
2043
2746
|
init_types();
|
|
2044
2747
|
try {
|
|
2045
|
-
await main(
|
|
2748
|
+
await main(process10.argv);
|
|
2046
2749
|
} catch (err) {
|
|
2047
2750
|
if (err instanceof CfInspectorError) {
|
|
2048
|
-
|
|
2751
|
+
process10.stderr.write(`Error [${err.code}]: ${err.message}
|
|
2049
2752
|
`);
|
|
2050
2753
|
if (err.detail !== void 0) {
|
|
2051
|
-
|
|
2754
|
+
process10.stderr.write(` detail: ${err.detail}
|
|
2052
2755
|
`);
|
|
2053
2756
|
}
|
|
2054
|
-
|
|
2757
|
+
process10.exit(1);
|
|
2055
2758
|
}
|
|
2056
2759
|
const message = err instanceof Error ? err.message : String(err);
|
|
2057
|
-
|
|
2760
|
+
process10.stderr.write(`Error: ${message}
|
|
2058
2761
|
`);
|
|
2059
|
-
|
|
2762
|
+
process10.exit(1);
|
|
2060
2763
|
}
|
|
2061
2764
|
//# sourceMappingURL=cli.js.map
|