doxum 0.1.21 → 0.1.22
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 +21 -2
- package/dist/{access-BeSV5ve-.js → access-CZXv_DIF.js} +179 -37
- package/dist/access-CZXv_DIF.js.map +1 -0
- package/dist/{access-Bienf28h.cjs → access-D64MFRSm.cjs} +228 -38
- package/dist/access-D64MFRSm.cjs.map +1 -0
- package/dist/{advanced-COFJtWMT.js → advanced-BTStm2cW.js} +29 -25
- package/dist/advanced-BTStm2cW.js.map +1 -0
- package/dist/{advanced-FgdTwu5b.d.ts → advanced-CqrR_4dh.d.ts} +3 -3
- package/dist/{advanced-DfKc7TZR.d.cts → advanced-CyR5o3Xr.d.cts} +3 -3
- package/dist/{advanced-BRL5pN0y.cjs → advanced-GKZ-CZ6a.cjs} +29 -25
- package/dist/advanced-GKZ-CZ6a.cjs.map +1 -0
- package/dist/advanced.cjs +1 -1
- package/dist/advanced.d.cts +1 -1
- package/dist/advanced.d.ts +1 -1
- package/dist/advanced.js +1 -1
- package/dist/{contract-KG5XnQZt.d.ts → contract-CRrZ-3sF.d.cts} +64 -34
- package/dist/{contract-CdmDHRUq.d.cts → contract-CxiNcqMn.d.ts} +64 -34
- package/dist/{driver-Djr3BVvo.cjs → driver-BC06FEMF.cjs} +2 -2
- package/dist/{driver-Djr3BVvo.cjs.map → driver-BC06FEMF.cjs.map} +1 -1
- package/dist/{driver-CLYJOGDQ.js → driver-BqRjwFmU.js} +2 -2
- package/dist/{driver-CLYJOGDQ.js.map → driver-BqRjwFmU.js.map} +1 -1
- package/dist/index.cjs +480 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +478 -115
- package/dist/index.js.map +1 -1
- package/dist/{integration-Z6MsfFI7.cjs → integration-BqDw7e2w.cjs} +3 -4
- package/dist/{integration-Z6MsfFI7.cjs.map → integration-BqDw7e2w.cjs.map} +1 -1
- package/dist/{integration-C4Isp8lJ.js → integration-CSXaF8HP.js} +2 -3
- package/dist/{integration-C4Isp8lJ.js.map → integration-CSXaF8HP.js.map} +1 -1
- package/dist/integration.cjs +5 -4
- package/dist/integration.d.cts +1 -1
- package/dist/integration.d.ts +1 -1
- package/dist/integration.js +4 -3
- package/dist/{issue-W0hT8u8D.js → issue-9U6YLYz3.js} +11 -6
- package/dist/issue-9U6YLYz3.js.map +1 -0
- package/dist/{issue-DjK1xp5K.cjs → issue-Bo0FIO3b.cjs} +11 -6
- package/dist/issue-Bo0FIO3b.cjs.map +1 -0
- package/dist/local-sync.cjs +2 -2
- package/dist/local-sync.d.cts +1 -1
- package/dist/local-sync.d.ts +1 -1
- package/dist/local-sync.js +2 -2
- package/dist/{notification-Bkjau6B6.js → notification-CrZHVx3a.js} +3 -59
- package/dist/notification-CrZHVx3a.js.map +1 -0
- package/dist/{notification-DJfouMEb.cjs → notification-DFoA9bQY.cjs} +3 -77
- package/dist/notification-DFoA9bQY.cjs.map +1 -0
- package/dist/react.cjs +4 -3
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +3 -2
- package/dist/react.js.map +1 -1
- package/package.json +1 -1
- package/skills/doxum-runtime/SKILL.md +5 -0
- package/skills/doxum-runtime/references/guide.en.md +9 -0
- package/skills/doxum-runtime/references/guide.zh-CN.md +8 -0
- package/skills/doxum-runtime/references/projections.en.md +5 -0
- package/skills/doxum-runtime/references/projections.zh-CN.md +5 -0
- package/dist/access-BeSV5ve-.js.map +0 -1
- package/dist/access-Bienf28h.cjs.map +0 -1
- package/dist/advanced-BRL5pN0y.cjs.map +0 -1
- package/dist/advanced-COFJtWMT.js.map +0 -1
- package/dist/issue-DjK1xp5K.cjs.map +0 -1
- package/dist/issue-W0hT8u8D.js.map +0 -1
- package/dist/notification-Bkjau6B6.js.map +0 -1
- package/dist/notification-DJfouMEb.cjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { A as install, B as profile, E as equal, F as matches, L as remove, O as indexedKeys, R as toArray, _ as resolveTreeContainer, a as compiledShape, c as debugKey, d as overlaps, f as read$1, g as resolveLocated, h as resolveContainer, i as AddressIndex, k as insert, l as memberKey, n as fail, o as contains, p as resolveAddress, r as invalidValue, s as createAddressResolver, t as MutationRejected, u as nodeAt, v as resolveValue } from "./issue-
|
|
2
|
-
import { a as DocumentReentrancyError, c as
|
|
3
|
-
import { C as list, D as table, E as optional, O as tree, S as field, T as object, _ as createScheduler, a as observe, b as collectionEntryNode, c as projectionRef, d as createCollectionOutput, f as mapRead, g as assertSynchronous, h as assertScope, i as isProjection, k as variant, l as collectionChangeBetween, m as stageCollectionRead, n as derive, o as ownProjection, p as snapshotCollectionView, r as input, s as producerOf, t as incremental, u as collectionView, v as ProjectionDisposedError, w as map, x as compilePath, y as ProjectionError } from "./advanced-
|
|
4
|
-
import {
|
|
5
|
-
import { n as
|
|
1
|
+
import { A as install, B as profile, C as validate, E as equal, F as matches, L as remove, O as indexedKeys, P as listSequence, R as toArray, T as isRecord, _ as resolveTreeContainer, a as compiledShape, c as debugKey, d as overlaps, f as read$1, g as resolveLocated, h as resolveContainer, i as AddressIndex, k as insert, l as memberKey, n as fail, o as contains, p as resolveAddress, r as invalidValue, s as createAddressResolver, t as MutationRejected, u as nodeAt, v as resolveValue } from "./issue-9U6YLYz3.js";
|
|
2
|
+
import { C as equalValue, S as equalTreeNode, _ as checkKey, a as DocumentReentrancyError, b as copyTreeNode, c as replace, d as affected, f as belongs, g as ParseError, h as visitChangedLocations, i as DocumentDisposedError, l as snapshot, n as bindRuntimeAccess, o as TransactionRejected, p as indexedAddress, r as readWith, s as createAccess, t as accessOf, u as SubscriptionIndex, v as checkTreePayload, w as parse, x as copyValue, y as checkValue } from "./access-CZXv_DIF.js";
|
|
3
|
+
import { C as list, D as table, E as optional, O as tree, S as field, T as object, _ as createScheduler, a as observe, b as collectionEntryNode, c as projectionRef, d as createCollectionOutput, f as mapRead, g as assertSynchronous, h as assertScope, i as isProjection, k as variant, l as collectionChangeBetween, m as stageCollectionRead, n as derive, o as ownProjection, p as snapshotCollectionView, r as input, s as producerOf, t as incremental, u as collectionView, v as ProjectionDisposedError, w as map, x as compilePath, y as ProjectionError } from "./advanced-BTStm2cW.js";
|
|
4
|
+
import { n as bindRuntimeDriver, o as decodeChanges, r as disposeRuntimeDriver, s as sealChanges, t as assertRuntimeWritable } from "./driver-BqRjwFmU.js";
|
|
5
|
+
import { a as documentReadableOwner, i as disposeNotification, l as subscribeRoot, n as bindDocumentReadable, o as notify, r as createNotification, s as shareNotification, t as attachProjection, u as subscribeTargets } from "./notification-CrZHVx3a.js";
|
|
6
6
|
//#region core/src/impact.ts
|
|
7
7
|
const queries = /* @__PURE__ */ new WeakMap();
|
|
8
|
-
const affectsTarget = (impact, value) => queries.get(impact).affects(value);
|
|
9
|
-
const collectionImpact = (impact, selector) => queries.get(impact).collection(selector);
|
|
10
8
|
const createImpact = (schema, changes) => {
|
|
11
9
|
const reset = changes.changes.some((change) => change.kind === "reset");
|
|
12
10
|
let values;
|
|
@@ -16,10 +14,7 @@ const createImpact = (schema, changes) => {
|
|
|
16
14
|
profile.impact.index();
|
|
17
15
|
values = new AddressIndex();
|
|
18
16
|
orders = new AddressIndex();
|
|
19
|
-
|
|
20
|
-
for (const member of change.members) values.add(change.at, true, member.key);
|
|
21
|
-
if (change.order) orders.add(change.at, true);
|
|
22
|
-
} else if (change.kind === "tree") values.add(change.at, true);
|
|
17
|
+
visitChangedLocations(changes, (at) => values.add(at, true), (at) => orders.add(at, true));
|
|
23
18
|
}
|
|
24
19
|
return values;
|
|
25
20
|
};
|
|
@@ -33,7 +28,7 @@ const createImpact = (schema, changes) => {
|
|
|
33
28
|
},
|
|
34
29
|
collection(selector) {
|
|
35
30
|
if (selector.schema !== schema) throw new TypeError("Collection belongs to another root model.");
|
|
36
|
-
const key = JSON.stringify(selector
|
|
31
|
+
const key = JSON.stringify(indexedAddress(selector)), cached = cache.get(key);
|
|
37
32
|
if (cached) return cached;
|
|
38
33
|
if (reset) {
|
|
39
34
|
const result = { kind: "reset" };
|
|
@@ -41,6 +36,7 @@ const createImpact = (schema, changes) => {
|
|
|
41
36
|
return result;
|
|
42
37
|
}
|
|
43
38
|
const at = selector.address;
|
|
39
|
+
const tree = selector.tree;
|
|
44
40
|
const added = /* @__PURE__ */ new Set(), removed = /* @__PURE__ */ new Set(), updated = /* @__PURE__ */ new Set();
|
|
45
41
|
let orderChanged = false;
|
|
46
42
|
for (const change of changes.changes) {
|
|
@@ -70,6 +66,12 @@ const createImpact = (schema, changes) => {
|
|
|
70
66
|
continue;
|
|
71
67
|
}
|
|
72
68
|
if (change.kind === "tree" && contains(change.at, at)) {
|
|
69
|
+
if (tree?.kind === "nodes" && change.at.length === at.length && contains(at, change.at)) {
|
|
70
|
+
for (const node of change.nodes) if (node.kind === "added") added.add(node.id);
|
|
71
|
+
else if (node.kind === "removed") removed.add(node.id);
|
|
72
|
+
else updated.add(node.id);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
73
75
|
const result = { kind: "reset" };
|
|
74
76
|
cache.set(key, result);
|
|
75
77
|
return result;
|
|
@@ -308,10 +310,6 @@ const forgetMember = (group, key) => {
|
|
|
308
310
|
}
|
|
309
311
|
};
|
|
310
312
|
const emptyGroup = (group) => !group.order && (group.layout === "fixed" ? !group.members.some(Boolean) : !group.members?.size);
|
|
311
|
-
const copyNode = (node) => ({
|
|
312
|
-
...node,
|
|
313
|
-
children: [...node.children]
|
|
314
|
-
});
|
|
315
313
|
const transition = (node, key, beforePresent, before, afterPresent, after) => {
|
|
316
314
|
if (beforePresent === afterPresent && (!beforePresent || Object.is(before, after))) return;
|
|
317
315
|
if (node.kind !== "field" && beforePresent === afterPresent && equalValue(node, before, after)) return;
|
|
@@ -402,7 +400,7 @@ const restore = (fact, schema, root, skip = 0) => {
|
|
|
402
400
|
const tree = location.value;
|
|
403
401
|
if (fact.before === null) delete tree.rootId;
|
|
404
402
|
else tree.rootId = fact.before;
|
|
405
|
-
for (const [id, node] of fact.nodes) installMember(tree.nodes, id, node !== void 0, node &&
|
|
403
|
+
for (const [id, node] of fact.nodes) installMember(tree.nodes, id, node !== void 0, node && copyTreeNode(node));
|
|
406
404
|
}
|
|
407
405
|
return root;
|
|
408
406
|
};
|
|
@@ -441,17 +439,16 @@ const sealMembers = (fact, changes) => {
|
|
|
441
439
|
const sealTree = (fact, state, changes) => {
|
|
442
440
|
const location = resolveValue(state.schema, state.document, fact.at);
|
|
443
441
|
const tree = location?.value;
|
|
444
|
-
|
|
445
|
-
if (node?.kind !== "tree") throw new Error("Tree schema disappeared before sealing.");
|
|
442
|
+
if ((location?.node)?.kind !== "tree") throw new Error("Tree schema disappeared before sealing.");
|
|
446
443
|
const nodes = [];
|
|
447
444
|
for (const [id, before] of fact.nodes) {
|
|
448
445
|
const after = Object.hasOwn(tree.nodes, id) ? tree.nodes[id] : void 0;
|
|
449
446
|
if (!before && !after) continue;
|
|
450
|
-
if (before && after &&
|
|
447
|
+
if (before && after && equalTreeNode(before, after)) continue;
|
|
451
448
|
nodes.push(!before ? {
|
|
452
449
|
id,
|
|
453
450
|
kind: "added",
|
|
454
|
-
after:
|
|
451
|
+
after: copyTreeNode(after)
|
|
455
452
|
} : !after ? {
|
|
456
453
|
id,
|
|
457
454
|
kind: "removed",
|
|
@@ -460,7 +457,7 @@ const sealTree = (fact, state, changes) => {
|
|
|
460
457
|
id,
|
|
461
458
|
kind: "updated",
|
|
462
459
|
before,
|
|
463
|
-
after:
|
|
460
|
+
after: copyTreeNode(after)
|
|
464
461
|
});
|
|
465
462
|
}
|
|
466
463
|
const after = tree.rootId ?? null;
|
|
@@ -642,7 +639,7 @@ var ChangeRecorder = class {
|
|
|
642
639
|
}
|
|
643
640
|
if (id !== void 0 && !fact.nodes.has(id)) {
|
|
644
641
|
profile.recorder("treeNodes");
|
|
645
|
-
fact.nodes.set(id, Object.hasOwn(tree.nodes, id) ?
|
|
642
|
+
fact.nodes.set(id, Object.hasOwn(tree.nodes, id) ? copyTreeNode(tree.nodes[id]) : void 0);
|
|
646
643
|
}
|
|
647
644
|
}
|
|
648
645
|
rollback() {
|
|
@@ -829,12 +826,13 @@ function apply(session, changes, direction) {
|
|
|
829
826
|
else current.rootId = root;
|
|
830
827
|
for (const item of change.nodes) {
|
|
831
828
|
const next = side === "after" ? item.kind !== "removed" ? item.after : void 0 : item.kind !== "added" ? item.before : void 0;
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
}
|
|
829
|
+
if (next !== void 0) {
|
|
830
|
+
const issue = checkTreePayload(node.value, next, change.at.concat(item.id));
|
|
831
|
+
if (issue) invalidValue(issue);
|
|
832
|
+
}
|
|
833
|
+
installMember(current.nodes, item.id, next !== void 0, next && copyTreeNode(next));
|
|
836
834
|
}
|
|
837
|
-
|
|
835
|
+
if (!validate(current)) return fail(change.at, "invalid-tree", "Invalid tree structure.");
|
|
838
836
|
session.invalidate();
|
|
839
837
|
}
|
|
840
838
|
}
|
|
@@ -1429,12 +1427,220 @@ const createSelectorReadable = (source, selector, equality = Object.is) => {
|
|
|
1429
1427
|
});
|
|
1430
1428
|
};
|
|
1431
1429
|
//#endregion
|
|
1430
|
+
//#region core/src/projection/document-materialization.ts
|
|
1431
|
+
const createDocumentDirty = () => ({
|
|
1432
|
+
replace: false,
|
|
1433
|
+
order: false,
|
|
1434
|
+
treeRoot: false,
|
|
1435
|
+
treeNodes: /* @__PURE__ */ new Set(),
|
|
1436
|
+
children: /* @__PURE__ */ new Map()
|
|
1437
|
+
});
|
|
1438
|
+
const clearDocumentDirty = (dirty) => {
|
|
1439
|
+
dirty.replace = false;
|
|
1440
|
+
dirty.order = false;
|
|
1441
|
+
dirty.treeRoot = false;
|
|
1442
|
+
dirty.treeNodes.clear();
|
|
1443
|
+
dirty.children.clear();
|
|
1444
|
+
};
|
|
1445
|
+
const hasDocumentDirty = (dirty) => dirty.replace || dirty.order || dirty.treeRoot || dirty.treeNodes.size > 0 || dirty.children.size > 0;
|
|
1446
|
+
const locate = (dirty, path) => {
|
|
1447
|
+
let current = dirty;
|
|
1448
|
+
for (const key of path) {
|
|
1449
|
+
if (current.replace) return void 0;
|
|
1450
|
+
let child = current.children.get(key);
|
|
1451
|
+
if (!child) {
|
|
1452
|
+
child = createDocumentDirty();
|
|
1453
|
+
current.children.set(key, child);
|
|
1454
|
+
}
|
|
1455
|
+
current = child;
|
|
1456
|
+
}
|
|
1457
|
+
return current;
|
|
1458
|
+
};
|
|
1459
|
+
const markDocumentReplace = (dirty, path) => {
|
|
1460
|
+
const current = locate(dirty, path);
|
|
1461
|
+
if (!current) return;
|
|
1462
|
+
current.replace = true;
|
|
1463
|
+
current.order = false;
|
|
1464
|
+
current.treeRoot = false;
|
|
1465
|
+
current.treeNodes.clear();
|
|
1466
|
+
current.children.clear();
|
|
1467
|
+
};
|
|
1468
|
+
const markDocumentOrder = (dirty, path) => {
|
|
1469
|
+
const current = locate(dirty, path);
|
|
1470
|
+
if (current && !current.replace) current.order = true;
|
|
1471
|
+
};
|
|
1472
|
+
const markDocumentTree = (dirty, path, root, nodes) => {
|
|
1473
|
+
const current = locate(dirty, path);
|
|
1474
|
+
if (!current || current.replace) return;
|
|
1475
|
+
current.treeRoot ||= root;
|
|
1476
|
+
for (const id of nodes) current.treeNodes.add(id);
|
|
1477
|
+
};
|
|
1478
|
+
const equivalent = (node, left, right) => node.kind === "field" ? Object.is(left, right) : equalValue(node, left, right);
|
|
1479
|
+
const replaceValue = (node, previous, current) => equivalent(node, previous, current) ? previous : copyValue(node, current);
|
|
1480
|
+
const cloneRecord = (value) => {
|
|
1481
|
+
profile.copy.shallowRecord(Object.getOwnPropertyNames(value).length);
|
|
1482
|
+
return Object.create(Object.getPrototypeOf(value), Object.getOwnPropertyDescriptors(value));
|
|
1483
|
+
};
|
|
1484
|
+
const materializeRecordChild = (node, previous, current, key, dirty) => {
|
|
1485
|
+
const before = Object.hasOwn(previous, key);
|
|
1486
|
+
const after = Object.hasOwn(current, key);
|
|
1487
|
+
if (!before && !after) return { changed: false };
|
|
1488
|
+
if (!after) return {
|
|
1489
|
+
changed: true,
|
|
1490
|
+
present: false,
|
|
1491
|
+
value: void 0
|
|
1492
|
+
};
|
|
1493
|
+
const next = before ? materializeDocumentValue(node, previous[key], current[key], dirty) : copyValue(node, current[key]);
|
|
1494
|
+
if (before && next === previous[key]) return { changed: false };
|
|
1495
|
+
return {
|
|
1496
|
+
changed: true,
|
|
1497
|
+
present: true,
|
|
1498
|
+
value: next
|
|
1499
|
+
};
|
|
1500
|
+
};
|
|
1501
|
+
const materializeObject = (node, previous, current, dirty) => {
|
|
1502
|
+
if (node.kind === "variant" && previous[node.tag] !== current[node.tag]) return replaceValue(node, previous, current);
|
|
1503
|
+
const layout = compiledShape(node, current);
|
|
1504
|
+
if (!layout) return replaceValue(node, previous, current);
|
|
1505
|
+
let result;
|
|
1506
|
+
for (const [key, childDirty] of dirty.children) {
|
|
1507
|
+
const member = layout.members.get(key);
|
|
1508
|
+
if (!member) return replaceValue(node, previous, current);
|
|
1509
|
+
const child = materializeRecordChild(member.node, previous, current, key, childDirty);
|
|
1510
|
+
if (!child.changed) continue;
|
|
1511
|
+
result ??= cloneRecord(previous);
|
|
1512
|
+
installMember(result, key, child.present, child.value);
|
|
1513
|
+
}
|
|
1514
|
+
return result ?? previous;
|
|
1515
|
+
};
|
|
1516
|
+
const materializeMap = (node, previous, current, dirty) => {
|
|
1517
|
+
let result;
|
|
1518
|
+
for (const [key, childDirty] of dirty.children) {
|
|
1519
|
+
const child = materializeRecordChild(node.value, previous, current, key, childDirty);
|
|
1520
|
+
if (!child.changed) continue;
|
|
1521
|
+
result ??= cloneRecord(previous);
|
|
1522
|
+
installMember(result, key, child.present, child.value);
|
|
1523
|
+
}
|
|
1524
|
+
return result ?? previous;
|
|
1525
|
+
};
|
|
1526
|
+
const materializeTable = (node, previous, current, dirty) => {
|
|
1527
|
+
if (!isRecord(previous.byId) || !isRecord(current.byId)) return replaceValue(node, previous, current);
|
|
1528
|
+
const beforeById = previous.byId;
|
|
1529
|
+
const afterById = current.byId;
|
|
1530
|
+
let result;
|
|
1531
|
+
let nextById;
|
|
1532
|
+
for (const [key, childDirty] of dirty.children) {
|
|
1533
|
+
const child = materializeRecordChild(node.value, beforeById, afterById, key, childDirty);
|
|
1534
|
+
if (!child.changed) continue;
|
|
1535
|
+
nextById ??= cloneRecord(beforeById);
|
|
1536
|
+
installMember(nextById, key, child.present, child.value);
|
|
1537
|
+
}
|
|
1538
|
+
if (nextById) {
|
|
1539
|
+
result = cloneRecord(previous);
|
|
1540
|
+
installMember(result, "byId", true, nextById);
|
|
1541
|
+
}
|
|
1542
|
+
if (dirty.order) {
|
|
1543
|
+
const beforeIds = previous.ids;
|
|
1544
|
+
const afterIds = current.ids;
|
|
1545
|
+
if (!Array.isArray(beforeIds) || !Array.isArray(afterIds)) return replaceValue(node, previous, current);
|
|
1546
|
+
if (!equal(beforeIds, afterIds)) {
|
|
1547
|
+
result ??= cloneRecord(previous);
|
|
1548
|
+
installMember(result, "ids", true, [...afterIds]);
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
return result ?? previous;
|
|
1552
|
+
};
|
|
1553
|
+
const materializeList = (node, previous, current, dirty) => {
|
|
1554
|
+
if (dirty.order) {
|
|
1555
|
+
const before = listSequence(previous, node.keyOf);
|
|
1556
|
+
const after = listSequence(current, node.keyOf);
|
|
1557
|
+
let changed = !equal(before.order, after.order);
|
|
1558
|
+
const result = new Array(after.order.length);
|
|
1559
|
+
for (let index = 0; index < after.order.length; index++) {
|
|
1560
|
+
const key = after.order[index];
|
|
1561
|
+
const currentValue = after.values.get(key);
|
|
1562
|
+
if (!before.values.has(key)) {
|
|
1563
|
+
changed = true;
|
|
1564
|
+
result[index] = currentValue;
|
|
1565
|
+
continue;
|
|
1566
|
+
}
|
|
1567
|
+
const previousValue = before.values.get(key);
|
|
1568
|
+
const childDirty = dirty.children.get(key);
|
|
1569
|
+
const next = childDirty ? materializeDocumentValue(node.value, previousValue, currentValue, childDirty) : previousValue;
|
|
1570
|
+
changed ||= next !== previousValue;
|
|
1571
|
+
result[index] = next;
|
|
1572
|
+
}
|
|
1573
|
+
return changed ? result : previous;
|
|
1574
|
+
}
|
|
1575
|
+
let result;
|
|
1576
|
+
const before = indexedKeys(previous, node.keyOf);
|
|
1577
|
+
const after = indexedKeys(current, node.keyOf);
|
|
1578
|
+
for (const [key, childDirty] of dirty.children) {
|
|
1579
|
+
const previousIndex = before.index(key);
|
|
1580
|
+
const currentIndex = after.index(key);
|
|
1581
|
+
if (previousIndex < 0 || currentIndex < 0) return replaceValue(node, previous, current);
|
|
1582
|
+
const next = materializeDocumentValue(node.value, previous[previousIndex], current[currentIndex], childDirty);
|
|
1583
|
+
if (next === previous[previousIndex] && previousIndex === currentIndex) continue;
|
|
1584
|
+
result ??= [...previous];
|
|
1585
|
+
result[currentIndex] = next;
|
|
1586
|
+
}
|
|
1587
|
+
return result ?? previous;
|
|
1588
|
+
};
|
|
1589
|
+
const materializeTree = (node, previous, current, dirty) => {
|
|
1590
|
+
if (!isRecord(previous.nodes) || !isRecord(current.nodes)) return replaceValue(node, previous, current);
|
|
1591
|
+
let result;
|
|
1592
|
+
if (dirty.treeRoot) {
|
|
1593
|
+
const beforePresent = Object.hasOwn(previous, "rootId");
|
|
1594
|
+
const afterPresent = Object.hasOwn(current, "rootId");
|
|
1595
|
+
if (beforePresent !== afterPresent || !Object.is(previous.rootId, current.rootId)) {
|
|
1596
|
+
result = cloneRecord(previous);
|
|
1597
|
+
installMember(result, "rootId", afterPresent, current.rootId);
|
|
1598
|
+
}
|
|
1599
|
+
}
|
|
1600
|
+
let nodes;
|
|
1601
|
+
for (const id of dirty.treeNodes) {
|
|
1602
|
+
const beforePresent = Object.hasOwn(previous.nodes, id);
|
|
1603
|
+
const afterPresent = Object.hasOwn(current.nodes, id);
|
|
1604
|
+
const before = beforePresent ? previous.nodes[id] : void 0;
|
|
1605
|
+
const after = afterPresent ? current.nodes[id] : void 0;
|
|
1606
|
+
if (beforePresent === afterPresent && equalTreeNode(before, after)) continue;
|
|
1607
|
+
nodes ??= cloneRecord(previous.nodes);
|
|
1608
|
+
installMember(nodes, id, afterPresent, after && copyTreeNode(after));
|
|
1609
|
+
}
|
|
1610
|
+
if (nodes) {
|
|
1611
|
+
result ??= cloneRecord(previous);
|
|
1612
|
+
installMember(result, "nodes", true, nodes);
|
|
1613
|
+
}
|
|
1614
|
+
return result ?? previous;
|
|
1615
|
+
};
|
|
1616
|
+
const materializeDocumentValue = (node, previous, current, dirty) => {
|
|
1617
|
+
if (!hasDocumentDirty(dirty)) return previous;
|
|
1618
|
+
if (dirty.replace || previous === void 0 || current === void 0 || node.kind === "field") return replaceValue(node, previous, current);
|
|
1619
|
+
if (node.kind === "object" || node.kind === "variant") {
|
|
1620
|
+
if (!isRecord(previous) || !isRecord(current)) return replaceValue(node, previous, current);
|
|
1621
|
+
return materializeObject(node, previous, current, dirty);
|
|
1622
|
+
}
|
|
1623
|
+
if (node.kind === "map") {
|
|
1624
|
+
if (!isRecord(previous) || !isRecord(current)) return replaceValue(node, previous, current);
|
|
1625
|
+
return materializeMap(node, previous, current, dirty);
|
|
1626
|
+
}
|
|
1627
|
+
if (node.kind === "table") {
|
|
1628
|
+
if (!isRecord(previous) || !isRecord(current)) return replaceValue(node, previous, current);
|
|
1629
|
+
return materializeTable(node, previous, current, dirty);
|
|
1630
|
+
}
|
|
1631
|
+
if (node.kind === "list") {
|
|
1632
|
+
if (!Array.isArray(previous) || !Array.isArray(current)) return replaceValue(node, previous, current);
|
|
1633
|
+
return materializeList(node, previous, current, dirty);
|
|
1634
|
+
}
|
|
1635
|
+
if (!isRecord(previous) || !isRecord(current)) return replaceValue(node, previous, current);
|
|
1636
|
+
return materializeTree(node, previous, current, dirty);
|
|
1637
|
+
};
|
|
1638
|
+
//#endregion
|
|
1432
1639
|
//#region core/src/projection/source.ts
|
|
1433
1640
|
const sourceError = (source, cause) => new ProjectionError("source", source.name, [source.outputs[0].revision()], cause);
|
|
1434
|
-
const createValueBoundary = (scheduler, name, initial, equality) => {
|
|
1641
|
+
const createValueBoundary = (scheduler, name, initial, equality, prepare, clearSource) => {
|
|
1435
1642
|
const state = createValueOutput();
|
|
1436
1643
|
let boundary;
|
|
1437
|
-
let latest = initial;
|
|
1438
1644
|
let pendingReset = false;
|
|
1439
1645
|
let pendingCause;
|
|
1440
1646
|
let contextCause;
|
|
@@ -1476,7 +1682,8 @@ const createValueBoundary = (scheduler, name, initial, equality) => {
|
|
|
1476
1682
|
contextCause = cause;
|
|
1477
1683
|
let active = true;
|
|
1478
1684
|
try {
|
|
1479
|
-
state.begin(() => active, pendingReset)
|
|
1685
|
+
const evaluation = state.begin(() => active, pendingReset);
|
|
1686
|
+
evaluation.output.set(prepare(evaluation.previous));
|
|
1480
1687
|
const changed = state.seal(pendingReset, equality);
|
|
1481
1688
|
recovering = false;
|
|
1482
1689
|
return {
|
|
@@ -1489,11 +1696,13 @@ const createValueBoundary = (scheduler, name, initial, equality) => {
|
|
|
1489
1696
|
},
|
|
1490
1697
|
publish: state.publish,
|
|
1491
1698
|
clear: () => {
|
|
1699
|
+
const failed = boundary.fault !== void 0;
|
|
1492
1700
|
state.clear();
|
|
1493
1701
|
pendingReset = false;
|
|
1494
1702
|
pendingCause = void 0;
|
|
1495
1703
|
contextCause = void 0;
|
|
1496
1704
|
recovering = false;
|
|
1705
|
+
clearSource(failed);
|
|
1497
1706
|
},
|
|
1498
1707
|
release: () => {
|
|
1499
1708
|
cleanup?.();
|
|
@@ -1529,9 +1738,6 @@ const createValueBoundary = (scheduler, name, initial, equality) => {
|
|
|
1529
1738
|
return {
|
|
1530
1739
|
producer: boundary,
|
|
1531
1740
|
output,
|
|
1532
|
-
setLatest: (value) => {
|
|
1533
|
-
latest = value;
|
|
1534
|
-
},
|
|
1535
1741
|
mark,
|
|
1536
1742
|
fail,
|
|
1537
1743
|
detach: (next) => {
|
|
@@ -1539,7 +1745,7 @@ const createValueBoundary = (scheduler, name, initial, equality) => {
|
|
|
1539
1745
|
}
|
|
1540
1746
|
};
|
|
1541
1747
|
};
|
|
1542
|
-
const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
|
|
1748
|
+
const createCollectionBoundary = (scheduler, name, readLatest, equality, clearSource) => {
|
|
1543
1749
|
const state = createCollectionOutput();
|
|
1544
1750
|
let boundary;
|
|
1545
1751
|
let pendingReset = false;
|
|
@@ -1586,7 +1792,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
|
|
|
1586
1792
|
contextCause = cause;
|
|
1587
1793
|
let active = true;
|
|
1588
1794
|
try {
|
|
1589
|
-
const changed = stageCollectionRead(state, readLatest(() => active), () => active, {
|
|
1795
|
+
const changed = stageCollectionRead(state, readLatest(() => active, state.current(() => void 0)), () => active, {
|
|
1590
1796
|
reset: pendingReset,
|
|
1591
1797
|
...fullScan ? {} : { candidates },
|
|
1592
1798
|
orderMayChange,
|
|
@@ -1603,6 +1809,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
|
|
|
1603
1809
|
},
|
|
1604
1810
|
publish: state.publish,
|
|
1605
1811
|
clear: () => {
|
|
1812
|
+
const failed = boundary.fault !== void 0;
|
|
1606
1813
|
state.clear();
|
|
1607
1814
|
pendingReset = false;
|
|
1608
1815
|
pendingCause = void 0;
|
|
@@ -1611,6 +1818,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
|
|
|
1611
1818
|
fullScan = false;
|
|
1612
1819
|
orderMayChange = false;
|
|
1613
1820
|
candidates.clear();
|
|
1821
|
+
clearSource(failed);
|
|
1614
1822
|
},
|
|
1615
1823
|
release: () => {
|
|
1616
1824
|
cleanup?.();
|
|
@@ -1622,7 +1830,7 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
|
|
|
1622
1830
|
scheduler.initialize(() => {
|
|
1623
1831
|
let active = true;
|
|
1624
1832
|
try {
|
|
1625
|
-
stageCollectionRead(state, readLatest(() => active), () => active, {
|
|
1833
|
+
stageCollectionRead(state, readLatest(() => active, state.current(() => void 0)), () => active, {
|
|
1626
1834
|
reset: true,
|
|
1627
1835
|
isEqual: equality
|
|
1628
1836
|
});
|
|
@@ -1658,6 +1866,109 @@ const createCollectionBoundary = (scheduler, name, readLatest, equality) => {
|
|
|
1658
1866
|
}
|
|
1659
1867
|
};
|
|
1660
1868
|
};
|
|
1869
|
+
const documentRootTarget = Object.freeze({
|
|
1870
|
+
kind: "value",
|
|
1871
|
+
at: Object.freeze([])
|
|
1872
|
+
});
|
|
1873
|
+
const createCollectionPending = () => ({
|
|
1874
|
+
reset: false,
|
|
1875
|
+
order: false,
|
|
1876
|
+
entries: /* @__PURE__ */ new Map()
|
|
1877
|
+
});
|
|
1878
|
+
const resetCollectionPending = (pending) => {
|
|
1879
|
+
pending.reset = true;
|
|
1880
|
+
pending.order = false;
|
|
1881
|
+
pending.entries.clear();
|
|
1882
|
+
};
|
|
1883
|
+
const clearCollectionPending = (pending) => {
|
|
1884
|
+
pending.reset = false;
|
|
1885
|
+
pending.order = false;
|
|
1886
|
+
pending.entries.clear();
|
|
1887
|
+
};
|
|
1888
|
+
const dirtyEntry = (pending, key) => {
|
|
1889
|
+
let dirty = pending.entries.get(key);
|
|
1890
|
+
if (!dirty) {
|
|
1891
|
+
dirty = createDocumentDirty();
|
|
1892
|
+
pending.entries.set(key, dirty);
|
|
1893
|
+
}
|
|
1894
|
+
return dirty;
|
|
1895
|
+
};
|
|
1896
|
+
const mergeValueChanges = (selector, dirty, changes) => {
|
|
1897
|
+
if (selector.tree) return;
|
|
1898
|
+
const at = selector.address;
|
|
1899
|
+
for (const change of changes.changes) {
|
|
1900
|
+
if (change.kind === "reset") {
|
|
1901
|
+
markDocumentReplace(dirty, []);
|
|
1902
|
+
return;
|
|
1903
|
+
}
|
|
1904
|
+
if (change.kind === "members") {
|
|
1905
|
+
for (const member of change.members) {
|
|
1906
|
+
const changed = [...change.at, member.key];
|
|
1907
|
+
if (contains(changed, at)) markDocumentReplace(dirty, []);
|
|
1908
|
+
else if (contains(at, changed)) markDocumentReplace(dirty, changed.slice(at.length));
|
|
1909
|
+
}
|
|
1910
|
+
if (change.order && contains(at, change.at)) markDocumentOrder(dirty, change.at.slice(at.length));
|
|
1911
|
+
continue;
|
|
1912
|
+
}
|
|
1913
|
+
if (contains(at, change.at)) markDocumentTree(dirty, change.at.slice(at.length), change.before !== change.after, change.nodes.map((node) => node.id));
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
const mergeCollectionChanges = (selector, pending, changes) => {
|
|
1917
|
+
if (pending.reset) return;
|
|
1918
|
+
const at = selector.address;
|
|
1919
|
+
for (const change of changes.changes) {
|
|
1920
|
+
if (change.kind === "reset") {
|
|
1921
|
+
resetCollectionPending(pending);
|
|
1922
|
+
return;
|
|
1923
|
+
}
|
|
1924
|
+
if (selector.tree?.kind === "nodes") {
|
|
1925
|
+
if (change.kind === "members") {
|
|
1926
|
+
for (const member of change.members) if (contains([...change.at, member.key], at)) {
|
|
1927
|
+
resetCollectionPending(pending);
|
|
1928
|
+
return;
|
|
1929
|
+
}
|
|
1930
|
+
} else if (change.at.length === at.length && contains(change.at, at)) for (const node of change.nodes) {
|
|
1931
|
+
markDocumentReplace(dirtyEntry(pending, node.id), []);
|
|
1932
|
+
pending.order ||= node.kind !== "updated";
|
|
1933
|
+
}
|
|
1934
|
+
else if (contains(change.at, at)) {
|
|
1935
|
+
resetCollectionPending(pending);
|
|
1936
|
+
return;
|
|
1937
|
+
}
|
|
1938
|
+
continue;
|
|
1939
|
+
}
|
|
1940
|
+
if (change.kind === "members") {
|
|
1941
|
+
for (const member of change.members) {
|
|
1942
|
+
const changed = [...change.at, member.key];
|
|
1943
|
+
if (contains(changed, at)) {
|
|
1944
|
+
resetCollectionPending(pending);
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
if (!contains(at, changed)) continue;
|
|
1948
|
+
const relative = changed.slice(at.length);
|
|
1949
|
+
if (!relative.length) {
|
|
1950
|
+
resetCollectionPending(pending);
|
|
1951
|
+
return;
|
|
1952
|
+
}
|
|
1953
|
+
markDocumentReplace(dirtyEntry(pending, relative[0]), relative.slice(1));
|
|
1954
|
+
if (relative.length === 1 && member.kind !== "updated") pending.order = true;
|
|
1955
|
+
}
|
|
1956
|
+
if (change.order && contains(at, change.at)) {
|
|
1957
|
+
const relative = change.at.slice(at.length);
|
|
1958
|
+
if (!relative.length) pending.order = true;
|
|
1959
|
+
else markDocumentOrder(dirtyEntry(pending, relative[0]), relative.slice(1));
|
|
1960
|
+
}
|
|
1961
|
+
continue;
|
|
1962
|
+
}
|
|
1963
|
+
if (!contains(at, change.at)) continue;
|
|
1964
|
+
const relative = change.at.slice(at.length);
|
|
1965
|
+
if (!relative.length) {
|
|
1966
|
+
resetCollectionPending(pending);
|
|
1967
|
+
return;
|
|
1968
|
+
}
|
|
1969
|
+
markDocumentTree(dirtyEntry(pending, relative[0]), relative.slice(1), change.before !== change.after, change.nodes.map((node) => node.id));
|
|
1970
|
+
}
|
|
1971
|
+
};
|
|
1661
1972
|
const createSourceRegistry = (scheduler) => {
|
|
1662
1973
|
const documents = /* @__PURE__ */ new Map();
|
|
1663
1974
|
const readables = /* @__PURE__ */ new Map();
|
|
@@ -1669,6 +1980,8 @@ const createSourceRegistry = (scheduler) => {
|
|
|
1669
1980
|
if (cached) return cached;
|
|
1670
1981
|
if (state.disposed) throw new Error("Document has been disposed.");
|
|
1671
1982
|
const bindings = /* @__PURE__ */ new Set();
|
|
1983
|
+
const index = new SubscriptionIndex(state.schema);
|
|
1984
|
+
const candidates = /* @__PURE__ */ new Set();
|
|
1672
1985
|
const guard = (locked) => {
|
|
1673
1986
|
state.projectionLocks = (state.projectionLocks ?? 0) + (locked ? 1 : -1);
|
|
1674
1987
|
};
|
|
@@ -1679,11 +1992,13 @@ const createSourceRegistry = (scheduler) => {
|
|
|
1679
1992
|
add(binding) {
|
|
1680
1993
|
if (closed) throw new Error("Document projection connection is closed.");
|
|
1681
1994
|
bindings.add(binding);
|
|
1995
|
+
index.add(binding.target, binding);
|
|
1682
1996
|
let active = true;
|
|
1683
1997
|
return () => {
|
|
1684
1998
|
if (!active) return;
|
|
1685
1999
|
active = false;
|
|
1686
2000
|
bindings.delete(binding);
|
|
2001
|
+
index.delete(binding.target, binding);
|
|
1687
2002
|
if (!bindings.size) connection.close();
|
|
1688
2003
|
};
|
|
1689
2004
|
},
|
|
@@ -1692,13 +2007,17 @@ const createSourceRegistry = (scheduler) => {
|
|
|
1692
2007
|
closed = true;
|
|
1693
2008
|
detach();
|
|
1694
2009
|
bindings.clear();
|
|
2010
|
+
candidates.clear();
|
|
2011
|
+
index.clear();
|
|
1695
2012
|
scheduler.guards.delete(guard);
|
|
1696
2013
|
documents.delete(state);
|
|
1697
2014
|
}
|
|
1698
2015
|
};
|
|
1699
2016
|
detach = attachProjection(runtime, {
|
|
1700
2017
|
capture: (commit) => {
|
|
1701
|
-
|
|
2018
|
+
candidates.clear();
|
|
2019
|
+
index.collect(commit.changes, (binding) => candidates.add(binding));
|
|
2020
|
+
candidates.forEach((binding) => binding.capture(commit));
|
|
1702
2021
|
},
|
|
1703
2022
|
settle: scheduler.settle,
|
|
1704
2023
|
flush: scheduler.flush,
|
|
@@ -1713,14 +2032,15 @@ const createSourceRegistry = (scheduler) => {
|
|
|
1713
2032
|
const valueInput = (definition) => {
|
|
1714
2033
|
if (definition.source.kind !== "value-input") throw new Error("Invalid value input source.");
|
|
1715
2034
|
const source = definition.source;
|
|
1716
|
-
|
|
2035
|
+
let latest = source.initial;
|
|
2036
|
+
const boundary = createValueBoundary(scheduler, "projection input", source.initial, source.equality, () => latest, () => void 0);
|
|
1717
2037
|
return {
|
|
1718
2038
|
...boundary,
|
|
1719
2039
|
write: {
|
|
1720
2040
|
kind: "value",
|
|
1721
2041
|
set(value) {
|
|
1722
2042
|
scheduler.assertIdle();
|
|
1723
|
-
|
|
2043
|
+
latest = value;
|
|
1724
2044
|
boundary.mark();
|
|
1725
2045
|
scheduler.run();
|
|
1726
2046
|
}
|
|
@@ -1730,7 +2050,7 @@ const createSourceRegistry = (scheduler) => {
|
|
|
1730
2050
|
const collectionInput = (definition) => {
|
|
1731
2051
|
if (definition.source.kind !== "collection-input") throw new Error("Invalid collection input source.");
|
|
1732
2052
|
const values = new Map(definition.source.initial);
|
|
1733
|
-
const boundary = createCollectionBoundary(scheduler, "projection collection input", () => mapRead(values), definition.output.equality);
|
|
2053
|
+
const boundary = createCollectionBoundary(scheduler, "projection collection input", () => mapRead(values), definition.output.equality, () => void 0);
|
|
1734
2054
|
return {
|
|
1735
2055
|
...boundary,
|
|
1736
2056
|
write: {
|
|
@@ -1806,87 +2126,131 @@ const createSourceRegistry = (scheduler) => {
|
|
|
1806
2126
|
}
|
|
1807
2127
|
};
|
|
1808
2128
|
};
|
|
1809
|
-
const
|
|
1810
|
-
if (definition.source.kind !== "document
|
|
2129
|
+
const documentSource = (definition) => {
|
|
2130
|
+
if (definition.source.kind !== "document") throw new Error("Invalid document source.");
|
|
1811
2131
|
const { document, selector } = definition.source;
|
|
1812
2132
|
const state = accessOf(document);
|
|
1813
|
-
const
|
|
1814
|
-
const
|
|
1815
|
-
const
|
|
2133
|
+
const schemaNode = () => nodeAt(state.schema, selector.address, state.document);
|
|
2134
|
+
const currentValue = () => read$1(state.document, selector.address, state.schema);
|
|
2135
|
+
const disposed = () => {
|
|
1816
2136
|
if (state.disposed) throw new Error("Document has been disposed.");
|
|
1817
|
-
const node = schemaNode();
|
|
1818
|
-
if (!node) return void 0;
|
|
1819
|
-
return copyValue(node, selector ? read$1(state.document, address, state.schema) : state.document);
|
|
1820
2137
|
};
|
|
1821
|
-
|
|
1822
|
-
const
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
2138
|
+
if (selector.kind === "value") {
|
|
2139
|
+
const dirty = createDocumentDirty();
|
|
2140
|
+
const read = (previous) => {
|
|
2141
|
+
disposed();
|
|
2142
|
+
const node = schemaNode();
|
|
2143
|
+
const current = currentValue();
|
|
2144
|
+
if (selector.tree?.kind === "root") return node?.kind === "tree" && isRecord(current) && typeof current.rootId === "string" ? current.rootId : void 0;
|
|
2145
|
+
if (selector.tree?.kind === "node") {
|
|
2146
|
+
if (node?.kind !== "tree" || !isRecord(current) || !isRecord(current.nodes)) return void 0;
|
|
2147
|
+
const entry = Object.hasOwn(current.nodes, selector.tree.id) ? current.nodes[selector.tree.id] : void 0;
|
|
2148
|
+
if (equalTreeNode(previous, entry)) return previous;
|
|
2149
|
+
return entry ? copyTreeNode(entry) : void 0;
|
|
1833
2150
|
}
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
2151
|
+
if (!node) return void 0;
|
|
2152
|
+
return previous === void 0 ? copyValue(node, current) : materializeDocumentValue(node, previous, current, dirty);
|
|
2153
|
+
};
|
|
2154
|
+
const initial = read();
|
|
2155
|
+
let boundary;
|
|
2156
|
+
boundary = createValueBoundary(scheduler, "document source", initial, Object.is, (previous) => read(previous), (failed) => {
|
|
2157
|
+
if (failed) markDocumentReplace(dirty, []);
|
|
2158
|
+
else clearDocumentDirty(dirty);
|
|
2159
|
+
});
|
|
2160
|
+
const remove = documentConnection(document).add({
|
|
2161
|
+
target: selector,
|
|
2162
|
+
capture: (commit) => {
|
|
2163
|
+
try {
|
|
2164
|
+
mergeValueChanges(selector, dirty, commit.changes);
|
|
2165
|
+
boundary.mark({ reset: commit.changes.changes.some((change) => change.kind === "reset") });
|
|
2166
|
+
} catch (cause) {
|
|
2167
|
+
boundary.fail(cause);
|
|
2168
|
+
}
|
|
2169
|
+
},
|
|
2170
|
+
dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
|
|
2171
|
+
});
|
|
2172
|
+
boundary.detach(remove);
|
|
2173
|
+
return boundary;
|
|
2174
|
+
}
|
|
2175
|
+
const pending = createCollectionPending();
|
|
2176
|
+
const read = (active, previous) => {
|
|
2177
|
+
disposed();
|
|
2178
|
+
const node = schemaNode();
|
|
2179
|
+
const value = currentValue();
|
|
2180
|
+
const treeNodes = selector.tree?.kind === "nodes";
|
|
2181
|
+
const entryNode = !treeNodes && node ? collectionEntryNode(node) : void 0;
|
|
1850
2182
|
const snapshots = /* @__PURE__ */ new Map();
|
|
2183
|
+
const replacement = createDocumentDirty();
|
|
2184
|
+
if (pending.reset) markDocumentReplace(replacement, []);
|
|
2185
|
+
const ids = () => {
|
|
2186
|
+
assertScope(active);
|
|
2187
|
+
if (treeNodes) return node?.kind === "tree" && isRecord(value) && isRecord(value.nodes) ? Object.freeze(Object.keys(value.nodes)) : Object.freeze([]);
|
|
2188
|
+
if (node?.kind === "map" && isRecord(value)) return Object.freeze(Object.keys(value));
|
|
2189
|
+
if (node?.kind === "table" && isRecord(value) && Array.isArray(value.ids)) return Object.freeze([...value.ids]);
|
|
2190
|
+
if (node?.kind === "list" && Array.isArray(value)) return Object.freeze(toArray(indexedKeys(value, node.keyOf)));
|
|
2191
|
+
return Object.freeze([]);
|
|
2192
|
+
};
|
|
2193
|
+
const has = (key) => {
|
|
2194
|
+
assertScope(active);
|
|
2195
|
+
if (treeNodes) return Boolean(node?.kind === "tree" && isRecord(value) && isRecord(value.nodes) && Object.hasOwn(value.nodes, key));
|
|
2196
|
+
if (node?.kind === "map" && isRecord(value)) return Object.hasOwn(value, key);
|
|
2197
|
+
if (node?.kind === "table" && isRecord(value) && isRecord(value.byId)) return Object.hasOwn(value.byId, key);
|
|
2198
|
+
if (node?.kind === "list" && Array.isArray(value)) return indexedKeys(value, node.keyOf).index(key) >= 0;
|
|
2199
|
+
return false;
|
|
2200
|
+
};
|
|
2201
|
+
const raw = (key) => {
|
|
2202
|
+
if (treeNodes) {
|
|
2203
|
+
if (node?.kind !== "tree" || !isRecord(value) || !isRecord(value.nodes)) return void 0;
|
|
2204
|
+
return Object.hasOwn(value.nodes, key) ? value.nodes[key] : void 0;
|
|
2205
|
+
}
|
|
2206
|
+
if (node?.kind === "map" && isRecord(value)) return Object.hasOwn(value, key) ? value[key] : void 0;
|
|
2207
|
+
if (node?.kind === "table" && isRecord(value) && isRecord(value.byId)) return Object.hasOwn(value.byId, key) ? value.byId[key] : void 0;
|
|
2208
|
+
if (node?.kind === "list" && Array.isArray(value)) {
|
|
2209
|
+
const index = indexedKeys(value, node.keyOf).index(key);
|
|
2210
|
+
return index < 0 ? void 0 : value[index];
|
|
2211
|
+
}
|
|
2212
|
+
};
|
|
1851
2213
|
return Object.freeze({
|
|
1852
2214
|
get: (key) => {
|
|
1853
2215
|
assertScope(active);
|
|
1854
|
-
if (!snapshots.has(key)) snapshots.set(key,
|
|
2216
|
+
if (!snapshots.has(key)) if (!has(key)) snapshots.set(key, void 0);
|
|
2217
|
+
else {
|
|
2218
|
+
const current = raw(key);
|
|
2219
|
+
if (treeNodes) {
|
|
2220
|
+
const entry = current;
|
|
2221
|
+
const before = previous.has(key) ? previous.get(key) : void 0;
|
|
2222
|
+
snapshots.set(key, equalTreeNode(before, entry) ? before : copyTreeNode(entry));
|
|
2223
|
+
} else if (!entryNode) snapshots.set(key, current);
|
|
2224
|
+
else if (!previous.has(key)) snapshots.set(key, copyValue(entryNode, current));
|
|
2225
|
+
else {
|
|
2226
|
+
const dirty = pending.entries.get(key) ?? (pending.reset ? replacement : void 0);
|
|
2227
|
+
snapshots.set(key, dirty ? materializeDocumentValue(entryNode, previous.get(key), current, dirty) : previous.get(key));
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
1855
2230
|
return snapshots.get(key);
|
|
1856
2231
|
},
|
|
1857
|
-
has
|
|
1858
|
-
|
|
1859
|
-
return raw.has(key);
|
|
1860
|
-
},
|
|
1861
|
-
ids: () => {
|
|
1862
|
-
assertScope(active);
|
|
1863
|
-
return raw.ids();
|
|
1864
|
-
}
|
|
2232
|
+
has,
|
|
2233
|
+
ids
|
|
1865
2234
|
});
|
|
1866
2235
|
};
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
return entry ? equalValue(entry, left, right) : Object.is(left, right);
|
|
2236
|
+
let boundary;
|
|
2237
|
+
boundary = createCollectionBoundary(scheduler, "document collection source", read, Object.is, (failed) => {
|
|
2238
|
+
if (failed) resetCollectionPending(pending);
|
|
2239
|
+
else clearCollectionPending(pending);
|
|
1872
2240
|
});
|
|
1873
2241
|
const remove = documentConnection(document).add({
|
|
1874
|
-
|
|
2242
|
+
target: selector,
|
|
1875
2243
|
capture: (commit) => {
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
boundary.mark({
|
|
1879
|
-
|
|
2244
|
+
try {
|
|
2245
|
+
mergeCollectionChanges(selector, pending, commit.changes);
|
|
2246
|
+
boundary.mark({
|
|
2247
|
+
reset: pending.reset,
|
|
2248
|
+
...pending.reset ? {} : { candidates: pending.entries.keys() },
|
|
2249
|
+
orderMayChange: pending.order
|
|
2250
|
+
});
|
|
2251
|
+
} catch (cause) {
|
|
2252
|
+
boundary.fail(cause);
|
|
1880
2253
|
}
|
|
1881
|
-
const candidates = new Set([
|
|
1882
|
-
...impact.added,
|
|
1883
|
-
...impact.updated,
|
|
1884
|
-
...impact.removed
|
|
1885
|
-
]);
|
|
1886
|
-
boundary.mark({
|
|
1887
|
-
candidates,
|
|
1888
|
-
orderMayChange: Boolean(impact.added.size || impact.removed.size || impact.orderChanged)
|
|
1889
|
-
});
|
|
1890
2254
|
},
|
|
1891
2255
|
dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
|
|
1892
2256
|
});
|
|
@@ -1896,13 +2260,12 @@ const createSourceRegistry = (scheduler) => {
|
|
|
1896
2260
|
const readable = (definition) => {
|
|
1897
2261
|
if (definition.source.kind !== "readable") throw new Error("Invalid Readable source.");
|
|
1898
2262
|
const { readable, equality } = definition.source;
|
|
1899
|
-
let
|
|
1900
|
-
const boundary = createValueBoundary(scheduler, "external readable",
|
|
2263
|
+
let latest = readable.current();
|
|
2264
|
+
const boundary = createValueBoundary(scheduler, "external readable", latest, equality, () => latest, () => void 0);
|
|
1901
2265
|
const member = {
|
|
1902
2266
|
boundary,
|
|
1903
2267
|
receive(value, metadata) {
|
|
1904
|
-
|
|
1905
|
-
boundary.setLatest(value);
|
|
2268
|
+
latest = value;
|
|
1906
2269
|
boundary.mark(metadata);
|
|
1907
2270
|
}
|
|
1908
2271
|
};
|
|
@@ -1934,7 +2297,7 @@ const createSourceRegistry = (scheduler) => {
|
|
|
1934
2297
|
};
|
|
1935
2298
|
const owner = documentReadableOwner(readable);
|
|
1936
2299
|
if (owner) detachDocument = documentConnection(owner).add({
|
|
1937
|
-
|
|
2300
|
+
target: documentRootTarget,
|
|
1938
2301
|
capture: () => receive(false),
|
|
1939
2302
|
dispose: () => {
|
|
1940
2303
|
members.forEach((next) => next.boundary.fail(/* @__PURE__ */ new Error("Document has been disposed.")));
|
|
@@ -1954,11 +2317,12 @@ const createSourceRegistry = (scheduler) => {
|
|
|
1954
2317
|
const externalValue = (definition) => {
|
|
1955
2318
|
if (definition.source.kind !== "external-value") throw new Error("Invalid external value source.");
|
|
1956
2319
|
const { source, equality } = definition.source;
|
|
1957
|
-
|
|
2320
|
+
let latest = source.current();
|
|
2321
|
+
const boundary = createValueBoundary(scheduler, "external source", latest, equality, () => latest, () => void 0);
|
|
1958
2322
|
const member = {
|
|
1959
2323
|
boundary,
|
|
1960
2324
|
receive(value, metadata) {
|
|
1961
|
-
|
|
2325
|
+
latest = value;
|
|
1962
2326
|
boundary.mark(metadata);
|
|
1963
2327
|
}
|
|
1964
2328
|
};
|
|
@@ -2012,7 +2376,7 @@ const createSourceRegistry = (scheduler) => {
|
|
|
2012
2376
|
has: (key) => current.has(key),
|
|
2013
2377
|
ids: () => current.ids()
|
|
2014
2378
|
});
|
|
2015
|
-
}, definition.output.equality);
|
|
2379
|
+
}, definition.output.equality, () => void 0);
|
|
2016
2380
|
let connection = externalCollections.get(source);
|
|
2017
2381
|
if (!connection) {
|
|
2018
2382
|
const members = /* @__PURE__ */ new Set();
|
|
@@ -2075,8 +2439,7 @@ const createSourceRegistry = (scheduler) => {
|
|
|
2075
2439
|
switch (definition.source.kind) {
|
|
2076
2440
|
case "value-input": return valueInput(definition);
|
|
2077
2441
|
case "collection-input": return collectionInput(definition);
|
|
2078
|
-
case "document
|
|
2079
|
-
case "document-collection": return documentCollection(definition);
|
|
2442
|
+
case "document": return documentSource(definition);
|
|
2080
2443
|
case "readable": return readable(definition);
|
|
2081
2444
|
case "external-value": return externalValue(definition);
|
|
2082
2445
|
case "external-collection": return externalCollection(definition);
|