@fuzdev/mdz 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +4 -0
  2. package/dist/Mdz.svelte +18 -9
  3. package/dist/Mdz.svelte.d.ts +9 -2
  4. package/dist/Mdz.svelte.d.ts.map +1 -1
  5. package/dist/MdzNodeView.svelte +129 -115
  6. package/dist/MdzNodeView.svelte.d.ts +4 -1
  7. package/dist/MdzNodeView.svelte.d.ts.map +1 -1
  8. package/dist/MdzPrecompiled.svelte +3 -3
  9. package/dist/MdzPrecompiled.svelte.d.ts.map +1 -1
  10. package/dist/MdzRoot.svelte +3 -3
  11. package/dist/MdzRoot.svelte.d.ts.map +1 -1
  12. package/dist/MdzStream.svelte +6 -6
  13. package/dist/MdzStream.svelte.d.ts.map +1 -1
  14. package/dist/MdzStreamNodeView.svelte +129 -118
  15. package/dist/MdzStreamNodeView.svelte.d.ts +4 -1
  16. package/dist/MdzStreamNodeView.svelte.d.ts.map +1 -1
  17. package/dist/context_helpers.js +1 -1
  18. package/dist/mdz.d.ts +6 -0
  19. package/dist/mdz.d.ts.map +1 -1
  20. package/dist/mdz.js +8 -0
  21. package/dist/mdz_contexts.d.ts.map +1 -1
  22. package/dist/mdz_debug_work.d.ts +38 -0
  23. package/dist/mdz_debug_work.d.ts.map +1 -0
  24. package/dist/mdz_debug_work.js +37 -0
  25. package/dist/mdz_helpers.d.ts +61 -0
  26. package/dist/mdz_helpers.d.ts.map +1 -1
  27. package/dist/mdz_helpers.js +74 -2
  28. package/dist/mdz_lexer.d.ts +12 -0
  29. package/dist/mdz_lexer.d.ts.map +1 -1
  30. package/dist/mdz_lexer.js +190 -40
  31. package/dist/mdz_opcodes.d.ts.map +1 -1
  32. package/dist/mdz_opcodes_to_nodes.d.ts.map +1 -1
  33. package/dist/mdz_opcodes_to_nodes.js +22 -17
  34. package/dist/mdz_stream_parser.d.ts.map +1 -1
  35. package/dist/mdz_stream_parser.js +8 -8
  36. package/dist/mdz_stream_parser_block.js +5 -5
  37. package/dist/mdz_stream_parser_inline.d.ts.map +1 -1
  38. package/dist/mdz_stream_parser_inline.js +22 -20
  39. package/dist/mdz_stream_parser_link.d.ts.map +1 -1
  40. package/dist/mdz_stream_parser_link.js +88 -56
  41. package/dist/mdz_stream_parser_list.d.ts.map +1 -1
  42. package/dist/mdz_stream_parser_list.js +23 -9
  43. package/dist/mdz_stream_parser_state.d.ts +29 -0
  44. package/dist/mdz_stream_parser_state.d.ts.map +1 -1
  45. package/dist/mdz_stream_parser_state.js +59 -10
  46. package/dist/mdz_stream_parser_table.d.ts +3 -2
  47. package/dist/mdz_stream_parser_table.d.ts.map +1 -1
  48. package/dist/mdz_stream_parser_table.js +24 -13
  49. package/dist/mdz_stream_parser_text.d.ts.map +1 -1
  50. package/dist/mdz_stream_parser_text.js +36 -25
  51. package/dist/mdz_stream_parser_url.js +7 -7
  52. package/dist/mdz_stream_state.svelte.d.ts.map +1 -1
  53. package/dist/mdz_to_svelte.d.ts +1 -1
  54. package/dist/mdz_to_svelte.d.ts.map +1 -1
  55. package/dist/mdz_to_svelte.js +28 -31
  56. package/dist/mdz_token_parser.d.ts.map +1 -1
  57. package/dist/mdz_token_parser.js +24 -25
  58. package/dist/svelte_preprocess_mdz.d.ts +2 -2
  59. package/dist/svelte_preprocess_mdz.d.ts.map +1 -1
  60. package/dist/svelte_preprocess_mdz.js +41 -47
  61. package/package.json +28 -40
  62. package/src/lib/context_helpers.ts +2 -2
  63. package/src/lib/mdz.ts +11 -2
  64. package/src/lib/mdz_contexts.ts +5 -5
  65. package/src/lib/mdz_debug_work.ts +38 -0
  66. package/src/lib/mdz_helpers.ts +126 -23
  67. package/src/lib/mdz_lexer.ts +243 -93
  68. package/src/lib/mdz_opcodes.ts +1 -1
  69. package/src/lib/mdz_opcodes_to_nodes.ts +35 -30
  70. package/src/lib/mdz_stream_parser.ts +17 -17
  71. package/src/lib/mdz_stream_parser_block.ts +14 -14
  72. package/src/lib/mdz_stream_parser_inline.ts +38 -34
  73. package/src/lib/mdz_stream_parser_link.ts +99 -66
  74. package/src/lib/mdz_stream_parser_list.ts +41 -29
  75. package/src/lib/mdz_stream_parser_state.ts +100 -28
  76. package/src/lib/mdz_stream_parser_table.ts +47 -36
  77. package/src/lib/mdz_stream_parser_text.ts +40 -39
  78. package/src/lib/mdz_stream_parser_url.ts +11 -11
  79. package/src/lib/mdz_stream_state.svelte.ts +8 -8
  80. package/src/lib/mdz_to_svelte.ts +38 -46
  81. package/src/lib/mdz_token_parser.ts +55 -58
  82. package/src/lib/svelte_preprocess_mdz.ts +92 -89
  83. package/src/lib/tsdoc_mdz.ts +1 -1
