@drarzter/kafka-client 0.9.4 → 0.11.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.
Files changed (180) hide show
  1. package/README.md +693 -8
  2. package/dist/chunk-OR7TPAAE.mjs +4760 -0
  3. package/dist/chunk-OR7TPAAE.mjs.map +1 -0
  4. package/dist/chunk-PQVBRDNV.mjs +149 -0
  5. package/dist/chunk-PQVBRDNV.mjs.map +1 -0
  6. package/dist/cli/dlq.d.ts +119 -0
  7. package/dist/cli/dlq.d.ts.map +1 -0
  8. package/dist/cli/index.d.ts +3 -0
  9. package/dist/cli/index.d.ts.map +1 -0
  10. package/dist/{chunk-SM4FZKAZ.mjs → cli/index.js} +1073 -309
  11. package/dist/cli/index.js.map +1 -0
  12. package/dist/cli/index.mjs +356 -0
  13. package/dist/cli/index.mjs.map +1 -0
  14. package/dist/client/config/from-env.d.ts +188 -0
  15. package/dist/client/config/from-env.d.ts.map +1 -0
  16. package/dist/client/config/index.d.ts +2 -0
  17. package/dist/client/config/index.d.ts.map +1 -0
  18. package/dist/client/errors.d.ts +67 -0
  19. package/dist/client/errors.d.ts.map +1 -0
  20. package/dist/client/kafka.client/admin/ops.d.ts +114 -0
  21. package/dist/client/kafka.client/admin/ops.d.ts.map +1 -0
  22. package/dist/client/kafka.client/consumer/features/delayed.d.ts +24 -0
  23. package/dist/client/kafka.client/consumer/features/delayed.d.ts.map +1 -0
  24. package/dist/client/kafka.client/consumer/features/dlq-replay.d.ts +52 -0
  25. package/dist/client/kafka.client/consumer/features/dlq-replay.d.ts.map +1 -0
  26. package/dist/client/kafka.client/consumer/features/routed.d.ts +4 -0
  27. package/dist/client/kafka.client/consumer/features/routed.d.ts.map +1 -0
  28. package/dist/client/kafka.client/consumer/features/snapshot.d.ts +10 -0
  29. package/dist/client/kafka.client/consumer/features/snapshot.d.ts.map +1 -0
  30. package/dist/client/kafka.client/consumer/features/window.d.ts +5 -0
  31. package/dist/client/kafka.client/consumer/features/window.d.ts.map +1 -0
  32. package/dist/client/kafka.client/consumer/handler.d.ts +163 -0
  33. package/dist/client/kafka.client/consumer/handler.d.ts.map +1 -0
  34. package/dist/client/kafka.client/consumer/ops.d.ts +64 -0
  35. package/dist/client/kafka.client/consumer/ops.d.ts.map +1 -0
  36. package/dist/client/kafka.client/consumer/pipeline.d.ts +168 -0
  37. package/dist/client/kafka.client/consumer/pipeline.d.ts.map +1 -0
  38. package/dist/client/kafka.client/consumer/queue.d.ts +37 -0
  39. package/dist/client/kafka.client/consumer/queue.d.ts.map +1 -0
  40. package/dist/client/kafka.client/consumer/retry-topic.d.ts +68 -0
  41. package/dist/client/kafka.client/consumer/retry-topic.d.ts.map +1 -0
  42. package/dist/client/kafka.client/consumer/setup.d.ts +66 -0
  43. package/dist/client/kafka.client/consumer/setup.d.ts.map +1 -0
  44. package/dist/client/kafka.client/consumer/start.d.ts +7 -0
  45. package/dist/client/kafka.client/consumer/start.d.ts.map +1 -0
  46. package/dist/client/kafka.client/consumer/stop.d.ts +19 -0
  47. package/dist/client/kafka.client/consumer/stop.d.ts.map +1 -0
  48. package/dist/client/kafka.client/consumer/subscribe-retry.d.ts +4 -0
  49. package/dist/client/kafka.client/consumer/subscribe-retry.d.ts.map +1 -0
  50. package/dist/client/kafka.client/context.d.ts +75 -0
  51. package/dist/client/kafka.client/context.d.ts.map +1 -0
  52. package/dist/client/kafka.client/index.d.ts +155 -0
  53. package/dist/client/kafka.client/index.d.ts.map +1 -0
  54. package/dist/client/kafka.client/infra/circuit-breaker.manager.d.ts +61 -0
  55. package/dist/client/kafka.client/infra/circuit-breaker.manager.d.ts.map +1 -0
  56. package/dist/client/kafka.client/infra/dedup.store.d.ts +28 -0
  57. package/dist/client/kafka.client/infra/dedup.store.d.ts.map +1 -0
  58. package/dist/client/kafka.client/infra/inflight.tracker.d.ts +22 -0
  59. package/dist/client/kafka.client/infra/inflight.tracker.d.ts.map +1 -0
  60. package/dist/client/kafka.client/infra/metrics.manager.d.ts +67 -0
  61. package/dist/client/kafka.client/infra/metrics.manager.d.ts.map +1 -0
  62. package/dist/client/kafka.client/producer/lifecycle.d.ts +41 -0
  63. package/dist/client/kafka.client/producer/lifecycle.d.ts.map +1 -0
  64. package/dist/client/kafka.client/producer/ops.d.ts +79 -0
  65. package/dist/client/kafka.client/producer/ops.d.ts.map +1 -0
  66. package/dist/client/kafka.client/producer/send.d.ts +21 -0
  67. package/dist/client/kafka.client/producer/send.d.ts.map +1 -0
  68. package/dist/client/kafka.client/validate-options.d.ts +11 -0
  69. package/dist/client/kafka.client/validate-options.d.ts.map +1 -0
  70. package/dist/client/message/envelope.d.ts +105 -0
  71. package/dist/client/message/envelope.d.ts.map +1 -0
  72. package/dist/client/message/schema-registry.d.ts +124 -0
  73. package/dist/client/message/schema-registry.d.ts.map +1 -0
  74. package/dist/client/message/serde.d.ts +68 -0
  75. package/dist/client/message/serde.d.ts.map +1 -0
  76. package/dist/client/message/topic.d.ts +159 -0
  77. package/dist/client/message/topic.d.ts.map +1 -0
  78. package/dist/client/message/versioned-schema.d.ts +53 -0
  79. package/dist/client/message/versioned-schema.d.ts.map +1 -0
  80. package/dist/client/outbox/index.d.ts +4 -0
  81. package/dist/client/outbox/index.d.ts.map +1 -0
  82. package/dist/client/outbox/outbox.relay.d.ts +90 -0
  83. package/dist/client/outbox/outbox.relay.d.ts.map +1 -0
  84. package/dist/client/outbox/outbox.store.d.ts +42 -0
  85. package/dist/client/outbox/outbox.store.d.ts.map +1 -0
  86. package/dist/client/outbox/outbox.types.d.ts +144 -0
  87. package/dist/client/outbox/outbox.types.d.ts.map +1 -0
  88. package/dist/client/security/acl.d.ts +108 -0
  89. package/dist/client/security/acl.d.ts.map +1 -0
  90. package/dist/client/security/index.d.ts +5 -0
  91. package/dist/client/security/index.d.ts.map +1 -0
  92. package/dist/client/security/providers.d.ts +88 -0
  93. package/dist/client/security/providers.d.ts.map +1 -0
  94. package/dist/client/security/resolve-security.d.ts +19 -0
  95. package/dist/client/security/resolve-security.d.ts.map +1 -0
  96. package/dist/client/security/security.types.d.ts +76 -0
  97. package/dist/client/security/security.types.d.ts.map +1 -0
  98. package/dist/client/transport/confluent.transport.d.ts +32 -0
  99. package/dist/client/transport/confluent.transport.d.ts.map +1 -0
  100. package/dist/client/transport/transport.interface.d.ts +221 -0
  101. package/dist/client/transport/transport.interface.d.ts.map +1 -0
  102. package/dist/client/types/admin.interface.d.ts +174 -0
  103. package/dist/client/types/admin.interface.d.ts.map +1 -0
  104. package/dist/client/types/admin.types.d.ts +140 -0
  105. package/dist/client/types/admin.types.d.ts.map +1 -0
  106. package/dist/client/types/client.d.ts +21 -0
  107. package/dist/client/types/client.d.ts.map +1 -0
  108. package/dist/client/types/common.d.ts +84 -0
  109. package/dist/client/types/common.d.ts.map +1 -0
  110. package/dist/client/types/config.types.d.ts +167 -0
  111. package/dist/client/types/config.types.d.ts.map +1 -0
  112. package/dist/client/types/consumer.interface.d.ts +115 -0
  113. package/dist/client/types/consumer.interface.d.ts.map +1 -0
  114. package/dist/{consumer.types-fFCag3VJ.d.mts → client/types/consumer.types.d.ts} +62 -383
  115. package/dist/client/types/consumer.types.d.ts.map +1 -0
  116. package/dist/client/types/dedup.types.d.ts +50 -0
  117. package/dist/client/types/dedup.types.d.ts.map +1 -0
  118. package/dist/client/types/lifecycle.interface.d.ts +72 -0
  119. package/dist/client/types/lifecycle.interface.d.ts.map +1 -0
  120. package/dist/client/types/producer.interface.d.ts +52 -0
  121. package/dist/client/types/producer.interface.d.ts.map +1 -0
  122. package/dist/client/types/producer.types.d.ts +90 -0
  123. package/dist/client/types/producer.types.d.ts.map +1 -0
  124. package/dist/client/types.d.ts +8 -0
  125. package/dist/client/types.d.ts.map +1 -0
  126. package/dist/core.d.ts +13 -314
  127. package/dist/core.d.ts.map +1 -0
  128. package/dist/core.js +1466 -123
  129. package/dist/core.js.map +1 -1
  130. package/dist/core.mjs +45 -3
  131. package/dist/index.d.ts +7 -128
  132. package/dist/index.d.ts.map +1 -0
  133. package/dist/index.js +1483 -123
  134. package/dist/index.js.map +1 -1
  135. package/dist/index.mjs +62 -3
  136. package/dist/index.mjs.map +1 -1
  137. package/dist/nest/kafka.constants.d.ts +5 -0
  138. package/dist/nest/kafka.constants.d.ts.map +1 -0
  139. package/dist/nest/kafka.decorator.d.ts +49 -0
  140. package/dist/nest/kafka.decorator.d.ts.map +1 -0
  141. package/dist/nest/kafka.explorer.d.ts +17 -0
  142. package/dist/nest/kafka.explorer.d.ts.map +1 -0
  143. package/dist/nest/kafka.health.d.ts +7 -0
  144. package/dist/nest/kafka.health.d.ts.map +1 -0
  145. package/dist/nest/kafka.module.d.ts +61 -0
  146. package/dist/nest/kafka.module.d.ts.map +1 -0
  147. package/dist/otel.d.ts +83 -5
  148. package/dist/otel.d.ts.map +1 -0
  149. package/dist/otel.js +100 -6
  150. package/dist/otel.js.map +1 -1
  151. package/dist/otel.mjs +98 -5
  152. package/dist/otel.mjs.map +1 -1
  153. package/dist/serde.d.ts +157 -0
  154. package/dist/serde.d.ts.map +1 -0
  155. package/dist/serde.js +308 -0
  156. package/dist/serde.js.map +1 -0
  157. package/dist/serde.mjs +158 -0
  158. package/dist/serde.mjs.map +1 -0
  159. package/dist/testing/client.mock.d.ts +47 -0
  160. package/dist/testing/client.mock.d.ts.map +1 -0
  161. package/dist/testing/index.d.ts +4 -0
  162. package/dist/testing/index.d.ts.map +1 -0
  163. package/dist/testing/test.container.d.ts +63 -0
  164. package/dist/testing/test.container.d.ts.map +1 -0
  165. package/dist/{testing.d.mts → testing/transport.fake.d.ts} +7 -111
  166. package/dist/testing/transport.fake.d.ts.map +1 -0
  167. package/dist/testing.d.ts +2 -318
  168. package/dist/testing.d.ts.map +1 -0
  169. package/dist/testing.js +26 -0
  170. package/dist/testing.js.map +1 -1
  171. package/dist/testing.mjs +26 -0
  172. package/dist/testing.mjs.map +1 -1
  173. package/package.json +40 -8
  174. package/dist/chunk-SM4FZKAZ.mjs.map +0 -1
  175. package/dist/client-1irhGEu0.d.mts +0 -751
  176. package/dist/client-BpFjkHhr.d.ts +0 -751
  177. package/dist/consumer.types-fFCag3VJ.d.ts +0 -958
  178. package/dist/core.d.mts +0 -314
  179. package/dist/index.d.mts +0 -128
  180. package/dist/otel.d.mts +0 -27
