@stream-mdx/worker 0.1.0 → 0.1.1

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/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @stream-mdx/worker
2
2
 
3
+ ## 0.1.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Refine streaming scheduling and list layout, add worker append batching/debug state support, and refresh docs/README examples.
8
+ - Updated dependencies
9
+ - @stream-mdx/core@0.1.1
10
+ - @stream-mdx/plugins@0.1.1
11
+
3
12
  ## 0.1.0
4
13
 
5
14
  ### Minor Changes
@@ -81968,6 +81968,12 @@ var documentPluginState = {};
81968
81968
  var txCounter = 0;
81969
81969
  var workerGrammarEngine = "js";
81970
81970
  var workerCredits = 1;
81971
+ var lastDiffSummary = null;
81972
+ var lastStructuralDiffSummary = null;
81973
+ var maxEmittedBlockCount = 0;
81974
+ var maxDeferredQueueSize = 0;
81975
+ var maxStructuralBlockCount = 0;
81976
+ var lastHighCountNoStructural = null;
81971
81977
  var MAX_DEFERRED_PATCHES = 400;
81972
81978
  var deferredPatchQueue = [];
81973
81979
  var MAX_DEFERRED_FLUSH_PATCHES = 120;
@@ -81984,6 +81990,50 @@ var MAX_WORKER_MDX_CACHE_ENTRIES = 128;
81984
81990
  var loggedMdxSkipCount = 0;
81985
81991
  var MAX_MDX_SKIP_LOGS = 20;
81986
81992
  var MIXED_CONTENT_AUTOCLOSE_NEWLINES = 2;
