@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.js CHANGED
@@ -172,8 +172,51 @@ var defaultUrlTransform = (value) => {
172
172
  return "";
173
173
  };
174
174
 
175
- // src/components/incrementalParse/computeFreezeBoundary.ts
176
- import { htmlBlockNames } from "micromark-util-html-tag-name";
175
+ // src/components/incrementalParse/freezeScanState.ts
176
+ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
177
+ return {
178
+ defListEnabled,
179
+ mathFlow,
180
+ referenceTaint,
181
+ confirmedOffset: 0,
182
+ candidates: [],
183
+ defs: /* @__PURE__ */ new Map(),
184
+ footnoteDefs: /* @__PURE__ */ new Map(),
185
+ unresolvedRefs: [],
186
+ tagBalance: /* @__PURE__ */ new Map(),
187
+ openTotal: 0,
188
+ p5Tok: { kind: "data" },
189
+ formPointerMaybeSet: false,
190
+ openStack: [],
191
+ mdBlock: { kind: "none" },
192
+ blankRun: 0,
193
+ lastBlankStart: -1,
194
+ hazardVerdict: false,
195
+ prevLineBlank: true,
196
+ // doc start counts as a block start
197
+ prevLineWasText: false,
198
+ prevLineWasValidDef: false,
199
+ prevLineOpenContent: false,
200
+ tableMaybeOpen: false,
201
+ containerMaybeOpen: false,
202
+ paragraphHasUnpairedRun: false,
203
+ openBracket: null,
204
+ p5SealPending: false,
205
+ defBlockMaybeOpen: false,
206
+ fnDefResumable: false,
207
+ phasePoisonedAt: Infinity,
208
+ pendingTruncatedTags: [],
209
+ pendingTruncatedCloses: [],
210
+ pendingTag: null
211
+ };
212
+ }
213
+ function pendingFenceCloser(checkpoint) {
214
+ const cp = checkpoint;
215
+ if (cp.phasePoisonedAt !== Infinity) return "";
216
+ if (cp.mdBlock.kind === "fence" && cp.mdBlock.indent === 0) return cp.mdBlock.char.repeat(cp.mdBlock.len);
217
+ if (cp.mdBlock.kind === "math" && cp.mdBlock.indent === 0) return "$".repeat(cp.mdBlock.len);
218
+ return "";
219
+ }
177
220
 
178
221
  // src/components/incrementalParse/mdLineText.ts
179
222
  var MD_BLANK_RE = /^[ \t\r]*$/;
@@ -181,6 +224,9 @@ var isMdBlank = (text) => MD_BLANK_RE.test(text);
181
224
  var mdTrim = (text) => text.replace(/^[ \t\r]+|[ \t\r]+$/g, "");
182
225
  var mdTrimStart = (text) => text.replace(/^[ \t\r]+/, "");
183
226
 
227
+ // src/components/incrementalParse/freezeLineSyntax.ts
228
+ import { htmlBlockNames } from "micromark-util-html-tag-name";
229
+
184
230
  // src/components/incrementalParse/referenceTaint.ts
185
231
  import { normalizeIdentifier } from "micromark-util-normalize-identifier";
186
232
  var FOOTNOTE_DEF_RE = /^ {0,3}\[\^[^\]]*\]:/;
@@ -366,7 +412,7 @@ function settleRefsAndEarliestUnresolved(cp) {
366
412
  return earliestUnresolved;
367
413
  }
368
414
 
369
- // src/components/incrementalParse/computeFreezeBoundary.ts
415
+ // src/components/incrementalParse/freezeLineSyntax.ts
370
416
  var TYPE6_NAMES = new Set(htmlBlockNames);
371
417
  var TABLE_PART_NAMES = /* @__PURE__ */ new Set(["td", "th", "tr", "tbody", "thead", "tfoot", "caption", "col", "colgroup"]);
372
418
  var DOCUMENT_STRUCTURE_NAMES = /* @__PURE__ */ new Set(["html", "head", "body", "frameset"]);
@@ -617,43 +663,6 @@ function maskIntraLineCodeSpans(text, carryOpen) {
617
663
  out += text.slice(cursor);
618
664
  return { masked: out, unpaired: false };
619
665
  }
620
- function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
621
- return {
622
- defListEnabled,
623
- mathFlow,
624
- referenceTaint,
625
- confirmedOffset: 0,
626
- candidates: [],
627
- defs: /* @__PURE__ */ new Map(),
628
- footnoteDefs: /* @__PURE__ */ new Map(),
629
- unresolvedRefs: [],
630
- tagBalance: /* @__PURE__ */ new Map(),
631
- openTotal: 0,
632
- p5Tok: { kind: "data" },
633
- formPointerMaybeSet: false,
634
- openStack: [],
635
- mdBlock: { kind: "none" },
636
- blankRun: 0,
637
- lastBlankStart: -1,
638
- hazardVerdict: false,
639
- prevLineBlank: true,
640
- // doc start counts as a block start
641
- prevLineWasText: false,
642
- prevLineWasValidDef: false,
643
- prevLineOpenContent: false,
644
- tableMaybeOpen: false,
645
- containerMaybeOpen: false,
646
- paragraphHasUnpairedRun: false,
647
- openBracket: null,
648
- p5SealPending: false,
649
- defBlockMaybeOpen: false,
650
- fnDefResumable: false,
651
- phasePoisonedAt: Infinity,
652
- pendingTruncatedTags: [],
653
- pendingTruncatedCloses: [],
654
- pendingTag: null
655
- };
656
- }
657
666
  function classifyBlockStart(text, indent, defListEnabled) {
658
667
  if (indent >= 4) return true;
659
668
  if (LIST_MARKER_RE.test(text) || FOOTNOTE_DEF_RE.test(text)) return true;
@@ -661,71 +670,23 @@ function classifyBlockStart(text, indent, defListEnabled) {
661
670
  if (indent === 0) return false;
662
671
  return null;
663
672
  }
664
- function computeFreezeBoundary(text, options, resume) {
665
- const mathFlow = options.mathFlow ?? true;
666
- const referenceTaint = options.referenceTaint ?? true;
667
- const prev = resume;
668
- const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
669
- let start = cp.confirmedOffset;
670
- let tailLine = null;
671
- while (start < text.length) {
672
- let end = text.length;
673
- let textEnd = text.length;
674
- {
675
- const nl = text.indexOf("\n", start);
676
- const cr = text.indexOf("\r", start);
677
- if (cr !== -1 && (nl === -1 || cr < nl)) {
678
- textEnd = cr;
679
- end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
680
- } else if (nl !== -1) {
681
- textEnd = nl;
682
- end = nl;
683
- }
684
- }
685
- const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
686
- const lineText = text.slice(start, textEnd);
687
- const ln = {
688
- start,
689
- end,
690
- text: lineText,
691
- blank: confirmed && isMdBlank(lineText),
692
- indent: computeIndent(lineText)
693
- };
694
- if (!confirmed) {
695
- tailLine = ln;
696
- break;
697
- }
698
- processConfirmedLine(cp, ln, text);
699
- cp.confirmedOffset = end + 1;
700
- start = end + 1;
701
- }
702
- const earliestUnresolved = settleRefsAndEarliestUnresolved(cp);
703
- const defListSettled = (c) => {
704
- if (!options.defListEnabled || c.blankRun >= 2) return true;
705
- if (c.defListSettled !== null) return c.defListSettled;
706
- if (tailLine) return !canBecomeDdLine(tailLine.text, false);
707
- return false;
708
- };
709
- const tailRetroactive = tailLine !== null && tailCarriesRetroactive(tailLine.text);
710
- let boundary = 0;
711
- for (let i = cp.candidates.length - 1; i >= 0 && !tailRetroactive; i--) {
712
- const c = cp.candidates[i];
713
- if (!c.htmlBalanced || c.hazard || c.seamRisk) continue;
714
- if (c.offset > cp.phasePoisonedAt) continue;
715
- if (c.offset > earliestUnresolved) continue;
716
- if (!defListSettled(c)) continue;
717
- boundary = c.offset;
718
- break;
719
- }
720
- return { boundary, checkpoint: cp };
721
- }
722
- function pendingFenceCloser(checkpoint) {
723
- const cp = checkpoint;
724
- if (cp.phasePoisonedAt !== Infinity) return "";
725
- if (cp.mdBlock.kind === "fence" && cp.mdBlock.indent === 0) return cp.mdBlock.char.repeat(cp.mdBlock.len);
726
- if (cp.mdBlock.kind === "math" && cp.mdBlock.indent === 0) return "$".repeat(cp.mdBlock.len);
727
- return "";
728
- }
673
+ var SCANNER_NAME_LISTS = [
674
+ ["type1", TYPE1_NAMES],
675
+ ["rawText", RAW_TEXT_ELEMENTS],
676
+ ["type6", TYPE6_NAMES],
677
+ ["void", VOID_TAGS],
678
+ ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
679
+ // Added with F28, and it is the list that would have made F28 visible: the
680
+ // derived census alphabet partitions names by their membership across THIS
681
+ // table, so before it existed `frame` shared one 39-name class with `div`
682
+ // and could never be sampled apart from it.
683
+ ["noElement", NO_ELEMENT_NAMES],
684
+ ["tablePart", TABLE_PART_NAMES],
685
+ ["scopeBarrier", SCOPE_BARRIER_NAMES],
686
+ ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
687
+ ];
688
+
689
+ // src/components/incrementalParse/freezeLineTransition.ts
729
690
  function floatingResidue(text, commentOpenAtStart) {
730
691
  let out = "";
731
692
  let open = commentOpenAtStart;
@@ -1369,24 +1330,66 @@ function processConfirmedLine(cp, ln, text) {
1369
1330
  }
1370
1331
  }
1371
1332
  }