package/dist/index.mjs CHANGED
@@ -1,21 +1,43 @@
1
1
  import {
2
+ ConfluentTransport,
2
3
  HEADER_CORRELATION_ID,
4
+ HEADER_DELAYED_TARGET,
5
+ HEADER_DELAYED_UNTIL,
3
6
  HEADER_EVENT_ID,
4
7
  HEADER_LAMPORT_CLOCK,
5
8
  HEADER_SCHEMA_VERSION,
6
9
  HEADER_TIMESTAMP,
7
10
  HEADER_TRACEPARENT,
11
+ InMemoryDedupStore,
12
+ InMemoryOutboxStore,
8
13
  KafkaClient,
9
14
  KafkaProcessingError,
10
15
  KafkaRetryExhaustedError,
11
16
  KafkaValidationError,
17
+ awsMskIamProvider,
12
18
  buildEnvelopeHeaders,
19
+ consumerOptionsFromEnv,
13
20
  decodeHeaders,
21
+ describeRequiredAcls,
14
22
  extractEnvelope,
23
+ gcpAccessTokenProvider,
15
24
  getEnvelopeContext,
25
+ kafkaClientConfigFromEnv,
26
+ mergeConsumerOptions,
27
+ resolveSecurityOptions,
16
28
  runWithEnvelopeContext,
17
- topic
18
- } from "./chunk-SM4FZKAZ.mjs";
29
+ startOutboxRelay,
30
+ toError,
31
+ toKafkaAclCommands,
32
+ toMskIamPolicy,
33
+ topic,
34
+ versionedSchema
35
+ } from "./chunk-OR7TPAAE.mjs";
36
+ import {
37
+ JsonSerde,
38
+ SchemaRegistryClient,
39
+ registrySchema
40
+ } from "./chunk-PQVBRDNV.mjs";
19
41
  import {
20
42
  __decorateClass,
21
43
  __decorateParam
@@ -68,11 +90,14 @@ var SubscribeTo = (topics, options) => {
68
90
  };
69
91
 
70
92
  // src/nest/kafka.explorer.ts
93
+ var wiredSubscriptions = /* @__PURE__ */ new WeakMap();
71
94
  var KafkaExplorer = class {
72
95
  constructor(discoveryService, moduleRef) {
73
96
  this.discoveryService = discoveryService;
74
97
  this.moduleRef = moduleRef;
75
98
  }
99
+ discoveryService;
100
+ moduleRef;
76
101
  logger = new Logger(KafkaExplorer.name);
77
102
  /**
78
103
  * Scan all NestJS providers for `@SubscribeTo()` metadata and wire each decorated
@@ -92,6 +117,14 @@ var KafkaExplorer = class {
92
117
  if (!metadata || metadata.length === 0) continue;
93
118
  for (const entry of metadata) {
94
119
  const token = getKafkaClientToken(entry.clientName);
120
+ const entryKey = `${token}:${String(entry.methodName)}`;
121
+ let wired = wiredSubscriptions.get(instance);
122
+ if (!wired) {
123
+ wired = /* @__PURE__ */ new Set();
124
+ wiredSubscriptions.set(instance, wired);
125
+ }
126
+ if (wired.has(entryKey)) continue;
127
+ wired.add(entryKey);
95
128
  let client;
96
129
  try {
97
130
  client = this.moduleRef.get(token, { strict: false });
@@ -181,6 +214,12 @@ var KafkaModule = class {
181
214
  instrumentation: options.instrumentation,
182
215
  onMessageLost: options.onMessageLost,
183
216
  onRebalance: options.onRebalance,
217
+ transactionalId: options.transactionalId,
218
+ clockRecovery: options.clockRecovery,
219
+ lagThrottle: options.lagThrottle,
220
+ onTtlExpired: options.onTtlExpired,
221
+ transport: options.transport,
222
+ security: options.security,
184
223
  logger: new Logger2(`KafkaClient:${options.clientId}`)
185
224
  }
186
225
  );
@@ -203,13 +242,19 @@ KafkaHealthIndicator = __decorateClass([
203
242
  Injectable2()
204
243
  ], KafkaHealthIndicator);
205
244
  export {
245
+ ConfluentTransport,
206
246
  HEADER_CORRELATION_ID,
247
+ HEADER_DELAYED_TARGET,
248
+ HEADER_DELAYED_UNTIL,
207
249
  HEADER_EVENT_ID,
208
250
  HEADER_LAMPORT_CLOCK,
209
251
  HEADER_SCHEMA_VERSION,
210
252
  HEADER_TIMESTAMP,
211
253
  HEADER_TRACEPARENT,
254
+ InMemoryDedupStore,
255
+ InMemoryOutboxStore,
212
256
  InjectKafkaClient,
257
+ JsonSerde,
213
258
  KAFKA_CLIENT,
214
259
  KAFKA_SUBSCRIBER_METADATA,
215
260
  KafkaClient,
@@ -219,13 +264,27 @@ export {
219
264
  KafkaProcessingError,
220
265
  KafkaRetryExhaustedError,
221
266
  KafkaValidationError,
267
+ SchemaRegistryClient,
222
268
  SubscribeTo,
269
+ awsMskIamProvider,
223
270
  buildEnvelopeHeaders,
271
+ consumerOptionsFromEnv,
224
272
  decodeHeaders,
273
+ describeRequiredAcls,
225
274
  extractEnvelope,
275
+ gcpAccessTokenProvider,
226
276
  getEnvelopeContext,
227
277
  getKafkaClientToken,
278
+ kafkaClientConfigFromEnv,
279
+ mergeConsumerOptions,
280
+ registrySchema,
281
+ resolveSecurityOptions,
228
282
  runWithEnvelopeContext,
229
- topic
283
+ startOutboxRelay,
284
+ toError,
285
+ toKafkaAclCommands,
286
+ toMskIamPolicy,
287
+ topic,
288
+ versionedSchema
230
289
  };
231
290
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/nest/kafka.module.ts","../src/nest/kafka.constants.ts","../src/nest/kafka.explorer.ts","../src/nest/kafka.decorator.ts","../src/nest/kafka.health.ts"],"sourcesContent":["import { Module, DynamicModule, Provider, Logger } from \"@nestjs/common\";\nimport { DiscoveryModule } from \"@nestjs/core\";\nimport {\n KafkaClient,\n ClientId,\n GroupId,\n TopicMapConstraint,\n KafkaInstrumentation,\n KafkaClientOptions,\n} from \"../client/kafka.client\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { KafkaExplorer } from \"./kafka.explorer\";\n\n/** Shared configuration fields for both `register()` and `registerAsync()`. */\ninterface KafkaModuleBaseOptions {\n /** Optional name for multi-client setups. Must match `@InjectKafkaClient(name)`. */\n name?: string;\n /** If true, makes KAFKA_CLIENT available globally without importing KafkaModule in every feature module. */\n isGlobal?: boolean;\n}\n\n/** Synchronous configuration for `KafkaModule.register()`. */\nexport interface KafkaModuleOptions extends KafkaModuleBaseOptions {\n /** Unique Kafka client identifier. */\n clientId: ClientId;\n /** Consumer group identifier. */\n groupId: GroupId;\n /** List of Kafka broker addresses. */\n brokers: string[];\n /** Auto-create topics via admin on first use (send/consume). Useful for development. */\n autoCreateTopics?: boolean;\n /** When `true`, string topic keys are validated against any schema previously registered via a TopicDescriptor. Default: `true`. */\n strictSchemas?: boolean;\n /** Number of partitions for auto-created topics. Default: `1`. */\n numPartitions?: number;\n /** Client-wide instrumentation hooks (e.g. OTel). Applied to both send and consume paths. */\n instrumentation?: KafkaInstrumentation[];\n /** Called when a message is dropped without being sent to a DLQ. @see `KafkaClientOptions.onMessageLost` */\n onMessageLost?: KafkaClientOptions[\"onMessageLost\"];\n /** Called whenever a consumer group rebalance occurs. @see `KafkaClientOptions.onRebalance` */\n onRebalance?: KafkaClientOptions[\"onRebalance\"];\n}\n\n/** Async configuration for `KafkaModule.registerAsync()` with dependency injection. */\nexport interface KafkaModuleAsyncOptions extends KafkaModuleBaseOptions {\n imports?: any[];\n useFactory: (\n ...args: any[]\n ) => KafkaModuleOptions | Promise<KafkaModuleOptions>;\n inject?: any[];\n}\n\n/**\n * NestJS dynamic module for registering type-safe Kafka clients.\n * Use `register()` for static config or `registerAsync()` for DI-based config.\n */\n@Module({})\nexport class KafkaModule {\n /** Register a Kafka client with static options. */\n static register<T extends TopicMapConstraint<T>>(\n options: KafkaModuleOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(options.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: () => KafkaModule.buildClient<T>(options),\n };\n\n return {\n global: options.isGlobal ?? false,\n module: KafkaModule,\n imports: [DiscoveryModule],\n providers: [kafkaClientProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n\n /** Register a Kafka client with async/factory-based options. */\n static registerAsync<T extends TopicMapConstraint<T>>(\n asyncOptions: KafkaModuleAsyncOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(asyncOptions.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: async (...args: any[]): Promise<KafkaClient<T>> =>\n KafkaModule.buildClient<T>(await asyncOptions.useFactory(...args)),\n inject: asyncOptions.inject || [],\n };\n\n return {\n global: asyncOptions.isGlobal ?? false,\n module: KafkaModule,\n imports: [...(asyncOptions.imports || []), DiscoveryModule],\n providers: [kafkaClientProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n\n private static async buildClient<T extends TopicMapConstraint<T>>(\n options: KafkaModuleOptions,\n ): Promise<KafkaClient<T>> {\n const client = new KafkaClient<T>(\n options.clientId,\n options.groupId,\n options.brokers,\n {\n autoCreateTopics: options.autoCreateTopics,\n strictSchemas: options.strictSchemas,\n numPartitions: options.numPartitions,\n instrumentation: options.instrumentation,\n onMessageLost: options.onMessageLost,\n onRebalance: options.onRebalance,\n logger: new Logger(`KafkaClient:${options.clientId}`),\n },\n );\n await client.connectProducer();\n return client;\n }\n}\n","/** Default DI token for the Kafka client. */\nexport const KAFKA_CLIENT = \"KAFKA_CLIENT\";\n\n/** Returns the DI token for a named (or default) Kafka client instance. */\nexport const getKafkaClientToken = (name?: string): string =>\n name ? `KAFKA_CLIENT_${name}` : KAFKA_CLIENT;\n","import { Inject, Injectable, OnModuleInit, Logger } from \"@nestjs/common\";\nimport { DiscoveryService, ModuleRef } from \"@nestjs/core\";\nimport { KafkaClient } from \"../client/kafka.client\";\nimport {\n KAFKA_SUBSCRIBER_METADATA,\n KafkaSubscriberMetadata,\n} from \"./kafka.decorator\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\n\ninterface SubscriberEntry extends KafkaSubscriberMetadata {\n methodName: string | symbol;\n}\n\n/** Discovers `@SubscribeTo()` decorators and wires them to their Kafka clients on startup. */\n@Injectable()\nexport class KafkaExplorer implements OnModuleInit {\n private readonly logger = new Logger(KafkaExplorer.name);\n\n constructor(\n @Inject(DiscoveryService)\n private readonly discoveryService: DiscoveryService,\n @Inject(ModuleRef)\n private readonly moduleRef: ModuleRef,\n ) {}\n\n /**\n * Scan all NestJS providers for `@SubscribeTo()` metadata and wire each decorated\n * method to its Kafka client via `startConsumer` or `startBatchConsumer`.\n *\n * Called automatically by the NestJS lifecycle — do not invoke manually.\n */\n async onModuleInit() {\n const providers = this.discoveryService.getProviders();\n\n for (const wrapper of providers) {\n const { instance } = wrapper;\n if (!instance || typeof instance !== \"object\") continue;\n\n const metadata: SubscriberEntry[] | undefined = Reflect.getMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n instance.constructor,\n );\n\n if (!metadata || metadata.length === 0) continue;\n\n for (const entry of metadata) {\n const token = getKafkaClientToken(entry.clientName);\n let client: KafkaClient<any>;\n\n try {\n client = this.moduleRef.get(token, { strict: false });\n } catch {\n this.logger.error(\n `KafkaClient \"${entry.clientName || \"default\"}\" not found for @SubscribeTo on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n continue;\n }\n\n const handler = (instance as any)[entry.methodName].bind(instance);\n\n const consumerOptions = { ...entry.options };\n if (entry.schemas) {\n consumerOptions.schemas = entry.schemas;\n }\n\n if (entry.batch) {\n await client.startBatchConsumer(\n entry.topics as any,\n async (envelopes: any[], meta: any) => {\n await handler(envelopes, meta);\n },\n consumerOptions,\n );\n } else {\n await client.startConsumer(\n entry.topics as any,\n async (envelope: any) => {\n await handler(envelope);\n },\n consumerOptions,\n );\n }\n\n this.logger.log(\n `Registered @SubscribeTo(${entry.topics.join(\", \")})${entry.batch ? \" [batch]\" : \"\"} on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n }\n }\n }\n}\n","import { Inject } from \"@nestjs/common\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { ConsumerOptions } from \"../client/kafka.client\";\nimport { TopicDescriptor, SchemaLike } from \"../client/message/topic\";\n\n/** Reflect metadata key used to store `@SubscribeTo` entries on a class constructor. */\nexport const KAFKA_SUBSCRIBER_METADATA = \"KAFKA_SUBSCRIBER_METADATA\";\n\n/** Internal shape stored per `@SubscribeTo()` decoration on a class. */\nexport interface KafkaSubscriberMetadata {\n /** Resolved topic name strings (descriptors are unwrapped to their `__topic` string). */\n topics: string[];\n /** Per-topic schema validators extracted from `TopicDescriptor` objects (if any). */\n schemas?: Map<string, SchemaLike>;\n /** Additional consumer options forwarded to `startConsumer` / `startBatchConsumer`. */\n options?: ConsumerOptions;\n /** Named client identifier — resolves to `KAFKA_CLIENT_<clientName>` in the DI container. */\n clientName?: string;\n /** When `true`, routes to `startBatchConsumer` instead of `startConsumer`. */\n batch?: boolean;\n /** Name of the decorated method on the provider class. */\n methodName?: string | symbol;\n}\n\n/** Inject a `KafkaClient` instance. Pass a name to target a specific named client. */\nexport const InjectKafkaClient = (name?: string): ParameterDecorator =>\n Inject(getKafkaClientToken(name));\n\n/**\n * Method decorator that auto-subscribes the decorated method to one or more Kafka topics\n * when the NestJS module initialises.\n *\n * The decorated method receives a fully-decoded `EventEnvelope` for each message\n * (or an array of envelopes + `BatchMeta` when `batch: true`).\n *\n * @param topics One or more topic names or `TopicDescriptor` objects. Schemas embedded in\n * descriptors are automatically extracted and forwarded to the consumer.\n * @param options Consumer and routing options:\n * - All `ConsumerOptions` fields (`groupId`, `retry`, `dlq`, `fromBeginning`, …)\n * - `clientName` — target a named `KafkaClient` (resolves `KAFKA_CLIENT_<name>` from the DI container)\n * - `batch` — use `startBatchConsumer` instead of `startConsumer`\n *\n * @example\n * ```ts\n * @SubscribeTo('orders.created', { groupId: 'orders-svc', retry: { maxRetries: 3 } })\n * async handleOrder(envelope: EventEnvelope<Order>) { ... }\n *\n * @SubscribeTo(OrdersTopic, { batch: true })\n * async handleBatch(envelopes: EventEnvelope<Order>[], meta: BatchMeta) { ... }\n * ```\n */\nexport const SubscribeTo = (\n topics:\n | string\n | string[]\n | TopicDescriptor\n | TopicDescriptor[]\n | (string | TopicDescriptor)[],\n options?: ConsumerOptions & { clientName?: string; batch?: boolean },\n): MethodDecorator => {\n const arr = Array.isArray(topics) ? topics : [topics];\n const topicsArray = arr.map((t) => (typeof t === \"string\" ? t : t.__topic));\n\n // Extract schemas from descriptors that have them\n const schemas = new Map<string, SchemaLike>();\n for (const t of arr) {\n if (typeof t !== \"string\" && t.__schema) {\n schemas.set(t.__topic, t.__schema);\n }\n }\n\n const { clientName, batch, ...consumerOptions } = options || {};\n\n return (target, propertyKey, _descriptor) => {\n const existing: KafkaSubscriberMetadata[] =\n Reflect.getMetadata(KAFKA_SUBSCRIBER_METADATA, target.constructor) || [];\n\n Reflect.defineMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n [\n ...existing,\n {\n topics: topicsArray,\n schemas: schemas.size > 0 ? schemas : undefined,\n options: Object.keys(consumerOptions).length\n ? consumerOptions\n : undefined,\n clientName,\n batch,\n methodName: propertyKey,\n },\n ],\n target.constructor,\n );\n };\n};\n","import { Injectable } from \"@nestjs/common\";\nimport type {\n IKafkaClient,\n KafkaHealthResult,\n TopicMapConstraint,\n} from \"../client/types\";\nexport type { KafkaHealthResult } from \"../client/types\";\n\n/** Health check service. Call `check(client)` to verify broker connectivity. */\n@Injectable()\nexport class KafkaHealthIndicator {\n async check<T extends TopicMapConstraint<T>>(\n client: IKafkaClient<T>,\n ): Promise<KafkaHealthResult> {\n return client.checkStatus();\n }\n}\n"],"mappings":"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","names":["Logger","Inject","Inject","Logger","Injectable","Injectable"]}
1
+ {"version":3,"sources":["../src/nest/kafka.module.ts","../src/nest/kafka.constants.ts","../src/nest/kafka.explorer.ts","../src/nest/kafka.decorator.ts","../src/nest/kafka.health.ts"],"sourcesContent":["import { Module, DynamicModule, Provider, Logger } from \"@nestjs/common\";\nimport { DiscoveryModule } from \"@nestjs/core\";\nimport {\n KafkaClient,\n ClientId,\n GroupId,\n TopicMapConstraint,\n KafkaInstrumentation,\n KafkaClientOptions,\n} from \"../client/kafka.client\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { KafkaExplorer } from \"./kafka.explorer\";\n\n/** Shared configuration fields for both `register()` and `registerAsync()`. */\ninterface KafkaModuleBaseOptions {\n /** Optional name for multi-client setups. Must match `@InjectKafkaClient(name)`. */\n name?: string;\n /** If true, makes KAFKA_CLIENT available globally without importing KafkaModule in every feature module. */\n isGlobal?: boolean;\n}\n\n/** Synchronous configuration for `KafkaModule.register()`. */\nexport interface KafkaModuleOptions extends KafkaModuleBaseOptions {\n /** Unique Kafka client identifier. */\n clientId: ClientId;\n /** Consumer group identifier. */\n groupId: GroupId;\n /** List of Kafka broker addresses. */\n brokers: string[];\n /** Auto-create topics via admin on first use (send/consume). Useful for development. */\n autoCreateTopics?: boolean;\n /** When `true`, string topic keys are validated against any schema previously registered via a TopicDescriptor. Default: `true`. */\n strictSchemas?: boolean;\n /** Number of partitions for auto-created topics. Default: `1`. */\n numPartitions?: number;\n /** Client-wide instrumentation hooks (e.g. OTel). Applied to both send and consume paths. */\n instrumentation?: KafkaInstrumentation[];\n /** Called when a message is dropped without being sent to a DLQ. @see `KafkaClientOptions.onMessageLost` */\n onMessageLost?: KafkaClientOptions[\"onMessageLost\"];\n /** Called whenever a consumer group rebalance occurs. @see `KafkaClientOptions.onRebalance` */\n onRebalance?: KafkaClientOptions[\"onRebalance\"];\n /** Transactional producer ID — must be unique per process/replica. @see `KafkaClientOptions.transactionalId` */\n transactionalId?: KafkaClientOptions[\"transactionalId\"];\n /** Recover the Lamport clock from these topics on startup. @see `KafkaClientOptions.clockRecovery` */\n clockRecovery?: KafkaClientOptions[\"clockRecovery\"];\n /** Delay producer sends when consumer lag exceeds a threshold. @see `KafkaClientOptions.lagThrottle` */\n lagThrottle?: KafkaClientOptions[\"lagThrottle\"];\n /** Client-wide TTL expiry callback. @see `KafkaClientOptions.onTtlExpired` */\n onTtlExpired?: KafkaClientOptions[\"onTtlExpired\"];\n /** Custom transport implementation (e.g. `FakeTransport` in tests). @see `KafkaClientOptions.transport` */\n transport?: KafkaClientOptions[\"transport\"];\n /** Transport security (TLS + SASL, incl. MSK IAM / GCP OAUTHBEARER). @see `KafkaClientOptions.security` */\n security?: KafkaClientOptions[\"security\"];\n}\n\n/** Async configuration for `KafkaModule.registerAsync()` with dependency injection. */\nexport interface KafkaModuleAsyncOptions extends KafkaModuleBaseOptions {\n imports?: any[];\n useFactory: (\n ...args: any[]\n ) => KafkaModuleOptions | Promise<KafkaModuleOptions>;\n inject?: any[];\n}\n\n/**\n * NestJS dynamic module for registering type-safe Kafka clients.\n * Use `register()` for static config or `registerAsync()` for DI-based config.\n */\n@Module({})\nexport class KafkaModule {\n /** Register a Kafka client with static options. */\n static register<T extends TopicMapConstraint<T>>(\n options: KafkaModuleOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(options.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: () => KafkaModule.buildClient<T>(options),\n };\n\n return {\n global: options.isGlobal ?? false,\n module: KafkaModule,\n imports: [DiscoveryModule],\n providers: [kafkaClientProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n\n /** Register a Kafka client with async/factory-based options. */\n static registerAsync<T extends TopicMapConstraint<T>>(\n asyncOptions: KafkaModuleAsyncOptions,\n ): DynamicModule {\n const token = getKafkaClientToken(asyncOptions.name);\n\n const kafkaClientProvider: Provider = {\n provide: token,\n useFactory: async (...args: any[]): Promise<KafkaClient<T>> =>\n KafkaModule.buildClient<T>(await asyncOptions.useFactory(...args)),\n inject: asyncOptions.inject || [],\n };\n\n return {\n global: asyncOptions.isGlobal ?? false,\n module: KafkaModule,\n imports: [...(asyncOptions.imports || []), DiscoveryModule],\n providers: [kafkaClientProvider, KafkaExplorer],\n exports: [kafkaClientProvider],\n };\n }\n\n private static async buildClient<T extends TopicMapConstraint<T>>(\n options: KafkaModuleOptions,\n ): Promise<KafkaClient<T>> {\n const client = new KafkaClient<T>(\n options.clientId,\n options.groupId,\n options.brokers,\n {\n autoCreateTopics: options.autoCreateTopics,\n strictSchemas: options.strictSchemas,\n numPartitions: options.numPartitions,\n instrumentation: options.instrumentation,\n onMessageLost: options.onMessageLost,\n onRebalance: options.onRebalance,\n transactionalId: options.transactionalId,\n clockRecovery: options.clockRecovery,\n lagThrottle: options.lagThrottle,\n onTtlExpired: options.onTtlExpired,\n transport: options.transport,\n security: options.security,\n logger: new Logger(`KafkaClient:${options.clientId}`),\n },\n );\n await client.connectProducer();\n return client;\n }\n}\n","/** Default DI token for the Kafka client. */\nexport const KAFKA_CLIENT = \"KAFKA_CLIENT\";\n\n/** Returns the DI token for a named (or default) Kafka client instance. */\nexport const getKafkaClientToken = (name?: string): string =>\n name ? `KAFKA_CLIENT_${name}` : KAFKA_CLIENT;\n","import { Inject, Injectable, OnModuleInit, Logger } from \"@nestjs/common\";\nimport { DiscoveryService, ModuleRef } from \"@nestjs/core\";\nimport { KafkaClient } from \"../client/kafka.client\";\nimport {\n KAFKA_SUBSCRIBER_METADATA,\n KafkaSubscriberMetadata,\n} from \"./kafka.decorator\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\n\ninterface SubscriberEntry extends KafkaSubscriberMetadata {\n methodName: string | symbol;\n}\n\n/**\n * Process-level registry of already-wired subscriptions, keyed by provider\n * instance. Every `KafkaModule.register()` call contributes its own\n * `KafkaExplorer`, and each explorer scans ALL providers — without this guard\n * a multi-client app would wire every `@SubscribeTo` handler once per\n * registered module (duplicate consumers / \"called twice\" startup errors).\n * Keyed by instance (not constructor) so separate Nest apps in one process\n * (e.g. tests) still wire their own instances independently.\n */\nconst wiredSubscriptions = new WeakMap<object, Set<string>>();\n\n/** Discovers `@SubscribeTo()` decorators and wires them to their Kafka clients on startup. */\n@Injectable()\nexport class KafkaExplorer implements OnModuleInit {\n private readonly logger = new Logger(KafkaExplorer.name);\n\n constructor(\n @Inject(DiscoveryService)\n private readonly discoveryService: DiscoveryService,\n @Inject(ModuleRef)\n private readonly moduleRef: ModuleRef,\n ) {}\n\n /**\n * Scan all NestJS providers for `@SubscribeTo()` metadata and wire each decorated\n * method to its Kafka client via `startConsumer` or `startBatchConsumer`.\n *\n * Called automatically by the NestJS lifecycle — do not invoke manually.\n */\n async onModuleInit() {\n const providers = this.discoveryService.getProviders();\n\n for (const wrapper of providers) {\n const { instance } = wrapper;\n if (!instance || typeof instance !== \"object\") continue;\n\n const metadata: SubscriberEntry[] | undefined = Reflect.getMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n instance.constructor,\n );\n\n if (!metadata || metadata.length === 0) continue;\n\n for (const entry of metadata) {\n const token = getKafkaClientToken(entry.clientName);\n\n const entryKey = `${token}:${String(entry.methodName)}`;\n let wired = wiredSubscriptions.get(instance);\n if (!wired) {\n wired = new Set();\n wiredSubscriptions.set(instance, wired);\n }\n if (wired.has(entryKey)) continue; // already wired by another KafkaExplorer instance\n wired.add(entryKey);\n\n let client: KafkaClient<any>;\n\n try {\n client = this.moduleRef.get(token, { strict: false });\n } catch {\n this.logger.error(\n `KafkaClient \"${entry.clientName || \"default\"}\" not found for @SubscribeTo on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n continue;\n }\n\n const handler = (instance as any)[entry.methodName].bind(instance);\n\n const consumerOptions = { ...entry.options };\n if (entry.schemas) {\n consumerOptions.schemas = entry.schemas;\n }\n\n if (entry.batch) {\n await client.startBatchConsumer(\n entry.topics as any,\n async (envelopes: any[], meta: any) => {\n await handler(envelopes, meta);\n },\n consumerOptions,\n );\n } else {\n await client.startConsumer(\n entry.topics as any,\n async (envelope: any) => {\n await handler(envelope);\n },\n consumerOptions,\n );\n }\n\n this.logger.log(\n `Registered @SubscribeTo(${entry.topics.join(\", \")})${entry.batch ? \" [batch]\" : \"\"} on ${instance.constructor.name}.${String(entry.methodName)}`,\n );\n }\n }\n }\n}\n","import { Inject } from \"@nestjs/common\";\nimport { getKafkaClientToken } from \"./kafka.constants\";\nimport { ConsumerOptions } from \"../client/kafka.client\";\nimport { TopicDescriptor, SchemaLike } from \"../client/message/topic\";\n\n/** Reflect metadata key used to store `@SubscribeTo` entries on a class constructor. */\nexport const KAFKA_SUBSCRIBER_METADATA = \"KAFKA_SUBSCRIBER_METADATA\";\n\n/** Internal shape stored per `@SubscribeTo()` decoration on a class. */\nexport interface KafkaSubscriberMetadata {\n /** Resolved topic name strings (descriptors are unwrapped to their `__topic` string). */\n topics: string[];\n /** Per-topic schema validators extracted from `TopicDescriptor` objects (if any). */\n schemas?: Map<string, SchemaLike>;\n /** Additional consumer options forwarded to `startConsumer` / `startBatchConsumer`. */\n options?: ConsumerOptions;\n /** Named client identifier — resolves to `KAFKA_CLIENT_<clientName>` in the DI container. */\n clientName?: string;\n /** When `true`, routes to `startBatchConsumer` instead of `startConsumer`. */\n batch?: boolean;\n /** Name of the decorated method on the provider class. */\n methodName?: string | symbol;\n}\n\n/** Inject a `KafkaClient` instance. Pass a name to target a specific named client. */\nexport const InjectKafkaClient = (name?: string): ParameterDecorator =>\n Inject(getKafkaClientToken(name));\n\n/**\n * Method decorator that auto-subscribes the decorated method to one or more Kafka topics\n * when the NestJS module initialises.\n *\n * The decorated method receives a fully-decoded `EventEnvelope` for each message\n * (or an array of envelopes + `BatchMeta` when `batch: true`).\n *\n * @param topics One or more topic names or `TopicDescriptor` objects. Schemas embedded in\n * descriptors are automatically extracted and forwarded to the consumer.\n * @param options Consumer and routing options:\n * - All `ConsumerOptions` fields (`groupId`, `retry`, `dlq`, `fromBeginning`, …)\n * - `clientName` — target a named `KafkaClient` (resolves `KAFKA_CLIENT_<name>` from the DI container)\n * - `batch` — use `startBatchConsumer` instead of `startConsumer`\n *\n * @example\n * ```ts\n * @SubscribeTo('orders.created', { groupId: 'orders-svc', retry: { maxRetries: 3 } })\n * async handleOrder(envelope: EventEnvelope<Order>) { ... }\n *\n * @SubscribeTo(OrdersTopic, { batch: true })\n * async handleBatch(envelopes: EventEnvelope<Order>[], meta: BatchMeta) { ... }\n * ```\n */\nexport const SubscribeTo = (\n topics:\n | string\n | string[]\n | TopicDescriptor\n | TopicDescriptor[]\n | (string | TopicDescriptor)[],\n options?: ConsumerOptions & { clientName?: string; batch?: boolean },\n): MethodDecorator => {\n const arr = Array.isArray(topics) ? topics : [topics];\n const topicsArray = arr.map((t) => (typeof t === \"string\" ? t : t.__topic));\n\n // Extract schemas from descriptors that have them\n const schemas = new Map<string, SchemaLike>();\n for (const t of arr) {\n if (typeof t !== \"string\" && t.__schema) {\n schemas.set(t.__topic, t.__schema);\n }\n }\n\n const { clientName, batch, ...consumerOptions } = options || {};\n\n return (target, propertyKey, _descriptor) => {\n const existing: KafkaSubscriberMetadata[] =\n Reflect.getMetadata(KAFKA_SUBSCRIBER_METADATA, target.constructor) || [];\n\n Reflect.defineMetadata(\n KAFKA_SUBSCRIBER_METADATA,\n [\n ...existing,\n {\n topics: topicsArray,\n schemas: schemas.size > 0 ? schemas : undefined,\n options: Object.keys(consumerOptions).length\n ? consumerOptions\n : undefined,\n clientName,\n batch,\n methodName: propertyKey,\n },\n ],\n target.constructor,\n );\n };\n};\n","import { Injectable } from \"@nestjs/common\";\nimport type {\n IKafkaClient,\n KafkaHealthResult,\n TopicMapConstraint,\n} from \"../client/types\";\nexport type { KafkaHealthResult } from \"../client/types\";\n\n/** Health check service. Call `check(client)` to verify broker connectivity. */\n@Injectable()\nexport class KafkaHealthIndicator {\n async check<T extends TopicMapConstraint<T>>(\n client: IKafkaClient<T>,\n ): Promise<KafkaHealthResult> {\n return client.checkStatus();\n }\n}\n"],"mappings":"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","names":["Logger","Inject","Inject","Logger","Injectable","Injectable"]}
@@ -0,0 +1,5 @@
1
+ /** Default DI token for the Kafka client. */
2
+ export declare const KAFKA_CLIENT = "KAFKA_CLIENT";
3
+ /** Returns the DI token for a named (or default) Kafka client instance. */
4
+ export declare const getKafkaClientToken: (name?: string) => string;
5
+ //# sourceMappingURL=kafka.constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kafka.constants.d.ts","sourceRoot":"","sources":["../../src/nest/kafka.constants.ts"],"names":[],"mappings":"AAAA,6CAA6C;AAC7C,eAAO,MAAM,YAAY,iBAAiB,CAAC;AAE3C,2EAA2E;AAC3E,eAAO,MAAM,mBAAmB,GAAI,OAAO,MAAM,KAAG,MACN,CAAC"}
@@ -0,0 +1,49 @@
1
+ import { ConsumerOptions } from "../client/kafka.client";
2
+ import { TopicDescriptor, SchemaLike } from "../client/message/topic";
3
+ /** Reflect metadata key used to store `@SubscribeTo` entries on a class constructor. */
4
+ export declare const KAFKA_SUBSCRIBER_METADATA = "KAFKA_SUBSCRIBER_METADATA";
5
+ /** Internal shape stored per `@SubscribeTo()` decoration on a class. */
6
+ export interface KafkaSubscriberMetadata {
7
+ /** Resolved topic name strings (descriptors are unwrapped to their `__topic` string). */
8
+ topics: string[];
9
+ /** Per-topic schema validators extracted from `TopicDescriptor` objects (if any). */
10
+ schemas?: Map<string, SchemaLike>;
11
+ /** Additional consumer options forwarded to `startConsumer` / `startBatchConsumer`. */
12
+ options?: ConsumerOptions;
13
+ /** Named client identifier — resolves to `KAFKA_CLIENT_<clientName>` in the DI container. */
14
+ clientName?: string;
15
+ /** When `true`, routes to `startBatchConsumer` instead of `startConsumer`. */
16
+ batch?: boolean;
17
+ /** Name of the decorated method on the provider class. */
18
+ methodName?: string | symbol;
19
+ }
20
+ /** Inject a `KafkaClient` instance. Pass a name to target a specific named client. */
21
+ export declare const InjectKafkaClient: (name?: string) => ParameterDecorator;
22
+ /**
23
+ * Method decorator that auto-subscribes the decorated method to one or more Kafka topics
24
+ * when the NestJS module initialises.
25
+ *
26
+ * The decorated method receives a fully-decoded `EventEnvelope` for each message
27
+ * (or an array of envelopes + `BatchMeta` when `batch: true`).
28
+ *
29
+ * @param topics One or more topic names or `TopicDescriptor` objects. Schemas embedded in
30
+ * descriptors are automatically extracted and forwarded to the consumer.
31
+ * @param options Consumer and routing options:
32
+ * - All `ConsumerOptions` fields (`groupId`, `retry`, `dlq`, `fromBeginning`, …)
33
+ * - `clientName` — target a named `KafkaClient` (resolves `KAFKA_CLIENT_<name>` from the DI container)
34
+ * - `batch` — use `startBatchConsumer` instead of `startConsumer`
35
+ *
36
+ * @example
37
+ * ```ts
38
+ * @SubscribeTo('orders.created', { groupId: 'orders-svc', retry: { maxRetries: 3 } })
39
+ * async handleOrder(envelope: EventEnvelope<Order>) { ... }
40
+ *
41
+ * @SubscribeTo(OrdersTopic, { batch: true })
42
+ * async handleBatch(envelopes: EventEnvelope<Order>[], meta: BatchMeta) { ... }
43
+ * ```
44
+ */
45
+ export declare const SubscribeTo: (topics: string | string[] | TopicDescriptor | TopicDescriptor[] | (string | TopicDescriptor)[], options?: ConsumerOptions & {
46
+ clientName?: string;
47
+ batch?: boolean;
48
+ }) => MethodDecorator;
49
+ //# sourceMappingURL=kafka.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kafka.decorator.d.ts","sourceRoot":"","sources":["../../src/nest/kafka.decorator.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEtE,wFAAwF;AACxF,eAAO,MAAM,yBAAyB,8BAA8B,CAAC;AAErE,wEAAwE;AACxE,MAAM,WAAW,uBAAuB;IACtC,yFAAyF;IACzF,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,qFAAqF;IACrF,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAClC,uFAAuF;IACvF,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,8EAA8E;IAC9E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,0DAA0D;IAC1D,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC9B;AAED,sFAAsF;AACtF,eAAO,MAAM,iBAAiB,GAAI,OAAO,MAAM,KAAG,kBACf,CAAC;AAEpC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,WAAW,GACtB,QACI,MAAM,GACN,MAAM,EAAE,GACR,eAAe,GACf,eAAe,EAAE,GACjB,CAAC,MAAM,GAAG,eAAe,CAAC,EAAE,EAChC,UAAU,eAAe,GAAG;IAAE,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,KACnE,eAoCF,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { OnModuleInit } from "@nestjs/common";
2
+ import { DiscoveryService, ModuleRef } from "@nestjs/core";
3
+ /** Discovers `@SubscribeTo()` decorators and wires them to their Kafka clients on startup. */
4
+ export declare class KafkaExplorer implements OnModuleInit {
5
+ private readonly discoveryService;
6
+ private readonly moduleRef;
7
+ private readonly logger;
8
+ constructor(discoveryService: DiscoveryService, moduleRef: ModuleRef);
9
+ /**
10
+ * Scan all NestJS providers for `@SubscribeTo()` metadata and wire each decorated
11
+ * method to its Kafka client via `startConsumer` or `startBatchConsumer`.
12
+ *
13
+ * Called automatically by the NestJS lifecycle — do not invoke manually.
14
+ */
15
+ onModuleInit(): Promise<void>;
16
+ }
17
+ //# sourceMappingURL=kafka.explorer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kafka.explorer.d.ts","sourceRoot":"","sources":["../../src/nest/kafka.explorer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,YAAY,EAAU,MAAM,gBAAgB,CAAC;AAC1E,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAuB3D,8FAA8F;AAC9F,qBACa,aAAc,YAAW,YAAY;IAK9C,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAEjC,OAAO,CAAC,QAAQ,CAAC,SAAS;IAN5B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAkC;gBAItC,gBAAgB,EAAE,gBAAgB,EAElC,SAAS,EAAE,SAAS;IAGvC;;;;;OAKG;IACG,YAAY;CAoEnB"}
@@ -0,0 +1,7 @@
1
+ import type { IKafkaClient, KafkaHealthResult, TopicMapConstraint } from "../client/types";
2
+ export type { KafkaHealthResult } from "../client/types";
3
+ /** Health check service. Call `check(client)` to verify broker connectivity. */
4
+ export declare class KafkaHealthIndicator {
5
+ check<T extends TopicMapConstraint<T>>(client: IKafkaClient<T>): Promise<KafkaHealthResult>;
6
+ }
7
+ //# sourceMappingURL=kafka.health.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kafka.health.d.ts","sourceRoot":"","sources":["../../src/nest/kafka.health.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAEzD,gFAAgF;AAChF,qBACa,oBAAoB;IACzB,KAAK,CAAC,CAAC,SAAS,kBAAkB,CAAC,CAAC,CAAC,EACzC,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GACtB,OAAO,CAAC,iBAAiB,CAAC;CAG9B"}
@@ -0,0 +1,61 @@
1
+ import { DynamicModule } from "@nestjs/common";
2
+ import { ClientId, GroupId, TopicMapConstraint, KafkaInstrumentation, KafkaClientOptions } from "../client/kafka.client";
3
+ /** Shared configuration fields for both `register()` and `registerAsync()`. */
4
+ interface KafkaModuleBaseOptions {
5
+ /** Optional name for multi-client setups. Must match `@InjectKafkaClient(name)`. */
6
+ name?: string;
7
+ /** If true, makes KAFKA_CLIENT available globally without importing KafkaModule in every feature module. */
8
+ isGlobal?: boolean;
9
+ }
10
+ /** Synchronous configuration for `KafkaModule.register()`. */
11
+ export interface KafkaModuleOptions extends KafkaModuleBaseOptions {
12
+ /** Unique Kafka client identifier. */
13
+ clientId: ClientId;
14
+ /** Consumer group identifier. */
15
+ groupId: GroupId;
16
+ /** List of Kafka broker addresses. */
17
+ brokers: string[];
18
+ /** Auto-create topics via admin on first use (send/consume). Useful for development. */
19
+ autoCreateTopics?: boolean;
20
+ /** When `true`, string topic keys are validated against any schema previously registered via a TopicDescriptor. Default: `true`. */
21
+ strictSchemas?: boolean;
22
+ /** Number of partitions for auto-created topics. Default: `1`. */
23
+ numPartitions?: number;
24
+ /** Client-wide instrumentation hooks (e.g. OTel). Applied to both send and consume paths. */
25
+ instrumentation?: KafkaInstrumentation[];
26
+ /** Called when a message is dropped without being sent to a DLQ. @see `KafkaClientOptions.onMessageLost` */
27
+ onMessageLost?: KafkaClientOptions["onMessageLost"];
28
+ /** Called whenever a consumer group rebalance occurs. @see `KafkaClientOptions.onRebalance` */
29
+ onRebalance?: KafkaClientOptions["onRebalance"];
30
+ /** Transactional producer ID — must be unique per process/replica. @see `KafkaClientOptions.transactionalId` */
31
+ transactionalId?: KafkaClientOptions["transactionalId"];
32
+ /** Recover the Lamport clock from these topics on startup. @see `KafkaClientOptions.clockRecovery` */
33
+ clockRecovery?: KafkaClientOptions["clockRecovery"];
34
+ /** Delay producer sends when consumer lag exceeds a threshold. @see `KafkaClientOptions.lagThrottle` */
35
+ lagThrottle?: KafkaClientOptions["lagThrottle"];
36
+ /** Client-wide TTL expiry callback. @see `KafkaClientOptions.onTtlExpired` */
37
+ onTtlExpired?: KafkaClientOptions["onTtlExpired"];
38
+ /** Custom transport implementation (e.g. `FakeTransport` in tests). @see `KafkaClientOptions.transport` */
39
+ transport?: KafkaClientOptions["transport"];
40
+ /** Transport security (TLS + SASL, incl. MSK IAM / GCP OAUTHBEARER). @see `KafkaClientOptions.security` */
41
+ security?: KafkaClientOptions["security"];
42
+ }
43
+ /** Async configuration for `KafkaModule.registerAsync()` with dependency injection. */
44
+ export interface KafkaModuleAsyncOptions extends KafkaModuleBaseOptions {
45
+ imports?: any[];
46
+ useFactory: (...args: any[]) => KafkaModuleOptions | Promise<KafkaModuleOptions>;
47
+ inject?: any[];
48
+ }
49
+ /**
50
+ * NestJS dynamic module for registering type-safe Kafka clients.
51
+ * Use `register()` for static config or `registerAsync()` for DI-based config.
52
+ */
53
+ export declare class KafkaModule {
54
+ /** Register a Kafka client with static options. */
55
+ static register<T extends TopicMapConstraint<T>>(options: KafkaModuleOptions): DynamicModule;
56
+ /** Register a Kafka client with async/factory-based options. */
57
+ static registerAsync<T extends TopicMapConstraint<T>>(asyncOptions: KafkaModuleAsyncOptions): DynamicModule;
58
+ private static buildClient;
59
+ }
60
+ export {};
61
+ //# sourceMappingURL=kafka.module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kafka.module.d.ts","sourceRoot":"","sources":["../../src/nest/kafka.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,aAAa,EAAoB,MAAM,gBAAgB,CAAC;AAEzE,OAAO,EAEL,QAAQ,EACR,OAAO,EACP,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,wBAAwB,CAAC;AAIhC,+EAA+E;AAC/E,UAAU,sBAAsB;IAC9B,oFAAoF;IACpF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4GAA4G;IAC5G,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,8DAA8D;AAC9D,MAAM,WAAW,kBAAmB,SAAQ,sBAAsB;IAChE,sCAAsC;IACtC,QAAQ,EAAE,QAAQ,CAAC;IACnB,iCAAiC;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,sCAAsC;IACtC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,oIAAoI;IACpI,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,6FAA6F;IAC7F,eAAe,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACzC,4GAA4G;IAC5G,aAAa,CAAC,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;IACpD,+FAA+F;IAC/F,WAAW,CAAC,EAAE,kBAAkB,CAAC,aAAa,CAAC,CAAC;IAChD,gHAAgH;IAChH,eAAe,CAAC,EAAE,kBAAkB,CAAC,iBAAiB,CAAC,CAAC;IACxD,sGAAsG;IACtG,aAAa,CAAC,EAAE,kBAAkB,CAAC,eAAe,CAAC,CAAC;IACpD,wGAAwG;IACxG,WAAW,CAAC,EAAE,kBAAkB,CAAC,aAAa,CAAC,CAAC;IAChD,8EAA8E;IAC9E,YAAY,CAAC,EAAE,kBAAkB,CAAC,cAAc,CAAC,CAAC;IAClD,2GAA2G;IAC3G,SAAS,CAAC,EAAE,kBAAkB,CAAC,WAAW,CAAC,CAAC;IAC5C,2GAA2G;IAC3G,QAAQ,CAAC,EAAE,kBAAkB,CAAC,UAAU,CAAC,CAAC;CAC3C;AAED,uFAAuF;AACvF,MAAM,WAAW,uBAAwB,SAAQ,sBAAsB;IACrE,OAAO,CAAC,EAAE,GAAG,EAAE,CAAC;IAChB,UAAU,EAAE,CACV,GAAG,IAAI,EAAE,GAAG,EAAE,KACX,kBAAkB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACtD,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;CAChB;AAED;;;GAGG;AACH,qBACa,WAAW;IACtB,mDAAmD;IACnD,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,kBAAkB,CAAC,CAAC,CAAC,EAC7C,OAAO,EAAE,kBAAkB,GAC1B,aAAa;IAiBhB,gEAAgE;IAChE,MAAM,CAAC,aAAa,CAAC,CAAC,SAAS,kBAAkB,CAAC,CAAC,CAAC,EAClD,YAAY,EAAE,uBAAuB,GACpC,aAAa;mBAmBK,WAAW;CA0BjC"}
package/dist/otel.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- import { K as KafkaInstrumentation } from './consumer.types-fFCag3VJ.js';
2
-
1
+ import type { Meter } from "@opentelemetry/api";
2
+ import type { KafkaInstrumentation } from "./client/types";
3
+ import type { IKafkaAdmin } from "./client/types/admin.interface";
4
+ import type { TopicMapConstraint } from "./client/types/common";
3
5
  /**
4
6
  * Create a `KafkaInstrumentation` that automatically propagates
5
7
  * W3C Trace Context via Kafka headers.
@@ -22,6 +24,82 @@ import { K as KafkaInstrumentation } from './consumer.types-fFCag3VJ.js';
22
24
  * });
23
25
  * ```
24
26
  */
