@powerhousedao/powerhouse-vetra-packages 6.1.0-dev.1 → 6.1.0-dev.10

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 (69) hide show
  1. package/dist/browser/assets/entry-Bzani6_n.js +313 -0
  2. package/dist/browser/assets/projection-entry-Bpu-8SnI.js +406 -0
  3. package/dist/browser/{connect-CKdlDSUw.js → connect-BuIyoCt-.js} +4 -4
  4. package/dist/browser/connect-BuIyoCt-.js.map +1 -0
  5. package/dist/browser/{dist-DOMEWT3x.js → dist-B8tyEIWi.js} +2 -2
  6. package/dist/browser/{dist-DOMEWT3x.js.map → dist-B8tyEIWi.js.map} +1 -1
  7. package/dist/browser/{dist-CC1E3l2O.js → dist-DRpNFJjV.js} +6 -4
  8. package/dist/browser/dist-DRpNFJjV.js.map +1 -0
  9. package/dist/browser/{dist-DQgJ8n4d.js → document-drive-uzcF-N7-.js} +371 -279
  10. package/dist/browser/document-drive-uzcF-N7-.js.map +1 -0
  11. package/dist/browser/document-models/index.js +1 -1
  12. package/dist/browser/{editor-Dm_73jiz.js → editor-C7dRl1Fh.js} +5 -5
  13. package/dist/browser/{editor-Dm_73jiz.js.map → editor-C7dRl1Fh.js.map} +1 -1
  14. package/dist/browser/{editor-B3yz7YdR.js → editor-D26bYkoR.js} +14 -13
  15. package/dist/browser/editor-D26bYkoR.js.map +1 -0
  16. package/dist/browser/editors/document-model-editor/module.js +1 -1
  17. package/dist/browser/editors/generic-drive-explorer/index.js +4 -4
  18. package/dist/browser/editors/generic-drive-explorer/module.js +1 -1
  19. package/dist/browser/{folder-view-H2ov-zId.js → folder-view-Bc86uo1d.js} +3 -3
  20. package/dist/browser/{folder-view-H2ov-zId.js.map → folder-view-Bc86uo1d.js.map} +1 -1
  21. package/dist/browser/{graphql-editor-CnaQ3XWx.js → graphql-editor-Djox2qqh.js} +2 -2
  22. package/dist/browser/{graphql-editor-CnaQ3XWx.js.map → graphql-editor-Djox2qqh.js.map} +1 -1
  23. package/dist/browser/index.js +2 -2
  24. package/dist/browser/{schema-context-C0GpuR27.js → schema-context-DeMVEHA-.js} +2 -2
  25. package/dist/browser/schema-context-DeMVEHA-.js.map +1 -0
  26. package/dist/browser/{state-schemas-C5NPeV6-.js → state-schemas-hDzujS2Q.js} +5 -5
  27. package/dist/browser/{state-schemas-C5NPeV6-.js.map → state-schemas-hDzujS2Q.js.map} +1 -1
  28. package/dist/browser/{style-DYS_RFSN.js → style-D4P9J6ZR.js} +4 -4
  29. package/dist/browser/style-D4P9J6ZR.js.map +1 -0
  30. package/dist/node/{connect-SGvLzr5K.mjs → connect-u7HVp5xR.mjs} +4 -4
  31. package/dist/node/connect-u7HVp5xR.mjs.map +1 -0
  32. package/dist/node/{dist-1kPMPFPD.mjs → dist-BSjsG8Nj.mjs} +2 -2
  33. package/dist/node/{dist-1kPMPFPD.mjs.map → dist-BSjsG8Nj.mjs.map} +1 -1
  34. package/dist/node/{dist-Cay1iRRr.mjs → dist-CubQ-Ql0.mjs} +6 -4
  35. package/dist/node/dist-CubQ-Ql0.mjs.map +1 -0
  36. package/dist/node/{dist-Bz4SgEHs.mjs → document-drive-VfCGuVCc.mjs} +371 -279
  37. package/dist/node/document-drive-VfCGuVCc.mjs.map +1 -0
  38. package/dist/node/document-models/index.mjs +1 -1
  39. package/dist/node/{editor-BaXuDsby.mjs → editor-BsoN1XNa.mjs} +5 -5
  40. package/dist/node/{editor-BaXuDsby.mjs.map → editor-BsoN1XNa.mjs.map} +1 -1
  41. package/dist/node/{editor-W8QOlGXD.mjs → editor-CBABx_QB.mjs} +14 -13
  42. package/dist/node/editor-CBABx_QB.mjs.map +1 -0
  43. package/dist/node/editors/document-model-editor/module.mjs +1 -1
  44. package/dist/node/editors/generic-drive-explorer/index.mjs +4 -4
  45. package/dist/node/editors/generic-drive-explorer/module.mjs +1 -1
  46. package/dist/node/{folder-view-B0FNXbc0.mjs → folder-view-VaGWNRsV.mjs} +3 -3
  47. package/dist/node/{folder-view-B0FNXbc0.mjs.map → folder-view-VaGWNRsV.mjs.map} +1 -1
  48. package/dist/node/{graphql-editor-D1koK5kR.mjs → graphql-editor-CvDfPfYy.mjs} +2 -2
  49. package/dist/node/{graphql-editor-D1koK5kR.mjs.map → graphql-editor-CvDfPfYy.mjs.map} +1 -1
  50. package/dist/node/index.mjs +2 -2
  51. package/dist/node/{schema-context-mLgEY1Hh.mjs → schema-context-BlHpyf8E.mjs} +2 -2
  52. package/dist/node/schema-context-BlHpyf8E.mjs.map +1 -0
  53. package/dist/node/{state-schemas-DAthoEEk.mjs → state-schemas-BUmvotrJ.mjs} +5 -5
  54. package/dist/node/{state-schemas-DAthoEEk.mjs.map → state-schemas-BUmvotrJ.mjs.map} +1 -1
  55. package/dist/node/{style-CxHkfyna.mjs → style-C449avuc.mjs} +4 -4
  56. package/dist/node/style-C449avuc.mjs.map +1 -0
  57. package/package.json +6 -6
  58. package/dist/browser/connect-CKdlDSUw.js.map +0 -1
  59. package/dist/browser/dist-CC1E3l2O.js.map +0 -1
  60. package/dist/browser/dist-DQgJ8n4d.js.map +0 -1
  61. package/dist/browser/editor-B3yz7YdR.js.map +0 -1
  62. package/dist/browser/schema-context-C0GpuR27.js.map +0 -1
  63. package/dist/browser/style-DYS_RFSN.js.map +0 -1
  64. package/dist/node/connect-SGvLzr5K.mjs.map +0 -1
  65. package/dist/node/dist-Bz4SgEHs.mjs.map +0 -1
  66. package/dist/node/dist-Cay1iRRr.mjs.map +0 -1
  67. package/dist/node/editor-W8QOlGXD.mjs.map +0 -1
  68. package/dist/node/schema-context-mLgEY1Hh.mjs.map +0 -1
  69. package/dist/node/style-CxHkfyna.mjs.map +0 -1