1372
- var SCANNER_NAME_LISTS = [
1373
- ["type1", TYPE1_NAMES],
1374
- ["rawText", RAW_TEXT_ELEMENTS],
1375
- ["type6", TYPE6_NAMES],
1376
- ["void", VOID_TAGS],
1377
- ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
1378
- // Added with F28, and it is the list that would have made F28 visible: the
1379
- // derived census alphabet partitions names by their membership across THIS
1380
- // table, so before it existed `frame` shared one 39-name class with `div`
1381
- // and could never be sampled apart from it.
1382
- ["noElement", NO_ELEMENT_NAMES],
1383
- ["tablePart", TABLE_PART_NAMES],
1384
- ["scopeBarrier", SCOPE_BARRIER_NAMES],
1385
- ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
1386
- ];
1387
1333
 
1388
- // src/components/incrementalParse/spliceParse.ts
1389
- import { normalizeIdentifier as normalizeIdentifier2 } from "micromark-util-normalize-identifier";
1334
+ // src/components/incrementalParse/computeFreezeBoundary.ts
1335
+ function computeFreezeBoundary(text, options, resume) {
1336
+ const mathFlow = options.mathFlow ?? true;
1337
+ const referenceTaint = options.referenceTaint ?? true;
1338
+ const prev = resume;
1339
+ const cp = prev && prev.defListEnabled === options.defListEnabled && prev.mathFlow === mathFlow && prev.referenceTaint === referenceTaint && prev.confirmedOffset <= text.length ? prev : freshCheckpoint(options.defListEnabled, mathFlow, referenceTaint);
1340
+ let start = cp.confirmedOffset;
1341
+ let tailLine = null;
1342
+ while (start < text.length) {
1343
+ let end = text.length;
1344
+ let textEnd = text.length;
1345
+ {
1346
+ const nl = text.indexOf("\n", start);
1347
+ const cr = text.indexOf("\r", start);
1348
+ if (cr !== -1 && (nl === -1 || cr < nl)) {
1349
+ textEnd = cr;
1350
+ end = text.charCodeAt(cr + 1) === 10 ? cr + 1 : cr;
1351
+ } else if (nl !== -1) {
1352
+ textEnd = nl;
1353
+ end = nl;
1354
+ }
1355
+ }
1356
+ const confirmed = end < text.length && !(end === text.length - 1 && text.charCodeAt(end) === 13);
1357
+ const lineText = text.slice(start, textEnd);
1358
+ const ln = {
1359
+ start,
1360
+ end,
1361
+ text: lineText,
1362
+ blank: confirmed && isMdBlank(lineText),
1363
+ indent: computeIndent(lineText)
1364
+ };
1365
+ if (!confirmed) {
1366
+ tailLine = ln;
1367
+ break;
1368
+ }
1369
+ processConfirmedLine(cp, ln, text);
1370
+ cp.confirmedOffset = end + 1;
1371
+ start = end + 1;
1372
+ }
1373
+ const earliestUnresolved = settleRefsAndEarliestUnresolved(cp);
1374
+ const defListSettled = (c) => {
1375
+ if (!options.defListEnabled || c.blankRun >= 2) return true;
1376
+ if (c.defListSettled !== null) return c.defListSettled;
1377
+ if (tailLine) return !canBecomeDdLine(tailLine.text, false);
1378
+ return false;
1379
+ };
1380
+ const tailRetroactive = tailLine !== null && tailCarriesRetroactive(tailLine.text);
1381
+ let boundary = 0;
1382
+ for (let i = cp.candidates.length - 1; i >= 0 && !tailRetroactive; i--) {
1383
+ const c = cp.candidates[i];
1384
+ if (!c.htmlBalanced || c.hazard || c.seamRisk) continue;
1385
+ if (c.offset > cp.phasePoisonedAt) continue;
1386
+ if (c.offset > earliestUnresolved) continue;
1387
+ if (!defListSettled(c)) continue;
1388
+ boundary = c.offset;
1389
+ break;
1390
+ }
1391
+ return { boundary, checkpoint: cp };
1392
+ }
1390
1393
 
1391
1394
  // src/components/hastPredicates.ts
1392
1395
  function isFootnoteSection(node) {
@@ -1434,121 +1437,7 @@ function attributeHastChildren(mdast, hast, stopAt = Infinity) {
1434
1437
  }
1435
1438
  }
1436
1439
 
1437
- // src/components/incrementalParse/spliceParse.ts
1438
- function collectPrefixInjection(mdast, content, boundary, resume) {
1439
- const resumeValid = resume != null && resume.boundary <= boundary;
1440
- if (resumeValid && resume.uninjectable) return { events: resume.events, uninjectable: true };
1441
- const resumeAt = resumeValid ? resume.boundary : 0;
1442
- const events = resumeAt > 0 ? cloneEventsForAppend(resume.events) : [];
1443
- let uninjectable = false;
1444
- let cacheable = true;
1445
- const pushRef = (token) => {
1446
- const last = events[events.length - 1];
1447
- if (last && last.kind === "refs") last.tokens.push(token);
1448
- else events.push({ kind: "refs", tokens: [token] });
1449
- };
1450
- const visit7 = (node, nested) => {
1451
- const type = node.type;
1452
- if (type === "definition") {
1453
- const start = node.position?.start?.offset;
1454
- const end = node.position?.end?.offset;
1455
- if (start !== void 0 && end !== void 0 && start < boundary) {
1456
- const slice = content.slice(start, end);
1457
- if (nested && slice.includes("\n")) uninjectable = true;
1458
- else events.push({ kind: "def", source: slice });
1459
- }
1460
- return;
1461
- }
1462
- if (type === "footnoteDefinition") {
1463
- const start = node.position?.start;
1464
- const end = node.position?.end?.offset;
1465
- if (start?.offset === void 0 || end === void 0 || start.offset >= boundary) return;
1466
- if (nested) {
1467
- uninjectable = true;
1468
- return;
1469
- }
1470
- const lineStart = start.offset - (start.column - 1);
1471
- events.push({
1472
- kind: "footnoteDef",
1473
- source: content.slice(lineStart, end),
1474
- origStart: lineStart,
1475
- origLine: start.line
1476
- });
1477
- return;
1478
- }
1479
- if (type === "footnoteReference") {
1480
- const start = node.position?.start?.offset;
1481
- const end = node.position?.end?.offset;
1482
- if (start === void 0 || end === void 0) {
1483
- uninjectable = true;
1484
- return;
1485
- }
1486
- pushRef(content.slice(start, end));
1487
- return;
1488
- }
1489
- const children = node.children;
1490
- if (children) {
1491
- for (const child of children) visit7(child, true);
1492
- }
1493
- };
1494
- let lastStart = -1;
1495
- for (const child of mdast.children) {
1496
- const start = child.position?.start?.offset;
1497
- if (start !== void 0) {
1498
- if (start < lastStart) return { events: [], uninjectable: true, cacheable: false };
1499
- lastStart = start;
1500
- }
1501
- if (start === void 0) {
1502
- if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
1503
- cacheable = false;
1504
- visit7(child, false);
1505
- continue;
1506
- }
1507
- if (start >= boundary || start < resumeAt) continue;
1508
- visit7(child, false);
1509
- }
1510
- return { events, uninjectable, cacheable };
1511
- }
1512
- function cloneEventsForAppend(events) {
1513
- const out = events.slice();
1514
- const last = out[out.length - 1];
1515
- if (last && last.kind === "refs") out[out.length - 1] = { kind: "refs", tokens: last.tokens.slice() };
1516
- return out;
1517
- }
1518
- var TERMINATOR_LABEL = "__aimd_injection_terminator__";
1519
- var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
1520
- function tailMentionsTerminator(tailSource) {
1521
- return normalizeIdentifier2(tailSource).replace(/\[ /g, "[").includes(`[${normalizeIdentifier2(TERMINATOR_LABEL)}`);
1522
- }
1523
- function buildInjectionPrefix(events) {
1524
- if (events.length === 0) return { text: "", segments: [] };
1525
- let text = "";
1526
- let line = 1;
1527
- const segments = [];
1528
- for (const event of events) {
1529
- if (text.length > 0) {
1530
- text += "\n\n";
1531
- line += 2;
1532
- }
1533
- const injStart = text.length;
1534
- const source = event.kind === "refs" ? event.tokens.join(" ") : event.source;
1535
- if (event.kind === "footnoteDef") {
1536
- segments.push({
1537
- injStart,
1538
- injEnd: injStart + source.length,
1539
- offsetDelta: event.origStart - injStart,
1540
- lineDelta: event.origLine - line
1541
- });
1542
- }
1543
- text += source;
1544
- line += countNewlines(source);
1545
- }
1546
- return { text: `${text}
1547
-
1548
- ${INJECTION_TERMINATOR}
1549
-
1550
- `, segments };
1551
- }
1440
+ // src/components/incrementalParse/spliceCoordinates.ts
1552
1441
  function rebaseTree(node, offsetDelta, lineDelta) {
1553
1442
  rebaseTreeDual(node, EMPTY_SEGMENTS, offsetDelta, lineDelta);
1554
1443
  }
@@ -1577,6 +1466,23 @@ function rebaseDualWalk(node, segments, maxEnd, offsetDelta, lineDelta) {
1577
1466
  for (const child of children) rebaseDualWalk(child, segments, maxEnd, offsetDelta, lineDelta);
1578
1467
  }
1579
1468
  }
