@hawkeyexl/manni 4.1.0 → 4.2.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/README.md +1 -1
- package/dist/{chunk-JODRT4NF.js → chunk-BNHQWIAE.js} +8 -37
- package/dist/chunk-BNHQWIAE.js.map +1 -0
- package/dist/{chunk-I57KBF45.js → chunk-TH565O2N.js} +476 -35
- package/dist/chunk-TH565O2N.js.map +1 -0
- package/dist/{chunk-S3NDEIOW.js → chunk-UYDRDFYF.js} +7508 -1057
- package/dist/chunk-UYDRDFYF.js.map +1 -0
- package/dist/cli.js +879 -106
- package/dist/cli.js.map +1 -1
- package/dist/docmeta.js +2 -2
- package/dist/graph/embed.d.ts +154 -0
- package/dist/graph/embed.js +137 -0
- package/dist/graph/embed.js.map +1 -0
- package/dist/graph/runtime.d.ts +751 -0
- package/dist/graph/runtime.js +3067 -0
- package/dist/graph/runtime.js.map +1 -0
- package/dist/index.d.ts +2784 -512
- package/dist/index.js +392 -5
- package/dist/index.js.map +1 -1
- package/package.json +30 -4
- package/shapes/graph/shapes-1.0.0.ttl +689 -0
- package/dist/chunk-I57KBF45.js.map +0 -1
- package/dist/chunk-JODRT4NF.js.map +0 -1
- package/dist/chunk-S3NDEIOW.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,37 +1,44 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
buildProgram
|
|
4
|
-
|
|
5
|
-
configOption,
|
|
6
|
-
explicitFalse,
|
|
7
|
-
readStdin,
|
|
8
|
-
reportConfig,
|
|
9
|
-
splitList
|
|
10
|
-
} from "./chunk-JODRT4NF.js";
|
|
3
|
+
buildProgram
|
|
4
|
+
} from "./chunk-BNHQWIAE.js";
|
|
11
5
|
import {
|
|
6
|
+
ALL_FILL_FIELDS,
|
|
7
|
+
CONFIG_SECTION,
|
|
12
8
|
CiteError,
|
|
9
|
+
DEFAULT_CONFIG_FILENAME2 as DEFAULT_CONFIG_FILENAME,
|
|
10
|
+
DEFAULT_EXTENSIONS,
|
|
13
11
|
DocevalsError,
|
|
14
12
|
EXECUTION_GRANTS,
|
|
13
|
+
EXPORT_TARGETS,
|
|
14
|
+
GraphError,
|
|
15
15
|
INFER_FORMATS,
|
|
16
16
|
KeyError,
|
|
17
17
|
LINT_JOBS,
|
|
18
18
|
LintError,
|
|
19
19
|
REPORT_FORMATS as REPORT_FORMATS2,
|
|
20
20
|
RULE_ID_PREFIX,
|
|
21
|
+
SEARCH_MODES,
|
|
21
22
|
SEVERITIES,
|
|
22
23
|
SEVERITY_LIST,
|
|
24
|
+
STDIN,
|
|
23
25
|
SUMMARY_FORMATS,
|
|
24
26
|
TOOLS_BY_JOB,
|
|
25
27
|
asCiteError,
|
|
28
|
+
assertInputFormat,
|
|
29
|
+
bundledShapesPath,
|
|
26
30
|
codeEndAt,
|
|
27
31
|
codeRegions,
|
|
32
|
+
compactIri,
|
|
28
33
|
errorSite,
|
|
29
34
|
findingMessage,
|
|
30
35
|
isCiteRule,
|
|
31
36
|
isInferFormat,
|
|
32
37
|
isSeverity,
|
|
33
38
|
listReviews,
|
|
34
|
-
|
|
39
|
+
loadConfig3 as loadConfig,
|
|
40
|
+
loadGraph,
|
|
41
|
+
loadRunConfig,
|
|
35
42
|
meetsSeverity,
|
|
36
43
|
parseFormat,
|
|
37
44
|
rebaseConfig,
|
|
@@ -41,20 +48,30 @@ import {
|
|
|
41
48
|
renderCheckGithub,
|
|
42
49
|
renderCheckJson,
|
|
43
50
|
renderCheckPretty,
|
|
51
|
+
renderEmbed,
|
|
44
52
|
renderFill,
|
|
53
|
+
renderFill2,
|
|
45
54
|
renderList,
|
|
55
|
+
renderQuery,
|
|
46
56
|
renderRemoveJson,
|
|
47
57
|
renderRemovePretty,
|
|
48
58
|
renderReviews,
|
|
59
|
+
renderSearch,
|
|
60
|
+
renderStats,
|
|
49
61
|
renderTemplates,
|
|
50
62
|
renderTools,
|
|
63
|
+
renderTraverse,
|
|
51
64
|
renderUpdateJson,
|
|
52
65
|
renderUpdatePretty,
|
|
53
66
|
resolveStructureTool,
|
|
54
67
|
runAdd,
|
|
68
|
+
runBuild,
|
|
55
69
|
runCalibrate,
|
|
56
70
|
runCheck,
|
|
71
|
+
runEmbed,
|
|
72
|
+
runExport,
|
|
57
73
|
runFill,
|
|
74
|
+
runFill2,
|
|
58
75
|
runGenerate,
|
|
59
76
|
runInit,
|
|
60
77
|
runKeyRotate,
|
|
@@ -62,20 +79,25 @@ import {
|
|
|
62
79
|
runLint,
|
|
63
80
|
runList,
|
|
64
81
|
runPromote,
|
|
82
|
+
runQuery,
|
|
65
83
|
runRemove,
|
|
66
84
|
runReview,
|
|
67
85
|
runRun,
|
|
86
|
+
runSearch,
|
|
87
|
+
runStats,
|
|
68
88
|
runTemplates,
|
|
69
89
|
runTemplatesInfer,
|
|
70
90
|
runTools,
|
|
91
|
+
runTraverse,
|
|
71
92
|
runUpdate,
|
|
72
93
|
seedGoldenCases,
|
|
73
94
|
shortCommit,
|
|
74
95
|
shortLine,
|
|
75
96
|
shortPin,
|
|
76
97
|
shortSrc,
|
|
77
|
-
spellSource
|
|
78
|
-
|
|
98
|
+
spellSource,
|
|
99
|
+
validateGraph
|
|
100
|
+
} from "./chunk-UYDRDFYF.js";
|
|
79
101
|
import {
|
|
80
102
|
COMMON_FORMAT_LIST,
|
|
81
103
|
CONCEPT_FIELDS,
|
|
@@ -89,9 +111,12 @@ import {
|
|
|
89
111
|
TERM_RELATION_FIELDS,
|
|
90
112
|
ToolError,
|
|
91
113
|
assertNonEmpty,
|
|
114
|
+
collect,
|
|
115
|
+
configOption,
|
|
92
116
|
errorMessage,
|
|
93
117
|
escapeWorkflowCommandMessage,
|
|
94
118
|
escapeWorkflowCommandProperty,
|
|
119
|
+
explicitFalse,
|
|
95
120
|
extractorByName,
|
|
96
121
|
extractorForExtension,
|
|
97
122
|
fail,
|
|
@@ -108,23 +133,28 @@ import {
|
|
|
108
133
|
palette,
|
|
109
134
|
programName,
|
|
110
135
|
readConfigFile,
|
|
136
|
+
readStdin,
|
|
111
137
|
registerTermLabelSource,
|
|
112
138
|
renderJunit,
|
|
113
139
|
renderSarif,
|
|
140
|
+
reportConfig,
|
|
114
141
|
resolveBaselineRequest,
|
|
115
142
|
resolveTargetSet,
|
|
116
143
|
runIfMain,
|
|
117
144
|
selectCollections,
|
|
118
145
|
settleBaseline,
|
|
119
146
|
shouldColor,
|
|
147
|
+
splitList,
|
|
120
148
|
splitPageArgument,
|
|
121
149
|
terminalConfirm,
|
|
122
150
|
valeConfigPath,
|
|
123
|
-
valueAt
|
|
124
|
-
|
|
151
|
+
valueAt,
|
|
152
|
+
warn,
|
|
153
|
+
writeTextAtomic
|
|
154
|
+
} from "./chunk-TH565O2N.js";
|
|
125
155
|
|
|
126
156
|
// src/cli.ts
|
|
127
|
-
import { Command as
|
|
157
|
+
import { Command as Command8 } from "commander";
|
|
128
158
|
|
|
129
159
|
// src/a11y/cli.ts
|
|
130
160
|
import { Command } from "commander";
|
|
@@ -224,17 +254,19 @@ async function crawl(opts, analyzer) {
|
|
|
224
254
|
url = normalizeUrl(raw);
|
|
225
255
|
key = dedupeKey(url);
|
|
226
256
|
} catch {
|
|
227
|
-
return;
|
|
257
|
+
return { as: "invalid" };
|
|
228
258
|
}
|
|
229
|
-
if (source !== "seed" && !isPageLink(url)) return;
|
|
230
|
-
if (!opts.seeds.some((seed) => sameHost(seed, url))) return;
|
|
231
|
-
|
|
259
|
+
if (source !== "seed" && !isPageLink(url)) return { as: "asset" };
|
|
260
|
+
if (!opts.seeds.some((seed) => sameHost(seed, url))) return { as: "off-host" };
|
|
261
|
+
const pattern = opts.exclude?.match(url) ?? null;
|
|
262
|
+
if (pattern !== null) {
|
|
232
263
|
excluded.add(key);
|
|
233
|
-
return;
|
|
264
|
+
return { as: "excluded", pattern };
|
|
234
265
|
}
|
|
235
|
-
if (seen.has(key)) return;
|
|
266
|
+
if (seen.has(key)) return { as: "seen" };
|
|
236
267
|
seen.add(key);
|
|
237
268
|
frontier.push({ url, key, source });
|
|
269
|
+
return { as: "queued" };
|
|
238
270
|
};
|
|
239
271
|
const landed = (finalUrl) => {
|
|
240
272
|
if (finalUrl === void 0) return;
|
|
@@ -251,6 +283,7 @@ async function crawl(opts, analyzer) {
|
|
|
251
283
|
progress({ kind: "excluded", urls: excluded.size, patterns: opts.exclude?.patterns.length ?? 0 });
|
|
252
284
|
}
|
|
253
285
|
const pages = [];
|
|
286
|
+
const redirects = [];
|
|
254
287
|
let duplicates = 0;
|
|
255
288
|
const capped = () => opts.maxPages !== void 0 && pages.length >= opts.maxPages;
|
|
256
289
|
for (let next = 0; next < frontier.length && !capped(); next++) {
|
|
@@ -263,10 +296,28 @@ async function crawl(opts, analyzer) {
|
|
|
263
296
|
}
|
|
264
297
|
visited.add(key);
|
|
265
298
|
const index = pages.length + 1;
|
|
266
|
-
if (
|
|
299
|
+
if (pages.length + redirects.length === 0) progress({ kind: "browser" });
|
|
267
300
|
progress({ kind: "page", index, queued: frontier.length, url });
|
|
268
301
|
try {
|
|
269
302
|
const analyzed = await analyzer.analyze(url, opts.analyze);
|
|
303
|
+
if ("redirect" in analyzed) {
|
|
304
|
+
const to = normalized(analyzed.redirect);
|
|
305
|
+
redirects.push({ url, to, source });
|
|
306
|
+
progress({ kind: "redirected", index, url, to });
|
|
307
|
+
const outcome2 = enqueue(to, source);
|
|
308
|
+
if (source === "seed") {
|
|
309
|
+
if (!isHttpUrl(to)) {
|
|
310
|
+
throw new A11yError(`${url} redirects to ${to}, which is not an http(s) URL.`);
|
|
311
|
+
}
|
|
312
|
+
if (outcome2.as === "off-host") {
|
|
313
|
+
throw new A11yError(`${url} redirects to ${to}, which is on another host. Check ${to} instead.`);
|
|
314
|
+
}
|
|
315
|
+
if (outcome2.as === "excluded") {
|
|
316
|
+
throw new A11yError(`${outcome2.pattern.source} excludes ${to}, where the seed ${url} redirects.`);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
continue;
|
|
320
|
+
}
|
|
270
321
|
pages.push({ ...analyzed.result, url, source });
|
|
271
322
|
landed(analyzed.finalUrl);
|
|
272
323
|
if (opts.crawl) for (const link of analyzed.links) enqueue(link, "link");
|
|
@@ -278,9 +329,23 @@ async function crawl(opts, analyzer) {
|
|
|
278
329
|
progress({ kind: "checked", index, url, violations: 0, error });
|
|
279
330
|
}
|
|
280
331
|
}
|
|
281
|
-
const skipped2 = frontier.length - pages.length - duplicates;
|
|
332
|
+
const skipped2 = frontier.length - pages.length - duplicates - redirects.length;
|
|
282
333
|
progress({ kind: "done", checked: pages.length, skipped: skipped2 });
|
|
283
|
-
return {
|
|
334
|
+
return {
|
|
335
|
+
pages,
|
|
336
|
+
discovered: frontier.length,
|
|
337
|
+
skipped: skipped2,
|
|
338
|
+
duplicates,
|
|
339
|
+
redirects,
|
|
340
|
+
excluded: excluded.size
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
function normalized(url) {
|
|
344
|
+
try {
|
|
345
|
+
return normalizeUrl(url);
|
|
346
|
+
} catch {
|
|
347
|
+
return url;
|
|
348
|
+
}
|
|
284
349
|
}
|
|
285
350
|
|
|
286
351
|
// src/a11y/core/exclude.ts
|
|
@@ -522,6 +587,7 @@ async function runCheck2(opts, deps) {
|
|
|
522
587
|
const results = outcome2.pages.map((page) => finishPage(page, opts.severity));
|
|
523
588
|
return {
|
|
524
589
|
results,
|
|
590
|
+
redirects: outcome2.redirects,
|
|
525
591
|
summary: summarize(results, outcome2, sitemap, opts.crawl)
|
|
526
592
|
};
|
|
527
593
|
} finally {
|
|
@@ -535,7 +601,7 @@ function finishPage(page, floor) {
|
|
|
535
601
|
const score = applicable === 0 ? null : Math.round(100 * page.passes / applicable);
|
|
536
602
|
return { ...page, violations, score };
|
|
537
603
|
}
|
|
538
|
-
function summarize(results, { discovered, skipped: skipped2, duplicates, excluded }, sitemap, crawl2) {
|
|
604
|
+
function summarize(results, { discovered, skipped: skipped2, duplicates, redirects, excluded }, sitemap, crawl2) {
|
|
539
605
|
const bySeverity = { notice: 0, warning: 0, error: 0 };
|
|
540
606
|
let failed = 0;
|
|
541
607
|
let violations = 0;
|
|
@@ -551,6 +617,7 @@ function summarize(results, { discovered, skipped: skipped2, duplicates, exclude
|
|
|
551
617
|
checked: results.length,
|
|
552
618
|
skipped: skipped2,
|
|
553
619
|
duplicates,
|
|
620
|
+
redirects: redirects.length,
|
|
554
621
|
excluded,
|
|
555
622
|
failed,
|
|
556
623
|
violations,
|
|
@@ -595,33 +662,18 @@ function createPlaywrightAnalyzer() {
|
|
|
595
662
|
return {
|
|
596
663
|
async analyze(url, opts) {
|
|
597
664
|
const { page } = await open();
|
|
665
|
+
const navigations = [];
|
|
666
|
+
const onRequest = (request) => {
|
|
667
|
+
if (request.isNavigationRequest() && request.frame() === page.mainFrame() && request.redirectedFrom() === null) {
|
|
668
|
+
navigations.push(request);
|
|
669
|
+
}
|
|
670
|
+
};
|
|
671
|
+
page.on("request", onRequest);
|
|
598
672
|
try {
|
|
599
|
-
await page
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
}
|
|
603
|
-
const links = await page.$$eval(
|
|
604
|
-
"a[href]",
|
|
605
|
-
(anchors) => anchors.map((a) => a.href)
|
|
606
|
-
);
|
|
607
|
-
let builder = new AxeBuilder({ page });
|
|
608
|
-
if (opts.tags.length > 0) builder = builder.withTags(opts.tags);
|
|
609
|
-
let results;
|
|
610
|
-
try {
|
|
611
|
-
results = await builder.analyze();
|
|
612
|
-
} catch (err) {
|
|
613
|
-
throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);
|
|
673
|
+
return await analyzePage(page, url, opts, navigations);
|
|
674
|
+
} finally {
|
|
675
|
+
page.off("request", onRequest);
|
|
614
676
|
}
|
|
615
|
-
return {
|
|
616
|
-
result: {
|
|
617
|
-
url,
|
|
618
|
-
violations: results.violations.map(toViolation),
|
|
619
|
-
passes: results.passes.length,
|
|
620
|
-
incomplete: results.incomplete.length
|
|
621
|
-
},
|
|
622
|
-
links,
|
|
623
|
-
finalUrl: page.url()
|
|
624
|
-
};
|
|
625
677
|
},
|
|
626
678
|
async close() {
|
|
627
679
|
if (session === null) return;
|
|
@@ -635,6 +687,103 @@ function createPlaywrightAnalyzer() {
|
|
|
635
687
|
}
|
|
636
688
|
};
|
|
637
689
|
}
|
|
690
|
+
async function analyzePage(page, url, opts, navigations) {
|
|
691
|
+
let response;
|
|
692
|
+
try {
|
|
693
|
+
response = await page.goto(url, { waitUntil: "load", timeout: opts.timeout });
|
|
694
|
+
} catch (err) {
|
|
695
|
+
throw new A11yError(`Could not load ${url}: ${reason(err)}`);
|
|
696
|
+
}
|
|
697
|
+
const loaded = response?.url() ?? page.url();
|
|
698
|
+
const nextNavigation = () => {
|
|
699
|
+
let first = response?.request() ?? null;
|
|
700
|
+
for (let from = first?.redirectedFrom() ?? null; from !== null; from = from.redirectedFrom()) {
|
|
701
|
+
first = from;
|
|
702
|
+
}
|
|
703
|
+
const own = first === null ? 0 : navigations.indexOf(first);
|
|
704
|
+
return own === -1 ? void 0 : navigations[own + 1];
|
|
705
|
+
};
|
|
706
|
+
const destination = () => nextNavigation()?.url() ?? page.url();
|
|
707
|
+
const navigatedAway = async () => {
|
|
708
|
+
if (nextNavigation() === void 0 && samePage(page.url(), loaded)) return null;
|
|
709
|
+
try {
|
|
710
|
+
await page.waitForURL((u) => !samePage(u.href, loaded), {
|
|
711
|
+
waitUntil: "commit",
|
|
712
|
+
timeout: opts.timeout
|
|
713
|
+
});
|
|
714
|
+
return { redirect: destination() };
|
|
715
|
+
} catch {
|
|
716
|
+
return null;
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
let read8;
|
|
720
|
+
try {
|
|
721
|
+
read8 = await page.evaluate(readPage);
|
|
722
|
+
} catch (err) {
|
|
723
|
+
const moved = await navigatedAway();
|
|
724
|
+
if (moved !== null) return moved;
|
|
725
|
+
throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);
|
|
726
|
+
}
|
|
727
|
+
const refresh = refreshTarget(read8.refresh, page.url());
|
|
728
|
+
if (refresh !== null) return { redirect: refresh };
|
|
729
|
+
let builder = new AxeBuilder({ page });
|
|
730
|
+
if (opts.tags.length > 0) builder = builder.withTags(opts.tags);
|
|
731
|
+
let results;
|
|
732
|
+
try {
|
|
733
|
+
results = await builder.analyze();
|
|
734
|
+
} catch (err) {
|
|
735
|
+
const moved = await navigatedAway();
|
|
736
|
+
if (moved !== null) return moved;
|
|
737
|
+
throw new A11yError(`Could not analyze ${url}: ${reason(err)}`);
|
|
738
|
+
}
|
|
739
|
+
if (!samePage(page.url(), loaded)) return { redirect: destination() };
|
|
740
|
+
return {
|
|
741
|
+
result: {
|
|
742
|
+
url,
|
|
743
|
+
violations: results.violations.map(toViolation),
|
|
744
|
+
passes: results.passes.length,
|
|
745
|
+
incomplete: results.incomplete.length
|
|
746
|
+
},
|
|
747
|
+
links: read8.links,
|
|
748
|
+
finalUrl: page.url()
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
function readPage() {
|
|
752
|
+
const doc = globalThis.document;
|
|
753
|
+
return {
|
|
754
|
+
links: Array.from(doc.querySelectorAll("a[href]"), (a) => a.href),
|
|
755
|
+
refresh: doc.querySelector('meta[http-equiv="refresh" i]')?.getAttribute("content") ?? null
|
|
756
|
+
};
|
|
757
|
+
}
|
|
758
|
+
function refreshUrl(content) {
|
|
759
|
+
const match = /^\s*[\d.]*\s*[;,]\s*(?:url\s*=\s*)?(.*)$/is.exec(content);
|
|
760
|
+
let target = match?.[1]?.trim() ?? "";
|
|
761
|
+
const quote = target[0];
|
|
762
|
+
if (quote === "'" || quote === '"') {
|
|
763
|
+
const end = target.indexOf(quote, 1);
|
|
764
|
+
target = (end === -1 ? target.slice(1) : target.slice(1, end)).trim();
|
|
765
|
+
}
|
|
766
|
+
return target === "" ? null : target;
|
|
767
|
+
}
|
|
768
|
+
function refreshTarget(content, base) {
|
|
769
|
+
if (content === null) return null;
|
|
770
|
+
const target = refreshUrl(content);
|
|
771
|
+
if (target === null) return null;
|
|
772
|
+
let to;
|
|
773
|
+
try {
|
|
774
|
+
to = new URL(target, base).href;
|
|
775
|
+
} catch {
|
|
776
|
+
return null;
|
|
777
|
+
}
|
|
778
|
+
return samePage(to, base) ? null : to;
|
|
779
|
+
}
|
|
780
|
+
function samePage(a, b) {
|
|
781
|
+
try {
|
|
782
|
+
return dedupeKey(a) === dedupeKey(b);
|
|
783
|
+
} catch {
|
|
784
|
+
return a === b;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
638
787
|
function severityOf(impact) {
|
|
639
788
|
switch (impact) {
|
|
640
789
|
case "critical":
|
|
@@ -819,8 +968,13 @@ function renderPretty(run, opts) {
|
|
|
819
968
|
);
|
|
820
969
|
for (const v of page.violations) lines.push(...violationLines(v, c));
|
|
821
970
|
}
|
|
971
|
+
if (!opts.quiet) {
|
|
972
|
+
for (const r of run.redirects) {
|
|
973
|
+
lines.push(`${c.dim("\u21AA")} ${c.cyan(r.url)} redirects to ${c.cyan(r.to)}`);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
822
976
|
const failed = summary.failed;
|
|
823
|
-
const footer = `${summary.violations} violation${summary.violations === 1 ? "" : "s"} on ${failed} of ${summary.checked} pages` + (summary.skipped > 0 ? `; ${summary.skipped} skipped (--max-pages)` : "") + (summary.duplicates > 0 ? `; ${summary.duplicates} duplicate${summary.duplicates === 1 ? "" : "s"} dropped` : "") + (summary.excluded > 0 ? `; ${summary.excluded} excluded` : "");
|
|
977
|
+
const footer = `${summary.violations} violation${summary.violations === 1 ? "" : "s"} on ${failed} of ${summary.checked} pages` + (summary.skipped > 0 ? `; ${summary.skipped} skipped (--max-pages)` : "") + (summary.duplicates > 0 ? `; ${summary.duplicates} duplicate${summary.duplicates === 1 ? "" : "s"} dropped` : "") + (summary.redirects > 0 ? `; ${summary.redirects} redirect${summary.redirects === 1 ? "" : "s"}` : "") + (summary.excluded > 0 ? `; ${summary.excluded} excluded` : "");
|
|
824
978
|
lines.push("");
|
|
825
979
|
lines.push(failed > 0 ? c.red(footer) : c.green(footer));
|
|
826
980
|
return lines.join("\n");
|
|
@@ -939,6 +1093,9 @@ function terminalReporter(opts) {
|
|
|
939
1093
|
case "checked":
|
|
940
1094
|
showUrl(`${c.dim(counter(event.index, queued))} ${outcome(event, c)}`, event.url);
|
|
941
1095
|
return;
|
|
1096
|
+
case "redirected":
|
|
1097
|
+
showUrl(`${c.dim(counter(event.index, queued))} ${c.dim("\u21AA")} redirects to `, event.to);
|
|
1098
|
+
return;
|
|
942
1099
|
case "done":
|
|
943
1100
|
stream.write(CLEAR_LINE);
|
|
944
1101
|
return;
|
|
@@ -979,6 +1136,9 @@ function plainReporter(stream) {
|
|
|
979
1136
|
else say(`${head} \u2717 ${plural(event.violations, "violation")}`);
|
|
980
1137
|
return;
|
|
981
1138
|
}
|
|
1139
|
+
case "redirected":
|
|
1140
|
+
say(`${counter(event.index, queued)} \u21AA redirects to ${event.to}`);
|
|
1141
|
+
return;
|
|
982
1142
|
case "done":
|
|
983
1143
|
say(`checked ${plural(event.checked, "page")}, ${event.skipped} skipped`);
|
|
984
1144
|
return;
|
|
@@ -2192,8 +2352,616 @@ function buildProgram5() {
|
|
|
2192
2352
|
return program;
|
|
2193
2353
|
}
|
|
2194
2354
|
|
|
2195
|
-
// src/
|
|
2355
|
+
// src/graph/cli.ts
|
|
2196
2356
|
import { Command as Command5 } from "commander";
|
|
2357
|
+
|
|
2358
|
+
// src/graph/reporters/index.ts
|
|
2359
|
+
var GRAPH_FORMATS = ["pretty", "json"];
|
|
2360
|
+
var GRAPH_FORMAT_LIST = GRAPH_FORMATS.join(" | ");
|
|
2361
|
+
var CHECK_FORMATS = [...GRAPH_FORMATS, "github"];
|
|
2362
|
+
var CHECK_FORMAT_LIST = CHECK_FORMATS.join(" | ");
|
|
2363
|
+
|
|
2364
|
+
// src/graph/commands/check.ts
|
|
2365
|
+
import { resolve } from "node:path";
|
|
2366
|
+
|
|
2367
|
+
// src/graph/reporters/github.ts
|
|
2368
|
+
function renderCheckGithub2(report) {
|
|
2369
|
+
const lines = [];
|
|
2370
|
+
for (const f of report.findings) {
|
|
2371
|
+
const file = f.docs[0];
|
|
2372
|
+
const where2 = file === void 0 ? "" : ` file=${escapeWorkflowCommandProperty(file)}`;
|
|
2373
|
+
const message = escapeWorkflowCommandMessage(
|
|
2374
|
+
`${f.message} (${compactIri(f.focusNode)})`
|
|
2375
|
+
);
|
|
2376
|
+
lines.push(`::${f.severity}${where2}::${message}`);
|
|
2377
|
+
}
|
|
2378
|
+
return lines.join("\n");
|
|
2379
|
+
}
|
|
2380
|
+
|
|
2381
|
+
// src/graph/commands/check.ts
|
|
2382
|
+
async function runCheck3(opts = {}) {
|
|
2383
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
2384
|
+
const config = loadRunConfig(
|
|
2385
|
+
{
|
|
2386
|
+
...opts.config === void 0 ? {} : { configPath: opts.config },
|
|
2387
|
+
...opts.noConfig === void 0 ? {} : { noConfig: opts.noConfig }
|
|
2388
|
+
},
|
|
2389
|
+
cwd
|
|
2390
|
+
);
|
|
2391
|
+
const store = loadGraph(resolve(cwd, opts.graph ?? config.out));
|
|
2392
|
+
const shapesInput = opts.shapes && opts.shapes.length > 0 ? opts.shapes : config.check.shapes;
|
|
2393
|
+
const shapes = shapesInput.length > 0 ? shapesInput.map((p) => resolve(cwd, p)) : [bundledShapesPath(import.meta.url)];
|
|
2394
|
+
const findings = await validateGraph(store, shapes);
|
|
2395
|
+
const count = (severity) => findings.filter((f) => f.severity === severity).length;
|
|
2396
|
+
const errors = count("error");
|
|
2397
|
+
return {
|
|
2398
|
+
findings,
|
|
2399
|
+
errors,
|
|
2400
|
+
warnings: count("warning"),
|
|
2401
|
+
notices: count("notice"),
|
|
2402
|
+
shapes,
|
|
2403
|
+
// Only an `error` fails the run; a warning and a notice are reported and
|
|
2404
|
+
// pass, as they were when they were called violations and warnings.
|
|
2405
|
+
exitCode: errors > 0 ? 1 : 0
|
|
2406
|
+
};
|
|
2407
|
+
}
|
|
2408
|
+
function plural3(n, word) {
|
|
2409
|
+
return `${String(n)} ${word}${n === 1 ? "" : "s"}`;
|
|
2410
|
+
}
|
|
2411
|
+
function renderCheck(report, format) {
|
|
2412
|
+
if (format === "json") {
|
|
2413
|
+
const { exitCode: _exitCode, ...rest } = report;
|
|
2414
|
+
return JSON.stringify(rest, null, 2);
|
|
2415
|
+
}
|
|
2416
|
+
if (format === "github") return renderCheckGithub2(report);
|
|
2417
|
+
const lines = [];
|
|
2418
|
+
for (const f of report.findings) {
|
|
2419
|
+
const where2 = f.docs.length > 0 ? ` [${f.docs.join(", ")}]` : "";
|
|
2420
|
+
lines.push(
|
|
2421
|
+
`${f.severity}: ${compactIri(f.focusNode)}${f.path ? ` ${compactIri(f.path)}` : ""} \u2014 ${f.message}${where2}`
|
|
2422
|
+
);
|
|
2423
|
+
}
|
|
2424
|
+
if (report.findings.length > 0) lines.push("");
|
|
2425
|
+
lines.push(
|
|
2426
|
+
`${plural3(report.errors, "error")}, ${plural3(report.warnings, "warning")}, ${plural3(report.notices, "notice")}`
|
|
2427
|
+
);
|
|
2428
|
+
return lines.join("\n");
|
|
2429
|
+
}
|
|
2430
|
+
|
|
2431
|
+
// src/graph/commands/init.ts
|
|
2432
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2433
|
+
import { resolve as resolve2 } from "node:path";
|
|
2434
|
+
import { parse as parseYaml } from "yaml";
|
|
2435
|
+
var HEADER = `# manni.config.yaml \u2014 shared configuration for the manni family of tools.
|
|
2436
|
+
# Each tool reads its own top-level key; manni graph reads "graph:".
|
|
2437
|
+
`;
|
|
2438
|
+
var STARTER = `# Base IRI for every minted node. Set this to a namespace you control;
|
|
2439
|
+
# without it, IRIs fall back to the urn:manni:graph: placeholder.
|
|
2440
|
+
# baseIri: https://example.com/graph/
|
|
2441
|
+
|
|
2442
|
+
# There is no document set here. \`manni graph build\` and \`manni graph fill\` read the
|
|
2443
|
+
# paths you name, or the collections declared once for every tool at the TOP
|
|
2444
|
+
# level of this file, beside \`graph:\` rather than inside it:
|
|
2445
|
+
#
|
|
2446
|
+
# collections:
|
|
2447
|
+
# - name: site
|
|
2448
|
+
# paths: ["docs/**/*.md"]
|
|
2449
|
+
#
|
|
2450
|
+
# Output of \`manni graph build\`.
|
|
2451
|
+
out: graph/graph.ttl
|
|
2452
|
+
|
|
2453
|
+
# Map published-site routes back to source files so route-style links
|
|
2454
|
+
# (/docs/actions/find) become graph edges. A route may also carry the BCP-47
|
|
2455
|
+
# \`language\` labelling every document under its root; a page's own \`lang\`
|
|
2456
|
+
# frontmatter wins. Uncomment and adjust:
|
|
2457
|
+
# routes:
|
|
2458
|
+
# - basePath: /docs
|
|
2459
|
+
# root: docs
|
|
2460
|
+
# extensions: [.md, .mdx, .markdown]
|
|
2461
|
+
# indexFiles: [index, README]
|
|
2462
|
+
# - basePath: /docs/de
|
|
2463
|
+
# root: docs/de
|
|
2464
|
+
# language: de
|
|
2465
|
+
|
|
2466
|
+
# What to derive triples from. Remove entries to opt out.
|
|
2467
|
+
build:
|
|
2468
|
+
derive: [frontmatter, sections, links, tags, images, code, provenance]
|
|
2469
|
+
|
|
2470
|
+
# PROV-O settings. Git history is detected, not switched: per-file dates and
|
|
2471
|
+
# author agents, rename -> prov:wasRevisionOf, and the build activity's HEAD
|
|
2472
|
+
# committer date are derived wherever git can run over a repository, and a run
|
|
2473
|
+
# that cannot says so once and builds the rest. Deterministic per commit;
|
|
2474
|
+
# wall-clock time never enters the graph.
|
|
2475
|
+
# qualified: emit prov:qualifiedAttribution/qualifiedAssociation nodes
|
|
2476
|
+
# with roles alongside the direct properties.
|
|
2477
|
+
provenance:
|
|
2478
|
+
qualified: true
|
|
2479
|
+
|
|
2480
|
+
# Which fields a published graph may carry is read from x-manni-graph-output in
|
|
2481
|
+
# the schema set \`manni meta validate\` resolves for each page. Set that under
|
|
2482
|
+
# meta: (schemas, strict, register), not here. The default set keeps the
|
|
2483
|
+
# stewardship people fields (owner, stakeholders, reviewed-by) out.
|
|
2484
|
+
|
|
2485
|
+
# SHACL shapes \`manni graph check\` validates the built graph against. Default:
|
|
2486
|
+
# the shapes contract bundled with manni (shapes/graph/shapes-1.0.0.ttl).
|
|
2487
|
+
# check:
|
|
2488
|
+
# shapes: ["./my-shapes.ttl"]
|
|
2489
|
+
|
|
2490
|
+
# Metadata coverage gate for \`manni graph stats --check\`. A number applies to
|
|
2491
|
+
# every measured field; a map gates named fields only. Unset gates nothing.
|
|
2492
|
+
# stats:
|
|
2493
|
+
# coverageThreshold:
|
|
2494
|
+
# title: 100
|
|
2495
|
+
# description: 50
|
|
2496
|
+
|
|
2497
|
+
# Which provider \`manni graph fill\` sends pages to. Unset, the family's
|
|
2498
|
+
# top-level providers: map decides, and then \`auto\`, which detects one: an
|
|
2499
|
+
# Anthropic key, then an OpenAI key, then the Claude CLI, then a local model.
|
|
2500
|
+
# Connection settings (apiKeyEnv, baseUrl, command) live in that map, declared
|
|
2501
|
+
# once for every manni tool. \`--provider\` wins over this, \`--local\` over both.
|
|
2502
|
+
# provider: anthropic # auto | anthropic | openai | claude-cli | llama-cpp
|
|
2503
|
+
# model: claude-sonnet-4-5 # the provider's default when omitted
|
|
2504
|
+
|
|
2505
|
+
# LLM settings for \`manni graph fill\` (SKOS frontmatter proposals).
|
|
2506
|
+
fill:
|
|
2507
|
+
temperature: 0
|
|
2508
|
+
# Stop after this many inference calls. One page is one turn, and a cached
|
|
2509
|
+
# page spends none. Unset is unbounded.
|
|
2510
|
+
# maxTurns: 50
|
|
2511
|
+
cacheDir: .manni/graph/cache
|
|
2512
|
+
# fill proposes every field; confidence (0..1 per field) gates what is
|
|
2513
|
+
# written. Fields scored below the threshold are reported, not written.
|
|
2514
|
+
confidenceThreshold: 0.7
|
|
2515
|
+
# fields: defaults to every fillable field \u2014 uncomment to restrict.
|
|
2516
|
+
# Record meta-provenance (model + /graph/ pointers + confidence) on filled docs.
|
|
2517
|
+
writeProvenance: true
|
|
2518
|
+
# Reject proposals that would violate the SHACL shapes contract
|
|
2519
|
+
# (broader/narrower cycles, conflicting labels).
|
|
2520
|
+
validateGraph: true
|
|
2521
|
+
# Also propose per-section metadata. Off by default: it costs more output per
|
|
2522
|
+
# call, and section metadata is explicit-only, so review it as carefully.
|
|
2523
|
+
sections: false
|
|
2524
|
+
|
|
2525
|
+
# Local embeddings for semantic search (\`manni graph embed\`). Needs the optional
|
|
2526
|
+
# peer: npm install @huggingface/transformers
|
|
2527
|
+
# embed:
|
|
2528
|
+
# model: onnx-community/granite-embedding-small-english-r2-ONNX
|
|
2529
|
+
# dtype: q8 # q8 keeps embedding reproducible across platforms
|
|
2530
|
+
# out: graph # directory for the per-language vector sidecars
|
|
2531
|
+
# cacheDir: .manni/graph/embed-cache
|
|
2532
|
+
|
|
2533
|
+
# Optional enrichment for \`manni graph export iirds\` (the iiRDS package).
|
|
2534
|
+
# Absent, a minimal valid package is still produced.
|
|
2535
|
+
# export:
|
|
2536
|
+
# iirds:
|
|
2537
|
+
# title: My Docs # package title (default: "manni graph export")
|
|
2538
|
+
# creator: Acme Corp # Creator iirds:Party + vcard:Organization
|
|
2539
|
+
# version: "1.3" # iiRDS version literal: "1.2" | "1.3"
|
|
2540
|
+
`;
|
|
2541
|
+
function starterSection() {
|
|
2542
|
+
const body = STARTER.split("\n").map((line) => line === "" ? "" : ` ${line}`).join("\n");
|
|
2543
|
+
return `${CONFIG_SECTION}:
|
|
2544
|
+
${body}`;
|
|
2545
|
+
}
|
|
2546
|
+
async function runInit2(cwd = process.cwd()) {
|
|
2547
|
+
const path = resolve2(cwd, DEFAULT_CONFIG_FILENAME);
|
|
2548
|
+
if (!existsSync(path)) {
|
|
2549
|
+
await writeTextAtomic(path, `${HEADER}${starterSection()}`);
|
|
2550
|
+
return path;
|
|
2551
|
+
}
|
|
2552
|
+
const text2 = readFileSync(path, "utf8");
|
|
2553
|
+
let doc;
|
|
2554
|
+
try {
|
|
2555
|
+
doc = parseYaml(text2);
|
|
2556
|
+
} catch (e) {
|
|
2557
|
+
throw new GraphError(
|
|
2558
|
+
`Invalid YAML in ${DEFAULT_CONFIG_FILENAME}: ${errorMessage(e)}`
|
|
2559
|
+
);
|
|
2560
|
+
}
|
|
2561
|
+
if (doc != null && (typeof doc !== "object" || Array.isArray(doc))) {
|
|
2562
|
+
throw new GraphError(
|
|
2563
|
+
`${DEFAULT_CONFIG_FILENAME}: top level must be a mapping \u2014 not adding a \`${CONFIG_SECTION}:\` section to it.`
|
|
2564
|
+
);
|
|
2565
|
+
}
|
|
2566
|
+
if (doc != null && Object.hasOwn(doc, CONFIG_SECTION)) {
|
|
2567
|
+
throw new GraphError(
|
|
2568
|
+
`${DEFAULT_CONFIG_FILENAME} already has a \`${CONFIG_SECTION}:\` section \u2014 not overwriting.`
|
|
2569
|
+
);
|
|
2570
|
+
}
|
|
2571
|
+
const separator = text2 === "" || text2.endsWith("\n") ? "" : "\n";
|
|
2572
|
+
const blank = text2 === "" ? "" : "\n";
|
|
2573
|
+
await writeTextAtomic(
|
|
2574
|
+
path,
|
|
2575
|
+
`${text2}${separator}${blank}${starterSection()}`
|
|
2576
|
+
);
|
|
2577
|
+
return path;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
// src/graph/cli.ts
|
|
2581
|
+
function buildProgram6() {
|
|
2582
|
+
const program = new Command5();
|
|
2583
|
+
program.name("graph").description(
|
|
2584
|
+
"Deterministic knowledge graphs derived from documentation frontmatter and formatting."
|
|
2585
|
+
).version(package_default.version).option("--no-color", "disable colored output").showHelpAfterError("(add --help for usage)").exitOverride();
|
|
2586
|
+
function numericOption(flag, { min, max, integer }) {
|
|
2587
|
+
return (raw) => {
|
|
2588
|
+
const value = Number.parseFloat(raw);
|
|
2589
|
+
if (!Number.isFinite(value)) {
|
|
2590
|
+
throw new GraphError(`${flag} expects a number, got "${raw}".`);
|
|
2591
|
+
}
|
|
2592
|
+
if (integer && !Number.isInteger(value)) {
|
|
2593
|
+
throw new GraphError(`${flag} expects a whole number, got ${value}.`);
|
|
2594
|
+
}
|
|
2595
|
+
if (value < min || max !== void 0 && value > max) {
|
|
2596
|
+
const range = max === void 0 ? `>= ${min}` : `${min}..${max}`;
|
|
2597
|
+
throw new GraphError(`${flag} must be ${range}, got ${value}.`);
|
|
2598
|
+
}
|
|
2599
|
+
return value;
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
2602
|
+
function countOption(flag, min = 1) {
|
|
2603
|
+
return numericOption(flag, { min, integer: true });
|
|
2604
|
+
}
|
|
2605
|
+
function enumOption(flag, allowed) {
|
|
2606
|
+
return (raw) => {
|
|
2607
|
+
if (!allowed.includes(raw)) {
|
|
2608
|
+
throw new GraphError(
|
|
2609
|
+
`${flag} must be one of ${allowed.join(" | ")}, got "${raw}".`
|
|
2610
|
+
);
|
|
2611
|
+
}
|
|
2612
|
+
return raw;
|
|
2613
|
+
};
|
|
2614
|
+
}
|
|
2615
|
+
function choiceOption(flag, allowed) {
|
|
2616
|
+
return (raw) => {
|
|
2617
|
+
if (!allowed.includes(raw)) {
|
|
2618
|
+
throw new GraphError(`Unknown ${flag} "${raw}". Use ${allowed.join(" | ")}.`);
|
|
2619
|
+
}
|
|
2620
|
+
return raw;
|
|
2621
|
+
};
|
|
2622
|
+
}
|
|
2623
|
+
const formatOption = choiceOption("--format", GRAPH_FORMATS);
|
|
2624
|
+
const checkFormatOption = choiceOption("--format", CHECK_FORMATS);
|
|
2625
|
+
const fieldsOption = (raw) => splitList(raw).map((value) => enumOption("--fields", ALL_FILL_FIELDS)(value));
|
|
2626
|
+
function documentInputs(command, verb) {
|
|
2627
|
+
return command.argument(
|
|
2628
|
+
"[paths...]",
|
|
2629
|
+
`files, directories, or globs to ${verb} (use - for stdin)`
|
|
2630
|
+
).option(
|
|
2631
|
+
"--collection <name>",
|
|
2632
|
+
"Configured collection to read (repeatable)",
|
|
2633
|
+
collect,
|
|
2634
|
+
[]
|
|
2635
|
+
).option("--exclude <glob>", "Glob to exclude (repeatable)", collect, []).option("--as <format>", "force an input format (markdown, mdx)").option(
|
|
2636
|
+
"--ext <list>",
|
|
2637
|
+
"comma-separated extensions for directory walks",
|
|
2638
|
+
DEFAULT_EXTENSIONS.join(",")
|
|
2639
|
+
).option("--allow-empty", "treat zero matched files as success").option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file");
|
|
2640
|
+
}
|
|
2641
|
+
async function inputOptions2(paths, opts) {
|
|
2642
|
+
assertInputFormat(paths, opts.as);
|
|
2643
|
+
return {
|
|
2644
|
+
...opts.as === void 0 ? {} : { as: opts.as },
|
|
2645
|
+
ext: splitList(opts.ext),
|
|
2646
|
+
...opts.allowEmpty === true ? { allowEmpty: true } : {},
|
|
2647
|
+
...paths.includes(STDIN) ? { stdinContent: await readStdin() } : {}
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
function configInputs(command) {
|
|
2651
|
+
return command.option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file");
|
|
2652
|
+
}
|
|
2653
|
+
function rest(opts) {
|
|
2654
|
+
const { config, ...others } = opts;
|
|
2655
|
+
return others;
|
|
2656
|
+
}
|
|
2657
|
+
function documentOptions(opts) {
|
|
2658
|
+
const { configPath, noConfig } = configOption(opts.config);
|
|
2659
|
+
return {
|
|
2660
|
+
...configPath === void 0 ? {} : { config: configPath },
|
|
2661
|
+
...noConfig === true ? { noConfig } : {},
|
|
2662
|
+
...opts.collection === void 0 ? {} : { collection: opts.collection },
|
|
2663
|
+
...opts.exclude === void 0 ? {} : { exclude: opts.exclude }
|
|
2664
|
+
};
|
|
2665
|
+
}
|
|
2666
|
+
program.command("init").description("Add a starter `graph:` section to manni.config.yaml in the current directory").action(async () => {
|
|
2667
|
+
try {
|
|
2668
|
+
console.log(`Created ${await runInit2()}`);
|
|
2669
|
+
} catch (e) {
|
|
2670
|
+
fail(e);
|
|
2671
|
+
}
|
|
2672
|
+
});
|
|
2673
|
+
documentInputs(
|
|
2674
|
+
program.command("build").description("Derive the knowledge graph and write deterministic Turtle"),
|
|
2675
|
+
"build"
|
|
2676
|
+
).option("-o, --out <path>", "Output .ttl path (default: config out)").action(async (paths, opts) => {
|
|
2677
|
+
try {
|
|
2678
|
+
const result = await runBuild({
|
|
2679
|
+
paths,
|
|
2680
|
+
...documentOptions(opts),
|
|
2681
|
+
...await inputOptions2(paths, opts),
|
|
2682
|
+
out: opts.out
|
|
2683
|
+
});
|
|
2684
|
+
for (const warning of result.warnings) warn(warning);
|
|
2685
|
+
console.log(
|
|
2686
|
+
`Wrote ${result.outPath} (${result.docs} docs, ${result.quads} triples)`
|
|
2687
|
+
);
|
|
2688
|
+
} catch (e) {
|
|
2689
|
+
fail(e);
|
|
2690
|
+
}
|
|
2691
|
+
});
|
|
2692
|
+
configInputs(
|
|
2693
|
+
program.command("check").description(
|
|
2694
|
+
"Validate the built graph against the bundled SHACL shapes (errors exit 1)"
|
|
2695
|
+
)
|
|
2696
|
+
).option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2697
|
+
"--shapes <path>",
|
|
2698
|
+
"Shapes .ttl file; repeatable (default: config check.shapes, then bundled)",
|
|
2699
|
+
collect,
|
|
2700
|
+
[]
|
|
2701
|
+
).option(
|
|
2702
|
+
"-f, --format <format>",
|
|
2703
|
+
`Output: ${CHECK_FORMAT_LIST}`,
|
|
2704
|
+
checkFormatOption,
|
|
2705
|
+
"pretty"
|
|
2706
|
+
).action(
|
|
2707
|
+
async (opts) => {
|
|
2708
|
+
try {
|
|
2709
|
+
const report = await runCheck3({ ...rest(opts), ...documentOptions(opts) });
|
|
2710
|
+
console.log(renderCheck(report, opts.format));
|
|
2711
|
+
process.exitCode = report.exitCode;
|
|
2712
|
+
} catch (e) {
|
|
2713
|
+
fail(e);
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
);
|
|
2717
|
+
documentInputs(
|
|
2718
|
+
program.command("fill").description(
|
|
2719
|
+
"Propose `graph:` frontmatter fields with an LLM, gated by confidence, and write them back"
|
|
2720
|
+
),
|
|
2721
|
+
"fill"
|
|
2722
|
+
).option(
|
|
2723
|
+
"-f, --format <format>",
|
|
2724
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2725
|
+
formatOption,
|
|
2726
|
+
"pretty"
|
|
2727
|
+
).option("--dry-run", "Report proposals without writing files").option("--force", "Overwrite human-set graph fields").option("--no-cache", "Bypass the proposal cache").option("--no-validate-graph", "Skip the SHACL graph guardrail on proposals").option("--sections", "Also propose per-section metadata").option(
|
|
2728
|
+
"--max-turns <n>",
|
|
2729
|
+
"Stop after this many inference calls (a cached page costs none)",
|
|
2730
|
+
countOption("--max-turns")
|
|
2731
|
+
).option(
|
|
2732
|
+
"--confidence <n>",
|
|
2733
|
+
"Minimum model confidence (0..1) to write a field (default: config, 0.7)",
|
|
2734
|
+
numericOption("--confidence", { min: 0, max: 1 })
|
|
2735
|
+
).option(
|
|
2736
|
+
// Comma-separated and given once: one separator per list (proposal 0034).
|
|
2737
|
+
"--fields <list>",
|
|
2738
|
+
`Fields to propose (comma-separated; default: config, then every field)`,
|
|
2739
|
+
fieldsOption
|
|
2740
|
+
).option(
|
|
2741
|
+
"--provider <name>",
|
|
2742
|
+
"Provider: auto (default) | anthropic | openai | claude-cli | llama-cpp"
|
|
2743
|
+
).option("--model <model>", "Model override; needs a named provider, from here or config").option("--local", LOCAL_FLAG_HELP).action(async (paths, opts) => {
|
|
2744
|
+
try {
|
|
2745
|
+
const report = await runFill2({
|
|
2746
|
+
paths,
|
|
2747
|
+
...documentOptions(opts),
|
|
2748
|
+
...await inputOptions2(paths, opts),
|
|
2749
|
+
dryRun: opts.dryRun,
|
|
2750
|
+
force: opts.force,
|
|
2751
|
+
noCache: opts.cache === false,
|
|
2752
|
+
noValidateGraph: opts.validateGraph === false,
|
|
2753
|
+
sections: opts.sections,
|
|
2754
|
+
maxTurns: opts.maxTurns,
|
|
2755
|
+
confidence: opts.confidence,
|
|
2756
|
+
fields: opts.fields,
|
|
2757
|
+
provider: opts.provider,
|
|
2758
|
+
model: opts.model,
|
|
2759
|
+
local: opts.local
|
|
2760
|
+
});
|
|
2761
|
+
for (const warning of report.warnings) warn(warning);
|
|
2762
|
+
const rendered2 = renderFill2(report, opts.format);
|
|
2763
|
+
if (paths.includes(STDIN)) {
|
|
2764
|
+
if (report.stdinDocument !== void 0) {
|
|
2765
|
+
process.stdout.write(report.stdinDocument);
|
|
2766
|
+
}
|
|
2767
|
+
if (rendered2.length > 0) process.stderr.write(`${rendered2}
|
|
2768
|
+
`);
|
|
2769
|
+
} else {
|
|
2770
|
+
console.log(rendered2);
|
|
2771
|
+
}
|
|
2772
|
+
process.exitCode = report.exitCode;
|
|
2773
|
+
} catch (e) {
|
|
2774
|
+
fail(e);
|
|
2775
|
+
}
|
|
2776
|
+
});
|
|
2777
|
+
program.command("query").description(
|
|
2778
|
+
"Match triple patterns against the built graph (omit a term for wildcard)"
|
|
2779
|
+
).option("--subject <term>", "Subject IRI or prefixed name").option("--predicate <term>", "Predicate IRI or prefixed name").option("--object <term>", "Object IRI, prefixed name, or literal value").option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2780
|
+
"-f, --format <format>",
|
|
2781
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2782
|
+
formatOption,
|
|
2783
|
+
"pretty"
|
|
2784
|
+
).action(
|
|
2785
|
+
(opts) => {
|
|
2786
|
+
try {
|
|
2787
|
+
const { subject, predicate, object, ...others } = rest(opts);
|
|
2788
|
+
const result = runQuery({
|
|
2789
|
+
...others,
|
|
2790
|
+
...documentOptions(opts),
|
|
2791
|
+
...subject === void 0 ? {} : { s: subject },
|
|
2792
|
+
...predicate === void 0 ? {} : { p: predicate },
|
|
2793
|
+
...object === void 0 ? {} : { o: object }
|
|
2794
|
+
});
|
|
2795
|
+
console.log(renderQuery(result, opts.format));
|
|
2796
|
+
} catch (e) {
|
|
2797
|
+
fail(e);
|
|
2798
|
+
}
|
|
2799
|
+
}
|
|
2800
|
+
);
|
|
2801
|
+
program.command("stats").description(
|
|
2802
|
+
"Summarize the built graph: counts, orphans, broken links, hubs, metadata coverage"
|
|
2803
|
+
).option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2804
|
+
"-f, --format <format>",
|
|
2805
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2806
|
+
formatOption,
|
|
2807
|
+
"pretty"
|
|
2808
|
+
).option(
|
|
2809
|
+
"--check",
|
|
2810
|
+
"Exit 1 when broken internal links exist or coverage is below threshold"
|
|
2811
|
+
).option(
|
|
2812
|
+
"--top <n>",
|
|
2813
|
+
"How many most-connected docs to list",
|
|
2814
|
+
countOption("--top")
|
|
2815
|
+
).option(
|
|
2816
|
+
"--coverage-threshold <pct>",
|
|
2817
|
+
"Minimum metadata coverage % (all fields); overrides config for this run",
|
|
2818
|
+
// A percentage, so 0..100 rather than the 0..1 of a confidence. NaN here is
|
|
2819
|
+
// worse than a wrong number: `pct < NaN` is false for every field, so the
|
|
2820
|
+
// gate this flag exists to tighten would silently pass instead.
|
|
2821
|
+
numericOption("--coverage-threshold", { min: 0, max: 100 })
|
|
2822
|
+
).action(
|
|
2823
|
+
(opts) => {
|
|
2824
|
+
try {
|
|
2825
|
+
const report = runStats({ ...rest(opts), ...documentOptions(opts) });
|
|
2826
|
+
console.log(renderStats(report, opts.format));
|
|
2827
|
+
process.exitCode = report.exitCode;
|
|
2828
|
+
} catch (e) {
|
|
2829
|
+
fail(e);
|
|
2830
|
+
}
|
|
2831
|
+
}
|
|
2832
|
+
);
|
|
2833
|
+
program.command("search").description("Rank graph nodes for a text query (needs `export search`)").argument("<query>", "Text query").option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2834
|
+
"-i, --index <dir>",
|
|
2835
|
+
"Directory holding the indexes and manifest (default: beside the graph)"
|
|
2836
|
+
).option(
|
|
2837
|
+
"--lang <tag>",
|
|
2838
|
+
"Which localization to search (required when the corpus has more than one)"
|
|
2839
|
+
).option("--limit <n>", "Maximum results (default 10)", countOption("--limit")).option(
|
|
2840
|
+
"--vectors <path>",
|
|
2841
|
+
"Vector sidecar path (default: from the manifest)"
|
|
2842
|
+
).option(
|
|
2843
|
+
"--mode <mode>",
|
|
2844
|
+
`Which legs to run: ${SEARCH_MODES.join(" | ")} (default: hybrid when vectors exist)`,
|
|
2845
|
+
choiceOption("--mode", SEARCH_MODES)
|
|
2846
|
+
).option(
|
|
2847
|
+
"-f, --format <format>",
|
|
2848
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2849
|
+
formatOption,
|
|
2850
|
+
"pretty"
|
|
2851
|
+
).action(
|
|
2852
|
+
async (query, opts) => {
|
|
2853
|
+
try {
|
|
2854
|
+
const report = await runSearch({
|
|
2855
|
+
...rest(opts),
|
|
2856
|
+
...documentOptions(opts),
|
|
2857
|
+
query
|
|
2858
|
+
});
|
|
2859
|
+
console.log(renderSearch(report, opts.format));
|
|
2860
|
+
} catch (e) {
|
|
2861
|
+
fail(e);
|
|
2862
|
+
}
|
|
2863
|
+
}
|
|
2864
|
+
);
|
|
2865
|
+
program.command("traverse").description(
|
|
2866
|
+
"Walk the graph from a node, honoring scope rules, with the full trace"
|
|
2867
|
+
).argument("<node>", "Starting node: a full IRI or a prefix:local CURIE").option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2868
|
+
"-d, --depth <n>",
|
|
2869
|
+
"Maximum hops from the node (default 1; 3 under --impact)",
|
|
2870
|
+
// Zero is allowed: it means the node itself, which is a real answer.
|
|
2871
|
+
countOption("--depth", 0)
|
|
2872
|
+
).option(
|
|
2873
|
+
"--predicates <list>",
|
|
2874
|
+
"Only follow these predicates (comma-separated)"
|
|
2875
|
+
).option("--reverse", "Follow inbound edges (who points at this node)").option("--impact", "Transitive inbound reach: what a change here affects").option(
|
|
2876
|
+
"--variant <variant>",
|
|
2877
|
+
"Scope filter: product variant IRI, title, or slug"
|
|
2878
|
+
).option("--software-subject <subject>", "Scope filter: software subject").option("--lang <tag>", "Scope filter: BCP-47 language tag, matched in any case").option("--limit <n>", "Stop after this many nodes", countOption("--limit")).option(
|
|
2879
|
+
"-f, --format <format>",
|
|
2880
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2881
|
+
formatOption,
|
|
2882
|
+
"pretty"
|
|
2883
|
+
).action(
|
|
2884
|
+
(node, opts) => {
|
|
2885
|
+
try {
|
|
2886
|
+
const { softwareSubject, ...others } = rest(opts);
|
|
2887
|
+
const report = runTraverse({
|
|
2888
|
+
...others,
|
|
2889
|
+
...documentOptions(opts),
|
|
2890
|
+
...softwareSubject === void 0 ? {} : { subject: softwareSubject },
|
|
2891
|
+
node,
|
|
2892
|
+
predicates: opts.predicates === void 0 ? void 0 : splitList(opts.predicates)
|
|
2893
|
+
});
|
|
2894
|
+
console.log(renderTraverse(report, opts.format));
|
|
2895
|
+
} catch (e) {
|
|
2896
|
+
fail(e);
|
|
2897
|
+
}
|
|
2898
|
+
}
|
|
2899
|
+
);
|
|
2900
|
+
program.command("embed").description(
|
|
2901
|
+
"Compute local embeddings for the search index (needs @huggingface/transformers)"
|
|
2902
|
+
).option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2903
|
+
"-i, --index <dir>",
|
|
2904
|
+
"Directory holding the indexes and manifest (default: beside the graph)"
|
|
2905
|
+
).option(
|
|
2906
|
+
"-o, --out <dir>",
|
|
2907
|
+
"Directory for the sidecars (default: the index directory)"
|
|
2908
|
+
).option(
|
|
2909
|
+
"--embedding-model <id>",
|
|
2910
|
+
"Embedding model id (any id; `mock` for offline runs)"
|
|
2911
|
+
).option("--dtype <dtype>", "Weight quantization (default q8)").option("--no-cache", "Ignore the vector cache").option(
|
|
2912
|
+
"-f, --format <format>",
|
|
2913
|
+
`Output: ${GRAPH_FORMAT_LIST}`,
|
|
2914
|
+
formatOption,
|
|
2915
|
+
"pretty"
|
|
2916
|
+
).action(
|
|
2917
|
+
async (opts) => {
|
|
2918
|
+
try {
|
|
2919
|
+
const { embeddingModel, ...others } = rest(opts);
|
|
2920
|
+
const report = await runEmbed({
|
|
2921
|
+
...others,
|
|
2922
|
+
...documentOptions(opts),
|
|
2923
|
+
...embeddingModel === void 0 ? {} : { model: embeddingModel },
|
|
2924
|
+
noCache: opts.cache === false
|
|
2925
|
+
});
|
|
2926
|
+
console.log(renderEmbed(report, opts.format));
|
|
2927
|
+
} catch (e) {
|
|
2928
|
+
fail(e);
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
);
|
|
2932
|
+
program.command("export").description(
|
|
2933
|
+
"Reserialize the built graph into a consumer format (jsonld file, iirds package, or search index)"
|
|
2934
|
+
).argument("<target>", `What to write: ${EXPORT_TARGETS.join(" | ")}`).option("-c, --config <path>", "Path to manni.config.yaml").option("--no-config", "Ignore any discovered config file").option("-g, --graph <path>", "Graph .ttl path (default: config out)").option(
|
|
2935
|
+
"-o, --out <path>",
|
|
2936
|
+
"Output path (default: the graph path with the target's extension)"
|
|
2937
|
+
).action(
|
|
2938
|
+
async (target, opts) => {
|
|
2939
|
+
try {
|
|
2940
|
+
if (!EXPORT_TARGETS.includes(target)) {
|
|
2941
|
+
throw new GraphError(
|
|
2942
|
+
`Unknown export target "${target}". Use ${EXPORT_TARGETS.join(" | ")}.`
|
|
2943
|
+
);
|
|
2944
|
+
}
|
|
2945
|
+
const result = await runExport({
|
|
2946
|
+
...documentOptions(opts),
|
|
2947
|
+
graph: opts.graph,
|
|
2948
|
+
format: target,
|
|
2949
|
+
out: opts.out
|
|
2950
|
+
});
|
|
2951
|
+
for (const warning of result.warnings) warn(warning);
|
|
2952
|
+
console.log(
|
|
2953
|
+
`Wrote ${result.nodes} node${result.nodes === 1 ? "" : "s"} to ${result.outPath}`
|
|
2954
|
+
);
|
|
2955
|
+
} catch (e) {
|
|
2956
|
+
fail(e);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
);
|
|
2960
|
+
return program;
|
|
2961
|
+
}
|
|
2962
|
+
|
|
2963
|
+
// src/lint/cli.ts
|
|
2964
|
+
import { Command as Command6 } from "commander";
|
|
2197
2965
|
var LIST_FORMATS = /* @__PURE__ */ new Set(["pretty", "json"]);
|
|
2198
2966
|
var TOOL_OPTIONS_GROUP = "Tool options:";
|
|
2199
2967
|
function resolveColor2(program) {
|
|
@@ -2271,8 +3039,8 @@ function inputOptions(paths, options) {
|
|
|
2271
3039
|
onNotice: notice
|
|
2272
3040
|
};
|
|
2273
3041
|
}
|
|
2274
|
-
function
|
|
2275
|
-
const program = new
|
|
3042
|
+
function buildProgram7() {
|
|
3043
|
+
const program = new Command6();
|
|
2276
3044
|
program.name("lint").description(
|
|
2277
3045
|
"Validate document structure against doctype templates. Deterministic, CI-friendly."
|
|
2278
3046
|
).version(package_default.version, "-V, --version").option("--no-color", "disable colored output").showHelpAfterError().exitOverride();
|
|
@@ -2471,13 +3239,13 @@ function buildProgram6() {
|
|
|
2471
3239
|
}
|
|
2472
3240
|
|
|
2473
3241
|
// src/term/cli.ts
|
|
2474
|
-
import { Command as
|
|
3242
|
+
import { Command as Command7 } from "commander";
|
|
2475
3243
|
|
|
2476
3244
|
// src/term/commands/check.ts
|
|
2477
|
-
import { existsSync as
|
|
3245
|
+
import { existsSync as existsSync3 } from "node:fs";
|
|
2478
3246
|
|
|
2479
3247
|
// src/term/core/config.ts
|
|
2480
|
-
import { resolve } from "node:path";
|
|
3248
|
+
import { resolve as resolve3 } from "node:path";
|
|
2481
3249
|
|
|
2482
3250
|
// src/term/errors.ts
|
|
2483
3251
|
var TermError = class extends ToolError {
|
|
@@ -2657,11 +3425,11 @@ function toLoaded(file) {
|
|
|
2657
3425
|
tools: file.tools
|
|
2658
3426
|
};
|
|
2659
3427
|
}
|
|
2660
|
-
var
|
|
3428
|
+
var STDIN2 = "-";
|
|
2661
3429
|
async function resolveTermRun(opts) {
|
|
2662
|
-
const cwd =
|
|
3430
|
+
const cwd = resolve3(opts.cwd);
|
|
2663
3431
|
const wanted = opts.collection ?? [];
|
|
2664
|
-
if (wanted.length > 0 && opts.inputs.some((input) => input !==
|
|
3432
|
+
if (wanted.length > 0 && opts.inputs.some((input) => input !== STDIN2)) {
|
|
2665
3433
|
throw new TermError(
|
|
2666
3434
|
"--collection selects a configured collection; it cannot be combined with paths."
|
|
2667
3435
|
);
|
|
@@ -2676,7 +3444,7 @@ async function resolveTermRun(opts) {
|
|
|
2676
3444
|
const fromCollections = opts.inputs.length === 0;
|
|
2677
3445
|
const inputs = fromCollections ? collections.flatMap((c) => c.paths) : opts.inputs;
|
|
2678
3446
|
const base = fromCollections && inputs.length > 0 && loaded ? loaded.dir : cwd;
|
|
2679
|
-
const manifests = loaded ? (loaded.config.manifests ?? []).map((written) => ({ path:
|
|
3447
|
+
const manifests = loaded ? (loaded.config.manifests ?? []).map((written) => ({ path: resolve3(loaded.dir, written), written })) : [];
|
|
2680
3448
|
return {
|
|
2681
3449
|
config: loaded ? loaded.config : null,
|
|
2682
3450
|
inputs,
|
|
@@ -2993,12 +3761,12 @@ function compareFindings2(a, b) {
|
|
|
2993
3761
|
}
|
|
2994
3762
|
|
|
2995
3763
|
// src/term/commands/run.ts
|
|
2996
|
-
import { existsSync } from "node:fs";
|
|
2997
|
-
import { isAbsolute, relative, resolve as
|
|
3764
|
+
import { existsSync as existsSync2 } from "node:fs";
|
|
3765
|
+
import { isAbsolute, relative, resolve as resolve5 } from "node:path";
|
|
2998
3766
|
|
|
2999
3767
|
// src/term/core/load-set.ts
|
|
3000
3768
|
import { readFile } from "node:fs/promises";
|
|
3001
|
-
import { extname, resolve as
|
|
3769
|
+
import { extname, resolve as resolve4 } from "node:path";
|
|
3002
3770
|
|
|
3003
3771
|
// src/term/core/writers/markup.ts
|
|
3004
3772
|
function xmlText(value) {
|
|
@@ -4580,7 +5348,7 @@ async function loadTermSet(opts) {
|
|
|
4580
5348
|
});
|
|
4581
5349
|
const namedFiles = new Set(named);
|
|
4582
5350
|
for (const file of files) {
|
|
4583
|
-
const path =
|
|
5351
|
+
const path = resolve4(run.base, file);
|
|
4584
5352
|
if (namedFiles.has(file) && MANIFEST_EXTENSIONS.has(extname(file).toLowerCase())) {
|
|
4585
5353
|
let content2;
|
|
4586
5354
|
try {
|
|
@@ -4631,7 +5399,7 @@ async function loadTermSet(opts) {
|
|
|
4631
5399
|
|
|
4632
5400
|
// src/term/commands/run.ts
|
|
4633
5401
|
async function loadTerms(opts) {
|
|
4634
|
-
const cwd =
|
|
5402
|
+
const cwd = resolve5(opts.cwd ?? process.cwd());
|
|
4635
5403
|
const run = await resolveTermRun({
|
|
4636
5404
|
cwd,
|
|
4637
5405
|
inputs: opts.inputs,
|
|
@@ -4677,18 +5445,18 @@ function displayPath(path, cwd) {
|
|
|
4677
5445
|
function valeConfigFor(run) {
|
|
4678
5446
|
if (run.configDir === void 0) return void 0;
|
|
4679
5447
|
const path = valeConfigPath(run.tools, run.configDir);
|
|
4680
|
-
if (path !== void 0 && !
|
|
5448
|
+
if (path !== void 0 && !existsSync2(path)) {
|
|
4681
5449
|
const source = run.configSource ?? "manni.config.yaml";
|
|
4682
5450
|
throw new TermError(`${source}: tools.vale.config "${run.tools.vale?.config ?? path}" does not exist.`);
|
|
4683
5451
|
}
|
|
4684
5452
|
return path;
|
|
4685
5453
|
}
|
|
4686
|
-
function
|
|
5454
|
+
function plural4(n, one, many = `${one}s`) {
|
|
4687
5455
|
return `${String(n)} ${n === 1 ? one : many}`;
|
|
4688
5456
|
}
|
|
4689
5457
|
|
|
4690
5458
|
// src/term/commands/check.ts
|
|
4691
|
-
async function
|
|
5459
|
+
async function runCheck4(opts) {
|
|
4692
5460
|
const { cwd, run, set } = await loadTerms(opts);
|
|
4693
5461
|
const config = run.config;
|
|
4694
5462
|
const findings = checkTermSet(set, {
|
|
@@ -4704,7 +5472,7 @@ async function runCheck3(opts) {
|
|
|
4704
5472
|
cwd,
|
|
4705
5473
|
{ current: DEFAULT_TERM_BASELINE_PATH }
|
|
4706
5474
|
);
|
|
4707
|
-
if (request !== null && !
|
|
5475
|
+
if (request !== null && !existsSync3(request.absPath)) {
|
|
4708
5476
|
if (opts.baseline !== true) {
|
|
4709
5477
|
throw new TermError(
|
|
4710
5478
|
`Baseline "${request.label}" not found. Record one with \`manni term check --baseline\`.`
|
|
@@ -4730,7 +5498,7 @@ async function runCheck3(opts) {
|
|
|
4730
5498
|
import { join } from "node:path";
|
|
4731
5499
|
|
|
4732
5500
|
// src/term/core/writers/page.ts
|
|
4733
|
-
import { parse as
|
|
5501
|
+
import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
|
|
4734
5502
|
function pageMetadata(term) {
|
|
4735
5503
|
const { record } = term;
|
|
4736
5504
|
const metadata = { title: record.label };
|
|
@@ -4757,7 +5525,7 @@ ${stringifyYaml2(metadata, { lineWidth: 0 })}---
|
|
|
4757
5525
|
function metaContent(value) {
|
|
4758
5526
|
if (typeof value === "string" && !value.includes("\n")) {
|
|
4759
5527
|
try {
|
|
4760
|
-
if (
|
|
5528
|
+
if (parseYaml2(value) === value) return value;
|
|
4761
5529
|
} catch {
|
|
4762
5530
|
}
|
|
4763
5531
|
}
|
|
@@ -5088,7 +5856,7 @@ var SCHEME_ID = "";
|
|
|
5088
5856
|
function literal(term, value) {
|
|
5089
5857
|
return term.language === void 0 ? value : { "@value": value, "@language": term.language };
|
|
5090
5858
|
}
|
|
5091
|
-
function
|
|
5859
|
+
function resolve6(terms, self, value) {
|
|
5092
5860
|
const wanted = value.toLowerCase();
|
|
5093
5861
|
const others = terms.filter((t) => t !== self);
|
|
5094
5862
|
return others.find((t) => t.record.label.toLowerCase() === wanted) ?? others.find((t) => t.id.toLowerCase() === wanted);
|
|
@@ -5096,7 +5864,7 @@ function resolve4(terms, self, value) {
|
|
|
5096
5864
|
function references(terms, self, values) {
|
|
5097
5865
|
const ids = [];
|
|
5098
5866
|
for (const value of values ?? []) {
|
|
5099
|
-
const target =
|
|
5867
|
+
const target = resolve6(terms, self, value);
|
|
5100
5868
|
if (target !== void 0 && !ids.includes(target.id)) ids.push(target.id);
|
|
5101
5869
|
}
|
|
5102
5870
|
return ids.map((id) => ({ "@id": `#${id}` }));
|
|
@@ -5493,7 +6261,7 @@ async function runGet(opts) {
|
|
|
5493
6261
|
set.terms.flatMap((t) => [t.id, t.record.label])
|
|
5494
6262
|
);
|
|
5495
6263
|
const hint = nearest === void 0 ? "." : `; did you mean "${nearest}"?`;
|
|
5496
|
-
throw new TermError(`no term "${opts.term}". ${
|
|
6264
|
+
throw new TermError(`no term "${opts.term}". ${plural4(set.terms.length, "term")}${hint}`);
|
|
5497
6265
|
}
|
|
5498
6266
|
|
|
5499
6267
|
// src/term/core/lint.ts
|
|
@@ -5746,9 +6514,9 @@ async function runList2(opts) {
|
|
|
5746
6514
|
}
|
|
5747
6515
|
|
|
5748
6516
|
// src/term/commands/write.ts
|
|
5749
|
-
import { existsSync as
|
|
6517
|
+
import { existsSync as existsSync4, statSync } from "node:fs";
|
|
5750
6518
|
import { lstat, mkdir, readdir, readFile as readFile2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
|
|
5751
|
-
import { dirname, extname as extname2, join as join4, resolve as
|
|
6519
|
+
import { dirname, extname as extname2, join as join4, resolve as resolve7 } from "node:path";
|
|
5752
6520
|
var SUGGESTED_SECTION = "*.md";
|
|
5753
6521
|
var STYLE2 = "Terms";
|
|
5754
6522
|
function writeFormats(writers = TERM_WRITERS) {
|
|
@@ -5782,7 +6550,7 @@ async function existingFor(target) {
|
|
|
5782
6550
|
const existing = /* @__PURE__ */ new Map();
|
|
5783
6551
|
if (target.shape === "directory") {
|
|
5784
6552
|
await filesUnder(target.path, existing);
|
|
5785
|
-
} else if (
|
|
6553
|
+
} else if (existsSync4(target.path) && !isDirectory(target.path)) {
|
|
5786
6554
|
existing.set(target.path, await readFile2(target.path, "utf8"));
|
|
5787
6555
|
}
|
|
5788
6556
|
return existing;
|
|
@@ -5802,7 +6570,7 @@ function droppedFrom(format, shape, readers) {
|
|
|
5802
6570
|
async function applyChanges(files, removals, existing, cwd, write) {
|
|
5803
6571
|
const changes = [];
|
|
5804
6572
|
for (const file of files) {
|
|
5805
|
-
const before = existing.get(file.path) ?? (
|
|
6573
|
+
const before = existing.get(file.path) ?? (existsSync4(file.path) ? await readFile2(file.path, "utf8") : void 0);
|
|
5806
6574
|
if (before === file.content) continue;
|
|
5807
6575
|
changes.push({ path: displayPath(file.path, cwd), action: before === void 0 ? "create" : "change" });
|
|
5808
6576
|
if (write) {
|
|
@@ -5811,14 +6579,14 @@ async function applyChanges(files, removals, existing, cwd, write) {
|
|
|
5811
6579
|
}
|
|
5812
6580
|
}
|
|
5813
6581
|
for (const path of removals) {
|
|
5814
|
-
if (!
|
|
6582
|
+
if (!existsSync4(path)) continue;
|
|
5815
6583
|
changes.push({ path: displayPath(path, cwd), action: "remove" });
|
|
5816
6584
|
if (write) await rm2(path, { force: true });
|
|
5817
6585
|
}
|
|
5818
6586
|
return changes;
|
|
5819
6587
|
}
|
|
5820
6588
|
async function runWrite(opts) {
|
|
5821
|
-
const cwd =
|
|
6589
|
+
const cwd = resolve7(opts.cwd ?? process.cwd());
|
|
5822
6590
|
const writers = opts.writers ?? TERM_WRITERS;
|
|
5823
6591
|
const readers = opts.readers ?? TERM_READERS;
|
|
5824
6592
|
const check = opts.check === true;
|
|
@@ -5846,19 +6614,19 @@ async function runWrite(opts) {
|
|
|
5846
6614
|
"Vale found no config file. Set tools.vale.config in manni.config.yaml, or pass -o <styles directory>."
|
|
5847
6615
|
);
|
|
5848
6616
|
}
|
|
5849
|
-
targetPath =
|
|
6617
|
+
targetPath = resolve7(cwd, resolved.stylesPath);
|
|
5850
6618
|
if (!Object.values(resolved.baseStyles).some((styles) => styles.includes(STYLE2))) {
|
|
5851
6619
|
const first = Object.entries(resolved.baseStyles)[0];
|
|
5852
6620
|
const [section, styles] = first ?? [SUGGESTED_SECTION, []];
|
|
5853
6621
|
wiring = {
|
|
5854
|
-
rootIni: displayPath(
|
|
6622
|
+
rootIni: displayPath(resolve7(cwd, resolved.rootIni), cwd),
|
|
5855
6623
|
section,
|
|
5856
6624
|
styles: [...styles, STYLE2],
|
|
5857
6625
|
addSection: first === void 0
|
|
5858
6626
|
};
|
|
5859
6627
|
}
|
|
5860
6628
|
} else {
|
|
5861
|
-
targetPath =
|
|
6629
|
+
targetPath = resolve7(cwd, opts.out);
|
|
5862
6630
|
}
|
|
5863
6631
|
const shape = format === "vale" || /[\\/]$/.test(opts.out ?? "") || isDirectory(targetPath) ? "directory" : "file";
|
|
5864
6632
|
const target = { path: targetPath, shape };
|
|
@@ -6119,7 +6887,7 @@ function renderListPretty(terms) {
|
|
|
6119
6887
|
const rows = terms.map(
|
|
6120
6888
|
(t) => `${t.id.padEnd(idWidth)}${t.record.label.padEnd(labelWidth)}${(t.record["alt-labels"] ?? []).join(", ")}`.trimEnd()
|
|
6121
6889
|
);
|
|
6122
|
-
return [...rows,
|
|
6890
|
+
return [...rows, plural4(terms.length, "term")].join("\n");
|
|
6123
6891
|
}
|
|
6124
6892
|
var GET_FIELDS = [
|
|
6125
6893
|
"alt-labels",
|
|
@@ -6160,21 +6928,21 @@ function severityText(c, severity) {
|
|
|
6160
6928
|
function counts(findings) {
|
|
6161
6929
|
const parts = [];
|
|
6162
6930
|
const n = (severity) => findings.filter((f) => f.severity === severity).length;
|
|
6163
|
-
if (n("error") > 0) parts.push(
|
|
6164
|
-
if (n("warning") > 0) parts.push(
|
|
6165
|
-
if (n("notice") > 0) parts.push(
|
|
6931
|
+
if (n("error") > 0) parts.push(plural4(n("error"), "error"));
|
|
6932
|
+
if (n("warning") > 0) parts.push(plural4(n("warning"), "warning"));
|
|
6933
|
+
if (n("notice") > 0) parts.push(plural4(n("notice"), "notice"));
|
|
6166
6934
|
return parts.join(", ");
|
|
6167
6935
|
}
|
|
6168
6936
|
function renderFindingsPretty(report, opts) {
|
|
6169
6937
|
const c = palette(opts.color);
|
|
6170
6938
|
const { baseline } = report;
|
|
6171
6939
|
if (baseline?.written === true) {
|
|
6172
|
-
return `${c.green("\u2713")} ${
|
|
6940
|
+
return `${c.green("\u2713")} ${plural4(report.found, "finding")} recorded in ${baseline.path}`;
|
|
6173
6941
|
}
|
|
6174
6942
|
const baselined = baseline !== void 0 && baseline.suppressed > 0 ? `, ${String(baseline.suppressed)} baselined` : "";
|
|
6175
|
-
const terms =
|
|
6943
|
+
const terms = plural4(report.terms, "term");
|
|
6176
6944
|
if (report.findings.length === 0) {
|
|
6177
|
-
const references2 = opts.references === false ? "" : `, ${
|
|
6945
|
+
const references2 = opts.references === false ? "" : `, ${plural4(report.references, "reference")}`;
|
|
6178
6946
|
return `${c.green("\u2713")} ${terms}${references2}, no findings${baselined}`;
|
|
6179
6947
|
}
|
|
6180
6948
|
const ruleWidth = Math.max(RULE_WIDTH, ...report.findings.map((f) => f.ruleId.length + 2));
|
|
@@ -6224,11 +6992,11 @@ function valeLines(report) {
|
|
|
6224
6992
|
const acronyms = [];
|
|
6225
6993
|
for (const file of report.files) {
|
|
6226
6994
|
const name = baseName2(file.path);
|
|
6227
|
-
if (CASING_RULES.has(name)) rows.push([name,
|
|
6228
|
-
else if (name === "Deprecated.yml") rows.push([name,
|
|
6995
|
+
if (CASING_RULES.has(name)) rows.push([name, plural4(swapCount(file.content), "label")]);
|
|
6996
|
+
else if (name === "Deprecated.yml") rows.push([name, plural4(swapCount(file.content), "swap")]);
|
|
6229
6997
|
else acronyms.push(name);
|
|
6230
6998
|
}
|
|
6231
|
-
if (acronyms.length > 0) rows.push([acronyms.join(", "),
|
|
6999
|
+
if (acronyms.length > 0) rows.push([acronyms.join(", "), plural4(acronyms.length, "acronym")]);
|
|
6232
7000
|
const width = Math.max(0, ...rows.map(([name]) => name.length)) + 2;
|
|
6233
7001
|
return rows.map(([name, what]) => ` ${name.padEnd(width)}${what}`);
|
|
6234
7002
|
}
|
|
@@ -6241,13 +7009,13 @@ function droppedLine(report) {
|
|
|
6241
7009
|
ids.set(field, set);
|
|
6242
7010
|
}
|
|
6243
7011
|
const fields = [...ids.entries()].map(([field, set]) => ({ field, n: set.size })).sort((a, b) => a.n - b.n || TERM_FIELDS.indexOf(a.field) - TERM_FIELDS.indexOf(b.field));
|
|
6244
|
-
const parts = fields.map(({ field, n }, i) => `${field} on ${i === 0 ?
|
|
7012
|
+
const parts = fields.map(({ field, n }, i) => `${field} on ${i === 0 ? plural4(n, "term") : String(n)}`);
|
|
6245
7013
|
return ` dropped from ${report.droppedFrom ?? report.format ?? "the render"}: ${parts.join(", ")}`;
|
|
6246
7014
|
}
|
|
6247
7015
|
function skippedLine(report) {
|
|
6248
7016
|
if (report.skipped.length === 0) return void 0;
|
|
6249
7017
|
const construct = report.droppedFrom ?? report.format ?? "the render";
|
|
6250
|
-
const count =
|
|
7018
|
+
const count = plural4(report.skipped.length, "term");
|
|
6251
7019
|
return ` skipped ${count} ${construct} cannot read without a definition: ${report.skipped.join(", ")}`;
|
|
6252
7020
|
}
|
|
6253
7021
|
function renderNotes(report) {
|
|
@@ -6265,11 +7033,11 @@ function renderWritePretty(report) {
|
|
|
6265
7033
|
return [...changes, ...renderNotes(report)].join("\n");
|
|
6266
7034
|
}
|
|
6267
7035
|
if (report.mode === "in-place") {
|
|
6268
|
-
return report.changes.length === 0 ? "Nothing to write" : `Wrote ${
|
|
7036
|
+
return report.changes.length === 0 ? "Nothing to write" : `Wrote ${plural4(report.changes.length, "file")}`;
|
|
6269
7037
|
}
|
|
6270
7038
|
const each = report.shape === "directory" && report.format !== "vale" ? ", one file each" : "";
|
|
6271
7039
|
const written = report.terms - report.skipped.length;
|
|
6272
|
-
const lines = [`Wrote ${
|
|
7040
|
+
const lines = [`Wrote ${plural4(written, "term")} to ${report.target ?? ""}${each}`];
|
|
6273
7041
|
if (report.format === "vale") lines.push(...valeLines(report));
|
|
6274
7042
|
lines.push(...renderNotes(report));
|
|
6275
7043
|
return lines.join("\n");
|
|
@@ -6350,8 +7118,8 @@ function renderFindings(report, format, options, references2) {
|
|
|
6350
7118
|
if (text2.length > 0 || !OMITTED_WHEN_CLEAN.has(format)) print(text2);
|
|
6351
7119
|
process.exitCode = report.summary.failed > 0 ? 1 : 0;
|
|
6352
7120
|
}
|
|
6353
|
-
function
|
|
6354
|
-
const program = new
|
|
7121
|
+
function buildProgram8() {
|
|
7122
|
+
const program = new Command7();
|
|
6355
7123
|
program.name("term").description("Check, lint and render a docset's terms and the references into them.").version(package_default.version, "-V, --version").showHelpAfterError("(add --help for usage)").exitOverride();
|
|
6356
7124
|
withInputs(
|
|
6357
7125
|
program.command("list").description("List the resolved entries").argument("[paths...]", "files, directories, or globs to read (use - for stdin)")
|
|
@@ -6403,7 +7171,7 @@ function buildProgram7() {
|
|
|
6403
7171
|
).action(async (paths, options) => {
|
|
6404
7172
|
try {
|
|
6405
7173
|
const format = formatChoice(options.format, FINDING_FORMATS);
|
|
6406
|
-
const report = await
|
|
7174
|
+
const report = await runCheck4({
|
|
6407
7175
|
...await coreOptions(paths, options),
|
|
6408
7176
|
...options.baseline === true ? { baseline: true } : {}
|
|
6409
7177
|
});
|
|
@@ -6469,12 +7237,12 @@ function buildProgram7() {
|
|
|
6469
7237
|
}
|
|
6470
7238
|
|
|
6471
7239
|
// src/cli.ts
|
|
6472
|
-
function
|
|
7240
|
+
function buildProgram9() {
|
|
6473
7241
|
const meta = buildProgram().name("meta").description(
|
|
6474
7242
|
"Validate, read, query and fill document metadata against JSON Schema"
|
|
6475
7243
|
);
|
|
6476
7244
|
const metaCommands = new Set(meta.commands.map((c) => c.name()));
|
|
6477
|
-
const program = new
|
|
7245
|
+
const program = new Command8();
|
|
6478
7246
|
program.name("manni").description("Tools for documentation that is meant to be checked.").version(package_default.version, "-V, --version").showHelpAfterError("(add --help for usage)").exitOverride().configureOutput({
|
|
6479
7247
|
outputError: (str, write) => {
|
|
6480
7248
|
write(str);
|
|
@@ -6500,7 +7268,7 @@ function buildProgram8() {
|
|
|
6500
7268
|
)
|
|
6501
7269
|
);
|
|
6502
7270
|
program.addCommand(
|
|
6503
|
-
|
|
7271
|
+
buildProgram7().name("lint").description(
|
|
6504
7272
|
"Validate document structure against doctype templates, routed by a page's type"
|
|
6505
7273
|
)
|
|
6506
7274
|
);
|
|
@@ -6510,15 +7278,20 @@ function buildProgram8() {
|
|
|
6510
7278
|
)
|
|
6511
7279
|
);
|
|
6512
7280
|
program.addCommand(
|
|
6513
|
-
|
|
7281
|
+
buildProgram8().name("term").description("Check, lint and render a docset's terms and the references into them")
|
|
7282
|
+
);
|
|
7283
|
+
program.addCommand(
|
|
7284
|
+
buildProgram6().name("graph").description(
|
|
7285
|
+
"Deterministic knowledge graphs derived from documentation frontmatter and formatting"
|
|
7286
|
+
)
|
|
6514
7287
|
);
|
|
6515
7288
|
program.addCommand(
|
|
6516
7289
|
buildProgram5().name("key").description("Set and rotate the family key that encrypted values are encrypted with.")
|
|
6517
7290
|
);
|
|
6518
7291
|
return program;
|
|
6519
7292
|
}
|
|
6520
|
-
runIfMain(import.meta.url,
|
|
7293
|
+
runIfMain(import.meta.url, buildProgram9);
|
|
6521
7294
|
export {
|
|
6522
|
-
|
|
7295
|
+
buildProgram9 as buildProgram
|
|
6523
7296
|
};
|
|
6524
7297
|
//# sourceMappingURL=cli.js.map
|