@orkestrel/markdown 0.0.12 → 0.0.14

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.
@@ -1,30 +1,30 @@
1
1
  import { BooleanShape } from '@orkestrel/contract';
2
- import { CommentNode } from '@orkestrel/html';
3
- import { ContractInterface } from '@orkestrel/contract';
4
- import { DoctypeNode } from '@orkestrel/html';
5
- import { ElementNode } from '@orkestrel/html';
6
- import { Guard } from '@orkestrel/contract';
7
- import { HTMLDocument } from '@orkestrel/html';
8
- import { HTMLNode } from '@orkestrel/html';
2
+ import type { CommentNode } from '@orkestrel/html';
3
+ import type { ContractInterface } from '@orkestrel/contract';
4
+ import type { DoctypeNode } from '@orkestrel/html';
5
+ import type { ElementNode } from '@orkestrel/html';
6
+ import type { Guard } from '@orkestrel/contract';
7
+ import type { HTMLDocument } from '@orkestrel/html';
8
+ import type { HTMLNode } from '@orkestrel/html';
9
9
  import { LiteralShape } from '@orkestrel/contract';
10
10
  import { NumberShape } from '@orkestrel/contract';
11
11
  import { ObjectShape } from '@orkestrel/contract';
12
12
  import { OptionalShape } from '@orkestrel/contract';
13
13
  import { StringShape } from '@orkestrel/contract';
14
- import { TextNode as TextNode_2 } from '@orkestrel/html';
14
+ import type { TextNode as TextNode_2 } from '@orkestrel/html';
15
15
 
16
- /** A node that can appear at the block level of a document (or inside a list item / blockquote). */
16
+ /** Represents a node that can appear at the block level of a document (or inside a list item / blockquote). */
17
17
  export declare type BlockNode = HeadingNode | ParagraphNode | ListNode | TableNode | CodeBlockNode | BlockquoteNode | ThematicBreakNode;
18
18
 
19
- /** A blockquote - `>`-prefixed lines; `children` the block content parsed from the de-quoted lines (so quotes nest). */
19
+ /** Represents a blockquote `>`-prefixed lines; `children` the block content parsed from the de-quoted lines (so quotes nest). */
20
20
  export declare interface BlockquoteNode {
21
21
  readonly element: 'blockquote';
22
- /** The block content of the quote (the `>`-stripped lines, re-parsed as blocks). */
22
+ /** Holds the block content of the quote (the `>`-stripped lines, re-parsed as blocks). */
23
23
  readonly children: readonly BlockNode[];
24
24
  }
25
25
 
26
26
  /**
27
- * Merge adjacent text nodes into one - the inline scanner emits a text node per
27
+ * Merges adjacent text nodes into one the inline scanner emits a text node per
28
28
  * unrecognized character, so coalescing keeps the AST clean and assertion-friendly.
29
29
  *
30
30
  * @param nodes - The inline nodes (possibly with adjacent text runs)
@@ -40,21 +40,21 @@ export declare interface BlockquoteNode {
40
40
  export declare function coalesceText(nodes: readonly InlineNode[], spans?: Map<MarkdownNode, MarkdownSpan>): readonly InlineNode[];
41
41
 
42
42
  /**
43
- * A fenced code block - ```` ```lang ````. `code` is the verbatim block content (no
43
+ * Represents a fenced code block ```` ```lang ````. `code` is the verbatim block content (no
44
44
  * inner markdown; the closing fence and the trailing newline are stripped), `lang`
45
45
  * the info-string language tag (the first word after the opening fence), absent when
46
46
  * none was given.
47
47
  */
48
48
  export declare interface CodeBlockNode {
49
49
  readonly element: 'codeBlock';
50
- /** The info-string language tag (first word after the opening fence), if any. */
50
+ /** Holds the info-string language tag (first word after the opening fence), if any. */
51
51
  readonly lang?: string;
52
- /** The verbatim code content (no inner markdown; HTML-escaped at render). */
52
+ /** Holds the verbatim code content (no inner markdown; HTML-escaped at render). */
53
53
  readonly code: string;
54
54
  }
55
55
 
56
56
  /**
57
- * The shape of a {@link CodeBlockNode} - a fenced code block. `lang` is
57
+ * Describes the shape of a {@link CodeBlockNode} a fenced code block. `lang` is
58
58
  * optional (absent when the opening fence carries no info-string).
59
59
  *
60
60
  * @example
@@ -67,25 +67,36 @@ export declare interface CodeBlockNode {
67
67
  * codeBlock.is({ element: 'codeBlock', code: 'x', lang: 'ts' }) // true
68
68
  * ```
69
69
  */
