@saptools/cf-inspector 0.4.0 → 0.4.2
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 +8 -0
- package/dist/cli.js +250 -98
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +125 -36
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/dist/cli.d.ts +0 -2
package/dist/cli.js
CHANGED
|
@@ -35,10 +35,19 @@ __export(wsTransport_exports, {
|
|
|
35
35
|
wsTransportFactory: () => wsTransportFactory
|
|
36
36
|
});
|
|
37
37
|
import { WebSocket } from "ws";
|
|
38
|
-
async function wsTransportFactory(url) {
|
|
38
|
+
async function wsTransportFactory(url, options = {}) {
|
|
39
39
|
const socket = new WebSocket(url, { perMessageDeflate: false });
|
|
40
|
-
await waitForOpen(socket, url);
|
|
41
|
-
const wrappers = /* @__PURE__ */ new
|
|
40
|
+
await waitForOpen(socket, url, options.connectTimeoutMs);
|
|
41
|
+
const wrappers = /* @__PURE__ */ new Map();
|
|
42
|
+
function wrappersFor(event) {
|
|
43
|
+
const existing = wrappers.get(event);
|
|
44
|
+
if (existing !== void 0) {
|
|
45
|
+
return existing;
|
|
46
|
+
}
|
|
47
|
+
const created = /* @__PURE__ */ new WeakMap();
|
|
48
|
+
wrappers.set(event, created);
|
|
49
|
+
return created;
|
|
50
|
+
}
|
|
42
51
|
return {
|
|
43
52
|
send(payload) {
|
|
44
53
|
socket.send(payload);
|
|
@@ -50,11 +59,12 @@ async function wsTransportFactory(url) {
|
|
|
50
59
|
return socket.readyState;
|
|
51
60
|
},
|
|
52
61
|
on(event, listener) {
|
|
53
|
-
const wrapped = wrapListener(event, listener
|
|
62
|
+
const wrapped = wrapListener(event, listener);
|
|
63
|
+
wrappersFor(event).set(listener, wrapped);
|
|
54
64
|
socket.on(event, wrapped);
|
|
55
65
|
},
|
|
56
66
|
off(event, listener) {
|
|
57
|
-
const wrapped =
|
|
67
|
+
const wrapped = wrappersFor(event).get(listener);
|
|
58
68
|
if (!wrapped) {
|
|
59
69
|
return;
|
|
60
70
|
}
|
|
@@ -62,14 +72,30 @@ async function wsTransportFactory(url) {
|
|
|
62
72
|
}
|
|
63
73
|
};
|
|
64
74
|
}
|
|
65
|
-
async function waitForOpen(socket, url) {
|
|
75
|
+
async function waitForOpen(socket, url, timeoutMs) {
|
|
66
76
|
await new Promise((resolve, reject) => {
|
|
67
|
-
|
|
77
|
+
let settled = false;
|
|
78
|
+
const cleanup = () => {
|
|
79
|
+
socket.off("open", onOpen);
|
|
68
80
|
socket.off("error", onError);
|
|
81
|
+
if (timer !== void 0) {
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const onOpen = () => {
|
|
86
|
+
if (settled) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
settled = true;
|
|
90
|
+
cleanup();
|
|
69
91
|
resolve();
|
|
70
92
|
};
|
|
71
93
|
const onError = (err) => {
|
|
72
|
-
|
|
94
|
+
if (settled) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
settled = true;
|
|
98
|
+
cleanup();
|
|
73
99
|
reject(
|
|
74
100
|
new CfInspectorError(
|
|
75
101
|
"INSPECTOR_CONNECTION_FAILED",
|
|
@@ -77,29 +103,45 @@ async function waitForOpen(socket, url) {
|
|
|
77
103
|
)
|
|
78
104
|
);
|
|
79
105
|
};
|
|
106
|
+
const timer = timeoutMs === void 0 ? void 0 : setTimeout(() => {
|
|
107
|
+
if (settled) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
settled = true;
|
|
111
|
+
cleanup();
|
|
112
|
+
socket.on("error", () => {
|
|
113
|
+
});
|
|
114
|
+
try {
|
|
115
|
+
socket.terminate();
|
|
116
|
+
} catch {
|
|
117
|
+
}
|
|
118
|
+
reject(
|
|
119
|
+
new CfInspectorError(
|
|
120
|
+
"INSPECTOR_CONNECTION_FAILED",
|
|
121
|
+
`WebSocket handshake to ${url} timed out after ${timeoutMs.toString()}ms`
|
|
122
|
+
)
|
|
123
|
+
);
|
|
124
|
+
}, timeoutMs);
|
|
80
125
|
socket.once("open", onOpen);
|
|
81
126
|
socket.once("error", onError);
|
|
82
127
|
});
|
|
83
128
|
}
|
|
84
|
-
function wrapListener(event, listener
|
|
129
|
+
function wrapListener(event, listener) {
|
|
85
130
|
if (event === "message") {
|
|
86
131
|
const wrapped2 = (data) => {
|
|
87
132
|
listener(data.toString("utf8"));
|
|
88
133
|
};
|
|
89
|
-
wrappers.set(listener, wrapped2);
|
|
90
134
|
return wrapped2;
|
|
91
135
|
}
|
|
92
136
|
if (event === "close") {
|
|
93
137
|
const wrapped2 = () => {
|
|
94
138
|
listener();
|
|
95
139
|
};
|
|
96
|
-
wrappers.set(listener, wrapped2);
|
|
97
140
|
return wrapped2;
|
|
98
141
|
}
|
|
99
142
|
const wrapped = (err) => {
|
|
100
143
|
listener(err);
|
|
101
144
|
};
|
|
102
|
-
wrappers.set(listener, wrapped);
|
|
103
145
|
return wrapped;
|
|
104
146
|
}
|
|
105
147
|
var init_wsTransport = __esm({
|
|
@@ -110,7 +152,7 @@ var init_wsTransport = __esm({
|
|
|
110
152
|
});
|
|
111
153
|
|
|
112
154
|
// src/cli.ts
|
|
113
|
-
import
|
|
155
|
+
import process11 from "process";
|
|
114
156
|
|
|
115
157
|
// src/cli/program.ts
|
|
116
158
|
import { Command } from "commander";
|
|
@@ -238,6 +280,10 @@ async function fetchInspectorVersion(host, port, timeoutMs) {
|
|
|
238
280
|
|
|
239
281
|
// src/cli/output.ts
|
|
240
282
|
import process from "process";
|
|
283
|
+
function writeProgress(message) {
|
|
284
|
+
process.stderr.write(`[cf-inspector] ${message}
|
|
285
|
+
`);
|
|
286
|
+
}
|
|
241
287
|
function writeJson(value) {
|
|
242
288
|
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
243
289
|
`);
|
|
@@ -352,7 +398,8 @@ async function openCfTunnel(target) {
|
|
|
352
398
|
...target.tunnelReadyTimeoutMs === void 0 ? {} : { tunnelReadyTimeoutMs: target.tunnelReadyTimeoutMs },
|
|
353
399
|
...target.preferredPort === void 0 ? {} : { preferredPort: target.preferredPort },
|
|
354
400
|
...target.verbose === void 0 ? {} : { verbose: target.verbose },
|
|
355
|
-
...target.signal === void 0 ? {} : { signal: target.signal }
|
|
401
|
+
...target.signal === void 0 ? {} : { signal: target.signal },
|
|
402
|
+
...target.onStatus === void 0 ? {} : { onStatus: target.onStatus }
|
|
356
403
|
};
|
|
357
404
|
const handle = await startDebugger(opts);
|
|
358
405
|
return {
|
|
@@ -404,10 +451,19 @@ var CdpClient = class _CdpClient {
|
|
|
404
451
|
return;
|
|
405
452
|
}
|
|
406
453
|
if (typeof parsed.method === "string") {
|
|
407
|
-
this.
|
|
408
|
-
this.
|
|
454
|
+
this.safeEmit(parsed.method, parsed.params);
|
|
455
|
+
this.safeEmit("event", { method: parsed.method, params: parsed.params });
|
|
409
456
|
}
|
|
410
457
|
};
|
|
458
|
+
safeEmit(event, payload) {
|
|
459
|
+
const listeners = this.emitter.listeners(event);
|
|
460
|
+
for (const listener of listeners) {
|
|
461
|
+
try {
|
|
462
|
+
listener(payload);
|
|
463
|
+
} catch {
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
411
467
|
handleClose = () => {
|
|
412
468
|
this.markClosed(new CfInspectorError("INSPECTOR_CONNECTION_FAILED", "Inspector connection closed"));
|
|
413
469
|
};
|
|
@@ -425,7 +481,8 @@ var CdpClient = class _CdpClient {
|
|
|
425
481
|
}
|
|
426
482
|
static async connect(options) {
|
|
427
483
|
const factory = options.transportFactory ?? await loadDefaultFactory();
|
|
428
|
-
const
|
|
484
|
+
const factoryOptions = options.connectTimeoutMs === void 0 ? {} : { connectTimeoutMs: options.connectTimeoutMs };
|
|
485
|
+
const transport = await factory(options.url, factoryOptions);
|
|
429
486
|
return new _CdpClient(transport, options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS);
|
|
430
487
|
}
|
|
431
488
|
async send(method, params = {}) {
|
|
@@ -475,8 +532,16 @@ var CdpClient = class _CdpClient {
|
|
|
475
532
|
return;
|
|
476
533
|
}
|
|
477
534
|
const params = raw;
|
|
478
|
-
if (options.predicate
|
|
479
|
-
|
|
535
|
+
if (options.predicate) {
|
|
536
|
+
let accepted;
|
|
537
|
+
try {
|
|
538
|
+
accepted = options.predicate(params);
|
|
539
|
+
} catch {
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
if (!accepted) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
480
545
|
}
|
|
481
546
|
finish(params);
|
|
482
547
|
});
|
|
@@ -716,7 +781,18 @@ async function connectInspector(options) {
|
|
|
716
781
|
`No inspector targets available on ${host}:${options.port.toString()}`
|
|
717
782
|
);
|
|
718
783
|
}
|
|
719
|
-
const client = await CdpClient.connect({
|
|
784
|
+
const client = await CdpClient.connect({
|
|
785
|
+
url: target.webSocketDebuggerUrl,
|
|
786
|
+
connectTimeoutMs
|
|
787
|
+
});
|
|
788
|
+
try {
|
|
789
|
+
return await initSession(client, target);
|
|
790
|
+
} catch (err) {
|
|
791
|
+
client.dispose();
|
|
792
|
+
throw err;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
async function initSession(client, target) {
|
|
720
796
|
const scripts = /* @__PURE__ */ new Map();
|
|
721
797
|
client.on("Debugger.scriptParsed", (raw) => {
|
|
722
798
|
const params = raw;
|
|
@@ -772,6 +848,22 @@ var DEFAULT_CF_TIMEOUT_SEC = 60;
|
|
|
772
848
|
var DEFAULT_EXCEPTION_TIMEOUT_SEC = 30;
|
|
773
849
|
|
|
774
850
|
// src/cli/target.ts
|
|
851
|
+
var CF_TUNNEL_STATUS_MESSAGES = {
|
|
852
|
+
starting: "Preparing the Cloud Foundry debugger...",
|
|
853
|
+
"logging-in": "Logging in to Cloud Foundry...",
|
|
854
|
+
targeting: "Targeting the Cloud Foundry org and space...",
|
|
855
|
+
"ssh-enabling": "Enabling SSH for the Cloud Foundry app...",
|
|
856
|
+
"ssh-restarting": "Restarting the Cloud Foundry app to activate SSH...",
|
|
857
|
+
signaling: "Starting the remote Node.js inspector...",
|
|
858
|
+
tunneling: "Opening the SSH inspector tunnel...",
|
|
859
|
+
ready: "Cloud Foundry inspector tunnel is ready.",
|
|
860
|
+
stopping: "Closing the Cloud Foundry inspector tunnel...",
|
|
861
|
+
stopped: "Cloud Foundry inspector tunnel closed.",
|
|
862
|
+
error: "Cloud Foundry inspector tunnel failed."
|
|
863
|
+
};
|
|
864
|
+
function formatCfTunnelStatus(status) {
|
|
865
|
+
return CF_TUNNEL_STATUS_MESSAGES[status];
|
|
866
|
+
}
|
|
775
867
|
function parsePositiveInt(raw, label) {
|
|
776
868
|
if (raw === void 0) {
|
|
777
869
|
return void 0;
|
|
@@ -806,20 +898,26 @@ function resolveTarget(opts) {
|
|
|
806
898
|
function hasCfTarget(opts) {
|
|
807
899
|
return opts.region !== void 0 && opts.org !== void 0 && opts.space !== void 0 && opts.app !== void 0;
|
|
808
900
|
}
|
|
809
|
-
async function withSession(target, fn) {
|
|
810
|
-
const tunnel = await openTarget(target);
|
|
901
|
+
async function withSession(target, fn, reportProgress) {
|
|
902
|
+
const tunnel = await openTarget(target, reportProgress);
|
|
811
903
|
let session;
|
|
812
904
|
try {
|
|
905
|
+
reportProgress?.(
|
|
906
|
+
`Connecting to the Node.js inspector at ${tunnel.host}:${tunnel.port.toString()}...`
|
|
907
|
+
);
|
|
813
908
|
session = await connectInspector({ port: tunnel.port, host: tunnel.host });
|
|
909
|
+
reportProgress?.("Inspector session is ready.");
|
|
814
910
|
return await fn(session, tunnel.port);
|
|
815
911
|
} finally {
|
|
816
912
|
if (session) {
|
|
913
|
+
reportProgress?.("Closing the inspector session...");
|
|
817
914
|
await session.dispose();
|
|
915
|
+
reportProgress?.("Inspector session closed.");
|
|
818
916
|
}
|
|
819
917
|
await tunnel.dispose();
|
|
820
918
|
}
|
|
821
919
|
}
|
|
822
|
-
async function openTarget(target) {
|
|
920
|
+
async function openTarget(target, reportProgress) {
|
|
823
921
|
if (target.kind === "port") {
|
|
824
922
|
return {
|
|
825
923
|
port: target.port,
|
|
@@ -827,12 +925,18 @@ async function openTarget(target) {
|
|
|
827
925
|
dispose: () => Promise.resolve()
|
|
828
926
|
};
|
|
829
927
|
}
|
|
928
|
+
reportProgress?.(formatCfTunnelStatus("starting"));
|
|
830
929
|
const tunnel = await openCfTunnel({
|
|
831
930
|
region: target.region,
|
|
832
931
|
org: target.org,
|
|
833
932
|
space: target.space,
|
|
834
933
|
app: target.app,
|
|
835
|
-
tunnelReadyTimeoutMs: target.cfTimeoutMs
|
|
934
|
+
tunnelReadyTimeoutMs: target.cfTimeoutMs,
|
|
935
|
+
...reportProgress === void 0 ? {} : {
|
|
936
|
+
onStatus: (status) => {
|
|
937
|
+
reportProgress(formatCfTunnelStatus(status));
|
|
938
|
+
}
|
|
939
|
+
}
|
|
836
940
|
});
|
|
837
941
|
return {
|
|
838
942
|
port: tunnel.localPort,
|
|
@@ -1066,9 +1170,11 @@ function normalizeRegexRootPattern(pattern) {
|
|
|
1066
1170
|
const withoutEndAnchor = withoutStartAnchor.endsWith("$") && !isEscaped(withoutStartAnchor, withoutStartAnchor.length - 1) ? withoutStartAnchor.slice(0, -1) : withoutStartAnchor;
|
|
1067
1171
|
return stripTrailingSlash(withoutEndAnchor);
|
|
1068
1172
|
}
|
|
1069
|
-
function buildFileUrlRegex(
|
|
1070
|
-
|
|
1071
|
-
|
|
1173
|
+
function buildFileUrlRegex(embeddedRoot, tail, separator) {
|
|
1174
|
+
return `^file://${embeddedRoot}${separator}${tail}$`;
|
|
1175
|
+
}
|
|
1176
|
+
function rootSeparator(rawPattern) {
|
|
1177
|
+
return rawPattern.endsWith("/") ? "" : "/";
|
|
1072
1178
|
}
|
|
1073
1179
|
function escapeRegExp(value) {
|
|
1074
1180
|
return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
@@ -1095,12 +1201,12 @@ function buildBreakpointUrlRegex(input) {
|
|
|
1095
1201
|
return `(?:^|/)${tail}$`;
|
|
1096
1202
|
}
|
|
1097
1203
|
case "literal": {
|
|
1098
|
-
const
|
|
1099
|
-
return buildFileUrlRegex(
|
|
1204
|
+
const root = input.remoteRoot.value;
|
|
1205
|
+
return buildFileUrlRegex(escapeRegExp(root), tail, rootSeparator(root));
|
|
1100
1206
|
}
|
|
1101
1207
|
case "regex": {
|
|
1102
|
-
const
|
|
1103
|
-
return buildFileUrlRegex(
|
|
1208
|
+
const root = normalizeRegexRootPattern(input.remoteRoot.pattern);
|
|
1209
|
+
return buildFileUrlRegex(`(?:${root})`, tail, rootSeparator(root));
|
|
1104
1210
|
}
|
|
1105
1211
|
}
|
|
1106
1212
|
}
|
|
@@ -1364,19 +1470,32 @@ function scalarFromVariable(variable) {
|
|
|
1364
1470
|
}
|
|
1365
1471
|
return value === "null" ? null : value;
|
|
1366
1472
|
}
|
|
1473
|
+
function isArrayLikeChildren(children) {
|
|
1474
|
+
let hasNumeric = false;
|
|
1475
|
+
for (const child of children) {
|
|
1476
|
+
if (child.name === "length") {
|
|
1477
|
+
continue;
|
|
1478
|
+
}
|
|
1479
|
+
if (parseNumericIndex(child.name) === void 0) {
|
|
1480
|
+
return false;
|
|
1481
|
+
}
|
|
1482
|
+
hasNumeric = true;
|
|
1483
|
+
}
|
|
1484
|
+
return hasNumeric;
|
|
1485
|
+
}
|
|
1367
1486
|
function toStructuredValue(variable) {
|
|
1368
1487
|
const children = variable.children;
|
|
1369
1488
|
if (children === void 0 || children.length === 0) {
|
|
1370
1489
|
return scalarFromVariable(variable);
|
|
1371
1490
|
}
|
|
1372
|
-
|
|
1373
|
-
const
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1491
|
+
if (isArrayLikeChildren(children)) {
|
|
1492
|
+
const indexed = children.flatMap((child) => {
|
|
1493
|
+
const index = parseNumericIndex(child.name);
|
|
1494
|
+
if (index === void 0) {
|
|
1495
|
+
return [];
|
|
1496
|
+
}
|
|
1497
|
+
return [[index, toStructuredValue(child)]];
|
|
1498
|
+
});
|
|
1380
1499
|
const maxIndex = Math.max(...indexed.map(([index]) => index));
|
|
1381
1500
|
const out2 = Array.from({ length: maxIndex + 1 }, () => null);
|
|
1382
1501
|
for (const [index, entry] of indexed) {
|
|
@@ -2041,7 +2160,7 @@ async function handleListScripts(opts) {
|
|
|
2041
2160
|
}
|
|
2042
2161
|
|
|
2043
2162
|
// src/cli/commands/log.ts
|
|
2044
|
-
import
|
|
2163
|
+
import process8 from "process";
|
|
2045
2164
|
|
|
2046
2165
|
// src/logpoint/stream.ts
|
|
2047
2166
|
init_types();
|
|
@@ -2200,7 +2319,10 @@ async function streamLogpoint(session, options) {
|
|
|
2200
2319
|
return;
|
|
2201
2320
|
}
|
|
2202
2321
|
emitted += 1;
|
|
2203
|
-
|
|
2322
|
+
try {
|
|
2323
|
+
options.onEvent(event);
|
|
2324
|
+
} catch {
|
|
2325
|
+
}
|
|
2204
2326
|
if (maxEvents !== void 0 && emitted >= maxEvents) {
|
|
2205
2327
|
maxEventsReached = true;
|
|
2206
2328
|
stopMaxEvents?.();
|
|
@@ -2285,6 +2407,25 @@ async function waitForStop(session, options, registerMaxEventsSignal) {
|
|
|
2285
2407
|
|
|
2286
2408
|
// src/cli/commands/log.ts
|
|
2287
2409
|
init_types();
|
|
2410
|
+
|
|
2411
|
+
// src/cli/signals.ts
|
|
2412
|
+
import process7 from "process";
|
|
2413
|
+
async function withTerminationSignal(fn) {
|
|
2414
|
+
const abort = new AbortController();
|
|
2415
|
+
const onSignal = () => {
|
|
2416
|
+
abort.abort();
|
|
2417
|
+
};
|
|
2418
|
+
process7.once("SIGINT", onSignal);
|
|
2419
|
+
process7.once("SIGTERM", onSignal);
|
|
2420
|
+
try {
|
|
2421
|
+
return await fn(abort.signal);
|
|
2422
|
+
} finally {
|
|
2423
|
+
process7.off("SIGINT", onSignal);
|
|
2424
|
+
process7.off("SIGTERM", onSignal);
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
// src/cli/commands/log.ts
|
|
2288
2429
|
async function handleLog(opts) {
|
|
2289
2430
|
const target = resolveTarget(opts);
|
|
2290
2431
|
const location = parseBreakpointSpec(opts.at);
|
|
@@ -2297,13 +2438,7 @@ async function handleLog(opts) {
|
|
|
2297
2438
|
throw new CfInspectorError("INVALID_EXPRESSION", "--expr must not be empty");
|
|
2298
2439
|
}
|
|
2299
2440
|
const condition = opts.condition !== void 0 && opts.condition.trim().length > 0 ? opts.condition.trim() : void 0;
|
|
2300
|
-
|
|
2301
|
-
const onSig = () => {
|
|
2302
|
-
abort.abort();
|
|
2303
|
-
};
|
|
2304
|
-
process7.once("SIGINT", onSig);
|
|
2305
|
-
process7.once("SIGTERM", onSig);
|
|
2306
|
-
try {
|
|
2441
|
+
await withTerminationSignal(async (signal) => {
|
|
2307
2442
|
await withSession(target, async (session) => {
|
|
2308
2443
|
await validateExpression(session, expression);
|
|
2309
2444
|
if (condition !== void 0) {
|
|
@@ -2317,7 +2452,7 @@ async function handleLog(opts) {
|
|
|
2317
2452
|
...maxEvents === void 0 ? {} : { maxEvents },
|
|
2318
2453
|
...hitCount === void 0 ? {} : { hitCount },
|
|
2319
2454
|
...condition === void 0 ? {} : { condition },
|
|
2320
|
-
signal
|
|
2455
|
+
signal,
|
|
2321
2456
|
onEvent: (event) => {
|
|
2322
2457
|
writeLogEvent(event, opts.json);
|
|
2323
2458
|
},
|
|
@@ -2327,18 +2462,15 @@ async function handleLog(opts) {
|
|
|
2327
2462
|
});
|
|
2328
2463
|
writeLogSummary(result.stoppedReason, result.emitted, opts.json);
|
|
2329
2464
|
});
|
|
2330
|
-
}
|
|
2331
|
-
process7.off("SIGINT", onSig);
|
|
2332
|
-
process7.off("SIGTERM", onSig);
|
|
2333
|
-
}
|
|
2465
|
+
});
|
|
2334
2466
|
}
|
|
2335
2467
|
function writeLogSummary(stoppedReason, emitted, json) {
|
|
2336
2468
|
if (json) {
|
|
2337
|
-
|
|
2469
|
+
process8.stderr.write(`${JSON.stringify({ stopped: stoppedReason, emitted })}
|
|
2338
2470
|
`);
|
|
2339
2471
|
return;
|
|
2340
2472
|
}
|
|
2341
|
-
|
|
2473
|
+
process8.stderr.write(
|
|
2342
2474
|
`Stopped (${stoppedReason}); emitted ${emitted.toString()} log ${emitted === 1 ? "entry" : "entries"}.
|
|
2343
2475
|
`
|
|
2344
2476
|
);
|
|
@@ -2346,16 +2478,18 @@ function writeLogSummary(stoppedReason, emitted, json) {
|
|
|
2346
2478
|
|
|
2347
2479
|
// src/cli/commands/snapshot.ts
|
|
2348
2480
|
import { performance as performance4 } from "perf_hooks";
|
|
2349
|
-
import
|
|
2481
|
+
import process9 from "process";
|
|
2350
2482
|
init_types();
|
|
2351
2483
|
async function handleSnapshot(opts) {
|
|
2352
2484
|
const prepared = prepareSnapshotCommand(opts);
|
|
2353
|
-
const
|
|
2485
|
+
const reportProgress = opts.quiet === true ? void 0 : writeProgress;
|
|
2486
|
+
const result = await runSnapshotCommand(prepared, opts, reportProgress);
|
|
2354
2487
|
if (opts.json) {
|
|
2355
2488
|
writeJson(result);
|
|
2356
2489
|
} else {
|
|
2357
2490
|
writeHumanSnapshot(result);
|
|
2358
2491
|
}
|
|
2492
|
+
reportProgress?.("Snapshot complete.");
|
|
2359
2493
|
}
|
|
2360
2494
|
function prepareSnapshotCommand(opts) {
|
|
2361
2495
|
const target = resolveTarget(opts);
|
|
@@ -2383,24 +2517,34 @@ function prepareSnapshotCommand(opts) {
|
|
|
2383
2517
|
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
2384
2518
|
};
|
|
2385
2519
|
}
|
|
2386
|
-
async function runSnapshotCommand(command, opts) {
|
|
2520
|
+
async function runSnapshotCommand(command, opts, reportProgress) {
|
|
2387
2521
|
return await withSession(command.target, async (session) => {
|
|
2388
2522
|
if (command.condition !== void 0) {
|
|
2523
|
+
reportProgress?.("Validating the breakpoint condition...");
|
|
2389
2524
|
await validateExpression(session, command.condition);
|
|
2525
|
+
reportProgress?.("Breakpoint condition is valid.");
|
|
2390
2526
|
}
|
|
2391
|
-
const
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2527
|
+
const breakpointCount = command.breakpoints.length;
|
|
2528
|
+
reportProgress?.(
|
|
2529
|
+
`Setting ${breakpointCount.toString()} ${breakpointCount === 1 ? "breakpoint" : "breakpoints"}...`
|
|
2530
|
+
);
|
|
2531
|
+
const handles = await setCommandBreakpoints(session, command);
|
|
2532
|
+
const resolvedCount = handles.reduce(
|
|
2533
|
+
(total, handle) => total + handle.resolvedLocations.length,
|
|
2534
|
+
0
|
|
2535
|
+
);
|
|
2536
|
+
reportProgress?.(
|
|
2537
|
+
`Breakpoint setup complete: ${resolvedCount.toString()} resolved ${resolvedCount === 1 ? "location" : "locations"}.`
|
|
2401
2538
|
);
|
|
2402
2539
|
warnOnUnboundBreakpoints(handles);
|
|
2540
|
+
reportProgress?.(
|
|
2541
|
+
`Waiting up to ${(command.timeoutMs / 1e3).toString()}s for a breakpoint hit...`
|
|
2542
|
+
);
|
|
2403
2543
|
const pause = await waitForCommandPause(session, opts, handles, command.timeoutMs);
|
|
2544
|
+
const captureCount = command.captures.length;
|
|
2545
|
+
reportProgress?.(
|
|
2546
|
+
`Breakpoint hit; capturing ${captureCount.toString()} ${captureCount === 1 ? "expression" : "expressions"}...`
|
|
2547
|
+
);
|
|
2404
2548
|
const pausedStartedAt = pause.receivedAtMs ?? performance4.now();
|
|
2405
2549
|
const snapshot = await captureSnapshot(session, pause, {
|
|
2406
2550
|
captures: command.captures,
|
|
@@ -2410,10 +2554,25 @@ async function runSnapshotCommand(command, opts) {
|
|
|
2410
2554
|
stackCaptures: command.stackCaptures
|
|
2411
2555
|
});
|
|
2412
2556
|
if (opts.keepPaused === true) {
|
|
2557
|
+
reportProgress?.("Snapshot captured; leaving the target paused as requested.");
|
|
2413
2558
|
return withPausedDuration(snapshot, null);
|
|
2414
2559
|
}
|
|
2415
|
-
|
|
2416
|
-
|
|
2560
|
+
reportProgress?.("Snapshot captured; resuming the target...");
|
|
2561
|
+
return await resumeAfterSnapshot(session, snapshot, pausedStartedAt, reportProgress);
|
|
2562
|
+
}, reportProgress);
|
|
2563
|
+
}
|
|
2564
|
+
async function setCommandBreakpoints(session, command) {
|
|
2565
|
+
return await Promise.all(
|
|
2566
|
+
command.breakpoints.map(
|
|
2567
|
+
(bp) => setBreakpoint(session, {
|
|
2568
|
+
file: bp.file,
|
|
2569
|
+
line: bp.line,
|
|
2570
|
+
remoteRoot: command.remoteRoot,
|
|
2571
|
+
...command.condition === void 0 ? {} : { condition: command.condition },
|
|
2572
|
+
...command.hitCount === void 0 ? {} : { hitCount: command.hitCount }
|
|
2573
|
+
})
|
|
2574
|
+
)
|
|
2575
|
+
);
|
|
2417
2576
|
}
|
|
2418
2577
|
async function waitForCommandPause(session, opts, handles, timeoutMs) {
|
|
2419
2578
|
let warnedUnmatchedPause = false;
|
|
@@ -2430,12 +2589,13 @@ async function waitForCommandPause(session, opts, handles, timeoutMs) {
|
|
|
2430
2589
|
}
|
|
2431
2590
|
});
|
|
2432
2591
|
}
|
|
2433
|
-
async function resumeAfterSnapshot(session, snapshot, pausedStartedAt) {
|
|
2592
|
+
async function resumeAfterSnapshot(session, snapshot, pausedStartedAt, reportProgress) {
|
|
2434
2593
|
try {
|
|
2435
2594
|
await resume(session);
|
|
2595
|
+
reportProgress?.("Target resumed.");
|
|
2436
2596
|
return withPausedDuration(snapshot, roundDurationMs(performance4.now() - pausedStartedAt));
|
|
2437
2597
|
} catch {
|
|
2438
|
-
|
|
2598
|
+
process9.stderr.write(
|
|
2439
2599
|
"[cf-inspector] warning: Debugger.resume failed after snapshot; pausedDurationMs is unknown.\n"
|
|
2440
2600
|
);
|
|
2441
2601
|
return withPausedDuration(snapshot, null);
|
|
@@ -2443,28 +2603,20 @@ async function resumeAfterSnapshot(session, snapshot, pausedStartedAt) {
|
|
|
2443
2603
|
}
|
|
2444
2604
|
|
|
2445
2605
|
// src/cli/commands/watch.ts
|
|
2446
|
-
import
|
|
2606
|
+
import { performance as performance5 } from "perf_hooks";
|
|
2607
|
+
import process10 from "process";
|
|
2447
2608
|
init_types();
|
|
2448
2609
|
async function handleWatch(opts) {
|
|
2449
2610
|
const prepared = prepareWatchCommand(opts);
|
|
2450
|
-
const abort = new AbortController();
|
|
2451
|
-
const onSig = () => {
|
|
2452
|
-
abort.abort();
|
|
2453
|
-
};
|
|
2454
|
-
process9.once("SIGINT", onSig);
|
|
2455
|
-
process9.once("SIGTERM", onSig);
|
|
2456
2611
|
let stoppedReason = "signal";
|
|
2457
2612
|
let emitted = 0;
|
|
2458
|
-
|
|
2613
|
+
await withTerminationSignal(async (signal) => {
|
|
2459
2614
|
await withSession(prepared.target, async (session) => {
|
|
2460
|
-
const result = await runWatchLoop(session, prepared, opts,
|
|
2615
|
+
const result = await runWatchLoop(session, prepared, opts, signal);
|
|
2461
2616
|
stoppedReason = result.stoppedReason;
|
|
2462
2617
|
emitted = result.emitted;
|
|
2463
2618
|
});
|
|
2464
|
-
}
|
|
2465
|
-
process9.off("SIGINT", onSig);
|
|
2466
|
-
process9.off("SIGTERM", onSig);
|
|
2467
|
-
}
|
|
2619
|
+
});
|
|
2468
2620
|
writeWatchSummary(stoppedReason, emitted, opts.json);
|
|
2469
2621
|
}
|
|
2470
2622
|
function prepareWatchCommand(opts) {
|
|
@@ -2545,7 +2697,7 @@ async function runWatchLoop(session, command, opts, signal) {
|
|
|
2545
2697
|
break;
|
|
2546
2698
|
}
|
|
2547
2699
|
if (pause === "timeout") {
|
|
2548
|
-
if (deadline !== void 0 &&
|
|
2700
|
+
if (deadline !== void 0 && performance5.now() >= deadline) {
|
|
2549
2701
|
setStop("duration");
|
|
2550
2702
|
break;
|
|
2551
2703
|
}
|
|
@@ -2557,7 +2709,7 @@ async function runWatchLoop(session, command, opts, signal) {
|
|
|
2557
2709
|
try {
|
|
2558
2710
|
await resume(session);
|
|
2559
2711
|
} catch {
|
|
2560
|
-
|
|
2712
|
+
process10.stderr.write("[cf-inspector] warning: Debugger.resume failed during watch.\n");
|
|
2561
2713
|
setStop("transport-closed");
|
|
2562
2714
|
break;
|
|
2563
2715
|
}
|
|
@@ -2575,13 +2727,13 @@ function computeDeadline(durationMs) {
|
|
|
2575
2727
|
if (durationMs === void 0) {
|
|
2576
2728
|
return void 0;
|
|
2577
2729
|
}
|
|
2578
|
-
return
|
|
2730
|
+
return performance5.now() + durationMs;
|
|
2579
2731
|
}
|
|
2580
2732
|
function remainingForLoop(deadline, perHitTimeoutMs) {
|
|
2581
2733
|
if (deadline === void 0) {
|
|
2582
2734
|
return perHitTimeoutMs;
|
|
2583
2735
|
}
|
|
2584
|
-
const remaining = deadline -
|
|
2736
|
+
const remaining = deadline - performance5.now();
|
|
2585
2737
|
if (remaining <= 0) {
|
|
2586
2738
|
return 0;
|
|
2587
2739
|
}
|
|
@@ -2662,11 +2814,11 @@ function formatLocation(command, topFrame) {
|
|
|
2662
2814
|
}
|
|
2663
2815
|
function writeWatchSummary(reason, emitted, json) {
|
|
2664
2816
|
if (json) {
|
|
2665
|
-
|
|
2817
|
+
process10.stderr.write(`${JSON.stringify({ stopped: reason, emitted })}
|
|
2666
2818
|
`);
|
|
2667
2819
|
return;
|
|
2668
2820
|
}
|
|
2669
|
-
|
|
2821
|
+
process10.stderr.write(
|
|
2670
2822
|
`Stopped (${reason}); emitted ${emitted.toString()} watch ${emitted === 1 ? "event" : "events"}.
|
|
2671
2823
|
`
|
|
2672
2824
|
);
|
|
@@ -2695,7 +2847,7 @@ async function main(argv) {
|
|
|
2695
2847
|
function registerSnapshot(program) {
|
|
2696
2848
|
applyTargetOptions(
|
|
2697
2849
|
program.command("snapshot").description("Set a breakpoint, wait for it to hit, capture expressions, and resume")
|
|
2698
|
-
).option("--bp <file:line>", "Breakpoint location (repeatable; first hit wins), e.g. src/handler.ts:42", collectStrings, []).option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate in the paused frame").option("--timeout <seconds>", "How long to wait for the breakpoint to hit (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default: 4096)").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--condition <expr>", "Only pause when this JS expression evaluates truthy in the paused frame").option("--hit-count <n>", "Only pause after the breakpoint has been hit N or more times").option("--stack-depth <n>", "Walk this many call frames when capturing (default: 1, only top frame)").option("--stack-captures <expr,\u2026>", "Expressions to evaluate on each call frame in the stack").option("--include-scopes", "Include expanded paused-frame scopes in the snapshot").option("--no-json", "Print a human-readable summary instead of JSON").option("--keep-paused", "Skip Debugger.resume after capture; Node may resume when this CLI disconnects").option("--fail-on-unmatched-pause", "Fail immediately if the target pauses somewhere else").action(async (opts) => {
|
|
2850
|
+
).option("--bp <file:line>", "Breakpoint location (repeatable; first hit wins), e.g. src/handler.ts:42", collectStrings, []).option("--capture <expr,\u2026>", "Top-level comma-separated expressions to evaluate in the paused frame").option("--timeout <seconds>", "How long to wait for the breakpoint to hit (default: 30)").option("--max-value-length <chars>", "Maximum characters per captured value before truncation (default: 4096)").option("--remote-root <value>", "Path-mapping anchor: literal path or regex:<pattern> / /pattern/flags").option("--condition <expr>", "Only pause when this JS expression evaluates truthy in the paused frame").option("--hit-count <n>", "Only pause after the breakpoint has been hit N or more times").option("--stack-depth <n>", "Walk this many call frames when capturing (default: 1, only top frame)").option("--stack-captures <expr,\u2026>", "Expressions to evaluate on each call frame in the stack").option("--include-scopes", "Include expanded paused-frame scopes in the snapshot").option("--no-json", "Print a human-readable summary instead of JSON").option("--quiet", "Suppress progress messages on stderr").option("--keep-paused", "Skip Debugger.resume after capture; Node may resume when this CLI disconnects").option("--fail-on-unmatched-pause", "Fail immediately if the target pauses somewhere else").action(async (opts) => {
|
|
2699
2851
|
await handleSnapshot(opts);
|
|
2700
2852
|
});
|
|
2701
2853
|
}
|
|
@@ -2745,20 +2897,20 @@ function registerAttach(program) {
|
|
|
2745
2897
|
// src/cli.ts
|
|
2746
2898
|
init_types();
|
|
2747
2899
|
try {
|
|
2748
|
-
await main(
|
|
2900
|
+
await main(process11.argv);
|
|
2749
2901
|
} catch (err) {
|
|
2750
2902
|
if (err instanceof CfInspectorError) {
|
|
2751
|
-
|
|
2903
|
+
process11.stderr.write(`Error [${err.code}]: ${err.message}
|
|
2752
2904
|
`);
|
|
2753
2905
|
if (err.detail !== void 0) {
|
|
2754
|
-
|
|
2906
|
+
process11.stderr.write(` detail: ${err.detail}
|
|
2755
2907
|
`);
|
|
2756
2908
|
}
|
|
2757
|
-
|
|
2909
|
+
process11.exit(1);
|
|
2758
2910
|
}
|
|
2759
2911
|
const message = err instanceof Error ? err.message : String(err);
|
|
2760
|
-
|
|
2912
|
+
process11.stderr.write(`Error: ${message}
|
|
2761
2913
|
`);
|
|
2762
|
-
|
|
2914
|
+
process11.exit(1);
|
|
2763
2915
|
}
|
|
2764
2916
|
//# sourceMappingURL=cli.js.map
|