@riaskov/nevo-messaging 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +831 -0
  3. package/dist/common/base.client.d.ts +12 -0
  4. package/dist/common/base.client.js +50 -0
  5. package/dist/common/base.controller.d.ts +28 -0
  6. package/dist/common/base.controller.js +159 -0
  7. package/dist/common/bigint.utils.d.ts +7 -0
  8. package/dist/common/bigint.utils.js +50 -0
  9. package/dist/common/error-code.d.ts +3 -0
  10. package/dist/common/error-code.js +7 -0
  11. package/dist/common/error-messages.d.ts +1 -0
  12. package/dist/common/error-messages.js +7 -0
  13. package/dist/common/errors.d.ts +9 -0
  14. package/dist/common/errors.js +41 -0
  15. package/dist/common/index.d.ts +8 -0
  16. package/dist/common/index.js +24 -0
  17. package/dist/common/service-utils.d.ts +21 -0
  18. package/dist/common/service-utils.js +54 -0
  19. package/dist/common/signal-router.decorator.d.ts +9 -0
  20. package/dist/common/signal-router.decorator.js +67 -0
  21. package/dist/common/types.d.ts +72 -0
  22. package/dist/common/types.js +2 -0
  23. package/dist/index.d.ts +4 -0
  24. package/dist/index.js +20 -0
  25. package/dist/signal-router.utils.d.ts +28 -0
  26. package/dist/signal-router.utils.js +131 -0
  27. package/dist/signal.decorator.d.ts +15 -0
  28. package/dist/signal.decorator.js +67 -0
  29. package/dist/transports/index.d.ts +1 -0
  30. package/dist/transports/index.js +17 -0
  31. package/dist/transports/kafka/index.d.ts +5 -0
  32. package/dist/transports/kafka/index.js +21 -0
  33. package/dist/transports/kafka/kafka.client-base.d.ts +8 -0
  34. package/dist/transports/kafka/kafka.client-base.js +18 -0
  35. package/dist/transports/kafka/kafka.config.d.ts +16 -0
  36. package/dist/transports/kafka/kafka.config.js +210 -0
  37. package/dist/transports/kafka/kafka.signal-router.decorator.d.ts +3 -0
  38. package/dist/transports/kafka/kafka.signal-router.decorator.js +78 -0
  39. package/dist/transports/kafka/microservice.config.d.ts +10 -0
  40. package/dist/transports/kafka/microservice.config.js +46 -0
  41. package/dist/transports/kafka/nevo-kafka.client.d.ts +16 -0
  42. package/dist/transports/kafka/nevo-kafka.client.js +87 -0
  43. package/package.json +64 -0
