@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/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DebuggerHandle } from '@saptools/cf-debugger';
|
|
2
2
|
|
|
3
|
-
type CfInspectorErrorCode = "INVALID_ARGUMENT" | "INVALID_BREAKPOINT" | "INVALID_REMOTE_ROOT" | "INVALID_EXPRESSION" | "BREAKPOINT_DID_NOT_BIND" | "INSPECTOR_DISCOVERY_FAILED" | "INSPECTOR_CONNECTION_FAILED" | "CDP_REQUEST_FAILED" | "BREAKPOINT_NOT_HIT" | "UNRELATED_PAUSE" | "UNRELATED_PAUSE_TIMEOUT" | "EVALUATION_FAILED" | "MISSING_TARGET" | "ABORTED";
|
|
3
|
+
type CfInspectorErrorCode = "INVALID_ARGUMENT" | "INVALID_BREAKPOINT" | "INVALID_REMOTE_ROOT" | "INVALID_EXPRESSION" | "INVALID_HIT_COUNT" | "INVALID_PAUSE_TYPE" | "BREAKPOINT_DID_NOT_BIND" | "INSPECTOR_DISCOVERY_FAILED" | "INSPECTOR_CONNECTION_FAILED" | "CDP_REQUEST_FAILED" | "BREAKPOINT_NOT_HIT" | "UNRELATED_PAUSE" | "UNRELATED_PAUSE_TIMEOUT" | "EVALUATION_FAILED" | "MISSING_TARGET" | "ABORTED";
|
|
4
4
|
declare class CfInspectorError extends Error {
|
|
5
5
|
readonly code: CfInspectorErrorCode;
|
|
6
6
|
readonly detail?: string;
|
|
@@ -55,6 +55,7 @@ interface PauseEvent {
|
|
|
55
55
|
readonly hitBreakpoints: readonly string[];
|
|
56
56
|
readonly callFrames: readonly CallFrameInfo[];
|
|
57
57
|
readonly receivedAtMs?: number;
|
|
58
|
+
readonly data?: unknown;
|
|
58
59
|
}
|
|
59
60
|
interface VariableSnapshot {
|
|
60
61
|
readonly name: string;
|
|
@@ -72,6 +73,7 @@ interface FrameSnapshot {
|
|
|
72
73
|
readonly line: number;
|
|
73
74
|
readonly column: number;
|
|
74
75
|
readonly scopes?: readonly ScopeSnapshot[];
|
|
76
|
+
readonly captures?: readonly CapturedExpression[];
|
|
75
77
|
}
|
|
76
78
|
interface CapturedExpression {
|
|
77
79
|
readonly expression: string;
|
|
@@ -79,16 +81,35 @@ interface CapturedExpression {
|
|
|
79
81
|
readonly type?: string;
|
|
80
82
|
readonly error?: string;
|
|
81
83
|
}
|
|
84
|
+
interface ExceptionSnapshot {
|
|
85
|
+
readonly value?: string;
|
|
86
|
+
readonly type?: string;
|
|
87
|
+
readonly description?: string;
|
|
88
|
+
readonly error?: string;
|
|
89
|
+
}
|
|
82
90
|
interface SnapshotCaptureResult {
|
|
83
91
|
readonly reason: string;
|
|
84
92
|
readonly hitBreakpoints: readonly string[];
|
|
85
93
|
readonly capturedAt: string;
|
|
86
94
|
readonly topFrame?: FrameSnapshot;
|
|
87
95
|
readonly captures: readonly CapturedExpression[];
|
|
96
|
+
readonly stack?: readonly FrameSnapshot[];
|
|
97
|
+
readonly exception?: ExceptionSnapshot;
|
|
88
98
|
}
|
|
89
99
|
interface SnapshotResult extends SnapshotCaptureResult {
|
|
90
100
|
readonly pausedDurationMs: number | null;
|
|
91
101
|
}
|
|
102
|
+
interface WatchEvent {
|
|
103
|
+
readonly ts: string;
|
|
104
|
+
readonly at: string;
|
|
105
|
+
readonly hit: number;
|
|
106
|
+
readonly reason: string;
|
|
107
|
+
readonly hitBreakpoints: readonly string[];
|
|
108
|
+
readonly topFrame?: FrameSnapshot;
|
|
109
|
+
readonly captures: readonly CapturedExpression[];
|
|
110
|
+
readonly stack?: readonly FrameSnapshot[];
|
|
111
|
+
readonly exception?: ExceptionSnapshot;
|
|
112
|
+
}
|
|
92
113
|
interface ScriptInfo {
|
|
93
114
|
readonly scriptId: string;
|
|
94
115
|
readonly url: string;
|
|
@@ -218,20 +239,25 @@ interface CdpProperty {
|
|
|
218
239
|
interface SetBreakpointInput extends BreakpointLocation {
|
|
219
240
|
readonly remoteRoot?: RemoteRootSetting;
|
|
220
241
|
readonly condition?: string;
|
|
242
|
+
readonly hitCount?: number;
|
|
221
243
|
}
|
|
222
244
|
interface WaitForPauseOptions {
|
|
223
245
|
readonly timeoutMs: number;
|
|
224
246
|
readonly breakpointIds?: readonly string[];
|
|
247
|
+
readonly pauseReasons?: readonly string[];
|
|
225
248
|
readonly unmatchedPausePolicy?: "wait-for-resume" | "fail";
|
|
226
249
|
readonly onUnmatchedPause?: (pause: PauseEvent) => void;
|
|
227
250
|
}
|
|
228
251
|
|
|
252
|
+
declare function buildHitCountedCondition(hitCount: number, counterKey: string, userCondition: string | undefined): string;
|
|
229
253
|
declare function setBreakpoint(session: InspectorSession, input: SetBreakpointInput): Promise<BreakpointHandle>;
|
|
230
254
|
declare function removeBreakpoint(session: InspectorSession, breakpointId: string): Promise<void>;
|
|
231
255
|
|
|
232
256
|
declare function waitForPause(session: InspectorSession, options: WaitForPauseOptions): Promise<PauseEvent>;
|
|
233
257
|
|
|
258
|
+
type PauseOnExceptionsState = "none" | "uncaught" | "caught" | "all";
|
|
234
259
|
declare function resume(session: InspectorSession): Promise<void>;
|
|
260
|
+
declare function setPauseOnExceptions(session: InspectorSession, state: PauseOnExceptionsState): Promise<void>;
|
|
235
261
|
declare function evaluateOnFrame(session: InspectorSession, callFrameId: string, expression: string): Promise<CdpEvalResult>;
|
|
236
262
|
declare function evaluateGlobal(session: InspectorSession, expression: string): Promise<CdpEvalResult>;
|
|
237
263
|
declare function listScripts(session: InspectorSession): readonly ScriptInfo[];
|
|
@@ -244,10 +270,26 @@ interface CaptureSnapshotOptions {
|
|
|
244
270
|
readonly captures?: readonly string[];
|
|
245
271
|
readonly includeScopes?: boolean;
|
|
246
272
|
readonly maxValueLength?: number;
|
|
273
|
+
readonly stackDepth?: number;
|
|
274
|
+
readonly stackCaptures?: readonly string[];
|
|
247
275
|
}
|
|
248
276
|
declare function captureSnapshot(session: InspectorSession, pause: PauseEvent, options?: CaptureSnapshotOptions): Promise<SnapshotCaptureResult>;
|
|
249
277
|
|
|
250
|
-
declare function
|
|
278
|
+
declare function captureException(session: InspectorSession, pause: PauseEvent, maxValueLength: number): Promise<ExceptionSnapshot | undefined>;
|
|
279
|
+
|
|
280
|
+
interface WalkStackOptions {
|
|
281
|
+
readonly stackDepth: number;
|
|
282
|
+
readonly stackCaptures: readonly string[];
|
|
283
|
+
readonly maxValueLength: number;
|
|
284
|
+
}
|
|
285
|
+
declare function walkStack(session: InspectorSession, callFrames: readonly CallFrameInfo[], options: WalkStackOptions): Promise<readonly FrameSnapshot[]>;
|
|
286
|
+
|
|
287
|
+
interface LogpointConditionOptions {
|
|
288
|
+
readonly predicate?: string;
|
|
289
|
+
readonly hitCount?: number;
|
|
290
|
+
readonly counterKey?: string;
|
|
291
|
+
}
|
|
292
|
+
declare function buildLogpointCondition(sentinel: string, expression: string, options?: LogpointConditionOptions): string;
|
|
251
293
|
|
|
252
294
|
interface LogpointEvent {
|
|
253
295
|
readonly ts: string;
|
|
@@ -257,11 +299,15 @@ interface LogpointEvent {
|
|
|
257
299
|
readonly raw?: string;
|
|
258
300
|
}
|
|
259
301
|
|
|
302
|
+
type LogpointStopReason = "duration" | "signal" | "transport-closed" | "max-events";
|
|
260
303
|
interface LogpointStreamOptions {
|
|
261
304
|
readonly location: BreakpointLocation;
|
|
262
305
|
readonly expression: string;
|
|
263
306
|
readonly remoteRoot?: RemoteRootSetting;
|
|
264
307
|
readonly durationMs?: number;
|
|
308
|
+
readonly maxEvents?: number;
|
|
309
|
+
readonly hitCount?: number;
|
|
310
|
+
readonly condition?: string;
|
|
265
311
|
readonly signal?: AbortSignal;
|
|
266
312
|
readonly onEvent: (event: LogpointEvent) => void;
|
|
267
313
|
readonly onBreakpointSet?: (handle: BreakpointHandle) => void;
|
|
@@ -270,7 +316,7 @@ interface LogpointStreamResult {
|
|
|
270
316
|
readonly handle: BreakpointHandle;
|
|
271
317
|
readonly sentinel: string;
|
|
272
318
|
readonly emitted: number;
|
|
273
|
-
readonly stoppedReason:
|
|
319
|
+
readonly stoppedReason: LogpointStopReason;
|
|
274
320
|
}
|
|
275
321
|
declare function streamLogpoint(session: InspectorSession, options: LogpointStreamOptions): Promise<LogpointStreamResult>;
|
|
276
322
|
|
|
@@ -291,4 +337,4 @@ interface OpenedTunnel {
|
|
|
291
337
|
}
|
|
292
338
|
declare function openCfTunnel(target: TunnelTarget): Promise<OpenedTunnel>;
|
|
293
339
|
|
|
294
|
-
export { type BreakpointHandle, type BreakpointLocation, type CallFrameInfo, type CaptureSnapshotOptions, type CapturedExpression, CfInspectorError, type CfInspectorErrorCode, type DebuggerState, type FrameSnapshot, type InspectorConnectOptions, type InspectorSession, type InspectorTarget, type LogpointEvent, type LogpointStreamOptions, type LogpointStreamResult, type OpenedTunnel, type PauseEvent, type RemoteRootSetting, type ResolvedLocation, type ScopeInfo, type ScopeSnapshot, type ScriptInfo, type SetBreakpointInput, type SnapshotCaptureResult, type SnapshotResult, type TunnelTarget, type VariableSnapshot, type WaitForPauseOptions, buildBreakpointUrlRegex, buildLogpointCondition, captureSnapshot, connectInspector, discoverInspectorTargets, evaluateGlobal, evaluateOnFrame, fetchInspectorVersion, getProperties, listScripts, openCfTunnel, parseBreakpointSpec, parseRemoteRoot, removeBreakpoint, resume, setBreakpoint, streamLogpoint, validateExpression, waitForPause };
|
|
340
|
+
export { type BreakpointHandle, type BreakpointLocation, type CallFrameInfo, type CaptureSnapshotOptions, type CapturedExpression, CfInspectorError, type CfInspectorErrorCode, type DebuggerState, type ExceptionSnapshot, type FrameSnapshot, type InspectorConnectOptions, type InspectorSession, type InspectorTarget, type LogpointConditionOptions, type LogpointEvent, type LogpointStopReason, type LogpointStreamOptions, type LogpointStreamResult, type OpenedTunnel, type PauseEvent, type PauseOnExceptionsState, type RemoteRootSetting, type ResolvedLocation, type ScopeInfo, type ScopeSnapshot, type ScriptInfo, type SetBreakpointInput, type SnapshotCaptureResult, type SnapshotResult, type TunnelTarget, type VariableSnapshot, type WaitForPauseOptions, type WalkStackOptions, type WatchEvent, buildBreakpointUrlRegex, buildHitCountedCondition, buildLogpointCondition, captureException, captureSnapshot, connectInspector, discoverInspectorTargets, evaluateGlobal, evaluateOnFrame, fetchInspectorVersion, getProperties, listScripts, openCfTunnel, parseBreakpointSpec, parseRemoteRoot, removeBreakpoint, resume, setBreakpoint, setPauseOnExceptions, streamLogpoint, validateExpression, waitForPause, walkStack };
|
package/dist/index.js
CHANGED
|
@@ -338,12 +338,13 @@ function toCallFrames(value, scripts) {
|
|
|
338
338
|
});
|
|
339
339
|
}
|
|
340
340
|
function toPauseEvent(params, receivedAtMs, scripts) {
|
|
341
|
-
|
|
341
|
+
const base = {
|
|
342
342
|
reason: asString(params.reason),
|
|
343
343
|
hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
|
|
344
344
|
callFrames: toCallFrames(params.callFrames, scripts),
|
|
345
345
|
receivedAtMs
|
|
346
346
|
};
|
|
347
|
+
return params.data === void 0 ? base : { ...base, data: params.data };
|
|
347
348
|
}
|
|
348
349
|
function topFrameLocation(pause) {
|
|
349
350
|
const top = pause.callFrames[0];
|
|
@@ -362,6 +363,46 @@ function pauseDetail(pause) {
|
|
|
362
363
|
}
|
|
363
364
|
|
|
364
365
|
// src/inspector/breakpoints.ts
|
|
366
|
+
var HITS_GLOBAL = "globalThis.__CFI_HITS";
|
|
367
|
+
var counterKeyCounter = 0;
|
|
368
|
+
function nextCounterKey(file, line) {
|
|
369
|
+
counterKeyCounter += 1;
|
|
370
|
+
return `${file}:${line.toString()}:${counterKeyCounter.toString()}`;
|
|
371
|
+
}
|
|
372
|
+
function validateHitCount(hitCount) {
|
|
373
|
+
if (hitCount === void 0) {
|
|
374
|
+
return void 0;
|
|
375
|
+
}
|
|
376
|
+
if (!Number.isInteger(hitCount) || hitCount <= 0) {
|
|
377
|
+
throw new CfInspectorError(
|
|
378
|
+
"INVALID_HIT_COUNT",
|
|
379
|
+
`hitCount must be a positive integer, received: ${hitCount.toString()}`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
return hitCount;
|
|
383
|
+
}
|
|
384
|
+
function buildHitCountedCondition(hitCount, counterKey, userCondition) {
|
|
385
|
+
const keyLiteral = JSON.stringify(counterKey);
|
|
386
|
+
const baseCondition = userCondition !== void 0 && userCondition.trim().length > 0 ? `(${userCondition})` : "true";
|
|
387
|
+
return [
|
|
388
|
+
"(function(){",
|
|
389
|
+
`var m=(${HITS_GLOBAL}=${HITS_GLOBAL}||{});`,
|
|
390
|
+
`var k=${keyLiteral};`,
|
|
391
|
+
"m[k]=(m[k]||0)+1;",
|
|
392
|
+
`if(m[k]<${hitCount.toString()})return false;`,
|
|
393
|
+
`return ${baseCondition};`,
|
|
394
|
+
"})()"
|
|
395
|
+
].join("");
|
|
396
|
+
}
|
|
397
|
+
function resolveCondition(input) {
|
|
398
|
+
const condition = input.condition;
|
|
399
|
+
const hitCount = validateHitCount(input.hitCount);
|
|
400
|
+
if (hitCount === void 0) {
|
|
401
|
+
return condition !== void 0 && condition.length > 0 ? condition : void 0;
|
|
402
|
+
}
|
|
403
|
+
const counterKey = nextCounterKey(input.file, input.line);
|
|
404
|
+
return buildHitCountedCondition(hitCount, counterKey, condition);
|
|
405
|
+
}
|
|
365
406
|
async function setBreakpoint(session, input) {
|
|
366
407
|
const remoteRoot = input.remoteRoot ?? { kind: "none" };
|
|
367
408
|
const urlRegex = buildBreakpointUrlRegex({ file: input.file, remoteRoot });
|
|
@@ -369,8 +410,9 @@ async function setBreakpoint(session, input) {
|
|
|
369
410
|
lineNumber: input.line - 1,
|
|
370
411
|
urlRegex
|
|
371
412
|
};
|
|
372
|
-
|
|
373
|
-
|
|
413
|
+
const condition = resolveCondition(input);
|
|
414
|
+
if (condition !== void 0) {
|
|
415
|
+
params["condition"] = condition;
|
|
374
416
|
}
|
|
375
417
|
const result = await session.client.send(
|
|
376
418
|
"Debugger.setBreakpointByUrl",
|
|
@@ -516,7 +558,10 @@ async function fetchInspectorVersion(host, port, timeoutMs) {
|
|
|
516
558
|
// src/inspector/pause.ts
|
|
517
559
|
init_types();
|
|
518
560
|
import { performance } from "perf_hooks";
|
|
519
|
-
function pauseMatches(pause, breakpointIds) {
|
|
561
|
+
function pauseMatches(pause, breakpointIds, pauseReasons) {
|
|
562
|
+
if (pauseReasons !== void 0 && pauseReasons.length > 0) {
|
|
563
|
+
return pauseReasons.includes(pause.reason);
|
|
564
|
+
}
|
|
520
565
|
if (breakpointIds === void 0 || breakpointIds.length === 0) {
|
|
521
566
|
return true;
|
|
522
567
|
}
|
|
@@ -584,13 +629,13 @@ async function waitForPause(session, options) {
|
|
|
584
629
|
if (buffered === void 0) {
|
|
585
630
|
continue;
|
|
586
631
|
}
|
|
587
|
-
if (pauseMatches(buffered, options.breakpointIds)) {
|
|
632
|
+
if (pauseMatches(buffered, options.breakpointIds, options.pauseReasons)) {
|
|
588
633
|
return buffered;
|
|
589
634
|
}
|
|
590
635
|
await handleUnmatchedPause(session, buffered, options, deadlineMs);
|
|
591
636
|
}
|
|
592
637
|
const pause = await waitForLivePause(session, options, deadlineMs);
|
|
593
|
-
if (pauseMatches(pause, options.breakpointIds)) {
|
|
638
|
+
if (pauseMatches(pause, options.breakpointIds, options.pauseReasons)) {
|
|
594
639
|
return pause;
|
|
595
640
|
}
|
|
596
641
|
await handleUnmatchedPause(session, pause, options, deadlineMs);
|
|
@@ -624,6 +669,9 @@ init_types();
|
|
|
624
669
|
async function resume(session) {
|
|
625
670
|
await session.client.send("Debugger.resume");
|
|
626
671
|
}
|
|
672
|
+
async function setPauseOnExceptions(session, state) {
|
|
673
|
+
await session.client.send("Debugger.setPauseOnExceptions", { state });
|
|
674
|
+
}
|
|
627
675
|
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
628
676
|
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
629
677
|
callFrameId,
|
|
@@ -1174,6 +1222,93 @@ async function capturePropertyChildren(session, described, depth, maxValueLength
|
|
|
1174
1222
|
}
|
|
1175
1223
|
}
|
|
1176
1224
|
|
|
1225
|
+
// src/snapshot/exception.ts
|
|
1226
|
+
function asString2(value) {
|
|
1227
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1228
|
+
}
|
|
1229
|
+
async function materializeObject(session, objectId, maxValueLength) {
|
|
1230
|
+
try {
|
|
1231
|
+
const properties = await captureProperties(
|
|
1232
|
+
session,
|
|
1233
|
+
objectId,
|
|
1234
|
+
MAX_SCOPE_VARIABLES,
|
|
1235
|
+
MAX_VARIABLE_DEPTH,
|
|
1236
|
+
maxValueLength
|
|
1237
|
+
);
|
|
1238
|
+
if (properties.length === 0) {
|
|
1239
|
+
return void 0;
|
|
1240
|
+
}
|
|
1241
|
+
const structured = {};
|
|
1242
|
+
for (const variable of properties) {
|
|
1243
|
+
structured[variable.name] = toStructuredValue(variable);
|
|
1244
|
+
}
|
|
1245
|
+
return JSON.stringify(structured);
|
|
1246
|
+
} catch {
|
|
1247
|
+
return void 0;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
async function readPropertyDescription(session, objectId, name) {
|
|
1251
|
+
try {
|
|
1252
|
+
const properties = await getProperties(session, objectId);
|
|
1253
|
+
for (const prop of properties) {
|
|
1254
|
+
if (prop.name !== name) {
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
1257
|
+
const value = prop.value;
|
|
1258
|
+
if (value === void 0) {
|
|
1259
|
+
continue;
|
|
1260
|
+
}
|
|
1261
|
+
if (typeof value.value === "string") {
|
|
1262
|
+
return value.value;
|
|
1263
|
+
}
|
|
1264
|
+
if (typeof value.description === "string") {
|
|
1265
|
+
return value.description;
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
return void 0;
|
|
1269
|
+
} catch {
|
|
1270
|
+
return void 0;
|
|
1271
|
+
}
|
|
1272
|
+
}
|
|
1273
|
+
async function captureException(session, pause, maxValueLength) {
|
|
1274
|
+
if (pause.reason !== "exception" && pause.reason !== "promiseRejection") {
|
|
1275
|
+
return void 0;
|
|
1276
|
+
}
|
|
1277
|
+
const data = pause.data;
|
|
1278
|
+
if (typeof data !== "object" || data === null) {
|
|
1279
|
+
return { error: "no exception data attached" };
|
|
1280
|
+
}
|
|
1281
|
+
const candidate = data;
|
|
1282
|
+
const type = asString2(candidate.type);
|
|
1283
|
+
const description = asString2(candidate.description);
|
|
1284
|
+
if (typeof candidate.value === "string") {
|
|
1285
|
+
return buildResult(type, description, JSON.stringify(candidate.value), maxValueLength);
|
|
1286
|
+
}
|
|
1287
|
+
if (typeof candidate.value === "number" || typeof candidate.value === "boolean") {
|
|
1288
|
+
return buildResult(type, description, String(candidate.value), maxValueLength);
|
|
1289
|
+
}
|
|
1290
|
+
const objectId = asString2(candidate.objectId);
|
|
1291
|
+
if (objectId === void 0) {
|
|
1292
|
+
if (description !== void 0) {
|
|
1293
|
+
return buildResult(type, description, description, maxValueLength);
|
|
1294
|
+
}
|
|
1295
|
+
return { error: "exception data has no objectId or value" };
|
|
1296
|
+
}
|
|
1297
|
+
const message = await readPropertyDescription(session, objectId, "message");
|
|
1298
|
+
const rendered = await materializeObject(session, objectId, maxValueLength);
|
|
1299
|
+
if (rendered !== void 0) {
|
|
1300
|
+
const result = buildResult(type, description, rendered, maxValueLength);
|
|
1301
|
+
return message === void 0 ? result : { ...result, description: limitValueLength(message, maxValueLength) };
|
|
1302
|
+
}
|
|
1303
|
+
return buildResult(type, description, description ?? "[exception]", maxValueLength);
|
|
1304
|
+
}
|
|
1305
|
+
function buildResult(type, description, value, maxValueLength) {
|
|
1306
|
+
const safeValue = limitValueLength(value, maxValueLength);
|
|
1307
|
+
const base = { value: safeValue };
|
|
1308
|
+
const withType = type === void 0 ? base : { ...base, type };
|
|
1309
|
+
return description === void 0 ? withType : { ...withType, description: limitValueLength(description, maxValueLength) };
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1177
1312
|
// src/snapshot/objects.ts
|
|
1178
1313
|
function objectIdFromEvalResult(result) {
|
|
1179
1314
|
const inner = result.result;
|
|
@@ -1276,12 +1411,73 @@ async function captureScopes(session, frame, maxValueLength) {
|
|
|
1276
1411
|
);
|
|
1277
1412
|
}
|
|
1278
1413
|
|
|
1414
|
+
// src/snapshot/stack.ts
|
|
1415
|
+
var DEFAULT_STACK_DEPTH = 1;
|
|
1416
|
+
var MAX_STACK_DEPTH = 64;
|
|
1417
|
+
function clampDepth(depth, frameCount) {
|
|
1418
|
+
if (depth <= 0) {
|
|
1419
|
+
return 0;
|
|
1420
|
+
}
|
|
1421
|
+
return Math.min(depth, frameCount, MAX_STACK_DEPTH);
|
|
1422
|
+
}
|
|
1423
|
+
function buildBaseFrame(frame) {
|
|
1424
|
+
const base = {
|
|
1425
|
+
functionName: frame.functionName,
|
|
1426
|
+
line: frame.lineNumber + 1,
|
|
1427
|
+
column: frame.columnNumber + 1
|
|
1428
|
+
};
|
|
1429
|
+
return frame.url === void 0 ? base : { ...base, url: frame.url };
|
|
1430
|
+
}
|
|
1431
|
+
async function captureFrameExpression(session, callFrameId, expression, maxValueLength) {
|
|
1432
|
+
try {
|
|
1433
|
+
const result = await evaluateOnFrame(session, callFrameId, expression);
|
|
1434
|
+
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1435
|
+
return await withSerializedObjectCapture(session, expression, result, captured, maxValueLength);
|
|
1436
|
+
} catch (err) {
|
|
1437
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1438
|
+
return { expression, error: limitValueLength(message, maxValueLength) };
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
async function captureFrameExpressions(session, frame, expressions, maxValueLength) {
|
|
1442
|
+
if (expressions.length === 0) {
|
|
1443
|
+
return [];
|
|
1444
|
+
}
|
|
1445
|
+
return await Promise.all(
|
|
1446
|
+
expressions.map(
|
|
1447
|
+
(expression) => captureFrameExpression(session, frame.callFrameId, expression, maxValueLength)
|
|
1448
|
+
)
|
|
1449
|
+
);
|
|
1450
|
+
}
|
|
1451
|
+
async function walkStack(session, callFrames, options) {
|
|
1452
|
+
const depth = clampDepth(options.stackDepth, callFrames.length);
|
|
1453
|
+
if (depth <= 1) {
|
|
1454
|
+
return [];
|
|
1455
|
+
}
|
|
1456
|
+
const slice = callFrames.slice(0, depth);
|
|
1457
|
+
return await Promise.all(
|
|
1458
|
+
slice.map(async (frame) => {
|
|
1459
|
+
const base = buildBaseFrame(frame);
|
|
1460
|
+
if (options.stackCaptures.length === 0) {
|
|
1461
|
+
return base;
|
|
1462
|
+
}
|
|
1463
|
+
const captures = await captureFrameExpressions(
|
|
1464
|
+
session,
|
|
1465
|
+
frame,
|
|
1466
|
+
options.stackCaptures,
|
|
1467
|
+
options.maxValueLength
|
|
1468
|
+
);
|
|
1469
|
+
return { ...base, captures };
|
|
1470
|
+
})
|
|
1471
|
+
);
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1279
1474
|
// src/snapshot/capture.ts
|
|
1280
1475
|
async function captureSnapshot(session, pause, options = {}) {
|
|
1281
1476
|
const maxValueLength = resolveMaxValueLength(options.maxValueLength);
|
|
1282
1477
|
const top = pause.callFrames[0];
|
|
1283
1478
|
let topFrame;
|
|
1284
1479
|
let captures = [];
|
|
1480
|
+
let stack = [];
|
|
1285
1481
|
if (top) {
|
|
1286
1482
|
topFrame = {
|
|
1287
1483
|
functionName: top.functionName,
|
|
@@ -1294,14 +1490,31 @@ async function captureSnapshot(session, pause, options = {}) {
|
|
|
1294
1490
|
topFrame = { ...topFrame, scopes };
|
|
1295
1491
|
}
|
|
1296
1492
|
captures = await captureExpressions(session, top.callFrameId, options.captures, maxValueLength);
|
|
1493
|
+
stack = await walkStack(session, pause.callFrames, {
|
|
1494
|
+
stackDepth: options.stackDepth ?? DEFAULT_STACK_DEPTH,
|
|
1495
|
+
stackCaptures: options.stackCaptures ?? [],
|
|
1496
|
+
maxValueLength
|
|
1497
|
+
});
|
|
1297
1498
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1499
|
+
const exception = await captureException(session, pause, maxValueLength);
|
|
1500
|
+
return buildResult2({
|
|
1501
|
+
pause,
|
|
1502
|
+
topFrame,
|
|
1503
|
+
captures,
|
|
1504
|
+
stack,
|
|
1505
|
+
exception
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
function buildResult2(input) {
|
|
1509
|
+
const base = {
|
|
1510
|
+
reason: input.pause.reason,
|
|
1511
|
+
hitBreakpoints: input.pause.hitBreakpoints,
|
|
1301
1512
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1302
|
-
|
|
1303
|
-
captures
|
|
1513
|
+
captures: input.captures
|
|
1304
1514
|
};
|
|
1515
|
+
const withFrame = input.topFrame === void 0 ? base : { ...base, topFrame: input.topFrame };
|
|
1516
|
+
const withStack = input.stack.length > 0 ? { ...withFrame, stack: input.stack } : withFrame;
|
|
1517
|
+
return input.exception === void 0 ? withStack : { ...withStack, exception: input.exception };
|
|
1305
1518
|
}
|
|
1306
1519
|
async function captureExpressions(session, callFrameId, captures, maxValueLength) {
|
|
1307
1520
|
if (captures === void 0 || captures.length === 0) {
|
|
@@ -1328,7 +1541,8 @@ async function captureExpression(session, callFrameId, expression, maxValueLengt
|
|
|
1328
1541
|
import { randomBytes } from "crypto";
|
|
1329
1542
|
var SENTINEL_PREFIX = "__CFI_LOG_";
|
|
1330
1543
|
var SENTINEL_SUFFIX = "__";
|
|
1331
|
-
|
|
1544
|
+
var HITS_GLOBAL2 = "globalThis.__CFI_LOG_HITS";
|
|
1545
|
+
function buildLoggingIife(sentinel, expression) {
|
|
1332
1546
|
return [
|
|
1333
1547
|
"(function(){",
|
|
1334
1548
|
`var s=${JSON.stringify(sentinel)};`,
|
|
@@ -1343,12 +1557,53 @@ function buildLogpointCondition(sentinel, expression) {
|
|
|
1343
1557
|
"})()"
|
|
1344
1558
|
].join("");
|
|
1345
1559
|
}
|
|
1560
|
+
function buildHitGate(hitCount, counterKey) {
|
|
1561
|
+
const keyLiteral = JSON.stringify(counterKey);
|
|
1562
|
+
return [
|
|
1563
|
+
"(function(){",
|
|
1564
|
+
`var m=(${HITS_GLOBAL2}=${HITS_GLOBAL2}||{});`,
|
|
1565
|
+
`var k=${keyLiteral};`,
|
|
1566
|
+
"m[k]=(m[k]||0)+1;",
|
|
1567
|
+
`return m[k]>=${hitCount.toString()};`,
|
|
1568
|
+
"})()"
|
|
1569
|
+
].join("");
|
|
1570
|
+
}
|
|
1571
|
+
function combineGuards(guards) {
|
|
1572
|
+
const filtered = guards.filter((guard) => guard.length > 0);
|
|
1573
|
+
if (filtered.length === 0) {
|
|
1574
|
+
return void 0;
|
|
1575
|
+
}
|
|
1576
|
+
if (filtered.length === 1) {
|
|
1577
|
+
return filtered[0];
|
|
1578
|
+
}
|
|
1579
|
+
return filtered.map((guard) => `(${guard})`).join("&&");
|
|
1580
|
+
}
|
|
1581
|
+
function buildLogpointCondition(sentinel, expression, options = {}) {
|
|
1582
|
+
const guards = [];
|
|
1583
|
+
if (options.hitCount !== void 0) {
|
|
1584
|
+
const counterKey = options.counterKey ?? sentinel;
|
|
1585
|
+
guards.push(buildHitGate(options.hitCount, counterKey));
|
|
1586
|
+
}
|
|
1587
|
+
const userPredicate = options.predicate?.trim();
|
|
1588
|
+
if (userPredicate !== void 0 && userPredicate.length > 0) {
|
|
1589
|
+
guards.push(`(${userPredicate})`);
|
|
1590
|
+
}
|
|
1591
|
+
const iife = buildLoggingIife(sentinel, expression);
|
|
1592
|
+
const guard = combineGuards(guards);
|
|
1593
|
+
if (guard === void 0) {
|
|
1594
|
+
return iife;
|
|
1595
|
+
}
|
|
1596
|
+
return `(${guard})?${iife}:false`;
|
|
1597
|
+
}
|
|
1346
1598
|
function generateSentinel() {
|
|
1347
1599
|
return `${SENTINEL_PREFIX}${randomBytes(8).toString("hex")}${SENTINEL_SUFFIX}`;
|
|
1348
1600
|
}
|
|
1349
1601
|
|
|
1602
|
+
// src/logpoint/stream.ts
|
|
1603
|
+
init_types();
|
|
1604
|
+
|
|
1350
1605
|
// src/logpoint/events.ts
|
|
1351
|
-
function
|
|
1606
|
+
function asString3(value) {
|
|
1352
1607
|
return typeof value === "string" ? value : void 0;
|
|
1353
1608
|
}
|
|
1354
1609
|
function readArg(arg, index) {
|
|
@@ -1395,17 +1650,55 @@ function parsePayload(ts, at, payload) {
|
|
|
1395
1650
|
}
|
|
1396
1651
|
|
|
1397
1652
|
// src/logpoint/stream.ts
|
|
1653
|
+
function validateMaxEvents(maxEvents) {
|
|
1654
|
+
if (maxEvents === void 0) {
|
|
1655
|
+
return void 0;
|
|
1656
|
+
}
|
|
1657
|
+
if (!Number.isInteger(maxEvents) || maxEvents <= 0) {
|
|
1658
|
+
throw new CfInspectorError(
|
|
1659
|
+
"INVALID_ARGUMENT",
|
|
1660
|
+
`maxEvents must be a positive integer, received: ${maxEvents.toString()}`
|
|
1661
|
+
);
|
|
1662
|
+
}
|
|
1663
|
+
return maxEvents;
|
|
1664
|
+
}
|
|
1665
|
+
function validateHitCount2(hitCount) {
|
|
1666
|
+
if (hitCount === void 0) {
|
|
1667
|
+
return void 0;
|
|
1668
|
+
}
|
|
1669
|
+
if (!Number.isInteger(hitCount) || hitCount <= 0) {
|
|
1670
|
+
throw new CfInspectorError(
|
|
1671
|
+
"INVALID_HIT_COUNT",
|
|
1672
|
+
`hitCount must be a positive integer, received: ${hitCount.toString()}`
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
return hitCount;
|
|
1676
|
+
}
|
|
1398
1677
|
async function streamLogpoint(session, options) {
|
|
1678
|
+
const maxEvents = validateMaxEvents(options.maxEvents);
|
|
1679
|
+
const hitCount = validateHitCount2(options.hitCount);
|
|
1399
1680
|
const sentinel = generateSentinel();
|
|
1400
|
-
const condition = buildLogpointCondition(sentinel, options.expression
|
|
1681
|
+
const condition = buildLogpointCondition(sentinel, options.expression, {
|
|
1682
|
+
...options.condition === void 0 ? {} : { predicate: options.condition },
|
|
1683
|
+
...hitCount === void 0 ? {} : { hitCount }
|
|
1684
|
+
});
|
|
1401
1685
|
let emitted = 0;
|
|
1686
|
+
let maxEventsReached = false;
|
|
1687
|
+
let stopMaxEvents;
|
|
1402
1688
|
const offEvent = session.client.on("Runtime.consoleAPICalled", (raw) => {
|
|
1689
|
+
if (maxEventsReached) {
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1403
1692
|
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
1404
1693
|
if (event === void 0) {
|
|
1405
1694
|
return;
|
|
1406
1695
|
}
|
|
1407
1696
|
emitted += 1;
|
|
1408
1697
|
options.onEvent(event);
|
|
1698
|
+
if (maxEvents !== void 0 && emitted >= maxEvents) {
|
|
1699
|
+
maxEventsReached = true;
|
|
1700
|
+
stopMaxEvents?.();
|
|
1701
|
+
}
|
|
1409
1702
|
});
|
|
1410
1703
|
let handle;
|
|
1411
1704
|
try {
|
|
@@ -1421,7 +1714,12 @@ async function streamLogpoint(session, options) {
|
|
|
1421
1714
|
}
|
|
1422
1715
|
options.onBreakpointSet?.(handle);
|
|
1423
1716
|
try {
|
|
1424
|
-
const reason = await waitForStop(session, options)
|
|
1717
|
+
const reason = await waitForStop(session, options, (signal) => {
|
|
1718
|
+
stopMaxEvents = signal;
|
|
1719
|
+
if (maxEventsReached) {
|
|
1720
|
+
signal();
|
|
1721
|
+
}
|
|
1722
|
+
});
|
|
1425
1723
|
return { handle, sentinel, emitted, stoppedReason: reason };
|
|
1426
1724
|
} finally {
|
|
1427
1725
|
offEvent();
|
|
@@ -1430,7 +1728,7 @@ async function streamLogpoint(session, options) {
|
|
|
1430
1728
|
}
|
|
1431
1729
|
function toLogpointEvent(raw, sentinel, location) {
|
|
1432
1730
|
const params = raw;
|
|
1433
|
-
if (
|
|
1731
|
+
if (asString3(params.type) !== "log") {
|
|
1434
1732
|
return void 0;
|
|
1435
1733
|
}
|
|
1436
1734
|
const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
|
|
@@ -1442,7 +1740,7 @@ async function removeBreakpointBestEffort(session, breakpointId) {
|
|
|
1442
1740
|
} catch {
|
|
1443
1741
|
}
|
|
1444
1742
|
}
|
|
1445
|
-
async function waitForStop(session, options) {
|
|
1743
|
+
async function waitForStop(session, options, registerMaxEventsSignal) {
|
|
1446
1744
|
return await new Promise((resolve) => {
|
|
1447
1745
|
let settled = false;
|
|
1448
1746
|
const finish = (reason) => {
|
|
@@ -1466,6 +1764,9 @@ async function waitForStop(session, options) {
|
|
|
1466
1764
|
if (options.signal?.aborted === true) {
|
|
1467
1765
|
finish("signal");
|
|
1468
1766
|
}
|
|
1767
|
+
registerMaxEventsSignal(() => {
|
|
1768
|
+
finish("max-events");
|
|
1769
|
+
});
|
|
1469
1770
|
function cleanup() {
|
|
1470
1771
|
if (timer !== void 0) {
|
|
1471
1772
|
clearTimeout(timer);
|
|
@@ -1501,7 +1802,9 @@ async function openCfTunnel(target) {
|
|
|
1501
1802
|
export {
|
|
1502
1803
|
CfInspectorError,
|
|
1503
1804
|
buildBreakpointUrlRegex,
|
|
1805
|
+
buildHitCountedCondition,
|
|
1504
1806
|
buildLogpointCondition,
|
|
1807
|
+
captureException,
|
|
1505
1808
|
captureSnapshot,
|
|
1506
1809
|
connectInspector,
|
|
1507
1810
|
discoverInspectorTargets,
|
|
@@ -1516,8 +1819,10 @@ export {
|
|
|
1516
1819
|
removeBreakpoint,
|
|
1517
1820
|
resume,
|
|
1518
1821
|
setBreakpoint,
|
|
1822
|
+
setPauseOnExceptions,
|
|
1519
1823
|
streamLogpoint,
|
|
1520
1824
|
validateExpression,
|
|
1521
|
-
waitForPause
|
|
1825
|
+
waitForPause,
|
|
1826
|
+
walkStack
|
|
1522
1827
|
};
|
|
1523
1828
|
//# sourceMappingURL=index.js.map
|