@drarzter/kafka-client 0.7.0 → 0.7.2

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/core.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/core.ts","../src/client/kafka.client/index.ts","../src/client/message/envelope.ts","../src/client/errors.ts","../src/client/kafka.client/producer-ops.ts","../src/client/kafka.client/consumer-ops.ts","../src/client/consumer/pipeline.ts","../src/client/kafka.client/message-handler.ts","../src/client/consumer/subscribe-retry.ts","../src/client/kafka.client/retry-topic.ts","../src/client/message/topic.ts"],"sourcesContent":["export * from \"./client/kafka.client\";\nexport * from \"./client/message/topic\";\nexport * from \"./client/errors\";\nexport * from \"./client/message/envelope\";\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Kafka = KafkaJS.Kafka;\ntype Producer = KafkaJS.Producer;\ntype Consumer = KafkaJS.Consumer;\ntype Admin = KafkaJS.Admin;\nconst { Kafka: KafkaClass, logLevel: KafkaLogLevel } = KafkaJS;\nimport { TopicDescriptor, SchemaLike } from \"../message/topic\";\nimport type { EventEnvelope } from \"../message/envelope\";\nimport type {\n ClientId,\n GroupId,\n SendOptions,\n BatchMessageItem,\n ConsumerOptions,\n ConsumerHandle,\n TransactionContext,\n TopicMapConstraint,\n IKafkaClient,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n KafkaMetrics,\n DlqReason,\n BatchMeta,\n DlqReplayOptions,\n CircuitBreakerOptions,\n} from \"../types\";\n\n/**\n * Process-level registry of active transactional producer IDs.\n * Used to detect same-process `transactionalId` conflicts before Kafka silently\n * fences one of the producers. Cross-process conflicts cannot be detected here —\n * they surface as fencing errors from the broker.\n */\nconst _activeTransactionalIds = new Set<string>();\n\n// Re-export all types so existing `import { ... } from './kafka.client'` keeps working\nexport * from \"../types\";\n\nimport {\n buildSendPayload,\n registerSchema,\n resolveTopicName,\n type BuildSendPayloadDeps,\n} from \"./producer-ops\";\nimport {\n getOrCreateConsumer,\n buildSchemaMap,\n type ConsumerOpsDeps,\n} from \"./consumer-ops\";\nimport { handleEachMessage, handleEachBatch } from \"./message-handler\";\nimport type {\n MessageHandlerDeps,\n DeduplicationContext,\n} from \"./message-handler\";\nimport { startRetryTopicConsumers } from \"./retry-topic\";\nimport { subscribeWithRetry } from \"../consumer/subscribe-retry\";\nimport { toError } from \"../consumer/pipeline\";\nimport { decodeHeaders } from \"../message/envelope\";\n\n/** Push-to-pull async queue used by consume() to bridge Kafka's push model to AsyncIterableIterator. */\nclass AsyncQueue<V> {\n private readonly items: V[] = [];\n private readonly waiting: Array<(r: IteratorResult<V>) => void> = [];\n private closed = false;\n\n push(item: V): void {\n if (this.waiting.length > 0) {\n this.waiting.shift()!({ value: item, done: false });\n } else {\n this.items.push(item);\n }\n }\n\n close(): void {\n this.closed = true;\n for (const r of this.waiting.splice(0)) {\n r({ value: undefined as any, done: true });\n }\n }\n\n next(): Promise<IteratorResult<V>> {\n if (this.items.length > 0)\n return Promise.resolve({ value: this.items.shift()!, done: false });\n if (this.closed)\n return Promise.resolve({ value: undefined as any, done: true });\n return new Promise((r) => this.waiting.push(r));\n }\n}\n\n/**\n * Type-safe Kafka client.\n * Wraps @confluentinc/kafka-javascript (librdkafka) with JSON serialization,\n * retries, DLQ, transactions, and interceptors.\n *\n * @typeParam T - Topic-to-message type mapping for compile-time safety.\n */\nexport class KafkaClient<\n T extends TopicMapConstraint<T>,\n> implements IKafkaClient<T> {\n private readonly kafka: Kafka;\n private readonly producer: Producer;\n private txProducer: Producer | undefined;\n private txProducerInitPromise: Promise<Producer> | undefined;\n /** Maps transactionalId → Producer for each active retry level consumer. */\n private readonly retryTxProducers = new Map<string, Producer>();\n private readonly consumers = new Map<string, Consumer>();\n private readonly admin: Admin;\n private readonly logger: KafkaLogger;\n private readonly autoCreateTopicsEnabled: boolean;\n private readonly strictSchemasEnabled: boolean;\n private readonly numPartitions: number;\n private readonly ensuredTopics = new Set<string>();\n /** Pending topic-creation promises keyed by topic name. Prevents duplicate createTopics calls. */\n private readonly ensureTopicPromises = new Map<string, Promise<void>>();\n private readonly defaultGroupId: string;\n private readonly schemaRegistry = new Map<string, SchemaLike>();\n private readonly runningConsumers = new Map<\n string,\n \"eachMessage\" | \"eachBatch\"\n >();\n private readonly consumerCreationOptions = new Map<\n string,\n { fromBeginning: boolean; autoCommit: boolean }\n >();\n /** Maps each main consumer groupId to its companion retry level groupIds. */\n private readonly companionGroupIds = new Map<string, string[]>();\n private readonly instrumentation: KafkaInstrumentation[];\n private readonly onMessageLost: KafkaClientOptions[\"onMessageLost\"];\n private readonly onRebalance: KafkaClientOptions[\"onRebalance\"];\n /** Transactional producer ID — configurable via `KafkaClientOptions.transactionalId`. */\n private readonly txId: string;\n /** Per-topic event counters, lazily created on first event. Aggregated by `getMetrics()`. */\n private readonly _topicMetrics = new Map<string, KafkaMetrics>();\n\n /** Monotonically increasing Lamport clock stamped on every outgoing message. */\n private _lamportClock = 0;\n /** Per-groupId deduplication state: `\"topic:partition\"` → last processed clock. */\n private readonly dedupStates = new Map<string, Map<string, number>>();\n\n /** Circuit breaker state per `\"${gid}:${topic}:${partition}\"` key. */\n private readonly circuitStates = new Map<\n string,\n {\n status: \"closed\" | \"open\" | \"half-open\";\n window: boolean[];\n successes: number;\n timer?: ReturnType<typeof setTimeout>;\n }\n >();\n /** Circuit breaker config per groupId, set at startConsumer/startBatchConsumer time. */\n private readonly circuitConfigs = new Map<string, CircuitBreakerOptions>();\n\n private isAdminConnected = false;\n private inFlightTotal = 0;\n private readonly drainResolvers: Array<() => void> = [];\n public readonly clientId: ClientId;\n\n constructor(\n clientId: ClientId,\n groupId: GroupId,\n brokers: string[],\n options?: KafkaClientOptions,\n ) {\n this.clientId = clientId;\n this.defaultGroupId = groupId;\n this.logger = options?.logger ?? {\n log: (msg) => console.log(`[KafkaClient:${clientId}] ${msg}`),\n warn: (msg, ...args) =>\n console.warn(`[KafkaClient:${clientId}] ${msg}`, ...args),\n error: (msg, ...args) =>\n console.error(`[KafkaClient:${clientId}] ${msg}`, ...args),\n debug: (msg, ...args) =>\n console.debug(`[KafkaClient:${clientId}] ${msg}`, ...args),\n };\n this.autoCreateTopicsEnabled = options?.autoCreateTopics ?? false;\n this.strictSchemasEnabled = options?.strictSchemas ?? true;\n this.numPartitions = options?.numPartitions ?? 1;\n this.instrumentation = options?.instrumentation ?? [];\n this.onMessageLost = options?.onMessageLost;\n this.onRebalance = options?.onRebalance;\n this.txId = options?.transactionalId ?? `${clientId}-tx`;\n\n this.kafka = new KafkaClass({\n kafkaJS: {\n clientId: this.clientId,\n brokers,\n logLevel: KafkaLogLevel.ERROR,\n },\n });\n this.producer = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n },\n });\n this.admin = this.kafka.admin();\n }\n\n // ── Send ─────────────────────────────────────────────────────────\n\n /** Send a single typed message. Accepts a topic key or a TopicDescriptor. */\n public async sendMessage<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(descriptor: D, message: D[\"__type\"], options?: SendOptions): Promise<void>;\n public async sendMessage<K extends keyof T>(\n topic: K,\n message: T[K],\n options?: SendOptions,\n ): Promise<void>;\n public async sendMessage(\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ): Promise<void> {\n const payload = await this.preparePayload(topicOrDesc, [\n {\n value: message,\n key: options.key,\n headers: options.headers,\n correlationId: options.correlationId,\n schemaVersion: options.schemaVersion,\n eventId: options.eventId,\n },\n ]);\n await this.producer.send(payload);\n this.notifyAfterSend(payload.topic, payload.messages.length);\n }\n\n /** Send multiple typed messages in one call. Accepts a topic key or a TopicDescriptor. */\n public async sendBatch<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n descriptor: D,\n messages: Array<BatchMessageItem<D[\"__type\"]>>,\n ): Promise<void>;\n public async sendBatch<K extends keyof T>(\n topic: K,\n messages: Array<BatchMessageItem<T[K]>>,\n ): Promise<void>;\n public async sendBatch(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n ): Promise<void> {\n const payload = await this.preparePayload(topicOrDesc, messages);\n await this.producer.send(payload);\n this.notifyAfterSend(payload.topic, payload.messages.length);\n }\n\n /** Execute multiple sends atomically. Commits on success, aborts on error. */\n public async transaction(\n fn: (ctx: TransactionContext<T>) => Promise<void>,\n ): Promise<void> {\n if (!this.txProducerInitPromise) {\n // Guarded by a promise (not just a flag) so that concurrent transaction()\n // calls do not each create their own producer — only the first call creates\n // the promise; subsequent concurrent calls await the same promise.\n // On connect failure the promise is cleared so the next call can retry.\n if (_activeTransactionalIds.has(this.txId)) {\n this.logger.warn(\n `transactionalId \"${this.txId}\" is already in use by another KafkaClient in this process. ` +\n `Kafka will fence one of the producers. ` +\n `Set a unique \\`transactionalId\\` (or distinct \\`clientId\\`) per instance.`,\n );\n }\n const initPromise: Promise<Producer> = (async () => {\n const p = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n idempotent: true,\n transactionalId: this.txId,\n maxInFlightRequests: 1,\n },\n });\n await p.connect();\n _activeTransactionalIds.add(this.txId);\n return p;\n })();\n this.txProducerInitPromise = initPromise.catch((err) => {\n this.txProducerInitPromise = undefined;\n throw err;\n });\n }\n this.txProducer = await this.txProducerInitPromise;\n const tx = await this.txProducer.transaction();\n try {\n const ctx: TransactionContext<T> = {\n send: async (\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ) => {\n const payload = await this.preparePayload(topicOrDesc, [\n {\n value: message,\n key: options.key,\n headers: options.headers,\n correlationId: options.correlationId,\n schemaVersion: options.schemaVersion,\n eventId: options.eventId,\n },\n ]);\n await tx.send(payload);\n this.notifyAfterSend(payload.topic, payload.messages.length);\n },\n sendBatch: async (\n topicOrDesc: any,\n messages: BatchMessageItem<any>[],\n ) => {\n const payload = await this.preparePayload(topicOrDesc, messages);\n await tx.send(payload);\n this.notifyAfterSend(payload.topic, payload.messages.length);\n },\n };\n await fn(ctx);\n await tx.commit();\n } catch (error) {\n try {\n await tx.abort();\n } catch (abortError) {\n this.logger.error(\n \"Failed to abort transaction:\",\n toError(abortError).message,\n );\n }\n throw error;\n }\n }\n\n // ── Producer lifecycle ───────────────────────────────────────────\n\n /**\n * Connect the idempotent producer. Called automatically by `KafkaModule.register()`.\n * @internal Not part of `IKafkaClient` — use `disconnect()` for full teardown.\n */\n public async connectProducer(): Promise<void> {\n await this.producer.connect();\n this.logger.log(\"Producer connected\");\n }\n\n /**\n * @internal Not part of `IKafkaClient` — use `disconnect()` for full teardown.\n */\n public async disconnectProducer(): Promise<void> {\n await this.producer.disconnect();\n this.logger.log(\"Producer disconnected\");\n }\n\n // ── Consumer: eachMessage ────────────────────────────────────────\n\n /** Subscribe to topics and start consuming messages with the given handler. */\n public async startConsumer<K extends Array<keyof T>>(\n topics: K,\n handleMessage: (envelope: EventEnvelope<T[K[number]]>) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<ConsumerHandle>;\n public async startConsumer<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n topics: D[],\n handleMessage: (envelope: EventEnvelope<D[\"__type\"]>) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<ConsumerHandle>;\n public async startConsumer(\n topics: any[],\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<ConsumerHandle> {\n if (options.retryTopics && !options.retry) {\n throw new Error(\n \"retryTopics requires retry to be configured — set retry.maxRetries to enable the retry topic chain\",\n );\n }\n\n // When retryTopics is enabled, the main consumer must run with autoCommit: false\n // so that offset commits can be coordinated with EOS routing transactions.\n const setupOptions = options.retryTopics\n ? { ...options, autoCommit: false as const }\n : options;\n\n const { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachMessage\", setupOptions);\n\n if (options.circuitBreaker)\n this.circuitConfigs.set(gid, options.circuitBreaker);\n const deps = this.messageDepsFor(gid);\n const timeoutMs = options.handlerTimeoutMs;\n const deduplication = this.resolveDeduplicationContext(\n gid,\n options.deduplication,\n );\n\n // Create EOS transactional producer for atomic main → retry.1 routing.\n let eosMainContext:\n | import(\"./message-handler\").EachMessageOpts[\"eosMainContext\"]\n | undefined;\n if (options.retryTopics && retry) {\n const mainTxId = `${gid}-main-tx`;\n const txProducer = await this.createRetryTxProducer(mainTxId);\n eosMainContext = { txProducer, consumer };\n }\n\n await consumer.run({\n eachMessage: (payload) =>\n this.trackInFlight(() =>\n handleEachMessage(\n payload,\n {\n schemaMap,\n handleMessage,\n interceptors,\n dlq,\n retry,\n retryTopics: options.retryTopics,\n timeoutMs,\n wrapWithTimeout: this.wrapWithTimeoutWarning.bind(this),\n deduplication,\n messageTtlMs: options.messageTtlMs,\n eosMainContext,\n },\n deps,\n ),\n ),\n });\n\n this.runningConsumers.set(gid, \"eachMessage\");\n\n if (options.retryTopics && retry) {\n if (!this.autoCreateTopicsEnabled) {\n await this.validateRetryTopicsExist(topicNames, retry.maxRetries);\n }\n const companions = await startRetryTopicConsumers(\n topicNames,\n gid,\n handleMessage,\n retry,\n dlq,\n interceptors,\n schemaMap,\n this.retryTopicDeps,\n options.retryTopicAssignmentTimeoutMs,\n );\n this.companionGroupIds.set(gid, companions);\n }\n\n return { groupId: gid, stop: () => this.stopConsumer(gid) };\n }\n\n // ── Consumer: eachBatch ──────────────────────────────────────────\n\n /** Subscribe to topics and consume messages in batches. */\n public async startBatchConsumer<K extends Array<keyof T>>(\n topics: K,\n handleBatch: (\n envelopes: EventEnvelope<T[K[number]]>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<ConsumerHandle>;\n public async startBatchConsumer<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n topics: D[],\n handleBatch: (\n envelopes: EventEnvelope<D[\"__type\"]>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<ConsumerHandle>;\n public async startBatchConsumer(\n topics: any[],\n handleBatch: (\n envelopes: EventEnvelope<any>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<ConsumerHandle> {\n if (options.retryTopics && !options.retry) {\n throw new Error(\n \"retryTopics requires retry to be configured — set retry.maxRetries to enable the retry topic chain\",\n );\n }\n\n if (options.retryTopics) {\n // When retryTopics is enabled, autoCommit: false is required for EOS routing.\n // Suppress the manual-offset diagnostic that would otherwise fire.\n } else if (options.autoCommit !== false) {\n this.logger.debug?.(\n `startBatchConsumer: autoCommit is enabled (default true). ` +\n `If your handler calls resolveOffset() or commitOffsetsIfNecessary(), set autoCommit: false to avoid offset conflicts.`,\n );\n }\n\n const setupOptions = options.retryTopics\n ? { ...options, autoCommit: false as const }\n : options;\n\n const { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachBatch\", setupOptions);\n\n if (options.circuitBreaker)\n this.circuitConfigs.set(gid, options.circuitBreaker);\n const deps = this.messageDepsFor(gid);\n const timeoutMs = options.handlerTimeoutMs;\n const deduplication = this.resolveDeduplicationContext(\n gid,\n options.deduplication,\n );\n\n let eosMainContext:\n | import(\"./message-handler\").EachBatchOpts[\"eosMainContext\"]\n | undefined;\n if (options.retryTopics && retry) {\n const mainTxId = `${gid}-main-tx`;\n const txProducer = await this.createRetryTxProducer(mainTxId);\n eosMainContext = { txProducer, consumer };\n }\n\n await consumer.run({\n eachBatch: (payload) =>\n this.trackInFlight(() =>\n handleEachBatch(\n payload,\n {\n schemaMap,\n handleBatch,\n interceptors,\n dlq,\n retry,\n retryTopics: options.retryTopics,\n timeoutMs,\n wrapWithTimeout: this.wrapWithTimeoutWarning.bind(this),\n deduplication,\n messageTtlMs: options.messageTtlMs,\n eosMainContext,\n },\n deps,\n ),\n ),\n });\n\n this.runningConsumers.set(gid, \"eachBatch\");\n\n if (options.retryTopics && retry) {\n if (!this.autoCreateTopicsEnabled) {\n await this.validateRetryTopicsExist(topicNames, retry.maxRetries);\n }\n // Wrap batch handler as single-message handler for retry consumers.\n // Retry consumers use eachMessage (not eachBatch), so the broker\n // high-watermark is not available. `highWatermark` is set to `null` —\n // handlers must guard against null before doing lag calculations.\n const handleMessageForRetry = (env: EventEnvelope<any>) =>\n handleBatch([env], {\n partition: env.partition,\n highWatermark: null,\n heartbeat: async () => {},\n resolveOffset: () => {},\n commitOffsetsIfNecessary: async () => {},\n });\n const companions = await startRetryTopicConsumers(\n topicNames,\n gid,\n handleMessageForRetry,\n retry,\n dlq,\n interceptors,\n schemaMap,\n this.retryTopicDeps,\n options.retryTopicAssignmentTimeoutMs,\n );\n this.companionGroupIds.set(gid, companions);\n }\n\n return { groupId: gid, stop: () => this.stopConsumer(gid) };\n }\n\n /**\n * Consume messages from a topic as an AsyncIterableIterator.\n * Use with `for await` — breaking out of the loop automatically stops the consumer.\n *\n * @example\n * for await (const envelope of kafka.consume('my.topic')) {\n * console.log(envelope.data);\n * }\n */\n public consume<K extends keyof T & string>(\n topic: K,\n options?: ConsumerOptions<T>,\n ): AsyncIterableIterator<EventEnvelope<T[K]>> {\n const queue = new AsyncQueue<EventEnvelope<T[K]>>();\n const handlePromise = this.startConsumer(\n [topic as any],\n async (envelope) => {\n queue.push(envelope as EventEnvelope<T[K]>);\n },\n options,\n );\n return {\n [Symbol.asyncIterator]() {\n return this;\n },\n next: () => queue.next(),\n return: async () => {\n queue.close();\n const handle = await handlePromise;\n await handle.stop();\n return { value: undefined as any, done: true as const };\n },\n };\n }\n\n // ── Consumer lifecycle ───────────────────────────────────────────\n\n public async stopConsumer(groupId?: string): Promise<void> {\n if (groupId !== undefined) {\n const consumer = this.consumers.get(groupId);\n if (!consumer) {\n this.logger.warn(\n `stopConsumer: no active consumer for group \"${groupId}\"`,\n );\n return;\n }\n await consumer\n .disconnect()\n .catch((e) =>\n this.logger.warn(\n `Error disconnecting consumer \"${groupId}\":`,\n toError(e).message,\n ),\n );\n this.consumers.delete(groupId);\n this.runningConsumers.delete(groupId);\n this.consumerCreationOptions.delete(groupId);\n this.dedupStates.delete(groupId);\n // Clean up circuit breaker state for this group\n for (const key of [...this.circuitStates.keys()]) {\n if (key.startsWith(`${groupId}:`)) {\n clearTimeout(this.circuitStates.get(key)!.timer);\n this.circuitStates.delete(key);\n }\n }\n this.circuitConfigs.delete(groupId);\n this.logger.log(`Consumer disconnected: group \"${groupId}\"`);\n\n // Clean up the main consumer's EOS tx producer (present when retryTopics: true)\n const mainTxId = `${groupId}-main-tx`;\n const mainTxProducer = this.retryTxProducers.get(mainTxId);\n if (mainTxProducer) {\n await mainTxProducer\n .disconnect()\n .catch((e) =>\n this.logger.warn(\n `Error disconnecting main tx producer \"${mainTxId}\":`,\n toError(e).message,\n ),\n );\n _activeTransactionalIds.delete(mainTxId);\n this.retryTxProducers.delete(mainTxId);\n }\n\n // Stop all companion retry level consumers and their tx producers\n const companions = this.companionGroupIds.get(groupId) ?? [];\n for (const cGroupId of companions) {\n const cConsumer = this.consumers.get(cGroupId);\n if (cConsumer) {\n await cConsumer\n .disconnect()\n .catch((e) =>\n this.logger.warn(\n `Error disconnecting retry consumer \"${cGroupId}\":`,\n toError(e).message,\n ),\n );\n this.consumers.delete(cGroupId);\n this.runningConsumers.delete(cGroupId);\n this.consumerCreationOptions.delete(cGroupId);\n this.logger.log(`Retry consumer disconnected: group \"${cGroupId}\"`);\n }\n // Disconnect the EOS tx producer for this retry level\n const txId = `${cGroupId}-tx`;\n const txProducer = this.retryTxProducers.get(txId);\n if (txProducer) {\n await txProducer\n .disconnect()\n .catch((e) =>\n this.logger.warn(\n `Error disconnecting retry tx producer \"${txId}\":`,\n toError(e).message,\n ),\n );\n _activeTransactionalIds.delete(txId);\n this.retryTxProducers.delete(txId);\n }\n }\n this.companionGroupIds.delete(groupId);\n } else {\n const tasks: Promise<void>[] = [\n ...Array.from(this.consumers.values()).map((c) =>\n c.disconnect().catch(() => {}),\n ),\n ...Array.from(this.retryTxProducers.values()).map((p) =>\n p.disconnect().catch(() => {}),\n ),\n ];\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.consumerCreationOptions.clear();\n this.companionGroupIds.clear();\n this.retryTxProducers.clear();\n this.dedupStates.clear();\n for (const state of this.circuitStates.values())\n clearTimeout(state.timer);\n this.circuitStates.clear();\n this.circuitConfigs.clear();\n this.logger.log(\"All consumers disconnected\");\n }\n }\n\n public pauseConsumer(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n ): void {\n const gid = groupId ?? this.defaultGroupId;\n const consumer = this.consumers.get(gid);\n if (!consumer) {\n this.logger.warn(`pauseConsumer: no active consumer for group \"${gid}\"`);\n return;\n }\n consumer.pause(\n assignments.flatMap(({ topic, partitions }) =>\n partitions.map((p) => ({ topic, partitions: [p] })),\n ),\n );\n }\n\n public resumeConsumer(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n ): void {\n const gid = groupId ?? this.defaultGroupId;\n const consumer = this.consumers.get(gid);\n if (!consumer) {\n this.logger.warn(`resumeConsumer: no active consumer for group \"${gid}\"`);\n return;\n }\n consumer.resume(\n assignments.flatMap(({ topic, partitions }) =>\n partitions.map((p) => ({ topic, partitions: [p] })),\n ),\n );\n }\n\n /** DLQ header keys added by `sendToDlq` — stripped before re-publishing. */\n private static readonly DLQ_HEADER_KEYS = new Set([\n \"x-dlq-original-topic\",\n \"x-dlq-failed-at\",\n \"x-dlq-error-message\",\n \"x-dlq-error-stack\",\n \"x-dlq-attempt-count\",\n ]);\n\n public async replayDlq(\n topic: string,\n options: DlqReplayOptions = {},\n ): Promise<{ replayed: number; skipped: number }> {\n const dlqTopic = `${topic}.dlq`;\n await this.ensureAdminConnected();\n\n const partitionOffsets = await this.admin.fetchTopicOffsets(dlqTopic);\n const activePartitions = partitionOffsets.filter(\n (p) => parseInt(p.high, 10) > 0,\n );\n if (activePartitions.length === 0) {\n this.logger.log(`replayDlq: \"${dlqTopic}\" is empty — nothing to replay`);\n return { replayed: 0, skipped: 0 };\n }\n\n const highWatermarks = new Map(\n activePartitions.map(({ partition, high }) => [\n partition,\n parseInt(high, 10),\n ]),\n );\n const processedOffsets = new Map<number, number>();\n\n let replayed = 0;\n let skipped = 0;\n\n const tempGroupId = `${dlqTopic}-replay-${Date.now()}`;\n\n await new Promise<void>((resolve, reject) => {\n const consumer = getOrCreateConsumer(\n tempGroupId,\n true,\n true,\n this.consumerOpsDeps,\n );\n\n const cleanup = () => {\n consumer\n .disconnect()\n .catch(() => {})\n .finally(() => {\n this.consumers.delete(tempGroupId);\n this.runningConsumers.delete(tempGroupId);\n this.consumerCreationOptions.delete(tempGroupId);\n });\n };\n\n consumer\n .connect()\n .then(() => subscribeWithRetry(consumer, [dlqTopic], this.logger))\n .then(() =>\n consumer.run({\n eachMessage: async ({ partition, message }) => {\n if (!message.value) return;\n\n const offset = parseInt(message.offset, 10);\n processedOffsets.set(partition, offset);\n\n const headers = decodeHeaders(message.headers);\n const targetTopic =\n options.targetTopic ?? headers[\"x-dlq-original-topic\"];\n const originalHeaders = Object.fromEntries(\n Object.entries(headers).filter(\n ([k]) => !KafkaClient.DLQ_HEADER_KEYS.has(k),\n ),\n );\n const value = message.value.toString();\n const shouldProcess =\n !options.filter || options.filter(headers, value);\n\n if (!targetTopic || !shouldProcess) {\n skipped++;\n } else if (options.dryRun) {\n this.logger.log(\n `[DLQ replay dry-run] Would replay to \"${targetTopic}\"`,\n );\n replayed++;\n } else {\n await this.producer.send({\n topic: targetTopic,\n messages: [{ value, headers: originalHeaders }],\n });\n replayed++;\n }\n\n // Stop when all active partitions have reached their high watermark\n const allDone = Array.from(highWatermarks.entries()).every(\n ([p, hwm]) => (processedOffsets.get(p) ?? -1) >= hwm - 1,\n );\n if (allDone) {\n cleanup();\n resolve();\n }\n },\n }),\n )\n .catch((err) => {\n cleanup();\n reject(err);\n });\n });\n\n this.logger.log(\n `replayDlq: replayed ${replayed}, skipped ${skipped} from \"${dlqTopic}\"`,\n );\n return { replayed, skipped };\n }\n\n public async resetOffsets(\n groupId: string | undefined,\n topic: string,\n position: \"earliest\" | \"latest\",\n ): Promise<void> {\n const gid = groupId ?? this.defaultGroupId;\n if (this.runningConsumers.has(gid)) {\n throw new Error(\n `resetOffsets: consumer group \"${gid}\" is still running. ` +\n `Call stopConsumer(\"${gid}\") before resetting offsets.`,\n );\n }\n await this.ensureAdminConnected();\n const partitionOffsets = await this.admin.fetchTopicOffsets(topic);\n const partitions = partitionOffsets.map(({ partition, low, high }) => ({\n partition,\n offset: position === \"earliest\" ? low : high,\n }));\n await (this.admin as any).setOffsets({ groupId: gid, topic, partitions });\n this.logger.log(\n `Offsets reset to ${position} for group \"${gid}\" on topic \"${topic}\"`,\n );\n }\n\n /**\n * Seek specific topic-partition pairs to explicit offsets for a stopped consumer group.\n * Throws if the group is still running — call `stopConsumer(groupId)` first.\n * Assignments are grouped by topic and committed via `admin.setOffsets`.\n */\n public async seekToOffset(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partition: number; offset: string }>,\n ): Promise<void> {\n const gid = groupId ?? this.defaultGroupId;\n if (this.runningConsumers.has(gid)) {\n throw new Error(\n `seekToOffset: consumer group \"${gid}\" is still running. ` +\n `Call stopConsumer(\"${gid}\") before seeking offsets.`,\n );\n }\n await this.ensureAdminConnected();\n const byTopic = new Map<\n string,\n Array<{ partition: number; offset: string }>\n >();\n for (const { topic, partition, offset } of assignments) {\n const list = byTopic.get(topic) ?? [];\n list.push({ partition, offset });\n byTopic.set(topic, list);\n }\n for (const [topic, partitions] of byTopic) {\n await (this.admin as any).setOffsets({ groupId: gid, topic, partitions });\n this.logger.log(\n `Offsets set for group \"${gid}\" on \"${topic}\": ${JSON.stringify(partitions)}`,\n );\n }\n }\n\n /**\n * Query consumer group lag per partition.\n * Lag = broker high-watermark − last committed offset.\n * A committed offset of -1 (nothing committed yet) counts as full lag.\n *\n * Returns an empty array when the consumer group has never committed any\n * offsets (freshly created group, `autoCommit: false` with no manual commits,\n * or group not yet assigned). This is a Kafka protocol limitation:\n * `fetchOffsets` only returns data for topic-partitions that have at least one\n * committed offset. Use `checkStatus()` to verify broker connectivity in that case.\n */\n public async getConsumerLag(\n groupId?: string,\n ): Promise<Array<{ topic: string; partition: number; lag: number }>> {\n const gid = groupId ?? this.defaultGroupId;\n await this.ensureAdminConnected();\n\n const committedByTopic = await this.admin.fetchOffsets({ groupId: gid });\n\n const brokerOffsetsAll = await Promise.all(\n committedByTopic.map(({ topic }) => this.admin.fetchTopicOffsets(topic)),\n );\n\n const result: Array<{ topic: string; partition: number; lag: number }> = [];\n\n for (let i = 0; i < committedByTopic.length; i++) {\n const { topic, partitions } = committedByTopic[i];\n const brokerOffsets = brokerOffsetsAll[i];\n\n for (const { partition, offset } of partitions) {\n const broker = brokerOffsets.find((o) => o.partition === partition);\n if (!broker) continue;\n\n const committed = parseInt(offset, 10);\n const high = parseInt(broker.high, 10);\n // committed === -1 means the group has never committed for this partition\n const lag = committed === -1 ? high : Math.max(0, high - committed);\n result.push({ topic, partition, lag });\n }\n }\n\n return result;\n }\n\n /** Check broker connectivity. Never throws — returns a discriminated union. */\n public async checkStatus(): Promise<import(\"../types\").KafkaHealthResult> {\n try {\n await this.ensureAdminConnected();\n const topics = await this.admin.listTopics();\n return { status: \"up\", clientId: this.clientId, topics };\n } catch (error) {\n return {\n status: \"down\",\n clientId: this.clientId,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n public getClientId(): ClientId {\n return this.clientId;\n }\n\n public getMetrics(topic?: string): Readonly<KafkaMetrics> {\n if (topic !== undefined) {\n const m = this._topicMetrics.get(topic);\n return m\n ? { ...m }\n : { processedCount: 0, retryCount: 0, dlqCount: 0, dedupCount: 0 };\n }\n // Aggregate across all topics\n const agg: KafkaMetrics = {\n processedCount: 0,\n retryCount: 0,\n dlqCount: 0,\n dedupCount: 0,\n };\n for (const m of this._topicMetrics.values()) {\n agg.processedCount += m.processedCount;\n agg.retryCount += m.retryCount;\n agg.dlqCount += m.dlqCount;\n agg.dedupCount += m.dedupCount;\n }\n return agg;\n }\n\n public resetMetrics(topic?: string): void {\n if (topic !== undefined) {\n this._topicMetrics.delete(topic);\n return;\n }\n this._topicMetrics.clear();\n }\n\n /** Gracefully disconnect producer, all consumers, and admin. */\n public async disconnect(drainTimeoutMs = 30_000): Promise<void> {\n await this.waitForDrain(drainTimeoutMs);\n const tasks: Promise<void>[] = [this.producer.disconnect()];\n if (this.txProducer) {\n tasks.push(this.txProducer.disconnect());\n _activeTransactionalIds.delete(this.txId);\n this.txProducer = undefined;\n this.txProducerInitPromise = undefined;\n }\n for (const txId of this.retryTxProducers.keys()) {\n _activeTransactionalIds.delete(txId);\n }\n for (const p of this.retryTxProducers.values()) {\n tasks.push(p.disconnect());\n }\n this.retryTxProducers.clear();\n for (const consumer of this.consumers.values()) {\n tasks.push(consumer.disconnect());\n }\n if (this.isAdminConnected) {\n tasks.push(this.admin.disconnect());\n this.isAdminConnected = false;\n }\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.consumerCreationOptions.clear();\n this.companionGroupIds.clear();\n for (const state of this.circuitStates.values()) clearTimeout(state.timer);\n this.circuitStates.clear();\n this.circuitConfigs.clear();\n this.logger.log(\"All connections closed\");\n }\n\n // ── Graceful shutdown ────────────────────────────────────────────\n\n /**\n * NestJS lifecycle hook — called automatically when the host module is torn down.\n * Drains in-flight handlers and disconnects all producers, consumers, and admin.\n * `KafkaModule` relies on this method; no separate destroy provider is needed.\n */\n public async onModuleDestroy(): Promise<void> {\n await this.disconnect();\n }\n\n /**\n * Register SIGTERM / SIGINT handlers that drain in-flight messages before\n * disconnecting. Call this once after constructing the client in non-NestJS apps.\n * NestJS apps get drain for free via `onModuleDestroy` → `disconnect()`.\n */\n public enableGracefulShutdown(\n signals: NodeJS.Signals[] = [\"SIGTERM\", \"SIGINT\"],\n drainTimeoutMs = 30_000,\n ): void {\n const handler = () => {\n this.logger.log(\n \"Shutdown signal received — draining in-flight handlers...\",\n );\n this.disconnect(drainTimeoutMs).catch((err) =>\n this.logger.error(\n \"Error during graceful shutdown:\",\n toError(err).message,\n ),\n );\n };\n for (const signal of signals) {\n process.once(signal, handler);\n }\n }\n\n private trackInFlight<R>(fn: () => Promise<R>): Promise<R> {\n this.inFlightTotal++;\n return fn().finally(() => {\n this.inFlightTotal--;\n if (this.inFlightTotal === 0) {\n this.drainResolvers.splice(0).forEach((r) => r());\n }\n });\n }\n\n private waitForDrain(timeoutMs: number): Promise<void> {\n if (this.inFlightTotal === 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n let handle: ReturnType<typeof setTimeout>;\n const onDrain = () => {\n clearTimeout(handle);\n resolve();\n };\n this.drainResolvers.push(onDrain);\n handle = setTimeout(() => {\n const idx = this.drainResolvers.indexOf(onDrain);\n if (idx !== -1) this.drainResolvers.splice(idx, 1);\n this.logger.warn(\n `Drain timed out after ${timeoutMs}ms — ${this.inFlightTotal} handler(s) still in flight`,\n );\n resolve();\n }, timeoutMs);\n });\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private async preparePayload(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n ) {\n registerSchema(topicOrDesc, this.schemaRegistry, this.logger);\n const payload = await buildSendPayload(\n topicOrDesc,\n messages,\n this.producerOpsDeps,\n );\n await this.ensureTopic(payload.topic);\n return payload;\n }\n\n // afterSend is called once per message — symmetric with beforeSend in buildSendPayload.\n private notifyAfterSend(topic: string, count: number): void {\n for (let i = 0; i < count; i++) {\n for (const inst of this.instrumentation) {\n inst.afterSend?.(topic);\n }\n }\n }\n\n private metricsFor(topic: string): KafkaMetrics {\n let m = this._topicMetrics.get(topic);\n if (!m) {\n m = { processedCount: 0, retryCount: 0, dlqCount: 0, dedupCount: 0 };\n this._topicMetrics.set(topic, m);\n }\n return m;\n }\n\n private notifyRetry(\n envelope: import(\"../message/envelope\").EventEnvelope<any>,\n attempt: number,\n maxRetries: number,\n ): void {\n this.metricsFor(envelope.topic).retryCount++;\n for (const inst of this.instrumentation) {\n inst.onRetry?.(envelope, attempt, maxRetries);\n }\n }\n\n private notifyDlq(\n envelope: import(\"../message/envelope\").EventEnvelope<any>,\n reason: DlqReason,\n gid?: string,\n ): void {\n this.metricsFor(envelope.topic).dlqCount++;\n for (const inst of this.instrumentation) {\n inst.onDlq?.(envelope, reason);\n }\n\n if (!gid) return;\n const cfg = this.circuitConfigs.get(gid);\n if (!cfg) return;\n\n const threshold = cfg.threshold ?? 5;\n const recoveryMs = cfg.recoveryMs ?? 30_000;\n\n const stateKey = `${gid}:${envelope.topic}:${envelope.partition}`;\n let state = this.circuitStates.get(stateKey);\n if (!state) {\n state = { status: \"closed\", window: [], successes: 0 };\n this.circuitStates.set(stateKey, state);\n }\n if (state.status === \"open\") return; // already tripped — skip window update\n\n const openCircuit = () => {\n state!.status = \"open\";\n this.pauseConsumer(gid, [\n { topic: envelope.topic, partitions: [envelope.partition] },\n ]);\n state!.timer = setTimeout(() => {\n state!.status = \"half-open\";\n state!.successes = 0;\n this.logger.log(\n `[CircuitBreaker] HALF-OPEN — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition}`,\n );\n this.resumeConsumer(gid, [\n { topic: envelope.topic, partitions: [envelope.partition] },\n ]);\n }, recoveryMs);\n };\n\n if (state.status === \"half-open\") {\n // Any failure in half-open immediately re-opens the circuit.\n clearTimeout(state.timer);\n this.logger.warn(\n `[CircuitBreaker] OPEN (half-open failure) — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition}`,\n );\n openCircuit();\n return;\n }\n\n // CLOSED: update sliding window\n const windowSize = cfg.windowSize ?? Math.max(threshold * 2, 10);\n state.window = [...state.window, false];\n if (state.window.length > windowSize) {\n state.window = state.window.slice(state.window.length - windowSize);\n }\n const failures = state.window.filter((v) => !v).length;\n\n if (failures >= threshold) {\n this.logger.warn(\n `[CircuitBreaker] OPEN — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition} ` +\n `(${failures}/${state.window.length} failures, threshold=${threshold})`,\n );\n openCircuit();\n }\n }\n\n private notifyDuplicate(\n envelope: import(\"../message/envelope\").EventEnvelope<any>,\n strategy: \"drop\" | \"dlq\" | \"topic\",\n ): void {\n this.metricsFor(envelope.topic).dedupCount++;\n for (const inst of this.instrumentation) {\n inst.onDuplicate?.(envelope, strategy);\n }\n }\n\n private notifyMessage(\n envelope: import(\"../message/envelope\").EventEnvelope<any>,\n gid?: string,\n ): void {\n this.metricsFor(envelope.topic).processedCount++;\n for (const inst of this.instrumentation) {\n inst.onMessage?.(envelope);\n }\n\n if (!gid) return;\n const cfg = this.circuitConfigs.get(gid);\n if (!cfg) return;\n\n const stateKey = `${gid}:${envelope.topic}:${envelope.partition}`;\n const state = this.circuitStates.get(stateKey);\n if (!state) return;\n\n const halfOpenSuccesses = cfg.halfOpenSuccesses ?? 1;\n\n if (state.status === \"half-open\") {\n state.successes++;\n if (state.successes >= halfOpenSuccesses) {\n clearTimeout(state.timer);\n state.timer = undefined;\n state.status = \"closed\";\n state.window = [];\n state.successes = 0;\n this.logger.log(\n `[CircuitBreaker] CLOSED — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition}`,\n );\n }\n } else if (state.status === \"closed\") {\n const threshold = cfg.threshold ?? 5;\n const windowSize = cfg.windowSize ?? Math.max(threshold * 2, 10);\n state.window = [...state.window, true];\n if (state.window.length > windowSize) {\n state.window = state.window.slice(state.window.length - windowSize);\n }\n }\n }\n\n /**\n * Start a timer that logs a warning if `fn` hasn't resolved within `timeoutMs`.\n * The handler itself is not cancelled — the warning is diagnostic only.\n */\n private wrapWithTimeoutWarning<R>(\n fn: () => Promise<R>,\n timeoutMs: number,\n topic: string,\n ): Promise<R> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const promise = fn().finally(() => {\n if (timer !== undefined) clearTimeout(timer);\n });\n timer = setTimeout(() => {\n this.logger.warn(\n `Handler for topic \"${topic}\" has not resolved after ${timeoutMs}ms — possible stuck handler`,\n );\n }, timeoutMs);\n return promise;\n }\n\n /**\n * When `retryTopics: true` and `autoCreateTopics: false`, verify that every\n * `<topic>.retry.<level>` topic already exists. Throws a clear error at startup\n * rather than silently discovering missing topics on the first handler failure.\n */\n private async validateRetryTopicsExist(\n topicNames: string[],\n maxRetries: number,\n ): Promise<void> {\n await this.ensureAdminConnected();\n const existing = new Set(await this.admin.listTopics());\n const missing: string[] = [];\n for (const t of topicNames) {\n for (let level = 1; level <= maxRetries; level++) {\n const retryTopic = `${t}.retry.${level}`;\n if (!existing.has(retryTopic)) missing.push(retryTopic);\n }\n }\n if (missing.length > 0) {\n throw new Error(\n `retryTopics: true but the following retry topics do not exist: ${missing.join(\", \")}. ` +\n `Create them manually or set autoCreateTopics: true.`,\n );\n }\n }\n\n /**\n * When `autoCreateTopics` is disabled, verify that `<topic>.dlq` exists for every\n * consumed topic. Throws a clear error at startup rather than silently discovering\n * missing DLQ topics on the first handler failure.\n */\n private async validateDlqTopicsExist(topicNames: string[]): Promise<void> {\n await this.ensureAdminConnected();\n const existing = new Set(await this.admin.listTopics());\n const missing = topicNames\n .filter((t) => !existing.has(`${t}.dlq`))\n .map((t) => `${t}.dlq`);\n if (missing.length > 0) {\n throw new Error(\n `dlq: true but the following DLQ topics do not exist: ${missing.join(\", \")}. ` +\n `Create them manually or set autoCreateTopics: true.`,\n );\n }\n }\n\n /**\n * When `deduplication.strategy: 'topic'` and `autoCreateTopics: false`, verify\n * that every `<topic>.duplicates` destination topic already exists. Throws a\n * clear error at startup rather than silently dropping duplicates on first hit.\n */\n private async validateDuplicatesTopicsExist(\n topicNames: string[],\n customDestination: string | undefined,\n ): Promise<void> {\n await this.ensureAdminConnected();\n const existing = new Set(await this.admin.listTopics());\n const toCheck = customDestination\n ? [customDestination]\n : topicNames.map((t) => `${t}.duplicates`);\n const missing = toCheck.filter((t) => !existing.has(t));\n if (missing.length > 0) {\n throw new Error(\n `deduplication.strategy: 'topic' but the following duplicate-routing topics do not exist: ${missing.join(\", \")}. ` +\n `Create them manually or set autoCreateTopics: true.`,\n );\n }\n }\n\n /**\n * Connect the admin client if not already connected.\n * The flag is only set to `true` after a successful connect — if `admin.connect()`\n * throws the flag remains `false` so the next call will retry the connection.\n */\n private async ensureAdminConnected(): Promise<void> {\n if (this.isAdminConnected) return;\n try {\n await this.admin.connect();\n this.isAdminConnected = true;\n } catch (err) {\n this.isAdminConnected = false;\n throw err;\n }\n }\n\n /**\n * Create and connect a transactional producer for EOS retry routing.\n * Each retry level consumer gets its own producer with a unique `transactionalId`\n * so Kafka can fence stale producers on restart without affecting other levels.\n */\n private async createRetryTxProducer(\n transactionalId: string,\n ): Promise<Producer> {\n if (_activeTransactionalIds.has(transactionalId)) {\n this.logger.warn(\n `transactionalId \"${transactionalId}\" is already in use by another KafkaClient in this process. ` +\n `Kafka will fence one of the producers. ` +\n `Set a unique \\`transactionalId\\` (or distinct \\`clientId\\`) per instance.`,\n );\n }\n const p = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n idempotent: true,\n transactionalId,\n maxInFlightRequests: 1,\n },\n });\n await p.connect();\n _activeTransactionalIds.add(transactionalId);\n this.retryTxProducers.set(transactionalId, p);\n return p;\n }\n\n private async ensureTopic(topic: string): Promise<void> {\n if (!this.autoCreateTopicsEnabled || this.ensuredTopics.has(topic)) return;\n // Deduplicate concurrent calls for the same topic so that parallel sends\n // (or consumer setup + send) don't each race to call createTopics.\n let p = this.ensureTopicPromises.get(topic);\n if (!p) {\n p = (async () => {\n await this.ensureAdminConnected();\n await this.admin.createTopics({\n topics: [{ topic, numPartitions: this.numPartitions }],\n });\n this.ensuredTopics.add(topic);\n })().finally(() => this.ensureTopicPromises.delete(topic));\n this.ensureTopicPromises.set(topic, p);\n }\n await p;\n }\n\n /** Shared consumer setup: groupId check, schema map, connect, subscribe. */\n private async setupConsumer(\n topics: any[],\n mode: \"eachMessage\" | \"eachBatch\",\n options: ConsumerOptions<T>,\n ) {\n const {\n groupId: optGroupId,\n fromBeginning = false,\n retry,\n dlq = false,\n interceptors = [],\n schemas: optionSchemas,\n } = options;\n\n const gid = optGroupId || this.defaultGroupId;\n const existingMode = this.runningConsumers.get(gid);\n const oppositeMode = mode === \"eachMessage\" ? \"eachBatch\" : \"eachMessage\";\n if (existingMode === oppositeMode) {\n throw new Error(\n `Cannot use ${mode} on consumer group \"${gid}\" — it is already running with ${oppositeMode}. ` +\n `Use a different groupId for this consumer.`,\n );\n }\n if (existingMode === mode) {\n const callerName =\n mode === \"eachMessage\" ? \"startConsumer\" : \"startBatchConsumer\";\n throw new Error(\n `${callerName}(\"${gid}\") called twice — this group is already consuming. ` +\n `Call stopConsumer(\"${gid}\") first or pass a different groupId.`,\n );\n }\n\n const consumer = getOrCreateConsumer(\n gid,\n fromBeginning,\n options.autoCommit ?? true,\n this.consumerOpsDeps,\n );\n const schemaMap = buildSchemaMap(\n topics,\n this.schemaRegistry,\n optionSchemas,\n this.logger,\n );\n\n const topicNames = (topics as any[]).map((t: any) => resolveTopicName(t));\n\n // Ensure topics exist before subscribing — librdkafka errors on unknown topics\n for (const t of topicNames) {\n await this.ensureTopic(t);\n }\n if (dlq) {\n for (const t of topicNames) {\n await this.ensureTopic(`${t}.dlq`);\n }\n if (!this.autoCreateTopicsEnabled) {\n await this.validateDlqTopicsExist(topicNames);\n }\n }\n\n if (options.deduplication?.strategy === \"topic\") {\n const dest = options.deduplication.duplicatesTopic;\n if (this.autoCreateTopicsEnabled) {\n for (const t of topicNames) {\n await this.ensureTopic(dest ?? `${t}.duplicates`);\n }\n } else {\n await this.validateDuplicatesTopicsExist(topicNames, dest);\n }\n }\n\n await consumer.connect();\n await subscribeWithRetry(\n consumer,\n topicNames,\n this.logger,\n options.subscribeRetry,\n );\n\n this.logger.log(\n `${mode === \"eachBatch\" ? \"Batch consumer\" : \"Consumer\"} subscribed to topics: ${topicNames.join(\", \")}`,\n );\n\n return { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry };\n }\n\n /** Create or retrieve the deduplication context for a consumer group. */\n private resolveDeduplicationContext(\n groupId: string,\n options: import(\"../types\").DeduplicationOptions | undefined,\n ): DeduplicationContext | undefined {\n if (!options) return undefined;\n if (!this.dedupStates.has(groupId)) {\n this.dedupStates.set(groupId, new Map());\n }\n return { options, state: this.dedupStates.get(groupId)! };\n }\n\n // ── Deps object getters ──────────────────────────────────────────\n\n private get producerOpsDeps(): BuildSendPayloadDeps {\n return {\n schemaRegistry: this.schemaRegistry,\n strictSchemasEnabled: this.strictSchemasEnabled,\n instrumentation: this.instrumentation,\n logger: this.logger,\n nextLamportClock: () => ++this._lamportClock,\n };\n }\n\n private get consumerOpsDeps(): ConsumerOpsDeps {\n return {\n consumers: this.consumers,\n consumerCreationOptions: this.consumerCreationOptions,\n kafka: this.kafka,\n onRebalance: this.onRebalance,\n logger: this.logger,\n };\n }\n\n /** Build MessageHandlerDeps with circuit breaker callbacks bound to the given groupId. */\n private messageDepsFor(gid: string): MessageHandlerDeps {\n return {\n logger: this.logger,\n producer: this.producer,\n instrumentation: this.instrumentation,\n onMessageLost: this.onMessageLost,\n onRetry: this.notifyRetry.bind(this),\n onDlq: (envelope, reason) => this.notifyDlq(envelope, reason, gid),\n onDuplicate: this.notifyDuplicate.bind(this),\n onMessage: (envelope) => this.notifyMessage(envelope, gid),\n };\n }\n\n private get retryTopicDeps() {\n return {\n logger: this.logger,\n producer: this.producer,\n instrumentation: this.instrumentation,\n onMessageLost: this.onMessageLost,\n onRetry: this.notifyRetry.bind(this),\n onDlq: this.notifyDlq.bind(this),\n onMessage: this.notifyMessage.bind(this),\n ensureTopic: (t: string) => this.ensureTopic(t),\n getOrCreateConsumer: (gid: string, fb: boolean, ac: boolean) =>\n getOrCreateConsumer(gid, fb, ac, this.consumerOpsDeps),\n runningConsumers: this.runningConsumers,\n createRetryTxProducer: (txId: string) => this.createRetryTxProducer(txId),\n };\n }\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { randomUUID } from \"node:crypto\";\nimport type { MessageHeaders } from \"../types\";\n\n// ── Header keys ──────────────────────────────────────────────────────\n\nexport const HEADER_EVENT_ID = \"x-event-id\";\nexport const HEADER_CORRELATION_ID = \"x-correlation-id\";\nexport const HEADER_TIMESTAMP = \"x-timestamp\";\nexport const HEADER_SCHEMA_VERSION = \"x-schema-version\";\nexport const HEADER_TRACEPARENT = \"traceparent\";\n/** Monotonically increasing logical clock stamped by the producer for deduplication. */\nexport const HEADER_LAMPORT_CLOCK = \"x-lamport-clock\";\n\n// ── EventEnvelope ────────────────────────────────────────────────────\n\n/**\n * Typed wrapper combining a parsed message payload with Kafka metadata\n * and envelope headers.\n *\n * On **send**, the library auto-generates envelope headers\n * (`x-event-id`, `x-correlation-id`, `x-timestamp`, `x-schema-version`).\n *\n * On **consume**, the library extracts those headers and assembles\n * an `EventEnvelope` that is passed to the handler.\n */\nexport interface EventEnvelope<T> {\n /** Deserialized + validated message body. */\n payload: T;\n /** Topic the message was produced to / consumed from. */\n topic: string;\n /** Kafka partition (consume-side only, `-1` on send). */\n partition: number;\n /** Kafka offset (consume-side only, empty string on send). */\n offset: string;\n /** ISO-8601 timestamp set by the producer. */\n timestamp: string;\n /** Unique ID for this event (UUID v4). */\n eventId: string;\n /** Correlation ID — auto-propagated via AsyncLocalStorage. */\n correlationId: string;\n /** Schema version of the payload. */\n schemaVersion: number;\n /** W3C Trace Context `traceparent` header (set by OTel instrumentation). */\n traceparent?: string;\n /** All decoded Kafka headers for extensibility. */\n headers: MessageHeaders;\n}\n\n// ── AsyncLocalStorage context ────────────────────────────────────────\n\ninterface EnvelopeCtx {\n correlationId: string;\n traceparent?: string;\n}\n\nconst envelopeStorage = new AsyncLocalStorage<EnvelopeCtx>();\n\n/** Read the current envelope context (correlationId / traceparent) from ALS. */\nexport function getEnvelopeContext(): EnvelopeCtx | undefined {\n return envelopeStorage.getStore();\n}\n\n/** Execute `fn` inside an envelope context so nested sends inherit correlationId. */\nexport function runWithEnvelopeContext<R>(ctx: EnvelopeCtx, fn: () => R): R {\n return envelopeStorage.run(ctx, fn);\n}\n\n// ── Header helpers ───────────────────────────────────────────────────\n\n/** Options accepted by `buildEnvelopeHeaders`. */\nexport interface EnvelopeHeaderOptions {\n correlationId?: string;\n schemaVersion?: number;\n eventId?: string;\n headers?: MessageHeaders;\n}\n\n/**\n * Generate envelope headers for the send path.\n *\n * Priority for `correlationId`:\n * explicit option → ALS context → new UUID.\n */\nexport function buildEnvelopeHeaders(\n options: EnvelopeHeaderOptions = {},\n): MessageHeaders {\n const ctx = getEnvelopeContext();\n\n const correlationId =\n options.correlationId ?? ctx?.correlationId ?? randomUUID();\n const eventId = options.eventId ?? randomUUID();\n const timestamp = new Date().toISOString();\n const schemaVersion = String(options.schemaVersion ?? 1);\n\n const envelope: MessageHeaders = {\n [HEADER_EVENT_ID]: eventId,\n [HEADER_CORRELATION_ID]: correlationId,\n [HEADER_TIMESTAMP]: timestamp,\n [HEADER_SCHEMA_VERSION]: schemaVersion,\n };\n\n // Propagate traceparent from ALS if present (OTel may override via instrumentation)\n if (ctx?.traceparent) {\n envelope[HEADER_TRACEPARENT] = ctx.traceparent;\n }\n\n // User-provided headers win on conflict\n return { ...envelope, ...options.headers };\n}\n\n/**\n * Decode kafkajs headers (`Record<string, Buffer | string | undefined>`)\n * into plain `Record<string, string>`.\n */\nexport function decodeHeaders(\n raw:\n | Record<string, Buffer | string | (Buffer | string)[] | undefined>\n | undefined,\n): MessageHeaders {\n if (!raw) return {};\n const result: MessageHeaders = {};\n for (const [key, value] of Object.entries(raw)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n // Kafka allows multiple headers with the same key — take the last one (last-wins),\n // consistent with HTTP semantics. Joining would corrupt values that contain commas.\n const items = value.map((v) => (Buffer.isBuffer(v) ? v.toString() : v));\n result[key] = items[items.length - 1] ?? \"\";\n } else {\n result[key] = Buffer.isBuffer(value) ? value.toString() : value;\n }\n }\n return result;\n}\n\n/**\n * Build an `EventEnvelope` from a consumed kafkajs message.\n * Tolerates missing envelope headers — generates defaults so messages\n * from non-envelope producers still work.\n */\nexport function extractEnvelope<T>(\n payload: T,\n headers: MessageHeaders,\n topic: string,\n partition: number,\n offset: string,\n): EventEnvelope<T> {\n return {\n payload,\n topic,\n partition,\n offset,\n eventId: headers[HEADER_EVENT_ID] ?? randomUUID(),\n correlationId: headers[HEADER_CORRELATION_ID] ?? randomUUID(),\n timestamp: headers[HEADER_TIMESTAMP] ?? new Date().toISOString(),\n schemaVersion: Number(headers[HEADER_SCHEMA_VERSION] ?? 1),\n traceparent: headers[HEADER_TRACEPARENT],\n headers,\n };\n}\n","/** Error thrown when a consumer message handler fails. */\nexport class KafkaProcessingError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n message: string,\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(message, options);\n this.name = \"KafkaProcessingError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when schema validation fails on send or consume. */\nexport class KafkaValidationError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(`Schema validation failed for topic \"${topic}\"`, options);\n this.name = \"KafkaValidationError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when all retry attempts are exhausted for a message. */\nexport class KafkaRetryExhaustedError extends KafkaProcessingError {\n constructor(\n topic: string,\n originalMessage: unknown,\n public readonly attempts: number,\n options?: { cause?: Error },\n ) {\n super(\n `Message processing failed after ${attempts} attempts on topic \"${topic}\"`,\n topic,\n originalMessage,\n options,\n );\n this.name = \"KafkaRetryExhaustedError\";\n }\n}\n","import {\n buildEnvelopeHeaders,\n HEADER_LAMPORT_CLOCK,\n} from \"../message/envelope\";\nimport { KafkaValidationError } from \"../errors\";\nimport type { SchemaLike, SchemaParseContext } from \"../message/topic\";\nimport type {\n BatchMessageItem,\n KafkaInstrumentation,\n KafkaLogger,\n MessageHeaders,\n} from \"../types\";\n\nexport function resolveTopicName(topicOrDescriptor: unknown): string {\n if (typeof topicOrDescriptor === \"string\") return topicOrDescriptor;\n if (\n topicOrDescriptor &&\n typeof topicOrDescriptor === \"object\" &&\n \"__topic\" in topicOrDescriptor\n ) {\n return (topicOrDescriptor as { __topic: string }).__topic;\n }\n return String(topicOrDescriptor);\n}\n\nexport function registerSchema(\n topicOrDesc: any,\n schemaRegistry: Map<string, SchemaLike>,\n logger?: KafkaLogger,\n): void {\n if (topicOrDesc?.__schema) {\n const topic = resolveTopicName(topicOrDesc);\n const existing = schemaRegistry.get(topic);\n if (existing && existing !== topicOrDesc.__schema) {\n logger?.warn(\n `Schema conflict for topic \"${topic}\": a different schema is already registered. ` +\n `Using the new schema — ensure consistent schemas to avoid silent validation mismatches.`,\n );\n }\n schemaRegistry.set(topic, topicOrDesc.__schema);\n }\n}\n\nexport async function validateMessage(\n topicOrDesc: any,\n message: any,\n deps: {\n schemaRegistry: Map<string, SchemaLike>;\n strictSchemasEnabled: boolean;\n },\n ctx?: SchemaParseContext,\n): Promise<any> {\n const topicName = resolveTopicName(topicOrDesc);\n if (topicOrDesc?.__schema) {\n try {\n return await topicOrDesc.__schema.parse(message, ctx);\n } catch (error) {\n throw new KafkaValidationError(topicName, message, {\n cause: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n if (deps.strictSchemasEnabled && typeof topicOrDesc === \"string\") {\n const schema = deps.schemaRegistry.get(topicOrDesc);\n if (schema) {\n try {\n return await schema.parse(message, ctx);\n } catch (error) {\n throw new KafkaValidationError(topicName, message, {\n cause: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n }\n return message;\n}\n\nexport type BuildSendPayloadDeps = {\n schemaRegistry: Map<string, SchemaLike>;\n strictSchemasEnabled: boolean;\n instrumentation: KafkaInstrumentation[];\n logger: KafkaLogger;\n /** Called once per message to get the next Lamport clock value. Omit to disable clock stamping. */\n nextLamportClock?: () => number;\n};\n\nexport async function buildSendPayload(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n deps: BuildSendPayloadDeps,\n): Promise<{\n topic: string;\n messages: Array<{\n value: string;\n key: string | null;\n headers: MessageHeaders;\n }>;\n}> {\n const topic = resolveTopicName(topicOrDesc);\n const builtMessages = await Promise.all(\n messages.map(async (m) => {\n const envelopeHeaders = buildEnvelopeHeaders({\n correlationId: m.correlationId,\n schemaVersion: m.schemaVersion,\n eventId: m.eventId,\n headers: m.headers,\n });\n\n // Stamp Lamport clock for consumer-side deduplication\n if (deps.nextLamportClock) {\n envelopeHeaders[HEADER_LAMPORT_CLOCK] = String(deps.nextLamportClock());\n }\n\n // beforeSend: let instrumentation mutate headers (e.g. OTel injects traceparent)\n for (const inst of deps.instrumentation) {\n inst.beforeSend?.(topic, envelopeHeaders);\n }\n\n const sendCtx: SchemaParseContext = {\n topic,\n headers: envelopeHeaders,\n version: m.schemaVersion ?? 1,\n };\n\n return {\n value: JSON.stringify(\n await validateMessage(topicOrDesc, m.value, deps, sendCtx),\n ),\n key: m.key ?? null,\n headers: envelopeHeaders,\n };\n }),\n );\n return { topic, messages: builtMessages };\n}\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Consumer = KafkaJS.Consumer;\ntype Kafka = KafkaJS.Kafka;\nimport type { SchemaLike } from \"../message/topic\";\nimport type { KafkaClientOptions, KafkaLogger } from \"../types\";\nimport { resolveTopicName } from \"./producer-ops\";\n\nexport type ConsumerOpsDeps = {\n consumers: Map<string, Consumer>;\n consumerCreationOptions: Map<\n string,\n { fromBeginning: boolean; autoCommit: boolean }\n >;\n kafka: Kafka;\n onRebalance: KafkaClientOptions[\"onRebalance\"];\n logger: KafkaLogger;\n};\n\nexport function getOrCreateConsumer(\n groupId: string,\n fromBeginning: boolean,\n autoCommit: boolean,\n deps: ConsumerOpsDeps,\n): Consumer {\n const { consumers, consumerCreationOptions, kafka, onRebalance, logger } =\n deps;\n\n if (consumers.has(groupId)) {\n const prev = consumerCreationOptions.get(groupId)!;\n if (\n prev.fromBeginning !== fromBeginning ||\n prev.autoCommit !== autoCommit\n ) {\n logger.warn(\n `Consumer group \"${groupId}\" already exists with options ` +\n `(fromBeginning: ${prev.fromBeginning}, autoCommit: ${prev.autoCommit}) — ` +\n `new options (fromBeginning: ${fromBeginning}, autoCommit: ${autoCommit}) ignored. ` +\n `Use a different groupId to apply different options.`,\n );\n }\n return consumers.get(groupId)!;\n }\n\n consumerCreationOptions.set(groupId, { fromBeginning, autoCommit });\n\n const config: Parameters<typeof kafka.consumer>[0] = {\n kafkaJS: { groupId, fromBeginning, autoCommit },\n };\n\n if (onRebalance) {\n const cb = onRebalance;\n // rebalance_cb is called by librdkafka on every partition assign/revoke.\n // err.code -175 = ERR__ASSIGN_PARTITIONS, -174 = ERR__REVOKE_PARTITIONS.\n // The library handles the actual assign/unassign in its finally block regardless\n // of what this callback does, so we only need it for the side-effect notification.\n (config as any)[\"rebalance_cb\"] = (err: any, assignment: any[]) => {\n const type = err.code === -175 ? \"assign\" : \"revoke\";\n try {\n cb(\n type,\n assignment.map((p) => ({ topic: p.topic, partition: p.partition })),\n );\n } catch (e) {\n logger.warn(`onRebalance callback threw: ${(e as Error).message}`);\n }\n };\n }\n\n const consumer = kafka.consumer(config);\n consumers.set(groupId, consumer);\n return consumer;\n}\n\nexport function buildSchemaMap(\n topics: any[],\n schemaRegistry: Map<string, SchemaLike>,\n optionSchemas?: Map<string, SchemaLike>,\n logger?: KafkaLogger,\n): Map<string, SchemaLike> {\n const schemaMap = new Map<string, SchemaLike>();\n\n const registerChecked = (name: string, schema: SchemaLike) => {\n const existing = schemaRegistry.get(name);\n if (existing && existing !== schema) {\n logger?.warn(\n `Schema conflict for topic \"${name}\": a different schema is already registered. ` +\n `Using the new schema — ensure consistent schemas to avoid silent validation mismatches.`,\n );\n }\n schemaMap.set(name, schema);\n schemaRegistry.set(name, schema);\n };\n\n for (const t of topics) {\n if (t?.__schema) {\n registerChecked(resolveTopicName(t), t.__schema);\n }\n }\n if (optionSchemas) {\n for (const [k, v] of optionSchemas) {\n registerChecked(k, v);\n }\n }\n return schemaMap;\n}\n","import type { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Producer = KafkaJS.Producer;\nimport type { EventEnvelope } from \"../message/envelope\";\nimport { extractEnvelope } from \"../message/envelope\";\nimport { KafkaRetryExhaustedError, KafkaValidationError } from \"../errors\";\nimport type { SchemaLike, SchemaParseContext } from \"../message/topic\";\nimport type {\n BeforeConsumeResult,\n ConsumerInterceptor,\n DlqReason,\n KafkaInstrumentation,\n KafkaLogger,\n MessageHeaders,\n MessageLostContext,\n RetryOptions,\n TopicMapConstraint,\n} from \"../types\";\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ── JSON parsing ────────────────────────────────────────────────────\n\n/** Parse raw message as JSON. Returns null on failure (logs error). */\nexport function parseJsonMessage(\n raw: string,\n topic: string,\n logger: KafkaLogger,\n): any | null {\n try {\n return JSON.parse(raw);\n } catch (error) {\n logger.error(\n `Failed to parse message from topic ${topic}:`,\n toError(error).stack,\n );\n return null;\n }\n}\n\n// ── Schema validation ───────────────────────────────────────────────\n\n/**\n * Validate a parsed message against the schema map.\n * On failure: logs error, sends to DLQ if enabled, calls interceptor.onError.\n * Returns validated message or null.\n */\nexport async function validateWithSchema<T extends TopicMapConstraint<T>>(\n message: any,\n raw: string,\n topic: string,\n schemaMap: Map<string, SchemaLike>,\n interceptors: ConsumerInterceptor<T>[],\n dlq: boolean,\n deps: {\n logger: KafkaLogger;\n producer: Producer;\n onMessageLost?: (ctx: MessageLostContext) => void | Promise<void>;\n originalHeaders?: MessageHeaders;\n instrumentation?: KafkaInstrumentation[];\n },\n): Promise<any | null> {\n const schema = schemaMap.get(topic);\n if (!schema) return message;\n\n const ctx: SchemaParseContext = {\n topic,\n headers: deps.originalHeaders ?? {},\n version: Number(deps.originalHeaders?.[\"x-schema-version\"] ?? 1),\n };\n\n try {\n return await schema.parse(message, ctx);\n } catch (error) {\n const err = toError(error);\n const validationError = new KafkaValidationError(topic, message, {\n cause: err,\n });\n deps.logger.error(\n `Schema validation failed for topic ${topic}:`,\n err.message,\n );\n if (dlq) {\n await sendToDlq(topic, raw, deps, {\n error: validationError,\n attempt: 0,\n originalHeaders: deps.originalHeaders,\n });\n } else {\n await deps.onMessageLost?.({\n topic,\n error: validationError,\n attempt: 0,\n headers: deps.originalHeaders ?? {},\n });\n }\n // Validation errors don't have an envelope yet — call hooks with a minimal envelope\n const errorEnvelope = extractEnvelope(\n message,\n deps.originalHeaders ?? {},\n topic,\n -1,\n \"\",\n );\n for (const inst of deps.instrumentation ?? []) {\n inst.onConsumeError?.(errorEnvelope, validationError);\n }\n for (const interceptor of interceptors) {\n await interceptor.onError?.(errorEnvelope, validationError);\n }\n return null;\n }\n}\n\n// ── DLQ ─────────────────────────────────────────────────────────────\n\nexport interface DlqMetadata {\n error: Error;\n attempt: number;\n /** Original Kafka message headers — forwarded to DLQ to preserve correlationId, traceparent, etc. */\n originalHeaders?: MessageHeaders;\n}\n\n/** Build the DLQ send payload without sending it. Used by sendToDlq and EOS routing. */\nexport function buildDlqPayload(\n topic: string,\n rawMessage: string,\n meta?: DlqMetadata,\n): {\n topic: string;\n messages: Array<{ value: string; headers: MessageHeaders }>;\n} {\n const dlqTopic = `${topic}.dlq`;\n const headers: MessageHeaders = {\n ...(meta?.originalHeaders ?? {}),\n \"x-dlq-original-topic\": topic,\n \"x-dlq-failed-at\": new Date().toISOString(),\n \"x-dlq-error-message\": meta?.error.message ?? \"unknown\",\n \"x-dlq-error-stack\": meta?.error.stack?.slice(0, 2000) ?? \"\",\n \"x-dlq-attempt-count\": String(meta?.attempt ?? 0),\n };\n return { topic: dlqTopic, messages: [{ value: rawMessage, headers }] };\n}\n\nexport async function sendToDlq(\n topic: string,\n rawMessage: string,\n deps: {\n logger: KafkaLogger;\n producer: Producer;\n onMessageLost?: (ctx: MessageLostContext) => void | Promise<void>;\n },\n meta?: DlqMetadata,\n): Promise<void> {\n const payload = buildDlqPayload(topic, rawMessage, meta);\n try {\n await deps.producer.send(payload);\n deps.logger.warn(`Message sent to DLQ: ${payload.topic}`);\n } catch (error) {\n const err = toError(error);\n deps.logger.error(\n `Failed to send message to DLQ ${payload.topic}:`,\n err.stack,\n );\n await deps.onMessageLost?.({\n topic,\n error: err,\n attempt: meta?.attempt ?? 0,\n headers: meta?.originalHeaders ?? {},\n });\n }\n}\n\n// ── Retry topic routing ─────────────────────────────────────────────\n\n/** Headers stamped on messages sent to a `<topic>.retry` topic. */\nexport const RETRY_HEADER_ATTEMPT = \"x-retry-attempt\";\nexport const RETRY_HEADER_AFTER = \"x-retry-after\";\nexport const RETRY_HEADER_MAX_RETRIES = \"x-retry-max-retries\";\nexport const RETRY_HEADER_ORIGINAL_TOPIC = \"x-retry-original-topic\";\n\n/** Build the retry topic send payload without sending it. Used by sendToRetryTopic and EOS routing. */\nexport function buildRetryTopicPayload(\n originalTopic: string,\n rawMessages: string[],\n attempt: number,\n maxRetries: number,\n delayMs: number,\n /** Per-message headers (array) or a single object applied to all messages. */\n originalHeaders: MessageHeaders | MessageHeaders[],\n): {\n topic: string;\n messages: Array<{ value: string; headers: MessageHeaders }>;\n} {\n const retryTopic = `${originalTopic}.retry.${attempt}`;\n const STRIP = new Set([\n RETRY_HEADER_ATTEMPT,\n RETRY_HEADER_AFTER,\n RETRY_HEADER_MAX_RETRIES,\n RETRY_HEADER_ORIGINAL_TOPIC,\n ]);\n function buildHeaders(hdr: MessageHeaders): MessageHeaders {\n // Strip any stale retry headers from a previous hop so they don't leak through.\n const userHeaders = Object.fromEntries(\n Object.entries(hdr).filter(([k]) => !STRIP.has(k)),\n );\n return {\n ...userHeaders,\n [RETRY_HEADER_ATTEMPT]: String(attempt),\n [RETRY_HEADER_AFTER]: String(Date.now() + delayMs),\n [RETRY_HEADER_MAX_RETRIES]: String(maxRetries),\n [RETRY_HEADER_ORIGINAL_TOPIC]: originalTopic,\n };\n }\n return {\n topic: retryTopic,\n messages: rawMessages.map((value, i) => ({\n value,\n headers: buildHeaders(\n Array.isArray(originalHeaders)\n ? (originalHeaders[i] ?? {})\n : originalHeaders,\n ),\n })),\n };\n}\n\n/**\n * Send raw messages to the retry topic `<originalTopic>.retry`.\n * Stamps scheduling headers so the retry consumer knows when and how many times to retry.\n */\nexport async function sendToRetryTopic(\n originalTopic: string,\n rawMessages: string[],\n attempt: number,\n maxRetries: number,\n delayMs: number,\n /** Per-message headers (array) or a single object applied to all messages. */\n originalHeaders: MessageHeaders | MessageHeaders[],\n deps: {\n logger: KafkaLogger;\n producer: Producer;\n onMessageLost?: (ctx: MessageLostContext) => void | Promise<void>;\n },\n): Promise<void> {\n const payload = buildRetryTopicPayload(\n originalTopic,\n rawMessages,\n attempt,\n maxRetries,\n delayMs,\n originalHeaders,\n );\n try {\n await deps.producer.send(payload);\n deps.logger.warn(\n `Message queued in retry topic ${payload.topic} (attempt ${attempt}/${maxRetries})`,\n );\n } catch (error) {\n const err = toError(error);\n deps.logger.error(\n `Failed to send message to retry topic ${payload.topic}:`,\n err.stack,\n );\n await deps.onMessageLost?.({\n topic: originalTopic,\n error: err,\n attempt,\n headers: Array.isArray(originalHeaders)\n ? (originalHeaders[0] ?? {})\n : originalHeaders,\n });\n }\n}\n\n// ── Deduplication routing ────────────────────────────────────────────\n\nexport interface DuplicateMetadata {\n /** The `x-lamport-clock` value from the incoming (duplicate) message. */\n incomingClock: number;\n /** The last processed clock value for this topic/partition. */\n lastProcessedClock: number;\n /** Original Kafka message headers — forwarded to preserve correlationId, traceparent, etc. */\n originalHeaders?: MessageHeaders;\n}\n\n/** Build the payload for a duplicate message forwarded to a custom topic. */\nexport function buildDuplicateTopicPayload(\n sourceTopic: string,\n rawMessage: string,\n destinationTopic: string,\n meta?: DuplicateMetadata,\n): {\n topic: string;\n messages: Array<{ value: string; headers: MessageHeaders }>;\n} {\n const headers: MessageHeaders = {\n ...(meta?.originalHeaders ?? {}),\n \"x-duplicate-original-topic\": sourceTopic,\n \"x-duplicate-detected-at\": new Date().toISOString(),\n \"x-duplicate-reason\": \"lamport-clock-duplicate\",\n \"x-duplicate-incoming-clock\": String(meta?.incomingClock ?? 0),\n \"x-duplicate-last-processed-clock\": String(meta?.lastProcessedClock ?? 0),\n };\n return {\n topic: destinationTopic,\n messages: [{ value: rawMessage, headers }],\n };\n}\n\n/**\n * Forward a duplicate message to a dedicated topic (e.g. `<topic>.duplicates`).\n * Stamps reason metadata headers so consumers of that topic know why it landed there.\n */\nexport async function sendToDuplicatesTopic(\n sourceTopic: string,\n rawMessage: string,\n destinationTopic: string,\n deps: { logger: KafkaLogger; producer: Producer },\n meta?: DuplicateMetadata,\n): Promise<void> {\n const payload = buildDuplicateTopicPayload(\n sourceTopic,\n rawMessage,\n destinationTopic,\n meta,\n );\n try {\n await deps.producer.send(payload);\n deps.logger.warn(`Duplicate message forwarded to ${destinationTopic}`);\n } catch (error) {\n deps.logger.error(\n `Failed to forward duplicate to ${destinationTopic}:`,\n toError(error).stack,\n );\n }\n}\n\n// ── Pipeline helpers ─────────────────────────────────────────────────\n\nasync function broadcastToInterceptors<T extends TopicMapConstraint<T>>(\n envelopes: EventEnvelope<any>[],\n interceptors: ConsumerInterceptor<T>[],\n cb: (\n interceptor: ConsumerInterceptor<T>,\n env: EventEnvelope<any>,\n ) => Promise<void> | void | undefined,\n): Promise<void> {\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await cb(interceptor, env);\n }\n }\n}\n\n/**\n * Run `fn` through the full instrumentation and interceptor lifecycle:\n * beforeConsume → interceptor.before → fn → interceptor.after → cleanup\n *\n * On error: fires `onConsumeError` and cleanup, then returns the error.\n * The caller is responsible for calling `notifyInterceptorsOnError` (with\n * a possibly-wrapped error) and deciding what happens next.\n *\n * Returns `null` on success, the caught `Error` on failure.\n */\nexport async function runHandlerWithPipeline<T extends TopicMapConstraint<T>>(\n fn: () => Promise<void>,\n envelopes: EventEnvelope<any>[],\n interceptors: ConsumerInterceptor<T>[],\n instrumentation: KafkaInstrumentation[],\n): Promise<Error | null> {\n const cleanups: (() => void)[] = [];\n const wraps: Array<(fn: () => Promise<void>) => Promise<void>> = [];\n\n try {\n for (const env of envelopes) {\n for (const inst of instrumentation) {\n const result: BeforeConsumeResult | void = inst.beforeConsume?.(env);\n if (typeof result === \"function\") {\n cleanups.push(result);\n } else if (result) {\n if (result.cleanup) cleanups.push(result.cleanup);\n if (result.wrap) wraps.push(result.wrap);\n }\n }\n }\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.before?.(env);\n }\n }\n\n // Compose wraps: first instrumentation is outermost, last is innermost.\n let runFn: () => Promise<void> = fn;\n for (let i = wraps.length - 1; i >= 0; i--) {\n const wrap = wraps[i];\n const inner = runFn;\n runFn = () => wrap(inner);\n }\n await runFn();\n\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.after?.(env);\n }\n }\n for (const cleanup of cleanups) cleanup();\n\n return null;\n } catch (error) {\n const err = toError(error);\n for (const env of envelopes) {\n for (const inst of instrumentation) {\n inst.onConsumeError?.(env, err);\n }\n }\n for (const cleanup of cleanups) cleanup();\n return err;\n }\n}\n\n/**\n * Call `interceptor.onError` for every envelope with the given error.\n * Separated from `runHandlerWithPipeline` so callers can wrap the raw error\n * (e.g. in `KafkaRetryExhaustedError`) before notifying interceptors.\n */\nexport async function notifyInterceptorsOnError<\n T extends TopicMapConstraint<T>,\n>(\n envelopes: EventEnvelope<any>[],\n interceptors: ConsumerInterceptor<T>[],\n error: Error,\n): Promise<void> {\n await broadcastToInterceptors(envelopes, interceptors, (i, env) =>\n i.onError?.(env, error),\n );\n}\n\n// ── Retry pipeline ──────────────────────────────────────────────────\n\nexport interface ExecuteWithRetryContext<T extends TopicMapConstraint<T>> {\n envelope: EventEnvelope<any> | EventEnvelope<any>[];\n rawMessages: string[];\n interceptors: ConsumerInterceptor<T>[];\n dlq: boolean;\n retry?: RetryOptions;\n isBatch?: boolean;\n /**\n * When `true`, failed messages are routed to `<topic>.retry` instead of being\n * retried in-process. All backoff and subsequent attempts are handled by the\n * companion retry consumer started by `startRetryTopicConsumers`.\n */\n retryTopics?: boolean;\n}\n\n/**\n * Execute a handler with retry, interceptors, instrumentation, and DLQ support.\n * Used by both single-message and batch consumers.\n */\nexport async function executeWithRetry<T extends TopicMapConstraint<T>>(\n fn: () => Promise<void>,\n ctx: ExecuteWithRetryContext<T>,\n deps: {\n logger: KafkaLogger;\n producer: Producer;\n instrumentation: KafkaInstrumentation[];\n onMessageLost?: (ctx: MessageLostContext) => void | Promise<void>;\n onRetry?: (\n envelope: EventEnvelope<any>,\n attempt: number,\n maxRetries: number,\n ) => void;\n onDlq?: (envelope: EventEnvelope<any>, reason: DlqReason) => void;\n onMessage?: (envelope: EventEnvelope<any>) => void;\n /**\n * EOS atomic routing to `<topic>.retry.1` (main consumer only).\n * When present, replaces `sendToRetryTopic` with a Kafka transaction that\n * sends to the retry topic AND commits the source offset atomically.\n */\n eosRouteToRetry?: (\n rawMessages: string[],\n envelopes: EventEnvelope<any>[],\n delay: number,\n ) => Promise<void>;\n /**\n * Manual offset commit for the success path when the main consumer runs\n * with `autoCommit: false` (required when `eosRouteToRetry` is active).\n */\n eosCommitOnSuccess?: () => Promise<void>;\n },\n): Promise<void> {\n const {\n envelope,\n rawMessages,\n interceptors,\n dlq,\n retry,\n isBatch,\n retryTopics,\n } = ctx;\n // With retryTopics mode the main consumer tries exactly once — retry consumer takes over.\n const maxAttempts = retryTopics ? 1 : retry ? retry.maxRetries + 1 : 1;\n const backoffMs = retry?.backoffMs ?? 1000;\n const maxBackoffMs = retry?.maxBackoffMs ?? 30_000;\n const envelopes = Array.isArray(envelope) ? envelope : [envelope];\n const topic = envelopes[0]?.topic ?? \"unknown\";\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const error = await runHandlerWithPipeline(\n fn,\n envelopes,\n interceptors,\n deps.instrumentation,\n );\n if (!error) {\n if (deps.eosCommitOnSuccess) {\n try {\n await deps.eosCommitOnSuccess();\n } catch (commitErr) {\n // Offset commit failed — message will be redelivered; don't call onMessage\n // to avoid counting a message whose offset was not actually committed.\n deps.logger.error(\n `EOS offset commit failed after successful handler — message will be redelivered:`,\n toError(commitErr).stack,\n );\n return;\n }\n }\n for (const env of envelopes) deps.onMessage?.(env);\n return;\n }\n\n const isLastAttempt = attempt === maxAttempts;\n const reportedError =\n isLastAttempt && maxAttempts > 1\n ? new KafkaRetryExhaustedError(\n topic,\n envelopes.map((e) => e.payload),\n maxAttempts,\n { cause: error },\n )\n : error;\n\n await notifyInterceptorsOnError(envelopes, interceptors, reportedError);\n\n deps.logger.error(\n `Error processing ${isBatch ? \"batch\" : \"message\"} from topic ${topic} (attempt ${attempt}/${maxAttempts}):`,\n error.stack,\n );\n\n if (retryTopics && retry) {\n // Route to retry topic — retry consumer handles backoff and further attempts.\n // Always use attempt 1 here (main consumer never retries in-process).\n const cap = Math.min(backoffMs, maxBackoffMs);\n const delay = Math.floor(Math.random() * cap);\n if (deps.eosRouteToRetry) {\n // EOS path (single-message consumers): send to retry.1 + commit source\n // offset atomically via a Kafka transaction. A crash at any point rolls\n // back the transaction — no duplicate is routed to retry.1.\n try {\n await deps.eosRouteToRetry(rawMessages, envelopes, delay);\n deps.onRetry?.(envelopes[0]!, 1, retry.maxRetries);\n } catch (txErr) {\n // Transaction failed — offset is NOT committed; message will be\n // redelivered and the routing retried on the next delivery.\n deps.logger.error(\n `EOS routing to retry topic failed — message will be redelivered:`,\n toError(txErr).stack,\n );\n }\n } else {\n // Non-EOS path (batch consumers, or single-message consumers without EOS context).\n // The regular producer sends to retry.1 and the offset is committed by\n // librdkafka's autoCommit after eachMessage returns. A crash between the\n // two operations can cause the message to appear in retry.1 AND be\n // redelivered to the main consumer — handlers must be idempotent.\n await sendToRetryTopic(\n topic,\n rawMessages,\n 1,\n retry.maxRetries,\n delay,\n isBatch\n ? envelopes.map((e) => e.headers)\n : (envelopes[0]?.headers ?? {}),\n deps,\n );\n deps.onRetry?.(envelopes[0]!, 1, retry.maxRetries);\n }\n } else if (isLastAttempt) {\n if (dlq) {\n // Use per-message headers so each DLQ message preserves its own\n // correlationId / traceparent instead of copying envelopes[0]'s headers\n // onto every message in the batch.\n for (let i = 0; i < rawMessages.length; i++) {\n await sendToDlq(topic, rawMessages[i], deps, {\n error,\n attempt,\n originalHeaders: envelopes[i]?.headers,\n });\n deps.onDlq?.(envelopes[i] ?? envelopes[0]!, \"handler-error\");\n }\n } else {\n await deps.onMessageLost?.({\n topic,\n error,\n attempt,\n headers: envelopes[0]?.headers ?? {},\n });\n }\n } else {\n // Exponential backoff with full jitter to avoid thundering herd\n const cap = Math.min(backoffMs * 2 ** (attempt - 1), maxBackoffMs);\n deps.onRetry?.(envelopes[0]!, attempt, maxAttempts - 1);\n await sleep(Math.floor(Math.random() * cap));\n }\n }\n}\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Producer = KafkaJS.Producer;\ntype Consumer = KafkaJS.Consumer;\nimport {\n decodeHeaders,\n extractEnvelope,\n runWithEnvelopeContext,\n} from \"../message/envelope\";\nimport type { EventEnvelope } from \"../message/envelope\";\nimport {\n parseJsonMessage,\n validateWithSchema,\n executeWithRetry,\n sendToDlq,\n sendToDuplicatesTopic,\n buildRetryTopicPayload,\n} from \"../consumer/pipeline\";\nimport type { DuplicateMetadata } from \"../consumer/pipeline\";\nimport { HEADER_LAMPORT_CLOCK } from \"../message/envelope\";\nimport type { SchemaLike } from \"../message/topic\";\nimport type {\n BatchMeta,\n ConsumerInterceptor,\n DeduplicationOptions,\n DlqReason,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n RetryOptions,\n} from \"../types\";\n\nexport type MessageHandlerDeps = {\n logger: KafkaLogger;\n producer: Producer;\n instrumentation: KafkaInstrumentation[];\n onMessageLost: KafkaClientOptions[\"onMessageLost\"];\n onRetry?: (\n envelope: EventEnvelope<any>,\n attempt: number,\n maxRetries: number,\n ) => void;\n onDlq?: (envelope: EventEnvelope<any>, reason: DlqReason) => void;\n onDuplicate?: (\n envelope: EventEnvelope<any>,\n strategy: \"drop\" | \"dlq\" | \"topic\",\n ) => void;\n onMessage?: (envelope: EventEnvelope<any>) => void;\n};\n\n/** Active deduplication context passed from KafkaClient to the message handler. */\nexport type DeduplicationContext = {\n options: DeduplicationOptions;\n /** Mutable map: `\"topic:partition\"` → last processed Lamport clock value. */\n state: Map<string, number>;\n};\n\nexport type EachMessageOpts = {\n schemaMap: Map<string, SchemaLike>;\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>;\n interceptors: ConsumerInterceptor<any>[];\n dlq: boolean;\n retry: RetryOptions | undefined;\n retryTopics: boolean | undefined;\n timeoutMs: number | undefined;\n wrapWithTimeout: <R>(\n fn: () => Promise<R>,\n ms: number,\n topic: string,\n ) => Promise<R>;\n deduplication?: DeduplicationContext;\n /** Drop messages older than this threshold (ms). See `ConsumerOptions.messageTtlMs`. */\n messageTtlMs?: number;\n /**\n * EOS context for main consumer → retry.1 routing.\n * When set, the main consumer runs with `autoCommit: false`. On handler failure,\n * routing to the retry topic and the source offset commit are wrapped in a single\n * Kafka transaction — a crash at any point rolls back the transaction, ensuring\n * the message is not duplicated between the main topic and retry.1.\n * On success, the offset is committed manually (no transaction needed).\n */\n eosMainContext?: {\n txProducer: Producer;\n consumer: Consumer;\n };\n};\n\n/**\n * Check Lamport clock header against per-partition state.\n * Returns `true` if the message is a duplicate and should be skipped.\n * Updates the state map on a fresh message.\n */\nasync function applyDeduplication(\n envelope: EventEnvelope<any>,\n raw: string,\n dedup: DeduplicationContext,\n dlq: boolean,\n deps: MessageHandlerDeps,\n): Promise<boolean> {\n const clockRaw = envelope.headers[HEADER_LAMPORT_CLOCK];\n if (clockRaw === undefined) return false; // no clock → pass through\n\n const incomingClock = Number(clockRaw);\n if (Number.isNaN(incomingClock)) return false; // malformed header → pass through\n\n const stateKey = `${envelope.topic}:${envelope.partition}`;\n const lastProcessedClock = dedup.state.get(stateKey) ?? -1;\n\n if (incomingClock <= lastProcessedClock) {\n const meta: DuplicateMetadata = {\n incomingClock,\n lastProcessedClock,\n originalHeaders: envelope.headers,\n };\n const strategy = dedup.options.strategy ?? \"drop\";\n deps.logger.warn(\n `Duplicate message on ${envelope.topic}[${envelope.partition}]: ` +\n `clock=${incomingClock} <= last=${lastProcessedClock} — strategy=${strategy}`,\n );\n\n deps.onDuplicate?.(envelope, strategy);\n\n if (strategy === \"dlq\" && dlq) {\n const augmentedHeaders = {\n ...envelope.headers,\n \"x-dlq-reason\": \"lamport-clock-duplicate\",\n \"x-dlq-duplicate-incoming-clock\": String(incomingClock),\n \"x-dlq-duplicate-last-processed-clock\": String(lastProcessedClock),\n };\n await sendToDlq(envelope.topic, raw, deps, {\n error: new Error(\"Lamport Clock duplicate detected\"),\n attempt: 0,\n originalHeaders: augmentedHeaders,\n });\n } else if (strategy === \"topic\") {\n const destination =\n dedup.options.duplicatesTopic ?? `${envelope.topic}.duplicates`;\n await sendToDuplicatesTopic(envelope.topic, raw, destination, deps, meta);\n }\n // strategy === 'drop': already logged, nothing more to do\n\n return true; // signal: skip this message\n }\n\n dedup.state.set(stateKey, incomingClock);\n return false;\n}\n\n/** Parse, validate and extract an envelope from a single raw Kafka message. Returns null to skip. */\nasync function parseSingleMessage(\n message: {\n value: Buffer | null;\n headers?: Record<string, any>;\n offset: string;\n },\n topic: string,\n partition: number,\n schemaMap: Map<string, SchemaLike>,\n interceptors: ConsumerInterceptor<any>[],\n dlq: boolean,\n deps: MessageHandlerDeps,\n): Promise<EventEnvelope<any> | null> {\n if (!message.value) {\n deps.logger.warn(`Received empty message from topic ${topic}`);\n return null;\n }\n\n const raw = message.value.toString();\n const parsed = parseJsonMessage(raw, topic, deps.logger);\n if (parsed === null) return null;\n\n const headers = decodeHeaders(message.headers);\n const validated = await validateWithSchema(\n parsed,\n raw,\n topic,\n schemaMap,\n interceptors,\n dlq,\n { ...deps, originalHeaders: headers },\n );\n if (validated === null) return null;\n\n return extractEnvelope(validated, headers, topic, partition, message.offset);\n}\n\nexport async function handleEachMessage(\n payload: {\n topic: string;\n partition: number;\n message: {\n value: Buffer | null;\n headers?: Record<string, any>;\n offset: string;\n };\n },\n opts: EachMessageOpts,\n deps: MessageHandlerDeps,\n): Promise<void> {\n const { topic, partition, message } = payload;\n const {\n schemaMap,\n handleMessage,\n interceptors,\n dlq,\n retry,\n retryTopics,\n timeoutMs,\n wrapWithTimeout,\n } = opts;\n\n // Build EOS callbacks when the main consumer runs with autoCommit: false\n // (activated by retryTopics: true in startConsumer).\n const eos = opts.eosMainContext;\n const nextOffsetStr = (parseInt(message.offset, 10) + 1).toString();\n\n // Commit offset manually (used on skip path: empty/invalid/duplicate message).\n const commitOffset = eos\n ? async () => {\n await eos.consumer.commitOffsets([\n { topic, partition, offset: nextOffsetStr },\n ]);\n }\n : undefined;\n\n // EOS routing closure: produce to retry.1 + commit source offset atomically.\n const eosRouteToRetry =\n eos && retry\n ? async (\n rawMsgs: string[],\n envelopes: EventEnvelope<any>[],\n delay: number,\n ) => {\n const { topic: rtTopic, messages: rtMsgs } = buildRetryTopicPayload(\n topic,\n rawMsgs,\n 1,\n retry.maxRetries,\n delay,\n envelopes[0]?.headers ?? {},\n );\n const tx = await eos.txProducer.transaction();\n try {\n await tx.send({ topic: rtTopic, messages: rtMsgs });\n await tx.sendOffsets({\n consumer: eos.consumer,\n topics: [\n {\n topic,\n partitions: [{ partition, offset: nextOffsetStr }],\n },\n ],\n });\n await tx.commit();\n } catch (txErr) {\n try {\n await tx.abort();\n } catch {}\n throw txErr;\n }\n }\n : undefined;\n\n const envelope = await parseSingleMessage(\n message,\n topic,\n partition,\n schemaMap,\n interceptors,\n dlq,\n deps,\n );\n if (envelope === null) {\n await commitOffset?.();\n return;\n }\n\n if (opts.deduplication) {\n const isDuplicate = await applyDeduplication(\n envelope,\n message.value!.toString(),\n opts.deduplication,\n dlq,\n deps,\n );\n if (isDuplicate) {\n await commitOffset?.();\n return;\n }\n }\n\n if (opts.messageTtlMs !== undefined) {\n const ageMs = Date.now() - new Date(envelope.timestamp).getTime();\n if (ageMs > opts.messageTtlMs) {\n deps.logger.warn(\n `[KafkaClient] TTL expired on ${topic}: age ${ageMs}ms > ${opts.messageTtlMs}ms`,\n );\n if (dlq) {\n await sendToDlq(topic, message.value!.toString(), deps, {\n error: new Error(`Message TTL expired: age ${ageMs}ms`),\n attempt: 0,\n originalHeaders: envelope.headers,\n });\n deps.onDlq?.(envelope, \"ttl-expired\");\n } else {\n await deps.onMessageLost?.({\n topic,\n error: new Error(`TTL expired: ${ageMs}ms`),\n attempt: 0,\n headers: envelope.headers,\n });\n }\n await commitOffset?.();\n return;\n }\n }\n\n await executeWithRetry(\n () => {\n const fn = () =>\n runWithEnvelopeContext(\n {\n correlationId: envelope.correlationId,\n traceparent: envelope.traceparent,\n },\n () => handleMessage(envelope),\n );\n return timeoutMs ? wrapWithTimeout(fn, timeoutMs, topic) : fn();\n },\n {\n envelope,\n rawMessages: [message.value!.toString()],\n interceptors,\n dlq,\n retry,\n retryTopics,\n },\n { ...deps, eosRouteToRetry, eosCommitOnSuccess: commitOffset },\n );\n}\n\nexport type EachBatchOpts = {\n schemaMap: Map<string, SchemaLike>;\n handleBatch: (\n envelopes: EventEnvelope<any>[],\n meta: BatchMeta,\n ) => Promise<void>;\n interceptors: ConsumerInterceptor<any>[];\n dlq: boolean;\n retry: RetryOptions | undefined;\n retryTopics: boolean | undefined;\n timeoutMs: number | undefined;\n wrapWithTimeout: <R>(\n fn: () => Promise<R>,\n ms: number,\n topic: string,\n ) => Promise<R>;\n deduplication?: DeduplicationContext;\n /** Drop messages older than this threshold (ms). See `ConsumerOptions.messageTtlMs`. */\n messageTtlMs?: number;\n /**\n * EOS context for batch consumer → retry.1 routing.\n * When set, the batch consumer runs with `autoCommit: false`.\n * On handler failure, all messages are routed to retry.1 and the partition\n * offset is committed atomically in a single Kafka transaction.\n * On success, the offset is committed manually.\n */\n eosMainContext?: {\n txProducer: Producer;\n consumer: Consumer;\n };\n};\n\nexport async function handleEachBatch(\n payload: {\n batch: {\n topic: string;\n partition: number;\n highWatermark: string;\n messages: Array<{\n value: Buffer | null;\n headers?: Record<string, any>;\n offset: string;\n }>;\n };\n heartbeat(): Promise<void>;\n resolveOffset(offset: string): void;\n commitOffsetsIfNecessary(): Promise<void>;\n },\n opts: EachBatchOpts,\n deps: MessageHandlerDeps,\n): Promise<void> {\n const { batch, heartbeat, resolveOffset, commitOffsetsIfNecessary } = payload;\n const {\n schemaMap,\n handleBatch,\n interceptors,\n dlq,\n retry,\n retryTopics,\n timeoutMs,\n wrapWithTimeout,\n } = opts;\n\n // Build EOS callbacks when the batch consumer runs with autoCommit: false.\n // Offset to commit = last message in batch + 1 (all messages in one partition, sequential).\n const eos = opts.eosMainContext;\n const lastRawOffset =\n batch.messages.length > 0\n ? batch.messages[batch.messages.length - 1]!.offset\n : undefined;\n const batchNextOffsetStr = lastRawOffset\n ? (parseInt(lastRawOffset, 10) + 1).toString()\n : undefined;\n\n const commitBatchOffset =\n eos && batchNextOffsetStr\n ? async () => {\n await eos.consumer.commitOffsets([\n {\n topic: batch.topic,\n partition: batch.partition,\n offset: batchNextOffsetStr,\n },\n ]);\n }\n : undefined;\n\n const eosRouteToRetry =\n eos && retry && batchNextOffsetStr\n ? async (\n rawMsgs: string[],\n envelopes: EventEnvelope<any>[],\n delay: number,\n ) => {\n const { topic: rtTopic, messages: rtMsgs } = buildRetryTopicPayload(\n batch.topic,\n rawMsgs,\n 1,\n retry.maxRetries,\n delay,\n envelopes.map((e) => e.headers),\n );\n const tx = await eos.txProducer.transaction();\n try {\n await tx.send({ topic: rtTopic, messages: rtMsgs });\n await tx.sendOffsets({\n consumer: eos.consumer,\n topics: [\n {\n topic: batch.topic,\n partitions: [\n { partition: batch.partition, offset: batchNextOffsetStr },\n ],\n },\n ],\n });\n await tx.commit();\n } catch (txErr) {\n try {\n await tx.abort();\n } catch {}\n throw txErr;\n }\n }\n : undefined;\n\n const envelopes: EventEnvelope<any>[] = [];\n const rawMessages: string[] = [];\n\n for (const message of batch.messages) {\n const envelope = await parseSingleMessage(\n message,\n batch.topic,\n batch.partition,\n schemaMap,\n interceptors,\n dlq,\n deps,\n );\n if (envelope === null) continue;\n\n if (opts.deduplication) {\n const raw = message.value!.toString();\n const isDuplicate = await applyDeduplication(\n envelope,\n raw,\n opts.deduplication,\n dlq,\n deps,\n );\n if (isDuplicate) continue;\n }\n\n if (opts.messageTtlMs !== undefined) {\n const ageMs = Date.now() - new Date(envelope.timestamp).getTime();\n if (ageMs > opts.messageTtlMs) {\n deps.logger.warn(\n `[KafkaClient] TTL expired on ${batch.topic}: age ${ageMs}ms > ${opts.messageTtlMs}ms`,\n );\n if (dlq) {\n await sendToDlq(batch.topic, message.value!.toString(), deps, {\n error: new Error(`Message TTL expired: age ${ageMs}ms`),\n attempt: 0,\n originalHeaders: envelope.headers,\n });\n deps.onDlq?.(envelope, \"ttl-expired\");\n } else {\n await deps.onMessageLost?.({\n topic: batch.topic,\n error: new Error(`TTL expired: ${ageMs}ms`),\n attempt: 0,\n headers: envelope.headers,\n });\n }\n continue;\n }\n }\n\n envelopes.push(envelope);\n rawMessages.push(message.value!.toString());\n }\n\n if (envelopes.length === 0) {\n // All messages in this batch were filtered (invalid/duplicate).\n // When running EOS, commit the batch offset so the consumer advances.\n await commitBatchOffset?.();\n return;\n }\n\n const meta: BatchMeta = {\n partition: batch.partition,\n highWatermark: batch.highWatermark,\n heartbeat,\n resolveOffset,\n commitOffsetsIfNecessary,\n };\n\n await executeWithRetry(\n () => {\n const fn = () => handleBatch(envelopes, meta);\n return timeoutMs ? wrapWithTimeout(fn, timeoutMs, batch.topic) : fn();\n },\n {\n envelope: envelopes,\n rawMessages,\n interceptors,\n dlq,\n retry,\n isBatch: true,\n retryTopics,\n },\n { ...deps, eosRouteToRetry, eosCommitOnSuccess: commitBatchOffset },\n );\n}\n","import type { KafkaJS } from \"@confluentinc/kafka-javascript\";\nimport type { KafkaLogger, SubscribeRetryOptions } from \"../types\";\nimport { toError, sleep } from \"./pipeline\";\n\nexport async function subscribeWithRetry(\n consumer: KafkaJS.Consumer,\n topics: string[],\n logger: KafkaLogger,\n retryOpts?: SubscribeRetryOptions,\n): Promise<void> {\n const maxAttempts = retryOpts?.retries ?? 5;\n const backoffMs = retryOpts?.backoffMs ?? 5000;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await consumer.subscribe({ topics });\n return;\n } catch (error) {\n if (attempt === maxAttempts) throw error;\n const msg = toError(error).message;\n const delay = Math.floor(Math.random() * backoffMs);\n logger.warn(\n `Failed to subscribe to [${topics.join(\", \")}] (attempt ${attempt}/${maxAttempts}): ${msg}. Retrying in ${delay}ms...`,\n );\n await sleep(delay);\n }\n }\n}\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Consumer = KafkaJS.Consumer;\ntype Producer = KafkaJS.Producer;\nimport {\n decodeHeaders,\n extractEnvelope,\n runWithEnvelopeContext,\n} from \"../message/envelope\";\nimport type { EventEnvelope } from \"../message/envelope\";\nimport {\n parseJsonMessage,\n validateWithSchema,\n runHandlerWithPipeline,\n notifyInterceptorsOnError,\n buildDlqPayload,\n buildRetryTopicPayload,\n sleep,\n toError,\n RETRY_HEADER_AFTER,\n RETRY_HEADER_MAX_RETRIES,\n RETRY_HEADER_ORIGINAL_TOPIC,\n} from \"../consumer/pipeline\";\nimport { KafkaRetryExhaustedError } from \"../errors\";\nimport { subscribeWithRetry } from \"../consumer/subscribe-retry\";\nimport type { SchemaLike } from \"../message/topic\";\nimport type {\n ConsumerInterceptor,\n DlqReason,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n} from \"../types\";\n\nexport type RetryTopicDeps = {\n logger: KafkaLogger;\n producer: Producer;\n instrumentation: KafkaInstrumentation[];\n onMessageLost: KafkaClientOptions[\"onMessageLost\"];\n onRetry?: (\n envelope: EventEnvelope<any>,\n attempt: number,\n maxRetries: number,\n ) => void;\n onDlq?: (envelope: EventEnvelope<any>, reason: DlqReason) => void;\n onMessage?: (envelope: EventEnvelope<any>) => void;\n ensureTopic: (topic: string) => Promise<void>;\n getOrCreateConsumer: (\n groupId: string,\n fromBeginning: boolean,\n autoCommit: boolean,\n ) => Consumer;\n runningConsumers: Map<string, \"eachMessage\" | \"eachBatch\">;\n /** Factory that creates and connects a transactional producer for EOS routing. */\n createRetryTxProducer: (transactionalId: string) => Promise<Producer>;\n};\n\n/**\n * Poll `consumer.assignment()` until the consumer has received at least one\n * partition for the given topics, then return. Logs a warning and returns\n * (rather than throwing) on timeout so that a slow broker does not break\n * the caller.\n */\nexport async function waitForPartitionAssignment(\n consumer: Consumer,\n topics: string[],\n logger: KafkaLogger,\n timeoutMs = 10_000,\n): Promise<void> {\n const topicSet = new Set(topics);\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n try {\n const assigned: { topic: string; partition: number }[] =\n consumer.assignment();\n if (assigned.some((a) => topicSet.has(a.topic))) return;\n } catch {\n // consumer.assignment() throws if not yet in CONNECTED state — keep polling\n }\n await sleep(200);\n }\n logger.warn(\n `Retry consumer did not receive partition assignments for [${topics.join(\", \")}] within ${timeoutMs}ms`,\n );\n}\n\n/**\n * Start a single retry level consumer on `<topic>.retry.<level>`.\n *\n * Exactly-once routing guarantee (EOS):\n * - The partition is paused while waiting for the scheduled delay window.\n * The offset is NOT committed during this window, so a process crash\n * causes the message to be redelivered on restart.\n * - On success: offset committed directly via `consumer.commitOffsets`.\n * - On failure (routing to next level or DLQ): produce + offset-commit are\n * wrapped in a single Kafka transaction via `sendOffsetsToTransaction`.\n * A crash at any point rolls back the transaction — no duplicate is\n * produced in the next level or DLQ.\n * - If the EOS transaction itself fails (broker unavailable), the offset is\n * NOT committed and the message is redelivered, retrying the transaction.\n *\n * Flow per message:\n * 1. If `x-retry-after` is in the future: pause partition → sleep → resume.\n * 2. Validate and call the original handler.\n * 3. On success: commit offset.\n * 4. On failure:\n * - Not exhausted → EOS tx: produce to `<topic>.retry.<level+1>` + commit offset\n * - Exhausted + dlq → EOS tx: produce to `<topic>.dlq` + commit offset\n * - Exhausted, no dlq → call `onMessageLost` + commit offset directly\n */\nasync function startLevelConsumer(\n level: number,\n levelTopics: string[],\n levelGroupId: string,\n originalTopics: string[],\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n retry: { maxRetries: number; backoffMs?: number; maxBackoffMs?: number },\n dlq: boolean,\n interceptors: ConsumerInterceptor<any>[],\n schemaMap: Map<string, SchemaLike>,\n deps: RetryTopicDeps,\n assignmentTimeoutMs?: number,\n): Promise<void> {\n const {\n logger,\n producer,\n instrumentation,\n onMessageLost,\n onRetry,\n onDlq,\n onMessage,\n ensureTopic,\n getOrCreateConsumer,\n runningConsumers,\n createRetryTxProducer,\n } = deps;\n\n const backoffMs = retry.backoffMs ?? 1_000;\n const maxBackoffMs = retry.maxBackoffMs ?? 30_000;\n const pipelineDeps = { logger, producer, instrumentation, onMessageLost };\n\n for (const lt of levelTopics) {\n await ensureTopic(lt);\n }\n\n // One transactional producer per level — routes messages to the next level or DLQ\n // atomically with the consumer offset commit (EOS).\n const levelTxProducer = await createRetryTxProducer(`${levelGroupId}-tx`);\n\n // autoCommit: false — offsets are committed manually after processing.\n const consumer = getOrCreateConsumer(levelGroupId, false, false);\n await consumer.connect();\n await subscribeWithRetry(consumer, levelTopics, logger);\n\n await consumer.run({\n eachMessage: async ({ topic: levelTopic, partition, message }) => {\n const nextOffset = {\n topic: levelTopic,\n partition,\n offset: (parseInt(message.offset, 10) + 1).toString(),\n };\n\n if (!message.value) {\n await consumer.commitOffsets([nextOffset]);\n return;\n }\n\n const headers = decodeHeaders(message.headers);\n const retryAfter = parseInt(\n (headers[RETRY_HEADER_AFTER] as string | undefined) ?? \"0\",\n 10,\n );\n const remaining = retryAfter - Date.now();\n\n // Pause this partition for the scheduled delay.\n // The offset is not committed yet — a crash here causes redelivery (at-least-once).\n if (remaining > 0) {\n consumer.pause([{ topic: levelTopic, partitions: [partition] }]);\n await sleep(remaining);\n consumer.resume([{ topic: levelTopic, partitions: [partition] }]);\n }\n\n const raw = message.value.toString();\n const parsed = parseJsonMessage(raw, levelTopic, logger);\n if (parsed === null) {\n await consumer.commitOffsets([nextOffset]);\n return;\n }\n\n const currentMaxRetries = parseInt(\n (headers[RETRY_HEADER_MAX_RETRIES] as string | undefined) ??\n String(retry.maxRetries),\n 10,\n );\n const originalTopic =\n (headers[RETRY_HEADER_ORIGINAL_TOPIC] as string | undefined) ??\n levelTopic.replace(/\\.retry\\.\\d+$/, \"\");\n\n const validated = await validateWithSchema(\n parsed,\n raw,\n originalTopic,\n schemaMap,\n interceptors,\n dlq,\n { ...pipelineDeps, originalHeaders: headers },\n );\n if (validated === null) {\n await consumer.commitOffsets([nextOffset]);\n return;\n }\n\n const envelope = extractEnvelope(\n validated,\n headers,\n originalTopic,\n partition,\n message.offset,\n );\n\n const error = await runHandlerWithPipeline(\n () =>\n runWithEnvelopeContext(\n {\n correlationId: envelope.correlationId,\n traceparent: envelope.traceparent,\n },\n () => handleMessage(envelope),\n ),\n [envelope],\n interceptors,\n instrumentation,\n );\n\n if (!error) {\n onMessage?.(envelope);\n await consumer.commitOffsets([nextOffset]);\n return;\n }\n\n const exhausted = level >= currentMaxRetries;\n const reportedError =\n exhausted && currentMaxRetries > 1\n ? new KafkaRetryExhaustedError(\n originalTopic,\n [envelope.payload],\n currentMaxRetries,\n { cause: error },\n )\n : error;\n\n await notifyInterceptorsOnError([envelope], interceptors, reportedError);\n\n logger.error(\n `Retry consumer error for ${originalTopic} (level ${level}/${currentMaxRetries}):`,\n error.stack,\n );\n\n if (!exhausted) {\n const nextLevel = level + 1;\n // Exponent = current level: level 1 → delay cap = backoffMs * 2^1, etc.\n const cap = Math.min(backoffMs * 2 ** level, maxBackoffMs);\n const delay = Math.floor(Math.random() * cap);\n const { topic: rtTopic, messages: rtMsgs } = buildRetryTopicPayload(\n originalTopic,\n [raw],\n nextLevel,\n currentMaxRetries,\n delay,\n headers,\n );\n // EOS: produce to next retry level + commit source offset atomically.\n // A crash at any point rolls back the transaction — no duplicate.\n const tx = await levelTxProducer.transaction();\n try {\n await tx.send({ topic: rtTopic, messages: rtMsgs });\n await tx.sendOffsets({\n consumer,\n topics: [\n {\n topic: nextOffset.topic,\n partitions: [\n {\n partition: nextOffset.partition,\n offset: nextOffset.offset,\n },\n ],\n },\n ],\n });\n await tx.commit();\n logger.warn(\n `Message routed to ${rtTopic} (EOS, level ${nextLevel}/${currentMaxRetries})`,\n );\n onRetry?.(envelope, nextLevel, currentMaxRetries);\n } catch (txErr) {\n try {\n await tx.abort();\n } catch {}\n logger.error(\n `EOS routing to ${rtTopic} failed — message will be redelivered:`,\n toError(txErr).stack,\n );\n // Don't commit offset — message is redelivered and the tx is retried.\n return;\n }\n } else if (dlq) {\n const { topic: dTopic, messages: dMsgs } = buildDlqPayload(\n originalTopic,\n raw,\n {\n error,\n // +1 to account for the main consumer's initial attempt before routing.\n attempt: level + 1,\n originalHeaders: headers,\n },\n );\n // EOS: produce to DLQ + commit source offset atomically.\n const tx = await levelTxProducer.transaction();\n try {\n await tx.send({ topic: dTopic, messages: dMsgs });\n await tx.sendOffsets({\n consumer,\n topics: [\n {\n topic: nextOffset.topic,\n partitions: [\n {\n partition: nextOffset.partition,\n offset: nextOffset.offset,\n },\n ],\n },\n ],\n });\n await tx.commit();\n logger.warn(`Message sent to DLQ: ${dTopic} (EOS)`);\n onDlq?.(envelope, \"handler-error\");\n } catch (txErr) {\n try {\n await tx.abort();\n } catch {}\n logger.error(\n `EOS DLQ routing to ${dTopic} failed — message will be redelivered:`,\n toError(txErr).stack,\n );\n // Don't commit offset — message stays in retry chain, DLQ tx retried on next delivery.\n return;\n }\n } else {\n // No DLQ — notify caller and commit offset directly (no routing produce, no EOS needed).\n await onMessageLost?.({\n topic: originalTopic,\n error,\n attempt: level,\n headers,\n });\n await consumer.commitOffsets([nextOffset]);\n }\n },\n });\n\n runningConsumers.set(levelGroupId, \"eachMessage\");\n\n await waitForPartitionAssignment(\n consumer,\n levelTopics,\n logger,\n assignmentTimeoutMs,\n );\n\n logger.log(\n `Retry level ${level}/${retry.maxRetries} consumer started for: ${originalTopics.join(\", \")} (group: ${levelGroupId})`,\n );\n}\n\n/**\n * Start one consumer per retry level on `<topic>.retry.<level>` topics.\n *\n * With `maxRetries: N`, creates N consumers:\n * - `${groupId}-retry.1` → `<topic>.retry.1`\n * - `${groupId}-retry.2` → `<topic>.retry.2`\n * - …\n * - `${groupId}-retry.N` → `<topic>.retry.N`\n *\n * Each level consumer uses pause/sleep/resume to honour the scheduled delay\n * without committing the offset early. Failed message routing (to the next\n * level or DLQ) is wrapped in a Kafka transaction via `sendOffsetsToTransaction`\n * for exactly-once routing semantics — no duplicates even if the process\n * crashes between the produce and the offset commit.\n *\n * Returns the list of started consumer group IDs so the caller can stop\n * them selectively via `stopConsumer`.\n */\nexport async function startRetryTopicConsumers(\n originalTopics: string[],\n originalGroupId: string,\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n retry: { maxRetries: number; backoffMs?: number; maxBackoffMs?: number },\n dlq: boolean,\n interceptors: ConsumerInterceptor<any>[],\n schemaMap: Map<string, SchemaLike>,\n deps: RetryTopicDeps,\n assignmentTimeoutMs?: number,\n): Promise<string[]> {\n const levelGroupIds: string[] = [];\n\n for (let level = 1; level <= retry.maxRetries; level++) {\n const levelTopics = originalTopics.map((t) => `${t}.retry.${level}`);\n const levelGroupId = `${originalGroupId}-retry.${level}`;\n\n await startLevelConsumer(\n level,\n levelTopics,\n levelGroupId,\n originalTopics,\n handleMessage,\n retry,\n dlq,\n interceptors,\n schemaMap,\n deps,\n assignmentTimeoutMs,\n );\n\n levelGroupIds.push(levelGroupId);\n }\n\n return levelGroupIds;\n}\n","import type { MessageHeaders } from \"../types\";\n\n/**\n * Context passed as the second argument to `SchemaLike.parse()`.\n * Enables schema-registry adapters, version-aware migration, and\n * header-driven parsing without coupling validators to Kafka internals.\n *\n * All fields are optional-friendly — validators that don't need the context\n * can simply ignore the second argument.\n */\nexport interface SchemaParseContext {\n /** Topic the message was produced to / consumed from. */\n topic: string;\n /** Decoded message headers (envelope headers included). */\n headers: MessageHeaders;\n /** Value of the `x-schema-version` header, defaults to `1`. */\n version: number;\n}\n\n/**\n * Any validation library with a `.parse()` method.\n * Works with Zod, Valibot, ArkType, or any custom validator.\n *\n * The optional `ctx` argument carries topic/header/version metadata so\n * validators can perform schema-registry lookups or version-aware migrations.\n * Existing validators that only use the first argument continue to work\n * unchanged — the second argument is silently ignored.\n *\n * @example\n * ```ts\n * import { z } from 'zod';\n * const schema: SchemaLike<{ id: string }> = z.object({ id: z.string() });\n *\n * // Context-aware validator:\n * const schema: SchemaLike<MyType> = {\n * parse(data, ctx) {\n * const version = ctx?.version ?? 1;\n * return version >= 2 ? migrateV1toV2(data) : validateV1(data);\n * }\n * };\n * ```\n */\nexport interface SchemaLike<T = any> {\n parse(data: unknown, ctx?: SchemaParseContext): T | Promise<T>;\n}\n\n/** Infer the output type from a SchemaLike. */\nexport type InferSchema<S extends SchemaLike> =\n S extends SchemaLike<infer T> ? T : never;\n\n/**\n * A typed topic descriptor that pairs a topic name with its message type.\n * Created via the `topic()` factory function.\n *\n * @typeParam N - The literal topic name string.\n * @typeParam M - The message payload type for this topic.\n */\nexport interface TopicDescriptor<\n N extends string = string,\n M extends Record<string, any> = Record<string, any>,\n> {\n readonly __topic: N;\n /** @internal Phantom type — never has a real value at runtime. */\n readonly __type: M;\n /** Runtime schema validator. Present only when created via `topic().schema()`. */\n readonly __schema?: SchemaLike<M>;\n}\n\n/**\n * Define a typed topic descriptor.\n *\n * @example\n * ```ts\n * // Without schema — explicit type via .type<T>():\n * const OrderCreated = topic('order.created').type<{ orderId: string; amount: number }>();\n *\n * // With schema — type inferred from schema:\n * const OrderCreated = topic('order.created').schema(z.object({\n * orderId: z.string(),\n * amount: z.number(),\n * }));\n *\n * // Use with KafkaClient:\n * await kafka.sendMessage(OrderCreated, { orderId: '123', amount: 100 });\n *\n * // Use with @SubscribeTo:\n * @SubscribeTo(OrderCreated)\n * async handleOrder(msg) { ... }\n * ```\n */\nexport function topic<N extends string>(name: N) {\n return {\n /** Provide an explicit message type without a runtime schema. */\n type: <M extends Record<string, any>>(): TopicDescriptor<N, M> => ({\n __topic: name,\n __type: undefined as unknown as M,\n }),\n\n schema: <S extends SchemaLike<Record<string, any>>>(\n schema: S,\n ): TopicDescriptor<N, InferSchema<S>> => ({\n __topic: name,\n __type: undefined as unknown as InferSchema<S>,\n __schema: schema as unknown as SchemaLike<InferSchema<S>>,\n }),\n };\n}\n\n/**\n * Build a topic-message map type from a union of TopicDescriptors.\n *\n * @example\n * ```ts\n * const OrderCreated = topic('order.created').type<{ orderId: string }>();\n * const OrderCompleted = topic('order.completed').type<{ completedAt: string }>();\n *\n * type MyTopics = TopicsFrom<typeof OrderCreated | typeof OrderCompleted>;\n * // { 'order.created': { orderId: string }; 'order.completed': { completedAt: string } }\n * ```\n */\nexport type TopicsFrom<D extends TopicDescriptor<any, any>> = {\n [K in D as K[\"__topic\"]]: K[\"__type\"];\n};\n"],"mappings":"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","names":["topic","topic","topic","topic","topic","envelopes","getOrCreateConsumer","topic"]}
1
+ {"version":3,"sources":["../src/core.ts","../src/client/kafka.client/index.ts","../src/client/message/envelope.ts","../src/client/errors.ts","../src/client/kafka.client/producer-ops.ts","../src/client/kafka.client/consumer-ops.ts","../src/client/consumer/pipeline.ts","../src/client/kafka.client/message-handler.ts","../src/client/consumer/subscribe-retry.ts","../src/client/kafka.client/retry-topic.ts","../src/client/message/topic.ts"],"sourcesContent":["export * from \"./client/kafka.client\";\nexport * from \"./client/message/topic\";\nexport * from \"./client/errors\";\nexport * from \"./client/message/envelope\";\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Kafka = KafkaJS.Kafka;\ntype Producer = KafkaJS.Producer;\ntype Consumer = KafkaJS.Consumer;\ntype Admin = KafkaJS.Admin;\nconst { Kafka: KafkaClass, logLevel: KafkaLogLevel } = KafkaJS;\nimport { TopicDescriptor, SchemaLike } from \"../message/topic\";\nimport type { EventEnvelope } from \"../message/envelope\";\nimport type {\n ClientId,\n GroupId,\n SendOptions,\n BatchMessageItem,\n BatchSendOptions,\n ConsumerOptions,\n ConsumerHandle,\n TransactionContext,\n TopicMapConstraint,\n IKafkaClient,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n KafkaMetrics,\n DlqReason,\n BatchMeta,\n DlqReplayOptions,\n CircuitBreakerOptions,\n ConsumerGroupSummary,\n TopicDescription,\n MessageHeaders,\n} from \"../types\";\n\n/**\n * Process-level registry of active transactional producer IDs.\n * Used to detect same-process `transactionalId` conflicts before Kafka silently\n * fences one of the producers. Cross-process conflicts cannot be detected here —\n * they surface as fencing errors from the broker.\n */\nconst _activeTransactionalIds = new Set<string>();\n\n// Re-export all types so existing `import { ... } from './kafka.client'` keeps working\nexport * from \"../types\";\n\nimport {\n buildSendPayload,\n registerSchema,\n resolveTopicName,\n type BuildSendPayloadDeps,\n} from \"./producer-ops\";\nimport {\n getOrCreateConsumer,\n buildSchemaMap,\n type ConsumerOpsDeps,\n} from \"./consumer-ops\";\nimport { handleEachMessage, handleEachBatch } from \"./message-handler\";\nimport type {\n MessageHandlerDeps,\n DeduplicationContext,\n} from \"./message-handler\";\nimport { startRetryTopicConsumers } from \"./retry-topic\";\nimport { subscribeWithRetry } from \"../consumer/subscribe-retry\";\nimport { toError } from \"../consumer/pipeline\";\nimport { decodeHeaders } from \"../message/envelope\";\n\n/** Push-to-pull async queue used by consume() to bridge Kafka's push model to AsyncIterableIterator. */\nclass AsyncQueue<V> {\n private readonly items: V[] = [];\n private readonly waiting: Array<{\n resolve: (r: IteratorResult<V>) => void;\n reject: (err: Error) => void;\n }> = [];\n private closed = false;\n private error?: Error;\n private paused = false;\n\n constructor(\n private readonly highWaterMark = Infinity,\n private readonly onFull: () => void = () => {},\n private readonly onDrained: () => void = () => {},\n ) {}\n\n push(item: V): void {\n if (this.waiting.length > 0) {\n this.waiting.shift()!.resolve({ value: item, done: false });\n } else {\n this.items.push(item);\n if (!this.paused && this.items.length >= this.highWaterMark) {\n this.paused = true;\n this.onFull();\n }\n }\n }\n\n fail(err: Error): void {\n this.closed = true;\n this.error = err;\n for (const { reject } of this.waiting.splice(0)) reject(err);\n }\n\n close(): void {\n this.closed = true;\n for (const { resolve } of this.waiting.splice(0))\n resolve({ value: undefined as any, done: true });\n }\n\n next(): Promise<IteratorResult<V>> {\n if (this.error) return Promise.reject(this.error);\n if (this.items.length > 0) {\n const value = this.items.shift()!;\n if (\n this.paused &&\n this.items.length <= Math.floor(this.highWaterMark / 2)\n ) {\n this.paused = false;\n this.onDrained();\n }\n return Promise.resolve({ value, done: false });\n }\n if (this.closed) return Promise.resolve({ value: undefined as any, done: true });\n return new Promise((resolve, reject) => this.waiting.push({ resolve, reject }));\n }\n}\n\n/**\n * Type-safe Kafka client.\n * Wraps @confluentinc/kafka-javascript (librdkafka) with JSON serialization,\n * retries, DLQ, transactions, and interceptors.\n *\n * @typeParam T - Topic-to-message type mapping for compile-time safety.\n */\nexport class KafkaClient<\n T extends TopicMapConstraint<T>,\n> implements IKafkaClient<T> {\n private readonly kafka: Kafka;\n private readonly producer: Producer;\n private txProducer: Producer | undefined;\n private txProducerInitPromise: Promise<Producer> | undefined;\n /** Maps transactionalId → Producer for each active retry level consumer. */\n private readonly retryTxProducers = new Map<string, Producer>();\n private readonly consumers = new Map<string, Consumer>();\n private readonly admin: Admin;\n private readonly logger: KafkaLogger;\n private readonly autoCreateTopicsEnabled: boolean;\n private readonly strictSchemasEnabled: boolean;\n private readonly numPartitions: number;\n private readonly ensuredTopics = new Set<string>();\n /** Pending topic-creation promises keyed by topic name. Prevents duplicate createTopics calls. */\n private readonly ensureTopicPromises = new Map<string, Promise<void>>();\n private readonly defaultGroupId: string;\n private readonly schemaRegistry = new Map<string, SchemaLike>();\n private readonly runningConsumers = new Map<\n string,\n \"eachMessage\" | \"eachBatch\"\n >();\n private readonly consumerCreationOptions = new Map<\n string,\n { fromBeginning: boolean; autoCommit: boolean }\n >();\n /** Maps each main consumer groupId to its companion retry level groupIds. */\n private readonly companionGroupIds = new Map<string, string[]>();\n private readonly instrumentation: KafkaInstrumentation[];\n private readonly onMessageLost: KafkaClientOptions[\"onMessageLost\"];\n private readonly onTtlExpired: KafkaClientOptions[\"onTtlExpired\"];\n private readonly onRebalance: KafkaClientOptions[\"onRebalance\"];\n /** Transactional producer ID — configurable via `KafkaClientOptions.transactionalId`. */\n private readonly txId: string;\n /** Per-topic event counters, lazily created on first event. Aggregated by `getMetrics()`. */\n private readonly _topicMetrics = new Map<string, KafkaMetrics>();\n\n /** Monotonically increasing Lamport clock stamped on every outgoing message. */\n private _lamportClock = 0;\n /** Per-groupId deduplication state: `\"topic:partition\"` → last processed clock. */\n private readonly dedupStates = new Map<string, Map<string, number>>();\n\n /** Circuit breaker state per `\"${gid}:${topic}:${partition}\"` key. */\n private readonly circuitStates = new Map<\n string,\n {\n status: \"closed\" | \"open\" | \"half-open\";\n window: boolean[];\n successes: number;\n timer?: ReturnType<typeof setTimeout>;\n }\n >();\n /** Circuit breaker config per groupId, set at startConsumer/startBatchConsumer time. */\n private readonly circuitConfigs = new Map<string, CircuitBreakerOptions>();\n\n private isAdminConnected = false;\n private inFlightTotal = 0;\n private readonly drainResolvers: Array<() => void> = [];\n public readonly clientId: ClientId;\n\n constructor(\n clientId: ClientId,\n groupId: GroupId,\n brokers: string[],\n options?: KafkaClientOptions,\n ) {\n this.clientId = clientId;\n this.defaultGroupId = groupId;\n this.logger = options?.logger ?? {\n log: (msg) => console.log(`[KafkaClient:${clientId}] ${msg}`),\n warn: (msg, ...args) =>\n console.warn(`[KafkaClient:${clientId}] ${msg}`, ...args),\n error: (msg, ...args) =>\n console.error(`[KafkaClient:${clientId}] ${msg}`, ...args),\n debug: (msg, ...args) =>\n console.debug(`[KafkaClient:${clientId}] ${msg}`, ...args),\n };\n this.autoCreateTopicsEnabled = options?.autoCreateTopics ?? false;\n this.strictSchemasEnabled = options?.strictSchemas ?? true;\n this.numPartitions = options?.numPartitions ?? 1;\n this.instrumentation = options?.instrumentation ?? [];\n this.onMessageLost = options?.onMessageLost;\n this.onTtlExpired = options?.onTtlExpired;\n this.onRebalance = options?.onRebalance;\n this.txId = options?.transactionalId ?? `${clientId}-tx`;\n\n this.kafka = new KafkaClass({\n kafkaJS: {\n clientId: this.clientId,\n brokers,\n logLevel: KafkaLogLevel.ERROR,\n },\n });\n this.producer = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n },\n });\n this.admin = this.kafka.admin();\n }\n\n // ── Send ─────────────────────────────────────────────────────────\n\n /**\n * Send a single typed message. Accepts a topic key or a `TopicDescriptor`.\n *\n * @param topic Topic key from the `TopicMessageMap` or a `TopicDescriptor` object.\n * @param message Message payload — validated against the topic schema when one is registered.\n * @param options Optional per-send settings: `key`, `headers`, `correlationId`, `compression`, etc.\n */\n public async sendMessage<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(descriptor: D, message: D[\"__type\"], options?: SendOptions): Promise<void>;\n public async sendMessage<K extends keyof T>(\n topic: K,\n message: T[K],\n options?: SendOptions,\n ): Promise<void>;\n public async sendMessage(\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ): Promise<void> {\n const payload = await this.preparePayload(\n topicOrDesc,\n [\n {\n value: message,\n key: options.key,\n headers: options.headers,\n correlationId: options.correlationId,\n schemaVersion: options.schemaVersion,\n eventId: options.eventId,\n },\n ],\n options.compression,\n );\n await this.producer.send(payload);\n this.notifyAfterSend(payload.topic, payload.messages.length);\n }\n\n /**\n * Send a null-value (tombstone) message. Used with log-compacted topics to signal\n * that a key's record should be removed during the next compaction cycle.\n *\n * Tombstones skip envelope headers, schema validation, and Lamport clock stamping.\n * Both `beforeSend` and `afterSend` instrumentation hooks are still called so tracing works correctly.\n *\n * @param topic Topic name.\n * @param key Partition key identifying the record to tombstone.\n * @param headers Optional custom Kafka headers.\n */\n public async sendTombstone(\n topic: string,\n key: string,\n headers?: MessageHeaders,\n ): Promise<void> {\n const hdrs: MessageHeaders = { ...headers };\n for (const inst of this.instrumentation) {\n inst.beforeSend?.(topic, hdrs);\n }\n await this.ensureTopic(topic);\n await this.producer.send({\n topic,\n messages: [{ value: null, key, headers: hdrs }],\n });\n for (const inst of this.instrumentation) {\n inst.afterSend?.(topic);\n }\n }\n\n /**\n * Send multiple typed messages in a single Kafka produce request. Accepts a topic key or a `TopicDescriptor`.\n *\n * Each item in `messages` can carry its own `key`, `headers`, `correlationId`, and `schemaVersion`.\n * The `key` is used for partition routing — messages with the same key always land on the same partition.\n *\n * @param topic Topic key from the `TopicMessageMap` or a `TopicDescriptor` object.\n * @param messages Array of messages to send.\n * @param options Optional batch-level settings: `compression` codec.\n */\n public async sendBatch<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n descriptor: D,\n messages: Array<BatchMessageItem<D[\"__type\"]>>,\n options?: BatchSendOptions,\n ): Promise<void>;\n public async sendBatch<K extends keyof T>(\n topic: K,\n messages: Array<BatchMessageItem<T[K]>>,\n options?: BatchSendOptions,\n ): Promise<void>;\n public async sendBatch(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n options?: BatchSendOptions,\n ): Promise<void> {\n const payload = await this.preparePayload(\n topicOrDesc,\n messages,\n options?.compression,\n );\n await this.producer.send(payload);\n this.notifyAfterSend(payload.topic, payload.messages.length);\n }\n\n /** Execute multiple sends atomically. Commits on success, aborts on error. */\n public async transaction(\n fn: (ctx: TransactionContext<T>) => Promise<void>,\n ): Promise<void> {\n if (!this.txProducerInitPromise) {\n // Guarded by a promise (not just a flag) so that concurrent transaction()\n // calls do not each create their own producer — only the first call creates\n // the promise; subsequent concurrent calls await the same promise.\n // On connect failure the promise is cleared so the next call can retry.\n if (_activeTransactionalIds.has(this.txId)) {\n this.logger.warn(\n `transactionalId \"${this.txId}\" is already in use by another KafkaClient in this process. ` +\n `Kafka will fence one of the producers. ` +\n `Set a unique \\`transactionalId\\` (or distinct \\`clientId\\`) per instance.`,\n );\n }\n const initPromise: Promise<Producer> = (async () => {\n const p = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n idempotent: true,\n transactionalId: this.txId,\n maxInFlightRequests: 1,\n },\n });\n await p.connect();\n _activeTransactionalIds.add(this.txId);\n return p;\n })();\n this.txProducerInitPromise = initPromise.catch((err) => {\n this.txProducerInitPromise = undefined;\n throw err;\n });\n }\n this.txProducer = await this.txProducerInitPromise;\n const tx = await this.txProducer.transaction();\n try {\n const ctx: TransactionContext<T> = {\n send: async (\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ) => {\n const payload = await this.preparePayload(topicOrDesc, [\n {\n value: message,\n key: options.key,\n headers: options.headers,\n correlationId: options.correlationId,\n schemaVersion: options.schemaVersion,\n eventId: options.eventId,\n },\n ]);\n await tx.send(payload);\n this.notifyAfterSend(payload.topic, payload.messages.length);\n },\n /**\n * Send multiple messages in a single call to the topic.\n * All messages in the batch will be sent atomically.\n * If any message fails to send, the entire batch will be aborted.\n * @param topicOrDesc - topic name or TopicDescriptor\n * @param messages - array of messages to send with optional key, headers, correlationId, schemaVersion, and eventId\n */\n sendBatch: async (\n topicOrDesc: any,\n messages: BatchMessageItem<any>[],\n ) => {\n const payload = await this.preparePayload(topicOrDesc, messages);\n await tx.send(payload);\n this.notifyAfterSend(payload.topic, payload.messages.length);\n },\n };\n await fn(ctx);\n await tx.commit();\n } catch (error) {\n try {\n await tx.abort();\n } catch (abortError) {\n this.logger.error(\n \"Failed to abort transaction:\",\n toError(abortError).message,\n );\n }\n throw error;\n }\n }\n\n // ── Producer lifecycle ───────────────────────────────────────────\n\n /**\n * Connect the idempotent producer. Called automatically by `KafkaModule.register()`.\n * @internal Not part of `IKafkaClient` — use `disconnect()` for full teardown.\n */\n public async connectProducer(): Promise<void> {\n await this.producer.connect();\n this.logger.log(\"Producer connected\");\n }\n\n /**\n * @internal Not part of `IKafkaClient` — use `disconnect()` for full teardown.\n */\n public async disconnectProducer(): Promise<void> {\n await this.producer.disconnect();\n this.logger.log(\"Producer disconnected\");\n }\n\n // ── Consumer: eachMessage ────────────────────────────────────────\n\n /**\n * Subscribe to one or more topics and start consuming messages one at a time.\n *\n * Each message is delivered to `handleMessage` as a fully-decoded `EventEnvelope`.\n * The call blocks until the consumer is connected and the subscription is set up,\n * then returns a `ConsumerHandle` with a `stop()` method for clean shutdown.\n *\n * @param topics Array of topic keys, `TopicDescriptor` objects, or `RegExp` patterns.\n * Regex patterns cannot be combined with `retryTopics: true`.\n * @param handleMessage Async handler called for every message. Throw to trigger retries.\n * @param options Consumer configuration — `groupId`, `retry`, `dlq`, `circuitBreaker`, etc.\n * @returns A handle with `{ groupId, stop() }` for managing the consumer lifecycle.\n */\n public async startConsumer<K extends Array<keyof T>>(\n topics: K,\n handleMessage: (envelope: EventEnvelope<T[K[number]]>) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<ConsumerHandle>;\n public async startConsumer<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n topics: D[],\n handleMessage: (envelope: EventEnvelope<D[\"__type\"]>) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<ConsumerHandle>;\n public async startConsumer(\n topics: any[],\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<ConsumerHandle> {\n if (options.retryTopics && !options.retry) {\n throw new Error(\n \"retryTopics requires retry to be configured — set retry.maxRetries to enable the retry topic chain\",\n );\n }\n const hasRegexTopics = topics.some((t) => t instanceof RegExp);\n if (options.retryTopics && hasRegexTopics) {\n throw new Error(\n \"retryTopics is incompatible with regex topic patterns — retry topics require a fixed topic name to build the retry chain.\",\n );\n }\n\n // When retryTopics is enabled, the main consumer must run with autoCommit: false\n // so that offset commits can be coordinated with EOS routing transactions.\n const setupOptions = options.retryTopics\n ? { ...options, autoCommit: false as const }\n : options;\n\n const { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachMessage\", setupOptions);\n\n if (options.circuitBreaker)\n this.circuitConfigs.set(gid, options.circuitBreaker);\n const deps = this.messageDepsFor(gid);\n const timeoutMs = options.handlerTimeoutMs;\n const deduplication = this.resolveDeduplicationContext(\n gid,\n options.deduplication,\n );\n\n // Create EOS transactional producer for atomic main → retry.1 routing.\n let eosMainContext:\n | import(\"./message-handler\").EachMessageOpts[\"eosMainContext\"]\n | undefined;\n if (options.retryTopics && retry) {\n const mainTxId = `${gid}-main-tx`;\n const txProducer = await this.createRetryTxProducer(mainTxId);\n eosMainContext = { txProducer, consumer };\n }\n\n await consumer.run({\n eachMessage: (payload) =>\n this.trackInFlight(() =>\n handleEachMessage(\n payload,\n {\n schemaMap,\n handleMessage,\n interceptors,\n dlq,\n retry,\n retryTopics: options.retryTopics,\n timeoutMs,\n wrapWithTimeout: this.wrapWithTimeoutWarning.bind(this),\n deduplication,\n messageTtlMs: options.messageTtlMs,\n onTtlExpired: options.onTtlExpired,\n eosMainContext,\n },\n deps,\n ),\n ),\n });\n\n this.runningConsumers.set(gid, \"eachMessage\");\n\n if (options.retryTopics && retry) {\n if (!this.autoCreateTopicsEnabled) {\n await this.validateRetryTopicsExist(topicNames, retry.maxRetries);\n }\n const companions = await startRetryTopicConsumers(\n topicNames,\n gid,\n handleMessage,\n retry,\n dlq,\n interceptors,\n schemaMap,\n this.retryTopicDeps,\n options.retryTopicAssignmentTimeoutMs,\n );\n this.companionGroupIds.set(gid, companions);\n }\n\n return { groupId: gid, stop: () => this.stopConsumer(gid) };\n }\n\n // ── Consumer: eachBatch ──────────────────────────────────────────\n\n /**\n * Subscribe to one or more topics and consume messages in batches.\n *\n * `handleBatch` receives an array of decoded `EventEnvelope` objects together with\n * batch metadata (topic, partition, high-watermark offset). Prefer this over\n * `startConsumer` when throughput matters more than per-message latency.\n *\n * Set `autoCommit: false` in options when the handler calls `resolveOffset()` or\n * `commitOffsetsIfNecessary()` directly, to avoid offset conflicts.\n *\n * @param topics Array of topic keys, `TopicDescriptor` objects, or `RegExp` patterns.\n * @param handleBatch Async handler called with each batch of decoded messages.\n * @param options Consumer configuration — `groupId`, `retry`, `dlq`, `autoCommit`, etc.\n * @returns A handle with `{ groupId, stop() }` for managing the consumer lifecycle.\n */\n public async startBatchConsumer<K extends Array<keyof T>>(\n topics: K,\n handleBatch: (\n envelopes: EventEnvelope<T[K[number]]>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<ConsumerHandle>;\n public async startBatchConsumer<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n topics: D[],\n handleBatch: (\n envelopes: EventEnvelope<D[\"__type\"]>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<ConsumerHandle>;\n public async startBatchConsumer(\n topics: any[],\n handleBatch: (\n envelopes: EventEnvelope<any>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<ConsumerHandle> {\n if (options.retryTopics && !options.retry) {\n throw new Error(\n \"retryTopics requires retry to be configured — set retry.maxRetries to enable the retry topic chain\",\n );\n }\n const hasRegexTopics = topics.some((t) => t instanceof RegExp);\n if (options.retryTopics && hasRegexTopics) {\n throw new Error(\n \"retryTopics is incompatible with regex topic patterns — retry topics require a fixed topic name to build the retry chain.\",\n );\n }\n\n if (options.retryTopics) {\n // When retryTopics is enabled, autoCommit: false is required for EOS routing.\n // Suppress the manual-offset diagnostic that would otherwise fire.\n } else if (options.autoCommit !== false) {\n this.logger.debug?.(\n `startBatchConsumer: autoCommit is enabled (default true). ` +\n `If your handler calls resolveOffset() or commitOffsetsIfNecessary(), set autoCommit: false to avoid offset conflicts.`,\n );\n }\n\n const setupOptions = options.retryTopics\n ? { ...options, autoCommit: false as const }\n : options;\n\n const { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachBatch\", setupOptions);\n\n if (options.circuitBreaker)\n this.circuitConfigs.set(gid, options.circuitBreaker);\n const deps = this.messageDepsFor(gid);\n const timeoutMs = options.handlerTimeoutMs;\n const deduplication = this.resolveDeduplicationContext(\n gid,\n options.deduplication,\n );\n\n let eosMainContext:\n | import(\"./message-handler\").EachBatchOpts[\"eosMainContext\"]\n | undefined;\n if (options.retryTopics && retry) {\n const mainTxId = `${gid}-main-tx`;\n const txProducer = await this.createRetryTxProducer(mainTxId);\n eosMainContext = { txProducer, consumer };\n }\n\n await consumer.run({\n /**\n * eachBatch: called by the consumer for each batch of messages.\n * Called with the `payload` argument, which is an object containing the\n * batch of messages and a `BatchMeta` object with offset management controls.\n *\n * The function is wrapped with `trackInFlight` and `handleEachBatch` to provide\n * error handling and offset management.\n *\n * @param payload - an object containing the batch of messages and a `BatchMeta` object.\n */\n eachBatch: (payload) =>\n this.trackInFlight(() =>\n handleEachBatch(\n payload,\n {\n schemaMap,\n handleBatch,\n interceptors,\n dlq,\n retry,\n retryTopics: options.retryTopics,\n timeoutMs,\n wrapWithTimeout: this.wrapWithTimeoutWarning.bind(this),\n deduplication,\n messageTtlMs: options.messageTtlMs,\n onTtlExpired: options.onTtlExpired,\n eosMainContext,\n },\n deps,\n ),\n ),\n });\n\n this.runningConsumers.set(gid, \"eachBatch\");\n\n if (options.retryTopics && retry) {\n if (!this.autoCreateTopicsEnabled) {\n await this.validateRetryTopicsExist(topicNames, retry.maxRetries);\n }\n // Wrap batch handler as single-message handler for retry consumers.\n // Retry consumers use eachMessage (not eachBatch), so the broker\n // high-watermark is not available. `highWatermark` is set to `null` —\n // handlers must guard against null before doing lag calculations.\n const handleMessageForRetry = (env: EventEnvelope<any>) =>\n handleBatch([env], {\n partition: env.partition,\n highWatermark: null,\n heartbeat: async () => {},\n resolveOffset: () => {},\n commitOffsetsIfNecessary: async () => {},\n });\n const companions = await startRetryTopicConsumers(\n topicNames,\n gid,\n handleMessageForRetry,\n retry,\n dlq,\n interceptors,\n schemaMap,\n this.retryTopicDeps,\n options.retryTopicAssignmentTimeoutMs,\n );\n this.companionGroupIds.set(gid, companions);\n }\n\n return { groupId: gid, stop: () => this.stopConsumer(gid) };\n }\n\n /**\n * Consume messages from a topic as an AsyncIterableIterator.\n * Use with `for await` — breaking out of the loop automatically stops the consumer.\n *\n * @example\n * for await (const envelope of kafka.consume('my.topic')) {\n * console.log(envelope.data);\n * }\n */\n public consume<K extends keyof T & string>(\n topic: K,\n options?: ConsumerOptions<T>,\n ): AsyncIterableIterator<EventEnvelope<T[K]>> {\n if (options?.retryTopics) {\n throw new Error(\n \"consume() does not support retryTopics (EOS retry chains). \" +\n \"Use startConsumer() with retryTopics: true for guaranteed retry delivery.\",\n );\n }\n const gid = options?.groupId ?? this.defaultGroupId;\n const queue = new AsyncQueue<EventEnvelope<T[K]>>(\n options?.queueHighWaterMark,\n () => this.pauseTopicAllPartitions(gid, topic as string),\n () => this.resumeTopicAllPartitions(gid, topic as string),\n );\n const handlePromise = this.startConsumer(\n [topic as any],\n async (envelope) => {\n queue.push(envelope as EventEnvelope<T[K]>);\n },\n options,\n );\n handlePromise.catch((err: Error) => queue.fail(err));\n return {\n [Symbol.asyncIterator]() {\n return this;\n },\n next: () => queue.next(),\n return: async () => {\n queue.close();\n const handle = await handlePromise;\n await handle.stop();\n return { value: undefined as any, done: true as const };\n },\n };\n }\n\n // ── Consumer lifecycle ───────────────────────────────────────────\n\n /**\n * Stop all consumers or a specific group.\n *\n * If `groupId` is unspecified, all active consumers are stopped.\n * If `groupId` is specified, only the consumer with that group ID is stopped.\n *\n * @throws {Error} if the consumer fails to disconnect.\n */\n public async stopConsumer(groupId?: string): Promise<void> {\n if (groupId !== undefined) {\n const consumer = this.consumers.get(groupId);\n if (!consumer) {\n this.logger.warn(\n `stopConsumer: no active consumer for group \"${groupId}\"`,\n );\n return;\n }\n await consumer\n .disconnect()\n .catch((e) =>\n this.logger.warn(\n `Error disconnecting consumer \"${groupId}\":`,\n toError(e).message,\n ),\n );\n this.consumers.delete(groupId);\n this.runningConsumers.delete(groupId);\n this.consumerCreationOptions.delete(groupId);\n this.dedupStates.delete(groupId);\n // Clean up circuit breaker state for this group\n for (const key of [...this.circuitStates.keys()]) {\n if (key.startsWith(`${groupId}:`)) {\n clearTimeout(this.circuitStates.get(key)!.timer);\n this.circuitStates.delete(key);\n }\n }\n this.circuitConfigs.delete(groupId);\n this.logger.log(`Consumer disconnected: group \"${groupId}\"`);\n\n // Clean up the main consumer's EOS tx producer (present when retryTopics: true)\n const mainTxId = `${groupId}-main-tx`;\n const mainTxProducer = this.retryTxProducers.get(mainTxId);\n if (mainTxProducer) {\n await mainTxProducer\n .disconnect()\n .catch((e) =>\n this.logger.warn(\n `Error disconnecting main tx producer \"${mainTxId}\":`,\n toError(e).message,\n ),\n );\n _activeTransactionalIds.delete(mainTxId);\n this.retryTxProducers.delete(mainTxId);\n }\n\n // Stop all companion retry level consumers and their tx producers\n const companions = this.companionGroupIds.get(groupId) ?? [];\n for (const cGroupId of companions) {\n const cConsumer = this.consumers.get(cGroupId);\n if (cConsumer) {\n await cConsumer\n .disconnect()\n .catch((e) =>\n this.logger.warn(\n `Error disconnecting retry consumer \"${cGroupId}\":`,\n toError(e).message,\n ),\n );\n this.consumers.delete(cGroupId);\n this.runningConsumers.delete(cGroupId);\n this.consumerCreationOptions.delete(cGroupId);\n this.logger.log(`Retry consumer disconnected: group \"${cGroupId}\"`);\n }\n // Disconnect the EOS tx producer for this retry level\n const txId = `${cGroupId}-tx`;\n const txProducer = this.retryTxProducers.get(txId);\n if (txProducer) {\n await txProducer\n .disconnect()\n .catch((e) =>\n this.logger.warn(\n `Error disconnecting retry tx producer \"${txId}\":`,\n toError(e).message,\n ),\n );\n _activeTransactionalIds.delete(txId);\n this.retryTxProducers.delete(txId);\n }\n }\n this.companionGroupIds.delete(groupId);\n } else {\n const tasks: Promise<void>[] = [\n ...Array.from(this.consumers.values()).map((c) =>\n c.disconnect().catch(() => {}),\n ),\n ...Array.from(this.retryTxProducers.values()).map((p) =>\n p.disconnect().catch(() => {}),\n ),\n ];\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.consumerCreationOptions.clear();\n this.companionGroupIds.clear();\n this.retryTxProducers.clear();\n this.dedupStates.clear();\n for (const state of this.circuitStates.values())\n clearTimeout(state.timer);\n this.circuitStates.clear();\n this.circuitConfigs.clear();\n this.logger.log(\"All consumers disconnected\");\n }\n }\n\n /**\n * Temporarily stop delivering messages from specific partitions without disconnecting the consumer.\n *\n * @param groupId Consumer group to pause. Defaults to the client's default groupId.\n * @param assignments Topic-partition pairs to pause.\n */\n public pauseConsumer(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n ): void {\n const gid = groupId ?? this.defaultGroupId;\n const consumer = this.consumers.get(gid);\n if (!consumer) {\n this.logger.warn(`pauseConsumer: no active consumer for group \"${gid}\"`);\n return;\n }\n consumer.pause(\n assignments.flatMap(({ topic, partitions }) =>\n partitions.map((p) => ({ topic, partitions: [p] })),\n ),\n );\n }\n\n /**\n * Resume message delivery for previously paused topic-partitions.\n *\n * @param {string|undefined} groupId Consumer group to resume. Defaults to the client's default groupId.\n * @param {Array<{ topic: string; partitions: number[] }>} assignments Topic-partition pairs to resume.\n */\n public resumeConsumer(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n ): void {\n const gid = groupId ?? this.defaultGroupId;\n const consumer = this.consumers.get(gid);\n if (!consumer) {\n this.logger.warn(`resumeConsumer: no active consumer for group \"${gid}\"`);\n return;\n }\n consumer.resume(\n assignments.flatMap(({ topic, partitions }) =>\n partitions.map((p) => ({ topic, partitions: [p] })),\n ),\n );\n }\n\n /** Pause all assigned partitions of a topic for a consumer group (used for queue backpressure). */\n private pauseTopicAllPartitions(gid: string, topic: string): void {\n const consumer = this.consumers.get(gid);\n if (!consumer) return;\n const assignment: Array<{ topic: string; partition: number }> =\n (consumer as any).assignment?.() ?? [];\n const partitions = assignment\n .filter((a) => a.topic === topic)\n .map((a) => a.partition);\n if (partitions.length > 0)\n consumer.pause(partitions.map((p) => ({ topic, partitions: [p] })));\n }\n\n /** Resume all assigned partitions of a topic for a consumer group (used for queue backpressure). */\n private resumeTopicAllPartitions(gid: string, topic: string): void {\n const consumer = this.consumers.get(gid);\n if (!consumer) return;\n const assignment: Array<{ topic: string; partition: number }> =\n (consumer as any).assignment?.() ?? [];\n const partitions = assignment\n .filter((a) => a.topic === topic)\n .map((a) => a.partition);\n if (partitions.length > 0)\n consumer.resume(partitions.map((p) => ({ topic, partitions: [p] })));\n }\n\n /** DLQ header keys added by `sendToDlq` — stripped before re-publishing. */\n private static readonly DLQ_HEADER_KEYS = new Set([\n \"x-dlq-original-topic\",\n \"x-dlq-failed-at\",\n \"x-dlq-error-message\",\n \"x-dlq-error-stack\",\n \"x-dlq-attempt-count\",\n ]);\n\n /**\n * Re-publish messages from a dead letter queue back to the original topic.\n *\n * Messages are consumed from `<topic>.dlq` and re-published to `<topic>`.\n * The original topic is determined by the `x-dlq-original-topic` header.\n * The `x-dlq-*` headers are stripped before re-publishing.\n *\n * @param topic - The topic to replay from `<topic>.dlq`\n * @param options - Options for replay\n * @returns { replayed: number; skipped: number } - counts of re-published vs skipped messages\n */\n public async replayDlq(\n topic: string,\n options: DlqReplayOptions = {},\n ): Promise<{ replayed: number; skipped: number }> {\n const dlqTopic = `${topic}.dlq`;\n await this.ensureAdminConnected();\n\n const partitionOffsets = await this.admin.fetchTopicOffsets(dlqTopic);\n const activePartitions = partitionOffsets.filter(\n (p) => parseInt(p.high, 10) > 0,\n );\n if (activePartitions.length === 0) {\n this.logger.log(`replayDlq: \"${dlqTopic}\" is empty — nothing to replay`);\n return { replayed: 0, skipped: 0 };\n }\n\n const highWatermarks = new Map(\n activePartitions.map(({ partition, high }) => [\n partition,\n parseInt(high, 10),\n ]),\n );\n const processedOffsets = new Map<number, number>();\n\n let replayed = 0;\n let skipped = 0;\n\n const tempGroupId = `${dlqTopic}-replay-${Date.now()}`;\n\n await new Promise<void>((resolve, reject) => {\n const consumer = getOrCreateConsumer(\n tempGroupId,\n true,\n true,\n this.consumerOpsDeps,\n );\n\n const cleanup = () => {\n consumer\n .disconnect()\n .catch(() => {})\n .finally(() => {\n this.consumers.delete(tempGroupId);\n this.runningConsumers.delete(tempGroupId);\n this.consumerCreationOptions.delete(tempGroupId);\n });\n };\n\n consumer\n .connect()\n .then(() => subscribeWithRetry(consumer, [dlqTopic], this.logger))\n .then(() =>\n consumer.run({\n eachMessage: async ({ partition, message }) => {\n if (!message.value) return;\n\n const offset = parseInt(message.offset, 10);\n processedOffsets.set(partition, offset);\n\n const headers = decodeHeaders(message.headers);\n const targetTopic =\n options.targetTopic ?? headers[\"x-dlq-original-topic\"];\n const originalHeaders = Object.fromEntries(\n Object.entries(headers).filter(\n ([k]) => !KafkaClient.DLQ_HEADER_KEYS.has(k),\n ),\n );\n const value = message.value.toString();\n const shouldProcess =\n !options.filter || options.filter(headers, value);\n\n if (!targetTopic || !shouldProcess) {\n skipped++;\n } else if (options.dryRun) {\n this.logger.log(\n `[DLQ replay dry-run] Would replay to \"${targetTopic}\"`,\n );\n replayed++;\n } else {\n await this.producer.send({\n topic: targetTopic,\n messages: [{ value, headers: originalHeaders }],\n });\n replayed++;\n }\n\n // Stop when all active partitions have reached their high watermark\n const allDone = Array.from(highWatermarks.entries()).every(\n ([p, hwm]) => (processedOffsets.get(p) ?? -1) >= hwm - 1,\n );\n if (allDone) {\n cleanup();\n resolve();\n }\n },\n }),\n )\n .catch((err) => {\n cleanup();\n reject(err);\n });\n });\n\n this.logger.log(\n `replayDlq: replayed ${replayed}, skipped ${skipped} from \"${dlqTopic}\"`,\n );\n return { replayed, skipped };\n }\n\n public async resetOffsets(\n groupId: string | undefined,\n topic: string,\n position: \"earliest\" | \"latest\",\n ): Promise<void> {\n const gid = groupId ?? this.defaultGroupId;\n if (this.runningConsumers.has(gid)) {\n throw new Error(\n `resetOffsets: consumer group \"${gid}\" is still running. ` +\n `Call stopConsumer(\"${gid}\") before resetting offsets.`,\n );\n }\n await this.ensureAdminConnected();\n const partitionOffsets = await this.admin.fetchTopicOffsets(topic);\n const partitions = partitionOffsets.map(({ partition, low, high }) => ({\n partition,\n offset: position === \"earliest\" ? low : high,\n }));\n await (this.admin as any).setOffsets({ groupId: gid, topic, partitions });\n this.logger.log(\n `Offsets reset to ${position} for group \"${gid}\" on topic \"${topic}\"`,\n );\n }\n\n /**\n * Seek specific topic-partition pairs to explicit offsets for a stopped consumer group.\n * Throws if the group is still running — call `stopConsumer(groupId)` first.\n * Assignments are grouped by topic and committed via `admin.setOffsets`.\n */\n public async seekToOffset(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partition: number; offset: string }>,\n ): Promise<void> {\n const gid = groupId ?? this.defaultGroupId;\n if (this.runningConsumers.has(gid)) {\n throw new Error(\n `seekToOffset: consumer group \"${gid}\" is still running. ` +\n `Call stopConsumer(\"${gid}\") before seeking offsets.`,\n );\n }\n await this.ensureAdminConnected();\n const byTopic = new Map<\n string,\n Array<{ partition: number; offset: string }>\n >();\n for (const { topic, partition, offset } of assignments) {\n const list = byTopic.get(topic) ?? [];\n list.push({ partition, offset });\n byTopic.set(topic, list);\n }\n for (const [topic, partitions] of byTopic) {\n await (this.admin as any).setOffsets({ groupId: gid, topic, partitions });\n this.logger.log(\n `Offsets set for group \"${gid}\" on \"${topic}\": ${JSON.stringify(partitions)}`,\n );\n }\n }\n\n /**\n * Seek specific topic-partition pairs to the offset nearest to a given timestamp\n * (in milliseconds) for a stopped consumer group.\n * Throws if the group is still running — call `stopConsumer(groupId)` first.\n * Assignments are grouped by topic and committed via `admin.setOffsets`.\n * If no offset exists at the requested timestamp (e.g. empty partition or\n * future timestamp), the partition falls back to `-1` (end of topic — new messages only).\n */\n public async seekToTimestamp(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partition: number; timestamp: number }>,\n ): Promise<void> {\n const gid = groupId ?? this.defaultGroupId;\n if (this.runningConsumers.has(gid)) {\n throw new Error(\n `seekToTimestamp: consumer group \"${gid}\" is still running. ` +\n `Call stopConsumer(\"${gid}\") before seeking offsets.`,\n );\n }\n await this.ensureAdminConnected();\n const byTopic = new Map<\n string,\n Array<{ partition: number; timestamp: number }>\n >();\n for (const { topic, partition, timestamp } of assignments) {\n const list = byTopic.get(topic) ?? [];\n list.push({ partition, timestamp });\n byTopic.set(topic, list);\n }\n for (const [topic, parts] of byTopic) {\n const offsets: Array<{ partition: number; offset: string }> =\n await Promise.all(\n parts.map(async ({ partition, timestamp }) => {\n const results = await (this.admin as any).fetchTopicOffsetsByTime(\n topic,\n timestamp,\n );\n const found = (results as Array<{ partition: number; offset: string }>).find(\n (r) => r.partition === partition,\n );\n return { partition, offset: found?.offset ?? \"-1\" };\n }),\n );\n await (this.admin as any).setOffsets({ groupId: gid, topic, partitions: offsets });\n this.logger.log(\n `Offsets set by timestamp for group \"${gid}\" on \"${topic}\": ${JSON.stringify(offsets)}`,\n );\n }\n }\n\n /**\n * Returns the current circuit breaker state for a specific topic partition.\n * Returns `undefined` when no circuit state exists — either `circuitBreaker` is not\n * configured for the group, or the circuit has never been tripped.\n *\n * @param topic Topic name.\n * @param partition Partition index.\n * @param groupId Consumer group. Defaults to the client's default groupId.\n *\n * @returns `{ status, failures, windowSize }` snapshot for a given partition or `undefined` if no state exists.\n */\n public getCircuitState(\n topic: string,\n partition: number,\n groupId?: string,\n ): { status: \"closed\" | \"open\" | \"half-open\"; failures: number; windowSize: number } | undefined {\n const gid = groupId ?? this.defaultGroupId;\n const state = this.circuitStates.get(`${gid}:${topic}:${partition}`);\n if (!state) return undefined;\n return {\n status: state.status,\n failures: state.window.filter((v) => !v).length,\n windowSize: state.window.length,\n };\n }\n\n /**\n * Query consumer group lag per partition.\n * Lag = broker high-watermark − last committed offset.\n * A committed offset of -1 (nothing committed yet) counts as full lag.\n *\n * Returns an empty array when the consumer group has never committed any\n * offsets (freshly created group, `autoCommit: false` with no manual commits,\n * or group not yet assigned). This is a Kafka protocol limitation:\n * `fetchOffsets` only returns data for topic-partitions that have at least one\n * committed offset. Use `checkStatus()` to verify broker connectivity in that case.\n */\n public async getConsumerLag(\n groupId?: string,\n ): Promise<Array<{ topic: string; partition: number; lag: number }>> {\n const gid = groupId ?? this.defaultGroupId;\n await this.ensureAdminConnected();\n\n const committedByTopic = await this.admin.fetchOffsets({ groupId: gid });\n\n const brokerOffsetsAll = await Promise.all(\n committedByTopic.map(({ topic }) => this.admin.fetchTopicOffsets(topic)),\n );\n\n const result: Array<{ topic: string; partition: number; lag: number }> = [];\n\n for (let i = 0; i < committedByTopic.length; i++) {\n const { topic, partitions } = committedByTopic[i];\n const brokerOffsets = brokerOffsetsAll[i];\n\n for (const { partition, offset } of partitions) {\n const broker = brokerOffsets.find((o) => o.partition === partition);\n if (!broker) continue;\n\n const committed = parseInt(offset, 10);\n const high = parseInt(broker.high, 10);\n // committed === -1 means the group has never committed for this partition\n const lag = committed === -1 ? high : Math.max(0, high - committed);\n result.push({ topic, partition, lag });\n }\n }\n\n return result;\n }\n\n /** Check broker connectivity. Never throws — returns a discriminated union. */\n public async checkStatus(): Promise<import(\"../types\").KafkaHealthResult> {\n try {\n await this.ensureAdminConnected();\n const topics = await this.admin.listTopics();\n return { status: \"up\", clientId: this.clientId, topics };\n } catch (error) {\n return {\n status: \"down\",\n clientId: this.clientId,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n\n /**\n * List all consumer groups known to the broker.\n * Useful for monitoring which groups are active and their current state.\n */\n public async listConsumerGroups(): Promise<ConsumerGroupSummary[]> {\n await this.ensureAdminConnected();\n const result = await this.admin.listGroups();\n return result.groups.map((g: any) => ({\n groupId: g.groupId,\n state: g.state ?? \"Unknown\",\n }));\n }\n\n /**\n * Describe topics — returns partition layout, leader, replicas, and ISR.\n * @param topics Topic names to describe. Omit to describe all topics.\n */\n public async describeTopics(topics?: string[]): Promise<TopicDescription[]> {\n await this.ensureAdminConnected();\n const result = await (this.admin as any).fetchTopicMetadata(\n topics ? { topics } : undefined,\n );\n return (result.topics as any[]).map((t: any) => ({\n name: t.name,\n partitions: (t.partitions as any[]).map((p: any) => ({\n partition: p.partitionId ?? p.partition,\n leader: p.leader,\n replicas: (p.replicas as any[]).map((r: any) =>\n typeof r === \"number\" ? r : r.nodeId,\n ),\n isr: (p.isr as any[]).map((r: any) =>\n typeof r === \"number\" ? r : r.nodeId,\n ),\n })),\n }));\n }\n\n /**\n * Delete records from a topic up to (but not including) the given offsets.\n * All messages with offsets **before** the given offset are deleted.\n */\n public async deleteRecords(\n topic: string,\n partitions: Array<{ partition: number; offset: string }>,\n ): Promise<void> {\n await this.ensureAdminConnected();\n await this.admin.deleteTopicRecords({ topic, partitions });\n }\n\n /** Return the client ID provided during `KafkaClient` construction. */\n public getClientId(): ClientId {\n return this.clientId;\n }\n\n /**\n * Return a snapshot of internal event counters accumulated since client creation\n * (or since the last `resetMetrics()` call).\n *\n * @param topic Topic name to scope the snapshot to. When omitted, counters are\n * aggregated across all topics. If the topic has no recorded events yet, returns\n * a zero-valued snapshot.\n * @returns Read-only `KafkaMetrics` snapshot: `processedCount`, `retryCount`, `dlqCount`, `dedupCount`.\n */\n public getMetrics(topic?: string): Readonly<KafkaMetrics> {\n if (topic !== undefined) {\n const m = this._topicMetrics.get(topic);\n return m\n ? { ...m }\n : { processedCount: 0, retryCount: 0, dlqCount: 0, dedupCount: 0 };\n }\n // Aggregate across all topics\n const agg: KafkaMetrics = {\n processedCount: 0,\n retryCount: 0,\n dlqCount: 0,\n dedupCount: 0,\n };\n for (const m of this._topicMetrics.values()) {\n agg.processedCount += m.processedCount;\n agg.retryCount += m.retryCount;\n agg.dlqCount += m.dlqCount;\n agg.dedupCount += m.dedupCount;\n }\n return agg;\n }\n\n /**\n * Reset internal event counters to zero.\n *\n * @param topic Topic name to reset. When omitted, all topics are reset.\n */\n public resetMetrics(topic?: string): void {\n if (topic !== undefined) {\n this._topicMetrics.delete(topic);\n return;\n }\n this._topicMetrics.clear();\n }\n\n /** Gracefully disconnect producer, all consumers, and admin. */\n public async disconnect(drainTimeoutMs = 30_000): Promise<void> {\n await this.waitForDrain(drainTimeoutMs);\n const tasks: Promise<void>[] = [this.producer.disconnect()];\n if (this.txProducer) {\n tasks.push(this.txProducer.disconnect());\n _activeTransactionalIds.delete(this.txId);\n this.txProducer = undefined;\n this.txProducerInitPromise = undefined;\n }\n for (const txId of this.retryTxProducers.keys()) {\n _activeTransactionalIds.delete(txId);\n }\n for (const p of this.retryTxProducers.values()) {\n tasks.push(p.disconnect());\n }\n this.retryTxProducers.clear();\n for (const consumer of this.consumers.values()) {\n tasks.push(consumer.disconnect());\n }\n if (this.isAdminConnected) {\n tasks.push(this.admin.disconnect());\n this.isAdminConnected = false;\n }\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.consumerCreationOptions.clear();\n this.companionGroupIds.clear();\n for (const state of this.circuitStates.values()) clearTimeout(state.timer);\n this.circuitStates.clear();\n this.circuitConfigs.clear();\n this.logger.log(\"All connections closed\");\n }\n\n // ── Graceful shutdown ────────────────────────────────────────────\n\n /**\n * NestJS lifecycle hook — called automatically when the host module is torn down.\n * Drains in-flight handlers and disconnects all producers, consumers, and admin.\n * `KafkaModule` relies on this method; no separate destroy provider is needed.\n */\n public async onModuleDestroy(): Promise<void> {\n await this.disconnect();\n }\n\n /**\n * Register SIGTERM / SIGINT handlers that drain in-flight messages before\n * disconnecting. Call this once after constructing the client in non-NestJS apps.\n * NestJS apps get drain for free via `onModuleDestroy` → `disconnect()`.\n */\n public enableGracefulShutdown(\n signals: NodeJS.Signals[] = [\"SIGTERM\", \"SIGINT\"],\n drainTimeoutMs = 30_000,\n ): void {\n const handler = () => {\n this.logger.log(\n \"Shutdown signal received — draining in-flight handlers...\",\n );\n this.disconnect(drainTimeoutMs).catch((err) =>\n this.logger.error(\n \"Error during graceful shutdown:\",\n toError(err).message,\n ),\n );\n };\n for (const signal of signals) {\n process.once(signal, handler);\n }\n }\n\n /**\n * Increment the in-flight handler count and return a promise that calls the given handler.\n * When the promise resolves or rejects, decrement the in flight handler count.\n * If the in flight handler count reaches 0, call all previously registered drain resolvers.\n * @param fn The handler to call when the promise is resolved or rejected.\n * @returns A promise that resolves or rejects with the result of calling the handler.\n */\n private trackInFlight<R>(fn: () => Promise<R>): Promise<R> {\n this.inFlightTotal++;\n return fn().finally(() => {\n this.inFlightTotal--;\n if (this.inFlightTotal === 0) {\n this.drainResolvers.splice(0).forEach((r) => r());\n }\n });\n }\n\n /**\n * Waits for all in-flight handlers to complete or for a given timeout, whichever comes first.\n * @param timeoutMs Maximum time to wait in milliseconds.\n * @returns A promise that resolves when all handlers have completed or the timeout is reached.\n * @private\n */\n private waitForDrain(timeoutMs: number): Promise<void> {\n if (this.inFlightTotal === 0) return Promise.resolve();\n return new Promise<void>((resolve) => {\n let handle: ReturnType<typeof setTimeout>;\n const onDrain = () => {\n clearTimeout(handle);\n resolve();\n };\n this.drainResolvers.push(onDrain);\n handle = setTimeout(() => {\n const idx = this.drainResolvers.indexOf(onDrain);\n if (idx !== -1) this.drainResolvers.splice(idx, 1);\n this.logger.warn(\n `Drain timed out after ${timeoutMs}ms — ${this.inFlightTotal} handler(s) still in flight`,\n );\n resolve();\n }, timeoutMs);\n });\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n /**\n * Prepare a send payload by registering the topic's schema and then building the payload.\n * @param topicOrDesc - topic name or topic descriptor\n * @param messages - batch of messages to send\n * @returns - prepared payload\n */\n private async preparePayload(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n compression?: import(\"../types\").CompressionType,\n ) {\n registerSchema(topicOrDesc, this.schemaRegistry, this.logger);\n const payload = await buildSendPayload(\n topicOrDesc,\n messages,\n this.producerOpsDeps,\n compression,\n );\n await this.ensureTopic(payload.topic);\n return payload;\n }\n\n // afterSend is called once per message — symmetric with beforeSend in buildSendPayload.\n private notifyAfterSend(topic: string, count: number): void {\n for (let i = 0; i < count; i++) {\n for (const inst of this.instrumentation) {\n inst.afterSend?.(topic);\n }\n }\n }\n\n /**\n * Returns the KafkaMetrics for a given topic.\n * If the topic hasn't seen any events, initializes a zero-valued snapshot.\n * @param topic - name of the topic to get the metrics for\n * @returns - KafkaMetrics for the given topic\n */\n private metricsFor(topic: string): KafkaMetrics {\n let m = this._topicMetrics.get(topic);\n if (!m) {\n m = { processedCount: 0, retryCount: 0, dlqCount: 0, dedupCount: 0 };\n this._topicMetrics.set(topic, m);\n }\n return m;\n }\n\n/**\n * Notifies instrumentation hooks of a retry event.\n * @param envelope The original message envelope that triggered the retry.\n * @param attempt The current retry attempt (1-indexed).\n * @param maxRetries The maximum number of retries configured for this topic.\n */\n private notifyRetry(\n envelope: import(\"../message/envelope\").EventEnvelope<any>,\n attempt: number,\n maxRetries: number,\n ): void {\n this.metricsFor(envelope.topic).retryCount++;\n for (const inst of this.instrumentation) {\n inst.onRetry?.(envelope, attempt, maxRetries);\n }\n }\n\n/**\n * Called whenever a message is routed to the dead letter queue.\n * @param envelope The original message envelope.\n * @param reason The reason for routing to the dead letter queue.\n * @param gid The group ID of the consumer that triggered the circuit breaker, if any.\n */\n private notifyDlq(\n envelope: import(\"../message/envelope\").EventEnvelope<any>,\n reason: DlqReason,\n gid?: string,\n ): void {\n this.metricsFor(envelope.topic).dlqCount++;\n for (const inst of this.instrumentation) {\n inst.onDlq?.(envelope, reason);\n }\n\n if (!gid) return;\n const cfg = this.circuitConfigs.get(gid);\n if (!cfg) return;\n\n const threshold = cfg.threshold ?? 5;\n const recoveryMs = cfg.recoveryMs ?? 30_000;\n\n const stateKey = `${gid}:${envelope.topic}:${envelope.partition}`;\n let state = this.circuitStates.get(stateKey);\n if (!state) {\n state = { status: \"closed\", window: [], successes: 0 };\n this.circuitStates.set(stateKey, state);\n }\n if (state.status === \"open\") return; // already tripped — skip window update\n\n const openCircuit = () => {\n state!.status = \"open\";\n state!.window = [];\n state!.successes = 0;\n clearTimeout(state!.timer);\n for (const inst of this.instrumentation)\n inst.onCircuitOpen?.(envelope.topic, envelope.partition);\n this.pauseConsumer(gid, [\n { topic: envelope.topic, partitions: [envelope.partition] },\n ]);\n state!.timer = setTimeout(() => {\n state!.status = \"half-open\";\n state!.successes = 0;\n this.logger.log(\n `[CircuitBreaker] HALF-OPEN — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition}`,\n );\n for (const inst of this.instrumentation)\n inst.onCircuitHalfOpen?.(envelope.topic, envelope.partition);\n this.resumeConsumer(gid, [\n { topic: envelope.topic, partitions: [envelope.partition] },\n ]);\n }, recoveryMs);\n };\n\n if (state.status === \"half-open\") {\n // Any failure in half-open immediately re-opens the circuit.\n clearTimeout(state.timer);\n this.logger.warn(\n `[CircuitBreaker] OPEN (half-open failure) — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition}`,\n );\n openCircuit();\n return;\n }\n\n // CLOSED: update sliding window\n const windowSize = cfg.windowSize ?? Math.max(threshold * 2, 10);\n state.window = [...state.window, false];\n if (state.window.length > windowSize) {\n state.window = state.window.slice(state.window.length - windowSize);\n }\n const failures = state.window.filter((v) => !v).length;\n\n if (failures >= threshold) {\n this.logger.warn(\n `[CircuitBreaker] OPEN — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition} ` +\n `(${failures}/${state.window.length} failures, threshold=${threshold})`,\n );\n openCircuit();\n }\n }\n\n /**\n * Notify all instrumentation hooks about a duplicate message detection.\n * Invoked by the consumer after a message has been successfully processed\n * and the Lamport clock detected a duplicate.\n * @param envelope The processed message envelope.\n * @param strategy The duplicate detection strategy used.\n */\n private notifyDuplicate(\n envelope: import(\"../message/envelope\").EventEnvelope<any>,\n strategy: \"drop\" | \"dlq\" | \"topic\",\n ): void {\n this.metricsFor(envelope.topic).dedupCount++;\n for (const inst of this.instrumentation) {\n inst.onDuplicate?.(envelope, strategy);\n }\n }\n\n /**\n * Notify all instrumentation hooks about a successfully processed message.\n * Invoked by the consumer after a message has been successfully processed\n * by the handler.\n * @param envelope The processed message envelope.\n * @param gid The optional consumer group ID.\n */\n private notifyMessage(\n envelope: import(\"../message/envelope\").EventEnvelope<any>,\n gid?: string,\n ): void {\n this.metricsFor(envelope.topic).processedCount++;\n for (const inst of this.instrumentation) {\n inst.onMessage?.(envelope);\n }\n\n if (!gid) return;\n const cfg = this.circuitConfigs.get(gid);\n if (!cfg) return;\n\n const stateKey = `${gid}:${envelope.topic}:${envelope.partition}`;\n const state = this.circuitStates.get(stateKey);\n if (!state) return;\n\n const halfOpenSuccesses = cfg.halfOpenSuccesses ?? 1;\n\n if (state.status === \"half-open\") {\n state.successes++;\n if (state.successes >= halfOpenSuccesses) {\n clearTimeout(state.timer);\n state.timer = undefined;\n state.status = \"closed\";\n state.window = [];\n state.successes = 0;\n this.logger.log(\n `[CircuitBreaker] CLOSED — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition}`,\n );\n for (const inst of this.instrumentation)\n inst.onCircuitClose?.(envelope.topic, envelope.partition);\n }\n } else if (state.status === \"closed\") {\n const threshold = cfg.threshold ?? 5;\n const windowSize = cfg.windowSize ?? Math.max(threshold * 2, 10);\n state.window = [...state.window, true];\n if (state.window.length > windowSize) {\n state.window = state.window.slice(state.window.length - windowSize);\n }\n }\n }\n\n /**\n * Start a timer that logs a warning if `fn` hasn't resolved within `timeoutMs`.\n * The handler itself is not cancelled — the warning is diagnostic only.\n */\n private wrapWithTimeoutWarning<R>(\n fn: () => Promise<R>,\n timeoutMs: number,\n topic: string,\n ): Promise<R> {\n let timer: ReturnType<typeof setTimeout> | undefined;\n const promise = fn().finally(() => {\n if (timer !== undefined) clearTimeout(timer);\n });\n timer = setTimeout(() => {\n this.logger.warn(\n `Handler for topic \"${topic}\" has not resolved after ${timeoutMs}ms — possible stuck handler`,\n );\n }, timeoutMs);\n return promise;\n }\n\n /**\n * When `retryTopics: true` and `autoCreateTopics: false`, verify that every\n * `<topic>.retry.<level>` topic already exists. Throws a clear error at startup\n * rather than silently discovering missing topics on the first handler failure.\n */\n private async validateRetryTopicsExist(\n topicNames: string[],\n maxRetries: number,\n ): Promise<void> {\n await this.ensureAdminConnected();\n const existing = new Set(await this.admin.listTopics());\n const missing: string[] = [];\n for (const t of topicNames) {\n for (let level = 1; level <= maxRetries; level++) {\n const retryTopic = `${t}.retry.${level}`;\n if (!existing.has(retryTopic)) missing.push(retryTopic);\n }\n }\n if (missing.length > 0) {\n throw new Error(\n `retryTopics: true but the following retry topics do not exist: ${missing.join(\", \")}. ` +\n `Create them manually or set autoCreateTopics: true.`,\n );\n }\n }\n\n /**\n * When `autoCreateTopics` is disabled, verify that `<topic>.dlq` exists for every\n * consumed topic. Throws a clear error at startup rather than silently discovering\n * missing DLQ topics on the first handler failure.\n */\n private async validateDlqTopicsExist(topicNames: string[]): Promise<void> {\n await this.ensureAdminConnected();\n const existing = new Set(await this.admin.listTopics());\n const missing = topicNames\n .filter((t) => !existing.has(`${t}.dlq`))\n .map((t) => `${t}.dlq`);\n if (missing.length > 0) {\n throw new Error(\n `dlq: true but the following DLQ topics do not exist: ${missing.join(\", \")}. ` +\n `Create them manually or set autoCreateTopics: true.`,\n );\n }\n }\n\n /**\n * When `deduplication.strategy: 'topic'` and `autoCreateTopics: false`, verify\n * that every `<topic>.duplicates` destination topic already exists. Throws a\n * clear error at startup rather than silently dropping duplicates on first hit.\n */\n private async validateDuplicatesTopicsExist(\n topicNames: string[],\n customDestination: string | undefined,\n ): Promise<void> {\n await this.ensureAdminConnected();\n const existing = new Set(await this.admin.listTopics());\n const toCheck = customDestination\n ? [customDestination]\n : topicNames.map((t) => `${t}.duplicates`);\n const missing = toCheck.filter((t) => !existing.has(t));\n if (missing.length > 0) {\n throw new Error(\n `deduplication.strategy: 'topic' but the following duplicate-routing topics do not exist: ${missing.join(\", \")}. ` +\n `Create them manually or set autoCreateTopics: true.`,\n );\n }\n }\n\n /**\n * Connect the admin client if not already connected.\n * The flag is only set to `true` after a successful connect — if `admin.connect()`\n * throws the flag remains `false` so the next call will retry the connection.\n */\n private async ensureAdminConnected(): Promise<void> {\n if (this.isAdminConnected) return;\n try {\n await this.admin.connect();\n this.isAdminConnected = true;\n } catch (err) {\n this.isAdminConnected = false;\n throw err;\n }\n }\n\n /**\n * Create and connect a transactional producer for EOS retry routing.\n * Each retry level consumer gets its own producer with a unique `transactionalId`\n * so Kafka can fence stale producers on restart without affecting other levels.\n */\n private async createRetryTxProducer(\n transactionalId: string,\n ): Promise<Producer> {\n if (_activeTransactionalIds.has(transactionalId)) {\n this.logger.warn(\n `transactionalId \"${transactionalId}\" is already in use by another KafkaClient in this process. ` +\n `Kafka will fence one of the producers. ` +\n `Set a unique \\`transactionalId\\` (or distinct \\`clientId\\`) per instance.`,\n );\n }\n const p = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n idempotent: true,\n transactionalId,\n maxInFlightRequests: 1,\n },\n });\n await p.connect();\n _activeTransactionalIds.add(transactionalId);\n this.retryTxProducers.set(transactionalId, p);\n return p;\n }\n\n /**\n * Ensure that a topic exists by creating it if it doesn't already exist.\n * If `autoCreateTopics` is disabled, this method will not create the topic and\n * will return immediately.\n * If multiple concurrent calls are made to `ensureTopic` for the same topic,\n * they are deduplicated to prevent multiple calls to `admin.createTopics()`.\n * @param topic - The topic to ensure exists.\n * @returns A promise that resolves when the topic has been created or already exists.\n */\n private async ensureTopic(topic: string): Promise<void> {\n if (!this.autoCreateTopicsEnabled || this.ensuredTopics.has(topic)) return;\n // Deduplicate concurrent calls for the same topic so that parallel sends\n // (or consumer setup + send) don't each race to call createTopics.\n let p = this.ensureTopicPromises.get(topic);\n if (!p) {\n p = (async () => {\n await this.ensureAdminConnected();\n await this.admin.createTopics({\n topics: [{ topic, numPartitions: this.numPartitions }],\n });\n this.ensuredTopics.add(topic);\n })().finally(() => this.ensureTopicPromises.delete(topic));\n this.ensureTopicPromises.set(topic, p);\n }\n await p;\n }\n\n /** Shared consumer setup: groupId check, schema map, connect, subscribe. */\n private async setupConsumer(\n topics: any[],\n mode: \"eachMessage\" | \"eachBatch\",\n options: ConsumerOptions<T>,\n ) {\n const {\n groupId: optGroupId,\n fromBeginning = false,\n retry,\n dlq = false,\n interceptors = [],\n schemas: optionSchemas,\n } = options;\n\n // Separate string topic names from regex patterns.\n // Schema registration, topic-ensure, and retry chains only apply to string topics.\n const stringTopics: any[] = topics.filter((t) => !(t instanceof RegExp));\n const regexTopics: RegExp[] = topics.filter((t) => t instanceof RegExp);\n const hasRegex = regexTopics.length > 0;\n\n const gid = optGroupId || this.defaultGroupId;\n const existingMode = this.runningConsumers.get(gid);\n const oppositeMode = mode === \"eachMessage\" ? \"eachBatch\" : \"eachMessage\";\n if (existingMode === oppositeMode) {\n throw new Error(\n `Cannot use ${mode} on consumer group \"${gid}\" — it is already running with ${oppositeMode}. ` +\n `Use a different groupId for this consumer.`,\n );\n }\n if (existingMode === mode) {\n const callerName =\n mode === \"eachMessage\" ? \"startConsumer\" : \"startBatchConsumer\";\n throw new Error(\n `${callerName}(\"${gid}\") called twice — this group is already consuming. ` +\n `Call stopConsumer(\"${gid}\") first or pass a different groupId.`,\n );\n }\n\n const consumer = getOrCreateConsumer(\n gid,\n fromBeginning,\n options.autoCommit ?? true,\n this.consumerOpsDeps,\n options.partitionAssigner,\n );\n\n // Only build schema map for string topics (regex patterns have no fixed name to look up).\n const schemaMap = buildSchemaMap(\n stringTopics,\n this.schemaRegistry,\n optionSchemas,\n this.logger,\n );\n\n const topicNames = stringTopics.map((t: any) => resolveTopicName(t));\n // All topics (strings + regex) passed to subscribe.\n const subscribeTopics: (string | RegExp)[] = [\n ...topicNames,\n ...regexTopics,\n ];\n\n // Ensure topics exist before subscribing — librdkafka errors on unknown topics.\n // Regex patterns are skipped (we can't create or validate a pattern).\n for (const t of topicNames) {\n await this.ensureTopic(t);\n }\n if (dlq) {\n for (const t of topicNames) {\n await this.ensureTopic(`${t}.dlq`);\n }\n if (!this.autoCreateTopicsEnabled && topicNames.length > 0) {\n await this.validateDlqTopicsExist(topicNames);\n }\n }\n\n if (options.deduplication?.strategy === \"topic\") {\n const dest = options.deduplication.duplicatesTopic;\n if (this.autoCreateTopicsEnabled) {\n for (const t of topicNames) {\n await this.ensureTopic(dest ?? `${t}.duplicates`);\n }\n } else if (topicNames.length > 0) {\n await this.validateDuplicatesTopicsExist(topicNames, dest);\n }\n }\n\n await consumer.connect();\n await subscribeWithRetry(\n consumer,\n subscribeTopics,\n this.logger,\n options.subscribeRetry,\n );\n\n const displayTopics = subscribeTopics\n .map((t) => (t instanceof RegExp ? t.toString() : t))\n .join(\", \");\n this.logger.log(\n `${mode === \"eachBatch\" ? \"Batch consumer\" : \"Consumer\"} subscribed to topics: ${displayTopics}`,\n );\n\n return { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry, hasRegex };\n }\n\n /** Create or retrieve the deduplication context for a consumer group. */\n private resolveDeduplicationContext(\n groupId: string,\n options: import(\"../types\").DeduplicationOptions | undefined,\n ): DeduplicationContext | undefined {\n if (!options) return undefined;\n if (!this.dedupStates.has(groupId)) {\n this.dedupStates.set(groupId, new Map());\n }\n return { options, state: this.dedupStates.get(groupId)! };\n }\n\n // ── Deps object getters ──────────────────────────────────────────\n\n /**\n * An object containing the necessary dependencies for building a send payload.\n *\n * @property {Map<string, SchemaLike>} schemaRegistry - A map of topic names to their schemas.\n * @property {boolean} strictSchemasEnabled - Whether strict schema validation is enabled.\n * @property {KafkaInstrumentation} instrumentation - An object for creating a span for instrumentation.\n * @property {KafkaLogger} logger - A logger for logging messages.\n * @property {() => number} nextLamportClock - A function that returns the next value of the logical clock.\n */\n private get producerOpsDeps(): BuildSendPayloadDeps {\n return {\n schemaRegistry: this.schemaRegistry,\n strictSchemasEnabled: this.strictSchemasEnabled,\n instrumentation: this.instrumentation,\n logger: this.logger,\n nextLamportClock: () => ++this._lamportClock,\n };\n }\n\n /**\n * ConsumerOpsDeps object properties:\n *\n * @property {Map<string, Consumer>} consumers - A map of consumer group IDs to their corresponding consumer instances.\n * @property {Map<string, { fromBeginning: boolean; autoCommit: boolean }>} consumerCreationOptions - A map of consumer group IDs to their creation options.\n * @property {Kafka} kafka - The Kafka client instance.\n * @property {function(string, Partition[]): void} onRebalance - An optional callback function called when a consumer group is rebalanced.\n * @property {KafkaLogger} logger - The logger instance used for logging consumer operations.\n */\n private get consumerOpsDeps(): ConsumerOpsDeps {\n return {\n consumers: this.consumers,\n consumerCreationOptions: this.consumerCreationOptions,\n kafka: this.kafka,\n onRebalance: this.onRebalance,\n logger: this.logger,\n };\n }\n\n /** Build MessageHandlerDeps with circuit breaker callbacks bound to the given groupId. */\n private messageDepsFor(gid: string): MessageHandlerDeps {\n return {\n logger: this.logger,\n producer: this.producer,\n instrumentation: this.instrumentation,\n onMessageLost: this.onMessageLost,\n onTtlExpired: this.onTtlExpired,\n onRetry: this.notifyRetry.bind(this),\n onDlq: (envelope, reason) => this.notifyDlq(envelope, reason, gid),\n onDuplicate: this.notifyDuplicate.bind(this),\n onMessage: (envelope) => this.notifyMessage(envelope, gid),\n };\n }\n\n /**\n * The dependencies object passed to the retry topic consumers.\n *\n * `logger`: The logger instance passed to the retry topic consumers.\n * `producer`: The producer instance passed to the retry topic consumers.\n * `instrumentation`: The instrumentation instance passed to the retry topic consumers.\n * `onMessageLost`: The callback function passed to the retry topic consumers for tracking lost messages.\n * `onRetry`: The callback function passed to the retry topic consumers for tracking retry attempts.\n * `onDlq`: The callback function passed to the retry topic consumers for tracking dead-letter queue routing.\n * `onMessage`: The callback function passed to the retry topic consumers for tracking message delivery.\n * `ensureTopic`: A function that ensures a topic exists before subscribing to it.\n * `getOrCreateConsumer`: A function that creates or retrieves a consumer instance.\n * `runningConsumers`: A map of consumer group IDs to their corresponding consumer instances.\n * `createRetryTxProducer`: A function that creates a retry transactional producer instance.\n */\n private get retryTopicDeps() {\n return {\n logger: this.logger,\n producer: this.producer,\n instrumentation: this.instrumentation,\n onMessageLost: this.onMessageLost,\n onRetry: this.notifyRetry.bind(this),\n onDlq: this.notifyDlq.bind(this),\n onMessage: this.notifyMessage.bind(this),\n ensureTopic: (t: string) => this.ensureTopic(t),\n getOrCreateConsumer: (gid: string, fb: boolean, ac: boolean) =>\n getOrCreateConsumer(gid, fb, ac, this.consumerOpsDeps),\n runningConsumers: this.runningConsumers,\n createRetryTxProducer: (txId: string) => this.createRetryTxProducer(txId),\n };\n }\n}\n","import { AsyncLocalStorage } from \"node:async_hooks\";\nimport { randomUUID } from \"node:crypto\";\nimport type { MessageHeaders } from \"../types\";\n\n// ── Header keys ──────────────────────────────────────────────────────\n\nexport const HEADER_EVENT_ID = \"x-event-id\";\nexport const HEADER_CORRELATION_ID = \"x-correlation-id\";\nexport const HEADER_TIMESTAMP = \"x-timestamp\";\nexport const HEADER_SCHEMA_VERSION = \"x-schema-version\";\nexport const HEADER_TRACEPARENT = \"traceparent\";\n/** Monotonically increasing logical clock stamped by the producer for deduplication. */\nexport const HEADER_LAMPORT_CLOCK = \"x-lamport-clock\";\n\n// ── EventEnvelope ────────────────────────────────────────────────────\n\n/**\n * Typed wrapper combining a parsed message payload with Kafka metadata\n * and envelope headers.\n *\n * On **send**, the library auto-generates envelope headers\n * (`x-event-id`, `x-correlation-id`, `x-timestamp`, `x-schema-version`).\n *\n * On **consume**, the library extracts those headers and assembles\n * an `EventEnvelope` that is passed to the handler.\n */\nexport interface EventEnvelope<T> {\n /** Deserialized + validated message body. */\n payload: T;\n /** Topic the message was produced to / consumed from. */\n topic: string;\n /** Kafka partition (consume-side only, `-1` on send). */\n partition: number;\n /** Kafka offset (consume-side only, empty string on send). */\n offset: string;\n /** ISO-8601 timestamp set by the producer. */\n timestamp: string;\n /** Unique ID for this event (UUID v4). */\n eventId: string;\n /** Correlation ID — auto-propagated via AsyncLocalStorage. */\n correlationId: string;\n /** Schema version of the payload. */\n schemaVersion: number;\n /** W3C Trace Context `traceparent` header (set by OTel instrumentation). */\n traceparent?: string;\n /** All decoded Kafka headers for extensibility. */\n headers: MessageHeaders;\n}\n\n// ── AsyncLocalStorage context ────────────────────────────────────────\n\ninterface EnvelopeCtx {\n correlationId: string;\n traceparent?: string;\n}\n\nconst envelopeStorage = new AsyncLocalStorage<EnvelopeCtx>();\n\n/** Read the current envelope context (correlationId / traceparent) from ALS. */\nexport function getEnvelopeContext(): EnvelopeCtx | undefined {\n return envelopeStorage.getStore();\n}\n\n/** Execute `fn` inside an envelope context so nested sends inherit correlationId. */\nexport function runWithEnvelopeContext<R>(ctx: EnvelopeCtx, fn: () => R): R {\n return envelopeStorage.run(ctx, fn);\n}\n\n// ── Header helpers ───────────────────────────────────────────────────\n\n/** Options accepted by `buildEnvelopeHeaders`. */\nexport interface EnvelopeHeaderOptions {\n correlationId?: string;\n schemaVersion?: number;\n eventId?: string;\n headers?: MessageHeaders;\n}\n\n/**\n * Generate envelope headers for the send path.\n *\n * Priority for `correlationId`:\n * explicit option → ALS context → new UUID.\n */\nexport function buildEnvelopeHeaders(\n options: EnvelopeHeaderOptions = {},\n): MessageHeaders {\n const ctx = getEnvelopeContext();\n\n const correlationId =\n options.correlationId ?? ctx?.correlationId ?? randomUUID();\n const eventId = options.eventId ?? randomUUID();\n const timestamp = new Date().toISOString();\n const schemaVersion = String(options.schemaVersion ?? 1);\n\n const envelope: MessageHeaders = {\n [HEADER_EVENT_ID]: eventId,\n [HEADER_CORRELATION_ID]: correlationId,\n [HEADER_TIMESTAMP]: timestamp,\n [HEADER_SCHEMA_VERSION]: schemaVersion,\n };\n\n // Propagate traceparent from ALS if present (OTel may override via instrumentation)\n if (ctx?.traceparent) {\n envelope[HEADER_TRACEPARENT] = ctx.traceparent;\n }\n\n // User-provided headers win on conflict\n return { ...envelope, ...options.headers };\n}\n\n/**\n * Decode kafkajs headers (`Record<string, Buffer | string | undefined>`)\n * into plain `Record<string, string>`.\n */\nexport function decodeHeaders(\n raw:\n | Record<string, Buffer | string | (Buffer | string)[] | undefined>\n | undefined,\n): MessageHeaders {\n if (!raw) return {};\n const result: MessageHeaders = {};\n for (const [key, value] of Object.entries(raw)) {\n if (value === undefined) continue;\n if (Array.isArray(value)) {\n // Kafka allows multiple headers with the same key — take the last one (last-wins),\n // consistent with HTTP semantics. Joining would corrupt values that contain commas.\n const items = value.map((v) => (Buffer.isBuffer(v) ? v.toString() : v));\n result[key] = items[items.length - 1] ?? \"\";\n } else {\n result[key] = Buffer.isBuffer(value) ? value.toString() : value;\n }\n }\n return result;\n}\n\n/**\n * Build an `EventEnvelope` from a consumed kafkajs message.\n * Tolerates missing envelope headers — generates defaults so messages\n * from non-envelope producers still work.\n */\nexport function extractEnvelope<T>(\n payload: T,\n headers: MessageHeaders,\n topic: string,\n partition: number,\n offset: string,\n): EventEnvelope<T> {\n return {\n payload,\n topic,\n partition,\n offset,\n eventId: headers[HEADER_EVENT_ID] ?? randomUUID(),\n correlationId: headers[HEADER_CORRELATION_ID] ?? randomUUID(),\n timestamp: headers[HEADER_TIMESTAMP] ?? new Date().toISOString(),\n schemaVersion: Number(headers[HEADER_SCHEMA_VERSION] ?? 1),\n traceparent: headers[HEADER_TRACEPARENT],\n headers,\n };\n}\n","/** Error thrown when a consumer message handler fails. */\nexport class KafkaProcessingError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n message: string,\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(message, options);\n this.name = \"KafkaProcessingError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when schema validation fails on send or consume. */\nexport class KafkaValidationError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(`Schema validation failed for topic \"${topic}\"`, options);\n this.name = \"KafkaValidationError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when all retry attempts are exhausted for a message. */\nexport class KafkaRetryExhaustedError extends KafkaProcessingError {\n constructor(\n topic: string,\n originalMessage: unknown,\n public readonly attempts: number,\n options?: { cause?: Error },\n ) {\n super(\n `Message processing failed after ${attempts} attempts on topic \"${topic}\"`,\n topic,\n originalMessage,\n options,\n );\n this.name = \"KafkaRetryExhaustedError\";\n }\n}\n","import {\n buildEnvelopeHeaders,\n HEADER_LAMPORT_CLOCK,\n} from \"../message/envelope\";\nimport { KafkaValidationError } from \"../errors\";\nimport type { SchemaLike, SchemaParseContext } from \"../message/topic\";\nimport type {\n BatchMessageItem,\n CompressionType,\n KafkaInstrumentation,\n KafkaLogger,\n MessageHeaders,\n} from \"../types\";\n\n/**\n * Extract the plain topic name string from either a `TopicDescriptor` object or a raw string.\n * Falls back to `String(topicOrDescriptor)` for any other value.\n */\nexport function resolveTopicName(topicOrDescriptor: unknown): string {\n if (typeof topicOrDescriptor === \"string\") return topicOrDescriptor;\n if (\n topicOrDescriptor &&\n typeof topicOrDescriptor === \"object\" &&\n \"__topic\" in topicOrDescriptor\n ) {\n return (topicOrDescriptor as { __topic: string }).__topic;\n }\n return String(topicOrDescriptor);\n}\n\n/**\n * Register the schema attached to a `TopicDescriptor` into the shared schema registry.\n * If a different schema is already registered for the same topic, logs a warning and\n * overwrites it — consistent schemas across all call sites avoid silent validation drift.\n *\n * @param topicOrDesc A `TopicDescriptor` (with `__schema`) or a plain string. Plain strings are ignored.\n * @param schemaRegistry Mutable map of topic name → validator shared across the client.\n * @param logger Optional logger for conflict warnings.\n */\nexport function registerSchema(\n topicOrDesc: any,\n schemaRegistry: Map<string, SchemaLike>,\n logger?: KafkaLogger,\n): void {\n if (topicOrDesc?.__schema) {\n const topic = resolveTopicName(topicOrDesc);\n const existing = schemaRegistry.get(topic);\n if (existing && existing !== topicOrDesc.__schema) {\n logger?.warn(\n `Schema conflict for topic \"${topic}\": a different schema is already registered. ` +\n `Using the new schema — ensure consistent schemas to avoid silent validation mismatches.`,\n );\n }\n schemaRegistry.set(topic, topicOrDesc.__schema);\n }\n}\n\n/**\n * Validate `message` against the schema attached to `topicOrDesc` (or looked up from the\n * registry when `strictSchemasEnabled` is on). Returns the parsed (potentially transformed)\n * value on success, or throws a `KafkaValidationError` on failure.\n *\n * Validation priority:\n * 1. Inline schema on the `TopicDescriptor` — always applied.\n * 2. Registry schema — applied only when `strictSchemasEnabled` is `true`.\n * 3. No schema found — returns `message` unchanged.\n *\n * @param topicOrDesc Topic descriptor carrying an inline `__schema`, or a plain topic string.\n * @param message The raw message payload to validate.\n * @param deps Schema registry and strict-mode flag.\n * @param ctx Optional parse context forwarded to the schema (topic name, headers, version).\n */\nexport async function validateMessage(\n topicOrDesc: any,\n message: any,\n deps: {\n schemaRegistry: Map<string, SchemaLike>;\n strictSchemasEnabled: boolean;\n },\n ctx?: SchemaParseContext,\n): Promise<any> {\n const topicName = resolveTopicName(topicOrDesc);\n if (topicOrDesc?.__schema) {\n try {\n return await topicOrDesc.__schema.parse(message, ctx);\n } catch (error) {\n throw new KafkaValidationError(topicName, message, {\n cause: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n if (deps.strictSchemasEnabled && typeof topicOrDesc === \"string\") {\n const schema = deps.schemaRegistry.get(topicOrDesc);\n if (schema) {\n try {\n return await schema.parse(message, ctx);\n } catch (error) {\n throw new KafkaValidationError(topicName, message, {\n cause: error instanceof Error ? error : new Error(String(error)),\n });\n }\n }\n }\n return message;\n}\n\nexport type BuildSendPayloadDeps = {\n schemaRegistry: Map<string, SchemaLike>;\n strictSchemasEnabled: boolean;\n instrumentation: KafkaInstrumentation[];\n logger: KafkaLogger;\n /** Called once per message to get the next Lamport clock value. Omit to disable clock stamping. */\n nextLamportClock?: () => number;\n};\n\n/**\n * Build the Kafka producer payload from a topic descriptor (or name) and an array of messages.\n *\n * For each message the function:\n * 1. Builds envelope headers (`x-event-id`, `x-correlation-id`, `x-timestamp`, …).\n * 2. Stamps the Lamport clock header when `deps.nextLamportClock` is provided.\n * 3. Calls `beforeSend` instrumentation hooks so tracing can inject `traceparent` etc.\n * 4. Validates the payload against the attached or registry schema.\n * 5. JSON-serialises the validated value.\n *\n * @param topicOrDesc Topic descriptor or plain topic name string.\n * @param messages Array of outgoing messages with optional key, headers, and metadata.\n * @param deps Schema registry, strict-mode flag, instrumentation hooks, and Lamport clock factory.\n * @param compression Optional compression codec to include in the returned payload object.\n * @returns Kafka producer `send()` payload — `{ topic, messages, compression? }`.\n */\nexport async function buildSendPayload(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n deps: BuildSendPayloadDeps,\n compression?: CompressionType,\n): Promise<{\n topic: string;\n compression?: CompressionType;\n messages: Array<{\n value: string;\n key: string | null;\n headers: MessageHeaders;\n }>;\n}> {\n const topic = resolveTopicName(topicOrDesc);\n const builtMessages = await Promise.all(\n messages.map(async (m) => {\n const envelopeHeaders = buildEnvelopeHeaders({\n correlationId: m.correlationId,\n schemaVersion: m.schemaVersion,\n eventId: m.eventId,\n headers: m.headers,\n });\n\n // Stamp Lamport clock for consumer-side deduplication\n if (deps.nextLamportClock) {\n envelopeHeaders[HEADER_LAMPORT_CLOCK] = String(deps.nextLamportClock());\n }\n\n // beforeSend: let instrumentation mutate headers (e.g. OTel injects traceparent)\n for (const inst of deps.instrumentation) {\n inst.beforeSend?.(topic, envelopeHeaders);\n }\n\n const sendCtx: SchemaParseContext = {\n topic,\n headers: envelopeHeaders,\n version: m.schemaVersion ?? 1,\n };\n\n return {\n value: JSON.stringify(\n await validateMessage(topicOrDesc, m.value, deps, sendCtx),\n ),\n key: m.key ?? null,\n headers: envelopeHeaders,\n };\n }),\n );\n return { topic, messages: builtMessages, ...(compression && { compression }) };\n}\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Consumer = KafkaJS.Consumer;\ntype Kafka = KafkaJS.Kafka;\nimport type { SchemaLike } from \"../message/topic\";\nimport type { KafkaClientOptions, KafkaLogger } from \"../types\";\nimport { resolveTopicName } from \"./producer-ops\";\n\nexport type ConsumerOpsDeps = {\n consumers: Map<string, Consumer>;\n consumerCreationOptions: Map<\n string,\n { fromBeginning: boolean; autoCommit: boolean }\n >;\n kafka: Kafka;\n onRebalance: KafkaClientOptions[\"onRebalance\"];\n logger: KafkaLogger;\n};\n\n/**\n * Return an existing consumer for `groupId`, or create and register a new one.\n *\n * If the group already exists with different `fromBeginning` / `autoCommit` options the\n * existing consumer is returned unchanged and a warning is logged — use a distinct\n * `groupId` if different options are required.\n *\n * Partition assignment strategy defaults to `cooperative-sticky`, which minimises\n * partition movement during rebalances and is the safest choice for horizontally\n * scaled deployments.\n *\n * @param groupId Kafka consumer group ID.\n * @param fromBeginning When `true`, the group starts from the earliest available offset.\n * @param autoCommit When `true`, offsets are committed automatically. Set to `false` for manual EOS commits.\n * @param deps Shared client dependencies (consumer map, Kafka instance, logger, …).\n * @param partitionAssigner Assignment strategy — `'cooperative-sticky'` (default), `'roundrobin'`, or `'range'`.\n * @returns The consumer instance for the group (existing or newly created).\n */\nexport function getOrCreateConsumer(\n groupId: string,\n fromBeginning: boolean,\n autoCommit: boolean,\n deps: ConsumerOpsDeps,\n partitionAssigner?: \"roundrobin\" | \"range\" | \"cooperative-sticky\",\n): Consumer {\n const { consumers, consumerCreationOptions, kafka, onRebalance, logger } =\n deps;\n\n if (consumers.has(groupId)) {\n const prev = consumerCreationOptions.get(groupId)!;\n if (\n prev.fromBeginning !== fromBeginning ||\n prev.autoCommit !== autoCommit\n ) {\n logger.warn(\n `Consumer group \"${groupId}\" already exists with options ` +\n `(fromBeginning: ${prev.fromBeginning}, autoCommit: ${prev.autoCommit}) — ` +\n `new options (fromBeginning: ${fromBeginning}, autoCommit: ${autoCommit}) ignored. ` +\n `Use a different groupId to apply different options.`,\n );\n }\n return consumers.get(groupId)!;\n }\n\n consumerCreationOptions.set(groupId, { fromBeginning, autoCommit });\n\n // Default to cooperative-sticky: minimal partition movement during rebalances.\n // This is especially important for horizontal scaling — only the partitions that\n // actually need to be reassigned are moved, avoiding a full stop-the-world rebalance.\n const assigners: KafkaJS.PartitionAssigners[] = [\n partitionAssigner === \"roundrobin\"\n ? KafkaJS.PartitionAssigners.roundRobin\n : partitionAssigner === \"range\"\n ? KafkaJS.PartitionAssigners.range\n : KafkaJS.PartitionAssigners.cooperativeSticky,\n ];\n\n const config: Parameters<typeof kafka.consumer>[0] = {\n kafkaJS: { groupId, fromBeginning, autoCommit, partitionAssigners: assigners },\n };\n\n if (onRebalance) {\n const cb = onRebalance;\n // rebalance_cb is called by librdkafka on every partition assign/revoke.\n // err.code -175 = ERR__ASSIGN_PARTITIONS, -174 = ERR__REVOKE_PARTITIONS.\n // The library handles the actual assign/unassign in its finally block regardless\n // of what this callback does, so we only need it for the side-effect notification.\n (config as any)[\"rebalance_cb\"] = (err: any, assignment: any[]) => {\n const type = err.code === -175 ? \"assign\" : \"revoke\";\n try {\n cb(\n type,\n assignment.map((p) => ({ topic: p.topic, partition: p.partition })),\n );\n } catch (e) {\n logger.warn(`onRebalance callback threw: ${(e as Error).message}`);\n }\n };\n }\n\n const consumer = kafka.consumer(config);\n consumers.set(groupId, consumer);\n return consumer;\n}\n\n/**\n * Build a local schema map for the topics being subscribed to.\n *\n * Schemas are collected from two sources in order:\n * 1. Inline schemas on `TopicDescriptor` objects in the `topics` array.\n * 2. Explicit overrides passed in `optionSchemas` (e.g. from `ConsumerOptions.schemas`).\n *\n * Both sources are also registered in the shared `schemaRegistry` so that the same\n * schema is used consistently across producers and consumers. A warning is logged when\n * a topic already has a different schema registered.\n *\n * @param topics Array of topic names or `TopicDescriptor` objects.\n * @param schemaRegistry Shared client-wide schema map (mutated in place).\n * @param optionSchemas Additional topic → schema overrides from consumer options.\n * @param logger Optional logger for schema-conflict warnings.\n * @returns A topic → schema map scoped to the current subscription.\n */\nexport function buildSchemaMap(\n topics: any[],\n schemaRegistry: Map<string, SchemaLike>,\n optionSchemas?: Map<string, SchemaLike>,\n logger?: KafkaLogger,\n): Map<string, SchemaLike> {\n const schemaMap = new Map<string, SchemaLike>();\n\n const registerChecked = (name: string, schema: SchemaLike) => {\n const existing = schemaRegistry.get(name);\n if (existing && existing !== schema) {\n logger?.warn(\n `Schema conflict for topic \"${name}\": a different schema is already registered. ` +\n `Using the new schema — ensure consistent schemas to avoid silent validation mismatches.`,\n );\n }\n schemaMap.set(name, schema);\n schemaRegistry.set(name, schema);\n };\n\n for (const t of topics) {\n if (t?.__schema) {\n registerChecked(resolveTopicName(t), t.__schema);\n }\n }\n if (optionSchemas) {\n for (const [k, v] of optionSchemas) {\n registerChecked(k, v);\n }\n }\n return schemaMap;\n}\n","import type { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Producer = KafkaJS.Producer;\nimport type { EventEnvelope } from \"../message/envelope\";\nimport { extractEnvelope } from \"../message/envelope\";\nimport { KafkaRetryExhaustedError, KafkaValidationError } from \"../errors\";\nimport type { SchemaLike, SchemaParseContext } from \"../message/topic\";\nimport type {\n BeforeConsumeResult,\n ConsumerInterceptor,\n DlqReason,\n KafkaInstrumentation,\n KafkaLogger,\n MessageHeaders,\n MessageLostContext,\n RetryOptions,\n TopicMapConstraint,\n} from \"../types\";\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ── JSON parsing ────────────────────────────────────────────────────\n\n/** Parse raw message as JSON. Returns null on failure (logs error). */\nexport function parseJsonMessage(\n raw: string,\n topic: string,\n logger: KafkaLogger,\n): any | null {\n try {\n return JSON.parse(raw);\n } catch (error) {\n logger.error(\n `Failed to parse message from topic ${topic}:`,\n toError(error).stack,\n );\n return null;\n }\n}\n\n// ── Schema validation ───────────────────────────────────────────────\n\n/**\n * Validate a parsed message against the schema map.\n * On failure: logs error, sends to DLQ if enabled, calls interceptor.onError.\n * Returns validated message or null.\n */\nexport async function validateWithSchema<T extends TopicMapConstraint<T>>(\n message: any,\n raw: string,\n topic: string,\n schemaMap: Map<string, SchemaLike>,\n interceptors: ConsumerInterceptor<T>[],\n dlq: boolean,\n deps: {\n logger: KafkaLogger;\n producer: Producer;\n onMessageLost?: (ctx: MessageLostContext) => void | Promise<void>;\n originalHeaders?: MessageHeaders;\n instrumentation?: KafkaInstrumentation[];\n },\n): Promise<any | null> {\n const schema = schemaMap.get(topic);\n if (!schema) return message;\n\n const ctx: SchemaParseContext = {\n topic,\n headers: deps.originalHeaders ?? {},\n version: Number(deps.originalHeaders?.[\"x-schema-version\"] ?? 1),\n };\n\n try {\n return await schema.parse(message, ctx);\n } catch (error) {\n const err = toError(error);\n const validationError = new KafkaValidationError(topic, message, {\n cause: err,\n });\n deps.logger.error(\n `Schema validation failed for topic ${topic}:`,\n err.message,\n );\n if (dlq) {\n await sendToDlq(topic, raw, deps, {\n error: validationError,\n attempt: 0,\n originalHeaders: deps.originalHeaders,\n });\n } else {\n await deps.onMessageLost?.({\n topic,\n error: validationError,\n attempt: 0,\n headers: deps.originalHeaders ?? {},\n });\n }\n // Validation errors don't have an envelope yet — call hooks with a minimal envelope\n const errorEnvelope = extractEnvelope(\n message,\n deps.originalHeaders ?? {},\n topic,\n -1,\n \"\",\n );\n for (const inst of deps.instrumentation ?? []) {\n inst.onConsumeError?.(errorEnvelope, validationError);\n }\n for (const interceptor of interceptors) {\n await interceptor.onError?.(errorEnvelope, validationError);\n }\n return null;\n }\n}\n\n// ── DLQ ─────────────────────────────────────────────────────────────\n\nexport interface DlqMetadata {\n error: Error;\n attempt: number;\n /** Original Kafka message headers — forwarded to DLQ to preserve correlationId, traceparent, etc. */\n originalHeaders?: MessageHeaders;\n}\n\n/** Build the DLQ send payload without sending it. Used by sendToDlq and EOS routing. */\nexport function buildDlqPayload(\n topic: string,\n rawMessage: string,\n meta?: DlqMetadata,\n): {\n topic: string;\n messages: Array<{ value: string; headers: MessageHeaders }>;\n} {\n const dlqTopic = `${topic}.dlq`;\n const headers: MessageHeaders = {\n ...(meta?.originalHeaders ?? {}),\n \"x-dlq-original-topic\": topic,\n \"x-dlq-failed-at\": new Date().toISOString(),\n \"x-dlq-error-message\": meta?.error.message ?? \"unknown\",\n \"x-dlq-error-stack\": meta?.error.stack?.slice(0, 2000) ?? \"\",\n \"x-dlq-attempt-count\": String(meta?.attempt ?? 0),\n };\n return { topic: dlqTopic, messages: [{ value: rawMessage, headers }] };\n}\n\nexport async function sendToDlq(\n topic: string,\n rawMessage: string,\n deps: {\n logger: KafkaLogger;\n producer: Producer;\n onMessageLost?: (ctx: MessageLostContext) => void | Promise<void>;\n },\n meta?: DlqMetadata,\n): Promise<void> {\n const payload = buildDlqPayload(topic, rawMessage, meta);\n try {\n await deps.producer.send(payload);\n deps.logger.warn(`Message sent to DLQ: ${payload.topic}`);\n } catch (error) {\n const err = toError(error);\n deps.logger.error(\n `Failed to send message to DLQ ${payload.topic}:`,\n err.stack,\n );\n await deps.onMessageLost?.({\n topic,\n error: err,\n attempt: meta?.attempt ?? 0,\n headers: meta?.originalHeaders ?? {},\n });\n }\n}\n\n// ── Retry topic routing ─────────────────────────────────────────────\n\n/** Headers stamped on messages sent to a `<topic>.retry` topic. */\nexport const RETRY_HEADER_ATTEMPT = \"x-retry-attempt\";\nexport const RETRY_HEADER_AFTER = \"x-retry-after\";\nexport const RETRY_HEADER_MAX_RETRIES = \"x-retry-max-retries\";\nexport const RETRY_HEADER_ORIGINAL_TOPIC = \"x-retry-original-topic\";\n\n/** Build the retry topic send payload without sending it. Used by sendToRetryTopic and EOS routing. */\nexport function buildRetryTopicPayload(\n originalTopic: string,\n rawMessages: string[],\n attempt: number,\n maxRetries: number,\n delayMs: number,\n /** Per-message headers (array) or a single object applied to all messages. */\n originalHeaders: MessageHeaders | MessageHeaders[],\n): {\n topic: string;\n messages: Array<{ value: string; headers: MessageHeaders }>;\n} {\n const retryTopic = `${originalTopic}.retry.${attempt}`;\n const STRIP = new Set([\n RETRY_HEADER_ATTEMPT,\n RETRY_HEADER_AFTER,\n RETRY_HEADER_MAX_RETRIES,\n RETRY_HEADER_ORIGINAL_TOPIC,\n ]);\n function buildHeaders(hdr: MessageHeaders): MessageHeaders {\n // Strip any stale retry headers from a previous hop so they don't leak through.\n const userHeaders = Object.fromEntries(\n Object.entries(hdr).filter(([k]) => !STRIP.has(k)),\n );\n return {\n ...userHeaders,\n [RETRY_HEADER_ATTEMPT]: String(attempt),\n [RETRY_HEADER_AFTER]: String(Date.now() + delayMs),\n [RETRY_HEADER_MAX_RETRIES]: String(maxRetries),\n [RETRY_HEADER_ORIGINAL_TOPIC]: originalTopic,\n };\n }\n return {\n topic: retryTopic,\n messages: rawMessages.map((value, i) => ({\n value,\n headers: buildHeaders(\n Array.isArray(originalHeaders)\n ? (originalHeaders[i] ?? {})\n : originalHeaders,\n ),\n })),\n };\n}\n\n/**\n * Send raw messages to the retry topic `<originalTopic>.retry`.\n * Stamps scheduling headers so the retry consumer knows when and how many times to retry.\n */\nexport async function sendToRetryTopic(\n originalTopic: string,\n rawMessages: string[],\n attempt: number,\n maxRetries: number,\n delayMs: number,\n /** Per-message headers (array) or a single object applied to all messages. */\n originalHeaders: MessageHeaders | MessageHeaders[],\n deps: {\n logger: KafkaLogger;\n producer: Producer;\n onMessageLost?: (ctx: MessageLostContext) => void | Promise<void>;\n },\n): Promise<void> {\n const payload = buildRetryTopicPayload(\n originalTopic,\n rawMessages,\n attempt,\n maxRetries,\n delayMs,\n originalHeaders,\n );\n try {\n await deps.producer.send(payload);\n deps.logger.warn(\n `Message queued in retry topic ${payload.topic} (attempt ${attempt}/${maxRetries})`,\n );\n } catch (error) {\n const err = toError(error);\n deps.logger.error(\n `Failed to send message to retry topic ${payload.topic}:`,\n err.stack,\n );\n await deps.onMessageLost?.({\n topic: originalTopic,\n error: err,\n attempt,\n headers: Array.isArray(originalHeaders)\n ? (originalHeaders[0] ?? {})\n : originalHeaders,\n });\n }\n}\n\n// ── Deduplication routing ────────────────────────────────────────────\n\nexport interface DuplicateMetadata {\n /** The `x-lamport-clock` value from the incoming (duplicate) message. */\n incomingClock: number;\n /** The last processed clock value for this topic/partition. */\n lastProcessedClock: number;\n /** Original Kafka message headers — forwarded to preserve correlationId, traceparent, etc. */\n originalHeaders?: MessageHeaders;\n}\n\n/** Build the payload for a duplicate message forwarded to a custom topic. */\nexport function buildDuplicateTopicPayload(\n sourceTopic: string,\n rawMessage: string,\n destinationTopic: string,\n meta?: DuplicateMetadata,\n): {\n topic: string;\n messages: Array<{ value: string; headers: MessageHeaders }>;\n} {\n const headers: MessageHeaders = {\n ...(meta?.originalHeaders ?? {}),\n \"x-duplicate-original-topic\": sourceTopic,\n \"x-duplicate-detected-at\": new Date().toISOString(),\n \"x-duplicate-reason\": \"lamport-clock-duplicate\",\n \"x-duplicate-incoming-clock\": String(meta?.incomingClock ?? 0),\n \"x-duplicate-last-processed-clock\": String(meta?.lastProcessedClock ?? 0),\n };\n return {\n topic: destinationTopic,\n messages: [{ value: rawMessage, headers }],\n };\n}\n\n/**\n * Forward a duplicate message to a dedicated topic (e.g. `<topic>.duplicates`).\n * Stamps reason metadata headers so consumers of that topic know why it landed there.\n */\nexport async function sendToDuplicatesTopic(\n sourceTopic: string,\n rawMessage: string,\n destinationTopic: string,\n deps: { logger: KafkaLogger; producer: Producer },\n meta?: DuplicateMetadata,\n): Promise<void> {\n const payload = buildDuplicateTopicPayload(\n sourceTopic,\n rawMessage,\n destinationTopic,\n meta,\n );\n try {\n await deps.producer.send(payload);\n deps.logger.warn(`Duplicate message forwarded to ${destinationTopic}`);\n } catch (error) {\n deps.logger.error(\n `Failed to forward duplicate to ${destinationTopic}:`,\n toError(error).stack,\n );\n }\n}\n\n// ── Pipeline helpers ─────────────────────────────────────────────────\n\nasync function broadcastToInterceptors<T extends TopicMapConstraint<T>>(\n envelopes: EventEnvelope<any>[],\n interceptors: ConsumerInterceptor<T>[],\n cb: (\n interceptor: ConsumerInterceptor<T>,\n env: EventEnvelope<any>,\n ) => Promise<void> | void | undefined,\n): Promise<void> {\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await cb(interceptor, env);\n }\n }\n}\n\n/**\n * Run `fn` through the full instrumentation and interceptor lifecycle:\n * beforeConsume → interceptor.before → fn → interceptor.after → cleanup\n *\n * On error: fires `onConsumeError` and cleanup, then returns the error.\n * The caller is responsible for calling `notifyInterceptorsOnError` (with\n * a possibly-wrapped error) and deciding what happens next.\n *\n * Returns `null` on success, the caught `Error` on failure.\n */\nexport async function runHandlerWithPipeline<T extends TopicMapConstraint<T>>(\n fn: () => Promise<void>,\n envelopes: EventEnvelope<any>[],\n interceptors: ConsumerInterceptor<T>[],\n instrumentation: KafkaInstrumentation[],\n): Promise<Error | null> {\n const cleanups: (() => void)[] = [];\n const wraps: Array<(fn: () => Promise<void>) => Promise<void>> = [];\n\n try {\n for (const env of envelopes) {\n for (const inst of instrumentation) {\n const result: BeforeConsumeResult | void = inst.beforeConsume?.(env);\n if (typeof result === \"function\") {\n cleanups.push(result);\n } else if (result) {\n if (result.cleanup) cleanups.push(result.cleanup);\n if (result.wrap) wraps.push(result.wrap);\n }\n }\n }\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.before?.(env);\n }\n }\n\n // Compose wraps: first instrumentation is outermost, last is innermost.\n let runFn: () => Promise<void> = fn;\n for (let i = wraps.length - 1; i >= 0; i--) {\n const wrap = wraps[i];\n const inner = runFn;\n runFn = () => wrap(inner);\n }\n await runFn();\n\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.after?.(env);\n }\n }\n for (const cleanup of cleanups) cleanup();\n\n return null;\n } catch (error) {\n const err = toError(error);\n for (const env of envelopes) {\n for (const inst of instrumentation) {\n inst.onConsumeError?.(env, err);\n }\n }\n for (const cleanup of cleanups) cleanup();\n return err;\n }\n}\n\n/**\n * Call `interceptor.onError` for every envelope with the given error.\n * Separated from `runHandlerWithPipeline` so callers can wrap the raw error\n * (e.g. in `KafkaRetryExhaustedError`) before notifying interceptors.\n */\nexport async function notifyInterceptorsOnError<\n T extends TopicMapConstraint<T>,\n>(\n envelopes: EventEnvelope<any>[],\n interceptors: ConsumerInterceptor<T>[],\n error: Error,\n): Promise<void> {\n await broadcastToInterceptors(envelopes, interceptors, (i, env) =>\n i.onError?.(env, error),\n );\n}\n\n// ── Retry pipeline ──────────────────────────────────────────────────\n\nexport interface ExecuteWithRetryContext<T extends TopicMapConstraint<T>> {\n envelope: EventEnvelope<any> | EventEnvelope<any>[];\n rawMessages: string[];\n interceptors: ConsumerInterceptor<T>[];\n dlq: boolean;\n retry?: RetryOptions;\n isBatch?: boolean;\n /**\n * When `true`, failed messages are routed to `<topic>.retry` instead of being\n * retried in-process. All backoff and subsequent attempts are handled by the\n * companion retry consumer started by `startRetryTopicConsumers`.\n */\n retryTopics?: boolean;\n}\n\n/**\n * Execute a handler with retry, interceptors, instrumentation, and DLQ support.\n * Used by both single-message and batch consumers.\n */\nexport async function executeWithRetry<T extends TopicMapConstraint<T>>(\n fn: () => Promise<void>,\n ctx: ExecuteWithRetryContext<T>,\n deps: {\n logger: KafkaLogger;\n producer: Producer;\n instrumentation: KafkaInstrumentation[];\n onMessageLost?: (ctx: MessageLostContext) => void | Promise<void>;\n onRetry?: (\n envelope: EventEnvelope<any>,\n attempt: number,\n maxRetries: number,\n ) => void;\n onDlq?: (envelope: EventEnvelope<any>, reason: DlqReason) => void;\n onMessage?: (envelope: EventEnvelope<any>) => void;\n /**\n * EOS atomic routing to `<topic>.retry.1` (main consumer only).\n * When present, replaces `sendToRetryTopic` with a Kafka transaction that\n * sends to the retry topic AND commits the source offset atomically.\n */\n eosRouteToRetry?: (\n rawMessages: string[],\n envelopes: EventEnvelope<any>[],\n delay: number,\n ) => Promise<void>;\n /**\n * Manual offset commit for the success path when the main consumer runs\n * with `autoCommit: false` (required when `eosRouteToRetry` is active).\n */\n eosCommitOnSuccess?: () => Promise<void>;\n },\n): Promise<void> {\n const {\n envelope,\n rawMessages,\n interceptors,\n dlq,\n retry,\n isBatch,\n retryTopics,\n } = ctx;\n // With retryTopics mode the main consumer tries exactly once — retry consumer takes over.\n const maxAttempts = retryTopics ? 1 : retry ? retry.maxRetries + 1 : 1;\n const backoffMs = retry?.backoffMs ?? 1000;\n const maxBackoffMs = retry?.maxBackoffMs ?? 30_000;\n const envelopes = Array.isArray(envelope) ? envelope : [envelope];\n const topic = envelopes[0]?.topic ?? \"unknown\";\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n const error = await runHandlerWithPipeline(\n fn,\n envelopes,\n interceptors,\n deps.instrumentation,\n );\n if (!error) {\n if (deps.eosCommitOnSuccess) {\n try {\n await deps.eosCommitOnSuccess();\n } catch (commitErr) {\n // Offset commit failed — message will be redelivered; don't call onMessage\n // to avoid counting a message whose offset was not actually committed.\n deps.logger.error(\n `EOS offset commit failed after successful handler — message will be redelivered:`,\n toError(commitErr).stack,\n );\n return;\n }\n }\n for (const env of envelopes) deps.onMessage?.(env);\n return;\n }\n\n const isLastAttempt = attempt === maxAttempts;\n const reportedError =\n isLastAttempt && maxAttempts > 1\n ? new KafkaRetryExhaustedError(\n topic,\n envelopes.map((e) => e.payload),\n maxAttempts,\n { cause: error },\n )\n : error;\n\n await notifyInterceptorsOnError(envelopes, interceptors, reportedError);\n\n deps.logger.error(\n `Error processing ${isBatch ? \"batch\" : \"message\"} from topic ${topic} (attempt ${attempt}/${maxAttempts}):`,\n error.stack,\n );\n\n if (retryTopics && retry) {\n // Route to retry topic — retry consumer handles backoff and further attempts.\n // Always use attempt 1 here (main consumer never retries in-process).\n const cap = Math.min(backoffMs, maxBackoffMs);\n const delay = Math.floor(Math.random() * cap);\n if (deps.eosRouteToRetry) {\n // EOS path (single-message consumers): send to retry.1 + commit source\n // offset atomically via a Kafka transaction. A crash at any point rolls\n // back the transaction — no duplicate is routed to retry.1.\n try {\n await deps.eosRouteToRetry(rawMessages, envelopes, delay);\n deps.onRetry?.(envelopes[0]!, 1, retry.maxRetries);\n } catch (txErr) {\n // Transaction failed — offset is NOT committed; message will be\n // redelivered and the routing retried on the next delivery.\n deps.logger.error(\n `EOS routing to retry topic failed — message will be redelivered:`,\n toError(txErr).stack,\n );\n }\n } else {\n // Non-EOS path (batch consumers, or single-message consumers without EOS context).\n // The regular producer sends to retry.1 and the offset is committed by\n // librdkafka's autoCommit after eachMessage returns. A crash between the\n // two operations can cause the message to appear in retry.1 AND be\n // redelivered to the main consumer — handlers must be idempotent.\n await sendToRetryTopic(\n topic,\n rawMessages,\n 1,\n retry.maxRetries,\n delay,\n isBatch\n ? envelopes.map((e) => e.headers)\n : (envelopes[0]?.headers ?? {}),\n deps,\n );\n deps.onRetry?.(envelopes[0]!, 1, retry.maxRetries);\n }\n } else if (isLastAttempt) {\n if (dlq) {\n // Use per-message headers so each DLQ message preserves its own\n // correlationId / traceparent instead of copying envelopes[0]'s headers\n // onto every message in the batch.\n for (let i = 0; i < rawMessages.length; i++) {\n await sendToDlq(topic, rawMessages[i], deps, {\n error,\n attempt,\n originalHeaders: envelopes[i]?.headers,\n });\n deps.onDlq?.(envelopes[i] ?? envelopes[0]!, \"handler-error\");\n }\n } else {\n await deps.onMessageLost?.({\n topic,\n error,\n attempt,\n headers: envelopes[0]?.headers ?? {},\n });\n }\n } else {\n // Exponential backoff with full jitter to avoid thundering herd\n const cap = Math.min(backoffMs * 2 ** (attempt - 1), maxBackoffMs);\n deps.onRetry?.(envelopes[0]!, attempt, maxAttempts - 1);\n await sleep(Math.floor(Math.random() * cap));\n }\n }\n}\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Producer = KafkaJS.Producer;\ntype Consumer = KafkaJS.Consumer;\nimport {\n decodeHeaders,\n extractEnvelope,\n runWithEnvelopeContext,\n} from \"../message/envelope\";\nimport type { EventEnvelope } from \"../message/envelope\";\nimport {\n parseJsonMessage,\n validateWithSchema,\n executeWithRetry,\n sendToDlq,\n sendToDuplicatesTopic,\n buildRetryTopicPayload,\n} from \"../consumer/pipeline\";\nimport type { DuplicateMetadata } from \"../consumer/pipeline\";\nimport { HEADER_LAMPORT_CLOCK } from \"../message/envelope\";\nimport type { SchemaLike } from \"../message/topic\";\nimport type {\n BatchMeta,\n ConsumerInterceptor,\n DeduplicationOptions,\n DlqReason,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n RetryOptions,\n} from \"../types\";\n\nexport type MessageHandlerDeps = {\n logger: KafkaLogger;\n producer: Producer;\n instrumentation: KafkaInstrumentation[];\n onMessageLost: KafkaClientOptions[\"onMessageLost\"];\n onTtlExpired?: KafkaClientOptions[\"onTtlExpired\"];\n onRetry?: (\n envelope: EventEnvelope<any>,\n attempt: number,\n maxRetries: number,\n ) => void;\n onDlq?: (envelope: EventEnvelope<any>, reason: DlqReason) => void;\n onDuplicate?: (\n envelope: EventEnvelope<any>,\n strategy: \"drop\" | \"dlq\" | \"topic\",\n ) => void;\n onMessage?: (envelope: EventEnvelope<any>) => void;\n};\n\n/** Active deduplication context passed from KafkaClient to the message handler. */\nexport type DeduplicationContext = {\n options: DeduplicationOptions;\n /** Mutable map: `\"topic:partition\"` → last processed Lamport clock value. */\n state: Map<string, number>;\n};\n\nexport type EachMessageOpts = {\n schemaMap: Map<string, SchemaLike>;\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>;\n interceptors: ConsumerInterceptor<any>[];\n dlq: boolean;\n retry: RetryOptions | undefined;\n retryTopics: boolean | undefined;\n timeoutMs: number | undefined;\n wrapWithTimeout: <R>(\n fn: () => Promise<R>,\n ms: number,\n topic: string,\n ) => Promise<R>;\n deduplication?: DeduplicationContext;\n /** Drop messages older than this threshold (ms). See `ConsumerOptions.messageTtlMs`. */\n messageTtlMs?: number;\n /**\n * Per-consumer TTL expiry callback. Takes precedence over the client-level\n * `KafkaClientOptions.onTtlExpired` stored in `deps.onTtlExpired`.\n */\n onTtlExpired?: KafkaClientOptions[\"onTtlExpired\"];\n /**\n * EOS context for main consumer → retry.1 routing.\n * When set, the main consumer runs with `autoCommit: false`. On handler failure,\n * routing to the retry topic and the source offset commit are wrapped in a single\n * Kafka transaction — a crash at any point rolls back the transaction, ensuring\n * the message is not duplicated between the main topic and retry.1.\n * On success, the offset is committed manually (no transaction needed).\n */\n eosMainContext?: {\n txProducer: Producer;\n consumer: Consumer;\n };\n};\n\n/**\n * Check Lamport clock header against per-partition state.\n * Returns `true` if the message is a duplicate and should be skipped.\n * Updates the state map on a fresh message.\n */\nasync function applyDeduplication(\n envelope: EventEnvelope<any>,\n raw: string,\n dedup: DeduplicationContext,\n dlq: boolean,\n deps: MessageHandlerDeps,\n): Promise<boolean> {\n const clockRaw = envelope.headers[HEADER_LAMPORT_CLOCK];\n if (clockRaw === undefined) return false; // no clock → pass through\n\n const incomingClock = Number(clockRaw);\n if (Number.isNaN(incomingClock)) return false; // malformed header → pass through\n\n const stateKey = `${envelope.topic}:${envelope.partition}`;\n const lastProcessedClock = dedup.state.get(stateKey) ?? -1;\n\n if (incomingClock <= lastProcessedClock) {\n const meta: DuplicateMetadata = {\n incomingClock,\n lastProcessedClock,\n originalHeaders: envelope.headers,\n };\n const strategy = dedup.options.strategy ?? \"drop\";\n deps.logger.warn(\n `Duplicate message on ${envelope.topic}[${envelope.partition}]: ` +\n `clock=${incomingClock} <= last=${lastProcessedClock} — strategy=${strategy}`,\n );\n\n deps.onDuplicate?.(envelope, strategy);\n\n if (strategy === \"dlq\" && dlq) {\n const augmentedHeaders = {\n ...envelope.headers,\n \"x-dlq-reason\": \"lamport-clock-duplicate\",\n \"x-dlq-duplicate-incoming-clock\": String(incomingClock),\n \"x-dlq-duplicate-last-processed-clock\": String(lastProcessedClock),\n };\n await sendToDlq(envelope.topic, raw, deps, {\n error: new Error(\"Lamport Clock duplicate detected\"),\n attempt: 0,\n originalHeaders: augmentedHeaders,\n });\n } else if (strategy === \"topic\") {\n const destination =\n dedup.options.duplicatesTopic ?? `${envelope.topic}.duplicates`;\n await sendToDuplicatesTopic(envelope.topic, raw, destination, deps, meta);\n }\n // strategy === 'drop': already logged, nothing more to do\n\n return true; // signal: skip this message\n }\n\n dedup.state.set(stateKey, incomingClock);\n return false;\n}\n\n/** Parse, validate and extract an envelope from a single raw Kafka message. Returns null to skip. */\nasync function parseSingleMessage(\n message: {\n value: Buffer | null;\n headers?: Record<string, any>;\n offset: string;\n },\n topic: string,\n partition: number,\n schemaMap: Map<string, SchemaLike>,\n interceptors: ConsumerInterceptor<any>[],\n dlq: boolean,\n deps: MessageHandlerDeps,\n): Promise<EventEnvelope<any> | null> {\n if (!message.value) {\n deps.logger.warn(`Received empty message from topic ${topic}`);\n return null;\n }\n\n const raw = message.value.toString();\n const parsed = parseJsonMessage(raw, topic, deps.logger);\n if (parsed === null) return null;\n\n const headers = decodeHeaders(message.headers);\n const validated = await validateWithSchema(\n parsed,\n raw,\n topic,\n schemaMap,\n interceptors,\n dlq,\n { ...deps, originalHeaders: headers },\n );\n if (validated === null) return null;\n\n return extractEnvelope(validated, headers, topic, partition, message.offset);\n}\n\nexport async function handleEachMessage(\n payload: {\n topic: string;\n partition: number;\n message: {\n value: Buffer | null;\n headers?: Record<string, any>;\n offset: string;\n };\n },\n opts: EachMessageOpts,\n deps: MessageHandlerDeps,\n): Promise<void> {\n const { topic, partition, message } = payload;\n const {\n schemaMap,\n handleMessage,\n interceptors,\n dlq,\n retry,\n retryTopics,\n timeoutMs,\n wrapWithTimeout,\n } = opts;\n\n // Build EOS callbacks when the main consumer runs with autoCommit: false\n // (activated by retryTopics: true in startConsumer).\n const eos = opts.eosMainContext;\n const nextOffsetStr = (parseInt(message.offset, 10) + 1).toString();\n\n // Commit offset manually (used on skip path: empty/invalid/duplicate message).\n const commitOffset = eos\n ? async () => {\n await eos.consumer.commitOffsets([\n { topic, partition, offset: nextOffsetStr },\n ]);\n }\n : undefined;\n\n // EOS routing closure: produce to retry.1 + commit source offset atomically.\n const eosRouteToRetry =\n eos && retry\n ? async (\n rawMsgs: string[],\n envelopes: EventEnvelope<any>[],\n delay: number,\n ) => {\n const { topic: rtTopic, messages: rtMsgs } = buildRetryTopicPayload(\n topic,\n rawMsgs,\n 1,\n retry.maxRetries,\n delay,\n envelopes[0]?.headers ?? {},\n );\n const tx = await eos.txProducer.transaction();\n try {\n await tx.send({ topic: rtTopic, messages: rtMsgs });\n await tx.sendOffsets({\n consumer: eos.consumer,\n topics: [\n {\n topic,\n partitions: [{ partition, offset: nextOffsetStr }],\n },\n ],\n });\n await tx.commit();\n } catch (txErr) {\n try {\n await tx.abort();\n } catch {}\n throw txErr;\n }\n }\n : undefined;\n\n const envelope = await parseSingleMessage(\n message,\n topic,\n partition,\n schemaMap,\n interceptors,\n dlq,\n deps,\n );\n if (envelope === null) {\n await commitOffset?.();\n return;\n }\n\n if (opts.deduplication) {\n const isDuplicate = await applyDeduplication(\n envelope,\n message.value!.toString(),\n opts.deduplication,\n dlq,\n deps,\n );\n if (isDuplicate) {\n await commitOffset?.();\n return;\n }\n }\n\n if (opts.messageTtlMs !== undefined) {\n const ageMs = Date.now() - new Date(envelope.timestamp).getTime();\n if (ageMs > opts.messageTtlMs) {\n deps.logger.warn(\n `[KafkaClient] TTL expired on ${topic}: age ${ageMs}ms > ${opts.messageTtlMs}ms`,\n );\n if (dlq) {\n await sendToDlq(topic, message.value!.toString(), deps, {\n error: new Error(`Message TTL expired: age ${ageMs}ms`),\n attempt: 0,\n originalHeaders: envelope.headers,\n });\n deps.onDlq?.(envelope, \"ttl-expired\");\n } else {\n const ttlHandler = opts.onTtlExpired ?? deps.onTtlExpired;\n await ttlHandler?.({\n topic,\n ageMs,\n messageTtlMs: opts.messageTtlMs!,\n headers: envelope.headers,\n });\n }\n await commitOffset?.();\n return;\n }\n }\n\n await executeWithRetry(\n () => {\n const fn = () =>\n runWithEnvelopeContext(\n {\n correlationId: envelope.correlationId,\n traceparent: envelope.traceparent,\n },\n () => handleMessage(envelope),\n );\n return timeoutMs ? wrapWithTimeout(fn, timeoutMs, topic) : fn();\n },\n {\n envelope,\n rawMessages: [message.value!.toString()],\n interceptors,\n dlq,\n retry,\n retryTopics,\n },\n { ...deps, eosRouteToRetry, eosCommitOnSuccess: commitOffset },\n );\n}\n\nexport type EachBatchOpts = {\n schemaMap: Map<string, SchemaLike>;\n handleBatch: (\n envelopes: EventEnvelope<any>[],\n meta: BatchMeta,\n ) => Promise<void>;\n interceptors: ConsumerInterceptor<any>[];\n dlq: boolean;\n retry: RetryOptions | undefined;\n retryTopics: boolean | undefined;\n timeoutMs: number | undefined;\n wrapWithTimeout: <R>(\n fn: () => Promise<R>,\n ms: number,\n topic: string,\n ) => Promise<R>;\n deduplication?: DeduplicationContext;\n /** Drop messages older than this threshold (ms). See `ConsumerOptions.messageTtlMs`. */\n messageTtlMs?: number;\n /**\n * Per-consumer TTL expiry callback. Takes precedence over the client-level\n * `KafkaClientOptions.onTtlExpired` stored in `deps.onTtlExpired`.\n */\n onTtlExpired?: KafkaClientOptions[\"onTtlExpired\"];\n /**\n * EOS context for batch consumer → retry.1 routing.\n * When set, the batch consumer runs with `autoCommit: false`.\n * On handler failure, all messages are routed to retry.1 and the partition\n * offset is committed atomically in a single Kafka transaction.\n * On success, the offset is committed manually.\n */\n eosMainContext?: {\n txProducer: Producer;\n consumer: Consumer;\n };\n};\n\nexport async function handleEachBatch(\n payload: {\n batch: {\n topic: string;\n partition: number;\n highWatermark: string;\n messages: Array<{\n value: Buffer | null;\n headers?: Record<string, any>;\n offset: string;\n }>;\n };\n heartbeat(): Promise<void>;\n resolveOffset(offset: string): void;\n commitOffsetsIfNecessary(): Promise<void>;\n },\n opts: EachBatchOpts,\n deps: MessageHandlerDeps,\n): Promise<void> {\n const { batch, heartbeat, resolveOffset, commitOffsetsIfNecessary } = payload;\n const {\n schemaMap,\n handleBatch,\n interceptors,\n dlq,\n retry,\n retryTopics,\n timeoutMs,\n wrapWithTimeout,\n } = opts;\n\n // Build EOS callbacks when the batch consumer runs with autoCommit: false.\n // Offset to commit = last message in batch + 1 (all messages in one partition, sequential).\n const eos = opts.eosMainContext;\n const lastRawOffset =\n batch.messages.length > 0\n ? batch.messages[batch.messages.length - 1]!.offset\n : undefined;\n const batchNextOffsetStr = lastRawOffset\n ? (parseInt(lastRawOffset, 10) + 1).toString()\n : undefined;\n\n const commitBatchOffset =\n eos && batchNextOffsetStr\n ? async () => {\n await eos.consumer.commitOffsets([\n {\n topic: batch.topic,\n partition: batch.partition,\n offset: batchNextOffsetStr,\n },\n ]);\n }\n : undefined;\n\n const eosRouteToRetry =\n eos && retry && batchNextOffsetStr\n ? async (\n rawMsgs: string[],\n envelopes: EventEnvelope<any>[],\n delay: number,\n ) => {\n const { topic: rtTopic, messages: rtMsgs } = buildRetryTopicPayload(\n batch.topic,\n rawMsgs,\n 1,\n retry.maxRetries,\n delay,\n envelopes.map((e) => e.headers),\n );\n const tx = await eos.txProducer.transaction();\n try {\n await tx.send({ topic: rtTopic, messages: rtMsgs });\n await tx.sendOffsets({\n consumer: eos.consumer,\n topics: [\n {\n topic: batch.topic,\n partitions: [\n { partition: batch.partition, offset: batchNextOffsetStr },\n ],\n },\n ],\n });\n await tx.commit();\n } catch (txErr) {\n try {\n await tx.abort();\n } catch {}\n throw txErr;\n }\n }\n : undefined;\n\n const envelopes: EventEnvelope<any>[] = [];\n const rawMessages: string[] = [];\n\n for (const message of batch.messages) {\n const envelope = await parseSingleMessage(\n message,\n batch.topic,\n batch.partition,\n schemaMap,\n interceptors,\n dlq,\n deps,\n );\n if (envelope === null) continue;\n\n if (opts.deduplication) {\n const raw = message.value!.toString();\n const isDuplicate = await applyDeduplication(\n envelope,\n raw,\n opts.deduplication,\n dlq,\n deps,\n );\n if (isDuplicate) continue;\n }\n\n if (opts.messageTtlMs !== undefined) {\n const ageMs = Date.now() - new Date(envelope.timestamp).getTime();\n if (ageMs > opts.messageTtlMs) {\n deps.logger.warn(\n `[KafkaClient] TTL expired on ${batch.topic}: age ${ageMs}ms > ${opts.messageTtlMs}ms`,\n );\n if (dlq) {\n await sendToDlq(batch.topic, message.value!.toString(), deps, {\n error: new Error(`Message TTL expired: age ${ageMs}ms`),\n attempt: 0,\n originalHeaders: envelope.headers,\n });\n deps.onDlq?.(envelope, \"ttl-expired\");\n } else {\n const ttlHandler = opts.onTtlExpired ?? deps.onTtlExpired;\n await ttlHandler?.({\n topic: batch.topic,\n ageMs,\n messageTtlMs: opts.messageTtlMs!,\n headers: envelope.headers,\n });\n }\n continue;\n }\n }\n\n envelopes.push(envelope);\n rawMessages.push(message.value!.toString());\n }\n\n if (envelopes.length === 0) {\n // All messages in this batch were filtered (invalid/duplicate).\n // When running EOS, commit the batch offset so the consumer advances.\n await commitBatchOffset?.();\n return;\n }\n\n const meta: BatchMeta = {\n partition: batch.partition,\n highWatermark: batch.highWatermark,\n heartbeat,\n resolveOffset,\n commitOffsetsIfNecessary,\n };\n\n await executeWithRetry(\n () => {\n const fn = () => handleBatch(envelopes, meta);\n return timeoutMs ? wrapWithTimeout(fn, timeoutMs, batch.topic) : fn();\n },\n {\n envelope: envelopes,\n rawMessages,\n interceptors,\n dlq,\n retry,\n isBatch: true,\n retryTopics,\n },\n { ...deps, eosRouteToRetry, eosCommitOnSuccess: commitBatchOffset },\n );\n}\n","import type { KafkaJS } from \"@confluentinc/kafka-javascript\";\nimport type { KafkaLogger, SubscribeRetryOptions } from \"../types\";\nimport { toError, sleep } from \"./pipeline\";\n\nexport async function subscribeWithRetry(\n consumer: KafkaJS.Consumer,\n topics: (string | RegExp)[],\n logger: KafkaLogger,\n retryOpts?: SubscribeRetryOptions,\n): Promise<void> {\n const maxAttempts = retryOpts?.retries ?? 5;\n const backoffMs = retryOpts?.backoffMs ?? 5000;\n const displayTopics = topics\n .map((t) => (t instanceof RegExp ? t.toString() : t))\n .join(\", \");\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await consumer.subscribe({ topics });\n return;\n } catch (error) {\n if (attempt === maxAttempts) throw error;\n const msg = toError(error).message;\n const delay = Math.floor(Math.random() * backoffMs);\n logger.warn(\n `Failed to subscribe to [${displayTopics}] (attempt ${attempt}/${maxAttempts}): ${msg}. Retrying in ${delay}ms...`,\n );\n await sleep(delay);\n }\n }\n}\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Consumer = KafkaJS.Consumer;\ntype Producer = KafkaJS.Producer;\nimport {\n decodeHeaders,\n extractEnvelope,\n runWithEnvelopeContext,\n} from \"../message/envelope\";\nimport type { EventEnvelope } from \"../message/envelope\";\nimport {\n parseJsonMessage,\n validateWithSchema,\n runHandlerWithPipeline,\n notifyInterceptorsOnError,\n buildDlqPayload,\n buildRetryTopicPayload,\n sleep,\n toError,\n RETRY_HEADER_AFTER,\n RETRY_HEADER_MAX_RETRIES,\n RETRY_HEADER_ORIGINAL_TOPIC,\n} from \"../consumer/pipeline\";\nimport { KafkaRetryExhaustedError } from \"../errors\";\nimport { subscribeWithRetry } from \"../consumer/subscribe-retry\";\nimport type { SchemaLike } from \"../message/topic\";\nimport type {\n ConsumerInterceptor,\n DlqReason,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n} from \"../types\";\n\nexport type RetryTopicDeps = {\n logger: KafkaLogger;\n producer: Producer;\n instrumentation: KafkaInstrumentation[];\n onMessageLost: KafkaClientOptions[\"onMessageLost\"];\n onRetry?: (\n envelope: EventEnvelope<any>,\n attempt: number,\n maxRetries: number,\n ) => void;\n onDlq?: (envelope: EventEnvelope<any>, reason: DlqReason) => void;\n onMessage?: (envelope: EventEnvelope<any>) => void;\n ensureTopic: (topic: string) => Promise<void>;\n getOrCreateConsumer: (\n groupId: string,\n fromBeginning: boolean,\n autoCommit: boolean,\n ) => Consumer;\n runningConsumers: Map<string, \"eachMessage\" | \"eachBatch\">;\n /** Factory that creates and connects a transactional producer for EOS routing. */\n createRetryTxProducer: (transactionalId: string) => Promise<Producer>;\n};\n\n/**\n * Poll `consumer.assignment()` until the consumer has received at least one\n * partition for the given topics, then return. Logs a warning and returns\n * (rather than throwing) on timeout so that a slow broker does not break\n * the caller.\n */\nexport async function waitForPartitionAssignment(\n consumer: Consumer,\n topics: string[],\n logger: KafkaLogger,\n timeoutMs = 10_000,\n): Promise<void> {\n const topicSet = new Set(topics);\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n try {\n const assigned: { topic: string; partition: number }[] =\n consumer.assignment();\n if (assigned.some((a) => topicSet.has(a.topic))) return;\n } catch {\n // consumer.assignment() throws if not yet in CONNECTED state — keep polling\n }\n await sleep(200);\n }\n logger.warn(\n `Retry consumer did not receive partition assignments for [${topics.join(\", \")}] within ${timeoutMs}ms`,\n );\n}\n\n/**\n * Start a single retry level consumer on `<topic>.retry.<level>`.\n *\n * Exactly-once routing guarantee (EOS):\n * - The partition is paused while waiting for the scheduled delay window.\n * The offset is NOT committed during this window, so a process crash\n * causes the message to be redelivered on restart.\n * - On success: offset committed directly via `consumer.commitOffsets`.\n * - On failure (routing to next level or DLQ): produce + offset-commit are\n * wrapped in a single Kafka transaction via `sendOffsetsToTransaction`.\n * A crash at any point rolls back the transaction — no duplicate is\n * produced in the next level or DLQ.\n * - If the EOS transaction itself fails (broker unavailable), the offset is\n * NOT committed and the message is redelivered, retrying the transaction.\n *\n * Flow per message:\n * 1. If `x-retry-after` is in the future: pause partition → sleep → resume.\n * 2. Validate and call the original handler.\n * 3. On success: commit offset.\n * 4. On failure:\n * - Not exhausted → EOS tx: produce to `<topic>.retry.<level+1>` + commit offset\n * - Exhausted + dlq → EOS tx: produce to `<topic>.dlq` + commit offset\n * - Exhausted, no dlq → call `onMessageLost` + commit offset directly\n */\nasync function startLevelConsumer(\n level: number,\n levelTopics: string[],\n levelGroupId: string,\n originalTopics: string[],\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n retry: { maxRetries: number; backoffMs?: number; maxBackoffMs?: number },\n dlq: boolean,\n interceptors: ConsumerInterceptor<any>[],\n schemaMap: Map<string, SchemaLike>,\n deps: RetryTopicDeps,\n assignmentTimeoutMs?: number,\n): Promise<void> {\n const {\n logger,\n producer,\n instrumentation,\n onMessageLost,\n onRetry,\n onDlq,\n onMessage,\n ensureTopic,\n getOrCreateConsumer,\n runningConsumers,\n createRetryTxProducer,\n } = deps;\n\n const backoffMs = retry.backoffMs ?? 1_000;\n const maxBackoffMs = retry.maxBackoffMs ?? 30_000;\n const pipelineDeps = { logger, producer, instrumentation, onMessageLost };\n\n for (const lt of levelTopics) {\n await ensureTopic(lt);\n }\n\n // One transactional producer per level — routes messages to the next level or DLQ\n // atomically with the consumer offset commit (EOS).\n const levelTxProducer = await createRetryTxProducer(`${levelGroupId}-tx`);\n\n // autoCommit: false — offsets are committed manually after processing.\n const consumer = getOrCreateConsumer(levelGroupId, false, false);\n await consumer.connect();\n await subscribeWithRetry(consumer, levelTopics, logger);\n\n await consumer.run({\n eachMessage: async ({ topic: levelTopic, partition, message }) => {\n const nextOffset = {\n topic: levelTopic,\n partition,\n offset: (parseInt(message.offset, 10) + 1).toString(),\n };\n\n if (!message.value) {\n await consumer.commitOffsets([nextOffset]);\n return;\n }\n\n const headers = decodeHeaders(message.headers);\n const retryAfter = parseInt(\n (headers[RETRY_HEADER_AFTER] as string | undefined) ?? \"0\",\n 10,\n );\n const remaining = retryAfter - Date.now();\n\n // Pause this partition for the scheduled delay.\n // The offset is not committed yet — a crash here causes redelivery (at-least-once).\n if (remaining > 0) {\n consumer.pause([{ topic: levelTopic, partitions: [partition] }]);\n await sleep(remaining);\n consumer.resume([{ topic: levelTopic, partitions: [partition] }]);\n }\n\n const raw = message.value.toString();\n const parsed = parseJsonMessage(raw, levelTopic, logger);\n if (parsed === null) {\n await consumer.commitOffsets([nextOffset]);\n return;\n }\n\n const currentMaxRetries = parseInt(\n (headers[RETRY_HEADER_MAX_RETRIES] as string | undefined) ??\n String(retry.maxRetries),\n 10,\n );\n const originalTopic =\n (headers[RETRY_HEADER_ORIGINAL_TOPIC] as string | undefined) ??\n levelTopic.replace(/\\.retry\\.\\d+$/, \"\");\n\n const validated = await validateWithSchema(\n parsed,\n raw,\n originalTopic,\n schemaMap,\n interceptors,\n dlq,\n { ...pipelineDeps, originalHeaders: headers },\n );\n if (validated === null) {\n await consumer.commitOffsets([nextOffset]);\n return;\n }\n\n const envelope = extractEnvelope(\n validated,\n headers,\n originalTopic,\n partition,\n message.offset,\n );\n\n const error = await runHandlerWithPipeline(\n () =>\n runWithEnvelopeContext(\n {\n correlationId: envelope.correlationId,\n traceparent: envelope.traceparent,\n },\n () => handleMessage(envelope),\n ),\n [envelope],\n interceptors,\n instrumentation,\n );\n\n if (!error) {\n onMessage?.(envelope);\n await consumer.commitOffsets([nextOffset]);\n return;\n }\n\n const exhausted = level >= currentMaxRetries;\n const reportedError =\n exhausted && currentMaxRetries > 1\n ? new KafkaRetryExhaustedError(\n originalTopic,\n [envelope.payload],\n currentMaxRetries,\n { cause: error },\n )\n : error;\n\n await notifyInterceptorsOnError([envelope], interceptors, reportedError);\n\n logger.error(\n `Retry consumer error for ${originalTopic} (level ${level}/${currentMaxRetries}):`,\n error.stack,\n );\n\n if (!exhausted) {\n const nextLevel = level + 1;\n // Exponent = current level: level 1 → delay cap = backoffMs * 2^1, etc.\n const cap = Math.min(backoffMs * 2 ** level, maxBackoffMs);\n const delay = Math.floor(Math.random() * cap);\n const { topic: rtTopic, messages: rtMsgs } = buildRetryTopicPayload(\n originalTopic,\n [raw],\n nextLevel,\n currentMaxRetries,\n delay,\n headers,\n );\n // EOS: produce to next retry level + commit source offset atomically.\n // A crash at any point rolls back the transaction — no duplicate.\n const tx = await levelTxProducer.transaction();\n try {\n await tx.send({ topic: rtTopic, messages: rtMsgs });\n await tx.sendOffsets({\n consumer,\n topics: [\n {\n topic: nextOffset.topic,\n partitions: [\n {\n partition: nextOffset.partition,\n offset: nextOffset.offset,\n },\n ],\n },\n ],\n });\n await tx.commit();\n logger.warn(\n `Message routed to ${rtTopic} (EOS, level ${nextLevel}/${currentMaxRetries})`,\n );\n onRetry?.(envelope, nextLevel, currentMaxRetries);\n } catch (txErr) {\n try {\n await tx.abort();\n } catch {}\n logger.error(\n `EOS routing to ${rtTopic} failed — message will be redelivered:`,\n toError(txErr).stack,\n );\n // Don't commit offset — message is redelivered and the tx is retried.\n return;\n }\n } else if (dlq) {\n const { topic: dTopic, messages: dMsgs } = buildDlqPayload(\n originalTopic,\n raw,\n {\n error,\n // +1 to account for the main consumer's initial attempt before routing.\n attempt: level + 1,\n originalHeaders: headers,\n },\n );\n // EOS: produce to DLQ + commit source offset atomically.\n const tx = await levelTxProducer.transaction();\n try {\n await tx.send({ topic: dTopic, messages: dMsgs });\n await tx.sendOffsets({\n consumer,\n topics: [\n {\n topic: nextOffset.topic,\n partitions: [\n {\n partition: nextOffset.partition,\n offset: nextOffset.offset,\n },\n ],\n },\n ],\n });\n await tx.commit();\n logger.warn(`Message sent to DLQ: ${dTopic} (EOS)`);\n onDlq?.(envelope, \"handler-error\");\n } catch (txErr) {\n try {\n await tx.abort();\n } catch {}\n logger.error(\n `EOS DLQ routing to ${dTopic} failed — message will be redelivered:`,\n toError(txErr).stack,\n );\n // Don't commit offset — message stays in retry chain, DLQ tx retried on next delivery.\n return;\n }\n } else {\n // No DLQ — notify caller and commit offset directly (no routing produce, no EOS needed).\n await onMessageLost?.({\n topic: originalTopic,\n error,\n attempt: level,\n headers,\n });\n await consumer.commitOffsets([nextOffset]);\n }\n },\n });\n\n runningConsumers.set(levelGroupId, \"eachMessage\");\n\n await waitForPartitionAssignment(\n consumer,\n levelTopics,\n logger,\n assignmentTimeoutMs,\n );\n\n logger.log(\n `Retry level ${level}/${retry.maxRetries} consumer started for: ${originalTopics.join(\", \")} (group: ${levelGroupId})`,\n );\n}\n\n/**\n * Start one consumer per retry level on `<topic>.retry.<level>` topics.\n *\n * With `maxRetries: N`, creates N consumers:\n * - `${groupId}-retry.1` → `<topic>.retry.1`\n * - `${groupId}-retry.2` → `<topic>.retry.2`\n * - …\n * - `${groupId}-retry.N` → `<topic>.retry.N`\n *\n * Each level consumer uses pause/sleep/resume to honour the scheduled delay\n * without committing the offset early. Failed message routing (to the next\n * level or DLQ) is wrapped in a Kafka transaction via `sendOffsetsToTransaction`\n * for exactly-once routing semantics — no duplicates even if the process\n * crashes between the produce and the offset commit.\n *\n * Returns the list of started consumer group IDs so the caller can stop\n * them selectively via `stopConsumer`.\n */\nexport async function startRetryTopicConsumers(\n originalTopics: string[],\n originalGroupId: string,\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n retry: { maxRetries: number; backoffMs?: number; maxBackoffMs?: number },\n dlq: boolean,\n interceptors: ConsumerInterceptor<any>[],\n schemaMap: Map<string, SchemaLike>,\n deps: RetryTopicDeps,\n assignmentTimeoutMs?: number,\n): Promise<string[]> {\n const levelGroupIds: string[] = [];\n\n for (let level = 1; level <= retry.maxRetries; level++) {\n const levelTopics = originalTopics.map((t) => `${t}.retry.${level}`);\n const levelGroupId = `${originalGroupId}-retry.${level}`;\n\n await startLevelConsumer(\n level,\n levelTopics,\n levelGroupId,\n originalTopics,\n handleMessage,\n retry,\n dlq,\n interceptors,\n schemaMap,\n deps,\n assignmentTimeoutMs,\n );\n\n levelGroupIds.push(levelGroupId);\n }\n\n return levelGroupIds;\n}\n","import type { MessageHeaders } from \"../types\";\n\n/**\n * Context passed as the second argument to `SchemaLike.parse()`.\n * Enables schema-registry adapters, version-aware migration, and\n * header-driven parsing without coupling validators to Kafka internals.\n *\n * All fields are optional-friendly — validators that don't need the context\n * can simply ignore the second argument.\n */\nexport interface SchemaParseContext {\n /** Topic the message was produced to / consumed from. */\n topic: string;\n /** Decoded message headers (envelope headers included). */\n headers: MessageHeaders;\n /** Value of the `x-schema-version` header, defaults to `1`. */\n version: number;\n}\n\n/**\n * Any validation library with a `.parse()` method.\n * Works with Zod, Valibot, ArkType, or any custom validator.\n *\n * The optional `ctx` argument carries topic/header/version metadata so\n * validators can perform schema-registry lookups or version-aware migrations.\n * Existing validators that only use the first argument continue to work\n * unchanged — the second argument is silently ignored.\n *\n * @example\n * ```ts\n * import { z } from 'zod';\n * const schema: SchemaLike<{ id: string }> = z.object({ id: z.string() });\n *\n * // Context-aware validator:\n * const schema: SchemaLike<MyType> = {\n * parse(data, ctx) {\n * const version = ctx?.version ?? 1;\n * return version >= 2 ? migrateV1toV2(data) : validateV1(data);\n * }\n * };\n * ```\n */\nexport interface SchemaLike<T = any> {\n parse(data: unknown, ctx?: SchemaParseContext): T | Promise<T>;\n}\n\n/** Infer the output type from a SchemaLike. */\nexport type InferSchema<S extends SchemaLike> =\n S extends SchemaLike<infer T> ? T : never;\n\n/**\n * A typed topic descriptor that pairs a topic name with its message type.\n * Created via the `topic()` factory function.\n *\n * @typeParam N - The literal topic name string.\n * @typeParam M - The message payload type for this topic.\n */\nexport interface TopicDescriptor<\n N extends string = string,\n M extends Record<string, any> = Record<string, any>,\n> {\n readonly __topic: N;\n /** @internal Phantom type — never has a real value at runtime. */\n readonly __type: M;\n /** Runtime schema validator. Present only when created via `topic().schema()`. */\n readonly __schema?: SchemaLike<M>;\n}\n\n/**\n * Define a typed topic descriptor.\n *\n * @example\n * ```ts\n * // Without schema — explicit type via .type<T>():\n * const OrderCreated = topic('order.created').type<{ orderId: string; amount: number }>();\n *\n * // With schema — type inferred from schema:\n * const OrderCreated = topic('order.created').schema(z.object({\n * orderId: z.string(),\n * amount: z.number(),\n * }));\n *\n * // Use with KafkaClient:\n * await kafka.sendMessage(OrderCreated, { orderId: '123', amount: 100 });\n *\n * // Use with @SubscribeTo:\n * @SubscribeTo(OrderCreated)\n * async handleOrder(msg) { ... }\n * ```\n */\nexport function topic<N extends string>(name: N) {\n return {\n /** Provide an explicit message type without a runtime schema. */\n type: <M extends Record<string, any>>(): TopicDescriptor<N, M> => ({\n __topic: name,\n __type: undefined as unknown as M,\n }),\n\n schema: <S extends SchemaLike<Record<string, any>>>(\n schema: S,\n ): TopicDescriptor<N, InferSchema<S>> => ({\n __topic: name,\n __type: undefined as unknown as InferSchema<S>,\n __schema: schema as unknown as SchemaLike<InferSchema<S>>,\n }),\n };\n}\n\n/**\n * Build a topic-message map type from a union of TopicDescriptors.\n *\n * @example\n * ```ts\n * const OrderCreated = topic('order.created').type<{ orderId: string }>();\n * const OrderCompleted = topic('order.completed').type<{ completedAt: string }>();\n *\n * type MyTopics = TopicsFrom<typeof OrderCreated | typeof OrderCompleted>;\n * // { 'order.created': { orderId: string }; 'order.completed': { completedAt: string } }\n * ```\n */\nexport type TopicsFrom<D extends TopicDescriptor<any, any>> = {\n [K in D as K[\"__topic\"]]: K[\"__type\"];\n};\n"],"mappings":"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","names":["import_kafka_javascript","topic","topic","topic","topic","topic","envelopes","getOrCreateConsumer","topic"]}