@ai-react-markdown/engine 2.11.0 → 2.13.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.
@@ -79,6 +79,7 @@ __export(src_exports, {
79
79
  rehypeRebaseHashLinks: () => rehypeRebaseHashLinks_default,
80
80
  rehypeVerifyEngineTags: () => rehypeVerifyEngineTags,
81
81
  removeComments: () => removeComments,
82
+ resolveCrossChunkReference: () => resolveCrossChunkReference,
82
83
  sanitizeCrossChunkUrl: () => sanitizeCrossChunkUrl,
83
84
  sanitizeSchema: () => sanitizeSchema,
84
85
  shortenDocumentId: () => shortenDocumentId,
@@ -264,8 +265,51 @@ var defaultUrlTransform = (value) => {
264
265
  return "";
265
266
  };
266
267
 
267
- // src/components/incrementalParse/computeFreezeBoundary.ts
268
- var import_micromark_util_html_tag_name = require("micromark-util-html-tag-name");
268
+ // src/components/incrementalParse/freezeScanState.ts
269
+ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
270
+ return {
271
+ defListEnabled,
272
+ mathFlow,
273
+ referenceTaint,
274
+ confirmedOffset: 0,
275
+ candidates: [],
276
+ defs: /* @__PURE__ */ new Map(),
277
+ footnoteDefs: /* @__PURE__ */ new Map(),
278
+ unresolvedRefs: [],
279
+ tagBalance: /* @__PURE__ */ new Map(),
280
+ openTotal: 0,
281
+ p5Tok: { kind: "data" },
282
+ formPointerMaybeSet: false,
283
+ openStack: [],
284
+ mdBlock: { kind: "none" },
285
+ blankRun: 0,
286
+ lastBlankStart: -1,
287
+ hazardVerdict: false,
288
+ prevLineBlank: true,
289
+ // doc start counts as a block start
290
+ prevLineWasText: false,
291
+ prevLineWasValidDef: false,
292
+ prevLineOpenContent: false,
293
+ tableMaybeOpen: false,
294
+ containerMaybeOpen: false,
295
+ paragraphHasUnpairedRun: false,
296
+ openBracket: null,
297
+ p5SealPending: false,
298
+ defBlockMaybeOpen: false,
299
+ fnDefResumable: false,
300
+ phasePoisonedAt: Infinity,
301
+ pendingTruncatedTags: [],
302
+ pendingTruncatedCloses: [],
303
+ pendingTag: null
304
+ };
305
+ }
306
+ function pendingFenceCloser(checkpoint) {
307
+ const cp = checkpoint;
308
+ if (cp.phasePoisonedAt !== Infinity) return "";
309
+ if (cp.mdBlock.kind === "fence" && cp.mdBlock.indent === 0) return cp.mdBlock.char.repeat(cp.mdBlock.len);
310
+ if (cp.mdBlock.kind === "math" && cp.mdBlock.indent === 0) return "$".repeat(cp.mdBlock.len);
311
+ return "";
312
+ }
269
313
 
270
314
  // src/components/incrementalParse/mdLineText.ts
271
315
  var MD_BLANK_RE = /^[ \t\r]*$/;
@@ -273,6 +317,9 @@ var isMdBlank = (text) => MD_BLANK_RE.test(text);
273
317
  var mdTrim = (text) => text.replace(/^[ \t\r]+|[ \t\r]+$/g, "");
274
318
  var mdTrimStart = (text) => text.replace(/^[ \t\r]+/, "");
275
319
 
320
+ // src/components/incrementalParse/freezeLineSyntax.ts
321
+ var import_micromark_util_html_tag_name = require("micromark-util-html-tag-name");
322
+
276
323
  // src/components/incrementalParse/referenceTaint.ts
277
324
  var import_micromark_util_normalize_identifier = require("micromark-util-normalize-identifier");
278
325
  var FOOTNOTE_DEF_RE = /^ {0,3}\[\^[^\]]*\]:/;
@@ -458,7 +505,7 @@ function settleRefsAndEarliestUnresolved(cp) {
458
505
  return earliestUnresolved;
459
506
  }
460
507
 
461
- // src/components/incrementalParse/computeFreezeBoundary.ts
508
+ // src/components/incrementalParse/freezeLineSyntax.ts
462
509
  var TYPE6_NAMES = new Set(import_micromark_util_html_tag_name.htmlBlockNames);
463
510
  var TABLE_PART_NAMES = /* @__PURE__ */ new Set(["td", "th", "tr", "tbody", "thead", "tfoot", "caption", "col", "colgroup"]);
464
511
  var DOCUMENT_STRUCTURE_NAMES = /* @__PURE__ */ new Set(["html", "head", "body", "frameset"]);
@@ -710,43 +757,6 @@ function maskIntraLineCodeSpans(text, carryOpen) {
710
757
  out += text.slice(cursor);
711
758
  return { masked: out, unpaired: false };
712
759
  }
713
- function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
714
- return {
715
- defListEnabled,
716
- mathFlow,
717
- referenceTaint,
718
- confirmedOffset: 0,
719
- candidates: [],
720
- defs: /* @__PURE__ */ new Map(),
721
- footnoteDefs: /* @__PURE__ */ new Map(),
722
- unresolvedRefs: [],
723
- tagBalance: /* @__PURE__ */ new Map(),
724
- openTotal: 0,
725
- p5Tok: { kind: "data" },
726
- formPointerMaybeSet: false,
727
- openStack: [],
728
- mdBlock: { kind: "none" },
729
- blankRun: 0,
730
- lastBlankStart: -1,
731
- hazardVerdict: false,
732
- prevLineBlank: true,
733
- // doc start counts as a block start
734
- prevLineWasText: false,
735
- prevLineWasValidDef: false,
736
- prevLineOpenContent: false,
737
- tableMaybeOpen: false,
738
- containerMaybeOpen: false,
739
- paragraphHasUnpairedRun: false,
740
- openBracket: null,
741
- p5SealPending: false,
742
- defBlockMaybeOpen: false,
743
- fnDefResumable: false,
744
- phasePoisonedAt: Infinity,
745
- pendingTruncatedTags: [],
746
- pendingTruncatedCloses: [],
747
- pendingTag: null
748
- };
749
- }
750
760
  function classifyBlockStart(text, indent, defListEnabled) {
751
761
  if (indent >= 4) return true;
752
762
  if (LIST_MARKER_RE.test(text) || FOOTNOTE_DEF_RE.test(text)) return true;
@@ -754,71 +764,23 @@ function classifyBlockStart(text, indent, defListEnabled) {
754
764
  if (indent === 0) return false;
755
765
  return null;
756
766
  }
757
- function computeFreezeBoundary(text, options, resume) {
758
- const mathFlow = options.mathFlow ?? true;
759
- const referenceTaint = options.referenceTaint ?? true;
760
- const prev = resume;
761
- const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
762
- let start = cp.confirmedOffset;
763
- let tailLine = null;
764
- while (start < text.length) {
765
- let end = text.length;
766
- let textEnd = text.length;
767
- {
768
- const nl = text.indexOf("\n", start);
769
- const cr = text.indexOf("\r", start);
770
- if (cr !== -1 && (nl === -1 || cr < nl)) {
771
- textEnd = cr;
772
- end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
773
- } else if (nl !== -1) {
774
- textEnd = nl;
775
- end = nl;
776
- }
777
- }
778
- const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
779
- const lineText = text.slice(start, textEnd);
780
- const ln = {
781
- start,
782
- end,
783
- text: lineText,
784
- blank: confirmed && isMdBlank(lineText),
785
- indent: computeIndent(lineText)
786
- };
787
- if (!confirmed) {
788
- tailLine = ln;
789
- break;
790
- }
791
- processConfirmedLine(cp, ln, text);
792
- cp.confirmedOffset = end + 1;
793
- start = end + 1;
794
- }
795
- const earliestUnresolved = settleRefsAndEarliestUnresolved(cp);
796
- const defListSettled = (c) => {
797
- if (!options.defListEnabled || c.blankRun >= 2) return true;
798
- if (c.defListSettled !== null) return c.defListSettled;
799
- if (tailLine) return !canBecomeDdLine(tailLine.text, false);
800
- return false;
801
- };
802
- const tailRetroactive = tailLine !== null && tailCarriesRetroactive(tailLine.text);
803
- let boundary = 0;
804
- for (let i = cp.candidates.length - 1; i >= 0 && !tailRetroactive; i--) {
805
- const c = cp.candidates[i];
806
- if (!c.htmlBalanced || c.hazard || c.seamRisk) continue;
807
- if (c.offset > cp.phasePoisonedAt) continue;
808
- if (c.offset > earliestUnresolved) continue;
809
- if (!defListSettled(c)) continue;
810
- boundary = c.offset;
811
- break;
812
- }
813
- return { boundary, checkpoint: cp };
814
- }
815
- function pendingFenceCloser(checkpoint) {
816
- const cp = checkpoint;
817
- if (cp.phasePoisonedAt !== Infinity) return "";
818
- if (cp.mdBlock.kind === "fence" && cp.mdBlock.indent === 0) return cp.mdBlock.char.repeat(cp.mdBlock.len);
819
- if (cp.mdBlock.kind === "math" && cp.mdBlock.indent === 0) return "$".repeat(cp.mdBlock.len);
820
- return "";
821
- }
767
+ var SCANNER_NAME_LISTS = [
768
+ ["type1", TYPE1_NAMES],
769
+ ["rawText", RAW_TEXT_ELEMENTS],
770
+ ["type6", TYPE6_NAMES],
771
+ ["void", VOID_TAGS],
772
+ ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
773
+ // Added with F28, and it is the list that would have made F28 visible: the
774
+ // derived census alphabet partitions names by their membership across THIS
775
+ // table, so before it existed `frame` shared one 39-name class with `div`
776
+ // and could never be sampled apart from it.
777
+ ["noElement", NO_ELEMENT_NAMES],
778
+ ["tablePart", TABLE_PART_NAMES],
779
+ ["scopeBarrier", SCOPE_BARRIER_NAMES],
780
+ ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
781
+ ];
782
+
783
+ // src/components/incrementalParse/freezeLineTransition.ts
822
784
  function floatingResidue(text, commentOpenAtStart) {
823
785
  let out = "";
824
786
  let open = commentOpenAtStart;
@@ -1468,24 +1430,66 @@ function processConfirmedLine(cp, ln, text) {
1468
1430
  }
1469
1431
  }
1470
1432
  }
