@ai-react-markdown/engine 2.10.1 → 2.12.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.dev.js CHANGED
@@ -2138,6 +2138,38 @@ function normalizeForMatch(s) {
2138
2138
  return normalizeIdentifier3(s);
2139
2139
  }
2140
2140
 
2141
+ // src/components/blankLineScanner.ts
2142
+ function createBlankLineScanner() {
2143
+ let end = 0;
2144
+ let newline = false;
2145
+ let cr = false;
2146
+ return (source, from = 0) => {
2147
+ if (from === 0) {
2148
+ end = 0;
2149
+ newline = false;
2150
+ cr = false;
2151
+ }
2152
+ for (let i = from; i < source.length; i++) {
2153
+ const c = source[i];
2154
+ if (c === "\n") {
2155
+ if (newline) {
2156
+ end = i + 1;
2157
+ newline = false;
2158
+ } else newline = true;
2159
+ cr = false;
2160
+ } else if (newline) {
2161
+ if ((c === " " || c === " ") && !cr) continue;
2162
+ if (c === "\r" && !cr) cr = true;
2163
+ else {
2164
+ newline = false;
2165
+ cr = false;
2166
+ }
2167
+ }
2168
+ }
2169
+ return end;
2170
+ };
2171
+ }
2172
+
2141
2173
  // src/components/collectDefLabels.ts
2142
2174
  var SCANNER_BOUNDARY_PROFILE = { defListEnabled: false, mathFlow: false, referenceTaint: false };
2143
2175
  function buildProcessor() {
@@ -2169,19 +2201,12 @@ var setsEqual = (a, b) => {
2169
2201
  for (const v of a) if (!b.has(v)) return false;
2170
2202
  return true;
2171
2203
  };
2172
- var BLANK_LINE_RE = /\r?\n[ \t]*\r?\n/g;
2173
- function lastRegionStart(source) {
2174
- BLANK_LINE_RE.lastIndex = 0;
2175
- let start = 0;
2176
- for (let m = BLANK_LINE_RE.exec(source); m !== null; m = BLANK_LINE_RE.exec(source)) {
2177
- start = m.index + m[0].length;
2178
- }
2179
- return start;
2180
- }
2181
2204
  var DEF_LINE_START_RE = /^[ \t>*+\d.)-]*\[(?:[^\]\\]|\\[\s\S])*\]:/m;
