@drarzter/kafka-client 0.5.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -6
- package/dist/{chunk-YCKN2YEC.mjs → chunk-P7GY4BLV.mjs} +3 -3
- package/dist/chunk-P7GY4BLV.mjs.map +1 -0
- package/dist/core.d.mts +5 -3
- package/dist/core.d.ts +5 -3
- package/dist/core.js +2 -2
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +1 -1
- package/dist/{envelope-QK1trQu4.d.mts → envelope-CPX1qudy.d.mts} +2 -0
- package/dist/{envelope-QK1trQu4.d.ts → envelope-CPX1qudy.d.ts} +2 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +3 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -7
- package/dist/index.mjs.map +1 -1
- package/dist/otel.d.mts +1 -1
- package/dist/otel.d.ts +1 -1
- package/dist/testing.d.mts +2 -2
- package/dist/testing.d.ts +2 -2
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +1 -1
- package/dist/testing.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-YCKN2YEC.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -82,6 +82,22 @@ await kafka.startConsumer([OrderCreated], async (envelope) => {
|
|
|
82
82
|
const kafka2 = new KafkaClient('my-app', 'my-group', ['localhost:9092'], {
|
|
83
83
|
logger: myWinstonLogger,
|
|
84
84
|
});
|
|
85
|
+
|
|
86
|
+
// All module options work in standalone mode too
|
|
87
|
+
const kafka3 = new KafkaClient('my-app', 'my-group', ['localhost:9092'], {
|
|
88
|
+
autoCreateTopics: true, // auto-create topics on first use
|
|
89
|
+
numPartitions: 3, // partitions for auto-created topics
|
|
90
|
+
strictSchemas: false, // disable schema enforcement for string topic keys
|
|
91
|
+
instrumentation: [...], // client-wide tracing/metrics hooks
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// Health check — available directly, no NestJS needed
|
|
95
|
+
const status = await kafka.checkStatus();
|
|
96
|
+
// { status: 'up', clientId: 'my-app', topics: ['order.created', ...] }
|
|
97
|
+
|
|
98
|
+
// Stop all consumers without disconnecting the producer or admin
|
|
99
|
+
// Useful when you want to re-subscribe with different options
|
|
100
|
+
await kafka.stopConsumer();
|
|
85
101
|
```
|
|
86
102
|
|
|
87
103
|
## Quick start (NestJS)
|
|
@@ -489,6 +505,10 @@ await this.kafka.startBatchConsumer(
|
|
|
489
505
|
for (const env of envelopes) {
|
|
490
506
|
await processOrder(env.payload);
|
|
491
507
|
meta.resolveOffset(env.offset);
|
|
508
|
+
|
|
509
|
+
// Call heartbeat() during long-running batch processing to prevent
|
|
510
|
+
// the broker from considering the consumer dead (session.timeout.ms)
|
|
511
|
+
await meta.heartbeat();
|
|
492
512
|
}
|
|
493
513
|
await meta.commitOffsetsIfNecessary();
|
|
494
514
|
},
|
|
@@ -496,6 +516,24 @@ await this.kafka.startBatchConsumer(
|
|
|
496
516
|
);
|
|
497
517
|
```
|
|
498
518
|
|
|
519
|
+
With `autoCommit: false` for full manual offset control:
|
|
520
|
+
|
|
521
|
+
```typescript
|
|
522
|
+
await this.kafka.startBatchConsumer(
|
|
523
|
+
['order.created'],
|
|
524
|
+
async (envelopes, meta) => {
|
|
525
|
+
for (const env of envelopes) {
|
|
526
|
+
await processOrder(env.payload);
|
|
527
|
+
meta.resolveOffset(env.offset);
|
|
528
|
+
}
|
|
529
|
+
// commitOffsetsIfNecessary() commits only when autoCommit is off
|
|
530
|
+
// or when the commit interval has elapsed
|
|
531
|
+
await meta.commitOffsetsIfNecessary();
|
|
532
|
+
},
|
|
533
|
+
{ autoCommit: false },
|
|
534
|
+
);
|
|
535
|
+
```
|
|
536
|
+
|
|
499
537
|
With `@SubscribeTo()`:
|
|
500
538
|
|
|
501
539
|
```typescript
|
|
@@ -507,7 +545,15 @@ async handleOrders(envelopes: EventEnvelope<OrdersTopicMap['order.created']>[],
|
|
|
507
545
|
|
|
508
546
|
Schema validation runs per-message — invalid messages are skipped (DLQ'd if enabled), valid ones are passed to the handler. Retry applies to the whole batch.
|
|
509
547
|
|
|
510
|
-
`BatchMeta` exposes:
|
|
548
|
+
`BatchMeta` exposes:
|
|
549
|
+
|
|
550
|
+
| Property/Method | Description |
|
|
551
|
+
| --------------- | ----------- |
|
|
552
|
+
| `partition` | Partition number for this batch |
|
|
553
|
+
| `highWatermark` | Latest offset in the partition (lag indicator) |
|
|
554
|
+
| `heartbeat()` | Send a heartbeat to keep the consumer session alive — call during long processing loops |
|
|
555
|
+
| `resolveOffset(offset)` | Mark offset as processed (required before `commitOffsetsIfNecessary`) |
|
|
556
|
+
| `commitOffsetsIfNecessary()` | Commit resolved offsets; respects `autoCommit` setting |
|
|
511
557
|
|
|
512
558
|
## Transactions
|
|
513
559
|
|
|
@@ -528,7 +574,17 @@ await this.kafka.transaction(async (tx) => {
|
|
|
528
574
|
});
|
|
529
575
|
```
|
|
530
576
|
|
|
531
|
-
`tx.sendBatch()` is also available inside transactions
|
|
577
|
+
`tx.sendBatch()` is also available inside transactions:
|
|
578
|
+
|
|
579
|
+
```typescript
|
|
580
|
+
await this.kafka.transaction(async (tx) => {
|
|
581
|
+
await tx.sendBatch('order.created', [
|
|
582
|
+
{ value: { orderId: '1', userId: '10', amount: 50 }, key: '1' },
|
|
583
|
+
{ value: { orderId: '2', userId: '20', amount: 75 }, key: '2' },
|
|
584
|
+
]);
|
|
585
|
+
// if anything throws, all messages are rolled back
|
|
586
|
+
});
|
|
587
|
+
```
|
|
532
588
|
|
|
533
589
|
## Consumer interceptors
|
|
534
590
|
|
|
@@ -590,7 +646,7 @@ const metrics: KafkaInstrumentation = {
|
|
|
590
646
|
Options for `sendMessage()` — the third argument:
|
|
591
647
|
|
|
592
648
|
| Option | Default | Description |
|
|
593
|
-
|
|
649
|
+
| ------ | ------- | ----------- |
|
|
594
650
|
| `key` | — | Partition key for message routing |
|
|
595
651
|
| `headers` | — | Custom metadata headers (merged with auto-generated envelope headers) |
|
|
596
652
|
| `correlationId` | auto | Override the auto-propagated correlation ID (default: inherited from ALS context or new UUID) |
|
|
@@ -602,7 +658,7 @@ Options for `sendMessage()` — the third argument:
|
|
|
602
658
|
### Consumer options
|
|
603
659
|
|
|
604
660
|
| Option | Default | Description |
|
|
605
|
-
|
|
661
|
+
| ------ | ------- | ----------- |
|
|
606
662
|
| `groupId` | constructor value | Override consumer group for this subscription |
|
|
607
663
|
| `fromBeginning` | `false` | Read from the beginning of the topic |
|
|
608
664
|
| `autoCommit` | `true` | Auto-commit offsets |
|
|
@@ -619,7 +675,7 @@ Options for `sendMessage()` — the third argument:
|
|
|
619
675
|
Passed to `KafkaModule.register()` or returned from `registerAsync()` factory:
|
|
620
676
|
|
|
621
677
|
| Option | Default | Description |
|
|
622
|
-
|
|
678
|
+
| ------ | ------- | ----------- |
|
|
623
679
|
| `clientId` | — | Kafka client identifier (required) |
|
|
624
680
|
| `groupId` | — | Default consumer group ID (required) |
|
|
625
681
|
| `brokers` | — | Array of broker addresses (required) |
|
|
@@ -882,7 +938,7 @@ it('sends and receives', async () => {
|
|
|
882
938
|
Options:
|
|
883
939
|
|
|
884
940
|
| Option | Default | Description |
|
|
885
|
-
|
|
941
|
+
| ------ | ------- | ----------- |
|
|
886
942
|
| `image` | `"confluentinc/cp-kafka:7.7.0"` | Docker image |
|
|
887
943
|
| `transactionWarmup` | `true` | Warm up transaction coordinator on start |
|
|
888
944
|
| `topics` | `[]` | Topics to pre-create (string or `{ topic, numPartitions }`) |
|
|
@@ -485,14 +485,14 @@ var KafkaClient = class {
|
|
|
485
485
|
this.runningConsumers.clear();
|
|
486
486
|
this.logger.log("All consumers disconnected");
|
|
487
487
|
}
|
|
488
|
-
/** Check broker connectivity and return available topics. */
|
|
488
|
+
/** Check broker connectivity and return status, clientId, and available topics. */
|
|
489
489
|
async checkStatus() {
|
|
490
490
|
if (!this.isAdminConnected) {
|
|
491
491
|
await this.admin.connect();
|
|
492
492
|
this.isAdminConnected = true;
|
|
493
493
|
}
|
|
494
494
|
const topics = await this.admin.listTopics();
|
|
495
|
-
return { topics };
|
|
495
|
+
return { status: "up", clientId: this.clientId, topics };
|
|
496
496
|
}
|
|
497
497
|
getClientId() {
|
|
498
498
|
return this.clientId;
|
|
@@ -680,4 +680,4 @@ export {
|
|
|
680
680
|
KafkaClient,
|
|
681
681
|
topic
|
|
682
682
|
};
|
|
683
|
-
//# sourceMappingURL=chunk-
|
|
683
|
+
//# sourceMappingURL=chunk-P7GY4BLV.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/client/kafka.client.ts","../src/client/envelope.ts","../src/client/errors.ts","../src/client/consumer-pipeline.ts","../src/client/subscribe-retry.ts","../src/client/topic.ts"],"sourcesContent":["import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Kafka = KafkaJS.Kafka;\ntype Producer = KafkaJS.Producer;\ntype Consumer = KafkaJS.Consumer;\ntype Admin = KafkaJS.Admin;\nconst { Kafka: KafkaClass, logLevel: KafkaLogLevel } = KafkaJS;\nimport { TopicDescriptor, SchemaLike } from \"./topic\";\nimport {\n buildEnvelopeHeaders,\n decodeHeaders,\n extractEnvelope,\n runWithEnvelopeContext,\n} from \"./envelope\";\nimport type { EventEnvelope } from \"./envelope\";\nimport {\n toError,\n parseJsonMessage,\n validateWithSchema,\n executeWithRetry,\n} from \"./consumer-pipeline\";\nimport { subscribeWithRetry } from \"./subscribe-retry\";\nimport type {\n ClientId,\n GroupId,\n SendOptions,\n MessageHeaders,\n BatchMessageItem,\n ConsumerOptions,\n TransactionContext,\n TopicMapConstraint,\n IKafkaClient,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n BatchMeta,\n} from \"./types\";\n\n// Re-export all types so existing `import { ... } from './kafka.client'` keeps working\nexport * from \"./types\";\n\n/**\n * Type-safe Kafka client.\n * Wraps @confluentinc/kafka-javascript (librdkafka) with JSON serialization,\n * retries, DLQ, transactions, and interceptors.\n *\n * @typeParam T - Topic-to-message type mapping for compile-time safety.\n */\nexport class KafkaClient<\n T extends TopicMapConstraint<T>,\n> implements IKafkaClient<T> {\n private readonly kafka: Kafka;\n private readonly producer: Producer;\n private txProducer: Producer | undefined;\n private readonly consumers = new Map<string, Consumer>();\n private readonly admin: Admin;\n private readonly logger: KafkaLogger;\n private readonly autoCreateTopicsEnabled: boolean;\n private readonly strictSchemasEnabled: boolean;\n private readonly numPartitions: number;\n private readonly ensuredTopics = new Set<string>();\n private readonly defaultGroupId: string;\n private readonly schemaRegistry = new Map<string, SchemaLike>();\n private readonly runningConsumers = new Map<string, \"eachMessage\" | \"eachBatch\">();\n private readonly instrumentation: KafkaInstrumentation[];\n\n private isAdminConnected = false;\n public readonly clientId: ClientId;\n\n constructor(\n clientId: ClientId,\n groupId: GroupId,\n brokers: string[],\n options?: KafkaClientOptions,\n ) {\n this.clientId = clientId;\n this.defaultGroupId = groupId;\n this.logger = options?.logger ?? {\n log: (msg) => console.log(`[KafkaClient:${clientId}] ${msg}`),\n warn: (msg, ...args) => console.warn(`[KafkaClient:${clientId}] ${msg}`, ...args),\n error: (msg, ...args) => console.error(`[KafkaClient:${clientId}] ${msg}`, ...args),\n };\n this.autoCreateTopicsEnabled = options?.autoCreateTopics ?? false;\n this.strictSchemasEnabled = options?.strictSchemas ?? true;\n this.numPartitions = options?.numPartitions ?? 1;\n this.instrumentation = options?.instrumentation ?? [];\n\n this.kafka = new KafkaClass({\n kafkaJS: {\n clientId: this.clientId,\n brokers,\n logLevel: KafkaLogLevel.ERROR,\n },\n });\n this.producer = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n },\n });\n this.admin = this.kafka.admin();\n }\n\n // ── Send ─────────────────────────────────────────────────────────\n\n /** Send a single typed message. Accepts a topic key or a TopicDescriptor. */\n public async sendMessage<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(descriptor: D, message: D[\"__type\"], options?: SendOptions): Promise<void>;\n public async sendMessage<K extends keyof T>(\n topic: K,\n message: T[K],\n options?: SendOptions,\n ): Promise<void>;\n public async sendMessage(\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ): Promise<void> {\n const payload = this.buildSendPayload(topicOrDesc, [\n {\n value: message,\n key: options.key,\n headers: options.headers,\n correlationId: options.correlationId,\n schemaVersion: options.schemaVersion,\n eventId: options.eventId,\n },\n ]);\n await this.ensureTopic(payload.topic);\n await this.producer.send(payload);\n for (const inst of this.instrumentation) {\n inst.afterSend?.(payload.topic);\n }\n }\n\n /** Send multiple typed messages in one call. Accepts a topic key or a TopicDescriptor. */\n public async sendBatch<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n descriptor: D,\n messages: Array<BatchMessageItem<D[\"__type\"]>>,\n ): Promise<void>;\n public async sendBatch<K extends keyof T>(\n topic: K,\n messages: Array<BatchMessageItem<T[K]>>,\n ): Promise<void>;\n public async sendBatch(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n ): Promise<void> {\n const payload = this.buildSendPayload(topicOrDesc, messages);\n await this.ensureTopic(payload.topic);\n await this.producer.send(payload);\n for (const inst of this.instrumentation) {\n inst.afterSend?.(payload.topic);\n }\n }\n\n /** Execute multiple sends atomically. Commits on success, aborts on error. */\n public async transaction(\n fn: (ctx: TransactionContext<T>) => Promise<void>,\n ): Promise<void> {\n if (!this.txProducer) {\n this.txProducer = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n idempotent: true,\n transactionalId: `${this.clientId}-tx`,\n maxInFlightRequests: 1,\n },\n });\n await this.txProducer.connect();\n }\n const tx = await this.txProducer.transaction();\n try {\n const ctx: TransactionContext<T> = {\n send: async (\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ) => {\n const payload = this.buildSendPayload(topicOrDesc, [\n {\n value: message,\n key: options.key,\n headers: options.headers,\n correlationId: options.correlationId,\n schemaVersion: options.schemaVersion,\n eventId: options.eventId,\n },\n ]);\n await this.ensureTopic(payload.topic);\n await tx.send(payload);\n },\n sendBatch: async (topicOrDesc: any, messages: BatchMessageItem<any>[]) => {\n const payload = this.buildSendPayload(topicOrDesc, messages);\n await this.ensureTopic(payload.topic);\n await tx.send(payload);\n },\n };\n await fn(ctx);\n await tx.commit();\n } catch (error) {\n try {\n await tx.abort();\n } catch (abortError) {\n this.logger.error(\n \"Failed to abort transaction:\",\n toError(abortError).message,\n );\n }\n throw error;\n }\n }\n\n // ── Producer lifecycle ───────────────────────────────────────────\n\n /** Connect the idempotent producer. Called automatically by `KafkaModule.register()`. */\n public async connectProducer(): Promise<void> {\n await this.producer.connect();\n this.logger.log(\"Producer connected\");\n }\n\n public async disconnectProducer(): Promise<void> {\n await this.producer.disconnect();\n this.logger.log(\"Producer disconnected\");\n }\n\n // ── Consumer: eachMessage ────────────────────────────────────────\n\n /** Subscribe to topics and start consuming messages with the given handler. */\n public async startConsumer<K extends Array<keyof T>>(\n topics: K,\n handleMessage: (envelope: EventEnvelope<T[K[number]]>) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<void>;\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<void>;\n public async startConsumer(\n topics: any[],\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<void> {\n const { consumer, schemaMap, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachMessage\", options);\n\n const deps = { logger: this.logger, producer: this.producer, instrumentation: this.instrumentation };\n\n await consumer.run({\n eachMessage: async ({ topic, partition, message }) => {\n if (!message.value) {\n this.logger.warn(`Received empty message from topic ${topic}`);\n return;\n }\n\n const raw = message.value.toString();\n const parsed = parseJsonMessage(raw, topic, this.logger);\n if (parsed === null) return;\n\n const validated = await validateWithSchema(\n parsed, raw, topic, schemaMap, interceptors, dlq, deps,\n );\n if (validated === null) return;\n\n const headers = decodeHeaders(message.headers);\n const envelope = extractEnvelope(\n validated, headers, topic, partition, message.offset,\n );\n\n await executeWithRetry(\n () =>\n runWithEnvelopeContext(\n { correlationId: envelope.correlationId, traceparent: envelope.traceparent },\n () => handleMessage(envelope),\n ),\n { envelope, rawMessages: [raw], interceptors, dlq, retry },\n deps,\n );\n },\n });\n\n this.runningConsumers.set(gid, \"eachMessage\");\n }\n\n // ── Consumer: eachBatch ──────────────────────────────────────────\n\n /** Subscribe to topics and consume messages in batches. */\n public async startBatchConsumer<K extends Array<keyof T>>(\n topics: K,\n handleBatch: (\n envelopes: EventEnvelope<T[K[number]]>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<void>;\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<void>;\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<void> {\n const { consumer, schemaMap, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachBatch\", options);\n\n const deps = { logger: this.logger, producer: this.producer, instrumentation: this.instrumentation };\n\n await consumer.run({\n eachBatch: async ({\n batch,\n heartbeat,\n resolveOffset,\n commitOffsetsIfNecessary,\n }) => {\n const envelopes: EventEnvelope<any>[] = [];\n const rawMessages: string[] = [];\n\n for (const message of batch.messages) {\n if (!message.value) {\n this.logger.warn(\n `Received empty message from topic ${batch.topic}`,\n );\n continue;\n }\n\n const raw = message.value.toString();\n const parsed = parseJsonMessage(raw, batch.topic, this.logger);\n if (parsed === null) continue;\n\n const validated = await validateWithSchema(\n parsed, raw, batch.topic, schemaMap, interceptors, dlq, deps,\n );\n if (validated === null) continue;\n\n const headers = decodeHeaders(message.headers);\n envelopes.push(\n extractEnvelope(validated, headers, batch.topic, batch.partition, message.offset),\n );\n rawMessages.push(raw);\n }\n\n if (envelopes.length === 0) return;\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 () => handleBatch(envelopes, meta),\n {\n envelope: envelopes,\n rawMessages: batch.messages\n .filter((m) => m.value)\n .map((m) => m.value!.toString()),\n interceptors,\n dlq,\n retry,\n isBatch: true,\n },\n deps,\n );\n },\n });\n\n this.runningConsumers.set(gid, \"eachBatch\");\n }\n\n // ── Consumer lifecycle ───────────────────────────────────────────\n\n public async stopConsumer(): Promise<void> {\n const tasks = [];\n for (const consumer of this.consumers.values()) {\n tasks.push(consumer.disconnect());\n }\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.logger.log(\"All consumers disconnected\");\n }\n\n /** Check broker connectivity and return status, clientId, and available topics. */\n public async checkStatus(): Promise<{ status: 'up'; clientId: string; topics: string[] }> {\n if (!this.isAdminConnected) {\n await this.admin.connect();\n this.isAdminConnected = true;\n }\n const topics = await this.admin.listTopics();\n return { status: 'up', clientId: this.clientId, topics };\n }\n\n public getClientId(): ClientId {\n return this.clientId;\n }\n\n /** Gracefully disconnect producer, all consumers, and admin. */\n public async disconnect(): Promise<void> {\n const tasks: Promise<void>[] = [this.producer.disconnect()];\n if (this.txProducer) {\n tasks.push(this.txProducer.disconnect());\n this.txProducer = undefined;\n }\n for (const consumer of this.consumers.values()) {\n tasks.push(consumer.disconnect());\n }\n if (this.isAdminConnected) {\n tasks.push(this.admin.disconnect());\n this.isAdminConnected = false;\n }\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.logger.log(\"All connections closed\");\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private getOrCreateConsumer(\n groupId: string,\n fromBeginning: boolean,\n autoCommit: boolean,\n ): Consumer {\n if (!this.consumers.has(groupId)) {\n this.consumers.set(\n groupId,\n this.kafka.consumer({\n kafkaJS: { groupId, fromBeginning, autoCommit },\n }),\n );\n }\n return this.consumers.get(groupId)!;\n }\n\n private 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 TopicDescriptor).__topic;\n }\n return String(topicOrDescriptor);\n }\n\n private async ensureTopic(topic: string): Promise<void> {\n if (!this.autoCreateTopicsEnabled || this.ensuredTopics.has(topic)) return;\n if (!this.isAdminConnected) {\n await this.admin.connect();\n this.isAdminConnected = true;\n }\n await this.admin.createTopics({\n topics: [{ topic, numPartitions: this.numPartitions }],\n });\n this.ensuredTopics.add(topic);\n }\n\n /** Register schema from descriptor into global registry (side-effect). */\n private registerSchema(topicOrDesc: any): void {\n if (topicOrDesc?.__schema) {\n const topic = this.resolveTopicName(topicOrDesc);\n this.schemaRegistry.set(topic, topicOrDesc.__schema);\n }\n }\n\n /** Validate message against schema. Pure — no side-effects on registry. */\n private validateMessage(topicOrDesc: any, message: any): any {\n if (topicOrDesc?.__schema) {\n return topicOrDesc.__schema.parse(message);\n }\n if (this.strictSchemasEnabled && typeof topicOrDesc === \"string\") {\n const schema = this.schemaRegistry.get(topicOrDesc);\n if (schema) return schema.parse(message);\n }\n return message;\n }\n\n /**\n * Build a kafkajs-ready send payload.\n * Handles: topic resolution, schema registration, validation, JSON serialization,\n * envelope header generation, and instrumentation hooks.\n */\n private buildSendPayload(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n ): { topic: string; messages: Array<{ value: string; key: string | null; headers: MessageHeaders }> } {\n this.registerSchema(topicOrDesc);\n const topic = this.resolveTopicName(topicOrDesc);\n return {\n topic,\n messages: messages.map((m) => {\n const envelopeHeaders = buildEnvelopeHeaders({\n correlationId: m.correlationId,\n schemaVersion: m.schemaVersion,\n eventId: m.eventId,\n headers: m.headers,\n });\n\n // Let instrumentation hooks mutate headers (e.g. OTel injects traceparent)\n for (const inst of this.instrumentation) {\n inst.beforeSend?.(topic, envelopeHeaders);\n }\n\n return {\n value: JSON.stringify(this.validateMessage(topicOrDesc, m.value)),\n key: m.key ?? null,\n headers: envelopeHeaders,\n };\n }),\n };\n }\n\n /** Shared consumer setup: groupId check, schema map, connect, subscribe. */\n private async setupConsumer(\n topics: any[],\n mode: \"eachMessage\" | \"eachBatch\",\n options: ConsumerOptions<T>,\n ) {\n const {\n groupId: optGroupId,\n fromBeginning = false,\n retry,\n dlq = false,\n interceptors = [],\n schemas: optionSchemas,\n } = options;\n\n const gid = optGroupId || this.defaultGroupId;\n const existingMode = this.runningConsumers.get(gid);\n const oppositeMode = mode === \"eachMessage\" ? \"eachBatch\" : \"eachMessage\";\n if (existingMode === oppositeMode) {\n throw new Error(\n `Cannot use ${mode} on consumer group \"${gid}\" — it is already running with ${oppositeMode}. ` +\n `Use a different groupId for this consumer.`,\n );\n }\n\n const consumer = this.getOrCreateConsumer(gid, fromBeginning, options.autoCommit ?? true);\n const schemaMap = this.buildSchemaMap(topics, optionSchemas);\n\n const topicNames = (topics as any[]).map((t: any) =>\n this.resolveTopicName(t),\n );\n\n // Ensure topics exist before subscribing — librdkafka errors on unknown topics\n for (const t of topicNames) {\n await this.ensureTopic(t);\n }\n if (dlq) {\n for (const t of topicNames) {\n await this.ensureTopic(`${t}.dlq`);\n }\n }\n\n await consumer.connect();\n await subscribeWithRetry(consumer, topicNames, this.logger, options.subscribeRetry);\n\n this.logger.log(\n `${mode === \"eachBatch\" ? \"Batch consumer\" : \"Consumer\"} subscribed to topics: ${topicNames.join(\", \")}`,\n );\n\n return { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry };\n }\n\n private buildSchemaMap(\n topics: any[],\n optionSchemas?: Map<string, SchemaLike>,\n ): Map<string, SchemaLike> {\n const schemaMap = new Map<string, SchemaLike>();\n for (const t of topics) {\n if (t?.__schema) {\n const name = this.resolveTopicName(t);\n schemaMap.set(name, t.__schema);\n this.schemaRegistry.set(name, t.__schema);\n }\n }\n if (optionSchemas) {\n for (const [k, v] of optionSchemas) {\n schemaMap.set(k, v);\n this.schemaRegistry.set(k, v);\n }\n }\n return schemaMap;\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\n// ── EventEnvelope ────────────────────────────────────────────────────\n\n/**\n * Typed wrapper combining a parsed message payload with Kafka metadata\n * and envelope headers.\n *\n * On **send**, the library auto-generates envelope headers\n * (`x-event-id`, `x-correlation-id`, `x-timestamp`, `x-schema-version`).\n *\n * On **consume**, the library extracts those headers and assembles\n * an `EventEnvelope` that is passed to the handler.\n */\nexport interface EventEnvelope<T> {\n /** Deserialized + validated message body. */\n payload: T;\n /** Topic the message was produced to / consumed from. */\n topic: string;\n /** Kafka partition (consume-side only, `-1` on send). */\n partition: number;\n /** Kafka offset (consume-side only, empty string on send). */\n offset: string;\n /** ISO-8601 timestamp set by the producer. */\n timestamp: string;\n /** Unique ID for this event (UUID v4). */\n eventId: string;\n /** Correlation ID — auto-propagated via AsyncLocalStorage. */\n correlationId: string;\n /** Schema version of the payload. */\n schemaVersion: number;\n /** W3C Trace Context `traceparent` header (set by OTel instrumentation). */\n traceparent?: string;\n /** All decoded Kafka headers for extensibility. */\n headers: MessageHeaders;\n}\n\n// ── AsyncLocalStorage context ────────────────────────────────────────\n\ninterface EnvelopeCtx {\n correlationId: string;\n traceparent?: string;\n}\n\nconst envelopeStorage = new AsyncLocalStorage<EnvelopeCtx>();\n\n/** Read the current envelope context (correlationId / traceparent) from ALS. */\nexport function getEnvelopeContext(): EnvelopeCtx | undefined {\n return envelopeStorage.getStore();\n}\n\n/** Execute `fn` inside an envelope context so nested sends inherit correlationId. */\nexport function runWithEnvelopeContext<R>(\n ctx: EnvelopeCtx,\n fn: () => R,\n): 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: Record<string, Buffer | string | (Buffer | string)[] | undefined> | 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 result[key] = value.map((v) => (Buffer.isBuffer(v) ? v.toString() : v)).join(\",\");\n } else {\n result[key] = Buffer.isBuffer(value) ? value.toString() : value;\n }\n }\n return result;\n}\n\n/**\n * Build an `EventEnvelope` from a consumed kafkajs message.\n * Tolerates missing envelope headers — generates defaults so messages\n * from non-envelope producers still work.\n */\nexport function extractEnvelope<T>(\n payload: T,\n headers: MessageHeaders,\n topic: string,\n partition: number,\n offset: string,\n): EventEnvelope<T> {\n return {\n payload,\n topic,\n partition,\n offset,\n eventId: headers[HEADER_EVENT_ID] ?? randomUUID(),\n correlationId: headers[HEADER_CORRELATION_ID] ?? randomUUID(),\n timestamp: headers[HEADER_TIMESTAMP] ?? new Date().toISOString(),\n schemaVersion: Number(headers[HEADER_SCHEMA_VERSION] ?? 1),\n traceparent: headers[HEADER_TRACEPARENT],\n headers,\n };\n}\n","/** Error thrown when a consumer message handler fails. */\nexport class KafkaProcessingError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n message: string,\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(message, options);\n this.name = \"KafkaProcessingError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when schema validation fails on send or consume. */\nexport class KafkaValidationError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(`Schema validation failed for topic \"${topic}\"`, options);\n this.name = \"KafkaValidationError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when all retry attempts are exhausted for a message. */\nexport class KafkaRetryExhaustedError extends KafkaProcessingError {\n constructor(\n topic: string,\n originalMessage: unknown,\n public readonly attempts: number,\n options?: { cause?: Error },\n ) {\n super(\n `Message processing failed after ${attempts} attempts on topic \"${topic}\"`,\n topic,\n originalMessage,\n options,\n );\n this.name = \"KafkaRetryExhaustedError\";\n }\n}\n","import type { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Producer = KafkaJS.Producer;\nimport type { EventEnvelope } from \"./envelope\";\nimport { extractEnvelope } from \"./envelope\";\nimport { KafkaRetryExhaustedError, KafkaValidationError } from \"./errors\";\nimport type { SchemaLike } from \"./topic\";\nimport type {\n ConsumerInterceptor,\n KafkaInstrumentation,\n KafkaLogger,\n RetryOptions,\n TopicMapConstraint,\n} from \"./types\";\n\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ── JSON parsing ────────────────────────────────────────────────────\n\n/** Parse raw message as JSON. Returns null on failure (logs error). */\nexport function parseJsonMessage(\n raw: string,\n topic: string,\n logger: KafkaLogger,\n): any | null {\n try {\n return JSON.parse(raw);\n } catch (error) {\n logger.error(\n `Failed to parse message from topic ${topic}:`,\n toError(error).stack,\n );\n return null;\n }\n}\n\n// ── Schema validation ───────────────────────────────────────────────\n\n/**\n * Validate a parsed message against the schema map.\n * On failure: logs error, sends to DLQ if enabled, calls interceptor.onError.\n * Returns validated message or null.\n */\nexport async function validateWithSchema<T extends TopicMapConstraint<T>>(\n message: any,\n raw: string,\n topic: string,\n schemaMap: Map<string, SchemaLike>,\n interceptors: ConsumerInterceptor<T>[],\n dlq: boolean,\n deps: { logger: KafkaLogger; producer: Producer },\n): Promise<any | null> {\n const schema = schemaMap.get(topic);\n if (!schema) return message;\n\n try {\n return schema.parse(message);\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) await sendToDlq(topic, raw, deps);\n // Validation errors don't have an envelope yet — call onError with a minimal envelope\n const errorEnvelope = extractEnvelope(message, {}, topic, -1, \"\");\n for (const interceptor of interceptors) {\n await interceptor.onError?.(errorEnvelope, validationError);\n }\n return null;\n }\n}\n\n// ── DLQ ─────────────────────────────────────────────────────────────\n\nexport async function sendToDlq(\n topic: string,\n rawMessage: string,\n deps: { logger: KafkaLogger; producer: Producer },\n): Promise<void> {\n const dlqTopic = `${topic}.dlq`;\n try {\n await deps.producer.send({\n topic: dlqTopic,\n messages: [{ value: rawMessage }],\n });\n deps.logger.warn(`Message sent to DLQ: ${dlqTopic}`);\n } catch (error) {\n deps.logger.error(\n `Failed to send message to DLQ ${dlqTopic}:`,\n toError(error).stack,\n );\n }\n}\n\n// ── Retry pipeline ──────────────────────────────────────────────────\n\nexport interface ExecuteWithRetryContext<T extends TopicMapConstraint<T>> {\n envelope: EventEnvelope<any> | EventEnvelope<any>[];\n rawMessages: string[];\n interceptors: ConsumerInterceptor<T>[];\n dlq: boolean;\n retry?: RetryOptions;\n isBatch?: boolean;\n}\n\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 },\n): Promise<void> {\n const { envelope, rawMessages, interceptors, dlq, retry, isBatch } = ctx;\n const maxAttempts = retry ? retry.maxRetries + 1 : 1;\n const backoffMs = retry?.backoffMs ?? 1000;\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 // Collect instrumentation cleanup functions\n const cleanups: (() => void)[] = [];\n\n try {\n // Instrumentation: beforeConsume\n for (const env of envelopes) {\n for (const inst of deps.instrumentation) {\n const cleanup = inst.beforeConsume?.(env);\n if (typeof cleanup === \"function\") cleanups.push(cleanup);\n }\n }\n\n // Consumer interceptors: before\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.before?.(env);\n }\n }\n\n await fn();\n\n // Consumer interceptors: after\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.after?.(env);\n }\n }\n\n // Instrumentation: cleanup (end spans etc.)\n for (const cleanup of cleanups) cleanup();\n\n return;\n } catch (error) {\n const err = toError(error);\n const isLastAttempt = attempt === maxAttempts;\n\n // Instrumentation: onConsumeError\n for (const env of envelopes) {\n for (const inst of deps.instrumentation) {\n inst.onConsumeError?.(env, err);\n }\n }\n // Instrumentation: cleanup even on error\n for (const cleanup of cleanups) cleanup();\n\n if (isLastAttempt && maxAttempts > 1) {\n const exhaustedError = new KafkaRetryExhaustedError(\n topic,\n envelopes.map((e) => e.payload),\n maxAttempts,\n { cause: err },\n );\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.onError?.(env, exhaustedError);\n }\n }\n } else {\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.onError?.(env, err);\n }\n }\n }\n\n deps.logger.error(\n `Error processing ${isBatch ? \"batch\" : \"message\"} from topic ${topic} (attempt ${attempt}/${maxAttempts}):`,\n err.stack,\n );\n\n if (isLastAttempt) {\n if (dlq) {\n for (const raw of rawMessages) {\n await sendToDlq(topic, raw, deps);\n }\n }\n } else {\n await sleep(backoffMs * attempt);\n }\n }\n }\n}\n","import type { KafkaJS } from \"@confluentinc/kafka-javascript\";\nimport type { KafkaLogger, SubscribeRetryOptions } from \"./types\";\nimport { toError, sleep } from \"./consumer-pipeline\";\n\nexport async function subscribeWithRetry(\n consumer: KafkaJS.Consumer,\n topics: string[],\n logger: KafkaLogger,\n retryOpts?: SubscribeRetryOptions,\n): Promise<void> {\n const maxAttempts = retryOpts?.retries ?? 5;\n const backoffMs = retryOpts?.backoffMs ?? 5000;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await consumer.subscribe({ topics });\n return;\n } catch (error) {\n if (attempt === maxAttempts) throw error;\n const msg = toError(error).message;\n logger.warn(\n `Failed to subscribe to [${topics.join(\", \")}] (attempt ${attempt}/${maxAttempts}): ${msg}. Retrying in ${backoffMs}ms...`,\n );\n await sleep(backoffMs);\n }\n }\n}\n","/**\n * Any validation library with a `.parse()` method.\n * Works with Zod, Valibot, ArkType, or any custom validator.\n *\n * @example\n * ```ts\n * import { z } from 'zod';\n * const schema: SchemaLike<{ id: string }> = z.object({ id: z.string() });\n * ```\n */\nexport interface SchemaLike<T = any> {\n parse(data: unknown): 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 — type provided explicitly:\n * const OrderCreated = topic('order.created')<{ 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 const fn = <M extends Record<string, any>>(): TopicDescriptor<N, M> => ({\n __topic: name,\n __type: undefined as unknown as M,\n });\n\n fn.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 return fn;\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')<{ orderId: string }>();\n * const OrderCompleted = topic('order.completed')<{ completedAt: string }>();\n *\n * type MyTopics = TopicsFrom<typeof OrderCreated | typeof OrderCompleted>;\n * // { 'order.created': { orderId: string }; 'order.completed': { completedAt: string } }\n * ```\n */\nexport type TopicsFrom<D extends TopicDescriptor<any, any>> = {\n [K in D as K[\"__topic\"]]: K[\"__type\"];\n};\n"],"mappings":"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","names":["topic","topic","topic","topic"]}
|
package/dist/core.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { T as TopicMapConstraint, I as IKafkaClient, C as ClientId, G as GroupId, k as KafkaClientOptions, b as TopicDescriptor, m as SendOptions, B as BatchMessageItem, q as TransactionContext, e as EventEnvelope, a as ConsumerOptions, c as BatchMeta } from './envelope-
|
|
2
|
-
export { d as ConsumerInterceptor, E as EnvelopeHeaderOptions, H as HEADER_CORRELATION_ID, f as HEADER_EVENT_ID, g as HEADER_SCHEMA_VERSION, h as HEADER_TIMESTAMP, i as HEADER_TRACEPARENT, j as InferSchema, K as KafkaInstrumentation, l as KafkaLogger, M as MessageHeaders, R as RetryOptions, S as SchemaLike, n as SubscribeRetryOptions, o as TTopicMessageMap, p as TopicsFrom, r as buildEnvelopeHeaders, s as decodeHeaders, t as extractEnvelope, u as getEnvelopeContext, v as runWithEnvelopeContext, w as topic } from './envelope-
|
|
1
|
+
import { T as TopicMapConstraint, I as IKafkaClient, C as ClientId, G as GroupId, k as KafkaClientOptions, b as TopicDescriptor, m as SendOptions, B as BatchMessageItem, q as TransactionContext, e as EventEnvelope, a as ConsumerOptions, c as BatchMeta } from './envelope-CPX1qudy.mjs';
|
|
2
|
+
export { d as ConsumerInterceptor, E as EnvelopeHeaderOptions, H as HEADER_CORRELATION_ID, f as HEADER_EVENT_ID, g as HEADER_SCHEMA_VERSION, h as HEADER_TIMESTAMP, i as HEADER_TRACEPARENT, j as InferSchema, K as KafkaInstrumentation, l as KafkaLogger, M as MessageHeaders, R as RetryOptions, S as SchemaLike, n as SubscribeRetryOptions, o as TTopicMessageMap, p as TopicsFrom, r as buildEnvelopeHeaders, s as decodeHeaders, t as extractEnvelope, u as getEnvelopeContext, v as runWithEnvelopeContext, w as topic } from './envelope-CPX1qudy.mjs';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Type-safe Kafka client.
|
|
@@ -44,8 +44,10 @@ declare class KafkaClient<T extends TopicMapConstraint<T>> implements IKafkaClie
|
|
|
44
44
|
startBatchConsumer<K extends Array<keyof T>>(topics: K, handleBatch: (envelopes: EventEnvelope<T[K[number]]>[], meta: BatchMeta) => Promise<void>, options?: ConsumerOptions<T>): Promise<void>;
|
|
45
45
|
startBatchConsumer<D extends TopicDescriptor<string & keyof T, T[string & keyof T]>>(topics: D[], handleBatch: (envelopes: EventEnvelope<D["__type"]>[], meta: BatchMeta) => Promise<void>, options?: ConsumerOptions<T>): Promise<void>;
|
|
46
46
|
stopConsumer(): Promise<void>;
|
|
47
|
-
/** Check broker connectivity and return available topics. */
|
|
47
|
+
/** Check broker connectivity and return status, clientId, and available topics. */
|
|
48
48
|
checkStatus(): Promise<{
|
|
49
|
+
status: 'up';
|
|
50
|
+
clientId: string;
|
|
49
51
|
topics: string[];
|
|
50
52
|
}>;
|
|
51
53
|
getClientId(): ClientId;
|
package/dist/core.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { T as TopicMapConstraint, I as IKafkaClient, C as ClientId, G as GroupId, k as KafkaClientOptions, b as TopicDescriptor, m as SendOptions, B as BatchMessageItem, q as TransactionContext, e as EventEnvelope, a as ConsumerOptions, c as BatchMeta } from './envelope-
|
|
2
|
-
export { d as ConsumerInterceptor, E as EnvelopeHeaderOptions, H as HEADER_CORRELATION_ID, f as HEADER_EVENT_ID, g as HEADER_SCHEMA_VERSION, h as HEADER_TIMESTAMP, i as HEADER_TRACEPARENT, j as InferSchema, K as KafkaInstrumentation, l as KafkaLogger, M as MessageHeaders, R as RetryOptions, S as SchemaLike, n as SubscribeRetryOptions, o as TTopicMessageMap, p as TopicsFrom, r as buildEnvelopeHeaders, s as decodeHeaders, t as extractEnvelope, u as getEnvelopeContext, v as runWithEnvelopeContext, w as topic } from './envelope-
|
|
1
|
+
import { T as TopicMapConstraint, I as IKafkaClient, C as ClientId, G as GroupId, k as KafkaClientOptions, b as TopicDescriptor, m as SendOptions, B as BatchMessageItem, q as TransactionContext, e as EventEnvelope, a as ConsumerOptions, c as BatchMeta } from './envelope-CPX1qudy.js';
|
|
2
|
+
export { d as ConsumerInterceptor, E as EnvelopeHeaderOptions, H as HEADER_CORRELATION_ID, f as HEADER_EVENT_ID, g as HEADER_SCHEMA_VERSION, h as HEADER_TIMESTAMP, i as HEADER_TRACEPARENT, j as InferSchema, K as KafkaInstrumentation, l as KafkaLogger, M as MessageHeaders, R as RetryOptions, S as SchemaLike, n as SubscribeRetryOptions, o as TTopicMessageMap, p as TopicsFrom, r as buildEnvelopeHeaders, s as decodeHeaders, t as extractEnvelope, u as getEnvelopeContext, v as runWithEnvelopeContext, w as topic } from './envelope-CPX1qudy.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Type-safe Kafka client.
|
|
@@ -44,8 +44,10 @@ declare class KafkaClient<T extends TopicMapConstraint<T>> implements IKafkaClie
|
|
|
44
44
|
startBatchConsumer<K extends Array<keyof T>>(topics: K, handleBatch: (envelopes: EventEnvelope<T[K[number]]>[], meta: BatchMeta) => Promise<void>, options?: ConsumerOptions<T>): Promise<void>;
|
|
45
45
|
startBatchConsumer<D extends TopicDescriptor<string & keyof T, T[string & keyof T]>>(topics: D[], handleBatch: (envelopes: EventEnvelope<D["__type"]>[], meta: BatchMeta) => Promise<void>, options?: ConsumerOptions<T>): Promise<void>;
|
|
46
46
|
stopConsumer(): Promise<void>;
|
|
47
|
-
/** Check broker connectivity and return available topics. */
|
|
47
|
+
/** Check broker connectivity and return status, clientId, and available topics. */
|
|
48
48
|
checkStatus(): Promise<{
|
|
49
|
+
status: 'up';
|
|
50
|
+
clientId: string;
|
|
49
51
|
topics: string[];
|
|
50
52
|
}>;
|
|
51
53
|
getClientId(): ClientId;
|
package/dist/core.js
CHANGED
|
@@ -525,14 +525,14 @@ var KafkaClient = class {
|
|
|
525
525
|
this.runningConsumers.clear();
|
|
526
526
|
this.logger.log("All consumers disconnected");
|
|
527
527
|
}
|
|
528
|
-
/** Check broker connectivity and return available topics. */
|
|
528
|
+
/** Check broker connectivity and return status, clientId, and available topics. */
|
|
529
529
|
async checkStatus() {
|
|
530
530
|
if (!this.isAdminConnected) {
|
|
531
531
|
await this.admin.connect();
|
|
532
532
|
this.isAdminConnected = true;
|
|
533
533
|
}
|
|
534
534
|
const topics = await this.admin.listTopics();
|
|
535
|
-
return { topics };
|
|
535
|
+
return { status: "up", clientId: this.clientId, topics };
|
|
536
536
|
}
|
|
537
537
|
getClientId() {
|
|
538
538
|
return this.clientId;
|
package/dist/core.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core.ts","../src/client/kafka.client.ts","../src/client/envelope.ts","../src/client/errors.ts","../src/client/consumer-pipeline.ts","../src/client/subscribe-retry.ts","../src/client/topic.ts"],"sourcesContent":["export * from \"./client/kafka.client\";\nexport * from \"./client/topic\";\nexport * from \"./client/errors\";\nexport * from \"./client/envelope\";\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Kafka = KafkaJS.Kafka;\ntype Producer = KafkaJS.Producer;\ntype Consumer = KafkaJS.Consumer;\ntype Admin = KafkaJS.Admin;\nconst { Kafka: KafkaClass, logLevel: KafkaLogLevel } = KafkaJS;\nimport { TopicDescriptor, SchemaLike } from \"./topic\";\nimport {\n buildEnvelopeHeaders,\n decodeHeaders,\n extractEnvelope,\n runWithEnvelopeContext,\n} from \"./envelope\";\nimport type { EventEnvelope } from \"./envelope\";\nimport {\n toError,\n parseJsonMessage,\n validateWithSchema,\n executeWithRetry,\n} from \"./consumer-pipeline\";\nimport { subscribeWithRetry } from \"./subscribe-retry\";\nimport type {\n ClientId,\n GroupId,\n SendOptions,\n MessageHeaders,\n BatchMessageItem,\n ConsumerOptions,\n TransactionContext,\n TopicMapConstraint,\n IKafkaClient,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n BatchMeta,\n} from \"./types\";\n\n// Re-export all types so existing `import { ... } from './kafka.client'` keeps working\nexport * from \"./types\";\n\n/**\n * Type-safe Kafka client.\n * Wraps @confluentinc/kafka-javascript (librdkafka) with JSON serialization,\n * retries, DLQ, transactions, and interceptors.\n *\n * @typeParam T - Topic-to-message type mapping for compile-time safety.\n */\nexport class KafkaClient<\n T extends TopicMapConstraint<T>,\n> implements IKafkaClient<T> {\n private readonly kafka: Kafka;\n private readonly producer: Producer;\n private txProducer: Producer | undefined;\n private readonly consumers = new Map<string, Consumer>();\n private readonly admin: Admin;\n private readonly logger: KafkaLogger;\n private readonly autoCreateTopicsEnabled: boolean;\n private readonly strictSchemasEnabled: boolean;\n private readonly numPartitions: number;\n private readonly ensuredTopics = new Set<string>();\n private readonly defaultGroupId: string;\n private readonly schemaRegistry = new Map<string, SchemaLike>();\n private readonly runningConsumers = new Map<string, \"eachMessage\" | \"eachBatch\">();\n private readonly instrumentation: KafkaInstrumentation[];\n\n private isAdminConnected = false;\n public readonly clientId: ClientId;\n\n constructor(\n clientId: ClientId,\n groupId: GroupId,\n brokers: string[],\n options?: KafkaClientOptions,\n ) {\n this.clientId = clientId;\n this.defaultGroupId = groupId;\n this.logger = options?.logger ?? {\n log: (msg) => console.log(`[KafkaClient:${clientId}] ${msg}`),\n warn: (msg, ...args) => console.warn(`[KafkaClient:${clientId}] ${msg}`, ...args),\n error: (msg, ...args) => console.error(`[KafkaClient:${clientId}] ${msg}`, ...args),\n };\n this.autoCreateTopicsEnabled = options?.autoCreateTopics ?? false;\n this.strictSchemasEnabled = options?.strictSchemas ?? true;\n this.numPartitions = options?.numPartitions ?? 1;\n this.instrumentation = options?.instrumentation ?? [];\n\n this.kafka = new KafkaClass({\n kafkaJS: {\n clientId: this.clientId,\n brokers,\n logLevel: KafkaLogLevel.ERROR,\n },\n });\n this.producer = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n },\n });\n this.admin = this.kafka.admin();\n }\n\n // ── Send ─────────────────────────────────────────────────────────\n\n /** Send a single typed message. Accepts a topic key or a TopicDescriptor. */\n public async sendMessage<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(descriptor: D, message: D[\"__type\"], options?: SendOptions): Promise<void>;\n public async sendMessage<K extends keyof T>(\n topic: K,\n message: T[K],\n options?: SendOptions,\n ): Promise<void>;\n public async sendMessage(\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ): Promise<void> {\n const payload = this.buildSendPayload(topicOrDesc, [\n {\n value: message,\n key: options.key,\n headers: options.headers,\n correlationId: options.correlationId,\n schemaVersion: options.schemaVersion,\n eventId: options.eventId,\n },\n ]);\n await this.ensureTopic(payload.topic);\n await this.producer.send(payload);\n for (const inst of this.instrumentation) {\n inst.afterSend?.(payload.topic);\n }\n }\n\n /** Send multiple typed messages in one call. Accepts a topic key or a TopicDescriptor. */\n public async sendBatch<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n descriptor: D,\n messages: Array<BatchMessageItem<D[\"__type\"]>>,\n ): Promise<void>;\n public async sendBatch<K extends keyof T>(\n topic: K,\n messages: Array<BatchMessageItem<T[K]>>,\n ): Promise<void>;\n public async sendBatch(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n ): Promise<void> {\n const payload = this.buildSendPayload(topicOrDesc, messages);\n await this.ensureTopic(payload.topic);\n await this.producer.send(payload);\n for (const inst of this.instrumentation) {\n inst.afterSend?.(payload.topic);\n }\n }\n\n /** Execute multiple sends atomically. Commits on success, aborts on error. */\n public async transaction(\n fn: (ctx: TransactionContext<T>) => Promise<void>,\n ): Promise<void> {\n if (!this.txProducer) {\n this.txProducer = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n idempotent: true,\n transactionalId: `${this.clientId}-tx`,\n maxInFlightRequests: 1,\n },\n });\n await this.txProducer.connect();\n }\n const tx = await this.txProducer.transaction();\n try {\n const ctx: TransactionContext<T> = {\n send: async (\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ) => {\n const payload = this.buildSendPayload(topicOrDesc, [\n {\n value: message,\n key: options.key,\n headers: options.headers,\n correlationId: options.correlationId,\n schemaVersion: options.schemaVersion,\n eventId: options.eventId,\n },\n ]);\n await this.ensureTopic(payload.topic);\n await tx.send(payload);\n },\n sendBatch: async (topicOrDesc: any, messages: BatchMessageItem<any>[]) => {\n const payload = this.buildSendPayload(topicOrDesc, messages);\n await this.ensureTopic(payload.topic);\n await tx.send(payload);\n },\n };\n await fn(ctx);\n await tx.commit();\n } catch (error) {\n try {\n await tx.abort();\n } catch (abortError) {\n this.logger.error(\n \"Failed to abort transaction:\",\n toError(abortError).message,\n );\n }\n throw error;\n }\n }\n\n // ── Producer lifecycle ───────────────────────────────────────────\n\n /** Connect the idempotent producer. Called automatically by `KafkaModule.register()`. */\n public async connectProducer(): Promise<void> {\n await this.producer.connect();\n this.logger.log(\"Producer connected\");\n }\n\n public async disconnectProducer(): Promise<void> {\n await this.producer.disconnect();\n this.logger.log(\"Producer disconnected\");\n }\n\n // ── Consumer: eachMessage ────────────────────────────────────────\n\n /** Subscribe to topics and start consuming messages with the given handler. */\n public async startConsumer<K extends Array<keyof T>>(\n topics: K,\n handleMessage: (envelope: EventEnvelope<T[K[number]]>) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<void>;\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<void>;\n public async startConsumer(\n topics: any[],\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<void> {\n const { consumer, schemaMap, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachMessage\", options);\n\n const deps = { logger: this.logger, producer: this.producer, instrumentation: this.instrumentation };\n\n await consumer.run({\n eachMessage: async ({ topic, partition, message }) => {\n if (!message.value) {\n this.logger.warn(`Received empty message from topic ${topic}`);\n return;\n }\n\n const raw = message.value.toString();\n const parsed = parseJsonMessage(raw, topic, this.logger);\n if (parsed === null) return;\n\n const validated = await validateWithSchema(\n parsed, raw, topic, schemaMap, interceptors, dlq, deps,\n );\n if (validated === null) return;\n\n const headers = decodeHeaders(message.headers);\n const envelope = extractEnvelope(\n validated, headers, topic, partition, message.offset,\n );\n\n await executeWithRetry(\n () =>\n runWithEnvelopeContext(\n { correlationId: envelope.correlationId, traceparent: envelope.traceparent },\n () => handleMessage(envelope),\n ),\n { envelope, rawMessages: [raw], interceptors, dlq, retry },\n deps,\n );\n },\n });\n\n this.runningConsumers.set(gid, \"eachMessage\");\n }\n\n // ── Consumer: eachBatch ──────────────────────────────────────────\n\n /** Subscribe to topics and consume messages in batches. */\n public async startBatchConsumer<K extends Array<keyof T>>(\n topics: K,\n handleBatch: (\n envelopes: EventEnvelope<T[K[number]]>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<void>;\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<void>;\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<void> {\n const { consumer, schemaMap, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachBatch\", options);\n\n const deps = { logger: this.logger, producer: this.producer, instrumentation: this.instrumentation };\n\n await consumer.run({\n eachBatch: async ({\n batch,\n heartbeat,\n resolveOffset,\n commitOffsetsIfNecessary,\n }) => {\n const envelopes: EventEnvelope<any>[] = [];\n const rawMessages: string[] = [];\n\n for (const message of batch.messages) {\n if (!message.value) {\n this.logger.warn(\n `Received empty message from topic ${batch.topic}`,\n );\n continue;\n }\n\n const raw = message.value.toString();\n const parsed = parseJsonMessage(raw, batch.topic, this.logger);\n if (parsed === null) continue;\n\n const validated = await validateWithSchema(\n parsed, raw, batch.topic, schemaMap, interceptors, dlq, deps,\n );\n if (validated === null) continue;\n\n const headers = decodeHeaders(message.headers);\n envelopes.push(\n extractEnvelope(validated, headers, batch.topic, batch.partition, message.offset),\n );\n rawMessages.push(raw);\n }\n\n if (envelopes.length === 0) return;\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 () => handleBatch(envelopes, meta),\n {\n envelope: envelopes,\n rawMessages: batch.messages\n .filter((m) => m.value)\n .map((m) => m.value!.toString()),\n interceptors,\n dlq,\n retry,\n isBatch: true,\n },\n deps,\n );\n },\n });\n\n this.runningConsumers.set(gid, \"eachBatch\");\n }\n\n // ── Consumer lifecycle ───────────────────────────────────────────\n\n public async stopConsumer(): Promise<void> {\n const tasks = [];\n for (const consumer of this.consumers.values()) {\n tasks.push(consumer.disconnect());\n }\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.logger.log(\"All consumers disconnected\");\n }\n\n /** Check broker connectivity and return available topics. */\n public async checkStatus(): Promise<{ topics: string[] }> {\n if (!this.isAdminConnected) {\n await this.admin.connect();\n this.isAdminConnected = true;\n }\n const topics = await this.admin.listTopics();\n return { topics };\n }\n\n public getClientId(): ClientId {\n return this.clientId;\n }\n\n /** Gracefully disconnect producer, all consumers, and admin. */\n public async disconnect(): Promise<void> {\n const tasks: Promise<void>[] = [this.producer.disconnect()];\n if (this.txProducer) {\n tasks.push(this.txProducer.disconnect());\n this.txProducer = undefined;\n }\n for (const consumer of this.consumers.values()) {\n tasks.push(consumer.disconnect());\n }\n if (this.isAdminConnected) {\n tasks.push(this.admin.disconnect());\n this.isAdminConnected = false;\n }\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.logger.log(\"All connections closed\");\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private getOrCreateConsumer(\n groupId: string,\n fromBeginning: boolean,\n autoCommit: boolean,\n ): Consumer {\n if (!this.consumers.has(groupId)) {\n this.consumers.set(\n groupId,\n this.kafka.consumer({\n kafkaJS: { groupId, fromBeginning, autoCommit },\n }),\n );\n }\n return this.consumers.get(groupId)!;\n }\n\n private 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 TopicDescriptor).__topic;\n }\n return String(topicOrDescriptor);\n }\n\n private async ensureTopic(topic: string): Promise<void> {\n if (!this.autoCreateTopicsEnabled || this.ensuredTopics.has(topic)) return;\n if (!this.isAdminConnected) {\n await this.admin.connect();\n this.isAdminConnected = true;\n }\n await this.admin.createTopics({\n topics: [{ topic, numPartitions: this.numPartitions }],\n });\n this.ensuredTopics.add(topic);\n }\n\n /** Register schema from descriptor into global registry (side-effect). */\n private registerSchema(topicOrDesc: any): void {\n if (topicOrDesc?.__schema) {\n const topic = this.resolveTopicName(topicOrDesc);\n this.schemaRegistry.set(topic, topicOrDesc.__schema);\n }\n }\n\n /** Validate message against schema. Pure — no side-effects on registry. */\n private validateMessage(topicOrDesc: any, message: any): any {\n if (topicOrDesc?.__schema) {\n return topicOrDesc.__schema.parse(message);\n }\n if (this.strictSchemasEnabled && typeof topicOrDesc === \"string\") {\n const schema = this.schemaRegistry.get(topicOrDesc);\n if (schema) return schema.parse(message);\n }\n return message;\n }\n\n /**\n * Build a kafkajs-ready send payload.\n * Handles: topic resolution, schema registration, validation, JSON serialization,\n * envelope header generation, and instrumentation hooks.\n */\n private buildSendPayload(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n ): { topic: string; messages: Array<{ value: string; key: string | null; headers: MessageHeaders }> } {\n this.registerSchema(topicOrDesc);\n const topic = this.resolveTopicName(topicOrDesc);\n return {\n topic,\n messages: messages.map((m) => {\n const envelopeHeaders = buildEnvelopeHeaders({\n correlationId: m.correlationId,\n schemaVersion: m.schemaVersion,\n eventId: m.eventId,\n headers: m.headers,\n });\n\n // Let instrumentation hooks mutate headers (e.g. OTel injects traceparent)\n for (const inst of this.instrumentation) {\n inst.beforeSend?.(topic, envelopeHeaders);\n }\n\n return {\n value: JSON.stringify(this.validateMessage(topicOrDesc, m.value)),\n key: m.key ?? null,\n headers: envelopeHeaders,\n };\n }),\n };\n }\n\n /** Shared consumer setup: groupId check, schema map, connect, subscribe. */\n private async setupConsumer(\n topics: any[],\n mode: \"eachMessage\" | \"eachBatch\",\n options: ConsumerOptions<T>,\n ) {\n const {\n groupId: optGroupId,\n fromBeginning = false,\n retry,\n dlq = false,\n interceptors = [],\n schemas: optionSchemas,\n } = options;\n\n const gid = optGroupId || this.defaultGroupId;\n const existingMode = this.runningConsumers.get(gid);\n const oppositeMode = mode === \"eachMessage\" ? \"eachBatch\" : \"eachMessage\";\n if (existingMode === oppositeMode) {\n throw new Error(\n `Cannot use ${mode} on consumer group \"${gid}\" — it is already running with ${oppositeMode}. ` +\n `Use a different groupId for this consumer.`,\n );\n }\n\n const consumer = this.getOrCreateConsumer(gid, fromBeginning, options.autoCommit ?? true);\n const schemaMap = this.buildSchemaMap(topics, optionSchemas);\n\n const topicNames = (topics as any[]).map((t: any) =>\n this.resolveTopicName(t),\n );\n\n // Ensure topics exist before subscribing — librdkafka errors on unknown topics\n for (const t of topicNames) {\n await this.ensureTopic(t);\n }\n if (dlq) {\n for (const t of topicNames) {\n await this.ensureTopic(`${t}.dlq`);\n }\n }\n\n await consumer.connect();\n await subscribeWithRetry(consumer, topicNames, this.logger, options.subscribeRetry);\n\n this.logger.log(\n `${mode === \"eachBatch\" ? \"Batch consumer\" : \"Consumer\"} subscribed to topics: ${topicNames.join(\", \")}`,\n );\n\n return { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry };\n }\n\n private buildSchemaMap(\n topics: any[],\n optionSchemas?: Map<string, SchemaLike>,\n ): Map<string, SchemaLike> {\n const schemaMap = new Map<string, SchemaLike>();\n for (const t of topics) {\n if (t?.__schema) {\n const name = this.resolveTopicName(t);\n schemaMap.set(name, t.__schema);\n this.schemaRegistry.set(name, t.__schema);\n }\n }\n if (optionSchemas) {\n for (const [k, v] of optionSchemas) {\n schemaMap.set(k, v);\n this.schemaRegistry.set(k, v);\n }\n }\n return schemaMap;\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\n// ── EventEnvelope ────────────────────────────────────────────────────\n\n/**\n * Typed wrapper combining a parsed message payload with Kafka metadata\n * and envelope headers.\n *\n * On **send**, the library auto-generates envelope headers\n * (`x-event-id`, `x-correlation-id`, `x-timestamp`, `x-schema-version`).\n *\n * On **consume**, the library extracts those headers and assembles\n * an `EventEnvelope` that is passed to the handler.\n */\nexport interface EventEnvelope<T> {\n /** Deserialized + validated message body. */\n payload: T;\n /** Topic the message was produced to / consumed from. */\n topic: string;\n /** Kafka partition (consume-side only, `-1` on send). */\n partition: number;\n /** Kafka offset (consume-side only, empty string on send). */\n offset: string;\n /** ISO-8601 timestamp set by the producer. */\n timestamp: string;\n /** Unique ID for this event (UUID v4). */\n eventId: string;\n /** Correlation ID — auto-propagated via AsyncLocalStorage. */\n correlationId: string;\n /** Schema version of the payload. */\n schemaVersion: number;\n /** W3C Trace Context `traceparent` header (set by OTel instrumentation). */\n traceparent?: string;\n /** All decoded Kafka headers for extensibility. */\n headers: MessageHeaders;\n}\n\n// ── AsyncLocalStorage context ────────────────────────────────────────\n\ninterface EnvelopeCtx {\n correlationId: string;\n traceparent?: string;\n}\n\nconst envelopeStorage = new AsyncLocalStorage<EnvelopeCtx>();\n\n/** Read the current envelope context (correlationId / traceparent) from ALS. */\nexport function getEnvelopeContext(): EnvelopeCtx | undefined {\n return envelopeStorage.getStore();\n}\n\n/** Execute `fn` inside an envelope context so nested sends inherit correlationId. */\nexport function runWithEnvelopeContext<R>(\n ctx: EnvelopeCtx,\n fn: () => R,\n): 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: Record<string, Buffer | string | (Buffer | string)[] | undefined> | 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 result[key] = value.map((v) => (Buffer.isBuffer(v) ? v.toString() : v)).join(\",\");\n } else {\n result[key] = Buffer.isBuffer(value) ? value.toString() : value;\n }\n }\n return result;\n}\n\n/**\n * Build an `EventEnvelope` from a consumed kafkajs message.\n * Tolerates missing envelope headers — generates defaults so messages\n * from non-envelope producers still work.\n */\nexport function extractEnvelope<T>(\n payload: T,\n headers: MessageHeaders,\n topic: string,\n partition: number,\n offset: string,\n): EventEnvelope<T> {\n return {\n payload,\n topic,\n partition,\n offset,\n eventId: headers[HEADER_EVENT_ID] ?? randomUUID(),\n correlationId: headers[HEADER_CORRELATION_ID] ?? randomUUID(),\n timestamp: headers[HEADER_TIMESTAMP] ?? new Date().toISOString(),\n schemaVersion: Number(headers[HEADER_SCHEMA_VERSION] ?? 1),\n traceparent: headers[HEADER_TRACEPARENT],\n headers,\n };\n}\n","/** Error thrown when a consumer message handler fails. */\nexport class KafkaProcessingError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n message: string,\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(message, options);\n this.name = \"KafkaProcessingError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when schema validation fails on send or consume. */\nexport class KafkaValidationError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(`Schema validation failed for topic \"${topic}\"`, options);\n this.name = \"KafkaValidationError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when all retry attempts are exhausted for a message. */\nexport class KafkaRetryExhaustedError extends KafkaProcessingError {\n constructor(\n topic: string,\n originalMessage: unknown,\n public readonly attempts: number,\n options?: { cause?: Error },\n ) {\n super(\n `Message processing failed after ${attempts} attempts on topic \"${topic}\"`,\n topic,\n originalMessage,\n options,\n );\n this.name = \"KafkaRetryExhaustedError\";\n }\n}\n","import type { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Producer = KafkaJS.Producer;\nimport type { EventEnvelope } from \"./envelope\";\nimport { extractEnvelope } from \"./envelope\";\nimport { KafkaRetryExhaustedError, KafkaValidationError } from \"./errors\";\nimport type { SchemaLike } from \"./topic\";\nimport type {\n ConsumerInterceptor,\n KafkaInstrumentation,\n KafkaLogger,\n RetryOptions,\n TopicMapConstraint,\n} from \"./types\";\n\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ── JSON parsing ────────────────────────────────────────────────────\n\n/** Parse raw message as JSON. Returns null on failure (logs error). */\nexport function parseJsonMessage(\n raw: string,\n topic: string,\n logger: KafkaLogger,\n): any | null {\n try {\n return JSON.parse(raw);\n } catch (error) {\n logger.error(\n `Failed to parse message from topic ${topic}:`,\n toError(error).stack,\n );\n return null;\n }\n}\n\n// ── Schema validation ───────────────────────────────────────────────\n\n/**\n * Validate a parsed message against the schema map.\n * On failure: logs error, sends to DLQ if enabled, calls interceptor.onError.\n * Returns validated message or null.\n */\nexport async function validateWithSchema<T extends TopicMapConstraint<T>>(\n message: any,\n raw: string,\n topic: string,\n schemaMap: Map<string, SchemaLike>,\n interceptors: ConsumerInterceptor<T>[],\n dlq: boolean,\n deps: { logger: KafkaLogger; producer: Producer },\n): Promise<any | null> {\n const schema = schemaMap.get(topic);\n if (!schema) return message;\n\n try {\n return schema.parse(message);\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) await sendToDlq(topic, raw, deps);\n // Validation errors don't have an envelope yet — call onError with a minimal envelope\n const errorEnvelope = extractEnvelope(message, {}, topic, -1, \"\");\n for (const interceptor of interceptors) {\n await interceptor.onError?.(errorEnvelope, validationError);\n }\n return null;\n }\n}\n\n// ── DLQ ─────────────────────────────────────────────────────────────\n\nexport async function sendToDlq(\n topic: string,\n rawMessage: string,\n deps: { logger: KafkaLogger; producer: Producer },\n): Promise<void> {\n const dlqTopic = `${topic}.dlq`;\n try {\n await deps.producer.send({\n topic: dlqTopic,\n messages: [{ value: rawMessage }],\n });\n deps.logger.warn(`Message sent to DLQ: ${dlqTopic}`);\n } catch (error) {\n deps.logger.error(\n `Failed to send message to DLQ ${dlqTopic}:`,\n toError(error).stack,\n );\n }\n}\n\n// ── Retry pipeline ──────────────────────────────────────────────────\n\nexport interface ExecuteWithRetryContext<T extends TopicMapConstraint<T>> {\n envelope: EventEnvelope<any> | EventEnvelope<any>[];\n rawMessages: string[];\n interceptors: ConsumerInterceptor<T>[];\n dlq: boolean;\n retry?: RetryOptions;\n isBatch?: boolean;\n}\n\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 },\n): Promise<void> {\n const { envelope, rawMessages, interceptors, dlq, retry, isBatch } = ctx;\n const maxAttempts = retry ? retry.maxRetries + 1 : 1;\n const backoffMs = retry?.backoffMs ?? 1000;\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 // Collect instrumentation cleanup functions\n const cleanups: (() => void)[] = [];\n\n try {\n // Instrumentation: beforeConsume\n for (const env of envelopes) {\n for (const inst of deps.instrumentation) {\n const cleanup = inst.beforeConsume?.(env);\n if (typeof cleanup === \"function\") cleanups.push(cleanup);\n }\n }\n\n // Consumer interceptors: before\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.before?.(env);\n }\n }\n\n await fn();\n\n // Consumer interceptors: after\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.after?.(env);\n }\n }\n\n // Instrumentation: cleanup (end spans etc.)\n for (const cleanup of cleanups) cleanup();\n\n return;\n } catch (error) {\n const err = toError(error);\n const isLastAttempt = attempt === maxAttempts;\n\n // Instrumentation: onConsumeError\n for (const env of envelopes) {\n for (const inst of deps.instrumentation) {\n inst.onConsumeError?.(env, err);\n }\n }\n // Instrumentation: cleanup even on error\n for (const cleanup of cleanups) cleanup();\n\n if (isLastAttempt && maxAttempts > 1) {\n const exhaustedError = new KafkaRetryExhaustedError(\n topic,\n envelopes.map((e) => e.payload),\n maxAttempts,\n { cause: err },\n );\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.onError?.(env, exhaustedError);\n }\n }\n } else {\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.onError?.(env, err);\n }\n }\n }\n\n deps.logger.error(\n `Error processing ${isBatch ? \"batch\" : \"message\"} from topic ${topic} (attempt ${attempt}/${maxAttempts}):`,\n err.stack,\n );\n\n if (isLastAttempt) {\n if (dlq) {\n for (const raw of rawMessages) {\n await sendToDlq(topic, raw, deps);\n }\n }\n } else {\n await sleep(backoffMs * attempt);\n }\n }\n }\n}\n","import type { KafkaJS } from \"@confluentinc/kafka-javascript\";\nimport type { KafkaLogger, SubscribeRetryOptions } from \"./types\";\nimport { toError, sleep } from \"./consumer-pipeline\";\n\nexport async function subscribeWithRetry(\n consumer: KafkaJS.Consumer,\n topics: string[],\n logger: KafkaLogger,\n retryOpts?: SubscribeRetryOptions,\n): Promise<void> {\n const maxAttempts = retryOpts?.retries ?? 5;\n const backoffMs = retryOpts?.backoffMs ?? 5000;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await consumer.subscribe({ topics });\n return;\n } catch (error) {\n if (attempt === maxAttempts) throw error;\n const msg = toError(error).message;\n logger.warn(\n `Failed to subscribe to [${topics.join(\", \")}] (attempt ${attempt}/${maxAttempts}): ${msg}. Retrying in ${backoffMs}ms...`,\n );\n await sleep(backoffMs);\n }\n }\n}\n","/**\n * Any validation library with a `.parse()` method.\n * Works with Zod, Valibot, ArkType, or any custom validator.\n *\n * @example\n * ```ts\n * import { z } from 'zod';\n * const schema: SchemaLike<{ id: string }> = z.object({ id: z.string() });\n * ```\n */\nexport interface SchemaLike<T = any> {\n parse(data: unknown): 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 — type provided explicitly:\n * const OrderCreated = topic('order.created')<{ 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 const fn = <M extends Record<string, any>>(): TopicDescriptor<N, M> => ({\n __topic: name,\n __type: undefined as unknown as M,\n });\n\n fn.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 return fn;\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')<{ orderId: string }>();\n * const OrderCompleted = topic('order.completed')<{ completedAt: string }>();\n *\n * type MyTopics = TopicsFrom<typeof OrderCreated | typeof OrderCompleted>;\n * // { 'order.created': { orderId: string }; 'order.completed': { completedAt: string } }\n * ```\n */\nexport type TopicsFrom<D extends TopicDescriptor<any, any>> = {\n [K in D as K[\"__topic\"]]: K[\"__type\"];\n};\n"],"mappings":"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","names":["topic","topic","topic","topic"]}
|
|
1
|
+
{"version":3,"sources":["../src/core.ts","../src/client/kafka.client.ts","../src/client/envelope.ts","../src/client/errors.ts","../src/client/consumer-pipeline.ts","../src/client/subscribe-retry.ts","../src/client/topic.ts"],"sourcesContent":["export * from \"./client/kafka.client\";\nexport * from \"./client/topic\";\nexport * from \"./client/errors\";\nexport * from \"./client/envelope\";\n","import { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Kafka = KafkaJS.Kafka;\ntype Producer = KafkaJS.Producer;\ntype Consumer = KafkaJS.Consumer;\ntype Admin = KafkaJS.Admin;\nconst { Kafka: KafkaClass, logLevel: KafkaLogLevel } = KafkaJS;\nimport { TopicDescriptor, SchemaLike } from \"./topic\";\nimport {\n buildEnvelopeHeaders,\n decodeHeaders,\n extractEnvelope,\n runWithEnvelopeContext,\n} from \"./envelope\";\nimport type { EventEnvelope } from \"./envelope\";\nimport {\n toError,\n parseJsonMessage,\n validateWithSchema,\n executeWithRetry,\n} from \"./consumer-pipeline\";\nimport { subscribeWithRetry } from \"./subscribe-retry\";\nimport type {\n ClientId,\n GroupId,\n SendOptions,\n MessageHeaders,\n BatchMessageItem,\n ConsumerOptions,\n TransactionContext,\n TopicMapConstraint,\n IKafkaClient,\n KafkaClientOptions,\n KafkaInstrumentation,\n KafkaLogger,\n BatchMeta,\n} from \"./types\";\n\n// Re-export all types so existing `import { ... } from './kafka.client'` keeps working\nexport * from \"./types\";\n\n/**\n * Type-safe Kafka client.\n * Wraps @confluentinc/kafka-javascript (librdkafka) with JSON serialization,\n * retries, DLQ, transactions, and interceptors.\n *\n * @typeParam T - Topic-to-message type mapping for compile-time safety.\n */\nexport class KafkaClient<\n T extends TopicMapConstraint<T>,\n> implements IKafkaClient<T> {\n private readonly kafka: Kafka;\n private readonly producer: Producer;\n private txProducer: Producer | undefined;\n private readonly consumers = new Map<string, Consumer>();\n private readonly admin: Admin;\n private readonly logger: KafkaLogger;\n private readonly autoCreateTopicsEnabled: boolean;\n private readonly strictSchemasEnabled: boolean;\n private readonly numPartitions: number;\n private readonly ensuredTopics = new Set<string>();\n private readonly defaultGroupId: string;\n private readonly schemaRegistry = new Map<string, SchemaLike>();\n private readonly runningConsumers = new Map<string, \"eachMessage\" | \"eachBatch\">();\n private readonly instrumentation: KafkaInstrumentation[];\n\n private isAdminConnected = false;\n public readonly clientId: ClientId;\n\n constructor(\n clientId: ClientId,\n groupId: GroupId,\n brokers: string[],\n options?: KafkaClientOptions,\n ) {\n this.clientId = clientId;\n this.defaultGroupId = groupId;\n this.logger = options?.logger ?? {\n log: (msg) => console.log(`[KafkaClient:${clientId}] ${msg}`),\n warn: (msg, ...args) => console.warn(`[KafkaClient:${clientId}] ${msg}`, ...args),\n error: (msg, ...args) => console.error(`[KafkaClient:${clientId}] ${msg}`, ...args),\n };\n this.autoCreateTopicsEnabled = options?.autoCreateTopics ?? false;\n this.strictSchemasEnabled = options?.strictSchemas ?? true;\n this.numPartitions = options?.numPartitions ?? 1;\n this.instrumentation = options?.instrumentation ?? [];\n\n this.kafka = new KafkaClass({\n kafkaJS: {\n clientId: this.clientId,\n brokers,\n logLevel: KafkaLogLevel.ERROR,\n },\n });\n this.producer = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n },\n });\n this.admin = this.kafka.admin();\n }\n\n // ── Send ─────────────────────────────────────────────────────────\n\n /** Send a single typed message. Accepts a topic key or a TopicDescriptor. */\n public async sendMessage<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(descriptor: D, message: D[\"__type\"], options?: SendOptions): Promise<void>;\n public async sendMessage<K extends keyof T>(\n topic: K,\n message: T[K],\n options?: SendOptions,\n ): Promise<void>;\n public async sendMessage(\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ): Promise<void> {\n const payload = this.buildSendPayload(topicOrDesc, [\n {\n value: message,\n key: options.key,\n headers: options.headers,\n correlationId: options.correlationId,\n schemaVersion: options.schemaVersion,\n eventId: options.eventId,\n },\n ]);\n await this.ensureTopic(payload.topic);\n await this.producer.send(payload);\n for (const inst of this.instrumentation) {\n inst.afterSend?.(payload.topic);\n }\n }\n\n /** Send multiple typed messages in one call. Accepts a topic key or a TopicDescriptor. */\n public async sendBatch<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n descriptor: D,\n messages: Array<BatchMessageItem<D[\"__type\"]>>,\n ): Promise<void>;\n public async sendBatch<K extends keyof T>(\n topic: K,\n messages: Array<BatchMessageItem<T[K]>>,\n ): Promise<void>;\n public async sendBatch(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n ): Promise<void> {\n const payload = this.buildSendPayload(topicOrDesc, messages);\n await this.ensureTopic(payload.topic);\n await this.producer.send(payload);\n for (const inst of this.instrumentation) {\n inst.afterSend?.(payload.topic);\n }\n }\n\n /** Execute multiple sends atomically. Commits on success, aborts on error. */\n public async transaction(\n fn: (ctx: TransactionContext<T>) => Promise<void>,\n ): Promise<void> {\n if (!this.txProducer) {\n this.txProducer = this.kafka.producer({\n kafkaJS: {\n acks: -1,\n idempotent: true,\n transactionalId: `${this.clientId}-tx`,\n maxInFlightRequests: 1,\n },\n });\n await this.txProducer.connect();\n }\n const tx = await this.txProducer.transaction();\n try {\n const ctx: TransactionContext<T> = {\n send: async (\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ) => {\n const payload = this.buildSendPayload(topicOrDesc, [\n {\n value: message,\n key: options.key,\n headers: options.headers,\n correlationId: options.correlationId,\n schemaVersion: options.schemaVersion,\n eventId: options.eventId,\n },\n ]);\n await this.ensureTopic(payload.topic);\n await tx.send(payload);\n },\n sendBatch: async (topicOrDesc: any, messages: BatchMessageItem<any>[]) => {\n const payload = this.buildSendPayload(topicOrDesc, messages);\n await this.ensureTopic(payload.topic);\n await tx.send(payload);\n },\n };\n await fn(ctx);\n await tx.commit();\n } catch (error) {\n try {\n await tx.abort();\n } catch (abortError) {\n this.logger.error(\n \"Failed to abort transaction:\",\n toError(abortError).message,\n );\n }\n throw error;\n }\n }\n\n // ── Producer lifecycle ───────────────────────────────────────────\n\n /** Connect the idempotent producer. Called automatically by `KafkaModule.register()`. */\n public async connectProducer(): Promise<void> {\n await this.producer.connect();\n this.logger.log(\"Producer connected\");\n }\n\n public async disconnectProducer(): Promise<void> {\n await this.producer.disconnect();\n this.logger.log(\"Producer disconnected\");\n }\n\n // ── Consumer: eachMessage ────────────────────────────────────────\n\n /** Subscribe to topics and start consuming messages with the given handler. */\n public async startConsumer<K extends Array<keyof T>>(\n topics: K,\n handleMessage: (envelope: EventEnvelope<T[K[number]]>) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<void>;\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<void>;\n public async startConsumer(\n topics: any[],\n handleMessage: (envelope: EventEnvelope<any>) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<void> {\n const { consumer, schemaMap, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachMessage\", options);\n\n const deps = { logger: this.logger, producer: this.producer, instrumentation: this.instrumentation };\n\n await consumer.run({\n eachMessage: async ({ topic, partition, message }) => {\n if (!message.value) {\n this.logger.warn(`Received empty message from topic ${topic}`);\n return;\n }\n\n const raw = message.value.toString();\n const parsed = parseJsonMessage(raw, topic, this.logger);\n if (parsed === null) return;\n\n const validated = await validateWithSchema(\n parsed, raw, topic, schemaMap, interceptors, dlq, deps,\n );\n if (validated === null) return;\n\n const headers = decodeHeaders(message.headers);\n const envelope = extractEnvelope(\n validated, headers, topic, partition, message.offset,\n );\n\n await executeWithRetry(\n () =>\n runWithEnvelopeContext(\n { correlationId: envelope.correlationId, traceparent: envelope.traceparent },\n () => handleMessage(envelope),\n ),\n { envelope, rawMessages: [raw], interceptors, dlq, retry },\n deps,\n );\n },\n });\n\n this.runningConsumers.set(gid, \"eachMessage\");\n }\n\n // ── Consumer: eachBatch ──────────────────────────────────────────\n\n /** Subscribe to topics and consume messages in batches. */\n public async startBatchConsumer<K extends Array<keyof T>>(\n topics: K,\n handleBatch: (\n envelopes: EventEnvelope<T[K[number]]>[],\n meta: BatchMeta,\n ) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<void>;\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<void>;\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<void> {\n const { consumer, schemaMap, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachBatch\", options);\n\n const deps = { logger: this.logger, producer: this.producer, instrumentation: this.instrumentation };\n\n await consumer.run({\n eachBatch: async ({\n batch,\n heartbeat,\n resolveOffset,\n commitOffsetsIfNecessary,\n }) => {\n const envelopes: EventEnvelope<any>[] = [];\n const rawMessages: string[] = [];\n\n for (const message of batch.messages) {\n if (!message.value) {\n this.logger.warn(\n `Received empty message from topic ${batch.topic}`,\n );\n continue;\n }\n\n const raw = message.value.toString();\n const parsed = parseJsonMessage(raw, batch.topic, this.logger);\n if (parsed === null) continue;\n\n const validated = await validateWithSchema(\n parsed, raw, batch.topic, schemaMap, interceptors, dlq, deps,\n );\n if (validated === null) continue;\n\n const headers = decodeHeaders(message.headers);\n envelopes.push(\n extractEnvelope(validated, headers, batch.topic, batch.partition, message.offset),\n );\n rawMessages.push(raw);\n }\n\n if (envelopes.length === 0) return;\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 () => handleBatch(envelopes, meta),\n {\n envelope: envelopes,\n rawMessages: batch.messages\n .filter((m) => m.value)\n .map((m) => m.value!.toString()),\n interceptors,\n dlq,\n retry,\n isBatch: true,\n },\n deps,\n );\n },\n });\n\n this.runningConsumers.set(gid, \"eachBatch\");\n }\n\n // ── Consumer lifecycle ───────────────────────────────────────────\n\n public async stopConsumer(): Promise<void> {\n const tasks = [];\n for (const consumer of this.consumers.values()) {\n tasks.push(consumer.disconnect());\n }\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.logger.log(\"All consumers disconnected\");\n }\n\n /** Check broker connectivity and return status, clientId, and available topics. */\n public async checkStatus(): Promise<{ status: 'up'; clientId: string; topics: string[] }> {\n if (!this.isAdminConnected) {\n await this.admin.connect();\n this.isAdminConnected = true;\n }\n const topics = await this.admin.listTopics();\n return { status: 'up', clientId: this.clientId, topics };\n }\n\n public getClientId(): ClientId {\n return this.clientId;\n }\n\n /** Gracefully disconnect producer, all consumers, and admin. */\n public async disconnect(): Promise<void> {\n const tasks: Promise<void>[] = [this.producer.disconnect()];\n if (this.txProducer) {\n tasks.push(this.txProducer.disconnect());\n this.txProducer = undefined;\n }\n for (const consumer of this.consumers.values()) {\n tasks.push(consumer.disconnect());\n }\n if (this.isAdminConnected) {\n tasks.push(this.admin.disconnect());\n this.isAdminConnected = false;\n }\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.logger.log(\"All connections closed\");\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private getOrCreateConsumer(\n groupId: string,\n fromBeginning: boolean,\n autoCommit: boolean,\n ): Consumer {\n if (!this.consumers.has(groupId)) {\n this.consumers.set(\n groupId,\n this.kafka.consumer({\n kafkaJS: { groupId, fromBeginning, autoCommit },\n }),\n );\n }\n return this.consumers.get(groupId)!;\n }\n\n private 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 TopicDescriptor).__topic;\n }\n return String(topicOrDescriptor);\n }\n\n private async ensureTopic(topic: string): Promise<void> {\n if (!this.autoCreateTopicsEnabled || this.ensuredTopics.has(topic)) return;\n if (!this.isAdminConnected) {\n await this.admin.connect();\n this.isAdminConnected = true;\n }\n await this.admin.createTopics({\n topics: [{ topic, numPartitions: this.numPartitions }],\n });\n this.ensuredTopics.add(topic);\n }\n\n /** Register schema from descriptor into global registry (side-effect). */\n private registerSchema(topicOrDesc: any): void {\n if (topicOrDesc?.__schema) {\n const topic = this.resolveTopicName(topicOrDesc);\n this.schemaRegistry.set(topic, topicOrDesc.__schema);\n }\n }\n\n /** Validate message against schema. Pure — no side-effects on registry. */\n private validateMessage(topicOrDesc: any, message: any): any {\n if (topicOrDesc?.__schema) {\n return topicOrDesc.__schema.parse(message);\n }\n if (this.strictSchemasEnabled && typeof topicOrDesc === \"string\") {\n const schema = this.schemaRegistry.get(topicOrDesc);\n if (schema) return schema.parse(message);\n }\n return message;\n }\n\n /**\n * Build a kafkajs-ready send payload.\n * Handles: topic resolution, schema registration, validation, JSON serialization,\n * envelope header generation, and instrumentation hooks.\n */\n private buildSendPayload(\n topicOrDesc: any,\n messages: Array<BatchMessageItem<any>>,\n ): { topic: string; messages: Array<{ value: string; key: string | null; headers: MessageHeaders }> } {\n this.registerSchema(topicOrDesc);\n const topic = this.resolveTopicName(topicOrDesc);\n return {\n topic,\n messages: messages.map((m) => {\n const envelopeHeaders = buildEnvelopeHeaders({\n correlationId: m.correlationId,\n schemaVersion: m.schemaVersion,\n eventId: m.eventId,\n headers: m.headers,\n });\n\n // Let instrumentation hooks mutate headers (e.g. OTel injects traceparent)\n for (const inst of this.instrumentation) {\n inst.beforeSend?.(topic, envelopeHeaders);\n }\n\n return {\n value: JSON.stringify(this.validateMessage(topicOrDesc, m.value)),\n key: m.key ?? null,\n headers: envelopeHeaders,\n };\n }),\n };\n }\n\n /** Shared consumer setup: groupId check, schema map, connect, subscribe. */\n private async setupConsumer(\n topics: any[],\n mode: \"eachMessage\" | \"eachBatch\",\n options: ConsumerOptions<T>,\n ) {\n const {\n groupId: optGroupId,\n fromBeginning = false,\n retry,\n dlq = false,\n interceptors = [],\n schemas: optionSchemas,\n } = options;\n\n const gid = optGroupId || this.defaultGroupId;\n const existingMode = this.runningConsumers.get(gid);\n const oppositeMode = mode === \"eachMessage\" ? \"eachBatch\" : \"eachMessage\";\n if (existingMode === oppositeMode) {\n throw new Error(\n `Cannot use ${mode} on consumer group \"${gid}\" — it is already running with ${oppositeMode}. ` +\n `Use a different groupId for this consumer.`,\n );\n }\n\n const consumer = this.getOrCreateConsumer(gid, fromBeginning, options.autoCommit ?? true);\n const schemaMap = this.buildSchemaMap(topics, optionSchemas);\n\n const topicNames = (topics as any[]).map((t: any) =>\n this.resolveTopicName(t),\n );\n\n // Ensure topics exist before subscribing — librdkafka errors on unknown topics\n for (const t of topicNames) {\n await this.ensureTopic(t);\n }\n if (dlq) {\n for (const t of topicNames) {\n await this.ensureTopic(`${t}.dlq`);\n }\n }\n\n await consumer.connect();\n await subscribeWithRetry(consumer, topicNames, this.logger, options.subscribeRetry);\n\n this.logger.log(\n `${mode === \"eachBatch\" ? \"Batch consumer\" : \"Consumer\"} subscribed to topics: ${topicNames.join(\", \")}`,\n );\n\n return { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry };\n }\n\n private buildSchemaMap(\n topics: any[],\n optionSchemas?: Map<string, SchemaLike>,\n ): Map<string, SchemaLike> {\n const schemaMap = new Map<string, SchemaLike>();\n for (const t of topics) {\n if (t?.__schema) {\n const name = this.resolveTopicName(t);\n schemaMap.set(name, t.__schema);\n this.schemaRegistry.set(name, t.__schema);\n }\n }\n if (optionSchemas) {\n for (const [k, v] of optionSchemas) {\n schemaMap.set(k, v);\n this.schemaRegistry.set(k, v);\n }\n }\n return schemaMap;\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\n// ── EventEnvelope ────────────────────────────────────────────────────\n\n/**\n * Typed wrapper combining a parsed message payload with Kafka metadata\n * and envelope headers.\n *\n * On **send**, the library auto-generates envelope headers\n * (`x-event-id`, `x-correlation-id`, `x-timestamp`, `x-schema-version`).\n *\n * On **consume**, the library extracts those headers and assembles\n * an `EventEnvelope` that is passed to the handler.\n */\nexport interface EventEnvelope<T> {\n /** Deserialized + validated message body. */\n payload: T;\n /** Topic the message was produced to / consumed from. */\n topic: string;\n /** Kafka partition (consume-side only, `-1` on send). */\n partition: number;\n /** Kafka offset (consume-side only, empty string on send). */\n offset: string;\n /** ISO-8601 timestamp set by the producer. */\n timestamp: string;\n /** Unique ID for this event (UUID v4). */\n eventId: string;\n /** Correlation ID — auto-propagated via AsyncLocalStorage. */\n correlationId: string;\n /** Schema version of the payload. */\n schemaVersion: number;\n /** W3C Trace Context `traceparent` header (set by OTel instrumentation). */\n traceparent?: string;\n /** All decoded Kafka headers for extensibility. */\n headers: MessageHeaders;\n}\n\n// ── AsyncLocalStorage context ────────────────────────────────────────\n\ninterface EnvelopeCtx {\n correlationId: string;\n traceparent?: string;\n}\n\nconst envelopeStorage = new AsyncLocalStorage<EnvelopeCtx>();\n\n/** Read the current envelope context (correlationId / traceparent) from ALS. */\nexport function getEnvelopeContext(): EnvelopeCtx | undefined {\n return envelopeStorage.getStore();\n}\n\n/** Execute `fn` inside an envelope context so nested sends inherit correlationId. */\nexport function runWithEnvelopeContext<R>(\n ctx: EnvelopeCtx,\n fn: () => R,\n): 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: Record<string, Buffer | string | (Buffer | string)[] | undefined> | 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 result[key] = value.map((v) => (Buffer.isBuffer(v) ? v.toString() : v)).join(\",\");\n } else {\n result[key] = Buffer.isBuffer(value) ? value.toString() : value;\n }\n }\n return result;\n}\n\n/**\n * Build an `EventEnvelope` from a consumed kafkajs message.\n * Tolerates missing envelope headers — generates defaults so messages\n * from non-envelope producers still work.\n */\nexport function extractEnvelope<T>(\n payload: T,\n headers: MessageHeaders,\n topic: string,\n partition: number,\n offset: string,\n): EventEnvelope<T> {\n return {\n payload,\n topic,\n partition,\n offset,\n eventId: headers[HEADER_EVENT_ID] ?? randomUUID(),\n correlationId: headers[HEADER_CORRELATION_ID] ?? randomUUID(),\n timestamp: headers[HEADER_TIMESTAMP] ?? new Date().toISOString(),\n schemaVersion: Number(headers[HEADER_SCHEMA_VERSION] ?? 1),\n traceparent: headers[HEADER_TRACEPARENT],\n headers,\n };\n}\n","/** Error thrown when a consumer message handler fails. */\nexport class KafkaProcessingError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n message: string,\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(message, options);\n this.name = \"KafkaProcessingError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when schema validation fails on send or consume. */\nexport class KafkaValidationError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(`Schema validation failed for topic \"${topic}\"`, options);\n this.name = \"KafkaValidationError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when all retry attempts are exhausted for a message. */\nexport class KafkaRetryExhaustedError extends KafkaProcessingError {\n constructor(\n topic: string,\n originalMessage: unknown,\n public readonly attempts: number,\n options?: { cause?: Error },\n ) {\n super(\n `Message processing failed after ${attempts} attempts on topic \"${topic}\"`,\n topic,\n originalMessage,\n options,\n );\n this.name = \"KafkaRetryExhaustedError\";\n }\n}\n","import type { KafkaJS } from \"@confluentinc/kafka-javascript\";\ntype Producer = KafkaJS.Producer;\nimport type { EventEnvelope } from \"./envelope\";\nimport { extractEnvelope } from \"./envelope\";\nimport { KafkaRetryExhaustedError, KafkaValidationError } from \"./errors\";\nimport type { SchemaLike } from \"./topic\";\nimport type {\n ConsumerInterceptor,\n KafkaInstrumentation,\n KafkaLogger,\n RetryOptions,\n TopicMapConstraint,\n} from \"./types\";\n\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nexport function toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ── JSON parsing ────────────────────────────────────────────────────\n\n/** Parse raw message as JSON. Returns null on failure (logs error). */\nexport function parseJsonMessage(\n raw: string,\n topic: string,\n logger: KafkaLogger,\n): any | null {\n try {\n return JSON.parse(raw);\n } catch (error) {\n logger.error(\n `Failed to parse message from topic ${topic}:`,\n toError(error).stack,\n );\n return null;\n }\n}\n\n// ── Schema validation ───────────────────────────────────────────────\n\n/**\n * Validate a parsed message against the schema map.\n * On failure: logs error, sends to DLQ if enabled, calls interceptor.onError.\n * Returns validated message or null.\n */\nexport async function validateWithSchema<T extends TopicMapConstraint<T>>(\n message: any,\n raw: string,\n topic: string,\n schemaMap: Map<string, SchemaLike>,\n interceptors: ConsumerInterceptor<T>[],\n dlq: boolean,\n deps: { logger: KafkaLogger; producer: Producer },\n): Promise<any | null> {\n const schema = schemaMap.get(topic);\n if (!schema) return message;\n\n try {\n return schema.parse(message);\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) await sendToDlq(topic, raw, deps);\n // Validation errors don't have an envelope yet — call onError with a minimal envelope\n const errorEnvelope = extractEnvelope(message, {}, topic, -1, \"\");\n for (const interceptor of interceptors) {\n await interceptor.onError?.(errorEnvelope, validationError);\n }\n return null;\n }\n}\n\n// ── DLQ ─────────────────────────────────────────────────────────────\n\nexport async function sendToDlq(\n topic: string,\n rawMessage: string,\n deps: { logger: KafkaLogger; producer: Producer },\n): Promise<void> {\n const dlqTopic = `${topic}.dlq`;\n try {\n await deps.producer.send({\n topic: dlqTopic,\n messages: [{ value: rawMessage }],\n });\n deps.logger.warn(`Message sent to DLQ: ${dlqTopic}`);\n } catch (error) {\n deps.logger.error(\n `Failed to send message to DLQ ${dlqTopic}:`,\n toError(error).stack,\n );\n }\n}\n\n// ── Retry pipeline ──────────────────────────────────────────────────\n\nexport interface ExecuteWithRetryContext<T extends TopicMapConstraint<T>> {\n envelope: EventEnvelope<any> | EventEnvelope<any>[];\n rawMessages: string[];\n interceptors: ConsumerInterceptor<T>[];\n dlq: boolean;\n retry?: RetryOptions;\n isBatch?: boolean;\n}\n\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 },\n): Promise<void> {\n const { envelope, rawMessages, interceptors, dlq, retry, isBatch } = ctx;\n const maxAttempts = retry ? retry.maxRetries + 1 : 1;\n const backoffMs = retry?.backoffMs ?? 1000;\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 // Collect instrumentation cleanup functions\n const cleanups: (() => void)[] = [];\n\n try {\n // Instrumentation: beforeConsume\n for (const env of envelopes) {\n for (const inst of deps.instrumentation) {\n const cleanup = inst.beforeConsume?.(env);\n if (typeof cleanup === \"function\") cleanups.push(cleanup);\n }\n }\n\n // Consumer interceptors: before\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.before?.(env);\n }\n }\n\n await fn();\n\n // Consumer interceptors: after\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.after?.(env);\n }\n }\n\n // Instrumentation: cleanup (end spans etc.)\n for (const cleanup of cleanups) cleanup();\n\n return;\n } catch (error) {\n const err = toError(error);\n const isLastAttempt = attempt === maxAttempts;\n\n // Instrumentation: onConsumeError\n for (const env of envelopes) {\n for (const inst of deps.instrumentation) {\n inst.onConsumeError?.(env, err);\n }\n }\n // Instrumentation: cleanup even on error\n for (const cleanup of cleanups) cleanup();\n\n if (isLastAttempt && maxAttempts > 1) {\n const exhaustedError = new KafkaRetryExhaustedError(\n topic,\n envelopes.map((e) => e.payload),\n maxAttempts,\n { cause: err },\n );\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.onError?.(env, exhaustedError);\n }\n }\n } else {\n for (const env of envelopes) {\n for (const interceptor of interceptors) {\n await interceptor.onError?.(env, err);\n }\n }\n }\n\n deps.logger.error(\n `Error processing ${isBatch ? \"batch\" : \"message\"} from topic ${topic} (attempt ${attempt}/${maxAttempts}):`,\n err.stack,\n );\n\n if (isLastAttempt) {\n if (dlq) {\n for (const raw of rawMessages) {\n await sendToDlq(topic, raw, deps);\n }\n }\n } else {\n await sleep(backoffMs * attempt);\n }\n }\n }\n}\n","import type { KafkaJS } from \"@confluentinc/kafka-javascript\";\nimport type { KafkaLogger, SubscribeRetryOptions } from \"./types\";\nimport { toError, sleep } from \"./consumer-pipeline\";\n\nexport async function subscribeWithRetry(\n consumer: KafkaJS.Consumer,\n topics: string[],\n logger: KafkaLogger,\n retryOpts?: SubscribeRetryOptions,\n): Promise<void> {\n const maxAttempts = retryOpts?.retries ?? 5;\n const backoffMs = retryOpts?.backoffMs ?? 5000;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await consumer.subscribe({ topics });\n return;\n } catch (error) {\n if (attempt === maxAttempts) throw error;\n const msg = toError(error).message;\n logger.warn(\n `Failed to subscribe to [${topics.join(\", \")}] (attempt ${attempt}/${maxAttempts}): ${msg}. Retrying in ${backoffMs}ms...`,\n );\n await sleep(backoffMs);\n }\n }\n}\n","/**\n * Any validation library with a `.parse()` method.\n * Works with Zod, Valibot, ArkType, or any custom validator.\n *\n * @example\n * ```ts\n * import { z } from 'zod';\n * const schema: SchemaLike<{ id: string }> = z.object({ id: z.string() });\n * ```\n */\nexport interface SchemaLike<T = any> {\n parse(data: unknown): 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 — type provided explicitly:\n * const OrderCreated = topic('order.created')<{ 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 const fn = <M extends Record<string, any>>(): TopicDescriptor<N, M> => ({\n __topic: name,\n __type: undefined as unknown as M,\n });\n\n fn.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 return fn;\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')<{ orderId: string }>();\n * const OrderCompleted = topic('order.completed')<{ completedAt: string }>();\n *\n * type MyTopics = TopicsFrom<typeof OrderCreated | typeof OrderCompleted>;\n * // { 'order.created': { orderId: string }; 'order.completed': { completedAt: string } }\n * ```\n */\nexport type TopicsFrom<D extends TopicDescriptor<any, any>> = {\n [K in D as K[\"__topic\"]]: K[\"__type\"];\n};\n"],"mappings":"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","names":["topic","topic","topic","topic"]}
|