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.
- package/README.md +77 -2
- package/dist/{contract-DwNiKioc.d.ts → contract-BsY1XLiG.d.ts} +64 -5
- package/dist/{contract-Otb5W6cQ.d.cts → contract-D8kjdfqT.d.cts} +64 -5
- package/dist/{dependency-BdEMyquf.js → dependency-C5_R1tvQ.js} +24 -263
- package/dist/dependency-C5_R1tvQ.js.map +1 -0
- package/dist/{dependency-DLcCvNKq.cjs → dependency-Dfzs3khk.cjs} +48 -341
- package/dist/dependency-Dfzs3khk.cjs.map +1 -0
- package/dist/driver-CNxqMVFH.cjs +69 -0
- package/dist/driver-CNxqMVFH.cjs.map +1 -0
- package/dist/driver-CbzfW5MR.js +46 -0
- package/dist/driver-CbzfW5MR.js.map +1 -0
- package/dist/index.cjs +428 -133
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -5
- package/dist/index.d.ts +8 -5
- package/dist/index.js +352 -61
- package/dist/index.js.map +1 -1
- package/dist/integration.cjs +1 -1
- package/dist/integration.cjs.map +1 -1
- package/dist/integration.d.cts +2 -2
- package/dist/integration.d.ts +2 -2
- package/dist/integration.js +1 -1
- package/dist/integration.js.map +1 -1
- package/dist/local-sync.cjs +485 -0
- package/dist/local-sync.cjs.map +1 -0
- package/dist/local-sync.d.cts +74 -0
- package/dist/local-sync.d.ts +74 -0
- package/dist/local-sync.js +476 -0
- package/dist/local-sync.js.map +1 -0
- package/dist/ownership-B-VAPcce.js +242 -0
- package/dist/ownership-B-VAPcce.js.map +1 -0
- package/dist/ownership-CU-9DKJQ.cjs +301 -0
- package/dist/ownership-CU-9DKJQ.cjs.map +1 -0
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +2 -2
- package/dist/react.d.ts +2 -2
- package/dist/react.js.map +1 -1
- package/package.json +14 -8
- package/skills/doxum-runtime/references/guide.en.md +60 -5
- package/skills/doxum-runtime/references/guide.zh-CN.md +48 -2
- package/skills/doxum-runtime/references/invariants.en.md +23 -8
- package/skills/doxum-runtime/references/invariants.zh-CN.md +2 -2
- package/dist/dependency-BdEMyquf.js.map +0 -1
- package/dist/dependency-DLcCvNKq.cjs.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import { A as
|
|
1
|
+
import { A as optional, C as resolveLocated, D as list, E as field, F as tree, I as variant, M as schema, N as single, O as map, P as table, S as resolveAddress, T as dict, _ as debugKey, a as readWith, b as overlaps, c as DocumentReentrancyError, d as address$2, f as belongs, g as contains, h as impact_target_exports, i as documentOf, j as record, k as object, l as documentReader, m as id, n as accessOf, o as schemaOf, p as bucket, r as bindRuntimeAccess, s as DocumentDisposedError, t as createDependencyTracker, u as readerFor, v as locate, w as set$1, x as read, y as nodeAt$1 } from "./dependency-C5_R1tvQ.js";
|
|
2
|
+
import { a as isStablePayload, c as snapshotPayload, i as isRecord, l as transferPayload, n as deepEqual, o as ownPayload, r as isPlainObject, s as sameStructuralValue, t as cloneValue, u as profile } from "./ownership-B-VAPcce.js";
|
|
3
|
+
import { n as bindRuntimeDriver, r as disposeRuntimeDriver, t as assertRuntimeWritable } from "./driver-CbzfW5MR.js";
|
|
2
4
|
//#region core/src/impact.ts
|
|
3
5
|
const emptyCollectionImpact = Object.freeze({
|
|
4
6
|
kind: "incremental",
|
|
@@ -36,9 +38,9 @@ const createImpact = (input) => {
|
|
|
36
38
|
profile.impact.affects();
|
|
37
39
|
if (kind === "reset") return true;
|
|
38
40
|
if (!belongs(value, input.schema)) return false;
|
|
39
|
-
if (pathsAffect(address$
|
|
41
|
+
if (pathsAffect(address$2(value))) return true;
|
|
40
42
|
if (value.kind === "collection") {
|
|
41
|
-
const collection = findCollection(address$
|
|
43
|
+
const collection = findCollection(address$2(value));
|
|
42
44
|
if (!collection) return false;
|
|
43
45
|
const id$1 = id(value);
|
|
44
46
|
if (id$1 !== void 0) return collection.added.has(id$1) || collection.removed.has(id$1) || collection.updated.has(id$1);
|
|
@@ -1156,7 +1158,7 @@ const execute = (root, operation, target, copyPayload) => {
|
|
|
1156
1158
|
//#region core/src/mutation/journal.ts
|
|
1157
1159
|
const indexNode = () => ({ children: /* @__PURE__ */ new Map() });
|
|
1158
1160
|
const hasOwn = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1159
|
-
const sameAddress = (left, right) => {
|
|
1161
|
+
const sameAddress$1 = (left, right) => {
|
|
1160
1162
|
if (left.length !== right.length) return false;
|
|
1161
1163
|
for (let index = 0; index < left.length; index += 1) if (left[index] !== right[index]) return false;
|
|
1162
1164
|
return true;
|
|
@@ -1271,7 +1273,7 @@ const restoreRootSubject = (value, subject) => {
|
|
|
1271
1273
|
return restoreChange(value, subject.state);
|
|
1272
1274
|
};
|
|
1273
1275
|
const restoreSubject = (value, prefix, subject) => {
|
|
1274
|
-
if (sameAddress(prefix, subject.address)) return restoreRootSubject(value, subject);
|
|
1276
|
+
if (sameAddress$1(prefix, subject.address)) return restoreRootSubject(value, subject);
|
|
1275
1277
|
const address = subject.address.slice(prefix.length);
|
|
1276
1278
|
if (subject.kind === "value") {
|
|
1277
1279
|
writePresence(value, address, subject.before);
|
|
@@ -1498,7 +1500,7 @@ const createChangeJournal = (root) => {
|
|
|
1498
1500
|
const candidates = buckets.get(addressHash) ?? [];
|
|
1499
1501
|
for (const subject of candidates) {
|
|
1500
1502
|
profile.journal.comparison();
|
|
1501
|
-
if (subject.kind === kind && sameAddress(subject.address, address)) return subject;
|
|
1503
|
+
if (subject.kind === kind && sameAddress$1(subject.address, address)) return subject;
|
|
1502
1504
|
}
|
|
1503
1505
|
}
|
|
1504
1506
|
const ownerOf = (address) => {
|
|
@@ -1720,7 +1722,7 @@ const createChangeJournal = (root) => {
|
|
|
1720
1722
|
const changeAt = (address, addressHash) => {
|
|
1721
1723
|
const candidates = collectionBuckets.get(addressHash) ?? [];
|
|
1722
1724
|
collectionBuckets.set(addressHash, candidates);
|
|
1723
|
-
let change = candidates.find((entry) => sameAddress(entry.address, address));
|
|
1725
|
+
let change = candidates.find((entry) => sameAddress$1(entry.address, address));
|
|
1724
1726
|
if (!change) {
|
|
1725
1727
|
change = {
|
|
1726
1728
|
address,
|
|
@@ -1764,7 +1766,7 @@ const createChangeJournal = (root) => {
|
|
|
1764
1766
|
const has = (value, key) => Object.prototype.hasOwnProperty.call(value, key);
|
|
1765
1767
|
const stringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
1766
1768
|
const positions = (value) => Array.isArray(value) && value.every((entry) => Number.isInteger(entry) && entry >= 0);
|
|
1767
|
-
const address = (value) => stringArray(value);
|
|
1769
|
+
const address$1 = (value) => stringArray(value);
|
|
1768
1770
|
const anchor = (value) => {
|
|
1769
1771
|
if (!isRecord(value)) return false;
|
|
1770
1772
|
if (Number(has(value, "at")) + Number(has(value, "before")) + Number(has(value, "after")) !== 1) return false;
|
|
@@ -1785,7 +1787,7 @@ const rejected = (code, message) => ({
|
|
|
1785
1787
|
});
|
|
1786
1788
|
const decode = (input) => {
|
|
1787
1789
|
if (!isRecord(input)) return rejected("invalid-operation", "Operation must be an object.");
|
|
1788
|
-
if (!address(input.at)) return rejected("invalid-address", "Operation address is malformed.");
|
|
1790
|
+
if (!address$1(input.at)) return rejected("invalid-address", "Operation address is malformed.");
|
|
1789
1791
|
if (typeof input.type !== "string") return rejected("invalid-operation", "Operation type must be a string.");
|
|
1790
1792
|
switch (input.type) {
|
|
1791
1793
|
case "field.set":
|
|
@@ -1821,6 +1823,7 @@ const ownAddress = (value) => {
|
|
|
1821
1823
|
profile.address.arrayCopied();
|
|
1822
1824
|
return Object.freeze(value.slice());
|
|
1823
1825
|
};
|
|
1826
|
+
const structuralPayload = (value) => Array.isArray(value) || isPlainObject(value);
|
|
1824
1827
|
const normalize = (operation) => {
|
|
1825
1828
|
profile.mutation.normalized();
|
|
1826
1829
|
const copy = {
|
|
@@ -1835,23 +1838,34 @@ const normalize = (operation) => {
|
|
|
1835
1838
|
return Object.freeze(copy);
|
|
1836
1839
|
};
|
|
1837
1840
|
const publish = (operation) => {
|
|
1838
|
-
if (!
|
|
1839
|
-
if (
|
|
1841
|
+
if (operation.type === "entity.create" && operation.entries.every((entry) => !structuralPayload(entry.value) || isStablePayload(entry.value))) return operation;
|
|
1842
|
+
if ("value" in operation && (!structuralPayload(operation.value) || isStablePayload(operation.value))) return operation;
|
|
1840
1843
|
const copy = { ...operation };
|
|
1841
1844
|
if (copy.entries) copy.entries = Object.freeze(copy.entries.map((entry) => Object.freeze({
|
|
1842
1845
|
id: entry.id,
|
|
1843
|
-
value: isStablePayload(entry.value) ? entry.value : snapshotPayload(entry.value, "commit")
|
|
1846
|
+
value: structuralPayload(entry.value) ? isStablePayload(entry.value) ? entry.value : snapshotPayload(entry.value, "commit") : entry.value
|
|
1844
1847
|
})));
|
|
1845
|
-
else if ("value" in copy) copy.value = isStablePayload(copy.value) ? copy.value : snapshotPayload(copy.value, "commit");
|
|
1848
|
+
else if ("value" in copy && structuralPayload(copy.value)) copy.value = isStablePayload(copy.value) ? copy.value : snapshotPayload(copy.value, "commit");
|
|
1846
1849
|
return Object.freeze(copy);
|
|
1847
1850
|
};
|
|
1851
|
+
const requiresPayloadCopy = (operation) => {
|
|
1852
|
+
if (operation.type === "entity.create") return operation.entries.some((entry) => isStablePayload(entry.value));
|
|
1853
|
+
return "value" in operation && isStablePayload(operation.value);
|
|
1854
|
+
};
|
|
1848
1855
|
const inverse = (operation) => {
|
|
1849
1856
|
const copy = { ...operation };
|
|
1850
1857
|
if (copy.anchor) copy.anchor = Object.freeze({ ...copy.anchor });
|
|
1851
1858
|
if (copy.keys) copy.keys = Object.freeze([...copy.keys]);
|
|
1852
|
-
if (copy.entries) copy.entries = Object.freeze(copy.entries.map((entry) => Object.freeze({
|
|
1859
|
+
if (copy.entries) copy.entries = Object.freeze(copy.entries.map((entry) => Object.freeze({
|
|
1860
|
+
id: entry.id,
|
|
1861
|
+
value: structuralPayload(entry.value) ? isStablePayload(entry.value) ? entry.value : snapshotPayload(entry.value, "inverse") : entry.value
|
|
1862
|
+
})));
|
|
1853
1863
|
if (copy.ids) copy.ids = Object.freeze([...copy.ids]);
|
|
1854
1864
|
if (copy.positions) copy.positions = Object.freeze([...copy.positions]);
|
|
1865
|
+
if ("value" in copy && structuralPayload(copy.value)) {
|
|
1866
|
+
const payload = copy;
|
|
1867
|
+
payload.value = isStablePayload(payload.value) ? payload.value : snapshotPayload(payload.value, "inverse");
|
|
1868
|
+
}
|
|
1855
1869
|
return Object.freeze(copy);
|
|
1856
1870
|
};
|
|
1857
1871
|
//#endregion
|
|
@@ -1859,18 +1873,18 @@ const inverse = (operation) => {
|
|
|
1859
1873
|
const createMutationSession = (root, schema, options = {}) => {
|
|
1860
1874
|
const steps = [];
|
|
1861
1875
|
const journal = createChangeJournal(root);
|
|
1862
|
-
let
|
|
1876
|
+
let state = "active";
|
|
1863
1877
|
const rollback = () => {
|
|
1864
|
-
if (
|
|
1878
|
+
if (state === "rolled-back") return;
|
|
1865
1879
|
for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) for (const inverse of steps[stepIndex].inverse) {
|
|
1866
1880
|
const result = execute(root, inverse, resolveLocated(schema, root, inverse.at), true);
|
|
1867
1881
|
if (result.status === "rejected") throw new Error(`Document rollback failed: ${result.issue.message}`);
|
|
1868
1882
|
}
|
|
1869
|
-
|
|
1883
|
+
state = "rolled-back";
|
|
1870
1884
|
};
|
|
1871
1885
|
return {
|
|
1872
1886
|
apply: (input) => {
|
|
1873
|
-
if (
|
|
1887
|
+
if (state !== "active") throw new Error("Mutation session is closed.");
|
|
1874
1888
|
profile.batch.operation();
|
|
1875
1889
|
const decoded = decode(input);
|
|
1876
1890
|
if (decoded.status === "rejected") {
|
|
@@ -1882,7 +1896,7 @@ const createMutationSession = (root, schema, options = {}) => {
|
|
|
1882
1896
|
const resolved = resolveLocated(schema, root, normalized.at);
|
|
1883
1897
|
if (resolved?.collection) profile.batch.collectionResolved();
|
|
1884
1898
|
profile.mutation.executed();
|
|
1885
|
-
const result = execute(root, normalized, resolved, options.copyPayload === true);
|
|
1899
|
+
const result = execute(root, normalized, resolved, options.copyPayload === true || requiresPayloadCopy(normalized));
|
|
1886
1900
|
if (result.status === "rejected") {
|
|
1887
1901
|
profile.batch.rejected();
|
|
1888
1902
|
return result.issue;
|
|
@@ -1909,9 +1923,9 @@ const createMutationSession = (root, schema, options = {}) => {
|
|
|
1909
1923
|
}
|
|
1910
1924
|
},
|
|
1911
1925
|
finish: () => {
|
|
1912
|
-
if (
|
|
1913
|
-
closed = true;
|
|
1926
|
+
if (state !== "active") throw new Error("Mutation session is closed.");
|
|
1914
1927
|
const changes = journal.finish();
|
|
1928
|
+
state = "finished";
|
|
1915
1929
|
if (changes.status === "unchanged") return { status: "unchanged" };
|
|
1916
1930
|
let inverseCount = 0;
|
|
1917
1931
|
for (const step of steps) inverseCount += step.inverse.length;
|
|
@@ -1948,6 +1962,191 @@ const mutateOperations = (root, schema, operations, options = {}) => {
|
|
|
1948
1962
|
return session.finish();
|
|
1949
1963
|
};
|
|
1950
1964
|
//#endregion
|
|
1965
|
+
//#region core/src/mutation/footprint.ts
|
|
1966
|
+
const sameAddress = (left, right) => left.length === right.length && left.every((segment, index) => segment === right[index]);
|
|
1967
|
+
const containsAddress = (parent, child) => parent.length <= child.length && parent.every((segment, index) => segment === child[index]);
|
|
1968
|
+
const address = (value) => Array.isArray(value) && value.every((segment) => typeof segment === "string") ? Object.freeze(value.slice()) : void 0;
|
|
1969
|
+
const targetKey = (target) => {
|
|
1970
|
+
const at = target.at.join("\0");
|
|
1971
|
+
if (target.kind === "dictionary-key" || target.kind === "list-item") return `${target.kind}:${at}:${target.key}`;
|
|
1972
|
+
if (target.kind === "entity" || target.kind === "tree-node") return `${target.kind}:${at}:${target.id}`;
|
|
1973
|
+
return `${target.kind}:${at}`;
|
|
1974
|
+
};
|
|
1975
|
+
const freezeTarget = (target) => Object.freeze({
|
|
1976
|
+
...target,
|
|
1977
|
+
at: Object.freeze(target.at.slice())
|
|
1978
|
+
});
|
|
1979
|
+
const add = (targets, target) => {
|
|
1980
|
+
const stable = freezeTarget(target);
|
|
1981
|
+
targets.set(targetKey(stable), stable);
|
|
1982
|
+
};
|
|
1983
|
+
const commandFootprint = (operations) => {
|
|
1984
|
+
const targets = /* @__PURE__ */ new Map();
|
|
1985
|
+
for (const operation of operations) switch (operation.type) {
|
|
1986
|
+
case "field.set":
|
|
1987
|
+
case "field.clear":
|
|
1988
|
+
case "variant.replace":
|
|
1989
|
+
add(targets, {
|
|
1990
|
+
kind: "value",
|
|
1991
|
+
at: operation.at
|
|
1992
|
+
});
|
|
1993
|
+
break;
|
|
1994
|
+
case "dict.replace":
|
|
1995
|
+
add(targets, {
|
|
1996
|
+
kind: "dictionary",
|
|
1997
|
+
at: operation.at
|
|
1998
|
+
});
|
|
1999
|
+
break;
|
|
2000
|
+
case "dict.set":
|
|
2001
|
+
case "dict.delete":
|
|
2002
|
+
add(targets, {
|
|
2003
|
+
kind: "dictionary-key",
|
|
2004
|
+
at: operation.at,
|
|
2005
|
+
key: operation.key
|
|
2006
|
+
});
|
|
2007
|
+
break;
|
|
2008
|
+
case "entity.create":
|
|
2009
|
+
add(targets, {
|
|
2010
|
+
kind: "collection",
|
|
2011
|
+
at: operation.at
|
|
2012
|
+
});
|
|
2013
|
+
for (const entry of operation.entries) add(targets, {
|
|
2014
|
+
kind: "entity",
|
|
2015
|
+
at: operation.at,
|
|
2016
|
+
id: entry.id
|
|
2017
|
+
});
|
|
2018
|
+
break;
|
|
2019
|
+
case "entity.remove":
|
|
2020
|
+
add(targets, {
|
|
2021
|
+
kind: "collection",
|
|
2022
|
+
at: operation.at
|
|
2023
|
+
});
|
|
2024
|
+
for (const id of operation.ids) add(targets, {
|
|
2025
|
+
kind: "entity",
|
|
2026
|
+
at: operation.at,
|
|
2027
|
+
id
|
|
2028
|
+
});
|
|
2029
|
+
break;
|
|
2030
|
+
case "entity.move":
|
|
2031
|
+
add(targets, {
|
|
2032
|
+
kind: "collection",
|
|
2033
|
+
at: operation.at
|
|
2034
|
+
});
|
|
2035
|
+
add(targets, {
|
|
2036
|
+
kind: "entity",
|
|
2037
|
+
at: operation.at,
|
|
2038
|
+
id: operation.id
|
|
2039
|
+
});
|
|
2040
|
+
break;
|
|
2041
|
+
case "list.insert":
|
|
2042
|
+
case "list.move":
|
|
2043
|
+
case "list.remove":
|
|
2044
|
+
add(targets, {
|
|
2045
|
+
kind: "list",
|
|
2046
|
+
at: operation.at
|
|
2047
|
+
});
|
|
2048
|
+
add(targets, {
|
|
2049
|
+
kind: "list-item",
|
|
2050
|
+
at: operation.at,
|
|
2051
|
+
key: operation.key
|
|
2052
|
+
});
|
|
2053
|
+
break;
|
|
2054
|
+
case "list.replace":
|
|
2055
|
+
add(targets, {
|
|
2056
|
+
kind: "list",
|
|
2057
|
+
at: operation.at
|
|
2058
|
+
});
|
|
2059
|
+
for (const key of operation.keys) add(targets, {
|
|
2060
|
+
kind: "list-item",
|
|
2061
|
+
at: operation.at,
|
|
2062
|
+
key
|
|
2063
|
+
});
|
|
2064
|
+
break;
|
|
2065
|
+
case "tree.insert":
|
|
2066
|
+
case "tree.move":
|
|
2067
|
+
case "tree.remove":
|
|
2068
|
+
case "tree.set":
|
|
2069
|
+
add(targets, {
|
|
2070
|
+
kind: "tree",
|
|
2071
|
+
at: operation.at
|
|
2072
|
+
});
|
|
2073
|
+
add(targets, {
|
|
2074
|
+
kind: "tree-node",
|
|
2075
|
+
at: operation.at,
|
|
2076
|
+
id: operation.treeNodeId
|
|
2077
|
+
});
|
|
2078
|
+
break;
|
|
2079
|
+
case "tree.replace":
|
|
2080
|
+
add(targets, {
|
|
2081
|
+
kind: "tree",
|
|
2082
|
+
at: operation.at
|
|
2083
|
+
});
|
|
2084
|
+
break;
|
|
2085
|
+
}
|
|
2086
|
+
return Object.freeze(Array.from(targets.values()));
|
|
2087
|
+
};
|
|
2088
|
+
const overlapsValueAndEntity = (value, entity) => containsAddress([...entity.at, entity.id], value.at) || containsAddress(value.at, [...entity.at, entity.id]);
|
|
2089
|
+
const targetsOverlap = (left, right) => {
|
|
2090
|
+
if (left.kind === "value" && right.kind === "value") return containsAddress(left.at, right.at) || containsAddress(right.at, left.at);
|
|
2091
|
+
if (left.kind === "value" && right.kind === "entity") return overlapsValueAndEntity(left, right);
|
|
2092
|
+
if (left.kind === "entity" && right.kind === "value") return overlapsValueAndEntity(right, left);
|
|
2093
|
+
if (left.kind === "dictionary" && right.kind === "dictionary") return sameAddress(left.at, right.at);
|
|
2094
|
+
if (left.kind === "dictionary" && right.kind === "dictionary-key") return sameAddress(left.at, right.at);
|
|
2095
|
+
if (left.kind === "dictionary-key" && right.kind === "dictionary") return sameAddress(left.at, right.at);
|
|
2096
|
+
if (left.kind === "dictionary-key" && right.kind === "dictionary-key") return sameAddress(left.at, right.at) && left.key === right.key;
|
|
2097
|
+
if (left.kind === "collection" && right.kind === "collection") return sameAddress(left.at, right.at);
|
|
2098
|
+
if (left.kind === "collection" && right.kind === "entity") return sameAddress(left.at, right.at);
|
|
2099
|
+
if (left.kind === "entity" && right.kind === "collection") return sameAddress(left.at, right.at);
|
|
2100
|
+
if (left.kind === "entity" && right.kind === "entity") return sameAddress(left.at, right.at) && left.id === right.id;
|
|
2101
|
+
if (left.kind === "list" && (right.kind === "list" || right.kind === "list-item")) return sameAddress(left.at, right.at);
|
|
2102
|
+
if (right.kind === "list" && left.kind === "list-item") return sameAddress(left.at, right.at);
|
|
2103
|
+
if (left.kind === "list-item" && right.kind === "list-item") return sameAddress(left.at, right.at) && left.key === right.key;
|
|
2104
|
+
if (left.kind === "tree" && (right.kind === "tree" || right.kind === "tree-node")) return sameAddress(left.at, right.at);
|
|
2105
|
+
if (right.kind === "tree" && left.kind === "tree-node") return sameAddress(left.at, right.at);
|
|
2106
|
+
if (left.kind === "tree-node" && right.kind === "tree-node") return sameAddress(left.at, right.at) && left.id === right.id;
|
|
2107
|
+
return false;
|
|
2108
|
+
};
|
|
2109
|
+
const footprintsOverlap = (left, right) => left.some((leftTarget) => right.some((rightTarget) => targetsOverlap(leftTarget, rightTarget)));
|
|
2110
|
+
const decodedTarget = (value) => {
|
|
2111
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
|
|
2112
|
+
const record = value;
|
|
2113
|
+
const at = address(record.at);
|
|
2114
|
+
if (!at || typeof record.kind !== "string") return void 0;
|
|
2115
|
+
switch (record.kind) {
|
|
2116
|
+
case "value":
|
|
2117
|
+
case "dictionary":
|
|
2118
|
+
case "collection":
|
|
2119
|
+
case "list":
|
|
2120
|
+
case "tree": return freezeTarget({
|
|
2121
|
+
kind: record.kind,
|
|
2122
|
+
at
|
|
2123
|
+
});
|
|
2124
|
+
case "dictionary-key":
|
|
2125
|
+
case "list-item": return typeof record.key === "string" ? freezeTarget({
|
|
2126
|
+
kind: record.kind,
|
|
2127
|
+
at,
|
|
2128
|
+
key: record.key
|
|
2129
|
+
}) : void 0;
|
|
2130
|
+
case "entity":
|
|
2131
|
+
case "tree-node": return typeof record.id === "string" ? freezeTarget({
|
|
2132
|
+
kind: record.kind,
|
|
2133
|
+
at,
|
|
2134
|
+
id: record.id
|
|
2135
|
+
}) : void 0;
|
|
2136
|
+
default: return;
|
|
2137
|
+
}
|
|
2138
|
+
};
|
|
2139
|
+
const decodeCommandFootprint = (value) => {
|
|
2140
|
+
if (!Array.isArray(value)) return void 0;
|
|
2141
|
+
const targets = /* @__PURE__ */ new Map();
|
|
2142
|
+
for (const entry of value) {
|
|
2143
|
+
const target = decodedTarget(entry);
|
|
2144
|
+
if (!target) return void 0;
|
|
2145
|
+
targets.set(targetKey(target), target);
|
|
2146
|
+
}
|
|
2147
|
+
return Object.freeze(Array.from(targets.values()));
|
|
2148
|
+
};
|
|
2149
|
+
//#endregion
|
|
1951
2150
|
//#region core/src/runtime/notification.ts
|
|
1952
2151
|
const ROOT_BUCKET = Symbol("document-root");
|
|
1953
2152
|
const notifications = /* @__PURE__ */ new WeakMap();
|
|
@@ -1970,6 +2169,9 @@ const notificationOf = (runtime) => {
|
|
|
1970
2169
|
if (!value) throw new Error("Unknown Doxum runtime.");
|
|
1971
2170
|
return value;
|
|
1972
2171
|
};
|
|
2172
|
+
const shareNotification = (source, target) => {
|
|
2173
|
+
notifications.set(target, notificationOf(source));
|
|
2174
|
+
};
|
|
1973
2175
|
const registerProcessor = (runtime, processor) => {
|
|
1974
2176
|
const notification = notificationOf(runtime);
|
|
1975
2177
|
const entry = {
|
|
@@ -2131,10 +2333,47 @@ const createDocument = (input) => {
|
|
|
2131
2333
|
let busy = false;
|
|
2132
2334
|
let runtime;
|
|
2133
2335
|
let notification;
|
|
2134
|
-
const assertWritable = () => {
|
|
2336
|
+
const assertWritable = (intent) => {
|
|
2135
2337
|
if (state.disposed) throw new DocumentDisposedError();
|
|
2136
2338
|
if (busy) throw new DocumentReentrancyError();
|
|
2339
|
+
assertRuntimeWritable(runtime, intent);
|
|
2340
|
+
};
|
|
2341
|
+
const runTransaction = (session, run) => {
|
|
2342
|
+
let reports;
|
|
2343
|
+
let active = true;
|
|
2344
|
+
const transaction = {
|
|
2345
|
+
read: documentReader(input.schema, () => state.document, () => active),
|
|
2346
|
+
write: documentWriter(input.schema, (operation) => {
|
|
2347
|
+
if (!active) throw new Error("Document writer is no longer active.");
|
|
2348
|
+
const rejected = session.apply(operation);
|
|
2349
|
+
if (rejected) throw new RejectedUpdate([rejected]);
|
|
2350
|
+
}),
|
|
2351
|
+
reject: (diagnostic) => {
|
|
2352
|
+
throw new RejectedUpdate((Array.isArray(diagnostic) ? diagnostic : [diagnostic]).map((entry) => publishDiagnostic(entry)));
|
|
2353
|
+
},
|
|
2354
|
+
report: (diagnostic) => {
|
|
2355
|
+
if (!active) throw new Error("Document transaction is no longer active.");
|
|
2356
|
+
(reports ??= []).push(publishDiagnostic(diagnostic));
|
|
2357
|
+
}
|
|
2358
|
+
};
|
|
2359
|
+
try {
|
|
2360
|
+
const value = run(transaction);
|
|
2361
|
+
if (value !== null && typeof value === "object" && typeof value.then === "function") throw new TypeError("Document update callback must be synchronous.");
|
|
2362
|
+
return {
|
|
2363
|
+
value,
|
|
2364
|
+
reports: Object.freeze(reports ? reports.slice() : EMPTY)
|
|
2365
|
+
};
|
|
2366
|
+
} finally {
|
|
2367
|
+
active = false;
|
|
2368
|
+
}
|
|
2137
2369
|
};
|
|
2370
|
+
const impactFor = (batch, kind) => createImpact({
|
|
2371
|
+
schema: input.schema,
|
|
2372
|
+
operations: batch.operations,
|
|
2373
|
+
paths: batch.paths,
|
|
2374
|
+
collections: batch.collections,
|
|
2375
|
+
reset: kind === "replace"
|
|
2376
|
+
});
|
|
2138
2377
|
const history = createHistory({
|
|
2139
2378
|
capacity: input.history === false ? 0 : Math.max(0, input.history?.capacity ?? 100),
|
|
2140
2379
|
revision: () => revision,
|
|
@@ -2148,13 +2387,7 @@ const createDocument = (input) => {
|
|
|
2148
2387
|
source,
|
|
2149
2388
|
operations: batch.operations,
|
|
2150
2389
|
inverse: batch.inverse,
|
|
2151
|
-
impact:
|
|
2152
|
-
schema: input.schema,
|
|
2153
|
-
operations: batch.operations,
|
|
2154
|
-
paths: batch.paths,
|
|
2155
|
-
collections: batch.collections,
|
|
2156
|
-
reset: kind === "replace"
|
|
2157
|
-
})
|
|
2390
|
+
impact: impactFor(batch, kind)
|
|
2158
2391
|
});
|
|
2159
2392
|
if (kind === "replace" || source === "remote") history.invalidate();
|
|
2160
2393
|
else if (recordHistory && (source === "local" || source === "system")) history.record(batch.operations, batch.inverse);
|
|
@@ -2172,7 +2405,10 @@ const createDocument = (input) => {
|
|
|
2172
2405
|
};
|
|
2173
2406
|
};
|
|
2174
2407
|
function applyBatch(operations, source, recordHistory) {
|
|
2175
|
-
assertWritable(
|
|
2408
|
+
assertWritable({
|
|
2409
|
+
kind: "apply",
|
|
2410
|
+
source
|
|
2411
|
+
});
|
|
2176
2412
|
const batch = mutateOperations(state.document, input.schema, operations, { copyPayload: source === "history" });
|
|
2177
2413
|
if (batch.status === "rejected") return {
|
|
2178
2414
|
status: "rejected",
|
|
@@ -2186,6 +2422,7 @@ const createDocument = (input) => {
|
|
|
2186
2422
|
return publish(batch, source, "operations", recordHistory);
|
|
2187
2423
|
}
|
|
2188
2424
|
runtime = {
|
|
2425
|
+
schema: input.schema,
|
|
2189
2426
|
address: {
|
|
2190
2427
|
resolve: (address) => resolveAddress(input.schema, address, state.document),
|
|
2191
2428
|
read: (address) => read(state.document, address),
|
|
@@ -2195,55 +2432,40 @@ const createDocument = (input) => {
|
|
|
2195
2432
|
},
|
|
2196
2433
|
revision: () => revision,
|
|
2197
2434
|
update: (run, options) => {
|
|
2198
|
-
|
|
2435
|
+
const source = options?.source ?? "local";
|
|
2436
|
+
assertWritable({
|
|
2437
|
+
kind: "update",
|
|
2438
|
+
source
|
|
2439
|
+
});
|
|
2199
2440
|
busy = true;
|
|
2200
2441
|
const session = createMutationSession(state.document, input.schema);
|
|
2201
|
-
let
|
|
2202
|
-
let active = true;
|
|
2203
|
-
const transaction = {
|
|
2204
|
-
read: documentReader(input.schema, () => state.document, () => active),
|
|
2205
|
-
write: documentWriter(input.schema, (operation) => {
|
|
2206
|
-
if (!active) throw new Error("Document writer is no longer active.");
|
|
2207
|
-
const rejected = session.apply(operation);
|
|
2208
|
-
if (rejected) throw new RejectedUpdate([rejected]);
|
|
2209
|
-
}),
|
|
2210
|
-
reject: (diagnostic) => {
|
|
2211
|
-
throw new RejectedUpdate((Array.isArray(diagnostic) ? diagnostic : [diagnostic]).map((entry) => publishDiagnostic(entry)));
|
|
2212
|
-
},
|
|
2213
|
-
report: (diagnostic) => {
|
|
2214
|
-
if (!active) throw new Error("Document transaction is no longer active.");
|
|
2215
|
-
(reports ??= []).push(publishDiagnostic(diagnostic));
|
|
2216
|
-
}
|
|
2217
|
-
};
|
|
2442
|
+
let committed = false;
|
|
2218
2443
|
try {
|
|
2219
|
-
const value = run
|
|
2220
|
-
if (value !== null && typeof value === "object" && typeof value.then === "function") throw new TypeError("Document update callback must be synchronous.");
|
|
2221
|
-
active = false;
|
|
2222
|
-
busy = false;
|
|
2444
|
+
const { value, reports } = runTransaction(session, run);
|
|
2223
2445
|
const batch = session.finish();
|
|
2224
2446
|
if (batch.status === "unchanged") return {
|
|
2225
2447
|
status: "unchanged",
|
|
2226
2448
|
value,
|
|
2227
2449
|
revision,
|
|
2228
|
-
reports
|
|
2450
|
+
reports
|
|
2229
2451
|
};
|
|
2230
2452
|
if (batch.status === "rejected") return {
|
|
2231
2453
|
status: "rejected",
|
|
2232
2454
|
issues: batch.issues,
|
|
2233
2455
|
revision
|
|
2234
2456
|
};
|
|
2235
|
-
const result = publish(batch,
|
|
2457
|
+
const result = publish(batch, source, "operations", options?.history ?? true);
|
|
2458
|
+
committed = true;
|
|
2236
2459
|
return {
|
|
2237
2460
|
status: "committed",
|
|
2238
2461
|
value,
|
|
2239
2462
|
commit: result.commit,
|
|
2240
|
-
reports
|
|
2463
|
+
reports,
|
|
2241
2464
|
observerErrors: result.observerErrors
|
|
2242
2465
|
};
|
|
2243
2466
|
} catch (error) {
|
|
2244
|
-
active = false;
|
|
2245
2467
|
try {
|
|
2246
|
-
session.rollback();
|
|
2468
|
+
if (!committed) session.rollback();
|
|
2247
2469
|
} finally {
|
|
2248
2470
|
busy = false;
|
|
2249
2471
|
}
|
|
@@ -2254,13 +2476,64 @@ const createDocument = (input) => {
|
|
|
2254
2476
|
};
|
|
2255
2477
|
throw error;
|
|
2256
2478
|
} finally {
|
|
2257
|
-
|
|
2479
|
+
busy = false;
|
|
2480
|
+
}
|
|
2481
|
+
},
|
|
2482
|
+
prepare: (run) => {
|
|
2483
|
+
assertWritable({ kind: "prepare" });
|
|
2484
|
+
busy = true;
|
|
2485
|
+
const session = createMutationSession(state.document, input.schema);
|
|
2486
|
+
try {
|
|
2487
|
+
const { value, reports } = runTransaction(session, run);
|
|
2488
|
+
const batch = session.finish();
|
|
2489
|
+
if (batch.status === "unchanged") {
|
|
2490
|
+
session.rollback();
|
|
2491
|
+
return {
|
|
2492
|
+
status: "unchanged",
|
|
2493
|
+
value,
|
|
2494
|
+
reports
|
|
2495
|
+
};
|
|
2496
|
+
}
|
|
2497
|
+
if (batch.status === "rejected") {
|
|
2498
|
+
session.rollback();
|
|
2499
|
+
return {
|
|
2500
|
+
status: "rejected",
|
|
2501
|
+
issues: batch.issues
|
|
2502
|
+
};
|
|
2503
|
+
}
|
|
2504
|
+
const impact = impactFor(batch, "operations");
|
|
2505
|
+
session.rollback();
|
|
2506
|
+
return {
|
|
2507
|
+
status: "prepared",
|
|
2508
|
+
value,
|
|
2509
|
+
operations: batch.operations,
|
|
2510
|
+
inverse: batch.inverse,
|
|
2511
|
+
impact,
|
|
2512
|
+
footprint: commandFootprint(batch.operations),
|
|
2513
|
+
reports
|
|
2514
|
+
};
|
|
2515
|
+
} catch (error) {
|
|
2516
|
+
try {
|
|
2517
|
+
session.rollback();
|
|
2518
|
+
} finally {
|
|
2519
|
+
busy = false;
|
|
2520
|
+
}
|
|
2521
|
+
if (error instanceof RejectedUpdate) return {
|
|
2522
|
+
status: "rejected",
|
|
2523
|
+
issues: error.issues
|
|
2524
|
+
};
|
|
2525
|
+
throw error;
|
|
2526
|
+
} finally {
|
|
2258
2527
|
busy = false;
|
|
2259
2528
|
}
|
|
2260
2529
|
},
|
|
2261
2530
|
apply: (operations, options) => applyBatch(operations, options?.source ?? "local", options?.history ?? true),
|
|
2262
2531
|
replace: (document, options) => {
|
|
2263
|
-
|
|
2532
|
+
const source = options?.source ?? "system";
|
|
2533
|
+
assertWritable({
|
|
2534
|
+
kind: "replace",
|
|
2535
|
+
source
|
|
2536
|
+
});
|
|
2264
2537
|
const invalidTree = invalidDocument(input.schema, document);
|
|
2265
2538
|
if (invalidTree) return {
|
|
2266
2539
|
status: "rejected",
|
|
@@ -2278,7 +2551,11 @@ const createDocument = (input) => {
|
|
|
2278
2551
|
inverse: EMPTY,
|
|
2279
2552
|
paths: EMPTY,
|
|
2280
2553
|
collections: EMPTY
|
|
2281
|
-
},
|
|
2554
|
+
}, source, "replace", false);
|
|
2555
|
+
},
|
|
2556
|
+
snapshot: () => {
|
|
2557
|
+
if (state.disposed) throw new DocumentDisposedError();
|
|
2558
|
+
return cloneValue(state.document, "snapshot");
|
|
2282
2559
|
},
|
|
2283
2560
|
subscribe: ((targetOrListener, listener) => {
|
|
2284
2561
|
if (state.disposed) throw new DocumentDisposedError();
|
|
@@ -2293,13 +2570,27 @@ const createDocument = (input) => {
|
|
|
2293
2570
|
state.disposed = true;
|
|
2294
2571
|
disposeNotification(notification);
|
|
2295
2572
|
history.api.clear();
|
|
2573
|
+
disposeRuntimeDriver(runtime);
|
|
2296
2574
|
}
|
|
2297
2575
|
};
|
|
2298
2576
|
bindRuntimeAccess(runtime, state);
|
|
2577
|
+
bindRuntimeDriver(runtime);
|
|
2299
2578
|
notification = createNotification(runtime);
|
|
2300
2579
|
return runtime;
|
|
2301
2580
|
};
|
|
2302
2581
|
//#endregion
|
|
2582
|
+
//#region core/src/runtime/readable.ts
|
|
2583
|
+
const asReadable = (runtime) => {
|
|
2584
|
+
const readable = {
|
|
2585
|
+
address: runtime.address,
|
|
2586
|
+
revision: runtime.revision,
|
|
2587
|
+
subscribe: runtime.subscribe
|
|
2588
|
+
};
|
|
2589
|
+
bindRuntimeAccess(readable, accessOf(runtime));
|
|
2590
|
+
shareNotification(runtime, readable);
|
|
2591
|
+
return Object.freeze(readable);
|
|
2592
|
+
};
|
|
2593
|
+
//#endregion
|
|
2303
2594
|
//#region core/src/projection/select.ts
|
|
2304
2595
|
const select = (runtime, selector) => readWith(runtime, selector);
|
|
2305
2596
|
//#endregion
|
|
@@ -2684,6 +2975,6 @@ const createMaterializedView = (runtime, spec) => {
|
|
|
2684
2975
|
return view;
|
|
2685
2976
|
};
|
|
2686
2977
|
//#endregion
|
|
2687
|
-
export { DocumentDisposedError, DocumentReentrancyError, contains, createCollectionView, createDocument, createMaterializedView, debugKey, dict, field, list, map, object, optional, overlaps, read as readAddress, record, resolveAddress, schema, select, single, table, impact_target_exports as target, tree, variant };
|
|
2978
|
+
export { DocumentDisposedError, DocumentReentrancyError, asReadable, commandFootprint, contains, createCollectionView, createDocument, createMaterializedView, debugKey, decodeCommandFootprint, dict, field, footprintsOverlap, list, map, object, optional, overlaps, read as readAddress, record, resolveAddress, schema, select, single, table, impact_target_exports as target, tree, variant };
|
|
2688
2979
|
|
|
2689
2980
|
//# sourceMappingURL=index.js.map
|