2182
2205
  function createDefLabelScanner(parse = collectDefLabels) {
2183
2206
  let prevSource = null;
2184
2207
  let prevLabels = null;
2208
+ const scanBlankLines = createBlankLineScanner();
2209
+ let regionStart = 0;
2185
2210
  let frozenEnd = 0;
2186
2211
  let frozenFootnotes = /* @__PURE__ */ new Set();
2187
2212
  let frozenLinks = /* @__PURE__ */ new Set();
@@ -2194,13 +2219,16 @@ function createDefLabelScanner(parse = collectDefLabels) {
2194
2219
  };
2195
2220
  return {
2196
2221
  scan(source) {
2222
+ if (source === prevSource && prevLabels !== null) return prevLabels;
2223
+ const previousRegionStart = regionStart;
2224
+ const appended = prevSource !== null && source.startsWith(prevSource);
2225
+ regionStart = scanBlankLines(source, appended ? prevSource.length : 0);
2197
2226
  let isAppend = false;
2198
2227
  if (prevSource !== null && prevLabels !== null) {
2199
2228
  if (source === prevSource) return prevLabels;
2200
2229
  if (source.startsWith(prevSource)) {
2201
2230
  isAppend = true;
2202
- const regionStart = lastRegionStart(prevSource);
2203
- const region = prevSource.slice(regionStart) + source.slice(prevSource.length);
2231
+ const region = source.slice(previousRegionStart);
2204
2232
  if (!DEF_LINE_START_RE.test(region)) {
2205
2233
  prevSource = source;
2206
2234
  return prevLabels;
@@ -2433,8 +2461,47 @@ function* extractContributions(mdast, options = {}) {
2433
2461
  for (const c of out) yield c;
2434
2462
  }
2435
2463
 
2464
+ // src/components/registryIndex.ts
2465
+ function buildRegistryIndex(registry) {
2466
+ const index = {
2467
+ footnotes: /* @__PURE__ */ new Map(),
2468
+ links: /* @__PURE__ */ new Map(),
2469
+ numbers: /* @__PURE__ */ new Map(),
2470
+ counts: /* @__PURE__ */ new Map(),
2471
+ occurrences: /* @__PURE__ */ new Map()
2472
+ };
2473
+ for (const sym of registry.chunkOrder) {
2474
+ const data = registry.chunkData.get(sym);
2475
+ if (!data) continue;
2476
+ for (const label of data.defs.keys()) if (!index.footnotes.has(label)) index.footnotes.set(label, sym);
2477
+ for (const label of data.linkDefs.keys()) if (!index.links.has(label)) index.links.set(label, sym);
2478
+ const local = /* @__PURE__ */ new Map();
2479
+ index.occurrences.set(sym, local);
2480
+ for (const ref of data.refs) {
2481
+ if (ref.kind !== "footnote") continue;
2482
+ const label = ref.label;
2483
+ if (!index.numbers.has(label)) index.numbers.set(label, index.numbers.size + 1);
2484
+ const total = (index.counts.get(label) ?? 0) + 1;
2485
+ index.counts.set(label, total);
2486
+ const prior = local.get(label);
2487
+ if (prior) prior.count++;
2488
+ else local.set(label, { start: total, count: 1 });
2489
+ }
2490
+ }
2491
+ return index;
2492
+ }
2493
+
2436
2494
  // src/components/documentRegistry.ts
2437
2495
  function createRegistry(onEmpty) {
2496
+ let index;
2497
+ let indexedVersion = -1;
2498
+ const getIndex = () => {
2499
+ if (!index || indexedVersion !== reg.version) {
2500
+ index = buildRegistryIndex(reg);
2501
+ indexedVersion = reg.version;
2502
+ }
2503
+ return index;
2504
+ };
2438
2505
  const reg = {
2439
2506
  chunkOrder: [],
2440
2507
  chunkData: /* @__PURE__ */ new Map(),
@@ -2586,72 +2653,25 @@ function createRegistry(onEmpty) {
2586
2653
  };
2587
2654
  },
2588
2655
  canonicalFootnoteFor(label) {
2589
- const id = normalizeId(label);
2590
- for (const sym of this.chunkOrder) {
2591
- const data = this.chunkData.get(sym);
2592
- if (data?.defs.has(id)) return sym;
2593
- }
2594
- return null;
2656
+ return getIndex().footnotes.get(normalizeId(label)) ?? null;
2595
2657
  },
2596
2658
  canonicalLinkFor(label) {
2597
- const id = normalizeId(label);
2598
- for (const sym of this.chunkOrder) {
2599
- const data = this.chunkData.get(sym);
2600
- if (data?.linkDefs.has(id)) return sym;
2601
- }
2602
- return null;
2659
+ return getIndex().links.get(normalizeId(label)) ?? null;
2603
2660
  },
2604
2661
  globalNumber(label) {
2605
- const id = normalizeId(label);
2606
- let n = 0;
2607
- const seen = /* @__PURE__ */ new Set();
2608
- for (const sym of this.chunkOrder) {
2609
- const data = this.chunkData.get(sym);
2610
- if (!data) continue;
2611
- for (const ref of data.refs) {
2612
- if (ref.kind !== "footnote") continue;
2613
- if (!seen.has(ref.label)) {
2614
- seen.add(ref.label);
2615
- n++;
2616
- if (ref.label === id) return n;
2617
- }
2618
- }
2619
- }
2620
- return null;
2662
+ return getIndex().numbers.get(normalizeId(label)) ?? null;
2621
2663
  },
2622
2664
  resolveLinkDef(label) {
2623
- const sym = this.canonicalLinkFor(label);
2624
- if (!sym) return null;
2625
- return this.chunkData.get(sym)?.linkDefs.get(normalizeId(label)) ?? null;
2665
+ const id = normalizeId(label);
2666
+ const sym = getIndex().links.get(id);
2667
+ return sym ? this.chunkData.get(sym)?.linkDefs.get(id) ?? null : null;
2626
2668
  },
2627
2669
  getRefsForLabel(label) {
2628
- const id = normalizeId(label);
2629
- let n = 0;
2630
- for (const sym of this.chunkOrder) {
2631
- const data = this.chunkData.get(sym);
2632
- if (!data) continue;
2633
- for (const ref of data.refs) {
2634
- if (ref.kind === "footnote" && ref.label === id) n++;
2635
- }
2636
- }
2637
- return n;
2670
+ return getIndex().counts.get(normalizeId(label)) ?? 0;
2638
2671
  },
2639
2672
  globalOccurrenceForRef(chunkSym, label, localOccurrence) {
2640
- const id = normalizeId(label);
2641
- let global2 = 0;
2642
- for (const sym of this.chunkOrder) {
2643
- const data = this.chunkData.get(sym);
2644
- if (!data) continue;
2645
- let localCount = 0;
2646
- for (const ref of data.refs) {
2647
- if (ref.kind !== "footnote") continue;
2648
- if (ref.label !== id) continue;
2649
- localCount++;
2650
- global2++;
2651
- if (sym === chunkSym && localCount === localOccurrence) return global2;
2652
- }
2653
- }
2654
- return null;
2673
+ const range = getIndex().occurrences.get(chunkSym)?.get(normalizeId(label));
2674
+ return range && Number.isInteger(localOccurrence) && localOccurrence > 0 && localOccurrence <= range.count ? range.start + localOccurrence - 1 : null;
2655
2675
  },
2656
2676
  _notify() {
2657
2677
  this.version++;
@@ -2708,6 +2728,55 @@ function rehypeUnwrapCrossChunkImages() {
2708
2728
 
2709
2729
  // src/components/pluginChain.ts
2710
2730
  import rehypeSanitize from "rehype-sanitize";
2731
+
2732
+ // src/components/rehypeVerifyEngineTags.ts
2733
+ var ENGINE_PLACEHOLDER_TAGS = /* @__PURE__ */ new Set([
2734
+ "footnote-sup",
2735
+ "cross-chunk-link",
2736
+ "cross-chunk-image"
2737
+ ]);
2738
+ var ENGINE_PROVENANCE_PROPERTY = "engineProvenance";
2739
+ function walk(parent, provenance, ancestors) {
2740
+ const children = parent.children;
2741
+ let i = 0;
2742
+ while (i < children.length) {
2743
+ const node = children[i];
2744
+ if (node.type === "element" && ENGINE_PLACEHOLDER_TAGS.has(node.tagName)) {
2745
+ const props = node.properties ?? {};
2746
+ const stamped = props[ENGINE_PROVENANCE_PROPERTY];
2747
+ const genuine = provenance !== "" && typeof stamped === "string" && stamped === provenance;
2748
+ if (genuine) {
2749
+ delete props[ENGINE_PROVENANCE_PROPERTY];
2750
+ if (ancestors && (node.tagName === "cross-chunk-link" || node.tagName === "cross-chunk-image")) {
2751
+ (node.data ??= {}).referenceAncestors = ancestors.slice();
2752
+ }
2753
+ ancestors?.push(
2754
+ node.tagName === "cross-chunk-link" ? "a" : node.tagName === "cross-chunk-image" ? "img" : node.tagName
2755
+ );
2756
+ walk(node, provenance, ancestors);
2757
+ ancestors?.pop();
2758
+ i += 1;
2759
+ } else {
2760
+ children.splice(i, 1, ...node.children);
2761
+ }
2762
+ continue;
2763
+ }
2764
+ if (node.type === "element") {
2765
+ ancestors?.push(node.tagName);
2766
+ walk(node, provenance, ancestors);
2767
+ ancestors?.pop();
2768
+ }
2769
+ i += 1;
2770
+ }
2771
+ }
2772
+ function rehypeVerifyEngineTags(options) {
2773
+ const provenance = options?.provenance ?? "";
2774
+ return function transformer(tree) {
2775
+ walk(tree, provenance, options?.referenceAncestors ? [] : void 0);
2776
+ };
2777
+ }
2778
+
2779
+ // src/components/pluginChain.ts
2711
2780
  import remarkBreaks from "remark-breaks";
2712
2781
  import remarkCjkFriendly from "remark-cjk-friendly";
2713
2782
  import remarkCjkFriendlyGfmStrikethrough from "remark-cjk-friendly-gfm-strikethrough";
@@ -2724,16 +2793,18 @@ import remarkRemoveComments from "remark-remove-comments";
2724
2793
  // src/components/rehypeRebaseHashLinks.ts
2725
2794
  import { visit as visit5 } from "unist-util-visit";
2726
2795
  var DEFAULT_PREFIX = "user-content-";
2796
+ function rebaseHashHref(href, prefix) {
2797
+ const hashPrefix = "#" + prefix;
2798
+ return href.startsWith("#") && !href.startsWith(hashPrefix) ? hashPrefix + href.slice(1) : href;
2799
+ }
2727
2800
  var rehypeRebaseHashLinks = (options) => {
2728
2801
  const prefix = options?.prefix ?? DEFAULT_PREFIX;
2729
- const hashPrefix = "#" + prefix;
2730
2802
  return (tree) => {
2731
2803
  visit5(tree, "element", (node) => {
2732
2804
  if (node.tagName !== "a") return;
2733
2805
  const href = node.properties?.href;
2734
2806
  if (typeof href !== "string" || !href.startsWith("#")) return;
2735
- if (href.startsWith(hashPrefix)) return;
2736
- node.properties.href = hashPrefix + href.slice(1);
2807
+ node.properties.href = rebaseHashHref(href, prefix);
2737
2808
  });
2738
2809
  };
2739
2810
  };
@@ -2813,10 +2884,21 @@ function buildCoreRemarkPlugins(enginePlugins) {
2813
2884
  ...DISPLAY_OPTIMIZE_CHAIN.filter(([name]) => selected.has(name)).map(([, plugin]) => plugin)
2814
2885
  ];
2815
2886
  }
2816
- function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix) {
2887
+ function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix, options) {
2817
2888
  return [
2818
2889
  // Allow raw HTML through so rehype-sanitize can handle it.
2819
2890
  [rehypeRaw, { passThrough: [] }],
2891
+ // Unwrap forged engine placeholders BEFORE sanitize admits their tag
2892
+ // names. Only when the caller holds a credential (see the option's doc).
2893
+ ...options ? [
2894
+ [
2895
+ rehypeVerifyEngineTags,
2896
+ {
2897
+ provenance: options.provenance,
2898
+ ...sanitizeSchema2.ancestors?.a || sanitizeSchema2.ancestors?.img ? { referenceAncestors: true } : {}
2899
+ }
2900
+ ]
2901
+ ] : [],
2820
2902
  // Sanitize HTML while allowing <mark> (highlight), KaTeX class names,
2821
2903
  // and any extra protocols the caller permitted via the `sanitizeSchema`
2822
2904
  // prop. Override `clobberPrefix` with the instance-scoped value — the
@@ -2856,6 +2938,10 @@ function buildCoreRemarkRehypeOptions(enableDefinitionList) {
2856
2938
  }
2857
2939
 
2858
2940
  // src/components/customMdastHandlers.ts
2941
+ function provenanceProps(s) {
2942
+ const provenance = s.options.provenance;
2943
+ return typeof provenance === "string" ? { engineProvenance: provenance } : {};
2944
+ }
2859
2945
  function localDefProps(s, id) {
2860
2946
  const def = s.definitionById.get(id);
2861
2947
  if (!def || typeof def.url !== "string" || def.url === SENTINEL_LINK_URL) return {};
@@ -2886,15 +2972,14 @@ function buildCrossChunkHandlers() {
2886
2972
  type: "element",
2887
2973
  tagName: "cross-chunk-link",
2888
2974
  properties: {
2889
- // `label` is the ORIGINAL source text (mdast's `label` field), NOT
2890
- // the normalized `identifier`. The placeholder uses it to construct
2891
- // hrefs that line up with mdast-util-to-hast's default `<li id>`
2892
- // which also preserves source case. Registry lookups normalize
2893
- // internally, so cross-chunk case-insensitive matching still works.
2975
+ // Display labels decode escapes; registry identifiers must retain
2976
+ // those bytes. Never use the display label as the lookup key.
2977
+ identifier: node.identifier,
2894
2978
  label: node.label ?? node.identifier,
2895
2979
  referenceType: node.referenceType,
2896
2980
  documentId: s.options.documentId,
2897
- ...localDefProps(s, id)
2981
+ ...localDefProps(s, id),
2982
+ ...provenanceProps(s)
2898
2983
  },
2899
2984
  children: s.all(node)
2900
2985
  };
@@ -2908,11 +2993,13 @@ function buildCrossChunkHandlers() {
2908
2993
  type: "element",
2909
2994
  tagName: "cross-chunk-image",
2910
2995
  properties: {
2996
+ identifier: node.identifier,
2911
2997
  label: node.label ?? node.identifier,
2912
2998
  referenceType: node.referenceType,
2913
2999
  alt: node.alt ?? "",
2914
3000
  documentId: s.options.documentId,
2915
- ...localDefProps(s, id)
3001
+ ...localDefProps(s, id),
3002
+ ...provenanceProps(s)
2916
3003
  },
2917
3004
  children: []
2918
3005
  };
@@ -2929,7 +3016,8 @@ function buildCrossChunkHandlers() {
2929
3016
  properties: {
2930
3017
  label: node.identifier,
2931
3018
  localOccurrence,
2932
- documentId: s.options.documentId
3019
+ documentId: s.options.documentId,
3020
+ ...provenanceProps(s)
2933
3021
  },
2934
3022
  children: []
2935
3023
  };
@@ -2947,7 +3035,8 @@ function buildCrossChunkHandlers() {
2947
3035
  // first client frame), where the local synthetic footer is what
2948
3036
  // renders, so marks and footer agree (core-render-02).
2949
3037
  localNumber: s.footnoteOrder.indexOf(id) + 1,
2950
- documentId: s.options.documentId
3038
+ documentId: s.options.documentId,
3039
+ ...provenanceProps(s)
2951
3040
  },
2952
3041
  children: []
2953
3042
  };
@@ -4167,8 +4256,8 @@ var sanitizeSchema = deepFreeze(
4167
4256
  attributes: {
4168
4257
  ...defaultSchema.attributes,
4169
4258
  code: mergeClassNameAllowlist(defaultSchema.attributes?.code, ["math-inline", "math-display"]),
4170
- "cross-chunk-link": ["label", "referenceType", "documentId", "localUrl", "localTitle"],
4171
- "cross-chunk-image": ["label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
4259
+ "cross-chunk-link": ["identifier", "label", "referenceType", "documentId", "localUrl", "localTitle"],
4260
+ "cross-chunk-image": ["identifier", "label", "referenceType", "documentId", "alt", "localUrl", "localTitle"],
4172
4261
  "footnote-sup": ["label", "localOccurrence", "localNumber", "documentId"]
4173
4262
  },
4174
4263
  strip: [.../* @__PURE__ */ new Set([...defaultSchema.strip || [], ...STRIPPED_TAGS])]
@@ -4204,6 +4293,49 @@ function sanitizeCrossChunkUrl(rawUrl, key, tagName, urlTransform, schema) {
4204
4293
  return String(transformed);
4205
4294
  }
4206
4295
 
4296
+ // src/components/resolveCrossChunkReference.ts
4297
+ import rehypeSanitize2 from "rehype-sanitize";
4298
+ import { normalizeUri as normalizeUri3 } from "micromark-util-sanitize-uri";
4299
+ function resolveCrossChunkReference(input, schema, urlTransform, clobberPrefix) {
4300
+ const key = input.tagName === "a" ? "href" : "src";
4301
+ const element = {
4302
+ type: "element",
4303
+ tagName: input.tagName,
4304
+ properties: {
4305
+ [key]: normalizeUri3(input.url),
4306
+ ...input.tagName === "img" ? { alt: input.alt ?? "" } : {},
4307
+ ...input.title !== void 0 ? { title: input.title } : {}
4308
+ },
4309
+ children: input.tagName === "a" ? [{ type: "text", value: "__reference_children__" }] : []
4310
+ };
4311
+ const requiredAncestors = schema.ancestors?.[input.tagName];
4312
+ const recordedAncestors = input.node?.data?.referenceAncestors;
4313
+ let finalSchema = schema;
4314
+ if (requiredAncestors && Array.isArray(recordedAncestors) && requiredAncestors.some((tag) => recordedAncestors.includes(tag))) {
4315
+ const ancestors = { ...schema.ancestors };
4316
+ delete ancestors[input.tagName];
4317
+ finalSchema = { ...schema, ancestors };
4318
+ }
4319
+ const root2 = rehypeSanitize2({ ...finalSchema, clobberPrefix })({ type: "root", children: [element] });
4320
+ const node = root2.children[0];
4321
+ if (node?.type !== "element") return { element: null, keepChildren: node?.type === "text" };
4322
+ if (node.tagName === "a" && typeof node.properties.href === "string") {
4323
+ node.properties.href = rebaseHashHref(node.properties.href, clobberPrefix);
4324
+ }
4325
+ node.children = input.node?.children ?? [];
4326
+ if (input.node?.position) node.position = input.node.position;
4327
+ buildTransform({
4328
+ allowedElements: void 0,
4329
+ disallowedElements: void 0,
4330
+ allowElement: void 0,
4331
+ skipHtml: void 0,
4332
+ unwrapDisallowed: void 0,
4333
+ urlTransform
4334
+ })(node, 0, root2);
4335
+ node.children = [];
4336
+ return { element: node, keepChildren: false };
4337
+ }
4338
+
4207
4339
  // src/plugins/defs.ts
4208
4340
  function getEnginePluginInternals(plugin) {
4209
4341
  const candidate = plugin;
@@ -4420,6 +4552,8 @@ var createSmoothStreamController = (options = {}) => {
4420
4552
  let source = "";
4421
4553
  let visibleEnd = 0;
4422
4554
  let pending = [];
4555
+ let pendingHead = 0;
4556
+ const pendingCount = () => pending.length - pendingHead;
4423
4557
  let tentativeEnd = 0;
4424
4558
  let finished = false;
4425
4559
  let seam;
@@ -4464,7 +4598,7 @@ var createSmoothStreamController = (options = {}) => {
4464
4598
  cancelFrame = void 0;
4465
4599
  };
4466
4600
  const ensureScheduled = () => {
4467
- if (disposed || cancelFrame || pending.length === 0) return;
4601
+ if (disposed || cancelFrame || pendingCount() === 0) return;
4468
4602
  lastTickAt = now();
4469
4603
  credit = 0;
4470
4604
  cancelFrame = schedule(tick);
@@ -4478,31 +4612,38 @@ var createSmoothStreamController = (options = {}) => {
4478
4612
  const params = resolveParams();
4479
4613
  let rate;
4480
4614
  if (finished && drainDeadlineAt !== void 0) {
4481
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, drainDeadlineAt - t));
4615
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, drainDeadlineAt - t));
4482
4616
  } else if (gapWindow.length > 0 && lastArrivalAt !== void 0) {
4483
4617
  const gaps = gapWindow.map((s) => s.gap).sort((a, b) => a - b);
4484
4618
  const intervalQ = gaps[Math.min(gaps.length - 1, Math.floor(gaps.length * INTERVAL_QUANTILE))];
4485
4619
  const horizon = Math.max(16, Math.min(params.bufferFactor * intervalQ + HORIZON_PAD_MS, params.maxLagMs));
4486
4620
  const deadline = Math.max(lastArrivalAt + horizon, t + DEADLINE_FLOOR_MS);
4487
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, deadline - t));
4621
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, deadline - t));
4488
4622
  } else {
4489
- rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, params.correctionTauMs));
4623
+ rate = Math.max(params.minCharsPerSecond, pendingCount() * 1e3 / Math.max(1, params.correctionTauMs));
4490
4624
  }
4491
4625
  credit += rate * dt / 1e3;
4492
- const reveal = Math.min(Math.floor(credit), pending.length);
4626
+ const reveal = Math.min(Math.floor(credit), pendingCount());
4493
4627
  if (reveal > 0) {
4494
- visibleEnd = pending[reveal - 1];
4495
- pending = pending.slice(reveal);
4496
- credit = pending.length > 0 ? credit - reveal : 0;
4628
+ visibleEnd = pending[pendingHead + reveal - 1];
4629
+ pendingHead += reveal;
4630
+ if (pendingHead === pending.length) {
4631
+ pending = [];
4632
+ pendingHead = 0;
4633
+ } else if (pendingHead >= 1024 && pendingHead * 2 >= pending.length) {
4634
+ pending = pending.slice(pendingHead);
4635
+ pendingHead = 0;
4636
+ }
4637
+ credit = pendingCount() > 0 ? credit - reveal : 0;
4497
4638
  notify();
4498
4639
  }
4499
- if (!disposed && !cancelFrame && pending.length > 0) cancelFrame = schedule(tick);
4640
+ if (!disposed && !cancelFrame && pendingCount() > 0) cancelFrame = schedule(tick);
4500
4641
  };
4501
4642
  const resegmentTail = () => {
4502
- if (seam !== void 0 && seam < source.length && pending[pending.length - 1] === seam) {
4643
+ if (pendingCount() > 0 && seam !== void 0 && seam < source.length && pending[pending.length - 1] === seam) {
4503
4644
  pending.pop();
4504
4645
  }
4505
- const from = pending.length > 0 ? pending[pending.length - 1] : visibleEnd;
4646
+ const from = pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd;
4506
4647
  let anchor = from;
4507
4648
  if (seam !== void 0 && from <= seam) {
4508
4649
  anchor = Math.max(0, from - RESUME_LOOKBACK);
@@ -4536,6 +4677,7 @@ var createSmoothStreamController = (options = {}) => {
4536
4677
  visibleEnd = next.length;
4537
4678
  tentativeEnd = next.length;
4538
4679
  pending = [];
4680
+ pendingHead = 0;
4539
4681
  seam = next.length;
4540
4682
  credit = 0;
4541
4683
  cancelScheduled();
@@ -4567,7 +4709,7 @@ var createSmoothStreamController = (options = {}) => {
4567
4709
  if (finished) return;
4568
4710
  finished = true;
4569
4711
  lastArrivalAt = void 0;
4570
- if (tentativeEnd > (pending.length > 0 ? pending[pending.length - 1] : visibleEnd)) {
4712
+ if (tentativeEnd > (pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd)) {
4571
4713
  pending.push(tentativeEnd);
4572
4714
  }
4573
4715
  const params = resolveParams();
@@ -4593,10 +4735,11 @@ var createSmoothStreamController = (options = {}) => {
4593
4735
  snap,
4594
4736
  flush() {
4595
4737
  disposed = false;
4596
- const target = finished ? source.length : pending.length > 0 ? pending[pending.length - 1] : visibleEnd;
4738
+ const target = finished ? source.length : pendingCount() > 0 ? pending[pending.length - 1] : visibleEnd;
4597
4739
  if (target <= visibleEnd) return;
4598
4740
  visibleEnd = target;
4599
4741
  pending = [];
4742
+ pendingHead = 0;
4600
4743
  credit = 0;
4601
4744
  cancelScheduled();
4602
4745
  notify();
@@ -4619,6 +4762,12 @@ var createSmoothStreamController = (options = {}) => {
4619
4762
  };
4620
4763
 
4621
4764
  // src/preprocessors/latex.ts
4765
+ function isHardBoundary(kind) {
4766
+ return kind === "code" || kind === "literal" || kind === "multilineTag";
4767
+ }
4768
+ function hasLineEnding(text) {
4769
+ return text.includes("\n") || text.includes("\r");
4770
+ }
4622
4771
  function getRepeatedMarkerLength(content, start, marker) {
4623
4772
  let end = start;
4624
4773
  while (end < content.length && content[end] === marker) {
@@ -4686,11 +4835,11 @@ function splitByProtectedRegions(content) {
4686
4835
  let multilineFenceMarker = null;
4687
4836
  let multilineFenceLength = 0;
4688
4837
  let multilineFenceIndent = 0;
4689
- function pushProtected(start, end) {
4838
+ function pushProtected(start, end, kind) {
4690
4839
  if (start > lastIndex) {
4691
- segments.push({ text: content.substring(lastIndex, start), isCode: false });
4840
+ segments.push({ kind: "text", text: content.substring(lastIndex, start) });
4692
4841
  }
4693
- segments.push({ text: content.substring(start, end), isCode: true });
4842
+ segments.push({ kind, text: content.substring(start, end) });
4694
4843
  lastIndex = end;
4695
4844
  }
4696
4845
  let i = 0;
@@ -4701,7 +4850,7 @@ function splitByProtectedRegions(content) {
4701
4850
  const runLen = getRepeatedMarkerLength(content, i, multilineFenceMarker);
4702
4851
  const closerIndent = lineIndentBefore(content, i);
4703
4852
  if (runLen >= multilineFenceLength && closerIndent !== -1 && closerIndent <= multilineFenceIndent + 3 && restOfLineIsBlank(content, i + runLen)) {
4704
- pushProtected(multilineStart, i + runLen);
4853
+ pushProtected(multilineStart, i + runLen, "code");
4705
4854
  multilineStart = -1;
4706
4855
  multilineFenceMarker = null;
4707
4856
  multilineFenceLength = 0;
@@ -4728,7 +4877,7 @@ function splitByProtectedRegions(content) {
4728
4877
  if (char === "`") {
4729
4878
  const closeIdx = findClosingBacktickRun(content, i + runLen, runLen);
4730
4879
  if (closeIdx !== -1) {
4731
- pushProtected(i, closeIdx + runLen);
4880
+ pushProtected(i, closeIdx + runLen, "code");
4732
4881
  i = closeIdx + runLen;
4733
4882
  continue;
4734
4883
  }
@@ -4753,7 +4902,11 @@ function splitByProtectedRegions(content) {
4753
4902
  endIndex = content.length;
4754
4903
  }
4755
4904
  }
4756
- pushProtected(i, endIndex);
4905
+ pushProtected(
4906
+ i,
4907
+ endIndex,
4908
+ isOpeningPairedTag ? "literal" : hasLineEnding(content.substring(i, endIndex)) ? "multilineTag" : "tag"
4909
+ );
4757
4910
  i = endIndex;
4758
4911
  continue;
4759
4912
  }
@@ -4761,10 +4914,10 @@ function splitByProtectedRegions(content) {
4761
4914
  i += 1;
4762
4915
  }
4763
4916
  if (multilineStart !== -1) {
4764
- pushProtected(multilineStart, content.length);
4917
+ pushProtected(multilineStart, content.length, "code");
4765
4918
  }
4766
4919
  if (lastIndex < content.length) {
4767
- segments.push({ text: content.substring(lastIndex), isCode: false });
4920
+ segments.push({ kind: "text", text: content.substring(lastIndex) });
4768
4921
  }
4769
4922
  return segments;
4770
4923
  }
@@ -4918,20 +5071,22 @@ function escapeLatexPipesInUnclosed(text) {
4918
5071
  const tail = text.substring(unclosedStart + delimLen);
4919
5072
  return before + delim + replaceUnescapedPipes(tail);
4920
5073
  }
4921
- function opensMathFlow(text, pos) {
5074
+ function opensMathFlow(text, pos, runStartsAtLineStart) {
4922
5075
  let i = pos;
4923
5076
  let spaces = 0;
4924
- while (i > 0 && text[i - 1] !== "\n") {
5077
+ while (i > 0) {
5078
+ const prev = text[i - 1];
5079
+ if (prev === "\n" || prev === "\r") return true;
4925
5080
  i -= 1;
4926
5081
  if (text[i] !== " ") return false;
4927
5082
  spaces += 1;
4928
5083
  if (spaces > 3) return false;
4929
5084
  }
4930
- return true;
5085
+ return runStartsAtLineStart;
4931
5086
  }
4932
- function truncateUnclosedLatexBlock(text, unclosedStart = findUnclosedDelimiterStart(text, "double-only")) {
5087
+ function truncateUnclosedLatexBlock(text, runStartsAtLineStart, unclosedStart = findUnclosedDelimiterStart(text, "double-only")) {
4933
5088
  if (unclosedStart === -1) return text;
4934
- if (!opensMathFlow(text, unclosedStart)) return text;
5089
+ if (!opensMathFlow(text, unclosedStart, runStartsAtLineStart)) return text;
4935
5090
  return text.substring(0, unclosedStart).trimEnd();
4936
5091
  }
4937
5092
  function escapeTextUnderscores(text) {
@@ -4976,7 +5131,8 @@ function convertSingleToDoubleDollar(text) {
4976
5131
  }
4977
5132
  function preprocessLaTeX(str) {
4978
5133
  if (!hasLatexTrigger(str)) return str;
4979
- return processSlice(str, false).out;
5134
+ const mask = selectMask(str);
5135
+ return (mask === null ? processSlice(str, { legacy: true, probe: false }) : processSlice(str, { probe: false, mask })).out;
4980
5136
  }
4981
5137
  function hasLatexTrigger(str) {
4982
5138
  return str.includes("$") || str.includes("\\[") || str.includes("\\(");
@@ -5005,42 +5161,240 @@ function hasUnclosedTextCommand(text) {
5005
5161
  }
5006
5162
  var RESIDUAL_OPEN_BRACKET_RE = /(?<!!)\\\[/;
5007
5163
  var LEADING_DOUBLE_DOLLAR_RE = /^\s*\$\$/;
5008
- function processSlice(slice, probe = true) {
5164
+ var PUA_START = 57344;
5165
+ var PUA_END = 63743;
5166
+ var PUA_SIZE = PUA_END - PUA_START + 1;
5167
+ function selectMask(source) {
5168
+ const first = String.fromCharCode(PUA_START);
5169
+ if (source.indexOf(first) === -1) return first;
5170
+ const seen = new Uint8Array(PUA_SIZE);
5171
+ for (let i = 0; i < source.length; i++) {
5172
+ const c = source.charCodeAt(i);
5173
+ if (c >= PUA_START && c <= PUA_END) seen[c - PUA_START] = 1;
5174
+ }
5175
+ for (let k = 0; k < PUA_SIZE; k++) if (seen[k] === 0) return String.fromCharCode(PUA_START + k);
5176
+ return null;
5177
+ }
5178
+ var PuaPresence = class {
5179
+ bits = new Uint8Array(PUA_SIZE);
5180
+ distinct = 0;
5181
+ reset() {
5182
+ this.bits.fill(0);
5183
+ this.distinct = 0;
5184
+ }
5185
+ add(text, from) {
5186
+ for (let i = from; i < text.length; i++) {
5187
+ const c = text.charCodeAt(i);
5188
+ if (c >= PUA_START && c <= PUA_END) {
5189
+ const k = c - PUA_START;
5190
+ if (this.bits[k] === 0) {
5191
+ this.bits[k] = 1;
5192
+ this.distinct += 1;
5193
+ }
5194
+ }
5195
+ }
5196
+ }
5197
+ select() {
5198
+ if (this.distinct === 0) return String.fromCharCode(PUA_START);
5199
+ if (this.distinct >= PUA_SIZE) return null;
5200
+ for (let k = 0; k < PUA_SIZE; k++) if (this.bits[k] === 0) return String.fromCharCode(PUA_START + k);
5201
+ return null;
5202
+ }
5203
+ };
5204
+ function transformRun(input, probe, runStartsAtLineStart, seamEligible) {
5205
+ let text = input;
5206
+ let tailSensitive = false;
5207
+ let truncatedAtSeamStart = false;
5208
+ text = escapeMhchemCommands(text);
5209
+ text = escapeCurrencyDollarSigns(text);
5210
+ text = convertLatexDelimiters(text);
5211
+ if (probe && RESIDUAL_OPEN_BRACKET_RE.test(text)) tailSensitive = true;
5212
+ text = escapeLatexPipes(text);
5213
+ if (probe && findUnclosedDelimiterStart(text, "both") !== -1) tailSensitive = true;
5214
+ text = escapeLatexPipesInUnclosed(text);
5215
+ if (probe && hasUnclosedTextCommand(text)) tailSensitive = true;
5216
+ text = escapeTextUnderscores(text);
5217
+ text = convertSingleToDoubleDollar(text);
5218
+ let unclosedDouble;
5219
+ if (probe || seamEligible && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
5220
+ unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
5221
+ if (unclosedDouble !== -1) {
5222
+ tailSensitive = true;
5223
+ if (seamEligible && opensMathFlow(text, unclosedDouble, runStartsAtLineStart) && text.slice(0, unclosedDouble).trim() === "") {
5224
+ truncatedAtSeamStart = true;
5225
+ }
5226
+ }
5227
+ }
5228
+ text = truncateUnclosedLatexBlock(text, runStartsAtLineStart, unclosedDouble);
5229
+ return { out: text, tailSensitive, truncatedAtSeamStart };
5230
+ }
5231
+ function processSliceLegacy(slice, probe) {
5009
5232
  const segments = splitByProtectedRegions(slice);
5010
5233
  const parts = [];
5011
5234
  let quiescent = true;
5012
5235
  let truncatedAtSeamStart = false;
5013
5236
  for (let index = 0; index < segments.length; index++) {
5014
5237
  const segment = segments[index];
5015
- if (segment.isCode) {
5238
+ if (segment.kind !== "text") {
5016
5239
  parts.push(segment.text);
5017
5240
  continue;
5018
5241
  }
5019
- let text = segment.text;
5020
- text = escapeMhchemCommands(text);
5021
- text = escapeCurrencyDollarSigns(text);
5022
- text = convertLatexDelimiters(text);
5023
- if (probe && RESIDUAL_OPEN_BRACKET_RE.test(text)) quiescent = false;
5024
- text = escapeLatexPipes(text);
5025
- if (probe && findUnclosedDelimiterStart(text, "both") !== -1) quiescent = false;
5026
- text = escapeLatexPipesInUnclosed(text);
5027
- if (probe && hasUnclosedTextCommand(text)) quiescent = false;
5028
- text = escapeTextUnderscores(text);
5029
- text = convertSingleToDoubleDollar(text);
5030
- let unclosedDouble;
5031
- if (probe || index === 0 && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
5032
- unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
5033
- if (unclosedDouble !== -1) {
5034
- quiescent = false;
5035
- if (index === 0 && opensMathFlow(text, unclosedDouble) && text.slice(0, unclosedDouble).trim() === "") {
5036
- truncatedAtSeamStart = true;
5242
+ const r = transformRun(segment.text, probe, true, index === 0);
5243
+ if (r.tailSensitive) quiescent = false;
5244
+ if (r.truncatedAtSeamStart) truncatedAtSeamStart = true;
5245
+ parts.push(r.out);
5246
+ }
5247
+ return { out: parts.join(""), quiescent, truncatedAtSeamStart, degradedReason: null };
5248
+ }
5249
+ var SCOPE_DEPTH_CAP = 8;
5250
+ var VOID_TAGS2 = /* @__PURE__ */ new Set(["br", "hr", "img", "wbr", "input", "source"]);
5251
+ var TAG_NAME_RE = /^<(\/?)([A-Za-z][A-Za-z0-9]*)/;
5252
+ function tagInfo(tag) {
5253
+ const m = TAG_NAME_RE.exec(tag);
5254
+ const closing = m?.[1] === "/";
5255
+ const name = (m?.[2] ?? "").toLowerCase();
5256
+ const opensScope = !closing && !tag.endsWith("/>") && !VOID_TAGS2.has(name);
5257
+ return { name, closing, opensScope };
5258
+ }
5259
+ function buildRunTree(segments) {
5260
+ const root2 = [];
5261
+ const stack = [];
5262
+ const current = () => stack.length > 0 ? stack[stack.length - 1].children : root2;
5263
+ const unwind = () => {
5264
+ while (stack.length > 0) {
5265
+ const frame = stack.pop();
5266
+ current().push({ type: "atom", text: frame.open }, ...frame.children);
5267
+ }
5268
+ };
5269
+ for (const segment of segments) {
5270
+ if (segment.kind === "tag") {
5271
+ const info = tagInfo(segment.text);
5272
+ if (info.closing) {
5273
+ const top = stack[stack.length - 1];
5274
+ if (top !== void 0 && top.name === info.name) {
5275
+ stack.pop();
5276
+ const parent = current();
5277
+ if (top.suppressed) {
5278
+ parent.push({ type: "atom", text: top.open }, ...top.children, { type: "atom", text: segment.text });
5279
+ } else {
5280
+ parent.push({ type: "scope", open: top.open, close: segment.text, children: top.children });
5281
+ }
5282
+ } else {
5283
+ current().push({ type: "atom", text: segment.text });
5037
5284
  }
5285
+ } else if (info.opensScope) {
5286
+ stack.push({ name: info.name, open: segment.text, suppressed: stack.length >= SCOPE_DEPTH_CAP, children: [] });
5287
+ } else {
5288
+ current().push({ type: "atom", text: segment.text });
5038
5289
  }
5290
+ continue;
5291
+ }
5292
+ const t = segment.text;
5293
+ let start = 0;
5294
+ for (let i = 0; i < t.length; i++) {
5295
+ const c = t.charCodeAt(i);
5296
+ if (c !== 10 && c !== 13) continue;
5297
+ if (i > start) current().push({ type: "text", text: t.slice(start, i) });
5298
+ const end = c === 13 && t.charCodeAt(i + 1) === 10 ? i + 2 : i + 1;
5299
+ unwind();
5300
+ root2.push({ type: "text", text: t.slice(i, end) });
5301
+ start = end;
5302
+ i = end - 1;
5303
+ }
5304
+ if (start < t.length) current().push({ type: "text", text: t.slice(start) });
5305
+ }
5306
+ unwind();
5307
+ return root2;
5308
+ }
5309
+ var restoreFailureInjector = null;
5310
+ function restore(out, atoms, mask) {
5311
+ if (restoreFailureInjector !== null && restoreFailureInjector(atoms)) return null;
5312
+ let result = "";
5313
+ let k = 0;
5314
+ let last = 0;
5315
+ for (; ; ) {
5316
+ const idx = out.indexOf(mask, last);
5317
+ if (idx === -1) break;
5318
+ if (k >= atoms.length) return null;
5319
+ result += out.slice(last, idx) + atoms[k];
5320
+ k += 1;
5321
+ last = idx + 1;
5322
+ }
5323
+ return result + out.slice(last);
5324
+ }
5325
+ function emitRun(nodes, mask) {
5326
+ let text = "";
5327
+ const atoms = [];
5328
+ for (const node of nodes) {
5329
+ if (node.type === "text") {
5330
+ text += node.text;
5331
+ } else if (node.type === "atom") {
5332
+ atoms.push(node.text);
5333
+ text += mask;
5334
+ } else {
5335
+ const inner = emitRun(node.children, mask);
5336
+ if (inner === null) return null;
5337
+ const transformed = transformRun(inner.text, false, false, false);
5338
+ const restored = restore(transformed.out, inner.atoms, mask);
5339
+ if (restored === null) return null;
5340
+ atoms.push(node.open + restored + node.close);
5341
+ text += mask;
5039
5342
  }
5040
- text = truncateUnclosedLatexBlock(text, unclosedDouble);
5041
- parts.push(text);
5042
5343
  }
5043
- return { out: parts.join(""), quiescent, truncatedAtSeamStart };
5344
+ return { text, atoms };
5345
+ }
5346
+ function reportRestoreViolation() {
5347
+ if (true) {
5348
+ console.error(
5349
+ "[ai-react-markdown] LaTeX preprocessor: atom restoration violated its invariant (more masks in the output than atoms); the slice was re-processed on the legacy path. This is an engine defect \u2014 please report it."
5350
+ );
5351
+ }
5352
+ }
5353
+ function processSliceDefault(slice, probe, mask) {
5354
+ const segments = splitByProtectedRegions(slice);
5355
+ const parts = [];
5356
+ let quiescent = true;
5357
+ let truncatedAtSeamStart = false;
5358
+ let offset = 0;
5359
+ let i = 0;
5360
+ while (i < segments.length) {
5361
+ const segment = segments[i];
5362
+ if (isHardBoundary(segment.kind)) {
5363
+ parts.push(segment.text);
5364
+ offset += segment.text.length;
5365
+ i += 1;
5366
+ continue;
5367
+ }
5368
+ const runStart = offset;
5369
+ const runSegments = [];
5370
+ while (i < segments.length && !isHardBoundary(segments[i].kind)) {
5371
+ runSegments.push(segments[i]);
5372
+ offset += segments[i].text.length;
5373
+ i += 1;
5374
+ }
5375
+ const before = runStart === 0 ? -1 : slice.charCodeAt(runStart - 1);
5376
+ const runStartsAtLineStart = before === -1 || before === 10 || before === 13;
5377
+ const seamEligible = runStart === 0;
5378
+ const emitted = emitRun(buildRunTree(runSegments), mask);
5379
+ if (emitted === null) {
5380
+ reportRestoreViolation();
5381
+ return { ...processSliceLegacy(slice, probe), degradedReason: "restore-invariant" };
5382
+ }
5383
+ const r = transformRun(emitted.text, probe, runStartsAtLineStart, seamEligible);
5384
+ const restored = restore(r.out, emitted.atoms, mask);
5385
+ if (restored === null) {
5386
+ reportRestoreViolation();
5387
+ return { ...processSliceLegacy(slice, probe), degradedReason: "restore-invariant" };
5388
+ }
5389
+ if (r.tailSensitive) quiescent = false;
5390
+ if (r.truncatedAtSeamStart) truncatedAtSeamStart = true;
5391
+ parts.push(restored);
5392
+ }
5393
+ return { out: parts.join(""), quiescent, truncatedAtSeamStart, degradedReason: null };
5394
+ }
5395
+ function processSlice(slice, options) {
5396
+ if (options.legacy === true) return processSliceLegacy(slice, options.probe);
5397
+ return processSliceDefault(slice, options.probe, options.mask);
5044
5398
  }
5045
5399
  function isBlankRawLine(text, from, to) {
5046
5400
  for (let i = from; i < to; i++) {
@@ -5056,7 +5410,7 @@ function findRawSafeCut(active) {
5056
5410
  let backtickHazard = false;
5057
5411
  let latentLt = false;
5058
5412
  for (const segment of segments) {
5059
- if (segment.isCode) {
5413
+ if (segment.kind !== "text") {
5060
5414
  if (segment.text.includes(">")) latentLt = false;
5061
5415
  offset += segment.text.length;
5062
5416
  continue;
@@ -5091,12 +5445,28 @@ function createIncrementalLatexPreprocessor(options) {
5091
5445
  const freezeThreshold = options?.freezeThreshold ?? DEFAULT_FREEZE_ATTEMPT_THRESHOLD;
5092
5446
  const onAttempt = options?.onAttempt;
5093
5447
  const backoff = options?.backoff ?? true;
5448
+ const onDegrade = options?.onDegrade;
5094
5449
  let prevSource = "";
5095
5450
  let prevOutput = "";
5096
5451
  let frozenSrcEnd = 0;
5097
5452
  let frozenOut = "";
5098
5453
  let triggered = false;
5099
5454
  let nextAttemptLen = 0;
5455
+ let lineageDegraded = false;
5456
+ const presence = new PuaPresence();
5457
+ const commit = (source, out) => {
5458
+ prevSource = source;
5459
+ prevOutput = out;
5460
+ return out;
5461
+ };
5462
+ const legacyWhole = (source) => processSlice(source, { legacy: true, probe: false }).out;
5463
+ const degrade = (source, reason) => {
5464
+ lineageDegraded = true;
5465
+ frozenSrcEnd = 0;
5466
+ frozenOut = "";
5467
+ onDegrade?.(reason);
5468
+ return commit(source, legacyWhole(source));
5469
+ };
5100
5470
  return function incrementalPreprocessLaTeX(source) {
5101
5471
  if (source === prevSource) return prevOutput;
5102
5472
  const isAppend = source.length > prevSource.length && source.startsWith(prevSource);
@@ -5105,16 +5475,20 @@ function createIncrementalLatexPreprocessor(options) {
5105
5475
  frozenOut = "";
5106
5476
  triggered = false;
5107
5477
  nextAttemptLen = 0;
5478
+ lineageDegraded = false;
5479
+ presence.reset();
5480
+ presence.add(source, 0);
5481
+ } else {
5482
+ presence.add(source, prevSource.length);
5108
5483
  }
5109
5484
  if (!triggered) {
5110
5485
  const checkFrom = isAppend ? Math.max(0, prevSource.length - 1) : 0;
5111
- if (!hasLatexTrigger(source.slice(checkFrom))) {
5112
- prevSource = source;
5113
- prevOutput = source;
5114
- return source;
5115
- }
5486
+ if (!hasLatexTrigger(source.slice(checkFrom))) return commit(source, source);
5116
5487
  triggered = true;
5117
5488
  }
5489
+ if (lineageDegraded) return commit(source, legacyWhole(source));
5490
+ const mask = presence.select();
5491
+ if (mask === null) return degrade(source, "mask-exhausted");
5118
5492
  let active = source.slice(frozenSrcEnd);
5119
5493
  if (active.length > freezeThreshold && active.length >= nextAttemptLen) {
5120
5494
  const activeLength = active.length;
@@ -5129,18 +5503,20 @@ function createIncrementalLatexPreprocessor(options) {
5129
5503
  };
5130
5504
  const cut = findRawSafeCut(active);
5131
5505
  if (cut > 0) {
5132
- const candidate = processSlice(active.slice(0, cut));
5506
+ const candidate = processSlice(active.slice(0, cut), { probe: true, mask });
5507
+ if (candidate.degradedReason !== null) {
5508
+ onAttempt?.({ activeLength, frozenBytes: 0 });
5509
+ return degrade(source, candidate.degradedReason);
5510
+ }
5133
5511
  if (candidate.quiescent) freeze(cut, candidate);
5134
5512
  }
5135
5513
  nextAttemptLen = advanced || !backoff ? 0 : active.length * 2;
5136
5514
  onAttempt?.({ activeLength, frozenBytes });
5137
5515
  }
5138
- const tail = processSlice(active, false);
5516
+ const tail = processSlice(active, { probe: false, mask });
5517
+ if (tail.degradedReason !== null) return degrade(source, tail.degradedReason);
5139
5518
  const head = tail.truncatedAtSeamStart ? frozenOut.replace(/\s+$/, "") : frozenOut;
5140
- const out = head + tail.out;
5141
- prevSource = source;
5142
- prevOutput = out;
5143
- return out;
5519
+ return commit(source, head + tail.out);
5144
5520
  };
5145
5521
  }
5146
5522
 
@@ -5165,6 +5541,8 @@ function createRemendPreprocessor(options) {
5165
5541
  }
5166
5542
  export {
5167
5543
  DEFAULT_PAYLOAD,
5544
+ ENGINE_PLACEHOLDER_TAGS,
5545
+ ENGINE_PROVENANCE_PROPERTY,
5168
5546
  PIPELINE_STAGES,
5169
5547
  SENTINEL_FN_CONTENT,
5170
5548
  SENTINEL_LINK_URL,
@@ -5209,7 +5587,9 @@ export {
5209
5587
  preprocessLaTeX,
5210
5588
  rehypeFooterAdorn,
5211
5589
  rehypeRebaseHashLinks_default as rehypeRebaseHashLinks,
5590
+ rehypeVerifyEngineTags,
5212
5591
  removeComments,
5592
+ resolveCrossChunkReference,
5213
5593
  sanitizeCrossChunkUrl,
5214
5594
  sanitizeSchema,
5215
5595
  shortenDocumentId,