@powerhousedao/reactor 6.2.3-dev.49 → 6.2.3-dev.50

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.
Files changed (28) hide show
  1. package/dist/{document-indexer-Dc_Sv6yi.js → document-indexer-DwAMDVRI.js} +2 -2
  2. package/dist/{document-indexer-Dc_Sv6yi.js.map → document-indexer-DwAMDVRI.js.map} +1 -1
  3. package/dist/{drive-container-types-D380mcB3.js → drive-container-types-Ci1pdv4X.js} +9 -9
  4. package/dist/{drive-container-types-D380mcB3.js.map → drive-container-types-Ci1pdv4X.js.map} +1 -1
  5. package/dist/entry.js +3 -2
  6. package/dist/entry.js.map +1 -1
  7. package/dist/{load-spec-D9lnje-H.js → forwarding-logger-ByfBFgMS.js} +9 -248
  8. package/dist/forwarding-logger-ByfBFgMS.js.map +1 -0
  9. package/dist/{index-Dq2f7oAC.d.ts → index-CNhT69De.d.ts} +34 -5
  10. package/dist/index-CNhT69De.d.ts.map +1 -0
  11. package/dist/index.d.ts +126 -26
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +1065 -157
  14. package/dist/index.js.map +1 -1
  15. package/dist/load-spec-ZLjuyhxz.js +246 -0
  16. package/dist/load-spec-ZLjuyhxz.js.map +1 -0
  17. package/dist/projection-entry.js +3 -2
  18. package/dist/projection-entry.js.map +1 -1
  19. package/dist/{projection-shard-manager-MRJ9BjVM.js → projection-shard-manager-CQ5FwQmv.js} +2 -2
  20. package/dist/{projection-shard-manager-MRJ9BjVM.js.map → projection-shard-manager-CQ5FwQmv.js.map} +1 -1
  21. package/dist/rpc.d.ts +2 -2
  22. package/dist/rpc.js +3 -2
  23. package/dist/rpc.js.map +1 -1
  24. package/dist/{worker-handle-B48V1hDm.js → worker-handle-CN2-oRv-.js} +2 -2
  25. package/dist/{worker-handle-B48V1hDm.js.map → worker-handle-CN2-oRv-.js.map} +1 -1
  26. package/package.json +13 -13
  27. package/dist/index-Dq2f7oAC.d.ts.map +0 -1
  28. package/dist/load-spec-D9lnje-H.js.map +0 -1
package/dist/entry.js CHANGED
@@ -1,5 +1,6 @@
1
- import { A as CollectionMembershipCache, At as ReactorEventTypes, _ as KyselyOperationIndex, d as instrumentPgPool, f as KyselyOperationStore, g as KyselyWriteCache, h as EventBus, i as errorToInfo, k as DocumentMetaCache, m as DocumentModelRegistry, n as loadDocumentModelSpec, p as KyselyKeyframeStore, r as createForwardingLogger, s as REACTOR_SCHEMA, t as defaultLoadFactory } from "./load-spec-D9lnje-H.js";
2
- import { a as KyselyExecutionScope, n as SimpleJobExecutor, t as DEFAULT_DRIVE_CONTAINER_TYPES } from "./drive-container-types-D380mcB3.js";
1
+ import { D as CollectionMembershipCache, E as DocumentMetaCache, St as ReactorEventTypes, a as REACTOR_SCHEMA, d as KyselyKeyframeStore, f as EventBus, l as instrumentPgPool, m as KyselyOperationIndex, n as errorToInfo, p as KyselyWriteCache, t as createForwardingLogger, u as KyselyOperationStore } from "./forwarding-logger-ByfBFgMS.js";
2
+ import { a as KyselyExecutionScope, n as SimpleJobExecutor, t as DEFAULT_DRIVE_CONTAINER_TYPES } from "./drive-container-types-Ci1pdv4X.js";
3
+ import { n as loadDocumentModelSpec, r as DocumentModelRegistry, t as defaultLoadFactory } from "./load-spec-ZLjuyhxz.js";
3
4
  import { ConsoleLogger } from "document-model";
4
5
  import { isMainThread, parentPort } from "node:worker_threads";
5
6
  //#region src/executor/worker/build-worker-executor.ts