1469
+ function rebasePoint(point, offsetDelta, lineDelta) {
1470
+ return {
1471
+ line: point.line + lineDelta,
1472
+ column: point.column,
1473
+ offset: point.offset !== void 0 ? point.offset + offsetDelta : void 0
1474
+ };
1475
+ }
1476
+ function countNewlines(text, end = text.length) {
1477
+ let count = 0;
1478
+ for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
1479
+ for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
1480
+ if (text.charCodeAt(i + 1) !== 10) count += 1;
1481
+ }
1482
+ return count;
1483
+ }
1484
+
1485
+ // src/components/incrementalParse/spliceHtmlGuards.ts
1580
1486
  var TABLE_PART_TAG_RE = /<(?:td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/i;
1581
1487
  var TABLE_TOKEN_RE = /<(\/?)(table|td|th|tr|tbody|thead|tfoot|caption|col|colgroup)\b/gi;
1582
1488
  function hasStrayTablePart(values) {
@@ -1658,127 +1564,26 @@ function rawTextRegionCrossesOut(values) {
1658
1564
  }
1659
1565
  return false;
1660
1566
  }
1661
- function spliceTrees(input) {
1662
- const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
1663
- const injectedLen = injectionPrefix.length;
1664
- const injectedLines = countNewlines(injectionPrefix);
1665
- const prefixLines = countNewlines(content, boundary);
1666
- const offsetDelta = boundary - injectedLen;
1667
- const lineDelta = prefixLines - injectedLines;
1668
- const prefixMdast = [];
1669
- for (const child of prevMdast.children) {
1670
- const start = child.position?.start?.offset;
1671
- if (start !== void 0 && start < boundary) prefixMdast.push(child);
1672
- }
1673
- const attrs = attributeHastChildren(prevMdast, prevHast, boundary);
1674
- const cutRegion = [];
1675
- for (let i = 0; i < prevHast.children.length; i++) {
1676
- if (attrs[i] < boundary) {
1677
- const node2 = prevHast.children[i];
1678
- const end = node2.position?.end?.offset;
1679
- if (end !== void 0 && end > boundary) return null;
1680
- if (isTrailingLiteralText(node2)) {
1681
- const prev = i > 0 ? prevHast.children[i - 1] : void 0;
1682
- if (!prev || prev.type !== "element" || prev.position === void 0) return null;
1683
- if (!ownsTrailingLiteral(prev, node2, prefixMdast))
1684
- return null;
1685
- }
1686
- cutRegion.push(node2);
1687
- continue;
1688
- }
1689
- const node = prevHast.children[i];
1690
- 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
1691
- // raw literal can only trail an `html` mdast node. A position-less
1692
- // text after a `<p>` is the NEXT block's remnant — a stray end tag
1693
- // (`</t>\na`) parse5 dropped, whose text merged with the wrap
1694
- // separator — owned by the tail, which re-parses it; freezing it
1695
- // here duplicated it (v2.4.0 review P3). Falls through to the
1696
- // remnant look-ahead below, which bails to a full parse. And the
1697
- // literal must really be THAT block's trailing text: the block's raw
1698
- // source ends with it. A dropped-tag block right after a frozen html
1699
- // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
1700
- // position, and freezing it duplicated it (release soak of the fix).
1701
- ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
1702
- cutRegion.push(node);
1703
- break;
1704
- }
1705
- for (let j = i; j < prevHast.children.length; j++) {
1706
- const look = prevHast.children[j];
1707
- if (look.type === "element" || look.position !== void 0) break;
1708
- if (isTrailingLiteralText(look)) return null;
1709
- }
1710
- break;
1711
- }
1712
- const tailMdastChildren = tailMdast.children.filter((child) => {
1713
- const start = child.position?.start?.offset;
1714
- return !(start !== void 0 && start < injectedLen);
1715
- });
1716
- const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
1717
- const prefixHtmlValues = prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []);
1718
- if (hasStrayTablePart(prefixHtmlValues)) return null;
1719
- if (rawTextRegionCrossesOut(prefixHtmlValues)) return null;
1720
- const leadingHtml = [];
1721
- for (const child of tailMdastChildren) {
1722
- if (isWrapInvisible(child)) continue;
1723
- if (child.type !== "html") break;
1724
- if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
1725
- leadingHtml.push(child.value);
1567
+ function isSanitizeStrippedConstruct(value) {
1568
+ const v = value.trim();
1569
+ 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(">");
1570
+ }
1571
+ function isExactSanitizeStrippedConstruct(value) {
1572
+ if (value.startsWith("<!--")) {
1573
+ if (value.length < 7 || !value.endsWith("-->")) return false;
1574
+ const close = value.indexOf("-->", 4);
1575
+ const bangClose = value.indexOf("--!>", 4);
1576
+ if (close !== value.length - 3) return false;
1577
+ return bangClose === -1 || bangClose > close;
1726
1578
  }
1727
- if (headRoutedCaptureUnclosed(leadingHtml)) return null;
1728
- const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
1729
- if (aligned === null) return null;
1730
- const hastChildren = aligned.children;
1731
- const interiorFinalLiteral = aligned.interiorFinalLiteral;
1732
- const stripResult = stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible);
1733
- if (stripResult === null) return null;
1734
- const strippedTailHast = stripResult.rest;
1735
- for (const child of tailMdastChildren) rebaseTree(child, offsetDelta, lineDelta);
1736
- for (const child of strippedTailHast) rebaseTreeDual(child, injectedSegments, offsetDelta, lineDelta);
1737
- const mdastChildren = prefixMdast.concat(tailMdastChildren);
1738
- const seamMergeVerdict = stripResult.remnantMerged ?? tailLeadingTextIsHoist(tailMdastChildren, strippedTailHast);
1739
- if (seamMergeVerdict === null) return null;
1740
- const seamMergeAllowed = seamMergeVerdict;
1741
- let firstTailChild = true;
1742
- for (const child of strippedTailHast) {
1743
- const tailEnd = hastChildren[hastChildren.length - 1];
1744
- if (firstTailChild && child.type === "text" && child.position !== void 0) {
1745
- return null;
1746
- }
1747
- if (firstTailChild && tailEnd && tailEnd.type === "text" && child.type === "text" && child.position === void 0) {
1748
- if (interiorFinalLiteral && tailEnd.value.trim() !== "") {
1749
- if (seamMergeAllowed) return null;
1750
- firstTailChild = false;
1751
- hastChildren.push(child);
1752
- continue;
1753
- }
1754
- const literalSeam = tailEnd.value.trim() !== "" && !tailWrapVisible;
1755
- if (seamMergeAllowed || literalSeam) {
1756
- hastChildren[hastChildren.length - 1] = { type: "text", value: tailEnd.value + child.value };
1757
- firstTailChild = false;
1758
- continue;
1759
- }
1760
- }
1761
- firstTailChild = false;
1762
- hastChildren.push(child);
1579
+ if (/^<!doctype/i.test(value)) return false;
1580
+ if (value.startsWith("<?") || /^<!(?:[A-Za-z]|\[CDATA\[)/.test(value)) {
1581
+ return value.indexOf(">") === value.length - 1;
1763
1582
  }
1764
- if (!hastChildren.some((child) => child.position !== void 0)) return null;
1765
- const docStart = { line: 1, column: 1, offset: 0 };
1766
- const mdastRootPosition = tailMdast.position ? { start: docStart, end: rebasePoint(tailMdast.position.end, offsetDelta, lineDelta) } : prevMdast.position;
1767
- const hastRootPosition = mdastRootPosition;
1768
- const mdast = { type: "root", children: mdastChildren };
1769
- if (mdastRootPosition) mdast.position = mdastRootPosition;
1770
- const hast = { type: "root", children: hastChildren };
1771
- if (hastRootPosition) hast.position = hastRootPosition;
1772
- if (prevHast.data !== void 0) hast.data = prevHast.data;
1773
- return { mdast, hast };
1774
- }
1775
- function rebasePoint(point, offsetDelta, lineDelta) {
1776
- return {
1777
- line: point.line + lineDelta,
1778
- column: point.column,
1779
- offset: point.offset !== void 0 ? point.offset + offsetDelta : void 0
1780
- };
1583
+ return false;
1781
1584
  }
1585
+
1586
+ // src/components/incrementalParse/prefixAlignment.ts
1782
1587
  function isWrapInvisible(node) {
1783
1588
  return node.type === "definition" || node.type === "footnoteDefinition";
1784
1589
  }
@@ -1920,96 +1725,303 @@ function stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible) {
1920
1725
  idx += 1;
1921
1726
  continue;
1922
1727
  }
1923
- const start = node.position?.start?.offset;
1924
- if (start === void 0 || start >= injectedLen) return null;
1925
- consumed += 1;
1926
- idx += 1;
1728
+ const start = node.position?.start?.offset;
1729
+ if (start === void 0 || start >= injectedLen) return null;
1730
+ consumed += 1;
1731
+ idx += 1;
1732
+ }
1733
+ if (consumed < injectedVisibleCount) return null;
1734
+ let remnant = "";
1735
+ let remnantMerged = null;
1736
+ if (tailWrapVisible) {
1737
+ const gap = children[idx];
1738
+ if (!gap || !isSeparatorText(gap)) return null;
1739
+ remnant = gap.value.slice(1);
1740
+ remnantMerged = remnant !== "";
1741
+ idx += 1;
1742
+ }
1743
+ const rest = children.slice(idx);
1744
+ if (remnant !== "") rest.unshift({ type: "text", value: remnant });
1745
+ return { rest, remnantMerged };
1746
+ }
1747
+ function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
1748
+ const firstText = tailHastChildren[0];
1749
+ const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
1750
+ if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
1751
+ if (!firstText) return false;
1752
+ if (!isSeparatorText(firstText)) {
1753
+ if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
1754
+ if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
1755
+ if (isSanitizeStrippedConstruct(firstVisible.value)) return false;
1756
+ return null;
1757
+ }
1758
+ return false;
1759
+ }
1760
+ if (!firstVisible) return false;
1761
+ const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
1762
+ if (firstContent) {
1763
+ const start = firstContent.position?.start?.offset;
1764
+ const vStart = firstVisible.position?.start?.offset;
1765
+ const vEnd = firstVisible.position?.end?.offset;
1766
+ if (start !== void 0 && vStart !== void 0 && vEnd !== void 0) {
1767
+ if (start >= vStart && start < vEnd) return true;
1768
+ }
1769
+ }
1770
+ if (firstVisible.type === "math") return false;
1771
+ if (firstVisible.type === "html" && isSanitizeStrippedConstruct(firstVisible.value)) return false;
1772
+ return null;
1773
+ }
1774
+ function isSeparatorText(node) {
1775
+ return node.type === "text" && node.position === void 0 && node.value.trim() === "";
1776
+ }
1777
+ function ownsTrailingLiteral(el, literal, prefixMdast) {
1778
+ const start = el.position?.start?.offset;
1779
+ if (start === void 0) return false;
1780
+ const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
1781
+ if (!owner || owner.type !== "html") return false;
1782
+ const text = literal.value.trim();
1783
+ return text !== "" && owner.value.trimEnd().endsWith(text);
1784
+ }
1785
+ function isTrailingLiteralText(node) {
1786
+ return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
1787
+ }
1788
+ function countTrailingNewlines(value) {
1789
+ let n = 0;
1790
+ for (let i = value.length - 1; i >= 0 && value[i] === "\n"; i--) n += 1;
1791
+ return n;
1792
+ }
1793
+
1794
+ // src/components/incrementalParse/prefixInjection.ts
1795
+ import { normalizeIdentifier as normalizeIdentifier2 } from "micromark-util-normalize-identifier";
1796
+ function collectPrefixInjection(mdast, content, boundary, resume) {
1797
+ const resumeValid = resume != null && resume.boundary <= boundary;
1798
+ if (resumeValid && resume.uninjectable) return { events: resume.events, uninjectable: true };
1799
+ const resumeAt = resumeValid ? resume.boundary : 0;
1800
+ const events = resumeAt > 0 ? cloneEventsForAppend(resume.events) : [];
1801
+ let uninjectable = false;
1802
+ let cacheable = true;
1803
+ const pushRef = (token) => {
1804
+ const last = events[events.length - 1];
1805
+ if (last && last.kind === "refs") last.tokens.push(token);
1806
+ else events.push({ kind: "refs", tokens: [token] });
1807
+ };
1808
+ const visit7 = (node, nested) => {
1809
+ const type = node.type;
1810
+ if (type === "definition") {
1811
+ const start = node.position?.start?.offset;
1812
+ const end = node.position?.end?.offset;
1813
+ if (start !== void 0 && end !== void 0 && start < boundary) {
1814
+ const slice = content.slice(start, end);
1815
+ if (nested && slice.includes("\n")) uninjectable = true;
1816
+ else events.push({ kind: "def", source: slice });
1817
+ }
1818
+ return;
1819
+ }
1820
+ if (type === "footnoteDefinition") {
1821
+ const start = node.position?.start;
1822
+ const end = node.position?.end?.offset;
1823
+ if (start?.offset === void 0 || end === void 0 || start.offset >= boundary) return;
1824
+ if (nested) {
1825
+ uninjectable = true;
1826
+ return;
1827
+ }
1828
+ const lineStart = start.offset - (start.column - 1);
1829
+ events.push({
1830
+ kind: "footnoteDef",
1831
+ source: content.slice(lineStart, end),
1832
+ origStart: lineStart,
1833
+ origLine: start.line
1834
+ });
1835
+ return;
1836
+ }
1837
+ if (type === "footnoteReference") {
1838
+ const start = node.position?.start?.offset;
1839
+ const end = node.position?.end?.offset;
1840
+ if (start === void 0 || end === void 0) {
1841
+ uninjectable = true;
1842
+ return;
1843
+ }
1844
+ pushRef(content.slice(start, end));
1845
+ return;
1846
+ }
1847
+ const children = node.children;
1848
+ if (children) {
1849
+ for (const child of children) visit7(child, true);
1850
+ }
1851
+ };
1852
+ let lastStart = -1;
1853
+ for (const child of mdast.children) {
1854
+ const start = child.position?.start?.offset;
1855
+ if (start !== void 0) {
1856
+ if (start < lastStart) return { events: [], uninjectable: true, cacheable: false };
1857
+ lastStart = start;
1858
+ }
1859
+ if (start === void 0) {
1860
+ if (resumeAt > 0) return collectPrefixInjection(mdast, content, boundary, null);
1861
+ cacheable = false;
1862
+ visit7(child, false);
1863
+ continue;
1864
+ }
1865
+ if (start >= boundary || start < resumeAt) continue;
1866
+ visit7(child, false);
1867
+ }
1868
+ return { events, uninjectable, cacheable };
1869
+ }
1870
+ function cloneEventsForAppend(events) {
1871
+ const out = events.slice();
1872
+ const last = out[out.length - 1];
1873
+ if (last && last.kind === "refs") out[out.length - 1] = { kind: "refs", tokens: last.tokens.slice() };
1874
+ return out;
1875
+ }
1876
+ var TERMINATOR_LABEL = "__aimd_injection_terminator__";
1877
+ var INJECTION_TERMINATOR = `[${TERMINATOR_LABEL}]: __aimd_sentinel_link__`;
1878
+ function tailMentionsTerminator(tailSource) {
1879
+ return normalizeIdentifier2(tailSource).replace(/\[ /g, "[").includes(`[${normalizeIdentifier2(TERMINATOR_LABEL)}`);
1880
+ }
1881
+ function buildInjectionPrefix(events) {
1882
+ if (events.length === 0) return { text: "", segments: [] };
1883
+ let text = "";
1884
+ let line = 1;
1885
+ const segments = [];
1886
+ for (const event of events) {
1887
+ if (text.length > 0) {
1888
+ text += "\n\n";
1889
+ line += 2;
1890
+ }
1891
+ const injStart = text.length;
1892
+ const source = event.kind === "refs" ? event.tokens.join(" ") : event.source;
1893
+ if (event.kind === "footnoteDef") {
1894
+ segments.push({
1895
+ injStart,
1896
+ injEnd: injStart + source.length,
1897
+ offsetDelta: event.origStart - injStart,
1898
+ lineDelta: event.origLine - line
1899
+ });
1900
+ }
1901
+ text += source;
1902
+ line += countNewlines(source);
1903
+ }
1904
+ return { text: `${text}
1905
+
1906
+ ${INJECTION_TERMINATOR}
1907
+
1908
+ `, segments };
1909
+ }
1910
+
1911
+ // src/components/incrementalParse/spliceParse.ts
1912
+ function spliceTrees(input) {
1913
+ const { prevMdast, prevHast, tailMdast, tailHast, content, boundary, injectionPrefix, injectedSegments } = input;
1914
+ const injectedLen = injectionPrefix.length;
1915
+ const injectedLines = countNewlines(injectionPrefix);
1916
+ const prefixLines = countNewlines(content, boundary);
1917
+ const offsetDelta = boundary - injectedLen;
1918
+ const lineDelta = prefixLines - injectedLines;
1919
+ const prefixMdast = [];
1920
+ for (const child of prevMdast.children) {
1921
+ const start = child.position?.start?.offset;
1922
+ if (start !== void 0 && start < boundary) prefixMdast.push(child);
1923
+ }
1924
+ const attrs = attributeHastChildren(prevMdast, prevHast, boundary);
1925
+ const cutRegion = [];
1926
+ for (let i = 0; i < prevHast.children.length; i++) {
1927
+ if (attrs[i] < boundary) {
1928
+ const node2 = prevHast.children[i];
1929
+ const end = node2.position?.end?.offset;
1930
+ if (end !== void 0 && end > boundary) return null;
1931
+ if (isTrailingLiteralText(node2)) {
1932
+ const prev = i > 0 ? prevHast.children[i - 1] : void 0;
1933
+ if (!prev || prev.type !== "element" || prev.position === void 0) return null;
1934
+ if (!ownsTrailingLiteral(prev, node2, prefixMdast))
1935
+ return null;
1936
+ }
1937
+ cutRegion.push(node2);
1938
+ continue;
1939
+ }
1940
+ const node = prevHast.children[i];
1941
+ 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
1942
+ // raw literal can only trail an `html` mdast node. A position-less
1943
+ // text after a `<p>` is the NEXT block's remnant — a stray end tag
1944
+ // (`</t>\na`) parse5 dropped, whose text merged with the wrap
1945
+ // separator — owned by the tail, which re-parses it; freezing it
1946
+ // here duplicated it (v2.4.0 review P3). Falls through to the
1947
+ // remnant look-ahead below, which bails to a full parse. And the
1948
+ // literal must really be THAT block's trailing text: the block's raw
1949
+ // source ends with it. A dropped-tag block right after a frozen html
1950
+ // element (`</details>\n\n</t>\ntext`) puts its remnant in the same
1951
+ // position, and freezing it duplicated it (release soak of the fix).
1952
+ ownsTrailingLiteral(prevHast.children[i - 1], node, prefixMdast)) {
1953
+ cutRegion.push(node);
1954
+ break;
1955
+ }
1956
+ for (let j = i; j < prevHast.children.length; j++) {
1957
+ const look = prevHast.children[j];
1958
+ if (look.type === "element" || look.position !== void 0) break;
1959
+ if (isTrailingLiteralText(look)) return null;
1960
+ }
1961
+ break;
1927
1962
  }
1928
- if (consumed < injectedVisibleCount) return null;
1929
- let remnant = "";
1930
- let remnantMerged = null;
1931
- if (tailWrapVisible) {
1932
- const gap = children[idx];
1933
- if (!gap || !isSeparatorText(gap)) return null;
1934
- remnant = gap.value.slice(1);
1935
- remnantMerged = remnant !== "";
1936
- idx += 1;
1963
+ const tailMdastChildren = tailMdast.children.filter((child) => {
1964
+ const start = child.position?.start?.offset;
1965
+ return !(start !== void 0 && start < injectedLen);
1966
+ });
1967
+ const tailWrapVisible = tailMdastChildren.some((child) => !isWrapInvisible(child));
1968
+ const prefixHtmlValues = prefixMdast.flatMap((c) => c.type === "html" ? [c.value] : []);
1969
+ if (hasStrayTablePart(prefixHtmlValues)) return null;
1970
+ if (rawTextRegionCrossesOut(prefixHtmlValues)) return null;
1971
+ const leadingHtml = [];
1972
+ for (const child of tailMdastChildren) {
1973
+ if (isWrapInvisible(child)) continue;
1974
+ if (child.type !== "html") break;
1975
+ if (STRAY_SYNTHESIZED_END_TAG_RE.test(child.value) || TABLE_PART_TAG_RE.test(child.value)) return null;
1976
+ leadingHtml.push(child.value);
1937
1977
  }
1938
- const rest = children.slice(idx);
1939
- if (remnant !== "") rest.unshift({ type: "text", value: remnant });
1940
- return { rest, remnantMerged };
1941
- }
1942
- function tailLeadingTextIsHoist(tailMdastChildren, tailHastChildren) {
1943
- const firstText = tailHastChildren[0];
1944
- const firstVisible = tailMdastChildren.find((c) => !isWrapInvisible(c));
1945
- if (firstVisible?.type === "html" && STRAY_SYNTHESIZED_END_TAG_RE.test(firstVisible.value)) return null;
1946
- if (!firstText) return false;
1947
- if (!isSeparatorText(firstText)) {
1948
- if (firstText.type === "text" && firstText.position === void 0 && firstVisible?.type === "html") {
1949
- if (/^\s*<\/[A-Za-z][A-Za-z0-9-]*\s*>/.test(firstVisible.value)) return true;
1950
- if (isSanitizeStrippedConstruct(firstVisible.value)) return false;
1978
+ if (headRoutedCaptureUnclosed(leadingHtml)) return null;
1979
+ const aligned = alignPrefixCut(prefixMdast, cutRegion, tailWrapVisible);
1980
+ if (aligned === null) return null;
1981
+ const hastChildren = aligned.children;
1982
+ const interiorFinalLiteral = aligned.interiorFinalLiteral;
1983
+ const stripResult = stripInjectedHast(tailMdast, tailHast, injectedLen, tailWrapVisible);
1984
+ if (stripResult === null) return null;
1985
+ const strippedTailHast = stripResult.rest;
1986
+ for (const child of tailMdastChildren) rebaseTree(child, offsetDelta, lineDelta);
1987
+ for (const child of strippedTailHast) rebaseTreeDual(child, injectedSegments, offsetDelta, lineDelta);
1988
+ const mdastChildren = prefixMdast.concat(tailMdastChildren);
1989
+ const seamMergeVerdict = stripResult.remnantMerged ?? tailLeadingTextIsHoist(tailMdastChildren, strippedTailHast);
1990
+ if (seamMergeVerdict === null) return null;
1991
+ const seamMergeAllowed = seamMergeVerdict;
1992
+ let firstTailChild = true;
1993
+ for (const child of strippedTailHast) {
1994
+ const tailEnd = hastChildren[hastChildren.length - 1];
1995
+ if (firstTailChild && child.type === "text" && child.position !== void 0) {
1951
1996
  return null;
1952
1997
  }
1953
- return false;
1954
- }
1955
- if (!firstVisible) return false;
1956
- const firstContent = tailHastChildren.find((c) => !isSeparatorText(c));
1957
- if (firstContent) {
1958
- const start = firstContent.position?.start?.offset;
1959
- const vStart = firstVisible.position?.start?.offset;
1960
- const vEnd = firstVisible.position?.end?.offset;
1961
- if (start !== void 0 && vStart !== void 0 && vEnd !== void 0) {
1962
- if (start >= vStart && start < vEnd) return true;
1998
+ if (firstTailChild && tailEnd && tailEnd.type === "text" && child.type === "text" && child.position === void 0) {
1999
+ if (interiorFinalLiteral && tailEnd.value.trim() !== "") {
2000
+ if (seamMergeAllowed) return null;
2001
+ firstTailChild = false;
2002
+ hastChildren.push(child);
2003
+ continue;
2004
+ }
2005
+ const literalSeam = tailEnd.value.trim() !== "" && !tailWrapVisible;
2006
+ if (seamMergeAllowed || literalSeam) {
2007
+ hastChildren[hastChildren.length - 1] = { type: "text", value: tailEnd.value + child.value };
2008
+ firstTailChild = false;
2009
+ continue;
2010
+ }
1963
2011
  }
2012
+ firstTailChild = false;
2013
+ hastChildren.push(child);
1964
2014
  }
1965
- if (firstVisible.type === "math") return false;
1966
- if (firstVisible.type === "html" && isSanitizeStrippedConstruct(firstVisible.value)) return false;
1967
- return null;
1968
- }
1969
- function isSanitizeStrippedConstruct(value) {
1970
- const v = value.trim();
1971
- 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(">");
1972
- }
1973
- function isExactSanitizeStrippedConstruct(value) {
1974
- if (value.startsWith("<!--")) {
1975
- if (value.length < 7 || !value.endsWith("-->")) return false;
1976
- const close = value.indexOf("-->", 4);
1977
- const bangClose = value.indexOf("--!>", 4);
1978
- if (close !== value.length - 3) return false;
1979
- return bangClose === -1 || bangClose > close;
1980
- }
1981
- if (/^<!doctype/i.test(value)) return false;
1982
- if (value.startsWith("<?") || /^<!(?:[A-Za-z]|\[CDATA\[)/.test(value)) {
1983
- return value.indexOf(">") === value.length - 1;
1984
- }
1985
- return false;
1986
- }
1987
- function isSeparatorText(node) {
1988
- return node.type === "text" && node.position === void 0 && node.value.trim() === "";
1989
- }
1990
- function ownsTrailingLiteral(el, literal, prefixMdast) {
1991
- const start = el.position?.start?.offset;
1992
- if (start === void 0) return false;
1993
- const owner = prefixMdast.find((c) => c.type === "html" && c.position?.start?.offset === start);
1994
- if (!owner || owner.type !== "html") return false;
1995
- const text = literal.value.trim();
1996
- return text !== "" && owner.value.trimEnd().endsWith(text);
1997
- }
1998
- function isTrailingLiteralText(node) {
1999
- return node.type === "text" && node.position === void 0 && node.value.trim() !== "";
2000
- }
2001
- function countTrailingNewlines(value) {
2002
- let n = 0;
2003
- for (let i = value.length - 1; i >= 0 && value[i] === "\n"; i--) n += 1;
2004
- return n;
2005
- }
2006
- function countNewlines(text, end = text.length) {
2007
- let count = 0;
2008
- for (let i = text.indexOf("\n"); i !== -1 && i < end; i = text.indexOf("\n", i + 1)) count += 1;
2009
- for (let i = text.indexOf("\r"); i !== -1 && i < end; i = text.indexOf("\r", i + 1)) {
2010
- if (text.charCodeAt(i + 1) !== 10) count += 1;
2011
- }
2012
- return count;
2015
+ if (!hastChildren.some((child) => child.position !== void 0)) return null;
2016
+ const docStart = { line: 1, column: 1, offset: 0 };
2017
+ const mdastRootPosition = tailMdast.position ? { start: docStart, end: rebasePoint(tailMdast.position.end, offsetDelta, lineDelta) } : prevMdast.position;
2018
+ const hastRootPosition = mdastRootPosition;
2019
+ const mdast = { type: "root", children: mdastChildren };
2020
+ if (mdastRootPosition) mdast.position = mdastRootPosition;
2021
+ const hast = { type: "root", children: hastChildren };
2022
+ if (hastRootPosition) hast.position = hastRootPosition;
2023
+ if (prevHast.data !== void 0) hast.data = prevHast.data;
2024
+ return { mdast, hast };
2013
2025
  }
2014
2026
 
2015
2027
  // src/components/incrementalParse/advanceIncrementalParse.ts
@@ -2131,6 +2143,38 @@ function normalizeForMatch(s) {
2131
2143
  return normalizeIdentifier3(s);
2132
2144
  }
2133
2145
 
2146
+ // src/components/blankLineScanner.ts
2147
+ function createBlankLineScanner() {
2148
+ let end = 0;
2149
+ let newline = false;
2150
+ let cr = false;
2151
+ return (source, from = 0) => {
2152
+ if (from === 0) {
2153
+ end = 0;
2154
+ newline = false;
2155
+ cr = false;
2156
+ }
2157
+ for (let i = from; i < source.length; i++) {
2158
+ const c = source[i];
2159
+ if (c === "\n") {
2160
+ if (newline) {
2161
+ end = i + 1;
2162
+ newline = false;
2163
+ } else newline = true;
2164
+ cr = false;
2165
+ } else if (newline) {
2166
+ if ((c === " " || c === " ") && !cr) continue;
2167
+ if (c === "\r" && !cr) cr = true;
2168
+ else {
2169
+ newline = false;
2170
+ cr = false;
2171
+ }
2172
+ }
2173
+ }
2174
+ return end;
2175
+ };
2176
+ }
2177
+
2134
2178
  // src/components/collectDefLabels.ts
2135
2179
  var SCANNER_BOUNDARY_PROFILE = { defListEnabled: false, mathFlow: false, referenceTaint: false };
2136
2180
  function buildProcessor() {
@@ -2162,19 +2206,12 @@ var setsEqual = (a, b) => {
2162
2206
  for (const v of a) if (!b.has(v)) return false;
2163
2207
  return true;
2164
2208
  };
2165
- var BLANK_LINE_RE = /\r?\n[ \t]*\r?\n/g;
2166
- function lastRegionStart(source) {
2167
- BLANK_LINE_RE.lastIndex = 0;
2168
- let start = 0;
2169
- for (let m = BLANK_LINE_RE.exec(source); m !== null; m = BLANK_LINE_RE.exec(source)) {
2170
- start = m.index + m[0].length;
2171
- }
2172
- return start;
2173
- }
2174
2209
  var DEF_LINE_START_RE = /^[ \t>*+\d.)-]*\[(?:[^\]\\]|\\[\s\S])*\]:/m;
2175
2210
  function createDefLabelScanner(parse = collectDefLabels) {
2176
2211
  let prevSource = null;
2177
2212
  let prevLabels = null;
2213
+ const scanBlankLines = createBlankLineScanner();
2214
+ let regionStart = 0;
2178
2215
  let frozenEnd = 0;
2179
2216
  let frozenFootnotes = /* @__PURE__ */ new Set();
2180
2217
  let frozenLinks = /* @__PURE__ */ new Set();
@@ -2187,13 +2224,16 @@ function createDefLabelScanner(parse = collectDefLabels) {
2187
2224
  };
2188
2225
  return {
2189
2226
  scan(source) {
2227
+ if (source === prevSource && prevLabels !== null) return prevLabels;
2228
+ const previousRegionStart = regionStart;
2229
+ const appended = prevSource !== null && source.startsWith(prevSource);
2230
+ regionStart = scanBlankLines(source, appended ? prevSource.length : 0);
2190
2231
  let isAppend = false;
2191
2232
  if (prevSource !== null && prevLabels !== null) {
2192
2233
  if (source === prevSource) return prevLabels;
2193
2234
  if (source.startsWith(prevSource)) {
2194
2235
  isAppend = true;
2195
- const regionStart = lastRegionStart(prevSource);
2196
- const region = prevSource.slice(regionStart) + source.slice(prevSource.length);
2236
+ const region = source.slice(previousRegionStart);
2197
2237
  if (!DEF_LINE_START_RE.test(region)) {
2198
2238
  prevSource = source;
2199
2239
  return prevLabels;
@@ -2426,8 +2466,115 @@ function* extractContributions(mdast, options = {}) {
2426
2466
  for (const c of out) yield c;
2427
2467
  }
2428
2468
 
2469
+ // src/components/registryIndex.ts
2470
+ function buildRegistryIndex(registry) {
2471
+ const index = {
2472
+ footnotes: /* @__PURE__ */ new Map(),
2473
+ links: /* @__PURE__ */ new Map(),
2474
+ numbers: /* @__PURE__ */ new Map(),
2475
+ counts: /* @__PURE__ */ new Map(),
2476
+ occurrences: /* @__PURE__ */ new Map(),
2477
+ labelOccurrences: /* @__PURE__ */ new Map()
2478
+ };
2479
+ for (const sym of registry.chunkOrder) {
2480
+ const data = registry.chunkData.get(sym);
2481
+ if (!data) continue;
2482
+ for (const label of data.defs.keys()) if (!index.footnotes.has(label)) index.footnotes.set(label, sym);
2483
+ for (const label of data.linkDefs.keys()) if (!index.links.has(label)) index.links.set(label, sym);
2484
+ const local = /* @__PURE__ */ new Map();
2485
+ index.occurrences.set(sym, local);
2486
+ for (const ref of data.refs) {
2487
+ if (ref.kind !== "footnote") continue;
2488
+ const label = ref.label;
2489
+ if (!index.numbers.has(label)) index.numbers.set(label, index.numbers.size + 1);
2490
+ const total = (index.counts.get(label) ?? 0) + 1;
2491
+ index.counts.set(label, total);
2492
+ const prior = local.get(label);
2493
+ if (prior) prior.count++;
2494
+ else {
2495
+ const range = { start: total, count: 1 };
2496
+ local.set(label, range);
2497
+ let byChunk = index.labelOccurrences.get(label);
2498
+ if (!byChunk) index.labelOccurrences.set(label, byChunk = /* @__PURE__ */ new Map());
2499
+ byChunk.set(sym, range);
2500
+ }
2501
+ }
2502
+ }
2503
+ return index;
2504
+ }
2505
+
2506
+ // src/components/registrySubscriptions.ts
2507
+ function equal(a, b) {
2508
+ if (a.owner !== b.owner || a.url !== b.url || a.title !== b.title || a.number !== b.number || a.count !== b.count)
2509
+ return false;
2510
+ if (a.occurrences?.size !== b.occurrences?.size) return false;
2511
+ if (a.occurrences) {
2512
+ for (const [chunk, range] of a.occurrences) {
2513
+ const next = b.occurrences?.get(chunk);
2514
+ if (!next || next.start !== range.start || next.count !== range.count) return false;
2515
+ }
2516
+ }
2517
+ return true;
2518
+ }
2519
+ function createRegistrySubscriptions(read) {
2520
+ const groups = { link: /* @__PURE__ */ new Map(), footnote: /* @__PURE__ */ new Map() };
2521
+ const snapshot = (kind, label, { registry, index }) => {
2522
+ if (kind === "link") {
2523
+ const owner = index.links.get(label);
2524
+ const def = owner ? registry.chunkData.get(owner)?.linkDefs.get(label) : void 0;
2525
+ return { owner, url: def?.url, title: def?.title };
2526
+ }
2527
+ return {
2528
+ owner: index.footnotes.get(label),
2529
+ number: index.numbers.get(label),
2530
+ count: index.counts.get(label),
2531
+ occurrences: index.labelOccurrences.get(label)
2532
+ };
2533
+ };
2534
+ return {
2535
+ subscribe(kind, rawLabel, cb) {
2536
+ const label = normalizeId(rawLabel);
2537
+ const map = groups[kind];
2538
+ let group = map.get(label);
2539
+ if (!group) {
2540
+ group = { snapshot: snapshot(kind, label, read()), listeners: /* @__PURE__ */ new Set() };
2541
+ map.set(label, group);
2542
+ }
2543
+ const listener = () => cb();
2544
+ group.listeners.add(listener);
2545
+ return () => {
2546
+ group.listeners.delete(listener);
2547
+ if (group.listeners.size === 0 && map.get(label) === group) map.delete(label);
2548
+ };
2549
+ },
2550
+ collect() {
2551
+ if (groups.link.size === 0 && groups.footnote.size === 0) return [];
2552
+ const state = read();
2553
+ const callbacks = [];
2554
+ for (const kind of ["link", "footnote"]) {
2555
+ for (const [label, group] of groups[kind]) {
2556
+ const next = snapshot(kind, label, state);
2557
+ if (!equal(group.snapshot, next)) callbacks.push(...group.listeners);
2558
+ group.snapshot = next;
2559
+ }
2560
+ }
2561
+ return callbacks;
2562
+ }
2563
+ };
2564
+ }
2565
+
2429
2566
  // src/components/documentRegistry.ts
2430
2567
  function createRegistry(onEmpty) {
2568
+ let index;
2569
+ let indexedVersion = -1;
2570
+ const getIndex = () => {
2571
+ if (!index || indexedVersion !== reg.version) {
2572
+ index = buildRegistryIndex(reg);
2573
+ indexedVersion = reg.version;
2574
+ }
2575
+ return index;
2576
+ };
2577
+ const labels = createRegistrySubscriptions(() => ({ registry: reg, index: getIndex() }));
2431
2578
  const reg = {
2432
2579
  chunkOrder: [],
2433
2580
  chunkData: /* @__PURE__ */ new Map(),
@@ -2578,73 +2725,29 @@ function createRegistry(onEmpty) {
2578
2725
  this._subscribers.delete(cb);
2579
2726
  };
2580
2727
  },
2728
+ subscribeLabel(kind, label, cb) {
2729
+ return labels.subscribe(kind, label, cb);
2730
+ },
2581
2731
  canonicalFootnoteFor(label) {
2582
- const id = normalizeId(label);
2583
- for (const sym of this.chunkOrder) {
2584
- const data = this.chunkData.get(sym);
2585
- if (data?.defs.has(id)) return sym;
2586
- }
2587
- return null;
2732
+ return getIndex().footnotes.get(normalizeId(label)) ?? null;
2588
2733
  },
2589
2734
  canonicalLinkFor(label) {
2590
- const id = normalizeId(label);
2591
- for (const sym of this.chunkOrder) {
2592
- const data = this.chunkData.get(sym);
2593
- if (data?.linkDefs.has(id)) return sym;
2594
- }
2595
- return null;
2735
+ return getIndex().links.get(normalizeId(label)) ?? null;
2596
2736
  },
2597
2737
  globalNumber(label) {
2598
- const id = normalizeId(label);
2599
- let n = 0;
2600
- const seen = /* @__PURE__ */ new Set();
2601
- for (const sym of this.chunkOrder) {
2602
- const data = this.chunkData.get(sym);
2603
- if (!data) continue;
2604
- for (const ref of data.refs) {
2605
- if (ref.kind !== "footnote") continue;
2606
- if (!seen.has(ref.label)) {
2607
- seen.add(ref.label);
2608
- n++;
2609
- if (ref.label === id) return n;
2610
- }
2611
- }
2612
- }
2613
- return null;
2738
+ return getIndex().numbers.get(normalizeId(label)) ?? null;
2614
2739
  },
2615
2740
  resolveLinkDef(label) {
2616
- const sym = this.canonicalLinkFor(label);
2617
- if (!sym) return null;
2618
- return this.chunkData.get(sym)?.linkDefs.get(normalizeId(label)) ?? null;
2741
+ const id = normalizeId(label);
2742
+ const sym = getIndex().links.get(id);
2743
+ return sym ? this.chunkData.get(sym)?.linkDefs.get(id) ?? null : null;
2619
2744
  },
2620
2745
  getRefsForLabel(label) {
2621
- const id = normalizeId(label);
2622
- let n = 0;
2623
- for (const sym of this.chunkOrder) {
2624
- const data = this.chunkData.get(sym);
2625
- if (!data) continue;
2626
- for (const ref of data.refs) {
2627
- if (ref.kind === "footnote" && ref.label === id) n++;
2628
- }
2629
- }
2630
- return n;
2746
+ return getIndex().counts.get(normalizeId(label)) ?? 0;
2631
2747
  },
2632
2748
  globalOccurrenceForRef(chunkSym, label, localOccurrence) {
2633
- const id = normalizeId(label);
2634
- let global2 = 0;
2635
- for (const sym of this.chunkOrder) {
2636
- const data = this.chunkData.get(sym);
2637
- if (!data) continue;
2638
- let localCount = 0;
2639
- for (const ref of data.refs) {
2640
- if (ref.kind !== "footnote") continue;
2641
- if (ref.label !== id) continue;
2642
- localCount++;
2643
- global2++;
2644
- if (sym === chunkSym && localCount === localOccurrence) return global2;
2645
- }
2646
- }
2647
- return null;
2749
+ const range = getIndex().occurrences.get(chunkSym)?.get(normalizeId(label));
2750
+ return range && Number.isInteger(localOccurrence) && localOccurrence > 0 && localOccurrence <= range.count ? range.start + localOccurrence - 1 : null;
2648
2751
  },
2649
2752
  _notify() {
2650
2753
  this.version++;
@@ -2652,7 +2755,9 @@ function createRegistry(onEmpty) {
2652
2755
  this._notifyScheduled = true;
2653
2756
  queueMicrotask(() => {
2654
2757
  this._notifyScheduled = false;
2758
+ const labelCallbacks = labels.collect();
2655
2759
  for (const cb of [...this._subscribers]) cb();
2760
+ for (const cb of labelCallbacks) cb();
2656
2761
  });
2657
2762
  }
2658
2763
  };
@@ -2709,7 +2814,7 @@ var ENGINE_PLACEHOLDER_TAGS = /* @__PURE__ */ new Set([
2709
2814
  "cross-chunk-image"
2710
2815
  ]);
2711
2816
  var ENGINE_PROVENANCE_PROPERTY = "engineProvenance";
2712
- function walk(parent, provenance) {
2817
+ function walk(parent, provenance, ancestors) {
2713
2818
  const children = parent.children;
2714
2819
  let i = 0;
2715
2820
  while (i < children.length) {
@@ -2720,21 +2825,32 @@ function walk(parent, provenance) {
2720
2825
  const genuine = provenance !== "" && typeof stamped === "string" && stamped === provenance;
2721
2826
  if (genuine) {
2722
2827
  delete props[ENGINE_PROVENANCE_PROPERTY];
2723
- walk(node, provenance);
2828
+ if (ancestors && (node.tagName === "cross-chunk-link" || node.tagName === "cross-chunk-image")) {
2829
+ (node.data ??= {}).referenceAncestors = ancestors.slice();
2830
+ }
2831
+ ancestors?.push(
2832
+ node.tagName === "cross-chunk-link" ? "a" : node.tagName === "cross-chunk-image" ? "img" : node.tagName
2833
+ );
2834
+ walk(node, provenance, ancestors);
2835
+ ancestors?.pop();
2724
2836
  i += 1;
2725
2837
  } else {
2726
2838
  children.splice(i, 1, ...node.children);
2727
2839
  }
2728
2840
  continue;
2729
2841
  }
2730
- if (node.type === "element") walk(node, provenance);
2842
+ if (node.type === "element") {
2843
+ ancestors?.push(node.tagName);
2844
+ walk(node, provenance, ancestors);
2845
+ ancestors?.pop();
2846
+ }
2731
2847
  i += 1;
2732
2848
  }
2733
2849
  }
2734
2850
  function rehypeVerifyEngineTags(options) {
2735
2851
  const provenance = options?.provenance ?? "";
2736
2852
  return function transformer(tree) {
2737
- walk(tree, provenance);
2853
+ walk(tree, provenance, options?.referenceAncestors ? [] : void 0);
2738
2854
  };
2739
2855
  }
2740
2856
 
@@ -2755,16 +2871,18 @@ import remarkRemoveComments from "remark-remove-comments";
2755
2871
  // src/components/rehypeRebaseHashLinks.ts
2756
2872
  import { visit as visit5 } from "unist-util-visit";
2757
2873
  var DEFAULT_PREFIX = "user-content-";
2874
+ function rebaseHashHref(href, prefix) {
2875
+ const hashPrefix = "#" + prefix;
2876
+ return href.startsWith("#") && !href.startsWith(hashPrefix) ? hashPrefix + href.slice(1) : href;
2877
+ }
2758
2878
  var rehypeRebaseHashLinks = (options) => {
2759
2879
  const prefix = options?.prefix ?? DEFAULT_PREFIX;
2760
- const hashPrefix = "#" + prefix;
2761
2880
  return (tree) => {
2762
2881
  visit5(tree, "element", (node) => {
2763
2882
  if (node.tagName !== "a") return;
2764
2883
  const href = node.properties?.href;
2765
2884
  if (typeof href !== "string" || !href.startsWith("#")) return;
2766
- if (href.startsWith(hashPrefix)) return;
2767
- node.properties.href = hashPrefix + href.slice(1);
2885
+ node.properties.href = rebaseHashHref(href, prefix);
2768
2886
  });
2769
2887
  };
2770
2888
  };
@@ -2850,7 +2968,15 @@ function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix, options) {
2850
2968
  [rehypeRaw, { passThrough: [] }],
2851
2969
  // Unwrap forged engine placeholders BEFORE sanitize admits their tag
2852
2970
  // names. Only when the caller holds a credential (see the option's doc).
2853
- ...options ? [[rehypeVerifyEngineTags, { provenance: options.provenance }]] : [],
2971
+ ...options ? [
2972
+ [
2973
+ rehypeVerifyEngineTags,
2974
+ {
2975
+ provenance: options.provenance,
2976
+ ...sanitizeSchema2.ancestors?.a || sanitizeSchema2.ancestors?.img ? { referenceAncestors: true } : {}
2977
+ }
2978
+ ]
2979
+ ] : [],
2854
2980
  // Sanitize HTML while allowing <mark> (highlight), KaTeX class names,
2855
2981
  // and any extra protocols the caller permitted via the `sanitizeSchema`
2856
2982
  // prop. Override `clobberPrefix` with the instance-scoped value — the
@@ -2924,11 +3050,9 @@ function buildCrossChunkHandlers() {
2924
3050
  type: "element",
2925
3051
  tagName: "cross-chunk-link",
2926
3052
  properties: {
2927
- // `label` is the ORIGINAL source text (mdast's `label` field), NOT
2928
- // the normalized `identifier`. The placeholder uses it to construct
2929
- // hrefs that line up with mdast-util-to-hast's default `<li id>`
2930
- // which also preserves source case. Registry lookups normalize
2931
- // internally, so cross-chunk case-insensitive matching still works.
3053
+ // Display labels decode escapes; registry identifiers must retain
3054
+ // those bytes. Never use the display label as the lookup key.
3055
+ identifier: node.identifier,
2932
3056
  label: node.label ?? node.identifier,
2933
3057
  referenceType: node.referenceType,
2934
3058
  documentId: s.options.documentId,
@@ -2947,6 +3071,7 @@ function buildCrossChunkHandlers() {
2947
3071
  type: "element",
2948
3072
  tagName: "cross-chunk-image",
2949
3073
  properties: {
3074
+ identifier: node.identifier,
2950
3075
  label: node.label ?? node.identifier,
2951
3076
  referenceType: node.referenceType,
2952
3077
  alt: node.alt ?? "",
@@ -4209,8 +4334,8 @@ var sanitizeSchema = deepFreeze(
4209
4334
  attributes: {
4210
4335
  ...defaultSchema.attributes,
4211
4336
  code: mergeClassNameAllowlist(defaultSchema.attributes?.code, ["math-inline", "math-display"]),
4212
- "cross-chunk-link": ["label", "referenceType", "documentId", "localUrl", "localTitle"],
4213
- "cross-chunk-image": ["label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
4337
+ "cross-chunk-link": ["identifier", "label", "referenceType", "documentId", "localUrl", "localTitle"],
4338
+ "cross-chunk-image": ["identifier", "label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
4214
4339
  "footnote-sup": ["label", "localOccurrence", "localNumber", "documentId"]
4215
4340
  },
4216
4341
  strip: [.../* @__PURE__ */ new Set([...defaultSchema.strip || [], ...STRIPPED_TAGS])]
@@ -4246,6 +4371,49 @@ function sanitizeCrossChunkUrl(rawUrl, key, tagName, urlTransform, schema) {
4246
4371
  return String(transformed);
4247
4372
  }
4248
4373
 
4374
+ // src/components/resolveCrossChunkReference.ts
4375
+ import rehypeSanitize2 from "rehype-sanitize";
4376
+ import { normalizeUri as normalizeUri3 } from "micromark-util-sanitize-uri";
4377
+ function resolveCrossChunkReference(input, schema, urlTransform, clobberPrefix) {
4378
+ const key = input.tagName === "a" ? "href" : "src";
4379
+ const element = {
4380
+ type: "element",
4381
+ tagName: input.tagName,
4382
+ properties: {
4383
+ [key]: normalizeUri3(input.url),
4384
+ ...input.tagName === "img" ? { alt: input.alt ?? "" } : {},
4385
+ ...input.title !== void 0 ? { title: input.title } : {}
4386
+ },
4387
+ children: input.tagName === "a" ? [{ type: "text", value: "__reference_children__" }] : []
4388
+ };
4389
+ const requiredAncestors = schema.ancestors?.[input.tagName];
4390
+ const recordedAncestors = input.node?.data?.referenceAncestors;
4391
+ let finalSchema = schema;
4392
+ if (requiredAncestors && Array.isArray(recordedAncestors) && requiredAncestors.some((tag) => recordedAncestors.includes(tag))) {
4393
+ const ancestors = { ...schema.ancestors };
4394
+ delete ancestors[input.tagName];
4395
+ finalSchema = { ...schema, ancestors };
4396
+ }
4397
+ const root2 = rehypeSanitize2({ ...finalSchema, clobberPrefix })({ type: "root", children: [element] });
4398
+ const node = root2.children[0];
4399
+ if (node?.type !== "element") return { element: null, keepChildren: node?.type === "text" };
4400
+ if (node.tagName === "a" && typeof node.properties.href === "string") {
4401
+ node.properties.href = rebaseHashHref(node.properties.href, clobberPrefix);
4402
+ }
4403
+ node.children = input.node?.children ?? [];
4404
+ if (input.node?.position) node.position = input.node.position;
4405
+ buildTransform({
4406
+ allowedElements: void 0,
4407
+ disallowedElements: void 0,
4408
+ allowElement: void 0,
4409
+ skipHtml: void 0,
4410
+ unwrapDisallowed: void 0,
4411
+ urlTransform
4412
+ })(node, 0, root2);
4413
+ node.children = [];
4414
+ return { element: node, keepChildren: false };
4415
+ }
4416
+
4249
4417
  // src/plugins/defs.ts
4250
4418
  function getEnginePluginInternals(plugin) {
4251
4419
  const candidate = plugin;
@@ -4450,6 +4618,8 @@ var createSmoothStreamController = (options = {}) => {
4450
4618
  let source = "";
4451
4619
  let visibleEnd = 0;
4452
4620
  let pending = [];
4621
+ let pendingHead = 0;
4622
+ const pendingCount = () => pending.length - pendingHead;
4453
4623
  let tentativeEnd = 0;
4454
4624
  let finished = false;
4455
4625
  let seam;
@@ -4494,7 +4664,7 @@ var createSmoothStreamController = (options = {}) => {
4494
4664
  cancelFrame = void 0;
4495
4665
  };
4496
4666
  const ensureScheduled = () => {
4497
- if (disposed || cancelFrame || pending.length === 0) return;
4667
+ if (disposed || cancelFrame || pendingCount() === 0) return;
4498
4668
  lastTickAt = now();
4499
4669
  credit = 0;
4500
4670
  cancelFrame = schedule(tick);
@@ -4508,31 +4678,38 @@ var createSmoothStreamController = (options = {}) => {
4508
4678
  const params = resolveParams();
4509
4679
  let rate;
4510
4680
  if (finished && drainDeadlineAt !== void 0) {
4511
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, drainDeadlineAt - t));
4681
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, drainDeadlineAt - t));
4512
4682
  } else if (gapWindow.length > 0 && lastArrivalAt !== void 0) {
4513
4683
  const gaps = gapWindow.map((s) => s.gap).sort((a, b) => a - b);
4514
4684
  const intervalQ = gaps[Math.min(gaps.length - 1, Math.floor(gaps.length * INTERVAL_QUANTILE))];
4515
4685
  const horizon = Math.max(16, Math.min(params.bufferFactor * intervalQ + HORIZON_PAD_MS, params.maxLagMs));
4516
4686
  const deadline = Math.max(lastArrivalAt + horizon, t + DEADLINE_FLOOR_MS);
4517
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, deadline - t));
4687
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, deadline - t));
4518
4688
  } else {
4519
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, params.correctionTauMs));
4689
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, params.correctionTauMs));
4520
4690
  }
4521
4691
  credit += rate * dt / 1e3;
4522
- const reveal = Math.min(Math.floor(credit), pending.length);
4692
+ const reveal = Math.min(Math.floor(credit), pendingCount());
4523
4693
  if (reveal > 0) {
4524
- visibleEnd = pending[reveal - 1];
4525
- pending = pending.slice(reveal);
4526
- credit = pending.length > 0 ? credit - reveal : 0;
4694
+ visibleEnd = pending[pendingHead + reveal - 1];
4695
+ pendingHead += reveal;
4696
+ if (pendingHead === pending.length) {
4697
+ pending = [];
4698
+ pendingHead = 0;
4699
+ } else if (pendingHead >= 1024 && pendingHead * 2 >= pending.length) {
4700
+ pending = pending.slice(pendingHead);
4701
+ pendingHead = 0;
4702
+ }
4703
+ credit = pendingCount() > 0 ? credit - reveal : 0;
4527
4704
  notify();
4528
4705
  }
4529
- if (!disposed && !cancelFrame && pending.length > 0) cancelFrame = schedule(tick);
4706
+ if (!disposed && !cancelFrame && pendingCount() > 0) cancelFrame = schedule(tick);
4530
4707
  };
4531
4708
  const resegmentTail = () => {
4532
- if (seam !== void 0 && seam < source.length && pending[pending.length - 1] === seam) {
4709
+ if (pendingCount() > 0 && seam !== void 0 && seam < source.length && pending[pending.length - 1] === seam) {
4533
4710
  pending.pop();
4534
4711
  }
4535
- const from = pending.length > 0 ? pending[pending.length - 1] : visibleEnd;
4712
+ const from = pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd;
4536
4713
  let anchor = from;
4537
4714
  if (seam !== void 0 && from <= seam) {
4538
4715
  anchor = Math.max(0, from - RESUME_LOOKBACK);
@@ -4566,6 +4743,7 @@ var createSmoothStreamController = (options = {}) => {
4566
4743
  visibleEnd = next.length;
4567
4744
  tentativeEnd = next.length;
4568
4745
  pending = [];
4746
+ pendingHead = 0;
4569
4747
  seam = next.length;
4570
4748
  credit = 0;
4571
4749
  cancelScheduled();
@@ -4597,7 +4775,7 @@ var createSmoothStreamController = (options = {}) => {
4597
4775
  if (finished) return;
4598
4776
  finished = true;
4599
4777
  lastArrivalAt = void 0;
4600
- if (tentativeEnd > (pending.length > 0 ? pending[pending.length - 1] : visibleEnd)) {
4778
+ if (tentativeEnd > (pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd)) {
4601
4779
  pending.push(tentativeEnd);
4602
4780
  }
4603
4781
  const params = resolveParams();
@@ -4623,10 +4801,11 @@ var createSmoothStreamController = (options = {}) => {
4623
4801
  snap,
4624
4802
  flush() {
4625
4803
  disposed = false;
4626
- const target = finished ? source.length : pending.length > 0 ? pending[pending.length - 1] : visibleEnd;
4804
+ const target = finished ? source.length : pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd;
4627
4805
  if (target <= visibleEnd) return;
4628
4806
  visibleEnd = target;
4629
4807
  pending = [];
4808
+ pendingHead = 0;
4630
4809
  credit = 0;
4631
4810
  cancelScheduled();
4632
4811
  notify();
@@ -5476,6 +5655,7 @@ export {
5476
5655
  rehypeRebaseHashLinks_default as rehypeRebaseHashLinks,
5477
5656
  rehypeVerifyEngineTags,
5478
5657
  removeComments,
5658
+ resolveCrossChunkReference,
5479
5659
  sanitizeCrossChunkUrl,
5480
5660
  sanitizeSchema,
5481
5661
  shortenDocumentId,