docbridge 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -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 +183 -330
- package/dist/workers/java/docbridge-java-scanner.jar +0 -0
- package/docs/README.md +1 -1
- package/docs/user/linking.md +3 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.14.0] - 2026-10-08
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- A heading anchor keeps `_`, as GitHub's heading slugs do. Before, `_` became
|
|
15
|
+
`-`, so `### title_definitions` gave `#title-definitions`; it now gives
|
|
16
|
+
`#title_definitions`. A link written with the old `-` form now produces
|
|
17
|
+
`doc_anchor_not_found`; change it to match the heading, such as
|
|
18
|
+
`#title_definitions`.
|
|
19
|
+
|
|
20
|
+
## [0.13.1] - 2026-10-06
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
|
|
24
|
+
- A worker that a scan started no longer outlives the CLI. On POSIX systems
|
|
25
|
+
each worker runs in its own process group, a time or output limit kills the
|
|
26
|
+
whole group, and `check`, `related`, `context`, and `graph` kill their
|
|
27
|
+
running workers when the CLI receives `SIGINT`, `SIGTERM`, or `SIGHUP`
|
|
28
|
+
during a scan, then end by that signal. Before, a worker that ran past the
|
|
29
|
+
CLI, or a process the worker started, could keep running.
|
|
30
|
+
|
|
10
31
|
## [0.13.0] - 2026-10-01
|
|
11
32
|
|
|
12
33
|
### Added
|
|
@@ -547,7 +568,9 @@ Initial release of the SpecLink CLI.
|
|
|
547
568
|
- `speclink check` command with `--root`, `--json`, and `--audit` options.
|
|
548
569
|
- `speclink --version` (alias `-v`) and `speclink --help` (alias `-h`).
|
|
549
570
|
|
|
550
|
-
[Unreleased]: https://github.com/salan70/docbridge/compare/v0.
|
|
571
|
+
[Unreleased]: https://github.com/salan70/docbridge/compare/v0.14.0...HEAD
|
|
572
|
+
[0.14.0]: https://github.com/salan70/docbridge/releases/tag/v0.14.0
|
|
573
|
+
[0.13.1]: https://github.com/salan70/docbridge/releases/tag/v0.13.1
|
|
551
574
|
[0.13.0]: https://github.com/salan70/docbridge/releases/tag/v0.13.0
|
|
552
575
|
[0.12.0]: https://github.com/salan70/docbridge/releases/tag/v0.12.0
|
|
553
576
|
[0.11.0]: https://github.com/salan70/docbridge/releases/tag/v0.11.0
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
2db42ddea8060f988cea4933696e73d652ae5b807a8e8ff00249375faf2b0be4 docbridge-swift-scanner
|
|
2
|
+
f3da86ea5a0c1c92fa1e1f077251bec490e294d257b09cd03a46c8ce382aad19 docbridge_dart_scanner
|
|
3
3
|
7fc3e6842b3e1bf425b8fd63e241eee7e64875f16be7ba8d1ae4a25baa7f2338 docbridge-rust-scanner
|
|
4
|
-
|
|
4
|
+
1646b06856b0420acdcb82d1692b7afea83fe2b7666adeb6412c99782dfc8cae docbridge-go-scanner
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
0c023cfacede26a217a62420f9da1206b93c228122a3685469685a3db89f7735 docbridge-swift-scanner
|
|
2
2
|
b75b14006ca792ed3ca8faf7ceee9e4d33c895f27dd416b640818cf8a4bdd9c8 docbridge_dart_scanner
|
|
3
3
|
e0da7b8cc6ee23704f5fe8514cc309f0bb60380c77b55e3bc5ed113abd1f3478 docbridge-rust-scanner
|
|
4
|
-
|
|
4
|
+
5b8aee5b85a38679875c473b2dc32f021f022cba242785bb0fd94ee6fda0a6bf 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: 0c023cfacede26a217a62420f9da1206b93c228122a3685469685a3db89f7735
|
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.
|
|
175584
|
+
version: "0.14.0",
|
|
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(", ")}.`));
|
|
@@ -177601,24 +177597,13 @@ function manifestLink(source, target, entry, side) {
|
|
|
177601
177597
|
}
|
|
177602
177598
|
|
|
177603
177599
|
// src/shared/cancelable.ts
|
|
177604
|
-
function deferred() {
|
|
177605
|
-
let settle;
|
|
177606
|
-
const promise = new Promise((resolve2, reject) => {
|
|
177607
|
-
settle = { resolve: resolve2, reject };
|
|
177608
|
-
});
|
|
177609
|
-
return {
|
|
177610
|
-
promise,
|
|
177611
|
-
resolve: (value) => settle?.resolve(value),
|
|
177612
|
-
reject: (error) => settle?.reject(error)
|
|
177613
|
-
};
|
|
177614
|
-
}
|
|
177615
177600
|
function settledCancelable(value) {
|
|
177616
177601
|
return { promise: Promise.resolve(value), cancel: () => {
|
|
177617
177602
|
return;
|
|
177618
177603
|
} };
|
|
177619
177604
|
}
|
|
177620
177605
|
function mapCancelable(task, map) {
|
|
177621
|
-
const mapped =
|
|
177606
|
+
const mapped = Promise.withResolvers();
|
|
177622
177607
|
let cancelled = false;
|
|
177623
177608
|
task.promise.then((value) => {
|
|
177624
177609
|
if (cancelled) {
|
|
@@ -177644,7 +177629,7 @@ function isAbortError(error) {
|
|
|
177644
177629
|
return error instanceof Error && error.name === "AbortError";
|
|
177645
177630
|
}
|
|
177646
177631
|
function cancelableSequence(body) {
|
|
177647
|
-
const outcome =
|
|
177632
|
+
const outcome = Promise.withResolvers();
|
|
177648
177633
|
let cancelled = false;
|
|
177649
177634
|
let running;
|
|
177650
177635
|
const step = async (next) => {
|
|
@@ -177779,9 +177764,6 @@ function duplicateLinkDiagnostic(source, target, location, range) {
|
|
|
177779
177764
|
// src/scan/code/typescript.ts
|
|
177780
177765
|
var typeScriptAdapter = {
|
|
177781
177766
|
language: "typescript",
|
|
177782
|
-
scanFile(filePath, content, options) {
|
|
177783
|
-
return scanTypeScript(filePath, content, options);
|
|
177784
|
-
},
|
|
177785
177767
|
scanFiles(files, options) {
|
|
177786
177768
|
return files.map(({ filePath, content }) => scanTypeScript(filePath, content, options));
|
|
177787
177769
|
}
|
|
@@ -178250,12 +178232,9 @@ function firstToken(text) {
|
|
|
178250
178232
|
import { accessSync as accessSync2, constants as constants2, existsSync as existsSync3, realpathSync as realpathSync2, statSync as statSync2 } from "node:fs";
|
|
178251
178233
|
import { extname, isAbsolute as isAbsolute2, join as join7, resolve as resolve2 } from "node:path";
|
|
178252
178234
|
|
|
178253
|
-
// src/scan/code/worker/runtime-probe.ts
|
|
178254
|
-
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
178255
|
-
|
|
178256
178235
|
// src/scan/code/worker/scanner-worker.ts
|
|
178257
178236
|
var import__2020 = __toESM(require_2020(), 1);
|
|
178258
|
-
import { spawn
|
|
178237
|
+
import { spawn } from "node:child_process";
|
|
178259
178238
|
import { mkdirSync } from "node:fs";
|
|
178260
178239
|
import { tmpdir } from "node:os";
|
|
178261
178240
|
import { dirname, join as join4 } from "node:path";
|
|
@@ -178454,14 +178433,12 @@ var scanner_worker_schema_default = {
|
|
|
178454
178433
|
// src/scan/code/worker/scanner-worker.ts
|
|
178455
178434
|
var WORKER_OUTPUT_LIMIT_BYTES = 1024 * 1024 * 1024;
|
|
178456
178435
|
var KILLED_WORKER_GRACE_MS = 500;
|
|
178457
|
-
|
|
178458
|
-
|
|
178459
|
-
|
|
178460
|
-
|
|
178461
|
-
|
|
178462
|
-
};
|
|
178436
|
+
var liveWorkers = new Map;
|
|
178437
|
+
var compiledResponseValidator;
|
|
178438
|
+
function responseValidator() {
|
|
178439
|
+
compiledResponseValidator ??= compileWorkerResponseValidator();
|
|
178440
|
+
return compiledResponseValidator;
|
|
178463
178441
|
}
|
|
178464
|
-
var responseValidator = createLazyWorkerResponseValidator(compileWorkerResponseValidator);
|
|
178465
178442
|
function compileWorkerResponseValidator() {
|
|
178466
178443
|
const ajv = new import__2020.default({ allErrors: true, strict: true });
|
|
178467
178444
|
ajv.addSchema(common_output_schema_default);
|
|
@@ -178472,13 +178449,10 @@ function compileWorkerResponseValidator() {
|
|
|
178472
178449
|
});
|
|
178473
178450
|
}
|
|
178474
178451
|
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));
|
|
178452
|
+
const task = run(processInput(request, command, stripEnv));
|
|
178479
178453
|
return {
|
|
178480
|
-
promise:
|
|
178481
|
-
cancel: () =>
|
|
178454
|
+
promise: task.promise.then((processResult) => interpretWorkerProcess(request, command, processResult)),
|
|
178455
|
+
cancel: () => task.cancel()
|
|
178482
178456
|
};
|
|
178483
178457
|
}
|
|
178484
178458
|
function processInput(request, command, stripEnv) {
|
|
@@ -178554,37 +178528,7 @@ function workerTimeoutMs(fileCount) {
|
|
|
178554
178528
|
return 30000 + 1000 * fileCount;
|
|
178555
178529
|
}
|
|
178556
178530
|
function runScannerWorkerProcess(input) {
|
|
178557
|
-
const
|
|
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
|
-
const run = deferred();
|
|
178531
|
+
const run = Promise.withResolvers();
|
|
178588
178532
|
let settled = false;
|
|
178589
178533
|
let timer;
|
|
178590
178534
|
let grace;
|
|
@@ -178593,6 +178537,9 @@ function runScannerWorkerProcessAsync(input) {
|
|
|
178593
178537
|
return;
|
|
178594
178538
|
}
|
|
178595
178539
|
settled = true;
|
|
178540
|
+
if (child !== undefined) {
|
|
178541
|
+
liveWorkers.delete(child);
|
|
178542
|
+
}
|
|
178596
178543
|
clearTimeout(timer);
|
|
178597
178544
|
clearTimeout(grace);
|
|
178598
178545
|
complete();
|
|
@@ -178601,24 +178548,34 @@ function runScannerWorkerProcessAsync(input) {
|
|
|
178601
178548
|
let child;
|
|
178602
178549
|
try {
|
|
178603
178550
|
const [executable = "", ...args] = input.command;
|
|
178604
|
-
child = spawn(executable, args, {
|
|
178551
|
+
child = spawn(executable, args, {
|
|
178552
|
+
env: workerProcessEnv(input.stripEnv),
|
|
178553
|
+
detached: ownGroup,
|
|
178554
|
+
windowsHide: true
|
|
178555
|
+
});
|
|
178605
178556
|
} catch (error) {
|
|
178606
178557
|
settle(() => run.resolve({ ok: false, kind: "start", error, stderr: "" }));
|
|
178607
178558
|
return { promise: run.promise, cancel: () => {
|
|
178608
178559
|
return;
|
|
178609
178560
|
} };
|
|
178610
178561
|
}
|
|
178562
|
+
liveWorkers.set(child, ownGroup);
|
|
178611
178563
|
const maxOutputBytes = input.maxOutputBytes ?? WORKER_OUTPUT_LIMIT_BYTES;
|
|
178612
178564
|
const budget = { bytes: 0, limit: maxOutputBytes };
|
|
178613
178565
|
const stdout = new OutputCollector(budget);
|
|
178614
178566
|
const stderr = new OutputCollector(budget);
|
|
178615
178567
|
let failure;
|
|
178616
|
-
|
|
178568
|
+
let cause;
|
|
178569
|
+
let exited = false;
|
|
178570
|
+
const kill = (reason, why) => {
|
|
178617
178571
|
failure ??= reason;
|
|
178618
|
-
|
|
178572
|
+
cause ??= why;
|
|
178573
|
+
if (!exited) {
|
|
178574
|
+
killWorker(child, ownGroup);
|
|
178575
|
+
}
|
|
178619
178576
|
grace ??= setTimeout(() => {
|
|
178620
178577
|
closeStreams(child);
|
|
178621
|
-
settle(() => run.resolve(closedProcessResult(null, null, failure, stdout, stderr)));
|
|
178578
|
+
settle(() => run.resolve(closedProcessResult(null, null, failure, cause, stdout, stderr)));
|
|
178622
178579
|
}, KILLED_WORKER_GRACE_MS);
|
|
178623
178580
|
};
|
|
178624
178581
|
let spawned = false;
|
|
@@ -178634,30 +178591,36 @@ function runScannerWorkerProcessAsync(input) {
|
|
|
178634
178591
|
});
|
|
178635
178592
|
child.stdout.on("data", (chunk) => {
|
|
178636
178593
|
if (!stdout.add(chunk)) {
|
|
178637
|
-
kill(outputLimitError(maxOutputBytes));
|
|
178594
|
+
kill(outputLimitError(maxOutputBytes), { type: "output-limit" });
|
|
178638
178595
|
}
|
|
178639
178596
|
});
|
|
178640
178597
|
child.stderr.on("data", (chunk) => {
|
|
178641
178598
|
if (!stderr.add(chunk)) {
|
|
178642
|
-
kill(outputLimitError(maxOutputBytes));
|
|
178599
|
+
kill(outputLimitError(maxOutputBytes), { type: "output-limit" });
|
|
178643
178600
|
}
|
|
178644
178601
|
});
|
|
178645
178602
|
child.stdin.on("error", () => {
|
|
178646
178603
|
return;
|
|
178647
178604
|
});
|
|
178605
|
+
child.on("exit", () => {
|
|
178606
|
+
exited = true;
|
|
178607
|
+
liveWorkers.delete(child);
|
|
178608
|
+
});
|
|
178648
178609
|
child.on("close", (code, signal) => {
|
|
178649
|
-
settle(() => run.resolve(closedProcessResult(code, signal, failure, stdout, stderr)));
|
|
178610
|
+
settle(() => run.resolve(closedProcessResult(code, signal, failure, cause, stdout, stderr)));
|
|
178650
178611
|
});
|
|
178651
178612
|
child.stdin.end(input.stdin);
|
|
178652
178613
|
const { timeoutMs } = input;
|
|
178653
178614
|
if (timeoutMs !== undefined) {
|
|
178654
|
-
timer = setTimeout(() => kill(timeoutError(timeoutMs)), timeoutMs);
|
|
178615
|
+
timer = setTimeout(() => kill(timeoutError(timeoutMs), { type: "timeout" }), timeoutMs);
|
|
178655
178616
|
}
|
|
178656
178617
|
return {
|
|
178657
178618
|
promise: run.promise,
|
|
178658
178619
|
cancel() {
|
|
178659
178620
|
settle(() => {
|
|
178660
|
-
|
|
178621
|
+
if (!exited) {
|
|
178622
|
+
killWorker(child, ownGroup);
|
|
178623
|
+
}
|
|
178661
178624
|
closeStreams(child);
|
|
178662
178625
|
run.reject(abortError());
|
|
178663
178626
|
});
|
|
@@ -178697,39 +178660,36 @@ class OutputCollector {
|
|
|
178697
178660
|
return Buffer.concat(this.chunks).toString("utf8");
|
|
178698
178661
|
}
|
|
178699
178662
|
}
|
|
178700
|
-
function closedProcessResult(code, signal, failure, stdout, stderr) {
|
|
178663
|
+
function closedProcessResult(code, signal, failure, cause, stdout, stderr) {
|
|
178701
178664
|
if (failure !== undefined) {
|
|
178702
|
-
return {
|
|
178665
|
+
return {
|
|
178666
|
+
ok: false,
|
|
178667
|
+
kind: "execution",
|
|
178668
|
+
error: failure,
|
|
178669
|
+
...cause === undefined ? {} : { cause },
|
|
178670
|
+
stderr: stderr.text()
|
|
178671
|
+
};
|
|
178703
178672
|
}
|
|
178704
178673
|
if (code === null) {
|
|
178705
|
-
return {
|
|
178674
|
+
return {
|
|
178675
|
+
ok: false,
|
|
178676
|
+
kind: "execution",
|
|
178677
|
+
error: signalError(signal),
|
|
178678
|
+
cause: { type: "signal", signal: signal ?? "unknown" },
|
|
178679
|
+
stderr: stderr.text()
|
|
178680
|
+
};
|
|
178706
178681
|
}
|
|
178707
178682
|
return { ok: true, exitCode: code, stdout: stdout.text(), stderr: stderr.text() };
|
|
178708
178683
|
}
|
|
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
178684
|
function signalError(signal) {
|
|
178723
178685
|
const name = signal ?? "unknown";
|
|
178724
|
-
return
|
|
178686
|
+
return new Error(`worker terminated by signal ${name}`);
|
|
178725
178687
|
}
|
|
178726
178688
|
function timeoutError(timeoutMs) {
|
|
178727
|
-
return
|
|
178728
|
-
code: "ETIMEDOUT"
|
|
178729
|
-
});
|
|
178689
|
+
return new Error(`worker timed out after ${timeoutMs} ms`);
|
|
178730
178690
|
}
|
|
178731
178691
|
function outputLimitError(maxOutputBytes) {
|
|
178732
|
-
return
|
|
178692
|
+
return new Error(`worker wrote more than ${maxOutputBytes} bytes to stdout and stderr together`);
|
|
178733
178693
|
}
|
|
178734
178694
|
function validateWorkerResponse(value, request) {
|
|
178735
178695
|
const validator = responseValidator();
|
|
@@ -178757,18 +178717,7 @@ function formatSchemaError(errors) {
|
|
|
178757
178717
|
return `worker response does not match scanner-worker.schema.json at ${path}: ${error.message ?? "invalid value"}`;
|
|
178758
178718
|
}
|
|
178759
178719
|
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);
|
|
178720
|
+
return responseFiles.length === requestFiles.length && responseFiles.every((file, index) => file.filePath === requestFiles[index]?.filePath);
|
|
178772
178721
|
}
|
|
178773
178722
|
function scannerUnavailableDiagnostic(language, error, executable) {
|
|
178774
178723
|
const label = languageLabel(language);
|
|
@@ -178807,20 +178756,17 @@ function scannerFailedDiagnostic(language, reason) {
|
|
|
178807
178756
|
function languageLabel(language) {
|
|
178808
178757
|
return language.charAt(0).toUpperCase() + language.slice(1);
|
|
178809
178758
|
}
|
|
178759
|
+
function killRunningWorkers() {
|
|
178760
|
+
for (const [child, ownGroup] of liveWorkers) {
|
|
178761
|
+
killWorker(child, ownGroup);
|
|
178762
|
+
}
|
|
178763
|
+
}
|
|
178810
178764
|
|
|
178811
178765
|
// src/scan/code/worker/runtime-probe.ts
|
|
178812
178766
|
var PROBE_TIMEOUT_MS = 1e4;
|
|
178813
178767
|
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({
|
|
178768
|
+
function probeRuntime(command, stripEnv, run = runScannerWorkerProcess) {
|
|
178769
|
+
const task = run({
|
|
178824
178770
|
command: [...command, "--probe"],
|
|
178825
178771
|
stdin: "",
|
|
178826
178772
|
stripEnv,
|
|
@@ -178828,49 +178774,34 @@ function probeRuntimeAsync(command, stripEnv, runAsync = runScannerWorkerProcess
|
|
|
178828
178774
|
maxOutputBytes: PROBE_MAX_OUTPUT_BYTES
|
|
178829
178775
|
});
|
|
178830
178776
|
return {
|
|
178831
|
-
promise:
|
|
178832
|
-
cancel: () =>
|
|
178777
|
+
promise: task.promise.then(classifyProbe),
|
|
178778
|
+
cancel: () => task.cancel()
|
|
178833
178779
|
};
|
|
178834
178780
|
}
|
|
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
178781
|
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` };
|
|
178782
|
+
if (result.ok) {
|
|
178783
|
+
if (result.exitCode !== 0) {
|
|
178784
|
+
const detail = firstLine(result.stderr);
|
|
178785
|
+
return {
|
|
178786
|
+
kind: "failed",
|
|
178787
|
+
reason: `probe exited with status ${result.exitCode}${detail === "" ? "" : `: ${detail}`}`
|
|
178788
|
+
};
|
|
178789
|
+
}
|
|
178790
|
+
return parseProbeOutput(result.stdout);
|
|
178866
178791
|
}
|
|
178867
|
-
|
|
178868
|
-
|
|
178869
|
-
kind: "failed",
|
|
178870
|
-
|
|
178871
|
-
|
|
178792
|
+
switch (result.cause?.type) {
|
|
178793
|
+
case "timeout":
|
|
178794
|
+
return { kind: "failed", reason: `probe did not finish within ${PROBE_TIMEOUT_MS / 1000} s` };
|
|
178795
|
+
case "output-limit":
|
|
178796
|
+
return {
|
|
178797
|
+
kind: "failed",
|
|
178798
|
+
reason: `probe printed more than ${PROBE_MAX_OUTPUT_BYTES / 1024} KiB`
|
|
178799
|
+
};
|
|
178800
|
+
case "signal":
|
|
178801
|
+
return { kind: "failed", reason: `probe terminated by signal ${result.cause.signal}` };
|
|
178802
|
+
default:
|
|
178803
|
+
return { kind: "unstartable", reason: reasonOf(result.error) };
|
|
178872
178804
|
}
|
|
178873
|
-
return { kind: "unstartable", reason: reasonOf(error) };
|
|
178874
178805
|
}
|
|
178875
178806
|
function parseProbeOutput(stdout) {
|
|
178876
178807
|
const text = stdout.trim();
|
|
@@ -178880,7 +178811,7 @@ function parseProbeOutput(stdout) {
|
|
|
178880
178811
|
} catch {
|
|
178881
178812
|
return malformed(text);
|
|
178882
178813
|
}
|
|
178883
|
-
if (!
|
|
178814
|
+
if (!isRecord(value)) {
|
|
178884
178815
|
return malformed(text);
|
|
178885
178816
|
}
|
|
178886
178817
|
if (value.ok === true && typeof value.runtime === "string" && typeof value.version === "string") {
|
|
@@ -178898,40 +178829,9 @@ function malformed(text) {
|
|
|
178898
178829
|
function firstLine(text) {
|
|
178899
178830
|
return text.trim().split(/\r?\n/u)[0] ?? "";
|
|
178900
178831
|
}
|
|
178901
|
-
function
|
|
178832
|
+
function isRecord(value) {
|
|
178902
178833
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
178903
178834
|
}
|
|
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
178835
|
|
|
178936
178836
|
// src/scan/code/worker/scanner-executable.ts
|
|
178937
178837
|
import { accessSync, chmodSync, constants, existsSync as existsSync2, statSync } from "node:fs";
|
|
@@ -179143,22 +179043,12 @@ function rememberProbe(key, generation, outcome) {
|
|
|
179143
179043
|
function resolveRuntimeWorkerCommand(language, options) {
|
|
179144
179044
|
const env = options.env ?? process.env;
|
|
179145
179045
|
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
179046
|
return cancelableSequence(async (run) => {
|
|
179157
179047
|
const steps = resolutionSteps(language, options, env);
|
|
179158
179048
|
let step = steps.next();
|
|
179159
179049
|
while (step.done !== true) {
|
|
179160
179050
|
const request = step.value;
|
|
179161
|
-
step = steps.next(await run(() =>
|
|
179051
|
+
step = steps.next(await run(() => cachedProbe(request, env, probe)));
|
|
179162
179052
|
}
|
|
179163
179053
|
return step.value;
|
|
179164
179054
|
});
|
|
@@ -179266,24 +179156,13 @@ function isExecutableFile(path, platform) {
|
|
|
179266
179156
|
}
|
|
179267
179157
|
}
|
|
179268
179158
|
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
179159
|
const key = probeCacheKey(command, env);
|
|
179281
179160
|
const cached = probeCache.get(key);
|
|
179282
179161
|
if (cached !== undefined) {
|
|
179283
179162
|
return settledCancelable(cached);
|
|
179284
179163
|
}
|
|
179285
179164
|
const generation = probeCacheGeneration;
|
|
179286
|
-
return mapCancelable(
|
|
179165
|
+
return mapCancelable(probe(command, stripEnv), (outcome) => {
|
|
179287
179166
|
rememberProbe(key, generation, outcome);
|
|
179288
179167
|
return outcome;
|
|
179289
179168
|
});
|
|
@@ -179339,16 +179218,12 @@ function failure(spec, language, code, reason) {
|
|
|
179339
179218
|
|
|
179340
179219
|
// src/scan/code/dispatch.ts
|
|
179341
179220
|
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));
|
|
179221
|
+
const resolve3 = (context) => typeof command === "function" ? settledCancelable(command(context)) : command.commandAsync(context);
|
|
179222
|
+
const prepare = (context) => mapCancelable(resolve3(context), (value) => boundWorkerAdapter(language, workerCommand(value), adapterOptions));
|
|
179345
179223
|
const scanBatchAsync = (files, options, context) => {
|
|
179346
179224
|
if (files.length === 0) {
|
|
179347
179225
|
return settledCancelable([]);
|
|
179348
179226
|
}
|
|
179349
|
-
if (commandAsync === undefined) {
|
|
179350
|
-
return bound(context).scanFilesAsync(files, options, context);
|
|
179351
|
-
}
|
|
179352
179227
|
return cancelableSequence(async (step) => {
|
|
179353
179228
|
const { adapter } = await step(() => prepare(context));
|
|
179354
179229
|
return step(() => adapter.scanFilesAsync(files, options, context));
|
|
@@ -179356,8 +179231,6 @@ function createScannerWorkerAdapter(language, command, adapterOptions = {}) {
|
|
|
179356
179231
|
};
|
|
179357
179232
|
return {
|
|
179358
179233
|
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
179234
|
scanFilesAsync: scanBatchAsync,
|
|
179362
179235
|
prepare
|
|
179363
179236
|
};
|
|
@@ -179367,20 +179240,6 @@ function boundWorkerAdapter(language, resolved, adapterOptions) {
|
|
|
179367
179240
|
const runtime = resolved.ok ? resolved.runtime : [];
|
|
179368
179241
|
const adapter = {
|
|
179369
179242
|
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
179243
|
scanFilesAsync(files, options, context) {
|
|
179385
179244
|
if (files.length === 0) {
|
|
179386
179245
|
return settledCancelable([]);
|
|
@@ -179388,36 +179247,28 @@ function boundWorkerAdapter(language, resolved, adapterOptions) {
|
|
|
179388
179247
|
if (!resolved.ok) {
|
|
179389
179248
|
return settledCancelable(failedBatch(language, files, resolved.diagnostic));
|
|
179390
179249
|
}
|
|
179391
|
-
const task =
|
|
179250
|
+
const task = invokeScannerWorker(workerRequest(language, files, options, context, adapterOptions), resolved.command, adapterOptions.run, resolved.stripEnv);
|
|
179392
179251
|
return mapCancelable(task, (result) => result.ok ? result.codeFiles : failedBatch(language, files, result.diagnostic));
|
|
179393
179252
|
},
|
|
179394
179253
|
prepare: () => settledCancelable({ argv, runtime, adapter })
|
|
179395
179254
|
};
|
|
179396
179255
|
return { argv, runtime, adapter };
|
|
179397
179256
|
}
|
|
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
179257
|
function scanContext(projectRoot, options) {
|
|
179408
179258
|
return options.scanners === undefined ? { projectRoot } : { projectRoot, scanners: options.scanners };
|
|
179409
179259
|
}
|
|
179410
|
-
function
|
|
179260
|
+
function scanCodeFiles(projectRoot, files, codeInclude, read, options = {}) {
|
|
179411
179261
|
const plan = planCodeScan(files, codeInclude, read, options.onContent);
|
|
179412
179262
|
const context = scanContext(projectRoot, options);
|
|
179413
179263
|
return cancelableSequence(async (step) => {
|
|
179414
179264
|
const cache = new Map;
|
|
179415
179265
|
for (const batch of plan.batches) {
|
|
179416
179266
|
const base = options.adapters?.[batch.language] ?? builtInAdapters[batch.language];
|
|
179417
|
-
const
|
|
179418
|
-
const
|
|
179419
|
-
const
|
|
179420
|
-
|
|
179267
|
+
const { argv, runtime, adapter } = "scanFilesAsync" in base ? await step(() => base.prepare(context)) : { argv: [], adapter: base };
|
|
179268
|
+
const keys = options.cache === undefined ? [] : batch.files.map((file) => codeScanCacheKey(batch.language, file.filePath, file.content, batch.options, argv, runtime));
|
|
179269
|
+
const cached = options.cache === undefined ? batch.files.map(() => {
|
|
179270
|
+
return;
|
|
179271
|
+
}) : keys.map((key) => options.cache?.get(key));
|
|
179421
179272
|
const missing = batch.files.filter((_, index) => cached[index] === undefined);
|
|
179422
179273
|
const scanned = missing.length === 0 ? [] : await step(() => scanFilesAsync(adapter, missing, batch.options, context));
|
|
179423
179274
|
let next = 0;
|
|
@@ -179425,7 +179276,7 @@ function scanCodeFilesAsync(projectRoot, files, codeInclude, read, options = {})
|
|
|
179425
179276
|
fillBatch(plan, batch, results);
|
|
179426
179277
|
results.forEach((scan, index) => {
|
|
179427
179278
|
const key = keys[index];
|
|
179428
|
-
if (scan !== undefined && key !== undefined && isReusableScan(scan)) {
|
|
179279
|
+
if (options.cache !== undefined && scan !== undefined && key !== undefined && isReusableScan(scan)) {
|
|
179429
179280
|
cache.set(key, scan);
|
|
179430
179281
|
}
|
|
179431
179282
|
});
|
|
@@ -179434,7 +179285,7 @@ function scanCodeFilesAsync(projectRoot, files, codeInclude, read, options = {})
|
|
|
179434
179285
|
});
|
|
179435
179286
|
}
|
|
179436
179287
|
function scanFilesAsync(adapter, files, options, context) {
|
|
179437
|
-
return adapter.scanFilesAsync
|
|
179288
|
+
return "scanFilesAsync" in adapter ? adapter.scanFilesAsync(files, options, context) : settledCancelable(adapter.scanFiles(files, options, context));
|
|
179438
179289
|
}
|
|
179439
179290
|
var builtInAdapters = {
|
|
179440
179291
|
typescript: typeScriptAdapter,
|
|
@@ -179452,7 +179303,9 @@ function runtimeWorkerAdapter(language) {
|
|
|
179452
179303
|
const command = scanners?.[language]?.command;
|
|
179453
179304
|
return command === undefined ? { projectRoot } : { projectRoot, command };
|
|
179454
179305
|
};
|
|
179455
|
-
return createScannerWorkerAdapter(language,
|
|
179306
|
+
return createScannerWorkerAdapter(language, {
|
|
179307
|
+
commandAsync: (context) => resolveRuntimeWorkerCommand(language, resolution(context))
|
|
179308
|
+
});
|
|
179456
179309
|
}
|
|
179457
179310
|
function planCodeScan(files, codeInclude, read, onContent) {
|
|
179458
179311
|
const slots = [];
|
|
@@ -179631,7 +179484,7 @@ function stripClosingHashes(text) {
|
|
|
179631
179484
|
}
|
|
179632
179485
|
function toAnchor(headingText) {
|
|
179633
179486
|
const lowered = headingText.toLowerCase();
|
|
179634
|
-
const replaced = lowered.replace(/[^\p{L}\p{N}]+/gu, "-");
|
|
179487
|
+
const replaced = lowered.replace(/[^\p{L}\p{N}_]+/gu, "-");
|
|
179635
179488
|
return replaced.replace(/^-+/, "").replace(/-+$/, "");
|
|
179636
179489
|
}
|
|
179637
179490
|
function matchCodeComment(line, filePath, lineNumber) {
|
|
@@ -179773,37 +179626,28 @@ function flushDanglingWith(pending, diagnostics, message) {
|
|
|
179773
179626
|
// src/query/project-scan.ts
|
|
179774
179627
|
function scanProject(options) {
|
|
179775
179628
|
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
179629
|
if (!loaded.ok) {
|
|
179787
179630
|
return cancelableSequence(async () => loaded);
|
|
179788
179631
|
}
|
|
179789
179632
|
const { inputs } = loaded;
|
|
179790
|
-
const
|
|
179791
|
-
const
|
|
179792
|
-
|
|
179633
|
+
const previousCache = options.cache;
|
|
179634
|
+
const fingerprint = previousCache === undefined ? undefined : JSON.stringify(inputs.config);
|
|
179635
|
+
const sameConfiguration = previousCache !== undefined && previousCache.fingerprint === fingerprint;
|
|
179636
|
+
if (previousCache !== undefined && !sameConfiguration && previousCache.fingerprint !== "") {
|
|
179793
179637
|
options.onConfigurationChange?.();
|
|
179794
179638
|
}
|
|
179795
|
-
const codeScan =
|
|
179639
|
+
const codeScan = scanCodeFiles(options.projectRoot, inputs.codeFiles, inputs.config.include.code, inputs.readFile, {
|
|
179796
179640
|
...codeScanOptions(inputs, options),
|
|
179797
|
-
...
|
|
179641
|
+
...previousCache === undefined ? {} : { cache: sameConfiguration ? previousCache.entries : new Map }
|
|
179798
179642
|
});
|
|
179799
179643
|
const docReads = readDocFiles(inputs, options);
|
|
179800
179644
|
return cancelableSequence(async (step) => {
|
|
179801
179645
|
const { cache, ...code } = await step(codeScan);
|
|
179802
|
-
const scan = finishProjectScan(inputs, code, docReads,
|
|
179646
|
+
const scan = finishProjectScan(inputs, code, docReads, options.buildGraph);
|
|
179803
179647
|
return {
|
|
179804
179648
|
ok: true,
|
|
179805
179649
|
scan,
|
|
179806
|
-
cache: { fingerprint, entries: cache }
|
|
179650
|
+
cache: { fingerprint: fingerprint ?? "", entries: cache }
|
|
179807
179651
|
};
|
|
179808
179652
|
});
|
|
179809
179653
|
}
|
|
@@ -179908,19 +179752,12 @@ class Project {
|
|
|
179908
179752
|
this.overlay.delete(relPath);
|
|
179909
179753
|
this.overlayRevision += 1;
|
|
179910
179754
|
}
|
|
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
179755
|
resolveAsync() {
|
|
179921
179756
|
const revision = this.overlayRevision;
|
|
179922
|
-
const scan =
|
|
179757
|
+
const scan = scanProject({
|
|
179923
179758
|
...this.scanSources(new Map(this.overlay)),
|
|
179759
|
+
buildGraph: true,
|
|
179760
|
+
keepContent: true,
|
|
179924
179761
|
cache: this.cache,
|
|
179925
179762
|
onConfigurationChange: clearRuntimeProbeCache
|
|
179926
179763
|
});
|
|
@@ -180419,12 +180256,12 @@ function fullChangeText(changes) {
|
|
|
180419
180256
|
}
|
|
180420
180257
|
|
|
180421
180258
|
// src/query/check.ts
|
|
180422
|
-
function check(options) {
|
|
180259
|
+
async function check(options) {
|
|
180423
180260
|
const audit = options.audit ?? false;
|
|
180424
|
-
const outcome = scanProject({
|
|
180261
|
+
const outcome = await scanProject({
|
|
180425
180262
|
projectRoot: options.projectRoot,
|
|
180426
180263
|
...options.adapters === undefined ? {} : { adapters: options.adapters }
|
|
180427
|
-
});
|
|
180264
|
+
}).promise;
|
|
180428
180265
|
if (!outcome.ok) {
|
|
180429
180266
|
const sorted = sortDiagnostics(outcome.diagnostics);
|
|
180430
180267
|
return { diagnostics: sorted, summary: summarizeDiagnostics(sorted) };
|
|
@@ -180498,8 +180335,8 @@ function collectGateViolations(result) {
|
|
|
180498
180335
|
}
|
|
180499
180336
|
return violations;
|
|
180500
180337
|
}
|
|
180501
|
-
function related(options) {
|
|
180502
|
-
const outcome = scanProject({ projectRoot: options.projectRoot, buildGraph: true });
|
|
180338
|
+
async function related(options) {
|
|
180339
|
+
const outcome = await scanProject({ projectRoot: options.projectRoot, buildGraph: true }).promise;
|
|
180503
180340
|
if (!outcome.ok) {
|
|
180504
180341
|
return { ok: false, diagnostics: outcome.diagnostics };
|
|
180505
180342
|
}
|
|
@@ -180557,12 +180394,12 @@ function computeContext(graph, contentByFile, inputFiles) {
|
|
|
180557
180394
|
summary: { inputFiles: inputSet.size, contexts: contexts.length }
|
|
180558
180395
|
};
|
|
180559
180396
|
}
|
|
180560
|
-
function context(options) {
|
|
180561
|
-
const outcome = scanProject({
|
|
180397
|
+
async function context(options) {
|
|
180398
|
+
const outcome = await scanProject({
|
|
180562
180399
|
projectRoot: options.projectRoot,
|
|
180563
180400
|
buildGraph: true,
|
|
180564
180401
|
keepContent: true
|
|
180565
|
-
});
|
|
180402
|
+
}).promise;
|
|
180566
180403
|
if (!outcome.ok) {
|
|
180567
180404
|
return { ok: false, diagnostics: outcome.diagnostics };
|
|
180568
180405
|
}
|
|
@@ -180636,12 +180473,12 @@ function compareBlocks(left, right) {
|
|
|
180636
180473
|
}
|
|
180637
180474
|
|
|
180638
180475
|
// src/query/graph-output.ts
|
|
180639
|
-
function graph(options) {
|
|
180640
|
-
const outcome = scanProject({
|
|
180476
|
+
async function graph(options) {
|
|
180477
|
+
const outcome = await scanProject({
|
|
180641
180478
|
projectRoot: options.projectRoot,
|
|
180642
180479
|
buildGraph: true,
|
|
180643
180480
|
keepContent: true
|
|
180644
|
-
});
|
|
180481
|
+
}).promise;
|
|
180645
180482
|
if (!outcome.ok) {
|
|
180646
180483
|
return { ok: false, diagnostics: outcome.diagnostics };
|
|
180647
180484
|
}
|
|
@@ -181136,13 +180973,6 @@ class CliError extends Error {
|
|
|
181136
180973
|
}
|
|
181137
180974
|
}
|
|
181138
180975
|
|
|
181139
|
-
class InitCliError extends CliError {
|
|
181140
|
-
constructor(message, guidance) {
|
|
181141
|
-
super(message, guidance);
|
|
181142
|
-
this.name = "InitCliError";
|
|
181143
|
-
}
|
|
181144
|
-
}
|
|
181145
|
-
|
|
181146
180976
|
class DiagnosticOutputError extends Error {
|
|
181147
180977
|
constructor(message) {
|
|
181148
180978
|
super(message);
|
|
@@ -182101,7 +181931,6 @@ function readFileOrUndefined(path) {
|
|
|
182101
181931
|
}
|
|
182102
181932
|
|
|
182103
181933
|
// src/setup/init-plan.ts
|
|
182104
|
-
var CONFIG_FILE_NAME2 = "docbridge.config.json";
|
|
182105
181934
|
var INIT_SKILL_NAMES = ["docbridge"];
|
|
182106
181935
|
var LEGACY_SKILL_NAMES = [
|
|
182107
181936
|
"docbridge-adopt",
|
|
@@ -182269,7 +182098,7 @@ function planConfigOperations(input) {
|
|
|
182269
182098
|
return [
|
|
182270
182099
|
{
|
|
182271
182100
|
action,
|
|
182272
|
-
path:
|
|
182101
|
+
path: CONFIG_FILE_NAME,
|
|
182273
182102
|
content,
|
|
182274
182103
|
reason: "Create docbridge.config.json from discovered scope."
|
|
182275
182104
|
}
|
|
@@ -182421,7 +182250,7 @@ function resolveInitWithAgentTarget(discovery, options) {
|
|
|
182421
182250
|
return { target: discovery.agent.defaultTarget, error: undefined };
|
|
182422
182251
|
}
|
|
182423
182252
|
function readExistingConfig(projectRoot) {
|
|
182424
|
-
const configPath = join12(projectRoot,
|
|
182253
|
+
const configPath = join12(projectRoot, CONFIG_FILE_NAME);
|
|
182425
182254
|
let rawText;
|
|
182426
182255
|
try {
|
|
182427
182256
|
rawText = readFileSync7(configPath, "utf8");
|
|
@@ -182499,7 +182328,7 @@ function parseInitOptions(args, command) {
|
|
|
182499
182328
|
if (arg === "--root") {
|
|
182500
182329
|
const root = args[index + 1];
|
|
182501
182330
|
if (root === undefined) {
|
|
182502
|
-
throw new
|
|
182331
|
+
throw new CliError("--root requires a path.", rootPathGuidance(command));
|
|
182503
182332
|
}
|
|
182504
182333
|
options.root = root;
|
|
182505
182334
|
index += 1;
|
|
@@ -182508,22 +182337,22 @@ function parseInitOptions(args, command) {
|
|
|
182508
182337
|
if (arg === "--agent-target") {
|
|
182509
182338
|
const target = args[index + 1];
|
|
182510
182339
|
if (target === undefined) {
|
|
182511
|
-
throw new
|
|
182340
|
+
throw new CliError("--agent-target requires a value.", agentTargetGuidance(command));
|
|
182512
182341
|
}
|
|
182513
182342
|
if (!AGENT_TARGETS.has(target)) {
|
|
182514
|
-
throw new
|
|
182343
|
+
throw new CliError(`Unknown agent target: ${target}. Supported values: codex, claude, both${command === "init" ? ", none" : ""}.`, agentTargetGuidance(command));
|
|
182515
182344
|
}
|
|
182516
182345
|
if (command === "init-with-agent" && target === "none") {
|
|
182517
|
-
throw new
|
|
182346
|
+
throw new CliError("init-with-agent requires an agent target other than none.", agentTargetGuidance(command));
|
|
182518
182347
|
}
|
|
182519
182348
|
options.agentTarget = target;
|
|
182520
182349
|
index += 1;
|
|
182521
182350
|
continue;
|
|
182522
182351
|
}
|
|
182523
182352
|
if (arg.startsWith("--")) {
|
|
182524
|
-
throw new
|
|
182353
|
+
throw new CliError(`Unknown option: ${arg}`, commandHelpGuidance(command));
|
|
182525
182354
|
}
|
|
182526
|
-
throw new
|
|
182355
|
+
throw new CliError(`Unexpected argument: ${arg}`, commandHelpGuidance(command));
|
|
182527
182356
|
}
|
|
182528
182357
|
return options;
|
|
182529
182358
|
}
|
|
@@ -182610,19 +182439,19 @@ function resolveConfirmedScope(input) {
|
|
|
182610
182439
|
};
|
|
182611
182440
|
}
|
|
182612
182441
|
if (!input.prompts.isInteractive) {
|
|
182613
|
-
throw new
|
|
182442
|
+
throw new CliError("Interactive setup requires a TTY. Re-run with --yes for non-interactive mode.");
|
|
182614
182443
|
}
|
|
182615
182444
|
if (input.discovery.docs.ambiguous) {
|
|
182616
182445
|
const choices = input.discovery.docs.candidates.map((candidate) => candidate.pattern);
|
|
182617
182446
|
if (choices.length === 0) {
|
|
182618
|
-
throw new
|
|
182447
|
+
throw new CliError("Docs scope is ambiguous and no candidates were detected. Create docbridge.config.json manually.");
|
|
182619
182448
|
}
|
|
182620
182449
|
const docsPattern2 = input.prompts.select("Choose docs scope:", choices, choices[0] ?? "");
|
|
182621
182450
|
const languages2 = selectLanguages(input.discovery.code.languages, input.prompts);
|
|
182622
182451
|
return { docsPattern: docsPattern2, languages: languages2 };
|
|
182623
182452
|
}
|
|
182624
182453
|
if (input.discovery.docs.recommended === undefined) {
|
|
182625
|
-
throw new
|
|
182454
|
+
throw new CliError("Docs scope could not be determined.");
|
|
182626
182455
|
}
|
|
182627
182456
|
let docsPattern = input.discovery.docs.recommended.pattern;
|
|
182628
182457
|
if (!input.prompts.confirm(`Use docs scope ${docsPattern}?`, true)) {
|
|
@@ -182656,7 +182485,7 @@ function selectLanguages(languages, prompts) {
|
|
|
182656
182485
|
}
|
|
182657
182486
|
}
|
|
182658
182487
|
if (selected.length === 0) {
|
|
182659
|
-
throw new
|
|
182488
|
+
throw new CliError("At least one supported code language must remain in scope.");
|
|
182660
182489
|
}
|
|
182661
182490
|
return selected;
|
|
182662
182491
|
}
|
|
@@ -182686,10 +182515,10 @@ function resolveProjectRoot(root, command) {
|
|
|
182686
182515
|
try {
|
|
182687
182516
|
stats = statSync4(projectRoot);
|
|
182688
182517
|
} catch {
|
|
182689
|
-
throw new
|
|
182518
|
+
throw new CliError(`Root path does not exist: ${root}`, rootPathGuidance(command));
|
|
182690
182519
|
}
|
|
182691
182520
|
if (!stats.isDirectory()) {
|
|
182692
|
-
throw new
|
|
182521
|
+
throw new CliError(`Root path is not a directory: ${root}`, rootPathGuidance(command));
|
|
182693
182522
|
}
|
|
182694
182523
|
return projectRoot;
|
|
182695
182524
|
}
|
|
@@ -182965,8 +182794,7 @@ function planUpgrade(input) {
|
|
|
182965
182794
|
pending,
|
|
182966
182795
|
messages,
|
|
182967
182796
|
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
|
|
182797
|
+
requiresConfirmation: mode === "apply" && !input.options.dryRun && !input.options.yes && operations.some((operation) => operation.action === "overwrite" || operation.action === "remove")
|
|
182970
182798
|
};
|
|
182971
182799
|
}
|
|
182972
182800
|
function planOperations(input) {
|
|
@@ -183149,7 +182977,7 @@ function runUpgrade(options, io, initRuntime, runtime) {
|
|
|
183149
182977
|
});
|
|
183150
182978
|
if (plan.mode === "check" || options.dryRun) {
|
|
183151
182979
|
io.stdout(formatUpgradePlan(plan));
|
|
183152
|
-
return
|
|
182980
|
+
return 0;
|
|
183153
182981
|
}
|
|
183154
182982
|
return applyUpgrade(projectRoot, packageRoot, plan, io, initRuntime);
|
|
183155
182983
|
}
|
|
@@ -183167,7 +182995,7 @@ function applyUpgrade(projectRoot, packageRoot, plan, io, initRuntime) {
|
|
|
183167
182995
|
applySkillOperation(projectRoot, packageRoot, operation);
|
|
183168
182996
|
}
|
|
183169
182997
|
io.stdout(formatUpgradePlan(plan));
|
|
183170
|
-
return
|
|
182998
|
+
return 0;
|
|
183171
182999
|
}
|
|
183172
183000
|
function parseAgentTarget(value) {
|
|
183173
183001
|
if (value === undefined) {
|
|
@@ -183192,6 +183020,31 @@ function resolveProjectRoot2(root) {
|
|
|
183192
183020
|
return projectRoot;
|
|
183193
183021
|
}
|
|
183194
183022
|
|
|
183023
|
+
// src/cli/worker-signals.ts
|
|
183024
|
+
async function killingWorkersOnSignal(scan) {
|
|
183025
|
+
const signals = process.platform === "win32" ? ["SIGINT", "SIGTERM"] : ["SIGINT", "SIGTERM", "SIGHUP"];
|
|
183026
|
+
const handlers = new Map;
|
|
183027
|
+
const remove = () => {
|
|
183028
|
+
for (const [signal, handler] of handlers) {
|
|
183029
|
+
process.off(signal, handler);
|
|
183030
|
+
}
|
|
183031
|
+
};
|
|
183032
|
+
for (const signal of signals) {
|
|
183033
|
+
const handler = () => {
|
|
183034
|
+
killRunningWorkers();
|
|
183035
|
+
remove();
|
|
183036
|
+
process.kill(process.pid, signal);
|
|
183037
|
+
};
|
|
183038
|
+
handlers.set(signal, handler);
|
|
183039
|
+
process.on(signal, handler);
|
|
183040
|
+
}
|
|
183041
|
+
try {
|
|
183042
|
+
return await scan();
|
|
183043
|
+
} finally {
|
|
183044
|
+
remove();
|
|
183045
|
+
}
|
|
183046
|
+
}
|
|
183047
|
+
|
|
183195
183048
|
// src/cli/index.ts
|
|
183196
183049
|
var VERSION = package_default.version;
|
|
183197
183050
|
function parseCheckOptions(args) {
|
|
@@ -183219,9 +183072,9 @@ function resolveProjectRoot3(root, command) {
|
|
|
183219
183072
|
}
|
|
183220
183073
|
return projectRoot;
|
|
183221
183074
|
}
|
|
183222
|
-
function runCheck(options, io) {
|
|
183075
|
+
async function runCheck(options, io) {
|
|
183223
183076
|
const projectRoot = resolveProjectRoot3(options.root, "check");
|
|
183224
|
-
const result = check({ projectRoot, audit: options.audit });
|
|
183077
|
+
const result = await killingWorkersOnSignal(() => check({ projectRoot, audit: options.audit }));
|
|
183225
183078
|
if (options.json) {
|
|
183226
183079
|
io.stdout(`${JSON.stringify(result, null, 2)}
|
|
183227
183080
|
`);
|
|
@@ -183242,7 +183095,7 @@ See \`docbridge docs show troubleshooting\` for diagnostic codes and fixes.
|
|
|
183242
183095
|
}
|
|
183243
183096
|
return result.summary.errors > 0 ? 1 : 0;
|
|
183244
183097
|
}
|
|
183245
|
-
function runFileCommand(command, options, io, execute, render) {
|
|
183098
|
+
async function runFileCommand(command, options, io, execute, render) {
|
|
183246
183099
|
const projectRoot = resolveProjectRoot3(options.root, command);
|
|
183247
183100
|
const inputFiles = [...options.files];
|
|
183248
183101
|
if (options.stdin) {
|
|
@@ -183250,7 +183103,7 @@ function runFileCommand(command, options, io, execute, render) {
|
|
|
183250
183103
|
inputFiles.push(...readStdin().split(`
|
|
183251
183104
|
`));
|
|
183252
183105
|
}
|
|
183253
|
-
const outcome = execute(projectRoot, inputFiles);
|
|
183106
|
+
const outcome = await killingWorkersOnSignal(async () => execute(projectRoot, inputFiles));
|
|
183254
183107
|
if (!outcome.ok) {
|
|
183255
183108
|
throw new DiagnosticOutputError(outcome.diagnostics.map(formatDiagnostic).join(`
|
|
183256
183109
|
`));
|
|
@@ -183271,7 +183124,7 @@ function writeStructuredResult(result, options) {
|
|
|
183271
183124
|
`);
|
|
183272
183125
|
}
|
|
183273
183126
|
}
|
|
183274
|
-
function runRelated(options, io) {
|
|
183127
|
+
async function runRelated(options, io) {
|
|
183275
183128
|
if (!options.stdin && options.files.length === 0) {
|
|
183276
183129
|
throw new CliError("No input files were provided.", missingInputGuidance("related"));
|
|
183277
183130
|
}
|
|
@@ -183300,7 +183153,7 @@ function runRelated(options, io) {
|
|
|
183300
183153
|
return 0;
|
|
183301
183154
|
});
|
|
183302
183155
|
}
|
|
183303
|
-
function runContext(options, io) {
|
|
183156
|
+
async function runContext(options, io) {
|
|
183304
183157
|
if (!options.stdin && options.files.length === 0) {
|
|
183305
183158
|
throw new CliError("No input files were provided.", missingInputGuidance("context"));
|
|
183306
183159
|
}
|
|
@@ -183314,7 +183167,7 @@ function runContext(options, io) {
|
|
|
183314
183167
|
return 0;
|
|
183315
183168
|
});
|
|
183316
183169
|
}
|
|
183317
|
-
function runGraph(options, io) {
|
|
183170
|
+
async function runGraph(options, io) {
|
|
183318
183171
|
return runFileCommand("graph", options, io, (projectRoot, inputFiles) => graph({ projectRoot, inputFiles, includeContent: options.includeContent }), (result, projectRoot, inputFiles) => {
|
|
183319
183172
|
const normalizedInputFiles = normalizeChangedPaths(projectRoot, inputFiles);
|
|
183320
183173
|
writeStructuredResult(result, {
|
|
@@ -183368,7 +183221,7 @@ var COMMAND_HANDLERS = {
|
|
|
183368
183221
|
return 0;
|
|
183369
183222
|
}
|
|
183370
183223
|
};
|
|
183371
|
-
function run(argv, io = {
|
|
183224
|
+
async function run(argv, io = {
|
|
183372
183225
|
stdout: (text) => process.stdout.write(text),
|
|
183373
183226
|
stderr: (text) => process.stderr.write(text)
|
|
183374
183227
|
}, initRuntime = { prompts: createDefaultPrompts() }, cliRuntime = {}) {
|
|
@@ -183390,7 +183243,7 @@ function run(argv, io = {
|
|
|
183390
183243
|
return 0;
|
|
183391
183244
|
}
|
|
183392
183245
|
if (isSubcommand(command)) {
|
|
183393
|
-
const exitCode = COMMAND_HANDLERS[command](rest, io, initRuntime, cliRuntime);
|
|
183246
|
+
const exitCode = await COMMAND_HANDLERS[command](rest, io, initRuntime, cliRuntime);
|
|
183394
183247
|
writeUpdateNotice(argv, io, initRuntime, cliRuntime);
|
|
183395
183248
|
return exitCode;
|
|
183396
183249
|
}
|
|
@@ -183455,7 +183308,7 @@ if (__require.main == __require.module) {
|
|
|
183455
183308
|
const argv = process.argv.slice(2);
|
|
183456
183309
|
const isTty = process.stderr.isTTY === true;
|
|
183457
183310
|
const latest = await resolveLatestForInvocation(argv, process.env, isTty);
|
|
183458
|
-
process.exitCode = run(argv, {
|
|
183311
|
+
process.exitCode = await run(argv, {
|
|
183459
183312
|
stdout: (text) => process.stdout.write(text),
|
|
183460
183313
|
stderr: (text) => process.stderr.write(text)
|
|
183461
183314
|
}, { 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
|
|
package/docs/user/linking.md
CHANGED
|
@@ -255,8 +255,9 @@ Use the scanner-produced canonical symbol ID exactly.
|
|
|
255
255
|
## Heading anchors and reciprocity
|
|
256
256
|
|
|
257
257
|
DocBridge creates anchors from ATX headings only. It lowercases the heading,
|
|
258
|
-
collapses runs of whitespace and punctuation to `-`,
|
|
259
|
-
|
|
258
|
+
collapses runs of whitespace and punctuation other than `_` to `-`, keeps `_`
|
|
259
|
+
as GitHub's heading slugs do, preserves Unicode letters and numbers, and removes
|
|
260
|
+
leading and trailing hyphens. `## Login Spec (v2)` is
|
|
260
261
|
`#login-spec-v2`.
|
|
261
262
|
|
|
262
263
|
Empty headings have no anchor; a `@code` annotation attached to an empty
|