doxum 0.1.1 → 0.1.3

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 (44) hide show
  1. package/README.md +77 -2
  2. package/dist/{contract-DwNiKioc.d.ts → contract-BsY1XLiG.d.ts} +64 -5
  3. package/dist/{contract-Otb5W6cQ.d.cts → contract-D8kjdfqT.d.cts} +64 -5
  4. package/dist/{dependency-BdEMyquf.js → dependency-C5_R1tvQ.js} +24 -263
  5. package/dist/dependency-C5_R1tvQ.js.map +1 -0
  6. package/dist/{dependency-DLcCvNKq.cjs → dependency-Dfzs3khk.cjs} +48 -341
  7. package/dist/dependency-Dfzs3khk.cjs.map +1 -0
  8. package/dist/driver-CNxqMVFH.cjs +69 -0
  9. package/dist/driver-CNxqMVFH.cjs.map +1 -0
  10. package/dist/driver-CbzfW5MR.js +46 -0
  11. package/dist/driver-CbzfW5MR.js.map +1 -0
  12. package/dist/index.cjs +428 -133
  13. package/dist/index.cjs.map +1 -1
  14. package/dist/index.d.cts +8 -5
  15. package/dist/index.d.ts +8 -5
  16. package/dist/index.js +352 -61
  17. package/dist/index.js.map +1 -1
  18. package/dist/integration.cjs +1 -1
  19. package/dist/integration.cjs.map +1 -1
  20. package/dist/integration.d.cts +2 -2
  21. package/dist/integration.d.ts +2 -2
  22. package/dist/integration.js +1 -1
  23. package/dist/integration.js.map +1 -1
  24. package/dist/local-sync.cjs +485 -0
  25. package/dist/local-sync.cjs.map +1 -0
  26. package/dist/local-sync.d.cts +74 -0
  27. package/dist/local-sync.d.ts +74 -0
  28. package/dist/local-sync.js +476 -0
  29. package/dist/local-sync.js.map +1 -0
  30. package/dist/ownership-B-VAPcce.js +242 -0
  31. package/dist/ownership-B-VAPcce.js.map +1 -0
  32. package/dist/ownership-CU-9DKJQ.cjs +301 -0
  33. package/dist/ownership-CU-9DKJQ.cjs.map +1 -0
  34. package/dist/react.cjs.map +1 -1
  35. package/dist/react.d.cts +2 -2
  36. package/dist/react.d.ts +2 -2
  37. package/dist/react.js.map +1 -1
  38. package/package.json +14 -8
  39. package/skills/doxum-runtime/references/guide.en.md +60 -5
  40. package/skills/doxum-runtime/references/guide.zh-CN.md +48 -2
  41. package/skills/doxum-runtime/references/invariants.en.md +23 -8
  42. package/skills/doxum-runtime/references/invariants.zh-CN.md +2 -2
  43. package/dist/dependency-BdEMyquf.js.map +0 -1
  44. package/dist/dependency-DLcCvNKq.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,5 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dependency = require("./dependency-DLcCvNKq.cjs");
2
+ const require_dependency = require("./dependency-Dfzs3khk.cjs");
3
+ const require_ownership = require("./ownership-CU-9DKJQ.cjs");
4
+ const require_driver = require("./driver-CNxqMVFH.cjs");
3
5
  //#region core/src/impact.ts
