@powerhousedao/reactor 6.2.2-dev.42 → 6.2.2-dev.44
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-CAEZD_yJ.js → build-worker-executor-CHkkh55C.js} +2 -2
- package/dist/{build-worker-executor-CAEZD_yJ.js.map → build-worker-executor-CHkkh55C.js.map} +1 -1
- package/dist/{document-indexer-C0GB0b8Q.js → document-indexer-BfdCgjG-.js} +15 -14
- package/dist/document-indexer-BfdCgjG-.js.map +1 -0
- package/dist/{drive-container-types-BJCKXJwH.js → drive-container-types-h3M1AK3K.js} +655 -114
- package/dist/drive-container-types-h3M1AK3K.js.map +1 -0
- package/dist/entry.js +2 -2
- package/dist/index.d.ts +232 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +176 -18
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.js +3 -3
- package/dist/{worker-jEJW6_j7.js → worker-CbtYMIju.js} +2 -2
- package/dist/{worker-jEJW6_j7.js.map → worker-CbtYMIju.js.map} +1 -1
- package/package.json +4 -4
- package/dist/document-indexer-C0GB0b8Q.js.map +0 -1
- package/dist/drive-container-types-BJCKXJwH.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { A as OptimisticLockError, B as ExcessiveReshuffleError, C as DocumentMetaCache, D as APPEND_CONDITION_FAILED_PREFIX, E as CollectionMembershipCache, F as ModuleNotFoundError, H as
|
|
1
|
+
import { A as OptimisticLockError, B as ExcessiveReshuffleError, C as DocumentMetaCache, D as APPEND_CONDITION_FAILED_PREFIX, E as CollectionMembershipCache, F as ModuleNotFoundError, G as throwIfAborted, H as UpgradePreconditionFailedError, I as AuthTimestampNotMonotonicError, L as AuthorizationDeniedError, M as DuplicateManifestError, N as DuplicateModuleError, O as AppendConditionFailedError, P as InvalidModuleError, R as DocumentDeletedError, S as KyselyOperationIndex, T as createEmptyConsistencyToken, U as matchesScope, V as InvalidOperationTimestampError, W as parsePagingOptions, _ as KyselyExecutionScope, a as createForwardingPoolInstrumentation, b as EventBus, c as KyselyKeyframeStore, d as DriveCollectionId, f as decideAtHead, g as authDecisionModel, h as buildDecisionModel, i as runMigrations, j as RevisionMismatchError, k as DuplicateOperationError, l as DocumentModelRegistry, m as documentDecisionModel, n as REACTOR_SCHEMA, o as instrumentPgPool, p as selectDecisionModel, r as getMigrationStatus, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor, v as FLAG_PREREQUISITES, w as createConsistencyToken, x as KyselyWriteCache, y as validateFeatureFlags, z as DocumentNotFoundError } from "./drive-container-types-h3M1AK3K.js";
|
|
2
2
|
import { n as ReactorEventTypes, t as EventBusAggregateError } from "./types-DMKLa0Ok.js";
|
|
3
3
|
import { i as WorkerInitFailedError, r as WorkerExitedError, t as WorkerAbortTimeoutError } from "./errors-D3S6Eysd.js";
|
|
4
|
-
import { a as ReadModelCoordinator, i as KyselyDocumentView, n as ConsistencyTracker, o as BaseReadModel, r as makeConsistencyKey, t as KyselyDocumentIndexer } from "./document-indexer-
|
|
4
|
+
import { a as ReadModelCoordinator, i as KyselyDocumentView, n as ConsistencyTracker, o as BaseReadModel, r as makeConsistencyKey, t as KyselyDocumentIndexer } from "./document-indexer-BfdCgjG-.js";
|
|
5
5
|
import { n as errorToInfo, r as sanitizeArg, t as createForwardingLogger } from "./forwarding-logger-BBkMSxuJ.js";
|
|
6
|
-
import { t as workerEntryPath } from "./worker-
|
|
7
|
-
import { actions, actions as documentActions, createPresignedHeader, decide, garbageCollect, generateId, hashDocumentStateForScope,
|
|
6
|
+
import { t as workerEntryPath } from "./worker-CbtYMIju.js";
|
|
7
|
+
import { DowngradeNotSupportedError, UnsupportedDocumentModelVersionError, actions, actions as documentActions, createPresignedHeader, decide, garbageCollect, generateId, groupDocumentType, groupMembershipActionTypes, hashDocumentStateForScope, normalizeDocumentModelVersion, sortOperations } from "@powerhousedao/shared/document-model";
|
|
8
8
|
import { addFile, addFolder, copyNode, deleteNode, driveCreateDocument, generateNodesCopy, getDescendants, handleTargetNameCollisions, isFileNode, isFolderNode, moveNode, updateNode } from "@powerhousedao/shared/document-drive";
|
|
9
9
|
import { v4 } from "uuid";
|
|
10
10
|
import { ConsoleLogger, childLogger } from "document-model";
|
|
@@ -365,7 +365,7 @@ var DriveClient = class {
|
|
|
365
365
|
documentId: document.header.id,
|
|
366
366
|
model: document.header.documentType,
|
|
367
367
|
fromVersion: 0,
|
|
368
|
-
toVersion:
|
|
368
|
+
toVersion: normalizeDocumentModelVersion(document.state.document?.version),
|
|
369
369
|
initialState: document.state
|
|
370
370
|
}),
|
|
371
371
|
addRelationshipAction(driveIdentifier, documentId, "child")
|
|
@@ -470,8 +470,12 @@ var DriveClient = class {
|
|
|
470
470
|
const node = drive.state.global.nodes.find((n) => n.id === entry.srcId);
|
|
471
471
|
if (!node || !isFileNode(node)) continue;
|
|
472
472
|
const srcDoc = await this.client.get(entry.srcId, void 0, signal);
|
|
473
|
-
const
|
|
474
|
-
|
|
473
|
+
const duplicated = {
|
|
474
|
+
...srcDoc,
|
|
475
|
+
header: createPresignedHeader(entry.targetId, srcDoc.header.documentType),
|
|
476
|
+
initialState: srcDoc.state,
|
|
477
|
+
operations: {}
|
|
478
|
+
};
|
|
475
479
|
const resolvedName = resolvedNamesByTargetId.get(entry.targetId);
|
|
476
480
|
if (resolvedName) duplicated.header.name = resolvedName;
|
|
477
481
|
await this.addFile(driveIdentifier, duplicated, entry.targetParentFolder ?? void 0, signal);
|
|
@@ -769,9 +773,37 @@ var ReactorClient = class {
|
|
|
769
773
|
* @returns The document model module
|
|
770
774
|
*/
|
|
771
775
|
async getDocumentModelModule(documentType) {
|
|
772
|
-
const
|
|
773
|
-
|
|
774
|
-
|
|
776
|
+
const modules = await this.reactor.getDocumentModels();
|
|
777
|
+
let latestModule;
|
|
778
|
+
let latestVersion = -1;
|
|
779
|
+
for (const module of modules.results) {
|
|
780
|
+
if (module.documentModel.global.id !== documentType) continue;
|
|
781
|
+
const version = module.version ?? 1;
|
|
782
|
+
if (version > latestVersion) {
|
|
783
|
+
latestVersion = version;
|
|
784
|
+
latestModule = module;
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
if (!latestModule) throw new Error(`Document model module not found for type: ${documentType}`);
|
|
788
|
+
return latestModule;
|
|
789
|
+
}
|
|
790
|
+
/**
|
|
791
|
+
* Retrieves the document model module matching the version the document is
|
|
792
|
+
* stamped with, so not-yet-upgraded documents get the reducer their
|
|
793
|
+
* history was written with rather than the latest.
|
|
794
|
+
*/
|
|
795
|
+
async getDocumentModelModuleForDocument(document) {
|
|
796
|
+
const documentType = document.header.documentType;
|
|
797
|
+
const version = normalizeDocumentModelVersion(document.state.document?.version);
|
|
798
|
+
const modules = await this.reactor.getDocumentModels();
|
|
799
|
+
const availableVersions = [];
|
|
800
|
+
for (const module of modules.results) {
|
|
801
|
+
if (module.documentModel.global.id !== documentType) continue;
|
|
802
|
+
const moduleVersion = normalizeDocumentModelVersion(module.version);
|
|
803
|
+
if (moduleVersion === version) return module;
|
|
804
|
+
availableVersions.push(moduleVersion);
|
|
805
|
+
}
|
|
806
|
+
throw new UnsupportedDocumentModelVersionError(documentType, version, availableVersions.sort((a, b) => a - b));
|
|
775
807
|
}
|
|
776
808
|
/**
|
|
777
809
|
* Retrieves a specific PHDocument
|
|
@@ -925,7 +957,7 @@ var ReactorClient = class {
|
|
|
925
957
|
documentId,
|
|
926
958
|
model: document.header.documentType,
|
|
927
959
|
fromVersion: 0,
|
|
928
|
-
toVersion: document.state.document
|
|
960
|
+
toVersion: normalizeDocumentModelVersion(document.state.document?.version),
|
|
929
961
|
initialState: document.state
|
|
930
962
|
})], this.signer, signal);
|
|
931
963
|
const jobs = [{
|
|
@@ -960,20 +992,61 @@ var ReactorClient = class {
|
|
|
960
992
|
const matchingModules = (await this.reactor.getDocumentModels(void 0, void 0, signal)).results.filter((m) => m.documentModel.global.id === documentModelType);
|
|
961
993
|
let module;
|
|
962
994
|
if (options?.documentModelVersion !== void 0) {
|
|
963
|
-
|
|
995
|
+
const requestedVersion = normalizeDocumentModelVersion(options.documentModelVersion);
|
|
996
|
+
module = matchingModules.find((m) => normalizeDocumentModelVersion(m.version) === requestedVersion);
|
|
964
997
|
if (!module) throw new Error(`Document model not found for type: ${documentModelType} with version: ${options.documentModelVersion}`);
|
|
965
998
|
} else {
|
|
966
999
|
module = matchingModules.reduce((latest, current) => {
|
|
967
1000
|
if (latest === void 0) return current;
|
|
968
|
-
return (current.version
|
|
1001
|
+
return normalizeDocumentModelVersion(current.version) > normalizeDocumentModelVersion(latest.version) ? current : latest;
|
|
969
1002
|
}, void 0);
|
|
970
1003
|
if (!module) throw new Error(`Document model not found for type: ${documentModelType}`);
|
|
971
1004
|
}
|
|
972
1005
|
const document = module.utils.createDocument();
|
|
973
|
-
document.state.document.version = module.version
|
|
1006
|
+
document.state.document.version = normalizeDocumentModelVersion(module.version);
|
|
974
1007
|
return this.create(document, options?.parentIdentifier, signal);
|
|
975
1008
|
}
|
|
976
1009
|
/**
|
|
1010
|
+
* Upgrades a document to a newer document model version by dispatching an
|
|
1011
|
+
* UPGRADE_DOCUMENT action. When toVersion is omitted, upgrades to the
|
|
1012
|
+
* latest registered module version for the document's type. Returns the
|
|
1013
|
+
* document unchanged when it is already at the target version.
|
|
1014
|
+
*
|
|
1015
|
+
* The executor validates the action's version and revision snapshot against
|
|
1016
|
+
* the state the migration actually runs on. When a concurrent edit
|
|
1017
|
+
* invalidates the snapshot, the upgrade is rebuilt from a fresh read and
|
|
1018
|
+
* retried up to maxConflictRetries times before the conflict is surfaced.
|
|
1019
|
+
*/
|
|
1020
|
+
async upgradeDocument(documentIdentifier, toVersion, options, signal) {
|
|
1021
|
+
this.logger.verbose("upgradeDocument(@documentIdentifier, @toVersion)", documentIdentifier, toVersion);
|
|
1022
|
+
const maxConflictRetries = options?.maxConflictRetries ?? 3;
|
|
1023
|
+
let lastConflictMessage = "";
|
|
1024
|
+
for (let attempt = 0; attempt <= maxConflictRetries; attempt++) {
|
|
1025
|
+
const document = await this.reactor.getByIdOrSlug(documentIdentifier, void 0, void 0, signal);
|
|
1026
|
+
const documentId = document.header.id;
|
|
1027
|
+
const documentType = document.header.documentType;
|
|
1028
|
+
const branch = document.header.branch || "main";
|
|
1029
|
+
const fromVersion = normalizeDocumentModelVersion(document.state.document?.version);
|
|
1030
|
+
let targetVersion = toVersion;
|
|
1031
|
+
if (targetVersion === void 0) targetVersion = normalizeDocumentModelVersion((await this.getDocumentModelModule(documentType)).version);
|
|
1032
|
+
if (targetVersion === fromVersion) return document;
|
|
1033
|
+
if (targetVersion < fromVersion) throw new DowngradeNotSupportedError(documentType, fromVersion, targetVersion);
|
|
1034
|
+
const signedActions = await signActions([upgradeDocumentAction({
|
|
1035
|
+
documentId,
|
|
1036
|
+
model: documentType,
|
|
1037
|
+
fromVersion,
|
|
1038
|
+
toVersion: targetVersion,
|
|
1039
|
+
revision: { ...document.header.revision }
|
|
1040
|
+
})], this.signer, signal);
|
|
1041
|
+
const jobInfo = await this.reactor.execute(documentId, branch, signedActions, signal);
|
|
1042
|
+
const completedJob = await this.waitForJob(jobInfo, signal);
|
|
1043
|
+
if (completedJob.status !== JobStatus.FAILED) return await this.reactor.getByIdOrSlug(documentId, { branch }, completedJob.consistencyToken, signal);
|
|
1044
|
+
if (completedJob.error?.name !== "UpgradePreconditionFailedError") throw new Error(completedJob.error?.message);
|
|
1045
|
+
lastConflictMessage = completedJob.error.message;
|
|
1046
|
+
}
|
|
1047
|
+
throw new Error(`Upgrade of document ${documentIdentifier} conflicted with concurrent edits after ${maxConflictRetries + 1} attempts: ${lastConflictMessage}`);
|
|
1048
|
+
}
|
|
1049
|
+
/**
|
|
977
1050
|
* Creates an empty document in a drive as a single batched operation.
|
|
978
1051
|
* Delegates to {@link IDriveClient.addFile}.
|
|
979
1052
|
*
|
|
@@ -1274,7 +1347,7 @@ var JobResultHandler = class {
|
|
|
1274
1347
|
callbacks.deferJob(handle.job.documentId, handle.job);
|
|
1275
1348
|
return;
|
|
1276
1349
|
}
|
|
1277
|
-
if (result.error && (DocumentDeletedError.isError(result.error) || AuthorizationDeniedError.isError(result.error) || AuthTimestampNotMonotonicError.isError(result.error) || InvalidOperationTimestampError.isError(result.error) || ExcessiveReshuffleError.isError(result.error))) {
|
|
1350
|
+
if (result.error && (DocumentDeletedError.isError(result.error) || AuthorizationDeniedError.isError(result.error) || AuthTimestampNotMonotonicError.isError(result.error) || InvalidOperationTimestampError.isError(result.error) || ExcessiveReshuffleError.isError(result.error) || UpgradePreconditionFailedError.isError(result.error))) {
|
|
1278
1351
|
const errorInfo = toErrorInfo(result.error);
|
|
1279
1352
|
this.jobTracker.markFailed(handle.job.id, errorInfo, handle.job);
|
|
1280
1353
|
this.eventBus.emit(ReactorEventTypes.JOB_FAILED, {
|
|
@@ -2668,9 +2741,11 @@ var InMemoryQueue = class {
|
|
|
2668
2741
|
this.jobIndex.delete(jobId);
|
|
2669
2742
|
this.completedJobs.add(jobId);
|
|
2670
2743
|
await this.remove(jobId);
|
|
2744
|
+
const emittedError = new Error(error?.message ?? "Job failed");
|
|
2745
|
+
if (error?.name) emittedError.name = error.name;
|
|
2671
2746
|
this.eventBus.emit(ReactorEventTypes.JOB_FAILED, {
|
|
2672
2747
|
jobId,
|
|
2673
|
-
error:
|
|
2748
|
+
error: emittedError,
|
|
2674
2749
|
job
|
|
2675
2750
|
}).catch(() => {});
|
|
2676
2751
|
this.checkDrained();
|
|
@@ -3210,6 +3285,83 @@ var SubscriptionNotificationReadModel = class {
|
|
|
3210
3285
|
}
|
|
3211
3286
|
};
|
|
3212
3287
|
//#endregion
|
|
3288
|
+
//#region src/core/group-reevaluation-trigger.ts
|
|
3289
|
+
/**
|
|
3290
|
+
* Watches committed writes for group membership changes and enqueues a
|
|
3291
|
+
* re-evaluation job for every document whose auth history references the
|
|
3292
|
+
* changed group, found through the reverse direction of the group-reference
|
|
3293
|
+
* relation. Each affected document is re-judged in its own job, so the work
|
|
3294
|
+
* runs under that document's execution slot rather than the group's.
|
|
3295
|
+
*
|
|
3296
|
+
* The job carries the earliest changed membership timestamp; the executor
|
|
3297
|
+
* skips the pass when everything the document holds sorts before it, which
|
|
3298
|
+
* keeps the common case (a membership write later than all history) free.
|
|
3299
|
+
*/
|
|
3300
|
+
var GroupReevaluationTrigger = class {
|
|
3301
|
+
unsubscribe;
|
|
3302
|
+
constructor(logger, eventBus, queue, operationIndex) {
|
|
3303
|
+
this.logger = logger;
|
|
3304
|
+
this.eventBus = eventBus;
|
|
3305
|
+
this.queue = queue;
|
|
3306
|
+
this.operationIndex = operationIndex;
|
|
3307
|
+
}
|
|
3308
|
+
startup() {
|
|
3309
|
+
this.unsubscribe = this.eventBus.subscribe(ReactorEventTypes.JOB_WRITE_READY, async (_type, event) => this.onWriteReady(event));
|
|
3310
|
+
}
|
|
3311
|
+
shutdown() {
|
|
3312
|
+
this.unsubscribe?.();
|
|
3313
|
+
this.unsubscribe = void 0;
|
|
3314
|
+
}
|
|
3315
|
+
async onWriteReady(event) {
|
|
3316
|
+
const changed = /* @__PURE__ */ new Map();
|
|
3317
|
+
for (const { operation, context } of event.operations) {
|
|
3318
|
+
if (context.documentType !== groupDocumentType || context.scope !== "global" || !groupMembershipActionTypes.includes(operation.action.type)) continue;
|
|
3319
|
+
const existing = changed.get(context.documentId);
|
|
3320
|
+
if (existing === void 0 || Date.parse(operation.timestampUtcMs) < Date.parse(existing)) changed.set(context.documentId, operation.timestampUtcMs);
|
|
3321
|
+
}
|
|
3322
|
+
if (changed.size === 0) return;
|
|
3323
|
+
const affected = /* @__PURE__ */ new Map();
|
|
3324
|
+
for (const [groupId, timestamp] of changed) {
|
|
3325
|
+
let referencers;
|
|
3326
|
+
try {
|
|
3327
|
+
referencers = await this.operationIndex.getGroupReferencers(groupId);
|
|
3328
|
+
} catch (error) {
|
|
3329
|
+
this.logger.error("Failed to resolve referencers of group @groupId: @error", groupId, error);
|
|
3330
|
+
continue;
|
|
3331
|
+
}
|
|
3332
|
+
for (const documentId of referencers) {
|
|
3333
|
+
const existing = affected.get(documentId);
|
|
3334
|
+
if (existing === void 0 || Date.parse(timestamp) < Date.parse(existing)) affected.set(documentId, timestamp);
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3337
|
+
for (const [documentId, timestamp] of affected) {
|
|
3338
|
+
const jobId = v4();
|
|
3339
|
+
const job = {
|
|
3340
|
+
id: jobId,
|
|
3341
|
+
kind: "reevaluation",
|
|
3342
|
+
documentId,
|
|
3343
|
+
scope: "global",
|
|
3344
|
+
branch: "main",
|
|
3345
|
+
actions: [],
|
|
3346
|
+
operations: [],
|
|
3347
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3348
|
+
queueHint: [],
|
|
3349
|
+
maxRetries: 3,
|
|
3350
|
+
errorHistory: [],
|
|
3351
|
+
meta: {
|
|
3352
|
+
...buildSingleJobMeta(jobId),
|
|
3353
|
+
triggerTimestampUtcMs: timestamp
|
|
3354
|
+
}
|
|
3355
|
+
};
|
|
3356
|
+
try {
|
|
3357
|
+
await this.queue.enqueue(job);
|
|
3358
|
+
} catch (error) {
|
|
3359
|
+
this.logger.error("Failed to enqueue re-evaluation of @documentId: @error", documentId, error);
|
|
3360
|
+
}
|
|
3361
|
+
}
|
|
3362
|
+
}
|
|
3363
|
+
};
|
|
3364
|
+
//#endregion
|
|
3213
3365
|
//#region src/sync/types.ts
|
|
3214
3366
|
let ChannelScheme = /* @__PURE__ */ function(ChannelScheme) {
|
|
3215
3367
|
ChannelScheme["CONNECT"] = "connect";
|
|
@@ -6385,7 +6537,7 @@ var Reactor = class {
|
|
|
6385
6537
|
documentId: document.header.id,
|
|
6386
6538
|
model: document.header.documentType,
|
|
6387
6539
|
fromVersion: 0,
|
|
6388
|
-
toVersion: document.state.document
|
|
6540
|
+
toVersion: normalizeDocumentModelVersion(document.state.document?.version),
|
|
6389
6541
|
initialState: document.state
|
|
6390
6542
|
})];
|
|
6391
6543
|
if (signer) actions = await signActions(actions, signer, signal);
|
|
@@ -7067,6 +7219,11 @@ var ReactorBuilder = class {
|
|
|
7067
7219
|
syncModule = this.syncBuilder.buildModule(reactor, this.logger, operationIndex, eventBus, database, this.driveContainerTypes);
|
|
7068
7220
|
await syncModule.syncManager.startup();
|
|
7069
7221
|
}
|
|
7222
|
+
let groupReevaluationTrigger;
|
|
7223
|
+
if (this.executorConfig.featureFlags?.authGroups) {
|
|
7224
|
+
groupReevaluationTrigger = new GroupReevaluationTrigger(this.logger, eventBus, queue, operationIndex);
|
|
7225
|
+
groupReevaluationTrigger.startup();
|
|
7226
|
+
}
|
|
7070
7227
|
const module = {
|
|
7071
7228
|
eventBus,
|
|
7072
7229
|
documentModelRegistry,
|
|
@@ -7088,6 +7245,7 @@ var ReactorBuilder = class {
|
|
|
7088
7245
|
processorManagerConsistencyTracker,
|
|
7089
7246
|
syncModule,
|
|
7090
7247
|
reactor,
|
|
7248
|
+
groupReevaluationTrigger,
|
|
7091
7249
|
pools: this.instrumentedPools
|
|
7092
7250
|
};
|
|
7093
7251
|
if (this.signalHandlersEnabled) this.attachSignalHandlers(module);
|
|
@@ -7162,7 +7320,7 @@ var ReactorBuilder = class {
|
|
|
7162
7320
|
const [{ WorkerHandle }, { createThreadTransport }, { workerEntryPath }] = await Promise.all([
|
|
7163
7321
|
import("./worker-handle-CrERzl8s.js"),
|
|
7164
7322
|
import("./transport-ByGviWdZ.js"),
|
|
7165
|
-
import("./worker-
|
|
7323
|
+
import("./worker-CbtYMIju.js").then((n) => n.n)
|
|
7166
7324
|
]);
|
|
7167
7325
|
const poolConfig = {
|
|
7168
7326
|
enabled: true,
|