@riaskov/nevo-messaging 1.0.1 → 1.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +202 -67
- package/dist/common/access-control.d.ts +15 -0
- package/dist/common/access-control.js +94 -0
- package/dist/common/base.client.d.ts +7 -2
- package/dist/common/base.client.js +16 -2
- package/dist/common/base.controller.d.ts +6 -1
- package/dist/common/base.controller.js +68 -4
- package/dist/common/constants.d.ts +4 -0
- package/dist/common/constants.js +7 -0
- package/dist/common/discovery.d.ts +8 -0
- package/dist/common/discovery.js +35 -0
- package/dist/common/error-code.d.ts +2 -1
- package/dist/common/error-code.js +1 -0
- package/dist/common/error-messages.js +2 -1
- package/dist/common/index.d.ts +3 -0
- package/dist/common/index.js +3 -0
- package/dist/common/service-utils.d.ts +2 -0
- package/dist/common/service-utils.js +8 -0
- package/dist/common/types.d.ts +62 -0
- package/dist/signal-router.utils.d.ts +3 -1
- package/dist/signal-router.utils.js +70 -6
- package/dist/transports/http/http.client-base.d.ts +13 -0
- package/dist/transports/http/http.client-base.js +33 -0
- package/dist/transports/http/http.config.d.ts +8 -0
- package/dist/transports/http/http.config.js +16 -0
- package/dist/transports/http/http.signal-router.decorator.d.ts +3 -0
- package/dist/transports/http/http.signal-router.decorator.js +18 -0
- package/dist/transports/http/http.transport.controller.d.ts +21 -0
- package/dist/transports/http/http.transport.controller.js +114 -0
- package/dist/transports/http/index.d.ts +5 -0
- package/dist/transports/http/index.js +21 -0
- package/dist/transports/http/nevo-http.client.d.ts +54 -0
- package/dist/transports/http/nevo-http.client.js +280 -0
- package/dist/transports/index.d.ts +3 -0
- package/dist/transports/index.js +3 -0
- package/dist/transports/kafka/kafka.client-base.d.ts +5 -0
- package/dist/transports/kafka/kafka.client-base.js +15 -0
- package/dist/transports/kafka/kafka.config.d.ts +7 -0
- package/dist/transports/kafka/kafka.config.js +48 -2
- package/dist/transports/kafka/kafka.signal-router.decorator.js +2 -1
- package/dist/transports/kafka/nevo-kafka.client.d.ts +42 -0
- package/dist/transports/kafka/nevo-kafka.client.js +210 -4
- package/dist/transports/nats/index.d.ts +4 -0
- package/dist/transports/nats/index.js +20 -0
- package/dist/transports/nats/nats.client-base.d.ts +13 -0
- package/dist/transports/nats/nats.client-base.js +33 -0
- package/dist/transports/nats/nats.config.d.ts +8 -0
- package/dist/transports/nats/nats.config.js +16 -0
- package/dist/transports/nats/nats.signal-router.decorator.d.ts +6 -0
- package/dist/transports/nats/nats.signal-router.decorator.js +49 -0
- package/dist/transports/nats/nevo-nats.client.d.ts +55 -0
- package/dist/transports/nats/nevo-nats.client.js +210 -0
- package/dist/transports/socket-io/index.d.ts +4 -0
- package/dist/transports/socket-io/index.js +20 -0
- package/dist/transports/socket-io/nevo-socket.client.d.ts +50 -0
- package/dist/transports/socket-io/nevo-socket.client.js +202 -0
- package/dist/transports/socket-io/socket.client-base.d.ts +13 -0
- package/dist/transports/socket-io/socket.client-base.js +33 -0
- package/dist/transports/socket-io/socket.config.d.ts +8 -0
- package/dist/transports/socket-io/socket.config.js +16 -0
- package/dist/transports/socket-io/socket.signal-router.decorator.d.ts +13 -0
- package/dist/transports/socket-io/socket.signal-router.decorator.js +109 -0
- package/package.json +11 -5
|
@@ -1,16 +1,58 @@
|
|
|
1
1
|
import { ClientKafka } from "@nestjs/microservices";
|
|
2
|
+
import { Subscription, SubscriptionContext, SubscriptionOptions } from "../../common";
|
|
2
3
|
export interface NevoKafkaClientOptions {
|
|
3
4
|
timeoutMs?: number;
|
|
4
5
|
debug?: boolean;
|
|
6
|
+
serviceName?: string;
|
|
7
|
+
authToken?: string;
|
|
8
|
+
brokers?: string[];
|
|
9
|
+
backoff?: {
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
baseMs?: number;
|
|
12
|
+
maxMs?: number;
|
|
13
|
+
maxAttempts?: number;
|
|
14
|
+
jitter?: boolean;
|
|
15
|
+
};
|
|
16
|
+
discovery?: {
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
heartbeatIntervalMs?: number;
|
|
19
|
+
ttlMs?: number;
|
|
20
|
+
};
|
|
5
21
|
}
|
|
6
22
|
export declare class NevoKafkaClient {
|
|
7
23
|
private readonly kafkaClient;
|
|
8
24
|
private readonly serviceNames;
|
|
9
25
|
private readonly timeoutMs;
|
|
10
26
|
private readonly debug;
|
|
27
|
+
private readonly serviceName?;
|
|
28
|
+
private readonly authToken?;
|
|
29
|
+
private readonly brokers;
|
|
30
|
+
private readonly backoffEnabled;
|
|
31
|
+
private readonly backoffBaseMs;
|
|
32
|
+
private readonly backoffMaxMs;
|
|
33
|
+
private readonly backoffMaxAttempts;
|
|
34
|
+
private readonly backoffJitter;
|
|
35
|
+
private readonly inFlight;
|
|
36
|
+
private readonly discoveryTopic;
|
|
37
|
+
private readonly broadcastTopic;
|
|
38
|
+
private readonly discoveryRegistry;
|
|
39
|
+
private readonly discoveryEnabled;
|
|
40
|
+
private readonly discoveryHeartbeatIntervalMs;
|
|
41
|
+
private readonly discoveryTtlMs;
|
|
42
|
+
private discoveryProducer?;
|
|
43
|
+
private discoveryConsumer?;
|
|
44
|
+
private discoveryTimer?;
|
|
45
|
+
private readonly subscriptionConsumers;
|
|
11
46
|
constructor(kafkaClient: ClientKafka, serviceNames: string[], options?: NevoKafkaClientOptions);
|
|
12
47
|
private createMessagePayload;
|
|
48
|
+
private waitForInFlightSlot;
|
|
13
49
|
query<T = any>(serviceName: string, method: string, params: any): Promise<T>;
|
|
14
50
|
emit(serviceName: string, method: string, params: any): Promise<void>;
|
|
15
51
|
getAvailableServices(): string[];
|
|
52
|
+
publish(serviceName: string, method: string, params: any): Promise<void>;
|
|
53
|
+
broadcast(method: string, params: any): Promise<void>;
|
|
54
|
+
subscribe<T = any>(serviceName: string, method: string, options: SubscriptionOptions | undefined, handler: (data: T, context: SubscriptionContext) => Promise<void> | void): Promise<Subscription>;
|
|
55
|
+
getDiscoveredServices(): import("../../common").DiscoveryEntry[];
|
|
56
|
+
isServiceAvailable(serviceName: string): boolean;
|
|
57
|
+
private initDiscovery;
|
|
16
58
|
}
|
|
@@ -4,26 +4,70 @@ exports.NevoKafkaClient = void 0;
|
|
|
4
4
|
const rxjs_1 = require("rxjs");
|
|
5
5
|
const common_1 = require("../../common");
|
|
6
6
|
const node_crypto_1 = require("node:crypto");
|
|
7
|
+
const kafkajs_1 = require("kafkajs");
|
|
7
8
|
class NevoKafkaClient {
|
|
8
9
|
constructor(kafkaClient, serviceNames, options) {
|
|
10
|
+
this.inFlight = new Set();
|
|
11
|
+
this.discoveryTopic = common_1.DEFAULT_DISCOVERY_TOPIC;
|
|
12
|
+
this.broadcastTopic = common_1.DEFAULT_BROADCAST_TOPIC;
|
|
13
|
+
this.discoveryRegistry = new common_1.DiscoveryRegistry();
|
|
14
|
+
this.subscriptionConsumers = new Set();
|
|
9
15
|
this.kafkaClient = kafkaClient;
|
|
10
16
|
this.serviceNames = serviceNames.map((name) => name.toLowerCase());
|
|
11
17
|
this.timeoutMs = options?.timeoutMs || 20000;
|
|
12
18
|
this.debug = options?.debug || false;
|
|
19
|
+
this.serviceName = options?.serviceName;
|
|
20
|
+
this.authToken = options?.authToken;
|
|
21
|
+
this.brokers = options?.brokers && options.brokers.length > 0 ? options.brokers : ["127.0.0.1:9092"];
|
|
22
|
+
this.backoffEnabled = options?.backoff?.enabled !== false;
|
|
23
|
+
this.backoffBaseMs = options?.backoff?.baseMs || 100;
|
|
24
|
+
this.backoffMaxMs = options?.backoff?.maxMs || 2000;
|
|
25
|
+
this.backoffMaxAttempts = options?.backoff?.maxAttempts || 0;
|
|
26
|
+
this.backoffJitter = options?.backoff?.jitter !== false;
|
|
27
|
+
this.discoveryEnabled = options?.discovery?.enabled !== false;
|
|
28
|
+
this.discoveryHeartbeatIntervalMs = options?.discovery?.heartbeatIntervalMs || 5000;
|
|
29
|
+
this.discoveryTtlMs = options?.discovery?.ttlMs || 15000;
|
|
13
30
|
this.serviceNames.forEach((serviceName) => {
|
|
14
31
|
const topicName = `${serviceName}-events`;
|
|
15
32
|
const replyTopicName = `${topicName}.reply`;
|
|
16
33
|
this.kafkaClient.subscribeToResponseOf(topicName);
|
|
17
34
|
this.kafkaClient.subscribeToResponseOf(replyTopicName);
|
|
18
35
|
});
|
|
36
|
+
if (this.discoveryEnabled) {
|
|
37
|
+
void this.initDiscovery();
|
|
38
|
+
}
|
|
19
39
|
}
|
|
20
|
-
createMessagePayload(method, params) {
|
|
40
|
+
createMessagePayload(method, params, type) {
|
|
21
41
|
const uuid = (0, node_crypto_1.randomUUID)();
|
|
42
|
+
const meta = {
|
|
43
|
+
type,
|
|
44
|
+
service: this.serviceName,
|
|
45
|
+
ts: Date.now(),
|
|
46
|
+
auth: { token: this.authToken }
|
|
47
|
+
};
|
|
22
48
|
return {
|
|
23
49
|
key: uuid,
|
|
24
|
-
value: (0, common_1.stringifyWithBigInt)({ uuid, method, params })
|
|
50
|
+
value: (0, common_1.stringifyWithBigInt)({ uuid, method, params, meta })
|
|
25
51
|
};
|
|
26
52
|
}
|
|
53
|
+
async waitForInFlightSlot(key) {
|
|
54
|
+
if (!this.backoffEnabled) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
let attempt = 0;
|
|
58
|
+
let delay = this.backoffBaseMs;
|
|
59
|
+
while (this.inFlight.has(key)) {
|
|
60
|
+
attempt++;
|
|
61
|
+
if (this.backoffMaxAttempts > 0 && attempt > this.backoffMaxAttempts) {
|
|
62
|
+
throw new common_1.MessagingError(common_1.ErrorCode.UNKNOWN, {
|
|
63
|
+
message: `Backoff exceeded for ${key}`
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const jitter = this.backoffJitter ? Math.floor(Math.random() * delay * 0.2) : 0;
|
|
67
|
+
await new Promise((resolve) => setTimeout(resolve, delay + jitter));
|
|
68
|
+
delay = Math.min(this.backoffMaxMs, delay * 2);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
27
71
|
async query(serviceName, method, params) {
|
|
28
72
|
const normalizedServiceName = serviceName.toLowerCase();
|
|
29
73
|
if (!this.serviceNames.includes(normalizedServiceName)) {
|
|
@@ -33,11 +77,16 @@ class NevoKafkaClient {
|
|
|
33
77
|
});
|
|
34
78
|
}
|
|
35
79
|
const topicName = `${normalizedServiceName}-events`;
|
|
36
|
-
const payload = this.createMessagePayload(method, params);
|
|
80
|
+
const payload = this.createMessagePayload(method, params, "query");
|
|
81
|
+
const inFlightKey = `${normalizedServiceName}:${method}`;
|
|
37
82
|
if (this.debug) {
|
|
38
83
|
console.log(`[NevoKafkaClient] Sending query to ${topicName}:`, { method, params });
|
|
39
84
|
}
|
|
85
|
+
let inFlightAcquired = false;
|
|
40
86
|
try {
|
|
87
|
+
await this.waitForInFlightSlot(inFlightKey);
|
|
88
|
+
this.inFlight.add(inFlightKey);
|
|
89
|
+
inFlightAcquired = true;
|
|
41
90
|
const response = await (0, rxjs_1.lastValueFrom)(this.kafkaClient.send(topicName, payload).pipe((0, rxjs_1.timeout)(this.timeoutMs)));
|
|
42
91
|
if (response?.params?.result === "error" && response?.params?.error) {
|
|
43
92
|
const errorData = response.params.error;
|
|
@@ -58,6 +107,11 @@ class NevoKafkaClient {
|
|
|
58
107
|
}
|
|
59
108
|
throw error;
|
|
60
109
|
}
|
|
110
|
+
finally {
|
|
111
|
+
if (inFlightAcquired) {
|
|
112
|
+
this.inFlight.delete(inFlightKey);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
61
115
|
}
|
|
62
116
|
async emit(serviceName, method, params) {
|
|
63
117
|
const normalizedServiceName = serviceName.toLowerCase();
|
|
@@ -68,7 +122,7 @@ class NevoKafkaClient {
|
|
|
68
122
|
});
|
|
69
123
|
}
|
|
70
124
|
const topicName = `${normalizedServiceName}-events`;
|
|
71
|
-
const payload = this.createMessagePayload(method, params);
|
|
125
|
+
const payload = this.createMessagePayload(method, params, "emit");
|
|
72
126
|
if (this.debug) {
|
|
73
127
|
console.log(`[NevoKafkaClient] Emitting to ${topicName}:`, { method, params });
|
|
74
128
|
}
|
|
@@ -83,5 +137,157 @@ class NevoKafkaClient {
|
|
|
83
137
|
getAvailableServices() {
|
|
84
138
|
return [...this.serviceNames];
|
|
85
139
|
}
|
|
140
|
+
async publish(serviceName, method, params) {
|
|
141
|
+
const normalizedServiceName = serviceName.toLowerCase();
|
|
142
|
+
if (!this.serviceNames.includes(normalizedServiceName)) {
|
|
143
|
+
throw new common_1.MessagingError(common_1.ErrorCode.UNKNOWN, {
|
|
144
|
+
message: `Service "${serviceName}" is not registered in nevo kafka client`,
|
|
145
|
+
availableServices: this.serviceNames
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
const topicName = `${normalizedServiceName}${common_1.DEFAULT_SUBSCRIPTION_SUFFIX}`;
|
|
149
|
+
const payload = this.createMessagePayload(method, params, "sub");
|
|
150
|
+
if (this.debug) {
|
|
151
|
+
console.log(`[NevoKafkaClient] Publishing to ${topicName}:`, { method, params });
|
|
152
|
+
}
|
|
153
|
+
try {
|
|
154
|
+
this.kafkaClient.emit(topicName, payload);
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
console.error(`Failed to publish event to ${serviceName}.${method}:`, error);
|
|
158
|
+
throw error;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async broadcast(method, params) {
|
|
162
|
+
const payload = this.createMessagePayload(method, params, "broadcast");
|
|
163
|
+
if (this.debug) {
|
|
164
|
+
console.log(`[NevoKafkaClient] Broadcasting to ${this.broadcastTopic}:`, { method, params });
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
this.kafkaClient.emit(this.broadcastTopic, payload);
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
console.error(`Failed to broadcast ${method}:`, error);
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
async subscribe(serviceName, method, options, handler) {
|
|
175
|
+
const normalizedServiceName = serviceName.toLowerCase();
|
|
176
|
+
const isBroadcast = normalizedServiceName === this.broadcastTopic;
|
|
177
|
+
if (!isBroadcast && !this.serviceNames.includes(normalizedServiceName)) {
|
|
178
|
+
throw new common_1.MessagingError(common_1.ErrorCode.UNKNOWN, {
|
|
179
|
+
message: `Service "${serviceName}" is not registered in nevo kafka client`,
|
|
180
|
+
availableServices: this.serviceNames
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
const kafka = new kafkajs_1.Kafka({
|
|
184
|
+
clientId: `${this.serviceName || "nevo"}-sub-${(0, node_crypto_1.randomUUID)()}`,
|
|
185
|
+
brokers: this.brokers
|
|
186
|
+
});
|
|
187
|
+
const groupId = options?.groupId || (options?.durableKey ? `nevo-sub-${options.durableKey}` : `nevo-sub-${this.serviceName || "client"}-${(0, node_crypto_1.randomUUID)()}`);
|
|
188
|
+
const consumer = kafka.consumer({
|
|
189
|
+
groupId,
|
|
190
|
+
allowAutoTopicCreation: true
|
|
191
|
+
});
|
|
192
|
+
await consumer.connect();
|
|
193
|
+
const topic = isBroadcast ? this.broadcastTopic : `${normalizedServiceName}${common_1.DEFAULT_SUBSCRIPTION_SUFFIX}`;
|
|
194
|
+
await consumer.subscribe({ topic, fromBeginning: options?.fromBeginning || false });
|
|
195
|
+
const manualAck = options?.ack === true;
|
|
196
|
+
await consumer.run({
|
|
197
|
+
autoCommit: !manualAck,
|
|
198
|
+
eachMessage: async ({ topic, partition, message }) => {
|
|
199
|
+
const raw = message.value?.toString();
|
|
200
|
+
if (!raw) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
let payload;
|
|
204
|
+
try {
|
|
205
|
+
payload = (0, common_1.parseWithBigInt)(raw);
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
console.error(`[NevoKafkaClient] Failed to parse subscription message`, error);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (method && payload.method !== method) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const context = {
|
|
215
|
+
meta: payload.meta || {},
|
|
216
|
+
ack: async () => {
|
|
217
|
+
if (!manualAck) {
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
const nextOffset = (Number(message.offset) + 1).toString();
|
|
221
|
+
await consumer.commitOffsets([{ topic, partition, offset: nextOffset }]);
|
|
222
|
+
},
|
|
223
|
+
nack: async () => {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
await handler(payload.params, context);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
this.subscriptionConsumers.add(consumer);
|
|
231
|
+
return {
|
|
232
|
+
unsubscribe: async () => {
|
|
233
|
+
this.subscriptionConsumers.delete(consumer);
|
|
234
|
+
await consumer.disconnect();
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
getDiscoveredServices() {
|
|
239
|
+
this.discoveryRegistry.prune(this.discoveryTtlMs);
|
|
240
|
+
return this.discoveryRegistry.list();
|
|
241
|
+
}
|
|
242
|
+
isServiceAvailable(serviceName) {
|
|
243
|
+
return this.discoveryRegistry.isAvailable(serviceName, this.discoveryTtlMs);
|
|
244
|
+
}
|
|
245
|
+
async initDiscovery() {
|
|
246
|
+
try {
|
|
247
|
+
const kafka = new kafkajs_1.Kafka({
|
|
248
|
+
clientId: `${this.serviceName || "nevo"}-discovery`,
|
|
249
|
+
brokers: this.brokers
|
|
250
|
+
});
|
|
251
|
+
this.discoveryProducer = kafka.producer();
|
|
252
|
+
this.discoveryConsumer = kafka.consumer({
|
|
253
|
+
groupId: `${this.serviceName || "nevo"}-discovery-${(0, node_crypto_1.randomUUID)()}`
|
|
254
|
+
});
|
|
255
|
+
await this.discoveryProducer.connect();
|
|
256
|
+
await this.discoveryConsumer.connect();
|
|
257
|
+
await this.discoveryConsumer.subscribe({ topic: this.discoveryTopic, fromBeginning: false });
|
|
258
|
+
await this.discoveryConsumer.run({
|
|
259
|
+
eachMessage: async ({ message }) => {
|
|
260
|
+
const raw = message.value?.toString();
|
|
261
|
+
if (!raw) {
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
try {
|
|
265
|
+
const announcement = (0, common_1.parseWithBigInt)(raw);
|
|
266
|
+
if (announcement?.serviceName) {
|
|
267
|
+
this.discoveryRegistry.update(announcement);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
console.error("[NevoKafkaClient] Failed to parse discovery message", error);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
this.discoveryTimer = setInterval(() => {
|
|
276
|
+
const announcement = {
|
|
277
|
+
serviceName: this.serviceName || "unknown",
|
|
278
|
+
clientId: this.serviceName,
|
|
279
|
+
transport: "kafka",
|
|
280
|
+
ts: Date.now()
|
|
281
|
+
};
|
|
282
|
+
void this.discoveryProducer?.send({
|
|
283
|
+
topic: this.discoveryTopic,
|
|
284
|
+
messages: [{ key: announcement.serviceName, value: (0, common_1.stringifyWithBigInt)(announcement) }]
|
|
285
|
+
});
|
|
286
|
+
}, this.discoveryHeartbeatIntervalMs);
|
|
287
|
+
}
|
|
288
|
+
catch (error) {
|
|
289
|
+
console.error("[NevoKafkaClient] Discovery initialization failed:", error);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
86
292
|
}
|
|
87
293
|
exports.NevoKafkaClient = NevoKafkaClient;
|
|
@@ -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("./nevo-nats.client"), exports);
|
|
18
|
+
__exportStar(require("./nats.client-base"), exports);
|
|
19
|
+
__exportStar(require("./nats.config"), exports);
|
|
20
|
+
__exportStar(require("./nats.signal-router.decorator"), exports);
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { NevoNatsClient } from "./nevo-nats.client";
|
|
2
|
+
export declare abstract class NatsClientBase {
|
|
3
|
+
protected readonly universalClient: NevoNatsClient;
|
|
4
|
+
protected constructor(universalClient: NevoNatsClient);
|
|
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 publish(serviceName: string, method: string, params: any): Promise<void>;
|
|
8
|
+
protected broadcast(method: string, params: any): Promise<void>;
|
|
9
|
+
protected subscribe<T = any>(serviceName: string, method: string, options: Parameters<NevoNatsClient["subscribe"]>[2], handler: Parameters<NevoNatsClient["subscribe"]>[3]): Promise<import("../..").Subscription>;
|
|
10
|
+
protected getAvailableServices(): string[];
|
|
11
|
+
protected getDiscoveredServices(): import("../..").DiscoveryEntry[];
|
|
12
|
+
protected isServiceAvailable(serviceName: string): boolean;
|
|
13
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NatsClientBase = void 0;
|
|
4
|
+
class NatsClientBase {
|
|
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
|
+
async publish(serviceName, method, params) {
|
|
15
|
+
return this.universalClient.publish(serviceName, method, params);
|
|
16
|
+
}
|
|
17
|
+
async broadcast(method, params) {
|
|
18
|
+
return this.universalClient.broadcast(method, params);
|
|
19
|
+
}
|
|
20
|
+
async subscribe(serviceName, method, options, handler) {
|
|
21
|
+
return this.universalClient.subscribe(serviceName, method, options, handler);
|
|
22
|
+
}
|
|
23
|
+
getAvailableServices() {
|
|
24
|
+
return this.universalClient.getAvailableServices();
|
|
25
|
+
}
|
|
26
|
+
getDiscoveredServices() {
|
|
27
|
+
return this.universalClient.getDiscoveredServices();
|
|
28
|
+
}
|
|
29
|
+
isServiceAvailable(serviceName) {
|
|
30
|
+
return this.universalClient.isServiceAvailable(serviceName);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.NatsClientBase = NatsClientBase;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { NevoNatsClient, NevoNatsClientOptions } from "./nevo-nats.client";
|
|
2
|
+
export interface NatsClientFactoryOptions extends NevoNatsClientOptions {
|
|
3
|
+
clientIdPrefix: string;
|
|
4
|
+
}
|
|
5
|
+
export declare const createNevoNatsClient: (serviceNames: string[], options: NatsClientFactoryOptions) => {
|
|
6
|
+
provide: string;
|
|
7
|
+
useFactory: () => Promise<NevoNatsClient>;
|
|
8
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createNevoNatsClient = void 0;
|
|
4
|
+
const nevo_nats_client_1 = require("./nevo-nats.client");
|
|
5
|
+
const createNevoNatsClient = (serviceNames, options) => {
|
|
6
|
+
return {
|
|
7
|
+
provide: "NEVO_NATS_CLIENT",
|
|
8
|
+
useFactory: async () => {
|
|
9
|
+
return nevo_nats_client_1.NevoNatsClient.create(serviceNames, {
|
|
10
|
+
...options,
|
|
11
|
+
serviceName: options.serviceName || options.clientIdPrefix
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
exports.createNevoNatsClient = createNevoNatsClient;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Type } from "@nestjs/common";
|
|
2
|
+
import { SignalRouterOptions } from "../../signal-router.utils";
|
|
3
|
+
export interface NatsSignalRouterOptions extends SignalRouterOptions {
|
|
4
|
+
servers?: string[];
|
|
5
|
+
}
|
|
6
|
+
export declare function NatsSignalRouter(serviceType: Type<any> | Type<any>[], options?: NatsSignalRouterOptions): (target: any) => any;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NatsSignalRouter = NatsSignalRouter;
|
|
4
|
+
const nats_1 = require("nats");
|
|
5
|
+
const signal_router_utils_1 = require("../../signal-router.utils");
|
|
6
|
+
const common_1 = require("../../common");
|
|
7
|
+
function NatsSignalRouter(serviceType, options) {
|
|
8
|
+
return (0, signal_router_utils_1.createSignalRouterDecorator)(serviceType, options, (data) => {
|
|
9
|
+
const messageData = typeof data === "string" ? (0, common_1.parseWithBigInt)(data) : data;
|
|
10
|
+
return {
|
|
11
|
+
method: messageData.method,
|
|
12
|
+
params: messageData.params,
|
|
13
|
+
uuid: messageData.uuid,
|
|
14
|
+
meta: messageData.meta
|
|
15
|
+
};
|
|
16
|
+
}, (target, eventPattern, handlerName) => {
|
|
17
|
+
const codec = (0, nats_1.StringCodec)();
|
|
18
|
+
target.prototype.natsConnection = null;
|
|
19
|
+
target.prototype.natsSubscription = null;
|
|
20
|
+
const originalOnModuleInit = target.prototype.onModuleInit || function () { };
|
|
21
|
+
target.prototype.onModuleInit = async function () {
|
|
22
|
+
await originalOnModuleInit.call(this);
|
|
23
|
+
const servers = options?.servers && options.servers.length > 0 ? options.servers : ["nats://127.0.0.1:4222"];
|
|
24
|
+
const nc = await (0, nats_1.connect)({ servers });
|
|
25
|
+
this.natsConnection = nc;
|
|
26
|
+
const sub = nc.subscribe(eventPattern);
|
|
27
|
+
this.natsSubscription = sub;
|
|
28
|
+
(async () => {
|
|
29
|
+
for await (const msg of sub) {
|
|
30
|
+
const payload = codec.decode(msg.data);
|
|
31
|
+
const result = await this[handlerName](payload);
|
|
32
|
+
if (msg.reply && result) {
|
|
33
|
+
nc.publish(msg.reply, codec.encode((0, common_1.stringifyWithBigInt)(result)));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
})();
|
|
37
|
+
};
|
|
38
|
+
const originalOnModuleDestroy = target.prototype.onModuleDestroy || function () { };
|
|
39
|
+
target.prototype.onModuleDestroy = async function () {
|
|
40
|
+
await originalOnModuleDestroy.call(this);
|
|
41
|
+
if (this.natsSubscription) {
|
|
42
|
+
this.natsSubscription.unsubscribe();
|
|
43
|
+
}
|
|
44
|
+
if (this.natsConnection) {
|
|
45
|
+
await this.natsConnection.close();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { NatsConnection } from "nats";
|
|
2
|
+
import { Subscription, SubscriptionContext, SubscriptionOptions } from "../../common";
|
|
3
|
+
export interface NevoNatsClientOptions {
|
|
4
|
+
servers?: string[];
|
|
5
|
+
timeoutMs?: number;
|
|
6
|
+
debug?: boolean;
|
|
7
|
+
serviceName?: string;
|
|
8
|
+
authToken?: string;
|
|
9
|
+
backoff?: {
|
|
10
|
+
enabled?: boolean;
|
|
11
|
+
baseMs?: number;
|
|
12
|
+
maxMs?: number;
|
|
13
|
+
maxAttempts?: number;
|
|
14
|
+
jitter?: boolean;
|
|
15
|
+
};
|
|
16
|
+
discovery?: {
|
|
17
|
+
enabled?: boolean;
|
|
18
|
+
heartbeatIntervalMs?: number;
|
|
19
|
+
ttlMs?: number;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export declare class NevoNatsClient {
|
|
23
|
+
private readonly nc;
|
|
24
|
+
private readonly codec;
|
|
25
|
+
private readonly serviceNames;
|
|
26
|
+
private readonly timeoutMs;
|
|
27
|
+
private readonly debug;
|
|
28
|
+
private readonly serviceName?;
|
|
29
|
+
private readonly authToken?;
|
|
30
|
+
private readonly backoffEnabled;
|
|
31
|
+
private readonly backoffBaseMs;
|
|
32
|
+
private readonly backoffMaxMs;
|
|
33
|
+
private readonly backoffMaxAttempts;
|
|
34
|
+
private readonly backoffJitter;
|
|
35
|
+
private readonly inFlight;
|
|
36
|
+
private readonly discoveryRegistry;
|
|
37
|
+
private readonly discoveryEnabled;
|
|
38
|
+
private readonly discoveryHeartbeatIntervalMs;
|
|
39
|
+
private readonly discoveryTtlMs;
|
|
40
|
+
private discoveryTimer?;
|
|
41
|
+
private discoverySubscription?;
|
|
42
|
+
constructor(nc: NatsConnection, serviceNames: string[], options?: NevoNatsClientOptions);
|
|
43
|
+
static create(serviceNames: string[], options?: NevoNatsClientOptions): Promise<NevoNatsClient>;
|
|
44
|
+
private createMessagePayload;
|
|
45
|
+
private waitForInFlightSlot;
|
|
46
|
+
query<T = any>(serviceName: string, method: string, params: any): Promise<T>;
|
|
47
|
+
emit(serviceName: string, method: string, params: any): Promise<void>;
|
|
48
|
+
publish(serviceName: string, method: string, params: any): Promise<void>;
|
|
49
|
+
broadcast(method: string, params: any): Promise<void>;
|
|
50
|
+
subscribe<T = any>(serviceName: string, method: string, options: SubscriptionOptions | undefined, handler: (data: T, context: SubscriptionContext) => Promise<void> | void): Promise<Subscription>;
|
|
51
|
+
getAvailableServices(): string[];
|
|
52
|
+
getDiscoveredServices(): import("../../common").DiscoveryEntry[];
|
|
53
|
+
isServiceAvailable(serviceName: string): boolean;
|
|
54
|
+
private initDiscovery;
|
|
55
|
+
}
|