@stream-mdx/core 0.1.0 → 0.2.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.mjs CHANGED
@@ -141,6 +141,22 @@ function manualExtractHighlightedLines(html, fallbackLength) {
141
141
  }
142
142
  return normalizeHighlightedLines(lines, fallbackLength);
143
143
  }
144
+ function getDefaultCodeWrapperAttributes(lang, themes = { dark: "github-dark", light: "github-light" }) {
145
+ const language = lang && typeof lang === "string" && lang.length > 0 ? lang : "text";
146
+ const themeLabel = `${themes.dark} ${themes.light}`;
147
+ return {
148
+ preAttrs: {
149
+ class: `shiki shiki-themes ${themeLabel}`,
150
+ "data-language": language,
151
+ style: "--shiki-dark-bg: transparent; --shiki-light-bg: transparent"
152
+ },
153
+ codeAttrs: {
154
+ "data-language": language,
155
+ "data-theme": themeLabel,
156
+ style: "display: grid;"
157
+ }
158
+ };
159
+ }
144
160
  function dedentIndentedCode(raw) {
145
161
  if (!raw) return "";
146
162
  const normalized = normalizeNewlines(raw);
@@ -547,6 +563,130 @@ function splitTextByRegexWithPrecedence(text, regex, toNode) {
547
563
  return result;
548
564
  }
549
565
 
566
+ // src/streaming/inline-streaming.ts
567
+ var DEFAULT_FORMAT_ANTICIPATION = {
568
+ inline: false,
569
+ mathInline: false,
570
+ mathBlock: false,
571
+ html: false,
572
+ mdx: false,
573
+ regex: false
574
+ };
575
+ function normalizeFormatAnticipation(input) {
576
+ if (input === true) {
577
+ return { ...DEFAULT_FORMAT_ANTICIPATION, inline: true };
578
+ }
579
+ if (!input) {
580
+ return { ...DEFAULT_FORMAT_ANTICIPATION };
581
+ }
582
+ return {
583
+ inline: input.inline ?? false,
584
+ mathInline: input.mathInline ?? false,
585
+ mathBlock: input.mathBlock ?? false,
586
+ html: input.html ?? false,
587
+ mdx: input.mdx ?? false,
588
+ regex: input.regex ?? false
589
+ };
590
+ }
591
+ function prepareInlineStreamingContent(content, options) {
592
+ const enableMath = options?.math !== false;
593
+ const anticipation = normalizeFormatAnticipation(options?.formatAnticipation);
594
+ const enableInlineAnticipation = anticipation.inline;
595
+ const enableMathInlineAnticipation = anticipation.mathInline;
596
+ const enableMathBlockAnticipation = anticipation.mathBlock;
597
+ const stack = [];
598
+ const toggleToken = (token) => {
599
+ const last = stack[stack.length - 1];
600
+ if (last === token) {
601
+ stack.pop();
602
+ } else {
603
+ stack.push(token);
604
+ }
605
+ };
606
+ let mathDisplayOpen = false;
607
+ let mathDisplayCrossedNewline = false;
608
+ for (let i = 0; i < content.length; i++) {
609
+ const code = content.charCodeAt(i);
610
+ if (code === 10 || code === 13) {
611
+ if (mathDisplayOpen) {
612
+ mathDisplayCrossedNewline = true;
613
+ }
614
+ continue;
615
+ }
616
+ if (code === 96) {
617
+ toggleToken("code");
618
+ continue;
619
+ }
620
+ if (code === 126 && i + 1 < content.length && content.charCodeAt(i + 1) === 126) {
621
+ toggleToken("strike");
622
+ i += 1;
623
+ continue;
624
+ }
625
+ if (code === 42) {
626
+ if (i + 1 < content.length && content.charCodeAt(i + 1) === 42) {
627
+ toggleToken("strong");
628
+ i += 1;
629
+ } else {
630
+ toggleToken("em");
631
+ }
632
+ continue;
633
+ }
634
+ if (enableMath && code === 36) {
635
+ if (i + 1 < content.length && content.charCodeAt(i + 1) === 36) {
636
+ toggleToken("math-display");
637
+ if (mathDisplayOpen) {
638
+ mathDisplayOpen = false;
639
+ mathDisplayCrossedNewline = false;
640
+ } else {
641
+ mathDisplayOpen = true;
642
+ mathDisplayCrossedNewline = false;
643
+ }
644
+ i += 1;
645
+ } else {
646
+ toggleToken("math-inline");
647
+ }
648
+ }
649
+ }
650
+ const hasIncompleteFormatting = stack.some((token) => token === "code" || token === "strike" || token === "strong" || token === "em");
651
+ const hasIncompleteMathInline = stack.includes("math-inline");
652
+ const hasIncompleteMathDisplay = stack.includes("math-display");
653
+ const hasIncompleteMath = hasIncompleteMathInline || hasIncompleteMathDisplay;
654
+ if (enableMath && hasIncompleteMath) {
655
+ if (hasIncompleteMathInline && !enableMathInlineAnticipation) {
656
+ return { kind: "raw", status: "raw", reason: "incomplete-math" };
657
+ }
658
+ if (hasIncompleteMathDisplay && (!enableMathBlockAnticipation || mathDisplayCrossedNewline)) {
659
+ return { kind: "raw", status: "raw", reason: "incomplete-math" };
660
+ }
661
+ }
662
+ if (hasIncompleteFormatting && !enableInlineAnticipation) {
663
+ return { kind: "raw", status: "raw", reason: "incomplete-formatting" };
664
+ }
665
+ if (!hasIncompleteFormatting && !hasIncompleteMath) {
666
+ return { kind: "parse", status: "complete", content, appended: "" };
667
+ }
668
+ const appendForToken = (token) => {
669
+ switch (token) {
670
+ case "code":
671
+ return "`";
672
+ case "strike":
673
+ return "~~";
674
+ case "strong":
675
+ return "**";
676
+ case "em":
677
+ return "*";
678
+ case "math-inline":
679
+ return "$";
680
+ case "math-display":
681
+ return "$$";
682
+ default:
683
+ return "";
684
+ }
685
+ };
686
+ const appended = stack.slice().reverse().map((token) => appendForToken(token)).join("");
687
+ return { kind: "parse", status: "anticipated", content: content + appended, appended };
688
+ }
689
+
550
690
  // src/worker-html-sanitizer.ts
551
691
  import * as rehypeParse from "rehype-parse";
552
692
  import * as rehypeSanitize from "rehype-sanitize";
@@ -728,6 +868,8 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
728
868
  const mdxAutoClose = options?.mdx?.autoClose === true;
729
869
  const mdxMaxNewlines = normalizeNewlineLimit(options?.mdx?.maxNewlines);
730
870
  const mdxAllowlist = normalizeComponentAllowlist(options?.mdx?.componentAllowlist);
871
+ const protectedRanges = options?.protectedRanges ?? [];
872
+ const protectedKinds = protectedRanges.length ? new Set(options?.protectedRangeKinds ?? ["math-inline", "math-display", "code-inline", "code-block", "autolink"]) : null;
731
873
  while (match !== null) {
732
874
  const start = match.index;
733
875
  const tagName = match[1];
@@ -742,6 +884,18 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
742
884
  continue;
743
885
  }
744
886
  let end = tagPattern.lastIndex;
887
+ if (protectedKinds && protectedRanges.length > 0) {
888
+ const absoluteStart = baseIsFinite ? baseOffset + start : start;
889
+ const absoluteEnd = baseIsFinite ? baseOffset + end : end;
890
+ const covered = protectedRanges.some(
891
+ (range) => protectedKinds.has(range.kind) && typeof range.from === "number" && typeof range.to === "number" && range.from <= absoluteStart && range.to >= absoluteEnd
892
+ );
893
+ if (covered) {
894
+ tagPattern.lastIndex = start + 1;
895
+ match = tagPattern.exec(source);
896
+ continue;
897
+ }
898
+ }
745
899
  if (!isSelfClosing && !mdxAllowed) {
746
900
  const closingIndex = findClosingHtmlTag(lowerSource, tagName.toLowerCase(), end);
747
901
  if (closingIndex === -1) {
@@ -1039,23 +1193,33 @@ function getBlockKey(block) {
1039
1193
  }
1040
1194
  function detectMDX(content, options) {
1041
1195
  const inlineCodeRanges = collectInlineCodeRanges(content);
1196
+ const protectedRanges = options?.protectedRanges ?? [];
1197
+ const baseOffset = typeof options?.baseOffset === "number" ? options.baseOffset : 0;
1198
+ const protectedKinds = protectedRanges.length ? /* @__PURE__ */ new Set(["math-inline", "math-display", "code-inline", "code-block", "html-inline", "html-block", "autolink"]) : null;
1042
1199
  const componentPattern = /<([A-Z][\w-]*)(\s|\/?>)/g;
1043
1200
  let componentMatch = componentPattern.exec(content);
1044
1201
  while (componentMatch !== null) {
1045
1202
  const start = componentMatch.index;
1046
1203
  const end = start + componentMatch[0].length;
1047
- if (!isWithinRanges(start, end, inlineCodeRanges)) {
1048
- return true;
1204
+ if (isWithinRanges(start, end, inlineCodeRanges)) {
1205
+ componentMatch = componentPattern.exec(content);
1206
+ continue;
1207
+ }
1208
+ if (protectedKinds) {
1209
+ const absoluteStart = baseOffset + start;
1210
+ const absoluteEnd = baseOffset + end;
1211
+ const covered = protectedRanges.some((range) => protectedKinds.has(range.kind) && range.from <= absoluteStart && range.to >= absoluteEnd);
1212
+ if (covered) {
1213
+ componentMatch = componentPattern.exec(content);
1214
+ continue;
1215
+ }
1049
1216
  }
1050
- componentMatch = componentPattern.exec(content);
1217
+ return true;
1051
1218
  }
1052
1219
  if (/(^|\n)\s*(import|export)\s/.test(content)) {
1053
1220
  return true;
1054
1221
  }
1055
1222
  const expressionPattern = /\{[^{}]+\}/g;
1056
- const protectedRanges = options?.protectedRanges ?? [];
1057
- const baseOffset = typeof options?.baseOffset === "number" ? options.baseOffset : 0;
1058
- const protectedKinds = protectedRanges.length ? /* @__PURE__ */ new Set(["math-inline", "math-display", "code-inline", "code-block"]) : null;
1059
1223
  for (let match = expressionPattern.exec(content); match !== null; match = expressionPattern.exec(content)) {
1060
1224
  const index = match.index;
1061
1225
  const prev = index > 0 ? content[index - 1] : "";
@@ -1303,7 +1467,12 @@ function buildListItemSnapshot(block, listItemNode, ordered, index, id, baseOffs
1303
1467
  }
1304
1468
  if (name === "Paragraph") {
1305
1469
  const paragraphRaw = raw.slice(cursor.from, cursor.to);
1306
- const paragraphData = processListItemParagraph(paragraphRaw);
1470
+ const meta = block.payload.meta ?? {};
1471
+ const paragraphData = processListItemParagraph(paragraphRaw, {
1472
+ formatAnticipation: meta.formatAnticipation,
1473
+ math: meta.mathEnabled,
1474
+ streaming: !block.isFinalized
1475
+ });
1307
1476
  const parsedInline = paragraphData.inline;
1308
1477
  if (!paragraphHandled) {
1309
1478
  inlineNodes = parsedInline;
@@ -1335,7 +1504,10 @@ function buildListItemSnapshot(block, listItemNode, ordered, index, id, baseOffs
1335
1504
  } else if (name === "BulletList" || name === "OrderedList") {
1336
1505
  const nestedId = `${id}::list:${subListIndex++}`;
1337
1506
  const nestedOrdered = name === "OrderedList";
1338
- childSnapshots.push(buildListNodeSnapshot(block, cursor.node, nestedOrdered, nestedId, baseOffset, raw));
1507
+ const nestedSnapshot = buildListNodeSnapshot(block, cursor.node, nestedOrdered, nestedId, baseOffset, raw);
1508
+ if (Array.isArray(nestedSnapshot.children) && nestedSnapshot.children.length > 0) {
1509
+ childSnapshots.push(nestedSnapshot);
1510
+ }
1339
1511
  } else if (name === "Blockquote") {
1340
1512
  const quoteId = `${id}::blockquote:${blockquoteIndex++}`;
1341
1513
  childSnapshots.push(buildBlockquoteSnapshot(block, cursor.node, quoteId, baseOffset, raw));
@@ -1371,11 +1543,31 @@ function buildListItemSnapshot(block, listItemNode, ordered, index, id, baseOffs
1371
1543
  };
1372
1544
  return itemSnapshot;
1373
1545
  }
1374
- function processListItemParagraph(raw) {
1546
+ function parseListInline(raw, options) {
1547
+ if (!options?.streaming || !options.formatAnticipation) {
1548
+ return listInlineParser.parse(raw);
1549
+ }
1550
+ const prepared = prepareInlineStreamingContent(raw, { formatAnticipation: options.formatAnticipation, math: options.math });
1551
+ if (prepared.kind === "raw") {
1552
+ return [{ kind: "text", text: raw }];
1553
+ }
1554
+ let preparedContent = prepared.content;
1555
+ let appended = prepared.appended;
1556
+ const normalized = normalizeFormatAnticipation(options.formatAnticipation);
1557
+ if (normalized.regex) {
1558
+ const regexAppend = listInlineParser.getRegexAnticipationAppend(raw);
1559
+ if (regexAppend) {
1560
+ preparedContent += regexAppend;
1561
+ appended += regexAppend;
1562
+ }
1563
+ }
1564
+ return listInlineParser.parse(preparedContent, { cache: false });
1565
+ }
1566
+ function processListItemParagraph(raw, options) {
1375
1567
  const normalized = normalizeParagraphText(raw);
1376
1568
  const { content, task } = stripTaskMarker(normalized);
1377
- const inline = listInlineParser.parse(content);
1378
- const segments = extractMixedContentSegments(content, void 0, (value) => listInlineParser.parse(value));
1569
+ const inline = parseListInline(content, options);
1570
+ const segments = extractMixedContentSegments(content, void 0, (value) => parseListInline(value, options));
1379
1571
  return {
1380
1572
  inline,
1381
1573
  segments,
@@ -1660,28 +1852,82 @@ function enrichTableSnapshot(block, snapshot) {
1660
1852
  function enrichCodeSnapshot(block, snapshot) {
1661
1853
  const source = typeof block.payload.meta?.code === "string" ? block.payload.meta?.code : block.payload.raw ?? "";
1662
1854
  const lines = extractCodeLines(source);
1855
+ const meta = block.payload.meta;
1663
1856
  const highlightedHtml = block.payload.highlightedHtml ?? "";
1664
- const highlightedLines = extractHighlightedLines(highlightedHtml, lines.length);
1665
- const { preAttrs, codeAttrs } = extractCodeWrapperAttributes(highlightedHtml);
1666
- const lang = typeof block.payload.meta?.lang === "string" ? String(block.payload.meta?.lang) : void 0;
1857
+ const hasBlockHighlight = typeof block.payload.highlightedHtml === "string" && block.payload.highlightedHtml.length > 0;
1858
+ const metaLines = Array.isArray(meta?.highlightedLines) ? meta?.highlightedLines : null;
1859
+ const includeLineHtml = metaLines ? true : !hasBlockHighlight || lines.length >= 200;
1860
+ const highlightedLines = metaLines ? normalizeHighlightedLines(metaLines, lines.length) : extractHighlightedLines(highlightedHtml, lines.length);
1861
+ const metaTokenLines = Array.isArray(meta?.tokenLines) ? meta?.tokenLines : null;
1862
+ const tokenLines = metaTokenLines ? normalizeTokenLines(metaTokenLines, lines.length) : null;
1863
+ const metaDiffKind = Array.isArray(meta?.diffKind) ? meta?.diffKind : null;
1864
+ const diffKindLines = metaDiffKind ? normalizeOptionalArray(metaDiffKind, lines.length) : null;
1865
+ const metaOldNo = Array.isArray(meta?.oldNo) ? meta?.oldNo : null;
1866
+ const oldNoLines = metaOldNo ? normalizeOptionalArray(metaOldNo, lines.length) : null;
1867
+ const metaNewNo = Array.isArray(meta?.newNo) ? meta?.newNo : null;
1868
+ const newNoLines = metaNewNo ? normalizeOptionalArray(metaNewNo, lines.length) : null;
1869
+ const lang = typeof meta?.lang === "string" ? String(meta.lang) : void 0;
1870
+ let { preAttrs, codeAttrs } = extractCodeWrapperAttributes(highlightedHtml);
1871
+ if (!preAttrs || !codeAttrs) {
1872
+ const defaults = getDefaultCodeWrapperAttributes(lang);
1873
+ preAttrs = preAttrs ?? defaults.preAttrs;
1874
+ codeAttrs = codeAttrs ?? defaults.codeAttrs;
1875
+ }
1667
1876
  snapshot.props = {
1668
1877
  ...snapshot.props ?? {},
1669
1878
  lang,
1670
1879
  preAttrs,
1671
1880
  codeAttrs
1672
1881
  };
1673
- snapshot.children = lines.map((line, index) => ({
1674
- id: `${block.id}::line:${index}`,
1675
- type: "code-line",
1676
- props: {
1882
+ snapshot.children = lines.map((line, index) => {
1883
+ const props = {
1677
1884
  index,
1678
1885
  text: line,
1679
- html: highlightedLines[index] ?? null
1680
- },
1681
- children: []
1682
- }));
1886
+ html: includeLineHtml ? highlightedLines[index] ?? null : null
1887
+ };
1888
+ if (tokenLines) {
1889
+ props.tokens = tokenLines[index] ?? null;
1890
+ }
1891
+ if (diffKindLines) {
1892
+ props.diffKind = diffKindLines[index] ?? null;
1893
+ }
1894
+ if (oldNoLines) {
1895
+ props.oldNo = oldNoLines[index] ?? null;
1896
+ }
1897
+ if (newNoLines) {
1898
+ props.newNo = newNoLines[index] ?? null;
1899
+ }
1900
+ return {
1901
+ id: `${block.id}::line:${index}`,
1902
+ type: "code-line",
1903
+ props,
1904
+ children: []
1905
+ };
1906
+ });
1683
1907
  return snapshot;
1684
1908
  }
1909
+ function normalizeTokenLines(lines, fallbackLength) {
1910
+ if (!lines || lines.length === 0) {
1911
+ return new Array(Math.max(0, fallbackLength)).fill(null);
1912
+ }
1913
+ const length = Math.max(fallbackLength, lines.length);
1914
+ const result = new Array(length).fill(null);
1915
+ for (let i = 0; i < lines.length; i++) {
1916
+ result[i] = lines[i] ?? null;
1917
+ }
1918
+ return result;
1919
+ }
1920
+ function normalizeOptionalArray(lines, fallbackLength) {
1921
+ if (!lines || lines.length === 0) {
1922
+ return new Array(Math.max(0, fallbackLength)).fill(null);
1923
+ }
1924
+ const length = Math.max(fallbackLength, lines.length);
1925
+ const result = new Array(length).fill(null);
1926
+ for (let i = 0; i < lines.length; i++) {
1927
+ result[i] = lines[i] ?? null;
1928
+ }
1929
+ return result;
1930
+ }
1685
1931
  function cloneInlineNodes(nodes) {
1686
1932
  return nodes.map((node) => {
1687
1933
  if ("children" in node && Array.isArray(node.children)) {
@@ -1850,7 +2096,17 @@ function applyPatchBatch(snapshot, patches) {
1850
2096
  case "appendLines": {
1851
2097
  const parent = resolveTargetNode(snapshot, patch.at);
1852
2098
  if (!parent) break;
1853
- appendLinesToCodeNode(snapshot, parent, patch.startIndex, patch.lines ?? [], patch.highlight ?? []);
2099
+ appendLinesToCodeNode(
2100
+ snapshot,
2101
+ parent,
2102
+ patch.startIndex,
2103
+ patch.lines ?? [],
2104
+ patch.highlight ?? [],
2105
+ patch.tokens,
2106
+ patch.diffKind,
2107
+ patch.oldNo,
2108
+ patch.newNo
2109
+ );
1854
2110
  break;
1855
2111
  }
1856
2112
  case "setHTML": {
@@ -1972,7 +2228,7 @@ function applyPropsToNode(node, props) {
1972
2228
  }
1973
2229
  node.props = next;
1974
2230
  }
1975
- function appendLinesToCodeNode(snapshot, parent, startIndex, lines, highlights) {
2231
+ function appendLinesToCodeNode(snapshot, parent, startIndex, lines, highlights, tokens, diffKind, oldNo, newNo) {
1976
2232
  if (parent.type !== "code") return;
1977
2233
  const insertionIndex = Math.max(0, Math.min(startIndex, parent.children.length));
1978
2234
  let currentIndex = insertionIndex;
@@ -1990,6 +2246,18 @@ function appendLinesToCodeNode(snapshot, parent, startIndex, lines, highlights)
1990
2246
  html: highlights[i] ?? null
1991
2247
  }
1992
2248
  };
2249
+ if (tokens && Object.prototype.hasOwnProperty.call(tokens, i)) {
2250
+ child.props.tokens = tokens[i] ?? null;
2251
+ }
2252
+ if (diffKind && Object.prototype.hasOwnProperty.call(diffKind, i)) {
2253
+ child.props.diffKind = diffKind[i] ?? null;
2254
+ }
2255
+ if (oldNo && Object.prototype.hasOwnProperty.call(oldNo, i)) {
2256
+ child.props.oldNo = oldNo[i] ?? null;
2257
+ }
2258
+ if (newNo && Object.prototype.hasOwnProperty.call(newNo, i)) {
2259
+ child.props.newNo = newNo[i] ?? null;
2260
+ }
1993
2261
  snapshot.nodes.set(child.id, child);
1994
2262
  parent.children.splice(currentIndex, 0, child.id);
1995
2263
  currentIndex++;
@@ -2545,6 +2813,14 @@ function mergeAppendLines(window2, startIndex) {
2545
2813
  }
2546
2814
  const lines = [...base.lines ?? []];
2547
2815
  const highlight = Array.isArray(base.highlight) ? [...base.highlight] : [];
2816
+ const tokens = Array.isArray(base.tokens) ? [...base.tokens] : [];
2817
+ const diffKind = Array.isArray(base.diffKind) ? [...base.diffKind] : [];
2818
+ const oldNo = Array.isArray(base.oldNo) ? [...base.oldNo] : [];
2819
+ const newNo = Array.isArray(base.newNo) ? [...base.newNo] : [];
2820
+ let includeTokens = Array.isArray(base.tokens);
2821
+ let includeDiffKind = Array.isArray(base.diffKind);
2822
+ let includeOldNo = Array.isArray(base.oldNo);
2823
+ let includeNewNo = Array.isArray(base.newNo);
2548
2824
  const baseStart = base.startIndex;
2549
2825
  let expectedStart = baseStart + lines.length;
2550
2826
  let j = startIndex + 1;
@@ -2552,6 +2828,7 @@ function mergeAppendLines(window2, startIndex) {
2552
2828
  while (j < window2.length && mergedCount < APPEND_MERGE_LIMIT) {
2553
2829
  const next = window2[j];
2554
2830
  if (next.op === "appendLines" && next.at.blockId === base.at.blockId && next.at.nodeId === base.at.nodeId && typeof next.startIndex === "number" && next.startIndex === expectedStart) {
2831
+ const priorLineCount = lines.length;
2555
2832
  lines.push(...next.lines ?? []);
2556
2833
  const nextHighlights = Array.isArray(next.highlight) ? next.highlight : [];
2557
2834
  const appendedCount = next.lines?.length ?? 0;
@@ -2565,6 +2842,50 @@ function mergeAppendLines(window2, startIndex) {
2565
2842
  highlight.push(null);
2566
2843
  }
2567
2844
  }
2845
+ if (Array.isArray(next.tokens)) {
2846
+ if (!includeTokens) {
2847
+ tokens.push(...new Array(priorLineCount).fill(null));
2848
+ includeTokens = true;
2849
+ }
2850
+ for (let idx = 0; idx < appendedCount; idx++) {
2851
+ tokens.push(idx < next.tokens.length ? next.tokens[idx] ?? null : null);
2852
+ }
2853
+ } else if (includeTokens) {
2854
+ tokens.push(...new Array(appendedCount).fill(null));
2855
+ }
2856
+ if (Array.isArray(next.diffKind)) {
2857
+ if (!includeDiffKind) {
2858
+ diffKind.push(...new Array(priorLineCount).fill(null));
2859
+ includeDiffKind = true;
2860
+ }
2861
+ for (let idx = 0; idx < appendedCount; idx++) {
2862
+ diffKind.push(idx < next.diffKind.length ? next.diffKind[idx] ?? null : null);
2863
+ }
2864
+ } else if (includeDiffKind) {
2865
+ diffKind.push(...new Array(appendedCount).fill(null));
2866
+ }
2867
+ if (Array.isArray(next.oldNo)) {
2868
+ if (!includeOldNo) {
2869
+ oldNo.push(...new Array(priorLineCount).fill(null));
2870
+ includeOldNo = true;
2871
+ }
2872
+ for (let idx = 0; idx < appendedCount; idx++) {
2873
+ oldNo.push(idx < next.oldNo.length ? next.oldNo[idx] ?? null : null);
2874
+ }
2875
+ } else if (includeOldNo) {
2876
+ oldNo.push(...new Array(appendedCount).fill(null));
2877
+ }
2878
+ if (Array.isArray(next.newNo)) {
2879
+ if (!includeNewNo) {
2880
+ newNo.push(...new Array(priorLineCount).fill(null));
2881
+ includeNewNo = true;
2882
+ }
2883
+ for (let idx = 0; idx < appendedCount; idx++) {
2884
+ newNo.push(idx < next.newNo.length ? next.newNo[idx] ?? null : null);
2885
+ }
2886
+ } else if (includeNewNo) {
2887
+ newNo.push(...new Array(appendedCount).fill(null));
2888
+ }
2568
2889
  expectedStart = baseStart + lines.length;
2569
2890
  mergedCount++;
2570
2891
  j++;
@@ -2575,7 +2896,11 @@ function mergeAppendLines(window2, startIndex) {
2575
2896
  const combined = {
2576
2897
  ...base,
2577
2898
  lines,
2578
- highlight: highlight.length > 0 ? highlight : void 0
2899
+ highlight: highlight.length > 0 ? highlight : void 0,
2900
+ tokens: includeTokens && tokens.length > 0 ? tokens : void 0,
2901
+ diffKind: includeDiffKind && diffKind.length > 0 ? diffKind : void 0,
2902
+ oldNo: includeOldNo && oldNo.length > 0 ? oldNo : void 0,
2903
+ newNo: includeNewNo && newNo.length > 0 ? newNo : void 0
2579
2904
  };
2580
2905
  return { patch: combined, nextIndex: j };
2581
2906
  }
@@ -2915,130 +3240,6 @@ var CustomStreamingMatcher = class {
2915
3240
  return false;
2916
3241
  }
2917
3242
  };
2918
-
2919
- // src/streaming/inline-streaming.ts
2920
- var DEFAULT_FORMAT_ANTICIPATION = {
2921
- inline: false,
2922
- mathInline: false,
2923
- mathBlock: false,
2924
- html: false,
2925
- mdx: false,
2926
- regex: false
2927
- };
2928
- function normalizeFormatAnticipation(input) {
2929
- if (input === true) {
2930
- return { ...DEFAULT_FORMAT_ANTICIPATION, inline: true };
2931
- }
2932
- if (!input) {
2933
- return { ...DEFAULT_FORMAT_ANTICIPATION };
2934
- }
2935
- return {
2936
- inline: input.inline ?? false,
2937
- mathInline: input.mathInline ?? false,
2938
- mathBlock: input.mathBlock ?? false,
2939
- html: input.html ?? false,
2940
- mdx: input.mdx ?? false,
2941
- regex: input.regex ?? false
2942
- };
2943
- }
2944
- function prepareInlineStreamingContent(content, options) {
2945
- const enableMath = options?.math !== false;
2946
- const anticipation = normalizeFormatAnticipation(options?.formatAnticipation);
2947
- const enableInlineAnticipation = anticipation.inline;
2948
- const enableMathInlineAnticipation = anticipation.mathInline;
2949
- const enableMathBlockAnticipation = anticipation.mathBlock;
2950
- const stack = [];
2951
- const toggleToken = (token) => {
2952
- const last = stack[stack.length - 1];
2953
- if (last === token) {
2954
- stack.pop();
2955
- } else {
2956
- stack.push(token);
2957
- }
2958
- };
2959
- let mathDisplayOpen = false;
2960
- let mathDisplayCrossedNewline = false;
2961
- for (let i = 0; i < content.length; i++) {
2962
- const code = content.charCodeAt(i);
2963
- if (code === 10 || code === 13) {
2964
- if (mathDisplayOpen) {
2965
- mathDisplayCrossedNewline = true;
2966
- }
2967
- continue;
2968
- }
2969
- if (code === 96) {
2970
- toggleToken("code");
2971
- continue;
2972
- }
2973
- if (code === 126 && i + 1 < content.length && content.charCodeAt(i + 1) === 126) {
2974
- toggleToken("strike");
2975
- i += 1;
2976
- continue;
2977
- }
2978
- if (code === 42) {
2979
- if (i + 1 < content.length && content.charCodeAt(i + 1) === 42) {
2980
- toggleToken("strong");
2981
- i += 1;
2982
- } else {
2983
- toggleToken("em");
2984
- }
2985
- continue;
2986
- }
2987
- if (enableMath && code === 36) {
2988
- if (i + 1 < content.length && content.charCodeAt(i + 1) === 36) {
2989
- toggleToken("math-display");
2990
- if (mathDisplayOpen) {
2991
- mathDisplayOpen = false;
2992
- mathDisplayCrossedNewline = false;
2993
- } else {
2994
- mathDisplayOpen = true;
2995
- mathDisplayCrossedNewline = false;
2996
- }
2997
- i += 1;
2998
- } else {
2999
- toggleToken("math-inline");
3000
- }
3001
- }
3002
- }
3003
- const hasIncompleteFormatting = stack.some((token) => token === "code" || token === "strike" || token === "strong" || token === "em");
3004
- const hasIncompleteMathInline = stack.includes("math-inline");
3005
- const hasIncompleteMathDisplay = stack.includes("math-display");
3006
- const hasIncompleteMath = hasIncompleteMathInline || hasIncompleteMathDisplay;
3007
- if (enableMath && hasIncompleteMath) {
3008
- if (hasIncompleteMathInline && !enableMathInlineAnticipation) {
3009
- return { kind: "raw", status: "raw", reason: "incomplete-math" };
3010
- }
3011
- if (hasIncompleteMathDisplay && (!enableMathBlockAnticipation || mathDisplayCrossedNewline)) {
3012
- return { kind: "raw", status: "raw", reason: "incomplete-math" };
3013
- }
3014
- }
3015
- if (hasIncompleteFormatting && !enableInlineAnticipation) {
3016
- return { kind: "raw", status: "raw", reason: "incomplete-formatting" };
3017
- }
3018
- if (!hasIncompleteFormatting && !hasIncompleteMath) {
3019
- return { kind: "parse", status: "complete", content, appended: "" };
3020
- }
3021
- const appendForToken = (token) => {
3022
- switch (token) {
3023
- case "code":
3024
- return "`";
3025
- case "strike":
3026
- return "~~";
3027
- case "strong":
3028
- return "**";
3029
- case "em":
3030
- return "*";
3031
- case "math-inline":
3032
- return "$";
3033
- case "math-display":
3034
- return "$$";
3035
- default:
3036
- return "";
3037
- }
3038
- };
3039
- const appended = stack.slice().reverse().map((token) => appendForToken(token)).join("");
3040
- return { kind: "parse", status: "anticipated", content: content + appended, appended };
3041
- }
3042
3243
  export {
3043
3244
  CSP_HEADERS,
3044
3245
  CustomStreamingMatcher,
@@ -3077,12 +3278,14 @@ export {
3077
3278
  findClosingHtmlTag,
3078
3279
  generateBlockId,
3079
3280
  getBlockKey,
3281
+ getDefaultCodeWrapperAttributes,
3080
3282
  initializeSecurity,
3081
3283
  initializeTrustedTypesPolicy,
3082
3284
  inlineNodesToPlainText,
3083
3285
  isLikelyMdxComponent,
3084
3286
  normalizeBlockquoteText,
3085
3287
  normalizeFormatAnticipation,
3288
+ normalizeHighlightedLines,
3086
3289
  normalizeLang,
3087
3290
  parseCodeFenceInfo,
3088
3291
  prepareInlineStreamingContent,
@@ -217,6 +217,8 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
217
217
  const mdxAutoClose = options?.mdx?.autoClose === true;
218
218
  const mdxMaxNewlines = normalizeNewlineLimit(options?.mdx?.maxNewlines);
219
219
  const mdxAllowlist = normalizeComponentAllowlist(options?.mdx?.componentAllowlist);
220
+ const protectedRanges = options?.protectedRanges ?? [];
221
+ const protectedKinds = protectedRanges.length ? new Set(options?.protectedRangeKinds ?? ["math-inline", "math-display", "code-inline", "code-block", "autolink"]) : null;
220
222
  while (match !== null) {
221
223
  const start = match.index;
222
224
  const tagName = match[1];
@@ -231,6 +233,18 @@ function splitByTagSegments(source, baseOffset, parseInline, options) {
231
233
  continue;
232
234
  }
233
235
  let end = tagPattern.lastIndex;
236
+ if (protectedKinds && protectedRanges.length > 0) {
237
+ const absoluteStart = baseIsFinite ? baseOffset + start : start;
238
+ const absoluteEnd = baseIsFinite ? baseOffset + end : end;
239
+ const covered = protectedRanges.some(
240
+ (range) => protectedKinds.has(range.kind) && typeof range.from === "number" && typeof range.to === "number" && range.from <= absoluteStart && range.to >= absoluteEnd
241
+ );
242
+ if (covered) {
243
+ tagPattern.lastIndex = start + 1;
244
+ match = tagPattern.exec(source);
245
+ continue;
246
+ }
247
+ }
234
248
  if (!isSelfClosing && !mdxAllowed) {
235
249
  const closingIndex = findClosingHtmlTag(lowerSource, tagName.toLowerCase(), end);
236
250
  if (closingIndex === -1) {
@@ -1,4 +1,4 @@
1
- import { InlineNode, MixedContentSegment } from './types.cjs';
1
+ import { ProtectedRange, InlineNode, MixedContentSegment } from './types.cjs';
2
2
 
3
3
  interface MixedContentAutoCloseHtmlOptions {
4
4
  autoClose?: boolean;
@@ -13,6 +13,8 @@ interface MixedContentAutoCloseMdxOptions {
13
13
  interface MixedContentOptions {
14
14
  html?: MixedContentAutoCloseHtmlOptions;
15
15
  mdx?: MixedContentAutoCloseMdxOptions;
16
+ protectedRanges?: ReadonlyArray<ProtectedRange>;
17
+ protectedRangeKinds?: ReadonlyArray<ProtectedRange["kind"]>;
16
18
  }
17
19
  declare function extractMixedContentSegments(raw: string, baseOffset: number | undefined, parseInline: (content: string) => InlineNode[], options?: MixedContentOptions): MixedContentSegment[];
18
20
  declare function isLikelyMdxComponent(tagName: string): boolean;