@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.js CHANGED
@@ -370,16 +370,15 @@ function settleRefsAndEarliestUnresolved(cp) {
370
370
  var TYPE6_NAMES = new Set(htmlBlockNames);
371
371
  var TABLE_PART_NAMES = /* @__PURE__ */ new Set(["td", "th", "tr", "tbody", "thead", "tfoot", "caption", "col", "colgroup"]);
372
372
  var DOCUMENT_STRUCTURE_NAMES = /* @__PURE__ */ new Set(["html", "head", "body", "frameset"]);
373
+ var NO_ELEMENT_NAMES = /* @__PURE__ */ new Set([...DOCUMENT_STRUCTURE_NAMES, "frame", "image"]);
373
374
  var RETROACTIVE_OPENERS = [
374
375
  "<!doctype",
375
- "<html",
376
- "<head",
377
- "<body",
378
- "<frameset",
379
- "</html",
380
- "</head",
381
- "</body",
382
- "</frameset"
376
+ // DERIVED, so the trailing-partial guard cannot drift from the poison the
377
+ // confirmed lines get. It was transcribed until F28, and the transcription
378
+ // was already two names behind by then. `<frame` subsumes `<frameset`,
379
+ // which is why the derived list is SHORTER than the one it replaces and
380
+ // still covers strictly more.
381
+ ...[...NO_ELEMENT_NAMES].flatMap((name) => [`<${name}`, `</${name}`])
383
382
  ];
384
383
  function tailCarriesRetroactive(text) {
385
384
  const lower = text.toLowerCase();
@@ -631,6 +630,7 @@ function freshCheckpoint(defListEnabled, mathFlow, referenceTaint) {
631
630
  tagBalance: /* @__PURE__ */ new Map(),
632
631
  openTotal: 0,
633
632
  p5Tok: { kind: "data" },
633
+ formPointerMaybeSet: false,
634
634
  openStack: [],
635
635
  mdBlock: { kind: "none" },
636
636
  blankRun: 0,
@@ -791,9 +791,12 @@ function processConfirmedLine(cp, ln, text) {
791
791
  if (sealReleaseDerived(cp, ln, isBlockStart)) {
792
792
  cp.p5SealPending = false;
793
793
  if (false) {
794
- console.error(
795
- `[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))}).`
796
- );
794
+ sealReleaseEvaluations += 1;
795
+ if (!sealReleaseEnumerated(cp, ln, isBlockStart)) {
796
+ console.error(
797
+ `[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))}).`
798
+ );
799
+ }
797
800
  }
798
801
  }
799
802
  }
@@ -822,7 +825,7 @@ function processConfirmedLine(cp, ln, text) {
822
825
  if (tag === "plaintext") cp.phasePoisonedAt = Math.min(cp.phasePoisonedAt, ln.start);
823
826
  }
824
827
  }
825
- if (DOCUMENT_STRUCTURE_NAMES.has(tag)) cp.phasePoisonedAt = 0;
828
+ if (NO_ELEMENT_NAMES.has(tag)) cp.phasePoisonedAt = 0;
826
829
  if (tag === "template" && !closing) cp.phasePoisonedAt = 0;
827
830
  if (closing) {
828
831
  let idx = -1;
@@ -838,12 +841,14 @@ function processConfirmedLine(cp, ln, text) {
838
841
  return;
839
842
  }
840
843
  cp.openStack.splice(idx, 1);
844
+ if (tag === "form") cp.formPointerMaybeSet = false;
841
845
  const count = cp.tagBalance.get(tag) ?? 0;
842
846
  if (count > 0) {
843
847
  cp.tagBalance.set(tag, count - 1);
844
848
  cp.openTotal -= 1;
845
849
  }
846
850
  } else {
851
+ if (tag === "form") cp.formPointerMaybeSet = true;
847
852
  cp.openStack.push(tag);
848
853
  cp.tagBalance.set(tag, (cp.tagBalance.get(tag) ?? 0) + 1);
849
854
  cp.openTotal += 1;
@@ -970,7 +975,11 @@ function processConfirmedLine(cp, ln, text) {
970
975
  // mask suppresses them — which is exactly why `openTotal` reads 0
971
976
  // and the candidate looked safe), and the 2-5 interiors are the
972
977
  // same construct to both grammars.
973
- htmlBalanced: cp.openTotal === 0 && cp.mdBlock.kind !== "html" && cp.p5Tok.kind !== "bogus",
978
+ htmlBalanced: cp.openTotal === 0 && cp.mdBlock.kind !== "html" && cp.p5Tok.kind !== "bogus" && // A form pointer parse5 may still be holding makes a LATER `<form>`
979
+ // in the tail vanish from the full parse while a split parse opens
980
+ // it — forward independence fails with every other condition clean.
981
+ // See the field doc.
982
+ !cp.formPointerMaybeSet,
974
983
  hazard: cp.hazardVerdict,
975
984
  seamRisk: cp.p5SealPending,
976
985
  defListSettled: null
@@ -1366,6 +1375,11 @@ var SCANNER_NAME_LISTS = [
1366
1375
  ["type6", TYPE6_NAMES],
1367
1376
  ["void", VOID_TAGS],
1368
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],
1369
1383
  ["tablePart", TABLE_PART_NAMES],
1370
1384
  ["scopeBarrier", SCOPE_BARRIER_NAMES],
1371
1385
  ["foreignRoot", new Set(FOREIGN_ROOT_NAMES)]
@@ -4321,24 +4335,32 @@ var SMOOTH_STREAM_PACING_PRESETS = Object.freeze({
4321
4335
  correctionTauMs: 280,
4322
4336
  emaTauMs: 2600,
4323
4337
  minCharsPerSecond: 4,
4324
- drainMs: 320
4338
+ drainMs: 320,
4339
+ maxLagMs: 3500
4325
4340
  }),
4326
4341
  balanced: Object.freeze({
4327
4342
  bufferFactor: 1,
4328
4343
  correctionTauMs: 180,
4329
4344
  emaTauMs: 1800,
4330
4345
  minCharsPerSecond: 6,
4331
- drainMs: 240
4346
+ drainMs: 240,
4347
+ maxLagMs: 2500
4332
4348
  }),
4333
4349
  responsive: Object.freeze({
4334
4350
  bufferFactor: 0.45,
4335
4351
  correctionTauMs: 110,
4336
4352
  emaTauMs: 1100,
4337
4353
  minCharsPerSecond: 10,
4338
- drainMs: 150
4354
+ drainMs: 150,
4355
+ maxLagMs: 800
4339
4356
  })
4340
4357
  });
4341
- var MAX_TARGET_BUFFER_MS = 1200;
4358
+ var GAP_WINDOW = 16;
4359
+ var INTERVAL_QUANTILE = 0.9;
4360
+ var HORIZON_PAD_MS = 50;
4361
+ var DEADLINE_FLOOR_MS = 33;
4362
+ var DRAIN_CATCHUP_FACTOR = 2;
4363
+ var DRAIN_MAX_FACTOR = 3;
4342
4364
  var defaultNow = typeof performance !== "undefined" && typeof performance.now === "function" ? () => performance.now() : () => Date.now();
4343
4365
  var defaultSchedule = (cb) => {
4344
4366
  if (typeof requestAnimationFrame === "function") {
@@ -4389,18 +4411,18 @@ var createSmoothStreamController = (options = {}) => {
4389
4411
  let lastTickAt = 0;
4390
4412
  let cancelFrame;
4391
4413
  let disposed = false;
4392
- let rateEma;
4393
- let intervalEma;
4414
+ let gapWindow = [];
4394
4415
  let lastArrivalAt;
4395
4416
  const listeners = /* @__PURE__ */ new Set();
4396
4417
  const resolveParams = () => {
4397
- const preset = SMOOTH_STREAM_PACING_PRESETS[options.pacing] ?? SMOOTH_STREAM_PACING_PRESETS.balanced;
4418
+ const preset = Object.hasOwn(SMOOTH_STREAM_PACING_PRESETS, options.pacing) ? SMOOTH_STREAM_PACING_PRESETS[options.pacing] : SMOOTH_STREAM_PACING_PRESETS.balanced;
4398
4419
  return {
4399
4420
  bufferFactor: finiteOr(options.bufferFactor, preset.bufferFactor),
4400
4421
  correctionTauMs: finiteOr(options.correctionTauMs, preset.correctionTauMs),
4401
4422
  emaTauMs: finiteOr(options.emaTauMs, preset.emaTauMs),
4402
4423
  minCharsPerSecond: finiteOr(options.minCharsPerSecond, preset.minCharsPerSecond),
4403
- drainMs: finiteOr(options.drainMs, preset.drainMs)
4424
+ drainMs: finiteOr(options.drainMs, preset.drainMs),
4425
+ maxLagMs: finiteOr(options.maxLagMs, preset.maxLagMs ?? 2500)
4404
4426
  };
4405
4427
  };
4406
4428
  const recordArrival = (t, added) => {
@@ -4411,12 +4433,8 @@ var createSmoothStreamController = (options = {}) => {
4411
4433
  }
4412
4434
  const gap = Math.max(1, t - lastArrivalAt);
4413
4435
  lastArrivalAt = t;
4414
- const tau = Math.max(1, resolveParams().emaTauMs);
4415
- if (gap > tau) return;
4416
- const alpha = 1 - Math.exp(-gap / tau);
4417
- const instantRate = added * 1e3 / gap;
4418
- rateEma = (rateEma ?? 0) + alpha * (instantRate - (rateEma ?? 0));
4419
- intervalEma = (intervalEma ?? 0) + alpha * (gap - (intervalEma ?? 0));
4436
+ gapWindow.push({ gap, added });
4437
+ if (gapWindow.length > GAP_WINDOW) gapWindow.shift();
4420
4438
  };
4421
4439
  const notify = () => {
4422
4440
  visibleCache = source.slice(0, visibleEnd);
@@ -4442,15 +4460,12 @@ var createSmoothStreamController = (options = {}) => {
4442
4460
  let rate;
4443
4461
  if (finished && drainDeadlineAt !== void 0) {
4444
4462
  rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, drainDeadlineAt - t));
4445
- } else if (rateEma !== void 0 && intervalEma !== void 0) {
4446
- const targetBuffer = Math.max(
4447
- 1,
4448
- Math.min(params.bufferFactor * rateEma * (intervalEma / 1e3), rateEma * MAX_TARGET_BUFFER_MS / 1e3)
4449
- );
4450
- rate = Math.max(
4451
- params.minCharsPerSecond,
4452
- rateEma + (pending.length - targetBuffer) * 1e3 / Math.max(1, params.correctionTauMs)
4453
- );
4463
+ } else if (gapWindow.length > 0 && lastArrivalAt !== void 0) {
4464
+ const gaps = gapWindow.map((s) => s.gap).sort((a, b) => a - b);
4465
+ const intervalQ = gaps[Math.min(gaps.length - 1, Math.floor(gaps.length * INTERVAL_QUANTILE))];
4466
+ const horizon = Math.max(16, Math.min(params.bufferFactor * intervalQ + HORIZON_PAD_MS, params.maxLagMs));
4467
+ const deadline = Math.max(lastArrivalAt + horizon, t + DEADLINE_FLOOR_MS);
4468
+ rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, deadline - t));
4454
4469
  } else {
4455
4470
  rate = Math.max(params.minCharsPerSecond, pending.length * 1e3 / Math.max(1, params.correctionTauMs));
4456
4471
  }
@@ -4496,8 +4511,7 @@ var createSmoothStreamController = (options = {}) => {
4496
4511
  initialized = true;
4497
4512
  finished = false;
4498
4513
  drainDeadlineAt = void 0;
4499
- rateEma = void 0;
4500
- intervalEma = void 0;
4514
+ gapWindow = [];
4501
4515
  lastArrivalAt = void 0;
4502
4516
  source = next;
4503
4517
  visibleEnd = next.length;
@@ -4534,10 +4548,26 @@ var createSmoothStreamController = (options = {}) => {
4534
4548
  if (finished) return;
4535
4549
  finished = true;
4536
4550
  lastArrivalAt = void 0;
4537
- drainDeadlineAt = now() + resolveParams().drainMs;
4538
4551
  if (tentativeEnd > (pending.length > 0 ? pending[pending.length - 1] : visibleEnd)) {
4539
4552
  pending.push(tentativeEnd);
4540
4553
  }
4554
+ const params = resolveParams();
4555
+ let drainWin = params.drainMs;
4556
+ let chars = 0;
4557
+ let ms = 0;
4558
+ for (let i = 0; i < gapWindow.length - 1; i += 1) {
4559
+ chars += gapWindow[i].added;
4560
+ ms += gapWindow[i].gap;
4561
+ }
4562
+ const backlog = source.length - visibleEnd;
4563
+ if (chars > 0 && ms > 0 && backlog > 0) {
4564
+ const throughput = chars * 1e3 / ms;
4565
+ drainWin = Math.min(
4566
+ Math.max(backlog * 1e3 / (DRAIN_CATCHUP_FACTOR * throughput), params.drainMs),
4567
+ DRAIN_MAX_FACTOR * params.drainMs
4568
+ );
4569
+ }
4570
+ drainDeadlineAt = now() + drainWin;
4541
4571
  seam = source.length;
4542
4572
  ensureScheduled();
4543
4573
  },