@drarzter/kafka-client 0.5.6 → 0.6.3
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 +176 -25
- package/dist/{chunk-6B72MJPU.mjs → chunk-RGRKN4E5.mjs} +581 -218
- package/dist/chunk-RGRKN4E5.mjs.map +1 -0
- package/dist/core.d.mts +43 -10
- package/dist/core.d.ts +43 -10
- package/dist/core.js +580 -217
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +1 -1
- package/dist/index.d.mts +5 -16
- package/dist/index.d.ts +5 -16
- package/dist/index.js +581 -226
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -10
- package/dist/index.mjs.map +1 -1
- package/dist/otel.d.mts +1 -1
- package/dist/otel.d.ts +1 -1
- package/dist/otel.js +15 -4
- package/dist/otel.js.map +1 -1
- package/dist/otel.mjs +15 -4
- package/dist/otel.mjs.map +1 -1
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +2 -1
- package/dist/testing.js.map +1 -1
- package/dist/testing.mjs +2 -1
- package/dist/testing.mjs.map +1 -1
- package/dist/{envelope-LeO5e3ob.d.mts → types-zFbQH_Cy.d.mts} +160 -88
- package/dist/{envelope-LeO5e3ob.d.ts → types-zFbQH_Cy.d.ts} +160 -88
- package/package.json +1 -1
- package/dist/chunk-6B72MJPU.mjs.map +0 -1
|
@@ -1,15 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context passed as the second argument to `SchemaLike.parse()`.
|
|
3
|
+
* Enables schema-registry adapters, version-aware migration, and
|
|
4
|
+
* header-driven parsing without coupling validators to Kafka internals.
|
|
5
|
+
*
|
|
6
|
+
* All fields are optional-friendly — validators that don't need the context
|
|
7
|
+
* can simply ignore the second argument.
|
|
8
|
+
*/
|
|
9
|
+
interface SchemaParseContext {
|
|
10
|
+
/** Topic the message was produced to / consumed from. */
|
|
11
|
+
topic: string;
|
|
12
|
+
/** Decoded message headers (envelope headers included). */
|
|
13
|
+
headers: MessageHeaders;
|
|
14
|
+
/** Value of the `x-schema-version` header, defaults to `1`. */
|
|
15
|
+
version: number;
|
|
16
|
+
}
|
|
1
17
|
/**
|
|
2
18
|
* Any validation library with a `.parse()` method.
|
|
3
19
|
* Works with Zod, Valibot, ArkType, or any custom validator.
|
|
4
20
|
*
|
|
21
|
+
* The optional `ctx` argument carries topic/header/version metadata so
|
|
22
|
+
* validators can perform schema-registry lookups or version-aware migrations.
|
|
23
|
+
* Existing validators that only use the first argument continue to work
|
|
24
|
+
* unchanged — the second argument is silently ignored.
|
|
25
|
+
*
|
|
5
26
|
* @example
|
|
6
27
|
* ```ts
|
|
7
28
|
* import { z } from 'zod';
|
|
8
29
|
* const schema: SchemaLike<{ id: string }> = z.object({ id: z.string() });
|
|
30
|
+
*
|
|
31
|
+
* // Context-aware validator:
|
|
32
|
+
* const schema: SchemaLike<MyType> = {
|
|
33
|
+
* parse(data, ctx) {
|
|
34
|
+
* const version = ctx?.version ?? 1;
|
|
35
|
+
* return version >= 2 ? migrateV1toV2(data) : validateV1(data);
|
|
36
|
+
* }
|
|
37
|
+
* };
|
|
9
38
|
* ```
|
|
10
39
|
*/
|
|
11
40
|
interface SchemaLike<T = any> {
|
|
12
|
-
parse(data: unknown): T | Promise<T>;
|
|
41
|
+
parse(data: unknown, ctx?: SchemaParseContext): T | Promise<T>;
|
|
13
42
|
}
|
|
14
43
|
/** Infer the output type from a SchemaLike. */
|
|
15
44
|
type InferSchema<S extends SchemaLike> = S extends SchemaLike<infer T> ? T : never;
|
|
@@ -32,8 +61,8 @@ interface TopicDescriptor<N extends string = string, M extends Record<string, an
|
|
|
32
61
|
*
|
|
33
62
|
* @example
|
|
34
63
|
* ```ts
|
|
35
|
-
* // Without schema — type
|
|
36
|
-
* const OrderCreated = topic('order.created')<{ orderId: string; amount: number }>();
|
|
64
|
+
* // Without schema — explicit type via .type<T>():
|
|
65
|
+
* const OrderCreated = topic('order.created').type<{ orderId: string; amount: number }>();
|
|
37
66
|
*
|
|
38
67
|
* // With schema — type inferred from schema:
|
|
39
68
|
* const OrderCreated = topic('order.created').schema(z.object({
|
|
@@ -50,16 +79,17 @@ interface TopicDescriptor<N extends string = string, M extends Record<string, an
|
|
|
50
79
|
* ```
|
|
51
80
|
*/
|
|
52
81
|
declare function topic<N extends string>(name: N): {
|
|
53
|
-
|
|
54
|
-
|
|
82
|
+
/** Provide an explicit message type without a runtime schema. */
|
|
83
|
+
type: <M extends Record<string, any>>() => TopicDescriptor<N, M>;
|
|
84
|
+
schema: <S extends SchemaLike<Record<string, any>>>(schema: S) => TopicDescriptor<N, InferSchema<S>>;
|
|
55
85
|
};
|
|
56
86
|
/**
|
|
57
87
|
* Build a topic-message map type from a union of TopicDescriptors.
|
|
58
88
|
*
|
|
59
89
|
* @example
|
|
60
90
|
* ```ts
|
|
61
|
-
* const OrderCreated = topic('order.created')<{ orderId: string }>();
|
|
62
|
-
* const OrderCompleted = topic('order.completed')<{ completedAt: string }>();
|
|
91
|
+
* const OrderCreated = topic('order.created').type<{ orderId: string }>();
|
|
92
|
+
* const OrderCompleted = topic('order.completed').type<{ completedAt: string }>();
|
|
63
93
|
*
|
|
64
94
|
* type MyTopics = TopicsFrom<typeof OrderCreated | typeof OrderCompleted>;
|
|
65
95
|
* // { 'order.created': { orderId: string }; 'order.completed': { completedAt: string } }
|
|
@@ -69,6 +99,77 @@ type TopicsFrom<D extends TopicDescriptor<any, any>> = {
|
|
|
69
99
|
[K in D as K["__topic"]]: K["__type"];
|
|
70
100
|
};
|
|
71
101
|
|
|
102
|
+
declare const HEADER_EVENT_ID = "x-event-id";
|
|
103
|
+
declare const HEADER_CORRELATION_ID = "x-correlation-id";
|
|
104
|
+
declare const HEADER_TIMESTAMP = "x-timestamp";
|
|
105
|
+
declare const HEADER_SCHEMA_VERSION = "x-schema-version";
|
|
106
|
+
declare const HEADER_TRACEPARENT = "traceparent";
|
|
107
|
+
/**
|
|
108
|
+
* Typed wrapper combining a parsed message payload with Kafka metadata
|
|
109
|
+
* and envelope headers.
|
|
110
|
+
*
|
|
111
|
+
* On **send**, the library auto-generates envelope headers
|
|
112
|
+
* (`x-event-id`, `x-correlation-id`, `x-timestamp`, `x-schema-version`).
|
|
113
|
+
*
|
|
114
|
+
* On **consume**, the library extracts those headers and assembles
|
|
115
|
+
* an `EventEnvelope` that is passed to the handler.
|
|
116
|
+
*/
|
|
117
|
+
interface EventEnvelope<T> {
|
|
118
|
+
/** Deserialized + validated message body. */
|
|
119
|
+
payload: T;
|
|
120
|
+
/** Topic the message was produced to / consumed from. */
|
|
121
|
+
topic: string;
|
|
122
|
+
/** Kafka partition (consume-side only, `-1` on send). */
|
|
123
|
+
partition: number;
|
|
124
|
+
/** Kafka offset (consume-side only, empty string on send). */
|
|
125
|
+
offset: string;
|
|
126
|
+
/** ISO-8601 timestamp set by the producer. */
|
|
127
|
+
timestamp: string;
|
|
128
|
+
/** Unique ID for this event (UUID v4). */
|
|
129
|
+
eventId: string;
|
|
130
|
+
/** Correlation ID — auto-propagated via AsyncLocalStorage. */
|
|
131
|
+
correlationId: string;
|
|
132
|
+
/** Schema version of the payload. */
|
|
133
|
+
schemaVersion: number;
|
|
134
|
+
/** W3C Trace Context `traceparent` header (set by OTel instrumentation). */
|
|
135
|
+
traceparent?: string;
|
|
136
|
+
/** All decoded Kafka headers for extensibility. */
|
|
137
|
+
headers: MessageHeaders;
|
|
138
|
+
}
|
|
139
|
+
interface EnvelopeCtx {
|
|
140
|
+
correlationId: string;
|
|
141
|
+
traceparent?: string;
|
|
142
|
+
}
|
|
143
|
+
/** Read the current envelope context (correlationId / traceparent) from ALS. */
|
|
144
|
+
declare function getEnvelopeContext(): EnvelopeCtx | undefined;
|
|
145
|
+
/** Execute `fn` inside an envelope context so nested sends inherit correlationId. */
|
|
146
|
+
declare function runWithEnvelopeContext<R>(ctx: EnvelopeCtx, fn: () => R): R;
|
|
147
|
+
/** Options accepted by `buildEnvelopeHeaders`. */
|
|
148
|
+
interface EnvelopeHeaderOptions {
|
|
149
|
+
correlationId?: string;
|
|
150
|
+
schemaVersion?: number;
|
|
151
|
+
eventId?: string;
|
|
152
|
+
headers?: MessageHeaders;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Generate envelope headers for the send path.
|
|
156
|
+
*
|
|
157
|
+
* Priority for `correlationId`:
|
|
158
|
+
* explicit option → ALS context → new UUID.
|
|
159
|
+
*/
|
|
160
|
+
declare function buildEnvelopeHeaders(options?: EnvelopeHeaderOptions): MessageHeaders;
|
|
161
|
+
/**
|
|
162
|
+
* Decode kafkajs headers (`Record<string, Buffer | string | undefined>`)
|
|
163
|
+
* into plain `Record<string, string>`.
|
|
164
|
+
*/
|
|
165
|
+
declare function decodeHeaders(raw: Record<string, Buffer | string | (Buffer | string)[] | undefined> | undefined): MessageHeaders;
|
|
166
|
+
/**
|
|
167
|
+
* Build an `EventEnvelope` from a consumed kafkajs message.
|
|
168
|
+
* Tolerates missing envelope headers — generates defaults so messages
|
|
169
|
+
* from non-envelope producers still work.
|
|
170
|
+
*/
|
|
171
|
+
declare function extractEnvelope<T>(payload: T, headers: MessageHeaders, topic: string, partition: number, offset: string): EventEnvelope<T>;
|
|
172
|
+
|
|
72
173
|
/**
|
|
73
174
|
* Mapping of topic names to their message types.
|
|
74
175
|
* Define this interface to get type-safe publish/subscribe across your app.
|
|
@@ -165,6 +266,12 @@ interface ConsumerOptions<T extends TopicMapConstraint<T> = TTopicMessageMap> {
|
|
|
165
266
|
* On exhaustion, messages go to `<topic>.dlq` (if `dlq: true`) or `onMessageLost`.
|
|
166
267
|
*/
|
|
167
268
|
retryTopics?: boolean;
|
|
269
|
+
/**
|
|
270
|
+
* Timeout (ms) for waiting until each retry level consumer receives partition
|
|
271
|
+
* assignments after `startConsumer` connects. Default: `10000`.
|
|
272
|
+
* Increase this when the broker is slow to rebalance.
|
|
273
|
+
*/
|
|
274
|
+
retryTopicAssignmentTimeoutMs?: number;
|
|
168
275
|
/**
|
|
169
276
|
* Log a warning if the message handler has not resolved within this window (ms).
|
|
170
277
|
* The handler is not cancelled — this is a diagnostic aid to surface stuck handlers
|
|
@@ -196,6 +303,21 @@ interface ConsumerInterceptor<T extends TopicMapConstraint<T> = TTopicMessageMap
|
|
|
196
303
|
/** Called when the message handler throws. */
|
|
197
304
|
onError?(envelope: EventEnvelope<T[keyof T]>, error: Error): Promise<void> | void;
|
|
198
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Return value of `KafkaInstrumentation.beforeConsume`.
|
|
308
|
+
*
|
|
309
|
+
* - `() => void` — legacy form: a cleanup function called after the handler.
|
|
310
|
+
* - Object form:
|
|
311
|
+
* - `cleanup?()` — called after the handler (same as the legacy function form).
|
|
312
|
+
* - `wrap?(fn)` — wraps the handler execution; call `fn()` inside the desired
|
|
313
|
+
* async context (e.g. `context.with(spanCtx, fn)` for OpenTelemetry). Multiple
|
|
314
|
+
* wraps from different instrumentations are composed in declaration order,
|
|
315
|
+
* so the first instrumentation's wrap is the outermost.
|
|
316
|
+
*/
|
|
317
|
+
type BeforeConsumeResult = (() => void) | {
|
|
318
|
+
cleanup?(): void;
|
|
319
|
+
wrap?(fn: () => Promise<void>): Promise<void>;
|
|
320
|
+
};
|
|
199
321
|
/**
|
|
200
322
|
* Client-wide instrumentation hooks for both send and consume paths.
|
|
201
323
|
* Use this for cross-cutting concerns like tracing and metrics.
|
|
@@ -207,8 +329,13 @@ interface KafkaInstrumentation {
|
|
|
207
329
|
beforeSend?(topic: string, headers: MessageHeaders): void;
|
|
208
330
|
/** Called after a successful send. */
|
|
209
331
|
afterSend?(topic: string): void;
|
|
210
|
-
/**
|
|
211
|
-
|
|
332
|
+
/**
|
|
333
|
+
* Called before the consumer handler.
|
|
334
|
+
* Return a cleanup function (legacy) or a `BeforeConsumeResult` object with
|
|
335
|
+
* optional `cleanup` and `wrap`. Use `wrap` to run the handler inside a
|
|
336
|
+
* specific async context (e.g. an active OpenTelemetry span).
|
|
337
|
+
*/
|
|
338
|
+
beforeConsume?(envelope: EventEnvelope<any>): BeforeConsumeResult | void;
|
|
212
339
|
/** Called when the consumer handler throws. */
|
|
213
340
|
onConsumeError?(envelope: EventEnvelope<any>, error: Error): void;
|
|
214
341
|
}
|
|
@@ -226,13 +353,19 @@ interface ConsumerHandle {
|
|
|
226
353
|
/** Stop this consumer. Equivalent to calling `client.stopConsumer(groupId)`. */
|
|
227
354
|
stop(): Promise<void>;
|
|
228
355
|
}
|
|
356
|
+
/** Result returned by `KafkaClient.checkStatus()`. */
|
|
357
|
+
type KafkaHealthResult = {
|
|
358
|
+
status: "up";
|
|
359
|
+
clientId: string;
|
|
360
|
+
topics: string[];
|
|
361
|
+
} | {
|
|
362
|
+
status: "down";
|
|
363
|
+
clientId: string;
|
|
364
|
+
error: string;
|
|
365
|
+
};
|
|
229
366
|
/** Interface describing all public methods of the Kafka client. */
|
|
230
367
|
interface IKafkaClient<T extends TopicMapConstraint<T>> {
|
|
231
|
-
checkStatus(): Promise<
|
|
232
|
-
status: "up";
|
|
233
|
-
clientId: string;
|
|
234
|
-
topics: string[];
|
|
235
|
-
}>;
|
|
368
|
+
checkStatus(): Promise<KafkaHealthResult>;
|
|
236
369
|
/**
|
|
237
370
|
* Query the consumer group lag per partition using the admin API.
|
|
238
371
|
* Lag = (broker high-watermark offset) − (last committed offset).
|
|
@@ -257,8 +390,18 @@ interface IKafkaClient<T extends TopicMapConstraint<T>> {
|
|
|
257
390
|
sendMessage<K extends keyof T>(topic: K, message: T[K], options?: SendOptions): Promise<void>;
|
|
258
391
|
sendBatch<K extends keyof T>(topic: K, messages: Array<BatchMessageItem<T[K]>>): Promise<void>;
|
|
259
392
|
transaction(fn: (ctx: TransactionContext<T>) => Promise<void>): Promise<void>;
|
|
260
|
-
getClientId
|
|
261
|
-
|
|
393
|
+
getClientId(): ClientId;
|
|
394
|
+
/**
|
|
395
|
+
* Drain in-flight handlers, then disconnect all producers, consumers, and admin.
|
|
396
|
+
* @param drainTimeoutMs Max ms to wait for in-flight handlers (default 30 000).
|
|
397
|
+
*/
|
|
398
|
+
disconnect(drainTimeoutMs?: number): Promise<void>;
|
|
399
|
+
/**
|
|
400
|
+
* Register SIGTERM / SIGINT signal handlers that drain in-flight messages before
|
|
401
|
+
* disconnecting. Call once after constructing the client in non-NestJS apps.
|
|
402
|
+
* NestJS apps get drain automatically via `onModuleDestroy` → `disconnect()`.
|
|
403
|
+
*/
|
|
404
|
+
enableGracefulShutdown(signals?: NodeJS.Signals[], drainTimeoutMs?: number): void;
|
|
262
405
|
}
|
|
263
406
|
/**
|
|
264
407
|
* Logger interface for KafkaClient.
|
|
@@ -325,75 +468,4 @@ interface SubscribeRetryOptions {
|
|
|
325
468
|
backoffMs?: number;
|
|
326
469
|
}
|
|
327
470
|
|
|
328
|
-
|
|
329
|
-
declare const HEADER_CORRELATION_ID = "x-correlation-id";
|
|
330
|
-
declare const HEADER_TIMESTAMP = "x-timestamp";
|
|
331
|
-
declare const HEADER_SCHEMA_VERSION = "x-schema-version";
|
|
332
|
-
declare const HEADER_TRACEPARENT = "traceparent";
|
|
333
|
-
/**
|
|
334
|
-
* Typed wrapper combining a parsed message payload with Kafka metadata
|
|
335
|
-
* and envelope headers.
|
|
336
|
-
*
|
|
337
|
-
* On **send**, the library auto-generates envelope headers
|
|
338
|
-
* (`x-event-id`, `x-correlation-id`, `x-timestamp`, `x-schema-version`).
|
|
339
|
-
*
|
|
340
|
-
* On **consume**, the library extracts those headers and assembles
|
|
341
|
-
* an `EventEnvelope` that is passed to the handler.
|
|
342
|
-
*/
|
|
343
|
-
interface EventEnvelope<T> {
|
|
344
|
-
/** Deserialized + validated message body. */
|
|
345
|
-
payload: T;
|
|
346
|
-
/** Topic the message was produced to / consumed from. */
|
|
347
|
-
topic: string;
|
|
348
|
-
/** Kafka partition (consume-side only, `-1` on send). */
|
|
349
|
-
partition: number;
|
|
350
|
-
/** Kafka offset (consume-side only, empty string on send). */
|
|
351
|
-
offset: string;
|
|
352
|
-
/** ISO-8601 timestamp set by the producer. */
|
|
353
|
-
timestamp: string;
|
|
354
|
-
/** Unique ID for this event (UUID v4). */
|
|
355
|
-
eventId: string;
|
|
356
|
-
/** Correlation ID — auto-propagated via AsyncLocalStorage. */
|
|
357
|
-
correlationId: string;
|
|
358
|
-
/** Schema version of the payload. */
|
|
359
|
-
schemaVersion: number;
|
|
360
|
-
/** W3C Trace Context `traceparent` header (set by OTel instrumentation). */
|
|
361
|
-
traceparent?: string;
|
|
362
|
-
/** All decoded Kafka headers for extensibility. */
|
|
363
|
-
headers: MessageHeaders;
|
|
364
|
-
}
|
|
365
|
-
interface EnvelopeCtx {
|
|
366
|
-
correlationId: string;
|
|
367
|
-
traceparent?: string;
|
|
368
|
-
}
|
|
369
|
-
/** Read the current envelope context (correlationId / traceparent) from ALS. */
|
|
370
|
-
declare function getEnvelopeContext(): EnvelopeCtx | undefined;
|
|
371
|
-
/** Execute `fn` inside an envelope context so nested sends inherit correlationId. */
|
|
372
|
-
declare function runWithEnvelopeContext<R>(ctx: EnvelopeCtx, fn: () => R): R;
|
|
373
|
-
/** Options accepted by `buildEnvelopeHeaders`. */
|
|
374
|
-
interface EnvelopeHeaderOptions {
|
|
375
|
-
correlationId?: string;
|
|
376
|
-
schemaVersion?: number;
|
|
377
|
-
eventId?: string;
|
|
378
|
-
headers?: MessageHeaders;
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Generate envelope headers for the send path.
|
|
382
|
-
*
|
|
383
|
-
* Priority for `correlationId`:
|
|
384
|
-
* explicit option → ALS context → new UUID.
|
|
385
|
-
*/
|
|
386
|
-
declare function buildEnvelopeHeaders(options?: EnvelopeHeaderOptions): MessageHeaders;
|
|
387
|
-
/**
|
|
388
|
-
* Decode kafkajs headers (`Record<string, Buffer | string | undefined>`)
|
|
389
|
-
* into plain `Record<string, string>`.
|
|
390
|
-
*/
|
|
391
|
-
declare function decodeHeaders(raw: Record<string, Buffer | string | (Buffer | string)[] | undefined> | undefined): MessageHeaders;
|
|
392
|
-
/**
|
|
393
|
-
* Build an `EventEnvelope` from a consumed kafkajs message.
|
|
394
|
-
* Tolerates missing envelope headers — generates defaults so messages
|
|
395
|
-
* from non-envelope producers still work.
|
|
396
|
-
*/
|
|
397
|
-
declare function extractEnvelope<T>(payload: T, headers: MessageHeaders, topic: string, partition: number, offset: string): EventEnvelope<T>;
|
|
398
|
-
|
|
399
|
-
export { type BatchMessageItem as B, type ClientId as C, type EnvelopeHeaderOptions as E, type GroupId as G, HEADER_CORRELATION_ID as H, type IKafkaClient as I, type KafkaInstrumentation as K, type MessageHeaders as M, type RetryOptions as R, type SchemaLike as S, type TopicMapConstraint as T, type KafkaClientOptions as a, type ConsumerOptions as b, type TopicDescriptor as c, type BatchMeta as d, type ConsumerHandle as e, type ConsumerInterceptor as f, type EventEnvelope as g, HEADER_EVENT_ID as h, HEADER_SCHEMA_VERSION as i, HEADER_TIMESTAMP as j, HEADER_TRACEPARENT as k, type InferSchema as l, type KafkaLogger as m, type MessageLostContext as n, type SendOptions as o, type SubscribeRetryOptions as p, type TTopicMessageMap as q, type TopicsFrom as r, type TransactionContext as s, buildEnvelopeHeaders as t, decodeHeaders as u, extractEnvelope as v, getEnvelopeContext as w, runWithEnvelopeContext as x, topic as y };
|
|
471
|
+
export { runWithEnvelopeContext as A, type BatchMessageItem as B, type ClientId as C, topic as D, type EnvelopeHeaderOptions as E, type GroupId as G, HEADER_CORRELATION_ID as H, type IKafkaClient as I, type KafkaInstrumentation as K, type MessageHeaders as M, type RetryOptions as R, type SchemaLike as S, type TopicMapConstraint as T, type KafkaClientOptions as a, type ConsumerOptions as b, type TopicDescriptor as c, type KafkaHealthResult as d, type BatchMeta as e, type BeforeConsumeResult as f, type ConsumerHandle as g, type ConsumerInterceptor as h, type EventEnvelope as i, HEADER_EVENT_ID as j, HEADER_SCHEMA_VERSION as k, HEADER_TIMESTAMP as l, HEADER_TRACEPARENT as m, type InferSchema as n, type KafkaLogger as o, type MessageLostContext as p, type SchemaParseContext as q, type SendOptions as r, type SubscribeRetryOptions as s, type TTopicMessageMap as t, type TopicsFrom as u, type TransactionContext as v, buildEnvelopeHeaders as w, decodeHeaders as x, extractEnvelope as y, getEnvelopeContext as z };
|
|
@@ -1,15 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context passed as the second argument to `SchemaLike.parse()`.
|
|
3
|
+
* Enables schema-registry adapters, version-aware migration, and
|
|
4
|
+
* header-driven parsing without coupling validators to Kafka internals.
|
|
5
|
+
*
|
|
6
|
+
* All fields are optional-friendly — validators that don't need the context
|
|
7
|
+
* can simply ignore the second argument.
|
|
8
|
+
*/
|
|
9
|
+
interface SchemaParseContext {
|
|
10
|
+
/** Topic the message was produced to / consumed from. */
|
|
11
|
+
topic: string;
|
|
12
|
+
/** Decoded message headers (envelope headers included). */
|
|
13
|
+
headers: MessageHeaders;
|
|
14
|
+
/** Value of the `x-schema-version` header, defaults to `1`. */
|
|
15
|
+
version: number;
|
|
16
|
+
}
|
|
1
17
|
/**
|
|
2
18
|
* Any validation library with a `.parse()` method.
|
|
3
19
|
* Works with Zod, Valibot, ArkType, or any custom validator.
|
|
4
20
|
*
|
|
21
|
+
* The optional `ctx` argument carries topic/header/version metadata so
|
|
22
|
+
* validators can perform schema-registry lookups or version-aware migrations.
|
|
23
|
+
* Existing validators that only use the first argument continue to work
|
|
24
|
+
* unchanged — the second argument is silently ignored.
|
|
25
|
+
*
|
|
5
26
|
* @example
|
|
6
27
|
* ```ts
|
|
7
28
|
* import { z } from 'zod';
|
|
8
29
|
* const schema: SchemaLike<{ id: string }> = z.object({ id: z.string() });
|
|
30
|
+
*
|
|
31
|
+
* // Context-aware validator:
|
|
32
|
+
* const schema: SchemaLike<MyType> = {
|
|
33
|
+
* parse(data, ctx) {
|
|
34
|
+
* const version = ctx?.version ?? 1;
|
|
35
|
+
* return version >= 2 ? migrateV1toV2(data) : validateV1(data);
|
|
36
|
+
* }
|
|
37
|
+
* };
|
|
9
38
|
* ```
|
|
10
39
|
*/
|
|
11
40
|
interface SchemaLike<T = any> {
|
|
12
|
-
parse(data: unknown): T | Promise<T>;
|
|
41
|
+
parse(data: unknown, ctx?: SchemaParseContext): T | Promise<T>;
|
|
13
42
|
}
|
|
14
43
|
/** Infer the output type from a SchemaLike. */
|
|
15
44
|
type InferSchema<S extends SchemaLike> = S extends SchemaLike<infer T> ? T : never;
|
|
@@ -32,8 +61,8 @@ interface TopicDescriptor<N extends string = string, M extends Record<string, an
|
|
|
32
61
|
*
|
|
33
62
|
* @example
|
|
34
63
|
* ```ts
|
|
35
|
-
* // Without schema — type
|
|
36
|
-
* const OrderCreated = topic('order.created')<{ orderId: string; amount: number }>();
|
|
64
|
+
* // Without schema — explicit type via .type<T>():
|
|
65
|
+
* const OrderCreated = topic('order.created').type<{ orderId: string; amount: number }>();
|
|
37
66
|
*
|
|
38
67
|
* // With schema — type inferred from schema:
|
|
39
68
|
* const OrderCreated = topic('order.created').schema(z.object({
|
|
@@ -50,16 +79,17 @@ interface TopicDescriptor<N extends string = string, M extends Record<string, an
|
|
|
50
79
|
* ```
|
|
51
80
|
*/
|
|
52
81
|
declare function topic<N extends string>(name: N): {
|
|
53
|
-
|
|
54
|
-
|
|
82
|
+
/** Provide an explicit message type without a runtime schema. */
|
|
83
|
+
type: <M extends Record<string, any>>() => TopicDescriptor<N, M>;
|
|
84
|
+
schema: <S extends SchemaLike<Record<string, any>>>(schema: S) => TopicDescriptor<N, InferSchema<S>>;
|
|
55
85
|
};
|
|
56
86
|
/**
|
|
57
87
|
* Build a topic-message map type from a union of TopicDescriptors.
|
|
58
88
|
*
|
|
59
89
|
* @example
|
|
60
90
|
* ```ts
|
|
61
|
-
* const OrderCreated = topic('order.created')<{ orderId: string }>();
|
|
62
|
-
* const OrderCompleted = topic('order.completed')<{ completedAt: string }>();
|
|
91
|
+
* const OrderCreated = topic('order.created').type<{ orderId: string }>();
|
|
92
|
+
* const OrderCompleted = topic('order.completed').type<{ completedAt: string }>();
|
|
63
93
|
*
|
|
64
94
|
* type MyTopics = TopicsFrom<typeof OrderCreated | typeof OrderCompleted>;
|
|
65
95
|
* // { 'order.created': { orderId: string }; 'order.completed': { completedAt: string } }
|
|
@@ -69,6 +99,77 @@ type TopicsFrom<D extends TopicDescriptor<any, any>> = {
|
|
|
69
99
|
[K in D as K["__topic"]]: K["__type"];
|
|
70
100
|
};
|
|
71
101
|
|
|
102
|
+
declare const HEADER_EVENT_ID = "x-event-id";
|
|
103
|
+
declare const HEADER_CORRELATION_ID = "x-correlation-id";
|
|
104
|
+
declare const HEADER_TIMESTAMP = "x-timestamp";
|
|
105
|
+
declare const HEADER_SCHEMA_VERSION = "x-schema-version";
|
|
106
|
+
declare const HEADER_TRACEPARENT = "traceparent";
|
|
107
|
+
/**
|
|
108
|
+
* Typed wrapper combining a parsed message payload with Kafka metadata
|
|
109
|
+
* and envelope headers.
|
|
110
|
+
*
|
|
111
|
+
* On **send**, the library auto-generates envelope headers
|
|
112
|
+
* (`x-event-id`, `x-correlation-id`, `x-timestamp`, `x-schema-version`).
|
|
113
|
+
*
|
|
114
|
+
* On **consume**, the library extracts those headers and assembles
|
|
115
|
+
* an `EventEnvelope` that is passed to the handler.
|
|
116
|
+
*/
|
|
117
|
+
interface EventEnvelope<T> {
|
|
118
|
+
/** Deserialized + validated message body. */
|
|
119
|
+
payload: T;
|
|
120
|
+
/** Topic the message was produced to / consumed from. */
|
|
121
|
+
topic: string;
|
|
122
|
+
/** Kafka partition (consume-side only, `-1` on send). */
|
|
123
|
+
partition: number;
|
|
124
|
+
/** Kafka offset (consume-side only, empty string on send). */
|
|
125
|
+
offset: string;
|
|
126
|
+
/** ISO-8601 timestamp set by the producer. */
|
|
127
|
+
timestamp: string;
|
|
128
|
+
/** Unique ID for this event (UUID v4). */
|
|
129
|
+
eventId: string;
|
|
130
|
+
/** Correlation ID — auto-propagated via AsyncLocalStorage. */
|
|
131
|
+
correlationId: string;
|
|
132
|
+
/** Schema version of the payload. */
|
|
133
|
+
schemaVersion: number;
|
|
134
|
+
/** W3C Trace Context `traceparent` header (set by OTel instrumentation). */
|
|
135
|
+
traceparent?: string;
|
|
136
|
+
/** All decoded Kafka headers for extensibility. */
|
|
137
|
+
headers: MessageHeaders;
|
|
138
|
+
}
|
|
139
|
+
interface EnvelopeCtx {
|
|
140
|
+
correlationId: string;
|
|
141
|
+
traceparent?: string;
|
|
142
|
+
}
|
|
143
|
+
/** Read the current envelope context (correlationId / traceparent) from ALS. */
|
|
144
|
+
declare function getEnvelopeContext(): EnvelopeCtx | undefined;
|
|
145
|
+
/** Execute `fn` inside an envelope context so nested sends inherit correlationId. */
|
|
146
|
+
declare function runWithEnvelopeContext<R>(ctx: EnvelopeCtx, fn: () => R): R;
|
|
147
|
+
/** Options accepted by `buildEnvelopeHeaders`. */
|
|
148
|
+
interface EnvelopeHeaderOptions {
|
|
149
|
+
correlationId?: string;
|
|
150
|
+
schemaVersion?: number;
|
|
151
|
+
eventId?: string;
|
|
152
|
+
headers?: MessageHeaders;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Generate envelope headers for the send path.
|
|
156
|
+
*
|
|
157
|
+
* Priority for `correlationId`:
|
|
158
|
+
* explicit option → ALS context → new UUID.
|
|
159
|
+
*/
|
|
160
|
+
declare function buildEnvelopeHeaders(options?: EnvelopeHeaderOptions): MessageHeaders;
|
|
161
|
+
/**
|
|
162
|
+
* Decode kafkajs headers (`Record<string, Buffer | string | undefined>`)
|
|
163
|
+
* into plain `Record<string, string>`.
|
|
164
|
+
*/
|
|
165
|
+
declare function decodeHeaders(raw: Record<string, Buffer | string | (Buffer | string)[] | undefined> | undefined): MessageHeaders;
|
|
166
|
+
/**
|
|
167
|
+
* Build an `EventEnvelope` from a consumed kafkajs message.
|
|
168
|
+
* Tolerates missing envelope headers — generates defaults so messages
|
|
169
|
+
* from non-envelope producers still work.
|
|
170
|
+
*/
|
|
171
|
+
declare function extractEnvelope<T>(payload: T, headers: MessageHeaders, topic: string, partition: number, offset: string): EventEnvelope<T>;
|
|
172
|
+
|
|
72
173
|
/**
|
|
73
174
|
* Mapping of topic names to their message types.
|
|
74
175
|
* Define this interface to get type-safe publish/subscribe across your app.
|
|
@@ -165,6 +266,12 @@ interface ConsumerOptions<T extends TopicMapConstraint<T> = TTopicMessageMap> {
|
|
|
165
266
|
* On exhaustion, messages go to `<topic>.dlq` (if `dlq: true`) or `onMessageLost`.
|
|
166
267
|
*/
|
|
167
268
|
retryTopics?: boolean;
|
|
269
|
+
/**
|
|
270
|
+
* Timeout (ms) for waiting until each retry level consumer receives partition
|
|
271
|
+
* assignments after `startConsumer` connects. Default: `10000`.
|
|
272
|
+
* Increase this when the broker is slow to rebalance.
|
|
273
|
+
*/
|
|
274
|
+
retryTopicAssignmentTimeoutMs?: number;
|
|
168
275
|
/**
|
|
169
276
|
* Log a warning if the message handler has not resolved within this window (ms).
|
|
170
277
|
* The handler is not cancelled — this is a diagnostic aid to surface stuck handlers
|
|
@@ -196,6 +303,21 @@ interface ConsumerInterceptor<T extends TopicMapConstraint<T> = TTopicMessageMap
|
|
|
196
303
|
/** Called when the message handler throws. */
|
|
197
304
|
onError?(envelope: EventEnvelope<T[keyof T]>, error: Error): Promise<void> | void;
|
|
198
305
|
}
|
|
306
|
+
/**
|
|
307
|
+
* Return value of `KafkaInstrumentation.beforeConsume`.
|
|
308
|
+
*
|
|
309
|
+
* - `() => void` — legacy form: a cleanup function called after the handler.
|
|
310
|
+
* - Object form:
|
|
311
|
+
* - `cleanup?()` — called after the handler (same as the legacy function form).
|
|
312
|
+
* - `wrap?(fn)` — wraps the handler execution; call `fn()` inside the desired
|
|
313
|
+
* async context (e.g. `context.with(spanCtx, fn)` for OpenTelemetry). Multiple
|
|
314
|
+
* wraps from different instrumentations are composed in declaration order,
|
|
315
|
+
* so the first instrumentation's wrap is the outermost.
|
|
316
|
+
*/
|
|
317
|
+
type BeforeConsumeResult = (() => void) | {
|
|
318
|
+
cleanup?(): void;
|
|
319
|
+
wrap?(fn: () => Promise<void>): Promise<void>;
|
|
320
|
+
};
|
|
199
321
|
/**
|
|
200
322
|
* Client-wide instrumentation hooks for both send and consume paths.
|
|
201
323
|
* Use this for cross-cutting concerns like tracing and metrics.
|
|
@@ -207,8 +329,13 @@ interface KafkaInstrumentation {
|
|
|
207
329
|
beforeSend?(topic: string, headers: MessageHeaders): void;
|
|
208
330
|
/** Called after a successful send. */
|
|
209
331
|
afterSend?(topic: string): void;
|
|
210
|
-
/**
|
|
211
|
-
|
|
332
|
+
/**
|
|
333
|
+
* Called before the consumer handler.
|
|
334
|
+
* Return a cleanup function (legacy) or a `BeforeConsumeResult` object with
|
|
335
|
+
* optional `cleanup` and `wrap`. Use `wrap` to run the handler inside a
|
|
336
|
+
* specific async context (e.g. an active OpenTelemetry span).
|
|
337
|
+
*/
|
|
338
|
+
beforeConsume?(envelope: EventEnvelope<any>): BeforeConsumeResult | void;
|
|
212
339
|
/** Called when the consumer handler throws. */
|
|
213
340
|
onConsumeError?(envelope: EventEnvelope<any>, error: Error): void;
|
|
214
341
|
}
|
|
@@ -226,13 +353,19 @@ interface ConsumerHandle {
|
|
|
226
353
|
/** Stop this consumer. Equivalent to calling `client.stopConsumer(groupId)`. */
|
|
227
354
|
stop(): Promise<void>;
|
|
228
355
|
}
|
|
356
|
+
/** Result returned by `KafkaClient.checkStatus()`. */
|
|
357
|
+
type KafkaHealthResult = {
|
|
358
|
+
status: "up";
|
|
359
|
+
clientId: string;
|
|
360
|
+
topics: string[];
|
|
361
|
+
} | {
|
|
362
|
+
status: "down";
|
|
363
|
+
clientId: string;
|
|
364
|
+
error: string;
|
|
365
|
+
};
|
|
229
366
|
/** Interface describing all public methods of the Kafka client. */
|
|
230
367
|
interface IKafkaClient<T extends TopicMapConstraint<T>> {
|
|
231
|
-
checkStatus(): Promise<
|
|
232
|
-
status: "up";
|
|
233
|
-
clientId: string;
|
|
234
|
-
topics: string[];
|
|
235
|
-
}>;
|
|
368
|
+
checkStatus(): Promise<KafkaHealthResult>;
|
|
236
369
|
/**
|
|
237
370
|
* Query the consumer group lag per partition using the admin API.
|
|
238
371
|
* Lag = (broker high-watermark offset) − (last committed offset).
|
|
@@ -257,8 +390,18 @@ interface IKafkaClient<T extends TopicMapConstraint<T>> {
|
|
|
257
390
|
sendMessage<K extends keyof T>(topic: K, message: T[K], options?: SendOptions): Promise<void>;
|
|
258
391
|
sendBatch<K extends keyof T>(topic: K, messages: Array<BatchMessageItem<T[K]>>): Promise<void>;
|
|
259
392
|
transaction(fn: (ctx: TransactionContext<T>) => Promise<void>): Promise<void>;
|
|
260
|
-
getClientId
|
|
261
|
-
|
|
393
|
+
getClientId(): ClientId;
|
|
394
|
+
/**
|
|
395
|
+
* Drain in-flight handlers, then disconnect all producers, consumers, and admin.
|
|
396
|
+
* @param drainTimeoutMs Max ms to wait for in-flight handlers (default 30 000).
|
|
397
|
+
*/
|
|
398
|
+
disconnect(drainTimeoutMs?: number): Promise<void>;
|
|
399
|
+
/**
|
|
400
|
+
* Register SIGTERM / SIGINT signal handlers that drain in-flight messages before
|
|
401
|
+
* disconnecting. Call once after constructing the client in non-NestJS apps.
|
|
402
|
+
* NestJS apps get drain automatically via `onModuleDestroy` → `disconnect()`.
|
|
403
|
+
*/
|
|
404
|
+
enableGracefulShutdown(signals?: NodeJS.Signals[], drainTimeoutMs?: number): void;
|
|
262
405
|
}
|
|
263
406
|
/**
|
|
264
407
|
* Logger interface for KafkaClient.
|
|
@@ -325,75 +468,4 @@ interface SubscribeRetryOptions {
|
|
|
325
468
|
backoffMs?: number;
|
|
326
469
|
}
|
|
327
470
|
|
|
328
|
-
|
|
329
|
-
declare const HEADER_CORRELATION_ID = "x-correlation-id";
|
|
330
|
-
declare const HEADER_TIMESTAMP = "x-timestamp";
|
|
331
|
-
declare const HEADER_SCHEMA_VERSION = "x-schema-version";
|
|
332
|
-
declare const HEADER_TRACEPARENT = "traceparent";
|
|
333
|
-
/**
|
|
334
|
-
* Typed wrapper combining a parsed message payload with Kafka metadata
|
|
335
|
-
* and envelope headers.
|
|
336
|
-
*
|
|
337
|
-
* On **send**, the library auto-generates envelope headers
|
|
338
|
-
* (`x-event-id`, `x-correlation-id`, `x-timestamp`, `x-schema-version`).
|
|
339
|
-
*
|
|
340
|
-
* On **consume**, the library extracts those headers and assembles
|
|
341
|
-
* an `EventEnvelope` that is passed to the handler.
|
|
342
|
-
*/
|
|
343
|
-
interface EventEnvelope<T> {
|
|
344
|
-
/** Deserialized + validated message body. */
|
|
345
|
-
payload: T;
|
|
346
|
-
/** Topic the message was produced to / consumed from. */
|
|
347
|
-
topic: string;
|
|
348
|
-
/** Kafka partition (consume-side only, `-1` on send). */
|
|
349
|
-
partition: number;
|
|
350
|
-
/** Kafka offset (consume-side only, empty string on send). */
|
|
351
|
-
offset: string;
|
|
352
|
-
/** ISO-8601 timestamp set by the producer. */
|
|
353
|
-
timestamp: string;
|
|
354
|
-
/** Unique ID for this event (UUID v4). */
|
|
355
|
-
eventId: string;
|
|
356
|
-
/** Correlation ID — auto-propagated via AsyncLocalStorage. */
|
|
357
|
-
correlationId: string;
|
|
358
|
-
/** Schema version of the payload. */
|
|
359
|
-
schemaVersion: number;
|
|
360
|
-
/** W3C Trace Context `traceparent` header (set by OTel instrumentation). */
|
|
361
|
-
traceparent?: string;
|
|
362
|
-
/** All decoded Kafka headers for extensibility. */
|
|
363
|
-
headers: MessageHeaders;
|
|
364
|
-
}
|
|
365
|
-
interface EnvelopeCtx {
|
|
366
|
-
correlationId: string;
|
|
367
|
-
traceparent?: string;
|
|
368
|
-
}
|
|
369
|
-
/** Read the current envelope context (correlationId / traceparent) from ALS. */
|
|
370
|
-
declare function getEnvelopeContext(): EnvelopeCtx | undefined;
|
|
371
|
-
/** Execute `fn` inside an envelope context so nested sends inherit correlationId. */
|
|
372
|
-
declare function runWithEnvelopeContext<R>(ctx: EnvelopeCtx, fn: () => R): R;
|
|
373
|
-
/** Options accepted by `buildEnvelopeHeaders`. */
|
|
374
|
-
interface EnvelopeHeaderOptions {
|
|
375
|
-
correlationId?: string;
|
|
376
|
-
schemaVersion?: number;
|
|
377
|
-
eventId?: string;
|
|
378
|
-
headers?: MessageHeaders;
|
|
379
|
-
}
|
|
380
|
-
/**
|
|
381
|
-
* Generate envelope headers for the send path.
|
|
382
|
-
*
|
|
383
|
-
* Priority for `correlationId`:
|
|
384
|
-
* explicit option → ALS context → new UUID.
|
|
385
|
-
*/
|
|
386
|
-
declare function buildEnvelopeHeaders(options?: EnvelopeHeaderOptions): MessageHeaders;
|
|
387
|
-
/**
|
|
388
|
-
* Decode kafkajs headers (`Record<string, Buffer | string | undefined>`)
|
|
389
|
-
* into plain `Record<string, string>`.
|
|
390
|
-
*/
|
|
391
|
-
declare function decodeHeaders(raw: Record<string, Buffer | string | (Buffer | string)[] | undefined> | undefined): MessageHeaders;
|
|
392
|
-
/**
|
|
393
|
-
* Build an `EventEnvelope` from a consumed kafkajs message.
|
|
394
|
-
* Tolerates missing envelope headers — generates defaults so messages
|
|
395
|
-
* from non-envelope producers still work.
|
|
396
|
-
*/
|
|
397
|
-
declare function extractEnvelope<T>(payload: T, headers: MessageHeaders, topic: string, partition: number, offset: string): EventEnvelope<T>;
|
|
398
|
-
|
|
399
|
-
export { type BatchMessageItem as B, type ClientId as C, type EnvelopeHeaderOptions as E, type GroupId as G, HEADER_CORRELATION_ID as H, type IKafkaClient as I, type KafkaInstrumentation as K, type MessageHeaders as M, type RetryOptions as R, type SchemaLike as S, type TopicMapConstraint as T, type KafkaClientOptions as a, type ConsumerOptions as b, type TopicDescriptor as c, type BatchMeta as d, type ConsumerHandle as e, type ConsumerInterceptor as f, type EventEnvelope as g, HEADER_EVENT_ID as h, HEADER_SCHEMA_VERSION as i, HEADER_TIMESTAMP as j, HEADER_TRACEPARENT as k, type InferSchema as l, type KafkaLogger as m, type MessageLostContext as n, type SendOptions as o, type SubscribeRetryOptions as p, type TTopicMessageMap as q, type TopicsFrom as r, type TransactionContext as s, buildEnvelopeHeaders as t, decodeHeaders as u, extractEnvelope as v, getEnvelopeContext as w, runWithEnvelopeContext as x, topic as y };
|
|
471
|
+
export { runWithEnvelopeContext as A, type BatchMessageItem as B, type ClientId as C, topic as D, type EnvelopeHeaderOptions as E, type GroupId as G, HEADER_CORRELATION_ID as H, type IKafkaClient as I, type KafkaInstrumentation as K, type MessageHeaders as M, type RetryOptions as R, type SchemaLike as S, type TopicMapConstraint as T, type KafkaClientOptions as a, type ConsumerOptions as b, type TopicDescriptor as c, type KafkaHealthResult as d, type BatchMeta as e, type BeforeConsumeResult as f, type ConsumerHandle as g, type ConsumerInterceptor as h, type EventEnvelope as i, HEADER_EVENT_ID as j, HEADER_SCHEMA_VERSION as k, HEADER_TIMESTAMP as l, HEADER_TRACEPARENT as m, type InferSchema as n, type KafkaLogger as o, type MessageLostContext as p, type SchemaParseContext as q, type SendOptions as r, type SubscribeRetryOptions as s, type TTopicMessageMap as t, type TopicsFrom as u, type TransactionContext as v, buildEnvelopeHeaders as w, decodeHeaders as x, extractEnvelope as y, getEnvelopeContext as z };
|