@saptools/cf-inspector 0.4.11 → 0.5.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 +169 -31
- package/dist/cli.js +1220 -429
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +44 -3
- package/dist/index.js +665 -116
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -474,42 +474,93 @@ async function removeBreakpoint(session, breakpointId) {
|
|
|
474
474
|
// src/inspector/discovery.ts
|
|
475
475
|
init_types();
|
|
476
476
|
import { request } from "http";
|
|
477
|
+
import { performance } from "perf_hooks";
|
|
478
|
+
var InvalidDiscoveryPayloadError = class extends CfInspectorError {
|
|
479
|
+
};
|
|
477
480
|
async function fetchJson(url, timeoutMs) {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
481
|
+
const deadline = performance.now() + timeoutMs;
|
|
482
|
+
let lastError;
|
|
483
|
+
while (performance.now() < deadline) {
|
|
484
|
+
try {
|
|
485
|
+
const remainingMs = deadline - performance.now();
|
|
486
|
+
if (remainingMs <= 0) {
|
|
487
|
+
break;
|
|
488
|
+
}
|
|
489
|
+
return await new Promise((resolve, reject) => {
|
|
490
|
+
const req = request(url, { method: "GET" }, (res) => {
|
|
491
|
+
const chunks = [];
|
|
492
|
+
res.on("data", (chunk) => {
|
|
493
|
+
chunks.push(chunk);
|
|
494
|
+
});
|
|
495
|
+
res.on("end", () => {
|
|
496
|
+
try {
|
|
497
|
+
resolve(parseJsonResponse(chunks));
|
|
498
|
+
} catch (err) {
|
|
499
|
+
reject(parseDiscoveryError(url, err));
|
|
500
|
+
}
|
|
501
|
+
});
|
|
502
|
+
res.on("error", (err) => {
|
|
503
|
+
reject(newDiscoveryError(`Inspector discovery response error: ${err.message}`));
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
const attemptTimeoutMs = Math.min(2e3, remainingMs);
|
|
507
|
+
req.setTimeout(attemptTimeoutMs, () => {
|
|
508
|
+
req.destroy(
|
|
509
|
+
new CfInspectorError(
|
|
510
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
511
|
+
`Inspector discovery at ${url} timed out after ${timeoutMs.toString()}ms`
|
|
512
|
+
)
|
|
513
|
+
);
|
|
514
|
+
});
|
|
515
|
+
req.on("error", (err) => {
|
|
516
|
+
reject(err instanceof CfInspectorError ? err : formatDiscoveryRequestError(url, err));
|
|
517
|
+
});
|
|
518
|
+
req.end();
|
|
493
519
|
});
|
|
494
|
-
})
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
520
|
+
} catch (err) {
|
|
521
|
+
if (err instanceof InvalidDiscoveryPayloadError) {
|
|
522
|
+
throw err;
|
|
523
|
+
}
|
|
524
|
+
lastError = err;
|
|
525
|
+
const now = performance.now();
|
|
526
|
+
if (now < deadline) {
|
|
527
|
+
const sleepMs = Math.min(1e3, deadline - now);
|
|
528
|
+
await new Promise((r) => setTimeout(r, sleepMs));
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
if (lastError instanceof Error) {
|
|
533
|
+
throw lastError;
|
|
534
|
+
}
|
|
535
|
+
throw new CfInspectorError("INSPECTOR_DISCOVERY_FAILED", `Inspector discovery at ${url} timed out after ${timeoutMs.toString()}ms`);
|
|
536
|
+
}
|
|
537
|
+
function isNodeSystemError(err) {
|
|
538
|
+
return err instanceof Error;
|
|
539
|
+
}
|
|
540
|
+
function isConnectionRefusedOrUnreachable(code) {
|
|
541
|
+
return code === "ECONNREFUSED" || code === "ECONNRESET" || code === "ETIMEDOUT" || code === "EHOSTUNREACH" || code === "ENETUNREACH";
|
|
542
|
+
}
|
|
543
|
+
function formatEndpoint(url, err) {
|
|
544
|
+
if (typeof err.address === "string" && typeof err.port === "number") {
|
|
545
|
+
return `${err.address}:${err.port.toString()}`;
|
|
546
|
+
}
|
|
547
|
+
const parsed = new URL(url);
|
|
548
|
+
return parsed.host;
|
|
549
|
+
}
|
|
550
|
+
function formatDiscoveryRequestError(url, err) {
|
|
551
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
552
|
+
if (!isNodeSystemError(err) || !isConnectionRefusedOrUnreachable(err.code)) {
|
|
553
|
+
return new CfInspectorError(
|
|
554
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
555
|
+
`Inspector discovery at ${url} failed: ${detail}`
|
|
556
|
+
);
|
|
557
|
+
}
|
|
558
|
+
const endpoint = formatEndpoint(url, err);
|
|
559
|
+
return new CfInspectorError(
|
|
560
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
561
|
+
`Cannot reach Node inspector discovery at ${url}. Nothing is listening on ${endpoint}, or the inspector tunnel is stale/closed. Restart the local inspector or tunnel and retry. If this port came from cf-debugger, stop the stale session and start a fresh tunnel, or run cf-inspector with --app/--region/--org/--space so it can open a tunnel.`,
|
|
562
|
+
detail
|
|
563
|
+
);
|
|
513
564
|
}
|
|
514
565
|
function parseJsonResponse(chunks) {
|
|
515
566
|
const text = Buffer.concat(chunks).toString("utf8");
|
|
@@ -517,7 +568,10 @@ function parseJsonResponse(chunks) {
|
|
|
517
568
|
}
|
|
518
569
|
function parseDiscoveryError(url, err) {
|
|
519
570
|
const message = err instanceof Error ? err.message : String(err);
|
|
520
|
-
return
|
|
571
|
+
return new InvalidDiscoveryPayloadError(
|
|
572
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
573
|
+
`Failed to parse inspector discovery response from ${url}: ${message}`
|
|
574
|
+
);
|
|
521
575
|
}
|
|
522
576
|
function newDiscoveryError(message) {
|
|
523
577
|
return new CfInspectorError("INSPECTOR_DISCOVERY_FAILED", message);
|
|
@@ -591,7 +645,7 @@ async function fetchInspectorVersion(host, port, timeoutMs) {
|
|
|
591
645
|
|
|
592
646
|
// src/inspector/pause.ts
|
|
593
647
|
init_types();
|
|
594
|
-
import { performance } from "perf_hooks";
|
|
648
|
+
import { performance as performance2 } from "perf_hooks";
|
|
595
649
|
function pauseMatches(pause, breakpointIds, pauseReasons) {
|
|
596
650
|
if (pauseReasons !== void 0 && pauseReasons.length > 0) {
|
|
597
651
|
return pauseReasons.includes(pause.reason);
|
|
@@ -602,7 +656,7 @@ function pauseMatches(pause, breakpointIds, pauseReasons) {
|
|
|
602
656
|
return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
|
|
603
657
|
}
|
|
604
658
|
function remainingUntil(deadlineMs) {
|
|
605
|
-
return Math.max(0, deadlineMs -
|
|
659
|
+
return Math.max(0, deadlineMs - performance2.now());
|
|
606
660
|
}
|
|
607
661
|
function hasResumedSincePause(session, pause) {
|
|
608
662
|
const pauseAt = pause.receivedAtMs;
|
|
@@ -632,7 +686,7 @@ async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, timeout
|
|
|
632
686
|
}
|
|
633
687
|
try {
|
|
634
688
|
await session.client.waitFor("Debugger.resumed", { timeoutMs: remainingMs });
|
|
635
|
-
session.debuggerState.lastResumedAtMs =
|
|
689
|
+
session.debuggerState.lastResumedAtMs = performance2.now();
|
|
636
690
|
} catch (err) {
|
|
637
691
|
if (err instanceof CfInspectorError && err.code === "BREAKPOINT_NOT_HIT") {
|
|
638
692
|
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
@@ -655,7 +709,7 @@ async function handleUnmatchedPause(session, pause, options, deadlineMs) {
|
|
|
655
709
|
await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
|
|
656
710
|
}
|
|
657
711
|
async function waitForPause(session, options) {
|
|
658
|
-
const deadlineMs =
|
|
712
|
+
const deadlineMs = performance2.now() + options.timeoutMs;
|
|
659
713
|
const buffer = session.pauseBuffer;
|
|
660
714
|
while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
|
|
661
715
|
while (buffer.length > 0) {
|
|
@@ -688,14 +742,14 @@ async function waitForLivePause(session, options, deadlineMs) {
|
|
|
688
742
|
params = await session.client.waitFor("Debugger.paused", {
|
|
689
743
|
timeoutMs: remainingMs,
|
|
690
744
|
predicate: () => {
|
|
691
|
-
receivedAtMs =
|
|
745
|
+
receivedAtMs = performance2.now();
|
|
692
746
|
return true;
|
|
693
747
|
}
|
|
694
748
|
});
|
|
695
749
|
} finally {
|
|
696
750
|
session.pauseWaitGate.active = false;
|
|
697
751
|
}
|
|
698
|
-
return toPauseEvent(params, receivedAtMs ??
|
|
752
|
+
return toPauseEvent(params, receivedAtMs ?? performance2.now(), session.scripts);
|
|
699
753
|
}
|
|
700
754
|
|
|
701
755
|
// src/inspector/runtime.ts
|
|
@@ -706,15 +760,30 @@ async function resume(session) {
|
|
|
706
760
|
async function setPauseOnExceptions(session, state) {
|
|
707
761
|
await session.client.send("Debugger.setPauseOnExceptions", { state });
|
|
708
762
|
}
|
|
709
|
-
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
763
|
+
async function evaluateOnFrame(session, callFrameId, expression, options = {}) {
|
|
710
764
|
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
711
765
|
callFrameId,
|
|
712
766
|
expression,
|
|
713
767
|
returnByValue: false,
|
|
714
768
|
generatePreview: true,
|
|
715
|
-
silent: true
|
|
769
|
+
silent: true,
|
|
770
|
+
...options.throwOnSideEffect === void 0 ? {} : { throwOnSideEffect: options.throwOnSideEffect }
|
|
716
771
|
});
|
|
717
772
|
}
|
|
773
|
+
function isSideEffectRefusal(result) {
|
|
774
|
+
const classNames = [
|
|
775
|
+
result.result?.className,
|
|
776
|
+
result.exceptionDetails?.exception?.className
|
|
777
|
+
];
|
|
778
|
+
const descriptions = [
|
|
779
|
+
result.result?.description,
|
|
780
|
+
result.exceptionDetails?.exception?.description
|
|
781
|
+
];
|
|
782
|
+
const isEvalError = classNames.includes("EvalError");
|
|
783
|
+
return isEvalError && descriptions.some(
|
|
784
|
+
(description) => typeof description === "string" && description.toLowerCase().includes("possible side-effect in debug-evaluate")
|
|
785
|
+
);
|
|
786
|
+
}
|
|
718
787
|
async function evaluateGlobal(session, expression) {
|
|
719
788
|
return await session.client.send("Runtime.evaluate", {
|
|
720
789
|
expression,
|
|
@@ -766,7 +835,7 @@ async function getProperties(session, objectId) {
|
|
|
766
835
|
}
|
|
767
836
|
|
|
768
837
|
// src/inspector/session.ts
|
|
769
|
-
import { performance as
|
|
838
|
+
import { performance as performance3 } from "perf_hooks";
|
|
770
839
|
|
|
771
840
|
// src/cdp/client.ts
|
|
772
841
|
init_types();
|
|
@@ -1008,12 +1077,203 @@ var CdpClient = class _CdpClient {
|
|
|
1008
1077
|
this.emitter.removeAllListeners();
|
|
1009
1078
|
}
|
|
1010
1079
|
};
|
|
1080
|
+
var NodeWorkerTransport = class {
|
|
1081
|
+
constructor(parent, sessionId) {
|
|
1082
|
+
this.parent = parent;
|
|
1083
|
+
this.sessionId = sessionId;
|
|
1084
|
+
this.detachParentListeners = [
|
|
1085
|
+
parent.on("NodeWorker.receivedMessageFromWorker", (raw) => {
|
|
1086
|
+
this.forwardWorkerMessage(raw);
|
|
1087
|
+
}),
|
|
1088
|
+
parent.on("NodeWorker.detachedFromWorker", (raw) => {
|
|
1089
|
+
this.handleWorkerDetach(raw);
|
|
1090
|
+
}),
|
|
1091
|
+
parent.onClose((error) => {
|
|
1092
|
+
this.closeWithError(error);
|
|
1093
|
+
})
|
|
1094
|
+
];
|
|
1095
|
+
}
|
|
1096
|
+
parent;
|
|
1097
|
+
sessionId;
|
|
1098
|
+
emitter = new EventEmitter();
|
|
1099
|
+
detachParentListeners;
|
|
1100
|
+
readyState = 1;
|
|
1101
|
+
send(payload) {
|
|
1102
|
+
if (this.readyState !== 1) {
|
|
1103
|
+
throw new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Worker inspector session is closed");
|
|
1104
|
+
}
|
|
1105
|
+
void this.parent.send("NodeWorker.sendMessageToWorker", {
|
|
1106
|
+
sessionId: this.sessionId,
|
|
1107
|
+
message: payload
|
|
1108
|
+
}).catch((error) => {
|
|
1109
|
+
const normalized = error instanceof Error ? error : new Error(String(error));
|
|
1110
|
+
this.closeWithError(normalized);
|
|
1111
|
+
});
|
|
1112
|
+
}
|
|
1113
|
+
close() {
|
|
1114
|
+
this.finishClose();
|
|
1115
|
+
}
|
|
1116
|
+
on(event, listener) {
|
|
1117
|
+
this.emitter.on(event, listener);
|
|
1118
|
+
}
|
|
1119
|
+
off(event, listener) {
|
|
1120
|
+
this.emitter.off(event, listener);
|
|
1121
|
+
}
|
|
1122
|
+
forwardWorkerMessage(raw) {
|
|
1123
|
+
const params = asNodeWorkerEventParams(raw);
|
|
1124
|
+
if (params.sessionId !== this.sessionId || typeof params.message !== "string") {
|
|
1125
|
+
return;
|
|
1126
|
+
}
|
|
1127
|
+
this.emitter.emit("message", params.message);
|
|
1128
|
+
}
|
|
1129
|
+
handleWorkerDetach(raw) {
|
|
1130
|
+
const params = asNodeWorkerEventParams(raw);
|
|
1131
|
+
if (params.sessionId === this.sessionId) {
|
|
1132
|
+
this.finishClose();
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
closeWithError(error) {
|
|
1136
|
+
if (this.readyState !== 1) {
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
this.emitter.emit("error", error);
|
|
1140
|
+
this.finishClose();
|
|
1141
|
+
}
|
|
1142
|
+
finishClose() {
|
|
1143
|
+
if (this.readyState !== 1) {
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
this.readyState = 3;
|
|
1147
|
+
for (const detach of this.detachParentListeners) {
|
|
1148
|
+
detach();
|
|
1149
|
+
}
|
|
1150
|
+
this.emitter.emit("close");
|
|
1151
|
+
this.emitter.removeAllListeners();
|
|
1152
|
+
}
|
|
1153
|
+
};
|
|
1154
|
+
function asNodeWorkerEventParams(raw) {
|
|
1155
|
+
if (!isUnknownRecord(raw)) {
|
|
1156
|
+
return {};
|
|
1157
|
+
}
|
|
1158
|
+
const sessionId = raw["sessionId"];
|
|
1159
|
+
const message = raw["message"];
|
|
1160
|
+
return {
|
|
1161
|
+
...typeof sessionId === "string" ? { sessionId } : {},
|
|
1162
|
+
...typeof message === "string" ? { message } : {}
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
function isUnknownRecord(value) {
|
|
1166
|
+
return typeof value === "object" && value !== null;
|
|
1167
|
+
}
|
|
1168
|
+
async function createNodeWorkerClient(parent, sessionId, requestTimeoutMs = DEFAULT_REQUEST_TIMEOUT_MS) {
|
|
1169
|
+
const transport = new NodeWorkerTransport(parent, sessionId);
|
|
1170
|
+
return await CdpClient.connect({
|
|
1171
|
+
url: `node-worker://${sessionId}`,
|
|
1172
|
+
transportFactory: () => Promise.resolve(transport),
|
|
1173
|
+
requestTimeoutMs
|
|
1174
|
+
});
|
|
1175
|
+
}
|
|
1011
1176
|
|
|
1012
1177
|
// src/inspector/session.ts
|
|
1013
1178
|
init_types();
|
|
1014
1179
|
var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
|
|
1015
1180
|
var DEFAULT_HOST = "127.0.0.1";
|
|
1016
1181
|
var PAUSE_BUFFER_LIMIT = 32;
|
|
1182
|
+
var NodeWorkerDiscovery = class {
|
|
1183
|
+
constructor(client) {
|
|
1184
|
+
this.client = client;
|
|
1185
|
+
this.detachListeners = [
|
|
1186
|
+
client.on("NodeWorker.attachedToWorker", (raw) => {
|
|
1187
|
+
const worker = toInspectorWorkerTarget(raw);
|
|
1188
|
+
if (worker !== void 0) {
|
|
1189
|
+
this.workers.set(worker.sessionId, worker);
|
|
1190
|
+
}
|
|
1191
|
+
}),
|
|
1192
|
+
client.on("NodeWorker.detachedFromWorker", (raw) => {
|
|
1193
|
+
const sessionId = readField(raw, "sessionId");
|
|
1194
|
+
if (typeof sessionId === "string") {
|
|
1195
|
+
this.workers.delete(sessionId);
|
|
1196
|
+
}
|
|
1197
|
+
})
|
|
1198
|
+
];
|
|
1199
|
+
}
|
|
1200
|
+
client;
|
|
1201
|
+
workers = /* @__PURE__ */ new Map();
|
|
1202
|
+
detachListeners;
|
|
1203
|
+
supported = false;
|
|
1204
|
+
disposed = false;
|
|
1205
|
+
async enable() {
|
|
1206
|
+
try {
|
|
1207
|
+
await this.client.send("NodeWorker.enable", { waitForDebuggerOnStart: false });
|
|
1208
|
+
this.supported = true;
|
|
1209
|
+
} catch (error) {
|
|
1210
|
+
if (!isUnsupportedNodeWorkerDomain(error)) {
|
|
1211
|
+
throw error;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
list() {
|
|
1216
|
+
return [...this.workers.values()].sort(compareWorkers);
|
|
1217
|
+
}
|
|
1218
|
+
async dispose() {
|
|
1219
|
+
if (this.disposed) {
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1222
|
+
this.disposed = true;
|
|
1223
|
+
if (this.supported && !this.client.isClosed) {
|
|
1224
|
+
try {
|
|
1225
|
+
await this.client.send("NodeWorker.disable");
|
|
1226
|
+
} catch {
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
for (const detach of this.detachListeners) {
|
|
1230
|
+
detach();
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
};
|
|
1234
|
+
function isUnsupportedNodeWorkerDomain(error) {
|
|
1235
|
+
if (!(error instanceof CfInspectorError) || error.code !== "CDP_REQUEST_FAILED") {
|
|
1236
|
+
return false;
|
|
1237
|
+
}
|
|
1238
|
+
return error.detail?.includes('"code":-32601') === true;
|
|
1239
|
+
}
|
|
1240
|
+
function compareWorkers(left, right) {
|
|
1241
|
+
const leftId = Number.parseInt(left.workerId, 10);
|
|
1242
|
+
const rightId = Number.parseInt(right.workerId, 10);
|
|
1243
|
+
if (!Number.isNaN(leftId) && !Number.isNaN(rightId) && leftId !== rightId) {
|
|
1244
|
+
return leftId - rightId;
|
|
1245
|
+
}
|
|
1246
|
+
return left.workerId.localeCompare(right.workerId);
|
|
1247
|
+
}
|
|
1248
|
+
function toInspectorWorkerTarget(raw) {
|
|
1249
|
+
const sessionId = readField(raw, "sessionId");
|
|
1250
|
+
const info = readField(raw, "workerInfo");
|
|
1251
|
+
if (typeof sessionId !== "string" || !isUnknownRecord2(info)) {
|
|
1252
|
+
return void 0;
|
|
1253
|
+
}
|
|
1254
|
+
const workerId = asString(info["workerId"]);
|
|
1255
|
+
if (workerId.length === 0) {
|
|
1256
|
+
return void 0;
|
|
1257
|
+
}
|
|
1258
|
+
return {
|
|
1259
|
+
sessionId,
|
|
1260
|
+
workerId,
|
|
1261
|
+
type: asString(info["type"]),
|
|
1262
|
+
title: asString(info["title"]),
|
|
1263
|
+
url: asString(info["url"])
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
function readField(value, name) {
|
|
1267
|
+
return isUnknownRecord2(value) ? value[name] : void 0;
|
|
1268
|
+
}
|
|
1269
|
+
function isUnknownRecord2(value) {
|
|
1270
|
+
return typeof value === "object" && value !== null;
|
|
1271
|
+
}
|
|
1272
|
+
async function startNodeWorkerDiscovery(client) {
|
|
1273
|
+
const discovery = new NodeWorkerDiscovery(client);
|
|
1274
|
+
await discovery.enable();
|
|
1275
|
+
return discovery;
|
|
1276
|
+
}
|
|
1017
1277
|
async function connectInspector(options) {
|
|
1018
1278
|
const host = options.host ?? DEFAULT_HOST;
|
|
1019
1279
|
const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
@@ -1030,13 +1290,70 @@ async function connectInspector(options) {
|
|
|
1030
1290
|
url: target.webSocketDebuggerUrl,
|
|
1031
1291
|
connectTimeoutMs
|
|
1032
1292
|
});
|
|
1293
|
+
let workerDiscovery;
|
|
1033
1294
|
try {
|
|
1034
|
-
|
|
1295
|
+
workerDiscovery = await startNodeWorkerDiscovery(client);
|
|
1296
|
+
if (options.workerIndex === void 0) {
|
|
1297
|
+
const session = await initSession(client, target);
|
|
1298
|
+
return withWorkerMetadata(session, workerDiscovery, targetIndex, targets.length);
|
|
1299
|
+
}
|
|
1300
|
+
return await initWorkerSession(
|
|
1301
|
+
client,
|
|
1302
|
+
workerDiscovery,
|
|
1303
|
+
options.workerIndex,
|
|
1304
|
+
targetIndex,
|
|
1305
|
+
targets.length
|
|
1306
|
+
);
|
|
1035
1307
|
} catch (err) {
|
|
1308
|
+
await workerDiscovery?.dispose();
|
|
1036
1309
|
client.dispose();
|
|
1037
1310
|
throw err;
|
|
1038
1311
|
}
|
|
1039
1312
|
}
|
|
1313
|
+
async function initWorkerSession(parent, discovery, workerIndex, targetIndex, targetCount) {
|
|
1314
|
+
const workers = discovery.list();
|
|
1315
|
+
if (!discovery.supported) {
|
|
1316
|
+
throw new CfInspectorError(
|
|
1317
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
1318
|
+
"This runtime does not expose the NodeWorker CDP domain; --worker cannot be used. Run list-targets for available raw targets."
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
const worker = workers[workerIndex];
|
|
1322
|
+
if (worker === void 0) {
|
|
1323
|
+
throw new CfInspectorError(
|
|
1324
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
1325
|
+
`No NodeWorker sub-session at index ${workerIndex.toString()} (available: ${workers.length.toString()}). Ensure the worker is alive, then rerun list-targets.`
|
|
1326
|
+
);
|
|
1327
|
+
}
|
|
1328
|
+
const client = await createNodeWorkerClient(parent, worker.sessionId);
|
|
1329
|
+
const session = await initSession(client, workerToInspectorTarget(worker));
|
|
1330
|
+
return withWorkerMetadata(session, discovery, targetIndex, targetCount, workerIndex, parent);
|
|
1331
|
+
}
|
|
1332
|
+
function withWorkerMetadata(session, discovery, targetIndex, targetCount, workerIndex, parent) {
|
|
1333
|
+
return {
|
|
1334
|
+
...session,
|
|
1335
|
+
targetIndex,
|
|
1336
|
+
targetCount,
|
|
1337
|
+
...workerIndex === void 0 ? {} : { workerIndex },
|
|
1338
|
+
workerTargets: discovery.list(),
|
|
1339
|
+
workerDiscoverySupported: discovery.supported,
|
|
1340
|
+
dispose: async () => {
|
|
1341
|
+
await session.dispose();
|
|
1342
|
+
await discovery.dispose();
|
|
1343
|
+
parent?.dispose();
|
|
1344
|
+
}
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1347
|
+
function workerToInspectorTarget(worker) {
|
|
1348
|
+
return {
|
|
1349
|
+
description: "Node worker sub-session",
|
|
1350
|
+
id: worker.workerId,
|
|
1351
|
+
title: worker.title,
|
|
1352
|
+
type: worker.type,
|
|
1353
|
+
url: worker.url,
|
|
1354
|
+
webSocketDebuggerUrl: `node-worker://${worker.sessionId}`
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1040
1357
|
async function initSession(client, target) {
|
|
1041
1358
|
const scripts = /* @__PURE__ */ new Map();
|
|
1042
1359
|
client.on("Debugger.scriptParsed", (raw) => {
|
|
@@ -1056,14 +1373,14 @@ async function initSession(client, target) {
|
|
|
1056
1373
|
return;
|
|
1057
1374
|
}
|
|
1058
1375
|
const params = raw;
|
|
1059
|
-
const event = toPauseEvent(params,
|
|
1376
|
+
const event = toPauseEvent(params, performance3.now(), scripts);
|
|
1060
1377
|
if (pauseBuffer.length >= PAUSE_BUFFER_LIMIT) {
|
|
1061
1378
|
pauseBuffer.shift();
|
|
1062
1379
|
}
|
|
1063
1380
|
pauseBuffer.push(event);
|
|
1064
1381
|
});
|
|
1065
1382
|
client.on("Debugger.resumed", () => {
|
|
1066
|
-
debuggerState.lastResumedAtMs =
|
|
1383
|
+
debuggerState.lastResumedAtMs = performance3.now();
|
|
1067
1384
|
});
|
|
1068
1385
|
await client.send("Runtime.enable");
|
|
1069
1386
|
await client.send("Debugger.enable");
|
|
@@ -1084,9 +1401,61 @@ async function initSession(client, target) {
|
|
|
1084
1401
|
};
|
|
1085
1402
|
}
|
|
1086
1403
|
|
|
1404
|
+
// src/cli/captureParser.ts
|
|
1405
|
+
function isQuoteChar(value) {
|
|
1406
|
+
return value === "'" || value === '"' || value === "`";
|
|
1407
|
+
}
|
|
1408
|
+
function consumeQuotedChar(state, char) {
|
|
1409
|
+
if (state.quote === void 0) {
|
|
1410
|
+
return false;
|
|
1411
|
+
}
|
|
1412
|
+
if (state.escaped) {
|
|
1413
|
+
state.escaped = false;
|
|
1414
|
+
return true;
|
|
1415
|
+
}
|
|
1416
|
+
if (char === "\\") {
|
|
1417
|
+
state.escaped = true;
|
|
1418
|
+
return true;
|
|
1419
|
+
}
|
|
1420
|
+
if (char === state.quote) {
|
|
1421
|
+
state.quote = void 0;
|
|
1422
|
+
}
|
|
1423
|
+
return true;
|
|
1424
|
+
}
|
|
1425
|
+
function stripQuotedText(expression) {
|
|
1426
|
+
const state = { quote: void 0, escaped: false };
|
|
1427
|
+
let stripped = "";
|
|
1428
|
+
for (const char of expression) {
|
|
1429
|
+
if (consumeQuotedChar(state, char)) {
|
|
1430
|
+
stripped += " ";
|
|
1431
|
+
continue;
|
|
1432
|
+
}
|
|
1433
|
+
if (isQuoteChar(char)) {
|
|
1434
|
+
state.quote = char;
|
|
1435
|
+
stripped += " ";
|
|
1436
|
+
continue;
|
|
1437
|
+
}
|
|
1438
|
+
stripped += char;
|
|
1439
|
+
}
|
|
1440
|
+
return stripped;
|
|
1441
|
+
}
|
|
1442
|
+
function looksLikeMutation(expression) {
|
|
1443
|
+
const stripped = stripQuotedText(expression);
|
|
1444
|
+
const hasUpdate = /(?:\+\+|--)/u.test(stripped);
|
|
1445
|
+
const hasAssignment = /(?:\*\*=|&&=|\|\|=|\?\?=|[+\-*/%&|^]=|(?:^|[^=!<>])=(?!=|>))/u.test(stripped);
|
|
1446
|
+
const hasDelete = /\bdelete\b/u.test(stripped);
|
|
1447
|
+
const hasMutatingMethod = /\.\s*(?:push|pop|shift|unshift|splice|sort|reverse|fill|copyWithin|set|add|delete|clear)\s*\(/u.test(stripped);
|
|
1448
|
+
const hasObjectMutation = /\bObject\s*\.\s*(?:assign|defineProperty|defineProperties)\s*\(/u.test(stripped);
|
|
1449
|
+
return hasUpdate || hasAssignment || hasDelete || hasMutatingMethod || hasObjectMutation;
|
|
1450
|
+
}
|
|
1451
|
+
|
|
1452
|
+
// src/snapshot/evaluation.ts
|
|
1453
|
+
init_types();
|
|
1454
|
+
|
|
1087
1455
|
// src/snapshot/values.ts
|
|
1088
1456
|
init_types();
|
|
1089
|
-
var DEFAULT_MAX_VALUE_LENGTH =
|
|
1457
|
+
var DEFAULT_MAX_VALUE_LENGTH = 131072;
|
|
1458
|
+
var DEFAULT_STREAM_MAX_VALUE_LENGTH = 4096;
|
|
1090
1459
|
function isPrimitive(value) {
|
|
1091
1460
|
const t = typeof value;
|
|
1092
1461
|
return t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol";
|
|
@@ -1114,9 +1483,16 @@ function resolveMaxValueLength(value) {
|
|
|
1114
1483
|
}
|
|
1115
1484
|
function limitValueLength(raw, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
1116
1485
|
if (raw.length <= maxValueLength) {
|
|
1117
|
-
return raw;
|
|
1486
|
+
return { text: raw, truncated: false };
|
|
1118
1487
|
}
|
|
1119
|
-
return
|
|
1488
|
+
return {
|
|
1489
|
+
text: raw.slice(0, maxValueLength),
|
|
1490
|
+
truncated: true,
|
|
1491
|
+
originalLength: raw.length
|
|
1492
|
+
};
|
|
1493
|
+
}
|
|
1494
|
+
function textTruncationFields(limited) {
|
|
1495
|
+
return limited.truncated ? { truncated: true, originalLength: limited.originalLength } : {};
|
|
1120
1496
|
}
|
|
1121
1497
|
function parseQuotedString(value) {
|
|
1122
1498
|
try {
|
|
@@ -1201,7 +1577,12 @@ function toStructuredValue(variable) {
|
|
|
1201
1577
|
// src/snapshot/evaluation.ts
|
|
1202
1578
|
function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
1203
1579
|
if (result.exceptionDetails !== void 0) {
|
|
1204
|
-
|
|
1580
|
+
const limited = readEvalError(result, maxValueLength);
|
|
1581
|
+
return {
|
|
1582
|
+
expression,
|
|
1583
|
+
error: limited.text,
|
|
1584
|
+
...textTruncationFields(limited)
|
|
1585
|
+
};
|
|
1205
1586
|
}
|
|
1206
1587
|
const inner = result.result;
|
|
1207
1588
|
if (!inner) {
|
|
@@ -1209,8 +1590,12 @@ function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_V
|
|
|
1209
1590
|
}
|
|
1210
1591
|
const type = typeof inner.type === "string" ? inner.type : void 0;
|
|
1211
1592
|
const buildCaptured = (rendered) => {
|
|
1212
|
-
const
|
|
1213
|
-
const base = {
|
|
1593
|
+
const limited = limitValueLength(rendered, maxValueLength);
|
|
1594
|
+
const base = {
|
|
1595
|
+
expression,
|
|
1596
|
+
value: limited.text,
|
|
1597
|
+
...textTruncationFields(limited)
|
|
1598
|
+
};
|
|
1214
1599
|
return type === void 0 ? base : { ...base, type };
|
|
1215
1600
|
};
|
|
1216
1601
|
if (type === "string" && typeof inner.value === "string") {
|
|
@@ -1227,6 +1612,18 @@ function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_V
|
|
|
1227
1612
|
}
|
|
1228
1613
|
return buildCaptured("undefined");
|
|
1229
1614
|
}
|
|
1615
|
+
function sideEffectRefusalToCaptured(expression) {
|
|
1616
|
+
const error = new CfInspectorError(
|
|
1617
|
+
"MUTATION_NOT_ALLOWED",
|
|
1618
|
+
`V8 blocked the capture expression "${expression}" because it may have side effects. Pass --allow-mutation to run it explicitly.`
|
|
1619
|
+
);
|
|
1620
|
+
return {
|
|
1621
|
+
expression,
|
|
1622
|
+
error: `${error.code}: ${error.message}`,
|
|
1623
|
+
mutationRisk: true,
|
|
1624
|
+
blocked: true
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1230
1627
|
function readEvalError(result, maxValueLength) {
|
|
1231
1628
|
const text = typeof result.exceptionDetails?.exception?.description === "string" ? result.exceptionDetails.exception.description : typeof result.exceptionDetails?.text === "string" ? result.exceptionDetails.text : "evaluation failed";
|
|
1232
1629
|
return limitValueLength(text, maxValueLength);
|
|
@@ -1284,56 +1681,88 @@ async function captureProperties(session, objectId, limit, depth, maxValueLength
|
|
|
1284
1681
|
return await captureProperty(session, prop, depth, maxValueLength);
|
|
1285
1682
|
})
|
|
1286
1683
|
);
|
|
1287
|
-
|
|
1684
|
+
const omittedCount = Math.max(properties.length - limited.length, 0);
|
|
1685
|
+
return omittedCount === 0 ? { variables } : { variables, omittedCount };
|
|
1288
1686
|
}
|
|
1289
1687
|
async function captureProperty(session, prop, depth, maxValueLength) {
|
|
1290
1688
|
const name = typeof prop.name === "string" ? prop.name : "?";
|
|
1291
1689
|
const described = describeProperty(prop);
|
|
1292
|
-
const
|
|
1293
|
-
|
|
1294
|
-
|
|
1690
|
+
const capturedChildren = await capturePropertyChildren(
|
|
1691
|
+
session,
|
|
1692
|
+
described,
|
|
1693
|
+
depth,
|
|
1694
|
+
maxValueLength
|
|
1695
|
+
);
|
|
1696
|
+
const limited = limitValueLength(described.value, maxValueLength);
|
|
1697
|
+
const base = {
|
|
1698
|
+
name,
|
|
1699
|
+
value: limited.text,
|
|
1700
|
+
...textTruncationFields(limited)
|
|
1701
|
+
};
|
|
1295
1702
|
const withType = described.type === void 0 ? base : { ...base, type: described.type };
|
|
1296
|
-
|
|
1703
|
+
const children = capturedChildren?.variables;
|
|
1704
|
+
const withChildren = children === void 0 || children.length === 0 ? withType : { ...withType, children };
|
|
1705
|
+
const omittedCount = capturedChildren?.omittedCount ?? 0;
|
|
1706
|
+
return omittedCount === 0 ? withChildren : { ...withChildren, truncated: true, omittedCount };
|
|
1297
1707
|
}
|
|
1298
1708
|
async function capturePropertyChildren(session, described, depth, maxValueLength) {
|
|
1299
|
-
if (
|
|
1709
|
+
if (described.objectId === void 0 || !isExpandable(described.type)) {
|
|
1300
1710
|
return void 0;
|
|
1301
1711
|
}
|
|
1712
|
+
if (depth <= 0) {
|
|
1713
|
+
return await countDepthOmissions(session, described.objectId);
|
|
1714
|
+
}
|
|
1302
1715
|
try {
|
|
1303
|
-
|
|
1716
|
+
return await captureProperties(
|
|
1304
1717
|
session,
|
|
1305
1718
|
described.objectId,
|
|
1306
1719
|
MAX_CHILD_VARIABLES,
|
|
1307
1720
|
depth - 1,
|
|
1308
1721
|
maxValueLength
|
|
1309
1722
|
);
|
|
1310
|
-
return nested.length > 0 ? nested : void 0;
|
|
1311
1723
|
} catch {
|
|
1312
1724
|
return void 0;
|
|
1313
1725
|
}
|
|
1314
1726
|
}
|
|
1727
|
+
async function countDepthOmissions(session, objectId) {
|
|
1728
|
+
try {
|
|
1729
|
+
const properties = await getProperties(session, objectId);
|
|
1730
|
+
return properties.length === 0 ? void 0 : { variables: [], omittedCount: properties.length };
|
|
1731
|
+
} catch {
|
|
1732
|
+
return void 0;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
function countPropertyOmissions(captured) {
|
|
1736
|
+
return (captured.omittedCount ?? 0) + captured.variables.reduce((total, variable) => {
|
|
1737
|
+
const childOmissions = variable.children === void 0 ? 0 : countPropertyOmissions({ variables: variable.children });
|
|
1738
|
+
return total + (variable.omittedCount ?? 0) + childOmissions;
|
|
1739
|
+
}, 0);
|
|
1740
|
+
}
|
|
1315
1741
|
|
|
1316
1742
|
// src/snapshot/exception.ts
|
|
1317
1743
|
function asString2(value) {
|
|
1318
1744
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1319
1745
|
}
|
|
1320
|
-
async function materializeObject(session, objectId
|
|
1746
|
+
async function materializeObject(session, objectId) {
|
|
1321
1747
|
try {
|
|
1322
|
-
const
|
|
1748
|
+
const captured = await captureProperties(
|
|
1323
1749
|
session,
|
|
1324
1750
|
objectId,
|
|
1325
1751
|
MAX_SCOPE_VARIABLES,
|
|
1326
1752
|
MAX_VARIABLE_DEPTH,
|
|
1327
|
-
|
|
1753
|
+
Number.MAX_SAFE_INTEGER
|
|
1328
1754
|
);
|
|
1329
|
-
if (
|
|
1755
|
+
if (captured.variables.length === 0) {
|
|
1330
1756
|
return void 0;
|
|
1331
1757
|
}
|
|
1332
1758
|
const structured = {};
|
|
1333
|
-
for (const variable of
|
|
1759
|
+
for (const variable of captured.variables) {
|
|
1334
1760
|
structured[variable.name] = toStructuredValue(variable);
|
|
1335
1761
|
}
|
|
1336
|
-
return
|
|
1762
|
+
return {
|
|
1763
|
+
value: JSON.stringify(structured),
|
|
1764
|
+
omittedCount: countPropertyOmissions(captured)
|
|
1765
|
+
};
|
|
1337
1766
|
} catch {
|
|
1338
1767
|
return void 0;
|
|
1339
1768
|
}
|
|
@@ -1386,18 +1815,44 @@ async function captureException(session, pause, maxValueLength) {
|
|
|
1386
1815
|
return { error: "exception data has no objectId or value" };
|
|
1387
1816
|
}
|
|
1388
1817
|
const message = await readPropertyDescription(session, objectId, "message");
|
|
1389
|
-
const rendered = await materializeObject(session, objectId
|
|
1818
|
+
const rendered = await materializeObject(session, objectId);
|
|
1390
1819
|
if (rendered !== void 0) {
|
|
1391
|
-
|
|
1392
|
-
|
|
1820
|
+
return buildResult(
|
|
1821
|
+
type,
|
|
1822
|
+
message ?? description,
|
|
1823
|
+
rendered.value,
|
|
1824
|
+
maxValueLength,
|
|
1825
|
+
rendered.omittedCount
|
|
1826
|
+
);
|
|
1393
1827
|
}
|
|
1394
1828
|
return buildResult(type, description, description ?? "[exception]", maxValueLength);
|
|
1395
1829
|
}
|
|
1396
|
-
function buildResult(type, description, value, maxValueLength) {
|
|
1397
|
-
const
|
|
1398
|
-
const
|
|
1830
|
+
function buildResult(type, description, value, maxValueLength, omittedCount = 0) {
|
|
1831
|
+
const limitedValue = limitValueLength(value, maxValueLength);
|
|
1832
|
+
const limitedDescription = description === void 0 ? void 0 : limitValueLength(description, maxValueLength);
|
|
1833
|
+
const base = {
|
|
1834
|
+
value: limitedValue.text,
|
|
1835
|
+
...exceptionTruncationFields(limitedValue, limitedDescription)
|
|
1836
|
+
};
|
|
1399
1837
|
const withType = type === void 0 ? base : { ...base, type };
|
|
1400
|
-
|
|
1838
|
+
const withDescription = limitedDescription === void 0 ? withType : { ...withType, description: limitedDescription.text };
|
|
1839
|
+
return omittedCount === 0 ? withDescription : { ...withDescription, truncated: true, omittedCount };
|
|
1840
|
+
}
|
|
1841
|
+
function exceptionTruncationFields(value, description) {
|
|
1842
|
+
const valueLength = value.truncated ? value.originalLength : void 0;
|
|
1843
|
+
const descriptionLength = description?.truncated === true ? description.originalLength : void 0;
|
|
1844
|
+
const lengths = [valueLength, descriptionLength].filter(
|
|
1845
|
+
(length) => length !== void 0
|
|
1846
|
+
);
|
|
1847
|
+
if (lengths.length === 0) {
|
|
1848
|
+
return {};
|
|
1849
|
+
}
|
|
1850
|
+
return {
|
|
1851
|
+
truncated: true,
|
|
1852
|
+
originalLength: Math.max(...lengths),
|
|
1853
|
+
...valueLength === void 0 ? {} : { valueOriginalLength: valueLength },
|
|
1854
|
+
...descriptionLength === void 0 ? {} : { descriptionOriginalLength: descriptionLength }
|
|
1855
|
+
};
|
|
1401
1856
|
}
|
|
1402
1857
|
|
|
1403
1858
|
// src/snapshot/objects.ts
|
|
@@ -1412,20 +1867,23 @@ function objectIdFromEvalResult(result) {
|
|
|
1412
1867
|
}
|
|
1413
1868
|
return objectId;
|
|
1414
1869
|
}
|
|
1415
|
-
async function renderObjectCapture(session, objectId
|
|
1870
|
+
async function renderObjectCapture(session, objectId) {
|
|
1416
1871
|
try {
|
|
1417
|
-
const
|
|
1872
|
+
const captured = await captureProperties(
|
|
1418
1873
|
session,
|
|
1419
1874
|
objectId,
|
|
1420
1875
|
MAX_SCOPE_VARIABLES,
|
|
1421
1876
|
MAX_VARIABLE_DEPTH,
|
|
1422
|
-
|
|
1877
|
+
Number.MAX_SAFE_INTEGER
|
|
1423
1878
|
);
|
|
1424
1879
|
const structured = {};
|
|
1425
|
-
for (const variable of
|
|
1880
|
+
for (const variable of captured.variables) {
|
|
1426
1881
|
structured[variable.name] = toStructuredValue(variable);
|
|
1427
1882
|
}
|
|
1428
|
-
return
|
|
1883
|
+
return {
|
|
1884
|
+
value: JSON.stringify(structured),
|
|
1885
|
+
omittedCount: countPropertyOmissions(captured)
|
|
1886
|
+
};
|
|
1429
1887
|
} catch {
|
|
1430
1888
|
return void 0;
|
|
1431
1889
|
}
|
|
@@ -1447,16 +1905,22 @@ async function withSerializedObjectCapture(session, expression, evalResult, capt
|
|
|
1447
1905
|
if (objectId === void 0) {
|
|
1448
1906
|
return captured;
|
|
1449
1907
|
}
|
|
1450
|
-
const rendered = await renderObjectCapture(session, objectId
|
|
1908
|
+
const rendered = await renderObjectCapture(session, objectId);
|
|
1451
1909
|
if (rendered === void 0) {
|
|
1452
1910
|
return captured;
|
|
1453
1911
|
}
|
|
1454
|
-
const normalized = normalizeRenderedObjectCapture(rendered, captured.value);
|
|
1912
|
+
const normalized = normalizeRenderedObjectCapture(rendered.value, captured.value);
|
|
1455
1913
|
if (normalized === void 0) {
|
|
1456
1914
|
return captured;
|
|
1457
1915
|
}
|
|
1458
|
-
const
|
|
1459
|
-
|
|
1916
|
+
const limited = limitValueLength(normalized, maxValueLength);
|
|
1917
|
+
const base = {
|
|
1918
|
+
expression,
|
|
1919
|
+
value: limited.text,
|
|
1920
|
+
...textTruncationFields(limited),
|
|
1921
|
+
...captured.type === void 0 ? {} : { type: captured.type }
|
|
1922
|
+
};
|
|
1923
|
+
return rendered.omittedCount === 0 ? base : { ...base, truncated: true, omittedCount: rendered.omittedCount };
|
|
1460
1924
|
}
|
|
1461
1925
|
|
|
1462
1926
|
// src/snapshot/scopes.ts
|
|
@@ -1471,35 +1935,39 @@ var PRIORITY_BY_TYPE = {
|
|
|
1471
1935
|
module: 6,
|
|
1472
1936
|
script: 7
|
|
1473
1937
|
};
|
|
1474
|
-
function
|
|
1938
|
+
function rankedScopes(scopeChain) {
|
|
1475
1939
|
const eligible = scopeChain.filter((scope) => scope.objectId !== void 0 && scope.type !== "global");
|
|
1476
|
-
return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type))
|
|
1940
|
+
return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type));
|
|
1477
1941
|
}
|
|
1478
1942
|
function priorityOf(type) {
|
|
1479
1943
|
return PRIORITY_BY_TYPE[type] ?? Number.MAX_SAFE_INTEGER;
|
|
1480
1944
|
}
|
|
1481
1945
|
async function captureScopes(session, frame, maxValueLength) {
|
|
1482
|
-
const
|
|
1483
|
-
|
|
1946
|
+
const ranked = rankedScopes(frame.scopeChain);
|
|
1947
|
+
const scopes = ranked.slice(0, MAX_SCOPES);
|
|
1948
|
+
const capturedScopes = await Promise.all(
|
|
1484
1949
|
scopes.map(async (scope) => {
|
|
1485
1950
|
const objectId = scope.objectId;
|
|
1486
1951
|
if (objectId === void 0) {
|
|
1487
1952
|
return { type: scope.type, variables: [] };
|
|
1488
1953
|
}
|
|
1489
1954
|
try {
|
|
1490
|
-
const
|
|
1955
|
+
const captured = await captureProperties(
|
|
1491
1956
|
session,
|
|
1492
1957
|
objectId,
|
|
1493
1958
|
MAX_SCOPE_VARIABLES,
|
|
1494
1959
|
MAX_VARIABLE_DEPTH,
|
|
1495
1960
|
maxValueLength
|
|
1496
1961
|
);
|
|
1497
|
-
|
|
1962
|
+
const base = { type: scope.type, variables: captured.variables };
|
|
1963
|
+
return captured.omittedCount === void 0 ? base : { ...base, truncated: true, omittedCount: captured.omittedCount };
|
|
1498
1964
|
} catch {
|
|
1499
1965
|
return { type: scope.type, variables: [] };
|
|
1500
1966
|
}
|
|
1501
1967
|
})
|
|
1502
1968
|
);
|
|
1969
|
+
const omittedCount = Math.max(ranked.length - capturedScopes.length, 0);
|
|
1970
|
+
return omittedCount === 0 ? { scopes: capturedScopes } : { scopes: capturedScopes, omittedCount };
|
|
1503
1971
|
}
|
|
1504
1972
|
|
|
1505
1973
|
// src/snapshot/stack.ts
|
|
@@ -1519,23 +1987,48 @@ function buildBaseFrame(frame) {
|
|
|
1519
1987
|
};
|
|
1520
1988
|
return frame.url === void 0 ? base : { ...base, url: frame.url };
|
|
1521
1989
|
}
|
|
1522
|
-
async function captureFrameExpression(session, callFrameId, expression, maxValueLength) {
|
|
1990
|
+
async function captureFrameExpression(session, callFrameId, expression, maxValueLength, throwOnSideEffect) {
|
|
1991
|
+
const mutationRisk = throwOnSideEffect === false && looksLikeMutation(expression);
|
|
1523
1992
|
try {
|
|
1524
|
-
const result = await evaluateOnFrame(session, callFrameId, expression
|
|
1993
|
+
const result = await evaluateOnFrame(session, callFrameId, expression, {
|
|
1994
|
+
...throwOnSideEffect === void 0 ? {} : { throwOnSideEffect }
|
|
1995
|
+
});
|
|
1996
|
+
if (isSideEffectRefusal(result)) {
|
|
1997
|
+
return sideEffectRefusalToCaptured(expression);
|
|
1998
|
+
}
|
|
1525
1999
|
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1526
|
-
|
|
2000
|
+
const serialized = await withSerializedObjectCapture(
|
|
2001
|
+
session,
|
|
2002
|
+
expression,
|
|
2003
|
+
result,
|
|
2004
|
+
captured,
|
|
2005
|
+
maxValueLength
|
|
2006
|
+
);
|
|
2007
|
+
return mutationRisk ? { ...serialized, mutationRisk: true } : serialized;
|
|
1527
2008
|
} catch (err) {
|
|
1528
2009
|
const message = err instanceof Error ? err.message : String(err);
|
|
1529
|
-
|
|
2010
|
+
const limited = limitValueLength(message, maxValueLength);
|
|
2011
|
+
const captured = {
|
|
2012
|
+
expression,
|
|
2013
|
+
error: limited.text,
|
|
2014
|
+
...textTruncationFields(limited)
|
|
2015
|
+
};
|
|
2016
|
+
return mutationRisk ? { ...captured, mutationRisk: true } : captured;
|
|
1530
2017
|
}
|
|
1531
2018
|
}
|
|
1532
|
-
async function captureFrameExpressions(session, frame, expressions, maxValueLength) {
|
|
2019
|
+
async function captureFrameExpressions(session, frame, expressions, maxValueLength, throwOnSideEffect) {
|
|
1533
2020
|
if (expressions.length === 0) {
|
|
1534
2021
|
return [];
|
|
1535
2022
|
}
|
|
1536
2023
|
return await Promise.all(
|
|
1537
2024
|
expressions.map(
|
|
1538
|
-
(expression) => captureFrameExpression(
|
|
2025
|
+
(expression) => captureFrameExpression(
|
|
2026
|
+
session,
|
|
2027
|
+
frame.callFrameId,
|
|
2028
|
+
expression,
|
|
2029
|
+
maxValueLength,
|
|
2030
|
+
throwOnSideEffect
|
|
2031
|
+
)
|
|
1539
2032
|
)
|
|
1540
2033
|
);
|
|
1541
2034
|
}
|
|
@@ -1555,7 +2048,8 @@ async function walkStack(session, callFrames, options) {
|
|
|
1555
2048
|
session,
|
|
1556
2049
|
frame,
|
|
1557
2050
|
options.stackCaptures,
|
|
1558
|
-
options.maxValueLength
|
|
2051
|
+
options.maxValueLength,
|
|
2052
|
+
options.throwOnSideEffect
|
|
1559
2053
|
);
|
|
1560
2054
|
return { ...base, captures };
|
|
1561
2055
|
})
|
|
@@ -1577,14 +2071,25 @@ async function captureSnapshot(session, pause, options = {}) {
|
|
|
1577
2071
|
column: top.columnNumber + 1
|
|
1578
2072
|
};
|
|
1579
2073
|
if (options.includeScopes === true) {
|
|
1580
|
-
const
|
|
1581
|
-
topFrame = {
|
|
2074
|
+
const capturedScopes = await captureScopes(session, top, maxValueLength);
|
|
2075
|
+
topFrame = {
|
|
2076
|
+
...topFrame,
|
|
2077
|
+
scopes: capturedScopes.scopes,
|
|
2078
|
+
...capturedScopes.omittedCount === void 0 ? {} : { truncated: true, omittedCount: capturedScopes.omittedCount }
|
|
2079
|
+
};
|
|
1582
2080
|
}
|
|
1583
|
-
captures = await captureExpressions(
|
|
2081
|
+
captures = await captureExpressions(
|
|
2082
|
+
session,
|
|
2083
|
+
top.callFrameId,
|
|
2084
|
+
options.captures,
|
|
2085
|
+
maxValueLength,
|
|
2086
|
+
options.throwOnSideEffect
|
|
2087
|
+
);
|
|
1584
2088
|
stack = await walkStack(session, pause.callFrames, {
|
|
1585
2089
|
stackDepth: options.stackDepth ?? DEFAULT_STACK_DEPTH,
|
|
1586
2090
|
stackCaptures: options.stackCaptures ?? [],
|
|
1587
|
-
maxValueLength
|
|
2091
|
+
maxValueLength,
|
|
2092
|
+
...options.throwOnSideEffect === void 0 ? {} : { throwOnSideEffect: options.throwOnSideEffect }
|
|
1588
2093
|
});
|
|
1589
2094
|
}
|
|
1590
2095
|
const exception = await captureException(session, pause, maxValueLength);
|
|
@@ -1607,24 +2112,49 @@ function buildResult2(input) {
|
|
|
1607
2112
|
const withStack = input.stack.length > 0 ? { ...withFrame, stack: input.stack } : withFrame;
|
|
1608
2113
|
return input.exception === void 0 ? withStack : { ...withStack, exception: input.exception };
|
|
1609
2114
|
}
|
|
1610
|
-
async function captureExpressions(session, callFrameId, captures, maxValueLength) {
|
|
2115
|
+
async function captureExpressions(session, callFrameId, captures, maxValueLength, throwOnSideEffect) {
|
|
1611
2116
|
if (captures === void 0 || captures.length === 0) {
|
|
1612
2117
|
return [];
|
|
1613
2118
|
}
|
|
1614
2119
|
return await Promise.all(
|
|
1615
2120
|
captures.map(async (expression) => {
|
|
1616
|
-
return await captureExpression(
|
|
2121
|
+
return await captureExpression(
|
|
2122
|
+
session,
|
|
2123
|
+
callFrameId,
|
|
2124
|
+
expression,
|
|
2125
|
+
maxValueLength,
|
|
2126
|
+
throwOnSideEffect
|
|
2127
|
+
);
|
|
1617
2128
|
})
|
|
1618
2129
|
);
|
|
1619
2130
|
}
|
|
1620
|
-
async function captureExpression(session, callFrameId, expression, maxValueLength) {
|
|
2131
|
+
async function captureExpression(session, callFrameId, expression, maxValueLength, throwOnSideEffect) {
|
|
2132
|
+
const mutationRisk = throwOnSideEffect === false && looksLikeMutation(expression);
|
|
1621
2133
|
try {
|
|
1622
|
-
const result = await evaluateOnFrame(session, callFrameId, expression
|
|
2134
|
+
const result = await evaluateOnFrame(session, callFrameId, expression, {
|
|
2135
|
+
...throwOnSideEffect === void 0 ? {} : { throwOnSideEffect }
|
|
2136
|
+
});
|
|
2137
|
+
if (isSideEffectRefusal(result)) {
|
|
2138
|
+
return sideEffectRefusalToCaptured(expression);
|
|
2139
|
+
}
|
|
1623
2140
|
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1624
|
-
|
|
2141
|
+
const serialized = await withSerializedObjectCapture(
|
|
2142
|
+
session,
|
|
2143
|
+
expression,
|
|
2144
|
+
result,
|
|
2145
|
+
captured,
|
|
2146
|
+
maxValueLength
|
|
2147
|
+
);
|
|
2148
|
+
return mutationRisk ? { ...serialized, mutationRisk: true } : serialized;
|
|
1625
2149
|
} catch (err) {
|
|
1626
2150
|
const message = err instanceof Error ? err.message : String(err);
|
|
1627
|
-
|
|
2151
|
+
const limited = limitValueLength(message, maxValueLength);
|
|
2152
|
+
const captured = {
|
|
2153
|
+
expression,
|
|
2154
|
+
error: limited.text,
|
|
2155
|
+
...textTruncationFields(limited)
|
|
2156
|
+
};
|
|
2157
|
+
return mutationRisk ? { ...captured, mutationRisk: true } : captured;
|
|
1628
2158
|
}
|
|
1629
2159
|
}
|
|
1630
2160
|
|
|
@@ -1712,7 +2242,7 @@ function readArg(arg, index) {
|
|
|
1712
2242
|
}
|
|
1713
2243
|
return index === 0 ? void 0 : "";
|
|
1714
2244
|
}
|
|
1715
|
-
function parseLogEvent(rawArgs, sentinel, location, timestamp) {
|
|
2245
|
+
function parseLogEvent(rawArgs, sentinel, location, timestamp, maxValueLength = DEFAULT_STREAM_MAX_VALUE_LENGTH) {
|
|
1716
2246
|
if (!Array.isArray(rawArgs) || rawArgs.length < 2) {
|
|
1717
2247
|
return void 0;
|
|
1718
2248
|
}
|
|
@@ -1724,21 +2254,37 @@ function parseLogEvent(rawArgs, sentinel, location, timestamp) {
|
|
|
1724
2254
|
const ts = new Date(typeof timestamp === "number" ? timestamp : Date.now()).toISOString();
|
|
1725
2255
|
const at = `${location.file}:${location.line.toString()}`;
|
|
1726
2256
|
if (payload.startsWith("!err:")) {
|
|
1727
|
-
|
|
2257
|
+
const limited = limitValueLength(payload.slice("!err:".length), maxValueLength);
|
|
2258
|
+
return {
|
|
2259
|
+
ts,
|
|
2260
|
+
at,
|
|
2261
|
+
error: limited.text,
|
|
2262
|
+
...textTruncationFields(limited)
|
|
2263
|
+
};
|
|
1728
2264
|
}
|
|
1729
|
-
return parsePayload(ts, at, payload);
|
|
2265
|
+
return parsePayload(ts, at, payload, maxValueLength);
|
|
1730
2266
|
}
|
|
1731
|
-
function parsePayload(ts, at, payload) {
|
|
2267
|
+
function parsePayload(ts, at, payload, maxValueLength) {
|
|
1732
2268
|
try {
|
|
1733
2269
|
const parsed = JSON.parse(payload);
|
|
1734
2270
|
if (typeof parsed === "string") {
|
|
1735
|
-
return
|
|
2271
|
+
return buildValueEvent(ts, at, parsed, maxValueLength);
|
|
1736
2272
|
}
|
|
1737
|
-
return
|
|
2273
|
+
return buildValueEvent(ts, at, JSON.stringify(parsed), maxValueLength);
|
|
1738
2274
|
} catch {
|
|
1739
|
-
return
|
|
2275
|
+
return buildValueEvent(ts, at, payload, maxValueLength, true);
|
|
1740
2276
|
}
|
|
1741
2277
|
}
|
|
2278
|
+
function buildValueEvent(ts, at, value, maxValueLength, includeRaw = false) {
|
|
2279
|
+
const limited = limitValueLength(value, maxValueLength);
|
|
2280
|
+
return {
|
|
2281
|
+
ts,
|
|
2282
|
+
at,
|
|
2283
|
+
value: limited.text,
|
|
2284
|
+
...includeRaw ? { raw: limited.text } : {},
|
|
2285
|
+
...textTruncationFields(limited)
|
|
2286
|
+
};
|
|
2287
|
+
}
|
|
1742
2288
|
|
|
1743
2289
|
// src/logpoint/stream.ts
|
|
1744
2290
|
function validateMaxEvents(maxEvents) {
|
|
@@ -1768,6 +2314,9 @@ function validateHitCount2(hitCount) {
|
|
|
1768
2314
|
async function streamLogpoint(session, options) {
|
|
1769
2315
|
const maxEvents = validateMaxEvents(options.maxEvents);
|
|
1770
2316
|
const hitCount = validateHitCount2(options.hitCount);
|
|
2317
|
+
const maxValueLength = resolveMaxValueLength(
|
|
2318
|
+
options.maxValueLength ?? DEFAULT_STREAM_MAX_VALUE_LENGTH
|
|
2319
|
+
);
|
|
1771
2320
|
const sentinel = generateSentinel();
|
|
1772
2321
|
const condition = buildLogpointCondition(sentinel, options.expression, {
|
|
1773
2322
|
...options.condition === void 0 ? {} : { predicate: options.condition },
|
|
@@ -1780,7 +2329,7 @@ async function streamLogpoint(session, options) {
|
|
|
1780
2329
|
if (maxEventsReached) {
|
|
1781
2330
|
return;
|
|
1782
2331
|
}
|
|
1783
|
-
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
2332
|
+
const event = toLogpointEvent(raw, sentinel, options.location, maxValueLength);
|
|
1784
2333
|
if (event === void 0) {
|
|
1785
2334
|
return;
|
|
1786
2335
|
}
|
|
@@ -1820,13 +2369,13 @@ async function streamLogpoint(session, options) {
|
|
|
1820
2369
|
await removeBreakpointBestEffort(session, handle.breakpointId);
|
|
1821
2370
|
}
|
|
1822
2371
|
}
|
|
1823
|
-
function toLogpointEvent(raw, sentinel, location) {
|
|
2372
|
+
function toLogpointEvent(raw, sentinel, location, maxValueLength) {
|
|
1824
2373
|
const params = raw;
|
|
1825
2374
|
if (asString3(params.type) !== "log") {
|
|
1826
2375
|
return void 0;
|
|
1827
2376
|
}
|
|
1828
2377
|
const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
|
|
1829
|
-
return parseLogEvent(params.args, sentinel, location, ts);
|
|
2378
|
+
return parseLogEvent(params.args, sentinel, location, ts, maxValueLength);
|
|
1830
2379
|
}
|
|
1831
2380
|
async function removeBreakpointBestEffort(session, breakpointId) {
|
|
1832
2381
|
try {
|