@eventferry/kafka 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Samet GOKTEPE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @eventferry/kafka
2
+
3
+ Kafka/Redpanda publisher for [eventferry](https://github.com/your-org/eventferry). One `KafkaPublisher` over two interchangeable drivers: `kafkajs` and `@confluentinc/kafka-javascript`. Idempotent + transactional producers, DLQ routing.
4
+
5
+ See the [root README](../../README.md) for usage.
package/dist/index.cjs ADDED
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ConfluentDriver: () => ConfluentDriver,
34
+ KafkaJsDriver: () => KafkaJsDriver,
35
+ KafkaPublisher: () => KafkaPublisher
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/kafkajs-driver.ts
40
+ var KafkaJsDriver = class {
41
+ transactional;
42
+ producer = null;
43
+ opts;
44
+ constructor(opts) {
45
+ this.opts = opts;
46
+ this.transactional = opts.transactional ?? false;
47
+ if (this.transactional && !opts.transactionalId) {
48
+ throw new Error(
49
+ "KafkaJsDriver: transactionalId is required when transactional=true"
50
+ );
51
+ }
52
+ }
53
+ async connect() {
54
+ this.producer = await this.createProducer();
55
+ await this.producer.connect();
56
+ }
57
+ /**
58
+ * Construct the underlying kafkajs producer. Overridable as a test seam so
59
+ * the send/transaction logic can be exercised without a real broker.
60
+ */
61
+ async createProducer() {
62
+ const mod = await importKafkaJs();
63
+ const kafka = new mod.Kafka({
64
+ clientId: this.opts.clientId ?? "eventferry",
65
+ brokers: this.opts.brokers,
66
+ ssl: this.opts.ssl,
67
+ sasl: this.opts.sasl
68
+ });
69
+ return kafka.producer({
70
+ idempotent: this.opts.idempotent ?? true,
71
+ maxInFlightRequests: this.transactional ? 1 : void 0,
72
+ transactionalId: this.transactional ? this.opts.transactionalId : void 0
73
+ });
74
+ }
75
+ async disconnect() {
76
+ await this.producer?.disconnect();
77
+ this.producer = null;
78
+ }
79
+ async sendBatch(messages) {
80
+ if (!this.producer) throw new Error("KafkaJsDriver not connected");
81
+ const topicMessages = groupByTopic(messages, this.opts.compression);
82
+ if (this.transactional) {
83
+ const txn = await this.producer.transaction();
84
+ try {
85
+ await txn.sendBatch({ topicMessages, acks: this.opts.acks ?? -1 });
86
+ await txn.commit();
87
+ return messages.map((m) => ({ recordId: m.recordId, ok: true }));
88
+ } catch (err) {
89
+ await txn.abort().catch(() => void 0);
90
+ const error = err instanceof Error ? err : new Error(String(err));
91
+ return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));
92
+ }
93
+ }
94
+ try {
95
+ await this.producer.sendBatch({ topicMessages, acks: this.opts.acks ?? -1 });
96
+ return messages.map((m) => ({ recordId: m.recordId, ok: true }));
97
+ } catch (err) {
98
+ const error = err instanceof Error ? err : new Error(String(err));
99
+ return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));
100
+ }
101
+ }
102
+ };
103
+ function groupByTopic(messages, compression) {
104
+ const byTopic = /* @__PURE__ */ new Map();
105
+ for (const m of messages) {
106
+ const arr = byTopic.get(m.topic) ?? [];
107
+ arr.push({
108
+ key: m.key,
109
+ value: m.value,
110
+ headers: m.headers
111
+ });
112
+ byTopic.set(m.topic, arr);
113
+ }
114
+ return [...byTopic.entries()].map(([topic, msgs]) => ({
115
+ topic,
116
+ messages: msgs,
117
+ ...compression && compression !== "none" ? { compression } : {}
118
+ }));
119
+ }
120
+ async function importKafkaJs() {
121
+ try {
122
+ return await import("kafkajs");
123
+ } catch {
124
+ throw new Error(
125
+ 'Driver "kafkajs" selected but the "kafkajs" package is not installed. Run: npm i kafkajs'
126
+ );
127
+ }
128
+ }
129
+
130
+ // src/confluent-driver.ts
131
+ var ConfluentDriver = class {
132
+ transactional;
133
+ producer = null;
134
+ opts;
135
+ constructor(opts) {
136
+ this.opts = opts;
137
+ this.transactional = opts.transactional ?? false;
138
+ if (this.transactional && !opts.transactionalId) {
139
+ throw new Error(
140
+ "ConfluentDriver: transactionalId is required when transactional=true"
141
+ );
142
+ }
143
+ }
144
+ async connect() {
145
+ this.producer = await this.createProducer();
146
+ await this.producer.connect();
147
+ }
148
+ /**
149
+ * Construct the underlying confluent producer. Overridable as a test seam so
150
+ * the send/transaction logic can be exercised without a real broker.
151
+ */
152
+ async createProducer() {
153
+ const mod = await importConfluent();
154
+ const kafka = new mod.KafkaJS.Kafka({
155
+ kafkaJS: {
156
+ clientId: this.opts.clientId ?? "eventferry",
157
+ brokers: this.opts.brokers,
158
+ ssl: this.opts.ssl,
159
+ sasl: this.opts.sasl
160
+ }
161
+ });
162
+ return kafka.producer({
163
+ kafkaJS: {
164
+ idempotent: this.opts.idempotent ?? true,
165
+ ...this.transactional ? { transactionalId: this.opts.transactionalId } : {}
166
+ }
167
+ });
168
+ }
169
+ async disconnect() {
170
+ await this.producer?.disconnect();
171
+ this.producer = null;
172
+ }
173
+ async sendBatch(messages) {
174
+ if (!this.producer) throw new Error("ConfluentDriver not connected");
175
+ const topicMessages = groupByTopic2(messages);
176
+ const acks = this.opts.acks ?? -1;
177
+ const compression = this.opts.compression;
178
+ const doSends = async (target) => {
179
+ for (const tm of topicMessages) {
180
+ await target.send({
181
+ topic: tm.topic,
182
+ messages: tm.messages,
183
+ acks,
184
+ ...compression && compression !== "none" ? { compression } : {}
185
+ });
186
+ }
187
+ };
188
+ if (this.transactional) {
189
+ const txn = await this.producer.transaction();
190
+ try {
191
+ await doSends(txn);
192
+ await txn.commit();
193
+ return messages.map((m) => ({ recordId: m.recordId, ok: true }));
194
+ } catch (err) {
195
+ await txn.abort().catch(() => void 0);
196
+ const error = err instanceof Error ? err : new Error(String(err));
197
+ return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));
198
+ }
199
+ }
200
+ try {
201
+ await doSends(this.producer);
202
+ return messages.map((m) => ({ recordId: m.recordId, ok: true }));
203
+ } catch (err) {
204
+ const error = err instanceof Error ? err : new Error(String(err));
205
+ return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));
206
+ }
207
+ }
208
+ };
209
+ function groupByTopic2(messages) {
210
+ const byTopic = /* @__PURE__ */ new Map();
211
+ for (const m of messages) {
212
+ const arr = byTopic.get(m.topic) ?? [];
213
+ arr.push({ key: m.key, value: m.value, headers: m.headers });
214
+ byTopic.set(m.topic, arr);
215
+ }
216
+ return [...byTopic.entries()].map(([topic, msgs]) => ({
217
+ topic,
218
+ messages: msgs
219
+ }));
220
+ }
221
+ async function importConfluent() {
222
+ try {
223
+ return await import("@confluentinc/kafka-javascript");
224
+ } catch {
225
+ throw new Error(
226
+ 'Driver "confluent" selected but "@confluentinc/kafka-javascript" is not installed. Run: npm i @confluentinc/kafka-javascript'
227
+ );
228
+ }
229
+ }
230
+
231
+ // src/publisher.ts
232
+ var KafkaPublisher = class {
233
+ driver;
234
+ constructor(opts) {
235
+ this.driver = opts.customDriver ?? selectDriver(opts);
236
+ }
237
+ connect() {
238
+ return this.driver.connect();
239
+ }
240
+ disconnect() {
241
+ return this.driver.disconnect();
242
+ }
243
+ publish(messages) {
244
+ return this.driver.sendBatch(messages);
245
+ }
246
+ /**
247
+ * Send a single dead-lettered message. The message already carries the
248
+ * DLQ topic (the Relay rewrites it), plus the failure reason as a header.
249
+ */
250
+ async publishToDlq(message, error) {
251
+ const dlqMessage = {
252
+ ...message,
253
+ headers: {
254
+ ...message.headers,
255
+ "dlq-reason": error.message,
256
+ "dlq-original-topic": message.headers["original-topic"] ?? "",
257
+ "dlq-failed-at": (/* @__PURE__ */ new Date()).toISOString()
258
+ }
259
+ };
260
+ const [result] = await this.driver.sendBatch([dlqMessage]);
261
+ if (result && !result.ok) {
262
+ throw result.error ?? new Error("DLQ publish failed");
263
+ }
264
+ }
265
+ /** Whether the configured driver provides atomic (EOS) batch sends. */
266
+ get transactional() {
267
+ return this.driver.transactional;
268
+ }
269
+ };
270
+ function selectDriver(opts) {
271
+ const kind = opts.driver ?? "kafkajs";
272
+ switch (kind) {
273
+ case "kafkajs":
274
+ return new KafkaJsDriver(opts);
275
+ case "confluent":
276
+ return new ConfluentDriver(opts);
277
+ default:
278
+ throw new Error(`Unknown driver "${kind}"`);
279
+ }
280
+ }
281
+ // Annotate the CommonJS export names for ESM import in node:
282
+ 0 && (module.exports = {
283
+ ConfluentDriver,
284
+ KafkaJsDriver,
285
+ KafkaPublisher
286
+ });
287
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/kafkajs-driver.ts","../src/confluent-driver.ts","../src/publisher.ts"],"sourcesContent":["export * from \"./driver.js\";\nexport * from \"./kafkajs-driver.js\";\nexport * from \"./confluent-driver.js\";\nexport * from \"./publisher.js\";\n","import type { PublishableMessage, PublishResult } from \"@eventferry/core\";\nimport type {\n KafkaConnectionConfig,\n KafkaDriver,\n ProducerBehaviorConfig,\n} from \"./driver.js\";\n\n// Loosely-typed structural references to the kafkajs API so this file\n// compiles without kafkajs installed (it's an optional peer dep).\ninterface KjsProducer {\n connect(): Promise<void>;\n disconnect(): Promise<void>;\n // kafkajs: sendBatch takes { topicMessages }, send takes a single { topic, messages }.\n sendBatch(args: unknown): Promise<unknown>;\n transaction(): Promise<KjsTransaction>;\n}\ninterface KjsTransaction {\n sendBatch(args: unknown): Promise<unknown>;\n commit(): Promise<void>;\n abort(): Promise<void>;\n}\ninterface KjsKafka {\n producer(args?: unknown): KjsProducer;\n}\n\nexport interface KafkaJsDriverOptions\n extends KafkaConnectionConfig,\n ProducerBehaviorConfig {}\n\n/**\n * Driver backed by the pure-JS `kafkajs` client. Simple, zero native deps.\n */\nexport class KafkaJsDriver implements KafkaDriver {\n readonly transactional: boolean;\n private producer: KjsProducer | null = null;\n private readonly opts: KafkaJsDriverOptions;\n\n constructor(opts: KafkaJsDriverOptions) {\n this.opts = opts;\n this.transactional = opts.transactional ?? false;\n if (this.transactional && !opts.transactionalId) {\n throw new Error(\n \"KafkaJsDriver: transactionalId is required when transactional=true\",\n );\n }\n }\n\n async connect(): Promise<void> {\n this.producer = await this.createProducer();\n await this.producer.connect();\n }\n\n /**\n * Construct the underlying kafkajs producer. Overridable as a test seam so\n * the send/transaction logic can be exercised without a real broker.\n */\n protected async createProducer(): Promise<KjsProducer> {\n const mod = await importKafkaJs();\n const kafka: KjsKafka = new mod.Kafka({\n clientId: this.opts.clientId ?? \"eventferry\",\n brokers: this.opts.brokers,\n ssl: this.opts.ssl,\n sasl: this.opts.sasl,\n });\n return kafka.producer({\n idempotent: this.opts.idempotent ?? true,\n maxInFlightRequests: this.transactional ? 1 : undefined,\n transactionalId: this.transactional ? this.opts.transactionalId : undefined,\n });\n }\n\n async disconnect(): Promise<void> {\n await this.producer?.disconnect();\n this.producer = null;\n }\n\n async sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]> {\n if (!this.producer) throw new Error(\"KafkaJsDriver not connected\");\n const topicMessages = groupByTopic(messages, this.opts.compression);\n\n if (this.transactional) {\n const txn = await this.producer.transaction();\n try {\n await txn.sendBatch({ topicMessages, acks: this.opts.acks ?? -1 });\n await txn.commit();\n return messages.map((m) => ({ recordId: m.recordId, ok: true }));\n } catch (err) {\n await txn.abort().catch(() => undefined);\n const error = err instanceof Error ? err : new Error(String(err));\n return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));\n }\n }\n\n try {\n await this.producer.sendBatch({ topicMessages, acks: this.opts.acks ?? -1 });\n return messages.map((m) => ({ recordId: m.recordId, ok: true }));\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));\n }\n }\n}\n\nfunction groupByTopic(messages: PublishableMessage[], compression?: string) {\n const byTopic = new Map<string, unknown[]>();\n for (const m of messages) {\n const arr = byTopic.get(m.topic) ?? [];\n arr.push({\n key: m.key,\n value: m.value,\n headers: m.headers,\n });\n byTopic.set(m.topic, arr);\n }\n return [...byTopic.entries()].map(([topic, msgs]) => ({\n topic,\n messages: msgs,\n ...(compression && compression !== \"none\" ? { compression } : {}),\n }));\n}\n\nasync function importKafkaJs(): Promise<{ Kafka: new (cfg: unknown) => KjsKafka }> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (await import(\"kafkajs\")) as any;\n } catch {\n throw new Error(\n 'Driver \"kafkajs\" selected but the \"kafkajs\" package is not installed. Run: npm i kafkajs',\n );\n }\n}\n","import type { PublishableMessage, PublishResult } from \"@eventferry/core\";\nimport type {\n KafkaConnectionConfig,\n KafkaDriver,\n ProducerBehaviorConfig,\n} from \"./driver.js\";\n\n// Structural typing of the confluent KafkaJS-compatible API surface so this\n// file compiles without the optional native dep installed.\ninterface CkProducer {\n connect(): Promise<void>;\n disconnect(): Promise<void>;\n send(args: unknown): Promise<unknown>;\n transaction(): Promise<CkTransaction>;\n}\ninterface CkTransaction {\n send(args: unknown): Promise<unknown>;\n commit(): Promise<void>;\n abort(): Promise<void>;\n}\ninterface CkKafka {\n producer(args?: unknown): CkProducer;\n}\n\nexport interface ConfluentDriverOptions\n extends KafkaConnectionConfig,\n ProducerBehaviorConfig {}\n\n/**\n * Driver backed by `@confluentinc/kafka-javascript` (librdkafka wrapper).\n * Higher throughput; uses the KafkaJS-compatible promisified API so the\n * adapter mirrors the kafkajs driver closely.\n */\nexport class ConfluentDriver implements KafkaDriver {\n readonly transactional: boolean;\n private producer: CkProducer | null = null;\n private readonly opts: ConfluentDriverOptions;\n\n constructor(opts: ConfluentDriverOptions) {\n this.opts = opts;\n this.transactional = opts.transactional ?? false;\n if (this.transactional && !opts.transactionalId) {\n throw new Error(\n \"ConfluentDriver: transactionalId is required when transactional=true\",\n );\n }\n }\n\n async connect(): Promise<void> {\n this.producer = await this.createProducer();\n await this.producer.connect();\n }\n\n /**\n * Construct the underlying confluent producer. Overridable as a test seam so\n * the send/transaction logic can be exercised without a real broker.\n */\n protected async createProducer(): Promise<CkProducer> {\n const mod = await importConfluent();\n const kafka: CkKafka = new mod.KafkaJS.Kafka({\n kafkaJS: {\n clientId: this.opts.clientId ?? \"eventferry\",\n brokers: this.opts.brokers,\n ssl: this.opts.ssl,\n sasl: this.opts.sasl,\n },\n });\n return kafka.producer({\n kafkaJS: {\n idempotent: this.opts.idempotent ?? true,\n ...(this.transactional\n ? { transactionalId: this.opts.transactionalId }\n : {}),\n },\n });\n }\n\n async disconnect(): Promise<void> {\n await this.producer?.disconnect();\n this.producer = null;\n }\n\n async sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]> {\n if (!this.producer) throw new Error(\"ConfluentDriver not connected\");\n const topicMessages = groupByTopic(messages);\n const acks = this.opts.acks ?? -1;\n const compression = this.opts.compression;\n\n const doSends = async (target: CkProducer | CkTransaction) => {\n for (const tm of topicMessages) {\n await target.send({\n topic: tm.topic,\n messages: tm.messages,\n acks,\n ...(compression && compression !== \"none\" ? { compression } : {}),\n });\n }\n };\n\n if (this.transactional) {\n const txn = await this.producer.transaction();\n try {\n await doSends(txn);\n await txn.commit();\n return messages.map((m) => ({ recordId: m.recordId, ok: true }));\n } catch (err) {\n await txn.abort().catch(() => undefined);\n const error = err instanceof Error ? err : new Error(String(err));\n return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));\n }\n }\n\n try {\n await doSends(this.producer);\n return messages.map((m) => ({ recordId: m.recordId, ok: true }));\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));\n }\n }\n}\n\nfunction groupByTopic(messages: PublishableMessage[]) {\n const byTopic = new Map<string, unknown[]>();\n for (const m of messages) {\n const arr = byTopic.get(m.topic) ?? [];\n arr.push({ key: m.key, value: m.value, headers: m.headers });\n byTopic.set(m.topic, arr);\n }\n return [...byTopic.entries()].map(([topic, msgs]) => ({\n topic,\n messages: msgs,\n }));\n}\n\nasync function importConfluent(): Promise<{\n KafkaJS: { Kafka: new (cfg: unknown) => CkKafka };\n}> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (await import(\"@confluentinc/kafka-javascript\")) as any;\n } catch {\n throw new Error(\n 'Driver \"confluent\" selected but \"@confluentinc/kafka-javascript\" is not installed. Run: npm i @confluentinc/kafka-javascript',\n );\n }\n}\n","import type {\n PublishableMessage,\n Publisher,\n PublishResult,\n} from \"@eventferry/core\";\nimport { ConfluentDriver } from \"./confluent-driver.js\";\nimport type {\n DriverKind,\n KafkaConnectionConfig,\n KafkaDriver,\n ProducerBehaviorConfig,\n} from \"./driver.js\";\nimport { KafkaJsDriver } from \"./kafkajs-driver.js\";\n\nexport interface KafkaPublisherOptions\n extends KafkaConnectionConfig,\n ProducerBehaviorConfig {\n /** Which underlying client to use. Default \"kafkajs\". */\n driver?: DriverKind;\n /**\n * Provide a fully custom driver instance instead of the built-ins.\n * Useful for testing or unsupported clients.\n */\n customDriver?: KafkaDriver;\n}\n\n/**\n * The Publisher the Relay talks to. Wraps a pluggable KafkaDriver and\n * adds dead-letter routing. Works against Kafka and Redpanda identically\n * (Redpanda is Kafka-API compatible).\n */\nexport class KafkaPublisher implements Publisher {\n private readonly driver: KafkaDriver;\n\n constructor(opts: KafkaPublisherOptions) {\n this.driver = opts.customDriver ?? selectDriver(opts);\n }\n\n connect(): Promise<void> {\n return this.driver.connect();\n }\n\n disconnect(): Promise<void> {\n return this.driver.disconnect();\n }\n\n publish(messages: PublishableMessage[]): Promise<PublishResult[]> {\n return this.driver.sendBatch(messages);\n }\n\n /**\n * Send a single dead-lettered message. The message already carries the\n * DLQ topic (the Relay rewrites it), plus the failure reason as a header.\n */\n async publishToDlq(message: PublishableMessage, error: Error): Promise<void> {\n const dlqMessage: PublishableMessage = {\n ...message,\n headers: {\n ...message.headers,\n \"dlq-reason\": error.message,\n \"dlq-original-topic\": message.headers[\"original-topic\"] ?? \"\",\n \"dlq-failed-at\": new Date().toISOString(),\n },\n };\n const [result] = await this.driver.sendBatch([dlqMessage]);\n if (result && !result.ok) {\n throw result.error ?? new Error(\"DLQ publish failed\");\n }\n }\n\n /** Whether the configured driver provides atomic (EOS) batch sends. */\n get transactional(): boolean {\n return this.driver.transactional;\n }\n}\n\nfunction selectDriver(opts: KafkaPublisherOptions): KafkaDriver {\n const kind = opts.driver ?? \"kafkajs\";\n switch (kind) {\n case \"kafkajs\":\n return new KafkaJsDriver(opts);\n case \"confluent\":\n return new ConfluentDriver(opts);\n default:\n throw new Error(`Unknown driver \"${kind}\"`);\n }\n}\n"],"mappings":"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","names":["groupByTopic"]}
@@ -0,0 +1,144 @@
1
+ import { PublishableMessage, PublishResult, Publisher } from '@eventferry/core';
2
+
3
+ /**
4
+ * Low-level driver contract. Each concrete driver (kafkajs, confluent)
5
+ * adapts its native client to this minimal surface. The KafkaPublisher
6
+ * orchestrates batching, transactions and DLQ on top of it.
7
+ */
8
+ interface KafkaDriver {
9
+ connect(): Promise<void>;
10
+ disconnect(): Promise<void>;
11
+ /**
12
+ * Send a batch of records. The driver should preserve ordering for
13
+ * messages that share the same key (Kafka guarantees this per-partition).
14
+ * Returns one result per input message.
15
+ */
16
+ sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]>;
17
+ /**
18
+ * Whether this driver supports transactional (EOS) sends. The publisher
19
+ * uses this to decide whether `sendBatch` is atomic.
20
+ */
21
+ readonly transactional: boolean;
22
+ }
23
+ /** Shared connection config accepted by both drivers. */
24
+ interface KafkaConnectionConfig {
25
+ brokers: string[];
26
+ clientId?: string;
27
+ ssl?: boolean;
28
+ sasl?: {
29
+ mechanism: "plain" | "scram-sha-256" | "scram-sha-512";
30
+ username: string;
31
+ password: string;
32
+ };
33
+ }
34
+ interface ProducerBehaviorConfig {
35
+ /** Enable idempotent producer (dedup + ordering). Default true. */
36
+ idempotent?: boolean;
37
+ /**
38
+ * Enable transactional producer for atomic batch publishing (EOS).
39
+ * Requires a stable transactionalId. Default false.
40
+ */
41
+ transactional?: boolean;
42
+ /** Required when transactional=true. Must be stable per producer instance. */
43
+ transactionalId?: string;
44
+ /** acks: -1/"all" (default), 0, or 1. */
45
+ acks?: number;
46
+ /** Compression codec. Driver maps to its native enum. */
47
+ compression?: "none" | "gzip" | "snappy" | "lz4" | "zstd";
48
+ }
49
+ type DriverKind = "kafkajs" | "confluent";
50
+
51
+ interface KjsProducer {
52
+ connect(): Promise<void>;
53
+ disconnect(): Promise<void>;
54
+ sendBatch(args: unknown): Promise<unknown>;
55
+ transaction(): Promise<KjsTransaction>;
56
+ }
57
+ interface KjsTransaction {
58
+ sendBatch(args: unknown): Promise<unknown>;
59
+ commit(): Promise<void>;
60
+ abort(): Promise<void>;
61
+ }
62
+ interface KafkaJsDriverOptions extends KafkaConnectionConfig, ProducerBehaviorConfig {
63
+ }
64
+ /**
65
+ * Driver backed by the pure-JS `kafkajs` client. Simple, zero native deps.
66
+ */
67
+ declare class KafkaJsDriver implements KafkaDriver {
68
+ readonly transactional: boolean;
69
+ private producer;
70
+ private readonly opts;
71
+ constructor(opts: KafkaJsDriverOptions);
72
+ connect(): Promise<void>;
73
+ /**
74
+ * Construct the underlying kafkajs producer. Overridable as a test seam so
75
+ * the send/transaction logic can be exercised without a real broker.
76
+ */
77
+ protected createProducer(): Promise<KjsProducer>;
78
+ disconnect(): Promise<void>;
79
+ sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]>;
80
+ }
81
+
82
+ interface CkProducer {
83
+ connect(): Promise<void>;
84
+ disconnect(): Promise<void>;
85
+ send(args: unknown): Promise<unknown>;
86
+ transaction(): Promise<CkTransaction>;
87
+ }
88
+ interface CkTransaction {
89
+ send(args: unknown): Promise<unknown>;
90
+ commit(): Promise<void>;
91
+ abort(): Promise<void>;
92
+ }
93
+ interface ConfluentDriverOptions extends KafkaConnectionConfig, ProducerBehaviorConfig {
94
+ }
95
+ /**
96
+ * Driver backed by `@confluentinc/kafka-javascript` (librdkafka wrapper).
97
+ * Higher throughput; uses the KafkaJS-compatible promisified API so the
98
+ * adapter mirrors the kafkajs driver closely.
99
+ */
100
+ declare class ConfluentDriver implements KafkaDriver {
101
+ readonly transactional: boolean;
102
+ private producer;
103
+ private readonly opts;
104
+ constructor(opts: ConfluentDriverOptions);
105
+ connect(): Promise<void>;
106
+ /**
107
+ * Construct the underlying confluent producer. Overridable as a test seam so
108
+ * the send/transaction logic can be exercised without a real broker.
109
+ */
110
+ protected createProducer(): Promise<CkProducer>;
111
+ disconnect(): Promise<void>;
112
+ sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]>;
113
+ }
114
+
115
+ interface KafkaPublisherOptions extends KafkaConnectionConfig, ProducerBehaviorConfig {
116
+ /** Which underlying client to use. Default "kafkajs". */
117
+ driver?: DriverKind;
118
+ /**
119
+ * Provide a fully custom driver instance instead of the built-ins.
120
+ * Useful for testing or unsupported clients.
121
+ */
122
+ customDriver?: KafkaDriver;
123
+ }
124
+ /**
125
+ * The Publisher the Relay talks to. Wraps a pluggable KafkaDriver and
126
+ * adds dead-letter routing. Works against Kafka and Redpanda identically
127
+ * (Redpanda is Kafka-API compatible).
128
+ */
129
+ declare class KafkaPublisher implements Publisher {
130
+ private readonly driver;
131
+ constructor(opts: KafkaPublisherOptions);
132
+ connect(): Promise<void>;
133
+ disconnect(): Promise<void>;
134
+ publish(messages: PublishableMessage[]): Promise<PublishResult[]>;
135
+ /**
136
+ * Send a single dead-lettered message. The message already carries the
137
+ * DLQ topic (the Relay rewrites it), plus the failure reason as a header.
138
+ */
139
+ publishToDlq(message: PublishableMessage, error: Error): Promise<void>;
140
+ /** Whether the configured driver provides atomic (EOS) batch sends. */
141
+ get transactional(): boolean;
142
+ }
143
+
144
+ export { ConfluentDriver, type ConfluentDriverOptions, type DriverKind, type KafkaConnectionConfig, type KafkaDriver, KafkaJsDriver, type KafkaJsDriverOptions, KafkaPublisher, type KafkaPublisherOptions, type ProducerBehaviorConfig };
@@ -0,0 +1,144 @@
1
+ import { PublishableMessage, PublishResult, Publisher } from '@eventferry/core';
2
+
3
+ /**
4
+ * Low-level driver contract. Each concrete driver (kafkajs, confluent)
5
+ * adapts its native client to this minimal surface. The KafkaPublisher
6
+ * orchestrates batching, transactions and DLQ on top of it.
7
+ */
8
+ interface KafkaDriver {
9
+ connect(): Promise<void>;
10
+ disconnect(): Promise<void>;
11
+ /**
12
+ * Send a batch of records. The driver should preserve ordering for
13
+ * messages that share the same key (Kafka guarantees this per-partition).
14
+ * Returns one result per input message.
15
+ */
16
+ sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]>;
17
+ /**
18
+ * Whether this driver supports transactional (EOS) sends. The publisher
19
+ * uses this to decide whether `sendBatch` is atomic.
20
+ */
21
+ readonly transactional: boolean;
22
+ }
23
+ /** Shared connection config accepted by both drivers. */
24
+ interface KafkaConnectionConfig {
25
+ brokers: string[];
26
+ clientId?: string;
27
+ ssl?: boolean;
28
+ sasl?: {
29
+ mechanism: "plain" | "scram-sha-256" | "scram-sha-512";
30
+ username: string;
31
+ password: string;
32
+ };
33
+ }
34
+ interface ProducerBehaviorConfig {
35
+ /** Enable idempotent producer (dedup + ordering). Default true. */
36
+ idempotent?: boolean;
37
+ /**
38
+ * Enable transactional producer for atomic batch publishing (EOS).
39
+ * Requires a stable transactionalId. Default false.
40
+ */
41
+ transactional?: boolean;
42
+ /** Required when transactional=true. Must be stable per producer instance. */
43
+ transactionalId?: string;
44
+ /** acks: -1/"all" (default), 0, or 1. */
45
+ acks?: number;
46
+ /** Compression codec. Driver maps to its native enum. */
47
+ compression?: "none" | "gzip" | "snappy" | "lz4" | "zstd";
48
+ }
49
+ type DriverKind = "kafkajs" | "confluent";
50
+
51
+ interface KjsProducer {
52
+ connect(): Promise<void>;
53
+ disconnect(): Promise<void>;
54
+ sendBatch(args: unknown): Promise<unknown>;
55
+ transaction(): Promise<KjsTransaction>;
56
+ }
57
+ interface KjsTransaction {
58
+ sendBatch(args: unknown): Promise<unknown>;
59
+ commit(): Promise<void>;
60
+ abort(): Promise<void>;
61
+ }
62
+ interface KafkaJsDriverOptions extends KafkaConnectionConfig, ProducerBehaviorConfig {
63
+ }
64
+ /**
65
+ * Driver backed by the pure-JS `kafkajs` client. Simple, zero native deps.
66
+ */
67
+ declare class KafkaJsDriver implements KafkaDriver {
68
+ readonly transactional: boolean;
69
+ private producer;
70
+ private readonly opts;
71
+ constructor(opts: KafkaJsDriverOptions);
72
+ connect(): Promise<void>;
73
+ /**
74
+ * Construct the underlying kafkajs producer. Overridable as a test seam so
75
+ * the send/transaction logic can be exercised without a real broker.
76
+ */
77
+ protected createProducer(): Promise<KjsProducer>;
78
+ disconnect(): Promise<void>;
79
+ sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]>;
80
+ }
81
+
82
+ interface CkProducer {
83
+ connect(): Promise<void>;
84
+ disconnect(): Promise<void>;
85
+ send(args: unknown): Promise<unknown>;
86
+ transaction(): Promise<CkTransaction>;
87
+ }
88
+ interface CkTransaction {
89
+ send(args: unknown): Promise<unknown>;
90
+ commit(): Promise<void>;
91
+ abort(): Promise<void>;
92
+ }
93
+ interface ConfluentDriverOptions extends KafkaConnectionConfig, ProducerBehaviorConfig {
94
+ }
95
+ /**
96
+ * Driver backed by `@confluentinc/kafka-javascript` (librdkafka wrapper).
97
+ * Higher throughput; uses the KafkaJS-compatible promisified API so the
98
+ * adapter mirrors the kafkajs driver closely.
99
+ */
100
+ declare class ConfluentDriver implements KafkaDriver {
101
+ readonly transactional: boolean;
102
+ private producer;
103
+ private readonly opts;
104
+ constructor(opts: ConfluentDriverOptions);
105
+ connect(): Promise<void>;
106
+ /**
107
+ * Construct the underlying confluent producer. Overridable as a test seam so
108
+ * the send/transaction logic can be exercised without a real broker.
109
+ */
110
+ protected createProducer(): Promise<CkProducer>;
111
+ disconnect(): Promise<void>;
112
+ sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]>;
113
+ }
114
+
115
+ interface KafkaPublisherOptions extends KafkaConnectionConfig, ProducerBehaviorConfig {
116
+ /** Which underlying client to use. Default "kafkajs". */
117
+ driver?: DriverKind;
118
+ /**
119
+ * Provide a fully custom driver instance instead of the built-ins.
120
+ * Useful for testing or unsupported clients.
121
+ */
122
+ customDriver?: KafkaDriver;
123
+ }
124
+ /**
125
+ * The Publisher the Relay talks to. Wraps a pluggable KafkaDriver and
126
+ * adds dead-letter routing. Works against Kafka and Redpanda identically
127
+ * (Redpanda is Kafka-API compatible).
128
+ */
129
+ declare class KafkaPublisher implements Publisher {
130
+ private readonly driver;
131
+ constructor(opts: KafkaPublisherOptions);
132
+ connect(): Promise<void>;
133
+ disconnect(): Promise<void>;
134
+ publish(messages: PublishableMessage[]): Promise<PublishResult[]>;
135
+ /**
136
+ * Send a single dead-lettered message. The message already carries the
137
+ * DLQ topic (the Relay rewrites it), plus the failure reason as a header.
138
+ */
139
+ publishToDlq(message: PublishableMessage, error: Error): Promise<void>;
140
+ /** Whether the configured driver provides atomic (EOS) batch sends. */
141
+ get transactional(): boolean;
142
+ }
143
+
144
+ export { ConfluentDriver, type ConfluentDriverOptions, type DriverKind, type KafkaConnectionConfig, type KafkaDriver, KafkaJsDriver, type KafkaJsDriverOptions, KafkaPublisher, type KafkaPublisherOptions, type ProducerBehaviorConfig };
package/dist/index.js ADDED
@@ -0,0 +1,248 @@
1
+ // src/kafkajs-driver.ts
2
+ var KafkaJsDriver = class {
3
+ transactional;
4
+ producer = null;
5
+ opts;
6
+ constructor(opts) {
7
+ this.opts = opts;
8
+ this.transactional = opts.transactional ?? false;
9
+ if (this.transactional && !opts.transactionalId) {
10
+ throw new Error(
11
+ "KafkaJsDriver: transactionalId is required when transactional=true"
12
+ );
13
+ }
14
+ }
15
+ async connect() {
16
+ this.producer = await this.createProducer();
17
+ await this.producer.connect();
18
+ }
19
+ /**
20
+ * Construct the underlying kafkajs producer. Overridable as a test seam so
21
+ * the send/transaction logic can be exercised without a real broker.
22
+ */
23
+ async createProducer() {
24
+ const mod = await importKafkaJs();
25
+ const kafka = new mod.Kafka({
26
+ clientId: this.opts.clientId ?? "eventferry",
27
+ brokers: this.opts.brokers,
28
+ ssl: this.opts.ssl,
29
+ sasl: this.opts.sasl
30
+ });
31
+ return kafka.producer({
32
+ idempotent: this.opts.idempotent ?? true,
33
+ maxInFlightRequests: this.transactional ? 1 : void 0,
34
+ transactionalId: this.transactional ? this.opts.transactionalId : void 0
35
+ });
36
+ }
37
+ async disconnect() {
38
+ await this.producer?.disconnect();
39
+ this.producer = null;
40
+ }
41
+ async sendBatch(messages) {
42
+ if (!this.producer) throw new Error("KafkaJsDriver not connected");
43
+ const topicMessages = groupByTopic(messages, this.opts.compression);
44
+ if (this.transactional) {
45
+ const txn = await this.producer.transaction();
46
+ try {
47
+ await txn.sendBatch({ topicMessages, acks: this.opts.acks ?? -1 });
48
+ await txn.commit();
49
+ return messages.map((m) => ({ recordId: m.recordId, ok: true }));
50
+ } catch (err) {
51
+ await txn.abort().catch(() => void 0);
52
+ const error = err instanceof Error ? err : new Error(String(err));
53
+ return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));
54
+ }
55
+ }
56
+ try {
57
+ await this.producer.sendBatch({ topicMessages, acks: this.opts.acks ?? -1 });
58
+ return messages.map((m) => ({ recordId: m.recordId, ok: true }));
59
+ } catch (err) {
60
+ const error = err instanceof Error ? err : new Error(String(err));
61
+ return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));
62
+ }
63
+ }
64
+ };
65
+ function groupByTopic(messages, compression) {
66
+ const byTopic = /* @__PURE__ */ new Map();
67
+ for (const m of messages) {
68
+ const arr = byTopic.get(m.topic) ?? [];
69
+ arr.push({
70
+ key: m.key,
71
+ value: m.value,
72
+ headers: m.headers
73
+ });
74
+ byTopic.set(m.topic, arr);
75
+ }
76
+ return [...byTopic.entries()].map(([topic, msgs]) => ({
77
+ topic,
78
+ messages: msgs,
79
+ ...compression && compression !== "none" ? { compression } : {}
80
+ }));
81
+ }
82
+ async function importKafkaJs() {
83
+ try {
84
+ return await import("kafkajs");
85
+ } catch {
86
+ throw new Error(
87
+ 'Driver "kafkajs" selected but the "kafkajs" package is not installed. Run: npm i kafkajs'
88
+ );
89
+ }
90
+ }
91
+
92
+ // src/confluent-driver.ts
93
+ var ConfluentDriver = class {
94
+ transactional;
95
+ producer = null;
96
+ opts;
97
+ constructor(opts) {
98
+ this.opts = opts;
99
+ this.transactional = opts.transactional ?? false;
100
+ if (this.transactional && !opts.transactionalId) {
101
+ throw new Error(
102
+ "ConfluentDriver: transactionalId is required when transactional=true"
103
+ );
104
+ }
105
+ }
106
+ async connect() {
107
+ this.producer = await this.createProducer();
108
+ await this.producer.connect();
109
+ }
110
+ /**
111
+ * Construct the underlying confluent producer. Overridable as a test seam so
112
+ * the send/transaction logic can be exercised without a real broker.
113
+ */
114
+ async createProducer() {
115
+ const mod = await importConfluent();
116
+ const kafka = new mod.KafkaJS.Kafka({
117
+ kafkaJS: {
118
+ clientId: this.opts.clientId ?? "eventferry",
119
+ brokers: this.opts.brokers,
120
+ ssl: this.opts.ssl,
121
+ sasl: this.opts.sasl
122
+ }
123
+ });
124
+ return kafka.producer({
125
+ kafkaJS: {
126
+ idempotent: this.opts.idempotent ?? true,
127
+ ...this.transactional ? { transactionalId: this.opts.transactionalId } : {}
128
+ }
129
+ });
130
+ }
131
+ async disconnect() {
132
+ await this.producer?.disconnect();
133
+ this.producer = null;
134
+ }
135
+ async sendBatch(messages) {
136
+ if (!this.producer) throw new Error("ConfluentDriver not connected");
137
+ const topicMessages = groupByTopic2(messages);
138
+ const acks = this.opts.acks ?? -1;
139
+ const compression = this.opts.compression;
140
+ const doSends = async (target) => {
141
+ for (const tm of topicMessages) {
142
+ await target.send({
143
+ topic: tm.topic,
144
+ messages: tm.messages,
145
+ acks,
146
+ ...compression && compression !== "none" ? { compression } : {}
147
+ });
148
+ }
149
+ };
150
+ if (this.transactional) {
151
+ const txn = await this.producer.transaction();
152
+ try {
153
+ await doSends(txn);
154
+ await txn.commit();
155
+ return messages.map((m) => ({ recordId: m.recordId, ok: true }));
156
+ } catch (err) {
157
+ await txn.abort().catch(() => void 0);
158
+ const error = err instanceof Error ? err : new Error(String(err));
159
+ return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));
160
+ }
161
+ }
162
+ try {
163
+ await doSends(this.producer);
164
+ return messages.map((m) => ({ recordId: m.recordId, ok: true }));
165
+ } catch (err) {
166
+ const error = err instanceof Error ? err : new Error(String(err));
167
+ return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));
168
+ }
169
+ }
170
+ };
171
+ function groupByTopic2(messages) {
172
+ const byTopic = /* @__PURE__ */ new Map();
173
+ for (const m of messages) {
174
+ const arr = byTopic.get(m.topic) ?? [];
175
+ arr.push({ key: m.key, value: m.value, headers: m.headers });
176
+ byTopic.set(m.topic, arr);
177
+ }
178
+ return [...byTopic.entries()].map(([topic, msgs]) => ({
179
+ topic,
180
+ messages: msgs
181
+ }));
182
+ }
183
+ async function importConfluent() {
184
+ try {
185
+ return await import("@confluentinc/kafka-javascript");
186
+ } catch {
187
+ throw new Error(
188
+ 'Driver "confluent" selected but "@confluentinc/kafka-javascript" is not installed. Run: npm i @confluentinc/kafka-javascript'
189
+ );
190
+ }
191
+ }
192
+
193
+ // src/publisher.ts
194
+ var KafkaPublisher = class {
195
+ driver;
196
+ constructor(opts) {
197
+ this.driver = opts.customDriver ?? selectDriver(opts);
198
+ }
199
+ connect() {
200
+ return this.driver.connect();
201
+ }
202
+ disconnect() {
203
+ return this.driver.disconnect();
204
+ }
205
+ publish(messages) {
206
+ return this.driver.sendBatch(messages);
207
+ }
208
+ /**
209
+ * Send a single dead-lettered message. The message already carries the
210
+ * DLQ topic (the Relay rewrites it), plus the failure reason as a header.
211
+ */
212
+ async publishToDlq(message, error) {
213
+ const dlqMessage = {
214
+ ...message,
215
+ headers: {
216
+ ...message.headers,
217
+ "dlq-reason": error.message,
218
+ "dlq-original-topic": message.headers["original-topic"] ?? "",
219
+ "dlq-failed-at": (/* @__PURE__ */ new Date()).toISOString()
220
+ }
221
+ };
222
+ const [result] = await this.driver.sendBatch([dlqMessage]);
223
+ if (result && !result.ok) {
224
+ throw result.error ?? new Error("DLQ publish failed");
225
+ }
226
+ }
227
+ /** Whether the configured driver provides atomic (EOS) batch sends. */
228
+ get transactional() {
229
+ return this.driver.transactional;
230
+ }
231
+ };
232
+ function selectDriver(opts) {
233
+ const kind = opts.driver ?? "kafkajs";
234
+ switch (kind) {
235
+ case "kafkajs":
236
+ return new KafkaJsDriver(opts);
237
+ case "confluent":
238
+ return new ConfluentDriver(opts);
239
+ default:
240
+ throw new Error(`Unknown driver "${kind}"`);
241
+ }
242
+ }
243
+ export {
244
+ ConfluentDriver,
245
+ KafkaJsDriver,
246
+ KafkaPublisher
247
+ };
248
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/kafkajs-driver.ts","../src/confluent-driver.ts","../src/publisher.ts"],"sourcesContent":["import type { PublishableMessage, PublishResult } from \"@eventferry/core\";\nimport type {\n KafkaConnectionConfig,\n KafkaDriver,\n ProducerBehaviorConfig,\n} from \"./driver.js\";\n\n// Loosely-typed structural references to the kafkajs API so this file\n// compiles without kafkajs installed (it's an optional peer dep).\ninterface KjsProducer {\n connect(): Promise<void>;\n disconnect(): Promise<void>;\n // kafkajs: sendBatch takes { topicMessages }, send takes a single { topic, messages }.\n sendBatch(args: unknown): Promise<unknown>;\n transaction(): Promise<KjsTransaction>;\n}\ninterface KjsTransaction {\n sendBatch(args: unknown): Promise<unknown>;\n commit(): Promise<void>;\n abort(): Promise<void>;\n}\ninterface KjsKafka {\n producer(args?: unknown): KjsProducer;\n}\n\nexport interface KafkaJsDriverOptions\n extends KafkaConnectionConfig,\n ProducerBehaviorConfig {}\n\n/**\n * Driver backed by the pure-JS `kafkajs` client. Simple, zero native deps.\n */\nexport class KafkaJsDriver implements KafkaDriver {\n readonly transactional: boolean;\n private producer: KjsProducer | null = null;\n private readonly opts: KafkaJsDriverOptions;\n\n constructor(opts: KafkaJsDriverOptions) {\n this.opts = opts;\n this.transactional = opts.transactional ?? false;\n if (this.transactional && !opts.transactionalId) {\n throw new Error(\n \"KafkaJsDriver: transactionalId is required when transactional=true\",\n );\n }\n }\n\n async connect(): Promise<void> {\n this.producer = await this.createProducer();\n await this.producer.connect();\n }\n\n /**\n * Construct the underlying kafkajs producer. Overridable as a test seam so\n * the send/transaction logic can be exercised without a real broker.\n */\n protected async createProducer(): Promise<KjsProducer> {\n const mod = await importKafkaJs();\n const kafka: KjsKafka = new mod.Kafka({\n clientId: this.opts.clientId ?? \"eventferry\",\n brokers: this.opts.brokers,\n ssl: this.opts.ssl,\n sasl: this.opts.sasl,\n });\n return kafka.producer({\n idempotent: this.opts.idempotent ?? true,\n maxInFlightRequests: this.transactional ? 1 : undefined,\n transactionalId: this.transactional ? this.opts.transactionalId : undefined,\n });\n }\n\n async disconnect(): Promise<void> {\n await this.producer?.disconnect();\n this.producer = null;\n }\n\n async sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]> {\n if (!this.producer) throw new Error(\"KafkaJsDriver not connected\");\n const topicMessages = groupByTopic(messages, this.opts.compression);\n\n if (this.transactional) {\n const txn = await this.producer.transaction();\n try {\n await txn.sendBatch({ topicMessages, acks: this.opts.acks ?? -1 });\n await txn.commit();\n return messages.map((m) => ({ recordId: m.recordId, ok: true }));\n } catch (err) {\n await txn.abort().catch(() => undefined);\n const error = err instanceof Error ? err : new Error(String(err));\n return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));\n }\n }\n\n try {\n await this.producer.sendBatch({ topicMessages, acks: this.opts.acks ?? -1 });\n return messages.map((m) => ({ recordId: m.recordId, ok: true }));\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));\n }\n }\n}\n\nfunction groupByTopic(messages: PublishableMessage[], compression?: string) {\n const byTopic = new Map<string, unknown[]>();\n for (const m of messages) {\n const arr = byTopic.get(m.topic) ?? [];\n arr.push({\n key: m.key,\n value: m.value,\n headers: m.headers,\n });\n byTopic.set(m.topic, arr);\n }\n return [...byTopic.entries()].map(([topic, msgs]) => ({\n topic,\n messages: msgs,\n ...(compression && compression !== \"none\" ? { compression } : {}),\n }));\n}\n\nasync function importKafkaJs(): Promise<{ Kafka: new (cfg: unknown) => KjsKafka }> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (await import(\"kafkajs\")) as any;\n } catch {\n throw new Error(\n 'Driver \"kafkajs\" selected but the \"kafkajs\" package is not installed. Run: npm i kafkajs',\n );\n }\n}\n","import type { PublishableMessage, PublishResult } from \"@eventferry/core\";\nimport type {\n KafkaConnectionConfig,\n KafkaDriver,\n ProducerBehaviorConfig,\n} from \"./driver.js\";\n\n// Structural typing of the confluent KafkaJS-compatible API surface so this\n// file compiles without the optional native dep installed.\ninterface CkProducer {\n connect(): Promise<void>;\n disconnect(): Promise<void>;\n send(args: unknown): Promise<unknown>;\n transaction(): Promise<CkTransaction>;\n}\ninterface CkTransaction {\n send(args: unknown): Promise<unknown>;\n commit(): Promise<void>;\n abort(): Promise<void>;\n}\ninterface CkKafka {\n producer(args?: unknown): CkProducer;\n}\n\nexport interface ConfluentDriverOptions\n extends KafkaConnectionConfig,\n ProducerBehaviorConfig {}\n\n/**\n * Driver backed by `@confluentinc/kafka-javascript` (librdkafka wrapper).\n * Higher throughput; uses the KafkaJS-compatible promisified API so the\n * adapter mirrors the kafkajs driver closely.\n */\nexport class ConfluentDriver implements KafkaDriver {\n readonly transactional: boolean;\n private producer: CkProducer | null = null;\n private readonly opts: ConfluentDriverOptions;\n\n constructor(opts: ConfluentDriverOptions) {\n this.opts = opts;\n this.transactional = opts.transactional ?? false;\n if (this.transactional && !opts.transactionalId) {\n throw new Error(\n \"ConfluentDriver: transactionalId is required when transactional=true\",\n );\n }\n }\n\n async connect(): Promise<void> {\n this.producer = await this.createProducer();\n await this.producer.connect();\n }\n\n /**\n * Construct the underlying confluent producer. Overridable as a test seam so\n * the send/transaction logic can be exercised without a real broker.\n */\n protected async createProducer(): Promise<CkProducer> {\n const mod = await importConfluent();\n const kafka: CkKafka = new mod.KafkaJS.Kafka({\n kafkaJS: {\n clientId: this.opts.clientId ?? \"eventferry\",\n brokers: this.opts.brokers,\n ssl: this.opts.ssl,\n sasl: this.opts.sasl,\n },\n });\n return kafka.producer({\n kafkaJS: {\n idempotent: this.opts.idempotent ?? true,\n ...(this.transactional\n ? { transactionalId: this.opts.transactionalId }\n : {}),\n },\n });\n }\n\n async disconnect(): Promise<void> {\n await this.producer?.disconnect();\n this.producer = null;\n }\n\n async sendBatch(messages: PublishableMessage[]): Promise<PublishResult[]> {\n if (!this.producer) throw new Error(\"ConfluentDriver not connected\");\n const topicMessages = groupByTopic(messages);\n const acks = this.opts.acks ?? -1;\n const compression = this.opts.compression;\n\n const doSends = async (target: CkProducer | CkTransaction) => {\n for (const tm of topicMessages) {\n await target.send({\n topic: tm.topic,\n messages: tm.messages,\n acks,\n ...(compression && compression !== \"none\" ? { compression } : {}),\n });\n }\n };\n\n if (this.transactional) {\n const txn = await this.producer.transaction();\n try {\n await doSends(txn);\n await txn.commit();\n return messages.map((m) => ({ recordId: m.recordId, ok: true }));\n } catch (err) {\n await txn.abort().catch(() => undefined);\n const error = err instanceof Error ? err : new Error(String(err));\n return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));\n }\n }\n\n try {\n await doSends(this.producer);\n return messages.map((m) => ({ recordId: m.recordId, ok: true }));\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return messages.map((m) => ({ recordId: m.recordId, ok: false, error }));\n }\n }\n}\n\nfunction groupByTopic(messages: PublishableMessage[]) {\n const byTopic = new Map<string, unknown[]>();\n for (const m of messages) {\n const arr = byTopic.get(m.topic) ?? [];\n arr.push({ key: m.key, value: m.value, headers: m.headers });\n byTopic.set(m.topic, arr);\n }\n return [...byTopic.entries()].map(([topic, msgs]) => ({\n topic,\n messages: msgs,\n }));\n}\n\nasync function importConfluent(): Promise<{\n KafkaJS: { Kafka: new (cfg: unknown) => CkKafka };\n}> {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return (await import(\"@confluentinc/kafka-javascript\")) as any;\n } catch {\n throw new Error(\n 'Driver \"confluent\" selected but \"@confluentinc/kafka-javascript\" is not installed. Run: npm i @confluentinc/kafka-javascript',\n );\n }\n}\n","import type {\n PublishableMessage,\n Publisher,\n PublishResult,\n} from \"@eventferry/core\";\nimport { ConfluentDriver } from \"./confluent-driver.js\";\nimport type {\n DriverKind,\n KafkaConnectionConfig,\n KafkaDriver,\n ProducerBehaviorConfig,\n} from \"./driver.js\";\nimport { KafkaJsDriver } from \"./kafkajs-driver.js\";\n\nexport interface KafkaPublisherOptions\n extends KafkaConnectionConfig,\n ProducerBehaviorConfig {\n /** Which underlying client to use. Default \"kafkajs\". */\n driver?: DriverKind;\n /**\n * Provide a fully custom driver instance instead of the built-ins.\n * Useful for testing or unsupported clients.\n */\n customDriver?: KafkaDriver;\n}\n\n/**\n * The Publisher the Relay talks to. Wraps a pluggable KafkaDriver and\n * adds dead-letter routing. Works against Kafka and Redpanda identically\n * (Redpanda is Kafka-API compatible).\n */\nexport class KafkaPublisher implements Publisher {\n private readonly driver: KafkaDriver;\n\n constructor(opts: KafkaPublisherOptions) {\n this.driver = opts.customDriver ?? selectDriver(opts);\n }\n\n connect(): Promise<void> {\n return this.driver.connect();\n }\n\n disconnect(): Promise<void> {\n return this.driver.disconnect();\n }\n\n publish(messages: PublishableMessage[]): Promise<PublishResult[]> {\n return this.driver.sendBatch(messages);\n }\n\n /**\n * Send a single dead-lettered message. The message already carries the\n * DLQ topic (the Relay rewrites it), plus the failure reason as a header.\n */\n async publishToDlq(message: PublishableMessage, error: Error): Promise<void> {\n const dlqMessage: PublishableMessage = {\n ...message,\n headers: {\n ...message.headers,\n \"dlq-reason\": error.message,\n \"dlq-original-topic\": message.headers[\"original-topic\"] ?? \"\",\n \"dlq-failed-at\": new Date().toISOString(),\n },\n };\n const [result] = await this.driver.sendBatch([dlqMessage]);\n if (result && !result.ok) {\n throw result.error ?? new Error(\"DLQ publish failed\");\n }\n }\n\n /** Whether the configured driver provides atomic (EOS) batch sends. */\n get transactional(): boolean {\n return this.driver.transactional;\n }\n}\n\nfunction selectDriver(opts: KafkaPublisherOptions): KafkaDriver {\n const kind = opts.driver ?? \"kafkajs\";\n switch (kind) {\n case \"kafkajs\":\n return new KafkaJsDriver(opts);\n case \"confluent\":\n return new ConfluentDriver(opts);\n default:\n throw new Error(`Unknown driver \"${kind}\"`);\n }\n}\n"],"mappings":"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","names":["groupByTopic"]}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "@eventferry/kafka",
3
+ "version": "1.0.0",
4
+ "description": "Kafka/Redpanda publisher for @eventferry (kafkajs + confluent drivers)",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "keywords": [
20
+ "outbox",
21
+ "kafka",
22
+ "redpanda",
23
+ "kafkajs",
24
+ "confluent"
25
+ ],
26
+ "license": "MIT",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/SametGoktepe/eventferry.git",
30
+ "directory": "packages/kafka"
31
+ },
32
+ "homepage": "https://github.com/SametGoktepe/eventferry/tree/main/packages/kafka#readme",
33
+ "bugs": "https://github.com/SametGoktepe/eventferry/issues",
34
+ "peerDependencies": {
35
+ "kafkajs": "^2.0.0",
36
+ "@confluentinc/kafka-javascript": "^1.0.0",
37
+ "@eventferry/core": "1.0.0"
38
+ },
39
+ "peerDependenciesMeta": {
40
+ "kafkajs": {
41
+ "optional": true
42
+ },
43
+ "@confluentinc/kafka-javascript": {
44
+ "optional": true
45
+ }
46
+ },
47
+ "devDependencies": {
48
+ "kafkajs": "^2.2.4",
49
+ "tsup": "^8.3.5",
50
+ "typescript": "^5.7.2",
51
+ "vitest": "^2.1.8",
52
+ "@eventferry/core": "1.0.0"
53
+ },
54
+ "scripts": {
55
+ "build": "tsup",
56
+ "test": "vitest",
57
+ "test:run": "vitest run",
58
+ "typecheck": "tsc --noEmit"
59
+ }
60
+ }