@ai-react-markdown/engine 2.10.1 → 2.11.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.
@@ -31,6 +31,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  DEFAULT_PAYLOAD: () => DEFAULT_PAYLOAD,
34
+ ENGINE_PLACEHOLDER_TAGS: () => ENGINE_PLACEHOLDER_TAGS,
35
+ ENGINE_PROVENANCE_PROPERTY: () => ENGINE_PROVENANCE_PROPERTY,
34
36
  PIPELINE_STAGES: () => PIPELINE_STAGES,
35
37
  SENTINEL_FN_CONTENT: () => SENTINEL_FN_CONTENT,
36
38
  SENTINEL_LINK_URL: () => SENTINEL_LINK_URL,
@@ -75,6 +77,7 @@ __export(src_exports, {
75
77
  preprocessLaTeX: () => preprocessLaTeX,
76
78
  rehypeFooterAdorn: () => rehypeFooterAdorn,
77
79
  rehypeRebaseHashLinks: () => rehypeRebaseHashLinks_default,
80
+ rehypeVerifyEngineTags: () => rehypeVerifyEngineTags,
78
81
  removeComments: () => removeComments,
79
82
  sanitizeCrossChunkUrl: () => sanitizeCrossChunkUrl,
80
83
  sanitizeSchema: () => sanitizeSchema,
@@ -2797,6 +2800,44 @@ function rehypeUnwrapCrossChunkImages() {
2797
2800
 
2798
2801
  // src/components/pluginChain.ts
2799
2802
  var import_rehype_sanitize = __toESM(require("rehype-sanitize"), 1);
2803
+
2804
+ // src/components/rehypeVerifyEngineTags.ts
2805
+ var ENGINE_PLACEHOLDER_TAGS = /* @__PURE__ */ new Set([
2806
+ "footnote-sup",
2807
+ "cross-chunk-link",
2808
+ "cross-chunk-image"
2809
+ ]);
2810
+ var ENGINE_PROVENANCE_PROPERTY = "engineProvenance";
2811
+ function walk(parent, provenance) {
2812
+ const children = parent.children;
2813
+ let i = 0;
2814
+ while (i < children.length) {
2815
+ const node = children[i];
2816
+ if (node.type === "element" && ENGINE_PLACEHOLDER_TAGS.has(node.tagName)) {
2817
+ const props = node.properties ?? {};
2818
+ const stamped = props[ENGINE_PROVENANCE_PROPERTY];
2819
+ const genuine = provenance !== "" && typeof stamped === "string" && stamped === provenance;
2820
+ if (genuine) {
2821
+ delete props[ENGINE_PROVENANCE_PROPERTY];
2822
+ walk(node, provenance);
2823
+ i += 1;
2824
+ } else {
2825
+ children.splice(i, 1, ...node.children);
2826
+ }
2827
+ continue;
2828
+ }
2829
+ if (node.type === "element") walk(node, provenance);
2830
+ i += 1;
2831
+ }
2832
+ }
2833
+ function rehypeVerifyEngineTags(options) {
2834
+ const provenance = options?.provenance ?? "";
2835
+ return function transformer(tree) {
2836
+ walk(tree, provenance);
2837
+ };
2838
+ }
2839
+
2840
+ // src/components/pluginChain.ts
2800
2841
  var import_remark_breaks = __toESM(require("remark-breaks"), 1);
2801
2842
  var import_remark_cjk_friendly = __toESM(require("remark-cjk-friendly"), 1);
2802
2843
  var import_remark_cjk_friendly_gfm_strikethrough = __toESM(require("remark-cjk-friendly-gfm-strikethrough"), 1);
@@ -2902,10 +2943,13 @@ function buildCoreRemarkPlugins(enginePlugins) {
2902
2943
  ...DISPLAY_OPTIMIZE_CHAIN.filter(([name]) => selected.has(name)).map(([, plugin]) => plugin)
2903
2944
  ];
2904
2945
  }
2905
- function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix) {
2946
+ function buildCoreRehypePlugins(sanitizeSchema2, clobberPrefix, options) {
2906
2947
  return [
2907
2948
  // Allow raw HTML through so rehype-sanitize can handle it.
2908
2949
  [import_rehype_raw.default, { passThrough: [] }],
2950
+ // Unwrap forged engine placeholders BEFORE sanitize admits their tag
2951
+ // names. Only when the caller holds a credential (see the option's doc).
2952
+ ...options ? [[rehypeVerifyEngineTags, { provenance: options.provenance }]] : [],
2909
2953
  // Sanitize HTML while allowing <mark> (highlight), KaTeX class names,
2910
2954
  // and any extra protocols the caller permitted via the `sanitizeSchema`
2911
2955
  // prop. Override `clobberPrefix` with the instance-scoped value — the
@@ -2945,6 +2989,10 @@ function buildCoreRemarkRehypeOptions(enableDefinitionList) {
2945
2989
  }
2946
2990
 
2947
2991
  // src/components/customMdastHandlers.ts
2992
+ function provenanceProps(s) {
2993
+ const provenance = s.options.provenance;
2994
+ return typeof provenance === "string" ? { engineProvenance: provenance } : {};
2995
+ }
2948
2996
  function localDefProps(s, id) {
2949
2997
  const def = s.definitionById.get(id);
2950
2998
  if (!def || typeof def.url !== "string" || def.url === SENTINEL_LINK_URL) return {};
@@ -2983,7 +3031,8 @@ function buildCrossChunkHandlers() {
2983
3031
  label: node.label ?? node.identifier,
2984
3032
  referenceType: node.referenceType,
2985
3033
  documentId: s.options.documentId,
2986
- ...localDefProps(s, id)
3034
+ ...localDefProps(s, id),
3035
+ ...provenanceProps(s)
2987
3036
  },
2988
3037
  children: s.all(node)
2989
3038
  };
@@ -3001,7 +3050,8 @@ function buildCrossChunkHandlers() {
3001
3050
  referenceType: node.referenceType,
3002
3051
  alt: node.alt ?? "",
3003
3052
  documentId: s.options.documentId,
3004
- ...localDefProps(s, id)
3053
+ ...localDefProps(s, id),
3054
+ ...provenanceProps(s)
3005
3055
  },
3006
3056
  children: []
3007
3057
  };
@@ -3018,7 +3068,8 @@ function buildCrossChunkHandlers() {
3018
3068
  properties: {
3019
3069
  label: node.identifier,
3020
3070
  localOccurrence,
3021
- documentId: s.options.documentId
3071
+ documentId: s.options.documentId,
3072
+ ...provenanceProps(s)
3022
3073
  },
3023
3074
  children: []
3024
3075
  };
@@ -3036,7 +3087,8 @@ function buildCrossChunkHandlers() {
3036
3087
  // first client frame), where the local synthetic footer is what
3037
3088
  // renders, so marks and footer agree (core-render-02).
3038
3089
  localNumber: s.footnoteOrder.indexOf(id) + 1,
3039
- documentId: s.options.documentId
3090
+ documentId: s.options.documentId,
3091
+ ...provenanceProps(s)
3040
3092
  },
3041
3093
  children: []
3042
3094
  };
@@ -4708,6 +4760,12 @@ var createSmoothStreamController = (options = {}) => {
4708
4760
  };
4709
4761
 
4710
4762
  // src/preprocessors/latex.ts
4763
+ function isHardBoundary(kind) {
4764
+ return kind === "code" || kind === "literal" || kind === "multilineTag";
4765
+ }
4766
+ function hasLineEnding(text) {
4767
+ return text.includes("\n") || text.includes("\r");
4768
+ }
4711
4769
  function getRepeatedMarkerLength(content, start, marker) {
4712
4770
  let end = start;
4713
4771
  while (end < content.length && content[end] === marker) {
@@ -4775,11 +4833,11 @@ function splitByProtectedRegions(content) {
4775
4833
  let multilineFenceMarker = null;
4776
4834
  let multilineFenceLength = 0;
4777
4835
  let multilineFenceIndent = 0;
4778
- function pushProtected(start, end) {
4836
+ function pushProtected(start, end, kind) {
4779
4837
  if (start > lastIndex) {
4780
- segments.push({ text: content.substring(lastIndex, start), isCode: false });
4838
+ segments.push({ kind: "text", text: content.substring(lastIndex, start) });
4781
4839
  }
4782
- segments.push({ text: content.substring(start, end), isCode: true });
4840
+ segments.push({ kind, text: content.substring(start, end) });
4783
4841
  lastIndex = end;
4784
4842
  }
4785
4843
  let i = 0;
@@ -4790,7 +4848,7 @@ function splitByProtectedRegions(content) {
4790
4848
  const runLen = getRepeatedMarkerLength(content, i, multilineFenceMarker);
4791
4849
  const closerIndent = lineIndentBefore(content, i);
4792
4850
  if (runLen >= multilineFenceLength && closerIndent !== -1 && closerIndent <= multilineFenceIndent + 3 && restOfLineIsBlank(content, i + runLen)) {
4793
- pushProtected(multilineStart, i + runLen);
4851
+ pushProtected(multilineStart, i + runLen, "code");
4794
4852
  multilineStart = -1;
4795
4853
  multilineFenceMarker = null;
4796
4854
  multilineFenceLength = 0;
@@ -4817,7 +4875,7 @@ function splitByProtectedRegions(content) {
4817
4875
  if (char === "`") {
4818
4876
  const closeIdx = findClosingBacktickRun(content, i + runLen, runLen);
4819
4877
  if (closeIdx !== -1) {
4820
- pushProtected(i, closeIdx + runLen);
4878
+ pushProtected(i, closeIdx + runLen, "code");
4821
4879
  i = closeIdx + runLen;
4822
4880
  continue;
4823
4881
  }
@@ -4842,7 +4900,11 @@ function splitByProtectedRegions(content) {
4842
4900
  endIndex = content.length;
4843
4901
  }
4844
4902
  }
4845
- pushProtected(i, endIndex);
4903
+ pushProtected(
4904
+ i,
4905
+ endIndex,
4906
+ isOpeningPairedTag ? "literal" : hasLineEnding(content.substring(i, endIndex)) ? "multilineTag" : "tag"
4907
+ );
4846
4908
  i = endIndex;
4847
4909
  continue;
4848
4910
  }
@@ -4850,10 +4912,10 @@ function splitByProtectedRegions(content) {
4850
4912
  i += 1;
4851
4913
  }
4852
4914
  if (multilineStart !== -1) {
4853
- pushProtected(multilineStart, content.length);
4915
+ pushProtected(multilineStart, content.length, "code");
4854
4916
  }
4855
4917
  if (lastIndex < content.length) {
4856
- segments.push({ text: content.substring(lastIndex), isCode: false });
4918
+ segments.push({ kind: "text", text: content.substring(lastIndex) });
4857
4919
  }
4858
4920
  return segments;
4859
4921
  }
@@ -5007,20 +5069,22 @@ function escapeLatexPipesInUnclosed(text) {
5007
5069
  const tail = text.substring(unclosedStart + delimLen);
5008
5070
  return before + delim + replaceUnescapedPipes(tail);
5009
5071
  }
5010
- function opensMathFlow(text, pos) {
5072
+ function opensMathFlow(text, pos, runStartsAtLineStart) {
5011
5073
  let i = pos;
5012
5074
  let spaces = 0;
5013
- while (i > 0 && text[i - 1] !== "\n") {
5075
+ while (i > 0) {
5076
+ const prev = text[i - 1];
5077
+ if (prev === "\n" || prev === "\r") return true;
5014
5078
  i -= 1;
5015
5079
  if (text[i] !== " ") return false;
5016
5080
  spaces += 1;
5017
5081
  if (spaces > 3) return false;
5018
5082
  }
5019
- return true;
5083
+ return runStartsAtLineStart;
5020
5084
  }
5021
- function truncateUnclosedLatexBlock(text, unclosedStart = findUnclosedDelimiterStart(text, "double-only")) {
5085
+ function truncateUnclosedLatexBlock(text, runStartsAtLineStart, unclosedStart = findUnclosedDelimiterStart(text, "double-only")) {
5022
5086
  if (unclosedStart === -1) return text;
5023
- if (!opensMathFlow(text, unclosedStart)) return text;
5087
+ if (!opensMathFlow(text, unclosedStart, runStartsAtLineStart)) return text;
5024
5088
  return text.substring(0, unclosedStart).trimEnd();
5025
5089
  }
5026
5090
  function escapeTextUnderscores(text) {
@@ -5065,7 +5129,8 @@ function convertSingleToDoubleDollar(text) {
5065
5129
  }
5066
5130
  function preprocessLaTeX(str) {
5067
5131
  if (!hasLatexTrigger(str)) return str;
5068
- return processSlice(str, false).out;
5132
+ const mask = selectMask(str);
5133
+ return (mask === null ? processSlice(str, { legacy: true, probe: false }) : processSlice(str, { probe: false, mask })).out;
5069
5134
  }
5070
5135
  function hasLatexTrigger(str) {
5071
5136
  return str.includes("$") || str.includes("\\[") || str.includes("\\(");
@@ -5094,42 +5159,240 @@ function hasUnclosedTextCommand(text) {
5094
5159
  }
5095
5160
  var RESIDUAL_OPEN_BRACKET_RE = /(?<!!)\\\[/;
5096
5161
  var LEADING_DOUBLE_DOLLAR_RE = /^\s*\$\$/;
5097
- function processSlice(slice, probe = true) {
5162
+ var PUA_START = 57344;
5163
+ var PUA_END = 63743;
5164
+ var PUA_SIZE = PUA_END - PUA_START + 1;
5165
+ function selectMask(source) {
5166
+ const first = String.fromCharCode(PUA_START);
5167
+ if (source.indexOf(first) === -1) return first;
5168
+ const seen = new Uint8Array(PUA_SIZE);
5169
+ for (let i = 0; i < source.length; i++) {
5170
+ const c = source.charCodeAt(i);
5171
+ if (c >= PUA_START && c <= PUA_END) seen[c - PUA_START] = 1;
5172
+ }
5173
+ for (let k = 0; k < PUA_SIZE; k++) if (seen[k] === 0) return String.fromCharCode(PUA_START + k);
5174
+ return null;
5175
+ }
5176
+ var PuaPresence = class {
5177
+ bits = new Uint8Array(PUA_SIZE);
5178
+ distinct = 0;
5179
+ reset() {
5180
+ this.bits.fill(0);
5181
+ this.distinct = 0;
5182
+ }
5183
+ add(text, from) {
5184
+ for (let i = from; i < text.length; i++) {
5185
+ const c = text.charCodeAt(i);
5186
+ if (c >= PUA_START && c <= PUA_END) {
5187
+ const k = c - PUA_START;
5188
+ if (this.bits[k] === 0) {
5189
+ this.bits[k] = 1;
5190
+ this.distinct += 1;
5191
+ }
5192
+ }
5193
+ }
5194
+ }
5195
+ select() {
5196
+ if (this.distinct === 0) return String.fromCharCode(PUA_START);
5197
+ if (this.distinct >= PUA_SIZE) return null;
5198
+ for (let k = 0; k < PUA_SIZE; k++) if (this.bits[k] === 0) return String.fromCharCode(PUA_START + k);
5199
+ return null;
5200
+ }
5201
+ };
5202
+ function transformRun(input, probe, runStartsAtLineStart, seamEligible) {
5203
+ let text = input;
5204
+ let tailSensitive = false;
5205
+ let truncatedAtSeamStart = false;
5206
+ text = escapeMhchemCommands(text);
5207
+ text = escapeCurrencyDollarSigns(text);
5208
+ text = convertLatexDelimiters(text);
5209
+ if (probe && RESIDUAL_OPEN_BRACKET_RE.test(text)) tailSensitive = true;
5210
+ text = escapeLatexPipes(text);
5211
+ if (probe && findUnclosedDelimiterStart(text, "both") !== -1) tailSensitive = true;
5212
+ text = escapeLatexPipesInUnclosed(text);
5213
+ if (probe && hasUnclosedTextCommand(text)) tailSensitive = true;
5214
+ text = escapeTextUnderscores(text);
5215
+ text = convertSingleToDoubleDollar(text);
5216
+ let unclosedDouble;
5217
+ if (probe || seamEligible && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
5218
+ unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
5219
+ if (unclosedDouble !== -1) {
5220
+ tailSensitive = true;
5221
+ if (seamEligible && opensMathFlow(text, unclosedDouble, runStartsAtLineStart) && text.slice(0, unclosedDouble).trim() === "") {
5222
+ truncatedAtSeamStart = true;
5223
+ }
5224
+ }
5225
+ }
5226
+ text = truncateUnclosedLatexBlock(text, runStartsAtLineStart, unclosedDouble);
5227
+ return { out: text, tailSensitive, truncatedAtSeamStart };
5228
+ }
5229
+ function processSliceLegacy(slice, probe) {
5098
5230
  const segments = splitByProtectedRegions(slice);
5099
5231
  const parts = [];
5100
5232
  let quiescent = true;
5101
5233
  let truncatedAtSeamStart = false;
5102
5234
  for (let index = 0; index < segments.length; index++) {
5103
5235
  const segment = segments[index];
5104
- if (segment.isCode) {
5236
+ if (segment.kind !== "text") {
5105
5237
  parts.push(segment.text);
5106
5238
  continue;
5107
5239
  }
5108
- let text = segment.text;
5109
- text = escapeMhchemCommands(text);
5110
- text = escapeCurrencyDollarSigns(text);
5111
- text = convertLatexDelimiters(text);
5112
- if (probe && RESIDUAL_OPEN_BRACKET_RE.test(text)) quiescent = false;
5113
- text = escapeLatexPipes(text);
5114
- if (probe && findUnclosedDelimiterStart(text, "both") !== -1) quiescent = false;
5115
- text = escapeLatexPipesInUnclosed(text);
5116
- if (probe && hasUnclosedTextCommand(text)) quiescent = false;
5117
- text = escapeTextUnderscores(text);
5118
- text = convertSingleToDoubleDollar(text);
5119
- let unclosedDouble;
5120
- if (probe || index === 0 && LEADING_DOUBLE_DOLLAR_RE.test(text)) {
5121
- unclosedDouble = findUnclosedDelimiterStart(text, "double-only");
5122
- if (unclosedDouble !== -1) {
5123
- quiescent = false;
5124
- if (index === 0 && opensMathFlow(text, unclosedDouble) && text.slice(0, unclosedDouble).trim() === "") {
5125
- truncatedAtSeamStart = true;
5240
+ const r = transformRun(segment.text, probe, true, index === 0);
5241
+ if (r.tailSensitive) quiescent = false;
5242
+ if (r.truncatedAtSeamStart) truncatedAtSeamStart = true;
5243
+ parts.push(r.out);
5244
+ }
5245
+ return { out: parts.join(""), quiescent, truncatedAtSeamStart, degradedReason: null };
5246
+ }
5247
+ var SCOPE_DEPTH_CAP = 8;
5248
+ var VOID_TAGS2 = /* @__PURE__ */ new Set(["br", "hr", "img", "wbr", "input", "source"]);
5249
+ var TAG_NAME_RE = /^<(\/?)([A-Za-z][A-Za-z0-9]*)/;
5250
+ function tagInfo(tag) {
5251
+ const m = TAG_NAME_RE.exec(tag);
5252
+ const closing = m?.[1] === "/";
5253
+ const name = (m?.[2] ?? "").toLowerCase();
5254
+ const opensScope = !closing && !tag.endsWith("/>") && !VOID_TAGS2.has(name);
5255
+ return { name, closing, opensScope };
5256
+ }
5257
+ function buildRunTree(segments) {
5258
+ const root2 = [];
5259
+ const stack = [];
5260
+ const current = () => stack.length > 0 ? stack[stack.length - 1].children : root2;
5261
+ const unwind = () => {
5262
+ while (stack.length > 0) {
5263
+ const frame = stack.pop();
5264
+ current().push({ type: "atom", text: frame.open }, ...frame.children);
5265
+ }
5266
+ };
5267
+ for (const segment of segments) {
5268
+ if (segment.kind === "tag") {
5269
+ const info = tagInfo(segment.text);
5270
+ if (info.closing) {
5271
+ const top = stack[stack.length - 1];
5272
+ if (top !== void 0 && top.name === info.name) {
5273
+ stack.pop();
5274
+ const parent = current();
5275
+ if (top.suppressed) {
5276
+ parent.push({ type: "atom", text: top.open }, ...top.children, { type: "atom", text: segment.text });
5277
+ } else {
5278
+ parent.push({ type: "scope", open: top.open, close: segment.text, children: top.children });
5279
+ }
5280
+ } else {
5281
+ current().push({ type: "atom", text: segment.text });
5126
5282
  }
5283
+ } else if (info.opensScope) {
5284
+ stack.push({ name: info.name, open: segment.text, suppressed: stack.length >= SCOPE_DEPTH_CAP, children: [] });
5285
+ } else {
5286
+ current().push({ type: "atom", text: segment.text });
5127
5287
  }
5288
+ continue;
5128
5289
  }
5129
- text = truncateUnclosedLatexBlock(text, unclosedDouble);
5130
- parts.push(text);
5290
+ const t = segment.text;
5291
+ let start = 0;
5292
+ for (let i = 0; i < t.length; i++) {
5293
+ const c = t.charCodeAt(i);
5294
+ if (c !== 10 && c !== 13) continue;
5295
+ if (i > start) current().push({ type: "text", text: t.slice(start, i) });
5296
+ const end = c === 13 && t.charCodeAt(i + 1) === 10 ? i + 2 : i + 1;
5297
+ unwind();
5298
+ root2.push({ type: "text", text: t.slice(i, end) });
5299
+ start = end;
5300
+ i = end - 1;
5301
+ }
5302
+ if (start < t.length) current().push({ type: "text", text: t.slice(start) });
5303
+ }
5304
+ unwind();
5305
+ return root2;
5306
+ }
5307
+ var restoreFailureInjector = null;
5308
+ function restore(out, atoms, mask) {
5309
+ if (restoreFailureInjector !== null && restoreFailureInjector(atoms)) return null;
5310
+ let result = "";
5311
+ let k = 0;
5312
+ let last = 0;
5313
+ for (; ; ) {
5314
+ const idx = out.indexOf(mask, last);
5315
+ if (idx === -1) break;
5316
+ if (k >= atoms.length) return null;
5317
+ result += out.slice(last, idx) + atoms[k];
5318
+ k += 1;
5319
+ last = idx + 1;
5320
+ }
5321
+ return result + out.slice(last);
5322
+ }
5323
+ function emitRun(nodes, mask) {
5324
+ let text = "";
5325
+ const atoms = [];
5326
+ for (const node of nodes) {
5327
+ if (node.type === "text") {
5328
+ text += node.text;
5329
+ } else if (node.type === "atom") {
5330
+ atoms.push(node.text);
5331
+ text += mask;
5332
+ } else {
5333
+ const inner = emitRun(node.children, mask);
5334
+ if (inner === null) return null;
5335
+ const transformed = transformRun(inner.text, false, false, false);
5336
+ const restored = restore(transformed.out, inner.atoms, mask);
5337
+ if (restored === null) return null;
5338
+ atoms.push(node.open + restored + node.close);
5339
+ text += mask;
5340
+ }
5341
+ }
5342
+ return { text, atoms };
5343
+ }
5344
+ function reportRestoreViolation() {
5345
+ if (true) {
5346
+ console.error(
5347
+ "[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."
5348
+ );
5131
5349
  }
5132
- return { out: parts.join(""), quiescent, truncatedAtSeamStart };
5350
+ }
5351
+ function processSliceDefault(slice, probe, mask) {
5352
+ const segments = splitByProtectedRegions(slice);
5353
+ const parts = [];
5354
+ let quiescent = true;
5355
+ let truncatedAtSeamStart = false;
5356
+ let offset = 0;
5357
+ let i = 0;
5358
+ while (i < segments.length) {
5359
+ const segment = segments[i];
5360
+ if (isHardBoundary(segment.kind)) {
5361
+ parts.push(segment.text);
5362
+ offset += segment.text.length;
5363
+ i += 1;
5364
+ continue;
5365
+ }
5366
+ const runStart = offset;
5367
+ const runSegments = [];
5368
+ while (i < segments.length && !isHardBoundary(segments[i].kind)) {
5369
+ runSegments.push(segments[i]);
5370
+ offset += segments[i].text.length;
5371
+ i += 1;
5372
+ }
5373
+ const before = runStart === 0 ? -1 : slice.charCodeAt(runStart - 1);
5374
+ const runStartsAtLineStart = before === -1 || before === 10 || before === 13;
5375
+ const seamEligible = runStart === 0;
5376
+ const emitted = emitRun(buildRunTree(runSegments), mask);
5377
+ if (emitted === null) {
5378
+ reportRestoreViolation();
5379
+ return { ...processSliceLegacy(slice, probe), degradedReason: "restore-invariant" };
5380
+ }
5381
+ const r = transformRun(emitted.text, probe, runStartsAtLineStart, seamEligible);
5382
+ const restored = restore(r.out, emitted.atoms, mask);
5383
+ if (restored === null) {
5384
+ reportRestoreViolation();
5385
+ return { ...processSliceLegacy(slice, probe), degradedReason: "restore-invariant" };
5386
+ }
5387
+ if (r.tailSensitive) quiescent = false;
5388
+ if (r.truncatedAtSeamStart) truncatedAtSeamStart = true;
5389
+ parts.push(restored);
5390
+ }
5391
+ return { out: parts.join(""), quiescent, truncatedAtSeamStart, degradedReason: null };
5392
+ }
5393
+ function processSlice(slice, options) {
5394
+ if (options.legacy === true) return processSliceLegacy(slice, options.probe);
5395
+ return processSliceDefault(slice, options.probe, options.mask);
5133
5396
  }
5134
5397
  function isBlankRawLine(text, from, to) {
5135
5398
  for (let i = from; i < to; i++) {
@@ -5145,7 +5408,7 @@ function findRawSafeCut(active) {
5145
5408
  let backtickHazard = false;
5146
5409
  let latentLt = false;
5147
5410
  for (const segment of segments) {
5148
- if (segment.isCode) {
5411
+ if (segment.kind !== "text") {
5149
5412
  if (segment.text.includes(">")) latentLt = false;
5150
5413
  offset += segment.text.length;
5151
5414
  continue;
@@ -5180,12 +5443,28 @@ function createIncrementalLatexPreprocessor(options) {
5180
5443
  const freezeThreshold = options?.freezeThreshold ?? DEFAULT_FREEZE_ATTEMPT_THRESHOLD;
5181
5444
  const onAttempt = options?.onAttempt;
5182
5445
  const backoff = options?.backoff ?? true;
5446
+ const onDegrade = options?.onDegrade;
5183
5447
  let prevSource = "";
5184
5448
  let prevOutput = "";
5185
5449
  let frozenSrcEnd = 0;
5186
5450
  let frozenOut = "";
5187
5451
  let triggered = false;
5188
5452
  let nextAttemptLen = 0;
5453
+ let lineageDegraded = false;
5454
+ const presence = new PuaPresence();
5455
+ const commit = (source, out) => {
5456
+ prevSource = source;
5457
+ prevOutput = out;
5458
+ return out;
5459
+ };
5460
+ const legacyWhole = (source) => processSlice(source, { legacy: true, probe: false }).out;
5461
+ const degrade = (source, reason) => {
5462
+ lineageDegraded = true;
5463
+ frozenSrcEnd = 0;
5464
+ frozenOut = "";
5465
+ onDegrade?.(reason);
5466
+ return commit(source, legacyWhole(source));
5467
+ };
5189
5468
  return function incrementalPreprocessLaTeX(source) {
5190
5469
  if (source === prevSource) return prevOutput;
5191
5470
  const isAppend = source.length > prevSource.length && source.startsWith(prevSource);
@@ -5194,16 +5473,20 @@ function createIncrementalLatexPreprocessor(options) {
5194
5473
  frozenOut = "";
5195
5474
  triggered = false;
5196
5475
  nextAttemptLen = 0;
5476
+ lineageDegraded = false;
5477
+ presence.reset();
5478
+ presence.add(source, 0);
5479
+ } else {
5480
+ presence.add(source, prevSource.length);
5197
5481
  }
5198
5482
  if (!triggered) {
5199
5483
  const checkFrom = isAppend ? Math.max(0, prevSource.length - 1) : 0;
5200
- if (!hasLatexTrigger(source.slice(checkFrom))) {
5201
- prevSource = source;
5202
- prevOutput = source;
5203
- return source;
5204
- }
5484
+ if (!hasLatexTrigger(source.slice(checkFrom))) return commit(source, source);
5205
5485
  triggered = true;
5206
5486
  }
5487
+ if (lineageDegraded) return commit(source, legacyWhole(source));
5488
+ const mask = presence.select();
5489
+ if (mask === null) return degrade(source, "mask-exhausted");
5207
5490
  let active = source.slice(frozenSrcEnd);
5208
5491
  if (active.length > freezeThreshold && active.length >= nextAttemptLen) {
5209
5492
  const activeLength = active.length;
@@ -5218,18 +5501,20 @@ function createIncrementalLatexPreprocessor(options) {
5218
5501
  };
5219
5502
  const cut = findRawSafeCut(active);
5220
5503
  if (cut > 0) {
5221
- const candidate = processSlice(active.slice(0, cut));
5504
+ const candidate = processSlice(active.slice(0, cut), { probe: true, mask });
5505
+ if (candidate.degradedReason !== null) {
5506
+ onAttempt?.({ activeLength, frozenBytes: 0 });
5507
+ return degrade(source, candidate.degradedReason);
5508
+ }
5222
5509
  if (candidate.quiescent) freeze(cut, candidate);
5223
5510
  }
5224
5511
  nextAttemptLen = advanced || !backoff ? 0 : active.length * 2;
5225
5512
  onAttempt?.({ activeLength, frozenBytes });
5226
5513
  }
5227
- const tail = processSlice(active, false);
5514
+ const tail = processSlice(active, { probe: false, mask });
5515
+ if (tail.degradedReason !== null) return degrade(source, tail.degradedReason);
5228
5516
  const head = tail.truncatedAtSeamStart ? frozenOut.replace(/\s+$/, "") : frozenOut;
5229
- const out = head + tail.out;
5230
- prevSource = source;
5231
- prevOutput = out;
5232
- return out;
5517
+ return commit(source, head + tail.out);
5233
5518
  };
5234
5519
  }
5235
5520
 
@@ -5255,6 +5540,8 @@ function createRemendPreprocessor(options) {
5255
5540
  // Annotate the CommonJS export names for ESM import in node:
5256
5541
  0 && (module.exports = {
5257
5542
  DEFAULT_PAYLOAD,
5543
+ ENGINE_PLACEHOLDER_TAGS,
5544
+ ENGINE_PROVENANCE_PROPERTY,
5258
5545
  PIPELINE_STAGES,
5259
5546
  SENTINEL_FN_CONTENT,
5260
5547
  SENTINEL_LINK_URL,
@@ -5299,6 +5586,7 @@ function createRemendPreprocessor(options) {
5299
5586
  preprocessLaTeX,
5300
5587
  rehypeFooterAdorn,
5301
5588
  rehypeRebaseHashLinks,
5589
+ rehypeVerifyEngineTags,
5302
5590
  removeComments,
5303
5591
  sanitizeCrossChunkUrl,
5304
5592
  sanitizeSchema,