doxum 0.1.27 → 0.1.29
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/README.md +66 -3
- package/dist/advanced.cjs +40 -14
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +27 -2
- package/dist/advanced.d.ts +27 -2
- package/dist/advanced.js +27 -1
- package/dist/advanced.js.map +1 -1
- package/dist/{definition-xgk3YNBp.d.ts → dependency-B3E65M1E.d.cts} +27 -3
- package/dist/{definition-CDOuvb5C.d.cts → dependency-BYbdDHNa.d.ts} +27 -3
- package/dist/{driver-B5vdPohL.js → driver-B7RR-VfX.js} +31 -12
- package/dist/driver-B7RR-VfX.js.map +1 -0
- package/dist/{driver-BL91gn7_.cjs → driver-CN3dGxQI.cjs} +31 -12
- package/dist/driver-CN3dGxQI.cjs.map +1 -0
- package/dist/index.cjs +824 -436
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -32
- package/dist/index.d.ts +23 -32
- package/dist/index.js +713 -325
- package/dist/index.js.map +1 -1
- package/dist/local-sync.cjs +1 -1
- package/dist/local-sync.cjs.map +1 -1
- package/dist/local-sync.js +1 -1
- package/dist/local-sync.js.map +1 -1
- package/dist/{scheduler-k1ZGR1PG.cjs → transform-B6YxfR0R.cjs} +550 -43
- package/dist/transform-B6YxfR0R.cjs.map +1 -0
- package/dist/transform-BQY9I6il.js +1034 -0
- package/dist/transform-BQY9I6il.js.map +1 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +11 -5
- package/skills/doxum-runtime/references/api.en.md +40 -21
- package/skills/doxum-runtime/references/api.zh-CN.md +21 -2
- package/skills/doxum-runtime/references/guide.en.md +2 -1
- package/skills/doxum-runtime/references/guide.zh-CN.md +2 -1
- package/skills/doxum-runtime/references/invariants.en.md +2 -2
- package/skills/doxum-runtime/references/invariants.zh-CN.md +2 -2
- package/skills/doxum-runtime/references/patterns.en.md +1 -1
- package/skills/doxum-runtime/references/patterns.zh-CN.md +1 -1
- package/skills/doxum-runtime/references/projections.en.md +47 -3
- package/skills/doxum-runtime/references/projections.zh-CN.md +45 -3
- package/dist/driver-B5vdPohL.js.map +0 -1
- package/dist/driver-BL91gn7_.cjs.map +0 -1
- package/dist/scheduler-k1ZGR1PG.cjs.map +0 -1
- package/dist/scheduler-vzf3OcjT.js +0 -593
- package/dist/scheduler-vzf3OcjT.js.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
2
|
+
const require_transform = require("./transform-B6YxfR0R.cjs");
|
|
3
3
|
const require_record = require("./record-ChYUi8PP.cjs");
|
|
4
|
-
const require_driver = require("./driver-
|
|
4
|
+
const require_driver = require("./driver-CN3dGxQI.cjs");
|
|
5
5
|
//#region core/src/schema/model.ts
|
|
6
6
|
const schemaNodeOf = (schema) => schema;
|
|
7
7
|
const collectionEntryNode = (node) => node.kind === "map" || node.kind === "table" || node.kind === "list" ? node.value : void 0;
|
|
@@ -59,9 +59,9 @@ function tree(value) {
|
|
|
59
59
|
const length$1 = (keys) => keys.length;
|
|
60
60
|
const keyAt = (keys, index) => Array.isArray(keys) ? keys[index] : keys.at(index);
|
|
61
61
|
const equal = (left, right) => {
|
|
62
|
-
|
|
62
|
+
require_transform.profile.equality.call();
|
|
63
63
|
if (left === right) return true;
|
|
64
|
-
|
|
64
|
+
require_transform.profile.equality.container();
|
|
65
65
|
if (left.length !== right.length) return false;
|
|
66
66
|
for (let index = 0; index < left.length; index++) if (left[index] !== keyAt(right, index)) return false;
|
|
67
67
|
return true;
|
|
@@ -84,7 +84,7 @@ const knownKeys = (values) => {
|
|
|
84
84
|
if (!positions) {
|
|
85
85
|
positions = /* @__PURE__ */ new Map();
|
|
86
86
|
for (let index = 0; index < values.length; index++) positions.set(values[index], index);
|
|
87
|
-
|
|
87
|
+
require_transform.profile.address.listIndex(values.length);
|
|
88
88
|
}
|
|
89
89
|
return positions.get(key) ?? -1;
|
|
90
90
|
}
|
|
@@ -108,7 +108,7 @@ const indexedKeys = (values, keyOf) => {
|
|
|
108
108
|
if (!positions) {
|
|
109
109
|
positions = /* @__PURE__ */ new Map();
|
|
110
110
|
for (let index = 0; index < values.length; index++) positions.set(keyOf(values[index]), index);
|
|
111
|
-
|
|
111
|
+
require_transform.profile.address.listIndex(values.length);
|
|
112
112
|
}
|
|
113
113
|
return positions.get(key) ?? -1;
|
|
114
114
|
}
|
|
@@ -187,7 +187,7 @@ const compiledShape = (node, value) => {
|
|
|
187
187
|
if (!layout) {
|
|
188
188
|
const members = /* @__PURE__ */ new Map();
|
|
189
189
|
for (const key of Object.keys(shape).sort()) {
|
|
190
|
-
|
|
190
|
+
require_transform.profile.address.schemaStep();
|
|
191
191
|
members.set(key, {
|
|
192
192
|
kind: "fixed",
|
|
193
193
|
key,
|
|
@@ -341,7 +341,7 @@ const checkTreePayload = (node, value, address, strict = false) => {
|
|
|
341
341
|
return present ? checkValue(node, value.value, address, strict) : void 0;
|
|
342
342
|
};
|
|
343
343
|
const copyTreeNode = (value) => {
|
|
344
|
-
|
|
344
|
+
require_transform.profile.copy.treeNode();
|
|
345
345
|
return {
|
|
346
346
|
...value,
|
|
347
347
|
children: [...value.children]
|
|
@@ -355,7 +355,7 @@ const equalTreeNode = (left, right) => {
|
|
|
355
355
|
/** Copy editable structure; atomic payloads retain their immutable ownership contract. */
|
|
356
356
|
const copyValue = (node, value) => {
|
|
357
357
|
if (value === void 0 || node.kind === "field") return value;
|
|
358
|
-
|
|
358
|
+
require_transform.profile.copy.structure();
|
|
359
359
|
if ((node.kind === "object" || node.kind === "variant") && require_record.isRecord(value)) {
|
|
360
360
|
const result = Object.create(Object.getPrototypeOf(value));
|
|
361
361
|
const { members } = compiledShape(node, value);
|
|
@@ -519,8 +519,8 @@ const resolveWithPrefix = (schema, root, address, prefix) => {
|
|
|
519
519
|
}
|
|
520
520
|
let cacheable = true;
|
|
521
521
|
for (let index = start; index < address.length - 1; index += 1) {
|
|
522
|
-
|
|
523
|
-
|
|
522
|
+
require_transform.profile.address.schemaStep();
|
|
523
|
+
require_transform.profile.address.documentStep();
|
|
524
524
|
const resolved = step(node, current, address[index]);
|
|
525
525
|
if (!resolved) return void 0;
|
|
526
526
|
if (node?.kind !== "object" && node?.kind !== "table" && node?.kind !== "map") cacheable = false;
|
|
@@ -538,8 +538,8 @@ const resolveWithPrefix = (schema, root, address, prefix) => {
|
|
|
538
538
|
};
|
|
539
539
|
/** Resolves one member from an already current schema/container pair. */
|
|
540
540
|
const resolveChild = (node, current, last) => {
|
|
541
|
-
|
|
542
|
-
|
|
541
|
+
require_transform.profile.address.schemaStep();
|
|
542
|
+
require_transform.profile.address.documentStep();
|
|
543
543
|
const resolved = step(node, current, last);
|
|
544
544
|
if (!resolved || !require_record.isRecord(current) && !Array.isArray(current)) return void 0;
|
|
545
545
|
if (node?.kind === "table" && require_record.isRecord(current) && require_record.isRecord(current.byId)) return {
|
|
@@ -569,18 +569,44 @@ const moveSelection = (current, selection, position) => {
|
|
|
569
569
|
selected.add(key);
|
|
570
570
|
}
|
|
571
571
|
const moved = [];
|
|
572
|
-
|
|
573
|
-
|
|
572
|
+
let remainingLength = 0;
|
|
573
|
+
let anchorIndex = -1;
|
|
574
|
+
const anchoredKey = position && ("before" in position ? position.before : "after" in position ? position.after : void 0);
|
|
575
|
+
for (const key of current) {
|
|
576
|
+
if (selected.has(key)) {
|
|
577
|
+
moved.push(key);
|
|
578
|
+
continue;
|
|
579
|
+
}
|
|
580
|
+
if (key === anchoredKey) anchorIndex = remainingLength;
|
|
581
|
+
remainingLength++;
|
|
582
|
+
}
|
|
574
583
|
if (moved.length !== selected.size) return { kind: "missing" };
|
|
575
|
-
|
|
576
|
-
|
|
584
|
+
let insertion;
|
|
585
|
+
if (!position || "at" in position && position.at === "end") insertion = remainingLength;
|
|
586
|
+
else if ("at" in position) {
|
|
587
|
+
if (position.at !== "start") return { kind: "invalid-anchor" };
|
|
588
|
+
insertion = 0;
|
|
589
|
+
} else {
|
|
590
|
+
if (anchorIndex < 0) return { kind: "invalid-anchor" };
|
|
591
|
+
insertion = "before" in position ? anchorIndex : anchorIndex + 1;
|
|
592
|
+
}
|
|
593
|
+
const order = new Array(current.length);
|
|
594
|
+
let outputIndex = 0;
|
|
595
|
+
let remainingIndex = 0;
|
|
596
|
+
let inserted = false;
|
|
597
|
+
for (const key of current) {
|
|
598
|
+
if (selected.has(key)) continue;
|
|
599
|
+
if (!inserted && remainingIndex === insertion) {
|
|
600
|
+
for (const movedKey of moved) order[outputIndex++] = movedKey;
|
|
601
|
+
inserted = true;
|
|
602
|
+
}
|
|
603
|
+
order[outputIndex++] = key;
|
|
604
|
+
remainingIndex++;
|
|
605
|
+
}
|
|
606
|
+
if (!inserted) for (const movedKey of moved) order[outputIndex++] = movedKey;
|
|
577
607
|
return {
|
|
578
608
|
kind: "ok",
|
|
579
|
-
order
|
|
580
|
-
...remaining.slice(0, insertion),
|
|
581
|
-
...moved,
|
|
582
|
-
...remaining.slice(insertion)
|
|
583
|
-
]
|
|
609
|
+
order
|
|
584
610
|
};
|
|
585
611
|
};
|
|
586
612
|
const index = (keys, anchor) => {
|
|
@@ -817,7 +843,7 @@ const sameTreeTarget = (left, right) => {
|
|
|
817
843
|
return left.kind !== "node" || right.kind === "node" && left.id === right.id;
|
|
818
844
|
};
|
|
819
845
|
const same = (left, right) => {
|
|
820
|
-
|
|
846
|
+
require_transform.profile.dependency.comparison();
|
|
821
847
|
if ("schema" in left && "schema" in right && left.schema !== right.schema) return false;
|
|
822
848
|
if (!sameTreeTarget(treeTarget(left), treeTarget(right))) return false;
|
|
823
849
|
if (left.kind !== right.kind) return false;
|
|
@@ -825,7 +851,7 @@ const same = (left, right) => {
|
|
|
825
851
|
const rightAddress = address(right);
|
|
826
852
|
if (leftAddress.length !== rightAddress.length) return false;
|
|
827
853
|
for (let index = 0; index < leftAddress.length; index += 1) {
|
|
828
|
-
|
|
854
|
+
require_transform.profile.dependency.segmentCompared();
|
|
829
855
|
if (leftAddress[index] !== rightAddress[index]) return false;
|
|
830
856
|
}
|
|
831
857
|
return left.kind !== "collection" || id(left) === id(right);
|
|
@@ -938,7 +964,7 @@ const locations = /* @__PURE__ */ new WeakMap();
|
|
|
938
964
|
const locationOf = (value) => locations.get(value);
|
|
939
965
|
const addressOf = (target) => {
|
|
940
966
|
if (target.at) return target.at;
|
|
941
|
-
|
|
967
|
+
require_transform.profile.access("addresses");
|
|
942
968
|
return target.at = addressOf(target.parent).concat(target.key);
|
|
943
969
|
};
|
|
944
970
|
const assertActive = (context) => {
|
|
@@ -960,7 +986,7 @@ const snapshot = (value) => {
|
|
|
960
986
|
if (!location) return value;
|
|
961
987
|
const { context } = location;
|
|
962
988
|
const at = addressOf(location.target);
|
|
963
|
-
|
|
989
|
+
require_transform.profile.access("snapshots");
|
|
964
990
|
collect(context, at);
|
|
965
991
|
const resolved = resolve(context, location.target);
|
|
966
992
|
if (!resolved.node) throw new Error("The selected schema address no longer exists.");
|
|
@@ -975,7 +1001,7 @@ const resolve = (context, target) => {
|
|
|
975
1001
|
assertActive(context);
|
|
976
1002
|
const generation = context.session?.generation ?? 0;
|
|
977
1003
|
if (target.generation !== generation) {
|
|
978
|
-
|
|
1004
|
+
require_transform.profile.access("resolutions");
|
|
979
1005
|
if (!target.parent && !target.at.length) {
|
|
980
1006
|
target.node = context.state.schema;
|
|
981
1007
|
target.value = context.state.document;
|
|
@@ -990,7 +1016,7 @@ const resolve = (context, target) => {
|
|
|
990
1016
|
return target;
|
|
991
1017
|
};
|
|
992
1018
|
const createAccess = (context, initial = []) => {
|
|
993
|
-
|
|
1019
|
+
require_transform.profile.access("scopes");
|
|
994
1020
|
const mutable = () => {
|
|
995
1021
|
assertActive(context);
|
|
996
1022
|
if (!context.session) throw new TypeError("Cannot modify read-only document access.");
|
|
@@ -1001,13 +1027,13 @@ const createAccess = (context, initial = []) => {
|
|
|
1001
1027
|
return target.container = session.bind(addressOf(target), target.node, target.value);
|
|
1002
1028
|
};
|
|
1003
1029
|
const childAt = (target, key) => {
|
|
1004
|
-
|
|
1030
|
+
require_transform.profile.access("addresses");
|
|
1005
1031
|
return addressOf(target).concat(key);
|
|
1006
1032
|
};
|
|
1007
1033
|
const access = (target) => {
|
|
1008
1034
|
if (target.proxy) return target.proxy;
|
|
1009
1035
|
const proxy = new Proxy(target, handler);
|
|
1010
|
-
|
|
1036
|
+
require_transform.profile.access("proxies");
|
|
1011
1037
|
locations.set(proxy, {
|
|
1012
1038
|
context,
|
|
1013
1039
|
target
|
|
@@ -1365,14 +1391,25 @@ const compilePath = (owner, kind, pick) => {
|
|
|
1365
1391
|
//#endregion
|
|
1366
1392
|
//#region core/src/address/relation.ts
|
|
1367
1393
|
const contains = (parent, child) => {
|
|
1368
|
-
|
|
1394
|
+
require_transform.profile.address.prefixComparison();
|
|
1369
1395
|
if (parent.length > child.length) return false;
|
|
1370
1396
|
for (let index = 0; index < parent.length; index += 1) {
|
|
1371
|
-
|
|
1397
|
+
require_transform.profile.address.segmentCompared();
|
|
1372
1398
|
if (parent[index] !== child[index]) return false;
|
|
1373
1399
|
}
|
|
1374
1400
|
return true;
|
|
1375
1401
|
};
|
|
1402
|
+
/** Compare a virtual `[...base, segment]` address with an existing address without allocating it. */
|
|
1403
|
+
const compareExtended = (base, segment, other) => {
|
|
1404
|
+
require_transform.profile.address.prefixComparison();
|
|
1405
|
+
const length = base.length + 1;
|
|
1406
|
+
const common = Math.min(length, other.length);
|
|
1407
|
+
for (let index = 0; index < common; index += 1) {
|
|
1408
|
+
require_transform.profile.address.segmentCompared();
|
|
1409
|
+
if ((index < base.length ? base[index] : segment) !== other[index]) return "disjoint";
|
|
1410
|
+
}
|
|
1411
|
+
return length === other.length ? "equal" : length < other.length ? "ancestor" : "descendant";
|
|
1412
|
+
};
|
|
1376
1413
|
//#endregion
|
|
1377
1414
|
//#region core/src/impact.ts
|
|
1378
1415
|
const createImpact = (schema, changes) => {
|
|
@@ -1381,7 +1418,7 @@ const createImpact = (schema, changes) => {
|
|
|
1381
1418
|
let orders;
|
|
1382
1419
|
const index = () => {
|
|
1383
1420
|
if (!values) {
|
|
1384
|
-
|
|
1421
|
+
require_transform.profile.impact.index();
|
|
1385
1422
|
values = new require_driver.AddressIndex();
|
|
1386
1423
|
orders = new require_driver.AddressIndex();
|
|
1387
1424
|
visitChangedLocations(changes, (at) => values.add(at, true), (at) => orders.add(at, true));
|
|
@@ -1390,7 +1427,7 @@ const createImpact = (schema, changes) => {
|
|
|
1390
1427
|
};
|
|
1391
1428
|
const cache = /* @__PURE__ */ new Map();
|
|
1392
1429
|
const affects = (value) => {
|
|
1393
|
-
|
|
1430
|
+
require_transform.profile.impact.affects();
|
|
1394
1431
|
if (!belongs(value, schema)) return false;
|
|
1395
1432
|
if (reset) return true;
|
|
1396
1433
|
return affected(value, index(), orders);
|
|
@@ -1701,7 +1738,7 @@ const transition = (node, key, beforePresent, before, afterPresent, after) => {
|
|
|
1701
1738
|
if (beforePresent === afterPresent && (!beforePresent || Object.is(before, after))) return;
|
|
1702
1739
|
if (node.kind !== "field" && beforePresent === afterPresent && equalValue(node, before, after)) return;
|
|
1703
1740
|
const value = afterPresent ? node.kind === "field" ? after : copyValue(node, after) : void 0;
|
|
1704
|
-
|
|
1741
|
+
require_transform.profile.recorder("transitions");
|
|
1705
1742
|
return !beforePresent ? {
|
|
1706
1743
|
key,
|
|
1707
1744
|
kind: "added",
|
|
@@ -1756,7 +1793,7 @@ const diffMember = (changes, members, node, at, key, beforePresent, before, afte
|
|
|
1756
1793
|
for (const id of Object.keys(right)) if (!Object.hasOwn(left, id)) diffMember(changes, children, node.value, childAt, id, false, void 0, true, right[id]);
|
|
1757
1794
|
let order;
|
|
1758
1795
|
if (node.kind === "table" && !equal(a.ids, b.ids)) {
|
|
1759
|
-
|
|
1796
|
+
require_transform.profile.recorder("publishedOrderItems", b.ids.length);
|
|
1760
1797
|
order = {
|
|
1761
1798
|
before: a.ids,
|
|
1762
1799
|
after: [...b.ids]
|
|
@@ -1799,7 +1836,7 @@ const reconstructBefore = (node, current, records, offset) => {
|
|
|
1799
1836
|
};
|
|
1800
1837
|
const sealMembers = (fact, changes) => {
|
|
1801
1838
|
const members = [];
|
|
1802
|
-
for (const member of fact.members
|
|
1839
|
+
if (fact.members) for (const member of fact.members.values()) {
|
|
1803
1840
|
if (!member) continue;
|
|
1804
1841
|
const key = memberKey(fact, member.key);
|
|
1805
1842
|
const present = Object.hasOwn(fact.parent, key);
|
|
@@ -1814,7 +1851,7 @@ const sealMembers = (fact, changes) => {
|
|
|
1814
1851
|
const current = fact.node.kind === "list" ? indexedKeys(fact.value, fact.node.keyOf) : fact.value.ids;
|
|
1815
1852
|
if (!equal(fact.order, current)) {
|
|
1816
1853
|
const after = orderOf(fact.node, fact.value);
|
|
1817
|
-
|
|
1854
|
+
require_transform.profile.recorder("publishedOrderItems", after.length);
|
|
1818
1855
|
order = {
|
|
1819
1856
|
before: fact.order,
|
|
1820
1857
|
after
|
|
@@ -1872,7 +1909,7 @@ var ChangeRecorder = class {
|
|
|
1872
1909
|
this.indexedGroups = true;
|
|
1873
1910
|
for (const fact of this.facts) if (fact.kind === "members") {
|
|
1874
1911
|
index.add(fact.at, fact);
|
|
1875
|
-
|
|
1912
|
+
require_transform.profile.recorder("indexedGroups");
|
|
1876
1913
|
}
|
|
1877
1914
|
}
|
|
1878
1915
|
return index;
|
|
@@ -1883,7 +1920,7 @@ var ChangeRecorder = class {
|
|
|
1883
1920
|
absorb(at, node, value, present) {
|
|
1884
1921
|
const index = this.indexGroups();
|
|
1885
1922
|
const children = [];
|
|
1886
|
-
index.
|
|
1923
|
+
index.forEachOverlap(at, (fact) => {
|
|
1887
1924
|
if (fact.kind === "members") {
|
|
1888
1925
|
if (fact.at.length < at.length) {
|
|
1889
1926
|
if (fact.at.length + 1 === at.length) {
|
|
@@ -1902,18 +1939,19 @@ var ChangeRecorder = class {
|
|
|
1902
1939
|
at: fact.at,
|
|
1903
1940
|
group: fact
|
|
1904
1941
|
});
|
|
1905
|
-
for (const member of fact.members
|
|
1942
|
+
if (fact.members) for (const member of fact.members.values()) {
|
|
1906
1943
|
if (!member) continue;
|
|
1907
|
-
const
|
|
1908
|
-
if (
|
|
1944
|
+
const compared = compareExtended(fact.at, member.key, at);
|
|
1945
|
+
if (compared !== "equal" && compared !== "descendant") continue;
|
|
1946
|
+
children.push({
|
|
1909
1947
|
kind: "member",
|
|
1910
|
-
at:
|
|
1948
|
+
at: fact.at.concat(member.key),
|
|
1911
1949
|
group: fact,
|
|
1912
1950
|
member
|
|
1913
1951
|
});
|
|
1914
1952
|
}
|
|
1915
1953
|
} else if (contains(at, fact.at)) children.push(fact);
|
|
1916
|
-
})
|
|
1954
|
+
});
|
|
1917
1955
|
if (present) value = reconstructBefore(node, value, children, at.length);
|
|
1918
1956
|
this.releaseCovered(children);
|
|
1919
1957
|
return value;
|
|
@@ -1921,7 +1959,7 @@ var ChangeRecorder = class {
|
|
|
1921
1959
|
releaseCovered(children) {
|
|
1922
1960
|
let changedGroups;
|
|
1923
1961
|
for (const child of children) {
|
|
1924
|
-
|
|
1962
|
+
require_transform.profile.recorder("absorbed");
|
|
1925
1963
|
if (child.kind === "member") {
|
|
1926
1964
|
const { group, member } = child;
|
|
1927
1965
|
forgetMember(group, member.key);
|
|
@@ -1931,15 +1969,17 @@ var ChangeRecorder = class {
|
|
|
1931
1969
|
(changedGroups ??= /* @__PURE__ */ new Set()).add(child.group);
|
|
1932
1970
|
} else this.unregister(child);
|
|
1933
1971
|
}
|
|
1934
|
-
|
|
1972
|
+
if (changedGroups) {
|
|
1973
|
+
for (const group of changedGroups) if (emptyGroup(group)) this.unregister(group);
|
|
1974
|
+
}
|
|
1935
1975
|
}
|
|
1936
1976
|
register(fact) {
|
|
1937
1977
|
this.facts.add(fact);
|
|
1938
1978
|
if (fact.kind === "members") {
|
|
1939
1979
|
this.groups.set(fact.parent, fact);
|
|
1940
|
-
|
|
1980
|
+
require_transform.profile.recorder("groups");
|
|
1941
1981
|
if (!this.indexedGroups) return;
|
|
1942
|
-
|
|
1982
|
+
require_transform.profile.recorder("indexedGroups");
|
|
1943
1983
|
}
|
|
1944
1984
|
(this.index ??= new require_driver.AddressIndex()).add(fact.at, fact);
|
|
1945
1985
|
}
|
|
@@ -1969,14 +2009,14 @@ var ChangeRecorder = class {
|
|
|
1969
2009
|
if (group.layout === "dynamic") (group.members ??= /* @__PURE__ */ new Map()).set(key, member);
|
|
1970
2010
|
else if (definition.kind === "fixed") group.members[definition.slot] = member;
|
|
1971
2011
|
else throw new Error("A fixed member group requires a fixed schema member.");
|
|
1972
|
-
|
|
2012
|
+
require_transform.profile.recorder("facts");
|
|
1973
2013
|
}
|
|
1974
2014
|
reset() {
|
|
1975
2015
|
if (this.resetBefore) return;
|
|
1976
2016
|
const value = this.absorb([], this.state.schema, this.state.document, true);
|
|
1977
2017
|
this.resetBefore = { value };
|
|
1978
2018
|
this.groups = /* @__PURE__ */ new WeakMap();
|
|
1979
|
-
|
|
2019
|
+
require_transform.profile.recorder("facts");
|
|
1980
2020
|
}
|
|
1981
2021
|
createGroup(container) {
|
|
1982
2022
|
const group = container.layout.kind === "fixed" ? {
|
|
@@ -2001,13 +2041,13 @@ var ChangeRecorder = class {
|
|
|
2001
2041
|
return group;
|
|
2002
2042
|
}
|
|
2003
2043
|
order(container) {
|
|
2004
|
-
|
|
2044
|
+
require_transform.profile.recorder("orderCaptures");
|
|
2005
2045
|
let group = this.groups.get(container.parent);
|
|
2006
2046
|
if (group?.order || this.covered(container.at)) return;
|
|
2007
2047
|
group ??= this.createGroup(container);
|
|
2008
2048
|
group.order = orderOf(container.node, container.value);
|
|
2009
|
-
|
|
2010
|
-
|
|
2049
|
+
require_transform.profile.recorder("orderSnapshots");
|
|
2050
|
+
require_transform.profile.recorder("orderItems", group.order.length);
|
|
2011
2051
|
}
|
|
2012
2052
|
tree(container, id) {
|
|
2013
2053
|
const { at, value: tree } = container;
|
|
@@ -2025,7 +2065,7 @@ var ChangeRecorder = class {
|
|
|
2025
2065
|
this.register(fact);
|
|
2026
2066
|
}
|
|
2027
2067
|
if (id !== void 0 && !fact.nodes.has(id)) {
|
|
2028
|
-
|
|
2068
|
+
require_transform.profile.recorder("treeNodes");
|
|
2029
2069
|
fact.nodes.set(id, Object.hasOwn(tree.nodes, id) ? copyTreeNode(tree.nodes[id]) : void 0);
|
|
2030
2070
|
}
|
|
2031
2071
|
}
|
|
@@ -2048,7 +2088,7 @@ var ChangeRecorder = class {
|
|
|
2048
2088
|
seal() {
|
|
2049
2089
|
if (this.resetBefore) {
|
|
2050
2090
|
if (equalValue(this.state.schema, this.resetBefore.value, this.state.document)) return require_driver.sealChanges([]);
|
|
2051
|
-
|
|
2091
|
+
require_transform.profile.recorder("sealed");
|
|
2052
2092
|
return require_driver.sealChanges([{
|
|
2053
2093
|
kind: "reset",
|
|
2054
2094
|
before: this.resetBefore.value,
|
|
@@ -2059,7 +2099,7 @@ var ChangeRecorder = class {
|
|
|
2059
2099
|
for (const fact of this.facts) if (fact.kind === "members") sealMembers(fact, changes);
|
|
2060
2100
|
else sealTree(fact, this.state, changes);
|
|
2061
2101
|
const result = require_driver.sealChanges(changes);
|
|
2062
|
-
|
|
2102
|
+
require_transform.profile.recorder("sealed", result.changes.length);
|
|
2063
2103
|
return result;
|
|
2064
2104
|
}
|
|
2065
2105
|
};
|
|
@@ -2664,7 +2704,7 @@ const select = (document, selector, equality = Object.is) => {
|
|
|
2664
2704
|
};
|
|
2665
2705
|
//#endregion
|
|
2666
2706
|
//#region core/src/projection/input.ts
|
|
2667
|
-
const valueInput = (initial, equality = Object.is) =>
|
|
2707
|
+
const valueInput = (initial, equality = Object.is) => require_transform.defineSource({
|
|
2668
2708
|
kind: "value-input",
|
|
2669
2709
|
initial,
|
|
2670
2710
|
equality
|
|
@@ -2678,7 +2718,7 @@ const collectionInput = (initial = /* @__PURE__ */ new Map(), equality = Object.
|
|
|
2678
2718
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
2679
2719
|
entries.set(key, value);
|
|
2680
2720
|
}
|
|
2681
|
-
return
|
|
2721
|
+
return require_transform.defineSource({
|
|
2682
2722
|
kind: "collection-input",
|
|
2683
2723
|
initial: entries,
|
|
2684
2724
|
equality
|
|
@@ -2691,13 +2731,13 @@ const input = Object.assign(valueInput, { collection: collectionInput });
|
|
|
2691
2731
|
//#endregion
|
|
2692
2732
|
//#region core/src/projection/observe.ts
|
|
2693
2733
|
function observe(source, selector) {
|
|
2694
|
-
if (isExternalSource(source)) return source.kind === "collection" ?
|
|
2734
|
+
if (isExternalSource(source)) return source.kind === "collection" ? require_transform.defineSource({
|
|
2695
2735
|
kind: "external-collection",
|
|
2696
2736
|
source
|
|
2697
2737
|
}, {
|
|
2698
2738
|
kind: "collection",
|
|
2699
2739
|
equality: Object.is
|
|
2700
|
-
}) :
|
|
2740
|
+
}) : require_transform.defineSource({
|
|
2701
2741
|
kind: "external-value",
|
|
2702
2742
|
source,
|
|
2703
2743
|
equality: Object.is
|
|
@@ -2705,7 +2745,7 @@ function observe(source, selector) {
|
|
|
2705
2745
|
kind: "value",
|
|
2706
2746
|
equality: Object.is
|
|
2707
2747
|
});
|
|
2708
|
-
if ("current" in source && typeof source.current === "function") return
|
|
2748
|
+
if ("current" in source && typeof source.current === "function") return require_transform.defineSource({
|
|
2709
2749
|
kind: "readable",
|
|
2710
2750
|
readable: source,
|
|
2711
2751
|
equality: Object.is
|
|
@@ -2720,7 +2760,7 @@ function observe(source, selector) {
|
|
|
2720
2760
|
schema: state.schema,
|
|
2721
2761
|
address: Object.freeze([])
|
|
2722
2762
|
});
|
|
2723
|
-
return
|
|
2763
|
+
return require_transform.defineSource({
|
|
2724
2764
|
kind: "document",
|
|
2725
2765
|
document,
|
|
2726
2766
|
selector: selected
|
|
@@ -2736,41 +2776,6 @@ const isExternalSource = (source) => {
|
|
|
2736
2776
|
};
|
|
2737
2777
|
//#endregion
|
|
2738
2778
|
//#region core/src/projection/derive/keyed.ts
|
|
2739
|
-
const outputKind = (projection) => {
|
|
2740
|
-
return require_scheduler.outputDefinitionOf(projection).kind;
|
|
2741
|
-
};
|
|
2742
|
-
const assertKeyedProjection = (projection, role) => {
|
|
2743
|
-
if (outputKind(projection) !== "collection") throw new TypeError(`${role} must be a keyed collection projection.`);
|
|
2744
|
-
};
|
|
2745
|
-
const isKeyedDependency = (value) => {
|
|
2746
|
-
if (!value || typeof value !== "object" || require_scheduler.isProjection(value)) return false;
|
|
2747
|
-
const candidate = value;
|
|
2748
|
-
return require_scheduler.isProjection(candidate.source) && typeof candidate.key === "function";
|
|
2749
|
-
};
|
|
2750
|
-
const bindKey = (index, outputKey, sourceKey) => {
|
|
2751
|
-
const previous = index.forward.get(outputKey);
|
|
2752
|
-
if (previous === sourceKey) return;
|
|
2753
|
-
if (previous !== void 0) {
|
|
2754
|
-
const dependents = index.reverse.get(previous);
|
|
2755
|
-
dependents?.delete(outputKey);
|
|
2756
|
-
if (dependents?.size === 0) index.reverse.delete(previous);
|
|
2757
|
-
index.forward.delete(outputKey);
|
|
2758
|
-
}
|
|
2759
|
-
if (sourceKey === void 0) return;
|
|
2760
|
-
index.forward.set(outputKey, sourceKey);
|
|
2761
|
-
const dependents = index.reverse.get(sourceKey);
|
|
2762
|
-
if (dependents) dependents.add(outputKey);
|
|
2763
|
-
else index.reverse.set(sourceKey, new Set([outputKey]));
|
|
2764
|
-
};
|
|
2765
|
-
const unbindKey = (indexes, outputKey) => {
|
|
2766
|
-
for (const index of indexes) if (index) bindKey(index, outputKey, void 0);
|
|
2767
|
-
};
|
|
2768
|
-
const createBindingIndex = () => ({
|
|
2769
|
-
forward: /* @__PURE__ */ new Map(),
|
|
2770
|
-
reverse: /* @__PURE__ */ new Map()
|
|
2771
|
-
});
|
|
2772
|
-
const emptyKeyedDependencies = Object.freeze(Object.create(null));
|
|
2773
|
-
const absent = Symbol("keyed-transform-absent");
|
|
2774
2779
|
const compileOrderedKeys = (value, role) => {
|
|
2775
2780
|
if (!Array.isArray(value)) throw new TypeError(`${role} must be an array of string keys.`);
|
|
2776
2781
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2784,164 +2789,22 @@ const compileOrderedKeys = (value, role) => {
|
|
|
2784
2789
|
requested: seen
|
|
2785
2790
|
};
|
|
2786
2791
|
};
|
|
2787
|
-
const createKeyedTransform = (source, dependencySpecs, select, equality, name) => {
|
|
2788
|
-
assertKeyedProjection(source, "Keyed derive source");
|
|
2789
|
-
const dependencies = [source];
|
|
2790
|
-
const dependencyNames = [];
|
|
2791
|
-
const keyed = [];
|
|
2792
|
-
if (dependencySpecs !== void 0) {
|
|
2793
|
-
if (!require_record.isPlainObject(dependencySpecs) || require_scheduler.isProjection(dependencySpecs)) throw new TypeError("Keyed derive dependencies must be a plain object.");
|
|
2794
|
-
for (const name of Reflect.ownKeys(dependencySpecs)) {
|
|
2795
|
-
if (typeof name !== "string") throw new TypeError("Keyed derive dependency names must be strings.");
|
|
2796
|
-
const descriptor = Object.getOwnPropertyDescriptor(dependencySpecs, name);
|
|
2797
|
-
if (!descriptor?.enumerable || !("value" in descriptor)) throw new TypeError("Keyed derive dependencies must be enumerable data properties.");
|
|
2798
|
-
const dependency = descriptor.value;
|
|
2799
|
-
dependencyNames.push(name);
|
|
2800
|
-
if (require_scheduler.isProjection(dependency)) {
|
|
2801
|
-
dependencies.push(dependency);
|
|
2802
|
-
keyed.push(void 0);
|
|
2803
|
-
continue;
|
|
2804
|
-
}
|
|
2805
|
-
if (!isKeyedDependency(dependency)) throw new TypeError("Keyed derive dependencies must be projections or keyed lookups.");
|
|
2806
|
-
assertKeyedProjection(dependency.source, "Dynamic keyed dependency source");
|
|
2807
|
-
dependencies.push(dependency.source);
|
|
2808
|
-
keyed.push(dependency);
|
|
2809
|
-
}
|
|
2810
|
-
}
|
|
2811
|
-
const [projection] = require_scheduler.defineProcessor({
|
|
2812
|
-
dependencies,
|
|
2813
|
-
outputs: [{
|
|
2814
|
-
kind: "collection",
|
|
2815
|
-
equality: equality ?? Object.is
|
|
2816
|
-
}],
|
|
2817
|
-
create: () => {
|
|
2818
|
-
const bindings = keyed.map((dependency) => dependency ? createBindingIndex() : void 0);
|
|
2819
|
-
const globalValues = keyed.map(() => void 0);
|
|
2820
|
-
const revisions = [];
|
|
2821
|
-
const clearBindings = () => {
|
|
2822
|
-
for (const index of bindings) {
|
|
2823
|
-
index?.forward.clear();
|
|
2824
|
-
index?.reverse.clear();
|
|
2825
|
-
}
|
|
2826
|
-
};
|
|
2827
|
-
const rememberRevisions = (sources) => {
|
|
2828
|
-
revisions.length = sources.length;
|
|
2829
|
-
for (let index = 0; index < sources.length; index++) revisions[index] = sources[index].revision;
|
|
2830
|
-
};
|
|
2831
|
-
return {
|
|
2832
|
-
evaluate: (evaluation) => {
|
|
2833
|
-
const driver = evaluation.sources[0];
|
|
2834
|
-
const output = evaluation.outputs[0];
|
|
2835
|
-
if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("Keyed derive requires collection input and output.");
|
|
2836
|
-
for (let index = 0; index < keyed.length; index++) {
|
|
2837
|
-
if (keyed[index]) continue;
|
|
2838
|
-
const dependencySource = evaluation.sources[index + 1];
|
|
2839
|
-
if (evaluation.reset || revisions[index + 1] !== dependencySource.revision) globalValues[index] = require_scheduler.snapshotDependencyValue(dependencySource);
|
|
2840
|
-
}
|
|
2841
|
-
const project = (key) => {
|
|
2842
|
-
if (!driver.read.has(key)) return;
|
|
2843
|
-
const value = driver.read.get(key);
|
|
2844
|
-
let dependencyValues = emptyKeyedDependencies;
|
|
2845
|
-
if (keyed.length) {
|
|
2846
|
-
const values = Object.create(null);
|
|
2847
|
-
for (let index = 0; index < keyed.length; index++) {
|
|
2848
|
-
const dependency = keyed[index];
|
|
2849
|
-
if (!dependency) {
|
|
2850
|
-
values[dependencyNames[index]] = globalValues[index];
|
|
2851
|
-
continue;
|
|
2852
|
-
}
|
|
2853
|
-
const sourceContext = evaluation.sources[index + 1];
|
|
2854
|
-
if (sourceContext.kind !== "collection") throw new TypeError("Dynamic keyed dependency resolved to a non-collection source.");
|
|
2855
|
-
const selectedKey = dependency.key(value, key);
|
|
2856
|
-
require_scheduler.assertSynchronous(selectedKey);
|
|
2857
|
-
if (selectedKey !== void 0 && typeof selectedKey !== "string") throw new TypeError("Dynamic keyed dependency keys must be strings or undefined.");
|
|
2858
|
-
bindKey(bindings[index], key, selectedKey);
|
|
2859
|
-
values[dependencyNames[index]] = selectedKey === void 0 ? void 0 : sourceContext.read.get(selectedKey);
|
|
2860
|
-
}
|
|
2861
|
-
dependencyValues = Object.freeze(values);
|
|
2862
|
-
}
|
|
2863
|
-
const next = select(value, key, dependencyValues);
|
|
2864
|
-
require_scheduler.assertSynchronous(next);
|
|
2865
|
-
if (next === absent) output.output.remove(key);
|
|
2866
|
-
else output.output.set(key, next);
|
|
2867
|
-
};
|
|
2868
|
-
if (evaluation.reset) {
|
|
2869
|
-
clearBindings();
|
|
2870
|
-
const ids = driver.read.ids();
|
|
2871
|
-
for (const key of ids) project(key);
|
|
2872
|
-
output.output.order(ids.filter((key) => output.next.has(key)));
|
|
2873
|
-
rememberRevisions(evaluation.sources);
|
|
2874
|
-
return;
|
|
2875
|
-
}
|
|
2876
|
-
const dirty = /* @__PURE__ */ new Set();
|
|
2877
|
-
let all = false;
|
|
2878
|
-
let updateOrder = false;
|
|
2879
|
-
let membershipChanged = false;
|
|
2880
|
-
if (revisions[0] !== driver.revision) {
|
|
2881
|
-
const change = driver.change;
|
|
2882
|
-
if (!change || change.kind === "reset") {
|
|
2883
|
-
all = true;
|
|
2884
|
-
updateOrder = true;
|
|
2885
|
-
} else {
|
|
2886
|
-
for (const entry of change.removed) {
|
|
2887
|
-
membershipChanged ||= output.previous.has(entry.key);
|
|
2888
|
-
output.output.remove(entry.key);
|
|
2889
|
-
unbindKey(bindings, entry.key);
|
|
2890
|
-
dirty.delete(entry.key);
|
|
2891
|
-
}
|
|
2892
|
-
for (const entry of change.added) dirty.add(entry.key);
|
|
2893
|
-
for (const entry of change.updated) dirty.add(entry.key);
|
|
2894
|
-
updateOrder = Boolean(change.order);
|
|
2895
|
-
}
|
|
2896
|
-
}
|
|
2897
|
-
for (let index = 0; index < keyed.length; index++) {
|
|
2898
|
-
const sourceContext = evaluation.sources[index + 1];
|
|
2899
|
-
if (revisions[index + 1] === sourceContext.revision) continue;
|
|
2900
|
-
if (!keyed[index]) {
|
|
2901
|
-
all = true;
|
|
2902
|
-
continue;
|
|
2903
|
-
}
|
|
2904
|
-
if (sourceContext.kind !== "collection" || !sourceContext.change) {
|
|
2905
|
-
all = true;
|
|
2906
|
-
continue;
|
|
2907
|
-
}
|
|
2908
|
-
if (sourceContext.change.kind === "reset") {
|
|
2909
|
-
all = true;
|
|
2910
|
-
continue;
|
|
2911
|
-
}
|
|
2912
|
-
const reverse = bindings[index].reverse;
|
|
2913
|
-
for (const sourceKey of require_scheduler.collectionChange.keys(sourceContext.change)) for (const outputKey of reverse.get(sourceKey) ?? []) dirty.add(outputKey);
|
|
2914
|
-
}
|
|
2915
|
-
if (all) for (const key of driver.read.ids()) dirty.add(key);
|
|
2916
|
-
for (const key of dirty) {
|
|
2917
|
-
if (!driver.read.has(key)) continue;
|
|
2918
|
-
const beforePresent = output.previous.has(key);
|
|
2919
|
-
project(key);
|
|
2920
|
-
membershipChanged ||= beforePresent !== output.next.has(key);
|
|
2921
|
-
}
|
|
2922
|
-
if (updateOrder || membershipChanged) output.output.order(driver.read.ids().filter((key) => output.next.has(key)));
|
|
2923
|
-
rememberRevisions(evaluation.sources);
|
|
2924
|
-
},
|
|
2925
|
-
release: () => {
|
|
2926
|
-
clearBindings();
|
|
2927
|
-
revisions.length = 0;
|
|
2928
|
-
}
|
|
2929
|
-
};
|
|
2930
|
-
},
|
|
2931
|
-
name
|
|
2932
|
-
});
|
|
2933
|
-
return projection;
|
|
2934
|
-
};
|
|
2935
2792
|
function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
2936
2793
|
const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
|
|
2937
2794
|
const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
|
|
2938
2795
|
const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
|
|
2939
2796
|
if (typeof select !== "function") throw new TypeError("Keyed derive requires a selector.");
|
|
2940
|
-
return createKeyedTransform(
|
|
2797
|
+
return require_transform.createKeyedTransform({
|
|
2798
|
+
source,
|
|
2799
|
+
dependencies: dependencySpecs,
|
|
2800
|
+
equality,
|
|
2801
|
+
name: "derive.keyed",
|
|
2802
|
+
evaluate: ({ value, key, dependencies }) => dependencySpecs === void 0 ? select(value, key) : select(value, key, dependencies)
|
|
2803
|
+
});
|
|
2941
2804
|
}
|
|
2942
2805
|
const createKeyedKeys = (source) => {
|
|
2943
|
-
assertKeyedProjection(source, "Keyed keys source");
|
|
2944
|
-
const [projection] =
|
|
2806
|
+
require_transform.assertKeyedProjection(source, "Keyed keys source");
|
|
2807
|
+
const [projection] = require_transform.defineProcessor({
|
|
2945
2808
|
dependencies: [source],
|
|
2946
2809
|
outputs: [{
|
|
2947
2810
|
kind: "value",
|
|
@@ -2951,15 +2814,15 @@ const createKeyedKeys = (source) => {
|
|
|
2951
2814
|
const driver = evaluation.sources[0];
|
|
2952
2815
|
const output = evaluation.outputs[0];
|
|
2953
2816
|
if (driver.kind !== "collection" || output.kind !== "value") throw new Error("derive.keyed.keys requires collection input and value output.");
|
|
2954
|
-
if (evaluation.reset || !driver.change ||
|
|
2817
|
+
if (evaluation.reset || !driver.change || require_transform.collectionHasStructuralChange(driver.change)) output.output.set(driver.read.ids());
|
|
2955
2818
|
} }),
|
|
2956
2819
|
name: "derive.keyed.keys"
|
|
2957
2820
|
});
|
|
2958
2821
|
return projection;
|
|
2959
2822
|
};
|
|
2960
|
-
const
|
|
2961
|
-
assertKeyedProjection(source,
|
|
2962
|
-
const [projection] =
|
|
2823
|
+
const createOrderedSnapshot = (source, name, make) => {
|
|
2824
|
+
require_transform.assertKeyedProjection(source, `${name} source`);
|
|
2825
|
+
const [projection] = require_transform.defineProcessor({
|
|
2963
2826
|
dependencies: [source],
|
|
2964
2827
|
outputs: [{
|
|
2965
2828
|
kind: "value",
|
|
@@ -2969,33 +2832,32 @@ const createKeyedValues = (source) => {
|
|
|
2969
2832
|
let indexes = /* @__PURE__ */ new Map();
|
|
2970
2833
|
const rebuild = (driver) => {
|
|
2971
2834
|
const nextIndexes = /* @__PURE__ */ new Map();
|
|
2972
|
-
const
|
|
2973
|
-
const values = Object.freeze(ids.map((key, index) => {
|
|
2835
|
+
const result = Object.freeze(driver.read.ids().map((key, index) => {
|
|
2974
2836
|
nextIndexes.set(key, index);
|
|
2975
|
-
return driver.read.get(key);
|
|
2837
|
+
return make(key, driver.read.get(key));
|
|
2976
2838
|
}));
|
|
2977
2839
|
indexes = nextIndexes;
|
|
2978
|
-
return
|
|
2840
|
+
return result;
|
|
2979
2841
|
};
|
|
2980
2842
|
const rebuildIncremental = (driver, change, previous) => {
|
|
2981
2843
|
const changedValues = /* @__PURE__ */ new Map();
|
|
2982
2844
|
for (const entry of change.added) changedValues.set(entry.key, entry.after);
|
|
2983
2845
|
for (const entry of change.updated) changedValues.set(entry.key, entry.after);
|
|
2984
2846
|
const nextIndexes = /* @__PURE__ */ new Map();
|
|
2985
|
-
const
|
|
2847
|
+
const result = Object.freeze(driver.read.ids().map((key, index) => {
|
|
2986
2848
|
nextIndexes.set(key, index);
|
|
2987
|
-
if (changedValues.has(key)) return changedValues.get(key);
|
|
2849
|
+
if (changedValues.has(key)) return make(key, changedValues.get(key));
|
|
2988
2850
|
const previousIndex = indexes.get(key);
|
|
2989
|
-
return previousIndex === void 0 ? driver.read.get(key) : previous[previousIndex];
|
|
2851
|
+
return previousIndex === void 0 ? make(key, driver.read.get(key)) : previous[previousIndex];
|
|
2990
2852
|
}));
|
|
2991
2853
|
indexes = nextIndexes;
|
|
2992
|
-
return
|
|
2854
|
+
return result;
|
|
2993
2855
|
};
|
|
2994
2856
|
return {
|
|
2995
2857
|
evaluate: (evaluation) => {
|
|
2996
2858
|
const driver = evaluation.sources[0];
|
|
2997
2859
|
const output = evaluation.outputs[0];
|
|
2998
|
-
if (driver.kind !== "collection" || output.kind !== "value") throw new Error(
|
|
2860
|
+
if (driver.kind !== "collection" || output.kind !== "value") throw new Error(`${name} requires collection input and value output.`);
|
|
2999
2861
|
const change = driver.change;
|
|
3000
2862
|
if (evaluation.reset || !change || change.kind === "reset") {
|
|
3001
2863
|
output.output.set(rebuild(driver));
|
|
@@ -3006,7 +2868,7 @@ const createKeyedValues = (source) => {
|
|
|
3006
2868
|
output.output.set(rebuild(driver));
|
|
3007
2869
|
return;
|
|
3008
2870
|
}
|
|
3009
|
-
if (
|
|
2871
|
+
if (require_transform.collectionHasStructuralChange(change)) {
|
|
3010
2872
|
output.output.set(rebuildIncremental(driver, change, previous));
|
|
3011
2873
|
return;
|
|
3012
2874
|
}
|
|
@@ -3018,14 +2880,54 @@ const createKeyedValues = (source) => {
|
|
|
3018
2880
|
output.output.set(rebuild(driver));
|
|
3019
2881
|
return;
|
|
3020
2882
|
}
|
|
3021
|
-
next[index] = entry.after;
|
|
2883
|
+
next[index] = make(entry.key, entry.after);
|
|
3022
2884
|
}
|
|
3023
2885
|
output.output.set(Object.freeze(next));
|
|
3024
2886
|
},
|
|
3025
2887
|
release: () => indexes.clear()
|
|
3026
2888
|
};
|
|
3027
2889
|
},
|
|
3028
|
-
name
|
|
2890
|
+
name
|
|
2891
|
+
});
|
|
2892
|
+
return projection;
|
|
2893
|
+
};
|
|
2894
|
+
const createKeyedValues = (source) => createOrderedSnapshot(source, "derive.keyed.values", (_key, value) => value);
|
|
2895
|
+
const createKeyedEntries = (source) => createOrderedSnapshot(source, "derive.keyed.entries", (key, value) => Object.freeze([key, value]));
|
|
2896
|
+
const createKeyedGet = (source, key, equality = Object.is) => {
|
|
2897
|
+
require_transform.assertKeyedProjection(source, "derive.keyed.get source");
|
|
2898
|
+
if (require_transform.outputKind(key) !== "value") throw new TypeError("derive.keyed.get key must be a scalar projection.");
|
|
2899
|
+
const [projection] = require_transform.defineProcessor({
|
|
2900
|
+
dependencies: [source, key],
|
|
2901
|
+
outputs: [{
|
|
2902
|
+
kind: "value",
|
|
2903
|
+
equality
|
|
2904
|
+
}],
|
|
2905
|
+
create: () => {
|
|
2906
|
+
let sourceRevision = -1;
|
|
2907
|
+
let keyRevision = -1;
|
|
2908
|
+
let selected;
|
|
2909
|
+
return { evaluate: (evaluation) => {
|
|
2910
|
+
const collection = evaluation.sources[0];
|
|
2911
|
+
const selectedKey = evaluation.sources[1];
|
|
2912
|
+
const output = evaluation.outputs[0];
|
|
2913
|
+
if (collection.kind !== "collection" || selectedKey.kind !== "value" || output.kind !== "value") throw new Error("derive.keyed.get resolved invalid input/output kinds.");
|
|
2914
|
+
let affected = evaluation.reset;
|
|
2915
|
+
if (evaluation.reset || keyRevision !== selectedKey.revision) {
|
|
2916
|
+
const candidate = selectedKey.value;
|
|
2917
|
+
if (candidate !== void 0 && typeof candidate !== "string") throw new TypeError("derive.keyed.get key must resolve to a string or undefined.");
|
|
2918
|
+
selected = candidate;
|
|
2919
|
+
affected = true;
|
|
2920
|
+
} else if (sourceRevision !== collection.revision) {
|
|
2921
|
+
const change = collection.change;
|
|
2922
|
+
if (!change || change.kind === "reset") affected = true;
|
|
2923
|
+
else if (selected !== void 0) affected = require_transform.collectionChangeTouchesKey(change, selected);
|
|
2924
|
+
}
|
|
2925
|
+
if (affected) output.output.set(selected === void 0 ? void 0 : collection.read.get(selected));
|
|
2926
|
+
sourceRevision = collection.revision;
|
|
2927
|
+
keyRevision = selectedKey.revision;
|
|
2928
|
+
} };
|
|
2929
|
+
},
|
|
2930
|
+
name: "derive.keyed.get"
|
|
3029
2931
|
});
|
|
3030
2932
|
return projection;
|
|
3031
2933
|
};
|
|
@@ -3033,28 +2935,40 @@ function createKeyedFilter(source, dependenciesOrPredicate, maybePredicate) {
|
|
|
3033
2935
|
const dependencySpecs = typeof dependenciesOrPredicate === "function" ? void 0 : dependenciesOrPredicate;
|
|
3034
2936
|
const predicate = dependencySpecs === void 0 ? dependenciesOrPredicate : maybePredicate;
|
|
3035
2937
|
if (typeof predicate !== "function") throw new TypeError("Keyed filter requires a predicate.");
|
|
3036
|
-
return createKeyedTransform(
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
2938
|
+
return require_transform.createKeyedTransform({
|
|
2939
|
+
source,
|
|
2940
|
+
dependencies: dependencySpecs,
|
|
2941
|
+
equality: Object.is,
|
|
2942
|
+
name: "derive.keyed.filter",
|
|
2943
|
+
evaluate: ({ value, key, dependencies }) => {
|
|
2944
|
+
const included = dependencySpecs === void 0 ? predicate(value, key) : predicate(value, key, dependencies);
|
|
2945
|
+
require_transform.assertSynchronous(included);
|
|
2946
|
+
if (typeof included !== "boolean") throw new TypeError("Keyed filter predicate must return boolean.");
|
|
2947
|
+
return included ? value : require_transform.keyedAbsent;
|
|
2948
|
+
}
|
|
2949
|
+
});
|
|
3042
2950
|
}
|
|
3043
2951
|
function createKeyedCompact(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
3044
2952
|
const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
|
|
3045
2953
|
const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
|
|
3046
2954
|
const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
|
|
3047
2955
|
if (typeof select !== "function") throw new TypeError("Keyed compact requires a selector.");
|
|
3048
|
-
return createKeyedTransform(
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
2956
|
+
return require_transform.createKeyedTransform({
|
|
2957
|
+
source,
|
|
2958
|
+
dependencies: dependencySpecs,
|
|
2959
|
+
equality,
|
|
2960
|
+
name: "derive.keyed.compact",
|
|
2961
|
+
evaluate: ({ value, key, dependencies }) => {
|
|
2962
|
+
const next = dependencySpecs === void 0 ? select(value, key) : select(value, key, dependencies);
|
|
2963
|
+
require_transform.assertSynchronous(next);
|
|
2964
|
+
return next === void 0 ? require_transform.keyedAbsent : next;
|
|
2965
|
+
}
|
|
2966
|
+
});
|
|
3053
2967
|
}
|
|
3054
2968
|
const createKeyedSubset = (source, orderedKeys) => {
|
|
3055
|
-
assertKeyedProjection(source, "Keyed subset source");
|
|
3056
|
-
const keyProjection =
|
|
3057
|
-
if (keyProjection && outputKind(keyProjection) !== "value") throw new TypeError("Keyed subset ordered keys must be a value projection.");
|
|
2969
|
+
require_transform.assertKeyedProjection(source, "Keyed subset source");
|
|
2970
|
+
const keyProjection = require_transform.isProjection(orderedKeys) ? orderedKeys : void 0;
|
|
2971
|
+
if (keyProjection && require_transform.outputKind(keyProjection) !== "value") throw new TypeError("Keyed subset ordered keys must be a value projection.");
|
|
3058
2972
|
const staticSelection = keyProjection ? void 0 : (() => {
|
|
3059
2973
|
const compiled = compileOrderedKeys(orderedKeys, "Keyed subset ordered keys");
|
|
3060
2974
|
return {
|
|
@@ -3062,7 +2976,7 @@ const createKeyedSubset = (source, orderedKeys) => {
|
|
|
3062
2976
|
requested: compiled.requested
|
|
3063
2977
|
};
|
|
3064
2978
|
})();
|
|
3065
|
-
const [projection] =
|
|
2979
|
+
const [projection] = require_transform.defineProcessor({
|
|
3066
2980
|
dependencies: keyProjection ? [source, keyProjection] : [source],
|
|
3067
2981
|
outputs: [{
|
|
3068
2982
|
kind: "collection",
|
|
@@ -3146,9 +3060,199 @@ const createKeyedSubset = (source, orderedKeys) => {
|
|
|
3146
3060
|
});
|
|
3147
3061
|
return projection;
|
|
3148
3062
|
};
|
|
3063
|
+
const normalizeGroups = (value) => {
|
|
3064
|
+
const groups = typeof value === "string" ? [value] : value;
|
|
3065
|
+
if (!Array.isArray(groups)) throw new TypeError("derive.keyed.groupBy selector must return a string or an array of strings.");
|
|
3066
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3067
|
+
const result = [];
|
|
3068
|
+
for (const group of groups) {
|
|
3069
|
+
if (typeof group !== "string") throw new TypeError("derive.keyed.groupBy selector must return only string group keys.");
|
|
3070
|
+
if (seen.has(group)) throw new TypeError("derive.keyed.groupBy selector must not return duplicate group keys.");
|
|
3071
|
+
seen.add(group);
|
|
3072
|
+
result.push(group);
|
|
3073
|
+
}
|
|
3074
|
+
return Object.freeze(result);
|
|
3075
|
+
};
|
|
3076
|
+
function createKeyedGroupBy(source, dependenciesOrSelector, maybeSelector) {
|
|
3077
|
+
require_transform.assertKeyedProjection(source, "derive.keyed.groupBy source");
|
|
3078
|
+
const dependencySpecs = typeof dependenciesOrSelector === "function" ? void 0 : dependenciesOrSelector;
|
|
3079
|
+
const selector = dependencySpecs === void 0 ? dependenciesOrSelector : maybeSelector;
|
|
3080
|
+
if (typeof selector !== "function") throw new TypeError("derive.keyed.groupBy requires a selector.");
|
|
3081
|
+
const compiled = require_transform.compileKeyedDependencies(source, dependencySpecs, "derive.keyed.groupBy");
|
|
3082
|
+
const [projection] = require_transform.defineProcessor({
|
|
3083
|
+
dependencies: compiled.projections,
|
|
3084
|
+
outputs: [{
|
|
3085
|
+
kind: "collection",
|
|
3086
|
+
equality: (previous, next) => {
|
|
3087
|
+
return require_transform.sameArray(previous, next);
|
|
3088
|
+
}
|
|
3089
|
+
}],
|
|
3090
|
+
create: () => {
|
|
3091
|
+
const dependencies = require_transform.createKeyedDependencyRuntime(compiled);
|
|
3092
|
+
const relation = require_transform.createKeyRelation();
|
|
3093
|
+
const orderIndex = /* @__PURE__ */ new Map();
|
|
3094
|
+
const groupRanks = /* @__PURE__ */ new Map();
|
|
3095
|
+
const refreshOrderIndex = (driver) => {
|
|
3096
|
+
orderIndex.clear();
|
|
3097
|
+
driver.read.ids().forEach((key, index) => orderIndex.set(key, index));
|
|
3098
|
+
};
|
|
3099
|
+
const selectGroups = (driver, sources, key, affected) => {
|
|
3100
|
+
if (!driver.read.has(key)) return false;
|
|
3101
|
+
const value = driver.read.get(key);
|
|
3102
|
+
const dependencyValues = dependencies.resolve(value, key, sources);
|
|
3103
|
+
const selected = dependencySpecs === void 0 ? selector(value, key) : selector(value, key, dependencyValues);
|
|
3104
|
+
require_transform.assertSynchronous(selected);
|
|
3105
|
+
const next = normalizeGroups(selected);
|
|
3106
|
+
const previous = relation.forward(key) ?? Object.freeze([]);
|
|
3107
|
+
if (!relation.replace(key, next)) return false;
|
|
3108
|
+
for (const group of previous) affected.add(group);
|
|
3109
|
+
for (const group of next) affected.add(group);
|
|
3110
|
+
return true;
|
|
3111
|
+
};
|
|
3112
|
+
const updateGroup = (group, output) => {
|
|
3113
|
+
const reverse = relation.reverse(group);
|
|
3114
|
+
if (!reverse?.size) {
|
|
3115
|
+
groupRanks.delete(group);
|
|
3116
|
+
output.output.remove(group);
|
|
3117
|
+
return;
|
|
3118
|
+
}
|
|
3119
|
+
const members = [...reverse].sort((left, right) => (orderIndex.get(left) ?? Number.MAX_SAFE_INTEGER) - (orderIndex.get(right) ?? Number.MAX_SAFE_INTEGER));
|
|
3120
|
+
const first = members[0];
|
|
3121
|
+
const source = orderIndex.get(first);
|
|
3122
|
+
const selector = relation.forward(first)?.indexOf(group) ?? -1;
|
|
3123
|
+
if (source === void 0 || selector < 0) throw new Error("derive.keyed.groupBy relation is inconsistent with source order.");
|
|
3124
|
+
groupRanks.set(group, {
|
|
3125
|
+
source,
|
|
3126
|
+
selector
|
|
3127
|
+
});
|
|
3128
|
+
output.output.set(group, Object.freeze(members));
|
|
3129
|
+
};
|
|
3130
|
+
const publishGroupOrder = (output) => {
|
|
3131
|
+
const order = [...groupRanks.keys()];
|
|
3132
|
+
order.sort((leftKey, rightKey) => {
|
|
3133
|
+
const left = groupRanks.get(leftKey);
|
|
3134
|
+
const right = groupRanks.get(rightKey);
|
|
3135
|
+
if (left.source !== right.source) return left.source - right.source;
|
|
3136
|
+
if (left.selector !== right.selector) return left.selector - right.selector;
|
|
3137
|
+
return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
|
|
3138
|
+
});
|
|
3139
|
+
output.output.order(order);
|
|
3140
|
+
};
|
|
3141
|
+
const rebuild = (driver, sources, output) => {
|
|
3142
|
+
dependencies.clearBindings();
|
|
3143
|
+
relation.clear();
|
|
3144
|
+
groupRanks.clear();
|
|
3145
|
+
refreshOrderIndex(driver);
|
|
3146
|
+
const affected = /* @__PURE__ */ new Set();
|
|
3147
|
+
for (const key of driver.read.ids()) selectGroups(driver, sources, key, affected);
|
|
3148
|
+
for (const key of output.previous.ids()) affected.add(key);
|
|
3149
|
+
for (const group of affected) updateGroup(group, output);
|
|
3150
|
+
publishGroupOrder(output);
|
|
3151
|
+
};
|
|
3152
|
+
const publishAffected = (affected, output, orderDirty) => {
|
|
3153
|
+
for (const group of affected) updateGroup(group, output);
|
|
3154
|
+
if (orderDirty) publishGroupOrder(output);
|
|
3155
|
+
};
|
|
3156
|
+
const removeSourceKey = (key, affected) => {
|
|
3157
|
+
const previous = relation.forward(key);
|
|
3158
|
+
if (!previous) return false;
|
|
3159
|
+
for (const group of previous) affected.add(group);
|
|
3160
|
+
return relation.delete(key);
|
|
3161
|
+
};
|
|
3162
|
+
const markAllGroups = (output, affected) => {
|
|
3163
|
+
for (const group of relation.rights()) affected.add(group);
|
|
3164
|
+
for (const group of output.previous.ids()) affected.add(group);
|
|
3165
|
+
};
|
|
3166
|
+
return {
|
|
3167
|
+
evaluate: (evaluation) => {
|
|
3168
|
+
const driver = evaluation.sources[0];
|
|
3169
|
+
const output = evaluation.outputs[0];
|
|
3170
|
+
if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("derive.keyed.groupBy requires collection input and output.");
|
|
3171
|
+
dependencies.prepare(evaluation.sources, evaluation.reset);
|
|
3172
|
+
if (evaluation.reset) {
|
|
3173
|
+
rebuild(driver, evaluation.sources, output);
|
|
3174
|
+
dependencies.remember(evaluation.sources);
|
|
3175
|
+
return;
|
|
3176
|
+
}
|
|
3177
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
3178
|
+
const affected = /* @__PURE__ */ new Set();
|
|
3179
|
+
let all = false;
|
|
3180
|
+
let structural = false;
|
|
3181
|
+
let orderDirty = false;
|
|
3182
|
+
if (dependencies.driverChanged(driver)) {
|
|
3183
|
+
const change = driver.change;
|
|
3184
|
+
if (!change || change.kind === "reset") {
|
|
3185
|
+
rebuild(driver, evaluation.sources, output);
|
|
3186
|
+
dependencies.remember(evaluation.sources);
|
|
3187
|
+
return;
|
|
3188
|
+
} else {
|
|
3189
|
+
for (const entry of change.removed) {
|
|
3190
|
+
orderDirty ||= removeSourceKey(entry.key, affected);
|
|
3191
|
+
dependencies.remove(entry.key);
|
|
3192
|
+
dirty.delete(entry.key);
|
|
3193
|
+
}
|
|
3194
|
+
for (const entry of change.added) dirty.add(entry.key);
|
|
3195
|
+
for (const entry of change.updated) dirty.add(entry.key);
|
|
3196
|
+
structural = Boolean(change.added.length || change.removed.length || change.order);
|
|
3197
|
+
orderDirty ||= structural;
|
|
3198
|
+
if (structural) refreshOrderIndex(driver);
|
|
3199
|
+
}
|
|
3200
|
+
}
|
|
3201
|
+
all ||= dependencies.collectInvalidated(evaluation.sources, dirty);
|
|
3202
|
+
if (all) for (const key of driver.read.ids()) dirty.add(key);
|
|
3203
|
+
for (const key of dirty) orderDirty ||= selectGroups(driver, evaluation.sources, key, affected);
|
|
3204
|
+
if (structural) markAllGroups(output, affected);
|
|
3205
|
+
publishAffected(affected, output, orderDirty);
|
|
3206
|
+
dependencies.remember(evaluation.sources);
|
|
3207
|
+
},
|
|
3208
|
+
release: () => {
|
|
3209
|
+
relation.clear();
|
|
3210
|
+
orderIndex.clear();
|
|
3211
|
+
groupRanks.clear();
|
|
3212
|
+
dependencies.release();
|
|
3213
|
+
}
|
|
3214
|
+
};
|
|
3215
|
+
},
|
|
3216
|
+
name: "derive.keyed.groupBy"
|
|
3217
|
+
});
|
|
3218
|
+
return projection;
|
|
3219
|
+
}
|
|
3220
|
+
const createKeyedSingleton = (source, keyOf, equality = Object.is) => {
|
|
3221
|
+
if (require_transform.outputKind(source) !== "value") throw new TypeError("derive.keyed.singleton source must be a scalar projection.");
|
|
3222
|
+
if (typeof keyOf !== "function") throw new TypeError("derive.keyed.singleton requires a key selector.");
|
|
3223
|
+
const [projection] = require_transform.defineProcessor({
|
|
3224
|
+
dependencies: [source],
|
|
3225
|
+
outputs: [{
|
|
3226
|
+
kind: "collection",
|
|
3227
|
+
equality
|
|
3228
|
+
}],
|
|
3229
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
3230
|
+
const scalar = evaluation.sources[0];
|
|
3231
|
+
const output = evaluation.outputs[0];
|
|
3232
|
+
if (scalar.kind !== "value" || output.kind !== "collection") throw new Error("derive.keyed.singleton resolved invalid input/output kinds.");
|
|
3233
|
+
const previous = output.previous.ids();
|
|
3234
|
+
if (scalar.value === void 0) {
|
|
3235
|
+
for (const key of previous) output.output.remove(key);
|
|
3236
|
+
return;
|
|
3237
|
+
}
|
|
3238
|
+
const key = keyOf(scalar.value);
|
|
3239
|
+
require_transform.assertSynchronous(key);
|
|
3240
|
+
if (typeof key !== "string") throw new TypeError("derive.keyed.singleton key selector must return a string.");
|
|
3241
|
+
for (const previousKey of previous) if (previousKey !== key) output.output.remove(previousKey);
|
|
3242
|
+
output.output.set(key, scalar.value);
|
|
3243
|
+
if (previous.length !== 1 || previous[0] !== key) output.output.order([key]);
|
|
3244
|
+
} }),
|
|
3245
|
+
name: "derive.keyed.singleton"
|
|
3246
|
+
});
|
|
3247
|
+
return projection;
|
|
3248
|
+
};
|
|
3149
3249
|
const keyedDerive = Object.assign(createKeyedDerive, {
|
|
3150
3250
|
keys: createKeyedKeys,
|
|
3151
3251
|
values: createKeyedValues,
|
|
3252
|
+
entries: createKeyedEntries,
|
|
3253
|
+
get: createKeyedGet,
|
|
3254
|
+
groupBy: createKeyedGroupBy,
|
|
3255
|
+
singleton: createKeyedSingleton,
|
|
3152
3256
|
subset: createKeyedSubset,
|
|
3153
3257
|
filter: createKeyedFilter,
|
|
3154
3258
|
compact: createKeyedCompact
|
|
@@ -3156,10 +3260,10 @@ const keyedDerive = Object.assign(createKeyedDerive, {
|
|
|
3156
3260
|
//#endregion
|
|
3157
3261
|
//#region core/src/projection/derive.ts
|
|
3158
3262
|
function createValueDerive(dependencies, compute, equality = Object.is) {
|
|
3159
|
-
const compiled =
|
|
3263
|
+
const compiled = require_transform.compileProjectionDependencies(dependencies, "Derive");
|
|
3160
3264
|
const names = compiled.names;
|
|
3161
3265
|
const ordered = compiled.projections;
|
|
3162
|
-
const [projection] =
|
|
3266
|
+
const [projection] = require_transform.defineProcessor({
|
|
3163
3267
|
dependencies: ordered,
|
|
3164
3268
|
outputs: [{
|
|
3165
3269
|
kind: "value",
|
|
@@ -3169,9 +3273,9 @@ function createValueDerive(dependencies, compute, equality = Object.is) {
|
|
|
3169
3273
|
const output = evaluation.outputs[0];
|
|
3170
3274
|
if (output.kind !== "value") throw new Error("derive requires a value output.");
|
|
3171
3275
|
const values = Object.create(null);
|
|
3172
|
-
for (let index = 0; index < names.length; index++) values[names[index]] =
|
|
3276
|
+
for (let index = 0; index < names.length; index++) values[names[index]] = require_transform.snapshotDependencyValue(evaluation.sources[index]);
|
|
3173
3277
|
const next = compute(Object.freeze(values));
|
|
3174
|
-
|
|
3278
|
+
require_transform.assertSynchronous(next);
|
|
3175
3279
|
output.output.set(next);
|
|
3176
3280
|
} })
|
|
3177
3281
|
});
|
|
@@ -3182,7 +3286,7 @@ const derive = Object.assign(createValueDerive, { keyed: keyedDerive });
|
|
|
3182
3286
|
//#region core/src/projection/collection/index.ts
|
|
3183
3287
|
const height = (node) => node?.height ?? 0;
|
|
3184
3288
|
const node = (key, value, left, right) => {
|
|
3185
|
-
|
|
3289
|
+
require_transform.profile.collectionIndex.node();
|
|
3186
3290
|
return Object.freeze({
|
|
3187
3291
|
key,
|
|
3188
3292
|
value,
|
|
@@ -3243,7 +3347,7 @@ var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
|
3243
3347
|
}
|
|
3244
3348
|
static from(entries) {
|
|
3245
3349
|
const sorted = [...entries].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
3246
|
-
|
|
3350
|
+
require_transform.profile.collectionIndex.build(sorted.length);
|
|
3247
3351
|
return new PersistentKeyedIndex(fromSorted(sorted));
|
|
3248
3352
|
}
|
|
3249
3353
|
get(key) {
|
|
@@ -3300,13 +3404,14 @@ const createPublishedCollection = () => {
|
|
|
3300
3404
|
},
|
|
3301
3405
|
publish: (staged, nextIds, reset) => {
|
|
3302
3406
|
if (reset) {
|
|
3303
|
-
const entries = [];
|
|
3304
|
-
for (const [key, entry] of staged) if (entry.present) entries.push([key, entry.value]);
|
|
3305
|
-
index = PersistentKeyedIndex.from(entries);
|
|
3306
3407
|
values.clear();
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
else
|
|
3408
|
+
for (const [key, entry] of staged) if (entry.present) values.set(key, entry.value);
|
|
3409
|
+
index = PersistentKeyedIndex.from(values);
|
|
3410
|
+
} else for (const [key, entry] of staged) {
|
|
3411
|
+
index = entry.present ? index.set(key, entry.value) : index.remove(key);
|
|
3412
|
+
if (entry.present) values.set(key, entry.value);
|
|
3413
|
+
else values.delete(key);
|
|
3414
|
+
}
|
|
3310
3415
|
ids = nextIds;
|
|
3311
3416
|
},
|
|
3312
3417
|
release: () => {
|
|
@@ -3319,7 +3424,7 @@ const createPublishedCollection = () => {
|
|
|
3319
3424
|
/** Owns staged keyed intent, exact transitions and published collection state. */
|
|
3320
3425
|
const createCollectionOutput = (binding) => {
|
|
3321
3426
|
const published = createPublishedCollection();
|
|
3322
|
-
|
|
3427
|
+
const staged = /* @__PURE__ */ new Map();
|
|
3323
3428
|
let nextIds = published.ids();
|
|
3324
3429
|
let change;
|
|
3325
3430
|
let initialized = false;
|
|
@@ -3335,12 +3440,15 @@ const createCollectionOutput = (binding) => {
|
|
|
3335
3440
|
return entry ? entry.present ? entry.value : void 0 : cleared ? void 0 : published.get(key);
|
|
3336
3441
|
};
|
|
3337
3442
|
const deriveIds = () => {
|
|
3338
|
-
const result =
|
|
3443
|
+
const result = [];
|
|
3444
|
+
if (!cleared) {
|
|
3445
|
+
for (const key of published.ids()) if (hasNext(key)) result.push(key);
|
|
3446
|
+
}
|
|
3339
3447
|
for (const [key, entry] of staged) if (entry.present && (cleared || !published.has(key))) result.push(key);
|
|
3340
3448
|
return result;
|
|
3341
3449
|
};
|
|
3342
3450
|
const begin = (active, initialize) => {
|
|
3343
|
-
staged
|
|
3451
|
+
staged.clear();
|
|
3344
3452
|
nextIds = published.ids();
|
|
3345
3453
|
change = void 0;
|
|
3346
3454
|
explicitOrder = void 0;
|
|
@@ -3348,16 +3456,18 @@ const createCollectionOutput = (binding) => {
|
|
|
3348
3456
|
reset = initialize;
|
|
3349
3457
|
const read = (next) => ({
|
|
3350
3458
|
get: (key) => {
|
|
3351
|
-
|
|
3459
|
+
require_transform.assertScope(active);
|
|
3352
3460
|
return next ? getNext(key) : published.get(key);
|
|
3353
3461
|
},
|
|
3354
3462
|
has: (key) => {
|
|
3355
|
-
|
|
3463
|
+
require_transform.assertScope(active);
|
|
3356
3464
|
return next ? hasNext(key) : published.has(key);
|
|
3357
3465
|
},
|
|
3358
3466
|
ids: () => {
|
|
3359
|
-
|
|
3360
|
-
|
|
3467
|
+
require_transform.assertScope(active);
|
|
3468
|
+
if (!next) return published.ids();
|
|
3469
|
+
if (explicitOrder) return explicitOrder;
|
|
3470
|
+
return Object.freeze(deriveIds());
|
|
3361
3471
|
}
|
|
3362
3472
|
});
|
|
3363
3473
|
return {
|
|
@@ -3365,7 +3475,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3365
3475
|
next: read(true),
|
|
3366
3476
|
output: {
|
|
3367
3477
|
set: (key, value) => {
|
|
3368
|
-
|
|
3478
|
+
require_transform.assertScope(active);
|
|
3369
3479
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
3370
3480
|
staged.set(key, {
|
|
3371
3481
|
present: true,
|
|
@@ -3373,12 +3483,12 @@ const createCollectionOutput = (binding) => {
|
|
|
3373
3483
|
});
|
|
3374
3484
|
},
|
|
3375
3485
|
remove: (key) => {
|
|
3376
|
-
|
|
3486
|
+
require_transform.assertScope(active);
|
|
3377
3487
|
staged.set(key, { present: false });
|
|
3378
3488
|
},
|
|
3379
3489
|
order: (order) => {
|
|
3380
|
-
|
|
3381
|
-
explicitOrder =
|
|
3490
|
+
require_transform.assertScope(active);
|
|
3491
|
+
explicitOrder = require_transform.snapshotArray(order);
|
|
3382
3492
|
}
|
|
3383
3493
|
}
|
|
3384
3494
|
};
|
|
@@ -3387,9 +3497,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3387
3497
|
if (cleared) {
|
|
3388
3498
|
for (const key of published.keys()) if (!staged.has(key)) staged.set(key, { present: false });
|
|
3389
3499
|
}
|
|
3390
|
-
|
|
3391
|
-
const added = /* @__PURE__ */ new Set();
|
|
3392
|
-
const removed = /* @__PURE__ */ new Set();
|
|
3500
|
+
require_transform.profile.projection("touchedKeys", staged.size);
|
|
3393
3501
|
const addedEntries = [];
|
|
3394
3502
|
const updatedEntries = [];
|
|
3395
3503
|
const removedEntries = [];
|
|
@@ -3397,7 +3505,6 @@ const createCollectionOutput = (binding) => {
|
|
|
3397
3505
|
const existed = published.has(key);
|
|
3398
3506
|
if (entry.present) {
|
|
3399
3507
|
if (!existed) {
|
|
3400
|
-
added.add(key);
|
|
3401
3508
|
addedEntries.push({
|
|
3402
3509
|
key,
|
|
3403
3510
|
after: entry.value
|
|
@@ -3413,22 +3520,31 @@ const createCollectionOutput = (binding) => {
|
|
|
3413
3520
|
else if (!nextReset) staged.delete(key);
|
|
3414
3521
|
continue;
|
|
3415
3522
|
}
|
|
3416
|
-
if (existed) {
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
});
|
|
3422
|
-
} else staged.delete(key);
|
|
3523
|
+
if (existed) removedEntries.push({
|
|
3524
|
+
key,
|
|
3525
|
+
before: published.get(key)
|
|
3526
|
+
});
|
|
3527
|
+
else staged.delete(key);
|
|
3423
3528
|
}
|
|
3424
3529
|
const ids = published.ids();
|
|
3425
|
-
if (explicitOrder
|
|
3426
|
-
const
|
|
3427
|
-
|
|
3428
|
-
|
|
3530
|
+
if (explicitOrder) {
|
|
3531
|
+
const expectedLength = published.size() + addedEntries.length - removedEntries.length;
|
|
3532
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3533
|
+
for (const key of explicitOrder) {
|
|
3534
|
+
if (seen.has(key) || !hasNext(key)) throw new TypeError("Projection order must contain every key exactly once.");
|
|
3535
|
+
seen.add(key);
|
|
3536
|
+
}
|
|
3537
|
+
if (seen.size !== expectedLength) throw new TypeError("Projection order must contain every key exactly once.");
|
|
3538
|
+
nextIds = require_transform.sameArray(ids, explicitOrder) ? ids : explicitOrder;
|
|
3539
|
+
} else if (addedEntries.length || removedEntries.length) {
|
|
3540
|
+
const order = new Array(published.size() + addedEntries.length - removedEntries.length);
|
|
3541
|
+
let index = 0;
|
|
3542
|
+
for (const key of ids) if (hasNext(key)) order[index++] = key;
|
|
3543
|
+
for (const entry of addedEntries) order[index++] = entry.key;
|
|
3544
|
+
nextIds = require_transform.sameArray(ids, order) ? ids : Object.freeze(order);
|
|
3429
3545
|
} else nextIds = ids;
|
|
3430
|
-
|
|
3431
|
-
change = nextReset ? Object.freeze({ kind: "reset" }) :
|
|
3546
|
+
require_transform.profile.projection("changedKeys", addedEntries.length + updatedEntries.length + removedEntries.length);
|
|
3547
|
+
change = nextReset ? Object.freeze({ kind: "reset" }) : require_transform.createCollectionChange({
|
|
3432
3548
|
added: addedEntries,
|
|
3433
3549
|
updated: updatedEntries,
|
|
3434
3550
|
removed: removedEntries,
|
|
@@ -3442,15 +3558,15 @@ const createCollectionOutput = (binding) => {
|
|
|
3442
3558
|
kind: "collection",
|
|
3443
3559
|
read: {
|
|
3444
3560
|
get: (key) => {
|
|
3445
|
-
|
|
3561
|
+
require_transform.assertScope(active);
|
|
3446
3562
|
return getNext(key);
|
|
3447
3563
|
},
|
|
3448
3564
|
has: (key) => {
|
|
3449
|
-
|
|
3565
|
+
require_transform.assertScope(active);
|
|
3450
3566
|
return hasNext(key);
|
|
3451
3567
|
},
|
|
3452
3568
|
ids: () => {
|
|
3453
|
-
|
|
3569
|
+
require_transform.assertScope(active);
|
|
3454
3570
|
return nextIds;
|
|
3455
3571
|
}
|
|
3456
3572
|
},
|
|
@@ -3476,7 +3592,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3476
3592
|
initialized = true;
|
|
3477
3593
|
},
|
|
3478
3594
|
emit: (call) => {
|
|
3479
|
-
Array.from(listeners).forEach((listener) => call(listener));
|
|
3595
|
+
Array.from(listeners).forEach((listener) => call(listener, change));
|
|
3480
3596
|
},
|
|
3481
3597
|
hasConsumers: () => consumers.size > 0,
|
|
3482
3598
|
forEachConsumer: (run) => consumers.forEach(run),
|
|
@@ -3532,12 +3648,12 @@ const createValueOutput = (binding) => {
|
|
|
3532
3648
|
return {
|
|
3533
3649
|
previous: initialized ? value : void 0,
|
|
3534
3650
|
next: () => {
|
|
3535
|
-
|
|
3651
|
+
require_transform.assertScope(active);
|
|
3536
3652
|
if (staged !== unset) return staged;
|
|
3537
3653
|
return initialize ? void 0 : initialized ? value : void 0;
|
|
3538
3654
|
},
|
|
3539
3655
|
output: Object.freeze({ set: (candidate) => {
|
|
3540
|
-
|
|
3656
|
+
require_transform.assertScope(active);
|
|
3541
3657
|
staged = candidate;
|
|
3542
3658
|
} })
|
|
3543
3659
|
};
|
|
@@ -3564,7 +3680,7 @@ const createValueOutput = (binding) => {
|
|
|
3564
3680
|
begin,
|
|
3565
3681
|
seal,
|
|
3566
3682
|
context: (active) => {
|
|
3567
|
-
|
|
3683
|
+
require_transform.assertScope(active);
|
|
3568
3684
|
return Object.freeze({
|
|
3569
3685
|
kind: "value",
|
|
3570
3686
|
value: next,
|
|
@@ -3585,8 +3701,8 @@ const createValueOutput = (binding) => {
|
|
|
3585
3701
|
initialized = true;
|
|
3586
3702
|
},
|
|
3587
3703
|
emit: (call) => {
|
|
3588
|
-
|
|
3589
|
-
Array.from(listeners).forEach((listener) => call(listener));
|
|
3704
|
+
require_transform.profile.materialized.notification();
|
|
3705
|
+
Array.from(listeners).forEach((listener) => call(listener, void 0));
|
|
3590
3706
|
},
|
|
3591
3707
|
hasConsumers: () => consumers.size > 0,
|
|
3592
3708
|
forEachConsumer: (run) => consumers.forEach(run),
|
|
@@ -3624,7 +3740,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3624
3740
|
let instance;
|
|
3625
3741
|
let cause;
|
|
3626
3742
|
const check = () => {
|
|
3627
|
-
if (!scheduler.active || processor.disposed) throw new
|
|
3743
|
+
if (!scheduler.active || processor.disposed) throw new require_transform.ProjectionDisposedError();
|
|
3628
3744
|
if (processor.fault) throw processor.fault;
|
|
3629
3745
|
};
|
|
3630
3746
|
const bound = definition.outputs.map((outputDefinition) => {
|
|
@@ -3678,8 +3794,8 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3678
3794
|
instance = void 0;
|
|
3679
3795
|
}
|
|
3680
3796
|
if (!instance) instance = definition.create();
|
|
3681
|
-
|
|
3682
|
-
|
|
3797
|
+
require_transform.profile.materialized[reset ? "rebuilt" : "updated"]();
|
|
3798
|
+
require_transform.assertSynchronous(instance.evaluate({
|
|
3683
3799
|
reset,
|
|
3684
3800
|
cause: runCause,
|
|
3685
3801
|
sources,
|
|
@@ -3726,7 +3842,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3726
3842
|
try {
|
|
3727
3843
|
scheduler.initialize(() => {
|
|
3728
3844
|
const invalid = dependencies.find((output) => output.owner.fault || output.owner.disposed);
|
|
3729
|
-
if (invalid) throw invalid.owner.fault ?? new
|
|
3845
|
+
if (invalid) throw invalid.owner.fault ?? new require_transform.ProjectionDisposedError();
|
|
3730
3846
|
if (processor.evaluate(true, void 0).length) processor.publish();
|
|
3731
3847
|
processor.clear();
|
|
3732
3848
|
});
|
|
@@ -3741,6 +3857,21 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3741
3857
|
return processor;
|
|
3742
3858
|
};
|
|
3743
3859
|
//#endregion
|
|
3860
|
+
//#region core/src/projection/readable/listeners.ts
|
|
3861
|
+
/** Exhaustively notifies an already-stable projection listener snapshot. */
|
|
3862
|
+
const notifyProjectionListenerSnapshot = (listeners, message) => {
|
|
3863
|
+
let failures;
|
|
3864
|
+
for (const listener of listeners) try {
|
|
3865
|
+
listener();
|
|
3866
|
+
} catch (error) {
|
|
3867
|
+
(failures ??= []).push(error);
|
|
3868
|
+
}
|
|
3869
|
+
if (failures?.length === 1) throw failures[0];
|
|
3870
|
+
if (failures?.length) throw new AggregateError(failures, message);
|
|
3871
|
+
};
|
|
3872
|
+
/** Snapshots a mutable listener collection before exhaustive notification. */
|
|
3873
|
+
const notifyProjectionListeners = (listeners, message) => notifyProjectionListenerSnapshot([...listeners], message);
|
|
3874
|
+
//#endregion
|
|
3744
3875
|
//#region core/src/projection/readable/selection.ts
|
|
3745
3876
|
let activeTracker;
|
|
3746
3877
|
const trackKey = (key) => {
|
|
@@ -3793,9 +3924,8 @@ const trackedMap = (source) => {
|
|
|
3793
3924
|
const selectionAffects = (selection, change) => {
|
|
3794
3925
|
if (!change || change.kind === "reset") return true;
|
|
3795
3926
|
if (selection.all) return true;
|
|
3796
|
-
if (selection.structure &&
|
|
3797
|
-
|
|
3798
|
-
return false;
|
|
3927
|
+
if (selection.structure && require_transform.collectionHasStructuralChange(change)) return true;
|
|
3928
|
+
return require_transform.collectionChangeIntersects(change, selection.keys);
|
|
3799
3929
|
};
|
|
3800
3930
|
const createDirectReadable = (source) => Object.freeze({
|
|
3801
3931
|
current: source.current,
|
|
@@ -3837,7 +3967,7 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
3837
3967
|
activeTracker = previousTracker;
|
|
3838
3968
|
}
|
|
3839
3969
|
const nextSelection = Object.freeze({
|
|
3840
|
-
keys:
|
|
3970
|
+
keys: tracker.keys,
|
|
3841
3971
|
all: tracker.all || isMapLike(current) && tracker.keys.size === 0 && !tracker.structure,
|
|
3842
3972
|
structure: tracker.structure
|
|
3843
3973
|
});
|
|
@@ -3862,7 +3992,7 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
3862
3992
|
if (!change && previousCollection) {
|
|
3863
3993
|
const current = source.current();
|
|
3864
3994
|
if (isMapLike(current)) {
|
|
3865
|
-
change =
|
|
3995
|
+
change = require_transform.diffCollection(previousCollection, current);
|
|
3866
3996
|
previousCollection = current;
|
|
3867
3997
|
if (!change) {
|
|
3868
3998
|
sourceRevision = source.revision();
|
|
@@ -3875,7 +4005,7 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
3875
4005
|
updateCollectionSnapshot();
|
|
3876
4006
|
return;
|
|
3877
4007
|
}
|
|
3878
|
-
if (evaluate())
|
|
4008
|
+
if (evaluate()) notifyProjectionListeners(listeners, "Projection selector listeners failed.");
|
|
3879
4009
|
};
|
|
3880
4010
|
const installSource = () => {
|
|
3881
4011
|
if (!unsubscribeSource) unsubscribeSource = source.subscribe(onProjectionChange);
|
|
@@ -3906,7 +4036,7 @@ const hasDocumentDirty = (dirty) => dirty.replace || dirty.order || dirty.treeRo
|
|
|
3906
4036
|
const equivalent = (node, left, right) => node.kind === "field" ? Object.is(left, right) : equalValue(node, left, right);
|
|
3907
4037
|
const replaceValue = (node, previous, current) => equivalent(node, previous, current) ? previous : copyValue(node, current);
|
|
3908
4038
|
const cloneRecord = (value) => {
|
|
3909
|
-
|
|
4039
|
+
require_transform.profile.copy.shallowRecord(Object.getOwnPropertyNames(value).length);
|
|
3910
4040
|
return Object.create(Object.getPrototypeOf(value), Object.getOwnPropertyDescriptors(value));
|
|
3911
4041
|
};
|
|
3912
4042
|
const materializeRecordChild = (node, previous, current, key, dirty) => {
|
|
@@ -4079,10 +4209,11 @@ const clearDocumentDirty = (dirty) => {
|
|
|
4079
4209
|
dirty.treeNodes.clear();
|
|
4080
4210
|
dirty.children.clear();
|
|
4081
4211
|
};
|
|
4082
|
-
const
|
|
4212
|
+
const locateRange = (dirty, path, start, tail) => {
|
|
4083
4213
|
let current = dirty;
|
|
4084
|
-
for (
|
|
4214
|
+
for (let index = start; index < path.length; index++) {
|
|
4085
4215
|
if (current.replace) return void 0;
|
|
4216
|
+
const key = path[index];
|
|
4086
4217
|
let child = current.children.get(key);
|
|
4087
4218
|
if (!child) {
|
|
4088
4219
|
child = createDocumentDirty();
|
|
@@ -4090,10 +4221,17 @@ const locate = (dirty, path) => {
|
|
|
4090
4221
|
}
|
|
4091
4222
|
current = child;
|
|
4092
4223
|
}
|
|
4093
|
-
return current;
|
|
4224
|
+
if (tail === void 0) return current;
|
|
4225
|
+
if (current.replace) return void 0;
|
|
4226
|
+
let child = current.children.get(tail);
|
|
4227
|
+
if (!child) {
|
|
4228
|
+
child = createDocumentDirty();
|
|
4229
|
+
current.children.set(tail, child);
|
|
4230
|
+
}
|
|
4231
|
+
return child;
|
|
4094
4232
|
};
|
|
4095
|
-
const
|
|
4096
|
-
const current =
|
|
4233
|
+
const replaceAt = (dirty, path, start, tail) => {
|
|
4234
|
+
const current = locateRange(dirty, path, start, tail);
|
|
4097
4235
|
if (!current) return;
|
|
4098
4236
|
current.replace = true;
|
|
4099
4237
|
current.order = false;
|
|
@@ -4101,15 +4239,18 @@ const markDocumentReplace = (dirty, path) => {
|
|
|
4101
4239
|
current.treeNodes.clear();
|
|
4102
4240
|
current.children.clear();
|
|
4103
4241
|
};
|
|
4104
|
-
const
|
|
4105
|
-
|
|
4242
|
+
const markDocumentReplace = (dirty, path) => {
|
|
4243
|
+
replaceAt(dirty, path, 0);
|
|
4244
|
+
};
|
|
4245
|
+
const markDocumentOrderRange = (dirty, path, start) => {
|
|
4246
|
+
const current = locateRange(dirty, path, start);
|
|
4106
4247
|
if (current && !current.replace) current.order = true;
|
|
4107
4248
|
};
|
|
4108
|
-
const markDocumentTree = (dirty, path, root, nodes) => {
|
|
4109
|
-
const current =
|
|
4249
|
+
const markDocumentTree = (dirty, path, start, root, nodes) => {
|
|
4250
|
+
const current = locateRange(dirty, path, start);
|
|
4110
4251
|
if (!current || current.replace) return;
|
|
4111
4252
|
current.treeRoot ||= root;
|
|
4112
|
-
for (const
|
|
4253
|
+
for (const node of nodes) current.treeNodes.add(node.id);
|
|
4113
4254
|
};
|
|
4114
4255
|
const createCollectionPending = () => ({
|
|
4115
4256
|
reset: false,
|
|
@@ -4139,19 +4280,19 @@ const mergeValueChanges = (selector, dirty, changes) => {
|
|
|
4139
4280
|
const at = selector.address;
|
|
4140
4281
|
for (const change of changes.changes) {
|
|
4141
4282
|
if (change.kind === "reset") {
|
|
4142
|
-
|
|
4283
|
+
replaceAt(dirty, at, at.length);
|
|
4143
4284
|
return;
|
|
4144
4285
|
}
|
|
4145
4286
|
if (change.kind === "members") {
|
|
4146
4287
|
for (const member of change.members) {
|
|
4147
|
-
const
|
|
4148
|
-
if (
|
|
4149
|
-
else if (
|
|
4288
|
+
const compared = compareExtended(change.at, member.key, at);
|
|
4289
|
+
if (compared === "ancestor" || compared === "equal") replaceAt(dirty, at, at.length);
|
|
4290
|
+
else if (compared === "descendant") replaceAt(dirty, change.at, at.length, member.key);
|
|
4150
4291
|
}
|
|
4151
|
-
if (change.order && contains(at, change.at))
|
|
4292
|
+
if (change.order && contains(at, change.at)) markDocumentOrderRange(dirty, change.at, at.length);
|
|
4152
4293
|
continue;
|
|
4153
4294
|
}
|
|
4154
|
-
if (contains(at, change.at)) markDocumentTree(dirty, change.at
|
|
4295
|
+
if (contains(at, change.at)) markDocumentTree(dirty, change.at, at.length, change.before !== change.after, change.nodes);
|
|
4155
4296
|
}
|
|
4156
4297
|
};
|
|
4157
4298
|
const mergeCollectionChanges = (selector, pending, changes) => {
|
|
@@ -4163,13 +4304,15 @@ const mergeCollectionChanges = (selector, pending, changes) => {
|
|
|
4163
4304
|
return;
|
|
4164
4305
|
}
|
|
4165
4306
|
if (selector.tree?.kind === "nodes") {
|
|
4166
|
-
if (change.kind === "members") {
|
|
4167
|
-
|
|
4307
|
+
if (change.kind === "members") for (const member of change.members) {
|
|
4308
|
+
const compared = compareExtended(change.at, member.key, at);
|
|
4309
|
+
if (compared === "ancestor" || compared === "equal") {
|
|
4168
4310
|
resetCollectionPending(pending);
|
|
4169
4311
|
return;
|
|
4170
4312
|
}
|
|
4171
|
-
}
|
|
4172
|
-
|
|
4313
|
+
}
|
|
4314
|
+
else if (change.at.length === at.length && contains(change.at, at)) for (const node of change.nodes) {
|
|
4315
|
+
replaceAt(dirtyEntry(pending, node.id), at, at.length);
|
|
4173
4316
|
pending.order ||= node.kind !== "updated";
|
|
4174
4317
|
}
|
|
4175
4318
|
else if (contains(change.at, at)) {
|
|
@@ -4180,39 +4323,32 @@ const mergeCollectionChanges = (selector, pending, changes) => {
|
|
|
4180
4323
|
}
|
|
4181
4324
|
if (change.kind === "members") {
|
|
4182
4325
|
for (const member of change.members) {
|
|
4183
|
-
const
|
|
4184
|
-
if (
|
|
4185
|
-
resetCollectionPending(pending);
|
|
4186
|
-
return;
|
|
4187
|
-
}
|
|
4188
|
-
if (!contains(at, changed)) continue;
|
|
4189
|
-
const relative = changed.slice(at.length);
|
|
4190
|
-
if (!relative.length) {
|
|
4326
|
+
const compared = compareExtended(change.at, member.key, at);
|
|
4327
|
+
if (compared === "ancestor" || compared === "equal") {
|
|
4191
4328
|
resetCollectionPending(pending);
|
|
4192
4329
|
return;
|
|
4193
4330
|
}
|
|
4194
|
-
|
|
4195
|
-
if (
|
|
4196
|
-
|
|
4197
|
-
|
|
4198
|
-
|
|
4199
|
-
if (!relative.length) pending.order = true;
|
|
4200
|
-
else markDocumentOrder(dirtyEntry(pending, relative[0]), relative.slice(1));
|
|
4331
|
+
if (compared !== "descendant") continue;
|
|
4332
|
+
if (at.length === change.at.length) {
|
|
4333
|
+
replaceAt(dirtyEntry(pending, member.key), change.at, change.at.length);
|
|
4334
|
+
if (member.kind !== "updated") pending.order = true;
|
|
4335
|
+
} else replaceAt(dirtyEntry(pending, change.at[at.length]), change.at, at.length + 1, member.key);
|
|
4201
4336
|
}
|
|
4337
|
+
if (change.order && contains(at, change.at)) if (change.at.length === at.length) pending.order = true;
|
|
4338
|
+
else markDocumentOrderRange(dirtyEntry(pending, change.at[at.length]), change.at, at.length + 1);
|
|
4202
4339
|
continue;
|
|
4203
4340
|
}
|
|
4204
4341
|
if (!contains(at, change.at)) continue;
|
|
4205
|
-
|
|
4206
|
-
if (!relative.length) {
|
|
4342
|
+
if (change.at.length === at.length) {
|
|
4207
4343
|
resetCollectionPending(pending);
|
|
4208
4344
|
return;
|
|
4209
4345
|
}
|
|
4210
|
-
markDocumentTree(dirtyEntry(pending,
|
|
4346
|
+
markDocumentTree(dirtyEntry(pending, change.at[at.length]), change.at, at.length + 1, change.before !== change.after, change.nodes);
|
|
4211
4347
|
}
|
|
4212
4348
|
};
|
|
4213
4349
|
//#endregion
|
|
4214
4350
|
//#region core/src/projection/source/boundary.ts
|
|
4215
|
-
const sourceError = (cause) => new
|
|
4351
|
+
const sourceError = (cause) => new require_transform.ProjectionError("source", cause);
|
|
4216
4352
|
const createValueBoundary = (scheduler, name, initial, equality, prepare, clearSource) => {
|
|
4217
4353
|
let boundary;
|
|
4218
4354
|
let pendingReset = false;
|
|
@@ -4221,7 +4357,7 @@ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearS
|
|
|
4221
4357
|
let recovering = false;
|
|
4222
4358
|
let cleanup;
|
|
4223
4359
|
const check = () => {
|
|
4224
|
-
if (!scheduler.active || boundary.disposed) throw new
|
|
4360
|
+
if (!scheduler.active || boundary.disposed) throw new require_transform.ProjectionDisposedError();
|
|
4225
4361
|
if (boundary.fault) throw boundary.fault;
|
|
4226
4362
|
};
|
|
4227
4363
|
const state = createValueOutput({
|
|
@@ -4328,24 +4464,37 @@ const stageCollectionRead = (state, read, active, options) => {
|
|
|
4328
4464
|
const evaluation = state.begin(active, options.reset);
|
|
4329
4465
|
if (options.reset) {
|
|
4330
4466
|
const ids = read.ids();
|
|
4331
|
-
|
|
4467
|
+
require_transform.profile.collectionView.idsScanned(ids.length);
|
|
4332
4468
|
for (const key of ids) {
|
|
4333
|
-
|
|
4469
|
+
require_transform.profile.collectionView.mapped();
|
|
4334
4470
|
evaluation.output.set(key, read.get(key));
|
|
4335
4471
|
}
|
|
4336
4472
|
evaluation.output.order(ids);
|
|
4337
4473
|
} else {
|
|
4338
4474
|
const previous = state.current();
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
for (const key of
|
|
4342
|
-
|
|
4475
|
+
let currentIds;
|
|
4476
|
+
const ids = () => currentIds ??= read.ids();
|
|
4477
|
+
if (options.candidates) for (const key of options.candidates) if (read.has(key)) {
|
|
4478
|
+
require_transform.profile.collectionView.mapped();
|
|
4343
4479
|
evaluation.output.set(key, read.get(key));
|
|
4344
4480
|
} else evaluation.output.remove(key);
|
|
4481
|
+
else {
|
|
4482
|
+
const previousIds = previous.ids();
|
|
4483
|
+
const nextIds = ids();
|
|
4484
|
+
require_transform.profile.collectionView.idsScanned(previousIds.length + nextIds.length);
|
|
4485
|
+
for (const key of previousIds) if (read.has(key)) {
|
|
4486
|
+
require_transform.profile.collectionView.mapped();
|
|
4487
|
+
evaluation.output.set(key, read.get(key));
|
|
4488
|
+
} else evaluation.output.remove(key);
|
|
4489
|
+
for (const key of nextIds) if (!previous.has(key)) {
|
|
4490
|
+
require_transform.profile.collectionView.mapped();
|
|
4491
|
+
evaluation.output.set(key, read.get(key));
|
|
4492
|
+
}
|
|
4493
|
+
}
|
|
4345
4494
|
if (options.orderMayChange || !options.candidates) {
|
|
4346
|
-
const
|
|
4347
|
-
if (options.candidates)
|
|
4348
|
-
evaluation.output.order(
|
|
4495
|
+
const nextIds = ids();
|
|
4496
|
+
if (options.candidates) require_transform.profile.collectionView.idsScanned(nextIds.length);
|
|
4497
|
+
evaluation.output.order(nextIds);
|
|
4349
4498
|
}
|
|
4350
4499
|
}
|
|
4351
4500
|
return state.seal(options.reset, options.isEqual ?? Object.is);
|
|
@@ -4361,7 +4510,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
|
|
|
4361
4510
|
const candidates = /* @__PURE__ */ new Set();
|
|
4362
4511
|
let cleanup;
|
|
4363
4512
|
const check = () => {
|
|
4364
|
-
if (!scheduler.active || boundary.disposed) throw new
|
|
4513
|
+
if (!scheduler.active || boundary.disposed) throw new require_transform.ProjectionDisposedError();
|
|
4365
4514
|
if (boundary.fault) throw boundary.fault;
|
|
4366
4515
|
};
|
|
4367
4516
|
const state = createCollectionOutput({
|
|
@@ -4639,16 +4788,18 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
4639
4788
|
const snapshots = /* @__PURE__ */ new Map();
|
|
4640
4789
|
const replacement = createDocumentDirty();
|
|
4641
4790
|
if (pending.reset) markDocumentReplace(replacement, []);
|
|
4791
|
+
let cachedIds;
|
|
4642
4792
|
const ids = () => {
|
|
4643
|
-
|
|
4644
|
-
if (
|
|
4645
|
-
if (node?.kind === "
|
|
4646
|
-
if (node?.kind === "
|
|
4647
|
-
if (node?.kind === "
|
|
4648
|
-
return Object.freeze(
|
|
4793
|
+
require_transform.assertScope(active);
|
|
4794
|
+
if (cachedIds) return cachedIds;
|
|
4795
|
+
if (treeNodes) return cachedIds = node?.kind === "tree" && require_record.isRecord(value) && require_record.isRecord(value.nodes) ? Object.freeze(Object.keys(value.nodes)) : Object.freeze([]);
|
|
4796
|
+
if (node?.kind === "map" && require_record.isRecord(value)) return cachedIds = Object.freeze(Object.keys(value));
|
|
4797
|
+
if (node?.kind === "table" && require_record.isRecord(value) && Array.isArray(value.ids)) return cachedIds = Object.freeze([...value.ids]);
|
|
4798
|
+
if (node?.kind === "list" && Array.isArray(value)) return cachedIds = Object.freeze(toArray(indexedKeys(value, node.keyOf)));
|
|
4799
|
+
return cachedIds = Object.freeze([]);
|
|
4649
4800
|
};
|
|
4650
4801
|
const has = (key) => {
|
|
4651
|
-
|
|
4802
|
+
require_transform.assertScope(active);
|
|
4652
4803
|
if (treeNodes) return Boolean(node?.kind === "tree" && require_record.isRecord(value) && require_record.isRecord(value.nodes) && Object.hasOwn(value.nodes, key));
|
|
4653
4804
|
if (node?.kind === "map" && require_record.isRecord(value)) return Object.hasOwn(value, key);
|
|
4654
4805
|
if (node?.kind === "table" && require_record.isRecord(value) && require_record.isRecord(value.byId)) return Object.hasOwn(value.byId, key);
|
|
@@ -4669,7 +4820,7 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
4669
4820
|
};
|
|
4670
4821
|
return Object.freeze({
|
|
4671
4822
|
get: (key) => {
|
|
4672
|
-
|
|
4823
|
+
require_transform.assertScope(active);
|
|
4673
4824
|
if (!snapshots.has(key)) if (!has(key)) snapshots.set(key, void 0);
|
|
4674
4825
|
else {
|
|
4675
4826
|
const current = raw(key);
|
|
@@ -5016,7 +5167,7 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5016
5167
|
}
|
|
5017
5168
|
if (source.kind !== "collection-input") throw new Error("Invalid projection input source.");
|
|
5018
5169
|
const values = new Map(source.initial);
|
|
5019
|
-
const boundary = createCollectionBoundary(scheduler, "projection collection input", () =>
|
|
5170
|
+
const boundary = createCollectionBoundary(scheduler, "projection collection input", () => require_transform.mapRead(values), definition.output.equality, () => void 0);
|
|
5020
5171
|
return {
|
|
5021
5172
|
...boundary,
|
|
5022
5173
|
write: {
|
|
@@ -5029,17 +5180,17 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5029
5180
|
let active = true;
|
|
5030
5181
|
const draft = Object.freeze({
|
|
5031
5182
|
get: (key) => {
|
|
5032
|
-
|
|
5183
|
+
require_transform.assertScope(() => active);
|
|
5033
5184
|
const entry = staged.get(key);
|
|
5034
5185
|
return entry ? entry.present ? entry.value : void 0 : values.get(key);
|
|
5035
5186
|
},
|
|
5036
5187
|
has: (key) => {
|
|
5037
|
-
|
|
5188
|
+
require_transform.assertScope(() => active);
|
|
5038
5189
|
const entry = staged.get(key);
|
|
5039
5190
|
return entry ? entry.present : values.has(key);
|
|
5040
5191
|
},
|
|
5041
5192
|
set: (key, value) => {
|
|
5042
|
-
|
|
5193
|
+
require_transform.assertScope(() => active);
|
|
5043
5194
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
5044
5195
|
const previous = staged.get(key);
|
|
5045
5196
|
structural ||= previous ? !previous.present : !values.has(key);
|
|
@@ -5054,7 +5205,7 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5054
5205
|
});
|
|
5055
5206
|
},
|
|
5056
5207
|
remove: (key) => {
|
|
5057
|
-
|
|
5208
|
+
require_transform.assertScope(() => active);
|
|
5058
5209
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
5059
5210
|
const previous = staged.get(key);
|
|
5060
5211
|
const present = previous ? previous.present : values.has(key);
|
|
@@ -5067,7 +5218,7 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5067
5218
|
}
|
|
5068
5219
|
});
|
|
5069
5220
|
try {
|
|
5070
|
-
|
|
5221
|
+
require_transform.assertSynchronous(run(draft));
|
|
5071
5222
|
} finally {
|
|
5072
5223
|
active = false;
|
|
5073
5224
|
}
|
|
@@ -5132,19 +5283,249 @@ const createSourceRegistry = (scheduler) => {
|
|
|
5132
5283
|
};
|
|
5133
5284
|
};
|
|
5134
5285
|
//#endregion
|
|
5286
|
+
//#region core/src/projection/readable/keyed.ts
|
|
5287
|
+
/** Runtime-owned keyed consumer family over one materialized collection output. */
|
|
5288
|
+
const createProjectionItems = (source, checkOwner) => {
|
|
5289
|
+
if (source.kind !== "collection") throw new TypeError("Projection items require a keyed collection projection.");
|
|
5290
|
+
let disposed = false;
|
|
5291
|
+
const active = /* @__PURE__ */ new Map();
|
|
5292
|
+
const followers = /* @__PURE__ */ new Map();
|
|
5293
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
5294
|
+
let keysValue = Object.freeze([...source.current().keys()]);
|
|
5295
|
+
let keysRevision = 0;
|
|
5296
|
+
const keyListeners = /* @__PURE__ */ new Set();
|
|
5297
|
+
const check = () => {
|
|
5298
|
+
if (disposed) throw new require_transform.ProjectionDisposedError();
|
|
5299
|
+
checkOwner();
|
|
5300
|
+
};
|
|
5301
|
+
const notifications = [];
|
|
5302
|
+
const notify = (listeners) => {
|
|
5303
|
+
for (const listener of listeners) notifications.push(listener);
|
|
5304
|
+
};
|
|
5305
|
+
const flushNotifications = () => {
|
|
5306
|
+
try {
|
|
5307
|
+
notifyProjectionListenerSnapshot(notifications, "Projection item listeners failed.");
|
|
5308
|
+
} finally {
|
|
5309
|
+
notifications.length = 0;
|
|
5310
|
+
}
|
|
5311
|
+
};
|
|
5312
|
+
const unregisterWaiting = (state) => {
|
|
5313
|
+
const states = waiting.get(state.key);
|
|
5314
|
+
states?.delete(state);
|
|
5315
|
+
if (states?.size === 0) waiting.delete(state.key);
|
|
5316
|
+
};
|
|
5317
|
+
const registerWaiting = (state) => {
|
|
5318
|
+
if (state.tracked || state.ended) return;
|
|
5319
|
+
const states = waiting.get(state.key);
|
|
5320
|
+
if (states) states.add(state);
|
|
5321
|
+
else waiting.set(state.key, new Set([state]));
|
|
5322
|
+
};
|
|
5323
|
+
const publishState = (state, value) => {
|
|
5324
|
+
if (state.ended) return;
|
|
5325
|
+
if (Object.is(state.value, value)) {
|
|
5326
|
+
state.value = value;
|
|
5327
|
+
return;
|
|
5328
|
+
}
|
|
5329
|
+
state.value = value;
|
|
5330
|
+
state.revision++;
|
|
5331
|
+
notify(state.listeners);
|
|
5332
|
+
};
|
|
5333
|
+
const attachActive = (state, value) => {
|
|
5334
|
+
if (state.ended) return;
|
|
5335
|
+
unregisterWaiting(state);
|
|
5336
|
+
state.tracked = true;
|
|
5337
|
+
const canonical = active.get(state.key);
|
|
5338
|
+
if (!canonical) active.set(state.key, state);
|
|
5339
|
+
else if (canonical !== state) {
|
|
5340
|
+
const states = followers.get(state.key);
|
|
5341
|
+
if (states) states.add(state);
|
|
5342
|
+
else followers.set(state.key, new Set([state]));
|
|
5343
|
+
}
|
|
5344
|
+
publishState(state, value);
|
|
5345
|
+
};
|
|
5346
|
+
const terminateState = (state) => {
|
|
5347
|
+
if (state.ended) return;
|
|
5348
|
+
unregisterWaiting(state);
|
|
5349
|
+
state.tracked = false;
|
|
5350
|
+
publishState(state, void 0);
|
|
5351
|
+
state.ended = true;
|
|
5352
|
+
};
|
|
5353
|
+
const terminateKey = (key) => {
|
|
5354
|
+
const canonical = active.get(key);
|
|
5355
|
+
if (canonical) terminateState(canonical);
|
|
5356
|
+
const extra = followers.get(key);
|
|
5357
|
+
if (extra) for (const state of extra) terminateState(state);
|
|
5358
|
+
active.delete(key);
|
|
5359
|
+
followers.delete(key);
|
|
5360
|
+
};
|
|
5361
|
+
const activateWaiting = (key, value) => {
|
|
5362
|
+
const states = waiting.get(key);
|
|
5363
|
+
if (!states?.size) return;
|
|
5364
|
+
waiting.delete(key);
|
|
5365
|
+
for (const state of states) attachActive(state, value);
|
|
5366
|
+
};
|
|
5367
|
+
const updateKey = (key, value) => {
|
|
5368
|
+
const canonical = active.get(key);
|
|
5369
|
+
if (canonical) publishState(canonical, value);
|
|
5370
|
+
const extra = followers.get(key);
|
|
5371
|
+
if (extra) for (const state of extra) publishState(state, value);
|
|
5372
|
+
};
|
|
5373
|
+
const updateKeys = (current) => {
|
|
5374
|
+
if (require_transform.iterableMatchesArray(current.keys(), keysValue)) return;
|
|
5375
|
+
keysValue = Object.freeze([...current.keys()]);
|
|
5376
|
+
keysRevision++;
|
|
5377
|
+
notify(keyListeners);
|
|
5378
|
+
};
|
|
5379
|
+
const createItem = (key, initial) => {
|
|
5380
|
+
const state = {
|
|
5381
|
+
key,
|
|
5382
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
5383
|
+
tracked: initial !== void 0,
|
|
5384
|
+
ended: false,
|
|
5385
|
+
value: initial?.value,
|
|
5386
|
+
revision: 0
|
|
5387
|
+
};
|
|
5388
|
+
const ensureObserved = () => {
|
|
5389
|
+
if (state.ended || state.tracked) return;
|
|
5390
|
+
const current = source.current();
|
|
5391
|
+
if (current.has(key)) attachActive(state, current.get(key));
|
|
5392
|
+
};
|
|
5393
|
+
state.readable = Object.freeze({
|
|
5394
|
+
current: () => {
|
|
5395
|
+
check();
|
|
5396
|
+
ensureObserved();
|
|
5397
|
+
return state.value;
|
|
5398
|
+
},
|
|
5399
|
+
revision: () => {
|
|
5400
|
+
check();
|
|
5401
|
+
ensureObserved();
|
|
5402
|
+
return state.revision;
|
|
5403
|
+
},
|
|
5404
|
+
subscribe: (listener) => {
|
|
5405
|
+
check();
|
|
5406
|
+
ensureObserved();
|
|
5407
|
+
state.listeners.add(listener);
|
|
5408
|
+
if (!state.tracked && !state.ended) registerWaiting(state);
|
|
5409
|
+
return () => {
|
|
5410
|
+
state.listeners.delete(listener);
|
|
5411
|
+
if (!state.listeners.size && !state.tracked) unregisterWaiting(state);
|
|
5412
|
+
};
|
|
5413
|
+
}
|
|
5414
|
+
});
|
|
5415
|
+
return state;
|
|
5416
|
+
};
|
|
5417
|
+
const reconcile = (current) => {
|
|
5418
|
+
for (const key of active.keys()) if (!current.has(key)) terminateKey(key);
|
|
5419
|
+
else updateKey(key, current.get(key));
|
|
5420
|
+
for (const key of waiting.keys()) if (current.has(key)) activateWaiting(key, current.get(key));
|
|
5421
|
+
updateKeys(current);
|
|
5422
|
+
};
|
|
5423
|
+
const onChange = (change) => {
|
|
5424
|
+
check();
|
|
5425
|
+
const current = source.current();
|
|
5426
|
+
if (!change || change.kind === "reset") {
|
|
5427
|
+
reconcile(current);
|
|
5428
|
+
flushNotifications();
|
|
5429
|
+
return;
|
|
5430
|
+
}
|
|
5431
|
+
for (const entry of change.removed) terminateKey(entry.key);
|
|
5432
|
+
for (const entry of change.added) activateWaiting(entry.key, entry.after);
|
|
5433
|
+
for (const entry of change.updated) updateKey(entry.key, entry.after);
|
|
5434
|
+
if (change.added.length || change.removed.length || change.order) updateKeys(current);
|
|
5435
|
+
flushNotifications();
|
|
5436
|
+
};
|
|
5437
|
+
const stop = source.subscribe(onChange);
|
|
5438
|
+
const keys = Object.freeze({
|
|
5439
|
+
current: () => {
|
|
5440
|
+
check();
|
|
5441
|
+
return keysValue;
|
|
5442
|
+
},
|
|
5443
|
+
revision: () => {
|
|
5444
|
+
check();
|
|
5445
|
+
return keysRevision;
|
|
5446
|
+
},
|
|
5447
|
+
subscribe: (listener) => {
|
|
5448
|
+
check();
|
|
5449
|
+
keyListeners.add(listener);
|
|
5450
|
+
return () => keyListeners.delete(listener);
|
|
5451
|
+
}
|
|
5452
|
+
});
|
|
5453
|
+
return {
|
|
5454
|
+
items: Object.freeze({
|
|
5455
|
+
keys,
|
|
5456
|
+
get: (key) => {
|
|
5457
|
+
check();
|
|
5458
|
+
const canonical = active.get(key);
|
|
5459
|
+
if (canonical) return canonical.readable;
|
|
5460
|
+
const pending = waiting.get(key)?.values().next().value;
|
|
5461
|
+
if (pending) return pending.readable;
|
|
5462
|
+
const current = source.current();
|
|
5463
|
+
if (current.has(key)) {
|
|
5464
|
+
const state = createItem(key, {
|
|
5465
|
+
present: true,
|
|
5466
|
+
value: current.get(key)
|
|
5467
|
+
});
|
|
5468
|
+
active.set(key, state);
|
|
5469
|
+
return state.readable;
|
|
5470
|
+
}
|
|
5471
|
+
return createItem(key).readable;
|
|
5472
|
+
}
|
|
5473
|
+
}),
|
|
5474
|
+
dispose() {
|
|
5475
|
+
if (disposed) return;
|
|
5476
|
+
disposed = true;
|
|
5477
|
+
stop();
|
|
5478
|
+
keyListeners.clear();
|
|
5479
|
+
const known = /* @__PURE__ */ new Set();
|
|
5480
|
+
active.forEach((state) => known.add(state));
|
|
5481
|
+
followers.forEach((states) => states.forEach((state) => known.add(state)));
|
|
5482
|
+
waiting.forEach((states) => states.forEach((state) => known.add(state)));
|
|
5483
|
+
known.forEach((state) => state.listeners.clear());
|
|
5484
|
+
active.clear();
|
|
5485
|
+
followers.clear();
|
|
5486
|
+
waiting.clear();
|
|
5487
|
+
notifications.length = 0;
|
|
5488
|
+
}
|
|
5489
|
+
};
|
|
5490
|
+
};
|
|
5491
|
+
//#endregion
|
|
5135
5492
|
//#region core/src/projection/runtime.ts
|
|
5136
5493
|
const createProjectionRuntime = (options) => {
|
|
5137
|
-
const scheduler =
|
|
5494
|
+
const scheduler = require_transform.createScheduler(options?.onError ?? (() => void 0));
|
|
5138
5495
|
const sources = createSourceRegistry(scheduler);
|
|
5139
5496
|
const materialized = /* @__PURE__ */ new WeakMap();
|
|
5140
5497
|
const publicViews = /* @__PURE__ */ new WeakMap();
|
|
5141
5498
|
const scopes = /* @__PURE__ */ new Set();
|
|
5499
|
+
const rootItems = /* @__PURE__ */ new Map();
|
|
5500
|
+
const disposeItemControllers = (registry, failures) => {
|
|
5501
|
+
const controllers = [...registry.values()];
|
|
5502
|
+
registry.clear();
|
|
5503
|
+
for (const controller of controllers) try {
|
|
5504
|
+
controller.dispose();
|
|
5505
|
+
} catch (error) {
|
|
5506
|
+
failures.push(error);
|
|
5507
|
+
}
|
|
5508
|
+
};
|
|
5509
|
+
const disposeScopeConsumers = (state, failures) => {
|
|
5510
|
+
disposeItemControllers(state.items, failures);
|
|
5511
|
+
const subscriptions = [...state.subscriptions];
|
|
5512
|
+
state.subscriptions.clear();
|
|
5513
|
+
for (const unsubscribe of subscriptions) try {
|
|
5514
|
+
unsubscribe();
|
|
5515
|
+
} catch (error) {
|
|
5516
|
+
failures.push(error);
|
|
5517
|
+
}
|
|
5518
|
+
};
|
|
5519
|
+
const throwCleanupFailures = (failures, message) => {
|
|
5520
|
+
if (failures.length === 1) throw failures[0];
|
|
5521
|
+
if (failures.length) throw new AggregateError(failures, message);
|
|
5522
|
+
};
|
|
5142
5523
|
const assertAccess = (definition, requester) => {
|
|
5143
5524
|
scheduler.assertActive();
|
|
5144
5525
|
const owner = definition.owner;
|
|
5145
5526
|
if (owner === void 0) return;
|
|
5146
5527
|
if (owner !== requester) throw new TypeError("Projection belongs to another scope.");
|
|
5147
|
-
if (!requester?.active) throw new
|
|
5528
|
+
if (!requester?.active) throw new require_transform.ProjectionDisposedError();
|
|
5148
5529
|
};
|
|
5149
5530
|
const rememberScoped = (definition, requester) => {
|
|
5150
5531
|
if (requester && definition.owner === requester) requester.materialized.push(definition);
|
|
@@ -5162,7 +5543,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5162
5543
|
...source.write ? { write: source.write } : {}
|
|
5163
5544
|
});
|
|
5164
5545
|
} else {
|
|
5165
|
-
if (definition.owner === void 0 && definition.dependencies.some((dependency) =>
|
|
5546
|
+
if (definition.owner === void 0 && definition.dependencies.some((dependency) => require_transform.producerOf(dependency).owner !== void 0)) throw new TypeError("A root projection cannot depend on a scoped projection.");
|
|
5166
5547
|
const dependencies = definition.dependencies.map((dependency) => resolveOutput(dependency, requester));
|
|
5167
5548
|
const processor = createProcessor(scheduler, definition, Object.freeze(dependencies));
|
|
5168
5549
|
result = Object.freeze({
|
|
@@ -5176,14 +5557,14 @@ const createProjectionRuntime = (options) => {
|
|
|
5176
5557
|
return result;
|
|
5177
5558
|
};
|
|
5178
5559
|
const resolveOutput = (projection, requester) => {
|
|
5179
|
-
const ref =
|
|
5560
|
+
const ref = require_transform.projectionRef(projection);
|
|
5180
5561
|
const output = materializeProducer(ref.producer, requester).outputs[ref.output];
|
|
5181
5562
|
if (!output) throw new TypeError("Projection output does not exist.");
|
|
5182
5563
|
return output;
|
|
5183
5564
|
};
|
|
5184
5565
|
const assertOutput = (output) => {
|
|
5185
5566
|
scheduler.assertActive();
|
|
5186
|
-
if (output.owner.disposed) throw new
|
|
5567
|
+
if (output.owner.disposed) throw new require_transform.ProjectionDisposedError();
|
|
5187
5568
|
if (output.owner.fault) throw output.owner.fault;
|
|
5188
5569
|
};
|
|
5189
5570
|
const publicCurrent = (output) => {
|
|
@@ -5193,7 +5574,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5193
5574
|
const cached = publicViews.get(output);
|
|
5194
5575
|
if (cached?.revision === revision) return cached.value;
|
|
5195
5576
|
if (output.kind === "collection") {
|
|
5196
|
-
const value =
|
|
5577
|
+
const value = require_transform.collectionView(current);
|
|
5197
5578
|
publicViews.set(output, {
|
|
5198
5579
|
revision,
|
|
5199
5580
|
value
|
|
@@ -5201,7 +5582,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5201
5582
|
return value;
|
|
5202
5583
|
}
|
|
5203
5584
|
if (isMapLike(current)) {
|
|
5204
|
-
const value =
|
|
5585
|
+
const value = require_transform.snapshotCollectionView(require_transform.mapRead(current));
|
|
5205
5586
|
publicViews.set(output, {
|
|
5206
5587
|
revision,
|
|
5207
5588
|
value
|
|
@@ -5224,6 +5605,19 @@ const createProjectionRuntime = (options) => {
|
|
|
5224
5605
|
subscribe: output.subscribe
|
|
5225
5606
|
});
|
|
5226
5607
|
};
|
|
5608
|
+
const projectionItems = (projection, requester) => {
|
|
5609
|
+
const output = resolveOutput(projection, requester);
|
|
5610
|
+
if (output.kind !== "collection") throw new TypeError("Projection items require a keyed collection projection.");
|
|
5611
|
+
const registry = requester?.items ?? rootItems;
|
|
5612
|
+
const existing = registry.get(output);
|
|
5613
|
+
if (existing) return existing.items;
|
|
5614
|
+
const controller = createProjectionItems(readableSource(projection, requester), () => {
|
|
5615
|
+
if (requester && !requester.active) throw new require_transform.ProjectionDisposedError();
|
|
5616
|
+
assertOutput(output);
|
|
5617
|
+
});
|
|
5618
|
+
registry.set(output, controller);
|
|
5619
|
+
return controller.items;
|
|
5620
|
+
};
|
|
5227
5621
|
const makeReadable = (projection, selector, equality, requester) => {
|
|
5228
5622
|
const source = readableSource(projection, requester);
|
|
5229
5623
|
return selector ? createSelectorReadable(source, selector, equality) : createDirectReadable(source);
|
|
@@ -5232,7 +5626,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5232
5626
|
return makeReadable(projection, selector, equality);
|
|
5233
5627
|
}
|
|
5234
5628
|
const inputWrite = (target, requester) => {
|
|
5235
|
-
const ref =
|
|
5629
|
+
const ref = require_transform.projectionRef(target);
|
|
5236
5630
|
if (ref.output !== 0 || ref.producer.kind !== "source") throw new TypeError("Projection is not an input.");
|
|
5237
5631
|
if (ref.producer.source.kind !== "value-input" && ref.producer.source.kind !== "collection-input") throw new TypeError("Projection is not an input.");
|
|
5238
5632
|
const write = materializeProducer(ref.producer, requester).write;
|
|
@@ -5257,13 +5651,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5257
5651
|
scheduler.assertIdle();
|
|
5258
5652
|
state.active = false;
|
|
5259
5653
|
const failures = [];
|
|
5260
|
-
|
|
5261
|
-
state.subscriptions.clear();
|
|
5262
|
-
for (const unsubscribe of subscriptions) try {
|
|
5263
|
-
unsubscribe();
|
|
5264
|
-
} catch (error) {
|
|
5265
|
-
failures.push(error);
|
|
5266
|
-
}
|
|
5654
|
+
disposeScopeConsumers(state, failures);
|
|
5267
5655
|
for (let index = state.materialized.length - 1; index >= 0; index--) {
|
|
5268
5656
|
const definition = state.materialized[index];
|
|
5269
5657
|
const current = materialized.get(definition);
|
|
@@ -5278,25 +5666,26 @@ const createProjectionRuntime = (options) => {
|
|
|
5278
5666
|
}
|
|
5279
5667
|
state.materialized.length = 0;
|
|
5280
5668
|
scopes.delete(state);
|
|
5281
|
-
|
|
5669
|
+
throwCleanupFailures(failures, "Projection scope cleanup failed.");
|
|
5282
5670
|
};
|
|
5283
5671
|
const createScope = () => {
|
|
5284
5672
|
scheduler.assertIdle();
|
|
5285
5673
|
const state = {
|
|
5286
5674
|
active: true,
|
|
5287
5675
|
materialized: [],
|
|
5288
|
-
subscriptions: /* @__PURE__ */ new Set()
|
|
5676
|
+
subscriptions: /* @__PURE__ */ new Set(),
|
|
5677
|
+
items: /* @__PURE__ */ new Map()
|
|
5289
5678
|
};
|
|
5290
5679
|
scopes.add(state);
|
|
5291
5680
|
const assertActive = () => {
|
|
5292
|
-
if (!state.active) throw new
|
|
5681
|
+
if (!state.active) throw new require_transform.ProjectionDisposedError();
|
|
5293
5682
|
scheduler.assertActive();
|
|
5294
5683
|
};
|
|
5295
5684
|
function own(target) {
|
|
5296
5685
|
assertActive();
|
|
5297
5686
|
const projections = [];
|
|
5298
5687
|
const collect = (value) => {
|
|
5299
|
-
if (
|
|
5688
|
+
if (require_transform.isProjection(value)) {
|
|
5300
5689
|
projections.push(value);
|
|
5301
5690
|
return;
|
|
5302
5691
|
}
|
|
@@ -5306,13 +5695,13 @@ const createProjectionRuntime = (options) => {
|
|
|
5306
5695
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
5307
5696
|
if (!descriptor?.enumerable || !("value" in descriptor)) throw new TypeError("Projection ownership tree must use enumerable data properties.");
|
|
5308
5697
|
const child = descriptor.value;
|
|
5309
|
-
if (!
|
|
5698
|
+
if (!require_transform.isProjection(child) && !require_record.isPlainObject(child)) throw new TypeError("Projection ownership tree leaves must be projections.");
|
|
5310
5699
|
collect(child);
|
|
5311
5700
|
}
|
|
5312
5701
|
};
|
|
5313
5702
|
collect(target);
|
|
5314
5703
|
if (!projections.length) throw new TypeError("Projection ownership tree must contain a projection.");
|
|
5315
|
-
|
|
5704
|
+
require_transform.ownProjections(projections, state);
|
|
5316
5705
|
return target;
|
|
5317
5706
|
}
|
|
5318
5707
|
function scopeSelect(projection, selector, equality) {
|
|
@@ -5357,6 +5746,10 @@ const createProjectionRuntime = (options) => {
|
|
|
5357
5746
|
return readProjection(projection, state);
|
|
5358
5747
|
},
|
|
5359
5748
|
select: scopeSelect,
|
|
5749
|
+
items: (projection) => {
|
|
5750
|
+
assertActive();
|
|
5751
|
+
return projectionItems(projection, state);
|
|
5752
|
+
},
|
|
5360
5753
|
update: scopeUpdate,
|
|
5361
5754
|
batch: scopeBatch,
|
|
5362
5755
|
dispose: () => disposeScope(state)
|
|
@@ -5365,6 +5758,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5365
5758
|
return Object.freeze({
|
|
5366
5759
|
read: readProjection,
|
|
5367
5760
|
select: selectProjection,
|
|
5761
|
+
items: projectionItems,
|
|
5368
5762
|
update,
|
|
5369
5763
|
batch,
|
|
5370
5764
|
scope: createScope,
|
|
@@ -5374,16 +5768,11 @@ const createProjectionRuntime = (options) => {
|
|
|
5374
5768
|
const failures = [];
|
|
5375
5769
|
for (const scope of scopes) {
|
|
5376
5770
|
scope.active = false;
|
|
5377
|
-
|
|
5378
|
-
scope.subscriptions.clear();
|
|
5379
|
-
for (const unsubscribe of subscriptions) try {
|
|
5380
|
-
unsubscribe();
|
|
5381
|
-
} catch (error) {
|
|
5382
|
-
failures.push(error);
|
|
5383
|
-
}
|
|
5771
|
+
disposeScopeConsumers(scope, failures);
|
|
5384
5772
|
scope.materialized.length = 0;
|
|
5385
5773
|
}
|
|
5386
5774
|
scopes.clear();
|
|
5775
|
+
disposeItemControllers(rootItems, failures);
|
|
5387
5776
|
try {
|
|
5388
5777
|
sources.dispose();
|
|
5389
5778
|
} catch (error) {
|
|
@@ -5394,8 +5783,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5394
5783
|
} catch (error) {
|
|
5395
5784
|
failures.push(error);
|
|
5396
5785
|
}
|
|
5397
|
-
|
|
5398
|
-
if (failures.length) throw new AggregateError(failures, "Projection cleanup failed.");
|
|
5786
|
+
throwCleanupFailures(failures, "Projection cleanup failed.");
|
|
5399
5787
|
}
|
|
5400
5788
|
});
|
|
5401
5789
|
};
|
|
@@ -5403,8 +5791,8 @@ const createProjectionRuntime = (options) => {
|
|
|
5403
5791
|
exports.DocumentDisposedError = DocumentDisposedError;
|
|
5404
5792
|
exports.DocumentReentrancyError = DocumentReentrancyError;
|
|
5405
5793
|
exports.ParseError = ParseError;
|
|
5406
|
-
exports.ProjectionDisposedError =
|
|
5407
|
-
exports.ProjectionError =
|
|
5794
|
+
exports.ProjectionDisposedError = require_transform.ProjectionDisposedError;
|
|
5795
|
+
exports.ProjectionError = require_transform.ProjectionError;
|
|
5408
5796
|
exports.TransactionRejected = TransactionRejected;
|
|
5409
5797
|
exports.createDocument = createDocument;
|
|
5410
5798
|
exports.createProjectionRuntime = createProjectionRuntime;
|