@drarzter/kafka-client 0.2.2 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +104 -12
- package/dist/chunk-A56D7HXR.mjs +545 -0
- package/dist/chunk-A56D7HXR.mjs.map +1 -0
- package/dist/chunk-EQQGB2QZ.mjs +17 -0
- package/dist/chunk-EQQGB2QZ.mjs.map +1 -0
- package/dist/core.d.mts +118 -0
- package/dist/core.d.ts +118 -0
- package/dist/core.js +575 -0
- package/dist/core.js.map +1 -0
- package/dist/core.mjs +16 -0
- package/dist/core.mjs.map +1 -0
- package/dist/index.d.mts +5 -324
- package/dist/index.d.ts +5 -324
- package/dist/index.js +73 -41
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +29 -534
- package/dist/index.mjs.map +1 -1
- package/dist/testing.d.mts +104 -0
- package/dist/testing.d.ts +104 -0
- package/dist/testing.js +151 -0
- package/dist/testing.js.map +1 -0
- package/dist/testing.mjs +127 -0
- package/dist/testing.mjs.map +1 -0
- package/dist/types-CtwJihJ3.d.mts +224 -0
- package/dist/types-CtwJihJ3.d.ts +224 -0
- package/package.json +23 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/module/kafka.module.ts","../src/client/kafka.client.ts","../src/client/errors.ts","../src/module/kafka.constants.ts","../src/module/kafka.explorer.ts","../src/decorators/kafka.decorator.ts","../src/client/topic.ts","../src/health/kafka.health.ts"],"sourcesContent":["import { Module, DynamicModule, Provider } from \"@nestjs/common\";\nimport { DiscoveryModule } from \"@nestjs/core\";\nimport {\n KafkaClient,\n ClientId,\n GroupId,\n TopicMapConstraint,\n} from \"../client/kafka.client\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { KafkaExplorer } from \"./kafka.explorer\";\n\n/** Synchronous configuration for `KafkaModule.register()`. */\nexport interface KafkaModuleOptions {\n /** Optional name for multi-client setups. Must match `@InjectKafkaClient(name)`. */\n name?: string;\n /** Unique Kafka client identifier. */\n clientId: ClientId;\n /** Consumer group identifier. */\n groupId: GroupId;\n /** List of Kafka broker addresses. */\n brokers: string[];\n /** If true, makes KAFKA_CLIENT available globally without importing KafkaModule in every feature module. */\n isGlobal?: boolean;\n /** Auto-create topics via admin on first use (send/consume). Useful for development. */\n autoCreateTopics?: boolean;\n}\n\n/** Async configuration for `KafkaModule.registerAsync()` with dependency injection. */\nexport interface KafkaModuleAsyncOptions {\n name?: string;\n /** If true, makes KAFKA_CLIENT available globally without importing KafkaModule in every feature module. */\n isGlobal?: boolean;\n /** Auto-create topics via admin on first use (send/consume). Useful for development. */\n autoCreateTopics?: boolean;\n imports?: any[];\n useFactory: (\n ...args: any[]\n ) => KafkaModuleOptions | Promise<KafkaModuleOptions>;\n inject?: any[];\n}\n\n/**\n * NestJS dynamic module for registering type-safe Kafka clients.\n * Use `register()` for static config or `registerAsync()` for DI-based config.\n */\n@Module({})\nexport class KafkaModule {\n /** Register a Kafka client with static options. */\n static register<T extends TopicMapConstraint<T>>(\n options: KafkaModuleOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(options.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: async (): Promise<KafkaClient<T>> => {\n const client = new KafkaClient<T>(\n options.clientId,\n options.groupId,\n options.brokers,\n { autoCreateTopics: options.autoCreateTopics },\n );\n await client.connectProducer();\n return client;\n },\n };\n\n const destroyProvider: Provider = {\n provide: `${token}_DESTROY`,\n useFactory: (client: KafkaClient<T>) => ({\n onModuleDestroy: () => client.disconnect(),\n }),\n inject: [token],\n };\n\n return {\n global: options.isGlobal ?? false,\n module: KafkaModule,\n imports: [DiscoveryModule],\n providers: [kafkaClientProvider, destroyProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n\n /** Register a Kafka client with async/factory-based options. */\n static registerAsync<T extends TopicMapConstraint<T>>(\n asyncOptions: KafkaModuleAsyncOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(asyncOptions.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: async (...args: any[]): Promise<KafkaClient<T>> => {\n const options = await asyncOptions.useFactory(...args);\n const client = new KafkaClient<T>(\n options.clientId,\n options.groupId,\n options.brokers,\n { autoCreateTopics: options.autoCreateTopics },\n );\n await client.connectProducer();\n return client;\n },\n inject: asyncOptions.inject || [],\n };\n\n const destroyProvider: Provider = {\n provide: `${token}_DESTROY`,\n useFactory: (client: KafkaClient<T>) => ({\n onModuleDestroy: () => client.disconnect(),\n }),\n inject: [token],\n };\n\n return {\n global: asyncOptions.isGlobal ?? false,\n module: KafkaModule,\n imports: [...(asyncOptions.imports || []), DiscoveryModule],\n providers: [kafkaClientProvider, destroyProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n}\n","import { Consumer, Kafka, Partitioners, Producer, Admin } from \"kafkajs\";\nimport { Logger } from \"@nestjs/common\";\nimport { TopicDescriptor, SchemaLike } from \"./topic\";\nimport { KafkaRetryExhaustedError, KafkaValidationError } from \"./errors\";\nimport type {\n ClientId,\n GroupId,\n SendOptions,\n MessageHeaders,\n ConsumerOptions,\n ConsumerInterceptor,\n RetryOptions,\n TransactionContext,\n TopicMapConstraint,\n IKafkaClient,\n KafkaClientOptions,\n BatchMeta,\n SubscribeRetryOptions,\n} from \"./types\";\n\n// Re-export all types so existing `import { ... } from './kafka.client'` keeps working\nexport * from \"./types\";\n\n// ── Helpers ──────────────────────────────────────────────────────────\n\nconst ACKS_ALL = -1 as const;\n\nfunction toError(error: unknown): Error {\n return error instanceof Error ? error : new Error(String(error));\n}\n\n// ─────────────────────────────────────────────────────────────────────\n\n/**\n * Type-safe Kafka client for NestJS.\n * Wraps kafkajs with JSON serialization, retries, DLQ, transactions, and interceptors.\n *\n * @typeParam T - Topic-to-message type mapping for compile-time safety.\n */\nexport class KafkaClient<\n T extends TopicMapConstraint<T>,\n> implements IKafkaClient<T> {\n private readonly kafka: Kafka;\n private readonly producer: Producer;\n private readonly consumers = new Map<string, Consumer>();\n private readonly admin: Admin;\n private readonly logger: Logger;\n private readonly autoCreateTopicsEnabled: boolean;\n private readonly strictSchemasEnabled: boolean;\n private readonly ensuredTopics = new Set<string>();\n private readonly defaultGroupId: string;\n private readonly schemaRegistry = new Map<string, SchemaLike>();\n private readonly runningConsumers = new Map<string, \"eachMessage\" | \"eachBatch\">();\n\n private isAdminConnected = false;\n public readonly clientId: ClientId;\n\n constructor(\n clientId: ClientId,\n groupId: GroupId,\n brokers: string[],\n options?: KafkaClientOptions,\n ) {\n this.clientId = clientId;\n this.defaultGroupId = groupId;\n this.logger = new Logger(`KafkaClient:${clientId}`);\n this.autoCreateTopicsEnabled = options?.autoCreateTopics ?? false;\n this.strictSchemasEnabled = options?.strictSchemas ?? true;\n\n this.kafka = new Kafka({\n clientId: this.clientId,\n brokers,\n });\n this.producer = this.kafka.producer({\n createPartitioner: Partitioners.DefaultPartitioner,\n idempotent: true,\n transactionalId: `${clientId}-tx`,\n maxInFlightRequests: 1,\n });\n this.admin = this.kafka.admin();\n }\n\n // ── Send ─────────────────────────────────────────────────────────\n\n /** Send a single typed message. Accepts a topic key or a TopicDescriptor. */\n public async sendMessage<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(descriptor: D, message: D[\"__type\"], options?: SendOptions): Promise<void>;\n public async sendMessage<K extends keyof T>(\n topic: K,\n message: T[K],\n options?: SendOptions,\n ): Promise<void>;\n public async sendMessage(\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ): Promise<void> {\n const payload = this.buildSendPayload(topicOrDesc, [\n { value: message, key: options.key, headers: options.headers },\n ]);\n await this.ensureTopic(payload.topic);\n await this.producer.send(payload);\n }\n\n /** Send multiple typed messages in one call. Accepts a topic key or a TopicDescriptor. */\n public async sendBatch<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n descriptor: D,\n messages: Array<{\n value: D[\"__type\"];\n key?: string;\n headers?: MessageHeaders;\n }>,\n ): Promise<void>;\n public async sendBatch<K extends keyof T>(\n topic: K,\n messages: Array<{ value: T[K]; key?: string; headers?: MessageHeaders }>,\n ): Promise<void>;\n public async sendBatch(\n topicOrDesc: any,\n messages: Array<{ value: any; key?: string; headers?: MessageHeaders }>,\n ): Promise<void> {\n const payload = this.buildSendPayload(topicOrDesc, messages);\n await this.ensureTopic(payload.topic);\n await this.producer.send(payload);\n }\n\n /** Execute multiple sends atomically. Commits on success, aborts on error. */\n public async transaction(\n fn: (ctx: TransactionContext<T>) => Promise<void>,\n ): Promise<void> {\n const tx = await this.producer.transaction();\n try {\n const ctx: TransactionContext<T> = {\n send: async (\n topicOrDesc: any,\n message: any,\n options: SendOptions = {},\n ) => {\n const payload = this.buildSendPayload(topicOrDesc, [\n { value: message, key: options.key, headers: options.headers },\n ]);\n await this.ensureTopic(payload.topic);\n await tx.send(payload);\n },\n sendBatch: async (topicOrDesc: any, messages: any[]) => {\n const payload = this.buildSendPayload(topicOrDesc, messages);\n await this.ensureTopic(payload.topic);\n await tx.send(payload);\n },\n };\n await fn(ctx);\n await tx.commit();\n } catch (error) {\n await tx.abort();\n throw error;\n }\n }\n\n // ── Producer lifecycle ───────────────────────────────────────────\n\n /** Connect the idempotent producer. Called automatically by `KafkaModule.register()`. */\n public async connectProducer(): Promise<void> {\n await this.producer.connect();\n this.logger.log(\"Producer connected\");\n }\n\n public async disconnectProducer(): Promise<void> {\n await this.producer.disconnect();\n this.logger.log(\"Producer disconnected\");\n }\n\n // ── Consumer: eachMessage ────────────────────────────────────────\n\n /** Subscribe to topics and start consuming messages with the given handler. */\n public async startConsumer<K extends Array<keyof T>>(\n topics: K,\n handleMessage: (message: T[K[number]], topic: K[number]) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<void>;\n public async startConsumer<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n topics: D[],\n handleMessage: (message: D[\"__type\"], topic: D[\"__topic\"]) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<void>;\n public async startConsumer(\n topics: any[],\n handleMessage: (message: any, topic: any) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<void> {\n const { consumer, schemaMap, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachMessage\", options);\n\n await consumer.run({\n autoCommit: options.autoCommit ?? true,\n eachMessage: async ({ topic, message }) => {\n if (!message.value) {\n this.logger.warn(`Received empty message from topic ${topic}`);\n return;\n }\n\n const raw = message.value.toString();\n const parsed = this.parseJsonMessage(raw, topic);\n if (parsed === null) return;\n\n const validated = await this.validateWithSchema(\n parsed, raw, topic, schemaMap, interceptors, dlq,\n );\n if (validated === null) return;\n\n await this.executeWithRetry(\n () => handleMessage(validated, topic as any),\n { topic, messages: validated, rawMessages: [raw], interceptors, dlq, retry },\n );\n },\n });\n\n this.runningConsumers.set(gid, \"eachMessage\");\n }\n\n // ── Consumer: eachBatch ──────────────────────────────────────────\n\n /** Subscribe to topics and consume messages in batches. */\n public async startBatchConsumer<K extends Array<keyof T>>(\n topics: K,\n handleBatch: (\n messages: T[K[number]][],\n topic: K[number],\n meta: BatchMeta,\n ) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<void>;\n public async startBatchConsumer<\n D extends TopicDescriptor<string & keyof T, T[string & keyof T]>,\n >(\n topics: D[],\n handleBatch: (\n messages: D[\"__type\"][],\n topic: D[\"__topic\"],\n meta: BatchMeta,\n ) => Promise<void>,\n options?: ConsumerOptions<T>,\n ): Promise<void>;\n public async startBatchConsumer(\n topics: any[],\n handleBatch: (\n messages: any[],\n topic: any,\n meta: BatchMeta,\n ) => Promise<void>,\n options: ConsumerOptions<T> = {},\n ): Promise<void> {\n const { consumer, schemaMap, gid, dlq, interceptors, retry } =\n await this.setupConsumer(topics, \"eachBatch\", options);\n\n await consumer.run({\n autoCommit: options.autoCommit ?? true,\n eachBatch: async ({\n batch,\n heartbeat,\n resolveOffset,\n commitOffsetsIfNecessary,\n }) => {\n const validMessages: any[] = [];\n const rawMessages: string[] = [];\n\n for (const message of batch.messages) {\n if (!message.value) {\n this.logger.warn(\n `Received empty message from topic ${batch.topic}`,\n );\n continue;\n }\n\n const raw = message.value.toString();\n const parsed = this.parseJsonMessage(raw, batch.topic);\n if (parsed === null) continue;\n\n const validated = await this.validateWithSchema(\n parsed, raw, batch.topic, schemaMap, interceptors, dlq,\n );\n if (validated === null) continue;\n\n validMessages.push(validated);\n rawMessages.push(raw);\n }\n\n if (validMessages.length === 0) return;\n\n const meta: BatchMeta = {\n partition: batch.partition,\n highWatermark: batch.highWatermark,\n heartbeat,\n resolveOffset,\n commitOffsetsIfNecessary,\n };\n\n await this.executeWithRetry(\n () => handleBatch(validMessages, batch.topic as any, meta),\n {\n topic: batch.topic,\n messages: validMessages,\n rawMessages: batch.messages\n .filter((m) => m.value)\n .map((m) => m.value!.toString()),\n interceptors,\n dlq,\n retry,\n isBatch: true,\n },\n );\n },\n });\n\n this.runningConsumers.set(gid, \"eachBatch\");\n }\n\n // ── Consumer lifecycle ───────────────────────────────────────────\n\n public async stopConsumer(): Promise<void> {\n const tasks = [];\n for (const consumer of this.consumers.values()) {\n tasks.push(consumer.disconnect());\n }\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.logger.log(\"All consumers disconnected\");\n }\n\n /** Check broker connectivity and return available topics. */\n public async checkStatus(): Promise<{ topics: string[] }> {\n if (!this.isAdminConnected) {\n await this.admin.connect();\n this.isAdminConnected = true;\n }\n const topics = await this.admin.listTopics();\n return { topics };\n }\n\n public getClientId(): ClientId {\n return this.clientId;\n }\n\n /** Gracefully disconnect producer, all consumers, and admin. */\n public async disconnect(): Promise<void> {\n const tasks: Promise<void>[] = [this.producer.disconnect()];\n for (const consumer of this.consumers.values()) {\n tasks.push(consumer.disconnect());\n }\n if (this.isAdminConnected) {\n tasks.push(this.admin.disconnect());\n this.isAdminConnected = false;\n }\n await Promise.allSettled(tasks);\n this.consumers.clear();\n this.runningConsumers.clear();\n this.logger.log(\"All connections closed\");\n }\n\n // ── Private helpers ──────────────────────────────────────────────\n\n private getOrCreateConsumer(groupId?: string): Consumer {\n const gid = groupId || this.defaultGroupId;\n if (!this.consumers.has(gid)) {\n this.consumers.set(gid, this.kafka.consumer({ groupId: gid }));\n }\n return this.consumers.get(gid)!;\n }\n\n private resolveTopicName(topicOrDescriptor: unknown): string {\n if (typeof topicOrDescriptor === \"string\") return topicOrDescriptor;\n if (\n topicOrDescriptor &&\n typeof topicOrDescriptor === \"object\" &&\n \"__topic\" in topicOrDescriptor\n ) {\n return (topicOrDescriptor as TopicDescriptor).__topic;\n }\n return String(topicOrDescriptor);\n }\n\n private async ensureTopic(topic: string): Promise<void> {\n if (!this.autoCreateTopicsEnabled || this.ensuredTopics.has(topic)) return;\n if (!this.isAdminConnected) {\n await this.admin.connect();\n this.isAdminConnected = true;\n }\n await this.admin.createTopics({\n topics: [{ topic, numPartitions: 1 }],\n });\n this.ensuredTopics.add(topic);\n }\n\n /** Register schema from descriptor into global registry (side-effect). */\n private registerSchema(topicOrDesc: any): void {\n if (topicOrDesc?.__schema) {\n const topic = this.resolveTopicName(topicOrDesc);\n this.schemaRegistry.set(topic, topicOrDesc.__schema);\n }\n }\n\n /** Validate message against schema. Pure — no side-effects on registry. */\n private validateMessage(topicOrDesc: any, message: any): any {\n if (topicOrDesc?.__schema) {\n return topicOrDesc.__schema.parse(message);\n }\n if (this.strictSchemasEnabled && typeof topicOrDesc === \"string\") {\n const schema = this.schemaRegistry.get(topicOrDesc);\n if (schema) return schema.parse(message);\n }\n return message;\n }\n\n /**\n * Build a kafkajs-ready send payload.\n * Handles: topic resolution, schema registration, validation, JSON serialization.\n */\n private buildSendPayload(\n topicOrDesc: any,\n messages: Array<{ value: any; key?: string; headers?: MessageHeaders }>,\n ): { topic: string; messages: Array<{ value: string; key: string | null; headers?: MessageHeaders }>; acks: -1 } {\n this.registerSchema(topicOrDesc);\n const topic = this.resolveTopicName(topicOrDesc);\n return {\n topic,\n messages: messages.map((m) => ({\n value: JSON.stringify(this.validateMessage(topicOrDesc, m.value)),\n key: m.key ?? null,\n headers: m.headers,\n })),\n acks: ACKS_ALL,\n };\n }\n\n /** Shared consumer setup: groupId check, schema map, connect, subscribe. */\n private async setupConsumer(\n topics: any[],\n mode: \"eachMessage\" | \"eachBatch\",\n options: ConsumerOptions<T>,\n ) {\n const {\n groupId: optGroupId,\n fromBeginning = false,\n retry,\n dlq = false,\n interceptors = [],\n schemas: optionSchemas,\n } = options;\n\n const gid = optGroupId || this.defaultGroupId;\n const existingMode = this.runningConsumers.get(gid);\n const oppositeMode = mode === \"eachMessage\" ? \"eachBatch\" : \"eachMessage\";\n if (existingMode === oppositeMode) {\n throw new Error(\n `Cannot use ${mode} on consumer group \"${gid}\" — it is already running with ${oppositeMode}. ` +\n `Use a different groupId for this consumer.`,\n );\n }\n\n const consumer = this.getOrCreateConsumer(optGroupId);\n const schemaMap = this.buildSchemaMap(topics, optionSchemas);\n\n const topicNames = (topics as any[]).map((t: any) =>\n this.resolveTopicName(t),\n );\n\n await consumer.connect();\n await this.subscribeWithRetry(consumer, topicNames, fromBeginning, options.subscribeRetry);\n\n this.logger.log(\n `${mode === \"eachBatch\" ? \"Batch consumer\" : \"Consumer\"} subscribed to topics: ${topicNames.join(\", \")}`,\n );\n\n return { consumer, schemaMap, topicNames, gid, dlq, interceptors, retry };\n }\n\n private buildSchemaMap(\n topics: any[],\n optionSchemas?: Map<string, SchemaLike>,\n ): Map<string, SchemaLike> {\n const schemaMap = new Map<string, SchemaLike>();\n for (const t of topics) {\n if (t?.__schema) {\n const name = this.resolveTopicName(t);\n schemaMap.set(name, t.__schema);\n this.schemaRegistry.set(name, t.__schema);\n }\n }\n if (optionSchemas) {\n for (const [k, v] of optionSchemas) {\n schemaMap.set(k, v);\n this.schemaRegistry.set(k, v);\n }\n }\n return schemaMap;\n }\n\n /** Parse raw message as JSON. Returns null on failure (logs error). */\n private parseJsonMessage(raw: string, topic: string): any | null {\n try {\n return JSON.parse(raw);\n } catch (error) {\n this.logger.error(\n `Failed to parse message from topic ${topic}:`,\n toError(error).stack,\n );\n return null;\n }\n }\n\n /**\n * Validate a parsed message against the schema map.\n * On failure: logs error, sends to DLQ if enabled, calls interceptor.onError.\n * Returns validated message or null.\n */\n private async validateWithSchema(\n message: any,\n raw: string,\n topic: string,\n schemaMap: Map<string, SchemaLike>,\n interceptors: ConsumerInterceptor<T>[],\n dlq: boolean,\n ): Promise<any | null> {\n const schema = schemaMap.get(topic);\n if (!schema) return message;\n\n try {\n return schema.parse(message);\n } catch (error) {\n const err = toError(error);\n const validationError = new KafkaValidationError(topic, message, {\n cause: err,\n });\n this.logger.error(\n `Schema validation failed for topic ${topic}:`,\n err.message,\n );\n if (dlq) await this.sendToDlq(topic, raw);\n for (const interceptor of interceptors) {\n await interceptor.onError?.(message, topic, validationError);\n }\n return null;\n }\n }\n\n /**\n * Execute a handler with retry, interceptors, and DLQ support.\n * Used by both single-message and batch consumers.\n */\n private async executeWithRetry(\n fn: () => Promise<void>,\n ctx: {\n topic: string;\n messages: any;\n rawMessages: string[];\n interceptors: ConsumerInterceptor<T>[];\n dlq: boolean;\n retry?: RetryOptions;\n isBatch?: boolean;\n },\n ): Promise<void> {\n const { topic, messages, rawMessages, interceptors, dlq, retry, isBatch } = ctx;\n const maxAttempts = retry ? retry.maxRetries + 1 : 1;\n const backoffMs = retry?.backoffMs ?? 1000;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n if (isBatch) {\n for (const interceptor of interceptors) {\n for (const msg of messages) {\n await interceptor.before?.(msg, topic);\n }\n }\n } else {\n for (const interceptor of interceptors) {\n await interceptor.before?.(messages, topic);\n }\n }\n\n await fn();\n\n if (isBatch) {\n for (const interceptor of interceptors) {\n for (const msg of messages) {\n await interceptor.after?.(msg, topic);\n }\n }\n } else {\n for (const interceptor of interceptors) {\n await interceptor.after?.(messages, topic);\n }\n }\n return;\n } catch (error) {\n const err = toError(error);\n const isLastAttempt = attempt === maxAttempts;\n\n if (isLastAttempt && maxAttempts > 1) {\n const exhaustedError = new KafkaRetryExhaustedError(\n topic,\n messages,\n maxAttempts,\n { cause: err },\n );\n for (const interceptor of interceptors) {\n await interceptor.onError?.(messages, topic, exhaustedError);\n }\n } else {\n for (const interceptor of interceptors) {\n await interceptor.onError?.(messages, topic, err);\n }\n }\n\n this.logger.error(\n `Error processing ${isBatch ? \"batch\" : \"message\"} from topic ${topic} (attempt ${attempt}/${maxAttempts}):`,\n err.stack,\n );\n\n if (isLastAttempt) {\n if (dlq) {\n for (const raw of rawMessages) {\n await this.sendToDlq(topic, raw);\n }\n }\n } else {\n await this.sleep(backoffMs * attempt);\n }\n }\n }\n }\n\n private async sendToDlq(topic: string, rawMessage: string): Promise<void> {\n const dlqTopic = `${topic}.dlq`;\n try {\n await this.producer.send({\n topic: dlqTopic,\n messages: [{ value: rawMessage }],\n acks: ACKS_ALL,\n });\n this.logger.warn(`Message sent to DLQ: ${dlqTopic}`);\n } catch (error) {\n this.logger.error(\n `Failed to send message to DLQ ${dlqTopic}:`,\n toError(error).stack,\n );\n }\n }\n\n private async subscribeWithRetry(\n consumer: Consumer,\n topics: string[],\n fromBeginning: boolean,\n retryOpts?: SubscribeRetryOptions,\n ): Promise<void> {\n const maxAttempts = retryOpts?.retries ?? 5;\n const backoffMs = retryOpts?.backoffMs ?? 5000;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n await consumer.subscribe({ topics, fromBeginning });\n return;\n } catch (error) {\n if (attempt === maxAttempts) throw error;\n const msg = toError(error).message;\n this.logger.warn(\n `Failed to subscribe to [${topics.join(\", \")}] (attempt ${attempt}/${maxAttempts}): ${msg}. Retrying in ${backoffMs}ms...`,\n );\n await this.sleep(backoffMs);\n }\n }\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n","/** Error thrown when a consumer message handler fails. */\nexport class KafkaProcessingError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n message: string,\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(message, options);\n this.name = \"KafkaProcessingError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when schema validation fails on send or consume. */\nexport class KafkaValidationError extends Error {\n declare readonly cause?: Error;\n\n constructor(\n public readonly topic: string,\n public readonly originalMessage: unknown,\n options?: { cause?: Error },\n ) {\n super(`Schema validation failed for topic \"${topic}\"`, options);\n this.name = \"KafkaValidationError\";\n if (options?.cause) this.cause = options.cause;\n }\n}\n\n/** Error thrown when all retry attempts are exhausted for a message. */\nexport class KafkaRetryExhaustedError extends KafkaProcessingError {\n constructor(\n topic: string,\n originalMessage: unknown,\n public readonly attempts: number,\n options?: { cause?: Error },\n ) {\n super(\n `Message processing failed after ${attempts} attempts on topic \"${topic}\"`,\n topic,\n originalMessage,\n options,\n );\n this.name = \"KafkaRetryExhaustedError\";\n }\n}\n","/** Default DI token for the Kafka client. */\nexport const KAFKA_CLIENT = \"KAFKA_CLIENT\";\n\n/** Returns the DI token for a named (or default) Kafka client instance. */\nexport const getKafkaClientToken = (name?: string): string =>\n name ? `KAFKA_CLIENT_${name}` : KAFKA_CLIENT;\n","import { Inject, Injectable, OnModuleInit, Logger } from \"@nestjs/common\";\nimport { DiscoveryService, ModuleRef } from \"@nestjs/core\";\nimport { KafkaClient } from \"../client/kafka.client\";\nimport {\n KAFKA_SUBSCRIBER_METADATA,\n KafkaSubscriberMetadata,\n} from \"../decorators/kafka.decorator\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\n\ninterface SubscriberEntry extends KafkaSubscriberMetadata {\n methodName: string | symbol;\n}\n\n/** Discovers `@SubscribeTo()` decorators and wires them to their Kafka clients on startup. */\n@Injectable()\nexport class KafkaExplorer implements OnModuleInit {\n private readonly logger = new Logger(KafkaExplorer.name);\n\n constructor(\n @Inject(DiscoveryService)\n private readonly discoveryService: DiscoveryService,\n @Inject(ModuleRef)\n private readonly moduleRef: ModuleRef,\n ) {}\n\n async onModuleInit() {\n const providers = this.discoveryService.getProviders();\n\n for (const wrapper of providers) {\n const { instance } = wrapper;\n if (!instance || typeof instance !== \"object\") continue;\n\n const metadata: SubscriberEntry[] | undefined = Reflect.getMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n instance.constructor,\n );\n\n if (!metadata || metadata.length === 0) continue;\n\n for (const entry of metadata) {\n const token = getKafkaClientToken(entry.clientName);\n let client: KafkaClient<any>;\n\n try {\n client = this.moduleRef.get(token, { strict: false });\n } catch {\n this.logger.error(\n `KafkaClient \"${entry.clientName || \"default\"}\" not found for @SubscribeTo on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n continue;\n }\n\n const handler = (instance as any)[entry.methodName].bind(instance);\n\n const consumerOptions = { ...entry.options };\n if (entry.schemas) {\n consumerOptions.schemas = entry.schemas;\n }\n\n if (entry.batch) {\n await client.startBatchConsumer(\n entry.topics as any,\n async (messages: any[], topic: any, meta: any) => {\n await handler(messages, topic, meta);\n },\n consumerOptions,\n );\n } else {\n await client.startConsumer(\n entry.topics as any,\n async (message: any, topic: any) => {\n await handler(message, topic);\n },\n consumerOptions,\n );\n }\n\n this.logger.log(\n `Registered @SubscribeTo(${entry.topics.join(\", \")})${entry.batch ? \" [batch]\" : \"\"} on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n }\n }\n }\n}\n","import { Inject } from \"@nestjs/common\";\nimport { getKafkaClientToken } from \"../module/kafka.constants\";\nimport { ConsumerOptions } from \"../client/kafka.client\";\nimport { TopicDescriptor, SchemaLike } from \"../client/topic\";\n\nexport const KAFKA_SUBSCRIBER_METADATA = \"KAFKA_SUBSCRIBER_METADATA\";\n\nexport interface KafkaSubscriberMetadata {\n topics: string[];\n schemas?: Map<string, SchemaLike>;\n options?: ConsumerOptions;\n clientName?: string;\n batch?: boolean;\n methodName?: string | symbol;\n}\n\n/** Inject a `KafkaClient` instance. Pass a name to target a specific named client. */\nexport const InjectKafkaClient = (name?: string): ParameterDecorator =>\n Inject(getKafkaClientToken(name));\n\n/**\n * Decorator that auto-subscribes a method to Kafka topics on module init.\n * The decorated method receives `(message, topic)` for each consumed message.\n */\nexport const SubscribeTo = (\n topics:\n | string\n | string[]\n | TopicDescriptor\n | TopicDescriptor[]\n | (string | TopicDescriptor)[],\n options?: ConsumerOptions & { clientName?: string; batch?: boolean },\n): MethodDecorator => {\n const arr = Array.isArray(topics) ? topics : [topics];\n const topicsArray = arr.map((t) => (typeof t === \"string\" ? t : t.__topic));\n\n // Extract schemas from descriptors that have them\n const schemas = new Map<string, SchemaLike>();\n for (const t of arr) {\n if (typeof t !== \"string\" && t.__schema) {\n schemas.set(t.__topic, t.__schema);\n }\n }\n\n const { clientName, batch, ...consumerOptions } = options || {};\n\n return (target, propertyKey, _descriptor) => {\n const existing: KafkaSubscriberMetadata[] =\n Reflect.getMetadata(KAFKA_SUBSCRIBER_METADATA, target.constructor) || [];\n\n Reflect.defineMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n [\n ...existing,\n {\n topics: topicsArray,\n schemas: schemas.size > 0 ? schemas : undefined,\n options: Object.keys(consumerOptions).length\n ? consumerOptions\n : undefined,\n clientName,\n batch,\n methodName: propertyKey,\n },\n ],\n target.constructor,\n );\n };\n};\n","/**\n * Any validation library with a `.parse()` method.\n * Works with Zod, Valibot, ArkType, or any custom validator.\n *\n * @example\n * ```ts\n * import { z } from 'zod';\n * const schema: SchemaLike<{ id: string }> = z.object({ id: z.string() });\n * ```\n */\nexport interface SchemaLike<T = any> {\n parse(data: unknown): T;\n}\n\n/** Infer the output type from a SchemaLike. */\nexport type InferSchema<S extends SchemaLike> =\n S extends SchemaLike<infer T> ? T : never;\n\n/**\n * A typed topic descriptor that pairs a topic name with its message type.\n * Created via the `topic()` factory function.\n *\n * @typeParam N - The literal topic name string.\n * @typeParam M - The message payload type for this topic.\n */\nexport interface TopicDescriptor<\n N extends string = string,\n M extends Record<string, any> = Record<string, any>,\n> {\n readonly __topic: N;\n /** @internal Phantom type — never has a real value at runtime. */\n readonly __type: M;\n /** Runtime schema validator. Present only when created via `topic().schema()`. */\n readonly __schema?: SchemaLike<M>;\n}\n\n/**\n * Define a typed topic descriptor.\n *\n * @example\n * ```ts\n * // Without schema — type provided explicitly:\n * const OrderCreated = topic('order.created')<{ orderId: string; amount: number }>();\n *\n * // With schema — type inferred from schema:\n * const OrderCreated = topic('order.created').schema(z.object({\n * orderId: z.string(),\n * amount: z.number(),\n * }));\n *\n * // Use with KafkaClient:\n * await kafka.sendMessage(OrderCreated, { orderId: '123', amount: 100 });\n *\n * // Use with @SubscribeTo:\n * @SubscribeTo(OrderCreated)\n * async handleOrder(msg) { ... }\n * ```\n */\nexport function topic<N extends string>(name: N) {\n const fn = <M extends Record<string, any>>(): TopicDescriptor<N, M> => ({\n __topic: name,\n __type: undefined as unknown as M,\n });\n\n fn.schema = <S extends SchemaLike<Record<string, any>>>(\n schema: S,\n ): TopicDescriptor<N, InferSchema<S>> => ({\n __topic: name,\n __type: undefined as unknown as InferSchema<S>,\n __schema: schema as unknown as SchemaLike<InferSchema<S>>,\n });\n\n return fn;\n}\n\n/**\n * Build a topic-message map type from a union of TopicDescriptors.\n *\n * @example\n * ```ts\n * const OrderCreated = topic('order.created')<{ orderId: string }>();\n * const OrderCompleted = topic('order.completed')<{ completedAt: string }>();\n *\n * type MyTopics = TopicsFrom<typeof OrderCreated | typeof OrderCompleted>;\n * // { 'order.created': { orderId: string }; 'order.completed': { completedAt: string } }\n * ```\n */\nexport type TopicsFrom<D extends TopicDescriptor<any, any>> = {\n [K in D as K[\"__topic\"]]: K[\"__type\"];\n};\n","import { Injectable } from \"@nestjs/common\";\nimport { KafkaClient, TopicMapConstraint } from \"../client/kafka.client\";\n\n/** Result returned by `KafkaHealthIndicator.check()`. */\nexport interface KafkaHealthResult {\n status: \"up\" | \"down\";\n clientId: string;\n topics?: string[];\n error?: string;\n}\n\n/** Health check service. Call `check(client)` to verify broker connectivity. */\n@Injectable()\nexport class KafkaHealthIndicator {\n async check<T extends TopicMapConstraint<T>>(\n client: KafkaClient<T>,\n ): Promise<KafkaHealthResult> {\n try {\n const { topics } = await client.checkStatus();\n return {\n status: \"up\",\n clientId: client.clientId,\n topics,\n };\n } catch (error) {\n return {\n status: \"down\",\n clientId: client.clientId,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n}\n"],"mappings":"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","names":["topic","topic","Inject","Logger","Logger","topic","Inject","Injectable","Injectable"]}
|
|
1
|
+
{"version":3,"sources":["../src/nest/kafka.module.ts","../src/nest/kafka.constants.ts","../src/nest/kafka.explorer.ts","../src/nest/kafka.decorator.ts","../src/nest/kafka.health.ts"],"sourcesContent":["import { Module, DynamicModule, Provider, Logger } from \"@nestjs/common\";\nimport { DiscoveryModule } from \"@nestjs/core\";\nimport {\n KafkaClient,\n ClientId,\n GroupId,\n TopicMapConstraint,\n} from \"../client/kafka.client\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { KafkaExplorer } from \"./kafka.explorer\";\n\n/** Synchronous configuration for `KafkaModule.register()`. */\nexport interface KafkaModuleOptions {\n /** Optional name for multi-client setups. Must match `@InjectKafkaClient(name)`. */\n name?: string;\n /** Unique Kafka client identifier. */\n clientId: ClientId;\n /** Consumer group identifier. */\n groupId: GroupId;\n /** List of Kafka broker addresses. */\n brokers: string[];\n /** If true, makes KAFKA_CLIENT available globally without importing KafkaModule in every feature module. */\n isGlobal?: boolean;\n /** Auto-create topics via admin on first use (send/consume). Useful for development. */\n autoCreateTopics?: boolean;\n}\n\n/** Async configuration for `KafkaModule.registerAsync()` with dependency injection. */\nexport interface KafkaModuleAsyncOptions {\n name?: string;\n /** If true, makes KAFKA_CLIENT available globally without importing KafkaModule in every feature module. */\n isGlobal?: boolean;\n /** Auto-create topics via admin on first use (send/consume). Useful for development. */\n autoCreateTopics?: boolean;\n imports?: any[];\n useFactory: (\n ...args: any[]\n ) => KafkaModuleOptions | Promise<KafkaModuleOptions>;\n inject?: any[];\n}\n\n/**\n * NestJS dynamic module for registering type-safe Kafka clients.\n * Use `register()` for static config or `registerAsync()` for DI-based config.\n */\n@Module({})\nexport class KafkaModule {\n /** Register a Kafka client with static options. */\n static register<T extends TopicMapConstraint<T>>(\n options: KafkaModuleOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(options.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: async (): Promise<KafkaClient<T>> => {\n const client = new KafkaClient<T>(\n options.clientId,\n options.groupId,\n options.brokers,\n {\n autoCreateTopics: options.autoCreateTopics,\n logger: new Logger(`KafkaClient:${options.clientId}`),\n },\n );\n await client.connectProducer();\n return client;\n },\n };\n\n const destroyProvider: Provider = {\n provide: `${token}_DESTROY`,\n useFactory: (client: KafkaClient<T>) => ({\n onModuleDestroy: () => client.disconnect(),\n }),\n inject: [token],\n };\n\n return {\n global: options.isGlobal ?? false,\n module: KafkaModule,\n imports: [DiscoveryModule],\n providers: [kafkaClientProvider, destroyProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n\n /** Register a Kafka client with async/factory-based options. */\n static registerAsync<T extends TopicMapConstraint<T>>(\n asyncOptions: KafkaModuleAsyncOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(asyncOptions.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: async (...args: any[]): Promise<KafkaClient<T>> => {\n const options = await asyncOptions.useFactory(...args);\n const client = new KafkaClient<T>(\n options.clientId,\n options.groupId,\n options.brokers,\n {\n autoCreateTopics: options.autoCreateTopics,\n logger: new Logger(`KafkaClient:${options.clientId}`),\n },\n );\n await client.connectProducer();\n return client;\n },\n inject: asyncOptions.inject || [],\n };\n\n const destroyProvider: Provider = {\n provide: `${token}_DESTROY`,\n useFactory: (client: KafkaClient<T>) => ({\n onModuleDestroy: () => client.disconnect(),\n }),\n inject: [token],\n };\n\n return {\n global: asyncOptions.isGlobal ?? false,\n module: KafkaModule,\n imports: [...(asyncOptions.imports || []), DiscoveryModule],\n providers: [kafkaClientProvider, destroyProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n}\n","/** Default DI token for the Kafka client. */\nexport const KAFKA_CLIENT = \"KAFKA_CLIENT\";\n\n/** Returns the DI token for a named (or default) Kafka client instance. */\nexport const getKafkaClientToken = (name?: string): string =>\n name ? `KAFKA_CLIENT_${name}` : KAFKA_CLIENT;\n","import { Inject, Injectable, OnModuleInit, Logger } from \"@nestjs/common\";\nimport { DiscoveryService, ModuleRef } from \"@nestjs/core\";\nimport { KafkaClient } from \"../client/kafka.client\";\nimport {\n KAFKA_SUBSCRIBER_METADATA,\n KafkaSubscriberMetadata,\n} from \"./kafka.decorator\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\n\ninterface SubscriberEntry extends KafkaSubscriberMetadata {\n methodName: string | symbol;\n}\n\n/** Discovers `@SubscribeTo()` decorators and wires them to their Kafka clients on startup. */\n@Injectable()\nexport class KafkaExplorer implements OnModuleInit {\n private readonly logger = new Logger(KafkaExplorer.name);\n\n constructor(\n @Inject(DiscoveryService)\n private readonly discoveryService: DiscoveryService,\n @Inject(ModuleRef)\n private readonly moduleRef: ModuleRef,\n ) {}\n\n async onModuleInit() {\n const providers = this.discoveryService.getProviders();\n\n for (const wrapper of providers) {\n const { instance } = wrapper;\n if (!instance || typeof instance !== \"object\") continue;\n\n const metadata: SubscriberEntry[] | undefined = Reflect.getMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n instance.constructor,\n );\n\n if (!metadata || metadata.length === 0) continue;\n\n for (const entry of metadata) {\n const token = getKafkaClientToken(entry.clientName);\n let client: KafkaClient<any>;\n\n try {\n client = this.moduleRef.get(token, { strict: false });\n } catch {\n this.logger.error(\n `KafkaClient \"${entry.clientName || \"default\"}\" not found for @SubscribeTo on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n continue;\n }\n\n const handler = (instance as any)[entry.methodName].bind(instance);\n\n const consumerOptions = { ...entry.options };\n if (entry.schemas) {\n consumerOptions.schemas = entry.schemas;\n }\n\n if (entry.batch) {\n await client.startBatchConsumer(\n entry.topics as any,\n async (messages: any[], topic: any, meta: any) => {\n await handler(messages, topic, meta);\n },\n consumerOptions,\n );\n } else {\n await client.startConsumer(\n entry.topics as any,\n async (message: any, topic: any) => {\n await handler(message, topic);\n },\n consumerOptions,\n );\n }\n\n this.logger.log(\n `Registered @SubscribeTo(${entry.topics.join(\", \")})${entry.batch ? \" [batch]\" : \"\"} on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n }\n }\n }\n}\n","import { Inject } from \"@nestjs/common\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { ConsumerOptions } from \"../client/kafka.client\";\nimport { TopicDescriptor, SchemaLike } from \"../client/topic\";\n\nexport const KAFKA_SUBSCRIBER_METADATA = \"KAFKA_SUBSCRIBER_METADATA\";\n\nexport interface KafkaSubscriberMetadata {\n topics: string[];\n schemas?: Map<string, SchemaLike>;\n options?: ConsumerOptions;\n clientName?: string;\n batch?: boolean;\n methodName?: string | symbol;\n}\n\n/** Inject a `KafkaClient` instance. Pass a name to target a specific named client. */\nexport const InjectKafkaClient = (name?: string): ParameterDecorator =>\n Inject(getKafkaClientToken(name));\n\n/**\n * Decorator that auto-subscribes a method to Kafka topics on module init.\n * The decorated method receives `(message, topic)` for each consumed message.\n */\nexport const SubscribeTo = (\n topics:\n | string\n | string[]\n | TopicDescriptor\n | TopicDescriptor[]\n | (string | TopicDescriptor)[],\n options?: ConsumerOptions & { clientName?: string; batch?: boolean },\n): MethodDecorator => {\n const arr = Array.isArray(topics) ? topics : [topics];\n const topicsArray = arr.map((t) => (typeof t === \"string\" ? t : t.__topic));\n\n // Extract schemas from descriptors that have them\n const schemas = new Map<string, SchemaLike>();\n for (const t of arr) {\n if (typeof t !== \"string\" && t.__schema) {\n schemas.set(t.__topic, t.__schema);\n }\n }\n\n const { clientName, batch, ...consumerOptions } = options || {};\n\n return (target, propertyKey, _descriptor) => {\n const existing: KafkaSubscriberMetadata[] =\n Reflect.getMetadata(KAFKA_SUBSCRIBER_METADATA, target.constructor) || [];\n\n Reflect.defineMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n [\n ...existing,\n {\n topics: topicsArray,\n schemas: schemas.size > 0 ? schemas : undefined,\n options: Object.keys(consumerOptions).length\n ? consumerOptions\n : undefined,\n clientName,\n batch,\n methodName: propertyKey,\n },\n ],\n target.constructor,\n );\n };\n};\n","import { Injectable } from \"@nestjs/common\";\nimport { KafkaClient, TopicMapConstraint } from \"../client/kafka.client\";\n\n/** Result returned by `KafkaHealthIndicator.check()`. */\nexport interface KafkaHealthResult {\n status: \"up\" | \"down\";\n clientId: string;\n topics?: string[];\n error?: string;\n}\n\n/** Health check service. Call `check(client)` to verify broker connectivity. */\n@Injectable()\nexport class KafkaHealthIndicator {\n async check<T extends TopicMapConstraint<T>>(\n client: KafkaClient<T>,\n ): Promise<KafkaHealthResult> {\n try {\n const { topics } = await client.checkStatus();\n return {\n status: \"up\",\n clientId: client.clientId,\n topics,\n };\n } catch (error) {\n return {\n status: \"down\",\n clientId: client.clientId,\n error: error instanceof Error ? error.message : String(error),\n };\n }\n }\n}\n"],"mappings":"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","names":["Logger","Inject","topic","Inject","Logger","Injectable","Injectable"]}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { T as TopicMapConstraint, I as IKafkaClient } from './types-CtwJihJ3.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 './types-CtwJihJ3.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 };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
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_kafkajs = require("kafkajs");
|
|
71
|
+
var KafkaTestContainer = class {
|
|
72
|
+
container;
|
|
73
|
+
image;
|
|
74
|
+
transactionWarmup;
|
|
75
|
+
topics;
|
|
76
|
+
constructor(options) {
|
|
77
|
+
this.image = options?.image ?? "confluentinc/cp-kafka:7.7.0";
|
|
78
|
+
this.transactionWarmup = options?.transactionWarmup ?? true;
|
|
79
|
+
this.topics = options?.topics ?? [];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Start the Kafka container, pre-create topics, and optionally warm up
|
|
83
|
+
* the transaction coordinator.
|
|
84
|
+
*
|
|
85
|
+
* @returns Broker connection strings, e.g. `["localhost:55123"]`.
|
|
86
|
+
*/
|
|
87
|
+
async start() {
|
|
88
|
+
this.container = await new import_kafka.KafkaContainer(this.image).withKraft().withExposedPorts(9093).withEnvironment({
|
|
89
|
+
KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: "1",
|
|
90
|
+
KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: "1"
|
|
91
|
+
}).start();
|
|
92
|
+
const host = this.container.getHost();
|
|
93
|
+
const port = this.container.getMappedPort(9093);
|
|
94
|
+
const brokers = [`${host}:${port}`];
|
|
95
|
+
const kafka = new import_kafkajs.Kafka({
|
|
96
|
+
clientId: "test-container-setup",
|
|
97
|
+
brokers,
|
|
98
|
+
logLevel: import_kafkajs.logLevel.NOTHING
|
|
99
|
+
});
|
|
100
|
+
if (this.topics.length > 0) {
|
|
101
|
+
const admin = kafka.admin();
|
|
102
|
+
await admin.connect();
|
|
103
|
+
await admin.createTopics({
|
|
104
|
+
topics: this.topics.map(
|
|
105
|
+
(t) => typeof t === "string" ? { topic: t, numPartitions: 1 } : { topic: t.topic, numPartitions: t.numPartitions ?? 1 }
|
|
106
|
+
)
|
|
107
|
+
});
|
|
108
|
+
await admin.disconnect();
|
|
109
|
+
}
|
|
110
|
+
if (this.transactionWarmup) {
|
|
111
|
+
const warmupKafka = new import_kafkajs.Kafka({
|
|
112
|
+
clientId: "test-container-warmup",
|
|
113
|
+
brokers,
|
|
114
|
+
logLevel: import_kafkajs.logLevel.NOTHING,
|
|
115
|
+
retry: { retries: 30, initialRetryTime: 500, maxRetryTime: 3e4 }
|
|
116
|
+
});
|
|
117
|
+
const txProducer = warmupKafka.producer({
|
|
118
|
+
transactionalId: "test-container-warmup-tx",
|
|
119
|
+
idempotent: true,
|
|
120
|
+
maxInFlightRequests: 1
|
|
121
|
+
});
|
|
122
|
+
await txProducer.connect();
|
|
123
|
+
const tx = await txProducer.transaction();
|
|
124
|
+
await tx.abort();
|
|
125
|
+
await txProducer.disconnect();
|
|
126
|
+
}
|
|
127
|
+
return brokers;
|
|
128
|
+
}
|
|
129
|
+
/** Stop and remove the container. */
|
|
130
|
+
async stop() {
|
|
131
|
+
await this.container?.stop();
|
|
132
|
+
this.container = void 0;
|
|
133
|
+
}
|
|
134
|
+
/** Broker connection strings. Throws if container is not started. */
|
|
135
|
+
get brokers() {
|
|
136
|
+
if (!this.container) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
"KafkaTestContainer is not started. Call start() first."
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const host = this.container.getHost();
|
|
142
|
+
const port = this.container.getMappedPort(9093);
|
|
143
|
+
return [`${host}:${port}`];
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
147
|
+
0 && (module.exports = {
|
|
148
|
+
KafkaTestContainer,
|
|
149
|
+
createMockKafkaClient
|
|
150
|
+
});
|
|
151
|
+
//# 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 { Kafka, logLevel as KafkaLogLevel } from \"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 clientId: \"test-container-setup\",\n brokers,\n logLevel: KafkaLogLevel.NOTHING,\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 clientId: \"test-container-warmup\",\n brokers,\n logLevel: KafkaLogLevel.NOTHING,\n retry: { retries: 30, initialRetryTime: 500, maxRetryTime: 30000 },\n });\n const txProducer = warmupKafka.producer({\n transactionalId: \"test-container-warmup-tx\",\n idempotent: true,\n maxInFlightRequests: 1,\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":["KafkaLogLevel"]}
|