@application-kit/kafka 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +95 -0
  3. package/dist/cjs/configs/envs/index.d.ts +2 -0
  4. package/dist/cjs/configs/envs/index.js +18 -0
  5. package/dist/cjs/configs/envs/kafka-config.environment.d.ts +9 -0
  6. package/dist/cjs/configs/envs/kafka-config.environment.js +64 -0
  7. package/dist/cjs/configs/envs/kafka-consumer.environment.d.ts +3 -0
  8. package/dist/cjs/configs/envs/kafka-consumer.environment.js +27 -0
  9. package/dist/cjs/configs/index.d.ts +4 -0
  10. package/dist/cjs/configs/index.js +20 -0
  11. package/dist/cjs/configs/kafka-consumer.config.d.ts +9 -0
  12. package/dist/cjs/configs/kafka-consumer.config.js +50 -0
  13. package/dist/cjs/configs/kafka-producer.config.d.ts +7 -0
  14. package/dist/cjs/configs/kafka-producer.config.js +41 -0
  15. package/dist/cjs/configs/kafka.config.d.ts +7 -0
  16. package/dist/cjs/configs/kafka.config.js +52 -0
  17. package/dist/cjs/constants.d.ts +3 -0
  18. package/dist/cjs/constants.js +6 -0
  19. package/dist/cjs/event-contract-version.d.ts +5 -0
  20. package/dist/cjs/event-contract-version.js +20 -0
  21. package/dist/cjs/index.d.ts +7 -0
  22. package/dist/cjs/index.js +23 -0
  23. package/dist/cjs/kafka-commit-interceptor.d.ts +13 -0
  24. package/dist/cjs/kafka-commit-interceptor.js +87 -0
  25. package/dist/cjs/kafka-error-handler.d.ts +25 -0
  26. package/dist/cjs/kafka-error-handler.js +118 -0
  27. package/dist/cjs/kafka.module.d.ts +9 -0
  28. package/dist/cjs/kafka.module.js +67 -0
  29. package/dist/cjs/libs/consumer.abstract.d.ts +6 -0
  30. package/dist/cjs/libs/consumer.abstract.js +14 -0
  31. package/dist/cjs/libs/errors/consumers-error.d.ts +4 -0
  32. package/dist/cjs/libs/errors/consumers-error.js +12 -0
  33. package/dist/cjs/libs/errors/index.d.ts +1 -0
  34. package/dist/cjs/libs/errors/index.js +17 -0
  35. package/dist/cjs/libs/heartbeat-file-writer.d.ts +6 -0
  36. package/dist/cjs/libs/heartbeat-file-writer.js +24 -0
  37. package/dist/cjs/libs/index.d.ts +9 -0
  38. package/dist/cjs/libs/index.js +25 -0
  39. package/dist/cjs/libs/kafka-consumer-heartbeat.d.ts +3 -0
  40. package/dist/cjs/libs/kafka-consumer-heartbeat.js +13 -0
  41. package/dist/cjs/libs/kafka-consumer-server.d.ts +2 -0
  42. package/dist/cjs/libs/kafka-consumer-server.js +25 -0
  43. package/dist/cjs/libs/kafka-logger.d.ts +5 -0
  44. package/dist/cjs/libs/kafka-logger.interface.d.ts +5 -0
  45. package/dist/cjs/libs/kafka-logger.interface.js +2 -0
  46. package/dist/cjs/libs/kafka-logger.js +59 -0
  47. package/dist/cjs/libs/kafka-message-context.d.ts +6 -0
  48. package/dist/cjs/libs/kafka-message-context.js +11 -0
  49. package/dist/cjs/libs/topic-suffixes.d.ts +1 -0
  50. package/dist/cjs/libs/topic-suffixes.js +10 -0
  51. package/dist/cjs/package.json +3 -0
  52. package/dist/esm/configs/envs/index.d.ts +2 -0
  53. package/dist/esm/configs/envs/index.js +2 -0
  54. package/dist/esm/configs/envs/kafka-config.environment.d.ts +9 -0
  55. package/dist/esm/configs/envs/kafka-config.environment.js +60 -0
  56. package/dist/esm/configs/envs/kafka-consumer.environment.d.ts +3 -0
  57. package/dist/esm/configs/envs/kafka-consumer.environment.js +24 -0
  58. package/dist/esm/configs/index.d.ts +4 -0
  59. package/dist/esm/configs/index.js +4 -0
  60. package/dist/esm/configs/kafka-consumer.config.d.ts +9 -0
  61. package/dist/esm/configs/kafka-consumer.config.js +47 -0
  62. package/dist/esm/configs/kafka-producer.config.d.ts +7 -0
  63. package/dist/esm/configs/kafka-producer.config.js +38 -0
  64. package/dist/esm/configs/kafka.config.d.ts +7 -0
  65. package/dist/esm/configs/kafka.config.js +49 -0
  66. package/dist/esm/constants.d.ts +3 -0
  67. package/dist/esm/constants.js +3 -0
  68. package/dist/esm/event-contract-version.d.ts +5 -0
  69. package/dist/esm/event-contract-version.js +14 -0
  70. package/dist/esm/index.d.ts +7 -0
  71. package/dist/esm/index.js +7 -0
  72. package/dist/esm/kafka-commit-interceptor.d.ts +13 -0
  73. package/dist/esm/kafka-commit-interceptor.js +83 -0
  74. package/dist/esm/kafka-error-handler.d.ts +25 -0
  75. package/dist/esm/kafka-error-handler.js +115 -0
  76. package/dist/esm/kafka.module.d.ts +9 -0
  77. package/dist/esm/kafka.module.js +64 -0
  78. package/dist/esm/libs/consumer.abstract.d.ts +6 -0
  79. package/dist/esm/libs/consumer.abstract.js +10 -0
  80. package/dist/esm/libs/errors/consumers-error.d.ts +4 -0
  81. package/dist/esm/libs/errors/consumers-error.js +8 -0
  82. package/dist/esm/libs/errors/index.d.ts +1 -0
  83. package/dist/esm/libs/errors/index.js +1 -0
  84. package/dist/esm/libs/heartbeat-file-writer.d.ts +6 -0
  85. package/dist/esm/libs/heartbeat-file-writer.js +20 -0
  86. package/dist/esm/libs/index.d.ts +9 -0
  87. package/dist/esm/libs/index.js +9 -0
  88. package/dist/esm/libs/kafka-consumer-heartbeat.d.ts +3 -0
  89. package/dist/esm/libs/kafka-consumer-heartbeat.js +9 -0
  90. package/dist/esm/libs/kafka-consumer-server.d.ts +2 -0
  91. package/dist/esm/libs/kafka-consumer-server.js +18 -0
  92. package/dist/esm/libs/kafka-logger.d.ts +5 -0
  93. package/dist/esm/libs/kafka-logger.interface.d.ts +5 -0
  94. package/dist/esm/libs/kafka-logger.interface.js +1 -0
  95. package/dist/esm/libs/kafka-logger.js +23 -0
  96. package/dist/esm/libs/kafka-message-context.d.ts +6 -0
  97. package/dist/esm/libs/kafka-message-context.js +7 -0
  98. package/dist/esm/libs/topic-suffixes.d.ts +1 -0
  99. package/dist/esm/libs/topic-suffixes.js +6 -0
  100. package/package.json +85 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 application-kit
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # @application-kit/kafka
2
+
3
+ Kafka-транспорт для NestJS: бутстрап консьюмер-процесса с heartbeat-файлом, труба потребления
4
+ (ручной commit offset'а, retry и DLQ), имена топиков и контракт версии события, общий для
5
+ производителя (outbox + Debezium EventRouter) и потребителя.
6
+
7
+ ## Установка
8
+
9
+ ```sh
10
+ yarn add @application-kit/kafka
11
+ ```
12
+
13
+ Peer-зависимости: `@nestjs/common`, `@nestjs/core`, `@nestjs/microservices`, `kafkajs`, `rxjs`,
14
+ `@libs-for-dev/nestjs-config-module`, `class-transformer`, `class-validator`, `nestjs-pino`.
15
+
16
+ Пакет грузится одним экземпляром на процесс: DI-токены — `Symbol()`, и ESM- и CJS-копии разом
17
+ дали бы два набора токенов.
18
+
19
+ ## Окружение
20
+
21
+ | Переменная | Назначение |
22
+ |---|---|
23
+ | `KAFKA_BROKERS` | список брокеров через запятую |
24
+ | `KAFKA_CLIENT_ID` | префикс `clientId` (к нему добавляется uuid процесса) |
25
+ | `KAFKA_GROUP_ID` | группа консьюмера — нужна только консьюмер-процессу |
26
+ | `KAFKA_LOG_LEVEL` | уровень kafkajs: `NOTHING`, `ERROR`, `WARN`, `INFO` (дефолт), `DEBUG` |
27
+ | `KAFKA_SASL_USERNAME`, `KAFKA_SASL_PASSWORD` | SCRAM-SHA-512; не заданы — без аутентификации |
28
+ | `KAFKA_SSL_CA_FILE` | путь к CA брокера; не задан — без TLS |
29
+ | `HEARTBEAT_FILE` | файл пульса для exec-liveness; не задан — пульс не пишется |
30
+
31
+ Переменные валидируются при старте: с неверным окружением процесс не поднимается.
32
+
33
+ ## Консьюмер-процесс
34
+
35
+ ```ts
36
+ import { KafkaLogger, KafkaModule, kafkaConsumerServer } from '@application-kit/kafka'
37
+ import { Module } from '@nestjs/common'
38
+ import { Logger } from 'nestjs-pino'
39
+
40
+ @Module({
41
+ imports: [
42
+ // logger — класс, а не экземпляр: экземпляр, созданный на импорте файла, забрал бы себе
43
+ // корневой логгер nestjs-pino с дефолтными опциями.
44
+ KafkaModule.forRootAsync({ logger: KafkaLogger }),
45
+ // …модули с консьюмерами
46
+ ],
47
+ })
48
+ class ConsumerModule {}
49
+
50
+ void kafkaConsumerServer(ConsumerModule, Logger)
51
+ ```
52
+
53
+ `kafkaConsumerServer` поднимает Nest-микросервис и обновляет `HEARTBEAT_FILE` на событиях kafkajs
54
+ `HEARTBEAT` и `GROUP_JOIN`: зависшая группа перестаёт слать пульс, файл протухает, и
55
+ оркестратор перезапускает процесс по `mtime`.
56
+
57
+ `forRootAsync` принимает `errorHandlerOptions` — `maxAttempts` (5), `baseDelayMs` (500) и
58
+ `topicSuffix` (`retry`, `dlq`). `KafkaLogger` — реализация `KafkaLoggerInterface` на `nestjs-pino`;
59
+ свой логгер — любой класс с `error`, `info`, `warn`.
60
+
61
+ ## Консьюмер
62
+
63
+ ```ts
64
+ import {
65
+ ConsumerAbstract,
66
+ eventIdOf,
67
+ eventTopics,
68
+ KafkaAutoCommit,
69
+ type KafkaMessageContextInterface,
70
+ } from '@application-kit/kafka'
71
+
72
+ @Controller()
73
+ export class UserConsumer extends ConsumerAbstract {
74
+ @EventPattern(eventTopics(UserWasCreatedEvent.queueName))
75
+ @KafkaAutoCommit()
76
+ public async created(@Payload() payload: UserDto, @Ctx() context: KafkaMessageContextInterface) {
77
+ await this.handleAll(payload, [this.welcomeLetter, this.audit])
78
+ }
79
+ }
80
+ ```
81
+
82
+ - `eventTopics(queue, version?)` — пара топиков очереди: `events.<queue>` и его `.retry`.
83
+ - `@KafkaAutoCommit()` коммитит offset после успешной обработки; упавшее сообщение уходит в
84
+ retry-топик с растущей задержкой, после `maxAttempts` — в DLQ.
85
+ - `ConsumerAbstract.handleAll` запускает обработчики (`HandlerInterface<TPayload>`) параллельно;
86
+ отказ любого — `ConsumersError`, сообщение переигрывается целиком.
87
+ - `eventIdOf(context)` — идентификатор события из заголовка `id` (его ставит Debezium EventRouter).
88
+
89
+ ## Версия контракта события
90
+
91
+ Несовместимая смена контракта уводит события в свою очередь `<queue>.v<N>`; первая версия живёт
92
+ в топике без сегмента. Производитель собирает имя очереди `versionedQueueName(queue, version)`,
93
+ потребитель подписывается `eventTopics(queue, version)`, а труба сверяет версию топика
94
+ (`eventVersionOfTopic`) с заголовком `x-event-version` (`eventVersionOfHeaders`) до вызова
95
+ обработчика: сообщение чужой версии уходит в DLQ, не доходя до кода потребителя.
@@ -0,0 +1,2 @@
1
+ export * from './kafka-config.environment.js';
2
+ export * from './kafka-consumer.environment.js';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./kafka-config.environment.js"), exports);
18
+ __exportStar(require("./kafka-consumer.environment.js"), exports);
@@ -0,0 +1,9 @@
1
+ import { logLevel } from '@nestjs/microservices/external/kafka.interface.js';
2
+ export declare class KafkaConfigEnvironment {
3
+ KAFKA_BROKERS: string;
4
+ KAFKA_CLIENT_ID: string;
5
+ KAFKA_LOG_LEVEL: keyof typeof logLevel;
6
+ KAFKA_SASL_PASSWORD?: string;
7
+ KAFKA_SASL_USERNAME?: string;
8
+ KAFKA_SSL_CA_FILE?: string;
9
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.KafkaConfigEnvironment = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ var KafkaLogLevel;
16
+ (function (KafkaLogLevel) {
17
+ KafkaLogLevel["DEBUG"] = "DEBUG";
18
+ KafkaLogLevel["ERROR"] = "ERROR";
19
+ KafkaLogLevel["INFO"] = "INFO";
20
+ KafkaLogLevel["NOTHING"] = "NOTHING";
21
+ KafkaLogLevel["WARN"] = "WARN";
22
+ })(KafkaLogLevel || (KafkaLogLevel = {}));
23
+ class KafkaConfigEnvironment {
24
+ KAFKA_BROKERS;
25
+ KAFKA_CLIENT_ID;
26
+ KAFKA_LOG_LEVEL = KafkaLogLevel.INFO;
27
+ KAFKA_SASL_PASSWORD;
28
+ KAFKA_SASL_USERNAME;
29
+ KAFKA_SSL_CA_FILE;
30
+ }
31
+ exports.KafkaConfigEnvironment = KafkaConfigEnvironment;
32
+ __decorate([
33
+ (0, class_transformer_1.Expose)(),
34
+ (0, class_validator_1.IsString)(),
35
+ __metadata("design:type", String)
36
+ ], KafkaConfigEnvironment.prototype, "KAFKA_BROKERS", void 0);
37
+ __decorate([
38
+ (0, class_transformer_1.Expose)(),
39
+ (0, class_validator_1.IsString)(),
40
+ __metadata("design:type", String)
41
+ ], KafkaConfigEnvironment.prototype, "KAFKA_CLIENT_ID", void 0);
42
+ __decorate([
43
+ (0, class_transformer_1.Expose)(),
44
+ (0, class_validator_1.IsEnum)(KafkaLogLevel),
45
+ __metadata("design:type", Object)
46
+ ], KafkaConfigEnvironment.prototype, "KAFKA_LOG_LEVEL", void 0);
47
+ __decorate([
48
+ (0, class_transformer_1.Expose)(),
49
+ (0, class_validator_1.IsOptional)(),
50
+ (0, class_validator_1.IsString)(),
51
+ __metadata("design:type", String)
52
+ ], KafkaConfigEnvironment.prototype, "KAFKA_SASL_PASSWORD", void 0);
53
+ __decorate([
54
+ (0, class_transformer_1.Expose)(),
55
+ (0, class_validator_1.IsOptional)(),
56
+ (0, class_validator_1.IsString)(),
57
+ __metadata("design:type", String)
58
+ ], KafkaConfigEnvironment.prototype, "KAFKA_SASL_USERNAME", void 0);
59
+ __decorate([
60
+ (0, class_transformer_1.Expose)(),
61
+ (0, class_validator_1.IsOptional)(),
62
+ (0, class_validator_1.IsString)(),
63
+ __metadata("design:type", String)
64
+ ], KafkaConfigEnvironment.prototype, "KAFKA_SSL_CA_FILE", void 0);
@@ -0,0 +1,3 @@
1
+ export declare class KafkaConsumerEnvironment {
2
+ KAFKA_GROUP_ID: string;
3
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.KafkaConsumerEnvironment = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const class_transformer_1 = require("class-transformer");
15
+ const class_validator_1 = require("class-validator");
16
+ let KafkaConsumerEnvironment = class KafkaConsumerEnvironment {
17
+ KAFKA_GROUP_ID;
18
+ };
19
+ exports.KafkaConsumerEnvironment = KafkaConsumerEnvironment;
20
+ __decorate([
21
+ (0, class_transformer_1.Expose)(),
22
+ (0, class_validator_1.IsString)(),
23
+ __metadata("design:type", String)
24
+ ], KafkaConsumerEnvironment.prototype, "KAFKA_GROUP_ID", void 0);
25
+ exports.KafkaConsumerEnvironment = KafkaConsumerEnvironment = __decorate([
26
+ (0, common_1.Injectable)()
27
+ ], KafkaConsumerEnvironment);
@@ -0,0 +1,4 @@
1
+ export * from './envs/index.js';
2
+ export * from './kafka-consumer.config.js';
3
+ export * from './kafka-producer.config.js';
4
+ export * from './kafka.config.js';
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./envs/index.js"), exports);
18
+ __exportStar(require("./kafka-consumer.config.js"), exports);
19
+ __exportStar(require("./kafka-producer.config.js"), exports);
20
+ __exportStar(require("./kafka.config.js"), exports);
@@ -0,0 +1,9 @@
1
+ import { KafkaOptions } from '@nestjs/microservices';
2
+ import { KafkaConsumerEnvironment } from './envs/kafka-consumer.environment.js';
3
+ import { KafkaConfig } from './kafka.config.js';
4
+ export declare class KafkaConsumerConfig {
5
+ private readonly kafkaConfig;
6
+ private readonly kafkaConsumerEnvironment;
7
+ constructor(kafkaConfig: KafkaConfig, kafkaConsumerEnvironment: KafkaConsumerEnvironment);
8
+ getOptions(): KafkaOptions;
9
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.KafkaConsumerConfig = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const kafka_consumer_environment_js_1 = require("./envs/kafka-consumer.environment.js");
15
+ const kafka_config_js_1 = require("./kafka.config.js");
16
+ let KafkaConsumerConfig = class KafkaConsumerConfig {
17
+ kafkaConfig;
18
+ kafkaConsumerEnvironment;
19
+ constructor(kafkaConfig, kafkaConsumerEnvironment) {
20
+ this.kafkaConfig = kafkaConfig;
21
+ this.kafkaConsumerEnvironment = kafkaConsumerEnvironment;
22
+ }
23
+ getOptions() {
24
+ const options = this.kafkaConfig.getOptions();
25
+ options.options = {
26
+ ...options.options,
27
+ consumer: {
28
+ allowAutoTopicCreation: true,
29
+ groupId: this.kafkaConsumerEnvironment.KAFKA_GROUP_ID,
30
+ retry: {
31
+ initialRetryTime: 100,
32
+ retries: 0,
33
+ },
34
+ },
35
+ run: {
36
+ autoCommit: false,
37
+ },
38
+ subscribe: {
39
+ fromBeginning: true,
40
+ },
41
+ };
42
+ return options;
43
+ }
44
+ };
45
+ exports.KafkaConsumerConfig = KafkaConsumerConfig;
46
+ exports.KafkaConsumerConfig = KafkaConsumerConfig = __decorate([
47
+ (0, common_1.Injectable)(),
48
+ __metadata("design:paramtypes", [kafka_config_js_1.KafkaConfig,
49
+ kafka_consumer_environment_js_1.KafkaConsumerEnvironment])
50
+ ], KafkaConsumerConfig);
@@ -0,0 +1,7 @@
1
+ import { KafkaOptions } from '@nestjs/microservices';
2
+ import { KafkaConfig } from './kafka.config.js';
3
+ export declare class KafkaProducerConfig {
4
+ private readonly kafkaConfig;
5
+ constructor(kafkaConfig: KafkaConfig);
6
+ getOptions(): KafkaOptions;
7
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.KafkaProducerConfig = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const kafkajs_1 = require("kafkajs");
15
+ const kafka_config_js_1 = require("./kafka.config.js");
16
+ let KafkaProducerConfig = class KafkaProducerConfig {
17
+ kafkaConfig;
18
+ constructor(kafkaConfig) {
19
+ this.kafkaConfig = kafkaConfig;
20
+ }
21
+ getOptions() {
22
+ const options = this.kafkaConfig.getOptions();
23
+ options.options = {
24
+ ...options.options,
25
+ producer: {
26
+ allowAutoTopicCreation: true,
27
+ createPartitioner: kafkajs_1.Partitioners.DefaultPartitioner,
28
+ retry: {
29
+ retries: 1,
30
+ },
31
+ },
32
+ producerOnlyMode: true,
33
+ };
34
+ return options;
35
+ }
36
+ };
37
+ exports.KafkaProducerConfig = KafkaProducerConfig;
38
+ exports.KafkaProducerConfig = KafkaProducerConfig = __decorate([
39
+ (0, common_1.Injectable)(),
40
+ __metadata("design:paramtypes", [kafka_config_js_1.KafkaConfig])
41
+ ], KafkaProducerConfig);
@@ -0,0 +1,7 @@
1
+ import { KafkaOptions } from '@nestjs/microservices';
2
+ import { KafkaConfigEnvironment } from './envs/kafka-config.environment.js';
3
+ export declare class KafkaConfig {
4
+ protected readonly kafkaEnvironment: KafkaConfigEnvironment;
5
+ constructor(kafkaEnvironment: KafkaConfigEnvironment);
6
+ getOptions(): KafkaOptions;
7
+ }
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.KafkaConfig = void 0;
13
+ const common_1 = require("@nestjs/common");
14
+ const microservices_1 = require("@nestjs/microservices");
15
+ const kafka_interface_js_1 = require("@nestjs/microservices/external/kafka.interface.js");
16
+ const node_crypto_1 = require("node:crypto");
17
+ const node_fs_1 = require("node:fs");
18
+ const kafka_config_environment_js_1 = require("./envs/kafka-config.environment.js");
19
+ let KafkaConfig = class KafkaConfig {
20
+ kafkaEnvironment;
21
+ constructor(kafkaEnvironment) {
22
+ this.kafkaEnvironment = kafkaEnvironment;
23
+ }
24
+ getOptions() {
25
+ const { KAFKA_BROKERS, KAFKA_CLIENT_ID, KAFKA_LOG_LEVEL, KAFKA_SASL_PASSWORD, KAFKA_SASL_USERNAME, KAFKA_SSL_CA_FILE, } = this.kafkaEnvironment;
26
+ let ssl;
27
+ if (KAFKA_SSL_CA_FILE !== undefined) {
28
+ ssl = { ca: [(0, node_fs_1.readFileSync)(KAFKA_SSL_CA_FILE, 'utf8')] };
29
+ }
30
+ const sasl = KAFKA_SASL_PASSWORD === undefined || KAFKA_SASL_USERNAME === undefined
31
+ ? undefined
32
+ : { mechanism: 'scram-sha-512', password: KAFKA_SASL_PASSWORD, username: KAFKA_SASL_USERNAME };
33
+ return {
34
+ options: {
35
+ client: {
36
+ brokers: KAFKA_BROKERS.split(','),
37
+ clientId: `${KAFKA_CLIENT_ID}-${(0, node_crypto_1.randomUUID)()}`,
38
+ logLevel: kafka_interface_js_1.logLevel[KAFKA_LOG_LEVEL],
39
+ ...(ssl !== undefined && { ssl }),
40
+ ...(sasl !== undefined && { sasl }),
41
+ },
42
+ postfixId: '',
43
+ },
44
+ transport: microservices_1.Transport.KAFKA,
45
+ };
46
+ }
47
+ };
48
+ exports.KafkaConfig = KafkaConfig;
49
+ exports.KafkaConfig = KafkaConfig = __decorate([
50
+ (0, common_1.Injectable)(),
51
+ __metadata("design:paramtypes", [kafka_config_environment_js_1.KafkaConfigEnvironment])
52
+ ], KafkaConfig);
@@ -0,0 +1,3 @@
1
+ export declare const KAFKA_PRODUCER: unique symbol;
2
+ export declare const KAFKA_LOGGER: unique symbol;
3
+ export declare const KAFKA_ERROR_HANDLER_OPTIONS: unique symbol;
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KAFKA_ERROR_HANDLER_OPTIONS = exports.KAFKA_LOGGER = exports.KAFKA_PRODUCER = void 0;
4
+ exports.KAFKA_PRODUCER = Symbol('KAFKA_PRODUCER');
5
+ exports.KAFKA_LOGGER = Symbol('KAFKA_LOGGER');
6
+ exports.KAFKA_ERROR_HANDLER_OPTIONS = Symbol('KAFKA_ERROR_HANDLER_OPTIONS');
@@ -0,0 +1,5 @@
1
+ export declare const DEFAULT_EVENT_VERSION = "1";
2
+ export declare const EVENT_VERSION_HEADER = "x-event-version";
3
+ export declare const eventVersionOfTopic: (topic: string) => string;
4
+ export declare const eventVersionOfHeaders: (headers: Record<string, unknown> | undefined) => string;
5
+ export declare const versionedQueueName: (queueName: string, version: string) => string;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.versionedQueueName = exports.eventVersionOfHeaders = exports.eventVersionOfTopic = exports.EVENT_VERSION_HEADER = exports.DEFAULT_EVENT_VERSION = void 0;
4
+ exports.DEFAULT_EVENT_VERSION = '1';
5
+ exports.EVENT_VERSION_HEADER = 'x-event-version';
6
+ const VERSION_SEGMENT = /^v\d+$/u;
7
+ const versionSegment = (version) => (version === exports.DEFAULT_EVENT_VERSION ? '' : `.v${version}`);
8
+ const eventVersionOfTopic = (topic) => {
9
+ const segment = topic.split('.').find((part) => VERSION_SEGMENT.test(part));
10
+ return segment === undefined ? exports.DEFAULT_EVENT_VERSION : segment.slice(1);
11
+ };
12
+ exports.eventVersionOfTopic = eventVersionOfTopic;
13
+ const eventVersionOfHeaders = (headers) => {
14
+ const raw = headers?.[exports.EVENT_VERSION_HEADER];
15
+ const value = Buffer.isBuffer(raw) ? raw.toString('utf8') : raw;
16
+ return typeof value === 'string' && value !== '' ? value : exports.DEFAULT_EVENT_VERSION;
17
+ };
18
+ exports.eventVersionOfHeaders = eventVersionOfHeaders;
19
+ const versionedQueueName = (queueName, version) => `${queueName}${versionSegment(version)}`;
20
+ exports.versionedQueueName = versionedQueueName;
@@ -0,0 +1,7 @@
1
+ export * from './configs/index.js';
2
+ export * from './libs/index.js';
3
+ export * from './constants.js';
4
+ export * from './event-contract-version.js';
5
+ export * from './kafka-commit-interceptor.js';
6
+ export * from './kafka-error-handler.js';
7
+ export * from './kafka.module.js';
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./configs/index.js"), exports);
18
+ __exportStar(require("./libs/index.js"), exports);
19
+ __exportStar(require("./constants.js"), exports);
20
+ __exportStar(require("./event-contract-version.js"), exports);
21
+ __exportStar(require("./kafka-commit-interceptor.js"), exports);
22
+ __exportStar(require("./kafka-error-handler.js"), exports);
23
+ __exportStar(require("./kafka.module.js"), exports);
@@ -0,0 +1,13 @@
1
+ import { CallHandler, ExecutionContext, NestInterceptor } from '@nestjs/common';
2
+ import { Observable } from 'rxjs';
3
+ import type { KafkaLoggerInterface } from './libs/kafka-logger.interface.js';
4
+ import { KafkaErrorHandler } from './kafka-error-handler.js';
5
+ export declare class KafkaCommitInterceptor implements NestInterceptor {
6
+ private readonly kafkaErrorHandler;
7
+ private readonly logger;
8
+ constructor(kafkaErrorHandler: KafkaErrorHandler, logger: KafkaLoggerInterface);
9
+ intercept(context: ExecutionContext, next: CallHandler): Observable<unknown>;
10
+ private readCommitContext;
11
+ private rejectForeignContractVersion;
12
+ }
13
+ export declare const KafkaAutoCommit: () => MethodDecorator;
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.KafkaAutoCommit = exports.KafkaCommitInterceptor = void 0;
16
+ const common_1 = require("@nestjs/common");
17
+ const rxjs_1 = require("rxjs");
18
+ const constants_js_1 = require("./constants.js");
19
+ const event_contract_version_js_1 = require("./event-contract-version.js");
20
+ const kafka_error_handler_js_1 = require("./kafka-error-handler.js");
21
+ let KafkaCommitInterceptor = class KafkaCommitInterceptor {
22
+ kafkaErrorHandler;
23
+ logger;
24
+ constructor(kafkaErrorHandler, logger) {
25
+ this.kafkaErrorHandler = kafkaErrorHandler;
26
+ this.logger = logger;
27
+ }
28
+ intercept(context, next) {
29
+ if (context.getType() !== 'rpc') {
30
+ return next.handle();
31
+ }
32
+ const kafkaContext = context.switchToRpc().getContext();
33
+ if (kafkaContext === undefined || typeof kafkaContext.getConsumer !== 'function') {
34
+ return next.handle();
35
+ }
36
+ const { consumer, logContext, nextOffset, partition, topic } = this.readCommitContext(kafkaContext);
37
+ const rejected = this.rejectForeignContractVersion(kafkaContext, topic);
38
+ return (rejected ?? next.handle()).pipe((0, rxjs_1.tap)(async () => {
39
+ try {
40
+ await consumer.commitOffsets([{ offset: nextOffset, partition, topic }]);
41
+ this.logger.info(logContext, 'Kafka message processed');
42
+ }
43
+ catch (commitError) {
44
+ this.logger.error({ errorContext: { ...logContext, error: commitError } }, `Failed to commit offset: ${Error.isError(commitError) ? commitError.message : 'Unknown error'}`);
45
+ }
46
+ }), (0, rxjs_1.catchError)(async (error) => {
47
+ try {
48
+ const commitOffsets = async () => {
49
+ await consumer.commitOffsets([{ offset: nextOffset, partition, topic }]);
50
+ };
51
+ await this.kafkaErrorHandler.handleError(kafkaContext, error, commitOffsets);
52
+ this.logger.warn({ errorContext: { ...logContext, error: error.message } }, `Message moved to retry/dlq: ${error.message}`);
53
+ }
54
+ catch (catchedError) {
55
+ this.logger.error({ errorContext: { ...logContext, error: catchedError } }, `Failed to move message to retry/dlq: ${Error.isError(catchedError) ? catchedError.message : 'Unknown error'}`);
56
+ }
57
+ return (0, rxjs_1.of)(error);
58
+ }));
59
+ }
60
+ readCommitContext(kafkaContext) {
61
+ const { headers, key, offset } = kafkaContext.getMessage();
62
+ const partition = kafkaContext.getPartition();
63
+ const topic = kafkaContext.getTopic();
64
+ return {
65
+ consumer: kafkaContext.getConsumer(),
66
+ logContext: { headers, key, offset, partition, topic },
67
+ nextOffset: (BigInt(offset) + 1n).toString(),
68
+ partition,
69
+ topic,
70
+ };
71
+ }
72
+ rejectForeignContractVersion(kafkaContext, topic) {
73
+ const expectedVersion = (0, event_contract_version_js_1.eventVersionOfTopic)(topic);
74
+ const messageVersion = (0, event_contract_version_js_1.eventVersionOfHeaders)(kafkaContext.getMessage().headers);
75
+ return messageVersion === expectedVersion
76
+ ? undefined
77
+ : (0, rxjs_1.throwError)(() => new common_1.BadRequestException(`Unsupported event contract version "${messageVersion}" on topic "${topic}": expected "${expectedVersion}"`));
78
+ }
79
+ };
80
+ exports.KafkaCommitInterceptor = KafkaCommitInterceptor;
81
+ exports.KafkaCommitInterceptor = KafkaCommitInterceptor = __decorate([
82
+ (0, common_1.Injectable)(),
83
+ __param(1, (0, common_1.Inject)(constants_js_1.KAFKA_LOGGER)),
84
+ __metadata("design:paramtypes", [kafka_error_handler_js_1.KafkaErrorHandler, Object])
85
+ ], KafkaCommitInterceptor);
86
+ const KafkaAutoCommit = () => (0, common_1.applyDecorators)((0, common_1.UseInterceptors)(KafkaCommitInterceptor));
87
+ exports.KafkaAutoCommit = KafkaAutoCommit;