artifact-graph 0.9.3 → 0.10.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 +28 -0
- package/dist/cli.js +1071 -75
- package/dist/index.cjs +351 -60
- package/dist/index.d.cts +32 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.js +331 -41
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2668,11 +2668,194 @@ var init_versioned_traceability = __esm({
|
|
|
2668
2668
|
}
|
|
2669
2669
|
});
|
|
2670
2670
|
|
|
2671
|
+
// src/native-binding-diagnostics.ts
|
|
2672
|
+
import { existsSync as existsSync2, readdirSync } from "fs";
|
|
2673
|
+
import { createRequire } from "module";
|
|
2674
|
+
import { dirname as dirname2, join as join4 } from "path";
|
|
2675
|
+
import { fileURLToPath } from "url";
|
|
2676
|
+
function parsePnpmVersion(stdout) {
|
|
2677
|
+
const trimmed = stdout.trim();
|
|
2678
|
+
const segments = trimmed.split(".");
|
|
2679
|
+
if (!/^\d+$/.test(segments[0] ?? "")) return void 0;
|
|
2680
|
+
const major = Number(segments[0]);
|
|
2681
|
+
const minorSegment = segments.length > 1 ? segments[1] : void 0;
|
|
2682
|
+
const minor = minorSegment !== void 0 && /^\d+$/.test(minorSegment) ? Number(minorSegment) : void 0;
|
|
2683
|
+
return minor === void 0 ? { major } : { major, minor };
|
|
2684
|
+
}
|
|
2685
|
+
function pnpmUsesAllowBuilds(version) {
|
|
2686
|
+
if (!version) return false;
|
|
2687
|
+
return version.major > 10 || version.major === 10 && (version.minor ?? 0) >= 26;
|
|
2688
|
+
}
|
|
2689
|
+
function createInstallBoundBindingLoader(installBase) {
|
|
2690
|
+
return () => createRequire(installBase)("better-sqlite3");
|
|
2691
|
+
}
|
|
2692
|
+
function defaultBindingLoaderImpl() {
|
|
2693
|
+
const base = typeof __filename === "string" ? __filename : import.meta.url;
|
|
2694
|
+
return createRequire(base)("better-sqlite3");
|
|
2695
|
+
}
|
|
2696
|
+
function defaultBindingLoader() {
|
|
2697
|
+
return defaultBindingLoaderImpl();
|
|
2698
|
+
}
|
|
2699
|
+
function classifyNativeBindingError(error, context = {}) {
|
|
2700
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2701
|
+
const code = error?.code;
|
|
2702
|
+
if (/was compiled against a different Node\.js version|NODE_MODULE_VERSION|different ABI|Incompatible ABI/i.test(
|
|
2703
|
+
message
|
|
2704
|
+
)) {
|
|
2705
|
+
return { cause: "ABI_MISMATCH", detail: message };
|
|
2706
|
+
}
|
|
2707
|
+
if (code === "MODULE_NOT_FOUND" || /Cannot find module|Cannot find package/i.test(message)) {
|
|
2708
|
+
if (context.packagePresent) {
|
|
2709
|
+
return {
|
|
2710
|
+
cause: "BUILD_DISABLED",
|
|
2711
|
+
detail: `${message}\uFF08better-sqlite3 \u5305\u76EE\u5F55\u5B58\u5728\u4F46\u539F\u751F\u4EA7\u7269\u7F3A\u5931\uFF0C\u7B26\u5408 lifecycle scripts \u88AB\u7981\u7684\u7279\u5F81\uFF09`
|
|
2712
|
+
};
|
|
2713
|
+
}
|
|
2714
|
+
return { cause: "MISSING", detail: message };
|
|
2715
|
+
}
|
|
2716
|
+
if (/Could not locate the bindings file/i.test(message)) {
|
|
2717
|
+
return {
|
|
2718
|
+
cause: "BUILD_DISABLED",
|
|
2719
|
+
detail: `${message}\uFF08better-sqlite3 \u5305\u5B58\u5728\u4F46\u539F\u751F .node \u4EA7\u7269\u672A\u6784\u5EFA\uFF0C\u7B26\u5408 lifecycle scripts \u88AB\u7981\u6216\u672A\u6784\u5EFA\u7684\u7279\u5F81\uFF09`
|
|
2720
|
+
};
|
|
2721
|
+
}
|
|
2722
|
+
if (/invalid ELF|broken binary|failed to load native/i.test(message)) {
|
|
2723
|
+
return { cause: "LOAD_ERROR", detail: message };
|
|
2724
|
+
}
|
|
2725
|
+
return { cause: "LOAD_ERROR", detail: message };
|
|
2726
|
+
}
|
|
2727
|
+
function buildNativeBindingSuggestion(cause, pnpmVersion) {
|
|
2728
|
+
const allowlist = pnpmUsesAllowBuilds(pnpmVersion) ? `pnpm 10.26+ \u5728\u9879\u76EE\u6839 pnpm-workspace.yaml \u6DFB\u52A0 allowBuilds \u952E\uFF08${PNPM_ALLOW_BUILDS_SNIPPET.replace(/\n/g, "\uFF1B")}\uFF09` : `pnpm 10.0\u201310.25 \u5728 package.json \u914D\u7F6E pnpm.onlyBuiltDependencies: ["better-sqlite3"]\uFF08pnpm 10.26+ \u6539\u7528\u9879\u76EE\u6839 pnpm-workspace.yaml \u7684 allowBuilds \u952E\uFF09`;
|
|
2729
|
+
switch (cause) {
|
|
2730
|
+
case "MISSING":
|
|
2731
|
+
return `better-sqlite3 \u539F\u751F\u4EA7\u7269\u7F3A\u5931\u3002\u6309 INSTALL.md \u58F0\u660E\u6784\u5EFA\u5141\u8BB8\u6E05\u5355\u540E\u91CD\u65B0\u5B89\u88C5\uFF1A${allowlist}\uFF0C\u7136\u540E pnpm install\u3002`;
|
|
2732
|
+
case "ABI_MISMATCH":
|
|
2733
|
+
return `\u5F53\u524D Node ABI \u4E0E better-sqlite3 prebuilt \u76EE\u6807\u4E0D\u4E00\u81F4\uFF08\u5F53\u524D process.versions.modules=${process.versions.modules}\uFF09\u3002\u5207\u6362\u5230\u4E0E\u5DF2\u5B89\u88C5 prebuilt \u5339\u914D\u7684 Node \u7248\u672C\uFF0C\u6216\u5BF9\u5F53\u524D Node \u91CD\u5EFA\uFF1Apnpm rebuild better-sqlite3\u3002\u4E0D\u8981\u624B\u5DE5\u7F16\u8BD1\u3002`;
|
|
2734
|
+
case "BUILD_DISABLED":
|
|
2735
|
+
return `pnpm lifecycle scripts \u88AB\u7981\u5BFC\u81F4 better-sqlite3 \u672A\u6784\u5EFA\u3002${allowlist}\uFF0C\u7136\u540E pnpm install \u91CD\u5EFA\u539F\u751F\u4EA7\u7269\u3002\u4E0D\u8981\u624B\u5DE5\u7F16\u8BD1\u3002`;
|
|
2736
|
+
default:
|
|
2737
|
+
return "better-sqlite3 \u52A0\u8F7D\u5931\u8D25\uFF08\u672A\u77E5\u539F\u751F\u9519\u8BEF\uFF09\u3002\u5148\u8FD0\u884C artifact-graph doctor --format json \u67E5\u770B\u63A2\u9488\u8BE6\u60C5\uFF0C\u518D\u6309 INSTALL.md \u91CD\u5EFA\u5F53\u524D\u9009\u4E2D\u8FD0\u884C\u65F6\u7684\u539F\u751F\u4EA7\u7269\u3002";
|
|
2738
|
+
}
|
|
2739
|
+
}
|
|
2740
|
+
function probeNativeBinding(context = {}) {
|
|
2741
|
+
const loader = context.loader ?? defaultBindingLoader;
|
|
2742
|
+
const presenceRoot = context.installRoot ?? context.root;
|
|
2743
|
+
const packagePresent = presenceRoot ? betterSqlite3PackagePresent(presenceRoot) : void 0;
|
|
2744
|
+
const classify = (error) => classifyNativeBindingError(error, { packagePresent });
|
|
2745
|
+
const fail = (failedStage, error) => {
|
|
2746
|
+
const { cause, detail } = classify(error);
|
|
2747
|
+
return { ok: false, failedStage, cause, detail, suggestion: buildNativeBindingSuggestion(cause, context.pnpmVersion) };
|
|
2748
|
+
};
|
|
2749
|
+
let DatabaseCtor;
|
|
2750
|
+
try {
|
|
2751
|
+
DatabaseCtor = loader();
|
|
2752
|
+
} catch (error) {
|
|
2753
|
+
return fail("load", error);
|
|
2754
|
+
}
|
|
2755
|
+
const Database = DatabaseCtor;
|
|
2756
|
+
let db;
|
|
2757
|
+
try {
|
|
2758
|
+
db = new Database(":memory:");
|
|
2759
|
+
} catch (error) {
|
|
2760
|
+
return fail("open", error);
|
|
2761
|
+
}
|
|
2762
|
+
try {
|
|
2763
|
+
db.exec("CREATE TABLE probe (id INTEGER PRIMARY KEY, value TEXT)");
|
|
2764
|
+
db.prepare("INSERT INTO probe (id, value) VALUES (?, ?)").run(1, "ok");
|
|
2765
|
+
} catch (error) {
|
|
2766
|
+
safeClose(db);
|
|
2767
|
+
return fail("write", error);
|
|
2768
|
+
}
|
|
2769
|
+
try {
|
|
2770
|
+
const row = db.prepare("SELECT value FROM probe WHERE id = ?").get(1);
|
|
2771
|
+
if (!row || row.value !== "ok") {
|
|
2772
|
+
safeClose(db);
|
|
2773
|
+
return {
|
|
2774
|
+
ok: false,
|
|
2775
|
+
failedStage: "read",
|
|
2776
|
+
cause: "LOAD_ERROR",
|
|
2777
|
+
detail: "probe \u56DE\u8BFB\u65AD\u8A00\u5931\u8D25\uFF1ASELECT \u7ED3\u679C\u4E0E\u5199\u5165\u503C\u4E0D\u4E00\u81F4\u3002",
|
|
2778
|
+
suggestion: buildNativeBindingSuggestion("LOAD_ERROR", context.pnpmVersion)
|
|
2779
|
+
};
|
|
2780
|
+
}
|
|
2781
|
+
} catch (error) {
|
|
2782
|
+
safeClose(db);
|
|
2783
|
+
return fail("read", error);
|
|
2784
|
+
}
|
|
2785
|
+
try {
|
|
2786
|
+
db.close();
|
|
2787
|
+
} catch (error) {
|
|
2788
|
+
return fail("close", error);
|
|
2789
|
+
}
|
|
2790
|
+
return { ok: true };
|
|
2791
|
+
}
|
|
2792
|
+
function buildNativeBindingDiagnostic(failure, probeContext) {
|
|
2793
|
+
return {
|
|
2794
|
+
selectedCli: probeContext.selectedCli,
|
|
2795
|
+
runtime: `${process.execPath} (v${process.versions.node})`,
|
|
2796
|
+
abi: `process.versions.modules=${process.versions.modules}${abiPrebuiltHint(failure.detail)}`,
|
|
2797
|
+
installSource: probeContext.installSource,
|
|
2798
|
+
probedFrom: probeContext.probedFrom,
|
|
2799
|
+
failedStage: failure.failedStage,
|
|
2800
|
+
cause: failure.cause,
|
|
2801
|
+
suggestion: failure.suggestion
|
|
2802
|
+
};
|
|
2803
|
+
}
|
|
2804
|
+
function buildNativeBindingSuccessDiagnostic(probeContext) {
|
|
2805
|
+
return {
|
|
2806
|
+
selectedCli: probeContext.selectedCli,
|
|
2807
|
+
runtime: `${process.execPath} (v${process.versions.node})`,
|
|
2808
|
+
installSource: probeContext.installSource,
|
|
2809
|
+
probedFrom: probeContext.probedFrom
|
|
2810
|
+
};
|
|
2811
|
+
}
|
|
2812
|
+
function findPackageRoot(anchorFile) {
|
|
2813
|
+
let dir = dirname2(anchorFile);
|
|
2814
|
+
for (; ; ) {
|
|
2815
|
+
if (existsSync2(join4(dir, "package.json"))) return dir;
|
|
2816
|
+
const parent = dirname2(dir);
|
|
2817
|
+
if (parent === dir) return void 0;
|
|
2818
|
+
dir = parent;
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
function safeClose(db) {
|
|
2822
|
+
try {
|
|
2823
|
+
db?.close();
|
|
2824
|
+
} catch {
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2827
|
+
function abiPrebuiltHint(detail) {
|
|
2828
|
+
const match = /using NODE_MODULE_VERSION (\d+)/.exec(detail);
|
|
2829
|
+
return match ? `\uFF1Bprebuilt \u76EE\u6807 NODE_MODULE_VERSION=${match[1]}` : "";
|
|
2830
|
+
}
|
|
2831
|
+
function betterSqlite3PackagePresent(root) {
|
|
2832
|
+
const direct = join4(root, "node_modules", "better-sqlite3", "package.json");
|
|
2833
|
+
if (existsSync2(direct)) return true;
|
|
2834
|
+
const pnpmDir = join4(root, "node_modules", ".pnpm");
|
|
2835
|
+
let entries;
|
|
2836
|
+
try {
|
|
2837
|
+
entries = readdirSync(pnpmDir);
|
|
2838
|
+
} catch {
|
|
2839
|
+
return false;
|
|
2840
|
+
}
|
|
2841
|
+
return entries.some((entry) => {
|
|
2842
|
+
if (!entry.startsWith("better-sqlite3@")) return false;
|
|
2843
|
+
return existsSync2(join4(pnpmDir, entry, "node_modules", "better-sqlite3", "package.json"));
|
|
2844
|
+
});
|
|
2845
|
+
}
|
|
2846
|
+
var PNPM_ALLOW_BUILDS_SNIPPET;
|
|
2847
|
+
var init_native_binding_diagnostics = __esm({
|
|
2848
|
+
"src/native-binding-diagnostics.ts"() {
|
|
2849
|
+
"use strict";
|
|
2850
|
+
PNPM_ALLOW_BUILDS_SNIPPET = "pnpm-workspace.yaml:\nallowBuilds:\n better-sqlite3: true";
|
|
2851
|
+
}
|
|
2852
|
+
});
|
|
2853
|
+
|
|
2671
2854
|
// src/cli-resolver.ts
|
|
2672
2855
|
import { execFile } from "child_process";
|
|
2673
|
-
import { constants } from "fs";
|
|
2856
|
+
import { constants, readFileSync, realpathSync } from "fs";
|
|
2674
2857
|
import { access } from "fs/promises";
|
|
2675
|
-
import { isAbsolute, join as
|
|
2858
|
+
import { basename, dirname as dirname3, isAbsolute, join as join5, resolve } from "path";
|
|
2676
2859
|
import { promisify } from "util";
|
|
2677
2860
|
async function resolveArtifactGraphCli(root, options = {}) {
|
|
2678
2861
|
const pathCli = await findCommandOnPath("artifact-graph");
|
|
@@ -2680,7 +2863,7 @@ async function resolveArtifactGraphCli(root, options = {}) {
|
|
|
2680
2863
|
const candidates = [
|
|
2681
2864
|
{
|
|
2682
2865
|
source: "node_modules",
|
|
2683
|
-
path:
|
|
2866
|
+
path: join5(root, "node_modules/.bin/artifact-graph"),
|
|
2684
2867
|
exists: false
|
|
2685
2868
|
},
|
|
2686
2869
|
{
|
|
@@ -2713,13 +2896,33 @@ async function resolveArtifactGraphCli(root, options = {}) {
|
|
|
2713
2896
|
}
|
|
2714
2897
|
async function doctorArtifactChain(root, options = {}) {
|
|
2715
2898
|
const cli = await resolveArtifactGraphCli(root, options);
|
|
2716
|
-
const configPath =
|
|
2717
|
-
const lockPath =
|
|
2899
|
+
const configPath = join5(root, "artifact-graph.config.yaml");
|
|
2900
|
+
const lockPath = join5(root, VERSION_LOCK_PATH);
|
|
2718
2901
|
const supportedCommands = cli.path ? await detectSupportedCommands(cli.path) : [];
|
|
2719
2902
|
const nodeCompatible = isNodeCompatible(process.versions.node);
|
|
2903
|
+
const pnpmVersion = await detectPnpmVersion();
|
|
2904
|
+
const installBase = bindingInstallBase(cli, root);
|
|
2905
|
+
const installRoot = bindingInstallRoot(cli, root);
|
|
2906
|
+
const loader = options.bindingLoader ?? (installBase ? createInstallBoundBindingLoader(installBase) : void 0);
|
|
2907
|
+
const probe = probeNativeBinding({
|
|
2908
|
+
root,
|
|
2909
|
+
...installRoot ? { installRoot } : {},
|
|
2910
|
+
pnpmVersion,
|
|
2911
|
+
...loader ? { loader } : {}
|
|
2912
|
+
});
|
|
2913
|
+
const probedFrom = installBase ?? "(current artifact-graph package)";
|
|
2914
|
+
const probeContext = {
|
|
2915
|
+
selectedCli: cli.path ? `${cli.path} (${cli.source})` : "not found",
|
|
2916
|
+
installSource: cli.source ?? "unknown",
|
|
2917
|
+
probedFrom
|
|
2918
|
+
};
|
|
2919
|
+
const nativeBinding = probe.ok ? { ok: true, ...buildNativeBindingSuccessDiagnostic(probeContext) } : { ok: false, ...buildNativeBindingDiagnostic(probe, probeContext) };
|
|
2720
2920
|
const warnings = [
|
|
2721
2921
|
...cli.warnings,
|
|
2722
|
-
...nodeCompatible ? [] : [`Node.js ${process.versions.node} does not satisfy >=22.0.0.`]
|
|
2922
|
+
...nodeCompatible ? [] : [`Node.js ${process.versions.node} does not satisfy >=22.0.0.`],
|
|
2923
|
+
...probe.ok ? [] : [
|
|
2924
|
+
`better-sqlite3 binding probe FAILED at stage "${probe.failedStage}" (cause ${probe.cause}): ${probe.suggestion}`
|
|
2925
|
+
]
|
|
2723
2926
|
];
|
|
2724
2927
|
return {
|
|
2725
2928
|
schemaVersion: "1.0",
|
|
@@ -2738,6 +2941,7 @@ async function doctorArtifactChain(root, options = {}) {
|
|
|
2738
2941
|
path: lockPath,
|
|
2739
2942
|
exists: await pathExists(lockPath)
|
|
2740
2943
|
},
|
|
2944
|
+
nativeBinding,
|
|
2741
2945
|
supportedCommands,
|
|
2742
2946
|
warnings
|
|
2743
2947
|
};
|
|
@@ -2753,6 +2957,31 @@ function renderDoctorMarkdown(report) {
|
|
|
2753
2957
|
`Lock: \`${report.lock.path}\` ${report.lock.exists ? "found" : "missing"}`,
|
|
2754
2958
|
""
|
|
2755
2959
|
];
|
|
2960
|
+
lines.push("## Native Binding Probe");
|
|
2961
|
+
if (report.nativeBinding.ok) {
|
|
2962
|
+
const binding = report.nativeBinding;
|
|
2963
|
+
lines.push(
|
|
2964
|
+
"- better-sqlite3 probe: PASS",
|
|
2965
|
+
` - selectedCli: \`${binding.selectedCli}\``,
|
|
2966
|
+
` - runtime: \`${binding.runtime}\``,
|
|
2967
|
+
` - installSource: \`${binding.installSource}\``,
|
|
2968
|
+
` - probedFrom: \`${binding.probedFrom}\``
|
|
2969
|
+
);
|
|
2970
|
+
} else {
|
|
2971
|
+
const binding = report.nativeBinding;
|
|
2972
|
+
lines.push(
|
|
2973
|
+
"- better-sqlite3 probe: **FAIL**",
|
|
2974
|
+
` - failedStage: \`${binding.failedStage}\``,
|
|
2975
|
+
` - cause: \`${binding.cause}\``,
|
|
2976
|
+
` - selectedCli: \`${binding.selectedCli}\``,
|
|
2977
|
+
` - runtime: \`${binding.runtime}\``,
|
|
2978
|
+
` - abi: \`${binding.abi}\``,
|
|
2979
|
+
` - installSource: \`${binding.installSource}\``,
|
|
2980
|
+
` - probedFrom: \`${binding.probedFrom}\``,
|
|
2981
|
+
` - suggestion: ${binding.suggestion}`
|
|
2982
|
+
);
|
|
2983
|
+
}
|
|
2984
|
+
lines.push("");
|
|
2756
2985
|
if (report.supportedCommands.length > 0) {
|
|
2757
2986
|
lines.push("## Supported Commands");
|
|
2758
2987
|
for (const command of report.supportedCommands) {
|
|
@@ -2809,11 +3038,71 @@ function isNodeCompatible(version) {
|
|
|
2809
3038
|
const major = Number(version.split(".")[0]);
|
|
2810
3039
|
return Number.isFinite(major) && major >= 22;
|
|
2811
3040
|
}
|
|
3041
|
+
async function detectPnpmVersion() {
|
|
3042
|
+
try {
|
|
3043
|
+
const result = await execFileAsync("pnpm", ["--version"]);
|
|
3044
|
+
return parsePnpmVersion(result.stdout);
|
|
3045
|
+
} catch {
|
|
3046
|
+
return void 0;
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
function bindingInstallBase(cli, root) {
|
|
3050
|
+
if (!cli.path || !cli.source) return void 0;
|
|
3051
|
+
switch (cli.source) {
|
|
3052
|
+
case "node_modules":
|
|
3053
|
+
return join5(root, "package.json");
|
|
3054
|
+
case "plugin-bundled":
|
|
3055
|
+
case "path":
|
|
3056
|
+
case "legacy":
|
|
3057
|
+
return resolveCliInstallAnchor(cli.path);
|
|
3058
|
+
default:
|
|
3059
|
+
return void 0;
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
function bindingInstallRoot(cli, root) {
|
|
3063
|
+
if (!cli.path || !cli.source) return void 0;
|
|
3064
|
+
switch (cli.source) {
|
|
3065
|
+
case "node_modules":
|
|
3066
|
+
return root;
|
|
3067
|
+
case "plugin-bundled":
|
|
3068
|
+
case "path":
|
|
3069
|
+
case "legacy":
|
|
3070
|
+
return findPackageRoot(resolveCliInstallAnchor(cli.path));
|
|
3071
|
+
default:
|
|
3072
|
+
return void 0;
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
function resolveCliInstallAnchor(cliPath) {
|
|
3076
|
+
const real = safeRealpath(cliPath);
|
|
3077
|
+
if (basename(dirname3(real)) !== ".bin") return real;
|
|
3078
|
+
const shimTarget = readBinShimTarget(real);
|
|
3079
|
+
return shimTarget ? safeRealpath(shimTarget) : real;
|
|
3080
|
+
}
|
|
3081
|
+
function readBinShimTarget(shimPath) {
|
|
3082
|
+
let content;
|
|
3083
|
+
try {
|
|
3084
|
+
content = readFileSync(shimPath, "utf-8");
|
|
3085
|
+
} catch {
|
|
3086
|
+
return void 0;
|
|
3087
|
+
}
|
|
3088
|
+
if (!content.startsWith("#!")) return void 0;
|
|
3089
|
+
const match = /\$basedir\/(\.\.(?:\/[^"'\s]+)+\.js)/.exec(content);
|
|
3090
|
+
if (!match) return void 0;
|
|
3091
|
+
return resolve(dirname3(shimPath), match[1]);
|
|
3092
|
+
}
|
|
3093
|
+
function safeRealpath(path) {
|
|
3094
|
+
try {
|
|
3095
|
+
return realpathSync(path);
|
|
3096
|
+
} catch {
|
|
3097
|
+
return path;
|
|
3098
|
+
}
|
|
3099
|
+
}
|
|
2812
3100
|
var execFileAsync, KNOWN_COMMANDS;
|
|
2813
3101
|
var init_cli_resolver = __esm({
|
|
2814
3102
|
"src/cli-resolver.ts"() {
|
|
2815
3103
|
"use strict";
|
|
2816
3104
|
init_versioned_traceability();
|
|
3105
|
+
init_native_binding_diagnostics();
|
|
2817
3106
|
execFileAsync = promisify(execFile);
|
|
2818
3107
|
KNOWN_COMMANDS = [
|
|
2819
3108
|
"init",
|
|
@@ -2832,6 +3121,7 @@ var init_cli_resolver = __esm({
|
|
|
2832
3121
|
"version-lock refresh",
|
|
2833
3122
|
"trace-version",
|
|
2834
3123
|
"next-id",
|
|
3124
|
+
"refactor-id",
|
|
2835
3125
|
"render",
|
|
2836
3126
|
"doctor"
|
|
2837
3127
|
];
|
|
@@ -2939,7 +3229,7 @@ var init_git_hook_path = __esm({
|
|
|
2939
3229
|
import { constants as constants2 } from "fs";
|
|
2940
3230
|
import { randomUUID } from "crypto";
|
|
2941
3231
|
import { lstat, mkdir as mkdir3, open, readlink, rename, unlink } from "fs/promises";
|
|
2942
|
-
import { basename, dirname as
|
|
3232
|
+
import { basename as basename2, dirname as dirname4, join as join6 } from "path";
|
|
2943
3233
|
function detectHookInterpreter(content) {
|
|
2944
3234
|
if (content.trim().length === 0) {
|
|
2945
3235
|
return "empty";
|
|
@@ -3250,8 +3540,8 @@ async function removeHookAtomically(hookPath, snapshot) {
|
|
|
3250
3540
|
}
|
|
3251
3541
|
}
|
|
3252
3542
|
async function writeHookAtomically(hookPath, content, snapshot, mode) {
|
|
3253
|
-
await mkdir3(
|
|
3254
|
-
const temporaryPath =
|
|
3543
|
+
await mkdir3(dirname4(hookPath), { recursive: true });
|
|
3544
|
+
const temporaryPath = join6(dirname4(hookPath), `.${basename2(hookPath)}.${randomUUID()}.tmp`);
|
|
3255
3545
|
let temporaryExists = false;
|
|
3256
3546
|
try {
|
|
3257
3547
|
const temporary = await open(temporaryPath, "wx", mode);
|
|
@@ -3690,7 +3980,7 @@ var init_review_result_validator = __esm({
|
|
|
3690
3980
|
// src/contract-kernel.ts
|
|
3691
3981
|
import { createHash as createHash2 } from "crypto";
|
|
3692
3982
|
import { readFile as readFile2, readdir as readdir2 } from "fs/promises";
|
|
3693
|
-
import { join as
|
|
3983
|
+
import { join as join7 } from "path";
|
|
3694
3984
|
import _AjvModule from "ajv";
|
|
3695
3985
|
function isOfficialNamespace(namespace) {
|
|
3696
3986
|
return OFFICIAL_NAMESPACE_PATTERN.test(namespace);
|
|
@@ -4022,7 +4312,7 @@ async function loadContractsFromDirectory(contractsDir, options) {
|
|
|
4022
4312
|
const entries = await readdir2(contractsDir, { withFileTypes: true });
|
|
4023
4313
|
for (const entry of entries) {
|
|
4024
4314
|
if (entry.isDirectory()) {
|
|
4025
|
-
const schemaPath =
|
|
4315
|
+
const schemaPath = join7(contractsDir, entry.name, "schema.json");
|
|
4026
4316
|
const contract = await loadContract(schemaPath, options);
|
|
4027
4317
|
contracts.push(contract);
|
|
4028
4318
|
}
|
|
@@ -4463,12 +4753,11 @@ var init_contract_kernel = __esm({
|
|
|
4463
4753
|
});
|
|
4464
4754
|
|
|
4465
4755
|
// src/index.ts
|
|
4466
|
-
import Database from "better-sqlite3";
|
|
4467
4756
|
import matter from "gray-matter";
|
|
4468
4757
|
import yaml from "js-yaml";
|
|
4469
|
-
import { accessSync, constants as fsConstants, existsSync as
|
|
4758
|
+
import { accessSync, constants as fsConstants, existsSync as existsSync3, statSync } from "fs";
|
|
4470
4759
|
import { mkdir as mkdir4, readFile as readFile3, readdir as readdir3, writeFile as writeFile3 } from "fs/promises";
|
|
4471
|
-
import { basename as
|
|
4760
|
+
import { basename as basename4, dirname as dirname5, extname, isAbsolute as isAbsolute3, join as join8, relative as relative3, resolve as resolve3 } from "path";
|
|
4472
4761
|
function isTargetArtifactType(type) {
|
|
4473
4762
|
return isPacketTargetType(type);
|
|
4474
4763
|
}
|
|
@@ -4510,7 +4799,7 @@ function resolveArtifactTypeName(schema, token) {
|
|
|
4510
4799
|
return void 0;
|
|
4511
4800
|
}
|
|
4512
4801
|
async function loadConfig(root) {
|
|
4513
|
-
const configPath =
|
|
4802
|
+
const configPath = join8(root, "artifact-graph.config.yaml");
|
|
4514
4803
|
let parsed = {};
|
|
4515
4804
|
try {
|
|
4516
4805
|
const raw = await readFile3(configPath, "utf-8");
|
|
@@ -4687,7 +4976,7 @@ async function scanArtifacts(root, schema) {
|
|
|
4687
4976
|
continue;
|
|
4688
4977
|
}
|
|
4689
4978
|
scannedFiles.set(file, type);
|
|
4690
|
-
const raw = await readFile3(
|
|
4979
|
+
const raw = await readFile3(join8(root, file), "utf-8");
|
|
4691
4980
|
const parsed = parseFile(type, file, raw, config);
|
|
4692
4981
|
nodes.push(...parsed.nodes);
|
|
4693
4982
|
edges.push(...parsed.edges);
|
|
@@ -5003,7 +5292,7 @@ async function validateScenarioPrdLinkIndex(root, graph) {
|
|
|
5003
5292
|
const indexPath = "artifacts/prd/feature-index.md";
|
|
5004
5293
|
let raw = "";
|
|
5005
5294
|
try {
|
|
5006
|
-
raw = await readFile3(
|
|
5295
|
+
raw = await readFile3(join8(root, indexPath), "utf-8");
|
|
5007
5296
|
} catch (error) {
|
|
5008
5297
|
if (error.code === "ENOENT") {
|
|
5009
5298
|
return [];
|
|
@@ -5193,11 +5482,12 @@ function nextId(graph, schema, type, rangeName) {
|
|
|
5193
5482
|
throw new Error(`ID range ${type}.${rangeName} is exhausted`);
|
|
5194
5483
|
}
|
|
5195
5484
|
async function writeGraphCache(root, graph) {
|
|
5196
|
-
const cacheDir =
|
|
5485
|
+
const cacheDir = join8(root, ".artifact-graph");
|
|
5197
5486
|
await mkdir4(cacheDir, { recursive: true });
|
|
5198
|
-
await writeFile3(
|
|
5487
|
+
await writeFile3(join8(cacheDir, "index.json"), `${JSON.stringify(graph, null, 2)}
|
|
5199
5488
|
`);
|
|
5200
|
-
const
|
|
5489
|
+
const { default: Database } = await import("better-sqlite3");
|
|
5490
|
+
const db = new Database(join8(cacheDir, "graph.sqlite"));
|
|
5201
5491
|
try {
|
|
5202
5492
|
db.exec(`
|
|
5203
5493
|
DROP TABLE IF EXISTS nodes;
|
|
@@ -5551,7 +5841,7 @@ function parseDecisions(path, raw) {
|
|
|
5551
5841
|
}
|
|
5552
5842
|
function isTestFile(filePath) {
|
|
5553
5843
|
const normalized = filePath.replace(/\\/g, "/");
|
|
5554
|
-
const name =
|
|
5844
|
+
const name = basename4(normalized);
|
|
5555
5845
|
if (/\.(test|spec)\.[^.]+$/.test(name)) return true;
|
|
5556
5846
|
if (/(^|\/)(tests|test|__tests__)\//.test(normalized)) return true;
|
|
5557
5847
|
if (/\w+Tests?\.java$/.test(name)) return true;
|
|
@@ -5872,7 +6162,7 @@ function parseE2eTest(path, raw) {
|
|
|
5872
6162
|
});
|
|
5873
6163
|
const nodes = [];
|
|
5874
6164
|
const edges = [];
|
|
5875
|
-
const batch = String(data.test_batch ??
|
|
6165
|
+
const batch = String(data.test_batch ?? basename4(path, extname(path))).trim();
|
|
5876
6166
|
const frontmatterScenarios = toArray(data.related_scenarios).map((value) => String(value).trim()).filter(Boolean);
|
|
5877
6167
|
const scopeFeatures = extractCodes(String(data.scope ?? ""), "feature");
|
|
5878
6168
|
const frontmatterFeatures = [.../* @__PURE__ */ new Set([...Object.keys(asRecord(data.ac_coverage)), ...scopeFeatures])];
|
|
@@ -5945,7 +6235,7 @@ function parseE2eRegistry(path, raw) {
|
|
|
5945
6235
|
data = { parseError: error.message };
|
|
5946
6236
|
}
|
|
5947
6237
|
return {
|
|
5948
|
-
nodes: [{ type: "e2e_registry", code:
|
|
6238
|
+
nodes: [{ type: "e2e_registry", code: basename4(path, extname(path)), title: "E2E Test Registry", path, line: 1, attrs: data }],
|
|
5949
6239
|
edges: []
|
|
5950
6240
|
};
|
|
5951
6241
|
}
|
|
@@ -6971,10 +7261,10 @@ function validateE2eRegistry(graph) {
|
|
|
6971
7261
|
async function validateExecutableTraceability(root, config) {
|
|
6972
7262
|
const issues = [];
|
|
6973
7263
|
const schema = config ?? await loadConfig(root);
|
|
6974
|
-
const e2eDir =
|
|
7264
|
+
const e2eDir = join8(root, "artifacts", "tests", "e2e");
|
|
6975
7265
|
let e2eFiles;
|
|
6976
7266
|
try {
|
|
6977
|
-
e2eFiles = (await readdir3(e2eDir)).filter((name) => /^test-.*\.md$/.test(name)).map((name) =>
|
|
7267
|
+
e2eFiles = (await readdir3(e2eDir)).filter((name) => /^test-.*\.md$/.test(name)).map((name) => join8(e2eDir, name));
|
|
6978
7268
|
} catch {
|
|
6979
7269
|
return [];
|
|
6980
7270
|
}
|
|
@@ -6987,7 +7277,7 @@ async function validateExecutableTraceability(root, config) {
|
|
|
6987
7277
|
const relPath = relative3(root, filePath).split("\\").join("/");
|
|
6988
7278
|
const parsed = matter(raw);
|
|
6989
7279
|
const data = parsed.data;
|
|
6990
|
-
const batch = String(data.test_batch ??
|
|
7280
|
+
const batch = String(data.test_batch ?? basename4(filePath, extname(filePath))).trim();
|
|
6991
7281
|
const lines = raw.split(/\r?\n/);
|
|
6992
7282
|
const tcStarts = [];
|
|
6993
7283
|
lines.forEach((line, index) => {
|
|
@@ -7032,7 +7322,7 @@ async function validateExecutableTraceability(root, config) {
|
|
|
7032
7322
|
const tcAnnotationRegex = /\/\/!?\s*@(?:e2e_test|tc)\s+(\S+?)\s+\[(\w+)\]/;
|
|
7033
7323
|
const tcAnnotationNoLevelRegex = /\/\/!?\s*@(?:e2e_test|tc)\s+(\S+)/;
|
|
7034
7324
|
for (const specFile of specFiles) {
|
|
7035
|
-
const fullSpecPath =
|
|
7325
|
+
const fullSpecPath = join8(root, specFile);
|
|
7036
7326
|
let content;
|
|
7037
7327
|
try {
|
|
7038
7328
|
content = await readFile3(fullSpecPath, "utf-8");
|
|
@@ -7114,7 +7404,7 @@ async function validateExecutableTraceability(root, config) {
|
|
|
7114
7404
|
if (entry.testId) {
|
|
7115
7405
|
let content;
|
|
7116
7406
|
try {
|
|
7117
|
-
content = await readFile3(
|
|
7407
|
+
content = await readFile3(join8(root, normalizedRefFile), "utf-8");
|
|
7118
7408
|
} catch {
|
|
7119
7409
|
continue;
|
|
7120
7410
|
}
|
|
@@ -7291,11 +7581,11 @@ async function computeE2eCoverageStats(graph, root, thresholds = {}) {
|
|
|
7291
7581
|
let withExecutableRef = 0;
|
|
7292
7582
|
const statusBreakdown = {};
|
|
7293
7583
|
const chainTypeBreakdown = {};
|
|
7294
|
-
const e2eDir =
|
|
7584
|
+
const e2eDir = join8(root, "artifacts", "tests", "e2e");
|
|
7295
7585
|
const tcFieldsMap = /* @__PURE__ */ new Map();
|
|
7296
7586
|
let e2eFiles;
|
|
7297
7587
|
try {
|
|
7298
|
-
e2eFiles = (await readdir3(e2eDir)).filter((name) => /^test-.*\.md$/.test(name)).map((name) =>
|
|
7588
|
+
e2eFiles = (await readdir3(e2eDir)).filter((name) => /^test-.*\.md$/.test(name)).map((name) => join8(e2eDir, name));
|
|
7299
7589
|
} catch {
|
|
7300
7590
|
e2eFiles = [];
|
|
7301
7591
|
}
|
|
@@ -7310,7 +7600,7 @@ async function computeE2eCoverageStats(graph, root, thresholds = {}) {
|
|
|
7310
7600
|
}
|
|
7311
7601
|
});
|
|
7312
7602
|
const parsed = matter(raw);
|
|
7313
|
-
const batch = String(parsed.data.test_batch ??
|
|
7603
|
+
const batch = String(parsed.data.test_batch ?? basename4(filePath, extname(filePath))).trim();
|
|
7314
7604
|
for (let i = 0; i < tcStarts.length; i++) {
|
|
7315
7605
|
const start = tcStarts[i];
|
|
7316
7606
|
const end = tcStarts[i + 1]?.index ?? lines.length;
|
|
@@ -7373,7 +7663,7 @@ async function computeE2eCoverageStats(graph, root, thresholds = {}) {
|
|
|
7373
7663
|
let hasActiveE2eRef = false;
|
|
7374
7664
|
for (const entry of parseExecutableRefLines(execRef)) {
|
|
7375
7665
|
const normalized = resolveExecutableRefFile(entry.file, allProjectFiles);
|
|
7376
|
-
if (!normalized || !
|
|
7666
|
+
if (!normalized || !existsSync3(join8(root, normalized))) continue;
|
|
7377
7667
|
const accepting = await getAcceptingRunners(root, normalized, runners);
|
|
7378
7668
|
if (accepting.some((runner) => runner.kind === "e2e")) {
|
|
7379
7669
|
hasActiveE2eRef = true;
|
|
@@ -7432,7 +7722,7 @@ async function computeE2eCoverageStats(graph, root, thresholds = {}) {
|
|
|
7432
7722
|
const acCoverageRateByFeature = {};
|
|
7433
7723
|
const featureAcMap = /* @__PURE__ */ new Map();
|
|
7434
7724
|
for (const node of featureNodes) {
|
|
7435
|
-
const acs = parseAcceptanceCriteria(await readFile3(
|
|
7725
|
+
const acs = parseAcceptanceCriteria(await readFile3(join8(root, node.path), "utf-8"));
|
|
7436
7726
|
featureAcMap.set(node.code, new Set(acs));
|
|
7437
7727
|
}
|
|
7438
7728
|
const coveredAcByFeature = /* @__PURE__ */ new Map();
|
|
@@ -7474,7 +7764,7 @@ async function computeE2eCoverageStats(graph, root, thresholds = {}) {
|
|
|
7474
7764
|
};
|
|
7475
7765
|
}
|
|
7476
7766
|
async function generateE2eRegistry(root, opts) {
|
|
7477
|
-
const e2eDir =
|
|
7767
|
+
const e2eDir = join8(root, "artifacts", "tests", "e2e");
|
|
7478
7768
|
let files;
|
|
7479
7769
|
try {
|
|
7480
7770
|
files = (await readdir3(e2eDir)).filter((name) => /^test-.*\.md$/.test(name)).sort();
|
|
@@ -7490,11 +7780,11 @@ async function generateE2eRegistry(root, opts) {
|
|
|
7490
7780
|
const batches = [];
|
|
7491
7781
|
let totalTestCases = 0;
|
|
7492
7782
|
for (const file of files) {
|
|
7493
|
-
const filePath =
|
|
7783
|
+
const filePath = join8(e2eDir, file);
|
|
7494
7784
|
const raw = await readFile3(filePath, "utf-8");
|
|
7495
7785
|
const parsed = matter(raw);
|
|
7496
7786
|
const data = parsed.data;
|
|
7497
|
-
const batch = String(data.test_batch ??
|
|
7787
|
+
const batch = String(data.test_batch ?? basename4(file, extname(file))).trim();
|
|
7498
7788
|
const relPath = `artifacts/tests/e2e/${file}`;
|
|
7499
7789
|
const scope = String(data.scope ?? "").trim();
|
|
7500
7790
|
const acCoverage = normalizeAcCoverageForRegistry(data.ac_coverage);
|
|
@@ -7618,7 +7908,7 @@ async function validatePartialRustEvidence(tcFields, tcKey, root, allFiles) {
|
|
|
7618
7908
|
if (!normalizedPath) {
|
|
7619
7909
|
return { hasValidPartialRust: false, detail: `partial_rust file not found: ${ref.file}` };
|
|
7620
7910
|
}
|
|
7621
|
-
const fullPath =
|
|
7911
|
+
const fullPath = join8(root, normalizedPath);
|
|
7622
7912
|
let content;
|
|
7623
7913
|
try {
|
|
7624
7914
|
content = await readFile3(fullPath, "utf-8");
|
|
@@ -7730,7 +8020,7 @@ function escapeRegExp(value) {
|
|
|
7730
8020
|
}
|
|
7731
8021
|
async function hasMarkdownTc(tcKey, e2eDir) {
|
|
7732
8022
|
const [batch, tcId] = tcKey.split(":");
|
|
7733
|
-
const filePath =
|
|
8023
|
+
const filePath = join8(e2eDir, `${batch}.md`);
|
|
7734
8024
|
try {
|
|
7735
8025
|
const raw = await readFile3(filePath, "utf-8");
|
|
7736
8026
|
const tcRegex = new RegExp(`^#{2,3}\\s+${escapeRegExp(tcId)}\\s*[:\uFF1A]?`, "m");
|
|
@@ -7963,7 +8253,7 @@ function normalizeDesignCode(value) {
|
|
|
7963
8253
|
if (!raw) {
|
|
7964
8254
|
return "";
|
|
7965
8255
|
}
|
|
7966
|
-
return
|
|
8256
|
+
return basename4(raw, extname(raw));
|
|
7967
8257
|
}
|
|
7968
8258
|
function toUid(type, code) {
|
|
7969
8259
|
return `${type}:${code}`;
|
|
@@ -8228,7 +8518,7 @@ function resolveArtifactContext(graph, opts) {
|
|
|
8228
8518
|
}
|
|
8229
8519
|
if (root) {
|
|
8230
8520
|
for (const ap of ALWAYS_PRESENT_ITEMS) {
|
|
8231
|
-
const fullPath =
|
|
8521
|
+
const fullPath = join8(root, ap.path);
|
|
8232
8522
|
let stat;
|
|
8233
8523
|
try {
|
|
8234
8524
|
stat = statSync(fullPath);
|
|
@@ -8596,7 +8886,7 @@ export {
|
|
|
8596
8886
|
collectChangedPaths,
|
|
8597
8887
|
computeE2eCoverageStats,
|
|
8598
8888
|
computeRevisionDigest,
|
|
8599
|
-
|
|
8889
|
+
dirname5 as dirname,
|
|
8600
8890
|
discoverAndAuditPackets,
|
|
8601
8891
|
discoverTargets,
|
|
8602
8892
|
doctorArtifactChain,
|