doxum 0.1.26 → 0.1.28
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 +86 -3
- package/dist/advanced.cjs +40 -30
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +34 -2
- package/dist/advanced.d.ts +34 -2
- package/dist/advanced.js +31 -22
- 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.map +1 -1
- package/dist/driver-BL91gn7_.cjs.map +1 -1
- package/dist/index.cjs +885 -394
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +33 -18
- package/dist/index.d.ts +33 -18
- package/dist/index.js +784 -293
- package/dist/index.js.map +1 -1
- package/dist/local-sync.cjs.map +1 -1
- package/dist/local-sync.js.map +1 -1
- package/dist/transform-B4T3AcJr.js +938 -0
- package/dist/transform-B4T3AcJr.js.map +1 -0
- package/dist/transform-DUleAsG0.cjs +1105 -0
- package/dist/transform-DUleAsG0.cjs.map +1 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +16 -6
- package/skills/doxum-runtime/references/api.en.md +49 -15
- package/skills/doxum-runtime/references/api.zh-CN.md +47 -13
- 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 +77 -4
- package/skills/doxum-runtime/references/projections.zh-CN.md +74 -3
- package/dist/scheduler--137L3j-.js +0 -500
- package/dist/scheduler--137L3j-.js.map +0 -1
- package/dist/scheduler-D4VthaaK.cjs +0 -589
- package/dist/scheduler-D4VthaaK.cjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const
|
|
2
|
+
const require_transform = require("./transform-DUleAsG0.cjs");
|
|
3
3
|
const require_record = require("./record-ChYUi8PP.cjs");
|
|
4
4
|
const require_driver = require("./driver-BL91gn7_.cjs");
|
|
5
5
|
//#region core/src/schema/model.ts
|
|
@@ -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 {
|
|
@@ -817,7 +817,7 @@ const sameTreeTarget = (left, right) => {
|
|
|
817
817
|
return left.kind !== "node" || right.kind === "node" && left.id === right.id;
|
|
818
818
|
};
|
|
819
819
|
const same = (left, right) => {
|
|
820
|
-
|
|
820
|
+
require_transform.profile.dependency.comparison();
|
|
821
821
|
if ("schema" in left && "schema" in right && left.schema !== right.schema) return false;
|
|
822
822
|
if (!sameTreeTarget(treeTarget(left), treeTarget(right))) return false;
|
|
823
823
|
if (left.kind !== right.kind) return false;
|
|
@@ -825,7 +825,7 @@ const same = (left, right) => {
|
|
|
825
825
|
const rightAddress = address(right);
|
|
826
826
|
if (leftAddress.length !== rightAddress.length) return false;
|
|
827
827
|
for (let index = 0; index < leftAddress.length; index += 1) {
|
|
828
|
-
|
|
828
|
+
require_transform.profile.dependency.segmentCompared();
|
|
829
829
|
if (leftAddress[index] !== rightAddress[index]) return false;
|
|
830
830
|
}
|
|
831
831
|
return left.kind !== "collection" || id(left) === id(right);
|
|
@@ -938,7 +938,7 @@ const locations = /* @__PURE__ */ new WeakMap();
|
|
|
938
938
|
const locationOf = (value) => locations.get(value);
|
|
939
939
|
const addressOf = (target) => {
|
|
940
940
|
if (target.at) return target.at;
|
|
941
|
-
|
|
941
|
+
require_transform.profile.access("addresses");
|
|
942
942
|
return target.at = addressOf(target.parent).concat(target.key);
|
|
943
943
|
};
|
|
944
944
|
const assertActive = (context) => {
|
|
@@ -960,7 +960,7 @@ const snapshot = (value) => {
|
|
|
960
960
|
if (!location) return value;
|
|
961
961
|
const { context } = location;
|
|
962
962
|
const at = addressOf(location.target);
|
|
963
|
-
|
|
963
|
+
require_transform.profile.access("snapshots");
|
|
964
964
|
collect(context, at);
|
|
965
965
|
const resolved = resolve(context, location.target);
|
|
966
966
|
if (!resolved.node) throw new Error("The selected schema address no longer exists.");
|
|
@@ -975,7 +975,7 @@ const resolve = (context, target) => {
|
|
|
975
975
|
assertActive(context);
|
|
976
976
|
const generation = context.session?.generation ?? 0;
|
|
977
977
|
if (target.generation !== generation) {
|
|
978
|
-
|
|
978
|
+
require_transform.profile.access("resolutions");
|
|
979
979
|
if (!target.parent && !target.at.length) {
|
|
980
980
|
target.node = context.state.schema;
|
|
981
981
|
target.value = context.state.document;
|
|
@@ -990,7 +990,7 @@ const resolve = (context, target) => {
|
|
|
990
990
|
return target;
|
|
991
991
|
};
|
|
992
992
|
const createAccess = (context, initial = []) => {
|
|
993
|
-
|
|
993
|
+
require_transform.profile.access("scopes");
|
|
994
994
|
const mutable = () => {
|
|
995
995
|
assertActive(context);
|
|
996
996
|
if (!context.session) throw new TypeError("Cannot modify read-only document access.");
|
|
@@ -1001,13 +1001,13 @@ const createAccess = (context, initial = []) => {
|
|
|
1001
1001
|
return target.container = session.bind(addressOf(target), target.node, target.value);
|
|
1002
1002
|
};
|
|
1003
1003
|
const childAt = (target, key) => {
|
|
1004
|
-
|
|
1004
|
+
require_transform.profile.access("addresses");
|
|
1005
1005
|
return addressOf(target).concat(key);
|
|
1006
1006
|
};
|
|
1007
1007
|
const access = (target) => {
|
|
1008
1008
|
if (target.proxy) return target.proxy;
|
|
1009
1009
|
const proxy = new Proxy(target, handler);
|
|
1010
|
-
|
|
1010
|
+
require_transform.profile.access("proxies");
|
|
1011
1011
|
locations.set(proxy, {
|
|
1012
1012
|
context,
|
|
1013
1013
|
target
|
|
@@ -1365,10 +1365,10 @@ const compilePath = (owner, kind, pick) => {
|
|
|
1365
1365
|
//#endregion
|
|
1366
1366
|
//#region core/src/address/relation.ts
|
|
1367
1367
|
const contains = (parent, child) => {
|
|
1368
|
-
|
|
1368
|
+
require_transform.profile.address.prefixComparison();
|
|
1369
1369
|
if (parent.length > child.length) return false;
|
|
1370
1370
|
for (let index = 0; index < parent.length; index += 1) {
|
|
1371
|
-
|
|
1371
|
+
require_transform.profile.address.segmentCompared();
|
|
1372
1372
|
if (parent[index] !== child[index]) return false;
|
|
1373
1373
|
}
|
|
1374
1374
|
return true;
|
|
@@ -1381,7 +1381,7 @@ const createImpact = (schema, changes) => {
|
|
|
1381
1381
|
let orders;
|
|
1382
1382
|
const index = () => {
|
|
1383
1383
|
if (!values) {
|
|
1384
|
-
|
|
1384
|
+
require_transform.profile.impact.index();
|
|
1385
1385
|
values = new require_driver.AddressIndex();
|
|
1386
1386
|
orders = new require_driver.AddressIndex();
|
|
1387
1387
|
visitChangedLocations(changes, (at) => values.add(at, true), (at) => orders.add(at, true));
|
|
@@ -1390,7 +1390,7 @@ const createImpact = (schema, changes) => {
|
|
|
1390
1390
|
};
|
|
1391
1391
|
const cache = /* @__PURE__ */ new Map();
|
|
1392
1392
|
const affects = (value) => {
|
|
1393
|
-
|
|
1393
|
+
require_transform.profile.impact.affects();
|
|
1394
1394
|
if (!belongs(value, schema)) return false;
|
|
1395
1395
|
if (reset) return true;
|
|
1396
1396
|
return affected(value, index(), orders);
|
|
@@ -1701,7 +1701,7 @@ const transition = (node, key, beforePresent, before, afterPresent, after) => {
|
|
|
1701
1701
|
if (beforePresent === afterPresent && (!beforePresent || Object.is(before, after))) return;
|
|
1702
1702
|
if (node.kind !== "field" && beforePresent === afterPresent && equalValue(node, before, after)) return;
|
|
1703
1703
|
const value = afterPresent ? node.kind === "field" ? after : copyValue(node, after) : void 0;
|
|
1704
|
-
|
|
1704
|
+
require_transform.profile.recorder("transitions");
|
|
1705
1705
|
return !beforePresent ? {
|
|
1706
1706
|
key,
|
|
1707
1707
|
kind: "added",
|
|
@@ -1756,7 +1756,7 @@ const diffMember = (changes, members, node, at, key, beforePresent, before, afte
|
|
|
1756
1756
|
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
1757
|
let order;
|
|
1758
1758
|
if (node.kind === "table" && !equal(a.ids, b.ids)) {
|
|
1759
|
-
|
|
1759
|
+
require_transform.profile.recorder("publishedOrderItems", b.ids.length);
|
|
1760
1760
|
order = {
|
|
1761
1761
|
before: a.ids,
|
|
1762
1762
|
after: [...b.ids]
|
|
@@ -1814,7 +1814,7 @@ const sealMembers = (fact, changes) => {
|
|
|
1814
1814
|
const current = fact.node.kind === "list" ? indexedKeys(fact.value, fact.node.keyOf) : fact.value.ids;
|
|
1815
1815
|
if (!equal(fact.order, current)) {
|
|
1816
1816
|
const after = orderOf(fact.node, fact.value);
|
|
1817
|
-
|
|
1817
|
+
require_transform.profile.recorder("publishedOrderItems", after.length);
|
|
1818
1818
|
order = {
|
|
1819
1819
|
before: fact.order,
|
|
1820
1820
|
after
|
|
@@ -1872,7 +1872,7 @@ var ChangeRecorder = class {
|
|
|
1872
1872
|
this.indexedGroups = true;
|
|
1873
1873
|
for (const fact of this.facts) if (fact.kind === "members") {
|
|
1874
1874
|
index.add(fact.at, fact);
|
|
1875
|
-
|
|
1875
|
+
require_transform.profile.recorder("indexedGroups");
|
|
1876
1876
|
}
|
|
1877
1877
|
}
|
|
1878
1878
|
return index;
|
|
@@ -1921,7 +1921,7 @@ var ChangeRecorder = class {
|
|
|
1921
1921
|
releaseCovered(children) {
|
|
1922
1922
|
let changedGroups;
|
|
1923
1923
|
for (const child of children) {
|
|
1924
|
-
|
|
1924
|
+
require_transform.profile.recorder("absorbed");
|
|
1925
1925
|
if (child.kind === "member") {
|
|
1926
1926
|
const { group, member } = child;
|
|
1927
1927
|
forgetMember(group, member.key);
|
|
@@ -1937,9 +1937,9 @@ var ChangeRecorder = class {
|
|
|
1937
1937
|
this.facts.add(fact);
|
|
1938
1938
|
if (fact.kind === "members") {
|
|
1939
1939
|
this.groups.set(fact.parent, fact);
|
|
1940
|
-
|
|
1940
|
+
require_transform.profile.recorder("groups");
|
|
1941
1941
|
if (!this.indexedGroups) return;
|
|
1942
|
-
|
|
1942
|
+
require_transform.profile.recorder("indexedGroups");
|
|
1943
1943
|
}
|
|
1944
1944
|
(this.index ??= new require_driver.AddressIndex()).add(fact.at, fact);
|
|
1945
1945
|
}
|
|
@@ -1969,14 +1969,14 @@ var ChangeRecorder = class {
|
|
|
1969
1969
|
if (group.layout === "dynamic") (group.members ??= /* @__PURE__ */ new Map()).set(key, member);
|
|
1970
1970
|
else if (definition.kind === "fixed") group.members[definition.slot] = member;
|
|
1971
1971
|
else throw new Error("A fixed member group requires a fixed schema member.");
|
|
1972
|
-
|
|
1972
|
+
require_transform.profile.recorder("facts");
|
|
1973
1973
|
}
|
|
1974
1974
|
reset() {
|
|
1975
1975
|
if (this.resetBefore) return;
|
|
1976
1976
|
const value = this.absorb([], this.state.schema, this.state.document, true);
|
|
1977
1977
|
this.resetBefore = { value };
|
|
1978
1978
|
this.groups = /* @__PURE__ */ new WeakMap();
|
|
1979
|
-
|
|
1979
|
+
require_transform.profile.recorder("facts");
|
|
1980
1980
|
}
|
|
1981
1981
|
createGroup(container) {
|
|
1982
1982
|
const group = container.layout.kind === "fixed" ? {
|
|
@@ -2001,13 +2001,13 @@ var ChangeRecorder = class {
|
|
|
2001
2001
|
return group;
|
|
2002
2002
|
}
|
|
2003
2003
|
order(container) {
|
|
2004
|
-
|
|
2004
|
+
require_transform.profile.recorder("orderCaptures");
|
|
2005
2005
|
let group = this.groups.get(container.parent);
|
|
2006
2006
|
if (group?.order || this.covered(container.at)) return;
|
|
2007
2007
|
group ??= this.createGroup(container);
|
|
2008
2008
|
group.order = orderOf(container.node, container.value);
|
|
2009
|
-
|
|
2010
|
-
|
|
2009
|
+
require_transform.profile.recorder("orderSnapshots");
|
|
2010
|
+
require_transform.profile.recorder("orderItems", group.order.length);
|
|
2011
2011
|
}
|
|
2012
2012
|
tree(container, id) {
|
|
2013
2013
|
const { at, value: tree } = container;
|
|
@@ -2025,7 +2025,7 @@ var ChangeRecorder = class {
|
|
|
2025
2025
|
this.register(fact);
|
|
2026
2026
|
}
|
|
2027
2027
|
if (id !== void 0 && !fact.nodes.has(id)) {
|
|
2028
|
-
|
|
2028
|
+
require_transform.profile.recorder("treeNodes");
|
|
2029
2029
|
fact.nodes.set(id, Object.hasOwn(tree.nodes, id) ? copyTreeNode(tree.nodes[id]) : void 0);
|
|
2030
2030
|
}
|
|
2031
2031
|
}
|
|
@@ -2048,7 +2048,7 @@ var ChangeRecorder = class {
|
|
|
2048
2048
|
seal() {
|
|
2049
2049
|
if (this.resetBefore) {
|
|
2050
2050
|
if (equalValue(this.state.schema, this.resetBefore.value, this.state.document)) return require_driver.sealChanges([]);
|
|
2051
|
-
|
|
2051
|
+
require_transform.profile.recorder("sealed");
|
|
2052
2052
|
return require_driver.sealChanges([{
|
|
2053
2053
|
kind: "reset",
|
|
2054
2054
|
before: this.resetBefore.value,
|
|
@@ -2059,7 +2059,7 @@ var ChangeRecorder = class {
|
|
|
2059
2059
|
for (const fact of this.facts) if (fact.kind === "members") sealMembers(fact, changes);
|
|
2060
2060
|
else sealTree(fact, this.state, changes);
|
|
2061
2061
|
const result = require_driver.sealChanges(changes);
|
|
2062
|
-
|
|
2062
|
+
require_transform.profile.recorder("sealed", result.changes.length);
|
|
2063
2063
|
return result;
|
|
2064
2064
|
}
|
|
2065
2065
|
};
|
|
@@ -2664,7 +2664,7 @@ const select = (document, selector, equality = Object.is) => {
|
|
|
2664
2664
|
};
|
|
2665
2665
|
//#endregion
|
|
2666
2666
|
//#region core/src/projection/input.ts
|
|
2667
|
-
const valueInput = (initial, equality = Object.is) =>
|
|
2667
|
+
const valueInput = (initial, equality = Object.is) => require_transform.defineSource({
|
|
2668
2668
|
kind: "value-input",
|
|
2669
2669
|
initial,
|
|
2670
2670
|
equality
|
|
@@ -2678,7 +2678,7 @@ const collectionInput = (initial = /* @__PURE__ */ new Map(), equality = Object.
|
|
|
2678
2678
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
2679
2679
|
entries.set(key, value);
|
|
2680
2680
|
}
|
|
2681
|
-
return
|
|
2681
|
+
return require_transform.defineSource({
|
|
2682
2682
|
kind: "collection-input",
|
|
2683
2683
|
initial: entries,
|
|
2684
2684
|
equality
|
|
@@ -2691,13 +2691,13 @@ const input = Object.assign(valueInput, { collection: collectionInput });
|
|
|
2691
2691
|
//#endregion
|
|
2692
2692
|
//#region core/src/projection/observe.ts
|
|
2693
2693
|
function observe(source, selector) {
|
|
2694
|
-
if (isExternalSource(source)) return source.kind === "collection" ?
|
|
2694
|
+
if (isExternalSource(source)) return source.kind === "collection" ? require_transform.defineSource({
|
|
2695
2695
|
kind: "external-collection",
|
|
2696
2696
|
source
|
|
2697
2697
|
}, {
|
|
2698
2698
|
kind: "collection",
|
|
2699
2699
|
equality: Object.is
|
|
2700
|
-
}) :
|
|
2700
|
+
}) : require_transform.defineSource({
|
|
2701
2701
|
kind: "external-value",
|
|
2702
2702
|
source,
|
|
2703
2703
|
equality: Object.is
|
|
@@ -2705,7 +2705,7 @@ function observe(source, selector) {
|
|
|
2705
2705
|
kind: "value",
|
|
2706
2706
|
equality: Object.is
|
|
2707
2707
|
});
|
|
2708
|
-
if ("current" in source && typeof source.current === "function") return
|
|
2708
|
+
if ("current" in source && typeof source.current === "function") return require_transform.defineSource({
|
|
2709
2709
|
kind: "readable",
|
|
2710
2710
|
readable: source,
|
|
2711
2711
|
equality: Object.is
|
|
@@ -2720,7 +2720,7 @@ function observe(source, selector) {
|
|
|
2720
2720
|
schema: state.schema,
|
|
2721
2721
|
address: Object.freeze([])
|
|
2722
2722
|
});
|
|
2723
|
-
return
|
|
2723
|
+
return require_transform.defineSource({
|
|
2724
2724
|
kind: "document",
|
|
2725
2725
|
document,
|
|
2726
2726
|
selector: selected
|
|
@@ -2735,273 +2735,522 @@ const isExternalSource = (source) => {
|
|
|
2735
2735
|
return (candidate.kind === "value" || candidate.kind === "collection") && typeof candidate.current === "function" && typeof candidate.revision === "function" && typeof candidate.subscribe === "function";
|
|
2736
2736
|
};
|
|
2737
2737
|
//#endregion
|
|
2738
|
-
//#region core/src/projection/derive.ts
|
|
2739
|
-
const
|
|
2740
|
-
|
|
2741
|
-
const
|
|
2742
|
-
const
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
};
|
|
2747
|
-
const assertKeyedProjection = (projection, role) => {
|
|
2748
|
-
if (outputKind(projection) !== "collection") throw new TypeError(`${role} must be a keyed collection projection.`);
|
|
2749
|
-
};
|
|
2750
|
-
const isKeyedDependency = (value) => {
|
|
2751
|
-
if (!value || typeof value !== "object" || require_scheduler.isProjection(value)) return false;
|
|
2752
|
-
const candidate = value;
|
|
2753
|
-
return require_scheduler.isProjection(candidate.source) && typeof candidate.key === "function";
|
|
2754
|
-
};
|
|
2755
|
-
const bindKey = (index, outputKey, sourceKey) => {
|
|
2756
|
-
const previous = index.forward.get(outputKey);
|
|
2757
|
-
if (previous === sourceKey) return;
|
|
2758
|
-
if (previous !== void 0) {
|
|
2759
|
-
const dependents = index.reverse.get(previous);
|
|
2760
|
-
dependents?.delete(outputKey);
|
|
2761
|
-
if (dependents?.size === 0) index.reverse.delete(previous);
|
|
2762
|
-
index.forward.delete(outputKey);
|
|
2738
|
+
//#region core/src/projection/derive/keyed.ts
|
|
2739
|
+
const compileOrderedKeys = (value, role) => {
|
|
2740
|
+
if (!Array.isArray(value)) throw new TypeError(`${role} must be an array of string keys.`);
|
|
2741
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2742
|
+
for (const key of value) {
|
|
2743
|
+
if (typeof key !== "string") throw new TypeError(`${role} must contain only string keys.`);
|
|
2744
|
+
if (seen.has(key)) throw new TypeError(`${role} must not contain duplicate keys.`);
|
|
2745
|
+
seen.add(key);
|
|
2763
2746
|
}
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
const
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2747
|
+
return {
|
|
2748
|
+
keys: value,
|
|
2749
|
+
requested: seen
|
|
2750
|
+
};
|
|
2751
|
+
};
|
|
2752
|
+
function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
2753
|
+
const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
|
|
2754
|
+
const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
|
|
2755
|
+
const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
|
|
2756
|
+
if (typeof select !== "function") throw new TypeError("Keyed derive requires a selector.");
|
|
2757
|
+
return require_transform.createKeyedTransform({
|
|
2758
|
+
source,
|
|
2759
|
+
dependencies: dependencySpecs,
|
|
2760
|
+
equality,
|
|
2761
|
+
name: "derive.keyed",
|
|
2762
|
+
evaluate: ({ value, key, dependencies }) => dependencySpecs === void 0 ? select(value, key) : select(value, key, dependencies)
|
|
2763
|
+
});
|
|
2764
|
+
}
|
|
2765
|
+
const createKeyedKeys = (source) => {
|
|
2766
|
+
require_transform.assertKeyedProjection(source, "Keyed keys source");
|
|
2767
|
+
const [projection] = require_transform.defineProcessor({
|
|
2768
|
+
dependencies: [source],
|
|
2784
2769
|
outputs: [{
|
|
2785
2770
|
kind: "value",
|
|
2786
|
-
equality
|
|
2771
|
+
equality: Object.is
|
|
2787
2772
|
}],
|
|
2788
2773
|
create: () => ({ evaluate: (evaluation) => {
|
|
2774
|
+
const driver = evaluation.sources[0];
|
|
2789
2775
|
const output = evaluation.outputs[0];
|
|
2790
|
-
if (output.kind !== "value") throw new Error("derive requires
|
|
2791
|
-
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
require_scheduler.assertSynchronous(next);
|
|
2795
|
-
output.output.set(next);
|
|
2796
|
-
} })
|
|
2776
|
+
if (driver.kind !== "collection" || output.kind !== "value") throw new Error("derive.keyed.keys requires collection input and value output.");
|
|
2777
|
+
if (evaluation.reset || !driver.change || require_transform.collectionHasStructuralChange(driver.change)) output.output.set(driver.read.ids());
|
|
2778
|
+
} }),
|
|
2779
|
+
name: "derive.keyed.keys"
|
|
2797
2780
|
});
|
|
2798
2781
|
return projection;
|
|
2782
|
+
};
|
|
2783
|
+
const createOrderedSnapshot = (source, name, make) => {
|
|
2784
|
+
require_transform.assertKeyedProjection(source, `${name} source`);
|
|
2785
|
+
const [projection] = require_transform.defineProcessor({
|
|
2786
|
+
dependencies: [source],
|
|
2787
|
+
outputs: [{
|
|
2788
|
+
kind: "value",
|
|
2789
|
+
equality: Object.is
|
|
2790
|
+
}],
|
|
2791
|
+
create: () => {
|
|
2792
|
+
let indexes = /* @__PURE__ */ new Map();
|
|
2793
|
+
const rebuild = (driver) => {
|
|
2794
|
+
const nextIndexes = /* @__PURE__ */ new Map();
|
|
2795
|
+
const result = Object.freeze(driver.read.ids().map((key, index) => {
|
|
2796
|
+
nextIndexes.set(key, index);
|
|
2797
|
+
return make(key, driver.read.get(key));
|
|
2798
|
+
}));
|
|
2799
|
+
indexes = nextIndexes;
|
|
2800
|
+
return result;
|
|
2801
|
+
};
|
|
2802
|
+
const rebuildIncremental = (driver, change, previous) => {
|
|
2803
|
+
const changedValues = /* @__PURE__ */ new Map();
|
|
2804
|
+
for (const entry of change.added) changedValues.set(entry.key, entry.after);
|
|
2805
|
+
for (const entry of change.updated) changedValues.set(entry.key, entry.after);
|
|
2806
|
+
const nextIndexes = /* @__PURE__ */ new Map();
|
|
2807
|
+
const result = Object.freeze(driver.read.ids().map((key, index) => {
|
|
2808
|
+
nextIndexes.set(key, index);
|
|
2809
|
+
if (changedValues.has(key)) return make(key, changedValues.get(key));
|
|
2810
|
+
const previousIndex = indexes.get(key);
|
|
2811
|
+
return previousIndex === void 0 ? make(key, driver.read.get(key)) : previous[previousIndex];
|
|
2812
|
+
}));
|
|
2813
|
+
indexes = nextIndexes;
|
|
2814
|
+
return result;
|
|
2815
|
+
};
|
|
2816
|
+
return {
|
|
2817
|
+
evaluate: (evaluation) => {
|
|
2818
|
+
const driver = evaluation.sources[0];
|
|
2819
|
+
const output = evaluation.outputs[0];
|
|
2820
|
+
if (driver.kind !== "collection" || output.kind !== "value") throw new Error(`${name} requires collection input and value output.`);
|
|
2821
|
+
const change = driver.change;
|
|
2822
|
+
if (evaluation.reset || !change || change.kind === "reset") {
|
|
2823
|
+
output.output.set(rebuild(driver));
|
|
2824
|
+
return;
|
|
2825
|
+
}
|
|
2826
|
+
const previous = output.previous;
|
|
2827
|
+
if (!previous) {
|
|
2828
|
+
output.output.set(rebuild(driver));
|
|
2829
|
+
return;
|
|
2830
|
+
}
|
|
2831
|
+
if (require_transform.collectionHasStructuralChange(change)) {
|
|
2832
|
+
output.output.set(rebuildIncremental(driver, change, previous));
|
|
2833
|
+
return;
|
|
2834
|
+
}
|
|
2835
|
+
if (!change.updated.length) return;
|
|
2836
|
+
const next = [...previous];
|
|
2837
|
+
for (const entry of change.updated) {
|
|
2838
|
+
const index = indexes.get(entry.key);
|
|
2839
|
+
if (index === void 0) {
|
|
2840
|
+
output.output.set(rebuild(driver));
|
|
2841
|
+
return;
|
|
2842
|
+
}
|
|
2843
|
+
next[index] = make(entry.key, entry.after);
|
|
2844
|
+
}
|
|
2845
|
+
output.output.set(Object.freeze(next));
|
|
2846
|
+
},
|
|
2847
|
+
release: () => indexes.clear()
|
|
2848
|
+
};
|
|
2849
|
+
},
|
|
2850
|
+
name
|
|
2851
|
+
});
|
|
2852
|
+
return projection;
|
|
2853
|
+
};
|
|
2854
|
+
const createKeyedValues = (source) => createOrderedSnapshot(source, "derive.keyed.values", (_key, value) => value);
|
|
2855
|
+
const createKeyedEntries = (source) => createOrderedSnapshot(source, "derive.keyed.entries", (key, value) => Object.freeze([key, value]));
|
|
2856
|
+
const createKeyedGet = (source, key, equality = Object.is) => {
|
|
2857
|
+
require_transform.assertKeyedProjection(source, "derive.keyed.get source");
|
|
2858
|
+
if (require_transform.outputKind(key) !== "value") throw new TypeError("derive.keyed.get key must be a scalar projection.");
|
|
2859
|
+
const [projection] = require_transform.defineProcessor({
|
|
2860
|
+
dependencies: [source, key],
|
|
2861
|
+
outputs: [{
|
|
2862
|
+
kind: "value",
|
|
2863
|
+
equality
|
|
2864
|
+
}],
|
|
2865
|
+
create: () => {
|
|
2866
|
+
let sourceRevision = -1;
|
|
2867
|
+
let keyRevision = -1;
|
|
2868
|
+
let selected;
|
|
2869
|
+
return { evaluate: (evaluation) => {
|
|
2870
|
+
const collection = evaluation.sources[0];
|
|
2871
|
+
const selectedKey = evaluation.sources[1];
|
|
2872
|
+
const output = evaluation.outputs[0];
|
|
2873
|
+
if (collection.kind !== "collection" || selectedKey.kind !== "value" || output.kind !== "value") throw new Error("derive.keyed.get resolved invalid input/output kinds.");
|
|
2874
|
+
let affected = evaluation.reset;
|
|
2875
|
+
if (evaluation.reset || keyRevision !== selectedKey.revision) {
|
|
2876
|
+
const candidate = selectedKey.value;
|
|
2877
|
+
if (candidate !== void 0 && typeof candidate !== "string") throw new TypeError("derive.keyed.get key must resolve to a string or undefined.");
|
|
2878
|
+
selected = candidate;
|
|
2879
|
+
affected = true;
|
|
2880
|
+
} else if (sourceRevision !== collection.revision) {
|
|
2881
|
+
const change = collection.change;
|
|
2882
|
+
if (!change || change.kind === "reset") affected = true;
|
|
2883
|
+
else if (selected !== void 0) {
|
|
2884
|
+
for (const changedKey of require_transform.collectionChange.keys(change)) if (changedKey === selected) {
|
|
2885
|
+
affected = true;
|
|
2886
|
+
break;
|
|
2887
|
+
}
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
if (affected) output.output.set(selected === void 0 ? void 0 : collection.read.get(selected));
|
|
2891
|
+
sourceRevision = collection.revision;
|
|
2892
|
+
keyRevision = selectedKey.revision;
|
|
2893
|
+
} };
|
|
2894
|
+
},
|
|
2895
|
+
name: "derive.keyed.get"
|
|
2896
|
+
});
|
|
2897
|
+
return projection;
|
|
2898
|
+
};
|
|
2899
|
+
function createKeyedFilter(source, dependenciesOrPredicate, maybePredicate) {
|
|
2900
|
+
const dependencySpecs = typeof dependenciesOrPredicate === "function" ? void 0 : dependenciesOrPredicate;
|
|
2901
|
+
const predicate = dependencySpecs === void 0 ? dependenciesOrPredicate : maybePredicate;
|
|
2902
|
+
if (typeof predicate !== "function") throw new TypeError("Keyed filter requires a predicate.");
|
|
2903
|
+
return require_transform.createKeyedTransform({
|
|
2904
|
+
source,
|
|
2905
|
+
dependencies: dependencySpecs,
|
|
2906
|
+
equality: Object.is,
|
|
2907
|
+
name: "derive.keyed.filter",
|
|
2908
|
+
evaluate: ({ value, key, dependencies }) => {
|
|
2909
|
+
const included = dependencySpecs === void 0 ? predicate(value, key) : predicate(value, key, dependencies);
|
|
2910
|
+
require_transform.assertSynchronous(included);
|
|
2911
|
+
if (typeof included !== "boolean") throw new TypeError("Keyed filter predicate must return boolean.");
|
|
2912
|
+
return included ? value : require_transform.keyedAbsent;
|
|
2913
|
+
}
|
|
2914
|
+
});
|
|
2799
2915
|
}
|
|
2800
|
-
function
|
|
2801
|
-
assertKeyedProjection(source, "Keyed derive source");
|
|
2916
|
+
function createKeyedCompact(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
2802
2917
|
const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
|
|
2803
2918
|
const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
|
|
2804
2919
|
const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
|
|
2805
|
-
if (typeof select !== "function") throw new TypeError("Keyed
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
const dependency = descriptor.value;
|
|
2816
|
-
dependencyNames.push(name);
|
|
2817
|
-
if (require_scheduler.isProjection(dependency)) {
|
|
2818
|
-
dependencies.push(dependency);
|
|
2819
|
-
keyed.push(void 0);
|
|
2820
|
-
continue;
|
|
2821
|
-
}
|
|
2822
|
-
if (!isKeyedDependency(dependency)) throw new TypeError("Keyed derive dependencies must be projections or keyed lookups.");
|
|
2823
|
-
assertKeyedProjection(dependency.source, "Dynamic keyed dependency source");
|
|
2824
|
-
dependencies.push(dependency.source);
|
|
2825
|
-
keyed.push(dependency);
|
|
2920
|
+
if (typeof select !== "function") throw new TypeError("Keyed compact requires a selector.");
|
|
2921
|
+
return require_transform.createKeyedTransform({
|
|
2922
|
+
source,
|
|
2923
|
+
dependencies: dependencySpecs,
|
|
2924
|
+
equality,
|
|
2925
|
+
name: "derive.keyed.compact",
|
|
2926
|
+
evaluate: ({ value, key, dependencies }) => {
|
|
2927
|
+
const next = dependencySpecs === void 0 ? select(value, key) : select(value, key, dependencies);
|
|
2928
|
+
require_transform.assertSynchronous(next);
|
|
2929
|
+
return next === void 0 ? require_transform.keyedAbsent : next;
|
|
2826
2930
|
}
|
|
2931
|
+
});
|
|
2932
|
+
}
|
|
2933
|
+
const createKeyedSubset = (source, orderedKeys) => {
|
|
2934
|
+
require_transform.assertKeyedProjection(source, "Keyed subset source");
|
|
2935
|
+
const keyProjection = require_transform.isProjection(orderedKeys) ? orderedKeys : void 0;
|
|
2936
|
+
if (keyProjection && require_transform.outputKind(keyProjection) !== "value") throw new TypeError("Keyed subset ordered keys must be a value projection.");
|
|
2937
|
+
const staticSelection = keyProjection ? void 0 : (() => {
|
|
2938
|
+
const compiled = compileOrderedKeys(orderedKeys, "Keyed subset ordered keys");
|
|
2939
|
+
return {
|
|
2940
|
+
keys: Object.freeze([...compiled.keys]),
|
|
2941
|
+
requested: compiled.requested
|
|
2942
|
+
};
|
|
2943
|
+
})();
|
|
2944
|
+
const [projection] = require_transform.defineProcessor({
|
|
2945
|
+
dependencies: keyProjection ? [source, keyProjection] : [source],
|
|
2946
|
+
outputs: [{
|
|
2947
|
+
kind: "collection",
|
|
2948
|
+
equality: Object.is
|
|
2949
|
+
}],
|
|
2950
|
+
create: () => {
|
|
2951
|
+
let keys = staticSelection?.keys;
|
|
2952
|
+
let requested = staticSelection?.requested;
|
|
2953
|
+
let keyRevision = -1;
|
|
2954
|
+
return {
|
|
2955
|
+
evaluate: (evaluation) => {
|
|
2956
|
+
const driver = evaluation.sources[0];
|
|
2957
|
+
const output = evaluation.outputs[0];
|
|
2958
|
+
if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("derive.keyed.subset requires collection input and output.");
|
|
2959
|
+
let keysChanged = false;
|
|
2960
|
+
if (keyProjection) {
|
|
2961
|
+
const sourceContext = evaluation.sources[1];
|
|
2962
|
+
if (sourceContext?.kind !== "value") throw new TypeError("Keyed subset ordered keys resolved to a collection.");
|
|
2963
|
+
if (keys === void 0 || sourceContext.reset || keyRevision !== sourceContext.revision) {
|
|
2964
|
+
const compiled = compileOrderedKeys(sourceContext.value, "Keyed subset ordered keys");
|
|
2965
|
+
keys = compiled.keys;
|
|
2966
|
+
requested = compiled.requested;
|
|
2967
|
+
keyRevision = sourceContext.revision;
|
|
2968
|
+
keysChanged = true;
|
|
2969
|
+
}
|
|
2970
|
+
}
|
|
2971
|
+
if (!keys || !requested) throw new Error("Keyed subset keys were not initialized.");
|
|
2972
|
+
const activeKeys = keys;
|
|
2973
|
+
const activeRequested = requested;
|
|
2974
|
+
const orderedPresentKeys = () => activeKeys.filter((key) => driver.read.has(key));
|
|
2975
|
+
const rebuild = () => {
|
|
2976
|
+
const nextKeys = orderedPresentKeys();
|
|
2977
|
+
for (const key of nextKeys) output.output.set(key, driver.read.get(key));
|
|
2978
|
+
output.output.order(nextKeys);
|
|
2979
|
+
};
|
|
2980
|
+
const reconcileKeys = () => {
|
|
2981
|
+
const nextKeys = orderedPresentKeys();
|
|
2982
|
+
for (const key of output.previous.ids()) if (!activeRequested.has(key) || !driver.read.has(key)) output.output.remove(key);
|
|
2983
|
+
for (const key of nextKeys) if (!output.previous.has(key)) output.output.set(key, driver.read.get(key));
|
|
2984
|
+
output.output.order(nextKeys);
|
|
2985
|
+
};
|
|
2986
|
+
const change = driver.change;
|
|
2987
|
+
if (evaluation.reset || change?.kind === "reset") rebuild();
|
|
2988
|
+
else if (keysChanged) {
|
|
2989
|
+
reconcileKeys();
|
|
2990
|
+
if (change) for (const entry of change.updated) {
|
|
2991
|
+
const key = entry.key;
|
|
2992
|
+
if (activeRequested.has(key) && output.previous.has(key)) output.output.set(key, entry.after);
|
|
2993
|
+
}
|
|
2994
|
+
} else if (change) {
|
|
2995
|
+
let updateOrder = false;
|
|
2996
|
+
for (const entry of change.removed) {
|
|
2997
|
+
const key = entry.key;
|
|
2998
|
+
if (!activeRequested.has(key)) continue;
|
|
2999
|
+
output.output.remove(key);
|
|
3000
|
+
updateOrder = true;
|
|
3001
|
+
}
|
|
3002
|
+
for (const entry of change.added) {
|
|
3003
|
+
const key = entry.key;
|
|
3004
|
+
if (!activeRequested.has(key)) continue;
|
|
3005
|
+
output.output.set(key, entry.after);
|
|
3006
|
+
updateOrder = true;
|
|
3007
|
+
}
|
|
3008
|
+
for (const entry of change.updated) {
|
|
3009
|
+
const key = entry.key;
|
|
3010
|
+
if (activeRequested.has(key)) output.output.set(key, entry.after);
|
|
3011
|
+
}
|
|
3012
|
+
if (updateOrder) output.output.order(orderedPresentKeys());
|
|
3013
|
+
}
|
|
3014
|
+
},
|
|
3015
|
+
release: () => {
|
|
3016
|
+
if (keyProjection) {
|
|
3017
|
+
keys = void 0;
|
|
3018
|
+
requested = void 0;
|
|
3019
|
+
keyRevision = -1;
|
|
3020
|
+
}
|
|
3021
|
+
}
|
|
3022
|
+
};
|
|
3023
|
+
},
|
|
3024
|
+
name: "derive.keyed.subset"
|
|
3025
|
+
});
|
|
3026
|
+
return projection;
|
|
3027
|
+
};
|
|
3028
|
+
const normalizeGroups = (value) => {
|
|
3029
|
+
const groups = typeof value === "string" ? [value] : value;
|
|
3030
|
+
if (!Array.isArray(groups)) throw new TypeError("derive.keyed.groupBy selector must return a string or an array of strings.");
|
|
3031
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3032
|
+
const result = [];
|
|
3033
|
+
for (const group of groups) {
|
|
3034
|
+
if (typeof group !== "string") throw new TypeError("derive.keyed.groupBy selector must return only string group keys.");
|
|
3035
|
+
if (seen.has(group)) throw new TypeError("derive.keyed.groupBy selector must not return duplicate group keys.");
|
|
3036
|
+
seen.add(group);
|
|
3037
|
+
result.push(group);
|
|
2827
3038
|
}
|
|
2828
|
-
|
|
2829
|
-
|
|
3039
|
+
return Object.freeze(result);
|
|
3040
|
+
};
|
|
3041
|
+
function createKeyedGroupBy(source, dependenciesOrSelector, maybeSelector) {
|
|
3042
|
+
require_transform.assertKeyedProjection(source, "derive.keyed.groupBy source");
|
|
3043
|
+
const dependencySpecs = typeof dependenciesOrSelector === "function" ? void 0 : dependenciesOrSelector;
|
|
3044
|
+
const selector = dependencySpecs === void 0 ? dependenciesOrSelector : maybeSelector;
|
|
3045
|
+
if (typeof selector !== "function") throw new TypeError("derive.keyed.groupBy requires a selector.");
|
|
3046
|
+
const compiled = require_transform.compileKeyedDependencies(source, dependencySpecs, "derive.keyed.groupBy");
|
|
3047
|
+
const [projection] = require_transform.defineProcessor({
|
|
3048
|
+
dependencies: compiled.projections,
|
|
2830
3049
|
outputs: [{
|
|
2831
3050
|
kind: "collection",
|
|
2832
|
-
equality:
|
|
3051
|
+
equality: (previous, next) => {
|
|
3052
|
+
const left = previous;
|
|
3053
|
+
const right = next;
|
|
3054
|
+
return left.length === right.length && left.every((key, index) => key === right[index]);
|
|
3055
|
+
}
|
|
2833
3056
|
}],
|
|
2834
3057
|
create: () => {
|
|
2835
|
-
const
|
|
2836
|
-
const
|
|
2837
|
-
const
|
|
2838
|
-
|
|
2839
|
-
const
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
3058
|
+
const dependencies = require_transform.createKeyedDependencyRuntime(compiled);
|
|
3059
|
+
const relation = require_transform.createKeyRelation();
|
|
3060
|
+
const orderIndex = /* @__PURE__ */ new Map();
|
|
3061
|
+
const groupRanks = /* @__PURE__ */ new Map();
|
|
3062
|
+
const refreshOrderIndex = (driver) => {
|
|
3063
|
+
orderIndex.clear();
|
|
3064
|
+
driver.read.ids().forEach((key, index) => orderIndex.set(key, index));
|
|
3065
|
+
};
|
|
3066
|
+
const selectGroups = (driver, sources, key, affected) => {
|
|
3067
|
+
if (!driver.read.has(key)) return false;
|
|
3068
|
+
const value = driver.read.get(key);
|
|
3069
|
+
const dependencyValues = dependencies.resolve(value, key, sources);
|
|
3070
|
+
const selected = dependencySpecs === void 0 ? selector(value, key) : selector(value, key, dependencyValues);
|
|
3071
|
+
require_transform.assertSynchronous(selected);
|
|
3072
|
+
const next = normalizeGroups(selected);
|
|
3073
|
+
const previous = relation.forward(key) ?? Object.freeze([]);
|
|
3074
|
+
if (!relation.replace(key, next)) return false;
|
|
3075
|
+
for (const group of previous) affected.add(group);
|
|
3076
|
+
for (const group of next) affected.add(group);
|
|
3077
|
+
return true;
|
|
3078
|
+
};
|
|
3079
|
+
const updateGroup = (group, output) => {
|
|
3080
|
+
const reverse = relation.reverse(group);
|
|
3081
|
+
if (!reverse?.size) {
|
|
3082
|
+
groupRanks.delete(group);
|
|
3083
|
+
output.output.remove(group);
|
|
3084
|
+
return;
|
|
2843
3085
|
}
|
|
3086
|
+
const members = [...reverse].sort((left, right) => (orderIndex.get(left) ?? Number.MAX_SAFE_INTEGER) - (orderIndex.get(right) ?? Number.MAX_SAFE_INTEGER));
|
|
3087
|
+
const first = members[0];
|
|
3088
|
+
const source = orderIndex.get(first);
|
|
3089
|
+
const selector = relation.forward(first)?.indexOf(group) ?? -1;
|
|
3090
|
+
if (source === void 0 || selector < 0) throw new Error("derive.keyed.groupBy relation is inconsistent with source order.");
|
|
3091
|
+
groupRanks.set(group, {
|
|
3092
|
+
source,
|
|
3093
|
+
selector
|
|
3094
|
+
});
|
|
3095
|
+
output.output.set(group, Object.freeze(members));
|
|
3096
|
+
};
|
|
3097
|
+
const publishGroupOrder = (output) => {
|
|
3098
|
+
const order = [...groupRanks.entries()].sort(([leftKey, left], [rightKey, right]) => {
|
|
3099
|
+
if (left.source !== right.source) return left.source - right.source;
|
|
3100
|
+
if (left.selector !== right.selector) return left.selector - right.selector;
|
|
3101
|
+
return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
|
|
3102
|
+
}).map(([group]) => group);
|
|
3103
|
+
output.output.order(order);
|
|
3104
|
+
};
|
|
3105
|
+
const rebuild = (driver, sources, output) => {
|
|
3106
|
+
dependencies.clearBindings();
|
|
3107
|
+
relation.clear();
|
|
3108
|
+
groupRanks.clear();
|
|
3109
|
+
refreshOrderIndex(driver);
|
|
3110
|
+
const affected = /* @__PURE__ */ new Set();
|
|
3111
|
+
for (const key of driver.read.ids()) selectGroups(driver, sources, key, affected);
|
|
3112
|
+
for (const key of output.previous.ids()) affected.add(key);
|
|
3113
|
+
for (const group of affected) updateGroup(group, output);
|
|
3114
|
+
publishGroupOrder(output);
|
|
3115
|
+
};
|
|
3116
|
+
const publishAffected = (affected, output, orderDirty) => {
|
|
3117
|
+
for (const group of affected) updateGroup(group, output);
|
|
3118
|
+
if (orderDirty) publishGroupOrder(output);
|
|
3119
|
+
};
|
|
3120
|
+
const removeSourceKey = (key, affected) => {
|
|
3121
|
+
const previous = relation.forward(key);
|
|
3122
|
+
if (!previous) return false;
|
|
3123
|
+
for (const group of previous) affected.add(group);
|
|
3124
|
+
return relation.delete(key);
|
|
3125
|
+
};
|
|
3126
|
+
const markAllGroups = (output, affected) => {
|
|
3127
|
+
for (const group of relation.rights()) affected.add(group);
|
|
3128
|
+
for (const group of output.previous.ids()) affected.add(group);
|
|
2844
3129
|
};
|
|
2845
3130
|
return {
|
|
2846
3131
|
evaluate: (evaluation) => {
|
|
2847
3132
|
const driver = evaluation.sources[0];
|
|
2848
3133
|
const output = evaluation.outputs[0];
|
|
2849
|
-
if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("
|
|
2850
|
-
|
|
2851
|
-
for (let index = 0; index < keyed.length; index++) {
|
|
2852
|
-
if (keyed[index]) continue;
|
|
2853
|
-
if (evaluation.reset || revisions[index + 1] !== currentRevisions[index + 1]) globalValues[index] = sourceValue(evaluation.sources[index + 1]);
|
|
2854
|
-
}
|
|
2855
|
-
const project = (key) => {
|
|
2856
|
-
if (!driver.read.has(key)) return;
|
|
2857
|
-
const value = driver.read.get(key);
|
|
2858
|
-
const dependencyValues = Object.create(null);
|
|
2859
|
-
for (let index = 0; index < keyed.length; index++) {
|
|
2860
|
-
const dependency = keyed[index];
|
|
2861
|
-
if (!dependency) {
|
|
2862
|
-
dependencyValues[dependencyNames[index]] = globalValues[index];
|
|
2863
|
-
continue;
|
|
2864
|
-
}
|
|
2865
|
-
const sourceContext = evaluation.sources[index + 1];
|
|
2866
|
-
if (sourceContext.kind !== "collection") throw new TypeError("Dynamic keyed dependency resolved to a non-collection source.");
|
|
2867
|
-
const selectedKey = dependency.key(value, key);
|
|
2868
|
-
require_scheduler.assertSynchronous(selectedKey);
|
|
2869
|
-
if (selectedKey !== void 0 && typeof selectedKey !== "string") throw new TypeError("Dynamic keyed dependency keys must be strings or undefined.");
|
|
2870
|
-
bindKey(bindings[index], key, selectedKey);
|
|
2871
|
-
dependencyValues[dependencyNames[index]] = selectedKey === void 0 ? void 0 : sourceContext.read.get(selectedKey);
|
|
2872
|
-
}
|
|
2873
|
-
const next = dependencySpecs === void 0 ? select(value, key) : select(value, key, Object.freeze(dependencyValues));
|
|
2874
|
-
require_scheduler.assertSynchronous(next);
|
|
2875
|
-
output.output.set(key, next);
|
|
2876
|
-
};
|
|
3134
|
+
if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("derive.keyed.groupBy requires collection input and output.");
|
|
3135
|
+
dependencies.prepare(evaluation.sources, evaluation.reset);
|
|
2877
3136
|
if (evaluation.reset) {
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
for (const key of ids) project(key);
|
|
2881
|
-
output.output.order(ids);
|
|
2882
|
-
revisions = Object.freeze(currentRevisions);
|
|
3137
|
+
rebuild(driver, evaluation.sources, output);
|
|
3138
|
+
dependencies.remember(evaluation.sources);
|
|
2883
3139
|
return;
|
|
2884
3140
|
}
|
|
2885
3141
|
const dirty = /* @__PURE__ */ new Set();
|
|
3142
|
+
const affected = /* @__PURE__ */ new Set();
|
|
2886
3143
|
let all = false;
|
|
2887
|
-
let
|
|
2888
|
-
|
|
3144
|
+
let structural = false;
|
|
3145
|
+
let orderDirty = false;
|
|
3146
|
+
if (dependencies.driverChanged(driver)) {
|
|
2889
3147
|
const change = driver.change;
|
|
2890
3148
|
if (!change || change.kind === "reset") {
|
|
2891
|
-
|
|
2892
|
-
|
|
3149
|
+
rebuild(driver, evaluation.sources, output);
|
|
3150
|
+
dependencies.remember(evaluation.sources);
|
|
3151
|
+
return;
|
|
2893
3152
|
} else {
|
|
2894
3153
|
for (const entry of change.removed) {
|
|
2895
|
-
|
|
2896
|
-
|
|
3154
|
+
orderDirty ||= removeSourceKey(entry.key, affected);
|
|
3155
|
+
dependencies.remove(entry.key);
|
|
2897
3156
|
dirty.delete(entry.key);
|
|
2898
3157
|
}
|
|
2899
3158
|
for (const entry of change.added) dirty.add(entry.key);
|
|
2900
3159
|
for (const entry of change.updated) dirty.add(entry.key);
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
for (let index = 0; index < keyed.length; index++) {
|
|
2905
|
-
if (revisions[index + 1] === currentRevisions[index + 1]) continue;
|
|
2906
|
-
if (!keyed[index]) {
|
|
2907
|
-
all = true;
|
|
2908
|
-
continue;
|
|
2909
|
-
}
|
|
2910
|
-
const sourceContext = evaluation.sources[index + 1];
|
|
2911
|
-
if (sourceContext.kind !== "collection" || !sourceContext.change) {
|
|
2912
|
-
all = true;
|
|
2913
|
-
continue;
|
|
2914
|
-
}
|
|
2915
|
-
if (sourceContext.change.kind === "reset") {
|
|
2916
|
-
all = true;
|
|
2917
|
-
continue;
|
|
3160
|
+
structural = Boolean(change.added.length || change.removed.length || change.order);
|
|
3161
|
+
orderDirty ||= structural;
|
|
3162
|
+
if (structural) refreshOrderIndex(driver);
|
|
2918
3163
|
}
|
|
2919
|
-
const reverse = bindings[index].reverse;
|
|
2920
|
-
for (const transition of sourceContext.change.added) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
|
|
2921
|
-
for (const transition of sourceContext.change.updated) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
|
|
2922
|
-
for (const transition of sourceContext.change.removed) for (const outputKey of reverse.get(transition.key) ?? []) dirty.add(outputKey);
|
|
2923
3164
|
}
|
|
3165
|
+
all ||= dependencies.collectInvalidated(evaluation.sources, dirty);
|
|
2924
3166
|
if (all) for (const key of driver.read.ids()) dirty.add(key);
|
|
2925
|
-
for (const key of dirty)
|
|
2926
|
-
if (
|
|
2927
|
-
|
|
3167
|
+
for (const key of dirty) orderDirty ||= selectGroups(driver, evaluation.sources, key, affected);
|
|
3168
|
+
if (structural) markAllGroups(output, affected);
|
|
3169
|
+
publishAffected(affected, output, orderDirty);
|
|
3170
|
+
dependencies.remember(evaluation.sources);
|
|
2928
3171
|
},
|
|
2929
3172
|
release: () => {
|
|
2930
|
-
|
|
2931
|
-
|
|
3173
|
+
relation.clear();
|
|
3174
|
+
orderIndex.clear();
|
|
3175
|
+
groupRanks.clear();
|
|
3176
|
+
dependencies.release();
|
|
2932
3177
|
}
|
|
2933
3178
|
};
|
|
2934
3179
|
},
|
|
2935
|
-
name: "derive.keyed"
|
|
3180
|
+
name: "derive.keyed.groupBy"
|
|
2936
3181
|
});
|
|
2937
3182
|
return projection;
|
|
2938
3183
|
}
|
|
2939
|
-
const
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
const
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
3184
|
+
const createKeyedSingleton = (source, keyOf, equality = Object.is) => {
|
|
3185
|
+
if (require_transform.outputKind(source) !== "value") throw new TypeError("derive.keyed.singleton source must be a scalar projection.");
|
|
3186
|
+
if (typeof keyOf !== "function") throw new TypeError("derive.keyed.singleton requires a key selector.");
|
|
3187
|
+
const [projection] = require_transform.defineProcessor({
|
|
3188
|
+
dependencies: [source],
|
|
3189
|
+
outputs: [{
|
|
3190
|
+
kind: "collection",
|
|
3191
|
+
equality
|
|
3192
|
+
}],
|
|
3193
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
3194
|
+
const scalar = evaluation.sources[0];
|
|
3195
|
+
const output = evaluation.outputs[0];
|
|
3196
|
+
if (scalar.kind !== "value" || output.kind !== "collection") throw new Error("derive.keyed.singleton resolved invalid input/output kinds.");
|
|
3197
|
+
const previous = output.previous.ids();
|
|
3198
|
+
if (scalar.value === void 0) {
|
|
3199
|
+
for (const key of previous) output.output.remove(key);
|
|
3200
|
+
return;
|
|
3201
|
+
}
|
|
3202
|
+
const key = keyOf(scalar.value);
|
|
3203
|
+
require_transform.assertSynchronous(key);
|
|
3204
|
+
if (typeof key !== "string") throw new TypeError("derive.keyed.singleton key selector must return a string.");
|
|
3205
|
+
for (const previousKey of previous) if (previousKey !== key) output.output.remove(previousKey);
|
|
3206
|
+
output.output.set(key, scalar.value);
|
|
3207
|
+
if (previous.length !== 1 || previous[0] !== key) output.output.order([key]);
|
|
3208
|
+
} }),
|
|
3209
|
+
name: "derive.keyed.singleton"
|
|
2962
3210
|
});
|
|
3211
|
+
return projection;
|
|
2963
3212
|
};
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
2980
|
-
|
|
2981
|
-
|
|
2982
|
-
|
|
2983
|
-
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
2989
|
-
|
|
2990
|
-
|
|
3213
|
+
const keyedDerive = Object.assign(createKeyedDerive, {
|
|
3214
|
+
keys: createKeyedKeys,
|
|
3215
|
+
values: createKeyedValues,
|
|
3216
|
+
entries: createKeyedEntries,
|
|
3217
|
+
get: createKeyedGet,
|
|
3218
|
+
groupBy: createKeyedGroupBy,
|
|
3219
|
+
singleton: createKeyedSingleton,
|
|
3220
|
+
subset: createKeyedSubset,
|
|
3221
|
+
filter: createKeyedFilter,
|
|
3222
|
+
compact: createKeyedCompact
|
|
3223
|
+
});
|
|
3224
|
+
//#endregion
|
|
3225
|
+
//#region core/src/projection/derive.ts
|
|
3226
|
+
function createValueDerive(dependencies, compute, equality = Object.is) {
|
|
3227
|
+
const compiled = require_transform.compileProjectionDependencies(dependencies, "Derive");
|
|
3228
|
+
const names = compiled.names;
|
|
3229
|
+
const ordered = compiled.projections;
|
|
3230
|
+
const [projection] = require_transform.defineProcessor({
|
|
3231
|
+
dependencies: ordered,
|
|
3232
|
+
outputs: [{
|
|
3233
|
+
kind: "value",
|
|
3234
|
+
equality
|
|
3235
|
+
}],
|
|
3236
|
+
create: () => ({ evaluate: (evaluation) => {
|
|
3237
|
+
const output = evaluation.outputs[0];
|
|
3238
|
+
if (output.kind !== "value") throw new Error("derive requires a value output.");
|
|
3239
|
+
const values = Object.create(null);
|
|
3240
|
+
for (let index = 0; index < names.length; index++) values[names[index]] = require_transform.snapshotDependencyValue(evaluation.sources[index]);
|
|
3241
|
+
const next = compute(Object.freeze(values));
|
|
3242
|
+
require_transform.assertSynchronous(next);
|
|
3243
|
+
output.output.set(next);
|
|
3244
|
+
} })
|
|
2991
3245
|
});
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
...change.updated.map((entry) => entry.key),
|
|
2996
|
-
...change.removed.map((entry) => entry.key)
|
|
2997
|
-
]);
|
|
2998
|
-
const collectionHasStructuralChange = (change) => change.kind === "reset" || Boolean(change.added.length || change.removed.length || change.order);
|
|
2999
|
-
const collectionHasAnyChange = (change) => change.kind === "reset" || Boolean(change.added.length || change.updated.length || change.removed.length || change.order);
|
|
3246
|
+
return projection;
|
|
3247
|
+
}
|
|
3248
|
+
const derive = Object.assign(createValueDerive, { keyed: keyedDerive });
|
|
3000
3249
|
//#endregion
|
|
3001
3250
|
//#region core/src/projection/collection/index.ts
|
|
3002
3251
|
const height = (node) => node?.height ?? 0;
|
|
3003
3252
|
const node = (key, value, left, right) => {
|
|
3004
|
-
|
|
3253
|
+
require_transform.profile.collectionIndex.node();
|
|
3005
3254
|
return Object.freeze({
|
|
3006
3255
|
key,
|
|
3007
3256
|
value,
|
|
@@ -3062,7 +3311,7 @@ var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
|
3062
3311
|
}
|
|
3063
3312
|
static from(entries) {
|
|
3064
3313
|
const sorted = [...entries].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
3065
|
-
|
|
3314
|
+
require_transform.profile.collectionIndex.build(sorted.length);
|
|
3066
3315
|
return new PersistentKeyedIndex(fromSorted(sorted));
|
|
3067
3316
|
}
|
|
3068
3317
|
get(key) {
|
|
@@ -3167,15 +3416,15 @@ const createCollectionOutput = (binding) => {
|
|
|
3167
3416
|
reset = initialize;
|
|
3168
3417
|
const read = (next) => ({
|
|
3169
3418
|
get: (key) => {
|
|
3170
|
-
|
|
3419
|
+
require_transform.assertScope(active);
|
|
3171
3420
|
return next ? getNext(key) : published.get(key);
|
|
3172
3421
|
},
|
|
3173
3422
|
has: (key) => {
|
|
3174
|
-
|
|
3423
|
+
require_transform.assertScope(active);
|
|
3175
3424
|
return next ? hasNext(key) : published.has(key);
|
|
3176
3425
|
},
|
|
3177
3426
|
ids: () => {
|
|
3178
|
-
|
|
3427
|
+
require_transform.assertScope(active);
|
|
3179
3428
|
return next ? Object.freeze(explicitOrder ? [...explicitOrder] : [...deriveIds()]) : published.ids();
|
|
3180
3429
|
}
|
|
3181
3430
|
});
|
|
@@ -3184,7 +3433,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3184
3433
|
next: read(true),
|
|
3185
3434
|
output: {
|
|
3186
3435
|
set: (key, value) => {
|
|
3187
|
-
|
|
3436
|
+
require_transform.assertScope(active);
|
|
3188
3437
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
3189
3438
|
staged.set(key, {
|
|
3190
3439
|
present: true,
|
|
@@ -3192,11 +3441,11 @@ const createCollectionOutput = (binding) => {
|
|
|
3192
3441
|
});
|
|
3193
3442
|
},
|
|
3194
3443
|
remove: (key) => {
|
|
3195
|
-
|
|
3444
|
+
require_transform.assertScope(active);
|
|
3196
3445
|
staged.set(key, { present: false });
|
|
3197
3446
|
},
|
|
3198
3447
|
order: (order) => {
|
|
3199
|
-
|
|
3448
|
+
require_transform.assertScope(active);
|
|
3200
3449
|
explicitOrder = Object.freeze([...order]);
|
|
3201
3450
|
}
|
|
3202
3451
|
}
|
|
@@ -3206,18 +3455,39 @@ const createCollectionOutput = (binding) => {
|
|
|
3206
3455
|
if (cleared) {
|
|
3207
3456
|
for (const key of published.keys()) if (!staged.has(key)) staged.set(key, { present: false });
|
|
3208
3457
|
}
|
|
3209
|
-
|
|
3458
|
+
require_transform.profile.projection("touchedKeys", staged.size);
|
|
3210
3459
|
const added = /* @__PURE__ */ new Set();
|
|
3211
3460
|
const removed = /* @__PURE__ */ new Set();
|
|
3212
|
-
const
|
|
3461
|
+
const addedEntries = [];
|
|
3462
|
+
const updatedEntries = [];
|
|
3463
|
+
const removedEntries = [];
|
|
3213
3464
|
for (const [key, entry] of staged) {
|
|
3214
3465
|
const existed = published.has(key);
|
|
3215
3466
|
if (entry.present) {
|
|
3216
|
-
if (!existed)
|
|
3217
|
-
|
|
3467
|
+
if (!existed) {
|
|
3468
|
+
added.add(key);
|
|
3469
|
+
addedEntries.push({
|
|
3470
|
+
key,
|
|
3471
|
+
after: entry.value
|
|
3472
|
+
});
|
|
3473
|
+
continue;
|
|
3474
|
+
}
|
|
3475
|
+
const before = published.get(key);
|
|
3476
|
+
if (!isEqual(before, entry.value)) updatedEntries.push({
|
|
3477
|
+
key,
|
|
3478
|
+
before,
|
|
3479
|
+
after: entry.value
|
|
3480
|
+
});
|
|
3218
3481
|
else if (!nextReset) staged.delete(key);
|
|
3219
|
-
|
|
3220
|
-
|
|
3482
|
+
continue;
|
|
3483
|
+
}
|
|
3484
|
+
if (existed) {
|
|
3485
|
+
removed.add(key);
|
|
3486
|
+
removedEntries.push({
|
|
3487
|
+
key,
|
|
3488
|
+
before: published.get(key)
|
|
3489
|
+
});
|
|
3490
|
+
} else staged.delete(key);
|
|
3221
3491
|
}
|
|
3222
3492
|
const ids = published.ids();
|
|
3223
3493
|
if (explicitOrder || added.size || removed.size) {
|
|
@@ -3225,36 +3495,8 @@ const createCollectionOutput = (binding) => {
|
|
|
3225
3495
|
if (new Set(order).size !== order.length || order.length !== published.size() + added.size - removed.size || order.some((key) => !hasNext(key))) throw new TypeError("Projection order must contain every key exactly once.");
|
|
3226
3496
|
nextIds = ids.length === order.length && ids.every((key, index) => key === order[index]) ? ids : Object.freeze([...order]);
|
|
3227
3497
|
} else nextIds = ids;
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
removed.forEach((key) => changedKeys.add(key));
|
|
3231
|
-
updated.forEach((key) => changedKeys.add(key));
|
|
3232
|
-
require_scheduler.profile.projection("changedKeys", changedKeys.size);
|
|
3233
|
-
const addedEntries = [];
|
|
3234
|
-
const updatedEntries = [];
|
|
3235
|
-
const removedEntries = [];
|
|
3236
|
-
for (const key of changedKeys) {
|
|
3237
|
-
const beforePresent = published.has(key);
|
|
3238
|
-
const afterPresent = hasNext(key);
|
|
3239
|
-
if (beforePresent === afterPresent) {
|
|
3240
|
-
if (!beforePresent) continue;
|
|
3241
|
-
if (isEqual !== Object.is && isEqual(published.get(key), getNext(key))) continue;
|
|
3242
|
-
}
|
|
3243
|
-
if (!beforePresent) addedEntries.push({
|
|
3244
|
-
key,
|
|
3245
|
-
after: getNext(key)
|
|
3246
|
-
});
|
|
3247
|
-
else if (!afterPresent) removedEntries.push({
|
|
3248
|
-
key,
|
|
3249
|
-
before: published.get(key)
|
|
3250
|
-
});
|
|
3251
|
-
else updatedEntries.push({
|
|
3252
|
-
key,
|
|
3253
|
-
before: published.get(key),
|
|
3254
|
-
after: getNext(key)
|
|
3255
|
-
});
|
|
3256
|
-
}
|
|
3257
|
-
change = nextReset ? Object.freeze({ kind: "reset" }) : createCollectionChange({
|
|
3498
|
+
require_transform.profile.projection("changedKeys", addedEntries.length + updatedEntries.length + removedEntries.length);
|
|
3499
|
+
change = nextReset ? Object.freeze({ kind: "reset" }) : require_transform.createCollectionChange({
|
|
3258
3500
|
added: addedEntries,
|
|
3259
3501
|
updated: updatedEntries,
|
|
3260
3502
|
removed: removedEntries,
|
|
@@ -3268,15 +3510,15 @@ const createCollectionOutput = (binding) => {
|
|
|
3268
3510
|
kind: "collection",
|
|
3269
3511
|
read: {
|
|
3270
3512
|
get: (key) => {
|
|
3271
|
-
|
|
3513
|
+
require_transform.assertScope(active);
|
|
3272
3514
|
return getNext(key);
|
|
3273
3515
|
},
|
|
3274
3516
|
has: (key) => {
|
|
3275
|
-
|
|
3517
|
+
require_transform.assertScope(active);
|
|
3276
3518
|
return hasNext(key);
|
|
3277
3519
|
},
|
|
3278
3520
|
ids: () => {
|
|
3279
|
-
|
|
3521
|
+
require_transform.assertScope(active);
|
|
3280
3522
|
return nextIds;
|
|
3281
3523
|
}
|
|
3282
3524
|
},
|
|
@@ -3302,7 +3544,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3302
3544
|
initialized = true;
|
|
3303
3545
|
},
|
|
3304
3546
|
emit: (call) => {
|
|
3305
|
-
Array.from(listeners).forEach((listener) => call(listener));
|
|
3547
|
+
Array.from(listeners).forEach((listener) => call(listener, change));
|
|
3306
3548
|
},
|
|
3307
3549
|
hasConsumers: () => consumers.size > 0,
|
|
3308
3550
|
forEachConsumer: (run) => consumers.forEach(run),
|
|
@@ -3358,12 +3600,12 @@ const createValueOutput = (binding) => {
|
|
|
3358
3600
|
return {
|
|
3359
3601
|
previous: initialized ? value : void 0,
|
|
3360
3602
|
next: () => {
|
|
3361
|
-
|
|
3603
|
+
require_transform.assertScope(active);
|
|
3362
3604
|
if (staged !== unset) return staged;
|
|
3363
3605
|
return initialize ? void 0 : initialized ? value : void 0;
|
|
3364
3606
|
},
|
|
3365
3607
|
output: Object.freeze({ set: (candidate) => {
|
|
3366
|
-
|
|
3608
|
+
require_transform.assertScope(active);
|
|
3367
3609
|
staged = candidate;
|
|
3368
3610
|
} })
|
|
3369
3611
|
};
|
|
@@ -3390,7 +3632,7 @@ const createValueOutput = (binding) => {
|
|
|
3390
3632
|
begin,
|
|
3391
3633
|
seal,
|
|
3392
3634
|
context: (active) => {
|
|
3393
|
-
|
|
3635
|
+
require_transform.assertScope(active);
|
|
3394
3636
|
return Object.freeze({
|
|
3395
3637
|
kind: "value",
|
|
3396
3638
|
value: next,
|
|
@@ -3411,8 +3653,8 @@ const createValueOutput = (binding) => {
|
|
|
3411
3653
|
initialized = true;
|
|
3412
3654
|
},
|
|
3413
3655
|
emit: (call) => {
|
|
3414
|
-
|
|
3415
|
-
Array.from(listeners).forEach((listener) => call(listener));
|
|
3656
|
+
require_transform.profile.materialized.notification();
|
|
3657
|
+
Array.from(listeners).forEach((listener) => call(listener, void 0));
|
|
3416
3658
|
},
|
|
3417
3659
|
hasConsumers: () => consumers.size > 0,
|
|
3418
3660
|
forEachConsumer: (run) => consumers.forEach(run),
|
|
@@ -3450,7 +3692,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3450
3692
|
let instance;
|
|
3451
3693
|
let cause;
|
|
3452
3694
|
const check = () => {
|
|
3453
|
-
if (!scheduler.active || processor.disposed) throw new
|
|
3695
|
+
if (!scheduler.active || processor.disposed) throw new require_transform.ProjectionDisposedError();
|
|
3454
3696
|
if (processor.fault) throw processor.fault;
|
|
3455
3697
|
};
|
|
3456
3698
|
const bound = definition.outputs.map((outputDefinition) => {
|
|
@@ -3504,8 +3746,8 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3504
3746
|
instance = void 0;
|
|
3505
3747
|
}
|
|
3506
3748
|
if (!instance) instance = definition.create();
|
|
3507
|
-
|
|
3508
|
-
|
|
3749
|
+
require_transform.profile.materialized[reset ? "rebuilt" : "updated"]();
|
|
3750
|
+
require_transform.assertSynchronous(instance.evaluate({
|
|
3509
3751
|
reset,
|
|
3510
3752
|
cause: runCause,
|
|
3511
3753
|
sources,
|
|
@@ -3552,7 +3794,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3552
3794
|
try {
|
|
3553
3795
|
scheduler.initialize(() => {
|
|
3554
3796
|
const invalid = dependencies.find((output) => output.owner.fault || output.owner.disposed);
|
|
3555
|
-
if (invalid) throw invalid.owner.fault ?? new
|
|
3797
|
+
if (invalid) throw invalid.owner.fault ?? new require_transform.ProjectionDisposedError();
|
|
3556
3798
|
if (processor.evaluate(true, void 0).length) processor.publish();
|
|
3557
3799
|
processor.clear();
|
|
3558
3800
|
});
|
|
@@ -3567,6 +3809,20 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3567
3809
|
return processor;
|
|
3568
3810
|
};
|
|
3569
3811
|
//#endregion
|
|
3812
|
+
//#region core/src/projection/readable/listeners.ts
|
|
3813
|
+
/** Exhaustively notifies one projection-readable listener snapshot. */
|
|
3814
|
+
const notifyProjectionListeners = (listeners, message) => {
|
|
3815
|
+
const snapshot = [...listeners];
|
|
3816
|
+
const failures = [];
|
|
3817
|
+
for (const listener of snapshot) try {
|
|
3818
|
+
listener();
|
|
3819
|
+
} catch (error) {
|
|
3820
|
+
failures.push(error);
|
|
3821
|
+
}
|
|
3822
|
+
if (failures.length === 1) throw failures[0];
|
|
3823
|
+
if (failures.length) throw new AggregateError(failures, message);
|
|
3824
|
+
};
|
|
3825
|
+
//#endregion
|
|
3570
3826
|
//#region core/src/projection/readable/selection.ts
|
|
3571
3827
|
let activeTracker;
|
|
3572
3828
|
const trackKey = (key) => {
|
|
@@ -3618,10 +3874,9 @@ const trackedMap = (source) => {
|
|
|
3618
3874
|
};
|
|
3619
3875
|
const selectionAffects = (selection, change) => {
|
|
3620
3876
|
if (!change || change.kind === "reset") return true;
|
|
3621
|
-
if (selection.all
|
|
3622
|
-
if (selection.structure && collectionHasStructuralChange(change)) return true;
|
|
3623
|
-
const
|
|
3624
|
-
for (const key of selection.keys) if (changedKeys.has(key)) return true;
|
|
3877
|
+
if (selection.all) return true;
|
|
3878
|
+
if (selection.structure && require_transform.collectionHasStructuralChange(change)) return true;
|
|
3879
|
+
for (const key of require_transform.collectionChange.keys(change)) if (selection.keys.has(key)) return true;
|
|
3625
3880
|
return false;
|
|
3626
3881
|
};
|
|
3627
3882
|
const createDirectReadable = (source) => Object.freeze({
|
|
@@ -3689,7 +3944,7 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
3689
3944
|
if (!change && previousCollection) {
|
|
3690
3945
|
const current = source.current();
|
|
3691
3946
|
if (isMapLike(current)) {
|
|
3692
|
-
change = diffCollection(previousCollection, current);
|
|
3947
|
+
change = require_transform.diffCollection(previousCollection, current);
|
|
3693
3948
|
previousCollection = current;
|
|
3694
3949
|
if (!change) {
|
|
3695
3950
|
sourceRevision = source.revision();
|
|
@@ -3702,7 +3957,7 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
3702
3957
|
updateCollectionSnapshot();
|
|
3703
3958
|
return;
|
|
3704
3959
|
}
|
|
3705
|
-
if (evaluate())
|
|
3960
|
+
if (evaluate()) notifyProjectionListeners(listeners, "Projection selector listeners failed.");
|
|
3706
3961
|
};
|
|
3707
3962
|
const installSource = () => {
|
|
3708
3963
|
if (!unsubscribeSource) unsubscribeSource = source.subscribe(onProjectionChange);
|
|
@@ -3733,7 +3988,7 @@ const hasDocumentDirty = (dirty) => dirty.replace || dirty.order || dirty.treeRo
|
|
|
3733
3988
|
const equivalent = (node, left, right) => node.kind === "field" ? Object.is(left, right) : equalValue(node, left, right);
|
|
3734
3989
|
const replaceValue = (node, previous, current) => equivalent(node, previous, current) ? previous : copyValue(node, current);
|
|
3735
3990
|
const cloneRecord = (value) => {
|
|
3736
|
-
|
|
3991
|
+
require_transform.profile.copy.shallowRecord(Object.getOwnPropertyNames(value).length);
|
|
3737
3992
|
return Object.create(Object.getPrototypeOf(value), Object.getOwnPropertyDescriptors(value));
|
|
3738
3993
|
};
|
|
3739
3994
|
const materializeRecordChild = (node, previous, current, key, dirty) => {
|
|
@@ -4039,7 +4294,7 @@ const mergeCollectionChanges = (selector, pending, changes) => {
|
|
|
4039
4294
|
};
|
|
4040
4295
|
//#endregion
|
|
4041
4296
|
//#region core/src/projection/source/boundary.ts
|
|
4042
|
-
const sourceError = (cause) => new
|
|
4297
|
+
const sourceError = (cause) => new require_transform.ProjectionError("source", cause);
|
|
4043
4298
|
const createValueBoundary = (scheduler, name, initial, equality, prepare, clearSource) => {
|
|
4044
4299
|
let boundary;
|
|
4045
4300
|
let pendingReset = false;
|
|
@@ -4048,7 +4303,7 @@ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearS
|
|
|
4048
4303
|
let recovering = false;
|
|
4049
4304
|
let cleanup;
|
|
4050
4305
|
const check = () => {
|
|
4051
|
-
if (!scheduler.active || boundary.disposed) throw new
|
|
4306
|
+
if (!scheduler.active || boundary.disposed) throw new require_transform.ProjectionDisposedError();
|
|
4052
4307
|
if (boundary.fault) throw boundary.fault;
|
|
4053
4308
|
};
|
|
4054
4309
|
const state = createValueOutput({
|
|
@@ -4155,23 +4410,23 @@ const stageCollectionRead = (state, read, active, options) => {
|
|
|
4155
4410
|
const evaluation = state.begin(active, options.reset);
|
|
4156
4411
|
if (options.reset) {
|
|
4157
4412
|
const ids = read.ids();
|
|
4158
|
-
|
|
4413
|
+
require_transform.profile.collectionView.idsScanned(ids.length);
|
|
4159
4414
|
for (const key of ids) {
|
|
4160
|
-
|
|
4415
|
+
require_transform.profile.collectionView.mapped();
|
|
4161
4416
|
evaluation.output.set(key, read.get(key));
|
|
4162
4417
|
}
|
|
4163
4418
|
evaluation.output.order(ids);
|
|
4164
4419
|
} else {
|
|
4165
4420
|
const previous = state.current();
|
|
4166
|
-
const keys = options.candidates ? [...
|
|
4167
|
-
if (!options.candidates)
|
|
4421
|
+
const keys = options.candidates ? [...options.candidates] : [...new Set([...previous.ids(), ...read.ids()])];
|
|
4422
|
+
if (!options.candidates) require_transform.profile.collectionView.idsScanned(keys.length);
|
|
4168
4423
|
for (const key of keys) if (read.has(key)) {
|
|
4169
|
-
|
|
4424
|
+
require_transform.profile.collectionView.mapped();
|
|
4170
4425
|
evaluation.output.set(key, read.get(key));
|
|
4171
4426
|
} else evaluation.output.remove(key);
|
|
4172
4427
|
if (options.orderMayChange || !options.candidates) {
|
|
4173
4428
|
const ids = read.ids();
|
|
4174
|
-
if (options.candidates)
|
|
4429
|
+
if (options.candidates) require_transform.profile.collectionView.idsScanned(ids.length);
|
|
4175
4430
|
evaluation.output.order(ids);
|
|
4176
4431
|
}
|
|
4177
4432
|
}
|
|
@@ -4188,7 +4443,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
|
|
|
4188
4443
|
const candidates = /* @__PURE__ */ new Set();
|
|
4189
4444
|
let cleanup;
|
|
4190
4445
|
const check = () => {
|
|
4191
|
-
if (!scheduler.active || boundary.disposed) throw new
|
|
4446
|
+
if (!scheduler.active || boundary.disposed) throw new require_transform.ProjectionDisposedError();
|
|
4192
4447
|
if (boundary.fault) throw boundary.fault;
|
|
4193
4448
|
};
|
|
4194
4449
|
const state = createCollectionOutput({
|
|
@@ -4467,7 +4722,7 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
4467
4722
|
const replacement = createDocumentDirty();
|
|
4468
4723
|
if (pending.reset) markDocumentReplace(replacement, []);
|
|
4469
4724
|
const ids = () => {
|
|
4470
|
-
|
|
4725
|
+
require_transform.assertScope(active);
|
|
4471
4726
|
if (treeNodes) return node?.kind === "tree" && require_record.isRecord(value) && require_record.isRecord(value.nodes) ? Object.freeze(Object.keys(value.nodes)) : Object.freeze([]);
|
|
4472
4727
|
if (node?.kind === "map" && require_record.isRecord(value)) return Object.freeze(Object.keys(value));
|
|
4473
4728
|
if (node?.kind === "table" && require_record.isRecord(value) && Array.isArray(value.ids)) return Object.freeze([...value.ids]);
|
|
@@ -4475,7 +4730,7 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
4475
4730
|
return Object.freeze([]);
|
|
4476
4731
|
};
|
|
4477
4732
|
const has = (key) => {
|
|
4478
|
-
|
|
4733
|
+
require_transform.assertScope(active);
|
|
4479
4734
|
if (treeNodes) return Boolean(node?.kind === "tree" && require_record.isRecord(value) && require_record.isRecord(value.nodes) && Object.hasOwn(value.nodes, key));
|
|
4480
4735
|
if (node?.kind === "map" && require_record.isRecord(value)) return Object.hasOwn(value, key);
|
|
4481
4736
|
if (node?.kind === "table" && require_record.isRecord(value) && require_record.isRecord(value.byId)) return Object.hasOwn(value.byId, key);
|
|
@@ -4496,7 +4751,7 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
4496
4751
|
};
|
|
4497
4752
|
return Object.freeze({
|
|
4498
4753
|
get: (key) => {
|
|
4499
|
-
|
|
4754
|
+
require_transform.assertScope(active);
|
|
4500
4755
|
if (!snapshots.has(key)) if (!has(key)) snapshots.set(key, void 0);
|
|
4501
4756
|
else {
|
|
4502
4757
|
const current = raw(key);
|
|
@@ -4843,7 +5098,7 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
4843
5098
|
}
|
|
4844
5099
|
if (source.kind !== "collection-input") throw new Error("Invalid projection input source.");
|
|
4845
5100
|
const values = new Map(source.initial);
|
|
4846
|
-
const boundary = createCollectionBoundary(scheduler, "projection collection input", () =>
|
|
5101
|
+
const boundary = createCollectionBoundary(scheduler, "projection collection input", () => require_transform.mapRead(values), definition.output.equality, () => void 0);
|
|
4847
5102
|
return {
|
|
4848
5103
|
...boundary,
|
|
4849
5104
|
write: {
|
|
@@ -4852,21 +5107,24 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
4852
5107
|
scheduler.assertIdle();
|
|
4853
5108
|
const staged = /* @__PURE__ */ new Map();
|
|
4854
5109
|
const operations = [];
|
|
5110
|
+
let structural = false;
|
|
4855
5111
|
let active = true;
|
|
4856
5112
|
const draft = Object.freeze({
|
|
4857
5113
|
get: (key) => {
|
|
4858
|
-
|
|
5114
|
+
require_transform.assertScope(() => active);
|
|
4859
5115
|
const entry = staged.get(key);
|
|
4860
5116
|
return entry ? entry.present ? entry.value : void 0 : values.get(key);
|
|
4861
5117
|
},
|
|
4862
5118
|
has: (key) => {
|
|
4863
|
-
|
|
5119
|
+
require_transform.assertScope(() => active);
|
|
4864
5120
|
const entry = staged.get(key);
|
|
4865
5121
|
return entry ? entry.present : values.has(key);
|
|
4866
5122
|
},
|
|
4867
5123
|
set: (key, value) => {
|
|
4868
|
-
|
|
5124
|
+
require_transform.assertScope(() => active);
|
|
4869
5125
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
5126
|
+
const previous = staged.get(key);
|
|
5127
|
+
structural ||= previous ? !previous.present : !values.has(key);
|
|
4870
5128
|
staged.set(key, {
|
|
4871
5129
|
present: true,
|
|
4872
5130
|
value
|
|
@@ -4878,46 +5136,42 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
4878
5136
|
});
|
|
4879
5137
|
},
|
|
4880
5138
|
remove: (key) => {
|
|
4881
|
-
|
|
5139
|
+
require_transform.assertScope(() => active);
|
|
4882
5140
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
5141
|
+
const previous = staged.get(key);
|
|
5142
|
+
const present = previous ? previous.present : values.has(key);
|
|
5143
|
+
structural ||= present;
|
|
4883
5144
|
staged.set(key, { present: false });
|
|
4884
|
-
operations.push({
|
|
5145
|
+
if (present) operations.push({
|
|
4885
5146
|
kind: "remove",
|
|
4886
5147
|
key
|
|
4887
5148
|
});
|
|
4888
5149
|
}
|
|
4889
5150
|
});
|
|
4890
5151
|
try {
|
|
4891
|
-
|
|
5152
|
+
require_transform.assertSynchronous(run(draft));
|
|
4892
5153
|
} finally {
|
|
4893
5154
|
active = false;
|
|
4894
5155
|
}
|
|
5156
|
+
if (!operations.length) return;
|
|
4895
5157
|
const candidates = /* @__PURE__ */ new Set();
|
|
4896
|
-
|
|
4897
|
-
const
|
|
4898
|
-
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
value: operation.value
|
|
4909
|
-
});
|
|
4910
|
-
} else {
|
|
4911
|
-
if (!present) continue;
|
|
4912
|
-
structural = true;
|
|
4913
|
-
overlay.set(operation.key, { present: false });
|
|
4914
|
-
}
|
|
4915
|
-
accepted.push(operation);
|
|
4916
|
-
candidates.add(operation.key);
|
|
5158
|
+
const retained = /* @__PURE__ */ new Map();
|
|
5159
|
+
for (const [key, entry] of staged) {
|
|
5160
|
+
const existed = values.has(key);
|
|
5161
|
+
if (entry.present) {
|
|
5162
|
+
if (!existed) {
|
|
5163
|
+
candidates.add(key);
|
|
5164
|
+
continue;
|
|
5165
|
+
}
|
|
5166
|
+
const previous = values.get(key);
|
|
5167
|
+
if (source.equality(previous, entry.value)) retained.set(key, previous);
|
|
5168
|
+
else candidates.add(key);
|
|
5169
|
+
} else if (existed) candidates.add(key);
|
|
4917
5170
|
}
|
|
4918
|
-
if (!
|
|
4919
|
-
for (const operation of
|
|
5171
|
+
if (!candidates.size && !structural) return;
|
|
5172
|
+
for (const operation of operations) if (operation.kind === "set") values.set(operation.key, operation.value);
|
|
4920
5173
|
else values.delete(operation.key);
|
|
5174
|
+
for (const [key, value] of retained) if (values.has(key)) values.set(key, value);
|
|
4921
5175
|
boundary.mark({
|
|
4922
5176
|
candidates,
|
|
4923
5177
|
orderMayChange: structural
|
|
@@ -4960,19 +5214,249 @@ const createSourceRegistry = (scheduler) => {
|
|
|
4960
5214
|
};
|
|
4961
5215
|
};
|
|
4962
5216
|
//#endregion
|
|
5217
|
+
//#region core/src/projection/readable/keyed.ts
|
|
5218
|
+
const sameKeys = (previous, next) => previous.length === next.length && previous.every((key, index) => key === next[index]);
|
|
5219
|
+
/** Runtime-owned keyed consumer family over one materialized collection output. */
|
|
5220
|
+
const createProjectionItems = (source, checkOwner) => {
|
|
5221
|
+
if (source.kind !== "collection") throw new TypeError("Projection items require a keyed collection projection.");
|
|
5222
|
+
let disposed = false;
|
|
5223
|
+
const active = /* @__PURE__ */ new Map();
|
|
5224
|
+
const followers = /* @__PURE__ */ new Map();
|
|
5225
|
+
const waiting = /* @__PURE__ */ new Map();
|
|
5226
|
+
let keysValue = Object.freeze([...source.current().keys()]);
|
|
5227
|
+
let keysRevision = 0;
|
|
5228
|
+
const keyListeners = /* @__PURE__ */ new Set();
|
|
5229
|
+
const check = () => {
|
|
5230
|
+
if (disposed) throw new require_transform.ProjectionDisposedError();
|
|
5231
|
+
checkOwner();
|
|
5232
|
+
};
|
|
5233
|
+
const notifications = [];
|
|
5234
|
+
const notify = (listeners) => {
|
|
5235
|
+
for (const listener of listeners) notifications.push(listener);
|
|
5236
|
+
};
|
|
5237
|
+
const flushNotifications = () => {
|
|
5238
|
+
notifyProjectionListeners(notifications.splice(0), "Projection item listeners failed.");
|
|
5239
|
+
};
|
|
5240
|
+
const unregisterWaiting = (state) => {
|
|
5241
|
+
const states = waiting.get(state.key);
|
|
5242
|
+
states?.delete(state);
|
|
5243
|
+
if (states?.size === 0) waiting.delete(state.key);
|
|
5244
|
+
};
|
|
5245
|
+
const registerWaiting = (state) => {
|
|
5246
|
+
if (state.tracked || state.ended) return;
|
|
5247
|
+
const states = waiting.get(state.key);
|
|
5248
|
+
if (states) states.add(state);
|
|
5249
|
+
else waiting.set(state.key, new Set([state]));
|
|
5250
|
+
};
|
|
5251
|
+
const publishState = (state, value) => {
|
|
5252
|
+
if (state.ended) return;
|
|
5253
|
+
if (Object.is(state.value, value)) {
|
|
5254
|
+
state.value = value;
|
|
5255
|
+
return;
|
|
5256
|
+
}
|
|
5257
|
+
state.value = value;
|
|
5258
|
+
state.revision++;
|
|
5259
|
+
notify(state.listeners);
|
|
5260
|
+
};
|
|
5261
|
+
const attachActive = (state, value) => {
|
|
5262
|
+
if (state.ended) return;
|
|
5263
|
+
unregisterWaiting(state);
|
|
5264
|
+
state.tracked = true;
|
|
5265
|
+
const canonical = active.get(state.key);
|
|
5266
|
+
if (!canonical) active.set(state.key, state);
|
|
5267
|
+
else if (canonical !== state) {
|
|
5268
|
+
const states = followers.get(state.key);
|
|
5269
|
+
if (states) states.add(state);
|
|
5270
|
+
else followers.set(state.key, new Set([state]));
|
|
5271
|
+
}
|
|
5272
|
+
publishState(state, value);
|
|
5273
|
+
};
|
|
5274
|
+
const terminateState = (state) => {
|
|
5275
|
+
if (state.ended) return;
|
|
5276
|
+
unregisterWaiting(state);
|
|
5277
|
+
state.tracked = false;
|
|
5278
|
+
publishState(state, void 0);
|
|
5279
|
+
state.ended = true;
|
|
5280
|
+
};
|
|
5281
|
+
const statesFor = (key) => {
|
|
5282
|
+
const result = [];
|
|
5283
|
+
const canonical = active.get(key);
|
|
5284
|
+
if (canonical) result.push(canonical);
|
|
5285
|
+
const extra = followers.get(key);
|
|
5286
|
+
if (extra) result.push(...extra);
|
|
5287
|
+
return result;
|
|
5288
|
+
};
|
|
5289
|
+
const terminateKey = (key) => {
|
|
5290
|
+
for (const state of statesFor(key)) terminateState(state);
|
|
5291
|
+
active.delete(key);
|
|
5292
|
+
followers.delete(key);
|
|
5293
|
+
};
|
|
5294
|
+
const activateWaiting = (key, value) => {
|
|
5295
|
+
const states = waiting.get(key);
|
|
5296
|
+
if (!states?.size) return;
|
|
5297
|
+
waiting.delete(key);
|
|
5298
|
+
for (const state of states) attachActive(state, value);
|
|
5299
|
+
};
|
|
5300
|
+
const updateKey = (key, value) => {
|
|
5301
|
+
for (const state of statesFor(key)) publishState(state, value);
|
|
5302
|
+
};
|
|
5303
|
+
const updateKeys = (current) => {
|
|
5304
|
+
const next = Object.freeze([...current.keys()]);
|
|
5305
|
+
if (sameKeys(keysValue, next)) return;
|
|
5306
|
+
keysValue = next;
|
|
5307
|
+
keysRevision++;
|
|
5308
|
+
notify(keyListeners);
|
|
5309
|
+
};
|
|
5310
|
+
const createItem = (key, initial) => {
|
|
5311
|
+
const state = {
|
|
5312
|
+
key,
|
|
5313
|
+
listeners: /* @__PURE__ */ new Set(),
|
|
5314
|
+
tracked: initial !== void 0,
|
|
5315
|
+
ended: false,
|
|
5316
|
+
value: initial?.value,
|
|
5317
|
+
revision: 0
|
|
5318
|
+
};
|
|
5319
|
+
const ensureObserved = () => {
|
|
5320
|
+
if (state.ended || state.tracked) return;
|
|
5321
|
+
const current = source.current();
|
|
5322
|
+
if (current.has(key)) attachActive(state, current.get(key));
|
|
5323
|
+
};
|
|
5324
|
+
state.readable = Object.freeze({
|
|
5325
|
+
current: () => {
|
|
5326
|
+
check();
|
|
5327
|
+
ensureObserved();
|
|
5328
|
+
return state.value;
|
|
5329
|
+
},
|
|
5330
|
+
revision: () => {
|
|
5331
|
+
check();
|
|
5332
|
+
ensureObserved();
|
|
5333
|
+
return state.revision;
|
|
5334
|
+
},
|
|
5335
|
+
subscribe: (listener) => {
|
|
5336
|
+
check();
|
|
5337
|
+
ensureObserved();
|
|
5338
|
+
state.listeners.add(listener);
|
|
5339
|
+
if (!state.tracked && !state.ended) registerWaiting(state);
|
|
5340
|
+
return () => {
|
|
5341
|
+
state.listeners.delete(listener);
|
|
5342
|
+
if (!state.listeners.size && !state.tracked) unregisterWaiting(state);
|
|
5343
|
+
};
|
|
5344
|
+
}
|
|
5345
|
+
});
|
|
5346
|
+
return state;
|
|
5347
|
+
};
|
|
5348
|
+
const reconcile = (current) => {
|
|
5349
|
+
for (const key of [...active.keys()]) if (!current.has(key)) terminateKey(key);
|
|
5350
|
+
else updateKey(key, current.get(key));
|
|
5351
|
+
for (const key of [...waiting.keys()]) if (current.has(key)) activateWaiting(key, current.get(key));
|
|
5352
|
+
updateKeys(current);
|
|
5353
|
+
};
|
|
5354
|
+
const onChange = (change) => {
|
|
5355
|
+
check();
|
|
5356
|
+
const current = source.current();
|
|
5357
|
+
if (!change || change.kind === "reset") {
|
|
5358
|
+
reconcile(current);
|
|
5359
|
+
flushNotifications();
|
|
5360
|
+
return;
|
|
5361
|
+
}
|
|
5362
|
+
for (const entry of change.removed) terminateKey(entry.key);
|
|
5363
|
+
for (const entry of change.added) activateWaiting(entry.key, entry.after);
|
|
5364
|
+
for (const entry of change.updated) updateKey(entry.key, entry.after);
|
|
5365
|
+
if (change.added.length || change.removed.length || change.order) updateKeys(current);
|
|
5366
|
+
flushNotifications();
|
|
5367
|
+
};
|
|
5368
|
+
const stop = source.subscribe(onChange);
|
|
5369
|
+
const keys = Object.freeze({
|
|
5370
|
+
current: () => {
|
|
5371
|
+
check();
|
|
5372
|
+
return keysValue;
|
|
5373
|
+
},
|
|
5374
|
+
revision: () => {
|
|
5375
|
+
check();
|
|
5376
|
+
return keysRevision;
|
|
5377
|
+
},
|
|
5378
|
+
subscribe: (listener) => {
|
|
5379
|
+
check();
|
|
5380
|
+
keyListeners.add(listener);
|
|
5381
|
+
return () => keyListeners.delete(listener);
|
|
5382
|
+
}
|
|
5383
|
+
});
|
|
5384
|
+
return {
|
|
5385
|
+
items: Object.freeze({
|
|
5386
|
+
keys,
|
|
5387
|
+
get: (key) => {
|
|
5388
|
+
check();
|
|
5389
|
+
const canonical = active.get(key);
|
|
5390
|
+
if (canonical) return canonical.readable;
|
|
5391
|
+
const pending = waiting.get(key)?.values().next().value;
|
|
5392
|
+
if (pending) return pending.readable;
|
|
5393
|
+
const current = source.current();
|
|
5394
|
+
if (current.has(key)) {
|
|
5395
|
+
const state = createItem(key, {
|
|
5396
|
+
present: true,
|
|
5397
|
+
value: current.get(key)
|
|
5398
|
+
});
|
|
5399
|
+
active.set(key, state);
|
|
5400
|
+
return state.readable;
|
|
5401
|
+
}
|
|
5402
|
+
return createItem(key).readable;
|
|
5403
|
+
}
|
|
5404
|
+
}),
|
|
5405
|
+
dispose() {
|
|
5406
|
+
if (disposed) return;
|
|
5407
|
+
disposed = true;
|
|
5408
|
+
stop();
|
|
5409
|
+
keyListeners.clear();
|
|
5410
|
+
const known = /* @__PURE__ */ new Set();
|
|
5411
|
+
active.forEach((state) => known.add(state));
|
|
5412
|
+
followers.forEach((states) => states.forEach((state) => known.add(state)));
|
|
5413
|
+
waiting.forEach((states) => states.forEach((state) => known.add(state)));
|
|
5414
|
+
known.forEach((state) => state.listeners.clear());
|
|
5415
|
+
active.clear();
|
|
5416
|
+
followers.clear();
|
|
5417
|
+
waiting.clear();
|
|
5418
|
+
notifications.length = 0;
|
|
5419
|
+
}
|
|
5420
|
+
};
|
|
5421
|
+
};
|
|
5422
|
+
//#endregion
|
|
4963
5423
|
//#region core/src/projection/runtime.ts
|
|
4964
5424
|
const createProjectionRuntime = (options) => {
|
|
4965
|
-
const scheduler =
|
|
5425
|
+
const scheduler = require_transform.createScheduler(options?.onError ?? (() => void 0));
|
|
4966
5426
|
const sources = createSourceRegistry(scheduler);
|
|
4967
5427
|
const materialized = /* @__PURE__ */ new WeakMap();
|
|
4968
5428
|
const publicViews = /* @__PURE__ */ new WeakMap();
|
|
4969
5429
|
const scopes = /* @__PURE__ */ new Set();
|
|
5430
|
+
const rootItems = /* @__PURE__ */ new Map();
|
|
5431
|
+
const disposeItemControllers = (registry, failures) => {
|
|
5432
|
+
const controllers = [...registry.values()];
|
|
5433
|
+
registry.clear();
|
|
5434
|
+
for (const controller of controllers) try {
|
|
5435
|
+
controller.dispose();
|
|
5436
|
+
} catch (error) {
|
|
5437
|
+
failures.push(error);
|
|
5438
|
+
}
|
|
5439
|
+
};
|
|
5440
|
+
const disposeScopeConsumers = (state, failures) => {
|
|
5441
|
+
disposeItemControllers(state.items, failures);
|
|
5442
|
+
const subscriptions = [...state.subscriptions];
|
|
5443
|
+
state.subscriptions.clear();
|
|
5444
|
+
for (const unsubscribe of subscriptions) try {
|
|
5445
|
+
unsubscribe();
|
|
5446
|
+
} catch (error) {
|
|
5447
|
+
failures.push(error);
|
|
5448
|
+
}
|
|
5449
|
+
};
|
|
5450
|
+
const throwCleanupFailures = (failures, message) => {
|
|
5451
|
+
if (failures.length === 1) throw failures[0];
|
|
5452
|
+
if (failures.length) throw new AggregateError(failures, message);
|
|
5453
|
+
};
|
|
4970
5454
|
const assertAccess = (definition, requester) => {
|
|
4971
5455
|
scheduler.assertActive();
|
|
4972
5456
|
const owner = definition.owner;
|
|
4973
5457
|
if (owner === void 0) return;
|
|
4974
5458
|
if (owner !== requester) throw new TypeError("Projection belongs to another scope.");
|
|
4975
|
-
if (!requester?.active) throw new
|
|
5459
|
+
if (!requester?.active) throw new require_transform.ProjectionDisposedError();
|
|
4976
5460
|
};
|
|
4977
5461
|
const rememberScoped = (definition, requester) => {
|
|
4978
5462
|
if (requester && definition.owner === requester) requester.materialized.push(definition);
|
|
@@ -4990,7 +5474,7 @@ const createProjectionRuntime = (options) => {
|
|
|
4990
5474
|
...source.write ? { write: source.write } : {}
|
|
4991
5475
|
});
|
|
4992
5476
|
} else {
|
|
4993
|
-
if (definition.owner === void 0 && definition.dependencies.some((dependency) =>
|
|
5477
|
+
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.");
|
|
4994
5478
|
const dependencies = definition.dependencies.map((dependency) => resolveOutput(dependency, requester));
|
|
4995
5479
|
const processor = createProcessor(scheduler, definition, Object.freeze(dependencies));
|
|
4996
5480
|
result = Object.freeze({
|
|
@@ -5004,14 +5488,14 @@ const createProjectionRuntime = (options) => {
|
|
|
5004
5488
|
return result;
|
|
5005
5489
|
};
|
|
5006
5490
|
const resolveOutput = (projection, requester) => {
|
|
5007
|
-
const ref =
|
|
5491
|
+
const ref = require_transform.projectionRef(projection);
|
|
5008
5492
|
const output = materializeProducer(ref.producer, requester).outputs[ref.output];
|
|
5009
5493
|
if (!output) throw new TypeError("Projection output does not exist.");
|
|
5010
5494
|
return output;
|
|
5011
5495
|
};
|
|
5012
5496
|
const assertOutput = (output) => {
|
|
5013
5497
|
scheduler.assertActive();
|
|
5014
|
-
if (output.owner.disposed) throw new
|
|
5498
|
+
if (output.owner.disposed) throw new require_transform.ProjectionDisposedError();
|
|
5015
5499
|
if (output.owner.fault) throw output.owner.fault;
|
|
5016
5500
|
};
|
|
5017
5501
|
const publicCurrent = (output) => {
|
|
@@ -5021,7 +5505,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5021
5505
|
const cached = publicViews.get(output);
|
|
5022
5506
|
if (cached?.revision === revision) return cached.value;
|
|
5023
5507
|
if (output.kind === "collection") {
|
|
5024
|
-
const value =
|
|
5508
|
+
const value = require_transform.collectionView(current);
|
|
5025
5509
|
publicViews.set(output, {
|
|
5026
5510
|
revision,
|
|
5027
5511
|
value
|
|
@@ -5029,7 +5513,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5029
5513
|
return value;
|
|
5030
5514
|
}
|
|
5031
5515
|
if (isMapLike(current)) {
|
|
5032
|
-
const value =
|
|
5516
|
+
const value = require_transform.snapshotCollectionView(require_transform.mapRead(current));
|
|
5033
5517
|
publicViews.set(output, {
|
|
5034
5518
|
revision,
|
|
5035
5519
|
value
|
|
@@ -5052,6 +5536,19 @@ const createProjectionRuntime = (options) => {
|
|
|
5052
5536
|
subscribe: output.subscribe
|
|
5053
5537
|
});
|
|
5054
5538
|
};
|
|
5539
|
+
const projectionItems = (projection, requester) => {
|
|
5540
|
+
const output = resolveOutput(projection, requester);
|
|
5541
|
+
if (output.kind !== "collection") throw new TypeError("Projection items require a keyed collection projection.");
|
|
5542
|
+
const registry = requester?.items ?? rootItems;
|
|
5543
|
+
const existing = registry.get(output);
|
|
5544
|
+
if (existing) return existing.items;
|
|
5545
|
+
const controller = createProjectionItems(readableSource(projection, requester), () => {
|
|
5546
|
+
if (requester && !requester.active) throw new require_transform.ProjectionDisposedError();
|
|
5547
|
+
assertOutput(output);
|
|
5548
|
+
});
|
|
5549
|
+
registry.set(output, controller);
|
|
5550
|
+
return controller.items;
|
|
5551
|
+
};
|
|
5055
5552
|
const makeReadable = (projection, selector, equality, requester) => {
|
|
5056
5553
|
const source = readableSource(projection, requester);
|
|
5057
5554
|
return selector ? createSelectorReadable(source, selector, equality) : createDirectReadable(source);
|
|
@@ -5060,7 +5557,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5060
5557
|
return makeReadable(projection, selector, equality);
|
|
5061
5558
|
}
|
|
5062
5559
|
const inputWrite = (target, requester) => {
|
|
5063
|
-
const ref =
|
|
5560
|
+
const ref = require_transform.projectionRef(target);
|
|
5064
5561
|
if (ref.output !== 0 || ref.producer.kind !== "source") throw new TypeError("Projection is not an input.");
|
|
5065
5562
|
if (ref.producer.source.kind !== "value-input" && ref.producer.source.kind !== "collection-input") throw new TypeError("Projection is not an input.");
|
|
5066
5563
|
const write = materializeProducer(ref.producer, requester).write;
|
|
@@ -5085,13 +5582,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5085
5582
|
scheduler.assertIdle();
|
|
5086
5583
|
state.active = false;
|
|
5087
5584
|
const failures = [];
|
|
5088
|
-
|
|
5089
|
-
state.subscriptions.clear();
|
|
5090
|
-
for (const unsubscribe of subscriptions) try {
|
|
5091
|
-
unsubscribe();
|
|
5092
|
-
} catch (error) {
|
|
5093
|
-
failures.push(error);
|
|
5094
|
-
}
|
|
5585
|
+
disposeScopeConsumers(state, failures);
|
|
5095
5586
|
for (let index = state.materialized.length - 1; index >= 0; index--) {
|
|
5096
5587
|
const definition = state.materialized[index];
|
|
5097
5588
|
const current = materialized.get(definition);
|
|
@@ -5106,25 +5597,26 @@ const createProjectionRuntime = (options) => {
|
|
|
5106
5597
|
}
|
|
5107
5598
|
state.materialized.length = 0;
|
|
5108
5599
|
scopes.delete(state);
|
|
5109
|
-
|
|
5600
|
+
throwCleanupFailures(failures, "Projection scope cleanup failed.");
|
|
5110
5601
|
};
|
|
5111
5602
|
const createScope = () => {
|
|
5112
5603
|
scheduler.assertIdle();
|
|
5113
5604
|
const state = {
|
|
5114
5605
|
active: true,
|
|
5115
5606
|
materialized: [],
|
|
5116
|
-
subscriptions: /* @__PURE__ */ new Set()
|
|
5607
|
+
subscriptions: /* @__PURE__ */ new Set(),
|
|
5608
|
+
items: /* @__PURE__ */ new Map()
|
|
5117
5609
|
};
|
|
5118
5610
|
scopes.add(state);
|
|
5119
5611
|
const assertActive = () => {
|
|
5120
|
-
if (!state.active) throw new
|
|
5612
|
+
if (!state.active) throw new require_transform.ProjectionDisposedError();
|
|
5121
5613
|
scheduler.assertActive();
|
|
5122
5614
|
};
|
|
5123
5615
|
function own(target) {
|
|
5124
5616
|
assertActive();
|
|
5125
5617
|
const projections = [];
|
|
5126
5618
|
const collect = (value) => {
|
|
5127
|
-
if (
|
|
5619
|
+
if (require_transform.isProjection(value)) {
|
|
5128
5620
|
projections.push(value);
|
|
5129
5621
|
return;
|
|
5130
5622
|
}
|
|
@@ -5134,13 +5626,13 @@ const createProjectionRuntime = (options) => {
|
|
|
5134
5626
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
5135
5627
|
if (!descriptor?.enumerable || !("value" in descriptor)) throw new TypeError("Projection ownership tree must use enumerable data properties.");
|
|
5136
5628
|
const child = descriptor.value;
|
|
5137
|
-
if (!
|
|
5629
|
+
if (!require_transform.isProjection(child) && !require_record.isPlainObject(child)) throw new TypeError("Projection ownership tree leaves must be projections.");
|
|
5138
5630
|
collect(child);
|
|
5139
5631
|
}
|
|
5140
5632
|
};
|
|
5141
5633
|
collect(target);
|
|
5142
5634
|
if (!projections.length) throw new TypeError("Projection ownership tree must contain a projection.");
|
|
5143
|
-
|
|
5635
|
+
require_transform.ownProjections(projections, state);
|
|
5144
5636
|
return target;
|
|
5145
5637
|
}
|
|
5146
5638
|
function scopeSelect(projection, selector, equality) {
|
|
@@ -5185,6 +5677,10 @@ const createProjectionRuntime = (options) => {
|
|
|
5185
5677
|
return readProjection(projection, state);
|
|
5186
5678
|
},
|
|
5187
5679
|
select: scopeSelect,
|
|
5680
|
+
items: (projection) => {
|
|
5681
|
+
assertActive();
|
|
5682
|
+
return projectionItems(projection, state);
|
|
5683
|
+
},
|
|
5188
5684
|
update: scopeUpdate,
|
|
5189
5685
|
batch: scopeBatch,
|
|
5190
5686
|
dispose: () => disposeScope(state)
|
|
@@ -5193,6 +5689,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5193
5689
|
return Object.freeze({
|
|
5194
5690
|
read: readProjection,
|
|
5195
5691
|
select: selectProjection,
|
|
5692
|
+
items: projectionItems,
|
|
5196
5693
|
update,
|
|
5197
5694
|
batch,
|
|
5198
5695
|
scope: createScope,
|
|
@@ -5202,16 +5699,11 @@ const createProjectionRuntime = (options) => {
|
|
|
5202
5699
|
const failures = [];
|
|
5203
5700
|
for (const scope of scopes) {
|
|
5204
5701
|
scope.active = false;
|
|
5205
|
-
|
|
5206
|
-
scope.subscriptions.clear();
|
|
5207
|
-
for (const unsubscribe of subscriptions) try {
|
|
5208
|
-
unsubscribe();
|
|
5209
|
-
} catch (error) {
|
|
5210
|
-
failures.push(error);
|
|
5211
|
-
}
|
|
5702
|
+
disposeScopeConsumers(scope, failures);
|
|
5212
5703
|
scope.materialized.length = 0;
|
|
5213
5704
|
}
|
|
5214
5705
|
scopes.clear();
|
|
5706
|
+
disposeItemControllers(rootItems, failures);
|
|
5215
5707
|
try {
|
|
5216
5708
|
sources.dispose();
|
|
5217
5709
|
} catch (error) {
|
|
@@ -5222,8 +5714,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5222
5714
|
} catch (error) {
|
|
5223
5715
|
failures.push(error);
|
|
5224
5716
|
}
|
|
5225
|
-
|
|
5226
|
-
if (failures.length) throw new AggregateError(failures, "Projection cleanup failed.");
|
|
5717
|
+
throwCleanupFailures(failures, "Projection cleanup failed.");
|
|
5227
5718
|
}
|
|
5228
5719
|
});
|
|
5229
5720
|
};
|
|
@@ -5231,8 +5722,8 @@ const createProjectionRuntime = (options) => {
|
|
|
5231
5722
|
exports.DocumentDisposedError = DocumentDisposedError;
|
|
5232
5723
|
exports.DocumentReentrancyError = DocumentReentrancyError;
|
|
5233
5724
|
exports.ParseError = ParseError;
|
|
5234
|
-
exports.ProjectionDisposedError =
|
|
5235
|
-
exports.ProjectionError =
|
|
5725
|
+
exports.ProjectionDisposedError = require_transform.ProjectionDisposedError;
|
|
5726
|
+
exports.ProjectionError = require_transform.ProjectionError;
|
|
5236
5727
|
exports.TransactionRejected = TransactionRejected;
|
|
5237
5728
|
exports.createDocument = createDocument;
|
|
5238
5729
|
exports.createProjectionRuntime = createProjectionRuntime;
|