@lokative/messaging 0.1.1 → 1.1.1
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/dist/cjs/index.d.ts +9 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/messaging.config.d.ts +15 -0
- package/dist/cjs/messaging.config.js +2 -0
- package/dist/cjs/messaging.decorator.d.ts +2 -0
- package/dist/cjs/messaging.decorator.js +20 -0
- package/dist/cjs/messaging.module.d.ts +5 -0
- package/dist/cjs/messaging.module.js +34 -0
- package/dist/cjs/messaging.publisher.d.ts +6 -0
- package/dist/cjs/messaging.publisher.js +32 -0
- package/dist/cjs/messaging.registry.d.ts +12 -0
- package/dist/cjs/messaging.registry.js +67 -0
- package/dist/cjs/transport.interface.d.ts +18 -0
- package/dist/cjs/transport.interface.js +4 -0
- package/dist/cjs/transports/kafka.transport.d.ts +15 -0
- package/dist/cjs/transports/kafka.transport.js +82 -0
- package/dist/cjs/transports/nats.transport.d.ts +20 -0
- package/dist/cjs/transports/nats.transport.js +90 -0
- package/dist/cjs/transports/redis.transport.d.ts +16 -0
- package/dist/cjs/transports/redis.transport.js +71 -0
- package/dist/esm/index.d.ts +9 -0
- package/dist/esm/index.js +8 -0
- package/dist/esm/messaging.config.d.ts +15 -0
- package/dist/esm/messaging.config.js +1 -0
- package/dist/esm/messaging.decorator.d.ts +2 -0
- package/dist/esm/messaging.decorator.js +16 -0
- package/dist/esm/messaging.module.d.ts +5 -0
- package/dist/esm/messaging.module.js +31 -0
- package/dist/esm/messaging.publisher.d.ts +6 -0
- package/dist/esm/messaging.publisher.js +29 -0
- package/dist/esm/messaging.registry.d.ts +12 -0
- package/dist/esm/messaging.registry.js +64 -0
- package/dist/esm/transport.interface.d.ts +18 -0
- package/dist/esm/transport.interface.js +1 -0
- package/dist/esm/transports/kafka.transport.d.ts +15 -0
- package/dist/esm/transports/kafka.transport.js +79 -0
- package/dist/esm/transports/nats.transport.d.ts +20 -0
- package/dist/esm/transports/nats.transport.js +87 -0
- package/dist/esm/transports/redis.transport.d.ts +16 -0
- package/dist/esm/transports/redis.transport.js +68 -0
- package/package.json +9 -7
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { MessagingConfig } from './messaging.config';
|
|
2
|
+
export { MessagingTransport, MessageEnvelope, Subscription, SubscribeOptions, MESSAGING_TRANSPORT } from './transport.interface';
|
|
3
|
+
export { Incoming, Outgoing } from './messaging.decorator';
|
|
4
|
+
export { MessagingModule } from './messaging.module';
|
|
5
|
+
export { MessagingPublisher } from './messaging.publisher';
|
|
6
|
+
export { MessagingConsumerRegistry } from './messaging.registry';
|
|
7
|
+
export { NatsTransport, NatsTransportOptions } from './transports/nats.transport';
|
|
8
|
+
export { KafkaTransport, KafkaTransportOptions } from './transports/kafka.transport';
|
|
9
|
+
export { RedisTransport, RedisTransportOptions } from './transports/redis.transport';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedisTransport = exports.KafkaTransport = exports.NatsTransport = exports.MessagingConsumerRegistry = exports.MessagingPublisher = exports.MessagingModule = exports.Outgoing = exports.Incoming = exports.MESSAGING_TRANSPORT = void 0;
|
|
4
|
+
var transport_interface_1 = require("./transport.interface");
|
|
5
|
+
Object.defineProperty(exports, "MESSAGING_TRANSPORT", { enumerable: true, get: function () { return transport_interface_1.MESSAGING_TRANSPORT; } });
|
|
6
|
+
var messaging_decorator_1 = require("./messaging.decorator");
|
|
7
|
+
Object.defineProperty(exports, "Incoming", { enumerable: true, get: function () { return messaging_decorator_1.Incoming; } });
|
|
8
|
+
Object.defineProperty(exports, "Outgoing", { enumerable: true, get: function () { return messaging_decorator_1.Outgoing; } });
|
|
9
|
+
var messaging_module_1 = require("./messaging.module");
|
|
10
|
+
Object.defineProperty(exports, "MessagingModule", { enumerable: true, get: function () { return messaging_module_1.MessagingModule; } });
|
|
11
|
+
var messaging_publisher_1 = require("./messaging.publisher");
|
|
12
|
+
Object.defineProperty(exports, "MessagingPublisher", { enumerable: true, get: function () { return messaging_publisher_1.MessagingPublisher; } });
|
|
13
|
+
var messaging_registry_1 = require("./messaging.registry");
|
|
14
|
+
Object.defineProperty(exports, "MessagingConsumerRegistry", { enumerable: true, get: function () { return messaging_registry_1.MessagingConsumerRegistry; } });
|
|
15
|
+
var nats_transport_1 = require("./transports/nats.transport");
|
|
16
|
+
Object.defineProperty(exports, "NatsTransport", { enumerable: true, get: function () { return nats_transport_1.NatsTransport; } });
|
|
17
|
+
var kafka_transport_1 = require("./transports/kafka.transport");
|
|
18
|
+
Object.defineProperty(exports, "KafkaTransport", { enumerable: true, get: function () { return kafka_transport_1.KafkaTransport; } });
|
|
19
|
+
var redis_transport_1 = require("./transports/redis.transport");
|
|
20
|
+
Object.defineProperty(exports, "RedisTransport", { enumerable: true, get: function () { return redis_transport_1.RedisTransport; } });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Type } from '@nestjs/common';
|
|
2
|
+
import type { MessagingTransport } from './transport.interface';
|
|
3
|
+
export interface MessagingConfig {
|
|
4
|
+
transport: Type<MessagingTransport>;
|
|
5
|
+
transportOptions?: Record<string, any>;
|
|
6
|
+
streams?: {
|
|
7
|
+
name: string;
|
|
8
|
+
subjects: string[];
|
|
9
|
+
}[];
|
|
10
|
+
consumers?: {
|
|
11
|
+
group?: string;
|
|
12
|
+
retry?: number;
|
|
13
|
+
dlq?: boolean;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Incoming = Incoming;
|
|
4
|
+
exports.Outgoing = Outgoing;
|
|
5
|
+
function Incoming(subject) {
|
|
6
|
+
return (target, key) => {
|
|
7
|
+
Reflect.defineMetadata("msg:incoming", { subject }, target[key]);
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
function Outgoing(subject) {
|
|
11
|
+
return (target, key, descriptor) => {
|
|
12
|
+
const original = descriptor.value;
|
|
13
|
+
descriptor.value = async function (...args) {
|
|
14
|
+
const result = await original.apply(this, args);
|
|
15
|
+
const publisher = this.publisher;
|
|
16
|
+
await publisher.publish(subject, result);
|
|
17
|
+
return result;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
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 MessagingModule_1;
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.MessagingModule = void 0;
|
|
11
|
+
const common_1 = require("@nestjs/common");
|
|
12
|
+
const core_1 = require("@nestjs/core");
|
|
13
|
+
const transport_interface_1 = require("./transport.interface");
|
|
14
|
+
const messaging_publisher_1 = require("./messaging.publisher");
|
|
15
|
+
const messaging_registry_1 = require("./messaging.registry");
|
|
16
|
+
let MessagingModule = MessagingModule_1 = class MessagingModule {
|
|
17
|
+
static register(config) {
|
|
18
|
+
return {
|
|
19
|
+
module: MessagingModule_1,
|
|
20
|
+
imports: [core_1.DiscoveryModule],
|
|
21
|
+
providers: [
|
|
22
|
+
{ provide: "MSG_CONFIG", useValue: config },
|
|
23
|
+
{ provide: transport_interface_1.MESSAGING_TRANSPORT, useClass: config.transport },
|
|
24
|
+
messaging_publisher_1.MessagingPublisher,
|
|
25
|
+
messaging_registry_1.MessagingConsumerRegistry,
|
|
26
|
+
],
|
|
27
|
+
exports: [messaging_publisher_1.MessagingPublisher],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
exports.MessagingModule = MessagingModule;
|
|
32
|
+
exports.MessagingModule = MessagingModule = MessagingModule_1 = __decorate([
|
|
33
|
+
(0, common_1.Module)({})
|
|
34
|
+
], MessagingModule);
|
|
@@ -0,0 +1,32 @@
|
|
|
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.MessagingPublisher = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const transport_interface_1 = require("./transport.interface");
|
|
18
|
+
let MessagingPublisher = class MessagingPublisher {
|
|
19
|
+
transport;
|
|
20
|
+
constructor(transport) {
|
|
21
|
+
this.transport = transport;
|
|
22
|
+
}
|
|
23
|
+
async publish(subject, payload) {
|
|
24
|
+
await this.transport.publish(subject, payload);
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
exports.MessagingPublisher = MessagingPublisher;
|
|
28
|
+
exports.MessagingPublisher = MessagingPublisher = __decorate([
|
|
29
|
+
(0, common_1.Injectable)(),
|
|
30
|
+
__param(0, (0, common_1.Inject)(transport_interface_1.MESSAGING_TRANSPORT)),
|
|
31
|
+
__metadata("design:paramtypes", [Object])
|
|
32
|
+
], MessagingPublisher);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { OnModuleInit } from "@nestjs/common";
|
|
2
|
+
import { DiscoveryService } from "@nestjs/core/discovery";
|
|
3
|
+
import { MessagingTransport } from "./transport.interface";
|
|
4
|
+
import type { MessagingConfig } from "./messaging.config";
|
|
5
|
+
export declare class MessagingConsumerRegistry implements OnModuleInit {
|
|
6
|
+
private transport;
|
|
7
|
+
private discovery;
|
|
8
|
+
private config;
|
|
9
|
+
constructor(transport: MessagingTransport, discovery: DiscoveryService, config: MessagingConfig);
|
|
10
|
+
onModuleInit(): Promise<void>;
|
|
11
|
+
private startConsumer;
|
|
12
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
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.MessagingConsumerRegistry = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const discovery_1 = require("@nestjs/core/discovery");
|
|
18
|
+
const transport_interface_1 = require("./transport.interface");
|
|
19
|
+
let MessagingConsumerRegistry = class MessagingConsumerRegistry {
|
|
20
|
+
transport;
|
|
21
|
+
discovery;
|
|
22
|
+
config;
|
|
23
|
+
constructor(transport, discovery, config) {
|
|
24
|
+
this.transport = transport;
|
|
25
|
+
this.discovery = discovery;
|
|
26
|
+
this.config = config;
|
|
27
|
+
}
|
|
28
|
+
async onModuleInit() {
|
|
29
|
+
await this.transport.connect();
|
|
30
|
+
const providers = this.discovery.getProviders();
|
|
31
|
+
for (const provider of providers) {
|
|
32
|
+
const instance = provider.instance;
|
|
33
|
+
if (!instance)
|
|
34
|
+
continue;
|
|
35
|
+
const proto = Object.getPrototypeOf(instance);
|
|
36
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
37
|
+
const handler = instance[key];
|
|
38
|
+
if (typeof handler !== 'function')
|
|
39
|
+
continue;
|
|
40
|
+
const meta = Reflect.getMetadata("msg:incoming", handler);
|
|
41
|
+
if (!meta)
|
|
42
|
+
continue;
|
|
43
|
+
this.startConsumer(instance, handler, meta);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async startConsumer(instance, handler, meta) {
|
|
48
|
+
const group = this.config.consumers?.group;
|
|
49
|
+
const sub = await this.transport.subscribe(meta.subject, { group });
|
|
50
|
+
for await (const msg of sub) {
|
|
51
|
+
try {
|
|
52
|
+
await handler.call(instance, msg.data);
|
|
53
|
+
msg.ack();
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
msg.nak();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
exports.MessagingConsumerRegistry = MessagingConsumerRegistry;
|
|
62
|
+
exports.MessagingConsumerRegistry = MessagingConsumerRegistry = __decorate([
|
|
63
|
+
(0, common_1.Injectable)(),
|
|
64
|
+
__param(0, (0, common_1.Inject)(transport_interface_1.MESSAGING_TRANSPORT)),
|
|
65
|
+
__param(2, (0, common_1.Inject)("MSG_CONFIG")),
|
|
66
|
+
__metadata("design:paramtypes", [Object, discovery_1.DiscoveryService, Object])
|
|
67
|
+
], MessagingConsumerRegistry);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface MessageEnvelope {
|
|
2
|
+
subject: string;
|
|
3
|
+
data: any;
|
|
4
|
+
ack(): void;
|
|
5
|
+
nak(): void;
|
|
6
|
+
}
|
|
7
|
+
export interface Subscription {
|
|
8
|
+
[Symbol.asyncIterator](): AsyncIterator<MessageEnvelope>;
|
|
9
|
+
}
|
|
10
|
+
export interface MessagingTransport {
|
|
11
|
+
connect(): Promise<void>;
|
|
12
|
+
publish(subject: string, payload: any): Promise<void>;
|
|
13
|
+
subscribe(subject: string, options?: SubscribeOptions): Promise<Subscription>;
|
|
14
|
+
}
|
|
15
|
+
export interface SubscribeOptions {
|
|
16
|
+
group?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const MESSAGING_TRANSPORT = "MESSAGING_TRANSPORT";
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MessagingTransport, Subscription, SubscribeOptions } from '../transport.interface';
|
|
2
|
+
import type { MessagingConfig } from '../messaging.config';
|
|
3
|
+
export interface KafkaTransportOptions {
|
|
4
|
+
brokers: string[];
|
|
5
|
+
clientId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class KafkaTransport implements MessagingTransport {
|
|
8
|
+
private config;
|
|
9
|
+
private kafka;
|
|
10
|
+
private producer;
|
|
11
|
+
constructor(config: MessagingConfig);
|
|
12
|
+
connect(): Promise<void>;
|
|
13
|
+
publish(subject: string, payload: any): Promise<void>;
|
|
14
|
+
subscribe(subject: string, options?: SubscribeOptions): Promise<Subscription>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
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.KafkaTransport = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const kafkajs_1 = require("kafkajs");
|
|
18
|
+
let KafkaTransport = class KafkaTransport {
|
|
19
|
+
config;
|
|
20
|
+
kafka;
|
|
21
|
+
producer;
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.config = config;
|
|
24
|
+
}
|
|
25
|
+
async connect() {
|
|
26
|
+
const opts = this.config.transportOptions;
|
|
27
|
+
this.kafka = new kafkajs_1.Kafka({
|
|
28
|
+
clientId: opts.clientId ?? 'nestjs-app',
|
|
29
|
+
brokers: opts.brokers,
|
|
30
|
+
});
|
|
31
|
+
this.producer = this.kafka.producer();
|
|
32
|
+
await this.producer.connect();
|
|
33
|
+
}
|
|
34
|
+
async publish(subject, payload) {
|
|
35
|
+
await this.producer.send({
|
|
36
|
+
topic: subject,
|
|
37
|
+
messages: [{ value: JSON.stringify(payload) }],
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async subscribe(subject, options) {
|
|
41
|
+
const consumer = this.kafka.consumer({
|
|
42
|
+
groupId: options?.group ?? 'nest-consumer',
|
|
43
|
+
});
|
|
44
|
+
await consumer.connect();
|
|
45
|
+
await consumer.subscribe({ topic: subject, fromBeginning: false });
|
|
46
|
+
const buffer = [];
|
|
47
|
+
let waiting = null;
|
|
48
|
+
await consumer.run({
|
|
49
|
+
eachMessage: async ({ topic, message }) => {
|
|
50
|
+
const envelope = {
|
|
51
|
+
subject: topic,
|
|
52
|
+
data: JSON.parse(message.value?.toString() ?? '{}'),
|
|
53
|
+
ack: () => { }, // kafkajs auto-commits offsets
|
|
54
|
+
nak: () => { },
|
|
55
|
+
};
|
|
56
|
+
if (waiting) {
|
|
57
|
+
const resolve = waiting;
|
|
58
|
+
waiting = null;
|
|
59
|
+
resolve({ done: false, value: envelope });
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
buffer.push(envelope);
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
const iterator = {
|
|
67
|
+
next() {
|
|
68
|
+
if (buffer.length > 0) {
|
|
69
|
+
return Promise.resolve({ done: false, value: buffer.shift() });
|
|
70
|
+
}
|
|
71
|
+
return new Promise(r => { waiting = r; });
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
return { [Symbol.asyncIterator]: () => iterator };
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
exports.KafkaTransport = KafkaTransport;
|
|
78
|
+
exports.KafkaTransport = KafkaTransport = __decorate([
|
|
79
|
+
(0, common_1.Injectable)(),
|
|
80
|
+
__param(0, (0, common_1.Inject)('MSG_CONFIG')),
|
|
81
|
+
__metadata("design:paramtypes", [Object])
|
|
82
|
+
], KafkaTransport);
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MessagingTransport, Subscription, SubscribeOptions } from '../transport.interface';
|
|
2
|
+
import type { MessagingConfig } from '../messaging.config';
|
|
3
|
+
export interface NatsTransportOptions {
|
|
4
|
+
servers: string[];
|
|
5
|
+
streams?: {
|
|
6
|
+
name: string;
|
|
7
|
+
subjects: string[];
|
|
8
|
+
}[];
|
|
9
|
+
}
|
|
10
|
+
export declare class NatsTransport implements MessagingTransport {
|
|
11
|
+
private config;
|
|
12
|
+
private nc;
|
|
13
|
+
private js;
|
|
14
|
+
private jsm;
|
|
15
|
+
constructor(config: MessagingConfig);
|
|
16
|
+
connect(): Promise<void>;
|
|
17
|
+
publish(subject: string, payload: any): Promise<void>;
|
|
18
|
+
subscribe(subject: string, options?: SubscribeOptions): Promise<Subscription>;
|
|
19
|
+
private ensureStreams;
|
|
20
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
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.NatsTransport = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const nats_1 = require("nats");
|
|
18
|
+
let NatsTransport = class NatsTransport {
|
|
19
|
+
config;
|
|
20
|
+
nc;
|
|
21
|
+
js;
|
|
22
|
+
jsm;
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
}
|
|
26
|
+
async connect() {
|
|
27
|
+
const opts = this.config.transportOptions;
|
|
28
|
+
this.nc = await (0, nats_1.connect)({ servers: opts.servers });
|
|
29
|
+
this.js = this.nc.jetstream();
|
|
30
|
+
this.jsm = await this.nc.jetstreamManager();
|
|
31
|
+
await this.ensureStreams(opts.streams ?? this.config.streams ?? []);
|
|
32
|
+
}
|
|
33
|
+
async publish(subject, payload) {
|
|
34
|
+
await this.js.publish(subject, Buffer.from(JSON.stringify(payload)));
|
|
35
|
+
}
|
|
36
|
+
async subscribe(subject, options) {
|
|
37
|
+
const durable = options?.group ?? 'nest-consumer';
|
|
38
|
+
const opts = (0, nats_1.consumerOpts)();
|
|
39
|
+
opts.durable(durable);
|
|
40
|
+
opts.ackExplicit();
|
|
41
|
+
opts.deliverTo(durable);
|
|
42
|
+
const sub = await this.js.subscribe(subject, opts);
|
|
43
|
+
const iterator = {
|
|
44
|
+
async next() {
|
|
45
|
+
const result = await sub[Symbol.asyncIterator]().next();
|
|
46
|
+
if (result.done)
|
|
47
|
+
return { done: true, value: undefined };
|
|
48
|
+
const msg = result.value;
|
|
49
|
+
return {
|
|
50
|
+
done: false,
|
|
51
|
+
value: {
|
|
52
|
+
subject: msg.subject,
|
|
53
|
+
data: JSON.parse(msg.data.toString()),
|
|
54
|
+
ack: () => msg.ack(),
|
|
55
|
+
nak: () => msg.nak(),
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
return { [Symbol.asyncIterator]: () => iterator };
|
|
61
|
+
}
|
|
62
|
+
async ensureStreams(streams) {
|
|
63
|
+
for (const stream of streams) {
|
|
64
|
+
try {
|
|
65
|
+
const info = await this.jsm.streams.info(stream.name);
|
|
66
|
+
const existing = info.config.subjects ?? [];
|
|
67
|
+
const missing = stream.subjects.filter(s => !existing.includes(s));
|
|
68
|
+
if (missing.length > 0) {
|
|
69
|
+
info.config.subjects = [...existing, ...missing];
|
|
70
|
+
await this.jsm.streams.update(stream.name, info.config);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
try {
|
|
75
|
+
await this.jsm.streams.add({ name: stream.name, subjects: stream.subjects });
|
|
76
|
+
}
|
|
77
|
+
catch (addErr) {
|
|
78
|
+
if (addErr?.api_error?.err_code !== 10065)
|
|
79
|
+
throw addErr;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
exports.NatsTransport = NatsTransport;
|
|
86
|
+
exports.NatsTransport = NatsTransport = __decorate([
|
|
87
|
+
(0, common_1.Injectable)(),
|
|
88
|
+
__param(0, (0, common_1.Inject)('MSG_CONFIG')),
|
|
89
|
+
__metadata("design:paramtypes", [Object])
|
|
90
|
+
], NatsTransport);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MessagingTransport, Subscription, SubscribeOptions } from '../transport.interface';
|
|
2
|
+
import type { MessagingConfig } from '../messaging.config';
|
|
3
|
+
export interface RedisTransportOptions {
|
|
4
|
+
url?: string;
|
|
5
|
+
host?: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class RedisTransport implements MessagingTransport {
|
|
9
|
+
private config;
|
|
10
|
+
private pub;
|
|
11
|
+
private sub;
|
|
12
|
+
constructor(config: MessagingConfig);
|
|
13
|
+
connect(): Promise<void>;
|
|
14
|
+
publish(subject: string, payload: any): Promise<void>;
|
|
15
|
+
subscribe(subject: string, _options?: SubscribeOptions): Promise<Subscription>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
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.RedisTransport = void 0;
|
|
16
|
+
const common_1 = require("@nestjs/common");
|
|
17
|
+
const redis_1 = require("redis");
|
|
18
|
+
let RedisTransport = class RedisTransport {
|
|
19
|
+
config;
|
|
20
|
+
pub;
|
|
21
|
+
sub;
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.config = config;
|
|
24
|
+
}
|
|
25
|
+
async connect() {
|
|
26
|
+
const opts = this.config.transportOptions;
|
|
27
|
+
const url = opts.url ?? `redis://${opts.host ?? 'localhost'}:${opts.port ?? 6379}`;
|
|
28
|
+
this.pub = (0, redis_1.createClient)({ url });
|
|
29
|
+
this.sub = this.pub.duplicate();
|
|
30
|
+
await this.pub.connect();
|
|
31
|
+
await this.sub.connect();
|
|
32
|
+
}
|
|
33
|
+
async publish(subject, payload) {
|
|
34
|
+
await this.pub.publish(subject, JSON.stringify(payload));
|
|
35
|
+
}
|
|
36
|
+
async subscribe(subject, _options) {
|
|
37
|
+
const buffer = [];
|
|
38
|
+
let waiting = null;
|
|
39
|
+
await this.sub.subscribe(subject, (message, channel) => {
|
|
40
|
+
const envelope = {
|
|
41
|
+
subject: channel,
|
|
42
|
+
data: JSON.parse(message),
|
|
43
|
+
ack: () => { }, // redis pub/sub has no ack mechanism
|
|
44
|
+
nak: () => { },
|
|
45
|
+
};
|
|
46
|
+
if (waiting) {
|
|
47
|
+
const resolve = waiting;
|
|
48
|
+
waiting = null;
|
|
49
|
+
resolve({ done: false, value: envelope });
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
buffer.push(envelope);
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
const iterator = {
|
|
56
|
+
next() {
|
|
57
|
+
if (buffer.length > 0) {
|
|
58
|
+
return Promise.resolve({ done: false, value: buffer.shift() });
|
|
59
|
+
}
|
|
60
|
+
return new Promise(r => { waiting = r; });
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
return { [Symbol.asyncIterator]: () => iterator };
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
exports.RedisTransport = RedisTransport;
|
|
67
|
+
exports.RedisTransport = RedisTransport = __decorate([
|
|
68
|
+
(0, common_1.Injectable)(),
|
|
69
|
+
__param(0, (0, common_1.Inject)('MSG_CONFIG')),
|
|
70
|
+
__metadata("design:paramtypes", [Object])
|
|
71
|
+
], RedisTransport);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { MessagingConfig } from './messaging.config';
|
|
2
|
+
export { MessagingTransport, MessageEnvelope, Subscription, SubscribeOptions, MESSAGING_TRANSPORT } from './transport.interface';
|
|
3
|
+
export { Incoming, Outgoing } from './messaging.decorator';
|
|
4
|
+
export { MessagingModule } from './messaging.module';
|
|
5
|
+
export { MessagingPublisher } from './messaging.publisher';
|
|
6
|
+
export { MessagingConsumerRegistry } from './messaging.registry';
|
|
7
|
+
export { NatsTransport, NatsTransportOptions } from './transports/nats.transport';
|
|
8
|
+
export { KafkaTransport, KafkaTransportOptions } from './transports/kafka.transport';
|
|
9
|
+
export { RedisTransport, RedisTransportOptions } from './transports/redis.transport';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { MESSAGING_TRANSPORT } from './transport.interface';
|
|
2
|
+
export { Incoming, Outgoing } from './messaging.decorator';
|
|
3
|
+
export { MessagingModule } from './messaging.module';
|
|
4
|
+
export { MessagingPublisher } from './messaging.publisher';
|
|
5
|
+
export { MessagingConsumerRegistry } from './messaging.registry';
|
|
6
|
+
export { NatsTransport } from './transports/nats.transport';
|
|
7
|
+
export { KafkaTransport } from './transports/kafka.transport';
|
|
8
|
+
export { RedisTransport } from './transports/redis.transport';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Type } from '@nestjs/common';
|
|
2
|
+
import type { MessagingTransport } from './transport.interface';
|
|
3
|
+
export interface MessagingConfig {
|
|
4
|
+
transport: Type<MessagingTransport>;
|
|
5
|
+
transportOptions?: Record<string, any>;
|
|
6
|
+
streams?: {
|
|
7
|
+
name: string;
|
|
8
|
+
subjects: string[];
|
|
9
|
+
}[];
|
|
10
|
+
consumers?: {
|
|
11
|
+
group?: string;
|
|
12
|
+
retry?: number;
|
|
13
|
+
dlq?: boolean;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function Incoming(subject) {
|
|
2
|
+
return (target, key) => {
|
|
3
|
+
Reflect.defineMetadata("msg:incoming", { subject }, target[key]);
|
|
4
|
+
};
|
|
5
|
+
}
|
|
6
|
+
export function Outgoing(subject) {
|
|
7
|
+
return (target, key, descriptor) => {
|
|
8
|
+
const original = descriptor.value;
|
|
9
|
+
descriptor.value = async function (...args) {
|
|
10
|
+
const result = await original.apply(this, args);
|
|
11
|
+
const publisher = this.publisher;
|
|
12
|
+
await publisher.publish(subject, result);
|
|
13
|
+
return result;
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var MessagingModule_1;
|
|
8
|
+
import { Module } from "@nestjs/common";
|
|
9
|
+
import { DiscoveryModule } from "@nestjs/core";
|
|
10
|
+
import { MESSAGING_TRANSPORT } from "./transport.interface";
|
|
11
|
+
import { MessagingPublisher } from "./messaging.publisher";
|
|
12
|
+
import { MessagingConsumerRegistry } from "./messaging.registry";
|
|
13
|
+
let MessagingModule = MessagingModule_1 = class MessagingModule {
|
|
14
|
+
static register(config) {
|
|
15
|
+
return {
|
|
16
|
+
module: MessagingModule_1,
|
|
17
|
+
imports: [DiscoveryModule],
|
|
18
|
+
providers: [
|
|
19
|
+
{ provide: "MSG_CONFIG", useValue: config },
|
|
20
|
+
{ provide: MESSAGING_TRANSPORT, useClass: config.transport },
|
|
21
|
+
MessagingPublisher,
|
|
22
|
+
MessagingConsumerRegistry,
|
|
23
|
+
],
|
|
24
|
+
exports: [MessagingPublisher],
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
MessagingModule = MessagingModule_1 = __decorate([
|
|
29
|
+
Module({})
|
|
30
|
+
], MessagingModule);
|
|
31
|
+
export { MessagingModule };
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
14
|
+
import { MESSAGING_TRANSPORT } from "./transport.interface";
|
|
15
|
+
let MessagingPublisher = class MessagingPublisher {
|
|
16
|
+
transport;
|
|
17
|
+
constructor(transport) {
|
|
18
|
+
this.transport = transport;
|
|
19
|
+
}
|
|
20
|
+
async publish(subject, payload) {
|
|
21
|
+
await this.transport.publish(subject, payload);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
MessagingPublisher = __decorate([
|
|
25
|
+
Injectable(),
|
|
26
|
+
__param(0, Inject(MESSAGING_TRANSPORT)),
|
|
27
|
+
__metadata("design:paramtypes", [Object])
|
|
28
|
+
], MessagingPublisher);
|
|
29
|
+
export { MessagingPublisher };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { OnModuleInit } from "@nestjs/common";
|
|
2
|
+
import { DiscoveryService } from "@nestjs/core/discovery";
|
|
3
|
+
import { MessagingTransport } from "./transport.interface";
|
|
4
|
+
import type { MessagingConfig } from "./messaging.config";
|
|
5
|
+
export declare class MessagingConsumerRegistry implements OnModuleInit {
|
|
6
|
+
private transport;
|
|
7
|
+
private discovery;
|
|
8
|
+
private config;
|
|
9
|
+
constructor(transport: MessagingTransport, discovery: DiscoveryService, config: MessagingConfig);
|
|
10
|
+
onModuleInit(): Promise<void>;
|
|
11
|
+
private startConsumer;
|
|
12
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { Inject, Injectable } from "@nestjs/common";
|
|
14
|
+
import { DiscoveryService } from "@nestjs/core/discovery";
|
|
15
|
+
import { MESSAGING_TRANSPORT } from "./transport.interface";
|
|
16
|
+
let MessagingConsumerRegistry = class MessagingConsumerRegistry {
|
|
17
|
+
transport;
|
|
18
|
+
discovery;
|
|
19
|
+
config;
|
|
20
|
+
constructor(transport, discovery, config) {
|
|
21
|
+
this.transport = transport;
|
|
22
|
+
this.discovery = discovery;
|
|
23
|
+
this.config = config;
|
|
24
|
+
}
|
|
25
|
+
async onModuleInit() {
|
|
26
|
+
await this.transport.connect();
|
|
27
|
+
const providers = this.discovery.getProviders();
|
|
28
|
+
for (const provider of providers) {
|
|
29
|
+
const instance = provider.instance;
|
|
30
|
+
if (!instance)
|
|
31
|
+
continue;
|
|
32
|
+
const proto = Object.getPrototypeOf(instance);
|
|
33
|
+
for (const key of Object.getOwnPropertyNames(proto)) {
|
|
34
|
+
const handler = instance[key];
|
|
35
|
+
if (typeof handler !== 'function')
|
|
36
|
+
continue;
|
|
37
|
+
const meta = Reflect.getMetadata("msg:incoming", handler);
|
|
38
|
+
if (!meta)
|
|
39
|
+
continue;
|
|
40
|
+
this.startConsumer(instance, handler, meta);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async startConsumer(instance, handler, meta) {
|
|
45
|
+
const group = this.config.consumers?.group;
|
|
46
|
+
const sub = await this.transport.subscribe(meta.subject, { group });
|
|
47
|
+
for await (const msg of sub) {
|
|
48
|
+
try {
|
|
49
|
+
await handler.call(instance, msg.data);
|
|
50
|
+
msg.ack();
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
msg.nak();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
MessagingConsumerRegistry = __decorate([
|
|
59
|
+
Injectable(),
|
|
60
|
+
__param(0, Inject(MESSAGING_TRANSPORT)),
|
|
61
|
+
__param(2, Inject("MSG_CONFIG")),
|
|
62
|
+
__metadata("design:paramtypes", [Object, DiscoveryService, Object])
|
|
63
|
+
], MessagingConsumerRegistry);
|
|
64
|
+
export { MessagingConsumerRegistry };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface MessageEnvelope {
|
|
2
|
+
subject: string;
|
|
3
|
+
data: any;
|
|
4
|
+
ack(): void;
|
|
5
|
+
nak(): void;
|
|
6
|
+
}
|
|
7
|
+
export interface Subscription {
|
|
8
|
+
[Symbol.asyncIterator](): AsyncIterator<MessageEnvelope>;
|
|
9
|
+
}
|
|
10
|
+
export interface MessagingTransport {
|
|
11
|
+
connect(): Promise<void>;
|
|
12
|
+
publish(subject: string, payload: any): Promise<void>;
|
|
13
|
+
subscribe(subject: string, options?: SubscribeOptions): Promise<Subscription>;
|
|
14
|
+
}
|
|
15
|
+
export interface SubscribeOptions {
|
|
16
|
+
group?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare const MESSAGING_TRANSPORT = "MESSAGING_TRANSPORT";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const MESSAGING_TRANSPORT = 'MESSAGING_TRANSPORT';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MessagingTransport, Subscription, SubscribeOptions } from '../transport.interface';
|
|
2
|
+
import type { MessagingConfig } from '../messaging.config';
|
|
3
|
+
export interface KafkaTransportOptions {
|
|
4
|
+
brokers: string[];
|
|
5
|
+
clientId?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class KafkaTransport implements MessagingTransport {
|
|
8
|
+
private config;
|
|
9
|
+
private kafka;
|
|
10
|
+
private producer;
|
|
11
|
+
constructor(config: MessagingConfig);
|
|
12
|
+
connect(): Promise<void>;
|
|
13
|
+
publish(subject: string, payload: any): Promise<void>;
|
|
14
|
+
subscribe(subject: string, options?: SubscribeOptions): Promise<Subscription>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
14
|
+
import { Kafka } from 'kafkajs';
|
|
15
|
+
let KafkaTransport = class KafkaTransport {
|
|
16
|
+
config;
|
|
17
|
+
kafka;
|
|
18
|
+
producer;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
}
|
|
22
|
+
async connect() {
|
|
23
|
+
const opts = this.config.transportOptions;
|
|
24
|
+
this.kafka = new Kafka({
|
|
25
|
+
clientId: opts.clientId ?? 'nestjs-app',
|
|
26
|
+
brokers: opts.brokers,
|
|
27
|
+
});
|
|
28
|
+
this.producer = this.kafka.producer();
|
|
29
|
+
await this.producer.connect();
|
|
30
|
+
}
|
|
31
|
+
async publish(subject, payload) {
|
|
32
|
+
await this.producer.send({
|
|
33
|
+
topic: subject,
|
|
34
|
+
messages: [{ value: JSON.stringify(payload) }],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async subscribe(subject, options) {
|
|
38
|
+
const consumer = this.kafka.consumer({
|
|
39
|
+
groupId: options?.group ?? 'nest-consumer',
|
|
40
|
+
});
|
|
41
|
+
await consumer.connect();
|
|
42
|
+
await consumer.subscribe({ topic: subject, fromBeginning: false });
|
|
43
|
+
const buffer = [];
|
|
44
|
+
let waiting = null;
|
|
45
|
+
await consumer.run({
|
|
46
|
+
eachMessage: async ({ topic, message }) => {
|
|
47
|
+
const envelope = {
|
|
48
|
+
subject: topic,
|
|
49
|
+
data: JSON.parse(message.value?.toString() ?? '{}'),
|
|
50
|
+
ack: () => { }, // kafkajs auto-commits offsets
|
|
51
|
+
nak: () => { },
|
|
52
|
+
};
|
|
53
|
+
if (waiting) {
|
|
54
|
+
const resolve = waiting;
|
|
55
|
+
waiting = null;
|
|
56
|
+
resolve({ done: false, value: envelope });
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
buffer.push(envelope);
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
const iterator = {
|
|
64
|
+
next() {
|
|
65
|
+
if (buffer.length > 0) {
|
|
66
|
+
return Promise.resolve({ done: false, value: buffer.shift() });
|
|
67
|
+
}
|
|
68
|
+
return new Promise(r => { waiting = r; });
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
return { [Symbol.asyncIterator]: () => iterator };
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
KafkaTransport = __decorate([
|
|
75
|
+
Injectable(),
|
|
76
|
+
__param(0, Inject('MSG_CONFIG')),
|
|
77
|
+
__metadata("design:paramtypes", [Object])
|
|
78
|
+
], KafkaTransport);
|
|
79
|
+
export { KafkaTransport };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { MessagingTransport, Subscription, SubscribeOptions } from '../transport.interface';
|
|
2
|
+
import type { MessagingConfig } from '../messaging.config';
|
|
3
|
+
export interface NatsTransportOptions {
|
|
4
|
+
servers: string[];
|
|
5
|
+
streams?: {
|
|
6
|
+
name: string;
|
|
7
|
+
subjects: string[];
|
|
8
|
+
}[];
|
|
9
|
+
}
|
|
10
|
+
export declare class NatsTransport implements MessagingTransport {
|
|
11
|
+
private config;
|
|
12
|
+
private nc;
|
|
13
|
+
private js;
|
|
14
|
+
private jsm;
|
|
15
|
+
constructor(config: MessagingConfig);
|
|
16
|
+
connect(): Promise<void>;
|
|
17
|
+
publish(subject: string, payload: any): Promise<void>;
|
|
18
|
+
subscribe(subject: string, options?: SubscribeOptions): Promise<Subscription>;
|
|
19
|
+
private ensureStreams;
|
|
20
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
14
|
+
import { connect, consumerOpts } from 'nats';
|
|
15
|
+
let NatsTransport = class NatsTransport {
|
|
16
|
+
config;
|
|
17
|
+
nc;
|
|
18
|
+
js;
|
|
19
|
+
jsm;
|
|
20
|
+
constructor(config) {
|
|
21
|
+
this.config = config;
|
|
22
|
+
}
|
|
23
|
+
async connect() {
|
|
24
|
+
const opts = this.config.transportOptions;
|
|
25
|
+
this.nc = await connect({ servers: opts.servers });
|
|
26
|
+
this.js = this.nc.jetstream();
|
|
27
|
+
this.jsm = await this.nc.jetstreamManager();
|
|
28
|
+
await this.ensureStreams(opts.streams ?? this.config.streams ?? []);
|
|
29
|
+
}
|
|
30
|
+
async publish(subject, payload) {
|
|
31
|
+
await this.js.publish(subject, Buffer.from(JSON.stringify(payload)));
|
|
32
|
+
}
|
|
33
|
+
async subscribe(subject, options) {
|
|
34
|
+
const durable = options?.group ?? 'nest-consumer';
|
|
35
|
+
const opts = consumerOpts();
|
|
36
|
+
opts.durable(durable);
|
|
37
|
+
opts.ackExplicit();
|
|
38
|
+
opts.deliverTo(durable);
|
|
39
|
+
const sub = await this.js.subscribe(subject, opts);
|
|
40
|
+
const iterator = {
|
|
41
|
+
async next() {
|
|
42
|
+
const result = await sub[Symbol.asyncIterator]().next();
|
|
43
|
+
if (result.done)
|
|
44
|
+
return { done: true, value: undefined };
|
|
45
|
+
const msg = result.value;
|
|
46
|
+
return {
|
|
47
|
+
done: false,
|
|
48
|
+
value: {
|
|
49
|
+
subject: msg.subject,
|
|
50
|
+
data: JSON.parse(msg.data.toString()),
|
|
51
|
+
ack: () => msg.ack(),
|
|
52
|
+
nak: () => msg.nak(),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
return { [Symbol.asyncIterator]: () => iterator };
|
|
58
|
+
}
|
|
59
|
+
async ensureStreams(streams) {
|
|
60
|
+
for (const stream of streams) {
|
|
61
|
+
try {
|
|
62
|
+
const info = await this.jsm.streams.info(stream.name);
|
|
63
|
+
const existing = info.config.subjects ?? [];
|
|
64
|
+
const missing = stream.subjects.filter(s => !existing.includes(s));
|
|
65
|
+
if (missing.length > 0) {
|
|
66
|
+
info.config.subjects = [...existing, ...missing];
|
|
67
|
+
await this.jsm.streams.update(stream.name, info.config);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
try {
|
|
72
|
+
await this.jsm.streams.add({ name: stream.name, subjects: stream.subjects });
|
|
73
|
+
}
|
|
74
|
+
catch (addErr) {
|
|
75
|
+
if (addErr?.api_error?.err_code !== 10065)
|
|
76
|
+
throw addErr;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
NatsTransport = __decorate([
|
|
83
|
+
Injectable(),
|
|
84
|
+
__param(0, Inject('MSG_CONFIG')),
|
|
85
|
+
__metadata("design:paramtypes", [Object])
|
|
86
|
+
], NatsTransport);
|
|
87
|
+
export { NatsTransport };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { MessagingTransport, Subscription, SubscribeOptions } from '../transport.interface';
|
|
2
|
+
import type { MessagingConfig } from '../messaging.config';
|
|
3
|
+
export interface RedisTransportOptions {
|
|
4
|
+
url?: string;
|
|
5
|
+
host?: string;
|
|
6
|
+
port?: number;
|
|
7
|
+
}
|
|
8
|
+
export declare class RedisTransport implements MessagingTransport {
|
|
9
|
+
private config;
|
|
10
|
+
private pub;
|
|
11
|
+
private sub;
|
|
12
|
+
constructor(config: MessagingConfig);
|
|
13
|
+
connect(): Promise<void>;
|
|
14
|
+
publish(subject: string, payload: any): Promise<void>;
|
|
15
|
+
subscribe(subject: string, _options?: SubscribeOptions): Promise<Subscription>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
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;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
11
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
12
|
+
};
|
|
13
|
+
import { Inject, Injectable } from '@nestjs/common';
|
|
14
|
+
import { createClient } from 'redis';
|
|
15
|
+
let RedisTransport = class RedisTransport {
|
|
16
|
+
config;
|
|
17
|
+
pub;
|
|
18
|
+
sub;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
this.config = config;
|
|
21
|
+
}
|
|
22
|
+
async connect() {
|
|
23
|
+
const opts = this.config.transportOptions;
|
|
24
|
+
const url = opts.url ?? `redis://${opts.host ?? 'localhost'}:${opts.port ?? 6379}`;
|
|
25
|
+
this.pub = createClient({ url });
|
|
26
|
+
this.sub = this.pub.duplicate();
|
|
27
|
+
await this.pub.connect();
|
|
28
|
+
await this.sub.connect();
|
|
29
|
+
}
|
|
30
|
+
async publish(subject, payload) {
|
|
31
|
+
await this.pub.publish(subject, JSON.stringify(payload));
|
|
32
|
+
}
|
|
33
|
+
async subscribe(subject, _options) {
|
|
34
|
+
const buffer = [];
|
|
35
|
+
let waiting = null;
|
|
36
|
+
await this.sub.subscribe(subject, (message, channel) => {
|
|
37
|
+
const envelope = {
|
|
38
|
+
subject: channel,
|
|
39
|
+
data: JSON.parse(message),
|
|
40
|
+
ack: () => { }, // redis pub/sub has no ack mechanism
|
|
41
|
+
nak: () => { },
|
|
42
|
+
};
|
|
43
|
+
if (waiting) {
|
|
44
|
+
const resolve = waiting;
|
|
45
|
+
waiting = null;
|
|
46
|
+
resolve({ done: false, value: envelope });
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
buffer.push(envelope);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
const iterator = {
|
|
53
|
+
next() {
|
|
54
|
+
if (buffer.length > 0) {
|
|
55
|
+
return Promise.resolve({ done: false, value: buffer.shift() });
|
|
56
|
+
}
|
|
57
|
+
return new Promise(r => { waiting = r; });
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
return { [Symbol.asyncIterator]: () => iterator };
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
RedisTransport = __decorate([
|
|
64
|
+
Injectable(),
|
|
65
|
+
__param(0, Inject('MSG_CONFIG')),
|
|
66
|
+
__metadata("design:paramtypes", [Object])
|
|
67
|
+
], RedisTransport);
|
|
68
|
+
export { RedisTransport };
|
package/package.json
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lokative/messaging",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"description": "",
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
5
|
+
"main": "./dist/cjs/index.js",
|
|
6
|
+
"module": "./dist/esm/index.js",
|
|
7
|
+
"types": "./dist/cjs/index.d.ts",
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"types": "./dist/index.d.ts",
|
|
11
|
-
"import": "./dist/index.js"
|
|
10
|
+
"types": "./dist/esm/index.d.ts",
|
|
11
|
+
"import": "./dist/esm/index.js",
|
|
12
|
+
"require": "./dist/cjs/index.js",
|
|
13
|
+
"default": "./dist/cjs/index.js"
|
|
12
14
|
}
|
|
13
15
|
},
|
|
14
16
|
"files": [
|
|
15
17
|
"dist"
|
|
16
18
|
],
|
|
17
19
|
"scripts": {
|
|
18
|
-
"build": "tsc",
|
|
20
|
+
"build": "tsc -p tsconfig.esm.json && tsc -p tsconfig.cjs.json",
|
|
19
21
|
"test": "vitest run",
|
|
20
22
|
"prepublishOnly": "npm run build"
|
|
21
23
|
},
|