@saptools/cf-inspector 0.4.12 → 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 +166 -32
- package/dist/cli.js +1124 -380
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +44 -3
- package/dist/index.js +637 -111
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -474,37 +474,65 @@ 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
|
-
|
|
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`);
|
|
508
536
|
}
|
|
509
537
|
function isNodeSystemError(err) {
|
|
510
538
|
return err instanceof Error;
|
|
@@ -540,7 +568,10 @@ function parseJsonResponse(chunks) {
|
|
|
540
568
|
}
|
|
541
569
|
function parseDiscoveryError(url, err) {
|
|
542
570
|
const message = err instanceof Error ? err.message : String(err);
|
|
543
|
-
return
|
|
571
|
+
return new InvalidDiscoveryPayloadError(
|
|
572
|
+
"INSPECTOR_DISCOVERY_FAILED",
|
|
573
|
+
`Failed to parse inspector discovery response from ${url}: ${message}`
|
|
574
|
+
);
|
|
544
575
|
}
|
|
545
576
|
function newDiscoveryError(message) {
|
|
546
577
|
return new CfInspectorError("INSPECTOR_DISCOVERY_FAILED", message);
|
|
@@ -614,7 +645,7 @@ async function fetchInspectorVersion(host, port, timeoutMs) {
|
|
|
614
645
|
|
|
615
646
|
// src/inspector/pause.ts
|
|
616
647
|
init_types();
|
|
617
|
-
import { performance } from "perf_hooks";
|
|
648
|
+
import { performance as performance2 } from "perf_hooks";
|
|
618
649
|
function pauseMatches(pause, breakpointIds, pauseReasons) {
|
|
619
650
|
if (pauseReasons !== void 0 && pauseReasons.length > 0) {
|
|
620
651
|
return pauseReasons.includes(pause.reason);
|
|
@@ -625,7 +656,7 @@ function pauseMatches(pause, breakpointIds, pauseReasons) {
|
|
|
625
656
|
return pause.hitBreakpoints.some((id) => breakpointIds.includes(id));
|
|
626
657
|
}
|
|
627
658
|
function remainingUntil(deadlineMs) {
|
|
628
|
-
return Math.max(0, deadlineMs -
|
|
659
|
+
return Math.max(0, deadlineMs - performance2.now());
|
|
629
660
|
}
|
|
630
661
|
function hasResumedSincePause(session, pause) {
|
|
631
662
|
const pauseAt = pause.receivedAtMs;
|
|
@@ -655,7 +686,7 @@ async function waitForUnmatchedPauseToResume(session, pause, deadlineMs, timeout
|
|
|
655
686
|
}
|
|
656
687
|
try {
|
|
657
688
|
await session.client.waitFor("Debugger.resumed", { timeoutMs: remainingMs });
|
|
658
|
-
session.debuggerState.lastResumedAtMs =
|
|
689
|
+
session.debuggerState.lastResumedAtMs = performance2.now();
|
|
659
690
|
} catch (err) {
|
|
660
691
|
if (err instanceof CfInspectorError && err.code === "BREAKPOINT_NOT_HIT") {
|
|
661
692
|
throwUnrelatedPauseTimeout(pause, timeoutMs);
|
|
@@ -678,7 +709,7 @@ async function handleUnmatchedPause(session, pause, options, deadlineMs) {
|
|
|
678
709
|
await waitForUnmatchedPauseToResume(session, pause, deadlineMs, options.timeoutMs);
|
|
679
710
|
}
|
|
680
711
|
async function waitForPause(session, options) {
|
|
681
|
-
const deadlineMs =
|
|
712
|
+
const deadlineMs = performance2.now() + options.timeoutMs;
|
|
682
713
|
const buffer = session.pauseBuffer;
|
|
683
714
|
while (buffer.length > 0 || remainingUntil(deadlineMs) > 0) {
|
|
684
715
|
while (buffer.length > 0) {
|
|
@@ -711,14 +742,14 @@ async function waitForLivePause(session, options, deadlineMs) {
|
|
|
711
742
|
params = await session.client.waitFor("Debugger.paused", {
|
|
712
743
|
timeoutMs: remainingMs,
|
|
713
744
|
predicate: () => {
|
|
714
|
-
receivedAtMs =
|
|
745
|
+
receivedAtMs = performance2.now();
|
|
715
746
|
return true;
|
|
716
747
|
}
|
|
717
748
|
});
|
|
718
749
|
} finally {
|
|
719
750
|
session.pauseWaitGate.active = false;
|
|
720
751
|
}
|
|
721
|
-
return toPauseEvent(params, receivedAtMs ??
|
|
752
|
+
return toPauseEvent(params, receivedAtMs ?? performance2.now(), session.scripts);
|
|
722
753
|
}
|
|
723
754
|
|
|
724
755
|
// src/inspector/runtime.ts
|
|
@@ -729,15 +760,30 @@ async function resume(session) {
|
|
|
729
760
|
async function setPauseOnExceptions(session, state) {
|
|
730
761
|
await session.client.send("Debugger.setPauseOnExceptions", { state });
|
|
731
762
|
}
|
|
732
|
-
async function evaluateOnFrame(session, callFrameId, expression) {
|
|
763
|
+
async function evaluateOnFrame(session, callFrameId, expression, options = {}) {
|
|
733
764
|
return await session.client.send("Debugger.evaluateOnCallFrame", {
|
|
734
765
|
callFrameId,
|
|
735
766
|
expression,
|
|
736
767
|
returnByValue: false,
|
|
737
768
|
generatePreview: true,
|
|
738
|
-
silent: true
|
|
769
|
+
silent: true,
|
|
770
|
+
...options.throwOnSideEffect === void 0 ? {} : { throwOnSideEffect: options.throwOnSideEffect }
|
|
739
771
|
});
|
|
740
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
|
+
}
|
|
741
787
|
async function evaluateGlobal(session, expression) {
|
|
742
788
|
return await session.client.send("Runtime.evaluate", {
|
|
743
789
|
expression,
|
|
@@ -789,7 +835,7 @@ async function getProperties(session, objectId) {
|
|
|
789
835
|
}
|
|
790
836
|
|
|
791
837
|
// src/inspector/session.ts
|
|
792
|
-
import { performance as
|
|
838
|
+
import { performance as performance3 } from "perf_hooks";
|
|
793
839
|
|
|
794
840
|
// src/cdp/client.ts
|
|
795
841
|
init_types();
|
|
@@ -1031,12 +1077,203 @@ var CdpClient = class _CdpClient {
|
|
|
1031
1077
|
this.emitter.removeAllListeners();
|
|
1032
1078
|
}
|
|
1033
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
|
+
}
|
|
1034
1176
|
|
|
1035
1177
|
// src/inspector/session.ts
|
|
1036
1178
|
init_types();
|
|
1037
1179
|
var DEFAULT_CONNECT_TIMEOUT_MS = 5e3;
|
|
1038
1180
|
var DEFAULT_HOST = "127.0.0.1";
|
|
1039
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
|
+
}
|
|
1040
1277
|
async function connectInspector(options) {
|
|
1041
1278
|
const host = options.host ?? DEFAULT_HOST;
|
|
1042
1279
|
const connectTimeoutMs = options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS;
|
|
@@ -1053,13 +1290,70 @@ async function connectInspector(options) {
|
|
|
1053
1290
|
url: target.webSocketDebuggerUrl,
|
|
1054
1291
|
connectTimeoutMs
|
|
1055
1292
|
});
|
|
1293
|
+
let workerDiscovery;
|
|
1056
1294
|
try {
|
|
1057
|
-
|
|
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
|
+
);
|
|
1058
1307
|
} catch (err) {
|
|
1308
|
+
await workerDiscovery?.dispose();
|
|
1059
1309
|
client.dispose();
|
|
1060
1310
|
throw err;
|
|
1061
1311
|
}
|
|
1062
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
|
+
}
|
|
1063
1357
|
async function initSession(client, target) {
|
|
1064
1358
|
const scripts = /* @__PURE__ */ new Map();
|
|
1065
1359
|
client.on("Debugger.scriptParsed", (raw) => {
|
|
@@ -1079,14 +1373,14 @@ async function initSession(client, target) {
|
|
|
1079
1373
|
return;
|
|
1080
1374
|
}
|
|
1081
1375
|
const params = raw;
|
|
1082
|
-
const event = toPauseEvent(params,
|
|
1376
|
+
const event = toPauseEvent(params, performance3.now(), scripts);
|
|
1083
1377
|
if (pauseBuffer.length >= PAUSE_BUFFER_LIMIT) {
|
|
1084
1378
|
pauseBuffer.shift();
|
|
1085
1379
|
}
|
|
1086
1380
|
pauseBuffer.push(event);
|
|
1087
1381
|
});
|
|
1088
1382
|
client.on("Debugger.resumed", () => {
|
|
1089
|
-
debuggerState.lastResumedAtMs =
|
|
1383
|
+
debuggerState.lastResumedAtMs = performance3.now();
|
|
1090
1384
|
});
|
|
1091
1385
|
await client.send("Runtime.enable");
|
|
1092
1386
|
await client.send("Debugger.enable");
|
|
@@ -1107,9 +1401,61 @@ async function initSession(client, target) {
|
|
|
1107
1401
|
};
|
|
1108
1402
|
}
|
|
1109
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
|
+
|
|
1110
1455
|
// src/snapshot/values.ts
|
|
1111
1456
|
init_types();
|
|
1112
|
-
var DEFAULT_MAX_VALUE_LENGTH =
|
|
1457
|
+
var DEFAULT_MAX_VALUE_LENGTH = 131072;
|
|
1458
|
+
var DEFAULT_STREAM_MAX_VALUE_LENGTH = 4096;
|
|
1113
1459
|
function isPrimitive(value) {
|
|
1114
1460
|
const t = typeof value;
|
|
1115
1461
|
return t === "string" || t === "number" || t === "boolean" || t === "bigint" || t === "symbol";
|
|
@@ -1137,9 +1483,16 @@ function resolveMaxValueLength(value) {
|
|
|
1137
1483
|
}
|
|
1138
1484
|
function limitValueLength(raw, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
1139
1485
|
if (raw.length <= maxValueLength) {
|
|
1140
|
-
return raw;
|
|
1486
|
+
return { text: raw, truncated: false };
|
|
1141
1487
|
}
|
|
1142
|
-
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 } : {};
|
|
1143
1496
|
}
|
|
1144
1497
|
function parseQuotedString(value) {
|
|
1145
1498
|
try {
|
|
@@ -1224,7 +1577,12 @@ function toStructuredValue(variable) {
|
|
|
1224
1577
|
// src/snapshot/evaluation.ts
|
|
1225
1578
|
function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_VALUE_LENGTH) {
|
|
1226
1579
|
if (result.exceptionDetails !== void 0) {
|
|
1227
|
-
|
|
1580
|
+
const limited = readEvalError(result, maxValueLength);
|
|
1581
|
+
return {
|
|
1582
|
+
expression,
|
|
1583
|
+
error: limited.text,
|
|
1584
|
+
...textTruncationFields(limited)
|
|
1585
|
+
};
|
|
1228
1586
|
}
|
|
1229
1587
|
const inner = result.result;
|
|
1230
1588
|
if (!inner) {
|
|
@@ -1232,8 +1590,12 @@ function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_V
|
|
|
1232
1590
|
}
|
|
1233
1591
|
const type = typeof inner.type === "string" ? inner.type : void 0;
|
|
1234
1592
|
const buildCaptured = (rendered) => {
|
|
1235
|
-
const
|
|
1236
|
-
const base = {
|
|
1593
|
+
const limited = limitValueLength(rendered, maxValueLength);
|
|
1594
|
+
const base = {
|
|
1595
|
+
expression,
|
|
1596
|
+
value: limited.text,
|
|
1597
|
+
...textTruncationFields(limited)
|
|
1598
|
+
};
|
|
1237
1599
|
return type === void 0 ? base : { ...base, type };
|
|
1238
1600
|
};
|
|
1239
1601
|
if (type === "string" && typeof inner.value === "string") {
|
|
@@ -1250,6 +1612,18 @@ function evalResultToCaptured(expression, result, maxValueLength = DEFAULT_MAX_V
|
|
|
1250
1612
|
}
|
|
1251
1613
|
return buildCaptured("undefined");
|
|
1252
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
|
+
}
|
|
1253
1627
|
function readEvalError(result, maxValueLength) {
|
|
1254
1628
|
const text = typeof result.exceptionDetails?.exception?.description === "string" ? result.exceptionDetails.exception.description : typeof result.exceptionDetails?.text === "string" ? result.exceptionDetails.text : "evaluation failed";
|
|
1255
1629
|
return limitValueLength(text, maxValueLength);
|
|
@@ -1307,56 +1681,88 @@ async function captureProperties(session, objectId, limit, depth, maxValueLength
|
|
|
1307
1681
|
return await captureProperty(session, prop, depth, maxValueLength);
|
|
1308
1682
|
})
|
|
1309
1683
|
);
|
|
1310
|
-
|
|
1684
|
+
const omittedCount = Math.max(properties.length - limited.length, 0);
|
|
1685
|
+
return omittedCount === 0 ? { variables } : { variables, omittedCount };
|
|
1311
1686
|
}
|
|
1312
1687
|
async function captureProperty(session, prop, depth, maxValueLength) {
|
|
1313
1688
|
const name = typeof prop.name === "string" ? prop.name : "?";
|
|
1314
1689
|
const described = describeProperty(prop);
|
|
1315
|
-
const
|
|
1316
|
-
|
|
1317
|
-
|
|
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
|
+
};
|
|
1318
1702
|
const withType = described.type === void 0 ? base : { ...base, type: described.type };
|
|
1319
|
-
|
|
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 };
|
|
1320
1707
|
}
|
|
1321
1708
|
async function capturePropertyChildren(session, described, depth, maxValueLength) {
|
|
1322
|
-
if (
|
|
1709
|
+
if (described.objectId === void 0 || !isExpandable(described.type)) {
|
|
1323
1710
|
return void 0;
|
|
1324
1711
|
}
|
|
1712
|
+
if (depth <= 0) {
|
|
1713
|
+
return await countDepthOmissions(session, described.objectId);
|
|
1714
|
+
}
|
|
1325
1715
|
try {
|
|
1326
|
-
|
|
1716
|
+
return await captureProperties(
|
|
1327
1717
|
session,
|
|
1328
1718
|
described.objectId,
|
|
1329
1719
|
MAX_CHILD_VARIABLES,
|
|
1330
1720
|
depth - 1,
|
|
1331
1721
|
maxValueLength
|
|
1332
1722
|
);
|
|
1333
|
-
return nested.length > 0 ? nested : void 0;
|
|
1334
1723
|
} catch {
|
|
1335
1724
|
return void 0;
|
|
1336
1725
|
}
|
|
1337
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
|
+
}
|
|
1338
1741
|
|
|
1339
1742
|
// src/snapshot/exception.ts
|
|
1340
1743
|
function asString2(value) {
|
|
1341
1744
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
1342
1745
|
}
|
|
1343
|
-
async function materializeObject(session, objectId
|
|
1746
|
+
async function materializeObject(session, objectId) {
|
|
1344
1747
|
try {
|
|
1345
|
-
const
|
|
1748
|
+
const captured = await captureProperties(
|
|
1346
1749
|
session,
|
|
1347
1750
|
objectId,
|
|
1348
1751
|
MAX_SCOPE_VARIABLES,
|
|
1349
1752
|
MAX_VARIABLE_DEPTH,
|
|
1350
|
-
|
|
1753
|
+
Number.MAX_SAFE_INTEGER
|
|
1351
1754
|
);
|
|
1352
|
-
if (
|
|
1755
|
+
if (captured.variables.length === 0) {
|
|
1353
1756
|
return void 0;
|
|
1354
1757
|
}
|
|
1355
1758
|
const structured = {};
|
|
1356
|
-
for (const variable of
|
|
1759
|
+
for (const variable of captured.variables) {
|
|
1357
1760
|
structured[variable.name] = toStructuredValue(variable);
|
|
1358
1761
|
}
|
|
1359
|
-
return
|
|
1762
|
+
return {
|
|
1763
|
+
value: JSON.stringify(structured),
|
|
1764
|
+
omittedCount: countPropertyOmissions(captured)
|
|
1765
|
+
};
|
|
1360
1766
|
} catch {
|
|
1361
1767
|
return void 0;
|
|
1362
1768
|
}
|
|
@@ -1409,18 +1815,44 @@ async function captureException(session, pause, maxValueLength) {
|
|
|
1409
1815
|
return { error: "exception data has no objectId or value" };
|
|
1410
1816
|
}
|
|
1411
1817
|
const message = await readPropertyDescription(session, objectId, "message");
|
|
1412
|
-
const rendered = await materializeObject(session, objectId
|
|
1818
|
+
const rendered = await materializeObject(session, objectId);
|
|
1413
1819
|
if (rendered !== void 0) {
|
|
1414
|
-
|
|
1415
|
-
|
|
1820
|
+
return buildResult(
|
|
1821
|
+
type,
|
|
1822
|
+
message ?? description,
|
|
1823
|
+
rendered.value,
|
|
1824
|
+
maxValueLength,
|
|
1825
|
+
rendered.omittedCount
|
|
1826
|
+
);
|
|
1416
1827
|
}
|
|
1417
1828
|
return buildResult(type, description, description ?? "[exception]", maxValueLength);
|
|
1418
1829
|
}
|
|
1419
|
-
function buildResult(type, description, value, maxValueLength) {
|
|
1420
|
-
const
|
|
1421
|
-
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
|
+
};
|
|
1422
1837
|
const withType = type === void 0 ? base : { ...base, type };
|
|
1423
|
-
|
|
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
|
+
};
|
|
1424
1856
|
}
|
|
1425
1857
|
|
|
1426
1858
|
// src/snapshot/objects.ts
|
|
@@ -1435,20 +1867,23 @@ function objectIdFromEvalResult(result) {
|
|
|
1435
1867
|
}
|
|
1436
1868
|
return objectId;
|
|
1437
1869
|
}
|
|
1438
|
-
async function renderObjectCapture(session, objectId
|
|
1870
|
+
async function renderObjectCapture(session, objectId) {
|
|
1439
1871
|
try {
|
|
1440
|
-
const
|
|
1872
|
+
const captured = await captureProperties(
|
|
1441
1873
|
session,
|
|
1442
1874
|
objectId,
|
|
1443
1875
|
MAX_SCOPE_VARIABLES,
|
|
1444
1876
|
MAX_VARIABLE_DEPTH,
|
|
1445
|
-
|
|
1877
|
+
Number.MAX_SAFE_INTEGER
|
|
1446
1878
|
);
|
|
1447
1879
|
const structured = {};
|
|
1448
|
-
for (const variable of
|
|
1880
|
+
for (const variable of captured.variables) {
|
|
1449
1881
|
structured[variable.name] = toStructuredValue(variable);
|
|
1450
1882
|
}
|
|
1451
|
-
return
|
|
1883
|
+
return {
|
|
1884
|
+
value: JSON.stringify(structured),
|
|
1885
|
+
omittedCount: countPropertyOmissions(captured)
|
|
1886
|
+
};
|
|
1452
1887
|
} catch {
|
|
1453
1888
|
return void 0;
|
|
1454
1889
|
}
|
|
@@ -1470,16 +1905,22 @@ async function withSerializedObjectCapture(session, expression, evalResult, capt
|
|
|
1470
1905
|
if (objectId === void 0) {
|
|
1471
1906
|
return captured;
|
|
1472
1907
|
}
|
|
1473
|
-
const rendered = await renderObjectCapture(session, objectId
|
|
1908
|
+
const rendered = await renderObjectCapture(session, objectId);
|
|
1474
1909
|
if (rendered === void 0) {
|
|
1475
1910
|
return captured;
|
|
1476
1911
|
}
|
|
1477
|
-
const normalized = normalizeRenderedObjectCapture(rendered, captured.value);
|
|
1912
|
+
const normalized = normalizeRenderedObjectCapture(rendered.value, captured.value);
|
|
1478
1913
|
if (normalized === void 0) {
|
|
1479
1914
|
return captured;
|
|
1480
1915
|
}
|
|
1481
|
-
const
|
|
1482
|
-
|
|
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 };
|
|
1483
1924
|
}
|
|
1484
1925
|
|
|
1485
1926
|
// src/snapshot/scopes.ts
|
|
@@ -1494,35 +1935,39 @@ var PRIORITY_BY_TYPE = {
|
|
|
1494
1935
|
module: 6,
|
|
1495
1936
|
script: 7
|
|
1496
1937
|
};
|
|
1497
|
-
function
|
|
1938
|
+
function rankedScopes(scopeChain) {
|
|
1498
1939
|
const eligible = scopeChain.filter((scope) => scope.objectId !== void 0 && scope.type !== "global");
|
|
1499
|
-
return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type))
|
|
1940
|
+
return [...eligible].sort((a, b) => priorityOf(a.type) - priorityOf(b.type));
|
|
1500
1941
|
}
|
|
1501
1942
|
function priorityOf(type) {
|
|
1502
1943
|
return PRIORITY_BY_TYPE[type] ?? Number.MAX_SAFE_INTEGER;
|
|
1503
1944
|
}
|
|
1504
1945
|
async function captureScopes(session, frame, maxValueLength) {
|
|
1505
|
-
const
|
|
1506
|
-
|
|
1946
|
+
const ranked = rankedScopes(frame.scopeChain);
|
|
1947
|
+
const scopes = ranked.slice(0, MAX_SCOPES);
|
|
1948
|
+
const capturedScopes = await Promise.all(
|
|
1507
1949
|
scopes.map(async (scope) => {
|
|
1508
1950
|
const objectId = scope.objectId;
|
|
1509
1951
|
if (objectId === void 0) {
|
|
1510
1952
|
return { type: scope.type, variables: [] };
|
|
1511
1953
|
}
|
|
1512
1954
|
try {
|
|
1513
|
-
const
|
|
1955
|
+
const captured = await captureProperties(
|
|
1514
1956
|
session,
|
|
1515
1957
|
objectId,
|
|
1516
1958
|
MAX_SCOPE_VARIABLES,
|
|
1517
1959
|
MAX_VARIABLE_DEPTH,
|
|
1518
1960
|
maxValueLength
|
|
1519
1961
|
);
|
|
1520
|
-
|
|
1962
|
+
const base = { type: scope.type, variables: captured.variables };
|
|
1963
|
+
return captured.omittedCount === void 0 ? base : { ...base, truncated: true, omittedCount: captured.omittedCount };
|
|
1521
1964
|
} catch {
|
|
1522
1965
|
return { type: scope.type, variables: [] };
|
|
1523
1966
|
}
|
|
1524
1967
|
})
|
|
1525
1968
|
);
|
|
1969
|
+
const omittedCount = Math.max(ranked.length - capturedScopes.length, 0);
|
|
1970
|
+
return omittedCount === 0 ? { scopes: capturedScopes } : { scopes: capturedScopes, omittedCount };
|
|
1526
1971
|
}
|
|
1527
1972
|
|
|
1528
1973
|
// src/snapshot/stack.ts
|
|
@@ -1542,23 +1987,48 @@ function buildBaseFrame(frame) {
|
|
|
1542
1987
|
};
|
|
1543
1988
|
return frame.url === void 0 ? base : { ...base, url: frame.url };
|
|
1544
1989
|
}
|
|
1545
|
-
async function captureFrameExpression(session, callFrameId, expression, maxValueLength) {
|
|
1990
|
+
async function captureFrameExpression(session, callFrameId, expression, maxValueLength, throwOnSideEffect) {
|
|
1991
|
+
const mutationRisk = throwOnSideEffect === false && looksLikeMutation(expression);
|
|
1546
1992
|
try {
|
|
1547
|
-
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
|
+
}
|
|
1548
1999
|
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1549
|
-
|
|
2000
|
+
const serialized = await withSerializedObjectCapture(
|
|
2001
|
+
session,
|
|
2002
|
+
expression,
|
|
2003
|
+
result,
|
|
2004
|
+
captured,
|
|
2005
|
+
maxValueLength
|
|
2006
|
+
);
|
|
2007
|
+
return mutationRisk ? { ...serialized, mutationRisk: true } : serialized;
|
|
1550
2008
|
} catch (err) {
|
|
1551
2009
|
const message = err instanceof Error ? err.message : String(err);
|
|
1552
|
-
|
|
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;
|
|
1553
2017
|
}
|
|
1554
2018
|
}
|
|
1555
|
-
async function captureFrameExpressions(session, frame, expressions, maxValueLength) {
|
|
2019
|
+
async function captureFrameExpressions(session, frame, expressions, maxValueLength, throwOnSideEffect) {
|
|
1556
2020
|
if (expressions.length === 0) {
|
|
1557
2021
|
return [];
|
|
1558
2022
|
}
|
|
1559
2023
|
return await Promise.all(
|
|
1560
2024
|
expressions.map(
|
|
1561
|
-
(expression) => captureFrameExpression(
|
|
2025
|
+
(expression) => captureFrameExpression(
|
|
2026
|
+
session,
|
|
2027
|
+
frame.callFrameId,
|
|
2028
|
+
expression,
|
|
2029
|
+
maxValueLength,
|
|
2030
|
+
throwOnSideEffect
|
|
2031
|
+
)
|
|
1562
2032
|
)
|
|
1563
2033
|
);
|
|
1564
2034
|
}
|
|
@@ -1578,7 +2048,8 @@ async function walkStack(session, callFrames, options) {
|
|
|
1578
2048
|
session,
|
|
1579
2049
|
frame,
|
|
1580
2050
|
options.stackCaptures,
|
|
1581
|
-
options.maxValueLength
|
|
2051
|
+
options.maxValueLength,
|
|
2052
|
+
options.throwOnSideEffect
|
|
1582
2053
|
);
|
|
1583
2054
|
return { ...base, captures };
|
|
1584
2055
|
})
|
|
@@ -1600,14 +2071,25 @@ async function captureSnapshot(session, pause, options = {}) {
|
|
|
1600
2071
|
column: top.columnNumber + 1
|
|
1601
2072
|
};
|
|
1602
2073
|
if (options.includeScopes === true) {
|
|
1603
|
-
const
|
|
1604
|
-
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
|
+
};
|
|
1605
2080
|
}
|
|
1606
|
-
captures = await captureExpressions(
|
|
2081
|
+
captures = await captureExpressions(
|
|
2082
|
+
session,
|
|
2083
|
+
top.callFrameId,
|
|
2084
|
+
options.captures,
|
|
2085
|
+
maxValueLength,
|
|
2086
|
+
options.throwOnSideEffect
|
|
2087
|
+
);
|
|
1607
2088
|
stack = await walkStack(session, pause.callFrames, {
|
|
1608
2089
|
stackDepth: options.stackDepth ?? DEFAULT_STACK_DEPTH,
|
|
1609
2090
|
stackCaptures: options.stackCaptures ?? [],
|
|
1610
|
-
maxValueLength
|
|
2091
|
+
maxValueLength,
|
|
2092
|
+
...options.throwOnSideEffect === void 0 ? {} : { throwOnSideEffect: options.throwOnSideEffect }
|
|
1611
2093
|
});
|
|
1612
2094
|
}
|
|
1613
2095
|
const exception = await captureException(session, pause, maxValueLength);
|
|
@@ -1630,24 +2112,49 @@ function buildResult2(input) {
|
|
|
1630
2112
|
const withStack = input.stack.length > 0 ? { ...withFrame, stack: input.stack } : withFrame;
|
|
1631
2113
|
return input.exception === void 0 ? withStack : { ...withStack, exception: input.exception };
|
|
1632
2114
|
}
|
|
1633
|
-
async function captureExpressions(session, callFrameId, captures, maxValueLength) {
|
|
2115
|
+
async function captureExpressions(session, callFrameId, captures, maxValueLength, throwOnSideEffect) {
|
|
1634
2116
|
if (captures === void 0 || captures.length === 0) {
|
|
1635
2117
|
return [];
|
|
1636
2118
|
}
|
|
1637
2119
|
return await Promise.all(
|
|
1638
2120
|
captures.map(async (expression) => {
|
|
1639
|
-
return await captureExpression(
|
|
2121
|
+
return await captureExpression(
|
|
2122
|
+
session,
|
|
2123
|
+
callFrameId,
|
|
2124
|
+
expression,
|
|
2125
|
+
maxValueLength,
|
|
2126
|
+
throwOnSideEffect
|
|
2127
|
+
);
|
|
1640
2128
|
})
|
|
1641
2129
|
);
|
|
1642
2130
|
}
|
|
1643
|
-
async function captureExpression(session, callFrameId, expression, maxValueLength) {
|
|
2131
|
+
async function captureExpression(session, callFrameId, expression, maxValueLength, throwOnSideEffect) {
|
|
2132
|
+
const mutationRisk = throwOnSideEffect === false && looksLikeMutation(expression);
|
|
1644
2133
|
try {
|
|
1645
|
-
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
|
+
}
|
|
1646
2140
|
const captured = evalResultToCaptured(expression, result, maxValueLength);
|
|
1647
|
-
|
|
2141
|
+
const serialized = await withSerializedObjectCapture(
|
|
2142
|
+
session,
|
|
2143
|
+
expression,
|
|
2144
|
+
result,
|
|
2145
|
+
captured,
|
|
2146
|
+
maxValueLength
|
|
2147
|
+
);
|
|
2148
|
+
return mutationRisk ? { ...serialized, mutationRisk: true } : serialized;
|
|
1648
2149
|
} catch (err) {
|
|
1649
2150
|
const message = err instanceof Error ? err.message : String(err);
|
|
1650
|
-
|
|
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;
|
|
1651
2158
|
}
|
|
1652
2159
|
}
|
|
1653
2160
|
|
|
@@ -1735,7 +2242,7 @@ function readArg(arg, index) {
|
|
|
1735
2242
|
}
|
|
1736
2243
|
return index === 0 ? void 0 : "";
|
|
1737
2244
|
}
|
|
1738
|
-
function parseLogEvent(rawArgs, sentinel, location, timestamp) {
|
|
2245
|
+
function parseLogEvent(rawArgs, sentinel, location, timestamp, maxValueLength = DEFAULT_STREAM_MAX_VALUE_LENGTH) {
|
|
1739
2246
|
if (!Array.isArray(rawArgs) || rawArgs.length < 2) {
|
|
1740
2247
|
return void 0;
|
|
1741
2248
|
}
|
|
@@ -1747,21 +2254,37 @@ function parseLogEvent(rawArgs, sentinel, location, timestamp) {
|
|
|
1747
2254
|
const ts = new Date(typeof timestamp === "number" ? timestamp : Date.now()).toISOString();
|
|
1748
2255
|
const at = `${location.file}:${location.line.toString()}`;
|
|
1749
2256
|
if (payload.startsWith("!err:")) {
|
|
1750
|
-
|
|
2257
|
+
const limited = limitValueLength(payload.slice("!err:".length), maxValueLength);
|
|
2258
|
+
return {
|
|
2259
|
+
ts,
|
|
2260
|
+
at,
|
|
2261
|
+
error: limited.text,
|
|
2262
|
+
...textTruncationFields(limited)
|
|
2263
|
+
};
|
|
1751
2264
|
}
|
|
1752
|
-
return parsePayload(ts, at, payload);
|
|
2265
|
+
return parsePayload(ts, at, payload, maxValueLength);
|
|
1753
2266
|
}
|
|
1754
|
-
function parsePayload(ts, at, payload) {
|
|
2267
|
+
function parsePayload(ts, at, payload, maxValueLength) {
|
|
1755
2268
|
try {
|
|
1756
2269
|
const parsed = JSON.parse(payload);
|
|
1757
2270
|
if (typeof parsed === "string") {
|
|
1758
|
-
return
|
|
2271
|
+
return buildValueEvent(ts, at, parsed, maxValueLength);
|
|
1759
2272
|
}
|
|
1760
|
-
return
|
|
2273
|
+
return buildValueEvent(ts, at, JSON.stringify(parsed), maxValueLength);
|
|
1761
2274
|
} catch {
|
|
1762
|
-
return
|
|
2275
|
+
return buildValueEvent(ts, at, payload, maxValueLength, true);
|
|
1763
2276
|
}
|
|
1764
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
|
+
}
|
|
1765
2288
|
|
|
1766
2289
|
// src/logpoint/stream.ts
|
|
1767
2290
|
function validateMaxEvents(maxEvents) {
|
|
@@ -1791,6 +2314,9 @@ function validateHitCount2(hitCount) {
|
|
|
1791
2314
|
async function streamLogpoint(session, options) {
|
|
1792
2315
|
const maxEvents = validateMaxEvents(options.maxEvents);
|
|
1793
2316
|
const hitCount = validateHitCount2(options.hitCount);
|
|
2317
|
+
const maxValueLength = resolveMaxValueLength(
|
|
2318
|
+
options.maxValueLength ?? DEFAULT_STREAM_MAX_VALUE_LENGTH
|
|
2319
|
+
);
|
|
1794
2320
|
const sentinel = generateSentinel();
|
|
1795
2321
|
const condition = buildLogpointCondition(sentinel, options.expression, {
|
|
1796
2322
|
...options.condition === void 0 ? {} : { predicate: options.condition },
|
|
@@ -1803,7 +2329,7 @@ async function streamLogpoint(session, options) {
|
|
|
1803
2329
|
if (maxEventsReached) {
|
|
1804
2330
|
return;
|
|
1805
2331
|
}
|
|
1806
|
-
const event = toLogpointEvent(raw, sentinel, options.location);
|
|
2332
|
+
const event = toLogpointEvent(raw, sentinel, options.location, maxValueLength);
|
|
1807
2333
|
if (event === void 0) {
|
|
1808
2334
|
return;
|
|
1809
2335
|
}
|
|
@@ -1843,13 +2369,13 @@ async function streamLogpoint(session, options) {
|
|
|
1843
2369
|
await removeBreakpointBestEffort(session, handle.breakpointId);
|
|
1844
2370
|
}
|
|
1845
2371
|
}
|
|
1846
|
-
function toLogpointEvent(raw, sentinel, location) {
|
|
2372
|
+
function toLogpointEvent(raw, sentinel, location, maxValueLength) {
|
|
1847
2373
|
const params = raw;
|
|
1848
2374
|
if (asString3(params.type) !== "log") {
|
|
1849
2375
|
return void 0;
|
|
1850
2376
|
}
|
|
1851
2377
|
const ts = typeof params.timestamp === "number" ? params.timestamp : void 0;
|
|
1852
|
-
return parseLogEvent(params.args, sentinel, location, ts);
|
|
2378
|
+
return parseLogEvent(params.args, sentinel, location, ts, maxValueLength);
|
|
1853
2379
|
}
|
|
1854
2380
|
async function removeBreakpointBestEffort(session, breakpointId) {
|
|
1855
2381
|
try {
|