doxum 0.1.40 → 0.1.41
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/advanced.cjs +1 -1
- package/dist/advanced.js +1 -1
- package/dist/index.cjs +121 -51
- package/dist/index.js +121 -51
- package/dist/{transform--WCn6_v0.js → transform-CKJ1ivhz.js} +2 -0
- package/dist/{transform-8xXf0OKB.cjs → transform-D6njdP3k.cjs} +2 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/references/invariants.md +10 -0
package/dist/advanced.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_transform = require("./transform-
|
|
2
|
+
const require_transform = require("./transform-D6njdP3k.cjs");
|
|
3
3
|
const require_record = require("./record-ChYUi8PP.cjs");
|
|
4
4
|
//#region core/src/projection/advanced.ts
|
|
5
5
|
const resetCollectionChange = Object.freeze({ kind: "reset" });
|
package/dist/advanced.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { D as defineProcessor, S as compileProjectionDependencies, _ as assertSynchronous, r as collectionChange, t as createKeyedTransform, w as collectionView } from "./transform
|
|
1
|
+
import { D as defineProcessor, S as compileProjectionDependencies, _ as assertSynchronous, r as collectionChange, t as createKeyedTransform, w as collectionView } from "./transform-CKJ1ivhz.js";
|
|
2
2
|
import { n as isPlainObject } from "./record-B2yRhYsc.js";
|
|
3
3
|
//#region core/src/projection/advanced.ts
|
|
4
4
|
const resetCollectionChange = Object.freeze({ kind: "reset" });
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_transform = require("./transform-
|
|
2
|
+
const require_transform = require("./transform-D6njdP3k.cjs");
|
|
3
3
|
const require_record = require("./record-ChYUi8PP.cjs");
|
|
4
4
|
const require_driver = require("./driver-CN3dGxQI.cjs");
|
|
5
5
|
//#region core/src/schema/model.ts
|
|
@@ -652,7 +652,7 @@ function remove$2(session, container, ids) {
|
|
|
652
652
|
for (const id of removed) session.writeMember(container, id, void 0, "remove");
|
|
653
653
|
table.ids = table.ids.filter((id) => !removed.has(id));
|
|
654
654
|
}
|
|
655
|
-
function replace$
|
|
655
|
+
function replace$4(session, container, id, value) {
|
|
656
656
|
const { at, node, value: tableValue } = container;
|
|
657
657
|
if (node.kind !== "table") return require_driver.fail(at, "invalid-collection", "Expected a table.");
|
|
658
658
|
const table = tableValue;
|
|
@@ -675,7 +675,7 @@ function insert$1(session, container, value, position) {
|
|
|
675
675
|
insert$2(items, index(keys$1, position), value);
|
|
676
676
|
session.invalidate();
|
|
677
677
|
}
|
|
678
|
-
function replace$
|
|
678
|
+
function replace$3(session, container, id, value) {
|
|
679
679
|
const { at, node, value: items } = container;
|
|
680
680
|
if (node.kind !== "list") return require_driver.fail(at, "invalid-list-key", "Expected a list.");
|
|
681
681
|
const index = indexedKeys(items, node.keyOf).index(id);
|
|
@@ -721,7 +721,7 @@ const insert = (session, container, id, value, position) => {
|
|
|
721
721
|
else tree.rootId = id;
|
|
722
722
|
session.invalidate();
|
|
723
723
|
};
|
|
724
|
-
const replace$
|
|
724
|
+
const replace$2 = (session, container, id, value) => {
|
|
725
725
|
const { at, node: schema, value: tree } = container;
|
|
726
726
|
const entry = node$1(tree, id);
|
|
727
727
|
if (!entry) return require_driver.fail(at, "missing-tree-node", "Tree node does not exist.");
|
|
@@ -1180,7 +1180,7 @@ const createAccess = (context, initial = []) => {
|
|
|
1180
1180
|
const [valueOrId, value] = args;
|
|
1181
1181
|
if (args.length === 1) return replaceCollection(target, node, valueOrId);
|
|
1182
1182
|
const writable = writableCollection(target, node);
|
|
1183
|
-
replace$
|
|
1183
|
+
replace$4(context.session, writable, valueOrId, value);
|
|
1184
1184
|
};
|
|
1185
1185
|
return orderedMethod(target, property, node);
|
|
1186
1186
|
};
|
|
@@ -1197,7 +1197,7 @@ const createAccess = (context, initial = []) => {
|
|
|
1197
1197
|
const [valueOrId, value] = args;
|
|
1198
1198
|
if (args.length === 1) return replaceCollection(target, node, valueOrId);
|
|
1199
1199
|
const writable = writableCollection(target, node);
|
|
1200
|
-
replace$
|
|
1200
|
+
replace$3(context.session, writable, valueOrId, value);
|
|
1201
1201
|
};
|
|
1202
1202
|
return orderedMethod(target, property, node);
|
|
1203
1203
|
};
|
|
@@ -1219,7 +1219,7 @@ const createAccess = (context, initial = []) => {
|
|
|
1219
1219
|
const [valueOrId, value] = args;
|
|
1220
1220
|
if (args.length === 1) return replaceCollection(target, node, valueOrId);
|
|
1221
1221
|
const writable = writableTree(target, node);
|
|
1222
|
-
replace$
|
|
1222
|
+
replace$2(context.session, writable, valueOrId, value);
|
|
1223
1223
|
};
|
|
1224
1224
|
const current = () => collectionValue(target, node);
|
|
1225
1225
|
if (property === "rootId") return () => {
|
|
@@ -3798,49 +3798,109 @@ const node = (key, value, left, right) => {
|
|
|
3798
3798
|
height: Math.max(height(left), height(right)) + 1
|
|
3799
3799
|
});
|
|
3800
3800
|
};
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
}
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
return
|
|
3814
|
-
}
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
if (
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
}
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
if (key > current.key) return rebalance(node(current.key, current.value, current.left, removeNode(current.right, key)));
|
|
3801
|
+
/** Local repair after a join descent or a single deletion (height difference at most two). */
|
|
3802
|
+
const balance = (key, value, left, right) => {
|
|
3803
|
+
if (height(left) > height(right) + 1) {
|
|
3804
|
+
const heavy = left;
|
|
3805
|
+
if (height(heavy.left) >= height(heavy.right)) return node(heavy.key, heavy.value, heavy.left, node(key, value, heavy.right, right));
|
|
3806
|
+
const middle = heavy.right;
|
|
3807
|
+
return node(middle.key, middle.value, node(heavy.key, heavy.value, heavy.left, middle.left), node(key, value, middle.right, right));
|
|
3808
|
+
}
|
|
3809
|
+
if (height(right) > height(left) + 1) {
|
|
3810
|
+
const heavy = right;
|
|
3811
|
+
if (height(heavy.right) >= height(heavy.left)) return node(heavy.key, heavy.value, node(key, value, left, heavy.left), heavy.right);
|
|
3812
|
+
const middle = heavy.left;
|
|
3813
|
+
return node(middle.key, middle.value, node(key, value, left, middle.left), node(heavy.key, heavy.value, middle.right, heavy.right));
|
|
3814
|
+
}
|
|
3815
|
+
return node(key, value, left, right);
|
|
3816
|
+
};
|
|
3817
|
+
/** All left keys precede key, all right keys follow it; heights may differ arbitrarily. */
|
|
3818
|
+
const join = (key, value, left, right) => {
|
|
3819
|
+
if (height(left) > height(right) + 1) {
|
|
3820
|
+
require_transform.profile.collectionIndex.visit();
|
|
3821
|
+
return balance(left.key, left.value, left.left, join(key, value, left.right, right));
|
|
3822
|
+
}
|
|
3823
|
+
if (height(right) > height(left) + 1) {
|
|
3824
|
+
require_transform.profile.collectionIndex.visit();
|
|
3825
|
+
return balance(right.key, right.value, join(key, value, left, right.left), right.right);
|
|
3826
|
+
}
|
|
3827
|
+
return node(key, value, left, right);
|
|
3828
|
+
};
|
|
3829
|
+
const removeMin = (current) => {
|
|
3830
|
+
require_transform.profile.collectionIndex.visit();
|
|
3832
3831
|
if (!current.left) return current.right;
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3832
|
+
return balance(current.key, current.value, removeMin(current.left), current.right);
|
|
3833
|
+
};
|
|
3834
|
+
const concat = (left, right) => {
|
|
3835
|
+
if (!left) return right;
|
|
3836
|
+
if (!right) return left;
|
|
3837
|
+
let first = right;
|
|
3838
|
+
while (first.left) {
|
|
3839
|
+
require_transform.profile.collectionIndex.visit();
|
|
3840
|
+
first = first.left;
|
|
3841
|
+
}
|
|
3842
|
+
return join(first.key, first.value, left, removeMin(right));
|
|
3843
|
+
};
|
|
3844
|
+
const replace$1 = (current, entry, left, right) => {
|
|
3845
|
+
if (entry && !entry.present) return concat(left, right);
|
|
3846
|
+
const value = entry ? entry.value : current.value;
|
|
3847
|
+
if (left === current.left && right === current.right && Object.is(value, current.value)) return current;
|
|
3848
|
+
return join(current.key, value, left, right);
|
|
3849
|
+
};
|
|
3850
|
+
const edit = (current, key, entry) => {
|
|
3851
|
+
if (!current) return entry.present ? node(key, entry.value) : void 0;
|
|
3852
|
+
require_transform.profile.collectionIndex.visit();
|
|
3853
|
+
if (key === current.key) return replace$1(current, entry, current.left, current.right);
|
|
3854
|
+
if (key < current.key) {
|
|
3855
|
+
const left = edit(current.left, key, entry);
|
|
3856
|
+
return left === current.left ? current : balance(current.key, current.value, left, current.right);
|
|
3857
|
+
}
|
|
3858
|
+
const right = edit(current.right, key, entry);
|
|
3859
|
+
return right === current.right ? current : balance(current.key, current.value, current.left, right);
|
|
3860
|
+
};
|
|
3861
|
+
/** Builds a new subtree in linear time, ignoring removals of absent keys. */
|
|
3862
|
+
const buildChanges = (keys, changes, start, end) => {
|
|
3863
|
+
let count = 0;
|
|
3864
|
+
for (let i = start; i < end; i++) if (changes.get(keys[i]).present) count++;
|
|
3865
|
+
let cursor = start;
|
|
3866
|
+
const build = (size) => {
|
|
3867
|
+
if (!size) return void 0;
|
|
3868
|
+
const leftSize = size >> 1;
|
|
3869
|
+
const left = build(leftSize);
|
|
3870
|
+
let key;
|
|
3871
|
+
let entry;
|
|
3872
|
+
do {
|
|
3873
|
+
key = keys[cursor++];
|
|
3874
|
+
entry = changes.get(key);
|
|
3875
|
+
} while (!entry.present);
|
|
3876
|
+
return node(key, entry.value, left, build(size - leftSize - 1));
|
|
3877
|
+
};
|
|
3878
|
+
return build(count);
|
|
3879
|
+
};
|
|
3880
|
+
const applyChanges = (current, keys, changes, start, end) => {
|
|
3881
|
+
if (start === end) return current;
|
|
3882
|
+
if (end - start === 1) return edit(current, keys[start], changes.get(keys[start]));
|
|
3883
|
+
if (!current) return buildChanges(keys, changes, start, end);
|
|
3884
|
+
require_transform.profile.collectionIndex.visit();
|
|
3885
|
+
let low = start;
|
|
3886
|
+
let high = end;
|
|
3887
|
+
while (low < high) {
|
|
3888
|
+
const middle = low + high >>> 1;
|
|
3889
|
+
if (keys[middle] < current.key) low = middle + 1;
|
|
3890
|
+
else high = middle;
|
|
3891
|
+
}
|
|
3892
|
+
const matched = low < end && keys[low] === current.key;
|
|
3893
|
+
const left = applyChanges(current.left, keys, changes, start, low);
|
|
3894
|
+
const right = applyChanges(current.right, keys, changes, matched ? low + 1 : low, end);
|
|
3895
|
+
return replace$1(current, matched ? changes.get(current.key) : void 0, left, right);
|
|
3836
3896
|
};
|
|
3837
3897
|
const fromSorted = (entries, start = 0, end = entries.length) => {
|
|
3838
3898
|
if (start >= end) return void 0;
|
|
3839
|
-
const middle = start + end
|
|
3899
|
+
const middle = start + end >>> 1;
|
|
3840
3900
|
const [key, value] = entries[middle];
|
|
3841
3901
|
return node(key, value, fromSorted(entries, start, middle), fromSorted(entries, middle + 1, end));
|
|
3842
3902
|
};
|
|
3843
|
-
/** Immutable keyed lookup used to keep
|
|
3903
|
+
/** Immutable keyed lookup used to keep collection snapshots durable across revisions. */
|
|
3844
3904
|
var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
3845
3905
|
constructor(root) {
|
|
3846
3906
|
this.root = root;
|
|
@@ -3848,6 +3908,7 @@ var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
|
3848
3908
|
static empty() {
|
|
3849
3909
|
return new PersistentKeyedIndex();
|
|
3850
3910
|
}
|
|
3911
|
+
/** The input must contain unique keys. Formal collection order is owned separately. */
|
|
3851
3912
|
static from(entries) {
|
|
3852
3913
|
const sorted = [...entries].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
3853
3914
|
require_transform.profile.collectionIndex.build(sorted.length);
|
|
@@ -3868,11 +3929,19 @@ var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
|
3868
3929
|
}
|
|
3869
3930
|
return false;
|
|
3870
3931
|
}
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
|
|
3932
|
+
/** Applies accepted final entry intents together; never calls user equality or changes old roots. */
|
|
3933
|
+
apply(changes) {
|
|
3934
|
+
if (!changes.size) return this;
|
|
3935
|
+
require_transform.profile.collectionIndex.batch(changes.size > 1 ? changes.size : 0);
|
|
3936
|
+
let root;
|
|
3937
|
+
if (changes.size === 1) {
|
|
3938
|
+
const [key, entry] = changes.entries().next().value;
|
|
3939
|
+
root = edit(this.root, key, entry);
|
|
3940
|
+
} else {
|
|
3941
|
+
const keys = [...changes.keys()].sort();
|
|
3942
|
+
root = applyChanges(this.root, keys, changes, 0, keys.length);
|
|
3943
|
+
}
|
|
3944
|
+
return root === this.root ? this : new PersistentKeyedIndex(root);
|
|
3876
3945
|
}
|
|
3877
3946
|
};
|
|
3878
3947
|
//#endregion
|
|
@@ -3910,10 +3979,11 @@ const createCollectionState = (initial) => {
|
|
|
3910
3979
|
values.clear();
|
|
3911
3980
|
for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
|
|
3912
3981
|
if (index) index = PersistentKeyedIndex.from(values);
|
|
3913
|
-
} else
|
|
3914
|
-
|
|
3915
|
-
if (entry.present) values.set(key, entry.value);
|
|
3982
|
+
} else {
|
|
3983
|
+
const nextIndex = index?.apply(staged);
|
|
3984
|
+
for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
|
|
3916
3985
|
else values.delete(key);
|
|
3986
|
+
index = nextIndex;
|
|
3917
3987
|
}
|
|
3918
3988
|
ids = nextIds;
|
|
3919
3989
|
},
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { A as ownProjections, C as snapshotProjectionValues, D as defineProcessor, E as snapshotCollectionView, M as projectionRef, N as profile, O as defineSource, S as compileProjectionDependencies, T as mapRead, _ as assertSynchronous, a as collectionChangeTouchesKey, b as ProjectionError, c as assertKeyedProjection, d as outputKind, f as createKeyRelation, g as assertScope, h as snapshotArray, i as collectionChangeIntersects, j as producerOf, k as isProjection, l as compileKeyedDependencies, m as sameArray, n as keyedAbsent, o as collectionHasStructuralChange, p as iterableMatchesArray, s as createCollectionChange, t as createKeyedTransform, u as createKeyedDependencyRuntime, v as createScheduler, w as collectionView, x as collectKeyedInvalidation, y as ProjectionDisposedError } from "./transform
|
|
1
|
+
import { A as ownProjections, C as snapshotProjectionValues, D as defineProcessor, E as snapshotCollectionView, M as projectionRef, N as profile, O as defineSource, S as compileProjectionDependencies, T as mapRead, _ as assertSynchronous, a as collectionChangeTouchesKey, b as ProjectionError, c as assertKeyedProjection, d as outputKind, f as createKeyRelation, g as assertScope, h as snapshotArray, i as collectionChangeIntersects, j as producerOf, k as isProjection, l as compileKeyedDependencies, m as sameArray, n as keyedAbsent, o as collectionHasStructuralChange, p as iterableMatchesArray, s as createCollectionChange, t as createKeyedTransform, u as createKeyedDependencyRuntime, v as createScheduler, w as collectionView, x as collectKeyedInvalidation, y as ProjectionDisposedError } from "./transform-CKJ1ivhz.js";
|
|
2
2
|
import { n as isPlainObject, r as isRecord, t as installOwn } from "./record-B2yRhYsc.js";
|
|
3
3
|
import { c as AddressIndex, d as invalidValue, f as children, g as validate, h as parent, i as contextOf, l as MutationRejected, m as is, o as decodeChanges, p as contains$1, r as bindContext, s as sealChanges, t as assertRuntimeWritable, u as fail } from "./driver-B7RR-VfX.js";
|
|
4
4
|
//#region core/src/schema/model.ts
|
|
@@ -651,7 +651,7 @@ function remove$2(session, container, ids) {
|
|
|
651
651
|
for (const id of removed) session.writeMember(container, id, void 0, "remove");
|
|
652
652
|
table.ids = table.ids.filter((id) => !removed.has(id));
|
|
653
653
|
}
|
|
654
|
-
function replace$
|
|
654
|
+
function replace$4(session, container, id, value) {
|
|
655
655
|
const { at, node, value: tableValue } = container;
|
|
656
656
|
if (node.kind !== "table") return fail(at, "invalid-collection", "Expected a table.");
|
|
657
657
|
const table = tableValue;
|
|
@@ -674,7 +674,7 @@ function insert$1(session, container, value, position) {
|
|
|
674
674
|
insert$2(items, index(keys$1, position), value);
|
|
675
675
|
session.invalidate();
|
|
676
676
|
}
|
|
677
|
-
function replace$
|
|
677
|
+
function replace$3(session, container, id, value) {
|
|
678
678
|
const { at, node, value: items } = container;
|
|
679
679
|
if (node.kind !== "list") return fail(at, "invalid-list-key", "Expected a list.");
|
|
680
680
|
const index = indexedKeys(items, node.keyOf).index(id);
|
|
@@ -720,7 +720,7 @@ const insert = (session, container, id, value, position) => {
|
|
|
720
720
|
else tree.rootId = id;
|
|
721
721
|
session.invalidate();
|
|
722
722
|
};
|
|
723
|
-
const replace$
|
|
723
|
+
const replace$2 = (session, container, id, value) => {
|
|
724
724
|
const { at, node: schema, value: tree } = container;
|
|
725
725
|
const entry = node$1(tree, id);
|
|
726
726
|
if (!entry) return fail(at, "missing-tree-node", "Tree node does not exist.");
|
|
@@ -1179,7 +1179,7 @@ const createAccess = (context, initial = []) => {
|
|
|
1179
1179
|
const [valueOrId, value] = args;
|
|
1180
1180
|
if (args.length === 1) return replaceCollection(target, node, valueOrId);
|
|
1181
1181
|
const writable = writableCollection(target, node);
|
|
1182
|
-
replace$
|
|
1182
|
+
replace$4(context.session, writable, valueOrId, value);
|
|
1183
1183
|
};
|
|
1184
1184
|
return orderedMethod(target, property, node);
|
|
1185
1185
|
};
|
|
@@ -1196,7 +1196,7 @@ const createAccess = (context, initial = []) => {
|
|
|
1196
1196
|
const [valueOrId, value] = args;
|
|
1197
1197
|
if (args.length === 1) return replaceCollection(target, node, valueOrId);
|
|
1198
1198
|
const writable = writableCollection(target, node);
|
|
1199
|
-
replace$
|
|
1199
|
+
replace$3(context.session, writable, valueOrId, value);
|
|
1200
1200
|
};
|
|
1201
1201
|
return orderedMethod(target, property, node);
|
|
1202
1202
|
};
|
|
@@ -1218,7 +1218,7 @@ const createAccess = (context, initial = []) => {
|
|
|
1218
1218
|
const [valueOrId, value] = args;
|
|
1219
1219
|
if (args.length === 1) return replaceCollection(target, node, valueOrId);
|
|
1220
1220
|
const writable = writableTree(target, node);
|
|
1221
|
-
replace$
|
|
1221
|
+
replace$2(context.session, writable, valueOrId, value);
|
|
1222
1222
|
};
|
|
1223
1223
|
const current = () => collectionValue(target, node);
|
|
1224
1224
|
if (property === "rootId") return () => {
|
|
@@ -3797,49 +3797,109 @@ const node = (key, value, left, right) => {
|
|
|
3797
3797
|
height: Math.max(height(left), height(right)) + 1
|
|
3798
3798
|
});
|
|
3799
3799
|
};
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
}
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
return
|
|
3813
|
-
}
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
if (
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
}
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
if (key > current.key) return rebalance(node(current.key, current.value, current.left, removeNode(current.right, key)));
|
|
3800
|
+
/** Local repair after a join descent or a single deletion (height difference at most two). */
|
|
3801
|
+
const balance = (key, value, left, right) => {
|
|
3802
|
+
if (height(left) > height(right) + 1) {
|
|
3803
|
+
const heavy = left;
|
|
3804
|
+
if (height(heavy.left) >= height(heavy.right)) return node(heavy.key, heavy.value, heavy.left, node(key, value, heavy.right, right));
|
|
3805
|
+
const middle = heavy.right;
|
|
3806
|
+
return node(middle.key, middle.value, node(heavy.key, heavy.value, heavy.left, middle.left), node(key, value, middle.right, right));
|
|
3807
|
+
}
|
|
3808
|
+
if (height(right) > height(left) + 1) {
|
|
3809
|
+
const heavy = right;
|
|
3810
|
+
if (height(heavy.right) >= height(heavy.left)) return node(heavy.key, heavy.value, node(key, value, left, heavy.left), heavy.right);
|
|
3811
|
+
const middle = heavy.left;
|
|
3812
|
+
return node(middle.key, middle.value, node(key, value, left, middle.left), node(heavy.key, heavy.value, middle.right, heavy.right));
|
|
3813
|
+
}
|
|
3814
|
+
return node(key, value, left, right);
|
|
3815
|
+
};
|
|
3816
|
+
/** All left keys precede key, all right keys follow it; heights may differ arbitrarily. */
|
|
3817
|
+
const join = (key, value, left, right) => {
|
|
3818
|
+
if (height(left) > height(right) + 1) {
|
|
3819
|
+
profile.collectionIndex.visit();
|
|
3820
|
+
return balance(left.key, left.value, left.left, join(key, value, left.right, right));
|
|
3821
|
+
}
|
|
3822
|
+
if (height(right) > height(left) + 1) {
|
|
3823
|
+
profile.collectionIndex.visit();
|
|
3824
|
+
return balance(right.key, right.value, join(key, value, left, right.left), right.right);
|
|
3825
|
+
}
|
|
3826
|
+
return node(key, value, left, right);
|
|
3827
|
+
};
|
|
3828
|
+
const removeMin = (current) => {
|
|
3829
|
+
profile.collectionIndex.visit();
|
|
3831
3830
|
if (!current.left) return current.right;
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3831
|
+
return balance(current.key, current.value, removeMin(current.left), current.right);
|
|
3832
|
+
};
|
|
3833
|
+
const concat = (left, right) => {
|
|
3834
|
+
if (!left) return right;
|
|
3835
|
+
if (!right) return left;
|
|
3836
|
+
let first = right;
|
|
3837
|
+
while (first.left) {
|
|
3838
|
+
profile.collectionIndex.visit();
|
|
3839
|
+
first = first.left;
|
|
3840
|
+
}
|
|
3841
|
+
return join(first.key, first.value, left, removeMin(right));
|
|
3842
|
+
};
|
|
3843
|
+
const replace$1 = (current, entry, left, right) => {
|
|
3844
|
+
if (entry && !entry.present) return concat(left, right);
|
|
3845
|
+
const value = entry ? entry.value : current.value;
|
|
3846
|
+
if (left === current.left && right === current.right && Object.is(value, current.value)) return current;
|
|
3847
|
+
return join(current.key, value, left, right);
|
|
3848
|
+
};
|
|
3849
|
+
const edit = (current, key, entry) => {
|
|
3850
|
+
if (!current) return entry.present ? node(key, entry.value) : void 0;
|
|
3851
|
+
profile.collectionIndex.visit();
|
|
3852
|
+
if (key === current.key) return replace$1(current, entry, current.left, current.right);
|
|
3853
|
+
if (key < current.key) {
|
|
3854
|
+
const left = edit(current.left, key, entry);
|
|
3855
|
+
return left === current.left ? current : balance(current.key, current.value, left, current.right);
|
|
3856
|
+
}
|
|
3857
|
+
const right = edit(current.right, key, entry);
|
|
3858
|
+
return right === current.right ? current : balance(current.key, current.value, current.left, right);
|
|
3859
|
+
};
|
|
3860
|
+
/** Builds a new subtree in linear time, ignoring removals of absent keys. */
|
|
3861
|
+
const buildChanges = (keys, changes, start, end) => {
|
|
3862
|
+
let count = 0;
|
|
3863
|
+
for (let i = start; i < end; i++) if (changes.get(keys[i]).present) count++;
|
|
3864
|
+
let cursor = start;
|
|
3865
|
+
const build = (size) => {
|
|
3866
|
+
if (!size) return void 0;
|
|
3867
|
+
const leftSize = size >> 1;
|
|
3868
|
+
const left = build(leftSize);
|
|
3869
|
+
let key;
|
|
3870
|
+
let entry;
|
|
3871
|
+
do {
|
|
3872
|
+
key = keys[cursor++];
|
|
3873
|
+
entry = changes.get(key);
|
|
3874
|
+
} while (!entry.present);
|
|
3875
|
+
return node(key, entry.value, left, build(size - leftSize - 1));
|
|
3876
|
+
};
|
|
3877
|
+
return build(count);
|
|
3878
|
+
};
|
|
3879
|
+
const applyChanges = (current, keys, changes, start, end) => {
|
|
3880
|
+
if (start === end) return current;
|
|
3881
|
+
if (end - start === 1) return edit(current, keys[start], changes.get(keys[start]));
|
|
3882
|
+
if (!current) return buildChanges(keys, changes, start, end);
|
|
3883
|
+
profile.collectionIndex.visit();
|
|
3884
|
+
let low = start;
|
|
3885
|
+
let high = end;
|
|
3886
|
+
while (low < high) {
|
|
3887
|
+
const middle = low + high >>> 1;
|
|
3888
|
+
if (keys[middle] < current.key) low = middle + 1;
|
|
3889
|
+
else high = middle;
|
|
3890
|
+
}
|
|
3891
|
+
const matched = low < end && keys[low] === current.key;
|
|
3892
|
+
const left = applyChanges(current.left, keys, changes, start, low);
|
|
3893
|
+
const right = applyChanges(current.right, keys, changes, matched ? low + 1 : low, end);
|
|
3894
|
+
return replace$1(current, matched ? changes.get(current.key) : void 0, left, right);
|
|
3835
3895
|
};
|
|
3836
3896
|
const fromSorted = (entries, start = 0, end = entries.length) => {
|
|
3837
3897
|
if (start >= end) return void 0;
|
|
3838
|
-
const middle = start + end
|
|
3898
|
+
const middle = start + end >>> 1;
|
|
3839
3899
|
const [key, value] = entries[middle];
|
|
3840
3900
|
return node(key, value, fromSorted(entries, start, middle), fromSorted(entries, middle + 1, end));
|
|
3841
3901
|
};
|
|
3842
|
-
/** Immutable keyed lookup used to keep
|
|
3902
|
+
/** Immutable keyed lookup used to keep collection snapshots durable across revisions. */
|
|
3843
3903
|
var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
3844
3904
|
constructor(root) {
|
|
3845
3905
|
this.root = root;
|
|
@@ -3847,6 +3907,7 @@ var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
|
3847
3907
|
static empty() {
|
|
3848
3908
|
return new PersistentKeyedIndex();
|
|
3849
3909
|
}
|
|
3910
|
+
/** The input must contain unique keys. Formal collection order is owned separately. */
|
|
3850
3911
|
static from(entries) {
|
|
3851
3912
|
const sorted = [...entries].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
3852
3913
|
profile.collectionIndex.build(sorted.length);
|
|
@@ -3867,11 +3928,19 @@ var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
|
3867
3928
|
}
|
|
3868
3929
|
return false;
|
|
3869
3930
|
}
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3931
|
+
/** Applies accepted final entry intents together; never calls user equality or changes old roots. */
|
|
3932
|
+
apply(changes) {
|
|
3933
|
+
if (!changes.size) return this;
|
|
3934
|
+
profile.collectionIndex.batch(changes.size > 1 ? changes.size : 0);
|
|
3935
|
+
let root;
|
|
3936
|
+
if (changes.size === 1) {
|
|
3937
|
+
const [key, entry] = changes.entries().next().value;
|
|
3938
|
+
root = edit(this.root, key, entry);
|
|
3939
|
+
} else {
|
|
3940
|
+
const keys = [...changes.keys()].sort();
|
|
3941
|
+
root = applyChanges(this.root, keys, changes, 0, keys.length);
|
|
3942
|
+
}
|
|
3943
|
+
return root === this.root ? this : new PersistentKeyedIndex(root);
|
|
3875
3944
|
}
|
|
3876
3945
|
};
|
|
3877
3946
|
//#endregion
|
|
@@ -3909,10 +3978,11 @@ const createCollectionState = (initial) => {
|
|
|
3909
3978
|
values.clear();
|
|
3910
3979
|
for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
|
|
3911
3980
|
if (index) index = PersistentKeyedIndex.from(values);
|
|
3912
|
-
} else
|
|
3913
|
-
|
|
3914
|
-
if (entry.present) values.set(key, entry.value);
|
|
3981
|
+
} else {
|
|
3982
|
+
const nextIndex = index?.apply(staged);
|
|
3983
|
+
for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
|
|
3915
3984
|
else values.delete(key);
|
|
3985
|
+
index = nextIndex;
|
|
3916
3986
|
}
|
|
3917
3987
|
ids = nextIds;
|
|
3918
3988
|
},
|
package/package.json
CHANGED
|
@@ -61,6 +61,16 @@ This file is maintainer-only. Application/library consumers should use [Public A
|
|
|
61
61
|
initialization failure releases already-created projection resources and preserves the
|
|
62
62
|
original error. Document dirty/pending state has one write owner; structural
|
|
63
63
|
materialization only reads it.
|
|
64
|
+
Collection storage installs an accepted entry batch through one persistent index
|
|
65
|
+
application. The index owns AVL batch merge/balance; storage owns the current Map
|
|
66
|
+
and formal ids; output sealing owns equality and publication. Share untouched
|
|
67
|
+
subtrees and copy the affected path union once for pure value updates. Batch
|
|
68
|
+
structural changes require height-aware join/concat, not just single-edit repair.
|
|
69
|
+
Preserve lazy index creation, durable old snapshots, zero index work for pure
|
|
70
|
+
reorder, and per-install versions for demanded reads inside a Runtime batch.
|
|
71
|
+
Sorting changed keys is internal lookup work, never formal order. Count node
|
|
72
|
+
allocations and sorting separately from selector calls; this is not an O(delta)
|
|
73
|
+
guarantee for all collection maintenance.
|
|
64
74
|
10. Local sync uses Web Lock leadership and contiguous durable sequence. Writes precede
|
|
65
75
|
asynchronous persistence. Version 5 / format 3 rejects old databases without editing
|
|
66
76
|
them. External replace and remote apply are prohibited while attached.
|