package/dist/index.js ADDED
@@ -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("./signal.decorator"), exports);
18
+ __exportStar(require("./signal-router.utils"), exports);
19
+ __exportStar(require("./transports"), exports);
20
+ __exportStar(require("./common"), exports);
@@ -0,0 +1,28 @@
1
+ import { Type } from "@nestjs/common";
2
+ import { BeforeHook, AfterHook } from "./common";
3
+ export interface SignalRouterOptions {
4
+ before?: BeforeHook;
5
+ after?: AfterHook;
6
+ debug?: boolean;
7
+ eventPattern?: string;
8
+ }
9
+ export interface MessageData {
10
+ method: string;
11
+ params: any;
12
+ uuid: string;
13
+ }
14
+ export type MessageExtractor = (data: any) => MessageData;
15
+ export declare function findPropertyByType(obj: any, type: Type<any>): string | null;
16
+ export declare function findServiceInstances(instance: any, serviceType: Type<any> | Type<any>[]): any[];
17
+ export declare function createErrorResponse(message: string, uuid?: string, method?: string, code?: number): {
18
+ uuid: string | undefined;
19
+ method: string | undefined;
20
+ params: {
21
+ result: string;
22
+ error: {
23
+ message: string;
24
+ code: number;
25
+ };
26
+ };
27
+ };
28
+ export declare function createSignalRouterDecorator(serviceType: Type<any> | Type<any>[], options: SignalRouterOptions | undefined, messageExtractor: MessageExtractor, registerHandler: (target: any, eventPattern: string, handlerName: string, context?: any) => void): (target: any) => any;
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findPropertyByType = findPropertyByType;
4
+ exports.findServiceInstances = findServiceInstances;
5
+ exports.createErrorResponse = createErrorResponse;
6
+ exports.createSignalRouterDecorator = createSignalRouterDecorator;
7
+ const common_1 = require("./common");
8
+ const signal_decorator_1 = require("./signal.decorator");
9
+ function findPropertyByType(obj, type) {
10
+ for (const prop in obj) {
11
+ if (obj[prop] instanceof type) {
12
+ return prop;
13
+ }
14
+ }
15
+ return null;
16
+ }
17
+ function findServiceInstances(instance, serviceType) {
18
+ return Array.isArray(serviceType)
19
+ ? // @ts-ignore
20
+ serviceType.map((type) => instance[findPropertyByType(instance, type)]).filter(Boolean)
21
+ : // @ts-ignore
22
+ [instance[findPropertyByType(instance, serviceType)]].filter(Boolean);
23
+ }
24
+ function createErrorResponse(message, uuid, method, code = 0) {
25
+ return {
26
+ uuid,
27
+ method,
28
+ params: {
29
+ result: "error",
30
+ error: {
31
+ message,
32
+ code
33
+ }
34
+ }
35
+ };
36
+ }
37
+ function createSignalRouterDecorator(serviceType, options = {}, messageExtractor, registerHandler) {
38
+ const debug = options?.debug || process.env["NODE_ENV"] !== "production";
39
+ return function (target) {
40
+ const eventPattern = options?.eventPattern || target.name.toLowerCase().replace("controller", "") + "-events";
41
+ const handlerName = "handleSignalMessage";
42
+ target.prototype[handlerName] = async function (data) {
43
+ try {
44
+ if (debug) {
45
+ console.log(`[${eventPattern}] Received message:`, (0, common_1.stringifyWithBigInt)(data));
46
+ }
47
+ const messageData = messageExtractor(data);
48
+ const { method, params, uuid } = messageData;
49
+ if (!method) {
50
+ console.error("Missing 'method' field in message");
51
+ return createErrorResponse("Invalid message format");
52
+ }
53
+ if (debug) {
54
+ console.log(`[${eventPattern}] Invoking method:`, method);
55
+ }
56
+ const serviceInstances = findServiceInstances(this, serviceType);
57
+ if (serviceInstances.length === 0) {
58
+ console.error(`No service instances found for:`, serviceType);
59
+ return createErrorResponse("Service not found", uuid, method);
60
+ }
61
+ let processedParams = params;
62
+ if (options.before) {
63
+ const hookResult = await options.before({
64
+ method,
65
+ serviceName: eventPattern,
66
+ uuid,
67
+ rawData: data,
68
+ params
69
+ });
70
+ if (hookResult !== undefined) {
71
+ processedParams = hookResult;
72
+ }
73
+ }
74
+ const signals = (0, signal_decorator_1.getClassSignals)(target);
75
+ const signalHandler = signals.find((s) => s.signalName === method);
76
+ if (!signalHandler) {
77
+ console.error(`No handler found for method:`, method);
78
+ return createErrorResponse(`Method ${method} not found`, uuid, method);
79
+ }
80
+ let serviceInstance = null;
81
+ const serviceMethod = signalHandler.methodName;
82
+ for (let s of serviceInstances) {
83
+ if (s[serviceMethod]) {
84
+ serviceInstance = s;
85
+ }
86
+ }
87
+ if (!serviceInstance[serviceMethod]) {
88
+ console.error(`Method ${serviceMethod} not found in service`);
89
+ return createErrorResponse(`Method ${serviceMethod} does not exist`, uuid, method);
90
+ }
91
+ const args = signalHandler.paramTransformer ? signalHandler.paramTransformer(processedParams) : [processedParams];
92
+ if (debug) {
93
+ console.log(`[${eventPattern}] Calling ${serviceMethod} with parameters:`, (0, common_1.stringifyWithBigInt)(args));
94
+ }
95
+ const result = await serviceInstance[serviceMethod](...args);
96
+ const transformedResult = signalHandler.resultTransformer ? signalHandler.resultTransformer(result) : result;
97
+ const serializedResult = (0, common_1.serializeBigInt)(transformedResult);
98
+ if (debug) {
99
+ console.log(`[${eventPattern}] Result:`, (0, common_1.stringifyWithBigInt)(serializedResult));
100
+ }
101
+ let response = {
102
+ uuid,
103
+ method,
104
+ params: { result: serializedResult }
105
+ };
106
+ if (options.after) {
107
+ const hookResponse = await options.after({
108
+ method,
109
+ serviceName: eventPattern,
110
+ uuid,
111
+ rawData: data,
112
+ params: processedParams,
113
+ result: serializedResult,
114
+ response
115
+ });
116
+ if (hookResponse !== undefined) {
117
+ response = hookResponse;
118
+ }
119
+ }
120
+ return response;
121
+ }
122
+ catch (error) {
123
+ const errorMessage = error instanceof Error ? error.message : "Unknown error";
124
+ console.error(`[${eventPattern}] Processing error:`, error);
125
+ return createErrorResponse(errorMessage, data.uuid, data.method, error.code);
126
+ }
127
+ };
128
+ registerHandler(target, eventPattern, handlerName);
129
+ return target;
130
+ };
131
+ }
@@ -0,0 +1,15 @@
1
+ import "reflect-metadata";
2
+ export declare const SIGNALS_METADATA_KEY = "kafka:signals";
3
+ export interface SignalOptions {
4
+ [key: string]: any;
5
+ }
6
+ export interface SignalMetadata {
7
+ signalName: string;
8
+ methodName: string;
9
+ paramTransformer?: (data: any) => any[];
10
+ resultTransformer?: (result: any) => any;
11
+ options?: SignalOptions;
12
+ }
13
+ export declare function addSignalMetadata(target: any, signalName: string, methodName: string, paramTransformer?: (data: any) => any[], resultTransformer?: (result: any) => any, options?: SignalOptions): void;
14
+ export declare function Signal(signalName: string, methodNameOrParamTransformer?: string | ((data: any) => any[]), paramTransformerOrOptions?: ((data: any) => any[]) | ((result: any) => any) | SignalOptions, resultTransformerOrOptions?: ((result: any) => any) | SignalOptions, options?: SignalOptions): MethodDecorator;
15
+ export declare function getClassSignals(target: any): SignalMetadata[];
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SIGNALS_METADATA_KEY = void 0;
4
+ exports.addSignalMetadata = addSignalMetadata;
5
+ exports.Signal = Signal;
6
+ exports.getClassSignals = getClassSignals;
7
+ require("reflect-metadata");
8
+ // FIXME
9
+ exports.SIGNALS_METADATA_KEY = "kafka:signals";
10
+ function addSignalMetadata(target, signalName, methodName, paramTransformer, resultTransformer, options) {
11
+ const existingSignals = Reflect.getMetadata(exports.SIGNALS_METADATA_KEY, target) || [];
12
+ existingSignals.push({
13
+ signalName,
14
+ methodName,
15
+ paramTransformer,
16
+ resultTransformer,
17
+ options
18
+ });
19
+ Reflect.defineMetadata(exports.SIGNALS_METADATA_KEY, existingSignals, target);
20
+ }
21
+ function Signal(signalName, methodNameOrParamTransformer, paramTransformerOrOptions, resultTransformerOrOptions, options) {
22
+ // @ts-ignore
23
+ return function (target, propertyKey, descriptor) {
24
+ let methodName;
25
+ let paramTransformer;
26
+ let resultTransformer;
27
+ let signalOptions;
28
+ // Case 1: @Signal("create", (data) => [data.id, data.param2])
29
+ if (typeof methodNameOrParamTransformer === "function") {
30
+ methodName = signalName;
31
+ paramTransformer = methodNameOrParamTransformer;
32
+ if (typeof paramTransformerOrOptions === "function") {
33
+ resultTransformer = paramTransformerOrOptions;
34
+ signalOptions = resultTransformerOrOptions;
35
+ }
36
+ else {
37
+ signalOptions = paramTransformerOrOptions;
38
+ }
39
+ }
40
+ // Case 2, 3, 4: @Signal("modify", "modifyInvoice", ...)
41
+ else if (typeof methodNameOrParamTransformer === "string") {
42
+ methodName = methodNameOrParamTransformer;
43
+ if (typeof paramTransformerOrOptions === "function") {
44
+ paramTransformer = paramTransformerOrOptions;
45
+ if (typeof resultTransformerOrOptions === "function") {
46
+ resultTransformer = resultTransformerOrOptions;
47
+ signalOptions = options;
48
+ }
49
+ else {
50
+ signalOptions = resultTransformerOrOptions;
51
+ }
52
+ }
53
+ else {
54
+ signalOptions = paramTransformerOrOptions;
55
+ }
56
+ }
57
+ // Default case: @Signal("create")
58
+ else {
59
+ methodName = signalName;
60
+ }
61
+ addSignalMetadata(target.constructor, signalName, methodName, paramTransformer, resultTransformer, signalOptions);
62
+ return descriptor;
63
+ };
64
+ }
65
+ function getClassSignals(target) {
66
+ return Reflect.getMetadata(exports.SIGNALS_METADATA_KEY, target) || [];
67
+ }
@@ -0,0 +1 @@
1
+ export * from "./kafka";
@@ -0,0 +1,17 @@
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"), exports);
@@ -0,0 +1,5 @@
1
+ export * from "./kafka.client-base";
2
+ export * from "./nevo-kafka.client";
3
+ export * from "./kafka.config";
4
+ export * from "./microservice.config";
5
+ export * from "./kafka.signal-router.decorator";
@@ -0,0 +1,21 @@
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.client-base"), exports);
18
+ __exportStar(require("./nevo-kafka.client"), exports);
19
+ __exportStar(require("./kafka.config"), exports);
20
+ __exportStar(require("./microservice.config"), exports);
21
+ __exportStar(require("./kafka.signal-router.decorator"), exports);
@@ -0,0 +1,8 @@
1
+ import { NevoKafkaClient } from "./nevo-kafka.client";
2
+ export declare abstract class KafkaClientBase {
3
+ protected readonly universalClient: NevoKafkaClient;
4
+ protected constructor(universalClient: NevoKafkaClient);
5
+ protected query<T = any>(serviceName: string, method: string, params: any): Promise<T>;
6
+ protected emit(serviceName: string, method: string, params: any): Promise<void>;
7
+ protected getAvailableServices(): string[];
8
+ }
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KafkaClientBase = void 0;
4
+ class KafkaClientBase {
5
+ constructor(universalClient) {
6
+ this.universalClient = universalClient;
7
+ }
8
+ async query(serviceName, method, params) {
9
+ return this.universalClient.query(serviceName, method, params);
10
+ }
11
+ async emit(serviceName, method, params) {
12
+ return this.universalClient.emit(serviceName, method, params);
13
+ }
14
+ getAvailableServices() {
15
+ return this.universalClient.getAvailableServices();
16
+ }
17
+ }
18
+ exports.KafkaClientBase = KafkaClientBase;
@@ -0,0 +1,16 @@
1
+ import { NevoKafkaClient } from "./nevo-kafka.client";
2
+ export interface KafkaClientFactoryOptions {
3
+ clientIdPrefix: string;
4
+ groupIdPrefix?: string;
5
+ sessionTimeout?: number;
6
+ allowAutoTopicCreation?: boolean;
7
+ retryAttempts?: number;
8
+ brokerRetryTimeout?: number;
9
+ kafkaHost?: string;
10
+ kafkaPort?: string;
11
+ debug?: boolean;
12
+ }
13
+ export declare const createNevoKafkaClient: (serviceNames: string[], options: KafkaClientFactoryOptions) => {
14
+ provide: string;
15
+ useFactory: () => Promise<NevoKafkaClient>;
16
+ };
@@ -0,0 +1,210 @@
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.createNevoKafkaClient = void 0;
37
+ const microservices_1 = require("@nestjs/microservices");
38
+ const kafkajs_1 = require("kafkajs");
39
+ const nevo_kafka_client_1 = require("./nevo-kafka.client");
40
+ async function createKafkaTopics(serviceNames, options) {
41
+ const host = process.env[options.kafkaHost || "KAFKA_HOST"] || "localhost";
42
+ const port = parseInt(process.env[options.kafkaPort || "KAFKA_PORT"] || "9092");
43
+ const { Kafka } = await Promise.resolve().then(() => __importStar(require("kafkajs")));
44
+ const kafka = new Kafka({
45
+ clientId: `${options.clientIdPrefix}-admin`,
46
+ brokers: [`${host}:${port}`],
47
+ connectionTimeout: 10000,
48
+ requestTimeout: 30000,
49
+ retry: {
50
+ retries: 5,
51
+ initialRetryTime: 300,
52
+ maxRetryTime: 30000
53
+ }
54
+ });
55
+ const admin = kafka.admin();
56
+ try {
57
+ await admin.connect();
58
+ console.log(`[KafkaAdmin] Connected to Kafka broker at ${host}:${port}`);
59
+ const existingTopics = await admin.listTopics();
60
+ console.log(`[KafkaAdmin] Existing topics:`, existingTopics);
61
+ const topicsToCreate = serviceNames.flatMap((serviceName) => {
62
+ const normalizedServiceName = serviceName.toLowerCase();
63
+ const eventsTopic = `${normalizedServiceName}-events`;
64
+ const replyTopic = `${eventsTopic}.reply`;
65
+ const topics = [];
66
+ if (!existingTopics.includes(eventsTopic)) {
67
+ topics.push({
68
+ topic: eventsTopic,
69
+ numPartitions: 3,
70
+ replicationFactor: 1,
71
+ configEntries: [
72
+ { name: "cleanup.policy", value: "delete" },
73
+ { name: "retention.ms", value: "86400000" }, // 24 hours
74
+ { name: "max.message.bytes", value: "1000012" }
75
+ ]
76
+ });
77
+ }
78
+ if (!existingTopics.includes(replyTopic)) {
79
+ topics.push({
80
+ topic: replyTopic,
81
+ numPartitions: 3,
82
+ replicationFactor: 1,
83
+ configEntries: [
84
+ { name: "cleanup.policy", value: "delete" },
85
+ { name: "retention.ms", value: "3600000" }, // 1 hour for reply topics
86
+ { name: "max.message.bytes", value: "1000012" }
87
+ ]
88
+ });
89
+ }
90
+ return topics;
91
+ });
92
+ if (topicsToCreate.length > 0) {
93
+ console.log(`[KafkaAdmin] Creating ${topicsToCreate.length} topics:`, topicsToCreate.map((t) => t.topic));
94
+ await admin.createTopics({
95
+ topics: topicsToCreate,
96
+ waitForLeaders: true,
97
+ timeout: 30000
98
+ });
99
+ console.log(`[KafkaAdmin] Successfully created topics:`, topicsToCreate.map((t) => t.topic));
100
+ // Wait a bit for topics to be fully initialized
101
+ await new Promise((resolve) => setTimeout(resolve, 2000));
102
+ // Verify topics were created
103
+ const updatedTopics = await admin.listTopics();
104
+ const createdTopics = topicsToCreate.map((t) => t.topic);
105
+ const missingTopics = createdTopics.filter((topic) => !updatedTopics.includes(topic));
106
+ if (missingTopics.length > 0) {
107
+ console.error(`[KafkaAdmin] Failed to create topics:`, missingTopics);
108
+ throw new Error(`Failed to create topics: ${missingTopics.join(", ")}`);
109
+ }
110
+ console.log(`[KafkaAdmin] Verified all topics created successfully`);
111
+ }
112
+ else {
113
+ console.log(`[KafkaAdmin] All topics already exist, skipping creation`);
114
+ }
115
+ }
116
+ catch (error) {
117
+ console.error(`[KafkaAdmin] Error managing topics:`, error.message);
118
+ // If it's a topic exists error, that's actually OK
119
+ if (error.message && error.message.includes("already exists")) {
120
+ console.log(`[KafkaAdmin] Topics already exist, continuing...`);
121
+ }
122
+ else {
123
+ throw error;
124
+ }
125
+ }
126
+ finally {
127
+ try {
128
+ await admin.disconnect();
129
+ console.log(`[KafkaAdmin] Disconnected from Kafka`);
130
+ }
131
+ catch (disconnectError) {
132
+ console.warn(`[KafkaAdmin] Warning during disconnect:`, disconnectError);
133
+ }
134
+ }
135
+ }
136
+ const createNevoKafkaClient = (serviceNames, options) => {
137
+ const defaultOptions = {
138
+ groupIdPrefix: options.clientIdPrefix,
139
+ sessionTimeout: 15000,
140
+ allowAutoTopicCreation: true,
141
+ retryAttempts: 3,
142
+ brokerRetryTimeout: 1000,
143
+ debug: false,
144
+ timeoutMs: 20000
145
+ };
146
+ const mergedOptions = { ...defaultOptions, ...options };
147
+ return {
148
+ provide: "NEVO_KAFKA_CLIENT",
149
+ useFactory: async () => {
150
+ const host = process.env[mergedOptions.kafkaHost || "KAFKA_HOST"] || "localhost";
151
+ const port = parseInt(process.env[mergedOptions.kafkaPort || "KAFKA_PORT"] || "9092");
152
+ console.log(`[NevoKafkaClient] Initializing for services: ${serviceNames.join(", ")}`);
153
+ let topicCreationAttempts = 0;
154
+ const maxTopicCreationAttempts = 3;
155
+ while (topicCreationAttempts < maxTopicCreationAttempts) {
156
+ try {
157
+ await createKafkaTopics(serviceNames, { ...mergedOptions, kafkaHost: "KAFKA_HOST", kafkaPort: "KAFKA_PORT" });
158
+ break;
159
+ }
160
+ catch (error) {
161
+ topicCreationAttempts++;
162
+ console.warn(`[NevoKafkaClient] Topic creation attempt ${topicCreationAttempts} failed:`, error.message);
163
+ if (topicCreationAttempts >= maxTopicCreationAttempts) {
164
+ console.error(`[NevoKafkaClient] Failed to create topics after ${maxTopicCreationAttempts} attempts`);
165
+ throw error;
166
+ }
167
+ // Wait before retrying
168
+ await new Promise((resolve) => setTimeout(resolve, 2000 * topicCreationAttempts));
169
+ }
170
+ }
171
+ const kafkaClientConfig = {
172
+ transport: microservices_1.Transport.KAFKA,
173
+ options: {
174
+ client: {
175
+ clientId: `${mergedOptions.clientIdPrefix}-nevo`,
176
+ brokers: [`${host}:${port}`],
177
+ connectionTimeout: 10000,
178
+ requestTimeout: 30000,
179
+ retry: {
180
+ retries: mergedOptions.retryAttempts,
181
+ initialRetryTime: 300,
182
+ maxRetryTime: mergedOptions.brokerRetryTimeout
183
+ }
184
+ },
185
+ consumer: {
186
+ groupId: `${mergedOptions.groupIdPrefix}-nevo-consumer`,
187
+ allowAutoTopicCreation: mergedOptions.allowAutoTopicCreation,
188
+ sessionTimeout: mergedOptions.sessionTimeout,
189
+ maxWaitTimeInMs: 5000,
190
+ heartbeatInterval: 3000
191
+ },
192
+ producer: {
193
+ createPartitioner: kafkajs_1.Partitioners.DefaultPartitioner,
194
+ allowAutoTopicCreation: mergedOptions.allowAutoTopicCreation,
195
+ idempotent: true,
196
+ maxInFlightRequests: 1
197
+ }
198
+ }
199
+ };
200
+ const { ClientKafka } = await Promise.resolve().then(() => __importStar(require("@nestjs/microservices")));
201
+ const kafkaClient = new ClientKafka(kafkaClientConfig.options);
202
+ console.log(`[NevoKafkaClient] Created client for services: ${serviceNames.join(", ")}`);
203
+ return new nevo_kafka_client_1.NevoKafkaClient(kafkaClient, serviceNames, {
204
+ timeoutMs: mergedOptions.timeoutMs,
205
+ debug: mergedOptions.debug
206
+ });
207
+ }
208
+ };
209
+ };
210
+ exports.createNevoKafkaClient = createNevoKafkaClient;
@@ -0,0 +1,3 @@
1
+ import { Type } from "@nestjs/common";
2
+ import { SignalRouterOptions } from "../../signal-router.utils";
3
+ export declare function KafkaSignalRouter(serviceType: Type<any> | Type<any>[], options?: SignalRouterOptions): (target: any) => any;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.KafkaSignalRouter = KafkaSignalRouter;
4
+ const microservices_1 = require("@nestjs/microservices");
5
+ const signal_router_utils_1 = require("../../signal-router.utils");
6
+ const common_1 = require("../../common");
7
+ const kafkajs_1 = require("kafkajs");
8
+ function KafkaSignalRouter(serviceType, options) {
9
+ return (0, signal_router_utils_1.createSignalRouterDecorator)(serviceType, options, (data) => {
10
+ let messageData = data;
11
+ if (data && data.value && typeof data.value === "string") {
12
+ try {
13
+ messageData = (0, common_1.parseWithBigInt)(data.value);
14
+ }
15
+ catch (e) {
16
+ console.error("Failed to parse JSON from message:", e);
17
+ }
18
+ }
19
+ return {
20
+ method: messageData.method,
21
+ params: messageData.params,
22
+ uuid: messageData.uuid
23
+ };
24
+ }, (target, eventPattern, handlerName) => {
25
+ const originalMethod = target.prototype[handlerName];
26
+ target.prototype.producer = null;
27
+ const originalOnModuleInit = target.prototype.onModuleInit || function () { };
28
+ target.prototype.onModuleInit = async function () {
29
+ await originalOnModuleInit.call(this);
30
+ const kafkaHost = process.env["KAFKA_HOST"] || "localhost";
31
+ const kafkaPort = process.env["KAFKA_PORT"] || "9092";
32
+ const kafka = new kafkajs_1.Kafka({
33
+ clientId: `${eventPattern}-producer`,
34
+ brokers: [`${kafkaHost}:${kafkaPort}`]
35
+ });
36
+ this.producer = kafka.producer();
37
+ await this.producer.connect();
38
+ if (options?.debug) {
39
+ console.log(`[${eventPattern}] Kafka Producer connected for reply topics`);
40
+ }
41
+ };
42
+ const originalOnModuleDestroy = target.prototype.onModuleDestroy || function () { };
43
+ target.prototype.onModuleDestroy = async function () {
44
+ await originalOnModuleDestroy.call(this);
45
+ if (this.producer) {
46
+ await this.producer.disconnect();
47
+ if (options?.debug) {
48
+ console.log(`[${eventPattern}] Kafka Producer disconnected`);
49
+ }
50
+ }
51
+ };
52
+ target.prototype[handlerName] = async function (data) {
53
+ const result = await originalMethod.call(this, data);
54
+ if (result && this.producer) {
55
+ try {
56
+ const replyTopic = `${eventPattern}.reply`;
57
+ await this.producer.send({
58
+ topic: replyTopic,
59
+ messages: [
60
+ {
61
+ key: result.uuid || "",
62
+ value: (0, common_1.stringifyWithBigInt)(result)
63
+ }
64
+ ]
65
+ });
66
+ if (options?.debug) {
67
+ console.log(`[${eventPattern}] Sent response to ${replyTopic}:`, (0, common_1.stringifyWithBigInt)(result));
68
+ }
69
+ }
70
+ catch (error) {
71
+ console.error(`[${eventPattern}] Failed to send to reply topic:`, error);
72
+ }
73
+ }
74
+ return result;
75
+ };
76
+ (0, microservices_1.MessagePattern)(eventPattern)(target.prototype, handlerName, Object.getOwnPropertyDescriptor(target.prototype, handlerName));
77
+ });
78
+ }
@@ -0,0 +1,10 @@
1
+ import { INestApplication, Type } from "@nestjs/common";
2
+ export interface NestApplicationOptions {
3
+ microserviceName: string;
4
+ module: Type<any>;
5
+ port?: number;
6
+ host?: string;
7
+ debug?: boolean;
8
+ onInit?: (app: INestApplication) => Promise<void>;
9
+ }
10
+ export declare function createKafkaMicroservice(options: NestApplicationOptions): Promise<INestApplication>;