package/dist/entry.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"entry.js","names":[],"sources":["../src/executor/worker/build-worker-executor.ts","../src/executor/worker/run-worker.ts","../src/executor/worker/entry.ts"],"sourcesContent":["import type {\n DocumentModelModule,\n ISigner,\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 type SignatureRefusedEvent,\n} from \"../../events/types.js\";\nimport { DocumentModelRegistry } from \"../../registry/implementation.js\";\nimport type { JobMeta } from \"../../shared/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 { SignatureTrustPolicy } from \"../../signer/types.js\";\nimport type { JobExecutorConfig } from \"../types.js\";\nimport type {\n FactorySpec,\n InitMessage,\n ModelManifestEntry,\n} from \"./protocol.js\";\nimport { defaultLoadFactory } from \"./load-spec.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 /** Drains the refusals this worker's executor emitted since the last call. */\n takeSignatureRefusals(): SignatureRefusedEvent[];\n /** Drops purged ids from this worker's write and meta caches. */\n evictPurged(documentIds: string[]): void;\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\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 signer: ISigner | undefined;\n if (init.signer) {\n try {\n signer = (await loadFactory(init.signer)) as ISigner;\n } catch (error) {\n logger.error(\n \"worker failed to load signer: @spec @error\",\n init.signer.module,\n error,\n );\n throw error;\n }\n }\n\n let trustPolicy: SignatureTrustPolicy | undefined;\n if (init.trustPolicy) {\n try {\n trustPolicy = (await loadFactory(\n init.trustPolicy,\n )) as SignatureTrustPolicy;\n } catch (error) {\n logger.error(\n \"worker failed to load trust policy: @spec @error\",\n init.trustPolicy.module,\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 let signatureRefusals: SignatureRefusedEvent[] = [];\n eventBus.subscribe(\n ReactorEventTypes.SIGNATURE_REFUSED,\n (_t: number, event: SignatureRefusedEvent) => {\n signatureRefusals.push(event);\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 executionScope,\n signer,\n trustPolicy,\n );\n\n return {\n executor,\n registry,\n takeLastWriteReady(): WorkerWriteReadyCapture | null {\n const captured = lastWriteReady;\n lastWriteReady = null;\n return captured;\n },\n takeSignatureRefusals(): SignatureRefusedEvent[] {\n const captured = signatureRefusals;\n signatureRefusals = [];\n return captured;\n },\n evictPurged(documentIds: string[]): void {\n for (const documentId of documentIds) {\n writeCache.invalidate(documentId);\n documentMetaCache.invalidate(documentId);\n }\n },\n };\n}\n","import type { DocumentModelModule } from \"@powerhousedao/shared/document-model\";\nimport { ConsoleLogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { MessagePort } from \"node:worker_threads\";\nimport type { Database } from \"../../core/types.js\";\nimport type { Job } from \"../../queue/types.js\";\nimport {\n instrumentPgPool,\n type PoolInstrumentation,\n} from \"../../storage/pool-instrumentation.js\";\nimport {\n buildWorkerExecutor,\n type BuildWorkerExecutorOptions,\n type WorkerExecutorStack,\n} from \"./build-worker-executor.js\";\nimport { createForwardingLogger } from \"./forwarding-logger.js\";\nimport { defaultLoadFactory, loadDocumentModelSpec } from \"./load-spec.js\";\nimport type {\n DbConfig,\n DocumentModelSpec,\n FactorySpec,\n InitMessage,\n LoadModelMessage,\n ParentMessage,\n WorkerMessage,\n} from \"./protocol.js\";\nimport { errorToInfo } from \"./sanitize.js\";\n\nconst POOL_SAMPLE_INTERVAL_MS = 1_000;\n\n/**\n * Closeable handle around the parent's Postgres pool the worker owns.\n * Decoupled so tests can substitute a PGlite-backed Kysely.\n *\n * When the worker owns a real pg.Pool, the handle exposes a\n * {@link PoolInstrumentation} so the run-loop can subscribe to acquire-wait\n * samples and forward them to the host. Tests that swap in PGlite leave\n * this undefined; pool metrics simply do not emit in that path.\n */\nexport type WorkerDatabaseHandle = {\n kysely: Kysely<Database>;\n poolInstrumentation?: PoolInstrumentation;\n shutdown(): Promise<void>;\n};\n\nexport type RunWorkerOverrides = {\n /**\n * Builds the worker's Kysely instance from the init's DbConfig. Defaults\n * to a real Postgres pool. Tests typically swap this for PGlite.\n */\n createDatabase?: (\n config: DbConfig,\n workerId: string,\n ) => Promise<WorkerDatabaseHandle>;\n /**\n * Overrides the dynamic-import factory loader used for the signer and\n * document model manifest. Tests usually inject this so\n * they don't need real packages.\n */\n loadFactory?: BuildWorkerExecutorOptions[\"loadFactory\"];\n /**\n * Called after the database is created but before the executor stack\n * is built. Lets tests run reactor migrations against PGlite, since\n * the production parent runs them once against shared Postgres.\n */\n beforeBuildExecutor?: (db: Kysely<Database>) => Promise<void>;\n};\n\nasync function defaultCreateDatabase(\n config: DbConfig,\n workerId: string,\n): Promise<WorkerDatabaseHandle> {\n const { Kysely, PostgresDialect } = await import(\"kysely\");\n const pgModule = await import(\"pg\");\n const Pool = pgModule.default.Pool;\n const pool = new Pool({\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n password: config.password,\n ssl: config.ssl ? { rejectUnauthorized: false } : undefined,\n application_name: config.applicationName ?? workerId,\n max: config.poolSize,\n connectionTimeoutMillis: config.connectionTimeoutMillis,\n idleTimeoutMillis: config.idleTimeoutMillis,\n });\n const poolInstrumentation = instrumentPgPool(pool, workerId);\n const kysely = new Kysely<Database>({\n dialect: new PostgresDialect({ pool }),\n });\n return {\n kysely,\n poolInstrumentation,\n async shutdown(): Promise<void> {\n try {\n await kysely.destroy();\n } catch {\n // best-effort\n }\n try {\n await pool.end();\n } catch {\n // best-effort\n }\n },\n };\n}\n\n/**\n * Drives the worker's message loop. Owns lifecycle of the database handle\n * and executor stack. The default factories build a real Postgres pool and\n * use dynamic `import()` for model/signer specs; tests inject overrides\n * for an in-process PGlite path.\n */\nexport function runWorker(\n parentPort: MessagePort,\n overrides: RunWorkerOverrides = {},\n): void {\n let workerId = \"\";\n let initCompleted = false;\n let initConfig: InitMessage | null = null;\n let executorStack: WorkerExecutorStack | null = null;\n let database: WorkerDatabaseHandle | null = null;\n let poolSampleTimer: NodeJS.Timeout | null = null;\n let pendingPoolSamples: number[] = [];\n let detachPoolListener: (() => void) | null = null;\n const activeLoadFactory: NonNullable<\n BuildWorkerExecutorOptions[\"loadFactory\"]\n > = overrides.loadFactory ?? defaultLoadFactory;\n const loadModelSpec = overrides.loadFactory\n ? async (spec: DocumentModelSpec) =>\n (await activeLoadFactory(spec)) as DocumentModelModule\n : loadDocumentModelSpec;\n\n function post(msg: WorkerMessage): void {\n parentPort.postMessage(msg);\n }\n\n const logger = createForwardingLogger(post);\n\n function startPoolReporter(instrumentation: PoolInstrumentation): void {\n detachPoolListener = instrumentation.onAcquire((durationMs) => {\n pendingPoolSamples.push(durationMs);\n });\n poolSampleTimer = setInterval(() => {\n if (pendingPoolSamples.length === 0) {\n return;\n }\n const durations = pendingPoolSamples;\n pendingPoolSamples = [];\n const stats = instrumentation.getStats();\n post({\n type: \"pool-acquire-samples\",\n workerId,\n poolName: instrumentation.name,\n timestamp: Date.now(),\n durations,\n size: stats.size,\n idle: stats.idle,\n waiting: stats.waiting,\n });\n }, POOL_SAMPLE_INTERVAL_MS);\n poolSampleTimer.unref();\n }\n\n function stopPoolReporter(): void {\n if (detachPoolListener) {\n detachPoolListener();\n detachPoolListener = null;\n }\n if (poolSampleTimer) {\n clearInterval(poolSampleTimer);\n poolSampleTimer = null;\n }\n pendingPoolSamples = [];\n }\n\n process.on(\"uncaughtException\", (err: unknown) => {\n try {\n post({\n type: \"log\",\n level: \"error\",\n message: \"worker uncaughtException\",\n args: [errorToInfo(err)],\n timestamp: Date.now(),\n });\n } catch {\n // nothing left to do\n }\n throw err;\n });\n\n process.on(\"unhandledRejection\", (reason: unknown) => {\n try {\n post({\n type: \"log\",\n level: \"error\",\n message: \"worker unhandledRejection\",\n args: [errorToInfo(reason)],\n timestamp: Date.now(),\n });\n } catch {\n // nothing left to do\n }\n });\n\n async function handleInit(msg: InitMessage): Promise<void> {\n workerId = msg.workerId;\n initConfig = msg;\n const createDb = overrides.createDatabase ?? defaultCreateDatabase;\n database = await createDb(msg.db, msg.workerId);\n if (overrides.beforeBuildExecutor) {\n await overrides.beforeBuildExecutor(database.kysely);\n }\n executorStack = await buildWorkerExecutor({\n init: msg,\n database: database.kysely,\n logger: new ConsoleLogger([`reactor-worker:${msg.workerId}`]),\n executorConfig: msg.executorConfig,\n loadFactory: activeLoadFactory,\n });\n if (database.poolInstrumentation) {\n startPoolReporter(database.poolInstrumentation);\n }\n initCompleted = true;\n logger.info(\"worker initialized: @workerId\", msg.workerId);\n post({\n type: \"ready\",\n correlationId: msg.correlationId,\n workerId: msg.workerId,\n });\n }\n\n async function handleExecute(correlationId: string, job: Job): Promise<void> {\n if (!executorStack) {\n post({\n type: \"result\",\n correlationId,\n result: { job, success: false },\n error: errorToInfo(new Error(\"execute received before init\")),\n });\n return;\n }\n try {\n const result = await executorStack.executor.executeJob(job);\n const writeReady = executorStack.takeLastWriteReady();\n const signatureRefusals = executorStack.takeSignatureRefusals();\n post({\n type: \"result\",\n correlationId,\n result,\n writeReady: writeReady ?? undefined,\n signatureRefusals:\n signatureRefusals.length > 0 ? signatureRefusals : undefined,\n // Structured clone drops a custom error name; the ErrorInfo keeps it.\n error:\n !result.success && result.error\n ? errorToInfo(result.error)\n : undefined,\n });\n } catch (error) {\n post({\n type: \"result\",\n correlationId,\n result: { job, success: false },\n error: errorToInfo(error),\n });\n }\n }\n\n async function handleLoadModel(msg: LoadModelMessage): Promise<void> {\n const stack = executorStack;\n if (!stack) {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(new Error(\"load-model received before init\")),\n });\n return;\n }\n let module: DocumentModelModule;\n try {\n module = await loadModelSpec(msg.model.spec);\n } catch (error) {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(error),\n });\n return;\n }\n const [result] = stack.registry.registerModules(module);\n if (result.status === \"error\") {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(result.error),\n });\n return;\n }\n post({\n type: \"model-loaded\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n });\n }\n\n async function shutdownDatabase(): Promise<void> {\n stopPoolReporter();\n if (database) {\n await database.shutdown();\n }\n }\n\n function handleParentMessage(msg: ParentMessage): void {\n switch (msg.type) {\n case \"init\": {\n handleInit(msg).catch((err: unknown) => {\n post({\n type: \"log\",\n level: \"error\",\n message: \"worker init failed\",\n args: [errorToInfo(err)],\n timestamp: Date.now(),\n });\n });\n break;\n }\n\n case \"execute\": {\n if (!initCompleted) {\n logger.warn(\"received execute before init\");\n post({\n type: \"result\",\n correlationId: msg.correlationId,\n result: { job: msg.job, success: false },\n error: errorToInfo(new Error(\"execute received before init\")),\n });\n break;\n }\n handleExecute(msg.correlationId, msg.job).catch((err: unknown) => {\n post({\n type: \"result\",\n correlationId: msg.correlationId,\n result: { job: msg.job, success: false },\n error: errorToInfo(err),\n });\n });\n break;\n }\n\n case \"shutdown\": {\n logger.info(\"worker shutting down: @workerId\", workerId);\n void shutdownDatabase().finally(() => {\n post({\n type: \"log\",\n level: \"info\",\n message: \"worker shutdown\",\n args: [],\n timestamp: Date.now(),\n });\n process.exit(0);\n });\n break;\n }\n\n case \"abort\": {\n logger.warn(\n \"abort received (no-op stub): @correlationId\",\n msg.correlationId,\n );\n break;\n }\n\n case \"load-model\": {\n handleLoadModel(msg).catch((err: unknown) => {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(err),\n });\n });\n break;\n }\n\n case \"evict-purged\": {\n executorStack?.evictPurged(msg.documentIds);\n break;\n }\n\n default: {\n const raw = msg as Record<string, unknown>;\n if (raw[\"type\"] === \"__test_throw\") {\n const rawReason = raw[\"reason\"];\n const reason =\n typeof rawReason === \"string\"\n ? rawReason\n : \"synthetic uncaughtException\";\n setTimeout(() => {\n throw new Error(reason);\n }, 0);\n return;\n }\n const _exhaustive: never = msg;\n void _exhaustive;\n break;\n }\n }\n }\n\n parentPort.on(\"message\", handleParentMessage);\n\n // Test-only exports surfaced on a side-channel for unit tests that drive\n // the message loop directly without a real worker_threads parent.\n const harness = {\n handleParentMessage,\n get initCompleted(): boolean {\n return initCompleted;\n },\n get initConfig(): InitMessage | null {\n return initConfig;\n },\n get workerId(): string {\n return workerId;\n },\n };\n (\n parentPort as unknown as { __reactorWorkerHarness?: unknown }\n ).__reactorWorkerHarness = harness;\n}\n\nexport type FactorySpecForTesting = FactorySpec;\n","import { isMainThread, parentPort } from \"node:worker_threads\";\nimport { runWorker } from \"./run-worker.js\";\n\nif (isMainThread || parentPort === null) {\n throw new Error(\"entry.ts must be run as a worker thread\");\n}\n\nrunWorker(parentPort);\n"],"mappings":"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"}
1
+ {"version":3,"file":"entry.js","names":[],"sources":["../src/executor/worker/build-worker-executor.ts","../src/executor/worker/run-worker.ts","../src/executor/worker/entry.ts"],"sourcesContent":["import type {\n DocumentModelModule,\n ISigner,\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 type SignatureRefusedEvent,\n} from \"../../events/types.js\";\nimport { DocumentModelRegistry } from \"../../registry/implementation.js\";\nimport type { JobMeta } from \"../../shared/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 { SignatureTrustPolicy } from \"../../signer/types.js\";\nimport type { JobExecutorConfig } from \"../types.js\";\nimport type {\n FactorySpec,\n InitMessage,\n ModelManifestEntry,\n} from \"./protocol.js\";\nimport { defaultLoadFactory } from \"./load-spec.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 /** Drains the refusals this worker's executor emitted since the last call. */\n takeSignatureRefusals(): SignatureRefusedEvent[];\n /** Drops purged ids from this worker's write and meta caches. */\n evictPurged(documentIds: string[]): void;\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\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 signer: ISigner | undefined;\n if (init.signer) {\n try {\n signer = (await loadFactory(init.signer)) as ISigner;\n } catch (error) {\n logger.error(\n \"worker failed to load signer: @spec @error\",\n init.signer.module,\n error,\n );\n throw error;\n }\n }\n\n let trustPolicy: SignatureTrustPolicy | undefined;\n if (init.trustPolicy) {\n try {\n trustPolicy = (await loadFactory(\n init.trustPolicy,\n )) as SignatureTrustPolicy;\n } catch (error) {\n logger.error(\n \"worker failed to load trust policy: @spec @error\",\n init.trustPolicy.module,\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 let signatureRefusals: SignatureRefusedEvent[] = [];\n eventBus.subscribe(\n ReactorEventTypes.SIGNATURE_REFUSED,\n (_t: number, event: SignatureRefusedEvent) => {\n signatureRefusals.push(event);\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 executionScope,\n signer,\n trustPolicy,\n );\n\n return {\n executor,\n registry,\n takeLastWriteReady(): WorkerWriteReadyCapture | null {\n const captured = lastWriteReady;\n lastWriteReady = null;\n return captured;\n },\n takeSignatureRefusals(): SignatureRefusedEvent[] {\n const captured = signatureRefusals;\n signatureRefusals = [];\n return captured;\n },\n evictPurged(documentIds: string[]): void {\n for (const documentId of documentIds) {\n writeCache.invalidate(documentId);\n documentMetaCache.invalidate(documentId);\n }\n },\n };\n}\n","import type { DocumentModelModule } from \"@powerhousedao/shared/document-model\";\nimport { ConsoleLogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { MessagePort } from \"node:worker_threads\";\nimport type { Database } from \"../../core/types.js\";\nimport type { Job } from \"../../queue/types.js\";\nimport {\n instrumentPgPool,\n type PoolInstrumentation,\n} from \"../../storage/pool-instrumentation.js\";\nimport {\n buildWorkerExecutor,\n type BuildWorkerExecutorOptions,\n type WorkerExecutorStack,\n} from \"./build-worker-executor.js\";\nimport { createForwardingLogger } from \"./forwarding-logger.js\";\nimport { defaultLoadFactory, loadDocumentModelSpec } from \"./load-spec.js\";\nimport type {\n DbConfig,\n DocumentModelSpec,\n FactorySpec,\n InitMessage,\n LoadModelMessage,\n ParentMessage,\n WorkerMessage,\n} from \"./protocol.js\";\nimport { errorToInfo } from \"./sanitize.js\";\n\nconst POOL_SAMPLE_INTERVAL_MS = 1_000;\n\n/**\n * Closeable handle around the parent's Postgres pool the worker owns.\n * Decoupled so tests can substitute a PGlite-backed Kysely.\n *\n * When the worker owns a real pg.Pool, the handle exposes a\n * {@link PoolInstrumentation} so the run-loop can subscribe to acquire-wait\n * samples and forward them to the host. Tests that swap in PGlite leave\n * this undefined; pool metrics simply do not emit in that path.\n */\nexport type WorkerDatabaseHandle = {\n kysely: Kysely<Database>;\n poolInstrumentation?: PoolInstrumentation;\n shutdown(): Promise<void>;\n};\n\nexport type RunWorkerOverrides = {\n /**\n * Builds the worker's Kysely instance from the init's DbConfig. Defaults\n * to a real Postgres pool. Tests typically swap this for PGlite.\n */\n createDatabase?: (\n config: DbConfig,\n workerId: string,\n ) => Promise<WorkerDatabaseHandle>;\n /**\n * Overrides the dynamic-import factory loader used for the signer and\n * document model manifest. Tests usually inject this so\n * they don't need real packages.\n */\n loadFactory?: BuildWorkerExecutorOptions[\"loadFactory\"];\n /**\n * Called after the database is created but before the executor stack\n * is built. Lets tests run reactor migrations against PGlite, since\n * the production parent runs them once against shared Postgres.\n */\n beforeBuildExecutor?: (db: Kysely<Database>) => Promise<void>;\n};\n\nasync function defaultCreateDatabase(\n config: DbConfig,\n workerId: string,\n): Promise<WorkerDatabaseHandle> {\n const { Kysely, PostgresDialect } = await import(\"kysely\");\n const pgModule = await import(\"pg\");\n const Pool = pgModule.default.Pool;\n const pool = new Pool({\n host: config.host,\n port: config.port,\n database: config.database,\n user: config.user,\n password: config.password,\n ssl: config.ssl ? { rejectUnauthorized: false } : undefined,\n application_name: config.applicationName ?? workerId,\n max: config.poolSize,\n connectionTimeoutMillis: config.connectionTimeoutMillis,\n idleTimeoutMillis: config.idleTimeoutMillis,\n });\n const poolInstrumentation = instrumentPgPool(pool, workerId);\n const kysely = new Kysely<Database>({\n dialect: new PostgresDialect({ pool }),\n });\n return {\n kysely,\n poolInstrumentation,\n async shutdown(): Promise<void> {\n try {\n await kysely.destroy();\n } catch {\n // best-effort\n }\n try {\n await pool.end();\n } catch {\n // best-effort\n }\n },\n };\n}\n\n/**\n * Drives the worker's message loop. Owns lifecycle of the database handle\n * and executor stack. The default factories build a real Postgres pool and\n * use dynamic `import()` for model/signer specs; tests inject overrides\n * for an in-process PGlite path.\n */\nexport function runWorker(\n parentPort: MessagePort,\n overrides: RunWorkerOverrides = {},\n): void {\n let workerId = \"\";\n let initCompleted = false;\n let initConfig: InitMessage | null = null;\n let executorStack: WorkerExecutorStack | null = null;\n let database: WorkerDatabaseHandle | null = null;\n let poolSampleTimer: NodeJS.Timeout | null = null;\n let pendingPoolSamples: number[] = [];\n let detachPoolListener: (() => void) | null = null;\n const activeLoadFactory: NonNullable<\n BuildWorkerExecutorOptions[\"loadFactory\"]\n > = overrides.loadFactory ?? defaultLoadFactory;\n const loadModelSpec = overrides.loadFactory\n ? async (spec: DocumentModelSpec) =>\n (await activeLoadFactory(spec)) as DocumentModelModule\n : loadDocumentModelSpec;\n\n function post(msg: WorkerMessage): void {\n parentPort.postMessage(msg);\n }\n\n const logger = createForwardingLogger(post);\n\n function startPoolReporter(instrumentation: PoolInstrumentation): void {\n detachPoolListener = instrumentation.onAcquire((durationMs) => {\n pendingPoolSamples.push(durationMs);\n });\n poolSampleTimer = setInterval(() => {\n if (pendingPoolSamples.length === 0) {\n return;\n }\n const durations = pendingPoolSamples;\n pendingPoolSamples = [];\n const stats = instrumentation.getStats();\n post({\n type: \"pool-acquire-samples\",\n workerId,\n poolName: instrumentation.name,\n timestamp: Date.now(),\n durations,\n size: stats.size,\n idle: stats.idle,\n waiting: stats.waiting,\n });\n }, POOL_SAMPLE_INTERVAL_MS);\n poolSampleTimer.unref();\n }\n\n function stopPoolReporter(): void {\n if (detachPoolListener) {\n detachPoolListener();\n detachPoolListener = null;\n }\n if (poolSampleTimer) {\n clearInterval(poolSampleTimer);\n poolSampleTimer = null;\n }\n pendingPoolSamples = [];\n }\n\n process.on(\"uncaughtException\", (err: unknown) => {\n try {\n post({\n type: \"log\",\n level: \"error\",\n message: \"worker uncaughtException\",\n args: [errorToInfo(err)],\n timestamp: Date.now(),\n });\n } catch {\n // nothing left to do\n }\n throw err;\n });\n\n process.on(\"unhandledRejection\", (reason: unknown) => {\n try {\n post({\n type: \"log\",\n level: \"error\",\n message: \"worker unhandledRejection\",\n args: [errorToInfo(reason)],\n timestamp: Date.now(),\n });\n } catch {\n // nothing left to do\n }\n });\n\n async function handleInit(msg: InitMessage): Promise<void> {\n workerId = msg.workerId;\n initConfig = msg;\n const createDb = overrides.createDatabase ?? defaultCreateDatabase;\n database = await createDb(msg.db, msg.workerId);\n if (overrides.beforeBuildExecutor) {\n await overrides.beforeBuildExecutor(database.kysely);\n }\n executorStack = await buildWorkerExecutor({\n init: msg,\n database: database.kysely,\n logger: new ConsoleLogger([`reactor-worker:${msg.workerId}`]),\n executorConfig: msg.executorConfig,\n loadFactory: activeLoadFactory,\n });\n if (database.poolInstrumentation) {\n startPoolReporter(database.poolInstrumentation);\n }\n initCompleted = true;\n logger.info(\"worker initialized: @workerId\", msg.workerId);\n post({\n type: \"ready\",\n correlationId: msg.correlationId,\n workerId: msg.workerId,\n });\n }\n\n async function handleExecute(correlationId: string, job: Job): Promise<void> {\n if (!executorStack) {\n post({\n type: \"result\",\n correlationId,\n result: { job, success: false },\n error: errorToInfo(new Error(\"execute received before init\")),\n });\n return;\n }\n try {\n const result = await executorStack.executor.executeJob(job);\n const writeReady = executorStack.takeLastWriteReady();\n const signatureRefusals = executorStack.takeSignatureRefusals();\n post({\n type: \"result\",\n correlationId,\n result,\n writeReady: writeReady ?? undefined,\n signatureRefusals:\n signatureRefusals.length > 0 ? signatureRefusals : undefined,\n // Structured clone drops a custom error name; the ErrorInfo keeps it.\n error:\n !result.success && result.error\n ? errorToInfo(result.error)\n : undefined,\n });\n } catch (error) {\n post({\n type: \"result\",\n correlationId,\n result: { job, success: false },\n error: errorToInfo(error),\n });\n }\n }\n\n async function handleLoadModel(msg: LoadModelMessage): Promise<void> {\n const stack = executorStack;\n if (!stack) {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(new Error(\"load-model received before init\")),\n });\n return;\n }\n let module: DocumentModelModule;\n try {\n module = await loadModelSpec(msg.model.spec);\n } catch (error) {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(error),\n });\n return;\n }\n const [result] = stack.registry.registerModules(module);\n if (result.status === \"error\") {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(result.error),\n });\n return;\n }\n post({\n type: \"model-loaded\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n });\n }\n\n async function shutdownDatabase(): Promise<void> {\n stopPoolReporter();\n if (database) {\n await database.shutdown();\n }\n }\n\n function handleParentMessage(msg: ParentMessage): void {\n switch (msg.type) {\n case \"init\": {\n handleInit(msg).catch((err: unknown) => {\n post({\n type: \"log\",\n level: \"error\",\n message: \"worker init failed\",\n args: [errorToInfo(err)],\n timestamp: Date.now(),\n });\n });\n break;\n }\n\n case \"execute\": {\n if (!initCompleted) {\n logger.warn(\"received execute before init\");\n post({\n type: \"result\",\n correlationId: msg.correlationId,\n result: { job: msg.job, success: false },\n error: errorToInfo(new Error(\"execute received before init\")),\n });\n break;\n }\n handleExecute(msg.correlationId, msg.job).catch((err: unknown) => {\n post({\n type: \"result\",\n correlationId: msg.correlationId,\n result: { job: msg.job, success: false },\n error: errorToInfo(err),\n });\n });\n break;\n }\n\n case \"shutdown\": {\n logger.info(\"worker shutting down: @workerId\", workerId);\n void shutdownDatabase().finally(() => {\n post({\n type: \"log\",\n level: \"info\",\n message: \"worker shutdown\",\n args: [],\n timestamp: Date.now(),\n });\n process.exit(0);\n });\n break;\n }\n\n case \"abort\": {\n logger.warn(\n \"abort received (no-op stub): @correlationId\",\n msg.correlationId,\n );\n break;\n }\n\n case \"load-model\": {\n handleLoadModel(msg).catch((err: unknown) => {\n post({\n type: \"model-load-failed\",\n correlationId: msg.correlationId,\n documentType: msg.model.documentType,\n version: msg.model.version,\n error: errorToInfo(err),\n });\n });\n break;\n }\n\n case \"evict-purged\": {\n executorStack?.evictPurged(msg.documentIds);\n break;\n }\n\n default: {\n const raw = msg as Record<string, unknown>;\n if (raw[\"type\"] === \"__test_throw\") {\n const rawReason = raw[\"reason\"];\n const reason =\n typeof rawReason === \"string\"\n ? rawReason\n : \"synthetic uncaughtException\";\n setTimeout(() => {\n throw new Error(reason);\n }, 0);\n return;\n }\n const _exhaustive: never = msg;\n void _exhaustive;\n break;\n }\n }\n }\n\n parentPort.on(\"message\", handleParentMessage);\n\n // Test-only exports surfaced on a side-channel for unit tests that drive\n // the message loop directly without a real worker_threads parent.\n const harness = {\n handleParentMessage,\n get initCompleted(): boolean {\n return initCompleted;\n },\n get initConfig(): InitMessage | null {\n return initConfig;\n },\n get workerId(): string {\n return workerId;\n },\n };\n (\n parentPort as unknown as { __reactorWorkerHarness?: unknown }\n ).__reactorWorkerHarness = harness;\n}\n\nexport type FactorySpecForTesting = FactorySpec;\n","import { isMainThread, parentPort } from \"node:worker_threads\";\nimport { runWorker } from \"./run-worker.js\";\n\nif (isMainThread || parentPort === null) {\n throw new Error(\"entry.ts must be run as a worker thread\");\n}\n\nrunWorker(parentPort);\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { DOCUMENT_DELETED_REASON, DOCUMENT_SCOPE_ACTION_TYPES, DowngradeNotSupportedError as DowngradeNotSupportedError$1, PURGE_DOCUMENT, appendWithoutApplying, applyDeleteDocumentAction, applyDeleteDocumentAction as applyDeleteDocumentAction$1, applyUpgradeDocumentAction, applyUpgradeDocumentAction as applyUpgradeDocumentAction$1, baseReducerVersion, createDocumentState, createPresignedHeader, defaultBaseState, deriveOperationId, isDenied, isPurgeMarker, mentionedGroupIds, normalizeDocumentModelVersion, operationOutcome, purgedProtocolVersions, targetDocumentId, withProtocolVersions } from "@powerhousedao/shared/document-model";
1
+ import { DOCUMENT_DELETED_REASON, DOCUMENT_SCOPE_ACTION_TYPES, PURGE_DOCUMENT, appendWithoutApplying, applyDeleteDocumentAction, applyDeleteDocumentAction as applyDeleteDocumentAction$1, applyUpgradeDocumentAction, applyUpgradeDocumentAction as applyUpgradeDocumentAction$1, baseReducerVersion, createDocumentState, createPresignedHeader, defaultBaseState, deriveOperationId, isDenied, isPurgeMarker, mentionedGroupIds, normalizeDocumentModelVersion, operationOutcome, purgedProtocolVersions, targetDocumentId, withProtocolVersions } from "@powerhousedao/shared/document-model";
2
2
  import { v4 } from "uuid";
3
3
  import { Migrator, TableNode, sql } from "kysely";
4
4
  //#region \0rolldown/runtime.js
@@ -837,99 +837,6 @@ function jobWriteIds(job) {
837
837
  return [...ids].sort();
838
838
  }
839
839
  //#endregion
840
- //#region src/registry/errors.ts
841
- /**
842
- * Error thrown when a document model module is not found in the registry.
843
- */
844
- var ModuleNotFoundError = class extends Error {
845
- documentType;
846
- requestedVersion;
847
- constructor(documentType, version) {
848
- const versionSuffix = version !== void 0 ? ` version ${version}` : "";
849
- super(`Document model module not found for type: ${documentType}${versionSuffix}`);
850
- this.name = "ModuleNotFoundError";
851
- this.documentType = documentType;
852
- this.requestedVersion = version;
853
- }
854
- static isError(error) {
855
- return Error.isError(error) && error.name === "ModuleNotFoundError";
856
- }
857
- };
858
- /** The host has the model only as a live module, which workers cannot import. */
859
- var ModelNotWorkerImportableError = class extends Error {
860
- documentType;
861
- constructor(documentType, version) {
862
- const versionSuffix = version !== void 0 ? ` version ${version}` : "";
863
- super(`Document model ${documentType}${versionSuffix} is registered on the host only as a live module, so executor workers cannot load it. Register it from a { filePath } or { packageName } source.`);
864
- this.name = "ModelNotWorkerImportableError";
865
- this.documentType = documentType;
866
- }
867
- static isError(error) {
868
- return Error.isError(error) && error.name === "ModelNotWorkerImportableError";
869
- }
870
- };
871
- /**
872
- * Error thrown when attempting to register a module that already exists.
873
- */
874
- var DuplicateModuleError = class extends Error {
875
- constructor(documentType, version) {
876
- const versionSuffix = version !== void 0 ? ` (version ${version})` : "";
877
- super(`Document model module already registered for type: ${documentType}${versionSuffix}`);
878
- this.name = "DuplicateModuleError";
879
- }
880
- static isError(error) {
881
- return Error.isError(error) && error.name === "DuplicateModuleError";
882
- }
883
- };
884
- /**
885
- * Error thrown when a module is invalid or malformed.
886
- */
887
- var InvalidModuleError = class extends Error {
888
- constructor(message) {
889
- super(`Invalid document model module: ${message}`);
890
- this.name = "InvalidModuleError";
891
- }
892
- };
893
- /**
894
- * Error thrown when attempting to register an upgrade manifest that already exists.
895
- */
896
- var DuplicateManifestError = class extends Error {
897
- constructor(documentType) {
898
- super(`Upgrade manifest already registered for type: ${documentType}`);
899
- this.name = "DuplicateManifestError";
900
- }
901
- static isError(error) {
902
- return Error.isError(error) && error.name === "DuplicateManifestError";
903
- }
904
- };
905
- /**
906
- * Error thrown when an upgrade manifest is not found.
907
- */
908
- var ManifestNotFoundError = class extends Error {
909
- constructor(documentType) {
910
- super(`Upgrade manifest not found for type: ${documentType}`);
911
- this.name = "ManifestNotFoundError";
912
- }
913
- };
914
- /**
915
- * Error thrown when a required upgrade transition is missing from the manifest.
916
- */
917
- var MissingUpgradeTransitionError = class extends Error {
918
- constructor(documentType, fromVersion, toVersion) {
919
- super(`Missing upgrade transition for ${documentType}: v${fromVersion} to v${toVersion}`);
920
- this.name = "MissingUpgradeTransitionError";
921
- }
922
- };
923
- /**
924
- * Error thrown when getUpgradeReducer is called with a non-single-step version increment.
925
- */
926
- var InvalidUpgradeStepError = class extends Error {
927
- constructor(documentType, fromVersion, toVersion) {
928
- super(`Invalid upgrade step for ${documentType}: must be single version increment, got v${fromVersion} to v${toVersion}`);
929
- this.name = "InvalidUpgradeStepError";
930
- }
931
- };
932
- //#endregion
933
840
  //#region src/cache/collection-membership-cache.ts
934
841
  var CollectionMembershipCache = class CollectionMembershipCache {
935
842
  operationIndex;
@@ -2548,142 +2455,6 @@ var EventBus = class {
2548
2455
  }
2549
2456
  };
2550
2457
  //#endregion
2551
- //#region src/registry/implementation.ts
2552
- /**
2553
- * In-memory implementation of the IDocumentModelRegistry interface.
2554
- * Manages document model modules with version-aware storage and upgrade manifest support.
2555
- */
2556
- var DocumentModelRegistry = class {
2557
- modules = [];
2558
- manifests = [];
2559
- registerModules(...modules) {
2560
- return modules.map((module) => {
2561
- try {
2562
- const documentType = module.documentModel.global.id;
2563
- const version = module.version ?? 1;
2564
- for (let i = 0; i < this.modules.length; i++) {
2565
- const existing = this.modules[i];
2566
- const existingType = existing.documentModel.global.id;
2567
- const existingVersion = existing.version ?? 1;
2568
- if (existingType === documentType && existingVersion === version) throw new DuplicateModuleError(documentType, version);
2569
- }
2570
- this.modules.push(module);
2571
- return {
2572
- status: "success",
2573
- item: module
2574
- };
2575
- } catch (error) {
2576
- return {
2577
- status: "error",
2578
- item: module,
2579
- error: error instanceof Error ? error : new Error(String(error))
2580
- };
2581
- }
2582
- });
2583
- }
2584
- unregisterModules(...documentTypes) {
2585
- let allFound = true;
2586
- for (const documentType of documentTypes) {
2587
- if (!this.modules.some((m) => m.documentModel.global.id === documentType)) allFound = false;
2588
- this.modules = this.modules.filter((m) => m.documentModel.global.id !== documentType);
2589
- }
2590
- return allFound;
2591
- }
2592
- getModule(documentType, version) {
2593
- let latestModule;
2594
- let latestVersion = -1;
2595
- for (let i = 0; i < this.modules.length; i++) {
2596
- const module = this.modules[i];
2597
- const moduleType = module.documentModel.global.id;
2598
- const moduleVersion = module.version ?? 1;
2599
- if (moduleType === documentType) {
2600
- if (version !== void 0 && moduleVersion === version) return module;
2601
- if (moduleVersion > latestVersion) {
2602
- latestModule = module;
2603
- latestVersion = moduleVersion;
2604
- }
2605
- }
2606
- }
2607
- if (version === void 0 && latestModule !== void 0) return latestModule;
2608
- throw new ModuleNotFoundError(documentType, version);
2609
- }
2610
- getAllModules() {
2611
- return [...this.modules];
2612
- }
2613
- clear() {
2614
- this.modules = [];
2615
- this.manifests = [];
2616
- }
2617
- getSupportedVersions(documentType) {
2618
- const versions = [];
2619
- for (const module of this.modules) if (module.documentModel.global.id === documentType) versions.push(module.version ?? 1);
2620
- if (versions.length === 0) throw new ModuleNotFoundError(documentType);
2621
- return versions.sort((a, b) => a - b);
2622
- }
2623
- getLatestVersion(documentType) {
2624
- let latest = -1;
2625
- let found = false;
2626
- for (const module of this.modules) if (module.documentModel.global.id === documentType) {
2627
- found = true;
2628
- const version = module.version ?? 1;
2629
- if (version > latest) latest = version;
2630
- }
2631
- if (!found) throw new ModuleNotFoundError(documentType);
2632
- return latest;
2633
- }
2634
- registerUpgradeManifests(...manifestsToRegister) {
2635
- return manifestsToRegister.map((manifestToRegister) => {
2636
- try {
2637
- if (!manifestToRegister.documentType) throw new Error("Upgrade manifest is missing a documentType");
2638
- for (const registeredManifest of this.manifests) if (registeredManifest.documentType === manifestToRegister.documentType) throw new DuplicateManifestError(manifestToRegister.documentType);
2639
- this.manifests.push(manifestToRegister);
2640
- return {
2641
- status: "success",
2642
- item: manifestToRegister
2643
- };
2644
- } catch (error) {
2645
- return {
2646
- status: "error",
2647
- item: manifestToRegister,
2648
- error: error instanceof Error ? error : new Error(String(error))
2649
- };
2650
- }
2651
- });
2652
- }
2653
- unregisterUpgradeManifests(...documentTypes) {
2654
- let allFound = true;
2655
- for (const documentType of documentTypes) {
2656
- if (!this.manifests.some((m) => m.documentType === documentType)) allFound = false;
2657
- this.manifests = this.manifests.filter((m) => m.documentType !== documentType);
2658
- }
2659
- return allFound;
2660
- }
2661
- getUpgradeManifest(documentType) {
2662
- for (let i = 0; i < this.manifests.length; i++) if (this.manifests[i].documentType === documentType) return this.manifests[i];
2663
- throw new ManifestNotFoundError(documentType);
2664
- }
2665
- computeUpgradePath(documentType, fromVersion, toVersion) {
2666
- if (fromVersion === toVersion) return [];
2667
- if (toVersion < fromVersion) throw new DowngradeNotSupportedError$1(documentType, fromVersion, toVersion);
2668
- const manifest = this.getUpgradeManifest(documentType);
2669
- const path = [];
2670
- for (let v = fromVersion + 1; v <= toVersion; v++) {
2671
- const key = `v${v}`;
2672
- if (!(key in manifest.upgrades)) throw new MissingUpgradeTransitionError(documentType, v - 1, v);
2673
- const transition = manifest.upgrades[key];
2674
- path.push(transition);
2675
- }
2676
- return path;
2677
- }
2678
- getUpgradeReducer(documentType, fromVersion, toVersion) {
2679
- if (toVersion !== fromVersion + 1) throw new InvalidUpgradeStepError(documentType, fromVersion, toVersion);
2680
- const manifest = this.getUpgradeManifest(documentType);
2681
- const key = `v${toVersion}`;
2682
- if (!(key in manifest.upgrades)) throw new MissingUpgradeTransitionError(documentType, fromVersion, toVersion);
2683
- return manifest.upgrades[key].upgradeReducer;
2684
- }
2685
- };
2686
- //#endregion
2687
2458
  //#region src/storage/kysely/keyframe-store.ts
2688
2459
  var KyselyKeyframeStore = class KyselyKeyframeStore {
2689
2460
  db;
@@ -2833,6 +2604,10 @@ var _UniqueConstraintContext = class extends Error {
2833
2604
  this.name = "UniqueConstraintContext";
2834
2605
  }
2835
2606
  };
2607
+ function duplicateOf(ctx) {
2608
+ const op = ctx.stagedOps[0];
2609
+ return new DuplicateOperationError(`${op.opId} at index ${op.index} with skip ${op.skip}`);
2610
+ }
2836
2611
  var KyselyOperationStore = class KyselyOperationStore {
2837
2612
  db;
2838
2613
  trx;
@@ -2860,7 +2635,7 @@ var KyselyOperationStore = class KyselyOperationStore {
2860
2635
  if (error instanceof _UniqueConstraintContext) uniqueCtx = error;
2861
2636
  else throw error;
2862
2637
  }
2863
- if (uniqueCtx !== null) return this.resolveUniqueConstraint(uniqueCtx);
2638
+ if (uniqueCtx !== null) throw duplicateOf(uniqueCtx);
2864
2639
  return executeResult;
2865
2640
  } else {
2866
2641
  let transactionResult = null;
@@ -2883,8 +2658,7 @@ var KyselyOperationStore = class KyselyOperationStore {
2883
2658
  replayOps = await this.findIdempotentReplay(this.db, ctx.documentId, ctx.scope, ctx.branch, ctx.revision, ctx.stagedOps);
2884
2659
  } catch {}
2885
2660
  if (replayOps !== null) return replayOps;
2886
- const op = ctx.stagedOps[0];
2887
- throw new DuplicateOperationError(`${op.opId} at index ${op.index} with skip ${op.skip}`);
2661
+ throw duplicateOf(ctx);
2888
2662
  }
2889
2663
  async executeApply(trx, documentId, documentType, scope, branch, revision, fn, signal, condition) {
2890
2664
  throwIfAborted(signal);
@@ -3856,19 +3630,6 @@ function createForwardingLogger(post, level = "info", tags = []) {
3856
3630
  };
3857
3631
  }
3858
3632
  //#endregion
3859
- //#region src/executor/worker/load-spec.ts
3860
- /** Imports `spec.module` and calls the export with `spec.initArgs`, or returns it if not a function. */
3861
- async function defaultLoadFactory(spec) {
3862
- const ref = spec.module;
3863
- const exported = (await ("filePath" in ref ? import(new URL(`file://${ref.filePath}`).href) : import(ref.packageName)))[ref.exportName];
3864
- if (typeof exported === "function") return exported(spec.initArgs);
3865
- return exported;
3866
- }
3867
- /** Loads the document model module a manifest entry's spec names. */
3868
- async function loadDocumentModelSpec(spec) {
3869
- return await defaultLoadFactory(spec);
3870
- }
3871
- //#endregion
3872
- export { relationshipTarget as $, CollectionMembershipCache as A, ReactorEventTypes as At, TouchedStreams as B, ProbeSettler as C, PurgeTooLargeError as Ct, parseSnapshot as D, UnsupportedStoredProtocolError as Dt, describeWaitingSessions as E, UnsupportedProtocolVersionError as Et, ModuleNotFoundError as F, __exportAll as Ft, createConsistencyToken as G, applyUpgradeDocumentAction$1 as H, DOCUMENT_SCOPE_ACTIONS as I, createOperation as J, createDocumentFromAction as K, FencedWriteCache as L, DuplicateModuleError as M, parsePagingOptions as Mt, InvalidModuleError as N, throwIfAborted as Nt, snapshotFunctionsFor as O, UpgradePreconditionFailedError as Ot, ModelNotWorkerImportableError as P, yieldToMain as Pt, refusalError as Q, GATED_DOCUMENT_ACTIONS as R, listPurged as S, InvalidSignatureError as St, createKyselyWatermarkProbe as T, ReservedActionError as Tt, buildErrorResult as U, applyDeleteDocumentAction$1 as V, buildSuccessResult as W, isGenesisOperation as X, getNextIndexForScope as Y, jobWriteIds as Z, KyselyOperationIndex as _, DocumentNotFoundError as _t, sanitizeArg as a, AppendConditionFailedError as at, acquirePurgeLocks as b, GroupInUseError as bt, getMigrationStatus as c, DuplicateOperationError as ct, instrumentPgPool as d, AuthEnforcementDisabledError as dt, submittedActionIds as et, KyselyOperationStore as f, AuthTimestampNotMonotonicError as ft, KyselyWriteCache as g, DocumentNotDeletedError as gt, EventBus as h, DocumentDeletedError as ht, errorToInfo as i, APPEND_CONDITION_FAILED_PREFIX as it, DuplicateManifestError as j, matchesScope as jt, DocumentMetaCache as k, EventBusAggregateError as kt, runMigrations as l, OptimisticLockError as lt, DocumentModelRegistry as m, BatchJobFailedError as mt, loadDocumentModelSpec as n, targetDocumentId as nt, fromErrorInfo as o, DocumentAlreadyExistsError as ot, KyselyKeyframeStore as p, AuthorizationDeniedError as pt, createEmptyConsistencyToken as q, createForwardingLogger as r, updateDocumentRevision as rt, REACTOR_SCHEMA as s, DocumentExistence as st, defaultLoadFactory as t, summarizeSubmittedActions as tt, createForwardingPoolInstrumentation as u, RevisionMismatchError as ut, PURGE_LOCK_BUCKETS as v, DocumentPurgedError as vt, SettledWatermark as w, RelationshipNotFoundError as wt, findPurged as x, InvalidOperationTimestampError as xt, PURGE_NS as y, ExcessiveReshuffleError as yt, PurgeFence as z };
3633
+ export { DocumentExistence as $, GATED_DOCUMENT_ACTIONS as A, createOperation as B, describeWaitingSessions as C, matchesScope as Ct, CollectionMembershipCache as D, __exportAll as Dt, DocumentMetaCache as E, yieldToMain as Et, buildErrorResult as F, relationshipTarget as G, isGenesisOperation as H, buildSuccessResult as I, targetDocumentId as J, submittedActionIds as K, createConsistencyToken as L, TouchedStreams as M, applyDeleteDocumentAction$1 as N, DOCUMENT_SCOPE_ACTIONS as O, applyUpgradeDocumentAction$1 as P, DocumentAlreadyExistsError as Q, createDocumentFromAction as R, createKyselyWatermarkProbe as S, ReactorEventTypes as St, snapshotFunctionsFor as T, throwIfAborted as Tt, jobWriteIds as U, getNextIndexForScope as V, refusalError as W, APPEND_CONDITION_FAILED_PREFIX as X, updateDocumentRevision as Y, AppendConditionFailedError as Z, acquirePurgeLocks as _, ReservedActionError as _t, REACTOR_SCHEMA as a, AuthorizationDeniedError as at, ProbeSettler as b, UpgradePreconditionFailedError as bt, createForwardingPoolInstrumentation as c, DocumentNotDeletedError as ct, KyselyKeyframeStore as d, ExcessiveReshuffleError as dt, DuplicateOperationError as et, EventBus as f, GroupInUseError as ft, PURGE_NS as g, RelationshipNotFoundError as gt, PURGE_LOCK_BUCKETS as h, PurgeTooLargeError as ht, fromErrorInfo as i, AuthTimestampNotMonotonicError as it, PurgeFence as j, FencedWriteCache as k, instrumentPgPool as l, DocumentNotFoundError as lt, KyselyOperationIndex as m, InvalidSignatureError as mt, errorToInfo as n, RevisionMismatchError as nt, getMigrationStatus as o, BatchJobFailedError as ot, KyselyWriteCache as p, InvalidOperationTimestampError as pt, summarizeSubmittedActions as q, sanitizeArg as r, AuthEnforcementDisabledError as rt, runMigrations as s, DocumentDeletedError as st, createForwardingLogger as t, OptimisticLockError as tt, KyselyOperationStore as u, DocumentPurgedError as ut, findPurged as v, UnsupportedProtocolVersionError as vt, parseSnapshot as w, parsePagingOptions as wt, SettledWatermark as x, EventBusAggregateError as xt, listPurged as y, UnsupportedStoredProtocolError as yt, createEmptyConsistencyToken as z };
3873
3634
 
3874
- //# sourceMappingURL=load-spec-D9lnje-H.js.map
3635
+ //# sourceMappingURL=forwarding-logger-ByfBFgMS.js.map