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.
Files changed (65) hide show
  1. package/README.md +21 -2
  2. package/dist/{access-BeSV5ve-.js → access-CZXv_DIF.js} +179 -37
  3. package/dist/access-CZXv_DIF.js.map +1 -0
  4. package/dist/{access-Bienf28h.cjs → access-D64MFRSm.cjs} +228 -38
  5. package/dist/access-D64MFRSm.cjs.map +1 -0
  6. package/dist/{advanced-COFJtWMT.js → advanced-BTStm2cW.js} +29 -25
  7. package/dist/advanced-BTStm2cW.js.map +1 -0
  8. package/dist/{advanced-FgdTwu5b.d.ts → advanced-CqrR_4dh.d.ts} +3 -3
  9. package/dist/{advanced-DfKc7TZR.d.cts → advanced-CyR5o3Xr.d.cts} +3 -3
  10. package/dist/{advanced-BRL5pN0y.cjs → advanced-GKZ-CZ6a.cjs} +29 -25
  11. package/dist/advanced-GKZ-CZ6a.cjs.map +1 -0
  12. package/dist/advanced.cjs +1 -1
  13. package/dist/advanced.d.cts +1 -1
  14. package/dist/advanced.d.ts +1 -1
  15. package/dist/advanced.js +1 -1
  16. package/dist/{contract-KG5XnQZt.d.ts → contract-CRrZ-3sF.d.cts} +64 -34
  17. package/dist/{contract-CdmDHRUq.d.cts → contract-CxiNcqMn.d.ts} +64 -34
  18. package/dist/{driver-Djr3BVvo.cjs → driver-BC06FEMF.cjs} +2 -2
  19. package/dist/{driver-Djr3BVvo.cjs.map → driver-BC06FEMF.cjs.map} +1 -1
  20. package/dist/{driver-CLYJOGDQ.js → driver-BqRjwFmU.js} +2 -2
  21. package/dist/{driver-CLYJOGDQ.js.map → driver-BqRjwFmU.js.map} +1 -1
  22. package/dist/index.cjs +480 -117
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +2 -2
  25. package/dist/index.d.ts +2 -2
  26. package/dist/index.js +478 -115
  27. package/dist/index.js.map +1 -1
  28. package/dist/{integration-Z6MsfFI7.cjs → integration-BqDw7e2w.cjs} +3 -4
  29. package/dist/{integration-Z6MsfFI7.cjs.map → integration-BqDw7e2w.cjs.map} +1 -1
  30. package/dist/{integration-C4Isp8lJ.js → integration-CSXaF8HP.js} +2 -3
  31. package/dist/{integration-C4Isp8lJ.js.map → integration-CSXaF8HP.js.map} +1 -1
  32. package/dist/integration.cjs +5 -4
  33. package/dist/integration.d.cts +1 -1
  34. package/dist/integration.d.ts +1 -1
  35. package/dist/integration.js +4 -3
  36. package/dist/{issue-W0hT8u8D.js → issue-9U6YLYz3.js} +11 -6
  37. package/dist/issue-9U6YLYz3.js.map +1 -0
  38. package/dist/{issue-DjK1xp5K.cjs → issue-Bo0FIO3b.cjs} +11 -6
  39. package/dist/issue-Bo0FIO3b.cjs.map +1 -0
  40. package/dist/local-sync.cjs +2 -2
  41. package/dist/local-sync.d.cts +1 -1
  42. package/dist/local-sync.d.ts +1 -1
  43. package/dist/local-sync.js +2 -2
  44. package/dist/{notification-Bkjau6B6.js → notification-CrZHVx3a.js} +3 -59
  45. package/dist/notification-CrZHVx3a.js.map +1 -0
  46. package/dist/{notification-DJfouMEb.cjs → notification-DFoA9bQY.cjs} +3 -77
  47. package/dist/notification-DFoA9bQY.cjs.map +1 -0
  48. package/dist/react.cjs +4 -3
  49. package/dist/react.cjs.map +1 -1
  50. package/dist/react.js +3 -2
  51. package/dist/react.js.map +1 -1
  52. package/package.json +1 -1
  53. package/skills/doxum-runtime/SKILL.md +5 -0
  54. package/skills/doxum-runtime/references/guide.en.md +9 -0
  55. package/skills/doxum-runtime/references/guide.zh-CN.md +8 -0
  56. package/skills/doxum-runtime/references/projections.en.md +5 -0
  57. package/skills/doxum-runtime/references/projections.zh-CN.md +5 -0
  58. package/dist/access-BeSV5ve-.js.map +0 -1
  59. package/dist/access-Bienf28h.cjs.map +0 -1
  60. package/dist/advanced-BRL5pN0y.cjs.map +0 -1
  61. package/dist/advanced-COFJtWMT.js.map +0 -1
  62. package/dist/issue-DjK1xp5K.cjs.map +0 -1
  63. package/dist/issue-W0hT8u8D.js.map +0 -1
  64. package/dist/notification-Bkjau6B6.js.map +0 -1
  65. 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-W0hT8u8D.js";
