@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.
@@ -31,7 +31,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  DEFAULT_PAYLOAD: () => DEFAULT_PAYLOAD,
34
- DEF_LINE_START_RE: () => DEF_LINE_START_RE,
35
34
  PIPELINE_STAGES: () => PIPELINE_STAGES,
36
35
  SENTINEL_FN_CONTENT: () => SENTINEL_FN_CONTENT,
37
36
  SENTINEL_LINK_URL: () => SENTINEL_LINK_URL,
@@ -66,7 +65,6 @@ __export(src_exports, {
66
65
  hasLoneSurrogate: () => hasLoneSurrogate,
67
66
  highlight: () => highlight,
68
67
  isFootnoteSection: () => isFootnoteSection,
69
- lastRegionStart: () => lastRegionStart,
70
68
  measureStage: () => measureStage,
71
69
  mergeClassNameAllowlist: () => mergeClassNameAllowlist,
72
70
  normalizeForMatch: () => normalizeForMatch,
@@ -270,10 +268,73 @@ var import_micromark_util_html_tag_name = require("micromark-util-html-tag-name"
270
268
  var import_micromark_util_normalize_identifier = require("micromark-util-normalize-identifier");
271
269
  var TYPE6_NAMES = new Set(import_micromark_util_html_tag_name.htmlBlockNames);
272
270
  var TABLE_PART_NAMES = /* @__PURE__ */ new Set(["td", "th", "tr", "tbody", "thead", "tfoot", "caption", "col", "colgroup"]);
271
+ var HTML_BREAKOUT_TAGS = /* @__PURE__ */ new Set([
272
+ "b",
273
+ "big",
274
+ "blockquote",
275
+ "body",
276
+ "br",
277
+ "center",
278
+ "code",
279
+ "dd",
280
+ "div",
281
+ "dl",
282
+ "dt",
283
+ "em",
284
+ "embed",
285
+ "h1",
286
+ "h2",
287
+ "h3",
288
+ "h4",
289
+ "h5",
290
+ "h6",
291
+ "head",
292
+ "hr",
293
+ "i",
294
+ "img",
295
+ "li",
296
+ "listing",
297
+ "menu",
298
+ "meta",
299
+ "nobr",
300
+ "ol",
301
+ "p",
302
+ "pre",
303
+ "ruby",
304
+ "s",
305
+ "small",
306
+ "span",
307
+ "strong",
308
+ "strike",
309
+ "sub",
310
+ "sup",
311
+ "table",
312
+ "tt",
313
+ "u",
314
+ "ul",
315
+ "var"
316
+ ]);
317
+ var HTML_INTEGRATION_POINTS = ["foreignobject", "desc", "mi", "mo", "mn", "ms", "mtext", "annotation-xml"];
273
318
  var TYPE1_NAMES = /* @__PURE__ */ new Set(["script", "pre", "style", "textarea"]);
319
+ var RAW_TEXT_ELEMENTS = /* @__PURE__ */ new Set([
320
+ "script",
321
+ "style",
322
+ "textarea",
323
+ "title",
324
+ "xmp",
325
+ "iframe",
326
+ "noembed",
327
+ "noframes",
328
+ // NOT `noscript`: hast-util-raw constructs parse5 with
329
+ // `scriptingEnabled: false`, under which `<noscript>` content is ordinary
330
+ // HTML — modelling it as raw text ignored a `<b>` inside and under-blocked
331
+ // (oracle review of the r2 batch; regression caught before release).
332
+ "plaintext"
333
+ ]);
274
334
  var TYPE6_START_RE = /^<\/?([A-Za-z][A-Za-z0-9-]*)(?:[ \t\r]|\/?>|$)/;
275
335
  var TYPE1_START_RE = /^<(script|pre|style|textarea)(?:[ \t\r]|>|$)/i;
276
- var TYPE7_LINE_RE = /^<(\/?)([A-Za-z][A-Za-z0-9-]*)(?:[ \t\r][^>]*|\/)?>[ \t\r]*$/;
336
+ var TYPE7_LINE_RE = /^(?:<[A-Za-z][A-Za-z0-9-]*(?:[ \t\r][^>]*|\/)?>|<\/[A-Za-z][A-Za-z0-9-]*[ \t\r]*>)[ \t\r]*$/;
337
+ var t7Name = (line) => /^<\/?([A-Za-z][A-Za-z0-9-]*)/.exec(line)[1];
277
338
  var VOID_TAGS = /* @__PURE__ */ new Set([
278
339
  "area",
279
340
  "base",
@@ -297,7 +358,35 @@ var DEF_RE = /^ {0,3}\[((?:[^[\]\\]|\\.)+)\]:/;
297
358
  var FENCE_RE = /^ {0,3}(`{3,}|~{3,})/;
298
359
  var MATH_RUN_RE = /^ {0,3}(\$\$+)/;
299
360
  var TAG_OR_COMMENT_RE = /<(\/?)([A-Za-z][A-Za-z0-9-]*)(?=[\s/>])([^>]*)>|<!--|-->|--!>/g;
300
- var TRUNCATED_TAG_RE = /<(\/?)([A-Za-z][A-Za-z0-9-]*)([^<>]*)$/;
361
+ var TRUNCATED_TAG_RE = /<(\/?)([A-Za-z][A-Za-z0-9-]*)([^>]*)$/;
362
+ var TAG_START_LT_RE = /<\/?[A-Za-z]/g;
363
+ function scanTagAttrs(text, from, to, out) {
364
+ let st = out.state;
365
+ for (let i = from; i < to; i++) {
366
+ const c = text[i];
367
+ if (st === '"' || st === "'") {
368
+ if (c === st) st = "outside";
369
+ continue;
370
+ }
371
+ const ws = c === " " || c === " " || c === "\n" || c === "\r" || c === "\f";
372
+ if (st === "afterEq") {
373
+ if (ws) continue;
374
+ if (c === '"' || c === "'") st = c;
375
+ else if (c === ">") return i;
376
+ else st = "unquoted";
377
+ continue;
378
+ }
379
+ if (st === "unquoted") {
380
+ if (ws) st = "outside";
381
+ else if (c === ">") return i;
382
+ continue;
383
+ }
384
+ if (c === "=") st = "afterEq";
385
+ else if (c === ">") return i;
386
+ }
387
+ out.state = st;
388
+ return -1;
389
+ }
301
390
  var REF_RE = /!?\[((?:[^[\]\\]|\\.)*)\]/g;
302
391
  var BACKTICK_RUN_RE = /`+/g;
303
392
  var MD_BLANK_RE = /^[ \t\r]*$/;
@@ -392,6 +481,8 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
392
481
  openTotal: 0,
393
482
  commentOpen: false,
394
483
  piOpen: false,
484
+ bogusOpen: false,
485
+ rawTextOpen: null,
395
486
  declOpen: false,
396
487
  cdataOpen: false,
397
488
  inFence: false,
@@ -416,6 +507,7 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
416
507
  pendingTruncatedCloses: [],
417
508
  tagAcrossLines: false,
418
509
  tagAcrossLinesIndent: 0,
510
+ tagAcrossLinesState: "outside",
419
511
  htmlFlowReal: false
420
512
  };
421
513
  }
@@ -515,11 +607,21 @@ function computeFreezeBoundary(text, options, resume) {
515
607
  let start = cp.confirmedOffset;
516
608
  let tailLine = null;
517
609
  while (start < text.length) {
518
- let end = text.indexOf("\n", start);
519
- if (end === -1) end = text.length;
520
- const confirmed = end < text.length;
521
- const rawLine = text.slice(start, end);
522
- const lineText = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
610
+ let end = text.length;
611
+ let textEnd = text.length;
612
+ {
613
+ const nl = text.indexOf("\n", start);
614
+ const cr = text.indexOf("\r", start);
615
+ if (cr !== -1 && (nl === -1 || cr < nl)) {
616
+ textEnd = cr;
617
+ end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
618
+ } else if (nl !== -1) {
619
+ textEnd = nl;
620
+ end = nl;
621
+ }
622
+ }
623
+ const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
624
+ const lineText = text.slice(start, textEnd);
523
625
  const ln = {
524
626
  start,
525
627
  end,
@@ -611,14 +713,33 @@ function processConfirmedLine(cp, ln, text) {
611
713
  newest.defListSettled = ln.blank ? true : !canBecomeDdLine(ln.text, true);
612
714
  }
613
715
  const isBlockStart = cp.prevLineBlank;
614
- if (cp.htmlSeamPending && !ln.blank && !cp.htmlFlowSinceBlank && !(cp.commentOpen || cp.piOpen || cp.declOpen || cp.cdataOpen)) {
716
+ if (cp.htmlSeamPending && !ln.blank && !cp.htmlFlowSinceBlank && !(cp.commentOpen || cp.piOpen || cp.declOpen || cp.cdataOpen || cp.bogusOpen)) {
615
717
  const defShapedLine = DEF_RE.test(ln.text) || FOOTNOTE_DEF_RE.test(ln.text);
616
718
  const commentOnly = ln.text.replace(/<!--[\s\S]*?-->/g, " ").replace(/<!--[\s\S]*$/, " ").replace(/[ \t\r]/g, "") === "";
617
719
  if (!defShapedLine && !commentOnly) {
618
720
  cp.htmlSeamPending = false;
619
721
  }
620
722
  }
723
+ const inForeignContent = () => (cp.tagBalance.get("svg") ?? 0) > 0 || (cp.tagBalance.get("math") ?? 0) > 0;
724
+ const honoursSelfClosing = (tag) => {
725
+ if (tag === "svg" || tag === "math") return true;
726
+ if (!inForeignContent() || HTML_BREAKOUT_TAGS.has(tag)) return false;
727
+ for (const ip of HTML_INTEGRATION_POINTS) if ((cp.tagBalance.get(ip) ?? 0) > 0) return false;
728
+ return true;
729
+ };
730
+ const htmlRulesApply = () => {
731
+ if (!inForeignContent()) return true;
732
+ for (const ip of HTML_INTEGRATION_POINTS) if ((cp.tagBalance.get(ip) ?? 0) > 0) return true;
733
+ return false;
734
+ };
621
735
  const applyTag = (tag, closing) => {
736
+ if (cp.rawTextOpen !== null) {
737
+ if (!(closing && tag === cp.rawTextOpen)) return;
738
+ cp.rawTextOpen = null;
739
+ } else if (!closing && RAW_TEXT_ELEMENTS.has(tag) && htmlRulesApply()) {
740
+ cp.rawTextOpen = tag;
741
+ if (tag === "plaintext") cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
742
+ }
622
743
  if (closing) {
623
744
  const count = cp.tagBalance.get(tag) ?? 0;
624
745
  if (count > 0) {
@@ -631,7 +752,7 @@ function processConfirmedLine(cp, ln, text) {
631
752
  }
632
753
  };
633
754
  const commentOpenAtLineStart = cp.commentOpen;
634
- const rawOpenAtLineStart = cp.commentOpen || cp.piOpen || cp.declOpen || cp.cdataOpen;
755
+ const rawOpenAtLineStart = cp.commentOpen || cp.piOpen || cp.declOpen || cp.cdataOpen || cp.bogusOpen;
635
756
  if (cp.inFence) {
636
757
  const close = FENCE_RE.exec(ln.text);
637
758
  if (close && close[1][0] === cp.fenceChar && close[1].length >= cp.fenceLen && isMdBlank(ln.text.slice(close[0].length))) {
@@ -714,13 +835,21 @@ function processConfirmedLine(cp, ln, text) {
714
835
  cp.pendingTruncatedTags = [];
715
836
  }
716
837
  cp.pendingTruncatedCloses = [];
838
+ if (cp.tagAcrossLines && (cp.tagAcrossLinesState === '"' || cp.tagAcrossLinesState === "'")) {
839
+ cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
840
+ }
717
841
  cp.tagAcrossLines = false;
842
+ cp.tagAcrossLinesState = "outside";
843
+ if (cp.bogusOpen) {
844
+ cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
845
+ cp.bogusOpen = false;
846
+ }
718
847
  cp.blankRun += 1;
719
848
  cp.lastBlankStart = ln.start;
720
849
  cp.candidates.push({
721
850
  offset: Math.min(ln.end + 1, text.length),
722
851
  blankRun: cp.blankRun,
723
- htmlBalanced: cp.openTotal === 0 && !cp.commentOpen && !cp.piOpen && !cp.declOpen && !cp.cdataOpen,
852
+ htmlBalanced: cp.openTotal === 0 && !cp.commentOpen && !cp.piOpen && !cp.declOpen && !cp.cdataOpen && !cp.bogusOpen,
724
853
  hazard: cp.hazardVerdict,
725
854
  seamRisk: cp.htmlSeamPending,
726
855
  defListSettled: null
@@ -750,7 +879,7 @@ function processConfirmedLine(cp, ln, text) {
750
879
  const t7 = TYPE7_LINE_RE.exec(t);
751
880
  if (t6 !== null && TYPE6_NAMES.has(t6[1].toLowerCase()) || TYPE1_START_RE.test(t) || // Type 7 cannot interrupt a paragraph, and excludes the raw-text
752
881
  // names (those are type 1 as start tags, paragraph as end tags).
753
- t7 !== null && !cp.prevLineWasText && !TYPE1_NAMES.has(t7[2].toLowerCase())) {
882
+ t7 !== null && !cp.prevLineWasText && !TYPE1_NAMES.has(t7Name(t).toLowerCase())) {
754
883
  cp.htmlFlowReal = true;
755
884
  }
756
885
  }
@@ -852,7 +981,7 @@ ${cont(scanText)}` };
852
981
  pos = c + 3;
853
982
  continue;
854
983
  }
855
- if (cp.declOpen) {
984
+ if (cp.declOpen || cp.bogusOpen) {
856
985
  const c = scanText.indexOf(">", pos);
857
986
  if (c === -1) {
858
987
  rawSpans.push([pos, scanText.length]);
@@ -860,6 +989,7 @@ ${cont(scanText)}` };
860
989
  }
861
990
  rawSpans.push([pos, c + 1]);
862
991
  cp.declOpen = false;
992
+ cp.bogusOpen = false;
863
993
  pos = c + 1;
864
994
  continue;
865
995
  }
@@ -867,10 +997,16 @@ ${cont(scanText)}` };
867
997
  const cd = scanText.indexOf("<![CDATA[", pos);
868
998
  const dm = scanText.slice(pos).search(/<![A-Za-z]/);
869
999
  const decl = dm === -1 ? -1 : pos + dm;
870
- const starts = [pi, cd, decl].filter((x) => x !== -1);
1000
+ const bm = cp.htmlFlowReal ? scanText.slice(pos).search(/<!(?!--|[A-Za-z]|\[CDATA\[)|<\/(?![A-Za-z])/) : -1;
1001
+ const bogus = bm === -1 ? -1 : pos + bm;
1002
+ const starts = [pi, cd, decl, bogus].filter((x) => x !== -1);
871
1003
  if (starts.length === 0) break;
872
1004
  const first = Math.min(...starts);
873
- if (first === cd) {
1005
+ if (first === bogus) {
1006
+ rawSpans.push([bogus, bogus + 2]);
1007
+ cp.bogusOpen = true;
1008
+ pos = bogus + 2;
1009
+ } else if (first === cd) {
874
1010
  rawSpans.push([cd, cd + 9]);
875
1011
  cp.cdataOpen = true;
876
1012
  pos = cd + 9;
@@ -897,22 +1033,27 @@ ${cont(scanText)}` };
897
1033
  let skipTagScan = false;
898
1034
  if (cp.tagAcrossLines) {
899
1035
  if (ln.indent < cp.tagAcrossLinesIndent) poisonRawDivergence();
900
- const gt = ln.text.indexOf(">");
1036
+ const attrs = { state: cp.tagAcrossLinesState };
1037
+ const gt = scanTagAttrs(ln.text, 0, ln.text.length, attrs);
901
1038
  if (gt === -1) {
1039
+ scanTagAttrs("\n", 0, 1, attrs);
1040
+ cp.tagAcrossLinesState = attrs.state;
902
1041
  skipTagScan = true;
903
1042
  } else {
904
- if (/["']/.test(ln.text.slice(0, gt))) poisonRawDivergence();
905
1043
  for (const tag of cp.pendingTruncatedCloses) applyTag(tag, true);
906
1044
  cp.pendingTruncatedCloses = [];
907
1045
  cp.tagAcrossLines = false;
1046
+ cp.tagAcrossLinesState = "outside";
908
1047
  tagText = " ".repeat(gt + 1) + tagText.slice(gt + 1);
909
1048
  }
910
1049
  }
1050
+ let tagHandledAsTruncated = false;
911
1051
  if (!skipTagScan) {
912
1052
  TAG_OR_COMMENT_RE.lastIndex = 0;
913
1053
  let m;
914
1054
  let lastCommentOpenerIdx = -1;
915
1055
  while ((m = TAG_OR_COMMENT_RE.exec(tagText)) !== null) {
1056
+ if (cp.rawTextOpen !== null && (m[0] === "<!--" || m[0] === "-->" || m[0] === "--!>")) continue;
916
1057
  if (m[0] === "<!--") {
917
1058
  const next = tagText.slice(m.index + 4, m.index + 6);
918
1059
  if (cp.commentOpen) {
@@ -939,8 +1080,31 @@ ${cont(scanText)}` };
939
1080
  const closing = m[1] === "/";
940
1081
  const tag = m[2].toLowerCase();
941
1082
  if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + m.index);
942
- const selfClosing = m[3] !== void 0 && /\/\s*$/.test(m[3]);
943
- if (VOID_TAGS.has(tag) || selfClosing) continue;
1083
+ let attrs = m[3] ?? "";
1084
+ if (cp.htmlFlowReal && (cp.rawTextOpen === null || closing && tag === cp.rawTextOpen)) {
1085
+ const attrStart = m.index + 1 + (closing ? 1 : 0) + m[2].length;
1086
+ const st = { state: "outside" };
1087
+ const gt = scanTagAttrs(tagText, attrStart, tagText.length, st);
1088
+ if (gt === -1) {
1089
+ if (!VOID_TAGS.has(tag)) {
1090
+ if (closing) cp.pendingTruncatedCloses.push(tag);
1091
+ else applyTag(tag, false);
1092
+ }
1093
+ scanTagAttrs("\n", 0, 1, st);
1094
+ cp.tagAcrossLines = true;
1095
+ cp.tagAcrossLinesIndent = ln.indent;
1096
+ cp.tagAcrossLinesState = st.state;
1097
+ tagHandledAsTruncated = true;
1098
+ break;
1099
+ }
1100
+ if (gt + 1 !== m.index + m[0].length) {
1101
+ attrs = tagText.slice(attrStart, gt);
1102
+ TAG_OR_COMMENT_RE.lastIndex = gt + 1;
1103
+ }
1104
+ }
1105
+ if (closing && !cp.htmlFlowReal && !/^\s*$/.test(attrs)) continue;
1106
+ const selfClosing = /\/\s*$/.test(attrs);
1107
+ if (VOID_TAGS.has(tag) || selfClosing && honoursSelfClosing(tag)) continue;
944
1108
  applyTag(tag, closing);
945
1109
  }
946
1110
  if (cp.commentOpen && lastCommentOpenerIdx !== -1 && !inRawText) {
@@ -960,25 +1124,39 @@ ${cont(scanText)}` };
960
1124
  const closing = mr[1] === "/";
961
1125
  const tag = mr[2].toLowerCase();
962
1126
  if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + mr.index);
1127
+ if (closing && mr[3] !== void 0 && !/^\s*$/.test(mr[3])) continue;
963
1128
  const selfClosing = mr[3] !== void 0 && /\/\s*$/.test(mr[3]);
964
- if (VOID_TAGS.has(tag) || selfClosing) continue;
1129
+ if (VOID_TAGS.has(tag) || selfClosing && honoursSelfClosing(tag)) continue;
965
1130
  applyTag(tag, closing);
966
1131
  }
967
1132
  }
968
1133
  if (cp.pendingTruncatedTags.length > 0 && ln.text.includes(">")) {
1134
+ if (cp.pendingTruncatedTags.some((t) => TABLE_PART_NAMES.has(t))) {
1135
+ cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
1136
+ }
969
1137
  cp.pendingTruncatedTags = [];
970
1138
  }
971
- if (!cp.commentOpen) {
972
- const lastLt = tagText.lastIndexOf("<");
1139
+ if (!cp.commentOpen && !tagHandledAsTruncated) {
1140
+ let lastLt = -1;
1141
+ TAG_START_LT_RE.lastIndex = 0;
1142
+ for (let ms = TAG_START_LT_RE.exec(tagText); ms !== null; ms = TAG_START_LT_RE.exec(tagText)) {
1143
+ lastLt = ms.index;
1144
+ TAG_START_LT_RE.lastIndex = ms.index + 1;
1145
+ }
973
1146
  if (lastLt !== -1 && !tagText.includes(">", lastLt)) {
974
1147
  const m2 = TRUNCATED_TAG_RE.exec(tagText.slice(lastLt));
975
1148
  if (m2) {
976
1149
  const closing = m2[1] === "/";
977
1150
  const tag = m2[2].toLowerCase();
978
- if (TABLE_PART_NAMES.has(tag)) cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + lastLt);
1151
+ if (TABLE_PART_NAMES.has(tag) && cp.htmlFlowReal) {
1152
+ cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start + lastLt);
1153
+ }
979
1154
  if (cp.htmlFlowReal) {
980
1155
  cp.tagAcrossLines = true;
981
1156
  cp.tagAcrossLinesIndent = ln.indent;
1157
+ const attrs = { state: "outside" };
1158
+ scanTagAttrs(m2[3] + "\n", 0, m2[3].length + 1, attrs);
1159
+ cp.tagAcrossLinesState = attrs.state;
982
1160
  }
983
1161
  if (closing) {
984
1162
  if (!VOID_TAGS.has(tag) && cp.htmlFlowReal) cp.pendingTruncatedCloses.push(tag);
@@ -1647,7 +1825,7 @@ function normalizeId(s) {
1647
1825
  return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s);
1648
1826
  }
1649
1827
  function normalizeForMatch(s) {
1650
- return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s.replace(/\\([!-/:-@[-`{-~])/g, "$1"));
1828
+ return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s);
1651
1829
  }
1652
1830
 
1653
1831
  // src/components/collectDefLabels.ts
@@ -1965,22 +2143,68 @@ function createRegistry(onEmpty) {
1965
2143
  labelSet: { footnoteLabels: /* @__PURE__ */ new Set(), linkLabels: /* @__PURE__ */ new Set() },
1966
2144
  version: 0,
1967
2145
  _reactIdMap: /* @__PURE__ */ new Map(),
2146
+ /** Symbol → its `documentIndex`, for chunks that supplied one. */
2147
+ _chunkIndex: /* @__PURE__ */ new Map(),
1968
2148
  _subscribers: /* @__PURE__ */ new Set(),
1969
2149
  _notifyScheduled: false,
1970
- allocateSymbol(reactId) {
2150
+ allocateSymbol(reactId, rawDocumentIndex) {
2151
+ const documentIndex = rawDocumentIndex !== void 0 && Number.isFinite(rawDocumentIndex) ? rawDocumentIndex : void 0;
2152
+ if (rawDocumentIndex !== void 0 && documentIndex === void 0) {
2153
+ console.warn(
2154
+ `[ai-react-markdown] documentIndex must be a finite number \u2014 received ${String(rawDocumentIndex)}; ignoring it for this chunk.`
2155
+ );
2156
+ }
1971
2157
  const existing = this._reactIdMap.get(reactId);
1972
2158
  if (existing) {
1973
2159
  existing.refcount++;
2160
+ const moved = documentIndex !== void 0 ? this._chunkIndex.get(existing.symbol) !== documentIndex : this._chunkIndex.has(existing.symbol);
2161
+ if (moved) {
2162
+ const at = this.chunkOrder.indexOf(existing.symbol);
2163
+ if (at !== -1) this.chunkOrder.splice(at, 1);
2164
+ this._placeChunk(existing.symbol, documentIndex);
2165
+ this._notify();
2166
+ }
1974
2167
  return existing.symbol;
1975
2168
  }
1976
2169
  const sym = Symbol(reactId);
1977
2170
  this._reactIdMap.set(reactId, { symbol: sym, refcount: 1 });
1978
- this.chunkOrder.push(sym);
2171
+ this._placeChunk(sym, documentIndex);
1979
2172
  this._notify();
1980
2173
  return sym;
1981
2174
  },
1982
- registerChunk(reactId, footnotes, links) {
1983
- const sym = this.allocateSymbol(reactId);
2175
+ /** Put `sym` into `chunkOrder` at its document position. Without an index
2176
+ * it goes last (mount order — the historical behaviour); with one it
2177
+ * sorts before the first chunk that sits later, where a chunk WITHOUT an
2178
+ * index counts as "later" so an indexed chunk never lands behind one
2179
+ * whose position is unknown. */
2180
+ _placeChunk(sym, documentIndex) {
2181
+ if (this.chunkOrder.length > 0) {
2182
+ const anyIndexed = documentIndex !== void 0 || this.chunkOrder.some((s) => this._chunkIndex.has(s));
2183
+ const anyPlain = documentIndex === void 0 || this.chunkOrder.some((s) => !this._chunkIndex.has(s));
2184
+ if (anyIndexed && anyPlain) {
2185
+ console.warn(
2186
+ "[ai-react-markdown] Some chunks of this document supply `documentIndex` and some do not. Indexed chunks always sort ahead of un-indexed ones, so footnote numbering and aggregate-footer placement will not follow document order. Pass `documentIndex` to every chunk, or to none."
2187
+ );
2188
+ }
2189
+ }
2190
+ if (documentIndex === void 0) {
2191
+ this._chunkIndex.delete(sym);
2192
+ this.chunkOrder.push(sym);
2193
+ return;
2194
+ }
2195
+ this._chunkIndex.set(sym, documentIndex);
2196
+ let at = this.chunkOrder.length;
2197
+ for (let i = 0; i < this.chunkOrder.length; i++) {
2198
+ const other = this._chunkIndex.get(this.chunkOrder[i]);
2199
+ if (other === void 0 || other > documentIndex) {
2200
+ at = i;
2201
+ break;
2202
+ }
2203
+ }
2204
+ this.chunkOrder.splice(at, 0, sym);
2205
+ },
2206
+ registerChunk(reactId, footnotes, links, documentIndex) {
2207
+ const sym = this.allocateSymbol(reactId, documentIndex);
1984
2208
  this.contributeLabels(sym, footnotes, links);
1985
2209
  return sym;
1986
2210
  },
@@ -2003,6 +2227,7 @@ function createRegistry(onEmpty) {
2003
2227
  this._reactIdMap.delete(reactId);
2004
2228
  const idx = this.chunkOrder.indexOf(entry.symbol);
2005
2229
  if (idx !== -1) this.chunkOrder.splice(idx, 1);
2230
+ this._chunkIndex.delete(entry.symbol);
2006
2231
  this.chunkData.delete(entry.symbol);
2007
2232
  const nextFn = /* @__PURE__ */ new Set();
2008
2233
  const nextLink = /* @__PURE__ */ new Set();
@@ -3629,17 +3854,28 @@ function mergeClassNameAllowlist(existing, extraClassNames) {
3629
3854
  return entries;
3630
3855
  }
3631
3856
  var crossChunkTags = ["cross-chunk-link", "cross-chunk-image", "footnote-sup"];
3632
- var sanitizeSchema = cloneDeep_default({
3633
- ...import_rehype_sanitize2.defaultSchema,
3634
- tagNames: [...import_rehype_sanitize2.defaultSchema.tagNames || [], "mark", ...crossChunkTags],
3635
- attributes: {
3636
- ...import_rehype_sanitize2.defaultSchema.attributes,
3637
- code: mergeClassNameAllowlist(import_rehype_sanitize2.defaultSchema.attributes?.code, ["math-inline", "math-display"]),
3638
- "cross-chunk-link": ["label", "referenceType", "documentId", "localUrl", "localTitle"],
3639
- "cross-chunk-image": ["label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
3640
- "footnote-sup": ["label", "localOccurrence", "localNumber", "documentId"]
3641
- }
3642
- });
3857
+ function deepFreeze(value) {
3858
+ if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
3859
+ Object.freeze(value);
3860
+ for (const key of Object.keys(value)) deepFreeze(value[key]);
3861
+ }
3862
+ return value;
3863
+ }
3864
+ var STRIPPED_TAGS = ["script", "style", "title", "textarea", "noframes", "noembed", "xmp", "iframe", "plaintext"];
3865
+ var sanitizeSchema = deepFreeze(
3866
+ cloneDeep_default({
3867
+ ...import_rehype_sanitize2.defaultSchema,
3868
+ tagNames: [...import_rehype_sanitize2.defaultSchema.tagNames || [], "mark", ...crossChunkTags],
3869
+ attributes: {
3870
+ ...import_rehype_sanitize2.defaultSchema.attributes,
3871
+ code: mergeClassNameAllowlist(import_rehype_sanitize2.defaultSchema.attributes?.code, ["math-inline", "math-display"]),
3872
+ "cross-chunk-link": ["label", "referenceType", "documentId", "localUrl", "localTitle"],
3873
+ "cross-chunk-image": ["label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
3874
+ "footnote-sup": ["label", "localOccurrence", "localNumber", "documentId"]
3875
+ },
3876
+ strip: [.../* @__PURE__ */ new Set([...import_rehype_sanitize2.defaultSchema.strip || [], ...STRIPPED_TAGS])]
3877
+ })
3878
+ );
3643
3879
 
3644
3880
  // src/components/crossChunkUrlSanitize.ts
3645
3881
  function fakeElement(tagName, key, url) {
@@ -3962,7 +4198,12 @@ var createSmoothStreamController = (options = {}) => {
3962
4198
  return;
3963
4199
  }
3964
4200
  tentativeEnd = ends[ends.length - 1];
3965
- const confirmedEnds = finished ? ends : ends.slice(0, -1);
4201
+ let hold = 1;
4202
+ if (!finished && ends.length > 1) {
4203
+ const last = source.charCodeAt(source.length - 1);
4204
+ if (last >= 55296 && last <= 56319) hold = 2;
4205
+ }
4206
+ const confirmedEnds = finished ? ends : ends.slice(0, -hold);
3966
4207
  for (const end of confirmedEnds) pending.push(end);
3967
4208
  };
3968
4209
  const snap = (next) => {
@@ -4090,10 +4331,11 @@ function findClosingBacktickRun(content, start, n) {
4090
4331
  const runLen = getRepeatedMarkerLength(content, i, "`");
4091
4332
  if (runLen === n) return i;
4092
4333
  i += runLen;
4093
- } else if (ch === "\n") {
4334
+ } else if (ch === "\n" || ch === "\r") {
4094
4335
  let j = i + 1;
4095
- while (j < content.length && (content[j] === " " || content[j] === " " || content[j] === "\r")) j += 1;
4096
- if (j >= content.length || content[j] === "\n") return -1;
4336
+ if (ch === "\r" && content[j] === "\n") j += 1;
4337
+ while (j < content.length && (content[j] === " " || content[j] === " ")) j += 1;
4338
+ if (j >= content.length || content[j] === "\n" || content[j] === "\r") return -1;
4097
4339
  i += 1;
4098
4340
  } else {
4099
4341
  i += 1;
@@ -4194,26 +4436,54 @@ function escapeMhchemCommands(text) {
4194
4436
  return text.replaceAll("$\\ce{", "$\\\\ce{").replaceAll("$\\pu{", "$\\\\pu{");
4195
4437
  }
4196
4438
  var CURRENCY_REGEX = /(?<![\\$])\$(?!\$)(?=\d+(?:,\d{3})*(?:\.\d+)?(?:[KMBkmb])?(?:\s|$|[^a-zA-Z\d]))/g;
4197
- var NO_ESCAPED_DOLLAR_REGEX = /(?<![\\$])\$(?!\$)/g;
4198
4439
  var DELIMITERS_REGEX = /(?<!!)\\\[([\S\s]*?[^\\])\\](?!\()|\\\((.*?)\\\)/g;
4199
4440
  var ARRAY_COL_SPEC_OR_PIPE_REGEX = /(\\begin\{(?:array|tabular[x*]?)\}\{[^}]*\})|(?<!\\)\|/g;
4200
- var LATEX_BLOCK_REGEX = /\$\$([\S\s]*?)\$\$|(?<![\\$])\$(?!\$)((?:[^$\n]|\\\$)*?)(?<![\\`])\$(?!\$)/g;
4441
+ var EVEN_BACKSLASHES = String.raw`(?<=(?:^|[^\\])(?:\\\\)*)`;
4442
+ var LATEX_BLOCK_REGEX = new RegExp(
4443
+ String.raw`${EVEN_BACKSLASHES}\$\$([\S\s]*?)${EVEN_BACKSLASHES}\$\$|(?<![\\$])\$(?!\$)((?:[^$\n]|\\\$)*?)(?<![\\` + "`" + String.raw`])\$(?!\$)`,
4444
+ "g"
4445
+ );
4201
4446
  var TEXT_COMMAND = "\\text{";
4202
4447
  var SINGLE_DOLLAR_REGEX = /(?<![\\$])\$(?!\$)((?:[^$\n]|\\[$])+?)(?<!\\)(?<!`)\$(?!\$)/g;
4448
+ function countBareDollars(str, from, to, prev, next) {
4449
+ let n = 0;
4450
+ for (let j = from; j < to; j++) {
4451
+ if (str.charCodeAt(j) !== 36) continue;
4452
+ const before = j > from ? str[j - 1] : prev;
4453
+ const after = j + 1 < to ? str[j + 1] : next;
4454
+ if (before !== "\\" && before !== "$" && after !== "$") n += 1;
4455
+ }
4456
+ return n;
4457
+ }
4203
4458
  function escapeCurrencyDollarSigns(text) {
4204
4459
  const parts = [];
4205
4460
  let lastIndex = 0;
4206
4461
  const currencyMatches = Array.from(text.matchAll(CURRENCY_REGEX));
4207
4462
  let currentLineProcessed = "";
4463
+ let currentLineDollars = 0;
4464
+ const appendToLine = (piece) => {
4465
+ if (piece.length === 0) return;
4466
+ const prevLast = currentLineProcessed.length > 0 ? currentLineProcessed[currentLineProcessed.length - 1] : "";
4467
+ const prevBeforeLast = currentLineProcessed.length > 1 ? currentLineProcessed[currentLineProcessed.length - 2] : "";
4468
+ const prevLastCounted = prevLast === "$" && prevBeforeLast !== "\\" && prevBeforeLast !== "$";
4469
+ if (prevLastCounted && piece[0] === "$") currentLineDollars -= 1;
4470
+ currentLineDollars += countBareDollars(piece, 0, piece.length, prevLast, "");
4471
+ currentLineProcessed += piece;
4472
+ };
4473
+ const resetLine = (rest) => {
4474
+ currentLineProcessed = "";
4475
+ currentLineDollars = 0;
4476
+ appendToLine(rest);
4477
+ };
4208
4478
  for (let i = 0; i < currencyMatches.length; i++) {
4209
4479
  const match = currencyMatches[i];
4210
4480
  const segment = text.substring(lastIndex, match.index);
4211
4481
  parts.push(segment);
4212
4482
  const newlineIdx = Math.max(segment.lastIndexOf("\n"), segment.lastIndexOf("\r"));
4213
4483
  if (newlineIdx !== -1) {
4214
- currentLineProcessed = segment.substring(newlineIdx + 1);
4484
+ resetLine(segment.substring(newlineIdx + 1));
4215
4485
  } else {
4216
- currentLineProcessed += segment;
4486
+ appendToLine(segment);
4217
4487
  }
4218
4488
  let needEscape = true;
4219
4489
  const restStart = match.index + 1;
@@ -4230,15 +4500,21 @@ function escapeCurrencyDollarSigns(text) {
4230
4500
  }
4231
4501
  firstLineBeforeNextMatch = text.substring(restStart, eol);
4232
4502
  }
4233
- if (Array.from(firstLineBeforeNextMatch.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {
4234
- const wholeLineBeforeNextMatchWithoutCurrentDollar = currentLineProcessed + firstLineBeforeNextMatch;
4235
- if (Array.from(wholeLineBeforeNextMatchWithoutCurrentDollar.matchAll(NO_ESCAPED_DOLLAR_REGEX)).length % 2 !== 0) {
4236
- needEscape = false;
4237
- }
4503
+ const restDollars = countBareDollars(firstLineBeforeNextMatch, 0, firstLineBeforeNextMatch.length, "", "");
4504
+ if (restDollars % 2 !== 0) {
4505
+ const L = currentLineProcessed;
4506
+ const lLast = L.length > 0 ? L[L.length - 1] : "";
4507
+ const lBeforeLast = L.length > 1 ? L[L.length - 2] : "";
4508
+ const lLastCounted = lLast === "$" && lBeforeLast !== "\\" && lBeforeLast !== "$";
4509
+ const f0 = firstLineBeforeNextMatch[0];
4510
+ let whole = currentLineDollars + restDollars;
4511
+ if (lLastCounted && f0 === "$") whole -= 1;
4512
+ if (f0 === "$" && (lLast === "\\" || lLast === "$") && firstLineBeforeNextMatch[1] !== "$") whole -= 1;
4513
+ if (whole % 2 !== 0) needEscape = false;
4238
4514
  }
4239
4515
  const replacement = needEscape ? "\\$" : "$";
4240
4516
  parts.push(replacement);
4241
- currentLineProcessed += replacement;
4517
+ appendToLine(replacement);
4242
4518
  lastIndex = match.index + 1;
4243
4519
  }
4244
4520
  parts.push(text.substring(lastIndex));
@@ -4306,8 +4582,7 @@ function escapeLatexPipesInUnclosed(text) {
4306
4582
  const tail = text.substring(unclosedStart + delimLen);
4307
4583
  return before + delim + replaceUnescapedPipes(tail);
4308
4584
  }
4309
- function truncateUnclosedLatexBlock(text) {
4310
- const unclosedStart = findUnclosedDelimiterStart(text, "double-only");
4585
+ function truncateUnclosedLatexBlock(text, unclosedStart = findUnclosedDelimiterStart(text, "double-only")) {
4311
4586
  if (unclosedStart === -1) return text;
4312
4587
  return text.substring(0, unclosedStart).trimEnd();
4313
4588
  }
@@ -4418,8 +4693,9 @@ function processSliceInstrumented(slice, probe = true) {
4418
4693
  if (probe && hasUnclosedTextCommand(text)) quiescent = false;
4419
4694
  text = escapeTextUnderscores(text);
4420
4695
  text = convertSingleToDoubleDollar(text);
4696
+ let unclosedDouble;
4421
4697
  if (probe || index === 0 && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
4422
- const unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
4698
+ unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
4423
4699
  if (unclosedDouble !== -1) {
4424
4700
  quiescent = false;
4425
4701
  if (index === 0 && text.slice(0, unclosedDouble).trim() === "") {
@@ -4427,7 +4703,7 @@ function processSliceInstrumented(slice, probe = true) {
4427
4703
  }
4428
4704
  }
4429
4705
  }
4430
- text = truncateUnclosedLatexBlock(text);
4706
+ text = truncateUnclosedLatexBlock(text, unclosedDouble);
4431
4707
  parts.push(text);
4432
4708
  }
4433
4709
  return { out: parts.join(""), quiescent, truncatedAtSeamStart };
@@ -4556,7 +4832,6 @@ function createRemendPreprocessor(options) {
4556
4832
  // Annotate the CommonJS export names for ESM import in node:
4557
4833
  0 && (module.exports = {
4558
4834
  DEFAULT_PAYLOAD,
4559
- DEF_LINE_START_RE,
4560
4835
  PIPELINE_STAGES,
4561
4836
  SENTINEL_FN_CONTENT,
4562
4837
  SENTINEL_LINK_URL,
@@ -4591,7 +4866,6 @@ function createRemendPreprocessor(options) {
4591
4866
  hasLoneSurrogate,
4592
4867
  highlight,
4593
4868
  isFootnoteSection,
4594
- lastRegionStart,
4595
4869
  measureStage,
4596
4870
  mergeClassNameAllowlist,
4597
4871
  normalizeForMatch,