doxum 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{contract-BsY1XLiG.d.ts → contract-4E3moCbY.d.ts} +53 -40
- package/dist/{contract-D8kjdfqT.d.cts → contract-uYJTmT8o.d.cts} +53 -40
- package/dist/dependency-C5_R1tvQ.js.map +1 -1
- package/dist/dependency-Dfzs3khk.cjs.map +1 -1
- package/dist/index.cjs +171 -22
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +171 -22
- package/dist/index.js.map +1 -1
- package/dist/integration.d.cts +1 -1
- package/dist/integration.d.ts +1 -1
- package/dist/local-sync.d.cts +1 -1
- package/dist/local-sync.d.ts +1 -1
- package/package.json +1 -1
- package/skills/doxum-runtime/references/guide.en.md +5 -0
- package/skills/doxum-runtime/references/guide.zh-CN.md +4 -0
- package/skills/doxum-runtime/references/patterns.en.md +7 -0
- package/skills/doxum-runtime/references/patterns.zh-CN.md +6 -0
package/dist/index.cjs
CHANGED
|
@@ -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 {
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
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 (
|
|
545
|
-
const
|
|
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 =
|
|
558
|
-
|
|
559
|
-
|
|
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
|
-
|
|
567
|
-
|
|
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 (
|
|
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
1079
|
if (result.status === "changed") require_dependency.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);
|
|
@@ -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
|
|
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),
|
|
@@ -1667,6 +1797,22 @@ const createChangeJournal = (root) => {
|
|
|
1667
1797
|
};
|
|
1668
1798
|
const recordTree = (resolved, operation, inverse) => {
|
|
1669
1799
|
const current = require_dependency.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
|
-
|
|
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":
|
|
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.");
|
|
@@ -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",
|