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-CAhznKRr.js";
import { n as ReactorEventTypes } from "./types-CxSpmNGK.js";
import { a as ReadModelCoordinator, i as KyselyDocumentView, n as ConsistencyTracker, t as KyselyDocumentIndexer } from "./document-indexer-B2iLRB0o.js";
import { n as errorToInfo, t as createForwardingLogger } from "./forwarding-logger-BBkMSxuJ.js";
import { n as defaultLoadFactory } from "./build-worker-executor-BqNFZX3J.js";
import { ConsoleLogger } from "document-model";
import { isMainThread, parentPort } from "node:worker_threads";
//#region src/projection/projection-worker/build-projection-stack.ts
async function loadModelManifest(entries, loadFactory, registry, logger) {
	for (const entry of entries) {
		let module;
		try {
			module = await loadFactory(entry.spec);
		} catch (error) {
			logger.error("projection worker failed to load document model: @entry @error", entry, error);
			throw error;
		}
		const [result] = registry.registerModules(module);
		if (result.status === "error") {
			logger.error("projection worker failed to register document model: @entry @error", entry, result.error);
			throw result.error;
		}
	}
}
function instantiateReadModel(kind, database, operationStore, operationIndex, writeCache) {
	switch (kind) {
		case "document-view": return new KyselyDocumentView(database, operationStore, operationIndex, writeCache, new ConsistencyTracker());
		case "document-indexer": return new KyselyDocumentIndexer(database, operationIndex, writeCache, new ConsistencyTracker());
		default: {
			const exhaustive = kind;
			throw new Error(`unknown built-in read model kind: ${String(exhaustive)}`);
		}
	}
}
async function initReadModels(models, logger) {
	for (const model of models) {
		const maybeInit = model;
		if (typeof maybeInit.init !== "function") continue;
		try {
			await maybeInit.init();
		} catch (error) {
			logger.error("projection worker read model init failed: @name @error", model.name, error);
			throw error;
		}
	}
}
async function buildProjectionStack(options) {
	const { init, database: baseDatabase, logger, events } = options;
	const loadFactory = options.loadFactory ?? defaultLoadFactory;
	const registry = new DocumentModelRegistry();
	await loadModelManifest(init.models, loadFactory, registry, logger);
	const database = baseDatabase.withSchema(REACTOR_SCHEMA);
	const operationStore = new KyselyOperationStore(database);
	const writeCache = new KyselyWriteCache(new KyselyKeyframeStore(database), operationStore, registry, {
		maxDocuments: 100,
		ringBufferSize: 10,
		keyframeInterval: 10
	});
	await writeCache.startup();
	const operationIndex = new KyselyOperationIndex(database);
	await new DocumentMetaCache(operationStore, { maxDocuments: 1e3 }).startup();
	new CollectionMembershipCache(operationIndex);
	const preReady = init.preReadyKinds.map((kind) => instantiateReadModel(kind, database, operationStore, operationIndex, writeCache));
	const postReady = init.postReadyKinds.map((kind) => instantiateReadModel(kind, database, operationStore, operationIndex, writeCache));
	await initReadModels([...preReady, ...postReady], logger);
	const eventBus = new EventBus();
	const subscriptions = [];
	subscriptions.push(eventBus.subscribe(ReactorEventTypes.JOB_READ_READY, (_t, event) => {
		events.onReadReady(event);
	}));
	subscriptions.push(eventBus.subscribe(ReactorEventTypes.READMODEL_INDEXED, (_t, event) => {
		events.onReadModelIndexed(event);
	}));
	subscriptions.push(eventBus.subscribe(ReactorEventTypes.READMODEL_BATCH_COMPLETED, (_t, event) => {
		events.onBatchCompleted(event);
	}));
	const coordinator = new ReadModelCoordinator(eventBus, preReady, postReady);
	coordinator.start();
	return {
		registry,
		coordinator,
		eventBus,
		async relayWriteReady(event) {
			await eventBus.emit(ReactorEventTypes.JOB_WRITE_READY, event);
		},
		getChainDepth() {
			return coordinator.getChainDepth();
		},
		async drain() {
			await coordinator.drain();
		},
		shutdown() {
			coordinator.stop();
			for (const unsub of subscriptions) unsub();
			return Promise.resolve();
		}
	};
}
//#endregion
//#region src/projection/projection-worker/run-projection-worker.ts
const POOL_SAMPLE_INTERVAL_MS = 1e3;
async function defaultCreateDatabase(config, shardId) {
	const { Kysely, PostgresDialect } = await import("kysely");
	const Pool = (await import("pg")).default.Pool;
	const pool = new Pool({
		host: config.host,
		port: config.port,
		database: config.database,
		user: config.user,
		password: config.password,
		ssl: config.ssl ? { rejectUnauthorized: false } : void 0,
		application_name: config.applicationName ?? shardId,
		max: config.poolSize,
		connectionTimeoutMillis: config.connectionTimeoutMillis,
		idleTimeoutMillis: config.idleTimeoutMillis
	});
	const poolInstrumentation = instrumentPgPool(pool, shardId);
	const kysely = new Kysely({ dialect: new PostgresDialect({ pool }) });
	return {
		kysely,
		poolInstrumentation,
		async shutdown() {
			try {
				await kysely.destroy();
			} catch {}
			try {
				await pool.end();
			} catch {}
		}
	};
}
/**
* Drives the projection worker's message loop. Owns lifecycle of the
* database handle and the projection stack. The default factory builds a
* real Postgres pool; tests inject overrides for an in-process PGlite path.
*/
function runProjectionWorker(parentPort, overrides = {}) {
	let shardId = "";
	let initCompleted = false;
	let stack = null;
	let database = null;
	let depthTimer = null;
	let lastReportedDepth = -1;
	let poolSampleTimer = null;
	let pendingPoolSamples = [];
	let detachPoolListener = null;
	function post(msg) {
		parentPort.postMessage(msg);
	}
	function startPoolReporter(instrumentation) {
		detachPoolListener = instrumentation.onAcquire((durationMs) => {
			pendingPoolSamples.push(durationMs);
		});
		poolSampleTimer = setInterval(() => {
			if (pendingPoolSamples.length === 0) return;
			const durations = pendingPoolSamples;
			pendingPoolSamples = [];
			const stats = instrumentation.getStats();
			post({
				type: "pool-acquire-samples",
				shardId,
				poolName: instrumentation.name,
				timestamp: Date.now(),
				durations,
				size: stats.size,
				idle: stats.idle,
				waiting: stats.waiting
			});
		}, POOL_SAMPLE_INTERVAL_MS);
		poolSampleTimer.unref();
	}
	function stopPoolReporter() {
		if (detachPoolListener) {
			detachPoolListener();
			detachPoolListener = null;
		}
		if (poolSampleTimer) {
			clearInterval(poolSampleTimer);
			poolSampleTimer = null;
		}
		pendingPoolSamples = [];
	}
	const logger = createForwardingLogger((msg) => post({
		...msg,
		shardId
	}));
	process.on("uncaughtException", (err) => {
		try {
			post({
				type: "log",
				shardId,
				level: "error",
				message: "projection worker uncaughtException",
				args: [errorToInfo(err)],
				timestamp: Date.now()
			});
		} catch {}
		throw err;
	});
	process.on("unhandledRejection", (reason) => {
		try {
			post({
				type: "log",
				shardId,
				level: "error",
				message: "projection worker unhandledRejection",
				args: [errorToInfo(reason)],
				timestamp: Date.now()
			});
		} catch {}
	});
	function startDepthReporter(intervalMs) {
		if (intervalMs <= 0) return;
		depthTimer = setInterval(() => {
			if (!stack) return;
			const depth = stack.getChainDepth();
			if (depth === lastReportedDepth) return;
			lastReportedDepth = depth;
			post({
				type: "chain-depth",
				shardId,
				depth,
				timestamp: Date.now()
			});
		}, intervalMs);
		depthTimer.unref();
	}
	function stopDepthReporter() {
		if (depthTimer) {
			clearInterval(depthTimer);
			depthTimer = null;
		}
	}
	async function handleInit(msg) {
		shardId = msg.shardId;
		database = await (overrides.createDatabase ?? defaultCreateDatabase)(msg.db, msg.shardId);
		if (overrides.beforeBuildStack) await overrides.beforeBuildStack(database.kysely);
		stack = await buildProjectionStack({
			init: msg,
			database: database.kysely,
			logger: new ConsoleLogger([`projection-shard:${msg.shardId}`]),
			loadFactory: overrides.loadFactory,
			events: {
				onReadReady: (event) => {
					post({
						type: "read-ready",
						shardId,
						jobId: event.jobId,
						operations: event.operations
					});
				},
				onReadModelIndexed: (event) => {
					post({
						type: "readmodel-indexed",
						shardId,
						jobId: event.jobId,
						readModelName: event.readModelName,
						stage: event.stage,
						durationMs: event.durationMs,
						operationCount: event.operationCount,
						success: event.success
					});
				},
				onBatchCompleted: (event) => {
					post({
						type: "readmodel-batch-completed",
						shardId,
						jobId: event.jobId,
						batchSize: event.batchSize,
						chainWaitDurationMs: event.chainWaitDurationMs,
						preReadyDurationMs: event.preReadyDurationMs,
						emitDurationMs: event.emitDurationMs,
						postReadyDurationMs: event.postReadyDurationMs
					});
				}
			}
		});
		initCompleted = true;
		startDepthReporter(msg.chainDepthReportIntervalMs);
		if (database.poolInstrumentation) startPoolReporter(database.poolInstrumentation);
		logger.info("projection worker initialized: @shardId", msg.shardId);
		post({
			type: "ready",
			correlationId: msg.correlationId,
			shardId
		});
	}
	async function handleWriteReady(msg) {
		if (!stack) {
			logger.warn("write-ready received before init on shard @shardId", shardId);
			return;
		}
		const event = {
			jobId: msg.jobId,
			operations: msg.operations,
			jobMeta: msg.jobMeta,
			collectionMemberships: msg.collectionMemberships
		};
		await stack.relayWriteReady(event);
	}
	async function handleDrain(correlationId) {
		if (stack) await stack.drain();
		post({
			type: "drained",
			correlationId,
			shardId
		});
	}
	async function shutdownStack() {
		stopDepthReporter();
		stopPoolReporter();
		if (stack) {
			try {
				await stack.drain();
			} catch (error) {
				logger.warn("projection worker drain failed during shutdown: @error", error);
			}
			try {
				await stack.shutdown();
			} catch (error) {
				logger.warn("projection worker stack shutdown failed: @error", error);
			}
			stack = null;
		}
		if (database) {
			await database.shutdown();
			database = null;
		}
	}
	function handleParentMessage(msg) {
		switch (msg.type) {
			case "init":
				handleInit(msg).catch((err) => {
					post({
						type: "log",
						shardId,
						level: "error",
						message: "projection worker init failed",
						args: [errorToInfo(err)],
						timestamp: Date.now()
					});
				});
				break;
			case "write-ready":
				if (!initCompleted) {
					logger.warn("write-ready received before init on shard @shardId", shardId);
					break;
				}
				handleWriteReady(msg).catch((err) => {
					post({
						type: "log",
						shardId,
						level: "error",
						message: "projection worker write-ready failed",
						args: [errorToInfo(err)],
						timestamp: Date.now()
					});
				});
				break;
			case "drain":
				handleDrain(msg.correlationId).catch((err) => {
					post({
						type: "log",
						shardId,
						level: "error",
						message: "projection worker drain failed",
						args: [errorToInfo(err)],
						timestamp: Date.now()
					});
				});
				break;
			case "shutdown":
				logger.info("projection worker shutting down: @shardId", shardId);
				shutdownStack().finally(() => {
					post({
						type: "log",
						shardId,
						level: "info",
						message: "projection worker shutdown",
						args: [],
						timestamp: Date.now()
					});
					process.exit(0);
				});
				break;
			default: break;
		}
	}
	parentPort.on("message", handleParentMessage);
	parentPort.__reactorProjectionWorkerHarness = {
		handleParentMessage,
		get initCompleted() {
			return initCompleted;
		},
		get shardId() {
			return shardId;
		}
	};
}
//#endregion
//#region src/projection/projection-worker/projection-entry.ts
if (isMainThread || parentPort === null) throw new Error("projection-worker entry.ts must be run as a worker thread");
runProjectionWorker(parentPort);
//#endregion

//# sourceMappingURL=projection-entry.js.map