1471
- var SCANNER_NAME_LISTS = [
1472
- ["type1", TYPE1_NAMES],
1473
- ["rawText", RAW_TEXT_ELEMENTS],
1474
- ["type6", TYPE6_NAMES],
1475
- ["void", VOID_TAGS],
1476
- ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
1477
- // Added with F28, and it is the list that would have made F28 visible: the
1478
- // derived census alphabet partitions names by their membership across THIS
1479
- // table, so before it existed `frame` shared one 39-name class with `div`
1480
- // and could never be sampled apart from it.
1481
- ["noElement", NO_ELEMENT_NAMES],
1482
- ["tablePart", TABLE_PART_NAMES],
1483
- ["scopeBarrier", SCOPE_BARRIER_NAMES],
1484
- ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
1485
- ];
1486
1433
 
1487
- // src/components/incrementalParse/spliceParse.ts
1488
- var import_micromark_util_normalize_identifier2 = require("micromark-util-normalize-identifier");
1434
+ // src/components/incrementalParse/computeFreezeBoundary.ts
1435
+ function computeFreezeBoundary(text, options, resume) {
1436
+ const mathFlow = options.mathFlow ?? true;
1437
+ const referenceTaint = options.referenceTaint ?? true;
1438
+ const prev = resume;
1439
+ const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
1440
+ let start = cp.confirmedOffset;
1441
+ let tailLine = null;
1442
+ while (start < text.length) {
1443
+ let end = text.length;
1444
+ let textEnd = text.length;
1445
+ {
1446
+ const nl = text.indexOf("\n", start);
1447
+ const cr = text.indexOf("\r", start);
1448
+ if (cr !== -1 && (nl === -1 || cr < nl)) {
1449
+ textEnd = cr;
1450
+ end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
1451
+ } else if (nl !== -1) {
1452
+ textEnd = nl;
1453
+ end = nl;
1454
+ }
1455
+ }
1456
+ const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
1457
+ const lineText = text.slice(start, textEnd);
1458
+ const ln = {
1459
+ start,
1460
+ end,
1461
+ text: lineText,
1462
+ blank: confirmed && isMdBlank(lineText),
1463
+ indent: computeIndent(lineText)
1464
+ };
1465
+ if (!confirmed) {
1466
+ tailLine = ln;
1467
+ break;
1468
+ }
1469
+ processConfirmedLine(cp, ln, text);
1470
+ cp.confirmedOffset = end + 1;
1471
+ start = end + 1;
1472
+ }
1473
+ const earliestUnresolved = settleRefsAndEarliestUnresolved(cp);
1474
+ const defListSettled = (c) => {
1475
+ if (!options.defListEnabled || c.blankRun >= 2) return true;
1476
+ if (c.defListSettled !== null) return c.defListSettled;
1477
+ if (tailLine) return !canBecomeDdLine(tailLine.text, false);
1478
+ return false;
1479
+ };
1480
+ const tailRetroactive = tailLine !== null && tailCarriesRetroactive(tailLine.text);
1481
+ let boundary = 0;
1482
+ for (let i = cp.candidates.length - 1; i >= 0 && !tailRetroactive; i--) {
1483
+ const c = cp.candidates[i];
1484
+ if (!c.htmlBalanced || c.hazard || c.seamRisk) continue;
1485
+ if (c.offset > cp.phasePoisonedAt) continue;
1486
+ if (c.offset > earliestUnresolved) continue;
1487
+ if (!defListSettled(c)) continue;
1488
+ boundary = c.offset;
1489
+ break;
1490
+ }
1491
+ return { boundary, checkpoint: cp };
1492
+ }
1489
1493
 
1490
1494
  // src/components/hastPredicates.ts
1491
1495
  function isFootnoteSection(node) {
@@ -1533,121 +1537,7 @@ function attributeHastChildren(mdast, hast, stopAt = Infinity) {
1533
1537
  }
1534
1538
  }
1535
1539
 
