@timiagama/ecr 0.0.1 → 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 (138) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +16 -0
  3. package/README.md +411 -7
  4. package/dist/bin.d.ts +20 -0
  5. package/dist/bin.d.ts.map +1 -0
  6. package/dist/bin.js +26 -0
  7. package/dist/bin.js.map +1 -0
  8. package/dist/cli/corpus-loader.d.ts +198 -0
  9. package/dist/cli/corpus-loader.d.ts.map +1 -0
  10. package/dist/cli/corpus-loader.js +351 -0
  11. package/dist/cli/corpus-loader.js.map +1 -0
  12. package/dist/cli/corpus-statistics.d.ts +113 -0
  13. package/dist/cli/corpus-statistics.d.ts.map +1 -0
  14. package/dist/cli/corpus-statistics.js +119 -0
  15. package/dist/cli/corpus-statistics.js.map +1 -0
  16. package/dist/cli/diagnostic-reporter.d.ts +103 -0
  17. package/dist/cli/diagnostic-reporter.d.ts.map +1 -0
  18. package/dist/cli/diagnostic-reporter.js +254 -0
  19. package/dist/cli/diagnostic-reporter.js.map +1 -0
  20. package/dist/cli/project-ignore.d.ts +113 -0
  21. package/dist/cli/project-ignore.d.ts.map +1 -0
  22. package/dist/cli/project-ignore.js +199 -0
  23. package/dist/cli/project-ignore.js.map +1 -0
  24. package/dist/cli/safe-text.d.ts +25 -0
  25. package/dist/cli/safe-text.d.ts.map +1 -0
  26. package/dist/cli/safe-text.js +34 -0
  27. package/dist/cli/safe-text.js.map +1 -0
  28. package/dist/cli/supervisor.d.ts +108 -0
  29. package/dist/cli/supervisor.d.ts.map +1 -0
  30. package/dist/cli/supervisor.js +139 -0
  31. package/dist/cli/supervisor.js.map +1 -0
  32. package/dist/cli.d.ts +247 -0
  33. package/dist/cli.d.ts.map +1 -0
  34. package/dist/cli.js +591 -0
  35. package/dist/cli.js.map +1 -0
  36. package/dist/corpus-validator.d.ts +183 -0
  37. package/dist/corpus-validator.d.ts.map +1 -0
  38. package/dist/corpus-validator.js +479 -0
  39. package/dist/corpus-validator.js.map +1 -0
  40. package/dist/document-identity-rule.d.ts +198 -0
  41. package/dist/document-identity-rule.d.ts.map +1 -0
  42. package/dist/document-identity-rule.js +244 -0
  43. package/dist/document-identity-rule.js.map +1 -0
  44. package/dist/ecr.d.ts +103 -0
  45. package/dist/ecr.d.ts.map +1 -0
  46. package/dist/ecr.js +216 -0
  47. package/dist/ecr.js.map +1 -0
  48. package/dist/glob-pattern.d.ts +94 -0
  49. package/dist/glob-pattern.d.ts.map +1 -0
  50. package/dist/glob-pattern.js +188 -0
  51. package/dist/glob-pattern.js.map +1 -0
  52. package/dist/heading-source-form.d.ts +77 -0
  53. package/dist/heading-source-form.d.ts.map +1 -0
  54. package/dist/heading-source-form.js +129 -0
  55. package/dist/heading-source-form.js.map +1 -0
  56. package/dist/identifier-grammar.d.ts +212 -0
  57. package/dist/identifier-grammar.d.ts.map +1 -0
  58. package/dist/identifier-grammar.js +273 -0
  59. package/dist/identifier-grammar.js.map +1 -0
  60. package/dist/index.d.ts +19 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +11 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/inline-reference-rule.d.ts +557 -0
  65. package/dist/inline-reference-rule.d.ts.map +1 -0
  66. package/dist/inline-reference-rule.js +802 -0
  67. package/dist/inline-reference-rule.js.map +1 -0
  68. package/dist/meta-documents.d.ts +91 -0
  69. package/dist/meta-documents.d.ts.map +1 -0
  70. package/dist/meta-documents.js +133 -0
  71. package/dist/meta-documents.js.map +1 -0
  72. package/dist/per-document-visitor.d.ts +510 -0
  73. package/dist/per-document-visitor.d.ts.map +1 -0
  74. package/dist/per-document-visitor.js +1032 -0
  75. package/dist/per-document-visitor.js.map +1 -0
  76. package/dist/references-section-rule.d.ts +482 -0
  77. package/dist/references-section-rule.d.ts.map +1 -0
  78. package/dist/references-section-rule.js +777 -0
  79. package/dist/references-section-rule.js.map +1 -0
  80. package/dist/section-hierarchy-rule.d.ts +299 -0
  81. package/dist/section-hierarchy-rule.d.ts.map +1 -0
  82. package/dist/section-hierarchy-rule.js +482 -0
  83. package/dist/section-hierarchy-rule.js.map +1 -0
  84. package/dist/source-alignment.d.ts +41 -0
  85. package/dist/source-alignment.d.ts.map +1 -0
  86. package/dist/source-alignment.js +166 -0
  87. package/dist/source-alignment.js.map +1 -0
  88. package/dist/source-lines.d.ts +69 -0
  89. package/dist/source-lines.d.ts.map +1 -0
  90. package/dist/source-lines.js +104 -0
  91. package/dist/source-lines.js.map +1 -0
  92. package/dist/spec-version.d.ts +19 -0
  93. package/dist/spec-version.d.ts.map +1 -0
  94. package/dist/spec-version.js +19 -0
  95. package/dist/spec-version.js.map +1 -0
  96. package/dist/types.d.ts +219 -0
  97. package/dist/types.d.ts.map +1 -0
  98. package/dist/types.js +11 -0
  99. package/dist/types.js.map +1 -0
  100. package/examples/docs/0. Orientation/0.0 - System Overview.md +34 -0
  101. package/examples/docs/0. Orientation/0.0.1 - Glossary.md +21 -0
  102. package/examples/docs/0. Orientation/0.0.2 - Quality Gates.md +22 -0
  103. package/examples/docs/3. Ingestion/3.1 - Ingestion - Validation Rules.md +33 -0
  104. package/examples/docs/4. Payments/4.10 - Settlement.md +22 -0
  105. package/examples/docs/4. Payments/4.2 - Payment Processing Contract.md +33 -0
  106. package/examples/docs/8. Orchestration/8.1 - Workflow Orchestration Contract.md +38 -0
  107. package/examples/docs/8. Orchestration/8.1.3 - Retry Policy.md +17 -0
  108. package/examples/docs/8. Orchestration/8.10 - Dead Letter Queue.md +20 -0
  109. package/examples/docs/README.md +7 -0
  110. package/package.json +65 -7
  111. package/protocol/navigation-protocol.md +223 -0
  112. package/spec/v2/1 - ECR - Structural Specification.md +1467 -0
  113. package/spec/v2/2 - ECR - User Guide.md +845 -0
  114. package/spec/v2/3 - Design Rationale - The Section Separator.md +243 -0
  115. package/src/bin.ts +30 -0
  116. package/src/cli/corpus-loader.ts +482 -0
  117. package/src/cli/corpus-statistics.ts +196 -0
  118. package/src/cli/diagnostic-reporter.ts +344 -0
  119. package/src/cli/project-ignore.ts +238 -0
  120. package/src/cli/safe-text.ts +39 -0
  121. package/src/cli/supervisor.ts +204 -0
  122. package/src/cli.ts +836 -0
  123. package/src/corpus-validator.ts +642 -0
  124. package/src/document-identity-rule.ts +422 -0
  125. package/src/ecr.ts +298 -0
  126. package/src/glob-pattern.ts +242 -0
  127. package/src/heading-source-form.ts +173 -0
  128. package/src/identifier-grammar.ts +380 -0
  129. package/src/index.ts +78 -0
  130. package/src/inline-reference-rule.ts +1235 -0
  131. package/src/meta-documents.ts +159 -0
  132. package/src/per-document-visitor.ts +1528 -0
  133. package/src/references-section-rule.ts +1182 -0
  134. package/src/section-hierarchy-rule.ts +705 -0
  135. package/src/source-alignment.ts +209 -0
  136. package/src/source-lines.ts +123 -0
  137. package/src/spec-version.ts +19 -0
  138. package/src/types.ts +267 -0
