@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.
package/dist/index.cjs CHANGED
@@ -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"]);
@@ -709,43 +756,6 @@ function maskIntraLineCodeSpans(text, carryOpen) {
709
756
  out += text.slice(cursor);
710
757
  return { masked: out, unpaired: false };
711
758
  }
712
- function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
713
- return {
714
- defListEnabled,
715
- mathFlow,
716
- referenceTaint,
717
- confirmedOffset: 0,
718
- candidates: [],
719
- defs: /* @__PURE__ */ new Map(),
720
- footnoteDefs: /* @__PURE__ */ new Map(),
721
- unresolvedRefs: [],
722
- tagBalance: /* @__PURE__ */ new Map(),
723
- openTotal: 0,
724
- p5Tok: { kind: "data" },
725
- formPointerMaybeSet: false,
726
- openStack: [],
727
- mdBlock: { kind: "none" },
728
- blankRun: 0,
729
- lastBlankStart: -1,
730
- hazardVerdict: false,
731
- prevLineBlank: true,
732
- // doc start counts as a block start
733
- prevLineWasText: false,
734
- prevLineWasValidDef: false,
735
- prevLineOpenContent: false,
736
- tableMaybeOpen: false,
737
- containerMaybeOpen: false,
738
- paragraphHasUnpairedRun: false,
739
- openBracket: null,
740
- p5SealPending: false,
741
- defBlockMaybeOpen: false,
742
- fnDefResumable: false,
743
- phasePoisonedAt: Infinity,
744
- pendingTruncatedTags: [],
745
- pendingTruncatedCloses: [],
746
- pendingTag: null
747
- };
748
- }
749
759
  function classifyBlockStart(text, indent, defListEnabled) {
750
760
  if (indent >= 4) return true;
751
761
  if (LIST_MARKER_RE.test(text) || FOOTNOTE_DEF_RE.test(text)) return true;
@@ -753,71 +763,23 @@ function classifyBlockStart(text, indent, defListEnabled) {
753
763
  if (indent === 0) return false;
754
764
  return null;
755
765
  }
756
- function computeFreezeBoundary(text, options, resume) {
757
- const mathFlow = options.mathFlow ?? true;
758
- const referenceTaint = options.referenceTaint ?? true;
759
- const prev = resume;
760
- const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
761
- let start = cp.confirmedOffset;
762
- let tailLine = null;
763
- while (start < text.length) {
764
- let end = text.length;
765
- let textEnd = text.length;
766
- {
767
- const nl = text.indexOf("\n", start);
768
- const cr = text.indexOf("\r", start);
769
- if (cr !== -1 && (nl === -1 || cr < nl)) {
770
- textEnd = cr;
771
- end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
772
- } else if (nl !== -1) {
773
- textEnd = nl;
774
- end = nl;
775
- }
776
- }
777
- const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
778
- const lineText = text.slice(start, textEnd);
779
- const ln = {
780
- start,
781
- end,
782
- text: lineText,
783
- blank: confirmed && isMdBlank(lineText),
784
- indent: computeIndent(lineText)
785
- };
786
- if (!confirmed) {
787
- tailLine = ln;
788
- break;
789
- }
790
- processConfirmedLine(cp, ln, text);
791
- cp.confirmedOffset = end + 1;
792
- start = end + 1;
793
- }
794
- const earliestUnresolved = settleRefsAndEarliestUnresolved(cp);
795
- const defListSettled = (c) => {
796
- if (!options.defListEnabled || c.blankRun >= 2) return true;
797
- if (c.defListSettled !== null) return c.defListSettled;
798
- if (tailLine) return !canBecomeDdLine(tailLine.text, false);
799
- return false;
800
- };
801
- const tailRetroactive = tailLine !== null && tailCarriesRetroactive(tailLine.text);
802
- let boundary = 0;
803
- for (let i = cp.candidates.length - 1; i >= 0 && !tailRetroactive; i--) {
804
- const c = cp.candidates[i];
805
- if (!c.htmlBalanced || c.hazard || c.seamRisk) continue;
806
- if (c.offset > cp.phasePoisonedAt) continue;
807
- if (c.offset > earliestUnresolved) continue;
808
- if (!defListSettled(c)) continue;
809
- boundary = c.offset;
810
- break;
811
- }
812
- return { boundary, checkpoint: cp };
813
- }
814
- function pendingFenceCloser(checkpoint) {
815
- const cp = checkpoint;
816
- if (cp.phasePoisonedAt !== Infinity) return "";
817
- if (cp.mdBlock.kind === "fence" && cp.mdBlock.indent === 0) return cp.mdBlock.char.repeat(cp.mdBlock.len);
818
- if (cp.mdBlock.kind === "math" && cp.mdBlock.indent === 0) return "$".repeat(cp.mdBlock.len);
819
- return "";
820
- }
766
+ var SCANNER_NAME_LISTS = [
767
+ ["type1", TYPE1_NAMES],
768
+ ["rawText", RAW_TEXT_ELEMENTS],
769
+ ["type6", TYPE6_NAMES],
770
+ ["void", VOID_TAGS],
771
+ ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
772
+ // Added with F28, and it is the list that would have made F28 visible: the
773
+ // derived census alphabet partitions names by their membership across THIS
774
+ // table, so before it existed `frame` shared one 39-name class with `div`
775
+ // and could never be sampled apart from it.
776
+ ["noElement", NO_ELEMENT_NAMES],
777
+ ["tablePart", TABLE_PART_NAMES],
778
+ ["scopeBarrier", SCOPE_BARRIER_NAMES],
779
+ ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
780
+ ];
781
+
782
+ // src/components/incrementalParse/freezeLineTransition.ts
821
783
  function floatingResidue(text, commentOpenAtStart) {
822
784
  let out = "";
823
785
  let open = commentOpenAtStart;
@@ -1461,24 +1423,66 @@ function processConfirmedLine(cp, ln, text) {
1461
1423
  }
1462
1424
  }
1463
1425
  }
1464
- var SCANNER_NAME_LISTS = [
1465
- ["type1", TYPE1_NAMES],
1466
- ["rawText", RAW_TEXT_ELEMENTS],
1467
- ["type6", TYPE6_NAMES],
1468
- ["void", VOID_TAGS],
1469
- ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
1470
- // Added with F28, and it is the list that would have made F28 visible: the
1471
- // derived census alphabet partitions names by their membership across THIS
1472
- // table, so before it existed `frame` shared one 39-name class with `div`
1473
- // and could never be sampled apart from it.
1474
- ["noElement", NO_ELEMENT_NAMES],
1475
- ["tablePart", TABLE_PART_NAMES],
1476
- ["scopeBarrier", SCOPE_BARRIER_NAMES],
1477
- ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
1478
- ];
1479
1426
 
1480
- // src/components/incrementalParse/spliceParse.ts
1481
- var import_micromark_util_normalize_identifier2 = require("micromark-util-normalize-identifier");
1427
+ // src/components/incrementalParse/computeFreezeBoundary.ts
1428
+ function computeFreezeBoundary(text, options, resume) {
1429
+ const mathFlow = options.mathFlow ?? true;
1430
+ const referenceTaint = options.referenceTaint ?? true;
1431
+ const prev = resume;
1432
+ const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
1433
+ let start = cp.confirmedOffset;
1434
+ let tailLine = null;
1435
+ while (start < text.length) {
1436
+ let end = text.length;
1437
+ let textEnd = text.length;
1438
+ {
1439
+ const nl = text.indexOf("\n", start);
1440
+ const cr = text.indexOf("\r", start);
1441
+ if (cr !== -1 && (nl === -1 || cr < nl)) {
1442
+ textEnd = cr;
1443
+ end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
1444
+ } else if (nl !== -1) {
1445
+ textEnd = nl;
1446
+ end = nl;
1447
+ }
1448
+ }
1449
+ const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
1450
+ const lineText = text.slice(start, textEnd);
1451
+ const ln = {
1452
+ start,
1453
+ end,
1454
+ text: lineText,
1455
+ blank: confirmed && isMdBlank(lineText),
1456
+ indent: computeIndent(lineText)
1457
+ };
1458
+ if (!confirmed) {
1459
+ tailLine = ln;
1460
+ break;
1461
+ }
1462
+ processConfirmedLine(cp, ln, text);
1463
+ cp.confirmedOffset = end + 1;
1464
+ start = end + 1;
1465
+ }
1466
+ const earliestUnresolved = settleRefsAndEarliestUnresolved(cp);
1467
+ const defListSettled = (c) => {
1468
+ if (!options.defListEnabled || c.blankRun >= 2) return true;
1469
+ if (c.defListSettled !== null) return c.defListSettled;
1470
+ if (tailLine) return !canBecomeDdLine(tailLine.text, false);
1471
+ return false;
1472
+ };
1473
+ const tailRetroactive = tailLine !== null && tailCarriesRetroactive(tailLine.text);
1474
+ let boundary = 0;
1475
+ for (let i = cp.candidates.length - 1; i >= 0 && !tailRetroactive; i--) {
1476
+ const c = cp.candidates[i];
1477
+ if (!c.htmlBalanced || c.hazard || c.seamRisk) continue;
1478
+ if (c.offset > cp.phasePoisonedAt) continue;
1479
+ if (c.offset > earliestUnresolved) continue;
1480
+ if (!defListSettled(c)) continue;
1481
+ boundary = c.offset;
1482
+ break;
1483
+ }
1484
+ return { boundary, checkpoint: cp };
1485
+ }
1482
1486
 
