@saptools/cf-inspector 0.3.19 → 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 +1178 -489
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +50 -4
- package/dist/index.js +323 -18
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
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";
|
|
@@ -634,12 +677,13 @@ function toCallFrames(value, scripts) {
|
|
|
634
677
|
});
|
|
635
678
|
}
|
|
636
679
|
function toPauseEvent(params, receivedAtMs, scripts) {
|
|
637
|
-
|
|
680
|
+
const base = {
|
|
638
681
|
reason: asString(params.reason),
|
|
639
682
|
hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
|
|
640
683
|
callFrames: toCallFrames(params.callFrames, scripts),
|
|
641
684
|
receivedAtMs
|
|
642
685
|
};
|
|
686
|
+
return params.data === void 0 ? base : { ...base, data: params.data };
|
|
643
687
|
}
|
|
644
688
|
function topFrameLocation(pause) {
|
|
645
689
|
const top = pause.callFrames[0];
|
|
@@ -725,6 +769,7 @@ init_types();
|
|
|
725
769
|
// src/cli/commandTypes.ts
|
|
726
770
|
var DEFAULT_BREAKPOINT_TIMEOUT_SEC = 30;
|
|
727
771
|
var DEFAULT_CF_TIMEOUT_SEC = 60;
|
|
772
|
+
var DEFAULT_EXCEPTION_TIMEOUT_SEC = 30;
|
|
728
773
|
|
|
729
774
|
// src/cli/target.ts
|
|
730
775
|
function parsePositiveInt(raw, label) {
|
|
@@ -827,6 +872,9 @@ init_types();
|
|
|
827
872
|
async function resume(session) {
|
|
828
873
|
await session.client.send("Debugger.resume");
|
|
829
874
|
}
|
|
875
|
+
async function setPauseOnExceptions(session, state) {
|
|
876
|
+
await session.client.send("Debugger.setPauseOnExceptions", { state });
|
|
877
|
+
}
|
|
830
878
|
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
831
879
|
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
832
880
|
callFrameId,
|
|
@@ -914,23 +962,9 @@ function writeHumanEvalResult(result) {
|
|
|
914
962
|
`);
|
|
915
963
|
}
|
|
916
964
|
|
|
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";
|
|
965
|
+
// src/cli/commands/exception.ts
|
|
966
|
+
import { performance as performance3 } from "perf_hooks";
|
|
967
|
+
import process5 from "process";
|
|
934
968
|
|
|
935
969
|
// src/pathMapper.ts
|
|
936
970
|
init_types();
|
|
@@ -1073,6 +1107,46 @@ function buildBreakpointUrlRegex(input) {
|
|
|
1073
1107
|
|
|
1074
1108
|
// src/inspector/breakpoints.ts
|
|
1075
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
|
+
}
|
|
1076
1150
|
async function setBreakpoint(session, input) {
|
|
1077
1151
|
const remoteRoot = input.remoteRoot ?? { kind: "none" };
|
|
1078
1152
|
const urlRegex = buildBreakpointUrlRegex({ file: input.file, remoteRoot });
|
|
@@ -1080,8 +1154,9 @@ async function setBreakpoint(session, input) {
|
|
|
1080
1154
|
lineNumber: input.line - 1,
|
|
1081
1155
|
urlRegex
|
|
1082
1156
|
};
|
|
1083
|
-
|
|
1084
|
-
|
|
1157
|
+
const condition = resolveCondition(input);
|
|
1158
|
+
if (condition !== void 0) {
|
|
1159
|
+
params["condition"] = condition;
|
|
1085
1160
|
}
|
|
1086
1161
|
const result = await session.client.send(
|
|
1087
1162
|
"Debugger.setBreakpointByUrl",
|
|
@@ -1106,378 +1181,130 @@ async function removeBreakpoint(session, breakpointId) {
|
|
|
1106
1181
|
await session.client.send("Debugger.removeBreakpoint", { breakpointId });
|
|
1107
1182
|
}
|
|
1108
1183
|
|
|
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("");
|
|
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));
|
|
1127
1195
|
}
|
|
1128
|
-
function
|
|
1129
|
-
return
|
|
1196
|
+
function remainingUntil(deadlineMs) {
|
|
1197
|
+
return Math.max(0, deadlineMs - performance2.now());
|
|
1130
1198
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
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;
|
|
1135
1203
|
}
|
|
1136
|
-
function
|
|
1137
|
-
|
|
1138
|
-
|
|
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;
|
|
1139
1220
|
}
|
|
1140
|
-
const
|
|
1141
|
-
if (
|
|
1142
|
-
|
|
1221
|
+
const remainingMs = remainingUntil(deadlineMs);
|
|
1222
|
+
if (remainingMs <= 0) {
|
|
1223
|
+
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1143
1224
|
}
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
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;
|
|
1148
1233
|
}
|
|
1149
|
-
return index === 0 ? void 0 : "";
|
|
1150
1234
|
}
|
|
1151
|
-
function
|
|
1152
|
-
if (
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
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
|
+
);
|
|
1158
1242
|
}
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
const at = `${location.file}:${location.line.toString()}`;
|
|
1162
|
-
if (payload.startsWith("!err:")) {
|
|
1163
|
-
return { ts, at, error: payload.slice("!err:".length) };
|
|
1243
|
+
if (hasResumedSincePause(session, pause)) {
|
|
1244
|
+
return;
|
|
1164
1245
|
}
|
|
1165
|
-
|
|
1246
|
+
options.onUnmatchedPause?.(pause);
|
|
1247
|
+
await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
|
|
1166
1248
|
}
|
|
1167
|
-
function
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
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);
|
|
1172
1262
|
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
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);
|
|
1176
1268
|
}
|
|
1269
|
+
throwBreakpointTimeout(options.timeoutMs);
|
|
1177
1270
|
}
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
const condition = buildLogpointCondition(sentinel, options.expression);
|
|
1183
|
-
let emitted = 0;
|
|
1184
|
-
const offEvent = session.client.on("Runtime.consoleAPICalled", (raw) => {
|
|
1185
|
-
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
1186
|
-
if (event === void 0) {
|
|
1187
|
-
return;
|
|
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;
|
|
1271
|
+
async function waitForLivePause(session, options, deadlineMs) {
|
|
1272
|
+
const remainingMs = remainingUntil(deadlineMs);
|
|
1273
|
+
if (remainingMs <= 0) {
|
|
1274
|
+
throwBreakpointTimeout(options.timeoutMs);
|
|
1203
1275
|
}
|
|
1204
|
-
|
|
1276
|
+
session.pauseWaitGate.active = true;
|
|
1277
|
+
let receivedAtMs;
|
|
1278
|
+
let params;
|
|
1205
1279
|
try {
|
|
1206
|
-
|
|
1207
|
-
|
|
1280
|
+
params = await session.client.waitFor("Debugger.paused", {
|
|
1281
|
+
timeoutMs: remainingMs,
|
|
1282
|
+
predicate: () => {
|
|
1283
|
+
receivedAtMs = performance2.now();
|
|
1284
|
+
return true;
|
|
1285
|
+
}
|
|
1286
|
+
});
|
|
1208
1287
|
} finally {
|
|
1209
|
-
|
|
1210
|
-
await removeBreakpointBestEffort(session, handle.breakpointId);
|
|
1288
|
+
session.pauseWaitGate.active = false;
|
|
1211
1289
|
}
|
|
1290
|
+
return toPauseEvent(params, receivedAtMs ?? performance2.now(), session.scripts);
|
|
1212
1291
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
const
|
|
1219
|
-
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";
|
|
1220
1299
|
}
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
}
|
|
1300
|
+
function formatPrimitive(value) {
|
|
1301
|
+
if (typeof value === "symbol") {
|
|
1302
|
+
return value.toString();
|
|
1303
|
+
}
|
|
1304
|
+
if (typeof value === "bigint") {
|
|
1305
|
+
return `${value.toString()}n`;
|
|
1225
1306
|
}
|
|
1226
|
-
|
|
1227
|
-
async function waitForStop(session, options) {
|
|
1228
|
-
return await new Promise((resolve) => {
|
|
1229
|
-
let settled = false;
|
|
1230
|
-
const finish = (reason) => {
|
|
1231
|
-
if (settled) {
|
|
1232
|
-
return;
|
|
1233
|
-
}
|
|
1234
|
-
settled = true;
|
|
1235
|
-
cleanup();
|
|
1236
|
-
resolve(reason);
|
|
1237
|
-
};
|
|
1238
|
-
const timer = options.durationMs === void 0 ? void 0 : setTimeout(() => {
|
|
1239
|
-
finish("duration");
|
|
1240
|
-
}, options.durationMs);
|
|
1241
|
-
const offClose = session.client.onClose(() => {
|
|
1242
|
-
finish("transport-closed");
|
|
1243
|
-
});
|
|
1244
|
-
const onAbort = () => {
|
|
1245
|
-
finish("signal");
|
|
1246
|
-
};
|
|
1247
|
-
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
1248
|
-
if (options.signal?.aborted === true) {
|
|
1249
|
-
finish("signal");
|
|
1250
|
-
}
|
|
1251
|
-
function cleanup() {
|
|
1252
|
-
if (timer !== void 0) {
|
|
1253
|
-
clearTimeout(timer);
|
|
1254
|
-
}
|
|
1255
|
-
offClose();
|
|
1256
|
-
options.signal?.removeEventListener("abort", onAbort);
|
|
1257
|
-
}
|
|
1258
|
-
});
|
|
1259
|
-
}
|
|
1260
|
-
|
|
1261
|
-
// src/cli/commands/log.ts
|
|
1262
|
-
init_types();
|
|
1263
|
-
|
|
1264
|
-
// src/cli/warnings.ts
|
|
1265
|
-
import process5 from "process";
|
|
1266
|
-
function warnOnUnboundBreakpoints(handles) {
|
|
1267
|
-
for (const handle of handles) {
|
|
1268
|
-
if (handle.resolvedLocations.length === 0) {
|
|
1269
|
-
process5.stderr.write(
|
|
1270
|
-
`[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.
|
|
1271
|
-
`
|
|
1272
|
-
);
|
|
1273
|
-
}
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
function roundDurationMs(durationMs) {
|
|
1277
|
-
return Math.round(durationMs * 1e3) / 1e3;
|
|
1278
|
-
}
|
|
1279
|
-
function warnOnUnmatchedPause(pause) {
|
|
1280
|
-
const reason = pause.reason.length > 0 ? pause.reason : "unknown";
|
|
1281
|
-
process5.stderr.write(
|
|
1282
|
-
`[cf-inspector] warning: target is paused by another debugger event (${reason} at ${formatPauseLocation(pause)}); waiting for it to resume...
|
|
1283
|
-
`
|
|
1284
|
-
);
|
|
1285
|
-
}
|
|
1286
|
-
function withPausedDuration(snapshot, pausedDurationMs) {
|
|
1287
|
-
return {
|
|
1288
|
-
reason: snapshot.reason,
|
|
1289
|
-
hitBreakpoints: snapshot.hitBreakpoints,
|
|
1290
|
-
capturedAt: snapshot.capturedAt,
|
|
1291
|
-
pausedDurationMs,
|
|
1292
|
-
...snapshot.topFrame === void 0 ? {} : { topFrame: snapshot.topFrame },
|
|
1293
|
-
captures: snapshot.captures
|
|
1294
|
-
};
|
|
1295
|
-
}
|
|
1296
|
-
function formatPauseLocation(pause) {
|
|
1297
|
-
const top = pause.callFrames[0];
|
|
1298
|
-
if (top === void 0) {
|
|
1299
|
-
return "(no call frame)";
|
|
1300
|
-
}
|
|
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
|
-
}
|
|
1304
|
-
|
|
1305
|
-
// src/cli/commands/log.ts
|
|
1306
|
-
async function handleLog(opts) {
|
|
1307
|
-
const target = resolveTarget(opts);
|
|
1308
|
-
const location = parseBreakpointSpec(opts.at);
|
|
1309
|
-
const remoteRoot = parseRemoteRoot(opts.remoteRoot);
|
|
1310
|
-
const durationSec = parsePositiveInt(opts.duration, "--duration");
|
|
1311
|
-
const expression = opts.expr.trim();
|
|
1312
|
-
if (expression.length === 0) {
|
|
1313
|
-
throw new CfInspectorError("INVALID_BREAKPOINT", "--expr must not be empty");
|
|
1314
|
-
}
|
|
1315
|
-
const abort = new AbortController();
|
|
1316
|
-
const onSig = () => {
|
|
1317
|
-
abort.abort();
|
|
1318
|
-
};
|
|
1319
|
-
process6.once("SIGINT", onSig);
|
|
1320
|
-
process6.once("SIGTERM", onSig);
|
|
1321
|
-
try {
|
|
1322
|
-
await withSession(target, async (session) => {
|
|
1323
|
-
await validateExpression(session, expression);
|
|
1324
|
-
const result = await streamLogpoint(session, {
|
|
1325
|
-
location,
|
|
1326
|
-
expression,
|
|
1327
|
-
remoteRoot,
|
|
1328
|
-
...durationSec === void 0 ? {} : { durationMs: durationSec * 1e3 },
|
|
1329
|
-
signal: abort.signal,
|
|
1330
|
-
onEvent: (event) => {
|
|
1331
|
-
writeLogEvent(event, opts.json);
|
|
1332
|
-
},
|
|
1333
|
-
onBreakpointSet: (handle) => {
|
|
1334
|
-
warnOnUnboundBreakpoints([handle]);
|
|
1335
|
-
}
|
|
1336
|
-
});
|
|
1337
|
-
writeLogSummary(result.stoppedReason, result.emitted, opts.json);
|
|
1338
|
-
});
|
|
1339
|
-
} finally {
|
|
1340
|
-
process6.off("SIGINT", onSig);
|
|
1341
|
-
process6.off("SIGTERM", onSig);
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
function writeLogSummary(stoppedReason, emitted, json) {
|
|
1345
|
-
if (json) {
|
|
1346
|
-
process6.stderr.write(`${JSON.stringify({ stopped: stoppedReason, emitted })}
|
|
1347
|
-
`);
|
|
1348
|
-
return;
|
|
1349
|
-
}
|
|
1350
|
-
process6.stderr.write(
|
|
1351
|
-
`Stopped (${stoppedReason}); emitted ${emitted.toString()} log ${emitted === 1 ? "entry" : "entries"}.
|
|
1352
|
-
`
|
|
1353
|
-
);
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
|
-
// src/cli/commands/snapshot.ts
|
|
1357
|
-
import { performance as performance3 } from "perf_hooks";
|
|
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;
|
|
1366
|
-
}
|
|
1367
|
-
return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
|
|
1368
|
-
}
|
|
1369
|
-
function remainingUntil(deadlineMs) {
|
|
1370
|
-
return Math.max(0, deadlineMs - performance2.now());
|
|
1371
|
-
}
|
|
1372
|
-
function hasResumedSincePause(session, pause) {
|
|
1373
|
-
const pauseAt = pause.receivedAtMs;
|
|
1374
|
-
const resumedAt = session.debuggerState.lastResumedAtMs;
|
|
1375
|
-
return pauseAt !== void 0 && resumedAt !== void 0 && resumedAt >= pauseAt;
|
|
1376
|
-
}
|
|
1377
|
-
function throwBreakpointTimeout(timeoutMs) {
|
|
1378
|
-
throw new CfInspectorError(
|
|
1379
|
-
"BREAKPOINT_NOT_HIT",
|
|
1380
|
-
`Timed out waiting for matching Debugger.paused after ${timeoutMs.toString()}ms`
|
|
1381
|
-
);
|
|
1382
|
-
}
|
|
1383
|
-
function throwUnrelatedPauseTimeout(pause, timeoutMs) {
|
|
1384
|
-
throw new CfInspectorError(
|
|
1385
|
-
"UNRELATED_PAUSE_TIMEOUT",
|
|
1386
|
-
`Target stayed paused by another debugger event before this command's breakpoint could hit within ${timeoutMs.toString()}ms`,
|
|
1387
|
-
pauseDetail(pause)
|
|
1388
|
-
);
|
|
1389
|
-
}
|
|
1390
|
-
async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, timeoutMs) {
|
|
1391
|
-
if (hasResumedSincePause(session, pause)) {
|
|
1392
|
-
return;
|
|
1393
|
-
}
|
|
1394
|
-
const remainingMs = remainingUntil(deadlineMs);
|
|
1395
|
-
if (remainingMs <= 0) {
|
|
1396
|
-
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
1397
|
-
}
|
|
1398
|
-
try {
|
|
1399
|
-
await session.client.waitFor("Debugger.resumed", { timeoutMs: remainingMs });
|
|
1400
|
-
session.debuggerState.lastResumedAtMs = performance2.now();
|
|
1401
|
-
} catch (err) {
|
|
1402
|
-
if (err instanceof CfInspectorError && err.code === "BREAKPOINT_NOT_HIT") {
|
|
1403
|
-
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
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;
|
|
1418
|
-
}
|
|
1419
|
-
options.onUnmatchedPause?.(pause);
|
|
1420
|
-
await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
|
|
1421
|
-
}
|
|
1422
|
-
async function waitForPause(session, options) {
|
|
1423
|
-
const deadlineMs = performance2.now() + options.timeoutMs;
|
|
1424
|
-
const buffer = session.pauseBuffer;
|
|
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);
|
|
1441
|
-
}
|
|
1442
|
-
throwBreakpointTimeout(options.timeoutMs);
|
|
1443
|
-
}
|
|
1444
|
-
async function waitForLivePause(session, options, deadlineMs) {
|
|
1445
|
-
const remainingMs = remainingUntil(deadlineMs);
|
|
1446
|
-
if (remainingMs <= 0) {
|
|
1447
|
-
throwBreakpointTimeout(options.timeoutMs);
|
|
1448
|
-
}
|
|
1449
|
-
session.pauseWaitGate.active = true;
|
|
1450
|
-
let receivedAtMs;
|
|
1451
|
-
let params;
|
|
1452
|
-
try {
|
|
1453
|
-
params = await session.client.waitFor("Debugger.paused", {
|
|
1454
|
-
timeoutMs: remainingMs,
|
|
1455
|
-
predicate: () => {
|
|
1456
|
-
receivedAtMs = performance2.now();
|
|
1457
|
-
return true;
|
|
1458
|
-
}
|
|
1459
|
-
});
|
|
1460
|
-
} finally {
|
|
1461
|
-
session.pauseWaitGate.active = false;
|
|
1462
|
-
}
|
|
1463
|
-
return toPauseEvent(params, receivedAtMs ?? performance2.now(), session.scripts);
|
|
1464
|
-
}
|
|
1465
|
-
|
|
1466
|
-
// src/snapshot/values.ts
|
|
1467
|
-
init_types();
|
|
1468
|
-
var DEFAULT_MAX_VALUE_LENGTH = 4096;
|
|
1469
|
-
function isPrimitive(value) {
|
|
1470
|
-
const t = typeof value;
|
|
1471
|
-
return t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol";
|
|
1472
|
-
}
|
|
1473
|
-
function formatPrimitive(value) {
|
|
1474
|
-
if (typeof value === "symbol") {
|
|
1475
|
-
return value.toString();
|
|
1476
|
-
}
|
|
1477
|
-
if (typeof value === "bigint") {
|
|
1478
|
-
return `${value.toString()}n`;
|
|
1479
|
-
}
|
|
1480
|
-
return String(value);
|
|
1307
|
+
return String(value);
|
|
1481
1308
|
}
|
|
1482
1309
|
function resolveMaxValueLength(value) {
|
|
1483
1310
|
if (value === void 0) {
|
|
@@ -1679,19 +1506,11 @@ async function capturePropertyChildren(session, described, depth, maxValueLength
|
|
|
1679
1506
|
}
|
|
1680
1507
|
}
|
|
1681
1508
|
|
|
1682
|
-
// src/snapshot/
|
|
1683
|
-
function
|
|
1684
|
-
|
|
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;
|
|
1509
|
+
// src/snapshot/exception.ts
|
|
1510
|
+
function asString2(value) {
|
|
1511
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1693
1512
|
}
|
|
1694
|
-
async function
|
|
1513
|
+
async function materializeObject(session, objectId, maxValueLength) {
|
|
1695
1514
|
try {
|
|
1696
1515
|
const properties = await captureProperties(
|
|
1697
1516
|
session,
|
|
@@ -1700,6 +1519,9 @@ async function renderObjectCapture(session, objectId, maxValueLength) {
|
|
|
1700
1519
|
MAX_VARIABLE_DEPTH,
|
|
1701
1520
|
maxValueLength
|
|
1702
1521
|
);
|
|
1522
|
+
if (properties.length === 0) {
|
|
1523
|
+
return void 0;
|
|
1524
|
+
}
|
|
1703
1525
|
const structured = {};
|
|
1704
1526
|
for (const variable of properties) {
|
|
1705
1527
|
structured[variable.name] = toStructuredValue(variable);
|
|
@@ -1709,10 +1531,102 @@ async function renderObjectCapture(session, objectId, maxValueLength) {
|
|
|
1709
1531
|
return void 0;
|
|
1710
1532
|
}
|
|
1711
1533
|
}
|
|
1712
|
-
function
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
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) {
|
|
1627
|
+
if (rendered === "{}" && original !== "Object") {
|
|
1628
|
+
return void 0;
|
|
1629
|
+
}
|
|
1716
1630
|
if (original.startsWith("Array(") && rendered === '{"length":0}') {
|
|
1717
1631
|
return "[]";
|
|
1718
1632
|
}
|
|
@@ -1781,12 +1695,73 @@ async function captureScopes(session, frame, maxValueLength) {
|
|
|
1781
1695
|
);
|
|
1782
1696
|
}
|
|
1783
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
|
+
|
|
1784
1758
|
// src/snapshot/capture.ts
|
|
1785
1759
|
async function captureSnapshot(session, pause, options = {}) {
|
|
1786
1760
|
const maxValueLength = resolveMaxValueLength(options.maxValueLength);
|
|
1787
1761
|
const top = pause.callFrames[0];
|
|
1788
1762
|
let topFrame;
|
|
1789
1763
|
let captures = [];
|
|
1764
|
+
let stack = [];
|
|
1790
1765
|
if (top) {
|
|
1791
1766
|
topFrame = {
|
|
1792
1767
|
functionName: top.functionName,
|
|
@@ -1799,14 +1774,31 @@ async function captureSnapshot(session, pause, options = {}) {
|
|
|
1799
1774
|
topFrame = { ...topFrame, scopes };
|
|
1800
1775
|
}
|
|
1801
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
|
+
});
|
|
1802
1782
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
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,
|
|
1806
1796
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1807
|
-
|
|
1808
|
-
captures
|
|
1797
|
+
captures: input.captures
|
|
1809
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 };
|
|
1810
1802
|
}
|
|
1811
1803
|
async function captureExpressions(session, callFrameId, captures, maxValueLength) {
|
|
1812
1804
|
if (captures === void 0 || captures.length === 0) {
|
|
@@ -1829,7 +1821,7 @@ async function captureExpression(session, callFrameId, expression, maxValueLengt
|
|
|
1829
1821
|
}
|
|
1830
1822
|
}
|
|
1831
1823
|
|
|
1832
|
-
// src/cli/commands/
|
|
1824
|
+
// src/cli/commands/exception.ts
|
|
1833
1825
|
init_types();
|
|
1834
1826
|
|
|
1835
1827
|
// src/cli/captureParser.ts
|
|
@@ -1912,123 +1904,820 @@ function splitCaptureExpressions(raw) {
|
|
|
1912
1904
|
return state.pieces;
|
|
1913
1905
|
}
|
|
1914
1906
|
|
|
1915
|
-
// src/cli/
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
const
|
|
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);
|
|
1919
1955
|
if (opts.json) {
|
|
1920
1956
|
writeJson(result);
|
|
1921
1957
|
} else {
|
|
1922
1958
|
writeHumanSnapshot(result);
|
|
1923
1959
|
}
|
|
1924
1960
|
}
|
|
1925
|
-
function
|
|
1961
|
+
function prepareExceptionCommand(opts) {
|
|
1926
1962
|
const target = resolveTarget(opts);
|
|
1927
|
-
|
|
1963
|
+
const stateRaw = (opts.type ?? "uncaught").trim().toLowerCase();
|
|
1964
|
+
if (!VALID_PAUSE_TYPES.includes(stateRaw)) {
|
|
1928
1965
|
throw new CfInspectorError(
|
|
1929
|
-
"
|
|
1930
|
-
|
|
1966
|
+
"INVALID_PAUSE_TYPE",
|
|
1967
|
+
`--type must be one of ${VALID_PAUSE_TYPES.join(", ")} (received "${stateRaw}")`
|
|
1931
1968
|
);
|
|
1932
1969
|
}
|
|
1933
|
-
const timeoutSec = parsePositiveInt(opts.timeout, "--timeout") ??
|
|
1970
|
+
const timeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_EXCEPTION_TIMEOUT_SEC;
|
|
1934
1971
|
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
1935
|
-
const
|
|
1972
|
+
const stackDepth = parsePositiveInt(opts.stackDepth, "--stack-depth");
|
|
1936
1973
|
return {
|
|
1937
1974
|
target,
|
|
1938
|
-
|
|
1975
|
+
state: stateRaw,
|
|
1939
1976
|
captures: parseCaptureList(opts.capture),
|
|
1940
1977
|
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
1941
1978
|
timeoutMs: timeoutSec * 1e3,
|
|
1942
|
-
...
|
|
1943
|
-
...
|
|
1979
|
+
...maxValueLength === void 0 ? {} : { maxValueLength },
|
|
1980
|
+
...stackDepth === void 0 ? {} : { stackDepth },
|
|
1981
|
+
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
1944
1982
|
};
|
|
1945
1983
|
}
|
|
1946
|
-
async function
|
|
1984
|
+
async function runExceptionCommand(command, opts) {
|
|
1947
1985
|
return await withSession(command.target, async (session) => {
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
captures: command.captures,
|
|
1966
|
-
includeScopes: opts.includeScopes === true,
|
|
1967
|
-
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength }
|
|
1968
|
-
});
|
|
1969
|
-
if (opts.keepPaused === true) {
|
|
1970
|
-
return withPausedDuration(snapshot, null);
|
|
1971
|
-
}
|
|
1972
|
-
return await resumeAfterSnapshot(session, snapshot, pausedStartedAt);
|
|
1973
|
-
});
|
|
1974
|
-
}
|
|
1975
|
-
async function waitForCommandPause(session, opts, handles, timeoutMs) {
|
|
1976
|
-
let warnedUnmatchedPause = false;
|
|
1977
|
-
return await waitForPause(session, {
|
|
1978
|
-
timeoutMs,
|
|
1979
|
-
breakpointIds: handles.map((h) => h.breakpointId),
|
|
1980
|
-
unmatchedPausePolicy: opts.failOnUnmatchedPause === true ? "fail" : "wait-for-resume",
|
|
1981
|
-
onUnmatchedPause: (unmatchedPause) => {
|
|
1982
|
-
if (warnedUnmatchedPause || opts.failOnUnmatchedPause === true) {
|
|
1983
|
-
return;
|
|
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);
|
|
1984
2003
|
}
|
|
1985
|
-
|
|
1986
|
-
|
|
2004
|
+
return await resumeAfterException(session, snapshot, pausedStartedAt);
|
|
2005
|
+
} finally {
|
|
2006
|
+
await disablePauseOnExceptionsBestEffort(session);
|
|
1987
2007
|
}
|
|
1988
2008
|
});
|
|
1989
2009
|
}
|
|
1990
|
-
async function
|
|
2010
|
+
async function resumeAfterException(session, snapshot, pausedStartedAt) {
|
|
1991
2011
|
try {
|
|
1992
2012
|
await resume(session);
|
|
1993
2013
|
return withPausedDuration(snapshot, roundDurationMs(performance3.now() - pausedStartedAt));
|
|
1994
2014
|
} catch {
|
|
1995
|
-
|
|
1996
|
-
"[cf-inspector] warning: Debugger.resume failed after
|
|
2015
|
+
process5.stderr.write(
|
|
2016
|
+
"[cf-inspector] warning: Debugger.resume failed after exception capture; pausedDurationMs is unknown.\n"
|
|
1997
2017
|
);
|
|
1998
2018
|
return withPausedDuration(snapshot, null);
|
|
1999
2019
|
}
|
|
2000
2020
|
}
|
|
2021
|
+
async function disablePauseOnExceptionsBestEffort(session) {
|
|
2022
|
+
try {
|
|
2023
|
+
await setPauseOnExceptions(session, "none");
|
|
2024
|
+
} catch {
|
|
2025
|
+
}
|
|
2026
|
+
}
|
|
2001
2027
|
|
|
2002
|
-
// src/cli/
|
|
2003
|
-
|
|
2004
|
-
|
|
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
|
+
}
|
|
2005
2041
|
}
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
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("");
|
|
2015
2068
|
}
|
|
2016
|
-
function
|
|
2017
|
-
const
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
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("");
|
|
2026
2079
|
}
|
|
2027
|
-
function
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
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
|
+
|
|
2347
|
+
// src/cli/commands/snapshot.ts
|
|
2348
|
+
import { performance as performance4 } from "perf_hooks";
|
|
2349
|
+
import process8 from "process";
|
|
2350
|
+
init_types();
|
|
2351
|
+
async function handleSnapshot(opts) {
|
|
2352
|
+
const prepared = prepareSnapshotCommand(opts);
|
|
2353
|
+
const result = await runSnapshotCommand(prepared, opts);
|
|
2354
|
+
if (opts.json) {
|
|
2355
|
+
writeJson(result);
|
|
2356
|
+
} else {
|
|
2357
|
+
writeHumanSnapshot(result);
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
function prepareSnapshotCommand(opts) {
|
|
2361
|
+
const target = resolveTarget(opts);
|
|
2362
|
+
if (opts.bp.length === 0) {
|
|
2363
|
+
throw new CfInspectorError(
|
|
2364
|
+
"INVALID_BREAKPOINT",
|
|
2365
|
+
"At least one --bp <file:line> is required."
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
2368
|
+
const timeoutSec = parsePositiveInt(opts.timeout, "--timeout") ?? DEFAULT_BREAKPOINT_TIMEOUT_SEC;
|
|
2369
|
+
const maxValueLength = parsePositiveInt(opts.maxValueLength, "--max-value-length");
|
|
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");
|
|
2373
|
+
return {
|
|
2374
|
+
target,
|
|
2375
|
+
breakpoints: opts.bp.map((spec) => parseBreakpointSpec(spec)),
|
|
2376
|
+
captures: parseCaptureList(opts.capture),
|
|
2377
|
+
remoteRoot: parseRemoteRoot(opts.remoteRoot),
|
|
2378
|
+
timeoutMs: timeoutSec * 1e3,
|
|
2379
|
+
...condition === void 0 ? {} : { condition },
|
|
2380
|
+
...maxValueLength === void 0 ? {} : { maxValueLength },
|
|
2381
|
+
...hitCount === void 0 ? {} : { hitCount },
|
|
2382
|
+
...stackDepth === void 0 ? {} : { stackDepth },
|
|
2383
|
+
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
2384
|
+
};
|
|
2385
|
+
}
|
|
2386
|
+
async function runSnapshotCommand(command, opts) {
|
|
2387
|
+
return await withSession(command.target, async (session) => {
|
|
2388
|
+
if (command.condition !== void 0) {
|
|
2389
|
+
await validateExpression(session, command.condition);
|
|
2390
|
+
}
|
|
2391
|
+
const handles = await Promise.all(
|
|
2392
|
+
command.breakpoints.map(
|
|
2393
|
+
(bp) => setBreakpoint(session, {
|
|
2394
|
+
file: bp.file,
|
|
2395
|
+
line: bp.line,
|
|
2396
|
+
remoteRoot: command.remoteRoot,
|
|
2397
|
+
...command.condition === void 0 ? {} : { condition: command.condition },
|
|
2398
|
+
...command.hitCount === void 0 ? {} : { hitCount: command.hitCount }
|
|
2399
|
+
})
|
|
2400
|
+
)
|
|
2401
|
+
);
|
|
2402
|
+
warnOnUnboundBreakpoints(handles);
|
|
2403
|
+
const pause = await waitForCommandPause(session, opts, handles, command.timeoutMs);
|
|
2404
|
+
const pausedStartedAt = pause.receivedAtMs ?? performance4.now();
|
|
2405
|
+
const snapshot = await captureSnapshot(session, pause, {
|
|
2406
|
+
captures: command.captures,
|
|
2407
|
+
includeScopes: opts.includeScopes === true,
|
|
2408
|
+
...command.maxValueLength === void 0 ? {} : { maxValueLength: command.maxValueLength },
|
|
2409
|
+
...command.stackDepth === void 0 ? {} : { stackDepth: command.stackDepth },
|
|
2410
|
+
stackCaptures: command.stackCaptures
|
|
2411
|
+
});
|
|
2412
|
+
if (opts.keepPaused === true) {
|
|
2413
|
+
return withPausedDuration(snapshot, null);
|
|
2414
|
+
}
|
|
2415
|
+
return await resumeAfterSnapshot(session, snapshot, pausedStartedAt);
|
|
2416
|
+
});
|
|
2417
|
+
}
|
|
2418
|
+
async function waitForCommandPause(session, opts, handles, timeoutMs) {
|
|
2419
|
+
let warnedUnmatchedPause = false;
|
|
2420
|
+
return await waitForPause(session, {
|
|
2421
|
+
timeoutMs,
|
|
2422
|
+
breakpointIds: handles.map((h) => h.breakpointId),
|
|
2423
|
+
unmatchedPausePolicy: opts.failOnUnmatchedPause === true ? "fail" : "wait-for-resume",
|
|
2424
|
+
onUnmatchedPause: (unmatchedPause) => {
|
|
2425
|
+
if (warnedUnmatchedPause || opts.failOnUnmatchedPause === true) {
|
|
2426
|
+
return;
|
|
2427
|
+
}
|
|
2428
|
+
warnedUnmatchedPause = true;
|
|
2429
|
+
warnOnUnmatchedPause(unmatchedPause);
|
|
2430
|
+
}
|
|
2431
|
+
});
|
|
2432
|
+
}
|
|
2433
|
+
async function resumeAfterSnapshot(session, snapshot, pausedStartedAt) {
|
|
2434
|
+
try {
|
|
2435
|
+
await resume(session);
|
|
2436
|
+
return withPausedDuration(snapshot, roundDurationMs(performance4.now() - pausedStartedAt));
|
|
2437
|
+
} catch {
|
|
2438
|
+
process8.stderr.write(
|
|
2439
|
+
"[cf-inspector] warning: Debugger.resume failed after snapshot; pausedDurationMs is unknown.\n"
|
|
2440
|
+
);
|
|
2441
|
+
return withPausedDuration(snapshot, null);
|
|
2442
|
+
}
|
|
2443
|
+
}
|
|
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
|
+
|
|
2675
|
+
// src/cli/program.ts
|
|
2676
|
+
function applyTargetOptions(cmd) {
|
|
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");
|
|
2678
|
+
}
|
|
2679
|
+
var collectStrings = (value, prev = []) => [
|
|
2680
|
+
...prev,
|
|
2681
|
+
value
|
|
2682
|
+
];
|
|
2683
|
+
async function main(argv) {
|
|
2684
|
+
const program = new Command();
|
|
2685
|
+
program.name("cf-inspector").description("Drive a Node.js inspector from the command line \u2014 set breakpoints, capture snapshots, evaluate expressions");
|
|
2686
|
+
registerSnapshot(program);
|
|
2687
|
+
registerLog(program);
|
|
2688
|
+
registerWatch(program);
|
|
2689
|
+
registerException(program);
|
|
2690
|
+
registerEval(program);
|
|
2691
|
+
registerListScripts(program);
|
|
2692
|
+
registerAttach(program);
|
|
2693
|
+
await program.parseAsync([...argv]);
|
|
2694
|
+
}
|
|
2695
|
+
function registerSnapshot(program) {
|
|
2696
|
+
applyTargetOptions(
|
|
2697
|
+
program.command("snapshot").description("Set a breakpoint, wait for it to hit, capture expressions, and resume")
|
|
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) => {
|
|
2699
|
+
await handleSnapshot(opts);
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
function registerLog(program) {
|
|
2703
|
+
applyTargetOptions(
|
|
2704
|
+
program.command("log").description("Stream a non-pausing logpoint: log an expression each time a line executes")
|
|
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) => {
|
|
2706
|
+
await handleLog(opts);
|
|
2707
|
+
});
|
|
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);
|
|
2032
2721
|
});
|
|
2033
2722
|
}
|
|
2034
2723
|
function registerEval(program) {
|
|
@@ -2056,20 +2745,20 @@ function registerAttach(program) {
|
|
|
2056
2745
|
// src/cli.ts
|
|
2057
2746
|
init_types();
|
|
2058
2747
|
try {
|
|
2059
|
-
await main(
|
|
2748
|
+
await main(process10.argv);
|
|
2060
2749
|
} catch (err) {
|
|
2061
2750
|
if (err instanceof CfInspectorError) {
|
|
2062
|
-
|
|
2751
|
+
process10.stderr.write(`Error [${err.code}]: ${err.message}
|
|
2063
2752
|
`);
|
|
2064
2753
|
if (err.detail !== void 0) {
|
|
2065
|
-
|
|
2754
|
+
process10.stderr.write(` detail: ${err.detail}
|
|
2066
2755
|
`);
|
|
2067
2756
|
}
|
|
2068
|
-
|
|
2757
|
+
process10.exit(1);
|
|
2069
2758
|
}
|
|
2070
2759
|
const message = err instanceof Error ? err.message : String(err);
|
|
2071
|
-
|
|
2760
|
+
process10.stderr.write(`Error: ${message}
|
|
2072
2761
|
`);
|
|
2073
|
-
|
|
2762
|
+
process10.exit(1);
|
|
2074
2763
|
}
|
|
2075
2764
|
//# sourceMappingURL=cli.js.map
|