@ai-react-markdown/engine 2.9.0 → 2.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -459,16 +459,15 @@ function settleRefsAndEarliestUnresolved(cp) {
459
459
  var TYPE6_NAMES = new Set(import_micromark_util_html_tag_name.htmlBlockNames);
460
460
  var TABLE_PART_NAMES = /* @__PURE__ */ new Set(["td", "th", "tr", "tbody", "thead", "tfoot", "caption", "col", "colgroup"]);
461
461
  var DOCUMENT_STRUCTURE_NAMES = /* @__PURE__ */ new Set(["html", "head", "body", "frameset"]);
462
+ var NO_ELEMENT_NAMES = /* @__PURE__ */ new Set([...DOCUMENT_STRUCTURE_NAMES, "frame", "image"]);
462
463
  var RETROACTIVE_OPENERS = [
463
464
  "<!doctype",
464
- "<html",
465
- "<head",
466
- "<body",
467
- "<frameset",
468
- "</html",
469
- "</head",
470
- "</body",
471
- "</frameset"
465
+ // DERIVED, so the trailing-partial guard cannot drift from the poison the
466
+ // confirmed lines get. It was transcribed until F28, and the transcription
467
+ // was already two names behind by then. `<frame` subsumes `<frameset`,
468
+ // which is why the derived list is SHORTER than the one it replaces and
469
+ // still covers strictly more.
470
+ ...[...NO_ELEMENT_NAMES].flatMap((name) => [`<${name}`, `</${name}`])
472
471
  ];
473
472
  function tailCarriesRetroactive(text) {
474
473
  const lower = text.toLowerCase();
@@ -720,6 +719,7 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
720
719
  tagBalance: /* @__PURE__ */ new Map(),
721
720
  openTotal: 0,
722
721
  p5Tok: { kind: "data" },
722
+ formPointerMaybeSet: false,
723
723
  openStack: [],
724
724
  mdBlock: { kind: "none" },
725
725
  blankRun: 0,
@@ -880,9 +880,12 @@ function processConfirmedLine(cp, ln, text) {
880
880
  if (sealReleaseDerived(cp, ln, isBlockStart)) {
881
881
  cp.p5SealPending = false;
882
882
  if (false) {
883
- console.error(
884
- `[ai-react-markdown] seal-release containment broken at offset ${ln.start}: the derived predicate released a line the retired enumeration withheld (${JSON.stringify(ln.text.slice(0, 80))}).`
885
- );
883
+ sealReleaseEvaluations += 1;
884
+ if (!sealReleaseEnumerated(cp, ln, isBlockStart)) {
885
+ console.error(
886
+ `[ai-react-markdown] seal-release containment broken at offset ${ln.start}: the derived predicate released a line the retired enumeration withheld (${JSON.stringify(ln.text.slice(0, 80))}).`
887
+ );
888
+ }
886
889
  }
887
890
  }
888
891
  }
@@ -911,7 +914,7 @@ function processConfirmedLine(cp, ln, text) {
911
914
  if (tag === "plaintext") cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
912
915
  }
913
916
  }
914
- if (DOCUMENT_STRUCTURE_NAMES.has(tag)) cp.phasePoisonedAt = 0;
917
+ if (NO_ELEMENT_NAMES.has(tag)) cp.phasePoisonedAt = 0;
915
918
  if (tag === "template" && !closing) cp.phasePoisonedAt = 0;
916
919
  if (closing) {
917
920
  let idx = -1;
@@ -927,12 +930,14 @@ function processConfirmedLine(cp, ln, text) {
927
930
  return;
928
931
  }
929
932
  cp.openStack.splice(idx, 1);
933
+ if (tag === "form") cp.formPointerMaybeSet = false;
930
934
  const count = cp.tagBalance.get(tag) ?? 0;
931
935
  if (count > 0) {
932
936
  cp.tagBalance.set(tag, count - 1);
933
937
  cp.openTotal -= 1;
934
938
  }
935
939
  } else {
940
+ if (tag === "form") cp.formPointerMaybeSet = true;
936
941
  cp.openStack.push(tag);
937
942
  cp.tagBalance.set(tag, (cp.tagBalance.get(tag) ?? 0) + 1);
938
943
  cp.openTotal += 1;
@@ -1059,7 +1064,11 @@ function processConfirmedLine(cp, ln, text) {
1059
1064
  // mask suppresses them — which is exactly why `openTotal` reads 0
1060
1065
  // and the candidate looked safe), and the 2-5 interiors are the
1061
1066
  // same construct to both grammars.
1062
- htmlBalanced: cp.openTotal === 0 && cp.mdBlock.kind !== "html" && cp.p5Tok.kind !== "bogus",
1067
+ htmlBalanced: cp.openTotal === 0 && cp.mdBlock.kind !== "html" && cp.p5Tok.kind !== "bogus" && // A form pointer parse5 may still be holding makes a LATER `<form>`
1068
+ // in the tail vanish from the full parse while a split parse opens
1069
+ // it — forward independence fails with every other condition clean.
1070
+ // See the field doc.
1071
+ !cp.formPointerMaybeSet,
1063
1072
  hazard: cp.hazardVerdict,
1064
1073
  seamRisk: cp.p5SealPending,
1065
1074
  defListSettled: null
@@ -1455,6 +1464,11 @@ var SCANNER_NAME_LISTS = [
1455
1464
  ["type6", TYPE6_NAMES],
1456
1465
  ["void", VOID_TAGS],
1457
1466
  ["documentStructure", DOCUMENT_STRUCTURE_NAMES],
1467
+ // Added with F28, and it is the list that would have made F28 visible: the
1468
+ // derived census alphabet partitions names by their membership across THIS
1469
+ // table, so before it existed `frame` shared one 39-name class with `div`
1470
+ // and could never be sampled apart from it.
1471
+ ["noElement", NO_ELEMENT_NAMES],
1458
1472
  ["tablePart", TABLE_PART_NAMES],
1459
1473
  ["scopeBarrier", SCOPE_BARRIER_NAMES],
1460
1474
  ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
@@ -4410,24 +4424,32 @@ var SMOOTH_STREAM_PACING_PRESETS = Object.freeze({
4410
4424
  correctionTauMs: 280,
4411
4425
  emaTauMs: 2600,
4412
4426
  minCharsPerSecond: 4,
4413
- drainMs: 320
4427
+ drainMs: 320,
4428
+ maxLagMs: 3500
4414
4429
  }),
4415
4430
  balanced: Object.freeze({
4416
4431
  bufferFactor: 1,
4417
4432
  correctionTauMs: 180,
4418
4433
  emaTauMs: 1800,
4419
4434
  minCharsPerSecond: 6,
4420
- drainMs: 240
4435
+ drainMs: 240,
4436
+ maxLagMs: 2500
4421
4437
  }),
4422
4438
  responsive: Object.freeze({
4423
4439
  bufferFactor: 0.45,
4424
4440
  correctionTauMs: 110,
4425
4441
  emaTauMs: 1100,
4426
4442
  minCharsPerSecond: 10,
4427
- drainMs: 150
4443
+ drainMs: 150,
4444
+ maxLagMs: 800
4428
4445
  })
4429
4446
  });
4430
- var MAX_TARGET_BUFFER_MS = 1200;
4447
+ var GAP_WINDOW = 16;
4448
+ var INTERVAL_QUANTILE = 0.9;
4449
+ var HORIZON_PAD_MS = 50;
4450
+ var DEADLINE_FLOOR_MS = 33;
4451
+ var DRAIN_CATCHUP_FACTOR = 2;
4452
+ var DRAIN_MAX_FACTOR = 3;
4431
4453
  var defaultNow = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
4432
4454
  var defaultSchedule = (cb) => {
4433
4455
  if (typeof requestAnimationFrame === "function") {
@@ -4478,18 +4500,18 @@ var createSmoothStreamController = (options = {}) => {
4478
4500
  let lastTickAt = 0;
4479
4501
  let cancelFrame;
4480
4502
  let disposed = false;
4481
- let rateEma;
4482
- let intervalEma;
4503
+ let gapWindow = [];
4483
4504
  let lastArrivalAt;
4484
4505
  const listeners = /* @__PURE__ */ new Set();
4485
4506
  const resolveParams = () => {
4486
- const preset = SMOOTH_STREAM_PACING_PRESETS[options.pacing] ?? SMOOTH_STREAM_PACING_PRESETS.balanced;
4507
+ const preset = Object.hasOwn(SMOOTH_STREAM_PACING_PRESETS, options.pacing) ? SMOOTH_STREAM_PACING_PRESETS[options.pacing] : SMOOTH_STREAM_PACING_PRESETS.balanced;
4487
4508
  return {
4488
4509
  bufferFactor: finiteOr(options.bufferFactor, preset.bufferFactor),
4489
4510
  correctionTauMs: finiteOr(options.correctionTauMs, preset.correctionTauMs),
4490
4511
  emaTauMs: finiteOr(options.emaTauMs, preset.emaTauMs),
4491
4512
  minCharsPerSecond: finiteOr(options.minCharsPerSecond, preset.minCharsPerSecond),
4492
- drainMs: finiteOr(options.drainMs, preset.drainMs)
4513
+ drainMs: finiteOr(options.drainMs, preset.drainMs),
4514
+ maxLagMs: finiteOr(options.maxLagMs, preset.maxLagMs ?? 2500)
4493
4515
  };
4494
4516
  };
4495
4517
  const recordArrival = (t, added) => {
@@ -4500,12 +4522,8 @@ var createSmoothStreamController = (options = {}) => {
4500
4522
  }
4501
4523
  const gap = Math.max(1, t - lastArrivalAt);
4502
4524
  lastArrivalAt = t;
4503
- const tau = Math.max(1, resolveParams().emaTauMs);
4504
- if (gap > tau) return;
4505
- const alpha = 1 - Math.exp(-gap / tau);
4506
- const instantRate = added * 1e3 / gap;
4507
- rateEma = (rateEma ?? 0) + alpha * (instantRate - (rateEma ?? 0));
4508
- intervalEma = (intervalEma ?? 0) + alpha * (gap - (intervalEma ?? 0));
4525
+ gapWindow.push({ gap, added });
4526
+ if (gapWindow.length > GAP_WINDOW) gapWindow.shift();
4509
4527
  };
4510
4528
  const notify = () => {
4511
4529
  visibleCache = source.slice(0, visibleEnd);
@@ -4531,15 +4549,12 @@ var createSmoothStreamController = (options = {}) => {
4531
4549
  let rate;
4532
4550
  if (finished && drainDeadlineAt !== void 0) {
4533
4551
  rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, drainDeadlineAt - t));
4534
- } else if (rateEma !== void 0 && intervalEma !== void 0) {
4535
- const targetBuffer = Math.max(
4536
- 1,
4537
- Math.min(params.bufferFactor * rateEma * (intervalEma / 1e3), rateEma * MAX_TARGET_BUFFER_MS / 1e3)
4538
- );
4539
- rate = Math.max(
4540
- params.minCharsPerSecond,
4541
- rateEma + (pending.length - targetBuffer) * 1e3 / Math.max(1, params.correctionTauMs)
4542
- );
4552
+ } else if (gapWindow.length > 0 && lastArrivalAt !== void 0) {
4553
+ const gaps = gapWindow.map((s) => s.gap).sort((a, b) => a - b);
4554
+ const intervalQ = gaps[Math.min(gaps.length - 1, Math.floor(gaps.length * INTERVAL_QUANTILE))];
4555
+ const horizon = Math.max(16, Math.min(params.bufferFactor * intervalQ + HORIZON_PAD_MS, params.maxLagMs));
4556
+ const deadline = Math.max(lastArrivalAt + horizon, t + DEADLINE_FLOOR_MS);
4557
+ rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, deadline - t));
4543
4558
  } else {
4544
4559
  rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, params.correctionTauMs));
4545
4560
  }
@@ -4585,8 +4600,7 @@ var createSmoothStreamController = (options = {}) => {
4585
4600
  initialized = true;
4586
4601
  finished = false;
4587
4602
  drainDeadlineAt = void 0;
4588
- rateEma = void 0;
4589
- intervalEma = void 0;
4603
+ gapWindow = [];
4590
4604
  lastArrivalAt = void 0;
4591
4605
  source = next;
4592
4606
  visibleEnd = next.length;
@@ -4623,10 +4637,26 @@ var createSmoothStreamController = (options = {}) => {
4623
4637
  if (finished) return;
4624
4638
  finished = true;
4625
4639
  lastArrivalAt = void 0;
4626
- drainDeadlineAt = now() + resolveParams().drainMs;
4627
4640
  if (tentativeEnd > (pending.length > 0 ? pending[pending.length - 1] : visibleEnd)) {
4628
4641
  pending.push(tentativeEnd);
4629
4642
  }
4643
+ const params = resolveParams();
4644
+ let drainWin = params.drainMs;
4645
+ let chars = 0;
4646
+ let ms = 0;
4647
+ for (let i = 0; i < gapWindow.length - 1; i += 1) {
4648
+ chars += gapWindow[i].added;
4649
+ ms += gapWindow[i].gap;
4650
+ }
4651
+ const backlog = source.length - visibleEnd;
4652
+ if (chars > 0 && ms > 0 && backlog > 0) {
4653
+ const throughput = chars * 1e3 / ms;
4654
+ drainWin = Math.min(
4655
+ Math.max(backlog * 1e3 / (DRAIN_CATCHUP_FACTOR * throughput), params.drainMs),
4656
+ DRAIN_MAX_FACTOR * params.drainMs
4657
+ );
4658
+ }
4659
+ drainDeadlineAt = now() + drainWin;
4630
4660
  seam = source.length;
4631
4661
  ensureScheduled();
4632
4662
  },