@drarzter/kafka-client 0.9.3 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/README.md +625 -8
  2. package/dist/chunk-CMO7SMVK.mjs +4814 -0
  3. package/dist/chunk-CMO7SMVK.mjs.map +1 -0
  4. package/dist/cli/dlq.d.ts +119 -0
  5. package/dist/cli/dlq.d.ts.map +1 -0
  6. package/dist/cli/index.d.ts +3 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/{chunk-TPIP5VV7.mjs → cli/index.js} +965 -265
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/cli/index.mjs +355 -0
  11. package/dist/cli/index.mjs.map +1 -0
  12. package/dist/client/config/from-env.d.ts +188 -0
  13. package/dist/client/config/from-env.d.ts.map +1 -0
  14. package/dist/client/config/index.d.ts +2 -0
  15. package/dist/client/config/index.d.ts.map +1 -0
  16. package/dist/client/errors.d.ts +67 -0
  17. package/dist/client/errors.d.ts.map +1 -0
  18. package/dist/client/kafka.client/admin/ops.d.ts +114 -0
  19. package/dist/client/kafka.client/admin/ops.d.ts.map +1 -0
  20. package/dist/client/kafka.client/consumer/features/delayed.d.ts +24 -0
  21. package/dist/client/kafka.client/consumer/features/delayed.d.ts.map +1 -0
  22. package/dist/client/kafka.client/consumer/features/dlq-replay.d.ts +52 -0
  23. package/dist/client/kafka.client/consumer/features/dlq-replay.d.ts.map +1 -0
  24. package/dist/client/kafka.client/consumer/features/routed.d.ts +4 -0
  25. package/dist/client/kafka.client/consumer/features/routed.d.ts.map +1 -0
  26. package/dist/client/kafka.client/consumer/features/snapshot.d.ts +10 -0
  27. package/dist/client/kafka.client/consumer/features/snapshot.d.ts.map +1 -0
  28. package/dist/client/kafka.client/consumer/features/window.d.ts +5 -0
  29. package/dist/client/kafka.client/consumer/features/window.d.ts.map +1 -0
  30. package/dist/client/kafka.client/consumer/handler.d.ts +149 -0
  31. package/dist/client/kafka.client/consumer/handler.d.ts.map +1 -0
  32. package/dist/client/kafka.client/consumer/ops.d.ts +51 -0
  33. package/dist/client/kafka.client/consumer/ops.d.ts.map +1 -0
  34. package/dist/client/kafka.client/consumer/pipeline.d.ts +167 -0
  35. package/dist/client/kafka.client/consumer/pipeline.d.ts.map +1 -0
  36. package/dist/client/kafka.client/consumer/queue.d.ts +37 -0
  37. package/dist/client/kafka.client/consumer/queue.d.ts.map +1 -0
  38. package/dist/client/kafka.client/consumer/retry-topic.d.ts +65 -0
  39. package/dist/client/kafka.client/consumer/retry-topic.d.ts.map +1 -0
  40. package/dist/client/kafka.client/consumer/setup.d.ts +63 -0
  41. package/dist/client/kafka.client/consumer/setup.d.ts.map +1 -0
  42. package/dist/client/kafka.client/consumer/start.d.ts +7 -0
  43. package/dist/client/kafka.client/consumer/start.d.ts.map +1 -0
  44. package/dist/client/kafka.client/consumer/stop.d.ts +19 -0
  45. package/dist/client/kafka.client/consumer/stop.d.ts.map +1 -0
  46. package/dist/client/kafka.client/consumer/subscribe-retry.d.ts +4 -0
  47. package/dist/client/kafka.client/consumer/subscribe-retry.d.ts.map +1 -0
  48. package/dist/client/kafka.client/context.d.ts +72 -0
  49. package/dist/client/kafka.client/context.d.ts.map +1 -0
  50. package/dist/client/kafka.client/index.d.ts +155 -0
  51. package/dist/client/kafka.client/index.d.ts.map +1 -0
  52. package/dist/client/kafka.client/infra/circuit-breaker.manager.d.ts +61 -0
  53. package/dist/client/kafka.client/infra/circuit-breaker.manager.d.ts.map +1 -0
  54. package/dist/client/kafka.client/infra/dedup.store.d.ts +28 -0
  55. package/dist/client/kafka.client/infra/dedup.store.d.ts.map +1 -0
  56. package/dist/client/kafka.client/infra/inflight.tracker.d.ts +22 -0
  57. package/dist/client/kafka.client/infra/inflight.tracker.d.ts.map +1 -0
  58. package/dist/client/kafka.client/infra/metrics.manager.d.ts +67 -0
  59. package/dist/client/kafka.client/infra/metrics.manager.d.ts.map +1 -0
  60. package/dist/client/kafka.client/producer/lifecycle.d.ts +41 -0
  61. package/dist/client/kafka.client/producer/lifecycle.d.ts.map +1 -0
  62. package/dist/client/kafka.client/producer/ops.d.ts +70 -0
  63. package/dist/client/kafka.client/producer/ops.d.ts.map +1 -0
  64. package/dist/client/kafka.client/producer/send.d.ts +21 -0
  65. package/dist/client/kafka.client/producer/send.d.ts.map +1 -0
  66. package/dist/client/kafka.client/validate-options.d.ts +11 -0
  67. package/dist/client/kafka.client/validate-options.d.ts.map +1 -0
  68. package/dist/client/message/envelope.d.ts +105 -0
  69. package/dist/client/message/envelope.d.ts.map +1 -0
  70. package/dist/client/message/schema-registry.d.ts +105 -0
  71. package/dist/client/message/schema-registry.d.ts.map +1 -0
  72. package/dist/client/message/topic.d.ts +138 -0
  73. package/dist/client/message/topic.d.ts.map +1 -0
  74. package/dist/client/message/versioned-schema.d.ts +53 -0
  75. package/dist/client/message/versioned-schema.d.ts.map +1 -0
  76. package/dist/client/outbox/index.d.ts +4 -0
  77. package/dist/client/outbox/index.d.ts.map +1 -0
  78. package/dist/client/outbox/outbox.relay.d.ts +90 -0
  79. package/dist/client/outbox/outbox.relay.d.ts.map +1 -0
  80. package/dist/client/outbox/outbox.store.d.ts +42 -0
  81. package/dist/client/outbox/outbox.store.d.ts.map +1 -0
  82. package/dist/client/outbox/outbox.types.d.ts +144 -0
  83. package/dist/client/outbox/outbox.types.d.ts.map +1 -0
  84. package/dist/client/security/acl.d.ts +108 -0
  85. package/dist/client/security/acl.d.ts.map +1 -0
  86. package/dist/client/security/index.d.ts +5 -0
  87. package/dist/client/security/index.d.ts.map +1 -0
  88. package/dist/client/security/providers.d.ts +88 -0
  89. package/dist/client/security/providers.d.ts.map +1 -0
  90. package/dist/client/security/resolve-security.d.ts +19 -0
  91. package/dist/client/security/resolve-security.d.ts.map +1 -0
  92. package/dist/client/security/security.types.d.ts +76 -0
  93. package/dist/client/security/security.types.d.ts.map +1 -0
  94. package/dist/client/transport/confluent.transport.d.ts +32 -0
  95. package/dist/client/transport/confluent.transport.d.ts.map +1 -0
  96. package/dist/client/transport/transport.interface.d.ts +216 -0
  97. package/dist/client/transport/transport.interface.d.ts.map +1 -0
  98. package/dist/client/types/admin.interface.d.ts +174 -0
  99. package/dist/client/types/admin.interface.d.ts.map +1 -0
  100. package/dist/client/types/admin.types.d.ts +140 -0
  101. package/dist/client/types/admin.types.d.ts.map +1 -0
  102. package/dist/client/types/client.d.ts +21 -0
  103. package/dist/client/types/client.d.ts.map +1 -0
  104. package/dist/client/types/common.d.ts +84 -0
  105. package/dist/client/types/common.d.ts.map +1 -0
  106. package/dist/client/types/config.types.d.ts +150 -0
  107. package/dist/client/types/config.types.d.ts.map +1 -0
  108. package/dist/client/types/consumer.interface.d.ts +115 -0
  109. package/dist/client/types/consumer.interface.d.ts.map +1 -0
  110. package/dist/{consumer.types-fFCag3VJ.d.mts → client/types/consumer.types.d.ts} +62 -383
  111. package/dist/client/types/consumer.types.d.ts.map +1 -0
  112. package/dist/client/types/dedup.types.d.ts +50 -0
  113. package/dist/client/types/dedup.types.d.ts.map +1 -0
  114. package/dist/client/types/lifecycle.interface.d.ts +72 -0
  115. package/dist/client/types/lifecycle.interface.d.ts.map +1 -0
  116. package/dist/client/types/producer.interface.d.ts +52 -0
  117. package/dist/client/types/producer.interface.d.ts.map +1 -0
  118. package/dist/client/types/producer.types.d.ts +90 -0
  119. package/dist/client/types/producer.types.d.ts.map +1 -0
  120. package/dist/client/types.d.ts +8 -0
  121. package/dist/client/types.d.ts.map +1 -0
  122. package/dist/core.d.ts +10 -314
  123. package/dist/core.d.ts.map +1 -0
  124. package/dist/core.js +1326 -74
  125. package/dist/core.js.map +1 -1
  126. package/dist/core.mjs +39 -3
  127. package/dist/index.d.ts +7 -128
  128. package/dist/index.d.ts.map +1 -0
  129. package/dist/index.js +1343 -74
  130. package/dist/index.js.map +1 -1
  131. package/dist/index.mjs +56 -3
  132. package/dist/index.mjs.map +1 -1
  133. package/dist/nest/kafka.constants.d.ts +5 -0
  134. package/dist/nest/kafka.constants.d.ts.map +1 -0
  135. package/dist/nest/kafka.decorator.d.ts +49 -0
  136. package/dist/nest/kafka.decorator.d.ts.map +1 -0
  137. package/dist/nest/kafka.explorer.d.ts +17 -0
  138. package/dist/nest/kafka.explorer.d.ts.map +1 -0
  139. package/dist/nest/kafka.health.d.ts +7 -0
  140. package/dist/nest/kafka.health.d.ts.map +1 -0
  141. package/dist/nest/kafka.module.d.ts +61 -0
  142. package/dist/nest/kafka.module.d.ts.map +1 -0
  143. package/dist/otel.d.ts +83 -5
  144. package/dist/otel.d.ts.map +1 -0
  145. package/dist/otel.js +100 -6
  146. package/dist/otel.js.map +1 -1
  147. package/dist/otel.mjs +98 -5
  148. package/dist/otel.mjs.map +1 -1
  149. package/dist/testing/client.mock.d.ts +47 -0
  150. package/dist/testing/client.mock.d.ts.map +1 -0
  151. package/dist/testing/index.d.ts +4 -0
  152. package/dist/testing/index.d.ts.map +1 -0
  153. package/dist/testing/test.container.d.ts +63 -0
  154. package/dist/testing/test.container.d.ts.map +1 -0
  155. package/dist/{testing.d.mts → testing/transport.fake.d.ts} +7 -111
  156. package/dist/testing/transport.fake.d.ts.map +1 -0
  157. package/dist/testing.d.ts +2 -318
  158. package/dist/testing.d.ts.map +1 -0
  159. package/dist/testing.js +28 -2
  160. package/dist/testing.js.map +1 -1
  161. package/dist/testing.mjs +28 -2
  162. package/dist/testing.mjs.map +1 -1
  163. package/package.json +22 -9
  164. package/dist/chunk-TPIP5VV7.mjs.map +0 -1
  165. package/dist/client-CBBUDDtu.d.ts +0 -751
  166. package/dist/client-D-SxYV2b.d.mts +0 -751
  167. package/dist/consumer.types-fFCag3VJ.d.ts +0 -958
  168. package/dist/core.d.mts +0 -314
  169. package/dist/index.d.mts +0 -128
  170. package/dist/otel.d.mts +0 -27
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/client/kafka.client/confluent-transport.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/kafka.client/admin/ops.ts","../src/client/kafka.client/consumer/pipeline.ts","../src/client/kafka.client/consumer/subscribe-retry.ts","../src/client/kafka.client/consumer/dlq-replay.ts","../src/client/kafka.client/infra/metrics.manager.ts","../src/client/kafka.client/infra/inflight.tracker.ts","../src/client/kafka.client/infra/circuit-breaker.manager.ts","../src/client/kafka.client/consumer/queue.ts","../src/client/kafka.client/producer/lifecycle.ts","../src/client/kafka.client/producer/send.ts","../src/client/kafka.client/consumer/retry-topic.ts","../src/client/kafka.client/consumer/setup.ts","../src/client/kafka.client/consumer/handler.ts","../src/client/kafka.client/consumer/stop.ts","../src/client/kafka.client/consumer/start.ts","../src/client/kafka.client/consumer/window.ts","../src/client/kafka.client/consumer/routed.ts","../src/client/kafka.client/consumer/snapshot.ts","../src/client/kafka.client/index.ts","../src/client/message/topic.ts","../src/nest/kafka.module.ts","../src/nest/kafka.constants.ts","../src/nest/kafka.explorer.ts","../src/nest/kafka.decorator.ts","../src/nest/kafka.health.ts"],"sourcesContent":["export * from \"./core\";\nexport * from \"./nest/kafka.module\";\nexport * from \"./nest/kafka.constants\";\nexport * from \"./nest/kafka.decorator\";\nexport * from \"./nest/kafka.explorer\";\nexport * from \"./nest/kafka.health\";\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\nconst { Kafka: KafkaClass, logLevel: KafkaLogLevel, PartitionAssigners } = KafkaJS;\n\nimport type {\n KafkaTransport,\n IProducer,\n IConsumer,\n IAdmin,\n ITransaction,\n IProducerRecord,\n IProducerCreationOptions,\n IConsumerCreationOptions,\n ITopicPartition,\n ITopicPartitions,\n ITopicPartitionOffset,\n IConsumerRunConfig,\n IPartitionWatermarks,\n IPartitionOffset,\n IGroupTopicOffsets,\n IGroupDescription,\n ITopicMetadata,\n} from \"../transport.interface\";\n\n// ── ConfluentTransaction ──────────────────────────────────────────────────────\n\nclass ConfluentTransaction implements ITransaction {\n constructor(private readonly tx: KafkaJS.Transaction) {}\n\n async send(record: IProducerRecord): Promise<void> {\n await this.tx.send(record as any);\n }\n\n async sendOffsets(options: {\n consumer: IConsumer;\n topics: Array<{\n topic: string;\n partitions: Array<{ partition: number; offset: string }>;\n }>;\n }): Promise<void> {\n // Unwrap the ConfluentConsumer to get the native KafkaJS.Consumer for sendOffsets\n const nativeConsumer = (options.consumer as ConfluentConsumer).getNative();\n await this.tx.sendOffsets({\n consumer: nativeConsumer,\n topics: options.topics,\n } as any);\n }\n\n async commit(): Promise<void> {\n await this.tx.commit();\n }\n\n async abort(): Promise<void> {\n await this.tx.abort();\n }\n}\n\n// ── ConfluentProducer ─────────────────────────────────────────────────────────\n\nclass ConfluentProducer implements IProducer {\n constructor(private readonly producer: KafkaJS.Producer) {}\n\n async connect(): Promise<void> {\n await this.producer.connect();\n }\n\n async disconnect(): Promise<void> {\n await this.producer.disconnect();\n }\n\n async send(record: IProducerRecord): Promise<void> {\n await this.producer.send(record as any);\n }\n\n async transaction(): Promise<ITransaction> {\n const tx = await this.producer.transaction();\n return new ConfluentTransaction(tx);\n }\n}\n\n// ── ConfluentConsumer ─────────────────────────────────────────────────────────\n\nexport class ConfluentConsumer implements IConsumer {\n constructor(private readonly consumer: KafkaJS.Consumer) {}\n\n /** Returns the underlying KafkaJS.Consumer — used by ConfluentTransaction.sendOffsets. */\n getNative(): KafkaJS.Consumer {\n return this.consumer;\n }\n\n async connect(): Promise<void> {\n await this.consumer.connect();\n }\n\n async disconnect(): Promise<void> {\n await this.consumer.disconnect();\n }\n\n async subscribe(options: { topics: (string | RegExp)[] }): Promise<void> {\n await this.consumer.subscribe(options as any);\n }\n\n async run(config: IConsumerRunConfig): Promise<void> {\n await this.consumer.run(config as any);\n }\n\n pause(assignments: ITopicPartitions[]): void {\n this.consumer.pause(assignments as any);\n }\n\n resume(assignments: ITopicPartitions[]): void {\n this.consumer.resume(assignments as any);\n }\n\n seek(options: ITopicPartitionOffset): void {\n this.consumer.seek(options as any);\n }\n\n assignment(): ITopicPartition[] {\n return (this.consumer as any).assignment() as ITopicPartition[];\n }\n\n async commitOffsets(offsets: ITopicPartitionOffset[]): Promise<void> {\n await this.consumer.commitOffsets(offsets as any);\n }\n\n async stop(): Promise<void> {\n await (this.consumer as any).stop?.();\n }\n}\n\n// ── ConfluentAdmin ────────────────────────────────────────────────────────────\n\nclass ConfluentAdmin implements IAdmin {\n constructor(private readonly admin: KafkaJS.Admin) {}\n\n async connect(): Promise<void> {\n await this.admin.connect();\n }\n\n async disconnect(): Promise<void> {\n await this.admin.disconnect();\n }\n\n async createTopics(options: {\n topics: Array<{ topic: string; numPartitions: number }>;\n }): Promise<void> {\n await this.admin.createTopics(options as any);\n }\n\n async fetchTopicOffsets(topic: string): Promise<IPartitionWatermarks[]> {\n return this.admin.fetchTopicOffsets(topic) as any;\n }\n\n async fetchTopicOffsetsByTimestamp(\n topic: string,\n timestamp: number,\n ): Promise<IPartitionOffset[]> {\n return (this.admin as any).fetchTopicOffsetsByTime(topic, timestamp);\n }\n\n async fetchOffsets(options: {\n groupId: string;\n }): Promise<IGroupTopicOffsets[]> {\n return this.admin.fetchOffsets(options as any) as any;\n }\n\n async setOffsets(options: {\n groupId: string;\n topic: string;\n partitions: IPartitionOffset[];\n }): Promise<void> {\n await (this.admin as any).setOffsets(options);\n }\n\n async listTopics(): Promise<string[]> {\n return this.admin.listTopics();\n }\n\n async listGroups(): Promise<{ groups: IGroupDescription[] }> {\n return this.admin.listGroups() as any;\n }\n\n async fetchTopicMetadata(options?: {\n topics?: string[];\n }): Promise<{ topics: ITopicMetadata[] }> {\n return (this.admin as any).fetchTopicMetadata(options);\n }\n\n async deleteGroups(groupIds: string[]): Promise<void> {\n await (this.admin as any).deleteGroups(groupIds);\n }\n\n async deleteTopicRecords(options: {\n topic: string;\n partitions: IPartitionOffset[];\n }): Promise<void> {\n await this.admin.deleteTopicRecords(options as any);\n }\n}\n\n// ── ConfluentTransport ────────────────────────────────────────────────────────\n\n/**\n * `KafkaTransport` implementation backed by `@confluentinc/kafka-javascript`.\n * Wraps the KafkaJS-compatibility layer from librdkafka.\n */\nexport class ConfluentTransport implements KafkaTransport {\n private readonly kafka: KafkaJS.Kafka;\n\n constructor(clientId: string, brokers: string[]) {\n this.kafka = new KafkaClass({\n kafkaJS: { clientId, brokers, logLevel: KafkaLogLevel.ERROR },\n });\n }\n\n producer(options?: IProducerCreationOptions): IProducer {\n const native = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n ...(options?.idempotent !== undefined && {\n idempotent: options.idempotent,\n }),\n ...(options?.transactionalId !== undefined && {\n transactionalId: options.transactionalId,\n maxInFlightRequests: 1,\n }),\n },\n });\n return new ConfluentProducer(native);\n }\n\n consumer(options: IConsumerCreationOptions): IConsumer {\n const assigner =\n options.partitionAssigner === \"roundrobin\"\n ? PartitionAssigners.roundRobin\n : options.partitionAssigner === \"range\"\n ? PartitionAssigners.range\n : PartitionAssigners.cooperativeSticky;\n\n const config: any = {\n kafkaJS: {\n groupId: options.groupId,\n fromBeginning: options.fromBeginning,\n autoCommit: options.autoCommit,\n partitionAssigners: [assigner],\n },\n };\n\n if (options.onRebalance) {\n const cb = options.onRebalance;\n // err.code -175 = ERR__ASSIGN_PARTITIONS, -174 = ERR__REVOKE_PARTITIONS\n config.rebalance_cb = (err: any, assignment: any[]) => {\n const type = err.code === -175 ? \"assign\" : \"revoke\";\n cb(\n type,\n assignment.map((p) => ({ topic: p.topic, partition: p.partition })),\n );\n };\n }\n\n return new ConfluentConsumer(this.kafka.consumer(config));\n }\n\n admin(): IAdmin {\n return new ConfluentAdmin(this.kafka.admin());\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 *\n * @example\n * ```ts\n * await kafka.startConsumer(['orders'], async (envelope: EventEnvelope<Order>) => {\n * console.log(envelope.payload.orderId); // typed payload\n * console.log(envelope.correlationId); // auto-propagated\n * console.log(envelope.eventId); // unique message ID\n * });\n * ```\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/**\n * Read the current envelope context (correlationId / traceparent) from ALS.\n * Returns `undefined` outside of a Kafka consumer handler.\n * @example\n * ```ts\n * const ctx = getEnvelopeContext();\n * if (ctx) console.log('correlationId:', ctx.correlationId);\n * ```\n */\nexport function getEnvelopeContext(): EnvelopeCtx | undefined {\n return envelopeStorage.getStore();\n}\n\n/**\n * Execute `fn` inside an envelope context so nested sends inherit correlationId.\n * Automatically called by the consumer pipeline — use this in tests or manual flows.\n * @example\n * ```ts\n * await runWithEnvelopeContext({ correlationId: 'abc-123' }, async () => {\n * await kafka.sendMessage('orders.created', payload); // inherits correlationId\n * });\n * ```\n */\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","/**\n * Error thrown when a consumer message handler fails.\n * @example\n * ```ts\n * await kafka.startConsumer(['orders'], async (envelope) => {\n * try { await process(envelope); }\n * catch (err) {\n * if (err instanceof KafkaProcessingError) {\n * console.error(err.topic, err.originalMessage);\n * }\n * }\n * });\n * ```\n */\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/**\n * Error thrown when schema validation fails on send or consume.\n * @example\n * ```ts\n * try { await kafka.sendMessage('orders.created', invalidPayload); }\n * catch (err) {\n * if (err instanceof KafkaValidationError) {\n * console.error('Validation failed for topic:', err.topic);\n * }\n * }\n * ```\n */\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/**\n * Error thrown when all retry attempts are exhausted for a message.\n * @example\n * ```ts\n * const kafka = new KafkaClient(config, groupId, { onMessageLost: (ctx) => {\n * if (ctx.error instanceof KafkaRetryExhaustedError) {\n * console.error(`Exhausted after ${ctx.error.attempts} attempts on ${ctx.error.topic}`);\n * }\n * }});\n * ```\n */\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 type { IConsumer, KafkaTransport } from \"../../transport.interface\";\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, IConsumer>;\n consumerCreationOptions: Map<\n string,\n { fromBeginning: boolean; autoCommit: boolean }\n >;\n transport: KafkaTransport;\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, transport, 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 onFirstAssignment?: () => void,\n): IConsumer {\n const { consumers, consumerCreationOptions, transport, 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 // Consumer already exists — treat as immediately ready (assignment already happened)\n onFirstAssignment?.();\n return consumers.get(groupId)!;\n }\n\n consumerCreationOptions.set(groupId, { fromBeginning, autoCommit });\n\n // Debounced assignment resolver for handle.ready().\n //\n // Rather than resolving on the *first* assign event, we debounce: the promise\n // resolves SETTLE_MS after the *last* assign event with no subsequent assign.\n // This correctly handles multi-consumer groups where the initial solo assignment\n // is immediately followed by a rebalance when peers join — we wait until the\n // group is stable.\n //\n // When fromBeginning is false (auto.offset.reset = latest), librdkafka fires\n // ERR__ASSIGN_PARTITIONS BEFORE completing the async broker round-trip that\n // establishes the initial fetch position. The settle window also covers this\n // offset fetch so messages sent right after ready() resolves are not missed.\n //\n // fromBeginning: true doesn't need a settle window — seeks to offset 0\n // happen synchronously and the consumer catches up from the beginning anyway.\n // Debounced readiness: resolves SETTLE_MS after the last rebalance event\n // (assign OR revoke) with no subsequent event. We reset on revoke too because\n // in cooperative-sticky rebalancing a REVOKE fires mid-protocol (e.g. ConsumerA\n // loses one partition when a peer joins) and the group isn't stable until the\n // revoke has settled and peers have received their assigns. We guard against\n // firing before any \"assign\" has been seen so the promise never resolves on a\n // pure-revoke cycle.\n //\n // fromBeginning: false needs the settle window so librdkafka can complete the\n // async broker round-trip that fetches the initial \"latest\" offset after each\n // assign. fromBeginning: true doesn't need it — seeks to offset 0 happen\n // synchronously and the consumer will always catch up from the beginning.\n const SETTLE_MS = fromBeginning ? 0 : 500;\n let hasAssignment = false;\n let settleTimer: ReturnType<typeof setTimeout> | undefined;\n const scheduleSettle = () => {\n if (!hasAssignment) return;\n if (settleTimer) clearTimeout(settleTimer);\n settleTimer = setTimeout(() => {\n settleTimer = undefined;\n onFirstAssignment?.();\n }, SETTLE_MS);\n };\n // Thin wrapper used at the call-site below — keeps the old name readable.\n const fireOnAssignment = () => {\n hasAssignment = true;\n scheduleSettle();\n };\n\n const consumer = transport.consumer({\n groupId,\n fromBeginning,\n autoCommit,\n partitionAssigner: partitionAssigner ?? \"cooperative-sticky\",\n onRebalance: (type, assignments) => {\n if (type === \"assign\") fireOnAssignment();\n else if (type === \"revoke\") scheduleSettle(); // reset timer mid-rebalance\n if (onRebalance) {\n try {\n onRebalance(type, assignments);\n } catch (e) {\n logger.warn(`onRebalance callback threw: ${(e as Error).message}`);\n }\n }\n },\n });\n\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 { IAdmin } from \"../../transport.interface\";\ntype Admin = IAdmin;\nimport type {\n ClientId,\n ConsumerGroupSummary,\n KafkaHealthResult,\n KafkaLogger,\n TopicDescription,\n} from \"../../types\";\n\nexport type AdminOpsDeps = {\n admin: Admin;\n logger: KafkaLogger;\n runningConsumers: Map<string, \"eachMessage\" | \"eachBatch\">;\n defaultGroupId: string;\n clientId: ClientId;\n};\n\nexport class AdminOps {\n private isConnected = false;\n\n constructor(private readonly deps: AdminOpsDeps) {}\n\n /** Underlying admin client — used by index.ts for topic validation. */\n get admin(): Admin {\n return this.deps.admin;\n }\n\n /** Whether the admin client is currently connected. */\n get connected(): boolean {\n return this.isConnected;\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 async ensureConnected(): Promise<void> {\n if (this.isConnected) return;\n try {\n await this.deps.admin.connect();\n this.isConnected = true;\n } catch (err) {\n this.isConnected = false;\n throw err;\n }\n }\n\n /** Disconnect admin if connected. Resets the connected flag. */\n async disconnect(): Promise<void> {\n if (!this.isConnected) return;\n await this.deps.admin.disconnect();\n this.isConnected = false;\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.deps.defaultGroupId;\n if (this.deps.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.ensureConnected();\n const partitionOffsets = await this.deps.admin.fetchTopicOffsets(topic);\n const partitions = partitionOffsets.map(({ partition, low, high }) => ({\n partition,\n offset: position === \"earliest\" ? low : high,\n }));\n await this.deps.admin.setOffsets({ groupId: gid, topic, partitions });\n this.deps.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.deps.defaultGroupId;\n if (this.deps.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.ensureConnected();\n const byTopic = new Map<string, Array<{ partition: number; offset: string }>>();\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.deps.admin.setOffsets({ groupId: gid, topic, partitions });\n this.deps.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.deps.defaultGroupId;\n if (this.deps.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.ensureConnected();\n const byTopic = new Map<string, Array<{ partition: number; timestamp: number }>>();\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.deps.admin.fetchTopicOffsetsByTimestamp(\n topic,\n timestamp,\n );\n const found = results.find(\n (r) => r.partition === partition,\n );\n return { partition, offset: found?.offset ?? \"-1\" };\n }),\n );\n await (this.deps.admin as any).setOffsets({ groupId: gid, topic, partitions: offsets });\n this.deps.logger.log(\n `Offsets set by timestamp for group \"${gid}\" on \"${topic}\": ${JSON.stringify(offsets)}`,\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.deps.defaultGroupId;\n await this.ensureConnected();\n\n const committedByTopic = await this.deps.admin.fetchOffsets({ groupId: gid });\n\n const brokerOffsetsAll = await Promise.all(\n committedByTopic.map(({ topic }) => this.deps.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<KafkaHealthResult> {\n try {\n await this.ensureConnected();\n const topics = await this.deps.admin.listTopics();\n return { status: \"up\", clientId: this.deps.clientId, topics };\n } catch (error) {\n return {\n status: \"down\",\n clientId: this.deps.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.ensureConnected();\n const result = await this.deps.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.ensureConnected();\n const result = await this.deps.admin.fetchTopicMetadata(\n topics ? { topics } : undefined,\n );\n return result.topics.map((t) => ({\n name: t.name,\n partitions: t.partitions.map((p) => ({\n partition: p.partitionId ?? p.partition,\n leader: p.leader,\n replicas: (p.replicas ?? []).map((r) =>\n typeof r === \"number\" ? r : r.nodeId,\n ),\n isr: (p.isr ?? []).map((r) =>\n typeof r === \"number\" ? r : r.nodeId,\n ),\n })),\n }));\n }\n\n /**\n * Delete consumer groups from the broker.\n * Groups must be empty (no active members) before deletion.\n * Silently skips unknown group IDs.\n * @param groupIds Consumer group IDs to delete.\n */\n public async deleteGroups(groupIds: string[]): Promise<void> {\n if (groupIds.length === 0) return;\n await this.ensureConnected();\n await this.deps.admin.deleteGroups(groupIds);\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.ensureConnected();\n await this.deps.admin.deleteTopicRecords({ topic, partitions });\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 public async validateRetryTopicsExist(\n topicNames: string[],\n maxRetries: number,\n ): Promise<void> {\n await this.ensureConnected();\n const existing = new Set(await this.deps.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 public async validateDlqTopicsExist(topicNames: string[]): Promise<void> {\n await this.ensureConnected();\n const existing = new Set(await this.deps.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 public async validateDuplicatesTopicsExist(\n topicNames: string[],\n customDestination: string | undefined,\n ): Promise<void> {\n await this.ensureConnected();\n const existing = new Set(await this.deps.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","import type { IProducer } from \"../../transport.interface\";\ntype Producer = IProducer;\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\n/**\n * Coerce an unknown thrown value to an `Error` instance.\n * Returns the value as-is if it is already an `Error`; otherwise wraps it with `String(error)`.\n * @param error The value caught in a `catch` clause.\n * @returns A guaranteed `Error` instance.\n */\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\n/**\n * Return a Promise that resolves after `ms` milliseconds.\n * Used for exponential backoff between retry attempts.\n * @param ms Delay in milliseconds.\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\n/**\n * Produce a message to `<topic>.dlq`, stamping standard DLQ headers.\n * Falls back to `onMessageLost` when the DLQ produce itself fails.\n * @param topic Original topic the message was consumed from.\n * @param rawMessage Raw JSON string of the original message.\n * @param deps Logger, producer, and optional `onMessageLost` callback.\n * @param meta Error, attempt count, and original headers to attach as DLQ metadata headers.\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\n/**\n * Input context passed to `executeWithRetry`.\n * Bundles the envelope(s), raw message bytes, interceptors, and retry/DLQ policy\n * so the retry loop has everything it needs without relying on closure state.\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 type { IConsumer } from \"../../transport.interface\";\nimport type { KafkaLogger, SubscribeRetryOptions } from \"../../types\";\nimport { toError, sleep } from \"./pipeline\";\n\nexport async function subscribeWithRetry(\n consumer: IConsumer,\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 type { IConsumer } from \"../../transport.interface\";\ntype Consumer = IConsumer;\nimport type { KafkaLogger, DlqReplayOptions } from \"../../types\";\nimport { subscribeWithRetry } from \"./subscribe-retry\";\nimport { decodeHeaders } from \"../../message/envelope\";\n\n/**\n * Dependencies injected into `replayDlqTopic` by `KafkaClient`.\n * Abstracts broker access (offset fetching, producing, consumer creation) so the\n * replay logic can be unit-tested without a real Kafka connection.\n */\nexport type DlqReplayDeps = {\n logger: KafkaLogger;\n fetchTopicOffsets: (topic: string) => Promise<Array<{ partition: number; low: string; high: string }>>;\n send: (topic: string, messages: Array<{ value: string; headers: Record<string, string> }>) => Promise<void>;\n /**\n * Create a consumer for the given group.\n * @param groupId Consumer group ID.\n * @param fromBeginning When `true`, `auto.offset.reset=earliest` (no committed offsets on a temp group).\n */\n createConsumer: (groupId: string, fromBeginning: boolean) => Consumer;\n /**\n * Disconnect the consumer and, when `deleteGroup` is `true`, delete the group\n * from the broker (used for ephemeral temp groups that should not accumulate).\n */\n cleanupConsumer: (consumer: Consumer, groupId: string, deleteGroup: boolean) => void;\n dlqHeaderKeys: Set<string>;\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 * ### Group ID strategy (driven by `options.fromBeginning`):\n * - `fromBeginning: true` (default) — a new ephemeral group `<topic>.dlq-replay-<ts>` is used\n * on every call so there are no committed offsets; reads all messages from the beginning\n * every time. The group is deleted from the broker after the replay finishes.\n * - `fromBeginning: false` — a stable group `<topic>.dlq-replay` is used; committed offsets\n * persist between calls so only messages added since the previous call are replayed.\n */\nexport async function replayDlqTopic(\n topic: string,\n deps: DlqReplayDeps,\n options: DlqReplayOptions = {},\n): Promise<{ replayed: number; skipped: number }> {\n const dlqTopic = `${topic}.dlq`;\n\n const partitionOffsets = await deps.fetchTopicOffsets(dlqTopic);\n // Only process partitions that have readable messages (high > low).\n // Checking high > 0 is insufficient: a topic truncated by retention policy\n // can have high > 0 but low == high (zero readable messages), causing an\n // infinite wait when consuming up to high − 1.\n const activePartitions = partitionOffsets.filter(\n (p) => Number.parseInt(p.high, 10) > Number.parseInt(p.low, 10),\n );\n if (activePartitions.length === 0) {\n deps.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 }) => [partition, Number.parseInt(high, 10)]),\n );\n const processedOffsets = new Map<number, number>();\n let replayed = 0;\n let skipped = 0;\n\n const fromBeginning = options.fromBeginning ?? true;\n // Ephemeral group when replaying from the beginning so no committed offsets\n // interfere with the seek. The group is deleted after use.\n // Stable group when replaying only new messages; committed offsets persist.\n const groupId = fromBeginning\n ? `${dlqTopic}-replay-${Date.now()}`\n : `${dlqTopic}-replay`;\n\n await new Promise<void>((resolve, reject) => {\n const consumer = deps.createConsumer(groupId, fromBeginning);\n const cleanup = () => deps.cleanupConsumer(consumer, groupId, fromBeginning);\n\n consumer\n .connect()\n .then(() => subscribeWithRetry(consumer, [dlqTopic], deps.logger))\n .then(() =>\n consumer.run({\n eachMessage: async ({ partition, message }) => {\n if (!message.value) return;\n\n const offset = Number.parseInt(message.offset, 10);\n processedOffsets.set(partition, offset);\n\n const headers = decodeHeaders(message.headers);\n const targetTopic = options.targetTopic ?? headers[\"x-dlq-original-topic\"];\n const originalHeaders = Object.fromEntries(\n Object.entries(headers).filter(([k]) => !deps.dlqHeaderKeys.has(k)),\n );\n const value = message.value.toString();\n const shouldProcess = !options.filter || options.filter(headers, value);\n\n if (!targetTopic || !shouldProcess) {\n skipped++;\n } else if (options.dryRun) {\n deps.logger.log(`[DLQ replay dry-run] Would replay to \"${targetTopic}\"`);\n replayed++;\n } else {\n await deps.send(targetTopic, [{ value, headers: originalHeaders }]);\n replayed++;\n }\n\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 deps.logger.log(`replayDlq: replayed ${replayed}, skipped ${skipped} from \"${dlqTopic}\"`);\n return { replayed, skipped };\n}\n","import type { EventEnvelope } from \"../../message/envelope\";\nimport type { DlqReason, KafkaInstrumentation, KafkaMetrics } from \"../../types\";\n\nexport type MetricsManagerDeps = {\n instrumentation: KafkaInstrumentation[];\n onCircuitFailure: (envelope: EventEnvelope<any>, gid: string) => void;\n onCircuitSuccess: (envelope: EventEnvelope<any>, gid: string) => void;\n};\n\n/**\n * Maintains per-topic event counters and dispatches instrumentation hooks.\n * Created once per `KafkaClient` instance and shared across all consumers and producers.\n */\nexport class MetricsManager {\n private readonly topicMetrics = new Map<string, KafkaMetrics>();\n\n constructor(private readonly deps: MetricsManagerDeps) {}\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 /** Fire `afterSend` instrumentation hooks for each message in a batch. */\n notifyAfterSend(topic: string, count: number): void {\n for (let i = 0; i < count; i++)\n for (const inst of this.deps.instrumentation) inst.afterSend?.(topic);\n }\n\n /**\n * Increment the retry counter for the envelope's topic and fire all `onRetry` instrumentation hooks.\n * @param envelope The message envelope being retried.\n * @param attempt Current retry attempt number (1-based).\n * @param maxRetries Maximum number of retries configured for this consumer.\n */\n notifyRetry(envelope: EventEnvelope<any>, attempt: number, maxRetries: number): void {\n this.metricsFor(envelope.topic).retryCount++;\n for (const inst of this.deps.instrumentation) inst.onRetry?.(envelope, attempt, maxRetries);\n }\n\n /**\n * Increment the DLQ counter for the envelope's topic, fire all `onDlq` instrumentation hooks,\n * and notify the circuit breaker of a failure (when `gid` is provided).\n * @param envelope The message envelope being sent to the DLQ.\n * @param reason The reason the message is being dead-lettered.\n * @param gid Consumer group ID — used to drive circuit breaker state.\n */\n notifyDlq(envelope: EventEnvelope<any>, reason: DlqReason, gid?: string): void {\n this.metricsFor(envelope.topic).dlqCount++;\n for (const inst of this.deps.instrumentation) inst.onDlq?.(envelope, reason);\n if (gid) this.deps.onCircuitFailure(envelope, gid);\n }\n\n /**\n * Increment the deduplication counter for the envelope's topic and fire all `onDuplicate` hooks.\n * @param envelope The duplicate message envelope.\n * @param strategy The deduplication strategy applied (`\"drop\"`, `\"dlq\"`, or `\"topic\"`).\n */\n notifyDuplicate(envelope: EventEnvelope<any>, strategy: \"drop\" | \"dlq\" | \"topic\"): void {\n this.metricsFor(envelope.topic).dedupCount++;\n for (const inst of this.deps.instrumentation) inst.onDuplicate?.(envelope, strategy);\n }\n\n /**\n * Increment the processed counter for the envelope's topic, fire all `onMessage` hooks,\n * and notify the circuit breaker of a success (when `gid` is provided).\n * @param envelope The successfully processed message envelope.\n * @param gid Consumer group ID — used to drive circuit breaker state.\n */\n notifyMessage(envelope: EventEnvelope<any>, gid?: string): void {\n this.metricsFor(envelope.topic).processedCount++;\n for (const inst of this.deps.instrumentation) inst.onMessage?.(envelope);\n if (gid) this.deps.onCircuitSuccess(envelope, gid);\n }\n\n /**\n * Return a snapshot of event counters.\n * @param topic When provided, returns counters for that topic only; otherwise aggregates all topics.\n * @returns Read-only `KafkaMetrics` snapshot. Returns zero-valued counters if the topic has no events.\n */\n getMetrics(topic?: string): Readonly<KafkaMetrics> {\n if (topic !== undefined) {\n const m = this.topicMetrics.get(topic);\n return m ? { ...m } : { processedCount: 0, retryCount: 0, dlqCount: 0, dedupCount: 0 };\n }\n const agg: KafkaMetrics = { processedCount: 0, retryCount: 0, dlqCount: 0, dedupCount: 0 };\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 event counters to zero.\n * @param topic When provided, clears counters for that topic only; otherwise clears all topics.\n */\n resetMetrics(topic?: string): void {\n if (topic !== undefined) {\n this.topicMetrics.delete(topic);\n return;\n }\n this.topicMetrics.clear();\n }\n}\n","/** Tracks in-flight async handlers and provides a drain-wait mechanism. */\nexport class InFlightTracker {\n private inFlightTotal = 0;\n private readonly drainResolvers: Array<() => void> = [];\n\n constructor(private readonly warn: (msg: string) => void) {}\n\n /**\n * Wrap an async handler so its lifetime is counted against the in-flight total.\n * Resolvers registered with `waitForDrain` are notified when the count reaches zero.\n * @param fn The async function to track.\n * @returns The same promise returned by `fn`.\n */\n track<R>(fn: () => Promise<R>): Promise<R> {\n this.inFlightTotal++;\n return fn().finally(() => {\n this.inFlightTotal--;\n if (this.inFlightTotal === 0) this.drainResolvers.splice(0).forEach((r) => r());\n });\n }\n\n /**\n * Resolve when all tracked handlers have completed, or after `timeoutMs` elapses.\n * Logs a warning (via the injected `warn` callback) if the timeout is hit before draining.\n * Returns immediately if there are no in-flight handlers.\n * @param timeoutMs Maximum time to wait in milliseconds before resolving anyway.\n */\n 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 = () => { clearTimeout(handle); resolve(); };\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.warn(\n `Drain timed out after ${timeoutMs}ms — ${this.inFlightTotal} handler(s) still in flight`,\n );\n resolve();\n }, timeoutMs);\n });\n }\n}\n","import type { EventEnvelope } from \"../../message/envelope\";\nimport type { CircuitBreakerOptions, KafkaInstrumentation, KafkaLogger } from \"../../types\";\n\ntype CircuitState = {\n status: \"closed\" | \"open\" | \"half-open\";\n window: boolean[];\n successes: number;\n timer?: ReturnType<typeof setTimeout>;\n};\n\ntype CircuitBreakerDeps = {\n pauseConsumer: (gid: string, assignments: Array<{ topic: string; partitions: number[] }>) => void;\n resumeConsumer: (gid: string, assignments: Array<{ topic: string; partitions: number[] }>) => void;\n logger: KafkaLogger;\n instrumentation: KafkaInstrumentation[];\n};\n\n/**\n * Per-consumer-group circuit breaker.\n * State is tracked per `${gid}:${topic}:${partition}` key using a sliding window.\n * Drives CLOSED → OPEN → HALF-OPEN → CLOSED transitions and pauses/resumes\n * topic-partition consumption accordingly.\n */\nexport class CircuitBreakerManager {\n private readonly states = new Map<string, CircuitState>();\n private readonly configs = new Map<string, CircuitBreakerOptions>();\n\n constructor(private readonly deps: CircuitBreakerDeps) {}\n\n /**\n * Register or update circuit breaker configuration for a consumer group.\n * Must be called before the group starts consuming for the config to take effect.\n * @param gid Consumer group ID.\n * @param options Circuit breaker thresholds and timing configuration.\n */\n setConfig(gid: string, options: CircuitBreakerOptions): void {\n this.configs.set(gid, options);\n }\n\n /**\n * Returns a snapshot of the circuit breaker state for a given topic-partition.\n * Returns `undefined` when no state exists for the key.\n */\n getState(\n topic: string,\n partition: number,\n gid: string,\n ): { status: \"closed\" | \"open\" | \"half-open\"; failures: number; windowSize: number } | undefined {\n const state = this.states.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 * Record a failure for the given envelope and group.\n * Drives the CLOSED → OPEN and HALF-OPEN → OPEN transitions.\n */\n onFailure(envelope: EventEnvelope<any>, gid: string): void {\n const cfg = this.configs.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.states.get(stateKey);\n if (!state) {\n state = { status: \"closed\", window: [], successes: 0 };\n this.states.set(stateKey, state);\n }\n if (state.status === \"open\") return;\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.deps.instrumentation)\n inst.onCircuitOpen?.(envelope.topic, envelope.partition);\n this.deps.pauseConsumer(gid, [{ topic: envelope.topic, partitions: [envelope.partition] }]);\n state!.timer = setTimeout(() => {\n state!.status = \"half-open\";\n state!.successes = 0;\n this.deps.logger.log(\n `[CircuitBreaker] HALF-OPEN — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition}`,\n );\n for (const inst of this.deps.instrumentation)\n inst.onCircuitHalfOpen?.(envelope.topic, envelope.partition);\n this.deps.resumeConsumer(gid, [{ topic: envelope.topic, partitions: [envelope.partition] }]);\n }, recoveryMs);\n };\n\n if (state.status === \"half-open\") {\n clearTimeout(state.timer);\n this.deps.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.deps.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 * Record a success for the given envelope and group.\n * Drives the HALF-OPEN → CLOSED transition and updates the success window.\n */\n onSuccess(envelope: EventEnvelope<any>, gid: string): void {\n const cfg = this.configs.get(gid);\n if (!cfg) return;\n\n const stateKey = `${gid}:${envelope.topic}:${envelope.partition}`;\n const state = this.states.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.deps.logger.log(\n `[CircuitBreaker] CLOSED — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition}`,\n );\n for (const inst of this.deps.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 * Remove all circuit state and config for the given group.\n * Called when a consumer is stopped via `stopConsumer(groupId)`.\n */\n removeGroup(gid: string): void {\n for (const key of [...this.states.keys()]) {\n if (key.startsWith(`${gid}:`)) {\n clearTimeout(this.states.get(key)!.timer);\n this.states.delete(key);\n }\n }\n this.configs.delete(gid);\n }\n\n /** Clear all circuit state and config. Called on `disconnect()`. */\n clear(): void {\n for (const state of this.states.values()) clearTimeout(state.timer);\n this.states.clear();\n this.configs.clear();\n }\n}\n","/** Push-to-pull async queue used by consume() to bridge Kafka's push model to AsyncIterableIterator. */\nexport class 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 /**\n * Enqueue an item. If a consumer is already awaiting the next item, delivers it immediately.\n * When the internal buffer reaches `highWaterMark`, calls `onFull` to signal backpressure.\n * @param item The value to enqueue.\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 /**\n * Terminate the queue with an error. All pending and future `next()` calls will reject.\n * @param err The error to propagate to all waiting consumers.\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 /**\n * Signal end-of-stream. All pending `next()` calls resolve with `{ done: true }`,\n * and future `next()` calls resolve immediately with `{ done: true }`.\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 /**\n * Pull the next item from the queue, conforming to the `AsyncIterator` protocol.\n * Rejects if the queue has been failed. Resolves with `{ done: true }` if it has been closed.\n * Suspends (returns a pending Promise) when the queue is empty and not yet closed.\n * When items drain below `highWaterMark / 2`, calls `onDrained` to resume backpressure.\n * @returns A Promise resolving to an `IteratorResult<V>`.\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","import type { IProducer } from \"../../transport.interface\";\ntype Producer = IProducer;\n\nimport type { KafkaClientContext } from \"../context\";\nimport type { TopicMapConstraint } from \"../../types\";\nimport { HEADER_LAMPORT_CLOCK } from \"../../message/envelope\";\nimport { toError } from \"../consumer/pipeline\";\n\n/**\n * Process-level registry of active transactional producer IDs.\n * Shared across `transactionImpl` (send.ts) and `createRetryTxProducer`.\n * Cross-process conflicts cannot be detected here — they surface as fencing errors from the broker.\n */\nexport const _activeTransactionalIds = new Set<string>();\n\n// ── Topic creation ────────────────────────────────────────────────────────────\n\n/**\n * Ensure a topic exists, creating it if `autoCreateTopics` is enabled.\n * Concurrent calls for the same topic are deduplicated via a promise cache.\n */\nexport async function ensureTopic<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topic: string,\n): Promise<void> {\n if (!ctx.autoCreateTopicsEnabled || ctx.ensuredTopics.has(topic)) return;\n let p = ctx.ensureTopicPromises.get(topic);\n if (!p) {\n p = (async () => {\n await ctx.adminOps.ensureConnected();\n await ctx.adminOps.admin.createTopics({\n topics: [{ topic, numPartitions: ctx.numPartitions }],\n });\n ctx.ensuredTopics.add(topic);\n })().finally(() => ctx.ensureTopicPromises.delete(topic));\n ctx.ensureTopicPromises.set(topic, p);\n }\n await p;\n}\n\n// ── Transactional producer ────────────────────────────────────────────────────\n\n/**\n * Create and connect a transactional producer for EOS routing.\n * Each retry level consumer uses its own producer with a unique `transactionalId`\n * so Kafka can fence stale producers on restart without affecting other levels.\n */\nexport async function createRetryTxProducer<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n transactionalId: string,\n): Promise<Producer> {\n if (_activeTransactionalIds.has(transactionalId)) {\n ctx.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 = ctx.transport.producer({\n idempotent: true,\n transactionalId,\n });\n await p.connect();\n _activeTransactionalIds.add(transactionalId);\n ctx.retryTxProducers.set(transactionalId, p);\n return p;\n}\n\n// ── Lifecycle ─────────────────────────────────────────────────────────────────\n\nexport async function connectProducerImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n): Promise<void> {\n await ctx.producer.connect();\n await recoverLamportClockImpl(ctx, ctx.clockRecoveryTopics);\n if (ctx.lagThrottleOpts) startLagThrottlePoller(ctx);\n ctx.logger.log(\"Producer connected\");\n}\n\nexport async function disconnectImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n drainTimeoutMs = 30_000,\n): Promise<void> {\n if (ctx._lagThrottleTimer) {\n clearInterval(ctx._lagThrottleTimer);\n ctx._lagThrottleTimer = undefined;\n }\n await ctx.inFlight.waitForDrain(drainTimeoutMs);\n const tasks: Promise<void>[] = [ctx.producer.disconnect()];\n if (ctx.txProducer) {\n tasks.push(ctx.txProducer.disconnect());\n _activeTransactionalIds.delete(ctx.txId);\n ctx.txProducer = undefined;\n ctx.txProducerInitPromise = undefined;\n }\n for (const txId of ctx.retryTxProducers.keys())\n _activeTransactionalIds.delete(txId);\n for (const p of ctx.retryTxProducers.values()) tasks.push(p.disconnect());\n ctx.retryTxProducers.clear();\n for (const consumer of ctx.consumers.values())\n tasks.push(consumer.disconnect());\n tasks.push(ctx.adminOps.disconnect());\n await Promise.allSettled(tasks);\n ctx.consumers.clear();\n ctx.runningConsumers.clear();\n ctx.consumerCreationOptions.clear();\n ctx.companionGroupIds.clear();\n ctx.circuitBreaker.clear();\n ctx.logger.log(\"All connections closed\");\n}\n\n// ── Lag throttle ──────────────────────────────────────────────────────────────\n\nexport function startLagThrottlePoller<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n): void {\n const opts = ctx.lagThrottleOpts!;\n const { maxLag, pollIntervalMs = 5_000 } = opts;\n const groupId = opts.groupId;\n\n const poll = async () => {\n try {\n const lags = await ctx.adminOps.getConsumerLag(groupId);\n const total = lags.reduce((sum, e) => sum + e.lag, 0);\n if (total > maxLag && !ctx._lagThrottled) {\n ctx._lagThrottled = true;\n ctx.logger.warn(\n `lagThrottle: lag ${total} > ${maxLag} — producer sends will be delayed`,\n );\n } else if (total <= maxLag && ctx._lagThrottled) {\n ctx._lagThrottled = false;\n ctx.logger.log(\n `lagThrottle: lag ${total} ≤ ${maxLag} — producer sends resumed`,\n );\n }\n } catch {\n // Poll errors do not block sends — silently ignore\n }\n };\n\n ctx._lagThrottleTimer = setInterval(() => {\n void poll();\n }, pollIntervalMs);\n\n ctx._lagThrottleTimer.unref?.();\n}\n\n// ── Lamport clock recovery ────────────────────────────────────────────────────\n\n/**\n * Recover the Lamport clock from the last message across the given topics.\n * Seeks every non-empty partition to its last offset, reads one message per\n * partition, and extracts the maximum `x-lamport-clock` header value.\n * Topics that are empty or missing are silently skipped.\n */\nexport async function recoverLamportClockImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topics: string[],\n): Promise<void> {\n if (topics.length === 0) return;\n\n ctx.logger.log(\n `Clock recovery: scanning ${topics.length} topic(s) for Lamport clock...`,\n );\n await ctx.adminOps.ensureConnected();\n\n const partitionsToRead: Array<{\n topic: string;\n partition: number;\n lastOffset: string;\n }> = [];\n for (const t of topics) {\n let offsets: Array<{ partition: number; low: string; high: string }>;\n try {\n offsets = await ctx.adminOps.admin.fetchTopicOffsets(t);\n } catch {\n ctx.logger.warn(\n `Clock recovery: could not fetch offsets for \"${t}\", skipping`,\n );\n continue;\n }\n for (const { partition, high, low } of offsets) {\n if (Number.parseInt(high, 10) > Number.parseInt(low, 10)) {\n partitionsToRead.push({\n topic: t,\n partition,\n lastOffset: String(Number.parseInt(high, 10) - 1),\n });\n }\n }\n }\n\n if (partitionsToRead.length === 0) {\n ctx.logger.log(\n \"Clock recovery: all topics empty — keeping Lamport clock at 0\",\n );\n return;\n }\n\n const recoveryGroupId = `${ctx.clientId}-clock-recovery-${Date.now()}`;\n let maxClock = -1;\n\n await new Promise<void>((resolve, reject) => {\n const consumer = ctx.transport.consumer({ groupId: recoveryGroupId });\n const remaining = new Set(\n partitionsToRead.map((p) => `${p.topic}:${p.partition}`),\n );\n const cleanup = () => {\n consumer\n .disconnect()\n .catch(() => {})\n .finally(() => {\n ctx.adminOps.deleteGroups([recoveryGroupId]).catch(() => {});\n });\n };\n\n consumer\n .connect()\n .then(async () => {\n const uniqueTopics = [\n ...new Set(partitionsToRead.map((p) => p.topic)),\n ];\n await consumer.subscribe({ topics: uniqueTopics });\n for (const { topic: t, partition, lastOffset } of partitionsToRead) {\n consumer.seek({ topic: t, partition, offset: lastOffset });\n }\n })\n .then(() =>\n consumer.run({\n eachMessage: async ({ topic: t, partition, message }) => {\n const key = `${t}:${partition}`;\n if (!remaining.has(key)) return;\n remaining.delete(key);\n\n const clockHeader = message.headers?.[HEADER_LAMPORT_CLOCK];\n if (clockHeader !== undefined) {\n const raw = Buffer.isBuffer(clockHeader)\n ? clockHeader.toString()\n : String(clockHeader);\n const clock = Number(raw);\n if (!Number.isNaN(clock) && clock > maxClock) maxClock = clock;\n }\n\n if (remaining.size === 0) {\n cleanup();\n resolve();\n }\n },\n }),\n )\n .catch((err) => {\n cleanup();\n reject(err);\n });\n });\n\n if (maxClock >= 0) {\n ctx._lamportClock = maxClock;\n ctx.logger.log(\n `Clock recovery: Lamport clock restored — next clock will be ${maxClock + 1}`,\n );\n } else {\n ctx.logger.log(\n \"Clock recovery: no x-lamport-clock headers found — keeping clock at 0\",\n );\n }\n}\n\n// ── Utilities ─────────────────────────────────────────────────────────────────\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 */\nexport function wrapWithTimeoutWarning<R>(\n logger: { warn: (msg: string) => void },\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 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// Re-export toError for callers that only import lifecycle\nexport { toError };\n","import type { KafkaClientContext } from \"../context\";\nimport type {\n TopicMapConstraint,\n SendOptions,\n BatchMessageItem,\n BatchSendOptions,\n TransactionContext,\n MessageHeaders,\n CompressionType,\n} from \"../../types\";\nimport { buildSendPayload, registerSchema } from \"./ops\";\nimport { ensureTopic, _activeTransactionalIds } from \"./lifecycle\";\n\n// ── Payload builder ───────────────────────────────────────────────────────────\n\n/**\n * Build a ProduceRequest payload: register schema, validate, stamp envelope headers.\n * Shared by sendMessage, sendBatch, and transaction sends.\n */\nexport async function preparePayload<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n compression?: CompressionType,\n) {\n registerSchema(topicOrDesc, ctx.schemaRegistry, ctx.logger);\n const payload = await buildSendPayload(\n topicOrDesc,\n messages,\n ctx.producerOpsDeps,\n compression,\n );\n await ensureTopic(ctx, payload.topic);\n return payload;\n}\n\n// ── Send operations ───────────────────────────────────────────────────────────\n\nexport async function sendMessageImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n): Promise<void> {\n await waitIfThrottled(ctx);\n const payload = await preparePayload(\n ctx,\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 ctx.producer.send(payload);\n ctx.metrics.notifyAfterSend(payload.topic, payload.messages.length);\n}\n\nexport async function sendBatchImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n options?: BatchSendOptions,\n): Promise<void> {\n await waitIfThrottled(ctx);\n const payload = await preparePayload(\n ctx,\n topicOrDesc,\n messages,\n options?.compression,\n );\n await ctx.producer.send(payload);\n ctx.metrics.notifyAfterSend(payload.topic, payload.messages.length);\n}\n\nexport async function sendTombstoneImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topic: string,\n key: string,\n headers?: MessageHeaders,\n): Promise<void> {\n await waitIfThrottled(ctx);\n const hdrs: MessageHeaders = { ...headers };\n for (const inst of ctx.instrumentation) inst.beforeSend?.(topic, hdrs);\n await ensureTopic(ctx, topic);\n await ctx.producer.send({\n topic,\n messages: [{ value: null, key, headers: hdrs }],\n });\n for (const inst of ctx.instrumentation) inst.afterSend?.(topic);\n}\n\nexport async function transactionImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n fn: (txCtx: TransactionContext<T>) => Promise<void>,\n): Promise<void> {\n if (!ctx.txProducerInitPromise) {\n if (_activeTransactionalIds.has(ctx.txId)) {\n ctx.logger.warn(\n `transactionalId \"${ctx.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 = (async () => {\n const p = ctx.transport.producer({\n idempotent: true,\n transactionalId: ctx.txId,\n });\n await p.connect();\n _activeTransactionalIds.add(ctx.txId);\n return p;\n })();\n ctx.txProducerInitPromise = initPromise.catch((err) => {\n ctx.txProducerInitPromise = undefined;\n throw err;\n });\n }\n ctx.txProducer = await ctx.txProducerInitPromise;\n const tx = await ctx.txProducer.transaction();\n try {\n const txCtx: TransactionContext<T> = {\n send: async (topicOrDesc: any, message: any, sendOpts: SendOptions = {}) => {\n const payload = await preparePayload(ctx, topicOrDesc, [\n {\n value: message,\n key: sendOpts.key,\n headers: sendOpts.headers,\n correlationId: sendOpts.correlationId,\n schemaVersion: sendOpts.schemaVersion,\n eventId: sendOpts.eventId,\n },\n ]);\n await tx.send(payload);\n ctx.metrics.notifyAfterSend(payload.topic, payload.messages.length);\n },\n sendBatch: async (\n topicOrDesc: any,\n messages: BatchMessageItem<any>[],\n batchOpts?: BatchSendOptions,\n ) => {\n const payload = await preparePayload(\n ctx,\n topicOrDesc,\n messages,\n batchOpts?.compression,\n );\n await tx.send(payload);\n ctx.metrics.notifyAfterSend(payload.topic, payload.messages.length);\n },\n };\n await fn(txCtx);\n await tx.commit();\n } catch (error) {\n try {\n await tx.abort();\n } catch (abortError) {\n ctx.logger.error(\n \"Failed to abort transaction:\",\n (abortError instanceof Error\n ? abortError\n : new Error(String(abortError))\n ).message,\n );\n }\n throw error;\n }\n}\n\n// ── Lag throttle wait (used by all send paths) ────────────────────────────────\n\nexport async function waitIfThrottled<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n): Promise<void> {\n if (!ctx._lagThrottled) return;\n const maxWait = ctx.lagThrottleOpts?.maxWaitMs ?? 30_000;\n const start = Date.now();\n while (ctx._lagThrottled) {\n if (Date.now() - start >= maxWait) {\n ctx.logger.warn(\n `lagThrottle: maxWaitMs (${maxWait} ms) exceeded — sending anyway`,\n );\n return;\n }\n await new Promise<void>((r) => setTimeout(r, 100));\n }\n}\n","import type { IConsumer, IProducer } from \"../../transport.interface\";\ntype Consumer = IConsumer;\ntype Producer = IProducer;\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 \"./pipeline\";\nimport { KafkaRetryExhaustedError } from \"../../errors\";\nimport { subscribeWithRetry } from \"./subscribe-retry\";\nimport type { SchemaLike } from \"../../message/topic\";\nimport type {\n ConsumerInterceptor,\n DlqReason,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n} from \"../../types\";\n\n/**\n * Dependencies injected into retry topic consumers by `KafkaClient`.\n * Provides infrastructure (producer, logger, instrumentation), lifecycle callbacks,\n * and factories for creating consumers and transactional producers per retry level.\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 * Called immediately after each retry level consumer successfully starts and\n * receives partition assignments. Allows the caller to register the group ID\n * incrementally so partial startup failures still leave started consumers tracked.\n */\n onLevelStarted?: (groupId: string) => void;\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 // Notify the caller that this level is fully started so it can be tracked\n // immediately. This ensures partial failures leave already-started consumers\n // registered and reachable by stopConsumer.\n deps.onLevelStarted?.(levelGroupId);\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 // Pre-allocate to preserve level order in the returned array.\n const levelGroupIds = new Array<string>(retry.maxRetries);\n\n // Launch all level consumers in parallel. Sequential startup was the main\n // cause of slow NestJS onModuleInit: N levels × waitForPartitionAssignment\n // (default 10 s each) = up to N × 10 s of blocking startup time.\n await Promise.all(\n Array.from({ length: retry.maxRetries }, async (_, i) => {\n const level = i + 1;\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[i] = levelGroupId;\n }),\n );\n\n return levelGroupIds;\n}\n","import type { IConsumer, IProducer } from \"../../transport.interface\";\nimport type { SchemaLike } from \"../../message/topic\";\nimport type { EventEnvelope } from \"../../message/envelope\";\nimport type { KafkaClientContext } from \"../context\";\nimport type {\n TopicMapConstraint,\n ConsumerOptions,\n ConsumerInterceptor,\n RetryOptions,\n} from \"../../types\";\nimport type { MessageHandlerDeps, DeduplicationContext } from \"./handler\";\nimport { getOrCreateConsumer, buildSchemaMap } from \"./ops\";\nimport { subscribeWithRetry } from \"./subscribe-retry\";\nimport { startRetryTopicConsumers } from \"./retry-topic\";\nimport { createRetryTxProducer, ensureTopic } from \"../producer/lifecycle\";\nimport { resolveTopicName } from \"../producer/ops\";\n\n// ── Topic validation ──────────────────────────────────────────────────────────\n\n/** Guard checks shared by startConsumer and startBatchConsumer. */\nexport function validateTopicConsumerOpts<T extends TopicMapConstraint<T>>(\n topics: any[],\n options: ConsumerOptions<T>,\n): void {\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 if (options.retryTopics && topics.some((t) => t instanceof RegExp)) {\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\n/** Ensure all required topics exist for a consumer: base, DLQ, and dedup topics. */\nexport async function ensureConsumerTopics<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topicNames: string[],\n dlq: boolean,\n deduplication: import(\"../../types\").DeduplicationOptions | undefined,\n): Promise<void> {\n for (const t of topicNames) await ensureTopic(ctx, t);\n if (dlq) {\n for (const t of topicNames) await ensureTopic(ctx, `${t}.dlq`);\n if (!ctx.autoCreateTopicsEnabled && topicNames.length > 0) {\n await ctx.adminOps.validateDlqTopicsExist(topicNames);\n }\n }\n if (deduplication?.strategy === \"topic\") {\n const dest = deduplication.duplicatesTopic;\n if (ctx.autoCreateTopicsEnabled) {\n for (const t of topicNames)\n await ensureTopic(ctx, dest ?? `${t}.duplicates`);\n } else if (topicNames.length > 0) {\n await ctx.adminOps.validateDuplicatesTopicsExist(topicNames, dest);\n }\n }\n}\n\n// ── Consumer bootstrap ────────────────────────────────────────────────────────\n\n/**\n * Shared consumer bootstrap: groupId conflict check, schema map, connect, subscribe.\n * Returns all values needed by startConsumer / startBatchConsumer.\n */\nexport async function setupConsumer<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\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 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 || ctx.defaultGroupId;\n const existingMode = ctx.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 let resolveReady!: () => void;\n const readyPromise = new Promise<void>((resolve) => { resolveReady = resolve; });\n\n const consumer = getOrCreateConsumer(\n gid,\n fromBeginning,\n options.autoCommit ?? true,\n ctx.consumerOpsDeps,\n options.partitionAssigner,\n resolveReady,\n );\n const schemaMap = buildSchemaMap(\n stringTopics,\n ctx.schemaRegistry,\n optionSchemas,\n ctx.logger,\n );\n const topicNames = stringTopics.map((t: any) => resolveTopicName(t));\n const subscribeTopics: (string | RegExp)[] = [...topicNames, ...regexTopics];\n\n await ensureConsumerTopics(ctx, topicNames, dlq, options.deduplication);\n\n await consumer.connect();\n await subscribeWithRetry(\n consumer,\n subscribeTopics,\n ctx.logger,\n options.subscribeRetry,\n );\n\n const displayTopics = subscribeTopics\n .map((t) => (t instanceof RegExp ? t.toString() : t))\n .join(\", \");\n ctx.logger.log(\n `${mode === \"eachBatch\" ? \"Batch consumer\" : \"Consumer\"} subscribed to topics: ${displayTopics}`,\n );\n\n return { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry, hasRegex, readyPromise };\n}\n\n// ── Deduplication ─────────────────────────────────────────────────────────────\n\n/** Create or retrieve the deduplication context for a consumer group. */\nexport function resolveDeduplicationContext<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n groupId: string,\n options: import(\"../../types\").DeduplicationOptions | undefined,\n): DeduplicationContext | undefined {\n if (!options) return undefined;\n if (!ctx.dedupStates.has(groupId))\n ctx.dedupStates.set(groupId, new Map());\n return { options, state: ctx.dedupStates.get(groupId)! };\n}\n\n// ── Deps builders ─────────────────────────────────────────────────────────────\n\n/** Build MessageHandlerDeps with circuit-breaker callbacks bound to the given groupId. */\nexport function messageDepsFor<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n gid: string,\n options?: Pick<import(\"../../types\").ConsumerOptions<T>, \"onMessageLost\" | \"onRetry\">,\n): MessageHandlerDeps {\n const notifyRetry = ctx.metrics.notifyRetry.bind(ctx.metrics);\n return {\n logger: ctx.logger,\n producer: ctx.producer,\n instrumentation: ctx.instrumentation,\n onMessageLost: options?.onMessageLost ?? ctx.onMessageLost,\n onTtlExpired: ctx.onTtlExpired,\n onRetry: options?.onRetry\n ? (envelope, attempt, max) => {\n notifyRetry(envelope, attempt, max);\n return options.onRetry!(envelope, attempt, max);\n }\n : notifyRetry,\n onDlq: (envelope, reason) => ctx.metrics.notifyDlq(envelope, reason, gid),\n onDuplicate: ctx.metrics.notifyDuplicate.bind(ctx.metrics),\n onMessage: (envelope) => ctx.metrics.notifyMessage(envelope, gid),\n };\n}\n\n/** Build the RetryTopicDeps object passed to retry topic consumers. */\nexport function buildRetryTopicDeps<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n) {\n return {\n logger: ctx.logger,\n producer: ctx.producer,\n instrumentation: ctx.instrumentation,\n onMessageLost: ctx.onMessageLost,\n onRetry: ctx.metrics.notifyRetry.bind(ctx.metrics),\n onDlq: ctx.metrics.notifyDlq.bind(ctx.metrics),\n onMessage: ctx.metrics.notifyMessage.bind(ctx.metrics),\n ensureTopic: (t: string) => ensureTopic(ctx, t),\n getOrCreateConsumer: (gid: string, fb: boolean, ac: boolean) =>\n getOrCreateConsumer(gid, fb, ac, ctx.consumerOpsDeps),\n runningConsumers: ctx.runningConsumers,\n createRetryTxProducer: (txId: string) => createRetryTxProducer(ctx, txId),\n };\n}\n\n// ── EOS main context ────────────────────────────────��─────────────────────────\n\n/** Create EOS transactional producer context for atomic main → retry.1 routing. */\nexport async function makeEosMainContext<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n gid: string,\n consumer: IConsumer,\n options: ConsumerOptions<T>,\n): Promise<{ txProducer: IProducer; consumer: IConsumer } | undefined> {\n if (!options.retryTopics || !options.retry) return undefined;\n const txProducer = await createRetryTxProducer(ctx, `${gid}-main-tx`);\n return { txProducer, consumer };\n}\n\n// ── Retry chain launcher ──────────────────────────────────────────────────────\n\n/** Start companion retry-level consumers and register them under the main groupId. */\nexport async function launchRetryChain<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n gid: string,\n topicNames: string[],\n handleMessage: (env: EventEnvelope<any>) => Promise<void>,\n opts: {\n retry: RetryOptions;\n dlq: boolean;\n interceptors: ConsumerInterceptor<T>[];\n schemaMap: Map<string, SchemaLike>;\n assignmentTimeoutMs?: number;\n },\n): Promise<void> {\n const { retry, dlq, interceptors, schemaMap, assignmentTimeoutMs } = opts;\n if (!ctx.autoCreateTopicsEnabled) {\n await ctx.adminOps.validateRetryTopicsExist(topicNames, retry.maxRetries);\n }\n // Pre-register an empty array so stopConsumer can find partially-started\n // companions if startRetryTopicConsumers throws mid-way.\n ctx.companionGroupIds.set(gid, []);\n await startRetryTopicConsumers(\n topicNames,\n gid,\n handleMessage,\n retry,\n dlq,\n interceptors,\n schemaMap,\n {\n ...ctx.retryTopicDeps,\n onLevelStarted: (levelGroupId) => {\n ctx.companionGroupIds.get(gid)!.push(levelGroupId);\n },\n },\n assignmentTimeoutMs,\n );\n}\n","import type { IProducer, IConsumer } from \"../../transport.interface\";\ntype Producer = IProducer;\ntype Consumer = IConsumer;\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 \"./pipeline\";\nimport type { DuplicateMetadata } from \"./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\n/**\n * Runtime dependencies injected into message handler functions by `KafkaClient`.\n * Holds shared infrastructure (producer, logger, instrumentation) and per-consumer\n * lifecycle callbacks (onMessageLost, onRetry, onDlq, etc.).\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\n/**\n * Per-consumer configuration forwarded from `startConsumer` into `handleEachMessage`.\n * Carries schema map, handler function, interceptors, retry/DLQ policy, timeout,\n * deduplication context, TTL options, and the optional EOS routing context.\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. */\nexport async 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\n/**\n * Core single-message processing pipeline invoked by the kafkajs `eachMessage` callback.\n * Parses, validates, deduplicates, checks TTL, runs the handler with retry/DLQ support,\n * and manages EOS offset commits when the main consumer runs with `autoCommit: false`.\n * @param payload Raw kafkajs `eachMessage` payload (topic, partition, message).\n * @param opts Consumer-level options including handler, schema map, retry policy, and EOS context.\n * @param deps Shared infrastructure dependencies.\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\n/**\n * Per-consumer configuration forwarded from `startBatchConsumer` into `handleEachBatch`.\n * Mirrors `EachMessageOpts` but carries a batch handler and batch-level EOS context.\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\n/**\n * Core batch processing pipeline invoked by the kafkajs `eachBatch` callback.\n * Iterates messages, parses, validates, deduplicates, and checks TTL for each one,\n * then calls the batch handler with the surviving envelopes and retry/DLQ support.\n * Manages EOS offset commits when the consumer runs with `autoCommit: false`.\n * @param payload Raw kafkajs `eachBatch` payload (batch, heartbeat, resolveOffset, commitOffsetsIfNecessary).\n * @param opts Consumer-level options including batch handler, schema map, retry policy, and EOS context.\n * @param deps Shared infrastructure dependencies.\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 { KafkaClientContext } from \"../context\";\nimport type { TopicMapConstraint } from \"../../types\";\nimport { _activeTransactionalIds } from \"../producer/lifecycle\";\nimport { toError } from \"./pipeline\";\n\n/** Minimal context surface required by this module. */\ntype StopCtx<T extends TopicMapConstraint<T>> = Pick<\n KafkaClientContext<T>,\n | \"circuitBreaker\"\n | \"companionGroupIds\"\n | \"consumerCreationOptions\"\n | \"consumers\"\n | \"dedupStates\"\n | \"logger\"\n | \"retryTxProducers\"\n | \"runningConsumers\"\n>;\n\n// ── Stop ──────────────────────────────────────────────────────────────────────\n\nexport async function stopConsumerImpl<T extends TopicMapConstraint<T>>(\n ctx: StopCtx<T>,\n groupId?: string,\n): Promise<void> {\n if (groupId === undefined) {\n const tasks: Promise<void>[] = [\n ...Array.from(ctx.consumers.values()).map((c) =>\n c.disconnect().catch(() => {}),\n ),\n ...Array.from(ctx.retryTxProducers.values()).map((p) =>\n p.disconnect().catch(() => {}),\n ),\n ];\n await Promise.allSettled(tasks);\n ctx.consumers.clear();\n ctx.runningConsumers.clear();\n ctx.consumerCreationOptions.clear();\n ctx.companionGroupIds.clear();\n ctx.retryTxProducers.clear();\n ctx.dedupStates.clear();\n ctx.circuitBreaker.clear();\n ctx.logger.log(\"All consumers disconnected\");\n return;\n }\n\n const consumer = ctx.consumers.get(groupId);\n if (!consumer) {\n ctx.logger.warn(\n `stopConsumer: no active consumer for group \"${groupId}\"`,\n );\n return;\n }\n await consumer\n .disconnect()\n .catch((e) =>\n ctx.logger.warn(\n `Error disconnecting consumer \"${groupId}\":`,\n toError(e).message,\n ),\n );\n ctx.consumers.delete(groupId);\n ctx.runningConsumers.delete(groupId);\n ctx.consumerCreationOptions.delete(groupId);\n ctx.dedupStates.delete(groupId);\n ctx.circuitBreaker.removeGroup(groupId);\n ctx.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 = ctx.retryTxProducers.get(mainTxId);\n if (mainTxProducer) {\n await mainTxProducer\n .disconnect()\n .catch((e) =>\n ctx.logger.warn(\n `Error disconnecting main tx producer \"${mainTxId}\":`,\n toError(e).message,\n ),\n );\n _activeTransactionalIds.delete(mainTxId);\n ctx.retryTxProducers.delete(mainTxId);\n }\n\n // Stop all companion retry level consumers and their tx producers\n const companions = ctx.companionGroupIds.get(groupId) ?? [];\n for (const cGroupId of companions) {\n const cConsumer = ctx.consumers.get(cGroupId);\n if (cConsumer) {\n await cConsumer\n .disconnect()\n .catch((e) =>\n ctx.logger.warn(\n `Error disconnecting retry consumer \"${cGroupId}\":`,\n toError(e).message,\n ),\n );\n ctx.consumers.delete(cGroupId);\n ctx.runningConsumers.delete(cGroupId);\n ctx.consumerCreationOptions.delete(cGroupId);\n ctx.logger.log(`Retry consumer disconnected: group \"${cGroupId}\"`);\n }\n const txId = `${cGroupId}-tx`;\n const txProducer = ctx.retryTxProducers.get(txId);\n if (txProducer) {\n await txProducer\n .disconnect()\n .catch((e) =>\n ctx.logger.warn(\n `Error disconnecting retry tx producer \"${txId}\":`,\n toError(e).message,\n ),\n );\n _activeTransactionalIds.delete(txId);\n ctx.retryTxProducers.delete(txId);\n }\n }\n ctx.companionGroupIds.delete(groupId);\n}\n\n// ── Pause / Resume ────────────────────────────────────────────────────────────\n\nexport function pauseConsumerImpl<T extends TopicMapConstraint<T>>(\n ctx: StopCtx<T>,\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n): void {\n const gid = groupId ?? ctx.defaultGroupId;\n const consumer = ctx.consumers.get(gid);\n if (!consumer) {\n ctx.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\nexport function resumeConsumerImpl<T extends TopicMapConstraint<T>>(\n ctx: StopCtx<T>,\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n): void {\n const gid = groupId ?? ctx.defaultGroupId;\n const consumer = ctx.consumers.get(gid);\n if (!consumer) {\n ctx.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 (backpressure for consume()). */\nexport function pauseTopicAllPartitions<T extends TopicMapConstraint<T>>(\n ctx: StopCtx<T>,\n gid: string,\n topic: string,\n): void {\n const consumer = ctx.consumers.get(gid);\n if (!consumer) return;\n const assignment = consumer.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 (backpressure for consume()). */\nexport function resumeTopicAllPartitions<T extends TopicMapConstraint<T>>(\n ctx: StopCtx<T>,\n gid: string,\n topic: string,\n): void {\n const consumer = ctx.consumers.get(gid);\n if (!consumer) return;\n const assignment = consumer.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","import type { KafkaClientContext } from \"../context\";\nimport type {\n TopicMapConstraint,\n ConsumerOptions,\n ConsumerHandle,\n BatchMeta,\n SendOptions,\n BatchSendOptions,\n TransactionalHandlerContext,\n} from \"../../types\";\nimport type { EventEnvelope } from \"../../message/envelope\";\nimport { runWithEnvelopeContext } from \"../../message/envelope\";\nimport { handleEachMessage, handleEachBatch, parseSingleMessage } from \"./handler\";\nimport {\n setupConsumer,\n validateTopicConsumerOpts,\n resolveDeduplicationContext,\n messageDepsFor,\n makeEosMainContext,\n launchRetryChain,\n} from \"./setup\";\nimport { createRetryTxProducer, wrapWithTimeoutWarning } from \"../producer/lifecycle\";\nimport { preparePayload } from \"../producer/send\";\nimport { toError } from \"./pipeline\";\n\n// ── startConsumer ─────────────────────────────────────────────────────────────\n\nexport async function startConsumerImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topics: any[],\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n options: ConsumerOptions<T> = {},\n): Promise<ConsumerHandle> {\n validateTopicConsumerOpts(topics, options);\n const setupOptions = options.retryTopics\n ? { ...options, autoCommit: false as const }\n : options;\n const { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry, readyPromise } =\n await setupConsumer(ctx, topics, \"eachMessage\", setupOptions);\n\n if (options.circuitBreaker)\n ctx.circuitBreaker.setConfig(gid, options.circuitBreaker);\n const deps = messageDepsFor(ctx, gid, options);\n const eosMainContext = await makeEosMainContext(ctx, gid, consumer, options);\n\n await consumer.run({\n eachMessage: (payload) =>\n ctx.inFlight.track(() =>\n handleEachMessage(\n payload,\n {\n schemaMap,\n handleMessage,\n interceptors,\n dlq,\n retry,\n retryTopics: options.retryTopics,\n timeoutMs: options.handlerTimeoutMs,\n wrapWithTimeout: (fn, ms, topic) =>\n wrapWithTimeoutWarning(ctx.logger, fn, ms, topic),\n deduplication: resolveDeduplicationContext(\n ctx,\n gid,\n options.deduplication,\n ),\n messageTtlMs: options.messageTtlMs,\n onTtlExpired: options.onTtlExpired,\n eosMainContext,\n },\n deps,\n ),\n ),\n });\n\n ctx.runningConsumers.set(gid, \"eachMessage\");\n if (options.retryTopics && retry) {\n await launchRetryChain(ctx, gid, topicNames, handleMessage, {\n retry,\n dlq,\n interceptors,\n schemaMap,\n assignmentTimeoutMs: options.retryTopicAssignmentTimeoutMs,\n });\n }\n return { groupId: gid, stop: () => stopConsumerByGid(ctx, gid), ready: () => readyPromise };\n}\n\n// ── startBatchConsumer ────────────────────────────────────────────────────────\n\nexport async function startBatchConsumerImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topics: any[],\n handleBatch: (\n envelopes: EventEnvelope<any>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options: ConsumerOptions<T> = {},\n): Promise<ConsumerHandle> {\n validateTopicConsumerOpts(topics, options);\n if (!options.retryTopics && options.autoCommit !== false) {\n ctx.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 const setupOptions = options.retryTopics\n ? { ...options, autoCommit: false as const }\n : options;\n const { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry, readyPromise } =\n await setupConsumer(ctx, topics, \"eachBatch\", setupOptions);\n\n if (options.circuitBreaker)\n ctx.circuitBreaker.setConfig(gid, options.circuitBreaker);\n const deps = messageDepsFor(ctx, gid, options);\n const eosMainContext = await makeEosMainContext(ctx, gid, consumer, options);\n\n await consumer.run({\n eachBatch: (payload) =>\n ctx.inFlight.track(() =>\n handleEachBatch(\n payload,\n {\n schemaMap,\n handleBatch,\n interceptors,\n dlq,\n retry,\n retryTopics: options.retryTopics,\n timeoutMs: options.handlerTimeoutMs,\n wrapWithTimeout: (fn, ms, topic) =>\n wrapWithTimeoutWarning(ctx.logger, fn, ms, topic),\n deduplication: resolveDeduplicationContext(\n ctx,\n gid,\n options.deduplication,\n ),\n messageTtlMs: options.messageTtlMs,\n onTtlExpired: options.onTtlExpired,\n eosMainContext,\n },\n deps,\n ),\n ),\n });\n\n ctx.runningConsumers.set(gid, \"eachBatch\");\n if (options.retryTopics && retry) {\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 await launchRetryChain(ctx, gid, topicNames, handleMessageForRetry, {\n retry,\n dlq,\n interceptors,\n schemaMap,\n assignmentTimeoutMs: options.retryTopicAssignmentTimeoutMs,\n });\n }\n return { groupId: gid, stop: () => stopConsumerByGid(ctx, gid), ready: () => readyPromise };\n}\n\n// ── startTransactionalConsumer ────────────────────────────────────────────────\n\nexport async function startTransactionalConsumerImpl<\n T extends TopicMapConstraint<T>,\n>(\n ctx: KafkaClientContext<T>,\n topics: any[],\n handler: (\n envelope: EventEnvelope<any>,\n tx: TransactionalHandlerContext<T>,\n ) => Promise<void>,\n options: ConsumerOptions<T> = {},\n): Promise<ConsumerHandle> {\n if (options.retryTopics) {\n throw new Error(\n \"startTransactionalConsumer: retryTopics is not supported. \" +\n \"EOS is already guaranteed by the transaction — redelivery on failure is handled automatically.\",\n );\n }\n\n const setupOptions = { ...options, autoCommit: false as const };\n const { consumer, schemaMap, gid, readyPromise } = await setupConsumer(\n ctx,\n topics,\n \"eachMessage\",\n setupOptions,\n );\n\n const txProducer = await createRetryTxProducer(ctx, `${gid}-txc`);\n const deps = messageDepsFor(ctx, gid);\n\n await consumer.run({\n eachMessage: ({ topic, partition, message }) =>\n ctx.inFlight.track(async () => {\n const envelope = await parseSingleMessage(\n message,\n topic,\n partition,\n schemaMap,\n options.interceptors ?? [],\n false,\n deps,\n );\n\n const nextOffset = String(Number.parseInt(message.offset, 10) + 1);\n\n if (envelope === null) {\n await consumer.commitOffsets([\n { topic, partition, offset: nextOffset },\n ]);\n return;\n }\n\n const tx = await txProducer.transaction();\n\n const txCtx: TransactionalHandlerContext<T> = {\n send: async (t: any, msg: any, sendOpts?: SendOptions) => {\n const payload = await preparePayload(ctx, t, [\n {\n value: msg,\n key: sendOpts?.key,\n headers: sendOpts?.headers,\n correlationId: sendOpts?.correlationId,\n schemaVersion: sendOpts?.schemaVersion,\n eventId: sendOpts?.eventId,\n },\n ]);\n await tx.send(payload);\n },\n sendBatch: async (t: any, msgs: any[], batchOpts?: BatchSendOptions) => {\n const payload = await preparePayload(\n ctx,\n t,\n msgs,\n batchOpts?.compression,\n );\n await tx.send(payload);\n },\n };\n\n try {\n await runWithEnvelopeContext(\n {\n correlationId: envelope.correlationId,\n traceparent: envelope.traceparent,\n },\n () => handler(envelope, txCtx),\n );\n await tx.sendOffsets({\n consumer,\n topics: [\n { topic, partitions: [{ partition, offset: nextOffset }] },\n ],\n });\n await tx.commit();\n deps.onMessage?.(envelope);\n } catch (err) {\n try {\n await tx.abort();\n } catch {}\n ctx.logger.warn(\n `startTransactionalConsumer: handler failed on ${topic}[${partition}]@${message.offset} — ` +\n `tx aborted, message will be redelivered (${toError(err).message})`,\n );\n throw err;\n }\n }),\n });\n\n ctx.runningConsumers.set(gid, \"eachMessage\");\n return { groupId: gid, stop: () => stopConsumerByGid(ctx, gid), ready: () => readyPromise };\n}\n\n// ── Shared stop helper (avoids circular dep with stop.ts) ─────────────────────\n\nimport { stopConsumerImpl } from \"./stop\";\n\nfunction stopConsumerByGid<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n gid: string,\n): Promise<void> {\n return stopConsumerImpl(ctx, gid);\n}\n","import type { KafkaClientContext } from \"../context\";\nimport type {\n TopicMapConstraint,\n WindowConsumerOptions,\n WindowMeta,\n ConsumerHandle,\n} from \"../../types\";\nimport type { EventEnvelope } from \"../../message/envelope\";\nimport { startConsumerImpl } from \"./start\";\nimport { toError } from \"./pipeline\";\n\n// ── startWindowConsumer ───────────────────────────────────────────────────────\n\nexport async function startWindowConsumerImpl<\n T extends TopicMapConstraint<T>,\n K extends keyof T & string,\n>(\n ctx: KafkaClientContext<T>,\n topic: K,\n handler: (envelopes: EventEnvelope<T[K]>[], meta: WindowMeta) => Promise<void>,\n options: WindowConsumerOptions<T>,\n): Promise<ConsumerHandle> {\n const { maxMessages, maxMs, ...consumerOptions } = options;\n\n if (maxMessages <= 0)\n throw new Error(\"startWindowConsumer: maxMessages must be > 0\");\n if (maxMs <= 0) throw new Error(\"startWindowConsumer: maxMs must be > 0\");\n if ((consumerOptions as any).retryTopics) {\n throw new Error(\n \"startWindowConsumer() does not support retryTopics. \" +\n \"Use startConsumer() with retryTopics: true for guaranteed retry delivery.\",\n );\n }\n\n const buffer: EventEnvelope<T[K]>[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n let windowStart = 0;\n\n const flush = async (trigger: \"size\" | \"time\"): Promise<void> => {\n if (flushTimer !== null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n if (buffer.length === 0) return;\n const envelopes = buffer.splice(0);\n await handler(envelopes, { trigger, windowStart, windowEnd: Date.now() });\n };\n\n const scheduleFlush = (): void => {\n if (flushTimer !== null) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n flush(\"time\").catch((err) => {\n ctx.logger.warn(\n `startWindowConsumer: time-triggered flush error — ${toError(err).message}`,\n );\n });\n }, maxMs);\n };\n\n const handle = await startConsumerImpl(\n ctx,\n [topic as any],\n async (envelope) => {\n if (buffer.length === 0) windowStart = Date.now();\n buffer.push(envelope as EventEnvelope<T[K]>);\n scheduleFlush();\n if (buffer.length >= maxMessages) await flush(\"size\");\n },\n consumerOptions as any,\n );\n\n const originalStop = handle.stop.bind(handle);\n handle.stop = async (): Promise<void> => {\n if (flushTimer !== null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n if (buffer.length > 0) {\n const envelopes = buffer.splice(0);\n await handler(envelopes, {\n trigger: \"time\",\n windowStart,\n windowEnd: Date.now(),\n }).catch(async (err) => {\n const error = toError(err);\n ctx.logger.warn(\n `startWindowConsumer: shutdown flush error — ${error.message}`,\n );\n for (const envelope of envelopes) {\n await Promise.resolve(\n ctx.onMessageLost?.({\n topic: envelope.topic,\n error,\n attempt: 0,\n headers: envelope.headers,\n }),\n ).catch(() => {});\n }\n });\n }\n return originalStop();\n };\n\n return handle;\n}\n","import type { KafkaClientContext } from \"../context\";\nimport type {\n TopicMapConstraint,\n ConsumerOptions,\n ConsumerHandle,\n RoutingOptions,\n} from \"../../types\";\nimport type { EventEnvelope } from \"../../message/envelope\";\nimport { startConsumerImpl } from \"./start\";\n\n// ── startRoutedConsumer ───────────────────────────────────────────────────────\n\nexport async function startRoutedConsumerImpl<\n T extends TopicMapConstraint<T>,\n K extends Array<keyof T>,\n>(\n ctx: KafkaClientContext<T>,\n topics: K,\n routing: RoutingOptions<T[K[number]]>,\n options?: ConsumerOptions<T>,\n): Promise<ConsumerHandle> {\n const { header, routes, fallback } = routing;\n const handleMessage = async (\n envelope: EventEnvelope<T[K[number]]>,\n ): Promise<void> => {\n const headerValue = envelope.headers[header];\n const routeHandler =\n headerValue === undefined ? undefined : routes[headerValue];\n if (routeHandler) {\n await routeHandler(envelope);\n } else {\n await fallback?.(envelope);\n }\n };\n return startConsumerImpl(ctx, topics, handleMessage, options);\n}\n","import type { KafkaClientContext } from \"../context\";\nimport type {\n TopicMapConstraint,\n ReadSnapshotOptions,\n CheckpointResult,\n CheckpointRestoreResult,\n RestoreCheckpointOptions,\n CheckpointEntry,\n} from \"../../types\";\nimport type { EventEnvelope } from \"../../message/envelope\";\nimport { decodeHeaders, extractEnvelope } from \"../../message/envelope\";\nimport { toError } from \"./pipeline\";\n\n/** Minimal context surface required by this module. */\ntype SnapshotCtx<T extends TopicMapConstraint<T>> = Pick<\n KafkaClientContext<T>,\n | \"adminOps\"\n | \"clientId\"\n | \"defaultGroupId\"\n | \"transport\"\n | \"logger\"\n | \"producer\"\n | \"runningConsumers\"\n>;\n\n// ── readSnapshot ──────────────────────────────────────────────────────────────\n\nexport async function readSnapshotImpl<\n T extends TopicMapConstraint<T>,\n K extends keyof T & string,\n>(\n ctx: SnapshotCtx<T>,\n topic: K,\n options: ReadSnapshotOptions = {},\n): Promise<Map<string, EventEnvelope<T[K]>>> {\n await ctx.adminOps.ensureConnected();\n\n let offsets: Array<{ partition: number; low: string; high: string }>;\n try {\n offsets = await ctx.adminOps.admin.fetchTopicOffsets(topic);\n } catch {\n ctx.logger.warn(\n `readSnapshot: could not fetch offsets for \"${String(topic)}\", returning empty snapshot`,\n );\n return new Map();\n }\n\n const targets = new Map<number, number>();\n for (const { partition, high, low } of offsets) {\n const highN = Number.parseInt(high, 10);\n const lowN = Number.parseInt(low, 10);\n if (highN > lowN) targets.set(partition, highN - 1);\n }\n\n if (targets.size === 0) {\n ctx.logger.debug?.(\n `readSnapshot: topic \"${String(topic)}\" is empty — returning empty snapshot`,\n );\n return new Map();\n }\n\n const snapshot = new Map<string, EventEnvelope<T[K]>>();\n const remaining = new Set(targets.keys());\n const snapshotGroupId = `${ctx.clientId}-snapshot-${Date.now()}`;\n\n await new Promise<void>((resolve, reject) => {\n const consumer = ctx.transport.consumer({\n groupId: snapshotGroupId,\n fromBeginning: true,\n });\n const cleanup = () => {\n consumer\n .disconnect()\n .catch(() => {})\n .finally(() => {\n ctx.adminOps.deleteGroups([snapshotGroupId]).catch(() => {});\n });\n };\n\n consumer\n .connect()\n .then(() => consumer.subscribe({ topics: [topic] }))\n .then(() =>\n consumer.run({\n eachMessage: async ({ topic: t, partition, message }) => {\n if (!remaining.has(partition)) return;\n\n const msgOffsetN = Number.parseInt(message.offset, 10);\n applySnapshotMessage(snapshot, options, ctx, t, partition, message);\n\n if (msgOffsetN >= targets.get(partition)!) {\n remaining.delete(partition);\n if (remaining.size === 0) {\n cleanup();\n resolve();\n }\n }\n },\n }),\n )\n .catch((err) => {\n cleanup();\n reject(err);\n });\n });\n\n ctx.logger.log(\n `readSnapshot: ${snapshot.size} key(s) from \"${String(topic)}\"`,\n );\n return snapshot;\n}\n\nfunction applySnapshotMessage<\n T extends TopicMapConstraint<T>,\n K extends keyof T & string,\n>(\n snapshot: Map<string, EventEnvelope<T[K]>>,\n options: ReadSnapshotOptions,\n ctx: SnapshotCtx<T>,\n t: string,\n partition: number,\n message: { key: Buffer | null; value: Buffer | null; offset: string; headers?: any },\n): void {\n let key: string | null = null;\n if (message.key) {\n key = Buffer.isBuffer(message.key)\n ? message.key.toString()\n : String(message.key);\n }\n\n if (message.value === null || message.value === undefined) {\n if (key !== null) {\n snapshot.delete(key);\n options.onTombstone?.(key);\n }\n return;\n }\n\n if (key === null) return;\n\n const rawValue = Buffer.isBuffer(message.value)\n ? message.value.toString()\n : String(message.value);\n try {\n const jsonValue = JSON.parse(rawValue);\n const headers = decodeHeaders(message.headers);\n const parsed: T[K] = options.schema ? options.schema.parse(jsonValue) : jsonValue;\n snapshot.set(\n key,\n extractEnvelope<T[K]>(parsed, headers, t, partition, message.offset),\n );\n } catch (err) {\n ctx.logger.warn(\n `readSnapshot: skipping ${t}:${partition}@${message.offset} — ${toError(err).message}`,\n );\n }\n}\n\n// ── checkpointOffsets ─────────────────────────────────────────────────────────\n\nexport async function checkpointOffsetsImpl<T extends TopicMapConstraint<T>>(\n ctx: SnapshotCtx<T>,\n groupId: string | undefined,\n checkpointTopic: string,\n): Promise<CheckpointResult> {\n const gid = groupId ?? ctx.defaultGroupId;\n await ctx.adminOps.ensureConnected();\n\n const committed = await ctx.adminOps.admin.fetchOffsets({ groupId: gid });\n\n const offsets: CheckpointEntry[] = [];\n for (const { topic, partitions } of committed) {\n for (const { partition, offset } of partitions) {\n offsets.push({ topic, partition, offset });\n }\n }\n\n const savedAt = Date.now();\n const payload = JSON.stringify({ groupId: gid, offsets, savedAt });\n\n await ctx.producer.send({\n topic: checkpointTopic,\n messages: [\n {\n key: gid,\n value: payload,\n headers: {\n \"x-checkpoint-group-id\": [gid],\n \"x-checkpoint-timestamp\": [String(savedAt)],\n },\n },\n ],\n });\n\n const topics = [...new Set(offsets.map((e) => e.topic))];\n ctx.logger.log(\n `checkpointOffsets: saved ${offsets.length} partition(s) for group \"${gid}\" → \"${checkpointTopic}\"`,\n );\n return { groupId: gid, topics, partitionCount: offsets.length, savedAt };\n}\n\n// ── restoreFromCheckpoint ─────────────────────────────────────────────────────\n\nexport async function restoreFromCheckpointImpl<T extends TopicMapConstraint<T>>(\n ctx: SnapshotCtx<T>,\n groupId: string | undefined,\n checkpointTopic: string,\n options: RestoreCheckpointOptions = {},\n): Promise<CheckpointRestoreResult> {\n const gid = groupId ?? ctx.defaultGroupId;\n\n if (ctx.runningConsumers.has(gid)) {\n throw new Error(\n `restoreFromCheckpoint: consumer group \"${gid}\" is still running. ` +\n `Call stopConsumer(\"${gid}\") before restoring offsets.`,\n );\n }\n\n await ctx.adminOps.ensureConnected();\n\n const checkpoints: Array<{ savedAt: number; offsets: CheckpointEntry[] }> = [];\n\n let hwmOffsets: Array<{ partition: number; low: string; high: string }>;\n try {\n hwmOffsets = await ctx.adminOps.admin.fetchTopicOffsets(checkpointTopic);\n } catch {\n throw new Error(\n `restoreFromCheckpoint: could not fetch offsets for \"${checkpointTopic}\" — does the topic exist?`,\n );\n }\n\n const targets = new Map<number, number>();\n for (const { partition, high, low } of hwmOffsets) {\n const highN = Number.parseInt(high, 10);\n if (highN > Number.parseInt(low, 10)) targets.set(partition, highN - 1);\n }\n\n if (targets.size > 0) {\n const checkpointGroupId = `${ctx.clientId}-checkpoint-restore-${Date.now()}`;\n await new Promise<void>((resolve, reject) => {\n const consumer = ctx.transport.consumer({\n groupId: checkpointGroupId,\n fromBeginning: true,\n });\n const cleanup = () => {\n consumer\n .disconnect()\n .catch(() => {})\n .finally(() => {\n ctx.adminOps.deleteGroups([checkpointGroupId]).catch(() => {});\n });\n };\n const remaining = new Set(targets.keys());\n\n consumer\n .connect()\n .then(() => consumer.subscribe({ topics: [checkpointTopic] }))\n .then(() =>\n consumer.run({\n eachMessage: async ({ partition, message }) => {\n if (!remaining.has(partition)) return;\n\n let msgKey: string | null = null;\n if (message.key) {\n msgKey = Buffer.isBuffer(message.key)\n ? message.key.toString()\n : String(message.key);\n }\n\n if (msgKey === gid && message.value) {\n try {\n const raw = Buffer.isBuffer(message.value)\n ? message.value.toString()\n : String(message.value);\n const parsed = JSON.parse(raw) as {\n groupId: string;\n offsets: CheckpointEntry[];\n savedAt: number;\n };\n checkpoints.push({\n savedAt: parsed.savedAt,\n offsets: parsed.offsets,\n });\n } catch {\n ctx.logger.warn(\n `restoreFromCheckpoint: skipping malformed checkpoint at partition ${partition}@${message.offset}`,\n );\n }\n }\n\n if (\n Number.parseInt(message.offset, 10) >= targets.get(partition)!\n ) {\n remaining.delete(partition);\n if (remaining.size === 0) {\n cleanup();\n resolve();\n }\n }\n },\n }),\n )\n .catch((err) => {\n cleanup();\n reject(err);\n });\n });\n }\n\n if (checkpoints.length === 0) {\n throw new Error(\n `restoreFromCheckpoint: no checkpoints found for group \"${gid}\" in \"${checkpointTopic}\"`,\n );\n }\n\n const target = options.timestamp;\n let best: (typeof checkpoints)[number];\n if (target === undefined) {\n best = checkpoints.reduce(\n (acc, c) => (c.savedAt > acc.savedAt ? c : acc),\n checkpoints[0],\n );\n } else {\n const candidates = checkpoints.filter((c) => c.savedAt <= target);\n if (candidates.length > 0) {\n best = candidates.reduce(\n (acc, c) => (c.savedAt > acc.savedAt ? c : acc),\n candidates[0],\n );\n } else {\n best = checkpoints.reduce(\n (acc, c) => (c.savedAt < acc.savedAt ? c : acc),\n checkpoints[0],\n );\n ctx.logger.warn(\n `restoreFromCheckpoint: no checkpoint at or before ${new Date(target).toISOString()} — ` +\n `using oldest available (${new Date(best.savedAt).toISOString()})`,\n );\n }\n }\n\n await ctx.adminOps.seekToOffset(gid, best.offsets);\n\n const checkpointAge = Date.now() - best.savedAt;\n ctx.logger.log(\n `restoreFromCheckpoint: restored ${best.offsets.length} partition(s) for group \"${gid}\" ` +\n `from checkpoint at ${new Date(best.savedAt).toISOString()} (age: ${checkpointAge}ms)`,\n );\n\n return {\n groupId: gid,\n offsets: best.offsets,\n restoredAt: best.savedAt,\n checkpointAge,\n };\n}\n","import type { KafkaTransport } from \"../transport.interface\";\nimport { ConfluentTransport } from \"./confluent-transport\";\nimport type { 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 BatchMeta,\n DlqReplayOptions,\n ConsumerGroupSummary,\n TopicDescription,\n MessageHeaders,\n ReadSnapshotOptions,\n CheckpointResult,\n CheckpointRestoreResult,\n RestoreCheckpointOptions,\n WindowConsumerOptions,\n WindowMeta,\n RoutingOptions,\n TransactionalHandlerContext,\n} from \"../types\";\n\n// Re-export all types so existing `import { ... } from './kafka.client'` keeps working\nexport * from \"../types\";\n\nimport { getOrCreateConsumer } from \"./consumer/ops\";\nimport { AdminOps } from \"./admin/ops\";\nimport { replayDlqTopic } from \"./consumer/dlq-replay\";\nimport { MetricsManager } from \"./infra/metrics.manager\";\nimport { InFlightTracker } from \"./infra/inflight.tracker\";\nimport { CircuitBreakerManager } from \"./infra/circuit-breaker.manager\";\nimport { AsyncQueue } from \"./consumer/queue\";\nimport { toError } from \"./consumer/pipeline\";\n\nimport type { KafkaClientContext } from \"./context\";\nimport { connectProducerImpl, disconnectImpl } from \"./producer/lifecycle\";\nimport {\n sendMessageImpl,\n sendBatchImpl,\n sendTombstoneImpl,\n transactionImpl,\n} from \"./producer/send\";\nimport { buildRetryTopicDeps } from \"./consumer/setup\";\nimport { startConsumerImpl, startBatchConsumerImpl, startTransactionalConsumerImpl } from \"./consumer/start\";\nimport { startWindowConsumerImpl } from \"./consumer/window\";\nimport { startRoutedConsumerImpl } from \"./consumer/routed\";\nimport {\n stopConsumerImpl,\n pauseConsumerImpl,\n resumeConsumerImpl,\n pauseTopicAllPartitions,\n resumeTopicAllPartitions,\n} from \"./consumer/stop\";\nimport {\n readSnapshotImpl,\n checkpointOffsetsImpl,\n restoreFromCheckpointImpl,\n} from \"./consumer/snapshot\";\n\n/** DLQ header keys added by the pipeline — stripped before re-publishing. */\nconst 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 * 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 public readonly clientId: ClientId;\n private readonly ctx: KafkaClientContext<T>;\n\n /**\n * Create a new KafkaClient.\n * @param clientId Unique client identifier (used in Kafka metadata and logs).\n * @param groupId Default consumer group ID for this client.\n * @param brokers Array of broker addresses, e.g. `['localhost:9092']`.\n * @param options Optional client-wide configuration.\n * @example\n * ```ts\n * const kafka = new KafkaClient('my-service', 'my-service-group', ['localhost:9092'], {\n * lagThrottle: { maxLag: 10_000 },\n * onMessageLost: (ctx) => logger.error('Message lost', ctx),\n * });\n * await kafka.connectProducer();\n * ```\n */\n constructor(\n clientId: ClientId,\n groupId: GroupId,\n brokers: string[],\n options?: KafkaClientOptions,\n ) {\n this.clientId = clientId;\n\n const logger = options?.logger ?? {\n log: (msg: string) => console.log(`[KafkaClient:${clientId}] ${msg}`),\n warn: (msg: string, ...args: any[]) =>\n console.warn(`[KafkaClient:${clientId}] ${msg}`, ...args),\n error: (msg: string, ...args: any[]) =>\n console.error(`[KafkaClient:${clientId}] ${msg}`, ...args),\n debug: (msg: string, ...args: any[]) =>\n console.debug(`[KafkaClient:${clientId}] ${msg}`, ...args),\n };\n\n const transport: KafkaTransport =\n options?.transport ?? new ConfluentTransport(clientId, brokers);\n const producer = transport.producer();\n\n const runningConsumers = new Map<string, \"eachMessage\" | \"eachBatch\">();\n const consumers = new Map();\n const consumerCreationOptions = new Map<string, { fromBeginning: boolean; autoCommit: boolean }>();\n const schemaRegistry = new Map<string, SchemaLike>();\n\n const adminOps = new AdminOps({\n admin: transport.admin(),\n logger,\n runningConsumers,\n defaultGroupId: groupId,\n clientId,\n });\n\n const circuitBreaker = new CircuitBreakerManager({\n pauseConsumer: (gid, assignments) => pauseConsumerImpl(this.ctx, gid, assignments),\n resumeConsumer: (gid, assignments) => resumeConsumerImpl(this.ctx, gid, assignments),\n logger,\n instrumentation: options?.instrumentation ?? [],\n });\n\n const metrics = new MetricsManager({\n instrumentation: options?.instrumentation ?? [],\n onCircuitFailure: (envelope, gid) => circuitBreaker.onFailure(envelope, gid),\n onCircuitSuccess: (envelope, gid) => circuitBreaker.onSuccess(envelope, gid),\n });\n\n const inFlight = new InFlightTracker((msg) => logger.warn(msg));\n\n // Partially construct context — retryTopicDeps is patched below\n const ctx = {\n clientId,\n defaultGroupId: groupId,\n logger,\n autoCreateTopicsEnabled: options?.autoCreateTopics ?? false,\n strictSchemasEnabled: options?.strictSchemas ?? true,\n numPartitions: options?.numPartitions ?? 1,\n txId: options?.transactionalId ?? `${clientId}-tx`,\n clockRecoveryTopics: options?.clockRecovery?.topics ?? [],\n lagThrottleOpts: options?.lagThrottle,\n instrumentation: options?.instrumentation ?? [],\n onMessageLost: options?.onMessageLost,\n onTtlExpired: options?.onTtlExpired,\n onRebalance: options?.onRebalance,\n transport,\n producer,\n txProducer: undefined as any,\n txProducerInitPromise: undefined as any,\n retryTxProducers: new Map(),\n consumers,\n runningConsumers,\n consumerCreationOptions,\n companionGroupIds: new Map<string, string[]>(),\n dedupStates: new Map<string, Map<string, number>>(),\n ensuredTopics: new Set<string>(),\n ensureTopicPromises: new Map<string, Promise<void>>(),\n schemaRegistry,\n _lagThrottled: false,\n _lagThrottleTimer: undefined as any,\n _lamportClock: 0,\n circuitBreaker,\n adminOps,\n metrics,\n inFlight,\n producerOpsDeps: {\n schemaRegistry,\n strictSchemasEnabled: options?.strictSchemas ?? true,\n instrumentation: options?.instrumentation ?? [],\n logger,\n nextLamportClock: () => 0, // patched below\n },\n consumerOpsDeps: {\n consumers,\n consumerCreationOptions,\n transport,\n onRebalance: options?.onRebalance,\n logger,\n },\n retryTopicDeps: {} as any, // patched below\n } satisfies KafkaClientContext<T>;\n\n // Patch mutable closures that need the fully constructed ctx\n ctx.producerOpsDeps = {\n schemaRegistry,\n strictSchemasEnabled: options?.strictSchemas ?? true,\n instrumentation: options?.instrumentation ?? [],\n logger,\n nextLamportClock: () => ++ctx._lamportClock,\n };\n ctx.retryTopicDeps = buildRetryTopicDeps(ctx);\n\n this.ctx = ctx;\n }\n\n // ── Send ──────────────────────────────────────────────────────────\n\n /** @inheritDoc */\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 return sendMessageImpl(this.ctx, topicOrDesc, message, options);\n }\n\n /** @inheritDoc */\n public async sendTombstone(\n topic: string,\n key: string,\n headers?: MessageHeaders,\n ): Promise<void> {\n return sendTombstoneImpl(this.ctx, topic, key, headers);\n }\n\n /** @inheritDoc */\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 return sendBatchImpl(this.ctx, topicOrDesc, messages, options);\n }\n\n /** @inheritDoc */\n public async transaction(\n fn: (ctx: TransactionContext<T>) => Promise<void>,\n ): Promise<void> {\n return transactionImpl(this.ctx, fn);\n }\n\n // ── Producer lifecycle ────────────────────────────────────────────\n\n /** @inheritDoc */\n public async connectProducer(): Promise<void> {\n return connectProducerImpl(this.ctx);\n }\n\n /** @internal */\n public async disconnectProducer(): Promise<void> {\n if (this.ctx._lagThrottleTimer) {\n clearInterval(this.ctx._lagThrottleTimer);\n this.ctx._lagThrottleTimer = undefined;\n }\n await this.ctx.producer.disconnect();\n this.ctx.logger.log(\"Producer disconnected\");\n }\n\n // ── Consumer: eachMessage ─────────────────────────────────────────\n\n /** @inheritDoc */\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 return startConsumerImpl(this.ctx, topics, handleMessage, options);\n }\n\n // ── Consumer: eachBatch ───────────────────────────────────────────\n\n /** @inheritDoc */\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 return startBatchConsumerImpl(this.ctx, topics, handleBatch, options);\n }\n\n // ── Consumer: AsyncIterableIterator ──────────────────────────────\n\n /** @inheritDoc */\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.ctx.defaultGroupId;\n const queue = new AsyncQueue<EventEnvelope<T[K]>>(\n options?.queueHighWaterMark,\n () => pauseTopicAllPartitions(this.ctx, gid, topic as string),\n () => resumeTopicAllPartitions(this.ctx, 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: windowed ────────────────────────────────────────────\n\n /** @inheritDoc */\n public startWindowConsumer<K extends keyof T & string>(\n topic: K,\n handler: (\n envelopes: EventEnvelope<T[K]>[],\n meta: WindowMeta,\n ) => Promise<void>,\n options: WindowConsumerOptions<T>,\n ): Promise<ConsumerHandle> {\n return startWindowConsumerImpl(this.ctx, topic, handler, options);\n }\n\n // ── Consumer: header routing ──────────────────────────────────────\n\n /** @inheritDoc */\n public startRoutedConsumer<K extends Array<keyof T>>(\n topics: K,\n routing: RoutingOptions<T[K[number]]>,\n options?: ConsumerOptions<T>,\n ): Promise<ConsumerHandle> {\n return startRoutedConsumerImpl(this.ctx, topics, routing, options);\n }\n\n // ── Consumer: transactional EOS ───────────────────────────────────\n\n /** @inheritDoc */\n public async startTransactionalConsumer<K extends Array<keyof T>>(\n topics: K,\n handler: (\n envelope: EventEnvelope<T[K[number]]>,\n tx: TransactionalHandlerContext<T>,\n ) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<ConsumerHandle> {\n return startTransactionalConsumerImpl(this.ctx, topics, handler, options);\n }\n\n // ── Consumer lifecycle ────────────────────────────────────────────\n\n /** @inheritDoc */\n public async stopConsumer(groupId?: string): Promise<void> {\n return stopConsumerImpl(this.ctx, groupId);\n }\n\n /** @inheritDoc */\n public pauseConsumer(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n ): void {\n return pauseConsumerImpl(this.ctx, groupId, assignments);\n }\n\n /** @inheritDoc */\n public resumeConsumer(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n ): void {\n return resumeConsumerImpl(this.ctx, groupId, assignments);\n }\n\n // ── DLQ replay ────────────────────────────────────────────────────\n\n /** @inheritDoc */\n public async replayDlq(\n topic: string,\n options: DlqReplayOptions = {},\n ): Promise<{ replayed: number; skipped: number }> {\n await this.ctx.adminOps.ensureConnected();\n return replayDlqTopic(\n topic,\n {\n logger: this.ctx.logger,\n fetchTopicOffsets: (t) => this.ctx.adminOps.admin.fetchTopicOffsets(t),\n send: async (t, messages) => {\n await this.ctx.producer.send({ topic: t, messages });\n },\n createConsumer: (gid, fromBeginning) =>\n getOrCreateConsumer(gid, fromBeginning, true, this.ctx.consumerOpsDeps),\n cleanupConsumer: (consumer, gid, deleteGroup) => {\n consumer\n .disconnect()\n .catch(() => {})\n .finally(() => {\n this.ctx.consumers.delete(gid);\n this.ctx.runningConsumers.delete(gid);\n this.ctx.consumerCreationOptions.delete(gid);\n if (deleteGroup) {\n this.ctx.adminOps.deleteGroups([gid]).catch(() => {});\n }\n });\n },\n dlqHeaderKeys: DLQ_HEADER_KEYS,\n },\n options,\n );\n }\n\n // ── Snapshot & checkpoint ─────────────────────────────────────────\n\n /** @inheritDoc */\n public async readSnapshot<K extends keyof T & string>(\n topic: K,\n options: ReadSnapshotOptions = {},\n ): Promise<Map<string, EventEnvelope<T[K]>>> {\n return readSnapshotImpl(this.ctx, topic, options);\n }\n\n /** @inheritDoc */\n public async checkpointOffsets(\n groupId: string | undefined,\n checkpointTopic: string,\n ): Promise<CheckpointResult> {\n return checkpointOffsetsImpl(this.ctx, groupId, checkpointTopic);\n }\n\n /** @inheritDoc */\n public async restoreFromCheckpoint(\n groupId: string | undefined,\n checkpointTopic: string,\n options: RestoreCheckpointOptions = {},\n ): Promise<CheckpointRestoreResult> {\n return restoreFromCheckpointImpl(this.ctx, groupId, checkpointTopic, options);\n }\n\n // ── Admin ─────────────────────────────────────────────────────────\n\n /** @inheritDoc */\n public async resetOffsets(\n groupId: string | undefined,\n topic: string,\n position: \"earliest\" | \"latest\",\n ): Promise<void> {\n return this.ctx.adminOps.resetOffsets(groupId, topic, position);\n }\n\n /** @inheritDoc */\n public async seekToOffset(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partition: number; offset: string }>,\n ): Promise<void> {\n return this.ctx.adminOps.seekToOffset(groupId, assignments);\n }\n\n /** @inheritDoc */\n public async seekToTimestamp(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partition: number; timestamp: number }>,\n ): Promise<void> {\n return this.ctx.adminOps.seekToTimestamp(groupId, assignments);\n }\n\n /** @inheritDoc */\n public async getConsumerLag(\n groupId?: string,\n ): Promise<Array<{ topic: string; partition: number; lag: number }>> {\n return this.ctx.adminOps.getConsumerLag(groupId);\n }\n\n /** @inheritDoc */\n public async checkStatus(): Promise<import(\"../types\").KafkaHealthResult> {\n return this.ctx.adminOps.checkStatus();\n }\n\n /** @inheritDoc */\n public async listConsumerGroups(): Promise<ConsumerGroupSummary[]> {\n return this.ctx.adminOps.listConsumerGroups();\n }\n\n /** @inheritDoc */\n public async describeTopics(topics?: string[]): Promise<TopicDescription[]> {\n return this.ctx.adminOps.describeTopics(topics);\n }\n\n /** @inheritDoc */\n public async deleteRecords(\n topic: string,\n partitions: Array<{ partition: number; offset: string }>,\n ): Promise<void> {\n return this.ctx.adminOps.deleteRecords(topic, partitions);\n }\n\n // ── Circuit breaker ───────────────────────────────────────────────\n\n /** @inheritDoc */\n public getCircuitState(\n topic: string,\n partition: number,\n groupId?: string,\n ):\n | { status: \"closed\" | \"open\" | \"half-open\"; failures: number; windowSize: number }\n | undefined {\n return this.ctx.circuitBreaker.getState(\n topic,\n partition,\n groupId ?? this.ctx.defaultGroupId,\n );\n }\n\n // ── Metrics ───────────────────────────────────────────────────────\n\n /** @inheritDoc */\n public getMetrics(topic?: string): Readonly<import(\"../types\").KafkaMetrics> {\n return this.ctx.metrics.getMetrics(topic);\n }\n\n /** @inheritDoc */\n public resetMetrics(topic?: string): void {\n this.ctx.metrics.resetMetrics(topic);\n }\n\n public getClientId(): ClientId {\n return this.clientId;\n }\n\n // ── Lifecycle ─────────────────────────────────────────────────────\n\n /** @inheritDoc */\n public async disconnect(drainTimeoutMs = 30_000): Promise<void> {\n return disconnectImpl(this.ctx, drainTimeoutMs);\n }\n\n /** NestJS lifecycle hook — called automatically on module teardown. */\n public async onModuleDestroy(): Promise<void> {\n await this.disconnect();\n }\n\n /** @inheritDoc */\n public enableGracefulShutdown(\n signals: NodeJS.Signals[] = [\"SIGTERM\", \"SIGINT\"],\n drainTimeoutMs = 30_000,\n ): void {\n const handler = () => {\n this.ctx.logger.log(\n \"Shutdown signal received — draining in-flight handlers...\",\n );\n this.disconnect(drainTimeoutMs)\n .catch((err) =>\n this.ctx.logger.error(\n \"Error during graceful shutdown:\",\n toError(err).message,\n ),\n )\n .finally(() => process.exit(0));\n };\n for (const signal of signals) process.once(signal, handler);\n }\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","import { Module, DynamicModule, Provider, Logger } from \"@nestjs/common\";\nimport { DiscoveryModule } from \"@nestjs/core\";\nimport {\n KafkaClient,\n ClientId,\n GroupId,\n TopicMapConstraint,\n KafkaInstrumentation,\n KafkaClientOptions,\n} from \"../client/kafka.client\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { KafkaExplorer } from \"./kafka.explorer\";\n\n/** Shared configuration fields for both `register()` and `registerAsync()`. */\ninterface KafkaModuleBaseOptions {\n /** Optional name for multi-client setups. Must match `@InjectKafkaClient(name)`. */\n name?: string;\n /** If true, makes KAFKA_CLIENT available globally without importing KafkaModule in every feature module. */\n isGlobal?: boolean;\n}\n\n/** Synchronous configuration for `KafkaModule.register()`. */\nexport interface KafkaModuleOptions extends KafkaModuleBaseOptions {\n /** Unique Kafka client identifier. */\n clientId: ClientId;\n /** Consumer group identifier. */\n groupId: GroupId;\n /** List of Kafka broker addresses. */\n brokers: string[];\n /** Auto-create topics via admin on first use (send/consume). Useful for development. */\n autoCreateTopics?: boolean;\n /** When `true`, string topic keys are validated against any schema previously registered via a TopicDescriptor. Default: `true`. */\n strictSchemas?: boolean;\n /** Number of partitions for auto-created topics. Default: `1`. */\n numPartitions?: number;\n /** Client-wide instrumentation hooks (e.g. OTel). Applied to both send and consume paths. */\n instrumentation?: KafkaInstrumentation[];\n /** Called when a message is dropped without being sent to a DLQ. @see `KafkaClientOptions.onMessageLost` */\n onMessageLost?: KafkaClientOptions[\"onMessageLost\"];\n /** Called whenever a consumer group rebalance occurs. @see `KafkaClientOptions.onRebalance` */\n onRebalance?: KafkaClientOptions[\"onRebalance\"];\n}\n\n/** Async configuration for `KafkaModule.registerAsync()` with dependency injection. */\nexport interface KafkaModuleAsyncOptions extends KafkaModuleBaseOptions {\n imports?: any[];\n useFactory: (\n ...args: any[]\n ) => KafkaModuleOptions | Promise<KafkaModuleOptions>;\n inject?: any[];\n}\n\n/**\n * NestJS dynamic module for registering type-safe Kafka clients.\n * Use `register()` for static config or `registerAsync()` for DI-based config.\n */\n@Module({})\nexport class KafkaModule {\n /** Register a Kafka client with static options. */\n static register<T extends TopicMapConstraint<T>>(\n options: KafkaModuleOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(options.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: () => KafkaModule.buildClient<T>(options),\n };\n\n return {\n global: options.isGlobal ?? false,\n module: KafkaModule,\n imports: [DiscoveryModule],\n providers: [kafkaClientProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n\n /** Register a Kafka client with async/factory-based options. */\n static registerAsync<T extends TopicMapConstraint<T>>(\n asyncOptions: KafkaModuleAsyncOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(asyncOptions.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: async (...args: any[]): Promise<KafkaClient<T>> =>\n KafkaModule.buildClient<T>(await asyncOptions.useFactory(...args)),\n inject: asyncOptions.inject || [],\n };\n\n return {\n global: asyncOptions.isGlobal ?? false,\n module: KafkaModule,\n imports: [...(asyncOptions.imports || []), DiscoveryModule],\n providers: [kafkaClientProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n\n private static async buildClient<T extends TopicMapConstraint<T>>(\n options: KafkaModuleOptions,\n ): Promise<KafkaClient<T>> {\n const client = new KafkaClient<T>(\n options.clientId,\n options.groupId,\n options.brokers,\n {\n autoCreateTopics: options.autoCreateTopics,\n strictSchemas: options.strictSchemas,\n numPartitions: options.numPartitions,\n instrumentation: options.instrumentation,\n onMessageLost: options.onMessageLost,\n onRebalance: options.onRebalance,\n logger: new Logger(`KafkaClient:${options.clientId}`),\n },\n );\n await client.connectProducer();\n return client;\n }\n}\n","/** Default DI token for the Kafka client. */\nexport const KAFKA_CLIENT = \"KAFKA_CLIENT\";\n\n/** Returns the DI token for a named (or default) Kafka client instance. */\nexport const getKafkaClientToken = (name?: string): string =>\n name ? `KAFKA_CLIENT_${name}` : KAFKA_CLIENT;\n","import { Inject, Injectable, OnModuleInit, Logger } from \"@nestjs/common\";\nimport { DiscoveryService, ModuleRef } from \"@nestjs/core\";\nimport { KafkaClient } from \"../client/kafka.client\";\nimport {\n KAFKA_SUBSCRIBER_METADATA,\n KafkaSubscriberMetadata,\n} from \"./kafka.decorator\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\n\ninterface SubscriberEntry extends KafkaSubscriberMetadata {\n methodName: string | symbol;\n}\n\n/** Discovers `@SubscribeTo()` decorators and wires them to their Kafka clients on startup. */\n@Injectable()\nexport class KafkaExplorer implements OnModuleInit {\n private readonly logger = new Logger(KafkaExplorer.name);\n\n constructor(\n @Inject(DiscoveryService)\n private readonly discoveryService: DiscoveryService,\n @Inject(ModuleRef)\n private readonly moduleRef: ModuleRef,\n ) {}\n\n /**\n * Scan all NestJS providers for `@SubscribeTo()` metadata and wire each decorated\n * method to its Kafka client via `startConsumer` or `startBatchConsumer`.\n *\n * Called automatically by the NestJS lifecycle — do not invoke manually.\n */\n async onModuleInit() {\n const providers = this.discoveryService.getProviders();\n\n for (const wrapper of providers) {\n const { instance } = wrapper;\n if (!instance || typeof instance !== \"object\") continue;\n\n const metadata: SubscriberEntry[] | undefined = Reflect.getMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n instance.constructor,\n );\n\n if (!metadata || metadata.length === 0) continue;\n\n for (const entry of metadata) {\n const token = getKafkaClientToken(entry.clientName);\n let client: KafkaClient<any>;\n\n try {\n client = this.moduleRef.get(token, { strict: false });\n } catch {\n this.logger.error(\n `KafkaClient \"${entry.clientName || \"default\"}\" not found for @SubscribeTo on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n continue;\n }\n\n const handler = (instance as any)[entry.methodName].bind(instance);\n\n const consumerOptions = { ...entry.options };\n if (entry.schemas) {\n consumerOptions.schemas = entry.schemas;\n }\n\n if (entry.batch) {\n await client.startBatchConsumer(\n entry.topics as any,\n async (envelopes: any[], meta: any) => {\n await handler(envelopes, meta);\n },\n consumerOptions,\n );\n } else {\n await client.startConsumer(\n entry.topics as any,\n async (envelope: any) => {\n await handler(envelope);\n },\n consumerOptions,\n );\n }\n\n this.logger.log(\n `Registered @SubscribeTo(${entry.topics.join(\", \")})${entry.batch ? \" [batch]\" : \"\"} on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n }\n }\n }\n}\n","import { Inject } from \"@nestjs/common\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { ConsumerOptions } from \"../client/kafka.client\";\nimport { TopicDescriptor, SchemaLike } from \"../client/message/topic\";\n\n/** Reflect metadata key used to store `@SubscribeTo` entries on a class constructor. */\nexport const KAFKA_SUBSCRIBER_METADATA = \"KAFKA_SUBSCRIBER_METADATA\";\n\n/** Internal shape stored per `@SubscribeTo()` decoration on a class. */\nexport interface KafkaSubscriberMetadata {\n /** Resolved topic name strings (descriptors are unwrapped to their `__topic` string). */\n topics: string[];\n /** Per-topic schema validators extracted from `TopicDescriptor` objects (if any). */\n schemas?: Map<string, SchemaLike>;\n /** Additional consumer options forwarded to `startConsumer` / `startBatchConsumer`. */\n options?: ConsumerOptions;\n /** Named client identifier — resolves to `KAFKA_CLIENT_<clientName>` in the DI container. */\n clientName?: string;\n /** When `true`, routes to `startBatchConsumer` instead of `startConsumer`. */\n batch?: boolean;\n /** Name of the decorated method on the provider class. */\n methodName?: string | symbol;\n}\n\n/** Inject a `KafkaClient` instance. Pass a name to target a specific named client. */\nexport const InjectKafkaClient = (name?: string): ParameterDecorator =>\n Inject(getKafkaClientToken(name));\n\n/**\n * Method decorator that auto-subscribes the decorated method to one or more Kafka topics\n * when the NestJS module initialises.\n *\n * The decorated method receives a fully-decoded `EventEnvelope` for each message\n * (or an array of envelopes + `BatchMeta` when `batch: true`).\n *\n * @param topics One or more topic names or `TopicDescriptor` objects. Schemas embedded in\n * descriptors are automatically extracted and forwarded to the consumer.\n * @param options Consumer and routing options:\n * - All `ConsumerOptions` fields (`groupId`, `retry`, `dlq`, `fromBeginning`, …)\n * - `clientName` — target a named `KafkaClient` (resolves `KAFKA_CLIENT_<name>` from the DI container)\n * - `batch` — use `startBatchConsumer` instead of `startConsumer`\n *\n * @example\n * ```ts\n * @SubscribeTo('orders.created', { groupId: 'orders-svc', retry: { maxRetries: 3 } })\n * async handleOrder(envelope: EventEnvelope<Order>) { ... }\n *\n * @SubscribeTo(OrdersTopic, { batch: true })\n * async handleBatch(envelopes: EventEnvelope<Order>[], meta: BatchMeta) { ... }\n * ```\n */\nexport const SubscribeTo = (\n topics:\n | string\n | string[]\n | TopicDescriptor\n | TopicDescriptor[]\n | (string | TopicDescriptor)[],\n options?: ConsumerOptions & { clientName?: string; batch?: boolean },\n): MethodDecorator => {\n const arr = Array.isArray(topics) ? topics : [topics];\n const topicsArray = arr.map((t) => (typeof t === \"string\" ? t : t.__topic));\n\n // Extract schemas from descriptors that have them\n const schemas = new Map<string, SchemaLike>();\n for (const t of arr) {\n if (typeof t !== \"string\" && t.__schema) {\n schemas.set(t.__topic, t.__schema);\n }\n }\n\n const { clientName, batch, ...consumerOptions } = options || {};\n\n return (target, propertyKey, _descriptor) => {\n const existing: KafkaSubscriberMetadata[] =\n Reflect.getMetadata(KAFKA_SUBSCRIBER_METADATA, target.constructor) || [];\n\n Reflect.defineMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n [\n ...existing,\n {\n topics: topicsArray,\n schemas: schemas.size > 0 ? schemas : undefined,\n options: Object.keys(consumerOptions).length\n ? consumerOptions\n : undefined,\n clientName,\n batch,\n methodName: propertyKey,\n },\n ],\n target.constructor,\n );\n };\n};\n","import { Injectable } from \"@nestjs/common\";\nimport type {\n IKafkaClient,\n KafkaHealthResult,\n TopicMapConstraint,\n} from \"../client/types\";\nexport type { KafkaHealthResult } from \"../client/types\";\n\n/** Health check service. Call `check(client)` to verify broker connectivity. */\n@Injectable()\nexport class KafkaHealthIndicator {\n async check<T extends TopicMapConstraint<T>>(\n client: IKafkaClient<T>,\n ): Promise<KafkaHealthResult> {\n return client.checkStatus();\n }\n}\n"],"mappings":"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","names":["topic","topic","topic","topic","topic","topic","topic","topic","topic","topic","topic","ensureTopic","getOrCreateConsumer","createRetryTxProducer","topic","envelopes","topic","topic","topic","topic","topic","import_common","import_core","import_common","import_common"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/client/transport/confluent.transport.ts","../src/client/kafka.client/infra/dedup.store.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/kafka.client/admin/ops.ts","../src/client/kafka.client/consumer/pipeline.ts","../src/client/kafka.client/consumer/subscribe-retry.ts","../src/client/kafka.client/consumer/features/dlq-replay.ts","../src/client/kafka.client/infra/metrics.manager.ts","../src/client/kafka.client/infra/inflight.tracker.ts","../src/client/kafka.client/infra/circuit-breaker.manager.ts","../src/client/kafka.client/consumer/queue.ts","../src/client/kafka.client/validate-options.ts","../src/client/security/resolve-security.ts","../src/client/kafka.client/producer/lifecycle.ts","../src/client/kafka.client/producer/send.ts","../src/client/kafka.client/consumer/retry-topic.ts","../src/client/kafka.client/consumer/setup.ts","../src/client/kafka.client/consumer/handler.ts","../src/client/kafka.client/consumer/stop.ts","../src/client/kafka.client/consumer/start.ts","../src/client/kafka.client/consumer/features/window.ts","../src/client/kafka.client/consumer/features/routed.ts","../src/client/kafka.client/consumer/features/delayed.ts","../src/client/kafka.client/consumer/features/snapshot.ts","../src/client/kafka.client/index.ts","../src/client/message/topic.ts","../src/client/message/versioned-schema.ts","../src/client/message/schema-registry.ts","../src/client/outbox/outbox.store.ts","../src/client/outbox/outbox.relay.ts","../src/client/security/providers.ts","../src/client/security/acl.ts","../src/client/config/from-env.ts","../src/nest/kafka.module.ts","../src/nest/kafka.constants.ts","../src/nest/kafka.explorer.ts","../src/nest/kafka.decorator.ts","../src/nest/kafka.health.ts"],"sourcesContent":["export * from \"./core\";\nexport * from \"./nest/kafka.module\";\nexport * from \"./nest/kafka.constants\";\nexport * from \"./nest/kafka.decorator\";\nexport * from \"./nest/kafka.explorer\";\nexport * from \"./nest/kafka.health\";\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\nconst { Kafka: KafkaClass, logLevel: KafkaLogLevel, PartitionAssigners } = KafkaJS;\n\nimport type {\n KafkaTransport,\n IProducer,\n IConsumer,\n IAdmin,\n ITransaction,\n IProducerRecord,\n IProducerCreationOptions,\n IConsumerCreationOptions,\n ITopicPartition,\n ITopicPartitions,\n ITopicPartitionOffset,\n IConsumerRunConfig,\n IPartitionWatermarks,\n IPartitionOffset,\n IGroupTopicOffsets,\n IGroupDescription,\n ITopicMetadata,\n} from \"./transport.interface\";\n\n// ── ConfluentTransaction ──────────────────────────────────────────────────────\n\nclass ConfluentTransaction implements ITransaction {\n constructor(private readonly tx: KafkaJS.Transaction) {}\n\n async send(record: IProducerRecord): Promise<void> {\n await this.tx.send(record as any);\n }\n\n async sendOffsets(options: {\n consumer: IConsumer;\n topics: Array<{\n topic: string;\n partitions: Array<{ partition: number; offset: string }>;\n }>;\n }): Promise<void> {\n // Unwrap the ConfluentConsumer to get the native KafkaJS.Consumer for sendOffsets\n const nativeConsumer = (options.consumer as ConfluentConsumer).getNative();\n await this.tx.sendOffsets({\n consumer: nativeConsumer,\n topics: options.topics,\n } as any);\n }\n\n async commit(): Promise<void> {\n await this.tx.commit();\n }\n\n async abort(): Promise<void> {\n await this.tx.abort();\n }\n}\n\n// ── ConfluentProducer ─────────────────────────────────────────────────────────\n\nclass ConfluentProducer implements IProducer {\n private connectPromise?: Promise<void>;\n\n constructor(private readonly producer: KafkaJS.Producer) {}\n\n async connect(): Promise<void> {\n // librdkafka's compat layer throws \"Connect has already been called\n // elsewhere\" on a second connect() — unlike KafkaJS, it is not idempotent.\n // Share one in-flight/settled promise so connectProducer() plus the lazy\n // consumer-pipeline connect (DLQ/retry/duplicates routing) can coexist.\n this.connectPromise ??= this.producer.connect().catch((err) => {\n this.connectPromise = undefined;\n throw err;\n });\n return this.connectPromise;\n }\n\n async disconnect(): Promise<void> {\n this.connectPromise = undefined;\n await this.producer.disconnect();\n }\n\n async send(record: IProducerRecord): Promise<void> {\n await this.producer.send(record as any);\n }\n\n async transaction(): Promise<ITransaction> {\n const tx = await this.producer.transaction();\n return new ConfluentTransaction(tx);\n }\n}\n\n// ── ConfluentConsumer ─────────────────────────────────────────────────────────\n\nexport class ConfluentConsumer implements IConsumer {\n constructor(private readonly consumer: KafkaJS.Consumer) {}\n\n /** Returns the underlying KafkaJS.Consumer — used by ConfluentTransaction.sendOffsets. */\n getNative(): KafkaJS.Consumer {\n return this.consumer;\n }\n\n async connect(): Promise<void> {\n await this.consumer.connect();\n }\n\n async disconnect(): Promise<void> {\n await this.consumer.disconnect();\n }\n\n async subscribe(options: { topics: (string | RegExp)[] }): Promise<void> {\n await this.consumer.subscribe(options as any);\n }\n\n async run(config: IConsumerRunConfig): Promise<void> {\n await this.consumer.run(config as any);\n }\n\n pause(assignments: ITopicPartitions[]): void {\n this.consumer.pause(assignments as any);\n }\n\n resume(assignments: ITopicPartitions[]): void {\n this.consumer.resume(assignments as any);\n }\n\n seek(options: ITopicPartitionOffset): void {\n this.consumer.seek(options as any);\n }\n\n assignment(): ITopicPartition[] {\n return (this.consumer as any).assignment() as ITopicPartition[];\n }\n\n async commitOffsets(offsets: ITopicPartitionOffset[]): Promise<void> {\n await this.consumer.commitOffsets(offsets as any);\n }\n\n async stop(): Promise<void> {\n await (this.consumer as any).stop?.();\n }\n}\n\n// ── ConfluentAdmin ────────────────────────────────────────────────────────────\n\nclass ConfluentAdmin implements IAdmin {\n constructor(private readonly admin: KafkaJS.Admin) {}\n\n async connect(): Promise<void> {\n await this.admin.connect();\n }\n\n async disconnect(): Promise<void> {\n await this.admin.disconnect();\n }\n\n async createTopics(options: {\n topics: Array<{ topic: string; numPartitions: number }>;\n }): Promise<void> {\n await this.admin.createTopics(options as any);\n }\n\n async fetchTopicOffsets(topic: string): Promise<IPartitionWatermarks[]> {\n return this.admin.fetchTopicOffsets(topic) as any;\n }\n\n async fetchTopicOffsetsByTimestamp(\n topic: string,\n timestamp: number,\n ): Promise<IPartitionOffset[]> {\n return (this.admin as any).fetchTopicOffsetsByTimestamp(topic, timestamp);\n }\n\n async fetchOffsets(options: {\n groupId: string;\n }): Promise<IGroupTopicOffsets[]> {\n return this.admin.fetchOffsets(options as any) as any;\n }\n\n async setOffsets(options: {\n groupId: string;\n topic: string;\n partitions: IPartitionOffset[];\n }): Promise<void> {\n await (this.admin as any).setOffsets(options);\n }\n\n async listTopics(): Promise<string[]> {\n return this.admin.listTopics();\n }\n\n async listGroups(): Promise<{ groups: IGroupDescription[] }> {\n return this.admin.listGroups() as any;\n }\n\n async fetchTopicMetadata(options?: {\n topics?: string[];\n }): Promise<{ topics: ITopicMetadata[] }> {\n return (this.admin as any).fetchTopicMetadata(options);\n }\n\n async deleteGroups(groupIds: string[]): Promise<void> {\n await (this.admin as any).deleteGroups(groupIds);\n }\n\n async deleteTopicRecords(options: {\n topic: string;\n partitions: IPartitionOffset[];\n }): Promise<void> {\n await this.admin.deleteTopicRecords(options as any);\n }\n}\n\n// ── ConfluentTransport ────────────────────────────────────────────────────────\n\n/**\n * `KafkaTransport` implementation backed by `@confluentinc/kafka-javascript`.\n * Wraps the KafkaJS-compatibility layer from librdkafka.\n */\nexport class ConfluentTransport implements KafkaTransport {\n private readonly kafka: KafkaJS.Kafka;\n\n constructor(\n clientId: string,\n brokers: string[],\n security?: import(\"../security/security.types\").KafkaSecurityOptions,\n ) {\n const kafkaJS: any = { clientId, brokers, logLevel: KafkaLogLevel.ERROR };\n if (security?.ssl !== undefined) kafkaJS.ssl = security.ssl;\n if (security?.sasl) {\n if (security.sasl.mechanism === \"oauthbearer\") {\n const provider = security.sasl.oauthBearerProvider;\n kafkaJS.sasl = {\n mechanism: \"oauthbearer\",\n oauthBearerProvider: async () => {\n const token = await provider();\n return {\n value: token.value,\n principal: token.principal ?? \"kafka-client\",\n lifetime: token.lifetimeMs ?? Date.now() + 15 * 60_000,\n ...(token.extensions && { extensions: token.extensions }),\n };\n },\n };\n } else {\n kafkaJS.sasl = security.sasl;\n }\n }\n this.kafka = new KafkaClass({ kafkaJS });\n }\n\n producer(options?: IProducerCreationOptions): IProducer {\n const native = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n ...(options?.idempotent !== undefined && {\n idempotent: options.idempotent,\n }),\n ...(options?.transactionalId !== undefined && {\n transactionalId: options.transactionalId,\n maxInFlightRequests: 1,\n }),\n },\n });\n return new ConfluentProducer(native);\n }\n\n consumer(options: IConsumerCreationOptions): IConsumer {\n const assigner =\n options.partitionAssigner === \"roundrobin\"\n ? PartitionAssigners.roundRobin\n : options.partitionAssigner === \"range\"\n ? PartitionAssigners.range\n : PartitionAssigners.cooperativeSticky;\n\n const config: any = {\n kafkaJS: {\n groupId: options.groupId,\n fromBeginning: options.fromBeginning,\n autoCommit: options.autoCommit,\n partitionAssigners: [assigner],\n },\n };\n\n // Static group membership — native rdkafka config key (the kafkaJS-compat\n // constructor config extends the native GlobalConfig, so top-level keys pass through).\n if (options.groupInstanceId) {\n config[\"group.instance.id\"] = options.groupInstanceId;\n }\n\n if (options.onRebalance) {\n const cb = options.onRebalance;\n // err.code -175 = ERR__ASSIGN_PARTITIONS, -174 = ERR__REVOKE_PARTITIONS\n config.rebalance_cb = (err: any, assignment: any[]) => {\n const type = err.code === -175 ? \"assign\" : \"revoke\";\n cb(\n type,\n assignment.map((p) => ({ topic: p.topic, partition: p.partition })),\n );\n };\n }\n\n return new ConfluentConsumer(this.kafka.consumer(config));\n }\n\n admin(): IAdmin {\n return new ConfluentAdmin(this.kafka.admin());\n }\n}\n","import type { DedupStore } from \"../../types\";\n\n/**\n * In-memory {@link DedupStore} backed by a nested map:\n * `groupId` → `\"topic:partition\"` → last processed Lamport clock.\n *\n * This is the default store used when `DeduplicationOptions.store` is not set.\n * State lives only in the current process and is cleared on `stopConsumer` /\n * `disconnect` and lost on restart or rebalance. Provide a persistent\n * `DedupStore` (e.g. Redis-backed) to survive restarts and rebalances.\n *\n * The backing map is injected so the client can share a single map across all\n * consumer groups and clear it during shutdown.\n *\n * @example\n * ```ts\n * const states = new Map<string, Map<string, number>>();\n * const store = new InMemoryDedupStore(states);\n * store.setLastClock('billing', 'orders:0', 42);\n * store.getLastClock('billing', 'orders:0'); // 42\n * ```\n */\nexport class InMemoryDedupStore implements DedupStore {\n constructor(\n private readonly states: Map<string, Map<string, number>>,\n ) {}\n\n getLastClock(groupId: string, topicPartition: string): number | undefined {\n return this.states.get(groupId)?.get(topicPartition);\n }\n\n setLastClock(groupId: string, topicPartition: string, clock: number): void {\n let group = this.states.get(groupId);\n if (!group) {\n group = new Map();\n this.states.set(groupId, group);\n }\n group.set(topicPartition, clock);\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/** Absolute epoch-ms deadline before which a delayed message must not be delivered. */\nexport const HEADER_DELAYED_UNTIL = \"x-delayed-until\";\n/** Target topic a delayed message is forwarded to once its deadline passes. */\nexport const HEADER_DELAYED_TARGET = \"x-delayed-target\";\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 *\n * @example\n * ```ts\n * await kafka.startConsumer(['orders'], async (envelope: EventEnvelope<Order>) => {\n * console.log(envelope.payload.orderId); // typed payload\n * console.log(envelope.correlationId); // auto-propagated\n * console.log(envelope.eventId); // unique message ID\n * });\n * ```\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/**\n * Read the current envelope context (correlationId / traceparent) from ALS.\n * Returns `undefined` outside of a Kafka consumer handler.\n * @example\n * ```ts\n * const ctx = getEnvelopeContext();\n * if (ctx) console.log('correlationId:', ctx.correlationId);\n * ```\n */\nexport function getEnvelopeContext(): EnvelopeCtx | undefined {\n return envelopeStorage.getStore();\n}\n\n/**\n * Execute `fn` inside an envelope context so nested sends inherit correlationId.\n * Automatically called by the consumer pipeline — use this in tests or manual flows.\n * @example\n * ```ts\n * await runWithEnvelopeContext({ correlationId: 'abc-123' }, async () => {\n * await kafka.sendMessage('orders.created', payload); // inherits correlationId\n * });\n * ```\n */\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","/**\n * Coerce an unknown thrown value to an `Error` instance.\n * Returns the value as-is if it is already an `Error`; otherwise wraps it with `String(error)`.\n * @param error The value caught in a `catch` clause.\n * @returns A guaranteed `Error` instance.\n */\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\n/**\n * Error thrown when a consumer message handler fails.\n * @example\n * ```ts\n * await kafka.startConsumer(['orders'], async (envelope) => {\n * try { await process(envelope); }\n * catch (err) {\n * if (err instanceof KafkaProcessingError) {\n * console.error(err.topic, err.originalMessage);\n * }\n * }\n * });\n * ```\n */\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/**\n * Error thrown when schema validation fails on send or consume.\n * @example\n * ```ts\n * try { await kafka.sendMessage('orders.created', invalidPayload); }\n * catch (err) {\n * if (err instanceof KafkaValidationError) {\n * console.error('Validation failed for topic:', err.topic);\n * }\n * }\n * ```\n */\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/**\n * Error thrown when all retry attempts are exhausted for a message.\n * @example\n * ```ts\n * const kafka = new KafkaClient(config, groupId, { onMessageLost: (ctx) => {\n * if (ctx.error instanceof KafkaRetryExhaustedError) {\n * console.error(`Exhausted after ${ctx.error.attempts} attempts on ${ctx.error.topic}`);\n * }\n * }});\n * ```\n */\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 // Explicit key wins; otherwise fall back to the descriptor's .key()\n // extractor (runs on the original, pre-validation payload).\n key: m.key ?? topicOrDesc?.__key?.(m.value) ?? null,\n headers: envelopeHeaders,\n };\n }),\n );\n return { topic, messages: builtMessages, ...(compression && { compression }) };\n}\n","import type { IConsumer, KafkaTransport } from \"../../transport/transport.interface\";\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, IConsumer>;\n consumerCreationOptions: Map<\n string,\n { fromBeginning: boolean; autoCommit: boolean }\n >;\n transport: KafkaTransport;\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, transport, 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 onFirstAssignment?: () => void,\n groupInstanceId?: string,\n): IConsumer {\n const { consumers, consumerCreationOptions, transport, 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 // Consumer already exists — treat as immediately ready (assignment already happened)\n onFirstAssignment?.();\n return consumers.get(groupId)!;\n }\n\n consumerCreationOptions.set(groupId, { fromBeginning, autoCommit });\n\n // Debounced assignment resolver for handle.ready().\n //\n // Rather than resolving on the *first* assign event, we debounce: the promise\n // resolves SETTLE_MS after the *last* assign event with no subsequent assign.\n // This correctly handles multi-consumer groups where the initial solo assignment\n // is immediately followed by a rebalance when peers join — we wait until the\n // group is stable.\n //\n // When fromBeginning is false (auto.offset.reset = latest), librdkafka fires\n // ERR__ASSIGN_PARTITIONS BEFORE completing the async broker round-trip that\n // establishes the initial fetch position. The settle window also covers this\n // offset fetch so messages sent right after ready() resolves are not missed.\n //\n // fromBeginning: true doesn't need a settle window — seeks to offset 0\n // happen synchronously and the consumer catches up from the beginning anyway.\n // Debounced readiness: resolves SETTLE_MS after the last rebalance event\n // (assign OR revoke) with no subsequent event. We reset on revoke too because\n // in cooperative-sticky rebalancing a REVOKE fires mid-protocol (e.g. ConsumerA\n // loses one partition when a peer joins) and the group isn't stable until the\n // revoke has settled and peers have received their assigns. We guard against\n // firing before any \"assign\" has been seen so the promise never resolves on a\n // pure-revoke cycle.\n //\n // fromBeginning: false needs the settle window so librdkafka can complete the\n // async broker round-trip that fetches the initial \"latest\" offset after each\n // assign. fromBeginning: true doesn't need it — seeks to offset 0 happen\n // synchronously and the consumer will always catch up from the beginning.\n const SETTLE_MS = fromBeginning ? 0 : 500;\n let hasAssignment = false;\n let settleTimer: ReturnType<typeof setTimeout> | undefined;\n const scheduleSettle = () => {\n if (!hasAssignment) return;\n if (settleTimer) clearTimeout(settleTimer);\n settleTimer = setTimeout(() => {\n settleTimer = undefined;\n onFirstAssignment?.();\n }, SETTLE_MS);\n };\n // Thin wrapper used at the call-site below — keeps the old name readable.\n const fireOnAssignment = () => {\n hasAssignment = true;\n scheduleSettle();\n };\n\n const consumer = transport.consumer({\n groupId,\n fromBeginning,\n autoCommit,\n partitionAssigner: partitionAssigner ?? \"cooperative-sticky\",\n groupInstanceId,\n onRebalance: (type, assignments) => {\n if (type === \"assign\") fireOnAssignment();\n else if (type === \"revoke\") scheduleSettle(); // reset timer mid-rebalance\n if (onRebalance) {\n try {\n onRebalance(type, assignments);\n } catch (e) {\n logger.warn(`onRebalance callback threw: ${(e as Error).message}`);\n }\n }\n },\n });\n\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 { IAdmin } from \"../../transport/transport.interface\";\ntype Admin = IAdmin;\nimport type {\n ClientId,\n ConsumerGroupSummary,\n KafkaHealthResult,\n KafkaLogger,\n TopicDescription,\n} from \"../../types\";\n\nexport type AdminOpsDeps = {\n admin: Admin;\n logger: KafkaLogger;\n runningConsumers: Map<string, \"eachMessage\" | \"eachBatch\">;\n defaultGroupId: string;\n clientId: ClientId;\n};\n\nexport class AdminOps {\n private isConnected = false;\n\n constructor(private readonly deps: AdminOpsDeps) {}\n\n /** Underlying admin client — used by index.ts for topic validation. */\n get admin(): Admin {\n return this.deps.admin;\n }\n\n /** Whether the admin client is currently connected. */\n get connected(): boolean {\n return this.isConnected;\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 async ensureConnected(): Promise<void> {\n if (this.isConnected) return;\n try {\n await this.deps.admin.connect();\n this.isConnected = true;\n } catch (err) {\n this.isConnected = false;\n throw err;\n }\n }\n\n /** Disconnect admin if connected. Resets the connected flag. */\n async disconnect(): Promise<void> {\n if (!this.isConnected) return;\n await this.deps.admin.disconnect();\n this.isConnected = false;\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.deps.defaultGroupId;\n if (this.deps.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.ensureConnected();\n const partitionOffsets = await this.deps.admin.fetchTopicOffsets(topic);\n const partitions = partitionOffsets.map(({ partition, low, high }) => ({\n partition,\n offset: position === \"earliest\" ? low : high,\n }));\n await this.deps.admin.setOffsets({ groupId: gid, topic, partitions });\n this.deps.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.deps.defaultGroupId;\n if (this.deps.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.ensureConnected();\n const byTopic = new Map<string, Array<{ partition: number; offset: string }>>();\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.deps.admin.setOffsets({ groupId: gid, topic, partitions });\n this.deps.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.deps.defaultGroupId;\n if (this.deps.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.ensureConnected();\n const byTopic = new Map<string, Array<{ partition: number; timestamp: number }>>();\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.deps.admin.fetchTopicOffsetsByTimestamp(\n topic,\n timestamp,\n );\n const found = results.find(\n (r) => r.partition === partition,\n );\n if (found) return { partition, offset: found.offset };\n // No offset at the requested timestamp (empty partition or future\n // timestamp) — committing a literal \"-1\" is not portable and can\n // trigger an auto.offset.reset replay. Commit the actual high\n // watermark instead so the group resumes at \"new messages only\".\n const topicOffsets = await this.deps.admin.fetchTopicOffsets(topic);\n const po = topicOffsets.find((o) => o.partition === partition);\n return { partition, offset: po?.high ?? \"0\" };\n }),\n );\n await this.deps.admin.setOffsets({ groupId: gid, topic, partitions: offsets });\n this.deps.logger.log(\n `Offsets set by timestamp for group \"${gid}\" on \"${topic}\": ${JSON.stringify(offsets)}`,\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.deps.defaultGroupId;\n await this.ensureConnected();\n\n const committedByTopic = await this.deps.admin.fetchOffsets({ groupId: gid });\n\n const brokerOffsetsAll = await Promise.all(\n committedByTopic.map(({ topic }) => this.deps.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<KafkaHealthResult> {\n try {\n await this.ensureConnected();\n const topics = await this.deps.admin.listTopics();\n return { status: \"up\", clientId: this.deps.clientId, topics };\n } catch (error) {\n return {\n status: \"down\",\n clientId: this.deps.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.ensureConnected();\n const result = await this.deps.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.ensureConnected();\n const result = await this.deps.admin.fetchTopicMetadata(\n topics ? { topics } : undefined,\n );\n return result.topics.map((t) => ({\n name: t.name,\n partitions: t.partitions.map((p) => ({\n partition: p.partitionId ?? p.partition ?? 0,\n // -1 is Kafka's own \"no leader\" sentinel; 0 is a valid broker id\n leader: p.leader ?? -1,\n replicas: (p.replicas ?? []).map((r) =>\n typeof r === \"number\" ? r : r.nodeId,\n ),\n isr: (p.isr ?? []).map((r) =>\n typeof r === \"number\" ? r : r.nodeId,\n ),\n })),\n }));\n }\n\n /**\n * Delete consumer groups from the broker.\n * Groups must be empty (no active members) before deletion.\n * Silently skips unknown group IDs.\n * @param groupIds Consumer group IDs to delete.\n */\n public async deleteGroups(groupIds: string[]): Promise<void> {\n if (groupIds.length === 0) return;\n await this.ensureConnected();\n await this.deps.admin.deleteGroups(groupIds);\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.ensureConnected();\n await this.deps.admin.deleteTopicRecords({ topic, partitions });\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 public async validateRetryTopicsExist(\n topicNames: string[],\n maxRetries: number,\n ): Promise<void> {\n await this.ensureConnected();\n const existing = new Set(await this.deps.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 public async validateDlqTopicsExist(topicNames: string[]): Promise<void> {\n await this.ensureConnected();\n const existing = new Set(await this.deps.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 public async validateDuplicatesTopicsExist(\n topicNames: string[],\n customDestination: string | undefined,\n ): Promise<void> {\n await this.ensureConnected();\n const existing = new Set(await this.deps.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","import type { IProducer } from \"../../transport/transport.interface\";\ntype Producer = IProducer;\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\n// Re-exported so the many existing `import { toError } from \"./pipeline\"` sites\n// keep working; the implementation lives in errors.ts (shared, domain-neutral).\nimport { toError } from \"../../errors\";\nexport { toError };\n\n/**\n * Return a Promise that resolves after `ms` milliseconds.\n * Used for exponential backoff between retry attempts.\n * @param ms Delay in milliseconds.\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\n/**\n * Produce a message to `<topic>.dlq`, stamping standard DLQ headers.\n * Falls back to `onMessageLost` when the DLQ produce itself fails.\n * @param topic Original topic the message was consumed from.\n * @param rawMessage Raw JSON string of the original message.\n * @param deps Logger, producer, and optional `onMessageLost` callback.\n * @param meta Error, attempt count, and original headers to attach as DLQ metadata headers.\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\n/**\n * Input context passed to `executeWithRetry`.\n * Bundles the envelope(s), raw message bytes, interceptors, and retry/DLQ policy\n * so the retry loop has everything it needs without relying on closure state.\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 /** Fired once per failed handler attempt — drives the circuit breaker. */\n onFailure?: (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 // Record the failure at the handler-error boundary so the circuit breaker\n // sees every failed attempt — not only messages that end up in the DLQ.\n deps.onFailure?.(envelopes[0]!);\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 type { IConsumer } from \"../../transport/transport.interface\";\nimport type { KafkaLogger, SubscribeRetryOptions } from \"../../types\";\nimport { toError, sleep } from \"./pipeline\";\n\nexport async function subscribeWithRetry(\n consumer: IConsumer,\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 type { IConsumer } from \"../../../transport/transport.interface\";\ntype Consumer = IConsumer;\nimport type { KafkaLogger, DlqReplayOptions } from \"../../../types\";\nimport { subscribeWithRetry } from \"../subscribe-retry\";\nimport { decodeHeaders } from \"../../../message/envelope\";\n\n/**\n * Dependencies injected into `replayDlqTopic` by `KafkaClient`.\n * Abstracts broker access (offset fetching, producing, consumer creation) so the\n * replay logic can be unit-tested without a real Kafka connection.\n */\nexport type DlqReplayDeps = {\n logger: KafkaLogger;\n fetchTopicOffsets: (topic: string) => Promise<Array<{ partition: number; low: string; high: string }>>;\n send: (topic: string, messages: Array<{ value: string; headers: Record<string, string> }>) => Promise<void>;\n /**\n * Create a consumer for the given group.\n * @param groupId Consumer group ID.\n * @param fromBeginning When `true`, `auto.offset.reset=earliest` (no committed offsets on a temp group).\n */\n createConsumer: (groupId: string, fromBeginning: boolean) => Consumer;\n /**\n * Disconnect the consumer and, when `deleteGroup` is `true`, delete the group\n * from the broker (used for ephemeral temp groups that should not accumulate).\n */\n cleanupConsumer: (consumer: Consumer, groupId: string, deleteGroup: boolean) => void;\n dlqHeaderKeys: Set<string>;\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 * ### Group ID strategy (driven by `options.fromBeginning`):\n * - `fromBeginning: true` (default) — a new ephemeral group `<topic>.dlq-replay-<ts>` is used\n * on every call so there are no committed offsets; reads all messages from the beginning\n * every time. The group is deleted from the broker after the replay finishes.\n * - `fromBeginning: false` — a stable group `<topic>.dlq-replay` is used; committed offsets\n * persist between calls so only messages added since the previous call are replayed.\n */\nexport async function replayDlqTopic(\n topic: string,\n deps: DlqReplayDeps,\n options: DlqReplayOptions = {},\n): Promise<{ replayed: number; skipped: number }> {\n if (topic.endsWith(\".dlq\")) {\n throw new Error(\n `replayDlq: pass the ORIGINAL topic name — \"${topic}\" already ends in \".dlq\" ` +\n `(the \".dlq\" suffix is appended internally, so this would read \"${topic}.dlq\")`,\n );\n }\n const dlqTopic = `${topic}.dlq`;\n\n const partitionOffsets = await deps.fetchTopicOffsets(dlqTopic);\n // Only process partitions that have readable messages (high > low).\n // Checking high > 0 is insufficient: a topic truncated by retention policy\n // can have high > 0 but low == high (zero readable messages), causing an\n // infinite wait when consuming up to high − 1.\n const activePartitions = partitionOffsets.filter(\n (p) => Number.parseInt(p.high, 10) > Number.parseInt(p.low, 10),\n );\n if (activePartitions.length === 0) {\n deps.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 }) => [partition, Number.parseInt(high, 10)]),\n );\n const processedOffsets = new Map<number, number>();\n let replayed = 0;\n let skipped = 0;\n\n const fromBeginning = options.fromBeginning ?? true;\n // Ephemeral group when replaying from the beginning so no committed offsets\n // interfere with the seek. The group is deleted after use.\n // Stable group when replaying only new messages; committed offsets persist.\n const groupId = fromBeginning\n ? `${dlqTopic}-replay-${Date.now()}`\n : `${dlqTopic}-replay`;\n\n await new Promise<void>((resolve, reject) => {\n const consumer = deps.createConsumer(groupId, fromBeginning);\n const cleanup = () => deps.cleanupConsumer(consumer, groupId, fromBeginning);\n\n consumer\n .connect()\n .then(() => subscribeWithRetry(consumer, [dlqTopic], deps.logger))\n .then(() =>\n consumer.run({\n eachMessage: async ({ partition, message }) => {\n if (!message.value) return;\n\n const offset = Number.parseInt(message.offset, 10);\n processedOffsets.set(partition, offset);\n\n const headers = decodeHeaders(message.headers);\n const targetTopic = options.targetTopic ?? headers[\"x-dlq-original-topic\"];\n const originalHeaders = Object.fromEntries(\n Object.entries(headers).filter(([k]) => !deps.dlqHeaderKeys.has(k)),\n );\n const value = message.value.toString();\n const shouldProcess = !options.filter || options.filter(headers, value);\n\n if (!targetTopic || !shouldProcess) {\n skipped++;\n } else if (options.dryRun) {\n deps.logger.log(`[DLQ replay dry-run] Would replay to \"${targetTopic}\"`);\n replayed++;\n } else {\n await deps.send(targetTopic, [{ value, headers: originalHeaders }]);\n replayed++;\n }\n\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 deps.logger.log(`replayDlq: replayed ${replayed}, skipped ${skipped} from \"${dlqTopic}\"`);\n return { replayed, skipped };\n}\n","import type { EventEnvelope } from \"../../message/envelope\";\nimport type { DlqReason, KafkaInstrumentation, KafkaMetrics } from \"../../types\";\n\nexport type MetricsManagerDeps = {\n instrumentation: KafkaInstrumentation[];\n onCircuitFailure: (envelope: EventEnvelope<any>, gid: string) => void;\n onCircuitSuccess: (envelope: EventEnvelope<any>, gid: string) => void;\n};\n\n/**\n * Maintains per-topic event counters and dispatches instrumentation hooks.\n * Created once per `KafkaClient` instance and shared across all consumers and producers.\n */\nexport class MetricsManager {\n private readonly topicMetrics = new Map<string, KafkaMetrics>();\n\n constructor(private readonly deps: MetricsManagerDeps) {}\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 /** Fire `afterSend` instrumentation hooks for each message in a batch. */\n notifyAfterSend(topic: string, count: number): void {\n for (let i = 0; i < count; i++)\n for (const inst of this.deps.instrumentation) inst.afterSend?.(topic);\n }\n\n /**\n * Increment the retry counter for the envelope's topic and fire all `onRetry` instrumentation hooks.\n * @param envelope The message envelope being retried.\n * @param attempt Current retry attempt number (1-based).\n * @param maxRetries Maximum number of retries configured for this consumer.\n */\n notifyRetry(envelope: EventEnvelope<any>, attempt: number, maxRetries: number): void {\n this.metricsFor(envelope.topic).retryCount++;\n for (const inst of this.deps.instrumentation) inst.onRetry?.(envelope, attempt, maxRetries);\n }\n\n /**\n * Increment the DLQ counter for the envelope's topic and fire all `onDlq` instrumentation hooks.\n * Circuit breaker failures are recorded separately via `notifyFailure` at the\n * handler-error boundary — dead-lettering itself is not a circuit event.\n * @param envelope The message envelope being sent to the DLQ.\n * @param reason The reason the message is being dead-lettered.\n */\n notifyDlq(envelope: EventEnvelope<any>, reason: DlqReason): void {\n this.metricsFor(envelope.topic).dlqCount++;\n for (const inst of this.deps.instrumentation) inst.onDlq?.(envelope, reason);\n }\n\n /**\n * Notify the circuit breaker of a handler failure. Fired on every failed\n * handler attempt (in-process retries and retry-topic levels included),\n * independent of whether the message is ultimately dead-lettered.\n * @param envelope The message envelope whose handler failed.\n * @param gid Consumer group ID — used to drive circuit breaker state.\n */\n notifyFailure(envelope: EventEnvelope<any>, gid: string): void {\n this.deps.onCircuitFailure(envelope, gid);\n }\n\n /**\n * Increment the deduplication counter for the envelope's topic and fire all `onDuplicate` hooks.\n * @param envelope The duplicate message envelope.\n * @param strategy The deduplication strategy applied (`\"drop\"`, `\"dlq\"`, or `\"topic\"`).\n */\n notifyDuplicate(envelope: EventEnvelope<any>, strategy: \"drop\" | \"dlq\" | \"topic\"): void {\n this.metricsFor(envelope.topic).dedupCount++;\n for (const inst of this.deps.instrumentation) inst.onDuplicate?.(envelope, strategy);\n }\n\n /**\n * Increment the processed counter for the envelope's topic, fire all `onMessage` hooks,\n * and notify the circuit breaker of a success (when `gid` is provided).\n * @param envelope The successfully processed message envelope.\n * @param gid Consumer group ID — used to drive circuit breaker state.\n */\n notifyMessage(envelope: EventEnvelope<any>, gid?: string): void {\n this.metricsFor(envelope.topic).processedCount++;\n for (const inst of this.deps.instrumentation) inst.onMessage?.(envelope);\n if (gid) this.deps.onCircuitSuccess(envelope, gid);\n }\n\n /**\n * Return a snapshot of event counters.\n * @param topic When provided, returns counters for that topic only; otherwise aggregates all topics.\n * @returns Read-only `KafkaMetrics` snapshot. Returns zero-valued counters if the topic has no events.\n */\n getMetrics(topic?: string): Readonly<KafkaMetrics> {\n if (topic !== undefined) {\n const m = this.topicMetrics.get(topic);\n return m ? { ...m } : { processedCount: 0, retryCount: 0, dlqCount: 0, dedupCount: 0 };\n }\n const agg: KafkaMetrics = { processedCount: 0, retryCount: 0, dlqCount: 0, dedupCount: 0 };\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 event counters to zero.\n * @param topic When provided, clears counters for that topic only; otherwise clears all topics.\n */\n resetMetrics(topic?: string): void {\n if (topic !== undefined) {\n this.topicMetrics.delete(topic);\n return;\n }\n this.topicMetrics.clear();\n }\n}\n","/** Tracks in-flight async handlers and provides a drain-wait mechanism. */\nexport class InFlightTracker {\n private inFlightTotal = 0;\n private readonly drainResolvers: Array<() => void> = [];\n\n constructor(private readonly warn: (msg: string) => void) {}\n\n /**\n * Wrap an async handler so its lifetime is counted against the in-flight total.\n * Resolvers registered with `waitForDrain` are notified when the count reaches zero.\n * @param fn The async function to track.\n * @returns The same promise returned by `fn`.\n */\n track<R>(fn: () => Promise<R>): Promise<R> {\n this.inFlightTotal++;\n const done = () => {\n this.inFlightTotal--;\n if (this.inFlightTotal === 0) this.drainResolvers.splice(0).forEach((r) => r());\n };\n try {\n return fn().finally(done);\n } catch (err) {\n // fn threw synchronously — without this the counter would leak and\n // waitForDrain would time out for the lifetime of the process.\n done();\n throw err;\n }\n }\n\n /**\n * Resolve when all tracked handlers have completed, or after `timeoutMs` elapses.\n * Logs a warning (via the injected `warn` callback) if the timeout is hit before draining.\n * Returns immediately if there are no in-flight handlers.\n * @param timeoutMs Maximum time to wait in milliseconds before resolving anyway.\n */\n 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 = () => { clearTimeout(handle); resolve(); };\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.warn(\n `Drain timed out after ${timeoutMs}ms — ${this.inFlightTotal} handler(s) still in flight`,\n );\n resolve();\n }, timeoutMs);\n });\n }\n}\n","import type { EventEnvelope } from \"../../message/envelope\";\nimport type { CircuitBreakerOptions, KafkaInstrumentation, KafkaLogger } from \"../../types\";\n\ntype CircuitState = {\n status: \"closed\" | \"open\" | \"half-open\";\n window: boolean[];\n successes: number;\n timer?: ReturnType<typeof setTimeout>;\n};\n\ntype CircuitBreakerDeps = {\n pauseConsumer: (gid: string, assignments: Array<{ topic: string; partitions: number[] }>) => void;\n resumeConsumer: (gid: string, assignments: Array<{ topic: string; partitions: number[] }>) => void;\n logger: KafkaLogger;\n instrumentation: KafkaInstrumentation[];\n};\n\n/**\n * Per-consumer-group circuit breaker.\n * State is tracked per `${gid}:${topic}:${partition}` key using a sliding window.\n * Drives CLOSED → OPEN → HALF-OPEN → CLOSED transitions and pauses/resumes\n * topic-partition consumption accordingly.\n */\nexport class CircuitBreakerManager {\n private readonly states = new Map<string, CircuitState>();\n private readonly configs = new Map<string, CircuitBreakerOptions>();\n\n constructor(private readonly deps: CircuitBreakerDeps) {}\n\n /**\n * Register or update circuit breaker configuration for a consumer group.\n * Must be called before the group starts consuming for the config to take effect.\n * @param gid Consumer group ID.\n * @param options Circuit breaker thresholds and timing configuration.\n */\n setConfig(gid: string, options: CircuitBreakerOptions): void {\n this.configs.set(gid, options);\n }\n\n /**\n * Returns a snapshot of the circuit breaker state for a given topic-partition.\n * Returns `undefined` when no state exists for the key.\n */\n getState(\n topic: string,\n partition: number,\n gid: string,\n ): { status: \"closed\" | \"open\" | \"half-open\"; failures: number; windowSize: number } | undefined {\n const state = this.states.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 * Record a failure for the given envelope and group.\n * Drives the CLOSED → OPEN and HALF-OPEN → OPEN transitions.\n */\n onFailure(envelope: EventEnvelope<any>, gid: string): void {\n const cfg = this.configs.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.states.get(stateKey);\n if (!state) {\n state = { status: \"closed\", window: [], successes: 0 };\n this.states.set(stateKey, state);\n }\n if (state.status === \"open\") return;\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.deps.instrumentation)\n inst.onCircuitOpen?.(envelope.topic, envelope.partition);\n this.deps.pauseConsumer(gid, [{ topic: envelope.topic, partitions: [envelope.partition] }]);\n state!.timer = setTimeout(() => {\n state!.status = \"half-open\";\n state!.successes = 0;\n this.deps.logger.log(\n `[CircuitBreaker] HALF-OPEN — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition}`,\n );\n for (const inst of this.deps.instrumentation)\n inst.onCircuitHalfOpen?.(envelope.topic, envelope.partition);\n this.deps.resumeConsumer(gid, [{ topic: envelope.topic, partitions: [envelope.partition] }]);\n }, recoveryMs);\n };\n\n if (state.status === \"half-open\") {\n clearTimeout(state.timer);\n this.deps.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.deps.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 * Record a success for the given envelope and group.\n * Drives the HALF-OPEN → CLOSED transition and updates the success window.\n */\n onSuccess(envelope: EventEnvelope<any>, gid: string): void {\n const cfg = this.configs.get(gid);\n if (!cfg) return;\n\n const stateKey = `${gid}:${envelope.topic}:${envelope.partition}`;\n const state = this.states.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.deps.logger.log(\n `[CircuitBreaker] CLOSED — group=\"${gid}\" topic=\"${envelope.topic}\" partition=${envelope.partition}`,\n );\n for (const inst of this.deps.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 * Remove all circuit state and config for the given group.\n * Called when a consumer is stopped via `stopConsumer(groupId)`.\n */\n removeGroup(gid: string): void {\n for (const key of [...this.states.keys()]) {\n if (key.startsWith(`${gid}:`)) {\n clearTimeout(this.states.get(key)!.timer);\n this.states.delete(key);\n }\n }\n this.configs.delete(gid);\n }\n\n /** Clear all circuit state and config. Called on `disconnect()`. */\n clear(): void {\n for (const state of this.states.values()) clearTimeout(state.timer);\n this.states.clear();\n this.configs.clear();\n }\n}\n","/** Push-to-pull async queue used by consume() to bridge Kafka's push model to AsyncIterableIterator. */\nexport class 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 /**\n * Enqueue an item. If a consumer is already awaiting the next item, delivers it immediately.\n * When the internal buffer reaches `highWaterMark`, calls `onFull` to signal backpressure.\n * @param item The value to enqueue.\n */\n push(item: V): void {\n if (this.closed) return; // late push after close()/fail() — drop, nobody will consume it\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 /**\n * Terminate the queue with an error. All pending and future `next()` calls will reject.\n * @param err The error to propagate to all waiting consumers.\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 /**\n * Signal end-of-stream. All pending `next()` calls resolve with `{ done: true }`,\n * and future `next()` calls resolve immediately with `{ done: true }`.\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 /**\n * Pull the next item from the queue, conforming to the `AsyncIterator` protocol.\n * Rejects if the queue has been failed. Resolves with `{ done: true }` if it has been closed.\n * Suspends (returns a pending Promise) when the queue is empty and not yet closed.\n * When items drain below `highWaterMark / 2`, calls `onDrained` to resume backpressure.\n * @returns A Promise resolving to an `IteratorResult<V>`.\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","import type { KafkaClientOptions } from \"../types\";\n\n/**\n * Fail-fast validation of `KafkaClient` constructor arguments.\n * Collects every problem and throws a single error listing all of them, so a\n * misconfigured client fails at construction with a clear message instead of\n * surfacing as a confusing driver error on first use.\n *\n * @throws Error listing every invalid argument/option found.\n */\nexport function validateClientOptions(\n clientId: string,\n groupId: string,\n brokers: string[],\n options?: KafkaClientOptions,\n): void {\n const problems: string[] = [];\n\n if (typeof clientId !== \"string\" || clientId.trim() === \"\") {\n problems.push(\"clientId must be a non-empty string\");\n }\n if (typeof groupId !== \"string\" || groupId.trim() === \"\") {\n problems.push(\"groupId must be a non-empty string\");\n }\n // With a custom transport (e.g. FakeTransport in tests) brokers are unused —\n // only validate emptiness when the client will actually dial them.\n if (!Array.isArray(brokers) || (brokers.length === 0 && !options?.transport)) {\n problems.push(\"brokers must be a non-empty array of broker addresses\");\n } else if (brokers.some((b) => typeof b !== \"string\" || b.trim() === \"\")) {\n problems.push(\"brokers must not contain empty entries\");\n }\n\n if (options) {\n const {\n numPartitions,\n transactionalId,\n clockRecovery,\n lagThrottle,\n } = options;\n\n if (\n numPartitions !== undefined &&\n (!Number.isInteger(numPartitions) || numPartitions < 1)\n ) {\n problems.push(\n `numPartitions must be a positive integer (got ${numPartitions})`,\n );\n }\n if (transactionalId !== undefined && transactionalId.trim() === \"\") {\n problems.push(\"transactionalId must be a non-empty string when set\");\n }\n if (clockRecovery) {\n if (!Array.isArray(clockRecovery.topics)) {\n problems.push(\"clockRecovery.topics must be an array of topic names\");\n }\n if (\n clockRecovery.timeoutMs !== undefined &&\n !(clockRecovery.timeoutMs > 0)\n ) {\n problems.push(\n `clockRecovery.timeoutMs must be > 0 (got ${clockRecovery.timeoutMs})`,\n );\n }\n }\n if (lagThrottle) {\n if (!(lagThrottle.maxLag >= 0)) {\n problems.push(`lagThrottle.maxLag must be >= 0 (got ${lagThrottle.maxLag})`);\n }\n if (\n lagThrottle.pollIntervalMs !== undefined &&\n !(lagThrottle.pollIntervalMs > 0)\n ) {\n problems.push(\n `lagThrottle.pollIntervalMs must be > 0 (got ${lagThrottle.pollIntervalMs})`,\n );\n }\n if (lagThrottle.maxWaitMs !== undefined && !(lagThrottle.maxWaitMs >= 0)) {\n problems.push(\n `lagThrottle.maxWaitMs must be >= 0 (got ${lagThrottle.maxWaitMs})`,\n );\n }\n }\n }\n\n if (problems.length > 0) {\n throw new Error(\n `KafkaClient: invalid configuration:\\n- ${problems.join(\"\\n- \")}`,\n );\n }\n}\n","import type { KafkaLogger } from \"../types\";\nimport type { KafkaSecurityOptions } from \"./security.types\";\n\n/** Hosts considered local — plaintext to these never warns. */\nconst LOCAL_HOST_PATTERNS = [\n /^localhost(:\\d+)?$/i,\n /^127\\.\\d+\\.\\d+\\.\\d+(:\\d+)?$/,\n /^\\[?::1\\]?(:\\d+)?$/,\n /^0\\.0\\.0\\.0(:\\d+)?$/,\n /^host\\.docker\\.internal(:\\d+)?$/i,\n];\n\nfunction isLocalBroker(broker: string): boolean {\n return LOCAL_HOST_PATTERNS.some((re) => re.test(broker.trim()));\n}\n\n/**\n * Apply the secure-by-default rules to a user-supplied security config:\n *\n * 1. `sasl` present + `ssl` unset → `ssl: true`. Credentials never travel\n * over plaintext by accident; opting out requires an explicit `ssl: false`\n * (which logs a warning).\n * 2. No `ssl`/`sasl` at all while any broker is non-local → one warning per\n * client, silenceable via `allowInsecure: true`.\n *\n * Never throws and never blocks a connection — the defaults protect, the\n * user stays in control.\n *\n * @returns The effective options passed to the transport (may be `undefined`\n * when no security was configured).\n */\nexport function resolveSecurityOptions(\n security: KafkaSecurityOptions | undefined,\n brokers: string[],\n logger: KafkaLogger,\n): KafkaSecurityOptions | undefined {\n const hasRemoteBroker = brokers.some((b) => !isLocalBroker(b));\n\n if (!security?.sasl && security?.ssl !== true) {\n if (hasRemoteBroker && !security?.allowInsecure) {\n logger.warn(\n \"Connecting to non-local brokers without TLS or SASL — traffic and payloads travel in \" +\n \"plaintext. Configure `security: { ssl, sasl }` for production clusters, or set \" +\n \"`security: { allowInsecure: true }` to acknowledge an intentionally insecure setup.\",\n );\n }\n return security;\n }\n\n if (security.sasl && security.ssl === undefined) {\n return { ...security, ssl: true };\n }\n\n if (security.sasl && security.ssl === false) {\n logger.warn(\n \"SASL credentials are configured with `ssl: false` — credentials will be sent over \" +\n \"plaintext. This is only safe on fully trusted networks.\",\n );\n }\n\n return security;\n}\n","import type { IProducer } from \"../../transport/transport.interface\";\ntype Producer = IProducer;\n\nimport type { KafkaClientContext } from \"../context\";\nimport type { TopicMapConstraint } from \"../../types\";\nimport { HEADER_LAMPORT_CLOCK } from \"../../message/envelope\";\nimport { toError } from \"../consumer/pipeline\";\n\n/**\n * Process-level registry of active transactional producer IDs.\n * Shared across `transactionImpl` (send.ts) and `createRetryTxProducer`.\n * Cross-process conflicts cannot be detected here — they surface as fencing errors from the broker.\n */\nexport const _activeTransactionalIds = new Set<string>();\n\n// ── Topic creation ────────────────────────────────────────────────────────────\n\n/**\n * Ensure a topic exists, creating it if `autoCreateTopics` is enabled.\n * Concurrent calls for the same topic are deduplicated via a promise cache.\n */\nexport async function ensureTopic<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topic: string,\n): Promise<void> {\n if (!ctx.autoCreateTopicsEnabled || ctx.ensuredTopics.has(topic)) return;\n let p = ctx.ensureTopicPromises.get(topic);\n if (!p) {\n p = (async () => {\n await ctx.adminOps.ensureConnected();\n await ctx.adminOps.admin.createTopics({\n topics: [{ topic, numPartitions: ctx.numPartitions }],\n });\n ctx.ensuredTopics.add(topic);\n })().finally(() => ctx.ensureTopicPromises.delete(topic));\n ctx.ensureTopicPromises.set(topic, p);\n }\n await p;\n}\n\n// ── Transactional producer ────────────────────────────────────────────────────\n\n/**\n * Create and connect a transactional producer for EOS routing.\n * Each retry level consumer uses its own producer with a unique `transactionalId`\n * so Kafka can fence stale producers on restart without affecting other levels.\n */\nexport async function createRetryTxProducer<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n transactionalId: string,\n): Promise<Producer> {\n if (_activeTransactionalIds.has(transactionalId)) {\n ctx.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 = ctx.transport.producer({\n idempotent: true,\n transactionalId,\n });\n await p.connect();\n _activeTransactionalIds.add(transactionalId);\n ctx.retryTxProducers.set(transactionalId, p);\n return p;\n}\n\n// ── Lifecycle ─────────────────────────────────────────────────────────────────\n\nexport async function connectProducerImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n): Promise<void> {\n await ctx.producer.connect();\n await recoverLamportClockImpl(ctx, ctx.clockRecoveryTopics);\n if (ctx.lagThrottleOpts) startLagThrottlePoller(ctx);\n ctx.logger.log(\"Producer connected\");\n}\n\nexport async function disconnectImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n drainTimeoutMs = 30_000,\n): Promise<void> {\n if (ctx._lagThrottleTimer) {\n clearInterval(ctx._lagThrottleTimer);\n ctx._lagThrottleTimer = undefined;\n }\n await ctx.inFlight.waitForDrain(drainTimeoutMs);\n const tasks: Promise<void>[] = [ctx.producer.disconnect()];\n if (ctx.txProducer) {\n tasks.push(ctx.txProducer.disconnect());\n _activeTransactionalIds.delete(ctx.txId);\n ctx.txProducer = undefined;\n ctx.txProducerInitPromise = undefined;\n }\n for (const txId of ctx.retryTxProducers.keys())\n _activeTransactionalIds.delete(txId);\n for (const p of ctx.retryTxProducers.values()) tasks.push(p.disconnect());\n ctx.retryTxProducers.clear();\n for (const consumer of ctx.consumers.values())\n tasks.push(consumer.disconnect());\n tasks.push(ctx.adminOps.disconnect());\n await Promise.allSettled(tasks);\n ctx.consumers.clear();\n ctx.runningConsumers.clear();\n ctx.consumerCreationOptions.clear();\n ctx.companionGroupIds.clear();\n ctx.circuitBreaker.clear();\n ctx.logger.log(\"All connections closed\");\n}\n\n// ── Lag throttle ──────────────────────────────────────────────────────────────\n\nexport function startLagThrottlePoller<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n): void {\n const opts = ctx.lagThrottleOpts!;\n const { maxLag, pollIntervalMs = 5_000 } = opts;\n const groupId = opts.groupId;\n\n const poll = async () => {\n try {\n const lags = await ctx.adminOps.getConsumerLag(groupId);\n const total = lags.reduce((sum, e) => sum + e.lag, 0);\n if (total > maxLag && !ctx._lagThrottled) {\n ctx._lagThrottled = true;\n ctx.logger.warn(\n `lagThrottle: lag ${total} > ${maxLag} — producer sends will be delayed`,\n );\n } else if (total <= maxLag && ctx._lagThrottled) {\n ctx._lagThrottled = false;\n ctx.logger.log(\n `lagThrottle: lag ${total} ≤ ${maxLag} — producer sends resumed`,\n );\n }\n } catch {\n // Poll errors do not block sends — silently ignore\n }\n };\n\n ctx._lagThrottleTimer = setInterval(() => {\n void poll();\n }, pollIntervalMs);\n\n ctx._lagThrottleTimer.unref?.();\n}\n\n// ── Lamport clock recovery ────────────────────────────────────────────────────\n\n/**\n * Recover the Lamport clock from the last message across the given topics.\n * Seeks every non-empty partition to its last offset, reads one message per\n * partition, and extracts the maximum `x-lamport-clock` header value.\n * Topics that are empty or missing are silently skipped.\n */\nexport async function recoverLamportClockImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topics: string[],\n): Promise<void> {\n if (topics.length === 0) return;\n\n ctx.logger.log(\n `Clock recovery: scanning ${topics.length} topic(s) for Lamport clock...`,\n );\n await ctx.adminOps.ensureConnected();\n\n const partitionsToRead: Array<{\n topic: string;\n partition: number;\n lastOffset: string;\n }> = [];\n for (const t of topics) {\n let offsets: Array<{ partition: number; low: string; high: string }>;\n try {\n offsets = await ctx.adminOps.admin.fetchTopicOffsets(t);\n } catch {\n ctx.logger.warn(\n `Clock recovery: could not fetch offsets for \"${t}\", skipping`,\n );\n continue;\n }\n for (const { partition, high, low } of offsets) {\n if (Number.parseInt(high, 10) > Number.parseInt(low, 10)) {\n partitionsToRead.push({\n topic: t,\n partition,\n lastOffset: String(Number.parseInt(high, 10) - 1),\n });\n }\n }\n }\n\n if (partitionsToRead.length === 0) {\n ctx.logger.log(\n \"Clock recovery: all topics empty — keeping Lamport clock at 0\",\n );\n return;\n }\n\n const recoveryGroupId = `${ctx.clientId}-clock-recovery-${Date.now()}`;\n let maxClock = -1;\n\n await new Promise<void>((resolve, reject) => {\n const consumer = ctx.transport.consumer({ groupId: recoveryGroupId });\n const remaining = new Set(\n partitionsToRead.map((p) => `${p.topic}:${p.partition}`),\n );\n let settled = false;\n const cleanup = () => {\n consumer\n .disconnect()\n .catch(() => {})\n .finally(() => {\n ctx.adminOps.deleteGroups([recoveryGroupId]).catch(() => {});\n });\n };\n\n // A seeked partition may never deliver its last message (e.g. it was\n // compacted/retention-trimmed between the offset fetch and the seek).\n // Without a bound, connectProducer() would hang forever — proceed with\n // whatever maxClock was gathered instead.\n const timeoutTimer = setTimeout(() => {\n if (settled) return;\n settled = true;\n ctx.logger.warn(\n `Clock recovery: timed out after ${ctx.clockRecoveryTimeoutMs} ms with ` +\n `${remaining.size} partition(s) unread — proceeding with partial result`,\n );\n cleanup();\n resolve();\n }, ctx.clockRecoveryTimeoutMs);\n timeoutTimer.unref?.();\n\n consumer\n .connect()\n .then(async () => {\n const uniqueTopics = [\n ...new Set(partitionsToRead.map((p) => p.topic)),\n ];\n await consumer.subscribe({ topics: uniqueTopics });\n for (const { topic: t, partition, lastOffset } of partitionsToRead) {\n consumer.seek({ topic: t, partition, offset: lastOffset });\n }\n })\n .then(() =>\n consumer.run({\n eachMessage: async ({ topic: t, partition, message }) => {\n const key = `${t}:${partition}`;\n if (!remaining.has(key)) return;\n remaining.delete(key);\n\n const clockHeader = message.headers?.[HEADER_LAMPORT_CLOCK];\n if (clockHeader !== undefined) {\n const raw = Buffer.isBuffer(clockHeader)\n ? clockHeader.toString()\n : String(clockHeader);\n const clock = Number(raw);\n if (!Number.isNaN(clock) && clock > maxClock) maxClock = clock;\n }\n\n if (remaining.size === 0 && !settled) {\n settled = true;\n clearTimeout(timeoutTimer);\n cleanup();\n resolve();\n }\n },\n }),\n )\n .catch((err) => {\n if (settled) return;\n settled = true;\n clearTimeout(timeoutTimer);\n cleanup();\n reject(err);\n });\n });\n\n if (maxClock >= 0) {\n ctx._lamportClock = maxClock;\n ctx.logger.log(\n `Clock recovery: Lamport clock restored — next clock will be ${maxClock + 1}`,\n );\n } else {\n ctx.logger.log(\n \"Clock recovery: no x-lamport-clock headers found — keeping clock at 0\",\n );\n }\n}\n\n// ── Utilities ─────────────────────────────────────────────────────────────────\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 */\nexport function wrapWithTimeoutWarning<R>(\n logger: { warn: (msg: string) => void },\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 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// Re-export toError for callers that only import lifecycle\nexport { toError };\n","import type { KafkaClientContext } from \"../context\";\nimport type {\n TopicMapConstraint,\n SendOptions,\n BatchMessageItem,\n BatchSendOptions,\n TransactionContext,\n MessageHeaders,\n CompressionType,\n} from \"../../types\";\nimport { buildSendPayload, registerSchema } from \"./ops\";\nimport { ensureTopic, _activeTransactionalIds } from \"./lifecycle\";\nimport {\n HEADER_DELAYED_TARGET,\n HEADER_DELAYED_UNTIL,\n} from \"../../message/envelope\";\n\n// ── Payload builder ───────────────────────────────────────────────────────────\n\n/**\n * Build a ProduceRequest payload: register schema, validate, stamp envelope headers.\n * Shared by sendMessage, sendBatch, and transaction sends.\n */\nexport async function preparePayload<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n compression?: CompressionType,\n) {\n registerSchema(topicOrDesc, ctx.schemaRegistry, ctx.logger);\n const payload = await buildSendPayload(\n topicOrDesc,\n messages,\n ctx.producerOpsDeps,\n compression,\n );\n await ensureTopic(ctx, payload.topic);\n return payload;\n}\n\n// ── Delayed delivery ──────────────────────────────────────────────────────────\n\n/**\n * Redirect a built payload to the `<topic>.delayed` staging topic, stamping\n * the `x-delayed-until` / `x-delayed-target` control headers on every message.\n * A relay started via `startDelayedRelay()` forwards it after the deadline.\n */\nasync function redirectToDelayed<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n payload: { topic: string; messages: Array<{ headers: MessageHeaders }> },\n deliverAfterMs: number,\n): Promise<void> {\n const until = String(Date.now() + deliverAfterMs);\n for (const m of payload.messages) {\n m.headers[HEADER_DELAYED_UNTIL] = until;\n m.headers[HEADER_DELAYED_TARGET] = payload.topic;\n }\n payload.topic = `${payload.topic}.delayed`;\n await ensureTopic(ctx, payload.topic);\n}\n\n// ── Send operations ───────────────────────────────────────────────────────────\n\nexport async function sendMessageImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n): Promise<void> {\n await waitIfThrottled(ctx);\n const payload = await preparePayload(\n ctx,\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 if (options.deliverAfterMs && options.deliverAfterMs > 0) {\n await redirectToDelayed(ctx, payload, options.deliverAfterMs);\n }\n await ctx.producer.send(payload);\n ctx.metrics.notifyAfterSend(payload.topic, payload.messages.length);\n}\n\nexport async function sendBatchImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n options?: BatchSendOptions,\n): Promise<void> {\n await waitIfThrottled(ctx);\n const payload = await preparePayload(\n ctx,\n topicOrDesc,\n messages,\n options?.compression,\n );\n if (options?.deliverAfterMs && options.deliverAfterMs > 0) {\n await redirectToDelayed(ctx, payload, options.deliverAfterMs);\n }\n await ctx.producer.send(payload);\n ctx.metrics.notifyAfterSend(payload.topic, payload.messages.length);\n}\n\nexport async function sendTombstoneImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topic: string,\n key: string,\n headers?: MessageHeaders,\n): Promise<void> {\n await waitIfThrottled(ctx);\n const hdrs: MessageHeaders = { ...headers };\n for (const inst of ctx.instrumentation) inst.beforeSend?.(topic, hdrs);\n await ensureTopic(ctx, topic);\n await ctx.producer.send({\n topic,\n messages: [{ value: null, key, headers: hdrs }],\n });\n for (const inst of ctx.instrumentation) inst.afterSend?.(topic);\n}\n\nexport async function transactionImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n fn: (txCtx: TransactionContext<T>) => Promise<void>,\n): Promise<void> {\n if (!ctx.txProducerInitPromise) {\n if (_activeTransactionalIds.has(ctx.txId)) {\n ctx.logger.warn(\n `transactionalId \"${ctx.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 = (async () => {\n const p = ctx.transport.producer({\n idempotent: true,\n transactionalId: ctx.txId,\n });\n await p.connect();\n _activeTransactionalIds.add(ctx.txId);\n return p;\n })();\n ctx.txProducerInitPromise = initPromise.catch((err) => {\n ctx.txProducerInitPromise = undefined;\n throw err;\n });\n }\n ctx.txProducer = await ctx.txProducerInitPromise;\n\n // A transactional producer supports only ONE open transaction at a time —\n // serialise overlapping transaction() calls so they cannot interleave\n // begin/send/commit/abort on the shared producer.\n const prev = ctx._txChain;\n let release!: () => void;\n ctx._txChain = new Promise<void>((r) => (release = r));\n await prev;\n try {\n await runTransaction(ctx, fn);\n } finally {\n release();\n }\n}\n\nasync function runTransaction<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n fn: (txCtx: TransactionContext<T>) => Promise<void>,\n): Promise<void> {\n const tx = await ctx.txProducer!.transaction();\n try {\n const txCtx: TransactionContext<T> = {\n send: async (topicOrDesc: any, message: any, sendOpts: SendOptions = {}) => {\n const payload = await preparePayload(ctx, topicOrDesc, [\n {\n value: message,\n key: sendOpts.key,\n headers: sendOpts.headers,\n correlationId: sendOpts.correlationId,\n schemaVersion: sendOpts.schemaVersion,\n eventId: sendOpts.eventId,\n },\n ]);\n await tx.send(payload);\n ctx.metrics.notifyAfterSend(payload.topic, payload.messages.length);\n },\n sendBatch: async (\n topicOrDesc: any,\n messages: BatchMessageItem<any>[],\n batchOpts?: BatchSendOptions,\n ) => {\n const payload = await preparePayload(\n ctx,\n topicOrDesc,\n messages,\n batchOpts?.compression,\n );\n await tx.send(payload);\n ctx.metrics.notifyAfterSend(payload.topic, payload.messages.length);\n },\n };\n await fn(txCtx);\n await tx.commit();\n } catch (error) {\n try {\n await tx.abort();\n } catch (abortError) {\n ctx.logger.error(\n \"Failed to abort transaction:\",\n (abortError instanceof Error\n ? abortError\n : new Error(String(abortError))\n ).message,\n );\n }\n throw error;\n }\n}\n\n// ── Lag throttle wait (used by all send paths) ────────────────────────────────\n\nexport async function waitIfThrottled<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n): Promise<void> {\n if (!ctx._lagThrottled) return;\n const maxWait = ctx.lagThrottleOpts?.maxWaitMs ?? 30_000;\n const start = Date.now();\n while (ctx._lagThrottled) {\n if (Date.now() - start >= maxWait) {\n ctx.logger.warn(\n `lagThrottle: maxWaitMs (${maxWait} ms) exceeded — sending anyway`,\n );\n return;\n }\n await new Promise<void>((r) => setTimeout(r, 100));\n }\n}\n","import type { IConsumer, IProducer } from \"../../transport/transport.interface\";\ntype Consumer = IConsumer;\ntype Producer = IProducer;\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 \"./pipeline\";\nimport { KafkaRetryExhaustedError } from \"../../errors\";\nimport { subscribeWithRetry } from \"./subscribe-retry\";\nimport type { SchemaLike } from \"../../message/topic\";\nimport type {\n ConsumerInterceptor,\n DlqReason,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n} from \"../../types\";\n\n/**\n * Dependencies injected into retry topic consumers by `KafkaClient`.\n * Provides infrastructure (producer, logger, instrumentation), lifecycle callbacks,\n * and factories for creating consumers and transactional producers per retry level.\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 /** Fired on every failed handler attempt inside the retry chain — drives the circuit breaker. */\n onFailure?: (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 * Called immediately after each retry level consumer successfully starts and\n * receives partition assignments. Allows the caller to register the group ID\n * incrementally so partial startup failures still leave started consumers tracked.\n */\n onLevelStarted?: (groupId: string) => void;\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 // Drive the main group's circuit breaker from retry-chain failures too.\n deps.onFailure?.(envelope);\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 // Notify the caller that this level is fully started so it can be tracked\n // immediately. This ensures partial failures leave already-started consumers\n // registered and reachable by stopConsumer.\n deps.onLevelStarted?.(levelGroupId);\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 // Pre-allocate to preserve level order in the returned array.\n const levelGroupIds = new Array<string>(retry.maxRetries);\n\n // Launch all level consumers in parallel. Sequential startup was the main\n // cause of slow NestJS onModuleInit: N levels × waitForPartitionAssignment\n // (default 10 s each) = up to N × 10 s of blocking startup time.\n await Promise.all(\n Array.from({ length: retry.maxRetries }, async (_, i) => {\n const level = i + 1;\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[i] = levelGroupId;\n }),\n );\n\n return levelGroupIds;\n}\n","import type { IConsumer, IProducer } from \"../../transport/transport.interface\";\nimport type { SchemaLike } from \"../../message/topic\";\nimport type { EventEnvelope } from \"../../message/envelope\";\nimport type { KafkaClientContext } from \"../context\";\nimport type {\n TopicMapConstraint,\n ConsumerOptions,\n ConsumerInterceptor,\n RetryOptions,\n} from \"../../types\";\nimport type { MessageHandlerDeps, DeduplicationContext } from \"./handler\";\nimport { getOrCreateConsumer, buildSchemaMap } from \"./ops\";\nimport { subscribeWithRetry } from \"./subscribe-retry\";\nimport { startRetryTopicConsumers } from \"./retry-topic\";\nimport { createRetryTxProducer, ensureTopic } from \"../producer/lifecycle\";\nimport { resolveTopicName } from \"../producer/ops\";\nimport { InMemoryDedupStore } from \"../infra/dedup.store\";\n\n// ── Topic validation ──────────────────────────────────────────────────────────\n\n/** Guard checks shared by startConsumer and startBatchConsumer. */\nexport function validateTopicConsumerOpts<T extends TopicMapConstraint<T>>(\n topics: any[],\n options: ConsumerOptions<T>,\n): void {\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 if (options.retryTopics && topics.some((t) => t instanceof RegExp)) {\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\n/** Ensure all required topics exist for a consumer: base, DLQ, and dedup topics. */\nexport async function ensureConsumerTopics<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topicNames: string[],\n dlq: boolean,\n deduplication: import(\"../../types\").DeduplicationOptions | undefined,\n): Promise<void> {\n for (const t of topicNames) await ensureTopic(ctx, t);\n if (dlq) {\n for (const t of topicNames) await ensureTopic(ctx, `${t}.dlq`);\n if (!ctx.autoCreateTopicsEnabled && topicNames.length > 0) {\n await ctx.adminOps.validateDlqTopicsExist(topicNames);\n }\n }\n if (deduplication?.strategy === \"topic\") {\n const dest = deduplication.duplicatesTopic;\n if (ctx.autoCreateTopicsEnabled) {\n for (const t of topicNames)\n await ensureTopic(ctx, dest ?? `${t}.duplicates`);\n } else if (topicNames.length > 0) {\n await ctx.adminOps.validateDuplicatesTopicsExist(topicNames, dest);\n }\n }\n}\n\n// ── Consumer bootstrap ────────────────────────────────────────────────────────\n\n/**\n * Shared consumer bootstrap: groupId conflict check, schema map, connect, subscribe.\n * Returns all values needed by startConsumer / startBatchConsumer.\n */\nexport async function setupConsumer<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\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 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 || ctx.defaultGroupId;\n const existingMode = ctx.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 let resolveReady!: () => void;\n const readyPromise = new Promise<void>((resolve) => { resolveReady = resolve; });\n\n const consumer = getOrCreateConsumer(\n gid,\n fromBeginning,\n options.autoCommit ?? true,\n ctx.consumerOpsDeps,\n options.partitionAssigner,\n resolveReady,\n options.groupInstanceId,\n );\n const schemaMap = buildSchemaMap(\n stringTopics,\n ctx.schemaRegistry,\n optionSchemas,\n ctx.logger,\n );\n const topicNames = stringTopics.map((t: any) => resolveTopicName(t));\n const subscribeTopics: (string | RegExp)[] = [...topicNames, ...regexTopics];\n\n await ensureConsumerTopics(ctx, topicNames, dlq, options.deduplication);\n\n await consumer.connect();\n\n // DLQ / retry-topic / duplicates routing produce via the shared producer.\n // Connect it lazily so a consumer-only client (never called connectProducer)\n // can still deliver to those topics instead of dropping to onMessageLost.\n if (dlq || options.retryTopics || options.deduplication) {\n await ctx.producer.connect();\n }\n\n await subscribeWithRetry(\n consumer,\n subscribeTopics,\n ctx.logger,\n options.subscribeRetry,\n );\n\n const displayTopics = subscribeTopics\n .map((t) => (t instanceof RegExp ? t.toString() : t))\n .join(\", \");\n ctx.logger.log(\n `${mode === \"eachBatch\" ? \"Batch consumer\" : \"Consumer\"} subscribed to topics: ${displayTopics}`,\n );\n\n return { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry, hasRegex, readyPromise };\n}\n\n// ── Deduplication ─────────────────────────────────────────────────────────────\n\n/**\n * Create or retrieve the deduplication context for a consumer group.\n *\n * Uses the user-supplied `options.store` when present, otherwise an\n * `InMemoryDedupStore` backed by `ctx.dedupStates` — so `stopConsumer` /\n * `disconnect` clearing that map continues to reset in-memory dedup state.\n */\nexport function resolveDeduplicationContext<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n groupId: string,\n options: import(\"../../types\").DeduplicationOptions | undefined,\n): DeduplicationContext | undefined {\n if (!options) return undefined;\n const store = options.store ?? new InMemoryDedupStore(ctx.dedupStates);\n return { options, store, groupId };\n}\n\n// ── Deps builders ─────────────────────────────────────────────────────────────\n\n/** Build MessageHandlerDeps with circuit-breaker callbacks bound to the given groupId. */\nexport function messageDepsFor<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n gid: string,\n options?: Pick<import(\"../../types\").ConsumerOptions<T>, \"onMessageLost\" | \"onRetry\">,\n): MessageHandlerDeps {\n const notifyRetry = ctx.metrics.notifyRetry.bind(ctx.metrics);\n return {\n logger: ctx.logger,\n producer: ctx.producer,\n instrumentation: ctx.instrumentation,\n onMessageLost: options?.onMessageLost ?? ctx.onMessageLost,\n onTtlExpired: ctx.onTtlExpired,\n onRetry: options?.onRetry\n ? (envelope, attempt, max) => {\n notifyRetry(envelope, attempt, max);\n return options.onRetry!(envelope, attempt, max);\n }\n : notifyRetry,\n onDlq: (envelope, reason) => ctx.metrics.notifyDlq(envelope, reason),\n onDuplicate: ctx.metrics.notifyDuplicate.bind(ctx.metrics),\n onMessage: (envelope) => ctx.metrics.notifyMessage(envelope, gid),\n onFailure: (envelope) => ctx.metrics.notifyFailure(envelope, gid),\n };\n}\n\n/** Build the RetryTopicDeps object passed to retry topic consumers. */\nexport function buildRetryTopicDeps<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n) {\n return {\n logger: ctx.logger,\n producer: ctx.producer,\n instrumentation: ctx.instrumentation,\n onMessageLost: ctx.onMessageLost,\n onRetry: ctx.metrics.notifyRetry.bind(ctx.metrics),\n onDlq: ctx.metrics.notifyDlq.bind(ctx.metrics),\n onMessage: ctx.metrics.notifyMessage.bind(ctx.metrics),\n ensureTopic: (t: string) => ensureTopic(ctx, t),\n getOrCreateConsumer: (gid: string, fb: boolean, ac: boolean) =>\n getOrCreateConsumer(gid, fb, ac, ctx.consumerOpsDeps),\n runningConsumers: ctx.runningConsumers,\n createRetryTxProducer: (txId: string) => createRetryTxProducer(ctx, txId),\n };\n}\n\n// ── EOS main context ────────────────────────────────��─────────────────────────\n\n/** Create EOS transactional producer context for atomic main → retry.1 routing. */\nexport async function makeEosMainContext<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n gid: string,\n consumer: IConsumer,\n options: ConsumerOptions<T>,\n): Promise<{ txProducer: IProducer; consumer: IConsumer } | undefined> {\n if (!options.retryTopics || !options.retry) return undefined;\n const txProducer = await createRetryTxProducer(ctx, `${gid}-main-tx`);\n return { txProducer, consumer };\n}\n\n// ── Retry chain launcher ──────────────────────────────────────────────────────\n\n/** Start companion retry-level consumers and register them under the main groupId. */\nexport async function launchRetryChain<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n gid: string,\n topicNames: string[],\n handleMessage: (env: EventEnvelope<any>) => Promise<void>,\n opts: {\n retry: RetryOptions;\n dlq: boolean;\n interceptors: ConsumerInterceptor<T>[];\n schemaMap: Map<string, SchemaLike>;\n assignmentTimeoutMs?: number;\n },\n): Promise<void> {\n const { retry, dlq, interceptors, schemaMap, assignmentTimeoutMs } = opts;\n if (!ctx.autoCreateTopicsEnabled) {\n await ctx.adminOps.validateRetryTopicsExist(topicNames, retry.maxRetries);\n }\n // Pre-register an empty array so stopConsumer can find partially-started\n // companions if startRetryTopicConsumers throws mid-way.\n ctx.companionGroupIds.set(gid, []);\n await startRetryTopicConsumers(\n topicNames,\n gid,\n handleMessage,\n retry,\n dlq,\n interceptors,\n schemaMap,\n {\n ...ctx.retryTopicDeps,\n // Bind circuit breaker events to the MAIN consumer group so failures and\n // successes inside the retry chain drive the same breaker as the main\n // consumer (the retry chain has no breaker config of its own).\n onFailure: (envelope) => ctx.metrics.notifyFailure(envelope, gid),\n onMessage: (envelope) => ctx.metrics.notifyMessage(envelope, gid),\n onLevelStarted: (levelGroupId) => {\n ctx.companionGroupIds.get(gid)!.push(levelGroupId);\n },\n },\n assignmentTimeoutMs,\n );\n}\n","import type { IProducer, IConsumer } from \"../../transport/transport.interface\";\ntype Producer = IProducer;\ntype Consumer = IConsumer;\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 \"./pipeline\";\nimport type { DuplicateMetadata } from \"./pipeline\";\nimport { HEADER_LAMPORT_CLOCK } from \"../../message/envelope\";\nimport type { SchemaLike } from \"../../message/topic\";\nimport type {\n BatchMeta,\n ConsumerInterceptor,\n DedupStore,\n DeduplicationOptions,\n DlqReason,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n RetryOptions,\n} from \"../../types\";\n\n/**\n * Runtime dependencies injected into message handler functions by `KafkaClient`.\n * Holds shared infrastructure (producer, logger, instrumentation) and per-consumer\n * lifecycle callbacks (onMessageLost, onRetry, onDlq, etc.).\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 /** Fired on every failed handler attempt — drives the circuit breaker. */\n onFailure?: (envelope: EventEnvelope<any>) => void;\n};\n\n/** Active deduplication context passed from KafkaClient to the message handler. */\nexport type DeduplicationContext = {\n options: DeduplicationOptions;\n /** Backing store for the last processed Lamport clock (in-memory by default). */\n store: DedupStore;\n /** Consumer group ID this context belongs to — used as the store key. */\n groupId: string;\n};\n\n/**\n * Per-consumer configuration forwarded from `startConsumer` into `handleEachMessage`.\n * Carries schema map, handler function, interceptors, retry/DLQ policy, timeout,\n * deduplication context, TTL options, and the optional EOS routing context.\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 the deduplication store.\n * Returns `true` if the message is a duplicate and should be skipped.\n * Persists the clock on a fresh message.\n *\n * Fail-open: if the store's `getLastClock` / `setLastClock` throws or rejects,\n * the error is logged and the message is treated as not a duplicate.\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 let lastProcessedClock: number;\n try {\n lastProcessedClock =\n (await dedup.store.getLastClock(dedup.groupId, stateKey)) ?? -1;\n } catch (err) {\n deps.logger.error(\n `Dedup store getLastClock failed on ${envelope.topic}[${envelope.partition}] — ` +\n `treating message as not a duplicate (fail-open): ${(err as Error).message}`,\n );\n return false; // fail-open → pass through\n }\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 try {\n await dedup.store.setLastClock(dedup.groupId, stateKey, incomingClock);\n } catch (err) {\n deps.logger.error(\n `Dedup store setLastClock failed on ${envelope.topic}[${envelope.partition}] — ` +\n `processing message anyway (fail-open): ${(err as Error).message}`,\n );\n }\n return false;\n}\n\n/** Parse, validate and extract an envelope from a single raw Kafka message. Returns null to skip. */\nexport async 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\n/**\n * Core single-message processing pipeline invoked by the kafkajs `eachMessage` callback.\n * Parses, validates, deduplicates, checks TTL, runs the handler with retry/DLQ support,\n * and manages EOS offset commits when the main consumer runs with `autoCommit: false`.\n * @param payload Raw kafkajs `eachMessage` payload (topic, partition, message).\n * @param opts Consumer-level options including handler, schema map, retry policy, and EOS context.\n * @param deps Shared infrastructure dependencies.\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\n/**\n * Per-consumer configuration forwarded from `startBatchConsumer` into `handleEachBatch`.\n * Mirrors `EachMessageOpts` but carries a batch handler and batch-level EOS context.\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\n/**\n * Core batch processing pipeline invoked by the kafkajs `eachBatch` callback.\n * Iterates messages, parses, validates, deduplicates, and checks TTL for each one,\n * then calls the batch handler with the surviving envelopes and retry/DLQ support.\n * Manages EOS offset commits when the consumer runs with `autoCommit: false`.\n * @param payload Raw kafkajs `eachBatch` payload (batch, heartbeat, resolveOffset, commitOffsetsIfNecessary).\n * @param opts Consumer-level options including batch handler, schema map, retry policy, and EOS context.\n * @param deps Shared infrastructure dependencies.\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 { KafkaClientContext } from \"../context\";\nimport type { TopicMapConstraint } from \"../../types\";\nimport { _activeTransactionalIds } from \"../producer/lifecycle\";\nimport { toError } from \"./pipeline\";\n\n/** Minimal context surface required by this module. */\ntype StopCtx<T extends TopicMapConstraint<T>> = Pick<\n KafkaClientContext<T>,\n | \"circuitBreaker\"\n | \"companionGroupIds\"\n | \"consumerCreationOptions\"\n | \"consumers\"\n | \"dedupStates\"\n | \"defaultGroupId\"\n | \"logger\"\n | \"retryTxProducers\"\n | \"runningConsumers\"\n>;\n\n// ── Stop ──────────────────────────────────────────────────────────────────────\n\nexport async function stopConsumerImpl<T extends TopicMapConstraint<T>>(\n ctx: StopCtx<T>,\n groupId?: string,\n): Promise<void> {\n if (groupId === undefined) {\n const tasks: Promise<void>[] = [\n ...Array.from(ctx.consumers.values()).map((c) =>\n c.disconnect().catch(() => {}),\n ),\n ...Array.from(ctx.retryTxProducers.values()).map((p) =>\n p.disconnect().catch(() => {}),\n ),\n ];\n await Promise.allSettled(tasks);\n ctx.consumers.clear();\n ctx.runningConsumers.clear();\n ctx.consumerCreationOptions.clear();\n ctx.companionGroupIds.clear();\n ctx.retryTxProducers.clear();\n ctx.dedupStates.clear();\n ctx.circuitBreaker.clear();\n ctx.logger.log(\"All consumers disconnected\");\n return;\n }\n\n const consumer = ctx.consumers.get(groupId);\n if (!consumer) {\n ctx.logger.warn(\n `stopConsumer: no active consumer for group \"${groupId}\"`,\n );\n return;\n }\n await consumer\n .disconnect()\n .catch((e) =>\n ctx.logger.warn(\n `Error disconnecting consumer \"${groupId}\":`,\n toError(e).message,\n ),\n );\n ctx.consumers.delete(groupId);\n ctx.runningConsumers.delete(groupId);\n ctx.consumerCreationOptions.delete(groupId);\n ctx.dedupStates.delete(groupId);\n ctx.circuitBreaker.removeGroup(groupId);\n ctx.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 = ctx.retryTxProducers.get(mainTxId);\n if (mainTxProducer) {\n await mainTxProducer\n .disconnect()\n .catch((e) =>\n ctx.logger.warn(\n `Error disconnecting main tx producer \"${mainTxId}\":`,\n toError(e).message,\n ),\n );\n _activeTransactionalIds.delete(mainTxId);\n ctx.retryTxProducers.delete(mainTxId);\n }\n\n // Stop all companion retry level consumers and their tx producers\n const companions = ctx.companionGroupIds.get(groupId) ?? [];\n for (const cGroupId of companions) {\n const cConsumer = ctx.consumers.get(cGroupId);\n if (cConsumer) {\n await cConsumer\n .disconnect()\n .catch((e) =>\n ctx.logger.warn(\n `Error disconnecting retry consumer \"${cGroupId}\":`,\n toError(e).message,\n ),\n );\n ctx.consumers.delete(cGroupId);\n ctx.runningConsumers.delete(cGroupId);\n ctx.consumerCreationOptions.delete(cGroupId);\n ctx.logger.log(`Retry consumer disconnected: group \"${cGroupId}\"`);\n }\n const txId = `${cGroupId}-tx`;\n const txProducer = ctx.retryTxProducers.get(txId);\n if (txProducer) {\n await txProducer\n .disconnect()\n .catch((e) =>\n ctx.logger.warn(\n `Error disconnecting retry tx producer \"${txId}\":`,\n toError(e).message,\n ),\n );\n _activeTransactionalIds.delete(txId);\n ctx.retryTxProducers.delete(txId);\n }\n }\n ctx.companionGroupIds.delete(groupId);\n}\n\n// ── Pause / Resume ────────────────────────────────────────────────────────────\n\nexport function pauseConsumerImpl<T extends TopicMapConstraint<T>>(\n ctx: StopCtx<T>,\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n): void {\n const gid = groupId ?? ctx.defaultGroupId;\n const consumer = ctx.consumers.get(gid);\n if (!consumer) {\n ctx.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\nexport function resumeConsumerImpl<T extends TopicMapConstraint<T>>(\n ctx: StopCtx<T>,\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n): void {\n const gid = groupId ?? ctx.defaultGroupId;\n const consumer = ctx.consumers.get(gid);\n if (!consumer) {\n ctx.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 (backpressure for consume()). */\nexport function pauseTopicAllPartitions<T extends TopicMapConstraint<T>>(\n ctx: StopCtx<T>,\n gid: string,\n topic: string,\n): void {\n const consumer = ctx.consumers.get(gid);\n if (!consumer) return;\n const assignment = consumer.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 (backpressure for consume()). */\nexport function resumeTopicAllPartitions<T extends TopicMapConstraint<T>>(\n ctx: StopCtx<T>,\n gid: string,\n topic: string,\n): void {\n const consumer = ctx.consumers.get(gid);\n if (!consumer) return;\n const assignment = consumer.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","import type { KafkaClientContext } from \"../context\";\nimport type {\n TopicMapConstraint,\n ConsumerOptions,\n ConsumerHandle,\n BatchMeta,\n SendOptions,\n BatchSendOptions,\n TransactionalHandlerContext,\n} from \"../../types\";\nimport type { EventEnvelope } from \"../../message/envelope\";\nimport { runWithEnvelopeContext } from \"../../message/envelope\";\nimport { handleEachMessage, handleEachBatch, parseSingleMessage } from \"./handler\";\nimport {\n setupConsumer,\n validateTopicConsumerOpts,\n resolveDeduplicationContext,\n messageDepsFor,\n makeEosMainContext,\n launchRetryChain,\n} from \"./setup\";\nimport { createRetryTxProducer, wrapWithTimeoutWarning } from \"../producer/lifecycle\";\nimport { preparePayload } from \"../producer/send\";\nimport { toError } from \"./pipeline\";\n\n// ── startConsumer ─────────────────────────────────────────────────────────────\n\nexport async function startConsumerImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topics: any[],\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n options: ConsumerOptions<T> = {},\n): Promise<ConsumerHandle> {\n validateTopicConsumerOpts(topics, options);\n const setupOptions = options.retryTopics\n ? { ...options, autoCommit: false as const }\n : options;\n const { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry, readyPromise } =\n await setupConsumer(ctx, topics, \"eachMessage\", setupOptions);\n\n if (options.circuitBreaker)\n ctx.circuitBreaker.setConfig(gid, options.circuitBreaker);\n const deps = messageDepsFor(ctx, gid, options);\n const eosMainContext = await makeEosMainContext(ctx, gid, consumer, options);\n\n await consumer.run({\n eachMessage: (payload) =>\n ctx.inFlight.track(() =>\n handleEachMessage(\n payload,\n {\n schemaMap,\n handleMessage,\n interceptors,\n dlq,\n retry,\n retryTopics: options.retryTopics,\n timeoutMs: options.handlerTimeoutMs,\n wrapWithTimeout: (fn, ms, topic) =>\n wrapWithTimeoutWarning(ctx.logger, fn, ms, topic),\n deduplication: resolveDeduplicationContext(\n ctx,\n gid,\n options.deduplication,\n ),\n messageTtlMs: options.messageTtlMs,\n onTtlExpired: options.onTtlExpired,\n eosMainContext,\n },\n deps,\n ),\n ),\n });\n\n ctx.runningConsumers.set(gid, \"eachMessage\");\n if (options.retryTopics && retry) {\n await launchRetryChain(ctx, gid, topicNames, handleMessage, {\n retry,\n dlq,\n interceptors,\n schemaMap,\n assignmentTimeoutMs: options.retryTopicAssignmentTimeoutMs,\n });\n }\n return { groupId: gid, stop: () => stopConsumerByGid(ctx, gid), ready: () => readyPromise };\n}\n\n// ── startBatchConsumer ────────────────────────────────────────────────────────\n\nexport async function startBatchConsumerImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topics: any[],\n handleBatch: (\n envelopes: EventEnvelope<any>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options: ConsumerOptions<T> = {},\n): Promise<ConsumerHandle> {\n validateTopicConsumerOpts(topics, options);\n if (!options.retryTopics && options.autoCommit !== false) {\n ctx.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 const setupOptions = options.retryTopics\n ? { ...options, autoCommit: false as const }\n : options;\n const { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry, readyPromise } =\n await setupConsumer(ctx, topics, \"eachBatch\", setupOptions);\n\n if (options.circuitBreaker)\n ctx.circuitBreaker.setConfig(gid, options.circuitBreaker);\n const deps = messageDepsFor(ctx, gid, options);\n const eosMainContext = await makeEosMainContext(ctx, gid, consumer, options);\n\n await consumer.run({\n eachBatch: (payload) =>\n ctx.inFlight.track(() =>\n handleEachBatch(\n payload,\n {\n schemaMap,\n handleBatch,\n interceptors,\n dlq,\n retry,\n retryTopics: options.retryTopics,\n timeoutMs: options.handlerTimeoutMs,\n wrapWithTimeout: (fn, ms, topic) =>\n wrapWithTimeoutWarning(ctx.logger, fn, ms, topic),\n deduplication: resolveDeduplicationContext(\n ctx,\n gid,\n options.deduplication,\n ),\n messageTtlMs: options.messageTtlMs,\n onTtlExpired: options.onTtlExpired,\n eosMainContext,\n },\n deps,\n ),\n ),\n });\n\n ctx.runningConsumers.set(gid, \"eachBatch\");\n if (options.retryTopics && retry) {\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 await launchRetryChain(ctx, gid, topicNames, handleMessageForRetry, {\n retry,\n dlq,\n interceptors,\n schemaMap,\n assignmentTimeoutMs: options.retryTopicAssignmentTimeoutMs,\n });\n }\n return { groupId: gid, stop: () => stopConsumerByGid(ctx, gid), ready: () => readyPromise };\n}\n\n// ── startTransactionalConsumer ────────────────────────────────────────────────\n\nexport async function startTransactionalConsumerImpl<\n T extends TopicMapConstraint<T>,\n>(\n ctx: KafkaClientContext<T>,\n topics: any[],\n handler: (\n envelope: EventEnvelope<any>,\n tx: TransactionalHandlerContext<T>,\n ) => Promise<void>,\n options: ConsumerOptions<T> = {},\n): Promise<ConsumerHandle> {\n if (options.retryTopics) {\n throw new Error(\n \"startTransactionalConsumer: retryTopics is not supported. \" +\n \"EOS is already guaranteed by the transaction — redelivery on failure is handled automatically.\",\n );\n }\n\n const setupOptions = { ...options, autoCommit: false as const };\n const { consumer, schemaMap, gid, readyPromise } = await setupConsumer(\n ctx,\n topics,\n \"eachMessage\",\n setupOptions,\n );\n\n const txProducer = await createRetryTxProducer(ctx, `${gid}-txc`);\n const deps = messageDepsFor(ctx, gid);\n\n await consumer.run({\n eachMessage: ({ topic, partition, message }) =>\n ctx.inFlight.track(async () => {\n const envelope = await parseSingleMessage(\n message,\n topic,\n partition,\n schemaMap,\n options.interceptors ?? [],\n false,\n deps,\n );\n\n const nextOffset = String(Number.parseInt(message.offset, 10) + 1);\n\n if (envelope === null) {\n await consumer.commitOffsets([\n { topic, partition, offset: nextOffset },\n ]);\n return;\n }\n\n const tx = await txProducer.transaction();\n\n const txCtx: TransactionalHandlerContext<T> = {\n send: async (t: any, msg: any, sendOpts?: SendOptions) => {\n const payload = await preparePayload(ctx, t, [\n {\n value: msg,\n key: sendOpts?.key,\n headers: sendOpts?.headers,\n correlationId: sendOpts?.correlationId,\n schemaVersion: sendOpts?.schemaVersion,\n eventId: sendOpts?.eventId,\n },\n ]);\n await tx.send(payload);\n },\n sendBatch: async (t: any, msgs: any[], batchOpts?: BatchSendOptions) => {\n const payload = await preparePayload(\n ctx,\n t,\n msgs,\n batchOpts?.compression,\n );\n await tx.send(payload);\n },\n };\n\n try {\n await runWithEnvelopeContext(\n {\n correlationId: envelope.correlationId,\n traceparent: envelope.traceparent,\n },\n () => handler(envelope, txCtx),\n );\n await tx.sendOffsets({\n consumer,\n topics: [\n { topic, partitions: [{ partition, offset: nextOffset }] },\n ],\n });\n await tx.commit();\n deps.onMessage?.(envelope);\n } catch (err) {\n try {\n await tx.abort();\n } catch {}\n ctx.logger.warn(\n `startTransactionalConsumer: handler failed on ${topic}[${partition}]@${message.offset} — ` +\n `tx aborted, message will be redelivered (${toError(err).message})`,\n );\n throw err;\n }\n }),\n });\n\n ctx.runningConsumers.set(gid, \"eachMessage\");\n return { groupId: gid, stop: () => stopConsumerByGid(ctx, gid), ready: () => readyPromise };\n}\n\n// ── Shared stop helper (avoids circular dep with stop.ts) ─────────────────────\n\nimport { stopConsumerImpl } from \"./stop\";\n\nfunction stopConsumerByGid<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n gid: string,\n): Promise<void> {\n return stopConsumerImpl(ctx, gid);\n}\n","import type { KafkaClientContext } from \"../../context\";\nimport type {\n TopicMapConstraint,\n WindowConsumerOptions,\n WindowMeta,\n ConsumerHandle,\n} from \"../../../types\";\nimport type { EventEnvelope } from \"../../../message/envelope\";\nimport { startConsumerImpl } from \"../start\";\nimport { toError } from \"../pipeline\";\n\n// ── startWindowConsumer ───────────────────────────────────────────────────────\n\nexport async function startWindowConsumerImpl<\n T extends TopicMapConstraint<T>,\n K extends keyof T & string,\n>(\n ctx: KafkaClientContext<T>,\n topic: K,\n handler: (envelopes: EventEnvelope<T[K]>[], meta: WindowMeta) => Promise<void>,\n options: WindowConsumerOptions<T>,\n): Promise<ConsumerHandle> {\n const { maxMessages, maxMs, ...consumerOptions } = options;\n\n if (maxMessages <= 0)\n throw new Error(\"startWindowConsumer: maxMessages must be > 0\");\n if (maxMs <= 0) throw new Error(\"startWindowConsumer: maxMs must be > 0\");\n if ((consumerOptions as any).retryTopics) {\n throw new Error(\n \"startWindowConsumer() does not support retryTopics. \" +\n \"Use startConsumer() with retryTopics: true for guaranteed retry delivery.\",\n );\n }\n\n const buffer: EventEnvelope<T[K]>[] = [];\n let flushTimer: ReturnType<typeof setTimeout> | null = null;\n let windowStart = 0;\n\n const onLost =\n (consumerOptions as { onMessageLost?: typeof ctx.onMessageLost })\n .onMessageLost ?? ctx.onMessageLost;\n\n const flush = async (trigger: \"size\" | \"time\"): Promise<void> => {\n if (flushTimer !== null) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n if (buffer.length === 0) return;\n const envelopes = buffer.splice(0);\n try {\n await handler(envelopes, { trigger, windowStart, windowEnd: Date.now() });\n } catch (err) {\n // Offsets for these messages may already be auto-committed — a thrown\n // flush would otherwise lose the whole window silently. Route every\n // buffered envelope to onMessageLost so the caller can react.\n const error = toError(err);\n ctx.logger.error(\n `startWindowConsumer: ${trigger}-triggered flush failed — window of ${envelopes.length} message(s) lost:`,\n error.stack,\n );\n for (const envelope of envelopes) {\n await Promise.resolve(\n onLost?.({\n topic: envelope.topic,\n error,\n attempt: 0,\n headers: envelope.headers,\n }),\n ).catch(() => {});\n }\n }\n };\n\n const scheduleFlush = (): void => {\n if (flushTimer !== null) return;\n flushTimer = setTimeout(() => {\n flushTimer = null;\n void flush(\"time\");\n }, maxMs);\n };\n\n const handle = await startConsumerImpl(\n ctx,\n [topic as any],\n async (envelope) => {\n if (buffer.length === 0) windowStart = Date.now();\n buffer.push(envelope as EventEnvelope<T[K]>);\n scheduleFlush();\n if (buffer.length >= maxMessages) await flush(\"size\");\n },\n consumerOptions as any,\n );\n\n const originalStop = handle.stop.bind(handle);\n handle.stop = async (): Promise<void> => {\n // flush() handles timer cleanup and routes failures to onMessageLost.\n await flush(\"time\");\n return originalStop();\n };\n\n return handle;\n}\n","import type { KafkaClientContext } from \"../../context\";\nimport type {\n TopicMapConstraint,\n ConsumerOptions,\n ConsumerHandle,\n RoutingOptions,\n} from \"../../../types\";\nimport type { EventEnvelope } from \"../../../message/envelope\";\nimport { startConsumerImpl } from \"../start\";\n\n// ── startRoutedConsumer ───────────────────────────────────────────────────────\n\nexport async function startRoutedConsumerImpl<\n T extends TopicMapConstraint<T>,\n K extends Array<keyof T>,\n>(\n ctx: KafkaClientContext<T>,\n topics: K,\n routing: RoutingOptions<T[K[number]]>,\n options?: ConsumerOptions<T>,\n): Promise<ConsumerHandle> {\n const { header, routes, fallback } = routing;\n const handleMessage = async (\n envelope: EventEnvelope<T[K[number]]>,\n ): Promise<void> => {\n const headerValue = envelope.headers[header];\n const routeHandler =\n headerValue === undefined ? undefined : routes[headerValue];\n if (routeHandler) {\n await routeHandler(envelope);\n } else {\n await fallback?.(envelope);\n }\n };\n return startConsumerImpl(ctx, topics, handleMessage, options);\n}\n","import type { KafkaClientContext } from \"../../context\";\nimport type { TopicMapConstraint, ConsumerHandle, MessageHeaders } from \"../../../types\";\nimport {\n decodeHeaders,\n HEADER_DELAYED_TARGET,\n HEADER_DELAYED_UNTIL,\n} from \"../../../message/envelope\";\nimport { getOrCreateConsumer } from \"../ops\";\nimport { subscribeWithRetry } from \"../subscribe-retry\";\nimport { createRetryTxProducer, ensureTopic } from \"../../producer/lifecycle\";\nimport { sleep, toError } from \"../pipeline\";\nimport { stopConsumerImpl } from \"../stop\";\n\n/** Staging topic name for delayed messages of `topic`. */\nexport function delayedTopicName(topic: string): string {\n return `${topic}.delayed`;\n}\n\n/**\n * Start a relay consumer that delivers messages produced with\n * `SendOptions.deliverAfterMs` from `<topic>.delayed` to their target topic\n * once their `x-delayed-until` deadline passes.\n *\n * Semantics mirror the retry-topic chain:\n * - The partition is paused while waiting for the deadline; the offset is not\n * committed during the wait, so a crash redelivers the message (at-least-once).\n * - Forwarding uses a Kafka transaction (produce to target + commit source\n * offset atomically), so no duplicates are relayed even on crash.\n * - Delivery time is a lower bound — head-of-line waiting applies per partition.\n *\n * The relay preserves the original key, value, and headers (minus the\n * `x-delayed-*` control headers), so envelope metadata (`x-event-id`,\n * `x-correlation-id`, `x-lamport-clock`, `traceparent`) survives the hop.\n */\nexport async function startDelayedRelayImpl<T extends TopicMapConstraint<T>>(\n ctx: KafkaClientContext<T>,\n topics: string[],\n options?: { groupId?: string },\n): Promise<ConsumerHandle> {\n if (topics.length === 0) {\n throw new Error(\"startDelayedRelay: at least one topic is required\");\n }\n const gid = options?.groupId ?? `${ctx.defaultGroupId}-delayed-relay`;\n if (ctx.runningConsumers.has(gid)) {\n throw new Error(\n `startDelayedRelay(\"${gid}\") called twice — this group is already consuming. ` +\n `Call stopConsumer(\"${gid}\") first or pass a different groupId.`,\n );\n }\n\n const delayedTopics = topics.map(delayedTopicName);\n for (const t of delayedTopics) await ensureTopic(ctx, t);\n\n // Transactional producer: forward to target + commit source offset atomically.\n const txProducer = await createRetryTxProducer(ctx, `${gid}-tx`);\n\n let resolveReady!: () => void;\n const readyPromise = new Promise<void>((resolve) => {\n resolveReady = resolve;\n });\n\n // autoCommit: false — offsets are committed only inside the forwarding\n // transaction (or explicitly for skipped messages).\n const consumer = getOrCreateConsumer(\n gid,\n false,\n false,\n ctx.consumerOpsDeps,\n undefined,\n resolveReady,\n );\n await consumer.connect();\n await subscribeWithRetry(consumer, delayedTopics, ctx.logger);\n\n await consumer.run({\n eachMessage: async ({ topic: stagingTopic, partition, message }) => {\n const nextOffset = {\n topic: stagingTopic,\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 target =\n (headers[HEADER_DELAYED_TARGET] as string | undefined) ??\n stagingTopic.replace(/\\.delayed$/, \"\");\n const until = parseInt(\n (headers[HEADER_DELAYED_UNTIL] as string | undefined) ?? \"0\",\n 10,\n );\n const remaining = until - Date.now();\n\n // Hold the partition until the deadline. Offset stays uncommitted —\n // a crash during the wait redelivers the message on restart.\n if (remaining > 0) {\n consumer.pause([{ topic: stagingTopic, partitions: [partition] }]);\n await sleep(remaining);\n consumer.resume([{ topic: stagingTopic, partitions: [partition] }]);\n }\n\n const forwardHeaders: MessageHeaders = Object.fromEntries(\n Object.entries(headers).filter(\n ([k]) => k !== HEADER_DELAYED_UNTIL && k !== HEADER_DELAYED_TARGET,\n ),\n ) as MessageHeaders;\n\n const tx = await txProducer.transaction();\n try {\n await tx.send({\n topic: target,\n messages: [\n {\n value: message.value.toString(),\n key: message.key ? message.key.toString() : null,\n headers: forwardHeaders,\n },\n ],\n });\n await tx.sendOffsets({\n consumer,\n topics: [\n {\n topic: nextOffset.topic,\n partitions: [\n { partition: nextOffset.partition, offset: nextOffset.offset },\n ],\n },\n ],\n });\n await tx.commit();\n ctx.logger.debug?.(\n `Delayed message relayed to \"${target}\" (deadline ${new Date(until).toISOString()})`,\n );\n } catch (txErr) {\n try {\n await tx.abort();\n } catch {}\n ctx.logger.error(\n `Delayed relay to \"${target}\" failed — message will be redelivered:`,\n toError(txErr).stack,\n );\n // Offset not committed — the staging message is redelivered and retried.\n }\n },\n });\n\n ctx.runningConsumers.set(gid, \"eachMessage\");\n ctx.logger.log(\n `Delayed relay started for: ${delayedTopics.join(\", \")} (group: ${gid})`,\n );\n\n return {\n groupId: gid,\n ready: () => readyPromise,\n stop: async () => {\n await stopConsumerImpl(ctx, gid);\n },\n };\n}\n","import type { KafkaClientContext } from \"../../context\";\nimport type {\n TopicMapConstraint,\n ReadSnapshotOptions,\n CheckpointResult,\n CheckpointRestoreResult,\n RestoreCheckpointOptions,\n CheckpointEntry,\n} from \"../../../types\";\nimport type { EventEnvelope } from \"../../../message/envelope\";\nimport { decodeHeaders, extractEnvelope } from \"../../../message/envelope\";\nimport { toError } from \"../pipeline\";\n\n/** Minimal context surface required by this module. */\ntype SnapshotCtx<T extends TopicMapConstraint<T>> = Pick<\n KafkaClientContext<T>,\n | \"adminOps\"\n | \"clientId\"\n | \"defaultGroupId\"\n | \"transport\"\n | \"logger\"\n | \"producer\"\n | \"runningConsumers\"\n>;\n\n// ── readSnapshot ──────────────────────────────────────────────────────────────\n\nexport async function readSnapshotImpl<\n T extends TopicMapConstraint<T>,\n K extends keyof T & string,\n>(\n ctx: SnapshotCtx<T>,\n topic: K,\n options: ReadSnapshotOptions = {},\n): Promise<Map<string, EventEnvelope<T[K]>>> {\n await ctx.adminOps.ensureConnected();\n\n let offsets: Array<{ partition: number; low: string; high: string }>;\n try {\n offsets = await ctx.adminOps.admin.fetchTopicOffsets(topic);\n } catch {\n ctx.logger.warn(\n `readSnapshot: could not fetch offsets for \"${String(topic)}\", returning empty snapshot`,\n );\n return new Map();\n }\n\n const targets = new Map<number, number>();\n for (const { partition, high, low } of offsets) {\n const highN = Number.parseInt(high, 10);\n const lowN = Number.parseInt(low, 10);\n if (highN > lowN) targets.set(partition, highN - 1);\n }\n\n if (targets.size === 0) {\n ctx.logger.debug?.(\n `readSnapshot: topic \"${String(topic)}\" is empty — returning empty snapshot`,\n );\n return new Map();\n }\n\n const snapshot = new Map<string, EventEnvelope<T[K]>>();\n const remaining = new Set(targets.keys());\n const snapshotGroupId = `${ctx.clientId}-snapshot-${Date.now()}`;\n\n await new Promise<void>((resolve, reject) => {\n const consumer = ctx.transport.consumer({\n groupId: snapshotGroupId,\n fromBeginning: true,\n });\n const cleanup = () => {\n consumer\n .disconnect()\n .catch(() => {})\n .finally(() => {\n ctx.adminOps.deleteGroups([snapshotGroupId]).catch(() => {});\n });\n };\n\n consumer\n .connect()\n .then(() => consumer.subscribe({ topics: [topic] }))\n .then(() =>\n consumer.run({\n eachMessage: async ({ topic: t, partition, message }) => {\n if (!remaining.has(partition)) return;\n\n const msgOffsetN = Number.parseInt(message.offset, 10);\n applySnapshotMessage(snapshot, options, ctx, t, partition, message);\n\n if (msgOffsetN >= targets.get(partition)!) {\n remaining.delete(partition);\n if (remaining.size === 0) {\n cleanup();\n resolve();\n }\n }\n },\n }),\n )\n .catch((err) => {\n cleanup();\n reject(err);\n });\n });\n\n ctx.logger.log(\n `readSnapshot: ${snapshot.size} key(s) from \"${String(topic)}\"`,\n );\n return snapshot;\n}\n\nfunction applySnapshotMessage<\n T extends TopicMapConstraint<T>,\n K extends keyof T & string,\n>(\n snapshot: Map<string, EventEnvelope<T[K]>>,\n options: ReadSnapshotOptions,\n ctx: SnapshotCtx<T>,\n t: string,\n partition: number,\n message: { key: Buffer | null; value: Buffer | null; offset: string; headers?: any },\n): void {\n let key: string | null = null;\n if (message.key) {\n key = Buffer.isBuffer(message.key)\n ? message.key.toString()\n : String(message.key);\n }\n\n if (message.value === null || message.value === undefined) {\n if (key !== null) {\n snapshot.delete(key);\n options.onTombstone?.(key);\n }\n return;\n }\n\n if (key === null) return;\n\n const rawValue = Buffer.isBuffer(message.value)\n ? message.value.toString()\n : String(message.value);\n try {\n const jsonValue = JSON.parse(rawValue);\n const headers = decodeHeaders(message.headers);\n const parsed: T[K] = options.schema ? options.schema.parse(jsonValue) : jsonValue;\n snapshot.set(\n key,\n extractEnvelope<T[K]>(parsed, headers, t, partition, message.offset),\n );\n } catch (err) {\n ctx.logger.warn(\n `readSnapshot: skipping ${t}:${partition}@${message.offset} — ${toError(err).message}`,\n );\n }\n}\n\n// ── checkpointOffsets ─────────────────────────────────────────────────────────\n\nexport async function checkpointOffsetsImpl<T extends TopicMapConstraint<T>>(\n ctx: SnapshotCtx<T>,\n groupId: string | undefined,\n checkpointTopic: string,\n): Promise<CheckpointResult> {\n const gid = groupId ?? ctx.defaultGroupId;\n await ctx.adminOps.ensureConnected();\n\n const committed = await ctx.adminOps.admin.fetchOffsets({ groupId: gid });\n\n const offsets: CheckpointEntry[] = [];\n for (const { topic, partitions } of committed) {\n for (const { partition, offset } of partitions) {\n offsets.push({ topic, partition, offset });\n }\n }\n\n const savedAt = Date.now();\n const payload = JSON.stringify({ groupId: gid, offsets, savedAt });\n\n await ctx.producer.send({\n topic: checkpointTopic,\n messages: [\n {\n key: gid,\n value: payload,\n headers: {\n \"x-checkpoint-group-id\": [gid],\n \"x-checkpoint-timestamp\": [String(savedAt)],\n },\n },\n ],\n });\n\n const topics = [...new Set(offsets.map((e) => e.topic))];\n ctx.logger.log(\n `checkpointOffsets: saved ${offsets.length} partition(s) for group \"${gid}\" → \"${checkpointTopic}\"`,\n );\n return { groupId: gid, topics, partitionCount: offsets.length, savedAt };\n}\n\n// ── restoreFromCheckpoint ─────────────────────────────────────────────────────\n\nexport async function restoreFromCheckpointImpl<T extends TopicMapConstraint<T>>(\n ctx: SnapshotCtx<T>,\n groupId: string | undefined,\n checkpointTopic: string,\n options: RestoreCheckpointOptions = {},\n): Promise<CheckpointRestoreResult> {\n const gid = groupId ?? ctx.defaultGroupId;\n\n if (ctx.runningConsumers.has(gid)) {\n throw new Error(\n `restoreFromCheckpoint: consumer group \"${gid}\" is still running. ` +\n `Call stopConsumer(\"${gid}\") before restoring offsets.`,\n );\n }\n\n await ctx.adminOps.ensureConnected();\n\n const checkpoints: Array<{ savedAt: number; offsets: CheckpointEntry[] }> = [];\n\n let hwmOffsets: Array<{ partition: number; low: string; high: string }>;\n try {\n hwmOffsets = await ctx.adminOps.admin.fetchTopicOffsets(checkpointTopic);\n } catch {\n throw new Error(\n `restoreFromCheckpoint: could not fetch offsets for \"${checkpointTopic}\" — does the topic exist?`,\n );\n }\n\n const targets = new Map<number, number>();\n for (const { partition, high, low } of hwmOffsets) {\n const highN = Number.parseInt(high, 10);\n if (highN > Number.parseInt(low, 10)) targets.set(partition, highN - 1);\n }\n\n if (targets.size > 0) {\n const checkpointGroupId = `${ctx.clientId}-checkpoint-restore-${Date.now()}`;\n await new Promise<void>((resolve, reject) => {\n const consumer = ctx.transport.consumer({\n groupId: checkpointGroupId,\n fromBeginning: true,\n });\n const cleanup = () => {\n consumer\n .disconnect()\n .catch(() => {})\n .finally(() => {\n ctx.adminOps.deleteGroups([checkpointGroupId]).catch(() => {});\n });\n };\n const remaining = new Set(targets.keys());\n\n consumer\n .connect()\n .then(() => consumer.subscribe({ topics: [checkpointTopic] }))\n .then(() =>\n consumer.run({\n eachMessage: async ({ partition, message }) => {\n if (!remaining.has(partition)) return;\n\n let msgKey: string | null = null;\n if (message.key) {\n msgKey = Buffer.isBuffer(message.key)\n ? message.key.toString()\n : String(message.key);\n }\n\n if (msgKey === gid && message.value) {\n try {\n const raw = Buffer.isBuffer(message.value)\n ? message.value.toString()\n : String(message.value);\n const parsed = JSON.parse(raw) as {\n groupId: string;\n offsets: CheckpointEntry[];\n savedAt: number;\n };\n checkpoints.push({\n savedAt: parsed.savedAt,\n offsets: parsed.offsets,\n });\n } catch {\n ctx.logger.warn(\n `restoreFromCheckpoint: skipping malformed checkpoint at partition ${partition}@${message.offset}`,\n );\n }\n }\n\n if (\n Number.parseInt(message.offset, 10) >= targets.get(partition)!\n ) {\n remaining.delete(partition);\n if (remaining.size === 0) {\n cleanup();\n resolve();\n }\n }\n },\n }),\n )\n .catch((err) => {\n cleanup();\n reject(err);\n });\n });\n }\n\n if (checkpoints.length === 0) {\n throw new Error(\n `restoreFromCheckpoint: no checkpoints found for group \"${gid}\" in \"${checkpointTopic}\"`,\n );\n }\n\n const target = options.timestamp;\n let best: (typeof checkpoints)[number];\n if (target === undefined) {\n best = checkpoints.reduce(\n (acc, c) => (c.savedAt > acc.savedAt ? c : acc),\n checkpoints[0],\n );\n } else {\n const candidates = checkpoints.filter((c) => c.savedAt <= target);\n if (candidates.length > 0) {\n best = candidates.reduce(\n (acc, c) => (c.savedAt > acc.savedAt ? c : acc),\n candidates[0],\n );\n } else {\n best = checkpoints.reduce(\n (acc, c) => (c.savedAt < acc.savedAt ? c : acc),\n checkpoints[0],\n );\n ctx.logger.warn(\n `restoreFromCheckpoint: no checkpoint at or before ${new Date(target).toISOString()} — ` +\n `using oldest available (${new Date(best.savedAt).toISOString()})`,\n );\n }\n }\n\n await ctx.adminOps.seekToOffset(gid, best.offsets);\n\n const checkpointAge = Date.now() - best.savedAt;\n ctx.logger.log(\n `restoreFromCheckpoint: restored ${best.offsets.length} partition(s) for group \"${gid}\" ` +\n `from checkpoint at ${new Date(best.savedAt).toISOString()} (age: ${checkpointAge}ms)`,\n );\n\n return {\n groupId: gid,\n offsets: best.offsets,\n restoredAt: best.savedAt,\n checkpointAge,\n };\n}\n","import type { KafkaTransport } from \"../transport/transport.interface\";\nimport { ConfluentTransport } from \"../transport/confluent.transport\";\nimport type { 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 BatchMeta,\n DlqReplayOptions,\n ConsumerGroupSummary,\n TopicDescription,\n MessageHeaders,\n ReadSnapshotOptions,\n CheckpointResult,\n CheckpointRestoreResult,\n RestoreCheckpointOptions,\n WindowConsumerOptions,\n WindowMeta,\n RoutingOptions,\n TransactionalHandlerContext,\n} from \"../types\";\n\n// Re-export all types so existing `import { ... } from './kafka.client'` keeps working\nexport * from \"../types\";\n\n// Re-export the default deduplication store implementation.\nexport { InMemoryDedupStore } from \"./infra/dedup.store\";\n\nimport { getOrCreateConsumer } from \"./consumer/ops\";\nimport { AdminOps } from \"./admin/ops\";\nimport { replayDlqTopic } from \"./consumer/features/dlq-replay\";\nimport { MetricsManager } from \"./infra/metrics.manager\";\nimport { InFlightTracker } from \"./infra/inflight.tracker\";\nimport { CircuitBreakerManager } from \"./infra/circuit-breaker.manager\";\nimport { AsyncQueue } from \"./consumer/queue\";\nimport { toError } from \"./consumer/pipeline\";\nimport { validateClientOptions } from \"./validate-options\";\nimport { resolveSecurityOptions } from \"../security/resolve-security\";\n\nimport type { KafkaClientContext } from \"./context\";\nimport { connectProducerImpl, disconnectImpl } from \"./producer/lifecycle\";\nimport {\n sendMessageImpl,\n sendBatchImpl,\n sendTombstoneImpl,\n transactionImpl,\n} from \"./producer/send\";\nimport { buildRetryTopicDeps } from \"./consumer/setup\";\nimport { startConsumerImpl, startBatchConsumerImpl, startTransactionalConsumerImpl } from \"./consumer/start\";\nimport { startWindowConsumerImpl } from \"./consumer/features/window\";\nimport { startRoutedConsumerImpl } from \"./consumer/features/routed\";\nimport { startDelayedRelayImpl } from \"./consumer/features/delayed\";\nimport {\n stopConsumerImpl,\n pauseConsumerImpl,\n resumeConsumerImpl,\n pauseTopicAllPartitions,\n resumeTopicAllPartitions,\n} from \"./consumer/stop\";\nimport {\n readSnapshotImpl,\n checkpointOffsetsImpl,\n restoreFromCheckpointImpl,\n} from \"./consumer/features/snapshot\";\n\n/** DLQ header keys added by the pipeline — stripped before re-publishing. */\nconst 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 * 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 public readonly clientId: ClientId;\n private readonly ctx: KafkaClientContext<T>;\n\n /**\n * Create a new KafkaClient.\n * @param clientId Unique client identifier (used in Kafka metadata and logs).\n * @param groupId Default consumer group ID for this client.\n * @param brokers Array of broker addresses, e.g. `['localhost:9092']`.\n * @param options Optional client-wide configuration.\n * @example\n * ```ts\n * const kafka = new KafkaClient('my-service', 'my-service-group', ['localhost:9092'], {\n * lagThrottle: { maxLag: 10_000 },\n * onMessageLost: (ctx) => logger.error('Message lost', ctx),\n * });\n * await kafka.connectProducer();\n * ```\n */\n constructor(\n clientId: ClientId,\n groupId: GroupId,\n brokers: string[],\n options?: KafkaClientOptions,\n ) {\n validateClientOptions(clientId, groupId, brokers, options);\n this.clientId = clientId;\n\n const logger = options?.logger ?? {\n log: (msg: string) => console.log(`[KafkaClient:${clientId}] ${msg}`),\n warn: (msg: string, ...args: any[]) =>\n console.warn(`[KafkaClient:${clientId}] ${msg}`, ...args),\n error: (msg: string, ...args: any[]) =>\n console.error(`[KafkaClient:${clientId}] ${msg}`, ...args),\n debug: (msg: string, ...args: any[]) =>\n console.debug(`[KafkaClient:${clientId}] ${msg}`, ...args),\n };\n\n const security = resolveSecurityOptions(options?.security, brokers, logger);\n const transport: KafkaTransport =\n options?.transport ?? new ConfluentTransport(clientId, brokers, security);\n const producer = transport.producer();\n\n const runningConsumers = new Map<string, \"eachMessage\" | \"eachBatch\">();\n const consumers = new Map();\n const consumerCreationOptions = new Map<string, { fromBeginning: boolean; autoCommit: boolean }>();\n const schemaRegistry = new Map<string, SchemaLike>();\n\n const adminOps = new AdminOps({\n admin: transport.admin(),\n logger,\n runningConsumers,\n defaultGroupId: groupId,\n clientId,\n });\n\n const circuitBreaker = new CircuitBreakerManager({\n pauseConsumer: (gid, assignments) => pauseConsumerImpl(this.ctx, gid, assignments),\n resumeConsumer: (gid, assignments) => resumeConsumerImpl(this.ctx, gid, assignments),\n logger,\n instrumentation: options?.instrumentation ?? [],\n });\n\n const metrics = new MetricsManager({\n instrumentation: options?.instrumentation ?? [],\n onCircuitFailure: (envelope, gid) => circuitBreaker.onFailure(envelope, gid),\n onCircuitSuccess: (envelope, gid) => circuitBreaker.onSuccess(envelope, gid),\n });\n\n const inFlight = new InFlightTracker((msg) => logger.warn(msg));\n\n // Partially construct context — retryTopicDeps is patched below\n const ctx = {\n clientId,\n defaultGroupId: groupId,\n logger,\n autoCreateTopicsEnabled: options?.autoCreateTopics ?? false,\n strictSchemasEnabled: options?.strictSchemas ?? true,\n numPartitions: options?.numPartitions ?? 1,\n txId: options?.transactionalId ?? `${clientId}-tx`,\n clockRecoveryTopics: options?.clockRecovery?.topics ?? [],\n clockRecoveryTimeoutMs: options?.clockRecovery?.timeoutMs ?? 30_000,\n lagThrottleOpts: options?.lagThrottle,\n instrumentation: options?.instrumentation ?? [],\n onMessageLost: options?.onMessageLost,\n onTtlExpired: options?.onTtlExpired,\n onRebalance: options?.onRebalance,\n transport,\n producer,\n txProducer: undefined as any,\n txProducerInitPromise: undefined as any,\n _txChain: Promise.resolve(),\n retryTxProducers: new Map(),\n consumers,\n runningConsumers,\n consumerCreationOptions,\n companionGroupIds: new Map<string, string[]>(),\n dedupStates: new Map<string, Map<string, number>>(),\n ensuredTopics: new Set<string>(),\n ensureTopicPromises: new Map<string, Promise<void>>(),\n schemaRegistry,\n _lagThrottled: false,\n _lagThrottleTimer: undefined as any,\n _lamportClock: 0,\n circuitBreaker,\n adminOps,\n metrics,\n inFlight,\n producerOpsDeps: {\n schemaRegistry,\n strictSchemasEnabled: options?.strictSchemas ?? true,\n instrumentation: options?.instrumentation ?? [],\n logger,\n nextLamportClock: () => 0, // patched below\n },\n consumerOpsDeps: {\n consumers,\n consumerCreationOptions,\n transport,\n onRebalance: options?.onRebalance,\n logger,\n },\n retryTopicDeps: {} as any, // patched below\n } satisfies KafkaClientContext<T>;\n\n // Patch mutable closures that need the fully constructed ctx\n ctx.producerOpsDeps = {\n schemaRegistry,\n strictSchemasEnabled: options?.strictSchemas ?? true,\n instrumentation: options?.instrumentation ?? [],\n logger,\n nextLamportClock: () => ++ctx._lamportClock,\n };\n ctx.retryTopicDeps = buildRetryTopicDeps(ctx);\n\n this.ctx = ctx;\n }\n\n // ── Send ──────────────────────────────────────────────────────────\n\n /** @inheritDoc */\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 return sendMessageImpl(this.ctx, topicOrDesc, message, options);\n }\n\n /** @inheritDoc */\n public async sendTombstone(\n topic: string,\n key: string,\n headers?: MessageHeaders,\n ): Promise<void> {\n return sendTombstoneImpl(this.ctx, topic, key, headers);\n }\n\n /** @inheritDoc */\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 return sendBatchImpl(this.ctx, topicOrDesc, messages, options);\n }\n\n /** @inheritDoc */\n public async transaction(\n fn: (ctx: TransactionContext<T>) => Promise<void>,\n ): Promise<void> {\n return transactionImpl(this.ctx, fn);\n }\n\n // ── Producer lifecycle ────────────────────────────────────────────\n\n /** @inheritDoc */\n public async connectProducer(): Promise<void> {\n return connectProducerImpl(this.ctx);\n }\n\n /** @internal */\n public async disconnectProducer(): Promise<void> {\n if (this.ctx._lagThrottleTimer) {\n clearInterval(this.ctx._lagThrottleTimer);\n this.ctx._lagThrottleTimer = undefined;\n }\n await this.ctx.producer.disconnect();\n this.ctx.logger.log(\"Producer disconnected\");\n }\n\n // ── Consumer: eachMessage ─────────────────────────────────────────\n\n /** @inheritDoc */\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 return startConsumerImpl(this.ctx, topics, handleMessage, options);\n }\n\n // ── Consumer: eachBatch ───────────────────────────────────────────\n\n /** @inheritDoc */\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 return startBatchConsumerImpl(this.ctx, topics, handleBatch, options);\n }\n\n // ── Consumer: AsyncIterableIterator ──────────────────────────────\n\n /** @inheritDoc */\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.ctx.defaultGroupId;\n const queue = new AsyncQueue<EventEnvelope<T[K]>>(\n options?.queueHighWaterMark,\n () => pauseTopicAllPartitions(this.ctx, gid, topic as string),\n () => resumeTopicAllPartitions(this.ctx, 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: windowed ────────────────────────────────────────────\n\n /** @inheritDoc */\n public startWindowConsumer<K extends keyof T & string>(\n topic: K,\n handler: (\n envelopes: EventEnvelope<T[K]>[],\n meta: WindowMeta,\n ) => Promise<void>,\n options: WindowConsumerOptions<T>,\n ): Promise<ConsumerHandle> {\n return startWindowConsumerImpl(this.ctx, topic, handler, options);\n }\n\n // ── Consumer: header routing ──────────────────────────────────────\n\n /** @inheritDoc */\n public startRoutedConsumer<K extends Array<keyof T>>(\n topics: K,\n routing: RoutingOptions<T[K[number]]>,\n options?: ConsumerOptions<T>,\n ): Promise<ConsumerHandle> {\n return startRoutedConsumerImpl(this.ctx, topics, routing, options);\n }\n\n // ── Consumer: delayed delivery relay ──────────────────────────────\n\n /**\n * Start a relay that delivers messages produced with\n * `SendOptions.deliverAfterMs` from `<topic>.delayed` to their target topic\n * once their deadline passes.\n *\n * Forwarding is transactional (produce + source-offset commit are atomic),\n * so no duplicates are relayed even if the relay crashes mid-forward.\n * Delivery time is a lower bound — the relay must be running for delayed\n * messages to be delivered at all.\n *\n * @param topics Target topic name(s) whose `<topic>.delayed` staging topics to relay.\n * @param options Optional `groupId` override (default: `<defaultGroupId>-delayed-relay`).\n *\n * @example\n * ```ts\n * await kafka.startDelayedRelay(['orders.reminder']);\n * await kafka.sendMessage('orders.reminder', payload, { deliverAfterMs: 60_000 });\n * // → delivered to orders.reminder ~60 s later\n * ```\n */\n public async startDelayedRelay(\n topics: (keyof T & string) | Array<keyof T & string>,\n options?: { groupId?: string },\n ): Promise<ConsumerHandle> {\n const list = (Array.isArray(topics) ? topics : [topics]) as string[];\n return startDelayedRelayImpl(this.ctx, list, options);\n }\n\n // ── Consumer: transactional EOS ───────────────────────────────────\n\n /** @inheritDoc */\n public async startTransactionalConsumer<K extends Array<keyof T>>(\n topics: K,\n handler: (\n envelope: EventEnvelope<T[K[number]]>,\n tx: TransactionalHandlerContext<T>,\n ) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<ConsumerHandle> {\n return startTransactionalConsumerImpl(this.ctx, topics, handler, options);\n }\n\n // ── Consumer lifecycle ────────────────────────────────────────────\n\n /** @inheritDoc */\n public async stopConsumer(groupId?: string): Promise<void> {\n return stopConsumerImpl(this.ctx, groupId);\n }\n\n /** @inheritDoc */\n public pauseConsumer(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n ): void {\n return pauseConsumerImpl(this.ctx, groupId, assignments);\n }\n\n /** @inheritDoc */\n public resumeConsumer(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partitions: number[] }>,\n ): void {\n return resumeConsumerImpl(this.ctx, groupId, assignments);\n }\n\n // ── DLQ replay ────────────────────────────────────────────────────\n\n /** @inheritDoc */\n public async replayDlq(\n topic: string,\n options: DlqReplayOptions = {},\n ): Promise<{ replayed: number; skipped: number }> {\n await this.ctx.adminOps.ensureConnected();\n return replayDlqTopic(\n topic,\n {\n logger: this.ctx.logger,\n fetchTopicOffsets: (t) => this.ctx.adminOps.admin.fetchTopicOffsets(t),\n send: async (t, messages) => {\n await this.ctx.producer.send({ topic: t, messages });\n },\n createConsumer: (gid, fromBeginning) =>\n getOrCreateConsumer(gid, fromBeginning, true, this.ctx.consumerOpsDeps),\n cleanupConsumer: (consumer, gid, deleteGroup) => {\n consumer\n .disconnect()\n .catch(() => {})\n .finally(() => {\n this.ctx.consumers.delete(gid);\n this.ctx.runningConsumers.delete(gid);\n this.ctx.consumerCreationOptions.delete(gid);\n if (deleteGroup) {\n this.ctx.adminOps.deleteGroups([gid]).catch(() => {});\n }\n });\n },\n dlqHeaderKeys: DLQ_HEADER_KEYS,\n },\n options,\n );\n }\n\n // ── Snapshot & checkpoint ─────────────────────────────────────────\n\n /** @inheritDoc */\n public async readSnapshot<K extends keyof T & string>(\n topic: K,\n options: ReadSnapshotOptions = {},\n ): Promise<Map<string, EventEnvelope<T[K]>>> {\n return readSnapshotImpl(this.ctx, topic, options);\n }\n\n /** @inheritDoc */\n public async checkpointOffsets(\n groupId: string | undefined,\n checkpointTopic: string,\n ): Promise<CheckpointResult> {\n return checkpointOffsetsImpl(this.ctx, groupId, checkpointTopic);\n }\n\n /** @inheritDoc */\n public async restoreFromCheckpoint(\n groupId: string | undefined,\n checkpointTopic: string,\n options: RestoreCheckpointOptions = {},\n ): Promise<CheckpointRestoreResult> {\n return restoreFromCheckpointImpl(this.ctx, groupId, checkpointTopic, options);\n }\n\n // ── Admin ─────────────────────────────────────────────────────────\n\n /** @inheritDoc */\n public async resetOffsets(\n groupId: string | undefined,\n topic: string,\n position: \"earliest\" | \"latest\",\n ): Promise<void> {\n return this.ctx.adminOps.resetOffsets(groupId, topic, position);\n }\n\n /** @inheritDoc */\n public async seekToOffset(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partition: number; offset: string }>,\n ): Promise<void> {\n return this.ctx.adminOps.seekToOffset(groupId, assignments);\n }\n\n /** @inheritDoc */\n public async seekToTimestamp(\n groupId: string | undefined,\n assignments: Array<{ topic: string; partition: number; timestamp: number }>,\n ): Promise<void> {\n return this.ctx.adminOps.seekToTimestamp(groupId, assignments);\n }\n\n /** @inheritDoc */\n public async getConsumerLag(\n groupId?: string,\n ): Promise<Array<{ topic: string; partition: number; lag: number }>> {\n return this.ctx.adminOps.getConsumerLag(groupId);\n }\n\n /** @inheritDoc */\n public async checkStatus(): Promise<import(\"../types\").KafkaHealthResult> {\n return this.ctx.adminOps.checkStatus();\n }\n\n /** @inheritDoc */\n public async listConsumerGroups(): Promise<ConsumerGroupSummary[]> {\n return this.ctx.adminOps.listConsumerGroups();\n }\n\n /** @inheritDoc */\n public async describeTopics(topics?: string[]): Promise<TopicDescription[]> {\n return this.ctx.adminOps.describeTopics(topics);\n }\n\n /** @inheritDoc */\n public async deleteRecords(\n topic: string,\n partitions: Array<{ partition: number; offset: string }>,\n ): Promise<void> {\n return this.ctx.adminOps.deleteRecords(topic, partitions);\n }\n\n // ── Circuit breaker ───────────────────────────────────────────────\n\n /** @inheritDoc */\n public getCircuitState(\n topic: string,\n partition: number,\n groupId?: string,\n ):\n | { status: \"closed\" | \"open\" | \"half-open\"; failures: number; windowSize: number }\n | undefined {\n return this.ctx.circuitBreaker.getState(\n topic,\n partition,\n groupId ?? this.ctx.defaultGroupId,\n );\n }\n\n // ── Metrics ───────────────────────────────────────────────────────\n\n /** @inheritDoc */\n public getMetrics(topic?: string): Readonly<import(\"../types\").KafkaMetrics> {\n return this.ctx.metrics.getMetrics(topic);\n }\n\n /** @inheritDoc */\n public resetMetrics(topic?: string): void {\n this.ctx.metrics.resetMetrics(topic);\n }\n\n public getClientId(): ClientId {\n return this.clientId;\n }\n\n // ── Lifecycle ─────────────────────────────────────────────────────\n\n /** @inheritDoc */\n public async disconnect(drainTimeoutMs = 30_000): Promise<void> {\n return disconnectImpl(this.ctx, drainTimeoutMs);\n }\n\n /** NestJS lifecycle hook — called automatically on module teardown. */\n public async onModuleDestroy(): Promise<void> {\n await this.disconnect();\n }\n\n /** @inheritDoc */\n public enableGracefulShutdown(\n signals: NodeJS.Signals[] = [\"SIGTERM\", \"SIGINT\"],\n drainTimeoutMs = 30_000,\n ): void {\n const handler = () => {\n this.ctx.logger.log(\n \"Shutdown signal received — draining in-flight handlers...\",\n );\n this.disconnect(drainTimeoutMs)\n .catch((err) =>\n this.ctx.logger.error(\n \"Error during graceful shutdown:\",\n toError(err).message,\n ),\n )\n .finally(() => process.exit(0));\n };\n for (const signal of signals) process.once(signal, handler);\n }\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 * Partition-key extractor. Present only when created via `.key()`.\n * Applied on every send through this descriptor unless an explicit\n * `key` is passed in `SendOptions` / the batch item.\n *\n * Declared with method syntax (not a function property) so `M` stays\n * bivariant — otherwise narrow descriptors would stop being assignable\n * to `TopicDescriptor<string, Record<string, any>>` parameters.\n */\n __key?(message: M): string;\n}\n\n/**\n * A `TopicDescriptor` that can still be extended with a `.key()` extractor.\n * Returned by `topic().type()` and `topic().schema()` — usable directly as a\n * descriptor, or chained once more to declare partition affinity.\n */\nexport type KeyableTopicDescriptor<\n N extends string,\n M extends Record<string, any>,\n> = TopicDescriptor<N, M> & {\n /**\n * Declare a partition-key extractor for this topic. The extractor runs on\n * the ORIGINAL (pre-validation) payload of every message sent through this\n * descriptor, so messages with the same logical key always land on the same\n * partition without passing `key` at each call site.\n *\n * An explicit `SendOptions.key` / batch-item `key` always wins.\n *\n * @example\n * ```ts\n * const OrderCreated = topic('order.created')\n * .type<{ orderId: string; amount: number }>()\n * .key((m) => m.orderId);\n *\n * await kafka.sendMessage(OrderCreated, { orderId: '42', amount: 100 });\n * // → produced with key '42'\n * ```\n */\n key(extractor: (message: M) => string): TopicDescriptor<N, 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>>(): KeyableTopicDescriptor<N, M> =>\n keyable({\n __topic: name,\n __type: undefined as unknown as M,\n }),\n\n schema: <S extends SchemaLike<Record<string, any>>>(\n schema: S,\n ): KeyableTopicDescriptor<N, InferSchema<S>> =>\n keyable({\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/** Attach the chainable `.key()` builder to a plain descriptor. */\nfunction keyable<N extends string, M extends Record<string, any>>(\n desc: TopicDescriptor<N, M>,\n): KeyableTopicDescriptor<N, M> {\n return {\n ...desc,\n key: (extractor: (message: M) => string): TopicDescriptor<N, M> => ({\n ...desc,\n __key: extractor,\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","import type { SchemaLike, SchemaParseContext } from \"./topic\";\n\n/**\n * Options for `versionedSchema()`.\n * @typeParam T - The (latest) output type produced after parsing and migration.\n */\nexport interface VersionedSchemaOptions<T> {\n /**\n * Called after a message parsed with a non-latest schema version.\n * Receives the parsed data, the version it was parsed with, and the latest\n * registered version. Must return the data in its latest shape.\n *\n * When omitted, older versions are returned as parsed — callers must handle\n * shape differences themselves.\n */\n migrate?: (\n data: any,\n fromVersion: number,\n latestVersion: number,\n ) => T | Promise<T>;\n}\n\n/**\n * Compose per-version validators into a single `SchemaLike` that dispatches on\n * the message's `x-schema-version` header (via `SchemaParseContext.version`).\n *\n * - Consume path: the version comes from the `x-schema-version` header\n * (defaults to `1` when absent).\n * - Send path: the version comes from `SendOptions.schemaVersion`\n * (defaults to `1`).\n * - No parse context at all (direct `.parse(data)` call): the latest\n * registered version is assumed.\n *\n * Throws when a message carries a version with no registered schema — a\n * misconfigured producer fails loudly instead of validating against the\n * wrong shape.\n *\n * @typeParam T - The latest message shape (post-migration).\n * @param versions Map of version number → validator for that version.\n * @param options Optional migration hook to upgrade old shapes to the latest.\n *\n * @example\n * ```ts\n * const OrderSchema = versionedSchema<{ orderId: string; amountMinor: number }>(\n * {\n * 1: z.object({ orderId: z.string(), amount: z.number() }),\n * 2: z.object({ orderId: z.string(), amountMinor: z.number().int() }),\n * },\n * {\n * migrate: (data, from) =>\n * from === 1 ? { orderId: data.orderId, amountMinor: Math.round(data.amount * 100) } : data,\n * },\n * );\n *\n * const OrderCreated = topic('order.created').schema(OrderSchema);\n * ```\n */\nexport function versionedSchema<T = any>(\n versions: Record<number, SchemaLike<any>>,\n options?: VersionedSchemaOptions<T>,\n): SchemaLike<T> {\n const registered = Object.keys(versions)\n .map(Number)\n .filter((v) => Number.isInteger(v) && v > 0)\n .sort((a, b) => a - b);\n if (registered.length === 0) {\n throw new Error(\n \"versionedSchema: at least one schema version must be registered (keys must be positive integers)\",\n );\n }\n const latestVersion = registered[registered.length - 1];\n\n return {\n async parse(data: unknown, ctx?: SchemaParseContext): Promise<T> {\n const version = ctx?.version ?? latestVersion;\n const schema = versions[version];\n if (!schema) {\n throw new Error(\n `versionedSchema: no schema registered for version ${version}` +\n `${ctx?.topic ? ` (topic \"${ctx.topic}\")` : \"\"} — registered versions: ${registered.join(\", \")}`,\n );\n }\n const parsed = await schema.parse(data, ctx);\n if (version < latestVersion && options?.migrate) {\n return options.migrate(parsed, version, latestVersion);\n }\n return parsed as T;\n },\n };\n}\n","import type { SchemaLike, SchemaParseContext } from \"./topic\";\n\n/** A schema registered in a Confluent-compatible Schema Registry. */\nexport interface RegisteredSchema {\n /** Globally unique schema id assigned by the registry. */\n id: number;\n /** Version of the schema within its subject. */\n version: number;\n /** The schema definition string (JSON Schema / Avro / Protobuf source). */\n schema: string;\n}\n\n/** Options for `SchemaRegistryClient`. */\nexport interface SchemaRegistryClientOptions {\n /** Registry base URL, e.g. `http://localhost:8081` or a Confluent Cloud SR endpoint. */\n baseUrl: string;\n /** HTTP Basic credentials (Confluent Cloud SR API key/secret). */\n auth?: { username: string; password: string };\n /** Cache TTL for subject lookups in ms. Default: `300_000` (5 min). */\n cacheTtlMs?: number;\n /** Injectable fetch implementation (tests). Default: global `fetch`. */\n fetchFn?: typeof fetch;\n}\n\n/** Schema type accepted by Confluent-compatible registries. */\nexport type RegistrySchemaType = \"JSON\" | \"AVRO\" | \"PROTOBUF\";\n\n/**\n * Minimal, dependency-free client for the Confluent Schema Registry REST API\n * (works with Confluent Platform/Cloud, Redpanda, Karapace, AWS Glue SR proxy).\n *\n * Scope: subject/version management and compatibility checks — the pieces\n * needed to keep locally-defined schemas in lockstep with a central registry.\n * Payload (de)serialisation stays JSON as everywhere in this library; wire-format\n * framing with magic bytes (Avro/Protobuf binary) is intentionally out of scope.\n *\n * @example\n * ```ts\n * const registry = new SchemaRegistryClient({ baseUrl: 'http://localhost:8081' });\n * const { id } = await registry.registerSchema(\n * 'order.created-value',\n * JSON.stringify(orderJsonSchema),\n * 'JSON',\n * );\n * ```\n */\nexport class SchemaRegistryClient {\n private readonly fetchFn: typeof fetch;\n private readonly cacheTtlMs: number;\n private readonly latestCache = new Map<\n string,\n { value: RegisteredSchema; expiresAt: number }\n >();\n\n constructor(private readonly options: SchemaRegistryClientOptions) {\n if (!options.baseUrl) {\n throw new Error(\"SchemaRegistryClient: baseUrl is required\");\n }\n this.fetchFn = options.fetchFn ?? fetch;\n this.cacheTtlMs = options.cacheTtlMs ?? 300_000;\n }\n\n private headers(): Record<string, string> {\n const h: Record<string, string> = {\n \"Content-Type\": \"application/vnd.schemaregistry.v1+json\",\n };\n if (this.options.auth) {\n const { username, password } = this.options.auth;\n h[\"Authorization\"] =\n \"Basic \" + Buffer.from(`${username}:${password}`).toString(\"base64\");\n }\n return h;\n }\n\n private async request<R>(\n method: \"GET\" | \"POST\",\n path: string,\n body?: unknown,\n ): Promise<R> {\n const url = `${this.options.baseUrl.replace(/\\/$/, \"\")}${path}`;\n const res = await this.fetchFn(url, {\n method,\n headers: this.headers(),\n ...(body !== undefined && { body: JSON.stringify(body) }),\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(\n `SchemaRegistry ${method} ${path} failed: ${res.status} ${res.statusText}${text ? ` — ${text}` : \"\"}`,\n );\n }\n return (await res.json()) as R;\n }\n\n /** Fetch the latest schema registered under `subject`. Cached for `cacheTtlMs`. */\n async getLatestSchema(subject: string): Promise<RegisteredSchema> {\n const cached = this.latestCache.get(subject);\n if (cached && cached.expiresAt > Date.now()) return cached.value;\n const raw = await this.request<{\n id: number;\n version: number;\n schema: string;\n }>(\"GET\", `/subjects/${encodeURIComponent(subject)}/versions/latest`);\n const value: RegisteredSchema = {\n id: raw.id,\n version: raw.version,\n schema: raw.schema,\n };\n this.latestCache.set(subject, {\n value,\n expiresAt: Date.now() + this.cacheTtlMs,\n });\n return value;\n }\n\n /** Fetch a specific schema version of a subject. */\n async getSchemaVersion(\n subject: string,\n version: number,\n ): Promise<RegisteredSchema> {\n const raw = await this.request<{\n id: number;\n version: number;\n schema: string;\n }>(\n \"GET\",\n `/subjects/${encodeURIComponent(subject)}/versions/${version}`,\n );\n return { id: raw.id, version: raw.version, schema: raw.schema };\n }\n\n /**\n * Register a schema under `subject` (idempotent — re-registering the same\n * schema returns the existing id). Returns the registry-assigned schema id.\n */\n async registerSchema(\n subject: string,\n schema: string,\n schemaType: RegistrySchemaType = \"JSON\",\n ): Promise<{ id: number }> {\n this.latestCache.delete(subject);\n return this.request<{ id: number }>(\n \"POST\",\n `/subjects/${encodeURIComponent(subject)}/versions`,\n { schema, schemaType },\n );\n }\n\n /**\n * Test `schema` against the subject's compatibility policy without registering.\n * Returns `true` when the registry reports the schema as compatible.\n */\n async checkCompatibility(\n subject: string,\n schema: string,\n schemaType: RegistrySchemaType = \"JSON\",\n ): Promise<boolean> {\n const res = await this.request<{ is_compatible: boolean }>(\n \"POST\",\n `/compatibility/subjects/${encodeURIComponent(subject)}/versions/latest`,\n { schema, schemaType },\n );\n return res.is_compatible;\n }\n}\n\n/** Options for `registrySchema()`. */\nexport interface RegistrySchemaOptions<T> {\n /**\n * Local structural validator (Zod/Valibot/…) applied to every message.\n * The registry governs schema *evolution*; this governs runtime *shape*.\n */\n validator?: SchemaLike<T>;\n /**\n * When `true` (default), the message's `x-schema-version` must not be newer\n * than the latest version registered for the subject — a producer publishing\n * an unregistered version fails loudly instead of drifting silently.\n */\n enforceVersion?: boolean;\n}\n\n/**\n * Bridge a Schema Registry subject to this library's `SchemaLike` seam.\n *\n * On each `parse` the adapter resolves the subject's latest registered version\n * (cached), optionally verifies the message's schema version does not exceed\n * it, and delegates structural validation to the provided local validator.\n * Attach the result to a `TopicDescriptor` like any other schema:\n *\n * @example\n * ```ts\n * const registry = new SchemaRegistryClient({ baseUrl: 'http://localhost:8081' });\n *\n * const OrderCreated = topic('order.created').schema(\n * registrySchema(registry, 'order.created-value', {\n * validator: z.object({ orderId: z.string() }),\n * }),\n * );\n * ```\n */\nexport function registrySchema<T = any>(\n client: SchemaRegistryClient,\n subject: string,\n options?: RegistrySchemaOptions<T>,\n): SchemaLike<T> {\n const enforceVersion = options?.enforceVersion ?? true;\n return {\n async parse(data: unknown, ctx?: SchemaParseContext): Promise<T> {\n const latest = await client.getLatestSchema(subject);\n if (enforceVersion && ctx?.version !== undefined && ctx.version > latest.version) {\n throw new Error(\n `registrySchema: message version ${ctx.version} for subject \"${subject}\" ` +\n `is newer than the latest registered version ${latest.version} — ` +\n `register the schema before producing with it`,\n );\n }\n if (options?.validator) {\n return options.validator.parse(data, ctx);\n }\n return data as T;\n },\n };\n}\n","import type { OutboxMessage, OutboxStore } from \"./outbox.types\";\n\n/**\n * In-memory reference implementation of {@link OutboxStore}.\n *\n * Intended for tests and as executable documentation of the expected\n * {@link OutboxStore} semantics — **not** for production use: it provides no\n * durability, so a process restart loses all pending rows and the \"same DB\n * transaction as the business write\" guarantee (the whole point of the outbox\n * pattern) does not hold. Back a real relay with a persistent store (Postgres,\n * MySQL, …) implementing the same interface.\n *\n * Semantics mirrored:\n * - {@link fetchUnpublished} returns unpublished rows **oldest first** (insertion\n * order), capped at `limit`.\n * - {@link markPublished} is idempotent — unknown ids are ignored.\n *\n * @example\n * ```ts\n * const store = new InMemoryOutboxStore();\n * store.add({ id: '1', topic: 'orders.created', payload: { orderId: 'o1' } });\n *\n * const handle = startOutboxRelay(kafka, store);\n * // ... later\n * await handle.stop();\n * ```\n */\nexport class InMemoryOutboxStore implements OutboxStore {\n /** Insertion-ordered rows. `published` flips to true after `markPublished`. */\n private readonly rows: Array<{ message: OutboxMessage; published: boolean }> =\n [];\n\n /**\n * Append a message to the outbox. In a real store this INSERT would run inside\n * the same DB transaction as the corresponding business write.\n */\n add(message: OutboxMessage): void {\n this.rows.push({ message, published: false });\n }\n\n async fetchUnpublished(limit: number): Promise<OutboxMessage[]> {\n const out: OutboxMessage[] = [];\n for (const row of this.rows) {\n if (row.published) continue;\n out.push(row.message);\n if (out.length >= limit) break;\n }\n return out;\n }\n\n async markPublished(ids: string[]): Promise<void> {\n const idSet = new Set(ids);\n for (const row of this.rows) {\n if (idSet.has(row.message.id)) row.published = true;\n }\n }\n\n /** Test helper: count of rows not yet marked published. */\n get pendingCount(): number {\n return this.rows.filter((r) => !r.published).length;\n }\n\n /** Test helper: count of rows marked published. */\n get publishedCount(): number {\n return this.rows.filter((r) => r.published).length;\n }\n}\n","import type { TopicMapConstraint } from \"../types\";\nimport type {\n OutboxMessage,\n OutboxProducer,\n OutboxRelayHandle,\n OutboxRelayOptions,\n OutboxStore,\n} from \"./outbox.types\";\n\n/** Coerce an unknown thrown value into an `Error`. */\nfunction toError(e: unknown): Error {\n return e instanceof Error ? e : new Error(String(e));\n}\n\n/**\n * Start a transactional-outbox relay.\n *\n * The outbox pattern decouples \"write my business state\" from \"publish an event\"\n * so the two can never diverge: application code writes an event row into an\n * outbox table **in the same DB transaction** as its business writes; this relay\n * polls that table and publishes the rows to Kafka, marking them published only\n * after Kafka has acked them.\n *\n * ## Poll loop\n * Every `pollIntervalMs` the relay calls {@link OutboxStore.fetchUnpublished}\n * with `batchSize`. An empty result means there is nothing to do — it waits for\n * the next tick. A non-empty batch is published **entirely inside one Kafka\n * transaction** (`kafka.transaction`, one `tx.send` per message), and only after\n * that transaction commits does it call {@link OutboxStore.markPublished} with\n * the row ids.\n *\n * ## Delivery guarantee: at-least-once\n * If the process crashes **after** the Kafka transaction commits but **before**\n * `markPublished` runs, those rows are still unpublished in the store and will be\n * re-published on the next poll. This produces **duplicates**, but each duplicate\n * carries the **same** `x-event-id` (when {@link OutboxMessage.eventId} is set on\n * the row), so consumers can deduplicate — either via this library's Lamport-clock\n * deduplication or an application-level idempotency check keyed on the event id.\n * Persist a stable `eventId` on each outbox row to make this work.\n *\n * ## Failure handling\n * The loop never dies and never leaks an unhandled rejection:\n * - If the publishing transaction throws, `onError` fires, the batch is **not**\n * marked published, and it is retried on the next tick.\n * - If the store throws (either `fetchUnpublished` or `markPublished`), same\n * behaviour: `onError`, no marking, retried next tick.\n *\n * Note the one asymmetry: if `markPublished` throws *after* a successful Kafka\n * commit, the batch is re-published next tick — an intentional consequence of\n * the at-least-once guarantee above.\n *\n * ## Concurrency\n * Iterations never overlap. If an iteration runs longer than `pollIntervalMs`,\n * intervening ticks are skipped (a guard flag) rather than stacked.\n *\n * ## Shutdown\n * `handle.stop()` clears the timer and awaits any in-flight iteration before\n * resolving, so no publish is left half-done. It is idempotent.\n *\n * @param kafka Any producer exposing `transaction()` — a `KafkaClient<T>` or\n * `IKafkaProducer<T>`. Its producer must already be connected.\n * @param store Your DB-backed {@link OutboxStore} implementation.\n * @param options Tuning and observability — see {@link OutboxRelayOptions}.\n *\n * @example\n * ```ts\n * // Pseudo-Postgres outbox store.\n * const store: OutboxStore = {\n * async fetchUnpublished(limit) {\n * const { rows } = await pool.query(\n * `SELECT id, topic, payload, key, correlation_id AS \"correlationId\",\n * event_id AS \"eventId\", headers\n * FROM outbox\n * WHERE published_at IS NULL\n * ORDER BY created_at ASC\n * LIMIT $1`,\n * [limit],\n * );\n * return rows;\n * },\n * async markPublished(ids) {\n * await pool.query(\n * `UPDATE outbox SET published_at = now() WHERE id = ANY($1)`,\n * [ids],\n * );\n * },\n * };\n *\n * await kafka.connectProducer();\n * const relay = startOutboxRelay(kafka, store, {\n * pollIntervalMs: 500,\n * batchSize: 200,\n * onPublished: (n) => metrics.increment('outbox.published', n),\n * });\n *\n * // On shutdown:\n * await relay.stop();\n * await kafka.disconnect();\n * ```\n */\nexport function startOutboxRelay<T extends TopicMapConstraint<T>>(\n kafka: OutboxProducer<T>,\n store: OutboxStore,\n options: OutboxRelayOptions = {},\n): OutboxRelayHandle {\n const pollIntervalMs = options.pollIntervalMs ?? 1_000;\n const batchSize = options.batchSize ?? 100;\n const onError =\n options.onError ??\n ((error: Error, batch: OutboxMessage[]) => {\n // Default: log to stderr and keep the relay running.\n\n console.error(\n `[outbox] batch of ${batch.length} message(s) failed — will retry:`,\n error,\n );\n });\n const onPublished = options.onPublished;\n\n let stopped = false;\n /** True while an iteration is running — guards against overlapping ticks. */\n let running = false;\n /** The in-flight iteration promise, awaited by `stop()`. */\n let inFlight: Promise<void> = Promise.resolve();\n\n /**\n * One poll iteration. Fully self-contained: it swallows every error into\n * `onError` so it can never reject and never leak an unhandled rejection.\n */\n const iterate = async (): Promise<void> => {\n let batch: OutboxMessage[] = [];\n try {\n batch = await store.fetchUnpublished(batchSize);\n if (batch.length === 0) return;\n\n // Publish the whole batch atomically. Either every message lands or none.\n await kafka.transaction(async (tx) => {\n for (const msg of batch) {\n await tx.send(msg.topic as keyof T, msg.payload as T[keyof T], {\n key: msg.key,\n headers: msg.headers,\n correlationId: msg.correlationId,\n eventId: msg.eventId,\n });\n }\n });\n\n // Transaction committed — mark published. A crash before this line\n // re-publishes the batch next tick (at-least-once, same eventId).\n await store.markPublished(batch.map((m) => m.id));\n\n onPublished?.(batch.length);\n } catch (err) {\n onError(toError(err), batch);\n }\n };\n\n const tick = (): void => {\n if (stopped || running) return; // skip stacked ticks\n running = true;\n inFlight = iterate().finally(() => {\n running = false;\n });\n };\n\n const timer = setInterval(tick, pollIntervalMs);\n timer.unref?.();\n\n return {\n stop: async (): Promise<void> => {\n stopped = true;\n clearInterval(timer);\n // Await whatever iteration is currently in flight (no-op if idle).\n await inFlight;\n },\n };\n}\n","import type { OAuthBearerProvider, OAuthBearerToken } from \"./security.types\";\n\n/** Injectable dynamic-import function — overridable in tests. */\ntype ImportFn = (specifier: string) => Promise<any>;\n\nconst defaultImport: ImportFn = (specifier) => import(specifier);\n\n/** Options for {@link awsMskIamProvider}. */\nexport interface AwsMskIamProviderOptions {\n /** AWS region of the MSK cluster, e.g. `\"eu-west-1\"`. */\n region: string;\n /**\n * Optional AWS credentials profile / role logic is resolved by the signer\n * library from the standard provider chain (env vars, ECS/EKS metadata,\n * shared config). Nothing to configure here for IRSA / task roles.\n */\n /** @internal Injectable import for tests. */\n importFn?: ImportFn;\n}\n\n/**\n * OAUTHBEARER token provider for **AWS MSK IAM authentication**.\n *\n * Delegates token signing to the official `aws-msk-iam-sasl-signer-js`\n * package (an optional dependency — install it alongside this library):\n *\n * ```bash\n * npm install aws-msk-iam-sasl-signer-js\n * ```\n *\n * Credentials are resolved from the standard AWS provider chain, so EKS\n * IRSA, ECS task roles, and env credentials all work with no extra config.\n * Authorisation is then governed by IAM policies (`kafka-cluster:*` actions) —\n * see `describeRequiredAcls()` + `toMskIamPolicy()` to generate one covering\n * every topic/group this library derives.\n *\n * @example\n * ```ts\n * const kafka = new KafkaClient(id, group, brokers, {\n * security: {\n * sasl: {\n * mechanism: 'oauthbearer',\n * oauthBearerProvider: awsMskIamProvider({ region: 'eu-west-1' }),\n * },\n * },\n * });\n * ```\n */\nexport function awsMskIamProvider(\n options: AwsMskIamProviderOptions,\n): OAuthBearerProvider {\n const importFn = options.importFn ?? defaultImport;\n return async (): Promise<OAuthBearerToken> => {\n let signer: any;\n try {\n signer = await importFn(\"aws-msk-iam-sasl-signer-js\");\n } catch {\n throw new Error(\n \"awsMskIamProvider: package 'aws-msk-iam-sasl-signer-js' is not installed. \" +\n \"Run `npm install aws-msk-iam-sasl-signer-js` to enable MSK IAM authentication.\",\n );\n }\n const { token, expiryTime } = await signer.generateAuthToken({\n region: options.region,\n });\n return {\n value: token,\n principal: \"msk-iam\",\n // expiryTime is epoch ms per the signer's contract\n lifetimeMs: expiryTime,\n };\n };\n}\n\n/** Options for {@link gcpAccessTokenProvider}. */\nexport interface GcpTokenProviderOptions {\n /** OAuth scopes. Default: `[\"https://www.googleapis.com/auth/cloud-platform\"]`. */\n scopes?: string[];\n /** Principal reported to the broker. Default: `\"gcp\"`. */\n principal?: string;\n /**\n * Token validity reported to the driver (refresh margin), epoch-relative ms.\n * GCP access tokens live ~1 h; default refresh window is 50 minutes.\n */\n tokenTtlMs?: number;\n /** @internal Injectable import for tests. */\n importFn?: ImportFn;\n}\n\n/**\n * OAUTHBEARER token provider for **Google Cloud Managed Service for Apache\n * Kafka** (and any broker accepting Google OAuth access tokens).\n *\n * Delegates to the official `google-auth-library` (optional dependency):\n *\n * ```bash\n * npm install google-auth-library\n * ```\n *\n * Uses Application Default Credentials, so GKE Workload Identity, attached\n * service accounts, and `GOOGLE_APPLICATION_CREDENTIALS` all work unchanged.\n * Verify the exact token format expected by your cluster against current\n * Google documentation — this provider supplies a raw ADC access token.\n *\n * @example\n * ```ts\n * const kafka = new KafkaClient(id, group, brokers, {\n * security: {\n * sasl: {\n * mechanism: 'oauthbearer',\n * oauthBearerProvider: gcpAccessTokenProvider(),\n * },\n * },\n * });\n * ```\n */\nexport function gcpAccessTokenProvider(\n options: GcpTokenProviderOptions = {},\n): OAuthBearerProvider {\n const importFn = options.importFn ?? defaultImport;\n const ttlMs = options.tokenTtlMs ?? 50 * 60_000;\n return async (): Promise<OAuthBearerToken> => {\n let lib: any;\n try {\n lib = await importFn(\"google-auth-library\");\n } catch {\n throw new Error(\n \"gcpAccessTokenProvider: package 'google-auth-library' is not installed. \" +\n \"Run `npm install google-auth-library` to enable GCP authentication.\",\n );\n }\n const auth = new lib.GoogleAuth({\n scopes: options.scopes ?? [\"https://www.googleapis.com/auth/cloud-platform\"],\n });\n const token = await auth.getAccessToken();\n if (!token) {\n throw new Error(\n \"gcpAccessTokenProvider: google-auth-library returned no access token — \" +\n \"check Application Default Credentials.\",\n );\n }\n return {\n value: token,\n principal: options.principal ?? \"gcp\",\n lifetimeMs: Date.now() + ttlMs,\n };\n };\n}\n","/**\n * ACL requirement calculator for the topics, consumer groups, and\n * transactional ids this library derives from your configuration.\n *\n * Features like retry topics, DLQ, delayed delivery, deduplication routing,\n * DLQ replay, snapshots, and clock recovery all create extra topics\n * (`<topic>.retry.N`, `<topic>.dlq`, `<topic>.delayed`, `<topic>.duplicates`)\n * and consumer groups (`<groupId>-retry.N`, `<groupId>-delayed-relay`, plus\n * timestamped ephemeral groups). On a locked-down cluster every one of them\n * needs an ACL — `describeRequiredAcls()` enumerates them so nothing is\n * discovered in production at 3 a.m.\n */\n\n/** One ACL requirement on a Kafka resource. */\nexport interface AclResource {\n resourceType: \"topic\" | \"group\" | \"transactional-id\" | \"cluster\";\n /**\n * `literal` — exact name match. `prefixed` — Kafka prefixed ACL pattern;\n * required for the timestamped ephemeral groups this library creates.\n */\n patternType: \"literal\" | \"prefixed\";\n name: string;\n /** Kafka operations: READ, WRITE, DESCRIBE, CREATE, DELETE. */\n operations: string[];\n /** Which feature requires this resource. */\n reason: string;\n}\n\n/** Input describing how a service uses its `KafkaClient`. */\nexport interface AclRequirementsInput {\n clientId: string;\n /** Default/typical consumer group ids used by this service. */\n groupIds?: string[];\n /** Topics this service produces to. */\n produceTopics?: string[];\n /** Topics this service consumes from. */\n consumeTopics?: string[];\n features?: {\n /** `retryTopics: true` — adds `.retry.N` topics, `-retry.N` groups, and per-level tx ids. */\n retryTopics?: { maxRetries: number };\n /** `dlq: true` — adds `.dlq` produce rights on consumed topics. */\n dlq?: boolean;\n /** `deliverAfterMs` / `startDelayedRelay` — adds `.delayed` topics, relay group, relay tx id. */\n delayedDelivery?: boolean;\n /** `deduplication.strategy: 'topic'` — adds `.duplicates` produce rights (or the custom topic). */\n duplicatesTopic?: boolean | string;\n /** `replayDlq()` usage — adds prefixed ephemeral replay groups + group deletion. */\n dlqReplay?: boolean;\n /** `readSnapshot` / `checkpointOffsets` / `restoreFromCheckpoint` — prefixed ephemeral groups. */\n snapshots?: boolean;\n /** `clockRecovery.topics` configured — prefixed ephemeral recovery groups. */\n clockRecovery?: boolean;\n /** `transaction()` usage — base transactional id. */\n transactions?: boolean;\n /** `autoCreateTopics: true` — cluster CREATE (avoid in production). */\n autoCreateTopics?: boolean;\n };\n}\n\nfunction addResource(\n out: Map<string, AclResource>,\n r: AclResource,\n): void {\n const key = `${r.resourceType}:${r.patternType}:${r.name}`;\n const existing = out.get(key);\n if (existing) {\n for (const op of r.operations)\n if (!existing.operations.includes(op)) existing.operations.push(op);\n if (!existing.reason.includes(r.reason))\n existing.reason += `; ${r.reason}`;\n } else {\n out.set(key, { ...r, operations: [...r.operations] });\n }\n}\n\n/**\n * Enumerate every Kafka resource (with required operations) a service\n * principal needs for the given usage profile — including all derived\n * topics, companion groups, ephemeral groups, and transactional ids.\n *\n * @example\n * ```ts\n * const acls = describeRequiredAcls({\n * clientId: 'billing-svc',\n * groupIds: ['billing-svc-group'],\n * produceTopics: ['invoices.created'],\n * consumeTopics: ['orders.created'],\n * features: { retryTopics: { maxRetries: 3 }, dlq: true, dlqReplay: true },\n * });\n * console.log(toKafkaAclCommands(acls, 'User:billing-svc'));\n * ```\n */\nexport function describeRequiredAcls(\n input: AclRequirementsInput,\n): AclResource[] {\n const out = new Map<string, AclResource>();\n const f = input.features ?? {};\n const produce = input.produceTopics ?? [];\n const consume = input.consumeTopics ?? [];\n const groups = input.groupIds ?? [];\n\n for (const t of produce) {\n addResource(out, {\n resourceType: \"topic\",\n patternType: \"literal\",\n name: t,\n operations: [\"WRITE\", \"DESCRIBE\"],\n reason: \"sendMessage/sendBatch\",\n });\n }\n\n for (const t of consume) {\n addResource(out, {\n resourceType: \"topic\",\n patternType: \"literal\",\n name: t,\n operations: [\"READ\", \"DESCRIBE\"],\n reason: \"startConsumer\",\n });\n }\n\n for (const g of groups) {\n addResource(out, {\n resourceType: \"group\",\n patternType: \"literal\",\n name: g,\n operations: [\"READ\", \"DESCRIBE\"],\n reason: \"consumer group membership + offset commits\",\n });\n }\n\n if (f.dlq) {\n for (const t of consume) {\n addResource(out, {\n resourceType: \"topic\",\n patternType: \"literal\",\n name: `${t}.dlq`,\n operations: [\"WRITE\", \"DESCRIBE\"],\n reason: \"dlq: true — failed messages routed to DLQ\",\n });\n }\n }\n\n if (f.retryTopics) {\n for (const t of consume) {\n for (let level = 1; level <= f.retryTopics.maxRetries; level++) {\n addResource(out, {\n resourceType: \"topic\",\n patternType: \"literal\",\n name: `${t}.retry.${level}`,\n operations: [\"READ\", \"WRITE\", \"DESCRIBE\"],\n reason: \"retryTopics — retry chain produce + companion consume\",\n });\n }\n }\n for (const g of groups) {\n // Covers <gid>-retry.1 … <gid>-retry.N companion groups.\n addResource(out, {\n resourceType: \"group\",\n patternType: \"prefixed\",\n name: `${g}-retry.`,\n operations: [\"READ\", \"DESCRIBE\"],\n reason: \"retryTopics — companion retry-level consumer groups\",\n });\n // Covers <gid>-main-tx and <gid>-retry.N-tx transactional producers.\n addResource(out, {\n resourceType: \"transactional-id\",\n patternType: \"prefixed\",\n name: `${g}-`,\n operations: [\"WRITE\", \"DESCRIBE\"],\n reason: \"retryTopics — EOS routing transactions per retry level\",\n });\n }\n }\n\n if (f.delayedDelivery) {\n for (const t of [...new Set([...produce, ...consume])]) {\n addResource(out, {\n resourceType: \"topic\",\n patternType: \"literal\",\n name: `${t}.delayed`,\n operations: [\"READ\", \"WRITE\", \"DESCRIBE\"],\n reason: \"deliverAfterMs staging + startDelayedRelay consume\",\n });\n }\n for (const g of groups) {\n addResource(out, {\n resourceType: \"group\",\n patternType: \"literal\",\n name: `${g}-delayed-relay`,\n operations: [\"READ\", \"DESCRIBE\"],\n reason: \"startDelayedRelay consumer group\",\n });\n addResource(out, {\n resourceType: \"transactional-id\",\n patternType: \"literal\",\n name: `${g}-delayed-relay-tx`,\n operations: [\"WRITE\", \"DESCRIBE\"],\n reason: \"startDelayedRelay transactional forwarding\",\n });\n }\n }\n\n if (f.duplicatesTopic) {\n if (typeof f.duplicatesTopic === \"string\") {\n addResource(out, {\n resourceType: \"topic\",\n patternType: \"literal\",\n name: f.duplicatesTopic,\n operations: [\"WRITE\", \"DESCRIBE\"],\n reason: \"deduplication.strategy 'topic' — custom duplicates topic\",\n });\n } else {\n for (const t of consume) {\n addResource(out, {\n resourceType: \"topic\",\n patternType: \"literal\",\n name: `${t}.duplicates`,\n operations: [\"WRITE\", \"DESCRIBE\"],\n reason: \"deduplication.strategy 'topic'\",\n });\n }\n }\n }\n\n if (f.dlqReplay) {\n for (const t of consume) {\n // <topic>.dlq-replay (stable, incremental) and <topic>.dlq-replay-<ts> (ephemeral).\n addResource(out, {\n resourceType: \"group\",\n patternType: \"prefixed\",\n name: `${t}.dlq-replay`,\n operations: [\"READ\", \"DESCRIBE\", \"DELETE\"],\n reason: \"replayDlq — ephemeral/stable replay groups (deleted after use)\",\n });\n addResource(out, {\n resourceType: \"topic\",\n patternType: \"literal\",\n name: `${t}.dlq`,\n operations: [\"READ\", \"DESCRIBE\"],\n reason: \"replayDlq — reads the DLQ\",\n });\n }\n }\n\n if (f.snapshots) {\n addResource(out, {\n resourceType: \"group\",\n patternType: \"prefixed\",\n name: `${input.clientId}-snapshot-`,\n operations: [\"READ\", \"DESCRIBE\", \"DELETE\"],\n reason: \"readSnapshot — timestamped ephemeral groups (deleted after use)\",\n });\n }\n\n if (f.clockRecovery) {\n addResource(out, {\n resourceType: \"group\",\n patternType: \"prefixed\",\n name: `${input.clientId}-clock-recovery-`,\n operations: [\"READ\", \"DESCRIBE\", \"DELETE\"],\n reason: \"clockRecovery — timestamped ephemeral groups (deleted after use)\",\n });\n }\n\n if (f.transactions) {\n addResource(out, {\n resourceType: \"transactional-id\",\n patternType: \"literal\",\n name: `${input.clientId}-tx`,\n operations: [\"WRITE\", \"DESCRIBE\"],\n reason: \"transaction() — default transactionalId (override-aware: adjust if you set one)\",\n });\n }\n\n if (f.autoCreateTopics) {\n addResource(out, {\n resourceType: \"cluster\",\n patternType: \"literal\",\n name: \"kafka-cluster\",\n operations: [\"CREATE\"],\n reason: \"autoCreateTopics: true — not recommended in production\",\n });\n }\n\n return [...out.values()];\n}\n\n/**\n * Render ACL requirements as `kafka-acls.sh` commands for a principal.\n *\n * @example\n * ```ts\n * toKafkaAclCommands(acls, 'User:billing-svc', 'broker:9092')\n * // kafka-acls.sh --bootstrap-server broker:9092 --add --allow-principal User:billing-svc \\\n * // --operation READ --operation DESCRIBE --topic orders.created\n * ```\n */\nexport function toKafkaAclCommands(\n resources: AclResource[],\n principal: string,\n bootstrapServer = \"<bootstrap-server>\",\n): string[] {\n return resources.map((r) => {\n const ops = r.operations.map((o) => `--operation ${o}`).join(\" \");\n const resourceFlag =\n r.resourceType === \"topic\"\n ? `--topic '${r.name}'`\n : r.resourceType === \"group\"\n ? `--group '${r.name}'`\n : r.resourceType === \"transactional-id\"\n ? `--transactional-id '${r.name}'`\n : \"--cluster\";\n const pattern =\n r.patternType === \"prefixed\" ? \" --resource-pattern-type prefixed\" : \"\";\n return (\n `kafka-acls.sh --bootstrap-server ${bootstrapServer} --add ` +\n `--allow-principal '${principal}' ${ops} ${resourceFlag}${pattern}` +\n ` # ${r.reason}`\n );\n });\n}\n\n/** Cluster coordinates for building MSK resource ARNs. */\nexport interface MskClusterCoordinates {\n region: string;\n accountId: string;\n clusterName: string;\n /** The UUID segment of the cluster ARN. */\n clusterUuid: string;\n}\n\nconst MSK_TOPIC_ACTIONS: Record<string, string[]> = {\n READ: [\"kafka-cluster:ReadData\", \"kafka-cluster:DescribeTopic\"],\n WRITE: [\"kafka-cluster:WriteData\", \"kafka-cluster:DescribeTopic\"],\n DESCRIBE: [\"kafka-cluster:DescribeTopic\"],\n CREATE: [\"kafka-cluster:CreateTopic\"],\n DELETE: [\"kafka-cluster:DeleteTopic\"],\n};\nconst MSK_GROUP_ACTIONS: Record<string, string[]> = {\n READ: [\"kafka-cluster:AlterGroup\", \"kafka-cluster:DescribeGroup\"],\n DESCRIBE: [\"kafka-cluster:DescribeGroup\"],\n DELETE: [\"kafka-cluster:DeleteGroup\"],\n};\nconst MSK_TX_ACTIONS: Record<string, string[]> = {\n WRITE: [\n \"kafka-cluster:AlterTransactionalId\",\n \"kafka-cluster:DescribeTransactionalId\",\n ],\n DESCRIBE: [\"kafka-cluster:DescribeTransactionalId\"],\n};\n\n/**\n * Render ACL requirements as an AWS IAM policy document for MSK IAM auth.\n *\n * Prefixed patterns become `name*` wildcards in the resource ARN. The\n * generated policy always includes `kafka-cluster:Connect` on the cluster.\n * **Review before use** — validate against current AWS documentation and\n * your organisation's least-privilege standards.\n */\nexport function toMskIamPolicy(\n resources: AclResource[],\n cluster: MskClusterCoordinates,\n): { Version: string; Statement: unknown[] } {\n const { region, accountId, clusterName, clusterUuid } = cluster;\n const arn = (type: string, name: string) =>\n `arn:aws:kafka:${region}:${accountId}:${type}/${clusterName}/${clusterUuid}/${name}`;\n\n const statements: unknown[] = [\n {\n Sid: \"Connect\",\n Effect: \"Allow\",\n Action: [\"kafka-cluster:Connect\"],\n Resource: [\n `arn:aws:kafka:${region}:${accountId}:cluster/${clusterName}/${clusterUuid}`,\n ],\n },\n ];\n\n let sid = 0;\n for (const r of resources) {\n const suffix = r.patternType === \"prefixed\" ? `${r.name}*` : r.name;\n let actions: string[] = [];\n let resource: string | undefined;\n if (r.resourceType === \"topic\") {\n actions = [...new Set(r.operations.flatMap((o) => MSK_TOPIC_ACTIONS[o] ?? []))];\n resource = arn(\"topic\", suffix);\n } else if (r.resourceType === \"group\") {\n actions = [...new Set(r.operations.flatMap((o) => MSK_GROUP_ACTIONS[o] ?? []))];\n resource = arn(\"group\", suffix);\n } else if (r.resourceType === \"transactional-id\") {\n actions = [...new Set(r.operations.flatMap((o) => MSK_TX_ACTIONS[o] ?? []))];\n resource = arn(\"transactional-id\", suffix);\n } else {\n actions = [\"kafka-cluster:CreateTopic\"];\n resource = `arn:aws:kafka:${region}:${accountId}:topic/${clusterName}/${clusterUuid}/*`;\n }\n if (actions.length === 0 || !resource) continue;\n statements.push({\n Sid: `Acl${sid++}`,\n Effect: \"Allow\",\n Action: actions,\n Resource: [resource],\n });\n }\n\n return { Version: \"2012-10-17\", Statement: statements };\n}\n","import type { TTopicMessageMap } from \"../types/common\";\nimport type { KafkaClientOptions } from \"../types/config.types\";\nimport type {\n ConsumerOptions,\n RetryOptions,\n DeduplicationOptions,\n CircuitBreakerOptions,\n SubscribeRetryOptions,\n} from \"../types/consumer.types\";\nimport type {\n KafkaSecurityOptions,\n KafkaSaslOptions,\n} from \"../security/security.types\";\n\n/**\n * Structured result of {@link kafkaClientConfigFromEnv}.\n *\n * The three positional `KafkaClient` constructor arguments (`clientId`,\n * `groupId`, `brokers`) are surfaced separately from {@link options} so callers\n * can supply their own fallbacks with `??`:\n *\n * ```ts\n * const { clientId, groupId, brokers, options } = kafkaClientConfigFromEnv();\n * const kafka = new KafkaClient(\n * clientId ?? 'my-svc',\n * groupId ?? 'my-grp',\n * brokers ?? ['localhost:9092'],\n * options,\n * );\n * ```\n *\n * Any of `clientId`, `groupId`, or `brokers` is `undefined` when its variable is\n * unset, so the caller's fallback (not an empty string / empty array) is used.\n * {@link options} only carries the keys whose variables were present — unset\n * variables never appear, so library defaults and any code-level values you spread\n * afterwards remain untouched.\n */\nexport interface EnvClientConfig {\n /** `KAFKA_CLIENT_ID` — first `KafkaClient` constructor argument. */\n clientId?: string;\n /** `KAFKA_GROUP_ID` — second `KafkaClient` constructor argument. */\n groupId?: string;\n /** `KAFKA_BROKERS` (comma-separated) — third `KafkaClient` constructor argument. */\n brokers?: string[];\n /** Partial {@link KafkaClientOptions} — only keys whose env vars were present. */\n options: KafkaClientOptions;\n}\n\n// ── Primitive parsers ─────────────────────────────────────────────────────────\n\nconst TRUE_VALUES = new Set([\"true\", \"1\", \"yes\"]);\nconst FALSE_VALUES = new Set([\"false\", \"0\", \"no\"]);\n\n/**\n * Parse a boolean env value. Accepts (case-insensitive) `true`/`false`,\n * `1`/`0`, `yes`/`no`. Anything else throws, naming the variable.\n *\n * @throws {Error} when the value is not a recognised boolean literal.\n */\nfunction parseBool(name: string, raw: string): boolean {\n const normalized = raw.trim().toLowerCase();\n if (TRUE_VALUES.has(normalized)) return true;\n if (FALSE_VALUES.has(normalized)) return false;\n throw new Error(\n `Invalid boolean for ${name}: \"${raw}\". ` +\n `Use one of true/false, 1/0, yes/no (case-insensitive).`,\n );\n}\n\n/**\n * Parse a numeric env value via `Number()`. `NaN` throws, naming the variable.\n *\n * @throws {Error} when the value does not parse to a finite number.\n */\nfunction parseNum(name: string, raw: string): number {\n const value = Number(raw.trim());\n if (Number.isNaN(value)) {\n throw new Error(`Invalid number for ${name}: \"${raw}\".`);\n }\n return value;\n}\n\n/**\n * Parse a comma-separated list. Entries are trimmed and empty entries dropped,\n * so trailing commas and stray whitespace are tolerated.\n */\nfunction parseList(raw: string): string[] {\n return raw\n .split(\",\")\n .map((part) => part.trim())\n .filter((part) => part.length > 0);\n}\n\n/**\n * Validate an env value against a fixed set of allowed literals.\n *\n * @throws {Error} when the value is not one of `allowed`.\n */\nfunction parseEnum<V extends string>(\n name: string,\n raw: string,\n allowed: readonly V[],\n): V {\n const value = raw.trim() as V;\n if (!allowed.includes(value)) {\n throw new Error(\n `Invalid value for ${name}: \"${raw}\". Allowed: ${allowed.join(\", \")}.`,\n );\n }\n return value;\n}\n\n/**\n * Read a variable and, if present and non-empty, invoke `apply` with its raw\n * value. Unset or whitespace-only variables are skipped so no key is emitted —\n * this is what preserves library defaults and code-level values.\n */\nfunction readVar(\n env: NodeJS.ProcessEnv,\n key: string,\n apply: (raw: string) => void,\n): void {\n const raw = env[key];\n if (raw === undefined || raw.trim() === \"\") return;\n apply(raw);\n}\n\n// ── Client config ───────────────────────────────────────────────────────────\n\n/**\n * Build partial `KafkaClient` constructor arguments from environment variables.\n *\n * **Precedence — read this carefully.** These helpers only *feed* the constructor\n * arguments; they never override anything. The effective precedence is:\n *\n * > **explicit code options > env vars > built-in library defaults**\n *\n * Env vars fill in what the developer did not hard-code; anything you spread\n * *after* `...options` wins over the env value, and any key absent from the\n * environment leaves the library default in place. The library never reads a\n * `.env` file itself — load one with `node --env-file=.env` (Node 20.6+) or the\n * `dotenv` package **before** constructing the client.\n *\n * ### Supported variables (default prefix `KAFKA_`)\n *\n * | Variable | Type | Maps to |\n * |---|---|---|\n * | `CLIENT_ID` | string | `clientId` (arg) |\n * | `GROUP_ID` | string | `groupId` (arg) |\n * | `BROKERS` | list | `brokers` (arg) |\n * | `AUTO_CREATE_TOPICS` | boolean | `autoCreateTopics` |\n * | `STRICT_SCHEMAS` | boolean | `strictSchemas` |\n * | `NUM_PARTITIONS` | number | `numPartitions` |\n * | `TRANSACTIONAL_ID` | string | `transactionalId` |\n * | `CLOCK_RECOVERY_TOPICS` | list | `clockRecovery.topics` |\n * | `CLOCK_RECOVERY_TIMEOUT_MS` | number | `clockRecovery.timeoutMs` |\n * | `LAG_THROTTLE_MAX_LAG` | number | `lagThrottle.maxLag` (enables the block) |\n * | `LAG_THROTTLE_GROUP_ID` | string | `lagThrottle.groupId` |\n * | `LAG_THROTTLE_POLL_INTERVAL_MS` | number | `lagThrottle.pollIntervalMs` |\n * | `LAG_THROTTLE_MAX_WAIT_MS` | number | `lagThrottle.maxWaitMs` |\n * | `SSL` | boolean | `security.ssl` |\n * | `SASL_MECHANISM` | `plain`\\|`scram-sha-256`\\|`scram-sha-512` | `security.sasl.mechanism` |\n * | `SASL_USERNAME` | string | `security.sasl.username` |\n * | `SASL_PASSWORD` | string | `security.sasl.password` |\n * | `ALLOW_INSECURE` | boolean | `security.allowInsecure` |\n *\n * `lagThrottle` is only present when `LAG_THROTTLE_MAX_LAG` is set (it is the\n * only required field). `clockRecovery` is only present when\n * `CLOCK_RECOVERY_TOPICS` is set. `security` is only present when at least one of\n * its variables is set.\n *\n * **`oauthbearer` cannot come from env.** Token providers\n * (`awsMskIamProvider`, `gcpAccessTokenProvider`, custom async factories) are\n * functions — configure them in code, not via environment variables. Only the\n * username/password SASL mechanisms are env-configurable.\n *\n * @param env Environment source. Defaults to `process.env`. Pass an explicit\n * object in tests to avoid mutating the real environment.\n * @param prefix Variable prefix. Defaults to `\"KAFKA_\"`.\n *\n * @throws {Error} when a boolean/number/enum value is malformed (the message\n * names the offending variable).\n *\n * @example Basic usage with fallbacks — code always wins / extends\n * ```ts\n * import { KafkaClient } from '@drarzter/kafka-client/core';\n * import { kafkaClientConfigFromEnv } from '@drarzter/kafka-client/core';\n *\n * const { clientId, groupId, brokers, options } = kafkaClientConfigFromEnv();\n * const kafka = new KafkaClient(\n * clientId ?? 'my-svc',\n * groupId ?? 'my-grp',\n * brokers ?? ['localhost:9092'],\n * {\n * ...options,\n * onMessageLost: alerting, // code-level value: not env-configurable, always applied\n * },\n * );\n * ```\n */\nexport function kafkaClientConfigFromEnv(\n env: NodeJS.ProcessEnv = process.env,\n prefix = \"KAFKA_\",\n): EnvClientConfig {\n const options: KafkaClientOptions = {};\n const result: EnvClientConfig = { options };\n\n readVar(env, `${prefix}CLIENT_ID`, (raw) => {\n result.clientId = raw.trim();\n });\n readVar(env, `${prefix}GROUP_ID`, (raw) => {\n result.groupId = raw.trim();\n });\n readVar(env, `${prefix}BROKERS`, (raw) => {\n result.brokers = parseList(raw);\n });\n\n readVar(env, `${prefix}AUTO_CREATE_TOPICS`, (raw) => {\n options.autoCreateTopics = parseBool(`${prefix}AUTO_CREATE_TOPICS`, raw);\n });\n readVar(env, `${prefix}STRICT_SCHEMAS`, (raw) => {\n options.strictSchemas = parseBool(`${prefix}STRICT_SCHEMAS`, raw);\n });\n readVar(env, `${prefix}NUM_PARTITIONS`, (raw) => {\n options.numPartitions = parseNum(`${prefix}NUM_PARTITIONS`, raw);\n });\n readVar(env, `${prefix}TRANSACTIONAL_ID`, (raw) => {\n options.transactionalId = raw.trim();\n });\n\n // clockRecovery — only present when topics are configured.\n readVar(env, `${prefix}CLOCK_RECOVERY_TOPICS`, (raw) => {\n const topics = parseList(raw);\n if (topics.length === 0) return;\n options.clockRecovery = { topics };\n });\n readVar(env, `${prefix}CLOCK_RECOVERY_TIMEOUT_MS`, (raw) => {\n const timeoutMs = parseNum(`${prefix}CLOCK_RECOVERY_TIMEOUT_MS`, raw);\n if (options.clockRecovery) {\n options.clockRecovery.timeoutMs = timeoutMs;\n }\n });\n\n // lagThrottle — only present when MAX_LAG (the sole required field) is set.\n readVar(env, `${prefix}LAG_THROTTLE_MAX_LAG`, (raw) => {\n options.lagThrottle = {\n maxLag: parseNum(`${prefix}LAG_THROTTLE_MAX_LAG`, raw),\n };\n });\n readVar(env, `${prefix}LAG_THROTTLE_GROUP_ID`, (raw) => {\n if (options.lagThrottle) options.lagThrottle.groupId = raw.trim();\n });\n readVar(env, `${prefix}LAG_THROTTLE_POLL_INTERVAL_MS`, (raw) => {\n if (options.lagThrottle) {\n options.lagThrottle.pollIntervalMs = parseNum(\n `${prefix}LAG_THROTTLE_POLL_INTERVAL_MS`,\n raw,\n );\n }\n });\n readVar(env, `${prefix}LAG_THROTTLE_MAX_WAIT_MS`, (raw) => {\n if (options.lagThrottle) {\n options.lagThrottle.maxWaitMs = parseNum(\n `${prefix}LAG_THROTTLE_MAX_WAIT_MS`,\n raw,\n );\n }\n });\n\n const security = securityFromEnv(env, prefix);\n if (security) options.security = security;\n\n return result;\n}\n\n/**\n * Build a {@link KafkaSecurityOptions} from `SSL` / `SASL_*` / `ALLOW_INSECURE`\n * env vars. Returns `undefined` when none of them are set so the `security` key\n * is only emitted on demand.\n *\n * Only username/password SASL mechanisms are env-configurable — `oauthbearer`\n * requires a token-provider function and must be set in code.\n *\n * @throws {Error} when `SASL_MECHANISM` is not one of the allowed literals, or\n * when a username/password mechanism is set without both\n * `SASL_USERNAME` and `SASL_PASSWORD`.\n */\nfunction securityFromEnv(\n env: NodeJS.ProcessEnv,\n prefix: string,\n): KafkaSecurityOptions | undefined {\n let ssl: boolean | undefined;\n let allowInsecure: boolean | undefined;\n let mechanism:\n | \"plain\"\n | \"scram-sha-256\"\n | \"scram-sha-512\"\n | undefined;\n let username: string | undefined;\n let password: string | undefined;\n\n readVar(env, `${prefix}SSL`, (raw) => {\n ssl = parseBool(`${prefix}SSL`, raw);\n });\n readVar(env, `${prefix}ALLOW_INSECURE`, (raw) => {\n allowInsecure = parseBool(`${prefix}ALLOW_INSECURE`, raw);\n });\n readVar(env, `${prefix}SASL_MECHANISM`, (raw) => {\n mechanism = parseEnum(`${prefix}SASL_MECHANISM`, raw, [\n \"plain\",\n \"scram-sha-256\",\n \"scram-sha-512\",\n ] as const);\n });\n readVar(env, `${prefix}SASL_USERNAME`, (raw) => {\n username = raw.trim();\n });\n readVar(env, `${prefix}SASL_PASSWORD`, (raw) => {\n password = raw;\n });\n\n if (\n ssl === undefined &&\n allowInsecure === undefined &&\n mechanism === undefined &&\n username === undefined &&\n password === undefined\n ) {\n return undefined;\n }\n\n const security: KafkaSecurityOptions = {};\n if (ssl !== undefined) security.ssl = ssl;\n if (allowInsecure !== undefined) security.allowInsecure = allowInsecure;\n\n if (mechanism !== undefined || username !== undefined || password !== undefined) {\n if (mechanism === undefined || username === undefined || password === undefined) {\n throw new Error(\n `Incomplete SASL configuration: ${prefix}SASL_MECHANISM, ` +\n `${prefix}SASL_USERNAME, and ${prefix}SASL_PASSWORD must all be set ` +\n `together (oauthbearer must be configured in code).`,\n );\n }\n const sasl: KafkaSaslOptions = { mechanism, username, password };\n security.sasl = sasl;\n }\n\n return security;\n}\n\n// ── Consumer options ──────────────────────────────────────────────────────────\n\n/**\n * Build a partial {@link ConsumerOptions} from environment variables.\n *\n * Intended as a base layer of defaults that you merge with code-level per-consumer\n * options via {@link mergeConsumerOptions}. As with {@link kafkaClientConfigFromEnv},\n * unset variables emit no key, and env values never override code — merge order\n * decides precedence.\n *\n * ### Supported variables (default prefix `KAFKA_CONSUMER_`)\n *\n * | Variable | Type | Maps to |\n * |---|---|---|\n * | `GROUP_ID` | string | `groupId` |\n * | `FROM_BEGINNING` | boolean | `fromBeginning` |\n * | `AUTO_COMMIT` | boolean | `autoCommit` |\n * | `DLQ` | boolean | `dlq` |\n * | `RETRY_MAX_RETRIES` | number | `retry.maxRetries` (enables `retry`) |\n * | `RETRY_BACKOFF_MS` | number | `retry.backoffMs` |\n * | `RETRY_MAX_BACKOFF_MS` | number | `retry.maxBackoffMs` |\n * | `RETRY_TOPICS` | boolean | `retryTopics` |\n * | `RETRY_TOPIC_ASSIGNMENT_TIMEOUT_MS` | number | `retryTopicAssignmentTimeoutMs` |\n * | `HANDLER_TIMEOUT_MS` | number | `handlerTimeoutMs` |\n * | `MESSAGE_TTL_MS` | number | `messageTtlMs` |\n * | `DEDUPLICATION_STRATEGY` | `drop`\\|`dlq`\\|`topic` | `deduplication.strategy` (enables `deduplication`) |\n * | `DEDUPLICATION_TOPIC` | string | `deduplication.duplicatesTopic` |\n * | `CIRCUIT_BREAKER_THRESHOLD` | number | `circuitBreaker.threshold` (enables `circuitBreaker`) |\n * | `CIRCUIT_BREAKER_RECOVERY_MS` | number | `circuitBreaker.recoveryMs` |\n * | `CIRCUIT_BREAKER_WINDOW_SIZE` | number | `circuitBreaker.windowSize` |\n * | `CIRCUIT_BREAKER_HALF_OPEN_SUCCESSES` | number | `circuitBreaker.halfOpenSuccesses` |\n * | `QUEUE_HIGH_WATER_MARK` | number | `queueHighWaterMark` |\n * | `PARTITION_ASSIGNER` | `roundrobin`\\|`range`\\|`cooperative-sticky` | `partitionAssigner` |\n * | `GROUP_INSTANCE_ID` | string | `groupInstanceId` |\n * | `SUBSCRIBE_RETRY_RETRIES` | number | `subscribeRetry.retries` (enables `subscribeRetry`) |\n * | `SUBSCRIBE_RETRY_DELAY_MS` | number | `subscribeRetry.backoffMs` |\n *\n * `retry` is only present when `RETRY_MAX_RETRIES` is set (it is the only required\n * field). `circuitBreaker` is only present when `CIRCUIT_BREAKER_THRESHOLD` is set.\n * `deduplication` is only present when `DEDUPLICATION_STRATEGY` is set.\n * `subscribeRetry` is only present when `SUBSCRIBE_RETRY_RETRIES` is set.\n *\n * @param env Environment source. Defaults to `process.env`.\n * @param prefix Variable prefix. Defaults to `\"KAFKA_CONSUMER_\"`.\n *\n * @throws {Error} when a boolean/number/enum value is malformed.\n *\n * @example\n * ```ts\n * const envDefaults = consumerOptionsFromEnv();\n * await kafka.startConsumer(\n * ['orders'],\n * handler,\n * mergeConsumerOptions(envDefaults, { dlq: true }), // code layer wins\n * );\n * ```\n */\nexport function consumerOptionsFromEnv<\n T extends TTopicMessageMap = TTopicMessageMap,\n>(\n env: NodeJS.ProcessEnv = process.env,\n prefix = \"KAFKA_CONSUMER_\",\n): Partial<ConsumerOptions<T>> {\n const options: Partial<ConsumerOptions<T>> = {};\n\n readVar(env, `${prefix}GROUP_ID`, (raw) => {\n options.groupId = raw.trim();\n });\n readVar(env, `${prefix}FROM_BEGINNING`, (raw) => {\n options.fromBeginning = parseBool(`${prefix}FROM_BEGINNING`, raw);\n });\n readVar(env, `${prefix}AUTO_COMMIT`, (raw) => {\n options.autoCommit = parseBool(`${prefix}AUTO_COMMIT`, raw);\n });\n readVar(env, `${prefix}DLQ`, (raw) => {\n options.dlq = parseBool(`${prefix}DLQ`, raw);\n });\n\n // retry — only present when RETRY_MAX_RETRIES (the sole required field) is set.\n readVar(env, `${prefix}RETRY_MAX_RETRIES`, (raw) => {\n const retry: RetryOptions = {\n maxRetries: parseNum(`${prefix}RETRY_MAX_RETRIES`, raw),\n };\n options.retry = retry;\n });\n readVar(env, `${prefix}RETRY_BACKOFF_MS`, (raw) => {\n if (options.retry) {\n options.retry.backoffMs = parseNum(`${prefix}RETRY_BACKOFF_MS`, raw);\n }\n });\n readVar(env, `${prefix}RETRY_MAX_BACKOFF_MS`, (raw) => {\n if (options.retry) {\n options.retry.maxBackoffMs = parseNum(`${prefix}RETRY_MAX_BACKOFF_MS`, raw);\n }\n });\n\n readVar(env, `${prefix}RETRY_TOPICS`, (raw) => {\n options.retryTopics = parseBool(`${prefix}RETRY_TOPICS`, raw);\n });\n readVar(env, `${prefix}RETRY_TOPIC_ASSIGNMENT_TIMEOUT_MS`, (raw) => {\n options.retryTopicAssignmentTimeoutMs = parseNum(\n `${prefix}RETRY_TOPIC_ASSIGNMENT_TIMEOUT_MS`,\n raw,\n );\n });\n readVar(env, `${prefix}HANDLER_TIMEOUT_MS`, (raw) => {\n options.handlerTimeoutMs = parseNum(`${prefix}HANDLER_TIMEOUT_MS`, raw);\n });\n readVar(env, `${prefix}MESSAGE_TTL_MS`, (raw) => {\n options.messageTtlMs = parseNum(`${prefix}MESSAGE_TTL_MS`, raw);\n });\n\n // deduplication — only present when DEDUPLICATION_STRATEGY is set.\n readVar(env, `${prefix}DEDUPLICATION_STRATEGY`, (raw) => {\n const strategy = parseEnum(`${prefix}DEDUPLICATION_STRATEGY`, raw, [\n \"drop\",\n \"dlq\",\n \"topic\",\n ] as const);\n const dedup: DeduplicationOptions = { strategy };\n options.deduplication = dedup;\n });\n readVar(env, `${prefix}DEDUPLICATION_TOPIC`, (raw) => {\n if (options.deduplication) {\n options.deduplication.duplicatesTopic = raw.trim();\n }\n });\n\n // circuitBreaker — only present when CIRCUIT_BREAKER_THRESHOLD is set.\n readVar(env, `${prefix}CIRCUIT_BREAKER_THRESHOLD`, (raw) => {\n const cb: CircuitBreakerOptions = {\n threshold: parseNum(`${prefix}CIRCUIT_BREAKER_THRESHOLD`, raw),\n };\n options.circuitBreaker = cb;\n });\n readVar(env, `${prefix}CIRCUIT_BREAKER_RECOVERY_MS`, (raw) => {\n if (options.circuitBreaker) {\n options.circuitBreaker.recoveryMs = parseNum(\n `${prefix}CIRCUIT_BREAKER_RECOVERY_MS`,\n raw,\n );\n }\n });\n readVar(env, `${prefix}CIRCUIT_BREAKER_WINDOW_SIZE`, (raw) => {\n if (options.circuitBreaker) {\n options.circuitBreaker.windowSize = parseNum(\n `${prefix}CIRCUIT_BREAKER_WINDOW_SIZE`,\n raw,\n );\n }\n });\n readVar(env, `${prefix}CIRCUIT_BREAKER_HALF_OPEN_SUCCESSES`, (raw) => {\n if (options.circuitBreaker) {\n options.circuitBreaker.halfOpenSuccesses = parseNum(\n `${prefix}CIRCUIT_BREAKER_HALF_OPEN_SUCCESSES`,\n raw,\n );\n }\n });\n\n readVar(env, `${prefix}QUEUE_HIGH_WATER_MARK`, (raw) => {\n options.queueHighWaterMark = parseNum(`${prefix}QUEUE_HIGH_WATER_MARK`, raw);\n });\n readVar(env, `${prefix}PARTITION_ASSIGNER`, (raw) => {\n options.partitionAssigner = parseEnum(`${prefix}PARTITION_ASSIGNER`, raw, [\n \"roundrobin\",\n \"range\",\n \"cooperative-sticky\",\n ] as const);\n });\n readVar(env, `${prefix}GROUP_INSTANCE_ID`, (raw) => {\n options.groupInstanceId = raw.trim();\n });\n\n // subscribeRetry — only present when SUBSCRIBE_RETRY_RETRIES is set.\n readVar(env, `${prefix}SUBSCRIBE_RETRY_RETRIES`, (raw) => {\n const subscribeRetry: SubscribeRetryOptions = {\n retries: parseNum(`${prefix}SUBSCRIBE_RETRY_RETRIES`, raw),\n };\n options.subscribeRetry = subscribeRetry;\n });\n readVar(env, `${prefix}SUBSCRIBE_RETRY_DELAY_MS`, (raw) => {\n if (options.subscribeRetry) {\n options.subscribeRetry.backoffMs = parseNum(\n `${prefix}SUBSCRIBE_RETRY_DELAY_MS`,\n raw,\n );\n }\n });\n\n return options;\n}\n\n// ── Layered merge ──────────────────────────────────────────────────────────────\n\n/** Nested {@link ConsumerOptions} keys that are deep-merged rather than replaced. */\nconst NESTED_CONSUMER_KEYS = [\n \"retry\",\n \"deduplication\",\n \"circuitBreaker\",\n \"subscribeRetry\",\n] as const;\n\nfunction isPlainObject(value: unknown): value is Record<string, unknown> {\n return (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value)\n );\n}\n\n/**\n * Merge several partial {@link ConsumerOptions} layers into one, **later layers\n * winning** on conflict. This is the mechanism that implements the\n * *code > env > defaults* precedence rule — pass the env layer first and the\n * code layer last.\n *\n * Top-level keys are replaced by the last defined value. The nested option\n * objects — `retry`, `deduplication`, `circuitBreaker`, and `subscribeRetry` —\n * are **deep-merged** so a later layer can override a single nested field (e.g.\n * bump `retry.maxRetries`) without discarding the other fields set by an earlier\n * layer. `undefined` values never overwrite a previously set value, and\n * `undefined` layers are skipped entirely.\n *\n * @example Env base overridden by code\n * ```ts\n * const env = { retry: { maxRetries: 3, backoffMs: 500 }, dlq: false };\n * const code = { retry: { maxRetries: 5 }, dlq: true };\n * mergeConsumerOptions(env, code);\n * // → { retry: { maxRetries: 5, backoffMs: 500 }, dlq: true }\n * // maxRetries overridden, backoffMs preserved, dlq overridden\n * ```\n */\nexport function mergeConsumerOptions<\n T extends TTopicMessageMap = TTopicMessageMap,\n>(\n ...layers: Array<Partial<ConsumerOptions<T>> | undefined>\n): ConsumerOptions<T> {\n const result: Record<string, unknown> = {};\n\n for (const layer of layers) {\n if (!layer) continue;\n for (const [key, value] of Object.entries(layer)) {\n if (value === undefined) continue;\n\n if (\n (NESTED_CONSUMER_KEYS as readonly string[]).includes(key) &&\n isPlainObject(value) &&\n isPlainObject(result[key])\n ) {\n result[key] = {\n ...(result[key] as Record<string, unknown>),\n ...value,\n };\n } else {\n result[key] = value;\n }\n }\n }\n\n return result as ConsumerOptions<T>;\n}\n","import { Module, DynamicModule, Provider, Logger } from \"@nestjs/common\";\nimport { DiscoveryModule } from \"@nestjs/core\";\nimport {\n KafkaClient,\n ClientId,\n GroupId,\n TopicMapConstraint,\n KafkaInstrumentation,\n KafkaClientOptions,\n} from \"../client/kafka.client\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { KafkaExplorer } from \"./kafka.explorer\";\n\n/** Shared configuration fields for both `register()` and `registerAsync()`. */\ninterface KafkaModuleBaseOptions {\n /** Optional name for multi-client setups. Must match `@InjectKafkaClient(name)`. */\n name?: string;\n /** If true, makes KAFKA_CLIENT available globally without importing KafkaModule in every feature module. */\n isGlobal?: boolean;\n}\n\n/** Synchronous configuration for `KafkaModule.register()`. */\nexport interface KafkaModuleOptions extends KafkaModuleBaseOptions {\n /** Unique Kafka client identifier. */\n clientId: ClientId;\n /** Consumer group identifier. */\n groupId: GroupId;\n /** List of Kafka broker addresses. */\n brokers: string[];\n /** Auto-create topics via admin on first use (send/consume). Useful for development. */\n autoCreateTopics?: boolean;\n /** When `true`, string topic keys are validated against any schema previously registered via a TopicDescriptor. Default: `true`. */\n strictSchemas?: boolean;\n /** Number of partitions for auto-created topics. Default: `1`. */\n numPartitions?: number;\n /** Client-wide instrumentation hooks (e.g. OTel). Applied to both send and consume paths. */\n instrumentation?: KafkaInstrumentation[];\n /** Called when a message is dropped without being sent to a DLQ. @see `KafkaClientOptions.onMessageLost` */\n onMessageLost?: KafkaClientOptions[\"onMessageLost\"];\n /** Called whenever a consumer group rebalance occurs. @see `KafkaClientOptions.onRebalance` */\n onRebalance?: KafkaClientOptions[\"onRebalance\"];\n /** Transactional producer ID — must be unique per process/replica. @see `KafkaClientOptions.transactionalId` */\n transactionalId?: KafkaClientOptions[\"transactionalId\"];\n /** Recover the Lamport clock from these topics on startup. @see `KafkaClientOptions.clockRecovery` */\n clockRecovery?: KafkaClientOptions[\"clockRecovery\"];\n /** Delay producer sends when consumer lag exceeds a threshold. @see `KafkaClientOptions.lagThrottle` */\n lagThrottle?: KafkaClientOptions[\"lagThrottle\"];\n /** Client-wide TTL expiry callback. @see `KafkaClientOptions.onTtlExpired` */\n onTtlExpired?: KafkaClientOptions[\"onTtlExpired\"];\n /** Custom transport implementation (e.g. `FakeTransport` in tests). @see `KafkaClientOptions.transport` */\n transport?: KafkaClientOptions[\"transport\"];\n /** Transport security (TLS + SASL, incl. MSK IAM / GCP OAUTHBEARER). @see `KafkaClientOptions.security` */\n security?: KafkaClientOptions[\"security\"];\n}\n\n/** Async configuration for `KafkaModule.registerAsync()` with dependency injection. */\nexport interface KafkaModuleAsyncOptions extends KafkaModuleBaseOptions {\n imports?: any[];\n useFactory: (\n ...args: any[]\n ) => KafkaModuleOptions | Promise<KafkaModuleOptions>;\n inject?: any[];\n}\n\n/**\n * NestJS dynamic module for registering type-safe Kafka clients.\n * Use `register()` for static config or `registerAsync()` for DI-based config.\n */\n@Module({})\nexport class KafkaModule {\n /** Register a Kafka client with static options. */\n static register<T extends TopicMapConstraint<T>>(\n options: KafkaModuleOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(options.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: () => KafkaModule.buildClient<T>(options),\n };\n\n return {\n global: options.isGlobal ?? false,\n module: KafkaModule,\n imports: [DiscoveryModule],\n providers: [kafkaClientProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n\n /** Register a Kafka client with async/factory-based options. */\n static registerAsync<T extends TopicMapConstraint<T>>(\n asyncOptions: KafkaModuleAsyncOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(asyncOptions.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: async (...args: any[]): Promise<KafkaClient<T>> =>\n KafkaModule.buildClient<T>(await asyncOptions.useFactory(...args)),\n inject: asyncOptions.inject || [],\n };\n\n return {\n global: asyncOptions.isGlobal ?? false,\n module: KafkaModule,\n imports: [...(asyncOptions.imports || []), DiscoveryModule],\n providers: [kafkaClientProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n\n private static async buildClient<T extends TopicMapConstraint<T>>(\n options: KafkaModuleOptions,\n ): Promise<KafkaClient<T>> {\n const client = new KafkaClient<T>(\n options.clientId,\n options.groupId,\n options.brokers,\n {\n autoCreateTopics: options.autoCreateTopics,\n strictSchemas: options.strictSchemas,\n numPartitions: options.numPartitions,\n instrumentation: options.instrumentation,\n onMessageLost: options.onMessageLost,\n onRebalance: options.onRebalance,\n transactionalId: options.transactionalId,\n clockRecovery: options.clockRecovery,\n lagThrottle: options.lagThrottle,\n onTtlExpired: options.onTtlExpired,\n transport: options.transport,\n security: options.security,\n logger: new Logger(`KafkaClient:${options.clientId}`),\n },\n );\n await client.connectProducer();\n return client;\n }\n}\n","/** Default DI token for the Kafka client. */\nexport const KAFKA_CLIENT = \"KAFKA_CLIENT\";\n\n/** Returns the DI token for a named (or default) Kafka client instance. */\nexport const getKafkaClientToken = (name?: string): string =>\n name ? `KAFKA_CLIENT_${name}` : KAFKA_CLIENT;\n","import { Inject, Injectable, OnModuleInit, Logger } from \"@nestjs/common\";\nimport { DiscoveryService, ModuleRef } from \"@nestjs/core\";\nimport { KafkaClient } from \"../client/kafka.client\";\nimport {\n KAFKA_SUBSCRIBER_METADATA,\n KafkaSubscriberMetadata,\n} from \"./kafka.decorator\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\n\ninterface SubscriberEntry extends KafkaSubscriberMetadata {\n methodName: string | symbol;\n}\n\n/**\n * Process-level registry of already-wired subscriptions, keyed by provider\n * instance. Every `KafkaModule.register()` call contributes its own\n * `KafkaExplorer`, and each explorer scans ALL providers — without this guard\n * a multi-client app would wire every `@SubscribeTo` handler once per\n * registered module (duplicate consumers / \"called twice\" startup errors).\n * Keyed by instance (not constructor) so separate Nest apps in one process\n * (e.g. tests) still wire their own instances independently.\n */\nconst wiredSubscriptions = new WeakMap<object, Set<string>>();\n\n/** Discovers `@SubscribeTo()` decorators and wires them to their Kafka clients on startup. */\n@Injectable()\nexport class KafkaExplorer implements OnModuleInit {\n private readonly logger = new Logger(KafkaExplorer.name);\n\n constructor(\n @Inject(DiscoveryService)\n private readonly discoveryService: DiscoveryService,\n @Inject(ModuleRef)\n private readonly moduleRef: ModuleRef,\n ) {}\n\n /**\n * Scan all NestJS providers for `@SubscribeTo()` metadata and wire each decorated\n * method to its Kafka client via `startConsumer` or `startBatchConsumer`.\n *\n * Called automatically by the NestJS lifecycle — do not invoke manually.\n */\n async onModuleInit() {\n const providers = this.discoveryService.getProviders();\n\n for (const wrapper of providers) {\n const { instance } = wrapper;\n if (!instance || typeof instance !== \"object\") continue;\n\n const metadata: SubscriberEntry[] | undefined = Reflect.getMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n instance.constructor,\n );\n\n if (!metadata || metadata.length === 0) continue;\n\n for (const entry of metadata) {\n const token = getKafkaClientToken(entry.clientName);\n\n const entryKey = `${token}:${String(entry.methodName)}`;\n let wired = wiredSubscriptions.get(instance);\n if (!wired) {\n wired = new Set();\n wiredSubscriptions.set(instance, wired);\n }\n if (wired.has(entryKey)) continue; // already wired by another KafkaExplorer instance\n wired.add(entryKey);\n\n let client: KafkaClient<any>;\n\n try {\n client = this.moduleRef.get(token, { strict: false });\n } catch {\n this.logger.error(\n `KafkaClient \"${entry.clientName || \"default\"}\" not found for @SubscribeTo on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n continue;\n }\n\n const handler = (instance as any)[entry.methodName].bind(instance);\n\n const consumerOptions = { ...entry.options };\n if (entry.schemas) {\n consumerOptions.schemas = entry.schemas;\n }\n\n if (entry.batch) {\n await client.startBatchConsumer(\n entry.topics as any,\n async (envelopes: any[], meta: any) => {\n await handler(envelopes, meta);\n },\n consumerOptions,\n );\n } else {\n await client.startConsumer(\n entry.topics as any,\n async (envelope: any) => {\n await handler(envelope);\n },\n consumerOptions,\n );\n }\n\n this.logger.log(\n `Registered @SubscribeTo(${entry.topics.join(\", \")})${entry.batch ? \" [batch]\" : \"\"} on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n }\n }\n }\n}\n","import { Inject } from \"@nestjs/common\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { ConsumerOptions } from \"../client/kafka.client\";\nimport { TopicDescriptor, SchemaLike } from \"../client/message/topic\";\n\n/** Reflect metadata key used to store `@SubscribeTo` entries on a class constructor. */\nexport const KAFKA_SUBSCRIBER_METADATA = \"KAFKA_SUBSCRIBER_METADATA\";\n\n/** Internal shape stored per `@SubscribeTo()` decoration on a class. */\nexport interface KafkaSubscriberMetadata {\n /** Resolved topic name strings (descriptors are unwrapped to their `__topic` string). */\n topics: string[];\n /** Per-topic schema validators extracted from `TopicDescriptor` objects (if any). */\n schemas?: Map<string, SchemaLike>;\n /** Additional consumer options forwarded to `startConsumer` / `startBatchConsumer`. */\n options?: ConsumerOptions;\n /** Named client identifier — resolves to `KAFKA_CLIENT_<clientName>` in the DI container. */\n clientName?: string;\n /** When `true`, routes to `startBatchConsumer` instead of `startConsumer`. */\n batch?: boolean;\n /** Name of the decorated method on the provider class. */\n methodName?: string | symbol;\n}\n\n/** Inject a `KafkaClient` instance. Pass a name to target a specific named client. */\nexport const InjectKafkaClient = (name?: string): ParameterDecorator =>\n Inject(getKafkaClientToken(name));\n\n/**\n * Method decorator that auto-subscribes the decorated method to one or more Kafka topics\n * when the NestJS module initialises.\n *\n * The decorated method receives a fully-decoded `EventEnvelope` for each message\n * (or an array of envelopes + `BatchMeta` when `batch: true`).\n *\n * @param topics One or more topic names or `TopicDescriptor` objects. Schemas embedded in\n * descriptors are automatically extracted and forwarded to the consumer.\n * @param options Consumer and routing options:\n * - All `ConsumerOptions` fields (`groupId`, `retry`, `dlq`, `fromBeginning`, …)\n * - `clientName` — target a named `KafkaClient` (resolves `KAFKA_CLIENT_<name>` from the DI container)\n * - `batch` — use `startBatchConsumer` instead of `startConsumer`\n *\n * @example\n * ```ts\n * @SubscribeTo('orders.created', { groupId: 'orders-svc', retry: { maxRetries: 3 } })\n * async handleOrder(envelope: EventEnvelope<Order>) { ... }\n *\n * @SubscribeTo(OrdersTopic, { batch: true })\n * async handleBatch(envelopes: EventEnvelope<Order>[], meta: BatchMeta) { ... }\n * ```\n */\nexport const SubscribeTo = (\n topics:\n | string\n | string[]\n | TopicDescriptor\n | TopicDescriptor[]\n | (string | TopicDescriptor)[],\n options?: ConsumerOptions & { clientName?: string; batch?: boolean },\n): MethodDecorator => {\n const arr = Array.isArray(topics) ? topics : [topics];\n const topicsArray = arr.map((t) => (typeof t === \"string\" ? t : t.__topic));\n\n // Extract schemas from descriptors that have them\n const schemas = new Map<string, SchemaLike>();\n for (const t of arr) {\n if (typeof t !== \"string\" && t.__schema) {\n schemas.set(t.__topic, t.__schema);\n }\n }\n\n const { clientName, batch, ...consumerOptions } = options || {};\n\n return (target, propertyKey, _descriptor) => {\n const existing: KafkaSubscriberMetadata[] =\n Reflect.getMetadata(KAFKA_SUBSCRIBER_METADATA, target.constructor) || [];\n\n Reflect.defineMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n [\n ...existing,\n {\n topics: topicsArray,\n schemas: schemas.size > 0 ? schemas : undefined,\n options: Object.keys(consumerOptions).length\n ? consumerOptions\n : undefined,\n clientName,\n batch,\n methodName: propertyKey,\n },\n ],\n target.constructor,\n );\n };\n};\n","import { Injectable } from \"@nestjs/common\";\nimport type {\n IKafkaClient,\n KafkaHealthResult,\n TopicMapConstraint,\n} from \"../client/types\";\nexport type { KafkaHealthResult } from \"../client/types\";\n\n/** Health check service. Call `check(client)` to verify broker connectivity. */\n@Injectable()\nexport class KafkaHealthIndicator {\n async check<T extends TopicMapConstraint<T>>(\n client: IKafkaClient<T>,\n ): Promise<KafkaHealthResult> {\n return client.checkStatus();\n }\n}\n"],"mappings":"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","names":["topic","topic","topic","topic","topic","topic","topic","topic","topic","topic","topic","ensureTopic","getOrCreateConsumer","createRetryTxProducer","topic","envelopes","topic","topic","topic","topic","topic","topic","toError","import_common","import_core","import_common","import_common"]}