@powerhousedao/reactor 6.2.2-dev.23 → 6.2.2-dev.25
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-CE-Yyezb.js → build-worker-executor-BTWIynvM.js} +2 -2
- package/dist/{build-worker-executor-CE-Yyezb.js.map → build-worker-executor-BTWIynvM.js.map} +1 -1
- package/dist/{drive-container-types-DrQMmpCa.js → drive-container-types-uigTGO3i.js} +162 -45
- package/dist/drive-container-types-uigTGO3i.js.map +1 -0
- package/dist/entry.js +3 -2
- package/dist/entry.js.map +1 -1
- package/dist/index.d.ts +72 -39
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -9
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.js +2 -2
- package/dist/{worker-BEzk4Q1I.js → worker-DhlYAOhr.js} +2 -2
- package/dist/{worker-BEzk4Q1I.js.map → worker-DhlYAOhr.js.map} +1 -1
- package/dist/{worker-handle-B1w03nRA.js → worker-handle-CrERzl8s.js} +3 -2
- package/dist/worker-handle-CrERzl8s.js.map +1 -0
- package/package.json +4 -4
- package/dist/drive-container-types-DrQMmpCa.js.map +0 -1
- package/dist/worker-handle-B1w03nRA.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { c as KyselyKeyframeStore, f as KyselyExecutionScope, g as DocumentMetaCache, h as KyselyOperationIndex, l as DocumentModelRegistry, m as KyselyWriteCache, n as REACTOR_SCHEMA, p as EventBus, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor, y as CollectionMembershipCache } from "./drive-container-types-
|
|
1
|
+
import { c as KyselyKeyframeStore, f as KyselyExecutionScope, g as DocumentMetaCache, h as KyselyOperationIndex, l as DocumentModelRegistry, m as KyselyWriteCache, n as REACTOR_SCHEMA, p as EventBus, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, u as SimpleJobExecutor, y as CollectionMembershipCache } from "./drive-container-types-uigTGO3i.js";
|
|
2
2
|
import { n as ReactorEventTypes } from "./types-DMKLa0Ok.js";
|
|
3
3
|
//#region src/executor/worker/build-worker-executor.ts
|
|
4
4
|
async function defaultLoadFactory(spec) {
|
|
@@ -80,4 +80,4 @@ async function buildWorkerExecutor(options) {
|
|
|
80
80
|
//#endregion
|
|
81
81
|
export { defaultLoadFactory as n, buildWorkerExecutor as t };
|
|
82
82
|
|
|
83
|
-
//# sourceMappingURL=build-worker-executor-
|
|
83
|
+
//# sourceMappingURL=build-worker-executor-BTWIynvM.js.map
|
package/dist/{build-worker-executor-CE-Yyezb.js.map → build-worker-executor-BTWIynvM.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-worker-executor-
|
|
1
|
+
{"version":3,"file":"build-worker-executor-BTWIynvM.js","names":[],"sources":["../src/executor/worker/build-worker-executor.ts"],"sourcesContent":["import type {\n DocumentModelModule,\n OperationWithContext,\n} from \"@powerhousedao/shared/document-model\";\nimport type { ILogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport { CollectionMembershipCache } from \"../../cache/collection-membership-cache.js\";\nimport { DocumentMetaCache } from \"../../cache/document-meta-cache.js\";\nimport { KyselyOperationIndex } from \"../../cache/kysely-operation-index.js\";\nimport { KyselyWriteCache } from \"../../cache/kysely-write-cache.js\";\nimport type { WriteCacheConfig } from \"../../cache/write-cache-types.js\";\nimport { DEFAULT_DRIVE_CONTAINER_TYPES } from \"../../core/drive-container-types.js\";\nimport type { Database } from \"../../core/types.js\";\nimport { EventBus } from \"../../events/event-bus.js\";\nimport {\n ReactorEventTypes,\n type JobWriteReadyEvent,\n} from \"../../events/types.js\";\nimport { DocumentModelRegistry } from \"../../registry/implementation.js\";\nimport type { JobMeta } from \"../../shared/types.js\";\nimport type { SignatureVerificationHandler } from \"../../signer/types.js\";\nimport { KyselyKeyframeStore } from \"../../storage/kysely/keyframe-store.js\";\nimport { KyselyOperationStore } from \"../../storage/kysely/store.js\";\nimport type { Database as StorageDatabase } from \"../../storage/kysely/types.js\";\nimport { REACTOR_SCHEMA } from \"../../storage/migrations/migrator.js\";\nimport { KyselyExecutionScope } from \"../execution-scope.js\";\nimport { SimpleJobExecutor } from \"../simple-job-executor.js\";\nimport type { JobExecutorConfig } from \"../types.js\";\nimport type {\n FactorySpec,\n InitMessage,\n ModelManifestEntry,\n} from \"./protocol.js\";\n\n/**\n * In-worker capture of the JOB_WRITE_READY event emitted by the executor.\n * The worker forwards `operations` and `jobMeta` back to the parent; the\n * parent re-enriches `collectionMemberships` at emit time.\n */\nexport type WorkerWriteReadyCapture = {\n operations: OperationWithContext[];\n jobMeta: JobMeta;\n};\n\nexport type WorkerExecutorStack = {\n executor: SimpleJobExecutor;\n registry: DocumentModelRegistry;\n /**\n * Synchronously pops the most-recent JOB_WRITE_READY captured on this\n * worker's local event bus and clears it. Returns null if the executor\n * did not produce one for this job.\n */\n takeLastWriteReady(): WorkerWriteReadyCapture | null;\n};\n\nexport type BuildWorkerExecutorOptions = {\n init: InitMessage;\n database: Kysely<Database>;\n logger: ILogger;\n executorConfig?: JobExecutorConfig;\n driveContainerTypes?: ReadonlySet<string>;\n /**\n * Override the module loader used to materialize factory specs. Tests\n * can inject a deterministic resolver instead of touching the real\n * Node module loader.\n */\n loadFactory?: (spec: FactorySpec) => Promise<unknown>;\n};\n\nexport async function defaultLoadFactory(spec: FactorySpec): Promise<unknown> {\n const ref = spec.module;\n const specifier =\n \"filePath\" in ref\n ? new URL(`file://${ref.filePath}`).href\n : ref.packageName;\n const mod = (await import(specifier)) as Record<string, unknown>;\n const exported = mod[ref.exportName];\n if (typeof exported === \"function\") {\n return (exported as (args: unknown) => unknown)(spec.initArgs);\n }\n return exported;\n}\n\nasync function loadModelManifest(\n entries: ModelManifestEntry[],\n loadFactory: (spec: FactorySpec) => Promise<unknown>,\n registry: DocumentModelRegistry,\n logger: ILogger,\n): Promise<void> {\n for (const entry of entries) {\n let module: DocumentModelModule;\n try {\n module = (await loadFactory(entry.spec)) as DocumentModelModule;\n } catch (error) {\n logger.error(\n \"worker failed to load document model: @entry @error\",\n entry,\n error,\n );\n throw error;\n }\n const [result] = registry.registerModules(module);\n if (result.status === \"error\") {\n logger.error(\n \"worker failed to register document model: @entry @error\",\n entry,\n result.error,\n );\n throw result.error;\n }\n }\n}\n\n/**\n * Assembles the in-worker storage stack plus a {@link SimpleJobExecutor}\n * bound to a pre-built Kysely instance. The parent owns the wire protocol\n * and routing; the worker owns everything below `SimpleJobExecutor`.\n *\n * The local event bus exists only to satisfy the executor's contract: its\n * JOB_WRITE_READY emissions are captured here and shipped to the parent\n * via {@link WorkerExecutorStack.takeLastWriteReady}.\n */\nexport async function buildWorkerExecutor(\n options: BuildWorkerExecutorOptions,\n): Promise<WorkerExecutorStack> {\n const { init, database: baseDatabase, logger } = options;\n const driveContainerTypes =\n options.driveContainerTypes ?? DEFAULT_DRIVE_CONTAINER_TYPES;\n const loadFactory = options.loadFactory ?? defaultLoadFactory;\n\n const registry = new DocumentModelRegistry();\n await loadModelManifest(init.models, loadFactory, registry, logger);\n\n let signatureVerifier: SignatureVerificationHandler | undefined;\n if (init.signatureVerifier) {\n try {\n signatureVerifier = (await loadFactory(\n init.signatureVerifier,\n )) as SignatureVerificationHandler;\n } catch (error) {\n logger.error(\n \"worker failed to load signature verifier: @spec @error\",\n init.signatureVerifier,\n error,\n );\n throw error;\n }\n }\n\n const database = baseDatabase.withSchema(REACTOR_SCHEMA);\n const operationStore = new KyselyOperationStore(\n database as unknown as Kysely<StorageDatabase>,\n );\n const keyframeStore = new KyselyKeyframeStore(\n database as unknown as Kysely<StorageDatabase>,\n );\n\n const cacheConfig: WriteCacheConfig = {\n maxDocuments: 100,\n ringBufferSize: 10,\n keyframeInterval: 10,\n };\n const writeCache = new KyselyWriteCache(\n keyframeStore,\n operationStore,\n registry,\n cacheConfig,\n );\n await writeCache.startup();\n\n const operationIndex = new KyselyOperationIndex(\n database as unknown as Kysely<StorageDatabase>,\n );\n\n const documentMetaCache = new DocumentMetaCache(operationStore, {\n maxDocuments: 1000,\n });\n await documentMetaCache.startup();\n\n const collectionMembershipCache = new CollectionMembershipCache(\n operationIndex,\n );\n\n const executionScope = new KyselyExecutionScope(\n database as unknown as Kysely<StorageDatabase>,\n operationStore,\n operationIndex,\n keyframeStore,\n writeCache,\n documentMetaCache,\n collectionMembershipCache,\n );\n\n const eventBus = new EventBus();\n let lastWriteReady: WorkerWriteReadyCapture | null = null;\n eventBus.subscribe(\n ReactorEventTypes.JOB_WRITE_READY,\n (_t: number, event: JobWriteReadyEvent) => {\n lastWriteReady = {\n operations: event.operations,\n jobMeta: event.jobMeta,\n };\n },\n );\n\n const executorConfig = options.executorConfig ?? {};\n const executor = new SimpleJobExecutor(\n logger,\n registry,\n operationStore,\n eventBus,\n writeCache,\n operationIndex,\n documentMetaCache,\n collectionMembershipCache,\n driveContainerTypes,\n executorConfig,\n signatureVerifier,\n executionScope,\n );\n\n return {\n executor,\n registry,\n takeLastWriteReady(): WorkerWriteReadyCapture | null {\n const captured = lastWriteReady;\n lastWriteReady = null;\n return captured;\n },\n };\n}\n"],"mappings":";;;AAqEA,eAAsB,mBAAmB,MAAqC;CAC5E,MAAM,MAAM,KAAK;CAMjB,MAAM,YADO,OAHX,cAAc,MAAA,OACV,IAAI,IAAI,UAAU,IAAI,WAAW,CAAC,QAAA,OAClC,IAAI,eAEW,IAAI;AACzB,KAAI,OAAO,aAAa,WACtB,QAAQ,SAAwC,KAAK,SAAS;AAEhE,QAAO;;AAGT,eAAe,kBACb,SACA,aACA,UACA,QACe;AACf,MAAK,MAAM,SAAS,SAAS;EAC3B,IAAI;AACJ,MAAI;AACF,YAAU,MAAM,YAAY,MAAM,KAAK;WAChC,OAAO;AACd,UAAO,MACL,uDACA,OACA,MACD;AACD,SAAM;;EAER,MAAM,CAAC,UAAU,SAAS,gBAAgB,OAAO;AACjD,MAAI,OAAO,WAAW,SAAS;AAC7B,UAAO,MACL,2DACA,OACA,OAAO,MACR;AACD,SAAM,OAAO;;;;;;;;;;;;;AAcnB,eAAsB,oBACpB,SAC8B;CAC9B,MAAM,EAAE,MAAM,UAAU,cAAc,WAAW;CACjD,MAAM,sBACJ,QAAQ,uBAAuB;CACjC,MAAM,cAAc,QAAQ,eAAe;CAE3C,MAAM,WAAW,IAAI,uBAAuB;AAC5C,OAAM,kBAAkB,KAAK,QAAQ,aAAa,UAAU,OAAO;CAEnE,IAAI;AACJ,KAAI,KAAK,kBACP,KAAI;AACF,sBAAqB,MAAM,YACzB,KAAK,kBACN;UACM,OAAO;AACd,SAAO,MACL,0DACA,KAAK,mBACL,MACD;AACD,QAAM;;CAIV,MAAM,WAAW,aAAa,WAAW,eAAe;CACxD,MAAM,iBAAiB,IAAI,qBACzB,SACD;CACD,MAAM,gBAAgB,IAAI,oBACxB,SACD;CAOD,MAAM,aAAa,IAAI,iBACrB,eACA,gBACA,UARoC;EACpC,cAAc;EACd,gBAAgB;EAChB,kBAAkB;EACnB,CAMA;AACD,OAAM,WAAW,SAAS;CAE1B,MAAM,iBAAiB,IAAI,qBACzB,SACD;CAED,MAAM,oBAAoB,IAAI,kBAAkB,gBAAgB,EAC9D,cAAc,KACf,CAAC;AACF,OAAM,kBAAkB,SAAS;CAEjC,MAAM,4BAA4B,IAAI,0BACpC,eACD;CAED,MAAM,iBAAiB,IAAI,qBACzB,UACA,gBACA,gBACA,eACA,YACA,mBACA,0BACD;CAED,MAAM,WAAW,IAAI,UAAU;CAC/B,IAAI,iBAAiD;AACrD,UAAS,UACP,kBAAkB,kBACjB,IAAY,UAA8B;AACzC,mBAAiB;GACf,YAAY,MAAM;GAClB,SAAS,MAAM;GAChB;GAEJ;AAkBD,QAAO;EACL,UAhBe,IAAI,kBACnB,QACA,UACA,gBACA,UACA,YACA,gBACA,mBACA,2BACA,qBAVqB,QAAQ,kBAAkB,EAAE,EAYjD,mBACA,eACD;EAIC;EACA,qBAAqD;GACnD,MAAM,WAAW;AACjB,oBAAiB;AACjB,UAAO;;EAEV"}
|
|
@@ -231,6 +231,8 @@ var RevisionMismatchError = class extends Error {
|
|
|
231
231
|
this.name = "RevisionMismatchError";
|
|
232
232
|
}
|
|
233
233
|
};
|
|
234
|
+
/** Error history keeps messages, not classes, so failures match by prefix. */
|
|
235
|
+
const APPEND_CONDITION_FAILED_PREFIX = "Append condition failed: ";
|
|
234
236
|
/**
|
|
235
237
|
* A read-set stream grew before the append committed. A concurrency
|
|
236
238
|
* conflict, not a fault: the caller retries against the new stream heads.
|
|
@@ -238,13 +240,17 @@ var RevisionMismatchError = class extends Error {
|
|
|
238
240
|
var AppendConditionFailedError = class extends Error {
|
|
239
241
|
constructor(condition) {
|
|
240
242
|
const streams = condition.streams.map((s) => `${s.documentId}:${s.scope}:${s.branch}@${s.revision}`).join(", ");
|
|
241
|
-
super(
|
|
243
|
+
super(`${APPEND_CONDITION_FAILED_PREFIX}a read-set stream advanced [${streams}]`);
|
|
242
244
|
this.condition = condition;
|
|
243
245
|
this.name = "AppendConditionFailedError";
|
|
244
246
|
}
|
|
245
247
|
static isError(error) {
|
|
246
248
|
return Error.isError(error) && error.name === "AppendConditionFailedError";
|
|
247
249
|
}
|
|
250
|
+
/** True when a recorded error message is an append-condition failure. */
|
|
251
|
+
static isFailureMessage(message) {
|
|
252
|
+
return message.startsWith(APPEND_CONDITION_FAILED_PREFIX);
|
|
253
|
+
}
|
|
248
254
|
};
|
|
249
255
|
//#endregion
|
|
250
256
|
//#region src/cache/collection-membership-cache.ts
|
|
@@ -939,11 +945,55 @@ var RingBuffer = class {
|
|
|
939
945
|
}
|
|
940
946
|
};
|
|
941
947
|
//#endregion
|
|
948
|
+
//#region src/cache/write-cache-types.ts
|
|
949
|
+
/**
|
|
950
|
+
* Where a snapshot sits in its stream.
|
|
951
|
+
*
|
|
952
|
+
* - `Head`: the newest revision of the stream when it was stored. Only these
|
|
953
|
+
* can answer a read that asks for the head.
|
|
954
|
+
* - `Historical`: state at an earlier revision. Usable as a starting point to
|
|
955
|
+
* replay forward from, and as an answer to a read for that same revision.
|
|
956
|
+
*/
|
|
957
|
+
let SnapshotPosition = /* @__PURE__ */ function(SnapshotPosition) {
|
|
958
|
+
SnapshotPosition["Head"] = "head";
|
|
959
|
+
SnapshotPosition["Historical"] = "historical";
|
|
960
|
+
return SnapshotPosition;
|
|
961
|
+
}({});
|
|
962
|
+
//#endregion
|
|
942
963
|
//#region src/cache/kysely-write-cache.ts
|
|
964
|
+
/**
|
|
965
|
+
* The last operation index a keyframe's document reflects for the scope. A
|
|
966
|
+
* keyframe only exists for a scope that has operations, so a missing entry
|
|
967
|
+
* means the stored row is corrupt.
|
|
968
|
+
*/
|
|
969
|
+
function keyframeRevision(keyframe, documentId, scope) {
|
|
970
|
+
const nextIndex = keyframe.document.header.revision[scope];
|
|
971
|
+
if (typeof nextIndex !== "number") throw new Error(`Corrupt keyframe for document ${documentId} at revision ${keyframe.revision}: header carries no ${scope} revision`);
|
|
972
|
+
return nextIndex - 1;
|
|
973
|
+
}
|
|
943
974
|
function extractModuleVersion(doc) {
|
|
944
975
|
const v = doc.state.document.version;
|
|
945
976
|
return v === 0 ? void 0 : v;
|
|
946
977
|
}
|
|
978
|
+
/** The highest revision held, latest push winning a tie. */
|
|
979
|
+
function highestRevision(snapshots) {
|
|
980
|
+
let newest = void 0;
|
|
981
|
+
for (const snapshot of snapshots) if (!newest || snapshot.revision >= newest.revision) newest = snapshot;
|
|
982
|
+
return newest;
|
|
983
|
+
}
|
|
984
|
+
/**
|
|
985
|
+
* Copies a document far enough that the caller cannot write through it. Inside
|
|
986
|
+
* this class, callers only ever replace whole fields on these four, so one
|
|
987
|
+
* level each is enough.
|
|
988
|
+
*/
|
|
989
|
+
function copyDocument(document) {
|
|
990
|
+
return {
|
|
991
|
+
...document,
|
|
992
|
+
header: { ...document.header },
|
|
993
|
+
state: { ...document.state },
|
|
994
|
+
operations: { ...document.operations }
|
|
995
|
+
};
|
|
996
|
+
}
|
|
947
997
|
/**
|
|
948
998
|
* In-memory write cache with keyframe persistence for PHDocuments.
|
|
949
999
|
*
|
|
@@ -1022,6 +1072,8 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1022
1072
|
/**
|
|
1023
1073
|
* Retrieves document state at a specific revision from cache or rebuilds it.
|
|
1024
1074
|
*
|
|
1075
|
+
* Note: this returns a _shallow_ copy of the document.
|
|
1076
|
+
*
|
|
1025
1077
|
* Cache hit path: Returns cached snapshot if available (O(1))
|
|
1026
1078
|
* Warm miss path: Rebuilds from cached base revision + incremental ops
|
|
1027
1079
|
* Cold miss path: Rebuilds from keyframe or from scratch using all operations
|
|
@@ -1045,30 +1097,35 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1045
1097
|
if (stream) {
|
|
1046
1098
|
const snapshots = stream.ringBuffer.getAll();
|
|
1047
1099
|
if (targetRevision === void 0) {
|
|
1048
|
-
|
|
1049
|
-
|
|
1100
|
+
const newest = highestRevision(snapshots);
|
|
1101
|
+
if (newest?.position === SnapshotPosition.Head) {
|
|
1050
1102
|
this.lruTracker.touch(streamKey);
|
|
1051
|
-
return newest.document;
|
|
1103
|
+
return copyDocument(newest.document);
|
|
1104
|
+
}
|
|
1105
|
+
if (newest) {
|
|
1106
|
+
const document = await this.warmMissRebuild(newest.document, newest.revision, documentId, scope, branch, void 0, signal);
|
|
1107
|
+
this.store(documentId, scope, branch, (document.header.revision[scope] ?? 0) - 1, document, SnapshotPosition.Head);
|
|
1108
|
+
this.lruTracker.touch(streamKey);
|
|
1109
|
+
return document;
|
|
1052
1110
|
}
|
|
1053
1111
|
} else {
|
|
1054
|
-
const exactMatch = snapshots.
|
|
1112
|
+
const exactMatch = snapshots.findLast((s) => s.revision === targetRevision);
|
|
1055
1113
|
if (exactMatch) {
|
|
1056
1114
|
this.lruTracker.touch(streamKey);
|
|
1057
|
-
return exactMatch.document;
|
|
1115
|
+
return copyDocument(exactMatch.document);
|
|
1058
1116
|
}
|
|
1059
1117
|
const newestOlder = this.findNearestOlderSnapshot(snapshots, targetRevision);
|
|
1060
1118
|
if (newestOlder) {
|
|
1061
1119
|
const document = await this.warmMissRebuild(newestOlder.document, newestOlder.revision, documentId, scope, branch, targetRevision, signal);
|
|
1062
|
-
this.
|
|
1120
|
+
this.store(documentId, scope, branch, targetRevision, document, SnapshotPosition.Historical);
|
|
1063
1121
|
this.lruTracker.touch(streamKey);
|
|
1064
1122
|
return document;
|
|
1065
1123
|
}
|
|
1066
1124
|
}
|
|
1067
1125
|
}
|
|
1068
1126
|
const document = await this.coldMissRebuild(documentId, scope, branch, targetRevision, signal);
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
this.putState(documentId, scope, branch, revision, document);
|
|
1127
|
+
const revision = targetRevision ?? (document.header.revision[scope] ?? 0) - 1;
|
|
1128
|
+
this.store(documentId, scope, branch, revision, document, targetRevision === void 0 ? SnapshotPosition.Head : SnapshotPosition.Historical);
|
|
1072
1129
|
return document;
|
|
1073
1130
|
}
|
|
1074
1131
|
/**
|
|
@@ -1091,16 +1148,20 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1091
1148
|
* @param document - The document to cache
|
|
1092
1149
|
* @throws {Error} If document serialization fails
|
|
1093
1150
|
*/
|
|
1094
|
-
putState(documentId, scope, branch, revision, document) {
|
|
1151
|
+
putState(documentId, scope, branch, revision, document, position) {
|
|
1152
|
+
this.store(documentId, scope, branch, revision, document, position);
|
|
1153
|
+
}
|
|
1154
|
+
store(documentId, scope, branch, revision, document, position) {
|
|
1095
1155
|
const streamKey = this.makeStreamKey(documentId, scope, branch);
|
|
1096
1156
|
const stream = this.getOrCreateStream(streamKey);
|
|
1097
1157
|
const snapshot = {
|
|
1098
1158
|
revision,
|
|
1099
1159
|
document: {
|
|
1100
|
-
...document,
|
|
1160
|
+
...copyDocument(document),
|
|
1101
1161
|
operations: Object.fromEntries(Object.entries(document.operations).map(([k, ops]) => [k, ops.length ? [ops.at(-1)] : []])),
|
|
1102
1162
|
clipboard: []
|
|
1103
|
-
}
|
|
1163
|
+
},
|
|
1164
|
+
position
|
|
1104
1165
|
};
|
|
1105
1166
|
stream.ringBuffer.push(snapshot);
|
|
1106
1167
|
if (this.isKeyframeRevision(revision)) this.keyframeStore.putKeyframe(documentId, scope, branch, revision, {
|
|
@@ -1168,58 +1229,75 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1168
1229
|
}
|
|
1169
1230
|
async findNearestKeyframe(documentId, scope, branch, targetRevision, signal) {
|
|
1170
1231
|
if (targetRevision === Number.MAX_SAFE_INTEGER || targetRevision <= 0) return;
|
|
1171
|
-
|
|
1232
|
+
const keyframe = await this.keyframeStore.findNearestKeyframe(documentId, scope, branch, targetRevision, signal);
|
|
1233
|
+
if (!keyframe) return;
|
|
1234
|
+
return {
|
|
1235
|
+
revision: Math.min(keyframeRevision(keyframe, documentId, scope), keyframe.revision),
|
|
1236
|
+
document: keyframe.document
|
|
1237
|
+
};
|
|
1172
1238
|
}
|
|
1173
1239
|
async coldMissRebuild(documentId, scope, branch, targetRevision, signal) {
|
|
1174
1240
|
const effectiveTargetRevision = targetRevision || Number.MAX_SAFE_INTEGER;
|
|
1175
1241
|
const keyframe = await this.findNearestKeyframe(documentId, scope, branch, effectiveTargetRevision, signal);
|
|
1242
|
+
const documentScopeBound = scope === "document" ? targetRevision : void 0;
|
|
1176
1243
|
let document;
|
|
1177
1244
|
let startRevision;
|
|
1178
1245
|
let documentType;
|
|
1179
1246
|
const validatedUpgrades = [];
|
|
1247
|
+
let lastDocumentScopeOperation;
|
|
1180
1248
|
if (keyframe) {
|
|
1181
1249
|
document = keyframe.document;
|
|
1182
1250
|
startRevision = keyframe.revision;
|
|
1183
1251
|
documentType = keyframe.document.header.documentType;
|
|
1184
|
-
const
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
if (
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1252
|
+
const documentScopeResume = scope === "document" ? keyframe.revision : keyframeRevision(keyframe, documentId, "document");
|
|
1253
|
+
const docScopeOpsAfterKeyframe = await this.operationStore.getSince(documentId, "document", branch, documentScopeResume, void 0, void 0, signal);
|
|
1254
|
+
for (const operation of docScopeOpsAfterKeyframe.results) {
|
|
1255
|
+
if (documentScopeBound !== void 0 && operation.index > documentScopeBound) break;
|
|
1256
|
+
lastDocumentScopeOperation = operation;
|
|
1257
|
+
if (operation.error) continue;
|
|
1258
|
+
if (operation.action.type === "UPGRADE_DOCUMENT") {
|
|
1259
|
+
const upgradeAction = operation.action;
|
|
1260
|
+
const fromVersion = upgradeAction.input.fromVersion;
|
|
1261
|
+
const toVersion = upgradeAction.input.toVersion;
|
|
1262
|
+
if (fromVersion > 0 && fromVersion < toVersion) {
|
|
1263
|
+
let upgradePath;
|
|
1264
|
+
try {
|
|
1265
|
+
upgradePath = this.registry.computeUpgradePath(documentType, fromVersion, toVersion);
|
|
1266
|
+
} catch (err) {
|
|
1267
|
+
if (upgradeAction.input.initialState !== void 0) upgradePath = void 0;
|
|
1268
|
+
else throw new Error(`Failed to rebuild document ${documentId}: no upgrade manifest for ${documentType} v${fromVersion}→v${toVersion} and no initialState snapshot. ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
1269
|
+
}
|
|
1270
|
+
validatedUpgrades.push({
|
|
1271
|
+
fromVersion,
|
|
1272
|
+
toVersion,
|
|
1273
|
+
revision: upgradeAction.input.revision,
|
|
1274
|
+
timestampUtcMs: operation.timestampUtcMs
|
|
1275
|
+
});
|
|
1276
|
+
document = applyUpgradeDocumentAction(document, upgradeAction, upgradePath);
|
|
1196
1277
|
}
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
toVersion,
|
|
1200
|
-
revision: upgradeAction.input.revision,
|
|
1201
|
-
timestampUtcMs: operation.timestampUtcMs
|
|
1202
|
-
});
|
|
1203
|
-
document = applyUpgradeDocumentAction(document, upgradeAction, upgradePath);
|
|
1204
|
-
}
|
|
1205
|
-
} else if (operation.action.type === "DELETE_DOCUMENT") applyDeleteDocumentAction(document, operation.action);
|
|
1278
|
+
} else if (operation.action.type === "DELETE_DOCUMENT") applyDeleteDocumentAction(document, operation.action);
|
|
1279
|
+
}
|
|
1206
1280
|
} else {
|
|
1207
1281
|
startRevision = -1;
|
|
1208
1282
|
const createOpResult = await this.operationStore.getSince(documentId, "document", branch, -1, void 0, {
|
|
1209
1283
|
cursor: "0",
|
|
1210
1284
|
limit: 1
|
|
1211
1285
|
}, signal);
|
|
1212
|
-
if (createOpResult.results.length === 0) throw new
|
|
1286
|
+
if (createOpResult.results.length === 0) throw new DocumentNotFoundError(documentId);
|
|
1213
1287
|
const createOp = createOpResult.results[0];
|
|
1214
1288
|
if (createOp.action.type !== "CREATE_DOCUMENT") throw new Error(`Failed to rebuild document ${documentId}: first operation in document scope must be CREATE_DOCUMENT, found ${createOp.action.type}`);
|
|
1215
1289
|
const documentCreateAction = createOp.action;
|
|
1216
1290
|
documentType = documentCreateAction.input.model;
|
|
1217
1291
|
if (!documentType) throw new Error(`Failed to rebuild document ${documentId}: CREATE_DOCUMENT action missing model in input`);
|
|
1218
1292
|
document = createDocumentFromAction(documentCreateAction);
|
|
1293
|
+
lastDocumentScopeOperation = createOp;
|
|
1219
1294
|
let docModule = this.registry.getModule(documentType, extractModuleVersion(document));
|
|
1220
1295
|
const docScopeOps = await this.operationStore.getSince(documentId, "document", branch, 0, void 0, void 0, signal);
|
|
1221
1296
|
for (const operation of docScopeOps.results) {
|
|
1297
|
+
if (documentScopeBound !== void 0 && operation.index > documentScopeBound) break;
|
|
1298
|
+
lastDocumentScopeOperation = operation;
|
|
1222
1299
|
if (operation.index === 0) continue;
|
|
1300
|
+
if (operation.error) continue;
|
|
1223
1301
|
if (operation.action.type === "UPGRADE_DOCUMENT") {
|
|
1224
1302
|
const upgradeAction = operation.action;
|
|
1225
1303
|
const fromVersion = upgradeAction.input.fromVersion;
|
|
@@ -1251,6 +1329,21 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1251
1329
|
}
|
|
1252
1330
|
}
|
|
1253
1331
|
}
|
|
1332
|
+
if (scope === "document") {
|
|
1333
|
+
const last = lastDocumentScopeOperation ?? await this.operationAt(documentId, "document", branch, startRevision, signal);
|
|
1334
|
+
document.operations = {
|
|
1335
|
+
...document.operations,
|
|
1336
|
+
document: last ? [last] : []
|
|
1337
|
+
};
|
|
1338
|
+
return this.stampRevisions(document, documentId, scope, branch, targetRevision, signal);
|
|
1339
|
+
}
|
|
1340
|
+
if (keyframe) {
|
|
1341
|
+
const resumeOperation = await this.operationAt(documentId, scope, branch, startRevision, signal);
|
|
1342
|
+
if (resumeOperation) document.operations = {
|
|
1343
|
+
...document.operations,
|
|
1344
|
+
[scope]: [resumeOperation]
|
|
1345
|
+
};
|
|
1346
|
+
}
|
|
1254
1347
|
const moduleCache = /* @__PURE__ */ new Map();
|
|
1255
1348
|
const getModuleCached = (version) => {
|
|
1256
1349
|
const key = version ?? 0;
|
|
@@ -1288,11 +1381,31 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1288
1381
|
throw new Error(`Failed to rebuild document ${documentId}: ${err instanceof Error ? err.message : String(err)}`, { cause: err });
|
|
1289
1382
|
}
|
|
1290
1383
|
} while (hasMorePages);
|
|
1384
|
+
return this.stampRevisions(document, documentId, scope, branch, targetRevision, signal);
|
|
1385
|
+
}
|
|
1386
|
+
/**
|
|
1387
|
+
* Copies the current document revisions onto the document. Overwrites the
|
|
1388
|
+
* requested scope revision with the target revision, if provided.
|
|
1389
|
+
*/
|
|
1390
|
+
async stampRevisions(document, documentId, scope, branch, targetRevision, signal) {
|
|
1291
1391
|
const revisions = await this.operationStore.getRevisions(documentId, branch, signal);
|
|
1292
1392
|
document.header.revision = revisions.revision;
|
|
1393
|
+
if (targetRevision !== void 0) document.header.revision = {
|
|
1394
|
+
...document.header.revision,
|
|
1395
|
+
[scope]: targetRevision + 1
|
|
1396
|
+
};
|
|
1293
1397
|
document.header.lastModifiedAtUtcIso = revisions.latestTimestamp;
|
|
1294
1398
|
return document;
|
|
1295
1399
|
}
|
|
1400
|
+
/** The stored operation at `index`, or undefined if it is no longer there. */
|
|
1401
|
+
async operationAt(documentId, scope, branch, index, signal) {
|
|
1402
|
+
if (index < 0) return;
|
|
1403
|
+
const operation = (await this.operationStore.getSince(documentId, scope, branch, index - 1, void 0, {
|
|
1404
|
+
cursor: "0",
|
|
1405
|
+
limit: 1
|
|
1406
|
+
}, signal)).results[0];
|
|
1407
|
+
return operation && operation.index === index ? operation : void 0;
|
|
1408
|
+
}
|
|
1296
1409
|
/**
|
|
1297
1410
|
* Resolves which module version to use for a given operation in phase 2.
|
|
1298
1411
|
*
|
|
@@ -1316,9 +1429,9 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1316
1429
|
async warmMissRebuild(baseDocument, baseRevision, documentId, scope, branch, targetRevision, signal) {
|
|
1317
1430
|
const documentType = baseDocument.header.documentType;
|
|
1318
1431
|
const docScopeNextIndex = baseDocument.header.revision["document"] ?? 0;
|
|
1319
|
-
if ((await this.operationStore.getSince(documentId, "document", branch, docScopeNextIndex - 1, void 0, void 0, signal)).results.
|
|
1432
|
+
if ((await this.operationStore.getSince(documentId, "document", branch, docScopeNextIndex - 1, void 0, void 0, signal)).results.length > 0) return this.coldMissRebuild(documentId, scope, branch, targetRevision, signal);
|
|
1320
1433
|
const module = this.registry.getModule(documentType, extractModuleVersion(baseDocument));
|
|
1321
|
-
let document = baseDocument;
|
|
1434
|
+
let document = copyDocument(baseDocument);
|
|
1322
1435
|
try {
|
|
1323
1436
|
const pagedResults = await this.operationStore.getSince(documentId, scope, branch, baseRevision, void 0, void 0, signal);
|
|
1324
1437
|
for (const operation of pagedResults.results) {
|
|
@@ -1336,6 +1449,10 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1336
1449
|
}
|
|
1337
1450
|
const revisions = await this.operationStore.getRevisions(documentId, branch, signal);
|
|
1338
1451
|
document.header.revision = revisions.revision;
|
|
1452
|
+
if (targetRevision !== void 0) document.header.revision = {
|
|
1453
|
+
...document.header.revision,
|
|
1454
|
+
[scope]: targetRevision + 1
|
|
1455
|
+
};
|
|
1339
1456
|
document.header.lastModifiedAtUtcIso = revisions.latestTimestamp;
|
|
1340
1457
|
return document;
|
|
1341
1458
|
}
|
|
@@ -1590,7 +1707,7 @@ var DocumentActionHandler = class {
|
|
|
1590
1707
|
...document.operations,
|
|
1591
1708
|
[job.scope]: [...document.operations[job.scope] ?? [], operation]
|
|
1592
1709
|
};
|
|
1593
|
-
stores.writeCache.putState(document.header.id, job.scope, job.branch, operation.index, document);
|
|
1710
|
+
stores.writeCache.putState(document.header.id, job.scope, job.branch, operation.index, document, SnapshotPosition.Head);
|
|
1594
1711
|
indexTxn.write([{
|
|
1595
1712
|
...operation,
|
|
1596
1713
|
documentId: document.header.id,
|
|
@@ -1642,7 +1759,7 @@ var DocumentActionHandler = class {
|
|
|
1642
1759
|
...document.operations,
|
|
1643
1760
|
[job.scope]: [...document.operations[job.scope] ?? [], operation]
|
|
1644
1761
|
};
|
|
1645
|
-
stores.writeCache.putState(documentId, job.scope, job.branch, operation.index, document);
|
|
1762
|
+
stores.writeCache.putState(documentId, job.scope, job.branch, operation.index, document, SnapshotPosition.Head);
|
|
1646
1763
|
indexTxn.write([{
|
|
1647
1764
|
...operation,
|
|
1648
1765
|
documentId,
|
|
@@ -1709,7 +1826,7 @@ var DocumentActionHandler = class {
|
|
|
1709
1826
|
...document.operations,
|
|
1710
1827
|
[job.scope]: [...document.operations[job.scope] ?? [], operation]
|
|
1711
1828
|
};
|
|
1712
|
-
stores.writeCache.putState(documentId, job.scope, job.branch, operation.index, document);
|
|
1829
|
+
stores.writeCache.putState(documentId, job.scope, job.branch, operation.index, document, SnapshotPosition.Head);
|
|
1713
1830
|
indexTxn.write([{
|
|
1714
1831
|
...operation,
|
|
1715
1832
|
documentId,
|
|
@@ -1780,7 +1897,7 @@ var DocumentActionHandler = class {
|
|
|
1780
1897
|
[job.scope]: scopeState === void 0 ? {} : structuredClone(scopeState)
|
|
1781
1898
|
};
|
|
1782
1899
|
const resultingState = JSON.stringify(resultingStateObj);
|
|
1783
|
-
stores.writeCache.putState(input.sourceId, job.scope, job.branch, operation.index, sourceDoc);
|
|
1900
|
+
stores.writeCache.putState(input.sourceId, job.scope, job.branch, operation.index, sourceDoc, SnapshotPosition.Head);
|
|
1784
1901
|
indexTxn.write([{
|
|
1785
1902
|
...operation,
|
|
1786
1903
|
documentId: input.sourceId,
|
|
@@ -2130,7 +2247,7 @@ var SimpleJobExecutor = class {
|
|
|
2130
2247
|
...updatedDocument.header.revision,
|
|
2131
2248
|
[scope]: storedOperation.index + 1
|
|
2132
2249
|
};
|
|
2133
|
-
stores.writeCache.putState(job.documentId, scope, job.branch, storedOperation.index, updatedDocument);
|
|
2250
|
+
stores.writeCache.putState(job.documentId, scope, job.branch, storedOperation.index, updatedDocument, SnapshotPosition.Head);
|
|
2134
2251
|
indexTxn.write([{
|
|
2135
2252
|
...storedOperation,
|
|
2136
2253
|
documentId: job.documentId,
|
|
@@ -3095,6 +3212,6 @@ async function getMigrationStatus(db, schema = REACTOR_SCHEMA) {
|
|
|
3095
3212
|
//#region src/core/drive-container-types.ts
|
|
3096
3213
|
const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
|
|
3097
3214
|
//#endregion
|
|
3098
|
-
export {
|
|
3215
|
+
export { DocumentDeletedError as A, OptimisticLockError as C, InvalidModuleError as D, DuplicateModuleError as E, __exportAll as F, matchesScope as M, parsePagingOptions as N, ModuleNotFoundError as O, throwIfAborted as P, DuplicateOperationError as S, DuplicateManifestError as T, createConsistencyToken as _, createForwardingPoolInstrumentation as a, APPEND_CONDITION_FAILED_PREFIX as b, KyselyKeyframeStore as c, DriveCollectionId as d, KyselyExecutionScope as f, DocumentMetaCache as g, KyselyOperationIndex as h, runMigrations as i, DocumentNotFoundError as j, AuthorizationDeniedError as k, DocumentModelRegistry as l, KyselyWriteCache as m, REACTOR_SCHEMA as n, instrumentPgPool as o, EventBus as p, getMigrationStatus as r, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, SimpleJobExecutor as u, createEmptyConsistencyToken as v, RevisionMismatchError as w, AppendConditionFailedError as x, CollectionMembershipCache as y };
|
|
3099
3216
|
|
|
3100
|
-
//# sourceMappingURL=drive-container-types-
|
|
3217
|
+
//# sourceMappingURL=drive-container-types-uigTGO3i.js.map
|