@@ -96,6 +96,23 @@ export const MIN_CODEBLOCK_BACKTICKS = 3; // Code blocks require minimum 3 backt
96
96
  export const MAX_LIST_NUMBER_DIGITS = 9; // Ordered list markers cap at 9 digits (CommonMark's cap)
97
97
  /** @nodocs */
98
98
  export const MAX_HEADING_LEVEL = 6; // Headings support levels 1-6
99
+ /**
100
+ * Cap on inline link/tag nesting depth. Reached at this many open
101
+ * `Link`/`Element`/`Component` containers, a further `[` or `<tag>` opener
102
+ * renders as literal text in both parsers rather than opening — a strict
103
+ * untrusted-content dialect's bound on pathological nesting (CommonMark
104
+ * permits such a limit). It bounds both the sync lexer's recursion depth (no
105
+ * stack overflow) and, with the failure memo, its revert re-scan work (linear
106
+ * instead of quadratic) on adversarial nested `[`/`<`. Deliberately far above
107
+ * any real content (inline nesting rarely exceeds a handful); only pathological
108
+ * input reaches it. Mirrored in the streaming parser so the two agree on where
109
+ * the cap bites. Delimiters (`**`/`_`/`~~`/`` ` ``) are uncounted: first-closer-wins
110
+ * keeps them from nesting deeply on their own, and mixed delimiter/link chains
111
+ * only deep-nest through the links, which this bounds.
112
+ *
113
+ * @nodocs
114
+ */
115
+ export const MAX_INLINE_NESTING_DEPTH = 100;
99
116
  /** @nodocs */
100
117
  export const HTTPS_PREFIX_LENGTH = 8; // Length of "https://"
101
118
  /** @nodocs */
@@ -495,6 +512,37 @@ const PATH_CHAR_TABLE = (() => {
495
512
  })();
496
513
  /** @nodocs */
497
514
  export const is_valid_path_char = (char_code) => char_code < 128 && PATH_CHAR_TABLE[char_code] === 1;
