doxum 0.1.4 → 0.1.6

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 (48) hide show
  1. package/README.md +46 -5
  2. package/dist/{contract-BsY1XLiG.d.ts → contract-I99DUMbN.d.ts} +53 -40
  3. package/dist/{contract-D8kjdfqT.d.cts → contract-uYJTmT8o.d.cts} +53 -40
  4. package/dist/index.cjs +244 -630
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +3 -64
  7. package/dist/index.d.ts +3 -64
  8. package/dist/index.js +182 -569
  9. package/dist/index.js.map +1 -1
  10. package/dist/integration-Bs3pOk65.cjs +35 -0
  11. package/dist/integration-Bs3pOk65.cjs.map +1 -0
  12. package/dist/integration-xupBIKjU.js +30 -0
  13. package/dist/integration-xupBIKjU.js.map +1 -0
  14. package/dist/integration.cjs +4 -14
  15. package/dist/integration.d.cts +3 -2
  16. package/dist/integration.d.ts +3 -2
  17. package/dist/integration.js +3 -14
  18. package/dist/local-sync.cjs +1 -1
  19. package/dist/local-sync.d.cts +1 -1
  20. package/dist/local-sync.d.ts +1 -1
  21. package/dist/local-sync.js +1 -1
  22. package/dist/{ownership-CU-9DKJQ.cjs → ownership-CY0nPXGF.cjs} +4 -1
  23. package/dist/ownership-CY0nPXGF.cjs.map +1 -0
  24. package/dist/{ownership-B-VAPcce.js → ownership-CduRygE7.js} +4 -1
  25. package/dist/ownership-CduRygE7.js.map +1 -0
  26. package/dist/react.cjs +1 -1
  27. package/dist/react.js +1 -1
  28. package/dist/runtime-BJRXF0gq.js +1735 -0
  29. package/dist/runtime-BJRXF0gq.js.map +1 -0
  30. package/dist/runtime-CRijXjYp.cjs +1991 -0
  31. package/dist/runtime-CRijXjYp.cjs.map +1 -0
  32. package/dist/runtime-CUcvhFNP.d.ts +152 -0
  33. package/dist/runtime-C_vKHmhy.d.cts +152 -0
  34. package/package.json +1 -1
  35. package/skills/doxum-runtime/references/guide.en.md +35 -18
  36. package/skills/doxum-runtime/references/guide.zh-CN.md +26 -14
  37. package/skills/doxum-runtime/references/invariants.en.md +12 -9
  38. package/skills/doxum-runtime/references/invariants.zh-CN.md +2 -2
  39. package/skills/doxum-runtime/references/patterns.en.md +36 -56
  40. package/skills/doxum-runtime/references/patterns.zh-CN.md +27 -45
  41. package/dist/dependency-C5_R1tvQ.js +0 -496
  42. package/dist/dependency-C5_R1tvQ.js.map +0 -1
  43. package/dist/dependency-Dfzs3khk.cjs +0 -722
  44. package/dist/dependency-Dfzs3khk.cjs.map +0 -1
  45. package/dist/integration.cjs.map +0 -1
  46. package/dist/integration.js.map +0 -1
  47. package/dist/ownership-B-VAPcce.js.map +0 -1
  48. package/dist/ownership-CU-9DKJQ.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_dependency = require("./dependency-Dfzs3khk.cjs");
3
- const require_ownership = require("./ownership-CU-9DKJQ.cjs");
2
+ const require_runtime = require("./runtime-CRijXjYp.cjs");
3
+ const require_ownership = require("./ownership-CY0nPXGF.cjs");
4
4
  const require_driver = require("./driver-CNxqMVFH.cjs");
5
5
  //#region core/src/impact.ts
