docbridge 0.13.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -1
- package/dist/bin/darwin-arm64/SHA256SUMS +3 -3
- package/dist/bin/darwin-arm64/docbridge-go-scanner +0 -0
- package/dist/bin/darwin-arm64/docbridge-swift-scanner +0 -0
- package/dist/bin/darwin-arm64/docbridge_dart_scanner +0 -0
- package/dist/bin/linux-x64/SHA256SUMS +2 -2
- package/dist/bin/linux-x64/docbridge-go-scanner +0 -0
- package/dist/bin/linux-x64/docbridge-swift-scanner +1 -1
- package/dist/index.js +179 -315
- package/dist/workers/java/docbridge-java-scanner.jar +0 -0
- package/docs/README.md +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.13.1] - 2026-10-06
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- A worker that a scan started no longer outlives the CLI. On POSIX systems
|
|
15
|
+
each worker runs in its own process group, a time or output limit kills the
|
|
16
|
+
whole group, and `check`, `related`, `context`, and `graph` kill their
|
|
17
|
+
running workers when the CLI receives `SIGINT`, `SIGTERM`, or `SIGHUP`
|
|
18
|
+
during a scan, then end by that signal. Before, a worker that ran past the
|
|
19
|
+
CLI, or a process the worker started, could keep running.
|
|
20
|
+
|
|
10
21
|
## [0.13.0] - 2026-10-01
|
|
11
22
|
|
|
12
23
|
### Added
|
|
@@ -547,7 +558,8 @@ Initial release of the SpecLink CLI.
|
|
|
547
558
|
- `speclink check` command with `--root`, `--json`, and `--audit` options.
|
|
548
559
|
- `speclink --version` (alias `-v`) and `speclink --help` (alias `-h`).
|
|
549
560
|
|
|
550
|
-
[Unreleased]: https://github.com/salan70/docbridge/compare/v0.13.
|
|
561
|
+
[Unreleased]: https://github.com/salan70/docbridge/compare/v0.13.1...HEAD
|
|
562
|
+
[0.13.1]: https://github.com/salan70/docbridge/releases/tag/v0.13.1
|
|
551
563
|
[0.13.0]: https://github.com/salan70/docbridge/releases/tag/v0.13.0
|
|
552
564
|
[0.12.0]: https://github.com/salan70/docbridge/releases/tag/v0.12.0
|
|
553
565
|
[0.11.0]: https://github.com/salan70/docbridge/releases/tag/v0.11.0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
41027ff70b494c96a27c0a4e373a070b47222323d0fb8941f4e694eeaa2f2ad9 docbridge-swift-scanner
|
|
2
|
+
42e6858926db804a07efc8b370b0744e9b23c691b8a363461e5286115c7950ef docbridge_dart_scanner
|
|
3
3
|
7fc3e6842b3e1bf425b8fd63e241eee7e64875f16be7ba8d1ae4a25baa7f2338 docbridge-rust-scanner
|
|
4
|
-
|
|
4
|
+
720b5a88fda4a2ffd10660bae19dd5070989fbe5c9463421a180f17bb50b7915 docbridge-go-scanner
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
3f2005f4df20fcf6001111724eac5a9ece412edb5457e3450bd67420e85ba8e3 docbridge-swift-scanner
|
|
2
2
|
b75b14006ca792ed3ca8faf7ceee9e4d33c895f27dd416b640818cf8a4bdd9c8 docbridge_dart_scanner
|
|
3
3
|
e0da7b8cc6ee23704f5fe8514cc309f0bb60380c77b55e3bc5ed113abd1f3478 docbridge-rust-scanner
|
|
4
|
-
|
|
4
|
+
2b8820630246507cdaca9de288dc8a0eb5d870db36287cf8ffd6ec31ce13b328 docbridge-go-scanner
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/dist/bin/linux-x64/docbridge-swift-scanner
|
|
3
3
|
size: 40932352 bytes
|
|
4
|
-
sha256:
|
|
4
|
+
sha256: 3f2005f4df20fcf6001111724eac5a9ece412edb5457e3450bd67420e85ba8e3
|
package/dist/index.js
CHANGED
|
@@ -175581,7 +175581,7 @@ import { join as join15, resolve as resolve5 } from "node:path";
|
|
|
175581
175581
|
// package.json
|
|
175582
175582
|
var package_default = {
|
|
175583
175583
|
name: "docbridge",
|
|
175584
|
-
version: "0.13.
|
|
175584
|
+
version: "0.13.1",
|
|
175585
175585
|
description: "A CLI that links Markdown specifications and code declarations. Runs on Node.js and Bun.",
|
|
175586
175586
|
keywords: [
|
|
175587
175587
|
"bun",
|
|
@@ -176065,10 +176065,6 @@ function validateCodeEntry(language, value, diagnostics) {
|
|
|
176065
176065
|
}
|
|
176066
176066
|
function validateVisibilityOptions(language, values, target, diagnostics) {
|
|
176067
176067
|
const allowed = LANGUAGE_VISIBILITY[language];
|
|
176068
|
-
if (allowed.length === 0) {
|
|
176069
|
-
diagnostics.push(configDiagnostic2("config_invalid_value", `${target}.visibility`, `\`${target}.visibility\` is not supported for ${language}.`));
|
|
176070
|
-
return;
|
|
176071
|
-
}
|
|
176072
176068
|
for (const value of values) {
|
|
176073
176069
|
if (!allowed.includes(value)) {
|
|
176074
176070
|
diagnostics.push(configDiagnostic2("config_invalid_value", `${target}.visibility`, `Unsupported ${language} visibility: ${value}. Supported values: ${allowed.join(", ")}.`));
|
|
@@ -177779,9 +177775,6 @@ function duplicateLinkDiagnostic(source, target, location, range) {
|
|
|
177779
177775
|
// src/scan/code/typescript.ts
|
|
177780
177776
|
var typeScriptAdapter = {
|
|
177781
177777
|
language: "typescript",
|
|
177782
|
-
scanFile(filePath, content, options) {
|
|
177783
|
-
return scanTypeScript(filePath, content, options);
|
|
177784
|
-
},
|
|
177785
177778
|
scanFiles(files, options) {
|
|
177786
177779
|
return files.map(({ filePath, content }) => scanTypeScript(filePath, content, options));
|
|
177787
177780
|
}
|
|
@@ -178250,12 +178243,9 @@ function firstToken(text) {
|
|
|
178250
178243
|
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync3, realpathSync as realpathSync2, statSync as statSync2 } from "node:fs";
|
|
178251
178244
|
import { extname, isAbsolute as isAbsolute2, join as join7, resolve as resolve2 } from "node:path";
|
|
178252
178245
|
|
|
178253
|
-
// src/scan/code/worker/runtime-probe.ts
|
|
178254
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
178255
|
-
|
|
178256
178246
|
// src/scan/code/worker/scanner-worker.ts
|
|
178257
178247
|
var import__2020 = __toESM(require_2020(), 1);
|
|
178258
|
-
import { spawn
|
|
178248
|
+
import { spawn } from "node:child_process";
|
|
178259
178249
|
import { mkdirSync } from "node:fs";
|
|
178260
178250
|
import { tmpdir } from "node:os";
|
|
178261
178251
|
import { dirname, join as join4 } from "node:path";
|
|
@@ -178454,14 +178444,12 @@ var scanner_worker_schema_default = {
|
|
|
178454
178444
|
// src/scan/code/worker/scanner-worker.ts
|
|
178455
178445
|
var WORKER_OUTPUT_LIMIT_BYTES = 1024 * 1024 * 1024;
|
|
178456
178446
|
var KILLED_WORKER_GRACE_MS = 500;
|
|
178457
|
-
|
|
178458
|
-
|
|
178459
|
-
|
|
178460
|
-
|
|
178461
|
-
|
|
178462
|
-
};
|
|
178447
|
+
var liveWorkers = new Map;
|
|
178448
|
+
var compiledResponseValidator;
|
|
178449
|
+
function responseValidator() {
|
|
178450
|
+
compiledResponseValidator ??= compileWorkerResponseValidator();
|
|
178451
|
+
return compiledResponseValidator;
|
|
178463
178452
|
}
|
|
178464
|
-
var responseValidator = createLazyWorkerResponseValidator(compileWorkerResponseValidator);
|
|
178465
178453
|
function compileWorkerResponseValidator() {
|
|
178466
178454
|
const ajv = new import__2020.default({ allErrors: true, strict: true });
|
|
178467
178455
|
ajv.addSchema(common_output_schema_default);
|
|
@@ -178472,13 +178460,10 @@ function compileWorkerResponseValidator() {
|
|
|
178472
178460
|
});
|
|
178473
178461
|
}
|
|
178474
178462
|
function invokeScannerWorker(request, command, run = runScannerWorkerProcess, stripEnv = []) {
|
|
178475
|
-
|
|
178476
|
-
}
|
|
178477
|
-
function invokeScannerWorkerAsync(request, command, runAsync = runScannerWorkerProcessAsync, stripEnv = []) {
|
|
178478
|
-
const run = runAsync(processInput(request, command, stripEnv));
|
|
178463
|
+
const task = run(processInput(request, command, stripEnv));
|
|
178479
178464
|
return {
|
|
178480
|
-
promise:
|
|
178481
|
-
cancel: () =>
|
|
178465
|
+
promise: task.promise.then((processResult) => interpretWorkerProcess(request, command, processResult)),
|
|
178466
|
+
cancel: () => task.cancel()
|
|
178482
178467
|
};
|
|
178483
178468
|
}
|
|
178484
178469
|
function processInput(request, command, stripEnv) {
|
|
@@ -178554,36 +178539,6 @@ function workerTimeoutMs(fileCount) {
|
|
|
178554
178539
|
return 30000 + 1000 * fileCount;
|
|
178555
178540
|
}
|
|
178556
178541
|
function runScannerWorkerProcess(input) {
|
|
178557
|
-
const maxOutputBytes = input.maxOutputBytes ?? WORKER_OUTPUT_LIMIT_BYTES;
|
|
178558
|
-
try {
|
|
178559
|
-
const [executable = "", ...args] = input.command;
|
|
178560
|
-
const result = spawnSync(executable, args, {
|
|
178561
|
-
env: workerProcessEnv(input.stripEnv),
|
|
178562
|
-
input: input.stdin,
|
|
178563
|
-
encoding: "utf8",
|
|
178564
|
-
maxBuffer: maxOutputBytes,
|
|
178565
|
-
killSignal: "SIGKILL",
|
|
178566
|
-
...input.timeoutMs === undefined ? {} : { timeout: input.timeoutMs }
|
|
178567
|
-
});
|
|
178568
|
-
return syncWorkerProcessResult(result, input.timeoutMs, maxOutputBytes);
|
|
178569
|
-
} catch (error) {
|
|
178570
|
-
return { ok: false, kind: "start", error, stderr: "" };
|
|
178571
|
-
}
|
|
178572
|
-
}
|
|
178573
|
-
function syncWorkerProcessResult(result, timeoutMs, maxOutputBytes) {
|
|
178574
|
-
const stderr = result.stderr ?? "";
|
|
178575
|
-
if (result.error !== undefined) {
|
|
178576
|
-
return syncSpawnFailure(result.error, workerStarted(result), timeoutMs, maxOutputBytes, stderr);
|
|
178577
|
-
}
|
|
178578
|
-
if (Buffer.byteLength(result.stdout ?? "") + Buffer.byteLength(stderr) > maxOutputBytes) {
|
|
178579
|
-
return { ok: false, kind: "execution", error: outputLimitError(maxOutputBytes), stderr };
|
|
178580
|
-
}
|
|
178581
|
-
if (typeof result.status !== "number") {
|
|
178582
|
-
return { ok: false, kind: "execution", error: signalError(result.signal), stderr };
|
|
178583
|
-
}
|
|
178584
|
-
return { ok: true, exitCode: result.status, stdout: result.stdout ?? "", stderr };
|
|
178585
|
-
}
|
|
178586
|
-
function runScannerWorkerProcessAsync(input) {
|
|
178587
178542
|
const run = deferred();
|
|
178588
178543
|
let settled = false;
|
|
178589
178544
|
let timer;
|
|
@@ -178593,6 +178548,9 @@ function runScannerWorkerProcessAsync(input) {
|
|
|
178593
178548
|
return;
|
|
178594
178549
|
}
|
|
178595
178550
|
settled = true;
|
|
178551
|
+
if (child !== undefined) {
|
|
178552
|
+
liveWorkers.delete(child);
|
|
178553
|
+
}
|
|
178596
178554
|
clearTimeout(timer);
|
|
178597
178555
|
clearTimeout(grace);
|
|
178598
178556
|
complete();
|
|
@@ -178601,24 +178559,34 @@ function runScannerWorkerProcessAsync(input) {
|
|
|
178601
178559
|
let child;
|
|
178602
178560
|
try {
|
|
178603
178561
|
const [executable = "", ...args] = input.command;
|
|
178604
|
-
child = spawn(executable, args, {
|
|
178562
|
+
child = spawn(executable, args, {
|
|
178563
|
+
env: workerProcessEnv(input.stripEnv),
|
|
178564
|
+
detached: ownGroup,
|
|
178565
|
+
windowsHide: true
|
|
178566
|
+
});
|
|
178605
178567
|
} catch (error) {
|
|
178606
178568
|
settle(() => run.resolve({ ok: false, kind: "start", error, stderr: "" }));
|
|
178607
178569
|
return { promise: run.promise, cancel: () => {
|
|
178608
178570
|
return;
|
|
178609
178571
|
} };
|
|
178610
178572
|
}
|
|
178573
|
+
liveWorkers.set(child, ownGroup);
|
|
178611
178574
|
const maxOutputBytes = input.maxOutputBytes ?? WORKER_OUTPUT_LIMIT_BYTES;
|
|
178612
178575
|
const budget = { bytes: 0, limit: maxOutputBytes };
|
|
178613
178576
|
const stdout = new OutputCollector(budget);
|
|
178614
178577
|
const stderr = new OutputCollector(budget);
|
|
178615
178578
|
let failure;
|
|
178616
|
-
|
|
178579
|
+
let cause;
|
|
178580
|
+
let exited = false;
|
|
178581
|
+
const kill = (reason, why) => {
|
|
178617
178582
|
failure ??= reason;
|
|
178618
|
-
|
|
178583
|
+
cause ??= why;
|
|
178584
|
+
if (!exited) {
|
|
178585
|
+
killWorker(child, ownGroup);
|
|
178586
|
+
}
|
|
178619
178587
|
grace ??= setTimeout(() => {
|
|
178620
178588
|
closeStreams(child);
|
|
178621
|
-
settle(() => run.resolve(closedProcessResult(null, null, failure, stdout, stderr)));
|
|
178589
|
+
settle(() => run.resolve(closedProcessResult(null, null, failure, cause, stdout, stderr)));
|
|
178622
178590
|
}, KILLED_WORKER_GRACE_MS);
|
|
178623
178591
|
};
|
|
178624
178592
|
let spawned = false;
|
|
@@ -178634,30 +178602,36 @@ function runScannerWorkerProcessAsync(input) {
|
|
|
178634
178602
|
});
|
|
178635
178603
|
child.stdout.on("data", (chunk) => {
|
|
178636
178604
|
if (!stdout.add(chunk)) {
|
|
178637
|
-
kill(outputLimitError(maxOutputBytes));
|
|
178605
|
+
kill(outputLimitError(maxOutputBytes), { type: "output-limit" });
|
|
178638
178606
|
}
|
|
178639
178607
|
});
|
|
178640
178608
|
child.stderr.on("data", (chunk) => {
|
|
178641
178609
|
if (!stderr.add(chunk)) {
|
|
178642
|
-
kill(outputLimitError(maxOutputBytes));
|
|
178610
|
+
kill(outputLimitError(maxOutputBytes), { type: "output-limit" });
|
|
178643
178611
|
}
|
|
178644
178612
|
});
|
|
178645
178613
|
child.stdin.on("error", () => {
|
|
178646
178614
|
return;
|
|
178647
178615
|
});
|
|
178616
|
+
child.on("exit", () => {
|
|
178617
|
+
exited = true;
|
|
178618
|
+
liveWorkers.delete(child);
|
|
178619
|
+
});
|
|
178648
178620
|
child.on("close", (code, signal) => {
|
|
178649
|
-
settle(() => run.resolve(closedProcessResult(code, signal, failure, stdout, stderr)));
|
|
178621
|
+
settle(() => run.resolve(closedProcessResult(code, signal, failure, cause, stdout, stderr)));
|
|
178650
178622
|
});
|
|
178651
178623
|
child.stdin.end(input.stdin);
|
|
178652
178624
|
const { timeoutMs } = input;
|
|
178653
178625
|
if (timeoutMs !== undefined) {
|
|
178654
|
-
timer = setTimeout(() => kill(timeoutError(timeoutMs)), timeoutMs);
|
|
178626
|
+
timer = setTimeout(() => kill(timeoutError(timeoutMs), { type: "timeout" }), timeoutMs);
|
|
178655
178627
|
}
|
|
178656
178628
|
return {
|
|
178657
178629
|
promise: run.promise,
|
|
178658
178630
|
cancel() {
|
|
178659
178631
|
settle(() => {
|
|
178660
|
-
|
|
178632
|
+
if (!exited) {
|
|
178633
|
+
killWorker(child, ownGroup);
|
|
178634
|
+
}
|
|
178661
178635
|
closeStreams(child);
|
|
178662
178636
|
run.reject(abortError());
|
|
178663
178637
|
});
|
|
@@ -178697,39 +178671,36 @@ class OutputCollector {
|
|
|
178697
178671
|
return Buffer.concat(this.chunks).toString("utf8");
|
|
178698
178672
|
}
|
|
178699
178673
|
}
|
|
178700
|
-
function closedProcessResult(code, signal, failure, stdout, stderr) {
|
|
178674
|
+
function closedProcessResult(code, signal, failure, cause, stdout, stderr) {
|
|
178701
178675
|
if (failure !== undefined) {
|
|
178702
|
-
return {
|
|
178676
|
+
return {
|
|
178677
|
+
ok: false,
|
|
178678
|
+
kind: "execution",
|
|
178679
|
+
error: failure,
|
|
178680
|
+
...cause === undefined ? {} : { cause },
|
|
178681
|
+
stderr: stderr.text()
|
|
178682
|
+
};
|
|
178703
178683
|
}
|
|
178704
178684
|
if (code === null) {
|
|
178705
|
-
return {
|
|
178685
|
+
return {
|
|
178686
|
+
ok: false,
|
|
178687
|
+
kind: "execution",
|
|
178688
|
+
error: signalError(signal),
|
|
178689
|
+
cause: { type: "signal", signal: signal ?? "unknown" },
|
|
178690
|
+
stderr: stderr.text()
|
|
178691
|
+
};
|
|
178706
178692
|
}
|
|
178707
178693
|
return { ok: true, exitCode: code, stdout: stdout.text(), stderr: stderr.text() };
|
|
178708
178694
|
}
|
|
178709
|
-
function workerStarted(result) {
|
|
178710
|
-
return typeof result.status === "number" || typeof result.signal === "string" || typeof result.pid === "number" && result.pid > 0;
|
|
178711
|
-
}
|
|
178712
|
-
function syncSpawnFailure(error, started, timeoutMs, maxOutputBytes, stderr) {
|
|
178713
|
-
const code = error.code;
|
|
178714
|
-
if (code === "ETIMEDOUT") {
|
|
178715
|
-
return { ok: false, kind: "execution", error: timeoutError(timeoutMs ?? 0), stderr };
|
|
178716
|
-
}
|
|
178717
|
-
if (code === "ENOBUFS") {
|
|
178718
|
-
return { ok: false, kind: "execution", error: outputLimitError(maxOutputBytes), stderr };
|
|
178719
|
-
}
|
|
178720
|
-
return { ok: false, kind: started ? "execution" : "start", error, stderr };
|
|
178721
|
-
}
|
|
178722
178695
|
function signalError(signal) {
|
|
178723
178696
|
const name = signal ?? "unknown";
|
|
178724
|
-
return
|
|
178697
|
+
return new Error(`worker terminated by signal ${name}`);
|
|
178725
178698
|
}
|
|
178726
178699
|
function timeoutError(timeoutMs) {
|
|
178727
|
-
return
|
|
178728
|
-
code: "ETIMEDOUT"
|
|
178729
|
-
});
|
|
178700
|
+
return new Error(`worker timed out after ${timeoutMs} ms`);
|
|
178730
178701
|
}
|
|
178731
178702
|
function outputLimitError(maxOutputBytes) {
|
|
178732
|
-
return
|
|
178703
|
+
return new Error(`worker wrote more than ${maxOutputBytes} bytes to stdout and stderr together`);
|
|
178733
178704
|
}
|
|
178734
178705
|
function validateWorkerResponse(value, request) {
|
|
178735
178706
|
const validator = responseValidator();
|
|
@@ -178757,18 +178728,7 @@ function formatSchemaError(errors) {
|
|
|
178757
178728
|
return `worker response does not match scanner-worker.schema.json at ${path}: ${error.message ?? "invalid value"}`;
|
|
178758
178729
|
}
|
|
178759
178730
|
function responseFilesMatchRequest(responseFiles, requestFiles) {
|
|
178760
|
-
|
|
178761
|
-
return false;
|
|
178762
|
-
}
|
|
178763
|
-
return responseFiles.every((file, index) => {
|
|
178764
|
-
if (!isRecord(file)) {
|
|
178765
|
-
return false;
|
|
178766
|
-
}
|
|
178767
|
-
return file.filePath === requestFiles[index]?.filePath;
|
|
178768
|
-
});
|
|
178769
|
-
}
|
|
178770
|
-
function isRecord(value) {
|
|
178771
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
178731
|
+
return responseFiles.length === requestFiles.length && responseFiles.every((file, index) => file.filePath === requestFiles[index]?.filePath);
|
|
178772
178732
|
}
|
|
178773
178733
|
function scannerUnavailableDiagnostic(language, error, executable) {
|
|
178774
178734
|
const label = languageLabel(language);
|
|
@@ -178807,20 +178767,17 @@ function scannerFailedDiagnostic(language, reason) {
|
|
|
178807
178767
|
function languageLabel(language) {
|
|
178808
178768
|
return language.charAt(0).toUpperCase() + language.slice(1);
|
|
178809
178769
|
}
|
|
178770
|
+
function killRunningWorkers() {
|
|
178771
|
+
for (const [child, ownGroup] of liveWorkers) {
|
|
178772
|
+
killWorker(child, ownGroup);
|
|
178773
|
+
}
|
|
178774
|
+
}
|
|
178810
178775
|
|
|
178811
178776
|
// src/scan/code/worker/runtime-probe.ts
|
|
178812
178777
|
var PROBE_TIMEOUT_MS = 1e4;
|
|
178813
178778
|
var PROBE_MAX_OUTPUT_BYTES = 64 * 1024;
|
|
178814
|
-
function probeRuntime(command, stripEnv,
|
|
178815
|
-
const
|
|
178816
|
-
return classifyProbe(spawn2(executable, [...args, "--probe"], {
|
|
178817
|
-
env: workerProcessEnv(stripEnv),
|
|
178818
|
-
timeout: PROBE_TIMEOUT_MS,
|
|
178819
|
-
maxBuffer: PROBE_MAX_OUTPUT_BYTES
|
|
178820
|
-
}));
|
|
178821
|
-
}
|
|
178822
|
-
function probeRuntimeAsync(command, stripEnv, runAsync = runScannerWorkerProcessAsync) {
|
|
178823
|
-
const run = runAsync({
|
|
178779
|
+
function probeRuntime(command, stripEnv, run = runScannerWorkerProcess) {
|
|
178780
|
+
const task = run({
|
|
178824
178781
|
command: [...command, "--probe"],
|
|
178825
178782
|
stdin: "",
|
|
178826
178783
|
stripEnv,
|
|
@@ -178828,49 +178785,34 @@ function probeRuntimeAsync(command, stripEnv, runAsync = runScannerWorkerProcess
|
|
|
178828
178785
|
maxOutputBytes: PROBE_MAX_OUTPUT_BYTES
|
|
178829
178786
|
});
|
|
178830
178787
|
return {
|
|
178831
|
-
promise:
|
|
178832
|
-
cancel: () =>
|
|
178788
|
+
promise: task.promise.then(classifyProbe),
|
|
178789
|
+
cancel: () => task.cancel()
|
|
178833
178790
|
};
|
|
178834
178791
|
}
|
|
178835
|
-
function spawnResultOf(result) {
|
|
178836
|
-
if (result.ok) {
|
|
178837
|
-
return { status: result.exitCode, signal: null, stdout: result.stdout, stderr: result.stderr };
|
|
178838
|
-
}
|
|
178839
|
-
const error = result.error instanceof Error ? result.error : new Error(reasonOf(result.error));
|
|
178840
|
-
const { signal } = error;
|
|
178841
|
-
if (typeof signal === "string") {
|
|
178842
|
-
return { status: null, signal, stdout: "", stderr: result.stderr };
|
|
178843
|
-
}
|
|
178844
|
-
return { status: null, signal: null, stdout: "", stderr: result.stderr, error };
|
|
178845
|
-
}
|
|
178846
178792
|
function classifyProbe(result) {
|
|
178847
|
-
if (result.
|
|
178848
|
-
|
|
178849
|
-
|
|
178850
|
-
|
|
178851
|
-
|
|
178852
|
-
|
|
178853
|
-
|
|
178854
|
-
|
|
178855
|
-
return
|
|
178856
|
-
kind: "failed",
|
|
178857
|
-
reason: `probe exited with status ${result.status}${detail === "" ? "" : `: ${detail}`}`
|
|
178858
|
-
};
|
|
178859
|
-
}
|
|
178860
|
-
return parseProbeOutput(result.stdout);
|
|
178861
|
-
}
|
|
178862
|
-
function classifySpawnError(error) {
|
|
178863
|
-
const code = error.code;
|
|
178864
|
-
if (code === "ETIMEDOUT") {
|
|
178865
|
-
return { kind: "failed", reason: `probe did not finish within ${PROBE_TIMEOUT_MS / 1000} s` };
|
|
178793
|
+
if (result.ok) {
|
|
178794
|
+
if (result.exitCode !== 0) {
|
|
178795
|
+
const detail = firstLine(result.stderr);
|
|
178796
|
+
return {
|
|
178797
|
+
kind: "failed",
|
|
178798
|
+
reason: `probe exited with status ${result.exitCode}${detail === "" ? "" : `: ${detail}`}`
|
|
178799
|
+
};
|
|
178800
|
+
}
|
|
178801
|
+
return parseProbeOutput(result.stdout);
|
|
178866
178802
|
}
|
|
178867
|
-
|
|
178868
|
-
|
|
178869
|
-
kind: "failed",
|
|
178870
|
-
|
|
178871
|
-
|
|
178803
|
+
switch (result.cause?.type) {
|
|
178804
|
+
case "timeout":
|
|
178805
|
+
return { kind: "failed", reason: `probe did not finish within ${PROBE_TIMEOUT_MS / 1000} s` };
|
|
178806
|
+
case "output-limit":
|
|
178807
|
+
return {
|
|
178808
|
+
kind: "failed",
|
|
178809
|
+
reason: `probe printed more than ${PROBE_MAX_OUTPUT_BYTES / 1024} KiB`
|
|
178810
|
+
};
|
|
178811
|
+
case "signal":
|
|
178812
|
+
return { kind: "failed", reason: `probe terminated by signal ${result.cause.signal}` };
|
|
178813
|
+
default:
|
|
178814
|
+
return { kind: "unstartable", reason: reasonOf(result.error) };
|
|
178872
178815
|
}
|
|
178873
|
-
return { kind: "unstartable", reason: reasonOf(error) };
|
|
178874
178816
|
}
|
|
178875
178817
|
function parseProbeOutput(stdout) {
|
|
178876
178818
|
const text = stdout.trim();
|
|
@@ -178880,7 +178822,7 @@ function parseProbeOutput(stdout) {
|
|
|
178880
178822
|
} catch {
|
|
178881
178823
|
return malformed(text);
|
|
178882
178824
|
}
|
|
178883
|
-
if (!
|
|
178825
|
+
if (!isRecord(value)) {
|
|
178884
178826
|
return malformed(text);
|
|
178885
178827
|
}
|
|
178886
178828
|
if (value.ok === true && typeof value.runtime === "string" && typeof value.version === "string") {
|
|
@@ -178898,40 +178840,9 @@ function malformed(text) {
|
|
|
178898
178840
|
function firstLine(text) {
|
|
178899
178841
|
return text.trim().split(/\r?\n/u)[0] ?? "";
|
|
178900
178842
|
}
|
|
178901
|
-
function
|
|
178843
|
+
function isRecord(value) {
|
|
178902
178844
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
178903
178845
|
}
|
|
178904
|
-
function spawnRuntimeProbe(executable, args, options) {
|
|
178905
|
-
try {
|
|
178906
|
-
const result = spawnSync2(executable, args, {
|
|
178907
|
-
env: options.env,
|
|
178908
|
-
timeout: options.timeout,
|
|
178909
|
-
killSignal: "SIGKILL",
|
|
178910
|
-
maxBuffer: options.maxBuffer,
|
|
178911
|
-
input: "",
|
|
178912
|
-
encoding: "utf8",
|
|
178913
|
-
windowsHide: true
|
|
178914
|
-
});
|
|
178915
|
-
const stdout = result.stdout ?? "";
|
|
178916
|
-
const stderr = result.stderr ?? "";
|
|
178917
|
-
const error = result.error === undefined && Buffer.byteLength(stdout) + Buffer.byteLength(stderr) > options.maxBuffer ? Object.assign(new Error("probe output exceeded maxBuffer"), { code: "ENOBUFS" }) : result.error;
|
|
178918
|
-
return {
|
|
178919
|
-
status: result.status,
|
|
178920
|
-
signal: result.signal,
|
|
178921
|
-
stdout,
|
|
178922
|
-
stderr,
|
|
178923
|
-
...error === undefined ? {} : { error }
|
|
178924
|
-
};
|
|
178925
|
-
} catch (error) {
|
|
178926
|
-
return {
|
|
178927
|
-
status: null,
|
|
178928
|
-
signal: null,
|
|
178929
|
-
stdout: "",
|
|
178930
|
-
stderr: "",
|
|
178931
|
-
error: error instanceof Error ? error : new Error(String(error))
|
|
178932
|
-
};
|
|
178933
|
-
}
|
|
178934
|
-
}
|
|
178935
178846
|
|
|
178936
178847
|
// src/scan/code/worker/scanner-executable.ts
|
|
178937
178848
|
import { accessSync, chmodSync, constants, existsSync as existsSync2, statSync } from "node:fs";
|
|
@@ -179143,22 +179054,12 @@ function rememberProbe(key, generation, outcome) {
|
|
|
179143
179054
|
function resolveRuntimeWorkerCommand(language, options) {
|
|
179144
179055
|
const env = options.env ?? process.env;
|
|
179145
179056
|
const probe = options.probe ?? probeRuntime;
|
|
179146
|
-
const steps = resolutionSteps(language, options, env);
|
|
179147
|
-
let step = steps.next();
|
|
179148
|
-
while (step.done !== true) {
|
|
179149
|
-
step = steps.next(cachedProbe(step.value, env, probe));
|
|
179150
|
-
}
|
|
179151
|
-
return step.value;
|
|
179152
|
-
}
|
|
179153
|
-
function resolveRuntimeWorkerCommandAsync(language, options) {
|
|
179154
|
-
const env = options.env ?? process.env;
|
|
179155
|
-
const probeAsync = options.probeAsync ?? probeRuntimeAsync;
|
|
179156
179057
|
return cancelableSequence(async (run) => {
|
|
179157
179058
|
const steps = resolutionSteps(language, options, env);
|
|
179158
179059
|
let step = steps.next();
|
|
179159
179060
|
while (step.done !== true) {
|
|
179160
179061
|
const request = step.value;
|
|
179161
|
-
step = steps.next(await run(() =>
|
|
179062
|
+
step = steps.next(await run(() => cachedProbe(request, env, probe)));
|
|
179162
179063
|
}
|
|
179163
179064
|
return step.value;
|
|
179164
179065
|
});
|
|
@@ -179266,24 +179167,13 @@ function isExecutableFile(path, platform) {
|
|
|
179266
179167
|
}
|
|
179267
179168
|
}
|
|
179268
179169
|
function cachedProbe({ command, stripEnv }, env, probe) {
|
|
179269
|
-
const key = probeCacheKey(command, env);
|
|
179270
|
-
const cached = probeCache.get(key);
|
|
179271
|
-
if (cached !== undefined) {
|
|
179272
|
-
return cached;
|
|
179273
|
-
}
|
|
179274
|
-
const generation = probeCacheGeneration;
|
|
179275
|
-
const outcome = probe(command, stripEnv);
|
|
179276
|
-
rememberProbe(key, generation, outcome);
|
|
179277
|
-
return outcome;
|
|
179278
|
-
}
|
|
179279
|
-
function cachedProbeAsync({ command, stripEnv }, env, probeAsync) {
|
|
179280
179170
|
const key = probeCacheKey(command, env);
|
|
179281
179171
|
const cached = probeCache.get(key);
|
|
179282
179172
|
if (cached !== undefined) {
|
|
179283
179173
|
return settledCancelable(cached);
|
|
179284
179174
|
}
|
|
179285
179175
|
const generation = probeCacheGeneration;
|
|
179286
|
-
return mapCancelable(
|
|
179176
|
+
return mapCancelable(probe(command, stripEnv), (outcome) => {
|
|
179287
179177
|
rememberProbe(key, generation, outcome);
|
|
179288
179178
|
return outcome;
|
|
179289
179179
|
});
|
|
@@ -179339,16 +179229,12 @@ function failure(spec, language, code, reason) {
|
|
|
179339
179229
|
|
|
179340
179230
|
// src/scan/code/dispatch.ts
|
|
179341
179231
|
function createScannerWorkerAdapter(language, command, adapterOptions = {}) {
|
|
179342
|
-
const
|
|
179343
|
-
const
|
|
179344
|
-
const prepare = (context) => mapCancelable(commandAsync?.(context) ?? settledCancelable(command(context)), (value) => boundWorkerAdapter(language, workerCommand(value), adapterOptions));
|
|
179232
|
+
const resolve3 = (context) => typeof command === "function" ? settledCancelable(command(context)) : command.commandAsync(context);
|
|
179233
|
+
const prepare = (context) => mapCancelable(resolve3(context), (value) => boundWorkerAdapter(language, workerCommand(value), adapterOptions));
|
|
179345
179234
|
const scanBatchAsync = (files, options, context) => {
|
|
179346
179235
|
if (files.length === 0) {
|
|
179347
179236
|
return settledCancelable([]);
|
|
179348
179237
|
}
|
|
179349
|
-
if (commandAsync === undefined) {
|
|
179350
|
-
return bound(context).scanFilesAsync(files, options, context);
|
|
179351
|
-
}
|
|
179352
179238
|
return cancelableSequence(async (step) => {
|
|
179353
179239
|
const { adapter } = await step(() => prepare(context));
|
|
179354
179240
|
return step(() => adapter.scanFilesAsync(files, options, context));
|
|
@@ -179356,8 +179242,6 @@ function createScannerWorkerAdapter(language, command, adapterOptions = {}) {
|
|
|
179356
179242
|
};
|
|
179357
179243
|
return {
|
|
179358
179244
|
language,
|
|
179359
|
-
scanFile: (filePath, content, options, context) => bound(context).scanFile(filePath, content, options, context),
|
|
179360
|
-
scanFiles: (files, options, context) => files.length === 0 ? [] : bound(context).scanFiles(files, options, context),
|
|
179361
179245
|
scanFilesAsync: scanBatchAsync,
|
|
179362
179246
|
prepare
|
|
179363
179247
|
};
|
|
@@ -179367,20 +179251,6 @@ function boundWorkerAdapter(language, resolved, adapterOptions) {
|
|
|
179367
179251
|
const runtime = resolved.ok ? resolved.runtime : [];
|
|
179368
179252
|
const adapter = {
|
|
179369
179253
|
language,
|
|
179370
|
-
scanFile(filePath, content, options, context) {
|
|
179371
|
-
const [scan] = adapter.scanFiles([{ filePath, content }], options, context);
|
|
179372
|
-
return scan ?? emptyScan(language, filePath);
|
|
179373
|
-
},
|
|
179374
|
-
scanFiles(files, options, context) {
|
|
179375
|
-
if (files.length === 0) {
|
|
179376
|
-
return [];
|
|
179377
|
-
}
|
|
179378
|
-
if (!resolved.ok) {
|
|
179379
|
-
return failedBatch(language, files, resolved.diagnostic);
|
|
179380
|
-
}
|
|
179381
|
-
const result = invokeScannerWorker(workerRequest(language, files, options, context, adapterOptions), resolved.command, adapterOptions.run, resolved.stripEnv);
|
|
179382
|
-
return result.ok ? result.codeFiles : failedBatch(language, files, result.diagnostic);
|
|
179383
|
-
},
|
|
179384
179254
|
scanFilesAsync(files, options, context) {
|
|
179385
179255
|
if (files.length === 0) {
|
|
179386
179256
|
return settledCancelable([]);
|
|
@@ -179388,36 +179258,28 @@ function boundWorkerAdapter(language, resolved, adapterOptions) {
|
|
|
179388
179258
|
if (!resolved.ok) {
|
|
179389
179259
|
return settledCancelable(failedBatch(language, files, resolved.diagnostic));
|
|
179390
179260
|
}
|
|
179391
|
-
const task =
|
|
179261
|
+
const task = invokeScannerWorker(workerRequest(language, files, options, context, adapterOptions), resolved.command, adapterOptions.run, resolved.stripEnv);
|
|
179392
179262
|
return mapCancelable(task, (result) => result.ok ? result.codeFiles : failedBatch(language, files, result.diagnostic));
|
|
179393
179263
|
},
|
|
179394
179264
|
prepare: () => settledCancelable({ argv, runtime, adapter })
|
|
179395
179265
|
};
|
|
179396
179266
|
return { argv, runtime, adapter };
|
|
179397
179267
|
}
|
|
179398
|
-
function scanCodeFiles(projectRoot, files, codeInclude, read, options = {}) {
|
|
179399
|
-
const plan = planCodeScan(files, codeInclude, read, options.onContent);
|
|
179400
|
-
const context = scanContext(projectRoot, options);
|
|
179401
|
-
for (const batch of plan.batches) {
|
|
179402
|
-
const adapter = options.adapters?.[batch.language] ?? builtInAdapters[batch.language];
|
|
179403
|
-
fillBatch(plan, batch, adapter.scanFiles(batch.files, batch.options, context));
|
|
179404
|
-
}
|
|
179405
|
-
return assemblePlan(plan);
|
|
179406
|
-
}
|
|
179407
179268
|
function scanContext(projectRoot, options) {
|
|
179408
179269
|
return options.scanners === undefined ? { projectRoot } : { projectRoot, scanners: options.scanners };
|
|
179409
179270
|
}
|
|
179410
|
-
function
|
|
179271
|
+
function scanCodeFiles(projectRoot, files, codeInclude, read, options = {}) {
|
|
179411
179272
|
const plan = planCodeScan(files, codeInclude, read, options.onContent);
|
|
179412
179273
|
const context = scanContext(projectRoot, options);
|
|
179413
179274
|
return cancelableSequence(async (step) => {
|
|
179414
179275
|
const cache = new Map;
|
|
179415
179276
|
for (const batch of plan.batches) {
|
|
179416
179277
|
const base = options.adapters?.[batch.language] ?? builtInAdapters[batch.language];
|
|
179417
|
-
const
|
|
179418
|
-
const
|
|
179419
|
-
const
|
|
179420
|
-
|
|
179278
|
+
const { argv, runtime, adapter } = "scanFilesAsync" in base ? await step(() => base.prepare(context)) : { argv: [], adapter: base };
|
|
179279
|
+
const keys = options.cache === undefined ? [] : batch.files.map((file) => codeScanCacheKey(batch.language, file.filePath, file.content, batch.options, argv, runtime));
|
|
179280
|
+
const cached = options.cache === undefined ? batch.files.map(() => {
|
|
179281
|
+
return;
|
|
179282
|
+
}) : keys.map((key) => options.cache?.get(key));
|
|
179421
179283
|
const missing = batch.files.filter((_, index) => cached[index] === undefined);
|
|
179422
179284
|
const scanned = missing.length === 0 ? [] : await step(() => scanFilesAsync(adapter, missing, batch.options, context));
|
|
179423
179285
|
let next = 0;
|
|
@@ -179425,7 +179287,7 @@ function scanCodeFilesAsync(projectRoot, files, codeInclude, read, options = {})
|
|
|
179425
179287
|
fillBatch(plan, batch, results);
|
|
179426
179288
|
results.forEach((scan, index) => {
|
|
179427
179289
|
const key = keys[index];
|
|
179428
|
-
if (scan !== undefined && key !== undefined && isReusableScan(scan)) {
|
|
179290
|
+
if (options.cache !== undefined && scan !== undefined && key !== undefined && isReusableScan(scan)) {
|
|
179429
179291
|
cache.set(key, scan);
|
|
179430
179292
|
}
|
|
179431
179293
|
});
|
|
@@ -179434,7 +179296,7 @@ function scanCodeFilesAsync(projectRoot, files, codeInclude, read, options = {})
|
|
|
179434
179296
|
});
|
|
179435
179297
|
}
|
|
179436
179298
|
function scanFilesAsync(adapter, files, options, context) {
|
|
179437
|
-
return adapter.scanFilesAsync
|
|
179299
|
+
return "scanFilesAsync" in adapter ? adapter.scanFilesAsync(files, options, context) : settledCancelable(adapter.scanFiles(files, options, context));
|
|
179438
179300
|
}
|
|
179439
179301
|
var builtInAdapters = {
|
|
179440
179302
|
typescript: typeScriptAdapter,
|
|
@@ -179452,7 +179314,9 @@ function runtimeWorkerAdapter(language) {
|
|
|
179452
179314
|
const command = scanners?.[language]?.command;
|
|
179453
179315
|
return command === undefined ? { projectRoot } : { projectRoot, command };
|
|
179454
179316
|
};
|
|
179455
|
-
return createScannerWorkerAdapter(language,
|
|
179317
|
+
return createScannerWorkerAdapter(language, {
|
|
179318
|
+
commandAsync: (context) => resolveRuntimeWorkerCommand(language, resolution(context))
|
|
179319
|
+
});
|
|
179456
179320
|
}
|
|
179457
179321
|
function planCodeScan(files, codeInclude, read, onContent) {
|
|
179458
179322
|
const slots = [];
|
|
@@ -179773,37 +179637,28 @@ function flushDanglingWith(pending, diagnostics, message) {
|
|
|
179773
179637
|
// src/query/project-scan.ts
|
|
179774
179638
|
function scanProject(options) {
|
|
179775
179639
|
const loaded = loadProjectScan(options, options.keepContent === true);
|
|
179776
|
-
if (!loaded.ok) {
|
|
179777
|
-
return loaded;
|
|
179778
|
-
}
|
|
179779
|
-
const { inputs } = loaded;
|
|
179780
|
-
const codeScan = scanCodeFiles(options.projectRoot, inputs.codeFiles, inputs.config.include.code, inputs.readFile, codeScanOptions(inputs, options));
|
|
179781
|
-
const docReads = readDocFiles(inputs, options);
|
|
179782
|
-
return { ok: true, scan: finishProjectScan(inputs, codeScan, docReads, options.buildGraph) };
|
|
179783
|
-
}
|
|
179784
|
-
function scanProjectAsync(options) {
|
|
179785
|
-
const loaded = loadProjectScan(options, true);
|
|
179786
179640
|
if (!loaded.ok) {
|
|
179787
179641
|
return cancelableSequence(async () => loaded);
|
|
179788
179642
|
}
|
|
179789
179643
|
const { inputs } = loaded;
|
|
179790
|
-
const
|
|
179791
|
-
const
|
|
179792
|
-
|
|
179644
|
+
const previousCache = options.cache;
|
|
179645
|
+
const fingerprint = previousCache === undefined ? undefined : JSON.stringify(inputs.config);
|
|
179646
|
+
const sameConfiguration = previousCache !== undefined && previousCache.fingerprint === fingerprint;
|
|
179647
|
+
if (previousCache !== undefined && !sameConfiguration && previousCache.fingerprint !== "") {
|
|
179793
179648
|
options.onConfigurationChange?.();
|
|
179794
179649
|
}
|
|
179795
|
-
const codeScan =
|
|
179650
|
+
const codeScan = scanCodeFiles(options.projectRoot, inputs.codeFiles, inputs.config.include.code, inputs.readFile, {
|
|
179796
179651
|
...codeScanOptions(inputs, options),
|
|
179797
|
-
...
|
|
179652
|
+
...previousCache === undefined ? {} : { cache: sameConfiguration ? previousCache.entries : new Map }
|
|
179798
179653
|
});
|
|
179799
179654
|
const docReads = readDocFiles(inputs, options);
|
|
179800
179655
|
return cancelableSequence(async (step) => {
|
|
179801
179656
|
const { cache, ...code } = await step(codeScan);
|
|
179802
|
-
const scan = finishProjectScan(inputs, code, docReads,
|
|
179657
|
+
const scan = finishProjectScan(inputs, code, docReads, options.buildGraph);
|
|
179803
179658
|
return {
|
|
179804
179659
|
ok: true,
|
|
179805
179660
|
scan,
|
|
179806
|
-
cache: { fingerprint, entries: cache }
|
|
179661
|
+
cache: { fingerprint: fingerprint ?? "", entries: cache }
|
|
179807
179662
|
};
|
|
179808
179663
|
});
|
|
179809
179664
|
}
|
|
@@ -179908,19 +179763,12 @@ class Project {
|
|
|
179908
179763
|
this.overlay.delete(relPath);
|
|
179909
179764
|
this.overlayRevision += 1;
|
|
179910
179765
|
}
|
|
179911
|
-
resolve() {
|
|
179912
|
-
const outcome = scanProject({
|
|
179913
|
-
...this.scanSources(this.overlay),
|
|
179914
|
-
buildGraph: true,
|
|
179915
|
-
keepContent: true
|
|
179916
|
-
});
|
|
179917
|
-
this.current = resolvedState(outcome);
|
|
179918
|
-
return this.current;
|
|
179919
|
-
}
|
|
179920
179766
|
resolveAsync() {
|
|
179921
179767
|
const revision = this.overlayRevision;
|
|
179922
|
-
const scan =
|
|
179768
|
+
const scan = scanProject({
|
|
179923
179769
|
...this.scanSources(new Map(this.overlay)),
|
|
179770
|
+
buildGraph: true,
|
|
179771
|
+
keepContent: true,
|
|
179924
179772
|
cache: this.cache,
|
|
179925
179773
|
onConfigurationChange: clearRuntimeProbeCache
|
|
179926
179774
|
});
|
|
@@ -180419,12 +180267,12 @@ function fullChangeText(changes) {
|
|
|
180419
180267
|
}
|
|
180420
180268
|
|
|
180421
180269
|
// src/query/check.ts
|
|
180422
|
-
function check(options) {
|
|
180270
|
+
async function check(options) {
|
|
180423
180271
|
const audit = options.audit ?? false;
|
|
180424
|
-
const outcome = scanProject({
|
|
180272
|
+
const outcome = await scanProject({
|
|
180425
180273
|
projectRoot: options.projectRoot,
|
|
180426
180274
|
...options.adapters === undefined ? {} : { adapters: options.adapters }
|
|
180427
|
-
});
|
|
180275
|
+
}).promise;
|
|
180428
180276
|
if (!outcome.ok) {
|
|
180429
180277
|
const sorted = sortDiagnostics(outcome.diagnostics);
|
|
180430
180278
|
return { diagnostics: sorted, summary: summarizeDiagnostics(sorted) };
|
|
@@ -180498,8 +180346,8 @@ function collectGateViolations(result) {
|
|
|
180498
180346
|
}
|
|
180499
180347
|
return violations;
|
|
180500
180348
|
}
|
|
180501
|
-
function related(options) {
|
|
180502
|
-
const outcome = scanProject({ projectRoot: options.projectRoot, buildGraph: true });
|
|
180349
|
+
async function related(options) {
|
|
180350
|
+
const outcome = await scanProject({ projectRoot: options.projectRoot, buildGraph: true }).promise;
|
|
180503
180351
|
if (!outcome.ok) {
|
|
180504
180352
|
return { ok: false, diagnostics: outcome.diagnostics };
|
|
180505
180353
|
}
|
|
@@ -180557,12 +180405,12 @@ function computeContext(graph, contentByFile, inputFiles) {
|
|
|
180557
180405
|
summary: { inputFiles: inputSet.size, contexts: contexts.length }
|
|
180558
180406
|
};
|
|
180559
180407
|
}
|
|
180560
|
-
function context(options) {
|
|
180561
|
-
const outcome = scanProject({
|
|
180408
|
+
async function context(options) {
|
|
180409
|
+
const outcome = await scanProject({
|
|
180562
180410
|
projectRoot: options.projectRoot,
|
|
180563
180411
|
buildGraph: true,
|
|
180564
180412
|
keepContent: true
|
|
180565
|
-
});
|
|
180413
|
+
}).promise;
|
|
180566
180414
|
if (!outcome.ok) {
|
|
180567
180415
|
return { ok: false, diagnostics: outcome.diagnostics };
|
|
180568
180416
|
}
|
|
@@ -180636,12 +180484,12 @@ function compareBlocks(left, right) {
|
|
|
180636
180484
|
}
|
|
180637
180485
|
|
|
180638
180486
|
// src/query/graph-output.ts
|
|
180639
|
-
function graph(options) {
|
|
180640
|
-
const outcome = scanProject({
|
|
180487
|
+
async function graph(options) {
|
|
180488
|
+
const outcome = await scanProject({
|
|
180641
180489
|
projectRoot: options.projectRoot,
|
|
180642
180490
|
buildGraph: true,
|
|
180643
180491
|
keepContent: true
|
|
180644
|
-
});
|
|
180492
|
+
}).promise;
|
|
180645
180493
|
if (!outcome.ok) {
|
|
180646
180494
|
return { ok: false, diagnostics: outcome.diagnostics };
|
|
180647
180495
|
}
|
|
@@ -181136,13 +180984,6 @@ class CliError extends Error {
|
|
|
181136
180984
|
}
|
|
181137
180985
|
}
|
|
181138
180986
|
|
|
181139
|
-
class InitCliError extends CliError {
|
|
181140
|
-
constructor(message, guidance) {
|
|
181141
|
-
super(message, guidance);
|
|
181142
|
-
this.name = "InitCliError";
|
|
181143
|
-
}
|
|
181144
|
-
}
|
|
181145
|
-
|
|
181146
180987
|
class DiagnosticOutputError extends Error {
|
|
181147
180988
|
constructor(message) {
|
|
181148
180989
|
super(message);
|
|
@@ -182101,7 +181942,6 @@ function readFileOrUndefined(path) {
|
|
|
182101
181942
|
}
|
|
182102
181943
|
|
|
182103
181944
|
// src/setup/init-plan.ts
|
|
182104
|
-
var CONFIG_FILE_NAME2 = "docbridge.config.json";
|
|
182105
181945
|
var INIT_SKILL_NAMES = ["docbridge"];
|
|
182106
181946
|
var LEGACY_SKILL_NAMES = [
|
|
182107
181947
|
"docbridge-adopt",
|
|
@@ -182269,7 +182109,7 @@ function planConfigOperations(input) {
|
|
|
182269
182109
|
return [
|
|
182270
182110
|
{
|
|
182271
182111
|
action,
|
|
182272
|
-
path:
|
|
182112
|
+
path: CONFIG_FILE_NAME,
|
|
182273
182113
|
content,
|
|
182274
182114
|
reason: "Create docbridge.config.json from discovered scope."
|
|
182275
182115
|
}
|
|
@@ -182421,7 +182261,7 @@ function resolveInitWithAgentTarget(discovery, options) {
|
|
|
182421
182261
|
return { target: discovery.agent.defaultTarget, error: undefined };
|
|
182422
182262
|
}
|
|
182423
182263
|
function readExistingConfig(projectRoot) {
|
|
182424
|
-
const configPath = join12(projectRoot,
|
|
182264
|
+
const configPath = join12(projectRoot, CONFIG_FILE_NAME);
|
|
182425
182265
|
let rawText;
|
|
182426
182266
|
try {
|
|
182427
182267
|
rawText = readFileSync7(configPath, "utf8");
|
|
@@ -182499,7 +182339,7 @@ function parseInitOptions(args, command) {
|
|
|
182499
182339
|
if (arg === "--root") {
|
|
182500
182340
|
const root = args[index + 1];
|
|
182501
182341
|
if (root === undefined) {
|
|
182502
|
-
throw new
|
|
182342
|
+
throw new CliError("--root requires a path.", rootPathGuidance(command));
|
|
182503
182343
|
}
|
|
182504
182344
|
options.root = root;
|
|
182505
182345
|
index += 1;
|
|
@@ -182508,22 +182348,22 @@ function parseInitOptions(args, command) {
|
|
|
182508
182348
|
if (arg === "--agent-target") {
|
|
182509
182349
|
const target = args[index + 1];
|
|
182510
182350
|
if (target === undefined) {
|
|
182511
|
-
throw new
|
|
182351
|
+
throw new CliError("--agent-target requires a value.", agentTargetGuidance(command));
|
|
182512
182352
|
}
|
|
182513
182353
|
if (!AGENT_TARGETS.has(target)) {
|
|
182514
|
-
throw new
|
|
182354
|
+
throw new CliError(`Unknown agent target: ${target}. Supported values: codex, claude, both${command === "init" ? ", none" : ""}.`, agentTargetGuidance(command));
|
|
182515
182355
|
}
|
|
182516
182356
|
if (command === "init-with-agent" && target === "none") {
|
|
182517
|
-
throw new
|
|
182357
|
+
throw new CliError("init-with-agent requires an agent target other than none.", agentTargetGuidance(command));
|
|
182518
182358
|
}
|
|
182519
182359
|
options.agentTarget = target;
|
|
182520
182360
|
index += 1;
|
|
182521
182361
|
continue;
|
|
182522
182362
|
}
|
|
182523
182363
|
if (arg.startsWith("--")) {
|
|
182524
|
-
throw new
|
|
182364
|
+
throw new CliError(`Unknown option: ${arg}`, commandHelpGuidance(command));
|
|
182525
182365
|
}
|
|
182526
|
-
throw new
|
|
182366
|
+
throw new CliError(`Unexpected argument: ${arg}`, commandHelpGuidance(command));
|
|
182527
182367
|
}
|
|
182528
182368
|
return options;
|
|
182529
182369
|
}
|
|
@@ -182610,19 +182450,19 @@ function resolveConfirmedScope(input) {
|
|
|
182610
182450
|
};
|
|
182611
182451
|
}
|
|
182612
182452
|
if (!input.prompts.isInteractive) {
|
|
182613
|
-
throw new
|
|
182453
|
+
throw new CliError("Interactive setup requires a TTY. Re-run with --yes for non-interactive mode.");
|
|
182614
182454
|
}
|
|
182615
182455
|
if (input.discovery.docs.ambiguous) {
|
|
182616
182456
|
const choices = input.discovery.docs.candidates.map((candidate) => candidate.pattern);
|
|
182617
182457
|
if (choices.length === 0) {
|
|
182618
|
-
throw new
|
|
182458
|
+
throw new CliError("Docs scope is ambiguous and no candidates were detected. Create docbridge.config.json manually.");
|
|
182619
182459
|
}
|
|
182620
182460
|
const docsPattern2 = input.prompts.select("Choose docs scope:", choices, choices[0] ?? "");
|
|
182621
182461
|
const languages2 = selectLanguages(input.discovery.code.languages, input.prompts);
|
|
182622
182462
|
return { docsPattern: docsPattern2, languages: languages2 };
|
|
182623
182463
|
}
|
|
182624
182464
|
if (input.discovery.docs.recommended === undefined) {
|
|
182625
|
-
throw new
|
|
182465
|
+
throw new CliError("Docs scope could not be determined.");
|
|
182626
182466
|
}
|
|
182627
182467
|
let docsPattern = input.discovery.docs.recommended.pattern;
|
|
182628
182468
|
if (!input.prompts.confirm(`Use docs scope ${docsPattern}?`, true)) {
|
|
@@ -182656,7 +182496,7 @@ function selectLanguages(languages, prompts) {
|
|
|
182656
182496
|
}
|
|
182657
182497
|
}
|
|
182658
182498
|
if (selected.length === 0) {
|
|
182659
|
-
throw new
|
|
182499
|
+
throw new CliError("At least one supported code language must remain in scope.");
|
|
182660
182500
|
}
|
|
182661
182501
|
return selected;
|
|
182662
182502
|
}
|
|
@@ -182686,10 +182526,10 @@ function resolveProjectRoot(root, command) {
|
|
|
182686
182526
|
try {
|
|
182687
182527
|
stats = statSync4(projectRoot);
|
|
182688
182528
|
} catch {
|
|
182689
|
-
throw new
|
|
182529
|
+
throw new CliError(`Root path does not exist: ${root}`, rootPathGuidance(command));
|
|
182690
182530
|
}
|
|
182691
182531
|
if (!stats.isDirectory()) {
|
|
182692
|
-
throw new
|
|
182532
|
+
throw new CliError(`Root path is not a directory: ${root}`, rootPathGuidance(command));
|
|
182693
182533
|
}
|
|
182694
182534
|
return projectRoot;
|
|
182695
182535
|
}
|
|
@@ -182965,8 +182805,7 @@ function planUpgrade(input) {
|
|
|
182965
182805
|
pending,
|
|
182966
182806
|
messages,
|
|
182967
182807
|
nextSteps: buildNextSteps({ mode, cli, options: input.options, pending }),
|
|
182968
|
-
requiresConfirmation: mode === "apply" && !input.options.dryRun && !input.options.yes && operations.some((operation) => operation.action === "overwrite" || operation.action === "remove")
|
|
182969
|
-
exitCode: 0
|
|
182808
|
+
requiresConfirmation: mode === "apply" && !input.options.dryRun && !input.options.yes && operations.some((operation) => operation.action === "overwrite" || operation.action === "remove")
|
|
182970
182809
|
};
|
|
182971
182810
|
}
|
|
182972
182811
|
function planOperations(input) {
|
|
@@ -183149,7 +182988,7 @@ function runUpgrade(options, io, initRuntime, runtime) {
|
|
|
183149
182988
|
});
|
|
183150
182989
|
if (plan.mode === "check" || options.dryRun) {
|
|
183151
182990
|
io.stdout(formatUpgradePlan(plan));
|
|
183152
|
-
return
|
|
182991
|
+
return 0;
|
|
183153
182992
|
}
|
|
183154
182993
|
return applyUpgrade(projectRoot, packageRoot, plan, io, initRuntime);
|
|
183155
182994
|
}
|
|
@@ -183167,7 +183006,7 @@ function applyUpgrade(projectRoot, packageRoot, plan, io, initRuntime) {
|
|
|
183167
183006
|
applySkillOperation(projectRoot, packageRoot, operation);
|
|
183168
183007
|
}
|
|
183169
183008
|
io.stdout(formatUpgradePlan(plan));
|
|
183170
|
-
return
|
|
183009
|
+
return 0;
|
|
183171
183010
|
}
|
|
183172
183011
|
function parseAgentTarget(value) {
|
|
183173
183012
|
if (value === undefined) {
|
|
@@ -183192,6 +183031,31 @@ function resolveProjectRoot2(root) {
|
|
|
183192
183031
|
return projectRoot;
|
|
183193
183032
|
}
|
|
183194
183033
|
|
|
183034
|
+
// src/cli/worker-signals.ts
|
|
183035
|
+
async function killingWorkersOnSignal(scan) {
|
|
183036
|
+
const signals = process.platform === "win32" ? ["SIGINT", "SIGTERM"] : ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
183037
|
+
const handlers = new Map;
|
|
183038
|
+
const remove = () => {
|
|
183039
|
+
for (const [signal, handler] of handlers) {
|
|
183040
|
+
process.off(signal, handler);
|
|
183041
|
+
}
|
|
183042
|
+
};
|
|
183043
|
+
for (const signal of signals) {
|
|
183044
|
+
const handler = () => {
|
|
183045
|
+
killRunningWorkers();
|
|
183046
|
+
remove();
|
|
183047
|
+
process.kill(process.pid, signal);
|
|
183048
|
+
};
|
|
183049
|
+
handlers.set(signal, handler);
|
|
183050
|
+
process.on(signal, handler);
|
|
183051
|
+
}
|
|
183052
|
+
try {
|
|
183053
|
+
return await scan();
|
|
183054
|
+
} finally {
|
|
183055
|
+
remove();
|
|
183056
|
+
}
|
|
183057
|
+
}
|
|
183058
|
+
|
|
183195
183059
|
// src/cli/index.ts
|
|
183196
183060
|
var VERSION = package_default.version;
|
|
183197
183061
|
function parseCheckOptions(args) {
|
|
@@ -183219,9 +183083,9 @@ function resolveProjectRoot3(root, command) {
|
|
|
183219
183083
|
}
|
|
183220
183084
|
return projectRoot;
|
|
183221
183085
|
}
|
|
183222
|
-
function runCheck(options, io) {
|
|
183086
|
+
async function runCheck(options, io) {
|
|
183223
183087
|
const projectRoot = resolveProjectRoot3(options.root, "check");
|
|
183224
|
-
const result = check({ projectRoot, audit: options.audit });
|
|
183088
|
+
const result = await killingWorkersOnSignal(() => check({ projectRoot, audit: options.audit }));
|
|
183225
183089
|
if (options.json) {
|
|
183226
183090
|
io.stdout(`${JSON.stringify(result, null, 2)}
|
|
183227
183091
|
`);
|
|
@@ -183242,7 +183106,7 @@ See \`docbridge docs show troubleshooting\` for diagnostic codes and fixes.
|
|
|
183242
183106
|
}
|
|
183243
183107
|
return result.summary.errors > 0 ? 1 : 0;
|
|
183244
183108
|
}
|
|
183245
|
-
function runFileCommand(command, options, io, execute, render) {
|
|
183109
|
+
async function runFileCommand(command, options, io, execute, render) {
|
|
183246
183110
|
const projectRoot = resolveProjectRoot3(options.root, command);
|
|
183247
183111
|
const inputFiles = [...options.files];
|
|
183248
183112
|
if (options.stdin) {
|
|
@@ -183250,7 +183114,7 @@ function runFileCommand(command, options, io, execute, render) {
|
|
|
183250
183114
|
inputFiles.push(...readStdin().split(`
|
|
183251
183115
|
`));
|
|
183252
183116
|
}
|
|
183253
|
-
const outcome = execute(projectRoot, inputFiles);
|
|
183117
|
+
const outcome = await killingWorkersOnSignal(async () => execute(projectRoot, inputFiles));
|
|
183254
183118
|
if (!outcome.ok) {
|
|
183255
183119
|
throw new DiagnosticOutputError(outcome.diagnostics.map(formatDiagnostic).join(`
|
|
183256
183120
|
`));
|
|
@@ -183271,7 +183135,7 @@ function writeStructuredResult(result, options) {
|
|
|
183271
183135
|
`);
|
|
183272
183136
|
}
|
|
183273
183137
|
}
|
|
183274
|
-
function runRelated(options, io) {
|
|
183138
|
+
async function runRelated(options, io) {
|
|
183275
183139
|
if (!options.stdin && options.files.length === 0) {
|
|
183276
183140
|
throw new CliError("No input files were provided.", missingInputGuidance("related"));
|
|
183277
183141
|
}
|
|
@@ -183300,7 +183164,7 @@ function runRelated(options, io) {
|
|
|
183300
183164
|
return 0;
|
|
183301
183165
|
});
|
|
183302
183166
|
}
|
|
183303
|
-
function runContext(options, io) {
|
|
183167
|
+
async function runContext(options, io) {
|
|
183304
183168
|
if (!options.stdin && options.files.length === 0) {
|
|
183305
183169
|
throw new CliError("No input files were provided.", missingInputGuidance("context"));
|
|
183306
183170
|
}
|
|
@@ -183314,7 +183178,7 @@ function runContext(options, io) {
|
|
|
183314
183178
|
return 0;
|
|
183315
183179
|
});
|
|
183316
183180
|
}
|
|
183317
|
-
function runGraph(options, io) {
|
|
183181
|
+
async function runGraph(options, io) {
|
|
183318
183182
|
return runFileCommand("graph", options, io, (projectRoot, inputFiles) => graph({ projectRoot, inputFiles, includeContent: options.includeContent }), (result, projectRoot, inputFiles) => {
|
|
183319
183183
|
const normalizedInputFiles = normalizeChangedPaths(projectRoot, inputFiles);
|
|
183320
183184
|
writeStructuredResult(result, {
|
|
@@ -183368,7 +183232,7 @@ var COMMAND_HANDLERS = {
|
|
|
183368
183232
|
return 0;
|
|
183369
183233
|
}
|
|
183370
183234
|
};
|
|
183371
|
-
function run(argv, io = {
|
|
183235
|
+
async function run(argv, io = {
|
|
183372
183236
|
stdout: (text) => process.stdout.write(text),
|
|
183373
183237
|
stderr: (text) => process.stderr.write(text)
|
|
183374
183238
|
}, initRuntime = { prompts: createDefaultPrompts() }, cliRuntime = {}) {
|
|
@@ -183390,7 +183254,7 @@ function run(argv, io = {
|
|
|
183390
183254
|
return 0;
|
|
183391
183255
|
}
|
|
183392
183256
|
if (isSubcommand(command)) {
|
|
183393
|
-
const exitCode = COMMAND_HANDLERS[command](rest, io, initRuntime, cliRuntime);
|
|
183257
|
+
const exitCode = await COMMAND_HANDLERS[command](rest, io, initRuntime, cliRuntime);
|
|
183394
183258
|
writeUpdateNotice(argv, io, initRuntime, cliRuntime);
|
|
183395
183259
|
return exitCode;
|
|
183396
183260
|
}
|
|
@@ -183455,7 +183319,7 @@ if (__require.main == __require.module) {
|
|
|
183455
183319
|
const argv = process.argv.slice(2);
|
|
183456
183320
|
const isTty = process.stderr.isTTY === true;
|
|
183457
183321
|
const latest = await resolveLatestForInvocation(argv, process.env, isTty);
|
|
183458
|
-
process.exitCode = run(argv, {
|
|
183322
|
+
process.exitCode = await run(argv, {
|
|
183459
183323
|
stdout: (text) => process.stdout.write(text),
|
|
183460
183324
|
stderr: (text) => process.stderr.write(text)
|
|
183461
183325
|
}, { prompts: createDefaultPrompts() }, { ...latest !== undefined ? { latest } : {}, env: process.env, isTty });
|
|
Binary file
|
package/docs/README.md
CHANGED
|
@@ -63,7 +63,7 @@ with the specification for the interface whose exact contract you need:
|
|
|
63
63
|
- [Commit messages](contributing/commits.md)
|
|
64
64
|
- [Pull requests](contributing/pull-requests.md)
|
|
65
65
|
- [Releasing](contributing/releasing.md)
|
|
66
|
-
- [Self-audit
|
|
66
|
+
- [Self-audit](contributing/self-audit.md)
|
|
67
67
|
|
|
68
68
|
## Historical records
|
|
69
69
|
|