@powerhousedao/reactor 6.2.2-dev.31 → 6.2.2-dev.33
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/{build-worker-executor-CO2_-cnA.js → build-worker-executor-B9gIvuvf.js} +2 -2
- package/dist/{build-worker-executor-CO2_-cnA.js.map → build-worker-executor-B9gIvuvf.js.map} +1 -1
- package/dist/{drive-container-types-BNwEHEXP.js → drive-container-types-ZSLCC3lC.js} +340 -118
- package/dist/drive-container-types-ZSLCC3lC.js.map +1 -0
- package/dist/entry.js +2 -2
- package/dist/index.d.ts +1220 -1206
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -3
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.js +2 -2
- package/dist/{worker-DNfxv5Ir.js → worker-BMn-2vsH.js} +2 -2
- package/dist/{worker-DNfxv5Ir.js.map → worker-BMn-2vsH.js.map} +1 -1
- package/package.json +4 -4
- package/dist/drive-container-types-BNwEHEXP.js.map +0 -1
|
@@ -425,6 +425,16 @@ function buildErrorResult(job, error, startTime) {
|
|
|
425
425
|
duration: Date.now() - startTime
|
|
426
426
|
};
|
|
427
427
|
}
|
|
428
|
+
/**
|
|
429
|
+
* Whether this operation is part of the document's creation. The create and the
|
|
430
|
+
* upgrade from version zero hold the first two indexes for the life of the
|
|
431
|
+
* document, so a reshuffle has to leave them where they are.
|
|
432
|
+
*/
|
|
433
|
+
function isGenesisOperation(operation) {
|
|
434
|
+
if (operation.action.type === "CREATE_DOCUMENT") return true;
|
|
435
|
+
if (operation.action.type !== "UPGRADE_DOCUMENT") return false;
|
|
436
|
+
return operation.action.input.fromVersion === 0;
|
|
437
|
+
}
|
|
428
438
|
//#endregion
|
|
429
439
|
//#region src/cache/lru/lru-tracker.ts
|
|
430
440
|
var LRUNode = class {
|
|
@@ -1743,7 +1753,7 @@ function documentDecisionModel(target) {
|
|
|
1743
1753
|
scope: "document"
|
|
1744
1754
|
}
|
|
1745
1755
|
} },
|
|
1746
|
-
|
|
1756
|
+
evaluatesScope() {
|
|
1747
1757
|
return true;
|
|
1748
1758
|
},
|
|
1749
1759
|
decide(model) {
|
|
@@ -1767,19 +1777,15 @@ function comparePositions(a, b) {
|
|
|
1767
1777
|
const aTime = Date.parse(a.operation.timestampUtcMs);
|
|
1768
1778
|
const bTime = Date.parse(b.operation.timestampUtcMs);
|
|
1769
1779
|
if (aTime !== bTime) return aTime - bTime;
|
|
1780
|
+
if (a.streamKey === b.streamKey) return a.operation.index - b.operation.index;
|
|
1770
1781
|
const actionIds = (a.operation.action.id ?? "").localeCompare(b.operation.action.id ?? "");
|
|
1771
1782
|
if (actionIds !== 0) return actionIds;
|
|
1772
1783
|
return (a.operation.id ?? "").localeCompare(b.operation.id ?? "");
|
|
1773
1784
|
}
|
|
1774
1785
|
/**
|
|
1775
|
-
* Merges the read-set streams into one sequence by position.
|
|
1776
|
-
*
|
|
1777
|
-
*
|
|
1778
|
-
* order it is stored.
|
|
1779
|
-
*
|
|
1780
|
-
* An operation's position in the result is the bound a decision at that
|
|
1781
|
-
* operation reads to: every operation before it has been applied, and it has
|
|
1782
|
-
* not.
|
|
1786
|
+
* Merges the read-set streams into one sequence by position. An operation's
|
|
1787
|
+
* place in the result is the bound a decision at that operation reads to: every
|
|
1788
|
+
* operation before it has been applied, and it has not.
|
|
1783
1789
|
*/
|
|
1784
1790
|
function mergeByPosition(streams) {
|
|
1785
1791
|
const merged = [];
|
|
@@ -1790,8 +1796,9 @@ function mergeByPosition(streams) {
|
|
|
1790
1796
|
return merged.sort(comparePositions);
|
|
1791
1797
|
}
|
|
1792
1798
|
/**
|
|
1793
|
-
*
|
|
1794
|
-
*
|
|
1799
|
+
* The skip that retracts everything from `firstRetractedIndex` up to where the
|
|
1800
|
+
* re-appended operation lands. It spans the indexes rather than counting the
|
|
1801
|
+
* operations, because a stream with a gap in it makes those differ.
|
|
1795
1802
|
*/
|
|
1796
1803
|
function retractionSkip(nextIndex, firstRetractedIndex) {
|
|
1797
1804
|
return nextIndex - firstRetractedIndex;
|
|
@@ -1799,6 +1806,23 @@ function retractionSkip(nextIndex, firstRetractedIndex) {
|
|
|
1799
1806
|
//#endregion
|
|
1800
1807
|
//#region src/decision/walk.ts
|
|
1801
1808
|
/**
|
|
1809
|
+
* A single forward pass is only correct while a stream's effective operations
|
|
1810
|
+
* are ordered.
|
|
1811
|
+
*/
|
|
1812
|
+
function assertPositionOrder(streamKey, operations) {
|
|
1813
|
+
for (let i = 1; i < operations.length; i++) {
|
|
1814
|
+
const previous = operations[i - 1];
|
|
1815
|
+
const current = operations[i];
|
|
1816
|
+
if (comparePositions({
|
|
1817
|
+
streamKey,
|
|
1818
|
+
operation: previous
|
|
1819
|
+
}, {
|
|
1820
|
+
streamKey,
|
|
1821
|
+
operation: current
|
|
1822
|
+
}) > 0) throw new Error(`Stream ${streamKey} is out of position order: index ${previous.index} at ${previous.timestampUtcMs} precedes index ${current.index} at ${current.timestampUtcMs}`);
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
/**
|
|
1802
1826
|
* Visits every operation in the read-set once, in the order their positions
|
|
1803
1827
|
* fall, and hands back the state each stream held just before it. That state is
|
|
1804
1828
|
* what a decision at that operation reads: everything ahead of it has been
|
|
@@ -1810,10 +1834,15 @@ function retractionSkip(nextIndex, firstRetractedIndex) {
|
|
|
1810
1834
|
* A denied operation is visited but not applied.
|
|
1811
1835
|
*/
|
|
1812
1836
|
function* walkByPosition(streams) {
|
|
1813
|
-
const merged = mergeByPosition(streams.map((stream) =>
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1837
|
+
const merged = mergeByPosition(streams.map((stream) => {
|
|
1838
|
+
const operations = garbageCollect(sortOperations([...stream.operations]));
|
|
1839
|
+
assertPositionOrder(stream.streamKey, operations);
|
|
1840
|
+
return {
|
|
1841
|
+
streamKey: stream.streamKey,
|
|
1842
|
+
operations
|
|
1843
|
+
};
|
|
1844
|
+
}));
|
|
1845
|
+
const byKey = new Map(streams.map((stream) => [stream.streamKey, stream]));
|
|
1817
1846
|
const states = new Map(streams.map((stream) => [stream.streamKey, stream.document]));
|
|
1818
1847
|
for (const { streamKey, operation } of merged) {
|
|
1819
1848
|
yield {
|
|
@@ -1822,16 +1851,16 @@ function* walkByPosition(streams) {
|
|
|
1822
1851
|
states: new Map(states)
|
|
1823
1852
|
};
|
|
1824
1853
|
if (isDenied(operation)) continue;
|
|
1825
|
-
const stream =
|
|
1854
|
+
const stream = byKey.get(streamKey);
|
|
1826
1855
|
const before = states.get(streamKey);
|
|
1827
1856
|
if (before === void 0 || stream === void 0) throw new Error(`No state for stream ${streamKey}`);
|
|
1828
1857
|
states.set(streamKey, stream.apply(before, operation));
|
|
1829
1858
|
}
|
|
1830
1859
|
}
|
|
1831
1860
|
//#endregion
|
|
1832
|
-
//#region src/decision/deletion-
|
|
1861
|
+
//#region src/decision/deletion-evaluation.ts
|
|
1833
1862
|
const WRITTEN = "written";
|
|
1834
|
-
/** Whether a read stream counts this action as one that changes
|
|
1863
|
+
/** Whether a read stream counts this action as one that changes an evaluation. */
|
|
1835
1864
|
function canRefuseOthers(operation, readSet) {
|
|
1836
1865
|
return readSet.some((stream) => stream.decidingActions.includes(operation.action.type));
|
|
1837
1866
|
}
|
|
@@ -1849,19 +1878,20 @@ function firstDeleted(candidates) {
|
|
|
1849
1878
|
* and leaves the earlier ones alone. A deletion among the ones passed in does
|
|
1850
1879
|
* the same to those after it.
|
|
1851
1880
|
*/
|
|
1852
|
-
async function
|
|
1853
|
-
const
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
});
|
|
1881
|
+
async function evaluateDeletionsByPosition(target, subject, stores, signal) {
|
|
1882
|
+
const { scope, operations } = subject;
|
|
1883
|
+
const { writeCache, operationStore } = stores;
|
|
1884
|
+
const definition = documentDecisionModel(target);
|
|
1857
1885
|
const readSet = staticReadSet(definition);
|
|
1858
|
-
if (!definition.
|
|
1886
|
+
if (!definition.evaluatesScope(scope)) return operations.map(() => void 0);
|
|
1887
|
+
const evaluating = new Set(operations.map((operation) => operation.id));
|
|
1859
1888
|
const readStreams = await Promise.all(readSet.map(async (stream) => ({
|
|
1860
1889
|
stream,
|
|
1861
|
-
operations: (await operationStore.getSince(stream.query.documentId, stream.query.scope, stream.query.branch, 0, { actionTypes: stream.decidingActions }, void 0, signal)).results
|
|
1890
|
+
operations: (await operationStore.getSince(stream.query.documentId, stream.query.scope, stream.query.branch, 0, { actionTypes: stream.decidingActions }, void 0, signal)).results.filter((operation) => !evaluating.has(operation.id))
|
|
1862
1891
|
})));
|
|
1863
1892
|
const decidingWritten = operations.filter((operation) => canRefuseOthers(operation, readSet));
|
|
1864
1893
|
if (readStreams.every((read) => read.operations.length === 0) && decidingWritten.length === 0) return operations.map(() => void 0);
|
|
1894
|
+
if (readStreams.length === 0) throw new Error(`Decision model for ${target.documentId} reads no stream whose query is known before it is built`);
|
|
1865
1895
|
const walked = [];
|
|
1866
1896
|
for (const read of readStreams) {
|
|
1867
1897
|
const before = await writeCache.getState(read.stream.query.documentId, read.stream.query.scope, read.stream.query.branch, 0, signal);
|
|
@@ -1942,23 +1972,81 @@ var DriveCollectionId = class DriveCollectionId {
|
|
|
1942
1972
|
//#endregion
|
|
1943
1973
|
//#region src/executor/document-action-handler.ts
|
|
1944
1974
|
var DocumentActionHandler = class {
|
|
1945
|
-
constructor(registry, logger, driveContainerTypes) {
|
|
1975
|
+
constructor(registry, logger, driveContainerTypes, featureFlags) {
|
|
1946
1976
|
this.registry = registry;
|
|
1947
1977
|
this.logger = logger;
|
|
1948
1978
|
this.driveContainerTypes = driveContainerTypes;
|
|
1979
|
+
this.featureFlags = featureFlags;
|
|
1949
1980
|
}
|
|
1950
|
-
|
|
1981
|
+
/** Whether the write arrives with its evaluation already decided. */
|
|
1982
|
+
alreadyEvaluated(executing) {
|
|
1983
|
+
return this.featureFlags.documentDecisions && executing.replayingAcceptedHistory;
|
|
1984
|
+
}
|
|
1985
|
+
async execute(write, executing) {
|
|
1986
|
+
const { action } = write;
|
|
1987
|
+
if (write.deniedReason !== void 0) return this.writeDenied(write, executing);
|
|
1951
1988
|
switch (action.type) {
|
|
1952
|
-
case "CREATE_DOCUMENT": return this.executeCreate(
|
|
1953
|
-
case "DELETE_DOCUMENT": return this.executeDelete(
|
|
1954
|
-
case "UPGRADE_DOCUMENT": return this.executeUpgrade(
|
|
1955
|
-
case "ADD_RELATIONSHIP": return this.executeAddRelationship(
|
|
1956
|
-
case "REMOVE_RELATIONSHIP": return this.executeRemoveRelationship(
|
|
1957
|
-
case "UPDATE_RELATIONSHIP": return this.executeUpdateRelationship(
|
|
1958
|
-
default: return buildErrorResult(job, /* @__PURE__ */ new Error(`Unknown document action type: ${action.type}`), startTime);
|
|
1989
|
+
case "CREATE_DOCUMENT": return this.executeCreate(write, executing);
|
|
1990
|
+
case "DELETE_DOCUMENT": return this.executeDelete(write, executing);
|
|
1991
|
+
case "UPGRADE_DOCUMENT": return this.executeUpgrade(write, executing);
|
|
1992
|
+
case "ADD_RELATIONSHIP": return this.executeAddRelationship(write, executing);
|
|
1993
|
+
case "REMOVE_RELATIONSHIP": return this.executeRemoveRelationship(write, executing);
|
|
1994
|
+
case "UPDATE_RELATIONSHIP": return this.executeUpdateRelationship(write, executing);
|
|
1995
|
+
default: return buildErrorResult(executing.job, /* @__PURE__ */ new Error(`Unknown document action type: ${action.type}`), executing.startTime);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
/** A refused operation holds a position in the stream but changes nothing. */
|
|
1999
|
+
async writeDenied(write, executing) {
|
|
2000
|
+
const { action, skip, sourceRemote, deniedReason } = write;
|
|
2001
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2002
|
+
let document;
|
|
2003
|
+
try {
|
|
2004
|
+
document = await stores.writeCache.getState(job.documentId, job.scope, job.branch, void 0, signal);
|
|
2005
|
+
} catch (error) {
|
|
2006
|
+
return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
|
|
1959
2007
|
}
|
|
2008
|
+
let operation = createOperation(action, getNextIndexForScope(document, job.scope), skip, {
|
|
2009
|
+
documentId: job.documentId,
|
|
2010
|
+
scope: job.scope,
|
|
2011
|
+
branch: job.branch
|
|
2012
|
+
});
|
|
2013
|
+
operation.deniedReason = deniedReason;
|
|
2014
|
+
operation.hash = hashDocumentStateForScope(document, job.scope);
|
|
2015
|
+
const writeResult = await this.writeOperationToStore({
|
|
2016
|
+
documentId: job.documentId,
|
|
2017
|
+
documentType: document.header.documentType,
|
|
2018
|
+
scope: job.scope,
|
|
2019
|
+
branch: job.branch
|
|
2020
|
+
}, operation, executing);
|
|
2021
|
+
if (!Array.isArray(writeResult)) return writeResult;
|
|
2022
|
+
operation = writeResult[0];
|
|
2023
|
+
updateDocumentRevision(document, job.scope, operation.index);
|
|
2024
|
+
document.operations = {
|
|
2025
|
+
...document.operations,
|
|
2026
|
+
[job.scope]: [...document.operations[job.scope] ?? [], operation]
|
|
2027
|
+
};
|
|
2028
|
+
stores.writeCache.putState(job.documentId, job.scope, job.branch, operation.index, document, SnapshotPosition.Head);
|
|
2029
|
+
indexTxn.write([{
|
|
2030
|
+
...operation,
|
|
2031
|
+
documentId: job.documentId,
|
|
2032
|
+
documentType: document.header.documentType,
|
|
2033
|
+
branch: job.branch,
|
|
2034
|
+
scope: job.scope,
|
|
2035
|
+
sourceRemote
|
|
2036
|
+
}]);
|
|
2037
|
+
stores.documentMetaCache.putDocumentMeta(job.documentId, job.branch, {
|
|
2038
|
+
state: document.state.document,
|
|
2039
|
+
documentType: document.header.documentType,
|
|
2040
|
+
documentScopeRevision: operation.index + 1
|
|
2041
|
+
});
|
|
2042
|
+
return buildSuccessResult(job, operation, job.documentId, document.header.documentType, JSON.stringify({
|
|
2043
|
+
header: document.header,
|
|
2044
|
+
document: document.state.document
|
|
2045
|
+
}), startTime);
|
|
1960
2046
|
}
|
|
1961
|
-
async executeCreate(
|
|
2047
|
+
async executeCreate(write, executing) {
|
|
2048
|
+
const { action, skip, sourceRemote } = write;
|
|
2049
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
1962
2050
|
if (job.scope !== "document") return {
|
|
1963
2051
|
job,
|
|
1964
2052
|
success: false,
|
|
@@ -1976,7 +2064,12 @@ var DocumentActionHandler = class {
|
|
|
1976
2064
|
...document.state
|
|
1977
2065
|
};
|
|
1978
2066
|
const resultingState = JSON.stringify(resultingStateObj);
|
|
1979
|
-
const writeResult = await this.writeOperationToStore(
|
|
2067
|
+
const writeResult = await this.writeOperationToStore({
|
|
2068
|
+
documentId: document.header.id,
|
|
2069
|
+
documentType: document.header.documentType,
|
|
2070
|
+
scope: job.scope,
|
|
2071
|
+
branch: job.branch
|
|
2072
|
+
}, operation, executing);
|
|
1980
2073
|
if (!Array.isArray(writeResult)) return writeResult;
|
|
1981
2074
|
operation = writeResult[0];
|
|
1982
2075
|
updateDocumentRevision(document, job.scope, operation.index);
|
|
@@ -2005,7 +2098,9 @@ var DocumentActionHandler = class {
|
|
|
2005
2098
|
});
|
|
2006
2099
|
return buildSuccessResult(job, operation, document.header.id, document.header.documentType, resultingState, startTime);
|
|
2007
2100
|
}
|
|
2008
|
-
async executeDelete(
|
|
2101
|
+
async executeDelete(write, executing) {
|
|
2102
|
+
const { action, skip, sourceRemote } = write;
|
|
2103
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2009
2104
|
const input = action.input;
|
|
2010
2105
|
if (!input.documentId) return buildErrorResult(job, /* @__PURE__ */ new Error("DELETE_DOCUMENT action requires a documentId in input"), startTime);
|
|
2011
2106
|
const documentId = input.documentId;
|
|
@@ -2016,8 +2111,8 @@ var DocumentActionHandler = class {
|
|
|
2016
2111
|
return buildErrorResult(job, /* @__PURE__ */ new Error(`Failed to fetch document before deletion: ${error instanceof Error ? error.message : String(error)}`), startTime);
|
|
2017
2112
|
}
|
|
2018
2113
|
const documentState = document.state.document;
|
|
2019
|
-
if (documentState.isDeleted && !
|
|
2020
|
-
let operation = createOperation(action, getNextIndexForScope(document, job.scope),
|
|
2114
|
+
if (documentState.isDeleted && !this.alreadyEvaluated(executing)) return buildErrorResult(job, new DocumentDeletedError(documentId, documentState.deletedAtUtcIso), startTime);
|
|
2115
|
+
let operation = createOperation(action, getNextIndexForScope(document, job.scope), skip, {
|
|
2021
2116
|
documentId,
|
|
2022
2117
|
scope: job.scope,
|
|
2023
2118
|
branch: job.branch
|
|
@@ -2028,7 +2123,12 @@ var DocumentActionHandler = class {
|
|
|
2028
2123
|
document: document.state.document
|
|
2029
2124
|
};
|
|
2030
2125
|
const resultingState = JSON.stringify(resultingStateObj);
|
|
2031
|
-
const writeResult = await this.writeOperationToStore(
|
|
2126
|
+
const writeResult = await this.writeOperationToStore({
|
|
2127
|
+
documentId,
|
|
2128
|
+
documentType: document.header.documentType,
|
|
2129
|
+
scope: job.scope,
|
|
2130
|
+
branch: job.branch
|
|
2131
|
+
}, operation, executing);
|
|
2032
2132
|
if (!Array.isArray(writeResult)) return writeResult;
|
|
2033
2133
|
operation = writeResult[0];
|
|
2034
2134
|
updateDocumentRevision(document, job.scope, operation.index);
|
|
@@ -2052,7 +2152,9 @@ var DocumentActionHandler = class {
|
|
|
2052
2152
|
});
|
|
2053
2153
|
return buildSuccessResult(job, operation, documentId, document.header.documentType, resultingState, startTime);
|
|
2054
2154
|
}
|
|
2055
|
-
async executeUpgrade(
|
|
2155
|
+
async executeUpgrade(write, executing) {
|
|
2156
|
+
const { action, skip, sourceRemote } = write;
|
|
2157
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2056
2158
|
const input = action.input;
|
|
2057
2159
|
if (!input.documentId) return buildErrorResult(job, /* @__PURE__ */ new Error("UPGRADE_DOCUMENT action requires a documentId in input"), startTime);
|
|
2058
2160
|
const documentId = input.documentId;
|
|
@@ -2065,7 +2167,7 @@ var DocumentActionHandler = class {
|
|
|
2065
2167
|
return buildErrorResult(job, /* @__PURE__ */ new Error(`Failed to fetch document for upgrade: ${error instanceof Error ? error.message : String(error)}`), startTime);
|
|
2066
2168
|
}
|
|
2067
2169
|
const documentState = document.state.document;
|
|
2068
|
-
if (documentState.isDeleted && !
|
|
2170
|
+
if (documentState.isDeleted && !this.alreadyEvaluated(executing)) return buildErrorResult(job, new DocumentDeletedError(documentId, documentState.deletedAtUtcIso), startTime);
|
|
2069
2171
|
const nextIndex = getNextIndexForScope(document, job.scope);
|
|
2070
2172
|
let upgradePath;
|
|
2071
2173
|
if (fromVersion > 0 && fromVersion < toVersion) try {
|
|
@@ -2095,7 +2197,12 @@ var DocumentActionHandler = class {
|
|
|
2095
2197
|
...document.state
|
|
2096
2198
|
};
|
|
2097
2199
|
const resultingState = JSON.stringify(resultingStateObj);
|
|
2098
|
-
const writeResult = await this.writeOperationToStore(
|
|
2200
|
+
const writeResult = await this.writeOperationToStore({
|
|
2201
|
+
documentId,
|
|
2202
|
+
documentType: document.header.documentType,
|
|
2203
|
+
scope: job.scope,
|
|
2204
|
+
branch: job.branch
|
|
2205
|
+
}, operation, executing);
|
|
2099
2206
|
if (!Array.isArray(writeResult)) return writeResult;
|
|
2100
2207
|
operation = writeResult[0];
|
|
2101
2208
|
updateDocumentRevision(document, job.scope, operation.index);
|
|
@@ -2119,8 +2226,8 @@ var DocumentActionHandler = class {
|
|
|
2119
2226
|
});
|
|
2120
2227
|
return buildSuccessResult(job, operation, documentId, document.header.documentType, resultingState, startTime);
|
|
2121
2228
|
}
|
|
2122
|
-
executeAddRelationship(
|
|
2123
|
-
return this.withRelationshipAction("ADD_RELATIONSHIP",
|
|
2229
|
+
executeAddRelationship(write, executing) {
|
|
2230
|
+
return this.withRelationshipAction("ADD_RELATIONSHIP", write, executing, (input) => input.sourceId === input.targetId ? /* @__PURE__ */ new Error("ADD_RELATIONSHIP: sourceId and targetId cannot be the same (self-relationships not allowed)") : null, ({ indexTxn: txn, stores: s, sourceDoc, input, job: j }) => {
|
|
2124
2231
|
if (this.driveContainerTypes.has(sourceDoc.header.documentType)) {
|
|
2125
2232
|
const collectionId = DriveCollectionId.forDrive(input.sourceId, j.branch).key;
|
|
2126
2233
|
txn.addToCollection(collectionId, input.targetId);
|
|
@@ -2128,8 +2235,8 @@ var DocumentActionHandler = class {
|
|
|
2128
2235
|
}
|
|
2129
2236
|
});
|
|
2130
2237
|
}
|
|
2131
|
-
executeRemoveRelationship(
|
|
2132
|
-
return this.withRelationshipAction("REMOVE_RELATIONSHIP",
|
|
2238
|
+
executeRemoveRelationship(write, executing) {
|
|
2239
|
+
return this.withRelationshipAction("REMOVE_RELATIONSHIP", write, executing, null, ({ indexTxn: txn, stores: s, sourceDoc, input, job: j }) => {
|
|
2133
2240
|
if (this.driveContainerTypes.has(sourceDoc.header.documentType)) {
|
|
2134
2241
|
const collectionId = DriveCollectionId.forDrive(input.sourceId, j.branch).key;
|
|
2135
2242
|
txn.removeFromCollection(collectionId, input.targetId);
|
|
@@ -2137,10 +2244,12 @@ var DocumentActionHandler = class {
|
|
|
2137
2244
|
}
|
|
2138
2245
|
});
|
|
2139
2246
|
}
|
|
2140
|
-
executeUpdateRelationship(
|
|
2141
|
-
return this.withRelationshipAction("UPDATE_RELATIONSHIP",
|
|
2247
|
+
executeUpdateRelationship(write, executing) {
|
|
2248
|
+
return this.withRelationshipAction("UPDATE_RELATIONSHIP", write, executing, null, null);
|
|
2142
2249
|
}
|
|
2143
|
-
async withRelationshipAction(actionTypeName,
|
|
2250
|
+
async withRelationshipAction(actionTypeName, write, executing, preValidate, postWrite) {
|
|
2251
|
+
const { action, skip, sourceRemote } = write;
|
|
2252
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2144
2253
|
if (job.scope !== "document") return buildErrorResult(job, /* @__PURE__ */ new Error(`${actionTypeName} must be in "document" scope, got "${job.scope}"`), startTime);
|
|
2145
2254
|
const input = action.input;
|
|
2146
2255
|
if (!input.sourceId || !input.targetId || !input.relationshipType) return buildErrorResult(job, /* @__PURE__ */ new Error(`${actionTypeName} action requires sourceId, targetId, and relationshipType in input`), startTime);
|
|
@@ -2154,12 +2263,17 @@ var DocumentActionHandler = class {
|
|
|
2154
2263
|
} catch (error) {
|
|
2155
2264
|
return buildErrorResult(job, /* @__PURE__ */ new Error(`${actionTypeName}: source document ${input.sourceId} not found: ${error instanceof Error ? error.message : String(error)}`), startTime);
|
|
2156
2265
|
}
|
|
2157
|
-
let operation = createOperation(action, getNextIndexForScope(sourceDoc, job.scope),
|
|
2266
|
+
let operation = createOperation(action, getNextIndexForScope(sourceDoc, job.scope), skip, {
|
|
2158
2267
|
documentId: input.sourceId,
|
|
2159
2268
|
scope: job.scope,
|
|
2160
2269
|
branch: job.branch
|
|
2161
2270
|
});
|
|
2162
|
-
const writeResult = await this.writeOperationToStore(
|
|
2271
|
+
const writeResult = await this.writeOperationToStore({
|
|
2272
|
+
documentId: input.sourceId,
|
|
2273
|
+
documentType: sourceDoc.header.documentType,
|
|
2274
|
+
scope: job.scope,
|
|
2275
|
+
branch: job.branch
|
|
2276
|
+
}, operation, executing);
|
|
2163
2277
|
if (!Array.isArray(writeResult)) return writeResult;
|
|
2164
2278
|
operation = writeResult[0];
|
|
2165
2279
|
sourceDoc.header.lastModifiedAtUtcIso = operation.timestampUtcMs || (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2197,7 +2311,9 @@ var DocumentActionHandler = class {
|
|
|
2197
2311
|
});
|
|
2198
2312
|
return buildSuccessResult(job, operation, input.sourceId, sourceDoc.header.documentType, resultingState, startTime);
|
|
2199
2313
|
}
|
|
2200
|
-
async writeOperationToStore(
|
|
2314
|
+
async writeOperationToStore(target, operation, executing) {
|
|
2315
|
+
const { documentId, documentType, scope, branch } = target;
|
|
2316
|
+
const { job, startTime, stores, signal } = executing;
|
|
2201
2317
|
let storedOperations;
|
|
2202
2318
|
try {
|
|
2203
2319
|
storedOperations = await stores.operationStore.apply(documentId, documentType, scope, branch, operation.index, (txn) => {
|
|
@@ -2313,7 +2429,7 @@ var SimpleJobExecutor = class {
|
|
|
2313
2429
|
};
|
|
2314
2430
|
this.featureFlags = { documentDecisions: config.featureFlags?.documentDecisions ?? false };
|
|
2315
2431
|
this.signatureVerifierModule = new SignatureVerifier(signatureVerifier);
|
|
2316
|
-
this.documentActionHandler = new DocumentActionHandler(registry, logger, driveContainerTypes);
|
|
2432
|
+
this.documentActionHandler = new DocumentActionHandler(registry, logger, driveContainerTypes, this.featureFlags);
|
|
2317
2433
|
this.executionScope = executionScope ?? new DefaultExecutionScope(operationStore, operationIndex, writeCache, documentMetaCache, collectionMembershipCache);
|
|
2318
2434
|
}
|
|
2319
2435
|
/**
|
|
@@ -2329,7 +2445,14 @@ var SimpleJobExecutor = class {
|
|
|
2329
2445
|
result = await this.executionScope.run(async (stores) => {
|
|
2330
2446
|
const indexTxn = stores.operationIndex.start();
|
|
2331
2447
|
if (job.kind === "load") {
|
|
2332
|
-
const loadResult = await this.executeLoadJob(
|
|
2448
|
+
const loadResult = await this.executeLoadJob({
|
|
2449
|
+
job,
|
|
2450
|
+
startTime,
|
|
2451
|
+
indexTxn,
|
|
2452
|
+
stores,
|
|
2453
|
+
signal,
|
|
2454
|
+
replayingAcceptedHistory: true
|
|
2455
|
+
});
|
|
2333
2456
|
if (loadResult.success && loadResult.operationsWithContext) {
|
|
2334
2457
|
for (const owc of loadResult.operationsWithContext) touchedCacheEntries.push({
|
|
2335
2458
|
documentId: owc.context.documentId,
|
|
@@ -2348,7 +2471,20 @@ var SimpleJobExecutor = class {
|
|
|
2348
2471
|
}
|
|
2349
2472
|
return loadResult;
|
|
2350
2473
|
}
|
|
2351
|
-
const
|
|
2474
|
+
const executing = {
|
|
2475
|
+
job,
|
|
2476
|
+
startTime,
|
|
2477
|
+
indexTxn,
|
|
2478
|
+
stores,
|
|
2479
|
+
signal,
|
|
2480
|
+
replayingAcceptedHistory: false
|
|
2481
|
+
};
|
|
2482
|
+
const positioned = await this.positionByTimestamp(job, stores, signal);
|
|
2483
|
+
const actionResult = await this.processActions(positioned.actions.map((action, i) => ({
|
|
2484
|
+
action,
|
|
2485
|
+
skip: positioned.skipValues?.[i] ?? 0,
|
|
2486
|
+
sourceRemote: ""
|
|
2487
|
+
})), executing);
|
|
2352
2488
|
if (!actionResult.success) return {
|
|
2353
2489
|
job,
|
|
2354
2490
|
success: false,
|
|
@@ -2360,6 +2496,16 @@ var SimpleJobExecutor = class {
|
|
|
2360
2496
|
scope: owc.context.scope,
|
|
2361
2497
|
branch: owc.context.branch
|
|
2362
2498
|
});
|
|
2499
|
+
const reevaluationError = await this.reevaluateIfCriteriaMet({
|
|
2500
|
+
scope: job.scope,
|
|
2501
|
+
operations: actionResult.generatedOperations
|
|
2502
|
+
}, executing);
|
|
2503
|
+
if (reevaluationError) return {
|
|
2504
|
+
job,
|
|
2505
|
+
success: false,
|
|
2506
|
+
error: reevaluationError,
|
|
2507
|
+
duration: Date.now() - startTime
|
|
2508
|
+
};
|
|
2363
2509
|
const ordinals = await stores.operationIndex.commit(indexTxn, signal);
|
|
2364
2510
|
if (actionResult.operationsWithContext.length > 0) {
|
|
2365
2511
|
for (let i = 0; i < actionResult.operationsWithContext.length; i++) actionResult.operationsWithContext[i].context.ordinal = ordinals[i];
|
|
@@ -2395,7 +2541,9 @@ var SimpleJobExecutor = class {
|
|
|
2395
2541
|
const documentIds = [...new Set(operations.map((op) => op.context.documentId))];
|
|
2396
2542
|
return stores.collectionMembershipCache.getCollectionsForDocuments(documentIds);
|
|
2397
2543
|
}
|
|
2398
|
-
async processActions(
|
|
2544
|
+
async processActions(writes, executing) {
|
|
2545
|
+
const { job, signal } = executing;
|
|
2546
|
+
const actions = writes.map((write) => write.action);
|
|
2399
2547
|
const generatedOperations = [];
|
|
2400
2548
|
const operationsWithContext = [];
|
|
2401
2549
|
try {
|
|
@@ -2415,12 +2563,8 @@ var SimpleJobExecutor = class {
|
|
|
2415
2563
|
error: /* @__PURE__ */ new Error(`Invalid timestamp "${action.timestampUtcMs}" on action ${action.type} (id: ${action.id})`)
|
|
2416
2564
|
};
|
|
2417
2565
|
let lastYield = performance.now();
|
|
2418
|
-
for (
|
|
2419
|
-
const
|
|
2420
|
-
const skip = skipValues?.[actionIndex] ?? 0;
|
|
2421
|
-
const sourceOperation = sourceOperations?.[actionIndex];
|
|
2422
|
-
const deniedReason = deniedReasons?.[actionIndex];
|
|
2423
|
-
const result = documentScopeActions.includes(action.type) ? await this.documentActionHandler.execute(job, action, startTime, indexTxn, stores, skip, sourceRemote, signal, this.featureFlags.documentDecisions && job.kind === "load") : await this.executeRegularAction(job, action, startTime, indexTxn, stores, skip, sourceOperation, sourceRemote, signal, deniedReason);
|
|
2566
|
+
for (const write of writes) {
|
|
2567
|
+
const result = documentScopeActions.includes(write.action.type) ? await this.documentActionHandler.execute(write, executing) : await this.executeRegularAction(write, executing);
|
|
2424
2568
|
const error = this.accumulateResultOrReturnError(result, generatedOperations, operationsWithContext);
|
|
2425
2569
|
if (error !== null) return {
|
|
2426
2570
|
success: false,
|
|
@@ -2445,11 +2589,13 @@ var SimpleJobExecutor = class {
|
|
|
2445
2589
|
operationsWithContext
|
|
2446
2590
|
};
|
|
2447
2591
|
}
|
|
2448
|
-
async executeRegularAction(
|
|
2592
|
+
async executeRegularAction(write, executing) {
|
|
2593
|
+
const { action, skip, sourceOperation, sourceRemote, deniedReason } = write;
|
|
2594
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2449
2595
|
let appendCondition;
|
|
2450
2596
|
let documentVersion;
|
|
2451
|
-
const
|
|
2452
|
-
if (this.featureFlags.documentDecisions && !
|
|
2597
|
+
const alreadyEvaluated = this.featureFlags.documentDecisions && executing.replayingAcceptedHistory;
|
|
2598
|
+
if (this.featureFlags.documentDecisions && !alreadyEvaluated) {
|
|
2453
2599
|
const target = {
|
|
2454
2600
|
documentId: job.documentId,
|
|
2455
2601
|
branch: job.branch
|
|
@@ -2471,7 +2617,7 @@ var SimpleJobExecutor = class {
|
|
|
2471
2617
|
}, { scopeState: void 0 }) === "deny") return buildErrorResult(job, new DocumentDeletedError(job.documentId, built.model.document.deletedAtUtcIso), startTime);
|
|
2472
2618
|
appendCondition = built.appendCondition;
|
|
2473
2619
|
documentVersion = built.model.document.version;
|
|
2474
|
-
} else if (
|
|
2620
|
+
} else if (alreadyEvaluated) documentVersion = (await stores.writeCache.getState(job.documentId, "document", job.branch, void 0, signal)).state.document.version;
|
|
2475
2621
|
else {
|
|
2476
2622
|
let docMeta;
|
|
2477
2623
|
try {
|
|
@@ -2489,10 +2635,10 @@ var SimpleJobExecutor = class {
|
|
|
2489
2635
|
} catch (error) {
|
|
2490
2636
|
return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
|
|
2491
2637
|
}
|
|
2492
|
-
if (
|
|
2638
|
+
if (!executing.replayingAcceptedHistory) {
|
|
2493
2639
|
const subject = {
|
|
2494
|
-
address: action.context?.signer?.user.address,
|
|
2495
|
-
key: action.context?.signer?.app.key
|
|
2640
|
+
address: write.action.context?.signer?.user.address,
|
|
2641
|
+
key: write.action.context?.signer?.app.key
|
|
2496
2642
|
};
|
|
2497
2643
|
if (decide(document.state.auth, subject, {
|
|
2498
2644
|
verb: "execute",
|
|
@@ -2509,18 +2655,25 @@ var SimpleJobExecutor = class {
|
|
|
2509
2655
|
}
|
|
2510
2656
|
let updatedDocument;
|
|
2511
2657
|
if (deniedReason !== void 0) {
|
|
2512
|
-
const
|
|
2658
|
+
const index = getNextIndexForScope(document, job.scope);
|
|
2659
|
+
const denied = createOperation(action, index, skip, {
|
|
2513
2660
|
documentId: job.documentId,
|
|
2514
2661
|
scope: job.scope,
|
|
2515
2662
|
branch: job.branch
|
|
2516
2663
|
});
|
|
2517
2664
|
denied.deniedReason = deniedReason;
|
|
2518
|
-
|
|
2665
|
+
let standing = document;
|
|
2666
|
+
if (skip > 0) try {
|
|
2667
|
+
standing = await stores.writeCache.getState(job.documentId, job.scope, job.branch, index - skip - 1, signal);
|
|
2668
|
+
} catch (error) {
|
|
2669
|
+
return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
|
|
2670
|
+
}
|
|
2671
|
+
denied.hash = hashDocumentStateForScope(standing, job.scope);
|
|
2519
2672
|
updatedDocument = {
|
|
2520
|
-
...
|
|
2673
|
+
...standing,
|
|
2521
2674
|
operations: {
|
|
2522
|
-
...
|
|
2523
|
-
[job.scope]: [...
|
|
2675
|
+
...standing.operations,
|
|
2676
|
+
[job.scope]: [...standing.operations[job.scope] ?? [], denied]
|
|
2524
2677
|
}
|
|
2525
2678
|
};
|
|
2526
2679
|
} else try {
|
|
@@ -2600,36 +2753,106 @@ var SimpleJobExecutor = class {
|
|
|
2600
2753
|
};
|
|
2601
2754
|
}
|
|
2602
2755
|
/**
|
|
2603
|
-
*
|
|
2604
|
-
*
|
|
2605
|
-
*
|
|
2606
|
-
*
|
|
2756
|
+
* Orders a write by timestamp. The caller supplies the timestamp, so a write
|
|
2757
|
+
* can belong before operations already stored, and appending it at the tail
|
|
2758
|
+
* would leave the scope out of order. The operations it belongs before are
|
|
2759
|
+
* re-appended alongside it, the way a load reshuffles.
|
|
2607
2760
|
*/
|
|
2608
|
-
async
|
|
2761
|
+
async positionByTimestamp(job, stores, signal) {
|
|
2762
|
+
if (!this.featureFlags.documentDecisions || job.actions.length === 0) return { actions: job.actions };
|
|
2763
|
+
let earliest = job.actions[0].timestampUtcMs;
|
|
2764
|
+
for (const action of job.actions) if (action.timestampUtcMs < earliest) earliest = action.timestampUtcMs;
|
|
2765
|
+
const revisions = await stores.operationStore.getRevisions(job.documentId, job.branch, signal);
|
|
2766
|
+
if (earliest >= revisions.latestTimestamp) return { actions: job.actions };
|
|
2767
|
+
const conflicting = (await stores.operationStore.getConflicting(job.documentId, job.scope, job.branch, earliest, void 0, signal)).results.filter((operation) => !isGenesisOperation(operation));
|
|
2768
|
+
if (conflicting.length === 0) return { actions: job.actions };
|
|
2769
|
+
const nextIndex = revisions.revision[job.scope] ?? 0;
|
|
2770
|
+
let firstConflicting = conflicting[0].index;
|
|
2771
|
+
for (const operation of conflicting) if (operation.index < firstConflicting) firstConflicting = operation.index;
|
|
2772
|
+
const incoming = job.actions.map((action, i) => ({
|
|
2773
|
+
id: action.id,
|
|
2774
|
+
index: nextIndex + i,
|
|
2775
|
+
skip: 0,
|
|
2776
|
+
hash: "",
|
|
2777
|
+
timestampUtcMs: action.timestampUtcMs,
|
|
2778
|
+
action
|
|
2779
|
+
}));
|
|
2780
|
+
const merged = reshuffleByTimestamp({
|
|
2781
|
+
index: nextIndex,
|
|
2782
|
+
skip: retractionSkip(nextIndex, firstConflicting)
|
|
2783
|
+
}, conflicting, incoming);
|
|
2784
|
+
stores.writeCache.invalidate(job.documentId, job.scope, job.branch);
|
|
2785
|
+
return {
|
|
2786
|
+
actions: merged.map((operation) => operation.action),
|
|
2787
|
+
skipValues: merged.map((operation) => operation.skip)
|
|
2788
|
+
};
|
|
2789
|
+
}
|
|
2790
|
+
/**
|
|
2791
|
+
* Re-evaluates the document when a write meets both criteria: it was written
|
|
2792
|
+
* to a stream the model reads, and it is timestamped before an operation
|
|
2793
|
+
* already stored. The caller supplies the timestamp and the reactor does not replace
|
|
2794
|
+
* it, so a mutation job can write such an operation just as a load job can,
|
|
2795
|
+
* which is why both executeJob and executeLoadJob call this.
|
|
2796
|
+
*/
|
|
2797
|
+
async reevaluateIfCriteriaMet(criteria, executing) {
|
|
2798
|
+
if (!this.featureFlags.documentDecisions) return;
|
|
2799
|
+
const { job, stores, signal } = executing;
|
|
2800
|
+
if (!staticReadSet(documentDecisionModel({
|
|
2801
|
+
documentId: job.documentId,
|
|
2802
|
+
branch: job.branch
|
|
2803
|
+
})).some((stream) => stream.query.documentId === job.documentId && stream.query.scope === criteria.scope && stream.query.branch === job.branch)) return;
|
|
2804
|
+
const revisions = await stores.operationStore.getRevisions(job.documentId, job.branch, signal);
|
|
2805
|
+
const latest = Date.parse(revisions.latestTimestamp);
|
|
2806
|
+
if (!criteria.operations.some((operation) => Date.parse(operation.timestampUtcMs) < latest)) return;
|
|
2807
|
+
return this.reevaluateDocument(executing);
|
|
2808
|
+
}
|
|
2809
|
+
/**
|
|
2810
|
+
* Re-evaluates every scope the model evaluates. Where an operation's
|
|
2811
|
+
* evaluation differs from what is stored, the tail from that operation is
|
|
2812
|
+
* re-appended, carrying a skip that spans the indices it supersedes.
|
|
2813
|
+
*/
|
|
2814
|
+
async reevaluateDocument(executing) {
|
|
2815
|
+
const { job, stores, signal } = executing;
|
|
2609
2816
|
const definition = documentDecisionModel({
|
|
2610
2817
|
documentId: job.documentId,
|
|
2611
2818
|
branch: job.branch
|
|
2612
2819
|
});
|
|
2613
2820
|
const revisions = await stores.operationStore.getRevisions(job.documentId, job.branch, signal);
|
|
2614
2821
|
for (const scope of Object.keys(revisions.revision)) {
|
|
2615
|
-
if (!definition.
|
|
2822
|
+
if (!definition.evaluatesScope(scope)) continue;
|
|
2616
2823
|
const stored = (await stores.operationStore.getSince(job.documentId, scope, job.branch, -1, void 0, void 0, signal)).results;
|
|
2617
2824
|
const effective = garbageCollect(sortOperations([...stored]));
|
|
2618
2825
|
if (effective.length === 0) continue;
|
|
2619
|
-
const
|
|
2620
|
-
|
|
2826
|
+
const reevaluated = await evaluateDeletionsByPosition({
|
|
2827
|
+
documentId: job.documentId,
|
|
2828
|
+
branch: job.branch
|
|
2829
|
+
}, {
|
|
2830
|
+
scope,
|
|
2831
|
+
operations: effective
|
|
2832
|
+
}, stores, signal);
|
|
2833
|
+
const firstChange = effective.findIndex((operation, i) => operation.deniedReason !== reevaluated[i]);
|
|
2621
2834
|
if (firstChange === -1) continue;
|
|
2622
2835
|
const tail = effective.slice(firstChange);
|
|
2623
2836
|
const nextIndex = revisions.revision[scope];
|
|
2624
2837
|
stores.writeCache.invalidate(job.documentId, scope, job.branch);
|
|
2625
|
-
const result = await this.processActions({
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2838
|
+
const result = await this.processActions(tail.map((operation, i) => ({
|
|
2839
|
+
action: operation.action,
|
|
2840
|
+
skip: i === 0 ? retractionSkip(nextIndex, tail[0].index) : 0,
|
|
2841
|
+
sourceRemote: "",
|
|
2842
|
+
deniedReason: reevaluated[firstChange + i]
|
|
2843
|
+
})), {
|
|
2844
|
+
...executing,
|
|
2845
|
+
job: {
|
|
2846
|
+
...job,
|
|
2847
|
+
scope
|
|
2848
|
+
},
|
|
2849
|
+
replayingAcceptedHistory: true
|
|
2850
|
+
});
|
|
2629
2851
|
if (!result.success) return result.error ?? /* @__PURE__ */ new Error(`Re-evaluation of ${job.documentId} ${scope} failed`);
|
|
2630
2852
|
}
|
|
2631
2853
|
}
|
|
2632
|
-
async executeLoadJob(
|
|
2854
|
+
async executeLoadJob(executing) {
|
|
2855
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2633
2856
|
if (job.operations.length === 0) return buildErrorResult(job, /* @__PURE__ */ new Error("Load job must include at least one operation"), startTime);
|
|
2634
2857
|
let docMeta;
|
|
2635
2858
|
try {
|
|
@@ -2679,7 +2902,7 @@ var SimpleJobExecutor = class {
|
|
|
2679
2902
|
}
|
|
2680
2903
|
return true;
|
|
2681
2904
|
});
|
|
2682
|
-
const existingOpsToReshuffle = nonSupersededOps;
|
|
2905
|
+
const existingOpsToReshuffle = nonSupersededOps.filter((operation) => !isGenesisOperation(operation));
|
|
2683
2906
|
if (existingOpsToReshuffle.length > this.config.maxSkipThreshold) return {
|
|
2684
2907
|
job,
|
|
2685
2908
|
success: false,
|
|
@@ -2722,11 +2945,15 @@ var SimpleJobExecutor = class {
|
|
|
2722
2945
|
id: operation.id
|
|
2723
2946
|
})));
|
|
2724
2947
|
for (const operation of reshuffledOperations) if (operation.action.type === "NOOP") operation.skip = 1;
|
|
2725
|
-
const actions = reshuffledOperations.map((operation) => operation.action);
|
|
2726
|
-
const skipValues = reshuffledOperations.map((operation) => operation.skip);
|
|
2727
2948
|
let deniedReasons;
|
|
2728
2949
|
if (this.featureFlags.documentDecisions) try {
|
|
2729
|
-
deniedReasons = await
|
|
2950
|
+
deniedReasons = await evaluateDeletionsByPosition({
|
|
2951
|
+
documentId: job.documentId,
|
|
2952
|
+
branch: job.branch
|
|
2953
|
+
}, {
|
|
2954
|
+
scope,
|
|
2955
|
+
operations: reshuffledOperations
|
|
2956
|
+
}, stores, signal);
|
|
2730
2957
|
} catch (error) {
|
|
2731
2958
|
return {
|
|
2732
2959
|
job,
|
|
@@ -2736,7 +2963,13 @@ var SimpleJobExecutor = class {
|
|
|
2736
2963
|
};
|
|
2737
2964
|
}
|
|
2738
2965
|
const effectiveSourceRemote = skipCount > 0 ? "" : job.meta.sourceRemote || "";
|
|
2739
|
-
const result = await this.processActions(
|
|
2966
|
+
const result = await this.processActions(reshuffledOperations.map((operation, i) => ({
|
|
2967
|
+
action: operation.action,
|
|
2968
|
+
skip: operation.skip,
|
|
2969
|
+
sourceOperation: operation,
|
|
2970
|
+
sourceRemote: effectiveSourceRemote,
|
|
2971
|
+
deniedReason: deniedReasons?.[i]
|
|
2972
|
+
})), executing);
|
|
2740
2973
|
if (!result.success) return {
|
|
2741
2974
|
job,
|
|
2742
2975
|
success: false,
|
|
@@ -2745,23 +2978,16 @@ var SimpleJobExecutor = class {
|
|
|
2745
2978
|
};
|
|
2746
2979
|
stores.writeCache.invalidate(job.documentId, scope, job.branch);
|
|
2747
2980
|
if (scope === "document") stores.documentMetaCache.invalidate(job.documentId, job.branch);
|
|
2748
|
-
const
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
}
|
|
2752
|
-
if (
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
job,
|
|
2759
|
-
success: false,
|
|
2760
|
-
error,
|
|
2761
|
-
duration: Date.now() - startTime
|
|
2762
|
-
};
|
|
2763
|
-
}
|
|
2764
|
-
}
|
|
2981
|
+
const reevaluationError = await this.reevaluateIfCriteriaMet({
|
|
2982
|
+
scope,
|
|
2983
|
+
operations: result.generatedOperations
|
|
2984
|
+
}, executing);
|
|
2985
|
+
if (reevaluationError) return {
|
|
2986
|
+
job,
|
|
2987
|
+
success: false,
|
|
2988
|
+
error: reevaluationError,
|
|
2989
|
+
duration: Date.now() - startTime
|
|
2990
|
+
};
|
|
2765
2991
|
return {
|
|
2766
2992
|
job,
|
|
2767
2993
|
success: true,
|
|
@@ -3266,16 +3492,12 @@ var KyselyOperationStore = class KyselyOperationStore {
|
|
|
3266
3492
|
"o1.index",
|
|
3267
3493
|
"o1.timestampUtcMs"
|
|
3268
3494
|
]).where("o1.documentId", "=", documentId).where("o1.branch", "=", branch).where((eb) => eb("o1.index", "=", eb.selectFrom("Operation as o2").select((eb2) => eb2.fn.max("o2.index").as("maxIndex")).where("o2.documentId", "=", eb.ref("o1.documentId")).where("o2.branch", "=", eb.ref("o1.branch")).where("o2.scope", "=", eb.ref("o1.scope")))).execute();
|
|
3495
|
+
const latest = await this.queryExecutor.selectFrom("Operation").select((eb) => eb.fn.max("timestampUtcMs").as("latestTimestamp")).where("documentId", "=", documentId).where("branch", "=", branch).executeTakeFirst();
|
|
3269
3496
|
const revision = {};
|
|
3270
|
-
|
|
3271
|
-
for (const row of scopeRevisions) {
|
|
3272
|
-
revision[row.scope] = row.index + 1;
|
|
3273
|
-
const timestamp = row.timestampUtcMs.toISOString();
|
|
3274
|
-
if (timestamp > latestTimestamp) latestTimestamp = timestamp;
|
|
3275
|
-
}
|
|
3497
|
+
for (const row of scopeRevisions) revision[row.scope] = row.index + 1;
|
|
3276
3498
|
return {
|
|
3277
3499
|
revision,
|
|
3278
|
-
latestTimestamp
|
|
3500
|
+
latestTimestamp: latest?.latestTimestamp ? new Date(latest.latestTimestamp).toISOString() : (/* @__PURE__ */ new Date(0)).toISOString()
|
|
3279
3501
|
};
|
|
3280
3502
|
}
|
|
3281
3503
|
rowToOperation(row) {
|
|
@@ -3622,4 +3844,4 @@ const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "pow
|
|
|
3622
3844
|
//#endregion
|
|
3623
3845
|
export { AuthorizationDeniedError as A, DuplicateOperationError as C, DuplicateModuleError as D, DuplicateManifestError as E, throwIfAborted as F, __exportAll as I, DocumentNotFoundError as M, matchesScope as N, InvalidModuleError as O, parsePagingOptions as P, AppendConditionFailedError as S, RevisionMismatchError as T, DocumentMetaCache as _, createForwardingPoolInstrumentation as a, CollectionMembershipCache as b, KyselyKeyframeStore as c, DriveCollectionId as d, buildDecisionModel as f, KyselyOperationIndex as g, KyselyWriteCache as h, runMigrations as i, DocumentDeletedError as j, ModuleNotFoundError as k, DocumentModelRegistry as l, EventBus as m, REACTOR_SCHEMA as n, instrumentPgPool as o, KyselyExecutionScope as p, getMigrationStatus as r, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, SimpleJobExecutor as u, createConsistencyToken as v, OptimisticLockError as w, APPEND_CONDITION_FAILED_PREFIX as x, createEmptyConsistencyToken as y };
|
|
3624
3846
|
|
|
3625
|
-
//# sourceMappingURL=drive-container-types-
|
|
3847
|
+
//# sourceMappingURL=drive-container-types-ZSLCC3lC.js.map
|