6
6
  const emptyCollectionImpact = Object.freeze({
@@ -29,7 +29,7 @@ const createImpact = (input) => {
29
29
  if (pathBuckets.get(void 0)?.some(test)) return true;
30
30
  return pathBuckets.get(address[0])?.some(test) ?? false;
31
31
  };
32
- const pathsAffect = (address) => address.length === 0 ? paths.length > 0 : somePath(address, (changed) => require_dependency.overlaps(changed, address));
32
+ const pathsAffect = (address) => address.length === 0 ? paths.length > 0 : somePath(address, (changed) => require_runtime.overlaps(changed, address));
33
33
  let collectionCache;
34
34
  const findCollection = (address) => input.collections?.find((entry) => entry.address.length === address.length && entry.address.every((segment, index) => segment === address[index]));
35
35
  return {
@@ -38,12 +38,12 @@ const createImpact = (input) => {
38
38
  affects: (value) => {
39
39
  require_ownership.profile.impact.affects();
40
40
  if (kind === "reset") return true;
41
- if (!require_dependency.belongs(value, input.schema)) return false;
42
- if (pathsAffect(require_dependency.address(value))) return true;
41
+ if (!require_runtime.belongs(value, input.schema)) return false;
42
+ if (pathsAffect(require_runtime.address(value))) return true;
43
43
  if (value.kind === "collection") {
44
- const collection = findCollection(require_dependency.address(value));
44
+ const collection = findCollection(require_runtime.address(value));
45
45
  if (!collection) return false;
46
- const id$1 = require_dependency.id(value);
46
+ const id$1 = require_runtime.id(value);
47
47
  if (id$1 !== void 0) return collection.added.has(id$1) || collection.removed.has(id$1) || collection.updated.has(id$1);
48
48
  return collection.added.size > 0 || collection.removed.size > 0 || collection.updated.size > 0 || collection.orderChanged;
49
49
  }
@@ -55,7 +55,7 @@ const createImpact = (input) => {
55
55
  const cached = collectionCache?.find((entry) => entry.address.length === selector.address.length && entry.address.every((segment, index) => segment === selector.address[index]))?.value;
56
56
  if (cached) return cached;
57
57
  const exact = findCollection(selector.address);
58
- if (somePath(selector.address, (changed) => require_dependency.contains(changed, selector.address) && changed.length < selector.address.length)) {
58
+ if (somePath(selector.address, (changed) => require_runtime.contains(changed, selector.address) && changed.length < selector.address.length)) {
59
59
  const reset = { kind: "reset" };
60
60
  (collectionCache ??= []).push({
61
61
  address: selector.address,
@@ -165,7 +165,7 @@ const createHistory = (input) => {
165
165
  };
166
166
  //#endregion
167
167
  //#region core/src/access/writer.ts
168
- const schemaRoot = (schema) => require_dependency.object(schema.shape);
168
+ const schemaRoot = (schema) => require_runtime.object(schema.shape);
169
169
  const emit = (sink, operation) => sink(operation);
170
170
  const writerFor = (node, inputAddress, sink) => {
171
171
  const address = Object.freeze(inputAddress);
@@ -196,7 +196,11 @@ const writerFor = (node, inputAddress, sink) => {
196
196
  type: "dict.replace",
197
197
  at: address,
198
198
  value
199
- })
199
+ }),
200
+ ...node.optional ? { clear: () => emit(sink, {
201
+ type: "value.clear",
202
+ at: address
203
+ }) } : {}
200
204
  };
201
205
  if (node.kind === "list") return {
202
206
  insert: (value, anchor) => emit(sink, {
@@ -222,7 +226,11 @@ const writerFor = (node, inputAddress, sink) => {
222
226
  at: address,
223
227
  value,
224
228
  keys: value.map(node.keyOf)
225
- })
229
+ }),
230
+ ...node.optional ? { clear: () => emit(sink, {
231
+ type: "value.clear",
232
+ at: address
233
+ }) } : {}
226
234
  };
227
235
  if (node.kind === "tree") return {
228
236
  insert: (treeNodeId, value, parentId, index) => emit(sink, {
@@ -255,7 +263,11 @@ const writerFor = (node, inputAddress, sink) => {
255
263
  type: "tree.replace",
256
264
  at: address,
257
265
  value
258
- })
266
+ }),
267
+ ...node.optional ? { clear: () => emit(sink, {
268
+ type: "value.clear",
269
+ at: address
270
+ }) } : {}
259
271
  };
260
272
  if (node.kind === "table") {
261
273
  let items;
@@ -319,11 +331,17 @@ const writerFor = (node, inputAddress, sink) => {
319
331
  };
320
332
  }
321
333
  if (node.kind === "single") return writerFor(node.value, address, sink);
322
- if (node.kind === "variant") return { replace: (value) => emit(sink, {
323
- type: "variant.replace",
324
- at: address,
325
- value
326
- }) };
334
+ if (node.kind === "variant") return {
335
+ replace: (value) => emit(sink, {
336
+ type: "variant.replace",
337
+ at: address,
338
+ value
339
+ }),
340
+ ...node.optional ? { clear: () => emit(sink, {
341
+ type: "value.clear",
342
+ at: address
343
+ }) } : {}
344
+ };
327
345
  let children;
328
346
  return new Proxy({}, { get: (_target, property) => {
329
347
  if (typeof property !== "string" || !node.shape[property]) return void 0;
@@ -541,8 +559,11 @@ const rejected$3 = (operation, code, message) => ({
541
559
  issue: from(operation, code, message)
542
560
  });
543
561
  const executeList = (target, operation, copyPayload) => {
544
- if (!Array.isArray(target.value) || target.node.kind !== "list") return rejected$3(operation, "invalid-address", "List target is invalid.");
545
- const list = target.value;
562
+ if (target.node.kind !== "list") return rejected$3(operation, "invalid-address", "List target is invalid.");
563
+ const absent = target.value === void 0 && "optional" in target.node && target.node.optional === true;
564
+ if (!Array.isArray(target.value) && !absent) return rejected$3(operation, "invalid-address", "List target is invalid.");
565
+ if (absent && operation.type !== "list.replace") return rejected$3(operation, "invalid-address", "Only list replacement can initialize an optional list.");
566
+ const list = Array.isArray(target.value) ? target.value : [];
546
567
  const orderedKeys = keys(list, target.node.keyOf);
547
568
  const own = (value) => copyPayload ? require_ownership.ownPayload(value) : value;
548
569
  if (operation.type === "list.replace") {
@@ -553,18 +574,23 @@ const executeList = (target, operation, copyPayload) => {
553
574
  if (seen.has(key) || target.node.keyOf(operation.value[index]) !== key) return rejected$3(operation, "invalid-list-keys", "List replacement keys are invalid.");
554
575
  seen.add(key);
555
576
  }
556
- if (require_ownership.sameStructuralValue(list, operation.value)) return { status: "unchanged" };
557
- const keys = new Array(list.length);
558
- for (let index = 0; index < list.length; index += 1) keys[index] = target.node.keyOf(list[index]);
559
- const inverse = {
577
+ if (!absent && require_ownership.sameStructuralValue(list, operation.value)) return { status: "unchanged" };
578
+ const keys = list.map(target.node.keyOf);
579
+ const inverse = absent ? {
580
+ type: "value.clear",
581
+ at: operation.at
582
+ } : {
560
583
  type: "list.replace",
561
584
  at: operation.at,
562
585
  value: require_ownership.cloneValue(list, "inverse"),
563
586
  keys
564
587
  };
565
588
  const owned = own(operation.value);
566
- list.length = owned.length;
567
- for (let index = 0; index < owned.length; index += 1) list[index] = owned[index];
589
+ if (absent) target.parent[target.key] = owned;
590
+ else {
591
+ list.length = owned.length;
592
+ for (let index = 0; index < owned.length; index += 1) list[index] = owned[index];
593
+ }
568
594
  return {
569
595
  status: "changed",
570
596
  inverse: [inverse]
@@ -1024,16 +1050,39 @@ const outcome = (operation, value) => value.status === "rejected" ? {
1024
1050
  issue: from(operation, value.code, value.message)
1025
1051
  } : value;
1026
1052
  const executeTree = (root, target, operation, copyPayload) => {
1027
- if (!is(target.value)) return {
1053
+ if (target.node.kind !== "tree") return {
1028
1054
  status: "rejected",
1029
1055
  issue: from(operation, "invalid-address", "Tree target is invalid.")
1030
1056
  };
1031
1057
  const own = (value) => copyPayload ? require_ownership.ownPayload(value) : value;
1058
+ const absent = target.value === void 0 && "optional" in target.node && target.node.optional === true;
1032
1059
  if (operation.type === "tree.replace") {
1060
+ if (absent) {
1061
+ if (!validate(operation.value)) return {
1062
+ status: "rejected",
1063
+ issue: from(operation, "invalid-tree", "Tree replacement is malformed.")
1064
+ };
1065
+ target.parent[target.key] = own(operation.value);
1066
+ return {
1067
+ status: "changed",
1068
+ inverse: [{
1069
+ type: "value.clear",
1070
+ at: operation.at
1071
+ }]
1072
+ };
1073
+ }
1074
+ if (!is(target.value)) return {
1075
+ status: "rejected",
1076
+ issue: from(operation, "invalid-address", "Tree target is invalid.")
1077
+ };
1033
1078
  const result = replace(target.value, operation);
1034
- if (result.status === "changed") require_dependency.set(root, operation.at, require_ownership.ownPayload(operation.value));
1079
+ if (result.status === "changed") require_runtime.set(root, operation.at, require_ownership.ownPayload(operation.value));
1035
1080
  return outcome(operation, result);
1036
1081
  }
1082
+ if (!is(target.value)) return {
1083
+ status: "rejected",
1084
+ issue: from(operation, "invalid-address", "Tree target is invalid.")
1085
+ };
1037
1086
  if (operation.type === "tree.insert") return outcome(operation, insert(target.value, operation, own(operation.value)));
1038
1087
  if (operation.type === "tree.set") return outcome(operation, set(target.value, operation, own(operation.value)));
1039
1088
  if (operation.type === "tree.remove") return outcome(operation, remove(target.value, operation));
@@ -1091,6 +1140,18 @@ const executeValue = (target, operation, copyPayload) => {
1091
1140
  inverse: [inverse]
1092
1141
  };
1093
1142
  }
1143
+ if (operation.type === "dict.replace" && target.value === void 0) {
1144
+ if (!("optional" in target.node) || !target.node.optional) return rejected$1(operation, "invalid-address", "Dictionary target is invalid.");
1145
+ const inverse = {
1146
+ type: "value.clear",
1147
+ at: operation.at
1148
+ };
1149
+ target.parent[target.key] = own(operation.value);
1150
+ return {
1151
+ status: "changed",
1152
+ inverse: [inverse]
1153
+ };
1154
+ }
1094
1155
  if (!require_ownership.isRecord(target.value)) return rejected$1(operation, "invalid-address", "Dictionary target is invalid.");
1095
1156
  if (operation.type === "dict.replace") {
1096
1157
  if (require_ownership.sameStructuralValue(target.value, operation.value)) return { status: "unchanged" };
@@ -1139,11 +1200,67 @@ const executeValue = (target, operation, copyPayload) => {
1139
1200
  };
1140
1201
  //#endregion
1141
1202
  //#region core/src/mutation/execute.ts
1203
+ const hasOwn$1 = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
1204
+ const executeClear = (target, operation) => {
1205
+ if (!("optional" in target.node) || !target.node.optional) return {
1206
+ status: "rejected",
1207
+ issue: from(operation, "required-field", "Only optional values can be cleared.")
1208
+ };
1209
+ if (!hasOwn$1(target.parent, target.key)) return { status: "unchanged" };
1210
+ if (target.node.kind !== "field" && target.node.kind !== "variant" && target.node.kind !== "dict" && target.node.kind !== "record" && target.node.kind !== "list" && target.node.kind !== "tree") return {
1211
+ status: "rejected",
1212
+ issue: from(operation, "invalid-operation", "This optional value has no clear operation.")
1213
+ };
1214
+ const inverse = (() => {
1215
+ switch (target.node.kind) {
1216
+ case "field": return {
1217
+ type: "field.set",
1218
+ at: operation.at,
1219
+ value: target.value
1220
+ };
1221
+ case "variant": return {
1222
+ type: "variant.replace",
1223
+ at: operation.at,
1224
+ value: require_ownership.cloneValue(target.value, "inverse")
1225
+ };
1226
+ case "dict":
1227
+ case "record": return {
1228
+ type: "dict.replace",
1229
+ at: operation.at,
1230
+ value: require_ownership.cloneValue(target.value, "inverse")
1231
+ };
1232
+ case "list":
1233
+ if (!Array.isArray(target.value)) return {
1234
+ type: "list.replace",
1235
+ at: operation.at,
1236
+ value: [],
1237
+ keys: []
1238
+ };
1239
+ return {
1240
+ type: "list.replace",
1241
+ at: operation.at,
1242
+ value: require_ownership.cloneValue(target.value, "inverse"),
1243
+ keys: target.value.map(target.node.keyOf)
1244
+ };
1245
+ case "tree": return {
1246
+ type: "tree.replace",
1247
+ at: operation.at,
1248
+ value: require_ownership.cloneValue(target.value, "inverse")
1249
+ };
1250
+ }
1251
+ })();
1252
+ delete target.parent[target.key];
1253
+ return {
1254
+ status: "changed",
1255
+ inverse: [inverse]
1256
+ };
1257
+ };
1142
1258
  const invalidTarget = (operation) => ({
1143
1259
  status: "rejected",
1144
1260
  issue: from(operation, "invalid-address", `Operation '${operation.type}' does not match the schema address.`)
1145
1261
  });
1146
1262
  const execute = (root, operation, target, copyPayload) => {
1263
+ if (operation.type === "value.clear") return !target ? invalidTarget(operation) : executeClear(target, operation);
1147
1264
  if (operation.type === "field.set" || operation.type === "field.clear") return !target || target.node.kind !== "field" ? invalidTarget(operation) : executeValue(target, operation, copyPayload);
1148
1265
  if (operation.type === "variant.replace") return !target || target.node.kind !== "variant" ? invalidTarget(operation) : executeValue(target, operation, copyPayload);
1149
1266
  if (operation.type === "dict.set" || operation.type === "dict.delete" || operation.type === "dict.replace") return !target || target.node.kind !== "dict" && target.node.kind !== "record" ? invalidTarget(operation) : executeValue(target, operation, copyPayload);
@@ -1209,14 +1326,14 @@ const indexedSubjects = (root, address) => {
1209
1326
  return subjects;
1210
1327
  };
1211
1328
  const writePresence = (root, address, before) => {
1212
- const target = require_dependency.locate(root, address);
1329
+ const target = require_runtime.locate(root, address);
1213
1330
  if (!target) return;
1214
1331
  if (before.status === "present") target.parent[target.key] = before.value;
1215
1332
  else if (Array.isArray(target.parent)) target.parent.splice(Number(target.key), 1);
1216
1333
  else delete target.parent[String(target.key)];
1217
1334
  };
1218
1335
  const writeContainer = (root, address, value) => {
1219
- const target = require_dependency.locate(root, address);
1336
+ const target = require_runtime.locate(root, address);
1220
1337
  if (!target) return;
1221
1338
  target.parent[target.key] = value;
1222
1339
  };
@@ -1284,7 +1401,7 @@ const restoreSubject = (value, prefix, subject) => {
1284
1401
  writeContainer(value, address, subject.before);
1285
1402
  return value;
1286
1403
  }
1287
- const current = require_dependency.read(value, address);
1404
+ const current = require_runtime.read(value, address);
1288
1405
  if (subject.kind === "dict") {
1289
1406
  if (require_ownership.isRecord(current)) restoreDict(current, subject.state);
1290
1407
  return value;
@@ -1302,13 +1419,13 @@ const restoreSubject = (value, prefix, subject) => {
1302
1419
  return value;
1303
1420
  };
1304
1421
  const valueChanged = (root, subject) => {
1305
- const target = require_dependency.locate(root, subject.address);
1422
+ const target = require_runtime.locate(root, subject.address);
1306
1423
  const exists = !!target && hasOwn(target.parent, target.key);
1307
1424
  if (subject.before.status === "absent") return exists;
1308
1425
  return !exists || !Object.is(subject.before.value, target?.value);
1309
1426
  };
1310
1427
  const dictChanged = (root, subject) => {
1311
- const value = require_dependency.read(root, subject.address);
1428
+ const value = require_runtime.read(root, subject.address);
1312
1429
  if (!require_ownership.isRecord(value)) return true;
1313
1430
  if (subject.state.mode === "whole") return !require_ownership.sameStructuralValue(subject.state.value, value);
1314
1431
  for (const [key, before] of subject.state.entries) {
@@ -1318,7 +1435,7 @@ const dictChanged = (root, subject) => {
1318
1435
  return false;
1319
1436
  };
1320
1437
  const listChanged = (root, subject) => {
1321
- const value = require_dependency.read(root, subject.address);
1438
+ const value = require_runtime.read(root, subject.address);
1322
1439
  if (!Array.isArray(value)) return true;
1323
1440
  if (subject.state.mode === "whole") return !require_ownership.sameStructuralValue(subject.state.value, value);
1324
1441
  const positions = /* @__PURE__ */ new Map();
@@ -1350,7 +1467,7 @@ const commonOrderChanged = (before, after) => {
1350
1467
  return false;
1351
1468
  };
1352
1469
  const analyzeEntity = (root, subject) => {
1353
- const resolution = entityCollection(subject, require_dependency.read(root, subject.address));
1470
+ const resolution = entityCollection(subject, require_runtime.read(root, subject.address));
1354
1471
  const added = /* @__PURE__ */ new Set();
1355
1472
  const removed = /* @__PURE__ */ new Set();
1356
1473
  const updated = /* @__PURE__ */ new Set();
@@ -1382,10 +1499,10 @@ const analyzeEntity = (root, subject) => {
1382
1499
  };
1383
1500
  const subjectChanged = (root, subject) => {
1384
1501
  if (subject.kind === "value") return valueChanged(root, subject);
1385
- if (subject.kind === "variant") return !require_ownership.sameStructuralValue(subject.before, require_dependency.read(root, subject.address));
1502
+ if (subject.kind === "variant") return !require_ownership.sameStructuralValue(subject.before, require_runtime.read(root, subject.address));
1386
1503
  if (subject.kind === "dict") return dictChanged(root, subject);
1387
1504
  if (subject.kind === "list") return listChanged(root, subject);
1388
- return changeChanged(subject.state, require_dependency.read(root, subject.address));
1505
+ return changeChanged(subject.state, require_runtime.read(root, subject.address));
1389
1506
  };
1390
1507
  const listOrderBefore = (value, node, inverse) => {
1391
1508
  const order = value.map(node.keyOf);
@@ -1533,11 +1650,12 @@ const createChangeJournal = (root) => {
1533
1650
  const recordValue = (resolved, operation, inverse) => {
1534
1651
  if (find("value", operation.at, resolved.addressHash)) return;
1535
1652
  const first = inverse[0];
1536
- if (first?.type !== "field.set" && first?.type !== "field.clear") return inverseMismatch(operation);
1653
+ if (first?.type !== "field.set" && first?.type !== "field.clear" && first?.type !== "value.clear" && !("value" in (first ?? {}))) return inverseMismatch(operation);
1654
+ const before = first.type === "field.clear" || first.type === "value.clear" ? absent : "value" in first ? present(first.value) : inverseMismatch(operation);
1537
1655
  add({
1538
1656
  ...base(resolved, operation),
1539
1657
  kind: "value",
1540
- before: first.type === "field.set" ? present(first.value) : absent
1658
+ before
1541
1659
  });
1542
1660
  };
1543
1661
  const recordVariant = (resolved, operation, inverse) => {
@@ -1580,6 +1698,18 @@ const createChangeJournal = (root) => {
1580
1698
  if (!first) return inverseMismatch(operation);
1581
1699
  let subject = find("list", operation.at, resolved.addressHash);
1582
1700
  if (!subject) {
1701
+ if (first.type === "value.clear") {
1702
+ add({
1703
+ ...base(resolved, operation),
1704
+ kind: "list",
1705
+ node: resolved.node,
1706
+ state: {
1707
+ mode: "whole",
1708
+ value: void 0
1709
+ }
1710
+ });
1711
+ return;
1712
+ }
1583
1713
  if (first.type === "list.replace") {
1584
1714
  add({
1585
1715
  ...base(resolved, operation),
@@ -1592,7 +1722,7 @@ const createChangeJournal = (root) => {
1592
1722
  });
1593
1723
  return;
1594
1724
  }
1595
- const value = require_dependency.read(root, operation.at);
1725
+ const value = require_runtime.read(root, operation.at);
1596
1726
  if (!Array.isArray(value)) return inverseMismatch(operation);
1597
1727
  subject = add({
1598
1728
  ...base(resolved, operation),
@@ -1630,7 +1760,7 @@ const createChangeJournal = (root) => {
1630
1760
  };
1631
1761
  const recordEntity = (resolved, operation, inverse) => {
1632
1762
  if (resolved.node.kind !== "table" && resolved.node.kind !== "map") return inverseMismatch(operation);
1633
- const resolution = resolveEntityCollection(resolved.node, require_dependency.read(root, operation.at));
1763
+ const resolution = resolveEntityCollection(resolved.node, require_runtime.read(root, operation.at));
1634
1764
  if (resolution.status === "invalid") return inverseMismatch(operation);
1635
1765
  const collection = resolution.collection;
1636
1766
  const subject = find("entity", operation.at, resolved.addressHash) ?? add({
@@ -1666,7 +1796,23 @@ const createChangeJournal = (root) => {
1666
1796
  return inverseMismatch(operation);
1667
1797
  };
1668
1798
  const recordTree = (resolved, operation, inverse) => {
1669
- const current = require_dependency.read(root, operation.at);
1799
+ const current = require_runtime.read(root, operation.at);
1800
+ if (inverse.length === 1 && inverse[0].type === "value.clear" && operation.type === "tree.replace") {
1801
+ const subject = find("tree", operation.at, resolved.addressHash);
1802
+ if (subject) subject.state = {
1803
+ mode: "whole",
1804
+ value: void 0
1805
+ };
1806
+ else add({
1807
+ ...base(resolved, operation),
1808
+ kind: "tree",
1809
+ state: {
1810
+ mode: "whole",
1811
+ value: void 0
1812
+ }
1813
+ });
1814
+ return;
1815
+ }
1670
1816
  if (!is(current)) return inverseMismatch(operation);
1671
1817
  const inverses = inverse.filter(treeOperation);
1672
1818
  if (inverses.length === 0 || inverses.length !== inverse.length) return inverseMismatch(operation);
@@ -1683,7 +1829,7 @@ const createChangeJournal = (root) => {
1683
1829
  record: (resolved, operation, inverse) => {
1684
1830
  require_ownership.profile.batch.journalRecord();
1685
1831
  if (ownerOf(operation.at)) return;
1686
- if (operation.type === "field.set" || operation.type === "field.clear") {
1832
+ if (operation.type === "field.set" || operation.type === "field.clear" || operation.type === "value.clear") {
1687
1833
  recordValue(resolved, operation, inverse);
1688
1834
  return;
1689
1835
  }
@@ -1692,7 +1838,8 @@ const createChangeJournal = (root) => {
1692
1838
  return;
1693
1839
  }
1694
1840
  if (operation.type === "dict.set" || operation.type === "dict.delete" || operation.type === "dict.replace") {
1695
- recordDict(resolved, operation, inverse);
1841
+ if (operation.type === "dict.replace" && inverse[0]?.type === "value.clear") recordValue(resolved, operation, inverse);
1842
+ else recordDict(resolved, operation, inverse);
1696
1843
  return;
1697
1844
  }
1698
1845
  if (operation.type === "entity.create" || operation.type === "entity.remove" || operation.type === "entity.move") {
@@ -1793,7 +1940,8 @@ const decode = (input) => {
1793
1940
  switch (input.type) {
1794
1941
  case "field.set":
1795
1942
  case "variant.replace": return has(input, "value") ? decoded(input) : rejected("invalid-operation", "Operation value is required.");
1796
- case "field.clear": return decoded(input);
1943
+ case "field.clear":
1944
+ case "value.clear": return decoded(input);
1797
1945
  case "dict.set": return typeof input.key === "string" && has(input, "value") ? decoded(input) : rejected("invalid-operation", "Dictionary set requires a string key and value.");
1798
1946
  case "dict.delete": return typeof input.key === "string" ? decoded(input) : rejected("invalid-operation", "Dictionary delete requires a string key.");
1799
1947
  case "dict.replace": return require_ownership.isRecord(input.value) ? decoded(input) : rejected("invalid-operation", "Dictionary replacement must be an object.");
@@ -1878,7 +2026,7 @@ const createMutationSession = (root, schema, options = {}) => {
1878
2026
  const rollback = () => {
1879
2027
  if (state === "rolled-back") return;
1880
2028
  for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) for (const inverse of steps[stepIndex].inverse) {
1881
- const result = execute(root, inverse, require_dependency.resolveLocated(schema, root, inverse.at), true);
2029
+ const result = execute(root, inverse, require_runtime.resolveLocated(schema, root, inverse.at), true);
1882
2030
  if (result.status === "rejected") throw new Error(`Document rollback failed: ${result.issue.message}`);
1883
2031
  }
1884
2032
  state = "rolled-back";
@@ -1894,7 +2042,7 @@ const createMutationSession = (root, schema, options = {}) => {
1894
2042
  }
1895
2043
  const normalized = normalize(decoded.operation);
1896
2044
  require_ownership.profile.batch.entry(normalized.type === "entity.create" ? normalized.entries.length : normalized.type === "entity.remove" ? normalized.ids.length : 1);
1897
- const resolved = require_dependency.resolveLocated(schema, root, normalized.at);
2045
+ const resolved = require_runtime.resolveLocated(schema, root, normalized.at);
1898
2046
  if (resolved?.collection) require_ownership.profile.batch.collectionResolved();
1899
2047
  require_ownership.profile.mutation.executed();
1900
2048
  const result = execute(root, normalized, resolved, options.copyPayload === true || requiresPayloadCopy(normalized));
@@ -1986,6 +2134,7 @@ const commandFootprint = (operations) => {
1986
2134
  for (const operation of operations) switch (operation.type) {
1987
2135
  case "field.set":
1988
2136
  case "field.clear":
2137
+ case "value.clear":
1989
2138
  case "variant.replace":
1990
2139
  add(targets, {
1991
2140
  kind: "value",
@@ -2148,166 +2297,6 @@ const decodeCommandFootprint = (value) => {
2148
2297
  return Object.freeze(Array.from(targets.values()));
2149
2298
  };
2150
2299
  //#endregion
2151
- //#region core/src/runtime/notification.ts
2152
- const ROOT_BUCKET = Symbol("document-root");
2153
- const notifications = /* @__PURE__ */ new WeakMap();
2154
- const key = (value) => require_dependency.bucket(value) ?? ROOT_BUCKET;
2155
- const createNotification = (runtime) => {
2156
- const notification = {
2157
- root: /* @__PURE__ */ new Set(),
2158
- filtered: /* @__PURE__ */ new Set(),
2159
- buckets: /* @__PURE__ */ new Map(),
2160
- processors: [],
2161
- candidates: /* @__PURE__ */ new Set(),
2162
- rootSnapshot: [],
2163
- notifying: false
2164
- };
2165
- notifications.set(runtime, notification);
2166
- return notification;
2167
- };
2168
- const notificationOf = (runtime) => {
2169
- const value = notifications.get(runtime);
2170
- if (!value) throw new Error("Unknown Doxum runtime.");
2171
- return value;
2172
- };
2173
- const shareNotification = (source, target) => {
2174
- notifications.set(target, notificationOf(source));
2175
- };
2176
- const registerProcessor = (runtime, processor) => {
2177
- const notification = notificationOf(runtime);
2178
- const entry = {
2179
- processor,
2180
- active: true
2181
- };
2182
- notification.processors.push(entry);
2183
- return () => {
2184
- if (!entry.active) return;
2185
- entry.active = false;
2186
- if (!notification.notifying) {
2187
- const index = notification.processors.indexOf(entry);
2188
- if (index >= 0) notification.processors.splice(index, 1);
2189
- }
2190
- };
2191
- };
2192
- const subscribeRoot = (notification, listener) => {
2193
- const entry = {
2194
- listener,
2195
- active: true
2196
- };
2197
- notification.root.add(entry);
2198
- return () => {
2199
- entry.active = false;
2200
- if (!notification.notifying) notification.root.delete(entry);
2201
- };
2202
- };
2203
- const subscribeTargets = (notification, targets, listener) => {
2204
- const entry = {
2205
- targets: Object.freeze(targets.slice()),
2206
- listener,
2207
- active: true
2208
- };
2209
- notification.filtered.add(entry);
2210
- const keys = new Set(targets.map(key));
2211
- keys.forEach((key) => {
2212
- const bucket = notification.buckets.get(key) ?? /* @__PURE__ */ new Set();
2213
- bucket.add(entry);
2214
- notification.buckets.set(key, bucket);
2215
- });
2216
- return () => {
2217
- if (!entry.active) return;
2218
- entry.active = false;
2219
- if (notification.notifying) return;
2220
- notification.filtered.delete(entry);
2221
- keys.forEach((key) => {
2222
- const bucket = notification.buckets.get(key);
2223
- bucket?.delete(entry);
2224
- if (bucket?.size === 0) notification.buckets.delete(key);
2225
- });
2226
- };
2227
- };
2228
- const notify = (notification, commit) => {
2229
- notification.notifying = true;
2230
- const errors = [];
2231
- const call = (listener) => {
2232
- try {
2233
- listener(commit);
2234
- } catch (error) {
2235
- errors.push(Object.freeze({
2236
- phase: "listener",
2237
- error
2238
- }));
2239
- }
2240
- };
2241
- try {
2242
- const processorCount = notification.processors.length;
2243
- for (let index = 0; index < processorCount; index += 1) {
2244
- const entry = notification.processors[index];
2245
- if (!entry?.active) continue;
2246
- try {
2247
- entry.processor.process(commit);
2248
- } catch (error) {
2249
- errors.push(Object.freeze({
2250
- phase: "processor",
2251
- error
2252
- }));
2253
- }
2254
- }
2255
- for (let index = 0; index < processorCount; index += 1) {
2256
- const entry = notification.processors[index];
2257
- if (!entry?.active) continue;
2258
- try {
2259
- entry.processor.flush();
2260
- } catch (error) {
2261
- errors.push(Object.freeze({
2262
- phase: "flush",
2263
- error
2264
- }));
2265
- }
2266
- }
2267
- const candidates = notification.candidates;
2268
- candidates.clear();
2269
- if (commit.impact.kind === "reset") notification.filtered.forEach((entry) => candidates.add(entry));
2270
- else for (const operation of commit.operations) {
2271
- const key = operation.at[0] ?? ROOT_BUCKET;
2272
- notification.buckets.get(key)?.forEach((entry) => candidates.add(entry));
2273
- notification.buckets.get(ROOT_BUCKET)?.forEach((entry) => candidates.add(entry));
2274
- }
2275
- candidates.forEach((entry) => {
2276
- if (entry.active && entry.targets.some(commit.impact.affects)) call(entry.listener);
2277
- });
2278
- const rootSnapshot = notification.rootSnapshot;
2279
- rootSnapshot.length = 0;
2280
- notification.root.forEach((entry) => rootSnapshot.push(entry));
2281
- for (const entry of rootSnapshot) if (entry.active) call(entry.listener);
2282
- } finally {
2283
- notification.notifying = false;
2284
- let writeIndex = 0;
2285
- for (const entry of notification.processors) if (entry.active) notification.processors[writeIndex++] = entry;
2286
- notification.processors.length = writeIndex;
2287
- notification.root.forEach((entry) => {
2288
- if (!entry.active) notification.root.delete(entry);
2289
- });
2290
- notification.filtered.forEach((entry) => {
2291
- if (!entry.active) notification.filtered.delete(entry);
2292
- });
2293
- notification.buckets.forEach((bucket, key) => {
2294
- bucket.forEach((entry) => {
2295
- if (!entry.active) bucket.delete(entry);
2296
- });
2297
- if (bucket.size === 0) notification.buckets.delete(key);
2298
- });
2299
- }
2300
- return Object.freeze(errors);
2301
- };
2302
- const disposeNotification = (notification) => {
2303
- notification.root.clear();
2304
- notification.filtered.clear();
2305
- notification.buckets.clear();
2306
- notification.processors.length = 0;
2307
- notification.candidates.clear();
2308
- notification.rootSnapshot.length = 0;
2309
- };
2310
- //#endregion
2311
2300
  //#region core/src/runtime.ts
2312
2301
  var RejectedUpdate = class extends Error {
2313
2302
  issues;
@@ -2335,15 +2324,16 @@ const createDocument = (input) => {
2335
2324
  let runtime;
2336
2325
  let notification;
2337
2326
  const assertWritable = (intent) => {
2338
- if (state.disposed) throw new require_dependency.DocumentDisposedError();
2339
- if (busy) throw new require_dependency.DocumentReentrancyError();
2327
+ if (state.disposed) throw new require_runtime.DocumentDisposedError();
2328
+ if (busy) throw new require_runtime.DocumentReentrancyError();
2329
+ if (require_runtime.accessOf(runtime).projectionLocks) throw new require_runtime.DocumentReentrancyError();
2340
2330
  require_driver.assertRuntimeWritable(runtime, intent);
2341
2331
  };
2342
2332
  const runTransaction = (session, run) => {
2343
2333
  let reports;
2344
2334
  let active = true;
2345
2335
  const transaction = {
2346
- read: require_dependency.documentReader(input.schema, () => state.document, () => active),
2336
+ read: require_runtime.documentReader(input.schema, () => state.document, () => active),
2347
2337
  write: documentWriter(input.schema, (operation) => {
2348
2338
  if (!active) throw new Error("Document writer is no longer active.");
2349
2339
  const rejected = session.apply(operation);
@@ -2395,7 +2385,7 @@ const createDocument = (input) => {
2395
2385
  busy = true;
2396
2386
  let observerErrors;
2397
2387
  try {
2398
- observerErrors = notify(notification, commit);
2388
+ observerErrors = require_runtime.notify(notification, commit);
2399
2389
  } finally {
2400
2390
  busy = false;
2401
2391
  }
@@ -2425,11 +2415,11 @@ const createDocument = (input) => {
2425
2415
  runtime = {
2426
2416
  schema: input.schema,
2427
2417
  address: {
2428
- resolve: (address) => require_dependency.resolveAddress(input.schema, address, state.document),
2429
- read: (address) => require_dependency.read(state.document, address),
2430
- contains: require_dependency.contains,
2431
- overlaps: require_dependency.overlaps,
2432
- debugKey: require_dependency.debugKey
2418
+ resolve: (address) => require_runtime.resolveAddress(input.schema, address, state.document),
2419
+ read: (address) => require_runtime.read(state.document, address),
2420
+ contains: require_runtime.contains,
2421
+ overlaps: require_runtime.overlaps,
2422
+ debugKey: require_runtime.debugKey
2433
2423
  },
2434
2424
  revision: () => revision,
2435
2425
  update: (run, options) => {
@@ -2555,28 +2545,32 @@ const createDocument = (input) => {
2555
2545
  }, source, "replace", false);
2556
2546
  },
2557
2547
  snapshot: () => {
2558
- if (state.disposed) throw new require_dependency.DocumentDisposedError();
2548
+ if (state.disposed) throw new require_runtime.DocumentDisposedError();
2559
2549
  return require_ownership.cloneValue(state.document, "snapshot");
2560
2550
  },
2561
2551
  subscribe: ((targetOrListener, listener) => {
2562
- if (state.disposed) throw new require_dependency.DocumentDisposedError();
2563
- if (typeof targetOrListener === "function") return subscribeRoot(notification, targetOrListener);
2552
+ if (state.disposed) throw new require_runtime.DocumentDisposedError();
2553
+ if (typeof targetOrListener === "function") return require_runtime.subscribeRoot(notification, targetOrListener);
2564
2554
  const targets = Array.isArray(targetOrListener) ? targetOrListener : [targetOrListener];
2565
2555
  if (targets.length === 0 || !listener) throw new TypeError("Filtered subscribe requires at least one target and a listener.");
2566
- return subscribeTargets(notification, targets, listener);
2556
+ return require_runtime.subscribeTargets(notification, targets, listener);
2567
2557
  }),
2568
2558
  history: history.api,
2569
2559
  dispose: () => {
2570
2560
  if (state.disposed) return;
2561
+ if (busy || require_runtime.accessOf(runtime).projectionLocks) throw new require_runtime.DocumentReentrancyError();
2571
2562
  state.disposed = true;
2572
- disposeNotification(notification);
2573
- history.api.clear();
2574
- require_driver.disposeRuntimeDriver(runtime);
2563
+ try {
2564
+ require_runtime.disposeNotification(notification);
2565
+ } finally {
2566
+ history.api.clear();
2567
+ require_driver.disposeRuntimeDriver(runtime);
2568
+ }
2575
2569
  }
2576
2570
  };
2577
- require_dependency.bindRuntimeAccess(runtime, state);
2571
+ require_runtime.bindRuntimeAccess(runtime, state);
2578
2572
  require_driver.bindRuntimeDriver(runtime);
2579
- notification = createNotification(runtime);
2573
+ notification = require_runtime.createNotification(runtime);
2580
2574
  return runtime;
2581
2575
  };
2582
2576
  //#endregion
@@ -2587,427 +2581,47 @@ const asReadable = (runtime) => {
2587
2581
  revision: runtime.revision,
2588
2582
  subscribe: runtime.subscribe
2589
2583
  };
2590
- require_dependency.bindRuntimeAccess(readable, require_dependency.accessOf(runtime));
2591
- shareNotification(runtime, readable);
2584
+ require_runtime.bindRuntimeAccess(readable, require_runtime.accessOf(runtime));
2585
+ require_runtime.shareNotification(runtime, readable);
2592
2586
  return Object.freeze(readable);
2593
2587
  };
2594
2588
  //#endregion
2595
2589
  //#region core/src/projection/select.ts
2596
- const select = (runtime, selector) => require_dependency.readWith(runtime, selector);
2597
- //#endregion
2598
- //#region core/src/projection/collection-view.ts
2599
- const createCollectionView = (input) => {
2600
- if (require_dependency.schemaOf(input.runtime) !== input.source.schema) throw new Error("Collection selector belongs to another schema.");
2601
- const idsListeners = /* @__PURE__ */ new Set();
2602
- const allListeners = /* @__PURE__ */ new Set();
2603
- const itemListeners = /* @__PURE__ */ new Map();
2604
- const itemReadables = /* @__PURE__ */ new Map();
2605
- let disposed = false;
2606
- const sourceNode = require_dependency.nodeAt(input.source.schema, input.source.address);
2607
- const entryNode = sourceNode?.kind === "table" || sourceNode?.kind === "map" ? sourceNode.value : {
2608
- kind: "object",
2609
- shape: {}
2610
- };
2611
- const mapEntry = (document, id) => {
2612
- require_ownership.profile.collectionView.mapped();
2613
- const address = [...input.source.address, String(id)];
2614
- let active = true;
2615
- try {
2616
- return input.map(id, require_dependency.readerFor(entryNode, {
2617
- root: () => document,
2618
- active: () => active
2619
- }, address));
2620
- } finally {
2621
- active = false;
2622
- }
2623
- };
2624
- const build = (previous, changed) => {
2625
- const document = require_dependency.documentOf(input.runtime);
2626
- const collection = require_dependency.read(document, input.source.address);
2627
- const ids = collection && typeof collection === "object" && "ids" in collection && Array.isArray(collection.ids) ? [...collection.ids] : Object.keys(collection ?? {});
2628
- require_ownership.profile.collectionView.idsScanned(ids.length);
2629
- const values = previous && changed ? previous.values : /* @__PURE__ */ new Map();
2630
- if (previous && changed) {
2631
- const nextIds = new Set(ids);
2632
- for (const id of previous.ids) if (!nextIds.has(id)) values.delete(id);
2633
- }
2634
- for (const id of ids) {
2635
- if (previous && changed && previous.values.has(id) && !changed.has(id)) continue;
2636
- const next = mapEntry(document, id);
2637
- if (previous && changed && previous.values.has(id)) {
2638
- const before = previous.values.get(id);
2639
- if (input.isEqual ? input.isEqual(before, next) : Object.is(before, next)) {
2640
- changed.delete(id);
2641
- continue;
2642
- }
2643
- }
2644
- values.set(id, next);
2645
- }
2646
- return {
2647
- ids: Object.freeze(ids),
2648
- values,
2649
- all: void 0,
2650
- allDirty: true
2651
- };
2652
- };
2653
- let state = build();
2654
- const ensureAll = () => {
2655
- if (!state.allDirty && state.all) return state.all;
2656
- require_ownership.profile.collectionView.arrayCopied();
2657
- const all = new Array(state.ids.length);
2658
- for (let index = 0; index < state.ids.length; index += 1) all[index] = state.values.get(state.ids[index]);
2659
- const stable = Object.freeze(all);
2660
- state.all = stable;
2661
- state.allDirty = false;
2662
- return stable;
2663
- };
2664
- const emit = (listeners) => {
2665
- Array.from(listeners).forEach((listener) => listener());
2666
- };
2667
- const unsubscribe = input.runtime.subscribe(input.source, (commit) => {
2668
- if (disposed) return;
2669
- const impact = commit.impact.collection(input.source);
2670
- if (impact.kind === "incremental" && impact.added.size === 0 && impact.removed.size === 0 && !impact.orderChanged) {
2671
- const previous = state;
2672
- const changes = [];
2673
- const document = require_dependency.documentOf(input.runtime);
2674
- impact.updated.forEach((id) => {
2675
- if (!previous.values.has(id)) return;
2676
- const before = previous.values.get(id);
2677
- const after = mapEntry(document, id);
2678
- if (input.isEqual ? input.isEqual(before, after) : Object.is(before, after)) return;
2679
- changes.push([id, after]);
2680
- });
2681
- if (changes.length === 0) return;
2682
- for (const [id, value] of changes) previous.values.set(id, value);
2683
- previous.all = void 0;
2684
- previous.allDirty = true;
2685
- emit(allListeners);
2686
- for (const [id] of changes) {
2687
- const listeners = itemListeners.get(id);
2688
- if (listeners) emit(listeners);
2689
- }
2690
- return;
2691
- }
2692
- const changed = impact.kind === "reset" ? void 0 : new Set([...impact.added, ...impact.updated]);
2693
- const previous = state;
2694
- const next = build(previous, changed);
2695
- const changedIds = /* @__PURE__ */ new Set();
2696
- if (changed) {
2697
- changed.forEach((id) => changedIds.add(id));
2698
- previous.ids.forEach((id) => {
2699
- if (!next.values.has(id)) changedIds.add(id);
2700
- });
2701
- next.ids.forEach((id) => {
2702
- if (!previous.values.has(id)) changedIds.add(id);
2703
- });
2704
- } else {
2705
- previous.ids.forEach((id) => {
2706
- if (!next.values.has(id)) changedIds.add(id);
2707
- else if (!(input.isEqual ? input.isEqual(previous.values.get(id), next.values.get(id)) : Object.is(previous.values.get(id), next.values.get(id)))) changedIds.add(id);
2708
- });
2709
- next.ids.forEach((id) => {
2710
- if (!previous.values.has(id)) changedIds.add(id);
2711
- });
2712
- }
2713
- const idsChanged = impact.kind === "incremental" ? impact.added.size > 0 || impact.removed.size > 0 || impact.orderChanged : previous.ids.length !== next.ids.length || previous.ids.some((id, index) => next.ids[index] !== id);
2714
- state = idsChanged || changedIds.size ? next : previous;
2715
- if (idsChanged) emit(idsListeners);
2716
- if (idsChanged || changedIds.size) emit(allListeners);
2717
- changedIds.forEach((id) => {
2718
- const listeners = itemListeners.get(id);
2719
- if (listeners) emit(listeners);
2720
- });
2721
- });
2722
- return {
2723
- ids: {
2724
- current: () => state.ids,
2725
- revision: () => input.runtime.revision(),
2726
- subscribe: (listener) => {
2727
- if (disposed) return () => void 0;
2728
- idsListeners.add(listener);
2729
- return () => idsListeners.delete(listener);
2730
- }
2731
- },
2732
- all: {
2733
- current: ensureAll,
2734
- revision: () => input.runtime.revision(),
2735
- subscribe: (listener) => {
2736
- if (disposed) return () => void 0;
2737
- allListeners.add(listener);
2738
- return () => allListeners.delete(listener);
2739
- }
2740
- },
2741
- item: (id) => {
2742
- const cached = itemReadables.get(id);
2743
- if (cached) return cached;
2744
- const readable = {
2745
- current: () => state.values.get(id),
2746
- revision: () => input.runtime.revision(),
2747
- subscribe: (listener) => {
2748
- if (disposed) return () => void 0;
2749
- const listeners = itemListeners.get(id) ?? /* @__PURE__ */ new Set();
2750
- itemListeners.set(id, listeners);
2751
- listeners.add(listener);
2752
- return () => {
2753
- listeners.delete(listener);
2754
- if (listeners.size === 0) itemListeners.delete(id);
2755
- };
2756
- }
2757
- };
2758
- itemReadables.set(id, readable);
2759
- return readable;
2760
- },
2761
- revision: () => input.runtime.revision(),
2762
- dispose: () => {
2763
- if (disposed) return;
2764
- disposed = true;
2765
- unsubscribe();
2766
- idsListeners.clear();
2767
- allListeners.clear();
2768
- itemListeners.clear();
2769
- itemReadables.clear();
2770
- state.values.clear();
2771
- state = {
2772
- ids: Object.freeze([]),
2773
- values: /* @__PURE__ */ new Map(),
2774
- all: Object.freeze([]),
2775
- allDirty: false
2776
- };
2777
- }
2778
- };
2779
- };
2780
- //#endregion
2781
- //#region core/src/projection/materialized-view.ts
2782
- const materializedMeta = /* @__PURE__ */ new WeakMap();
2783
- const createMaterializedView = (runtime, spec) => {
2784
- const listeners = /* @__PURE__ */ new Set();
2785
- let disposed = false;
2786
- let fault;
2787
- let revision = runtime.revision();
2788
- let instance;
2789
- let currentValue;
2790
- let pendingEmit = false;
2791
- const dependencies = require_dependency.createDependencyTracker();
2792
- const sources = spec.sources ?? {};
2793
- const sourceEntries = Object.keys(sources).map((key) => [key, sources[key]]);
2794
- sourceEntries.forEach(([, source]) => {
2795
- const meta = materializedMeta.get(source);
2796
- if (!meta || meta.runtime !== runtime) throw new Error("Materialized sources must belong to the same runtime and be created first.");
2797
- });
2798
- const sourceValuesRecord = {};
2799
- let sourceChanged = false;
2800
- for (const [key] of sourceEntries) sourceValuesRecord[key] = {
2801
- value: void 0,
2802
- change: void 0
2803
- };
2804
- const sourceValues = (commitRevision) => {
2805
- sourceChanged = false;
2806
- for (const [key, source] of sourceEntries) {
2807
- const meta = materializedMeta.get(source);
2808
- const entry = sourceValuesRecord[key];
2809
- entry.value = meta?.value ?? source.current();
2810
- entry.change = meta?.change;
2811
- if (commitRevision !== void 0 && meta?.changedRevision === commitRevision) {
2812
- sourceChanged = true;
2813
- require_ownership.profile.materialized.sourceChanged();
2814
- }
2815
- }
2816
- return sourceValuesRecord;
2817
- };
2818
- let activeImpact;
2819
- const assertImpact = () => {
2820
- if (!activeImpact) throw new Error("Materialized impact is no longer active.");
2821
- return activeImpact;
2822
- };
2823
- const trackedImpact = {
2824
- get kind() {
2825
- return assertImpact().kind;
2826
- },
2827
- affects: (target) => {
2828
- dependencies.record(target);
2829
- return assertImpact().affects(target);
2830
- },
2831
- collection: (selector) => {
2832
- dependencies.record(selector);
2833
- return assertImpact().collection(selector);
2834
- },
2835
- get operations() {
2836
- dependencies.record({
2837
- kind: "value",
2838
- at: []
2839
- });
2840
- return assertImpact().operations;
2841
- }
2842
- };
2843
- let view;
2844
- const writeMeta = (change, value, nextRevision, changed) => {
2845
- const meta = materializedMeta.get(view);
2846
- if (meta) {
2847
- meta.value = value;
2848
- meta.change = change;
2849
- meta.revision = nextRevision;
2850
- if (changed) meta.changedRevision = nextRevision;
2851
- } else materializedMeta.set(view, {
2852
- runtime,
2853
- value,
2854
- change,
2855
- revision: nextRevision,
2856
- changedRevision: changed ? nextRevision : -1
2857
- });
2858
- };
2859
- const build = () => {
2860
- require_ownership.profile.materialized.rebuilt();
2861
- dependencies.clear();
2862
- let active = true;
2863
- try {
2864
- instance = spec.build({
2865
- read: require_dependency.documentReader(require_dependency.schemaOf(runtime), () => require_dependency.documentOf(runtime), () => active),
2866
- sources: sourceValues()
2867
- });
2868
- currentValue = instance.value;
2869
- fault = void 0;
2870
- revision = runtime.revision();
2871
- } finally {
2872
- active = false;
2873
- }
2874
- };
2875
- build();
2876
- const emit = () => {
2877
- require_ownership.profile.materialized.notification();
2878
- Array.from(listeners).forEach((listener) => listener());
2879
- };
2880
- const process = (commit) => {
2881
- if (disposed) return;
2882
- try {
2883
- if (commit.kind === "replace" || fault) {
2884
- build();
2885
- writeMeta(void 0, currentValue, commit.revision, true);
2886
- pendingEmit = true;
2887
- return;
2888
- }
2889
- const sourceSnapshot = sourceValues(commit.revision);
2890
- if (!sourceChanged && dependencies.size() > 0 && !dependencies.some(commit.impact.affects)) {
2891
- require_ownership.profile.materialized.skipped();
2892
- revision = commit.revision;
2893
- writeMeta(void 0, currentValue, commit.revision, false);
2894
- return;
2895
- }
2896
- dependencies.clear();
2897
- require_ownership.profile.materialized.updated();
2898
- let active = true;
2899
- let result;
2900
- try {
2901
- activeImpact = commit.impact;
2902
- result = instance.update({
2903
- read: require_dependency.documentReader(require_dependency.schemaOf(runtime), () => require_dependency.documentOf(runtime), () => active),
2904
- revision: commit.revision,
2905
- source: commit.source,
2906
- impact: trackedImpact,
2907
- sources: sourceSnapshot
2908
- });
2909
- } finally {
2910
- activeImpact = void 0;
2911
- active = false;
2912
- }
2913
- if (result.kind === "rebuild") {
2914
- build();
2915
- writeMeta(void 0, currentValue, commit.revision, true);
2916
- pendingEmit = true;
2917
- } else if (result.kind === "changed") {
2918
- currentValue = result.value;
2919
- revision = commit.revision;
2920
- writeMeta(result.change, currentValue, commit.revision, true);
2921
- pendingEmit = true;
2922
- } else {
2923
- revision = commit.revision;
2924
- writeMeta(void 0, currentValue, commit.revision, false);
2925
- }
2926
- } catch (error) {
2927
- try {
2928
- build();
2929
- writeMeta(void 0, currentValue, commit.revision, true);
2930
- pendingEmit = true;
2931
- } catch (rebuildError) {
2932
- fault = rebuildError;
2933
- throw rebuildError;
2934
- }
2935
- throw error;
2936
- }
2937
- };
2938
- view = {
2939
- current: () => {
2940
- if (fault) throw fault;
2941
- return currentValue;
2942
- },
2943
- revision: () => revision,
2944
- subscribe: (listener) => {
2945
- listeners.add(listener);
2946
- return () => listeners.delete(listener);
2947
- },
2948
- rebuild: () => {
2949
- build();
2950
- writeMeta(void 0, currentValue, runtime.revision(), true);
2951
- emit();
2952
- },
2953
- dispose: () => {
2954
- if (disposed) return;
2955
- disposed = true;
2956
- unregister();
2957
- listeners.clear();
2958
- materializedMeta.delete(view);
2959
- }
2960
- };
2961
- materializedMeta.set(view, {
2962
- runtime,
2963
- value: currentValue,
2964
- change: void 0,
2965
- revision,
2966
- changedRevision: -1
2967
- });
2968
- const unregister = registerProcessor(runtime, {
2969
- process,
2970
- flush: () => {
2971
- if (!pendingEmit) return;
2972
- pendingEmit = false;
2973
- emit();
2974
- }
2975
- });
2976
- return view;
2977
- };
2590
+ const select = (runtime, selector) => require_runtime.readWith(runtime, selector);
2978
2591
  //#endregion
2979
- exports.DocumentDisposedError = require_dependency.DocumentDisposedError;
2980
- exports.DocumentReentrancyError = require_dependency.DocumentReentrancyError;
2592
+ exports.DocumentDisposedError = require_runtime.DocumentDisposedError;
2593
+ exports.DocumentReentrancyError = require_runtime.DocumentReentrancyError;
2594
+ exports.ProjectionDisposedError = require_runtime.ProjectionDisposedError;
2595
+ exports.ProjectionError = require_runtime.ProjectionError;
2981
2596
  exports.asReadable = asReadable;
2982
2597
  exports.commandFootprint = commandFootprint;
2983
- exports.contains = require_dependency.contains;
2984
- exports.createCollectionView = createCollectionView;
2598
+ exports.contains = require_runtime.contains;
2985
2599
  exports.createDocument = createDocument;
2986
- exports.createMaterializedView = createMaterializedView;
2987
- exports.debugKey = require_dependency.debugKey;
2600
+ exports.createProjectionRuntime = require_runtime.createProjectionRuntime;
2601
+ exports.debugKey = require_runtime.debugKey;
2988
2602
  exports.decodeCommandFootprint = decodeCommandFootprint;
2989
- exports.dict = require_dependency.dict;
2990
- exports.field = require_dependency.field;
2603
+ exports.dict = require_runtime.dict;
2604
+ exports.field = require_runtime.field;
2991
2605
  exports.footprintsOverlap = footprintsOverlap;
2992
- exports.list = require_dependency.list;
2993
- exports.map = require_dependency.map;
2994
- exports.object = require_dependency.object;
2995
- exports.optional = require_dependency.optional;
2996
- exports.overlaps = require_dependency.overlaps;
2997
- exports.readAddress = require_dependency.read;
2998
- exports.record = require_dependency.record;
2999
- exports.resolveAddress = require_dependency.resolveAddress;
3000
- exports.schema = require_dependency.schema;
2606
+ exports.list = require_runtime.list;
2607
+ exports.map = require_runtime.map;
2608
+ exports.object = require_runtime.object;
2609
+ exports.optional = require_runtime.optional;
2610
+ exports.overlaps = require_runtime.overlaps;
2611
+ exports.readAddress = require_runtime.read;
2612
+ exports.record = require_runtime.record;
2613
+ exports.resolveAddress = require_runtime.resolveAddress;
2614
+ exports.schema = require_runtime.schema;
3001
2615
  exports.select = select;
3002
- exports.single = require_dependency.single;
3003
- exports.table = require_dependency.table;
2616
+ exports.single = require_runtime.single;
2617
+ exports.table = require_runtime.table;
3004
2618
  Object.defineProperty(exports, "target", {
3005
2619
  enumerable: true,
3006
2620
  get: function() {
3007
- return require_dependency.impact_target_exports;
2621
+ return require_runtime.impact_target_exports;
3008
2622
  }
3009
2623
  });
3010
- exports.tree = require_dependency.tree;
3011
- exports.variant = require_dependency.variant;
2624
+ exports.tree = require_runtime.tree;
2625
+ exports.variant = require_runtime.variant;
3012
2626
 
3013
2627
  //# sourceMappingURL=index.cjs.map