@autofleet/kafka 0.0.4 → 0.1.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/README.md +421 -256
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -29
- package/dist/index.d.ts +70 -29
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +14 -11
package/dist/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
|
|
1
|
+
const e={info:(...e)=>console.log(`[INFO]`,...e),error:(...e)=>console.error(`[ERROR]`,...e),warn:(...e)=>console.warn(`[WARN]`,...e),debug:(...e)=>console.debug(`[DEBUG]`,...e)};var t=e,n=class extends Error{constructor(e){super(e),this.name=`KafkaError`}};const r=`x-timestamp`,i=`autofleet-kafka-producer`;var a=class{constructor(e,t,n){this.producer=null,this._isConnected=!1,this.initPromise=null,this.name=e,this.config=t,this.logger=n}async initialize(){if(!this.producer){if(this.initPromise){await this.initPromise;return}this.initPromise=(async()=>{let{Producer:e,stringSerializers:t}=await import(`@platformatic/kafka`);this.producer=new e({bootstrapBrokers:this.config.brokers,clientId:this.config.clientId||`${i}-${this.name}`,serializers:t,autocreateTopics:this.config.autoCreateTopics??!1,sasl:this.config.sasl}),this.logger.info(`Kafka: [${this.name}] Initialized producer`,{clientId:this.config.clientId||`${i}-${this.name}`,brokers:this.config.brokers})})(),await this.initPromise}}get isConnected(){return this._isConnected}async ping(){if(!this._isConnected){await this.initialize();try{let e=await this.producer.metadata({topics:[]});this._isConnected=!0,this.logger.info(`Kafka: [${this.name}] Successfully connected to cluster`,{clusterId:e.id,brokers:e.brokers.size})}catch(e){throw this.logger.error(`Kafka: [${this.name}] Failed to connect to brokers`,{error:e}),new n(`[${this.name}] Failed to connect to Kafka brokers: ${e.message}`)}}}async publish(e,t,i){if(!e)throw new n(`[${this.name}] Topic name is required`);await this.ping();try{let n={[r]:Date.now().toString(),...i?.headers},a=await this.producer.send({messages:[{topic:e,value:JSON.stringify(t),key:i?.key,partition:i?.partition,headers:n}]});return this.logger.debug(`Kafka: [${this.name}] Published message to topic ${e}`,{topic:e}),[{topic:e,partition:i?.partition??0,offset:a?.offsets?.[0]?.offset?.toString()??`0`}]}catch(r){let i=r;throw this.logger.error(`Kafka: [${this.name}] Error publishing to topic ${e}`,{error:r,value:t}),new n(`[${this.name}] Failed to publish to topic ${e}: ${i.message||`Unknown error`}`)}}async publishBatch(e){if(!e.topic)throw new n(`[${this.name}] Topic name is required`);if(!e.messages||e.messages.length===0)throw new n(`[${this.name}] At least one message is required`);await this.ping();try{let t=e.messages.map(t=>({topic:e.topic,value:JSON.stringify(t.value),key:t.key,partition:t.partition,headers:{[r]:Date.now().toString(),...t.headers}})),n=await this.producer.send({messages:t});return this.logger.debug(`Kafka: [${this.name}] Published ${t.length} messages to topic ${e.topic}`,{topic:e.topic,count:t.length}),t.map((t,r)=>({topic:e.topic,partition:t.partition??0,offset:n?.offsets?.[r]?.offset?.toString()??r.toString()}))}catch(t){let r=t;throw this.logger.error(`Kafka: [${this.name}] Error publishing batch to topic ${e.topic}`,{error:t}),new n(`[${this.name}] Failed to publish batch to topic ${e.topic}: ${r.message||`Unknown error`}`)}}async disconnect(){if(!this.producer||!this._isConnected){this.logger.debug(`Kafka: [${this.name}] Producer already disconnected`);return}this.logger.info(`Kafka: [${this.name}] Disconnecting producer`);try{await this.producer.close(),this._isConnected=!1,this.logger.info(`Kafka: [${this.name}] Producer disconnected successfully`)}catch(e){throw this.logger.error(`Kafka: [${this.name}] Error disconnecting producer`,{error:e}),new n(`[${this.name}] Failed to disconnect producer: ${e.message}`)}}},o=class{constructor(e,t){this.isConnected=!0,this.name=e,this.logger=t}async ping(){return this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping ping`),Promise.resolve()}async publish(e,t,n){return this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping publish to topic: ${e}`,{value:t}),Promise.resolve([{topic:e,partition:0,offset:`0`}])}async publishBatch(e){return this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping batch publish to topic: ${e.topic}`,{messageCount:e.messages.length}),Promise.resolve(e.messages.map(()=>({topic:e.topic,partition:0,offset:`0`})))}async disconnect(){return this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping disconnect`),Promise.resolve()}},s=class e{constructor(e){if(this.producers=new Map,this.gracefulShutdownStarted=!1,this.logger=e.logger??t,this.enabled=e.enabled??!0,this.logger.info(`Kafka: Initialized KafkaManager`,{enabled:this.enabled,producerCount:Object.keys(e.producers).length,producers:Object.keys(e.producers)}),this.enabled)for(let[t,r]of Object.entries(e.producers)){if(!r.brokers||r.brokers.length===0)throw new n(`[${t}] At least one broker is required`);this.producers.set(t,new a(t,r,this.logger))}else{for(let[t,n]of Object.entries(e.producers))this.producers.set(t,new o(t,this.logger));this.logger.info(`Kafka: Created mock producers (Kafka disabled)`)}e.dontGracefulShutdown||this.setupGracefulShutdown()}static create(t){return new e(t)}setupGracefulShutdown(){this.logger.info(`Kafka: [graceful-shutdown] adding graceful shutdown for process.pid ${process.pid}`),process.on(`SIGTERM`,async()=>{await this.gracefulShutdown(`SIGTERM`)}),process.on(`SIGINT`,async()=>{await this.gracefulShutdown(`SIGINT`)})}async gracefulShutdown(e){if(!this.gracefulShutdownStarted){this.gracefulShutdownStarted=!0,this.logger.info(`Kafka: [graceful-shutdown] received ${e}! Disconnecting all producers...`);try{await this.disconnect(),this.logger.info(`Kafka: [graceful-shutdown] all producers disconnected successfully`)}catch(e){throw this.logger.error(`Kafka: [graceful-shutdown] error during shutdown`,{error:e}),e}}}get isEnabled(){return this.enabled}get producerNames(){return Array.from(this.producers.keys())}hasProducer(e){return this.producers.has(e)}getProducer(e){let t=this.producers.get(e);if(!t)throw new n(`Producer '${e}' not found. Available producers: ${this.producerNames.join(`, `)}`);return t}async ping(){let e=Array.from(this.producers.values()).map(e=>e.ping());await Promise.all(e)}async pingProducer(e){await this.getProducer(e).ping()}async isReady(){if(!this.enabled)return!0;try{return await this.ping(),!0}catch{return!1}}getConnectionStatus(){let e={};for(let[t,n]of this.producers.entries())e[t]=n.isConnected;return e}async publish(e,t,n,r){return this.getProducer(e).publish(t,n,r)}async publishBatch(e,t){return this.getProducer(e).publishBatch(t)}async disconnect(){let e=Array.from(this.producers.values()).map(e=>e.disconnect());await Promise.all(e),this.logger.info(`Kafka: All producers disconnected`)}async disconnectProducer(e){await this.getProducer(e).disconnect()}};exports.KafkaError=n,exports.KafkaManager=s;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["fallbackLogger: LoggerInstanceManager","fallbackLogger","headers: Record<string, string>"],"sources":["../src/logger.ts","../src/kafkaError.ts","../src/consts.ts","../src/index.ts"],"sourcesContent":["import type { LoggerInstanceManager } from '@autofleet/logger';\n\n/* eslint-disable no-console */\nconst fallbackLogger: LoggerInstanceManager = {\n info: (...args: unknown[]) => console.log('[INFO]', ...args),\n error: (...args: unknown[]) => console.error('[ERROR]', ...args),\n warn: (...args: unknown[]) => console.warn('[WARN]', ...args),\n debug: (...args: unknown[]) => console.debug('[DEBUG]', ...args),\n} as LoggerInstanceManager;\n/* eslint-enable no-console */\n\nexport default fallbackLogger;\n","export default class KafkaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KafkaError';\n }\n}\n","export const TIMESTAMP_HEADER = 'x-timestamp';\nexport const CORRELATION_ID_HEADER = 'x-correlation-id';\nexport const SOURCE_HEADER = 'x-source';\n\nexport const DEFAULT_CLIENT_ID = 'autofleet-kafka-producer';\nexport const DEFAULT_TIMEOUT = 30000; // 30 seconds\nexport const DEFAULT_RETRY = {\n retries: 5,\n initialRetryTime: 300,\n maxRetryTime: 30000,\n};\n","import type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { Producer } from '@platformatic/kafka/dist/clients/producer/index.ts';\nimport fallbackLogger from './logger';\nimport KafkaError from './kafkaError';\nimport {\n DEFAULT_CLIENT_ID,\n TIMESTAMP_HEADER,\n} from './consts';\nimport type {\n KafkaOptions,\n PublishOptions,\n PublishBatchOptions,\n RecordMetadata,\n} from './types';\n\nexport interface IAfKafka {\n isConnected: boolean;\n ping(): Promise<void>;\n publish<T = unknown>(topic: string, value: T, options?: PublishOptions): Promise<RecordMetadata[]>;\n publishBatch(options: PublishBatchOptions): Promise<RecordMetadata[]>;\n disconnect(): Promise<void>;\n}\n\nclass AfKafka implements IAfKafka {\n private producer!: Producer<string, string, string, string>;\n private readonly logger: LoggerInstanceManager;\n private _isConnected = false;\n private gracefulShutdownStarted = false;\n\n private constructor(options: KafkaOptions, producer: Producer<string, string, string, string>) {\n this.logger = options.logger ?? fallbackLogger;\n this.producer = producer;\n\n this.logger.info('Kafka: Initialized Kafka producer', {\n clientId: options.clientId || DEFAULT_CLIENT_ID,\n brokers: options.brokers,\n });\n\n if (!options.dontGracefulShutdown) {\n this.setupGracefulShutdown();\n }\n }\n\n /**\n * Create a new Kafka producer instance\n */\n static async create(options: KafkaOptions): Promise<AfKafka> {\n if (!options.brokers || options.brokers.length === 0) {\n throw new KafkaError('At least one broker is required');\n }\n\n // Dynamic import of ESM-only package\n const { Producer, stringSerializers } = await import('@platformatic/kafka');\n\n const producer = new Producer({\n bootstrapBrokers: options.brokers,\n clientId: options.clientId || DEFAULT_CLIENT_ID,\n serializers: stringSerializers,\n autocreateTopics: false,\n sasl: options.sasl,\n });\n\n return new AfKafka(options, producer);\n }\n\n private setupGracefulShutdown(): void {\n this.logger.info(`Kafka: [graceful-shutdown] adding graceful shutdown for process.pid ${process.pid}`);\n\n process.on('SIGTERM', async () => {\n await this.gracefulShutdown('SIGTERM');\n });\n\n process.on('SIGINT', async () => {\n await this.gracefulShutdown('SIGINT');\n });\n }\n\n private async gracefulShutdown(signal: string): Promise<void> {\n if (this.gracefulShutdownStarted) {\n return;\n }\n\n this.gracefulShutdownStarted = true;\n\n this.logger.info(`Kafka: [graceful-shutdown] received ${signal}! Disconnecting producer...`);\n\n try {\n await this.disconnect();\n this.logger.info('Kafka: [graceful-shutdown] producer disconnected successfully');\n } catch (error) {\n this.logger.error('Kafka: [graceful-shutdown] error during shutdown', { error });\n throw error;\n }\n }\n\n get isConnected(): boolean {\n return this._isConnected;\n }\n\n /**\n * Ping Kafka brokers to verify connectivity\n */\n async ping(): Promise<void> {\n if (this._isConnected) {\n return;\n }\n\n try {\n // Fetch metadata to verify connection\n const metadata = await this.producer.metadata({ topics: [] });\n this._isConnected = true;\n\n this.logger.info('Kafka: Successfully connected to cluster', {\n clusterId: metadata.id,\n brokers: metadata.brokers.size,\n });\n } catch (error) {\n this.logger.error('Kafka: Failed to connect to brokers', { error });\n throw new KafkaError(`Failed to connect to Kafka brokers: ${(error as Error).message}`);\n }\n }\n\n /**\n * Publish a single message to a topic\n */\n async publish<T = unknown>(\n topic: string,\n value: T,\n options?: PublishOptions,\n ): Promise<RecordMetadata[]> {\n if (!topic) {\n throw new KafkaError('Topic name is required');\n }\n\n await this.ping();\n\n try {\n const headers: Record<string, string> = {\n [TIMESTAMP_HEADER]: Date.now().toString(),\n ...options?.headers,\n };\n\n const result = await this.producer.send({\n messages: [{\n topic,\n value: JSON.stringify(value),\n key: options?.key,\n partition: options?.partition,\n headers,\n }],\n });\n\n this.logger.debug(`Kafka: Published message to topic ${topic}`, {\n topic,\n });\n\n // Map ProduceResult to RecordMetadata\n return [{\n topic,\n partition: options?.partition ?? 0,\n offset: result?.offsets?.[0]?.offset?.toString() ?? '0',\n }];\n } catch (error) {\n const err = error as { message?: string; };\n this.logger.error(`Kafka: Error publishing to topic ${topic}`, { error, value });\n throw new KafkaError(`Failed to publish to topic ${topic}: ${err.message || 'Unknown error'}`);\n }\n }\n\n /**\n * Publish multiple messages in a batch\n */\n async publishBatch(options: PublishBatchOptions): Promise<RecordMetadata[]> {\n if (!options.topic) {\n throw new KafkaError('Topic name is required');\n }\n\n if (!options.messages || options.messages.length === 0) {\n throw new KafkaError('At least one message is required');\n }\n\n await this.ping();\n\n try {\n const messages = options.messages.map(msg => ({\n topic: options.topic,\n value: JSON.stringify(msg.value),\n key: msg.key,\n partition: msg.partition,\n headers: {\n [TIMESTAMP_HEADER]: Date.now().toString(),\n ...msg.headers,\n },\n }));\n\n const result = await this.producer.send({ messages });\n\n this.logger.debug(`Kafka: Published ${messages.length} messages to topic ${options.topic}`, {\n topic: options.topic,\n count: messages.length,\n });\n\n // Map ProduceResult to RecordMetadata array\n return messages.map((msg, idx) => ({\n topic: options.topic,\n partition: msg.partition ?? 0,\n offset: result?.offsets?.[idx]?.offset?.toString() ?? idx.toString(),\n }));\n } catch (error) {\n const err = error as { message?: string; };\n this.logger.error(`Kafka: Error publishing batch to topic ${options.topic}`, { error });\n throw new KafkaError(`Failed to publish batch to topic ${options.topic}: ${err.message || 'Unknown error'}`);\n }\n }\n\n /**\n * Disconnect the producer and clean up resources\n */\n async disconnect(): Promise<void> {\n if (!this._isConnected) {\n this.logger.debug('Kafka: Producer already disconnected');\n return;\n }\n\n this.logger.info('Kafka: Disconnecting producer');\n\n try {\n await this.producer.close();\n this._isConnected = false;\n this.logger.info('Kafka: Producer disconnected successfully');\n } catch (error) {\n this.logger.error('Kafka: Error disconnecting producer', { error });\n throw new KafkaError(`Failed to disconnect producer: ${(error as Error).message}`);\n }\n }\n}\n\nexport default AfKafka;\nexport { KafkaError };\nexport type {\n KafkaOptions,\n PublishOptions,\n PublishBatchOptions,\n RecordMetadata,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["fallbackLogger: LoggerInstanceManager","headers: Record<string, string>","fallbackLogger","status: Record<string, boolean>"],"sources":["../src/logger.ts","../src/kafkaError.ts","../src/consts.ts","../src/manager.ts"],"sourcesContent":["import type { LoggerInstanceManager } from '@autofleet/logger';\n\n/* eslint-disable no-console */\nconst fallbackLogger: LoggerInstanceManager = {\n info: (...args: unknown[]) => console.log('[INFO]', ...args),\n error: (...args: unknown[]) => console.error('[ERROR]', ...args),\n warn: (...args: unknown[]) => console.warn('[WARN]', ...args),\n debug: (...args: unknown[]) => console.debug('[DEBUG]', ...args),\n} as LoggerInstanceManager;\n/* eslint-enable no-console */\n\nexport default fallbackLogger;\n","export default class KafkaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KafkaError';\n }\n}\n","export const TIMESTAMP_HEADER = 'x-timestamp';\nexport const CORRELATION_ID_HEADER = 'x-correlation-id';\nexport const SOURCE_HEADER = 'x-source';\n\nexport const DEFAULT_CLIENT_ID = 'autofleet-kafka-producer';\nexport const DEFAULT_TIMEOUT = 30000; // 30 seconds\nexport const DEFAULT_RETRY = {\n retries: 5,\n initialRetryTime: 300,\n maxRetryTime: 30000,\n};\n","import type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { Producer } from '@platformatic/kafka/dist/clients/producer/index.ts';\nimport fallbackLogger from './logger';\nimport KafkaError from './kafkaError';\nimport {\n DEFAULT_CLIENT_ID,\n TIMESTAMP_HEADER,\n} from './consts';\nimport type {\n KafkaManagerOptions,\n ProducerConfig,\n PublishOptions,\n PublishBatchOptions,\n RecordMetadata,\n} from './types';\n\n/**\n * Wrapper for a single Kafka producer instance with lazy initialization\n */\nclass ProducerWrapper {\n private producer: Producer<string, string, string, string> | null = null;\n private readonly logger: LoggerInstanceManager;\n private readonly name: string;\n private readonly config: ProducerConfig;\n private _isConnected = false;\n private initPromise: Promise<void> | null = null;\n\n constructor(\n name: string,\n config: ProducerConfig,\n logger: LoggerInstanceManager,\n ) {\n this.name = name;\n this.config = config;\n this.logger = logger;\n }\n\n private async initialize(): Promise<void> {\n if (this.producer) {\n return;\n }\n\n if (this.initPromise) {\n await this.initPromise;\n return;\n }\n\n this.initPromise = (async (): Promise<void> => {\n const { Producer, stringSerializers } = await import('@platformatic/kafka');\n\n this.producer = new Producer({\n bootstrapBrokers: this.config.brokers,\n clientId: this.config.clientId || `${DEFAULT_CLIENT_ID}-${this.name}`,\n serializers: stringSerializers,\n autocreateTopics: this.config.autoCreateTopics ?? false,\n sasl: this.config.sasl,\n });\n\n this.logger.info(`Kafka: [${this.name}] Initialized producer`, {\n clientId: this.config.clientId || `${DEFAULT_CLIENT_ID}-${this.name}`,\n brokers: this.config.brokers,\n });\n })();\n\n await this.initPromise;\n }\n\n get isConnected(): boolean {\n return this._isConnected;\n }\n\n async ping(): Promise<void> {\n if (this._isConnected) {\n return;\n }\n\n await this.initialize();\n\n try {\n const metadata = await this.producer!.metadata({ topics: [] });\n this._isConnected = true;\n\n this.logger.info(`Kafka: [${this.name}] Successfully connected to cluster`, {\n clusterId: metadata.id,\n brokers: metadata.brokers.size,\n });\n } catch (error) {\n this.logger.error(`Kafka: [${this.name}] Failed to connect to brokers`, { error });\n throw new KafkaError(`[${this.name}] Failed to connect to Kafka brokers: ${(error as Error).message}`);\n }\n }\n\n async publish<T = unknown>(\n topic: string,\n value: T,\n options?: PublishOptions,\n ): Promise<RecordMetadata[]> {\n if (!topic) {\n throw new KafkaError(`[${this.name}] Topic name is required`);\n }\n\n await this.ping();\n\n try {\n const headers: Record<string, string> = {\n [TIMESTAMP_HEADER]: Date.now().toString(),\n ...options?.headers,\n };\n\n const result = await this.producer!.send({\n messages: [{\n topic,\n value: JSON.stringify(value),\n key: options?.key,\n partition: options?.partition,\n headers,\n }],\n });\n\n this.logger.debug(`Kafka: [${this.name}] Published message to topic ${topic}`, {\n topic,\n });\n\n return [{\n topic,\n partition: options?.partition ?? 0,\n offset: result?.offsets?.[0]?.offset?.toString() ?? '0',\n }];\n } catch (error) {\n const err = error as { message?: string; };\n this.logger.error(`Kafka: [${this.name}] Error publishing to topic ${topic}`, { error, value });\n throw new KafkaError(`[${this.name}] Failed to publish to topic ${topic}: ${err.message || 'Unknown error'}`);\n }\n }\n\n async publishBatch(options: PublishBatchOptions): Promise<RecordMetadata[]> {\n if (!options.topic) {\n throw new KafkaError(`[${this.name}] Topic name is required`);\n }\n\n if (!options.messages || options.messages.length === 0) {\n throw new KafkaError(`[${this.name}] At least one message is required`);\n }\n\n await this.ping();\n\n try {\n const messages = options.messages.map(msg => ({\n topic: options.topic,\n value: JSON.stringify(msg.value),\n key: msg.key,\n partition: msg.partition,\n headers: {\n [TIMESTAMP_HEADER]: Date.now().toString(),\n ...msg.headers,\n },\n }));\n\n const result = await this.producer!.send({ messages });\n\n this.logger.debug(`Kafka: [${this.name}] Published ${messages.length} messages to topic ${options.topic}`, {\n topic: options.topic,\n count: messages.length,\n });\n\n return messages.map((msg, idx) => ({\n topic: options.topic,\n partition: msg.partition ?? 0,\n offset: result?.offsets?.[idx]?.offset?.toString() ?? idx.toString(),\n }));\n } catch (error) {\n const err = error as { message?: string; };\n this.logger.error(`Kafka: [${this.name}] Error publishing batch to topic ${options.topic}`, { error });\n throw new KafkaError(`[${this.name}] Failed to publish batch to topic ${options.topic}: ${err.message || 'Unknown error'}`);\n }\n }\n\n async disconnect(): Promise<void> {\n if (!this.producer || !this._isConnected) {\n this.logger.debug(`Kafka: [${this.name}] Producer already disconnected`);\n return;\n }\n\n this.logger.info(`Kafka: [${this.name}] Disconnecting producer`);\n\n try {\n await this.producer.close();\n this._isConnected = false;\n this.logger.info(`Kafka: [${this.name}] Producer disconnected successfully`);\n } catch (error) {\n this.logger.error(`Kafka: [${this.name}] Error disconnecting producer`, { error });\n throw new KafkaError(`[${this.name}] Failed to disconnect producer: ${(error as Error).message}`);\n }\n }\n}\n\n/**\n * Mock producer for when Kafka is disabled\n */\nclass MockProducer {\n private readonly logger: LoggerInstanceManager;\n private readonly name: string;\n\n constructor(name: string, logger: LoggerInstanceManager) {\n this.name = name;\n this.logger = logger;\n }\n\n readonly isConnected = true;\n\n async ping(): Promise<void> {\n this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping ping`);\n return Promise.resolve();\n }\n\n async publish<T = unknown>(topic: string, value: T, _options?: PublishOptions): Promise<RecordMetadata[]> {\n this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping publish to topic: ${topic}`, { value });\n return Promise.resolve([{\n topic,\n partition: 0,\n offset: '0',\n }]);\n }\n\n async publishBatch(options: PublishBatchOptions): Promise<RecordMetadata[]> {\n this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping batch publish to topic: ${options.topic}`, {\n messageCount: options.messages.length,\n });\n return Promise.resolve(options.messages.map(() => ({\n topic: options.topic,\n partition: 0,\n offset: '0',\n })));\n }\n\n async disconnect(): Promise<void> {\n this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping disconnect`);\n return Promise.resolve();\n }\n}\n\n/**\n * Manager for multiple Kafka producers\n */\nexport class KafkaManager<TProducers extends string = string> {\n private readonly producers = new Map<string, ProducerWrapper | MockProducer>();\n private readonly logger: LoggerInstanceManager;\n private readonly enabled: boolean;\n private gracefulShutdownStarted = false;\n\n private constructor(options: KafkaManagerOptions) {\n this.logger = options.logger ?? fallbackLogger;\n this.enabled = options.enabled ?? true;\n\n this.logger.info('Kafka: Initialized KafkaManager', {\n enabled: this.enabled,\n producerCount: Object.keys(options.producers).length,\n producers: Object.keys(options.producers),\n });\n\n // Create producers\n if (!this.enabled) {\n // Create mock producers\n for (const [name, _config] of Object.entries(options.producers)) {\n this.producers.set(name, new MockProducer(name, this.logger));\n }\n this.logger.info('Kafka: Created mock producers (Kafka disabled)');\n } else {\n // Create real producers (they will initialize lazily)\n for (const [name, config] of Object.entries(options.producers)) {\n if (!config.brokers || config.brokers.length === 0) {\n throw new KafkaError(`[${name}] At least one broker is required`);\n }\n\n this.producers.set(name, new ProducerWrapper(name, config, this.logger));\n }\n }\n\n if (!options.dontGracefulShutdown) {\n this.setupGracefulShutdown();\n }\n }\n\n /**\n * Create a new KafkaManager instance with multiple named producers\n * Producers are initialized lazily on first use\n * Producer names are type-safe based on the configuration\n */\n static create<T extends Record<string, ProducerConfig>>(\n options: Omit<KafkaManagerOptions, 'producers'> & { producers: T; },\n ): KafkaManager<keyof T & string> {\n return new KafkaManager(options);\n }\n\n private setupGracefulShutdown(): void {\n this.logger.info(`Kafka: [graceful-shutdown] adding graceful shutdown for process.pid ${process.pid}`);\n\n process.on('SIGTERM', async () => {\n await this.gracefulShutdown('SIGTERM');\n });\n\n process.on('SIGINT', async () => {\n await this.gracefulShutdown('SIGINT');\n });\n }\n\n private async gracefulShutdown(signal: string): Promise<void> {\n if (this.gracefulShutdownStarted) {\n return;\n }\n\n this.gracefulShutdownStarted = true;\n\n this.logger.info(`Kafka: [graceful-shutdown] received ${signal}! Disconnecting all producers...`);\n\n try {\n await this.disconnect();\n this.logger.info('Kafka: [graceful-shutdown] all producers disconnected successfully');\n } catch (error) {\n this.logger.error('Kafka: [graceful-shutdown] error during shutdown', { error });\n throw error;\n }\n }\n\n /**\n * Check if Kafka is enabled\n */\n get isEnabled(): boolean {\n return this.enabled;\n }\n\n /**\n * Get all producer names\n */\n get producerNames(): TProducers[] {\n return Array.from(this.producers.keys()) as TProducers[];\n }\n\n /**\n * Check if a producer exists\n */\n hasProducer(name: TProducers): boolean {\n return this.producers.has(name);\n }\n\n /**\n * Get a specific producer\n */\n private getProducer(name: TProducers): ProducerWrapper | MockProducer {\n const producer = this.producers.get(name);\n if (!producer) {\n throw new KafkaError(`Producer '${name}' not found. Available producers: ${this.producerNames.join(', ')}`);\n }\n return producer;\n }\n\n /**\n * Ping all producers to verify connectivity\n */\n async ping(): Promise<void> {\n const promises = Array.from(this.producers.values()).map(p => p.ping());\n await Promise.all(promises);\n }\n\n /**\n * Ping a specific producer\n */\n async pingProducer(name: TProducers): Promise<void> {\n await this.getProducer(name).ping();\n }\n\n /**\n * Check if all producers are connected and ready\n */\n async isReady(): Promise<boolean> {\n if (!this.enabled) {\n return true;\n }\n\n try {\n await this.ping();\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Check connection status of all producers\n */\n getConnectionStatus(): Record<TProducers, boolean> {\n const status: Record<string, boolean> = {};\n for (const [name, producer] of this.producers.entries()) {\n status[name] = producer.isConnected;\n }\n return status as Record<TProducers, boolean>;\n }\n\n /**\n * Publish a message using a named producer\n */\n async publish<T = unknown>(\n producerName: TProducers,\n topic: string,\n value: T,\n options?: PublishOptions,\n ): Promise<RecordMetadata[]> {\n return this.getProducer(producerName).publish(topic, value, options);\n }\n\n /**\n * Publish a batch of messages using a named producer\n */\n async publishBatch(\n producerName: TProducers,\n options: PublishBatchOptions,\n ): Promise<RecordMetadata[]> {\n return this.getProducer(producerName).publishBatch(options);\n }\n\n /**\n * Disconnect all producers\n */\n async disconnect(): Promise<void> {\n const promises = Array.from(this.producers.values()).map(p => p.disconnect());\n await Promise.all(promises);\n this.logger.info('Kafka: All producers disconnected');\n }\n\n /**\n * Disconnect a specific producer\n */\n async disconnectProducer(name: TProducers): Promise<void> {\n await this.getProducer(name).disconnect();\n }\n}\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { LoggerInstanceManager } from "@autofleet/logger";
|
|
2
2
|
import { ConnectionOptions } from "@platformatic/kafka";
|
|
3
3
|
|
|
4
|
-
//#region src/kafkaError.d.ts
|
|
5
|
-
declare class KafkaError extends Error {
|
|
6
|
-
constructor(message: string);
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
9
4
|
//#region src/types.d.ts
|
|
10
|
-
interface
|
|
5
|
+
interface ProducerConfig {
|
|
11
6
|
/** Kafka broker addresses */
|
|
12
7
|
brokers: string[];
|
|
13
8
|
/** Client ID for this producer */
|
|
14
9
|
clientId?: string;
|
|
10
|
+
/** SASL authentication options */
|
|
11
|
+
sasl?: ConnectionOptions["sasl"];
|
|
12
|
+
/** Automatically create topics if they don't exist
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
autoCreateTopics?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface KafkaManagerOptions {
|
|
18
|
+
/** Enable/disable Kafka - when false, returns mock implementations */
|
|
19
|
+
enabled?: boolean;
|
|
15
20
|
/** Custom logger instance */
|
|
16
21
|
logger?: LoggerInstanceManager;
|
|
17
22
|
/**
|
|
@@ -19,8 +24,8 @@ interface KafkaOptions {
|
|
|
19
24
|
* @default false
|
|
20
25
|
*/
|
|
21
26
|
dontGracefulShutdown?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
|
|
27
|
+
/** Named producers configuration */
|
|
28
|
+
producers: Record<string, ProducerConfig>;
|
|
24
29
|
}
|
|
25
30
|
interface PublishOptions {
|
|
26
31
|
/** Custom headers to attach to the message */
|
|
@@ -47,44 +52,80 @@ interface RecordMetadata {
|
|
|
47
52
|
offset: string;
|
|
48
53
|
}
|
|
49
54
|
//#endregion
|
|
50
|
-
//#region src/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
disconnect(): Promise<void>;
|
|
57
|
-
}
|
|
58
|
-
declare class AfKafka implements IAfKafka {
|
|
59
|
-
private producer;
|
|
55
|
+
//#region src/manager.d.ts
|
|
56
|
+
/**
|
|
57
|
+
* Manager for multiple Kafka producers
|
|
58
|
+
*/
|
|
59
|
+
declare class KafkaManager<TProducers extends string = string> {
|
|
60
|
+
private readonly producers;
|
|
60
61
|
private readonly logger;
|
|
61
|
-
private
|
|
62
|
+
private readonly enabled;
|
|
62
63
|
private gracefulShutdownStarted;
|
|
63
64
|
private constructor();
|
|
64
65
|
/**
|
|
65
|
-
* Create a new
|
|
66
|
+
* Create a new KafkaManager instance with multiple named producers
|
|
67
|
+
* Producers are initialized lazily on first use
|
|
68
|
+
* Producer names are type-safe based on the configuration
|
|
66
69
|
*/
|
|
67
|
-
static create(options:
|
|
70
|
+
static create<T extends Record<string, ProducerConfig>>(options: Omit<KafkaManagerOptions, "producers"> & {
|
|
71
|
+
producers: T;
|
|
72
|
+
}): KafkaManager<keyof T & string>;
|
|
68
73
|
private setupGracefulShutdown;
|
|
69
74
|
private gracefulShutdown;
|
|
70
|
-
get isConnected(): boolean;
|
|
71
75
|
/**
|
|
72
|
-
*
|
|
76
|
+
* Check if Kafka is enabled
|
|
77
|
+
*/
|
|
78
|
+
get isEnabled(): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Get all producer names
|
|
81
|
+
*/
|
|
82
|
+
get producerNames(): TProducers[];
|
|
83
|
+
/**
|
|
84
|
+
* Check if a producer exists
|
|
85
|
+
*/
|
|
86
|
+
hasProducer(name: TProducers): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Get a specific producer
|
|
89
|
+
*/
|
|
90
|
+
private getProducer;
|
|
91
|
+
/**
|
|
92
|
+
* Ping all producers to verify connectivity
|
|
73
93
|
*/
|
|
74
94
|
ping(): Promise<void>;
|
|
75
95
|
/**
|
|
76
|
-
*
|
|
96
|
+
* Ping a specific producer
|
|
97
|
+
*/
|
|
98
|
+
pingProducer(name: TProducers): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Check if all producers are connected and ready
|
|
101
|
+
*/
|
|
102
|
+
isReady(): Promise<boolean>;
|
|
103
|
+
/**
|
|
104
|
+
* Check connection status of all producers
|
|
77
105
|
*/
|
|
78
|
-
|
|
106
|
+
getConnectionStatus(): Record<TProducers, boolean>;
|
|
79
107
|
/**
|
|
80
|
-
* Publish
|
|
108
|
+
* Publish a message using a named producer
|
|
81
109
|
*/
|
|
82
|
-
|
|
110
|
+
publish<T = unknown>(producerName: TProducers, topic: string, value: T, options?: PublishOptions): Promise<RecordMetadata[]>;
|
|
83
111
|
/**
|
|
84
|
-
*
|
|
112
|
+
* Publish a batch of messages using a named producer
|
|
113
|
+
*/
|
|
114
|
+
publishBatch(producerName: TProducers, options: PublishBatchOptions): Promise<RecordMetadata[]>;
|
|
115
|
+
/**
|
|
116
|
+
* Disconnect all producers
|
|
85
117
|
*/
|
|
86
118
|
disconnect(): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Disconnect a specific producer
|
|
121
|
+
*/
|
|
122
|
+
disconnectProducer(name: TProducers): Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/kafkaError.d.ts
|
|
126
|
+
declare class KafkaError extends Error {
|
|
127
|
+
constructor(message: string);
|
|
87
128
|
}
|
|
88
129
|
//#endregion
|
|
89
|
-
export {
|
|
130
|
+
export { KafkaError, KafkaManager, type KafkaManagerOptions, type ProducerConfig, type PublishBatchOptions, type PublishOptions, type RecordMetadata };
|
|
90
131
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
import { ConnectionOptions } from "@platformatic/kafka";
|
|
2
2
|
import { LoggerInstanceManager } from "@autofleet/logger";
|
|
3
3
|
|
|
4
|
-
//#region src/kafkaError.d.ts
|
|
5
|
-
declare class KafkaError extends Error {
|
|
6
|
-
constructor(message: string);
|
|
7
|
-
}
|
|
8
|
-
//#endregion
|
|
9
4
|
//#region src/types.d.ts
|
|
10
|
-
interface
|
|
5
|
+
interface ProducerConfig {
|
|
11
6
|
/** Kafka broker addresses */
|
|
12
7
|
brokers: string[];
|
|
13
8
|
/** Client ID for this producer */
|
|
14
9
|
clientId?: string;
|
|
10
|
+
/** SASL authentication options */
|
|
11
|
+
sasl?: ConnectionOptions["sasl"];
|
|
12
|
+
/** Automatically create topics if they don't exist
|
|
13
|
+
* @default false
|
|
14
|
+
*/
|
|
15
|
+
autoCreateTopics?: boolean;
|
|
16
|
+
}
|
|
17
|
+
interface KafkaManagerOptions {
|
|
18
|
+
/** Enable/disable Kafka - when false, returns mock implementations */
|
|
19
|
+
enabled?: boolean;
|
|
15
20
|
/** Custom logger instance */
|
|
16
21
|
logger?: LoggerInstanceManager;
|
|
17
22
|
/**
|
|
@@ -19,8 +24,8 @@ interface KafkaOptions {
|
|
|
19
24
|
* @default false
|
|
20
25
|
*/
|
|
21
26
|
dontGracefulShutdown?: boolean;
|
|
22
|
-
/**
|
|
23
|
-
|
|
27
|
+
/** Named producers configuration */
|
|
28
|
+
producers: Record<string, ProducerConfig>;
|
|
24
29
|
}
|
|
25
30
|
interface PublishOptions {
|
|
26
31
|
/** Custom headers to attach to the message */
|
|
@@ -47,44 +52,80 @@ interface RecordMetadata {
|
|
|
47
52
|
offset: string;
|
|
48
53
|
}
|
|
49
54
|
//#endregion
|
|
50
|
-
//#region src/
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
disconnect(): Promise<void>;
|
|
57
|
-
}
|
|
58
|
-
declare class AfKafka implements IAfKafka {
|
|
59
|
-
private producer;
|
|
55
|
+
//#region src/manager.d.ts
|
|
56
|
+
/**
|
|
57
|
+
* Manager for multiple Kafka producers
|
|
58
|
+
*/
|
|
59
|
+
declare class KafkaManager<TProducers extends string = string> {
|
|
60
|
+
private readonly producers;
|
|
60
61
|
private readonly logger;
|
|
61
|
-
private
|
|
62
|
+
private readonly enabled;
|
|
62
63
|
private gracefulShutdownStarted;
|
|
63
64
|
private constructor();
|
|
64
65
|
/**
|
|
65
|
-
* Create a new
|
|
66
|
+
* Create a new KafkaManager instance with multiple named producers
|
|
67
|
+
* Producers are initialized lazily on first use
|
|
68
|
+
* Producer names are type-safe based on the configuration
|
|
66
69
|
*/
|
|
67
|
-
static create(options:
|
|
70
|
+
static create<T extends Record<string, ProducerConfig>>(options: Omit<KafkaManagerOptions, "producers"> & {
|
|
71
|
+
producers: T;
|
|
72
|
+
}): KafkaManager<keyof T & string>;
|
|
68
73
|
private setupGracefulShutdown;
|
|
69
74
|
private gracefulShutdown;
|
|
70
|
-
get isConnected(): boolean;
|
|
71
75
|
/**
|
|
72
|
-
*
|
|
76
|
+
* Check if Kafka is enabled
|
|
77
|
+
*/
|
|
78
|
+
get isEnabled(): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Get all producer names
|
|
81
|
+
*/
|
|
82
|
+
get producerNames(): TProducers[];
|
|
83
|
+
/**
|
|
84
|
+
* Check if a producer exists
|
|
85
|
+
*/
|
|
86
|
+
hasProducer(name: TProducers): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Get a specific producer
|
|
89
|
+
*/
|
|
90
|
+
private getProducer;
|
|
91
|
+
/**
|
|
92
|
+
* Ping all producers to verify connectivity
|
|
73
93
|
*/
|
|
74
94
|
ping(): Promise<void>;
|
|
75
95
|
/**
|
|
76
|
-
*
|
|
96
|
+
* Ping a specific producer
|
|
97
|
+
*/
|
|
98
|
+
pingProducer(name: TProducers): Promise<void>;
|
|
99
|
+
/**
|
|
100
|
+
* Check if all producers are connected and ready
|
|
101
|
+
*/
|
|
102
|
+
isReady(): Promise<boolean>;
|
|
103
|
+
/**
|
|
104
|
+
* Check connection status of all producers
|
|
77
105
|
*/
|
|
78
|
-
|
|
106
|
+
getConnectionStatus(): Record<TProducers, boolean>;
|
|
79
107
|
/**
|
|
80
|
-
* Publish
|
|
108
|
+
* Publish a message using a named producer
|
|
81
109
|
*/
|
|
82
|
-
|
|
110
|
+
publish<T = unknown>(producerName: TProducers, topic: string, value: T, options?: PublishOptions): Promise<RecordMetadata[]>;
|
|
83
111
|
/**
|
|
84
|
-
*
|
|
112
|
+
* Publish a batch of messages using a named producer
|
|
113
|
+
*/
|
|
114
|
+
publishBatch(producerName: TProducers, options: PublishBatchOptions): Promise<RecordMetadata[]>;
|
|
115
|
+
/**
|
|
116
|
+
* Disconnect all producers
|
|
85
117
|
*/
|
|
86
118
|
disconnect(): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Disconnect a specific producer
|
|
121
|
+
*/
|
|
122
|
+
disconnectProducer(name: TProducers): Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/kafkaError.d.ts
|
|
126
|
+
declare class KafkaError extends Error {
|
|
127
|
+
constructor(message: string);
|
|
87
128
|
}
|
|
88
129
|
//#endregion
|
|
89
|
-
export {
|
|
130
|
+
export { KafkaError, KafkaManager, type KafkaManagerOptions, type ProducerConfig, type PublishBatchOptions, type PublishOptions, type RecordMetadata };
|
|
90
131
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var e={info:(...e)=>console.log(`[INFO]`,...e),error:(...e)=>console.error(`[ERROR]`,...e),warn:(...e)=>console.warn(`[WARN]`,...e),debug:(...e)=>console.debug(`[DEBUG]`,...e)},t=class extends Error{constructor(e){super(e),this.name=`KafkaError`}};const n=`x-timestamp`,r=`autofleet-kafka-producer`;var i=class
|
|
1
|
+
var e={info:(...e)=>console.log(`[INFO]`,...e),error:(...e)=>console.error(`[ERROR]`,...e),warn:(...e)=>console.warn(`[WARN]`,...e),debug:(...e)=>console.debug(`[DEBUG]`,...e)},t=class extends Error{constructor(e){super(e),this.name=`KafkaError`}};const n=`x-timestamp`,r=`autofleet-kafka-producer`;var i=class{constructor(e,t,n){this.producer=null,this._isConnected=!1,this.initPromise=null,this.name=e,this.config=t,this.logger=n}async initialize(){if(!this.producer){if(this.initPromise){await this.initPromise;return}this.initPromise=(async()=>{let{Producer:e,stringSerializers:t}=await import(`@platformatic/kafka`);this.producer=new e({bootstrapBrokers:this.config.brokers,clientId:this.config.clientId||`${r}-${this.name}`,serializers:t,autocreateTopics:this.config.autoCreateTopics??!1,sasl:this.config.sasl}),this.logger.info(`Kafka: [${this.name}] Initialized producer`,{clientId:this.config.clientId||`${r}-${this.name}`,brokers:this.config.brokers})})(),await this.initPromise}}get isConnected(){return this._isConnected}async ping(){if(!this._isConnected){await this.initialize();try{let e=await this.producer.metadata({topics:[]});this._isConnected=!0,this.logger.info(`Kafka: [${this.name}] Successfully connected to cluster`,{clusterId:e.id,brokers:e.brokers.size})}catch(e){throw this.logger.error(`Kafka: [${this.name}] Failed to connect to brokers`,{error:e}),new t(`[${this.name}] Failed to connect to Kafka brokers: ${e.message}`)}}}async publish(e,r,i){if(!e)throw new t(`[${this.name}] Topic name is required`);await this.ping();try{let t={[n]:Date.now().toString(),...i?.headers},a=await this.producer.send({messages:[{topic:e,value:JSON.stringify(r),key:i?.key,partition:i?.partition,headers:t}]});return this.logger.debug(`Kafka: [${this.name}] Published message to topic ${e}`,{topic:e}),[{topic:e,partition:i?.partition??0,offset:a?.offsets?.[0]?.offset?.toString()??`0`}]}catch(n){let i=n;throw this.logger.error(`Kafka: [${this.name}] Error publishing to topic ${e}`,{error:n,value:r}),new t(`[${this.name}] Failed to publish to topic ${e}: ${i.message||`Unknown error`}`)}}async publishBatch(e){if(!e.topic)throw new t(`[${this.name}] Topic name is required`);if(!e.messages||e.messages.length===0)throw new t(`[${this.name}] At least one message is required`);await this.ping();try{let t=e.messages.map(t=>({topic:e.topic,value:JSON.stringify(t.value),key:t.key,partition:t.partition,headers:{[n]:Date.now().toString(),...t.headers}})),r=await this.producer.send({messages:t});return this.logger.debug(`Kafka: [${this.name}] Published ${t.length} messages to topic ${e.topic}`,{topic:e.topic,count:t.length}),t.map((t,n)=>({topic:e.topic,partition:t.partition??0,offset:r?.offsets?.[n]?.offset?.toString()??n.toString()}))}catch(n){let r=n;throw this.logger.error(`Kafka: [${this.name}] Error publishing batch to topic ${e.topic}`,{error:n}),new t(`[${this.name}] Failed to publish batch to topic ${e.topic}: ${r.message||`Unknown error`}`)}}async disconnect(){if(!this.producer||!this._isConnected){this.logger.debug(`Kafka: [${this.name}] Producer already disconnected`);return}this.logger.info(`Kafka: [${this.name}] Disconnecting producer`);try{await this.producer.close(),this._isConnected=!1,this.logger.info(`Kafka: [${this.name}] Producer disconnected successfully`)}catch(e){throw this.logger.error(`Kafka: [${this.name}] Error disconnecting producer`,{error:e}),new t(`[${this.name}] Failed to disconnect producer: ${e.message}`)}}},a=class{constructor(e,t){this.isConnected=!0,this.name=e,this.logger=t}async ping(){return this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping ping`),Promise.resolve()}async publish(e,t,n){return this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping publish to topic: ${e}`,{value:t}),Promise.resolve([{topic:e,partition:0,offset:`0`}])}async publishBatch(e){return this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping batch publish to topic: ${e.topic}`,{messageCount:e.messages.length}),Promise.resolve(e.messages.map(()=>({topic:e.topic,partition:0,offset:`0`})))}async disconnect(){return this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping disconnect`),Promise.resolve()}},o=class n{constructor(n){if(this.producers=new Map,this.gracefulShutdownStarted=!1,this.logger=n.logger??e,this.enabled=n.enabled??!0,this.logger.info(`Kafka: Initialized KafkaManager`,{enabled:this.enabled,producerCount:Object.keys(n.producers).length,producers:Object.keys(n.producers)}),this.enabled)for(let[e,r]of Object.entries(n.producers)){if(!r.brokers||r.brokers.length===0)throw new t(`[${e}] At least one broker is required`);this.producers.set(e,new i(e,r,this.logger))}else{for(let[e,t]of Object.entries(n.producers))this.producers.set(e,new a(e,this.logger));this.logger.info(`Kafka: Created mock producers (Kafka disabled)`)}n.dontGracefulShutdown||this.setupGracefulShutdown()}static create(e){return new n(e)}setupGracefulShutdown(){this.logger.info(`Kafka: [graceful-shutdown] adding graceful shutdown for process.pid ${process.pid}`),process.on(`SIGTERM`,async()=>{await this.gracefulShutdown(`SIGTERM`)}),process.on(`SIGINT`,async()=>{await this.gracefulShutdown(`SIGINT`)})}async gracefulShutdown(e){if(!this.gracefulShutdownStarted){this.gracefulShutdownStarted=!0,this.logger.info(`Kafka: [graceful-shutdown] received ${e}! Disconnecting all producers...`);try{await this.disconnect(),this.logger.info(`Kafka: [graceful-shutdown] all producers disconnected successfully`)}catch(e){throw this.logger.error(`Kafka: [graceful-shutdown] error during shutdown`,{error:e}),e}}}get isEnabled(){return this.enabled}get producerNames(){return Array.from(this.producers.keys())}hasProducer(e){return this.producers.has(e)}getProducer(e){let n=this.producers.get(e);if(!n)throw new t(`Producer '${e}' not found. Available producers: ${this.producerNames.join(`, `)}`);return n}async ping(){let e=Array.from(this.producers.values()).map(e=>e.ping());await Promise.all(e)}async pingProducer(e){await this.getProducer(e).ping()}async isReady(){if(!this.enabled)return!0;try{return await this.ping(),!0}catch{return!1}}getConnectionStatus(){let e={};for(let[t,n]of this.producers.entries())e[t]=n.isConnected;return e}async publish(e,t,n,r){return this.getProducer(e).publish(t,n,r)}async publishBatch(e,t){return this.getProducer(e).publishBatch(t)}async disconnect(){let e=Array.from(this.producers.values()).map(e=>e.disconnect());await Promise.all(e),this.logger.info(`Kafka: All producers disconnected`)}async disconnectProducer(e){await this.getProducer(e).disconnect()}};export{t as KafkaError,o as KafkaManager};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["fallbackLogger: LoggerInstanceManager","fallbackLogger","headers: Record<string, string>"],"sources":["../src/logger.ts","../src/kafkaError.ts","../src/consts.ts","../src/index.ts"],"sourcesContent":["import type { LoggerInstanceManager } from '@autofleet/logger';\n\n/* eslint-disable no-console */\nconst fallbackLogger: LoggerInstanceManager = {\n info: (...args: unknown[]) => console.log('[INFO]', ...args),\n error: (...args: unknown[]) => console.error('[ERROR]', ...args),\n warn: (...args: unknown[]) => console.warn('[WARN]', ...args),\n debug: (...args: unknown[]) => console.debug('[DEBUG]', ...args),\n} as LoggerInstanceManager;\n/* eslint-enable no-console */\n\nexport default fallbackLogger;\n","export default class KafkaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KafkaError';\n }\n}\n","export const TIMESTAMP_HEADER = 'x-timestamp';\nexport const CORRELATION_ID_HEADER = 'x-correlation-id';\nexport const SOURCE_HEADER = 'x-source';\n\nexport const DEFAULT_CLIENT_ID = 'autofleet-kafka-producer';\nexport const DEFAULT_TIMEOUT = 30000; // 30 seconds\nexport const DEFAULT_RETRY = {\n retries: 5,\n initialRetryTime: 300,\n maxRetryTime: 30000,\n};\n","import type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { Producer } from '@platformatic/kafka/dist/clients/producer/index.ts';\nimport fallbackLogger from './logger';\nimport KafkaError from './kafkaError';\nimport {\n DEFAULT_CLIENT_ID,\n TIMESTAMP_HEADER,\n} from './consts';\nimport type {\n KafkaOptions,\n PublishOptions,\n PublishBatchOptions,\n RecordMetadata,\n} from './types';\n\nexport interface IAfKafka {\n isConnected: boolean;\n ping(): Promise<void>;\n publish<T = unknown>(topic: string, value: T, options?: PublishOptions): Promise<RecordMetadata[]>;\n publishBatch(options: PublishBatchOptions): Promise<RecordMetadata[]>;\n disconnect(): Promise<void>;\n}\n\nclass AfKafka implements IAfKafka {\n private producer!: Producer<string, string, string, string>;\n private readonly logger: LoggerInstanceManager;\n private _isConnected = false;\n private gracefulShutdownStarted = false;\n\n private constructor(options: KafkaOptions, producer: Producer<string, string, string, string>) {\n this.logger = options.logger ?? fallbackLogger;\n this.producer = producer;\n\n this.logger.info('Kafka: Initialized Kafka producer', {\n clientId: options.clientId || DEFAULT_CLIENT_ID,\n brokers: options.brokers,\n });\n\n if (!options.dontGracefulShutdown) {\n this.setupGracefulShutdown();\n }\n }\n\n /**\n * Create a new Kafka producer instance\n */\n static async create(options: KafkaOptions): Promise<AfKafka> {\n if (!options.brokers || options.brokers.length === 0) {\n throw new KafkaError('At least one broker is required');\n }\n\n // Dynamic import of ESM-only package\n const { Producer, stringSerializers } = await import('@platformatic/kafka');\n\n const producer = new Producer({\n bootstrapBrokers: options.brokers,\n clientId: options.clientId || DEFAULT_CLIENT_ID,\n serializers: stringSerializers,\n autocreateTopics: false,\n sasl: options.sasl,\n });\n\n return new AfKafka(options, producer);\n }\n\n private setupGracefulShutdown(): void {\n this.logger.info(`Kafka: [graceful-shutdown] adding graceful shutdown for process.pid ${process.pid}`);\n\n process.on('SIGTERM', async () => {\n await this.gracefulShutdown('SIGTERM');\n });\n\n process.on('SIGINT', async () => {\n await this.gracefulShutdown('SIGINT');\n });\n }\n\n private async gracefulShutdown(signal: string): Promise<void> {\n if (this.gracefulShutdownStarted) {\n return;\n }\n\n this.gracefulShutdownStarted = true;\n\n this.logger.info(`Kafka: [graceful-shutdown] received ${signal}! Disconnecting producer...`);\n\n try {\n await this.disconnect();\n this.logger.info('Kafka: [graceful-shutdown] producer disconnected successfully');\n } catch (error) {\n this.logger.error('Kafka: [graceful-shutdown] error during shutdown', { error });\n throw error;\n }\n }\n\n get isConnected(): boolean {\n return this._isConnected;\n }\n\n /**\n * Ping Kafka brokers to verify connectivity\n */\n async ping(): Promise<void> {\n if (this._isConnected) {\n return;\n }\n\n try {\n // Fetch metadata to verify connection\n const metadata = await this.producer.metadata({ topics: [] });\n this._isConnected = true;\n\n this.logger.info('Kafka: Successfully connected to cluster', {\n clusterId: metadata.id,\n brokers: metadata.brokers.size,\n });\n } catch (error) {\n this.logger.error('Kafka: Failed to connect to brokers', { error });\n throw new KafkaError(`Failed to connect to Kafka brokers: ${(error as Error).message}`);\n }\n }\n\n /**\n * Publish a single message to a topic\n */\n async publish<T = unknown>(\n topic: string,\n value: T,\n options?: PublishOptions,\n ): Promise<RecordMetadata[]> {\n if (!topic) {\n throw new KafkaError('Topic name is required');\n }\n\n await this.ping();\n\n try {\n const headers: Record<string, string> = {\n [TIMESTAMP_HEADER]: Date.now().toString(),\n ...options?.headers,\n };\n\n const result = await this.producer.send({\n messages: [{\n topic,\n value: JSON.stringify(value),\n key: options?.key,\n partition: options?.partition,\n headers,\n }],\n });\n\n this.logger.debug(`Kafka: Published message to topic ${topic}`, {\n topic,\n });\n\n // Map ProduceResult to RecordMetadata\n return [{\n topic,\n partition: options?.partition ?? 0,\n offset: result?.offsets?.[0]?.offset?.toString() ?? '0',\n }];\n } catch (error) {\n const err = error as { message?: string; };\n this.logger.error(`Kafka: Error publishing to topic ${topic}`, { error, value });\n throw new KafkaError(`Failed to publish to topic ${topic}: ${err.message || 'Unknown error'}`);\n }\n }\n\n /**\n * Publish multiple messages in a batch\n */\n async publishBatch(options: PublishBatchOptions): Promise<RecordMetadata[]> {\n if (!options.topic) {\n throw new KafkaError('Topic name is required');\n }\n\n if (!options.messages || options.messages.length === 0) {\n throw new KafkaError('At least one message is required');\n }\n\n await this.ping();\n\n try {\n const messages = options.messages.map(msg => ({\n topic: options.topic,\n value: JSON.stringify(msg.value),\n key: msg.key,\n partition: msg.partition,\n headers: {\n [TIMESTAMP_HEADER]: Date.now().toString(),\n ...msg.headers,\n },\n }));\n\n const result = await this.producer.send({ messages });\n\n this.logger.debug(`Kafka: Published ${messages.length} messages to topic ${options.topic}`, {\n topic: options.topic,\n count: messages.length,\n });\n\n // Map ProduceResult to RecordMetadata array\n return messages.map((msg, idx) => ({\n topic: options.topic,\n partition: msg.partition ?? 0,\n offset: result?.offsets?.[idx]?.offset?.toString() ?? idx.toString(),\n }));\n } catch (error) {\n const err = error as { message?: string; };\n this.logger.error(`Kafka: Error publishing batch to topic ${options.topic}`, { error });\n throw new KafkaError(`Failed to publish batch to topic ${options.topic}: ${err.message || 'Unknown error'}`);\n }\n }\n\n /**\n * Disconnect the producer and clean up resources\n */\n async disconnect(): Promise<void> {\n if (!this._isConnected) {\n this.logger.debug('Kafka: Producer already disconnected');\n return;\n }\n\n this.logger.info('Kafka: Disconnecting producer');\n\n try {\n await this.producer.close();\n this._isConnected = false;\n this.logger.info('Kafka: Producer disconnected successfully');\n } catch (error) {\n this.logger.error('Kafka: Error disconnecting producer', { error });\n throw new KafkaError(`Failed to disconnect producer: ${(error as Error).message}`);\n }\n }\n}\n\nexport default AfKafka;\nexport { KafkaError };\nexport type {\n KafkaOptions,\n PublishOptions,\n PublishBatchOptions,\n RecordMetadata,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["fallbackLogger: LoggerInstanceManager","headers: Record<string, string>","fallbackLogger","status: Record<string, boolean>"],"sources":["../src/logger.ts","../src/kafkaError.ts","../src/consts.ts","../src/manager.ts"],"sourcesContent":["import type { LoggerInstanceManager } from '@autofleet/logger';\n\n/* eslint-disable no-console */\nconst fallbackLogger: LoggerInstanceManager = {\n info: (...args: unknown[]) => console.log('[INFO]', ...args),\n error: (...args: unknown[]) => console.error('[ERROR]', ...args),\n warn: (...args: unknown[]) => console.warn('[WARN]', ...args),\n debug: (...args: unknown[]) => console.debug('[DEBUG]', ...args),\n} as LoggerInstanceManager;\n/* eslint-enable no-console */\n\nexport default fallbackLogger;\n","export default class KafkaError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KafkaError';\n }\n}\n","export const TIMESTAMP_HEADER = 'x-timestamp';\nexport const CORRELATION_ID_HEADER = 'x-correlation-id';\nexport const SOURCE_HEADER = 'x-source';\n\nexport const DEFAULT_CLIENT_ID = 'autofleet-kafka-producer';\nexport const DEFAULT_TIMEOUT = 30000; // 30 seconds\nexport const DEFAULT_RETRY = {\n retries: 5,\n initialRetryTime: 300,\n maxRetryTime: 30000,\n};\n","import type { LoggerInstanceManager } from '@autofleet/logger';\nimport type { Producer } from '@platformatic/kafka/dist/clients/producer/index.ts';\nimport fallbackLogger from './logger';\nimport KafkaError from './kafkaError';\nimport {\n DEFAULT_CLIENT_ID,\n TIMESTAMP_HEADER,\n} from './consts';\nimport type {\n KafkaManagerOptions,\n ProducerConfig,\n PublishOptions,\n PublishBatchOptions,\n RecordMetadata,\n} from './types';\n\n/**\n * Wrapper for a single Kafka producer instance with lazy initialization\n */\nclass ProducerWrapper {\n private producer: Producer<string, string, string, string> | null = null;\n private readonly logger: LoggerInstanceManager;\n private readonly name: string;\n private readonly config: ProducerConfig;\n private _isConnected = false;\n private initPromise: Promise<void> | null = null;\n\n constructor(\n name: string,\n config: ProducerConfig,\n logger: LoggerInstanceManager,\n ) {\n this.name = name;\n this.config = config;\n this.logger = logger;\n }\n\n private async initialize(): Promise<void> {\n if (this.producer) {\n return;\n }\n\n if (this.initPromise) {\n await this.initPromise;\n return;\n }\n\n this.initPromise = (async (): Promise<void> => {\n const { Producer, stringSerializers } = await import('@platformatic/kafka');\n\n this.producer = new Producer({\n bootstrapBrokers: this.config.brokers,\n clientId: this.config.clientId || `${DEFAULT_CLIENT_ID}-${this.name}`,\n serializers: stringSerializers,\n autocreateTopics: this.config.autoCreateTopics ?? false,\n sasl: this.config.sasl,\n });\n\n this.logger.info(`Kafka: [${this.name}] Initialized producer`, {\n clientId: this.config.clientId || `${DEFAULT_CLIENT_ID}-${this.name}`,\n brokers: this.config.brokers,\n });\n })();\n\n await this.initPromise;\n }\n\n get isConnected(): boolean {\n return this._isConnected;\n }\n\n async ping(): Promise<void> {\n if (this._isConnected) {\n return;\n }\n\n await this.initialize();\n\n try {\n const metadata = await this.producer!.metadata({ topics: [] });\n this._isConnected = true;\n\n this.logger.info(`Kafka: [${this.name}] Successfully connected to cluster`, {\n clusterId: metadata.id,\n brokers: metadata.brokers.size,\n });\n } catch (error) {\n this.logger.error(`Kafka: [${this.name}] Failed to connect to brokers`, { error });\n throw new KafkaError(`[${this.name}] Failed to connect to Kafka brokers: ${(error as Error).message}`);\n }\n }\n\n async publish<T = unknown>(\n topic: string,\n value: T,\n options?: PublishOptions,\n ): Promise<RecordMetadata[]> {\n if (!topic) {\n throw new KafkaError(`[${this.name}] Topic name is required`);\n }\n\n await this.ping();\n\n try {\n const headers: Record<string, string> = {\n [TIMESTAMP_HEADER]: Date.now().toString(),\n ...options?.headers,\n };\n\n const result = await this.producer!.send({\n messages: [{\n topic,\n value: JSON.stringify(value),\n key: options?.key,\n partition: options?.partition,\n headers,\n }],\n });\n\n this.logger.debug(`Kafka: [${this.name}] Published message to topic ${topic}`, {\n topic,\n });\n\n return [{\n topic,\n partition: options?.partition ?? 0,\n offset: result?.offsets?.[0]?.offset?.toString() ?? '0',\n }];\n } catch (error) {\n const err = error as { message?: string; };\n this.logger.error(`Kafka: [${this.name}] Error publishing to topic ${topic}`, { error, value });\n throw new KafkaError(`[${this.name}] Failed to publish to topic ${topic}: ${err.message || 'Unknown error'}`);\n }\n }\n\n async publishBatch(options: PublishBatchOptions): Promise<RecordMetadata[]> {\n if (!options.topic) {\n throw new KafkaError(`[${this.name}] Topic name is required`);\n }\n\n if (!options.messages || options.messages.length === 0) {\n throw new KafkaError(`[${this.name}] At least one message is required`);\n }\n\n await this.ping();\n\n try {\n const messages = options.messages.map(msg => ({\n topic: options.topic,\n value: JSON.stringify(msg.value),\n key: msg.key,\n partition: msg.partition,\n headers: {\n [TIMESTAMP_HEADER]: Date.now().toString(),\n ...msg.headers,\n },\n }));\n\n const result = await this.producer!.send({ messages });\n\n this.logger.debug(`Kafka: [${this.name}] Published ${messages.length} messages to topic ${options.topic}`, {\n topic: options.topic,\n count: messages.length,\n });\n\n return messages.map((msg, idx) => ({\n topic: options.topic,\n partition: msg.partition ?? 0,\n offset: result?.offsets?.[idx]?.offset?.toString() ?? idx.toString(),\n }));\n } catch (error) {\n const err = error as { message?: string; };\n this.logger.error(`Kafka: [${this.name}] Error publishing batch to topic ${options.topic}`, { error });\n throw new KafkaError(`[${this.name}] Failed to publish batch to topic ${options.topic}: ${err.message || 'Unknown error'}`);\n }\n }\n\n async disconnect(): Promise<void> {\n if (!this.producer || !this._isConnected) {\n this.logger.debug(`Kafka: [${this.name}] Producer already disconnected`);\n return;\n }\n\n this.logger.info(`Kafka: [${this.name}] Disconnecting producer`);\n\n try {\n await this.producer.close();\n this._isConnected = false;\n this.logger.info(`Kafka: [${this.name}] Producer disconnected successfully`);\n } catch (error) {\n this.logger.error(`Kafka: [${this.name}] Error disconnecting producer`, { error });\n throw new KafkaError(`[${this.name}] Failed to disconnect producer: ${(error as Error).message}`);\n }\n }\n}\n\n/**\n * Mock producer for when Kafka is disabled\n */\nclass MockProducer {\n private readonly logger: LoggerInstanceManager;\n private readonly name: string;\n\n constructor(name: string, logger: LoggerInstanceManager) {\n this.name = name;\n this.logger = logger;\n }\n\n readonly isConnected = true;\n\n async ping(): Promise<void> {\n this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping ping`);\n return Promise.resolve();\n }\n\n async publish<T = unknown>(topic: string, value: T, _options?: PublishOptions): Promise<RecordMetadata[]> {\n this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping publish to topic: ${topic}`, { value });\n return Promise.resolve([{\n topic,\n partition: 0,\n offset: '0',\n }]);\n }\n\n async publishBatch(options: PublishBatchOptions): Promise<RecordMetadata[]> {\n this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping batch publish to topic: ${options.topic}`, {\n messageCount: options.messages.length,\n });\n return Promise.resolve(options.messages.map(() => ({\n topic: options.topic,\n partition: 0,\n offset: '0',\n })));\n }\n\n async disconnect(): Promise<void> {\n this.logger.debug(`Kafka: [${this.name}] Mock mode - skipping disconnect`);\n return Promise.resolve();\n }\n}\n\n/**\n * Manager for multiple Kafka producers\n */\nexport class KafkaManager<TProducers extends string = string> {\n private readonly producers = new Map<string, ProducerWrapper | MockProducer>();\n private readonly logger: LoggerInstanceManager;\n private readonly enabled: boolean;\n private gracefulShutdownStarted = false;\n\n private constructor(options: KafkaManagerOptions) {\n this.logger = options.logger ?? fallbackLogger;\n this.enabled = options.enabled ?? true;\n\n this.logger.info('Kafka: Initialized KafkaManager', {\n enabled: this.enabled,\n producerCount: Object.keys(options.producers).length,\n producers: Object.keys(options.producers),\n });\n\n // Create producers\n if (!this.enabled) {\n // Create mock producers\n for (const [name, _config] of Object.entries(options.producers)) {\n this.producers.set(name, new MockProducer(name, this.logger));\n }\n this.logger.info('Kafka: Created mock producers (Kafka disabled)');\n } else {\n // Create real producers (they will initialize lazily)\n for (const [name, config] of Object.entries(options.producers)) {\n if (!config.brokers || config.brokers.length === 0) {\n throw new KafkaError(`[${name}] At least one broker is required`);\n }\n\n this.producers.set(name, new ProducerWrapper(name, config, this.logger));\n }\n }\n\n if (!options.dontGracefulShutdown) {\n this.setupGracefulShutdown();\n }\n }\n\n /**\n * Create a new KafkaManager instance with multiple named producers\n * Producers are initialized lazily on first use\n * Producer names are type-safe based on the configuration\n */\n static create<T extends Record<string, ProducerConfig>>(\n options: Omit<KafkaManagerOptions, 'producers'> & { producers: T; },\n ): KafkaManager<keyof T & string> {\n return new KafkaManager(options);\n }\n\n private setupGracefulShutdown(): void {\n this.logger.info(`Kafka: [graceful-shutdown] adding graceful shutdown for process.pid ${process.pid}`);\n\n process.on('SIGTERM', async () => {\n await this.gracefulShutdown('SIGTERM');\n });\n\n process.on('SIGINT', async () => {\n await this.gracefulShutdown('SIGINT');\n });\n }\n\n private async gracefulShutdown(signal: string): Promise<void> {\n if (this.gracefulShutdownStarted) {\n return;\n }\n\n this.gracefulShutdownStarted = true;\n\n this.logger.info(`Kafka: [graceful-shutdown] received ${signal}! Disconnecting all producers...`);\n\n try {\n await this.disconnect();\n this.logger.info('Kafka: [graceful-shutdown] all producers disconnected successfully');\n } catch (error) {\n this.logger.error('Kafka: [graceful-shutdown] error during shutdown', { error });\n throw error;\n }\n }\n\n /**\n * Check if Kafka is enabled\n */\n get isEnabled(): boolean {\n return this.enabled;\n }\n\n /**\n * Get all producer names\n */\n get producerNames(): TProducers[] {\n return Array.from(this.producers.keys()) as TProducers[];\n }\n\n /**\n * Check if a producer exists\n */\n hasProducer(name: TProducers): boolean {\n return this.producers.has(name);\n }\n\n /**\n * Get a specific producer\n */\n private getProducer(name: TProducers): ProducerWrapper | MockProducer {\n const producer = this.producers.get(name);\n if (!producer) {\n throw new KafkaError(`Producer '${name}' not found. Available producers: ${this.producerNames.join(', ')}`);\n }\n return producer;\n }\n\n /**\n * Ping all producers to verify connectivity\n */\n async ping(): Promise<void> {\n const promises = Array.from(this.producers.values()).map(p => p.ping());\n await Promise.all(promises);\n }\n\n /**\n * Ping a specific producer\n */\n async pingProducer(name: TProducers): Promise<void> {\n await this.getProducer(name).ping();\n }\n\n /**\n * Check if all producers are connected and ready\n */\n async isReady(): Promise<boolean> {\n if (!this.enabled) {\n return true;\n }\n\n try {\n await this.ping();\n return true;\n } catch {\n return false;\n }\n }\n\n /**\n * Check connection status of all producers\n */\n getConnectionStatus(): Record<TProducers, boolean> {\n const status: Record<string, boolean> = {};\n for (const [name, producer] of this.producers.entries()) {\n status[name] = producer.isConnected;\n }\n return status as Record<TProducers, boolean>;\n }\n\n /**\n * Publish a message using a named producer\n */\n async publish<T = unknown>(\n producerName: TProducers,\n topic: string,\n value: T,\n options?: PublishOptions,\n ): Promise<RecordMetadata[]> {\n return this.getProducer(producerName).publish(topic, value, options);\n }\n\n /**\n * Publish a batch of messages using a named producer\n */\n async publishBatch(\n producerName: TProducers,\n options: PublishBatchOptions,\n ): Promise<RecordMetadata[]> {\n return this.getProducer(producerName).publishBatch(options);\n }\n\n /**\n * Disconnect all producers\n */\n async disconnect(): Promise<void> {\n const promises = Array.from(this.producers.values()).map(p => p.disconnect());\n await Promise.all(promises);\n this.logger.info('Kafka: All producers disconnected');\n }\n\n /**\n * Disconnect a specific producer\n */\n async disconnectProducer(name: TProducers): Promise<void> {\n await this.getProducer(name).disconnect();\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@autofleet/kafka",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Internal wrapper for Apache Kafka producer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -23,11 +23,6 @@
|
|
|
23
23
|
"engines": {
|
|
24
24
|
"node": ">=18.0.0"
|
|
25
25
|
},
|
|
26
|
-
"scripts": {
|
|
27
|
-
"build": "tsdown",
|
|
28
|
-
"test": "vitest",
|
|
29
|
-
"coverage": "vitest --coverage"
|
|
30
|
-
},
|
|
31
26
|
"repository": {
|
|
32
27
|
"type": "git",
|
|
33
28
|
"url": "git+https://github.com/Autofleet/autorepo.git"
|
|
@@ -43,11 +38,19 @@
|
|
|
43
38
|
"@autofleet/logger": "*"
|
|
44
39
|
},
|
|
45
40
|
"devDependencies": {
|
|
46
|
-
"@
|
|
47
|
-
"@
|
|
41
|
+
"@types/node": "^20.14.11",
|
|
42
|
+
"@autofleet/logger": "^4.2.33"
|
|
48
43
|
},
|
|
49
|
-
"keywords": [
|
|
44
|
+
"keywords": [
|
|
45
|
+
"kafka",
|
|
46
|
+
"producer",
|
|
47
|
+
"messaging"
|
|
48
|
+
],
|
|
50
49
|
"author": "",
|
|
51
50
|
"license": "Proprietary",
|
|
52
|
-
"
|
|
53
|
-
|
|
51
|
+
"scripts": {
|
|
52
|
+
"build": "tsdown",
|
|
53
|
+
"test": "vitest",
|
|
54
|
+
"coverage": "vitest --coverage"
|
|
55
|
+
}
|
|
56
|
+
}
|