@powerhousedao/reactor 6.2.3-dev.43 → 6.2.3-dev.45
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/{document-indexer-B3yKal-y.js → document-indexer-lfgXu9ph.js} +13 -4
- package/dist/document-indexer-lfgXu9ph.js.map +1 -0
- package/dist/drive-container-types-CifLQjd3.js +3766 -0
- package/dist/drive-container-types-CifLQjd3.js.map +1 -0
- package/dist/entry.js +99 -3
- package/dist/entry.js.map +1 -1
- package/dist/index-81SYQKda.d.ts +6353 -0
- package/dist/index-81SYQKda.d.ts.map +1 -0
- package/dist/index.d.ts +16 -6339
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +53 -49
- package/dist/index.js.map +1 -1
- package/dist/load-spec-f2ppXyKZ.js +3848 -0
- package/dist/load-spec-f2ppXyKZ.js.map +1 -0
- package/dist/projection-entry.js +2 -3
- package/dist/projection-entry.js.map +1 -1
- package/dist/{projection-shard-manager-BZPCQK-4.js → projection-shard-manager-XMam-rTs.js} +2 -2
- package/dist/{projection-shard-manager-BZPCQK-4.js.map → projection-shard-manager-XMam-rTs.js.map} +1 -1
- package/dist/rpc.d.ts +318 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +570 -0
- package/dist/rpc.js.map +1 -0
- package/dist/{worker-handle-CefOwokd.js → worker-handle-BKuaSp9T.js} +2 -2
- package/dist/{worker-handle-CefOwokd.js.map → worker-handle-BKuaSp9T.js.map} +1 -1
- package/package.json +10 -4
- package/dist/build-worker-executor-Bonu2jUB.js +0 -105
- package/dist/build-worker-executor-Bonu2jUB.js.map +0 -1
- package/dist/document-indexer-B3yKal-y.js.map +0 -1
- package/dist/forwarding-logger-BKDP4JnD.js +0 -7593
- package/dist/forwarding-logger-BKDP4JnD.js.map +0 -1
package/dist/entry.js
CHANGED
|
@@ -1,7 +1,102 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as
|
|
1
|
+
import { A as CollectionMembershipCache, _ as KyselyOperationIndex, d as instrumentPgPool, f as KyselyOperationStore, g as KyselyWriteCache, h as EventBus, i as errorToInfo, k as DocumentMetaCache, kt as ReactorEventTypes, m as DocumentModelRegistry, n as loadDocumentModelSpec, p as KyselyKeyframeStore, r as createForwardingLogger, s as REACTOR_SCHEMA, t as defaultLoadFactory } from "./load-spec-f2ppXyKZ.js";
|
|
2
|
+
import { a as KyselyExecutionScope, n as SimpleJobExecutor, t as DEFAULT_DRIVE_CONTAINER_TYPES } from "./drive-container-types-CifLQjd3.js";
|
|
3
3
|
import { ConsoleLogger } from "document-model";
|
|
4
4
|
import { isMainThread, parentPort } from "node:worker_threads";
|
|
5
|
+
//#region src/executor/worker/build-worker-executor.ts
|
|
6
|
+
async function loadModelManifest(entries, loadFactory, registry, logger) {
|
|
7
|
+
for (const entry of entries) {
|
|
8
|
+
let module;
|
|
9
|
+
try {
|
|
10
|
+
module = await loadFactory(entry.spec);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
logger.error("worker failed to load document model: @entry @error", entry, error);
|
|
13
|
+
throw error;
|
|
14
|
+
}
|
|
15
|
+
const [result] = registry.registerModules(module);
|
|
16
|
+
if (result.status === "error") {
|
|
17
|
+
logger.error("worker failed to register document model: @entry @error", entry, result.error);
|
|
18
|
+
throw result.error;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Assembles the in-worker storage stack plus a {@link SimpleJobExecutor}
|
|
24
|
+
* bound to a pre-built Kysely instance. The parent owns the wire protocol
|
|
25
|
+
* and routing; the worker owns everything below `SimpleJobExecutor`.
|
|
26
|
+
*
|
|
27
|
+
* The local event bus exists only to satisfy the executor's contract: its
|
|
28
|
+
* JOB_WRITE_READY emissions are captured here and shipped to the parent
|
|
29
|
+
* via {@link WorkerExecutorStack.takeLastWriteReady}.
|
|
30
|
+
*/
|
|
31
|
+
async function buildWorkerExecutor(options) {
|
|
32
|
+
const { init, database: baseDatabase, logger } = options;
|
|
33
|
+
const driveContainerTypes = options.driveContainerTypes ?? DEFAULT_DRIVE_CONTAINER_TYPES;
|
|
34
|
+
const loadFactory = options.loadFactory ?? defaultLoadFactory;
|
|
35
|
+
const registry = new DocumentModelRegistry();
|
|
36
|
+
await loadModelManifest(init.models, loadFactory, registry, logger);
|
|
37
|
+
let signer;
|
|
38
|
+
if (init.signer) try {
|
|
39
|
+
signer = await loadFactory(init.signer);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
logger.error("worker failed to load signer: @spec @error", init.signer.module, error);
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
let trustPolicy;
|
|
45
|
+
if (init.trustPolicy) try {
|
|
46
|
+
trustPolicy = await loadFactory(init.trustPolicy);
|
|
47
|
+
} catch (error) {
|
|
48
|
+
logger.error("worker failed to load trust policy: @spec @error", init.trustPolicy.module, error);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
const database = baseDatabase.withSchema(REACTOR_SCHEMA);
|
|
52
|
+
const operationStore = new KyselyOperationStore(database);
|
|
53
|
+
const keyframeStore = new KyselyKeyframeStore(database);
|
|
54
|
+
const writeCache = new KyselyWriteCache(keyframeStore, operationStore, registry, {
|
|
55
|
+
maxDocuments: 100,
|
|
56
|
+
ringBufferSize: 10,
|
|
57
|
+
keyframeInterval: 10
|
|
58
|
+
});
|
|
59
|
+
await writeCache.startup();
|
|
60
|
+
const operationIndex = new KyselyOperationIndex(database);
|
|
61
|
+
const documentMetaCache = new DocumentMetaCache(operationStore, { maxDocuments: 1e3 });
|
|
62
|
+
await documentMetaCache.startup();
|
|
63
|
+
const collectionMembershipCache = new CollectionMembershipCache(operationIndex);
|
|
64
|
+
const executionScope = new KyselyExecutionScope(database, operationStore, operationIndex, keyframeStore, writeCache, documentMetaCache, collectionMembershipCache);
|
|
65
|
+
const eventBus = new EventBus();
|
|
66
|
+
let lastWriteReady = null;
|
|
67
|
+
eventBus.subscribe(ReactorEventTypes.JOB_WRITE_READY, (_t, event) => {
|
|
68
|
+
lastWriteReady = {
|
|
69
|
+
operations: event.operations,
|
|
70
|
+
jobMeta: event.jobMeta
|
|
71
|
+
};
|
|
72
|
+
});
|
|
73
|
+
let signatureRefusals = [];
|
|
74
|
+
eventBus.subscribe(ReactorEventTypes.SIGNATURE_REFUSED, (_t, event) => {
|
|
75
|
+
signatureRefusals.push(event);
|
|
76
|
+
});
|
|
77
|
+
const executorConfig = options.executorConfig ?? {};
|
|
78
|
+
return {
|
|
79
|
+
executor: new SimpleJobExecutor(logger, registry, operationStore, eventBus, writeCache, operationIndex, documentMetaCache, collectionMembershipCache, driveContainerTypes, executorConfig, executionScope, signer, trustPolicy),
|
|
80
|
+
registry,
|
|
81
|
+
takeLastWriteReady() {
|
|
82
|
+
const captured = lastWriteReady;
|
|
83
|
+
lastWriteReady = null;
|
|
84
|
+
return captured;
|
|
85
|
+
},
|
|
86
|
+
takeSignatureRefusals() {
|
|
87
|
+
const captured = signatureRefusals;
|
|
88
|
+
signatureRefusals = [];
|
|
89
|
+
return captured;
|
|
90
|
+
},
|
|
91
|
+
evictPurged(documentIds) {
|
|
92
|
+
for (const documentId of documentIds) {
|
|
93
|
+
writeCache.invalidate(documentId);
|
|
94
|
+
documentMetaCache.invalidate(documentId);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
//#endregion
|
|
5
100
|
//#region src/executor/worker/run-worker.ts
|
|
6
101
|
const POOL_SAMPLE_INTERVAL_MS = 1e3;
|
|
7
102
|
async function defaultCreateDatabase(config, workerId) {
|
|
@@ -50,6 +145,7 @@ function runWorker(parentPort, overrides = {}) {
|
|
|
50
145
|
let pendingPoolSamples = [];
|
|
51
146
|
let detachPoolListener = null;
|
|
52
147
|
const activeLoadFactory = overrides.loadFactory ?? defaultLoadFactory;
|
|
148
|
+
const loadModelSpec = overrides.loadFactory ? async (spec) => await activeLoadFactory(spec) : loadDocumentModelSpec;
|
|
53
149
|
function post(msg) {
|
|
54
150
|
parentPort.postMessage(msg);
|
|
55
151
|
}
|
|
@@ -182,7 +278,7 @@ function runWorker(parentPort, overrides = {}) {
|
|
|
182
278
|
}
|
|
183
279
|
let module;
|
|
184
280
|
try {
|
|
185
|
-
module = await
|
|
281
|
+
module = await loadModelSpec(msg.model.spec);
|
|
186
282
|
} catch (error) {
|
|
187
283
|
post({
|
|
188
284
|
type: "model-load-failed",
|
package/dist/entry.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entry.js","names":[],"sources":["../src/executor/worker/run-worker.ts","../src/executor/worker/entry.ts"],"sourcesContent":["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 defaultLoadFactory,\n type BuildWorkerExecutorOptions,\n type WorkerExecutorStack,\n} from \"./build-worker-executor.js\";\nimport { createForwardingLogger } from \"./forwarding-logger.js\";\nimport type {\n DbConfig,\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\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 activeLoadFactory(msg.model.spec)) as DocumentModelModule;\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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"}
|