@powerhousedao/reactor 6.2.0-dev.3 → 6.2.0-dev.30
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-DDVXB921.js → build-worker-executor-DeJ2DW16.js} +2 -2
- package/dist/{build-worker-executor-DDVXB921.js.map → build-worker-executor-DeJ2DW16.js.map} +1 -1
- package/dist/{drive-container-types-BNpMlgT_.js → drive-container-types-BxnXaOAp.js} +128 -104
- package/dist/drive-container-types-BxnXaOAp.js.map +1 -0
- package/dist/entry.js +2 -2
- package/dist/index.d.ts +84 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +102 -24
- package/dist/index.js.map +1 -1
- package/dist/projection-entry.js +2 -2
- package/dist/{worker-SUoDhurA.js → worker-XYrQaEmt.js} +2 -2
- package/dist/{worker-SUoDhurA.js.map → worker-XYrQaEmt.js.map} +1 -1
- package/package.json +19 -7
- package/dist/drive-container-types-BNpMlgT_.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as CollectionMembershipCache, _ as KyselyWriteCache, d as KyselyKeyframeStore, f as DocumentModelRegistry, g as EventBus, h as KyselyExecutionScope, n as REACTOR_SCHEMA, p as SimpleJobExecutor, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, v as KyselyOperationIndex, y as DocumentMetaCache } from "./drive-container-types-
|
|
1
|
+
import { S as CollectionMembershipCache, _ as KyselyWriteCache, d as KyselyKeyframeStore, f as DocumentModelRegistry, g as EventBus, h as KyselyExecutionScope, n as REACTOR_SCHEMA, p as SimpleJobExecutor, s as KyselyOperationStore, t as DEFAULT_DRIVE_CONTAINER_TYPES, v as KyselyOperationIndex, y as DocumentMetaCache } from "./drive-container-types-BxnXaOAp.js";
|
|
2
2
|
import { n as ReactorEventTypes } from "./types-CxSpmNGK.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-DeJ2DW16.js.map
|
package/dist/{build-worker-executor-DDVXB921.js.map → build-worker-executor-DeJ2DW16.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build-worker-executor-
|
|
1
|
+
{"version":3,"file":"build-worker-executor-DeJ2DW16.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 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 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;AACF,sBAAqB,MAAM,YACzB,KAAK,kBACN;UACM,OAAO;AACd,SAAO,MACL,0DACA,KAAK,mBACL,MACD;AACD,QAAM;;CAGR,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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { n as ReactorEventTypes, t as EventBusAggregateError } from "./types-CxSpmNGK.js";
|
|
2
|
-
import { createPresignedHeader, defaultBaseState, deriveOperationId, isUndoRedo } from "@powerhousedao/shared/document-model";
|
|
2
|
+
import { DowngradeNotSupportedError, applyDeleteDocumentAction, applyDeleteDocumentAction as applyDeleteDocumentAction$1, applyUpgradeDocumentAction, applyUpgradeDocumentAction as applyUpgradeDocumentAction$1, createPresignedHeader, defaultBaseState, deriveOperationId, isUndoRedo } from "@powerhousedao/shared/document-model";
|
|
3
3
|
import { v4 } from "uuid";
|
|
4
4
|
import { Migrator, sql } from "kysely";
|
|
5
5
|
//#region \0rolldown/runtime.js
|
|
@@ -86,22 +86,6 @@ var InvalidSignatureError = class InvalidSignatureError extends Error {
|
|
|
86
86
|
}
|
|
87
87
|
};
|
|
88
88
|
/**
|
|
89
|
-
* Error thrown when attempting to downgrade a document version.
|
|
90
|
-
*/
|
|
91
|
-
var DowngradeNotSupportedError$1 = class DowngradeNotSupportedError$1 extends Error {
|
|
92
|
-
documentType;
|
|
93
|
-
fromVersion;
|
|
94
|
-
toVersion;
|
|
95
|
-
constructor(documentType, fromVersion, toVersion) {
|
|
96
|
-
super(`Downgrade not supported for ${documentType}: cannot upgrade from version ${fromVersion} to ${toVersion}`);
|
|
97
|
-
this.name = "DowngradeNotSupportedError";
|
|
98
|
-
this.documentType = documentType;
|
|
99
|
-
this.fromVersion = fromVersion;
|
|
100
|
-
this.toVersion = toVersion;
|
|
101
|
-
Error.captureStackTrace(this, DowngradeNotSupportedError$1);
|
|
102
|
-
}
|
|
103
|
-
};
|
|
104
|
-
/**
|
|
105
89
|
* Error thrown when a document is not found (no operations exist for the document ID).
|
|
106
90
|
*/
|
|
107
91
|
var DocumentNotFoundError = class DocumentNotFoundError extends Error {
|
|
@@ -179,15 +163,6 @@ var ManifestNotFoundError = class extends Error {
|
|
|
179
163
|
}
|
|
180
164
|
};
|
|
181
165
|
/**
|
|
182
|
-
* Error thrown when attempting a downgrade operation.
|
|
183
|
-
*/
|
|
184
|
-
var DowngradeNotSupportedError = class extends Error {
|
|
185
|
-
constructor(documentType, fromVersion, toVersion) {
|
|
186
|
-
super(`Downgrade not supported for ${documentType}: cannot go from version ${fromVersion} to ${toVersion}`);
|
|
187
|
-
this.name = "DowngradeNotSupportedError";
|
|
188
|
-
}
|
|
189
|
-
};
|
|
190
|
-
/**
|
|
191
166
|
* Error thrown when a required upgrade transition is missing from the manifest.
|
|
192
167
|
*/
|
|
193
168
|
var MissingUpgradeTransitionError = class extends Error {
|
|
@@ -277,68 +252,6 @@ function createDocumentFromAction(action) {
|
|
|
277
252
|
};
|
|
278
253
|
}
|
|
279
254
|
/**
|
|
280
|
-
* Applies an UPGRADE_DOCUMENT action to a document.
|
|
281
|
-
* Handles all upgrade scenarios including initial upgrades, no-ops, and multi-step upgrades.
|
|
282
|
-
*
|
|
283
|
-
* Behavior based on fromVersion/toVersion:
|
|
284
|
-
* - fromVersion === toVersion (and fromVersion > 0): No-op - return unchanged document
|
|
285
|
-
* - fromVersion > toVersion: Throw DowngradeNotSupportedError
|
|
286
|
-
* - All other cases: Apply upgradePath transitions (if provided), then apply initialState, set version
|
|
287
|
-
*
|
|
288
|
-
* The initialState from the action is always applied (if provided) to maintain backward
|
|
289
|
-
* compatibility with the original implementation.
|
|
290
|
-
*
|
|
291
|
-
* @param document - The document to upgrade
|
|
292
|
-
* @param action - The UPGRADE_DOCUMENT action
|
|
293
|
-
* @param upgradePath - Optional pre-computed upgrade path for multi-step upgrades
|
|
294
|
-
* @returns The upgraded document (unchanged if no-op)
|
|
295
|
-
* @throws DowngradeNotSupportedError if attempting to downgrade
|
|
296
|
-
*/
|
|
297
|
-
function applyUpgradeDocumentAction(document, action, upgradePath) {
|
|
298
|
-
const fromVersion = action.input.fromVersion;
|
|
299
|
-
const toVersion = action.input.toVersion;
|
|
300
|
-
if (fromVersion === toVersion && fromVersion > 0) return document;
|
|
301
|
-
if (fromVersion > toVersion) throw new DowngradeNotSupportedError$1(document.header.documentType, fromVersion, toVersion);
|
|
302
|
-
if (upgradePath) for (const transition of upgradePath) document = transition.upgradeReducer(document, action);
|
|
303
|
-
applyInitialState(document, action);
|
|
304
|
-
document.state.document = {
|
|
305
|
-
...document.state.document,
|
|
306
|
-
version: toVersion
|
|
307
|
-
};
|
|
308
|
-
return document;
|
|
309
|
-
}
|
|
310
|
-
function applyInitialState(document, action) {
|
|
311
|
-
const input = action.input;
|
|
312
|
-
const newState = input.initialState || input.state;
|
|
313
|
-
if (newState) {
|
|
314
|
-
document.state = {
|
|
315
|
-
...document.state,
|
|
316
|
-
...newState
|
|
317
|
-
};
|
|
318
|
-
document.initialState = document.state;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Applies a DELETE_DOCUMENT action to a document.
|
|
323
|
-
* Marks the document as deleted in the document scope state.
|
|
324
|
-
*
|
|
325
|
-
* @param document - The document to mark as deleted
|
|
326
|
-
* @param action - The DELETE_DOCUMENT action
|
|
327
|
-
* @returns The updated document (mutates in place and returns for convenience)
|
|
328
|
-
*/
|
|
329
|
-
function applyDeleteDocumentAction(document, action) {
|
|
330
|
-
const deletedAt = action.timestampUtcMs || (/* @__PURE__ */ new Date()).toISOString();
|
|
331
|
-
document.state = {
|
|
332
|
-
...document.state,
|
|
333
|
-
document: {
|
|
334
|
-
...document.state.document,
|
|
335
|
-
isDeleted: true,
|
|
336
|
-
deletedAtUtcIso: deletedAt
|
|
337
|
-
}
|
|
338
|
-
};
|
|
339
|
-
return document;
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
255
|
* Calculate the next operation index for a specific scope.
|
|
343
256
|
* Each scope maintains its own independent index sequence.
|
|
344
257
|
*
|
|
@@ -616,8 +529,8 @@ var DocumentMetaCache = class DocumentMetaCache {
|
|
|
616
529
|
documentScopeRevision = op.index;
|
|
617
530
|
if (op.action.type === "UPGRADE_DOCUMENT") {
|
|
618
531
|
const upgradeAction = op.action;
|
|
619
|
-
document = applyUpgradeDocumentAction(document, upgradeAction);
|
|
620
|
-
} else if (op.action.type === "DELETE_DOCUMENT") document = applyDeleteDocumentAction(document, op.action);
|
|
532
|
+
document = applyUpgradeDocumentAction$1(document, upgradeAction);
|
|
533
|
+
} else if (op.action.type === "DELETE_DOCUMENT") document = applyDeleteDocumentAction$1(document, op.action);
|
|
621
534
|
}
|
|
622
535
|
return {
|
|
623
536
|
state: document.state.document,
|
|
@@ -1201,10 +1114,33 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1201
1114
|
let document;
|
|
1202
1115
|
let startRevision;
|
|
1203
1116
|
let documentType;
|
|
1117
|
+
const validatedUpgrades = [];
|
|
1204
1118
|
if (keyframe) {
|
|
1205
1119
|
document = keyframe.document;
|
|
1206
1120
|
startRevision = keyframe.revision;
|
|
1207
1121
|
documentType = keyframe.document.header.documentType;
|
|
1122
|
+
const docScopeOpsAfterKeyframe = await this.operationStore.getSince(documentId, "document", branch, keyframe.revision, void 0, void 0, signal);
|
|
1123
|
+
for (const operation of docScopeOpsAfterKeyframe.results) if (operation.action.type === "UPGRADE_DOCUMENT") {
|
|
1124
|
+
const upgradeAction = operation.action;
|
|
1125
|
+
const fromVersion = upgradeAction.input.fromVersion;
|
|
1126
|
+
const toVersion = upgradeAction.input.toVersion;
|
|
1127
|
+
if (fromVersion > 0 && fromVersion < toVersion) {
|
|
1128
|
+
let upgradePath;
|
|
1129
|
+
try {
|
|
1130
|
+
upgradePath = this.registry.computeUpgradePath(documentType, fromVersion, toVersion);
|
|
1131
|
+
} catch (err) {
|
|
1132
|
+
if (upgradeAction.input.initialState !== void 0) upgradePath = void 0;
|
|
1133
|
+
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 });
|
|
1134
|
+
}
|
|
1135
|
+
validatedUpgrades.push({
|
|
1136
|
+
fromVersion,
|
|
1137
|
+
toVersion,
|
|
1138
|
+
revision: upgradeAction.input.revision,
|
|
1139
|
+
timestampUtcMs: operation.timestampUtcMs
|
|
1140
|
+
});
|
|
1141
|
+
document = applyUpgradeDocumentAction(document, upgradeAction, upgradePath);
|
|
1142
|
+
}
|
|
1143
|
+
} else if (operation.action.type === "DELETE_DOCUMENT") applyDeleteDocumentAction(document, operation.action);
|
|
1208
1144
|
} else {
|
|
1209
1145
|
startRevision = -1;
|
|
1210
1146
|
const createOpResult = await this.operationStore.getSince(documentId, "document", branch, -1, void 0, {
|
|
@@ -1224,7 +1160,24 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1224
1160
|
if (operation.index === 0) continue;
|
|
1225
1161
|
if (operation.action.type === "UPGRADE_DOCUMENT") {
|
|
1226
1162
|
const upgradeAction = operation.action;
|
|
1227
|
-
|
|
1163
|
+
const fromVersion = upgradeAction.input.fromVersion;
|
|
1164
|
+
const toVersion = upgradeAction.input.toVersion;
|
|
1165
|
+
let upgradePath;
|
|
1166
|
+
if (fromVersion > 0 && fromVersion < toVersion) {
|
|
1167
|
+
try {
|
|
1168
|
+
upgradePath = this.registry.computeUpgradePath(documentType, fromVersion, toVersion);
|
|
1169
|
+
} catch (err) {
|
|
1170
|
+
if (upgradeAction.input.initialState !== void 0) upgradePath = void 0;
|
|
1171
|
+
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 });
|
|
1172
|
+
}
|
|
1173
|
+
validatedUpgrades.push({
|
|
1174
|
+
fromVersion,
|
|
1175
|
+
toVersion,
|
|
1176
|
+
revision: upgradeAction.input.revision,
|
|
1177
|
+
timestampUtcMs: operation.timestampUtcMs
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1180
|
+
document = applyUpgradeDocumentAction(document, upgradeAction, upgradePath);
|
|
1228
1181
|
docModule = this.registry.getModule(documentType, extractModuleVersion(document));
|
|
1229
1182
|
} else if (operation.action.type === "DELETE_DOCUMENT") applyDeleteDocumentAction(document, operation.action);
|
|
1230
1183
|
else {
|
|
@@ -1236,7 +1189,16 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1236
1189
|
}
|
|
1237
1190
|
}
|
|
1238
1191
|
}
|
|
1239
|
-
const
|
|
1192
|
+
const moduleCache = /* @__PURE__ */ new Map();
|
|
1193
|
+
const getModuleCached = (version) => {
|
|
1194
|
+
const key = version ?? 0;
|
|
1195
|
+
let mod = moduleCache.get(key);
|
|
1196
|
+
if (!mod) {
|
|
1197
|
+
mod = this.registry.getModule(documentType, version);
|
|
1198
|
+
moduleCache.set(key, mod);
|
|
1199
|
+
}
|
|
1200
|
+
return mod;
|
|
1201
|
+
};
|
|
1240
1202
|
let cursor = void 0;
|
|
1241
1203
|
const pageSize = 100;
|
|
1242
1204
|
let hasMorePages;
|
|
@@ -1250,8 +1212,9 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1250
1212
|
const result = await this.operationStore.getSince(documentId, scope, branch, startRevision, void 0, paging, signal);
|
|
1251
1213
|
for (const operation of result.results) {
|
|
1252
1214
|
if (targetRevision !== void 0 && operation.index > targetRevision) break;
|
|
1215
|
+
const moduleVersion = this.resolveModuleVersionForOp(operation.index, operation.timestampUtcMs, scope, validatedUpgrades, extractModuleVersion(document));
|
|
1253
1216
|
const protocolVersion = document.header.protocolVersions?.["base-reducer"] ?? 1;
|
|
1254
|
-
document =
|
|
1217
|
+
document = getModuleCached(moduleVersion).reducer(document, operation.action, void 0, {
|
|
1255
1218
|
skip: operation.skip,
|
|
1256
1219
|
protocolVersion
|
|
1257
1220
|
});
|
|
@@ -1268,9 +1231,31 @@ var KyselyWriteCache = class KyselyWriteCache {
|
|
|
1268
1231
|
document.header.lastModifiedAtUtcIso = revisions.latestTimestamp;
|
|
1269
1232
|
return document;
|
|
1270
1233
|
}
|
|
1234
|
+
/**
|
|
1235
|
+
* Resolves which module version to use for a given operation in phase 2.
|
|
1236
|
+
*
|
|
1237
|
+
* Uses the validated-upgrade boundary rules from D7:
|
|
1238
|
+
* - If `input.revision` is present: op.index < revision[scope] → before the upgrade boundary
|
|
1239
|
+
* - Otherwise: timestamp fallback
|
|
1240
|
+
* - Falls back to final module version when neither is decidable
|
|
1241
|
+
*/
|
|
1242
|
+
resolveModuleVersionForOp(opIndex, opTimestamp, scope, validatedUpgrades, finalVersion) {
|
|
1243
|
+
if (validatedUpgrades.length === 0) return finalVersion;
|
|
1244
|
+
let currentVersion = validatedUpgrades[0]?.fromVersion;
|
|
1245
|
+
for (const upgrade of validatedUpgrades) {
|
|
1246
|
+
let beforeUpgrade;
|
|
1247
|
+
if (upgrade.revision !== void 0) beforeUpgrade = opIndex < (upgrade.revision[scope] ?? 0);
|
|
1248
|
+
else beforeUpgrade = opTimestamp < upgrade.timestampUtcMs;
|
|
1249
|
+
if (beforeUpgrade) return currentVersion;
|
|
1250
|
+
currentVersion = upgrade.toVersion;
|
|
1251
|
+
}
|
|
1252
|
+
return currentVersion;
|
|
1253
|
+
}
|
|
1271
1254
|
async warmMissRebuild(baseDocument, baseRevision, documentId, scope, branch, targetRevision, signal) {
|
|
1272
1255
|
const documentType = baseDocument.header.documentType;
|
|
1273
|
-
const
|
|
1256
|
+
const docScopeNextIndex = baseDocument.header.revision["document"] ?? 0;
|
|
1257
|
+
if ((await this.operationStore.getSince(documentId, "document", branch, docScopeNextIndex - 1, void 0, void 0, signal)).results.some((op) => op.action.type === "UPGRADE_DOCUMENT")) return this.coldMissRebuild(documentId, scope, branch, targetRevision, signal);
|
|
1258
|
+
const module = this.registry.getModule(documentType, extractModuleVersion(baseDocument));
|
|
1274
1259
|
let document = baseDocument;
|
|
1275
1260
|
try {
|
|
1276
1261
|
const pagedResults = await this.operationStore.getSince(documentId, scope, branch, baseRevision, void 0, void 0, signal);
|
|
@@ -1456,9 +1441,48 @@ function reshuffleByTimestamp(startIndex, opsA, opsB) {
|
|
|
1456
1441
|
}
|
|
1457
1442
|
//#endregion
|
|
1458
1443
|
//#region src/cache/operation-index-types.ts
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1444
|
+
const DRIVE_COLLECTION_PREFIX = "drive.";
|
|
1445
|
+
/**
|
|
1446
|
+
* Identifies the collection a remote synchronizes. Collections are drive-level
|
|
1447
|
+
* abstractions (document-drive and reactor-drive), so a collection id is the
|
|
1448
|
+
* drive document id plus the branch it scopes to rather than an opaque string.
|
|
1449
|
+
*
|
|
1450
|
+
* The canonical string form (`drive.${branch}.${driveId}`) is produced only by
|
|
1451
|
+
* `key` and parsed only by `fromKey`; that string is the wire and storage
|
|
1452
|
+
* representation and is byte-for-byte identical to the legacy
|
|
1453
|
+
* `driveCollectionId(branch, driveId)` output, so existing `document_collections`
|
|
1454
|
+
* rows and persisted remotes remain valid without migration.
|
|
1455
|
+
*/
|
|
1456
|
+
var DriveCollectionId = class DriveCollectionId {
|
|
1457
|
+
constructor(driveId, branch) {
|
|
1458
|
+
this.driveId = driveId;
|
|
1459
|
+
this.branch = branch;
|
|
1460
|
+
}
|
|
1461
|
+
static forDrive(driveId, branch = "main") {
|
|
1462
|
+
return new DriveCollectionId(driveId, branch);
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* The single deserializer for the wire/storage form. `branch` may contain
|
|
1466
|
+
* dots, while `driveId` is a dot-free document id, so the drive id is the
|
|
1467
|
+
* final dot-delimited segment.
|
|
1468
|
+
*/
|
|
1469
|
+
static fromKey(key) {
|
|
1470
|
+
if (!key.startsWith(DRIVE_COLLECTION_PREFIX)) throw new Error(`Unsupported collection id: ${key}`);
|
|
1471
|
+
const rest = key.slice(6);
|
|
1472
|
+
const lastDot = rest.lastIndexOf(".");
|
|
1473
|
+
if (lastDot === -1 || lastDot === rest.length - 1) throw new Error(`Malformed drive collection id: ${key}`);
|
|
1474
|
+
return new DriveCollectionId(rest.slice(lastDot + 1), rest.slice(0, lastDot));
|
|
1475
|
+
}
|
|
1476
|
+
get key() {
|
|
1477
|
+
return `${DRIVE_COLLECTION_PREFIX}${this.branch}.${this.driveId}`;
|
|
1478
|
+
}
|
|
1479
|
+
toString() {
|
|
1480
|
+
return this.key;
|
|
1481
|
+
}
|
|
1482
|
+
equals(other) {
|
|
1483
|
+
return this.driveId === other.driveId && this.branch === other.branch;
|
|
1484
|
+
}
|
|
1485
|
+
};
|
|
1462
1486
|
//#endregion
|
|
1463
1487
|
//#region src/executor/document-action-handler.ts
|
|
1464
1488
|
var DocumentActionHandler = class {
|
|
@@ -1514,7 +1538,7 @@ var DocumentActionHandler = class {
|
|
|
1514
1538
|
sourceRemote
|
|
1515
1539
|
}]);
|
|
1516
1540
|
if (this.driveContainerTypes.has(document.header.documentType)) {
|
|
1517
|
-
const collectionId =
|
|
1541
|
+
const collectionId = DriveCollectionId.forDrive(document.header.id, job.branch).key;
|
|
1518
1542
|
indexTxn.createCollection(collectionId);
|
|
1519
1543
|
indexTxn.addToCollection(collectionId, document.header.id);
|
|
1520
1544
|
}
|
|
@@ -1542,7 +1566,7 @@ var DocumentActionHandler = class {
|
|
|
1542
1566
|
scope: job.scope,
|
|
1543
1567
|
branch: job.branch
|
|
1544
1568
|
});
|
|
1545
|
-
applyDeleteDocumentAction(document, action);
|
|
1569
|
+
applyDeleteDocumentAction$1(document, action);
|
|
1546
1570
|
const resultingStateObj = {
|
|
1547
1571
|
header: document.header,
|
|
1548
1572
|
document: document.state.document
|
|
@@ -1601,7 +1625,7 @@ var DocumentActionHandler = class {
|
|
|
1601
1625
|
duration: Date.now() - startTime
|
|
1602
1626
|
};
|
|
1603
1627
|
try {
|
|
1604
|
-
document = applyUpgradeDocumentAction(document, action, upgradePath);
|
|
1628
|
+
document = applyUpgradeDocumentAction$1(document, action, upgradePath);
|
|
1605
1629
|
} catch (error) {
|
|
1606
1630
|
return buildErrorResult(job, error instanceof Error ? error : new Error(String(error)), startTime);
|
|
1607
1631
|
}
|
|
@@ -1642,7 +1666,7 @@ var DocumentActionHandler = class {
|
|
|
1642
1666
|
executeAddRelationship(job, action, startTime, indexTxn, stores, sourceRemote = "", signal) {
|
|
1643
1667
|
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 }) => {
|
|
1644
1668
|
if (this.driveContainerTypes.has(sourceDoc.header.documentType)) {
|
|
1645
|
-
const collectionId =
|
|
1669
|
+
const collectionId = DriveCollectionId.forDrive(input.sourceId, j.branch).key;
|
|
1646
1670
|
txn.addToCollection(collectionId, input.targetId);
|
|
1647
1671
|
s.collectionMembershipCache.invalidate(input.targetId);
|
|
1648
1672
|
}
|
|
@@ -1651,7 +1675,7 @@ var DocumentActionHandler = class {
|
|
|
1651
1675
|
executeRemoveRelationship(job, action, startTime, indexTxn, stores, sourceRemote = "", signal) {
|
|
1652
1676
|
return this.withRelationshipAction("REMOVE_RELATIONSHIP", job, action, startTime, indexTxn, stores, sourceRemote, signal, null, ({ indexTxn: txn, stores: s, sourceDoc, input, job: j }) => {
|
|
1653
1677
|
if (this.driveContainerTypes.has(sourceDoc.header.documentType)) {
|
|
1654
|
-
const collectionId =
|
|
1678
|
+
const collectionId = DriveCollectionId.forDrive(input.sourceId, j.branch).key;
|
|
1655
1679
|
txn.removeFromCollection(collectionId, input.targetId);
|
|
1656
1680
|
s.collectionMembershipCache.invalidate(input.targetId);
|
|
1657
1681
|
}
|
|
@@ -2959,6 +2983,6 @@ async function getMigrationStatus(db, schema = REACTOR_SCHEMA) {
|
|
|
2959
2983
|
//#region src/core/drive-container-types.ts
|
|
2960
2984
|
const DEFAULT_DRIVE_CONTAINER_TYPES = new Set(["powerhouse/document-drive", "powerhouse/reactor-drive"]);
|
|
2961
2985
|
//#endregion
|
|
2962
|
-
export { parsePagingOptions as A, DuplicateManifestError as C, DocumentDeletedError as D, ModuleNotFoundError as E, __exportAll as M, DocumentNotFoundError as O, CollectionMembershipCache as S, InvalidModuleError as T, KyselyWriteCache as _, createForwardingPoolInstrumentation as a, createConsistencyToken as b, DuplicateOperationError as c, KyselyKeyframeStore as d, DocumentModelRegistry as f, EventBus as g, KyselyExecutionScope as h, runMigrations as i, throwIfAborted as j, matchesScope as k, OptimisticLockError as l,
|
|
2986
|
+
export { parsePagingOptions as A, DuplicateManifestError as C, DocumentDeletedError as D, ModuleNotFoundError as E, __exportAll as M, DocumentNotFoundError as O, CollectionMembershipCache as S, InvalidModuleError as T, KyselyWriteCache as _, createForwardingPoolInstrumentation as a, createConsistencyToken as b, DuplicateOperationError as c, KyselyKeyframeStore as d, DocumentModelRegistry as f, EventBus as g, KyselyExecutionScope as h, runMigrations as i, throwIfAborted as j, matchesScope as k, OptimisticLockError as l, DriveCollectionId as m, REACTOR_SCHEMA as n, instrumentPgPool as o, SimpleJobExecutor as p, getMigrationStatus as r, KyselyOperationStore as s, DEFAULT_DRIVE_CONTAINER_TYPES as t, RevisionMismatchError as u, KyselyOperationIndex as v, DuplicateModuleError as w, createEmptyConsistencyToken as x, DocumentMetaCache as y };
|
|
2963
2987
|
|
|
2964
|
-
//# sourceMappingURL=drive-container-types-
|
|
2988
|
+
//# sourceMappingURL=drive-container-types-BxnXaOAp.js.map
|