1483
1487
  // src/components/hastPredicates.ts
1484
1488
  function isFootnoteSection(node) {
@@ -1526,121 +1530,7 @@ function attributeHastChildren(mdast, hast, stopAt = Infinity) {
1526
1530
  }
1527
1531
  }
1528
1532
 
1529
- // src/components/incrementalParse/spliceParse.ts
1530
- function collectPrefixInjection(mdast, content, boundary, resume) {
1531
- const resumeValid = resume != null && resume.boundary <= boundary;
1532
- if (resumeValid && resume.uninjectable) return { events: resume.events, uninjectable: true };
1533
- const resumeAt = resumeValid ? resume.boundary : 0;
1534
- const events = resumeAt > 0 ? cloneEventsForAppend(resume.events) : [];
1535
- let uninjectable = false;
1536
- let cacheable = true;
1537
- const pushRef = (token) => {
1538
- const last = events[events.length - 1];
1539
- if (last && last.kind === "refs") last.tokens.push(token);
1540
- else events.push({ kind: "refs", tokens: [token] });
1541
- };
1542
- const visit7 = (node, nested) => {
1543
- const type = node.type;
1544
- if (type === "definition") {
1545
- const start = node.position?.start?.offset;
1546
- const end = node.position?.end?.offset;
1547
- if (start !== void 0 && end !== void 0 && start < boundary) {
1548
- const slice = content.slice(start, end);
1549
- if (nested && slice.includes("\n")) uninjectable = true;
1550
- else events.push({ kind: "def", source: slice });
1551
- }
1552
- return;
1553
- }
1554
- if (type === "footnoteDefinition") {
1555
- const start = node.position?.start;
1556
- const end = node.position?.end?.offset;
1557
- if (start?.offset === void 0 || end === void 0 || start.offset >= boundary) return;
1558
- if (nested) {
1559
- uninjectable = true;
1560
- return;
1561
- }
1562
- const lineStart = start.offset - (start.column - 1);
1563
- events.push({
1564
- kind: "footnoteDef",
1565
- source: content.slice(lineStart, end),
1566
- origStart: lineStart,
1567
- origLine: start.line
1568
- });
1569
- return;
1570
- }
1571
- if (type === "footnoteReference") {
1572
- const start = node.position?.start?.offset;
1573
- const end = node.position?.end?.offset;
1574
- if (start === void 0 || end === void 0) {
1575
- uninjectable = true;
1576
- return;
1577
- }
1578
- pushRef(content.slice(start, end));
1579
- return;
1580
- }
1581
- const children = node.children;
1582
- if (children) {
1583
- for (const child of children) visit7(child, true);
1584
- }
1585
- };
1586
- let lastStart = -1;
1587
- for (const child of mdast.children) {
1588
- const start = child.position?.start?.offset;
1589
- if (start !== void 0) {
1590
- if (start < lastStart) return { events: [], uninjectable: true, cacheable: false };
1591
- lastStart = start;
1592
- }
1593
- if (start === void 0) {
1594
- if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
1595
- cacheable = false;
1596
- visit7(child, false);
1597
- continue;
1598
- }
1599
- if (start >= boundary || start < resumeAt) continue;
1600
- visit7(child, false);
1601
- }
1602
- return { events, uninjectable, cacheable };
1603
- }
1604
- function cloneEventsForAppend(events) {
1605
- const out = events.slice();
1606
- const last = out[out.length - 1];
1607
- if (last && last.kind === "refs") out[out.length - 1] = { kind: "refs", tokens: last.tokens.slice() };
1608
- return out;
1609
- }
1610
- var TERMINATOR_LABEL = "__aimd_injection_terminator__";
1611
- var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
1612
- function tailMentionsTerminator(tailSource) {
1613
- return (0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(tailSource).replace(/\[ /g, "[").includes(`[${(0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(TERMINATOR_LABEL)}`);
1614
- }
1615
- function buildInjectionPrefix(events) {
1616
- if (events.length === 0) return { text: "", segments: [] };
1617
- let text = "";
1618
- let line = 1;
1619
- const segments = [];
1620
- for (const event of events) {
1621
- if (text.length > 0) {
1622
- text += "\n\n";
1623
- line += 2;
1624
- }
1625
- const injStart = text.length;
1626
- const source = event.kind === "refs" ? event.tokens.join(" ") : event.source;
1627
- if (event.kind === "footnoteDef") {
1628
- segments.push({
1629
- injStart,
1630
- injEnd: injStart + source.length,
1631
- offsetDelta: event.origStart - injStart,
1632
- lineDelta: event.origLine - line
1633
- });
1634
- }
1635
- text += source;
1636
- line += countNewlines(source);
1637
- }
1638
- return { text: `${text}
1639
-
1640
- ${INJECTION_TERMINATOR}
1641
-
1642
- `, segments };
1643
- }
1533
+ // src/components/incrementalParse/spliceCoordinates.ts
1644
1534
  function rebaseTree(node, offsetDelta, lineDelta) {
1645
1535
  rebaseTreeDual(node, EMPTY_SEGMENTS, offsetDelta, lineDelta);
1646
1536
  }
@@ -1669,6 +1559,23 @@ function rebaseDualWalk(node, segments, maxEnd, offsetDelta, lineDelta) {
1669
1559
  for (const child of children) rebaseDualWalk(child, segments, maxEnd, offsetDelta, lineDelta);
1670
1560
  }
1671
1561
  }
1562
+ function rebasePoint(point, offsetDelta, lineDelta) {
1563
+ return {
1564
+ line: point.line + lineDelta,
1565
+ column: point.column,
1566
+ offset: point.offset !== void 0 ? point.offset + offsetDelta : void 0
1567
+ };
1568
+ }
1569
+ function countNewlines(text, end = text.length) {
1570
+ let count = 0;
1571
+ for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
1572
+ for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
1573
+ if (text.charCodeAt(i + 1) !== 10) count += 1;
1574
+ }
1575
+ return count;
1576
+ }
1577
+
1578
+ // src/components/incrementalParse/spliceHtmlGuards.ts
1672
1579
  var TABLE_PART_TAG_RE = /<(?:td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/i;
1673
1580
  var TABLE_TOKEN_RE = /<(\/?)(table|td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/gi;
1674
1581
  function hasStrayTablePart(values) {
@@ -1750,127 +1657,26 @@ function rawTextRegionCrossesOut(values) {
1750
1657
  }
1751
1658
  return false;
1752
1659
  }
1753
- function spliceTrees(input) {
1754
- const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
1755
- const injectedLen = injectionPrefix.length;
1756
- const injectedLines = countNewlines(injectionPrefix);
1757
- const prefixLines = countNewlines(content, boundary);
1758
- const offsetDelta = boundary - injectedLen;
1759
- const lineDelta = prefixLines - injectedLines;
1760
- const prefixMdast = [];
1761
- for (const child of prevMdast.children) {
1762
- const start = child.position?.start?.offset;
1763
- if (start !== void 0 && start < boundary) prefixMdast.push(child);
1764
- }
1765
- const attrs = attributeHastChildren(prevMdast, prevHast, boundary);
1766
- const cutRegion = [];
1767
- for (let i = 0; i < prevHast.children.length; i++) {
1768
- if (attrs[i] < boundary) {
1769
- const node2 = prevHast.children[i];
1770
- const end = node2.position?.end?.offset;
1771
- if (end !== void 0 && end > boundary) return null;
1772
- if (isTrailingLiteralText(node2)) {
1773
- const prev = i > 0 ? prevHast.children[i - 1] : void 0;
1774
- if (!prev || prev.type !== "element" || prev.position === void 0) return null;
1775
- if (!ownsTrailingLiteral(prev, node2, prefixMdast))
1776
- return null;
1777
- }
1778
- cutRegion.push(node2);
1779
- continue;
1780
- }
1781
- const node = prevHast.children[i];
1782
- 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
1783
- // raw literal can only trail an `html` mdast node. A position-less
1784
- // text after a `<p>` is the NEXT block's remnant — a stray end tag
1785
- // (`</t>\na`) parse5 dropped, whose text merged with the wrap
1786
- // separator — owned by the tail, which re-parses it; freezing it
1787
- // here duplicated it (v2.4.0 review P3). Falls through to the
1788
- // remnant look-ahead below, which bails to a full parse. And the
1789
- // literal must really be THAT block's trailing text: the block's raw
1790
- // source ends with it. A dropped-tag block right after a frozen html
1791
- // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
1792
- // position, and freezing it duplicated it (release soak of the fix).
1793
- ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
1794
- cutRegion.push(node);
1795
- break;
1796
- }
1797
- for (let j = i; j < prevHast.children.length; j++) {
1798
- const look = prevHast.children[j];
1799
- if (look.type === "element" || look.position !== void 0) break;
1800
- if (isTrailingLiteralText(look)) return null;
1801
- }
1802
- break;
1803
- }
1804
- const tailMdastChildren = tailMdast.children.filter((child) => {
1805
- const start = child.position?.start?.offset;
1806
- return !(start !== void 0 && start < injectedLen);
1807
- });
1808
- const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
1809
- const prefixHtmlValues = prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []);
1810
- if (hasStrayTablePart(prefixHtmlValues)) return null;
1811
- if (rawTextRegionCrossesOut(prefixHtmlValues)) return null;
1812
- const leadingHtml = [];
1813
- for (const child of tailMdastChildren) {
1814
- if (isWrapInvisible(child)) continue;
1815
- if (child.type !== "html") break;
1816
- if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
1817
- leadingHtml.push(child.value);
1660
+ function isSanitizeStrippedConstruct(value) {
1661
+ const v = value.trim();
1662
+ 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(">");
1663
+ }
1664
+ function isExactSanitizeStrippedConstruct(value) {
1665
+ if (value.startsWith("<!--")) {
1666
+ if (value.length < 7 || !value.endsWith("-->")) return false;
1667
+ const close = value.indexOf("-->", 4);
1668
+ const bangClose = value.indexOf("--!>", 4);
1669
+ if (close !== value.length - 3) return false;
1670
+ return bangClose === -1 || bangClose > close;
1818
1671
  }
1819
- if (headRoutedCaptureUnclosed(leadingHtml)) return null;
1820
- const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
1821
- if (aligned === null) return null;
1822
- const hastChildren = aligned.children;
1823
- const interiorFinalLiteral = aligned.interiorFinalLiteral;
1824
- const stripResult = stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible);
1825
- if (stripResult === null) return null;
1826
- const strippedTailHast = stripResult.rest;
1827
- for (const child of tailMdastChildren) rebaseTree(child, offsetDelta, lineDelta);
1828
- for (const child of strippedTailHast) rebaseTreeDual(child, injectedSegments, offsetDelta, lineDelta);
1829
- const mdastChildren = prefixMdast.concat(tailMdastChildren);
1830
- const seamMergeVerdict = stripResult.remnantMerged ?? tailLeadingTextIsHoist(tailMdastChildren, strippedTailHast);
1831
- if (seamMergeVerdict === null) return null;
1832
- const seamMergeAllowed = seamMergeVerdict;
1833
- let firstTailChild = true;
1834
- for (const child of strippedTailHast) {
1835
- const tailEnd = hastChildren[hastChildren.length - 1];
1836
- if (firstTailChild && child.type === "text" && child.position !== void 0) {
1837
- return null;
1838
- }
1839
- if (firstTailChild && tailEnd && tailEnd.type === "text" && child.type === "text" && child.position === void 0) {
1840
- if (interiorFinalLiteral && tailEnd.value.trim() !== "") {
1841
- if (seamMergeAllowed) return null;
1842
- firstTailChild = false;
1843
- hastChildren.push(child);
1844
- continue;
1845
- }
1846
- const literalSeam = tailEnd.value.trim() !== "" && !tailWrapVisible;
1847
- if (seamMergeAllowed || literalSeam) {
1848
- hastChildren[hastChildren.length - 1] = { type: "text", value: tailEnd.value + child.value };
1849
- firstTailChild = false;
1850
- continue;
1851
- }
1852
- }
1853
- firstTailChild = false;
1854
- hastChildren.push(child);
1672
+ if (/^<!doctype/i.test(value)) return false;
1673
+ if (value.startsWith("<?") || /^<!(?:[A-Za-z]|\[CDATA\[)/.test(value)) {
1674
+ return value.indexOf(">") === value.length - 1;
1855
1675
  }
1856
- if (!hastChildren.some((child) => child.position !== void 0)) return null;
1857
- const docStart = { line: 1, column: 1, offset: 0 };
1858
- const mdastRootPosition = tailMdast.position ? { start: docStart, end: rebasePoint(tailMdast.position.end, offsetDelta, lineDelta) } : prevMdast.position;
1859
- const hastRootPosition = mdastRootPosition;
1860
- const mdast = { type: "root", children: mdastChildren };
1861
- if (mdastRootPosition) mdast.position = mdastRootPosition;
1862
- const hast = { type: "root", children: hastChildren };
1863
- if (hastRootPosition) hast.position = hastRootPosition;
1864
- if (prevHast.data !== void 0) hast.data = prevHast.data;
1865
- return { mdast, hast };
1866
- }
1867
- function rebasePoint(point, offsetDelta, lineDelta) {
1868
- return {
1869
- line: point.line + lineDelta,
1870
- column: point.column,
1871
- offset: point.offset !== void 0 ? point.offset + offsetDelta : void 0
1872
- };
1676
+ return false;
1873
1677
  }
1678
+
1679
+ // src/components/incrementalParse/prefixAlignment.ts
1874
1680
  function isWrapInvisible(node) {
1875
1681
  return node.type === "definition" || node.type === "footnoteDefinition";
1876
1682
  }
@@ -2012,96 +1818,303 @@ function stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible) {
2012
1818
  idx += 1;
2013
1819
  continue;
2014
1820
  }
2015
- const start = node.position?.start?.offset;
2016
- if (start === void 0 || start >= injectedLen) return null;
2017
- consumed += 1;
2018
- idx += 1;
1821
+ const start = node.position?.start?.offset;
1822
+ if (start === void 0 || start >= injectedLen) return null;
1823
+ consumed += 1;
1824
+ idx += 1;
1825
+ }
1826
+ if (consumed < injectedVisibleCount) return null;
1827
+ let remnant = "";
1828
+ let remnantMerged = null;
1829
+ if (tailWrapVisible) {
1830
+ const gap = children[idx];
1831
+ if (!gap || !isSeparatorText(gap)) return null;
1832
+ remnant = gap.value.slice(1);
1833
+ remnantMerged = remnant !== "";
1834
+ idx += 1;
1835
+ }
1836
+ const rest = children.slice(idx);
1837
+ if (remnant !== "") rest.unshift({ type: "text", value: remnant });
1838
+ return { rest, remnantMerged };
1839
+ }
1840
+ function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
1841
+ const firstText = tailHastChildren[0];
1842
+ const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
1843
+ if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
1844
+ if (!firstText) return false;
1845
+ if (!isSeparatorText(firstText)) {
1846
+ if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
1847
+ if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
1848
+ if (isSanitizeStrippedConstruct(firstVisible.value)) return false;
1849
+ return null;
1850
+ }
1851
+ return false;
1852
+ }
1853
+ if (!firstVisible) return false;
1854
+ const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
1855
+ if (firstContent) {
1856
+ const start = firstContent.position?.start?.offset;
1857
+ const vStart = firstVisible.position?.start?.offset;
1858
+ const vEnd = firstVisible.position?.end?.offset;
1859
+ if (start !== void 0 && vStart !== void 0 && vEnd !== void 0) {
1860
+ if (start >= vStart && start < vEnd) return true;
1861
+ }
1862
+ }
1863
+ if (firstVisible.type === "math") return false;
1864
+ if (firstVisible.type === "html" && isSanitizeStrippedConstruct(firstVisible.value)) return false;
1865
+ return null;
1866
+ }
1867
+ function isSeparatorText(node) {
1868
+ return node.type === "text" && node.position === void 0 && node.value.trim() === "";
1869
+ }
1870
+ function ownsTrailingLiteral(el, literal, prefixMdast) {
1871
+ const start = el.position?.start?.offset;
1872
+ if (start === void 0) return false;
1873
+ const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
1874
+ if (!owner || owner.type !== "html") return false;
1875
+ const text = literal.value.trim();
1876
+ return text !== "" && owner.value.trimEnd().endsWith(text);
1877
+ }
1878
+ function isTrailingLiteralText(node) {
1879
+ return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
1880
+ }
1881
+ function countTrailingNewlines(value) {
1882
+ let n = 0;
1883
+ for (let i = value.length - 1; i >= 0 && value[i] === "\n"; i--) n += 1;
1884
+ return n;
1885
+ }
1886
+
1887
+ // src/components/incrementalParse/prefixInjection.ts
1888
+ var import_micromark_util_normalize_identifier2 = require("micromark-util-normalize-identifier");
1889
+ function collectPrefixInjection(mdast, content, boundary, resume) {
1890
+ const resumeValid = resume != null && resume.boundary <= boundary;
1891
+ if (resumeValid && resume.uninjectable) return { events: resume.events, uninjectable: true };
1892
+ const resumeAt = resumeValid ? resume.boundary : 0;
1893
+ const events = resumeAt > 0 ? cloneEventsForAppend(resume.events) : [];
1894
+ let uninjectable = false;
1895
+ let cacheable = true;
1896
+ const pushRef = (token) => {
1897
+ const last = events[events.length - 1];
1898
+ if (last && last.kind === "refs") last.tokens.push(token);
1899
+ else events.push({ kind: "refs", tokens: [token] });
1900
+ };
1901
+ const visit7 = (node, nested) => {
1902
+ const type = node.type;
1903
+ if (type === "definition") {
1904
+ const start = node.position?.start?.offset;
1905
+ const end = node.position?.end?.offset;
1906
+ if (start !== void 0 && end !== void 0 && start < boundary) {
1907
+ const slice = content.slice(start, end);
1908
+ if (nested && slice.includes("\n")) uninjectable = true;
1909
+ else events.push({ kind: "def", source: slice });
1910
+ }
1911
+ return;
1912
+ }
1913
+ if (type === "footnoteDefinition") {
1914
+ const start = node.position?.start;
1915
+ const end = node.position?.end?.offset;
1916
+ if (start?.offset === void 0 || end === void 0 || start.offset >= boundary) return;
1917
+ if (nested) {
1918
+ uninjectable = true;
1919
+ return;
1920
+ }
1921
+ const lineStart = start.offset - (start.column - 1);
1922
+ events.push({
1923
+ kind: "footnoteDef",
1924
+ source: content.slice(lineStart, end),
1925
+ origStart: lineStart,
1926
+ origLine: start.line
1927
+ });
1928
+ return;
1929
+ }
1930
+ if (type === "footnoteReference") {
1931
+ const start = node.position?.start?.offset;
1932
+ const end = node.position?.end?.offset;
1933
+ if (start === void 0 || end === void 0) {
1934
+ uninjectable = true;
1935
+ return;
1936
+ }
1937
+ pushRef(content.slice(start, end));
1938
+ return;
1939
+ }
1940
+ const children = node.children;
1941
+ if (children) {
1942
+ for (const child of children) visit7(child, true);
1943
+ }
1944
+ };
1945
+ let lastStart = -1;
1946
+ for (const child of mdast.children) {
1947
+ const start = child.position?.start?.offset;
1948
+ if (start !== void 0) {
1949
+ if (start < lastStart) return { events: [], uninjectable: true, cacheable: false };
1950
+ lastStart = start;
1951
+ }
1952
+ if (start === void 0) {
1953
+ if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
1954
+ cacheable = false;
1955
+ visit7(child, false);
1956
+ continue;
1957
+ }
1958
+ if (start >= boundary || start < resumeAt) continue;
1959
+ visit7(child, false);
1960
+ }
1961
+ return { events, uninjectable, cacheable };
1962
+ }
1963
+ function cloneEventsForAppend(events) {
1964
+ const out = events.slice();
1965
+ const last = out[out.length - 1];
1966
+ if (last && last.kind === "refs") out[out.length - 1] = { kind: "refs", tokens: last.tokens.slice() };
1967
+ return out;
1968
+ }
1969
+ var TERMINATOR_LABEL = "__aimd_injection_terminator__";
1970
+ var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
1971
+ function tailMentionsTerminator(tailSource) {
1972
+ return (0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(tailSource).replace(/\[ /g, "[").includes(`[${(0, import_micromark_util_normalize_identifier2.normalizeIdentifier)(TERMINATOR_LABEL)}`);
1973
+ }
1974
+ function buildInjectionPrefix(events) {
1975
+ if (events.length === 0) return { text: "", segments: [] };
1976
+ let text = "";
1977
+ let line = 1;
1978
+ const segments = [];
1979
+ for (const event of events) {
1980
+ if (text.length > 0) {
1981
+ text += "\n\n";
1982
+ line += 2;
1983
+ }
1984
+ const injStart = text.length;
1985
+ const source = event.kind === "refs" ? event.tokens.join(" ") : event.source;
1986
+ if (event.kind === "footnoteDef") {
1987
+ segments.push({
1988
+ injStart,
1989
+ injEnd: injStart + source.length,
1990
+ offsetDelta: event.origStart - injStart,
1991
+ lineDelta: event.origLine - line
1992
+ });
1993
+ }
1994
+ text += source;
1995
+ line += countNewlines(source);
1996
+ }
1997
+ return { text: `${text}
1998
+
1999
+ ${INJECTION_TERMINATOR}
2000
+
2001
+ `, segments };
2002
+ }
2003
+
2004
+ // src/components/incrementalParse/spliceParse.ts
2005
+ function spliceTrees(input) {
2006
+ const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
2007
+ const injectedLen = injectionPrefix.length;
2008
+ const injectedLines = countNewlines(injectionPrefix);
2009
+ const prefixLines = countNewlines(content, boundary);
2010
+ const offsetDelta = boundary - injectedLen;
2011
+ const lineDelta = prefixLines - injectedLines;
2012
+ const prefixMdast = [];
2013
+ for (const child of prevMdast.children) {
2014
+ const start = child.position?.start?.offset;
2015
+ if (start !== void 0 && start < boundary) prefixMdast.push(child);
2016
+ }
2017
+ const attrs = attributeHastChildren(prevMdast, prevHast, boundary);
2018
+ const cutRegion = [];
2019
+ for (let i = 0; i < prevHast.children.length; i++) {
2020
+ if (attrs[i] < boundary) {
2021
+ const node2 = prevHast.children[i];
2022
+ const end = node2.position?.end?.offset;
2023
+ if (end !== void 0 && end > boundary) return null;
2024
+ if (isTrailingLiteralText(node2)) {
2025
+ const prev = i > 0 ? prevHast.children[i - 1] : void 0;
2026
+ if (!prev || prev.type !== "element" || prev.position === void 0) return null;
2027
+ if (!ownsTrailingLiteral(prev, node2, prefixMdast))
2028
+ return null;
2029
+ }
2030
+ cutRegion.push(node2);
2031
+ continue;
2032
+ }
2033
+ const node = prevHast.children[i];
2034
+ 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
2035
+ // raw literal can only trail an `html` mdast node. A position-less
2036
+ // text after a `<p>` is the NEXT block's remnant — a stray end tag
2037
+ // (`</t>\na`) parse5 dropped, whose text merged with the wrap
2038
+ // separator — owned by the tail, which re-parses it; freezing it
2039
+ // here duplicated it (v2.4.0 review P3). Falls through to the
2040
+ // remnant look-ahead below, which bails to a full parse. And the
2041
+ // literal must really be THAT block's trailing text: the block's raw
2042
+ // source ends with it. A dropped-tag block right after a frozen html
2043
+ // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
2044
+ // position, and freezing it duplicated it (release soak of the fix).
2045
+ ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
2046
+ cutRegion.push(node);
2047
+ break;
2048
+ }
2049
+ for (let j = i; j < prevHast.children.length; j++) {
2050
+ const look = prevHast.children[j];
2051
+ if (look.type === "element" || look.position !== void 0) break;
2052
+ if (isTrailingLiteralText(look)) return null;
2053
+ }
2054
+ break;
2019
2055
  }
2020
- if (consumed < injectedVisibleCount) return null;
2021
- let remnant = "";
2022
- let remnantMerged = null;
2023
- if (tailWrapVisible) {
2024
- const gap = children[idx];
2025
- if (!gap || !isSeparatorText(gap)) return null;
2026
- remnant = gap.value.slice(1);
2027
- remnantMerged = remnant !== "";
2028
- idx += 1;
2056
+ const tailMdastChildren = tailMdast.children.filter((child) => {
2057
+ const start = child.position?.start?.offset;
2058
+ return !(start !== void 0 && start < injectedLen);
2059
+ });
2060
+ const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
2061
+ const prefixHtmlValues = prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []);
2062
+ if (hasStrayTablePart(prefixHtmlValues)) return null;
2063
+ if (rawTextRegionCrossesOut(prefixHtmlValues)) return null;
2064
+ const leadingHtml = [];
2065
+ for (const child of tailMdastChildren) {
2066
+ if (isWrapInvisible(child)) continue;
2067
+ if (child.type !== "html") break;
2068
+ if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
2069
+ leadingHtml.push(child.value);
2029
2070
  }
2030
- const rest = children.slice(idx);
2031
- if (remnant !== "") rest.unshift({ type: "text", value: remnant });
2032
- return { rest, remnantMerged };
2033
- }
2034
- function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
2035
- const firstText = tailHastChildren[0];
2036
- const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
2037
- if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
2038
- if (!firstText) return false;
2039
- if (!isSeparatorText(firstText)) {
2040
- if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
2041
- if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
2042
- if (isSanitizeStrippedConstruct(firstVisible.value)) return false;
2071
+ if (headRoutedCaptureUnclosed(leadingHtml)) return null;
2072
+ const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
2073
+ if (aligned === null) return null;
2074
+ const hastChildren = aligned.children;
2075
+ const interiorFinalLiteral = aligned.interiorFinalLiteral;
2076
+ const stripResult = stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible);
2077
+ if (stripResult === null) return null;
2078
+ const strippedTailHast = stripResult.rest;
2079
+ for (const child of tailMdastChildren) rebaseTree(child, offsetDelta, lineDelta);
2080
+ for (const child of strippedTailHast) rebaseTreeDual(child, injectedSegments, offsetDelta, lineDelta);
2081
+ const mdastChildren = prefixMdast.concat(tailMdastChildren);
2082
+ const seamMergeVerdict = stripResult.remnantMerged ?? tailLeadingTextIsHoist(tailMdastChildren, strippedTailHast);
2083
+ if (seamMergeVerdict === null) return null;
2084
+ const seamMergeAllowed = seamMergeVerdict;
2085
+ let firstTailChild = true;
2086
+ for (const child of strippedTailHast) {
2087
+ const tailEnd = hastChildren[hastChildren.length - 1];
2088
+ if (firstTailChild && child.type === "text" && child.position !== void 0) {
2043
2089
  return null;
2044
2090
  }
2045
- return false;
2046
- }
2047
- if (!firstVisible) return false;
2048
- const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
2049
- if (firstContent) {
2050
- const start = firstContent.position?.start?.offset;
2051
- const vStart = firstVisible.position?.start?.offset;
2052
- const vEnd = firstVisible.position?.end?.offset;
2053
- if (start !== void 0 && vStart !== void 0 && vEnd !== void 0) {
2054
- if (start >= vStart && start < vEnd) return true;
2091
+ if (firstTailChild && tailEnd && tailEnd.type === "text" && child.type === "text" && child.position === void 0) {
2092
+ if (interiorFinalLiteral && tailEnd.value.trim() !== "") {
2093
+ if (seamMergeAllowed) return null;
2094
+ firstTailChild = false;
2095
+ hastChildren.push(child);
2096
+ continue;
2097
+ }
2098
+ const literalSeam = tailEnd.value.trim() !== "" && !tailWrapVisible;
2099
+ if (seamMergeAllowed || literalSeam) {
2100
+ hastChildren[hastChildren.length - 1] = { type: "text", value: tailEnd.value + child.value };
2101
+ firstTailChild = false;
2102
+ continue;
2103
+ }
2055
2104
  }
2105
+ firstTailChild = false;
2106
+ hastChildren.push(child);
2056
2107
  }
2057
- if (firstVisible.type === "math") return false;
2058
- if (firstVisible.type === "html" && isSanitizeStrippedConstruct(firstVisible.value)) return false;
2059
- return null;
2060
- }
2061
- function isSanitizeStrippedConstruct(value) {
2062
- const v = value.trim();
2063
- 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(">");
2064
- }
2065
- function isExactSanitizeStrippedConstruct(value) {
2066
- if (value.startsWith("<!--")) {
2067
- if (value.length < 7 || !value.endsWith("-->")) return false;
2068
- const close = value.indexOf("-->", 4);
2069
- const bangClose = value.indexOf("--!>", 4);
2070
- if (close !== value.length - 3) return false;
2071
- return bangClose === -1 || bangClose > close;
2072
- }
2073
- if (/^<!doctype/i.test(value)) return false;
2074
- if (value.startsWith("<?") || /^<!(?:[A-Za-z]|\[CDATA\[)/.test(value)) {
2075
- return value.indexOf(">") === value.length - 1;
2076
- }
2077
- return false;
2078
- }
2079
- function isSeparatorText(node) {
2080
- return node.type === "text" && node.position === void 0 && node.value.trim() === "";
2081
- }
2082
- function ownsTrailingLiteral(el, literal, prefixMdast) {
2083
- const start = el.position?.start?.offset;
2084
- if (start === void 0) return false;
2085
- const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
2086
- if (!owner || owner.type !== "html") return false;
2087
- const text = literal.value.trim();
2088
- return text !== "" && owner.value.trimEnd().endsWith(text);
2089
- }
2090
- function isTrailingLiteralText(node) {
2091
- return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
2092
- }
2093
- function countTrailingNewlines(value) {
2094
- let n = 0;
2095
- for (let i = value.length - 1; i >= 0 && value[i] === "\n"; i--) n += 1;
2096
- return n;
2097
- }
2098
- function countNewlines(text, end = text.length) {
2099
- let count = 0;
2100
- for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
2101
- for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
2102
- if (text.charCodeAt(i + 1) !== 10) count += 1;
2103
- }
2104
- return count;
2108
+ if (!hastChildren.some((child) => child.position !== void 0)) return null;
2109
+ const docStart = { line: 1, column: 1, offset: 0 };
2110
+ const mdastRootPosition = tailMdast.position ? { start: docStart, end: rebasePoint(tailMdast.position.end, offsetDelta, lineDelta) } : prevMdast.position;
2111
+ const hastRootPosition = mdastRootPosition;
2112
+ const mdast = { type: "root", children: mdastChildren };
2113
+ if (mdastRootPosition) mdast.position = mdastRootPosition;
2114
+ const hast = { type: "root", children: hastChildren };
2115
+ if (hastRootPosition) hast.position = hastRootPosition;
2116
+ if (prevHast.data !== void 0) hast.data = prevHast.data;
2117
+ return { mdast, hast };
2105
2118
  }
2106
2119
 
2107
2120
  // src/components/incrementalParse/advanceIncrementalParse.ts
@@ -2223,6 +2236,38 @@ function normalizeForMatch(s) {
2223
2236
  return (0, import_micromark_util_normalize_identifier3.normalizeIdentifier)(s);
2224
2237
  }
2225
2238
 
2239
+ // src/components/blankLineScanner.ts
2240
+ function createBlankLineScanner() {
2241
+ let end = 0;
2242
+ let newline = false;
2243
+ let cr = false;
2244
+ return (source, from = 0) => {
2245
+ if (from === 0) {
2246
+ end = 0;
2247
+ newline = false;
2248
+ cr = false;
2249
+ }
2250
+ for (let i = from; i < source.length; i++) {
2251
+ const c = source[i];
2252
+ if (c === "\n") {
2253
+ if (newline) {
2254
+ end = i + 1;
2255
+ newline = false;
2256
+ } else newline = true;
2257
+ cr = false;
2258
+ } else if (newline) {
2259
+ if ((c === " " || c === " ") && !cr) continue;
2260
+ if (c === "\r" && !cr) cr = true;
2261
+ else {
2262
+ newline = false;
2263
+ cr = false;
2264
+ }
2265
+ }
2266
+ }
2267
+ return end;
2268
+ };
2269
+ }
2270
+
2226
2271
  // src/components/collectDefLabels.ts
2227
2272
  var SCANNER_BOUNDARY_PROFILE = { defListEnabled: false, mathFlow: false, referenceTaint: false };
2228
2273
  function buildProcessor() {
@@ -2254,19 +2299,12 @@ var setsEqual = (a, b) => {
2254
2299
  for (const v of a) if (!b.has(v)) return false;
2255
2300
  return true;
2256
2301
  };
2257
- var BLANK_LINE_RE = /\r?\n[ \t]*\r?\n/g;
2258
- function lastRegionStart(source) {
2259
- BLANK_LINE_RE.lastIndex = 0;
2260
- let start = 0;
2261
- for (let m = BLANK_LINE_RE.exec(source); m !== null; m = BLANK_LINE_RE.exec(source)) {
2262
- start = m.index + m[0].length;
2263
- }
2264
- return start;
2265
- }
2266
2302
  var DEF_LINE_START_RE = /^[ \t>*+\d.)-]*\[(?:[^\]\\]|\\[\s\S])*\]:/m;
2267
2303
  function createDefLabelScanner(parse = collectDefLabels) {
2268
2304
  let prevSource = null;
2269
2305
  let prevLabels = null;
2306
+ const scanBlankLines = createBlankLineScanner();
2307
+ let regionStart = 0;
2270
2308
  let frozenEnd = 0;
2271
2309
  let frozenFootnotes = /* @__PURE__ */ new Set();
2272
2310
  let frozenLinks = /* @__PURE__ */ new Set();
@@ -2279,13 +2317,16 @@ function createDefLabelScanner(parse = collectDefLabels) {
2279
2317
  };
2280
2318
  return {
2281
2319
  scan(source) {
2320
+ if (source === prevSource && prevLabels !== null) return prevLabels;
2321
+ const previousRegionStart = regionStart;
2322
+ const appended = prevSource !== null && source.startsWith(prevSource);
2323
+ regionStart = scanBlankLines(source, appended ? prevSource.length : 0);
2282
2324
  let isAppend = false;
2283
2325
  if (prevSource !== null && prevLabels !== null) {
2284
2326
  if (source === prevSource) return prevLabels;
2285
2327
  if (source.startsWith(prevSource)) {
2286
2328
  isAppend = true;
2287
- const regionStart = lastRegionStart(prevSource);
2288
- const region = prevSource.slice(regionStart) + source.slice(prevSource.length);
2329
+ const region = source.slice(previousRegionStart);
2289
2330
  if (!DEF_LINE_START_RE.test(region)) {
2290
2331
  prevSource = source;
2291
2332
  return prevLabels;
@@ -2518,8 +2559,115 @@ function* extractContributions(mdast, options = {}) {
2518
2559
  for (const c of out) yield c;
2519
2560
  }
2520
2561
 
2562
+ // src/components/registryIndex.ts
2563
+ function buildRegistryIndex(registry) {
2564
+ const index = {
2565
+ footnotes: /* @__PURE__ */ new Map(),
2566
+ links: /* @__PURE__ */ new Map(),
2567
+ numbers: /* @__PURE__ */ new Map(),
2568
+ counts: /* @__PURE__ */ new Map(),
2569
+ occurrences: /* @__PURE__ */ new Map(),
2570
+ labelOccurrences: /* @__PURE__ */ new Map()
2571
+ };
2572
+ for (const sym of registry.chunkOrder) {
2573
+ const data = registry.chunkData.get(sym);
2574
+ if (!data) continue;
2575
+ for (const label of data.defs.keys()) if (!index.footnotes.has(label)) index.footnotes.set(label, sym);
2576
+ for (const label of data.linkDefs.keys()) if (!index.links.has(label)) index.links.set(label, sym);
2577
+ const local = /* @__PURE__ */ new Map();
2578
+ index.occurrences.set(sym, local);
2579
+ for (const ref of data.refs) {
2580
+ if (ref.kind !== "footnote") continue;
2581
+ const label = ref.label;
2582
+ if (!index.numbers.has(label)) index.numbers.set(label, index.numbers.size + 1);
2583
+ const total = (index.counts.get(label) ?? 0) + 1;
2584
+ index.counts.set(label, total);
2585
+ const prior = local.get(label);
2586
+ if (prior) prior.count++;
2587
+ else {
2588
+ const range = { start: total, count: 1 };
2589
+ local.set(label, range);
2590
+ let byChunk = index.labelOccurrences.get(label);
2591
+ if (!byChunk) index.labelOccurrences.set(label, byChunk = /* @__PURE__ */ new Map());
2592
+ byChunk.set(sym, range);
2593
+ }
2594
+ }
2595
+ }
2596
+ return index;
2597
+ }
2598
+
2599
+ // src/components/registrySubscriptions.ts
2600
+ function equal(a, b) {
2601
+ if (a.owner !== b.owner || a.url !== b.url || a.title !== b.title || a.number !== b.number || a.count !== b.count)
2602
+ return false;
2603
+ if (a.occurrences?.size !== b.occurrences?.size) return false;
2604
+ if (a.occurrences) {
2605
+ for (const [chunk, range] of a.occurrences) {
2606
+ const next = b.occurrences?.get(chunk);
2607
+ if (!next || next.start !== range.start || next.count !== range.count) return false;
2608
+ }
2609
+ }
2610
+ return true;
2611
+ }
2612
+ function createRegistrySubscriptions(read) {
2613
+ const groups = { link: /* @__PURE__ */ new Map(), footnote: /* @__PURE__ */ new Map() };
2614
+ const snapshot = (kind, label, { registry, index }) => {
2615
+ if (kind === "link") {
2616
+ const owner = index.links.get(label);
2617
+ const def = owner ? registry.chunkData.get(owner)?.linkDefs.get(label) : void 0;
2618
+ return { owner, url: def?.url, title: def?.title };
2619
+ }
2620
+ return {
2621
+ owner: index.footnotes.get(label),
2622
+ number: index.numbers.get(label),
2623
+ count: index.counts.get(label),
2624
+ occurrences: index.labelOccurrences.get(label)
2625
+ };
2626
+ };
2627
+ return {
2628
+ subscribe(kind, rawLabel, cb) {
2629
+ const label = normalizeId(rawLabel);
2630
+ const map = groups[kind];
2631
+ let group = map.get(label);
2632
+ if (!group) {
2633
+ group = { snapshot: snapshot(kind, label, read()), listeners: /* @__PURE__ */ new Set() };
2634
+ map.set(label, group);
2635
+ }
2636
+ const listener = () => cb();
2637
+ group.listeners.add(listener);
2638
+ return () => {
2639
+ group.listeners.delete(listener);
2640
+ if (group.listeners.size === 0 && map.get(label) === group) map.delete(label);
2641
+ };
2642
+ },
2643
+ collect() {
2644
+ if (groups.link.size === 0 && groups.footnote.size === 0) return [];
2645
+ const state = read();
2646
+ const callbacks = [];
2647
+ for (const kind of ["link", "footnote"]) {
2648
+ for (const [label, group] of groups[kind]) {
2649
+ const next = snapshot(kind, label, state);
2650
+ if (!equal(group.snapshot, next)) callbacks.push(...group.listeners);
2651
+ group.snapshot = next;
2652
+ }
2653
+ }
2654
+ return callbacks;
2655
+ }
2656
+ };
2657
+ }
2658
+
2521
2659
  // src/components/documentRegistry.ts
2522
2660
  function createRegistry(onEmpty) {
2661
+ let index;
2662
+ let indexedVersion = -1;
2663
+ const getIndex = () => {
2664
+ if (!index || indexedVersion !== reg.version) {
2665
+ index = buildRegistryIndex(reg);
2666
+ indexedVersion = reg.version;
2667
+ }
2668
+ return index;
2669
+ };
2670
+ const labels = createRegistrySubscriptions(() => ({ registry: reg, index: getIndex() }));
2523
2671
  const reg = {
2524
2672
  chunkOrder: [],
2525
2673
  chunkData: /* @__PURE__ */ new Map(),
@@ -2670,73 +2818,29 @@ function createRegistry(onEmpty) {
2670
2818
  this._subscribers.delete(cb);
2671
2819
  };
2672
2820
  },
2821
+ subscribeLabel(kind, label, cb) {
2822
+ return labels.subscribe(kind, label, cb);
2823
+ },
2673
2824
  canonicalFootnoteFor(label) {
2674
- const id = normalizeId(label);
2675
- for (const sym of this.chunkOrder) {
2676
- const data = this.chunkData.get(sym);
2677
- if (data?.defs.has(id)) return sym;
2678
- }
2679
- return null;
2825
+ return getIndex().footnotes.get(normalizeId(label)) ?? null;
2680
2826
  },
2681
2827
  canonicalLinkFor(label) {
2682
- const id = normalizeId(label);
2683
- for (const sym of this.chunkOrder) {
2684
- const data = this.chunkData.get(sym);
2685
- if (data?.linkDefs.has(id)) return sym;
2686
- }
2687
- return null;
2828
+ return getIndex().links.get(normalizeId(label)) ?? null;
2688
2829
  },
2689
2830
  globalNumber(label) {
2690
- const id = normalizeId(label);
2691
- let n = 0;
2692
- const seen = /* @__PURE__ */ new Set();
2693
- for (const sym of this.chunkOrder) {
2694
- const data = this.chunkData.get(sym);
2695
- if (!data) continue;
2696
- for (const ref of data.refs) {
2697
- if (ref.kind !== "footnote") continue;
2698
- if (!seen.has(ref.label)) {
2699
- seen.add(ref.label);
2700
- n++;
2701
- if (ref.label === id) return n;
2702
- }
2703
- }
2704
- }
2705
- return null;
2831
+ return getIndex().numbers.get(normalizeId(label)) ?? null;
2706
2832
  },
2707
2833
  resolveLinkDef(label) {
2708
- const sym = this.canonicalLinkFor(label);
2709
- if (!sym) return null;
2710
- return this.chunkData.get(sym)?.linkDefs.get(normalizeId(label)) ?? null;
2834
+ const id = normalizeId(label);
2835
+ const sym = getIndex().links.get(id);
2836
+ return sym ? this.chunkData.get(sym)?.linkDefs.get(id) ?? null : null;
2711
2837
  },
2712
2838
  getRefsForLabel(label) {
2713
- const id = normalizeId(label);
2714
- let n = 0;
2715
- for (const sym of this.chunkOrder) {
2716
- const data = this.chunkData.get(sym);
2717
- if (!data) continue;
2718
- for (const ref of data.refs) {
2719
- if (ref.kind === "footnote" && ref.label === id) n++;
2720
- }
2721
- }
2722
- return n;
2839
+ return getIndex().counts.get(normalizeId(label)) ?? 0;
2723
2840
  },
2724
2841
  globalOccurrenceForRef(chunkSym, label, localOccurrence) {
2725
- const id = normalizeId(label);
2726
- let global2 = 0;
2727
- for (const sym of this.chunkOrder) {
2728
- const data = this.chunkData.get(sym);
2729
- if (!data) continue;
2730
- let localCount = 0;
2731
- for (const ref of data.refs) {
2732
- if (ref.kind !== "footnote") continue;
2733
- if (ref.label !== id) continue;
2734
- localCount++;
2735
- global2++;
2736
- if (sym === chunkSym && localCount === localOccurrence) return global2;
2737
- }
2738
- }
2739
- return null;
2842
+ const range = getIndex().occurrences.get(chunkSym)?.get(normalizeId(label));
2843
+ return range && Number.isInteger(localOccurrence) && localOccurrence > 0 && localOccurrence <= range.count ? range.start + localOccurrence - 1 : null;
2740
2844
  },
2741
2845
  _notify() {
2742
2846
  this.version++;
@@ -2744,7 +2848,9 @@ function createRegistry(onEmpty) {
2744
2848
  this._notifyScheduled = true;
2745
2849
  queueMicrotask(() => {
2746
2850
  this._notifyScheduled = false;
2851
+ const labelCallbacks = labels.collect();
2747
2852
  for (const cb of [...this._subscribers]) cb();
2853
+ for (const cb of labelCallbacks) cb();
2748
2854
  });
2749
2855
  }
2750
2856
  };
@@ -2801,7 +2907,7 @@ var ENGINE_PLACEHOLDER_TAGS = /* @__PURE__ */ new Set([
2801
2907
  "cross-chunk-image"
2802
2908
  ]);
2803
2909
  var ENGINE_PROVENANCE_PROPERTY = "engineProvenance";
2804
- function walk(parent, provenance) {
2910
+ function walk(parent, provenance, ancestors) {
2805
2911
  const children = parent.children;
2806
2912
  let i = 0;
2807
2913
  while (i < children.length) {
@@ -2812,21 +2918,32 @@ function walk(parent, provenance) {
2812
2918
  const genuine = provenance !== "" && typeof stamped === "string" && stamped === provenance;
2813
2919
  if (genuine) {
2814
2920
  delete props[ENGINE_PROVENANCE_PROPERTY];
2815
- walk(node, provenance);
2921
+ if (ancestors && (node.tagName === "cross-chunk-link" || node.tagName === "cross-chunk-image")) {
2922
+ (node.data ??= {}).referenceAncestors = ancestors.slice();
2923
+ }
2924
+ ancestors?.push(
2925
+ node.tagName === "cross-chunk-link" ? "a" : node.tagName === "cross-chunk-image" ? "img" : node.tagName
2926
+ );
2927
+ walk(node, provenance, ancestors);
2928
+ ancestors?.pop();
2816
2929
  i += 1;
2817
2930
  } else {
2818
2931
  children.splice(i, 1, ...node.children);
2819
2932
  }
2820
2933
  continue;
2821
2934
  }
2822
- if (node.type === "element") walk(node, provenance);
2935
+ if (node.type === "element") {
2936
+ ancestors?.push(node.tagName);
2937
+ walk(node, provenance, ancestors);
2938
+ ancestors?.pop();
2939
+ }
2823
2940
  i += 1;
2824
2941
  }
2825
2942
  }
2826
2943
  function rehypeVerifyEngineTags(options) {
2827
2944
  const provenance = options?.provenance ?? "";
2828
2945
  return function transformer(tree) {
2829
- walk(tree, provenance);
2946
+ walk(tree, provenance, options?.referenceAncestors ? [] : void 0);
2830
2947
  };
2831
2948
  }
2832
2949
 
@@ -2847,16 +2964,18 @@ var import_remark_remove_comments = __toESM(require("remark-remove-comments"), 1
2847
2964
  // src/components/rehypeRebaseHashLinks.ts
2848
2965
  var import_unist_util_visit5 = require("unist-util-visit");
2849
2966
  var DEFAULT_PREFIX = "user-content-";
2967
+ function rebaseHashHref(href, prefix) {
2968
+ const hashPrefix = "#" + prefix;
2969
+ return href.startsWith("#") && !href.startsWith(hashPrefix) ? hashPrefix + href.slice(1) : href;
2970
+ }
2850
2971
  var rehypeRebaseHashLinks = (options) => {
2851
2972
  const prefix = options?.prefix ?? DEFAULT_PREFIX;
2852
- const hashPrefix = "#" + prefix;
2853
2973
  return (tree) => {
2854
2974
  (0, import_unist_util_visit5.visit)(tree, "element", (node) => {
2855
2975
  if (node.tagName !== "a") return;
2856
2976
  const href = node.properties?.href;
2857
2977
  if (typeof href !== "string" || !href.startsWith("#")) return;
2858
- if (href.startsWith(hashPrefix)) return;
2859
- node.properties.href = hashPrefix + href.slice(1);
2978
+ node.properties.href = rebaseHashHref(href, prefix);
2860
2979
  });
2861
2980
  };
2862
2981
  };
@@ -2942,7 +3061,15 @@ function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix, options) {
2942
3061
  [import_rehype_raw.default, { passThrough: [] }],
2943
3062
  // Unwrap forged engine placeholders BEFORE sanitize admits their tag
2944
3063
  // names. Only when the caller holds a credential (see the option's doc).
2945
- ...options ? [[rehypeVerifyEngineTags, { provenance: options.provenance }]] : [],
3064
+ ...options ? [
3065
+ [
3066
+ rehypeVerifyEngineTags,
3067
+ {
3068
+ provenance: options.provenance,
3069
+ ...sanitizeSchema2.ancestors?.a || sanitizeSchema2.ancestors?.img ? { referenceAncestors: true } : {}
3070
+ }
3071
+ ]
3072
+ ] : [],
2946
3073
  // Sanitize HTML while allowing <mark> (highlight), KaTeX class names,
2947
3074
  // and any extra protocols the caller permitted via the `sanitizeSchema`
2948
3075
  // prop. Override `clobberPrefix` with the instance-scoped value — the
@@ -3016,11 +3143,9 @@ function buildCrossChunkHandlers() {
3016
3143
  type: "element",
3017
3144
  tagName: "cross-chunk-link",
3018
3145
  properties: {
3019
- // `label` is the ORIGINAL source text (mdast's `label` field), NOT
3020
- // the normalized `identifier`. The placeholder uses it to construct
3021
- // hrefs that line up with mdast-util-to-hast's default `<li id>`
3022
- // which also preserves source case. Registry lookups normalize
3023
- // internally, so cross-chunk case-insensitive matching still works.
3146
+ // Display labels decode escapes; registry identifiers must retain
3147
+ // those bytes. Never use the display label as the lookup key.
3148
+ identifier: node.identifier,
3024
3149
  label: node.label ?? node.identifier,
3025
3150
  referenceType: node.referenceType,
3026
3151
  documentId: s.options.documentId,
@@ -3039,6 +3164,7 @@ function buildCrossChunkHandlers() {
3039
3164
  type: "element",
3040
3165
  tagName: "cross-chunk-image",
3041
3166
  properties: {
3167
+ identifier: node.identifier,
3042
3168
  label: node.label ?? node.identifier,
3043
3169
  referenceType: node.referenceType,
3044
3170
  alt: node.alt ?? "",
@@ -4301,8 +4427,8 @@ var sanitizeSchema = deepFreeze(
4301
4427
  attributes: {
4302
4428
  ...import_rehype_sanitize2.defaultSchema.attributes,
4303
4429
  code: mergeClassNameAllowlist(import_rehype_sanitize2.defaultSchema.attributes?.code, ["math-inline", "math-display"]),
4304
- "cross-chunk-link": ["label", "referenceType", "documentId", "localUrl", "localTitle"],
4305
- "cross-chunk-image": ["label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
4430
+ "cross-chunk-link": ["identifier", "label", "referenceType", "documentId", "localUrl", "localTitle"],
4431
+ "cross-chunk-image": ["identifier", "label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
4306
4432
  "footnote-sup": ["label", "localOccurrence", "localNumber", "documentId"]
4307
4433
  },
4308
4434
  strip: [.../* @__PURE__ */ new Set([...import_rehype_sanitize2.defaultSchema.strip || [], ...STRIPPED_TAGS])]
@@ -4338,6 +4464,49 @@ function sanitizeCrossChunkUrl(rawUrl, key, tagName, urlTransform, schema) {
4338
4464
  return String(transformed);
4339
4465
  }
4340
4466
 
4467
+ // src/components/resolveCrossChunkReference.ts
4468
+ var import_rehype_sanitize3 = __toESM(require("rehype-sanitize"), 1);
4469
+ var import_micromark_util_sanitize_uri3 = require("micromark-util-sanitize-uri");
4470
+ function resolveCrossChunkReference(input, schema, urlTransform, clobberPrefix) {
4471
+ const key = input.tagName === "a" ? "href" : "src";
4472
+ const element = {
4473
+ type: "element",
4474
+ tagName: input.tagName,
4475
+ properties: {
4476
+ [key]: (0, import_micromark_util_sanitize_uri3.normalizeUri)(input.url),
4477
+ ...input.tagName === "img" ? { alt: input.alt ?? "" } : {},
4478
+ ...input.title !== void 0 ? { title: input.title } : {}
4479
+ },
4480
+ children: input.tagName === "a" ? [{ type: "text", value: "__reference_children__" }] : []
4481
+ };
4482
+ const requiredAncestors = schema.ancestors?.[input.tagName];
4483
+ const recordedAncestors = input.node?.data?.referenceAncestors;
4484
+ let finalSchema = schema;
4485
+ if (requiredAncestors && Array.isArray(recordedAncestors) && requiredAncestors.some((tag) => recordedAncestors.includes(tag))) {
4486
+ const ancestors = { ...schema.ancestors };
4487
+ delete ancestors[input.tagName];
4488
+ finalSchema = { ...schema, ancestors };
4489
+ }
4490
+ const root2 = (0, import_rehype_sanitize3.default)({ ...finalSchema, clobberPrefix })({ type: "root", children: [element] });
4491
+ const node = root2.children[0];
4492
+ if (node?.type !== "element") return { element: null, keepChildren: node?.type === "text" };
4493
+ if (node.tagName === "a" && typeof node.properties.href === "string") {
4494
+ node.properties.href = rebaseHashHref(node.properties.href, clobberPrefix);
4495
+ }
4496
+ node.children = input.node?.children ?? [];
4497
+ if (input.node?.position) node.position = input.node.position;
4498
+ buildTransform({
4499
+ allowedElements: void 0,
4500
+ disallowedElements: void 0,
4501
+ allowElement: void 0,
4502
+ skipHtml: void 0,
4503
+ unwrapDisallowed: void 0,
4504
+ urlTransform
4505
+ })(node, 0, root2);
4506
+ node.children = [];
4507
+ return { element: node, keepChildren: false };
4508
+ }
4509
+
4341
4510
  // src/plugins/defs.ts
4342
4511
  function getEnginePluginInternals(plugin) {
4343
4512
  const candidate = plugin;
@@ -4542,6 +4711,8 @@ var createSmoothStreamController = (options = {}) => {
4542
4711
  let source = "";
4543
4712
  let visibleEnd = 0;
4544
4713
  let pending = [];
4714
+ let pendingHead = 0;
4715
+ const pendingCount = () => pending.length - pendingHead;
4545
4716
  let tentativeEnd = 0;
4546
4717
  let finished = false;
4547
4718
  let seam;
@@ -4586,7 +4757,7 @@ var createSmoothStreamController = (options = {}) => {
4586
4757
  cancelFrame = void 0;
4587
4758
  };
4588
4759
  const ensureScheduled = () => {
4589
- if (disposed || cancelFrame || pending.length === 0) return;
4760
+ if (disposed || cancelFrame || pendingCount() === 0) return;
4590
4761
  lastTickAt = now();
4591
4762
  credit = 0;
4592
4763
  cancelFrame = schedule(tick);
@@ -4600,31 +4771,38 @@ var createSmoothStreamController = (options = {}) => {
4600
4771
  const params = resolveParams();
4601
4772
  let rate;
4602
4773
  if (finished && drainDeadlineAt !== void 0) {
4603
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, drainDeadlineAt - t));
4774
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, drainDeadlineAt - t));
4604
4775
  } else if (gapWindow.length > 0 && lastArrivalAt !== void 0) {
4605
4776
  const gaps = gapWindow.map((s) => s.gap).sort((a, b) => a - b);
4606
4777
  const intervalQ = gaps[Math.min(gaps.length - 1, Math.floor(gaps.length * INTERVAL_QUANTILE))];
4607
4778
  const horizon = Math.max(16, Math.min(params.bufferFactor * intervalQ + HORIZON_PAD_MS, params.maxLagMs));
4608
4779
  const deadline = Math.max(lastArrivalAt + horizon, t + DEADLINE_FLOOR_MS);
4609
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, deadline - t));
4780
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, deadline - t));
4610
4781
  } else {
4611
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, params.correctionTauMs));
4782
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, params.correctionTauMs));
4612
4783
  }
4613
4784
  credit += rate * dt / 1e3;
4614
- const reveal = Math.min(Math.floor(credit), pending.length);
4785
+ const reveal = Math.min(Math.floor(credit), pendingCount());
4615
4786
  if (reveal > 0) {
4616
- visibleEnd = pending[reveal - 1];
4617
- pending = pending.slice(reveal);
4618
- credit = pending.length > 0 ? credit - reveal : 0;
4787
+ visibleEnd = pending[pendingHead + reveal - 1];
4788
+ pendingHead += reveal;
4789
+ if (pendingHead === pending.length) {
4790
+ pending = [];
4791
+ pendingHead = 0;
4792
+ } else if (pendingHead >= 1024 && pendingHead * 2 >= pending.length) {
4793
+ pending = pending.slice(pendingHead);
4794
+ pendingHead = 0;
4795
+ }
4796
+ credit = pendingCount() > 0 ? credit - reveal : 0;
4619
4797
  notify();
4620
4798
  }
4621
- if (!disposed && !cancelFrame && pending.length > 0) cancelFrame = schedule(tick);
4799
+ if (!disposed && !cancelFrame && pendingCount() > 0) cancelFrame = schedule(tick);
4622
4800
  };
4623
4801
  const resegmentTail = () => {
4624
- if (seam !== void 0 && seam < source.length && pending[pending.length - 1] === seam) {
4802
+ if (pendingCount() > 0 && seam !== void 0 && seam < source.length && pending[pending.length - 1] === seam) {
4625
4803
  pending.pop();
4626
4804
  }
4627
- const from = pending.length > 0 ? pending[pending.length - 1] : visibleEnd;
4805
+ const from = pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd;
4628
4806
  let anchor = from;
4629
4807
  if (seam !== void 0 && from <= seam) {
4630
4808
  anchor = Math.max(0, from - RESUME_LOOKBACK);
@@ -4658,6 +4836,7 @@ var createSmoothStreamController = (options = {}) => {
4658
4836
  visibleEnd = next.length;
4659
4837
  tentativeEnd = next.length;
4660
4838
  pending = [];
4839
+ pendingHead = 0;
4661
4840
  seam = next.length;
4662
4841
  credit = 0;
4663
4842
  cancelScheduled();
@@ -4689,7 +4868,7 @@ var createSmoothStreamController = (options = {}) => {
4689
4868
  if (finished) return;
4690
4869
  finished = true;
4691
4870
  lastArrivalAt = void 0;
4692
- if (tentativeEnd > (pending.length > 0 ? pending[pending.length - 1] : visibleEnd)) {
4871
+ if (tentativeEnd > (pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd)) {
4693
4872
  pending.push(tentativeEnd);
4694
4873
  }
4695
4874
  const params = resolveParams();
@@ -4715,10 +4894,11 @@ var createSmoothStreamController = (options = {}) => {
4715
4894
  snap,
4716
4895
  flush() {
4717
4896
  disposed = false;
4718
- const target = finished ? source.length : pending.length > 0 ? pending[pending.length - 1] : visibleEnd;
4897
+ const target = finished ? source.length : pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd;
4719
4898
  if (target <= visibleEnd) return;
4720
4899
  visibleEnd = target;
4721
4900
  pending = [];
4901
+ pendingHead = 0;
4722
4902
  credit = 0;
4723
4903
  cancelScheduled();
4724
4904
  notify();
@@ -5569,6 +5749,7 @@ function createRemendPreprocessor(options) {
5569
5749
  rehypeRebaseHashLinks,
5570
5750
  rehypeVerifyEngineTags,
5571
5751
  removeComments,
5752
+ resolveCrossChunkReference,
5572
5753
  sanitizeCrossChunkUrl,
5573
5754
  sanitizeSchema,
5574
5755
  shortenDocumentId,