@nielspeter/eess-md 0.1.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.
Files changed (91) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/dist/builders/docs.d.ts +28 -0
  4. package/dist/builders/docs.d.ts.map +1 -0
  5. package/dist/builders/docs.js +44 -0
  6. package/dist/builders/docs.js.map +1 -0
  7. package/dist/builders/links.d.ts +26 -0
  8. package/dist/builders/links.d.ts.map +1 -0
  9. package/dist/builders/links.js +42 -0
  10. package/dist/builders/links.js.map +1 -0
  11. package/dist/builders/pointers.d.ts +26 -0
  12. package/dist/builders/pointers.d.ts.map +1 -0
  13. package/dist/builders/pointers.js +43 -0
  14. package/dist/builders/pointers.js.map +1 -0
  15. package/dist/builders/rows.d.ts +36 -0
  16. package/dist/builders/rows.d.ts.map +1 -0
  17. package/dist/builders/rows.js +42 -0
  18. package/dist/builders/rows.js.map +1 -0
  19. package/dist/builders/task-items.d.ts +21 -0
  20. package/dist/builders/task-items.d.ts.map +1 -0
  21. package/dist/builders/task-items.js +33 -0
  22. package/dist/builders/task-items.js.map +1 -0
  23. package/dist/builders/vocabulary.d.ts +66 -0
  24. package/dist/builders/vocabulary.d.ts.map +1 -0
  25. package/dist/builders/vocabulary.js +118 -0
  26. package/dist/builders/vocabulary.js.map +1 -0
  27. package/dist/conditions/pointer-resolve.d.ts +51 -0
  28. package/dist/conditions/pointer-resolve.d.ts.map +1 -0
  29. package/dist/conditions/pointer-resolve.js +164 -0
  30. package/dist/conditions/pointer-resolve.js.map +1 -0
  31. package/dist/conditions/resolve.d.ts +36 -0
  32. package/dist/conditions/resolve.d.ts.map +1 -0
  33. package/dist/conditions/resolve.js +108 -0
  34. package/dist/conditions/resolve.js.map +1 -0
  35. package/dist/conditions/structure.d.ts +13 -0
  36. package/dist/conditions/structure.d.ts.map +1 -0
  37. package/dist/conditions/structure.js +42 -0
  38. package/dist/conditions/structure.js.map +1 -0
  39. package/dist/conditions/table-rows.d.ts +28 -0
  40. package/dist/conditions/table-rows.d.ts.map +1 -0
  41. package/dist/conditions/table-rows.js +25 -0
  42. package/dist/conditions/table-rows.js.map +1 -0
  43. package/dist/corpus.d.ts +40 -0
  44. package/dist/corpus.d.ts.map +1 -0
  45. package/dist/corpus.js +73 -0
  46. package/dist/corpus.js.map +1 -0
  47. package/dist/index.d.ts +26 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +22 -0
  50. package/dist/index.js.map +1 -0
  51. package/dist/model/document.d.ts +78 -0
  52. package/dist/model/document.d.ts.map +1 -0
  53. package/dist/model/document.js +68 -0
  54. package/dist/model/document.js.map +1 -0
  55. package/dist/model/links.d.ts +32 -0
  56. package/dist/model/links.d.ts.map +1 -0
  57. package/dist/model/links.js +53 -0
  58. package/dist/model/links.js.map +1 -0
  59. package/dist/model/pointers.d.ts +29 -0
  60. package/dist/model/pointers.d.ts.map +1 -0
  61. package/dist/model/pointers.js +65 -0
  62. package/dist/model/pointers.js.map +1 -0
  63. package/dist/model/query.d.ts +6 -0
  64. package/dist/model/query.d.ts.map +1 -0
  65. package/dist/model/query.js +15 -0
  66. package/dist/model/query.js.map +1 -0
  67. package/dist/model/rows.d.ts +52 -0
  68. package/dist/model/rows.d.ts.map +1 -0
  69. package/dist/model/rows.js +42 -0
  70. package/dist/model/rows.js.map +1 -0
  71. package/dist/model/task-items.d.ts +28 -0
  72. package/dist/model/task-items.d.ts.map +1 -0
  73. package/dist/model/task-items.js +47 -0
  74. package/dist/model/task-items.js.map +1 -0
  75. package/dist/model/violation.d.ts +23 -0
  76. package/dist/model/violation.d.ts.map +1 -0
  77. package/dist/model/violation.js +22 -0
  78. package/dist/model/violation.js.map +1 -0
  79. package/dist/predicates/document.d.ts +8 -0
  80. package/dist/predicates/document.d.ts.map +1 -0
  81. package/dist/predicates/document.js +22 -0
  82. package/dist/predicates/document.js.map +1 -0
  83. package/dist/rules/adr.d.ts +34 -0
  84. package/dist/rules/adr.d.ts.map +1 -0
  85. package/dist/rules/adr.js +103 -0
  86. package/dist/rules/adr.js.map +1 -0
  87. package/dist/rules/ledger.d.ts +44 -0
  88. package/dist/rules/ledger.d.ts.map +1 -0
  89. package/dist/rules/ledger.js +119 -0
  90. package/dist/rules/ledger.js.map +1 -0
  91. package/package.json +60 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Niels Peter
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # @nielspeter/eess-md
2
+
3
+ Architecture testing for a **Markdown corpus** — the Markdown dialect of the [eess](../../README.md) family. _Specifications you can run._
4
+
5
+ A repo's markdown (docs, ADRs, plans) should stay honest: cross-links resolve, code pointers point at real lines, and — if you use them — ADRs declare how they're enforced. `eess-md` validates all of that on the shared [`@nielspeter/eess`](../core) kernel, so you write a declarative rules file, not a custom validator script.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install -D @nielspeter/eess-md
11
+ ```
12
+
13
+ ## Example
14
+
15
+ Neutral, universal conventions — a `docs/**` corpus, English headers, a minimal frozen set:
16
+
17
+ ```typescript
18
+ import { corpus, docs, links, pointers } from '@nielspeter/eess-md'
19
+
20
+ const c = corpus({
21
+ roots: ['docs/**'],
22
+ frozen: ['**/completed/**', '**/archived/**'], // historical records: reported, never failed
23
+ })
24
+
25
+ // markdown-to-markdown links resolve
26
+ links(c).that().areInternal().should().resolve().check()
27
+
28
+ // `path:line` code pointers ground against real files
29
+ // (resolves by unique path-suffix: `admin/index.vue` matches the one file
30
+ // ending with it; pass `{ paths: 'exact' }` to require full repo paths)
31
+ pointers(c).that().areLive().should().resolve().check()
32
+
33
+ // documents have the sections/tables they should
34
+ docs(c).that().resideInFolder('docs/adr/**').should().haveSection('Context').check()
35
+ ```
36
+
37
+ `docs()`, `links()`, and `pointers()` are entry points on the same fluent
38
+ `.that().should().check()` chain as every other eess dialect — with `.because()`,
39
+ `.warn()`, `.excluding()`, and baseline mode all working unchanged.
40
+
41
+ ## Binding a spec table to code: `rows()` + `correspondence()`
42
+
43
+ A markdown table is a **spec** — a package list, an ADR index, a field
44
+ reference. `rows()` turns its body rows into first-class elements, and
45
+ `.select()` (inherited from the kernel, on every eess builder) turns any
46
+ selection into one side of a `correspondence()`. Bind a table to what it
47
+ describes and drift in either direction fails the build:
48
+
49
+ ```typescript
50
+ import { corpus, rows } from '@nielspeter/eess-md'
51
+ import { correspondence } from '@nielspeter/eess'
52
+
53
+ const c = corpus({ roots: ['README.md'] })
54
+
55
+ // each body row of the "Packages" table becomes a selectable element
56
+ const packageRows = rows(c, {
57
+ section: /^Packages$/,
58
+ columns: { pkg: /^Package$/, status: /^Status$/ }, // role → header pattern
59
+ }).select({
60
+ label: 'README package row',
61
+ identify: (r) => ({ name: r.get('pkg'), file: r.doc.relPath, line: r.line }),
62
+ })
63
+
64
+ // `workspacePackages` is a plain Selection you build from the filesystem
65
+ correspondence({ left: packageRows, right: workspacePackages, keyBy: (e) => e.name })
66
+ .should()
67
+ .beComplete({ direction: 'both' }) // every row has a package AND vice versa
68
+ .because('the README package table must not drift from the workspace')
69
+ .check()
70
+ ```
71
+
72
+ A row carries `get(role)` (the located cell text), its real source `line`, and
73
+ back references to its `doc` and `table`. Rows are drawn from **every** table
74
+ that matches the section and columns — a correspondence source never silently
75
+ ignores a second matching table. Cell text is flattened (bold/links become
76
+ plain text); a link's URL is not recoverable from a row, so verify link targets
77
+ with a `links()` rule alongside.
78
+
79
+ `direction: 'both'` is doing two jobs: left-to-right catches a table row with no
80
+ package (a _lying_ spec), and right-to-left catches a package with no row
81
+ (_unclaimed_ code — coverage). Drop to a single direction when you only want
82
+ one. Coverage is the same "gate on declaredness" move the ADR `## Enforcement`
83
+ table makes, pointed at code.
84
+
85
+ ## The ADR gate is an opt-in, opinionated preset
86
+
87
+ `@nielspeter/eess-md/rules/adr` ships `adrEnforcement()`, which implements **one
88
+ specific methodology** — the EESS enforcement-tier model (a `## Enforcement`
89
+ table with tier + mechanism + status). Most teams' ADRs (MADR, Nygard) have none
90
+ of that, and **you don't need it to get value from `eess-md`** — links, pointers,
91
+ sections, and tables all validate through the generic primitives above. Reach for
92
+ `adrEnforcement()` only if your ADRs follow the tier model; otherwise compose your
93
+ own gate from `haveSection` / `haveTable` / `haveTableRowsSatisfying` / `resolve`.
94
+
95
+ ```typescript
96
+ import { adrEnforcement } from '@nielspeter/eess-md/rules/adr'
97
+
98
+ adrEnforcement(c, { dir: 'docs/adr/**' }) // section+table present, valid tiers, citations resolve
99
+ ```
100
+
101
+ An index/schema doc in the ADR directory (`README.md` or `index.md`) is exempt —
102
+ it isn't an ADR, so it isn't required to carry an `## Enforcement` table.
103
+
104
+ ## Custom conventions (localization, project-specific lifecycle)
105
+
106
+ Everything project-specific is configuration, not a fork — column headers match
107
+ by pattern (non-English works), and frozen folders are globs:
108
+
109
+ ```typescript
110
+ const c = corpus({
111
+ roots: ['work/**', 'docs/**'], // plans live under work/
112
+ frozen: ['**/completed/**', '**/delivered/**', '**/wont-do/**', '**/archived/**'],
113
+ ignore: ['node_modules/**', '.git/**', '.nuxt/**'],
114
+ })
115
+
116
+ adrEnforcement(c, {
117
+ section: /håndhævelse/i,
118
+ columns: { tier: /tier/i, mechanism: /mekanisme/i, status: /status/i }, // Danish headers
119
+ })
120
+ ```
121
+
122
+ ## License
123
+
124
+ MIT
@@ -0,0 +1,28 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import type { Corpus } from '../corpus.js';
3
+ import type { MdDocument } from '../model/document.js';
4
+ import { type HaveTableOptions } from '../conditions/structure.js';
5
+ /**
6
+ * Rule builder over the corpus's markdown documents.
7
+ *
8
+ * `.that()` methods filter documents (predicates); `.should()` methods assert
9
+ * over them (conditions). `haveSection` is a single phase-dispatched method —
10
+ * a predicate after `.that()`, a condition after `.should()` — mirroring the
11
+ * dual-use methods in the TS dialect (plan 0041).
12
+ */
13
+ export declare class DocsRuleBuilder extends RuleBuilder<MdDocument, Corpus> {
14
+ protected getElements(): MdDocument[];
15
+ /** Filter to documents whose repo-relative path matches the folder glob. */
16
+ resideInFolder(glob: string): this;
17
+ /** Filter to documents whose repo-relative path matches the file glob. */
18
+ resideInFile(glob: string): this;
19
+ /** Filter to documents whose file name matches the pattern. */
20
+ haveNameMatching(re: RegExp): this;
21
+ /** Dual-use: predicate after `.that()`, condition after `.should()`. */
22
+ haveSection(name: string | RegExp): this;
23
+ /** Assert each matched document contains a GFM table with the required columns. */
24
+ haveTable(opts: HaveTableOptions): this;
25
+ }
26
+ /** Entry point: build rules over the corpus's markdown documents. */
27
+ export declare function docs(corpus: Corpus): DocsRuleBuilder;
28
+ //# sourceMappingURL=docs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/builders/docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAOtD,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAA;AAEnC;;;;;;;GAOG;AACH,qBAAa,eAAgB,SAAQ,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC;IAClE,SAAS,CAAC,WAAW,IAAI,UAAU,EAAE;IAIrC,4EAA4E;IAC5E,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIlC,0EAA0E;IAC1E,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIhC,+DAA+D;IAC/D,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAIlC,wEAAwE;IACxE,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAOxC,mFAAmF;IACnF,SAAS,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;CAGxC;AAED,qEAAqE;AACrE,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAEpD"}
@@ -0,0 +1,44 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import { resideInFolder, resideInFile, haveNameMatching, haveSectionPredicate, } from '../predicates/document.js';
3
+ import { haveSection as haveSectionCondition, haveTable as haveTableCondition, } from '../conditions/structure.js';
4
+ /**
5
+ * Rule builder over the corpus's markdown documents.
6
+ *
7
+ * `.that()` methods filter documents (predicates); `.should()` methods assert
8
+ * over them (conditions). `haveSection` is a single phase-dispatched method —
9
+ * a predicate after `.that()`, a condition after `.should()` — mirroring the
10
+ * dual-use methods in the TS dialect (plan 0041).
11
+ */
12
+ export class DocsRuleBuilder extends RuleBuilder {
13
+ getElements() {
14
+ return this.project.documents();
15
+ }
16
+ /** Filter to documents whose repo-relative path matches the folder glob. */
17
+ resideInFolder(glob) {
18
+ return this.addPredicate(resideInFolder(glob));
19
+ }
20
+ /** Filter to documents whose repo-relative path matches the file glob. */
21
+ resideInFile(glob) {
22
+ return this.addPredicate(resideInFile(glob));
23
+ }
24
+ /** Filter to documents whose file name matches the pattern. */
25
+ haveNameMatching(re) {
26
+ return this.addPredicate(haveNameMatching(re));
27
+ }
28
+ /** Dual-use: predicate after `.that()`, condition after `.should()`. */
29
+ haveSection(name) {
30
+ if (this._phase === 'condition') {
31
+ return this.addCondition(haveSectionCondition(name));
32
+ }
33
+ return this.addPredicate(haveSectionPredicate(name));
34
+ }
35
+ /** Assert each matched document contains a GFM table with the required columns. */
36
+ haveTable(opts) {
37
+ return this.addCondition(haveTableCondition(opts));
38
+ }
39
+ }
40
+ /** Entry point: build rules over the corpus's markdown documents. */
41
+ export function docs(corpus) {
42
+ return new DocsRuleBuilder(corpus);
43
+ }
44
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/builders/docs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAG9C,OAAO,EACL,cAAc,EACd,YAAY,EACZ,gBAAgB,EAChB,oBAAoB,GACrB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACL,WAAW,IAAI,oBAAoB,EACnC,SAAS,IAAI,kBAAkB,GAEhC,MAAM,4BAA4B,CAAA;AAEnC;;;;;;;GAOG;AACH,MAAM,OAAO,eAAgB,SAAQ,WAA+B;IACxD,WAAW;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAA;IACjC,CAAC;IAED,4EAA4E;IAC5E,cAAc,CAAC,IAAY;QACzB,OAAO,IAAI,CAAC,YAAY,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAA;IAChD,CAAC;IAED,0EAA0E;IAC1E,YAAY,CAAC,IAAY;QACvB,OAAO,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAA;IAC9C,CAAC;IAED,+DAA+D;IAC/D,gBAAgB,CAAC,EAAU;QACzB,OAAO,IAAI,CAAC,YAAY,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC,CAAA;IAChD,CAAC;IAED,wEAAwE;IACxE,WAAW,CAAC,IAAqB;QAC/B,IAAI,IAAI,CAAC,MAAM,KAAK,WAAW,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC,YAAY,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAA;QACtD,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,CAAC,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAA;IACtD,CAAC;IAED,mFAAmF;IACnF,SAAS,CAAC,IAAsB;QAC9B,OAAO,IAAI,CAAC,YAAY,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAA;IACpD,CAAC;CACF;AAED,qEAAqE;AACrE,MAAM,UAAU,IAAI,CAAC,MAAc;IACjC,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAA;AACpC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import type { Corpus } from '../corpus.js';
3
+ import { type MdLink } from '../model/links.js';
4
+ import { type LinkResolveOptions } from '../conditions/resolve.js';
5
+ export type { LinkResolveOptions } from '../conditions/resolve.js';
6
+ /** A link element: a link occurrence plus its containing document. */
7
+ export type { MdLink } from '../model/links.js';
8
+ /**
9
+ * Rule builder over the corpus's markdown links.
10
+ */
11
+ export declare class LinkRuleBuilder extends RuleBuilder<MdLink, Corpus> {
12
+ protected getElements(): MdLink[];
13
+ /** Filter to links that point within the repo (no scheme). */
14
+ areInternal(): this;
15
+ /** Filter to links with an external scheme (http, mailto, protocol-relative). */
16
+ areExternal(): this;
17
+ /**
18
+ * Condition: internal links resolve to an existing repo file. Static-site
19
+ * corpora with extensionless links pass `tryExtensions`/`tryIndex`, e.g.
20
+ * `resolve({ tryExtensions: ['.md'], tryIndex: 'index.md' })`.
21
+ */
22
+ resolve(options?: LinkResolveOptions): this;
23
+ }
24
+ /** Entry point: build rules over the corpus's markdown links. */
25
+ export declare function links(corpus: Corpus): LinkRuleBuilder;
26
+ //# sourceMappingURL=links.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.d.ts","sourceRoot":"","sources":["../../src/builders/links.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,kBAAkB,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAgB,KAAK,MAAM,EAAkB,MAAM,mBAAmB,CAAA;AAC7E,OAAO,EAAgB,KAAK,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAEhF,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAElE,sEAAsE;AACtE,YAAY,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAA;AAW/C;;GAEG;AACH,qBAAa,eAAgB,SAAQ,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;IAC9D,SAAS,CAAC,WAAW,IAAI,MAAM,EAAE;IAQjC,8DAA8D;IAC9D,WAAW,IAAI,IAAI;IAInB,iFAAiF;IACjF,WAAW,IAAI,IAAI;IAInB;;;;OAIG;IACH,OAAO,CAAC,OAAO,CAAC,EAAE,kBAAkB,GAAG,IAAI;CAG5C;AAED,iEAAiE;AACjE,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,GAAG,eAAe,CAErD"}
@@ -0,0 +1,42 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import { collectLinks } from '../model/links.js';
3
+ import { linkResolves } from '../conditions/resolve.js';
4
+ const areInternal = () => ({
5
+ description: 'are internal',
6
+ test: (l) => !l.external,
7
+ });
8
+ const areExternal = () => ({
9
+ description: 'are external',
10
+ test: (l) => l.external,
11
+ });
12
+ /**
13
+ * Rule builder over the corpus's markdown links.
14
+ */
15
+ export class LinkRuleBuilder extends RuleBuilder {
16
+ getElements() {
17
+ return this.project
18
+ .documents()
19
+ .flatMap((doc) => collectLinks(doc.root, doc.text).map((link) => ({ ...link, doc })));
20
+ }
21
+ /** Filter to links that point within the repo (no scheme). */
22
+ areInternal() {
23
+ return this.addPredicate(areInternal());
24
+ }
25
+ /** Filter to links with an external scheme (http, mailto, protocol-relative). */
26
+ areExternal() {
27
+ return this.addPredicate(areExternal());
28
+ }
29
+ /**
30
+ * Condition: internal links resolve to an existing repo file. Static-site
31
+ * corpora with extensionless links pass `tryExtensions`/`tryIndex`, e.g.
32
+ * `resolve({ tryExtensions: ['.md'], tryIndex: 'index.md' })`.
33
+ */
34
+ resolve(options) {
35
+ return this.addCondition(linkResolves(this.project, options));
36
+ }
37
+ }
38
+ /** Entry point: build rules over the corpus's markdown links. */
39
+ export function links(corpus) {
40
+ return new LinkRuleBuilder(corpus);
41
+ }
42
+ //# sourceMappingURL=links.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"links.js","sourceRoot":"","sources":["../../src/builders/links.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,kBAAkB,CAAA;AAE9D,OAAO,EAAE,YAAY,EAA+B,MAAM,mBAAmB,CAAA;AAC7E,OAAO,EAAE,YAAY,EAA2B,MAAM,0BAA0B,CAAA;AAOhF,MAAM,WAAW,GAAG,GAAsB,EAAE,CAAC,CAAC;IAC5C,WAAW,EAAE,cAAc;IAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ;CACzB,CAAC,CAAA;AACF,MAAM,WAAW,GAAG,GAAsB,EAAE,CAAC,CAAC;IAC5C,WAAW,EAAE,cAAc;IAC3B,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ;CACxB,CAAC,CAAA;AAEF;;GAEG;AACH,MAAM,OAAO,eAAgB,SAAQ,WAA2B;IACpD,WAAW;QACnB,OAAO,IAAI,CAAC,OAAO;aAChB,SAAS,EAAE;aACX,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CACf,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,IAAe,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,CAAC,CAAC,CAC9E,CAAA;IACL,CAAC;IAED,8DAA8D;IAC9D,WAAW;QACT,OAAO,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAA;IACzC,CAAC;IAED,iFAAiF;IACjF,WAAW;QACT,OAAO,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAA;IACzC,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAC,OAA4B;QAClC,OAAO,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;IAC/D,CAAC;CACF;AAED,iEAAiE;AACjE,MAAM,UAAU,KAAK,CAAC,MAAc;IAClC,OAAO,IAAI,eAAe,CAAC,MAAM,CAAC,CAAA;AACpC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import type { Corpus } from '../corpus.js';
3
+ import { type MdPointer } from '../model/pointers.js';
4
+ import { type PointerResolveOptions } from '../conditions/pointer-resolve.js';
5
+ /** A code-pointer element: a pointer occurrence plus its containing document. */
6
+ export type { MdPointer } from '../model/pointers.js';
7
+ /**
8
+ * Rule builder over the corpus's `path:line` code pointers.
9
+ */
10
+ export declare class PointerRuleBuilder extends RuleBuilder<MdPointer, Corpus> {
11
+ protected getElements(): MdPointer[];
12
+ /** Filter to pointers in live documents (not in a frozen folder). */
13
+ areLive(): this;
14
+ /** Filter to pointers in frozen documents (historical records). */
15
+ areFrozen(): this;
16
+ /**
17
+ * Condition: pointers resolve to a real file with the line in range. By
18
+ * default a pointer resolves by unique path-suffix (`admin/index.vue` matches
19
+ * the one file ending in it); pass `{ paths: 'exact' }` to require full
20
+ * repo-relative paths.
21
+ */
22
+ resolve(options?: PointerResolveOptions): this;
23
+ }
24
+ /** Entry point: build rules over the corpus's code pointers. */
25
+ export declare function pointers(corpus: Corpus): PointerRuleBuilder;
26
+ //# sourceMappingURL=pointers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pointers.d.ts","sourceRoot":"","sources":["../../src/builders/pointers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,kBAAkB,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAmB,KAAK,SAAS,EAAqB,MAAM,sBAAsB,CAAA;AACzF,OAAO,EAAmB,KAAK,qBAAqB,EAAE,MAAM,kCAAkC,CAAA;AAE9F,iFAAiF;AACjF,YAAY,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAWrD;;GAEG;AACH,qBAAa,kBAAmB,SAAQ,WAAW,CAAC,SAAS,EAAE,MAAM,CAAC;IACpE,SAAS,CAAC,WAAW,IAAI,SAAS,EAAE;IAQpC,qEAAqE;IACrE,OAAO,IAAI,IAAI;IAIf,mEAAmE;IACnE,SAAS,IAAI,IAAI;IAIjB;;;;;OAKG;IACH,OAAO,CAAC,OAAO,CAAC,EAAE,qBAAqB,GAAG,IAAI;CAG/C;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,CAE3D"}
@@ -0,0 +1,43 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import { extractPointers } from '../model/pointers.js';
3
+ import { pointerResolves } from '../conditions/pointer-resolve.js';
4
+ const areLive = () => ({
5
+ description: 'are live (not in a frozen folder)',
6
+ test: (p) => !p.doc.frozen,
7
+ });
8
+ const areFrozen = () => ({
9
+ description: 'are frozen (historical record)',
10
+ test: (p) => p.doc.frozen,
11
+ });
12
+ /**
13
+ * Rule builder over the corpus's `path:line` code pointers.
14
+ */
15
+ export class PointerRuleBuilder extends RuleBuilder {
16
+ getElements() {
17
+ return this.project
18
+ .documents()
19
+ .flatMap((doc) => extractPointers(doc.text, doc.root).map((p) => ({ ...p, doc })));
20
+ }
21
+ /** Filter to pointers in live documents (not in a frozen folder). */
22
+ areLive() {
23
+ return this.addPredicate(areLive());
24
+ }
25
+ /** Filter to pointers in frozen documents (historical records). */
26
+ areFrozen() {
27
+ return this.addPredicate(areFrozen());
28
+ }
29
+ /**
30
+ * Condition: pointers resolve to a real file with the line in range. By
31
+ * default a pointer resolves by unique path-suffix (`admin/index.vue` matches
32
+ * the one file ending in it); pass `{ paths: 'exact' }` to require full
33
+ * repo-relative paths.
34
+ */
35
+ resolve(options) {
36
+ return this.addCondition(pointerResolves(this.project, options));
37
+ }
38
+ }
39
+ /** Entry point: build rules over the corpus's code pointers. */
40
+ export function pointers(corpus) {
41
+ return new PointerRuleBuilder(corpus);
42
+ }
43
+ //# sourceMappingURL=pointers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pointers.js","sourceRoot":"","sources":["../../src/builders/pointers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,kBAAkB,CAAA;AAE9D,OAAO,EAAE,eAAe,EAAqC,MAAM,sBAAsB,CAAA;AACzF,OAAO,EAAE,eAAe,EAA8B,MAAM,kCAAkC,CAAA;AAK9F,MAAM,OAAO,GAAG,GAAyB,EAAE,CAAC,CAAC;IAC3C,WAAW,EAAE,mCAAmC;IAChD,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM;CAC3B,CAAC,CAAA;AACF,MAAM,SAAS,GAAG,GAAyB,EAAE,CAAC,CAAC;IAC7C,WAAW,EAAE,gCAAgC;IAC7C,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM;CAC1B,CAAC,CAAA;AAEF;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,WAA8B;IAC1D,WAAW;QACnB,OAAO,IAAI,CAAC,OAAO;aAChB,SAAS,EAAE;aACX,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CACf,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAe,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAC9E,CAAA;IACL,CAAC;IAED,qEAAqE;IACrE,OAAO;QACL,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAA;IACrC,CAAC;IAED,mEAAmE;IACnE,SAAS;QACP,OAAO,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,CAAC,CAAA;IACvC,CAAC;IAED;;;;;OAKG;IACH,OAAO,CAAC,OAA+B;QACrC,OAAO,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAA;IAClE,CAAC;CACF;AAED,gEAAgE;AAChE,MAAM,UAAU,QAAQ,CAAC,MAAc;IACrC,OAAO,IAAI,kBAAkB,CAAC,MAAM,CAAC,CAAA;AACvC,CAAC"}
@@ -0,0 +1,36 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import type { Corpus } from '../corpus.js';
3
+ import { type MdRow, type RowMatchOptions } from '../model/rows.js';
4
+ /**
5
+ * Rule builder over table rows drawn from the corpus.
6
+ *
7
+ * A row is a first-class element (`MdRow`): its located-column accessor
8
+ * (`get(role)`), its real source line, and back references to its document and
9
+ * table. Rows are drawn from **every** table (across all documents) that sits
10
+ * under the requested section and has all the requested columns.
11
+ *
12
+ * The primary use is `.select()` — turning spec-table rows into one side of a
13
+ * kernel `correspondence()` so a table can be bound to code and fail on drift.
14
+ * The standard `.that().satisfy(…)` / `.should().satisfy(…)` surface is
15
+ * inherited, so rows are also directly rule-able.
16
+ */
17
+ export declare class RowsRuleBuilder extends RuleBuilder<MdRow, Corpus> {
18
+ private readonly opts;
19
+ constructor(corpus: Corpus, opts: RowMatchOptions);
20
+ protected getElements(): MdRow[];
21
+ }
22
+ /**
23
+ * Entry point: build rules over table rows in the corpus.
24
+ *
25
+ * ```ts
26
+ * const packageRows = rows(c, {
27
+ * section: /^Packages$/,
28
+ * columns: { pkg: /^Package$/, status: /^Status$/ },
29
+ * }).select({
30
+ * label: 'README package row',
31
+ * identify: (r) => ({ name: r.get('pkg'), file: r.doc.relPath, line: r.line }),
32
+ * })
33
+ * ```
34
+ */
35
+ export declare function rows(corpus: Corpus, opts: RowMatchOptions): RowsRuleBuilder;
36
+ //# sourceMappingURL=rows.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rows.d.ts","sourceRoot":"","sources":["../../src/builders/rows.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAC9C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,EAAkB,KAAK,KAAK,EAAE,KAAK,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAEnF;;;;;;;;;;;;GAYG;AACH,qBAAa,eAAgB,SAAQ,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC;IAG3D,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADrB,MAAM,EAAE,MAAM,EACG,IAAI,EAAE,eAAe;IAKxC,SAAS,CAAC,WAAW,IAAI,KAAK,EAAE;CAMjC;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,eAAe,CAE3E"}
@@ -0,0 +1,42 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import { matchTableRows } from '../model/rows.js';
3
+ /**
4
+ * Rule builder over table rows drawn from the corpus.
5
+ *
6
+ * A row is a first-class element (`MdRow`): its located-column accessor
7
+ * (`get(role)`), its real source line, and back references to its document and
8
+ * table. Rows are drawn from **every** table (across all documents) that sits
9
+ * under the requested section and has all the requested columns.
10
+ *
11
+ * The primary use is `.select()` — turning spec-table rows into one side of a
12
+ * kernel `correspondence()` so a table can be bound to code and fail on drift.
13
+ * The standard `.that().satisfy(…)` / `.should().satisfy(…)` surface is
14
+ * inherited, so rows are also directly rule-able.
15
+ */
16
+ export class RowsRuleBuilder extends RuleBuilder {
17
+ opts;
18
+ constructor(corpus, opts) {
19
+ super(corpus);
20
+ this.opts = opts;
21
+ }
22
+ getElements() {
23
+ return this.project.documents().flatMap((doc) => matchTableRows(doc, this.opts));
24
+ }
25
+ }
26
+ /**
27
+ * Entry point: build rules over table rows in the corpus.
28
+ *
29
+ * ```ts
30
+ * const packageRows = rows(c, {
31
+ * section: /^Packages$/,
32
+ * columns: { pkg: /^Package$/, status: /^Status$/ },
33
+ * }).select({
34
+ * label: 'README package row',
35
+ * identify: (r) => ({ name: r.get('pkg'), file: r.doc.relPath, line: r.line }),
36
+ * })
37
+ * ```
38
+ */
39
+ export function rows(corpus, opts) {
40
+ return new RowsRuleBuilder(corpus, opts);
41
+ }
42
+ //# sourceMappingURL=rows.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rows.js","sourceRoot":"","sources":["../../src/builders/rows.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAA;AAE9C,OAAO,EAAE,cAAc,EAAoC,MAAM,kBAAkB,CAAA;AAEnF;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,eAAgB,SAAQ,WAA0B;IAG1C;IAFnB,YACE,MAAc,EACG,IAAqB;QAEtC,KAAK,CAAC,MAAM,CAAC,CAAA;QAFI,SAAI,GAAJ,IAAI,CAAiB;IAGxC,CAAC;IAES,WAAW;QACnB,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAA;IAClF,CAAC;CAIF;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,IAAI,CAAC,MAAc,EAAE,IAAqB;IACxD,OAAO,IAAI,eAAe,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;AAC1C,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import type { Corpus } from '../corpus.js';
3
+ import type { MdDocument } from '../model/document.js';
4
+ import { type MdTaskItemRef } from '../model/task-items.js';
5
+ /** A task-item element: a task-list item plus its containing document. */
6
+ export interface MdTaskItem extends MdTaskItemRef {
7
+ readonly doc: MdDocument;
8
+ }
9
+ /**
10
+ * Rule builder over the corpus's GFM task-list items (`- [ ]` / `- [x]`).
11
+ */
12
+ export declare class TaskItemRuleBuilder extends RuleBuilder<MdTaskItem, Corpus> {
13
+ protected getElements(): MdTaskItem[];
14
+ /** Filter to open boxes (`- [ ]`). */
15
+ areOpen(): this;
16
+ /** Filter to checked boxes (`- [x]`). */
17
+ areChecked(): this;
18
+ }
19
+ /** Entry point: build rules over the corpus's task-list items. */
20
+ export declare function taskItems(corpus: Corpus): TaskItemRuleBuilder;
21
+ //# sourceMappingURL=task-items.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-items.d.ts","sourceRoot":"","sources":["../../src/builders/task-items.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,kBAAkB,CAAA;AAC9D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AACtD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAE7E,0EAA0E;AAC1E,MAAM,WAAW,UAAW,SAAQ,aAAa;IAC/C,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;CACzB;AAWD;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,WAAW,CAAC,UAAU,EAAE,MAAM,CAAC;IACtE,SAAS,CAAC,WAAW,IAAI,UAAU,EAAE;IAMrC,sCAAsC;IACtC,OAAO,IAAI,IAAI;IAIf,yCAAyC;IACzC,UAAU,IAAI,IAAI;CAGnB;AAED,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,CAE7D"}
@@ -0,0 +1,33 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import { collectTaskItems } from '../model/task-items.js';
3
+ const areOpen = () => ({
4
+ description: 'are open',
5
+ test: (t) => !t.checked,
6
+ });
7
+ const areChecked = () => ({
8
+ description: 'are checked',
9
+ test: (t) => t.checked,
10
+ });
11
+ /**
12
+ * Rule builder over the corpus's GFM task-list items (`- [ ]` / `- [x]`).
13
+ */
14
+ export class TaskItemRuleBuilder extends RuleBuilder {
15
+ getElements() {
16
+ return this.project
17
+ .documents()
18
+ .flatMap((doc) => collectTaskItems(doc.root).map((t) => ({ ...t, doc })));
19
+ }
20
+ /** Filter to open boxes (`- [ ]`). */
21
+ areOpen() {
22
+ return this.addPredicate(areOpen());
23
+ }
24
+ /** Filter to checked boxes (`- [x]`). */
25
+ areChecked() {
26
+ return this.addPredicate(areChecked());
27
+ }
28
+ }
29
+ /** Entry point: build rules over the corpus's task-list items. */
30
+ export function taskItems(corpus) {
31
+ return new TaskItemRuleBuilder(corpus);
32
+ }
33
+ //# sourceMappingURL=task-items.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-items.js","sourceRoot":"","sources":["../../src/builders/task-items.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAkB,MAAM,kBAAkB,CAAA;AAG9D,OAAO,EAAE,gBAAgB,EAAsB,MAAM,wBAAwB,CAAA;AAO7E,MAAM,OAAO,GAAG,GAA0B,EAAE,CAAC,CAAC;IAC5C,WAAW,EAAE,UAAU;IACvB,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO;CACxB,CAAC,CAAA;AACF,MAAM,UAAU,GAAG,GAA0B,EAAE,CAAC,CAAC;IAC/C,WAAW,EAAE,aAAa;IAC1B,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO;CACvB,CAAC,CAAA;AAEF;;GAEG;AACH,MAAM,OAAO,mBAAoB,SAAQ,WAA+B;IAC5D,WAAW;QACnB,OAAO,IAAI,CAAC,OAAO;aAChB,SAAS,EAAE;aACX,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAA;IAC7E,CAAC;IAED,sCAAsC;IACtC,OAAO;QACL,OAAO,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC,CAAA;IACrC,CAAC;IAED,yCAAyC;IACzC,UAAU;QACR,OAAO,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,CAAC,CAAA;IACxC,CAAC;CACF;AAED,kEAAkE;AAClE,MAAM,UAAU,SAAS,CAAC,MAAc;IACtC,OAAO,IAAI,mBAAmB,CAAC,MAAM,CAAC,CAAA;AACxC,CAAC"}
@@ -0,0 +1,66 @@
1
+ import { RuleBuilder } from '@nielspeter/eess';
2
+ import type { Corpus } from '../corpus.js';
3
+ import type { MdDocument } from '../model/document.js';
4
+ /**
5
+ * Controlled-vocabulary primitive (plan 0069 Phase 4): derive a term set from
6
+ * the corpus, then assert that named references in prose resolve against it.
7
+ *
8
+ * Both halves are consumer parameters — where the terms come from
9
+ * (`fromFolders` / `fromHeadings` / explicit `terms`) and what a reference
10
+ * looks like (the `label` pattern). Nothing domain-shaped lives here.
11
+ */
12
+ export interface VocabularyOptions {
13
+ /**
14
+ * Glob over directory paths (repo-relative, POSIX); each matched
15
+ * directory's basename becomes a term — e.g. folders-as-canonical-names.
16
+ */
17
+ readonly fromFolders?: string;
18
+ /** Glob over documents; every heading whose depth matches becomes a term. */
19
+ readonly fromHeadings?: {
20
+ readonly files: string;
21
+ readonly depth?: number;
22
+ };
23
+ /** Explicit terms, unioned with the derived ones. */
24
+ readonly terms?: readonly string[];
25
+ /** Normalization applied to terms AND references before comparing. Default: trim. */
26
+ readonly normalize?: (value: string) => string;
27
+ }
28
+ export interface Vocabulary {
29
+ /** The normalized term set. */
30
+ readonly terms: ReadonlySet<string>;
31
+ /** The normalization in force (shared with `resolveAgainst`). */
32
+ readonly normalize: (value: string) => string;
33
+ }
34
+ /** Derive a controlled vocabulary from the corpus. */
35
+ export declare function vocabulary(corpus: Corpus, options: VocabularyOptions): Vocabulary;
36
+ /** A labeled named reference found in prose — e.g. `Bounded Context: Billing`. */
37
+ export interface MdTerm {
38
+ /** The referenced value (markdown emphasis stripped, trimmed). */
39
+ readonly value: string;
40
+ /** The line's text after the label match, before cleanup. */
41
+ readonly raw: string;
42
+ readonly doc: MdDocument;
43
+ readonly line: number;
44
+ }
45
+ export interface TermsOptions {
46
+ /** Pattern locating a labeled reference on a line — e.g. `/Bounded Context:/`. */
47
+ readonly label: RegExp;
48
+ /**
49
+ * Extract the referenced value from the text following the label match.
50
+ * Default: strip markdown emphasis characters and trim.
51
+ */
52
+ readonly value?: (rest: string) => string;
53
+ }
54
+ /** Rule builder over labeled named references in the corpus's prose. */
55
+ export declare class TermRuleBuilder extends RuleBuilder<MdTerm, Corpus> {
56
+ private readonly opts;
57
+ constructor(corpus: Corpus, opts: TermsOptions);
58
+ protected getElements(): MdTerm[];
59
+ /** Filter to references in documents whose path matches the glob. */
60
+ resideInFile(glob: string): this;
61
+ /** Assert every reference's value is a term of the vocabulary. */
62
+ resolveAgainst(vocab: Vocabulary): this;
63
+ }
64
+ /** Entry point: build rules over labeled named references (`Label: Value` lines). */
65
+ export declare function terms(corpus: Corpus, options: TermsOptions): TermRuleBuilder;
66
+ //# sourceMappingURL=vocabulary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vocabulary.d.ts","sourceRoot":"","sources":["../../src/builders/vocabulary.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAsD,MAAM,kBAAkB,CAAA;AAClG,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAA;AAEtD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,6EAA6E;IAC7E,QAAQ,CAAC,YAAY,CAAC,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC3E,qDAAqD;IACrD,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAClC,qFAAqF;IACrF,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;CAC/C;AAED,MAAM,WAAW,UAAU;IACzB,+BAA+B;IAC/B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;IACnC,iEAAiE;IACjE,QAAQ,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAA;CAC9C;AAID,sDAAsD;AACtD,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAoCjF;AAED,kFAAkF;AAClF,MAAM,WAAW,MAAM;IACrB,kEAAkE;IAClE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,6DAA6D;IAC7D,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB;;;OAGG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAC1C;AA8DD,wEAAwE;AACxE,qBAAa,eAAgB,SAAQ,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC;IAG5D,OAAO,CAAC,QAAQ,CAAC,IAAI;gBADrB,MAAM,EAAE,MAAM,EACG,IAAI,EAAE,YAAY;IAKrC,SAAS,CAAC,WAAW,IAAI,MAAM,EAAE;IAIjC,qEAAqE;IACrE,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIhC,kEAAkE;IAClE,cAAc,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAGxC;AAED,qFAAqF;AACrF,wBAAgB,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,GAAG,eAAe,CAE5E"}