@powerhousedao/reactor 6.0.0-dev.253 → 6.0.0-dev.254
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/index.d.ts +85 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +253 -144
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -55,13 +55,18 @@ function deleteDocumentAction(documentId) {
|
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
/**
|
|
58
|
-
* Creates an ADD_RELATIONSHIP action
|
|
58
|
+
* Creates an ADD_RELATIONSHIP action that records a directed edge from
|
|
59
|
+
* `sourceId` to `targetId` with an arbitrary `relationshipType` and optional
|
|
60
|
+
* `metadata`. The edge is opaque to the reactor — consumers (e.g. reactor-drive)
|
|
61
|
+
* define their own type strings such as `"drive/child"` and attach
|
|
62
|
+
* domain-specific metadata.
|
|
59
63
|
*/
|
|
60
|
-
function addRelationshipAction(sourceId, targetId, relationshipType
|
|
64
|
+
function addRelationshipAction(sourceId, targetId, relationshipType, metadata) {
|
|
61
65
|
const input = {
|
|
62
66
|
sourceId,
|
|
63
67
|
targetId,
|
|
64
|
-
relationshipType
|
|
68
|
+
relationshipType,
|
|
69
|
+
...metadata !== void 0 ? { metadata } : {}
|
|
65
70
|
};
|
|
66
71
|
return {
|
|
67
72
|
id: generateId(),
|
|
@@ -72,6 +77,25 @@ function addRelationshipAction(sourceId, targetId, relationshipType = "child") {
|
|
|
72
77
|
};
|
|
73
78
|
}
|
|
74
79
|
/**
|
|
80
|
+
* Creates an UPDATE_RELATIONSHIP action to replace a relationship's metadata
|
|
81
|
+
* without losing its createdAt ordering.
|
|
82
|
+
*/
|
|
83
|
+
function updateRelationshipAction(sourceId, targetId, relationshipType, metadata) {
|
|
84
|
+
const input = {
|
|
85
|
+
sourceId,
|
|
86
|
+
targetId,
|
|
87
|
+
relationshipType,
|
|
88
|
+
metadata
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
id: generateId(),
|
|
92
|
+
type: "UPDATE_RELATIONSHIP",
|
|
93
|
+
scope: "document",
|
|
94
|
+
timestampUtcMs: (/* @__PURE__ */ new Date()).toISOString(),
|
|
95
|
+
input
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
75
99
|
* Creates a REMOVE_RELATIONSHIP action to remove a parent-child relationship.
|
|
76
100
|
*/
|
|
77
101
|
function removeRelationshipAction(sourceId, targetId, relationshipType = "child") {
|
|
@@ -291,6 +315,44 @@ let JobStatus = /* @__PURE__ */ function(JobStatus) {
|
|
|
291
315
|
return JobStatus;
|
|
292
316
|
}({});
|
|
293
317
|
//#endregion
|
|
318
|
+
//#region src/shared/utils.ts
|
|
319
|
+
function matchesScope(view = {}, scope) {
|
|
320
|
+
if (view.scopes) return view.scopes.includes(scope);
|
|
321
|
+
return true;
|
|
322
|
+
}
|
|
323
|
+
function yieldToMain() {
|
|
324
|
+
const s = globalThis.scheduler;
|
|
325
|
+
if (s?.yield) return s.yield();
|
|
326
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
327
|
+
}
|
|
328
|
+
const defaultAbortError = () => /* @__PURE__ */ new Error("Operation aborted");
|
|
329
|
+
function throwIfAborted(signal, makeError = defaultAbortError) {
|
|
330
|
+
if (signal?.aborted) throw makeError();
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Validates PagingOptions and returns a normalized offset and limit.
|
|
334
|
+
* Throws if the cursor is not empty and not a non-negative integer, or if
|
|
335
|
+
* limit is less than 1. When `paging` is undefined, returns offset 0 and
|
|
336
|
+
* the caller-supplied `defaultLimit`.
|
|
337
|
+
*/
|
|
338
|
+
function parsePagingOptions(paging, defaultLimit) {
|
|
339
|
+
if (paging === void 0) return {
|
|
340
|
+
offset: 0,
|
|
341
|
+
limit: defaultLimit
|
|
342
|
+
};
|
|
343
|
+
if (!Number.isInteger(paging.limit) || paging.limit < 1) throw new Error(`Invalid paging limit: ${String(paging.limit)} (must be an integer >= 1)`);
|
|
344
|
+
if (paging.cursor === "") return {
|
|
345
|
+
offset: 0,
|
|
346
|
+
limit: paging.limit
|
|
347
|
+
};
|
|
348
|
+
const parsed = Number(paging.cursor);
|
|
349
|
+
if (!Number.isInteger(parsed) || parsed < 0) throw new Error(`Invalid paging cursor: ${JSON.stringify(paging.cursor)} (must be empty or a non-negative integer)`);
|
|
350
|
+
return {
|
|
351
|
+
offset: parsed,
|
|
352
|
+
limit: paging.limit
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
//#endregion
|
|
294
356
|
//#region src/client/drive-client.ts
|
|
295
357
|
/**
|
|
296
358
|
* Implementation of {@link IDriveClient}.
|
|
@@ -460,11 +522,22 @@ var DriveClient = class {
|
|
|
460
522
|
if (!node) throw new Error(`Node ${nodeId} not found in drive ${driveIdentifier}`);
|
|
461
523
|
return node;
|
|
462
524
|
}
|
|
463
|
-
async listNodes(driveIdentifier, parentFolder, signal) {
|
|
464
|
-
this.logger.verbose("drives.listNodes(@driveIdentifier, @parentFolder)", driveIdentifier, parentFolder);
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
525
|
+
async listNodes(driveIdentifier, parentFolder, paging, signal) {
|
|
526
|
+
this.logger.verbose("drives.listNodes(@driveIdentifier, @parentFolder, @paging)", driveIdentifier, parentFolder, paging);
|
|
527
|
+
const allNodes = (await this.client.get(driveIdentifier, void 0, signal)).state.global.nodes;
|
|
528
|
+
const filtered = parentFolder === void 0 ? [...allNodes] : allNodes.filter((n) => (n.parentFolder ?? null) === parentFolder);
|
|
529
|
+
const { offset: startIndex, limit } = parsePagingOptions(paging, filtered.length);
|
|
530
|
+
const effective = paging ?? {
|
|
531
|
+
cursor: "",
|
|
532
|
+
limit
|
|
533
|
+
};
|
|
534
|
+
const endIndex = startIndex + limit;
|
|
535
|
+
return {
|
|
536
|
+
results: filtered.slice(startIndex, endIndex),
|
|
537
|
+
options: effective,
|
|
538
|
+
...endIndex < filtered.length ? { nextCursor: String(endIndex) } : {},
|
|
539
|
+
totalCount: filtered.length
|
|
540
|
+
};
|
|
468
541
|
}
|
|
469
542
|
async removeFileNode(driveId, fileId, signal) {
|
|
470
543
|
const relationshipActions = await signActions([removeRelationshipAction(driveId, fileId, "child")], this.signer, signal);
|
|
@@ -924,6 +997,17 @@ var ReactorClient = class {
|
|
|
924
997
|
const signedActions = await signActions(actions, this.signer, signal);
|
|
925
998
|
return this.reactor.execute(documentIdentifier, branch, signedActions, signal);
|
|
926
999
|
}
|
|
1000
|
+
async executeBatch(request, signal) {
|
|
1001
|
+
this.logger.verbose("executeBatch(@count jobs)", request.jobs.length);
|
|
1002
|
+
const signedJobs = await Promise.all(request.jobs.map(async (job) => ({
|
|
1003
|
+
...job,
|
|
1004
|
+
actions: await signActions(job.actions, this.signer, signal)
|
|
1005
|
+
})));
|
|
1006
|
+
const batchResult = await this.reactor.executeBatch({ jobs: signedJobs }, signal);
|
|
1007
|
+
const completedJobs = await Promise.all(Object.values(batchResult.jobs).map((job) => this.waitForJob(job, signal)));
|
|
1008
|
+
for (const job of completedJobs) if (job.status === JobStatus.FAILED) throw new Error(job.error?.message);
|
|
1009
|
+
return batchResult;
|
|
1010
|
+
}
|
|
927
1011
|
/**
|
|
928
1012
|
* Renames a document and waits for completion
|
|
929
1013
|
*/
|
|
@@ -2162,7 +2246,6 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
2162
2246
|
startRevision = keyframe.revision;
|
|
2163
2247
|
documentType = keyframe.document.header.documentType;
|
|
2164
2248
|
} else {
|
|
2165
|
-
document = void 0;
|
|
2166
2249
|
startRevision = -1;
|
|
2167
2250
|
const createOpResult = await this.operationStore.getSince(documentId, "document", branch, -1, void 0, {
|
|
2168
2251
|
cursor: "0",
|
|
@@ -2722,28 +2805,17 @@ var SimpleJobExecutorManager = class {
|
|
|
2722
2805
|
}
|
|
2723
2806
|
};
|
|
2724
2807
|
//#endregion
|
|
2725
|
-
//#region src/shared/utils.ts
|
|
2726
|
-
function matchesScope(view = {}, scope) {
|
|
2727
|
-
if (view.scopes) return view.scopes.includes(scope);
|
|
2728
|
-
return true;
|
|
2729
|
-
}
|
|
2730
|
-
function yieldToMain() {
|
|
2731
|
-
const s = globalThis.scheduler;
|
|
2732
|
-
if (s?.yield) return s.yield();
|
|
2733
|
-
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
2734
|
-
}
|
|
2735
|
-
const defaultAbortError = () => /* @__PURE__ */ new Error("Operation aborted");
|
|
2736
|
-
function throwIfAborted(signal, makeError = defaultAbortError) {
|
|
2737
|
-
if (signal?.aborted) throw makeError();
|
|
2738
|
-
}
|
|
2739
|
-
//#endregion
|
|
2740
2808
|
//#region src/utils/reshuffle.ts
|
|
2741
2809
|
const STRICT_ORDER_ACTION_TYPES = new Set([
|
|
2742
2810
|
"CREATE_DOCUMENT",
|
|
2743
2811
|
"DELETE_DOCUMENT",
|
|
2744
2812
|
"UPGRADE_DOCUMENT",
|
|
2745
2813
|
"ADD_RELATIONSHIP",
|
|
2746
|
-
"REMOVE_RELATIONSHIP"
|
|
2814
|
+
"REMOVE_RELATIONSHIP",
|
|
2815
|
+
"UPDATE_RELATIONSHIP",
|
|
2816
|
+
"ADD_FOLDER",
|
|
2817
|
+
"UPDATE_FOLDER",
|
|
2818
|
+
"REMOVE_FOLDER"
|
|
2747
2819
|
]);
|
|
2748
2820
|
/**
|
|
2749
2821
|
* Reshuffles operations by timestamp, then applies deterministic tie-breaking.
|
|
@@ -2790,9 +2862,10 @@ function driveCollectionId(branch, driveId) {
|
|
|
2790
2862
|
//#endregion
|
|
2791
2863
|
//#region src/executor/document-action-handler.ts
|
|
2792
2864
|
var DocumentActionHandler = class {
|
|
2793
|
-
constructor(registry, logger) {
|
|
2865
|
+
constructor(registry, logger, driveContainerTypes) {
|
|
2794
2866
|
this.registry = registry;
|
|
2795
2867
|
this.logger = logger;
|
|
2868
|
+
this.driveContainerTypes = driveContainerTypes;
|
|
2796
2869
|
}
|
|
2797
2870
|
async execute(job, action, startTime, indexTxn, stores, skip = 0, sourceRemote = "", signal) {
|
|
2798
2871
|
switch (action.type) {
|
|
@@ -2801,6 +2874,7 @@ var DocumentActionHandler = class {
|
|
|
2801
2874
|
case "UPGRADE_DOCUMENT": return this.executeUpgrade(job, action, startTime, indexTxn, stores, skip, sourceRemote, signal);
|
|
2802
2875
|
case "ADD_RELATIONSHIP": return this.executeAddRelationship(job, action, startTime, indexTxn, stores, sourceRemote, signal);
|
|
2803
2876
|
case "REMOVE_RELATIONSHIP": return this.executeRemoveRelationship(job, action, startTime, indexTxn, stores, sourceRemote, signal);
|
|
2877
|
+
case "UPDATE_RELATIONSHIP": return this.executeUpdateRelationship(job, action, startTime, indexTxn, stores, sourceRemote, signal);
|
|
2804
2878
|
default: return buildErrorResult(job, /* @__PURE__ */ new Error(`Unknown document action type: ${action.type}`), startTime);
|
|
2805
2879
|
}
|
|
2806
2880
|
}
|
|
@@ -2825,6 +2899,10 @@ var DocumentActionHandler = class {
|
|
|
2825
2899
|
const writeError = await this.writeOperationToStore(document.header.id, document.header.documentType, job.scope, job.branch, operation, job, startTime, stores, signal);
|
|
2826
2900
|
if (writeError !== null) return writeError;
|
|
2827
2901
|
updateDocumentRevision(document, job.scope, operation.index);
|
|
2902
|
+
document.operations = {
|
|
2903
|
+
...document.operations,
|
|
2904
|
+
[job.scope]: [...document.operations[job.scope] ?? [], operation]
|
|
2905
|
+
};
|
|
2828
2906
|
stores.writeCache.putState(document.header.id, job.scope, job.branch, operation.index, document);
|
|
2829
2907
|
indexTxn.write([{
|
|
2830
2908
|
...operation,
|
|
@@ -2834,7 +2912,7 @@ var DocumentActionHandler = class {
|
|
|
2834
2912
|
scope: job.scope,
|
|
2835
2913
|
sourceRemote
|
|
2836
2914
|
}]);
|
|
2837
|
-
if (document.header.documentType
|
|
2915
|
+
if (this.driveContainerTypes.has(document.header.documentType)) {
|
|
2838
2916
|
const collectionId = driveCollectionId(job.branch, document.header.id);
|
|
2839
2917
|
indexTxn.createCollection(collectionId);
|
|
2840
2918
|
indexTxn.addToCollection(collectionId, document.header.id);
|
|
@@ -2872,6 +2950,10 @@ var DocumentActionHandler = class {
|
|
|
2872
2950
|
const writeError = await this.writeOperationToStore(documentId, document.header.documentType, job.scope, job.branch, operation, job, startTime, stores, signal);
|
|
2873
2951
|
if (writeError !== null) return writeError;
|
|
2874
2952
|
updateDocumentRevision(document, job.scope, operation.index);
|
|
2953
|
+
document.operations = {
|
|
2954
|
+
...document.operations,
|
|
2955
|
+
[job.scope]: [...document.operations[job.scope] ?? [], operation]
|
|
2956
|
+
};
|
|
2875
2957
|
stores.writeCache.putState(documentId, job.scope, job.branch, operation.index, document);
|
|
2876
2958
|
indexTxn.write([{
|
|
2877
2959
|
...operation,
|
|
@@ -2934,6 +3016,10 @@ var DocumentActionHandler = class {
|
|
|
2934
3016
|
const writeError = await this.writeOperationToStore(documentId, document.header.documentType, job.scope, job.branch, operation, job, startTime, stores, signal);
|
|
2935
3017
|
if (writeError !== null) return writeError;
|
|
2936
3018
|
updateDocumentRevision(document, job.scope, operation.index);
|
|
3019
|
+
document.operations = {
|
|
3020
|
+
...document.operations,
|
|
3021
|
+
[job.scope]: [...document.operations[job.scope] ?? [], operation]
|
|
3022
|
+
};
|
|
2937
3023
|
stores.writeCache.putState(documentId, job.scope, job.branch, operation.index, document);
|
|
2938
3024
|
indexTxn.write([{
|
|
2939
3025
|
...operation,
|
|
@@ -2950,66 +3036,40 @@ var DocumentActionHandler = class {
|
|
|
2950
3036
|
});
|
|
2951
3037
|
return buildSuccessResult(job, operation, documentId, document.header.documentType, resultingState, startTime);
|
|
2952
3038
|
}
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
sourceDoc = await stores.writeCache.getState(input.sourceId, "document", job.branch, void 0, signal);
|
|
2961
|
-
} catch (error) {
|
|
2962
|
-
return buildErrorResult(job, /* @__PURE__ */ new Error(`ADD_RELATIONSHIP: source document ${input.sourceId} not found: ${error instanceof Error ? error.message : String(error)}`), startTime);
|
|
2963
|
-
}
|
|
2964
|
-
const operation = createOperation(action, getNextIndexForScope(sourceDoc, job.scope), 0, {
|
|
2965
|
-
documentId: input.sourceId,
|
|
2966
|
-
scope: job.scope,
|
|
2967
|
-
branch: job.branch
|
|
3039
|
+
executeAddRelationship(job, action, startTime, indexTxn, stores, sourceRemote = "", signal) {
|
|
3040
|
+
return this.withRelationshipAction("ADD_RELATIONSHIP", job, action, startTime, indexTxn, stores, sourceRemote, signal, (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 }) => {
|
|
3041
|
+
if (this.driveContainerTypes.has(sourceDoc.header.documentType)) {
|
|
3042
|
+
const collectionId = driveCollectionId(j.branch, input.sourceId);
|
|
3043
|
+
txn.addToCollection(collectionId, input.targetId);
|
|
3044
|
+
s.collectionMembershipCache.invalidate(input.targetId);
|
|
3045
|
+
}
|
|
2968
3046
|
});
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
2977
|
-
const scopeState = sourceDoc.state[job.scope];
|
|
2978
|
-
const resultingStateObj = {
|
|
2979
|
-
header: structuredClone(sourceDoc.header),
|
|
2980
|
-
[job.scope]: scopeState === void 0 ? {} : structuredClone(scopeState)
|
|
2981
|
-
};
|
|
2982
|
-
const resultingState = JSON.stringify(resultingStateObj);
|
|
2983
|
-
stores.writeCache.putState(input.sourceId, job.scope, job.branch, operation.index, sourceDoc);
|
|
2984
|
-
indexTxn.write([{
|
|
2985
|
-
...operation,
|
|
2986
|
-
documentId: input.sourceId,
|
|
2987
|
-
documentType: sourceDoc.header.documentType,
|
|
2988
|
-
branch: job.branch,
|
|
2989
|
-
scope: job.scope,
|
|
2990
|
-
sourceRemote
|
|
2991
|
-
}]);
|
|
2992
|
-
if (sourceDoc.header.documentType === "powerhouse/document-drive") {
|
|
2993
|
-
const collectionId = driveCollectionId(job.branch, input.sourceId);
|
|
2994
|
-
indexTxn.addToCollection(collectionId, input.targetId);
|
|
2995
|
-
stores.collectionMembershipCache.invalidate(input.targetId);
|
|
2996
|
-
}
|
|
2997
|
-
stores.documentMetaCache.putDocumentMeta(input.sourceId, job.branch, {
|
|
2998
|
-
state: sourceDoc.state.document,
|
|
2999
|
-
documentType: sourceDoc.header.documentType,
|
|
3000
|
-
documentScopeRevision: operation.index + 1
|
|
3047
|
+
}
|
|
3048
|
+
executeRemoveRelationship(job, action, startTime, indexTxn, stores, sourceRemote = "", signal) {
|
|
3049
|
+
return this.withRelationshipAction("REMOVE_RELATIONSHIP", job, action, startTime, indexTxn, stores, sourceRemote, signal, null, ({ indexTxn: txn, stores: s, sourceDoc, input, job: j }) => {
|
|
3050
|
+
if (this.driveContainerTypes.has(sourceDoc.header.documentType)) {
|
|
3051
|
+
const collectionId = driveCollectionId(j.branch, input.sourceId);
|
|
3052
|
+
txn.removeFromCollection(collectionId, input.targetId);
|
|
3053
|
+
s.collectionMembershipCache.invalidate(input.targetId);
|
|
3054
|
+
}
|
|
3001
3055
|
});
|
|
3002
|
-
return buildSuccessResult(job, operation, input.sourceId, sourceDoc.header.documentType, resultingState, startTime);
|
|
3003
3056
|
}
|
|
3004
|
-
|
|
3005
|
-
|
|
3057
|
+
executeUpdateRelationship(job, action, startTime, indexTxn, stores, sourceRemote = "", signal) {
|
|
3058
|
+
return this.withRelationshipAction("UPDATE_RELATIONSHIP", job, action, startTime, indexTxn, stores, sourceRemote, signal, null, null);
|
|
3059
|
+
}
|
|
3060
|
+
async withRelationshipAction(actionTypeName, job, action, startTime, indexTxn, stores, sourceRemote, signal, preValidate, postWrite) {
|
|
3061
|
+
if (job.scope !== "document") return buildErrorResult(job, /* @__PURE__ */ new Error(`${actionTypeName} must be in "document" scope, got "${job.scope}"`), startTime);
|
|
3006
3062
|
const input = action.input;
|
|
3007
|
-
if (!input.sourceId || !input.targetId || !input.relationshipType) return buildErrorResult(job, /* @__PURE__ */ new Error(
|
|
3063
|
+
if (!input.sourceId || !input.targetId || !input.relationshipType) return buildErrorResult(job, /* @__PURE__ */ new Error(`${actionTypeName} action requires sourceId, targetId, and relationshipType in input`), startTime);
|
|
3064
|
+
if (preValidate !== null) {
|
|
3065
|
+
const validationError = preValidate(input);
|
|
3066
|
+
if (validationError !== null) return buildErrorResult(job, validationError, startTime);
|
|
3067
|
+
}
|
|
3008
3068
|
let sourceDoc;
|
|
3009
3069
|
try {
|
|
3010
3070
|
sourceDoc = await stores.writeCache.getState(input.sourceId, "document", job.branch, void 0, signal);
|
|
3011
3071
|
} catch (error) {
|
|
3012
|
-
return buildErrorResult(job, /* @__PURE__ */ new Error(
|
|
3072
|
+
return buildErrorResult(job, /* @__PURE__ */ new Error(`${actionTypeName}: source document ${input.sourceId} not found: ${error instanceof Error ? error.message : String(error)}`), startTime);
|
|
3013
3073
|
}
|
|
3014
3074
|
const operation = createOperation(action, getNextIndexForScope(sourceDoc, job.scope), 0, {
|
|
3015
3075
|
documentId: input.sourceId,
|
|
@@ -3039,11 +3099,13 @@ var DocumentActionHandler = class {
|
|
|
3039
3099
|
scope: job.scope,
|
|
3040
3100
|
sourceRemote
|
|
3041
3101
|
}]);
|
|
3042
|
-
if (
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3102
|
+
if (postWrite !== null) postWrite({
|
|
3103
|
+
indexTxn,
|
|
3104
|
+
stores,
|
|
3105
|
+
sourceDoc,
|
|
3106
|
+
input,
|
|
3107
|
+
job
|
|
3108
|
+
});
|
|
3047
3109
|
stores.documentMetaCache.putDocumentMeta(input.sourceId, job.branch, {
|
|
3048
3110
|
state: sourceDoc.state.document,
|
|
3049
3111
|
documentType: sourceDoc.header.documentType,
|
|
@@ -3082,7 +3144,7 @@ var SignatureVerifier = class {
|
|
|
3082
3144
|
if (!signer) continue;
|
|
3083
3145
|
if (signer.signatures.length === 0) throw new InvalidSignatureError(documentId, `Action ${action.id} has signer but no signatures`);
|
|
3084
3146
|
const publicKey = signer.app.key;
|
|
3085
|
-
let isValid
|
|
3147
|
+
let isValid;
|
|
3086
3148
|
try {
|
|
3087
3149
|
const tempOperation = {
|
|
3088
3150
|
id: deriveOperationId(documentId, action.scope, branch, action.id),
|
|
@@ -3108,7 +3170,7 @@ var SignatureVerifier = class {
|
|
|
3108
3170
|
if (!signer) continue;
|
|
3109
3171
|
if (signer.signatures.length === 0) throw new InvalidSignatureError(documentId, `Operation ${operation.id} at index ${operation.index} has signer but no signatures`);
|
|
3110
3172
|
const publicKey = signer.app.key;
|
|
3111
|
-
let isValid
|
|
3173
|
+
let isValid;
|
|
3112
3174
|
try {
|
|
3113
3175
|
isValid = await this.verifier(operation, publicKey);
|
|
3114
3176
|
} catch (error) {
|
|
@@ -3132,7 +3194,8 @@ const documentScopeActions = [
|
|
|
3132
3194
|
"DELETE_DOCUMENT",
|
|
3133
3195
|
"UPGRADE_DOCUMENT",
|
|
3134
3196
|
"ADD_RELATIONSHIP",
|
|
3135
|
-
"REMOVE_RELATIONSHIP"
|
|
3197
|
+
"REMOVE_RELATIONSHIP",
|
|
3198
|
+
"UPDATE_RELATIONSHIP"
|
|
3136
3199
|
];
|
|
3137
3200
|
/**
|
|
3138
3201
|
* Simple job executor that processes a job by applying actions through document model reducers.
|
|
@@ -3142,7 +3205,7 @@ var SimpleJobExecutor = class {
|
|
|
3142
3205
|
signatureVerifierModule;
|
|
3143
3206
|
documentActionHandler;
|
|
3144
3207
|
executionScope;
|
|
3145
|
-
constructor(logger, registry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, config, signatureVerifier, executionScope) {
|
|
3208
|
+
constructor(logger, registry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, driveContainerTypes, config, signatureVerifier, executionScope) {
|
|
3146
3209
|
this.logger = logger;
|
|
3147
3210
|
this.registry = registry;
|
|
3148
3211
|
this.operationStore = operationStore;
|
|
@@ -3151,6 +3214,7 @@ var SimpleJobExecutor = class {
|
|
|
3151
3214
|
this.operationIndex = operationIndex;
|
|
3152
3215
|
this.documentMetaCache = documentMetaCache;
|
|
3153
3216
|
this.collectionMembershipCache = collectionMembershipCache;
|
|
3217
|
+
this.driveContainerTypes = driveContainerTypes;
|
|
3154
3218
|
this.config = {
|
|
3155
3219
|
maxSkipThreshold: config.maxSkipThreshold ?? MAX_SKIP_THRESHOLD,
|
|
3156
3220
|
maxConcurrency: config.maxConcurrency ?? 1,
|
|
@@ -3160,7 +3224,7 @@ var SimpleJobExecutor = class {
|
|
|
3160
3224
|
yieldDeadlineMs: config.yieldDeadlineMs ?? 50
|
|
3161
3225
|
};
|
|
3162
3226
|
this.signatureVerifierModule = new SignatureVerifier(signatureVerifier);
|
|
3163
|
-
this.documentActionHandler = new DocumentActionHandler(registry, logger);
|
|
3227
|
+
this.documentActionHandler = new DocumentActionHandler(registry, logger, driveContainerTypes);
|
|
3164
3228
|
this.executionScope = executionScope ?? new DefaultExecutionScope(operationStore, operationIndex, writeCache, documentMetaCache, collectionMembershipCache);
|
|
3165
3229
|
}
|
|
3166
3230
|
/**
|
|
@@ -3395,7 +3459,7 @@ var SimpleJobExecutor = class {
|
|
|
3395
3459
|
} catch {}
|
|
3396
3460
|
if (docMeta?.state.isDeleted) return buildErrorResult(job, new DocumentDeletedError(job.documentId, docMeta.state.deletedAtUtcIso), startTime);
|
|
3397
3461
|
const scope = job.scope;
|
|
3398
|
-
let latestRevision
|
|
3462
|
+
let latestRevision;
|
|
3399
3463
|
try {
|
|
3400
3464
|
latestRevision = (await stores.operationStore.getRevisions(job.documentId, job.branch, signal)).revision[scope] ?? 0;
|
|
3401
3465
|
} catch {
|
|
@@ -3414,7 +3478,7 @@ var SimpleJobExecutor = class {
|
|
|
3414
3478
|
const ts = operation.timestampUtcMs || "";
|
|
3415
3479
|
if (ts < minIncomingTimestamp) minIncomingTimestamp = ts;
|
|
3416
3480
|
}
|
|
3417
|
-
let conflictingOps
|
|
3481
|
+
let conflictingOps;
|
|
3418
3482
|
try {
|
|
3419
3483
|
conflictingOps = (await stores.operationStore.getConflicting(job.documentId, scope, job.branch, minIncomingTimestamp, void 0, signal)).results;
|
|
3420
3484
|
} catch {
|
|
@@ -3429,7 +3493,9 @@ var SimpleJobExecutor = class {
|
|
|
3429
3493
|
allOpsFromMinConflictingIndex = conflictingOps;
|
|
3430
3494
|
}
|
|
3431
3495
|
}
|
|
3496
|
+
const incomingActionIds = new Set(job.operations.map((op) => op.action.id));
|
|
3432
3497
|
const nonSupersededOps = conflictingOps.filter((op) => {
|
|
3498
|
+
if (op.index < minIncomingIndex && !incomingActionIds.has(op.action.id)) return false;
|
|
3433
3499
|
for (const laterOp of allOpsFromMinConflictingIndex) if (laterOp.index > op.index && laterOp.skip > 0) {
|
|
3434
3500
|
if (laterOp.index - laterOp.skip <= op.index) return false;
|
|
3435
3501
|
}
|
|
@@ -3467,7 +3533,10 @@ var SimpleJobExecutor = class {
|
|
|
3467
3533
|
operationsWithContext: [],
|
|
3468
3534
|
duration: Date.now() - startTime
|
|
3469
3535
|
};
|
|
3470
|
-
const reshuffledOperations =
|
|
3536
|
+
const reshuffledOperations = existingOpsToReshuffle.length === 0 && skipCount === 0 ? incomingOpsToApply.slice().sort((a, b) => a.index - b.index).map((operation, i) => ({
|
|
3537
|
+
...operation,
|
|
3538
|
+
index: latestRevision + i
|
|
3539
|
+
})) : reshuffleByTimestamp({
|
|
3471
3540
|
index: latestRevision,
|
|
3472
3541
|
skip: skipCount
|
|
3473
3542
|
}, existingOpsToReshuffle, incomingOpsToApply.map((operation) => ({
|
|
@@ -3736,10 +3805,6 @@ var BaseReadModel = class {
|
|
|
3736
3805
|
};
|
|
3737
3806
|
//#endregion
|
|
3738
3807
|
//#region src/processors/utils.ts
|
|
3739
|
-
const DRIVE_DOCUMENT_TYPE = "powerhouse/document-drive";
|
|
3740
|
-
function isDriveCreation(op) {
|
|
3741
|
-
return op.operation.action.type === "CREATE_DOCUMENT" && op.context.documentType === "powerhouse/document-drive";
|
|
3742
|
-
}
|
|
3743
3808
|
function isDriveDeletion(op) {
|
|
3744
3809
|
return op.operation.action.type === "DELETE_DOCUMENT";
|
|
3745
3810
|
}
|
|
@@ -3750,10 +3815,10 @@ function extractDriveHeader(op) {
|
|
|
3750
3815
|
function extractDeletedDocumentId(op) {
|
|
3751
3816
|
return op.operation.action.input.documentId ?? op.context.documentId;
|
|
3752
3817
|
}
|
|
3753
|
-
function createMinimalDriveHeader(driveId) {
|
|
3818
|
+
function createMinimalDriveHeader(driveId, documentType) {
|
|
3754
3819
|
return {
|
|
3755
3820
|
id: driveId,
|
|
3756
|
-
documentType
|
|
3821
|
+
documentType,
|
|
3757
3822
|
sig: {
|
|
3758
3823
|
publicKey: {},
|
|
3759
3824
|
nonce: ""
|
|
@@ -3798,15 +3863,17 @@ var ProcessorManager = class extends BaseReadModel {
|
|
|
3798
3863
|
factoryRegistry = /* @__PURE__ */ new Map();
|
|
3799
3864
|
processorsByDrive = /* @__PURE__ */ new Map();
|
|
3800
3865
|
factoryToProcessors = /* @__PURE__ */ new Map();
|
|
3801
|
-
|
|
3866
|
+
knownDrives = /* @__PURE__ */ new Map();
|
|
3802
3867
|
cursorCache = /* @__PURE__ */ new Map();
|
|
3803
3868
|
logger;
|
|
3804
|
-
|
|
3869
|
+
driveContainerTypes;
|
|
3870
|
+
constructor(db, operationIndex, writeCache, consistencyTracker, logger, driveContainerTypes) {
|
|
3805
3871
|
super(db, operationIndex, writeCache, consistencyTracker, {
|
|
3806
3872
|
readModelId: "processor-manager",
|
|
3807
3873
|
rebuildStateOnInit: true
|
|
3808
3874
|
});
|
|
3809
3875
|
this.logger = logger;
|
|
3876
|
+
this.driveContainerTypes = driveContainerTypes;
|
|
3810
3877
|
}
|
|
3811
3878
|
async init() {
|
|
3812
3879
|
await super.init();
|
|
@@ -3822,8 +3889,8 @@ var ProcessorManager = class extends BaseReadModel {
|
|
|
3822
3889
|
if (this.factoryRegistry.has(identifier)) await this.unregisterFactory(identifier);
|
|
3823
3890
|
this.factoryRegistry.set(identifier, factory);
|
|
3824
3891
|
this.factoryToProcessors.set(identifier, /* @__PURE__ */ new Map());
|
|
3825
|
-
for (const driveId of this.
|
|
3826
|
-
const driveHeader = createMinimalDriveHeader(driveId);
|
|
3892
|
+
for (const [driveId, documentType] of this.knownDrives) {
|
|
3893
|
+
const driveHeader = createMinimalDriveHeader(driveId, documentType);
|
|
3827
3894
|
await this.createProcessorsForDrive(driveId, identifier, factory, driveHeader);
|
|
3828
3895
|
}
|
|
3829
3896
|
}
|
|
@@ -3854,34 +3921,37 @@ var ProcessorManager = class extends BaseReadModel {
|
|
|
3854
3921
|
}
|
|
3855
3922
|
async detectAndRegisterNewDrives(operations) {
|
|
3856
3923
|
for (const op of operations) {
|
|
3857
|
-
if (!isDriveCreation(op)) continue;
|
|
3924
|
+
if (!this.isDriveCreation(op)) continue;
|
|
3858
3925
|
const driveId = op.context.documentId;
|
|
3859
|
-
if (this.
|
|
3860
|
-
this.
|
|
3926
|
+
if (this.knownDrives.has(driveId)) continue;
|
|
3927
|
+
this.knownDrives.set(driveId, op.context.documentType);
|
|
3861
3928
|
const driveHeader = extractDriveHeader(op);
|
|
3862
3929
|
if (!driveHeader) continue;
|
|
3863
3930
|
for (const [identifier, factory] of this.factoryRegistry) await this.createProcessorsForDrive(driveId, identifier, factory, driveHeader);
|
|
3864
3931
|
}
|
|
3865
3932
|
}
|
|
3933
|
+
isDriveCreation(op) {
|
|
3934
|
+
return op.operation.action.type === "CREATE_DOCUMENT" && this.driveContainerTypes.has(op.context.documentType);
|
|
3935
|
+
}
|
|
3866
3936
|
async detectAndCleanupDeletedDrives(operations) {
|
|
3867
3937
|
for (const op of operations) {
|
|
3868
3938
|
if (!isDriveDeletion(op)) continue;
|
|
3869
3939
|
const driveId = extractDeletedDocumentId(op);
|
|
3870
|
-
if (!driveId || !this.
|
|
3940
|
+
if (!driveId || !this.knownDrives.has(driveId)) continue;
|
|
3871
3941
|
if (!this.isDeletedDocumentADrive(driveId)) continue;
|
|
3872
3942
|
await this.cleanupDriveProcessors(driveId);
|
|
3873
|
-
this.
|
|
3943
|
+
this.knownDrives.delete(driveId);
|
|
3874
3944
|
}
|
|
3875
3945
|
}
|
|
3876
3946
|
async discoverExistingDrives() {
|
|
3877
|
-
const drives = await this.db.selectFrom("DocumentSnapshot").select("documentId").where("documentType", "
|
|
3878
|
-
for (const drive of drives) this.
|
|
3947
|
+
const drives = await this.db.selectFrom("DocumentSnapshot").select(["documentId", "documentType"]).where("documentType", "in", [...this.driveContainerTypes]).where("isDeleted", "=", false).execute();
|
|
3948
|
+
for (const drive of drives) this.knownDrives.set(drive.documentId, drive.documentType);
|
|
3879
3949
|
}
|
|
3880
3950
|
isDeletedDocumentADrive(documentId) {
|
|
3881
|
-
return this.
|
|
3951
|
+
return this.knownDrives.has(documentId);
|
|
3882
3952
|
}
|
|
3883
3953
|
async createProcessorsForDrive(driveId, identifier, factory, driveHeader) {
|
|
3884
|
-
let records
|
|
3954
|
+
let records;
|
|
3885
3955
|
try {
|
|
3886
3956
|
records = await factory(driveHeader);
|
|
3887
3957
|
} catch (error) {
|
|
@@ -4550,7 +4620,7 @@ var KyselyDocumentView = class extends BaseReadModel {
|
|
|
4550
4620
|
const { documentId, scope, branch, documentType, resultingState } = context;
|
|
4551
4621
|
const { index, hash } = operation;
|
|
4552
4622
|
if (!resultingState) throw new Error(`Missing resultingState in context for operation ${operation.id || "unknown"}. IDocumentView requires resultingState from upstream - it does not rebuild documents.`);
|
|
4553
|
-
let fullState
|
|
4623
|
+
let fullState;
|
|
4554
4624
|
try {
|
|
4555
4625
|
fullState = JSON.parse(resultingState);
|
|
4556
4626
|
} catch (error) {
|
|
@@ -5144,6 +5214,7 @@ var KyselyDocumentIndexer = class extends BaseReadModel {
|
|
|
5144
5214
|
const actionType = operation.action.type;
|
|
5145
5215
|
if (actionType === "ADD_RELATIONSHIP") await this.handleAddRelationship(trx, operation);
|
|
5146
5216
|
else if (actionType === "REMOVE_RELATIONSHIP") await this.handleRemoveRelationship(trx, operation);
|
|
5217
|
+
else if (actionType === "UPDATE_RELATIONSHIP") await this.handleUpdateRelationship(trx, operation);
|
|
5147
5218
|
}
|
|
5148
5219
|
});
|
|
5149
5220
|
}
|
|
@@ -5358,6 +5429,13 @@ var KyselyDocumentIndexer = class extends BaseReadModel {
|
|
|
5358
5429
|
const input = operation.action.input;
|
|
5359
5430
|
await trx.deleteFrom("DocumentRelationship").where("sourceId", "=", input.sourceId).where("targetId", "=", input.targetId).where("relationshipType", "=", input.relationshipType).execute();
|
|
5360
5431
|
}
|
|
5432
|
+
async handleUpdateRelationship(trx, operation) {
|
|
5433
|
+
const input = operation.action.input;
|
|
5434
|
+
await trx.updateTable("DocumentRelationship").set({
|
|
5435
|
+
metadata: input.metadata,
|
|
5436
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
5437
|
+
}).where("sourceId", "=", input.sourceId).where("targetId", "=", input.targetId).where("relationshipType", "=", input.relationshipType).execute();
|
|
5438
|
+
}
|
|
5361
5439
|
};
|
|
5362
5440
|
//#endregion
|
|
5363
5441
|
//#region src/storage/kysely/keyframe-store.ts
|
|
@@ -6695,25 +6773,6 @@ function consolidateSyncOperations(syncOps) {
|
|
|
6695
6773
|
}
|
|
6696
6774
|
return result;
|
|
6697
6775
|
}
|
|
6698
|
-
function mergeCollectionMemberships(events) {
|
|
6699
|
-
const mergedMemberships = {};
|
|
6700
|
-
for (const event of events) {
|
|
6701
|
-
if (event.collectionMemberships) for (const [docId, collections] of Object.entries(event.collectionMemberships)) {
|
|
6702
|
-
if (!(docId in mergedMemberships)) mergedMemberships[docId] = [];
|
|
6703
|
-
for (const c of collections) if (!mergedMemberships[docId].includes(c)) mergedMemberships[docId].push(c);
|
|
6704
|
-
}
|
|
6705
|
-
for (const op of event.operations) {
|
|
6706
|
-
const action = op.operation.action;
|
|
6707
|
-
if (action.type !== "ADD_RELATIONSHIP") continue;
|
|
6708
|
-
const input = action.input;
|
|
6709
|
-
if (!input?.sourceId || !input.targetId) continue;
|
|
6710
|
-
const collectionId = driveCollectionId(op.context.branch, input.sourceId);
|
|
6711
|
-
if (!(input.targetId in mergedMemberships)) mergedMemberships[input.targetId] = [];
|
|
6712
|
-
if (!mergedMemberships[input.targetId].includes(collectionId)) mergedMemberships[input.targetId].push(collectionId);
|
|
6713
|
-
}
|
|
6714
|
-
}
|
|
6715
|
-
return mergedMemberships;
|
|
6716
|
-
}
|
|
6717
6776
|
/**
|
|
6718
6777
|
* Chunks sync operations into batches that respect dependency-connected
|
|
6719
6778
|
* components. SyncOps linked by jobDependencies are kept in the same chunk.
|
|
@@ -8042,12 +8101,14 @@ var KyselySyncRemoteStorage = class {
|
|
|
8042
8101
|
//#region src/sync/batch-aggregator.ts
|
|
8043
8102
|
var BatchAggregator = class {
|
|
8044
8103
|
logger;
|
|
8104
|
+
driveContainerTypes;
|
|
8045
8105
|
onBatchReady;
|
|
8046
8106
|
queue = [];
|
|
8047
8107
|
processing = false;
|
|
8048
8108
|
pendingBatches = /* @__PURE__ */ new Map();
|
|
8049
|
-
constructor(logger, onBatchReady) {
|
|
8109
|
+
constructor(logger, driveContainerTypes, onBatchReady) {
|
|
8050
8110
|
this.logger = logger;
|
|
8111
|
+
this.driveContainerTypes = driveContainerTypes;
|
|
8051
8112
|
this.onBatchReady = onBatchReady;
|
|
8052
8113
|
}
|
|
8053
8114
|
async enqueueWriteReady(event) {
|
|
@@ -8106,7 +8167,7 @@ var BatchAggregator = class {
|
|
|
8106
8167
|
}
|
|
8107
8168
|
}
|
|
8108
8169
|
prepareBatch(events) {
|
|
8109
|
-
const collectionMemberships = mergeCollectionMemberships(events);
|
|
8170
|
+
const collectionMemberships = this.mergeCollectionMemberships(events);
|
|
8110
8171
|
const isBatch = events.length > 1;
|
|
8111
8172
|
const priorJobIds = [];
|
|
8112
8173
|
const entries = [];
|
|
@@ -8122,6 +8183,26 @@ var BatchAggregator = class {
|
|
|
8122
8183
|
entries
|
|
8123
8184
|
};
|
|
8124
8185
|
}
|
|
8186
|
+
mergeCollectionMemberships(events) {
|
|
8187
|
+
const mergedMemberships = {};
|
|
8188
|
+
for (const event of events) {
|
|
8189
|
+
if (event.collectionMemberships) for (const [docId, collections] of Object.entries(event.collectionMemberships)) {
|
|
8190
|
+
if (!(docId in mergedMemberships)) mergedMemberships[docId] = [];
|
|
8191
|
+
for (const c of collections) if (!mergedMemberships[docId].includes(c)) mergedMemberships[docId].push(c);
|
|
8192
|
+
}
|
|
8193
|
+
for (const op of event.operations) {
|
|
8194
|
+
const action = op.operation.action;
|
|
8195
|
+
if (action.type !== "ADD_RELATIONSHIP") continue;
|
|
8196
|
+
if (!this.driveContainerTypes.has(op.context.documentType)) continue;
|
|
8197
|
+
const input = action.input;
|
|
8198
|
+
if (!input?.sourceId || !input.targetId) continue;
|
|
8199
|
+
const collectionId = driveCollectionId(op.context.branch, input.sourceId);
|
|
8200
|
+
if (!(input.targetId in mergedMemberships)) mergedMemberships[input.targetId] = [];
|
|
8201
|
+
if (!mergedMemberships[input.targetId].includes(collectionId)) mergedMemberships[input.targetId].push(collectionId);
|
|
8202
|
+
}
|
|
8203
|
+
}
|
|
8204
|
+
return mergedMemberships;
|
|
8205
|
+
}
|
|
8125
8206
|
};
|
|
8126
8207
|
//#endregion
|
|
8127
8208
|
//#region src/sync/sync-awaiter.ts
|
|
@@ -8373,7 +8454,7 @@ var SyncManager = class {
|
|
|
8373
8454
|
planKeyToJobUuid = /* @__PURE__ */ new Map();
|
|
8374
8455
|
lastEnqueuedJobIdByKey = /* @__PURE__ */ new Map();
|
|
8375
8456
|
inboxChunkChain = Promise.resolve();
|
|
8376
|
-
constructor(logger, remoteStorage, cursorStorage, deadLetterStorage, channelFactory, operationIndex, reactor, eventBus, config = {}) {
|
|
8457
|
+
constructor(logger, remoteStorage, cursorStorage, deadLetterStorage, channelFactory, operationIndex, reactor, eventBus, driveContainerTypes, config = {}) {
|
|
8377
8458
|
this.logger = logger;
|
|
8378
8459
|
this.remoteStorage = remoteStorage;
|
|
8379
8460
|
this.cursorStorage = cursorStorage;
|
|
@@ -8390,7 +8471,7 @@ var SyncManager = class {
|
|
|
8390
8471
|
this.awaiter = new JobAwaiter(eventBus, (jobId, signal) => reactor.getJobStatus(jobId, signal));
|
|
8391
8472
|
this.syncAwaiter = new SyncAwaiter(eventBus);
|
|
8392
8473
|
this.isShutdown = false;
|
|
8393
|
-
this.batchAggregator = new BatchAggregator(logger, (batch) => this.processCompleteBatch(batch));
|
|
8474
|
+
this.batchAggregator = new BatchAggregator(logger, driveContainerTypes, (batch) => this.processCompleteBatch(batch));
|
|
8394
8475
|
this.syncStatusTracker = new SyncStatusTracker();
|
|
8395
8476
|
}
|
|
8396
8477
|
async startup() {
|
|
@@ -8888,15 +8969,15 @@ var SyncBuilder = class {
|
|
|
8888
8969
|
this.config.maxInboxBatchSize = limit;
|
|
8889
8970
|
return this;
|
|
8890
8971
|
}
|
|
8891
|
-
build(reactor, logger, operationIndex, eventBus, db) {
|
|
8892
|
-
return this.buildModule(reactor, logger, operationIndex, eventBus, db).syncManager;
|
|
8972
|
+
build(reactor, logger, operationIndex, eventBus, db, driveContainerTypes) {
|
|
8973
|
+
return this.buildModule(reactor, logger, operationIndex, eventBus, db, driveContainerTypes).syncManager;
|
|
8893
8974
|
}
|
|
8894
|
-
buildModule(reactor, logger, operationIndex, eventBus, db) {
|
|
8975
|
+
buildModule(reactor, logger, operationIndex, eventBus, db, driveContainerTypes) {
|
|
8895
8976
|
if (!this.channelFactory) throw new Error("Channel factory is required");
|
|
8896
8977
|
const remoteStorage = this.remoteStorage ?? new KyselySyncRemoteStorage(db);
|
|
8897
8978
|
const cursorStorage = this.cursorStorage ?? new KyselySyncCursorStorage(db);
|
|
8898
8979
|
const deadLetterStorage = this.deadLetterStorage ?? new KyselySyncDeadLetterStorage(db);
|
|
8899
|
-
const syncManager = new SyncManager(logger, remoteStorage, cursorStorage, deadLetterStorage, this.channelFactory, operationIndex, reactor, eventBus, this.config);
|
|
8980
|
+
const syncManager = new SyncManager(logger, remoteStorage, cursorStorage, deadLetterStorage, this.channelFactory, operationIndex, reactor, eventBus, driveContainerTypes, this.config);
|
|
8900
8981
|
return {
|
|
8901
8982
|
remoteStorage,
|
|
8902
8983
|
cursorStorage,
|
|
@@ -8915,6 +8996,9 @@ async function createDefaultDatabase() {
|
|
|
8915
8996
|
return new Kysely({ dialect: new PGliteDialect(new PGlite()) });
|
|
8916
8997
|
}
|
|
8917
8998
|
//#endregion
|
|
8999
|
+
//#region src/core/drive-container-types.ts
|
|
9000
|
+
const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
|
|
9001
|
+
//#endregion
|
|
8918
9002
|
//#region src/shared/factories.ts
|
|
8919
9003
|
/**
|
|
8920
9004
|
* Factory method to create a ShutdownStatus that can be updated
|
|
@@ -9494,6 +9578,7 @@ var ReactorBuilder = class {
|
|
|
9494
9578
|
upgradeManifests = [];
|
|
9495
9579
|
features = { legacyStorageEnabled: false };
|
|
9496
9580
|
readModels = [];
|
|
9581
|
+
readModelFactories = [];
|
|
9497
9582
|
executorManager;
|
|
9498
9583
|
executorConfig = {};
|
|
9499
9584
|
writeCacheConfig;
|
|
@@ -9509,6 +9594,7 @@ var ReactorBuilder = class {
|
|
|
9509
9594
|
jwtHandler;
|
|
9510
9595
|
documentModelLoader;
|
|
9511
9596
|
shutdownHooks = [];
|
|
9597
|
+
driveContainerTypes = DEFAULT_DRIVE_CONTAINER_TYPES;
|
|
9512
9598
|
withLogger(logger) {
|
|
9513
9599
|
this.logger = logger;
|
|
9514
9600
|
return this;
|
|
@@ -9532,6 +9618,17 @@ var ReactorBuilder = class {
|
|
|
9532
9618
|
this.readModels.push(readModel);
|
|
9533
9619
|
return this;
|
|
9534
9620
|
}
|
|
9621
|
+
/**
|
|
9622
|
+
* Register a factory that builds a pre-ready read model after the reactor's
|
|
9623
|
+
* internal `operationIndex`, `writeCache`, and processor-manager consistency
|
|
9624
|
+
* tracker are constructed. Use this for read models (e.g. `BaseReadModel`
|
|
9625
|
+
* subclasses) that need those dependencies and therefore cannot be built
|
|
9626
|
+
* before calling `buildModule()`.
|
|
9627
|
+
*/
|
|
9628
|
+
withReadModelFactory(factory) {
|
|
9629
|
+
this.readModelFactories.push(factory);
|
|
9630
|
+
return this;
|
|
9631
|
+
}
|
|
9535
9632
|
withReadModelCoordinator(readModelCoordinator) {
|
|
9536
9633
|
this.readModelCoordinator = readModelCoordinator;
|
|
9537
9634
|
return this;
|
|
@@ -9551,6 +9648,10 @@ var ReactorBuilder = class {
|
|
|
9551
9648
|
this.writeCacheConfig = config;
|
|
9552
9649
|
return this;
|
|
9553
9650
|
}
|
|
9651
|
+
withDriveContainerTypes(types) {
|
|
9652
|
+
this.driveContainerTypes = new Set(types);
|
|
9653
|
+
return this;
|
|
9654
|
+
}
|
|
9554
9655
|
withMigrationStrategy(strategy) {
|
|
9555
9656
|
this.migrationStrategy = strategy;
|
|
9556
9657
|
return this;
|
|
@@ -9641,7 +9742,7 @@ var ReactorBuilder = class {
|
|
|
9641
9742
|
const collectionMembershipCache = new CollectionMembershipCache(operationIndex);
|
|
9642
9743
|
const executionScope = new KyselyExecutionScope(database, operationStore, operationIndex, keyframeStore, writeCache, documentMetaCache, collectionMembershipCache);
|
|
9643
9744
|
let executorManager = this.executorManager;
|
|
9644
|
-
if (!executorManager) executorManager = new SimpleJobExecutorManager(() => new SimpleJobExecutor(this.logger, documentModelRegistry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, this.executorConfig, this.signatureVerifier, executionScope), eventBus, queue, jobTracker, this.logger, resolver, this.executorConfig.jobTimeoutMs);
|
|
9745
|
+
if (!executorManager) executorManager = new SimpleJobExecutorManager(() => new SimpleJobExecutor(this.logger, documentModelRegistry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, this.driveContainerTypes, this.executorConfig, this.signatureVerifier, executionScope), eventBus, queue, jobTracker, this.logger, resolver, this.executorConfig.jobTimeoutMs);
|
|
9645
9746
|
await executorManager.start(this.executorConfig.maxConcurrency ?? 1);
|
|
9646
9747
|
const readModelInstances = Array.from(new Set([...this.readModels]));
|
|
9647
9748
|
const documentViewConsistencyTracker = new ConsistencyTracker();
|
|
@@ -9663,21 +9764,29 @@ var ReactorBuilder = class {
|
|
|
9663
9764
|
const subscriptionManager = new ReactorSubscriptionManager(new DefaultSubscriptionErrorHandler());
|
|
9664
9765
|
const subscriptionNotificationReadModel = new SubscriptionNotificationReadModel(subscriptionManager, documentView);
|
|
9665
9766
|
const processorManagerConsistencyTracker = new ConsistencyTracker();
|
|
9666
|
-
const processorManager = new ProcessorManager(database, operationIndex, writeCache, processorManagerConsistencyTracker, this.logger);
|
|
9767
|
+
const processorManager = new ProcessorManager(database, operationIndex, writeCache, processorManagerConsistencyTracker, this.logger, this.driveContainerTypes);
|
|
9667
9768
|
try {
|
|
9668
9769
|
await processorManager.init();
|
|
9669
9770
|
} catch (error) {
|
|
9670
9771
|
console.error("Error initializing processor manager", error);
|
|
9671
9772
|
}
|
|
9773
|
+
for (const factory of this.readModelFactories) {
|
|
9774
|
+
const readModel = await factory({
|
|
9775
|
+
operationIndex,
|
|
9776
|
+
writeCache,
|
|
9777
|
+
processorManagerConsistencyTracker
|
|
9778
|
+
});
|
|
9779
|
+
readModelInstances.push(readModel);
|
|
9780
|
+
}
|
|
9672
9781
|
const readModelCoordinator = this.readModelCoordinator ? this.readModelCoordinator : new ReadModelCoordinator(eventBus, readModelInstances, [subscriptionNotificationReadModel, processorManager]);
|
|
9673
9782
|
const reactor = new Reactor(this.logger, documentModelRegistry, queue, jobTracker, readModelCoordinator, this.features, documentView, documentIndexer, operationStore, eventBus, executorManager);
|
|
9674
9783
|
let syncModule = void 0;
|
|
9675
9784
|
if (this.channelScheme) {
|
|
9676
9785
|
const factory = this.channelScheme === ChannelScheme.CONNECT ? new GqlRequestChannelFactory(this.logger, this.jwtHandler, queue) : new GqlResponseChannelFactory(this.logger);
|
|
9677
|
-
syncModule = new SyncBuilder().withChannelFactory(factory).buildModule(reactor, this.logger, operationIndex, eventBus, database);
|
|
9786
|
+
syncModule = new SyncBuilder().withChannelFactory(factory).buildModule(reactor, this.logger, operationIndex, eventBus, database, this.driveContainerTypes);
|
|
9678
9787
|
await syncModule.syncManager.startup();
|
|
9679
9788
|
} else if (this.syncBuilder) {
|
|
9680
|
-
syncModule = this.syncBuilder.buildModule(reactor, this.logger, operationIndex, eventBus, database);
|
|
9789
|
+
syncModule = this.syncBuilder.buildModule(reactor, this.logger, operationIndex, eventBus, database, this.driveContainerTypes);
|
|
9681
9790
|
await syncModule.syncManager.startup();
|
|
9682
9791
|
}
|
|
9683
9792
|
const module = {
|
|
@@ -10032,6 +10141,6 @@ var DocumentIntegrityService = class {
|
|
|
10032
10141
|
}
|
|
10033
10142
|
};
|
|
10034
10143
|
//#endregion
|
|
10035
|
-
export { BaseReadModel, ChannelError, ChannelErrorSource, ChannelScheme, ConsistencyTracker, DefaultSubscriptionErrorHandler, DocumentChangeType, DocumentIntegrityService, DocumentModelRegistry, DocumentModelResolver, DriveClient, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, IntervalPollTimer, InvalidModuleError, JobAwaiter, JobExecutorEventTypes, JobStatus, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, Mailbox, ModuleNotFoundError, NullDocumentModelResolver, OptimisticLockError, PollBehavior, PollingChannelError, ProcessorManager, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, ReactorEventTypes, ReactorSubscriptionManager, ReadModelCoordinator, RelationalDbProcessor, RelationshipChangeType, RevisionMismatchError, SimpleJobExecutorManager, SyncBuilder, SyncEventTypes, SyncOperation, SyncOperationAggregateError, SyncOperationStatus, SyncStatus, SyncStatusTracker, addRelationshipAction, batchOperationsByDocument, consolidateSyncOperations, createDocumentAction, createMutableShutdownStatus, createRelationalDb, deleteDocumentAction, documentActions, driveCollectionId, driveIdFromUrl, envelopesToSyncOperations, getMigrationStatus, makeConsistencyKey, parseDriveUrl, removeRelationshipAction, runMigrations, trimMailboxFromAckOrdinal, upgradeDocumentAction };
|
|
10144
|
+
export { BaseReadModel, ChannelError, ChannelErrorSource, ChannelScheme, ConsistencyTracker, DEFAULT_DRIVE_CONTAINER_TYPES, DefaultSubscriptionErrorHandler, DocumentChangeType, DocumentIntegrityService, DocumentModelRegistry, DocumentModelResolver, DriveClient, DuplicateManifestError, DuplicateModuleError, DuplicateOperationError, EventBus, EventBusAggregateError, GqlRequestChannel, GqlRequestChannelFactory, GqlResponseChannel, GqlResponseChannelFactory, SimpleJobExecutor as InMemoryJobExecutor, SimpleJobExecutor, InMemoryJobTracker, InMemoryQueue, IntervalPollTimer, InvalidModuleError, JobAwaiter, JobExecutorEventTypes, JobStatus, KyselyDocumentIndexer, KyselyDocumentView, KyselyKeyframeStore, KyselyOperationStore, KyselySyncCursorStorage, KyselySyncRemoteStorage, KyselyWriteCache, Mailbox, ModuleNotFoundError, NullDocumentModelResolver, OptimisticLockError, PollBehavior, PollingChannelError, ProcessorManager, PropagationMode, QueueEventTypes, REACTOR_SCHEMA, Reactor, ReactorBuilder, ReactorClient, ReactorClientBuilder, ReactorEventTypes, ReactorSubscriptionManager, ReadModelCoordinator, RelationalDbProcessor, RelationshipChangeType, RevisionMismatchError, SimpleJobExecutorManager, SyncBuilder, SyncEventTypes, SyncOperation, SyncOperationAggregateError, SyncOperationStatus, SyncStatus, SyncStatusTracker, addRelationshipAction, batchOperationsByDocument, consolidateSyncOperations, createDocumentAction, createMutableShutdownStatus, createRelationalDb, deleteDocumentAction, documentActions, driveCollectionId, driveIdFromUrl, envelopesToSyncOperations, getMigrationStatus, makeConsistencyKey, parseDriveUrl, parsePagingOptions, removeRelationshipAction, runMigrations, trimMailboxFromAckOrdinal, updateRelationshipAction, upgradeDocumentAction };
|
|
10036
10145
|
|
|
10037
10146
|
//# sourceMappingURL=index.js.map
|