2
- import { a as DocumentReentrancyError, c as createAccess, d as ParseError, f as checkKey, g as parse, h as equalValue, i as DocumentDisposedError, l as replace, m as copyValue, n as bindRuntimeAccess, o as TransactionRejected, p as checkValue, r as readWith, s as collectionAccess, t as accessOf, u as snapshot } from "./access-BeSV5ve-.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-COFJtWMT.js";
4
- import { a as documentReadableOwner, d as affected, f as belongs, 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-Bkjau6B6.js";
5
- import { n as bindRuntimeDriver, o as decodeChanges, r as disposeRuntimeDriver, s as sealChanges, t as assertRuntimeWritable } from "./driver-CLYJOGDQ.js";
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
- for (const change of changes.changes) if (change.kind === "members") {
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.address), cached = cache.get(key);
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 && copyNode(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
- const node = location?.node;
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 && before.parentId === after.parentId && equal(before.children, after.children) && Object.hasOwn(before, "value") === Object.hasOwn(after, "value") && equalValue(node.value, before.value, after.value)) continue;
447
+ if (before && after && equalTreeNode(before, after)) continue;
451
448
  nodes.push(!before ? {
452
449
  id,
453
450
  kind: "added",
454
- after: copyNode(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: copyNode(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) ? copyNode(tree.nodes[id]) : void 0);
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
- installMember(current.nodes, item.id, next !== void 0, next && {
833
- ...next,
834
- children: [...next.children]
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
- session.validate(node, current, change.at);
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).output.set(latest);
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
- for (const binding of bindings) if (binding.matches(commit)) binding.capture(commit);
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
- const boundary = createValueBoundary(scheduler, "projection input", source.initial, source.equality);
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
- boundary.setLatest(value);
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 documentValue = (definition) => {
1810
- if (definition.source.kind !== "document-value") throw new Error("Invalid document value source.");
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 address = selector?.address ?? Object.freeze([]);
1814
- const schemaNode = () => nodeAt(state.schema, address, state.document);
1815
- const read = () => {
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
- const boundary = createValueBoundary(scheduler, "document source", read(), (left, right) => {
1822
- const node = schemaNode();
1823
- return node ? equalValue(node, left, right) : Object.is(left, right);
1824
- });
1825
- const remove = documentConnection(document).add({
1826
- matches: (commit) => !selector || affectsTarget(commit.impact, selector),
1827
- capture: (commit) => {
1828
- try {
1829
- boundary.setLatest(read());
1830
- boundary.mark({ reset: commit.impact.kind === "reset" });
1831
- } catch (cause) {
1832
- boundary.fail(cause);
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
- dispose: () => boundary.fail(/* @__PURE__ */ new Error("Document has been disposed."))
1836
- });
1837
- boundary.detach(remove);
1838
- return boundary;
1839
- };
1840
- const documentCollection = (definition) => {
1841
- if (definition.source.kind !== "document-collection") throw new Error("Invalid document collection source.");
1842
- const { document, selector } = definition.source;
1843
- const state = accessOf(document);
1844
- const read = (active) => {
1845
- if (state.disposed) throw new Error("Document has been disposed.");
1846
- const raw = collectionAccess({
1847
- state,
1848
- active
1849
- }, selector.address);
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, snapshot(raw.get(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: (key) => {
1858
- assertScope(active);
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
- const collectionNode = () => nodeAt(state.schema, selector.address, state.document);
1868
- const boundary = createCollectionBoundary(scheduler, "document collection source", read, (left, right) => {
1869
- const node = collectionNode();
1870
- const entry = node && collectionEntryNode(node);
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
- matches: (commit) => affectsTarget(commit.impact, selector),
2242
+ target: selector,
1875
2243
  capture: (commit) => {
1876
- const impact = collectionImpact(commit.impact, selector);
1877
- if (impact.kind === "reset") {
1878
- boundary.mark({ reset: true });
1879
- return;
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 initial = readable.current();
1900
- const boundary = createValueBoundary(scheduler, "external readable", initial, equality);
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
- initial = value;
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
- matches: () => true,
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
- const boundary = createValueBoundary(scheduler, "external source", source.current(), equality);
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
- boundary.setLatest(value);
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-value": return documentValue(definition);
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);