@powerhousedao/reactor 6.2.2-dev.43 → 6.2.2-dev.45
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-CBWALjgq.js → build-worker-executor-CHkkh55C.js} +2 -2
- package/dist/{build-worker-executor-CBWALjgq.js.map → build-worker-executor-CHkkh55C.js.map} +1 -1
- package/dist/{drive-container-types-CU1ZUfD1.js → drive-container-types-h3M1AK3K.js} +479 -90
- package/dist/drive-container-types-h3M1AK3K.js.map +1 -0
- package/dist/entry.js +2 -2
- package/dist/index.d.ts +128 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +87 -4
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.js +2 -2
- package/dist/{worker-CeW6WsVF.js → worker-CbtYMIju.js} +2 -2
- package/dist/{worker-CeW6WsVF.js.map → worker-CbtYMIju.js.map} +1 -1
- package/package.json +4 -4
- package/dist/drive-container-types-CU1ZUfD1.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as ReactorEventTypes, t as EventBusAggregateError } from "./types-DMKLa0Ok.js";
|
|
2
|
-
import { AUTH_ACTION_TYPES, AUTH_DENIED_BY_GRANT_REASON, AUTH_NO_GRANT_REASON, AUTH_VERSION_UNSUPPORTED_REASON, DOCUMENT_DELETED_REASON, DowngradeNotSupportedError as DowngradeNotSupportedError$1, appendWithoutApplying, applyAuthAction, applyDeleteDocumentAction, applyDeleteDocumentAction as applyDeleteDocumentAction$1, applyUpgradeDocumentAction, applyUpgradeDocumentAction as applyUpgradeDocumentAction$1, baseReducerVersion, createPresignedHeader, decide, defaultBaseState, deriveOperationId, evaluate, garbageCollect, hashDocumentStateForScope, isDenied, isUndoRedo, normalizeDocumentModelVersion, sortOperations } from "@powerhousedao/shared/document-model";
|
|
2
|
+
import { AUTH_ACTION_TYPES, AUTH_DENIED_BY_GRANT_REASON, AUTH_NO_GRANT_REASON, AUTH_VERSION_UNSUPPORTED_REASON, DOCUMENT_DELETED_REASON, DowngradeNotSupportedError as DowngradeNotSupportedError$1, appendWithoutApplying, applyAuthAction, applyDeleteDocumentAction, applyDeleteDocumentAction as applyDeleteDocumentAction$1, applyUpgradeDocumentAction, applyUpgradeDocumentAction as applyUpgradeDocumentAction$1, baseReducerVersion, createPresignedHeader, decide, defaultBaseState, deriveOperationId, evaluate, garbageCollect, groupDocumentType, groupMembershipActionTypes, hashDocumentStateForScope, isDenied, isUndoRedo, mentionedGroupIds, normalizeDocumentModelVersion, referencedGroupIds, sortOperations } from "@powerhousedao/shared/document-model";
|
|
3
3
|
import { v4 } from "uuid";
|
|
4
4
|
import { Migrator, sql } from "kysely";
|
|
5
5
|
//#region \0rolldown/runtime.js
|
|
@@ -758,6 +758,7 @@ var KyselyOperationIndexTxn = class {
|
|
|
758
758
|
collections = [];
|
|
759
759
|
collectionMemberships = [];
|
|
760
760
|
collectionRemovals = [];
|
|
761
|
+
groupReferences = [];
|
|
761
762
|
operations = [];
|
|
762
763
|
createCollection(collectionId) {
|
|
763
764
|
this.collections.push(collectionId);
|
|
@@ -780,12 +781,25 @@ var KyselyOperationIndexTxn = class {
|
|
|
780
781
|
operationIndex: lastOpIndex
|
|
781
782
|
});
|
|
782
783
|
}
|
|
784
|
+
recordGroupReferences(documentId, groupIds) {
|
|
785
|
+
const lastOpIndex = this.operations.length - 1;
|
|
786
|
+
if (lastOpIndex < 0) throw new Error("recordGroupReferences must be called after write() - no operations in transaction");
|
|
787
|
+
if (groupIds.length === 0) return;
|
|
788
|
+
this.groupReferences.push({
|
|
789
|
+
documentId,
|
|
790
|
+
groupIds,
|
|
791
|
+
operationIndex: lastOpIndex
|
|
792
|
+
});
|
|
793
|
+
}
|
|
783
794
|
write(operations) {
|
|
784
795
|
this.operations.push(...operations);
|
|
785
796
|
}
|
|
786
797
|
getCollections() {
|
|
787
798
|
return this.collections;
|
|
788
799
|
}
|
|
800
|
+
getGroupReferenceRecords() {
|
|
801
|
+
return this.groupReferences;
|
|
802
|
+
}
|
|
789
803
|
getCollectionMembershipRecords() {
|
|
790
804
|
return this.collectionMemberships;
|
|
791
805
|
}
|
|
@@ -822,10 +836,27 @@ var KyselyOperationIndex = class KyselyOperationIndex {
|
|
|
822
836
|
});
|
|
823
837
|
return resultOrdinals;
|
|
824
838
|
}
|
|
839
|
+
/**
|
|
840
|
+
* A policy-driven join: keeps the earliest join so a rediscovered reference
|
|
841
|
+
* never shrinks a backfill window remotes already rely on, and reopens a
|
|
842
|
+
* closed membership because a policy reference is not a removable one.
|
|
843
|
+
*/
|
|
844
|
+
async joinKeepingEarliest(trx, documentId, collectionId, ordinal) {
|
|
845
|
+
await trx.insertInto("document_collections").values({
|
|
846
|
+
documentId,
|
|
847
|
+
collectionId,
|
|
848
|
+
joinedOrdinal: ordinal,
|
|
849
|
+
leftOrdinal: null
|
|
850
|
+
}).onConflict((oc) => oc.columns(["documentId", "collectionId"]).doUpdateSet({
|
|
851
|
+
joinedOrdinal: sql`LEAST("document_collections"."joinedOrdinal", EXCLUDED."joinedOrdinal")`,
|
|
852
|
+
leftOrdinal: null
|
|
853
|
+
})).execute();
|
|
854
|
+
}
|
|
825
855
|
async executeCommit(trx, kyselyTxn) {
|
|
826
856
|
const collections = kyselyTxn.getCollections();
|
|
827
857
|
const memberships = kyselyTxn.getCollectionMembershipRecords();
|
|
828
858
|
const removals = kyselyTxn.getCollectionRemovals();
|
|
859
|
+
const groupReferences = kyselyTxn.getGroupReferenceRecords();
|
|
829
860
|
const operations = kyselyTxn.getOperations();
|
|
830
861
|
if (collections.length > 0) {
|
|
831
862
|
const collectionRows = collections.map((collectionId) => ({
|
|
@@ -865,13 +896,28 @@ var KyselyOperationIndex = class KyselyOperationIndex {
|
|
|
865
896
|
joinedOrdinal: BigInt(ordinal),
|
|
866
897
|
leftOrdinal: null
|
|
867
898
|
})).execute();
|
|
899
|
+
const references = await trx.selectFrom("group_references").select("groupId").where("documentId", "=", m.documentId).execute();
|
|
900
|
+
for (const { groupId } of references) await this.joinKeepingEarliest(trx, groupId, m.collectionId, BigInt(ordinal));
|
|
868
901
|
}
|
|
869
902
|
if (removals.length > 0) for (const r of removals) {
|
|
870
903
|
const ordinal = operationOrdinals[r.operationIndex];
|
|
871
904
|
await trx.updateTable("document_collections").set({ leftOrdinal: BigInt(ordinal) }).where("collectionId", "=", r.collectionId).where("documentId", "=", r.documentId).where("leftOrdinal", "is", null).execute();
|
|
872
905
|
}
|
|
906
|
+
if (groupReferences.length > 0) for (const record of groupReferences) {
|
|
907
|
+
const ordinal = operationOrdinals[record.operationIndex];
|
|
908
|
+
await trx.insertInto("group_references").values(record.groupIds.map((groupId) => ({
|
|
909
|
+
documentId: record.documentId,
|
|
910
|
+
groupId
|
|
911
|
+
}))).onConflict((oc) => oc.doNothing()).execute();
|
|
912
|
+
const rows = await trx.selectFrom("document_collections").select("collectionId").where("documentId", "=", record.documentId).execute();
|
|
913
|
+
for (const groupId of record.groupIds) for (const { collectionId } of rows) await this.joinKeepingEarliest(trx, groupId, collectionId, BigInt(ordinal));
|
|
914
|
+
}
|
|
873
915
|
return operationOrdinals;
|
|
874
916
|
}
|
|
917
|
+
async getGroupReferencers(groupId, signal) {
|
|
918
|
+
if (signal?.aborted) throw new Error("Operation aborted");
|
|
919
|
+
return (await this.queryExecutor.selectFrom("group_references").select("documentId").where("groupId", "=", groupId).orderBy("documentId").execute()).map((row) => row.documentId);
|
|
920
|
+
}
|
|
875
921
|
async find(collectionId, cursor, view, paging, signal) {
|
|
876
922
|
if (signal?.aborted) throw new Error("Operation aborted");
|
|
877
923
|
const outerCursor = cursor ?? -1;
|
|
@@ -1768,7 +1814,9 @@ var EventBus = class {
|
|
|
1768
1814
|
*/
|
|
1769
1815
|
const FLAG_PREREQUISITES = {
|
|
1770
1816
|
documentDecisions: [],
|
|
1771
|
-
authEnforcement: ["documentDecisions"]
|
|
1817
|
+
authEnforcement: ["documentDecisions"],
|
|
1818
|
+
authGroups: ["authEnforcement"],
|
|
1819
|
+
authConditions: ["authGroups"]
|
|
1772
1820
|
};
|
|
1773
1821
|
/**
|
|
1774
1822
|
* Throws when the flags ask for enforcement the reactor cannot deliver. Either
|
|
@@ -1891,49 +1939,159 @@ function refusalReason(refusal) {
|
|
|
1891
1939
|
case "no-applicable-grant": return AUTH_NO_GRANT_REASON;
|
|
1892
1940
|
}
|
|
1893
1941
|
}
|
|
1942
|
+
function decideAuthModel(model, subject, request, groups, conditions) {
|
|
1943
|
+
if (request.verb === "execute" && model.document.isDeleted) return {
|
|
1944
|
+
decision: "deny",
|
|
1945
|
+
reason: DOCUMENT_DELETED_REASON
|
|
1946
|
+
};
|
|
1947
|
+
const evaluation = evaluate(model.auth, subject, request, groups, conditions);
|
|
1948
|
+
if (evaluation.decision === "allow") return { decision: "allow" };
|
|
1949
|
+
return {
|
|
1950
|
+
decision: "deny",
|
|
1951
|
+
reason: refusalReason(evaluation.refusal)
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
function documentProjection(target) {
|
|
1955
|
+
return {
|
|
1956
|
+
decidingActions: ["DELETE_DOCUMENT"],
|
|
1957
|
+
apply: (document, operation) => operation.action.type === "DELETE_DOCUMENT" ? applyDeleteDocumentAction({
|
|
1958
|
+
...document,
|
|
1959
|
+
state: { ...document.state }
|
|
1960
|
+
}, operation.action) : document,
|
|
1961
|
+
query: {
|
|
1962
|
+
documentId: target.documentId,
|
|
1963
|
+
branch: target.branch,
|
|
1964
|
+
scope: "document"
|
|
1965
|
+
}
|
|
1966
|
+
};
|
|
1967
|
+
}
|
|
1968
|
+
function authProjection(target) {
|
|
1969
|
+
return {
|
|
1970
|
+
decidingActions: [...AUTH_ACTION_TYPES],
|
|
1971
|
+
apply: (document, operation) => applyAuthAction(document, operation.action),
|
|
1972
|
+
query: {
|
|
1973
|
+
documentId: target.documentId,
|
|
1974
|
+
branch: target.branch,
|
|
1975
|
+
scope: "auth"
|
|
1976
|
+
}
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1894
1979
|
/** This decision model uses both the document and the auth streams. */
|
|
1895
1980
|
function authDecisionModel(target) {
|
|
1896
1981
|
return {
|
|
1897
1982
|
projections: {
|
|
1898
|
-
document:
|
|
1899
|
-
|
|
1900
|
-
apply: (document, operation) => operation.action.type === "DELETE_DOCUMENT" ? applyDeleteDocumentAction({
|
|
1901
|
-
...document,
|
|
1902
|
-
state: { ...document.state }
|
|
1903
|
-
}, operation.action) : document,
|
|
1904
|
-
query: {
|
|
1905
|
-
documentId: target.documentId,
|
|
1906
|
-
branch: target.branch,
|
|
1907
|
-
scope: "document"
|
|
1908
|
-
}
|
|
1909
|
-
},
|
|
1910
|
-
auth: {
|
|
1911
|
-
decidingActions: [...AUTH_ACTION_TYPES],
|
|
1912
|
-
apply: (document, operation) => applyAuthAction(document, operation.action),
|
|
1913
|
-
query: {
|
|
1914
|
-
documentId: target.documentId,
|
|
1915
|
-
branch: target.branch,
|
|
1916
|
-
scope: "auth"
|
|
1917
|
-
}
|
|
1918
|
-
}
|
|
1983
|
+
document: documentProjection(target),
|
|
1984
|
+
auth: authProjection(target)
|
|
1919
1985
|
},
|
|
1920
1986
|
evaluatesScope() {
|
|
1921
1987
|
return true;
|
|
1922
1988
|
},
|
|
1923
1989
|
decide(model, subject, request) {
|
|
1924
|
-
|
|
1925
|
-
decision: "deny",
|
|
1926
|
-
reason: DOCUMENT_DELETED_REASON
|
|
1927
|
-
};
|
|
1928
|
-
const evaluation = evaluate(model.auth, subject, request);
|
|
1929
|
-
if (evaluation.decision === "allow") return { decision: "allow" };
|
|
1930
|
-
return {
|
|
1931
|
-
decision: "deny",
|
|
1932
|
-
reason: refusalReason(evaluation.refusal)
|
|
1933
|
-
};
|
|
1990
|
+
return decideAuthModel(model, subject, request);
|
|
1934
1991
|
}
|
|
1935
1992
|
};
|
|
1936
1993
|
}
|
|
1994
|
+
/**
|
|
1995
|
+
* Folds one group-stream operation with the registered group model's reducer.
|
|
1996
|
+
* A reactor without the module registered folds nothing, so the member list
|
|
1997
|
+
* stays as read and a missing reducer never widens access.
|
|
1998
|
+
*/
|
|
1999
|
+
function applyGroupOperation(registry, document, operation) {
|
|
2000
|
+
let reducer;
|
|
2001
|
+
try {
|
|
2002
|
+
reducer = registry.getModule(groupDocumentType).reducer;
|
|
2003
|
+
} catch {
|
|
2004
|
+
return document;
|
|
2005
|
+
}
|
|
2006
|
+
return reducer(document, operation.action);
|
|
2007
|
+
}
|
|
2008
|
+
/**
|
|
2009
|
+
* Folds one evaluated-scope operation with the reducer registered for the
|
|
2010
|
+
* document's own type, at the document's stamped version. A reactor without
|
|
2011
|
+
* that module folds nothing, so conditions read the base state and an
|
|
2012
|
+
* unresolvable reducer never widens access.
|
|
2013
|
+
*/
|
|
2014
|
+
function applyModelOperation(registry, document, operation) {
|
|
2015
|
+
let reducer;
|
|
2016
|
+
try {
|
|
2017
|
+
const version = normalizeDocumentModelVersion(document.state.document?.version);
|
|
2018
|
+
reducer = registry.getModule(document.header.documentType, version).reducer;
|
|
2019
|
+
} catch {
|
|
2020
|
+
return document;
|
|
2021
|
+
}
|
|
2022
|
+
return reducer(document, operation.action);
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* The auth model extended with a derived groups projection: the streams it
|
|
2026
|
+
* reads are the group documents the folded grant list names, so adding a
|
|
2027
|
+
* grant that names a new group pulls that group's stream into the read-set.
|
|
2028
|
+
* Group queries pin the main branch, because a group's member list lives on
|
|
2029
|
+
* its main branch no matter which branch the referencing document is on.
|
|
2030
|
+
*/
|
|
2031
|
+
function groupsProjection(registry) {
|
|
2032
|
+
return {
|
|
2033
|
+
decidingActions: [...groupMembershipActionTypes],
|
|
2034
|
+
apply: (document, operation) => applyGroupOperation(registry, document, operation),
|
|
2035
|
+
query: (model) => referencedGroupIds(model.auth?.grants ?? []).map((id) => ({
|
|
2036
|
+
documentId: id,
|
|
2037
|
+
branch: "main",
|
|
2038
|
+
scope: "global"
|
|
2039
|
+
})),
|
|
2040
|
+
queryOverHistory: (reads) => {
|
|
2041
|
+
const ids = [];
|
|
2042
|
+
for (const read of reads) {
|
|
2043
|
+
if (read.name !== "auth") continue;
|
|
2044
|
+
for (const operation of read.operations) for (const id of mentionedGroupIds(operation.action)) if (!ids.includes(id)) ids.push(id);
|
|
2045
|
+
}
|
|
2046
|
+
return ids.map((id) => ({
|
|
2047
|
+
documentId: id,
|
|
2048
|
+
branch: "main",
|
|
2049
|
+
scope: "global"
|
|
2050
|
+
}));
|
|
2051
|
+
}
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
function authGroupsDecisionModel(registry) {
|
|
2055
|
+
return (target) => ({
|
|
2056
|
+
projections: {
|
|
2057
|
+
document: documentProjection(target),
|
|
2058
|
+
auth: authProjection(target),
|
|
2059
|
+
groups: groupsProjection(registry)
|
|
2060
|
+
},
|
|
2061
|
+
evaluatesScope() {
|
|
2062
|
+
return true;
|
|
2063
|
+
},
|
|
2064
|
+
decide(model, subject, request) {
|
|
2065
|
+
return decideAuthModel(model, subject, request, model.groups);
|
|
2066
|
+
}
|
|
2067
|
+
});
|
|
2068
|
+
}
|
|
2069
|
+
/**
|
|
2070
|
+
* The groups model with conditions live: decide hands the executing scope's
|
|
2071
|
+
* state and the action input through to the evaluator, so `where` clauses
|
|
2072
|
+
* and { match } principals apply. The model folds the evaluated scope during
|
|
2073
|
+
* a positional walk, so a condition reads the state as it stood at each
|
|
2074
|
+
* operation's position.
|
|
2075
|
+
*/
|
|
2076
|
+
function authConditionsDecisionModel(registry) {
|
|
2077
|
+
return (target) => ({
|
|
2078
|
+
projections: {
|
|
2079
|
+
document: documentProjection(target),
|
|
2080
|
+
auth: authProjection(target),
|
|
2081
|
+
groups: groupsProjection(registry)
|
|
2082
|
+
},
|
|
2083
|
+
foldEvaluatedScope: (document, operation) => applyModelOperation(registry, document, operation),
|
|
2084
|
+
evaluatesScope() {
|
|
2085
|
+
return true;
|
|
2086
|
+
},
|
|
2087
|
+
decide(model, subject, request, ctx) {
|
|
2088
|
+
return decideAuthModel(model, subject, request, model.groups, {
|
|
2089
|
+
scopeState: ctx.scopeState,
|
|
2090
|
+
actionInput: ctx.actionInput
|
|
2091
|
+
});
|
|
2092
|
+
}
|
|
2093
|
+
});
|
|
2094
|
+
}
|
|
1937
2095
|
//#endregion
|
|
1938
2096
|
//#region src/decision/build-decision-model.ts
|
|
1939
2097
|
/**
|
|
@@ -1957,7 +2115,16 @@ async function buildDecisionModel(cache, definition, target, signal) {
|
|
|
1957
2115
|
const queries = projection.query(staticModel);
|
|
1958
2116
|
const value = {};
|
|
1959
2117
|
for (const query of queries) {
|
|
1960
|
-
|
|
2118
|
+
let read;
|
|
2119
|
+
try {
|
|
2120
|
+
read = await readStream(cache, query, reads, signal);
|
|
2121
|
+
} catch (error) {
|
|
2122
|
+
if (error instanceof DocumentNotFoundError) {
|
|
2123
|
+
recordEmptyStream(query, reads);
|
|
2124
|
+
continue;
|
|
2125
|
+
}
|
|
2126
|
+
throw error;
|
|
2127
|
+
}
|
|
1961
2128
|
value[query.documentId] = read.state;
|
|
1962
2129
|
}
|
|
1963
2130
|
model[key] = value;
|
|
@@ -1967,6 +2134,20 @@ async function buildDecisionModel(cache, definition, target, signal) {
|
|
|
1967
2134
|
appendCondition: { streams: [...reads.values()].map((read) => read.stream) }
|
|
1968
2135
|
};
|
|
1969
2136
|
}
|
|
2137
|
+
/** Guards a stream that holds nothing yet: any operation appearing is growth. */
|
|
2138
|
+
function recordEmptyStream(query, reads) {
|
|
2139
|
+
const key = `${query.documentId}:${query.scope}:${query.branch}`;
|
|
2140
|
+
if (reads.has(key)) return;
|
|
2141
|
+
reads.set(key, {
|
|
2142
|
+
state: void 0,
|
|
2143
|
+
stream: {
|
|
2144
|
+
documentId: query.documentId,
|
|
2145
|
+
scope: query.scope,
|
|
2146
|
+
branch: query.branch,
|
|
2147
|
+
revision: -1
|
|
2148
|
+
}
|
|
2149
|
+
});
|
|
2150
|
+
}
|
|
1970
2151
|
async function readStream(cache, query, reads, signal) {
|
|
1971
2152
|
const key = `${query.documentId}:${query.scope}:${query.branch}`;
|
|
1972
2153
|
const existing = reads.get(key);
|
|
@@ -1998,6 +2179,24 @@ function observedRevision(document, scope) {
|
|
|
1998
2179
|
return document.header.revision[scope] - 1;
|
|
1999
2180
|
}
|
|
2000
2181
|
/**
|
|
2182
|
+
* The projections whose queries depend on folded state. A positional walk
|
|
2183
|
+
* resolves their streams through `queryOverHistory`; a projection without one
|
|
2184
|
+
* contributes no streams to a walk.
|
|
2185
|
+
*/
|
|
2186
|
+
function derivedReadSet(definition) {
|
|
2187
|
+
const projections = [];
|
|
2188
|
+
for (const [name, projection] of Object.entries(definition.projections)) {
|
|
2189
|
+
if (typeof projection.query !== "function") continue;
|
|
2190
|
+
projections.push({
|
|
2191
|
+
name,
|
|
2192
|
+
decidingActions: projection.decidingActions,
|
|
2193
|
+
apply: projection.apply,
|
|
2194
|
+
queryOverHistory: projection.queryOverHistory
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
return projections;
|
|
2198
|
+
}
|
|
2199
|
+
/**
|
|
2001
2200
|
* The streams a model reads whose queries are known before it is built. A
|
|
2002
2201
|
* derived query needs the statically-queried projections first, so it is not
|
|
2003
2202
|
* included here.
|
|
@@ -2051,11 +2250,21 @@ function documentDecisionModel(target) {
|
|
|
2051
2250
|
/**
|
|
2052
2251
|
* Builds the model at the stream heads and decides one request against it. The
|
|
2053
2252
|
* append condition it returns is the read-set the store enforces at write time.
|
|
2253
|
+
*
|
|
2254
|
+
* With `conditions` supplied, the executing scope's state is read at the head
|
|
2255
|
+
* for `doc.<scope>.*` paths. That read carries no append-condition entry of
|
|
2256
|
+
* its own: the written stream's expected-revision check already refuses a
|
|
2257
|
+
* write whose scope grew between the read and the append.
|
|
2054
2258
|
*/
|
|
2055
|
-
async function decideAtHead(model, cache, target, subject, request, signal) {
|
|
2259
|
+
async function decideAtHead(model, cache, target, subject, request, signal, conditions) {
|
|
2056
2260
|
const built = await buildDecisionModel(cache, model, target, signal);
|
|
2261
|
+
let scopeState;
|
|
2262
|
+
if (conditions !== void 0) scopeState = (await cache.getState(target.documentId, request.scope, target.branch, void 0, signal)).state[request.scope];
|
|
2057
2263
|
return {
|
|
2058
|
-
evaluation: model(target).decide(built.model, subject, request, {
|
|
2264
|
+
evaluation: model(target).decide(built.model, subject, request, {
|
|
2265
|
+
scopeState,
|
|
2266
|
+
actionInput: conditions?.actionInput
|
|
2267
|
+
}),
|
|
2059
2268
|
appendCondition: built.appendCondition,
|
|
2060
2269
|
documentVersion: built.model.document.version,
|
|
2061
2270
|
deletedAtUtcIso: built.model.document.deletedAtUtcIso ?? null
|
|
@@ -2063,9 +2272,13 @@ async function decideAtHead(model, cache, target, subject, request, signal) {
|
|
|
2063
2272
|
}
|
|
2064
2273
|
/**
|
|
2065
2274
|
* The model this reactor enforces. With `authEnforcement` off the auth scope is
|
|
2066
|
-
* absent from every append condition and no load walks it
|
|
2275
|
+
* absent from every append condition and no load walks it; with `authGroups`
|
|
2276
|
+
* on, the group documents the grant list names join the read-set and the
|
|
2277
|
+
* registry supplies the reducer that folds them.
|
|
2067
2278
|
*/
|
|
2068
|
-
function selectDecisionModel(flags) {
|
|
2279
|
+
function selectDecisionModel(flags, registry) {
|
|
2280
|
+
if (flags.authConditions) return authConditionsDecisionModel(registry);
|
|
2281
|
+
if (flags.authGroups) return authGroupsDecisionModel(registry);
|
|
2069
2282
|
return flags.authEnforcement ? authDecisionModel : documentDecisionModel;
|
|
2070
2283
|
}
|
|
2071
2284
|
//#endregion
|
|
@@ -2195,16 +2408,24 @@ function subjectOf(operation) {
|
|
|
2195
2408
|
};
|
|
2196
2409
|
}
|
|
2197
2410
|
/**
|
|
2198
|
-
* The model as the walk reached this operation: each projection's value
|
|
2199
|
-
* own scope's state,
|
|
2411
|
+
* The model as the walk reached this operation: each static projection's value
|
|
2412
|
+
* is its own scope's state, and each derived projection's value maps document
|
|
2413
|
+
* id to that document's state, holding only the streams this replica walked. A
|
|
2414
|
+
* derived stream it does not hold stays out of the map, which fails closed.
|
|
2200
2415
|
*/
|
|
2201
|
-
function modelAt(readSet, states) {
|
|
2416
|
+
function modelAt(readSet, derivedNames, derived, states) {
|
|
2202
2417
|
const model = {};
|
|
2203
2418
|
for (const stream of readSet) {
|
|
2204
2419
|
const document = states.get(streamKey(stream.query));
|
|
2205
2420
|
if (document === void 0) throw new Error(`No state walked for projection ${stream.name}`);
|
|
2206
2421
|
model[stream.name] = document.state[stream.query.scope];
|
|
2207
2422
|
}
|
|
2423
|
+
for (const name of derivedNames) model[name] = {};
|
|
2424
|
+
for (const entry of derived) {
|
|
2425
|
+
const map = model[entry.name];
|
|
2426
|
+
const document = states.get(streamKey(entry.query));
|
|
2427
|
+
if (document !== void 0) map[entry.query.documentId] = document.state[entry.query.scope];
|
|
2428
|
+
}
|
|
2208
2429
|
return model;
|
|
2209
2430
|
}
|
|
2210
2431
|
/**
|
|
@@ -2220,6 +2441,7 @@ async function evaluateByPosition(model, target, subject, stores, signal) {
|
|
|
2220
2441
|
const { writeCache, operationStore } = stores;
|
|
2221
2442
|
const definition = model(target);
|
|
2222
2443
|
const readSet = staticReadSet(definition);
|
|
2444
|
+
const derivedSet = derivedReadSet(definition);
|
|
2223
2445
|
if (!definition.evaluatesScope(scope)) return operations.map(() => void 0);
|
|
2224
2446
|
const evaluating = new Set(operations.map((operation) => operation.id));
|
|
2225
2447
|
const readStreams = await Promise.all(readSet.map(async (stream) => ({
|
|
@@ -2231,24 +2453,76 @@ async function evaluateByPosition(model, target, subject, stores, signal) {
|
|
|
2231
2453
|
if (readStreams.length === 0) throw new Error(`Decision model for ${target.documentId} reads no stream whose query is known before it is built`);
|
|
2232
2454
|
const writtenProjection = readSet.find((stream) => stream.query.scope === scope);
|
|
2233
2455
|
const walked = [];
|
|
2456
|
+
const histories = [];
|
|
2234
2457
|
for (const read of readStreams) {
|
|
2235
|
-
const
|
|
2458
|
+
const streamOperations = read.stream === writtenProjection ? [...read.operations, ...operations] : read.operations;
|
|
2236
2459
|
const before = await writeCache.getState(read.stream.query.documentId, read.stream.query.scope, read.stream.query.branch, -1, signal);
|
|
2237
2460
|
walked.push({
|
|
2238
2461
|
streamKey: streamKey(read.stream.query),
|
|
2239
2462
|
scope: read.stream.query.scope,
|
|
2240
2463
|
document: before,
|
|
2241
|
-
operations:
|
|
2464
|
+
operations: streamOperations,
|
|
2242
2465
|
apply: read.stream.apply
|
|
2243
2466
|
});
|
|
2467
|
+
histories.push({
|
|
2468
|
+
name: read.stream.name,
|
|
2469
|
+
operations: streamOperations
|
|
2470
|
+
});
|
|
2244
2471
|
}
|
|
2245
|
-
|
|
2472
|
+
let evaluatedStateKey;
|
|
2473
|
+
if (writtenProjection !== void 0) evaluatedStateKey = streamKey(writtenProjection.query);
|
|
2474
|
+
else if (definition.foldEvaluatedScope !== void 0) {
|
|
2475
|
+
const query = {
|
|
2476
|
+
documentId: target.documentId,
|
|
2477
|
+
scope,
|
|
2478
|
+
branch: target.branch
|
|
2479
|
+
};
|
|
2480
|
+
const storedOperations = (await operationStore.getSince(query.documentId, query.scope, query.branch, -1, void 0, void 0, signal)).results.filter((operation) => !evaluating.has(operation.id));
|
|
2481
|
+
const before = await writeCache.getState(query.documentId, query.scope, query.branch, -1, signal);
|
|
2482
|
+
evaluatedStateKey = streamKey(query);
|
|
2483
|
+
walked.push({
|
|
2484
|
+
streamKey: evaluatedStateKey,
|
|
2485
|
+
scope,
|
|
2486
|
+
document: before,
|
|
2487
|
+
operations: [...storedOperations, ...operations],
|
|
2488
|
+
apply: definition.foldEvaluatedScope
|
|
2489
|
+
});
|
|
2490
|
+
} else walked.push({
|
|
2246
2491
|
streamKey: EVALUATED_ONLY,
|
|
2247
2492
|
scope,
|
|
2248
2493
|
document: walked[0].document,
|
|
2249
2494
|
operations,
|
|
2250
2495
|
apply: (document) => document
|
|
2251
2496
|
});
|
|
2497
|
+
const derivedEntries = [];
|
|
2498
|
+
const walkedKeys = new Set(walked.map((stream) => stream.streamKey));
|
|
2499
|
+
for (const projection of derivedSet) {
|
|
2500
|
+
const queries = projection.queryOverHistory?.(histories) ?? [];
|
|
2501
|
+
for (const query of queries) {
|
|
2502
|
+
const key = streamKey(query);
|
|
2503
|
+
if (walkedKeys.has(key)) continue;
|
|
2504
|
+
let before;
|
|
2505
|
+
try {
|
|
2506
|
+
before = await writeCache.getState(query.documentId, query.scope, query.branch, -1, signal);
|
|
2507
|
+
} catch (error) {
|
|
2508
|
+
if (error instanceof DocumentNotFoundError) continue;
|
|
2509
|
+
throw error;
|
|
2510
|
+
}
|
|
2511
|
+
const streamOperations = (await operationStore.getSince(query.documentId, query.scope, query.branch, -1, { actionTypes: projection.decidingActions }, void 0, signal)).results.filter((operation) => !evaluating.has(operation.id));
|
|
2512
|
+
walkedKeys.add(key);
|
|
2513
|
+
walked.push({
|
|
2514
|
+
streamKey: key,
|
|
2515
|
+
scope: query.scope,
|
|
2516
|
+
document: before,
|
|
2517
|
+
operations: streamOperations,
|
|
2518
|
+
apply: projection.apply
|
|
2519
|
+
});
|
|
2520
|
+
derivedEntries.push({
|
|
2521
|
+
name: projection.name,
|
|
2522
|
+
query
|
|
2523
|
+
});
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2252
2526
|
const reasons = /* @__PURE__ */ new Map();
|
|
2253
2527
|
const walk = walkByPosition(walked);
|
|
2254
2528
|
let step = walk.next(false);
|
|
@@ -2258,11 +2532,16 @@ async function evaluateByPosition(model, target, subject, stores, signal) {
|
|
|
2258
2532
|
step = walk.next(false);
|
|
2259
2533
|
continue;
|
|
2260
2534
|
}
|
|
2261
|
-
const
|
|
2535
|
+
const evaluatedDocument = evaluatedStateKey === void 0 ? void 0 : position.states.get(evaluatedStateKey);
|
|
2536
|
+
const scopeState = evaluatedDocument === void 0 ? void 0 : evaluatedDocument.state[scope];
|
|
2537
|
+
const evaluation = definition.decide(modelAt(readSet, derivedSet.map((projection) => projection.name), derivedEntries, position.states), subjectOf(position.operation), {
|
|
2262
2538
|
verb: "execute",
|
|
2263
2539
|
scope: position.operation.action.scope,
|
|
2264
2540
|
operation: position.operation.action.type
|
|
2265
|
-
}, {
|
|
2541
|
+
}, {
|
|
2542
|
+
scopeState,
|
|
2543
|
+
actionInput: position.operation.action.input
|
|
2544
|
+
});
|
|
2266
2545
|
const denied = evaluation.decision === "deny";
|
|
2267
2546
|
reasons.set(position.operation.id, denied ? evaluation.reason : void 0);
|
|
2268
2547
|
step = walk.next(denied);
|
|
@@ -2363,7 +2642,7 @@ var DocumentActionHandler = class {
|
|
|
2363
2642
|
verb: "execute",
|
|
2364
2643
|
scope: action.scope,
|
|
2365
2644
|
operation: action.type
|
|
2366
|
-
}, signal);
|
|
2645
|
+
}, signal, this.featureFlags.authConditions ? { actionInput: action.input } : void 0);
|
|
2367
2646
|
} catch (error) {
|
|
2368
2647
|
return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
|
|
2369
2648
|
}
|
|
@@ -2845,10 +3124,12 @@ var SimpleJobExecutor = class {
|
|
|
2845
3124
|
};
|
|
2846
3125
|
this.featureFlags = {
|
|
2847
3126
|
documentDecisions: config.featureFlags?.documentDecisions ?? false,
|
|
2848
|
-
authEnforcement: config.featureFlags?.authEnforcement ?? false
|
|
3127
|
+
authEnforcement: config.featureFlags?.authEnforcement ?? false,
|
|
3128
|
+
authGroups: config.featureFlags?.authGroups ?? false,
|
|
3129
|
+
authConditions: config.featureFlags?.authConditions ?? false
|
|
2849
3130
|
};
|
|
2850
3131
|
validateFeatureFlags(this.featureFlags, FLAG_PREREQUISITES);
|
|
2851
|
-
this.decisionModel = selectDecisionModel(this.featureFlags);
|
|
3132
|
+
this.decisionModel = selectDecisionModel(this.featureFlags, registry);
|
|
2852
3133
|
this.signatureVerifierModule = new SignatureVerifier(signatureVerifier);
|
|
2853
3134
|
this.documentActionHandler = new DocumentActionHandler(registry, logger, driveContainerTypes, this.featureFlags, this.decisionModel);
|
|
2854
3135
|
this.executionScope = executionScope ?? new DefaultExecutionScope(operationStore, operationIndex, writeCache, documentMetaCache, collectionMembershipCache);
|
|
@@ -2895,6 +3176,37 @@ var SimpleJobExecutor = class {
|
|
|
2895
3176
|
}
|
|
2896
3177
|
return loadResult;
|
|
2897
3178
|
}
|
|
3179
|
+
if (job.kind === "reevaluation") {
|
|
3180
|
+
const reevalResult = await this.executeReevaluationJob({
|
|
3181
|
+
job,
|
|
3182
|
+
startTime,
|
|
3183
|
+
indexTxn,
|
|
3184
|
+
stores,
|
|
3185
|
+
signal,
|
|
3186
|
+
replayingAcceptedHistory: false,
|
|
3187
|
+
evaluatedByPosition: false,
|
|
3188
|
+
postCommitInvalidations
|
|
3189
|
+
});
|
|
3190
|
+
if (reevalResult.success && reevalResult.operationsWithContext) {
|
|
3191
|
+
for (const owc of reevalResult.operationsWithContext) touchedCacheEntries.push({
|
|
3192
|
+
documentId: owc.context.documentId,
|
|
3193
|
+
scope: owc.context.scope,
|
|
3194
|
+
branch: owc.context.branch
|
|
3195
|
+
});
|
|
3196
|
+
const ordinals = await stores.operationIndex.commit(indexTxn, signal);
|
|
3197
|
+
for (let i = 0; i < reevalResult.operationsWithContext.length; i++) reevalResult.operationsWithContext[i].context.ordinal = ordinals[i];
|
|
3198
|
+
if (reevalResult.operationsWithContext.length > 0) {
|
|
3199
|
+
const collectionMemberships = await this.getCollectionMembershipsForOperations(reevalResult.operationsWithContext, stores);
|
|
3200
|
+
pendingEvent = {
|
|
3201
|
+
jobId: job.id,
|
|
3202
|
+
operations: reevalResult.operationsWithContext,
|
|
3203
|
+
jobMeta: job.meta,
|
|
3204
|
+
collectionMemberships
|
|
3205
|
+
};
|
|
3206
|
+
}
|
|
3207
|
+
}
|
|
3208
|
+
return reevalResult;
|
|
3209
|
+
}
|
|
2898
3210
|
const positioned = await this.positionByTimestamp(job, stores, signal);
|
|
2899
3211
|
if (positioned.error) return buildErrorResult(job, positioned.error, startTime);
|
|
2900
3212
|
const executing = {
|
|
@@ -3033,7 +3345,7 @@ var SimpleJobExecutor = class {
|
|
|
3033
3345
|
verb: "execute",
|
|
3034
3346
|
scope: action.scope,
|
|
3035
3347
|
operation: action.type
|
|
3036
|
-
}, signal);
|
|
3348
|
+
}, signal, this.featureFlags.authConditions ? { actionInput: action.input } : void 0);
|
|
3037
3349
|
} catch (error) {
|
|
3038
3350
|
return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
|
|
3039
3351
|
}
|
|
@@ -3156,6 +3468,7 @@ var SimpleJobExecutor = class {
|
|
|
3156
3468
|
scope,
|
|
3157
3469
|
sourceRemote
|
|
3158
3470
|
}]);
|
|
3471
|
+
if (scope === "auth") indexTxn.recordGroupReferences(job.documentId, mentionedGroupIds(action));
|
|
3159
3472
|
return {
|
|
3160
3473
|
job,
|
|
3161
3474
|
success: true,
|
|
@@ -3352,7 +3665,7 @@ var SimpleJobExecutor = class {
|
|
|
3352
3665
|
const revisions = await stores.operationStore.getRevisions(job.documentId, job.branch, signal);
|
|
3353
3666
|
const latest = Date.parse(revisions.latestTimestamp);
|
|
3354
3667
|
if (!criteria.operations.some((operation) => Date.parse(operation.timestampUtcMs) < latest)) return;
|
|
3355
|
-
return this.reevaluateDocument(executing);
|
|
3668
|
+
return (await this.reevaluateDocument(executing)).error;
|
|
3356
3669
|
}
|
|
3357
3670
|
/**
|
|
3358
3671
|
* Re-evaluates every scope the model evaluates. Where an operation's
|
|
@@ -3365,6 +3678,7 @@ var SimpleJobExecutor = class {
|
|
|
3365
3678
|
documentId: job.documentId,
|
|
3366
3679
|
branch: job.branch
|
|
3367
3680
|
};
|
|
3681
|
+
const reappended = [];
|
|
3368
3682
|
const revisions = await stores.operationStore.getRevisions(job.documentId, job.branch, signal);
|
|
3369
3683
|
for (const scope of this.evaluationOrder(target, revisions.revision)) {
|
|
3370
3684
|
const stored = (await stores.operationStore.getSince(job.documentId, scope, job.branch, -1, void 0, void 0, signal)).results;
|
|
@@ -3393,8 +3707,60 @@ var SimpleJobExecutor = class {
|
|
|
3393
3707
|
replayingAcceptedHistory: true,
|
|
3394
3708
|
evaluatedByPosition: true
|
|
3395
3709
|
});
|
|
3396
|
-
if (!result.success) return
|
|
3710
|
+
if (!result.success) return {
|
|
3711
|
+
error: result.error ?? /* @__PURE__ */ new Error(`Re-evaluation of ${job.documentId} ${scope} failed`),
|
|
3712
|
+
operationsWithContext: reappended
|
|
3713
|
+
};
|
|
3714
|
+
reappended.push(...result.operationsWithContext);
|
|
3715
|
+
}
|
|
3716
|
+
return { operationsWithContext: reappended };
|
|
3717
|
+
}
|
|
3718
|
+
/**
|
|
3719
|
+
* Re-judges a document's stored operations because a read-set stream in
|
|
3720
|
+
* another document (a group) gained an operation. The trigger timestamp
|
|
3721
|
+
* bounds the work: an operation later than everything this document holds
|
|
3722
|
+
* cannot change any evaluation, so the pass is skipped.
|
|
3723
|
+
*/
|
|
3724
|
+
async executeReevaluationJob(executing) {
|
|
3725
|
+
const { job, startTime, stores, signal } = executing;
|
|
3726
|
+
if (!this.featureFlags.documentDecisions) return {
|
|
3727
|
+
job,
|
|
3728
|
+
success: true,
|
|
3729
|
+
operations: [],
|
|
3730
|
+
operationsWithContext: [],
|
|
3731
|
+
duration: Date.now() - startTime
|
|
3732
|
+
};
|
|
3733
|
+
const trigger = job.meta.triggerTimestampUtcMs;
|
|
3734
|
+
if (typeof trigger === "string") {
|
|
3735
|
+
let latestTimestamp;
|
|
3736
|
+
try {
|
|
3737
|
+
latestTimestamp = (await stores.operationStore.getRevisions(job.documentId, job.branch, signal)).latestTimestamp;
|
|
3738
|
+
} catch {
|
|
3739
|
+
return {
|
|
3740
|
+
job,
|
|
3741
|
+
success: true,
|
|
3742
|
+
operations: [],
|
|
3743
|
+
operationsWithContext: [],
|
|
3744
|
+
duration: Date.now() - startTime
|
|
3745
|
+
};
|
|
3746
|
+
}
|
|
3747
|
+
if (Date.parse(trigger) > Date.parse(latestTimestamp)) return {
|
|
3748
|
+
job,
|
|
3749
|
+
success: true,
|
|
3750
|
+
operations: [],
|
|
3751
|
+
operationsWithContext: [],
|
|
3752
|
+
duration: Date.now() - startTime
|
|
3753
|
+
};
|
|
3397
3754
|
}
|
|
3755
|
+
const outcome = await this.reevaluateDocument(executing);
|
|
3756
|
+
if (outcome.error) return buildErrorResult(job, outcome.error, startTime);
|
|
3757
|
+
return {
|
|
3758
|
+
job,
|
|
3759
|
+
success: true,
|
|
3760
|
+
operations: outcome.operationsWithContext.map((owc) => owc.operation),
|
|
3761
|
+
operationsWithContext: outcome.operationsWithContext,
|
|
3762
|
+
duration: Date.now() - startTime
|
|
3763
|
+
};
|
|
3398
3764
|
}
|
|
3399
3765
|
async executeLoadJob(executing) {
|
|
3400
3766
|
const { job, startTime, indexTxn, stores, signal } = executing;
|
|
@@ -4156,8 +4522,8 @@ function createForwardingPoolInstrumentation(name) {
|
|
|
4156
4522
|
}
|
|
4157
4523
|
//#endregion
|
|
4158
4524
|
//#region src/storage/migrations/001_create_operation_table.ts
|
|
4159
|
-
var _001_create_operation_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4160
|
-
async function up$
|
|
4525
|
+
var _001_create_operation_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$16 });
|
|
4526
|
+
async function up$16(db) {
|
|
4161
4527
|
await db.schema.createTable("Operation").addColumn("id", "serial", (col) => col.primaryKey()).addColumn("jobId", "text", (col) => col.notNull()).addColumn("opId", "text", (col) => col.notNull()).addColumn("prevOpId", "text", (col) => col.notNull()).addColumn("writeTimestampUtcMs", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("documentId", "text", (col) => col.notNull()).addColumn("documentType", "text", (col) => col.notNull()).addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("timestampUtcMs", "timestamptz", (col) => col.notNull()).addColumn("index", "integer", (col) => col.notNull()).addColumn("action", "jsonb", (col) => col.notNull()).addColumn("skip", "integer", (col) => col.notNull()).addColumn("error", "text").addColumn("hash", "text", (col) => col.notNull()).addUniqueConstraint("unique_revision", [
|
|
4162
4528
|
"documentId",
|
|
4163
4529
|
"scope",
|
|
@@ -4182,8 +4548,8 @@ async function up$15(db) {
|
|
|
4182
4548
|
}
|
|
4183
4549
|
//#endregion
|
|
4184
4550
|
//#region src/storage/migrations/002_create_keyframe_table.ts
|
|
4185
|
-
var _002_create_keyframe_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4186
|
-
async function up$
|
|
4551
|
+
var _002_create_keyframe_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$15 });
|
|
4552
|
+
async function up$15(db) {
|
|
4187
4553
|
await db.schema.createTable("Keyframe").addColumn("id", "serial", (col) => col.primaryKey()).addColumn("documentId", "text", (col) => col.notNull()).addColumn("documentType", "text", (col) => col.notNull()).addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("revision", "integer", (col) => col.notNull()).addColumn("document", "jsonb", (col) => col.notNull()).addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addUniqueConstraint("unique_keyframe", [
|
|
4188
4554
|
"documentId",
|
|
4189
4555
|
"scope",
|
|
@@ -4199,14 +4565,14 @@ async function up$14(db) {
|
|
|
4199
4565
|
}
|
|
4200
4566
|
//#endregion
|
|
4201
4567
|
//#region src/storage/migrations/003_create_document_table.ts
|
|
4202
|
-
var _003_create_document_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4203
|
-
async function up$
|
|
4568
|
+
var _003_create_document_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$14 });
|
|
4569
|
+
async function up$14(db) {
|
|
4204
4570
|
await db.schema.createTable("Document").addColumn("id", "text", (col) => col.primaryKey()).addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
|
|
4205
4571
|
}
|
|
4206
4572
|
//#endregion
|
|
4207
4573
|
//#region src/storage/migrations/004_create_document_relationship_table.ts
|
|
4208
|
-
var _004_create_document_relationship_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4209
|
-
async function up$
|
|
4574
|
+
var _004_create_document_relationship_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$13 });
|
|
4575
|
+
async function up$13(db) {
|
|
4210
4576
|
await db.schema.createTable("DocumentRelationship").addColumn("id", "text", (col) => col.primaryKey()).addColumn("sourceId", "text", (col) => col.notNull().references("Document.id").onDelete("cascade")).addColumn("targetId", "text", (col) => col.notNull().references("Document.id").onDelete("cascade")).addColumn("relationshipType", "text", (col) => col.notNull()).addColumn("metadata", "jsonb").addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addUniqueConstraint("unique_source_target_type", [
|
|
4211
4577
|
"sourceId",
|
|
4212
4578
|
"targetId",
|
|
@@ -4218,14 +4584,14 @@ async function up$12(db) {
|
|
|
4218
4584
|
}
|
|
4219
4585
|
//#endregion
|
|
4220
4586
|
//#region src/storage/migrations/005_create_indexer_state_table.ts
|
|
4221
|
-
var _005_create_indexer_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4222
|
-
async function up$
|
|
4587
|
+
var _005_create_indexer_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$12 });
|
|
4588
|
+
async function up$12(db) {
|
|
4223
4589
|
await db.schema.createTable("IndexerState").addColumn("id", "integer", (col) => col.primaryKey().generatedAlwaysAsIdentity()).addColumn("lastOperationId", "integer", (col) => col.notNull()).addColumn("lastOperationTimestamp", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
|
|
4224
4590
|
}
|
|
4225
4591
|
//#endregion
|
|
4226
4592
|
//#region src/storage/migrations/006_create_document_snapshot_table.ts
|
|
4227
|
-
var _006_create_document_snapshot_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4228
|
-
async function up$
|
|
4593
|
+
var _006_create_document_snapshot_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$11 });
|
|
4594
|
+
async function up$11(db) {
|
|
4229
4595
|
await db.schema.createTable("DocumentSnapshot").addColumn("id", "text", (col) => col.primaryKey()).addColumn("documentId", "text", (col) => col.notNull()).addColumn("slug", "text").addColumn("name", "text").addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("content", "jsonb", (col) => col.notNull()).addColumn("documentType", "text", (col) => col.notNull()).addColumn("lastOperationIndex", "integer", (col) => col.notNull()).addColumn("lastOperationHash", "text", (col) => col.notNull()).addColumn("lastUpdatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("snapshotVersion", "integer", (col) => col.notNull().defaultTo(1)).addColumn("identifiers", "jsonb").addColumn("metadata", "jsonb").addColumn("isDeleted", "boolean", (col) => col.notNull().defaultTo(false)).addColumn("deletedAt", "timestamptz").addUniqueConstraint("unique_doc_scope_branch", [
|
|
4230
4596
|
"documentId",
|
|
4231
4597
|
"scope",
|
|
@@ -4246,8 +4612,8 @@ async function up$10(db) {
|
|
|
4246
4612
|
}
|
|
4247
4613
|
//#endregion
|
|
4248
4614
|
//#region src/storage/migrations/007_create_slug_mapping_table.ts
|
|
4249
|
-
var _007_create_slug_mapping_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4250
|
-
async function up$
|
|
4615
|
+
var _007_create_slug_mapping_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$10 });
|
|
4616
|
+
async function up$10(db) {
|
|
4251
4617
|
await db.schema.createTable("SlugMapping").addColumn("slug", "text", (col) => col.primaryKey()).addColumn("documentId", "text", (col) => col.notNull()).addColumn("scope", "text", (col) => col.notNull()).addColumn("branch", "text", (col) => col.notNull()).addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addUniqueConstraint("unique_docid_scope_branch", [
|
|
4252
4618
|
"documentId",
|
|
4253
4619
|
"scope",
|
|
@@ -4257,14 +4623,14 @@ async function up$9(db) {
|
|
|
4257
4623
|
}
|
|
4258
4624
|
//#endregion
|
|
4259
4625
|
//#region src/storage/migrations/008_create_view_state_table.ts
|
|
4260
|
-
var _008_create_view_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4261
|
-
async function up$
|
|
4626
|
+
var _008_create_view_state_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$9 });
|
|
4627
|
+
async function up$9(db) {
|
|
4262
4628
|
await db.schema.createTable("ViewState").addColumn("readModelId", "text", (col) => col.primaryKey()).addColumn("lastOrdinal", "integer", (col) => col.notNull().defaultTo(0)).addColumn("lastOperationTimestamp", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
|
|
4263
4629
|
}
|
|
4264
4630
|
//#endregion
|
|
4265
4631
|
//#region src/storage/migrations/009_create_operation_index_tables.ts
|
|
4266
|
-
var _009_create_operation_index_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4267
|
-
async function up$
|
|
4632
|
+
var _009_create_operation_index_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$8 });
|
|
4633
|
+
async function up$8(db) {
|
|
4268
4634
|
await db.schema.createTable("document_collections").addColumn("documentId", "text", (col) => col.notNull()).addColumn("collectionId", "text", (col) => col.notNull()).addColumn("joinedOrdinal", "bigint", (col) => col.notNull().defaultTo(0)).addColumn("leftOrdinal", "bigint").addPrimaryKeyConstraint("document_collections_pkey", ["documentId", "collectionId"]).execute();
|
|
4269
4635
|
await db.schema.createIndex("idx_document_collections_collectionId").on("document_collections").column("collectionId").execute();
|
|
4270
4636
|
await db.schema.createIndex("idx_doc_collections_collection_range").on("document_collections").columns(["collectionId", "joinedOrdinal"]).execute();
|
|
@@ -4278,8 +4644,8 @@ async function up$7(db) {
|
|
|
4278
4644
|
}
|
|
4279
4645
|
//#endregion
|
|
4280
4646
|
//#region src/storage/migrations/010_create_sync_tables.ts
|
|
4281
|
-
var _010_create_sync_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4282
|
-
async function up$
|
|
4647
|
+
var _010_create_sync_tables_exports = /* @__PURE__ */ __exportAll({ up: () => up$7 });
|
|
4648
|
+
async function up$7(db) {
|
|
4283
4649
|
await db.schema.createTable("sync_remotes").addColumn("name", "text", (col) => col.primaryKey()).addColumn("collection_id", "text", (col) => col.notNull()).addColumn("channel_type", "text", (col) => col.notNull()).addColumn("channel_id", "text", (col) => col.notNull().defaultTo("")).addColumn("remote_name", "text", (col) => col.notNull().defaultTo("")).addColumn("channel_parameters", "jsonb", (col) => col.notNull().defaultTo(sql`'{}'::jsonb`)).addColumn("filter_document_ids", "jsonb").addColumn("filter_scopes", "jsonb").addColumn("filter_branch", "text", (col) => col.notNull().defaultTo("main")).addColumn("push_state", "text", (col) => col.notNull().defaultTo("idle")).addColumn("push_last_success_utc_ms", "text").addColumn("push_last_failure_utc_ms", "text").addColumn("push_failure_count", "integer", (col) => col.notNull().defaultTo(0)).addColumn("pull_state", "text", (col) => col.notNull().defaultTo("idle")).addColumn("pull_last_success_utc_ms", "text").addColumn("pull_last_failure_utc_ms", "text").addColumn("pull_failure_count", "integer", (col) => col.notNull().defaultTo(0)).addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
|
|
4284
4650
|
await db.schema.createIndex("idx_sync_remotes_collection").on("sync_remotes").column("collection_id").execute();
|
|
4285
4651
|
await db.schema.createTable("sync_cursors").addColumn("remote_name", "text", (col) => col.primaryKey().references("sync_remotes.name").onDelete("cascade")).addColumn("cursor_ordinal", "bigint", (col) => col.notNull().defaultTo(0)).addColumn("last_synced_at_utc_ms", "text").addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
|
|
@@ -4287,8 +4653,8 @@ async function up$6(db) {
|
|
|
4287
4653
|
}
|
|
4288
4654
|
//#endregion
|
|
4289
4655
|
//#region src/storage/migrations/011_add_cursor_type_column.ts
|
|
4290
|
-
var _011_add_cursor_type_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4291
|
-
async function up$
|
|
4656
|
+
var _011_add_cursor_type_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$6 });
|
|
4657
|
+
async function up$6(db) {
|
|
4292
4658
|
await db.deleteFrom("sync_cursors").where("remote_name", "like", "outbox::%").execute();
|
|
4293
4659
|
await db.deleteFrom("sync_remotes").where("name", "like", "outbox::%").execute();
|
|
4294
4660
|
await db.schema.dropTable("sync_cursors").execute();
|
|
@@ -4297,60 +4663,82 @@ async function up$5(db) {
|
|
|
4297
4663
|
}
|
|
4298
4664
|
//#endregion
|
|
4299
4665
|
//#region src/storage/migrations/012_add_source_remote_column.ts
|
|
4300
|
-
var _012_add_source_remote_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4301
|
-
async function up$
|
|
4666
|
+
var _012_add_source_remote_column_exports = /* @__PURE__ */ __exportAll({ up: () => up$5 });
|
|
4667
|
+
async function up$5(db) {
|
|
4302
4668
|
await db.schema.alterTable("operation_index_operations").addColumn("sourceRemote", "text", (col) => col.notNull().defaultTo("")).execute();
|
|
4303
4669
|
}
|
|
4304
4670
|
//#endregion
|
|
4305
4671
|
//#region src/storage/migrations/013_create_sync_dead_letters_table.ts
|
|
4306
|
-
var _013_create_sync_dead_letters_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4307
|
-
async function up$
|
|
4672
|
+
var _013_create_sync_dead_letters_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$4 });
|
|
4673
|
+
async function up$4(db) {
|
|
4308
4674
|
await db.schema.createTable("sync_dead_letters").addColumn("ordinal", "serial", (col) => col.primaryKey()).addColumn("id", "text", (col) => col.unique().notNull()).addColumn("job_id", "text", (col) => col.notNull()).addColumn("job_dependencies", "jsonb", (col) => col.notNull().defaultTo(sql`'[]'::jsonb`)).addColumn("remote_name", "text", (col) => col.notNull().references("sync_remotes.name").onDelete("cascade")).addColumn("document_id", "text", (col) => col.notNull()).addColumn("scopes", "jsonb", (col) => col.notNull().defaultTo(sql`'[]'::jsonb`)).addColumn("branch", "text", (col) => col.notNull()).addColumn("operations", "jsonb", (col) => col.notNull().defaultTo(sql`'[]'::jsonb`)).addColumn("error_source", "text", (col) => col.notNull()).addColumn("error_message", "text", (col) => col.notNull()).addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
|
|
4309
4675
|
await db.schema.createIndex("idx_sync_dead_letters_remote").on("sync_dead_letters").column("remote_name").execute();
|
|
4310
4676
|
}
|
|
4311
4677
|
//#endregion
|
|
4312
4678
|
//#region src/storage/migrations/014_create_processor_cursor_table.ts
|
|
4313
|
-
var _014_create_processor_cursor_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$
|
|
4314
|
-
async function up$
|
|
4679
|
+
var _014_create_processor_cursor_table_exports = /* @__PURE__ */ __exportAll({ up: () => up$3 });
|
|
4680
|
+
async function up$3(db) {
|
|
4315
4681
|
await db.schema.createTable("ProcessorCursor").addColumn("processorId", "text", (col) => col.primaryKey()).addColumn("factoryId", "text", (col) => col.notNull()).addColumn("driveId", "text", (col) => col.notNull()).addColumn("processorIndex", "integer", (col) => col.notNull()).addColumn("lastOrdinal", "integer", (col) => col.notNull().defaultTo(sql`0`)).addColumn("status", "text", (col) => col.notNull().defaultTo(sql`'active'`)).addColumn("lastError", "text").addColumn("lastErrorTimestamp", "timestamptz").addColumn("createdAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).addColumn("updatedAt", "timestamptz", (col) => col.notNull().defaultTo(sql`NOW()`)).execute();
|
|
4316
4682
|
}
|
|
4317
4683
|
//#endregion
|
|
4318
4684
|
//#region src/storage/migrations/015_add_operation_denied_reason.ts
|
|
4319
4685
|
var _015_add_operation_denied_reason_exports = /* @__PURE__ */ __exportAll({
|
|
4320
|
-
down: () => down$
|
|
4321
|
-
up: () => up$
|
|
4686
|
+
down: () => down$2,
|
|
4687
|
+
up: () => up$2
|
|
4322
4688
|
});
|
|
4323
4689
|
/**
|
|
4324
4690
|
* Records why authorization refused an operation. Separate from `error` so a
|
|
4325
4691
|
* denial is distinguishable from a reducer failure without matching on a
|
|
4326
4692
|
* message. Null for every operation written before decisions were enforced.
|
|
4327
4693
|
*/
|
|
4328
|
-
async function up$
|
|
4694
|
+
async function up$2(db) {
|
|
4329
4695
|
await db.schema.alterTable("Operation").addColumn("deniedReason", "text").execute();
|
|
4330
4696
|
await db.schema.alterTable("operation_index_operations").addColumn("deniedReason", "text").execute();
|
|
4331
4697
|
}
|
|
4332
|
-
async function down$
|
|
4698
|
+
async function down$2(db) {
|
|
4333
4699
|
await db.schema.alterTable("operation_index_operations").dropColumn("deniedReason").execute();
|
|
4334
4700
|
await db.schema.alterTable("Operation").dropColumn("deniedReason").execute();
|
|
4335
4701
|
}
|
|
4336
4702
|
//#endregion
|
|
4337
4703
|
//#region src/storage/migrations/016_add_dead_letter_error_type.ts
|
|
4338
4704
|
var _016_add_dead_letter_error_type_exports = /* @__PURE__ */ __exportAll({
|
|
4339
|
-
down: () => down,
|
|
4340
|
-
up: () => up
|
|
4705
|
+
down: () => down$1,
|
|
4706
|
+
up: () => up$1
|
|
4341
4707
|
});
|
|
4342
4708
|
/**
|
|
4343
4709
|
* The classification a dead letter falls into, stored because it decides whether
|
|
4344
4710
|
* the document stays quarantined and the in-memory error is gone after a restart.
|
|
4345
4711
|
* Defaulted rather than nullable, so a pre-existing row rehydrates.
|
|
4346
4712
|
*/
|
|
4347
|
-
async function up(db) {
|
|
4713
|
+
async function up$1(db) {
|
|
4348
4714
|
await db.schema.alterTable("sync_dead_letters").addColumn("error_type", "text", (col) => col.notNull().defaultTo("UNCLASSIFIED")).execute();
|
|
4349
4715
|
}
|
|
4350
|
-
async function down(db) {
|
|
4716
|
+
async function down$1(db) {
|
|
4351
4717
|
await db.schema.alterTable("sync_dead_letters").dropColumn("error_type").execute();
|
|
4352
4718
|
}
|
|
4353
4719
|
//#endregion
|
|
4720
|
+
//#region src/storage/migrations/017_create_group_references.ts
|
|
4721
|
+
var _017_create_group_references_exports = /* @__PURE__ */ __exportAll({
|
|
4722
|
+
down: () => down,
|
|
4723
|
+
up: () => up
|
|
4724
|
+
});
|
|
4725
|
+
/**
|
|
4726
|
+
* One row per (document, group) reference ever discovered from an auth
|
|
4727
|
+
* operation's input. Rows are never updated or deleted: auth evaluation is
|
|
4728
|
+
* positional, so a grant that named a group at any position keeps that
|
|
4729
|
+
* group's stream in the document's read-set even after a later operation
|
|
4730
|
+
* removes the reference. Read by documentId for the groups a document
|
|
4731
|
+
* requires (sync), and by groupId for the documents a group change affects
|
|
4732
|
+
* (re-evaluation).
|
|
4733
|
+
*/
|
|
4734
|
+
async function up(db) {
|
|
4735
|
+
await db.schema.createTable("group_references").addColumn("documentId", "text", (col) => col.notNull()).addColumn("groupId", "text", (col) => col.notNull()).addPrimaryKeyConstraint("group_references_pkey", ["documentId", "groupId"]).execute();
|
|
4736
|
+
await db.schema.createIndex("idx_group_references_groupId").on("group_references").column("groupId").execute();
|
|
4737
|
+
}
|
|
4738
|
+
async function down(db) {
|
|
4739
|
+
await db.schema.dropTable("group_references").execute();
|
|
4740
|
+
}
|
|
4741
|
+
//#endregion
|
|
4354
4742
|
//#region src/storage/migrations/migrator.ts
|
|
4355
4743
|
const REACTOR_SCHEMA = "reactor";
|
|
4356
4744
|
const migrations = {
|
|
@@ -4369,7 +4757,8 @@ const migrations = {
|
|
|
4369
4757
|
"013_create_sync_dead_letters_table": _013_create_sync_dead_letters_table_exports,
|
|
4370
4758
|
"014_create_processor_cursor_table": _014_create_processor_cursor_table_exports,
|
|
4371
4759
|
"015_add_operation_denied_reason": _015_add_operation_denied_reason_exports,
|
|
4372
|
-
"016_add_dead_letter_error_type": _016_add_dead_letter_error_type_exports
|
|
4760
|
+
"016_add_dead_letter_error_type": _016_add_dead_letter_error_type_exports,
|
|
4761
|
+
"017_create_group_references": _017_create_group_references_exports
|
|
4373
4762
|
};
|
|
4374
4763
|
var ProgrammaticMigrationProvider = class {
|
|
4375
4764
|
getMigrations() {
|
|
@@ -4425,4 +4814,4 @@ const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "pow
|
|
|
4425
4814
|
//#endregion
|
|
4426
4815
|
export { OptimisticLockError as A, ExcessiveReshuffleError as B, DocumentMetaCache as C, APPEND_CONDITION_FAILED_PREFIX as D, CollectionMembershipCache as E, ModuleNotFoundError as F, throwIfAborted as G, UpgradePreconditionFailedError as H, AuthTimestampNotMonotonicError as I, __exportAll as K, AuthorizationDeniedError as L, DuplicateManifestError as M, DuplicateModuleError as N, AppendConditionFailedError as O, InvalidModuleError as P, DocumentDeletedError as R, KyselyOperationIndex as S, createEmptyConsistencyToken as T, matchesScope as U, InvalidOperationTimestampError as V, parsePagingOptions as W, KyselyExecutionScope as _, createForwardingPoolInstrumentation as a, EventBus as b, KyselyKeyframeStore as c, DriveCollectionId as d, decideAtHead as f, authDecisionModel as g, buildDecisionModel as h, runMigrations as i, RevisionMismatchError as j, DuplicateOperationError as k, DocumentModelRegistry as l, documentDecisionModel as m, REACTOR_SCHEMA as n, instrumentPgPool as o, selectDecisionModel as p, getMigrationStatus as r, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, SimpleJobExecutor as u, FLAG_PREREQUISITES as v, createConsistencyToken as w, KyselyWriteCache as x, validateFeatureFlags as y, DocumentNotFoundError as z };
|
|
4427
4816
|
|
|
4428
|
-
//# sourceMappingURL=drive-container-types-
|
|
4817
|
+
//# sourceMappingURL=drive-container-types-h3M1AK3K.js.map
|