@@ -0,0 +1,510 @@
1
+ /**
2
+ * Per-Document Visitor
3
+ *
4
+ * Orchestrates all four ECR rules (ECR101--ECR104) over a single Markdown
5
+ * document, producing a composite {@link LintResult}.
6
+ *
7
+ * This is the integration layer that:
8
+ * 1. Parses Markdown text into an AST using `unified` + `remark-parse`
9
+ * 2. Walks the AST and dispatches nodes to the appropriate rules
10
+ * 3. Manages the rule initialisation dependency chain:
11
+ * - ECR101 must complete before ECR102/ECR103 can start
12
+ * - ECR103 must complete before ECR104 can start (to provide declared DocIDs)
13
+ * - If ECR101 fails, downstream rules are skipped entirely
14
+ * 4. Filters text nodes: excludes those inside `code`, `inlineCode`, `html`,
15
+ * or link URL contexts before feeding to ECR104
16
+ * 5. Tracks the current section context (heading identifier) for ECR104
17
+ * 6. Feeds list items within the References section to ECR103
18
+ * 7. Finalises all rules and assembles the composite {@link LintResult}
19
+ *
20
+ * Spec references:
21
+ * - 1#8 -- Visitor and State Model
22
+ * - 1#11.1 -- Pass 1: Per-Document Parse, Validate, Extract
23
+ * - 1#9.3 -- Document Identity Rule [ECR101]
24
+ * - 1#9.4 -- Section Structure Rules [ECR102]
25
+ * - 1#9.5 -- Inline Reference Rules [ECR104]
26
+ * - 1#9.6 -- References Section Rules [ECR103]
27
+ * - 1#10.2 -- LintResult
28
+ * - 1#10.4 -- ExtractedDocument
29
+ */
30
+ import type { LintResult } from './types.js';
31
+ /**
32
+ * Configuration options for constructing a {@link PerDocumentVisitor} instance.
33
+ */
34
+ export interface PerDocumentVisitorOptions {
35
+ /**
36
+ * The opaque, host-provided URI identifying the document being validated.
37
+ *
38
+ * Passed through to all rules and echoed in the {@link LintResult.input} field.
39
+ */
40
+ readonly uri: string;
41
+ /**
42
+ * Optional version tag for the document instance.
43
+ *
44
+ * Passed through to the {@link LintResult.input} field. When omitted, the
45
+ * resulting {@link LintInput.version} will be `undefined`.
46
+ */
47
+ readonly version?: number;
48
+ }
49
+ /** Rule identifier reported when a document cannot be parsed at all. */
50
+ export declare const UNPARSABLE_DOCUMENT_RULE_ID: string;
51
+ /** The `data.cause` of that diagnostic. */
52
+ export declare const UNPARSABLE_DOCUMENT_CAUSE: string;
53
+ /**
54
+ * MDAST node types whose descendant text nodes must be excluded from
55
+ * ECR104 inline reference detection.
56
+ *
57
+ * Per 1#9.5, text nodes inside code blocks, inline code, and HTML
58
+ * elements are not valid candidates for inline reference detection.
59
+ */
60
+ export declare const EXCLUDED_ANCESTOR_NODE_TYPES: ReadonlySet<string>;
61
+ /**
62
+ * Orchestrates all four ECR rules (ECR101--ECR104) over a single Markdown
63
+ * document and produces a composite {@link LintResult}.
64
+ *
65
+ * The visitor is stateless between calls to {@link lint}. Each invocation
66
+ * produces a fresh result with no side effects on the visitor instance.
67
+ *
68
+ * The visitor creates its own {@link IdentifierGrammar} instance internally
69
+ * and manages the rule initialisation dependency chain:
70
+ * - ECR101 (Document Identity) runs first on all headings
71
+ * - If ECR101 produces a valid DocID, ECR102 (Section Hierarchy) and
72
+ * ECR103 (References Section) are initialised and fed their respective nodes
73
+ * - After ECR103 finalises, the set of declared DocIDs is extracted and
74
+ * used to initialise ECR104 (Inline Reference)
75
+ * - If ECR101 fails (no valid DocID), the result is returned early with
76
+ * `ok=false` and no `extracted` field
77
+ *
78
+ * Usage:
79
+ * ```ts
80
+ * const visitor = new PerDocumentVisitor({
81
+ * uri: 'file:///docs/3.1.md',
82
+ * version: 1,
83
+ * });
84
+ *
85
+ * const result: LintResult = visitor.lint('# 3.1 - My Document\n\n## References\n- 8.1 - ...');
86
+ * ```
87
+ *
88
+ * @example
89
+ * ```ts
90
+ * const visitor = new PerDocumentVisitor({ uri: 'file:///docs/5.1.md' });
91
+ * const result: LintResult = visitor.lint(markdownText);
92
+ *
93
+ * if (result.ok) {
94
+ * console.log('Document is valid:', result.extracted?.docId);
95
+ * } else {
96
+ * console.log('Diagnostics:', result.diagnostics);
97
+ * }
98
+ * ```
99
+ */
100
+ export declare class PerDocumentVisitor {
101
+ /**
102
+ * The opaque, host-provided URI identifying the document being validated.
103
+ */
104
+ private readonly uri;
105
+ /**
106
+ * Optional version tag for the document instance.
107
+ */
108
+ private readonly version;
109
+ /**
110
+ * The {@link IdentifierGrammar} instance shared across all rules
111
+ * within a single visitor. Created once during construction.
112
+ */
113
+ private readonly grammar;
114
+ /**
115
+ * Constructs a new Per-Document Visitor.
116
+ *
117
+ * Creates an internal {@link IdentifierGrammar} instance for use
118
+ * across all rule evaluations performed by this visitor.
119
+ *
120
+ * @param options - Configuration including the document URI and optional version
121
+ */
122
+ constructor(options: PerDocumentVisitorOptions);
123
+ /**
124
+ * Lints a single Markdown document and produces a composite {@link LintResult}.
125
+ *
126
+ * Each call is stateless: the visitor creates fresh rule instances,
127
+ * parses the Markdown text into an AST, walks the AST to feed nodes
128
+ * to the appropriate rules, finalises all rules, and assembles the result.
129
+ *
130
+ * The orchestration proceeds in phases:
131
+ * 1. Parse the Markdown text into an MDAST {@link MdastRoot} node
132
+ * 2. First pass: walk headings and list items for ECR101, ECR102, ECR103
133
+ * 3. If ECR101 fails (no valid DocID), return early with `ok=false`
134
+ * 4. Second pass: walk text nodes for ECR104 (with ancestor filtering
135
+ * and section context tracking)
136
+ * 5. Assemble the composite {@link LintResult} from all rule results
137
+ *
138
+ * @param markdownText - The raw Markdown text of the document to lint
139
+ * @returns The composite lint result including diagnostics and, when a valid
140
+ * DocID is recovered, extracted structural artefacts
141
+ */
142
+ lint(markdownText: string): LintResult;
143
+ /**
144
+ * Parses raw Markdown text into an MDAST {@link MdastRoot} node.
145
+ *
146
+ * Uses `unified` with `remark-parse` to produce a standards-compliant
147
+ * MDAST tree with positional metadata attached to all nodes.
148
+ *
149
+ * The `unified().use(remarkParse).parse()` call returns a type that
150
+ * cannot be directly assigned to our local {@link MdastRoot} interface
151
+ * because `mdast` types are not importable as a module specifier in this
152
+ * project. The cast via `unknown` is safe because `remark-parse` always
153
+ * produces an MDAST Root node at runtime.
154
+ *
155
+ * @param markdownText - The raw Markdown text to parse
156
+ * @returns The parsed MDAST root node
157
+ */
158
+ private parseMarkdown;
159
+ /**
160
+ * Reports a document the parser could not read.
161
+ *
162
+ * The parser walks a document's structure by recursion, inside a package
163
+ * this project does not control, so a deeply nested link label or image
164
+ * description can end the parse with a stack overflow. That must be one
165
+ * document's error, reported like any other, and never a crash that stops
166
+ * a corpus part way and leaves the rest unvalidated.
167
+ *
168
+ * @param error - What the parser threw
169
+ * @returns A failing result carrying one error diagnostic
170
+ */
171
+ private reportUnparsable;
172
+ /**
173
+ * Executes the first AST traversal pass: headings, sections, and references.
174
+ *
175
+ * This pass walks the AST in document order and:
176
+ * - Feeds every heading node to ECR101 (`DocumentIdentityRule.evaluateHeading`)
177
+ * - Finalises ECR101 to determine whether a valid DocID was recovered
178
+ * - If a valid DocID is recovered:
179
+ * - Registers the root H1 heading with ECR102
180
+ * (`SectionHierarchyRule.registerRootHeading`)
181
+ * - Feeds headings with depth \>= 2 to ECR102
182
+ * (`SectionHierarchyRule.evaluateHeading`)
183
+ * - Feeds all headings to ECR103
184
+ * (`ReferencesSectionRule.evaluateHeading`)
185
+ * - Feeds list items within the References section to ECR103
186
+ * (`ReferencesSectionRule.evaluateListItem`)
187
+ * - Finalises ECR102 and ECR103
188
+ * - If no valid DocID is recovered, ECR102 and ECR103 are skipped
189
+ *
190
+ * The pass is structured so that all heading nodes are collected first
191
+ * for ECR101, then the collected headings are replayed for ECR102 and ECR103.
192
+ *
193
+ * @param root - The parsed MDAST root node
194
+ * @param sourceText - The document's raw Markdown, for the heading source-form check of 1#9.11
195
+ * @returns The intermediate result from the headings and references pass
196
+ */
197
+ private executeHeadingsAndReferencesPass;
198
+ /**
199
+ * Collects all heading nodes from the AST into the provided array.
200
+ *
201
+ * Walks the AST depth-first in document order, extracting
202
+ * {@link HeadingNodeData} from every node with `type === 'heading'`.
203
+ *
204
+ * @param node - The root of the walk
205
+ * @param headings - The accumulator array for collected heading data
206
+ */
207
+ private collectHeadings;
208
+ /**
209
+ * Reads a node's plain text: what a reader sees, with the formatting gone.
210
+ *
211
+ * @param node - The node to read
212
+ * @returns Its text, and its descendants' text, in order
213
+ */
214
+ private static showNodeText;
215
+ /**
216
+ * Puts a node's children on a walk's stack so that they come off it in
217
+ * document order.
218
+ *
219
+ * Every walk over a document's nodes keeps its own stack rather than
220
+ * calling itself, because nesting in Markdown costs the author almost
221
+ * nothing: a few thousand nested blockquotes, or nested bold spans, fit in
222
+ * a few kilobytes, and recursion over them exhausts the call stack and
223
+ * takes the process with it. A stack on the heap has no such limit.
224
+ *
225
+ * @param node - The node whose children are to be walked
226
+ * @param pending - The stack to push onto, from which nodes are taken with `pop`
227
+ */
228
+ private static pushChildren;
229
+ /**
230
+ * Finds the References section and feeds its placement and entries to ECR103.
231
+ *
232
+ * The `## References` heading is looked for anywhere in the tree, not only
233
+ * among the root's children. A section nested in a blockquote or a list
234
+ * item used to be passed over, so its entries were silently discarded and
235
+ * the document reported as having an empty References section. It is now
236
+ * found, reported as misplaced (1#9.11 rule 3), and its entries still read.
237
+ *
238
+ * The entries are the items of the list that immediately follows the
239
+ * heading, or of a list that opens the container immediately following it
240
+ * -- which is also a placement violation.
241
+ *
242
+ * @param root - The parsed MDAST root node
243
+ * @param referencesSectionRule - The ECR103 rule instance to feed
244
+ */
245
+ private feedListItemsToReferencesRule;
246
+ /**
247
+ * Finds the list holding the References entries, given the node after the
248
+ * heading: that node itself, or a list opening the container it is, at any
249
+ * depth of nesting.
250
+ *
251
+ * Looking one container deep lost `> > - 8.1 - …`: the list was never
252
+ * found, so its entries were discarded and the section reported as empty.
253
+ *
254
+ * @param next - The node immediately following the `## References` heading
255
+ * @returns The entries' list, or `undefined` when there is none
256
+ */
257
+ private findReferencesList;
258
+ /**
259
+ * Finds the first `## References` heading in document order, at any depth.
260
+ *
261
+ * @param node - The node to search from
262
+ * @returns The heading and its position among its siblings, or `undefined`
263
+ */
264
+ private findReferencesSection;
265
+ /**
266
+ * Executes the second AST traversal pass: inline reference detection.
267
+ *
268
+ * This pass walks the AST in document order using a custom recursive
269
+ * traversal that tracks ancestor node types and:
270
+ * - Tracks the current section context (the most recently encountered
271
+ * heading identifier), starting with the DocID before any H2 heading
272
+ * - For each text node encountered, checks whether the node's ancestor
273
+ * chain includes any excluded node types (`code`, `inlineCode`, `html`)
274
+ * - Text nodes whose ancestors include excluded types are skipped
275
+ * - Valid text nodes are fed to ECR104
276
+ * (`InlineReferenceRule.evaluateTextNode`) with the current
277
+ * section context
278
+ * - After traversal, ECR104 is finalised
279
+ *
280
+ * @param root - The parsed MDAST root node
281
+ * @param docId - The document's established DocID
282
+ * @param declaredDocIds - The set of DocIDs declared in the References section
283
+ * @param sourceText - The document's raw Markdown, for the source-form checks of 1#9.11
284
+ * @returns The intermediate result from the inline references pass
285
+ */
286
+ private executeInlineReferencesPass;
287
+ /**
288
+ * Walks the AST to find inline runs for ECR104, tracking whether an
289
+ * excluded ancestor encloses the node and updating section context when
290
+ * headings are encountered.
291
+ *
292
+ * A paragraph is handed to ECR104 whole, as one inline run, because a
293
+ * citation is recognised in the text a reader sees and not one parsed text
294
+ * node at a time (1#9.5 rule 1). Its children are not walked separately.
295
+ *
296
+ * A hand-written depth-first, pre-order walk over an explicit stack: document
297
+ * order decides which section each run belongs to, and see
298
+ * {@link PerDocumentVisitor.pushChildren} for why the stack is not the call
299
+ * stack. Exclusion is carried down as one flag, because a node is excluded
300
+ * exactly when some ancestor's type is, so nothing is gained by keeping the
301
+ * ancestors themselves.
302
+ *
303
+ * @param root - The root of the walk
304
+ * @param sectionContext - Mutable wrapper holding the current section context
305
+ * @param inlineReferenceRule - The ECR104 rule instance to feed runs to
306
+ */
307
+ private walkNodesForInlineReferences;
308
+ /**
309
+ * Puts a node's children on the inline walk's stack, in document order,
310
+ * noting whether an excluded ancestor now encloses them.
311
+ *
312
+ * @param node - The node whose children are to be walked
313
+ * @param excluded - Whether an excluded ancestor already encloses the node
314
+ * @param pending - The stack to push onto
315
+ */
316
+ private static pushInlineWalkChildren;
317
+ /**
318
+ * Flattens one inline node into the segments a reader sees, in order.
319
+ *
320
+ * Text becomes a `text` segment carrying its position, and inline code a
321
+ * `code` segment. Formatting contributes its contents, each segment noting
322
+ * the spans around it. A hard break, an image, and a `<br>` tag become a
323
+ * `break` of one space, because a reader sees the words either side of them
324
+ * as separate. Any other inline HTML -- a comment, or a tag such as
325
+ * `<span>` -- contributes nothing, because it separates nothing:
326
+ * `60<span>s</span>` reads as `60s`.
327
+ *
328
+ * @param node - An inline node
329
+ * @param wrappers - Types of the formatting spans enclosing it, outermost first
330
+ * @param segments - Accumulator the segments are appended to
331
+ */
332
+ private collectInlineSegments;
333
+ /**
334
+ * Turns one inline node into segments, or puts its children on the stack
335
+ * to be turned into segments in their turn.
336
+ *
337
+ * @param entry - The node and the formatting spans enclosing it
338
+ * @param segments - Accumulator the segments are appended to
339
+ * @param pending - The walk's stack, which children are pushed onto in document order
340
+ */
341
+ private collectInlineSegment;
342
+ /**
343
+ * Determines the current section context identifier for a given heading.
344
+ *
345
+ * The section context is the identifier (DocID or SectionID) of the most
346
+ * recently encountered numbered heading. Before any H2 heading, the context
347
+ * is the DocID. After an H2 or deeper heading, the context is the
348
+ * SectionID of that heading.
349
+ *
350
+ * This method extracts the identifier from the heading text using the
351
+ * grammar's separator convention. If the heading text does not contain
352
+ * a valid identifier, the previous context is retained.
353
+ *
354
+ * @param headingText - The plain text content of the heading node
355
+ * @param headingDepth - The Markdown heading depth (1--6)
356
+ * @param currentContext - The current section context before this heading
357
+ * @returns The updated section context identifier
358
+ */
359
+ private determineSectionContext;
360
+ /**
361
+ * Determines whether a heading is the `## References` structural heading.
362
+ *
363
+ * The `## References` heading is owned by ECR103 and should not be
364
+ * evaluated by ECR102 (Section Hierarchy), because it is not a numbered
365
+ * section heading and would fail SectionID parsing.
366
+ *
367
+ * Per 1#9.6, the References heading is identified by depth === 2
368
+ * and text content exactly equal to `"References"`.
369
+ *
370
+ * @param headingData - The heading node data to check
371
+ * @returns `true` if the heading is the `## References` heading, `false` otherwise
372
+ */
373
+ private tellReferencesHeading;
374
+ /**
375
+ * Extracts the plain text content from a list item AST node.
376
+ *
377
+ * Gathers the text of the list item's child nodes into a single plain text
378
+ * string suitable for feeding to ECR103.
379
+ *
380
+ * @param listItemNode - The MDAST list item node to extract text from
381
+ * @returns The extracted {@link ListItemNodeData} with text and optional range
382
+ */
383
+ private extractListItemNodeData;
384
+ /**
385
+ * Classifies a literal node for tracing its text back to the source.
386
+ *
387
+ * @param type - The node's MDAST type
388
+ * @returns `text` for a text node, `code` for inline code, `other` otherwise
389
+ */
390
+ private static showSegmentKind;
391
+ /**
392
+ * Splits a list item's text into the parsed nodes it comes from.
393
+ *
394
+ * Follows `mdast-util-to-string` exactly -- a node's `value`, else an
395
+ * image's `alt`, else its children in order -- so that joining the
396
+ * segments reproduces the entry text the rule parses, and an offset in that
397
+ * text identifies the node, and so the source, it came from.
398
+ *
399
+ * @param node - A node within the list item
400
+ * @param segments - Accumulator the segments are appended to
401
+ */
402
+ private collectListItemSegments;
403
+ /**
404
+ * Extracts heading node data from an MDAST heading node.
405
+ *
406
+ * Reads the plain text content with {@link PerDocumentVisitor.showNodeText}
407
+ * and maps the MDAST position to a {@link HeadingNodeData} structure
408
+ * suitable for consumption by ECR101, ECR102, and ECR103.
409
+ *
410
+ * @param headingNode - The MDAST heading node to extract data from
411
+ * @returns The extracted {@link HeadingNodeData} with depth, text, and optional range
412
+ */
413
+ private extractHeadingNodeData;
414
+ /**
415
+ * Extracts text node data from an MDAST text node.
416
+ *
417
+ * Maps the MDAST text node's value and position to a {@link TextNodeData}
418
+ * structure suitable for consumption by ECR104.
419
+ *
420
+ * @param textNode - The MDAST text node to extract data from
421
+ * @returns The extracted {@link TextNodeData} with text and optional range
422
+ */
423
+ private extractTextNodeData;
424
+ /**
425
+ * Extracts the set of declared DocIDs from a completed ECR103 result.
426
+ *
427
+ * Iterates over the extracted {@link ReferenceEdge} artefacts and
428
+ * collects the `toDocId` field from each edge into a `ReadonlySet<DocID>`.
429
+ * This set is used to initialise ECR104 for undeclared reference detection.
430
+ *
431
+ * @param references - The extracted reference edges from ECR103
432
+ * @returns The set of declared DocIDs
433
+ */
434
+ private extractDeclaredDocIds;
435
+ /**
436
+ * Maps an MDAST position to a {@link PositionRange}, if available.
437
+ *
438
+ * MDAST positions use 1-based lines and 1-based columns, while
439
+ * {@link PositionRange} uses 0-based lines and 0-based characters.
440
+ * This method performs the conversion.
441
+ *
442
+ * @param mdastPosition - The MDAST position object, or `undefined` if
443
+ * positional metadata is not available
444
+ * @returns The mapped position range, or `undefined` if no position is available
445
+ */
446
+ private mapPosition;
447
+ /**
448
+ * Assembles the composite {@link LintResult} from individual rule results.
449
+ *
450
+ * Aggregates diagnostics from all rules, determines the `ok` status
451
+ * (true when no error-severity diagnostics are present), and constructs
452
+ * the {@link ExtractedDocument} when a valid DocID was recovered.
453
+ *
454
+ * @param identityResult - The result from ECR101
455
+ * @param sectionsResult - The result from ECR102 (undefined if ECR101 failed)
456
+ * @param referencesResult - The result from ECR103 (undefined if ECR101 failed)
457
+ * @param inlineResult - The result from ECR104 (undefined if ECR101 failed)
458
+ * @returns The composite lint result
459
+ */
460
+ private assembleLintResult;
461
+ /**
462
+ * Aggregates diagnostics from all rule results into a single array.
463
+ *
464
+ * Collects diagnostics from ECR101, ECR102, ECR103, and ECR104 in that
465
+ * order. Rules that did not run (undefined results) contribute no
466
+ * diagnostics.
467
+ *
468
+ * @param identityResult - The result from ECR101
469
+ * @param sectionsResult - The result from ECR102 (undefined if ECR101 failed)
470
+ * @param referencesResult - The result from ECR103 (undefined if ECR101 failed)
471
+ * @param inlineResult - The result from ECR104 (undefined if ECR101 failed)
472
+ * @returns The aggregated array of all diagnostics
473
+ */
474
+ private aggregateDiagnostics;
475
+ /**
476
+ * Constructs the {@link LintInput} descriptor for the result.
477
+ *
478
+ * Echoes the visitor's URI and optional version into the standard
479
+ * input descriptor shape.
480
+ *
481
+ * @returns The lint input descriptor
482
+ */
483
+ private buildLintInput;
484
+ /**
485
+ * Determines the composite `ok` status from an aggregated diagnostics array.
486
+ *
487
+ * The result is `ok` (true) when no diagnostics have severity `"error"`.
488
+ * Any error-severity diagnostic causes the result to be not-ok (false).
489
+ *
490
+ * @param diagnostics - The full array of aggregated diagnostics from all rules
491
+ * @returns `true` if no error diagnostics are present, `false` otherwise
492
+ */
493
+ private tellAllDiagnosticsPassable;
494
+ /**
495
+ * Constructs the {@link ExtractedDocument} from individual rule extraction
496
+ * results.
497
+ *
498
+ * Assembles the document identity (DocID and title), section nodes,
499
+ * reference edges, and inline reference edges into the canonical
500
+ * extracted document shape.
501
+ *
502
+ * @param identity - The document identity from ECR101
503
+ * @param sections - The section nodes from ECR102
504
+ * @param references - The reference edges from ECR103
505
+ * @param inlineReferences - The inline reference edges from ECR104
506
+ * @returns The assembled extracted document
507
+ */
508
+ private buildExtractedDocument;
509
+ }
510
+ //# sourceMappingURL=per-document-visitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"per-document-visitor.d.ts","sourceRoot":"","sources":["../src/per-document-visitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAaH,OAAO,KAAK,EAMV,UAAU,EAKX,MAAM,YAAY,CAAC;AA6EpB;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IAErB;;;;;OAKG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;CAC3B;AA6CD,wEAAwE;AACxE,eAAO,MAAM,2BAA2B,EAAE,MAA8B,CAAC;AAEzE,2CAA2C;AAC3C,eAAO,MAAM,yBAAyB,EAAE,MAA8B,CAAC;AASvE;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,EAAE,WAAW,CAAC,MAAM,CAI3D,CAAC;AAiGH;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AACH,qBAAa,kBAAkB;IAC7B;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAE7B;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAE7C;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAE5C;;;;;;;OAOG;gBACgB,OAAO,EAAE,yBAAyB;IAMrD;;;;;;;;;;;;;;;;;;OAkBG;IACI,IAAI,CAAC,YAAY,EAAE,MAAM,GAAG,UAAU;IAqD7C;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,aAAa;IAOrB;;;;;;;;;;;OAWG;IACH,OAAO,CAAC,gBAAgB;IA0BxB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,OAAO,CAAC,gCAAgC;IA4ExC;;;;;;;;OAQG;IACH,OAAO,CAAC,eAAe;IAqBvB;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAgC3B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAgB3B;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,6BAA6B;IA8BrC;;;;;;;;;;OAUG;IACH,OAAO,CAAC,kBAAkB;IAc1B;;;;;OAKG;IACH,OAAO,CAAC,qBAAqB;IAsC7B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,OAAO,CAAC,2BAA2B;IAkCnC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,4BAA4B;IAyDpC;;;;;;;OAOG;IACH,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAsBrC;;;;;;;;;;;;;;OAcG;IACH,OAAO,CAAC,qBAAqB;IAkB7B;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAiD5B;;;;;;;;;;;;;;;;OAgBG;IACH,OAAO,CAAC,uBAAuB;IA6B/B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,qBAAqB;IAQ7B;;;;;;;;OAQG;IACH,OAAO,CAAC,uBAAuB;IAoB/B;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,eAAe;IAQ9B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,uBAAuB;IAmC/B;;;;;;;;;OASG;IACH,OAAO,CAAC,sBAAsB;IAoB9B;;;;;;;;OAQG;IACH,OAAO,CAAC,mBAAmB;IAkB3B;;;;;;;;;OASG;IACH,OAAO,CAAC,qBAAqB;IAgB7B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,WAAW;IAyBnB;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,kBAAkB;IAiD1B;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,oBAAoB;IAiC5B;;;;;;;OAOG;IACH,OAAO,CAAC,cAAc;IAStB;;;;;;;;OAQG;IACH,OAAO,CAAC,0BAA0B;IAQlC;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,sBAAsB;CAgB/B"}