kibi-cli 0.10.0 → 0.11.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/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +11 -0
- package/dist/commands/check.d.ts.map +1 -1
- package/dist/commands/check.js +204 -3
- package/dist/commands/init-helpers.d.ts.map +1 -1
- package/dist/commands/init-helpers.js +11 -14
- package/dist/commands/sync/manifest.d.ts +8 -2
- package/dist/commands/sync/manifest.d.ts.map +1 -1
- package/dist/commands/sync/manifest.js +56 -11
- package/dist/commands/sync.d.ts +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +9 -7
- package/dist/commands/usage-metrics.d.ts +8 -0
- package/dist/commands/usage-metrics.d.ts.map +1 -0
- package/dist/commands/usage-metrics.js +323 -0
- package/dist/extractors/manifest.d.ts +30 -0
- package/dist/extractors/manifest.d.ts.map +1 -1
- package/dist/extractors/manifest.js +60 -7
- package/dist/extractors/symbol-coordinates.d.ts +15 -0
- package/dist/extractors/symbol-coordinates.d.ts.map +1 -0
- package/dist/extractors/symbol-coordinates.js +83 -0
- package/dist/public/extractors/manifest.d.ts +1 -1
- package/dist/public/extractors/manifest.d.ts.map +1 -1
- package/dist/public/extractors/manifest.js +1 -1
- package/dist/traceability/evidence-model.d.ts +142 -0
- package/dist/traceability/evidence-model.d.ts.map +1 -0
- package/dist/traceability/evidence-model.js +70 -0
- package/dist/traceability/git-staged.d.ts +1 -0
- package/dist/traceability/git-staged.d.ts.map +1 -1
- package/dist/traceability/git-staged.js +28 -3
- package/dist/traceability/staged-diagnostics.d.ts +25 -0
- package/dist/traceability/staged-diagnostics.d.ts.map +1 -0
- package/dist/traceability/staged-diagnostics.js +67 -0
- package/dist/traceability/staged-impact-contract.d.ts +57 -0
- package/dist/traceability/staged-impact-contract.d.ts.map +1 -0
- package/dist/traceability/staged-impact-contract.js +202 -0
- package/dist/traceability/staged-symbols-manifest.d.ts +23 -0
- package/dist/traceability/staged-symbols-manifest.d.ts.map +1 -0
- package/dist/traceability/staged-symbols-manifest.js +269 -0
- package/dist/traceability/symbol-extract.d.ts.map +1 -1
- package/dist/traceability/symbol-extract.js +33 -9
- package/dist/utils/manifest-paths.d.ts +8 -0
- package/dist/utils/manifest-paths.d.ts.map +1 -0
- package/dist/utils/manifest-paths.js +62 -0
- package/package.json +1 -1
- package/src/public/extractors/manifest.ts +2 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { StagedFile } from "./git-staged.js";
|
|
2
|
+
export interface StagedSymbolsManifestAssessment {
|
|
3
|
+
state: "fresh" | "stale" | "missing" | "not_required";
|
|
4
|
+
sourcePaths: string[];
|
|
5
|
+
path: string;
|
|
6
|
+
}
|
|
7
|
+
export interface StagedAuthoredSymbolsManifestEvidence {
|
|
8
|
+
path: string;
|
|
9
|
+
entries: Array<{
|
|
10
|
+
sourcePath: string;
|
|
11
|
+
entityIds: string[];
|
|
12
|
+
}>;
|
|
13
|
+
}
|
|
14
|
+
export declare function assessStagedSymbolsManifest(options: {
|
|
15
|
+
symbolsManifestPath: string;
|
|
16
|
+
sourceFiles: StagedFile[];
|
|
17
|
+
stagedFiles: StagedFile[];
|
|
18
|
+
}): StagedSymbolsManifestAssessment;
|
|
19
|
+
export declare function collectStagedAuthoredSymbolsManifestEvidence(options: {
|
|
20
|
+
sourceFiles: StagedFile[];
|
|
21
|
+
stagedFiles: StagedFile[];
|
|
22
|
+
}): StagedAuthoredSymbolsManifestEvidence;
|
|
23
|
+
//# sourceMappingURL=staged-symbols-manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"staged-symbols-manifest.d.ts","sourceRoot":"","sources":["../../src/traceability/staged-symbols-manifest.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AA8BlD,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,cAAc,CAAC;IACtD,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,qCAAqC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,CAAC;CAC7D;AAmQD,wBAAgB,2BAA2B,CAAC,OAAO,EAAE;IACnD,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,GAAG,+BAA+B,CA0ElC;AAoBD,wBAAgB,4CAA4C,CAAC,OAAO,EAAE;IACpE,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,WAAW,EAAE,UAAU,EAAE,CAAC;CAC3B,GAAG,qCAAqC,CA6CxC"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { extractManifestSymbolRecordsString, } from "../extractors/manifest.js";
|
|
4
|
+
import { mergeCoordinatesWithManifest, readCoordinateArtifact, } from "../extractors/symbol-coordinates.js";
|
|
5
|
+
import { analyzeSourceText } from "../extractors/symbols-coordinator.js";
|
|
6
|
+
import { resolveSymbolsManifestPaths } from "../utils/manifest-paths.js";
|
|
7
|
+
function toRepoRelativePath(absoluteOrRelativePath) {
|
|
8
|
+
if (!path.isAbsolute(absoluteOrRelativePath)) {
|
|
9
|
+
return absoluteOrRelativePath.replace(/\\/g, "/");
|
|
10
|
+
}
|
|
11
|
+
const relativePath = path.relative(process.cwd(), absoluteOrRelativePath);
|
|
12
|
+
return relativePath.replace(/\\/g, "/");
|
|
13
|
+
}
|
|
14
|
+
function resolveRelativeManifestPaths(symbolsManifestPath) {
|
|
15
|
+
if (symbolsManifestPath) {
|
|
16
|
+
const normalizedSymbolsPath = toRepoRelativePath(symbolsManifestPath);
|
|
17
|
+
return {
|
|
18
|
+
symbolsPath: normalizedSymbolsPath,
|
|
19
|
+
coordinatesPath: path
|
|
20
|
+
.join(path.dirname(normalizedSymbolsPath), "symbol-coordinates.yaml")
|
|
21
|
+
.replace(/\\/g, "/"),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
const { coordinatesPath, symbolsPath } = resolveSymbolsManifestPaths(process.cwd());
|
|
25
|
+
return {
|
|
26
|
+
symbolsPath: toRepoRelativePath(symbolsPath),
|
|
27
|
+
coordinatesPath: toRepoRelativePath(coordinatesPath),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function readHeadFileContent(filePath) {
|
|
31
|
+
try {
|
|
32
|
+
return execSync(`git show HEAD:${filePath}`, {
|
|
33
|
+
encoding: "utf8",
|
|
34
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function parseManifestRecords(content, filePath) {
|
|
42
|
+
if (content === null || content === undefined) {
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
return extractManifestSymbolRecordsString(content, filePath);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function parseCoordinateArtifact(content) {
|
|
53
|
+
if (content === null || content === undefined) {
|
|
54
|
+
return { coordinates: {} };
|
|
55
|
+
}
|
|
56
|
+
try {
|
|
57
|
+
return readCoordinateArtifact(content);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function normalizeNumber(value) {
|
|
64
|
+
return typeof value === "number" ? value : null;
|
|
65
|
+
}
|
|
66
|
+
function normalizeLinks(links) {
|
|
67
|
+
if (!Array.isArray(links)) {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
const normalized = [];
|
|
71
|
+
for (const link of links) {
|
|
72
|
+
if (typeof link === "string") {
|
|
73
|
+
normalized.push(link);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (link &&
|
|
77
|
+
typeof link === "object" &&
|
|
78
|
+
typeof link.type === "string" &&
|
|
79
|
+
typeof link.target === "string") {
|
|
80
|
+
normalized.push({ type: link.type, target: link.target });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return normalized.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
84
|
+
}
|
|
85
|
+
function normalizeRelationships(relationships) {
|
|
86
|
+
if (!Array.isArray(relationships)) {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
return relationships
|
|
90
|
+
.flatMap((relationship) => {
|
|
91
|
+
if (relationship &&
|
|
92
|
+
typeof relationship.type === "string" &&
|
|
93
|
+
typeof relationship.target === "string") {
|
|
94
|
+
return [{ type: relationship.type, target: relationship.target }];
|
|
95
|
+
}
|
|
96
|
+
return [];
|
|
97
|
+
})
|
|
98
|
+
.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
99
|
+
}
|
|
100
|
+
function normalizeManifestSymbolsForSourceFile(records, sourceFile) {
|
|
101
|
+
return records
|
|
102
|
+
.filter((record) => {
|
|
103
|
+
const recordSource = typeof record.sourceFile === "string"
|
|
104
|
+
? record.sourceFile
|
|
105
|
+
: typeof record.source === "string"
|
|
106
|
+
? record.source
|
|
107
|
+
: null;
|
|
108
|
+
return recordSource === sourceFile && typeof record.title === "string";
|
|
109
|
+
})
|
|
110
|
+
.map((record) => ({
|
|
111
|
+
title: record.title,
|
|
112
|
+
sourceFile,
|
|
113
|
+
sourceLine: normalizeNumber(record.sourceLine),
|
|
114
|
+
sourceColumn: normalizeNumber(record.sourceColumn),
|
|
115
|
+
sourceEndLine: normalizeNumber(record.sourceEndLine),
|
|
116
|
+
sourceEndColumn: normalizeNumber(record.sourceEndColumn),
|
|
117
|
+
}))
|
|
118
|
+
.sort(compareNormalizedSymbols);
|
|
119
|
+
}
|
|
120
|
+
function normalizeAuthoredManifestSymbolsForSourceFile(records, sourceFile) {
|
|
121
|
+
return records
|
|
122
|
+
.filter((record) => {
|
|
123
|
+
const recordSource = typeof record.sourceFile === "string"
|
|
124
|
+
? record.sourceFile
|
|
125
|
+
: typeof record.source === "string"
|
|
126
|
+
? record.source
|
|
127
|
+
: null;
|
|
128
|
+
return recordSource === sourceFile && typeof record.title === "string";
|
|
129
|
+
})
|
|
130
|
+
.map((record) => ({
|
|
131
|
+
id: typeof record.id === "string" ? record.id : null,
|
|
132
|
+
title: record.title,
|
|
133
|
+
sourceFile,
|
|
134
|
+
status: typeof record.status === "string" ? record.status : null,
|
|
135
|
+
links: normalizeLinks(record.links),
|
|
136
|
+
relationships: normalizeRelationships(record.relationships),
|
|
137
|
+
tags: Array.isArray(record.tags)
|
|
138
|
+
? record.tags.filter((tag) => typeof tag === "string").sort()
|
|
139
|
+
: [],
|
|
140
|
+
owner: typeof record.owner === "string" ? record.owner : null,
|
|
141
|
+
priority: typeof record.priority === "string" ? record.priority : null,
|
|
142
|
+
severity: typeof record.severity === "string" ? record.severity : null,
|
|
143
|
+
textRef: typeof record.text_ref === "string" ? record.text_ref : null,
|
|
144
|
+
}))
|
|
145
|
+
.sort((left, right) => JSON.stringify(left).localeCompare(JSON.stringify(right)));
|
|
146
|
+
}
|
|
147
|
+
function normalizeExpectedSymbolsForStagedFile(stagedFile) {
|
|
148
|
+
const analysis = analyzeSourceText(stagedFile.path, stagedFile.content ?? "");
|
|
149
|
+
return analysis.symbols
|
|
150
|
+
.map((symbol) => ({
|
|
151
|
+
title: symbol.name,
|
|
152
|
+
sourceFile: stagedFile.path,
|
|
153
|
+
sourceLine: symbol.startLine,
|
|
154
|
+
sourceColumn: symbol.startColumn,
|
|
155
|
+
sourceEndLine: symbol.endLine,
|
|
156
|
+
sourceEndColumn: symbol.endColumn,
|
|
157
|
+
}))
|
|
158
|
+
.sort(compareNormalizedSymbols);
|
|
159
|
+
}
|
|
160
|
+
function compareNormalizedSymbols(left, right) {
|
|
161
|
+
return JSON.stringify(left).localeCompare(JSON.stringify(right));
|
|
162
|
+
}
|
|
163
|
+
function signaturesEqual(left, right) {
|
|
164
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
165
|
+
}
|
|
166
|
+
function uniqueSorted(paths) {
|
|
167
|
+
return Array.from(new Set(paths)).sort();
|
|
168
|
+
}
|
|
169
|
+
function mergeManifestRecordsWithCoordinates(manifestRecords, coordinateArtifact) {
|
|
170
|
+
return mergeCoordinatesWithManifest(manifestRecords ?? [], coordinateArtifact);
|
|
171
|
+
}
|
|
172
|
+
function getEffectiveManifestRecords(options) {
|
|
173
|
+
const { headCoordinateArtifact, headManifestRecords, paths, stagedFiles } = options;
|
|
174
|
+
const stagedManifestFile = stagedFiles.find((file) => file.path === paths.symbolsPath);
|
|
175
|
+
const stagedCoordinatesFile = stagedFiles.find((file) => file.path === paths.coordinatesPath);
|
|
176
|
+
const stagedManifestRecords = stagedManifestFile
|
|
177
|
+
? parseManifestRecords(stagedManifestFile.content, paths.symbolsPath)
|
|
178
|
+
: headManifestRecords;
|
|
179
|
+
const stagedCoordinateArtifact = stagedCoordinatesFile
|
|
180
|
+
? parseCoordinateArtifact(stagedCoordinatesFile.content)
|
|
181
|
+
: headCoordinateArtifact;
|
|
182
|
+
return {
|
|
183
|
+
stagedManifestFile,
|
|
184
|
+
stagedCoordinatesFile,
|
|
185
|
+
stagedManifestRecords,
|
|
186
|
+
stagedCoordinateArtifact,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
export function assessStagedSymbolsManifest(options) {
|
|
190
|
+
const { sourceFiles, stagedFiles, symbolsManifestPath } = options;
|
|
191
|
+
const paths = resolveRelativeManifestPaths(symbolsManifestPath);
|
|
192
|
+
const headManifestRecords = parseManifestRecords(readHeadFileContent(paths.symbolsPath), paths.symbolsPath);
|
|
193
|
+
const headCoordinateArtifact = parseCoordinateArtifact(readHeadFileContent(paths.coordinatesPath));
|
|
194
|
+
const { stagedCoordinatesFile, stagedCoordinateArtifact, stagedManifestRecords, } = getEffectiveManifestRecords({
|
|
195
|
+
stagedFiles,
|
|
196
|
+
paths,
|
|
197
|
+
headManifestRecords,
|
|
198
|
+
headCoordinateArtifact,
|
|
199
|
+
});
|
|
200
|
+
const baselineMergedRecords = mergeManifestRecordsWithCoordinates(headManifestRecords, headCoordinateArtifact);
|
|
201
|
+
const stagedMergedRecords = mergeManifestRecordsWithCoordinates(stagedManifestRecords, stagedCoordinateArtifact);
|
|
202
|
+
const requiredRefreshPaths = [];
|
|
203
|
+
const freshPaths = new Set();
|
|
204
|
+
for (const sourceFile of sourceFiles) {
|
|
205
|
+
const expectedSymbols = normalizeExpectedSymbolsForStagedFile(sourceFile);
|
|
206
|
+
const baselineSymbols = normalizeManifestSymbolsForSourceFile(baselineMergedRecords, sourceFile.path);
|
|
207
|
+
if (signaturesEqual(expectedSymbols, baselineSymbols)) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
requiredRefreshPaths.push(sourceFile.path);
|
|
211
|
+
if (!stagedCoordinatesFile || stagedCoordinateArtifact === null) {
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
const stagedSymbols = normalizeManifestSymbolsForSourceFile(stagedMergedRecords, sourceFile.path);
|
|
215
|
+
if (signaturesEqual(expectedSymbols, stagedSymbols)) {
|
|
216
|
+
freshPaths.add(sourceFile.path);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
const sourcePaths = uniqueSorted(requiredRefreshPaths);
|
|
220
|
+
if (sourcePaths.length === 0) {
|
|
221
|
+
return { state: "not_required", sourcePaths: [], path: paths.coordinatesPath };
|
|
222
|
+
}
|
|
223
|
+
if (sourcePaths.every((sourcePath) => freshPaths.has(sourcePath))) {
|
|
224
|
+
return { state: "fresh", sourcePaths, path: paths.coordinatesPath };
|
|
225
|
+
}
|
|
226
|
+
if (!stagedCoordinatesFile) {
|
|
227
|
+
return { state: "missing", sourcePaths, path: paths.coordinatesPath };
|
|
228
|
+
}
|
|
229
|
+
return { state: "stale", sourcePaths, path: paths.coordinatesPath };
|
|
230
|
+
}
|
|
231
|
+
function getEntityIdsForSourceFile(records, sourceFile) {
|
|
232
|
+
return records
|
|
233
|
+
.filter((record) => {
|
|
234
|
+
const recordSource = typeof record.sourceFile === "string"
|
|
235
|
+
? record.sourceFile
|
|
236
|
+
: typeof record.source === "string"
|
|
237
|
+
? record.source
|
|
238
|
+
: null;
|
|
239
|
+
return recordSource === sourceFile && typeof record.id === "string";
|
|
240
|
+
})
|
|
241
|
+
.map((record) => record.id)
|
|
242
|
+
.sort();
|
|
243
|
+
}
|
|
244
|
+
export function collectStagedAuthoredSymbolsManifestEvidence(options) {
|
|
245
|
+
const { sourceFiles, stagedFiles } = options;
|
|
246
|
+
const paths = resolveRelativeManifestPaths();
|
|
247
|
+
const stagedManifestFile = stagedFiles.find((file) => file.path === paths.symbolsPath);
|
|
248
|
+
if (!stagedManifestFile) {
|
|
249
|
+
return { path: paths.symbolsPath, entries: [] };
|
|
250
|
+
}
|
|
251
|
+
const headManifestRecords = parseManifestRecords(readHeadFileContent(paths.symbolsPath), paths.symbolsPath) ?? [];
|
|
252
|
+
const stagedManifestRecords = parseManifestRecords(stagedManifestFile.content, paths.symbolsPath);
|
|
253
|
+
if (stagedManifestRecords === null) {
|
|
254
|
+
return { path: paths.symbolsPath, entries: [] };
|
|
255
|
+
}
|
|
256
|
+
const entries = [];
|
|
257
|
+
for (const sourceFile of sourceFiles) {
|
|
258
|
+
const headSymbols = normalizeAuthoredManifestSymbolsForSourceFile(headManifestRecords, sourceFile.path);
|
|
259
|
+
const stagedSymbols = normalizeAuthoredManifestSymbolsForSourceFile(stagedManifestRecords, sourceFile.path);
|
|
260
|
+
if (signaturesEqual(headSymbols, stagedSymbols)) {
|
|
261
|
+
continue;
|
|
262
|
+
}
|
|
263
|
+
entries.push({
|
|
264
|
+
sourcePath: sourceFile.path,
|
|
265
|
+
entityIds: getEntityIdsForSourceFile(stagedManifestRecords, sourceFile.path),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return { path: paths.symbolsPath, entries };
|
|
269
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"symbol-extract.d.ts","sourceRoot":"","sources":["../../src/traceability/symbol-extract.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,KAAK,wBAAwB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAU7D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAE9D,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAG5E;
|
|
1
|
+
{"version":3,"file":"symbol-extract.d.ts","sourceRoot":"","sources":["../../src/traceability/symbol-extract.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7D,KAAK,wBAAwB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAU7D,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,GAAG,SAAS,CAAC;IAC7D,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,UAAU,EAAE,SAAS,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,CAAC,EAAE,wBAAwB,EAAE,CAAC;CAC5C;AAED,MAAM,MAAM,cAAc,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAE9D,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAG5E;AA+ND,wBAAgB,4BAA4B,CAE1C,UAAU,EAAE,UAAU,EACtB,cAAc,CAAC,EAAE,cAAc,GAC9B,eAAe,EAAE,CA+JnB"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { Project, ScriptKind } from "ts-morph";
|
|
3
|
-
import {
|
|
3
|
+
import { readManifestWithCoordinateOverlay } from "../extractors/manifest.js";
|
|
4
4
|
const TRACEABILITY_RELATIONSHIP_TYPES = new Set([
|
|
5
5
|
"implements",
|
|
6
6
|
"covered_by",
|
|
@@ -55,15 +55,12 @@ function resolveSymbolTraceability(filePath, name, manifestLookup) {
|
|
|
55
55
|
}
|
|
56
56
|
for (const manifestPath of candidateManifestPaths) {
|
|
57
57
|
try {
|
|
58
|
-
const
|
|
59
|
-
for (const
|
|
60
|
-
if (
|
|
58
|
+
const records = readManifestWithCoordinateOverlay(manifestPath);
|
|
59
|
+
for (const record of records) {
|
|
60
|
+
if (record.title === name && typeof record.id === "string") {
|
|
61
61
|
return {
|
|
62
|
-
id:
|
|
63
|
-
relationships: filterTraceabilityRelationships(
|
|
64
|
-
type: relationship.type,
|
|
65
|
-
to: relationship.to,
|
|
66
|
-
}))),
|
|
62
|
+
id: record.id,
|
|
63
|
+
relationships: filterTraceabilityRelationships(extractRelationshipsFromManifestRecord(record)),
|
|
67
64
|
};
|
|
68
65
|
}
|
|
69
66
|
}
|
|
@@ -74,6 +71,33 @@ function resolveSymbolTraceability(filePath, name, manifestLookup) {
|
|
|
74
71
|
}
|
|
75
72
|
return { id: createHashFallbackId(filePath, name) };
|
|
76
73
|
}
|
|
74
|
+
function extractRelationshipsFromManifestRecord(record) {
|
|
75
|
+
const relationships = [];
|
|
76
|
+
if (Array.isArray(record.links)) {
|
|
77
|
+
for (const link of record.links) {
|
|
78
|
+
if (typeof link === "string") {
|
|
79
|
+
relationships.push({ type: "implements", to: link });
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (link &&
|
|
83
|
+
typeof link === "object" &&
|
|
84
|
+
typeof link.type === "string" &&
|
|
85
|
+
typeof link.target === "string") {
|
|
86
|
+
relationships.push({ type: link.type, to: link.target });
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (Array.isArray(record.relationships)) {
|
|
91
|
+
for (const relationship of record.relationships) {
|
|
92
|
+
if (relationship &&
|
|
93
|
+
typeof relationship.type === "string" &&
|
|
94
|
+
typeof relationship.target === "string") {
|
|
95
|
+
relationships.push({ type: relationship.type, to: relationship.target });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return relationships;
|
|
100
|
+
}
|
|
77
101
|
function buildSymbolResult(stagedFile, name, kind, span, inlineReqLinks, manifestLookup) {
|
|
78
102
|
const { id, relationships } = resolveSymbolTraceability(stagedFile.path, name, manifestLookup);
|
|
79
103
|
const manifestReqLinks = getRequirementLinks(relationships);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const DEFAULT_SYMBOLS_PATH = "documentation/symbols.yaml";
|
|
2
|
+
export declare const DEFAULT_COORDINATES_PATH = "documentation/symbol-coordinates.yaml";
|
|
3
|
+
export declare function resolveSymbolsManifestPaths(workspaceRoot: string, configPath?: string): {
|
|
4
|
+
symbolsPath: string;
|
|
5
|
+
coordinatesPath: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function resolveSymbolsManifestPath(workspaceRoot: string, configPath?: string): string;
|
|
8
|
+
//# sourceMappingURL=manifest-paths.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-paths.d.ts","sourceRoot":"","sources":["../../src/utils/manifest-paths.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AACjE,eAAO,MAAM,wBAAwB,0CAA0C,CAAC;AAuEhF,wBAAgB,2BAA2B,CACzC,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,MAAM,GAClB;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CASlD;AAGD,wBAAgB,0BAA0B,CACxC,aAAa,EAAE,MAAM,EACrB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAER"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
export const DEFAULT_SYMBOLS_PATH = "documentation/symbols.yaml";
|
|
4
|
+
export const DEFAULT_COORDINATES_PATH = "documentation/symbol-coordinates.yaml";
|
|
5
|
+
function isNonEmptyString(value) {
|
|
6
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
7
|
+
}
|
|
8
|
+
function resolveConfigPath(workspaceRoot, configPath) {
|
|
9
|
+
const configuredPath = configPath ?? path.join(workspaceRoot, ".kb", "config.json");
|
|
10
|
+
return path.isAbsolute(configuredPath)
|
|
11
|
+
? configuredPath
|
|
12
|
+
: path.resolve(workspaceRoot, configuredPath);
|
|
13
|
+
}
|
|
14
|
+
function readManifestResolverConfig(workspaceRoot, configPath) {
|
|
15
|
+
const resolvedConfigPath = resolveConfigPath(workspaceRoot, configPath);
|
|
16
|
+
if (!existsSync(resolvedConfigPath)) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
return JSON.parse(readFileSync(resolvedConfigPath, "utf8"));
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function resolveConfiguredSymbolsPath(workspaceRoot, configPath) {
|
|
27
|
+
const config = readManifestResolverConfig(workspaceRoot, configPath);
|
|
28
|
+
if (!config) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const configuredPathCandidate = config.paths?.symbols ?? config.symbolsManifest;
|
|
32
|
+
if (!isNonEmptyString(configuredPathCandidate)) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return path.isAbsolute(configuredPathCandidate)
|
|
36
|
+
? configuredPathCandidate
|
|
37
|
+
: path.resolve(workspaceRoot, configuredPathCandidate);
|
|
38
|
+
}
|
|
39
|
+
function resolveDefaultSymbolsPath(workspaceRoot) {
|
|
40
|
+
const candidates = [
|
|
41
|
+
path.join(workspaceRoot, DEFAULT_SYMBOLS_PATH),
|
|
42
|
+
path.join(workspaceRoot, "symbols.yaml"),
|
|
43
|
+
path.join(workspaceRoot, "symbols.yml"),
|
|
44
|
+
];
|
|
45
|
+
return candidates.find((candidate) => existsSync(candidate)) ?? candidates[0] ?? path.join(workspaceRoot, DEFAULT_SYMBOLS_PATH);
|
|
46
|
+
}
|
|
47
|
+
function deriveCoordinatesPath(symbolsPath) {
|
|
48
|
+
return path.join(path.dirname(symbolsPath), path.basename(DEFAULT_COORDINATES_PATH));
|
|
49
|
+
}
|
|
50
|
+
// implements REQ-cli-sync
|
|
51
|
+
export function resolveSymbolsManifestPaths(workspaceRoot, configPath) {
|
|
52
|
+
const symbolsPath = resolveConfiguredSymbolsPath(workspaceRoot, configPath) ??
|
|
53
|
+
resolveDefaultSymbolsPath(workspaceRoot);
|
|
54
|
+
return {
|
|
55
|
+
symbolsPath,
|
|
56
|
+
coordinatesPath: deriveCoordinatesPath(symbolsPath),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// implements REQ-cli-sync
|
|
60
|
+
export function resolveSymbolsManifestPath(workspaceRoot, configPath) {
|
|
61
|
+
return resolveSymbolsManifestPaths(workspaceRoot, configPath).symbolsPath;
|
|
62
|
+
}
|
package/package.json
CHANGED