@orkestrel/markdown 0.0.13 → 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.
- package/README.md +12 -6
- package/dist/src/core/index.cjs +121 -114
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +247 -218
- package/dist/src/core/index.d.ts +247 -218
- package/dist/src/core/index.js +121 -114
- package/dist/src/core/index.js.map +1 -1
- package/package.json +9 -10
package/dist/src/core/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import { HTML, SAFE_ATTRIBUTES, SAFE_URL_SCHEMES, TABLE_ALIGNMENTS, UNSAFE_ELEME
|
|
|
5
5
|
* Caps the recursion depth the parse pipeline (`parseDocument` and its
|
|
6
6
|
* `parsers.ts` helpers), the `helpers.ts` traversal / projection functions
|
|
7
7
|
* (`markdownToHTML`, `renderMarkdown`, `walkNodes`, `foldNode`, `rewriteDocument`),
|
|
8
|
-
* and the `compilers.ts` renderer (`renderHTML`) honor before degrading. It bounds blockquote nesting, inline
|
|
8
|
+
* and the `compilers.ts` renderer (`renderHTML`) honor before degrading, at 64. It bounds blockquote nesting, inline
|
|
9
9
|
* nesting (emphasis / links), and traversal / projection recursion so pathological
|
|
10
10
|
* or hostile input cannot exhaust the call stack. {@link htmlToMarkdown} is the
|
|
11
11
|
* inherited exception: its fold and depth cap belong to `@orkestrel/html`.
|
|
@@ -160,7 +160,7 @@ function isEmphasisNode(node) {
|
|
|
160
160
|
* Determines whether a node is an inline code span.
|
|
161
161
|
*
|
|
162
162
|
* @remarks
|
|
163
|
-
* Narrows to {@link CodeSpanNode}
|
|
163
|
+
* Narrows to {@link CodeSpanNode} — the node whose `element` discriminant is
|
|
164
164
|
* `'codeSpan'`.
|
|
165
165
|
*
|
|
166
166
|
* @param node - The AST node to test
|
|
@@ -217,11 +217,11 @@ function isImageNode(node) {
|
|
|
217
217
|
return node.element === "image";
|
|
218
218
|
}
|
|
219
219
|
/**
|
|
220
|
-
* Determines whether an arbitrary value is a valid {@link InlineNode}
|
|
220
|
+
* Determines whether an arbitrary value is a valid {@link InlineNode} — a text
|
|
221
221
|
* run, emphasis, code span, hard break, link, or image, recursively validated.
|
|
222
222
|
*
|
|
223
223
|
* @remarks
|
|
224
|
-
* Total: never throws, even on cyclic or pathologically deep input
|
|
224
|
+
* Total: never throws, even on cyclic or pathologically deep input — every
|
|
225
225
|
* combinator involved (`unionOf`, `recordOf`, `arrayOf`, `lazyOf`) is
|
|
226
226
|
* throw-contained per the `@orkestrel/contract` guard contract.
|
|
227
227
|
*
|
|
@@ -256,16 +256,16 @@ var isInlineNode = unionOf(recordOf({
|
|
|
256
256
|
children: arrayOf(lazyOf(() => isInlineNode))
|
|
257
257
|
}));
|
|
258
258
|
/**
|
|
259
|
-
* Determines whether an arbitrary value is a valid {@link BlockNode}
|
|
259
|
+
* Determines whether an arbitrary value is a valid {@link BlockNode} — a
|
|
260
260
|
* heading, paragraph, list, table, code block, blockquote, or thematic break,
|
|
261
261
|
* recursively validated.
|
|
262
262
|
*
|
|
263
263
|
* @remarks
|
|
264
|
-
* Total: never throws, even on cyclic or pathologically deep input
|
|
264
|
+
* Total: never throws, even on cyclic or pathologically deep input — every
|
|
265
265
|
* combinator involved (`unionOf`, `recordOf`, `arrayOf`, `lazyOf`) is
|
|
266
266
|
* throw-contained per the `@orkestrel/contract` guard contract.
|
|
267
267
|
* A list item's shape is inlined here (and in {@link isMarkdownNode}) rather
|
|
268
|
-
* than named separately
|
|
268
|
+
* than named separately — it is used at exactly these two sites.
|
|
269
269
|
*
|
|
270
270
|
* @param value - The value to test
|
|
271
271
|
* @returns True if `value` is a well-formed {@link BlockNode}; false otherwise
|
|
@@ -307,16 +307,16 @@ var isBlockNode = unionOf(recordOf({
|
|
|
307
307
|
children: arrayOf(lazyOf(() => isBlockNode))
|
|
308
308
|
}), recordOf({ element: literalOf("thematicBreak") }));
|
|
309
309
|
/**
|
|
310
|
-
* Determines whether an arbitrary value is a valid {@link MarkdownNode}
|
|
310
|
+
* Determines whether an arbitrary value is a valid {@link MarkdownNode} — the
|
|
311
311
|
* {@link MarkdownDocument} root, a {@link BlockNode}, a {@link ListItemNode}, or
|
|
312
312
|
* an {@link InlineNode}, recursively validated.
|
|
313
313
|
*
|
|
314
314
|
* @remarks
|
|
315
|
-
* Total: never throws, even on cyclic or pathologically deep input
|
|
315
|
+
* Total: never throws, even on cyclic or pathologically deep input — every
|
|
316
316
|
* combinator involved (`unionOf`, `recordOf`, `arrayOf`, `lazyOf`) is
|
|
317
317
|
* throw-contained per the `@orkestrel/contract` guard contract.
|
|
318
318
|
* A list item's shape is inlined here (and in {@link isBlockNode}) rather than
|
|
319
|
-
* named separately
|
|
319
|
+
* named separately — it is used at exactly these two sites.
|
|
320
320
|
*
|
|
321
321
|
* @param value - The value to test
|
|
322
322
|
* @returns True if `value` is a well-formed {@link MarkdownNode}; false otherwise
|
|
@@ -334,12 +334,12 @@ var isMarkdownNode = unionOf(lazyOf(() => isMarkdownDocument), lazyOf(() => isBl
|
|
|
334
334
|
children: arrayOf(lazyOf(() => isBlockNode))
|
|
335
335
|
}), lazyOf(() => isInlineNode));
|
|
336
336
|
/**
|
|
337
|
-
* Determines whether an arbitrary value is a valid {@link MarkdownDocument}
|
|
337
|
+
* Determines whether an arbitrary value is a valid {@link MarkdownDocument} —
|
|
338
338
|
* the parsed-AST root {@link parseDocument} returns, recursively
|
|
339
339
|
* validated.
|
|
340
340
|
*
|
|
341
341
|
* @remarks
|
|
342
|
-
* Total: never throws, even on cyclic or pathologically deep input
|
|
342
|
+
* Total: never throws, even on cyclic or pathologically deep input — every
|
|
343
343
|
* combinator involved (`recordOf`, `arrayOf`) is throw-contained per the
|
|
344
344
|
* `@orkestrel/contract` guard contract.
|
|
345
345
|
*
|
|
@@ -511,7 +511,8 @@ function parseBlocks(lines, depth, spans = /* @__PURE__ */ new Map(), end) {
|
|
|
511
511
|
}
|
|
512
512
|
/**
|
|
513
513
|
* Parses a markdown string into a typed {@link MarkdownDocument} AST through the
|
|
514
|
-
* block phase.
|
|
514
|
+
* block phase — the document half of what {@link parseProvenance} returns. Malformed
|
|
515
|
+
* markdown degrades to literal text, so the parse never throws.
|
|
515
516
|
*
|
|
516
517
|
* @param markdown - The markdown source to parse.
|
|
517
518
|
* @returns The parsed document.
|
|
@@ -526,7 +527,8 @@ function parseDocument(markdown) {
|
|
|
526
527
|
return document;
|
|
527
528
|
}
|
|
528
529
|
/**
|
|
529
|
-
* Parses a markdown string into a document and its original-source spans.
|
|
530
|
+
* Parses a markdown string into a document and its original-source spans. Malformed
|
|
531
|
+
* markdown degrades to literal text, so the parse never throws.
|
|
530
532
|
*
|
|
531
533
|
* @param markdown - The markdown source to parse.
|
|
532
534
|
* @returns The parsed document and its node-identity span map.
|
|
@@ -551,7 +553,8 @@ function parseProvenance(markdown) {
|
|
|
551
553
|
}
|
|
552
554
|
/**
|
|
553
555
|
* Parses inline markdown text (emphasis, code spans, links, images, and hard
|
|
554
|
-
* breaks) into inline AST nodes, coalescing adjacent text runs
|
|
556
|
+
* breaks) into inline AST nodes, coalescing adjacent text runs and reading no block
|
|
557
|
+
* structure. Malformed markdown degrades to literal text, so the parse never throws.
|
|
555
558
|
*
|
|
556
559
|
* @param text - The inline markdown text to parse.
|
|
557
560
|
* @returns The parsed inline nodes.
|
|
@@ -781,7 +784,7 @@ function normalizeParagraphLine(source, breaks) {
|
|
|
781
784
|
}], "");
|
|
782
785
|
}
|
|
783
786
|
/**
|
|
784
|
-
* Counts the leading space / tab characters on `line` (a tab counts as one)
|
|
787
|
+
* Counts the leading space / tab characters on `line` (a tab counts as one) — the
|
|
785
788
|
* indent that decides whether a list item's continuation belongs to the item.
|
|
786
789
|
*
|
|
787
790
|
* @param line - The line to measure
|
|
@@ -799,7 +802,7 @@ function countIndent(line) {
|
|
|
799
802
|
return count;
|
|
800
803
|
}
|
|
801
804
|
/**
|
|
802
|
-
* Checks whether `character` is whitespace under the emphasis flanking rule
|
|
805
|
+
* Checks whether `character` is whitespace under the emphasis flanking rule — a space, a
|
|
803
806
|
* tab, or a newline.
|
|
804
807
|
*
|
|
805
808
|
* @param character - The character to test
|
|
@@ -815,7 +818,7 @@ function isFlankingWhitespace(character) {
|
|
|
815
818
|
return character === " " || character === " " || character === "\n";
|
|
816
819
|
}
|
|
817
820
|
/**
|
|
818
|
-
* Checks whether `character` is escapable by a leading backslash
|
|
821
|
+
* Checks whether `character` is escapable by a leading backslash — the ASCII punctuation
|
|
819
822
|
* markdown gives meaning to (so `\*` becomes `*` but `\.` stays `\.`).
|
|
820
823
|
*
|
|
821
824
|
* @param character - The single character after a backslash
|
|
@@ -831,7 +834,7 @@ function isEscapable(character) {
|
|
|
831
834
|
return /[\\`*_{}[\]()#+\-.!>~|]/.test(character);
|
|
832
835
|
}
|
|
833
836
|
/**
|
|
834
|
-
* Checks whether `line` is blank
|
|
837
|
+
* Checks whether `line` is blank — empty, or containing only whitespace — the markdown
|
|
835
838
|
* definition of a blank line that block parsing uses to separate paragraphs, skip
|
|
836
839
|
* gaps, and end list continuations.
|
|
837
840
|
*
|
|
@@ -847,7 +850,7 @@ function isBlankLine(line) {
|
|
|
847
850
|
return isEmptyString(line.trim());
|
|
848
851
|
}
|
|
849
852
|
/**
|
|
850
|
-
* Checks whether `line` is a blockquote line (`>` optionally indented up to three spaces)
|
|
853
|
+
* Checks whether `line` is a blockquote line (`>` optionally indented up to three spaces) —
|
|
851
854
|
* its content is de-quoted by {@link stripQuote}.
|
|
852
855
|
*
|
|
853
856
|
* @param line - The candidate line
|
|
@@ -862,7 +865,7 @@ function isQuote(line) {
|
|
|
862
865
|
return /^\s{0,3}>/.test(line);
|
|
863
866
|
}
|
|
864
867
|
/**
|
|
865
|
-
* Checks whether `line` closes a fence opened by `marker`
|
|
868
|
+
* Checks whether `line` closes a fence opened by `marker` — the same fence character, a run
|
|
866
869
|
* at least as long, and nothing else but surrounding whitespace.
|
|
867
870
|
*
|
|
868
871
|
* @param line - The candidate closing line
|
|
@@ -888,7 +891,7 @@ function isFenceClose(line, marker) {
|
|
|
888
891
|
return index === line.length;
|
|
889
892
|
}
|
|
890
893
|
/**
|
|
891
|
-
* Checks whether `character` is a regex-`\s`-equivalent whitespace character
|
|
894
|
+
* Checks whether `character` is a regex-`\s`-equivalent whitespace character — the
|
|
892
895
|
* character class {@link isFenceClose}'s scan treats as surrounding padding.
|
|
893
896
|
*
|
|
894
897
|
* @param character - The single character to test, or `undefined` past the end of a line
|
|
@@ -904,7 +907,7 @@ function isFenceWhitespace(character) {
|
|
|
904
907
|
return character === " " || character === " " || character === "\n" || character === "\r" || character === "\f" || character === "\v";
|
|
905
908
|
}
|
|
906
909
|
/**
|
|
907
|
-
* Checks whether `line` is a thematic break (horizontal rule)
|
|
910
|
+
* Checks whether `line` is a thematic break (horizontal rule) — three or more of the same
|
|
908
911
|
* marker `-`, `*`, or `_` (optionally space-separated) and nothing else (`---`,
|
|
909
912
|
* `***`, `___`, `- - -`).
|
|
910
913
|
*
|
|
@@ -924,9 +927,9 @@ function isThematicBreak(line) {
|
|
|
924
927
|
return [...stripped].every((character) => character === marker);
|
|
925
928
|
}
|
|
926
929
|
/**
|
|
927
|
-
* Checks whether the pair (`header`, `delimiter`) opens a GFM table
|
|
930
|
+
* Checks whether the pair (`header`, `delimiter`) opens a GFM table — `delimiter` is a row of
|
|
928
931
|
* `|`-separated cells each matching `:?-+:?`, the GFM rule that a table requires a
|
|
929
|
-
* header row
|
|
932
|
+
* header row immediately followed by a delimiter row.
|
|
930
933
|
*
|
|
931
934
|
* @param header - The candidate header line
|
|
932
935
|
* @param delimiter - The line after it (the candidate delimiter)
|
|
@@ -1056,10 +1059,11 @@ function stripQuote(source) {
|
|
|
1056
1059
|
return sliceSource(source, (/^\s{0,3}>\s?/.exec(source.text)?.[0] ?? "").length, source.text.length);
|
|
1057
1060
|
}
|
|
1058
1061
|
/**
|
|
1059
|
-
* Splits one GFM table row into its cell strings
|
|
1060
|
-
*
|
|
1061
|
-
* empty leading / trailing cell
|
|
1062
|
-
* form from {@link splitTableSources}, which owns the
|
|
1062
|
+
* Splits one GFM table row into its cell strings — outer pipes are optional, a pipe
|
|
1063
|
+
* escaped by a leading backslash inside a cell is not a separator (it becomes a literal
|
|
1064
|
+
* pipe character), and the empty leading / trailing cell an outer pipe produces is
|
|
1065
|
+
* dropped. Derives the string form from {@link splitTableSources}, which owns the
|
|
1066
|
+
* escaped-pipe splitting rule.
|
|
1063
1067
|
*
|
|
1064
1068
|
* @param row - The raw table row line
|
|
1065
1069
|
* @returns The row's cells, in column order
|
|
@@ -1122,7 +1126,7 @@ function splitTableSources(row) {
|
|
|
1122
1126
|
return cells;
|
|
1123
1127
|
}
|
|
1124
1128
|
/**
|
|
1125
|
-
* Derives the per-column {@link TableAlign} list from a GFM delimiter row
|
|
1129
|
+
* Derives the per-column {@link TableAlign} list from a GFM delimiter row — `:---`
|
|
1126
1130
|
* left, `---:` right, `:---:` center, and `---` as the explicit no-alignment
|
|
1127
1131
|
* marker represented by `null`.
|
|
1128
1132
|
*
|
|
@@ -1146,8 +1150,8 @@ function delimiterToAlignments(delimiter) {
|
|
|
1146
1150
|
});
|
|
1147
1151
|
}
|
|
1148
1152
|
/**
|
|
1149
|
-
* Checks whether the line at `index` starts a
|
|
1150
|
-
* break / blockquote / list / table)
|
|
1153
|
+
* Checks whether the line at `index` starts a new block kind (heading / fence / thematic
|
|
1154
|
+
* break / blockquote / list / table) — the paragraph collector stops at such a line
|
|
1151
1155
|
* so a block following a paragraph without a blank line still parses (a trusted-input
|
|
1152
1156
|
* caller writing a `##` heading directly under a paragraph, with no intervening blank
|
|
1153
1157
|
* line).
|
|
@@ -1166,7 +1170,7 @@ function startsBlock(lines, index) {
|
|
|
1166
1170
|
return extractHeading(line) !== void 0 || extractFence(line) !== void 0 || isThematicBreak(line) || isQuote(line) || extractListItem(line) !== void 0 || isTableStart(line, lines[index + 1]);
|
|
1167
1171
|
}
|
|
1168
1172
|
/**
|
|
1169
|
-
* Resolves backslash escapes in a raw string to their literal characters
|
|
1173
|
+
* Resolves backslash escapes in a raw string to their literal characters — used for a
|
|
1170
1174
|
* link `href` (which is not otherwise inline-parsed) and any plain text run.
|
|
1171
1175
|
*
|
|
1172
1176
|
* @param text - The raw text possibly carrying `\x` escapes
|
|
@@ -1189,7 +1193,7 @@ function unescapeText(text) {
|
|
|
1189
1193
|
return out;
|
|
1190
1194
|
}
|
|
1191
1195
|
/**
|
|
1192
|
-
* Merges adjacent text nodes into one
|
|
1196
|
+
* Merges adjacent text nodes into one — the inline scanner emits a text node per
|
|
1193
1197
|
* unrecognized character, so coalescing keeps the AST clean and assertion-friendly.
|
|
1194
1198
|
*
|
|
1195
1199
|
* @param nodes - The inline nodes (possibly with adjacent text runs)
|
|
@@ -1228,7 +1232,7 @@ function coalesceText(nodes, spans) {
|
|
|
1228
1232
|
}
|
|
1229
1233
|
/**
|
|
1230
1234
|
* Scans an inline code span at `start` (a `` ` ``-run … a matching `` ` ``-run of the
|
|
1231
|
-
*
|
|
1235
|
+
* same length, the CommonMark rule that lets a span contain backticks). Returns the
|
|
1232
1236
|
* span's literal text + end index, or `undefined` when no matching closer exists (it
|
|
1233
1237
|
* then degrades to literal backticks).
|
|
1234
1238
|
*
|
|
@@ -1262,7 +1266,7 @@ function scanCode(source, start, to) {
|
|
|
1262
1266
|
}
|
|
1263
1267
|
}
|
|
1264
1268
|
/**
|
|
1265
|
-
* Locates a link `[text](href)` at `start`
|
|
1269
|
+
* Locates a link `[text](href)` at `start` — the text runs to a balanced `]`, then `(`
|
|
1266
1270
|
* must immediately follow and the destination runs to the matching `)` (both respect
|
|
1267
1271
|
* nested delimiters + escapes). Returns the label close and syntax end, or `undefined` when the shape
|
|
1268
1272
|
* does not hold (it then degrades to a literal `[`).
|
|
@@ -1320,7 +1324,7 @@ function locateLink(source, start, to) {
|
|
|
1320
1324
|
};
|
|
1321
1325
|
}
|
|
1322
1326
|
/**
|
|
1323
|
-
* Scans a link `[text](href)` at `start`
|
|
1327
|
+
* Scans a link `[text](href)` at `start` — the text runs to a balanced `]`, then `(`
|
|
1324
1328
|
* must immediately follow and the destination runs to the matching `)` (both respect
|
|
1325
1329
|
* nested delimiters + escapes) through {@link locateLink}, and returns the parsed node
|
|
1326
1330
|
* and end index. Returns `undefined` when the shape does not hold (it then degrades to
|
|
@@ -1353,7 +1357,7 @@ function scanLink(source, start, to, depth = 0) {
|
|
|
1353
1357
|
};
|
|
1354
1358
|
}
|
|
1355
1359
|
/**
|
|
1356
|
-
* Locates an emphasis run at `start` (`*` / `_`, doubled for strong)
|
|
1360
|
+
* Locates an emphasis run at `start` (`*` / `_`, doubled for strong) — finds the nearest
|
|
1357
1361
|
* matching closing run of the same marker + width while skipping complete nested
|
|
1358
1362
|
* runs from the other marker family, and requires non-space immediately inside both
|
|
1359
1363
|
* delimiters (the CommonMark flanking simplification that blocks `* x *`). Returns
|
|
@@ -1412,7 +1416,7 @@ function locateEmphasis(source, start, to) {
|
|
|
1412
1416
|
}
|
|
1413
1417
|
}
|
|
1414
1418
|
/**
|
|
1415
|
-
* Scans an emphasis run at `start` (`*` / `_`, doubled for strong)
|
|
1419
|
+
* Scans an emphasis run at `start` (`*` / `_`, doubled for strong) — finds the nearest
|
|
1416
1420
|
* matching closing run of the same marker + width while skipping complete nested runs
|
|
1417
1421
|
* from the other marker family, and requires non-space immediately inside both
|
|
1418
1422
|
* delimiters (the CommonMark flanking simplification that blocks `* x *`) through
|
|
@@ -1446,7 +1450,7 @@ function scanEmphasis(source, start, to, depth = 0) {
|
|
|
1446
1450
|
};
|
|
1447
1451
|
}
|
|
1448
1452
|
/**
|
|
1449
|
-
* Scans the window `[from, to)` of `source` into inline nodes
|
|
1453
|
+
* Scans the window `[from, to)` of `source` into inline nodes — the single recursive
|
|
1450
1454
|
* engine the inline phase runs on (emphasis, link text, and image alternative
|
|
1451
1455
|
* content recurse through it). Linear:
|
|
1452
1456
|
* each character is consumed once; a failed construct emits its opening character as
|
|
@@ -1458,10 +1462,10 @@ function scanEmphasis(source, start, to, depth = 0) {
|
|
|
1458
1462
|
* @param depth - The current inline-recursion depth (defaults to 0 at the entry point);
|
|
1459
1463
|
* incremented by one on every recursive descent {@link scanInlineSource} makes into
|
|
1460
1464
|
* itself for a link's text, an image's alternative content, or an emphasis run's
|
|
1461
|
-
* children. At {@link MAX_DEPTH} the window is never scanned for markup
|
|
1462
|
-
* a single literal text node
|
|
1465
|
+
* children. At {@link MAX_DEPTH} the window is never scanned for markup — it emits as
|
|
1466
|
+
* a single literal text node — so pathological nesting (`[[[[…`, `****…`) cannot
|
|
1463
1467
|
* exhaust the call stack.
|
|
1464
|
-
* @returns The parsed inline nodes (
|
|
1468
|
+
* @returns The parsed inline nodes (not yet coalesced)
|
|
1465
1469
|
*
|
|
1466
1470
|
* @example
|
|
1467
1471
|
* ```ts
|
|
@@ -2159,15 +2163,15 @@ function markdownToHTML(node) {
|
|
|
2159
2163
|
};
|
|
2160
2164
|
}
|
|
2161
2165
|
/**
|
|
2162
|
-
* Renders a {@link MarkdownNode} to its
|
|
2163
|
-
* projection of `renderHTML
|
|
2166
|
+
* Renders a {@link MarkdownNode} to its canonical markdown source — the inverse
|
|
2167
|
+
* projection of `renderHTML`. It is the serializer a `parse(renderMarkdown(doc))`
|
|
2164
2168
|
* round-trip is built on. Canonical forms: `*` / `**` emphasis at even emphasis
|
|
2165
2169
|
* nesting depths and `_` / `__` at odd depths, `- ` bullets, `N. ` sequential
|
|
2166
2170
|
* ordinals (from the list's `start`), `---` thematic breaks, fenced code blocks
|
|
2167
2171
|
* (backtick run widened past any 3+ backtick run inside the body), ATX headings,
|
|
2168
|
-
* `> `-prefixed blockquote lines, GFM tables (1-space-padded cells,
|
|
2169
|
-
*
|
|
2170
|
-
* and two-space hard breaks. A `text` node's literal content is backslash-escaped
|
|
2172
|
+
* `> `-prefixed blockquote lines, GFM tables (1-space-padded cells, a backslash
|
|
2173
|
+
* before each literal pipe, an alignment delimiter row), `[text](href)` links,
|
|
2174
|
+
* `` images, and two-space hard breaks. A `text` node's literal content is backslash-escaped
|
|
2171
2175
|
* wherever it would otherwise re-parse as markup, so parsing the rendered source
|
|
2172
2176
|
* returns the node it was rendered from.
|
|
2173
2177
|
*
|
|
@@ -2484,8 +2488,8 @@ function createProjection(parts = {}) {
|
|
|
2484
2488
|
};
|
|
2485
2489
|
}
|
|
2486
2490
|
/**
|
|
2487
|
-
* Trims the whitespace at the two ends of an inline run
|
|
2488
|
-
* leading text node and the trailing whitespace of a trailing one
|
|
2491
|
+
* Trims the whitespace at the two ends of an inline run — the leading whitespace of a
|
|
2492
|
+
* leading text node and the trailing whitespace of a trailing one — dropping either
|
|
2489
2493
|
* node when nothing survives.
|
|
2490
2494
|
*
|
|
2491
2495
|
* @remarks
|
|
@@ -2530,11 +2534,11 @@ function trimInlines(nodes) {
|
|
|
2530
2534
|
* ending or spent as a space.
|
|
2531
2535
|
*
|
|
2532
2536
|
* @remarks
|
|
2533
|
-
* A hard break is ` \n` in markdown source, so it survives a re-parse only
|
|
2537
|
+
* A hard break is ` \n` in markdown source, so it survives a re-parse only between
|
|
2534
2538
|
* two lines of content and only with no whitespace touching it: a leading or trailing
|
|
2535
2539
|
* break has no line to end, a run of breaks reads as one blank line (which would end
|
|
2536
2540
|
* the paragraph), and a space beside one is eaten by the parser's line trimming. Where
|
|
2537
|
-
* a break cannot be written at all
|
|
2541
|
+
* a break cannot be written at all — a heading and a table cell are one line each — it
|
|
2538
2542
|
* becomes the space it stood for.
|
|
2539
2543
|
*
|
|
2540
2544
|
* @param nodes - The inline run to normalize
|
|
@@ -2590,14 +2594,14 @@ function normalizeInlines(nodes, breaks) {
|
|
|
2590
2594
|
return coalesceText(out);
|
|
2591
2595
|
}
|
|
2592
2596
|
/**
|
|
2593
|
-
* Combines the projections of one node's children into the projection of that node
|
|
2597
|
+
* Combines the projections of one node's children into the projection of that node —
|
|
2594
2598
|
* the single place inline runs become paragraphs, so no ancestor has to decide it
|
|
2595
2599
|
* twice.
|
|
2596
2600
|
*
|
|
2597
2601
|
* @remarks
|
|
2598
2602
|
* A child is either inline or block, never both, so merging preserves source order
|
|
2599
2603
|
* exactly: an inline run is held pending until a block arrives, then written out as a
|
|
2600
|
-
* paragraph
|
|
2604
|
+
* paragraph before it. That is what keeps `<div>lead<p>a</p></div>` two paragraphs in
|
|
2601
2605
|
* the order they were written rather than two lists that lost their interleaving. A
|
|
2602
2606
|
* pending run carrying no text is dropped rather than becoming a blank paragraph.
|
|
2603
2607
|
* Direct cells become one row before a later row, while cells/rows before a block
|
|
@@ -2677,7 +2681,7 @@ function mergeProjections(children) {
|
|
|
2677
2681
|
});
|
|
2678
2682
|
}
|
|
2679
2683
|
/**
|
|
2680
|
-
* Reads a projection as
|
|
2684
|
+
* Reads a projection as block content — the view a document, a blockquote, and a list
|
|
2681
2685
|
* item each need.
|
|
2682
2686
|
*
|
|
2683
2687
|
* @remarks
|
|
@@ -2723,7 +2727,7 @@ function projectionToBlocks(projection) {
|
|
|
2723
2727
|
return blocks;
|
|
2724
2728
|
}
|
|
2725
2729
|
/**
|
|
2726
|
-
* Reads a projection as
|
|
2730
|
+
* Reads a projection as inline content — the view a link, an emphasis, and a table cell
|
|
2727
2731
|
* each need.
|
|
2728
2732
|
*
|
|
2729
2733
|
* @remarks
|
|
@@ -2750,7 +2754,7 @@ function projectionToInlines(projection) {
|
|
|
2750
2754
|
}];
|
|
2751
2755
|
}
|
|
2752
2756
|
/**
|
|
2753
|
-
* Projects one HTML leaf
|
|
2757
|
+
* Projects one HTML leaf — a text node, a comment, or a doctype — to its
|
|
2754
2758
|
* {@link MarkdownProjection}.
|
|
2755
2759
|
*
|
|
2756
2760
|
* @remarks
|
|
@@ -2780,8 +2784,8 @@ function projectHTMLLeaf(leaf) {
|
|
|
2780
2784
|
});
|
|
2781
2785
|
}
|
|
2782
2786
|
/**
|
|
2783
|
-
* Projects one HTML container
|
|
2784
|
-
* already-computed projections.
|
|
2787
|
+
* Projects one HTML container — the document root or an element — from its children's
|
|
2788
|
+
* already-computed projections. The element mapping, and the only place that decides
|
|
2785
2789
|
* what an HTML tag becomes in markdown.
|
|
2786
2790
|
*
|
|
2787
2791
|
* @remarks
|
|
@@ -2793,13 +2797,13 @@ function projectHTMLLeaf(leaf) {
|
|
|
2793
2797
|
* inline runs wrapped in paragraphs; `ul` / `ol` a list, ordered from the tag and
|
|
2794
2798
|
* numbered from `start`; `th` / `td`, `tr`, and `table` a GFM table whose column
|
|
2795
2799
|
* alignment comes from each header-position cell's `align` attribute. Every
|
|
2796
|
-
* `UNSAFE_ELEMENTS` subtree contributes nothing at all, text included. Every
|
|
2800
|
+
* `UNSAFE_ELEMENTS` subtree contributes nothing at all, text included. Every other
|
|
2797
2801
|
* element unwraps to its children, so wrapper soup melts while its content keeps its
|
|
2798
|
-
* shape
|
|
2802
|
+
* shape — `<div><p>a</p><p>b</p></div>` stays two paragraphs.
|
|
2799
2803
|
*
|
|
2800
2804
|
* Three mappings read their own node rather than only their children's projections,
|
|
2801
2805
|
* because HTML puts the fact in a position rather than in a value: a `pre` takes its
|
|
2802
|
-
* body from its `code` child's raw text, and a list takes one item per `li` child
|
|
2806
|
+
* body from its `code` child's raw text, and a list takes one item per `li` child — so
|
|
2803
2807
|
* an empty `<li>` is still an item, while the whitespace between two of them is not.
|
|
2804
2808
|
* A `tr` accepts only its own direct cells, and a table derives the first `th`-bearing
|
|
2805
2809
|
* row from its own source structure.
|
|
@@ -3087,36 +3091,36 @@ function projectHTMLNode(node, children) {
|
|
|
3087
3091
|
return merged;
|
|
3088
3092
|
}
|
|
3089
3093
|
/**
|
|
3090
|
-
* Projects an `@orkestrel/html` {@link HTMLNode} into a {@link MarkdownDocument}
|
|
3094
|
+
* Projects an `@orkestrel/html` {@link HTMLNode} into a {@link MarkdownDocument} — the
|
|
3091
3095
|
* HTML→markdown direction, and the inverse of {@link markdownToHTML}.
|
|
3092
3096
|
*
|
|
3093
3097
|
* @remarks
|
|
3094
|
-
* **Engine.** One total handler table
|
|
3095
|
-
* {@link projectHTMLLeaf} for the leaves
|
|
3098
|
+
* **Engine.** One total handler table — {@link projectHTMLNode} for the containers,
|
|
3099
|
+
* {@link projectHTMLLeaf} for the leaves — folded by `@orkestrel/html`'s own `foldNode`, so
|
|
3096
3100
|
* depth capping, cycle safety, and bottom-up ordering are inherited rather than
|
|
3097
3101
|
* rebuilt. Total: hostile, cyclic, and pathologically deep input degrades instead of
|
|
3098
3102
|
* throwing.
|
|
3099
3103
|
*
|
|
3100
3104
|
* **Composed depth.** Both packages cap recursion at 64, and html's cap is reached
|
|
3101
|
-
* first: a document nested past it projects to a chain bounded by
|
|
3105
|
+
* first: a document nested past it projects to a chain bounded by that cap, with the
|
|
3102
3106
|
* content below it truncated before markdown ever sees it. Since the projected chain
|
|
3103
3107
|
* can be a level or two deeper than {@link MAX_DEPTH}, the serializer's own cap can
|
|
3104
|
-
* then truncate again
|
|
3108
|
+
* then truncate again — so the anchor law that follows is a law within the depth budget, and
|
|
3105
3109
|
* beyond it only totality is promised.
|
|
3106
3110
|
*
|
|
3107
3111
|
* **Safety.** Every `href` and `src` is re-sanitized through
|
|
3108
3112
|
* `sanitizeURL(value, SAFE_URL_SCHEMES)` whether or not the AST was ever sanitized,
|
|
3109
3113
|
* because a hand-built one never was. A refused destination empties to `''` and the
|
|
3110
|
-
* link or image is
|
|
3114
|
+
* link or image is kept — `[text]()` — because a bad URL is no reason to lose the words
|
|
3111
3115
|
* around it. An `UNSAFE_ELEMENTS` subtree contributes nothing at all, text included, so
|
|
3112
3116
|
* a `script` body can never resurface as prose.
|
|
3113
3117
|
*
|
|
3114
3118
|
* **The anchor law.** HTML→markdown is lossy, so the fixpoint that matters is the
|
|
3115
|
-
*
|
|
3119
|
+
* projected AST, not the input bytes:
|
|
3116
3120
|
* `parseDocument(renderMarkdown(htmlToMarkdown(x)))` deep-equals `htmlToMarkdown(x)`.
|
|
3117
3121
|
* The projection therefore emits canonical markdown shapes rather than literal
|
|
3118
|
-
* translations
|
|
3119
|
-
* break only where a line can end
|
|
3122
|
+
* translations — whitespace collapsed, edges trimmed, a blank paragraph dropped, a hard
|
|
3123
|
+
* break only where a line can end — because a shape markdown cannot write back is a
|
|
3120
3124
|
* shape this projection has no business producing.
|
|
3121
3125
|
*
|
|
3122
3126
|
* @param node - The HTML document or bare node to project
|
|
@@ -3143,7 +3147,7 @@ function htmlToMarkdown(node) {
|
|
|
3143
3147
|
};
|
|
3144
3148
|
}
|
|
3145
3149
|
/**
|
|
3146
|
-
* Walks a {@link MarkdownNode} depth-first, pre-order, root-inclusive
|
|
3150
|
+
* Walks a {@link MarkdownNode} depth-first, pre-order, root-inclusive — yields
|
|
3147
3151
|
* the node itself, then recurses into its children (block children, list items,
|
|
3148
3152
|
* image/link inline children, table header/row cells' inline nodes) in walk order.
|
|
3149
3153
|
*
|
|
@@ -3206,16 +3210,16 @@ function* walkNodes(node) {
|
|
|
3206
3210
|
}
|
|
3207
3211
|
}
|
|
3208
3212
|
/**
|
|
3209
|
-
* Folds a {@link MarkdownNode} into a `T` through a total catamorphism
|
|
3213
|
+
* Folds a {@link MarkdownNode} into a `T` through a total catamorphism — children are
|
|
3210
3214
|
* folded first (post-order), then the node's own {@link MarkdownHandler} is invoked
|
|
3211
3215
|
* with the already-folded children.
|
|
3212
3216
|
*
|
|
3213
3217
|
* @remarks
|
|
3214
|
-
* **Table contract.** A {@link TableNode} has no single `children` array
|
|
3218
|
+
* **Table contract.** A {@link TableNode} has no single `children` array — its cells
|
|
3215
3219
|
* live in `header` (one inline-node list per column) and `rows` (a list of such
|
|
3216
|
-
* rows). The `table` handler receives
|
|
3217
|
-
* walk order across
|
|
3218
|
-
* every body row's cells' inline nodes (row order, then column order)
|
|
3220
|
+
* rows). The `table` handler receives one folded `T` per inline node, flattened in
|
|
3221
|
+
* walk order across all cells — every header cell's inline nodes (column order), then
|
|
3222
|
+
* every body row's cells' inline nodes (row order, then column order) — and reads
|
|
3219
3223
|
* `node.header[c].length` / `node.rows[r][c].length` off the table node itself to
|
|
3220
3224
|
* recover cell boundaries within the flat list.
|
|
3221
3225
|
*
|
|
@@ -3358,10 +3362,10 @@ function foldNode(node, handlers, depth) {
|
|
|
3358
3362
|
}
|
|
3359
3363
|
}
|
|
3360
3364
|
/**
|
|
3361
|
-
* Rewrites a {@link MarkdownDocument} bottom-up (copy-on-write)
|
|
3365
|
+
* Rewrites a {@link MarkdownDocument} bottom-up (copy-on-write) — each node's children
|
|
3362
3366
|
* are rewritten first (post-order), then `rewrite` is applied to the node itself; the
|
|
3363
|
-
* document
|
|
3364
|
-
* always holds). A table's inline cells and a list's items
|
|
3367
|
+
* document root is never passed to `rewrite` (the `element: 'document'` invariant
|
|
3368
|
+
* always holds). A table's inline cells and a list's items are rewritten too.
|
|
3365
3369
|
*
|
|
3366
3370
|
* @remarks
|
|
3367
3371
|
* Never mutates `document`. An unchanged subtree keeps its input identity. A parent
|
|
@@ -3370,12 +3374,12 @@ function foldNode(node, handlers, depth) {
|
|
|
3370
3374
|
* whose `element` does not fit the slot it was called for (a block slot handed a
|
|
3371
3375
|
* non-{@link BlockNode}, an inline slot handed a non-{@link InlineNode}, a list-item
|
|
3372
3376
|
* slot handed a non-`listItem`), the ill-fitting result is discarded and the accepted
|
|
3373
|
-
* input child is reused
|
|
3377
|
+
* input child is reused — `rewriteDocument` stays total and never produces a
|
|
3374
3378
|
* structurally invalid document.
|
|
3375
3379
|
*
|
|
3376
3380
|
* Descent is capped at {@link MAX_DEPTH}, the same cap {@link walkNodes} and
|
|
3377
3381
|
* {@link foldNode} observe: at `depth >= MAX_DEPTH` the subtree is passed through
|
|
3378
|
-
*
|
|
3382
|
+
* unchanged (by reference, not rebuilt, and `rewrite` is not invoked on it) instead of
|
|
3379
3383
|
* recursing further, so a pathologically deep adopted document cannot exhaust the
|
|
3380
3384
|
* call stack. {@link MarkdownInterface.map} inherits this cap since it delegates here.
|
|
3381
3385
|
*
|
|
@@ -3642,7 +3646,7 @@ function rewriteDocument(document, rewrite) {
|
|
|
3642
3646
|
}
|
|
3643
3647
|
/**
|
|
3644
3648
|
* Concatenates the `value` / `code` content of every descendant text / code-span /
|
|
3645
|
-
* code-block node under `node`, including image alternative content, in walk order
|
|
3649
|
+
* code-block node under `node`, including image alternative content, in walk order —
|
|
3646
3650
|
* the plain-text projection of an AST (search indexing, word counts, a text-only
|
|
3647
3651
|
* preview).
|
|
3648
3652
|
*
|
|
@@ -3719,6 +3723,9 @@ function flattenText(node) {
|
|
|
3719
3723
|
* Renders a {@link MarkdownNode} to sanitized canonical HTML.
|
|
3720
3724
|
*
|
|
3721
3725
|
* @remarks
|
|
3726
|
+
* Sanitization is unconditional: the function takes one argument and declares no
|
|
3727
|
+
* options, so no call shape opts out of it.
|
|
3728
|
+
*
|
|
3722
3729
|
* Markdown widens `@orkestrel/html`'s attribute floor by exactly `src`, because image
|
|
3723
3730
|
* syntax is meaningless without its source. `src` is still a URL attribute, so the
|
|
3724
3731
|
* floor refuses `javascript:`, `data:`, `vbscript:`, and `file:` values. A stricter
|
|
@@ -3740,7 +3747,7 @@ function renderHTML(node) {
|
|
|
3740
3747
|
//#endregion
|
|
3741
3748
|
//#region src/core/shapers.ts
|
|
3742
3749
|
/**
|
|
3743
|
-
* Describes the shape of a {@link TextNode}
|
|
3750
|
+
* Describes the shape of a {@link TextNode} — a plain-text leaf inline run.
|
|
3744
3751
|
*
|
|
3745
3752
|
* @example
|
|
3746
3753
|
* ```ts
|
|
@@ -3756,7 +3763,7 @@ var textShape = objectShape({
|
|
|
3756
3763
|
value: stringShape()
|
|
3757
3764
|
});
|
|
3758
3765
|
/**
|
|
3759
|
-
* Describes the shape of a {@link CodeSpanNode}
|
|
3766
|
+
* Describes the shape of a {@link CodeSpanNode} — an inline code span (`` `code` ``).
|
|
3760
3767
|
*
|
|
3761
3768
|
* @example
|
|
3762
3769
|
* ```ts
|
|
@@ -3772,7 +3779,7 @@ var codeSpanShape = objectShape({
|
|
|
3772
3779
|
value: stringShape()
|
|
3773
3780
|
});
|
|
3774
3781
|
/**
|
|
3775
|
-
* Describes the shape of a {@link LineBreakNode}
|
|
3782
|
+
* Describes the shape of a {@link LineBreakNode} — a GFM hard line-break leaf.
|
|
3776
3783
|
*
|
|
3777
3784
|
* @example
|
|
3778
3785
|
* ```ts
|
|
@@ -3785,7 +3792,7 @@ var codeSpanShape = objectShape({
|
|
|
3785
3792
|
*/
|
|
3786
3793
|
var lineBreakShape = objectShape({ element: literalShape(["break"]) });
|
|
3787
3794
|
/**
|
|
3788
|
-
* Describes the shape of a {@link CodeBlockNode}
|
|
3795
|
+
* Describes the shape of a {@link CodeBlockNode} — a fenced code block. `lang` is
|
|
3789
3796
|
* optional (absent when the opening fence carries no info-string).
|
|
3790
3797
|
*
|
|
3791
3798
|
* @example
|
|
@@ -3804,7 +3811,7 @@ var codeBlockShape = objectShape({
|
|
|
3804
3811
|
code: stringShape()
|
|
3805
3812
|
});
|
|
3806
3813
|
/**
|
|
3807
|
-
* Describes the shape of a {@link ThematicBreakNode}
|
|
3814
|
+
* Describes the shape of a {@link ThematicBreakNode} — a horizontal rule. Carries no
|
|
3808
3815
|
* fields beyond its `element` discriminant.
|
|
3809
3816
|
*
|
|
3810
3817
|
* @example
|
|
@@ -3818,8 +3825,9 @@ var codeBlockShape = objectShape({
|
|
|
3818
3825
|
*/
|
|
3819
3826
|
var thematicBreakShape = objectShape({ element: literalShape(["thematicBreak"]) });
|
|
3820
3827
|
/**
|
|
3821
|
-
* Describes the shape of a {@link TableAlign}
|
|
3822
|
-
* literal.
|
|
3828
|
+
* Describes the shape of a {@link TableAlign} — the per-column GFM table alignment
|
|
3829
|
+
* literal. Absence is no member of it, so the shape refuses the `null` a bare `---`
|
|
3830
|
+
* delimiter takes in a `TableNode`'s `align` list.
|
|
3823
3831
|
*
|
|
3824
3832
|
* @example
|
|
3825
3833
|
* ```ts
|
|
@@ -3838,7 +3846,7 @@ var tableAlignShape = literalShape([
|
|
|
3838
3846
|
"center"
|
|
3839
3847
|
]);
|
|
3840
3848
|
/**
|
|
3841
|
-
* Describes the shape of {@link ListItemMatch}
|
|
3849
|
+
* Describes the shape of {@link ListItemMatch} — the parsed parts of a single list-item
|
|
3842
3850
|
* line the block phase's list detector returns. Fully non-recursive (no
|
|
3843
3851
|
* nested node fields), so every field shapes directly.
|
|
3844
3852
|
*
|
|
@@ -3867,14 +3875,14 @@ var listItemMatchShape = objectShape({
|
|
|
3867
3875
|
*
|
|
3868
3876
|
* @remarks
|
|
3869
3877
|
* - **Construction.** Given a `string`, the constructor runs {@link parseProvenance} (the
|
|
3870
|
-
* block phase then the inline phase) once, keeping the AST and a
|
|
3871
|
-
* that parse recorded. Given a {@link MarkdownDocument}, the document is adopted
|
|
3872
|
-
* and is
|
|
3873
|
-
* - **Provenance.** {@link span} reads the region of the
|
|
3878
|
+
* block phase then the inline phase) once, keeping the AST and a copy of the span map
|
|
3879
|
+
* that parse recorded. Given a {@link MarkdownDocument}, the document is adopted as-is
|
|
3880
|
+
* and is not re-validated — gate an untrusted value with `isMarkdownDocument` first.
|
|
3881
|
+
* - **Provenance.** {@link span} reads the region of the original constructor string a
|
|
3874
3882
|
* node was produced from, and it is handle-relative: a string-constructed handle exposes
|
|
3875
3883
|
* the regions of the nodes it parsed, an adopted document exposes none, and a node from
|
|
3876
3884
|
* another handle reports `undefined` here whatever that handle reports. Each call
|
|
3877
|
-
* returns a fresh value. A node reports the region
|
|
3885
|
+
* returns a fresh value. A node reports the region this handle holds for its identity,
|
|
3878
3886
|
* else the region of the direct input a rewrite named for it, else `undefined`: a text
|
|
3879
3887
|
* run the parse joined from adjacent scanner output reports the region enclosing its
|
|
3880
3888
|
* parts, and only a rewrite output that holds no region of its own and was assembled
|
|
@@ -3882,23 +3890,22 @@ var listItemMatchShape = objectShape({
|
|
|
3882
3890
|
* {@link map} carries provenance across the rewrite: an unchanged node keeps its
|
|
3883
3891
|
* region, a one-source replacement takes the region of the node it replaced, and a
|
|
3884
3892
|
* rebuilt parent takes its original's.
|
|
3885
|
-
* - **Immutable.** {@link map} never mutates the stored AST
|
|
3893
|
+
* - **Immutable.** {@link map} never mutates the stored AST — it returns a new `Markdown`
|
|
3886
3894
|
* instance; the document root invariant (`element: 'document'`) always holds. An
|
|
3887
3895
|
* identity rewrite still returns a new handle, over the same document tree.
|
|
3888
3896
|
* - **Traversal order.** {@link walk} and the `find` / `filter` / `reduce` queries built
|
|
3889
3897
|
* on it walk the AST depth-first, pre-order, root-inclusive (through {@link walkNodes});
|
|
3890
|
-
* `stream` is shallow
|
|
3898
|
+
* `stream` is shallow — only the document's direct block children.
|
|
3891
3899
|
*
|
|
3892
|
-
* @example
|
|
3900
|
+
* @example Construct from a string and narrow with a guard
|
|
3893
3901
|
* ```ts
|
|
3894
|
-
* import { Markdown, isHeadingNode
|
|
3902
|
+
* import { Markdown, isHeadingNode } from '@orkestrel/markdown'
|
|
3895
3903
|
*
|
|
3896
3904
|
* const markdown = new Markdown('# Title\n\nA **bold** [link](https://x.dev).')
|
|
3897
|
-
*
|
|
3898
|
-
*
|
|
3899
|
-
*
|
|
3900
|
-
* )
|
|
3901
|
-
* renderMarkdown(shouted.document) // '# TITLE\n\nA **BOLD** [LINK](https://x.dev).'
|
|
3905
|
+
* markdown.document.children[0] // { element: 'heading', level: 1, children: [...] }
|
|
3906
|
+
*
|
|
3907
|
+
* const heading = markdown.find(isHeadingNode) // HeadingNode | undefined, narrowed
|
|
3908
|
+
* if (heading !== undefined) heading.level // number — narrowed to HeadingNode
|
|
3902
3909
|
* ```
|
|
3903
3910
|
*/
|
|
3904
3911
|
var Markdown = class Markdown {
|
|
@@ -3943,7 +3950,7 @@ var Markdown = class Markdown {
|
|
|
3943
3950
|
};
|
|
3944
3951
|
}
|
|
3945
3952
|
/**
|
|
3946
|
-
* Returns
|
|
3953
|
+
* Returns the deep traversal — a lazy, depth-first, pre-order, root-inclusive generator
|
|
3947
3954
|
* over every {@link MarkdownNode} in the document. `find` / `filter` / `reduce`
|
|
3948
3955
|
* all iterate this single traversal.
|
|
3949
3956
|
*
|
|
@@ -3995,7 +4002,7 @@ var Markdown = class Markdown {
|
|
|
3995
4002
|
}
|
|
3996
4003
|
/**
|
|
3997
4004
|
* Returns a web-standard {@link ReadableStream} over the document's top-level block nodes
|
|
3998
|
-
* (shallow, source order)
|
|
4005
|
+
* (shallow, source order) — a fresh, pull-based source per call: one block is
|
|
3999
4006
|
* enqueued per `pull`, so a slow reader's backpressure is respected. Cancellable,
|
|
4000
4007
|
* async-iterable wherever the platform supports it (Node, Deno), and pipeable
|
|
4001
4008
|
* through any {@link TransformStream} / {@link WritableStream}.
|
|
@@ -4050,7 +4057,7 @@ var Markdown = class Markdown {
|
|
|
4050
4057
|
//#region src/core/factories.ts
|
|
4051
4058
|
/**
|
|
4052
4059
|
* Creates a stateful markdown handle from a markdown string or an already-parsed
|
|
4053
|
-
* {@link MarkdownDocument}
|
|
4060
|
+
* {@link MarkdownDocument} — a typed AST plus the query, rewrite, and fold operations
|
|
4054
4061
|
* {@link MarkdownInterface} exposes.
|
|
4055
4062
|
*
|
|
4056
4063
|
* @remarks
|
|
@@ -4058,9 +4065,9 @@ var Markdown = class Markdown {
|
|
|
4058
4065
|
* fenced code / blockquotes / thematic breaks) then an inline phase (emphasis /
|
|
4059
4066
|
* inline code / links / images / hard breaks) to build a render-agnostic
|
|
4060
4067
|
* {@link MarkdownDocument}. Given a
|
|
4061
|
-
* {@link MarkdownDocument}, adopts it
|
|
4068
|
+
* {@link MarkdownDocument}, adopts it as-is without re-validation — gate an untrusted
|
|
4062
4069
|
* value with `isMarkdownDocument` first. Pure + total parse (malformed markdown
|
|
4063
|
-
* degrades to text, never throws) and zero-dependency
|
|
4070
|
+
* degrades to text, never throws) and zero-dependency — a hand-written scanner, no
|
|
4064
4071
|
* regex-only structural parse, linear-time (no ReDoS).
|
|
4065
4072
|
*
|
|
4066
4073
|
* @param input - A markdown string to parse, or an already-parsed {@link MarkdownDocument}
|
|
@@ -4079,7 +4086,7 @@ function createMarkdown(input) {
|
|
|
4079
4086
|
}
|
|
4080
4087
|
/**
|
|
4081
4088
|
* Compiles the {@link textShape} into a {@link ContractInterface} for
|
|
4082
|
-
* {@link TextNode}
|
|
4089
|
+
* {@link TextNode} — a guard, coercing parser, JSON Schema, and seeded
|
|
4083
4090
|
* generator from one shape declaration.
|
|
4084
4091
|
*
|
|
4085
4092
|
* @returns A `TextNode` contract bundling `schema` / `is` / `parse` / `generate`
|
|
@@ -4097,7 +4104,7 @@ function createTextContract() {
|
|
|
4097
4104
|
}
|
|
4098
4105
|
/**
|
|
4099
4106
|
* Compiles the {@link codeSpanShape} into a {@link ContractInterface} for
|
|
4100
|
-
* {@link CodeSpanNode}
|
|
4107
|
+
* {@link CodeSpanNode} — a guard, coercing parser, JSON Schema, and seeded
|
|
4101
4108
|
* generator from one shape declaration.
|
|
4102
4109
|
*
|
|
4103
4110
|
* @returns A `CodeSpanNode` contract bundling `schema` / `is` / `parse` / `generate`
|
|
@@ -4131,7 +4138,7 @@ function createLineBreakContract() {
|
|
|
4131
4138
|
}
|
|
4132
4139
|
/**
|
|
4133
4140
|
* Compiles the {@link codeBlockShape} into a {@link ContractInterface} for
|
|
4134
|
-
* {@link CodeBlockNode}
|
|
4141
|
+
* {@link CodeBlockNode} — a guard, coercing parser, JSON Schema, and seeded
|
|
4135
4142
|
* generator from one shape declaration.
|
|
4136
4143
|
*
|
|
4137
4144
|
* @returns A `CodeBlockNode` contract bundling `schema` / `is` / `parse` / `generate`
|
|
@@ -4149,7 +4156,7 @@ function createCodeBlockContract() {
|
|
|
4149
4156
|
}
|
|
4150
4157
|
/**
|
|
4151
4158
|
* Compiles the {@link thematicBreakShape} into a {@link ContractInterface} for
|
|
4152
|
-
* {@link ThematicBreakNode}
|
|
4159
|
+
* {@link ThematicBreakNode} — a guard, coercing parser, JSON Schema, and
|
|
4153
4160
|
* seeded generator from one shape declaration.
|
|
4154
4161
|
*
|
|
4155
4162
|
* @returns A `ThematicBreakNode` contract bundling `schema` / `is` / `parse` / `generate`
|