515
+ // Character classes for the plain-text run scanners (the hottest loops in
516
+ // both parsers) — one table load per character instead of a comparison chain.
517
+ // A zero class is the common case: the char can't end or interrupt a text run.
518
+ /** Always ends a text run: `` ` `` `*` `_` `~` `[` `]` `<`. @nodocs */
519
+ export const TEXT_CLASS_STOP = 1;
520
+ /** `\n` — run handling is context-dependent (soft break, paragraph break, block lookahead). @nodocs */
521
+ export const TEXT_CLASS_NEWLINE = 2;
522
+ /** `h`/`H` — potential URL scheme start, needs the prefix probe. @nodocs */
523
+ export const TEXT_CLASS_URL = 4;
524
+ /** `/` and `.` — potential path start, needs the boundary probe. @nodocs */
525
+ export const TEXT_CLASS_PATH = 8;
526
+ /** `\` — a literal-pipe escape candidate inside table cells only. @nodocs */
527
+ export const TEXT_CLASS_BACKSLASH = 16;
528
+ /** @nodocs */
529
+ export const TEXT_SCAN_CLASS = (() => {
530
+ const t = new Uint8Array(128);
531
+ t[BACKTICK] = TEXT_CLASS_STOP;
532
+ t[ASTERISK] = TEXT_CLASS_STOP;
533
+ t[UNDERSCORE] = TEXT_CLASS_STOP;
534
+ t[TILDE] = TEXT_CLASS_STOP;
535
+ t[LEFT_BRACKET] = TEXT_CLASS_STOP;
536
+ t[RIGHT_BRACKET] = TEXT_CLASS_STOP;
537
+ t[LEFT_ANGLE] = TEXT_CLASS_STOP;
538
+ t[NEWLINE] = TEXT_CLASS_NEWLINE;
539
+ t[H_LOWER] = TEXT_CLASS_URL;
540
+ t[H_UPPER] = TEXT_CLASS_URL;
541
+ t[SLASH] = TEXT_CLASS_PATH;
542
+ t[PERIOD] = TEXT_CLASS_PATH;
543
+ t[BACKSLASH] = TEXT_CLASS_BACKSLASH;
544
+ return t;
545
+ })();
498
546
  /**
499
547
  * Trim trailing punctuation from URL/path per RFC 3986 and GFM rules.
500
548
  * - Trims simple trailing: .,;:!?]
@@ -655,7 +703,7 @@ const VOID_ELEMENTS = new Set([
655
703
  'meta',
656
704
  'source',
657
705
  'track',
658
- 'wbr',
706
+ 'wbr'
659
707
  ]);
660
708
  /**
661
709
  * Check if an element name is an HTML void element (`br`, `img`, `hr`, ...).
@@ -699,6 +747,30 @@ export const mdz_resolve_relative_path = (reference, base) => {
699
747
  segments.push('');
700
748
  return segments.join('/');
701
749
  };
750
+ /**
751
+ * Classify a `Link` reference into how it should render. Pure — no rendering,
752
+ * no context; `base` is the resolved base path (or `undefined`).
753
+ * @nodocs
754
+ */
755
+ export const mdz_classify_link = (reference, link_type, base) => {
756
+ if (!mdz_is_safe_reference(reference))
757
+ return { kind: 'unsafe' };
758
+ if (link_type === 'internal') {
759
+ if (reference.startsWith('.') && base) {
760
+ return { kind: 'resolve', href: mdz_resolve_relative_path(reference, base) };
761
+ }
762
+ // fragment/query/relative/bare — `resolve()` only accepts absolute paths or
763
+ // route ids and throws on anything else
764
+ if (reference.startsWith('#') ||
765
+ reference.startsWith('?') ||
766
+ reference.startsWith('.') ||
767
+ !reference.startsWith('/')) {
768
+ return { kind: 'internal', href: reference };
769
+ }
770
+ return { kind: 'resolve', href: reference };
771
+ }
772
+ return { kind: 'external', href: reference };
773
+ };
702
774
  /**
703
775
  * Push a node into a children array, coalescing with the previous Text node.
704
776
  * Mutates `prev.content` and `prev.end` when both are Text, avoiding array growth
@@ -741,7 +813,7 @@ export const mdz_merge_adjacent_text = (nodes) => {
741
813
  type: 'Text',
742
814
  content: pending.content + node.content,
743
815
  start: pending.start,
744
- end: node.end,
816
+ end: node.end
745
817
  };
746
818
  }
747
819
  else {
@@ -141,6 +141,18 @@ export interface MdzTokenTableCellClose extends MdzTokenBase {
141
141
  export declare class MdzLexer {
142
142
  #private;
143
143
  constructor(text: string);
144
+ /**
145
+ * Rebind to a new text, so one lexer instance can be reused across inputs —
146
+ * the streaming table path reuses one across a table's rows via
147
+ * `MdzTableCellParser` instead of allocating a lexer (and its memo `Map`)
148
+ * per row. The search memos survive when the text is identical (they're
149
+ * valid over the immutable text — the common case for rows parsed from one
150
+ * buffered chunk) and clear otherwise. Per-parse cursor state is reset by
151
+ * `lex_table_cell` itself.
152
+ *
153
+ * @nodocs
154
+ */
155
+ rebind(text: string): void;
144
156
  tokenize(): Array<MdzToken>;
145
157
  /**
146
158
  * Tokenize a table cell's inline content `[cell_start, cell_end)` as a
@@ -1 +1 @@
1
- {"version":3,"file":"mdz_lexer.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_lexer.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mdz_lexer.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_lexer.ts"],"names":[],"mappings":"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"}
package/dist/mdz_lexer.js CHANGED
@@ -6,7 +6,8 @@
6
6
  *
7
7
  * @module
8
8
  */
9
- import { mdz_is_url, mdz_is_safe_reference, is_letter, is_tag_name_char, is_word_char, is_valid_path_char, mdz_trim_url_punctuation, BACKTICK, ASTERISK, UNDERSCORE, TILDE, NEWLINE, HYPHEN, HASH, SPACE, LEFT_ANGLE, RIGHT_ANGLE, SLASH, PERIOD, LEFT_BRACKET, LEFT_PAREN, RIGHT_BRACKET, A_UPPER, Z_UPPER, ZERO, ONE, NINE, HR_HYPHEN_COUNT, MIN_CODEBLOCK_BACKTICKS, MAX_HEADING_LEVEL, MAX_LIST_NUMBER_DIGITS, match_url_prefix_case_insensitive, is_at_absolute_path, is_at_relative_path, is_line_whitespace, mdz_match_blockquote_prefix, mdz_blockquote_line_has_content, mdz_blockquote_strip_width, mdz_remap_segments, mdz_split_table_row, mdz_parse_table_delimiter, PIPE, BACKSLASH, } from './mdz_helpers.js';
9
+ import { mdz_is_url, mdz_is_safe_reference, is_letter, is_tag_name_char, is_word_char, is_valid_path_char, mdz_trim_url_punctuation, BACKTICK, ASTERISK, UNDERSCORE, TILDE, NEWLINE, HYPHEN, HASH, SPACE, LEFT_ANGLE, RIGHT_ANGLE, SLASH, PERIOD, LEFT_BRACKET, LEFT_PAREN, RIGHT_BRACKET, A_UPPER, Z_UPPER, ZERO, ONE, NINE, HR_HYPHEN_COUNT, MIN_CODEBLOCK_BACKTICKS, MAX_HEADING_LEVEL, MAX_INLINE_NESTING_DEPTH, MAX_LIST_NUMBER_DIGITS, match_url_prefix_case_insensitive, is_at_absolute_path, is_at_relative_path, is_line_whitespace, mdz_match_blockquote_prefix, mdz_blockquote_line_has_content, mdz_blockquote_strip_width, mdz_remap_segments, mdz_split_table_row, mdz_parse_table_delimiter, PIPE, TEXT_CLASS_BACKSLASH, TEXT_CLASS_NEWLINE, TEXT_CLASS_STOP, TEXT_CLASS_URL, TEXT_SCAN_CLASS } from './mdz_helpers.js';
10
+ import { mdz_debug_work } from './mdz_debug_work.js';
10
11
  //