25
- declare function otelInstrumentation(): KafkaInstrumentation;
26
-
27
- export { otelInstrumentation };
27
+ export declare function otelInstrumentation(): KafkaInstrumentation;
28
+ /**
29
+ * Create a `KafkaInstrumentation` that records OpenTelemetry **metrics** for
30
+ * both the send and consume paths.
31
+ *
32
+ * Requires `@opentelemetry/api` as a peer dependency. Instruments are created
33
+ * once per instrumentation instance (not per message), so a single call to this
34
+ * factory registers all counters/histograms exactly once.
35
+ *
36
+ * Recorded instruments (all under meter `@drarzter/kafka-client`):
37
+ *
38
+ * | Instrument | Type | Attributes | Recorded in |
39
+ * |---|---|---|---|
40
+ * | `kafka.client.messages.sent` | Counter | `topic` | `afterSend` |
41
+ * | `kafka.client.messages.processed` | Counter | `topic` | `onMessage` |
42
+ * | `kafka.client.messages.retried` | Counter | `topic` | `onRetry` |
43
+ * | `kafka.client.messages.dlq` | Counter | `topic`, `reason` | `onDlq` |
44
+ * | `kafka.client.messages.duplicate` | Counter | `topic`, `strategy` | `onDuplicate` |
45
+ * | `kafka.client.consume.errors` | Counter | `topic` | `onConsumeError` |
46
+ * | `kafka.client.consume.duration` | Histogram (ms) | `topic` | `beforeConsume` → `cleanup()` |
47
+ *
48
+ * Composes with `otelInstrumentation()` (traces): list both in
49
+ * `instrumentation`. They share nothing and can be added in any order.
50
+ *
51
+ * @param options.meter - Override the meter used to create instruments.
52
+ * Defaults to `metrics.getMeter("@drarzter/kafka-client")`.
53
+ *
54
+ * @example
55
+ * ```ts
56
+ * import {
57
+ * otelInstrumentation,
58
+ * otelMetricsInstrumentation,
59
+ * } from '@drarzter/kafka-client/otel';
60
+ *
61
+ * const kafka = new KafkaClient('my-app', 'my-group', brokers, {
62
+ * instrumentation: [otelInstrumentation(), otelMetricsInstrumentation()],
63
+ * });
64
+ * ```
65
+ */
66
+ export declare function otelMetricsInstrumentation(options?: {
67
+ meter?: Meter;
68
+ }): KafkaInstrumentation;
69
+ /**
70
+ * Register an OpenTelemetry **ObservableGauge** `kafka.client.consumer.lag`
71
+ * that reports per-partition consumer lag by polling `kafka.getConsumerLag()`
72
+ * on each metric collection cycle.
73
+ *
74
+ * Requires `@opentelemetry/api` as a peer dependency. Accepts any object
75
+ * implementing the `IKafkaAdmin` sub-interface (i.e. any `KafkaClient`).
76
+ *
77
+ * The async callback swallows errors silently — a broker query failure during
78
+ * a collection cycle simply reports no lag samples for that cycle rather than
79
+ * throwing inside the OTel metric reader.
80
+ *
81
+ * Gauge attributes: `topic`, `partition`, and `groupId` (empty string when the
82
+ * client's default group is used).
83
+ *
84
+ * @param kafka - The client (or any `IKafkaAdmin`) to poll for lag.
85
+ * @param options.meter - Override the meter. Defaults to
86
+ * `metrics.getMeter("@drarzter/kafka-client")`.
87
+ * @param options.groupId - Consumer group to query. Defaults to the client's
88
+ * constructor group.
89
+ * @returns An unregister function that removes the observable callback. Call it
90
+ * on shutdown to stop observing.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * import { otelLagGauge } from '@drarzter/kafka-client/otel';
95
+ *
96
+ * const unregister = otelLagGauge(kafka, { groupId: 'billing-service' });
97
+ * // ...later, on shutdown:
98
+ * unregister();
99
+ * ```
100
+ */
101
+ export declare function otelLagGauge<T extends TopicMapConstraint<T>>(kafka: IKafkaAdmin<T>, options?: {
102
+ meter?: Meter;
103
+ groupId?: string;
104
+ }): () => void;
105
+ //# sourceMappingURL=otel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"otel.d.ts","sourceRoot":"","sources":["../src/otel.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,KAAK,EAAoB,MAAM,oBAAoB,CAAC;AAClE,OAAO,KAAK,EAAuB,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAChF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAGhE;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,mBAAmB,IAAI,oBAAoB,CA0D1D;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,CAAC,EAAE;IACnD,KAAK,CAAC,EAAE,KAAK,CAAC;CACf,GAAG,oBAAoB,CA4EvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,YAAY,CAAC,CAAC,SAAS,kBAAkB,CAAC,CAAC,CAAC,EAC1D,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,EACrB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,MAAM,IAAI,CA4BZ"}
package/dist/otel.js CHANGED
@@ -20,13 +20,15 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/otel.ts
21
21
  var otel_exports = {};
