@saptools/cf-inspector 0.4.1 → 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 +82 -21
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,9 +101,17 @@ cf-inspector snapshot --port 9229 \
|
|
|
101
101
|
| `--remote-root <value>` | Optional path-mapping anchor: literal path or `regex:<pattern>` / `/pattern/flags` |
|
|
102
102
|
| `--include-scopes` | Include expanded paused-frame scopes under `topFrame.scopes`. Omitted by default to keep targeted captures concise |
|
|
103
103
|
| `--no-json` | Print a human-readable summary instead of JSON |
|
|
104
|
+
| `--quiet` | Suppress snapshot progress messages on stderr |
|
|
104
105
|
| `--keep-paused` | Skip `Debugger.resume` after capture |
|
|
105
106
|
| `--fail-on-unmatched-pause` | Fail immediately if the target pauses somewhere else instead of waiting cooperatively |
|
|
106
107
|
|
|
108
|
+
Snapshot progress is printed to `stderr` by default, including Cloud Foundry
|
|
109
|
+
login/tunnel setup, inspector connection, breakpoint binding, the breakpoint
|
|
110
|
+
wait, capture, resume, and cleanup phases. The final JSON document remains the
|
|
111
|
+
only content written to `stdout`, so piping it to `jq` or another parser stays
|
|
112
|
+
safe. Pass `--quiet` to suppress these progress lines; warnings and errors still
|
|
113
|
+
use `stderr`.
|
|
114
|
+
|
|
107
115
|
Snapshot JSON includes frame metadata and `captures` by default. `topFrame.scopes`
|
|
108
116
|
is only present with `--include-scopes` because scope objects can be large and
|
|
109
117
|
drown out targeted captures. Values are raw debugger values, so be careful when
|
package/dist/cli.js
CHANGED
|
@@ -280,6 +280,10 @@ async function fetchInspectorVersion(host, port, timeoutMs) {
|
|
|
280
280
|
|
|
281
281
|
// src/cli/output.ts
|
|
282
282
|
import process from "process";
|
|
283
|
+
function writeProgress(message) {
|
|
284
|
+
process.stderr.write(`[cf-inspector] ${message}
|
|
285
|
+
`);
|
|
286
|
+
}
|
|
283
287
|
function writeJson(value) {
|
|
284
288
|
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
285
289
|
`);
|
|
@@ -394,7 +398,8 @@ async function openCfTunnel(target) {
|
|
|
394
398
|
...target.tunnelReadyTimeoutMs === void 0 ? {} : { tunnelReadyTimeoutMs: target.tunnelReadyTimeoutMs },
|
|
395
399
|
...target.preferredPort === void 0 ? {} : { preferredPort: target.preferredPort },
|
|
396
400
|
...target.verbose === void 0 ? {} : { verbose: target.verbose },
|
|
397
|
-
...target.signal === void 0 ? {} : { signal: target.signal }
|
|
401
|
+
...target.signal === void 0 ? {} : { signal: target.signal },
|
|
402
|
+
...target.onStatus === void 0 ? {} : { onStatus: target.onStatus }
|
|
398
403
|
};
|
|
399
404
|
const handle = await startDebugger(opts);
|
|
400
405
|
return {
|
|
@@ -843,6 +848,22 @@ var DEFAULT_CF_TIMEOUT_SEC = 60;
|
|
|
843
848
|
var DEFAULT_EXCEPTION_TIMEOUT_SEC = 30;
|
|
844
849
|
|
|
845
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
|
+
}
|
|
846
867
|
function parsePositiveInt(raw, label) {
|
|
847
868
|
if (raw === void 0) {
|
|
848
869
|
return void 0;
|
|
@@ -877,20 +898,26 @@ function resolveTarget(opts) {
|
|
|
877
898
|
function hasCfTarget(opts) {
|
|
878
899
|
return opts.region !== void 0 && opts.org !== void 0 && opts.space !== void 0 && opts.app !== void 0;
|
|
879
900
|
}
|
|
880
|
-
async function withSession(target, fn) {
|
|
881
|
-
const tunnel = await openTarget(target);
|
|
901
|
+
async function withSession(target, fn, reportProgress) {
|
|
902
|
+
const tunnel = await openTarget(target, reportProgress);
|
|
882
903
|
let session;
|
|
883
904
|
try {
|
|
905
|
+
reportProgress?.(
|
|
906
|
+
`Connecting to the Node.js inspector at ${tunnel.host}:${tunnel.port.toString()}...`
|
|
907
|
+
);
|
|
884
908
|
session = await connectInspector({ port: tunnel.port, host: tunnel.host });
|
|
909
|
+
reportProgress?.("Inspector session is ready.");
|
|
885
910
|
return await fn(session, tunnel.port);
|
|
886
911
|
} finally {
|
|
887
912
|
if (session) {
|
|
913
|
+
reportProgress?.("Closing the inspector session...");
|
|
888
914
|
await session.dispose();
|
|
915
|
+
reportProgress?.("Inspector session closed.");
|
|
889
916
|
}
|
|
890
917
|
await tunnel.dispose();
|
|
891
918
|
}
|
|
892
919
|
}
|
|
893
|
-
async function openTarget(target) {
|
|
920
|
+
async function openTarget(target, reportProgress) {
|
|
894
921
|
if (target.kind === "port") {
|
|
895
922
|
return {
|
|
896
923
|
port: target.port,
|
|
@@ -898,12 +925,18 @@ async function openTarget(target) {
|
|
|
898
925
|
dispose: () => Promise.resolve()
|
|
899
926
|
};
|
|
900
927
|
}
|
|
928
|
+
reportProgress?.(formatCfTunnelStatus("starting"));
|
|
901
929
|
const tunnel = await openCfTunnel({
|
|
902
930
|
region: target.region,
|
|
903
931
|
org: target.org,
|
|
904
932
|
space: target.space,
|
|
905
933
|
app: target.app,
|
|
906
|
-
tunnelReadyTimeoutMs: target.cfTimeoutMs
|
|
934
|
+
tunnelReadyTimeoutMs: target.cfTimeoutMs,
|
|
935
|
+
...reportProgress === void 0 ? {} : {
|
|
936
|
+
onStatus: (status) => {
|
|
937
|
+
reportProgress(formatCfTunnelStatus(status));
|
|
938
|
+
}
|
|
939
|
+
}
|
|
907
940
|
});
|
|
908
941
|
return {
|
|
909
942
|
port: tunnel.localPort,
|
|
@@ -2449,12 +2482,14 @@ import process9 from "process";
|
|
|
2449
2482
|
init_types();
|
|
2450
2483
|
async function handleSnapshot(opts) {
|
|
2451
2484
|
const prepared = prepareSnapshotCommand(opts);
|
|
2452
|
-
const
|
|
2485
|
+
const reportProgress = opts.quiet === true ? void 0 : writeProgress;
|
|
2486
|
+
const result = await runSnapshotCommand(prepared, opts, reportProgress);
|
|
2453
2487
|
if (opts.json) {
|
|
2454
2488
|
writeJson(result);
|
|
2455
2489
|
} else {
|
|
2456
2490
|
writeHumanSnapshot(result);
|
|
2457
2491
|
}
|
|
2492
|
+
reportProgress?.("Snapshot complete.");
|
|
2458
2493
|
}
|
|
2459
2494
|
function prepareSnapshotCommand(opts) {
|
|
2460
2495
|
const target = resolveTarget(opts);
|
|
@@ -2482,24 +2517,34 @@ function prepareSnapshotCommand(opts) {
|
|
|
2482
2517
|
stackCaptures: parseCaptureList(opts.stackCaptures)
|
|
2483
2518
|
};
|
|
2484
2519
|
}
|
|
2485
|
-
async function runSnapshotCommand(command, opts) {
|
|
2520
|
+
async function runSnapshotCommand(command, opts, reportProgress) {
|
|
2486
2521
|
return await withSession(command.target, async (session) => {
|
|
2487
2522
|
if (command.condition !== void 0) {
|
|
2523
|
+
reportProgress?.("Validating the breakpoint condition...");
|
|
2488
2524
|
await validateExpression(session, command.condition);
|
|
2525
|
+
reportProgress?.("Breakpoint condition is valid.");
|
|
2489
2526
|
}
|
|
2490
|
-
const
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
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"}.`
|
|
2500
2538
|
);
|
|
2501
2539
|
warnOnUnboundBreakpoints(handles);
|
|
2540
|
+
reportProgress?.(
|
|
2541
|
+
`Waiting up to ${(command.timeoutMs / 1e3).toString()}s for a breakpoint hit...`
|
|
2542
|
+
);
|
|
2502
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
|
+
);
|
|
2503
2548
|
const pausedStartedAt = pause.receivedAtMs ?? performance4.now();
|
|
2504
2549
|
const snapshot = await captureSnapshot(session, pause, {
|
|
2505
2550
|
captures: command.captures,
|
|
@@ -2509,10 +2554,25 @@ async function runSnapshotCommand(command, opts) {
|
|
|
2509
2554
|
stackCaptures: command.stackCaptures
|
|
2510
2555
|
});
|
|
2511
2556
|
if (opts.keepPaused === true) {
|
|
2557
|
+
reportProgress?.("Snapshot captured; leaving the target paused as requested.");
|
|
2512
2558
|
return withPausedDuration(snapshot, null);
|
|
2513
2559
|
}
|
|
2514
|
-
|
|
2515
|
-
|
|
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
|
+
);
|
|
2516
2576
|
}
|
|
2517
2577
|
async function waitForCommandPause(session, opts, handles, timeoutMs) {
|
|
2518
2578
|
let warnedUnmatchedPause = false;
|
|
@@ -2529,9 +2589,10 @@ async function waitForCommandPause(session, opts, handles, timeoutMs) {
|
|
|
2529
2589
|
}
|
|
2530
2590
|
});
|
|
2531
2591
|
}
|
|
2532
|
-
async function resumeAfterSnapshot(session, snapshot, pausedStartedAt) {
|
|
2592
|
+
async function resumeAfterSnapshot(session, snapshot, pausedStartedAt, reportProgress) {
|
|
2533
2593
|
try {
|
|
2534
2594
|
await resume(session);
|
|
2595
|
+
reportProgress?.("Target resumed.");
|
|
2535
2596
|
return withPausedDuration(snapshot, roundDurationMs(performance4.now() - pausedStartedAt));
|
|
2536
2597
|
} catch {
|
|
2537
2598
|
process9.stderr.write(
|
|
@@ -2786,7 +2847,7 @@ async function main(argv) {
|
|
|
2786
2847
|
function registerSnapshot(program) {
|
|
2787
2848
|
applyTargetOptions(
|
|
2788
2849
|
program.command("snapshot").description("Set a breakpoint, wait for it to hit, capture expressions, and resume")
|
|
2789
|
-
).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) => {
|
|
2790
2851
|
await handleSnapshot(opts);
|
|
2791
2852
|
});
|
|
2792
2853
|
}
|