@stream-mdx/react 0.4.0 → 0.5.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/CHANGELOG.md +14 -4
- package/README.md +65 -15
- package/dist/components/bottom-stick-scroll-area.cjs +344 -0
- package/dist/components/bottom-stick-scroll-area.d.cts +36 -0
- package/dist/components/bottom-stick-scroll-area.d.ts +36 -0
- package/dist/components/bottom-stick-scroll-area.mjs +320 -0
- package/dist/components/index.cjs +503 -172
- package/dist/components/index.d.cts +3 -1
- package/dist/components/index.d.ts +3 -1
- package/dist/components/index.mjs +350 -22
- package/dist/{index-D2MYusAM.d.cts → index-BVhp_pcF.d.ts} +3 -0
- package/dist/{index-D2MYusAM.d.ts → index-EaeouYp2.d.cts} +3 -0
- package/dist/index.cjs +1472 -486
- package/dist/index.d.cts +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.mjs +1256 -272
- package/dist/mdx-client.cjs +1 -1
- package/dist/mdx-client.mjs +1 -1
- package/dist/mdx-coordinator.cjs +28 -7
- package/dist/mdx-coordinator.d.cts +2 -2
- package/dist/mdx-coordinator.d.ts +2 -2
- package/dist/mdx-coordinator.mjs +28 -7
- package/dist/renderer/node-views.cjs +271 -26
- package/dist/renderer/node-views.d.cts +3 -1
- package/dist/renderer/node-views.d.ts +3 -1
- package/dist/renderer/node-views.mjs +272 -27
- package/dist/renderer/patch-batching.cjs +2 -0
- package/dist/renderer/patch-batching.d.cts +1 -1
- package/dist/renderer/patch-batching.d.ts +1 -1
- package/dist/renderer/patch-batching.mjs +2 -1
- package/dist/renderer/patch-commit-scheduler.cjs +82 -30
- package/dist/renderer/patch-commit-scheduler.d.cts +15 -3
- package/dist/renderer/patch-commit-scheduler.d.ts +15 -3
- package/dist/renderer/patch-commit-scheduler.mjs +84 -30
- package/dist/renderer/store.cjs +382 -36
- package/dist/renderer/store.d.cts +3 -0
- package/dist/renderer/store.d.ts +3 -0
- package/dist/renderer/store.mjs +382 -36
- package/dist/renderer.cjs +758 -114
- package/dist/renderer.d.cts +3 -1
- package/dist/renderer.d.ts +3 -1
- package/dist/renderer.mjs +760 -116
- package/dist/server.cjs +669 -71
- package/dist/server.d.cts +5 -0
- package/dist/server.d.ts +5 -0
- package/dist/server.mjs +670 -72
- package/dist/{streaming-markdown-DOc2uLiP.d.ts → streaming-markdown-CEI9eDZm.d.ts} +3 -1
- package/dist/{streaming-markdown-D-JcByd9.d.cts → streaming-markdown-DRisClXb.d.cts} +3 -1
- package/dist/streaming-markdown.cjs +788 -121
- package/dist/streaming-markdown.d.cts +4 -2
- package/dist/streaming-markdown.d.ts +4 -2
- package/dist/streaming-markdown.mjs +790 -123
- package/dist/utils/inline-html.d.cts +3 -1
- package/dist/utils/inline-html.d.ts +3 -1
- package/package.json +14 -3
- package/shadcn/README.md +28 -0
- package/shadcn/bottom-stick-scroll-area.tsx +358 -0
|
@@ -24,6 +24,7 @@ __export(patch_commit_scheduler_exports, {
|
|
|
24
24
|
PatchCommitScheduler: () => PatchCommitScheduler
|
|
25
25
|
});
|
|
26
26
|
module.exports = __toCommonJS(patch_commit_scheduler_exports);
|
|
27
|
+
var import_patch_batching = require("@stream-mdx/core/perf/patch-batching");
|
|
27
28
|
|
|
28
29
|
// src/renderer/patch-coalescing.ts
|
|
29
30
|
var import_core = require("@stream-mdx/core");
|
|
@@ -35,6 +36,9 @@ var COALESCING_DURATION_ACTIVATE_MS = 6;
|
|
|
35
36
|
var COALESCING_DURATION_DEACTIVATE_MS = 4;
|
|
36
37
|
var RETAIN_TOUCHED_IN_HISTORY = typeof process !== "undefined" && process.env ? process.env.NODE_ENV !== "production" : true;
|
|
37
38
|
var EMPTY_TOUCHED_SET = /* @__PURE__ */ new Set();
|
|
39
|
+
function shouldSkipCoalescing(patches) {
|
|
40
|
+
return patches.length <= 1;
|
|
41
|
+
}
|
|
38
42
|
function getDefaultNow() {
|
|
39
43
|
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
40
44
|
return () => performance.now();
|
|
@@ -61,8 +65,9 @@ function getDefaultCancelRaf() {
|
|
|
61
65
|
}
|
|
62
66
|
var PatchCommitScheduler = class {
|
|
63
67
|
constructor(params) {
|
|
64
|
-
this.
|
|
65
|
-
this.
|
|
68
|
+
this.semanticQueue = [];
|
|
69
|
+
this.enrichmentHighQueue = [];
|
|
70
|
+
this.enrichmentLowQueue = [];
|
|
66
71
|
this.scheduled = false;
|
|
67
72
|
this.flushing = false;
|
|
68
73
|
this.scheduledHandle = null;
|
|
@@ -74,6 +79,7 @@ var PatchCommitScheduler = class {
|
|
|
74
79
|
this.scheduleToken = 0;
|
|
75
80
|
this.coalescingDurationSamples = [];
|
|
76
81
|
this.adaptiveBudgetActive = false;
|
|
82
|
+
this.flushCount = 0;
|
|
77
83
|
this.store = params.store;
|
|
78
84
|
this.onFlush = params.onFlush;
|
|
79
85
|
this.frameBudgetMs = Math.max(1, params.options?.frameBudgetMs ?? 10);
|
|
@@ -89,6 +95,8 @@ var PatchCommitScheduler = class {
|
|
|
89
95
|
this.maxLowPriorityBatchesPerFlush = params.options?.maxLowPriorityBatchesPerFlush ?? 2;
|
|
90
96
|
this.urgentQueueThreshold = Math.max(1, params.options?.urgentQueueThreshold ?? 4);
|
|
91
97
|
this.historyLimit = Math.max(1, params.options?.historyLimit ?? 200);
|
|
98
|
+
this.startupMicrotaskFlushes = Math.max(0, Math.floor(params.options?.startupMicrotaskFlushes ?? 0));
|
|
99
|
+
this.adaptiveBudgeting = params.options?.adaptiveBudgeting ?? true;
|
|
92
100
|
const requestedBatch = params.options?.batch;
|
|
93
101
|
if (requestedBatch === "microtask") {
|
|
94
102
|
this.batchStrategy = "microtask";
|
|
@@ -115,19 +123,23 @@ var PatchCommitScheduler = class {
|
|
|
115
123
|
patches: input.patches,
|
|
116
124
|
meta: input.meta ?? {},
|
|
117
125
|
receivedAt: input.meta?.receivedAt ?? this.now(),
|
|
126
|
+
kind: input.meta?.kind === "semantic" || input.meta?.kind === "enrichment" ? input.meta.kind : input.patches.some((patch) => (0, import_patch_batching.getPatchKind)(patch) === "semantic") ? "semantic" : "enrichment",
|
|
118
127
|
priority: input.meta?.priority === "low" ? "low" : "high"
|
|
119
128
|
};
|
|
120
|
-
if (batch.
|
|
121
|
-
|
|
129
|
+
if (batch.kind === "semantic") {
|
|
130
|
+
batch.priority = "high";
|
|
131
|
+
this.semanticQueue.push(batch);
|
|
132
|
+
} else if (batch.priority === "low") {
|
|
133
|
+
this.enrichmentLowQueue.push(batch);
|
|
122
134
|
} else {
|
|
123
|
-
this.
|
|
135
|
+
this.enrichmentHighQueue.push(batch);
|
|
124
136
|
}
|
|
125
137
|
if (!this.paused) {
|
|
126
138
|
this.schedule();
|
|
127
139
|
}
|
|
128
140
|
}
|
|
129
141
|
flushAll() {
|
|
130
|
-
if (this.
|
|
142
|
+
if (this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0 && !this.flushing) {
|
|
131
143
|
return null;
|
|
132
144
|
}
|
|
133
145
|
this.cancelScheduled();
|
|
@@ -137,9 +149,11 @@ var PatchCommitScheduler = class {
|
|
|
137
149
|
}
|
|
138
150
|
clear() {
|
|
139
151
|
this.cancelScheduled();
|
|
140
|
-
this.
|
|
141
|
-
this.
|
|
152
|
+
this.semanticQueue = [];
|
|
153
|
+
this.enrichmentHighQueue = [];
|
|
154
|
+
this.enrichmentLowQueue = [];
|
|
142
155
|
this.flushing = false;
|
|
156
|
+
this.flushCount = 0;
|
|
143
157
|
this.coalescingDurationSamples = [];
|
|
144
158
|
this.adaptiveBudgetActive = false;
|
|
145
159
|
this.adaptiveHighBatchCap = void 0;
|
|
@@ -147,10 +161,10 @@ var PatchCommitScheduler = class {
|
|
|
147
161
|
this.maybeResolveIdle();
|
|
148
162
|
}
|
|
149
163
|
getPendingCount() {
|
|
150
|
-
return this.
|
|
164
|
+
return this.semanticQueue.length + this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
|
|
151
165
|
}
|
|
152
166
|
isIdle() {
|
|
153
|
-
return !this.flushing && !this.scheduled && this.
|
|
167
|
+
return !this.flushing && !this.scheduled && this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0;
|
|
154
168
|
}
|
|
155
169
|
awaitIdle() {
|
|
156
170
|
if (this.isIdle()) {
|
|
@@ -190,7 +204,8 @@ var PatchCommitScheduler = class {
|
|
|
190
204
|
this.runFlush(this.frameBudgetMs, false);
|
|
191
205
|
this.maybeResolveIdle();
|
|
192
206
|
};
|
|
193
|
-
|
|
207
|
+
const useStartupMicrotask = this.batchStrategy !== "microtask" && this.startupMicrotaskFlushes > 0 && this.flushCount < this.startupMicrotaskFlushes && typeof queueMicrotask === "function";
|
|
208
|
+
if ((this.batchStrategy === "microtask" || useStartupMicrotask) && typeof queueMicrotask === "function") {
|
|
194
209
|
this.scheduledViaRaf = false;
|
|
195
210
|
this.scheduledHandle = token;
|
|
196
211
|
queueMicrotask(execute);
|
|
@@ -220,12 +235,14 @@ var PatchCommitScheduler = class {
|
|
|
220
235
|
if (this.paused && !manual) {
|
|
221
236
|
return null;
|
|
222
237
|
}
|
|
223
|
-
if (this.
|
|
238
|
+
if (this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0) {
|
|
224
239
|
return null;
|
|
225
240
|
}
|
|
226
241
|
this.flushing = true;
|
|
227
242
|
const startedAt = this.now();
|
|
228
|
-
const
|
|
243
|
+
const initialSemanticQueueSize = this.semanticQueue.length;
|
|
244
|
+
const initialEnrichmentQueueSize = this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
|
|
245
|
+
const initialQueueSize = initialSemanticQueueSize + initialEnrichmentQueueSize;
|
|
229
246
|
const batches = [];
|
|
230
247
|
let totalInputPatches = 0;
|
|
231
248
|
let totalAppliedPatches = 0;
|
|
@@ -240,7 +257,15 @@ var PatchCommitScheduler = class {
|
|
|
240
257
|
consumed += 1;
|
|
241
258
|
const applyStart = this.now();
|
|
242
259
|
const queueDelayMs = Math.max(0, applyStart - next.receivedAt);
|
|
243
|
-
|
|
260
|
+
let coalesced;
|
|
261
|
+
let coalescingMetrics;
|
|
262
|
+
if (shouldSkipCoalescing(next.patches)) {
|
|
263
|
+
coalesced = next.patches;
|
|
264
|
+
} else {
|
|
265
|
+
const result = (0, import_core.coalescePatchesWithMetrics)(next.patches, import_core.DEFAULT_COALESCE_CONFIG);
|
|
266
|
+
coalesced = result.patches;
|
|
267
|
+
coalescingMetrics = result.metrics;
|
|
268
|
+
}
|
|
244
269
|
const touched = this.store.applyPatches(coalesced, {
|
|
245
270
|
coalesced: true,
|
|
246
271
|
metrics: coalescingMetrics
|
|
@@ -260,6 +285,7 @@ var PatchCommitScheduler = class {
|
|
|
260
285
|
receivedAt: next.receivedAt,
|
|
261
286
|
appliedAt: applyStart,
|
|
262
287
|
priority: next.priority,
|
|
288
|
+
kind: next.kind,
|
|
263
289
|
coalescing: coalescingMetrics
|
|
264
290
|
});
|
|
265
291
|
if (maxBatches && batches.length - startingBatchCount >= maxBatches) {
|
|
@@ -276,27 +302,32 @@ var PatchCommitScheduler = class {
|
|
|
276
302
|
queue.splice(0, consumed);
|
|
277
303
|
}
|
|
278
304
|
};
|
|
279
|
-
|
|
305
|
+
processQueue(this.semanticQueue, void 0, Number.POSITIVE_INFINITY);
|
|
306
|
+
const initialHighQueueSize = this.enrichmentHighQueue.length;
|
|
280
307
|
const configuredHighLimit = manual || !this.maxBatchesPerFlush ? void 0 : initialHighQueueSize > this.maxBatchesPerFlush ? void 0 : this.maxBatchesPerFlush;
|
|
281
|
-
const highBatchLimit = this.adaptiveHighBatchCap ?? configuredHighLimit;
|
|
282
|
-
processQueue(this.
|
|
308
|
+
const highBatchLimit = this.adaptiveBudgeting ? this.adaptiveHighBatchCap ?? configuredHighLimit : configuredHighLimit;
|
|
309
|
+
processQueue(this.enrichmentHighQueue, highBatchLimit, budgetMs);
|
|
283
310
|
if (manual) {
|
|
284
|
-
processQueue(this.
|
|
285
|
-
} else if (this.
|
|
311
|
+
processQueue(this.enrichmentLowQueue, void 0, Number.POSITIVE_INFINITY);
|
|
312
|
+
} else if (this.enrichmentLowQueue.length > 0) {
|
|
286
313
|
elapsed = this.now() - startedAt;
|
|
287
314
|
const budgetForLow = Math.max(0, this.lowPriorityFrameBudgetMs - elapsed);
|
|
288
|
-
|
|
289
|
-
|
|
315
|
+
const processedHigherPriorityWork = batches.length > 0;
|
|
316
|
+
if (budgetForLow > 0 || !processedHigherPriorityWork) {
|
|
317
|
+
const initialLowQueueSize = this.enrichmentLowQueue.length;
|
|
290
318
|
const configuredLowLimit = manual || !this.maxLowPriorityBatchesPerFlush ? void 0 : initialLowQueueSize > this.maxLowPriorityBatchesPerFlush ? void 0 : this.maxLowPriorityBatchesPerFlush;
|
|
291
|
-
const lowBatchLimit = this.adaptiveLowBatchCap ?? configuredLowLimit;
|
|
292
|
-
processQueue(this.
|
|
319
|
+
const lowBatchLimit = this.adaptiveBudgeting ? this.adaptiveLowBatchCap ?? configuredLowLimit : configuredLowLimit;
|
|
320
|
+
processQueue(this.enrichmentLowQueue, lowBatchLimit, budgetForLow);
|
|
293
321
|
}
|
|
294
322
|
}
|
|
295
323
|
this.flushing = false;
|
|
296
324
|
const completedAt = this.now();
|
|
297
325
|
const totalDurationMs = completedAt - startedAt;
|
|
298
|
-
const
|
|
326
|
+
const remainingSemantic = this.semanticQueue.length;
|
|
327
|
+
const remainingEnrichment = this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
|
|
328
|
+
const remaining = remainingSemantic + remainingEnrichment;
|
|
299
329
|
if (batches.length > 0) {
|
|
330
|
+
this.flushCount += 1;
|
|
300
331
|
try {
|
|
301
332
|
this.onFlush({
|
|
302
333
|
batches,
|
|
@@ -304,7 +335,11 @@ var PatchCommitScheduler = class {
|
|
|
304
335
|
totalAppliedPatches,
|
|
305
336
|
totalDurationMs,
|
|
306
337
|
remainingQueueSize: remaining,
|
|
338
|
+
remainingSemanticQueueSize: remainingSemantic,
|
|
339
|
+
remainingEnrichmentQueueSize: remainingEnrichment,
|
|
307
340
|
queueDepthBefore: initialQueueSize,
|
|
341
|
+
semanticQueueDepthBefore: initialSemanticQueueSize,
|
|
342
|
+
enrichmentQueueDepthBefore: initialEnrichmentQueueSize,
|
|
308
343
|
flushStartedAt: startedAt,
|
|
309
344
|
flushCompletedAt: completedAt
|
|
310
345
|
});
|
|
@@ -315,21 +350,27 @@ var PatchCommitScheduler = class {
|
|
|
315
350
|
if (remaining > 0 && !this.paused) {
|
|
316
351
|
this.schedule();
|
|
317
352
|
}
|
|
318
|
-
const coalescingStats = this.computeCoalescingDurationStats();
|
|
319
|
-
this.
|
|
320
|
-
|
|
353
|
+
const coalescingStats = this.adaptiveBudgeting ? this.computeCoalescingDurationStats() : { sampleCount: this.coalescingDurationSamples.length, p95: null };
|
|
354
|
+
if (this.adaptiveBudgeting) {
|
|
355
|
+
this.updateAdaptiveBudget(coalescingStats.p95);
|
|
356
|
+
}
|
|
357
|
+
const adaptiveState = this.adaptiveBudgeting ? this.snapshotAdaptiveBudget(coalescingStats.p95 ?? null) : void 0;
|
|
321
358
|
const flushResult = batches.length > 0 ? {
|
|
322
359
|
batches,
|
|
323
360
|
totalPatches: totalInputPatches,
|
|
324
361
|
totalAppliedPatches,
|
|
325
362
|
totalDurationMs,
|
|
326
363
|
remainingQueueSize: remaining,
|
|
364
|
+
remainingSemanticQueueSize: remainingSemantic,
|
|
365
|
+
remainingEnrichmentQueueSize: remainingEnrichment,
|
|
327
366
|
queueDepthBefore: initialQueueSize,
|
|
367
|
+
semanticQueueDepthBefore: initialSemanticQueueSize,
|
|
368
|
+
enrichmentQueueDepthBefore: initialEnrichmentQueueSize,
|
|
328
369
|
flushStartedAt: startedAt,
|
|
329
370
|
flushCompletedAt: completedAt,
|
|
330
371
|
coalescingDurationP95: coalescingStats.p95 ?? void 0,
|
|
331
372
|
coalescingDurationSampleCount: coalescingStats.sampleCount,
|
|
332
|
-
adaptiveBudgetActive: adaptiveState
|
|
373
|
+
adaptiveBudgetActive: adaptiveState?.active,
|
|
333
374
|
adaptiveBudgetState: adaptiveState
|
|
334
375
|
} : null;
|
|
335
376
|
if (flushResult) {
|
|
@@ -365,6 +406,9 @@ var PatchCommitScheduler = class {
|
|
|
365
406
|
};
|
|
366
407
|
}
|
|
367
408
|
updateAdaptiveBudget(p95) {
|
|
409
|
+
if (!this.adaptiveBudgeting) {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
368
412
|
if (p95 !== null && p95 > COALESCING_DURATION_ACTIVATE_MS) {
|
|
369
413
|
if (!this.adaptiveBudgetActive) {
|
|
370
414
|
this.adaptiveBudgetActive = true;
|
|
@@ -438,11 +482,13 @@ var PatchCommitScheduler = class {
|
|
|
438
482
|
}
|
|
439
483
|
restart() {
|
|
440
484
|
this.cancelScheduled();
|
|
441
|
-
this.
|
|
442
|
-
this.
|
|
485
|
+
this.semanticQueue = [];
|
|
486
|
+
this.enrichmentHighQueue = [];
|
|
487
|
+
this.enrichmentLowQueue = [];
|
|
443
488
|
this.flushing = false;
|
|
444
489
|
this.sequence = 0;
|
|
445
490
|
this.history = [];
|
|
491
|
+
this.flushCount = 0;
|
|
446
492
|
this.scheduleToken++;
|
|
447
493
|
this.paused = false;
|
|
448
494
|
this.coalescingDurationSamples = [];
|
|
@@ -489,6 +535,12 @@ var PatchCommitScheduler = class {
|
|
|
489
535
|
if (typeof options.historyLimit !== "undefined") {
|
|
490
536
|
this.setHistoryLimit(options.historyLimit);
|
|
491
537
|
}
|
|
538
|
+
if (typeof options.startupMicrotaskFlushes !== "undefined") {
|
|
539
|
+
this.startupMicrotaskFlushes = Math.max(0, Math.floor(options.startupMicrotaskFlushes));
|
|
540
|
+
}
|
|
541
|
+
if (typeof options.adaptiveBudgeting !== "undefined") {
|
|
542
|
+
this.adaptiveBudgeting = options.adaptiveBudgeting;
|
|
543
|
+
}
|
|
492
544
|
if (typeof options.batch !== "undefined") {
|
|
493
545
|
const requestedBatch = options.batch;
|
|
494
546
|
if (requestedBatch === "microtask") {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PatchMetrics, CoalescingMetrics, Patch } from '@stream-mdx/core';
|
|
1
|
+
import { PatchMetrics, PatchKind, CoalescingMetrics, Patch } from '@stream-mdx/core';
|
|
2
2
|
import { RendererStore } from './store.cjs';
|
|
3
3
|
|
|
4
4
|
type NowFn = () => number;
|
|
@@ -21,6 +21,7 @@ interface PatchBatchMeta {
|
|
|
21
21
|
metrics?: PatchMetrics;
|
|
22
22
|
receivedAt?: number;
|
|
23
23
|
priority?: "high" | "low";
|
|
24
|
+
kind?: PatchKind;
|
|
24
25
|
}
|
|
25
26
|
interface PatchBatchInput {
|
|
26
27
|
patches: Patch[];
|
|
@@ -37,6 +38,7 @@ interface PatchFlushBatchResult {
|
|
|
37
38
|
receivedAt: number;
|
|
38
39
|
appliedAt: number;
|
|
39
40
|
priority: "high" | "low";
|
|
41
|
+
kind: PatchKind;
|
|
40
42
|
coalescing?: CoalescingMetrics;
|
|
41
43
|
}
|
|
42
44
|
interface PatchFlushResult {
|
|
@@ -45,7 +47,11 @@ interface PatchFlushResult {
|
|
|
45
47
|
totalAppliedPatches: number;
|
|
46
48
|
totalDurationMs: number;
|
|
47
49
|
remainingQueueSize: number;
|
|
50
|
+
remainingSemanticQueueSize: number;
|
|
51
|
+
remainingEnrichmentQueueSize: number;
|
|
48
52
|
queueDepthBefore: number;
|
|
53
|
+
semanticQueueDepthBefore: number;
|
|
54
|
+
enrichmentQueueDepthBefore: number;
|
|
49
55
|
flushStartedAt: number;
|
|
50
56
|
flushCompletedAt: number;
|
|
51
57
|
coalescingDurationP95?: number | null;
|
|
@@ -65,6 +71,8 @@ interface PatchCommitSchedulerOptions {
|
|
|
65
71
|
urgentQueueThreshold?: number;
|
|
66
72
|
batch?: "rAF" | "microtask" | "timeout";
|
|
67
73
|
historyLimit?: number;
|
|
74
|
+
startupMicrotaskFlushes?: number;
|
|
75
|
+
adaptiveBudgeting?: boolean;
|
|
68
76
|
}
|
|
69
77
|
declare class PatchCommitScheduler {
|
|
70
78
|
private readonly store;
|
|
@@ -78,8 +86,10 @@ declare class PatchCommitScheduler {
|
|
|
78
86
|
private lowPriorityFrameBudgetMs;
|
|
79
87
|
private maxLowPriorityBatchesPerFlush?;
|
|
80
88
|
private urgentQueueThreshold;
|
|
81
|
-
private
|
|
82
|
-
private
|
|
89
|
+
private adaptiveBudgeting;
|
|
90
|
+
private semanticQueue;
|
|
91
|
+
private enrichmentHighQueue;
|
|
92
|
+
private enrichmentLowQueue;
|
|
83
93
|
private scheduled;
|
|
84
94
|
private flushing;
|
|
85
95
|
private scheduledHandle;
|
|
@@ -95,6 +105,8 @@ declare class PatchCommitScheduler {
|
|
|
95
105
|
private adaptiveBudgetActive;
|
|
96
106
|
private adaptiveHighBatchCap;
|
|
97
107
|
private adaptiveLowBatchCap;
|
|
108
|
+
private startupMicrotaskFlushes;
|
|
109
|
+
private flushCount;
|
|
98
110
|
constructor(params: {
|
|
99
111
|
store: RendererStore;
|
|
100
112
|
onFlush: (result: PatchFlushResult) => void;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { PatchMetrics, CoalescingMetrics, Patch } from '@stream-mdx/core';
|
|
1
|
+
import { PatchMetrics, PatchKind, CoalescingMetrics, Patch } from '@stream-mdx/core';
|
|
2
2
|
import { RendererStore } from './store.js';
|
|
3
3
|
|
|
4
4
|
type NowFn = () => number;
|
|
@@ -21,6 +21,7 @@ interface PatchBatchMeta {
|
|
|
21
21
|
metrics?: PatchMetrics;
|
|
22
22
|
receivedAt?: number;
|
|
23
23
|
priority?: "high" | "low";
|
|
24
|
+
kind?: PatchKind;
|
|
24
25
|
}
|
|
25
26
|
interface PatchBatchInput {
|
|
26
27
|
patches: Patch[];
|
|
@@ -37,6 +38,7 @@ interface PatchFlushBatchResult {
|
|
|
37
38
|
receivedAt: number;
|
|
38
39
|
appliedAt: number;
|
|
39
40
|
priority: "high" | "low";
|
|
41
|
+
kind: PatchKind;
|
|
40
42
|
coalescing?: CoalescingMetrics;
|
|
41
43
|
}
|
|
42
44
|
interface PatchFlushResult {
|
|
@@ -45,7 +47,11 @@ interface PatchFlushResult {
|
|
|
45
47
|
totalAppliedPatches: number;
|
|
46
48
|
totalDurationMs: number;
|
|
47
49
|
remainingQueueSize: number;
|
|
50
|
+
remainingSemanticQueueSize: number;
|
|
51
|
+
remainingEnrichmentQueueSize: number;
|
|
48
52
|
queueDepthBefore: number;
|
|
53
|
+
semanticQueueDepthBefore: number;
|
|
54
|
+
enrichmentQueueDepthBefore: number;
|
|
49
55
|
flushStartedAt: number;
|
|
50
56
|
flushCompletedAt: number;
|
|
51
57
|
coalescingDurationP95?: number | null;
|
|
@@ -65,6 +71,8 @@ interface PatchCommitSchedulerOptions {
|
|
|
65
71
|
urgentQueueThreshold?: number;
|
|
66
72
|
batch?: "rAF" | "microtask" | "timeout";
|
|
67
73
|
historyLimit?: number;
|
|
74
|
+
startupMicrotaskFlushes?: number;
|
|
75
|
+
adaptiveBudgeting?: boolean;
|
|
68
76
|
}
|
|
69
77
|
declare class PatchCommitScheduler {
|
|
70
78
|
private readonly store;
|
|
@@ -78,8 +86,10 @@ declare class PatchCommitScheduler {
|
|
|
78
86
|
private lowPriorityFrameBudgetMs;
|
|
79
87
|
private maxLowPriorityBatchesPerFlush?;
|
|
80
88
|
private urgentQueueThreshold;
|
|
81
|
-
private
|
|
82
|
-
private
|
|
89
|
+
private adaptiveBudgeting;
|
|
90
|
+
private semanticQueue;
|
|
91
|
+
private enrichmentHighQueue;
|
|
92
|
+
private enrichmentLowQueue;
|
|
83
93
|
private scheduled;
|
|
84
94
|
private flushing;
|
|
85
95
|
private scheduledHandle;
|
|
@@ -95,6 +105,8 @@ declare class PatchCommitScheduler {
|
|
|
95
105
|
private adaptiveBudgetActive;
|
|
96
106
|
private adaptiveHighBatchCap;
|
|
97
107
|
private adaptiveLowBatchCap;
|
|
108
|
+
private startupMicrotaskFlushes;
|
|
109
|
+
private flushCount;
|
|
98
110
|
constructor(params: {
|
|
99
111
|
store: RendererStore;
|
|
100
112
|
onFlush: (result: PatchFlushResult) => void;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
+
// src/renderer/patch-commit-scheduler.ts
|
|
4
|
+
import { getPatchKind } from "@stream-mdx/core/perf/patch-batching";
|
|
5
|
+
|
|
3
6
|
// src/renderer/patch-coalescing.ts
|
|
4
7
|
import {
|
|
5
8
|
DEFAULT_COALESCE_CONFIG,
|
|
@@ -16,6 +19,9 @@ var COALESCING_DURATION_ACTIVATE_MS = 6;
|
|
|
16
19
|
var COALESCING_DURATION_DEACTIVATE_MS = 4;
|
|
17
20
|
var RETAIN_TOUCHED_IN_HISTORY = typeof process !== "undefined" && process.env ? process.env.NODE_ENV !== "production" : true;
|
|
18
21
|
var EMPTY_TOUCHED_SET = /* @__PURE__ */ new Set();
|
|
22
|
+
function shouldSkipCoalescing(patches) {
|
|
23
|
+
return patches.length <= 1;
|
|
24
|
+
}
|
|
19
25
|
function getDefaultNow() {
|
|
20
26
|
if (typeof performance !== "undefined" && typeof performance.now === "function") {
|
|
21
27
|
return () => performance.now();
|
|
@@ -42,8 +48,9 @@ function getDefaultCancelRaf() {
|
|
|
42
48
|
}
|
|
43
49
|
var PatchCommitScheduler = class {
|
|
44
50
|
constructor(params) {
|
|
45
|
-
this.
|
|
46
|
-
this.
|
|
51
|
+
this.semanticQueue = [];
|
|
52
|
+
this.enrichmentHighQueue = [];
|
|
53
|
+
this.enrichmentLowQueue = [];
|
|
47
54
|
this.scheduled = false;
|
|
48
55
|
this.flushing = false;
|
|
49
56
|
this.scheduledHandle = null;
|
|
@@ -55,6 +62,7 @@ var PatchCommitScheduler = class {
|
|
|
55
62
|
this.scheduleToken = 0;
|
|
56
63
|
this.coalescingDurationSamples = [];
|
|
57
64
|
this.adaptiveBudgetActive = false;
|
|
65
|
+
this.flushCount = 0;
|
|
58
66
|
this.store = params.store;
|
|
59
67
|
this.onFlush = params.onFlush;
|
|
60
68
|
this.frameBudgetMs = Math.max(1, params.options?.frameBudgetMs ?? 10);
|
|
@@ -70,6 +78,8 @@ var PatchCommitScheduler = class {
|
|
|
70
78
|
this.maxLowPriorityBatchesPerFlush = params.options?.maxLowPriorityBatchesPerFlush ?? 2;
|
|
71
79
|
this.urgentQueueThreshold = Math.max(1, params.options?.urgentQueueThreshold ?? 4);
|
|
72
80
|
this.historyLimit = Math.max(1, params.options?.historyLimit ?? 200);
|
|
81
|
+
this.startupMicrotaskFlushes = Math.max(0, Math.floor(params.options?.startupMicrotaskFlushes ?? 0));
|
|
82
|
+
this.adaptiveBudgeting = params.options?.adaptiveBudgeting ?? true;
|
|
73
83
|
const requestedBatch = params.options?.batch;
|
|
74
84
|
if (requestedBatch === "microtask") {
|
|
75
85
|
this.batchStrategy = "microtask";
|
|
@@ -96,19 +106,23 @@ var PatchCommitScheduler = class {
|
|
|
96
106
|
patches: input.patches,
|
|
97
107
|
meta: input.meta ?? {},
|
|
98
108
|
receivedAt: input.meta?.receivedAt ?? this.now(),
|
|
109
|
+
kind: input.meta?.kind === "semantic" || input.meta?.kind === "enrichment" ? input.meta.kind : input.patches.some((patch) => getPatchKind(patch) === "semantic") ? "semantic" : "enrichment",
|
|
99
110
|
priority: input.meta?.priority === "low" ? "low" : "high"
|
|
100
111
|
};
|
|
101
|
-
if (batch.
|
|
102
|
-
|
|
112
|
+
if (batch.kind === "semantic") {
|
|
113
|
+
batch.priority = "high";
|
|
114
|
+
this.semanticQueue.push(batch);
|
|
115
|
+
} else if (batch.priority === "low") {
|
|
116
|
+
this.enrichmentLowQueue.push(batch);
|
|
103
117
|
} else {
|
|
104
|
-
this.
|
|
118
|
+
this.enrichmentHighQueue.push(batch);
|
|
105
119
|
}
|
|
106
120
|
if (!this.paused) {
|
|
107
121
|
this.schedule();
|
|
108
122
|
}
|
|
109
123
|
}
|
|
110
124
|
flushAll() {
|
|
111
|
-
if (this.
|
|
125
|
+
if (this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0 && !this.flushing) {
|
|
112
126
|
return null;
|
|
113
127
|
}
|
|
114
128
|
this.cancelScheduled();
|
|
@@ -118,9 +132,11 @@ var PatchCommitScheduler = class {
|
|
|
118
132
|
}
|
|
119
133
|
clear() {
|
|
120
134
|
this.cancelScheduled();
|
|
121
|
-
this.
|
|
122
|
-
this.
|
|
135
|
+
this.semanticQueue = [];
|
|
136
|
+
this.enrichmentHighQueue = [];
|
|
137
|
+
this.enrichmentLowQueue = [];
|
|
123
138
|
this.flushing = false;
|
|
139
|
+
this.flushCount = 0;
|
|
124
140
|
this.coalescingDurationSamples = [];
|
|
125
141
|
this.adaptiveBudgetActive = false;
|
|
126
142
|
this.adaptiveHighBatchCap = void 0;
|
|
@@ -128,10 +144,10 @@ var PatchCommitScheduler = class {
|
|
|
128
144
|
this.maybeResolveIdle();
|
|
129
145
|
}
|
|
130
146
|
getPendingCount() {
|
|
131
|
-
return this.
|
|
147
|
+
return this.semanticQueue.length + this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
|
|
132
148
|
}
|
|
133
149
|
isIdle() {
|
|
134
|
-
return !this.flushing && !this.scheduled && this.
|
|
150
|
+
return !this.flushing && !this.scheduled && this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0;
|
|
135
151
|
}
|
|
136
152
|
awaitIdle() {
|
|
137
153
|
if (this.isIdle()) {
|
|
@@ -171,7 +187,8 @@ var PatchCommitScheduler = class {
|
|
|
171
187
|
this.runFlush(this.frameBudgetMs, false);
|
|
172
188
|
this.maybeResolveIdle();
|
|
173
189
|
};
|
|
174
|
-
|
|
190
|
+
const useStartupMicrotask = this.batchStrategy !== "microtask" && this.startupMicrotaskFlushes > 0 && this.flushCount < this.startupMicrotaskFlushes && typeof queueMicrotask === "function";
|
|
191
|
+
if ((this.batchStrategy === "microtask" || useStartupMicrotask) && typeof queueMicrotask === "function") {
|
|
175
192
|
this.scheduledViaRaf = false;
|
|
176
193
|
this.scheduledHandle = token;
|
|
177
194
|
queueMicrotask(execute);
|
|
@@ -201,12 +218,14 @@ var PatchCommitScheduler = class {
|
|
|
201
218
|
if (this.paused && !manual) {
|
|
202
219
|
return null;
|
|
203
220
|
}
|
|
204
|
-
if (this.
|
|
221
|
+
if (this.semanticQueue.length === 0 && this.enrichmentHighQueue.length === 0 && this.enrichmentLowQueue.length === 0) {
|
|
205
222
|
return null;
|
|
206
223
|
}
|
|
207
224
|
this.flushing = true;
|
|
208
225
|
const startedAt = this.now();
|
|
209
|
-
const
|
|
226
|
+
const initialSemanticQueueSize = this.semanticQueue.length;
|
|
227
|
+
const initialEnrichmentQueueSize = this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
|
|
228
|
+
const initialQueueSize = initialSemanticQueueSize + initialEnrichmentQueueSize;
|
|
210
229
|
const batches = [];
|
|
211
230
|
let totalInputPatches = 0;
|
|
212
231
|
let totalAppliedPatches = 0;
|
|
@@ -221,7 +240,15 @@ var PatchCommitScheduler = class {
|
|
|
221
240
|
consumed += 1;
|
|
222
241
|
const applyStart = this.now();
|
|
223
242
|
const queueDelayMs = Math.max(0, applyStart - next.receivedAt);
|
|
224
|
-
|
|
243
|
+
let coalesced;
|
|
244
|
+
let coalescingMetrics;
|
|
245
|
+
if (shouldSkipCoalescing(next.patches)) {
|
|
246
|
+
coalesced = next.patches;
|
|
247
|
+
} else {
|
|
248
|
+
const result = coalescePatchesWithMetrics(next.patches, DEFAULT_COALESCE_CONFIG);
|
|
249
|
+
coalesced = result.patches;
|
|
250
|
+
coalescingMetrics = result.metrics;
|
|
251
|
+
}
|
|
225
252
|
const touched = this.store.applyPatches(coalesced, {
|
|
226
253
|
coalesced: true,
|
|
227
254
|
metrics: coalescingMetrics
|
|
@@ -241,6 +268,7 @@ var PatchCommitScheduler = class {
|
|
|
241
268
|
receivedAt: next.receivedAt,
|
|
242
269
|
appliedAt: applyStart,
|
|
243
270
|
priority: next.priority,
|
|
271
|
+
kind: next.kind,
|
|
244
272
|
coalescing: coalescingMetrics
|
|
245
273
|
});
|
|
246
274
|
if (maxBatches && batches.length - startingBatchCount >= maxBatches) {
|
|
@@ -257,27 +285,32 @@ var PatchCommitScheduler = class {
|
|
|
257
285
|
queue.splice(0, consumed);
|
|
258
286
|
}
|
|
259
287
|
};
|
|
260
|
-
|
|
288
|
+
processQueue(this.semanticQueue, void 0, Number.POSITIVE_INFINITY);
|
|
289
|
+
const initialHighQueueSize = this.enrichmentHighQueue.length;
|
|
261
290
|
const configuredHighLimit = manual || !this.maxBatchesPerFlush ? void 0 : initialHighQueueSize > this.maxBatchesPerFlush ? void 0 : this.maxBatchesPerFlush;
|
|
262
|
-
const highBatchLimit = this.adaptiveHighBatchCap ?? configuredHighLimit;
|
|
263
|
-
processQueue(this.
|
|
291
|
+
const highBatchLimit = this.adaptiveBudgeting ? this.adaptiveHighBatchCap ?? configuredHighLimit : configuredHighLimit;
|
|
292
|
+
processQueue(this.enrichmentHighQueue, highBatchLimit, budgetMs);
|
|
264
293
|
if (manual) {
|
|
265
|
-
processQueue(this.
|
|
266
|
-
} else if (this.
|
|
294
|
+
processQueue(this.enrichmentLowQueue, void 0, Number.POSITIVE_INFINITY);
|
|
295
|
+
} else if (this.enrichmentLowQueue.length > 0) {
|
|
267
296
|
elapsed = this.now() - startedAt;
|
|
268
297
|
const budgetForLow = Math.max(0, this.lowPriorityFrameBudgetMs - elapsed);
|
|
269
|
-
|
|
270
|
-
|
|
298
|
+
const processedHigherPriorityWork = batches.length > 0;
|
|
299
|
+
if (budgetForLow > 0 || !processedHigherPriorityWork) {
|
|
300
|
+
const initialLowQueueSize = this.enrichmentLowQueue.length;
|
|
271
301
|
const configuredLowLimit = manual || !this.maxLowPriorityBatchesPerFlush ? void 0 : initialLowQueueSize > this.maxLowPriorityBatchesPerFlush ? void 0 : this.maxLowPriorityBatchesPerFlush;
|
|
272
|
-
const lowBatchLimit = this.adaptiveLowBatchCap ?? configuredLowLimit;
|
|
273
|
-
processQueue(this.
|
|
302
|
+
const lowBatchLimit = this.adaptiveBudgeting ? this.adaptiveLowBatchCap ?? configuredLowLimit : configuredLowLimit;
|
|
303
|
+
processQueue(this.enrichmentLowQueue, lowBatchLimit, budgetForLow);
|
|
274
304
|
}
|
|
275
305
|
}
|
|
276
306
|
this.flushing = false;
|
|
277
307
|
const completedAt = this.now();
|
|
278
308
|
const totalDurationMs = completedAt - startedAt;
|
|
279
|
-
const
|
|
309
|
+
const remainingSemantic = this.semanticQueue.length;
|
|
310
|
+
const remainingEnrichment = this.enrichmentHighQueue.length + this.enrichmentLowQueue.length;
|
|
311
|
+
const remaining = remainingSemantic + remainingEnrichment;
|
|
280
312
|
if (batches.length > 0) {
|
|
313
|
+
this.flushCount += 1;
|
|
281
314
|
try {
|
|
282
315
|
this.onFlush({
|
|
283
316
|
batches,
|
|
@@ -285,7 +318,11 @@ var PatchCommitScheduler = class {
|
|
|
285
318
|
totalAppliedPatches,
|
|
286
319
|
totalDurationMs,
|
|
287
320
|
remainingQueueSize: remaining,
|
|
321
|
+
remainingSemanticQueueSize: remainingSemantic,
|
|
322
|
+
remainingEnrichmentQueueSize: remainingEnrichment,
|
|
288
323
|
queueDepthBefore: initialQueueSize,
|
|
324
|
+
semanticQueueDepthBefore: initialSemanticQueueSize,
|
|
325
|
+
enrichmentQueueDepthBefore: initialEnrichmentQueueSize,
|
|
289
326
|
flushStartedAt: startedAt,
|
|
290
327
|
flushCompletedAt: completedAt
|
|
291
328
|
});
|
|
@@ -296,21 +333,27 @@ var PatchCommitScheduler = class {
|
|
|
296
333
|
if (remaining > 0 && !this.paused) {
|
|
297
334
|
this.schedule();
|
|
298
335
|
}
|
|
299
|
-
const coalescingStats = this.computeCoalescingDurationStats();
|
|
300
|
-
this.
|
|
301
|
-
|
|
336
|
+
const coalescingStats = this.adaptiveBudgeting ? this.computeCoalescingDurationStats() : { sampleCount: this.coalescingDurationSamples.length, p95: null };
|
|
337
|
+
if (this.adaptiveBudgeting) {
|
|
338
|
+
this.updateAdaptiveBudget(coalescingStats.p95);
|
|
339
|
+
}
|
|
340
|
+
const adaptiveState = this.adaptiveBudgeting ? this.snapshotAdaptiveBudget(coalescingStats.p95 ?? null) : void 0;
|
|
302
341
|
const flushResult = batches.length > 0 ? {
|
|
303
342
|
batches,
|
|
304
343
|
totalPatches: totalInputPatches,
|
|
305
344
|
totalAppliedPatches,
|
|
306
345
|
totalDurationMs,
|
|
307
346
|
remainingQueueSize: remaining,
|
|
347
|
+
remainingSemanticQueueSize: remainingSemantic,
|
|
348
|
+
remainingEnrichmentQueueSize: remainingEnrichment,
|
|
308
349
|
queueDepthBefore: initialQueueSize,
|
|
350
|
+
semanticQueueDepthBefore: initialSemanticQueueSize,
|
|
351
|
+
enrichmentQueueDepthBefore: initialEnrichmentQueueSize,
|
|
309
352
|
flushStartedAt: startedAt,
|
|
310
353
|
flushCompletedAt: completedAt,
|
|
311
354
|
coalescingDurationP95: coalescingStats.p95 ?? void 0,
|
|
312
355
|
coalescingDurationSampleCount: coalescingStats.sampleCount,
|
|
313
|
-
adaptiveBudgetActive: adaptiveState
|
|
356
|
+
adaptiveBudgetActive: adaptiveState?.active,
|
|
314
357
|
adaptiveBudgetState: adaptiveState
|
|
315
358
|
} : null;
|
|
316
359
|
if (flushResult) {
|
|
@@ -346,6 +389,9 @@ var PatchCommitScheduler = class {
|
|
|
346
389
|
};
|
|
347
390
|
}
|
|
348
391
|
updateAdaptiveBudget(p95) {
|
|
392
|
+
if (!this.adaptiveBudgeting) {
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
349
395
|
if (p95 !== null && p95 > COALESCING_DURATION_ACTIVATE_MS) {
|
|
350
396
|
if (!this.adaptiveBudgetActive) {
|
|
351
397
|
this.adaptiveBudgetActive = true;
|
|
@@ -419,11 +465,13 @@ var PatchCommitScheduler = class {
|
|
|
419
465
|
}
|
|
420
466
|
restart() {
|
|
421
467
|
this.cancelScheduled();
|
|
422
|
-
this.
|
|
423
|
-
this.
|
|
468
|
+
this.semanticQueue = [];
|
|
469
|
+
this.enrichmentHighQueue = [];
|
|
470
|
+
this.enrichmentLowQueue = [];
|
|
424
471
|
this.flushing = false;
|
|
425
472
|
this.sequence = 0;
|
|
426
473
|
this.history = [];
|
|
474
|
+
this.flushCount = 0;
|
|
427
475
|
this.scheduleToken++;
|
|
428
476
|
this.paused = false;
|
|
429
477
|
this.coalescingDurationSamples = [];
|
|
@@ -470,6 +518,12 @@ var PatchCommitScheduler = class {
|
|
|
470
518
|
if (typeof options.historyLimit !== "undefined") {
|
|
471
519
|
this.setHistoryLimit(options.historyLimit);
|
|
472
520
|
}
|
|
521
|
+
if (typeof options.startupMicrotaskFlushes !== "undefined") {
|
|
522
|
+
this.startupMicrotaskFlushes = Math.max(0, Math.floor(options.startupMicrotaskFlushes));
|
|
523
|
+
}
|
|
524
|
+
if (typeof options.adaptiveBudgeting !== "undefined") {
|
|
525
|
+
this.adaptiveBudgeting = options.adaptiveBudgeting;
|
|
526
|
+
}
|
|
473
527
|
if (typeof options.batch !== "undefined") {
|
|
474
528
|
const requestedBatch = options.batch;
|
|
475
529
|
if (requestedBatch === "microtask") {
|