81993
+ var DEFAULT_APPEND_CHUNK_SIZE = 1400;
81994
+ var DEFAULT_APPEND_BATCH_MS = 12;
81995
+ var MIN_APPEND_CHUNK_SIZE = 256;
81996
+ var MAX_APPEND_CHUNK_SIZE = 12e3;
81997
+ var MIN_APPEND_BATCH_MS = 4;
81998
+ var MAX_APPEND_BATCH_MS = 50;
81999
+ var APPEND_NEWLINE_GRACE = 200;
82000
+ function clampInt(value, min, max) {
82001
+ if (!Number.isFinite(value)) return min;
82002
+ return Math.min(max, Math.max(min, Math.floor(value)));
82003
+ }
82004
+ function readNumericEnv(key2) {
82005
+ try {
82006
+ if (typeof process !== "undefined" && process.env && process.env[key2]) {
82007
+ const parsed = Number(process.env[key2]);
82008
+ if (Number.isFinite(parsed)) {
82009
+ return parsed;
82010
+ }
82011
+ }
82012
+ } catch {
82013
+ }
82014
+ return null;
82015
+ }
82016
+ var APPEND_CHUNK_SIZE = clampInt(
82017
+ readNumericEnv("NEXT_PUBLIC_STREAMING_APPEND_CHUNK") ?? DEFAULT_APPEND_CHUNK_SIZE,
82018
+ MIN_APPEND_CHUNK_SIZE,
82019
+ MAX_APPEND_CHUNK_SIZE
82020
+ );
82021
+ var APPEND_BATCH_MS = clampInt(
82022
+ readNumericEnv("NEXT_PUBLIC_STREAMING_APPEND_BATCH_MS") ?? DEFAULT_APPEND_BATCH_MS,
82023
+ MIN_APPEND_BATCH_MS,
82024
+ MAX_APPEND_BATCH_MS
82025
+ );
82026
+ function sliceAppendChunk(text12, maxChars) {
82027
+ if (text12.length <= maxChars) {
82028
+ return { chunk: text12, rest: "" };
82029
+ }
82030
+ const newlineIndex = text12.lastIndexOf("\n", maxChars);
82031
+ const cut = newlineIndex > 0 && maxChars - newlineIndex <= APPEND_NEWLINE_GRACE ? newlineIndex + 1 : maxChars;
82032
+ return { chunk: text12.slice(0, cut), rest: text12.slice(cut) };
82033
+ }
82034
+ function waitForNextTick() {
82035
+ return new Promise((resolve) => setTimeout(resolve, 0));
82036
+ }
81987
82037
  function isDebugEnabled(flag) {
81988
82038
  try {
81989
82039
  if (typeof process !== "undefined" && process.env) {
@@ -82191,7 +82241,23 @@ function partitionPatchesForCredits(patches, maxImmediate) {
82191
82241
  const immediate = [];
82192
82242
  const deferred = [];
82193
82243
  let heavyBudget = computeHeavyPatchBudget(workerCredits);
82244
+ let nonStructuralImmediate = 0;
82245
+ const isStructuralPatch = (patch5) => {
82246
+ switch (patch5.op) {
82247
+ case "insertChild":
82248
+ case "deleteChild":
82249
+ case "replaceChild":
82250
+ case "reorder":
82251
+ return true;
82252
+ default:
82253
+ return false;
82254
+ }
82255
+ };
82194
82256
  for (const patch5 of combined) {
82257
+ if (isStructuralPatch(patch5)) {
82258
+ immediate.push(patch5);
82259
+ continue;
82260
+ }
82195
82261
  const heavy = isHeavyPatch(patch5);
82196
82262
  if (heavy) {
82197
82263
  if (heavyBudget <= 0) {
@@ -82203,14 +82269,14 @@ function partitionPatchesForCredits(patches, maxImmediate) {
82203
82269
  heavyBudget -= 1;
82204
82270
  }
82205
82271
  }
82272
+ if (typeof maxImmediate === "number" && nonStructuralImmediate >= maxImmediate) {
82273
+ deferred.push(patch5);
82274
+ continue;
82275
+ }
82206
82276
  immediate.push(patch5);
82277
+ nonStructuralImmediate += 1;
82207
82278
  }
82208
- if (typeof maxImmediate === "number" && immediate.length > maxImmediate) {
82209
- const overflow = immediate.splice(maxImmediate);
82210
- deferredPatchQueue = overflow.concat(deferred);
82211
- } else {
82212
- deferredPatchQueue = deferred;
82213
- }
82279
+ deferredPatchQueue = deferred;
82214
82280
  return immediate;
82215
82281
  }
82216
82282
  function flushDeferredPatches() {
@@ -82354,25 +82420,35 @@ async function initialize(initialContent = "", prewarmLangs = [], docPlugins, md
82354
82420
  }
82355
82421
  }
82356
82422
  async function handleAppend(text12) {
82357
- performanceTimer.mark("append-operation");
82358
- const metricsCollector = new WorkerMetricsCollector(workerGrammarEngine);
82359
- setActiveMetricsCollector(metricsCollector);
82360
- await appendAndReparse(text12, metricsCollector);
82361
- const hadPatchMetrics = metricsCollector.patchCount > 0;
82362
- const totalTime = performanceTimer.measure("append-operation");
82363
- if (getActiveMetricsCollector() === metricsCollector) {
82364
- setActiveMetricsCollector(null);
82365
- }
82366
- if (!hadPatchMetrics && totalTime !== null && Number.isFinite(totalTime)) {
82367
- postMessage({
82368
- type: "METRICS",
82369
- metrics: {
82370
- parseMs: roundMetric(totalTime),
82371
- parseTime: roundMetric(totalTime),
82372
- blocksProduced: blocks.length,
82373
- grammarEngine: workerGrammarEngine
82374
- }
82375
- });
82423
+ let remaining = text12;
82424
+ let batchStartedAt = now();
82425
+ while (remaining.length > 0) {
82426
+ const { chunk, rest } = sliceAppendChunk(remaining, APPEND_CHUNK_SIZE);
82427
+ remaining = rest;
82428
+ performanceTimer.mark("append-operation");
82429
+ const metricsCollector = new WorkerMetricsCollector(workerGrammarEngine);
82430
+ setActiveMetricsCollector(metricsCollector);
82431
+ await appendAndReparse(chunk, metricsCollector);
82432
+ const hadPatchMetrics = metricsCollector.patchCount > 0;
82433
+ const totalTime = performanceTimer.measure("append-operation");
82434
+ if (getActiveMetricsCollector() === metricsCollector) {
82435
+ setActiveMetricsCollector(null);
82436
+ }
82437
+ if (!hadPatchMetrics && totalTime !== null && Number.isFinite(totalTime)) {
82438
+ postMessage({
82439
+ type: "METRICS",
82440
+ metrics: {
82441
+ parseMs: roundMetric(totalTime),
82442
+ parseTime: roundMetric(totalTime),
82443
+ blocksProduced: blocks.length,
82444
+ grammarEngine: workerGrammarEngine
82445
+ }
82446
+ });
82447
+ }
82448
+ if (remaining.length > 0 && now() - batchStartedAt >= APPEND_BATCH_MS) {
82449
+ await waitForNextTick();
82450
+ batchStartedAt = now();
82451
+ }
82376
82452
  }
82377
82453
  }
82378
82454
  async function parseAll(content4, options = {}) {
@@ -82395,9 +82471,24 @@ async function appendAndReparse(appendedText, metrics) {
82395
82471
  metrics?.markParseStart();
82396
82472
  const newContent = currentContent + appendedText;
82397
82473
  const changeRanges = computeChangedRanges(currentContent, newContent);
82398
- const newTree = lastTree ? parser.parse(newContent, lastTree.fragments) : parser.parse(newContent);
82474
+ let newTree = lastTree ? parser.parse(newContent, lastTree.fragments) : parser.parse(newContent);
82399
82475
  let changedBlocks = await extractBlocks(newTree, newContent);
82400
82476
  changedBlocks = runDocumentPlugins(changedBlocks, newContent);
82477
+ const lastRange = changedBlocks.length > 0 ? changedBlocks[changedBlocks.length - 1]?.payload?.range : void 0;
82478
+ const lastTo = typeof lastRange?.to === "number" ? lastRange.to : 0;
82479
+ if (lastTo < newContent.length - 1) {
82480
+ const tail = newContent.slice(lastTo).trim();
82481
+ if (tail.length > 0) {
82482
+ const fullTree = parser.parse(newContent);
82483
+ let fullBlocks = await extractBlocks(fullTree, newContent);
82484
+ fullBlocks = runDocumentPlugins(fullBlocks, newContent);
82485
+ const fullLast = fullBlocks.length > 0 ? fullBlocks[fullBlocks.length - 1]?.payload?.range?.to ?? 0 : 0;
82486
+ if (fullLast >= newContent.length - 1 || fullBlocks.length >= changedBlocks.length) {
82487
+ newTree = fullTree;
82488
+ changedBlocks = fullBlocks;
82489
+ }
82490
+ }
82491
+ }
82401
82492
  const prevBlocks = blocks;
82402
82493
  blocks = changedBlocks;
82403
82494
  lastTree = newTree;
@@ -83218,6 +83309,50 @@ async function emitBlockDiffPatches(previousBlocks, nextBlocks, changedRanges, m
83218
83309
  });
83219
83310
  }
83220
83311
  const immediatePatches = partitionPatchesForCredits(combined, paragraphLimit === null ? void 0 : paragraphLimit);
83312
+ if (deferredPatchQueue.length > maxDeferredQueueSize) {
83313
+ maxDeferredQueueSize = deferredPatchQueue.length;
83314
+ }
83315
+ let structuralCount = 0;
83316
+ for (const patch5 of patches) {
83317
+ if (patch5.op === "insertChild" || patch5.op === "deleteChild" || patch5.op === "replaceChild" || patch5.op === "reorder") {
83318
+ structuralCount += 1;
83319
+ }
83320
+ }
83321
+ lastDiffSummary = {
83322
+ prevCount: previousBlocks.length,
83323
+ nextCount: nextBlocks.length,
83324
+ prefix,
83325
+ removeCount,
83326
+ addCount,
83327
+ structuralPatches: structuralCount,
83328
+ contentPatches: contentPatches.length,
83329
+ immediatePatches: immediatePatches.length,
83330
+ deferredQueue: deferredPatchQueue.length
83331
+ };
83332
+ if (addCount > 0 || removeCount > 0 || structuralCount > 0 || deferredPatchQueue.length > 0) {
83333
+ lastStructuralDiffSummary = { ...lastDiffSummary };
83334
+ }
83335
+ if (immediatePatches.length > 0) {
83336
+ maxEmittedBlockCount = Math.max(maxEmittedBlockCount, nextBlocks.length);
83337
+ }
83338
+ if (structuralCount > 0) {
83339
+ maxStructuralBlockCount = Math.max(maxStructuralBlockCount, nextBlocks.length);
83340
+ }
83341
+ if (nextBlocks.length >= 60 && removeCount === 0 && addCount === 0) {
83342
+ const sliceStart = Math.max(0, Math.min(55, nextBlocks.length - 1));
83343
+ const sliceEnd = Math.min(nextBlocks.length, sliceStart + 8);
83344
+ const toPreview = (block) => ({
83345
+ id: block.id,
83346
+ type: block.type,
83347
+ raw: typeof block.payload.raw === "string" ? block.payload.raw.slice(0, 80) : ""
83348
+ });
83349
+ lastHighCountNoStructural = {
83350
+ nextCount: nextBlocks.length,
83351
+ sliceStart,
83352
+ prevSlice: previousBlocks.slice(sliceStart, sliceEnd).map(toPreview),
83353
+ nextSlice: nextBlocks.slice(sliceStart, sliceEnd).map(toPreview)
83354
+ };
83355
+ }
83221
83356
  if (immediatePatches.length === 0) {
83222
83357
  if (metrics) {
83223
83358
  metrics.finalizePatch(txCounter, 0, deferredPatchQueue.length, 0);
@@ -83786,6 +83921,104 @@ async function processWorkerMessage(msg) {
83786
83921
  case "FINALIZE":
83787
83922
  await finalizeAllBlocks();
83788
83923
  return;
83924
+ case "DEBUG_STATE": {
83925
+ const blockTypeCounts = {};
83926
+ for (const block of blocks) {
83927
+ const key2 = block.type ?? "unknown";
83928
+ blockTypeCounts[key2] = (blockTypeCounts[key2] || 0) + 1;
83929
+ }
83930
+ let lastBlockType;
83931
+ let lastBlockRange;
83932
+ let lastBlockRawTail;
83933
+ const headingTexts = [];
83934
+ const tailBlocks = [];
83935
+ const headBlocks = [];
83936
+ const duplicateBlockIds = [];
83937
+ const seenBlockIds = /* @__PURE__ */ new Set();
83938
+ const headingIndices = {};
83939
+ for (const block of blocks) {
83940
+ if (seenBlockIds.has(block.id)) {
83941
+ if (duplicateBlockIds.length < 8) {
83942
+ duplicateBlockIds.push(block.id);
83943
+ }
83944
+ } else {
83945
+ seenBlockIds.add(block.id);
83946
+ }
83947
+ }
83948
+ if (blocks.length > 0) {
83949
+ const lastBlock = blocks[blocks.length - 1];
83950
+ lastBlockType = lastBlock.type;
83951
+ const range2 = lastBlock.payload.range;
83952
+ if (range2 && typeof range2.from === "number" && typeof range2.to === "number") {
83953
+ lastBlockRange = { from: range2.from, to: range2.to };
83954
+ }
83955
+ const raw2 = typeof lastBlock.payload.raw === "string" ? lastBlock.payload.raw : "";
83956
+ lastBlockRawTail = raw2 ? raw2.slice(Math.max(0, raw2.length - 240)) : void 0;
83957
+ for (const block of blocks) {
83958
+ if (block.type === "heading" && typeof block.payload.raw === "string") {
83959
+ headingTexts.push(block.payload.raw);
83960
+ }
83961
+ }
83962
+ const targets = ["HTML and MDX Testing", "Inline Code", "Code Blocks", "Media", "Tables", "Footnotes"];
83963
+ for (let i = 0; i < blocks.length; i++) {
83964
+ const block = blocks[i];
83965
+ if (block.type === "heading" && typeof block.payload.raw === "string") {
83966
+ if (targets.includes(block.payload.raw)) {
83967
+ headingIndices[block.payload.raw] = i;
83968
+ }
83969
+ }
83970
+ }
83971
+ const tailStart = Math.max(0, blocks.length - 8);
83972
+ for (let i = tailStart; i < blocks.length; i++) {
83973
+ const block = blocks[i];
83974
+ const raw3 = typeof block.payload.raw === "string" ? block.payload.raw : "";
83975
+ tailBlocks.push({
83976
+ id: block.id,
83977
+ type: block.type,
83978
+ raw: raw3.slice(0, 120)
83979
+ });
83980
+ }
83981
+ const headEnd = Math.min(8, blocks.length);
83982
+ for (let i = 0; i < headEnd; i++) {
83983
+ const block = blocks[i];
83984
+ const raw3 = typeof block.payload.raw === "string" ? block.payload.raw : "";
83985
+ headBlocks.push({
83986
+ id: block.id,
83987
+ type: block.type,
83988
+ raw: raw3.slice(0, 120)
83989
+ });
83990
+ }
83991
+ }
83992
+ const contentTail = currentContent.slice(Math.max(0, currentContent.length - 500));
83993
+ postMessage({
83994
+ type: "DEBUG_STATE",
83995
+ state: {
83996
+ contentLength: currentContent.length,
83997
+ contentTail,
83998
+ blockCount: blocks.length,
83999
+ blockTypeCounts,
84000
+ lastBlockType,
84001
+ lastBlockRange,
84002
+ lastBlockRawTail,
84003
+ headingTexts,
84004
+ headBlocks,
84005
+ tailBlocks,
84006
+ headingIndices,
84007
+ lastDiffSummary,
84008
+ lastStructuralDiffSummary,
84009
+ maxEmittedBlockCount,
84010
+ maxDeferredQueueSize,
84011
+ maxStructuralBlockCount,
84012
+ lastHighCountNoStructural,
84013
+ duplicateBlockIds,
84014
+ duplicateBlockCount: duplicateBlockIds.length,
84015
+ hasInlineCodeHeading: currentContent.includes("# Inline Code"),
84016
+ hasCodeBlocksHeading: currentContent.includes("# Code Blocks"),
84017
+ hasMediaHeading: currentContent.includes("# Media")
84018
+ }
84019
+ });
84020
+ return;
84021
+ }
83789
84022
  case "MDX_COMPILED":
83790
84023
  handleMdxStatus(msg.blockId, {
83791
84024
  compiledRef: { id: msg.compiledId },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stream-mdx/worker",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Worker client utilities and shared worker helpers for the Streaming Markdown V2 pipeline",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -63,8 +63,8 @@
63
63
  "@lezer/common": "^1.2.3",
64
64
  "@lezer/lr": "^1.4.2",
65
65
  "@lezer/markdown": "^1.3.0",
66
- "@stream-mdx/core": "0.1.0",
67
- "@stream-mdx/plugins": "0.1.0",
66
+ "@stream-mdx/core": "0.1.1",
67
+ "@stream-mdx/plugins": "0.1.1",
68
68
  "@mdx-js/mdx": "^3.1.0",
69
69
  "@shikijs/engine-javascript": "^1.29.2",
70
70
  "@shikijs/engine-oniguruma": "^1.29.2",