doxum 0.1.27 → 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 +66 -3
- package/dist/advanced.cjs +40 -14
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +27 -2
- package/dist/advanced.d.ts +27 -2
- package/dist/advanced.js +27 -1
- package/dist/advanced.js.map +1 -1
- package/dist/{definition-xgk3YNBp.d.ts → dependency-B3E65M1E.d.cts} +27 -3
- package/dist/{definition-CDOuvb5C.d.cts → dependency-BYbdDHNa.d.ts} +27 -3
- package/dist/driver-B5vdPohL.js.map +1 -1
- package/dist/driver-BL91gn7_.cjs.map +1 -1
- package/dist/index.cjs +663 -344
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -32
- package/dist/index.d.ts +23 -32
- package/dist/index.js +549 -230
- 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/{scheduler-vzf3OcjT.js → transform-B4T3AcJr.js} +356 -11
- package/dist/transform-B4T3AcJr.js.map +1 -0
- package/dist/{scheduler-k1ZGR1PG.cjs → transform-DUleAsG0.cjs} +393 -12
- package/dist/transform-DUleAsG0.cjs.map +1 -0
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +11 -5
- package/skills/doxum-runtime/references/api.en.md +40 -21
- package/skills/doxum-runtime/references/api.zh-CN.md +21 -2
- package/skills/doxum-runtime/references/guide.en.md +2 -1
- package/skills/doxum-runtime/references/guide.zh-CN.md +2 -1
- package/skills/doxum-runtime/references/invariants.en.md +2 -2
- package/skills/doxum-runtime/references/invariants.zh-CN.md +2 -2
- package/skills/doxum-runtime/references/patterns.en.md +1 -1
- package/skills/doxum-runtime/references/patterns.zh-CN.md +1 -1
- package/skills/doxum-runtime/references/projections.en.md +47 -3
- package/skills/doxum-runtime/references/projections.zh-CN.md +45 -3
- package/dist/scheduler-k1ZGR1PG.cjs.map +0 -1
- package/dist/scheduler-vzf3OcjT.js.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
|
|
@@ -2736,41 +2736,6 @@ const isExternalSource = (source) => {
|
|
|
2736
2736
|
};
|
|
2737
2737
|
//#endregion
|
|
2738
2738
|
//#region core/src/projection/derive/keyed.ts
|
|
2739
|
-
const outputKind = (projection) => {
|
|
2740
|
-
return require_scheduler.outputDefinitionOf(projection).kind;
|
|
2741
|
-
};
|
|
2742
|
-
const assertKeyedProjection = (projection, role) => {
|
|
2743
|
-
if (outputKind(projection) !== "collection") throw new TypeError(`${role} must be a keyed collection projection.`);
|
|
2744
|
-
};
|
|
2745
|
-
const isKeyedDependency = (value) => {
|
|
2746
|
-
if (!value || typeof value !== "object" || require_scheduler.isProjection(value)) return false;
|
|
2747
|
-
const candidate = value;
|
|
2748
|
-
return require_scheduler.isProjection(candidate.source) && typeof candidate.key === "function";
|
|
2749
|
-
};
|
|
2750
|
-
const bindKey = (index, outputKey, sourceKey) => {
|
|
2751
|
-
const previous = index.forward.get(outputKey);
|
|
2752
|
-
if (previous === sourceKey) return;
|
|
2753
|
-
if (previous !== void 0) {
|
|
2754
|
-
const dependents = index.reverse.get(previous);
|
|
2755
|
-
dependents?.delete(outputKey);
|
|
2756
|
-
if (dependents?.size === 0) index.reverse.delete(previous);
|
|
2757
|
-
index.forward.delete(outputKey);
|
|
2758
|
-
}
|
|
2759
|
-
if (sourceKey === void 0) return;
|
|
2760
|
-
index.forward.set(outputKey, sourceKey);
|
|
2761
|
-
const dependents = index.reverse.get(sourceKey);
|
|
2762
|
-
if (dependents) dependents.add(outputKey);
|
|
2763
|
-
else index.reverse.set(sourceKey, new Set([outputKey]));
|
|
2764
|
-
};
|
|
2765
|
-
const unbindKey = (indexes, outputKey) => {
|
|
2766
|
-
for (const index of indexes) if (index) bindKey(index, outputKey, void 0);
|
|
2767
|
-
};
|
|
2768
|
-
const createBindingIndex = () => ({
|
|
2769
|
-
forward: /* @__PURE__ */ new Map(),
|
|
2770
|
-
reverse: /* @__PURE__ */ new Map()
|
|
2771
|
-
});
|
|
2772
|
-
const emptyKeyedDependencies = Object.freeze(Object.create(null));
|
|
2773
|
-
const absent = Symbol("keyed-transform-absent");
|
|
2774
2739
|
const compileOrderedKeys = (value, role) => {
|
|
2775
2740
|
if (!Array.isArray(value)) throw new TypeError(`${role} must be an array of string keys.`);
|
|
2776
2741
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2784,164 +2749,22 @@ const compileOrderedKeys = (value, role) => {
|
|
|
2784
2749
|
requested: seen
|
|
2785
2750
|
};
|
|
2786
2751
|
};
|
|
2787
|
-
const createKeyedTransform = (source, dependencySpecs, select, equality, name) => {
|
|
2788
|
-
assertKeyedProjection(source, "Keyed derive source");
|
|
2789
|
-
const dependencies = [source];
|
|
2790
|
-
const dependencyNames = [];
|
|
2791
|
-
const keyed = [];
|
|
2792
|
-
if (dependencySpecs !== void 0) {
|
|
2793
|
-
if (!require_record.isPlainObject(dependencySpecs) || require_scheduler.isProjection(dependencySpecs)) throw new TypeError("Keyed derive dependencies must be a plain object.");
|
|
2794
|
-
for (const name of Reflect.ownKeys(dependencySpecs)) {
|
|
2795
|
-
if (typeof name !== "string") throw new TypeError("Keyed derive dependency names must be strings.");
|
|
2796
|
-
const descriptor = Object.getOwnPropertyDescriptor(dependencySpecs, name);
|
|
2797
|
-
if (!descriptor?.enumerable || !("value" in descriptor)) throw new TypeError("Keyed derive dependencies must be enumerable data properties.");
|
|
2798
|
-
const dependency = descriptor.value;
|
|
2799
|
-
dependencyNames.push(name);
|
|
2800
|
-
if (require_scheduler.isProjection(dependency)) {
|
|
2801
|
-
dependencies.push(dependency);
|
|
2802
|
-
keyed.push(void 0);
|
|
2803
|
-
continue;
|
|
2804
|
-
}
|
|
2805
|
-
if (!isKeyedDependency(dependency)) throw new TypeError("Keyed derive dependencies must be projections or keyed lookups.");
|
|
2806
|
-
assertKeyedProjection(dependency.source, "Dynamic keyed dependency source");
|
|
2807
|
-
dependencies.push(dependency.source);
|
|
2808
|
-
keyed.push(dependency);
|
|
2809
|
-
}
|
|
2810
|
-
}
|
|
2811
|
-
const [projection] = require_scheduler.defineProcessor({
|
|
2812
|
-
dependencies,
|
|
2813
|
-
outputs: [{
|
|
2814
|
-
kind: "collection",
|
|
2815
|
-
equality: equality ?? Object.is
|
|
2816
|
-
}],
|
|
2817
|
-
create: () => {
|
|
2818
|
-
const bindings = keyed.map((dependency) => dependency ? createBindingIndex() : void 0);
|
|
2819
|
-
const globalValues = keyed.map(() => void 0);
|
|
2820
|
-
const revisions = [];
|
|
2821
|
-
const clearBindings = () => {
|
|
2822
|
-
for (const index of bindings) {
|
|
2823
|
-
index?.forward.clear();
|
|
2824
|
-
index?.reverse.clear();
|
|
2825
|
-
}
|
|
2826
|
-
};
|
|
2827
|
-
const rememberRevisions = (sources) => {
|
|
2828
|
-
revisions.length = sources.length;
|
|
2829
|
-
for (let index = 0; index < sources.length; index++) revisions[index] = sources[index].revision;
|
|
2830
|
-
};
|
|
2831
|
-
return {
|
|
2832
|
-
evaluate: (evaluation) => {
|
|
2833
|
-
const driver = evaluation.sources[0];
|
|
2834
|
-
const output = evaluation.outputs[0];
|
|
2835
|
-
if (driver.kind !== "collection" || output.kind !== "collection") throw new Error("Keyed derive requires collection input and output.");
|
|
2836
|
-
for (let index = 0; index < keyed.length; index++) {
|
|
2837
|
-
if (keyed[index]) continue;
|
|
2838
|
-
const dependencySource = evaluation.sources[index + 1];
|
|
2839
|
-
if (evaluation.reset || revisions[index + 1] !== dependencySource.revision) globalValues[index] = require_scheduler.snapshotDependencyValue(dependencySource);
|
|
2840
|
-
}
|
|
2841
|
-
const project = (key) => {
|
|
2842
|
-
if (!driver.read.has(key)) return;
|
|
2843
|
-
const value = driver.read.get(key);
|
|
2844
|
-
let dependencyValues = emptyKeyedDependencies;
|
|
2845
|
-
if (keyed.length) {
|
|
2846
|
-
const values = Object.create(null);
|
|
2847
|
-
for (let index = 0; index < keyed.length; index++) {
|
|
2848
|
-
const dependency = keyed[index];
|
|
2849
|
-
if (!dependency) {
|
|
2850
|
-
values[dependencyNames[index]] = globalValues[index];
|
|
2851
|
-
continue;
|
|
2852
|
-
}
|
|
2853
|
-
const sourceContext = evaluation.sources[index + 1];
|
|
2854
|
-
if (sourceContext.kind !== "collection") throw new TypeError("Dynamic keyed dependency resolved to a non-collection source.");
|
|
2855
|
-
const selectedKey = dependency.key(value, key);
|
|
2856
|
-
require_scheduler.assertSynchronous(selectedKey);
|
|
2857
|
-
if (selectedKey !== void 0 && typeof selectedKey !== "string") throw new TypeError("Dynamic keyed dependency keys must be strings or undefined.");
|
|
2858
|
-
bindKey(bindings[index], key, selectedKey);
|
|
2859
|
-
values[dependencyNames[index]] = selectedKey === void 0 ? void 0 : sourceContext.read.get(selectedKey);
|
|
2860
|
-
}
|
|
2861
|
-
dependencyValues = Object.freeze(values);
|
|
2862
|
-
}
|
|
2863
|
-
const next = select(value, key, dependencyValues);
|
|
2864
|
-
require_scheduler.assertSynchronous(next);
|
|
2865
|
-
if (next === absent) output.output.remove(key);
|
|
2866
|
-
else output.output.set(key, next);
|
|
2867
|
-
};
|
|
2868
|
-
if (evaluation.reset) {
|
|
2869
|
-
clearBindings();
|
|
2870
|
-
const ids = driver.read.ids();
|
|
2871
|
-
for (const key of ids) project(key);
|
|
2872
|
-
output.output.order(ids.filter((key) => output.next.has(key)));
|
|
2873
|
-
rememberRevisions(evaluation.sources);
|
|
2874
|
-
return;
|
|
2875
|
-
}
|
|
2876
|
-
const dirty = /* @__PURE__ */ new Set();
|
|
2877
|
-
let all = false;
|
|
2878
|
-
let updateOrder = false;
|
|
2879
|
-
let membershipChanged = false;
|
|
2880
|
-
if (revisions[0] !== driver.revision) {
|
|
2881
|
-
const change = driver.change;
|
|
2882
|
-
if (!change || change.kind === "reset") {
|
|
2883
|
-
all = true;
|
|
2884
|
-
updateOrder = true;
|
|
2885
|
-
} else {
|
|
2886
|
-
for (const entry of change.removed) {
|
|
2887
|
-
membershipChanged ||= output.previous.has(entry.key);
|
|
2888
|
-
output.output.remove(entry.key);
|
|
2889
|
-
unbindKey(bindings, entry.key);
|
|
2890
|
-
dirty.delete(entry.key);
|
|
2891
|
-
}
|
|
2892
|
-
for (const entry of change.added) dirty.add(entry.key);
|
|
2893
|
-
for (const entry of change.updated) dirty.add(entry.key);
|
|
2894
|
-
updateOrder = Boolean(change.order);
|
|
2895
|
-
}
|
|
2896
|
-
}
|
|
2897
|
-
for (let index = 0; index < keyed.length; index++) {
|
|
2898
|
-
const sourceContext = evaluation.sources[index + 1];
|
|
2899
|
-
if (revisions[index + 1] === sourceContext.revision) continue;
|
|
2900
|
-
if (!keyed[index]) {
|
|
2901
|
-
all = true;
|
|
2902
|
-
continue;
|
|
2903
|
-
}
|
|
2904
|
-
if (sourceContext.kind !== "collection" || !sourceContext.change) {
|
|
2905
|
-
all = true;
|
|
2906
|
-
continue;
|
|
2907
|
-
}
|
|
2908
|
-
if (sourceContext.change.kind === "reset") {
|
|
2909
|
-
all = true;
|
|
2910
|
-
continue;
|
|
2911
|
-
}
|
|
2912
|
-
const reverse = bindings[index].reverse;
|
|
2913
|
-
for (const sourceKey of require_scheduler.collectionChange.keys(sourceContext.change)) for (const outputKey of reverse.get(sourceKey) ?? []) dirty.add(outputKey);
|
|
2914
|
-
}
|
|
2915
|
-
if (all) for (const key of driver.read.ids()) dirty.add(key);
|
|
2916
|
-
for (const key of dirty) {
|
|
2917
|
-
if (!driver.read.has(key)) continue;
|
|
2918
|
-
const beforePresent = output.previous.has(key);
|
|
2919
|
-
project(key);
|
|
2920
|
-
membershipChanged ||= beforePresent !== output.next.has(key);
|
|
2921
|
-
}
|
|
2922
|
-
if (updateOrder || membershipChanged) output.output.order(driver.read.ids().filter((key) => output.next.has(key)));
|
|
2923
|
-
rememberRevisions(evaluation.sources);
|
|
2924
|
-
},
|
|
2925
|
-
release: () => {
|
|
2926
|
-
clearBindings();
|
|
2927
|
-
revisions.length = 0;
|
|
2928
|
-
}
|
|
2929
|
-
};
|
|
2930
|
-
},
|
|
2931
|
-
name
|
|
2932
|
-
});
|
|
2933
|
-
return projection;
|
|
2934
|
-
};
|
|
2935
2752
|
function createKeyedDerive(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
2936
2753
|
const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
|
|
2937
2754
|
const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
|
|
2938
2755
|
const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
|
|
2939
2756
|
if (typeof select !== "function") throw new TypeError("Keyed derive requires a selector.");
|
|
2940
|
-
return createKeyedTransform(
|
|
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
|
+
});
|
|
2941
2764
|
}
|
|
2942
2765
|
const createKeyedKeys = (source) => {
|
|
2943
|
-
assertKeyedProjection(source, "Keyed keys source");
|
|
2944
|
-
const [projection] =
|
|
2766
|
+
require_transform.assertKeyedProjection(source, "Keyed keys source");
|
|
2767
|
+
const [projection] = require_transform.defineProcessor({
|
|
2945
2768
|
dependencies: [source],
|
|
2946
2769
|
outputs: [{
|
|
2947
2770
|
kind: "value",
|
|
@@ -2951,15 +2774,15 @@ const createKeyedKeys = (source) => {
|
|
|
2951
2774
|
const driver = evaluation.sources[0];
|
|
2952
2775
|
const output = evaluation.outputs[0];
|
|
2953
2776
|
if (driver.kind !== "collection" || output.kind !== "value") throw new Error("derive.keyed.keys requires collection input and value output.");
|
|
2954
|
-
if (evaluation.reset || !driver.change ||
|
|
2777
|
+
if (evaluation.reset || !driver.change || require_transform.collectionHasStructuralChange(driver.change)) output.output.set(driver.read.ids());
|
|
2955
2778
|
} }),
|
|
2956
2779
|
name: "derive.keyed.keys"
|
|
2957
2780
|
});
|
|
2958
2781
|
return projection;
|
|
2959
2782
|
};
|
|
2960
|
-
const
|
|
2961
|
-
assertKeyedProjection(source,
|
|
2962
|
-
const [projection] =
|
|
2783
|
+
const createOrderedSnapshot = (source, name, make) => {
|
|
2784
|
+
require_transform.assertKeyedProjection(source, `${name} source`);
|
|
2785
|
+
const [projection] = require_transform.defineProcessor({
|
|
2963
2786
|
dependencies: [source],
|
|
2964
2787
|
outputs: [{
|
|
2965
2788
|
kind: "value",
|
|
@@ -2969,33 +2792,32 @@ const createKeyedValues = (source) => {
|
|
|
2969
2792
|
let indexes = /* @__PURE__ */ new Map();
|
|
2970
2793
|
const rebuild = (driver) => {
|
|
2971
2794
|
const nextIndexes = /* @__PURE__ */ new Map();
|
|
2972
|
-
const
|
|
2973
|
-
const values = Object.freeze(ids.map((key, index) => {
|
|
2795
|
+
const result = Object.freeze(driver.read.ids().map((key, index) => {
|
|
2974
2796
|
nextIndexes.set(key, index);
|
|
2975
|
-
return driver.read.get(key);
|
|
2797
|
+
return make(key, driver.read.get(key));
|
|
2976
2798
|
}));
|
|
2977
2799
|
indexes = nextIndexes;
|
|
2978
|
-
return
|
|
2800
|
+
return result;
|
|
2979
2801
|
};
|
|
2980
2802
|
const rebuildIncremental = (driver, change, previous) => {
|
|
2981
2803
|
const changedValues = /* @__PURE__ */ new Map();
|
|
2982
2804
|
for (const entry of change.added) changedValues.set(entry.key, entry.after);
|
|
2983
2805
|
for (const entry of change.updated) changedValues.set(entry.key, entry.after);
|
|
2984
2806
|
const nextIndexes = /* @__PURE__ */ new Map();
|
|
2985
|
-
const
|
|
2807
|
+
const result = Object.freeze(driver.read.ids().map((key, index) => {
|
|
2986
2808
|
nextIndexes.set(key, index);
|
|
2987
|
-
if (changedValues.has(key)) return changedValues.get(key);
|
|
2809
|
+
if (changedValues.has(key)) return make(key, changedValues.get(key));
|
|
2988
2810
|
const previousIndex = indexes.get(key);
|
|
2989
|
-
return previousIndex === void 0 ? driver.read.get(key) : previous[previousIndex];
|
|
2811
|
+
return previousIndex === void 0 ? make(key, driver.read.get(key)) : previous[previousIndex];
|
|
2990
2812
|
}));
|
|
2991
2813
|
indexes = nextIndexes;
|
|
2992
|
-
return
|
|
2814
|
+
return result;
|
|
2993
2815
|
};
|
|
2994
2816
|
return {
|
|
2995
2817
|
evaluate: (evaluation) => {
|
|
2996
2818
|
const driver = evaluation.sources[0];
|
|
2997
2819
|
const output = evaluation.outputs[0];
|
|
2998
|
-
if (driver.kind !== "collection" || output.kind !== "value") throw new Error(
|
|
2820
|
+
if (driver.kind !== "collection" || output.kind !== "value") throw new Error(`${name} requires collection input and value output.`);
|
|
2999
2821
|
const change = driver.change;
|
|
3000
2822
|
if (evaluation.reset || !change || change.kind === "reset") {
|
|
3001
2823
|
output.output.set(rebuild(driver));
|
|
@@ -3006,7 +2828,7 @@ const createKeyedValues = (source) => {
|
|
|
3006
2828
|
output.output.set(rebuild(driver));
|
|
3007
2829
|
return;
|
|
3008
2830
|
}
|
|
3009
|
-
if (
|
|
2831
|
+
if (require_transform.collectionHasStructuralChange(change)) {
|
|
3010
2832
|
output.output.set(rebuildIncremental(driver, change, previous));
|
|
3011
2833
|
return;
|
|
3012
2834
|
}
|
|
@@ -3018,14 +2840,59 @@ const createKeyedValues = (source) => {
|
|
|
3018
2840
|
output.output.set(rebuild(driver));
|
|
3019
2841
|
return;
|
|
3020
2842
|
}
|
|
3021
|
-
next[index] = entry.after;
|
|
2843
|
+
next[index] = make(entry.key, entry.after);
|
|
3022
2844
|
}
|
|
3023
2845
|
output.output.set(Object.freeze(next));
|
|
3024
2846
|
},
|
|
3025
2847
|
release: () => indexes.clear()
|
|
3026
2848
|
};
|
|
3027
2849
|
},
|
|
3028
|
-
name
|
|
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"
|
|
3029
2896
|
});
|
|
3030
2897
|
return projection;
|
|
3031
2898
|
};
|
|
@@ -3033,28 +2900,40 @@ function createKeyedFilter(source, dependenciesOrPredicate, maybePredicate) {
|
|
|
3033
2900
|
const dependencySpecs = typeof dependenciesOrPredicate === "function" ? void 0 : dependenciesOrPredicate;
|
|
3034
2901
|
const predicate = dependencySpecs === void 0 ? dependenciesOrPredicate : maybePredicate;
|
|
3035
2902
|
if (typeof predicate !== "function") throw new TypeError("Keyed filter requires a predicate.");
|
|
3036
|
-
return createKeyedTransform(
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
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
|
+
});
|
|
3042
2915
|
}
|
|
3043
2916
|
function createKeyedCompact(source, dependenciesOrSelect, selectOrEquality, maybeEquality) {
|
|
3044
2917
|
const dependencySpecs = typeof dependenciesOrSelect === "function" ? void 0 : dependenciesOrSelect;
|
|
3045
2918
|
const select = dependencySpecs === void 0 ? dependenciesOrSelect : selectOrEquality;
|
|
3046
2919
|
const equality = dependencySpecs === void 0 ? selectOrEquality : maybeEquality;
|
|
3047
2920
|
if (typeof select !== "function") throw new TypeError("Keyed compact requires a selector.");
|
|
3048
|
-
return createKeyedTransform(
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
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;
|
|
2930
|
+
}
|
|
2931
|
+
});
|
|
3053
2932
|
}
|
|
3054
2933
|
const createKeyedSubset = (source, orderedKeys) => {
|
|
3055
|
-
assertKeyedProjection(source, "Keyed subset source");
|
|
3056
|
-
const keyProjection =
|
|
3057
|
-
if (keyProjection && outputKind(keyProjection) !== "value") throw new TypeError("Keyed subset ordered keys must be a value projection.");
|
|
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.");
|
|
3058
2937
|
const staticSelection = keyProjection ? void 0 : (() => {
|
|
3059
2938
|
const compiled = compileOrderedKeys(orderedKeys, "Keyed subset ordered keys");
|
|
3060
2939
|
return {
|
|
@@ -3062,7 +2941,7 @@ const createKeyedSubset = (source, orderedKeys) => {
|
|
|
3062
2941
|
requested: compiled.requested
|
|
3063
2942
|
};
|
|
3064
2943
|
})();
|
|
3065
|
-
const [projection] =
|
|
2944
|
+
const [projection] = require_transform.defineProcessor({
|
|
3066
2945
|
dependencies: keyProjection ? [source, keyProjection] : [source],
|
|
3067
2946
|
outputs: [{
|
|
3068
2947
|
kind: "collection",
|
|
@@ -3146,9 +3025,198 @@ const createKeyedSubset = (source, orderedKeys) => {
|
|
|
3146
3025
|
});
|
|
3147
3026
|
return projection;
|
|
3148
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);
|
|
3038
|
+
}
|
|
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,
|
|
3049
|
+
outputs: [{
|
|
3050
|
+
kind: "collection",
|
|
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
|
+
}
|
|
3056
|
+
}],
|
|
3057
|
+
create: () => {
|
|
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;
|
|
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);
|
|
3129
|
+
};
|
|
3130
|
+
return {
|
|
3131
|
+
evaluate: (evaluation) => {
|
|
3132
|
+
const driver = evaluation.sources[0];
|
|
3133
|
+
const output = evaluation.outputs[0];
|
|
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);
|
|
3136
|
+
if (evaluation.reset) {
|
|
3137
|
+
rebuild(driver, evaluation.sources, output);
|
|
3138
|
+
dependencies.remember(evaluation.sources);
|
|
3139
|
+
return;
|
|
3140
|
+
}
|
|
3141
|
+
const dirty = /* @__PURE__ */ new Set();
|
|
3142
|
+
const affected = /* @__PURE__ */ new Set();
|
|
3143
|
+
let all = false;
|
|
3144
|
+
let structural = false;
|
|
3145
|
+
let orderDirty = false;
|
|
3146
|
+
if (dependencies.driverChanged(driver)) {
|
|
3147
|
+
const change = driver.change;
|
|
3148
|
+
if (!change || change.kind === "reset") {
|
|
3149
|
+
rebuild(driver, evaluation.sources, output);
|
|
3150
|
+
dependencies.remember(evaluation.sources);
|
|
3151
|
+
return;
|
|
3152
|
+
} else {
|
|
3153
|
+
for (const entry of change.removed) {
|
|
3154
|
+
orderDirty ||= removeSourceKey(entry.key, affected);
|
|
3155
|
+
dependencies.remove(entry.key);
|
|
3156
|
+
dirty.delete(entry.key);
|
|
3157
|
+
}
|
|
3158
|
+
for (const entry of change.added) dirty.add(entry.key);
|
|
3159
|
+
for (const entry of change.updated) dirty.add(entry.key);
|
|
3160
|
+
structural = Boolean(change.added.length || change.removed.length || change.order);
|
|
3161
|
+
orderDirty ||= structural;
|
|
3162
|
+
if (structural) refreshOrderIndex(driver);
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
all ||= dependencies.collectInvalidated(evaluation.sources, dirty);
|
|
3166
|
+
if (all) for (const key of driver.read.ids()) dirty.add(key);
|
|
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);
|
|
3171
|
+
},
|
|
3172
|
+
release: () => {
|
|
3173
|
+
relation.clear();
|
|
3174
|
+
orderIndex.clear();
|
|
3175
|
+
groupRanks.clear();
|
|
3176
|
+
dependencies.release();
|
|
3177
|
+
}
|
|
3178
|
+
};
|
|
3179
|
+
},
|
|
3180
|
+
name: "derive.keyed.groupBy"
|
|
3181
|
+
});
|
|
3182
|
+
return projection;
|
|
3183
|
+
}
|
|
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"
|
|
3210
|
+
});
|
|
3211
|
+
return projection;
|
|
3212
|
+
};
|
|
3149
3213
|
const keyedDerive = Object.assign(createKeyedDerive, {
|
|
3150
3214
|
keys: createKeyedKeys,
|
|
3151
3215
|
values: createKeyedValues,
|
|
3216
|
+
entries: createKeyedEntries,
|
|
3217
|
+
get: createKeyedGet,
|
|
3218
|
+
groupBy: createKeyedGroupBy,
|
|
3219
|
+
singleton: createKeyedSingleton,
|
|
3152
3220
|
subset: createKeyedSubset,
|
|
3153
3221
|
filter: createKeyedFilter,
|
|
3154
3222
|
compact: createKeyedCompact
|
|
@@ -3156,10 +3224,10 @@ const keyedDerive = Object.assign(createKeyedDerive, {
|
|
|
3156
3224
|
//#endregion
|
|
3157
3225
|
//#region core/src/projection/derive.ts
|
|
3158
3226
|
function createValueDerive(dependencies, compute, equality = Object.is) {
|
|
3159
|
-
const compiled =
|
|
3227
|
+
const compiled = require_transform.compileProjectionDependencies(dependencies, "Derive");
|
|
3160
3228
|
const names = compiled.names;
|
|
3161
3229
|
const ordered = compiled.projections;
|
|
3162
|
-
const [projection] =
|
|
3230
|
+
const [projection] = require_transform.defineProcessor({
|
|
3163
3231
|
dependencies: ordered,
|
|
3164
3232
|
outputs: [{
|
|
3165
3233
|
kind: "value",
|
|
@@ -3169,9 +3237,9 @@ function createValueDerive(dependencies, compute, equality = Object.is) {
|
|
|
3169
3237
|
const output = evaluation.outputs[0];
|
|
3170
3238
|
if (output.kind !== "value") throw new Error("derive requires a value output.");
|
|
3171
3239
|
const values = Object.create(null);
|
|
3172
|
-
for (let index = 0; index < names.length; index++) values[names[index]] =
|
|
3240
|
+
for (let index = 0; index < names.length; index++) values[names[index]] = require_transform.snapshotDependencyValue(evaluation.sources[index]);
|
|
3173
3241
|
const next = compute(Object.freeze(values));
|
|
3174
|
-
|
|
3242
|
+
require_transform.assertSynchronous(next);
|
|
3175
3243
|
output.output.set(next);
|
|
3176
3244
|
} })
|
|
3177
3245
|
});
|
|
@@ -3182,7 +3250,7 @@ const derive = Object.assign(createValueDerive, { keyed: keyedDerive });
|
|
|
3182
3250
|
//#region core/src/projection/collection/index.ts
|
|
3183
3251
|
const height = (node) => node?.height ?? 0;
|
|
3184
3252
|
const node = (key, value, left, right) => {
|
|
3185
|
-
|
|
3253
|
+
require_transform.profile.collectionIndex.node();
|
|
3186
3254
|
return Object.freeze({
|
|
3187
3255
|
key,
|
|
3188
3256
|
value,
|
|
@@ -3243,7 +3311,7 @@ var PersistentKeyedIndex = class PersistentKeyedIndex {
|
|
|
3243
3311
|
}
|
|
3244
3312
|
static from(entries) {
|
|
3245
3313
|
const sorted = [...entries].sort(([left], [right]) => left < right ? -1 : left > right ? 1 : 0);
|
|
3246
|
-
|
|
3314
|
+
require_transform.profile.collectionIndex.build(sorted.length);
|
|
3247
3315
|
return new PersistentKeyedIndex(fromSorted(sorted));
|
|
3248
3316
|
}
|
|
3249
3317
|
get(key) {
|
|
@@ -3348,15 +3416,15 @@ const createCollectionOutput = (binding) => {
|
|
|
3348
3416
|
reset = initialize;
|
|
3349
3417
|
const read = (next) => ({
|
|
3350
3418
|
get: (key) => {
|
|
3351
|
-
|
|
3419
|
+
require_transform.assertScope(active);
|
|
3352
3420
|
return next ? getNext(key) : published.get(key);
|
|
3353
3421
|
},
|
|
3354
3422
|
has: (key) => {
|
|
3355
|
-
|
|
3423
|
+
require_transform.assertScope(active);
|
|
3356
3424
|
return next ? hasNext(key) : published.has(key);
|
|
3357
3425
|
},
|
|
3358
3426
|
ids: () => {
|
|
3359
|
-
|
|
3427
|
+
require_transform.assertScope(active);
|
|
3360
3428
|
return next ? Object.freeze(explicitOrder ? [...explicitOrder] : [...deriveIds()]) : published.ids();
|
|
3361
3429
|
}
|
|
3362
3430
|
});
|
|
@@ -3365,7 +3433,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3365
3433
|
next: read(true),
|
|
3366
3434
|
output: {
|
|
3367
3435
|
set: (key, value) => {
|
|
3368
|
-
|
|
3436
|
+
require_transform.assertScope(active);
|
|
3369
3437
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
3370
3438
|
staged.set(key, {
|
|
3371
3439
|
present: true,
|
|
@@ -3373,11 +3441,11 @@ const createCollectionOutput = (binding) => {
|
|
|
3373
3441
|
});
|
|
3374
3442
|
},
|
|
3375
3443
|
remove: (key) => {
|
|
3376
|
-
|
|
3444
|
+
require_transform.assertScope(active);
|
|
3377
3445
|
staged.set(key, { present: false });
|
|
3378
3446
|
},
|
|
3379
3447
|
order: (order) => {
|
|
3380
|
-
|
|
3448
|
+
require_transform.assertScope(active);
|
|
3381
3449
|
explicitOrder = Object.freeze([...order]);
|
|
3382
3450
|
}
|
|
3383
3451
|
}
|
|
@@ -3387,7 +3455,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3387
3455
|
if (cleared) {
|
|
3388
3456
|
for (const key of published.keys()) if (!staged.has(key)) staged.set(key, { present: false });
|
|
3389
3457
|
}
|
|
3390
|
-
|
|
3458
|
+
require_transform.profile.projection("touchedKeys", staged.size);
|
|
3391
3459
|
const added = /* @__PURE__ */ new Set();
|
|
3392
3460
|
const removed = /* @__PURE__ */ new Set();
|
|
3393
3461
|
const addedEntries = [];
|
|
@@ -3427,8 +3495,8 @@ const createCollectionOutput = (binding) => {
|
|
|
3427
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.");
|
|
3428
3496
|
nextIds = ids.length === order.length && ids.every((key, index) => key === order[index]) ? ids : Object.freeze([...order]);
|
|
3429
3497
|
} else nextIds = ids;
|
|
3430
|
-
|
|
3431
|
-
change = nextReset ? Object.freeze({ kind: "reset" }) :
|
|
3498
|
+
require_transform.profile.projection("changedKeys", addedEntries.length + updatedEntries.length + removedEntries.length);
|
|
3499
|
+
change = nextReset ? Object.freeze({ kind: "reset" }) : require_transform.createCollectionChange({
|
|
3432
3500
|
added: addedEntries,
|
|
3433
3501
|
updated: updatedEntries,
|
|
3434
3502
|
removed: removedEntries,
|
|
@@ -3442,15 +3510,15 @@ const createCollectionOutput = (binding) => {
|
|
|
3442
3510
|
kind: "collection",
|
|
3443
3511
|
read: {
|
|
3444
3512
|
get: (key) => {
|
|
3445
|
-
|
|
3513
|
+
require_transform.assertScope(active);
|
|
3446
3514
|
return getNext(key);
|
|
3447
3515
|
},
|
|
3448
3516
|
has: (key) => {
|
|
3449
|
-
|
|
3517
|
+
require_transform.assertScope(active);
|
|
3450
3518
|
return hasNext(key);
|
|
3451
3519
|
},
|
|
3452
3520
|
ids: () => {
|
|
3453
|
-
|
|
3521
|
+
require_transform.assertScope(active);
|
|
3454
3522
|
return nextIds;
|
|
3455
3523
|
}
|
|
3456
3524
|
},
|
|
@@ -3476,7 +3544,7 @@ const createCollectionOutput = (binding) => {
|
|
|
3476
3544
|
initialized = true;
|
|
3477
3545
|
},
|
|
3478
3546
|
emit: (call) => {
|
|
3479
|
-
Array.from(listeners).forEach((listener) => call(listener));
|
|
3547
|
+
Array.from(listeners).forEach((listener) => call(listener, change));
|
|
3480
3548
|
},
|
|
3481
3549
|
hasConsumers: () => consumers.size > 0,
|
|
3482
3550
|
forEachConsumer: (run) => consumers.forEach(run),
|
|
@@ -3532,12 +3600,12 @@ const createValueOutput = (binding) => {
|
|
|
3532
3600
|
return {
|
|
3533
3601
|
previous: initialized ? value : void 0,
|
|
3534
3602
|
next: () => {
|
|
3535
|
-
|
|
3603
|
+
require_transform.assertScope(active);
|
|
3536
3604
|
if (staged !== unset) return staged;
|
|
3537
3605
|
return initialize ? void 0 : initialized ? value : void 0;
|
|
3538
3606
|
},
|
|
3539
3607
|
output: Object.freeze({ set: (candidate) => {
|
|
3540
|
-
|
|
3608
|
+
require_transform.assertScope(active);
|
|
3541
3609
|
staged = candidate;
|
|
3542
3610
|
} })
|
|
3543
3611
|
};
|
|
@@ -3564,7 +3632,7 @@ const createValueOutput = (binding) => {
|
|
|
3564
3632
|
begin,
|
|
3565
3633
|
seal,
|
|
3566
3634
|
context: (active) => {
|
|
3567
|
-
|
|
3635
|
+
require_transform.assertScope(active);
|
|
3568
3636
|
return Object.freeze({
|
|
3569
3637
|
kind: "value",
|
|
3570
3638
|
value: next,
|
|
@@ -3585,8 +3653,8 @@ const createValueOutput = (binding) => {
|
|
|
3585
3653
|
initialized = true;
|
|
3586
3654
|
},
|
|
3587
3655
|
emit: (call) => {
|
|
3588
|
-
|
|
3589
|
-
Array.from(listeners).forEach((listener) => call(listener));
|
|
3656
|
+
require_transform.profile.materialized.notification();
|
|
3657
|
+
Array.from(listeners).forEach((listener) => call(listener, void 0));
|
|
3590
3658
|
},
|
|
3591
3659
|
hasConsumers: () => consumers.size > 0,
|
|
3592
3660
|
forEachConsumer: (run) => consumers.forEach(run),
|
|
@@ -3624,7 +3692,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3624
3692
|
let instance;
|
|
3625
3693
|
let cause;
|
|
3626
3694
|
const check = () => {
|
|
3627
|
-
if (!scheduler.active || processor.disposed) throw new
|
|
3695
|
+
if (!scheduler.active || processor.disposed) throw new require_transform.ProjectionDisposedError();
|
|
3628
3696
|
if (processor.fault) throw processor.fault;
|
|
3629
3697
|
};
|
|
3630
3698
|
const bound = definition.outputs.map((outputDefinition) => {
|
|
@@ -3678,8 +3746,8 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3678
3746
|
instance = void 0;
|
|
3679
3747
|
}
|
|
3680
3748
|
if (!instance) instance = definition.create();
|
|
3681
|
-
|
|
3682
|
-
|
|
3749
|
+
require_transform.profile.materialized[reset ? "rebuilt" : "updated"]();
|
|
3750
|
+
require_transform.assertSynchronous(instance.evaluate({
|
|
3683
3751
|
reset,
|
|
3684
3752
|
cause: runCause,
|
|
3685
3753
|
sources,
|
|
@@ -3726,7 +3794,7 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3726
3794
|
try {
|
|
3727
3795
|
scheduler.initialize(() => {
|
|
3728
3796
|
const invalid = dependencies.find((output) => output.owner.fault || output.owner.disposed);
|
|
3729
|
-
if (invalid) throw invalid.owner.fault ?? new
|
|
3797
|
+
if (invalid) throw invalid.owner.fault ?? new require_transform.ProjectionDisposedError();
|
|
3730
3798
|
if (processor.evaluate(true, void 0).length) processor.publish();
|
|
3731
3799
|
processor.clear();
|
|
3732
3800
|
});
|
|
@@ -3741,6 +3809,20 @@ const createProcessor = (scheduler, definition, dependencies) => {
|
|
|
3741
3809
|
return processor;
|
|
3742
3810
|
};
|
|
3743
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
|
|
3744
3826
|
//#region core/src/projection/readable/selection.ts
|
|
3745
3827
|
let activeTracker;
|
|
3746
3828
|
const trackKey = (key) => {
|
|
@@ -3793,8 +3875,8 @@ const trackedMap = (source) => {
|
|
|
3793
3875
|
const selectionAffects = (selection, change) => {
|
|
3794
3876
|
if (!change || change.kind === "reset") return true;
|
|
3795
3877
|
if (selection.all) return true;
|
|
3796
|
-
if (selection.structure &&
|
|
3797
|
-
for (const key of
|
|
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;
|
|
3798
3880
|
return false;
|
|
3799
3881
|
};
|
|
3800
3882
|
const createDirectReadable = (source) => Object.freeze({
|
|
@@ -3862,7 +3944,7 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
3862
3944
|
if (!change && previousCollection) {
|
|
3863
3945
|
const current = source.current();
|
|
3864
3946
|
if (isMapLike(current)) {
|
|
3865
|
-
change =
|
|
3947
|
+
change = require_transform.diffCollection(previousCollection, current);
|
|
3866
3948
|
previousCollection = current;
|
|
3867
3949
|
if (!change) {
|
|
3868
3950
|
sourceRevision = source.revision();
|
|
@@ -3875,7 +3957,7 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
3875
3957
|
updateCollectionSnapshot();
|
|
3876
3958
|
return;
|
|
3877
3959
|
}
|
|
3878
|
-
if (evaluate())
|
|
3960
|
+
if (evaluate()) notifyProjectionListeners(listeners, "Projection selector listeners failed.");
|
|
3879
3961
|
};
|
|
3880
3962
|
const installSource = () => {
|
|
3881
3963
|
if (!unsubscribeSource) unsubscribeSource = source.subscribe(onProjectionChange);
|
|
@@ -3906,7 +3988,7 @@ const hasDocumentDirty = (dirty) => dirty.replace || dirty.order || dirty.treeRo
|
|
|
3906
3988
|
const equivalent = (node, left, right) => node.kind === "field" ? Object.is(left, right) : equalValue(node, left, right);
|
|
3907
3989
|
const replaceValue = (node, previous, current) => equivalent(node, previous, current) ? previous : copyValue(node, current);
|
|
3908
3990
|
const cloneRecord = (value) => {
|
|
3909
|
-
|
|
3991
|
+
require_transform.profile.copy.shallowRecord(Object.getOwnPropertyNames(value).length);
|
|
3910
3992
|
return Object.create(Object.getPrototypeOf(value), Object.getOwnPropertyDescriptors(value));
|
|
3911
3993
|
};
|
|
3912
3994
|
const materializeRecordChild = (node, previous, current, key, dirty) => {
|
|
@@ -4212,7 +4294,7 @@ const mergeCollectionChanges = (selector, pending, changes) => {
|
|
|
4212
4294
|
};
|
|
4213
4295
|
//#endregion
|
|
4214
4296
|
//#region core/src/projection/source/boundary.ts
|
|
4215
|
-
const sourceError = (cause) => new
|
|
4297
|
+
const sourceError = (cause) => new require_transform.ProjectionError("source", cause);
|
|
4216
4298
|
const createValueBoundary = (scheduler, name, initial, equality, prepare, clearSource) => {
|
|
4217
4299
|
let boundary;
|
|
4218
4300
|
let pendingReset = false;
|
|
@@ -4221,7 +4303,7 @@ const createValueBoundary = (scheduler, name, initial, equality, prepare, clearS
|
|
|
4221
4303
|
let recovering = false;
|
|
4222
4304
|
let cleanup;
|
|
4223
4305
|
const check = () => {
|
|
4224
|
-
if (!scheduler.active || boundary.disposed) throw new
|
|
4306
|
+
if (!scheduler.active || boundary.disposed) throw new require_transform.ProjectionDisposedError();
|
|
4225
4307
|
if (boundary.fault) throw boundary.fault;
|
|
4226
4308
|
};
|
|
4227
4309
|
const state = createValueOutput({
|
|
@@ -4328,23 +4410,23 @@ const stageCollectionRead = (state, read, active, options) => {
|
|
|
4328
4410
|
const evaluation = state.begin(active, options.reset);
|
|
4329
4411
|
if (options.reset) {
|
|
4330
4412
|
const ids = read.ids();
|
|
4331
|
-
|
|
4413
|
+
require_transform.profile.collectionView.idsScanned(ids.length);
|
|
4332
4414
|
for (const key of ids) {
|
|
4333
|
-
|
|
4415
|
+
require_transform.profile.collectionView.mapped();
|
|
4334
4416
|
evaluation.output.set(key, read.get(key));
|
|
4335
4417
|
}
|
|
4336
4418
|
evaluation.output.order(ids);
|
|
4337
4419
|
} else {
|
|
4338
4420
|
const previous = state.current();
|
|
4339
4421
|
const keys = options.candidates ? [...options.candidates] : [...new Set([...previous.ids(), ...read.ids()])];
|
|
4340
|
-
if (!options.candidates)
|
|
4422
|
+
if (!options.candidates) require_transform.profile.collectionView.idsScanned(keys.length);
|
|
4341
4423
|
for (const key of keys) if (read.has(key)) {
|
|
4342
|
-
|
|
4424
|
+
require_transform.profile.collectionView.mapped();
|
|
4343
4425
|
evaluation.output.set(key, read.get(key));
|
|
4344
4426
|
} else evaluation.output.remove(key);
|
|
4345
4427
|
if (options.orderMayChange || !options.candidates) {
|
|
4346
4428
|
const ids = read.ids();
|
|
4347
|
-
if (options.candidates)
|
|
4429
|
+
if (options.candidates) require_transform.profile.collectionView.idsScanned(ids.length);
|
|
4348
4430
|
evaluation.output.order(ids);
|
|
4349
4431
|
}
|
|
4350
4432
|
}
|
|
@@ -4361,7 +4443,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSo
|
|
|
4361
4443
|
const candidates = /* @__PURE__ */ new Set();
|
|
4362
4444
|
let cleanup;
|
|
4363
4445
|
const check = () => {
|
|
4364
|
-
if (!scheduler.active || boundary.disposed) throw new
|
|
4446
|
+
if (!scheduler.active || boundary.disposed) throw new require_transform.ProjectionDisposedError();
|
|
4365
4447
|
if (boundary.fault) throw boundary.fault;
|
|
4366
4448
|
};
|
|
4367
4449
|
const state = createCollectionOutput({
|
|
@@ -4640,7 +4722,7 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
4640
4722
|
const replacement = createDocumentDirty();
|
|
4641
4723
|
if (pending.reset) markDocumentReplace(replacement, []);
|
|
4642
4724
|
const ids = () => {
|
|
4643
|
-
|
|
4725
|
+
require_transform.assertScope(active);
|
|
4644
4726
|
if (treeNodes) return node?.kind === "tree" && require_record.isRecord(value) && require_record.isRecord(value.nodes) ? Object.freeze(Object.keys(value.nodes)) : Object.freeze([]);
|
|
4645
4727
|
if (node?.kind === "map" && require_record.isRecord(value)) return Object.freeze(Object.keys(value));
|
|
4646
4728
|
if (node?.kind === "table" && require_record.isRecord(value) && Array.isArray(value.ids)) return Object.freeze([...value.ids]);
|
|
@@ -4648,7 +4730,7 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
4648
4730
|
return Object.freeze([]);
|
|
4649
4731
|
};
|
|
4650
4732
|
const has = (key) => {
|
|
4651
|
-
|
|
4733
|
+
require_transform.assertScope(active);
|
|
4652
4734
|
if (treeNodes) return Boolean(node?.kind === "tree" && require_record.isRecord(value) && require_record.isRecord(value.nodes) && Object.hasOwn(value.nodes, key));
|
|
4653
4735
|
if (node?.kind === "map" && require_record.isRecord(value)) return Object.hasOwn(value, key);
|
|
4654
4736
|
if (node?.kind === "table" && require_record.isRecord(value) && require_record.isRecord(value.byId)) return Object.hasOwn(value.byId, key);
|
|
@@ -4669,7 +4751,7 @@ const createDocumentSourceRegistry = (scheduler) => {
|
|
|
4669
4751
|
};
|
|
4670
4752
|
return Object.freeze({
|
|
4671
4753
|
get: (key) => {
|
|
4672
|
-
|
|
4754
|
+
require_transform.assertScope(active);
|
|
4673
4755
|
if (!snapshots.has(key)) if (!has(key)) snapshots.set(key, void 0);
|
|
4674
4756
|
else {
|
|
4675
4757
|
const current = raw(key);
|
|
@@ -5016,7 +5098,7 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5016
5098
|
}
|
|
5017
5099
|
if (source.kind !== "collection-input") throw new Error("Invalid projection input source.");
|
|
5018
5100
|
const values = new Map(source.initial);
|
|
5019
|
-
const boundary = createCollectionBoundary(scheduler, "projection collection input", () =>
|
|
5101
|
+
const boundary = createCollectionBoundary(scheduler, "projection collection input", () => require_transform.mapRead(values), definition.output.equality, () => void 0);
|
|
5020
5102
|
return {
|
|
5021
5103
|
...boundary,
|
|
5022
5104
|
write: {
|
|
@@ -5029,17 +5111,17 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5029
5111
|
let active = true;
|
|
5030
5112
|
const draft = Object.freeze({
|
|
5031
5113
|
get: (key) => {
|
|
5032
|
-
|
|
5114
|
+
require_transform.assertScope(() => active);
|
|
5033
5115
|
const entry = staged.get(key);
|
|
5034
5116
|
return entry ? entry.present ? entry.value : void 0 : values.get(key);
|
|
5035
5117
|
},
|
|
5036
5118
|
has: (key) => {
|
|
5037
|
-
|
|
5119
|
+
require_transform.assertScope(() => active);
|
|
5038
5120
|
const entry = staged.get(key);
|
|
5039
5121
|
return entry ? entry.present : values.has(key);
|
|
5040
5122
|
},
|
|
5041
5123
|
set: (key, value) => {
|
|
5042
|
-
|
|
5124
|
+
require_transform.assertScope(() => active);
|
|
5043
5125
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
5044
5126
|
const previous = staged.get(key);
|
|
5045
5127
|
structural ||= previous ? !previous.present : !values.has(key);
|
|
@@ -5054,7 +5136,7 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5054
5136
|
});
|
|
5055
5137
|
},
|
|
5056
5138
|
remove: (key) => {
|
|
5057
|
-
|
|
5139
|
+
require_transform.assertScope(() => active);
|
|
5058
5140
|
if (typeof key !== "string") throw new TypeError("Projection keys must be strings.");
|
|
5059
5141
|
const previous = staged.get(key);
|
|
5060
5142
|
const present = previous ? previous.present : values.has(key);
|
|
@@ -5067,7 +5149,7 @@ const materializeInputSource = (scheduler, definition) => {
|
|
|
5067
5149
|
}
|
|
5068
5150
|
});
|
|
5069
5151
|
try {
|
|
5070
|
-
|
|
5152
|
+
require_transform.assertSynchronous(run(draft));
|
|
5071
5153
|
} finally {
|
|
5072
5154
|
active = false;
|
|
5073
5155
|
}
|
|
@@ -5132,19 +5214,249 @@ const createSourceRegistry = (scheduler) => {
|
|
|
5132
5214
|
};
|
|
5133
5215
|
};
|
|
5134
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
|
|
5135
5423
|
//#region core/src/projection/runtime.ts
|
|
5136
5424
|
const createProjectionRuntime = (options) => {
|
|
5137
|
-
const scheduler =
|
|
5425
|
+
const scheduler = require_transform.createScheduler(options?.onError ?? (() => void 0));
|
|
5138
5426
|
const sources = createSourceRegistry(scheduler);
|
|
5139
5427
|
const materialized = /* @__PURE__ */ new WeakMap();
|
|
5140
5428
|
const publicViews = /* @__PURE__ */ new WeakMap();
|
|
5141
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
|
+
};
|
|
5142
5454
|
const assertAccess = (definition, requester) => {
|
|
5143
5455
|
scheduler.assertActive();
|
|
5144
5456
|
const owner = definition.owner;
|
|
5145
5457
|
if (owner === void 0) return;
|
|
5146
5458
|
if (owner !== requester) throw new TypeError("Projection belongs to another scope.");
|
|
5147
|
-
if (!requester?.active) throw new
|
|
5459
|
+
if (!requester?.active) throw new require_transform.ProjectionDisposedError();
|
|
5148
5460
|
};
|
|
5149
5461
|
const rememberScoped = (definition, requester) => {
|
|
5150
5462
|
if (requester && definition.owner === requester) requester.materialized.push(definition);
|
|
@@ -5162,7 +5474,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5162
5474
|
...source.write ? { write: source.write } : {}
|
|
5163
5475
|
});
|
|
5164
5476
|
} else {
|
|
5165
|
-
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.");
|
|
5166
5478
|
const dependencies = definition.dependencies.map((dependency) => resolveOutput(dependency, requester));
|
|
5167
5479
|
const processor = createProcessor(scheduler, definition, Object.freeze(dependencies));
|
|
5168
5480
|
result = Object.freeze({
|
|
@@ -5176,14 +5488,14 @@ const createProjectionRuntime = (options) => {
|
|
|
5176
5488
|
return result;
|
|
5177
5489
|
};
|
|
5178
5490
|
const resolveOutput = (projection, requester) => {
|
|
5179
|
-
const ref =
|
|
5491
|
+
const ref = require_transform.projectionRef(projection);
|
|
5180
5492
|
const output = materializeProducer(ref.producer, requester).outputs[ref.output];
|
|
5181
5493
|
if (!output) throw new TypeError("Projection output does not exist.");
|
|
5182
5494
|
return output;
|
|
5183
5495
|
};
|
|
5184
5496
|
const assertOutput = (output) => {
|
|
5185
5497
|
scheduler.assertActive();
|
|
5186
|
-
if (output.owner.disposed) throw new
|
|
5498
|
+
if (output.owner.disposed) throw new require_transform.ProjectionDisposedError();
|
|
5187
5499
|
if (output.owner.fault) throw output.owner.fault;
|
|
5188
5500
|
};
|
|
5189
5501
|
const publicCurrent = (output) => {
|
|
@@ -5193,7 +5505,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5193
5505
|
const cached = publicViews.get(output);
|
|
5194
5506
|
if (cached?.revision === revision) return cached.value;
|
|
5195
5507
|
if (output.kind === "collection") {
|
|
5196
|
-
const value =
|
|
5508
|
+
const value = require_transform.collectionView(current);
|
|
5197
5509
|
publicViews.set(output, {
|
|
5198
5510
|
revision,
|
|
5199
5511
|
value
|
|
@@ -5201,7 +5513,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5201
5513
|
return value;
|
|
5202
5514
|
}
|
|
5203
5515
|
if (isMapLike(current)) {
|
|
5204
|
-
const value =
|
|
5516
|
+
const value = require_transform.snapshotCollectionView(require_transform.mapRead(current));
|
|
5205
5517
|
publicViews.set(output, {
|
|
5206
5518
|
revision,
|
|
5207
5519
|
value
|
|
@@ -5224,6 +5536,19 @@ const createProjectionRuntime = (options) => {
|
|
|
5224
5536
|
subscribe: output.subscribe
|
|
5225
5537
|
});
|
|
5226
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
|
+
};
|
|
5227
5552
|
const makeReadable = (projection, selector, equality, requester) => {
|
|
5228
5553
|
const source = readableSource(projection, requester);
|
|
5229
5554
|
return selector ? createSelectorReadable(source, selector, equality) : createDirectReadable(source);
|
|
@@ -5232,7 +5557,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5232
5557
|
return makeReadable(projection, selector, equality);
|
|
5233
5558
|
}
|
|
5234
5559
|
const inputWrite = (target, requester) => {
|
|
5235
|
-
const ref =
|
|
5560
|
+
const ref = require_transform.projectionRef(target);
|
|
5236
5561
|
if (ref.output !== 0 || ref.producer.kind !== "source") throw new TypeError("Projection is not an input.");
|
|
5237
5562
|
if (ref.producer.source.kind !== "value-input" && ref.producer.source.kind !== "collection-input") throw new TypeError("Projection is not an input.");
|
|
5238
5563
|
const write = materializeProducer(ref.producer, requester).write;
|
|
@@ -5257,13 +5582,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5257
5582
|
scheduler.assertIdle();
|
|
5258
5583
|
state.active = false;
|
|
5259
5584
|
const failures = [];
|
|
5260
|
-
|
|
5261
|
-
state.subscriptions.clear();
|
|
5262
|
-
for (const unsubscribe of subscriptions) try {
|
|
5263
|
-
unsubscribe();
|
|
5264
|
-
} catch (error) {
|
|
5265
|
-
failures.push(error);
|
|
5266
|
-
}
|
|
5585
|
+
disposeScopeConsumers(state, failures);
|
|
5267
5586
|
for (let index = state.materialized.length - 1; index >= 0; index--) {
|
|
5268
5587
|
const definition = state.materialized[index];
|
|
5269
5588
|
const current = materialized.get(definition);
|
|
@@ -5278,25 +5597,26 @@ const createProjectionRuntime = (options) => {
|
|
|
5278
5597
|
}
|
|
5279
5598
|
state.materialized.length = 0;
|
|
5280
5599
|
scopes.delete(state);
|
|
5281
|
-
|
|
5600
|
+
throwCleanupFailures(failures, "Projection scope cleanup failed.");
|
|
5282
5601
|
};
|
|
5283
5602
|
const createScope = () => {
|
|
5284
5603
|
scheduler.assertIdle();
|
|
5285
5604
|
const state = {
|
|
5286
5605
|
active: true,
|
|
5287
5606
|
materialized: [],
|
|
5288
|
-
subscriptions: /* @__PURE__ */ new Set()
|
|
5607
|
+
subscriptions: /* @__PURE__ */ new Set(),
|
|
5608
|
+
items: /* @__PURE__ */ new Map()
|
|
5289
5609
|
};
|
|
5290
5610
|
scopes.add(state);
|
|
5291
5611
|
const assertActive = () => {
|
|
5292
|
-
if (!state.active) throw new
|
|
5612
|
+
if (!state.active) throw new require_transform.ProjectionDisposedError();
|
|
5293
5613
|
scheduler.assertActive();
|
|
5294
5614
|
};
|
|
5295
5615
|
function own(target) {
|
|
5296
5616
|
assertActive();
|
|
5297
5617
|
const projections = [];
|
|
5298
5618
|
const collect = (value) => {
|
|
5299
|
-
if (
|
|
5619
|
+
if (require_transform.isProjection(value)) {
|
|
5300
5620
|
projections.push(value);
|
|
5301
5621
|
return;
|
|
5302
5622
|
}
|
|
@@ -5306,13 +5626,13 @@ const createProjectionRuntime = (options) => {
|
|
|
5306
5626
|
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
5307
5627
|
if (!descriptor?.enumerable || !("value" in descriptor)) throw new TypeError("Projection ownership tree must use enumerable data properties.");
|
|
5308
5628
|
const child = descriptor.value;
|
|
5309
|
-
if (!
|
|
5629
|
+
if (!require_transform.isProjection(child) && !require_record.isPlainObject(child)) throw new TypeError("Projection ownership tree leaves must be projections.");
|
|
5310
5630
|
collect(child);
|
|
5311
5631
|
}
|
|
5312
5632
|
};
|
|
5313
5633
|
collect(target);
|
|
5314
5634
|
if (!projections.length) throw new TypeError("Projection ownership tree must contain a projection.");
|
|
5315
|
-
|
|
5635
|
+
require_transform.ownProjections(projections, state);
|
|
5316
5636
|
return target;
|
|
5317
5637
|
}
|
|
5318
5638
|
function scopeSelect(projection, selector, equality) {
|
|
@@ -5357,6 +5677,10 @@ const createProjectionRuntime = (options) => {
|
|
|
5357
5677
|
return readProjection(projection, state);
|
|
5358
5678
|
},
|
|
5359
5679
|
select: scopeSelect,
|
|
5680
|
+
items: (projection) => {
|
|
5681
|
+
assertActive();
|
|
5682
|
+
return projectionItems(projection, state);
|
|
5683
|
+
},
|
|
5360
5684
|
update: scopeUpdate,
|
|
5361
5685
|
batch: scopeBatch,
|
|
5362
5686
|
dispose: () => disposeScope(state)
|
|
@@ -5365,6 +5689,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5365
5689
|
return Object.freeze({
|
|
5366
5690
|
read: readProjection,
|
|
5367
5691
|
select: selectProjection,
|
|
5692
|
+
items: projectionItems,
|
|
5368
5693
|
update,
|
|
5369
5694
|
batch,
|
|
5370
5695
|
scope: createScope,
|
|
@@ -5374,16 +5699,11 @@ const createProjectionRuntime = (options) => {
|
|
|
5374
5699
|
const failures = [];
|
|
5375
5700
|
for (const scope of scopes) {
|
|
5376
5701
|
scope.active = false;
|
|
5377
|
-
|
|
5378
|
-
scope.subscriptions.clear();
|
|
5379
|
-
for (const unsubscribe of subscriptions) try {
|
|
5380
|
-
unsubscribe();
|
|
5381
|
-
} catch (error) {
|
|
5382
|
-
failures.push(error);
|
|
5383
|
-
}
|
|
5702
|
+
disposeScopeConsumers(scope, failures);
|
|
5384
5703
|
scope.materialized.length = 0;
|
|
5385
5704
|
}
|
|
5386
5705
|
scopes.clear();
|
|
5706
|
+
disposeItemControllers(rootItems, failures);
|
|
5387
5707
|
try {
|
|
5388
5708
|
sources.dispose();
|
|
5389
5709
|
} catch (error) {
|
|
@@ -5394,8 +5714,7 @@ const createProjectionRuntime = (options) => {
|
|
|
5394
5714
|
} catch (error) {
|
|
5395
5715
|
failures.push(error);
|
|
5396
5716
|
}
|
|
5397
|
-
|
|
5398
|
-
if (failures.length) throw new AggregateError(failures, "Projection cleanup failed.");
|
|
5717
|
+
throwCleanupFailures(failures, "Projection cleanup failed.");
|
|
5399
5718
|
}
|
|
5400
5719
|
});
|
|
5401
5720
|
};
|
|
@@ -5403,8 +5722,8 @@ const createProjectionRuntime = (options) => {
|
|
|
5403
5722
|
exports.DocumentDisposedError = DocumentDisposedError;
|
|
5404
5723
|
exports.DocumentReentrancyError = DocumentReentrancyError;
|
|
5405
5724
|
exports.ParseError = ParseError;
|
|
5406
|
-
exports.ProjectionDisposedError =
|
|
5407
|
-
exports.ProjectionError =
|
|
5725
|
+
exports.ProjectionDisposedError = require_transform.ProjectionDisposedError;
|
|
5726
|
+
exports.ProjectionError = require_transform.ProjectionError;
|
|
5408
5727
|
exports.TransactionRejected = TransactionRejected;
|
|
5409
5728
|
exports.createDocument = createDocument;
|
|
5410
5729
|
exports.createProjectionRuntime = createProjectionRuntime;
|