@saptools/cf-inspector 0.3.19 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +105 -23
- package/dist/cli.js +1515 -735
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +56 -5
- package/dist/index.js +446 -53
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/cli.d.ts +0 -2
package/dist/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
1
|
var __defProp = Object.defineProperty;
|
|
3
2
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
3
|
var __esm = (fn, res) => function __init() {
|
|
@@ -35,10 +34,19 @@ __export(wsTransport_exports, {
|
|
|
35
34
|
wsTransportFactory: () => wsTransportFactory
|
|
36
35
|
});
|
|
37
36
|
import { WebSocket } from "ws";
|
|
38
|
-
async function wsTransportFactory(url) {
|
|
37
|
+
async function wsTransportFactory(url, options = {}) {
|
|
39
38
|
const socket = new WebSocket(url, { perMessageDeflate: false });
|
|
40
|
-
await waitForOpen(socket, url);
|
|
41
|
-
const wrappers = /* @__PURE__ */ new
|
|
39
|
+
await waitForOpen(socket, url, options.connectTimeoutMs);
|
|
40
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
41
|
+
function wrappersFor(event) {
|
|
42
|
+
const existing = wrappers.get(event);
|
|
43
|
+
if (existing !== void 0) {
|
|
44
|
+
return existing;
|
|
45
|
+
}
|
|
46
|
+
const created = /* @__PURE__ */ new WeakMap();
|
|
47
|
+
wrappers.set(event, created);
|
|
48
|
+
return created;
|
|
49
|
+
}
|
|
42
50
|
return {
|
|
43
51
|
send(payload) {
|
|
44
52
|
socket.send(payload);
|
|
@@ -50,11 +58,12 @@ async function wsTransportFactory(url) {
|
|
|
50
58
|
return socket.readyState;
|
|
51
59
|
},
|
|
52
60
|
on(event, listener) {
|
|
53
|
-
const wrapped = wrapListener(event, listener
|
|
61
|
+
const wrapped = wrapListener(event, listener);
|
|
62
|
+
wrappersFor(event).set(listener, wrapped);
|
|
54
63
|
socket.on(event, wrapped);
|
|
55
64
|
},
|
|
56
65
|
off(event, listener) {
|
|
57
|
-
const wrapped =
|
|
66
|
+
const wrapped = wrappersFor(event).get(listener);
|
|
58
67
|
if (!wrapped) {
|
|
59
68
|
return;
|
|
60
69
|
}
|
|
@@ -62,14 +71,30 @@ async function wsTransportFactory(url) {
|
|
|
62
71
|
}
|
|
63
72
|
};
|
|
64
73
|
}
|
|
65
|
-
async function waitForOpen(socket, url) {
|
|
74
|
+
async function waitForOpen(socket, url, timeoutMs) {
|
|
66
75
|
await new Promise((resolve, reject) => {
|
|
67
|
-
|
|
76
|
+
let settled = false;
|
|
77
|
+
const cleanup = () => {
|
|
78
|
+
socket.off("open", onOpen);
|
|
68
79
|
socket.off("error", onError);
|
|
80
|
+
if (timer !== void 0) {
|
|
81
|
+
clearTimeout(timer);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const onOpen = () => {
|
|
85
|
+
if (settled) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
settled = true;
|
|
89
|
+
cleanup();
|
|
69
90
|
resolve();
|
|
70
91
|
};
|
|
71
92
|
const onError = (err) => {
|
|
72
|
-
|
|
93
|
+
if (settled) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
settled = true;
|
|
97
|
+
cleanup();
|
|
73
98
|
reject(
|
|
74
99
|
new CfInspectorError(
|
|
75
100
|
"INSPECTOR_CONNECTION_FAILED",
|
|
@@ -77,29 +102,45 @@ async function waitForOpen(socket, url) {
|
|
|
77
102
|
)
|
|
78
103
|
);
|
|
79
104
|
};
|
|
105
|
+
const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
106
|
+
if (settled) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
settled = true;
|
|
110
|
+
cleanup();
|
|
111
|
+
socket.on("error", () => {
|
|
112
|
+
});
|
|
113
|
+
try {
|
|
114
|
+
socket.terminate();
|
|
115
|
+
} catch {
|
|
116
|
+
}
|
|
117
|
+
reject(
|
|
118
|
+
new CfInspectorError(
|
|
119
|
+
"INSPECTOR_CONNECTION_FAILED",
|
|
120
|
+
`WebSocket handshake to ${url} timed out after ${timeoutMs.toString()}ms`
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
}, timeoutMs);
|
|
80
124
|
socket.once("open", onOpen);
|
|
81
125
|
socket.once("error", onError);
|
|
82
126
|
});
|
|
83
127
|
}
|
|
84
|
-
function wrapListener(event, listener
|
|
128
|
+
function wrapListener(event, listener) {
|
|
85
129
|
if (event === "message") {
|
|
86
130
|
const wrapped2 = (data) => {
|
|
87
131
|
listener(data.toString("utf8"));
|
|
88
132
|
};
|
|
89
|
-
wrappers.set(listener, wrapped2);
|
|
90
133
|
return wrapped2;
|
|
91
134
|
}
|
|
92
135
|
if (event === "close") {
|
|
93
136
|
const wrapped2 = () => {
|
|
94
137
|
listener();
|
|
95
138
|
};
|
|
96
|
-
wrappers.set(listener, wrapped2);
|
|
97
139
|
return wrapped2;
|
|
98
140
|
}
|
|
99
141
|
const wrapped = (err) => {
|
|
100
142
|
listener(err);
|
|
101
143
|
};
|
|
102
|
-
wrappers.set(listener, wrapped);
|
|
103
144
|
return wrapped;
|
|
104
145
|
}
|
|
105
146
|
var init_wsTransport = __esm({
|
|
@@ -212,9 +253,11 @@ function normalizeRegexRootPattern(pattern) {
|
|
|
212
253
|
const withoutEndAnchor = withoutStartAnchor.endsWith("$") && !isEscaped(withoutStartAnchor, withoutStartAnchor.length - 1) ? withoutStartAnchor.slice(0, -1) : withoutStartAnchor;
|
|
213
254
|
return stripTrailingSlash(withoutEndAnchor);
|
|
214
255
|
}
|
|
215
|
-
function buildFileUrlRegex(
|
|
216
|
-
|
|
217
|
-
|
|
256
|
+
function buildFileUrlRegex(embeddedRoot, tail, separator) {
|
|
257
|
+
return `^file://${embeddedRoot}${separator}${tail}$`;
|
|
258
|
+
}
|
|
259
|
+
function rootSeparator(rawPattern) {
|
|
260
|
+
return rawPattern.endsWith("/") ? "" : "/";
|
|
218
261
|
}
|
|
219
262
|
function escapeRegExp(value) {
|
|
220
263
|
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
@@ -241,12 +284,12 @@ function buildBreakpointUrlRegex(input) {
|
|
|
241
284
|
return `(?:^|/)${tail}$`;
|
|
242
285
|
}
|
|
243
286
|
case "literal": {
|
|
244
|
-
const
|
|
245
|
-
return buildFileUrlRegex(
|
|
287
|
+
const root = input.remoteRoot.value;
|
|
288
|
+
return buildFileUrlRegex(escapeRegExp(root), tail, rootSeparator(root));
|
|
246
289
|
}
|
|
247
290
|
case "regex": {
|
|
248
|
-
const
|
|
249
|
-
return buildFileUrlRegex(
|
|
291
|
+
const root = normalizeRegexRootPattern(input.remoteRoot.pattern);
|
|
292
|
+
return buildFileUrlRegex(`(?:${root})`, tail, rootSeparator(root));
|
|
250
293
|
}
|
|
251
294
|
}
|
|
252
295
|
}
|
|
@@ -338,12 +381,13 @@ function toCallFrames(value, scripts) {
|
|
|
338
381
|
});
|
|
339
382
|
}
|
|
340
383
|
function toPauseEvent(params, receivedAtMs, scripts) {
|
|
341
|
-
|
|
384
|
+
const base = {
|
|
342
385
|
reason: asString(params.reason),
|
|
343
386
|
hitBreakpoints: Array.isArray(params.hitBreakpoints) ? params.hitBreakpoints.filter((id) => typeof id === "string") : [],
|
|
344
387
|
callFrames: toCallFrames(params.callFrames, scripts),
|
|
345
388
|
receivedAtMs
|
|
346
389
|
};
|
|
390
|
+
return params.data === void 0 ? base : { ...base, data: params.data };
|
|
347
391
|
}
|
|
348
392
|
function topFrameLocation(pause) {
|
|
349
393
|
const top = pause.callFrames[0];
|
|
@@ -362,6 +406,46 @@ function pauseDetail(pause) {
|
|
|
362
406
|
}
|
|
363
407
|
|
|
364
408
|
// src/inspector/breakpoints.ts
|
|
409
|
+
var HITS_GLOBAL = "globalThis.__CFI_HITS";
|
|
410
|
+
var counterKeyCounter = 0;
|
|
411
|
+
function nextCounterKey(file, line) {
|
|
412
|
+
counterKeyCounter += 1;
|
|
413
|
+
return `${file}:${line.toString()}:${counterKeyCounter.toString()}`;
|
|
414
|
+
}
|
|
415
|
+
function validateHitCount(hitCount) {
|
|
416
|
+
if (hitCount === void 0) {
|
|
417
|
+
return void 0;
|
|
418
|
+
}
|
|
419
|
+
if (!Number.isInteger(hitCount) || hitCount <= 0) {
|
|
420
|
+
throw new CfInspectorError(
|
|
421
|
+
"INVALID_HIT_COUNT",
|
|
422
|
+
`hitCount must be a positive integer, received: ${hitCount.toString()}`
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return hitCount;
|
|
426
|
+
}
|
|
427
|
+
function buildHitCountedCondition(hitCount, counterKey, userCondition) {
|
|
428
|
+
const keyLiteral = JSON.stringify(counterKey);
|
|
429
|
+
const baseCondition = userCondition !== void 0 && userCondition.trim().length > 0 ? `(${userCondition})` : "true";
|
|
430
|
+
return [
|
|
431
|
+
"(function(){",
|
|
432
|
+
`var m=(${HITS_GLOBAL}=${HITS_GLOBAL}||{});`,
|
|
433
|
+
`var k=${keyLiteral};`,
|
|
434
|
+
"m[k]=(m[k]||0)+1;",
|
|
435
|
+
`if(m[k]<${hitCount.toString()})return false;`,
|
|
436
|
+
`return ${baseCondition};`,
|
|
437
|
+
"})()"
|
|
438
|
+
].join("");
|
|
439
|
+
}
|
|
440
|
+
function resolveCondition(input) {
|
|
441
|
+
const condition = input.condition;
|
|
442
|
+
const hitCount = validateHitCount(input.hitCount);
|
|
443
|
+
if (hitCount === void 0) {
|
|
444
|
+
return condition !== void 0 && condition.length > 0 ? condition : void 0;
|
|
445
|
+
}
|
|
446
|
+
const counterKey = nextCounterKey(input.file, input.line);
|
|
447
|
+
return buildHitCountedCondition(hitCount, counterKey, condition);
|
|
448
|
+
}
|
|
365
449
|
async function setBreakpoint(session, input) {
|
|
366
450
|
const remoteRoot = input.remoteRoot ?? { kind: "none" };
|
|
367
451
|
const urlRegex = buildBreakpointUrlRegex({ file: input.file, remoteRoot });
|
|
@@ -369,8 +453,9 @@ async function setBreakpoint(session, input) {
|
|
|
369
453
|
lineNumber: input.line - 1,
|
|
370
454
|
urlRegex
|
|
371
455
|
};
|
|
372
|
-
|
|
373
|
-
|
|
456
|
+
const condition = resolveCondition(input);
|
|
457
|
+
if (condition !== void 0) {
|
|
458
|
+
params["condition"] = condition;
|
|
374
459
|
}
|
|
375
460
|
const result = await session.client.send(
|
|
376
461
|
"Debugger.setBreakpointByUrl",
|
|
@@ -516,7 +601,10 @@ async function fetchInspectorVersion(host, port, timeoutMs) {
|
|
|
516
601
|
// src/inspector/pause.ts
|
|
517
602
|
init_types();
|
|
518
603
|
import { performance } from "perf_hooks";
|
|
519
|
-
function pauseMatches(pause, breakpointIds) {
|
|
604
|
+
function pauseMatches(pause, breakpointIds, pauseReasons) {
|
|
605
|
+
if (pauseReasons !== void 0 && pauseReasons.length > 0) {
|
|
606
|
+
return pauseReasons.includes(pause.reason);
|
|
607
|
+
}
|
|
520
608
|
if (breakpointIds === void 0 || breakpointIds.length === 0) {
|
|
521
609
|
return true;
|
|
522
610
|
}
|
|
@@ -584,13 +672,13 @@ async function waitForPause(session, options) {
|
|
|
584
672
|
if (buffered === void 0) {
|
|
585
673
|
continue;
|
|
586
674
|
}
|
|
587
|
-
if (pauseMatches(buffered, options.breakpointIds)) {
|
|
675
|
+
if (pauseMatches(buffered, options.breakpointIds, options.pauseReasons)) {
|
|
588
676
|
return buffered;
|
|
589
677
|
}
|
|
590
678
|
await handleUnmatchedPause(session, buffered, options, deadlineMs);
|
|
591
679
|
}
|
|
592
680
|
const pause = await waitForLivePause(session, options, deadlineMs);
|
|
593
|
-
if (pauseMatches(pause, options.breakpointIds)) {
|
|
681
|
+
if (pauseMatches(pause, options.breakpointIds, options.pauseReasons)) {
|
|
594
682
|
return pause;
|
|
595
683
|
}
|
|
596
684
|
await handleUnmatchedPause(session, pause, options, deadlineMs);
|
|
@@ -624,6 +712,9 @@ init_types();
|
|
|
624
712
|
async function resume(session) {
|
|
625
713
|
await session.client.send("Debugger.resume");
|
|
626
714
|
}
|
|
715
|
+
async function setPauseOnExceptions(session, state) {
|
|
716
|
+
await session.client.send("Debugger.setPauseOnExceptions", { state });
|
|
717
|
+
}
|
|
627
718
|
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
628
719
|
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
629
720
|
callFrameId,
|
|
@@ -709,10 +800,19 @@ var CdpClient = class _CdpClient {
|
|
|
709
800
|
return;
|
|
710
801
|
}
|
|
711
802
|
if (typeof parsed.method === "string") {
|
|
712
|
-
this.
|
|
713
|
-
this.
|
|
803
|
+
this.safeEmit(parsed.method, parsed.params);
|
|
804
|
+
this.safeEmit("event", { method: parsed.method, params: parsed.params });
|
|
714
805
|
}
|
|
715
806
|
};
|
|
807
|
+
safeEmit(event, payload) {
|
|
808
|
+
const listeners = this.emitter.listeners(event);
|
|
809
|
+
for (const listener of listeners) {
|
|
810
|
+
try {
|
|
811
|
+
listener(payload);
|
|
812
|
+
} catch {
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
}
|
|
716
816
|
handleClose = () => {
|
|
717
817
|
this.markClosed(new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Inspector connection closed"));
|
|
718
818
|
};
|
|
@@ -730,7 +830,8 @@ var CdpClient = class _CdpClient {
|
|
|
730
830
|
}
|
|
731
831
|
static async connect(options) {
|
|
732
832
|
const factory = options.transportFactory ?? await loadDefaultFactory();
|
|
733
|
-
const
|
|
833
|
+
const factoryOptions = options.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: options.connectTimeoutMs };
|
|
834
|
+
const transport = await factory(options.url, factoryOptions);
|
|
734
835
|
return new _CdpClient(transport, options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
|
|
735
836
|
}
|
|
736
837
|
async send(method, params = {}) {
|
|
@@ -780,8 +881,16 @@ var CdpClient = class _CdpClient {
|
|
|
780
881
|
return;
|
|
781
882
|
}
|
|
782
883
|
const params = raw;
|
|
783
|
-
if (options.predicate
|
|
784
|
-
|
|
884
|
+
if (options.predicate) {
|
|
885
|
+
let accepted;
|
|
886
|
+
try {
|
|
887
|
+
accepted = options.predicate(params);
|
|
888
|
+
} catch {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
if (!accepted) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
785
894
|
}
|
|
786
895
|
finish(params);
|
|
787
896
|
});
|
|
@@ -911,7 +1020,18 @@ async function connectInspector(options) {
|
|
|
911
1020
|
`No inspector targets available on ${host}:${options.port.toString()}`
|
|
912
1021
|
);
|
|
913
1022
|
}
|
|
914
|
-
const client = await CdpClient.connect({
|
|
1023
|
+
const client = await CdpClient.connect({
|
|
1024
|
+
url: target.webSocketDebuggerUrl,
|
|
1025
|
+
connectTimeoutMs
|
|
1026
|
+
});
|
|
1027
|
+
try {
|
|
1028
|
+
return await initSession(client, target);
|
|
1029
|
+
} catch (err) {
|
|
1030
|
+
client.dispose();
|
|
1031
|
+
throw err;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
async function initSession(client, target) {
|
|
915
1035
|
const scripts = /* @__PURE__ */ new Map();
|
|
916
1036
|
client.on("Debugger.scriptParsed", (raw) => {
|
|
917
1037
|
const params = raw;
|
|
@@ -1032,19 +1152,32 @@ function scalarFromVariable(variable) {
|
|
|
1032
1152
|
}
|
|
1033
1153
|
return value === "null" ? null : value;
|
|
1034
1154
|
}
|
|
1155
|
+
function isArrayLikeChildren(children) {
|
|
1156
|
+
let hasNumeric = false;
|
|
1157
|
+
for (const child of children) {
|
|
1158
|
+
if (child.name === "length") {
|
|
1159
|
+
continue;
|
|
1160
|
+
}
|
|
1161
|
+
if (parseNumericIndex(child.name) === void 0) {
|
|
1162
|
+
return false;
|
|
1163
|
+
}
|
|
1164
|
+
hasNumeric = true;
|
|
1165
|
+
}
|
|
1166
|
+
return hasNumeric;
|
|
1167
|
+
}
|
|
1035
1168
|
function toStructuredValue(variable) {
|
|
1036
1169
|
const children = variable.children;
|
|
1037
1170
|
if (children === void 0 || children.length === 0) {
|
|
1038
1171
|
return scalarFromVariable(variable);
|
|
1039
1172
|
}
|
|
1040
|
-
|
|
1041
|
-
const
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1173
|
+
if (isArrayLikeChildren(children)) {
|
|
1174
|
+
const indexed = children.flatMap((child) => {
|
|
1175
|
+
const index = parseNumericIndex(child.name);
|
|
1176
|
+
if (index === void 0) {
|
|
1177
|
+
return [];
|
|
1178
|
+
}
|
|
1179
|
+
return [[index, toStructuredValue(child)]];
|
|
1180
|
+
});
|
|
1048
1181
|
const maxIndex = Math.max(...indexed.map(([index]) => index));
|
|
1049
1182
|
const out2 = Array.from({ length: maxIndex + 1 }, () => null);
|
|
1050
1183
|
for (const [index, entry] of indexed) {
|
|
@@ -1174,6 +1307,93 @@ async function capturePropertyChildren(session, described, depth, maxValueLength
|
|
|
1174
1307
|
}
|
|
1175
1308
|
}
|
|
1176
1309
|
|
|
1310
|
+
// src/snapshot/exception.ts
|
|
1311
|
+
function asString2(value) {
|
|
1312
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1313
|
+
}
|
|
1314
|
+
async function materializeObject(session, objectId, maxValueLength) {
|
|
1315
|
+
try {
|
|
1316
|
+
const properties = await captureProperties(
|
|
1317
|
+
session,
|
|
1318
|
+
objectId,
|
|
1319
|
+
MAX_SCOPE_VARIABLES,
|
|
1320
|
+
MAX_VARIABLE_DEPTH,
|
|
1321
|
+
maxValueLength
|
|
1322
|
+
);
|
|
1323
|
+
if (properties.length === 0) {
|
|
1324
|
+
return void 0;
|
|
1325
|
+
}
|
|
1326
|
+
const structured = {};
|
|
1327
|
+
for (const variable of properties) {
|
|
1328
|
+
structured[variable.name] = toStructuredValue(variable);
|
|
1329
|
+
}
|
|
1330
|
+
return JSON.stringify(structured);
|
|
1331
|
+
} catch {
|
|
1332
|
+
return void 0;
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
async function readPropertyDescription(session, objectId, name) {
|
|
1336
|
+
try {
|
|
1337
|
+
const properties = await getProperties(session, objectId);
|
|
1338
|
+
for (const prop of properties) {
|
|
1339
|
+
if (prop.name !== name) {
|
|
1340
|
+
continue;
|
|
1341
|
+
}
|
|
1342
|
+
const value = prop.value;
|
|
1343
|
+
if (value === void 0) {
|
|
1344
|
+
continue;
|
|
1345
|
+
}
|
|
1346
|
+
if (typeof value.value === "string") {
|
|
1347
|
+
return value.value;
|
|
1348
|
+
}
|
|
1349
|
+
if (typeof value.description === "string") {
|
|
1350
|
+
return value.description;
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
return void 0;
|
|
1354
|
+
} catch {
|
|
1355
|
+
return void 0;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
async function captureException(session, pause, maxValueLength) {
|
|
1359
|
+
if (pause.reason !== "exception" && pause.reason !== "promiseRejection") {
|
|
1360
|
+
return void 0;
|
|
1361
|
+
}
|
|
1362
|
+
const data = pause.data;
|
|
1363
|
+
if (typeof data !== "object" || data === null) {
|
|
1364
|
+
return { error: "no exception data attached" };
|
|
1365
|
+
}
|
|
1366
|
+
const candidate = data;
|
|
1367
|
+
const type = asString2(candidate.type);
|
|
1368
|
+
const description = asString2(candidate.description);
|
|
1369
|
+
if (typeof candidate.value === "string") {
|
|
1370
|
+
return buildResult(type, description, JSON.stringify(candidate.value), maxValueLength);
|
|
1371
|
+
}
|
|
1372
|
+
if (typeof candidate.value === "number" || typeof candidate.value === "boolean") {
|
|
1373
|
+
return buildResult(type, description, String(candidate.value), maxValueLength);
|
|
1374
|
+
}
|
|
1375
|
+
const objectId = asString2(candidate.objectId);
|
|
1376
|
+
if (objectId === void 0) {
|
|
1377
|
+
if (description !== void 0) {
|
|
1378
|
+
return buildResult(type, description, description, maxValueLength);
|
|
1379
|
+
}
|
|
1380
|
+
return { error: "exception data has no objectId or value" };
|
|
1381
|
+
}
|
|
1382
|
+
const message = await readPropertyDescription(session, objectId, "message");
|
|
1383
|
+
const rendered = await materializeObject(session, objectId, maxValueLength);
|
|
1384
|
+
if (rendered !== void 0) {
|
|
1385
|
+
const result = buildResult(type, description, rendered, maxValueLength);
|
|
1386
|
+
return message === void 0 ? result : { ...result, description: limitValueLength(message, maxValueLength) };
|
|
1387
|
+
}
|
|
1388
|
+
return buildResult(type, description, description ?? "[exception]", maxValueLength);
|
|
1389
|
+
}
|
|
1390
|
+
function buildResult(type, description, value, maxValueLength) {
|
|
1391
|
+
const safeValue = limitValueLength(value, maxValueLength);
|
|
1392
|
+
const base = { value: safeValue };
|
|
1393
|
+
const withType = type === void 0 ? base : { ...base, type };
|
|
1394
|
+
return description === void 0 ? withType : { ...withType, description: limitValueLength(description, maxValueLength) };
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1177
1397
|
// src/snapshot/objects.ts
|
|
1178
1398
|
function objectIdFromEvalResult(result) {
|
|
1179
1399
|
const inner = result.result;
|
|
@@ -1276,12 +1496,73 @@ async function captureScopes(session, frame, maxValueLength) {
|
|
|
1276
1496
|
);
|
|
1277
1497
|
}
|
|
1278
1498
|
|
|
1499
|
+
// src/snapshot/stack.ts
|
|
1500
|
+
var DEFAULT_STACK_DEPTH = 1;
|
|
1501
|
+
var MAX_STACK_DEPTH = 64;
|
|
1502
|
+
function clampDepth(depth, frameCount) {
|
|
1503
|
+
if (depth <= 0) {
|
|
1504
|
+
return 0;
|
|
1505
|
+
}
|
|
1506
|
+
return Math.min(depth, frameCount, MAX_STACK_DEPTH);
|
|
1507
|
+
}
|
|
1508
|
+
function buildBaseFrame(frame) {
|
|
1509
|
+
const base = {
|
|
1510
|
+
functionName: frame.functionName,
|
|
1511
|
+
line: frame.lineNumber + 1,
|
|
1512
|
+
column: frame.columnNumber + 1
|
|
1513
|
+
};
|
|
1514
|
+
return frame.url === void 0 ? base : { ...base, url: frame.url };
|
|
1515
|
+
}
|
|
1516
|
+
async function captureFrameExpression(session, callFrameId, expression, maxValueLength) {
|
|
1517
|
+
try {
|
|
1518
|
+
const result = await evaluateOnFrame(session, callFrameId, expression);
|
|
1519
|
+
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1520
|
+
return await withSerializedObjectCapture(session, expression, result, captured, maxValueLength);
|
|
1521
|
+
} catch (err) {
|
|
1522
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1523
|
+
return { expression, error: limitValueLength(message, maxValueLength) };
|
|
1524
|
+
}
|
|
1525
|
+
}
|
|
1526
|
+
async function captureFrameExpressions(session, frame, expressions, maxValueLength) {
|
|
1527
|
+
if (expressions.length === 0) {
|
|
1528
|
+
return [];
|
|
1529
|
+
}
|
|
1530
|
+
return await Promise.all(
|
|
1531
|
+
expressions.map(
|
|
1532
|
+
(expression) => captureFrameExpression(session, frame.callFrameId, expression, maxValueLength)
|
|
1533
|
+
)
|
|
1534
|
+
);
|
|
1535
|
+
}
|
|
1536
|
+
async function walkStack(session, callFrames, options) {
|
|
1537
|
+
const depth = clampDepth(options.stackDepth, callFrames.length);
|
|
1538
|
+
if (depth <= 1) {
|
|
1539
|
+
return [];
|
|
1540
|
+
}
|
|
1541
|
+
const slice = callFrames.slice(0, depth);
|
|
1542
|
+
return await Promise.all(
|
|
1543
|
+
slice.map(async (frame) => {
|
|
1544
|
+
const base = buildBaseFrame(frame);
|
|
1545
|
+
if (options.stackCaptures.length === 0) {
|
|
1546
|
+
return base;
|
|
1547
|
+
}
|
|
1548
|
+
const captures = await captureFrameExpressions(
|
|
1549
|
+
session,
|
|
1550
|
+
frame,
|
|
1551
|
+
options.stackCaptures,
|
|
1552
|
+
options.maxValueLength
|
|
1553
|
+
);
|
|
1554
|
+
return { ...base, captures };
|
|
1555
|
+
})
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1279
1559
|
// src/snapshot/capture.ts
|
|
1280
1560
|
async function captureSnapshot(session, pause, options = {}) {
|
|
1281
1561
|
const maxValueLength = resolveMaxValueLength(options.maxValueLength);
|
|
1282
1562
|
const top = pause.callFrames[0];
|
|
1283
1563
|
let topFrame;
|
|
1284
1564
|
let captures = [];
|
|
1565
|
+
let stack = [];
|
|
1285
1566
|
if (top) {
|
|
1286
1567
|
topFrame = {
|
|
1287
1568
|
functionName: top.functionName,
|
|
@@ -1294,14 +1575,31 @@ async function captureSnapshot(session, pause, options = {}) {
|
|
|
1294
1575
|
topFrame = { ...topFrame, scopes };
|
|
1295
1576
|
}
|
|
1296
1577
|
captures = await captureExpressions(session, top.callFrameId, options.captures, maxValueLength);
|
|
1578
|
+
stack = await walkStack(session, pause.callFrames, {
|
|
1579
|
+
stackDepth: options.stackDepth ?? DEFAULT_STACK_DEPTH,
|
|
1580
|
+
stackCaptures: options.stackCaptures ?? [],
|
|
1581
|
+
maxValueLength
|
|
1582
|
+
});
|
|
1297
1583
|
}
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1584
|
+
const exception = await captureException(session, pause, maxValueLength);
|
|
1585
|
+
return buildResult2({
|
|
1586
|
+
pause,
|
|
1587
|
+
topFrame,
|
|
1588
|
+
captures,
|
|
1589
|
+
stack,
|
|
1590
|
+
exception
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
function buildResult2(input) {
|
|
1594
|
+
const base = {
|
|
1595
|
+
reason: input.pause.reason,
|
|
1596
|
+
hitBreakpoints: input.pause.hitBreakpoints,
|
|
1301
1597
|
capturedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1302
|
-
|
|
1303
|
-
captures
|
|
1598
|
+
captures: input.captures
|
|
1304
1599
|
};
|
|
1600
|
+
const withFrame = input.topFrame === void 0 ? base : { ...base, topFrame: input.topFrame };
|
|
1601
|
+
const withStack = input.stack.length > 0 ? { ...withFrame, stack: input.stack } : withFrame;
|
|
1602
|
+
return input.exception === void 0 ? withStack : { ...withStack, exception: input.exception };
|
|
1305
1603
|
}
|
|
1306
1604
|
async function captureExpressions(session, callFrameId, captures, maxValueLength) {
|
|
1307
1605
|
if (captures === void 0 || captures.length === 0) {
|
|
@@ -1328,7 +1626,8 @@ async function captureExpression(session, callFrameId, expression, maxValueLengt
|
|
|
1328
1626
|
import { randomBytes } from "crypto";
|
|
1329
1627
|
var SENTINEL_PREFIX = "__CFI_LOG_";
|
|
1330
1628
|
var SENTINEL_SUFFIX = "__";
|
|
1331
|
-
|
|
1629
|
+
var HITS_GLOBAL2 = "globalThis.__CFI_LOG_HITS";
|
|
1630
|
+
function buildLoggingIife(sentinel, expression) {
|
|
1332
1631
|
return [
|
|
1333
1632
|
"(function(){",
|
|
1334
1633
|
`var s=${JSON.stringify(sentinel)};`,
|
|
@@ -1343,12 +1642,53 @@ function buildLogpointCondition(sentinel, expression) {
|
|
|
1343
1642
|
"})()"
|
|
1344
1643
|
].join("");
|
|
1345
1644
|
}
|
|
1645
|
+
function buildHitGate(hitCount, counterKey) {
|
|
1646
|
+
const keyLiteral = JSON.stringify(counterKey);
|
|
1647
|
+
return [
|
|
1648
|
+
"(function(){",
|
|
1649
|
+
`var m=(${HITS_GLOBAL2}=${HITS_GLOBAL2}||{});`,
|
|
1650
|
+
`var k=${keyLiteral};`,
|
|
1651
|
+
"m[k]=(m[k]||0)+1;",
|
|
1652
|
+
`return m[k]>=${hitCount.toString()};`,
|
|
1653
|
+
"})()"
|
|
1654
|
+
].join("");
|
|
1655
|
+
}
|
|
1656
|
+
function combineGuards(guards) {
|
|
1657
|
+
const filtered = guards.filter((guard) => guard.length > 0);
|
|
1658
|
+
if (filtered.length === 0) {
|
|
1659
|
+
return void 0;
|
|
1660
|
+
}
|
|
1661
|
+
if (filtered.length === 1) {
|
|
1662
|
+
return filtered[0];
|
|
1663
|
+
}
|
|
1664
|
+
return filtered.map((guard) => `(${guard})`).join("&&");
|
|
1665
|
+
}
|
|
1666
|
+
function buildLogpointCondition(sentinel, expression, options = {}) {
|
|
1667
|
+
const guards = [];
|
|
1668
|
+
if (options.hitCount !== void 0) {
|
|
1669
|
+
const counterKey = options.counterKey ?? sentinel;
|
|
1670
|
+
guards.push(buildHitGate(options.hitCount, counterKey));
|
|
1671
|
+
}
|
|
1672
|
+
const userPredicate = options.predicate?.trim();
|
|
1673
|
+
if (userPredicate !== void 0 && userPredicate.length > 0) {
|
|
1674
|
+
guards.push(`(${userPredicate})`);
|
|
1675
|
+
}
|
|
1676
|
+
const iife = buildLoggingIife(sentinel, expression);
|
|
1677
|
+
const guard = combineGuards(guards);
|
|
1678
|
+
if (guard === void 0) {
|
|
1679
|
+
return iife;
|
|
1680
|
+
}
|
|
1681
|
+
return `(${guard})?${iife}:false`;
|
|
1682
|
+
}
|
|
1346
1683
|
function generateSentinel() {
|
|
1347
1684
|
return `${SENTINEL_PREFIX}${randomBytes(8).toString("hex")}${SENTINEL_SUFFIX}`;
|
|
1348
1685
|
}
|
|
1349
1686
|
|
|
1687
|
+
// src/logpoint/stream.ts
|
|
1688
|
+
init_types();
|
|
1689
|
+
|
|
1350
1690
|
// src/logpoint/events.ts
|
|
1351
|
-
function
|
|
1691
|
+
function asString3(value) {
|
|
1352
1692
|
return typeof value === "string" ? value : void 0;
|
|
1353
1693
|
}
|
|
1354
1694
|
function readArg(arg, index) {
|
|
@@ -1395,17 +1735,58 @@ function parsePayload(ts, at, payload) {
|
|
|
1395
1735
|
}
|
|
1396
1736
|
|
|
1397
1737
|
// src/logpoint/stream.ts
|
|
1738
|
+
function validateMaxEvents(maxEvents) {
|
|
1739
|
+
if (maxEvents === void 0) {
|
|
1740
|
+
return void 0;
|
|
1741
|
+
}
|
|
1742
|
+
if (!Number.isInteger(maxEvents) || maxEvents <= 0) {
|
|
1743
|
+
throw new CfInspectorError(
|
|
1744
|
+
"INVALID_ARGUMENT",
|
|
1745
|
+
`maxEvents must be a positive integer, received: ${maxEvents.toString()}`
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1748
|
+
return maxEvents;
|
|
1749
|
+
}
|
|
1750
|
+
function validateHitCount2(hitCount) {
|
|
1751
|
+
if (hitCount === void 0) {
|
|
1752
|
+
return void 0;
|
|
1753
|
+
}
|
|
1754
|
+
if (!Number.isInteger(hitCount) || hitCount <= 0) {
|
|
1755
|
+
throw new CfInspectorError(
|
|
1756
|
+
"INVALID_HIT_COUNT",
|
|
1757
|
+
`hitCount must be a positive integer, received: ${hitCount.toString()}`
|
|
1758
|
+
);
|
|
1759
|
+
}
|
|
1760
|
+
return hitCount;
|
|
1761
|
+
}
|
|
1398
1762
|
async function streamLogpoint(session, options) {
|
|
1763
|
+
const maxEvents = validateMaxEvents(options.maxEvents);
|
|
1764
|
+
const hitCount = validateHitCount2(options.hitCount);
|
|
1399
1765
|
const sentinel = generateSentinel();
|
|
1400
|
-
const condition = buildLogpointCondition(sentinel, options.expression
|
|
1766
|
+
const condition = buildLogpointCondition(sentinel, options.expression, {
|
|
1767
|
+
...options.condition === void 0 ? {} : { predicate: options.condition },
|
|
1768
|
+
...hitCount === void 0 ? {} : { hitCount }
|
|
1769
|
+
});
|
|
1401
1770
|
let emitted = 0;
|
|
1771
|
+
let maxEventsReached = false;
|
|
1772
|
+
let stopMaxEvents;
|
|
1402
1773
|
const offEvent = session.client.on("Runtime.consoleAPICalled", (raw) => {
|
|
1774
|
+
if (maxEventsReached) {
|
|
1775
|
+
return;
|
|
1776
|
+
}
|
|
1403
1777
|
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
1404
1778
|
if (event === void 0) {
|
|
1405
1779
|
return;
|
|
1406
1780
|
}
|
|
1407
1781
|
emitted += 1;
|
|
1408
|
-
|
|
1782
|
+
try {
|
|
1783
|
+
options.onEvent(event);
|
|
1784
|
+
} catch {
|
|
1785
|
+
}
|
|
1786
|
+
if (maxEvents !== void 0 && emitted >= maxEvents) {
|
|
1787
|
+
maxEventsReached = true;
|
|
1788
|
+
stopMaxEvents?.();
|
|
1789
|
+
}
|
|
1409
1790
|
});
|
|
1410
1791
|
let handle;
|
|
1411
1792
|
try {
|
|
@@ -1421,7 +1802,12 @@ async function streamLogpoint(session, options) {
|
|
|
1421
1802
|
}
|
|
1422
1803
|
options.onBreakpointSet?.(handle);
|
|
1423
1804
|
try {
|
|
1424
|
-
const reason = await waitForStop(session, options)
|
|
1805
|
+
const reason = await waitForStop(session, options, (signal) => {
|
|
1806
|
+
stopMaxEvents = signal;
|
|
1807
|
+
if (maxEventsReached) {
|
|
1808
|
+
signal();
|
|
1809
|
+
}
|
|
1810
|
+
});
|
|
1425
1811
|
return { handle, sentinel, emitted, stoppedReason: reason };
|
|
1426
1812
|
} finally {
|
|
1427
1813
|
offEvent();
|
|
@@ -1430,7 +1816,7 @@ async function streamLogpoint(session, options) {
|
|
|
1430
1816
|
}
|
|
1431
1817
|
function toLogpointEvent(raw, sentinel, location) {
|
|
1432
1818
|
const params = raw;
|
|
1433
|
-
if (
|
|
1819
|
+
if (asString3(params.type) !== "log") {
|
|
1434
1820
|
return void 0;
|
|
1435
1821
|
}
|
|
1436
1822
|
const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
|
|
@@ -1442,7 +1828,7 @@ async function removeBreakpointBestEffort(session, breakpointId) {
|
|
|
1442
1828
|
} catch {
|
|
1443
1829
|
}
|
|
1444
1830
|
}
|
|
1445
|
-
async function waitForStop(session, options) {
|
|
1831
|
+
async function waitForStop(session, options, registerMaxEventsSignal) {
|
|
1446
1832
|
return await new Promise((resolve) => {
|
|
1447
1833
|
let settled = false;
|
|
1448
1834
|
const finish = (reason) => {
|
|
@@ -1466,6 +1852,9 @@ async function waitForStop(session, options) {
|
|
|
1466
1852
|
if (options.signal?.aborted === true) {
|
|
1467
1853
|
finish("signal");
|
|
1468
1854
|
}
|
|
1855
|
+
registerMaxEventsSignal(() => {
|
|
1856
|
+
finish("max-events");
|
|
1857
|
+
});
|
|
1469
1858
|
function cleanup() {
|
|
1470
1859
|
if (timer !== void 0) {
|
|
1471
1860
|
clearTimeout(timer);
|
|
@@ -1501,7 +1890,9 @@ async function openCfTunnel(target) {
|
|
|
1501
1890
|
export {
|
|
1502
1891
|
CfInspectorError,
|
|
1503
1892
|
buildBreakpointUrlRegex,
|
|
1893
|
+
buildHitCountedCondition,
|
|
1504
1894
|
buildLogpointCondition,
|
|
1895
|
+
captureException,
|
|
1505
1896
|
captureSnapshot,
|
|
1506
1897
|
connectInspector,
|
|
1507
1898
|
discoverInspectorTargets,
|
|
@@ -1516,8 +1907,10 @@ export {
|
|
|
1516
1907
|
removeBreakpoint,
|
|
1517
1908
|
resume,
|
|
1518
1909
|
setBreakpoint,
|
|
1910
|
+
setPauseOnExceptions,
|
|
1519
1911
|
streamLogpoint,
|
|
1520
1912
|
validateExpression,
|
|
1521
|
-
waitForPause
|
|
1913
|
+
waitForPause,
|
|
1914
|
+
walkStack
|
|
1522
1915
|
};
|
|
1523
1916
|
//# sourceMappingURL=index.js.map
|