@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
package/dist/renderer/store.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
sanitizeHTML,
|
|
11
11
|
PATCH_ROOT_ID
|
|
12
12
|
} from "@stream-mdx/core";
|
|
13
|
+
import { getPatchKind } from "@stream-mdx/core/perf/patch-batching";
|
|
13
14
|
|
|
14
15
|
// src/renderer/list-utils.ts
|
|
15
16
|
function updateNodeDepth(node, depth) {
|
|
@@ -114,12 +115,28 @@ function snapshotToBlock(node) {
|
|
|
114
115
|
}
|
|
115
116
|
return null;
|
|
116
117
|
}
|
|
117
|
-
var
|
|
118
|
-
var
|
|
118
|
+
var nodeSnapshotCacheByMap = /* @__PURE__ */ new WeakMap();
|
|
119
|
+
var childrenSnapshotCacheByMap = /* @__PURE__ */ new WeakMap();
|
|
119
120
|
var NODE_SNAPSHOT_CACHE_LIMIT = 5e4;
|
|
120
121
|
var CHILDREN_SNAPSHOT_CACHE_LIMIT = 5e4;
|
|
121
122
|
var NODE_SNAPSHOT_CACHE_BUFFER = Math.max(200, Math.floor(NODE_SNAPSHOT_CACHE_LIMIT * 0.1));
|
|
122
123
|
var CHILDREN_SNAPSHOT_CACHE_BUFFER = Math.max(200, Math.floor(CHILDREN_SNAPSHOT_CACHE_LIMIT * 0.1));
|
|
124
|
+
function getNodeSnapshotCacheForMap(map) {
|
|
125
|
+
let cache = nodeSnapshotCacheByMap.get(map);
|
|
126
|
+
if (!cache) {
|
|
127
|
+
cache = /* @__PURE__ */ new Map();
|
|
128
|
+
nodeSnapshotCacheByMap.set(map, cache);
|
|
129
|
+
}
|
|
130
|
+
return cache;
|
|
131
|
+
}
|
|
132
|
+
function getChildrenSnapshotCacheForMap(map) {
|
|
133
|
+
let cache = childrenSnapshotCacheByMap.get(map);
|
|
134
|
+
if (!cache) {
|
|
135
|
+
cache = /* @__PURE__ */ new Map();
|
|
136
|
+
childrenSnapshotCacheByMap.set(map, cache);
|
|
137
|
+
}
|
|
138
|
+
return cache;
|
|
139
|
+
}
|
|
123
140
|
function pruneCache(cache, max, buffer) {
|
|
124
141
|
if (cache.size <= max + buffer) return;
|
|
125
142
|
let remaining = cache.size - max;
|
|
@@ -129,6 +146,14 @@ function pruneCache(cache, max, buffer) {
|
|
|
129
146
|
if (remaining <= 0) break;
|
|
130
147
|
}
|
|
131
148
|
}
|
|
149
|
+
function deleteSnapshotCacheEntry(map, id) {
|
|
150
|
+
getNodeSnapshotCacheForMap(map).delete(id);
|
|
151
|
+
getChildrenSnapshotCacheForMap(map).delete(id);
|
|
152
|
+
}
|
|
153
|
+
function clearSnapshotCaches(map) {
|
|
154
|
+
getNodeSnapshotCacheForMap(map).clear();
|
|
155
|
+
getChildrenSnapshotCacheForMap(map).clear();
|
|
156
|
+
}
|
|
132
157
|
var EMPTY_CHILDREN = Object.freeze([]);
|
|
133
158
|
var EMPTY_NODE_SNAPSHOT = Object.freeze({ version: -1, node: void 0 });
|
|
134
159
|
var EMPTY_CHILDREN_SNAPSHOT = Object.freeze({
|
|
@@ -296,9 +321,57 @@ function createRootRecord() {
|
|
|
296
321
|
parentId: null,
|
|
297
322
|
children: [],
|
|
298
323
|
props: {},
|
|
299
|
-
version: 0
|
|
324
|
+
version: 0,
|
|
325
|
+
blockEpoch: 0
|
|
300
326
|
};
|
|
301
327
|
}
|
|
328
|
+
function readNumericEpoch(value) {
|
|
329
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
330
|
+
return void 0;
|
|
331
|
+
}
|
|
332
|
+
return value;
|
|
333
|
+
}
|
|
334
|
+
function readSnapshotBlockEpoch(snapshot) {
|
|
335
|
+
return readNumericEpoch(snapshot.meta?.blockEpoch);
|
|
336
|
+
}
|
|
337
|
+
function readPatchBlockEpoch(patch) {
|
|
338
|
+
if (patch.op === "setHTML") {
|
|
339
|
+
return readNumericEpoch(patch.patchMeta?.blockEpoch);
|
|
340
|
+
}
|
|
341
|
+
return readNumericEpoch(patch.meta?.blockEpoch);
|
|
342
|
+
}
|
|
343
|
+
function readPatchParseEpoch(patch) {
|
|
344
|
+
if (patch.op === "setHTML") {
|
|
345
|
+
return readNumericEpoch(patch.patchMeta?.parseEpoch);
|
|
346
|
+
}
|
|
347
|
+
return readNumericEpoch(patch.meta?.parseEpoch);
|
|
348
|
+
}
|
|
349
|
+
function readPatchTx(patch) {
|
|
350
|
+
if (patch.op === "setHTML") {
|
|
351
|
+
return readNumericEpoch(patch.patchMeta?.tx);
|
|
352
|
+
}
|
|
353
|
+
return readNumericEpoch(patch.meta?.tx);
|
|
354
|
+
}
|
|
355
|
+
function assignSubtreeBlockEpoch(map, rootId, blockEpoch, touched) {
|
|
356
|
+
const root = map.get(rootId);
|
|
357
|
+
if (!root) return;
|
|
358
|
+
const stack = [root];
|
|
359
|
+
while (stack.length > 0) {
|
|
360
|
+
const node = stack.pop();
|
|
361
|
+
if (!node) continue;
|
|
362
|
+
if (node.blockEpoch !== blockEpoch) {
|
|
363
|
+
node.blockEpoch = blockEpoch;
|
|
364
|
+
node.version += 1;
|
|
365
|
+
touched?.add(node.id);
|
|
366
|
+
}
|
|
367
|
+
for (const childId of node.children) {
|
|
368
|
+
const child = map.get(childId);
|
|
369
|
+
if (child) {
|
|
370
|
+
stack.push(child);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
302
375
|
function ensureArray(value) {
|
|
303
376
|
return Array.isArray(value) ? value : [];
|
|
304
377
|
}
|
|
@@ -358,7 +431,7 @@ function escapeHtml(value) {
|
|
|
358
431
|
}
|
|
359
432
|
function stripOuterLineSpan(html) {
|
|
360
433
|
if (!html) return null;
|
|
361
|
-
const openTagMatch = html.match(/<span[^>]*>/i);
|
|
434
|
+
const openTagMatch = html.match(/<span[^>]*class="[^"]*\bline\b[^"]*"[^>]*>/i);
|
|
362
435
|
if (!openTagMatch) {
|
|
363
436
|
return null;
|
|
364
437
|
}
|
|
@@ -407,8 +480,7 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
|
|
|
407
480
|
if (node.type !== "code-line") {
|
|
408
481
|
anomalies.push(`type-mismatch:${node.id}:${node.type}`);
|
|
409
482
|
nodes.delete(node.id);
|
|
410
|
-
|
|
411
|
-
childrenSnapshotCache.delete(node.id);
|
|
483
|
+
deleteSnapshotCacheEntry(nodes, node.id);
|
|
412
484
|
touched.add(node.id);
|
|
413
485
|
continue;
|
|
414
486
|
}
|
|
@@ -452,16 +524,14 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
|
|
|
452
524
|
const existing = nodes.get(desiredId);
|
|
453
525
|
if (existing && existing !== node) {
|
|
454
526
|
nodes.delete(desiredId);
|
|
455
|
-
|
|
456
|
-
childrenSnapshotCache.delete(desiredId);
|
|
527
|
+
deleteSnapshotCacheEntry(nodes, desiredId);
|
|
457
528
|
touched.add(desiredId);
|
|
458
529
|
mutated = true;
|
|
459
530
|
}
|
|
460
531
|
if (node.id !== desiredId) {
|
|
461
532
|
const previousId = node.id;
|
|
462
533
|
nodes.delete(node.id);
|
|
463
|
-
|
|
464
|
-
childrenSnapshotCache.delete(previousId);
|
|
534
|
+
deleteSnapshotCacheEntry(nodes, previousId);
|
|
465
535
|
node.id = desiredId;
|
|
466
536
|
nodes.set(desiredId, node);
|
|
467
537
|
touched.add(desiredId);
|
|
@@ -492,8 +562,7 @@ function normalizeCodeBlockChildren(nodes, parent, touched) {
|
|
|
492
562
|
for (const [id, record] of nodes) {
|
|
493
563
|
if (record.parentId === parent.id && record.type === "code-line" && !allowed.has(id)) {
|
|
494
564
|
nodes.delete(id);
|
|
495
|
-
|
|
496
|
-
childrenSnapshotCache.delete(id);
|
|
565
|
+
deleteSnapshotCacheEntry(nodes, id);
|
|
497
566
|
touched.add(id);
|
|
498
567
|
mutated = true;
|
|
499
568
|
}
|
|
@@ -577,8 +646,10 @@ function normalizeCodeLineProps(incoming, previous) {
|
|
|
577
646
|
}
|
|
578
647
|
function createCodeLineProps(index, text, highlight, tokens) {
|
|
579
648
|
const safeText = typeof text === "string" ? text : "";
|
|
580
|
-
const
|
|
581
|
-
const
|
|
649
|
+
const hasIncomingHighlight = typeof highlight === "string" && highlight.trim().length > 0;
|
|
650
|
+
const sanitized = hasIncomingHighlight ? sanitizeLineInnerHtml(highlight, safeText) : null;
|
|
651
|
+
const hasRenderableTokens = tokens !== void 0 && tokens !== null;
|
|
652
|
+
const html = sanitized && sanitized.length >= safeText.length ? sanitized : hasRenderableTokens ? null : escapeHtml(safeText);
|
|
582
653
|
const props = {
|
|
583
654
|
index,
|
|
584
655
|
text: safeText,
|
|
@@ -689,6 +760,18 @@ function validateCodeBlockChildren(nodes, parent) {
|
|
|
689
760
|
issues.push({ kind: "dangling-node", id });
|
|
690
761
|
}
|
|
691
762
|
}
|
|
763
|
+
if (parent.block?.isFinalized) {
|
|
764
|
+
const snapshot = createBlockSnapshot(cloneBlock(parent.block));
|
|
765
|
+
const expectedChildren = ensureArray(snapshot.children).filter((child) => child.type === "code-line");
|
|
766
|
+
if (expectedChildren.length !== parent.children.length) {
|
|
767
|
+
issues.push({
|
|
768
|
+
kind: "line-count-mismatch",
|
|
769
|
+
id: parent.id,
|
|
770
|
+
expected: expectedChildren.length,
|
|
771
|
+
actual: parent.children.length
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
}
|
|
692
775
|
return { ok: issues.length === 0, issues };
|
|
693
776
|
}
|
|
694
777
|
function rebuildCodeBlockFromSnapshot(nodes, parent, touched, issues) {
|
|
@@ -705,7 +788,7 @@ function rebuildCodeBlockFromSnapshot(nodes, parent, touched, issues) {
|
|
|
705
788
|
}
|
|
706
789
|
const snapshot = createBlockSnapshot(cloneBlock(parent.block));
|
|
707
790
|
removeSubtree(nodes, parent.id, touched);
|
|
708
|
-
const inserted = insertSnapshotAt(nodes, ancestor, index, snapshot);
|
|
791
|
+
const inserted = insertSnapshotAt(nodes, ancestor, index, snapshot, touched, parent.blockEpoch);
|
|
709
792
|
if (!inserted) {
|
|
710
793
|
return false;
|
|
711
794
|
}
|
|
@@ -728,7 +811,55 @@ function arraysEqual(a, b) {
|
|
|
728
811
|
}
|
|
729
812
|
return true;
|
|
730
813
|
}
|
|
731
|
-
function
|
|
814
|
+
function expectedCodeLineId(parentId, index) {
|
|
815
|
+
return `${parentId}::line:${index}`;
|
|
816
|
+
}
|
|
817
|
+
function collectStoreInvariantViolations(nodes) {
|
|
818
|
+
const violations = [];
|
|
819
|
+
for (const [id, node] of nodes) {
|
|
820
|
+
if (node.type === "__root__") continue;
|
|
821
|
+
if (node.children.length > 1) {
|
|
822
|
+
const seen = /* @__PURE__ */ new Set();
|
|
823
|
+
for (const childId of node.children) {
|
|
824
|
+
if (seen.has(childId)) {
|
|
825
|
+
violations.push(`duplicate child id:${id}:${childId}`);
|
|
826
|
+
} else {
|
|
827
|
+
seen.add(childId);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
if (node.type === "list") {
|
|
832
|
+
const parent = node.parentId ? nodes.get(node.parentId) : void 0;
|
|
833
|
+
if (parent?.type === "list-item" && node.children.length === 0) {
|
|
834
|
+
violations.push(`empty nested list:${id}`);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (node.type === "code") {
|
|
838
|
+
for (let index = 0; index < node.children.length; index += 1) {
|
|
839
|
+
const childId = node.children[index];
|
|
840
|
+
const child = nodes.get(childId);
|
|
841
|
+
if (!child) {
|
|
842
|
+
violations.push(`missing code-line node:${node.id}:${childId}`);
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
if (child.type !== "code-line") {
|
|
846
|
+
violations.push(`non-code child in code block:${node.id}:${child.id}:${child.type}`);
|
|
847
|
+
continue;
|
|
848
|
+
}
|
|
849
|
+
const expectedId = expectedCodeLineId(node.id, index);
|
|
850
|
+
if (child.id !== expectedId) {
|
|
851
|
+
violations.push(`code-line id mismatch:${node.id}:${child.id}:${expectedId}`);
|
|
852
|
+
}
|
|
853
|
+
const indexProp = typeof child.props?.index === "number" ? child.props.index : null;
|
|
854
|
+
if (indexProp !== index) {
|
|
855
|
+
violations.push(`code-line index mismatch:${node.id}:${child.id}:${indexProp}:${index}`);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
return violations;
|
|
861
|
+
}
|
|
862
|
+
function buildNodeFromSnapshot(map, snapshot, parentId, blockEpoch) {
|
|
732
863
|
const record = {
|
|
733
864
|
id: snapshot.id,
|
|
734
865
|
type: snapshot.type,
|
|
@@ -738,6 +869,7 @@ function buildNodeFromSnapshot(map, snapshot, parentId) {
|
|
|
738
869
|
meta: snapshot.meta ? { ...snapshot.meta } : void 0,
|
|
739
870
|
range: snapshot.range ? { ...snapshot.range } : void 0,
|
|
740
871
|
version: 0,
|
|
872
|
+
blockEpoch,
|
|
741
873
|
block: snapshotToBlock(snapshot) ?? void 0
|
|
742
874
|
};
|
|
743
875
|
if (record.type === "code") {
|
|
@@ -746,7 +878,7 @@ function buildNodeFromSnapshot(map, snapshot, parentId) {
|
|
|
746
878
|
map.set(record.id, record);
|
|
747
879
|
const childSnapshots = ensureArray(snapshot.children);
|
|
748
880
|
for (const child of childSnapshots) {
|
|
749
|
-
const childRecord = buildNodeFromSnapshot(map, child, record.id);
|
|
881
|
+
const childRecord = buildNodeFromSnapshot(map, child, record.id, blockEpoch);
|
|
750
882
|
record.children.push(childRecord.id);
|
|
751
883
|
}
|
|
752
884
|
return record;
|
|
@@ -774,8 +906,7 @@ function removeSubtree(map, id, touched) {
|
|
|
774
906
|
}
|
|
775
907
|
}
|
|
776
908
|
map.delete(node.id);
|
|
777
|
-
|
|
778
|
-
childrenSnapshotCache.delete(node.id);
|
|
909
|
+
deleteSnapshotCacheEntry(map, node.id);
|
|
779
910
|
touched.add(node.id);
|
|
780
911
|
for (const childId of node.children) {
|
|
781
912
|
const child = map.get(childId);
|
|
@@ -807,13 +938,13 @@ function resolveParent(map, path) {
|
|
|
807
938
|
}
|
|
808
939
|
return map.get(path.blockId);
|
|
809
940
|
}
|
|
810
|
-
function insertSnapshotAt(map, parent, index, snapshot, touched) {
|
|
941
|
+
function insertSnapshotAt(map, parent, index, snapshot, touched, blockEpoch) {
|
|
811
942
|
if (!parent) return null;
|
|
812
943
|
const existingIndex = parent.children.indexOf(snapshot.id);
|
|
813
944
|
if (existingIndex !== -1) {
|
|
814
945
|
removeSubtree(map, parent.children[existingIndex], touched ?? /* @__PURE__ */ new Set());
|
|
815
946
|
}
|
|
816
|
-
const record = buildNodeFromSnapshot(map, snapshot, parent.id);
|
|
947
|
+
const record = buildNodeFromSnapshot(map, snapshot, parent.id, blockEpoch ?? readSnapshotBlockEpoch(snapshot) ?? parent.blockEpoch);
|
|
817
948
|
const insertedIds = [];
|
|
818
949
|
const queue = [record];
|
|
819
950
|
while (queue.length > 0) {
|
|
@@ -831,13 +962,13 @@ function insertSnapshotAt(map, parent, index, snapshot, touched) {
|
|
|
831
962
|
ensureUniqueChildren(parent, touched);
|
|
832
963
|
return { record, insertedIds };
|
|
833
964
|
}
|
|
834
|
-
function replaceSnapshotAt(map, parent, index, snapshot, touched) {
|
|
965
|
+
function replaceSnapshotAt(map, parent, index, snapshot, touched, blockEpoch) {
|
|
835
966
|
if (!parent) return;
|
|
836
967
|
if (index < 0 || index >= parent.children.length) return;
|
|
837
968
|
const targetId = parent.children[index];
|
|
838
969
|
parent.children.splice(index, 1);
|
|
839
970
|
removeSubtree(map, targetId, touched);
|
|
840
|
-
const inserted = insertSnapshotAt(map, parent, index, snapshot, touched);
|
|
971
|
+
const inserted = insertSnapshotAt(map, parent, index, snapshot, touched, blockEpoch);
|
|
841
972
|
if (inserted) {
|
|
842
973
|
for (const id of inserted.insertedIds) {
|
|
843
974
|
touched.add(id);
|
|
@@ -857,6 +988,24 @@ function createRendererStore(initialBlocks = []) {
|
|
|
857
988
|
const listeners = /* @__PURE__ */ new Set();
|
|
858
989
|
let notifyScheduled = false;
|
|
859
990
|
let lastCoalescingMetrics = null;
|
|
991
|
+
let invariantViolations = [];
|
|
992
|
+
let transientDiagnostics = [];
|
|
993
|
+
let nextBlockEpoch = 1;
|
|
994
|
+
const debugCounters = {
|
|
995
|
+
appendLineGuardRejected: 0,
|
|
996
|
+
staleEpochRejected: 0,
|
|
997
|
+
storeInvariantViolationCount: 0
|
|
998
|
+
};
|
|
999
|
+
const allocateBlockEpoch = (preferred) => {
|
|
1000
|
+
const resolved = readNumericEpoch(preferred);
|
|
1001
|
+
if (resolved !== void 0) {
|
|
1002
|
+
nextBlockEpoch = Math.max(nextBlockEpoch, resolved + 1);
|
|
1003
|
+
return resolved;
|
|
1004
|
+
}
|
|
1005
|
+
const epoch = nextBlockEpoch;
|
|
1006
|
+
nextBlockEpoch += 1;
|
|
1007
|
+
return epoch;
|
|
1008
|
+
};
|
|
860
1009
|
const getRaf = () => {
|
|
861
1010
|
if (typeof globalThis !== "undefined" && typeof globalThis.requestAnimationFrame === "function") {
|
|
862
1011
|
return globalThis.requestAnimationFrame;
|
|
@@ -888,9 +1037,12 @@ function createRendererStore(initialBlocks = []) {
|
|
|
888
1037
|
}
|
|
889
1038
|
}
|
|
890
1039
|
}
|
|
1040
|
+
function appendDiagnostic(message) {
|
|
1041
|
+
transientDiagnostics = [...transientDiagnostics, message];
|
|
1042
|
+
invariantViolations = [...invariantViolations, message];
|
|
1043
|
+
}
|
|
891
1044
|
function rebuildFromBlocks(blocks) {
|
|
892
|
-
|
|
893
|
-
childrenSnapshotCache.clear();
|
|
1045
|
+
clearSnapshotCaches(nodes);
|
|
894
1046
|
nodes = /* @__PURE__ */ new Map();
|
|
895
1047
|
const root = createRootRecord();
|
|
896
1048
|
nodes.set(root.id, root);
|
|
@@ -899,9 +1051,12 @@ function createRendererStore(initialBlocks = []) {
|
|
|
899
1051
|
for (let i = 0; i < blocks.length; i++) {
|
|
900
1052
|
const block = cloneBlock(blocks[i]);
|
|
901
1053
|
const snapshot = createBlockSnapshot(block);
|
|
902
|
-
insertSnapshotAt(nodes, root, root.children.length, snapshot);
|
|
1054
|
+
insertSnapshotAt(nodes, root, root.children.length, snapshot, void 0, allocateBlockEpoch(readSnapshotBlockEpoch(snapshot)));
|
|
903
1055
|
}
|
|
904
1056
|
normalizeAllListDepths(nodes, /* @__PURE__ */ new Set(), PATCH_ROOT_ID);
|
|
1057
|
+
transientDiagnostics = [];
|
|
1058
|
+
invariantViolations = collectStoreInvariantViolations(nodes);
|
|
1059
|
+
debugCounters.storeInvariantViolationCount += invariantViolations.length;
|
|
905
1060
|
version++;
|
|
906
1061
|
blocksDirty = true;
|
|
907
1062
|
scheduleNotify();
|
|
@@ -966,11 +1121,57 @@ function createRendererStore(initialBlocks = []) {
|
|
|
966
1121
|
listDepthDirty = true;
|
|
967
1122
|
}
|
|
968
1123
|
};
|
|
969
|
-
const
|
|
1124
|
+
const semanticTxEpochs = /* @__PURE__ */ new Map();
|
|
1125
|
+
const resolveBlockEpochTarget = (patch, at) => {
|
|
1126
|
+
if ((patch.op === "insertChild" || patch.op === "replaceChild") && at.blockId === PATCH_ROOT_ID) {
|
|
1127
|
+
const nodeId = patch.node?.id;
|
|
1128
|
+
return nodeId ? nodes.get(nodeId) : void 0;
|
|
1129
|
+
}
|
|
1130
|
+
return nodes.get(at.blockId) ?? (at.nodeId ? nodes.get(at.nodeId) : void 0);
|
|
1131
|
+
};
|
|
1132
|
+
const rejectStaleEpochPatch = (patch, at) => {
|
|
1133
|
+
const expectedBlockEpoch = readPatchBlockEpoch(patch);
|
|
1134
|
+
const patchTx = readPatchTx(patch);
|
|
1135
|
+
if (expectedBlockEpoch === void 0) {
|
|
1136
|
+
return false;
|
|
1137
|
+
}
|
|
1138
|
+
if (getPatchKind(patch) !== "semantic" || !at) {
|
|
1139
|
+
return false;
|
|
1140
|
+
}
|
|
1141
|
+
const target = resolveBlockEpochTarget(patch, at);
|
|
1142
|
+
if (!target || target.blockEpoch === expectedBlockEpoch) {
|
|
1143
|
+
return false;
|
|
1144
|
+
}
|
|
1145
|
+
const txEpoch = at.blockId && patchTx !== void 0 ? semanticTxEpochs.get(at.blockId) : void 0;
|
|
1146
|
+
if (txEpoch && txEpoch.tx === patchTx && txEpoch.blockEpoch === target.blockEpoch) {
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
debugCounters.staleEpochRejected += 1;
|
|
1150
|
+
appendDiagnostic(
|
|
1151
|
+
`stale epoch rejected:${patch.op}:${target.id}:expected=${expectedBlockEpoch}:actual=${target.blockEpoch}`
|
|
1152
|
+
);
|
|
1153
|
+
return true;
|
|
1154
|
+
};
|
|
1155
|
+
const advanceBlockEpoch = (blockId, touchedSet, preferredEpoch, tx) => {
|
|
1156
|
+
if (!blockId || blockId === PATCH_ROOT_ID) {
|
|
1157
|
+
return null;
|
|
1158
|
+
}
|
|
1159
|
+
const blockRoot = nodes.get(blockId);
|
|
1160
|
+
if (!blockRoot) {
|
|
1161
|
+
return null;
|
|
1162
|
+
}
|
|
1163
|
+
const nextEpoch = allocateBlockEpoch(preferredEpoch);
|
|
1164
|
+
assignSubtreeBlockEpoch(nodes, blockRoot.id, nextEpoch, touchedSet);
|
|
1165
|
+
if (tx !== void 0) {
|
|
1166
|
+
semanticTxEpochs.set(blockRoot.id, { tx, blockEpoch: nextEpoch });
|
|
1167
|
+
}
|
|
1168
|
+
return nextEpoch;
|
|
1169
|
+
};
|
|
1170
|
+
const applyPropsUpdate = (at, props, preferredEpoch, tx, semanticUpdate = false) => {
|
|
970
1171
|
const perfStartLocal = perfEnabled ? getPerfNow() : 0;
|
|
971
1172
|
const targetId = at.nodeId ?? at.blockId;
|
|
972
1173
|
if (!targetId) return;
|
|
973
|
-
|
|
1174
|
+
let target = nodes.get(targetId);
|
|
974
1175
|
if (!target) return;
|
|
975
1176
|
const parentType = target.parentId ? nodes.get(target.parentId)?.type : void 0;
|
|
976
1177
|
if (debugListPatches && (isListRelated(target.type) || isListRelated(parentType))) {
|
|
@@ -997,6 +1198,9 @@ function createRendererStore(initialBlocks = []) {
|
|
|
997
1198
|
propsChanged = true;
|
|
998
1199
|
}
|
|
999
1200
|
if (propsChanged) {
|
|
1201
|
+
if (semanticUpdate) {
|
|
1202
|
+
advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
|
|
1203
|
+
}
|
|
1000
1204
|
target.version++;
|
|
1001
1205
|
touched.add(target.id);
|
|
1002
1206
|
const parent = target.parentId ? nodes.get(target.parentId) : void 0;
|
|
@@ -1060,16 +1264,56 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1060
1264
|
}
|
|
1061
1265
|
}
|
|
1062
1266
|
let blockChanged = false;
|
|
1267
|
+
let typeChanged = false;
|
|
1268
|
+
let rebuiltFromFinalizedBlock = false;
|
|
1269
|
+
let blockEpochAdvanced = false;
|
|
1063
1270
|
const incomingBlock = props?.block;
|
|
1064
1271
|
if (incomingBlock && typeof incomingBlock === "object") {
|
|
1065
1272
|
target.block = cloneBlock(incomingBlock);
|
|
1273
|
+
const previousType = target.type;
|
|
1274
|
+
const incomingType = typeof target.block.type === "string" ? target.block.type : target.type;
|
|
1275
|
+
if (incomingType !== target.type) {
|
|
1276
|
+
target.type = incomingType;
|
|
1277
|
+
typeChanged = true;
|
|
1278
|
+
if (isListRelated(previousType) || isListRelated(target.type)) {
|
|
1279
|
+
markListDepthDirty(target.id);
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1066
1282
|
if (target.type === "code") {
|
|
1067
1283
|
applyCodeBlockMetadata(target);
|
|
1068
1284
|
}
|
|
1285
|
+
if (semanticUpdate) {
|
|
1286
|
+
advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
|
|
1287
|
+
blockEpochAdvanced = true;
|
|
1288
|
+
}
|
|
1289
|
+
const shouldRebuildFinalizedTree = !at.nodeId && target.id === at.blockId && Boolean(target.block?.isFinalized) && target.type !== "code" && Boolean(target.parentId);
|
|
1290
|
+
if (shouldRebuildFinalizedTree) {
|
|
1291
|
+
const parent = target.parentId ? nodes.get(target.parentId) : void 0;
|
|
1292
|
+
const index = parent ? parent.children.indexOf(target.id) : -1;
|
|
1293
|
+
if (parent && index !== -1) {
|
|
1294
|
+
const snapshot = createBlockSnapshot(cloneBlock(target.block));
|
|
1295
|
+
replaceSnapshotAt(nodes, parent, index, snapshot, touched, target.blockEpoch);
|
|
1296
|
+
mutatedBlocks = true;
|
|
1297
|
+
blockChanged = true;
|
|
1298
|
+
rebuiltFromFinalizedBlock = true;
|
|
1299
|
+
const rebuilt = nodes.get(target.id);
|
|
1300
|
+
if (isListRelated(previousType) || isListRelated(rebuilt?.type)) {
|
|
1301
|
+
markListDepthDirty(target.id);
|
|
1302
|
+
}
|
|
1303
|
+
target = rebuilt ?? target;
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1069
1306
|
mutatedBlocks = true;
|
|
1070
1307
|
blockChanged = true;
|
|
1071
1308
|
}
|
|
1072
|
-
if (
|
|
1309
|
+
if (rebuiltFromFinalizedBlock) {
|
|
1310
|
+
return;
|
|
1311
|
+
}
|
|
1312
|
+
if (propsChanged || blockChanged || typeChanged) {
|
|
1313
|
+
if (semanticUpdate && !blockEpochAdvanced) {
|
|
1314
|
+
advanceBlockEpoch(at.blockId, touched, preferredEpoch, tx);
|
|
1315
|
+
blockEpochAdvanced = true;
|
|
1316
|
+
}
|
|
1073
1317
|
target.version++;
|
|
1074
1318
|
touched.add(target.id);
|
|
1075
1319
|
let parentForNormalization;
|
|
@@ -1112,13 +1356,22 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1112
1356
|
const applyPatch = (patch) => {
|
|
1113
1357
|
switch (patch.op) {
|
|
1114
1358
|
case "insertChild": {
|
|
1359
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
1115
1360
|
const parent = resolveParent(nodes, patch.at);
|
|
1116
|
-
const inserted = insertSnapshotAt(
|
|
1361
|
+
const inserted = insertSnapshotAt(
|
|
1362
|
+
nodes,
|
|
1363
|
+
parent,
|
|
1364
|
+
patch.index,
|
|
1365
|
+
patch.node,
|
|
1366
|
+
touched,
|
|
1367
|
+
allocateBlockEpoch(readPatchParseEpoch(patch) ?? readSnapshotBlockEpoch(patch.node))
|
|
1368
|
+
);
|
|
1117
1369
|
if (inserted) {
|
|
1118
1370
|
for (const id of inserted.insertedIds) {
|
|
1119
1371
|
touched.add(id);
|
|
1120
1372
|
}
|
|
1121
1373
|
mutatedBlocks = true;
|
|
1374
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
1122
1375
|
if (isListRelated(inserted.record.type) || isListRelated(parent?.type)) {
|
|
1123
1376
|
markListDepthDirty(inserted.record.id);
|
|
1124
1377
|
if (parent) {
|
|
@@ -1138,6 +1391,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1138
1391
|
break;
|
|
1139
1392
|
}
|
|
1140
1393
|
case "deleteChild": {
|
|
1394
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
1141
1395
|
const parent = resolveParent(nodes, patch.at);
|
|
1142
1396
|
if (!parent) break;
|
|
1143
1397
|
if (patch.index < 0 || patch.index >= parent.children.length) break;
|
|
@@ -1149,6 +1403,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1149
1403
|
touched.add(parent.id);
|
|
1150
1404
|
ensureUniqueChildren(parent, touched);
|
|
1151
1405
|
mutatedBlocks = true;
|
|
1406
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
1152
1407
|
if (isListRelated(parent.type) || isListRelated(removedNode?.type)) {
|
|
1153
1408
|
markListDepthDirty(parent.id);
|
|
1154
1409
|
}
|
|
@@ -1165,11 +1420,20 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1165
1420
|
break;
|
|
1166
1421
|
}
|
|
1167
1422
|
case "replaceChild": {
|
|
1423
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
1168
1424
|
const parent = resolveParent(nodes, patch.at);
|
|
1169
1425
|
if (!parent) break;
|
|
1170
|
-
replaceSnapshotAt(
|
|
1426
|
+
replaceSnapshotAt(
|
|
1427
|
+
nodes,
|
|
1428
|
+
parent,
|
|
1429
|
+
patch.index,
|
|
1430
|
+
patch.node,
|
|
1431
|
+
touched,
|
|
1432
|
+
allocateBlockEpoch(readPatchParseEpoch(patch) ?? readSnapshotBlockEpoch(patch.node))
|
|
1433
|
+
);
|
|
1171
1434
|
touched.add(parent.id);
|
|
1172
1435
|
mutatedBlocks = true;
|
|
1436
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
1173
1437
|
if (isListRelated(parent.type)) {
|
|
1174
1438
|
markListDepthDirty(parent.id);
|
|
1175
1439
|
}
|
|
@@ -1183,18 +1447,33 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1183
1447
|
break;
|
|
1184
1448
|
}
|
|
1185
1449
|
case "setProps": {
|
|
1186
|
-
|
|
1450
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
1451
|
+
applyPropsUpdate(patch.at, patch.props, readPatchParseEpoch(patch), readPatchTx(patch), getPatchKind(patch) === "semantic");
|
|
1187
1452
|
break;
|
|
1188
1453
|
}
|
|
1189
1454
|
case "setPropsBatch": {
|
|
1190
1455
|
const entries = Array.isArray(patch.entries) ? patch.entries : [];
|
|
1191
1456
|
for (const entry of entries) {
|
|
1192
1457
|
if (!entry || !entry.at) continue;
|
|
1193
|
-
|
|
1458
|
+
const entryPatch = {
|
|
1459
|
+
op: "setProps",
|
|
1460
|
+
at: entry.at,
|
|
1461
|
+
props: entry.props,
|
|
1462
|
+
meta: entry.meta ?? patch.meta
|
|
1463
|
+
};
|
|
1464
|
+
if (rejectStaleEpochPatch(entryPatch, entry.at)) continue;
|
|
1465
|
+
applyPropsUpdate(
|
|
1466
|
+
entry.at,
|
|
1467
|
+
entry.props,
|
|
1468
|
+
readPatchParseEpoch(entryPatch),
|
|
1469
|
+
readPatchTx(entryPatch),
|
|
1470
|
+
getPatchKind(entryPatch) === "semantic"
|
|
1471
|
+
);
|
|
1194
1472
|
}
|
|
1195
1473
|
break;
|
|
1196
1474
|
}
|
|
1197
1475
|
case "finalize": {
|
|
1476
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
1198
1477
|
const target = nodes.get(patch.at.nodeId ?? patch.at.blockId);
|
|
1199
1478
|
if (!target || !target.block) break;
|
|
1200
1479
|
if (!target.block.isFinalized) {
|
|
@@ -1203,6 +1482,17 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1203
1482
|
target.version++;
|
|
1204
1483
|
touched.add(target.id);
|
|
1205
1484
|
mutatedBlocks = true;
|
|
1485
|
+
if (!patch.at.nodeId && target.id === patch.at.blockId && target.type !== "code" && target.parentId) {
|
|
1486
|
+
const parent = nodes.get(target.parentId);
|
|
1487
|
+
const index = parent ? parent.children.indexOf(target.id) : -1;
|
|
1488
|
+
if (parent && index !== -1) {
|
|
1489
|
+
const snapshot = createBlockSnapshot(cloneBlock(target.block));
|
|
1490
|
+
replaceSnapshotAt(nodes, parent, index, snapshot, touched, target.blockEpoch);
|
|
1491
|
+
if (isListRelated(target.type) || isListRelated(snapshot.type)) {
|
|
1492
|
+
markListDepthDirty(target.id);
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
}
|
|
1206
1496
|
}
|
|
1207
1497
|
if (isListRelated(target.type)) {
|
|
1208
1498
|
markListDepthDirty(target.id);
|
|
@@ -1216,6 +1506,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1216
1506
|
break;
|
|
1217
1507
|
}
|
|
1218
1508
|
case "reorder": {
|
|
1509
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
1219
1510
|
const parent = resolveParent(nodes, patch.at);
|
|
1220
1511
|
if (!parent) break;
|
|
1221
1512
|
const length = parent.children.length;
|
|
@@ -1230,6 +1521,9 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1230
1521
|
parent.children.splice(to, 0, ...moved);
|
|
1231
1522
|
parent.version++;
|
|
1232
1523
|
touched.add(parent.id);
|
|
1524
|
+
if (getPatchKind(patch) === "semantic") {
|
|
1525
|
+
advanceBlockEpoch(patch.at.blockId, touched, readPatchParseEpoch(patch), readPatchTx(patch));
|
|
1526
|
+
}
|
|
1233
1527
|
if (isListRelated(parent.type)) {
|
|
1234
1528
|
markListDepthDirty(parent.id);
|
|
1235
1529
|
}
|
|
@@ -1245,15 +1539,31 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1245
1539
|
break;
|
|
1246
1540
|
}
|
|
1247
1541
|
case "appendLines": {
|
|
1542
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
1248
1543
|
const parent = resolveParent(nodes, patch.at);
|
|
1249
1544
|
if (!parent) break;
|
|
1250
|
-
|
|
1545
|
+
if (parent.type !== "code") {
|
|
1546
|
+
debugCounters.appendLineGuardRejected += 1;
|
|
1547
|
+
appendDiagnostic(`appendLines target not code:${parent.id}:${parent.type}`);
|
|
1548
|
+
break;
|
|
1549
|
+
}
|
|
1550
|
+
const requestedStartIndex = Math.max(0, patch.startIndex);
|
|
1551
|
+
const startIndex = Math.max(0, Math.min(requestedStartIndex, parent.children.length));
|
|
1251
1552
|
const lineCount = patch.lines?.length ?? 0;
|
|
1252
1553
|
if (lineCount === 0) break;
|
|
1554
|
+
const prefixMatches = parent.children.every((childId, index) => childId === expectedCodeLineId(parent.id, index));
|
|
1555
|
+
const isStrictTailAppend = requestedStartIndex === parent.children.length;
|
|
1556
|
+
if (!isStrictTailAppend || !prefixMatches) {
|
|
1557
|
+
debugCounters.appendLineGuardRejected += 1;
|
|
1558
|
+
appendDiagnostic(
|
|
1559
|
+
`appendLines guard rejected:${parent.id}:requestedStart=${requestedStartIndex}:childCount=${parent.children.length}:prefixMatches=${prefixMatches}`
|
|
1560
|
+
);
|
|
1561
|
+
break;
|
|
1562
|
+
}
|
|
1253
1563
|
const originalChildren = parent.children.slice();
|
|
1254
1564
|
const insertedIds = [];
|
|
1255
1565
|
const insertedSet = /* @__PURE__ */ new Set();
|
|
1256
|
-
const isTrailingAppend =
|
|
1566
|
+
const isTrailingAppend = true;
|
|
1257
1567
|
let parentChildrenChanged = false;
|
|
1258
1568
|
let codeLinesMutated = false;
|
|
1259
1569
|
for (let offset = 0; offset < lineCount; offset++) {
|
|
@@ -1295,7 +1605,8 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1295
1605
|
parentId: parent.id,
|
|
1296
1606
|
children: [],
|
|
1297
1607
|
props: nextLineProps,
|
|
1298
|
-
version: 0
|
|
1608
|
+
version: 0,
|
|
1609
|
+
blockEpoch: parent.blockEpoch
|
|
1299
1610
|
};
|
|
1300
1611
|
nodes.set(lineId, record);
|
|
1301
1612
|
touched.add(lineId);
|
|
@@ -1345,6 +1656,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1345
1656
|
break;
|
|
1346
1657
|
}
|
|
1347
1658
|
case "setHTML": {
|
|
1659
|
+
if (rejectStaleEpochPatch(patch, patch.at)) break;
|
|
1348
1660
|
const target = nodes.get(patch.at.nodeId ?? patch.at.blockId);
|
|
1349
1661
|
if (!target) break;
|
|
1350
1662
|
const sanitized = patch.sanitized ? patch.html : sanitizeHTML(patch.html);
|
|
@@ -1387,6 +1699,30 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1387
1699
|
applyPatch(patch);
|
|
1388
1700
|
}
|
|
1389
1701
|
}
|
|
1702
|
+
const finalizedTableRoots = /* @__PURE__ */ new Set();
|
|
1703
|
+
for (const touchedId of touched) {
|
|
1704
|
+
let cursor = nodes.get(touchedId);
|
|
1705
|
+
while (cursor && cursor.id !== PATCH_ROOT_ID) {
|
|
1706
|
+
if (cursor.type === "table" && cursor.block?.isFinalized && cursor.parentId) {
|
|
1707
|
+
finalizedTableRoots.add(cursor.id);
|
|
1708
|
+
break;
|
|
1709
|
+
}
|
|
1710
|
+
cursor = cursor.parentId ? nodes.get(cursor.parentId) : void 0;
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
if (finalizedTableRoots.size > 0) {
|
|
1714
|
+
for (const tableId of finalizedTableRoots) {
|
|
1715
|
+
const tableNode = nodes.get(tableId);
|
|
1716
|
+
if (!tableNode || !tableNode.block || !tableNode.parentId) continue;
|
|
1717
|
+
const parent = nodes.get(tableNode.parentId);
|
|
1718
|
+
if (!parent) continue;
|
|
1719
|
+
const index = parent.children.indexOf(tableNode.id);
|
|
1720
|
+
if (index === -1) continue;
|
|
1721
|
+
const snapshot = createBlockSnapshot(cloneBlock(tableNode.block));
|
|
1722
|
+
replaceSnapshotAt(nodes, parent, index, snapshot, touched, tableNode.blockEpoch);
|
|
1723
|
+
mutatedBlocks = true;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1390
1726
|
if (mutatedBlocks) {
|
|
1391
1727
|
blocksDirty = true;
|
|
1392
1728
|
}
|
|
@@ -1407,6 +1743,8 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1407
1743
|
normalizeListDepthsForIds(nodes, touched, listDepthDirtyNodes, PATCH_ROOT_ID);
|
|
1408
1744
|
}
|
|
1409
1745
|
}
|
|
1746
|
+
invariantViolations = [...collectStoreInvariantViolations(nodes), ...transientDiagnostics];
|
|
1747
|
+
debugCounters.storeInvariantViolationCount += invariantViolations.length;
|
|
1410
1748
|
if (touched.size > 0 || mutatedBlocks) {
|
|
1411
1749
|
version++;
|
|
1412
1750
|
scheduleNotify();
|
|
@@ -1455,6 +1793,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1455
1793
|
if (!record) {
|
|
1456
1794
|
return EMPTY_NODE_SNAPSHOT;
|
|
1457
1795
|
}
|
|
1796
|
+
const nodeSnapshotCache = getNodeSnapshotCacheForMap(nodes);
|
|
1458
1797
|
const cached = nodeSnapshotCache.get(id);
|
|
1459
1798
|
if (cached && cached.version === record.version && cached.node === record) {
|
|
1460
1799
|
return cached;
|
|
@@ -1474,6 +1813,7 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1474
1813
|
if (!record) {
|
|
1475
1814
|
return EMPTY_CHILDREN_SNAPSHOT;
|
|
1476
1815
|
}
|
|
1816
|
+
const childrenSnapshotCache = getChildrenSnapshotCacheForMap(nodes);
|
|
1477
1817
|
const cached = childrenSnapshotCache.get(id);
|
|
1478
1818
|
if (cached && cached.version === record.version) {
|
|
1479
1819
|
return cached;
|
|
@@ -1483,6 +1823,12 @@ function createRendererStore(initialBlocks = []) {
|
|
|
1483
1823
|
pruneCache(childrenSnapshotCache, CHILDREN_SNAPSHOT_CACHE_LIMIT, CHILDREN_SNAPSHOT_CACHE_BUFFER);
|
|
1484
1824
|
return snapshot;
|
|
1485
1825
|
},
|
|
1826
|
+
getInvariantViolations() {
|
|
1827
|
+
return invariantViolations.slice();
|
|
1828
|
+
},
|
|
1829
|
+
getDebugCounters() {
|
|
1830
|
+
return { ...debugCounters };
|
|
1831
|
+
},
|
|
1486
1832
|
getLastCoalescingMetrics() {
|
|
1487
1833
|
return lastCoalescingMetrics;
|
|
1488
1834
|
},
|