1536
- // src/components/incrementalParse/spliceParse.ts
1537
- function collectPrefixInjection(mdast, content, boundary, resume) {
1538
- const resumeValid = resume != null && resume.boundary <= boundary;
1539
- if (resumeValid && resume.uninjectable) return { events: resume.events, uninjectable: true };
1540
- const resumeAt = resumeValid ? resume.boundary : 0;
1541
- const events = resumeAt > 0 ? cloneEventsForAppend(resume.events) : [];
1542
- let uninjectable = false;
1543
- let cacheable = true;
1544
- const pushRef = (token) => {
1545
- const last = events[events.length - 1];
1546
- if (last && last.kind === "refs") last.tokens.push(token);
1547
- else events.push({ kind: "refs", tokens: [token] });
1548
- };
1549
- const visit7 = (node, nested) => {
1550
- const type = node.type;
1551
- if (type === "definition") {
1552
- const start = node.position?.start?.offset;
1553
- const end = node.position?.end?.offset;
1554
- if (start !== void 0 && end !== void 0 && start < boundary) {
1555
- const slice = content.slice(start, end);
1556
- if (nested && slice.includes("\n")) uninjectable = true;
1557
- else events.push({ kind: "def", source: slice });
1558
- }
1559
- return;
1560
- }
1561
- if (type === "footnoteDefinition") {
1562
- const start = node.position?.start;
1563
- const end = node.position?.end?.offset;
1564
- if (start?.offset === void 0 || end === void 0 || start.offset >= boundary) return;
1565
- if (nested) {
1566
- uninjectable = true;
1567
- return;
1568
- }
1569
- const lineStart = start.offset - (start.column - 1);
1570
- events.push({
1571
- kind: "footnoteDef",
1572
- source: content.slice(lineStart, end),
1573
- origStart: lineStart,
1574
- origLine: start.line
1575
- });
1576
- return;
1577
- }
1578
- if (type === "footnoteReference") {
1579
- const start = node.position?.start?.offset;
1580
- const end = node.position?.end?.offset;
1581
- if (start === void 0 || end === void 0) {
1582
- uninjectable = true;
1583
- return;
1584
- }
1585
- pushRef(content.slice(start, end));
1586
- return;
1587
- }
1588
- const children = node.children;
1589
- if (children) {
1590
- for (const child of children) visit7(child, true);
1591
- }
1592
- };
1593
- let lastStart = -1;
1594
- for (const child of mdast.children) {
1595
- const start = child.position?.start?.offset;
1596
- if (start !== void 0) {
1597
- if (start < lastStart) return { events: [], uninjectable: true, cacheable: false };
1598
- lastStart = start;
1599
- }
1600
- if (start === void 0) {
1601
- if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
1602
- cacheable = false;
1603
- visit7(child, false);
1604
- continue;
1605
- }
1606
- if (start >= boundary || start < resumeAt) continue;
1607
- visit7(child, false);
1608
- }
1609
- return { events, uninjectable, cacheable };
1610
- }
1611
- function cloneEventsForAppend(events) {
1612
- const out = events.slice();
1613
- const last = out[out.length - 1];
1614
- if (last && last.kind === "refs") out[out.length - 1] = { kind: "refs", tokens: last.tokens.slice() };
1615
- return out;
1616
- }
1617
- var TERMINATOR_LABEL = "__aimd_injection_terminator__";
1618
- var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
1619
- function tailMentionsTerminator(tailSource) {
1620
- return (0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(tailSource).replace(/\[ /g, "[").includes(`[${(0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(TERMINATOR_LABEL)}`);
1621
- }
1622
- function buildInjectionPrefix(events) {
1623
- if (events.length === 0) return { text: "", segments: [] };
1624
- let text = "";
1625
- let line = 1;
1626
- const segments = [];
1627
- for (const event of events) {
1628
- if (text.length > 0) {
1629
- text += "\n\n";
1630
- line += 2;
1631
- }
1632
- const injStart = text.length;
1633
- const source = event.kind === "refs" ? event.tokens.join(" ") : event.source;
1634
- if (event.kind === "footnoteDef") {
1635
- segments.push({
1636
- injStart,
1637
- injEnd: injStart + source.length,
1638
- offsetDelta: event.origStart - injStart,
1639
- lineDelta: event.origLine - line
1640
- });
1641
- }
1642
- text += source;
1643
- line += countNewlines(source);
1644
- }
1645
- return { text: `${text}
1646
-
1647
- ${INJECTION_TERMINATOR}
1648
-
1649
- `, segments };
1650
- }
1540
+ // src/components/incrementalParse/spliceCoordinates.ts
1651
1541
  function rebaseTree(node, offsetDelta, lineDelta) {
1652
1542
  rebaseTreeDual(node, EMPTY_SEGMENTS, offsetDelta, lineDelta);
1653
1543
  }
@@ -1676,6 +1566,23 @@ function rebaseDualWalk(node, segments, maxEnd, offsetDelta, lineDelta) {
1676
1566
  for (const child of children) rebaseDualWalk(child, segments, maxEnd, offsetDelta, lineDelta);
1677
1567
  }
1678
1568
  }
1569
+ function rebasePoint(point, offsetDelta, lineDelta) {
1570
+ return {
1571
+ line: point.line + lineDelta,
1572
+ column: point.column,
1573
+ offset: point.offset !== void 0 ? point.offset + offsetDelta : void 0
1574
+ };
1575
+ }
1576
+ function countNewlines(text, end = text.length) {
1577
+ let count = 0;
1578
+ for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
1579
+ for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
1580
+ if (text.charCodeAt(i + 1) !== 10) count += 1;
1581
+ }
1582
+ return count;
1583
+ }
1584
+
1585
+ // src/components/incrementalParse/spliceHtmlGuards.ts
1679
1586
  var TABLE_PART_TAG_RE = /<(?:td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/i;
1680
1587
  var TABLE_TOKEN_RE = /<(\/?)(table|td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/gi;
1681
1588
  function hasStrayTablePart(values) {
@@ -1757,127 +1664,26 @@ function rawTextRegionCrossesOut(values) {
1757
1664
  }
1758
1665
  return false;
1759
1666
  }
1760
- function spliceTrees(input) {
1761
- const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
1762
- const injectedLen = injectionPrefix.length;
1763
- const injectedLines = countNewlines(injectionPrefix);
1764
- const prefixLines = countNewlines(content, boundary);
1765
- const offsetDelta = boundary - injectedLen;
1766
- const lineDelta = prefixLines - injectedLines;
1767
- const prefixMdast = [];
1768
- for (const child of prevMdast.children) {
1769
- const start = child.position?.start?.offset;
1770
- if (start !== void 0 && start < boundary) prefixMdast.push(child);
1771
- }
1772
- const attrs = attributeHastChildren(prevMdast, prevHast, boundary);
1773
- const cutRegion = [];
1774
- for (let i = 0; i < prevHast.children.length; i++) {
1775
- if (attrs[i] < boundary) {
1776
- const node2 = prevHast.children[i];
1777
- const end = node2.position?.end?.offset;
1778
- if (end !== void 0 && end > boundary) return null;
1779
- if (isTrailingLiteralText(node2)) {
1780
- const prev = i > 0 ? prevHast.children[i - 1] : void 0;
1781
- if (!prev || prev.type !== "element" || prev.position === void 0) return null;
1782
- if (!ownsTrailingLiteral(prev, node2, prefixMdast))
1783
- return null;
1784
- }
1785
- cutRegion.push(node2);
1786
- continue;
1787
- }
1788
- const node = prevHast.children[i];
1789
- if (i > 0 && attrs[i - 1] < boundary && prevHast.children[i - 1].type === "element" && isTrailingLiteralText(node) && // …and only when that element really IS an html block's output: a
1790
- // raw literal can only trail an `html` mdast node. A position-less
1791
- // text after a `<p>` is the NEXT block's remnant — a stray end tag
1792
- // (`</t>\na`) parse5 dropped, whose text merged with the wrap
1793
- // separator — owned by the tail, which re-parses it; freezing it
1794
- // here duplicated it (v2.4.0 review P3). Falls through to the
1795
- // remnant look-ahead below, which bails to a full parse. And the
1796
- // literal must really be THAT block's trailing text: the block's raw
1797
- // source ends with it. A dropped-tag block right after a frozen html
1798
- // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
1799
- // position, and freezing it duplicated it (release soak of the fix).
1800
- ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
1801
- cutRegion.push(node);
1802
- break;
1803
- }
1804
- for (let j = i; j < prevHast.children.length; j++) {
1805
- const look = prevHast.children[j];
1806
- if (look.type === "element" || look.position !== void 0) break;
1807
- if (isTrailingLiteralText(look)) return null;
1808
- }
1809
- break;
1810
- }
1811
- const tailMdastChildren = tailMdast.children.filter((child) => {
1812
- const start = child.position?.start?.offset;
1813
- return !(start !== void 0 && start < injectedLen);
1814
- });
1815
- const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
1816
- const prefixHtmlValues = prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []);
1817
- if (hasStrayTablePart(prefixHtmlValues)) return null;
1818
- if (rawTextRegionCrossesOut(prefixHtmlValues)) return null;
1819
- const leadingHtml = [];
1820
- for (const child of tailMdastChildren) {
1821
- if (isWrapInvisible(child)) continue;
1822
- if (child.type !== "html") break;
1823
- if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
1824
- leadingHtml.push(child.value);
1667
+ function isSanitizeStrippedConstruct(value) {
1668
+ const v = value.trim();
1669
+ return v.startsWith("<!--") && v.endsWith("-->") || v.startsWith("<?") && v.endsWith("?>") || v.startsWith("<![CDATA[") && v.endsWith("]]>") || /^<![A-Za-z]/.test(v) && !/^<!doctype/i.test(v) && v.endsWith(">");
1670
+ }
1671
+ function isExactSanitizeStrippedConstruct(value) {
1672
+ if (value.startsWith("<!--")) {
1673
+ if (value.length < 7 || !value.endsWith("-->")) return false;
1674
+ const close = value.indexOf("-->", 4);
1675
+ const bangClose = value.indexOf("--!>", 4);
1676
+ if (close !== value.length - 3) return false;
1677
+ return bangClose === -1 || bangClose > close;
1825
1678
  }
1826
- if (headRoutedCaptureUnclosed(leadingHtml)) return null;
1827
- const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
1828
- if (aligned === null) return null;
1829
- const hastChildren = aligned.children;
1830
- const interiorFinalLiteral = aligned.interiorFinalLiteral;
1831
- const stripResult = stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible);
1832
- if (stripResult === null) return null;
1833
- const strippedTailHast = stripResult.rest;
1834
- for (const child of tailMdastChildren) rebaseTree(child, offsetDelta, lineDelta);
1835
- for (const child of strippedTailHast) rebaseTreeDual(child, injectedSegments, offsetDelta, lineDelta);
1836
- const mdastChildren = prefixMdast.concat(tailMdastChildren);
1837
- const seamMergeVerdict = stripResult.remnantMerged ?? tailLeadingTextIsHoist(tailMdastChildren, strippedTailHast);
1838
- if (seamMergeVerdict === null) return null;
1839
- const seamMergeAllowed = seamMergeVerdict;
1840
- let firstTailChild = true;
1841
- for (const child of strippedTailHast) {
1842
- const tailEnd = hastChildren[hastChildren.length - 1];
1843
- if (firstTailChild && child.type === "text" && child.position !== void 0) {
1844
- return null;
1845
- }
1846
- if (firstTailChild && tailEnd && tailEnd.type === "text" && child.type === "text" && child.position === void 0) {
1847
- if (interiorFinalLiteral && tailEnd.value.trim() !== "") {
1848
- if (seamMergeAllowed) return null;
1849
- firstTailChild = false;
1850
- hastChildren.push(child);
1851
- continue;
1852
- }
1853
- const literalSeam = tailEnd.value.trim() !== "" && !tailWrapVisible;
1854
- if (seamMergeAllowed || literalSeam) {
1855
- hastChildren[hastChildren.length - 1] = { type: "text", value: tailEnd.value + child.value };
1856
- firstTailChild = false;
1857
- continue;
1858
- }
1859
- }
1860
- firstTailChild = false;
1861
- hastChildren.push(child);
1679
+ if (/^<!doctype/i.test(value)) return false;
1680
+ if (value.startsWith("<?") || /^<!(?:[A-Za-z]|\[CDATA\[)/.test(value)) {
1681
+ return value.indexOf(">") === value.length - 1;
1862
1682
  }
1863
- if (!hastChildren.some((child) => child.position !== void 0)) return null;
1864
- const docStart = { line: 1, column: 1, offset: 0 };
1865
- const mdastRootPosition = tailMdast.position ? { start: docStart, end: rebasePoint(tailMdast.position.end, offsetDelta, lineDelta) } : prevMdast.position;
1866
- const hastRootPosition = mdastRootPosition;
1867
- const mdast = { type: "root", children: mdastChildren };
1868
- if (mdastRootPosition) mdast.position = mdastRootPosition;
1869
- const hast = { type: "root", children: hastChildren };
1870
- if (hastRootPosition) hast.position = hastRootPosition;
1871
- if (prevHast.data !== void 0) hast.data = prevHast.data;
1872
- return { mdast, hast };
1873
- }
1874
- function rebasePoint(point, offsetDelta, lineDelta) {
1875
- return {
1876
- line: point.line + lineDelta,
1877
- column: point.column,
1878
- offset: point.offset !== void 0 ? point.offset + offsetDelta : void 0
1879
- };
1683
+ return false;
1880
1684
  }
1685
+
1686
+ // src/components/incrementalParse/prefixAlignment.ts
1881
1687
  function isWrapInvisible(node) {
1882
1688
  return node.type === "definition" || node.type === "footnoteDefinition";
1883
1689
  }
@@ -2019,96 +1825,303 @@ function stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible) {
2019
1825
  idx += 1;
2020
1826
  continue;
2021
1827
  }
2022
- const start = node.position?.start?.offset;
2023
- if (start === void 0 || start >= injectedLen) return null;
2024
- consumed += 1;
2025
- idx += 1;
1828
+ const start = node.position?.start?.offset;
1829
+ if (start === void 0 || start >= injectedLen) return null;
1830
+ consumed += 1;
1831
+ idx += 1;
1832
+ }
1833
+ if (consumed < injectedVisibleCount) return null;
1834
+ let remnant = "";
1835
+ let remnantMerged = null;
1836
+ if (tailWrapVisible) {
1837
+ const gap = children[idx];
1838
+ if (!gap || !isSeparatorText(gap)) return null;
1839
+ remnant = gap.value.slice(1);
1840
+ remnantMerged = remnant !== "";
1841
+ idx += 1;
1842
+ }
1843
+ const rest = children.slice(idx);
1844
+ if (remnant !== "") rest.unshift({ type: "text", value: remnant });
1845
+ return { rest, remnantMerged };
1846
+ }
1847
+ function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
1848
+ const firstText = tailHastChildren[0];
1849
+ const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
1850
+ if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
1851
+ if (!firstText) return false;
1852
+ if (!isSeparatorText(firstText)) {
1853
+ if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
1854
+ if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
1855
+ if (isSanitizeStrippedConstruct(firstVisible.value)) return false;
1856
+ return null;
1857
+ }
1858
+ return false;
1859
+ }
1860
+ if (!firstVisible) return false;
1861
+ const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
1862
+ if (firstContent) {
1863
+ const start = firstContent.position?.start?.offset;
1864
+ const vStart = firstVisible.position?.start?.offset;
1865
+ const vEnd = firstVisible.position?.end?.offset;
1866
+ if (start !== void 0 && vStart !== void 0 && vEnd !== void 0) {
1867
+ if (start >= vStart && start < vEnd) return true;
1868
+ }
1869
+ }
1870
+ if (firstVisible.type === "math") return false;
1871
+ if (firstVisible.type === "html" && isSanitizeStrippedConstruct(firstVisible.value)) return false;
1872
+ return null;
1873
+ }
1874
+ function isSeparatorText(node) {
1875
+ return node.type === "text" && node.position === void 0 && node.value.trim() === "";
1876
+ }
1877
+ function ownsTrailingLiteral(el, literal, prefixMdast) {
1878
+ const start = el.position?.start?.offset;
1879
+ if (start === void 0) return false;
1880
+ const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
1881
+ if (!owner || owner.type !== "html") return false;
1882
+ const text = literal.value.trim();
1883
+ return text !== "" && owner.value.trimEnd().endsWith(text);
1884
+ }
1885
+ function isTrailingLiteralText(node) {
1886
+ return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
1887
+ }
1888
+ function countTrailingNewlines(value) {
1889
+ let n = 0;
1890
+ for (let i = value.length - 1; i >= 0 && value[i] === "\n"; i--) n += 1;
1891
+ return n;
1892
+ }
1893
+
1894
+ // src/components/incrementalParse/prefixInjection.ts
1895
+ var import_micromark_util_normalize_identifier2 = require("micromark-util-normalize-identifier");
1896
+ function collectPrefixInjection(mdast, content, boundary, resume) {
1897
+ const resumeValid = resume != null && resume.boundary <= boundary;
1898
+ if (resumeValid && resume.uninjectable) return { events: resume.events, uninjectable: true };
1899
+ const resumeAt = resumeValid ? resume.boundary : 0;
1900
+ const events = resumeAt > 0 ? cloneEventsForAppend(resume.events) : [];
1901
+ let uninjectable = false;
1902
+ let cacheable = true;
1903
+ const pushRef = (token) => {
1904
+ const last = events[events.length - 1];
1905
+ if (last && last.kind === "refs") last.tokens.push(token);
1906
+ else events.push({ kind: "refs", tokens: [token] });
1907
+ };
1908
+ const visit7 = (node, nested) => {
1909
+ const type = node.type;
1910
+ if (type === "definition") {
1911
+ const start = node.position?.start?.offset;
1912
+ const end = node.position?.end?.offset;
1913
+ if (start !== void 0 && end !== void 0 && start < boundary) {
1914
+ const slice = content.slice(start, end);
1915
+ if (nested && slice.includes("\n")) uninjectable = true;
1916
+ else events.push({ kind: "def", source: slice });
1917
+ }
1918
+ return;
1919
+ }
1920
+ if (type === "footnoteDefinition") {
1921
+ const start = node.position?.start;
1922
+ const end = node.position?.end?.offset;
1923
+ if (start?.offset === void 0 || end === void 0 || start.offset >= boundary) return;
1924
+ if (nested) {
1925
+ uninjectable = true;
1926
+ return;
1927
+ }
1928
+ const lineStart = start.offset - (start.column - 1);
1929
+ events.push({
1930
+ kind: "footnoteDef",
1931
+ source: content.slice(lineStart, end),
1932
+ origStart: lineStart,
1933
+ origLine: start.line
1934
+ });
1935
+ return;
1936
+ }
1937
+ if (type === "footnoteReference") {
1938
+ const start = node.position?.start?.offset;
1939
+ const end = node.position?.end?.offset;
1940
+ if (start === void 0 || end === void 0) {
1941
+ uninjectable = true;
1942
+ return;
1943
+ }
1944
+ pushRef(content.slice(start, end));
1945
+ return;
1946
+ }
1947
+ const children = node.children;
1948
+ if (children) {
1949
+ for (const child of children) visit7(child, true);
1950
+ }
1951
+ };
1952
+ let lastStart = -1;
1953
+ for (const child of mdast.children) {
1954
+ const start = child.position?.start?.offset;
1955
+ if (start !== void 0) {
1956
+ if (start < lastStart) return { events: [], uninjectable: true, cacheable: false };
1957
+ lastStart = start;
1958
+ }
1959
+ if (start === void 0) {
1960
+ if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
1961
+ cacheable = false;
1962
+ visit7(child, false);
1963
+ continue;
1964
+ }
1965
+ if (start >= boundary || start < resumeAt) continue;
1966
+ visit7(child, false);
1967
+ }
1968
+ return { events, uninjectable, cacheable };
1969
+ }
1970
+ function cloneEventsForAppend(events) {
1971
+ const out = events.slice();
1972
+ const last = out[out.length - 1];
1973
+ if (last && last.kind === "refs") out[out.length - 1] = { kind: "refs", tokens: last.tokens.slice() };
1974
+ return out;
1975
+ }
1976
+ var TERMINATOR_LABEL = "__aimd_injection_terminator__";
1977
+ var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
1978
+ function tailMentionsTerminator(tailSource) {
1979
+ return (0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(tailSource).replace(/\[ /g, "[").includes(`[${(0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(TERMINATOR_LABEL)}`);
1980
+ }
1981
+ function buildInjectionPrefix(events) {
1982
+ if (events.length === 0) return { text: "", segments: [] };
1983
+ let text = "";
1984
+ let line = 1;
1985
+ const segments = [];
1986
+ for (const event of events) {
1987
+ if (text.length > 0) {
1988
+ text += "\n\n";
1989
+ line += 2;
1990
+ }
1991
+ const injStart = text.length;
1992
+ const source = event.kind === "refs" ? event.tokens.join(" ") : event.source;
1993
+ if (event.kind === "footnoteDef") {
1994
+ segments.push({
1995
+ injStart,
1996
+ injEnd: injStart + source.length,
1997
+ offsetDelta: event.origStart - injStart,
1998
+ lineDelta: event.origLine - line
1999
+ });
2000
+ }
2001
+ text += source;
2002
+ line += countNewlines(source);
2003
+ }
2004
+ return { text: `${text}
2005
+
2006
+ ${INJECTION_TERMINATOR}
2007
+
2008
+ `, segments };
2009
+ }
2010
+
2011
+ // src/components/incrementalParse/spliceParse.ts
2012
+ function spliceTrees(input) {
2013
+ const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
2014
+ const injectedLen = injectionPrefix.length;
2015
+ const injectedLines = countNewlines(injectionPrefix);
2016
+ const prefixLines = countNewlines(content, boundary);
2017
+ const offsetDelta = boundary - injectedLen;
2018
+ const lineDelta = prefixLines - injectedLines;
2019
+ const prefixMdast = [];
2020
+ for (const child of prevMdast.children) {
2021
+ const start = child.position?.start?.offset;
2022
+ if (start !== void 0 && start < boundary) prefixMdast.push(child);
2023
+ }
2024
+ const attrs = attributeHastChildren(prevMdast, prevHast, boundary);
2025
+ const cutRegion = [];
2026
+ for (let i = 0; i < prevHast.children.length; i++) {
2027
+ if (attrs[i] < boundary) {
2028
+ const node2 = prevHast.children[i];
2029
+ const end = node2.position?.end?.offset;
2030
+ if (end !== void 0 && end > boundary) return null;
2031
+ if (isTrailingLiteralText(node2)) {
2032
+ const prev = i > 0 ? prevHast.children[i - 1] : void 0;
2033
+ if (!prev || prev.type !== "element" || prev.position === void 0) return null;
2034
+ if (!ownsTrailingLiteral(prev, node2, prefixMdast))
2035
+ return null;
2036
+ }
2037
+ cutRegion.push(node2);
2038
+ continue;
2039
+ }
2040
+ const node = prevHast.children[i];
2041
+ if (i > 0 && attrs[i - 1] < boundary && prevHast.children[i - 1].type === "element" && isTrailingLiteralText(node) && // …and only when that element really IS an html block's output: a
2042
+ // raw literal can only trail an `html` mdast node. A position-less
2043
+ // text after a `<p>` is the NEXT block's remnant — a stray end tag
2044
+ // (`</t>\na`) parse5 dropped, whose text merged with the wrap
2045
+ // separator — owned by the tail, which re-parses it; freezing it
2046
+ // here duplicated it (v2.4.0 review P3). Falls through to the
2047
+ // remnant look-ahead below, which bails to a full parse. And the
2048
+ // literal must really be THAT block's trailing text: the block's raw
2049
+ // source ends with it. A dropped-tag block right after a frozen html
2050
+ // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
2051
+ // position, and freezing it duplicated it (release soak of the fix).
2052
+ ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
2053
+ cutRegion.push(node);
2054
+ break;
2055
+ }
2056
+ for (let j = i; j < prevHast.children.length; j++) {
2057
+ const look = prevHast.children[j];
2058
+ if (look.type === "element" || look.position !== void 0) break;
2059
+ if (isTrailingLiteralText(look)) return null;
2060
+ }
2061
+ break;
2026
2062
  }
2027
- if (consumed < injectedVisibleCount) return null;
2028
- let remnant = "";
2029
- let remnantMerged = null;
2030
- if (tailWrapVisible) {
2031
- const gap = children[idx];
2032
- if (!gap || !isSeparatorText(gap)) return null;
2033
- remnant = gap.value.slice(1);
2034
- remnantMerged = remnant !== "";
2035
- idx += 1;
2063
+ const tailMdastChildren = tailMdast.children.filter((child) => {
2064
+ const start = child.position?.start?.offset;
2065
+ return !(start !== void 0 && start < injectedLen);
2066
+ });
2067
+ const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
2068
+ const prefixHtmlValues = prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []);
2069
+ if (hasStrayTablePart(prefixHtmlValues)) return null;
2070
+ if (rawTextRegionCrossesOut(prefixHtmlValues)) return null;
2071
+ const leadingHtml = [];
2072
+ for (const child of tailMdastChildren) {
2073
+ if (isWrapInvisible(child)) continue;
2074
+ if (child.type !== "html") break;
2075
+ if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
2076
+ leadingHtml.push(child.value);
2036
2077
  }
2037
- const rest = children.slice(idx);
2038
- if (remnant !== "") rest.unshift({ type: "text", value: remnant });
2039
- return { rest, remnantMerged };
2040
- }
2041
- function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
2042
- const firstText = tailHastChildren[0];
2043
- const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
2044
- if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
2045
- if (!firstText) return false;
2046
- if (!isSeparatorText(firstText)) {
2047
- if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
2048
- if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
2049
- if (isSanitizeStrippedConstruct(firstVisible.value)) return false;
2078
+ if (headRoutedCaptureUnclosed(leadingHtml)) return null;
2079
+ const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
2080
+ if (aligned === null) return null;
2081
+ const hastChildren = aligned.children;
2082
+ const interiorFinalLiteral = aligned.interiorFinalLiteral;
2083
+ const stripResult = stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible);
2084
+ if (stripResult === null) return null;
2085
+ const strippedTailHast = stripResult.rest;
2086
+ for (const child of tailMdastChildren) rebaseTree(child, offsetDelta, lineDelta);
2087
+ for (const child of strippedTailHast) rebaseTreeDual(child, injectedSegments, offsetDelta, lineDelta);
2088
+ const mdastChildren = prefixMdast.concat(tailMdastChildren);
2089
+ const seamMergeVerdict = stripResult.remnantMerged ?? tailLeadingTextIsHoist(tailMdastChildren, strippedTailHast);
2090
+ if (seamMergeVerdict === null) return null;
2091
+ const seamMergeAllowed = seamMergeVerdict;
2092
+ let firstTailChild = true;
2093
+ for (const child of strippedTailHast) {
2094
+ const tailEnd = hastChildren[hastChildren.length - 1];
2095
+ if (firstTailChild && child.type === "text" && child.position !== void 0) {
2050
2096
  return null;
2051
2097
  }
2052
- return false;
2053
- }
2054
- if (!firstVisible) return false;
2055
- const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
2056
- if (firstContent) {
2057
- const start = firstContent.position?.start?.offset;
2058
- const vStart = firstVisible.position?.start?.offset;
2059
- const vEnd = firstVisible.position?.end?.offset;
2060
- if (start !== void 0 && vStart !== void 0 && vEnd !== void 0) {
2061
- if (start >= vStart && start < vEnd) return true;
2098
+ if (firstTailChild && tailEnd && tailEnd.type === "text" && child.type === "text" && child.position === void 0) {
2099
+ if (interiorFinalLiteral && tailEnd.value.trim() !== "") {
2100
+ if (seamMergeAllowed) return null;
2101
+ firstTailChild = false;
2102
+ hastChildren.push(child);
2103
+ continue;
2104
+ }
2105
+ const literalSeam = tailEnd.value.trim() !== "" && !tailWrapVisible;
2106
+ if (seamMergeAllowed || literalSeam) {
2107
+ hastChildren[hastChildren.length - 1] = { type: "text", value: tailEnd.value + child.value };
2108
+ firstTailChild = false;
2109
+ continue;
2110
+ }
2062
2111
  }
2112
+ firstTailChild = false;
2113
+ hastChildren.push(child);
2063
2114
  }
2064
- if (firstVisible.type === "math") return false;
2065
- if (firstVisible.type === "html" && isSanitizeStrippedConstruct(firstVisible.value)) return false;
2066
- return null;
2067
- }
2068
- function isSanitizeStrippedConstruct(value) {
2069
- const v = value.trim();
2070
- return v.startsWith("<!--") && v.endsWith("-->") || v.startsWith("<?") && v.endsWith("?>") || v.startsWith("<![CDATA[") && v.endsWith("]]>") || /^<![A-Za-z]/.test(v) && !/^<!doctype/i.test(v) && v.endsWith(">");
2071
- }
2072
- function isExactSanitizeStrippedConstruct(value) {
2073
- if (value.startsWith("<!--")) {
2074
- if (value.length < 7 || !value.endsWith("-->")) return false;
2075
- const close = value.indexOf("-->", 4);
2076
- const bangClose = value.indexOf("--!>", 4);
2077
- if (close !== value.length - 3) return false;
2078
- return bangClose === -1 || bangClose > close;
2079
- }
2080
- if (/^<!doctype/i.test(value)) return false;
2081
- if (value.startsWith("<?") || /^<!(?:[A-Za-z]|\[CDATA\[)/.test(value)) {
2082
- return value.indexOf(">") === value.length - 1;
2083
- }
2084
- return false;
2085
- }
2086
- function isSeparatorText(node) {
2087
- return node.type === "text" && node.position === void 0 && node.value.trim() === "";
2088
- }
2089
- function ownsTrailingLiteral(el, literal, prefixMdast) {
2090
- const start = el.position?.start?.offset;
2091
- if (start === void 0) return false;
2092
- const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
2093
- if (!owner || owner.type !== "html") return false;
2094
- const text = literal.value.trim();
2095
- return text !== "" && owner.value.trimEnd().endsWith(text);
2096
- }
2097
- function isTrailingLiteralText(node) {
2098
- return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
2099
- }
2100
- function countTrailingNewlines(value) {
2101
- let n = 0;
2102
- for (let i = value.length - 1; i >= 0 && value[i] === "\n"; i--) n += 1;
2103
- return n;
2104
- }
2105
- function countNewlines(text, end = text.length) {
2106
- let count = 0;
2107
- for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
2108
- for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
2109
- if (text.charCodeAt(i + 1) !== 10) count += 1;
2110
- }
2111
- return count;
2115
+ if (!hastChildren.some((child) => child.position !== void 0)) return null;
2116
+ const docStart = { line: 1, column: 1, offset: 0 };
2117
+ const mdastRootPosition = tailMdast.position ? { start: docStart, end: rebasePoint(tailMdast.position.end, offsetDelta, lineDelta) } : prevMdast.position;
2118
+ const hastRootPosition = mdastRootPosition;
2119
+ const mdast = { type: "root", children: mdastChildren };
2120
+ if (mdastRootPosition) mdast.position = mdastRootPosition;
2121
+ const hast = { type: "root", children: hastChildren };
2122
+ if (hastRootPosition) hast.position = hastRootPosition;
2123
+ if (prevHast.data !== void 0) hast.data = prevHast.data;
2124
+ return { mdast, hast };
2112
2125
  }
2113
2126
 
2114
2127
  // src/components/incrementalParse/advanceIncrementalParse.ts
@@ -2230,6 +2243,38 @@ function normalizeForMatch(s) {
2230
2243
  return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s);
2231
2244
  }
2232
2245
 
2246
+ // src/components/blankLineScanner.ts
2247
+ function createBlankLineScanner() {
2248
+ let end = 0;
2249
+ let newline = false;
2250
+ let cr = false;
2251
+ return (source, from = 0) => {
2252
+ if (from === 0) {
2253
+ end = 0;
2254
+ newline = false;
2255
+ cr = false;
2256
+ }
2257
+ for (let i = from; i < source.length; i++) {
2258
+ const c = source[i];
2259
+ if (c === "\n") {
2260
+ if (newline) {
2261
+ end = i + 1;
2262
+ newline = false;
2263
+ } else newline = true;
2264
+ cr = false;
2265
+ } else if (newline) {
2266
+ if ((c === " " || c === " ") && !cr) continue;
2267
+ if (c === "\r" && !cr) cr = true;
2268
+ else {
2269
+ newline = false;
2270
+ cr = false;
2271
+ }
2272
+ }
2273
+ }
2274
+ return end;
2275
+ };
2276
+ }
2277
+
2233
2278
  // src/components/collectDefLabels.ts
2234
2279
  var SCANNER_BOUNDARY_PROFILE = { defListEnabled: false, mathFlow: false, referenceTaint: false };
2235
2280
  function buildProcessor() {
@@ -2261,19 +2306,12 @@ var setsEqual = (a, b) => {
2261
2306
  for (const v of a) if (!b.has(v)) return false;
2262
2307
  return true;
2263
2308
  };
2264
- var BLANK_LINE_RE = /\r?\n[ \t]*\r?\n/g;
2265
- function lastRegionStart(source) {
2266
- BLANK_LINE_RE.lastIndex = 0;
2267
- let start = 0;
2268
- for (let m = BLANK_LINE_RE.exec(source); m !== null; m = BLANK_LINE_RE.exec(source)) {
2269
- start = m.index + m[0].length;
2270
- }
2271
- return start;
2272
- }
2273
2309
  var DEF_LINE_START_RE = /^[ \t>*+\d.)-]*\[(?:[^\]\\]|\\[\s\S])*\]:/m;
2274
2310
  function createDefLabelScanner(parse = collectDefLabels) {
2275
2311
  let prevSource = null;
2276
2312
  let prevLabels = null;
2313
+ const scanBlankLines = createBlankLineScanner();
2314
+ let regionStart = 0;
2277
2315
  let frozenEnd = 0;
2278
2316
  let frozenFootnotes = /* @__PURE__ */ new Set();
2279
2317
  let frozenLinks = /* @__PURE__ */ new Set();
@@ -2286,13 +2324,16 @@ function createDefLabelScanner(parse = collectDefLabels) {
2286
2324
  };
2287
2325
  return {
2288
2326
  scan(source) {
2327
+ if (source === prevSource && prevLabels !== null) return prevLabels;
2328
+ const previousRegionStart = regionStart;
2329
+ const appended = prevSource !== null && source.startsWith(prevSource);
2330
+ regionStart = scanBlankLines(source, appended ? prevSource.length : 0);
2289
2331
  let isAppend = false;
2290
2332
  if (prevSource !== null && prevLabels !== null) {
2291
2333
  if (source === prevSource) return prevLabels;
2292
2334
  if (source.startsWith(prevSource)) {
2293
2335
  isAppend = true;
2294
- const regionStart = lastRegionStart(prevSource);
2295
- const region = prevSource.slice(regionStart) + source.slice(prevSource.length);
2336
+ const region = source.slice(previousRegionStart);
2296
2337
  if (!DEF_LINE_START_RE.test(region)) {
2297
2338
  prevSource = source;
2298
2339
  return prevLabels;
@@ -2525,8 +2566,115 @@ function* extractContributions(mdast, options = {}) {
2525
2566
  for (const c of out) yield c;
2526
2567
  }
2527
2568
 
2569
+ // src/components/registryIndex.ts
2570
+ function buildRegistryIndex(registry) {
2571
+ const index = {
2572
+ footnotes: /* @__PURE__ */ new Map(),
2573
+ links: /* @__PURE__ */ new Map(),
2574
+ numbers: /* @__PURE__ */ new Map(),
2575
+ counts: /* @__PURE__ */ new Map(),
2576
+ occurrences: /* @__PURE__ */ new Map(),
2577
+ labelOccurrences: /* @__PURE__ */ new Map()
2578
+ };
2579
+ for (const sym of registry.chunkOrder) {
2580
+ const data = registry.chunkData.get(sym);
2581
+ if (!data) continue;
2582
+ for (const label of data.defs.keys()) if (!index.footnotes.has(label)) index.footnotes.set(label, sym);
2583
+ for (const label of data.linkDefs.keys()) if (!index.links.has(label)) index.links.set(label, sym);
2584
+ const local = /* @__PURE__ */ new Map();
2585
+ index.occurrences.set(sym, local);
2586
+ for (const ref of data.refs) {
2587
+ if (ref.kind !== "footnote") continue;
2588
+ const label = ref.label;
2589
+ if (!index.numbers.has(label)) index.numbers.set(label, index.numbers.size + 1);
2590
+ const total = (index.counts.get(label) ?? 0) + 1;
2591
+ index.counts.set(label, total);
2592
+ const prior = local.get(label);
2593
+ if (prior) prior.count++;
2594
+ else {
2595
+ const range = { start: total, count: 1 };
2596
+ local.set(label, range);
2597
+ let byChunk = index.labelOccurrences.get(label);
2598
+ if (!byChunk) index.labelOccurrences.set(label, byChunk = /* @__PURE__ */ new Map());
2599
+ byChunk.set(sym, range);
2600
+ }
2601
+ }
2602
+ }
2603
+ return index;
2604
+ }
2605
+
2606
+ // src/components/registrySubscriptions.ts
2607
+ function equal(a, b) {
2608
+ if (a.owner !== b.owner || a.url !== b.url || a.title !== b.title || a.number !== b.number || a.count !== b.count)
2609
+ return false;
2610
+ if (a.occurrences?.size !== b.occurrences?.size) return false;
2611
+ if (a.occurrences) {
2612
+ for (const [chunk, range] of a.occurrences) {
2613
+ const next = b.occurrences?.get(chunk);
2614
+ if (!next || next.start !== range.start || next.count !== range.count) return false;
2615
+ }
2616
+ }
2617
+ return true;
2618
+ }
2619
+ function createRegistrySubscriptions(read) {
2620
+ const groups = { link: /* @__PURE__ */ new Map(), footnote: /* @__PURE__ */ new Map() };
2621
+ const snapshot = (kind, label, { registry, index }) => {
2622
+ if (kind === "link") {
2623
+ const owner = index.links.get(label);
2624
+ const def = owner ? registry.chunkData.get(owner)?.linkDefs.get(label) : void 0;
2625
+ return { owner, url: def?.url, title: def?.title };
2626
+ }
2627
+ return {
2628
+ owner: index.footnotes.get(label),
2629
+ number: index.numbers.get(label),
2630
+ count: index.counts.get(label),
2631
+ occurrences: index.labelOccurrences.get(label)
2632
+ };
2633
+ };
2634
+ return {
2635
+ subscribe(kind, rawLabel, cb) {
2636
+ const label = normalizeId(rawLabel);
2637
+ const map = groups[kind];
2638
+ let group = map.get(label);
2639
+ if (!group) {
2640
+ group = { snapshot: snapshot(kind, label, read()), listeners: /* @__PURE__ */ new Set() };
2641
+ map.set(label, group);
2642
+ }
2643
+ const listener = () => cb();
2644
+ group.listeners.add(listener);
2645
+ return () => {
2646
+ group.listeners.delete(listener);
2647
+ if (group.listeners.size === 0 && map.get(label) === group) map.delete(label);
2648
+ };
2649
+ },
2650
+ collect() {
2651
+ if (groups.link.size === 0 && groups.footnote.size === 0) return [];
2652
+ const state = read();
2653
+ const callbacks = [];
2654
+ for (const kind of ["link", "footnote"]) {
2655
+ for (const [label, group] of groups[kind]) {
2656
+ const next = snapshot(kind, label, state);
2657
+ if (!equal(group.snapshot, next)) callbacks.push(...group.listeners);
2658
+ group.snapshot = next;
2659
+ }
2660
+ }
2661
+ return callbacks;
2662
+ }
2663
+ };
2664
+ }
2665
+
2528
2666
  // src/components/documentRegistry.ts
2529
2667
  function createRegistry(onEmpty) {
2668
+ let index;
2669
+ let indexedVersion = -1;
2670
+ const getIndex = () => {
2671
+ if (!index || indexedVersion !== reg.version) {
2672
+ index = buildRegistryIndex(reg);
2673
+ indexedVersion = reg.version;
2674
+ }
2675
+ return index;
2676
+ };
2677
+ const labels = createRegistrySubscriptions(() => ({ registry: reg, index: getIndex() }));
2530
2678
  const reg = {
2531
2679
  chunkOrder: [],
2532
2680
  chunkData: /* @__PURE__ */ new Map(),
@@ -2677,73 +2825,29 @@ function createRegistry(onEmpty) {
2677
2825
  this._subscribers.delete(cb);
2678
2826
  };
2679
2827
  },
2828
+ subscribeLabel(kind, label, cb) {
2829
+ return labels.subscribe(kind, label, cb);
2830
+ },
2680
2831
  canonicalFootnoteFor(label) {
2681
- const id = normalizeId(label);
2682
- for (const sym of this.chunkOrder) {
2683
- const data = this.chunkData.get(sym);
2684
- if (data?.defs.has(id)) return sym;
2685
- }
2686
- return null;
2832
+ return getIndex().footnotes.get(normalizeId(label)) ?? null;
2687
2833
  },
2688
2834
  canonicalLinkFor(label) {
2689
- const id = normalizeId(label);
2690
- for (const sym of this.chunkOrder) {
2691
- const data = this.chunkData.get(sym);
2692
- if (data?.linkDefs.has(id)) return sym;
2693
- }
2694
- return null;
2835
+ return getIndex().links.get(normalizeId(label)) ?? null;
2695
2836
  },
2696
2837
  globalNumber(label) {
2697
- const id = normalizeId(label);
2698
- let n = 0;
2699
- const seen = /* @__PURE__ */ new Set();
2700
- for (const sym of this.chunkOrder) {
2701
- const data = this.chunkData.get(sym);
2702
- if (!data) continue;
2703
- for (const ref of data.refs) {
2704
- if (ref.kind !== "footnote") continue;
2705
- if (!seen.has(ref.label)) {
2706
- seen.add(ref.label);
2707
- n++;
2708
- if (ref.label === id) return n;
2709
- }
2710
- }
2711
- }
2712
- return null;
2838
+ return getIndex().numbers.get(normalizeId(label)) ?? null;
2713
2839
  },
2714
2840
  resolveLinkDef(label) {
2715
- const sym = this.canonicalLinkFor(label);
2716
- if (!sym) return null;
2717
- return this.chunkData.get(sym)?.linkDefs.get(normalizeId(label)) ?? null;
2841
+ const id = normalizeId(label);
2842
+ const sym = getIndex().links.get(id);
2843
+ return sym ? this.chunkData.get(sym)?.linkDefs.get(id) ?? null : null;
2718
2844
  },
2719
2845
  getRefsForLabel(label) {
2720
- const id = normalizeId(label);
2721
- let n = 0;
2722
- for (const sym of this.chunkOrder) {
2723
- const data = this.chunkData.get(sym);
2724
- if (!data) continue;
2725
- for (const ref of data.refs) {
2726
- if (ref.kind === "footnote" && ref.label === id) n++;
2727
- }
2728
- }
2729
- return n;
2846
+ return getIndex().counts.get(normalizeId(label)) ?? 0;
2730
2847
  },
2731
2848
  globalOccurrenceForRef(chunkSym, label, localOccurrence) {
2732
- const id = normalizeId(label);
2733
- let global2 = 0;
2734
- for (const sym of this.chunkOrder) {
2735
- const data = this.chunkData.get(sym);
2736
- if (!data) continue;
2737
- let localCount = 0;
2738
- for (const ref of data.refs) {
2739
- if (ref.kind !== "footnote") continue;
2740
- if (ref.label !== id) continue;
2741
- localCount++;
2742
- global2++;
2743
- if (sym === chunkSym && localCount === localOccurrence) return global2;
2744
- }
2745
- }
2746
- return null;
2849
+ const range = getIndex().occurrences.get(chunkSym)?.get(normalizeId(label));
2850
+ return range && Number.isInteger(localOccurrence) && localOccurrence > 0 && localOccurrence <= range.count ? range.start + localOccurrence - 1 : null;
2747
2851
  },
2748
2852
  _notify() {
2749
2853
  this.version++;
@@ -2751,7 +2855,9 @@ function createRegistry(onEmpty) {
2751
2855
  this._notifyScheduled = true;
2752
2856
  queueMicrotask(() => {
2753
2857
  this._notifyScheduled = false;
2858
+ const labelCallbacks = labels.collect();
2754
2859
  for (const cb of [...this._subscribers]) cb();
2860
+ for (const cb of labelCallbacks) cb();
2755
2861
  });
2756
2862
  }
2757
2863
  };
@@ -2808,7 +2914,7 @@ var ENGINE_PLACEHOLDER_TAGS = /* @__PURE__ */ new Set([
2808
2914
  "cross-chunk-image"
2809
2915
  ]);
2810
2916
  var ENGINE_PROVENANCE_PROPERTY = "engineProvenance";
2811
- function walk(parent, provenance) {
2917
+ function walk(parent, provenance, ancestors) {
2812
2918
  const children = parent.children;
2813
2919
  let i = 0;
2814
2920
  while (i < children.length) {
@@ -2819,21 +2925,32 @@ function walk(parent, provenance) {
2819
2925
  const genuine = provenance !== "" && typeof stamped === "string" && stamped === provenance;
2820
2926
  if (genuine) {
2821
2927
  delete props[ENGINE_PROVENANCE_PROPERTY];
2822
- walk(node, provenance);
2928
+ if (ancestors && (node.tagName === "cross-chunk-link" || node.tagName === "cross-chunk-image")) {
2929
+ (node.data ??= {}).referenceAncestors = ancestors.slice();
2930
+ }
2931
+ ancestors?.push(
2932
+ node.tagName === "cross-chunk-link" ? "a" : node.tagName === "cross-chunk-image" ? "img" : node.tagName
2933
+ );
2934
+ walk(node, provenance, ancestors);
2935
+ ancestors?.pop();
2823
2936
  i += 1;
2824
2937
  } else {
2825
2938
  children.splice(i, 1, ...node.children);
2826
2939
  }
2827
2940
  continue;
2828
2941
  }
2829
- if (node.type === "element") walk(node, provenance);
2942
+ if (node.type === "element") {
2943
+ ancestors?.push(node.tagName);
2944
+ walk(node, provenance, ancestors);
2945
+ ancestors?.pop();
2946
+ }
2830
2947
  i += 1;
2831
2948
  }
2832
2949
  }
2833
2950
  function rehypeVerifyEngineTags(options) {
2834
2951
  const provenance = options?.provenance ?? "";
2835
2952
  return function transformer(tree) {
2836
- walk(tree, provenance);
2953
+ walk(tree, provenance, options?.referenceAncestors ? [] : void 0);
2837
2954
  };
2838
2955
  }
2839
2956
 
@@ -2854,16 +2971,18 @@ var import_remark_remove_comments = __toESM(require("remark-remove-comments"), 1
2854
2971
  // src/components/rehypeRebaseHashLinks.ts
2855
2972
  var import_unist_util_visit5 = require("unist-util-visit");
2856
2973
  var DEFAULT_PREFIX = "user-content-";
2974
+ function rebaseHashHref(href, prefix) {
2975
+ const hashPrefix = "#" + prefix;
2976
+ return href.startsWith("#") && !href.startsWith(hashPrefix) ? hashPrefix + href.slice(1) : href;
2977
+ }
2857
2978
  var rehypeRebaseHashLinks = (options) => {
2858
2979
  const prefix = options?.prefix ?? DEFAULT_PREFIX;
2859
- const hashPrefix = "#" + prefix;
2860
2980
  return (tree) => {
2861
2981
  (0, import_unist_util_visit5.visit)(tree, "element", (node) => {
2862
2982
  if (node.tagName !== "a") return;
2863
2983
  const href = node.properties?.href;
2864
2984
  if (typeof href !== "string" || !href.startsWith("#")) return;
2865
- if (href.startsWith(hashPrefix)) return;
2866
- node.properties.href = hashPrefix + href.slice(1);
2985
+ node.properties.href = rebaseHashHref(href, prefix);
2867
2986
  });
2868
2987
  };
2869
2988
  };
@@ -2949,7 +3068,15 @@ function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix, options) {
2949
3068
  [import_rehype_raw.default, { passThrough: [] }],
2950
3069
  // Unwrap forged engine placeholders BEFORE sanitize admits their tag
2951
3070
  // names. Only when the caller holds a credential (see the option's doc).
2952
- ...options ? [[rehypeVerifyEngineTags, { provenance: options.provenance }]] : [],
3071
+ ...options ? [
3072
+ [
3073
+ rehypeVerifyEngineTags,
3074
+ {
3075
+ provenance: options.provenance,
3076
+ ...sanitizeSchema2.ancestors?.a || sanitizeSchema2.ancestors?.img ? { referenceAncestors: true } : {}
3077
+ }
3078
+ ]
3079
+ ] : [],
2953
3080
  // Sanitize HTML while allowing <mark> (highlight), KaTeX class names,
2954
3081
  // and any extra protocols the caller permitted via the `sanitizeSchema`
2955
3082
  // prop. Override `clobberPrefix` with the instance-scoped value — the
@@ -3023,11 +3150,9 @@ function buildCrossChunkHandlers() {
3023
3150
  type: "element",
3024
3151
  tagName: "cross-chunk-link",
3025
3152
  properties: {
3026
- // `label` is the ORIGINAL source text (mdast's `label` field), NOT
3027
- // the normalized `identifier`. The placeholder uses it to construct
3028
- // hrefs that line up with mdast-util-to-hast's default `<li id>`
3029
- // which also preserves source case. Registry lookups normalize
3030
- // internally, so cross-chunk case-insensitive matching still works.
3153
+ // Display labels decode escapes; registry identifiers must retain
3154
+ // those bytes. Never use the display label as the lookup key.
3155
+ identifier: node.identifier,
3031
3156
  label: node.label ?? node.identifier,
3032
3157
  referenceType: node.referenceType,
3033
3158
  documentId: s.options.documentId,
@@ -3046,6 +3171,7 @@ function buildCrossChunkHandlers() {
3046
3171
  type: "element",
3047
3172
  tagName: "cross-chunk-image",
3048
3173
  properties: {
3174
+ identifier: node.identifier,
3049
3175
  label: node.label ?? node.identifier,
3050
3176
  referenceType: node.referenceType,
3051
3177
  alt: node.alt ?? "",
@@ -4308,8 +4434,8 @@ var sanitizeSchema = deepFreeze(
4308
4434
  attributes: {
4309
4435
  ...import_rehype_sanitize2.defaultSchema.attributes,
4310
4436
  code: mergeClassNameAllowlist(import_rehype_sanitize2.defaultSchema.attributes?.code, ["math-inline", "math-display"]),
4311
- "cross-chunk-link": ["label", "referenceType", "documentId", "localUrl", "localTitle"],
4312
- "cross-chunk-image": ["label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
4437
+ "cross-chunk-link": ["identifier", "label", "referenceType", "documentId", "localUrl", "localTitle"],
4438
+ "cross-chunk-image": ["identifier", "label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
4313
4439
  "footnote-sup": ["label", "localOccurrence", "localNumber", "documentId"]
4314
4440
  },
4315
4441
  strip: [.../* @__PURE__ */ new Set([...import_rehype_sanitize2.defaultSchema.strip || [], ...STRIPPED_TAGS])]
@@ -4345,6 +4471,49 @@ function sanitizeCrossChunkUrl(rawUrl, key, tagName, urlTransform, schema) {
4345
4471
  return String(transformed);
4346
4472
  }
4347
4473
 
4474
+ // src/components/resolveCrossChunkReference.ts
4475
+ var import_rehype_sanitize3 = __toESM(require("rehype-sanitize"), 1);
4476
+ var import_micromark_util_sanitize_uri3 = require("micromark-util-sanitize-uri");
4477
+ function resolveCrossChunkReference(input, schema, urlTransform, clobberPrefix) {
4478
+ const key = input.tagName === "a" ? "href" : "src";
4479
+ const element = {
4480
+ type: "element",
4481
+ tagName: input.tagName,
4482
+ properties: {
4483
+ [key]: (0, import_micromark_util_sanitize_uri3.normalizeUri)(input.url),
4484
+ ...input.tagName === "img" ? { alt: input.alt ?? "" } : {},
4485
+ ...input.title !== void 0 ? { title: input.title } : {}
4486
+ },
4487
+ children: input.tagName === "a" ? [{ type: "text", value: "__reference_children__" }] : []
4488
+ };
4489
+ const requiredAncestors = schema.ancestors?.[input.tagName];
4490
+ const recordedAncestors = input.node?.data?.referenceAncestors;
4491
+ let finalSchema = schema;
4492
+ if (requiredAncestors && Array.isArray(recordedAncestors) && requiredAncestors.some((tag) => recordedAncestors.includes(tag))) {
4493
+ const ancestors = { ...schema.ancestors };
4494
+ delete ancestors[input.tagName];
4495
+ finalSchema = { ...schema, ancestors };
4496
+ }
4497
+ const root2 = (0, import_rehype_sanitize3.default)({ ...finalSchema, clobberPrefix })({ type: "root", children: [element] });
4498
+ const node = root2.children[0];
4499
+ if (node?.type !== "element") return { element: null, keepChildren: node?.type === "text" };
4500
+ if (node.tagName === "a" && typeof node.properties.href === "string") {
4501
+ node.properties.href = rebaseHashHref(node.properties.href, clobberPrefix);
4502
+ }
4503
+ node.children = input.node?.children ?? [];
4504
+ if (input.node?.position) node.position = input.node.position;
4505
+ buildTransform({
4506
+ allowedElements: void 0,
4507
+ disallowedElements: void 0,
4508
+ allowElement: void 0,
4509
+ skipHtml: void 0,
4510
+ unwrapDisallowed: void 0,
4511
+ urlTransform
4512
+ })(node, 0, root2);
4513
+ node.children = [];
4514
+ return { element: node, keepChildren: false };
4515
+ }
4516
+
4348
4517
  // src/plugins/defs.ts
4349
4518
  function getEnginePluginInternals(plugin) {
4350
4519
  const candidate = plugin;
@@ -4561,6 +4730,8 @@ var createSmoothStreamController = (options = {}) => {
4561
4730
  let source = "";
4562
4731
  let visibleEnd = 0;
4563
4732
  let pending = [];
4733
+ let pendingHead = 0;
4734
+ const pendingCount = () => pending.length - pendingHead;
4564
4735
  let tentativeEnd = 0;
4565
4736
  let finished = false;
4566
4737
  let seam;
@@ -4605,7 +4776,7 @@ var createSmoothStreamController = (options = {}) => {
4605
4776
  cancelFrame = void 0;
4606
4777
  };
4607
4778
  const ensureScheduled = () => {
4608
- if (disposed || cancelFrame || pending.length === 0) return;
4779
+ if (disposed || cancelFrame || pendingCount() === 0) return;
4609
4780
  lastTickAt = now();
4610
4781
  credit = 0;
4611
4782
  cancelFrame = schedule(tick);
@@ -4619,31 +4790,38 @@ var createSmoothStreamController = (options = {}) => {
4619
4790
  const params = resolveParams();
4620
4791
  let rate;
4621
4792
  if (finished && drainDeadlineAt !== void 0) {
4622
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, drainDeadlineAt - t));
4793
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, drainDeadlineAt - t));
4623
4794
  } else if (gapWindow.length > 0 && lastArrivalAt !== void 0) {
4624
4795
  const gaps = gapWindow.map((s) => s.gap).sort((a, b) => a - b);
4625
4796
  const intervalQ = gaps[Math.min(gaps.length - 1, Math.floor(gaps.length * INTERVAL_QUANTILE))];
4626
4797
  const horizon = Math.max(16, Math.min(params.bufferFactor * intervalQ + HORIZON_PAD_MS, params.maxLagMs));
4627
4798
  const deadline = Math.max(lastArrivalAt + horizon, t + DEADLINE_FLOOR_MS);
4628
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, deadline - t));
4799
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, deadline - t));
4629
4800
  } else {
4630
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, params.correctionTauMs));
4801
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, params.correctionTauMs));
4631
4802
  }
4632
4803
  credit += rate * dt / 1e3;
4633
- const reveal = Math.min(Math.floor(credit), pending.length);
4804
+ const reveal = Math.min(Math.floor(credit), pendingCount());
4634
4805
  if (reveal > 0) {
4635
- visibleEnd = pending[reveal - 1];
4636
- pending = pending.slice(reveal);
4637
- credit = pending.length > 0 ? credit - reveal : 0;
4806
+ visibleEnd = pending[pendingHead + reveal - 1];
4807
+ pendingHead += reveal;
4808
+ if (pendingHead === pending.length) {
4809
+ pending = [];
4810
+ pendingHead = 0;
4811
+ } else if (pendingHead >= 1024 && pendingHead * 2 >= pending.length) {
4812
+ pending = pending.slice(pendingHead);
4813
+ pendingHead = 0;
4814
+ }
4815
+ credit = pendingCount() > 0 ? credit - reveal : 0;
4638
4816
  notify();
4639
4817
  }
4640
- if (!disposed && !cancelFrame && pending.length > 0) cancelFrame = schedule(tick);
4818
+ if (!disposed && !cancelFrame && pendingCount() > 0) cancelFrame = schedule(tick);
4641
4819
  };
4642
4820
  const resegmentTail = () => {
4643
- if (seam !== void 0 && seam < source.length && pending[pending.length - 1] === seam) {
4821
+ if (pendingCount() > 0 && seam !== void 0 && seam < source.length && pending[pending.length - 1] === seam) {
4644
4822
  pending.pop();
4645
4823
  }
4646
- const from = pending.length > 0 ? pending[pending.length - 1] : visibleEnd;
4824
+ const from = pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd;
4647
4825
  let anchor = from;
4648
4826
  if (seam !== void 0 && from <= seam) {
4649
4827
  anchor = Math.max(0, from - RESUME_LOOKBACK);
@@ -4677,6 +4855,7 @@ var createSmoothStreamController = (options = {}) => {
4677
4855
  visibleEnd = next.length;
4678
4856
  tentativeEnd = next.length;
4679
4857
  pending = [];
4858
+ pendingHead = 0;
4680
4859
  seam = next.length;
4681
4860
  credit = 0;
4682
4861
  cancelScheduled();
@@ -4708,7 +4887,7 @@ var createSmoothStreamController = (options = {}) => {
4708
4887
  if (finished) return;
4709
4888
  finished = true;
4710
4889
  lastArrivalAt = void 0;
4711
- if (tentativeEnd > (pending.length > 0 ? pending[pending.length - 1] : visibleEnd)) {
4890
+ if (tentativeEnd > (pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd)) {
4712
4891
  pending.push(tentativeEnd);
4713
4892
  }
4714
4893
  const params = resolveParams();
@@ -4734,10 +4913,11 @@ var createSmoothStreamController = (options = {}) => {
4734
4913
  snap,
4735
4914
  flush() {
4736
4915
  disposed = false;
4737
- const target = finished ? source.length : pending.length > 0 ? pending[pending.length - 1] : visibleEnd;
4916
+ const target = finished ? source.length : pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd;
4738
4917
  if (target <= visibleEnd) return;
4739
4918
  visibleEnd = target;
4740
4919
  pending = [];
4920
+ pendingHead = 0;
4741
4921
  credit = 0;
4742
4922
  cancelScheduled();
4743
4923
  notify();
@@ -5588,6 +5768,7 @@ function createRemendPreprocessor(options) {
5588
5768
  rehypeRebaseHashLinks,
5589
5769
  rehypeVerifyEngineTags,
5590
5770
  removeComments,
5771
+ resolveCrossChunkReference,
5591
5772
  sanitizeCrossChunkUrl,
5592
5773
  sanitizeSchema,
5593
5774
  shortenDocumentId,