4
6
  const emptyCollectionImpact = Object.freeze({
5
7
  kind: "incremental",
@@ -34,7 +36,7 @@ const createImpact = (input) => {
34
36
  kind,
35
37
  operations: input.operations,
36
38
  affects: (value) => {
37
- require_dependency.profile.impact.affects();
39
+ require_ownership.profile.impact.affects();
38
40
  if (kind === "reset") return true;
39
41
  if (!require_dependency.belongs(value, input.schema)) return false;
40
42
  if (pathsAffect(require_dependency.address(value))) return true;
@@ -403,7 +405,7 @@ const restore = (keys, inserted, positions) => {
403
405
  //#region core/src/mutation/execute-entity.ts
404
406
  const resolveEntityCollection = (node, value) => {
405
407
  if (node.kind === "table") {
406
- if (!require_dependency.isRecord(value) || !Array.isArray(value.ids) || !require_dependency.isRecord(value.byId)) return { status: "invalid" };
408
+ if (!require_ownership.isRecord(value) || !Array.isArray(value.ids) || !require_ownership.isRecord(value.byId)) return { status: "invalid" };
407
409
  return {
408
410
  status: "resolved",
409
411
  collection: {
@@ -413,7 +415,7 @@ const resolveEntityCollection = (node, value) => {
413
415
  }
414
416
  };
415
417
  }
416
- return require_dependency.isRecord(value) ? {
418
+ return require_ownership.isRecord(value) ? {
417
419
  status: "resolved",
418
420
  collection: {
419
421
  kind: "map",
@@ -445,7 +447,7 @@ const executeEntity = (target, operation, copyPayload) => {
445
447
  if (ids && operation.positions) {
446
448
  if (operation.positions.length !== operation.entries.length || !validPositions(ids.length, operation.positions)) return rejected$4(operation, "invalid-operation", "Entity restore positions are invalid.");
447
449
  } else if (ids && !valid(ids, operation.anchor)) return rejected$4(operation, "invalid-anchor", "Collection anchor is invalid.");
448
- for (const entry of operation.entries) byId[entry.id] = copyPayload ? require_dependency.cloneValue(entry.value, "canonical") : require_dependency.transferPayload(entry.value);
450
+ for (const entry of operation.entries) byId[entry.id] = copyPayload ? require_ownership.cloneValue(entry.value, "canonical") : require_ownership.transferPayload(entry.value);
449
451
  if (ids) if (operation.positions) restore(ids, operation.entries.map((entry) => entry.id), operation.positions);
450
452
  else if (!operation.anchor || "at" in operation.anchor && operation.anchor.at === "end") for (const entry of operation.entries) ids.push(entry.id);
451
453
  else {
@@ -487,7 +489,7 @@ const executeEntity = (target, operation, copyPayload) => {
487
489
  if (seen.has(id)) {
488
490
  entries.push({
489
491
  id,
490
- value: require_dependency.cloneValue(byId[id], "inverse")
492
+ value: require_ownership.cloneValue(byId[id], "inverse")
491
493
  });
492
494
  positions.push(index);
493
495
  delete byId[id];
@@ -497,7 +499,7 @@ const executeEntity = (target, operation, copyPayload) => {
497
499
  } else for (const id of operation.ids) {
498
500
  entries.push({
499
501
  id,
500
- value: require_dependency.cloneValue(byId[id], "inverse")
502
+ value: require_ownership.cloneValue(byId[id], "inverse")
501
503
  });
502
504
  delete byId[id];
503
505
  }
@@ -542,7 +544,7 @@ const executeList = (target, operation, copyPayload) => {
542
544
  if (!Array.isArray(target.value) || target.node.kind !== "list") return rejected$3(operation, "invalid-address", "List target is invalid.");
543
545
  const list = target.value;
544
546
  const orderedKeys = keys(list, target.node.keyOf);
545
- const own = (value) => copyPayload ? require_dependency.ownPayload(value) : value;
547
+ const own = (value) => copyPayload ? require_ownership.ownPayload(value) : value;
546
548
  if (operation.type === "list.replace") {
547
549
  if (operation.keys.length !== operation.value.length) return rejected$3(operation, "invalid-list-keys", "List replacement keys are invalid.");
548
550
  const seen = /* @__PURE__ */ new Set();
@@ -551,13 +553,13 @@ const executeList = (target, operation, copyPayload) => {
551
553
  if (seen.has(key) || target.node.keyOf(operation.value[index]) !== key) return rejected$3(operation, "invalid-list-keys", "List replacement keys are invalid.");
552
554
  seen.add(key);
553
555
  }
554
- if (require_dependency.sameStructuralValue(list, operation.value)) return { status: "unchanged" };
556
+ if (require_ownership.sameStructuralValue(list, operation.value)) return { status: "unchanged" };
555
557
  const keys = new Array(list.length);
556
558
  for (let index = 0; index < list.length; index += 1) keys[index] = target.node.keyOf(list[index]);
557
559
  const inverse = {
558
560
  type: "list.replace",
559
561
  at: operation.at,
560
- value: require_dependency.cloneValue(list, "inverse"),
562
+ value: require_ownership.cloneValue(list, "inverse"),
561
563
  keys
562
564
  };
563
565
  const owned = own(operation.value);
@@ -589,7 +591,7 @@ const executeList = (target, operation, copyPayload) => {
589
591
  type: "list.insert",
590
592
  at: operation.at,
591
593
  key: operation.key,
592
- value: require_dependency.cloneValue(list[position], "inverse"),
594
+ value: require_ownership.cloneValue(list[position], "inverse"),
593
595
  anchor: at(orderedKeys, position)
594
596
  };
595
597
  list.splice(position, 1);
@@ -679,7 +681,7 @@ const restoreIncremental = (value, change) => {
679
681
  }
680
682
  return value;
681
683
  };
682
- const restoreChange = (value, change) => change.mode === "whole" ? require_dependency.ownPayload(change.value, "journal") : restoreIncremental(value, change);
684
+ const restoreChange = (value, change) => change.mode === "whole" ? require_ownership.ownPayload(change.value, "journal") : restoreIncremental(value, change);
683
685
  const wholeChange = (inverse, previous) => {
684
686
  if (!previous) return {
685
687
  mode: "whole",
@@ -688,7 +690,7 @@ const wholeChange = (inverse, previous) => {
688
690
  if (previous.mode === "whole") return previous;
689
691
  return {
690
692
  mode: "whole",
691
- value: restoreIncremental(require_dependency.ownPayload(inverse.value, "journal"), previous)
693
+ value: restoreIncremental(require_ownership.ownPayload(inverse.value, "journal"), previous)
692
694
  };
693
695
  };
694
696
  const removedNodePresences = (inverses) => {
@@ -779,16 +781,16 @@ const recordChange = (previous, current, forward, inverses) => {
779
781
  };
780
782
  const sameNodePresence = (before, value) => {
781
783
  if (before.status === "absent") return value === void 0;
782
- return value !== void 0 && before.parentId === value.parentId && sameOrder$1(before.children, value.children) && require_dependency.sameStructuralValue(before.value, value.value);
784
+ return value !== void 0 && before.parentId === value.parentId && sameOrder$1(before.children, value.children) && require_ownership.sameStructuralValue(before.value, value.value);
783
785
  };
784
786
  const changeChanged = (change, value) => {
785
- if (change.mode === "whole") return !require_dependency.sameStructuralValue(change.value, value);
787
+ if (change.mode === "whole") return !require_ownership.sameStructuralValue(change.value, value);
786
788
  if (!is(value)) return true;
787
789
  if ((change.root.status === "present" ? change.root.id : void 0) !== value.rootId) return true;
788
790
  for (const [id, before] of change.nodes) if (!sameNodePresence(before, node(value, id))) return true;
789
791
  return false;
790
792
  };
791
- const is = (value) => require_dependency.isRecord(value) && require_dependency.isRecord(value.nodes);
793
+ const is = (value) => require_ownership.isRecord(value) && require_ownership.isRecord(value.nodes);
792
794
  const contains$1 = (tree, id) => has$1(tree.nodes, id);
793
795
  const validate = (value) => {
794
796
  if (!is(value)) return false;
@@ -797,7 +799,7 @@ const validate = (value) => {
797
799
  if (typeof value.rootId !== "string" || !has$1(value.nodes, value.rootId)) return false;
798
800
  for (const id of ids) {
799
801
  const entry = value.nodes[id];
800
- if (!require_dependency.isRecord(entry) || !Array.isArray(entry.children)) return false;
802
+ if (!require_ownership.isRecord(entry) || !Array.isArray(entry.children)) return false;
801
803
  if (entry.children.some((child) => typeof child !== "string") || new Set(entry.children).size !== entry.children.length) return false;
802
804
  if (entry.parentId !== void 0 && typeof entry.parentId !== "string") return false;
803
805
  if (id === value.rootId) {
@@ -824,7 +826,7 @@ const documentTreeError = (node, value, address) => {
824
826
  if ("optional" in node && node.optional && value === void 0) return void 0;
825
827
  if (node.kind === "tree") return validate(value) ? void 0 : address;
826
828
  if (node.kind === "object") {
827
- if (!require_dependency.isRecord(value)) return void 0;
829
+ if (!require_ownership.isRecord(value)) return void 0;
828
830
  for (const [key, child] of Object.entries(node.shape)) {
829
831
  const error = documentTreeError(child, value[key], [...address, key]);
830
832
  if (error) return error;
@@ -833,12 +835,12 @@ const documentTreeError = (node, value, address) => {
833
835
  }
834
836
  if (node.kind === "single") return documentTreeError(node.value, value, address);
835
837
  if (node.kind === "variant") {
836
- if (!require_dependency.isRecord(value) || typeof value[node.tag] !== "string") return void 0;
838
+ if (!require_ownership.isRecord(value) || typeof value[node.tag] !== "string") return void 0;
837
839
  const variant = node.variants[value[node.tag]];
838
840
  return variant ? documentTreeError(variant, value, address) : void 0;
839
841
  }
840
842
  if (node.kind === "table") {
841
- if (!require_dependency.isRecord(value) || !require_dependency.isRecord(value.byId)) return void 0;
843
+ if (!require_ownership.isRecord(value) || !require_ownership.isRecord(value.byId)) return void 0;
842
844
  for (const [id, entry] of Object.entries(value.byId)) {
843
845
  const error = documentTreeError(node.value, entry, [...address, id]);
844
846
  if (error) return error;
@@ -846,7 +848,7 @@ const documentTreeError = (node, value, address) => {
846
848
  return;
847
849
  }
848
850
  if (node.kind === "map") {
849
- if (!require_dependency.isRecord(value)) return void 0;
851
+ if (!require_ownership.isRecord(value)) return void 0;
850
852
  for (const [id, entry] of Object.entries(value)) {
851
853
  const error = documentTreeError(node.value, entry, [...address, id]);
852
854
  if (error) return error;
@@ -859,13 +861,13 @@ const invalidDocument = (schema, value) => documentTreeError({
859
861
  }, value, []);
860
862
  const replace = (current, operation) => {
861
863
  if (!validate(operation.value)) return rejected$2("invalid-tree", "Tree replacement is malformed.");
862
- if (require_dependency.sameStructuralValue(current, operation.value)) return { status: "unchanged" };
864
+ if (require_ownership.sameStructuralValue(current, operation.value)) return { status: "unchanged" };
863
865
  return {
864
866
  status: "changed",
865
867
  inverse: [{
866
868
  type: "tree.replace",
867
869
  at: operation.at,
868
- value: require_dependency.cloneValue(current, "inverse")
870
+ value: require_ownership.cloneValue(current, "inverse")
869
871
  }]
870
872
  };
871
873
  };
@@ -907,7 +909,7 @@ const set = (tree, operation, value) => {
907
909
  type: "tree.set",
908
910
  at: operation.at,
909
911
  treeNodeId: operation.treeNodeId,
910
- value: require_dependency.cloneValue(entry.value, "inverse")
912
+ value: require_ownership.cloneValue(entry.value, "inverse")
911
913
  };
912
914
  entry.value = value;
913
915
  return {
@@ -938,7 +940,7 @@ const remove = (tree, operation) => {
938
940
  id: frame.id,
939
941
  parentId: frame.parentId,
940
942
  index: frame.index,
941
- value: require_dependency.cloneValue(entry.value, "inverse")
943
+ value: require_ownership.cloneValue(entry.value, "inverse")
942
944
  });
943
945
  for (let index = entry.children.length - 1; index >= 0; index -= 1) {
944
946
  const childId = entry.children[index];
@@ -1026,10 +1028,10 @@ const executeTree = (root, target, operation, copyPayload) => {
1026
1028
  status: "rejected",
1027
1029
  issue: from(operation, "invalid-address", "Tree target is invalid.")
1028
1030
  };
1029
- const own = (value) => copyPayload ? require_dependency.ownPayload(value) : value;
1031
+ const own = (value) => copyPayload ? require_ownership.ownPayload(value) : value;
1030
1032
  if (operation.type === "tree.replace") {
1031
1033
  const result = replace(target.value, operation);
1032
- if (result.status === "changed") require_dependency.set(root, operation.at, require_dependency.ownPayload(operation.value));
1034
+ if (result.status === "changed") require_dependency.set(root, operation.at, require_ownership.ownPayload(operation.value));
1033
1035
  return outcome(operation, result);
1034
1036
  }
1035
1037
  if (operation.type === "tree.insert") return outcome(operation, insert(target.value, operation, own(operation.value)));
@@ -1044,7 +1046,7 @@ const rejected$1 = (operation, code, message) => ({
1044
1046
  issue: from(operation, code, message)
1045
1047
  });
1046
1048
  const executeValue = (target, operation, copyPayload) => {
1047
- const own = (value) => copyPayload ? require_dependency.ownPayload(value) : value;
1049
+ const own = (value) => copyPayload ? require_ownership.ownPayload(value) : value;
1048
1050
  if (operation.type === "field.set" || operation.type === "field.clear") {
1049
1051
  const existed = Object.prototype.hasOwnProperty.call(target.parent, target.key);
1050
1052
  if (operation.type === "field.clear") {
@@ -1077,11 +1079,11 @@ const executeValue = (target, operation, copyPayload) => {
1077
1079
  };
1078
1080
  }
1079
1081
  if (operation.type === "variant.replace") {
1080
- if (require_dependency.sameStructuralValue(target.value, operation.value)) return { status: "unchanged" };
1082
+ if (require_ownership.sameStructuralValue(target.value, operation.value)) return { status: "unchanged" };
1081
1083
  const inverse = {
1082
1084
  type: "variant.replace",
1083
1085
  at: operation.at,
1084
- value: require_dependency.ownPayload(target.value, "inverse")
1086
+ value: require_ownership.ownPayload(target.value, "inverse")
1085
1087
  };
1086
1088
  target.parent[target.key] = own(operation.value);
1087
1089
  return {
@@ -1089,13 +1091,13 @@ const executeValue = (target, operation, copyPayload) => {
1089
1091
  inverse: [inverse]
1090
1092
  };
1091
1093
  }
1092
- if (!require_dependency.isRecord(target.value)) return rejected$1(operation, "invalid-address", "Dictionary target is invalid.");
1094
+ if (!require_ownership.isRecord(target.value)) return rejected$1(operation, "invalid-address", "Dictionary target is invalid.");
1093
1095
  if (operation.type === "dict.replace") {
1094
- if (require_dependency.sameStructuralValue(target.value, operation.value)) return { status: "unchanged" };
1096
+ if (require_ownership.sameStructuralValue(target.value, operation.value)) return { status: "unchanged" };
1095
1097
  const inverse = {
1096
1098
  type: "dict.replace",
1097
1099
  at: operation.at,
1098
- value: require_dependency.cloneValue(target.value, "inverse")
1100
+ value: require_ownership.cloneValue(target.value, "inverse")
1099
1101
  };
1100
1102
  target.parent[target.key] = own(operation.value);
1101
1103
  return {
@@ -1157,7 +1159,7 @@ const execute = (root, operation, target, copyPayload) => {
1157
1159
  //#region core/src/mutation/journal.ts
1158
1160
  const indexNode = () => ({ children: /* @__PURE__ */ new Map() });
1159
1161
  const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
1160
- const sameAddress = (left, right) => {
1162
+ const sameAddress$1 = (left, right) => {
1161
1163
  if (left.length !== right.length) return false;
1162
1164
  for (let index = 0; index < left.length; index += 1) if (left[index] !== right[index]) return false;
1163
1165
  return true;
@@ -1258,7 +1260,7 @@ const restoreRootSubject = (value, subject) => {
1258
1260
  if (subject.kind === "value") return subject.before.status === "present" ? subject.before.value : void 0;
1259
1261
  if (subject.kind === "variant") return subject.before;
1260
1262
  if (subject.kind === "dict") {
1261
- if (require_dependency.isRecord(value)) restoreDict(value, subject.state);
1263
+ if (require_ownership.isRecord(value)) restoreDict(value, subject.state);
1262
1264
  return value;
1263
1265
  }
1264
1266
  if (subject.kind === "entity") {
@@ -1272,7 +1274,7 @@ const restoreRootSubject = (value, subject) => {
1272
1274
  return restoreChange(value, subject.state);
1273
1275
  };
1274
1276
  const restoreSubject = (value, prefix, subject) => {
1275
- if (sameAddress(prefix, subject.address)) return restoreRootSubject(value, subject);
1277
+ if (sameAddress$1(prefix, subject.address)) return restoreRootSubject(value, subject);
1276
1278
  const address = subject.address.slice(prefix.length);
1277
1279
  if (subject.kind === "value") {
1278
1280
  writePresence(value, address, subject.before);
@@ -1284,7 +1286,7 @@ const restoreSubject = (value, prefix, subject) => {
1284
1286
  }
1285
1287
  const current = require_dependency.read(value, address);
1286
1288
  if (subject.kind === "dict") {
1287
- if (require_dependency.isRecord(current)) restoreDict(current, subject.state);
1289
+ if (require_ownership.isRecord(current)) restoreDict(current, subject.state);
1288
1290
  return value;
1289
1291
  }
1290
1292
  if (subject.kind === "entity") {
@@ -1307,8 +1309,8 @@ const valueChanged = (root, subject) => {
1307
1309
  };
1308
1310
  const dictChanged = (root, subject) => {
1309
1311
  const value = require_dependency.read(root, subject.address);
1310
- if (!require_dependency.isRecord(value)) return true;
1311
- if (subject.state.mode === "whole") return !require_dependency.sameStructuralValue(subject.state.value, value);
1312
+ if (!require_ownership.isRecord(value)) return true;
1313
+ if (subject.state.mode === "whole") return !require_ownership.sameStructuralValue(subject.state.value, value);
1312
1314
  for (const [key, before] of subject.state.entries) {
1313
1315
  const exists = hasOwn(value, key);
1314
1316
  if (before.status === "absent" ? exists : !exists || !Object.is(before.value, value[key])) return true;
@@ -1318,7 +1320,7 @@ const dictChanged = (root, subject) => {
1318
1320
  const listChanged = (root, subject) => {
1319
1321
  const value = require_dependency.read(root, subject.address);
1320
1322
  if (!Array.isArray(value)) return true;
1321
- if (subject.state.mode === "whole") return !require_dependency.sameStructuralValue(subject.state.value, value);
1323
+ if (subject.state.mode === "whole") return !require_ownership.sameStructuralValue(subject.state.value, value);
1322
1324
  const positions = /* @__PURE__ */ new Map();
1323
1325
  const order = new Array(value.length);
1324
1326
  for (let index = 0; index < value.length; index += 1) {
@@ -1331,7 +1333,7 @@ const listChanged = (root, subject) => {
1331
1333
  const position = positions.get(key);
1332
1334
  if (before.status === "absent") {
1333
1335
  if (position !== void 0) return true;
1334
- } else if (position === void 0 || !require_dependency.sameStructuralValue(before.value, value[position])) return true;
1336
+ } else if (position === void 0 || !require_ownership.sameStructuralValue(before.value, value[position])) return true;
1335
1337
  }
1336
1338
  return false;
1337
1339
  };
@@ -1365,7 +1367,7 @@ const analyzeEntity = (root, subject) => {
1365
1367
  if (before.status === "absent") {
1366
1368
  if (exists) added.add(id);
1367
1369
  } else if (!exists) removed.add(id);
1368
- else if (!require_dependency.sameStructuralValue(before.value, collection.byId[id])) updated.add(id);
1370
+ else if (!require_ownership.sameStructuralValue(before.value, collection.byId[id])) updated.add(id);
1369
1371
  }
1370
1372
  const beforeOrder = subject.order.kind === "tracked" ? subject.order.before : void 0;
1371
1373
  const finalOrder = collection.kind === "table" ? collection.ids : [];
@@ -1380,7 +1382,7 @@ const analyzeEntity = (root, subject) => {
1380
1382
  };
1381
1383
  const subjectChanged = (root, subject) => {
1382
1384
  if (subject.kind === "value") return valueChanged(root, subject);
1383
- if (subject.kind === "variant") return !require_dependency.sameStructuralValue(subject.before, require_dependency.read(root, subject.address));
1385
+ if (subject.kind === "variant") return !require_ownership.sameStructuralValue(subject.before, require_dependency.read(root, subject.address));
1384
1386
  if (subject.kind === "dict") return dictChanged(root, subject);
1385
1387
  if (subject.kind === "list") return listChanged(root, subject);
1386
1388
  return changeChanged(subject.state, require_dependency.read(root, subject.address));
@@ -1469,7 +1471,7 @@ const createChangeJournal = (root) => {
1469
1471
  }
1470
1472
  if (node.subject) throw new Error(`Change journal already owns '${subject.address.join(".")}'.`);
1471
1473
  node.subject = subject;
1472
- require_dependency.profile.journal.subject();
1474
+ require_ownership.profile.journal.subject();
1473
1475
  return subject;
1474
1476
  };
1475
1477
  const remove = (subject) => {
@@ -1498,8 +1500,8 @@ const createChangeJournal = (root) => {
1498
1500
  function find(kind, address, addressHash) {
1499
1501
  const candidates = buckets.get(addressHash) ?? [];
1500
1502
  for (const subject of candidates) {
1501
- require_dependency.profile.journal.comparison();
1502
- if (subject.kind === kind && sameAddress(subject.address, address)) return subject;
1503
+ require_ownership.profile.journal.comparison();
1504
+ if (subject.kind === kind && sameAddress$1(subject.address, address)) return subject;
1503
1505
  }
1504
1506
  }
1505
1507
  const ownerOf = (address) => {
@@ -1515,11 +1517,11 @@ const createChangeJournal = (root) => {
1515
1517
  const subjects = indexedSubjects(index, address);
1516
1518
  if (subjects.length === 0) return value;
1517
1519
  subjects.sort((left, right) => left.address.length - right.address.length);
1518
- let before = require_dependency.ownPayload(value, "journal");
1520
+ let before = require_ownership.ownPayload(value, "journal");
1519
1521
  for (const subject of subjects) before = restoreSubject(before, address, subject);
1520
1522
  for (const subject of subjects) {
1521
1523
  remove(subject);
1522
- require_dependency.profile.journal.absorbed();
1524
+ require_ownership.profile.journal.absorbed();
1523
1525
  }
1524
1526
  return before;
1525
1527
  };
@@ -1561,7 +1563,7 @@ const createChangeJournal = (root) => {
1561
1563
  });
1562
1564
  if (subject.state.mode === "whole") return;
1563
1565
  if (first.type === "dict.replace") {
1564
- const value = require_dependency.ownPayload(first.value, "journal");
1566
+ const value = require_ownership.ownPayload(first.value, "journal");
1565
1567
  restoreDict(value, subject.state);
1566
1568
  subject.state = {
1567
1569
  mode: "whole",
@@ -1605,7 +1607,7 @@ const createChangeJournal = (root) => {
1605
1607
  }
1606
1608
  if (subject.state.mode === "whole") return;
1607
1609
  if (first.type === "list.replace") {
1608
- const value = Array.from(require_dependency.ownPayload(first.value, "journal"));
1610
+ const value = Array.from(require_ownership.ownPayload(first.value, "journal"));
1609
1611
  restoreList(value, subject);
1610
1612
  subject.state = {
1611
1613
  mode: "whole",
@@ -1618,7 +1620,7 @@ const createChangeJournal = (root) => {
1618
1620
  };
1619
1621
  const trackEntityOrder = (subject, collection) => {
1620
1622
  if (subject.order.kind !== "stable") return;
1621
- require_dependency.profile.journal.orderSnapshot(collection.ids.length);
1623
+ require_ownership.profile.journal.orderSnapshot(collection.ids.length);
1622
1624
  const before = [...collection.ids];
1623
1625
  restoreEntityOrder(before, subject.order.inverses);
1624
1626
  subject.order = {
@@ -1679,7 +1681,7 @@ const createChangeJournal = (root) => {
1679
1681
  };
1680
1682
  return {
1681
1683
  record: (resolved, operation, inverse) => {
1682
- require_dependency.profile.batch.journalRecord();
1684
+ require_ownership.profile.batch.journalRecord();
1683
1685
  if (ownerOf(operation.at)) return;
1684
1686
  if (operation.type === "field.set" || operation.type === "field.clear") {
1685
1687
  recordValue(resolved, operation, inverse);
@@ -1707,7 +1709,7 @@ const createChangeJournal = (root) => {
1707
1709
  const changed = [];
1708
1710
  const entityChanges = /* @__PURE__ */ new Map();
1709
1711
  for (const subject of active) {
1710
- require_dependency.profile.journal.comparison();
1712
+ require_ownership.profile.journal.comparison();
1711
1713
  if (subject.kind === "entity") {
1712
1714
  const analysis = analyzeEntity(root, subject);
1713
1715
  if (!analysis.changed) continue;
@@ -1721,7 +1723,7 @@ const createChangeJournal = (root) => {
1721
1723
  const changeAt = (address, addressHash) => {
1722
1724
  const candidates = collectionBuckets.get(addressHash) ?? [];
1723
1725
  collectionBuckets.set(addressHash, candidates);
1724
- let change = candidates.find((entry) => sameAddress(entry.address, address));
1726
+ let change = candidates.find((entry) => sameAddress$1(entry.address, address));
1725
1727
  if (!change) {
1726
1728
  change = {
1727
1729
  address,
@@ -1765,9 +1767,9 @@ const createChangeJournal = (root) => {
1765
1767
  const has = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
1766
1768
  const stringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
1767
1769
  const positions = (value) => Array.isArray(value) && value.every((entry) => Number.isInteger(entry) && entry >= 0);
1768
- const address = (value) => stringArray(value);
1770
+ const address$1 = (value) => stringArray(value);
1769
1771
  const anchor = (value) => {
1770
- if (!require_dependency.isRecord(value)) return false;
1772
+ if (!require_ownership.isRecord(value)) return false;
1771
1773
  if (Number(has(value, "at")) + Number(has(value, "before")) + Number(has(value, "after")) !== 1) return false;
1772
1774
  if (has(value, "at")) return value.at === "start" || value.at === "end";
1773
1775
  return typeof (has(value, "before") ? value.before : value.after) === "string";
@@ -1775,7 +1777,7 @@ const anchor = (value) => {
1775
1777
  const optionalAnchor = (value) => !has(value, "anchor") || value.anchor === void 0 || anchor(value.anchor);
1776
1778
  const optionalParent = (value) => !has(value, "parentId") || value.parentId === void 0 || typeof value.parentId === "string";
1777
1779
  const optionalIndex = (value) => !has(value, "index") || value.index === void 0 || Number.isInteger(value.index) && value.index >= 0;
1778
- const entries = (value) => Array.isArray(value) && value.every((entry) => require_dependency.isRecord(entry) && typeof entry.id === "string" && has(entry, "value"));
1780
+ const entries = (value) => Array.isArray(value) && value.every((entry) => require_ownership.isRecord(entry) && typeof entry.id === "string" && has(entry, "value"));
1779
1781
  const decoded = (operation) => ({
1780
1782
  status: "decoded",
1781
1783
  operation
@@ -1785,8 +1787,8 @@ const rejected = (code, message) => ({
1785
1787
  issue: input(code, message)
1786
1788
  });
1787
1789
  const decode = (input) => {
1788
- if (!require_dependency.isRecord(input)) return rejected("invalid-operation", "Operation must be an object.");
1789
- if (!address(input.at)) return rejected("invalid-address", "Operation address is malformed.");
1790
+ if (!require_ownership.isRecord(input)) return rejected("invalid-operation", "Operation must be an object.");
1791
+ if (!address$1(input.at)) return rejected("invalid-address", "Operation address is malformed.");
1790
1792
  if (typeof input.type !== "string") return rejected("invalid-operation", "Operation type must be a string.");
1791
1793
  switch (input.type) {
1792
1794
  case "field.set":
@@ -1794,7 +1796,7 @@ const decode = (input) => {
1794
1796
  case "field.clear": return decoded(input);
1795
1797
  case "dict.set": return typeof input.key === "string" && has(input, "value") ? decoded(input) : rejected("invalid-operation", "Dictionary set requires a string key and value.");
1796
1798
  case "dict.delete": return typeof input.key === "string" ? decoded(input) : rejected("invalid-operation", "Dictionary delete requires a string key.");
1797
- case "dict.replace": return require_dependency.isRecord(input.value) ? decoded(input) : rejected("invalid-operation", "Dictionary replacement must be an object.");
1799
+ case "dict.replace": return require_ownership.isRecord(input.value) ? decoded(input) : rejected("invalid-operation", "Dictionary replacement must be an object.");
1798
1800
  case "entity.create": return entries(input.entries) && optionalAnchor(input) && (!has(input, "positions") || input.positions === void 0 || positions(input.positions)) ? decoded(input) : rejected("invalid-operation", "Entity create payload is malformed.");
1799
1801
  case "entity.remove": return stringArray(input.ids) ? decoded(input) : rejected("invalid-operation", "Entity remove requires string ids.");
1800
1802
  case "entity.move": return typeof input.id === "string" && optionalAnchor(input) ? decoded(input) : rejected("invalid-operation", "Entity move payload is malformed.");
@@ -1819,11 +1821,12 @@ const decodeBatch = (input$1) => Array.isArray(input$1) ? {
1819
1821
  };
1820
1822
  const ownAddress = (value) => {
1821
1823
  if (Object.isFrozen(value)) return value;
1822
- require_dependency.profile.address.arrayCopied();
1824
+ require_ownership.profile.address.arrayCopied();
1823
1825
  return Object.freeze(value.slice());
1824
1826
  };
1827
+ const structuralPayload = (value) => Array.isArray(value) || require_ownership.isPlainObject(value);
1825
1828
  const normalize = (operation) => {
1826
- require_dependency.profile.mutation.normalized();
1829
+ require_ownership.profile.mutation.normalized();
1827
1830
  const copy = {
1828
1831
  ...operation,
1829
1832
  at: ownAddress(operation.at)
@@ -1836,23 +1839,34 @@ const normalize = (operation) => {
1836
1839
  return Object.freeze(copy);
1837
1840
  };
1838
1841
  const publish = (operation) => {
1839
- if (!require_dependency.operationOwnsStructuralPayload(operation)) return operation;
1840
- if (operation.type === "entity.create" && operation.entries.every((entry) => require_dependency.isStablePayload(entry.value))) return operation;
1842
+ if (operation.type === "entity.create" && operation.entries.every((entry) => !structuralPayload(entry.value) || require_ownership.isStablePayload(entry.value))) return operation;
1843
+ if ("value" in operation && (!structuralPayload(operation.value) || require_ownership.isStablePayload(operation.value))) return operation;
1841
1844
  const copy = { ...operation };
1842
1845
  if (copy.entries) copy.entries = Object.freeze(copy.entries.map((entry) => Object.freeze({
1843
1846
  id: entry.id,
1844
- value: require_dependency.isStablePayload(entry.value) ? entry.value : require_dependency.snapshotPayload(entry.value, "commit")
1847
+ value: structuralPayload(entry.value) ? require_ownership.isStablePayload(entry.value) ? entry.value : require_ownership.snapshotPayload(entry.value, "commit") : entry.value
1845
1848
  })));
1846
- else if ("value" in copy) copy.value = require_dependency.isStablePayload(copy.value) ? copy.value : require_dependency.snapshotPayload(copy.value, "commit");
1849
+ else if ("value" in copy && structuralPayload(copy.value)) copy.value = require_ownership.isStablePayload(copy.value) ? copy.value : require_ownership.snapshotPayload(copy.value, "commit");
1847
1850
  return Object.freeze(copy);
1848
1851
  };
1852
+ const requiresPayloadCopy = (operation) => {
1853
+ if (operation.type === "entity.create") return operation.entries.some((entry) => require_ownership.isStablePayload(entry.value));
1854
+ return "value" in operation && require_ownership.isStablePayload(operation.value);
1855
+ };
1849
1856
  const inverse = (operation) => {
1850
1857
  const copy = { ...operation };
1851
1858
  if (copy.anchor) copy.anchor = Object.freeze({ ...copy.anchor });
1852
1859
  if (copy.keys) copy.keys = Object.freeze([...copy.keys]);
1853
- if (copy.entries) copy.entries = Object.freeze(copy.entries.map((entry) => Object.freeze({ ...entry })));
1860
+ if (copy.entries) copy.entries = Object.freeze(copy.entries.map((entry) => Object.freeze({
1861
+ id: entry.id,
1862
+ value: structuralPayload(entry.value) ? require_ownership.isStablePayload(entry.value) ? entry.value : require_ownership.snapshotPayload(entry.value, "inverse") : entry.value
1863
+ })));
1854
1864
  if (copy.ids) copy.ids = Object.freeze([...copy.ids]);
1855
1865
  if (copy.positions) copy.positions = Object.freeze([...copy.positions]);
1866
+ if ("value" in copy && structuralPayload(copy.value)) {
1867
+ const payload = copy;
1868
+ payload.value = require_ownership.isStablePayload(payload.value) ? payload.value : require_ownership.snapshotPayload(payload.value, "inverse");
1869
+ }
1856
1870
  return Object.freeze(copy);
1857
1871
  };
1858
1872
  //#endregion
@@ -1860,32 +1874,32 @@ const inverse = (operation) => {
1860
1874
  const createMutationSession = (root, schema, options = {}) => {
1861
1875
  const steps = [];
1862
1876
  const journal = createChangeJournal(root);
1863
- let closed = false;
1877
+ let state = "active";
1864
1878
  const rollback = () => {
1865
- if (closed) return;
1879
+ if (state === "rolled-back") return;
1866
1880
  for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) for (const inverse of steps[stepIndex].inverse) {
1867
1881
  const result = execute(root, inverse, require_dependency.resolveLocated(schema, root, inverse.at), true);
1868
1882
  if (result.status === "rejected") throw new Error(`Document rollback failed: ${result.issue.message}`);
1869
1883
  }
1870
- closed = true;
1884
+ state = "rolled-back";
1871
1885
  };
1872
1886
  return {
1873
1887
  apply: (input) => {
1874
- if (closed) throw new Error("Mutation session is closed.");
1875
- require_dependency.profile.batch.operation();
1888
+ if (state !== "active") throw new Error("Mutation session is closed.");
1889
+ require_ownership.profile.batch.operation();
1876
1890
  const decoded = decode(input);
1877
1891
  if (decoded.status === "rejected") {
1878
- require_dependency.profile.batch.rejected();
1892
+ require_ownership.profile.batch.rejected();
1879
1893
  return decoded.issue;
1880
1894
  }
1881
1895
  const normalized = normalize(decoded.operation);
1882
- require_dependency.profile.batch.entry(normalized.type === "entity.create" ? normalized.entries.length : normalized.type === "entity.remove" ? normalized.ids.length : 1);
1896
+ require_ownership.profile.batch.entry(normalized.type === "entity.create" ? normalized.entries.length : normalized.type === "entity.remove" ? normalized.ids.length : 1);
1883
1897
  const resolved = require_dependency.resolveLocated(schema, root, normalized.at);
1884
- if (resolved?.collection) require_dependency.profile.batch.collectionResolved();
1885
- require_dependency.profile.mutation.executed();
1886
- const result = execute(root, normalized, resolved, options.copyPayload === true);
1898
+ if (resolved?.collection) require_ownership.profile.batch.collectionResolved();
1899
+ require_ownership.profile.mutation.executed();
1900
+ const result = execute(root, normalized, resolved, options.copyPayload === true || requiresPayloadCopy(normalized));
1887
1901
  if (result.status === "rejected") {
1888
- require_dependency.profile.batch.rejected();
1902
+ require_ownership.profile.batch.rejected();
1889
1903
  return result.issue;
1890
1904
  }
1891
1905
  if (result.status === "unchanged") return void 0;
@@ -1894,8 +1908,8 @@ const createMutationSession = (root, schema, options = {}) => {
1894
1908
  for (const rawInverse of result.inverse) {
1895
1909
  const inverse$1 = inverse(rawInverse);
1896
1910
  group.push(inverse$1);
1897
- require_dependency.profile.mutation.inverse();
1898
- require_dependency.profile.batch.inverse();
1911
+ require_ownership.profile.mutation.inverse();
1912
+ require_ownership.profile.batch.inverse();
1899
1913
  }
1900
1914
  steps.push({
1901
1915
  forward: published,
@@ -1910,9 +1924,9 @@ const createMutationSession = (root, schema, options = {}) => {
1910
1924
  }
1911
1925
  },
1912
1926
  finish: () => {
1913
- if (closed) throw new Error("Mutation session is closed.");
1914
- closed = true;
1927
+ if (state !== "active") throw new Error("Mutation session is closed.");
1915
1928
  const changes = journal.finish();
1929
+ state = "finished";
1916
1930
  if (changes.status === "unchanged") return { status: "unchanged" };
1917
1931
  let inverseCount = 0;
1918
1932
  for (const step of steps) inverseCount += step.inverse.length;
@@ -1949,6 +1963,191 @@ const mutateOperations = (root, schema, operations, options = {}) => {
1949
1963
  return session.finish();
1950
1964
  };
1951
1965
  //#endregion
1966
+ //#region core/src/mutation/footprint.ts
1967
+ const sameAddress = (left, right) => left.length === right.length && left.every((segment, index) => segment === right[index]);
1968
+ const containsAddress = (parent, child) => parent.length <= child.length && parent.every((segment, index) => segment === child[index]);
1969
+ const address = (value) => Array.isArray(value) && value.every((segment) => typeof segment === "string") ? Object.freeze(value.slice()) : void 0;
1970
+ const targetKey = (target) => {
1971
+ const at = target.at.join("\0");
1972
+ if (target.kind === "dictionary-key" || target.kind === "list-item") return `${target.kind}:${at}:${target.key}`;
1973
+ if (target.kind === "entity" || target.kind === "tree-node") return `${target.kind}:${at}:${target.id}`;
1974
+ return `${target.kind}:${at}`;
1975
+ };
1976
+ const freezeTarget = (target) => Object.freeze({
1977
+ ...target,
1978
+ at: Object.freeze(target.at.slice())
1979
+ });
1980
+ const add = (targets, target) => {
1981
+ const stable = freezeTarget(target);
1982
+ targets.set(targetKey(stable), stable);
1983
+ };
1984
+ const commandFootprint = (operations) => {
1985
+ const targets = /* @__PURE__ */ new Map();
1986
+ for (const operation of operations) switch (operation.type) {
1987
+ case "field.set":
1988
+ case "field.clear":
1989
+ case "variant.replace":
1990
+ add(targets, {
1991
+ kind: "value",
1992
+ at: operation.at
1993
+ });
1994
+ break;
1995
+ case "dict.replace":
1996
+ add(targets, {
1997
+ kind: "dictionary",
1998
+ at: operation.at
1999
+ });
2000
+ break;
2001
+ case "dict.set":
2002
+ case "dict.delete":
2003
+ add(targets, {
2004
+ kind: "dictionary-key",
2005
+ at: operation.at,
2006
+ key: operation.key
2007
+ });
2008
+ break;
2009
+ case "entity.create":
2010
+ add(targets, {
2011
+ kind: "collection",
2012
+ at: operation.at
2013
+ });
2014
+ for (const entry of operation.entries) add(targets, {
2015
+ kind: "entity",
2016
+ at: operation.at,
2017
+ id: entry.id
2018
+ });
2019
+ break;
2020
+ case "entity.remove":
2021
+ add(targets, {
2022
+ kind: "collection",
2023
+ at: operation.at
2024
+ });
2025
+ for (const id of operation.ids) add(targets, {
2026
+ kind: "entity",
2027
+ at: operation.at,
2028
+ id
2029
+ });
2030
+ break;
2031
+ case "entity.move":
2032
+ add(targets, {
2033
+ kind: "collection",
2034
+ at: operation.at
2035
+ });
2036
+ add(targets, {
2037
+ kind: "entity",
2038
+ at: operation.at,
2039
+ id: operation.id
2040
+ });
2041
+ break;
2042
+ case "list.insert":
2043
+ case "list.move":
2044
+ case "list.remove":
2045
+ add(targets, {
2046
+ kind: "list",
2047
+ at: operation.at
2048
+ });
2049
+ add(targets, {
2050
+ kind: "list-item",
2051
+ at: operation.at,
2052
+ key: operation.key
2053
+ });
2054
+ break;
2055
+ case "list.replace":
2056
+ add(targets, {
2057
+ kind: "list",
2058
+ at: operation.at
2059
+ });
2060
+ for (const key of operation.keys) add(targets, {
2061
+ kind: "list-item",
2062
+ at: operation.at,
2063
+ key
2064
+ });
2065
+ break;
2066
+ case "tree.insert":
2067
+ case "tree.move":
2068
+ case "tree.remove":
2069
+ case "tree.set":
2070
+ add(targets, {
2071
+ kind: "tree",
2072
+ at: operation.at
2073
+ });
2074
+ add(targets, {
2075
+ kind: "tree-node",
2076
+ at: operation.at,
2077
+ id: operation.treeNodeId
2078
+ });
2079
+ break;
2080
+ case "tree.replace":
2081
+ add(targets, {
2082
+ kind: "tree",
2083
+ at: operation.at
2084
+ });
2085
+ break;
2086
+ }
2087
+ return Object.freeze(Array.from(targets.values()));
2088
+ };
2089
+ const overlapsValueAndEntity = (value, entity) => containsAddress([...entity.at, entity.id], value.at) || containsAddress(value.at, [...entity.at, entity.id]);
2090
+ const targetsOverlap = (left, right) => {
2091
+ if (left.kind === "value" && right.kind === "value") return containsAddress(left.at, right.at) || containsAddress(right.at, left.at);
2092
+ if (left.kind === "value" && right.kind === "entity") return overlapsValueAndEntity(left, right);
2093
+ if (left.kind === "entity" && right.kind === "value") return overlapsValueAndEntity(right, left);
2094
+ if (left.kind === "dictionary" && right.kind === "dictionary") return sameAddress(left.at, right.at);
2095
+ if (left.kind === "dictionary" && right.kind === "dictionary-key") return sameAddress(left.at, right.at);
2096
+ if (left.kind === "dictionary-key" && right.kind === "dictionary") return sameAddress(left.at, right.at);
2097
+ if (left.kind === "dictionary-key" && right.kind === "dictionary-key") return sameAddress(left.at, right.at) && left.key === right.key;
2098
+ if (left.kind === "collection" && right.kind === "collection") return sameAddress(left.at, right.at);
2099
+ if (left.kind === "collection" && right.kind === "entity") return sameAddress(left.at, right.at);
2100
+ if (left.kind === "entity" && right.kind === "collection") return sameAddress(left.at, right.at);
2101
+ if (left.kind === "entity" && right.kind === "entity") return sameAddress(left.at, right.at) && left.id === right.id;
2102
+ if (left.kind === "list" && (right.kind === "list" || right.kind === "list-item")) return sameAddress(left.at, right.at);
2103
+ if (right.kind === "list" && left.kind === "list-item") return sameAddress(left.at, right.at);
2104
+ if (left.kind === "list-item" && right.kind === "list-item") return sameAddress(left.at, right.at) && left.key === right.key;
2105
+ if (left.kind === "tree" && (right.kind === "tree" || right.kind === "tree-node")) return sameAddress(left.at, right.at);
2106
+ if (right.kind === "tree" && left.kind === "tree-node") return sameAddress(left.at, right.at);
2107
+ if (left.kind === "tree-node" && right.kind === "tree-node") return sameAddress(left.at, right.at) && left.id === right.id;
2108
+ return false;
2109
+ };
2110
+ const footprintsOverlap = (left, right) => left.some((leftTarget) => right.some((rightTarget) => targetsOverlap(leftTarget, rightTarget)));
2111
+ const decodedTarget = (value) => {
2112
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
2113
+ const record = value;
2114
+ const at = address(record.at);
2115
+ if (!at || typeof record.kind !== "string") return void 0;
2116
+ switch (record.kind) {
2117
+ case "value":
2118
+ case "dictionary":
2119
+ case "collection":
2120
+ case "list":
2121
+ case "tree": return freezeTarget({
2122
+ kind: record.kind,
2123
+ at
2124
+ });
2125
+ case "dictionary-key":
2126
+ case "list-item": return typeof record.key === "string" ? freezeTarget({
2127
+ kind: record.kind,
2128
+ at,
2129
+ key: record.key
2130
+ }) : void 0;
2131
+ case "entity":
2132
+ case "tree-node": return typeof record.id === "string" ? freezeTarget({
2133
+ kind: record.kind,
2134
+ at,
2135
+ id: record.id
2136
+ }) : void 0;
2137
+ default: return;
2138
+ }
2139
+ };
2140
+ const decodeCommandFootprint = (value) => {
2141
+ if (!Array.isArray(value)) return void 0;
2142
+ const targets = /* @__PURE__ */ new Map();
2143
+ for (const entry of value) {
2144
+ const target = decodedTarget(entry);
2145
+ if (!target) return void 0;
2146
+ targets.set(targetKey(target), target);
2147
+ }
2148
+ return Object.freeze(Array.from(targets.values()));
2149
+ };
2150
+ //#endregion
1952
2151
  //#region core/src/runtime/notification.ts
1953
2152
  const ROOT_BUCKET = Symbol("document-root");
1954
2153
  const notifications = /* @__PURE__ */ new WeakMap();
@@ -1971,6 +2170,9 @@ const notificationOf = (runtime) => {
1971
2170
  if (!value) throw new Error("Unknown Doxum runtime.");
1972
2171
  return value;
1973
2172
  };
2173
+ const shareNotification = (source, target) => {
2174
+ notifications.set(target, notificationOf(source));
2175
+ };
1974
2176
  const registerProcessor = (runtime, processor) => {
1975
2177
  const notification = notificationOf(runtime);
1976
2178
  const entry = {
@@ -2122,20 +2324,57 @@ const publishDiagnostic = (value) => Object.freeze({
2122
2324
  const createDocument = (input) => {
2123
2325
  const initialTreeError = invalidDocument(input.schema, input.initial);
2124
2326
  if (initialTreeError) throw new TypeError(`Initial document contains an invalid tree at '${initialTreeError.join(".")}'.`);
2125
- require_dependency.profile.clone.initialDocument();
2327
+ require_ownership.profile.clone.initialDocument();
2126
2328
  const state = {
2127
2329
  schema: input.schema,
2128
- document: require_dependency.cloneValue(input.initial, "initial"),
2330
+ document: require_ownership.cloneValue(input.initial, "initial"),
2129
2331
  disposed: false
2130
2332
  };
2131
2333
  let revision = 0;
2132
2334
  let busy = false;
2133
2335
  let runtime;
2134
2336
  let notification;
2135
- const assertWritable = () => {
2337
+ const assertWritable = (intent) => {
2136
2338
  if (state.disposed) throw new require_dependency.DocumentDisposedError();
2137
2339
  if (busy) throw new require_dependency.DocumentReentrancyError();
2340
+ require_driver.assertRuntimeWritable(runtime, intent);
2341
+ };
2342
+ const runTransaction = (session, run) => {
2343
+ let reports;
2344
+ let active = true;
2345
+ const transaction = {
2346
+ read: require_dependency.documentReader(input.schema, () => state.document, () => active),
2347
+ write: documentWriter(input.schema, (operation) => {
2348
+ if (!active) throw new Error("Document writer is no longer active.");
2349
+ const rejected = session.apply(operation);
2350
+ if (rejected) throw new RejectedUpdate([rejected]);
2351
+ }),
2352
+ reject: (diagnostic) => {
2353
+ throw new RejectedUpdate((Array.isArray(diagnostic) ? diagnostic : [diagnostic]).map((entry) => publishDiagnostic(entry)));
2354
+ },
2355
+ report: (diagnostic) => {
2356
+ if (!active) throw new Error("Document transaction is no longer active.");
2357
+ (reports ??= []).push(publishDiagnostic(diagnostic));
2358
+ }
2359
+ };
2360
+ try {
2361
+ const value = run(transaction);
2362
+ if (value !== null && typeof value === "object" && typeof value.then === "function") throw new TypeError("Document update callback must be synchronous.");
2363
+ return {
2364
+ value,
2365
+ reports: Object.freeze(reports ? reports.slice() : EMPTY)
2366
+ };
2367
+ } finally {
2368
+ active = false;
2369
+ }
2138
2370
  };
2371
+ const impactFor = (batch, kind) => createImpact({
2372
+ schema: input.schema,
2373
+ operations: batch.operations,
2374
+ paths: batch.paths,
2375
+ collections: batch.collections,
2376
+ reset: kind === "replace"
2377
+ });
2139
2378
  const history = createHistory({
2140
2379
  capacity: input.history === false ? 0 : Math.max(0, input.history?.capacity ?? 100),
2141
2380
  revision: () => revision,
@@ -2149,13 +2388,7 @@ const createDocument = (input) => {
2149
2388
  source,
2150
2389
  operations: batch.operations,
2151
2390
  inverse: batch.inverse,
2152
- impact: createImpact({
2153
- schema: input.schema,
2154
- operations: batch.operations,
2155
- paths: batch.paths,
2156
- collections: batch.collections,
2157
- reset: kind === "replace"
2158
- })
2391
+ impact: impactFor(batch, kind)
2159
2392
  });
2160
2393
  if (kind === "replace" || source === "remote") history.invalidate();
2161
2394
  else if (recordHistory && (source === "local" || source === "system")) history.record(batch.operations, batch.inverse);
@@ -2173,7 +2406,10 @@ const createDocument = (input) => {
2173
2406
  };
2174
2407
  };
2175
2408
  function applyBatch(operations, source, recordHistory) {
2176
- assertWritable();
2409
+ assertWritable({
2410
+ kind: "apply",
2411
+ source
2412
+ });
2177
2413
  const batch = mutateOperations(state.document, input.schema, operations, { copyPayload: source === "history" });
2178
2414
  if (batch.status === "rejected") return {
2179
2415
  status: "rejected",
@@ -2187,6 +2423,7 @@ const createDocument = (input) => {
2187
2423
  return publish(batch, source, "operations", recordHistory);
2188
2424
  }
2189
2425
  runtime = {
2426
+ schema: input.schema,
2190
2427
  address: {
2191
2428
  resolve: (address) => require_dependency.resolveAddress(input.schema, address, state.document),
2192
2429
  read: (address) => require_dependency.read(state.document, address),
@@ -2196,55 +2433,40 @@ const createDocument = (input) => {
2196
2433
  },
2197
2434
  revision: () => revision,
2198
2435
  update: (run, options) => {
2199
- assertWritable();
2436
+ const source = options?.source ?? "local";
2437
+ assertWritable({
2438
+ kind: "update",
2439
+ source
2440
+ });
2200
2441
  busy = true;
2201
2442
  const session = createMutationSession(state.document, input.schema);
2202
- let reports;
2203
- let active = true;
2204
- const transaction = {
2205
- read: require_dependency.documentReader(input.schema, () => state.document, () => active),
2206
- write: documentWriter(input.schema, (operation) => {
2207
- if (!active) throw new Error("Document writer is no longer active.");
2208
- const rejected = session.apply(operation);
2209
- if (rejected) throw new RejectedUpdate([rejected]);
2210
- }),
2211
- reject: (diagnostic) => {
2212
- throw new RejectedUpdate((Array.isArray(diagnostic) ? diagnostic : [diagnostic]).map((entry) => publishDiagnostic(entry)));
2213
- },
2214
- report: (diagnostic) => {
2215
- if (!active) throw new Error("Document transaction is no longer active.");
2216
- (reports ??= []).push(publishDiagnostic(diagnostic));
2217
- }
2218
- };
2443
+ let committed = false;
2219
2444
  try {
2220
- const value = run(transaction);
2221
- if (value !== null && typeof value === "object" && typeof value.then === "function") throw new TypeError("Document update callback must be synchronous.");
2222
- active = false;
2223
- busy = false;
2445
+ const { value, reports } = runTransaction(session, run);
2224
2446
  const batch = session.finish();
2225
2447
  if (batch.status === "unchanged") return {
2226
2448
  status: "unchanged",
2227
2449
  value,
2228
2450
  revision,
2229
- reports: Object.freeze(reports ?? EMPTY)
2451
+ reports
2230
2452
  };
2231
2453
  if (batch.status === "rejected") return {
2232
2454
  status: "rejected",
2233
2455
  issues: batch.issues,
2234
2456
  revision
2235
2457
  };
2236
- const result = publish(batch, options?.source ?? "local", "operations", options?.history ?? true);
2458
+ const result = publish(batch, source, "operations", options?.history ?? true);
2459
+ committed = true;
2237
2460
  return {
2238
2461
  status: "committed",
2239
2462
  value,
2240
2463
  commit: result.commit,
2241
- reports: Object.freeze(reports ?? EMPTY),
2464
+ reports,
2242
2465
  observerErrors: result.observerErrors
2243
2466
  };
2244
2467
  } catch (error) {
2245
- active = false;
2246
2468
  try {
2247
- session.rollback();
2469
+ if (!committed) session.rollback();
2248
2470
  } finally {
2249
2471
  busy = false;
2250
2472
  }
@@ -2255,31 +2477,86 @@ const createDocument = (input) => {
2255
2477
  };
2256
2478
  throw error;
2257
2479
  } finally {
2258
- active = false;
2480
+ busy = false;
2481
+ }
2482
+ },
2483
+ prepare: (run) => {
2484
+ assertWritable({ kind: "prepare" });
2485
+ busy = true;
2486
+ const session = createMutationSession(state.document, input.schema);
2487
+ try {
2488
+ const { value, reports } = runTransaction(session, run);
2489
+ const batch = session.finish();
2490
+ if (batch.status === "unchanged") {
2491
+ session.rollback();
2492
+ return {
2493
+ status: "unchanged",
2494
+ value,
2495
+ reports
2496
+ };
2497
+ }
2498
+ if (batch.status === "rejected") {
2499
+ session.rollback();
2500
+ return {
2501
+ status: "rejected",
2502
+ issues: batch.issues
2503
+ };
2504
+ }
2505
+ const impact = impactFor(batch, "operations");
2506
+ session.rollback();
2507
+ return {
2508
+ status: "prepared",
2509
+ value,
2510
+ operations: batch.operations,
2511
+ inverse: batch.inverse,
2512
+ impact,
2513
+ footprint: commandFootprint(batch.operations),
2514
+ reports
2515
+ };
2516
+ } catch (error) {
2517
+ try {
2518
+ session.rollback();
2519
+ } finally {
2520
+ busy = false;
2521
+ }
2522
+ if (error instanceof RejectedUpdate) return {
2523
+ status: "rejected",
2524
+ issues: error.issues
2525
+ };
2526
+ throw error;
2527
+ } finally {
2259
2528
  busy = false;
2260
2529
  }
2261
2530
  },
2262
2531
  apply: (operations, options) => applyBatch(operations, options?.source ?? "local", options?.history ?? true),
2263
2532
  replace: (document, options) => {
2264
- assertWritable();
2533
+ const source = options?.source ?? "system";
2534
+ assertWritable({
2535
+ kind: "replace",
2536
+ source
2537
+ });
2265
2538
  const invalidTree = invalidDocument(input.schema, document);
2266
2539
  if (invalidTree) return {
2267
2540
  status: "rejected",
2268
2541
  issues: [at$1(invalidTree, "invalid-tree", "Document replacement contains an invalid tree.")],
2269
2542
  revision
2270
2543
  };
2271
- if (require_dependency.deepEqual(state.document, document)) return {
2544
+ if (require_ownership.deepEqual(state.document, document)) return {
2272
2545
  status: "unchanged",
2273
2546
  revision
2274
2547
  };
2275
- state.document = require_dependency.cloneValue(document, "replace");
2548
+ state.document = require_ownership.cloneValue(document, "replace");
2276
2549
  return publish({
2277
2550
  status: "changed",
2278
2551
  operations: EMPTY,
2279
2552
  inverse: EMPTY,
2280
2553
  paths: EMPTY,
2281
2554
  collections: EMPTY
2282
- }, options?.source ?? "system", "replace", false);
2555
+ }, source, "replace", false);
2556
+ },
2557
+ snapshot: () => {
2558
+ if (state.disposed) throw new require_dependency.DocumentDisposedError();
2559
+ return require_ownership.cloneValue(state.document, "snapshot");
2283
2560
  },
2284
2561
  subscribe: ((targetOrListener, listener) => {
2285
2562
  if (state.disposed) throw new require_dependency.DocumentDisposedError();
@@ -2294,13 +2571,27 @@ const createDocument = (input) => {
2294
2571
  state.disposed = true;
2295
2572
  disposeNotification(notification);
2296
2573
  history.api.clear();
2574
+ require_driver.disposeRuntimeDriver(runtime);
2297
2575
  }
2298
2576
  };
2299
2577
  require_dependency.bindRuntimeAccess(runtime, state);
2578
+ require_driver.bindRuntimeDriver(runtime);
2300
2579
  notification = createNotification(runtime);
2301
2580
  return runtime;
2302
2581
  };
2303
2582
  //#endregion
2583
+ //#region core/src/runtime/readable.ts
2584
+ const asReadable = (runtime) => {
2585
+ const readable = {
2586
+ address: runtime.address,
2587
+ revision: runtime.revision,
2588
+ subscribe: runtime.subscribe
2589
+ };
2590
+ require_dependency.bindRuntimeAccess(readable, require_dependency.accessOf(runtime));
2591
+ shareNotification(runtime, readable);
2592
+ return Object.freeze(readable);
2593
+ };
2594
+ //#endregion
2304
2595
  //#region core/src/projection/select.ts
2305
2596
  const select = (runtime, selector) => require_dependency.readWith(runtime, selector);
2306
2597
  //#endregion
@@ -2318,7 +2609,7 @@ const createCollectionView = (input) => {
2318
2609
  shape: {}
2319
2610
  };
2320
2611
  const mapEntry = (document, id) => {
2321
- require_dependency.profile.collectionView.mapped();
2612
+ require_ownership.profile.collectionView.mapped();
2322
2613
  const address = [...input.source.address, String(id)];
2323
2614
  let active = true;
2324
2615
  try {
@@ -2334,7 +2625,7 @@ const createCollectionView = (input) => {
2334
2625
  const document = require_dependency.documentOf(input.runtime);
2335
2626
  const collection = require_dependency.read(document, input.source.address);
2336
2627
  const ids = collection && typeof collection === "object" && "ids" in collection && Array.isArray(collection.ids) ? [...collection.ids] : Object.keys(collection ?? {});
2337
- require_dependency.profile.collectionView.idsScanned(ids.length);
2628
+ require_ownership.profile.collectionView.idsScanned(ids.length);
2338
2629
  const values = previous && changed ? previous.values : /* @__PURE__ */ new Map();
2339
2630
  if (previous && changed) {
2340
2631
  const nextIds = new Set(ids);
@@ -2362,7 +2653,7 @@ const createCollectionView = (input) => {
2362
2653
  let state = build();
2363
2654
  const ensureAll = () => {
2364
2655
  if (!state.allDirty && state.all) return state.all;
2365
- require_dependency.profile.collectionView.arrayCopied();
2656
+ require_ownership.profile.collectionView.arrayCopied();
2366
2657
  const all = new Array(state.ids.length);
2367
2658
  for (let index = 0; index < state.ids.length; index += 1) all[index] = state.values.get(state.ids[index]);
2368
2659
  const stable = Object.freeze(all);
@@ -2519,7 +2810,7 @@ const createMaterializedView = (runtime, spec) => {
2519
2810
  entry.change = meta?.change;
2520
2811
  if (commitRevision !== void 0 && meta?.changedRevision === commitRevision) {
2521
2812
  sourceChanged = true;
2522
- require_dependency.profile.materialized.sourceChanged();
2813
+ require_ownership.profile.materialized.sourceChanged();
2523
2814
  }
2524
2815
  }
2525
2816
  return sourceValuesRecord;
@@ -2566,7 +2857,7 @@ const createMaterializedView = (runtime, spec) => {
2566
2857
  });
2567
2858
  };
2568
2859
  const build = () => {
2569
- require_dependency.profile.materialized.rebuilt();
2860
+ require_ownership.profile.materialized.rebuilt();
2570
2861
  dependencies.clear();
2571
2862
  let active = true;
2572
2863
  try {
@@ -2583,7 +2874,7 @@ const createMaterializedView = (runtime, spec) => {
2583
2874
  };
2584
2875
  build();
2585
2876
  const emit = () => {
2586
- require_dependency.profile.materialized.notification();
2877
+ require_ownership.profile.materialized.notification();
2587
2878
  Array.from(listeners).forEach((listener) => listener());
2588
2879
  };
2589
2880
  const process = (commit) => {
@@ -2597,13 +2888,13 @@ const createMaterializedView = (runtime, spec) => {
2597
2888
  }
2598
2889
  const sourceSnapshot = sourceValues(commit.revision);
2599
2890
  if (!sourceChanged && dependencies.size() > 0 && !dependencies.some(commit.impact.affects)) {
2600
- require_dependency.profile.materialized.skipped();
2891
+ require_ownership.profile.materialized.skipped();
2601
2892
  revision = commit.revision;
2602
2893
  writeMeta(void 0, currentValue, commit.revision, false);
2603
2894
  return;
2604
2895
  }
2605
2896
  dependencies.clear();
2606
- require_dependency.profile.materialized.updated();
2897
+ require_ownership.profile.materialized.updated();
2607
2898
  let active = true;
2608
2899
  let result;
2609
2900
  try {
@@ -2687,13 +2978,17 @@ const createMaterializedView = (runtime, spec) => {
2687
2978
  //#endregion
2688
2979
  exports.DocumentDisposedError = require_dependency.DocumentDisposedError;
2689
2980
  exports.DocumentReentrancyError = require_dependency.DocumentReentrancyError;
2981
+ exports.asReadable = asReadable;
2982
+ exports.commandFootprint = commandFootprint;
2690
2983
  exports.contains = require_dependency.contains;
2691
2984
  exports.createCollectionView = createCollectionView;
2692
2985
  exports.createDocument = createDocument;
2693
2986
  exports.createMaterializedView = createMaterializedView;
2694
2987
  exports.debugKey = require_dependency.debugKey;
2988
+ exports.decodeCommandFootprint = decodeCommandFootprint;
2695
2989
  exports.dict = require_dependency.dict;
2696
2990
  exports.field = require_dependency.field;
2991
+ exports.footprintsOverlap = footprintsOverlap;
2697
2992
  exports.list = require_dependency.list;
2698
2993
  exports.map = require_dependency.map;
2699
2994
  exports.object = require_dependency.object;