@drarzter/kafka-client 0.3.0 → 0.5.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/dist/otel.mjs ADDED
@@ -0,0 +1,49 @@
1
+ import "./chunk-EQQGB2QZ.mjs";
2
+
3
+ // src/otel.ts
4
+ import {
5
+ trace,
6
+ context,
7
+ propagation,
8
+ SpanKind,
9
+ SpanStatusCode
10
+ } from "@opentelemetry/api";
11
+ function otelInstrumentation() {
12
+ const tracer = trace.getTracer("@drarzter/kafka-client");
13
+ return {
14
+ beforeSend(topic, headers) {
15
+ propagation.inject(context.active(), headers);
16
+ },
17
+ afterSend(_topic) {
18
+ },
19
+ beforeConsume(envelope) {
20
+ const parentCtx = propagation.extract(context.active(), envelope.headers);
21
+ const span = tracer.startSpan(
22
+ `kafka.consume ${envelope.topic}`,
23
+ {
24
+ kind: SpanKind.CONSUMER,
25
+ attributes: {
26
+ "messaging.system": "kafka",
27
+ "messaging.destination.name": envelope.topic,
28
+ "messaging.message.id": envelope.eventId,
29
+ "messaging.kafka.partition": envelope.partition,
30
+ "messaging.kafka.offset": envelope.offset
31
+ }
32
+ },
33
+ parentCtx
34
+ );
35
+ return () => span.end();
36
+ },
37
+ onConsumeError(envelope, error) {
38
+ const span = trace.getActiveSpan();
39
+ if (span) {
40
+ span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
41
+ span.recordException(error);
42
+ }
43
+ }
44
+ };
45
+ }
46
+ export {
47
+ otelInstrumentation
48
+ };
49
+ //# sourceMappingURL=otel.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/otel.ts"],"sourcesContent":["import {\n trace,\n context,\n propagation,\n SpanKind,\n SpanStatusCode,\n} from \"@opentelemetry/api\";\nimport type { KafkaInstrumentation } from \"./client/types\";\nimport type { EventEnvelope } from \"./client/envelope\";\n\n/**\n * Create a `KafkaInstrumentation` that automatically propagates\n * W3C Trace Context via Kafka headers.\n *\n * Requires `@opentelemetry/api` as a peer dependency.\n *\n * **Send path:** injects `traceparent` into message headers from the\n * active OpenTelemetry context.\n *\n * **Consume path:** extracts `traceparent` from message headers,\n * starts a `CONSUMER` span as a child of the extracted context,\n * and ends it when the handler completes.\n *\n * @example\n * ```ts\n * import { otelInstrumentation } from '@drarzter/kafka-client/otel';\n *\n * const kafka = new KafkaClient('my-app', 'my-group', brokers, {\n * instrumentation: [otelInstrumentation()],\n * });\n * ```\n */\nexport function otelInstrumentation(): KafkaInstrumentation {\n const tracer = trace.getTracer(\"@drarzter/kafka-client\");\n\n return {\n beforeSend(topic: string, headers: Record<string, string>) {\n propagation.inject(context.active(), headers);\n },\n\n afterSend(_topic: string) {\n // Span management for producers is left to the caller's OTel setup.\n // We only inject context — creating producer spans here would be\n // inaccurate since buildSendPayload runs synchronously per-message.\n },\n\n beforeConsume(envelope: EventEnvelope<any>) {\n const parentCtx = propagation.extract(context.active(), envelope.headers);\n const span = tracer.startSpan(\n `kafka.consume ${envelope.topic}`,\n {\n kind: SpanKind.CONSUMER,\n attributes: {\n \"messaging.system\": \"kafka\",\n \"messaging.destination.name\": envelope.topic,\n \"messaging.message.id\": envelope.eventId,\n \"messaging.kafka.partition\": envelope.partition,\n \"messaging.kafka.offset\": envelope.offset,\n },\n },\n parentCtx,\n );\n return () => span.end();\n },\n\n onConsumeError(envelope: EventEnvelope<any>, error: Error) {\n const span = trace.getActiveSpan();\n if (span) {\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.recordException(error);\n }\n },\n };\n}\n"],"mappings":";;;AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AA0BA,SAAS,sBAA4C;AAC1D,QAAM,SAAS,MAAM,UAAU,wBAAwB;AAEvD,SAAO;AAAA,IACL,WAAW,OAAe,SAAiC;AACzD,kBAAY,OAAO,QAAQ,OAAO,GAAG,OAAO;AAAA,IAC9C;AAAA,IAEA,UAAU,QAAgB;AAAA,IAI1B;AAAA,IAEA,cAAc,UAA8B;AAC1C,YAAM,YAAY,YAAY,QAAQ,QAAQ,OAAO,GAAG,SAAS,OAAO;AACxE,YAAM,OAAO,OAAO;AAAA,QAClB,iBAAiB,SAAS,KAAK;AAAA,QAC/B;AAAA,UACE,MAAM,SAAS;AAAA,UACf,YAAY;AAAA,YACV,oBAAoB;AAAA,YACpB,8BAA8B,SAAS;AAAA,YACvC,wBAAwB,SAAS;AAAA,YACjC,6BAA6B,SAAS;AAAA,YACtC,0BAA0B,SAAS;AAAA,UACrC;AAAA,QACF;AAAA,QACA;AAAA,MACF;AACA,aAAO,MAAM,KAAK,IAAI;AAAA,IACxB;AAAA,IAEA,eAAe,UAA8B,OAAc;AACzD,YAAM,OAAO,MAAM,cAAc;AACjC,UAAI,MAAM;AACR,aAAK,UAAU,EAAE,MAAM,eAAe,OAAO,SAAS,MAAM,QAAQ,CAAC;AACrE,aAAK,gBAAgB,KAAK;AAAA,MAC5B;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -0,0 +1,104 @@
1
+ import { T as TopicMapConstraint, I as IKafkaClient } from './envelope-QK1trQu4.mjs';
2
+
3
+ /**
4
+ * Fully typed mock of `IKafkaClient<T>` where every method is a mock function.
5
+ * Compatible with Jest, Vitest, or any framework whose `fn()` returns
6
+ * an object with `.mock`, `.mockResolvedValue`, etc.
7
+ */
8
+ type MockKafkaClient<T extends TopicMapConstraint<T>> = {
9
+ [K in keyof IKafkaClient<T>]: IKafkaClient<T>[K] & Record<string, any>;
10
+ };
11
+ /** Factory that creates a no-op mock function (e.g. `() => jest.fn()`). */
12
+ type MockFactory = () => (...args: any[]) => any;
13
+ /**
14
+ * Create a fully typed mock implementing every `IKafkaClient<T>` method.
15
+ * Useful for unit-testing services that depend on `KafkaClient` without
16
+ * touching a real broker.
17
+ *
18
+ * Auto-detects Jest (`jest.fn()`) or Vitest (`vi.fn()`). Pass a custom
19
+ * `mockFactory` for other frameworks.
20
+ *
21
+ * All methods resolve to sensible defaults:
22
+ * - `checkStatus()` → `{ topics: [] }`
23
+ * - `getClientId()` → `"mock-client"`
24
+ * - void methods → `undefined`
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const kafka = createMockKafkaClient<MyTopics>();
29
+ *
30
+ * const service = new OrdersService(kafka);
31
+ * await service.createOrder();
32
+ *
33
+ * expect(kafka.sendMessage).toHaveBeenCalledWith(
34
+ * 'order.created',
35
+ * expect.objectContaining({ orderId: '123' }),
36
+ * );
37
+ * ```
38
+ */
39
+ declare function createMockKafkaClient<T extends TopicMapConstraint<T>>(mockFactory?: MockFactory): MockKafkaClient<T>;
40
+
41
+ /** Options for `KafkaTestContainer`. */
42
+ interface KafkaTestContainerOptions {
43
+ /** Docker image. Default: `"confluentinc/cp-kafka:7.7.0"`. */
44
+ image?: string;
45
+ /** Warm up the transactional coordinator on start. Default: `true`. */
46
+ transactionWarmup?: boolean;
47
+ /** Topics to pre-create. Each entry can be a string (1 partition) or `{ topic, numPartitions }`. */
48
+ topics?: Array<string | {
49
+ topic: string;
50
+ numPartitions?: number;
51
+ }>;
52
+ }
53
+ /**
54
+ * Thin wrapper around `@testcontainers/kafka` that starts a single-node
55
+ * KRaft Kafka container and exposes `brokers` for use with `KafkaClient`.
56
+ *
57
+ * Handles common setup pain points:
58
+ * - Transaction coordinator warmup (avoids transactional producer hangs)
59
+ * - Topic pre-creation (avoids race conditions)
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const container = new KafkaTestContainer({ topics: ['orders', 'payments'] });
64
+ * const brokers = await container.start();
65
+ *
66
+ * const kafka = new KafkaClient('test', 'test-group', brokers);
67
+ * // ... run tests ...
68
+ *
69
+ * await container.stop();
70
+ * ```
71
+ *
72
+ * @example Jest lifecycle
73
+ * ```ts
74
+ * let container: KafkaTestContainer;
75
+ * let brokers: string[];
76
+ *
77
+ * beforeAll(async () => {
78
+ * container = new KafkaTestContainer({ topics: ['orders'] });
79
+ * brokers = await container.start();
80
+ * }, 120_000);
81
+ *
82
+ * afterAll(() => container.stop());
83
+ * ```
84
+ */
85
+ declare class KafkaTestContainer {
86
+ private container;
87
+ private readonly image;
88
+ private readonly transactionWarmup;
89
+ private readonly topics;
90
+ constructor(options?: KafkaTestContainerOptions);
91
+ /**
92
+ * Start the Kafka container, pre-create topics, and optionally warm up
93
+ * the transaction coordinator.
94
+ *
95
+ * @returns Broker connection strings, e.g. `["localhost:55123"]`.
96
+ */
97
+ start(): Promise<string[]>;
98
+ /** Stop and remove the container. */
99
+ stop(): Promise<void>;
100
+ /** Broker connection strings. Throws if container is not started. */
101
+ get brokers(): string[];
102
+ }
103
+
104
+ export { KafkaTestContainer, type KafkaTestContainerOptions, type MockKafkaClient, createMockKafkaClient };
@@ -0,0 +1,104 @@
1
+ import { T as TopicMapConstraint, I as IKafkaClient } from './envelope-QK1trQu4.js';
2
+
3
+ /**
4
+ * Fully typed mock of `IKafkaClient<T>` where every method is a mock function.
5
+ * Compatible with Jest, Vitest, or any framework whose `fn()` returns
6
+ * an object with `.mock`, `.mockResolvedValue`, etc.
7
+ */
8
+ type MockKafkaClient<T extends TopicMapConstraint<T>> = {
9
+ [K in keyof IKafkaClient<T>]: IKafkaClient<T>[K] & Record<string, any>;
10
+ };
11
+ /** Factory that creates a no-op mock function (e.g. `() => jest.fn()`). */
12
+ type MockFactory = () => (...args: any[]) => any;
13
+ /**
14
+ * Create a fully typed mock implementing every `IKafkaClient<T>` method.
15
+ * Useful for unit-testing services that depend on `KafkaClient` without
16
+ * touching a real broker.
17
+ *
18
+ * Auto-detects Jest (`jest.fn()`) or Vitest (`vi.fn()`). Pass a custom
19
+ * `mockFactory` for other frameworks.
20
+ *
21
+ * All methods resolve to sensible defaults:
22
+ * - `checkStatus()` → `{ topics: [] }`
23
+ * - `getClientId()` → `"mock-client"`
24
+ * - void methods → `undefined`
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * const kafka = createMockKafkaClient<MyTopics>();
29
+ *
30
+ * const service = new OrdersService(kafka);
31
+ * await service.createOrder();
32
+ *
33
+ * expect(kafka.sendMessage).toHaveBeenCalledWith(
34
+ * 'order.created',
35
+ * expect.objectContaining({ orderId: '123' }),
36
+ * );
37
+ * ```
38
+ */
39
+ declare function createMockKafkaClient<T extends TopicMapConstraint<T>>(mockFactory?: MockFactory): MockKafkaClient<T>;
40
+
41
+ /** Options for `KafkaTestContainer`. */
42
+ interface KafkaTestContainerOptions {
43
+ /** Docker image. Default: `"confluentinc/cp-kafka:7.7.0"`. */
44
+ image?: string;
45
+ /** Warm up the transactional coordinator on start. Default: `true`. */
46
+ transactionWarmup?: boolean;
47
+ /** Topics to pre-create. Each entry can be a string (1 partition) or `{ topic, numPartitions }`. */
48
+ topics?: Array<string | {
49
+ topic: string;
50
+ numPartitions?: number;
51
+ }>;
52
+ }
53
+ /**
54
+ * Thin wrapper around `@testcontainers/kafka` that starts a single-node
55
+ * KRaft Kafka container and exposes `brokers` for use with `KafkaClient`.
56
+ *
57
+ * Handles common setup pain points:
58
+ * - Transaction coordinator warmup (avoids transactional producer hangs)
59
+ * - Topic pre-creation (avoids race conditions)
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * const container = new KafkaTestContainer({ topics: ['orders', 'payments'] });
64
+ * const brokers = await container.start();
65
+ *
66
+ * const kafka = new KafkaClient('test', 'test-group', brokers);
67
+ * // ... run tests ...
68
+ *
69
+ * await container.stop();
70
+ * ```
71
+ *
72
+ * @example Jest lifecycle
73
+ * ```ts
74
+ * let container: KafkaTestContainer;
75
+ * let brokers: string[];
76
+ *
77
+ * beforeAll(async () => {
78
+ * container = new KafkaTestContainer({ topics: ['orders'] });
79
+ * brokers = await container.start();
80
+ * }, 120_000);
81
+ *
82
+ * afterAll(() => container.stop());
83
+ * ```
84
+ */
85
+ declare class KafkaTestContainer {
86
+ private container;
87
+ private readonly image;
88
+ private readonly transactionWarmup;
89
+ private readonly topics;
90
+ constructor(options?: KafkaTestContainerOptions);
91
+ /**
92
+ * Start the Kafka container, pre-create topics, and optionally warm up
93
+ * the transaction coordinator.
94
+ *
95
+ * @returns Broker connection strings, e.g. `["localhost:55123"]`.
96
+ */
97
+ start(): Promise<string[]>;
98
+ /** Stop and remove the container. */
99
+ stop(): Promise<void>;
100
+ /** Broker connection strings. Throws if container is not started. */
101
+ get brokers(): string[];
102
+ }
103
+
104
+ export { KafkaTestContainer, type KafkaTestContainerOptions, type MockKafkaClient, createMockKafkaClient };
@@ -0,0 +1,157 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/testing.ts
21
+ var testing_exports = {};
22
+ __export(testing_exports, {
23
+ KafkaTestContainer: () => KafkaTestContainer,
24
+ createMockKafkaClient: () => createMockKafkaClient
25
+ });
26
+ module.exports = __toCommonJS(testing_exports);
27
+
28
+ // src/testing/mock-client.ts
29
+ function detectMockFactory() {
30
+ try {
31
+ const jestFn = eval("typeof jest !== 'undefined' && jest.fn");
32
+ if (typeof jestFn === "function") return () => eval("jest.fn")();
33
+ } catch {
34
+ }
35
+ try {
36
+ const viFn = eval("typeof vi !== 'undefined' && vi.fn");
37
+ if (typeof viFn === "function") return () => eval("vi.fn")();
38
+ } catch {
39
+ }
40
+ throw new Error(
41
+ "createMockKafkaClient: no mock framework detected (jest/vitest). Pass a custom mockFactory."
42
+ );
43
+ }
44
+ function createMockKafkaClient(mockFactory) {
45
+ const fn = mockFactory ?? detectMockFactory();
46
+ const mock = () => fn();
47
+ const resolved = (value) => mock().mockResolvedValue(value);
48
+ const returning = (value) => mock().mockReturnValue(value);
49
+ return {
50
+ checkStatus: resolved({ topics: [] }),
51
+ getClientId: returning("mock-client"),
52
+ sendMessage: resolved(void 0),
53
+ sendBatch: resolved(void 0),
54
+ transaction: mock().mockImplementation(async (cb) => {
55
+ const ctx = {
56
+ send: resolved(void 0),
57
+ sendBatch: resolved(void 0)
58
+ };
59
+ await cb(ctx);
60
+ }),
61
+ startConsumer: resolved(void 0),
62
+ startBatchConsumer: resolved(void 0),
63
+ stopConsumer: resolved(void 0),
64
+ disconnect: resolved(void 0)
65
+ };
66
+ }
67
+
68
+ // src/testing/test-container.ts
69
+ var import_kafka = require("@testcontainers/kafka");
70
+ var import_kafka_javascript = require("@confluentinc/kafka-javascript");
71
+ var { Kafka, logLevel: KafkaLogLevel } = import_kafka_javascript.KafkaJS;
72
+ var KafkaTestContainer = class {
73
+ container;
74
+ image;
75
+ transactionWarmup;
76
+ topics;
77
+ constructor(options) {
78
+ this.image = options?.image ?? "confluentinc/cp-kafka:7.7.0";
79
+ this.transactionWarmup = options?.transactionWarmup ?? true;
80
+ this.topics = options?.topics ?? [];
81
+ }
82
+ /**
83
+ * Start the Kafka container, pre-create topics, and optionally warm up
84
+ * the transaction coordinator.
85
+ *
86
+ * @returns Broker connection strings, e.g. `["localhost:55123"]`.
87
+ */
88
+ async start() {
89
+ this.container = await new import_kafka.KafkaContainer(this.image).withKraft().withExposedPorts(9093).withEnvironment({
90
+ KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: "1",
91
+ KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: "1"
92
+ }).start();
93
+ const host = this.container.getHost();
94
+ const port = this.container.getMappedPort(9093);
95
+ const brokers = [`${host}:${port}`];
96
+ const kafka = new Kafka({
97
+ kafkaJS: {
98
+ clientId: "test-container-setup",
99
+ brokers,
100
+ logLevel: KafkaLogLevel.NOTHING
101
+ }
102
+ });
103
+ if (this.topics.length > 0) {
104
+ const admin = kafka.admin();
105
+ await admin.connect();
106
+ await admin.createTopics({
107
+ topics: this.topics.map(
108
+ (t) => typeof t === "string" ? { topic: t, numPartitions: 1 } : { topic: t.topic, numPartitions: t.numPartitions ?? 1 }
109
+ )
110
+ });
111
+ await admin.disconnect();
112
+ }
113
+ if (this.transactionWarmup) {
114
+ const warmupKafka = new Kafka({
115
+ kafkaJS: {
116
+ clientId: "test-container-warmup",
117
+ brokers,
118
+ logLevel: KafkaLogLevel.NOTHING
119
+ }
120
+ });
121
+ const txProducer = warmupKafka.producer({
122
+ kafkaJS: {
123
+ transactionalId: "test-container-warmup-tx",
124
+ idempotent: true,
125
+ maxInFlightRequests: 1
126
+ }
127
+ });
128
+ await txProducer.connect();
129
+ const tx = await txProducer.transaction();
130
+ await tx.abort();
131
+ await txProducer.disconnect();
132
+ }
133
+ return brokers;
134
+ }
135
+ /** Stop and remove the container. */
136
+ async stop() {
137
+ await this.container?.stop();
138
+ this.container = void 0;
139
+ }
140
+ /** Broker connection strings. Throws if container is not started. */
141
+ get brokers() {
142
+ if (!this.container) {
143
+ throw new Error(
144
+ "KafkaTestContainer is not started. Call start() first."
145
+ );
146
+ }
147
+ const host = this.container.getHost();
148
+ const port = this.container.getMappedPort(9093);
149
+ return [`${host}:${port}`];
150
+ }
151
+ };
152
+ // Annotate the CommonJS export names for ESM import in node:
153
+ 0 && (module.exports = {
154
+ KafkaTestContainer,
155
+ createMockKafkaClient
156
+ });
157
+ //# sourceMappingURL=testing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/testing.ts","../src/testing/mock-client.ts","../src/testing/test-container.ts"],"sourcesContent":["export * from \"./testing/index\";\n","import type { IKafkaClient, TopicMapConstraint } from \"../client/types\";\n\n/**\n * Fully typed mock of `IKafkaClient<T>` where every method is a mock function.\n * Compatible with Jest, Vitest, or any framework whose `fn()` returns\n * an object with `.mock`, `.mockResolvedValue`, etc.\n */\nexport type MockKafkaClient<T extends TopicMapConstraint<T>> = {\n [K in keyof IKafkaClient<T>]: IKafkaClient<T>[K] & Record<string, any>;\n};\n\n/** Factory that creates a no-op mock function (e.g. `() => jest.fn()`). */\nexport type MockFactory = () => (...args: any[]) => any;\n\nfunction detectMockFactory(): MockFactory {\n \n // Jest and Vitest inject globals into the test environment scope,\n // which may not be on `globalThis`. Use eval to check the actual scope.\n try {\n const jestFn = eval(\"typeof jest !== 'undefined' && jest.fn\");\n if (typeof jestFn === \"function\") return () => (eval(\"jest.fn\") as () => (...args: any[]) => any)();\n } catch { /* not available */ }\n try {\n const viFn = eval(\"typeof vi !== 'undefined' && vi.fn\");\n if (typeof viFn === \"function\") return () => (eval(\"vi.fn\") as () => (...args: any[]) => any)();\n } catch { /* not available */ }\n \n\n throw new Error(\n \"createMockKafkaClient: no mock framework detected (jest/vitest). \" +\n \"Pass a custom mockFactory.\",\n );\n}\n\n/**\n * Create a fully typed mock implementing every `IKafkaClient<T>` method.\n * Useful for unit-testing services that depend on `KafkaClient` without\n * touching a real broker.\n *\n * Auto-detects Jest (`jest.fn()`) or Vitest (`vi.fn()`). Pass a custom\n * `mockFactory` for other frameworks.\n *\n * All methods resolve to sensible defaults:\n * - `checkStatus()` → `{ topics: [] }`\n * - `getClientId()` → `\"mock-client\"`\n * - void methods → `undefined`\n *\n * @example\n * ```ts\n * const kafka = createMockKafkaClient<MyTopics>();\n *\n * const service = new OrdersService(kafka);\n * await service.createOrder();\n *\n * expect(kafka.sendMessage).toHaveBeenCalledWith(\n * 'order.created',\n * expect.objectContaining({ orderId: '123' }),\n * );\n * ```\n */\nexport function createMockKafkaClient<T extends TopicMapConstraint<T>>(\n mockFactory?: MockFactory,\n): MockKafkaClient<T> {\n const fn = mockFactory ?? detectMockFactory();\n\n const mock = () => fn() as any;\n const resolved = (value: unknown) => mock().mockResolvedValue(value);\n const returning = (value: unknown) => mock().mockReturnValue(value);\n\n return {\n checkStatus: resolved({ topics: [] }),\n getClientId: returning(\"mock-client\"),\n sendMessage: resolved(undefined),\n sendBatch: resolved(undefined),\n transaction: mock().mockImplementation(async (cb: (ctx: Record<string, unknown>) => Promise<void>) => {\n const ctx = {\n send: resolved(undefined),\n sendBatch: resolved(undefined),\n };\n await cb(ctx);\n }),\n startConsumer: resolved(undefined),\n startBatchConsumer: resolved(undefined),\n stopConsumer: resolved(undefined),\n disconnect: resolved(undefined),\n } as unknown as MockKafkaClient<T>;\n}\n","import {\n KafkaContainer,\n type StartedKafkaContainer,\n} from \"@testcontainers/kafka\";\nimport { KafkaJS } from \"@confluentinc/kafka-javascript\";\nconst { Kafka, logLevel: KafkaLogLevel } = KafkaJS;\n\n/** Options for `KafkaTestContainer`. */\nexport interface KafkaTestContainerOptions {\n /** Docker image. Default: `\"confluentinc/cp-kafka:7.7.0\"`. */\n image?: string;\n /** Warm up the transactional coordinator on start. Default: `true`. */\n transactionWarmup?: boolean;\n /** Topics to pre-create. Each entry can be a string (1 partition) or `{ topic, numPartitions }`. */\n topics?: Array<string | { topic: string; numPartitions?: number }>;\n}\n\n/**\n * Thin wrapper around `@testcontainers/kafka` that starts a single-node\n * KRaft Kafka container and exposes `brokers` for use with `KafkaClient`.\n *\n * Handles common setup pain points:\n * - Transaction coordinator warmup (avoids transactional producer hangs)\n * - Topic pre-creation (avoids race conditions)\n *\n * @example\n * ```ts\n * const container = new KafkaTestContainer({ topics: ['orders', 'payments'] });\n * const brokers = await container.start();\n *\n * const kafka = new KafkaClient('test', 'test-group', brokers);\n * // ... run tests ...\n *\n * await container.stop();\n * ```\n *\n * @example Jest lifecycle\n * ```ts\n * let container: KafkaTestContainer;\n * let brokers: string[];\n *\n * beforeAll(async () => {\n * container = new KafkaTestContainer({ topics: ['orders'] });\n * brokers = await container.start();\n * }, 120_000);\n *\n * afterAll(() => container.stop());\n * ```\n */\nexport class KafkaTestContainer {\n private container: StartedKafkaContainer | undefined;\n private readonly image: string;\n private readonly transactionWarmup: boolean;\n private readonly topics: Array<\n string | { topic: string; numPartitions?: number }\n >;\n\n constructor(options?: KafkaTestContainerOptions) {\n this.image = options?.image ?? \"confluentinc/cp-kafka:7.7.0\";\n this.transactionWarmup = options?.transactionWarmup ?? true;\n this.topics = options?.topics ?? [];\n }\n\n /**\n * Start the Kafka container, pre-create topics, and optionally warm up\n * the transaction coordinator.\n *\n * @returns Broker connection strings, e.g. `[\"localhost:55123\"]`.\n */\n async start(): Promise<string[]> {\n this.container = await new KafkaContainer(this.image)\n .withKraft()\n .withExposedPorts(9093)\n .withEnvironment({\n KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: \"1\",\n KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: \"1\",\n })\n .start();\n\n const host = this.container.getHost();\n const port = this.container.getMappedPort(9093);\n const brokers = [`${host}:${port}`];\n\n const kafka = new Kafka({\n kafkaJS: {\n clientId: \"test-container-setup\",\n brokers,\n logLevel: KafkaLogLevel.NOTHING,\n },\n });\n\n if (this.topics.length > 0) {\n const admin = kafka.admin();\n await admin.connect();\n await admin.createTopics({\n topics: this.topics.map((t) =>\n typeof t === \"string\"\n ? { topic: t, numPartitions: 1 }\n : { topic: t.topic, numPartitions: t.numPartitions ?? 1 },\n ),\n });\n await admin.disconnect();\n }\n\n if (this.transactionWarmup) {\n const warmupKafka = new Kafka({\n kafkaJS: {\n clientId: \"test-container-warmup\",\n brokers,\n logLevel: KafkaLogLevel.NOTHING,\n },\n });\n const txProducer = warmupKafka.producer({\n kafkaJS: {\n transactionalId: \"test-container-warmup-tx\",\n idempotent: true,\n maxInFlightRequests: 1,\n },\n });\n await txProducer.connect();\n const tx = await txProducer.transaction();\n await tx.abort();\n await txProducer.disconnect();\n }\n\n return brokers;\n }\n\n /** Stop and remove the container. */\n async stop(): Promise<void> {\n await this.container?.stop();\n this.container = undefined;\n }\n\n /** Broker connection strings. Throws if container is not started. */\n get brokers(): string[] {\n if (!this.container) {\n throw new Error(\n \"KafkaTestContainer is not started. Call start() first.\",\n );\n }\n const host = this.container.getHost();\n const port = this.container.getMappedPort(9093);\n return [`${host}:${port}`];\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,133 @@
1
+ import "./chunk-EQQGB2QZ.mjs";
2
+
3
+ // src/testing/mock-client.ts
4
+ function detectMockFactory() {
5
+ try {
6
+ const jestFn = eval("typeof jest !== 'undefined' && jest.fn");
7
+ if (typeof jestFn === "function") return () => eval("jest.fn")();
8
+ } catch {
9
+ }
10
+ try {
11
+ const viFn = eval("typeof vi !== 'undefined' && vi.fn");
12
+ if (typeof viFn === "function") return () => eval("vi.fn")();
13
+ } catch {
14
+ }
15
+ throw new Error(
16
+ "createMockKafkaClient: no mock framework detected (jest/vitest). Pass a custom mockFactory."
17
+ );
18
+ }
19
+ function createMockKafkaClient(mockFactory) {
20
+ const fn = mockFactory ?? detectMockFactory();
21
+ const mock = () => fn();
22
+ const resolved = (value) => mock().mockResolvedValue(value);
23
+ const returning = (value) => mock().mockReturnValue(value);
24
+ return {
25
+ checkStatus: resolved({ topics: [] }),
26
+ getClientId: returning("mock-client"),
27
+ sendMessage: resolved(void 0),
28
+ sendBatch: resolved(void 0),
29
+ transaction: mock().mockImplementation(async (cb) => {
30
+ const ctx = {
31
+ send: resolved(void 0),
32
+ sendBatch: resolved(void 0)
33
+ };
34
+ await cb(ctx);
35
+ }),
36
+ startConsumer: resolved(void 0),
37
+ startBatchConsumer: resolved(void 0),
38
+ stopConsumer: resolved(void 0),
39
+ disconnect: resolved(void 0)
40
+ };
41
+ }
42
+
43
+ // src/testing/test-container.ts
44
+ import {
45
+ KafkaContainer
46
+ } from "@testcontainers/kafka";
47
+ import { KafkaJS } from "@confluentinc/kafka-javascript";
48
+ var { Kafka, logLevel: KafkaLogLevel } = KafkaJS;
49
+ var KafkaTestContainer = class {
50
+ container;
51
+ image;
52
+ transactionWarmup;
53
+ topics;
54
+ constructor(options) {
55
+ this.image = options?.image ?? "confluentinc/cp-kafka:7.7.0";
56
+ this.transactionWarmup = options?.transactionWarmup ?? true;
57
+ this.topics = options?.topics ?? [];
58
+ }
59
+ /**
60
+ * Start the Kafka container, pre-create topics, and optionally warm up
61
+ * the transaction coordinator.
62
+ *
63
+ * @returns Broker connection strings, e.g. `["localhost:55123"]`.
64
+ */
65
+ async start() {
66
+ this.container = await new KafkaContainer(this.image).withKraft().withExposedPorts(9093).withEnvironment({
67
+ KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: "1",
68
+ KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: "1"
69
+ }).start();
70
+ const host = this.container.getHost();
71
+ const port = this.container.getMappedPort(9093);
72
+ const brokers = [`${host}:${port}`];
73
+ const kafka = new Kafka({
74
+ kafkaJS: {
75
+ clientId: "test-container-setup",
76
+ brokers,
77
+ logLevel: KafkaLogLevel.NOTHING
78
+ }
79
+ });
80
+ if (this.topics.length > 0) {
81
+ const admin = kafka.admin();
82
+ await admin.connect();
83
+ await admin.createTopics({
84
+ topics: this.topics.map(
85
+ (t) => typeof t === "string" ? { topic: t, numPartitions: 1 } : { topic: t.topic, numPartitions: t.numPartitions ?? 1 }
86
+ )
87
+ });
88
+ await admin.disconnect();
89
+ }
90
+ if (this.transactionWarmup) {
91
+ const warmupKafka = new Kafka({
92
+ kafkaJS: {
93
+ clientId: "test-container-warmup",
94
+ brokers,
95
+ logLevel: KafkaLogLevel.NOTHING
96
+ }
97
+ });
98
+ const txProducer = warmupKafka.producer({
99
+ kafkaJS: {
100
+ transactionalId: "test-container-warmup-tx",
101
+ idempotent: true,
102
+ maxInFlightRequests: 1
103
+ }
104
+ });
105
+ await txProducer.connect();
106
+ const tx = await txProducer.transaction();
107
+ await tx.abort();
108
+ await txProducer.disconnect();
109
+ }
110
+ return brokers;
111
+ }
112
+ /** Stop and remove the container. */
113
+ async stop() {
114
+ await this.container?.stop();
115
+ this.container = void 0;
116
+ }
117
+ /** Broker connection strings. Throws if container is not started. */
118
+ get brokers() {
119
+ if (!this.container) {
120
+ throw new Error(
121
+ "KafkaTestContainer is not started. Call start() first."
122
+ );
123
+ }
124
+ const host = this.container.getHost();
125
+ const port = this.container.getMappedPort(9093);
126
+ return [`${host}:${port}`];
127
+ }
128
+ };
129
+ export {
130
+ KafkaTestContainer,
131
+ createMockKafkaClient
132
+ };
133
+ //# sourceMappingURL=testing.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/testing/mock-client.ts","../src/testing/test-container.ts"],"sourcesContent":["import type { IKafkaClient, TopicMapConstraint } from \"../client/types\";\n\n/**\n * Fully typed mock of `IKafkaClient<T>` where every method is a mock function.\n * Compatible with Jest, Vitest, or any framework whose `fn()` returns\n * an object with `.mock`, `.mockResolvedValue`, etc.\n */\nexport type MockKafkaClient<T extends TopicMapConstraint<T>> = {\n [K in keyof IKafkaClient<T>]: IKafkaClient<T>[K] & Record<string, any>;\n};\n\n/** Factory that creates a no-op mock function (e.g. `() => jest.fn()`). */\nexport type MockFactory = () => (...args: any[]) => any;\n\nfunction detectMockFactory(): MockFactory {\n \n // Jest and Vitest inject globals into the test environment scope,\n // which may not be on `globalThis`. Use eval to check the actual scope.\n try {\n const jestFn = eval(\"typeof jest !== 'undefined' && jest.fn\");\n if (typeof jestFn === \"function\") return () => (eval(\"jest.fn\") as () => (...args: any[]) => any)();\n } catch { /* not available */ }\n try {\n const viFn = eval(\"typeof vi !== 'undefined' && vi.fn\");\n if (typeof viFn === \"function\") return () => (eval(\"vi.fn\") as () => (...args: any[]) => any)();\n } catch { /* not available */ }\n \n\n throw new Error(\n \"createMockKafkaClient: no mock framework detected (jest/vitest). \" +\n \"Pass a custom mockFactory.\",\n );\n}\n\n/**\n * Create a fully typed mock implementing every `IKafkaClient<T>` method.\n * Useful for unit-testing services that depend on `KafkaClient` without\n * touching a real broker.\n *\n * Auto-detects Jest (`jest.fn()`) or Vitest (`vi.fn()`). Pass a custom\n * `mockFactory` for other frameworks.\n *\n * All methods resolve to sensible defaults:\n * - `checkStatus()` → `{ topics: [] }`\n * - `getClientId()` → `\"mock-client\"`\n * - void methods → `undefined`\n *\n * @example\n * ```ts\n * const kafka = createMockKafkaClient<MyTopics>();\n *\n * const service = new OrdersService(kafka);\n * await service.createOrder();\n *\n * expect(kafka.sendMessage).toHaveBeenCalledWith(\n * 'order.created',\n * expect.objectContaining({ orderId: '123' }),\n * );\n * ```\n */\nexport function createMockKafkaClient<T extends TopicMapConstraint<T>>(\n mockFactory?: MockFactory,\n): MockKafkaClient<T> {\n const fn = mockFactory ?? detectMockFactory();\n\n const mock = () => fn() as any;\n const resolved = (value: unknown) => mock().mockResolvedValue(value);\n const returning = (value: unknown) => mock().mockReturnValue(value);\n\n return {\n checkStatus: resolved({ topics: [] }),\n getClientId: returning(\"mock-client\"),\n sendMessage: resolved(undefined),\n sendBatch: resolved(undefined),\n transaction: mock().mockImplementation(async (cb: (ctx: Record<string, unknown>) => Promise<void>) => {\n const ctx = {\n send: resolved(undefined),\n sendBatch: resolved(undefined),\n };\n await cb(ctx);\n }),\n startConsumer: resolved(undefined),\n startBatchConsumer: resolved(undefined),\n stopConsumer: resolved(undefined),\n disconnect: resolved(undefined),\n } as unknown as MockKafkaClient<T>;\n}\n","import {\n KafkaContainer,\n type StartedKafkaContainer,\n} from \"@testcontainers/kafka\";\nimport { KafkaJS } from \"@confluentinc/kafka-javascript\";\nconst { Kafka, logLevel: KafkaLogLevel } = KafkaJS;\n\n/** Options for `KafkaTestContainer`. */\nexport interface KafkaTestContainerOptions {\n /** Docker image. Default: `\"confluentinc/cp-kafka:7.7.0\"`. */\n image?: string;\n /** Warm up the transactional coordinator on start. Default: `true`. */\n transactionWarmup?: boolean;\n /** Topics to pre-create. Each entry can be a string (1 partition) or `{ topic, numPartitions }`. */\n topics?: Array<string | { topic: string; numPartitions?: number }>;\n}\n\n/**\n * Thin wrapper around `@testcontainers/kafka` that starts a single-node\n * KRaft Kafka container and exposes `brokers` for use with `KafkaClient`.\n *\n * Handles common setup pain points:\n * - Transaction coordinator warmup (avoids transactional producer hangs)\n * - Topic pre-creation (avoids race conditions)\n *\n * @example\n * ```ts\n * const container = new KafkaTestContainer({ topics: ['orders', 'payments'] });\n * const brokers = await container.start();\n *\n * const kafka = new KafkaClient('test', 'test-group', brokers);\n * // ... run tests ...\n *\n * await container.stop();\n * ```\n *\n * @example Jest lifecycle\n * ```ts\n * let container: KafkaTestContainer;\n * let brokers: string[];\n *\n * beforeAll(async () => {\n * container = new KafkaTestContainer({ topics: ['orders'] });\n * brokers = await container.start();\n * }, 120_000);\n *\n * afterAll(() => container.stop());\n * ```\n */\nexport class KafkaTestContainer {\n private container: StartedKafkaContainer | undefined;\n private readonly image: string;\n private readonly transactionWarmup: boolean;\n private readonly topics: Array<\n string | { topic: string; numPartitions?: number }\n >;\n\n constructor(options?: KafkaTestContainerOptions) {\n this.image = options?.image ?? \"confluentinc/cp-kafka:7.7.0\";\n this.transactionWarmup = options?.transactionWarmup ?? true;\n this.topics = options?.topics ?? [];\n }\n\n /**\n * Start the Kafka container, pre-create topics, and optionally warm up\n * the transaction coordinator.\n *\n * @returns Broker connection strings, e.g. `[\"localhost:55123\"]`.\n */\n async start(): Promise<string[]> {\n this.container = await new KafkaContainer(this.image)\n .withKraft()\n .withExposedPorts(9093)\n .withEnvironment({\n KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: \"1\",\n KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: \"1\",\n })\n .start();\n\n const host = this.container.getHost();\n const port = this.container.getMappedPort(9093);\n const brokers = [`${host}:${port}`];\n\n const kafka = new Kafka({\n kafkaJS: {\n clientId: \"test-container-setup\",\n brokers,\n logLevel: KafkaLogLevel.NOTHING,\n },\n });\n\n if (this.topics.length > 0) {\n const admin = kafka.admin();\n await admin.connect();\n await admin.createTopics({\n topics: this.topics.map((t) =>\n typeof t === \"string\"\n ? { topic: t, numPartitions: 1 }\n : { topic: t.topic, numPartitions: t.numPartitions ?? 1 },\n ),\n });\n await admin.disconnect();\n }\n\n if (this.transactionWarmup) {\n const warmupKafka = new Kafka({\n kafkaJS: {\n clientId: \"test-container-warmup\",\n brokers,\n logLevel: KafkaLogLevel.NOTHING,\n },\n });\n const txProducer = warmupKafka.producer({\n kafkaJS: {\n transactionalId: \"test-container-warmup-tx\",\n idempotent: true,\n maxInFlightRequests: 1,\n },\n });\n await txProducer.connect();\n const tx = await txProducer.transaction();\n await tx.abort();\n await txProducer.disconnect();\n }\n\n return brokers;\n }\n\n /** Stop and remove the container. */\n async stop(): Promise<void> {\n await this.container?.stop();\n this.container = undefined;\n }\n\n /** Broker connection strings. Throws if container is not started. */\n get brokers(): string[] {\n if (!this.container) {\n throw new Error(\n \"KafkaTestContainer is not started. Call start() first.\",\n );\n }\n const host = this.container.getHost();\n const port = this.container.getMappedPort(9093);\n return [`${host}:${port}`];\n }\n}\n"],"mappings":"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","names":[]}