@@ -0,0 +1,313 @@
1
+ import { o as instrumentPgPool } from "./drive-container-types-BNpMlgT_.js";
2
+ import { n as errorToInfo, t as createForwardingLogger } from "./forwarding-logger-BBkMSxuJ.js";
3
+ import { n as defaultLoadFactory, t as buildWorkerExecutor } from "./build-worker-executor-DDVXB921.js";
4
+ import { ConsoleLogger } from "document-model";
5
+ import { isMainThread, parentPort } from "node:worker_threads";
6
+ //#region src/executor/worker/run-worker.ts
7
+ const POOL_SAMPLE_INTERVAL_MS = 1e3;
8
+ async function defaultCreateDatabase(config, workerId) {
9
+ const { Kysely, PostgresDialect } = await import("kysely");
10
+ const Pool = (await import("pg")).default.Pool;
11
+ const pool = new Pool({
12
+ host: config.host,
13
+ port: config.port,
14
+ database: config.database,
15
+ user: config.user,
16
+ password: config.password,
17
+ ssl: config.ssl ? { rejectUnauthorized: false } : void 0,
18
+ application_name: config.applicationName ?? workerId,
19
+ max: config.poolSize,
20
+ connectionTimeoutMillis: config.connectionTimeoutMillis,
21
+ idleTimeoutMillis: config.idleTimeoutMillis
22
+ });
23
+ const poolInstrumentation = instrumentPgPool(pool, workerId);
24
+ const kysely = new Kysely({ dialect: new PostgresDialect({ pool }) });
25
+ return {
26
+ kysely,
27
+ poolInstrumentation,
28
+ async shutdown() {
29
+ try {
30
+ await kysely.destroy();
31
+ } catch {}
32
+ try {
33
+ await pool.end();
34
+ } catch {}
35
+ }
36
+ };
37
+ }
38
+ /**
39
+ * Drives the worker's message loop. Owns lifecycle of the database handle
40
+ * and executor stack. The default factories build a real Postgres pool and
41
+ * use dynamic `import()` for model/verifier specs; tests inject overrides
42
+ * for an in-process PGlite path.
43
+ */
44
+ function runWorker(parentPort, overrides = {}) {
45
+ let workerId = "";
46
+ let initCompleted = false;
47
+ let initConfig = null;
48
+ let executorStack = null;
49
+ let database = null;
50
+ let poolSampleTimer = null;
51
+ let pendingPoolSamples = [];
52
+ let detachPoolListener = null;
53
+ const activeLoadFactory = overrides.loadFactory ?? defaultLoadFactory;
54
+ function post(msg) {
55
+ parentPort.postMessage(msg);
56
+ }
57
+ const logger = createForwardingLogger(post);
58
+ function startPoolReporter(instrumentation) {
59
+ detachPoolListener = instrumentation.onAcquire((durationMs) => {
60
+ pendingPoolSamples.push(durationMs);
61
+ });
62
+ poolSampleTimer = setInterval(() => {
63
+ if (pendingPoolSamples.length === 0) return;
64
+ const durations = pendingPoolSamples;
65
+ pendingPoolSamples = [];
66
+ const stats = instrumentation.getStats();
67
+ post({
68
+ type: "pool-acquire-samples",
69
+ workerId,
70
+ poolName: instrumentation.name,
71
+ timestamp: Date.now(),
72
+ durations,
73
+ size: stats.size,
74
+ idle: stats.idle,
75
+ waiting: stats.waiting
76
+ });
77
+ }, POOL_SAMPLE_INTERVAL_MS);
78
+ poolSampleTimer.unref();
79
+ }
80
+ function stopPoolReporter() {
81
+ if (detachPoolListener) {
82
+ detachPoolListener();
83
+ detachPoolListener = null;
84
+ }
85
+ if (poolSampleTimer) {
86
+ clearInterval(poolSampleTimer);
87
+ poolSampleTimer = null;
88
+ }
89
+ pendingPoolSamples = [];
90
+ }
91
+ process.on("uncaughtException", (err) => {
92
+ try {
93
+ post({
94
+ type: "log",
95
+ level: "error",
96
+ message: "worker uncaughtException",
97
+ args: [errorToInfo(err)],
98
+ timestamp: Date.now()
99
+ });
100
+ } catch {}
101
+ throw err;
102
+ });
103
+ process.on("unhandledRejection", (reason) => {
104
+ try {
105
+ post({
106
+ type: "log",
107
+ level: "error",
108
+ message: "worker unhandledRejection",
109
+ args: [errorToInfo(reason)],
110
+ timestamp: Date.now()
111
+ });
112
+ } catch {}
113
+ });
114
+ async function handleInit(msg) {
115
+ workerId = msg.workerId;
116
+ initConfig = msg;
117
+ database = await (overrides.createDatabase ?? defaultCreateDatabase)(msg.db, msg.workerId);
118
+ if (overrides.beforeBuildExecutor) await overrides.beforeBuildExecutor(database.kysely);
119
+ executorStack = await buildWorkerExecutor({
120
+ init: msg,
121
+ database: database.kysely,
122
+ logger: new ConsoleLogger([`reactor-worker:${msg.workerId}`]),
123
+ loadFactory: activeLoadFactory
124
+ });
125
+ if (database.poolInstrumentation) startPoolReporter(database.poolInstrumentation);
126
+ initCompleted = true;
127
+ logger.info("worker initialized: @workerId", msg.workerId);
128
+ post({
129
+ type: "ready",
130
+ correlationId: msg.correlationId,
131
+ workerId: msg.workerId
132
+ });
133
+ }
134
+ async function handleExecute(correlationId, job) {
135
+ if (!executorStack) {
136
+ post({
137
+ type: "result",
138
+ correlationId,
139
+ result: {
140
+ job,
141
+ success: false
142
+ },
143
+ error: errorToInfo(/* @__PURE__ */ new Error("execute received before init"))
144
+ });
145
+ return;
146
+ }
147
+ try {
148
+ post({
149
+ type: "result",
150
+ correlationId,
151
+ result: await executorStack.executor.executeJob(job),
152
+ writeReady: executorStack.takeLastWriteReady() ?? void 0
153
+ });
154
+ } catch (error) {
155
+ post({
156
+ type: "result",
157
+ correlationId,
158
+ result: {
159
+ job,
160
+ success: false
161
+ },
162
+ error: errorToInfo(error)
163
+ });
164
+ }
165
+ }
166
+ async function handleLoadModel(msg) {
167
+ const stack = executorStack;
168
+ if (!stack) {
169
+ post({
170
+ type: "model-load-failed",
171
+ correlationId: msg.correlationId,
172
+ documentType: msg.model.documentType,
173
+ version: msg.model.version,
174
+ error: errorToInfo(/* @__PURE__ */ new Error("load-model received before init"))
175
+ });
176
+ return;
177
+ }
178
+ let module;
179
+ try {
180
+ module = await activeLoadFactory(msg.model.spec);
181
+ } catch (error) {
182
+ post({
183
+ type: "model-load-failed",
184
+ correlationId: msg.correlationId,
185
+ documentType: msg.model.documentType,
186
+ version: msg.model.version,
187
+ error: errorToInfo(error)
188
+ });
189
+ return;
190
+ }
191
+ const [result] = stack.registry.registerModules(module);
192
+ if (result.status === "error") {
193
+ post({
194
+ type: "model-load-failed",
195
+ correlationId: msg.correlationId,
196
+ documentType: msg.model.documentType,
197
+ version: msg.model.version,
198
+ error: errorToInfo(result.error)
199
+ });
200
+ return;
201
+ }
202
+ post({
203
+ type: "model-loaded",
204
+ correlationId: msg.correlationId,
205
+ documentType: msg.model.documentType,
206
+ version: msg.model.version
207
+ });
208
+ }
209
+ async function shutdownDatabase() {
210
+ stopPoolReporter();
211
+ if (database) await database.shutdown();
212
+ }
213
+ function handleParentMessage(msg) {
214
+ switch (msg.type) {
215
+ case "init":
216
+ handleInit(msg).catch((err) => {
217
+ post({
218
+ type: "log",
219
+ level: "error",
220
+ message: "worker init failed",
221
+ args: [errorToInfo(err)],
222
+ timestamp: Date.now()
223
+ });
224
+ });
225
+ break;
226
+ case "execute":
227
+ if (!initCompleted) {
228
+ logger.warn("received execute before init");
229
+ post({
230
+ type: "result",
231
+ correlationId: msg.correlationId,
232
+ result: {
233
+ job: msg.job,
234
+ success: false
235
+ },
236
+ error: errorToInfo(/* @__PURE__ */ new Error("execute received before init"))
237
+ });
238
+ break;
239
+ }
240
+ handleExecute(msg.correlationId, msg.job).catch((err) => {
241
+ post({
242
+ type: "result",
243
+ correlationId: msg.correlationId,
244
+ result: {
245
+ job: msg.job,
246
+ success: false
247
+ },
248
+ error: errorToInfo(err)
249
+ });
250
+ });
251
+ break;
252
+ case "shutdown":
253
+ logger.info("worker shutting down: @workerId", workerId);
254
+ shutdownDatabase().finally(() => {
255
+ post({
256
+ type: "log",
257
+ level: "info",
258
+ message: "worker shutdown",
259
+ args: [],
260
+ timestamp: Date.now()
261
+ });
262
+ process.exit(0);
263
+ });
264
+ break;
265
+ case "abort":
266
+ logger.warn("abort received (no-op stub): @correlationId", msg.correlationId);
267
+ break;
268
+ case "load-model":
269
+ handleLoadModel(msg).catch((err) => {
270
+ post({
271
+ type: "model-load-failed",
272
+ correlationId: msg.correlationId,
273
+ documentType: msg.model.documentType,
274
+ version: msg.model.version,
275
+ error: errorToInfo(err)
276
+ });
277
+ });
278
+ break;
279
+ default: {
280
+ const raw = msg;
281
+ if (raw["type"] === "__test_throw") {
282
+ const rawReason = raw["reason"];
283
+ const reason = typeof rawReason === "string" ? rawReason : "synthetic uncaughtException";
284
+ setTimeout(() => {
285
+ throw new Error(reason);
286
+ }, 0);
287
+ return;
288
+ }
289
+ break;
290
+ }
291
+ }
292
+ }
293
+ parentPort.on("message", handleParentMessage);
294
+ parentPort.__reactorWorkerHarness = {
295
+ handleParentMessage,
296
+ get initCompleted() {
297
+ return initCompleted;
298
+ },
299
+ get initConfig() {
300
+ return initConfig;
301
+ },
302
+ get workerId() {
303
+ return workerId;
304
+ }
305
+ };
306
+ }
307
+ //#endregion
308
+ //#region src/executor/worker/entry.ts
309
+ if (isMainThread || parentPort === null) throw new Error("entry.ts must be run as a worker thread");
310
+ runWorker(parentPort);
311
+ //#endregion
312
+
313
+ //# sourceMappingURL=entry.js.map
@@ -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