@saptools/cf-inspector 0.3.18 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -23
- package/dist/cli.js +1124 -421
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +50 -4
- package/dist/index.js +343 -24
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/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
|
@@ -261,6 +261,9 @@ function asString(value, fallback = "") {
|
|
|
261
261
|
function asNumber(value, fallback = 0) {
|
|
262
262
|
return typeof value === "number" && Number.isFinite(value) ? value : fallback;
|
|
263
263
|
}
|
|
264
|
+
function nonEmptyString(value) {
|
|
265
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
266
|
+
}
|
|
264
267
|
function toResolvedLocations(value) {
|
|
265
268
|
if (!Array.isArray(value)) {
|
|
266
269
|
return [];
|
|
@@ -299,7 +302,18 @@ function toScopeChain(value) {
|
|
|
299
302
|
return [objectId === void 0 ? base : { ...base, objectId }];
|
|
300
303
|
});
|
|
301
304
|
}
|
|
302
|
-
function
|
|
305
|
+
function resolveCallFrameUrl(frame, scripts) {
|
|
306
|
+
const direct = nonEmptyString(frame.url);
|
|
307
|
+
if (direct !== void 0) {
|
|
308
|
+
return direct;
|
|
309
|
+
}
|
|
310
|
+
const scriptId = nonEmptyString(frame.location?.scriptId);
|
|
311
|
+
if (scriptId === void 0) {
|
|
312
|
+
return void 0;
|
|
313
|
+
}
|
|
314
|
+
return nonEmptyString(scripts?.get(scriptId)?.url);
|
|
315
|
+
}
|
|
316
|
+
function toCallFrames(value, scripts) {
|
|
303
317
|
if (!Array.isArray(value)) {
|
|
304
318
|
return [];
|
|
305
319
|
}
|
|
@@ -312,7 +326,7 @@ function toCallFrames(value) {
|
|
|
312
326
|
if (callFrameId.length === 0) {
|
|
313
327
|
return [];
|
|
314
328
|
}
|
|
315
|
-
const url =
|
|
329
|
+
const url = resolveCallFrameUrl(candidate, scripts);
|
|
316
330
|
const base = {
|
|
317
331
|
callFrameId,
|
|
318
332
|
functionName: asString(candidate.functionName),
|
|
@@ -323,13 +337,14 @@ function toCallFrames(value) {
|
|
|
323
337
|
return [url === void 0 ? base : { ...base, url }];
|
|
324
338
|
});
|
|
325
339
|
}
|
|
326
|
-
function toPauseEvent(params, receivedAtMs) {
|
|
327
|
-
|
|
340
|
+
function toPauseEvent(params, receivedAtMs, scripts) {
|
|
341
|
+
const base = {
|
|
328
342
|
reason: asString(params.reason),
|
|
329
343
|
hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
|
|
330
|
-
callFrames: toCallFrames(params.callFrames),
|
|
344
|
+
callFrames: toCallFrames(params.callFrames, scripts),
|
|
331
345
|
receivedAtMs
|
|
332
346
|
};
|
|
347
|
+
return params.data === void 0 ? base : { ...base, data: params.data };
|
|
333
348
|
}
|
|
334
349
|
function topFrameLocation(pause) {
|
|
335
350
|
const top = pause.callFrames[0];
|
|
@@ -348,6 +363,46 @@ function pauseDetail(pause) {
|
|
|
348
363
|
}
|
|
349
364
|
|
|
350
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
|
+
}
|
|
351
406
|
async function setBreakpoint(session, input) {
|
|
352
407
|
const remoteRoot = input.remoteRoot ?? { kind: "none" };
|
|
353
408
|
const urlRegex = buildBreakpointUrlRegex({ file: input.file, remoteRoot });
|
|
@@ -355,8 +410,9 @@ async function setBreakpoint(session, input) {
|
|
|
355
410
|
lineNumber: input.line - 1,
|
|
356
411
|
urlRegex
|
|
357
412
|
};
|
|
358
|
-
|
|
359
|
-
|
|
413
|
+
const condition = resolveCondition(input);
|
|
414
|
+
if (condition !== void 0) {
|
|
415
|
+
params["condition"] = condition;
|
|
360
416
|
}
|
|
361
417
|
const result = await session.client.send(
|
|
362
418
|
"Debugger.setBreakpointByUrl",
|
|
@@ -502,7 +558,10 @@ async function fetchInspectorVersion(host, port, timeoutMs) {
|
|
|
502
558
|
// src/inspector/pause.ts
|
|
503
559
|
init_types();
|
|
504
560
|
import { performance } from "perf_hooks";
|
|
505
|
-
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
|
+
}
|
|
506
565
|
if (breakpointIds === void 0 || breakpointIds.length === 0) {
|
|
507
566
|
return true;
|
|
508
567
|
}
|
|
@@ -570,13 +629,13 @@ async function waitForPause(session, options) {
|
|
|
570
629
|
if (buffered === void 0) {
|
|
571
630
|
continue;
|
|
572
631
|
}
|
|
573
|
-
if (pauseMatches(buffered, options.breakpointIds)) {
|
|
632
|
+
if (pauseMatches(buffered, options.breakpointIds, options.pauseReasons)) {
|
|
574
633
|
return buffered;
|
|
575
634
|
}
|
|
576
635
|
await handleUnmatchedPause(session, buffered, options, deadlineMs);
|
|
577
636
|
}
|
|
578
637
|
const pause = await waitForLivePause(session, options, deadlineMs);
|
|
579
|
-
if (pauseMatches(pause, options.breakpointIds)) {
|
|
638
|
+
if (pauseMatches(pause, options.breakpointIds, options.pauseReasons)) {
|
|
580
639
|
return pause;
|
|
581
640
|
}
|
|
582
641
|
await handleUnmatchedPause(session, pause, options, deadlineMs);
|
|
@@ -602,7 +661,7 @@ async function waitForLivePause(session, options, deadlineMs) {
|
|
|
602
661
|
} finally {
|
|
603
662
|
session.pauseWaitGate.active = false;
|
|
604
663
|
}
|
|
605
|
-
return toPauseEvent(params, receivedAtMs ?? performance.now());
|
|
664
|
+
return toPauseEvent(params, receivedAtMs ?? performance.now(), session.scripts);
|
|
606
665
|
}
|
|
607
666
|
|
|
608
667
|
// src/inspector/runtime.ts
|
|
@@ -610,6 +669,9 @@ init_types();
|
|
|
610
669
|
async function resume(session) {
|
|
611
670
|
await session.client.send("Debugger.resume");
|
|
612
671
|
}
|
|
672
|
+
async function setPauseOnExceptions(session, state) {
|
|
673
|
+
await session.client.send("Debugger.setPauseOnExceptions", { state });
|
|
674
|
+
}
|
|
613
675
|
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
614
676
|
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
615
677
|
callFrameId,
|
|
@@ -916,7 +978,7 @@ async function connectInspector(options) {
|
|
|
916
978
|
return;
|
|
917
979
|
}
|
|
918
980
|
const params = raw;
|
|
919
|
-
const event = toPauseEvent(params, performance2.now());
|
|
981
|
+
const event = toPauseEvent(params, performance2.now(), scripts);
|
|
920
982
|
if (pauseBuffer.length >= PAUSE_BUFFER_LIMIT) {
|
|
921
983
|
pauseBuffer.shift();
|
|
922
984
|
}
|
|
@@ -1160,6 +1222,93 @@ async function capturePropertyChildren(session, described, depth, maxValueLength
|
|
|
1160
1222
|
}
|
|
1161
1223
|
}
|
|
1162
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
|
+
|
|
1163
1312
|
// src/snapshot/objects.ts
|
|
1164
1313
|
function objectIdFromEvalResult(result) {
|
|
1165
1314
|
const inner = result.result;
|
|
@@ -1262,12 +1411,73 @@ async function captureScopes(session, frame, maxValueLength) {
|
|
|
1262
1411
|
);
|
|
1263
1412
|
}
|
|
1264
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
|
+
|
|
1265
1474
|
// src/snapshot/capture.ts
|
|
1266
1475
|
async function captureSnapshot(session, pause, options = {}) {
|
|
1267
1476
|
const maxValueLength = resolveMaxValueLength(options.maxValueLength);
|
|
1268
1477
|
const top = pause.callFrames[0];
|
|
1269
1478
|
let topFrame;
|
|
1270
1479
|
let captures = [];
|
|
1480
|
+
let stack = [];
|
|
1271
1481
|
if (top) {
|
|
1272
1482
|
topFrame = {
|
|
1273
1483
|
functionName: top.functionName,
|
|
@@ -1280,14 +1490,31 @@ async function captureSnapshot(session, pause, options = {}) {
|
|
|
1280
1490
|
topFrame = { ...topFrame, scopes };
|
|
1281
1491
|
}
|
|
1282
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
|
+
});
|
|
1283
1498
|
}
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
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,
|
|
1287
1512
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1288
|
-
|
|
1289
|
-
captures
|
|
1513
|
+
captures: input.captures
|
|
1290
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 };
|
|
1291
1518
|
}
|
|
1292
1519
|
async function captureExpressions(session, callFrameId, captures, maxValueLength) {
|
|
1293
1520
|
if (captures === void 0 || captures.length === 0) {
|
|
@@ -1314,7 +1541,8 @@ async function captureExpression(session, callFrameId, expression, maxValueLengt
|
|
|
1314
1541
|
import { randomBytes } from "crypto";
|
|
1315
1542
|
var SENTINEL_PREFIX = "__CFI_LOG_";
|
|
1316
1543
|
var SENTINEL_SUFFIX = "__";
|
|
1317
|
-
|
|
1544
|
+
var HITS_GLOBAL2 = "globalThis.__CFI_LOG_HITS";
|
|
1545
|
+
function buildLoggingIife(sentinel, expression) {
|
|
1318
1546
|
return [
|
|
1319
1547
|
"(function(){",
|
|
1320
1548
|
`var s=${JSON.stringify(sentinel)};`,
|
|
@@ -1329,12 +1557,53 @@ function buildLogpointCondition(sentinel, expression) {
|
|
|
1329
1557
|
"})()"
|
|
1330
1558
|
].join("");
|
|
1331
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
|
+
}
|
|
1332
1598
|
function generateSentinel() {
|
|
1333
1599
|
return `${SENTINEL_PREFIX}${randomBytes(8).toString("hex")}${SENTINEL_SUFFIX}`;
|
|
1334
1600
|
}
|
|
1335
1601
|
|
|
1602
|
+
// src/logpoint/stream.ts
|
|
1603
|
+
init_types();
|
|
1604
|
+
|
|
1336
1605
|
// src/logpoint/events.ts
|
|
1337
|
-
function
|
|
1606
|
+
function asString3(value) {
|
|
1338
1607
|
return typeof value === "string" ? value : void 0;
|
|
1339
1608
|
}
|
|
1340
1609
|
function readArg(arg, index) {
|
|
@@ -1381,17 +1650,55 @@ function parsePayload(ts, at, payload) {
|
|
|
1381
1650
|
}
|
|
1382
1651
|
|
|
1383
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
|
+
}
|
|
1384
1677
|
async function streamLogpoint(session, options) {
|
|
1678
|
+
const maxEvents = validateMaxEvents(options.maxEvents);
|
|
1679
|
+
const hitCount = validateHitCount2(options.hitCount);
|
|
1385
1680
|
const sentinel = generateSentinel();
|
|
1386
|
-
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
|
+
});
|
|
1387
1685
|
let emitted = 0;
|
|
1686
|
+
let maxEventsReached = false;
|
|
1687
|
+
let stopMaxEvents;
|
|
1388
1688
|
const offEvent = session.client.on("Runtime.consoleAPICalled", (raw) => {
|
|
1689
|
+
if (maxEventsReached) {
|
|
1690
|
+
return;
|
|
1691
|
+
}
|
|
1389
1692
|
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
1390
1693
|
if (event === void 0) {
|
|
1391
1694
|
return;
|
|
1392
1695
|
}
|
|
1393
1696
|
emitted += 1;
|
|
1394
1697
|
options.onEvent(event);
|
|
1698
|
+
if (maxEvents !== void 0 && emitted >= maxEvents) {
|
|
1699
|
+
maxEventsReached = true;
|
|
1700
|
+
stopMaxEvents?.();
|
|
1701
|
+
}
|
|
1395
1702
|
});
|
|
1396
1703
|
let handle;
|
|
1397
1704
|
try {
|
|
@@ -1407,7 +1714,12 @@ async function streamLogpoint(session, options) {
|
|
|
1407
1714
|
}
|
|
1408
1715
|
options.onBreakpointSet?.(handle);
|
|
1409
1716
|
try {
|
|
1410
|
-
const reason = await waitForStop(session, options)
|
|
1717
|
+
const reason = await waitForStop(session, options, (signal) => {
|
|
1718
|
+
stopMaxEvents = signal;
|
|
1719
|
+
if (maxEventsReached) {
|
|
1720
|
+
signal();
|
|
1721
|
+
}
|
|
1722
|
+
});
|
|
1411
1723
|
return { handle, sentinel, emitted, stoppedReason: reason };
|
|
1412
1724
|
} finally {
|
|
1413
1725
|
offEvent();
|
|
@@ -1416,7 +1728,7 @@ async function streamLogpoint(session, options) {
|
|
|
1416
1728
|
}
|
|
1417
1729
|
function toLogpointEvent(raw, sentinel, location) {
|
|
1418
1730
|
const params = raw;
|
|
1419
|
-
if (
|
|
1731
|
+
if (asString3(params.type) !== "log") {
|
|
1420
1732
|
return void 0;
|
|
1421
1733
|
}
|
|
1422
1734
|
const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
|
|
@@ -1428,7 +1740,7 @@ async function removeBreakpointBestEffort(session, breakpointId) {
|
|
|
1428
1740
|
} catch {
|
|
1429
1741
|
}
|
|
1430
1742
|
}
|
|
1431
|
-
async function waitForStop(session, options) {
|
|
1743
|
+
async function waitForStop(session, options, registerMaxEventsSignal) {
|
|
1432
1744
|
return await new Promise((resolve) => {
|
|
1433
1745
|
let settled = false;
|
|
1434
1746
|
const finish = (reason) => {
|
|
@@ -1452,6 +1764,9 @@ async function waitForStop(session, options) {
|
|
|
1452
1764
|
if (options.signal?.aborted === true) {
|
|
1453
1765
|
finish("signal");
|
|
1454
1766
|
}
|
|
1767
|
+
registerMaxEventsSignal(() => {
|
|
1768
|
+
finish("max-events");
|
|
1769
|
+
});
|
|
1455
1770
|
function cleanup() {
|
|
1456
1771
|
if (timer !== void 0) {
|
|
1457
1772
|
clearTimeout(timer);
|
|
@@ -1487,7 +1802,9 @@ async function openCfTunnel(target) {
|
|
|
1487
1802
|
export {
|
|
1488
1803
|
CfInspectorError,
|
|
1489
1804
|
buildBreakpointUrlRegex,
|
|
1805
|
+
buildHitCountedCondition,
|
|
1490
1806
|
buildLogpointCondition,
|
|
1807
|
+
captureException,
|
|
1491
1808
|
captureSnapshot,
|
|
1492
1809
|
connectInspector,
|
|
1493
1810
|
discoverInspectorTargets,
|
|
@@ -1502,8 +1819,10 @@ export {
|
|
|
1502
1819
|
removeBreakpoint,
|
|
1503
1820
|
resume,
|
|
1504
1821
|
setBreakpoint,
|
|
1822
|
+
setPauseOnExceptions,
|
|
1505
1823
|
streamLogpoint,
|
|
1506
1824
|
validateExpression,
|
|
1507
|
-
waitForPause
|
|
1825
|
+
waitForPause,
|
|
1826
|
+
walkStack
|
|
1508
1827
|
};
|
|
1509
1828
|
//# sourceMappingURL=index.js.map
|