11
12
  // Lexer
12
13
  //
@@ -39,9 +40,67 @@ export class MdzLexer {
39
40
  * and `\|` unescapes to a literal pipe.
40
41
  */
41
42
  #in_table_cell = false;
43
+ /**
44
+ * Start offsets of markdown-link opens (`[`) that definitively failed — so
45
+ * a re-lex from before the `[` short-circuits to literal text instead of
46
+ * re-attempting (and re-descending into) the same failing link. Without it,
47
+ * the revert-and-re-tokenize failure path is exponential on nested `[`
48
+ * (a ~30-byte `[`×n input hangs). A link's outcome (does a valid `](ref)`
49
+ * follow?) is independent of `#max_search_index`, so `start` alone keys it.
50
+ * The outcome is a pure function of the immutable text, so the memo is valid
51
+ * for the whole lex and across table-cell ranges; cleared on `rebind`.
52
+ */
53
+ #failed_link_starts = new Set();
54
+ /**
55
+ * The tag analogue of `#failed_link_starts`, keyed `${start}:${bound}`. A
56
+ * tag's closer search respects `#max_search_index`, so the same `start` can
57
+ * fail under a narrow bound yet succeed under the wider one a re-lex uses —
58
+ * the bound is part of the key.
59
+ */
60
+ #failed_tag = new Set();
61
+ /**
62
+ * Current link/tag nesting depth — the number of open
63
+ * `link_text_open`/`tag_open` containers being recursed into. Bumped around
64
+ * `#tokenize_markdown_link`/`#tokenize_tag`'s children loops (not by
65
+ * delimiters, which can't nest deeply — see `MAX_INLINE_NESTING_DEPTH`). At
66
+ * the cap a further `[`/`<` renders literal instead of opening, bounding
67
+ * recursion depth (no stack overflow) and, with the failure memos, the
68
+ * revert re-scan (linear, not quadratic) on adversarial nesting.
69
+ */
70
+ #inline_depth = 0;
71
+ /**
72
+ * Start offsets of `[`/`<` openers suppressed by the nesting-depth cap
73
+ * (`#inline_depth >= MAX_INLINE_NESTING_DEPTH`) — position-keyed like
74
+ * `#failed_link_starts` so a re-lex from a shallower depth short-circuits to
75
+ * literal text rather than re-opening the construct (which would reintroduce
76
+ * the quadratic the cap removes). A suppression is bound-independent, so
77
+ * `start` alone keys it; cleared on `rebind`.
78
+ */
79
+ #depth_capped = new Set();
42
80
  constructor(text) {
43
81
  this.#text = text;
44
82
  }
83
+ /**
84
+ * Rebind to a new text, so one lexer instance can be reused across inputs —
85
+ * the streaming table path reuses one across a table's rows via
86
+ * `MdzTableCellParser` instead of allocating a lexer (and its memo `Map`)
87
+ * per row. The search memos survive when the text is identical (they're
88
+ * valid over the immutable text — the common case for rows parsed from one
89
+ * buffered chunk) and clear otherwise. Per-parse cursor state is reset by
90
+ * `lex_table_cell` itself.
91
+ *
92
+ * @nodocs
93
+ */
94
+ rebind(text) {
95
+ if (this.#text === text)
96
+ return;
97
+ this.#text = text;
98
+ this.#search_memo.clear();
99
+ this.#break_memo = null;
100
+ this.#failed_link_starts.clear();
101
+ this.#failed_tag.clear();
102
+ this.#depth_capped.clear();
103
+ }
45
104
  /**
46
105
  * Memoized `String#indexOf` over the immutable text.
47
106
  *
@@ -58,6 +117,10 @@ export class MdzLexer {
58
117
  return memo.result;
59
118
  }
60
119
  const result = this.#text.indexOf(needle, from);
120
+ if (mdz_debug_work.enabled) {
121
+ // scan span: `indexOf` examined [from, result] (found) or [from, EOF)
122
+ mdz_debug_work.total += (result === -1 ? this.#text.length : result) - from;
123
+ }
61
124
  if (memo === undefined) {
62
125
  this.#search_memo.set(needle, { from, result });
63
126
  }
@@ -110,6 +173,9 @@ export class MdzLexer {
110
173
  */
111
174
  lex_table_cell(cell_start, cell_end) {
112
175
  this.#tokens = [];
176
+ // depth is balanced back to 0 per cell, but pin it so a future early-return
177
+ // between an `#inline_depth++` and its decrement can't leak into the next cell
178
+ this.#inline_depth = 0;
113
179
  this.#tokenize_cell_inline(cell_start, cell_end);
114
180
  return this.#tokens;
115
181
  }
@@ -195,7 +261,7 @@ export class MdzLexer {
195
261
  type: 'heading_start',
196
262
  level: hash_count,
197
263
  start,
198
- end: this.#index, // end of "## " prefix
264
+ end: this.#index // end of "## " prefix
199
265
  });
200
266
  // Find end-of-line to bound nested tokenizers (prevents tag scanner from scanning past heading)
201
267
  let eol = this.#text.indexOf('\n', this.#index);
@@ -498,7 +564,12 @@ export class MdzLexer {
498
564
  */
499
565
  #tokenize_item_table(first, marker_indent) {
500
566
  const head = this.#match_item_table_head(first, marker_indent);
501
- this.#tokens.push({ type: 'table_open', align: head.align, start: first, end: head.header_end });
567
+ this.#tokens.push({
568
+ type: 'table_open',
569
+ align: head.align,
570
+ start: first,
571
+ end: head.header_end
572
+ });
502
573
  this.#emit_table_row(head.header_cells, true, first, head.header_end);