22
22
  __export(otel_exports, {
23
- otelInstrumentation: () => otelInstrumentation
23
+ otelInstrumentation: () => otelInstrumentation,
24
+ otelLagGauge: () => otelLagGauge,
25
+ otelMetricsInstrumentation: () => otelMetricsInstrumentation
24
26
  });
25
27
  module.exports = __toCommonJS(otel_exports);
26
28
  var import_api = require("@opentelemetry/api");
27
29
  function otelInstrumentation() {
28
30
  const tracer = import_api.trace.getTracer("@drarzter/kafka-client");
29
- const activeSpans = /* @__PURE__ */ new Map();
31
+ const activeSpans = /* @__PURE__ */ new WeakMap();
30
32
  return {
31
33
  beforeSend(_topic, headers) {
32
34
  import_api.propagation.inject(import_api.context.active(), headers);
@@ -50,11 +52,11 @@ function otelInstrumentation() {
50
52
  parentCtx
51
53
  );
52
54
  const spanCtx = import_api.trace.setSpan(parentCtx, span);
53
- activeSpans.set(envelope.eventId, span);
55
+ activeSpans.set(envelope, span);
54
56
  return {
55
57
  cleanup() {
56
58
  span.end();
57
- activeSpans.delete(envelope.eventId);
59
+ activeSpans.delete(envelope);
58
60
  },
59
61
  wrap(fn) {
60
62
  return import_api.context.with(spanCtx, fn);
@@ -62,7 +64,7 @@ function otelInstrumentation() {
62
64
  };
63
65
  },
64
66
  onConsumeError(envelope, error) {
65
- const span = activeSpans.get(envelope.eventId);
67
+ const span = activeSpans.get(envelope);
66
68
  if (span) {
67
69
  span.setStatus({ code: import_api.SpanStatusCode.ERROR, message: error.message });
68
70
  span.recordException(error);
@@ -70,8 +72,100 @@ function otelInstrumentation() {
70
72
  }
71
73
  };
72
74
  }
75
+ function otelMetricsInstrumentation(options) {
76
+ const meter = options?.meter ?? import_api.metrics.getMeter("@drarzter/kafka-client");
77
+ const sentCounter = meter.createCounter("kafka.client.messages.sent", {
78
+ description: "Number of messages successfully sent to Kafka."
79
+ });
80
+ const processedCounter = meter.createCounter(
81
+ "kafka.client.messages.processed",
82
+ {
83
+ description: "Number of messages successfully processed by a consumer."
84
+ }
85
+ );
86
+ const retriedCounter = meter.createCounter("kafka.client.messages.retried", {
87
+ description: "Number of messages queued for retry."
88
+ });
89
+ const dlqCounter = meter.createCounter("kafka.client.messages.dlq", {
90
+ description: "Number of messages routed to a DLQ topic."
91
+ });
92
+ const duplicateCounter = meter.createCounter(
93
+ "kafka.client.messages.duplicate",
94
+ {
95
+ description: "Number of Lamport-clock duplicate messages detected."
96
+ }
97
+ );
98
+ const consumeErrorsCounter = meter.createCounter(
99
+ "kafka.client.consume.errors",
100
+ {
101
+ description: "Number of consumer handler errors."
102
+ }
103
+ );
104
+ const consumeDuration = meter.createHistogram(
105
+ "kafka.client.consume.duration",
106
+ {
107
+ description: "Consumer handler duration.",
108
+ unit: "ms"
109
+ }
110
+ );
111
+ return {
112
+ afterSend(topic) {
113
+ sentCounter.add(1, { topic });
114
+ },
115
+ beforeConsume(envelope) {
116
+ const topic = envelope.topic;
117
+ const start = Date.now();
118
+ return {
119
+ cleanup() {
120
+ consumeDuration.record(Date.now() - start, { topic });
121
+ }
122
+ };
123
+ },
124
+ onConsumeError(envelope, _error) {
125
+ consumeErrorsCounter.add(1, { topic: envelope.topic });
126
+ },
127
+ onRetry(envelope, _attempt, _max) {
128
+ retriedCounter.add(1, { topic: envelope.topic });
129
+ },
130
+ onDlq(envelope, reason) {
131
+ dlqCounter.add(1, { topic: envelope.topic, reason });
132
+ },
133
+ onDuplicate(envelope, strategy) {
134
+ duplicateCounter.add(1, { topic: envelope.topic, strategy });
135
+ },
136
+ onMessage(envelope) {
137
+ processedCounter.add(1, { topic: envelope.topic });
138
+ }
139
+ };
140
+ }
141
+ function otelLagGauge(kafka, options) {
142
+ const meter = options?.meter ?? import_api.metrics.getMeter("@drarzter/kafka-client");
143
+ const groupId = options?.groupId;
144
+ const gauge = meter.createObservableGauge("kafka.client.consumer.lag", {
145
+ description: "Consumer group lag per topic partition."
146
+ });
147
+ const callback = async (result) => {
148
+ try {
149
+ const lag = await kafka.getConsumerLag(groupId);
150
+ for (const entry of lag) {
151
+ result.observe(entry.lag, {
152
+ topic: entry.topic,
153
+ partition: entry.partition,
154
+ groupId: groupId ?? ""
155
+ });
156
+ }
157
+ } catch {
158
+ }
159
+ };
160
+ gauge.addCallback(callback);
161
+ return () => {
162
+ gauge.removeCallback(callback);
163
+ };
164
+ }
73
165
  // Annotate the CommonJS export names for ESM import in node:
74
166
  0 && (module.exports = {
75
- otelInstrumentation
167
+ otelInstrumentation,
168
+ otelLagGauge,
169
+ otelMetricsInstrumentation
76
170
  });
77
171
  //# sourceMappingURL=otel.js.map