@palmetto/pubsub 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.
- package/README.md +154 -0
- package/dist/bullmq/config.d.ts +21 -0
- package/dist/bullmq/config.js +2 -0
- package/dist/bullmq/connection.d.ts +2 -0
- package/dist/bullmq/connection.js +5 -0
- package/dist/bullmq/main.d.ts +6 -0
- package/dist/bullmq/main.js +21 -0
- package/dist/bullmq/publisher.d.ts +13 -0
- package/dist/bullmq/publisher.js +62 -0
- package/dist/bullmq/pubsub.d.ts +12 -0
- package/dist/bullmq/pubsub.js +35 -0
- package/dist/bullmq/subscriber.d.ts +27 -0
- package/dist/bullmq/subscriber.js +118 -0
- package/dist/errors.d.ts +17 -0
- package/dist/errors.js +37 -0
- package/dist/interfaces.d.ts +120 -0
- package/dist/interfaces.js +28 -0
- package/dist/lazy-load.d.ts +11 -0
- package/dist/lazy-load.js +79 -0
- package/dist/main.d.ts +6 -0
- package/dist/main.js +22 -0
- package/dist/publisher.d.ts +11 -0
- package/dist/publisher.js +87 -0
- package/dist/rabbitmq/config.d.ts +84 -0
- package/dist/rabbitmq/config.js +82 -0
- package/dist/rabbitmq/connection.d.ts +41 -0
- package/dist/rabbitmq/connection.js +140 -0
- package/dist/rabbitmq/main.d.ts +5 -0
- package/dist/rabbitmq/main.js +21 -0
- package/dist/rabbitmq/publisher.d.ts +29 -0
- package/dist/rabbitmq/publisher.js +98 -0
- package/dist/rabbitmq/pubsub.d.ts +15 -0
- package/dist/rabbitmq/pubsub.js +37 -0
- package/dist/rabbitmq/subscriber.d.ts +33 -0
- package/dist/rabbitmq/subscriber.js +197 -0
- package/dist/rabbitmq/utility.d.ts +6 -0
- package/dist/rabbitmq/utility.js +11 -0
- package/dist/subscriber.d.ts +28 -0
- package/dist/subscriber.js +140 -0
- package/package.json +54 -0
- package/src/bullmq/README.md +63 -0
- package/src/rabbitmq/README.md +184 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MessageResult = exports.IdMetaSchema = exports.MetaSchema = void 0;
|
|
4
|
+
const v4_1 = require("zod/v4");
|
|
5
|
+
exports.MetaSchema = v4_1.z.object({
|
|
6
|
+
createdAt: v4_1.z.iso.datetime(),
|
|
7
|
+
schemaId: v4_1.z.string(),
|
|
8
|
+
publishedBy: v4_1.z.string(),
|
|
9
|
+
});
|
|
10
|
+
exports.IdMetaSchema = v4_1.z.object({
|
|
11
|
+
id: v4_1.z.string().optional(),
|
|
12
|
+
meta: exports.MetaSchema.optional(),
|
|
13
|
+
});
|
|
14
|
+
var MessageResult;
|
|
15
|
+
(function (MessageResult) {
|
|
16
|
+
/**
|
|
17
|
+
* the message was handled successfully or can otherwise be ignored
|
|
18
|
+
*/
|
|
19
|
+
MessageResult["Ok"] = "ok";
|
|
20
|
+
/**
|
|
21
|
+
* The message should be retried
|
|
22
|
+
*/
|
|
23
|
+
MessageResult["Retry"] = "retry";
|
|
24
|
+
/**
|
|
25
|
+
* The message failed and is either discarded or stored in a dead-letter queue
|
|
26
|
+
*/
|
|
27
|
+
MessageResult["Fail"] = "fail";
|
|
28
|
+
})(MessageResult || (exports.MessageResult = MessageResult = {}));
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This loads and returns a typed package.
|
|
3
|
+
* TPackage is a package type
|
|
4
|
+
* @returns The package
|
|
5
|
+
*/
|
|
6
|
+
export declare function lazyLoad<TPackage>({ packageName, context, }: {
|
|
7
|
+
/** The name of the package to load (eg: "bullmq") */
|
|
8
|
+
packageName: string;
|
|
9
|
+
/** The name of the module trying to lazy-load the package (eg: "BullMqPublisher") */
|
|
10
|
+
context: string;
|
|
11
|
+
}): Promise<TPackage>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
example usage:
|
|
4
|
+
|
|
5
|
+
// this imports the package as a type:
|
|
6
|
+
import type * as bullmq from "bullmq";
|
|
7
|
+
|
|
8
|
+
//then call lazyLoad with a function that is called at runtime, not a static startup call:
|
|
9
|
+
const BullMqPackage = await lazyLoad<
|
|
10
|
+
typeof bullmq,
|
|
11
|
+
>({
|
|
12
|
+
packageName: "bullmq",
|
|
13
|
+
context: "BullMqPublisher",
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// then you can use the result of lazyLoad as if it was the original imported package type:
|
|
17
|
+
const queue = new BullMqPackage.Queue(new BullMqPackage.Queue(queueName, {
|
|
18
|
+
connection: this.connection,
|
|
19
|
+
});
|
|
20
|
+
*/
|
|
21
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
22
|
+
if (k2 === undefined) k2 = k;
|
|
23
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
24
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
25
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
26
|
+
}
|
|
27
|
+
Object.defineProperty(o, k2, desc);
|
|
28
|
+
}) : (function(o, m, k, k2) {
|
|
29
|
+
if (k2 === undefined) k2 = k;
|
|
30
|
+
o[k2] = m[k];
|
|
31
|
+
}));
|
|
32
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
33
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
34
|
+
}) : function(o, v) {
|
|
35
|
+
o["default"] = v;
|
|
36
|
+
});
|
|
37
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
38
|
+
var ownKeys = function(o) {
|
|
39
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
40
|
+
var ar = [];
|
|
41
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
42
|
+
return ar;
|
|
43
|
+
};
|
|
44
|
+
return ownKeys(o);
|
|
45
|
+
};
|
|
46
|
+
return function (mod) {
|
|
47
|
+
if (mod && mod.__esModule) return mod;
|
|
48
|
+
var result = {};
|
|
49
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
50
|
+
__setModuleDefault(result, mod);
|
|
51
|
+
return result;
|
|
52
|
+
};
|
|
53
|
+
})();
|
|
54
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
55
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
56
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
57
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
58
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
59
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
60
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
64
|
+
exports.lazyLoad = lazyLoad;
|
|
65
|
+
/**
|
|
66
|
+
* This loads and returns a typed package.
|
|
67
|
+
* TPackage is a package type
|
|
68
|
+
* @returns The package
|
|
69
|
+
*/
|
|
70
|
+
function lazyLoad(_a) {
|
|
71
|
+
return __awaiter(this, arguments, void 0, function* ({ packageName, context, }) {
|
|
72
|
+
try {
|
|
73
|
+
return (yield Promise.resolve(`${packageName}`).then(s => __importStar(require(s))));
|
|
74
|
+
}
|
|
75
|
+
catch (err) {
|
|
76
|
+
throw new Error(`The "${packageName}" package is missing. Please, make sure to install it to take advantage of ${context}.`, { cause: err });
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
package/dist/main.d.ts
ADDED
package/dist/main.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
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("./errors.js"), exports);
|
|
18
|
+
__exportStar(require("./interfaces.js"), exports);
|
|
19
|
+
__exportStar(require("./publisher.js"), exports);
|
|
20
|
+
__exportStar(require("./subscriber.js"), exports);
|
|
21
|
+
__exportStar(require("./bullmq/main.js"), exports);
|
|
22
|
+
__exportStar(require("./rabbitmq/main.js"), exports);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseMessage, Logger, PublisherProvider, PubSubConfiguration } from "./interfaces";
|
|
2
|
+
export declare class Publisher {
|
|
3
|
+
private readonly logger;
|
|
4
|
+
private readonly hashes;
|
|
5
|
+
private readonly publisherProviders;
|
|
6
|
+
constructor(logger: Logger, providers?: PublisherProvider[]);
|
|
7
|
+
addProvider(provider: PublisherProvider): void;
|
|
8
|
+
removeProvider(providerOrTransport: PublisherProvider | string): boolean;
|
|
9
|
+
publish(config: PubSubConfiguration, message: BaseMessage): Promise<void>;
|
|
10
|
+
close(): Promise<void>;
|
|
11
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.Publisher = void 0;
|
|
13
|
+
const v4_1 = require("zod/v4");
|
|
14
|
+
const uuid_1 = require("uuid");
|
|
15
|
+
const crypto_hash_1 = require("crypto-hash");
|
|
16
|
+
const errors_1 = require("./errors");
|
|
17
|
+
class Publisher {
|
|
18
|
+
constructor(logger, providers) {
|
|
19
|
+
this.logger = logger;
|
|
20
|
+
this.hashes = new Map();
|
|
21
|
+
this.publisherProviders = new Map();
|
|
22
|
+
if (providers) {
|
|
23
|
+
providers.forEach((provider) => this.publisherProviders.set(provider.transport, provider));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
addProvider(provider) {
|
|
27
|
+
var _a, _b;
|
|
28
|
+
this.publisherProviders.set(provider.transport, provider);
|
|
29
|
+
(_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `Publisher added provider for ${provider.transport}`);
|
|
30
|
+
}
|
|
31
|
+
removeProvider(providerOrTransport) {
|
|
32
|
+
var _a, _b;
|
|
33
|
+
let transport;
|
|
34
|
+
if (typeof providerOrTransport === "string") {
|
|
35
|
+
transport = providerOrTransport;
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
transport = providerOrTransport.transport;
|
|
39
|
+
}
|
|
40
|
+
(_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `Publisher removing provider for ${transport}`);
|
|
41
|
+
return this.publisherProviders.delete(transport);
|
|
42
|
+
}
|
|
43
|
+
publish(config, message) {
|
|
44
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
45
|
+
var _a, _b;
|
|
46
|
+
const { transport, schema } = config;
|
|
47
|
+
const provider = this.publisherProviders.get(transport);
|
|
48
|
+
if (!provider) {
|
|
49
|
+
throw new errors_1.MissingPubSubProviderError(`No provider configured for ${transport}`);
|
|
50
|
+
}
|
|
51
|
+
if (!message.id) {
|
|
52
|
+
message.id = (0, uuid_1.v4)();
|
|
53
|
+
}
|
|
54
|
+
let hash = this.hashes.get(schema);
|
|
55
|
+
if (!hash) {
|
|
56
|
+
const jsonSchema = JSON.stringify(v4_1.z.toJSONSchema(schema), null, 3);
|
|
57
|
+
hash = yield (0, crypto_hash_1.sha256)(jsonSchema);
|
|
58
|
+
this.hashes.set(schema, hash);
|
|
59
|
+
}
|
|
60
|
+
if (!message.meta) {
|
|
61
|
+
message.meta = {
|
|
62
|
+
createdAt: new Date().toISOString(),
|
|
63
|
+
publishedBy: "",
|
|
64
|
+
schemaId: hash,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
message.meta.schemaId = hash;
|
|
69
|
+
}
|
|
70
|
+
const json = JSON.stringify(message);
|
|
71
|
+
const check = schema.safeParse(JSON.parse(json));
|
|
72
|
+
if (!check.success) {
|
|
73
|
+
throw new errors_1.SchemaValidationError(`Schema did not accept the published message: ${check.error.message}`);
|
|
74
|
+
}
|
|
75
|
+
(_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `Publisher publishing message for ${transport}`);
|
|
76
|
+
yield provider.publish(config, json);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
close() {
|
|
80
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
81
|
+
for (const provider of this.publisherProviders.values()) {
|
|
82
|
+
yield provider.close();
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.Publisher = Publisher;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { MessageContext, PubSubConfiguration } from "../interfaces";
|
|
2
|
+
export interface RabbitMqConnectionConfig {
|
|
3
|
+
/**
|
|
4
|
+
* The URL to the RabbitMQ host
|
|
5
|
+
*/
|
|
6
|
+
host: string;
|
|
7
|
+
/**
|
|
8
|
+
* Delay between publisher startup retries if the connection fails (default: 500ms)
|
|
9
|
+
*/
|
|
10
|
+
startupRetryDelayMs?: number;
|
|
11
|
+
/**
|
|
12
|
+
* Timeout for publisher startup retries if the connection fails (default: 10 seconds)
|
|
13
|
+
*/
|
|
14
|
+
startupRetryTimeoutMs?: number;
|
|
15
|
+
}
|
|
16
|
+
export type QueueType = "default" | "dead-letter" | "retry";
|
|
17
|
+
export type ExchangeType = "direct" | "topic";
|
|
18
|
+
/**
|
|
19
|
+
* Default binding key for all messages
|
|
20
|
+
*/
|
|
21
|
+
export declare const DEFAULT_BINDING_KEY = "default";
|
|
22
|
+
export interface RabbitQueueExchangeConfiguration extends PubSubConfiguration {
|
|
23
|
+
/**
|
|
24
|
+
* The queue name prefix
|
|
25
|
+
*/
|
|
26
|
+
name: string;
|
|
27
|
+
/**
|
|
28
|
+
* For topic exchanges, the name of the subscriber is used here
|
|
29
|
+
*/
|
|
30
|
+
topicSubscriberName?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Support direct exchange where each message is delivered once, or topic exchange where each message is delivered to multiple queues
|
|
33
|
+
*/
|
|
34
|
+
exchangeType?: ExchangeType;
|
|
35
|
+
/**
|
|
36
|
+
* When true, the queue and exchange will be deleted after the consumers exit [note: dead-letter exchanges & queues main remain when there are no dead-letter messages or consumers]
|
|
37
|
+
*/
|
|
38
|
+
temporary?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The queue prefetch amount for subscribers. Defaults to 5 if not set.
|
|
41
|
+
*/
|
|
42
|
+
prefetch?: number;
|
|
43
|
+
/**
|
|
44
|
+
* Pass a queueType when you want to publish or subscribe to the dead-letter or retry queues.
|
|
45
|
+
*/
|
|
46
|
+
queueType?: QueueType;
|
|
47
|
+
/**
|
|
48
|
+
* Override default queue & exchange names
|
|
49
|
+
*/
|
|
50
|
+
overrides?: RabbitQueueExchangeCustomConfiguration;
|
|
51
|
+
}
|
|
52
|
+
export interface RabbitQueueExchangeNames {
|
|
53
|
+
queueName?: string;
|
|
54
|
+
exchangeName?: string;
|
|
55
|
+
routingKey?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A collection of properties to override default pub/sub configuration values
|
|
59
|
+
*/
|
|
60
|
+
export interface RabbitQueueExchangeCustomConfiguration {
|
|
61
|
+
names: Partial<Record<QueueType, RabbitQueueExchangeNames>>;
|
|
62
|
+
retryQueueExchangeName?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface RabbitMqMessageContext extends MessageContext {
|
|
65
|
+
exchangeName?: string;
|
|
66
|
+
routingKey?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Returns the queue name based on the configuration
|
|
70
|
+
*
|
|
71
|
+
* @param config
|
|
72
|
+
* @returns The queue name
|
|
73
|
+
*/
|
|
74
|
+
export declare function getQueueName(config: RabbitQueueExchangeConfiguration): string;
|
|
75
|
+
export declare const QueueNameExtensions: Record<QueueType, string | undefined>;
|
|
76
|
+
/**
|
|
77
|
+
* Returns the exchange name based on the configuration
|
|
78
|
+
* @param config
|
|
79
|
+
* @returns The exchange name
|
|
80
|
+
*/
|
|
81
|
+
export declare function getExchangeName(config: RabbitQueueExchangeConfiguration): string;
|
|
82
|
+
export declare function getQueueType(config: RabbitQueueExchangeConfiguration): QueueType;
|
|
83
|
+
export declare function getRoutingKey(config: RabbitQueueExchangeConfiguration): string;
|
|
84
|
+
export declare function getExchangeType(config: RabbitQueueExchangeConfiguration): ExchangeType;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.QueueNameExtensions = exports.DEFAULT_BINDING_KEY = void 0;
|
|
4
|
+
exports.getQueueName = getQueueName;
|
|
5
|
+
exports.getExchangeName = getExchangeName;
|
|
6
|
+
exports.getQueueType = getQueueType;
|
|
7
|
+
exports.getRoutingKey = getRoutingKey;
|
|
8
|
+
exports.getExchangeType = getExchangeType;
|
|
9
|
+
const errors_1 = require("../errors");
|
|
10
|
+
/**
|
|
11
|
+
* Default binding key for all messages
|
|
12
|
+
*/
|
|
13
|
+
exports.DEFAULT_BINDING_KEY = "default";
|
|
14
|
+
/**
|
|
15
|
+
* Returns the queue name based on the configuration
|
|
16
|
+
*
|
|
17
|
+
* @param config
|
|
18
|
+
* @returns The queue name
|
|
19
|
+
*/
|
|
20
|
+
function getQueueName(config) {
|
|
21
|
+
var _a, _b;
|
|
22
|
+
const queueType = getQueueType(config);
|
|
23
|
+
if ((_b = (_a = config.overrides) === null || _a === void 0 ? void 0 : _a.names[queueType]) === null || _b === void 0 ? void 0 : _b.queueName) {
|
|
24
|
+
return config.overrides.names[queueType].queueName;
|
|
25
|
+
}
|
|
26
|
+
let queueName = config.name;
|
|
27
|
+
if (config.exchangeType === "topic") {
|
|
28
|
+
if (!config.topicSubscriberName) {
|
|
29
|
+
throw new errors_1.ConfigurationError("Topic queues must have a subscriber name");
|
|
30
|
+
}
|
|
31
|
+
queueName += "." + config.topicSubscriberName;
|
|
32
|
+
}
|
|
33
|
+
if (queueType === "dead-letter") {
|
|
34
|
+
queueName += ".dlq";
|
|
35
|
+
}
|
|
36
|
+
else if (queueType === "retry") {
|
|
37
|
+
queueName += ".rtq";
|
|
38
|
+
}
|
|
39
|
+
return queueName;
|
|
40
|
+
}
|
|
41
|
+
exports.QueueNameExtensions = {
|
|
42
|
+
"dead-letter": ".dlx",
|
|
43
|
+
retry: ".rtx",
|
|
44
|
+
default: undefined,
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Returns the exchange name based on the configuration
|
|
48
|
+
* @param config
|
|
49
|
+
* @returns The exchange name
|
|
50
|
+
*/
|
|
51
|
+
function getExchangeName(config) {
|
|
52
|
+
var _a, _b;
|
|
53
|
+
const queueType = getQueueType(config);
|
|
54
|
+
if ((_b = (_a = config.overrides) === null || _a === void 0 ? void 0 : _a.names[queueType]) === null || _b === void 0 ? void 0 : _b.exchangeName) {
|
|
55
|
+
return config.overrides.names[queueType].exchangeName;
|
|
56
|
+
}
|
|
57
|
+
let exchangeName = config.name;
|
|
58
|
+
if (["dead-letter", "retry"].includes(queueType)) {
|
|
59
|
+
if (config.exchangeType === "topic") {
|
|
60
|
+
if (!config.topicSubscriberName) {
|
|
61
|
+
throw new errors_1.ConfigurationError(`Topic ${queueType} exchanges must have a subscriber name`);
|
|
62
|
+
}
|
|
63
|
+
exchangeName += "." + config.topicSubscriberName;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const ext = exports.QueueNameExtensions[queueType];
|
|
67
|
+
if (ext) {
|
|
68
|
+
exchangeName += ext;
|
|
69
|
+
}
|
|
70
|
+
return exchangeName;
|
|
71
|
+
}
|
|
72
|
+
function getQueueType(config) {
|
|
73
|
+
return config.queueType || "default";
|
|
74
|
+
}
|
|
75
|
+
function getRoutingKey(config) {
|
|
76
|
+
var _a, _b;
|
|
77
|
+
return (((_b = (_a = config.overrides) === null || _a === void 0 ? void 0 : _a.names[getQueueType(config)]) === null || _b === void 0 ? void 0 : _b.routingKey) ||
|
|
78
|
+
exports.DEFAULT_BINDING_KEY);
|
|
79
|
+
}
|
|
80
|
+
function getExchangeType(config) {
|
|
81
|
+
return config.exchangeType || "direct";
|
|
82
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type * as amqpmgr from "amqp-connection-manager";
|
|
2
|
+
import type { Channel } from "amqplib";
|
|
3
|
+
import { RabbitQueueExchangeConfiguration, RabbitMqConnectionConfig } from "./config";
|
|
4
|
+
import { Logger } from "../interfaces";
|
|
5
|
+
export declare class SocketError extends Error {
|
|
6
|
+
readonly code: string;
|
|
7
|
+
readonly error: unknown;
|
|
8
|
+
constructor(code: string, error: unknown);
|
|
9
|
+
}
|
|
10
|
+
export declare const RABBITMQ_TRANSPORT = "rabbitmq";
|
|
11
|
+
/**
|
|
12
|
+
* Manages access to the AMQP connection itself
|
|
13
|
+
*/
|
|
14
|
+
export declare class RabbitMqConnection {
|
|
15
|
+
readonly connection: amqpmgr.AmqpConnectionManager;
|
|
16
|
+
private readonly logger;
|
|
17
|
+
readonly config: RabbitMqConnectionConfig;
|
|
18
|
+
static create(config: RabbitMqConnectionConfig, logger: Logger): Promise<RabbitMqConnection>;
|
|
19
|
+
private constructor();
|
|
20
|
+
close(): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Creates the exchanges necessary for the given config
|
|
23
|
+
* @param channel
|
|
24
|
+
* @param config Must be isDlq === false | undefined
|
|
25
|
+
* @param dlConfig Must be isDlq
|
|
26
|
+
* @returns
|
|
27
|
+
*/
|
|
28
|
+
assertExchange(channel: Channel, config: RabbitQueueExchangeConfiguration): Promise<string>;
|
|
29
|
+
assertQueueAndBindings(channel: Channel, config: RabbitQueueExchangeConfiguration): Promise<{
|
|
30
|
+
exchangeName: string;
|
|
31
|
+
dlExchangeName: string;
|
|
32
|
+
retryExchangeName: string;
|
|
33
|
+
queueName: string;
|
|
34
|
+
dlQueueName: string;
|
|
35
|
+
retryQueueName: string;
|
|
36
|
+
retryDlExchangeName: string;
|
|
37
|
+
}>;
|
|
38
|
+
private assertQueue;
|
|
39
|
+
private bindQueue;
|
|
40
|
+
private static getConfigs;
|
|
41
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.RabbitMqConnection = exports.RABBITMQ_TRANSPORT = exports.SocketError = void 0;
|
|
13
|
+
const config_1 = require("./config");
|
|
14
|
+
const lazy_load_1 = require("../lazy-load");
|
|
15
|
+
class SocketError extends Error {
|
|
16
|
+
constructor(code, error) {
|
|
17
|
+
super(`Socket error: ${code}`);
|
|
18
|
+
this.code = code;
|
|
19
|
+
this.error = error;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.SocketError = SocketError;
|
|
23
|
+
exports.RABBITMQ_TRANSPORT = "rabbitmq";
|
|
24
|
+
/**
|
|
25
|
+
* Manages access to the AMQP connection itself
|
|
26
|
+
*/
|
|
27
|
+
class RabbitMqConnection {
|
|
28
|
+
static create(config, logger) {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
const AmqpMgrPackage = yield (0, lazy_load_1.lazyLoad)({
|
|
31
|
+
packageName: "amqp-connection-manager",
|
|
32
|
+
context: "RabbitMqConnection",
|
|
33
|
+
});
|
|
34
|
+
return new RabbitMqConnection(config, AmqpMgrPackage.connect(config.host), logger);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
constructor(config, connection, logger) {
|
|
38
|
+
this.connection = connection;
|
|
39
|
+
this.logger = logger;
|
|
40
|
+
this.config = Object.assign({}, config);
|
|
41
|
+
}
|
|
42
|
+
close() {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
var _a, _b;
|
|
45
|
+
(_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, "RabbitMQ closing connection");
|
|
46
|
+
yield this.connection.close();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Creates the exchanges necessary for the given config
|
|
51
|
+
* @param channel
|
|
52
|
+
* @param config Must be isDlq === false | undefined
|
|
53
|
+
* @param dlConfig Must be isDlq
|
|
54
|
+
* @returns
|
|
55
|
+
*/
|
|
56
|
+
assertExchange(channel, config) {
|
|
57
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
58
|
+
var _a, _b;
|
|
59
|
+
const { exchange } = yield channel.assertExchange((0, config_1.getExchangeName)(config), (0, config_1.getExchangeType)(config), {
|
|
60
|
+
autoDelete: !!config.temporary,
|
|
61
|
+
});
|
|
62
|
+
(_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `Asserted exchange ${exchange}`);
|
|
63
|
+
return exchange;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
assertQueueAndBindings(channel, config) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
69
|
+
let dlConfig, retryConfig;
|
|
70
|
+
({ config, dlConfig, retryConfig } = RabbitMqConnection.getConfigs(config));
|
|
71
|
+
const exchangeName = yield this.assertExchange(channel, config);
|
|
72
|
+
const dlExchangeName = yield this.assertExchange(channel, dlConfig);
|
|
73
|
+
const retryExchangeName = yield this.assertExchange(channel, retryConfig);
|
|
74
|
+
const queueName = (0, config_1.getQueueName)(config);
|
|
75
|
+
const dlQueueName = (0, config_1.getQueueName)(dlConfig);
|
|
76
|
+
const retryQueueName = (0, config_1.getQueueName)(retryConfig);
|
|
77
|
+
const autoDelete = !!config.temporary;
|
|
78
|
+
// special case to ensure the retry dead-letter only reverts to the original queue,
|
|
79
|
+
// regardless of original exchange type [that is, not re-using the original queue exchange here]
|
|
80
|
+
const retryDlExchangeName = ((_a = config.overrides) === null || _a === void 0 ? void 0 : _a.retryQueueExchangeName) ||
|
|
81
|
+
`${queueName}${config_1.QueueNameExtensions["dead-letter"]}${config_1.QueueNameExtensions["retry"]}`;
|
|
82
|
+
yield channel.assertExchange(retryDlExchangeName, "direct", {
|
|
83
|
+
autoDelete: !!config.temporary,
|
|
84
|
+
});
|
|
85
|
+
(_c = (_b = this.logger).debug) === null || _c === void 0 ? void 0 : _c.call(_b, `Asserted exchange ${retryDlExchangeName}`);
|
|
86
|
+
// create the dead-letter queue without any DLX
|
|
87
|
+
yield this.assertQueue(channel, dlQueueName, autoDelete);
|
|
88
|
+
// create the retry queue using retry dead-letter exchange as the DLX
|
|
89
|
+
yield this.assertQueue(channel, retryQueueName, autoDelete, retryDlExchangeName);
|
|
90
|
+
// create the default queue using the dead-letter exchange as the DLX
|
|
91
|
+
yield this.assertQueue(channel, queueName, autoDelete, dlExchangeName);
|
|
92
|
+
// bind the default queue using the default exchange and default routing key
|
|
93
|
+
yield this.bindQueue(channel, queueName, exchangeName, ((_e = (_d = config.overrides) === null || _d === void 0 ? void 0 : _d.names.default) === null || _e === void 0 ? void 0 : _e.routingKey) || config_1.DEFAULT_BINDING_KEY);
|
|
94
|
+
// bind the dead-letter queue to the dead-letter exchange and dead-letter routing key
|
|
95
|
+
yield this.bindQueue(channel, dlQueueName, dlExchangeName, ((_g = (_f = config.overrides) === null || _f === void 0 ? void 0 : _f.names["dead-letter"]) === null || _g === void 0 ? void 0 : _g.routingKey) || config_1.DEFAULT_BINDING_KEY);
|
|
96
|
+
// bind the retry queue to the retry exchange and retry routing key
|
|
97
|
+
yield this.bindQueue(channel, retryQueueName, retryExchangeName, ((_j = (_h = config.overrides) === null || _h === void 0 ? void 0 : _h.names.retry) === null || _j === void 0 ? void 0 : _j.routingKey) || config_1.DEFAULT_BINDING_KEY);
|
|
98
|
+
// bind the queue to the retry dead-letter exchange and default routing key
|
|
99
|
+
yield this.bindQueue(channel, queueName, retryDlExchangeName, ((_l = (_k = config.overrides) === null || _k === void 0 ? void 0 : _k.names.default) === null || _l === void 0 ? void 0 : _l.routingKey) || config_1.DEFAULT_BINDING_KEY);
|
|
100
|
+
return {
|
|
101
|
+
exchangeName,
|
|
102
|
+
dlExchangeName,
|
|
103
|
+
retryExchangeName,
|
|
104
|
+
queueName,
|
|
105
|
+
dlQueueName,
|
|
106
|
+
retryQueueName,
|
|
107
|
+
retryDlExchangeName,
|
|
108
|
+
};
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
assertQueue(channel, queueName, autoDelete, deadLetterExchange) {
|
|
112
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
113
|
+
var _a, _b;
|
|
114
|
+
yield channel.assertQueue(queueName, {
|
|
115
|
+
autoDelete,
|
|
116
|
+
deadLetterExchange,
|
|
117
|
+
});
|
|
118
|
+
(_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `Asserted queue ${queueName}`);
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
bindQueue(channel, queueName, exchangeName, bindingKey) {
|
|
122
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
var _a, _b;
|
|
124
|
+
yield channel.bindQueue(queueName, exchangeName, bindingKey);
|
|
125
|
+
(_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `Bound queue ${queueName} to ${exchangeName}:${bindingKey}`);
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
static getConfigs(config) {
|
|
129
|
+
const dlConfig = config.queueType === "dead-letter"
|
|
130
|
+
? config
|
|
131
|
+
: Object.assign(Object.assign({}, config), { queueType: "dead-letter" });
|
|
132
|
+
const retryConfig = config.queueType === "retry" ? config : Object.assign(Object.assign({}, config), { queueType: "retry" });
|
|
133
|
+
config =
|
|
134
|
+
!config.queueType || config.queueType === "default"
|
|
135
|
+
? config
|
|
136
|
+
: Object.assign(Object.assign({}, config), { queueType: "default" });
|
|
137
|
+
return { config, dlConfig, retryConfig };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
exports.RabbitMqConnection = RabbitMqConnection;
|
|
@@ -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("./config.js"), exports);
|
|
18
|
+
__exportStar(require("./connection.js"), exports);
|
|
19
|
+
__exportStar(require("./publisher.js"), exports);
|
|
20
|
+
__exportStar(require("./subscriber.js"), exports);
|
|
21
|
+
__exportStar(require("./pubsub.js"), exports);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ChannelWrapper } from "amqp-connection-manager";
|
|
2
|
+
import { Logger, PublisherProvider } from "../interfaces";
|
|
3
|
+
import { RabbitMqConnection } from "./connection";
|
|
4
|
+
import { RabbitQueueExchangeConfiguration } from "./config";
|
|
5
|
+
export declare class RabbitMqPublisher implements PublisherProvider {
|
|
6
|
+
private readonly connection;
|
|
7
|
+
private readonly logger;
|
|
8
|
+
private inited;
|
|
9
|
+
private channel?;
|
|
10
|
+
private connected;
|
|
11
|
+
constructor(connection: RabbitMqConnection, logger: Logger);
|
|
12
|
+
readonly transport: string;
|
|
13
|
+
/**
|
|
14
|
+
* Initializes the rabbit connection and asserts the exchange for the configuration.
|
|
15
|
+
*
|
|
16
|
+
* @param config
|
|
17
|
+
* @returns a Promise containing the ChannelWrapper to publish messages with
|
|
18
|
+
*/
|
|
19
|
+
init(config: RabbitQueueExchangeConfiguration): Promise<ChannelWrapper>;
|
|
20
|
+
/**
|
|
21
|
+
* Publishes a message to Rabbit based on the configuration
|
|
22
|
+
*
|
|
23
|
+
* @param config The rabbit queue configuration
|
|
24
|
+
* @param message The JSON message to send
|
|
25
|
+
* @returns A promise that is completed when the message is published
|
|
26
|
+
*/
|
|
27
|
+
publish(config: RabbitQueueExchangeConfiguration, message: string): Promise<void>;
|
|
28
|
+
close(): Promise<void> | undefined;
|
|
29
|
+
}
|