@ai-react-markdown/engine 2.4.5 → 2.5.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.
package/dist/index.d.cts CHANGED
@@ -360,6 +360,16 @@ interface FreezeScanCheckpoint {
360
360
  piOpen: boolean;
361
361
  declOpen: boolean;
362
362
  cdataOpen: boolean;
363
+ /** parse5 BOGUS COMMENT open inside a real html-flow run: `<!` not
364
+ * followed by `--` / letter / `[CDATA[`, or `</` not followed by a
365
+ * letter (`<!\n`, `<!-`, `</\n`, `<//`) — eaten up to the next `>`;
366
+ * a `</div>` inside is comment text, not a close (2026-08-19 review r2
367
+ * P1-3: the balance zeroed past an open div). micromark has no such
368
+ * construct: those bytes are just html-block content. */
369
+ bogusOpen: boolean;
370
+ /** The RAW_TEXT_ELEMENTS element currently open (its content is text to
371
+ * parse5): tags and comment tokens are ignored until `</name>`. */
372
+ rawTextOpen: string | null;
363
373
  inFence: boolean;
364
374
  fenceChar: string;
365
375
  fenceLen: number;
@@ -457,6 +467,10 @@ interface FreezeScanCheckpoint {
457
467
  * — or may still be the same html block (root-level ` </div\n</div>`,
458
468
  * still garbage). Unknowable here → poison (oracle 3rd pass). */
459
469
  tagAcrossLinesIndent: number;
470
+ /** parse5's position inside that tag's attribute area at the end of the
471
+ * last scanned line (see TagAttrState). A quoted value left open swallows
472
+ * `>` and line endings; a `>` that arrives while `outside` ends the tag. */
473
+ tagAcrossLinesState: TagAttrState;
460
474
  /** The html-flow run since the last blank REALLY started as a micromark
461
475
  * html block (type 6 / type 1 / a paragraph-not-interrupting type 7) —
462
476
  * as opposed to `htmlFlowSinceBlank`, which any `<tag` / `</tag` line
@@ -469,6 +483,13 @@ interface FreezeScanCheckpoint {
469
483
  * blank; a type 6/1 start on a later line of a non-real run promotes it. */
470
484
  htmlFlowReal: boolean;
471
485
  }
486
+ /** parse5 tokenizer position inside a tag's attribute area, tracked across
487
+ * line endings for `tagAcrossLines` (2026-08-19 review r2 P1-2 / P2-3):
488
+ * `outside` = before/in an attribute name (a `"` here starts a NAME, not a
489
+ * value); `afterEq` = just past `=`; `unquoted` = in an unquoted value
490
+ * (whitespace ends it); `"` / `'` = inside a quoted value — `>` and line
491
+ * endings are value bytes there, only the matching quote leaves it. */
492
+ type TagAttrState = 'outside' | 'afterEq' | 'unquoted' | '"' | "'";
472
493
  declare function computeFreezeBoundary(text: string, options: FreezeBoundaryOptions, resume?: FreezeScanCheckpoint | null): FreezeScanResult;
473
494
 
474
495
  /**
@@ -692,38 +713,6 @@ interface DefLabels {
692
713
  linkLabels: Set<string>;
693
714
  }
694
715
  declare function collectDefLabels(source: string): DefLabels;
695
- /** Index just past the LAST blank line of `source`, or 0 if none.
696
- * Plain non-overlapping scan: for runs of blanks ("\n\n\n") this can land
697
- * a newline or two early, but the slack is whitespace-only and whitespace
698
- * can never satisfy DEF_LINE_START_RE, so the decision is identical.
699
- * @internal exported for tests only — the fast path is otherwise
700
- * indistinguishable from a full parse whose sets came out equal. */
701
- declare function lastRegionStart(source: string): number;
702
- /** A line that can START a definition, matched by the FULL def signature:
703
- * container prefixes (blockquote `>`, list bullets, ordered-list digits),
704
- * then `[label]` with the closing bracket IMMEDIATELY followed by `:` —
705
- * remark accepts a definition only with that adjacency (grammar-verified:
706
- * `[x]\n: url` and `[x] : url` are paragraphs, and `[a][b]: url` is a
707
- * reference because the label's first unescaped `]` isn't followed by
708
- * `:`). The label alternation admits escape pairs (`\]` stays inside the
709
- * label) and spans newlines (labels may soft-wrap; they cannot cross the
710
- * blank line that bounds the region). Both alternatives are disjoint, so
711
- * the scan is linear — no backtracking blowup on bracket-dense regions.
712
- *
713
- * Requiring the signature (not just a line-start `[`) is what keeps the
714
- * streaming-heavy shapes — bulleted link lists `- [t](u)`, task boxes
715
- * `- [x]`, reference lists `- [a][b]` — on the fast path; a bracket-only
716
- * probe made every append inside a blank-line-free link list pay a full
717
- * reparse (the measured Documents+smooth cliff). An INCOMPLETE def line
718
- * (`[x` with `]:` still in flight) correctly stays on the fast path too:
719
- * the parser sees no definition in it either, and the region re-check on
720
- * the completing append flips to the full parse exactly when the answer
721
- * can change. The `m` flag also matches at index 0, which is a true line
722
- * start (the region begins just past a blank line or at the document
723
- * start). Residual over-matching (e.g. `[x]:` inside an open code fence)
724
- * is safe: it costs a redundant full parse, never a wrong result.
725
- * @internal exported for tests only. */
726
- declare const DEF_LINE_START_RE: RegExp;
727
716
  interface DefLabelScanner {
728
717
  /** Equivalent to `collectDefLabels(source)` at every call, but cheap for
729
718
  * the streaming common case. Returns a REFERENCE-STABLE result while the
@@ -949,8 +938,18 @@ interface RegistryInternal extends Registry {
949
938
  * keeps the version monotonic-by-1-per-mount which makes debugging
950
939
  * easier). The granular `allocateSymbol` / `contributeLabels` methods
951
940
  * remain available for tests that need to exercise each step. */
952
- registerChunk(reactId: string, footnotes: Set<string>, links: Set<string>): symbol;
953
- allocateSymbol(reactId: string): symbol;
941
+ registerChunk(reactId: string, footnotes: Set<string>, links: Set<string>, documentIndex?: number): symbol;
942
+ /** `documentIndex` — the chunk's position in the DOCUMENT (not its mount
943
+ * order). Without it a chunk joins `chunkOrder` at the end, which is
944
+ * wrong for any list that unmounts and remounts chunks: a virtualized
945
+ * transcript scrolling back re-registers an earlier chunk after the ones
946
+ * still mounted, and footnote numbering plus "which chunk renders the
947
+ * aggregate footer" follow that order (2026-08-19 review r2 P2-10).
948
+ * Chunks that supply an index are kept sorted by it; chunks without one
949
+ * keep mount order after them. */
950
+ allocateSymbol(reactId: string, documentIndex?: number): symbol;
951
+ /** @internal Insert a chunk Symbol at its document position. */
952
+ _placeChunk(sym: symbol, documentIndex?: number): void;
954
953
  releaseSymbol(reactId: string): void;
955
954
  contributeLabels(symbol: symbol, footnotes: Set<string>, links: Set<string>): void;
956
955
  contributeChunkData(symbol: symbol, data: ChunkData): void;
@@ -958,6 +957,8 @@ interface RegistryInternal extends Registry {
958
957
  symbol: symbol;
959
958
  refcount: number;
960
959
  }>;
960
+ /** Symbol → the `documentIndex` its chunk supplied (see allocateSymbol). */
961
+ _chunkIndex: Map<symbol, number>;
961
962
  _subscribers: Set<() => void>;
962
963
  _notifyScheduled: boolean;
963
964
  _notify(): void;
@@ -1062,6 +1063,12 @@ type SanitizeSchema = typeof defaultSchema;
1062
1063
  * uncaught — there is no try/catch. Callers usually invoke this once at
1063
1064
  * module load, where a thrown error surfaces as a startup-time crash and
1064
1065
  * is the correct failure mode.
1066
+ * - **Widening is total.** Adding a tag to `tagNames` admits that element
1067
+ * with ALL of its semantics — `script`, `iframe`, `style`, `object`,
1068
+ * `form`, `base` re-enter the tree verbatim, and the two-gate URL story
1069
+ * in the README / SECURITY.md no longer describes your renderer. Prefer
1070
+ * allowing an attribute or a protocol over allowing an element; treat
1071
+ * any element you add as trusted input.
1065
1072
  *
1066
1073
  * @param modifier - Receives a deep clone of the library default. Mutate it
1067
1074
  * freely; either return the (possibly different) result, or return
@@ -1456,10 +1463,16 @@ declare function isFootnoteSection(node: Element): boolean;
1456
1463
 
1457
1464
  declare function normalizeId(s: string): string;
1458
1465
  /**
1459
- * Same as {@link normalizeId} plus resolution of backslash escapes.
1460
- * Used by PASS 0.5 substring pre-check against raw chunk source text:
1461
- * sources may write `[foo\]bar]` but the resulting label identifier is
1462
- * `foo]bar`, so we must unescape source before substring matching.
1466
+ * The form of raw chunk SOURCE that the PASS 0.5 substring pre-check compares
1467
+ * registry labels against. It is exactly {@link normalizeId}: micromark's
1468
+ * `identifier` (what the registry keys are built from) is the label's
1469
+ * source bytes with backslashes KEPT (`[^a\*b]` `a\*b`; only the `label`
1470
+ * field is unescaped), so the source must NOT be unescaped before matching
1471
+ * — an unescaping pass made every label holding an escaped punctuation
1472
+ * character miss the pre-check for good, and the cross-chunk reference
1473
+ * rendered as literal `[text][label]` (2026-08-19 review r2 P2-5; the
1474
+ * 2.4.5 narrowing to ASCII punctuation kept the wrong premise). Kept as a
1475
+ * named export for API stability.
1463
1476
  */
1464
1477
  declare function normalizeForMatch(s: string): string;
1465
1478
 
@@ -1647,25 +1660,6 @@ type AttributeEntry = NonNullable<NonNullable<Schema['attributes']>[string]>[num
1647
1660
  * is always tuple-form, so this branch is defensive only.
1648
1661
  */
1649
1662
  declare function mergeClassNameAllowlist(existing: ReadonlyArray<AttributeEntry> | undefined, extraClassNames: readonly string[]): AttributeEntry[];
1650
- /**
1651
- * The full sanitize schema used by the markdown renderer: extends
1652
- * `defaultSchema` to allow `<mark>`, the KaTeX math class names, and the
1653
- * three custom hast tags emitted by cross-chunk coordination handlers.
1654
- *
1655
- * **Owns its arrays and objects.** The shallow spread of `defaultSchema`
1656
- * alone would leave `attributes.a`, `attributes.img`, `protocols`,
1657
- * `ancestors`, and similar nested fields aliased to `rehype-sanitize`'s
1658
- * default singleton. A consumer who reasonably (but mistakenly) writes
1659
- * `sanitizeSchema.protocols.href.push('myapp')` would then poison
1660
- * `rehype-sanitize`'s `defaultSchema` for every other consumer in the
1661
- * process — a cross-package side-effect that's near-impossible to debug.
1662
- *
1663
- * One `cloneDeep` at module init breaks that aliasing without measurable
1664
- * cost (init-time only, single small object graph). The recommended
1665
- * extension API is still {@link extendSanitizeSchema}, which clones again
1666
- * per call; this layer just makes the exported singleton safe if someone
1667
- * skips the helper.
1668
- */
1669
1663
  declare const sanitizeSchema: Schema;
1670
1664
 
1671
1665
  /**
@@ -2027,4 +2021,4 @@ type RemendPreprocessorOptions = Omit<RemendOptions, 'katex' | 'inlineKatex'>;
2027
2021
  */
2028
2022
  declare function createRemendPreprocessor(options?: RemendPreprocessorOptions): AIMDContentPreprocessor;
2029
2023
 
2030
- export { type AIMDContentPreprocessor, type AIMarkdownEnginePlugin, type AIMarkdownEnginePluginName, type AdvanceOptions, type AdvanceResult, type AllowElement, type ChunkData, type Contribution, type CrossChunkHandlerOptions, DEFAULT_PAYLOAD, DEF_LINE_START_RE, type DefLabelScanner, type DefLabels, type Deprecation, type EnginePluginInternals, type EnginePluginStage, type ExtractContributionsOptions, type FootnoteDef, type FreezeBoundaryOptions, type IncrementalParseState, type IncrementalStage, type LinkDef, PIPELINE_STAGES, type ParsedMarkdown, type PhantomLabels, type PipelineOptions, type PipelineStage, type RefKind, type RefRecord, type Registry, type RegistryInternal, type RehypePlugins, type RehypeRebaseHashLinksOptions, type RemarkPlugins, type RemarkRehypeOptions, type RemendPreprocessorOptions, SENTINEL_FN_CONTENT, SENTINEL_LINK_URL, SMOOTH_STREAM_PACING_PRESETS, STAGE_MEASURE_PREFIX, type SanitizeSchema, type SmoothStreamController, type SmoothStreamOptions, type SmoothStreamPacing, type SmoothStreamPacingParams, type TransformContext, type UrlAttrKey, type UrlAttrTag, type UrlTransform, advanceIncrementalParse, attributeHastChildren, buildCoreRehypePlugins, buildCoreRemarkPlugins, buildCoreRemarkRehypeOptions, buildCrossChunkHandlers, buildPhantomSuffix, buildTransform, codePointSnapshots, collectDefLabels, computeFreezeBoundary, createDefLabelScanner, createFile, createIncrementalLatexPreprocessor, createProcessor, createRegistry, createRemendPreprocessor, createSmoothStreamController, defaultEnginePlugins, defaultUrlTransform, definitionList, extendSanitizeSchema, extractContributions, extractDefBodiesFromHast, footnoteSafeId, getEnginePluginInternals, hasLoneSurrogate, highlight, isFootnoteSection, lastRegionStart, measureStage, mergeClassNameAllowlist, normalizeForMatch, normalizeId, pangu, parseStage, phantomSuffixCloser, preprocessAIMDContent, preprocessLaTeX, rehypeFooterAdorn, rehypeRebaseHashLinks, removeComments, sanitizeCrossChunkUrl, sanitizeSchema, shortenDocumentId, smartypants, sourceIdFromFootnoteLiId, splitByProtectedRegions, subscribeStageTimings, transformStage, withDefs };
2024
+ export { type AIMDContentPreprocessor, type AIMarkdownEnginePlugin, type AIMarkdownEnginePluginName, type AdvanceOptions, type AdvanceResult, type AllowElement, type ChunkData, type Contribution, type CrossChunkHandlerOptions, DEFAULT_PAYLOAD, type DefLabelScanner, type DefLabels, type Deprecation, type EnginePluginInternals, type EnginePluginStage, type ExtractContributionsOptions, type FootnoteDef, type FreezeBoundaryOptions, type IncrementalParseState, type IncrementalStage, type LinkDef, PIPELINE_STAGES, type ParsedMarkdown, type PhantomLabels, type PipelineOptions, type PipelineStage, type RefKind, type RefRecord, type Registry, type RegistryInternal, type RehypePlugins, type RehypeRebaseHashLinksOptions, type RemarkPlugins, type RemarkRehypeOptions, type RemendPreprocessorOptions, SENTINEL_FN_CONTENT, SENTINEL_LINK_URL, SMOOTH_STREAM_PACING_PRESETS, STAGE_MEASURE_PREFIX, type SanitizeSchema, type SmoothStreamController, type SmoothStreamOptions, type SmoothStreamPacing, type SmoothStreamPacingParams, type TransformContext, type UrlAttrKey, type UrlAttrTag, type UrlTransform, advanceIncrementalParse, attributeHastChildren, buildCoreRehypePlugins, buildCoreRemarkPlugins, buildCoreRemarkRehypeOptions, buildCrossChunkHandlers, buildPhantomSuffix, buildTransform, codePointSnapshots, collectDefLabels, computeFreezeBoundary, createDefLabelScanner, createFile, createIncrementalLatexPreprocessor, createProcessor, createRegistry, createRemendPreprocessor, createSmoothStreamController, defaultEnginePlugins, defaultUrlTransform, definitionList, extendSanitizeSchema, extractContributions, extractDefBodiesFromHast, footnoteSafeId, getEnginePluginInternals, hasLoneSurrogate, highlight, isFootnoteSection, measureStage, mergeClassNameAllowlist, normalizeForMatch, normalizeId, pangu, parseStage, phantomSuffixCloser, preprocessAIMDContent, preprocessLaTeX, rehypeFooterAdorn, rehypeRebaseHashLinks, removeComments, sanitizeCrossChunkUrl, sanitizeSchema, shortenDocumentId, smartypants, sourceIdFromFootnoteLiId, splitByProtectedRegions, subscribeStageTimings, transformStage, withDefs };
package/dist/index.d.ts CHANGED
@@ -360,6 +360,16 @@ interface FreezeScanCheckpoint {
360
360
  piOpen: boolean;
361
361
  declOpen: boolean;
362
362
  cdataOpen: boolean;
363
+ /** parse5 BOGUS COMMENT open inside a real html-flow run: `<!` not
364
+ * followed by `--` / letter / `[CDATA[`, or `</` not followed by a
365
+ * letter (`<!\n`, `<!-`, `</\n`, `<//`) — eaten up to the next `>`;
366
+ * a `</div>` inside is comment text, not a close (2026-08-19 review r2
367
+ * P1-3: the balance zeroed past an open div). micromark has no such
368
+ * construct: those bytes are just html-block content. */
369
+ bogusOpen: boolean;
370
+ /** The RAW_TEXT_ELEMENTS element currently open (its content is text to
371
+ * parse5): tags and comment tokens are ignored until `</name>`. */
372
+ rawTextOpen: string | null;
363
373
  inFence: boolean;
364
374
  fenceChar: string;
365
375
  fenceLen: number;
@@ -457,6 +467,10 @@ interface FreezeScanCheckpoint {
457
467
  * — or may still be the same html block (root-level ` </div\n</div>`,
458
468
  * still garbage). Unknowable here → poison (oracle 3rd pass). */
459
469
  tagAcrossLinesIndent: number;
470
+ /** parse5's position inside that tag's attribute area at the end of the
471
+ * last scanned line (see TagAttrState). A quoted value left open swallows
472
+ * `>` and line endings; a `>` that arrives while `outside` ends the tag. */
473
+ tagAcrossLinesState: TagAttrState;
460
474
  /** The html-flow run since the last blank REALLY started as a micromark
461
475
  * html block (type 6 / type 1 / a paragraph-not-interrupting type 7) —
462
476
  * as opposed to `htmlFlowSinceBlank`, which any `<tag` / `</tag` line
@@ -469,6 +483,13 @@ interface FreezeScanCheckpoint {
469
483
  * blank; a type 6/1 start on a later line of a non-real run promotes it. */
470
484
  htmlFlowReal: boolean;
471
485
  }
486
+ /** parse5 tokenizer position inside a tag's attribute area, tracked across
487
+ * line endings for `tagAcrossLines` (2026-08-19 review r2 P1-2 / P2-3):
488
+ * `outside` = before/in an attribute name (a `"` here starts a NAME, not a
489
+ * value); `afterEq` = just past `=`; `unquoted` = in an unquoted value
490
+ * (whitespace ends it); `"` / `'` = inside a quoted value — `>` and line
491
+ * endings are value bytes there, only the matching quote leaves it. */
492
+ type TagAttrState = 'outside' | 'afterEq' | 'unquoted' | '"' | "'";
472
493
  declare function computeFreezeBoundary(text: string, options: FreezeBoundaryOptions, resume?: FreezeScanCheckpoint | null): FreezeScanResult;
473
494
 
474
495
  /**
@@ -692,38 +713,6 @@ interface DefLabels {
692
713
  linkLabels: Set<string>;
693
714
  }
694
715
  declare function collectDefLabels(source: string): DefLabels;
695
- /** Index just past the LAST blank line of `source`, or 0 if none.
696
- * Plain non-overlapping scan: for runs of blanks ("\n\n\n") this can land
697
- * a newline or two early, but the slack is whitespace-only and whitespace
698
- * can never satisfy DEF_LINE_START_RE, so the decision is identical.
699
- * @internal exported for tests only — the fast path is otherwise
700
- * indistinguishable from a full parse whose sets came out equal. */
701
- declare function lastRegionStart(source: string): number;
702
- /** A line that can START a definition, matched by the FULL def signature:
703
- * container prefixes (blockquote `>`, list bullets, ordered-list digits),
704
- * then `[label]` with the closing bracket IMMEDIATELY followed by `:` —
705
- * remark accepts a definition only with that adjacency (grammar-verified:
706
- * `[x]\n: url` and `[x] : url` are paragraphs, and `[a][b]: url` is a
707
- * reference because the label's first unescaped `]` isn't followed by
708
- * `:`). The label alternation admits escape pairs (`\]` stays inside the
709
- * label) and spans newlines (labels may soft-wrap; they cannot cross the
710
- * blank line that bounds the region). Both alternatives are disjoint, so
711
- * the scan is linear — no backtracking blowup on bracket-dense regions.
712
- *
713
- * Requiring the signature (not just a line-start `[`) is what keeps the
714
- * streaming-heavy shapes — bulleted link lists `- [t](u)`, task boxes
715
- * `- [x]`, reference lists `- [a][b]` — on the fast path; a bracket-only
716
- * probe made every append inside a blank-line-free link list pay a full
717
- * reparse (the measured Documents+smooth cliff). An INCOMPLETE def line
718
- * (`[x` with `]:` still in flight) correctly stays on the fast path too:
719
- * the parser sees no definition in it either, and the region re-check on
720
- * the completing append flips to the full parse exactly when the answer
721
- * can change. The `m` flag also matches at index 0, which is a true line
722
- * start (the region begins just past a blank line or at the document
723
- * start). Residual over-matching (e.g. `[x]:` inside an open code fence)
724
- * is safe: it costs a redundant full parse, never a wrong result.
725
- * @internal exported for tests only. */
726
- declare const DEF_LINE_START_RE: RegExp;
727
716
  interface DefLabelScanner {
728
717
  /** Equivalent to `collectDefLabels(source)` at every call, but cheap for
729
718
  * the streaming common case. Returns a REFERENCE-STABLE result while the
@@ -949,8 +938,18 @@ interface RegistryInternal extends Registry {
949
938
  * keeps the version monotonic-by-1-per-mount which makes debugging
950
939
  * easier). The granular `allocateSymbol` / `contributeLabels` methods
951
940
  * remain available for tests that need to exercise each step. */
952
- registerChunk(reactId: string, footnotes: Set<string>, links: Set<string>): symbol;
953
- allocateSymbol(reactId: string): symbol;
941
+ registerChunk(reactId: string, footnotes: Set<string>, links: Set<string>, documentIndex?: number): symbol;
942
+ /** `documentIndex` — the chunk's position in the DOCUMENT (not its mount
943
+ * order). Without it a chunk joins `chunkOrder` at the end, which is
944
+ * wrong for any list that unmounts and remounts chunks: a virtualized
945
+ * transcript scrolling back re-registers an earlier chunk after the ones
946
+ * still mounted, and footnote numbering plus "which chunk renders the
947
+ * aggregate footer" follow that order (2026-08-19 review r2 P2-10).
948
+ * Chunks that supply an index are kept sorted by it; chunks without one
949
+ * keep mount order after them. */
950
+ allocateSymbol(reactId: string, documentIndex?: number): symbol;
951
+ /** @internal Insert a chunk Symbol at its document position. */
952
+ _placeChunk(sym: symbol, documentIndex?: number): void;
954
953
  releaseSymbol(reactId: string): void;
955
954
  contributeLabels(symbol: symbol, footnotes: Set<string>, links: Set<string>): void;
956
955
  contributeChunkData(symbol: symbol, data: ChunkData): void;
@@ -958,6 +957,8 @@ interface RegistryInternal extends Registry {
958
957
  symbol: symbol;
959
958
  refcount: number;
960
959
  }>;
960
+ /** Symbol → the `documentIndex` its chunk supplied (see allocateSymbol). */
961
+ _chunkIndex: Map<symbol, number>;
961
962
  _subscribers: Set<() => void>;
962
963
  _notifyScheduled: boolean;
963
964
  _notify(): void;
@@ -1062,6 +1063,12 @@ type SanitizeSchema = typeof defaultSchema;
1062
1063
  * uncaught — there is no try/catch. Callers usually invoke this once at
1063
1064
  * module load, where a thrown error surfaces as a startup-time crash and
1064
1065
  * is the correct failure mode.
1066
+ * - **Widening is total.** Adding a tag to `tagNames` admits that element
1067
+ * with ALL of its semantics — `script`, `iframe`, `style`, `object`,
1068
+ * `form`, `base` re-enter the tree verbatim, and the two-gate URL story
1069
+ * in the README / SECURITY.md no longer describes your renderer. Prefer
1070
+ * allowing an attribute or a protocol over allowing an element; treat
1071
+ * any element you add as trusted input.
1065
1072
  *
1066
1073
  * @param modifier - Receives a deep clone of the library default. Mutate it
1067
1074
  * freely; either return the (possibly different) result, or return
@@ -1456,10 +1463,16 @@ declare function isFootnoteSection(node: Element): boolean;
1456
1463
 
1457
1464
  declare function normalizeId(s: string): string;
1458
1465
  /**
1459
- * Same as {@link normalizeId} plus resolution of backslash escapes.
1460
- * Used by PASS 0.5 substring pre-check against raw chunk source text:
1461
- * sources may write `[foo\]bar]` but the resulting label identifier is
1462
- * `foo]bar`, so we must unescape source before substring matching.
1466
+ * The form of raw chunk SOURCE that the PASS 0.5 substring pre-check compares
1467
+ * registry labels against. It is exactly {@link normalizeId}: micromark's
1468
+ * `identifier` (what the registry keys are built from) is the label's
1469
+ * source bytes with backslashes KEPT (`[^a\*b]` `a\*b`; only the `label`
1470
+ * field is unescaped), so the source must NOT be unescaped before matching
1471
+ * — an unescaping pass made every label holding an escaped punctuation
1472
+ * character miss the pre-check for good, and the cross-chunk reference
1473
+ * rendered as literal `[text][label]` (2026-08-19 review r2 P2-5; the
1474
+ * 2.4.5 narrowing to ASCII punctuation kept the wrong premise). Kept as a
1475
+ * named export for API stability.
1463
1476
  */
1464
1477
  declare function normalizeForMatch(s: string): string;
1465
1478
 
@@ -1647,25 +1660,6 @@ type AttributeEntry = NonNullable<NonNullable<Schema['attributes']>[string]>[num
1647
1660
  * is always tuple-form, so this branch is defensive only.
1648
1661
  */
1649
1662
  declare function mergeClassNameAllowlist(existing: ReadonlyArray<AttributeEntry> | undefined, extraClassNames: readonly string[]): AttributeEntry[];
1650
- /**
1651
- * The full sanitize schema used by the markdown renderer: extends
1652
- * `defaultSchema` to allow `<mark>`, the KaTeX math class names, and the
1653
- * three custom hast tags emitted by cross-chunk coordination handlers.
1654
- *
1655
- * **Owns its arrays and objects.** The shallow spread of `defaultSchema`
1656
- * alone would leave `attributes.a`, `attributes.img`, `protocols`,
1657
- * `ancestors`, and similar nested fields aliased to `rehype-sanitize`'s
1658
- * default singleton. A consumer who reasonably (but mistakenly) writes
1659
- * `sanitizeSchema.protocols.href.push('myapp')` would then poison
1660
- * `rehype-sanitize`'s `defaultSchema` for every other consumer in the
1661
- * process — a cross-package side-effect that's near-impossible to debug.
1662
- *
1663
- * One `cloneDeep` at module init breaks that aliasing without measurable
1664
- * cost (init-time only, single small object graph). The recommended
1665
- * extension API is still {@link extendSanitizeSchema}, which clones again
1666
- * per call; this layer just makes the exported singleton safe if someone
1667
- * skips the helper.
1668
- */
1669
1663
  declare const sanitizeSchema: Schema;
1670
1664
 
1671
1665
  /**
@@ -2027,4 +2021,4 @@ type RemendPreprocessorOptions = Omit<RemendOptions, 'katex' | 'inlineKatex'>;
2027
2021
  */
2028
2022
  declare function createRemendPreprocessor(options?: RemendPreprocessorOptions): AIMDContentPreprocessor;
2029
2023
 
2030
- export { type AIMDContentPreprocessor, type AIMarkdownEnginePlugin, type AIMarkdownEnginePluginName, type AdvanceOptions, type AdvanceResult, type AllowElement, type ChunkData, type Contribution, type CrossChunkHandlerOptions, DEFAULT_PAYLOAD, DEF_LINE_START_RE, type DefLabelScanner, type DefLabels, type Deprecation, type EnginePluginInternals, type EnginePluginStage, type ExtractContributionsOptions, type FootnoteDef, type FreezeBoundaryOptions, type IncrementalParseState, type IncrementalStage, type LinkDef, PIPELINE_STAGES, type ParsedMarkdown, type PhantomLabels, type PipelineOptions, type PipelineStage, type RefKind, type RefRecord, type Registry, type RegistryInternal, type RehypePlugins, type RehypeRebaseHashLinksOptions, type RemarkPlugins, type RemarkRehypeOptions, type RemendPreprocessorOptions, SENTINEL_FN_CONTENT, SENTINEL_LINK_URL, SMOOTH_STREAM_PACING_PRESETS, STAGE_MEASURE_PREFIX, type SanitizeSchema, type SmoothStreamController, type SmoothStreamOptions, type SmoothStreamPacing, type SmoothStreamPacingParams, type TransformContext, type UrlAttrKey, type UrlAttrTag, type UrlTransform, advanceIncrementalParse, attributeHastChildren, buildCoreRehypePlugins, buildCoreRemarkPlugins, buildCoreRemarkRehypeOptions, buildCrossChunkHandlers, buildPhantomSuffix, buildTransform, codePointSnapshots, collectDefLabels, computeFreezeBoundary, createDefLabelScanner, createFile, createIncrementalLatexPreprocessor, createProcessor, createRegistry, createRemendPreprocessor, createSmoothStreamController, defaultEnginePlugins, defaultUrlTransform, definitionList, extendSanitizeSchema, extractContributions, extractDefBodiesFromHast, footnoteSafeId, getEnginePluginInternals, hasLoneSurrogate, highlight, isFootnoteSection, lastRegionStart, measureStage, mergeClassNameAllowlist, normalizeForMatch, normalizeId, pangu, parseStage, phantomSuffixCloser, preprocessAIMDContent, preprocessLaTeX, rehypeFooterAdorn, rehypeRebaseHashLinks, removeComments, sanitizeCrossChunkUrl, sanitizeSchema, shortenDocumentId, smartypants, sourceIdFromFootnoteLiId, splitByProtectedRegions, subscribeStageTimings, transformStage, withDefs };
2024
+ export { type AIMDContentPreprocessor, type AIMarkdownEnginePlugin, type AIMarkdownEnginePluginName, type AdvanceOptions, type AdvanceResult, type AllowElement, type ChunkData, type Contribution, type CrossChunkHandlerOptions, DEFAULT_PAYLOAD, type DefLabelScanner, type DefLabels, type Deprecation, type EnginePluginInternals, type EnginePluginStage, type ExtractContributionsOptions, type FootnoteDef, type FreezeBoundaryOptions, type IncrementalParseState, type IncrementalStage, type LinkDef, PIPELINE_STAGES, type ParsedMarkdown, type PhantomLabels, type PipelineOptions, type PipelineStage, type RefKind, type RefRecord, type Registry, type RegistryInternal, type RehypePlugins, type RehypeRebaseHashLinksOptions, type RemarkPlugins, type RemarkRehypeOptions, type RemendPreprocessorOptions, SENTINEL_FN_CONTENT, SENTINEL_LINK_URL, SMOOTH_STREAM_PACING_PRESETS, STAGE_MEASURE_PREFIX, type SanitizeSchema, type SmoothStreamController, type SmoothStreamOptions, type SmoothStreamPacing, type SmoothStreamPacingParams, type TransformContext, type UrlAttrKey, type UrlAttrTag, type UrlTransform, advanceIncrementalParse, attributeHastChildren, buildCoreRehypePlugins, buildCoreRemarkPlugins, buildCoreRemarkRehypeOptions, buildCrossChunkHandlers, buildPhantomSuffix, buildTransform, codePointSnapshots, collectDefLabels, computeFreezeBoundary, createDefLabelScanner, createFile, createIncrementalLatexPreprocessor, createProcessor, createRegistry, createRemendPreprocessor, createSmoothStreamController, defaultEnginePlugins, defaultUrlTransform, definitionList, extendSanitizeSchema, extractContributions, extractDefBodiesFromHast, footnoteSafeId, getEnginePluginInternals, hasLoneSurrogate, highlight, isFootnoteSection, measureStage, mergeClassNameAllowlist, normalizeForMatch, normalizeId, pangu, parseStage, phantomSuffixCloser, preprocessAIMDContent, preprocessLaTeX, rehypeFooterAdorn, rehypeRebaseHashLinks, removeComments, sanitizeCrossChunkUrl, sanitizeSchema, shortenDocumentId, smartypants, sourceIdFromFootnoteLiId, splitByProtectedRegions, subscribeStageTimings, transformStage, withDefs };