70
- export declare const codeBlockShape: ObjectShape<{
71
- element: LiteralShape<readonly ["codeBlock"]>;
72
- lang: OptionalShape<StringShape>;
73
- code: StringShape;
70
+ export declare const codeBlockShape: ObjectShape< {
71
+ element: LiteralShape<readonly ["codeBlock"]>;
72
+ lang: OptionalShape<StringShape>;
73
+ code: StringShape;
74
74
  }, false>;
75
75
 
76
76
  /**
77
- * An inline code span - `` `code` ``. `value` is the verbatim span text; no inner
77
+ * Represents the located extent of one inline code span the value the inline phase's code
78
+ * scanner returns for a matched backtick run.
79
+ */
80
+ export declare interface CodeSpanMatch {
81
+ /** Holds the span's literal text, with one padding space stripped from each end. */
82
+ readonly value: string;
83
+ /** Holds the index one past the span's closing backtick run, exclusive. */
84
+ readonly end: number;
85
+ }
86
+
87
+ /**
88
+ * Represents an inline code span — `` `code` ``. `value` is the verbatim span text; no inner
78
89
  * markdown is parsed (code is literal), and the renderer HTML-escapes it inside a
79
90
  * `<code>` element.
80
91
  */
81
92
  export declare interface CodeSpanNode {
82
93
  readonly element: 'codeSpan';
83
- /** The verbatim code text (no inner markdown; HTML-escaped at render). */
94
+ /** Holds the verbatim code text (no inner markdown; HTML-escaped at render). */
84
95
  readonly value: string;
85
96
  }
86
97
 
87
98
  /**
88
- * The shape of a {@link CodeSpanNode} - an inline code span (`` `code` ``).
99
+ * Describes the shape of a {@link CodeSpanNode} an inline code span (`` `code` ``).
89
100
  *
90
101
  * @example
91
102
  * ```ts
@@ -96,9 +107,9 @@ export declare interface CodeSpanNode {
96
107
  * codeSpan.is({ element: 'codeSpan', value: 'const x = 1' }) // true
97
108
  * ```
98
109
  */
99
- export declare const codeSpanShape: ObjectShape<{
100
- element: LiteralShape<readonly ["codeSpan"]>;
101
- value: StringShape;
110
+ export declare const codeSpanShape: ObjectShape< {
111
+ element: LiteralShape<readonly ["codeSpan"]>;
112
+ value: StringShape;
102
113
  }, false>;
103
114
 
104
115
  /**
@@ -117,10 +128,7 @@ export declare const codeSpanShape: ObjectShape<{
117
128
  * collectList(splitLines('- item'), 0, 0) // { node: { element: 'list', ... }, next: 1 }
118
129
  * ```
119
130
  */
120
- export declare function collectList(lines: readonly MarkdownSource[], start: number, depth: number, spans?: Map<MarkdownNode, MarkdownSpan>, end?: number): {
121
- readonly node: ListNode;
122
- readonly next: number;
123
- };
131
+ export declare function collectList(lines: readonly MarkdownSource[], start: number, depth: number, spans?: Map<MarkdownNode, MarkdownSpan>, end?: number): ListCollection;
124
132
 
125
133
  /**
126
134
  * Collects a GFM table starting at a header row, parsing the header, the
@@ -136,13 +144,10 @@ export declare function collectList(lines: readonly MarkdownSource[], start: num
136
144
  * collectTable(splitLines('| a |\n| - |'), 0) // { node: { element: 'table', ... }, next: 2 }
137
145
  * ```
138
146
  */
139
- export declare function collectTable(lines: readonly MarkdownSource[], start: number, spans?: Map<MarkdownNode, MarkdownSpan>): {
140
- readonly node: TableNode;
141
- readonly next: number;
142
- };
147
+ export declare function collectTable(lines: readonly MarkdownSource[], start: number, spans?: Map<MarkdownNode, MarkdownSpan>): TableCollection;
143
148
 
144
149
  /**
145
- * The count of leading space / tab characters on `line` (a tab counts as one) - the
150
+ * Counts the leading space / tab characters on `line` (a tab counts as one) the
146
151
  * indent that decides whether a list item's continuation belongs to the item.
147
152
  *
148
153
  * @param line - The line to measure
@@ -156,9 +161,9 @@ export declare function collectTable(lines: readonly MarkdownSource[], start: nu
156
161
  export declare function countIndent(line: string): number;
157
162
 
158
163
  /**
159
- * Compile the {@link codeBlockShape} into a {@link ContractInterface} for
160
- * {@link CodeBlockNode} - a guard, coercing parser, JSON Schema, and seeded
161
- * generator from one shape declaration (AGENTS §14).
164
+ * Compiles the {@link codeBlockShape} into a {@link ContractInterface} for
165
+ * {@link CodeBlockNode} a guard, coercing parser, JSON Schema, and seeded
166
+ * generator from one shape declaration.
162
167
  *
163
168
  * @returns A `CodeBlockNode` contract bundling `schema` / `is` / `parse` / `generate`
164
169
  *
@@ -173,9 +178,9 @@ export declare function countIndent(line: string): number;
173
178
  export declare function createCodeBlockContract(): ContractInterface<CodeBlockNode>;
174
179
 
175
180
  /**
176
- * Compile the {@link codeSpanShape} into a {@link ContractInterface} for
177
- * {@link CodeSpanNode} - a guard, coercing parser, JSON Schema, and seeded
178
- * generator from one shape declaration (AGENTS §14).
181
+ * Compiles the {@link codeSpanShape} into a {@link ContractInterface} for
182
+ * {@link CodeSpanNode} a guard, coercing parser, JSON Schema, and seeded
183
+ * generator from one shape declaration.
179
184
  *
180
185
  * @returns A `CodeSpanNode` contract bundling `schema` / `is` / `parse` / `generate`
181
186
  *
@@ -190,7 +195,7 @@ export declare function createCodeBlockContract(): ContractInterface<CodeBlockNo
190
195
  export declare function createCodeSpanContract(): ContractInterface<CodeSpanNode>;
191
196
 
192
197
  /**
193
- * Compile the {@link lineBreakShape} into a {@link ContractInterface} for
198
+ * Compiles the {@link lineBreakShape} into a {@link ContractInterface} for
194
199
  * {@link LineBreakNode}.
195
200
  *
196
201
  * @returns A `LineBreakNode` contract bundling `schema` / `is` / `parse` / `generate`
@@ -205,8 +210,8 @@ export declare function createCodeSpanContract(): ContractInterface<CodeSpanNode
205
210
  export declare function createLineBreakContract(): ContractInterface<LineBreakNode>;
206
211
 
207
212
  /**
208
- * Create a stateful markdown handle from a markdown string or an already-parsed
209
- * {@link MarkdownDocument} - a typed AST plus the query, rewrite, and fold operations
213
+ * Creates a stateful markdown handle from a markdown string or an already-parsed
214
+ * {@link MarkdownDocument} a typed AST plus the query, rewrite, and fold operations
210
215
  * {@link MarkdownInterface} exposes.
211
216
  *
212
217
  * @remarks
@@ -214,9 +219,9 @@ export declare function createLineBreakContract(): ContractInterface<LineBreakNo
214
219
  * fenced code / blockquotes / thematic breaks) then an inline phase (emphasis /
215
220
  * inline code / links / images / hard breaks) to build a render-agnostic
216
221
  * {@link MarkdownDocument}. Given a
217
- * {@link MarkdownDocument}, adopts it AS-IS without re-validation - gate an untrusted
222
+ * {@link MarkdownDocument}, adopts it as-is without re-validation gate an untrusted
218
223
  * value with `isMarkdownDocument` first. Pure + total parse (malformed markdown
219
- * degrades to text, never throws) and zero-dependency - a hand-written scanner, no
224
+ * degrades to text, never throws) and zero-dependency a hand-written scanner, no
220
225
  * regex-only structural parse, linear-time (no ReDoS).
221
226
  *
222
227
  * @param input - A markdown string to parse, or an already-parsed {@link MarkdownDocument}
@@ -233,7 +238,7 @@ export declare function createLineBreakContract(): ContractInterface<LineBreakNo
233
238
  export declare function createMarkdown(input: string | MarkdownDocument): MarkdownInterface;
234
239
 
235
240
  /**
236
- * Create an HTML-to-markdown projection with absent fields defaulted from
241
+ * Builds an HTML-to-markdown projection with absent fields defaulted from
237
242
  * {@link EMPTY_PROJECTION} and the block/inline exclusivity invariant enforced.
238
243
  *
239
244
  * @remarks
@@ -255,9 +260,9 @@ export declare function createMarkdown(input: string | MarkdownDocument): Markdo
255
260
  export declare function createProjection(parts?: Partial<MarkdownProjection>): MarkdownProjection;
256
261
 
257
262
  /**
258
- * Compile the {@link textShape} into a {@link ContractInterface} for
259
- * {@link TextNode} - a guard, coercing parser, JSON Schema, and seeded
260
- * generator from one shape declaration (AGENTS §14).
263
+ * Compiles the {@link textShape} into a {@link ContractInterface} for
264
+ * {@link TextNode} a guard, coercing parser, JSON Schema, and seeded
265
+ * generator from one shape declaration.
261
266
  *
262
267
  * @returns A `TextNode` contract bundling `schema` / `is` / `parse` / `generate`
263
268
  *
@@ -272,9 +277,9 @@ export declare function createProjection(parts?: Partial<MarkdownProjection>): M
272
277
  export declare function createTextContract(): ContractInterface<TextNode>;
273
278
 
274
279
  /**
275
- * Compile the {@link thematicBreakShape} into a {@link ContractInterface} for
276
- * {@link ThematicBreakNode} - a guard, coercing parser, JSON Schema, and
277
- * seeded generator from one shape declaration (AGENTS §14).
280
+ * Compiles the {@link thematicBreakShape} into a {@link ContractInterface} for
281
+ * {@link ThematicBreakNode} a guard, coercing parser, JSON Schema, and
282
+ * seeded generator from one shape declaration.
278
283
  *
279
284
  * @returns A `ThematicBreakNode` contract bundling `schema` / `is` / `parse` / `generate`
280
285
  *
@@ -289,7 +294,7 @@ export declare function createTextContract(): ContractInterface<TextNode>;
289
294
  export declare function createThematicBreakContract(): ContractInterface<ThematicBreakNode>;
290
295
 
291
296
  /**
292
- * Derive the per-column {@link TableAlign} list from a GFM delimiter row - `:---`
297
+ * Derives the per-column {@link TableAlign} list from a GFM delimiter row `:---`
293
298
  * left, `---:` right, `:---:` center, and `---` as the explicit no-alignment
294
299
  * marker represented by `null`.
295
300
  *
@@ -304,21 +309,47 @@ export declare function createThematicBreakContract(): ContractInterface<Themati
304
309
  export declare function delimiterToAlignments(delimiter: string): ReadonlyArray<TableAlign | null>;
305
310
 
306
311
  /**
307
- * Emphasized inline content - `*italic*` / `_italic_` (`strong: false`) or
312
+ * Represents the located content and syntax bounds of one emphasis run — the value the inline
313
+ * phase's emphasis locator returns for a matched marker run.
314
+ */
315
+ export declare interface EmphasisBounds {
316
+ /** Holds `true` for a doubled marker (`**strong**`), `false` for a single one (`*em*`). */
317
+ readonly strong: boolean;
318
+ /** Holds the index of the run's first content character. */
319
+ readonly open: number;
320
+ /** Holds the index of the closing marker run's first character. */
321
+ readonly close: number;
322
+ /** Holds the index one past the closing marker run, exclusive. */
323
+ readonly end: number;
324
+ }
325
+
326
+ /**
327
+ * Represents emphasized inline content — `*italic*` / `_italic_` (`strong: false`) or
308
328
  * `**bold**` / `__bold__` (`strong: true`). `children` are the nested inline nodes,
309
329
  * so emphasis composes (a `**bold _and italic_**` is a strong node wrapping a text
310
330
  * node and an emphasis node).
311
331
  */
312
332
  export declare interface EmphasisNode {
313
333
  readonly element: 'emphasis';
314
- /** `true` for strong (`**` / `__`, → `<strong>`); `false` for ordinary emphasis (`*` / `_`, → `<em>`). */
334
+ /** Holds `true` for strong (`**` / `__`, → `<strong>`); `false` for ordinary emphasis (`*` / `_`, → `<em>`). */
315
335
  readonly strong: boolean;
316
- /** The emphasized inline content. */
336
+ /** Holds the emphasized inline content. */
317
337
  readonly children: readonly InlineNode[];
318
338
  }
319
339
 
320
340
  /**
321
- * The frozen empty HTML-to-markdown projection from which projection factories
341
+ * Represents the scanned result of one emphasis run — the node the inline phase's emphasis
342
+ * scanner built from {@link EmphasisBounds} and where the scan resumes.
343
+ */
344
+ export declare interface EmphasisScan {
345
+ /** Holds the scanned emphasis run, its content already scanned into inline children. */
346
+ readonly node: EmphasisNode;
347
+ /** Holds the index one past the closing marker run, exclusive. */
348
+ readonly end: number;
349
+ }
350
+
351
+ /**
352
+ * Holds the frozen empty HTML-to-markdown projection from which projection factories
322
353
  * default every absent field.
323
354
  *
324
355
  * @example
@@ -330,7 +361,7 @@ export declare interface EmphasisNode {
330
361
  export declare const EMPTY_PROJECTION: MarkdownProjection;
331
362
 
332
363
  /**
333
- * Extract a fenced-code opening line (```` ``` ```` or `~~~`, optionally with an info
364
+ * Extracts a fenced-code opening line (```` ``` ```` or `~~~`, optionally with an info
334
365
  * string) into its `{ marker, lang }`, or `undefined` when `line` is not a fence
335
366
  * opener. `marker` is the exact fence run (the closer must match the same character +
336
367
  * at least the same length); `lang` is the first word of the info string.
@@ -343,10 +374,7 @@ export declare const EMPTY_PROJECTION: MarkdownProjection;
343
374
  * extractFence('```ts') // { marker: '```', lang: 'ts' }
344
375
  * ```
345
376
  */
346
- export declare function extractFence(line: string): {
347
- readonly marker: string;
348
- readonly lang: string | undefined;
349
- } | undefined;
377
+ export declare function extractFence(line: string): FenceMatch | undefined;
350
378
 
351
379
  /**
352
380
  * Extracts an ATX heading line (`#` … `######` followed by text) into its level,
@@ -362,14 +390,10 @@ export declare function extractFence(line: string): {
362
390
  * extractHeading('## Title') // { level: 2, text: 'Title', offset: 3 }
363
391
  * ```
364
392
  */
365
- export declare function extractHeading(line: string): {
366
- readonly level: number;
367
- readonly text: string;
368
- readonly offset: number;
369
- } | undefined;
393
+ export declare function extractHeading(line: string): HeadingMatch | undefined;
370
394
 
371
395
  /**
372
- * Extract a list-item line (`-` / `*` / `+` bullet, or `1.` / `1)` ordinal, followed by
396
+ * Extracts a list-item line (`-` / `*` / `+` bullet, or `1.` / `1)` ordinal, followed by
373
397
  * a space) into its {@link ListItemMatch}, or `undefined` when `line` is not a list
374
398
  * item. `content` is the text after the marker; `marker` is the full marker-plus-space
375
399
  * width (for measuring a continuation's indent).
@@ -385,8 +409,19 @@ export declare function extractHeading(line: string): {
385
409
  export declare function extractListItem(line: string): ListItemMatch | undefined;
386
410
 
387
411
  /**
388
- * Concatenate the `value` / `code` content of every descendant text / code-span /
389
- * code-block node under `node`, including image alternative content, in walk order -
412
+ * Represents the parsed parts of a fenced-code opening line the value the block phase's fence
413
+ * detector returns for a ```` ``` ```` or `~~~` opener.
414
+ */
415
+ export declare interface FenceMatch {
416
+ /** Holds the exact fence run; a closer must repeat the same character at least as long. */
417
+ readonly marker: string;
418
+ /** Holds the first word of the info string, or `undefined` when the fence declares none. */
419
+ readonly lang: string | undefined;
420
+ }
421
+
422
+ /**
423
+ * Concatenates the `value` / `code` content of every descendant text / code-span /
424
+ * code-block node under `node`, including image alternative content, in walk order —
390
425
  * the plain-text projection of an AST (search indexing, word counts, a text-only
391
426
  * preview).
392
427
  *
@@ -409,16 +444,16 @@ export declare function extractListItem(line: string): ListItemMatch | undefined
409
444
  export declare function flattenText(node: MarkdownNode): string;
410
445
 
411
446
  /**
412
- * Fold a {@link MarkdownNode} into a `T` via a total catamorphism - children are
447
+ * Folds a {@link MarkdownNode} into a `T` through a total catamorphism children are
413
448
  * folded first (post-order), then the node's own {@link MarkdownHandler} is invoked
414
449
  * with the already-folded children.
415
450
  *
416
451
  * @remarks
417
- * **Table contract.** A {@link TableNode} has no single `children` array - its cells
452
+ * **Table contract.** A {@link TableNode} has no single `children` array its cells
418
453
  * live in `header` (one inline-node list per column) and `rows` (a list of such
419
- * rows). The `table` handler receives ONE folded `T` per inline node, flattened in
420
- * walk order across ALL cells - every header cell's inline nodes (column order), then
421
- * every body row's cells' inline nodes (row order, then column order) - and reads
454
+ * rows). The `table` handler receives one folded `T` per inline node, flattened in
455
+ * walk order across all cells every header cell's inline nodes (column order), then
456
+ * every body row's cells' inline nodes (row order, then column order) and reads
422
457
  * `node.header[c].length` / `node.rows[r][c].length` off the table node itself to
423
458
  * recover cell boundaries within the flat list.
424
459
  *
@@ -426,64 +461,77 @@ export declare function flattenText(node: MarkdownNode): string;
426
461
  * with an empty children list instead of recursing further.
427
462
  *
428
463
  * @param node - The AST node to fold
429
- * @param handlers - The total {@link MarkdownHandlers} table, one handler per element
464
+ * @param handlers - The total {@link MarkdownHandlerMap} table, one handler per element
430
465
  * @param depth - The starting recursion depth (pass `0` at the entry point)
431
466
  * @returns The folded `T`
432
467
  *
433
468
  * @example
434
469
  * ```ts
435
- * const countHandlers: MarkdownHandlers<number> = {
470
+ * const countHandlers: MarkdownHandlerMap<number> = {
436
471
  * document: (_, children) => children.reduce((a, b) => a + b, 1),
437
472
  * // ...one handler per element, each summing its folded children
438
473
  * }
439
474
  * foldNode(document, countHandlers, 0) // total node count
440
475
  * ```
441
476
  */
442
- export declare function foldNode<T>(node: MarkdownNode, handlers: MarkdownHandlers<T>, depth: number): T;
477
+ export declare function foldNode<T>(node: MarkdownNode, handlers: MarkdownHandlerMap<T>, depth: number): T;
443
478
 
444
479
  /**
445
- * An ATX heading - `#` `######`. `level` is 1–6 (the number of leading `#`),
480
+ * Represents the parsed parts of a single ATX heading line the value the block phase's heading
481
+ * detector returns for a `#` … `######` line.
482
+ */
483
+ export declare interface HeadingMatch {
484
+ /** Holds the heading's level, 1 to 6. */
485
+ readonly level: number;
486
+ /** Holds the heading's raw inline text, with an optional closing `#` run stripped. */
487
+ readonly text: string;
488
+ /** Holds the offset of {@link HeadingMatch.text} inside the original line. */
489
+ readonly offset: number;
490
+ }
491
+
492
+ /**
493
+ * Represents an ATX heading — `#` … `######`. `level` is 1–6 (the number of leading `#`),
446
494
  * `children` the inline content of the heading text.
447
495
  */
448
496
  export declare interface HeadingNode {
449
497
  readonly element: 'heading';
450
- /** The heading level, 1 (`#`) through 6 (`######`). */
498
+ /** Holds the heading level, 1 (`#`) through 6 (`######`). */
451
499
  readonly level: number;
452
- /** The inline content of the heading text. */
500
+ /** Holds the inline content of the heading text. */
453
501
  readonly children: readonly InlineNode[];
454
502
  }
455
503
 
456
504
  /**
457
- * Project an `@orkestrel/html` {@link HTMLNode} into a {@link MarkdownDocument} - the
505
+ * Projects an `@orkestrel/html` {@link HTMLNode} into a {@link MarkdownDocument} the
458
506
  * HTML→markdown direction, and the inverse of {@link markdownToHTML}.
459
507
  *
460
508
  * @remarks
461
- * **Engine.** One total handler table - {@link projectHTMLNode} for the containers,
462
- * {@link projectHTMLLeaf} for the leaves - folded by `@orkestrel/html`'s own `foldNode`, so
509
+ * **Engine.** One total handler table {@link projectHTMLNode} for the containers,
510
+ * {@link projectHTMLLeaf} for the leaves folded by `@orkestrel/html`'s own `foldNode`, so
463
511
  * depth capping, cycle safety, and bottom-up ordering are inherited rather than
464
512
  * rebuilt. Total: hostile, cyclic, and pathologically deep input degrades instead of
465
513
  * throwing.
466
514
  *
467
515
  * **Composed depth.** Both packages cap recursion at 64, and html's cap is reached
468
- * first: a document nested past it projects to a chain bounded by THAT cap, with the
516
+ * first: a document nested past it projects to a chain bounded by that cap, with the
469
517
  * content below it truncated before markdown ever sees it. Since the projected chain
470
518
  * can be a level or two deeper than {@link MAX_DEPTH}, the serializer's own cap can
471
- * then truncate again - so the anchor law below is a law within the depth budget, and
519
+ * then truncate again so the anchor law that follows is a law within the depth budget, and
472
520
  * beyond it only totality is promised.
473
521
  *
474
522
  * **Safety.** Every `href` and `src` is re-sanitized through
475
523
  * `sanitizeURL(value, SAFE_URL_SCHEMES)` whether or not the AST was ever sanitized,
476
524
  * because a hand-built one never was. A refused destination empties to `''` and the
477
- * link or image is KEPT - `[text]()` - since a bad URL is no reason to lose the words
525
+ * link or image is kept `[text]()` because a bad URL is no reason to lose the words
478
526
  * around it. An `UNSAFE_ELEMENTS` subtree contributes nothing at all, text included, so
479
527
  * a `script` body can never resurface as prose.
480
528
  *
481
529
  * **The anchor law.** HTML→markdown is lossy, so the fixpoint that matters is the
482
- * PROJECTED AST, not the input bytes:
530
+ * projected AST, not the input bytes:
483
531
  * `parseDocument(renderMarkdown(htmlToMarkdown(x)))` deep-equals `htmlToMarkdown(x)`.
484
532
  * The projection therefore emits canonical markdown shapes rather than literal
485
- * translations - whitespace collapsed, edges trimmed, a blank paragraph dropped, a hard
486
- * break only where a line can end - because a shape markdown cannot write back is a
533
+ * translations whitespace collapsed, edges trimmed, a blank paragraph dropped, a hard
534
+ * break only where a line can end because a shape markdown cannot write back is a
487
535
  * shape this projection has no business producing.
488
536
  *
489
537
  * @param node - The HTML document or bare node to project
@@ -500,27 +548,27 @@ export declare interface HeadingNode {
500
548
  export declare function htmlToMarkdown(node: HTMLNode): MarkdownDocument;
501
549
 
502
550
  /**
503
- * An inline image - `![alt](src)`. `children` are the inline nodes of the
551
+ * Represents an inline image `![alt](src)`. `children` are the inline nodes of the
504
552
  * alternative content and `src` is the image destination.
505
553
  */
506
554
  export declare interface ImageNode {
507
555
  readonly element: 'image';
508
- /** The image destination. */
556
+ /** Holds the image destination. */
509
557
  readonly src: string;
510
- /** The inline alternative content. */
558
+ /** Holds the inline alternative content. */
511
559
  readonly children: readonly InlineNode[];
512
560
  }
513
561
 
514
- /** A node that can appear inside inline content (a heading / paragraph / cell / list item / link text). */
562
+ /** Represents a node that can appear inside inline content (a heading / paragraph / cell / list item / link text). */
515
563
  export declare type InlineNode = TextNode | EmphasisNode | CodeSpanNode | LineBreakNode | LinkNode | ImageNode;
516
564
 
517
565
  /**
518
- * Whether `line` is blank - empty, or containing only whitespace - the markdown
566
+ * Checks whether `line` is blank empty, or containing only whitespace the markdown
519
567
  * definition of a blank line that block parsing uses to separate paragraphs, skip
520
568
  * gaps, and end list continuations.
521
569
  *
522
570
  * @param line - The candidate line
523
- * @returns `true` when the line is blank
571
+ * @returns True if the line is blank; false otherwise
524
572
  *
525
573
  * @example
526
574
  * ```ts
@@ -530,19 +578,19 @@ export declare type InlineNode = TextNode | EmphasisNode | CodeSpanNode | LineBr
530
578
  export declare function isBlankLine(line: string): boolean;
531
579
 
532
580
  /**
533
- * Determine whether an arbitrary value is a valid {@link BlockNode} - a
581
+ * Determines whether an arbitrary value is a valid {@link BlockNode} a
534
582
  * heading, paragraph, list, table, code block, blockquote, or thematic break,
535
583
  * recursively validated.
536
584
  *
537
585
  * @remarks
538
- * Total: never throws, even on cyclic or pathologically deep input - every
586
+ * Total: never throws, even on cyclic or pathologically deep input every
539
587
  * combinator involved (`unionOf`, `recordOf`, `arrayOf`, `lazyOf`) is
540
- * throw-contained per the `@orkestrel/contract` guard contract (AGENTS §14).
588
+ * throw-contained per the `@orkestrel/contract` guard contract.
541
589
  * A list item's shape is inlined here (and in {@link isMarkdownNode}) rather
542
- * than named separately - it is used at exactly these two sites.
590
+ * than named separately it is used at exactly these two sites.
543
591
  *
544
592
  * @param value - The value to test
545
- * @returns `true` when `value` is a well-formed {@link BlockNode}
593
+ * @returns True if `value` is a well-formed {@link BlockNode}; false otherwise
546
594
  *
547
595
  * @example
548
596
  * ```ts
@@ -555,7 +603,10 @@ export declare function isBlankLine(line: string): boolean;
555
603
  export declare const isBlockNode: Guard<BlockNode>;
556
604
 
557
605
  /**
558
- * Determine whether a node is a blockquote block.
606
+ * Determines whether a node is a blockquote block.
607
+ *
608
+ * @param node - The AST node to test
609
+ * @returns True if the node is a {@link BlockquoteNode}; false otherwise
559
610
  *
560
611
  * @example
561
612
  * ```ts
@@ -565,7 +616,10 @@ export declare const isBlockNode: Guard<BlockNode>;
565
616
  export declare function isBlockquoteNode(node: MarkdownNode): node is BlockquoteNode;
566
617
 
567
618
  /**
568
- * Determine whether a node is a fenced code block.
619
+ * Determines whether a node is a fenced code block.
620
+ *
621
+ * @param node - The AST node to test
622
+ * @returns True if the node is a {@link CodeBlockNode}; false otherwise
569
623
  *
570
624
  * @example
571
625
  * ```ts
@@ -575,12 +629,15 @@ export declare function isBlockquoteNode(node: MarkdownNode): node is Blockquote
575
629
  export declare function isCodeBlockNode(node: MarkdownNode): node is CodeBlockNode;
576
630
 
577
631
  /**
578
- * Determine whether a node is an inline code span.
632
+ * Determines whether a node is an inline code span.
579
633
  *
580
634
  * @remarks
581
- * Narrows to {@link CodeSpanNode} - the node whose `element` discriminant is
635
+ * Narrows to {@link CodeSpanNode} the node whose `element` discriminant is
582
636
  * `'codeSpan'`.
583
637
  *
638
+ * @param node - The AST node to test
639
+ * @returns True if the node is a {@link CodeSpanNode}; false otherwise
640
+ *
584
641
  * @example
585
642
  * ```ts
586
643
  * isCodeSpanNode({ element: 'codeSpan', value: 'x' }) // true
@@ -589,7 +646,10 @@ export declare function isCodeBlockNode(node: MarkdownNode): node is CodeBlockNo
589
646
  export declare function isCodeSpanNode(node: MarkdownNode): node is CodeSpanNode;
590
647
 
591
648
  /**
592
- * Determine whether a node is an emphasis run (`*em*` / `**strong**`).
649
+ * Determines whether a node is an emphasis run (`*em*` / `**strong**`).
650
+ *
651
+ * @param node - The AST node to test
652
+ * @returns True if the node is an {@link EmphasisNode}; false otherwise
593
653
  *
594
654
  * @example
595
655
  * ```ts
@@ -599,11 +659,11 @@ export declare function isCodeSpanNode(node: MarkdownNode): node is CodeSpanNode
599
659
  export declare function isEmphasisNode(node: MarkdownNode): node is EmphasisNode;
600
660
 
601
661
  /**
602
- * Whether `character` is escapable by a leading backslash - the ASCII punctuation
662
+ * Checks whether `character` is escapable by a leading backslash the ASCII punctuation
603
663
  * markdown gives meaning to (so `\*` becomes `*` but `\.` stays `\.`).
604
664
  *
605
665
  * @param character - The single character after a backslash
606
- * @returns `true` when a backslash before it is an escape
666
+ * @returns True if a backslash before it is an escape; false otherwise
607
667
  *
608
668
  * @example
609
669
  * ```ts
@@ -614,12 +674,12 @@ export declare function isEmphasisNode(node: MarkdownNode): node is EmphasisNode
614
674
  export declare function isEscapable(character: string): boolean;
615
675
 
616
676
  /**
617
- * Whether `line` closes a fence opened by `marker` - the same fence character, a run
677
+ * Checks whether `line` closes a fence opened by `marker` the same fence character, a run
618
678
  * at least as long, and nothing else but surrounding whitespace.
619
679
  *
620
680
  * @param line - The candidate closing line
621
681
  * @param marker - The opening fence's marker run (from {@link extractFence})
622
- * @returns `true` when `line` closes the fence
682
+ * @returns True if `line` closes the fence; false otherwise
623
683
  *
624
684
  * @example
625
685
  * ```ts
@@ -629,11 +689,11 @@ export declare function isEscapable(character: string): boolean;
629
689
  export declare function isFenceClose(line: string, marker: string): boolean;
630
690
 
631
691
  /**
632
- * Whether `character` is a regex-`\s`-equivalent whitespace character - the
692
+ * Checks whether `character` is a regex-`\s`-equivalent whitespace character the
633
693
  * character class {@link isFenceClose}'s scan treats as surrounding padding.
634
694
  *
635
695
  * @param character - The single character to test, or `undefined` past the end of a line
636
- * @returns `true` when it is whitespace
696
+ * @returns True if it is whitespace; false otherwise
637
697
  *
638
698
  * @example
639
699
  * ```ts
@@ -643,11 +703,39 @@ export declare function isFenceClose(line: string, marker: string): boolean;
643
703
  */
644
704
  export declare function isFenceWhitespace(character: string | undefined): boolean;
645
705
 
646
- /** Determine whether a node is a heading block. */
706
+ /**
707
+ * Checks whether `character` is whitespace under the emphasis flanking rule — a space, a
708
+ * tab, or a newline.
709
+ *
710
+ * @param character - The character to test
711
+ * @returns True if the flanking rule counts it as whitespace; false otherwise
712
+ *
713
+ * @example
714
+ * ```ts
715
+ * isFlankingWhitespace(' ') // true
716
+ * isFlankingWhitespace('a') // false
717
+ * ```
718
+ */
719
+ export declare function isFlankingWhitespace(character: string): boolean;
720
+
721
+ /**
722
+ * Determines whether a node is a heading block.
723
+ *
724
+ * @param node - The AST node to test
725
+ * @returns True if the node is a {@link HeadingNode}; false otherwise
726
+ *
727
+ * @example
728
+ * ```ts
729
+ * isHeadingNode({ element: 'heading', level: 1, children: [] }) // true
730
+ * ```
731
+ */
647
732
  export declare function isHeadingNode(node: MarkdownNode): node is HeadingNode;
648
733
 
649
734
  /**
650
- * Determine whether a node is an image.
735
+ * Determines whether a node is an image.
736
+ *
737
+ * @param node - The AST node to test
738
+ * @returns True if the node is an {@link ImageNode}; false otherwise
651
739
  *
652
740
  * @example
653
741
  * ```ts
@@ -657,16 +745,16 @@ export declare function isHeadingNode(node: MarkdownNode): node is HeadingNode;
657
745
  export declare function isImageNode(node: MarkdownNode): node is ImageNode;
658
746
 
659
747
  /**
660
- * Determine whether an arbitrary value is a valid {@link InlineNode} - a text
748
+ * Determines whether an arbitrary value is a valid {@link InlineNode} a text
661
749
  * run, emphasis, code span, hard break, link, or image, recursively validated.
662
750
  *
663
751
  * @remarks
664
- * Total: never throws, even on cyclic or pathologically deep input - every
752
+ * Total: never throws, even on cyclic or pathologically deep input every
665
753
  * combinator involved (`unionOf`, `recordOf`, `arrayOf`, `lazyOf`) is
666
- * throw-contained per the `@orkestrel/contract` guard contract (AGENTS §14).
754
+ * throw-contained per the `@orkestrel/contract` guard contract.
667
755
  *
668
756
  * @param value - The value to test
669
- * @returns `true` when `value` is a well-formed {@link InlineNode}
757
+ * @returns True if `value` is a well-formed {@link InlineNode}; false otherwise
670
758
  *
671
759
  * @example
672
760
  * ```ts
@@ -679,7 +767,10 @@ export declare function isImageNode(node: MarkdownNode): node is ImageNode;
679
767
  export declare const isInlineNode: Guard<InlineNode>;
680
768
 
681
769
  /**
682
- * Determine whether a node is a GFM hard line break.
770
+ * Determines whether a node is a GFM hard line break.
771
+ *
772
+ * @param node - The AST node to test
773
+ * @returns True if the node is a {@link LineBreakNode}; false otherwise
683
774
  *
684
775
  * @example
685
776
  * ```ts
@@ -688,11 +779,24 @@ export declare const isInlineNode: Guard<InlineNode>;
688
779
  */
689
780
  export declare function isLineBreakNode(node: MarkdownNode): node is LineBreakNode;
690
781
 
691
- /** Determine whether a node is a link. */
782
+ /**
783
+ * Determines whether a node is a link.
784
+ *
785
+ * @param node - The AST node to test
786
+ * @returns True if the node is a {@link LinkNode}; false otherwise
787
+ *
788
+ * @example
789
+ * ```ts
790
+ * isLinkNode({ element: 'link', href: 'https://example.dev', children: [] }) // true
791
+ * ```
792
+ */
692
793
  export declare function isLinkNode(node: MarkdownNode): node is LinkNode;
693
794
 
694
795
  /**
695
- * Determine whether a node is a list block.
796
+ * Determines whether a node is a list block.
797
+ *
798
+ * @param node - The AST node to test
799
+ * @returns True if the node is a {@link ListNode}; false otherwise
696
800
  *
697
801
  * @example
698
802
  * ```ts
@@ -702,17 +806,17 @@ export declare function isLinkNode(node: MarkdownNode): node is LinkNode;
702
806
  export declare function isListNode(node: MarkdownNode): node is ListNode;
703
807
 
704
808
  /**
705
- * Determine whether an arbitrary value is a valid {@link MarkdownDocument} -
809
+ * Determines whether an arbitrary value is a valid {@link MarkdownDocument}
706
810
  * the parsed-AST root {@link parseDocument} returns, recursively
707
811
  * validated.
708
812
  *
709
813
  * @remarks
710
- * Total: never throws, even on cyclic or pathologically deep input - every
814
+ * Total: never throws, even on cyclic or pathologically deep input every
711
815
  * combinator involved (`recordOf`, `arrayOf`) is throw-contained per the
712
- * `@orkestrel/contract` guard contract (AGENTS §14).
816
+ * `@orkestrel/contract` guard contract.
713
817
  *
714
818
  * @param value - The value to test
715
- * @returns `true` when `value` is a well-formed {@link MarkdownDocument}
819
+ * @returns True if `value` is a well-formed {@link MarkdownDocument}; false otherwise
716
820
  *
717
821
  * @example
718
822
  * ```ts
@@ -725,19 +829,19 @@ export declare function isListNode(node: MarkdownNode): node is ListNode;
725
829
  export declare const isMarkdownDocument: Guard<MarkdownDocument>;
726
830
 
727
831
  /**
728
- * Determine whether an arbitrary value is a valid {@link MarkdownNode} - the
832
+ * Determines whether an arbitrary value is a valid {@link MarkdownNode} the
729
833
  * {@link MarkdownDocument} root, a {@link BlockNode}, a {@link ListItemNode}, or
730
834
  * an {@link InlineNode}, recursively validated.
731
835
  *
732
836
  * @remarks
733
- * Total: never throws, even on cyclic or pathologically deep input - every
837
+ * Total: never throws, even on cyclic or pathologically deep input every
734
838
  * combinator involved (`unionOf`, `recordOf`, `arrayOf`, `lazyOf`) is
735
- * throw-contained per the `@orkestrel/contract` guard contract (AGENTS §14).
839
+ * throw-contained per the `@orkestrel/contract` guard contract.
736
840
  * A list item's shape is inlined here (and in {@link isBlockNode}) rather than
737
- * named separately - it is used at exactly these two sites.
841
+ * named separately it is used at exactly these two sites.
738
842
  *
739
843
  * @param value - The value to test
740
- * @returns `true` when `value` is a well-formed {@link MarkdownNode}
844
+ * @returns True if `value` is a well-formed {@link MarkdownNode}; false otherwise
741
845
  *
742
846
  * @example
743
847
  * ```ts
@@ -750,7 +854,10 @@ export declare const isMarkdownDocument: Guard<MarkdownDocument>;
750
854
  export declare const isMarkdownNode: Guard<MarkdownNode>;
751
855
 
752
856
  /**
753
- * Determine whether a node is a paragraph block.
857
+ * Determines whether a node is a paragraph block.
858
+ *
859
+ * @param node - The AST node to test
860
+ * @returns True if the node is a {@link ParagraphNode}; false otherwise
754
861
  *
755
862
  * @example
756
863
  * ```ts
@@ -760,11 +867,11 @@ export declare const isMarkdownNode: Guard<MarkdownNode>;
760
867
  export declare function isParagraphNode(node: MarkdownNode): node is ParagraphNode;
761
868
 
762
869
  /**
763
- * Whether `line` is a blockquote line (`>` optionally indented up to three spaces) -
870
+ * Checks whether `line` is a blockquote line (`>` optionally indented up to three spaces)
764
871
  * its content is de-quoted by {@link stripQuote}.
765
872
  *
766
873
  * @param line - The candidate line
767
- * @returns `true` when the line begins a blockquote
874
+ * @returns True if the line begins a blockquote; false otherwise
768
875
  *
769
876
  * @example
770
877
  * ```ts
@@ -773,17 +880,27 @@ export declare function isParagraphNode(node: MarkdownNode): node is ParagraphNo
773
880
  */
774
881
  export declare function isQuote(line: string): boolean;
775
882
 
776
- /** Determine whether a node is a GFM table block. */
883
+ /**
884
+ * Determines whether a node is a GFM table block.
885
+ *
886
+ * @param node - The AST node to test
887
+ * @returns True if the node is a {@link TableNode}; false otherwise
888
+ *
889
+ * @example
890
+ * ```ts
891
+ * isTableNode({ element: 'table', header: [], rows: [], align: [] }) // true
892
+ * ```
893
+ */
777
894
  export declare function isTableNode(node: MarkdownNode): node is TableNode;
778
895
 
779
896
  /**
780
- * Whether the pair (`header`, `delimiter`) opens a GFM table - `delimiter` is a row of
897
+ * Checks whether the pair (`header`, `delimiter`) opens a GFM table `delimiter` is a row of
781
898
  * `|`-separated cells each matching `:?-+:?`, the GFM rule that a table requires a
782
- * header row IMMEDIATELY followed by a delimiter row.
899
+ * header row immediately followed by a delimiter row.
783
900
  *
784
901
  * @param header - The candidate header line
785
902
  * @param delimiter - The line after it (the candidate delimiter)
786
- * @returns `true` when the two lines open a table
903
+ * @returns True if the two lines open a table; false otherwise
787
904
  *
788
905
  * @example
789
906
  * ```ts
@@ -793,7 +910,10 @@ export declare function isTableNode(node: MarkdownNode): node is TableNode;
793
910
  export declare function isTableStart(header: string, delimiter: string | undefined): boolean;
794
911
 
795
912
  /**
796
- * Determine whether a node is a plain text run.
913
+ * Determines whether a node is a plain text run.
914
+ *
915
+ * @param node - The AST node to test
916
+ * @returns True if the node is a {@link TextNode}; false otherwise
797
917
  *
798
918
  * @example
799
919
  * ```ts
@@ -803,12 +923,12 @@ export declare function isTableStart(header: string, delimiter: string | undefin
803
923
  export declare function isTextNode(node: MarkdownNode): node is TextNode;
804
924
 
805
925
  /**
806
- * Whether `line` is a thematic break (horizontal rule) - three or more of the SAME
926
+ * Checks whether `line` is a thematic break (horizontal rule) three or more of the same
807
927
  * marker `-`, `*`, or `_` (optionally space-separated) and nothing else (`---`,
808
928
  * `***`, `___`, `- - -`).
809
929
  *
810
930
  * @param line - The candidate line
811
- * @returns `true` when the line is a thematic break
931
+ * @returns True if the line is a thematic break; false otherwise
812
932
  *
813
933
  * @example
814
934
  * ```ts
@@ -818,7 +938,10 @@ export declare function isTextNode(node: MarkdownNode): node is TextNode;
818
938
  export declare function isThematicBreak(line: string): boolean;
819
939
 
820
940
  /**
821
- * Determine whether a node is a thematic break (horizontal rule) block.
941
+ * Determines whether a node is a thematic break (horizontal rule) block.
942
+ *
943
+ * @param node - The AST node to test
944
+ * @returns True if the node is a {@link ThematicBreakNode}; false otherwise
822
945
  *
823
946
  * @example
824
947
  * ```ts
@@ -827,21 +950,6 @@ export declare function isThematicBreak(line: string): boolean;
827
950
  */
828
951
  export declare function isThematicBreakNode(node: MarkdownNode): node is ThematicBreakNode;
829
952
 
830
- /**
831
- * Whether `character` is an inline whitespace character (space / tab / newline) - the
832
- * emphasis flanking rule's space test.
833
- *
834
- * @param character - The character to test
835
- * @returns `true` when it is inline whitespace
836
- *
837
- * @example
838
- * ```ts
839
- * isWhitespace(' ') // true
840
- * isWhitespace('a') // false
841
- * ```
842
- */
843
- export declare function isWhitespace(character: string): boolean;
844
-
845
953
  /**
846
954
  * Joins offset-bearing markdown sources while mapping a separator to the original
847
955
  * region between adjacent mapped sources.
@@ -858,13 +966,16 @@ export declare function isWhitespace(character: string): boolean;
858
966
  */
859
967
  export declare function joinSources(sources: readonly MarkdownSource[], separator: string): MarkdownSource;
860
968
 
861
- /** A GFM hard line break - two or more trailing spaces before a newline. */
969
+ /**
970
+ * Represents a GFM hard line break — two or more trailing spaces before a newline in
971
+ * markdown source, a `br` element in HTML.
972
+ */
862
973
  export declare interface LineBreakNode {
863
974
  readonly element: 'break';
864
975
  }
865
976
 
866
977
  /**
867
- * The shape of a {@link LineBreakNode} - a GFM hard line-break leaf.
978
+ * Describes the shape of a {@link LineBreakNode} a GFM hard line-break leaf.
868
979
  *
869
980
  * @example
870
981
  * ```ts
@@ -875,42 +986,75 @@ export declare interface LineBreakNode {
875
986
  * lineBreak.is({ element: 'break' }) // true
876
987
  * ```
877
988
  */
878
- export declare const lineBreakShape: ObjectShape<{
879
- element: LiteralShape<readonly ["break"]>;
989
+ export declare const lineBreakShape: ObjectShape< {
990
+ element: LiteralShape<readonly ["break"]>;
880
991
  }, false>;
881
992
 
882
993
  /**
883
- * An inline link - `[text](href)`. `children` are the inline nodes of the link text.
994
+ * Represents the located syntax bounds of one `[text](href)` link the value the inline phase's
995
+ * link locator returns for a balanced label followed by a destination.
996
+ */
997
+ export declare interface LinkBounds {
998
+ /** Holds the index of the label's closing `]`. */
999
+ readonly close: number;
1000
+ /** Holds the index one past the destination's closing `)`, exclusive. */
1001
+ readonly end: number;
1002
+ }
1003
+
1004
+ /**
1005
+ * Represents an inline link — `[text](href)`. `children` are the inline nodes of the link text.
884
1006
  * At render, html's floor removes a refused `href` attribute and the link keeps its
885
1007
  * text; {@link htmlToMarkdown} instead stores a refused destination as `''`.
886
1008
  */
887
1009
  export declare interface LinkNode {
888
1010
  readonly element: 'link';
889
- /** The link destination (sanitized + attribute-escaped at render). */
1011
+ /** Holds the link destination (sanitized + attribute-escaped at render). */
890
1012
  readonly href: string;
891
- /** The inline content of the link text. */
1013
+ /** Holds the inline content of the link text. */
892
1014
  readonly children: readonly InlineNode[];
893
1015
  }
894
1016
 
895
1017
  /**
896
- * The parsed parts of a single list-item line - the value the block phase's
1018
+ * Represents the scanned result of one `[text](href)` link the node the inline phase's link
1019
+ * scanner built from {@link LinkBounds} and where the scan resumes.
1020
+ */
1021
+ export declare interface LinkScan {
1022
+ /** Holds the scanned link, its text already scanned into inline children. */
1023
+ readonly node: LinkNode;
1024
+ /** Holds the index one past the destination's closing `)`, exclusive. */
1025
+ readonly end: number;
1026
+ }
1027
+
1028
+ /**
1029
+ * Represents the result of collecting one list — the node the construct scanner built and where
1030
+ * the block phase resumes.
1031
+ */
1032
+ export declare interface ListCollection {
1033
+ /** Holds the collected list. */
1034
+ readonly node: ListNode;
1035
+ /** Holds the index of the first line after the list. */
1036
+ readonly next: number;
1037
+ }
1038
+
1039
+ /**
1040
+ * Represents the parsed parts of a single list-item line — the value the block phase's
897
1041
  * list detector returns for a `-` / `*` / `+` bullet or a `1.` / `1)` ordinal line.
898
1042
  */
899
1043
  export declare interface ListItemMatch {
900
- /** `true` for an ordered (`1.` / `1)`) item, `false` for a bullet (`-` / `*` / `+`). */
1044
+ /** Holds `true` for an ordered (`1.` / `1)`) item, `false` for a bullet (`-` / `*` / `+`). */
901
1045
  readonly ordered: boolean;
902
- /** The ordinal of an ordered item (its number); `1` for a bullet. */
1046
+ /** Holds the ordinal of an ordered item (its number); `1` for a bullet. */
903
1047
  readonly start: number;
904
- /** The item's text after the marker. */
1048
+ /** Holds the item's text after the marker. */
905
1049
  readonly content: string;
906
- /** The leading-space indent of the marker. */
1050
+ /** Holds the leading-space indent of the marker. */
907
1051
  readonly indent: number;
908
- /** The full marker width (indent + bullet/ordinal + the following space) - the continuation indent. */
1052
+ /** Holds the full marker width (indent + bullet/ordinal + the following space) the continuation indent. */
909
1053
  readonly marker: number;
910
1054
  }
911
1055
 
912
1056
  /**
913
- * The shape of {@link ListItemMatch} - the parsed parts of a single list-item
1057
+ * Describes the shape of {@link ListItemMatch} the parsed parts of a single list-item
914
1058
  * line the block phase's list detector returns. Fully non-recursive (no
915
1059
  * nested node fields), so every field shapes directly.
916
1060
  *
@@ -923,39 +1067,39 @@ export declare interface ListItemMatch {
923
1067
  * listItemParts.is({ ordered: false, start: 1, content: 'hi', indent: 0, marker: 2 }) // true
924
1068
  * ```
925
1069
  */
926
- export declare const listItemMatchShape: ObjectShape<{
927
- ordered: BooleanShape;
928
- start: NumberShape;
929
- content: StringShape;
930
- indent: NumberShape;
931
- marker: NumberShape;
1070
+ export declare const listItemMatchShape: ObjectShape< {
1071
+ ordered: BooleanShape;
1072
+ start: NumberShape;
1073
+ content: StringShape;
1074
+ indent: NumberShape;
1075
+ marker: NumberShape;
932
1076
  }, false>;
933
1077
 
934
- /** One item of a {@link ListNode} - `children` the block content of the item (typically one paragraph, plus any nested list). */
1078
+ /** Represents one item of a {@link ListNode} `children` the block content of the item (typically one paragraph, plus any nested list). */
935
1079
  export declare interface ListItemNode {
936
1080
  readonly element: 'listItem';
937
- /** The block content of the list item (its text as a paragraph, plus any nested list). */
1081
+ /** Holds the block content of the list item (its text as a paragraph, plus any nested list). */
938
1082
  readonly children: readonly BlockNode[];
939
1083
  }
940
1084
 
941
1085
  /**
942
- * A list - bulleted (`-` / `*` / `+`, `ordered: false`) or numbered (`1.` / `1)`,
1086
+ * Represents a list bulleted (`-` / `*` / `+`, `ordered: false`) or numbered (`1.` / `1)`,
943
1087
  * `ordered: true`). `start` is the first ordinal of an ordered list (usually `1`).
944
1088
  * Nesting is expressed by a {@link ListNode} appearing in a {@link ListItemNode}'s
945
1089
  * `children`.
946
1090
  */
947
1091
  export declare interface ListNode {
948
1092
  readonly element: 'list';
949
- /** `true` for an ordered (numbered) list (→ `<ol>`); `false` for a bulleted list (→ `<ul>`). */
1093
+ /** Holds `true` for an ordered (numbered) list (→ `<ol>`); `false` for a bulleted list (→ `<ul>`). */
950
1094
  readonly ordered: boolean;
951
- /** The starting ordinal of an ordered list (the first item's number); `1` for a bulleted list. */
1095
+ /** Holds the starting ordinal of an ordered list (the first item's number); `1` for a bulleted list. */
952
1096
  readonly start: number;
953
- /** The list's items, in order. */
1097
+ /** Holds the list's items, in order. */
954
1098
  readonly items: readonly ListItemNode[];
955
1099
  }
956
1100
 
957
1101
  /**
958
- * Locates an emphasis run at `start` (`*` / `_`, doubled for strong) - finds the nearest
1102
+ * Locates an emphasis run at `start` (`*` / `_`, doubled for strong) finds the nearest
959
1103
  * matching closing run of the same marker + width while skipping complete nested
960
1104
  * runs from the other marker family, and requires non-space immediately inside both
961
1105
  * delimiters (the CommonMark flanking simplification that blocks `* x *`). Returns
@@ -972,15 +1116,10 @@ export declare interface ListNode {
972
1116
  * locateEmphasis('*em*', 0, 4) // { strong: false, open: 1, close: 3, end: 4 }
973
1117
  * ```
974
1118
  */
975
- export declare function locateEmphasis(source: string, start: number, to: number): {
976
- readonly strong: boolean;
977
- readonly open: number;
978
- readonly close: number;
979
- readonly end: number;
980
- } | undefined;
1119
+ export declare function locateEmphasis(source: string, start: number, to: number): EmphasisBounds | undefined;
981
1120
 
982
1121
  /**
983
- * Locates a link `[text](href)` at `start` - the text runs to a BALANCED `]`, then `(`
1122
+ * Locates a link `[text](href)` at `start` the text runs to a balanced `]`, then `(`
984
1123
  * must immediately follow and the destination runs to the matching `)` (both respect
985
1124
  * nested delimiters + escapes). Returns the label close and syntax end, or `undefined` when the shape
986
1125
  * does not hold (it then degrades to a literal `[`).
@@ -995,26 +1134,23 @@ export declare function locateEmphasis(source: string, start: number, to: number
995
1134
  * locateLink('[text](url)', 0, 11) // { close: 5, end: 11 }
996
1135
  * ```
997
1136
  */
998
- export declare function locateLink(source: string, start: number, to: number): {
999
- readonly close: number;
1000
- readonly end: number;
1001
- } | undefined;
1137
+ export declare function locateLink(source: string, start: number, to: number): LinkBounds | undefined;
1002
1138
 
1003
1139
  /**
1004
- * A stateful, parsed markdown document - wraps a typed {@link MarkdownDocument} AST
1140
+ * Wraps a typed {@link MarkdownDocument} AST as a stateful, parsed markdown document
1005
1141
  * with the query (`find` / `filter` / `reduce` / iteration), rewrite (`map`), fold, and
1006
1142
  * streaming operations {@link MarkdownInterface} declares.
1007
1143
  *
1008
1144
  * @remarks
1009
1145
  * - **Construction.** Given a `string`, the constructor runs {@link parseProvenance} (the
1010
- * block phase then the inline phase) once, keeping the AST and a COPY of the span map
1011
- * that parse recorded. Given a {@link MarkdownDocument}, the document is adopted AS-IS
1012
- * and is NOT re-validated - gate an untrusted value with `isMarkdownDocument` first.
1013
- * - **Provenance.** {@link span} reads the region of the ORIGINAL constructor string a
1146
+ * block phase then the inline phase) once, keeping the AST and a copy of the span map
1147
+ * that parse recorded. Given a {@link MarkdownDocument}, the document is adopted as-is
1148
+ * and is not re-validated gate an untrusted value with `isMarkdownDocument` first.
1149
+ * - **Provenance.** {@link span} reads the region of the original constructor string a
1014
1150
  * node was produced from, and it is handle-relative: a string-constructed handle exposes
1015
1151
  * the regions of the nodes it parsed, an adopted document exposes none, and a node from
1016
1152
  * another handle reports `undefined` here whatever that handle reports. Each call
1017
- * returns a fresh value. A node reports the region THIS handle holds for its identity,
1153
+ * returns a fresh value. A node reports the region this handle holds for its identity,
1018
1154
  * else the region of the direct input a rewrite named for it, else `undefined`: a text
1019
1155
  * run the parse joined from adjacent scanner output reports the region enclosing its
1020
1156
  * parts, and only a rewrite output that holds no region of its own and was assembled
@@ -1022,29 +1158,28 @@ export declare function locateLink(source: string, start: number, to: number): {
1022
1158
  * {@link map} carries provenance across the rewrite: an unchanged node keeps its
1023
1159
  * region, a one-source replacement takes the region of the node it replaced, and a
1024
1160
  * rebuilt parent takes its original's.
1025
- * - **Immutable.** {@link map} never mutates the stored AST - it returns a NEW `Markdown`
1161
+ * - **Immutable.** {@link map} never mutates the stored AST it returns a new `Markdown`
1026
1162
  * instance; the document root invariant (`element: 'document'`) always holds. An
1027
1163
  * identity rewrite still returns a new handle, over the same document tree.
1028
1164
  * - **Traversal order.** {@link walk} and the `find` / `filter` / `reduce` queries built
1029
- * on it walk the AST depth-first, pre-order, root-inclusive (via {@link walkNodes});
1030
- * `stream` is shallow - only the document's direct block children.
1165
+ * on it walk the AST depth-first, pre-order, root-inclusive (through {@link walkNodes});
1166
+ * `stream` is shallow only the document's direct block children.
1031
1167
  *
1032
- * @example
1168
+ * @example Construct from a string and narrow with a guard
1033
1169
  * ```ts
1034
- * import { Markdown, isHeadingNode, renderMarkdown } from '@src/core'
1170
+ * import { Markdown, isHeadingNode } from '@orkestrel/markdown'
1035
1171
  *
1036
1172
  * const markdown = new Markdown('# Title\n\nA **bold** [link](https://x.dev).')
1037
- * const heading = markdown.find(isHeadingNode) // the HeadingNode, or undefined
1038
- * const shouted = markdown.map((node) =>
1039
- * node.element === 'text' ? { element: 'text', value: node.value.toUpperCase() } : node,
1040
- * )
1041
- * renderMarkdown(shouted.document) // '# TITLE\n\nA **BOLD** [LINK](https://x.dev).'
1173
+ * markdown.document.children[0] // { element: 'heading', level: 1, children: [...] }
1174
+ *
1175
+ * const heading = markdown.find(isHeadingNode) // HeadingNode | undefined, narrowed
1176
+ * if (heading !== undefined) heading.level // number — narrowed to HeadingNode
1042
1177
  * ```
1043
1178
  */
1044
1179
  export declare class Markdown implements MarkdownInterface {
1045
1180
  #private;
1046
1181
  constructor(input: string | MarkdownDocument);
1047
- /** The stored {@link MarkdownDocument} AST root. */
1182
+ /** Holds the stored {@link MarkdownDocument} AST root. */
1048
1183
  get document(): MarkdownDocument;
1049
1184
  /**
1050
1185
  * Reads the region of the original markdown string a node of this handle's tree was
@@ -1065,7 +1200,7 @@ export declare class Markdown implements MarkdownInterface {
1065
1200
  */
1066
1201
  span(node: MarkdownNode): MarkdownSpan | undefined;
1067
1202
  /**
1068
- * THE deep traversal - a lazy, depth-first, pre-order, root-inclusive generator
1203
+ * Returns the deep traversal a lazy, depth-first, pre-order, root-inclusive generator
1069
1204
  * over every {@link MarkdownNode} in the document. `find` / `filter` / `reduce`
1070
1205
  * all iterate this single traversal.
1071
1206
  *
@@ -1098,11 +1233,11 @@ export declare class Markdown implements MarkdownInterface {
1098
1233
  map(rewrite: MarkdownRewriteHandler): MarkdownInterface;
1099
1234
  /** Folds the AST depth-first, pre-order into an accumulator. */
1100
1235
  reduce<T>(callback: (accumulator: T, node: MarkdownNode) => T, initial: T): T;
1101
- /** Runs a total catamorphism over the document using a {@link MarkdownHandlers} table. */
1102
- fold<T>(handlers: MarkdownHandlers<T>): T;
1236
+ /** Runs a total catamorphism over the document using a {@link MarkdownHandlerMap} table. */
1237
+ fold<T>(handlers: MarkdownHandlerMap<T>): T;
1103
1238
  /**
1104
- * A web-standard {@link ReadableStream} over the document's top-level block nodes
1105
- * (shallow, source order) - a fresh, pull-based source per call: one block is
1239
+ * Returns a web-standard {@link ReadableStream} over the document's top-level block nodes
1240
+ * (shallow, source order) a fresh, pull-based source per call: one block is
1106
1241
  * enqueued per `pull`, so a slow reader's backpressure is respected. Cancellable,
1107
1242
  * async-iterable wherever the platform supports it (Node, Deno), and pipeable
1108
1243
  * through any {@link TransformStream} / {@link WritableStream}.
@@ -1116,7 +1251,7 @@ export declare class Markdown implements MarkdownInterface {
1116
1251
  * }
1117
1252
  *
1118
1253
  * // Node / Deno / Firefox support async iteration of ReadableStream natively;
1119
- * // other environments should use the reader loop above instead.
1254
+ * // other environments use the reader loop shown earlier.
1120
1255
  * for await (const block of markdown.stream()) {
1121
1256
  * console.log(block)
1122
1257
  * }
@@ -1125,28 +1260,35 @@ export declare class Markdown implements MarkdownInterface {
1125
1260
  stream(): ReadableStream<BlockNode>;
1126
1261
  }
1127
1262
 
1128
- /** One projected table cell - the inline content and alignment of a `th` / `td`. */
1263
+ /**
1264
+ * Represents one projected table cell — the inline content and alignment of a `th` / `td`.
1265
+ *
1266
+ * @remarks
1267
+ * {@link htmlToMarkdown} derives a projected table's header row from the source HTML
1268
+ * structure rather than from any flag a cell carries: the header is the first
1269
+ * `th`-bearing row, and every row after it becomes a body row.
1270
+ */
1129
1271
  export declare interface MarkdownCell {
1130
- /** The alignment the cell's `align` attribute declared; `undefined` when it declared none. */
1272
+ /** Holds the alignment the cell's `align` attribute declared; `undefined` when it declared none. */
1131
1273
  readonly align: TableAlign | undefined;
1132
- /** The cell's inline content - a table cell is inline-only, so block content flattens to text. */
1274
+ /** Holds the cell's inline content a table cell is inline-only, so block content flattens to text. */
1133
1275
  readonly inlines: readonly InlineNode[];
1134
1276
  }
1135
1277
 
1136
1278
  /**
1137
- * Pairs a rewritten value with the input node each rewritten node was produced from -
1279
+ * Pairs a rewritten value with the input node each rewritten node was produced from
1138
1280
  * what `rewriteDocument` returns, so provenance survives a rewrite instead of ending at
1139
1281
  * it. `T` is the rewritten value: the document for a whole-document rewrite.
1140
1282
  *
1141
1283
  * @remarks
1142
- * `derivations` is keyed by the nodes of the OUTPUT, and each entry names the DIRECT
1284
+ * `derivations` is keyed by the nodes of the output, and each entry names the direct
1143
1285
  * input the rewrite drew that output from. {@link MarkdownInterface.map} resolves each
1144
1286
  * output node against the source handle's own spans in a fixed order, and follows no
1145
1287
  * second derivation edge:
1146
1288
  *
1147
- * - the output identity's OWN span in the source handle wins, whatever the map says,
1148
- * so an identity the rewrite reused - one node returned for several inputs, or a
1149
- * node the handler moved elsewhere in the tree - keeps the region it already had;
1289
+ * - the output identity's own span in the source handle wins, whatever the map says,
1290
+ * so an identity the rewrite reused one node returned for several inputs, or a
1291
+ * node the handler moved elsewhere in the tree keeps the region it already had;
1150
1292
  * - otherwise the span of the direct input the entry names, where that input has one;
1151
1293
  * - otherwise none. Where the output identity holds no region of its own, a node mapped
1152
1294
  * to `undefined`, a node whose direct input has no span, and a node with no entry at
@@ -1163,39 +1305,39 @@ derivations: ReadonlyMap<MarkdownNode, MarkdownNode | undefined>
1163
1305
  ];
1164
1306
 
1165
1307
  /**
1166
- * The root of a parsed markdown AST - the ordered block children of the whole
1308
+ * Represents the root of a parsed markdown AST the ordered block children of the whole
1167
1309
  * document. The value {@link MarkdownInterface.document} holds.
1168
1310
  */
1169
1311
  export declare interface MarkdownDocument {
1170
1312
  readonly element: 'document';
1171
- /** The document's top-level block nodes, in source order. */
1313
+ /** Holds the document's top-level block nodes, in source order. */
1172
1314
  readonly children: readonly BlockNode[];
1173
1315
  }
1174
1316
 
1175
1317
  /**
1176
- * A fold handler for one AST element - receives the node and its children
1177
- * ALREADY folded to `T`, and produces the node's own `T`. The building block of a
1178
- * {@link MarkdownHandlers} catamorphism table.
1318
+ * Represents a fold handler for one AST element receives the node and its children
1319
+ * already folded to `T`, and produces the node's own `T`. The building block of a
1320
+ * {@link MarkdownHandlerMap} catamorphism table.
1179
1321
  */
1180
1322
  export declare type MarkdownHandler<TNode, T> = (node: TNode, children: readonly T[]) => T;
1181
1323
 
1182
1324
  /**
1183
- * The total catamorphism table for {@link MarkdownInterface.fold} - one
1325
+ * Represents the total catamorphism table for {@link MarkdownInterface.fold} one
1184
1326
  * {@link MarkdownHandler} per AST element, keyed by its `element` discriminant. Every
1185
1327
  * key is required: a fold is total over the AST, so there is no element it can skip.
1186
1328
  */
1187
- export declare interface MarkdownHandlers<T> {
1329
+ export declare interface MarkdownHandlerMap<T> {
1188
1330
  /** Folds a {@link MarkdownDocument} root from its already-folded block children. */
1189
1331
  readonly document: MarkdownHandler<MarkdownDocument, T>;
1190
1332
  /** Folds a {@link HeadingNode} from its already-folded inline children. */
1191
1333
  readonly heading: MarkdownHandler<HeadingNode, T>;
1192
1334
  /** Folds a {@link ParagraphNode} from its already-folded inline children. */
1193
1335
  readonly paragraph: MarkdownHandler<ParagraphNode, T>;
1194
- /** Folds a {@link ThematicBreakNode} (leaf - always called with an empty children list). */
1336
+ /** Folds a {@link ThematicBreakNode} (leaf always called with an empty children list). */
1195
1337
  readonly thematicBreak: MarkdownHandler<ThematicBreakNode, T>;
1196
1338
  /** Folds a {@link BlockquoteNode} from its already-folded block children. */
1197
1339
  readonly blockquote: MarkdownHandler<BlockquoteNode, T>;
1198
- /** Folds a {@link CodeBlockNode} (leaf - always called with an empty children list). */
1340
+ /** Folds a {@link CodeBlockNode} (leaf always called with an empty children list). */
1199
1341
  readonly codeBlock: MarkdownHandler<CodeBlockNode, T>;
1200
1342
  /** Folds a {@link ListNode} from its already-folded item children. */
1201
1343
  readonly list: MarkdownHandler<ListNode, T>;
@@ -1203,19 +1345,19 @@ export declare interface MarkdownHandlers<T> {
1203
1345
  readonly listItem: MarkdownHandler<ListItemNode, T>;
1204
1346
  /**
1205
1347
  * Folds a {@link TableNode} from its cells' already-folded inline nodes, flattened
1206
- * to ONE folded `T` per inline node - header cells first (column order), then body
1207
- * rows' cells (row order, then column order). It is NOT a leaf: recover cell
1348
+ * to one folded `T` per inline node header cells first (column order), then body
1349
+ * rows' cells (row order, then column order). It is not a leaf: recover cell
1208
1350
  * boundaries from `node.header[c].length` / `node.rows[r][c].length` against the
1209
1351
  * flat `children` list.
1210
1352
  */
1211
1353
  readonly table: MarkdownHandler<TableNode, T>;
1212
- /** Folds a {@link TextNode} (leaf - always called with an empty children list). */
1354
+ /** Folds a {@link TextNode} (leaf always called with an empty children list). */
1213
1355
  readonly text: MarkdownHandler<TextNode, T>;
1214
1356
  /** Folds an {@link EmphasisNode} from its already-folded inline children. */
1215
1357
  readonly emphasis: MarkdownHandler<EmphasisNode, T>;
1216
- /** Folds a {@link CodeSpanNode} (leaf - always called with an empty children list). */
1358
+ /** Folds a {@link CodeSpanNode} (leaf always called with an empty children list). */
1217
1359
  readonly codeSpan: MarkdownHandler<CodeSpanNode, T>;
1218
- /** Folds a {@link LineBreakNode} (leaf - always called with an empty children list). */
1360
+ /** Folds a {@link LineBreakNode} (leaf always called with an empty children list). */
1219
1361
  readonly break: MarkdownHandler<LineBreakNode, T>;
1220
1362
  /** Folds a {@link LinkNode} from its already-folded inline children. */
1221
1363
  readonly link: MarkdownHandler<LinkNode, T>;
@@ -1224,20 +1366,20 @@ export declare interface MarkdownHandlers<T> {
1224
1366
  }
1225
1367
 
1226
1368
  /**
1227
- * A stateful, parsed markdown document: the typed {@link MarkdownDocument} AST plus
1369
+ * Represents a stateful, parsed markdown document: the typed {@link MarkdownDocument} AST plus
1228
1370
  * the query, rewrite, and fold operations over it.
1229
1371
  *
1230
1372
  * @remarks
1231
- * - **Immutable.** {@link MarkdownInterface.map} never mutates the stored AST - it
1232
- * returns a NEW {@link MarkdownInterface} instance; the document root invariant
1373
+ * - **Immutable.** {@link MarkdownInterface.map} never mutates the stored AST it
1374
+ * returns a new {@link MarkdownInterface} instance; the document root invariant
1233
1375
  * (`element: 'document'`) always holds.
1234
1376
  * - **Traversal order.** `walk` / `find` / `filter` / `reduce` walk the AST
1235
- * depth-first, pre-order, root-inclusive; `stream` is shallow - only the
1377
+ * depth-first, pre-order, root-inclusive; `stream` is shallow only the
1236
1378
  * document's direct block children.
1237
1379
  * - **`stream`.** Returns a web-standard {@link ReadableStream} over the top-level
1238
- * blocks - a fresh, pull-based source per call: exactly one block is enqueued per
1380
+ * blocks a fresh, pull-based source per call: exactly one block is enqueued per
1239
1381
  * `pull`, so a slow consumer's backpressure is respected and no work happens ahead
1240
- * of demand. Cancellable via the returned stream's own `cancel()`, async-iterable
1382
+ * of demand. Cancellable through the returned stream's own `cancel()`, async-iterable
1241
1383
  * wherever the platform supports it (Node, Deno, and browsers that ship the
1242
1384
  * proposal), and pipeable through any {@link TransformStream} / {@link WritableStream}.
1243
1385
  * - **The surface.** `document` (the AST root), `walk` (the deep traversal), `find` /
@@ -1246,10 +1388,10 @@ export declare interface MarkdownHandlers<T> {
1246
1388
  * total catamorphism), and `stream` (the shallow, backpressured top-level source).
1247
1389
  */
1248
1390
  export declare interface MarkdownInterface {
1249
- /** The stored {@link MarkdownDocument} AST root. */
1391
+ /** Holds the stored {@link MarkdownDocument} AST root. */
1250
1392
  readonly document: MarkdownDocument;
1251
1393
  /**
1252
- * THE deep traversal - a lazy, depth-first, pre-order, root-inclusive
1394
+ * Returns the deep traversal a lazy, depth-first, pre-order, root-inclusive
1253
1395
  * {@link Generator} over every {@link MarkdownNode} in the document. The sync
1254
1396
  * `for (const node of markdown.walk())` surface is also consumable by
1255
1397
  * `for await (const node of markdown.walk())` (JavaScript accepts a sync
@@ -1258,11 +1400,17 @@ export declare interface MarkdownInterface {
1258
1400
  * is shallow (top-level blocks only) and backpressure-respecting.
1259
1401
  */
1260
1402
  walk(): Generator<MarkdownNode>;
1261
- /** Finds the first node (depth-first, pre-order) narrowed by a type guard. */
1403
+ /**
1404
+ * Finds the first node (depth-first, pre-order) narrowed by a type guard, and returns
1405
+ * `undefined` when no node matches; a second overload takes a plain predicate.
1406
+ */
1262
1407
  find<T extends MarkdownNode>(guard: (node: MarkdownNode) => node is T): T | undefined;
1263
1408
  /** Finds the first node (depth-first, pre-order) matching a predicate. */
1264
1409
  find(predicate: (node: MarkdownNode) => boolean): MarkdownNode | undefined;
1265
- /** Collects every node (depth-first, pre-order) narrowed by a type guard. */
1410
+ /**
1411
+ * Collects every node (depth-first, pre-order) narrowed by a type guard; a second
1412
+ * overload takes a plain predicate.
1413
+ */
1266
1414
  filter<T extends MarkdownNode>(guard: (node: MarkdownNode) => node is T): readonly T[];
1267
1415
  /** Collects every node (depth-first, pre-order) matching a predicate. */
1268
1416
  filter(predicate: (node: MarkdownNode) => boolean): readonly MarkdownNode[];
@@ -1275,11 +1423,11 @@ export declare interface MarkdownInterface {
1275
1423
  *
1276
1424
  * @remarks
1277
1425
  * Provenance is per handle and per node identity, so a node reports a region only
1278
- * where THIS handle holds coordinates for it. A handle constructed from an adopted
1426
+ * where this handle holds coordinates for it. A handle constructed from an adopted
1279
1427
  * {@link MarkdownDocument} reports `undefined` for every node: it parsed no string,
1280
- * so no coordinates exist to report. A text run the PARSE joined from adjacent
1428
+ * so no coordinates exist to report. A text run the parse joined from adjacent
1281
1429
  * scanner output reports the region enclosing its parts rather than `undefined`;
1282
- * only a REWRITE output that holds no region of its own and was assembled from
1430
+ * only a rewrite output that holds no region of its own and was assembled from
1283
1431
  * separate source nodes reports `undefined`. The region a node does report is the
1284
1432
  * original source it was produced from, which can include syntax its value drops
1285
1433
  * and characters that normalization removed. Each call returns a fresh value rather
@@ -1288,33 +1436,33 @@ export declare interface MarkdownInterface {
1288
1436
  span(node: MarkdownNode): MarkdownSpan | undefined;
1289
1437
  /** Rewrites the AST bottom-up (copy-on-write) and returns a new {@link MarkdownInterface}. */
1290
1438
  map(rewrite: MarkdownRewriteHandler): MarkdownInterface;
1291
- /** Folds the AST depth-first, pre-order into an accumulator. */
1439
+ /** Folds the AST depth-first, pre-order into an accumulator through a reducer callback. */
1292
1440
  reduce<T>(callback: (accumulator: T, node: MarkdownNode) => T, initial: T): T;
1293
- /** Runs a total catamorphism over the document using a {@link MarkdownHandlers} table. */
1294
- fold<T>(handlers: MarkdownHandlers<T>): T;
1441
+ /** Runs a total catamorphism over the document using a {@link MarkdownHandlerMap} table. */
1442
+ fold<T>(handlers: MarkdownHandlerMap<T>): T;
1295
1443
  /**
1296
- * A web-standard {@link ReadableStream} over the document's top-level block nodes
1297
- * (shallow, source order) - a lazy, pull-based, backpressure-respecting source. A
1444
+ * Returns a web-standard {@link ReadableStream} over the document's top-level block nodes
1445
+ * (shallow, source order) a lazy, pull-based, backpressure-respecting source. A
1298
1446
  * fresh, independently-replayable stream every call; never mutates the document.
1299
1447
  */
1300
1448
  stream(): ReadableStream<BlockNode>;
1301
1449
  }
1302
1450
 
1303
1451
  /**
1304
- * Any node in a markdown AST - the {@link MarkdownDocument} root, a {@link BlockNode},
1452
+ * Represents any node in a markdown AST the {@link MarkdownDocument} root, a {@link BlockNode},
1305
1453
  * a {@link ListItemNode}, or an {@link InlineNode}. The exhaustive set every
1306
1454
  * projection's `switch` covers.
1307
1455
  */
1308
1456
  export declare type MarkdownNode = MarkdownDocument | BlockNode | ListItemNode | InlineNode;
1309
1457
 
1310
1458
  /**
1311
- * Pairs a parsed document with the {@link MarkdownSpan} of each of its nodes - what
1459
+ * Pairs a parsed document with the {@link MarkdownSpan} of each of its nodes what
1312
1460
  * `parseProvenance` returns, and what `parseDocument` projects the document out of.
1313
1461
  *
1314
1462
  * @remarks
1315
- * `spans` is keyed by node identity, so it addresses the nodes of THAT document and
1316
- * no other. A node the parse merged from adjacent scanner output - the text run
1317
- * `coalesceText` joins - is present and carries the region ENCLOSING its parts, from
1463
+ * `spans` is keyed by node identity, so it addresses the nodes of that document and
1464
+ * no other. A node the parse merged from adjacent scanner output the text run
1465
+ * `coalesceText` joins is present and carries the region enclosing its parts, from
1318
1466
  * the first part's `start` to the last part's `end`, which can include original text
1319
1467
  * lying between them. Absence means the parse recorded no region for the node, not
1320
1468
  * that the node was assembled from more than one region. Destructure it as
@@ -1326,7 +1474,7 @@ spans: ReadonlyMap<MarkdownNode, MarkdownSpan>
1326
1474
  ];
1327
1475
 
1328
1476
  /**
1329
- * What one HTML node projects to on the way to markdown - the fold value
1477
+ * Represents what one HTML node projects to on the way to markdown the fold value
1330
1478
  * `htmlToMarkdown` carries up the AST.
1331
1479
  *
1332
1480
  * @remarks
@@ -1336,45 +1484,45 @@ spans: ReadonlyMap<MarkdownNode, MarkdownSpan>
1336
1484
  * under a `pre`. Rather than guess, each node reports every view its ancestors could
1337
1485
  * need, and the ancestor that knows the context takes the one it wants.
1338
1486
  *
1339
- * - `blocks` / `inlines` - the block and inline views. They are exclusive by
1487
+ * - `blocks` / `inlines` the block and inline views. They are exclusive by
1340
1488
  * construction: as soon as a node contributes a block, the inline runs around it
1341
1489
  * are wrapped into paragraphs, so `blocks` being non-empty means `inlines` is
1342
1490
  * empty and no interleaving is ever lost.
1343
- * - `text` - the raw, uncollapsed, unescaped subtree text a code span and a
1491
+ * - `text` the raw, uncollapsed, unescaped subtree text a code span and a
1344
1492
  * `pre > code` body need verbatim. An `UNSAFE_ELEMENTS` subtree contributes none
1345
1493
  * of it, so a script body can never resurface as prose.
1346
- * - `cells` / `rows` - table structure in flight. A cell travels up to its `tr` and a
1494
+ * - `cells` / `rows` table structure in flight. A cell travels up to its `tr` and a
1347
1495
  * row up to its `table`, passing through the `thead` / `tbody` wrappers between
1348
1496
  * them untouched; whatever never reaches a table degrades to paragraphs.
1349
1497
  */
1350
1498
  export declare interface MarkdownProjection {
1351
- /** The node's block content, with any surrounding inline runs already wrapped into paragraphs. */
1499
+ /** Holds the node's block content, with any surrounding inline runs already wrapped into paragraphs. */
1352
1500
  readonly blocks: readonly BlockNode[];
1353
- /** The node's inline content; empty whenever `blocks` is not. */
1501
+ /** Holds the node's inline content; empty whenever `blocks` is not. */
1354
1502
  readonly inlines: readonly InlineNode[];
1355
- /** The raw subtree text, whitespace uncollapsed and escapes unresolved. */
1503
+ /** Holds the raw subtree text, whitespace uncollapsed and escapes unresolved. */
1356
1504
  readonly text: string;
1357
- /** The cells this node contributes to an enclosing row. */
1505
+ /** Holds the cells this node contributes to an enclosing row. */
1358
1506
  readonly cells: readonly MarkdownCell[];
1359
- /** The rows this node contributes to an enclosing table - each its cells, in column order. */
1507
+ /** Holds the rows this node contributes to an enclosing table each its cells, in column order. */
1360
1508
  readonly rows: ReadonlyArray<readonly MarkdownCell[]>;
1361
1509
  }
1362
1510
 
1363
1511
  /**
1364
- * A copy-on-write node rewrite applied bottom-up by {@link MarkdownInterface.map} -
1512
+ * Represents a copy-on-write node rewrite applied bottom-up by {@link MarkdownInterface.map}
1365
1513
  * receives one node (its own children already rewritten) and returns its
1366
1514
  * replacement (the same node, unchanged, or a new node).
1367
1515
  */
1368
1516
  export declare type MarkdownRewriteHandler = (node: MarkdownNode) => MarkdownNode;
1369
1517
 
1370
1518
  /**
1371
- * Maps one run of a {@link MarkdownSource} back to the region of the ORIGINAL
1519
+ * Maps one run of a {@link MarkdownSource} back to the region of the original
1372
1520
  * markdown string it was taken from.
1373
1521
  *
1374
1522
  * @remarks
1375
1523
  * `offset` addresses {@link MarkdownSource.text}; `start` and `end` address the
1376
1524
  * original string. The run's original length derives from `end - start` rather than
1377
- * being stored beside them, so no length member exists to drift. The run's DERIVED
1525
+ * being stored beside them, so no length member exists to drift. The run's derived
1378
1526
  * extent ends where the next segment's `offset` begins, so a run may cover more of the
1379
1527
  * original than it holds derived: the separator run `joinSources` records over a
1380
1528
  * normalized `\r\n` terminator is one derived code unit over a two-unit original
@@ -1385,27 +1533,27 @@ export declare type MarkdownRewriteHandler = (node: MarkdownNode) => MarkdownNod
1385
1533
  *
1386
1534
  * - strictly inside the run, `p` projects to `start + (p - offset)`;
1387
1535
  * - at the run's derived end, `p` projects to `end`, so the boundary claims the run's
1388
- * whole original region instead of the prefix an affine step would reach - which is
1536
+ * whole original region instead of the prefix an affine step would reach which is
1389
1537
  * how the one-unit `\r\n` separator run above reports its two-unit region;
1390
1538
  * - a zero-width `p` that coincides with a later segment's `offset` resolves through the
1391
- * LAST segment whose `offset` equals `p`, skipping every earlier segment at that
1539
+ * last segment whose `offset` equals `p`, skipping every earlier segment at that
1392
1540
  * position whatever its extent, so a discontinuous abutment reports that final run's
1393
1541
  * `start` rather than the earlier run's `end`.
1394
1542
  *
1395
1543
  * The mapping is therefore affine strictly inside a run and clamped at its end.
1396
1544
  */
1397
1545
  export declare interface MarkdownSegment {
1398
- /** The first code unit of the run inside {@link MarkdownSource.text}. */
1546
+ /** Holds the first code unit of the run inside {@link MarkdownSource.text}. */
1399
1547
  readonly offset: number;
1400
- /** The first code unit of the original-string region the run was produced from, inclusive. */
1548
+ /** Holds the first code unit of the original-string region the run was produced from, inclusive. */
1401
1549
  readonly start: number;
1402
- /** The code unit one past that region's last, exclusive. */
1550
+ /** Holds the code unit one past that region's last, exclusive. */
1403
1551
  readonly end: number;
1404
1552
  }
1405
1553
 
1406
1554
  /**
1407
1555
  * Pairs a piece of derived markdown text with the runs mapping it back to the
1408
- * original string - what `splitLines` returns per line, so every phase downstream of
1556
+ * original string what `splitLines` returns per line, so every phase downstream of
1409
1557
  * it keeps original coordinates instead of reconstructing them from node values.
1410
1558
  *
1411
1559
  * @remarks
@@ -1423,21 +1571,21 @@ export declare interface MarkdownSegment {
1423
1571
  * coverage with `projectSpan` rather than assuming it.
1424
1572
  */
1425
1573
  export declare interface MarkdownSource {
1426
- /** The derived text a parser reads. */
1574
+ /** Holds the derived text a parser reads. */
1427
1575
  readonly text: string;
1428
- /** The runs mapping `text` back to the original string, in ascending `offset` order. */
1576
+ /** Holds the runs mapping `text` back to the original string, in ascending `offset` order. */
1429
1577
  readonly segments: readonly MarkdownSegment[];
1430
1578
  }
1431
1579
 
1432
1580
  /**
1433
- * Addresses a half-open region of the ORIGINAL markdown string, in UTF-16 code units -
1581
+ * Addresses a half-open region of the original markdown string, in UTF-16 code units
1434
1582
  * `start` inclusive, `end` exclusive. The provenance a parse records for a node and
1435
1583
  * {@link MarkdownInterface.span} reads back.
1436
1584
  *
1437
1585
  * @remarks
1438
1586
  * The coordinates address the string the handle was constructed from, never the line
1439
1587
  * text a later phase walks, so `markdown.slice(span.start, span.end)` returns the
1440
- * ORIGINAL source region the node was produced from. That region is not the node's
1588
+ * original source region the node was produced from. That region is not the node's
1441
1589
  * value: it carries the syntax the value drops, such as a `\` escape marker, and the
1442
1590
  * characters that normalization removed, such as a trailing space the paragraph phase
1443
1591
  * trimmed. The text node of `'a \nb'` has the `value` `a\nb` and reports
@@ -1446,14 +1594,14 @@ export declare interface MarkdownSource {
1446
1594
  * is `end - start`; no length member exists to drift from the two offsets.
1447
1595
  */
1448
1596
  export declare interface MarkdownSpan {
1449
- /** The first code unit of the region, inclusive. */
1597
+ /** Holds the first code unit of the region, inclusive. */
1450
1598
  readonly start: number;
1451
- /** The code unit one past the region's last, exclusive. */
1599
+ /** Holds the code unit one past the region's last, exclusive. */
1452
1600
  readonly end: number;
1453
1601
  }
1454
1602
 
1455
1603
  /**
1456
- * Project a {@link MarkdownNode} into an unsanitized {@link HTMLDocument}.
1604
+ * Projects a {@link MarkdownNode} into an unsanitized {@link HTMLDocument}.
1457
1605
  *
1458
1606
  * @remarks
1459
1607
  * The projection is pure and iterative. Text and attribute values remain literal for
@@ -1474,10 +1622,10 @@ export declare interface MarkdownSpan {
1474
1622
  export declare function markdownToHTML(node: MarkdownNode): HTMLDocument;
1475
1623
 
1476
1624
  /**
1477
- * The maximum recursion depth the parse pipeline (`parseDocument` and its
1478
- * `parsers.ts` helpers) and the `helpers.ts` traversal / projection functions
1479
- * (`markdownToHTML`, `renderHTML`, `renderMarkdown`, `walkNodes`, `foldNode`,
1480
- * `rewriteDocument`) honor before degrading. It bounds blockquote nesting, inline
1625
+ * Caps the recursion depth the parse pipeline (`parseDocument` and its
1626
+ * `parsers.ts` helpers), the `helpers.ts` traversal / projection functions
1627
+ * (`markdownToHTML`, `renderMarkdown`, `walkNodes`, `foldNode`, `rewriteDocument`),
1628
+ * and the `compilers.ts` renderer (`renderHTML`) honor before degrading, at 64. It bounds blockquote nesting, inline
1481
1629
  * nesting (emphasis / links), and traversal / projection recursion so pathological
1482
1630
  * or hostile input cannot exhaust the call stack. {@link htmlToMarkdown} is the
1483
1631
  * inherited exception: its fold and depth cap belong to `@orkestrel/html`.
@@ -1485,14 +1633,14 @@ export declare function markdownToHTML(node: MarkdownNode): HTMLDocument;
1485
1633
  export declare const MAX_DEPTH = 64;
1486
1634
 
1487
1635
  /**
1488
- * Combine the projections of one node's children into the projection of that node -
1636
+ * Combines the projections of one node's children into the projection of that node
1489
1637
  * the single place inline runs become paragraphs, so no ancestor has to decide it
1490
1638
  * twice.
1491
1639
  *
1492
1640
  * @remarks
1493
1641
  * A child is either inline or block, never both, so merging preserves source order
1494
1642
  * exactly: an inline run is held pending until a block arrives, then written out as a
1495
- * paragraph BEFORE it. That is what keeps `<div>lead<p>a</p></div>` two paragraphs in
1643
+ * paragraph before it. That is what keeps `<div>lead<p>a</p></div>` two paragraphs in
1496
1644
  * the order they were written rather than two lists that lost their interleaving. A
1497
1645
  * pending run carrying no text is dropped rather than becoming a blank paragraph.
1498
1646
  * Direct cells become one row before a later row, while cells/rows before a block
@@ -1513,21 +1661,21 @@ export declare const MAX_DEPTH = 64;
1513
1661
  export declare function mergeProjections(children: readonly MarkdownProjection[]): MarkdownProjection;
1514
1662
 
1515
1663
  /**
1516
- * Reduce an inline run to the shape markdown can actually write back: adjacent text
1664
+ * Reduces an inline run to the shape markdown can actually write back: adjacent text
1517
1665
  * coalesced, empty text dropped, and every hard break either kept as a real line
1518
1666
  * ending or spent as a space.
1519
1667
  *
1520
1668
  * @remarks
1521
- * A hard break is ` \n` in markdown source, so it survives a re-parse only BETWEEN
1669
+ * A hard break is ` \n` in markdown source, so it survives a re-parse only between
1522
1670
  * two lines of content and only with no whitespace touching it: a leading or trailing
1523
1671
  * break has no line to end, a run of breaks reads as one blank line (which would end
1524
1672
  * the paragraph), and a space beside one is eaten by the parser's line trimming. Where
1525
- * a break cannot be written at all - a heading and a table cell are one line each - it
1673
+ * a break cannot be written at all a heading and a table cell are one line each it
1526
1674
  * becomes the space it stood for.
1527
1675
  *
1528
1676
  * @param nodes - The inline run to normalize
1529
- * @param breaks - Whether the target context can carry a hard break at all; `false` for
1530
- * a heading or a table cell, where every break becomes a space
1677
+ * @param breaks - If `true`, keeps each hard break as a real line ending; if `false`, spends
1678
+ * every break as the space it stood for, as a heading or a table cell requires
1531
1679
  * @returns The normalized run
1532
1680
  *
1533
1681
  * @example
@@ -1554,10 +1702,10 @@ export declare function normalizeInlines(nodes: readonly InlineNode[], breaks: b
1554
1702
  */
1555
1703
  export declare function normalizeParagraphLine(source: MarkdownSource, breaks: boolean): MarkdownSource;
1556
1704
 
1557
- /** A paragraph - a run of non-blank lines that is not another block; `children` its inline content. */
1705
+ /** Represents a paragraph a run of non-blank lines that is not another block; `children` its inline content. */
1558
1706
  export declare interface ParagraphNode {
1559
1707
  readonly element: 'paragraph';
1560
- /** The inline content of the paragraph. */
1708
+ /** Holds the inline content of the paragraph. */
1561
1709
  readonly children: readonly InlineNode[];
1562
1710
  }
1563
1711
 
@@ -1579,33 +1727,52 @@ export declare interface ParagraphNode {
1579
1727
  export declare function parseBlocks(lines: readonly MarkdownSource[], depth: number, spans?: Map<MarkdownNode, MarkdownSpan>, end?: number): readonly BlockNode[];
1580
1728
 
1581
1729
  /**
1582
- * Parses a markdown string into a typed {@link MarkdownDocument} AST via the
1583
- * block phase.
1730
+ * Parses a markdown string into a typed {@link MarkdownDocument} AST through the
1731
+ * block phase — the document half of what {@link parseProvenance} returns. Malformed
1732
+ * markdown degrades to literal text, so the parse never throws.
1584
1733
  *
1585
1734
  * @param markdown - The markdown source to parse.
1586
1735
  * @returns The parsed document.
1736
+ *
1737
+ * @example
1738
+ * ```ts
1739
+ * parseDocument('# Hi') // { element: 'document', children: [{ element: 'heading', ... }] }
1740
+ * ```
1587
1741
  */
1588
1742
  export declare function parseDocument(markdown: string): MarkdownDocument;
1589
1743
 
1590
1744
  /**
1591
1745
  * Parses inline markdown text (emphasis, code spans, links, images, and hard
1592
- * breaks) into inline AST nodes, coalescing adjacent text runs.
1746
+ * breaks) into inline AST nodes, coalescing adjacent text runs and reading no block
1747
+ * structure. Malformed markdown degrades to literal text, so the parse never throws.
1593
1748
  *
1594
1749
  * @param text - The inline markdown text to parse.
1595
1750
  * @returns The parsed inline nodes.
1751
+ *
1752
+ * @example
1753
+ * ```ts
1754
+ * parseInline('a *b*') // [{ element: 'text', value: 'a ' }, { element: 'emphasis', ... }]
1755
+ * ```
1596
1756
  */
1597
1757
  export declare function parseInline(text: string): readonly InlineNode[];
1598
1758
 
1599
1759
  /**
1600
- * Parses a markdown string into a document and its original-source spans.
1760
+ * Parses a markdown string into a document and its original-source spans. Malformed
1761
+ * markdown degrades to literal text, so the parse never throws.
1601
1762
  *
1602
1763
  * @param markdown - The markdown source to parse.
1603
1764
  * @returns The parsed document and its node-identity span map.
1765
+ *
1766
+ * @example
1767
+ * ```ts
1768
+ * const [document, spans] = parseProvenance('# Hi')
1769
+ * spans.get(document) // { start: 0, end: 4 }
1770
+ * ```
1604
1771
  */
1605
1772
  export declare function parseProvenance(markdown: string): MarkdownParseResult;
1606
1773
 
1607
1774
  /**
1608
- * Project one HTML leaf - a text node, a comment, or a doctype - to its
1775
+ * Projects one HTML leaf a text node, a comment, or a doctype to its
1609
1776
  * {@link MarkdownProjection}.
1610
1777
  *
1611
1778
  * @remarks
@@ -1626,8 +1793,8 @@ export declare function parseProvenance(markdown: string): MarkdownParseResult;
1626
1793
  export declare function projectHTMLLeaf(leaf: CommentNode | DoctypeNode | TextNode_2): MarkdownProjection;
1627
1794
 
1628
1795
  /**
1629
- * Project one HTML container - the document root or an element - from its children's
1630
- * already-computed projections. THE element mapping, and the only place that decides
1796
+ * Projects one HTML container the document root or an element from its children's
1797
+ * already-computed projections. The element mapping, and the only place that decides
1631
1798
  * what an HTML tag becomes in markdown.
1632
1799
  *
1633
1800
  * @remarks
@@ -1639,13 +1806,13 @@ export declare function projectHTMLLeaf(leaf: CommentNode | DoctypeNode | TextNo
1639
1806
  * inline runs wrapped in paragraphs; `ul` / `ol` a list, ordered from the tag and
1640
1807
  * numbered from `start`; `th` / `td`, `tr`, and `table` a GFM table whose column
1641
1808
  * alignment comes from each header-position cell's `align` attribute. Every
1642
- * `UNSAFE_ELEMENTS` subtree contributes nothing at all, text included. Every OTHER
1809
+ * `UNSAFE_ELEMENTS` subtree contributes nothing at all, text included. Every other
1643
1810
  * element unwraps to its children, so wrapper soup melts while its content keeps its
1644
- * shape - `<div><p>a</p><p>b</p></div>` stays two paragraphs.
1811
+ * shape `<div><p>a</p><p>b</p></div>` stays two paragraphs.
1645
1812
  *
1646
1813
  * Three mappings read their own node rather than only their children's projections,
1647
1814
  * because HTML puts the fact in a position rather than in a value: a `pre` takes its
1648
- * body from its `code` child's raw text, and a list takes one item per `li` child - so
1815
+ * body from its `code` child's raw text, and a list takes one item per `li` child so
1649
1816
  * an empty `<li>` is still an item, while the whitespace between two of them is not.
1650
1817
  * A `tr` accepts only its own direct cells, and a table derives the first `th`-bearing
1651
1818
  * row from its own source structure.
@@ -1663,7 +1830,7 @@ export declare function projectHTMLLeaf(leaf: CommentNode | DoctypeNode | TextNo
1663
1830
  export declare function projectHTMLNode(node: ElementNode | HTMLDocument, children: readonly MarkdownProjection[]): MarkdownProjection;
1664
1831
 
1665
1832
  /**
1666
- * Read a projection as BLOCK content - the view a document, a blockquote, and a list
1833
+ * Reads a projection as block content the view a document, a blockquote, and a list
1667
1834
  * item each need.
1668
1835
  *
1669
1836
  * @remarks
@@ -1684,7 +1851,7 @@ export declare function projectHTMLNode(node: ElementNode | HTMLDocument, childr
1684
1851
  export declare function projectionToBlocks(projection: MarkdownProjection): readonly BlockNode[];
1685
1852
 
1686
1853
  /**
1687
- * Read a projection as INLINE content - the view a link, an emphasis, and a table cell
1854
+ * Reads a projection as inline content the view a link, an emphasis, and a table cell
1688
1855
  * each need.
1689
1856
  *
1690
1857
  * @remarks
@@ -1722,9 +1889,12 @@ export declare function projectionToInlines(projection: MarkdownProjection): rea
1722
1889
  export declare function projectSpan(source: MarkdownSource, from: number, to: number): MarkdownSpan | undefined;
1723
1890
 
1724
1891
  /**
1725
- * Render a {@link MarkdownNode} to sanitized canonical HTML.
1892
+ * Renders a {@link MarkdownNode} to sanitized canonical HTML.
1726
1893
  *
1727
1894
  * @remarks
1895
+ * Sanitization is unconditional: the function takes one argument and declares no
1896
+ * options, so no call shape opts out of it.
1897
+ *
1728
1898
  * Markdown widens `@orkestrel/html`'s attribute floor by exactly `src`, because image
1729
1899
  * syntax is meaningless without its source. `src` is still a URL attribute, so the
1730
1900
  * floor refuses `javascript:`, `data:`, `vbscript:`, and `file:` values. A stricter
@@ -1743,17 +1913,17 @@ export declare function projectSpan(source: MarkdownSource, from: number, to: nu
1743
1913
  export declare function renderHTML(node: MarkdownNode): string;
1744
1914
 
1745
1915
  /**
1746
- * Render a {@link MarkdownNode} to its CANONICAL markdown source - the inverse
1747
- * projection of `renderHTML`, and the serializer a `parse(renderMarkdown(doc))`
1916
+ * Renders a {@link MarkdownNode} to its canonical markdown source the inverse
1917
+ * projection of `renderHTML`. It is the serializer a `parse(renderMarkdown(doc))`
1748
1918
  * round-trip is built on. Canonical forms: `*` / `**` emphasis at even emphasis
1749
1919
  * nesting depths and `_` / `__` at odd depths, `- ` bullets, `N. ` sequential
1750
1920
  * ordinals (from the list's `start`), `---` thematic breaks, fenced code blocks
1751
1921
  * (backtick run widened past any 3+ backtick run inside the body), ATX headings,
1752
- * `> `-prefixed blockquote lines, GFM tables (1-space-padded cells, `\|`-escaped
1753
- * pipes, an alignment delimiter row), `[text](href)` links, `![alt](src)` images,
1754
- * and two-space hard breaks. A `text` node's literal content is backslash-escaped
1755
- * wherever it would otherwise re-parse as markup (AGENTS §14 parse↔render
1756
- * soundness).
1922
+ * `> `-prefixed blockquote lines, GFM tables (1-space-padded cells, a backslash
1923
+ * before each literal pipe, an alignment delimiter row), `[text](href)` links,
1924
+ * `![alt](src)` images, and two-space hard breaks. A `text` node's literal content is backslash-escaped
1925
+ * wherever it would otherwise re-parse as markup, so parsing the rendered source
1926
+ * returns the node it was rendered from.
1757
1927
  *
1758
1928
  * @remarks
1759
1929
  * Total: never throws. At {@link MAX_DEPTH} a value-bearing node degrades to its
@@ -1774,10 +1944,10 @@ export declare function renderHTML(node: MarkdownNode): string;
1774
1944
  export declare function renderMarkdown(node: MarkdownNode): string;
1775
1945
 
1776
1946
  /**
1777
- * Rewrite a {@link MarkdownDocument} bottom-up (copy-on-write) - each node's children
1947
+ * Rewrites a {@link MarkdownDocument} bottom-up (copy-on-write) each node's children
1778
1948
  * are rewritten first (post-order), then `rewrite` is applied to the node itself; the
1779
- * document ROOT is never passed to `rewrite` (the `element: 'document'` invariant
1780
- * always holds). A table's inline cells and a list's items ARE rewritten.
1949
+ * document root is never passed to `rewrite` (the `element: 'document'` invariant
1950
+ * always holds). A table's inline cells and a list's items are rewritten too.
1781
1951
  *
1782
1952
  * @remarks
1783
1953
  * Never mutates `document`. An unchanged subtree keeps its input identity. A parent
@@ -1786,12 +1956,12 @@ export declare function renderMarkdown(node: MarkdownNode): string;
1786
1956
  * whose `element` does not fit the slot it was called for (a block slot handed a
1787
1957
  * non-{@link BlockNode}, an inline slot handed a non-{@link InlineNode}, a list-item
1788
1958
  * slot handed a non-`listItem`), the ill-fitting result is discarded and the accepted
1789
- * input child is reused - `rewriteDocument` stays total and never produces a
1959
+ * input child is reused `rewriteDocument` stays total and never produces a
1790
1960
  * structurally invalid document.
1791
1961
  *
1792
1962
  * Descent is capped at {@link MAX_DEPTH}, the same cap {@link walkNodes} and
1793
1963
  * {@link foldNode} observe: at `depth >= MAX_DEPTH` the subtree is passed through
1794
- * UNCHANGED (by reference, not rebuilt, and `rewrite` is not invoked on it) instead of
1964
+ * unchanged (by reference, not rebuilt, and `rewrite` is not invoked on it) instead of
1795
1965
  * recursing further, so a pathologically deep adopted document cannot exhaust the
1796
1966
  * call stack. {@link MarkdownInterface.map} inherits this cap since it delegates here.
1797
1967
  *
@@ -1809,8 +1979,8 @@ export declare function renderMarkdown(node: MarkdownNode): string;
1809
1979
  export declare function rewriteDocument(document: MarkdownDocument, rewrite: MarkdownRewriteHandler): MarkdownDerivation<MarkdownDocument>;
1810
1980
 
1811
1981
  /**
1812
- * Scan an inline code span at `start` (a `` ` ``-run … a matching `` ` ``-run of the
1813
- * SAME length, the CommonMark rule that lets a span contain backticks). Returns the
1982
+ * Scans an inline code span at `start` (a `` ` ``-run … a matching `` ` ``-run of the
1983
+ * same length, the CommonMark rule that lets a span contain backticks). Returns the
1814
1984
  * span's literal text + end index, or `undefined` when no matching closer exists (it
1815
1985
  * then degrades to literal backticks).
1816
1986
  *
@@ -1824,13 +1994,10 @@ export declare function rewriteDocument(document: MarkdownDocument, rewrite: Mar
1824
1994
  * scanCode('`code`', 0, 6) // { value: 'code', end: 6 }
1825
1995
  * ```
1826
1996
  */
1827
- export declare function scanCode(source: string, start: number, to: number): {
1828
- readonly value: string;
1829
- readonly end: number;
1830
- } | undefined;
1997
+ export declare function scanCode(source: string, start: number, to: number): CodeSpanMatch | undefined;
1831
1998
 
1832
1999
  /**
1833
- * Scans an emphasis run at `start` (`*` / `_`, doubled for strong) - finds the nearest
2000
+ * Scans an emphasis run at `start` (`*` / `_`, doubled for strong) finds the nearest
1834
2001
  * matching closing run of the same marker + width while skipping complete nested runs
1835
2002
  * from the other marker family, and requires non-space immediately inside both
1836
2003
  * delimiters (the CommonMark flanking simplification that blocks `* x *`) through
@@ -1851,13 +2018,10 @@ export declare function scanCode(source: string, start: number, to: number): {
1851
2018
  * // { node: { element: 'emphasis', strong: false, children: [{ element: 'text', value: 'em' }] }, end: 4 }
1852
2019
  * ```
1853
2020
  */
1854
- export declare function scanEmphasis(source: string, start: number, to: number, depth?: number): {
1855
- readonly node: EmphasisNode;
1856
- readonly end: number;
1857
- } | undefined;
2021
+ export declare function scanEmphasis(source: string, start: number, to: number, depth?: number): EmphasisScan | undefined;
1858
2022
 
1859
2023
  /**
1860
- * Scan the window `[from, to)` of `source` into inline nodes - the single recursive
2024
+ * Scans the window `[from, to)` of `source` into inline nodes the single recursive
1861
2025
  * engine the inline phase runs on (emphasis, link text, and image alternative
1862
2026
  * content recurse through it). Linear:
1863
2027
  * each character is consumed once; a failed construct emits its opening character as
@@ -1867,11 +2031,12 @@ export declare function scanEmphasis(source: string, start: number, to: number,
1867
2031
  * @param from - The inclusive start of the scan window
1868
2032
  * @param to - The exclusive end of the scan window
1869
2033
  * @param depth - The current inline-recursion depth (defaults to 0 at the entry point);
1870
- * incremented by one on every recursive descent through {@link scanLink} /
1871
- * {@link scanEmphasis}. At {@link MAX_DEPTH} the window is never scanned for markup -
1872
- * it emits as a single literal text node - so pathological nesting (`[[[[…`,
1873
- * `****…`) cannot exhaust the call stack.
1874
- * @returns The parsed inline nodes (NOT yet coalesced)
2034
+ * incremented by one on every recursive descent {@link scanInlineSource} makes into
2035
+ * itself for a link's text, an image's alternative content, or an emphasis run's
2036
+ * children. At {@link MAX_DEPTH} the window is never scanned for markup it emits as
2037
+ * a single literal text node — so pathological nesting (`[[[[…`, `****…`) cannot
2038
+ * exhaust the call stack.
2039
+ * @returns The parsed inline nodes (not yet coalesced)
1875
2040
  *
1876
2041
  * @example
1877
2042
  * ```ts
@@ -1888,7 +2053,9 @@ export declare function scanInline(source: string, from: number, to: number, dep
1888
2053
  * @param from - The inclusive start of the scan window
1889
2054
  * @param to - The exclusive end of the scan window
1890
2055
  * @param spans - The operation-owned node span recorder
1891
- * @param depth - The current inline-recursion depth
2056
+ * @param depth - The current inline-recursion depth, incremented by one on every
2057
+ * recursive descent this function makes into itself for a link's text, an image's
2058
+ * alternative content, or an emphasis run's children
1892
2059
  * @returns The parsed inline nodes before adjacent text coalescing
1893
2060
  *
1894
2061
  * @example
@@ -1905,7 +2072,7 @@ export declare function scanInline(source: string, from: number, to: number, dep
1905
2072
  export declare function scanInlineSource(source: MarkdownSource, from: number, to: number, spans: Map<MarkdownNode, MarkdownSpan>, depth?: number): readonly InlineNode[];
1906
2073
 
1907
2074
  /**
1908
- * Scans a link `[text](href)` at `start` - the text runs to a BALANCED `]`, then `(`
2075
+ * Scans a link `[text](href)` at `start` the text runs to a balanced `]`, then `(`
1909
2076
  * must immediately follow and the destination runs to the matching `)` (both respect
1910
2077
  * nested delimiters + escapes) through {@link locateLink}, and returns the parsed node
1911
2078
  * and end index. Returns `undefined` when the shape does not hold (it then degrades to
@@ -1925,10 +2092,7 @@ export declare function scanInlineSource(source: MarkdownSource, from: number, t
1925
2092
  * // { node: { element: 'link', href: 'url', children: [{ element: 'text', value: 'text' }] }, end: 11 }
1926
2093
  * ```
1927
2094
  */
1928
- export declare function scanLink(source: string, start: number, to: number, depth?: number): {
1929
- readonly node: LinkNode;
1930
- readonly end: number;
1931
- } | undefined;
2095
+ export declare function scanLink(source: string, start: number, to: number, depth?: number): LinkScan | undefined;
1932
2096
 
1933
2097
  /**
1934
2098
  * Slices derived markdown text and narrows each intersecting source segment to the
@@ -1963,10 +2127,11 @@ export declare function sliceSource(source: MarkdownSource, from: number, to: nu
1963
2127
  export declare function splitLines(markdown: string): readonly MarkdownSource[];
1964
2128
 
1965
2129
  /**
1966
- * Split one GFM table row into its cell strings - outer pipes are optional, an escaped
1967
- * pipe (`\|`) inside a cell is NOT a separator (it becomes a literal `|`), and the
1968
- * empty leading / trailing cell produced by an outer `|` is dropped. Derives the string
1969
- * form from {@link splitTableSources}, which owns the escaped-pipe splitting rule.
2130
+ * Splits one GFM table row into its cell strings outer pipes are optional, a pipe
2131
+ * escaped by a leading backslash inside a cell is not a separator (it becomes a literal
2132
+ * pipe character), and the empty leading / trailing cell an outer pipe produces is
2133
+ * dropped. Derives the string form from {@link splitTableSources}, which owns the
2134
+ * escaped-pipe splitting rule.
1970
2135
  *
1971
2136
  * @param row - The raw table row line
1972
2137
  * @returns The row's cells, in column order
@@ -1993,15 +2158,15 @@ export declare function splitTableRow(row: string): readonly string[];
1993
2158
  export declare function splitTableSources(row: MarkdownSource): readonly MarkdownSource[];
1994
2159
 
1995
2160
  /**
1996
- * Whether the line at `index` starts a NEW block kind (heading / fence / thematic
1997
- * break / blockquote / list / table) - the paragraph collector stops at such a line
2161
+ * Checks whether the line at `index` starts a new block kind (heading / fence / thematic
2162
+ * break / blockquote / list / table) the paragraph collector stops at such a line
1998
2163
  * so a block following a paragraph without a blank line still parses (a trusted-input
1999
2164
  * caller writing a `##` heading directly under a paragraph, with no intervening blank
2000
2165
  * line).
2001
2166
  *
2002
2167
  * @param lines - The document's lines
2003
2168
  * @param index - The line index to test
2004
- * @returns `true` when the line begins a different block
2169
+ * @returns True if the line begins a different block; false otherwise
2005
2170
  *
2006
2171
  * @example
2007
2172
  * ```ts
@@ -2027,17 +2192,24 @@ export declare function startsBlock(lines: readonly string[], index: number): bo
2027
2192
  export declare function stripQuote(source: MarkdownSource): MarkdownSource;
2028
2193
 
2029
2194
  /**
2030
- * The horizontal alignment of a GFM table column, as declared by its delimiter row
2195
+ * Names the horizontal alignment of a GFM table column, as declared by its delimiter row
2031
2196
  * (`:---` left, `---:` right, `:---:` center). A bare `---` delimiter is represented
2032
2197
  * by `null` in {@link TableNode.align}: the positional array requires one entry per
2033
2198
  * column, JSON cannot carry `undefined` in an array, and the bare delimiter is an
2034
2199
  * explicit no-alignment marker rather than an omitted value.
2200
+ *
2201
+ * @remarks
2202
+ * The other place absence appears is {@link MarkdownCell.align}, which holds
2203
+ * `undefined` when the projected cell declared no alignment. That member is a plain
2204
+ * optional property on one cell rather than an entry in a positional array, so it
2205
+ * takes the ordinary `undefined` instead of the in-band `null` marker.
2035
2206
  */
2036
2207
  export declare type TableAlign = 'left' | 'right' | 'center';
2037
2208
 
2038
2209
  /**
2039
- * The shape of a {@link TableAlign} - the per-column GFM table alignment
2040
- * literal.
2210
+ * Describes the shape of a {@link TableAlign} the per-column GFM table alignment
2211
+ * literal. Absence is no member of it, so the shape refuses the `null` a bare `---`
2212
+ * delimiter takes in a `TableNode`'s `align` list.
2041
2213
  *
2042
2214
  * @example
2043
2215
  * ```ts
@@ -2053,19 +2225,30 @@ export declare type TableAlign = 'left' | 'right' | 'center';
2053
2225
  export declare const tableAlignShape: LiteralShape<readonly ["left", "right", "center"]>;
2054
2226
 
2055
2227
  /**
2056
- * A GFM table - `header` the inline content of each header cell, `rows` the body
2228
+ * Represents the result of collecting one GFM table the node the construct scanner built and
2229
+ * where the block phase resumes.
2230
+ */
2231
+ export declare interface TableCollection {
2232
+ /** Holds the collected table. */
2233
+ readonly node: TableNode;
2234
+ /** Holds the index of the first line after the table. */
2235
+ readonly next: number;
2236
+ }
2237
+
2238
+ /**
2239
+ * Represents a GFM table — `header` the inline content of each header cell, `rows` the body
2057
2240
  * rows (each a list of cells, each cell inline content), `align` the per-column
2058
2241
  * alignment from the delimiter row. A short body row is padded with empty cells; an
2059
2242
  * over-long one is truncated to the header's column count.
2060
2243
  */
2061
2244
  export declare interface TableNode {
2062
2245
  readonly element: 'table';
2063
- /** The header row - one cell of inline content per column. */
2246
+ /** Holds the header row one cell of inline content per column. */
2064
2247
  readonly header: ReadonlyArray<readonly InlineNode[]>;
2065
- /** The body rows - each a list of cells, each cell inline content. */
2248
+ /** Holds the body rows each a list of cells, each cell inline content. */
2066
2249
  readonly rows: ReadonlyArray<ReadonlyArray<readonly InlineNode[]>>;
2067
2250
  /**
2068
- * The per-column alignment from the delimiter row, in column order. `null`
2251
+ * Holds the per-column alignment from the delimiter row, in column order. `null`
2069
2252
  * represents a bare `---` delimiter because this positional array requires one
2070
2253
  * entry per column, JSON cannot carry `undefined` in an array, and the delimiter
2071
2254
  * is an explicit no-alignment marker rather than an omitted value.
@@ -2074,19 +2257,19 @@ export declare interface TableNode {
2074
2257
  }
2075
2258
 
2076
2259
  /**
2077
- * A run of plain text - the leaf inline node. `value` is the decoded text with
2260
+ * Represents a run of plain text the leaf inline node. `value` is the decoded text with
2078
2261
  * markdown escapes (`\*`, `\_`, …) already resolved to their literal characters;
2079
2262
  * html's text encoder escapes `&`, `<`, `>` on the way out; `"` and `'` stay literal
2080
2263
  * in character data.
2081
2264
  */
2082
2265
  export declare interface TextNode {
2083
2266
  readonly element: 'text';
2084
- /** The literal text content (escapes resolved, NOT yet HTML-escaped). */
2267
+ /** Holds the literal text content (escapes resolved, not yet HTML-escaped). */
2085
2268
  readonly value: string;
2086
2269
  }
2087
2270
 
2088
2271
  /**
2089
- * The shape of a {@link TextNode} - a plain-text leaf inline run.
2272
+ * Describes the shape of a {@link TextNode} a plain-text leaf inline run.
2090
2273
  *
2091
2274
  * @example
2092
2275
  * ```ts
@@ -2097,18 +2280,18 @@ export declare interface TextNode {
2097
2280
  * text.is({ element: 'text', value: 'hi' }) // true
2098
2281
  * ```
2099
2282
  */
2100
- export declare const textShape: ObjectShape<{
2101
- element: LiteralShape<readonly ["text"]>;
2102
- value: StringShape;
2283
+ export declare const textShape: ObjectShape< {
2284
+ element: LiteralShape<readonly ["text"]>;
2285
+ value: StringShape;
2103
2286
  }, false>;
2104
2287
 
2105
- /** A thematic break - a horizontal rule (`---` / `***` / `___` on its own line). */
2288
+ /** Represents a thematic break a horizontal rule (`---` / `***` / `___` on its own line). */
2106
2289
  export declare interface ThematicBreakNode {
2107
2290
  readonly element: 'thematicBreak';
2108
2291
  }
2109
2292
 
2110
2293
  /**
2111
- * The shape of a {@link ThematicBreakNode} - a horizontal rule. Carries no
2294
+ * Describes the shape of a {@link ThematicBreakNode} a horizontal rule. Carries no
2112
2295
  * fields beyond its `element` discriminant.
2113
2296
  *
2114
2297
  * @example
@@ -2120,13 +2303,13 @@ export declare interface ThematicBreakNode {
2120
2303
  * thematicBreak.is({ element: 'thematicBreak' }) // true
2121
2304
  * ```
2122
2305
  */
2123
- export declare const thematicBreakShape: ObjectShape<{
2124
- element: LiteralShape<readonly ["thematicBreak"]>;
2306
+ export declare const thematicBreakShape: ObjectShape< {
2307
+ element: LiteralShape<readonly ["thematicBreak"]>;
2125
2308
  }, false>;
2126
2309
 
2127
2310
  /**
2128
- * Trim the whitespace at the two ends of an inline run - the leading whitespace of a
2129
- * leading text node and the trailing whitespace of a trailing one - dropping either
2311
+ * Trims the whitespace at the two ends of an inline run the leading whitespace of a
2312
+ * leading text node and the trailing whitespace of a trailing one dropping either
2130
2313
  * node when nothing survives.
2131
2314
  *
2132
2315
  * @remarks
@@ -2159,7 +2342,7 @@ export declare function trimInlines(nodes: readonly InlineNode[]): readonly Inli
2159
2342
  export declare function trimSource(source: MarkdownSource): MarkdownSource;
2160
2343
 
2161
2344
  /**
2162
- * Resolve backslash escapes in a raw string to their literal characters - used for a
2345
+ * Resolves backslash escapes in a raw string to their literal characters used for a
2163
2346
  * link `href` (which is not otherwise inline-parsed) and any plain text run.
2164
2347
  *
2165
2348
  * @param text - The raw text possibly carrying `\x` escapes
@@ -2173,7 +2356,7 @@ export declare function trimSource(source: MarkdownSource): MarkdownSource;
2173
2356
  export declare function unescapeText(text: string): string;
2174
2357
 
2175
2358
  /**
2176
- * Depth-first, pre-order, root-inclusive traversal of a {@link MarkdownNode} - yields
2359
+ * Walks a {@link MarkdownNode} depth-first, pre-order, root-inclusive yields
2177
2360
  * the node itself, then recurses into its children (block children, list items,
2178
2361
  * image/link inline children, table header/row cells' inline nodes) in walk order.
2179
2362
  *