@powerhousedao/reactor 6.2.2-dev.32 → 6.2.2-dev.34
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-DisavYJh.js → build-worker-executor-B9gIvuvf.js} +2 -2
- package/dist/{build-worker-executor-DisavYJh.js.map → build-worker-executor-B9gIvuvf.js.map} +1 -1
- package/dist/{drive-container-types-CPQDOggp.js → drive-container-types-ZSLCC3lC.js} +210 -96
- package/dist/drive-container-types-ZSLCC3lC.js.map +1 -0
- package/dist/entry.js +2 -2
- package/dist/index.d.ts +1650 -1643
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -3
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.js +2 -2
- package/dist/{worker-B2JPT0sg.js → worker-BMn-2vsH.js} +2 -2
- package/dist/{worker-B2JPT0sg.js.map → worker-BMn-2vsH.js.map} +1 -1
- package/package.json +4 -4
- package/dist/drive-container-types-CPQDOggp.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 {
|
|
@@ -1773,14 +1783,9 @@ function comparePositions(a, b) {
|
|
|
1773
1783
|
return (a.operation.id ?? "").localeCompare(b.operation.id ?? "");
|
|
1774
1784
|
}
|
|
1775
1785
|
/**
|
|
1776
|
-
* Merges the read-set streams into one sequence by position.
|
|
1777
|
-
*
|
|
1778
|
-
*
|
|
1779
|
-
* order it is stored.
|
|
1780
|
-
*
|
|
1781
|
-
* An operation's position in the result is the bound a decision at that
|
|
1782
|
-
* operation reads to: every operation before it has been applied, and it has
|
|
1783
|
-
* 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.
|
|
1784
1789
|
*/
|
|
1785
1790
|
function mergeByPosition(streams) {
|
|
1786
1791
|
const merged = [];
|
|
@@ -1791,8 +1796,9 @@ function mergeByPosition(streams) {
|
|
|
1791
1796
|
return merged.sort(comparePositions);
|
|
1792
1797
|
}
|
|
1793
1798
|
/**
|
|
1794
|
-
*
|
|
1795
|
-
*
|
|
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.
|
|
1796
1802
|
*/
|
|
1797
1803
|
function retractionSkip(nextIndex, firstRetractedIndex) {
|
|
1798
1804
|
return nextIndex - firstRetractedIndex;
|
|
@@ -1836,6 +1842,7 @@ function* walkByPosition(streams) {
|
|
|
1836
1842
|
operations
|
|
1837
1843
|
};
|
|
1838
1844
|
}));
|
|
1845
|
+
const byKey = new Map(streams.map((stream) => [stream.streamKey, stream]));
|
|
1839
1846
|
const states = new Map(streams.map((stream) => [stream.streamKey, stream.document]));
|
|
1840
1847
|
for (const { streamKey, operation } of merged) {
|
|
1841
1848
|
yield {
|
|
@@ -1844,7 +1851,7 @@ function* walkByPosition(streams) {
|
|
|
1844
1851
|
states: new Map(states)
|
|
1845
1852
|
};
|
|
1846
1853
|
if (isDenied(operation)) continue;
|
|
1847
|
-
const stream =
|
|
1854
|
+
const stream = byKey.get(streamKey);
|
|
1848
1855
|
const before = states.get(streamKey);
|
|
1849
1856
|
if (before === void 0 || stream === void 0) throw new Error(`No state for stream ${streamKey}`);
|
|
1850
1857
|
states.set(streamKey, stream.apply(before, operation));
|
|
@@ -1871,11 +1878,10 @@ function firstDeleted(candidates) {
|
|
|
1871
1878
|
* and leaves the earlier ones alone. A deletion among the ones passed in does
|
|
1872
1879
|
* the same to those after it.
|
|
1873
1880
|
*/
|
|
1874
|
-
async function evaluateDeletionsByPosition(
|
|
1875
|
-
const
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
});
|
|
1881
|
+
async function evaluateDeletionsByPosition(target, subject, stores, signal) {
|
|
1882
|
+
const { scope, operations } = subject;
|
|
1883
|
+
const { writeCache, operationStore } = stores;
|
|
1884
|
+
const definition = documentDecisionModel(target);
|
|
1879
1885
|
const readSet = staticReadSet(definition);
|
|
1880
1886
|
if (!definition.evaluatesScope(scope)) return operations.map(() => void 0);
|
|
1881
1887
|
const evaluating = new Set(operations.map((operation) => operation.id));
|
|
@@ -1885,6 +1891,7 @@ async function evaluateDeletionsByPosition(documentId, scope, branch, operations
|
|
|
1885
1891
|
})));
|
|
1886
1892
|
const decidingWritten = operations.filter((operation) => canRefuseOthers(operation, readSet));
|
|
1887
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`);
|
|
1888
1895
|
const walked = [];
|
|
1889
1896
|
for (const read of readStreams) {
|
|
1890
1897
|
const before = await writeCache.getState(read.stream.query.documentId, read.stream.query.scope, read.stream.query.branch, 0, signal);
|
|
@@ -1965,25 +1972,33 @@ var DriveCollectionId = class DriveCollectionId {
|
|
|
1965
1972
|
//#endregion
|
|
1966
1973
|
//#region src/executor/document-action-handler.ts
|
|
1967
1974
|
var DocumentActionHandler = class {
|
|
1968
|
-
constructor(registry, logger, driveContainerTypes) {
|
|
1975
|
+
constructor(registry, logger, driveContainerTypes, featureFlags) {
|
|
1969
1976
|
this.registry = registry;
|
|
1970
1977
|
this.logger = logger;
|
|
1971
1978
|
this.driveContainerTypes = driveContainerTypes;
|
|
1979
|
+
this.featureFlags = featureFlags;
|
|
1972
1980
|
}
|
|
1973
|
-
|
|
1974
|
-
|
|
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);
|
|
1975
1988
|
switch (action.type) {
|
|
1976
|
-
case "CREATE_DOCUMENT": return this.executeCreate(
|
|
1977
|
-
case "DELETE_DOCUMENT": return this.executeDelete(
|
|
1978
|
-
case "UPGRADE_DOCUMENT": return this.executeUpgrade(
|
|
1979
|
-
case "ADD_RELATIONSHIP": return this.executeAddRelationship(
|
|
1980
|
-
case "REMOVE_RELATIONSHIP": return this.executeRemoveRelationship(
|
|
1981
|
-
case "UPDATE_RELATIONSHIP": return this.executeUpdateRelationship(
|
|
1982
|
-
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);
|
|
1983
1996
|
}
|
|
1984
1997
|
}
|
|
1985
1998
|
/** A refused operation holds a position in the stream but changes nothing. */
|
|
1986
|
-
async writeDenied(
|
|
1999
|
+
async writeDenied(write, executing) {
|
|
2000
|
+
const { action, skip, sourceRemote, deniedReason } = write;
|
|
2001
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
1987
2002
|
let document;
|
|
1988
2003
|
try {
|
|
1989
2004
|
document = await stores.writeCache.getState(job.documentId, job.scope, job.branch, void 0, signal);
|
|
@@ -1997,7 +2012,12 @@ var DocumentActionHandler = class {
|
|
|
1997
2012
|
});
|
|
1998
2013
|
operation.deniedReason = deniedReason;
|
|
1999
2014
|
operation.hash = hashDocumentStateForScope(document, job.scope);
|
|
2000
|
-
const writeResult = await this.writeOperationToStore(
|
|
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);
|
|
2001
2021
|
if (!Array.isArray(writeResult)) return writeResult;
|
|
2002
2022
|
operation = writeResult[0];
|
|
2003
2023
|
updateDocumentRevision(document, job.scope, operation.index);
|
|
@@ -2024,7 +2044,9 @@ var DocumentActionHandler = class {
|
|
|
2024
2044
|
document: document.state.document
|
|
2025
2045
|
}), startTime);
|
|
2026
2046
|
}
|
|
2027
|
-
async executeCreate(
|
|
2047
|
+
async executeCreate(write, executing) {
|
|
2048
|
+
const { action, skip, sourceRemote } = write;
|
|
2049
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2028
2050
|
if (job.scope !== "document") return {
|
|
2029
2051
|
job,
|
|
2030
2052
|
success: false,
|
|
@@ -2042,7 +2064,12 @@ var DocumentActionHandler = class {
|
|
|
2042
2064
|
...document.state
|
|
2043
2065
|
};
|
|
2044
2066
|
const resultingState = JSON.stringify(resultingStateObj);
|
|
2045
|
-
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);
|
|
2046
2073
|
if (!Array.isArray(writeResult)) return writeResult;
|
|
2047
2074
|
operation = writeResult[0];
|
|
2048
2075
|
updateDocumentRevision(document, job.scope, operation.index);
|
|
@@ -2071,7 +2098,9 @@ var DocumentActionHandler = class {
|
|
|
2071
2098
|
});
|
|
2072
2099
|
return buildSuccessResult(job, operation, document.header.id, document.header.documentType, resultingState, startTime);
|
|
2073
2100
|
}
|
|
2074
|
-
async executeDelete(
|
|
2101
|
+
async executeDelete(write, executing) {
|
|
2102
|
+
const { action, skip, sourceRemote } = write;
|
|
2103
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2075
2104
|
const input = action.input;
|
|
2076
2105
|
if (!input.documentId) return buildErrorResult(job, /* @__PURE__ */ new Error("DELETE_DOCUMENT action requires a documentId in input"), startTime);
|
|
2077
2106
|
const documentId = input.documentId;
|
|
@@ -2082,7 +2111,7 @@ var DocumentActionHandler = class {
|
|
|
2082
2111
|
return buildErrorResult(job, /* @__PURE__ */ new Error(`Failed to fetch document before deletion: ${error instanceof Error ? error.message : String(error)}`), startTime);
|
|
2083
2112
|
}
|
|
2084
2113
|
const documentState = document.state.document;
|
|
2085
|
-
if (documentState.isDeleted && !alreadyEvaluated) return buildErrorResult(job, new DocumentDeletedError(documentId, documentState.deletedAtUtcIso), startTime);
|
|
2114
|
+
if (documentState.isDeleted && !this.alreadyEvaluated(executing)) return buildErrorResult(job, new DocumentDeletedError(documentId, documentState.deletedAtUtcIso), startTime);
|
|
2086
2115
|
let operation = createOperation(action, getNextIndexForScope(document, job.scope), skip, {
|
|
2087
2116
|
documentId,
|
|
2088
2117
|
scope: job.scope,
|
|
@@ -2094,7 +2123,12 @@ var DocumentActionHandler = class {
|
|
|
2094
2123
|
document: document.state.document
|
|
2095
2124
|
};
|
|
2096
2125
|
const resultingState = JSON.stringify(resultingStateObj);
|
|
2097
|
-
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);
|
|
2098
2132
|
if (!Array.isArray(writeResult)) return writeResult;
|
|
2099
2133
|
operation = writeResult[0];
|
|
2100
2134
|
updateDocumentRevision(document, job.scope, operation.index);
|
|
@@ -2118,7 +2152,9 @@ var DocumentActionHandler = class {
|
|
|
2118
2152
|
});
|
|
2119
2153
|
return buildSuccessResult(job, operation, documentId, document.header.documentType, resultingState, startTime);
|
|
2120
2154
|
}
|
|
2121
|
-
async executeUpgrade(
|
|
2155
|
+
async executeUpgrade(write, executing) {
|
|
2156
|
+
const { action, skip, sourceRemote } = write;
|
|
2157
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2122
2158
|
const input = action.input;
|
|
2123
2159
|
if (!input.documentId) return buildErrorResult(job, /* @__PURE__ */ new Error("UPGRADE_DOCUMENT action requires a documentId in input"), startTime);
|
|
2124
2160
|
const documentId = input.documentId;
|
|
@@ -2131,7 +2167,7 @@ var DocumentActionHandler = class {
|
|
|
2131
2167
|
return buildErrorResult(job, /* @__PURE__ */ new Error(`Failed to fetch document for upgrade: ${error instanceof Error ? error.message : String(error)}`), startTime);
|
|
2132
2168
|
}
|
|
2133
2169
|
const documentState = document.state.document;
|
|
2134
|
-
if (documentState.isDeleted && !alreadyEvaluated) return buildErrorResult(job, new DocumentDeletedError(documentId, documentState.deletedAtUtcIso), startTime);
|
|
2170
|
+
if (documentState.isDeleted && !this.alreadyEvaluated(executing)) return buildErrorResult(job, new DocumentDeletedError(documentId, documentState.deletedAtUtcIso), startTime);
|
|
2135
2171
|
const nextIndex = getNextIndexForScope(document, job.scope);
|
|
2136
2172
|
let upgradePath;
|
|
2137
2173
|
if (fromVersion > 0 && fromVersion < toVersion) try {
|
|
@@ -2161,7 +2197,12 @@ var DocumentActionHandler = class {
|
|
|
2161
2197
|
...document.state
|
|
2162
2198
|
};
|
|
2163
2199
|
const resultingState = JSON.stringify(resultingStateObj);
|
|
2164
|
-
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);
|
|
2165
2206
|
if (!Array.isArray(writeResult)) return writeResult;
|
|
2166
2207
|
operation = writeResult[0];
|
|
2167
2208
|
updateDocumentRevision(document, job.scope, operation.index);
|
|
@@ -2185,8 +2226,8 @@ var DocumentActionHandler = class {
|
|
|
2185
2226
|
});
|
|
2186
2227
|
return buildSuccessResult(job, operation, documentId, document.header.documentType, resultingState, startTime);
|
|
2187
2228
|
}
|
|
2188
|
-
executeAddRelationship(
|
|
2189
|
-
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 }) => {
|
|
2190
2231
|
if (this.driveContainerTypes.has(sourceDoc.header.documentType)) {
|
|
2191
2232
|
const collectionId = DriveCollectionId.forDrive(input.sourceId, j.branch).key;
|
|
2192
2233
|
txn.addToCollection(collectionId, input.targetId);
|
|
@@ -2194,8 +2235,8 @@ var DocumentActionHandler = class {
|
|
|
2194
2235
|
}
|
|
2195
2236
|
});
|
|
2196
2237
|
}
|
|
2197
|
-
executeRemoveRelationship(
|
|
2198
|
-
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 }) => {
|
|
2199
2240
|
if (this.driveContainerTypes.has(sourceDoc.header.documentType)) {
|
|
2200
2241
|
const collectionId = DriveCollectionId.forDrive(input.sourceId, j.branch).key;
|
|
2201
2242
|
txn.removeFromCollection(collectionId, input.targetId);
|
|
@@ -2203,10 +2244,12 @@ var DocumentActionHandler = class {
|
|
|
2203
2244
|
}
|
|
2204
2245
|
});
|
|
2205
2246
|
}
|
|
2206
|
-
executeUpdateRelationship(
|
|
2207
|
-
return this.withRelationshipAction("UPDATE_RELATIONSHIP",
|
|
2247
|
+
executeUpdateRelationship(write, executing) {
|
|
2248
|
+
return this.withRelationshipAction("UPDATE_RELATIONSHIP", write, executing, null, null);
|
|
2208
2249
|
}
|
|
2209
|
-
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;
|
|
2210
2253
|
if (job.scope !== "document") return buildErrorResult(job, /* @__PURE__ */ new Error(`${actionTypeName} must be in "document" scope, got "${job.scope}"`), startTime);
|
|
2211
2254
|
const input = action.input;
|
|
2212
2255
|
if (!input.sourceId || !input.targetId || !input.relationshipType) return buildErrorResult(job, /* @__PURE__ */ new Error(`${actionTypeName} action requires sourceId, targetId, and relationshipType in input`), startTime);
|
|
@@ -2220,12 +2263,17 @@ var DocumentActionHandler = class {
|
|
|
2220
2263
|
} catch (error) {
|
|
2221
2264
|
return buildErrorResult(job, /* @__PURE__ */ new Error(`${actionTypeName}: source document ${input.sourceId} not found: ${error instanceof Error ? error.message : String(error)}`), startTime);
|
|
2222
2265
|
}
|
|
2223
|
-
let operation = createOperation(action, getNextIndexForScope(sourceDoc, job.scope),
|
|
2266
|
+
let operation = createOperation(action, getNextIndexForScope(sourceDoc, job.scope), skip, {
|
|
2224
2267
|
documentId: input.sourceId,
|
|
2225
2268
|
scope: job.scope,
|
|
2226
2269
|
branch: job.branch
|
|
2227
2270
|
});
|
|
2228
|
-
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);
|
|
2229
2277
|
if (!Array.isArray(writeResult)) return writeResult;
|
|
2230
2278
|
operation = writeResult[0];
|
|
2231
2279
|
sourceDoc.header.lastModifiedAtUtcIso = operation.timestampUtcMs || (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -2263,7 +2311,9 @@ var DocumentActionHandler = class {
|
|
|
2263
2311
|
});
|
|
2264
2312
|
return buildSuccessResult(job, operation, input.sourceId, sourceDoc.header.documentType, resultingState, startTime);
|
|
2265
2313
|
}
|
|
2266
|
-
async writeOperationToStore(
|
|
2314
|
+
async writeOperationToStore(target, operation, executing) {
|
|
2315
|
+
const { documentId, documentType, scope, branch } = target;
|
|
2316
|
+
const { job, startTime, stores, signal } = executing;
|
|
2267
2317
|
let storedOperations;
|
|
2268
2318
|
try {
|
|
2269
2319
|
storedOperations = await stores.operationStore.apply(documentId, documentType, scope, branch, operation.index, (txn) => {
|
|
@@ -2379,7 +2429,7 @@ var SimpleJobExecutor = class {
|
|
|
2379
2429
|
};
|
|
2380
2430
|
this.featureFlags = { documentDecisions: config.featureFlags?.documentDecisions ?? false };
|
|
2381
2431
|
this.signatureVerifierModule = new SignatureVerifier(signatureVerifier);
|
|
2382
|
-
this.documentActionHandler = new DocumentActionHandler(registry, logger, driveContainerTypes);
|
|
2432
|
+
this.documentActionHandler = new DocumentActionHandler(registry, logger, driveContainerTypes, this.featureFlags);
|
|
2383
2433
|
this.executionScope = executionScope ?? new DefaultExecutionScope(operationStore, operationIndex, writeCache, documentMetaCache, collectionMembershipCache);
|
|
2384
2434
|
}
|
|
2385
2435
|
/**
|
|
@@ -2395,7 +2445,14 @@ var SimpleJobExecutor = class {
|
|
|
2395
2445
|
result = await this.executionScope.run(async (stores) => {
|
|
2396
2446
|
const indexTxn = stores.operationIndex.start();
|
|
2397
2447
|
if (job.kind === "load") {
|
|
2398
|
-
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
|
+
});
|
|
2399
2456
|
if (loadResult.success && loadResult.operationsWithContext) {
|
|
2400
2457
|
for (const owc of loadResult.operationsWithContext) touchedCacheEntries.push({
|
|
2401
2458
|
documentId: owc.context.documentId,
|
|
@@ -2414,7 +2471,20 @@ var SimpleJobExecutor = class {
|
|
|
2414
2471
|
}
|
|
2415
2472
|
return loadResult;
|
|
2416
2473
|
}
|
|
2417
|
-
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);
|
|
2418
2488
|
if (!actionResult.success) return {
|
|
2419
2489
|
job,
|
|
2420
2490
|
success: false,
|
|
@@ -2429,13 +2499,7 @@ var SimpleJobExecutor = class {
|
|
|
2429
2499
|
const reevaluationError = await this.reevaluateIfCriteriaMet({
|
|
2430
2500
|
scope: job.scope,
|
|
2431
2501
|
operations: actionResult.generatedOperations
|
|
2432
|
-
},
|
|
2433
|
-
job,
|
|
2434
|
-
startTime,
|
|
2435
|
-
indexTxn,
|
|
2436
|
-
stores,
|
|
2437
|
-
signal
|
|
2438
|
-
});
|
|
2502
|
+
}, executing);
|
|
2439
2503
|
if (reevaluationError) return {
|
|
2440
2504
|
job,
|
|
2441
2505
|
success: false,
|
|
@@ -2477,7 +2541,9 @@ var SimpleJobExecutor = class {
|
|
|
2477
2541
|
const documentIds = [...new Set(operations.map((op) => op.context.documentId))];
|
|
2478
2542
|
return stores.collectionMembershipCache.getCollectionsForDocuments(documentIds);
|
|
2479
2543
|
}
|
|
2480
|
-
async processActions(
|
|
2544
|
+
async processActions(writes, executing) {
|
|
2545
|
+
const { job, signal } = executing;
|
|
2546
|
+
const actions = writes.map((write) => write.action);
|
|
2481
2547
|
const generatedOperations = [];
|
|
2482
2548
|
const operationsWithContext = [];
|
|
2483
2549
|
try {
|
|
@@ -2497,13 +2563,8 @@ var SimpleJobExecutor = class {
|
|
|
2497
2563
|
error: /* @__PURE__ */ new Error(`Invalid timestamp "${action.timestampUtcMs}" on action ${action.type} (id: ${action.id})`)
|
|
2498
2564
|
};
|
|
2499
2565
|
let lastYield = performance.now();
|
|
2500
|
-
const
|
|
2501
|
-
|
|
2502
|
-
const action = actions[actionIndex];
|
|
2503
|
-
const skip = skipValues?.[actionIndex] ?? 0;
|
|
2504
|
-
const sourceOperation = sourceOperations?.[actionIndex];
|
|
2505
|
-
const deniedReason = deniedReasons?.[actionIndex];
|
|
2506
|
-
const result = documentScopeActions.includes(action.type) ? await this.documentActionHandler.execute(job, action, startTime, indexTxn, stores, skip, sourceRemote, signal, this.featureFlags.documentDecisions && replayingAcceptedHistory, deniedReason) : await this.executeRegularAction(job, action, startTime, indexTxn, stores, skip, sourceOperation, sourceRemote, signal, deniedReason, replayingAcceptedHistory);
|
|
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);
|
|
2507
2568
|
const error = this.accumulateResultOrReturnError(result, generatedOperations, operationsWithContext);
|
|
2508
2569
|
if (error !== null) return {
|
|
2509
2570
|
success: false,
|
|
@@ -2528,10 +2589,12 @@ var SimpleJobExecutor = class {
|
|
|
2528
2589
|
operationsWithContext
|
|
2529
2590
|
};
|
|
2530
2591
|
}
|
|
2531
|
-
async executeRegularAction(
|
|
2592
|
+
async executeRegularAction(write, executing) {
|
|
2593
|
+
const { action, skip, sourceOperation, sourceRemote, deniedReason } = write;
|
|
2594
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2532
2595
|
let appendCondition;
|
|
2533
2596
|
let documentVersion;
|
|
2534
|
-
const alreadyEvaluated = this.featureFlags.documentDecisions && replayingAcceptedHistory;
|
|
2597
|
+
const alreadyEvaluated = this.featureFlags.documentDecisions && executing.replayingAcceptedHistory;
|
|
2535
2598
|
if (this.featureFlags.documentDecisions && !alreadyEvaluated) {
|
|
2536
2599
|
const target = {
|
|
2537
2600
|
documentId: job.documentId,
|
|
@@ -2572,10 +2635,10 @@ var SimpleJobExecutor = class {
|
|
|
2572
2635
|
} catch (error) {
|
|
2573
2636
|
return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
|
|
2574
2637
|
}
|
|
2575
|
-
if (!replayingAcceptedHistory) {
|
|
2638
|
+
if (!executing.replayingAcceptedHistory) {
|
|
2576
2639
|
const subject = {
|
|
2577
|
-
address: action.context?.signer?.user.address,
|
|
2578
|
-
key: action.context?.signer?.app.key
|
|
2640
|
+
address: write.action.context?.signer?.user.address,
|
|
2641
|
+
key: write.action.context?.signer?.app.key
|
|
2579
2642
|
};
|
|
2580
2643
|
if (decide(document.state.auth, subject, {
|
|
2581
2644
|
verb: "execute",
|
|
@@ -2690,6 +2753,41 @@ var SimpleJobExecutor = class {
|
|
|
2690
2753
|
};
|
|
2691
2754
|
}
|
|
2692
2755
|
/**
|
|
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.
|
|
2760
|
+
*/
|
|
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
|
+
/**
|
|
2693
2791
|
* Re-evaluates the document when a write meets both criteria: it was written
|
|
2694
2792
|
* to a stream the model reads, and it is timestamped before an operation
|
|
2695
2793
|
* already stored. The caller supplies the timestamp and the reactor does not replace
|
|
@@ -2714,7 +2812,7 @@ var SimpleJobExecutor = class {
|
|
|
2714
2812
|
* re-appended, carrying a skip that spans the indices it supersedes.
|
|
2715
2813
|
*/
|
|
2716
2814
|
async reevaluateDocument(executing) {
|
|
2717
|
-
const { job,
|
|
2815
|
+
const { job, stores, signal } = executing;
|
|
2718
2816
|
const definition = documentDecisionModel({
|
|
2719
2817
|
documentId: job.documentId,
|
|
2720
2818
|
branch: job.branch
|
|
@@ -2725,20 +2823,36 @@ var SimpleJobExecutor = class {
|
|
|
2725
2823
|
const stored = (await stores.operationStore.getSince(job.documentId, scope, job.branch, -1, void 0, void 0, signal)).results;
|
|
2726
2824
|
const effective = garbageCollect(sortOperations([...stored]));
|
|
2727
2825
|
if (effective.length === 0) continue;
|
|
2728
|
-
const reevaluated = await evaluateDeletionsByPosition(
|
|
2826
|
+
const reevaluated = await evaluateDeletionsByPosition({
|
|
2827
|
+
documentId: job.documentId,
|
|
2828
|
+
branch: job.branch
|
|
2829
|
+
}, {
|
|
2830
|
+
scope,
|
|
2831
|
+
operations: effective
|
|
2832
|
+
}, stores, signal);
|
|
2729
2833
|
const firstChange = effective.findIndex((operation, i) => operation.deniedReason !== reevaluated[i]);
|
|
2730
2834
|
if (firstChange === -1) continue;
|
|
2731
2835
|
const tail = effective.slice(firstChange);
|
|
2732
2836
|
const nextIndex = revisions.revision[scope];
|
|
2733
2837
|
stores.writeCache.invalidate(job.documentId, scope, job.branch);
|
|
2734
|
-
const result = await this.processActions({
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
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
|
+
});
|
|
2738
2851
|
if (!result.success) return result.error ?? /* @__PURE__ */ new Error(`Re-evaluation of ${job.documentId} ${scope} failed`);
|
|
2739
2852
|
}
|
|
2740
2853
|
}
|
|
2741
|
-
async executeLoadJob(
|
|
2854
|
+
async executeLoadJob(executing) {
|
|
2855
|
+
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
2742
2856
|
if (job.operations.length === 0) return buildErrorResult(job, /* @__PURE__ */ new Error("Load job must include at least one operation"), startTime);
|
|
2743
2857
|
let docMeta;
|
|
2744
2858
|
try {
|
|
@@ -2788,7 +2902,7 @@ var SimpleJobExecutor = class {
|
|
|
2788
2902
|
}
|
|
2789
2903
|
return true;
|
|
2790
2904
|
});
|
|
2791
|
-
const existingOpsToReshuffle = nonSupersededOps;
|
|
2905
|
+
const existingOpsToReshuffle = nonSupersededOps.filter((operation) => !isGenesisOperation(operation));
|
|
2792
2906
|
if (existingOpsToReshuffle.length > this.config.maxSkipThreshold) return {
|
|
2793
2907
|
job,
|
|
2794
2908
|
success: false,
|
|
@@ -2831,11 +2945,15 @@ var SimpleJobExecutor = class {
|
|
|
2831
2945
|
id: operation.id
|
|
2832
2946
|
})));
|
|
2833
2947
|
for (const operation of reshuffledOperations) if (operation.action.type === "NOOP") operation.skip = 1;
|
|
2834
|
-
const actions = reshuffledOperations.map((operation) => operation.action);
|
|
2835
|
-
const skipValues = reshuffledOperations.map((operation) => operation.skip);
|
|
2836
2948
|
let deniedReasons;
|
|
2837
2949
|
if (this.featureFlags.documentDecisions) try {
|
|
2838
|
-
deniedReasons = await evaluateDeletionsByPosition(
|
|
2950
|
+
deniedReasons = await evaluateDeletionsByPosition({
|
|
2951
|
+
documentId: job.documentId,
|
|
2952
|
+
branch: job.branch
|
|
2953
|
+
}, {
|
|
2954
|
+
scope,
|
|
2955
|
+
operations: reshuffledOperations
|
|
2956
|
+
}, stores, signal);
|
|
2839
2957
|
} catch (error) {
|
|
2840
2958
|
return {
|
|
2841
2959
|
job,
|
|
@@ -2845,7 +2963,13 @@ var SimpleJobExecutor = class {
|
|
|
2845
2963
|
};
|
|
2846
2964
|
}
|
|
2847
2965
|
const effectiveSourceRemote = skipCount > 0 ? "" : job.meta.sourceRemote || "";
|
|
2848
|
-
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);
|
|
2849
2973
|
if (!result.success) return {
|
|
2850
2974
|
job,
|
|
2851
2975
|
success: false,
|
|
@@ -2857,13 +2981,7 @@ var SimpleJobExecutor = class {
|
|
|
2857
2981
|
const reevaluationError = await this.reevaluateIfCriteriaMet({
|
|
2858
2982
|
scope,
|
|
2859
2983
|
operations: result.generatedOperations
|
|
2860
|
-
},
|
|
2861
|
-
job,
|
|
2862
|
-
startTime,
|
|
2863
|
-
indexTxn,
|
|
2864
|
-
stores,
|
|
2865
|
-
signal
|
|
2866
|
-
});
|
|
2984
|
+
}, executing);
|
|
2867
2985
|
if (reevaluationError) return {
|
|
2868
2986
|
job,
|
|
2869
2987
|
success: false,
|
|
@@ -3374,16 +3492,12 @@ var KyselyOperationStore = class KyselyOperationStore {
|
|
|
3374
3492
|
"o1.index",
|
|
3375
3493
|
"o1.timestampUtcMs"
|
|
3376
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();
|
|
3377
3496
|
const revision = {};
|
|
3378
|
-
|
|
3379
|
-
for (const row of scopeRevisions) {
|
|
3380
|
-
revision[row.scope] = row.index + 1;
|
|
3381
|
-
const timestamp = row.timestampUtcMs.toISOString();
|
|
3382
|
-
if (timestamp > latestTimestamp) latestTimestamp = timestamp;
|
|
3383
|
-
}
|
|
3497
|
+
for (const row of scopeRevisions) revision[row.scope] = row.index + 1;
|
|
3384
3498
|
return {
|
|
3385
3499
|
revision,
|
|
3386
|
-
latestTimestamp
|
|
3500
|
+
latestTimestamp: latest?.latestTimestamp ? new Date(latest.latestTimestamp).toISOString() : (/* @__PURE__ */ new Date(0)).toISOString()
|
|
3387
3501
|
};
|
|
3388
3502
|
}
|
|
3389
3503
|
rowToOperation(row) {
|
|
@@ -3730,4 +3844,4 @@ const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "pow
|
|
|
3730
3844
|
//#endregion
|
|
3731
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 };
|
|
3732
3846
|
|
|
3733
|
-
//# sourceMappingURL=drive-container-types-
|
|
3847
|
+
//# sourceMappingURL=drive-container-types-ZSLCC3lC.js.map
|