@ai-react-markdown/engine 2.12.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;
1927
- }
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;
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);
1937
1867
  }
1938
- const rest = children.slice(idx);
1939
- if (remnant !== "") rest.unshift({ type: "text", value: remnant });
1940
- return { rest, remnantMerged };
1868
+ return { events, uninjectable, cacheable };
1941
1869
  }
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;
1951
- return null;
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;
1952
1890
  }
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;
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
+ });
1963
1900
  }
1901
+ text += source;
1902
+ line += countNewlines(source);
1964
1903
  }
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(">");
1904
+ return { text: `${text}
1905
+
1906
+ ${INJECTION_TERMINATOR}
1907
+
1908
+ `, segments };
1972
1909
  }
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;
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);
1980
1923
  }
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;
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;
1984
1962
  }
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;
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);
2011
1977
  }
2012
- return count;
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) {
1996
+ return null;
1997
+ }
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
+ }
2011
+ }
2012
+ firstTailChild = false;
2013
+ hastChildren.push(child);
2014
+ }
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
@@ -2461,7 +2473,8 @@ function buildRegistryIndex(registry) {
2461
2473
  links: /* @__PURE__ */ new Map(),
2462
2474
  numbers: /* @__PURE__ */ new Map(),
2463
2475
  counts: /* @__PURE__ */ new Map(),
2464
- occurrences: /* @__PURE__ */ new Map()
2476
+ occurrences: /* @__PURE__ */ new Map(),
2477
+ labelOccurrences: /* @__PURE__ */ new Map()
2465
2478
  };
2466
2479
  for (const sym of registry.chunkOrder) {
2467
2480
  const data = registry.chunkData.get(sym);
@@ -2478,12 +2491,78 @@ function buildRegistryIndex(registry) {
2478
2491
  index.counts.set(label, total);
2479
2492
  const prior = local.get(label);
2480
2493
  if (prior) prior.count++;
2481
- else local.set(label, { start: total, count: 1 });
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
+ }
2482
2501
  }
2483
2502
  }
2484
2503
  return index;
2485
2504
  }
2486
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
+
2487
2566
  // src/components/documentRegistry.ts
2488
2567
  function createRegistry(onEmpty) {
2489
2568
  let index;
@@ -2495,6 +2574,7 @@ function createRegistry(onEmpty) {
2495
2574
  }
2496
2575
  return index;
2497
2576
  };
2577
+ const labels = createRegistrySubscriptions(() => ({ registry: reg, index: getIndex() }));
2498
2578
  const reg = {
2499
2579
  chunkOrder: [],
2500
2580
  chunkData: /* @__PURE__ */ new Map(),
@@ -2645,6 +2725,9 @@ function createRegistry(onEmpty) {
2645
2725
  this._subscribers.delete(cb);
2646
2726
  };
2647
2727
  },
2728
+ subscribeLabel(kind, label, cb) {
2729
+ return labels.subscribe(kind, label, cb);
2730
+ },
2648
2731
  canonicalFootnoteFor(label) {
2649
2732
  return getIndex().footnotes.get(normalizeId(label)) ?? null;
2650
2733
  },
@@ -2672,7 +2755,9 @@ function createRegistry(onEmpty) {
2672
2755
  this._notifyScheduled = true;
2673
2756
  queueMicrotask(() => {
2674
2757
  this._notifyScheduled = false;
2758
+ const labelCallbacks = labels.collect();
2675
2759
  for (const cb of [...this._subscribers]) cb();
2760
+ for (const cb of labelCallbacks) cb();
2676
2761
  });
2677
2762
  }
2678
2763
  };