sentinel-kafka-manager 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +391 -0
- package/dist/index.d.mts +77 -0
- package/dist/index.d.ts +77 -0
- package/dist/index.js +219 -0
- package/dist/index.mjs +194 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
# sentinel-kafka-manager
|
|
2
|
+
|
|
3
|
+
Reusable Kafka manager for Node.js microservices built on top of `kafkajs`.
|
|
4
|
+
|
|
5
|
+
This package helps services:
|
|
6
|
+
|
|
7
|
+
- connect to Kafka with a small shared API
|
|
8
|
+
- create and reuse producers, consumers, and admin clients safely
|
|
9
|
+
- publish JSON messages
|
|
10
|
+
- provision topics if they do not already exist
|
|
11
|
+
- bootstrap from environment variables for both Docker-internal and host-external Kafka access
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install sentinel-kafka-manager
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
For local development in this package:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install
|
|
23
|
+
npm run build
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Features
|
|
27
|
+
|
|
28
|
+
- built on `kafkajs`
|
|
29
|
+
- supports direct broker lists or env-driven broker discovery
|
|
30
|
+
- supports internal Docker-network broker routing
|
|
31
|
+
- supports external localhost or host-based broker routing
|
|
32
|
+
- supports optional SSL, SASL, retry, and timeout settings
|
|
33
|
+
- avoids duplicate producer, consumer, and admin connections during concurrent startup
|
|
34
|
+
|
|
35
|
+
## Exports
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { KafkaManager } from 'sentinel-kafka-manager'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quick Start
|
|
42
|
+
|
|
43
|
+
### 1. Create a manager with explicit brokers
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { KafkaManager } from 'sentinel-kafka-manager'
|
|
47
|
+
|
|
48
|
+
const kafkaManager = new KafkaManager({
|
|
49
|
+
clientId: 'claims-service',
|
|
50
|
+
brokers: ['localhost:29092', 'localhost:39092', 'localhost:49092'],
|
|
51
|
+
})
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 2. Publish a message
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
await kafkaManager.publish({
|
|
58
|
+
topic: 'claims.created',
|
|
59
|
+
key: 'claim-1001',
|
|
60
|
+
payload: {
|
|
61
|
+
claimId: 'claim-1001',
|
|
62
|
+
status: 'created',
|
|
63
|
+
},
|
|
64
|
+
})
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 3. Provision topics
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
await kafkaManager.provisionTopics([
|
|
71
|
+
{
|
|
72
|
+
topic: 'claims.created',
|
|
73
|
+
numPartitions: 6,
|
|
74
|
+
replicationFactor: 3,
|
|
75
|
+
},
|
|
76
|
+
])
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### 4. Reuse a connected consumer
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
const consumer = await kafkaManager.getConsumer('claims-service-group')
|
|
83
|
+
|
|
84
|
+
await consumer.subscribe({
|
|
85
|
+
topic: 'claims.created',
|
|
86
|
+
fromBeginning: false,
|
|
87
|
+
})
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### 5. Disconnect on shutdown
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
await kafkaManager.disconnect()
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Environment-Based Setup
|
|
97
|
+
|
|
98
|
+
`KafkaManager.fromEnv()` supports two styles:
|
|
99
|
+
|
|
100
|
+
1. explicit broker list with `KAFKA_BROKERS`
|
|
101
|
+
2. derived broker list from your Kafka runtime env naming
|
|
102
|
+
|
|
103
|
+
### Option A: Explicit broker list
|
|
104
|
+
|
|
105
|
+
```env
|
|
106
|
+
KAFKA_BROKERS=localhost:29092,localhost:39092,localhost:49092
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { KafkaManager } from 'sentinel-kafka-manager'
|
|
111
|
+
|
|
112
|
+
const kafkaManager = KafkaManager.fromEnv({
|
|
113
|
+
clientId: 'claims-service',
|
|
114
|
+
})
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Option B: Derived from your current Kafka cluster env
|
|
118
|
+
|
|
119
|
+
This package supports the env structure you shared for your KRaft cluster.
|
|
120
|
+
|
|
121
|
+
#### External mode
|
|
122
|
+
|
|
123
|
+
Use this when your app runs on the host machine or outside the Kafka Docker network.
|
|
124
|
+
|
|
125
|
+
```env
|
|
126
|
+
KAFKA_EXTERNAL_HOST=localhost
|
|
127
|
+
KAFKA_BROKER_1_EXTERNAL_PORT=29092
|
|
128
|
+
KAFKA_BROKER_2_EXTERNAL_PORT=39092
|
|
129
|
+
KAFKA_BROKER_3_EXTERNAL_PORT=49092
|
|
130
|
+
KAFKA_BROKER_INTERNAL_PORT=9092
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
const kafkaManager = KafkaManager.fromEnv({
|
|
135
|
+
clientId: 'claims-service',
|
|
136
|
+
mode: 'external',
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
This resolves to:
|
|
141
|
+
|
|
142
|
+
```text
|
|
143
|
+
localhost:29092
|
|
144
|
+
localhost:39092
|
|
145
|
+
localhost:49092
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
#### Internal mode
|
|
149
|
+
|
|
150
|
+
Use this when your app runs inside Docker on the same Kafka network.
|
|
151
|
+
|
|
152
|
+
```env
|
|
153
|
+
KAFKA_BROKER_INTERNAL_PORT=9092
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
const kafkaManager = KafkaManager.fromEnv({
|
|
158
|
+
clientId: 'claims-service',
|
|
159
|
+
mode: 'internal',
|
|
160
|
+
})
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
This resolves to:
|
|
164
|
+
|
|
165
|
+
```text
|
|
166
|
+
broker-1:9092
|
|
167
|
+
broker-2:9092
|
|
168
|
+
broker-3:9092
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Supported Environment Variables
|
|
172
|
+
|
|
173
|
+
### Minimal variables
|
|
174
|
+
|
|
175
|
+
- `KAFKA_BROKERS`
|
|
176
|
+
- `KAFKA_EXTERNAL_HOST`
|
|
177
|
+
- `KAFKA_BROKER_1_EXTERNAL_PORT`
|
|
178
|
+
- `KAFKA_BROKER_2_EXTERNAL_PORT`
|
|
179
|
+
- `KAFKA_BROKER_3_EXTERNAL_PORT`
|
|
180
|
+
- `KAFKA_BROKER_INTERNAL_PORT`
|
|
181
|
+
|
|
182
|
+
### `fromEnv()` options
|
|
183
|
+
|
|
184
|
+
You can customize the env lookup behavior:
|
|
185
|
+
|
|
186
|
+
```ts
|
|
187
|
+
const kafkaManager = KafkaManager.fromEnv({
|
|
188
|
+
clientId: 'claims-service',
|
|
189
|
+
mode: 'external',
|
|
190
|
+
brokerCount: 3,
|
|
191
|
+
envKey: 'KAFKA_BROKERS',
|
|
192
|
+
externalHostEnvKey: 'KAFKA_EXTERNAL_HOST',
|
|
193
|
+
internalPortEnvKey: 'KAFKA_BROKER_INTERNAL_PORT',
|
|
194
|
+
externalPortEnvKeyPrefix: 'KAFKA_BROKER_',
|
|
195
|
+
externalPortEnvKeySuffix: '_EXTERNAL_PORT',
|
|
196
|
+
internalBrokerHostPrefix: 'broker-',
|
|
197
|
+
})
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Advanced Connection Options
|
|
201
|
+
|
|
202
|
+
### SSL
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
const kafkaManager = new KafkaManager({
|
|
206
|
+
clientId: 'claims-service',
|
|
207
|
+
brokers: ['localhost:29092'],
|
|
208
|
+
ssl: {
|
|
209
|
+
rejectUnauthorized: true,
|
|
210
|
+
},
|
|
211
|
+
})
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### SASL
|
|
215
|
+
|
|
216
|
+
```ts
|
|
217
|
+
const kafkaManager = new KafkaManager({
|
|
218
|
+
clientId: 'claims-service',
|
|
219
|
+
brokers: ['localhost:29092'],
|
|
220
|
+
sasl: {
|
|
221
|
+
mechanism: 'plain',
|
|
222
|
+
username: 'kafka-user',
|
|
223
|
+
password: 'secret',
|
|
224
|
+
},
|
|
225
|
+
})
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### Retry and timeout tuning
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
const kafkaManager = new KafkaManager({
|
|
232
|
+
clientId: 'claims-service',
|
|
233
|
+
brokers: ['localhost:29092'],
|
|
234
|
+
connectionTimeout: 5000,
|
|
235
|
+
requestTimeout: 30000,
|
|
236
|
+
retry: {
|
|
237
|
+
initialRetryTime: 300,
|
|
238
|
+
retries: 10,
|
|
239
|
+
},
|
|
240
|
+
})
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
## API
|
|
244
|
+
|
|
245
|
+
### `new KafkaManager(options)`
|
|
246
|
+
|
|
247
|
+
Creates a manager from an explicit broker list.
|
|
248
|
+
|
|
249
|
+
Options:
|
|
250
|
+
|
|
251
|
+
- `clientId: string`
|
|
252
|
+
- `brokers: string[]`
|
|
253
|
+
- `ssl?: boolean | { rejectUnauthorized?: boolean }`
|
|
254
|
+
- `sasl?: { mechanism, username, password }`
|
|
255
|
+
- `connectionTimeout?: number`
|
|
256
|
+
- `requestTimeout?: number`
|
|
257
|
+
- `retry?: { initialRetryTime?: number; retries?: number }`
|
|
258
|
+
|
|
259
|
+
### `KafkaManager.fromEnv(options)`
|
|
260
|
+
|
|
261
|
+
Creates a manager by reading broker information from environment variables.
|
|
262
|
+
|
|
263
|
+
Options:
|
|
264
|
+
|
|
265
|
+
- `clientId: string`
|
|
266
|
+
- `envKey?: string`
|
|
267
|
+
- `mode?: 'internal' | 'external'`
|
|
268
|
+
- `brokerCount?: number`
|
|
269
|
+
- `externalHostEnvKey?: string`
|
|
270
|
+
- `internalPortEnvKey?: string`
|
|
271
|
+
- `externalPortEnvKeyPrefix?: string`
|
|
272
|
+
- `externalPortEnvKeySuffix?: string`
|
|
273
|
+
- `internalBrokerHostPrefix?: string`
|
|
274
|
+
- `ssl?: boolean | { rejectUnauthorized?: boolean }`
|
|
275
|
+
- `sasl?: { mechanism, username, password }`
|
|
276
|
+
- `connectionTimeout?: number`
|
|
277
|
+
- `requestTimeout?: number`
|
|
278
|
+
- `retry?: { initialRetryTime?: number; retries?: number }`
|
|
279
|
+
|
|
280
|
+
### `getProducer()`
|
|
281
|
+
|
|
282
|
+
Returns a shared connected Kafka producer.
|
|
283
|
+
|
|
284
|
+
### `getConsumer(groupId)`
|
|
285
|
+
|
|
286
|
+
Returns a shared connected Kafka consumer for the provided group id.
|
|
287
|
+
|
|
288
|
+
### `provisionTopics(topics)`
|
|
289
|
+
|
|
290
|
+
Creates missing topics using the Kafka admin client.
|
|
291
|
+
|
|
292
|
+
Topic shape:
|
|
293
|
+
|
|
294
|
+
```ts
|
|
295
|
+
{
|
|
296
|
+
topic: string
|
|
297
|
+
numPartitions?: number
|
|
298
|
+
replicationFactor?: number
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### `publish(options)`
|
|
303
|
+
|
|
304
|
+
Publishes one JSON-encoded message.
|
|
305
|
+
|
|
306
|
+
Message shape:
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
{
|
|
310
|
+
topic: string
|
|
311
|
+
payload: T
|
|
312
|
+
key?: string
|
|
313
|
+
headers?: IHeaders
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### `disconnect()`
|
|
318
|
+
|
|
319
|
+
Disconnects the managed producer, admin client, and any connected consumers.
|
|
320
|
+
|
|
321
|
+
## Example With Your Kafka Cluster
|
|
322
|
+
|
|
323
|
+
### Host-based app
|
|
324
|
+
|
|
325
|
+
```ts
|
|
326
|
+
import { KafkaManager } from 'sentinel-kafka-manager'
|
|
327
|
+
|
|
328
|
+
const kafkaManager = KafkaManager.fromEnv({
|
|
329
|
+
clientId: 'claims-api',
|
|
330
|
+
mode: 'external',
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
await kafkaManager.publish({
|
|
334
|
+
topic: 'claims.created',
|
|
335
|
+
key: 'claim-1001',
|
|
336
|
+
payload: {
|
|
337
|
+
claimId: 'claim-1001',
|
|
338
|
+
source: 'claims-api',
|
|
339
|
+
},
|
|
340
|
+
})
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### Dockerized app on the same network
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
import { KafkaManager } from 'sentinel-kafka-manager'
|
|
347
|
+
|
|
348
|
+
const kafkaManager = KafkaManager.fromEnv({
|
|
349
|
+
clientId: 'claims-worker',
|
|
350
|
+
mode: 'internal',
|
|
351
|
+
})
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## Publishing
|
|
355
|
+
|
|
356
|
+
Build before publishing:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
npm run build
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
Publish the package:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
npm publish
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
This package already sets `"publishConfig": { "access": "public" }`, so you do not need to pass `--access public` every time.
|
|
369
|
+
|
|
370
|
+
### Publish Troubleshooting
|
|
371
|
+
|
|
372
|
+
#### `403` with 2FA or token message
|
|
373
|
+
|
|
374
|
+
If npm says two-factor authentication or a granular token with bypass 2FA is required, authenticate with an account that can publish or use a publish-capable npm token.
|
|
375
|
+
|
|
376
|
+
Useful checks:
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
npm whoami
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
#### `404` or package name unavailable
|
|
383
|
+
|
|
384
|
+
If npm says the package could not be published, confirm that the package name is available to your npm account and not already owned by someone else.
|
|
385
|
+
|
|
386
|
+
## Notes
|
|
387
|
+
|
|
388
|
+
- connect clients only to brokers, not KRaft controller nodes
|
|
389
|
+
- for your current cluster, PLAINTEXT is enough and SSL/SASL is optional
|
|
390
|
+
- if your services use `localhost`, use `mode: 'external'`
|
|
391
|
+
- if your services run in Docker on the Kafka network, use `mode: 'internal'`
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SASLOptions, Consumer, IHeaders, Producer } from 'kafkajs';
|
|
2
|
+
|
|
3
|
+
type KafkaConnectionMode = 'internal' | 'external';
|
|
4
|
+
interface KafkaRetryOptions {
|
|
5
|
+
initialRetryTime?: number;
|
|
6
|
+
retries?: number;
|
|
7
|
+
}
|
|
8
|
+
interface KafkaSslOptions {
|
|
9
|
+
rejectUnauthorized?: boolean;
|
|
10
|
+
}
|
|
11
|
+
type KafkaSaslOptions = SASLOptions;
|
|
12
|
+
interface KafkaTopicConfig {
|
|
13
|
+
topic: string;
|
|
14
|
+
numPartitions?: number;
|
|
15
|
+
replicationFactor?: number;
|
|
16
|
+
}
|
|
17
|
+
interface PublishMessageOptions<T> {
|
|
18
|
+
topic: string;
|
|
19
|
+
payload: T;
|
|
20
|
+
key?: string;
|
|
21
|
+
headers?: IHeaders;
|
|
22
|
+
}
|
|
23
|
+
interface KafkaManagerOptions {
|
|
24
|
+
clientId: string;
|
|
25
|
+
brokers: string[];
|
|
26
|
+
ssl?: boolean | KafkaSslOptions;
|
|
27
|
+
sasl?: KafkaSaslOptions;
|
|
28
|
+
connectionTimeout?: number;
|
|
29
|
+
requestTimeout?: number;
|
|
30
|
+
retry?: KafkaRetryOptions;
|
|
31
|
+
}
|
|
32
|
+
interface KafkaConnectionOptions {
|
|
33
|
+
clientId: string;
|
|
34
|
+
envKey?: string;
|
|
35
|
+
mode?: KafkaConnectionMode;
|
|
36
|
+
brokerCount?: number;
|
|
37
|
+
externalHostEnvKey?: string;
|
|
38
|
+
internalPortEnvKey?: string;
|
|
39
|
+
externalPortEnvKeyPrefix?: string;
|
|
40
|
+
externalPortEnvKeySuffix?: string;
|
|
41
|
+
internalBrokerHostPrefix?: string;
|
|
42
|
+
ssl?: boolean | KafkaSslOptions;
|
|
43
|
+
sasl?: KafkaSaslOptions;
|
|
44
|
+
connectionTimeout?: number;
|
|
45
|
+
requestTimeout?: number;
|
|
46
|
+
retry?: KafkaRetryOptions;
|
|
47
|
+
}
|
|
48
|
+
interface KafkaConsumers {
|
|
49
|
+
[groupId: string]: Consumer;
|
|
50
|
+
}
|
|
51
|
+
type KafkaProducer = Producer;
|
|
52
|
+
type KafkaConsumer = Consumer;
|
|
53
|
+
|
|
54
|
+
declare class KafkaManager {
|
|
55
|
+
private kafka;
|
|
56
|
+
private producer;
|
|
57
|
+
private producerPromise;
|
|
58
|
+
private admin;
|
|
59
|
+
private adminPromise;
|
|
60
|
+
private consumers;
|
|
61
|
+
private consumerPromises;
|
|
62
|
+
constructor(options: KafkaManagerOptions);
|
|
63
|
+
static fromEnv(options: KafkaConnectionOptions): KafkaManager;
|
|
64
|
+
getProducer(): Promise<Producer>;
|
|
65
|
+
getConsumer(groupId: string): Promise<Consumer>;
|
|
66
|
+
provisionTopics(topics: KafkaTopicConfig[]): Promise<void>;
|
|
67
|
+
publish<T>(options: PublishMessageOptions<T>): Promise<void>;
|
|
68
|
+
disconnect(): Promise<void>;
|
|
69
|
+
private static parseBrokerList;
|
|
70
|
+
private static buildBrokersFromRuntime;
|
|
71
|
+
private connectProducer;
|
|
72
|
+
private connectConsumer;
|
|
73
|
+
private getAdmin;
|
|
74
|
+
private connectAdmin;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { type KafkaConnectionMode, type KafkaConnectionOptions, type KafkaConsumer, type KafkaConsumers, KafkaManager, type KafkaManagerOptions, type KafkaProducer, type KafkaRetryOptions, type KafkaSaslOptions, type KafkaSslOptions, type KafkaTopicConfig, type PublishMessageOptions };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { SASLOptions, Consumer, IHeaders, Producer } from 'kafkajs';
|
|
2
|
+
|
|
3
|
+
type KafkaConnectionMode = 'internal' | 'external';
|
|
4
|
+
interface KafkaRetryOptions {
|
|
5
|
+
initialRetryTime?: number;
|
|
6
|
+
retries?: number;
|
|
7
|
+
}
|
|
8
|
+
interface KafkaSslOptions {
|
|
9
|
+
rejectUnauthorized?: boolean;
|
|
10
|
+
}
|
|
11
|
+
type KafkaSaslOptions = SASLOptions;
|
|
12
|
+
interface KafkaTopicConfig {
|
|
13
|
+
topic: string;
|
|
14
|
+
numPartitions?: number;
|
|
15
|
+
replicationFactor?: number;
|
|
16
|
+
}
|
|
17
|
+
interface PublishMessageOptions<T> {
|
|
18
|
+
topic: string;
|
|
19
|
+
payload: T;
|
|
20
|
+
key?: string;
|
|
21
|
+
headers?: IHeaders;
|
|
22
|
+
}
|
|
23
|
+
interface KafkaManagerOptions {
|
|
24
|
+
clientId: string;
|
|
25
|
+
brokers: string[];
|
|
26
|
+
ssl?: boolean | KafkaSslOptions;
|
|
27
|
+
sasl?: KafkaSaslOptions;
|
|
28
|
+
connectionTimeout?: number;
|
|
29
|
+
requestTimeout?: number;
|
|
30
|
+
retry?: KafkaRetryOptions;
|
|
31
|
+
}
|
|
32
|
+
interface KafkaConnectionOptions {
|
|
33
|
+
clientId: string;
|
|
34
|
+
envKey?: string;
|
|
35
|
+
mode?: KafkaConnectionMode;
|
|
36
|
+
brokerCount?: number;
|
|
37
|
+
externalHostEnvKey?: string;
|
|
38
|
+
internalPortEnvKey?: string;
|
|
39
|
+
externalPortEnvKeyPrefix?: string;
|
|
40
|
+
externalPortEnvKeySuffix?: string;
|
|
41
|
+
internalBrokerHostPrefix?: string;
|
|
42
|
+
ssl?: boolean | KafkaSslOptions;
|
|
43
|
+
sasl?: KafkaSaslOptions;
|
|
44
|
+
connectionTimeout?: number;
|
|
45
|
+
requestTimeout?: number;
|
|
46
|
+
retry?: KafkaRetryOptions;
|
|
47
|
+
}
|
|
48
|
+
interface KafkaConsumers {
|
|
49
|
+
[groupId: string]: Consumer;
|
|
50
|
+
}
|
|
51
|
+
type KafkaProducer = Producer;
|
|
52
|
+
type KafkaConsumer = Consumer;
|
|
53
|
+
|
|
54
|
+
declare class KafkaManager {
|
|
55
|
+
private kafka;
|
|
56
|
+
private producer;
|
|
57
|
+
private producerPromise;
|
|
58
|
+
private admin;
|
|
59
|
+
private adminPromise;
|
|
60
|
+
private consumers;
|
|
61
|
+
private consumerPromises;
|
|
62
|
+
constructor(options: KafkaManagerOptions);
|
|
63
|
+
static fromEnv(options: KafkaConnectionOptions): KafkaManager;
|
|
64
|
+
getProducer(): Promise<Producer>;
|
|
65
|
+
getConsumer(groupId: string): Promise<Consumer>;
|
|
66
|
+
provisionTopics(topics: KafkaTopicConfig[]): Promise<void>;
|
|
67
|
+
publish<T>(options: PublishMessageOptions<T>): Promise<void>;
|
|
68
|
+
disconnect(): Promise<void>;
|
|
69
|
+
private static parseBrokerList;
|
|
70
|
+
private static buildBrokersFromRuntime;
|
|
71
|
+
private connectProducer;
|
|
72
|
+
private connectConsumer;
|
|
73
|
+
private getAdmin;
|
|
74
|
+
private connectAdmin;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { type KafkaConnectionMode, type KafkaConnectionOptions, type KafkaConsumer, type KafkaConsumers, KafkaManager, type KafkaManagerOptions, type KafkaProducer, type KafkaRetryOptions, type KafkaSaslOptions, type KafkaSslOptions, type KafkaTopicConfig, type PublishMessageOptions };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
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/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
KafkaManager: () => KafkaManager
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/kafka-manager.ts
|
|
28
|
+
var import_kafkajs = require("kafkajs");
|
|
29
|
+
var KafkaManager = class _KafkaManager {
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.producer = null;
|
|
32
|
+
// Share the same in-flight connection across concurrent callers.
|
|
33
|
+
this.producerPromise = null;
|
|
34
|
+
this.admin = null;
|
|
35
|
+
// Admin access is reused so topic provisioning does not race on startup.
|
|
36
|
+
this.adminPromise = null;
|
|
37
|
+
this.consumers = /* @__PURE__ */ new Map();
|
|
38
|
+
// Consumers are cached per group id to avoid duplicate group members.
|
|
39
|
+
this.consumerPromises = /* @__PURE__ */ new Map();
|
|
40
|
+
if (!options.brokers.length) {
|
|
41
|
+
throw new Error("Kafka brokers are required.");
|
|
42
|
+
}
|
|
43
|
+
this.kafka = new import_kafkajs.Kafka({
|
|
44
|
+
clientId: options.clientId,
|
|
45
|
+
brokers: options.brokers,
|
|
46
|
+
ssl: options.ssl,
|
|
47
|
+
sasl: options.sasl,
|
|
48
|
+
connectionTimeout: options.connectionTimeout,
|
|
49
|
+
requestTimeout: options.requestTimeout,
|
|
50
|
+
retry: options.retry ?? {
|
|
51
|
+
initialRetryTime: 300,
|
|
52
|
+
retries: 10
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
static fromEnv(options) {
|
|
57
|
+
const brokerString = options.envKey ? process.env[options.envKey] : process.env.KAFKA_BROKERS;
|
|
58
|
+
const brokers = brokerString ? _KafkaManager.parseBrokerList(brokerString) : _KafkaManager.buildBrokersFromRuntime(options);
|
|
59
|
+
if (!brokers.length) {
|
|
60
|
+
throw new Error("Kafka brokers are not configured.");
|
|
61
|
+
}
|
|
62
|
+
return new _KafkaManager({
|
|
63
|
+
clientId: options.clientId,
|
|
64
|
+
brokers,
|
|
65
|
+
ssl: options.ssl,
|
|
66
|
+
sasl: options.sasl,
|
|
67
|
+
connectionTimeout: options.connectionTimeout,
|
|
68
|
+
requestTimeout: options.requestTimeout,
|
|
69
|
+
retry: options.retry
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async getProducer() {
|
|
73
|
+
if (this.producer) {
|
|
74
|
+
return this.producer;
|
|
75
|
+
}
|
|
76
|
+
if (!this.producerPromise) {
|
|
77
|
+
this.producerPromise = this.connectProducer();
|
|
78
|
+
}
|
|
79
|
+
return this.producerPromise;
|
|
80
|
+
}
|
|
81
|
+
async getConsumer(groupId) {
|
|
82
|
+
const existingConsumer = this.consumers.get(groupId);
|
|
83
|
+
if (existingConsumer) {
|
|
84
|
+
return existingConsumer;
|
|
85
|
+
}
|
|
86
|
+
const existingPromise = this.consumerPromises.get(groupId);
|
|
87
|
+
if (existingPromise) {
|
|
88
|
+
return existingPromise;
|
|
89
|
+
}
|
|
90
|
+
const consumerPromise = this.connectConsumer(groupId);
|
|
91
|
+
this.consumerPromises.set(groupId, consumerPromise);
|
|
92
|
+
return consumerPromise;
|
|
93
|
+
}
|
|
94
|
+
async provisionTopics(topics) {
|
|
95
|
+
const admin = await this.getAdmin();
|
|
96
|
+
const existingTopics = await admin.listTopics();
|
|
97
|
+
const topicsToCreate = topics.filter((topic) => !existingTopics.includes(topic.topic)).map((topic) => ({
|
|
98
|
+
topic: topic.topic,
|
|
99
|
+
numPartitions: topic.numPartitions ?? 3,
|
|
100
|
+
replicationFactor: topic.replicationFactor ?? 1
|
|
101
|
+
}));
|
|
102
|
+
if (!topicsToCreate.length) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
await admin.createTopics({
|
|
106
|
+
topics: topicsToCreate
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
async publish(options) {
|
|
110
|
+
const producer = await this.getProducer();
|
|
111
|
+
await producer.send({
|
|
112
|
+
topic: options.topic,
|
|
113
|
+
messages: [
|
|
114
|
+
{
|
|
115
|
+
key: options.key,
|
|
116
|
+
value: JSON.stringify(options.payload),
|
|
117
|
+
headers: options.headers
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async disconnect() {
|
|
123
|
+
this.producerPromise = null;
|
|
124
|
+
if (this.producer) {
|
|
125
|
+
await this.producer.disconnect();
|
|
126
|
+
this.producer = null;
|
|
127
|
+
}
|
|
128
|
+
this.consumerPromises.clear();
|
|
129
|
+
for (const consumer of this.consumers.values()) {
|
|
130
|
+
await consumer.disconnect();
|
|
131
|
+
}
|
|
132
|
+
this.consumers.clear();
|
|
133
|
+
this.adminPromise = null;
|
|
134
|
+
if (this.admin) {
|
|
135
|
+
await this.admin.disconnect().catch(() => void 0);
|
|
136
|
+
this.admin = null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
static parseBrokerList(brokerString) {
|
|
140
|
+
return brokerString.split(",").map((broker) => broker.trim()).filter(Boolean);
|
|
141
|
+
}
|
|
142
|
+
static buildBrokersFromRuntime(options) {
|
|
143
|
+
const mode = options.mode ?? "external";
|
|
144
|
+
const brokerCount = options.brokerCount ?? 3;
|
|
145
|
+
if (mode === "internal") {
|
|
146
|
+
const internalPortEnvKey = options.internalPortEnvKey ?? "KAFKA_BROKER_INTERNAL_PORT";
|
|
147
|
+
const internalPort = process.env[internalPortEnvKey];
|
|
148
|
+
const hostPrefix = options.internalBrokerHostPrefix ?? "broker-";
|
|
149
|
+
if (!internalPort) {
|
|
150
|
+
throw new Error(`${internalPortEnvKey} is not configured.`);
|
|
151
|
+
}
|
|
152
|
+
return Array.from({ length: brokerCount }, (_, index) => `${hostPrefix}${index + 1}:${internalPort}`);
|
|
153
|
+
}
|
|
154
|
+
const externalHostEnvKey = options.externalHostEnvKey ?? "KAFKA_EXTERNAL_HOST";
|
|
155
|
+
const externalHost = process.env[externalHostEnvKey];
|
|
156
|
+
const externalPortEnvKeyPrefix = options.externalPortEnvKeyPrefix ?? "KAFKA_BROKER_";
|
|
157
|
+
const externalPortEnvKeySuffix = options.externalPortEnvKeySuffix ?? "_EXTERNAL_PORT";
|
|
158
|
+
if (!externalHost) {
|
|
159
|
+
throw new Error(`${externalHostEnvKey} is not configured.`);
|
|
160
|
+
}
|
|
161
|
+
return Array.from({ length: brokerCount }, (_, index) => {
|
|
162
|
+
const brokerNumber = index + 1;
|
|
163
|
+
const portEnvKey = `${externalPortEnvKeyPrefix}${brokerNumber}${externalPortEnvKeySuffix}`;
|
|
164
|
+
const port = process.env[portEnvKey];
|
|
165
|
+
if (!port) {
|
|
166
|
+
throw new Error(`${portEnvKey} is not configured.`);
|
|
167
|
+
}
|
|
168
|
+
return `${externalHost}:${port}`;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
async connectProducer() {
|
|
172
|
+
const producer = this.kafka.producer();
|
|
173
|
+
try {
|
|
174
|
+
await producer.connect();
|
|
175
|
+
this.producer = producer;
|
|
176
|
+
return producer;
|
|
177
|
+
} catch (error) {
|
|
178
|
+
this.producerPromise = null;
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
async connectConsumer(groupId) {
|
|
183
|
+
const consumer = this.kafka.consumer({
|
|
184
|
+
groupId
|
|
185
|
+
});
|
|
186
|
+
try {
|
|
187
|
+
await consumer.connect();
|
|
188
|
+
this.consumers.set(groupId, consumer);
|
|
189
|
+
return consumer;
|
|
190
|
+
} catch (error) {
|
|
191
|
+
this.consumerPromises.delete(groupId);
|
|
192
|
+
throw error;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async getAdmin() {
|
|
196
|
+
if (this.admin) {
|
|
197
|
+
return this.admin;
|
|
198
|
+
}
|
|
199
|
+
if (!this.adminPromise) {
|
|
200
|
+
this.adminPromise = this.connectAdmin();
|
|
201
|
+
}
|
|
202
|
+
return this.adminPromise;
|
|
203
|
+
}
|
|
204
|
+
async connectAdmin() {
|
|
205
|
+
const admin = this.kafka.admin();
|
|
206
|
+
try {
|
|
207
|
+
await admin.connect();
|
|
208
|
+
this.admin = admin;
|
|
209
|
+
return admin;
|
|
210
|
+
} catch (error) {
|
|
211
|
+
this.adminPromise = null;
|
|
212
|
+
throw error;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
217
|
+
0 && (module.exports = {
|
|
218
|
+
KafkaManager
|
|
219
|
+
});
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
// src/kafka-manager.ts
|
|
2
|
+
import {
|
|
3
|
+
Kafka
|
|
4
|
+
} from "kafkajs";
|
|
5
|
+
var KafkaManager = class _KafkaManager {
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.producer = null;
|
|
8
|
+
// Share the same in-flight connection across concurrent callers.
|
|
9
|
+
this.producerPromise = null;
|
|
10
|
+
this.admin = null;
|
|
11
|
+
// Admin access is reused so topic provisioning does not race on startup.
|
|
12
|
+
this.adminPromise = null;
|
|
13
|
+
this.consumers = /* @__PURE__ */ new Map();
|
|
14
|
+
// Consumers are cached per group id to avoid duplicate group members.
|
|
15
|
+
this.consumerPromises = /* @__PURE__ */ new Map();
|
|
16
|
+
if (!options.brokers.length) {
|
|
17
|
+
throw new Error("Kafka brokers are required.");
|
|
18
|
+
}
|
|
19
|
+
this.kafka = new Kafka({
|
|
20
|
+
clientId: options.clientId,
|
|
21
|
+
brokers: options.brokers,
|
|
22
|
+
ssl: options.ssl,
|
|
23
|
+
sasl: options.sasl,
|
|
24
|
+
connectionTimeout: options.connectionTimeout,
|
|
25
|
+
requestTimeout: options.requestTimeout,
|
|
26
|
+
retry: options.retry ?? {
|
|
27
|
+
initialRetryTime: 300,
|
|
28
|
+
retries: 10
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
static fromEnv(options) {
|
|
33
|
+
const brokerString = options.envKey ? process.env[options.envKey] : process.env.KAFKA_BROKERS;
|
|
34
|
+
const brokers = brokerString ? _KafkaManager.parseBrokerList(brokerString) : _KafkaManager.buildBrokersFromRuntime(options);
|
|
35
|
+
if (!brokers.length) {
|
|
36
|
+
throw new Error("Kafka brokers are not configured.");
|
|
37
|
+
}
|
|
38
|
+
return new _KafkaManager({
|
|
39
|
+
clientId: options.clientId,
|
|
40
|
+
brokers,
|
|
41
|
+
ssl: options.ssl,
|
|
42
|
+
sasl: options.sasl,
|
|
43
|
+
connectionTimeout: options.connectionTimeout,
|
|
44
|
+
requestTimeout: options.requestTimeout,
|
|
45
|
+
retry: options.retry
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
async getProducer() {
|
|
49
|
+
if (this.producer) {
|
|
50
|
+
return this.producer;
|
|
51
|
+
}
|
|
52
|
+
if (!this.producerPromise) {
|
|
53
|
+
this.producerPromise = this.connectProducer();
|
|
54
|
+
}
|
|
55
|
+
return this.producerPromise;
|
|
56
|
+
}
|
|
57
|
+
async getConsumer(groupId) {
|
|
58
|
+
const existingConsumer = this.consumers.get(groupId);
|
|
59
|
+
if (existingConsumer) {
|
|
60
|
+
return existingConsumer;
|
|
61
|
+
}
|
|
62
|
+
const existingPromise = this.consumerPromises.get(groupId);
|
|
63
|
+
if (existingPromise) {
|
|
64
|
+
return existingPromise;
|
|
65
|
+
}
|
|
66
|
+
const consumerPromise = this.connectConsumer(groupId);
|
|
67
|
+
this.consumerPromises.set(groupId, consumerPromise);
|
|
68
|
+
return consumerPromise;
|
|
69
|
+
}
|
|
70
|
+
async provisionTopics(topics) {
|
|
71
|
+
const admin = await this.getAdmin();
|
|
72
|
+
const existingTopics = await admin.listTopics();
|
|
73
|
+
const topicsToCreate = topics.filter((topic) => !existingTopics.includes(topic.topic)).map((topic) => ({
|
|
74
|
+
topic: topic.topic,
|
|
75
|
+
numPartitions: topic.numPartitions ?? 3,
|
|
76
|
+
replicationFactor: topic.replicationFactor ?? 1
|
|
77
|
+
}));
|
|
78
|
+
if (!topicsToCreate.length) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
await admin.createTopics({
|
|
82
|
+
topics: topicsToCreate
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
async publish(options) {
|
|
86
|
+
const producer = await this.getProducer();
|
|
87
|
+
await producer.send({
|
|
88
|
+
topic: options.topic,
|
|
89
|
+
messages: [
|
|
90
|
+
{
|
|
91
|
+
key: options.key,
|
|
92
|
+
value: JSON.stringify(options.payload),
|
|
93
|
+
headers: options.headers
|
|
94
|
+
}
|
|
95
|
+
]
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async disconnect() {
|
|
99
|
+
this.producerPromise = null;
|
|
100
|
+
if (this.producer) {
|
|
101
|
+
await this.producer.disconnect();
|
|
102
|
+
this.producer = null;
|
|
103
|
+
}
|
|
104
|
+
this.consumerPromises.clear();
|
|
105
|
+
for (const consumer of this.consumers.values()) {
|
|
106
|
+
await consumer.disconnect();
|
|
107
|
+
}
|
|
108
|
+
this.consumers.clear();
|
|
109
|
+
this.adminPromise = null;
|
|
110
|
+
if (this.admin) {
|
|
111
|
+
await this.admin.disconnect().catch(() => void 0);
|
|
112
|
+
this.admin = null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
static parseBrokerList(brokerString) {
|
|
116
|
+
return brokerString.split(",").map((broker) => broker.trim()).filter(Boolean);
|
|
117
|
+
}
|
|
118
|
+
static buildBrokersFromRuntime(options) {
|
|
119
|
+
const mode = options.mode ?? "external";
|
|
120
|
+
const brokerCount = options.brokerCount ?? 3;
|
|
121
|
+
if (mode === "internal") {
|
|
122
|
+
const internalPortEnvKey = options.internalPortEnvKey ?? "KAFKA_BROKER_INTERNAL_PORT";
|
|
123
|
+
const internalPort = process.env[internalPortEnvKey];
|
|
124
|
+
const hostPrefix = options.internalBrokerHostPrefix ?? "broker-";
|
|
125
|
+
if (!internalPort) {
|
|
126
|
+
throw new Error(`${internalPortEnvKey} is not configured.`);
|
|
127
|
+
}
|
|
128
|
+
return Array.from({ length: brokerCount }, (_, index) => `${hostPrefix}${index + 1}:${internalPort}`);
|
|
129
|
+
}
|
|
130
|
+
const externalHostEnvKey = options.externalHostEnvKey ?? "KAFKA_EXTERNAL_HOST";
|
|
131
|
+
const externalHost = process.env[externalHostEnvKey];
|
|
132
|
+
const externalPortEnvKeyPrefix = options.externalPortEnvKeyPrefix ?? "KAFKA_BROKER_";
|
|
133
|
+
const externalPortEnvKeySuffix = options.externalPortEnvKeySuffix ?? "_EXTERNAL_PORT";
|
|
134
|
+
if (!externalHost) {
|
|
135
|
+
throw new Error(`${externalHostEnvKey} is not configured.`);
|
|
136
|
+
}
|
|
137
|
+
return Array.from({ length: brokerCount }, (_, index) => {
|
|
138
|
+
const brokerNumber = index + 1;
|
|
139
|
+
const portEnvKey = `${externalPortEnvKeyPrefix}${brokerNumber}${externalPortEnvKeySuffix}`;
|
|
140
|
+
const port = process.env[portEnvKey];
|
|
141
|
+
if (!port) {
|
|
142
|
+
throw new Error(`${portEnvKey} is not configured.`);
|
|
143
|
+
}
|
|
144
|
+
return `${externalHost}:${port}`;
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
async connectProducer() {
|
|
148
|
+
const producer = this.kafka.producer();
|
|
149
|
+
try {
|
|
150
|
+
await producer.connect();
|
|
151
|
+
this.producer = producer;
|
|
152
|
+
return producer;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
this.producerPromise = null;
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async connectConsumer(groupId) {
|
|
159
|
+
const consumer = this.kafka.consumer({
|
|
160
|
+
groupId
|
|
161
|
+
});
|
|
162
|
+
try {
|
|
163
|
+
await consumer.connect();
|
|
164
|
+
this.consumers.set(groupId, consumer);
|
|
165
|
+
return consumer;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
this.consumerPromises.delete(groupId);
|
|
168
|
+
throw error;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async getAdmin() {
|
|
172
|
+
if (this.admin) {
|
|
173
|
+
return this.admin;
|
|
174
|
+
}
|
|
175
|
+
if (!this.adminPromise) {
|
|
176
|
+
this.adminPromise = this.connectAdmin();
|
|
177
|
+
}
|
|
178
|
+
return this.adminPromise;
|
|
179
|
+
}
|
|
180
|
+
async connectAdmin() {
|
|
181
|
+
const admin = this.kafka.admin();
|
|
182
|
+
try {
|
|
183
|
+
await admin.connect();
|
|
184
|
+
this.admin = admin;
|
|
185
|
+
return admin;
|
|
186
|
+
} catch (error) {
|
|
187
|
+
this.adminPromise = null;
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
export {
|
|
193
|
+
KafkaManager
|
|
194
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "sentinel-kafka-manager",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Reusable Kafka manager for Node.js microservices",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"module": "dist/index.mjs",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"require": "./dist/index.js",
|
|
12
|
+
"import": "./dist/index.mjs"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsup src/index.ts --format cjs,esm --dts",
|
|
23
|
+
"prepublishOnly": "npm run build"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"kafka",
|
|
27
|
+
"kafkajs",
|
|
28
|
+
"nodejs",
|
|
29
|
+
"microservice"
|
|
30
|
+
],
|
|
31
|
+
"author": "Ngen Claims",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"type": "commonjs",
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"kafkajs": "^2.2.4"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^24.0.0",
|
|
39
|
+
"tsup": "^8.0.0",
|
|
40
|
+
"typescript": "^5.0.0"
|
|
41
|
+
}
|
|
42
|
+
}
|