@sera4/essentia 3.0.13 → 3.1.0-rc.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/README.md +93 -308
- package/dist/ts/cache/index.d.ts +33 -0
- package/dist/ts/cache/index.js +175 -0
- package/dist/ts/cache/index.js.map +1 -0
- package/dist/ts/configurations/server-config.d.ts +4 -0
- package/dist/ts/configurations/server-config.js +2 -0
- package/dist/ts/configurations/server-config.js.map +1 -0
- package/dist/ts/constants/index.d.ts +7 -0
- package/dist/ts/constants/index.js +8 -0
- package/dist/ts/constants/index.js.map +1 -0
- package/dist/ts/cycle/index.d.ts +53 -0
- package/dist/ts/cycle/index.js +171 -0
- package/dist/ts/cycle/index.js.map +1 -0
- package/dist/ts/dns/dns-cache.d.ts +50 -0
- package/dist/ts/dns/dns-cache.js +144 -0
- package/dist/ts/dns/dns-cache.js.map +1 -0
- package/dist/ts/dns/index.d.ts +4 -0
- package/dist/ts/dns/index.js +4 -0
- package/dist/ts/dns/index.js.map +1 -0
- package/dist/ts/formatter/index.d.ts +45 -0
- package/dist/ts/formatter/index.js +82 -0
- package/dist/ts/formatter/index.js.map +1 -0
- package/dist/ts/hal/index.d.ts +26 -0
- package/dist/ts/hal/index.js +70 -0
- package/dist/ts/hal/index.js.map +1 -0
- package/dist/ts/health/index.d.ts +52 -0
- package/dist/ts/health/index.js +170 -0
- package/dist/ts/health/index.js.map +1 -0
- package/dist/ts/helpers/index.d.ts +1 -0
- package/dist/ts/helpers/index.js +2 -0
- package/dist/ts/helpers/index.js.map +1 -0
- package/dist/ts/helpers/test-server-wrapper.d.ts +56 -0
- package/dist/ts/helpers/test-server-wrapper.js +153 -0
- package/dist/ts/helpers/test-server-wrapper.js.map +1 -0
- package/{index.js → dist/ts/index.d.ts} +14 -11
- package/dist/ts/index.js +25 -0
- package/dist/ts/index.js.map +1 -0
- package/dist/ts/last-commit/index.d.ts +12 -0
- package/dist/ts/last-commit/index.js +82 -0
- package/dist/ts/last-commit/index.js.map +1 -0
- package/dist/ts/logger/index.d.ts +4 -0
- package/dist/ts/logger/index.js +5 -0
- package/dist/ts/logger/index.js.map +1 -0
- package/dist/ts/logger/s4-logger.d.ts +41 -0
- package/dist/ts/logger/s4-logger.js +134 -0
- package/dist/ts/logger/s4-logger.js.map +1 -0
- package/dist/ts/paginator/index.d.ts +2 -0
- package/dist/ts/paginator/index.js +3 -0
- package/dist/ts/paginator/index.js.map +1 -0
- package/dist/ts/paginator/s4-pagination.d.ts +28 -0
- package/dist/ts/paginator/s4-pagination.js +49 -0
- package/dist/ts/paginator/s4-pagination.js.map +1 -0
- package/dist/ts/paginator/sql-pagination.d.ts +52 -0
- package/dist/ts/paginator/sql-pagination.js +176 -0
- package/dist/ts/paginator/sql-pagination.js.map +1 -0
- package/dist/ts/paper-trail/helpers.d.ts +14 -0
- package/dist/ts/paper-trail/helpers.js +72 -0
- package/dist/ts/paper-trail/helpers.js.map +1 -0
- package/dist/ts/paper-trail/index.d.ts +54 -0
- package/dist/ts/paper-trail/index.js +235 -0
- package/dist/ts/paper-trail/index.js.map +1 -0
- package/dist/ts/prompts/index.d.ts +21 -0
- package/dist/ts/prompts/index.js +68 -0
- package/dist/ts/prompts/index.js.map +1 -0
- package/dist/ts/queue/index.d.ts +232 -0
- package/dist/ts/queue/index.js +446 -0
- package/dist/ts/queue/index.js.map +1 -0
- package/dist/ts/queue/publisher.d.ts +41 -0
- package/dist/ts/queue/publisher.js +87 -0
- package/dist/ts/queue/publisher.js.map +1 -0
- package/dist/ts/queue/queue-logger.d.ts +10 -0
- package/dist/ts/queue/queue-logger.js +35 -0
- package/dist/ts/queue/queue-logger.js.map +1 -0
- package/dist/ts/queue/subscriber.d.ts +55 -0
- package/dist/ts/queue/subscriber.js +104 -0
- package/dist/ts/queue/subscriber.js.map +1 -0
- package/dist/ts/safe-proxy/index.d.ts +24 -0
- package/dist/ts/safe-proxy/index.js +50 -0
- package/dist/ts/safe-proxy/index.js.map +1 -0
- package/dist/ts/serializer/index.d.ts +52 -0
- package/dist/ts/serializer/index.js +83 -0
- package/dist/ts/serializer/index.js.map +1 -0
- package/dist/ts/task-scheduler/index.d.ts +130 -0
- package/dist/ts/task-scheduler/index.js +261 -0
- package/dist/ts/task-scheduler/index.js.map +1 -0
- package/dist/ts/utils/index.d.ts +49 -0
- package/dist/ts/utils/index.js +162 -0
- package/dist/ts/utils/index.js.map +1 -0
- package/last-commit.ts +12 -0
- package/package.json +28 -11
- package/package.tar.gz +0 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
interface ConnectionOptions {
|
|
2
|
+
connectionUrl: string;
|
|
3
|
+
logEnabled?: boolean;
|
|
4
|
+
retryInterval?: number;
|
|
5
|
+
}
|
|
6
|
+
interface Exchange {
|
|
7
|
+
name: string;
|
|
8
|
+
type: string;
|
|
9
|
+
options?: any;
|
|
10
|
+
}
|
|
11
|
+
interface PublisherOptions {
|
|
12
|
+
exchange: Exchange;
|
|
13
|
+
}
|
|
14
|
+
interface SubscriberOptions {
|
|
15
|
+
exchange: Exchange;
|
|
16
|
+
queue: {
|
|
17
|
+
name: string;
|
|
18
|
+
options?: any;
|
|
19
|
+
};
|
|
20
|
+
key: string;
|
|
21
|
+
}
|
|
22
|
+
interface UnregisterSubscriberOptions {
|
|
23
|
+
exchange: Exchange;
|
|
24
|
+
queue: {
|
|
25
|
+
name: string;
|
|
26
|
+
};
|
|
27
|
+
key: string;
|
|
28
|
+
}
|
|
29
|
+
interface FanoutQueueConfig {
|
|
30
|
+
name: string;
|
|
31
|
+
routingKey?: string;
|
|
32
|
+
options?: any;
|
|
33
|
+
}
|
|
34
|
+
interface FanoutConfiguration {
|
|
35
|
+
exchange: {
|
|
36
|
+
name: string;
|
|
37
|
+
type: "fanout";
|
|
38
|
+
options?: any;
|
|
39
|
+
};
|
|
40
|
+
queues: FanoutQueueConfig[];
|
|
41
|
+
}
|
|
42
|
+
type MessageListener = (payload: any, error?: any) => void;
|
|
43
|
+
/**
|
|
44
|
+
* An interface to AMQP service
|
|
45
|
+
*/
|
|
46
|
+
declare class S4Queue {
|
|
47
|
+
private connection;
|
|
48
|
+
private connectionTestChannel;
|
|
49
|
+
private retry;
|
|
50
|
+
private pubs;
|
|
51
|
+
private subs;
|
|
52
|
+
private fanouts;
|
|
53
|
+
constructor();
|
|
54
|
+
/**
|
|
55
|
+
* Tests the connection to the service.
|
|
56
|
+
* Internally, the same connection is used for test
|
|
57
|
+
* and production code, but a separate channel is opened.
|
|
58
|
+
* Once the channel is obtained, there is an assert on the
|
|
59
|
+
* existence of a topic exchange called '--health-check--'.
|
|
60
|
+
* The following must be satisfied for the test to pass
|
|
61
|
+
* 1. must be connected
|
|
62
|
+
* 2. must have a test channel
|
|
63
|
+
* 3. must assert the exchange
|
|
64
|
+
*/
|
|
65
|
+
testConnection(): Promise<void>;
|
|
66
|
+
/**
|
|
67
|
+
* Opens a connection to AMQP server.
|
|
68
|
+
* @param options Connection parameters. At the very least options.connectionUrl must be present.
|
|
69
|
+
*/
|
|
70
|
+
openConnection(options: ConnectionOptions): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* Closes the connection to AMQP server
|
|
73
|
+
* and clears the registered publishers
|
|
74
|
+
* and subscribers
|
|
75
|
+
*/
|
|
76
|
+
closeConnection(): Promise<void>;
|
|
77
|
+
/**
|
|
78
|
+
* Returns whether we have an active connection
|
|
79
|
+
* or not.
|
|
80
|
+
*/
|
|
81
|
+
isConnected(): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* @deprecated Use registerPublisher
|
|
84
|
+
* Registers a new direct publisher on the given exchange.
|
|
85
|
+
* If exchange doesn't exist, one will be created.
|
|
86
|
+
* @param exchange the name of the exchange on AMQP service.
|
|
87
|
+
*/
|
|
88
|
+
registerDirectPublisher(exchange: string): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* @deprecated Use registerPublisher
|
|
91
|
+
* Registers a new topic publisher on the given exchange.
|
|
92
|
+
* If exchange doesn't exist, one will be created.
|
|
93
|
+
* @param exchange the name of the exchange on AMQP service.
|
|
94
|
+
*/
|
|
95
|
+
registerTopicPublisher(exchange: string): Promise<void>;
|
|
96
|
+
/**
|
|
97
|
+
* @deprecated Use registerPublisher
|
|
98
|
+
* Registers a new fanout publisher on the given exchange.
|
|
99
|
+
* If exchange doesn't exist, one will be created.
|
|
100
|
+
* @param exchange the name of the exchange on AMQP service.
|
|
101
|
+
*/
|
|
102
|
+
registerFanoutPublisher(exchange: string): Promise<void>;
|
|
103
|
+
/**
|
|
104
|
+
* Registers a new publisher
|
|
105
|
+
* @param options connection options.
|
|
106
|
+
*/
|
|
107
|
+
registerPublisher(options: PublisherOptions): Promise<void>;
|
|
108
|
+
/**
|
|
109
|
+
* Checks whether or not a publisher with given parameters
|
|
110
|
+
* has been registered already
|
|
111
|
+
* @param exchange the name of the exchange.
|
|
112
|
+
* @returns true if registered already, false otherwise
|
|
113
|
+
*/
|
|
114
|
+
hasPublisher(exchange: string): boolean;
|
|
115
|
+
/**
|
|
116
|
+
* Checks whether or not a fanout with given exchange name
|
|
117
|
+
* has been registered already
|
|
118
|
+
* @param exchange the name of the exchange.
|
|
119
|
+
* @returns true if registered already, false otherwise
|
|
120
|
+
*/
|
|
121
|
+
hasFanout(exchange: string): boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Removes a previously registered publisher
|
|
124
|
+
* @param options
|
|
125
|
+
*/
|
|
126
|
+
unregisterPublisher(options: PublisherOptions): Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* @deprecated Use registerSubscriber
|
|
129
|
+
* Registers a new direct subscriber on a given exchange and queue.
|
|
130
|
+
* If exchange and queue don't exists, they'll be created
|
|
131
|
+
* and bound through the provided routing key.
|
|
132
|
+
* @param exchange the name of the exchange.
|
|
133
|
+
* @param queue the name of the queue.
|
|
134
|
+
* @param key the name of the routing key.
|
|
135
|
+
*/
|
|
136
|
+
registerDirectSubscriber(exchange: string, queue: string, key: string): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* @deprecated Use registerSubscriber
|
|
139
|
+
* Registers a new topic subscriber on a given exchange and queue.
|
|
140
|
+
* If exchange and queue don't exists, they'll be created
|
|
141
|
+
* and bound through the provided routing key.
|
|
142
|
+
* @param exchange the name of the exchange.
|
|
143
|
+
* @param queue the name of the queue.
|
|
144
|
+
* @param key the name of the routing key.
|
|
145
|
+
*/
|
|
146
|
+
registerTopicSubscriber(exchange: string, queue: string, key: string): Promise<void>;
|
|
147
|
+
/**
|
|
148
|
+
* An overloaded version of registering a subscriber
|
|
149
|
+
* allowing for more fine tuned parameters
|
|
150
|
+
* @param options subscribing options
|
|
151
|
+
*/
|
|
152
|
+
registerSubscriber(options: SubscriberOptions): Promise<void>;
|
|
153
|
+
/**
|
|
154
|
+
* Checks whether or not a subscriber with given parameters
|
|
155
|
+
* has been registered already
|
|
156
|
+
* @param exchange the name of the exchange.
|
|
157
|
+
* @param queue the name of the queue.
|
|
158
|
+
* @param key the name of the routing key.
|
|
159
|
+
* @returns true if registered already, false otherwise
|
|
160
|
+
*/
|
|
161
|
+
hasSubscriber(exchange: string, queue: string, key: string): boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Removes a previously registered subscriber
|
|
164
|
+
* @param options
|
|
165
|
+
*/
|
|
166
|
+
unregisterSubscriber(options: UnregisterSubscriberOptions): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Publishes a message on a given exchange with routing key.
|
|
169
|
+
* @param exchange the name of the exchange.
|
|
170
|
+
* @param key the name of the routing key.
|
|
171
|
+
* @param msg a JSON object as the message.
|
|
172
|
+
*/
|
|
173
|
+
publishDirectMessage(exchange: string, key: string, msg: any): Promise<string>;
|
|
174
|
+
/**
|
|
175
|
+
* Publishes a message on a given exchange with a topic.
|
|
176
|
+
* @param exchange the name of the exchange.
|
|
177
|
+
* @param topic the name of the topic.
|
|
178
|
+
* @param msg a JSON object as the message.
|
|
179
|
+
*/
|
|
180
|
+
publishTopicMessage(exchange: string, topic: string, msg: any): Promise<string>;
|
|
181
|
+
/**
|
|
182
|
+
* Publish a message to the exchange
|
|
183
|
+
* @param exchange name of exchange
|
|
184
|
+
* @param key name of the key or topic
|
|
185
|
+
* @param msg payload to be published
|
|
186
|
+
*/
|
|
187
|
+
publishMessage(exchange: string, key: string, msg: any): Promise<string>;
|
|
188
|
+
/**
|
|
189
|
+
* Binds an exchange to a queue
|
|
190
|
+
* @param exchange the name of the exchange.
|
|
191
|
+
* @param queue the name of the queue.
|
|
192
|
+
* @param pattern (optional) pattern for routing key
|
|
193
|
+
*/
|
|
194
|
+
bindQueueToExchange(exchange: string, queue: string, pattern?: string): Promise<void>;
|
|
195
|
+
/**
|
|
196
|
+
* Adds a listener for a specific key.
|
|
197
|
+
* If a subscriber that was registered was found
|
|
198
|
+
* with that key, the listener is added, otherwise
|
|
199
|
+
* it's ignored
|
|
200
|
+
* @param key AMQP routing key
|
|
201
|
+
* @param listener callback
|
|
202
|
+
*/
|
|
203
|
+
addMessageListener(key: string, listener: MessageListener): void;
|
|
204
|
+
/**
|
|
205
|
+
* Registers a fanout exchange with multiple bound queues.
|
|
206
|
+
* Creates the fanout exchange if it doesn't exist, then creates and binds
|
|
207
|
+
* all specified queues to the exchange.
|
|
208
|
+
* @param config Fanout configuration with exchange and queue definitions
|
|
209
|
+
*/
|
|
210
|
+
registerFanout(config: FanoutConfiguration): Promise<void>;
|
|
211
|
+
/**
|
|
212
|
+
* Unregisters a fanout configuration by removing all associated subscribers
|
|
213
|
+
* and optionally the publisher/exchange.
|
|
214
|
+
* @param config Fanout configuration to unregister
|
|
215
|
+
* @param removeExchange Whether to also remove the exchange (default: false)
|
|
216
|
+
*/
|
|
217
|
+
unregisterFanout(config: FanoutConfiguration, removeExchange?: boolean): Promise<void>;
|
|
218
|
+
/**
|
|
219
|
+
* Updates the connection reference to all the previously
|
|
220
|
+
* registered publishers, subscribers, and fanouts.
|
|
221
|
+
* After we successfully establish a connection and register pubs/subs/fanouts,
|
|
222
|
+
* it's possible for the connection to break. This object will try
|
|
223
|
+
* to re-establish a new connection every 3 seconds (or whatever configuration is set)
|
|
224
|
+
* and if a new connection is established, we need to pass that connection to
|
|
225
|
+
* all our pubs/subs, otherwise they'll be operating with old/stale references
|
|
226
|
+
* @param connRef a new connection reference to the AMQP service
|
|
227
|
+
*/
|
|
228
|
+
updatePubsAndSubsConnectionRef(connRef: any): void;
|
|
229
|
+
}
|
|
230
|
+
declare const queue: S4Queue;
|
|
231
|
+
export default queue;
|
|
232
|
+
export type { Exchange, FanoutConfiguration, FanoutQueueConfig, PublisherOptions, SubscriberOptions };
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
import amqp from "amqplib";
|
|
2
|
+
import urlParse from "url-parse";
|
|
3
|
+
import { Publisher } from "./publisher.js";
|
|
4
|
+
import logger from "./queue-logger.js";
|
|
5
|
+
import { Subscriber } from "./subscriber.js";
|
|
6
|
+
/**
|
|
7
|
+
* An interface to AMQP service
|
|
8
|
+
*/
|
|
9
|
+
class S4Queue {
|
|
10
|
+
connection;
|
|
11
|
+
connectionTestChannel;
|
|
12
|
+
retry;
|
|
13
|
+
pubs;
|
|
14
|
+
subs;
|
|
15
|
+
fanouts;
|
|
16
|
+
constructor() {
|
|
17
|
+
this.connection = null;
|
|
18
|
+
this.connectionTestChannel = null;
|
|
19
|
+
this.retry = 10;
|
|
20
|
+
this.pubs = {};
|
|
21
|
+
this.subs = {};
|
|
22
|
+
this.fanouts = {};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Tests the connection to the service.
|
|
26
|
+
* Internally, the same connection is used for test
|
|
27
|
+
* and production code, but a separate channel is opened.
|
|
28
|
+
* Once the channel is obtained, there is an assert on the
|
|
29
|
+
* existence of a topic exchange called '--health-check--'.
|
|
30
|
+
* The following must be satisfied for the test to pass
|
|
31
|
+
* 1. must be connected
|
|
32
|
+
* 2. must have a test channel
|
|
33
|
+
* 3. must assert the exchange
|
|
34
|
+
*/
|
|
35
|
+
async testConnection() {
|
|
36
|
+
if (!this.isConnected()) {
|
|
37
|
+
this.connectionTestChannel = null;
|
|
38
|
+
throw new Error("queue service not connected");
|
|
39
|
+
}
|
|
40
|
+
if (!this.connectionTestChannel) {
|
|
41
|
+
const ch = await this.connection.createChannel();
|
|
42
|
+
if (!ch) {
|
|
43
|
+
throw new Error("queue service failed to obtain a channel");
|
|
44
|
+
}
|
|
45
|
+
ch.on("error", () => { this.connectionTestChannel = null; });
|
|
46
|
+
ch.on("close", () => { this.connectionTestChannel = null; });
|
|
47
|
+
this.connectionTestChannel = ch;
|
|
48
|
+
}
|
|
49
|
+
await this.connectionTestChannel.assertExchange("--health-check--", "topic", { durable: false });
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Opens a connection to AMQP server.
|
|
53
|
+
* @param options Connection parameters. At the very least options.connectionUrl must be present.
|
|
54
|
+
*/
|
|
55
|
+
async openConnection(options) {
|
|
56
|
+
logger.setEnabled(options.logEnabled || false);
|
|
57
|
+
if (this.connection) {
|
|
58
|
+
logger.warn("Already initialized");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const retry = parseInt(options.retryInterval?.toString() || "10", 10);
|
|
62
|
+
this.retry = retry || 10;
|
|
63
|
+
// We may run into issues with SNI TLS if
|
|
64
|
+
// we host with 3rd parties using our own certs.
|
|
65
|
+
// To avoid any issues, let's parse the url
|
|
66
|
+
// and provide the server name in the socket options
|
|
67
|
+
const urlParts = urlParse(options.connectionUrl);
|
|
68
|
+
const socketOptions = { servername: urlParts.hostname };
|
|
69
|
+
return new Promise((resolve) => {
|
|
70
|
+
const attempt = async () => {
|
|
71
|
+
logger.debug("Attempting connection...");
|
|
72
|
+
try {
|
|
73
|
+
const conn = await amqp.connect(options.connectionUrl, socketOptions);
|
|
74
|
+
this.connection = conn;
|
|
75
|
+
this.updatePubsAndSubsConnectionRef(conn);
|
|
76
|
+
this.connection.on("error", (err) => {
|
|
77
|
+
logger.error("connection error", err);
|
|
78
|
+
// Do not attempt to reconnect as the
|
|
79
|
+
// 'close' event will also be emitted
|
|
80
|
+
// if this error closes the connection
|
|
81
|
+
});
|
|
82
|
+
this.connection.on("close", (err) => {
|
|
83
|
+
logger.error("connection closed");
|
|
84
|
+
this.connection = null;
|
|
85
|
+
this.connectionTestChannel = null;
|
|
86
|
+
if (err) {
|
|
87
|
+
logger.error("connection closed due to error:", err);
|
|
88
|
+
logger.error("Reconnecting in", this.retry, "seconds");
|
|
89
|
+
setTimeout(attempt, this.retry * 1000);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
logger.debug("Will not re-attempt");
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
logger.debug("Connected");
|
|
96
|
+
resolve();
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
logger.error("Error while connecting", err);
|
|
100
|
+
logger.error("Attempting again in", this.retry, "seconds");
|
|
101
|
+
setTimeout(attempt, this.retry * 1000);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
attempt();
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Closes the connection to AMQP server
|
|
109
|
+
* and clears the registered publishers
|
|
110
|
+
* and subscribers
|
|
111
|
+
*/
|
|
112
|
+
async closeConnection() {
|
|
113
|
+
if (!this.connection)
|
|
114
|
+
return;
|
|
115
|
+
await this.connection.close();
|
|
116
|
+
this.subs = {};
|
|
117
|
+
this.pubs = {};
|
|
118
|
+
this.connectionTestChannel = null;
|
|
119
|
+
this.connection = null;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Returns whether we have an active connection
|
|
123
|
+
* or not.
|
|
124
|
+
*/
|
|
125
|
+
isConnected() {
|
|
126
|
+
return this.connection != null && typeof this.connection !== "undefined";
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* @deprecated Use registerPublisher
|
|
130
|
+
* Registers a new direct publisher on the given exchange.
|
|
131
|
+
* If exchange doesn't exist, one will be created.
|
|
132
|
+
* @param exchange the name of the exchange on AMQP service.
|
|
133
|
+
*/
|
|
134
|
+
async registerDirectPublisher(exchange) {
|
|
135
|
+
return this.registerPublisher({ exchange: { name: exchange, type: "direct" } });
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* @deprecated Use registerPublisher
|
|
139
|
+
* Registers a new topic publisher on the given exchange.
|
|
140
|
+
* If exchange doesn't exist, one will be created.
|
|
141
|
+
* @param exchange the name of the exchange on AMQP service.
|
|
142
|
+
*/
|
|
143
|
+
async registerTopicPublisher(exchange) {
|
|
144
|
+
return this.registerPublisher({ exchange: { name: exchange, type: "topic" } });
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* @deprecated Use registerPublisher
|
|
148
|
+
* Registers a new fanout publisher on the given exchange.
|
|
149
|
+
* If exchange doesn't exist, one will be created.
|
|
150
|
+
* @param exchange the name of the exchange on AMQP service.
|
|
151
|
+
*/
|
|
152
|
+
async registerFanoutPublisher(exchange) {
|
|
153
|
+
return this.registerPublisher({ exchange: { name: exchange, type: "fanout" } });
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Registers a new publisher
|
|
157
|
+
* @param options connection options.
|
|
158
|
+
*/
|
|
159
|
+
async registerPublisher(options) {
|
|
160
|
+
const { exchange } = options;
|
|
161
|
+
const { name } = exchange;
|
|
162
|
+
if (Object.keys(this.pubs).includes(name)) {
|
|
163
|
+
throw new Error(`Publisher ${name} already registered`);
|
|
164
|
+
}
|
|
165
|
+
const p = new Publisher(exchange, name);
|
|
166
|
+
await p.init(this.connection);
|
|
167
|
+
this.pubs[name] = p;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Checks whether or not a publisher with given parameters
|
|
171
|
+
* has been registered already
|
|
172
|
+
* @param exchange the name of the exchange.
|
|
173
|
+
* @returns true if registered already, false otherwise
|
|
174
|
+
*/
|
|
175
|
+
hasPublisher(exchange) {
|
|
176
|
+
return Object.keys(this.pubs).includes(exchange);
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Checks whether or not a fanout with given exchange name
|
|
180
|
+
* has been registered already
|
|
181
|
+
* @param exchange the name of the exchange.
|
|
182
|
+
* @returns true if registered already, false otherwise
|
|
183
|
+
*/
|
|
184
|
+
hasFanout(exchange) {
|
|
185
|
+
return Object.keys(this.fanouts).includes(exchange);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Removes a previously registered publisher
|
|
189
|
+
* @param options
|
|
190
|
+
*/
|
|
191
|
+
async unregisterPublisher(options) {
|
|
192
|
+
const { exchange } = options;
|
|
193
|
+
const { name } = exchange;
|
|
194
|
+
if (!Object.keys(this.pubs).includes(name)) {
|
|
195
|
+
throw new Error(`Publisher ${name} not registered`);
|
|
196
|
+
}
|
|
197
|
+
const p = this.pubs[name];
|
|
198
|
+
await p.closeChannel();
|
|
199
|
+
delete this.pubs[name];
|
|
200
|
+
logger.debug("Unregistered publisher", name);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* @deprecated Use registerSubscriber
|
|
204
|
+
* Registers a new direct subscriber on a given exchange and queue.
|
|
205
|
+
* If exchange and queue don't exists, they'll be created
|
|
206
|
+
* and bound through the provided routing key.
|
|
207
|
+
* @param exchange the name of the exchange.
|
|
208
|
+
* @param queue the name of the queue.
|
|
209
|
+
* @param key the name of the routing key.
|
|
210
|
+
*/
|
|
211
|
+
async registerDirectSubscriber(exchange, queue, key) {
|
|
212
|
+
const options = {
|
|
213
|
+
key,
|
|
214
|
+
exchange: { name: exchange, type: "direct" },
|
|
215
|
+
queue: { name: queue },
|
|
216
|
+
};
|
|
217
|
+
return this.registerSubscriber(options);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* @deprecated Use registerSubscriber
|
|
221
|
+
* Registers a new topic subscriber on a given exchange and queue.
|
|
222
|
+
* If exchange and queue don't exists, they'll be created
|
|
223
|
+
* and bound through the provided routing key.
|
|
224
|
+
* @param exchange the name of the exchange.
|
|
225
|
+
* @param queue the name of the queue.
|
|
226
|
+
* @param key the name of the routing key.
|
|
227
|
+
*/
|
|
228
|
+
async registerTopicSubscriber(exchange, queue, key) {
|
|
229
|
+
const options = {
|
|
230
|
+
key,
|
|
231
|
+
exchange: { name: exchange, type: "topic" },
|
|
232
|
+
queue: { name: queue },
|
|
233
|
+
};
|
|
234
|
+
return this.registerSubscriber(options);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* An overloaded version of registering a subscriber
|
|
238
|
+
* allowing for more fine tuned parameters
|
|
239
|
+
* @param options subscribing options
|
|
240
|
+
*/
|
|
241
|
+
async registerSubscriber(options) {
|
|
242
|
+
const { exchange, queue, key } = options;
|
|
243
|
+
const name = `${exchange.name}-${queue.name}-${key}`;
|
|
244
|
+
if (Object.keys(this.subs).includes(name)) {
|
|
245
|
+
throw new Error(`Subscriber ${name} already registered`);
|
|
246
|
+
}
|
|
247
|
+
const s = new Subscriber(exchange, queue, key, name);
|
|
248
|
+
await s.init(this.connection);
|
|
249
|
+
this.subs[name] = s;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Checks whether or not a subscriber with given parameters
|
|
253
|
+
* has been registered already
|
|
254
|
+
* @param exchange the name of the exchange.
|
|
255
|
+
* @param queue the name of the queue.
|
|
256
|
+
* @param key the name of the routing key.
|
|
257
|
+
* @returns true if registered already, false otherwise
|
|
258
|
+
*/
|
|
259
|
+
hasSubscriber(exchange, queue, key) {
|
|
260
|
+
const name = `${exchange}-${queue}-${key}`;
|
|
261
|
+
return Object.keys(this.subs).includes(name);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Removes a previously registered subscriber
|
|
265
|
+
* @param options
|
|
266
|
+
*/
|
|
267
|
+
async unregisterSubscriber(options) {
|
|
268
|
+
const { exchange, queue, key } = options;
|
|
269
|
+
const name = `${exchange.name}-${queue.name}-${key}`;
|
|
270
|
+
if (!Object.keys(this.subs).includes(name)) {
|
|
271
|
+
throw new Error(`Subscriber ${name} not registered`);
|
|
272
|
+
}
|
|
273
|
+
const s = this.subs[name];
|
|
274
|
+
await s.closeChannel();
|
|
275
|
+
delete this.subs[name];
|
|
276
|
+
logger.debug("Unregistered subscriber", name);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Publishes a message on a given exchange with routing key.
|
|
280
|
+
* @param exchange the name of the exchange.
|
|
281
|
+
* @param key the name of the routing key.
|
|
282
|
+
* @param msg a JSON object as the message.
|
|
283
|
+
*/
|
|
284
|
+
async publishDirectMessage(exchange, key, msg) {
|
|
285
|
+
return this.publishMessage(exchange, key, msg);
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Publishes a message on a given exchange with a topic.
|
|
289
|
+
* @param exchange the name of the exchange.
|
|
290
|
+
* @param topic the name of the topic.
|
|
291
|
+
* @param msg a JSON object as the message.
|
|
292
|
+
*/
|
|
293
|
+
async publishTopicMessage(exchange, topic, msg) {
|
|
294
|
+
return this.publishMessage(exchange, topic, msg);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Publish a message to the exchange
|
|
298
|
+
* @param exchange name of exchange
|
|
299
|
+
* @param key name of the key or topic
|
|
300
|
+
* @param msg payload to be published
|
|
301
|
+
*/
|
|
302
|
+
async publishMessage(exchange, key, msg) {
|
|
303
|
+
if (!key) {
|
|
304
|
+
throw new Error(`Key argument not defined`);
|
|
305
|
+
}
|
|
306
|
+
const p = this.pubs[exchange];
|
|
307
|
+
if (!p) {
|
|
308
|
+
throw new Error(`No PUB for ${exchange} found`);
|
|
309
|
+
}
|
|
310
|
+
let content = msg;
|
|
311
|
+
if (content.constructor !== {}.constructor) {
|
|
312
|
+
// if not in JSON format, let's make it so
|
|
313
|
+
content = {
|
|
314
|
+
msg: msg,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
return await p.publish({ key, content });
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Binds an exchange to a queue
|
|
321
|
+
* @param exchange the name of the exchange.
|
|
322
|
+
* @param queue the name of the queue.
|
|
323
|
+
* @param pattern (optional) pattern for routing key
|
|
324
|
+
*/
|
|
325
|
+
async bindQueueToExchange(exchange, queue, pattern) {
|
|
326
|
+
const p = this.pubs[exchange];
|
|
327
|
+
if (!p) {
|
|
328
|
+
throw new Error(`No PUB for ${exchange} found`);
|
|
329
|
+
}
|
|
330
|
+
if (!queue) {
|
|
331
|
+
throw new Error(`Binding a queue requires a queue name`);
|
|
332
|
+
}
|
|
333
|
+
return await p.bindQueue(queue, pattern || "");
|
|
334
|
+
}
|
|
335
|
+
/**
|
|
336
|
+
* Adds a listener for a specific key.
|
|
337
|
+
* If a subscriber that was registered was found
|
|
338
|
+
* with that key, the listener is added, otherwise
|
|
339
|
+
* it's ignored
|
|
340
|
+
* @param key AMQP routing key
|
|
341
|
+
* @param listener callback
|
|
342
|
+
*/
|
|
343
|
+
addMessageListener(key, listener) {
|
|
344
|
+
Object.keys(this.subs).forEach((k) => {
|
|
345
|
+
const s = this.subs[k];
|
|
346
|
+
if (s.key === key) {
|
|
347
|
+
s.registerCallback(listener);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Registers a fanout exchange with multiple bound queues.
|
|
353
|
+
* Creates the fanout exchange if it doesn't exist, then creates and binds
|
|
354
|
+
* all specified queues to the exchange.
|
|
355
|
+
* @param config Fanout configuration with exchange and queue definitions
|
|
356
|
+
*/
|
|
357
|
+
async registerFanout(config) {
|
|
358
|
+
const { exchange, queues } = config;
|
|
359
|
+
logger.debug(`Registering fanout configuration for exchange: ${exchange.name}`);
|
|
360
|
+
// First, register the fanout publisher (this creates the exchange)
|
|
361
|
+
await this.registerPublisher({ exchange });
|
|
362
|
+
// Then register subscribers for each queue
|
|
363
|
+
for (const queueConfig of queues) {
|
|
364
|
+
const subscriberOptions = {
|
|
365
|
+
exchange,
|
|
366
|
+
queue: {
|
|
367
|
+
name: queueConfig.name,
|
|
368
|
+
options: queueConfig.options,
|
|
369
|
+
},
|
|
370
|
+
key: queueConfig.routingKey || "", // Fanout exchanges ignore routing keys, but we need it for the interface
|
|
371
|
+
};
|
|
372
|
+
await this.registerSubscriber(subscriberOptions);
|
|
373
|
+
logger.debug(`Registered fanout subscriber for queue: ${queueConfig.name}${queueConfig.routingKey ? ` with routing key: ${queueConfig.routingKey}` : " (no routing key - fanout ignores it)"}`);
|
|
374
|
+
}
|
|
375
|
+
// Store the fanout configuration
|
|
376
|
+
this.fanouts[exchange.name] = config;
|
|
377
|
+
logger.info(`Successfully registered fanout configuration with ${queues.length} queues`);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Unregisters a fanout configuration by removing all associated subscribers
|
|
381
|
+
* and optionally the publisher/exchange.
|
|
382
|
+
* @param config Fanout configuration to unregister
|
|
383
|
+
* @param removeExchange Whether to also remove the exchange (default: false)
|
|
384
|
+
*/
|
|
385
|
+
async unregisterFanout(config, removeExchange = false) {
|
|
386
|
+
const { exchange, queues } = config;
|
|
387
|
+
logger.debug(`Unregistering fanout configuration for exchange: ${exchange.name}`);
|
|
388
|
+
// Unregister all subscribers
|
|
389
|
+
for (const queueConfig of queues) {
|
|
390
|
+
const unregisterOptions = {
|
|
391
|
+
exchange,
|
|
392
|
+
queue: {
|
|
393
|
+
name: queueConfig.name,
|
|
394
|
+
},
|
|
395
|
+
key: queueConfig.routingKey || "",
|
|
396
|
+
};
|
|
397
|
+
try {
|
|
398
|
+
await this.unregisterSubscriber(unregisterOptions);
|
|
399
|
+
logger.debug(`Unregistered fanout subscriber for queue: ${queueConfig.name}${queueConfig.routingKey ? ` with routing key: ${queueConfig.routingKey}` : " (no routing key)"}`);
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
logger.warn(`Failed to unregister subscriber for queue ${queueConfig.name}:`, error);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
// Optionally remove the exchange/publisher
|
|
406
|
+
if (removeExchange) {
|
|
407
|
+
try {
|
|
408
|
+
await this.unregisterPublisher({ exchange });
|
|
409
|
+
logger.debug(`Unregistered fanout publisher for exchange: ${exchange.name}`);
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
logger.warn(`Failed to unregister publisher for exchange ${exchange.name}:`, error);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
// Remove the fanout configuration from storage
|
|
416
|
+
delete this.fanouts[exchange.name];
|
|
417
|
+
logger.info(`Successfully unregistered fanout configuration`);
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Updates the connection reference to all the previously
|
|
421
|
+
* registered publishers, subscribers, and fanouts.
|
|
422
|
+
* After we successfully establish a connection and register pubs/subs/fanouts,
|
|
423
|
+
* it's possible for the connection to break. This object will try
|
|
424
|
+
* to re-establish a new connection every 3 seconds (or whatever configuration is set)
|
|
425
|
+
* and if a new connection is established, we need to pass that connection to
|
|
426
|
+
* all our pubs/subs, otherwise they'll be operating with old/stale references
|
|
427
|
+
* @param connRef a new connection reference to the AMQP service
|
|
428
|
+
*/
|
|
429
|
+
updatePubsAndSubsConnectionRef(connRef) {
|
|
430
|
+
Object.keys(this.pubs).forEach((k) => {
|
|
431
|
+
logger.debug(`Updating publisher ${k} with a new connection object`);
|
|
432
|
+
const p = this.pubs[k];
|
|
433
|
+
p.init(connRef);
|
|
434
|
+
});
|
|
435
|
+
Object.keys(this.subs).forEach((k) => {
|
|
436
|
+
logger.debug(`Updating subscriber ${k} with a new connection object`);
|
|
437
|
+
const s = this.subs[k];
|
|
438
|
+
s.init(connRef);
|
|
439
|
+
});
|
|
440
|
+
// Note: Fanouts don't need connection updates as they use the same pubs/subs
|
|
441
|
+
logger.debug(`Updated connection for ${Object.keys(this.pubs).length} publishers and ${Object.keys(this.subs).length} subscribers`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
const queue = new S4Queue();
|
|
445
|
+
export default queue;
|
|
446
|
+
//# sourceMappingURL=index.js.map
|