503
574
  this.#index = head.delim_end;
504
575
  let table_end = head.delim_end;
@@ -708,7 +779,7 @@ export class MdzLexer {
708
779
  start: i,
709
780
  marker_end: after,
710
781
  content_start: after,
711
- empty: true,
782
+ empty: true
712
783
  };
713
784
  }
714
785
  if (this.#text.charCodeAt(after) !== SPACE)
@@ -737,7 +808,7 @@ export class MdzLexer {
737
808
  start: i,
738
809
  marker_end: after,
739
810
  content_start: after,
740
- empty: true,
811
+ empty: true
741
812
  };
742
813
  }
743
814
  if (this.#text.charCodeAt(after) !== SPACE)
@@ -777,7 +848,7 @@ export class MdzLexer {
777
848
  type: 'list_open',
778
849
  ordered: marker.ordered,
779
850
  start: marker.start,
780
- end: marker.start,
851
+ end: marker.start
781
852
  });
782
853
  this.#list_item_open(marker);
783
854
  levels.push({ indent, ordered: marker.ordered });
@@ -786,7 +857,12 @@ export class MdzLexer {
786
857
  #list_item_open(marker) {
787
858
  const end = marker.empty ? marker.marker_end : marker.content_start;
788
859
  if (marker.ordered) {
789
- this.#tokens.push({ type: 'list_item_open', number: marker.number, start: marker.start, end });
860
+ this.#tokens.push({
861
+ type: 'list_item_open',
862
+ number: marker.number,
863
+ start: marker.start,
864
+ end
865
+ });
790
866
  }
791
867
  else {
792
868
  this.#tokens.push({ type: 'list_item_open', start: marker.start, end });
@@ -946,7 +1022,7 @@ export class MdzLexer {
946
1022
  : content_lines.join('\n');
947
1023
  return {
948
1024
  token: { type: 'codeblock', lang, content, start: first, end: m },
949
- end: m,
1025
+ end: m
950
1026
  };
951
1027
  }
952
1028
  }
