qfai 0.3.3 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/assets/init/.qfai/README.md +11 -0
- package/assets/init/.qfai/promptpack/commands/implement.md +6 -0
- package/assets/init/.qfai/promptpack/commands/plan.md +9 -0
- package/assets/init/.qfai/promptpack/commands/release.md +6 -0
- package/assets/init/.qfai/promptpack/commands/review.md +6 -0
- package/assets/init/.qfai/promptpack/constitution.md +15 -0
- package/assets/init/.qfai/promptpack/modes/change.md +4 -0
- package/assets/init/.qfai/promptpack/modes/compatibility.md +4 -0
- package/assets/init/.qfai/promptpack/roles/qa.md +4 -0
- package/assets/init/.qfai/promptpack/roles/spec.md +4 -0
- package/assets/init/.qfai/promptpack/roles/test.md +4 -0
- package/assets/init/.qfai/promptpack/steering/compatibility-vs-change.md +8 -0
- package/assets/init/.qfai/promptpack/steering/naming.md +7 -0
- package/assets/init/.qfai/promptpack/steering/traceability.md +9 -0
- package/assets/init/.qfai/prompts/require-to-spec.md +4 -4
- package/assets/init/.qfai/require/README.md +28 -0
- package/assets/init/.qfai/rules/conventions.md +6 -0
- package/dist/cli/commands/init.d.ts +8 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +30 -0
- package/dist/cli/commands/init.js.map +1 -0
- package/dist/cli/commands/report.d.ts +7 -0
- package/dist/cli/commands/report.d.ts.map +1 -0
- package/dist/cli/commands/report.js +87 -0
- package/dist/cli/commands/report.js.map +1 -0
- package/dist/cli/commands/validate.d.ts +9 -0
- package/dist/cli/commands/validate.d.ts.map +1 -0
- package/dist/cli/commands/validate.js +57 -0
- package/dist/cli/commands/validate.js.map +1 -0
- package/dist/cli/index.cjs +2 -2
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/index.mjs +2 -2
- package/dist/cli/lib/args.d.ts +18 -0
- package/dist/cli/lib/args.d.ts.map +1 -0
- package/dist/cli/lib/args.js +98 -0
- package/dist/cli/lib/args.js.map +1 -0
- package/dist/cli/lib/assets.d.ts +2 -0
- package/dist/cli/lib/assets.d.ts.map +1 -0
- package/dist/cli/lib/assets.js +24 -0
- package/dist/cli/lib/assets.js.map +1 -0
- package/dist/cli/lib/failOn.d.ts +5 -0
- package/dist/cli/lib/failOn.d.ts.map +1 -0
- package/dist/cli/lib/failOn.js +10 -0
- package/dist/cli/lib/failOn.js.map +1 -0
- package/dist/cli/lib/fs.d.ts +11 -0
- package/dist/cli/lib/fs.d.ts.map +1 -0
- package/dist/cli/lib/fs.js +91 -0
- package/dist/cli/lib/fs.js.map +1 -0
- package/dist/cli/lib/logger.d.ts +4 -0
- package/dist/cli/lib/logger.d.ts.map +1 -0
- package/dist/cli/lib/logger.js +10 -0
- package/dist/cli/lib/logger.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.d.ts.map +1 -0
- package/dist/cli/main.js +66 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/core/config.d.ts +44 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +218 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/contractIndex.d.ts +13 -0
- package/dist/core/contractIndex.d.ts.map +1 -0
- package/dist/core/contractIndex.js +66 -0
- package/dist/core/contractIndex.js.map +1 -0
- package/dist/core/contracts.d.ts +5 -0
- package/dist/core/contracts.d.ts.map +1 -0
- package/dist/core/contracts.js +42 -0
- package/dist/core/contracts.js.map +1 -0
- package/dist/core/discovery.d.ts +14 -0
- package/dist/core/discovery.d.ts.map +1 -0
- package/dist/core/discovery.js +55 -0
- package/dist/core/discovery.js.map +1 -0
- package/dist/core/fs.d.ts +6 -0
- package/dist/core/fs.d.ts.map +1 -0
- package/dist/core/fs.js +55 -0
- package/dist/core/fs.js.map +1 -0
- package/dist/core/gherkin/parse.d.ts +7 -0
- package/dist/core/gherkin/parse.d.ts.map +1 -0
- package/dist/core/gherkin/parse.js +25 -0
- package/dist/core/gherkin/parse.js.map +1 -0
- package/dist/core/ids.d.ts +6 -0
- package/dist/core/ids.d.ts.map +1 -0
- package/dist/core/ids.js +52 -0
- package/dist/core/ids.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/parse/adr.d.ts +13 -0
- package/dist/core/parse/adr.d.ts.map +1 -0
- package/dist/core/parse/adr.js +33 -0
- package/dist/core/parse/adr.js.map +1 -0
- package/dist/core/parse/gherkin.d.ts +12 -0
- package/dist/core/parse/gherkin.d.ts.map +1 -0
- package/dist/core/parse/gherkin.js +22 -0
- package/dist/core/parse/gherkin.js.map +1 -0
- package/dist/core/parse/markdown.d.ts +14 -0
- package/dist/core/parse/markdown.d.ts.map +1 -0
- package/dist/core/parse/markdown.js +45 -0
- package/dist/core/parse/markdown.js.map +1 -0
- package/dist/core/parse/spec.d.ts +28 -0
- package/dist/core/parse/spec.d.ts.map +1 -0
- package/dist/core/parse/spec.js +80 -0
- package/dist/core/parse/spec.js.map +1 -0
- package/dist/core/report.d.ts +39 -0
- package/dist/core/report.d.ts.map +1 -0
- package/dist/core/report.js +226 -0
- package/dist/core/report.js.map +1 -0
- package/dist/core/scenarioModel.d.ts +33 -0
- package/dist/core/scenarioModel.d.ts.map +1 -0
- package/dist/core/scenarioModel.js +130 -0
- package/dist/core/scenarioModel.js.map +1 -0
- package/dist/core/specLayout.d.ts +8 -0
- package/dist/core/specLayout.d.ts.map +1 -0
- package/dist/core/specLayout.js +36 -0
- package/dist/core/specLayout.js.map +1 -0
- package/dist/core/types.d.ts +27 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/validate.d.ts +4 -0
- package/dist/core/validate.d.ts.map +1 -0
- package/dist/core/validate.js +36 -0
- package/dist/core/validate.js.map +1 -0
- package/dist/core/validators/contracts.d.ts +5 -0
- package/dist/core/validators/contracts.d.ts.map +1 -0
- package/dist/core/validators/contracts.js +162 -0
- package/dist/core/validators/contracts.js.map +1 -0
- package/dist/core/validators/delta.d.ts +4 -0
- package/dist/core/validators/delta.d.ts.map +1 -0
- package/dist/core/validators/delta.js +68 -0
- package/dist/core/validators/delta.js.map +1 -0
- package/dist/core/validators/ids.d.ts +4 -0
- package/dist/core/validators/ids.d.ts.map +1 -0
- package/dist/core/validators/ids.js +88 -0
- package/dist/core/validators/ids.js.map +1 -0
- package/dist/core/validators/scenario.d.ts +5 -0
- package/dist/core/validators/scenario.d.ts.map +1 -0
- package/dist/core/validators/scenario.js +137 -0
- package/dist/core/validators/scenario.js.map +1 -0
- package/dist/core/validators/spec.d.ts +5 -0
- package/dist/core/validators/spec.d.ts.map +1 -0
- package/dist/core/validators/spec.js +94 -0
- package/dist/core/validators/spec.js.map +1 -0
- package/dist/core/validators/traceability.d.ts +4 -0
- package/dist/core/validators/traceability.d.ts.map +1 -0
- package/dist/core/validators/traceability.js +180 -0
- package/dist/core/validators/traceability.js.map +1 -0
- package/dist/core/version.d.ts +2 -0
- package/dist/core/version.d.ts.map +1 -0
- package/dist/core/version.js +25 -0
- package/dist/core/version.js.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.d.ts +2 -133
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -2
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
- package/assets/init/root/require/README.md +0 -28
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { resolvePath } from "../config.js";
|
|
3
|
+
import { extractIds, extractInvalidIds } from "../ids.js";
|
|
4
|
+
import { parseSpec } from "../parse/spec.js";
|
|
5
|
+
import { collectSpecEntries } from "../specLayout.js";
|
|
6
|
+
export async function validateSpecs(root, config) {
|
|
7
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
8
|
+
const entries = await collectSpecEntries(specsRoot);
|
|
9
|
+
if (entries.length === 0) {
|
|
10
|
+
const expected = "spec-0001/spec.md";
|
|
11
|
+
const legacy = "spec-001/spec.md";
|
|
12
|
+
return [
|
|
13
|
+
issue("QFAI-SPEC-000", `Spec ファイルが見つかりません。配置場所: ${config.paths.specsDir} / 期待パターン: ${expected} (${legacy} は非対応)`, "info", specsRoot, "spec.files"),
|
|
14
|
+
];
|
|
15
|
+
}
|
|
16
|
+
const issues = [];
|
|
17
|
+
for (const entry of entries) {
|
|
18
|
+
let text;
|
|
19
|
+
try {
|
|
20
|
+
text = await readFile(entry.specPath, "utf-8");
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (isMissingFileError(error)) {
|
|
24
|
+
issues.push(issue("QFAI-SPEC-005", "spec.md が見つかりません。", "error", entry.specPath, "spec.exists"));
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
issues.push(...validateSpecContent(text, entry.specPath, config.validation.require.specSections));
|
|
30
|
+
}
|
|
31
|
+
return issues;
|
|
32
|
+
}
|
|
33
|
+
export function validateSpecContent(text, file, requiredSections) {
|
|
34
|
+
const issues = [];
|
|
35
|
+
const parsed = parseSpec(text, file);
|
|
36
|
+
const invalidIds = extractInvalidIds(text, [
|
|
37
|
+
"SPEC",
|
|
38
|
+
"BR",
|
|
39
|
+
"SC",
|
|
40
|
+
"UI",
|
|
41
|
+
"API",
|
|
42
|
+
"DATA",
|
|
43
|
+
"ADR",
|
|
44
|
+
]);
|
|
45
|
+
if (invalidIds.length > 0) {
|
|
46
|
+
issues.push(issue("QFAI-ID-002", `ID フォーマットが不正です: ${invalidIds.join(", ")}`, "error", file, "id.format", invalidIds));
|
|
47
|
+
}
|
|
48
|
+
if (!parsed.specId) {
|
|
49
|
+
issues.push(issue("QFAI-SPEC-001", "SPEC ID が見つかりません。", "error", file, "spec.id"));
|
|
50
|
+
}
|
|
51
|
+
if (parsed.brs.length === 0) {
|
|
52
|
+
issues.push(issue("QFAI-SPEC-002", "BR ID が見つかりません。", "error", file, "spec.br"));
|
|
53
|
+
}
|
|
54
|
+
for (const br of parsed.brsWithoutPriority) {
|
|
55
|
+
issues.push(issue("QFAI-BR-001", `BR 行に Priority がありません: ${br.id}`, "error", file, "spec.brPriority", [br.id]));
|
|
56
|
+
}
|
|
57
|
+
for (const br of parsed.brsWithInvalidPriority) {
|
|
58
|
+
issues.push(issue("QFAI-BR-002", `BR Priority が不正です: ${br.id} (${br.priority})`, "error", file, "spec.brPriority", [br.id]));
|
|
59
|
+
}
|
|
60
|
+
const scIds = extractIds(text, "SC");
|
|
61
|
+
if (scIds.length > 0) {
|
|
62
|
+
issues.push(issue("QFAI-SPEC-003", "Spec は SC を参照しないルールです。", "warning", file, "spec.noSc", scIds));
|
|
63
|
+
}
|
|
64
|
+
for (const section of requiredSections) {
|
|
65
|
+
if (!parsed.sections.has(section)) {
|
|
66
|
+
issues.push(issue("QFAI-SPEC-004", `必須セクションが不足しています: ${section}`, "error", file, "spec.requiredSection"));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return issues;
|
|
70
|
+
}
|
|
71
|
+
function issue(code, message, severity, file, rule, refs) {
|
|
72
|
+
const issue = {
|
|
73
|
+
code,
|
|
74
|
+
severity,
|
|
75
|
+
message,
|
|
76
|
+
};
|
|
77
|
+
if (file) {
|
|
78
|
+
issue.file = file;
|
|
79
|
+
}
|
|
80
|
+
if (rule) {
|
|
81
|
+
issue.rule = rule;
|
|
82
|
+
}
|
|
83
|
+
if (refs && refs.length > 0) {
|
|
84
|
+
issue.refs = refs;
|
|
85
|
+
}
|
|
86
|
+
return issue;
|
|
87
|
+
}
|
|
88
|
+
function isMissingFileError(error) {
|
|
89
|
+
if (!error || typeof error !== "object") {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return error.code === "ENOENT";
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec.js","sourceRoot":"","sources":["../../../src/core/validators/spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traceability.d.ts","sourceRoot":"","sources":["../../../src/core/validators/traceability.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAQ/C,OAAO,KAAK,EAAE,KAAK,EAAiB,MAAM,aAAa,CAAC;AAMxD,wBAAsB,oBAAoB,CACxC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,KAAK,EAAE,CAAC,CAsPlB"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { resolvePath } from "../config.js";
|
|
3
|
+
import { buildContractIndex } from "../contractIndex.js";
|
|
4
|
+
import { collectScenarioFiles, collectSpecFiles } from "../discovery.js";
|
|
5
|
+
import { collectFiles } from "../fs.js";
|
|
6
|
+
import { extractAllIds, extractIds } from "../ids.js";
|
|
7
|
+
import { parseSpec } from "../parse/spec.js";
|
|
8
|
+
import { buildScenarioAtoms, parseScenarioDocument } from "../scenarioModel.js";
|
|
9
|
+
const SC_TAG_RE = /^SC-\d{4}$/;
|
|
10
|
+
const SPEC_TAG_RE = /^SPEC-\d{4}$/;
|
|
11
|
+
const BR_TAG_RE = /^BR-\d{4}$/;
|
|
12
|
+
export async function validateTraceability(root, config) {
|
|
13
|
+
const issues = [];
|
|
14
|
+
const specsRoot = resolvePath(root, config, "specsDir");
|
|
15
|
+
const srcRoot = resolvePath(root, config, "srcDir");
|
|
16
|
+
const testsRoot = resolvePath(root, config, "testsDir");
|
|
17
|
+
const specFiles = await collectSpecFiles(specsRoot);
|
|
18
|
+
const scenarioFiles = await collectScenarioFiles(specsRoot);
|
|
19
|
+
const upstreamIds = new Set();
|
|
20
|
+
const specIds = new Set();
|
|
21
|
+
const brIdsInSpecs = new Set();
|
|
22
|
+
const brIdsInScenarios = new Set();
|
|
23
|
+
const scIdsInScenarios = new Set();
|
|
24
|
+
const scenarioContractIds = new Set();
|
|
25
|
+
const scWithContracts = new Set();
|
|
26
|
+
const specToBrIds = new Map();
|
|
27
|
+
const contractIndex = await buildContractIndex(root, config);
|
|
28
|
+
const contractIds = contractIndex.ids;
|
|
29
|
+
for (const file of specFiles) {
|
|
30
|
+
const text = await readFile(file, "utf-8");
|
|
31
|
+
extractAllIds(text).forEach((id) => upstreamIds.add(id));
|
|
32
|
+
const parsed = parseSpec(text, file);
|
|
33
|
+
if (parsed.specId) {
|
|
34
|
+
specIds.add(parsed.specId);
|
|
35
|
+
}
|
|
36
|
+
const brIds = parsed.brs.map((br) => br.id);
|
|
37
|
+
brIds.forEach((id) => brIdsInSpecs.add(id));
|
|
38
|
+
const referencedContractIds = new Set([
|
|
39
|
+
...extractIds(text, "UI"),
|
|
40
|
+
...extractIds(text, "API"),
|
|
41
|
+
...extractIds(text, "DATA"),
|
|
42
|
+
]);
|
|
43
|
+
const unknownContractIds = Array.from(referencedContractIds).filter((id) => !contractIds.has(id));
|
|
44
|
+
if (unknownContractIds.length > 0) {
|
|
45
|
+
issues.push(issue("QFAI-TRACE-009", `Spec が存在しない契約 ID を参照しています: ${unknownContractIds.join(", ")}`, "error", file, "traceability.specContractExists", unknownContractIds));
|
|
46
|
+
}
|
|
47
|
+
if (parsed.specId) {
|
|
48
|
+
const current = specToBrIds.get(parsed.specId) ?? new Set();
|
|
49
|
+
brIds.forEach((id) => current.add(id));
|
|
50
|
+
specToBrIds.set(parsed.specId, current);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
for (const file of scenarioFiles) {
|
|
54
|
+
const text = await readFile(file, "utf-8");
|
|
55
|
+
extractAllIds(text).forEach((id) => upstreamIds.add(id));
|
|
56
|
+
const { document, errors } = parseScenarioDocument(text, file);
|
|
57
|
+
if (!document || errors.length > 0) {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const atoms = buildScenarioAtoms(document);
|
|
61
|
+
for (const [index, scenario] of document.scenarios.entries()) {
|
|
62
|
+
const atom = atoms[index];
|
|
63
|
+
if (!atom) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const specTags = scenario.tags.filter((tag) => SPEC_TAG_RE.test(tag));
|
|
67
|
+
const brTags = scenario.tags.filter((tag) => BR_TAG_RE.test(tag));
|
|
68
|
+
const scTags = scenario.tags.filter((tag) => SC_TAG_RE.test(tag));
|
|
69
|
+
brTags.forEach((id) => brIdsInScenarios.add(id));
|
|
70
|
+
scTags.forEach((id) => scIdsInScenarios.add(id));
|
|
71
|
+
atom.contractIds.forEach((id) => scenarioContractIds.add(id));
|
|
72
|
+
if (atom.contractIds.length > 0) {
|
|
73
|
+
scTags.forEach((id) => scWithContracts.add(id));
|
|
74
|
+
}
|
|
75
|
+
const unknownSpecIds = specTags.filter((id) => !specIds.has(id));
|
|
76
|
+
if (unknownSpecIds.length > 0) {
|
|
77
|
+
issues.push(issue("QFAI-TRACE-005", `Scenario が存在しない SPEC を参照しています: ${unknownSpecIds.join(", ")} (${scenario.name})`, "error", file, "traceability.scenarioSpecExists", unknownSpecIds));
|
|
78
|
+
}
|
|
79
|
+
const unknownBrIds = brTags.filter((id) => !brIdsInSpecs.has(id));
|
|
80
|
+
if (unknownBrIds.length > 0) {
|
|
81
|
+
issues.push(issue("QFAI-TRACE-006", `Scenario が存在しない BR を参照しています: ${unknownBrIds.join(", ")} (${scenario.name})`, "error", file, "traceability.scenarioBrExists", unknownBrIds));
|
|
82
|
+
}
|
|
83
|
+
const unknownContractIds = atom.contractIds.filter((id) => !contractIds.has(id));
|
|
84
|
+
if (unknownContractIds.length > 0) {
|
|
85
|
+
issues.push(issue("QFAI-TRACE-008", `Scenario が存在しない契約 ID を参照しています: ${unknownContractIds.join(", ")} (${scenario.name})`, config.validation.traceability.unknownContractIdSeverity, file, "traceability.scenarioContractExists", unknownContractIds));
|
|
86
|
+
}
|
|
87
|
+
if (specTags.length > 0 && brTags.length > 0) {
|
|
88
|
+
const allowedBrIds = new Set();
|
|
89
|
+
for (const specId of specTags) {
|
|
90
|
+
const brIdsForSpec = specToBrIds.get(specId);
|
|
91
|
+
if (!brIdsForSpec) {
|
|
92
|
+
continue;
|
|
93
|
+
}
|
|
94
|
+
brIdsForSpec.forEach((id) => allowedBrIds.add(id));
|
|
95
|
+
}
|
|
96
|
+
const invalidBrIds = brTags.filter((id) => !allowedBrIds.has(id));
|
|
97
|
+
if (invalidBrIds.length > 0) {
|
|
98
|
+
issues.push(issue("QFAI-TRACE-007", `Scenario の BR が参照 SPEC に属していません: ${invalidBrIds.join(", ")} (SPEC: ${specTags.join(", ")}) (${scenario.name})`, "error", file, "traceability.scenarioBrUnderSpec", invalidBrIds));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (upstreamIds.size === 0) {
|
|
104
|
+
return [
|
|
105
|
+
issue("QFAI-TRACE-000", "上流 ID が見つかりません。", "info", specsRoot, "traceability.upstream"),
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
if (config.validation.traceability.brMustHaveSc && brIdsInSpecs.size > 0) {
|
|
109
|
+
const orphanBrIds = Array.from(brIdsInSpecs).filter((id) => !brIdsInScenarios.has(id));
|
|
110
|
+
if (orphanBrIds.length > 0) {
|
|
111
|
+
issues.push(issue("QFAI_TRACE_BR_ORPHAN", `BR が SC に紐づいていません: ${orphanBrIds.join(", ")}`, "error", specsRoot, "traceability.brMustHaveSc", orphanBrIds));
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (config.validation.traceability.scMustTouchContracts &&
|
|
115
|
+
scIdsInScenarios.size > 0) {
|
|
116
|
+
const scWithoutContracts = Array.from(scIdsInScenarios).filter((id) => !scWithContracts.has(id));
|
|
117
|
+
if (scWithoutContracts.length > 0) {
|
|
118
|
+
issues.push(issue("QFAI_TRACE_SC_NO_CONTRACT", `SC が契約(UI/API/DATA)に接続していません: ${scWithoutContracts.join(", ")}`, "error", specsRoot, "traceability.scMustTouchContracts", scWithoutContracts));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
if (!config.validation.traceability.allowOrphanContracts) {
|
|
122
|
+
if (contractIds.size > 0) {
|
|
123
|
+
const orphanContracts = Array.from(contractIds).filter((id) => !scenarioContractIds.has(id));
|
|
124
|
+
if (orphanContracts.length > 0) {
|
|
125
|
+
issues.push(issue("QFAI_CONTRACT_ORPHAN", `契約が SC から参照されていません: ${orphanContracts.join(", ")}`, "error", specsRoot, "traceability.allowOrphanContracts", orphanContracts));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
issues.push(...(await validateCodeReferences(upstreamIds, srcRoot, testsRoot)));
|
|
130
|
+
return issues;
|
|
131
|
+
}
|
|
132
|
+
async function validateCodeReferences(upstreamIds, srcRoot, testsRoot) {
|
|
133
|
+
const issues = [];
|
|
134
|
+
const codeFiles = await collectFiles(srcRoot, {
|
|
135
|
+
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
|
136
|
+
});
|
|
137
|
+
const testFiles = await collectFiles(testsRoot, {
|
|
138
|
+
extensions: [".ts", ".tsx", ".js", ".jsx"],
|
|
139
|
+
});
|
|
140
|
+
const targetFiles = [...codeFiles, ...testFiles];
|
|
141
|
+
if (targetFiles.length === 0) {
|
|
142
|
+
issues.push(issue("QFAI-TRACE-001", "参照対象のコード/テストが見つかりません。", "info", srcRoot, "traceability.codeReferences"));
|
|
143
|
+
return issues;
|
|
144
|
+
}
|
|
145
|
+
const pattern = buildIdPattern(Array.from(upstreamIds));
|
|
146
|
+
let found = false;
|
|
147
|
+
for (const file of targetFiles) {
|
|
148
|
+
const text = await readFile(file, "utf-8");
|
|
149
|
+
if (pattern.test(text)) {
|
|
150
|
+
found = true;
|
|
151
|
+
break;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (!found) {
|
|
155
|
+
issues.push(issue("QFAI-TRACE-002", "上流 ID がコード/テストに参照されていません。", "warning", srcRoot, "traceability.codeReferences"));
|
|
156
|
+
}
|
|
157
|
+
return issues;
|
|
158
|
+
}
|
|
159
|
+
function buildIdPattern(ids) {
|
|
160
|
+
const escaped = ids.map((id) => id.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
161
|
+
return new RegExp(`\\b(${escaped.join("|")})\\b`);
|
|
162
|
+
}
|
|
163
|
+
function issue(code, message, severity, file, rule, refs) {
|
|
164
|
+
const issue = {
|
|
165
|
+
code,
|
|
166
|
+
severity,
|
|
167
|
+
message,
|
|
168
|
+
};
|
|
169
|
+
if (file) {
|
|
170
|
+
issue.file = file;
|
|
171
|
+
}
|
|
172
|
+
if (rule) {
|
|
173
|
+
issue.rule = rule;
|
|
174
|
+
}
|
|
175
|
+
if (refs && refs.length > 0) {
|
|
176
|
+
issue.refs = refs;
|
|
177
|
+
}
|
|
178
|
+
return issue;
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=traceability.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"traceability.js","sourceRoot":"","sources":["../../../src/core/validators/traceability.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAMA,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,MAAM,CAAC,CAiB1D"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
export async function resolveToolVersion() {
|
|
5
|
+
if (typeof __QFAI_TOOL_VERSION__ === "string" &&
|
|
6
|
+
__QFAI_TOOL_VERSION__.length > 0) {
|
|
7
|
+
return __QFAI_TOOL_VERSION__;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const packagePath = resolvePackageJsonPath();
|
|
11
|
+
const raw = await readFile(packagePath, "utf-8");
|
|
12
|
+
const parsed = JSON.parse(raw);
|
|
13
|
+
const version = typeof parsed.version === "string" ? parsed.version : "";
|
|
14
|
+
return version.length > 0 ? version : "unknown";
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return "unknown";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function resolvePackageJsonPath() {
|
|
21
|
+
const base = import.meta.url;
|
|
22
|
+
const basePath = base.startsWith("file:") ? fileURLToPath(base) : base;
|
|
23
|
+
return path.resolve(path.dirname(basePath), "../../package.json");
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/core/version.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAIzC,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,IACE,OAAO,qBAAqB,KAAK,QAAQ;QACzC,qBAAqB,CAAC,MAAM,GAAG,CAAC,EAChC,CAAC;QACD,OAAO,qBAAqB,CAAC;IAC/B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,sBAAsB,EAAE,CAAC;QAC7C,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAA0B,CAAC;QACxD,MAAM,OAAO,GAAG,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,sBAAsB;IAC7B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC;IAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACvE,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,oBAAoB,CAAC,CAAC;AACpE,CAAC"}
|
package/dist/index.cjs
CHANGED
|
@@ -591,8 +591,8 @@ var import_promises5 = require("fs/promises");
|
|
|
591
591
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
592
592
|
var import_node_url = require("url");
|
|
593
593
|
async function resolveToolVersion() {
|
|
594
|
-
if ("0.3.
|
|
595
|
-
return "0.3.
|
|
594
|
+
if ("0.3.5".length > 0) {
|
|
595
|
+
return "0.3.5";
|
|
596
596
|
}
|
|
597
597
|
try {
|
|
598
598
|
const packagePath = resolvePackageJsonPath();
|
package/dist/index.d.ts
CHANGED
|
@@ -1,133 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
type IssueLocation = {
|
|
4
|
-
line: number;
|
|
5
|
-
column?: number;
|
|
6
|
-
};
|
|
7
|
-
type Issue = {
|
|
8
|
-
code: string;
|
|
9
|
-
severity: IssueSeverity;
|
|
10
|
-
message: string;
|
|
11
|
-
file?: string;
|
|
12
|
-
refs?: string[];
|
|
13
|
-
rule?: string;
|
|
14
|
-
loc?: IssueLocation;
|
|
15
|
-
};
|
|
16
|
-
type ValidationCounts = {
|
|
17
|
-
info: number;
|
|
18
|
-
warning: number;
|
|
19
|
-
error: number;
|
|
20
|
-
};
|
|
21
|
-
type ValidationResult = {
|
|
22
|
-
schemaVersion: typeof VALIDATION_SCHEMA_VERSION;
|
|
23
|
-
toolVersion: string;
|
|
24
|
-
issues: Issue[];
|
|
25
|
-
counts: ValidationCounts;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
type FailOn = "never" | "warning" | "error";
|
|
29
|
-
type OutputFormat = "text" | "github";
|
|
30
|
-
type TraceabilitySeverity = "warning" | "error";
|
|
31
|
-
type QfaiPaths = {
|
|
32
|
-
contractsDir: string;
|
|
33
|
-
specsDir: string;
|
|
34
|
-
rulesDir: string;
|
|
35
|
-
outDir: string;
|
|
36
|
-
promptsDir: string;
|
|
37
|
-
srcDir: string;
|
|
38
|
-
testsDir: string;
|
|
39
|
-
};
|
|
40
|
-
type QfaiValidationConfig = {
|
|
41
|
-
failOn: FailOn;
|
|
42
|
-
require: {
|
|
43
|
-
specSections: string[];
|
|
44
|
-
};
|
|
45
|
-
traceability: {
|
|
46
|
-
brMustHaveSc: boolean;
|
|
47
|
-
scMustTouchContracts: boolean;
|
|
48
|
-
allowOrphanContracts: boolean;
|
|
49
|
-
unknownContractIdSeverity: TraceabilitySeverity;
|
|
50
|
-
};
|
|
51
|
-
};
|
|
52
|
-
type QfaiOutputConfig = {
|
|
53
|
-
validateJsonPath: string;
|
|
54
|
-
};
|
|
55
|
-
type QfaiConfig = {
|
|
56
|
-
paths: QfaiPaths;
|
|
57
|
-
validation: QfaiValidationConfig;
|
|
58
|
-
output: QfaiOutputConfig;
|
|
59
|
-
};
|
|
60
|
-
type ConfigPathKey = keyof QfaiPaths;
|
|
61
|
-
type ConfigLoadResult = {
|
|
62
|
-
config: QfaiConfig;
|
|
63
|
-
issues: Issue[];
|
|
64
|
-
configPath: string;
|
|
65
|
-
};
|
|
66
|
-
declare const defaultConfig: QfaiConfig;
|
|
67
|
-
declare function getConfigPath(root: string): string;
|
|
68
|
-
declare function loadConfig(root: string): Promise<ConfigLoadResult>;
|
|
69
|
-
declare function resolvePath(root: string, config: QfaiConfig, key: ConfigPathKey): string;
|
|
70
|
-
|
|
71
|
-
type IdPrefix = "SPEC" | "BR" | "SC" | "UI" | "API" | "DATA";
|
|
72
|
-
type IdFormatPrefix = IdPrefix | "ADR";
|
|
73
|
-
declare function extractIds(text: string, prefix: IdPrefix): string[];
|
|
74
|
-
declare function extractAllIds(text: string): string[];
|
|
75
|
-
declare function extractInvalidIds(text: string, prefixes: IdFormatPrefix[]): string[];
|
|
76
|
-
|
|
77
|
-
type ReportSummary = {
|
|
78
|
-
specs: number;
|
|
79
|
-
scenarios: number;
|
|
80
|
-
contracts: {
|
|
81
|
-
api: number;
|
|
82
|
-
ui: number;
|
|
83
|
-
db: number;
|
|
84
|
-
};
|
|
85
|
-
counts: ValidationCounts;
|
|
86
|
-
};
|
|
87
|
-
type ReportIds = {
|
|
88
|
-
spec: string[];
|
|
89
|
-
br: string[];
|
|
90
|
-
sc: string[];
|
|
91
|
-
ui: string[];
|
|
92
|
-
api: string[];
|
|
93
|
-
data: string[];
|
|
94
|
-
};
|
|
95
|
-
type ReportTraceability = {
|
|
96
|
-
upstreamIdsFound: number;
|
|
97
|
-
referencedInCodeOrTests: boolean;
|
|
98
|
-
};
|
|
99
|
-
type ReportData = {
|
|
100
|
-
tool: "qfai";
|
|
101
|
-
version: string;
|
|
102
|
-
generatedAt: string;
|
|
103
|
-
root: string;
|
|
104
|
-
configPath: string;
|
|
105
|
-
summary: ReportSummary;
|
|
106
|
-
ids: ReportIds;
|
|
107
|
-
traceability: ReportTraceability;
|
|
108
|
-
issues: Issue[];
|
|
109
|
-
};
|
|
110
|
-
declare function createReportData(root: string, validation?: ValidationResult, configResult?: ConfigLoadResult): Promise<ReportData>;
|
|
111
|
-
declare function formatReportMarkdown(data: ReportData): string;
|
|
112
|
-
declare function formatReportJson(data: ReportData): string;
|
|
113
|
-
|
|
114
|
-
declare function validateProject(root: string, configResult?: ConfigLoadResult): Promise<ValidationResult>;
|
|
115
|
-
|
|
116
|
-
declare function resolveToolVersion(): Promise<string>;
|
|
117
|
-
|
|
118
|
-
declare function validateContracts(root: string, config: QfaiConfig): Promise<Issue[]>;
|
|
119
|
-
declare function lintSql(text: string, file: string): Issue[];
|
|
120
|
-
|
|
121
|
-
declare function validateDeltas(root: string, config: QfaiConfig): Promise<Issue[]>;
|
|
122
|
-
|
|
123
|
-
declare function validateDefinedIds(root: string, config: QfaiConfig): Promise<Issue[]>;
|
|
124
|
-
|
|
125
|
-
declare function validateScenarios(root: string, config: QfaiConfig): Promise<Issue[]>;
|
|
126
|
-
declare function validateScenarioContent(text: string, file: string): Issue[];
|
|
127
|
-
|
|
128
|
-
declare function validateSpecs(root: string, config: QfaiConfig): Promise<Issue[]>;
|
|
129
|
-
declare function validateSpecContent(text: string, file: string, requiredSections: string[]): Issue[];
|
|
130
|
-
|
|
131
|
-
declare function validateTraceability(root: string, config: QfaiConfig): Promise<Issue[]>;
|
|
132
|
-
|
|
133
|
-
export { type ConfigLoadResult, type ConfigPathKey, type FailOn, type IdFormatPrefix, type IdPrefix, type Issue, type IssueLocation, type IssueSeverity, type OutputFormat, type QfaiConfig, type QfaiOutputConfig, type QfaiPaths, type QfaiValidationConfig, type ReportData, type ReportIds, type ReportSummary, type ReportTraceability, type TraceabilitySeverity, VALIDATION_SCHEMA_VERSION, type ValidationCounts, type ValidationResult, createReportData, defaultConfig, extractAllIds, extractIds, extractInvalidIds, formatReportJson, formatReportMarkdown, getConfigPath, lintSql, loadConfig, resolvePath, resolveToolVersion, validateContracts, validateDefinedIds, validateDeltas, validateProject, validateScenarioContent, validateScenarios, validateSpecContent, validateSpecs, validateTraceability };
|
|
1
|
+
export * from "./core/index.js";
|
|
2
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
|
package/dist/index.mjs
CHANGED
|
@@ -534,8 +534,8 @@ import { readFile as readFile2 } from "fs/promises";
|
|
|
534
534
|
import path4 from "path";
|
|
535
535
|
import { fileURLToPath } from "url";
|
|
536
536
|
async function resolveToolVersion() {
|
|
537
|
-
if ("0.3.
|
|
538
|
-
return "0.3.
|
|
537
|
+
if ("0.3.5".length > 0) {
|
|
538
|
+
return "0.3.5";
|
|
539
539
|
}
|
|
540
540
|
try {
|
|
541
541
|
const packagePath = resolvePackageJsonPath();
|