@powerhousedao/reactor 6.1.0-dev.4 → 6.1.0-dev.6

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 (37) hide show
  1. package/dist/build-worker-executor-DDVXB921.js +83 -0
  2. package/dist/build-worker-executor-DDVXB921.js.map +1 -0
  3. package/dist/document-indexer-B2iLRB0o.js +917 -0
  4. package/dist/document-indexer-B2iLRB0o.js.map +1 -0
  5. package/dist/drive-container-types-BNpMlgT_.js +2964 -0
  6. package/dist/drive-container-types-BNpMlgT_.js.map +1 -0
  7. package/dist/entry.d.ts +1 -0
  8. package/dist/entry.js +313 -0
  9. package/dist/entry.js.map +1 -0
  10. package/dist/error-info-Cpu4OY3o.js +62 -0
  11. package/dist/error-info-Cpu4OY3o.js.map +1 -0
  12. package/dist/errors-D3S6Eysd.js +56 -0
  13. package/dist/errors-D3S6Eysd.js.map +1 -0
  14. package/dist/forwarding-logger-BBkMSxuJ.js +85 -0
  15. package/dist/forwarding-logger-BBkMSxuJ.js.map +1 -0
  16. package/dist/index.d.ts +990 -75
  17. package/dist/index.d.ts.map +1 -1
  18. package/dist/index.js +889 -3890
  19. package/dist/index.js.map +1 -1
  20. package/dist/projection-entry.d.ts +1 -0
  21. package/dist/projection-entry.js +406 -0
  22. package/dist/projection-entry.js.map +1 -0
  23. package/dist/projection-shard-manager-_c7orNo5.js +313 -0
  24. package/dist/projection-shard-manager-_c7orNo5.js.map +1 -0
  25. package/dist/projection-worker-wI4PwcV2.js +13 -0
  26. package/dist/projection-worker-wI4PwcV2.js.map +1 -0
  27. package/dist/transport-ByGviWdZ.js +33 -0
  28. package/dist/transport-ByGviWdZ.js.map +1 -0
  29. package/dist/transport-CuogVKN_.js +23 -0
  30. package/dist/transport-CuogVKN_.js.map +1 -0
  31. package/dist/types-CxSpmNGK.js +32 -0
  32. package/dist/types-CxSpmNGK.js.map +1 -0
  33. package/dist/worker-SUoDhurA.js +22 -0
  34. package/dist/worker-SUoDhurA.js.map +1 -0
  35. package/dist/worker-handle-B1w03nRA.js +383 -0
  36. package/dist/worker-handle-B1w03nRA.js.map +1 -0
  37. package/package.json +6 -4
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,406 @@
1
+ import { S as CollectionMembershipCache, _ as KyselyWriteCache, d as KyselyKeyframeStore, f as DocumentModelRegistry, g as EventBus, n as REACTOR_SCHEMA, o as instrumentPgPool, s as KyselyOperationStore, v as KyselyOperationIndex, y as DocumentMetaCache } from "./drive-container-types-BNpMlgT_.js";
2
+ import { n as ReactorEventTypes } from "./types-CxSpmNGK.js";
3
+ import { a as ReadModelCoordinator, i as KyselyDocumentView, n as ConsistencyTracker, t as KyselyDocumentIndexer } from "./document-indexer-B2iLRB0o.js";
4
+ import { n as errorToInfo, t as createForwardingLogger } from "./forwarding-logger-BBkMSxuJ.js";
5
+ import { n as defaultLoadFactory } from "./build-worker-executor-DDVXB921.js";
6
+ import { ConsoleLogger } from "document-model";
7
+ import { isMainThread, parentPort } from "node:worker_threads";
8
+ //#region src/projection/projection-worker/build-projection-stack.ts
9
+ async function loadModelManifest(entries, loadFactory, registry, logger) {
10
+ for (const entry of entries) {
11
+ let module;
12
+ try {
13
+ module = await loadFactory(entry.spec);
14
+ } catch (error) {
15
+ logger.error("projection worker failed to load document model: @entry @error", entry, error);
16
+ throw error;
17
+ }
18
+ const [result] = registry.registerModules(module);
19
+ if (result.status === "error") {
20
+ logger.error("projection worker failed to register document model: @entry @error", entry, result.error);
21
+ throw result.error;
22
+ }
23
+ }
24
+ }
25
+ function instantiateReadModel(kind, database, operationStore, operationIndex, writeCache) {
26
+ switch (kind) {
27
+ case "document-view": return new KyselyDocumentView(database, operationStore, operationIndex, writeCache, new ConsistencyTracker());
28
+ case "document-indexer": return new KyselyDocumentIndexer(database, operationIndex, writeCache, new ConsistencyTracker());
29
+ default: {
30
+ const exhaustive = kind;
31
+ throw new Error(`unknown built-in read model kind: ${String(exhaustive)}`);
32
+ }
33
+ }
34
+ }
35
+ async function initReadModels(models, logger) {
36
+ for (const model of models) {
37
+ const maybeInit = model;
38
+ if (typeof maybeInit.init !== "function") continue;
39
+ try {
40
+ await maybeInit.init();
41
+ } catch (error) {
42
+ logger.error("projection worker read model init failed: @name @error", model.name, error);
43
+ throw error;
44
+ }
45
+ }
46
+ }
47
+ async function buildProjectionStack(options) {
48
+ const { init, database: baseDatabase, logger, events } = options;
49
+ const loadFactory = options.loadFactory ?? defaultLoadFactory;
50
+ const registry = new DocumentModelRegistry();
51
+ await loadModelManifest(init.models, loadFactory, registry, logger);
52
+ const database = baseDatabase.withSchema(REACTOR_SCHEMA);
53
+ const operationStore = new KyselyOperationStore(database);
54
+ const writeCache = new KyselyWriteCache(new KyselyKeyframeStore(database), operationStore, registry, {
55
+ maxDocuments: 100,
56
+ ringBufferSize: 10,
57
+ keyframeInterval: 10
58
+ });
59
+ await writeCache.startup();
60
+ const operationIndex = new KyselyOperationIndex(database);
61
+ await new DocumentMetaCache(operationStore, { maxDocuments: 1e3 }).startup();
62
+ new CollectionMembershipCache(operationIndex);
63
+ const preReady = init.preReadyKinds.map((kind) => instantiateReadModel(kind, database, operationStore, operationIndex, writeCache));
64
+ const postReady = init.postReadyKinds.map((kind) => instantiateReadModel(kind, database, operationStore, operationIndex, writeCache));
65
+ await initReadModels([...preReady, ...postReady], logger);
66
+ const eventBus = new EventBus();
67
+ const subscriptions = [];
68
+ subscriptions.push(eventBus.subscribe(ReactorEventTypes.JOB_READ_READY, (_t, event) => {
69
+ events.onReadReady(event);
70
+ }));
71
+ subscriptions.push(eventBus.subscribe(ReactorEventTypes.READMODEL_INDEXED, (_t, event) => {
72
+ events.onReadModelIndexed(event);
73
+ }));
74
+ subscriptions.push(eventBus.subscribe(ReactorEventTypes.READMODEL_BATCH_COMPLETED, (_t, event) => {
75
+ events.onBatchCompleted(event);
76
+ }));
77
+ const coordinator = new ReadModelCoordinator(eventBus, preReady, postReady);
78
+ coordinator.start();
79
+ return {
80
+ registry,
81
+ coordinator,
82
+ eventBus,
83
+ async relayWriteReady(event) {
84
+ await eventBus.emit(ReactorEventTypes.JOB_WRITE_READY, event);
85
+ },
86
+ getChainDepth() {
87
+ return coordinator.getChainDepth();
88
+ },
89
+ async drain() {
90
+ await coordinator.drain();
91
+ },
92
+ shutdown() {
93
+ coordinator.stop();
94
+ for (const unsub of subscriptions) unsub();
95
+ return Promise.resolve();
96
+ }
97
+ };
98
+ }
99
+ //#endregion
100
+ //#region src/projection/projection-worker/run-projection-worker.ts
101
+ const POOL_SAMPLE_INTERVAL_MS = 1e3;
102
+ async function defaultCreateDatabase(config, shardId) {
103
+ const { Kysely, PostgresDialect } = await import("kysely");
104
+ const Pool = (await import("pg")).default.Pool;
105
+ const pool = new Pool({
106
+ host: config.host,
107
+ port: config.port,
108
+ database: config.database,
109
+ user: config.user,
110
+ password: config.password,
111
+ ssl: config.ssl ? { rejectUnauthorized: false } : void 0,
112
+ application_name: config.applicationName ?? shardId,
113
+ max: config.poolSize,
114
+ connectionTimeoutMillis: config.connectionTimeoutMillis,
115
+ idleTimeoutMillis: config.idleTimeoutMillis
116
+ });
117
+ const poolInstrumentation = instrumentPgPool(pool, shardId);
118
+ const kysely = new Kysely({ dialect: new PostgresDialect({ pool }) });
119
+ return {
120
+ kysely,
121
+ poolInstrumentation,
122
+ async shutdown() {
123
+ try {
124
+ await kysely.destroy();
125
+ } catch {}
126
+ try {
127
+ await pool.end();
128
+ } catch {}
129
+ }
130
+ };
131
+ }
132
+ /**
133
+ * Drives the projection worker's message loop. Owns lifecycle of the
134
+ * database handle and the projection stack. The default factory builds a
135
+ * real Postgres pool; tests inject overrides for an in-process PGlite path.
136
+ */
137
+ function runProjectionWorker(parentPort, overrides = {}) {
138
+ let shardId = "";
139
+ let initCompleted = false;
140
+ let stack = null;
141
+ let database = null;
142
+ let depthTimer = null;
143
+ let lastReportedDepth = -1;
144
+ let poolSampleTimer = null;
145
+ let pendingPoolSamples = [];
146
+ let detachPoolListener = null;
147
+ function post(msg) {
148
+ parentPort.postMessage(msg);
149
+ }
150
+ function startPoolReporter(instrumentation) {
151
+ detachPoolListener = instrumentation.onAcquire((durationMs) => {
152
+ pendingPoolSamples.push(durationMs);
153
+ });
154
+ poolSampleTimer = setInterval(() => {
155
+ if (pendingPoolSamples.length === 0) return;
156
+ const durations = pendingPoolSamples;
157
+ pendingPoolSamples = [];
158
+ const stats = instrumentation.getStats();
159
+ post({
160
+ type: "pool-acquire-samples",
161
+ shardId,
162
+ poolName: instrumentation.name,
163
+ timestamp: Date.now(),
164
+ durations,
165
+ size: stats.size,
166
+ idle: stats.idle,
167
+ waiting: stats.waiting
168
+ });
169
+ }, POOL_SAMPLE_INTERVAL_MS);
170
+ poolSampleTimer.unref();
171
+ }
172
+ function stopPoolReporter() {
173
+ if (detachPoolListener) {
174
+ detachPoolListener();
175
+ detachPoolListener = null;
176
+ }
177
+ if (poolSampleTimer) {
178
+ clearInterval(poolSampleTimer);
179
+ poolSampleTimer = null;
180
+ }
181
+ pendingPoolSamples = [];
182
+ }
183
+ const logger = createForwardingLogger((msg) => post({
184
+ ...msg,
185
+ shardId
186
+ }));
187
+ process.on("uncaughtException", (err) => {
188
+ try {
189
+ post({
190
+ type: "log",
191
+ shardId,
192
+ level: "error",
193
+ message: "projection worker uncaughtException",
194
+ args: [errorToInfo(err)],
195
+ timestamp: Date.now()
196
+ });
197
+ } catch {}
198
+ throw err;
199
+ });
200
+ process.on("unhandledRejection", (reason) => {
201
+ try {
202
+ post({
203
+ type: "log",
204
+ shardId,
205
+ level: "error",
206
+ message: "projection worker unhandledRejection",
207
+ args: [errorToInfo(reason)],
208
+ timestamp: Date.now()
209
+ });
210
+ } catch {}
211
+ });
212
+ function startDepthReporter(intervalMs) {
213
+ if (intervalMs <= 0) return;
214
+ depthTimer = setInterval(() => {
215
+ if (!stack) return;
216
+ const depth = stack.getChainDepth();
217
+ if (depth === lastReportedDepth) return;
218
+ lastReportedDepth = depth;
219
+ post({
220
+ type: "chain-depth",
221
+ shardId,
222
+ depth,
223
+ timestamp: Date.now()
224
+ });
225
+ }, intervalMs);
226
+ depthTimer.unref();
227
+ }
228
+ function stopDepthReporter() {
229
+ if (depthTimer) {
230
+ clearInterval(depthTimer);
231
+ depthTimer = null;
232
+ }
233
+ }
234
+ async function handleInit(msg) {
235
+ shardId = msg.shardId;
236
+ database = await (overrides.createDatabase ?? defaultCreateDatabase)(msg.db, msg.shardId);
237
+ if (overrides.beforeBuildStack) await overrides.beforeBuildStack(database.kysely);
238
+ stack = await buildProjectionStack({
239
+ init: msg,
240
+ database: database.kysely,
241
+ logger: new ConsoleLogger([`projection-shard:${msg.shardId}`]),
242
+ loadFactory: overrides.loadFactory,
243
+ events: {
244
+ onReadReady: (event) => {
245
+ post({
246
+ type: "read-ready",
247
+ shardId,
248
+ jobId: event.jobId,
249
+ operations: event.operations
250
+ });
251
+ },
252
+ onReadModelIndexed: (event) => {
253
+ post({
254
+ type: "readmodel-indexed",
255
+ shardId,
256
+ jobId: event.jobId,
257
+ readModelName: event.readModelName,
258
+ stage: event.stage,
259
+ durationMs: event.durationMs,
260
+ operationCount: event.operationCount,
261
+ success: event.success
262
+ });
263
+ },
264
+ onBatchCompleted: (event) => {
265
+ post({
266
+ type: "readmodel-batch-completed",
267
+ shardId,
268
+ jobId: event.jobId,
269
+ batchSize: event.batchSize,
270
+ chainWaitDurationMs: event.chainWaitDurationMs,
271
+ preReadyDurationMs: event.preReadyDurationMs,
272
+ emitDurationMs: event.emitDurationMs,
273
+ postReadyDurationMs: event.postReadyDurationMs
274
+ });
275
+ }
276
+ }
277
+ });
278
+ initCompleted = true;
279
+ startDepthReporter(msg.chainDepthReportIntervalMs);
280
+ if (database.poolInstrumentation) startPoolReporter(database.poolInstrumentation);
281
+ logger.info("projection worker initialized: @shardId", msg.shardId);
282
+ post({
283
+ type: "ready",
284
+ correlationId: msg.correlationId,
285
+ shardId
286
+ });
287
+ }
288
+ async function handleWriteReady(msg) {
289
+ if (!stack) {
290
+ logger.warn("write-ready received before init on shard @shardId", shardId);
291
+ return;
292
+ }
293
+ const event = {
294
+ jobId: msg.jobId,
295
+ operations: msg.operations,
296
+ jobMeta: msg.jobMeta,
297
+ collectionMemberships: msg.collectionMemberships
298
+ };
299
+ await stack.relayWriteReady(event);
300
+ }
301
+ async function handleDrain(correlationId) {
302
+ if (stack) await stack.drain();
303
+ post({
304
+ type: "drained",
305
+ correlationId,
306
+ shardId
307
+ });
308
+ }
309
+ async function shutdownStack() {
310
+ stopDepthReporter();
311
+ stopPoolReporter();
312
+ if (stack) {
313
+ try {
314
+ await stack.drain();
315
+ } catch (error) {
316
+ logger.warn("projection worker drain failed during shutdown: @error", error);
317
+ }
318
+ try {
319
+ await stack.shutdown();
320
+ } catch (error) {
321
+ logger.warn("projection worker stack shutdown failed: @error", error);
322
+ }
323
+ stack = null;
324
+ }
325
+ if (database) {
326
+ await database.shutdown();
327
+ database = null;
328
+ }
329
+ }
330
+ function handleParentMessage(msg) {
331
+ switch (msg.type) {
332
+ case "init":
333
+ handleInit(msg).catch((err) => {
334
+ post({
335
+ type: "log",
336
+ shardId,
337
+ level: "error",
338
+ message: "projection worker init failed",
339
+ args: [errorToInfo(err)],
340
+ timestamp: Date.now()
341
+ });
342
+ });
343
+ break;
344
+ case "write-ready":
345
+ if (!initCompleted) {
346
+ logger.warn("write-ready received before init on shard @shardId", shardId);
347
+ break;
348
+ }
349
+ handleWriteReady(msg).catch((err) => {
350
+ post({
351
+ type: "log",
352
+ shardId,
353
+ level: "error",
354
+ message: "projection worker write-ready failed",
355
+ args: [errorToInfo(err)],
356
+ timestamp: Date.now()
357
+ });
358
+ });
359
+ break;
360
+ case "drain":
361
+ handleDrain(msg.correlationId).catch((err) => {
362
+ post({
363
+ type: "log",
364
+ shardId,
365
+ level: "error",
366
+ message: "projection worker drain failed",
367
+ args: [errorToInfo(err)],
368
+ timestamp: Date.now()
369
+ });
370
+ });
371
+ break;
372
+ case "shutdown":
373
+ logger.info("projection worker shutting down: @shardId", shardId);
374
+ shutdownStack().finally(() => {
375
+ post({
376
+ type: "log",
377
+ shardId,
378
+ level: "info",
379
+ message: "projection worker shutdown",
380
+ args: [],
381
+ timestamp: Date.now()
382
+ });
383
+ process.exit(0);
384
+ });
385
+ break;
386
+ default: break;
387
+ }
388
+ }
389
+ parentPort.on("message", handleParentMessage);
390
+ parentPort.__reactorProjectionWorkerHarness = {
391
+ handleParentMessage,
392
+ get initCompleted() {
393
+ return initCompleted;
394
+ },
395
+ get shardId() {
396
+ return shardId;
397
+ }
398
+ };
399
+ }
400
+ //#endregion
401
+ //#region src/projection/projection-worker/projection-entry.ts
402
+ if (isMainThread || parentPort === null) throw new Error("projection-worker entry.ts must be run as a worker thread");
403
+ runProjectionWorker(parentPort);
404
+ //#endregion
405
+
406
+ //# sourceMappingURL=projection-entry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projection-entry.js","names":[],"sources":["../src/projection/projection-worker/build-projection-stack.ts","../src/projection/projection-worker/run-projection-worker.ts","../src/projection/projection-worker/projection-entry.ts"],"sourcesContent":["/**\n * In-worker projection stack builder.\n *\n * Mirrors {@link buildWorkerExecutor} but for the projection-side\n * read models. The worker owns one full copy of the storage stack — read\n * cache, operation index, document-meta cache — bound to its own\n * pg.Pool/Kysely, plus an in-process `ReadModelCoordinator` that\n * subscribes to a local `EventBus`.\n *\n * The host relays JOB_WRITE_READY into the local bus by calling\n * `relayWriteReady`, and forwards the local bus's JOB_READ_READY plus\n * READMODEL_* events back to the host bus via the IPC bridge.\n */\n\nimport type { DocumentModelModule } 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 type { Database } from \"../../core/types.js\";\nimport { EventBus } from \"../../events/event-bus.js\";\nimport {\n ReactorEventTypes,\n type JobReadReadyEvent,\n type JobWriteReadyEvent,\n type ReadModelBatchCompletedEvent,\n type ReadModelIndexedEvent,\n type Unsubscribe,\n} from \"../../events/types.js\";\nimport {\n defaultLoadFactory,\n type BuildWorkerExecutorOptions,\n} from \"../../executor/worker/build-worker-executor.js\";\nimport type {\n FactorySpec,\n ModelManifestEntry,\n} from \"../../executor/worker/protocol.js\";\nimport { ReadModelCoordinator } from \"../../read-models/coordinator.js\";\nimport { KyselyDocumentView } from \"../../read-models/document-view.js\";\nimport type { IReadModel } from \"../../read-models/interfaces.js\";\nimport { DocumentModelRegistry } from \"../../registry/implementation.js\";\nimport { ConsistencyTracker } from \"../../shared/consistency-tracker.js\";\nimport {\n KyselyDocumentIndexer,\n type IndexerDatabase,\n} from \"../../storage/kysely/document-indexer.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 type {\n BuiltInReadModelKind,\n ProjectionInitMessage,\n} from \"../protocol.js\";\n\nexport type ProjectionStackEvents = {\n onReadReady: (event: JobReadReadyEvent) => void;\n onReadModelIndexed: (event: ReadModelIndexedEvent) => void;\n onBatchCompleted: (event: ReadModelBatchCompletedEvent) => void;\n};\n\nexport type ProjectionStack = {\n registry: DocumentModelRegistry;\n coordinator: ReadModelCoordinator;\n eventBus: EventBus;\n relayWriteReady(event: JobWriteReadyEvent): Promise<void>;\n getChainDepth(): number;\n drain(): Promise<void>;\n shutdown(): Promise<void>;\n};\n\nexport type BuildProjectionStackOptions = {\n init: ProjectionInitMessage;\n database: Kysely<Database>;\n logger: ILogger;\n events: ProjectionStackEvents;\n loadFactory?: BuildWorkerExecutorOptions[\"loadFactory\"];\n driveContainerTypes?: ReadonlySet<string>;\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 \"projection 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 \"projection worker failed to register document model: @entry @error\",\n entry,\n result.error,\n );\n throw result.error;\n }\n }\n}\n\nfunction instantiateReadModel(\n kind: BuiltInReadModelKind,\n database: Kysely<Database>,\n operationStore: KyselyOperationStore,\n operationIndex: KyselyOperationIndex,\n writeCache: KyselyWriteCache,\n): IReadModel {\n switch (kind) {\n case \"document-view\": {\n return new KyselyDocumentView(\n // @ts-expect-error - Database superset\n database,\n operationStore,\n operationIndex,\n writeCache,\n new ConsistencyTracker(),\n );\n }\n case \"document-indexer\": {\n return new KyselyDocumentIndexer(\n database as unknown as Kysely<IndexerDatabase>,\n operationIndex,\n writeCache,\n new ConsistencyTracker(),\n );\n }\n default: {\n const exhaustive: never = kind;\n throw new Error(\n `unknown built-in read model kind: ${String(exhaustive)}`,\n );\n }\n }\n}\n\nasync function initReadModels(\n models: IReadModel[],\n logger: ILogger,\n): Promise<void> {\n for (const model of models) {\n const maybeInit = model as IReadModel & { init?: () => Promise<void> };\n if (typeof maybeInit.init !== \"function\") {\n continue;\n }\n try {\n await maybeInit.init();\n } catch (error) {\n logger.error(\n \"projection worker read model init failed: @name @error\",\n model.name,\n error,\n );\n throw error;\n }\n }\n}\n\nexport async function buildProjectionStack(\n options: BuildProjectionStackOptions,\n): Promise<ProjectionStack> {\n const { init, database: baseDatabase, logger, events } = options;\n const loadFactory = options.loadFactory ?? defaultLoadFactory;\n\n const registry = new DocumentModelRegistry();\n await loadModelManifest(init.models, loadFactory, registry, logger);\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 void collectionMembershipCache;\n void documentMetaCache;\n\n const preReady = init.preReadyKinds.map((kind) =>\n instantiateReadModel(\n kind,\n database,\n operationStore,\n operationIndex,\n writeCache,\n ),\n );\n const postReady = init.postReadyKinds.map((kind) =>\n instantiateReadModel(\n kind,\n database,\n operationStore,\n operationIndex,\n writeCache,\n ),\n );\n\n await initReadModels([...preReady, ...postReady], logger);\n\n const eventBus = new EventBus();\n const subscriptions: Unsubscribe[] = [];\n\n subscriptions.push(\n eventBus.subscribe(\n ReactorEventTypes.JOB_READ_READY,\n (_t: number, event: JobReadReadyEvent) => {\n events.onReadReady(event);\n },\n ),\n );\n subscriptions.push(\n eventBus.subscribe(\n ReactorEventTypes.READMODEL_INDEXED,\n (_t: number, event: ReadModelIndexedEvent) => {\n events.onReadModelIndexed(event);\n },\n ),\n );\n subscriptions.push(\n eventBus.subscribe(\n ReactorEventTypes.READMODEL_BATCH_COMPLETED,\n (_t: number, event: ReadModelBatchCompletedEvent) => {\n events.onBatchCompleted(event);\n },\n ),\n );\n\n const coordinator = new ReadModelCoordinator(eventBus, preReady, postReady);\n coordinator.start();\n\n return {\n registry,\n coordinator,\n eventBus,\n async relayWriteReady(event: JobWriteReadyEvent): Promise<void> {\n await eventBus.emit(ReactorEventTypes.JOB_WRITE_READY, event);\n },\n getChainDepth(): number {\n return coordinator.getChainDepth();\n },\n async drain(): Promise<void> {\n await coordinator.drain();\n },\n shutdown(): Promise<void> {\n coordinator.stop();\n for (const unsub of subscriptions) {\n unsub();\n }\n return Promise.resolve();\n },\n };\n}\n","import { ConsoleLogger } from \"document-model\";\nimport type { Kysely } from \"kysely\";\nimport type { MessagePort } from \"node:worker_threads\";\nimport type { Database } from \"../../core/types.js\";\nimport { createForwardingLogger } from \"../../executor/worker/forwarding-logger.js\";\nimport type { DbConfig } from \"../../executor/worker/protocol.js\";\nimport { errorToInfo } from \"../../executor/worker/sanitize.js\";\nimport type {\n JobReadReadyEvent,\n JobWriteReadyEvent,\n ReadModelBatchCompletedEvent,\n ReadModelIndexedEvent,\n} from \"../../events/types.js\";\nimport {\n instrumentPgPool,\n type PoolInstrumentation,\n} from \"../../storage/pool-instrumentation.js\";\nimport type {\n ProjectionInitMessage,\n ProjectionParentMessage,\n ProjectionWorkerMessage,\n} from \"../protocol.js\";\nimport {\n buildProjectionStack,\n type ProjectionStack,\n} from \"./build-projection-stack.js\";\n\nconst POOL_SAMPLE_INTERVAL_MS = 1_000;\n\n/**\n * Closeable handle around the worker's Postgres pool. Decoupled from the\n * default factory so tests can swap in PGlite via `RunProjectionWorkerOverrides`.\n *\n * When the worker owns a real pg.Pool, the handle exposes a\n * {@link PoolInstrumentation} so the run loop can forward acquire-wait\n * samples to the host. Tests that swap in PGlite leave this undefined.\n */\nexport type ProjectionWorkerDatabaseHandle = {\n kysely: Kysely<Database>;\n poolInstrumentation?: PoolInstrumentation;\n shutdown(): Promise<void>;\n};\n\nexport type RunProjectionWorkerOverrides = {\n createDatabase?: (\n config: DbConfig,\n shardId: string,\n ) => Promise<ProjectionWorkerDatabaseHandle>;\n loadFactory?: Parameters<typeof buildProjectionStack>[0][\"loadFactory\"];\n beforeBuildStack?: (db: Kysely<Database>) => Promise<void>;\n};\n\nasync function defaultCreateDatabase(\n config: DbConfig,\n shardId: string,\n): Promise<ProjectionWorkerDatabaseHandle> {\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 ?? shardId,\n max: config.poolSize,\n connectionTimeoutMillis: config.connectionTimeoutMillis,\n idleTimeoutMillis: config.idleTimeoutMillis,\n });\n const poolInstrumentation = instrumentPgPool(pool, shardId);\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 projection worker's message loop. Owns lifecycle of the\n * database handle and the projection stack. The default factory builds a\n * real Postgres pool; tests inject overrides for an in-process PGlite path.\n */\nexport function runProjectionWorker(\n parentPort: MessagePort,\n overrides: RunProjectionWorkerOverrides = {},\n): void {\n let shardId = \"\";\n let initCompleted = false;\n let stack: ProjectionStack | null = null;\n let database: ProjectionWorkerDatabaseHandle | null = null;\n let depthTimer: NodeJS.Timeout | null = null;\n let lastReportedDepth = -1;\n let poolSampleTimer: NodeJS.Timeout | null = null;\n let pendingPoolSamples: number[] = [];\n let detachPoolListener: (() => void) | null = null;\n\n function post(msg: ProjectionWorkerMessage): void {\n parentPort.postMessage(msg);\n }\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 shardId,\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 const logger = createForwardingLogger((msg) => post({ ...msg, shardId }));\n\n process.on(\"uncaughtException\", (err: unknown) => {\n try {\n post({\n type: \"log\",\n shardId,\n level: \"error\",\n message: \"projection 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 shardId,\n level: \"error\",\n message: \"projection worker unhandledRejection\",\n args: [errorToInfo(reason)],\n timestamp: Date.now(),\n });\n } catch {\n // nothing left to do\n }\n });\n\n function startDepthReporter(intervalMs: number): void {\n if (intervalMs <= 0) {\n return;\n }\n depthTimer = setInterval(() => {\n if (!stack) {\n return;\n }\n const depth = stack.getChainDepth();\n if (depth === lastReportedDepth) {\n return;\n }\n lastReportedDepth = depth;\n post({\n type: \"chain-depth\",\n shardId,\n depth,\n timestamp: Date.now(),\n });\n }, intervalMs);\n depthTimer.unref();\n }\n\n function stopDepthReporter(): void {\n if (depthTimer) {\n clearInterval(depthTimer);\n depthTimer = null;\n }\n }\n\n async function handleInit(msg: ProjectionInitMessage): Promise<void> {\n shardId = msg.shardId;\n const createDb = overrides.createDatabase ?? defaultCreateDatabase;\n database = await createDb(msg.db, msg.shardId);\n if (overrides.beforeBuildStack) {\n await overrides.beforeBuildStack(database.kysely);\n }\n stack = await buildProjectionStack({\n init: msg,\n database: database.kysely,\n logger: new ConsoleLogger([`projection-shard:${msg.shardId}`]),\n loadFactory: overrides.loadFactory,\n events: {\n onReadReady: (event: JobReadReadyEvent) => {\n post({\n type: \"read-ready\",\n shardId,\n jobId: event.jobId,\n operations: event.operations,\n });\n },\n onReadModelIndexed: (event: ReadModelIndexedEvent) => {\n post({\n type: \"readmodel-indexed\",\n shardId,\n jobId: event.jobId,\n readModelName: event.readModelName,\n stage: event.stage,\n durationMs: event.durationMs,\n operationCount: event.operationCount,\n success: event.success,\n });\n },\n onBatchCompleted: (event: ReadModelBatchCompletedEvent) => {\n post({\n type: \"readmodel-batch-completed\",\n shardId,\n jobId: event.jobId,\n batchSize: event.batchSize,\n chainWaitDurationMs: event.chainWaitDurationMs,\n preReadyDurationMs: event.preReadyDurationMs,\n emitDurationMs: event.emitDurationMs,\n postReadyDurationMs: event.postReadyDurationMs,\n });\n },\n },\n });\n initCompleted = true;\n startDepthReporter(msg.chainDepthReportIntervalMs);\n if (database.poolInstrumentation) {\n startPoolReporter(database.poolInstrumentation);\n }\n logger.info(\"projection worker initialized: @shardId\", msg.shardId);\n post({ type: \"ready\", correlationId: msg.correlationId, shardId });\n }\n\n async function handleWriteReady(\n msg: Extract<ProjectionParentMessage, { type: \"write-ready\" }>,\n ): Promise<void> {\n if (!stack) {\n logger.warn(\n \"write-ready received before init on shard @shardId\",\n shardId,\n );\n return;\n }\n const event: JobWriteReadyEvent = {\n jobId: msg.jobId,\n operations: msg.operations,\n jobMeta: msg.jobMeta,\n collectionMemberships: msg.collectionMemberships,\n };\n await stack.relayWriteReady(event);\n }\n\n async function handleDrain(correlationId: string): Promise<void> {\n if (stack) {\n await stack.drain();\n }\n post({ type: \"drained\", correlationId, shardId });\n }\n\n async function shutdownStack(): Promise<void> {\n stopDepthReporter();\n stopPoolReporter();\n if (stack) {\n try {\n await stack.drain();\n } catch (error) {\n logger.warn(\n \"projection worker drain failed during shutdown: @error\",\n error,\n );\n }\n try {\n await stack.shutdown();\n } catch (error) {\n logger.warn(\"projection worker stack shutdown failed: @error\", error);\n }\n stack = null;\n }\n if (database) {\n await database.shutdown();\n database = null;\n }\n }\n\n function handleParentMessage(msg: ProjectionParentMessage): void {\n switch (msg.type) {\n case \"init\": {\n handleInit(msg).catch((err: unknown) => {\n post({\n type: \"log\",\n shardId,\n level: \"error\",\n message: \"projection worker init failed\",\n args: [errorToInfo(err)],\n timestamp: Date.now(),\n });\n });\n break;\n }\n case \"write-ready\": {\n if (!initCompleted) {\n logger.warn(\n \"write-ready received before init on shard @shardId\",\n shardId,\n );\n break;\n }\n handleWriteReady(msg).catch((err: unknown) => {\n post({\n type: \"log\",\n shardId,\n level: \"error\",\n message: \"projection worker write-ready failed\",\n args: [errorToInfo(err)],\n timestamp: Date.now(),\n });\n });\n break;\n }\n case \"drain\": {\n handleDrain(msg.correlationId).catch((err: unknown) => {\n post({\n type: \"log\",\n shardId,\n level: \"error\",\n message: \"projection worker drain failed\",\n args: [errorToInfo(err)],\n timestamp: Date.now(),\n });\n });\n break;\n }\n case \"shutdown\": {\n logger.info(\"projection worker shutting down: @shardId\", shardId);\n void shutdownStack().finally(() => {\n post({\n type: \"log\",\n shardId,\n level: \"info\",\n message: \"projection worker shutdown\",\n args: [],\n timestamp: Date.now(),\n });\n process.exit(0);\n });\n break;\n }\n default: {\n const exhaustive: never = msg;\n void exhaustive;\n break;\n }\n }\n }\n\n parentPort.on(\"message\", handleParentMessage);\n\n const harness = {\n handleParentMessage,\n get initCompleted(): boolean {\n return initCompleted;\n },\n get shardId(): string {\n return shardId;\n },\n };\n (\n parentPort as unknown as { __reactorProjectionWorkerHarness?: unknown }\n ).__reactorProjectionWorkerHarness = harness;\n}\n","import { isMainThread, parentPort } from \"node:worker_threads\";\nimport { runProjectionWorker } from \"./run-projection-worker.js\";\n\nif (isMainThread || parentPort === null) {\n throw new Error(\"projection-worker entry.ts must be run as a worker thread\");\n}\n\nrunProjectionWorker(parentPort);\n"],"mappings":"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"}