@@ -968,7 +1044,7 @@ export class MdzLexer {
968
1044
  content = content.slice(0, -1);
969
1045
  return {
970
1046
  token: { type: 'codeblock', lang, content, start: first, end: this.#text.length },
971
- end: this.#text.length,
1047
+ end: this.#text.length
972
1048
  };
973
1049
  }
974
1050
  /**
@@ -1178,7 +1254,7 @@ export class MdzLexer {
1178
1254
  this.#tokens.push({
1179
1255
  type: 'italic_close',
1180
1256
  start: close_index,
1181
- end: close_index + 1,
1257
+ end: close_index + 1
1182
1258
  });
1183
1259
  this.#index = close_index + 1;
1184
1260
  }
@@ -1188,18 +1264,36 @@ export class MdzLexer {
1188
1264
  type: 'text',
1189
1265
  content: '_',
1190
1266
  start,
1191
- end: start + 1,
1267
+ end: start + 1
1192
1268
  };
1193
1269
  }
1194
1270
  }
1195
1271
  #tokenize_markdown_link() {
1196
1272
  const start = this.#index;
1273
+ // a link open at this offset already failed, or was suppressed by the
1274
+ // nesting cap — emit `[` as text instead of re-descending into the same
1275
+ // failing children (exponential on nested `[`); the short-circuit
1276
+ // reproduces the revert's exact net effect (`[` text, cursor at start + 1)
1277
+ if (this.#failed_link_starts.has(start) || this.#depth_capped.has(start)) {
1278
+ this.#index = start + 1;
1279
+ this.#emit_text('[', start);
1280
+ return;
1281
+ }
1282
+ // nesting-depth cap: too deep to open — render literal and record the
1283
+ // suppression so a shallower re-lex short-circuits here too
1284
+ if (this.#inline_depth >= MAX_INLINE_NESTING_DEPTH) {
1285
+ this.#depth_capped.add(start);
1286
+ this.#index = start + 1;
1287
+ this.#emit_text('[', start);
1288
+ return;
1289
+ }
1197
1290
  this.#index++; // consume [ (dispatch guarantees it)
1198
1291
  // Emit link_text_open
1199
1292
  this.#tokens.push({ type: 'link_text_open', start, end: this.#index });
1200
1293
  // Tokenize children until ] — only `]` delimits link text; a bare `)`
1201
1294
  // is ordinary content (matching the streaming parser, which has no `)`
1202
1295
  // dispatch case — the reference scan finds its own `)` after `](`)
1296
+ this.#inline_depth++;
1203
1297
  while (this.#index < this.#text.length) {
1204
1298
  if (this.#text.charCodeAt(this.#index) === RIGHT_BRACKET)
1205
1299
  break;
@@ -1207,6 +1301,7 @@ export class MdzLexer {
1207
1301
  break;
1208
1302
  this.#tokenize_inline();
1209
1303
  }
1304
+ this.#inline_depth--;
1210
1305
  // Check for ]
1211
1306
  if (this.#index >= this.#text.length || this.#text.charCodeAt(this.#index) !== RIGHT_BRACKET) {
1212
1307
  // Revert - remove link_text_open and all children tokens added
@@ -1218,7 +1313,7 @@ export class MdzLexer {
1218
1313
  this.#tokens.push({
1219
1314
  type: 'link_text_close',
1220
1315
  start: bracket_close_start,
1221
- end: this.#index,
1316
+ end: this.#index
1222
1317
  });
1223
1318
  // Check for (
1224
1319
  if (this.#index >= this.#text.length || this.#text.charCodeAt(this.#index) !== LEFT_PAREN) {
@@ -1259,10 +1354,17 @@ export class MdzLexer {
1259
1354
  reference,
1260
1355
  link_type,
1261
1356
  start: bracket_close_start + 1, // after ]
1262
- end: this.#index,
1357
+ end: this.#index
1263
1358
  });
1264
1359
  }
1265
1360
  #revert_tokens_from_link_open(start) {
1361
+ // record the failure so a re-lex from before this `[` short-circuits.
1362
+ // Keyed by `start` alone — unlike `#failed_tag`'s `start:bound` — because a
1363
+ // link's outcome is bound-independent: the `]`-terminator scan (the children
1364
+ // loop) runs to `text.length` and never consults `#max_search_index`, so a
1365
+ // wider-bound re-lex can't move it. The bound reaches nested tags inside the
1366
+ // link text, but re-attempting those wider never flips the link's net result.
1367
+ this.#failed_link_starts.add(start);
1266
1368
  // Find and remove link_text_open and all tokens after it
1267
1369
  let open_idx = -1;
1268
1370
  for (let i = this.#tokens.length - 1; i >= 0; i--) {
@@ -1279,6 +1381,30 @@ export class MdzLexer {
1279
1381
  }
1280
1382
  #tokenize_tag() {
1281
1383
  const start = this.#index;
1384
+ // short-circuit a re-attempt of a tag open that already failed at this
1385
+ // offset+bound — re-descending into the same failing children is
1386
+ // exponential on nested same-name tags. Keyed by
1387
+ // `#max_search_index` (captured as `bound`) because the closer search is
1388
+ // bound-sensitive, so a wider-bound re-lex can legitimately succeed. The
1389
+ // key string is built only when the memo is non-empty (adversarial
1390
+ // input); prose never allocates it. The short-circuit reproduces a
1391
+ // revert's exact net effect (`<` text, cursor at start + 1).
1392
+ const bound = this.#max_search_index;
1393
+ if (this.#depth_capped.has(start) ||
1394
+ (this.#failed_tag.size !== 0 && this.#failed_tag.has(`${start}:${bound}`))) {
1395
+ this.#index = start + 1;
1396
+ this.#emit_text('<', start);
1397
+ return;
1398
+ }
1399
+ // nesting-depth cap: too deep to open a tag — render literal and record
1400
+ // the suppression (position-keyed) so a shallower re-lex short-circuits
1401
+ // here too, keeping the revert re-scan linear
1402
+ if (this.#inline_depth >= MAX_INLINE_NESTING_DEPTH) {
1403
+ this.#depth_capped.add(start);
1404
+ this.#index = start + 1;
1405
+ this.#emit_text('<', start);
1406
+ return;
1407
+ }
1282
1408
  this.#index++; // consume <
1283
1409
  // Tag name must start with a letter
1284
1410
  if (this.#index >= this.#text.length || !is_letter(this.#text.charCodeAt(this.#index))) {
@@ -1309,12 +1435,13 @@ export class MdzLexer {
1309
1435
  name: tag_name,
1310
1436
  is_component,
1311
1437
  start,
1312
- end: this.#index,
1438
+ end: this.#index
1313
1439
  });
1314
1440
  return;
1315
1441
  }
1316
1442
  // Check for >
1317
1443
  if (this.#index >= this.#text.length || this.#text.charCodeAt(this.#index) !== RIGHT_ANGLE) {
1444
+ this.#failed_tag.add(`${start}:${bound}`);
1318
1445
  this.#index = start + 1;
1319
1446
  this.#emit_text('<', start);
1320
1447
  return;
@@ -1332,11 +1459,13 @@ export class MdzLexer {
1332
1459
  const search_limit = Math.min(this.#max_search_index, this.#text.length);
1333
1460
  const closing_tag_pos = this.#index_of(closing_tag, this.#index);
1334
1461
  if (closing_tag_pos === -1 || closing_tag_pos > search_limit) {
1462
+ this.#failed_tag.add(`${start}:${bound}`);
1335
1463
  this.#index = start + 1;
1336
1464
  this.#emit_text('<', start);
1337
1465
  return;
1338
1466
  }
1339
1467
  if (this.#has_paragraph_break_between(this.#index, closing_tag_pos)) {
1468
+ this.#failed_tag.add(`${start}:${bound}`);
1340
1469
  this.#index = start + 1;
1341
1470
  this.#emit_text('<', start);
1342
1471
  return;
@@ -1350,8 +1479,10 @@ export class MdzLexer {
1350
1479
  // the code span). Recomputed per iteration because a nested same-name
1351
1480
  // tag legitimately consumes the nearest closer (`<b>x<b>y</b></b>`).
1352
1481
  const saved_max = this.#max_search_index;
1482
+ this.#inline_depth++;
1353
1483
  while (this.#index < this.#text.length) {
1354
1484
  if (this.#match(closing_tag)) {
1485
+ this.#inline_depth--;
1355
1486
  this.#max_search_index = saved_max;
1356
1487
  const close_start = this.#index;
1357
1488
  this.#index += closing_tag.length;
@@ -1359,7 +1490,7 @@ export class MdzLexer {
1359
1490
  type: 'tag_close',
1360
1491
  name: tag_name,
1361
1492
  start: close_start,
1362
- end: this.#index,
1493
+ end: this.#index
1363
1494
  });
1364
1495
  return;
1365
1496
  }
@@ -1372,11 +1503,13 @@ export class MdzLexer {
1372
1503
  this.#max_search_index = next_close;
1373
1504
  this.#tokenize_inline();
1374
1505
  }
1506
+ this.#inline_depth--;
1375
1507
  // No valid closer left — revert: drop the open token and all consumed
1376
1508
  // children tokens, emit `<` as text, and re-lex from the next char
1377
1509
  // (the `#revert_tokens_from_link_open` pattern; the old fallthrough
1378
1510
  // left a dangling `tag_open` whose parser fallback discarded content)
1379
1511
  this.#max_search_index = saved_max;
1512
+ this.#failed_tag.add(`${start}:${bound}`);
1380
1513
  this.#tokens.splice(open_token_index);
1381
1514
  this.#index = start + 1;
1382
1515
  this.#emit_text('<', start);
@@ -1400,15 +1533,30 @@ export class MdzLexer {
1400
1533
  // content: parts accumulate around each stripped region
1401
1534
  let parts = null;
1402
1535
  let part_start = start;
1403
- while (this.#index < this.#text.length) {
1536
+ // in a table cell the trimmed cell end is a hard stop (no newline to
1537
+ // halt on mid-line) — hoisted to the loop bound
1538
+ const scan_end = this.#in_table_cell
1539
+ ? Math.min(this.#max_search_index, this.#text.length)
1540
+ : this.#text.length;
1541
+ while (this.#index < scan_end) {
1404
1542
  const char_code = this.#text.charCodeAt(this.#index);
1405
- // in a table cell: the trimmed cell end is a hard stop (no newline to
1406
- // halt on mid-line), and `\|` is a literal pipe (the only mdz escape,
1407
- // scoped here)
1408
- if (this.#in_table_cell) {
1409
- if (this.#index >= this.#max_search_index)
1410
- break;
1411
- if (char_code === BACKSLASH &&
1543
+ // one table load classifies the char; zero (the common case — the
1544
+ // char can't end or interrupt the run) skips the dispatch below.
1545
+ // `TEXT_SCAN_CLASS` is shared with the streaming scanner
1546
+ // (`consume_text_run` in `mdz_stream_parser_text.ts`); the two dispatch
1547
+ // bodies intentionally differ around it — this one handles `\|` escapes
1548
+ // and list-run strip, that one URL speculation across chunk boundaries.
1549
+ const char_class = char_code < 128 ? TEXT_SCAN_CLASS[char_code] : 0;
1550
+ if (char_class === 0) {
1551
+ this.#index++;
1552
+ continue;
1553
+ }
1554
+ // Stop at special characters
1555
+ if ((char_class & TEXT_CLASS_STOP) !== 0)
1556
+ break;
1557
+ if ((char_class & TEXT_CLASS_BACKSLASH) !== 0) {
1558
+ // `\|` is a literal pipe — the only mdz escape, scoped to table cells
1559
+ if (this.#in_table_cell &&
1412
1560
  this.#index + 1 < this.#max_search_index &&
1413
1561
  this.#text.charCodeAt(this.#index + 1) === PIPE) {
1414
1562
  (parts ??= []).push(this.#text.slice(part_start, this.#index));
@@ -1417,18 +1565,10 @@ export class MdzLexer {
1417
1565
  part_start = this.#index;
1418
1566
  continue;
1419
1567
  }
1568
+ this.#index++;
1569
+ continue;
1420
1570
  }
1421
- // Stop at special characters
1422
- if (char_code === BACKTICK ||
1423
- char_code === ASTERISK ||
1424
- char_code === UNDERSCORE ||
1425
- char_code === TILDE ||
1426
- char_code === LEFT_BRACKET ||
1427
- char_code === RIGHT_BRACKET ||
1428
- char_code === LEFT_ANGLE) {
1429
- break;
1430
- }
1431
- if (char_code === NEWLINE) {
1571
+ if ((char_class & TEXT_CLASS_NEWLINE) !== 0) {
1432
1572
  if (this.#list_run_strip) {
1433
1573
  if (this.#index >= this.#max_search_index)
1434
1574
  break; // the run's terminator
@@ -1465,15 +1605,25 @@ export class MdzLexer {
1465
1605
  break;
1466
1606
  }
1467
1607
  }
1608
+ this.#index++; // single newline — ordinary text
1609
+ continue;
1610
+ }
1611
+ // URL or internal path mid-text (class guard avoids probing every char)
1612
+ if ((char_class & TEXT_CLASS_URL) !== 0) {
1613
+ if (this.#is_at_url())
1614
+ break;
1615
+ }
1616
+ else if (char_code === SLASH) {
1617
+ if (is_at_absolute_path(this.#text, this.#index))
1618
+ break;
1468
1619
  }
1469
- // Check for URL or internal path mid-text (char code guard avoids startsWith on every char)
1470
- if (((char_code === 104 /* h */ || char_code === 72) /* H */ && this.#is_at_url()) ||
1471
- (char_code === SLASH && is_at_absolute_path(this.#text, this.#index)) ||
1472
- (char_code === PERIOD && is_at_relative_path(this.#text, this.#index))) {
1620
+ else if (is_at_relative_path(this.#text, this.#index)) {
1473
1621
  break;
1474
1622
  }
1475
1623
  this.#index++;
1476
1624
  }
1625
+ if (mdz_debug_work.enabled)
1626
+ mdz_debug_work.total += this.#index - start;
1477
1627
  // Ensure we always consume at least one character
1478
1628
  if (this.#index === start && this.#index < this.#text.length) {
1479
1629
  this.#index++;
@@ -1504,7 +1654,7 @@ export class MdzLexer {
1504
1654
  reference,
1505
1655
  link_type: 'external',
1506
1656
  start,
1507
- end: this.#index,
1657
+ end: this.#index
1508
1658
  });
1509
1659
  }
1510
1660
  #tokenize_auto_link_internal() {
@@ -1525,7 +1675,7 @@ export class MdzLexer {
1525
1675
  reference,
1526
1676
  link_type: 'internal',
1527
1677
  start,
1528
- end: this.#index,
1678
+ end: this.#index
1529
1679
  });
1530
1680
  }
1531
1681
  // -- Helper methods --
@@ -1 +1 @@
1
- {"version":3,"file":"mdz_opcodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mdz_opcodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"mdz_opcodes_to_nodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes_to_nodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EACX,OAAO,EASP,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAC,SAAS,EAAY,MAAM,kBAAkB,CAAC;AAyB3D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,KAAK,CAAC,SAAS,CAAC,KAAG,KAAK,CAAC,OAAO,CAgM7E,CAAC"}
1
+ {"version":3,"file":"mdz_opcodes_to_nodes.d.ts","sourceRoot":"../src/lib/","sources":["../src/lib/mdz_opcodes_to_nodes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EACX,OAAO,EASP,MAAM,UAAU,CAAC;AAClB,OAAO,KAAK,EAAE,SAAS,EAAa,MAAM,kBAAkB,CAAC;AAyB7D;;;GAGG;AACH,eAAO,MAAM,oBAAoB,GAAI,SAAS,KAAK,CAAC,SAAS,CAAC,KAAG,KAAK,CAAC,OAAO,CAgM7E,CAAC"}