@warlock.js/herald 4.0.171 → 4.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/README.md +36 -33
- package/cjs/index.cjs +1355 -0
- package/cjs/index.cjs.map +1 -0
- package/esm/communicators/broker-registry.d.mts +157 -0
- package/esm/communicators/broker-registry.d.mts.map +1 -0
- package/esm/communicators/broker-registry.mjs +204 -0
- package/esm/communicators/broker-registry.mjs.map +1 -0
- package/esm/communicators/broker.d.mts +104 -0
- package/esm/communicators/broker.d.mts.map +1 -0
- package/esm/communicators/broker.mjs +104 -0
- package/esm/communicators/broker.mjs.map +1 -0
- package/esm/communicators/index.d.mts +2 -0
- package/esm/communicators/index.mjs +4 -0
- package/esm/contracts/broker-driver.contract.d.mts +200 -0
- package/esm/contracts/broker-driver.contract.d.mts.map +1 -0
- package/esm/contracts/channel.contract.d.mts +197 -0
- package/esm/contracts/channel.contract.d.mts.map +1 -0
- package/esm/contracts/index.d.mts +2 -0
- package/esm/decorators/consumable.d.mts +17 -0
- package/esm/decorators/consumable.d.mts.map +1 -0
- package/esm/decorators/consumable.mjs +36 -0
- package/esm/decorators/consumable.mjs.map +1 -0
- package/esm/decorators/index.d.mts +1 -0
- package/esm/decorators/index.mjs +3 -0
- package/esm/drivers/index.d.mts +2 -0
- package/esm/drivers/index.mjs +5 -0
- package/esm/drivers/rabbitmq/index.d.mts +2 -0
- package/esm/drivers/rabbitmq/index.mjs +4 -0
- package/esm/drivers/rabbitmq/rabbitmq-channel.d.mts +85 -0
- package/esm/drivers/rabbitmq/rabbitmq-channel.d.mts.map +1 -0
- package/esm/drivers/rabbitmq/rabbitmq-channel.mjs +352 -0
- package/esm/drivers/rabbitmq/rabbitmq-channel.mjs.map +1 -0
- package/esm/drivers/rabbitmq/rabbitmq-driver.d.mts +130 -0
- package/esm/drivers/rabbitmq/rabbitmq-driver.d.mts.map +1 -0
- package/esm/drivers/rabbitmq/rabbitmq-driver.mjs +291 -0
- package/esm/drivers/rabbitmq/rabbitmq-driver.mjs.map +1 -0
- package/esm/index.d.mts +20 -0
- package/esm/index.mjs +16 -0
- package/esm/message-managers/event-consumer.d.mts +61 -0
- package/esm/message-managers/event-consumer.d.mts.map +1 -0
- package/esm/message-managers/event-consumer.mjs +63 -0
- package/esm/message-managers/event-consumer.mjs.map +1 -0
- package/esm/message-managers/event-message.d.mts +87 -0
- package/esm/message-managers/event-message.d.mts.map +1 -0
- package/esm/message-managers/event-message.mjs +68 -0
- package/esm/message-managers/event-message.mjs.map +1 -0
- package/esm/message-managers/index.d.mts +3 -0
- package/esm/message-managers/index.mjs +4 -0
- package/esm/message-managers/prepare-consumer-subscription.mjs +42 -0
- package/esm/message-managers/prepare-consumer-subscription.mjs.map +1 -0
- package/esm/message-managers/types.d.mts +23 -0
- package/esm/message-managers/types.d.mts.map +1 -0
- package/esm/types/channel.types.d.mts +23 -0
- package/esm/types/channel.types.d.mts.map +1 -0
- package/esm/types/connection.types.d.mts +67 -0
- package/esm/types/connection.types.d.mts.map +1 -0
- package/esm/types/driver.types.d.mts +13 -0
- package/esm/types/driver.types.d.mts.map +1 -0
- package/esm/types/index.d.mts +7 -0
- package/esm/types/message.types.d.mts +37 -0
- package/esm/types/message.types.d.mts.map +1 -0
- package/esm/types/publish.types.d.mts +16 -0
- package/esm/types/publish.types.d.mts.map +1 -0
- package/esm/types/registry.types.d.mts +8 -0
- package/esm/types/registry.types.d.mts.map +1 -0
- package/esm/types/subscribe.types.d.mts +21 -0
- package/esm/types/subscribe.types.d.mts.map +1 -0
- package/esm/use-case-broadcast.d.mts +41 -0
- package/esm/use-case-broadcast.d.mts.map +1 -0
- package/esm/use-case-broadcast.mjs +30 -0
- package/esm/use-case-broadcast.mjs.map +1 -0
- package/esm/utils/{connect-to-broker.d.ts → connect-to-broker.d.mts} +15 -10
- package/esm/utils/connect-to-broker.d.mts.map +1 -0
- package/esm/utils/connect-to-broker.mjs +168 -0
- package/esm/utils/connect-to-broker.mjs.map +1 -0
- package/esm/utils/index.d.mts +1 -0
- package/esm/utils/index.mjs +3 -0
- package/llms-full.txt +581 -0
- package/llms.txt +12 -0
- package/package.json +46 -46
- package/skills/README.md +21 -0
- package/skills/consume-message/SKILL.md +217 -0
- package/skills/herald-basics/SKILL.md +96 -0
- package/skills/publish-message/SKILL.md +172 -0
- package/skills/request-and-respond/SKILL.md +74 -0
- package/cjs/communicators/broker-registry.d.ts +0 -155
- package/cjs/communicators/broker-registry.d.ts.map +0 -1
- package/cjs/communicators/broker-registry.js +0 -206
- package/cjs/communicators/broker-registry.js.map +0 -1
- package/cjs/communicators/broker.d.ts +0 -100
- package/cjs/communicators/broker.d.ts.map +0 -1
- package/cjs/communicators/broker.js +0 -105
- package/cjs/communicators/broker.js.map +0 -1
- package/cjs/communicators/index.d.ts +0 -3
- package/cjs/communicators/index.d.ts.map +0 -1
- package/cjs/contracts/broker-driver.contract.d.ts +0 -196
- package/cjs/contracts/broker-driver.contract.d.ts.map +0 -1
- package/cjs/contracts/channel.contract.d.ts +0 -191
- package/cjs/contracts/channel.contract.d.ts.map +0 -1
- package/cjs/contracts/index.d.ts +0 -3
- package/cjs/contracts/index.d.ts.map +0 -1
- package/cjs/decorators/consumable.d.ts +0 -13
- package/cjs/decorators/consumable.d.ts.map +0 -1
- package/cjs/decorators/consumable.js +0 -33
- package/cjs/decorators/consumable.js.map +0 -1
- package/cjs/decorators/index.d.ts +0 -2
- package/cjs/decorators/index.d.ts.map +0 -1
- package/cjs/drivers/index.d.ts +0 -2
- package/cjs/drivers/index.d.ts.map +0 -1
- package/cjs/drivers/rabbitmq/index.d.ts +0 -3
- package/cjs/drivers/rabbitmq/index.d.ts.map +0 -1
- package/cjs/drivers/rabbitmq/rabbitmq-channel.d.ts +0 -79
- package/cjs/drivers/rabbitmq/rabbitmq-channel.d.ts.map +0 -1
- package/cjs/drivers/rabbitmq/rabbitmq-channel.js +0 -414
- package/cjs/drivers/rabbitmq/rabbitmq-channel.js.map +0 -1
- package/cjs/drivers/rabbitmq/rabbitmq-driver.d.ts +0 -124
- package/cjs/drivers/rabbitmq/rabbitmq-driver.d.ts.map +0 -1
- package/cjs/drivers/rabbitmq/rabbitmq-driver.js +0 -347
- package/cjs/drivers/rabbitmq/rabbitmq-driver.js.map +0 -1
- package/cjs/index.d.ts +0 -47
- package/cjs/index.d.ts.map +0 -1
- package/cjs/index.js +0 -1
- package/cjs/index.js.map +0 -1
- package/cjs/message-managers/event-consumer.d.ts +0 -64
- package/cjs/message-managers/event-consumer.d.ts.map +0 -1
- package/cjs/message-managers/event-consumer.js +0 -71
- package/cjs/message-managers/event-consumer.js.map +0 -1
- package/cjs/message-managers/event-message.d.ts +0 -90
- package/cjs/message-managers/event-message.d.ts.map +0 -1
- package/cjs/message-managers/event-message.js +0 -84
- package/cjs/message-managers/event-message.js.map +0 -1
- package/cjs/message-managers/index.d.ts +0 -4
- package/cjs/message-managers/index.d.ts.map +0 -1
- package/cjs/message-managers/prepare-consumer-subscription.d.ts +0 -4
- package/cjs/message-managers/prepare-consumer-subscription.d.ts.map +0 -1
- package/cjs/message-managers/prepare-consumer-subscription.js +0 -40
- package/cjs/message-managers/prepare-consumer-subscription.js.map +0 -1
- package/cjs/message-managers/types.d.ts +0 -20
- package/cjs/message-managers/types.d.ts.map +0 -1
- package/cjs/types/channel.types.d.ts +0 -19
- package/cjs/types/channel.types.d.ts.map +0 -1
- package/cjs/types/connection.types.d.ts +0 -65
- package/cjs/types/connection.types.d.ts.map +0 -1
- package/cjs/types/driver.types.d.ts +0 -10
- package/cjs/types/driver.types.d.ts.map +0 -1
- package/cjs/types/index.d.ts +0 -8
- package/cjs/types/index.d.ts.map +0 -1
- package/cjs/types/message.types.d.ts +0 -34
- package/cjs/types/message.types.d.ts.map +0 -1
- package/cjs/types/publish.types.d.ts +0 -13
- package/cjs/types/publish.types.d.ts.map +0 -1
- package/cjs/types/registry.types.d.ts +0 -4
- package/cjs/types/registry.types.d.ts.map +0 -1
- package/cjs/types/subscribe.types.d.ts +0 -18
- package/cjs/types/subscribe.types.d.ts.map +0 -1
- package/cjs/utils/connect-to-broker.d.ts +0 -131
- package/cjs/utils/connect-to-broker.d.ts.map +0 -1
- package/cjs/utils/connect-to-broker.js +0 -171
- package/cjs/utils/connect-to-broker.js.map +0 -1
- package/cjs/utils/index.d.ts +0 -2
- package/cjs/utils/index.d.ts.map +0 -1
- package/esm/communicators/broker-registry.d.ts +0 -155
- package/esm/communicators/broker-registry.d.ts.map +0 -1
- package/esm/communicators/broker-registry.js +0 -206
- package/esm/communicators/broker-registry.js.map +0 -1
- package/esm/communicators/broker.d.ts +0 -100
- package/esm/communicators/broker.d.ts.map +0 -1
- package/esm/communicators/broker.js +0 -105
- package/esm/communicators/broker.js.map +0 -1
- package/esm/communicators/index.d.ts +0 -3
- package/esm/communicators/index.d.ts.map +0 -1
- package/esm/contracts/broker-driver.contract.d.ts +0 -196
- package/esm/contracts/broker-driver.contract.d.ts.map +0 -1
- package/esm/contracts/channel.contract.d.ts +0 -191
- package/esm/contracts/channel.contract.d.ts.map +0 -1
- package/esm/contracts/index.d.ts +0 -3
- package/esm/contracts/index.d.ts.map +0 -1
- package/esm/decorators/consumable.d.ts +0 -13
- package/esm/decorators/consumable.d.ts.map +0 -1
- package/esm/decorators/consumable.js +0 -33
- package/esm/decorators/consumable.js.map +0 -1
- package/esm/decorators/index.d.ts +0 -2
- package/esm/decorators/index.d.ts.map +0 -1
- package/esm/drivers/index.d.ts +0 -2
- package/esm/drivers/index.d.ts.map +0 -1
- package/esm/drivers/rabbitmq/index.d.ts +0 -3
- package/esm/drivers/rabbitmq/index.d.ts.map +0 -1
- package/esm/drivers/rabbitmq/rabbitmq-channel.d.ts +0 -79
- package/esm/drivers/rabbitmq/rabbitmq-channel.d.ts.map +0 -1
- package/esm/drivers/rabbitmq/rabbitmq-channel.js +0 -414
- package/esm/drivers/rabbitmq/rabbitmq-channel.js.map +0 -1
- package/esm/drivers/rabbitmq/rabbitmq-driver.d.ts +0 -124
- package/esm/drivers/rabbitmq/rabbitmq-driver.d.ts.map +0 -1
- package/esm/drivers/rabbitmq/rabbitmq-driver.js +0 -347
- package/esm/drivers/rabbitmq/rabbitmq-driver.js.map +0 -1
- package/esm/index.d.ts +0 -47
- package/esm/index.d.ts.map +0 -1
- package/esm/index.js +0 -1
- package/esm/index.js.map +0 -1
- package/esm/message-managers/event-consumer.d.ts +0 -64
- package/esm/message-managers/event-consumer.d.ts.map +0 -1
- package/esm/message-managers/event-consumer.js +0 -71
- package/esm/message-managers/event-consumer.js.map +0 -1
- package/esm/message-managers/event-message.d.ts +0 -90
- package/esm/message-managers/event-message.d.ts.map +0 -1
- package/esm/message-managers/event-message.js +0 -84
- package/esm/message-managers/event-message.js.map +0 -1
- package/esm/message-managers/index.d.ts +0 -4
- package/esm/message-managers/index.d.ts.map +0 -1
- package/esm/message-managers/prepare-consumer-subscription.d.ts +0 -4
- package/esm/message-managers/prepare-consumer-subscription.d.ts.map +0 -1
- package/esm/message-managers/prepare-consumer-subscription.js +0 -40
- package/esm/message-managers/prepare-consumer-subscription.js.map +0 -1
- package/esm/message-managers/types.d.ts +0 -20
- package/esm/message-managers/types.d.ts.map +0 -1
- package/esm/types/channel.types.d.ts +0 -19
- package/esm/types/channel.types.d.ts.map +0 -1
- package/esm/types/connection.types.d.ts +0 -65
- package/esm/types/connection.types.d.ts.map +0 -1
- package/esm/types/driver.types.d.ts +0 -10
- package/esm/types/driver.types.d.ts.map +0 -1
- package/esm/types/index.d.ts +0 -8
- package/esm/types/index.d.ts.map +0 -1
- package/esm/types/message.types.d.ts +0 -34
- package/esm/types/message.types.d.ts.map +0 -1
- package/esm/types/publish.types.d.ts +0 -13
- package/esm/types/publish.types.d.ts.map +0 -1
- package/esm/types/registry.types.d.ts +0 -4
- package/esm/types/registry.types.d.ts.map +0 -1
- package/esm/types/subscribe.types.d.ts +0 -18
- package/esm/types/subscribe.types.d.ts.map +0 -1
- package/esm/utils/connect-to-broker.d.ts.map +0 -1
- package/esm/utils/connect-to-broker.js +0 -171
- package/esm/utils/connect-to-broker.js.map +0 -1
- package/esm/utils/index.d.ts +0 -2
- package/esm/utils/index.d.ts.map +0 -1
package/cjs/index.cjs
ADDED
|
@@ -0,0 +1,1355 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __exportAll = (all, no_symbols) => {
|
|
5
|
+
let target = {};
|
|
6
|
+
for (var name in all) {
|
|
7
|
+
__defProp(target, name, {
|
|
8
|
+
get: all[name],
|
|
9
|
+
enumerable: true
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
if (!no_symbols) {
|
|
13
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
14
|
+
}
|
|
15
|
+
return target;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
//#endregion
|
|
19
|
+
let node_events = require("node:events");
|
|
20
|
+
let _warlock_js_seal = require("@warlock.js/seal");
|
|
21
|
+
let node_crypto = require("node:crypto");
|
|
22
|
+
let crypto = require("crypto");
|
|
23
|
+
|
|
24
|
+
//#region ../../@warlock.js/herald/src/communicators/broker.ts
|
|
25
|
+
/**
|
|
26
|
+
* Broker - wrapper around a driver with metadata
|
|
27
|
+
*
|
|
28
|
+
* Similar to DataSource in @warlock.js/cascade
|
|
29
|
+
*
|
|
30
|
+
* @example
|
|
31
|
+
* ```typescript
|
|
32
|
+
* const broker = new Broker({
|
|
33
|
+
* name: "default",
|
|
34
|
+
* driver: rabbitMQDriver,
|
|
35
|
+
* isDefault: true,
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // Get a channel
|
|
39
|
+
* const channel = broker.channel("user.created");
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
var Broker = class {
|
|
43
|
+
/**
|
|
44
|
+
* Create a new Broker
|
|
45
|
+
*
|
|
46
|
+
* @param options - Broker configuration
|
|
47
|
+
*/
|
|
48
|
+
constructor(options) {
|
|
49
|
+
this.name = options.name;
|
|
50
|
+
this.driver = options.driver;
|
|
51
|
+
this.isDefault = Boolean(options.isDefault);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Subscribe the given consumer
|
|
55
|
+
*/
|
|
56
|
+
subscribe(consumer) {
|
|
57
|
+
return this.driver.subscribe(consumer);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Publish the given event message
|
|
61
|
+
*/
|
|
62
|
+
publish(event) {
|
|
63
|
+
this.driver.publish(event);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Get or create a channel
|
|
67
|
+
*
|
|
68
|
+
* @param name - Channel name
|
|
69
|
+
* @param options - Channel options
|
|
70
|
+
* @returns Channel instance
|
|
71
|
+
*
|
|
72
|
+
* @example
|
|
73
|
+
* ```typescript
|
|
74
|
+
* // Simple channel
|
|
75
|
+
* const channel = broker.channel("notifications");
|
|
76
|
+
*
|
|
77
|
+
* // Typed channel with schema
|
|
78
|
+
* const orderChannel = broker.channel<OrderPayload>("orders", {
|
|
79
|
+
* schema: OrderSchema,
|
|
80
|
+
* durable: true,
|
|
81
|
+
* });
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
channel(name, options) {
|
|
85
|
+
return this.driver.channel(name, options);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Check if the broker is connected
|
|
89
|
+
*/
|
|
90
|
+
get isConnected() {
|
|
91
|
+
return this.driver.isConnected;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Connect the underlying driver
|
|
95
|
+
*/
|
|
96
|
+
async connect() {
|
|
97
|
+
await this.driver.connect();
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Disconnect the underlying driver
|
|
101
|
+
*/
|
|
102
|
+
async disconnect() {
|
|
103
|
+
await this.driver.disconnect();
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Start consuming messages
|
|
107
|
+
*/
|
|
108
|
+
async startConsuming() {
|
|
109
|
+
await this.driver.startConsuming();
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Stop consuming messages
|
|
113
|
+
*/
|
|
114
|
+
async stopConsuming() {
|
|
115
|
+
await this.driver.stopConsuming();
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Health check
|
|
119
|
+
*/
|
|
120
|
+
async healthCheck() {
|
|
121
|
+
return this.driver.healthCheck();
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
//#endregion
|
|
126
|
+
//#region ../../@warlock.js/herald/src/communicators/broker-registry.ts
|
|
127
|
+
/**
|
|
128
|
+
* Error thrown when a broker is not found
|
|
129
|
+
*/
|
|
130
|
+
var MissingBrokerError = class extends Error {
|
|
131
|
+
constructor(message, brokerName) {
|
|
132
|
+
super(message);
|
|
133
|
+
this.name = "MissingBrokerError";
|
|
134
|
+
this.brokerName = brokerName;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
/**
|
|
138
|
+
* Broker Registry
|
|
139
|
+
*
|
|
140
|
+
* Maintains registry of named brokers.
|
|
141
|
+
* Similar to DataSourceRegistry in @warlock.js/cascade
|
|
142
|
+
*
|
|
143
|
+
* @example
|
|
144
|
+
* ```typescript
|
|
145
|
+
* // Register a broker
|
|
146
|
+
* brokerRegistry.register({
|
|
147
|
+
* name: "default",
|
|
148
|
+
* driver: rabbitMQDriver,
|
|
149
|
+
* isDefault: true,
|
|
150
|
+
* });
|
|
151
|
+
*
|
|
152
|
+
* // Get the default broker
|
|
153
|
+
* const comm = brokerRegistry.get();
|
|
154
|
+
*
|
|
155
|
+
* // Get a specific broker by name
|
|
156
|
+
* const analytics = brokerRegistry.get("analytics");
|
|
157
|
+
*
|
|
158
|
+
* // Listen for events
|
|
159
|
+
* brokerRegistry.on("connected", (comm) => {
|
|
160
|
+
* console.log(`${comm.name} connected`);
|
|
161
|
+
* });
|
|
162
|
+
* ```
|
|
163
|
+
*/
|
|
164
|
+
var BrokerRegistry = class {
|
|
165
|
+
constructor() {
|
|
166
|
+
this.sources = /* @__PURE__ */ new Map();
|
|
167
|
+
this.events = new node_events.EventEmitter();
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Register a new broker
|
|
171
|
+
*
|
|
172
|
+
* Sets up event forwarding from the driver to the registry.
|
|
173
|
+
*
|
|
174
|
+
* @param options - Broker configuration
|
|
175
|
+
* @returns The registered broker instance
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```typescript
|
|
179
|
+
* const broker = brokerRegistry.register({
|
|
180
|
+
* name: "primary",
|
|
181
|
+
* driver: myDriver,
|
|
182
|
+
* isDefault: true,
|
|
183
|
+
* });
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
register(options) {
|
|
187
|
+
const broker = new Broker(options);
|
|
188
|
+
this.sources.set(broker.name, broker);
|
|
189
|
+
const isNewDefault = broker.isDefault || !this.defaultSource;
|
|
190
|
+
if (isNewDefault) this.defaultSource = broker;
|
|
191
|
+
this.events.emit("registered", broker);
|
|
192
|
+
if (isNewDefault) this.events.emit("default-registered", broker);
|
|
193
|
+
broker.driver.on("connected", () => {
|
|
194
|
+
this.events.emit("connected", broker);
|
|
195
|
+
});
|
|
196
|
+
broker.driver.on("disconnected", () => {
|
|
197
|
+
this.events.emit("disconnected", broker);
|
|
198
|
+
});
|
|
199
|
+
return broker;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Clear all registered brokers
|
|
203
|
+
*/
|
|
204
|
+
clear() {
|
|
205
|
+
this.defaultSource = void 0;
|
|
206
|
+
this.sources.clear();
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Listen for registry events
|
|
210
|
+
*
|
|
211
|
+
* @param event - Event to listen for
|
|
212
|
+
* @param listener - Callback function
|
|
213
|
+
*
|
|
214
|
+
* @example
|
|
215
|
+
* ```typescript
|
|
216
|
+
* brokerRegistry.on("registered", (comm) => {
|
|
217
|
+
* console.log(`Broker "${comm.name}" registered`);
|
|
218
|
+
* });
|
|
219
|
+
*
|
|
220
|
+
* brokerRegistry.on("connected", (comm) => {
|
|
221
|
+
* console.log(`Broker "${comm.name}" connected`);
|
|
222
|
+
* });
|
|
223
|
+
* ```
|
|
224
|
+
*/
|
|
225
|
+
on(event, listener) {
|
|
226
|
+
this.events.on(event, listener);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Listen for a registry event once
|
|
230
|
+
*
|
|
231
|
+
* @param event - Event to listen for
|
|
232
|
+
* @param listener - Callback function
|
|
233
|
+
*/
|
|
234
|
+
once(event, listener) {
|
|
235
|
+
this.events.once(event, listener);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Remove an event listener
|
|
239
|
+
*
|
|
240
|
+
* @param event - Event to stop listening for
|
|
241
|
+
* @param listener - Callback to remove
|
|
242
|
+
*/
|
|
243
|
+
off(event, listener) {
|
|
244
|
+
this.events.off(event, listener);
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Get a broker by name or the default one
|
|
248
|
+
*
|
|
249
|
+
* @param name - Optional broker name
|
|
250
|
+
* @returns Broker instance
|
|
251
|
+
* @throws MissingBrokerError if not found
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* ```typescript
|
|
255
|
+
* // Get default broker
|
|
256
|
+
* const comm = brokerRegistry.get();
|
|
257
|
+
*
|
|
258
|
+
* // Get specific broker
|
|
259
|
+
* const analytics = brokerRegistry.get("analytics");
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
get(name) {
|
|
263
|
+
if (name !== void 0) {
|
|
264
|
+
const source = this.sources.get(name);
|
|
265
|
+
if (!source) throw new MissingBrokerError(`Broker "${name}" is not registered.`, name);
|
|
266
|
+
return source;
|
|
267
|
+
}
|
|
268
|
+
if (!this.defaultSource) throw new MissingBrokerError("No default broker registered.");
|
|
269
|
+
return this.defaultSource;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Check if a broker exists
|
|
273
|
+
*
|
|
274
|
+
* @param name - Broker name to check
|
|
275
|
+
* @returns True if exists
|
|
276
|
+
*/
|
|
277
|
+
has(name) {
|
|
278
|
+
return this.sources.has(name);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Check if any brokers are registered
|
|
282
|
+
*/
|
|
283
|
+
hasAny() {
|
|
284
|
+
return this.sources.size > 0;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Get all registered brokers
|
|
288
|
+
*
|
|
289
|
+
* @returns Array of all brokers
|
|
290
|
+
*
|
|
291
|
+
* @example
|
|
292
|
+
* ```typescript
|
|
293
|
+
* // Disconnect all brokers
|
|
294
|
+
* for (const comm of brokerRegistry.getAll()) {
|
|
295
|
+
* await comm.disconnect();
|
|
296
|
+
* }
|
|
297
|
+
* ```
|
|
298
|
+
*/
|
|
299
|
+
getAll() {
|
|
300
|
+
return Array.from(this.sources.values());
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Get all broker names
|
|
304
|
+
*
|
|
305
|
+
* @returns Array of broker names
|
|
306
|
+
*/
|
|
307
|
+
getNames() {
|
|
308
|
+
return Array.from(this.sources.keys());
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Get the default broker (if any)
|
|
312
|
+
*
|
|
313
|
+
* @returns Default broker or undefined
|
|
314
|
+
*/
|
|
315
|
+
getDefault() {
|
|
316
|
+
return this.defaultSource;
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
/**
|
|
320
|
+
* Global broker registry instance
|
|
321
|
+
*/
|
|
322
|
+
const brokerRegistry = new BrokerRegistry();
|
|
323
|
+
|
|
324
|
+
//#endregion
|
|
325
|
+
//#region ../../@warlock.js/herald/src/message-managers/prepare-consumer-subscription.ts
|
|
326
|
+
function prepareConsumerSubscription(Consumer, onError) {
|
|
327
|
+
const callback = async (message, ctx) => {
|
|
328
|
+
const envelope = message.payload;
|
|
329
|
+
let eventPayload = envelope.payload;
|
|
330
|
+
if (envelope.version) {
|
|
331
|
+
if (!Consumer.isAcceptedVersion(Number(envelope.version))) {
|
|
332
|
+
ctx.ack();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
const consumer = new Consumer();
|
|
337
|
+
if (consumer.schema) {
|
|
338
|
+
const result = await consumer.validate(eventPayload);
|
|
339
|
+
if (!result || result.isValid === false) {
|
|
340
|
+
ctx.nack();
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
eventPayload = result.data;
|
|
344
|
+
}
|
|
345
|
+
try {
|
|
346
|
+
await consumer.handle(eventPayload, {
|
|
347
|
+
payload: eventPayload,
|
|
348
|
+
eventName: Consumer.eventName,
|
|
349
|
+
messageId: message.metadata.messageId,
|
|
350
|
+
occurredAt: envelope.occurredAt ? new Date(envelope.occurredAt) : void 0,
|
|
351
|
+
metadata: envelope.metadata,
|
|
352
|
+
version: envelope.version,
|
|
353
|
+
message
|
|
354
|
+
});
|
|
355
|
+
ctx.ack();
|
|
356
|
+
} catch (error) {
|
|
357
|
+
ctx.nack(true);
|
|
358
|
+
if (onError) onError(error, Consumer.eventName);
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
return callback;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
//#endregion
|
|
365
|
+
//#region ../../@warlock.js/herald/src/drivers/rabbitmq/rabbitmq-channel.ts
|
|
366
|
+
/**
|
|
367
|
+
* RabbitMQ Channel Implementation
|
|
368
|
+
*
|
|
369
|
+
* Wraps a RabbitMQ queue/exchange with a unified API.
|
|
370
|
+
*
|
|
371
|
+
* @template TPayload - The typed payload
|
|
372
|
+
*/
|
|
373
|
+
var RabbitMQChannel = class {
|
|
374
|
+
/**
|
|
375
|
+
* Create a new RabbitMQ channel
|
|
376
|
+
*/
|
|
377
|
+
constructor(name, amqpChannel, options) {
|
|
378
|
+
this.subscriptions = /* @__PURE__ */ new Map();
|
|
379
|
+
this.asserted = false;
|
|
380
|
+
this.name = name;
|
|
381
|
+
this.amqpChannel = amqpChannel;
|
|
382
|
+
this.options = options ?? {};
|
|
383
|
+
}
|
|
384
|
+
/**
|
|
385
|
+
* Assert the queue exists
|
|
386
|
+
*/
|
|
387
|
+
async assert() {
|
|
388
|
+
if (this.asserted) return;
|
|
389
|
+
const queueOptions = {
|
|
390
|
+
durable: this.options.durable ?? true,
|
|
391
|
+
autoDelete: this.options.autoDelete ?? false,
|
|
392
|
+
exclusive: this.options.exclusive ?? false,
|
|
393
|
+
messageTtl: this.options.messageTtl,
|
|
394
|
+
maxLength: this.options.maxLength,
|
|
395
|
+
deadLetterExchange: this.options.deadLetter?.channel ? "" : void 0,
|
|
396
|
+
deadLetterRoutingKey: this.options.deadLetter?.channel
|
|
397
|
+
};
|
|
398
|
+
await this.amqpChannel.assertQueue(this.name, queueOptions);
|
|
399
|
+
this.asserted = true;
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Publish a message
|
|
403
|
+
*/
|
|
404
|
+
async publish(payload, options) {
|
|
405
|
+
await this.assert();
|
|
406
|
+
if (this.options.schema) {
|
|
407
|
+
const context = {
|
|
408
|
+
allValues: payload,
|
|
409
|
+
value: payload
|
|
410
|
+
};
|
|
411
|
+
const result = await _warlock_js_seal.v.validate(this.options.schema, payload, { context });
|
|
412
|
+
if (!result.isValid) throw new Error(`Message validation failed: ${JSON.stringify(result.errors)}`);
|
|
413
|
+
payload = result.data;
|
|
414
|
+
}
|
|
415
|
+
const messageId = (0, node_crypto.randomUUID)();
|
|
416
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
417
|
+
const messageContent = JSON.stringify({
|
|
418
|
+
payload,
|
|
419
|
+
metadata: {
|
|
420
|
+
messageId,
|
|
421
|
+
timestamp: timestamp.toISOString(),
|
|
422
|
+
correlationId: options?.correlationId,
|
|
423
|
+
headers: options?.headers
|
|
424
|
+
}
|
|
425
|
+
});
|
|
426
|
+
const publishOptions = {
|
|
427
|
+
persistent: options?.persistent ?? true,
|
|
428
|
+
messageId,
|
|
429
|
+
timestamp: timestamp.getTime(),
|
|
430
|
+
correlationId: options?.correlationId,
|
|
431
|
+
expiration: options?.expiration?.toString(),
|
|
432
|
+
priority: options?.priority,
|
|
433
|
+
headers: options?.headers
|
|
434
|
+
};
|
|
435
|
+
if (options?.delay) publishOptions.headers = {
|
|
436
|
+
...publishOptions.headers,
|
|
437
|
+
"x-delay": options.delay
|
|
438
|
+
};
|
|
439
|
+
this.amqpChannel.sendToQueue(this.name, Buffer.from(messageContent), publishOptions);
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* Publish multiple messages
|
|
443
|
+
*/
|
|
444
|
+
async publishBatch(messages, options) {
|
|
445
|
+
for (const payload of messages) await this.publish(payload, options);
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Subscribe to messages
|
|
449
|
+
*
|
|
450
|
+
* Smart auto-ack behavior (when autoAck is not true):
|
|
451
|
+
* - If handler completes successfully without explicit ack/nack/reject → auto-ack
|
|
452
|
+
* - If handler throws an error → auto-nack (with retry if configured)
|
|
453
|
+
* - If handler explicitly calls ack/nack/reject → respects that call
|
|
454
|
+
*/
|
|
455
|
+
async subscribe(handler, options) {
|
|
456
|
+
await this.assert();
|
|
457
|
+
const subscriptionId = options?.consumerId ?? (0, node_crypto.randomUUID)();
|
|
458
|
+
if (options?.prefetch) await this.amqpChannel.prefetch(options.prefetch);
|
|
459
|
+
const isFireAndForget = options?.autoAck === true;
|
|
460
|
+
const consumerOptions = {
|
|
461
|
+
noAck: isFireAndForget,
|
|
462
|
+
exclusive: options?.exclusive ?? false,
|
|
463
|
+
consumerTag: options?.group ?? subscriptionId
|
|
464
|
+
};
|
|
465
|
+
const { consumerTag } = await this.amqpChannel.consume(this.name, async (msg) => {
|
|
466
|
+
if (!msg) return;
|
|
467
|
+
let ackHandled = isFireAndForget;
|
|
468
|
+
try {
|
|
469
|
+
const content = JSON.parse(msg.content.toString());
|
|
470
|
+
let payload = content.payload;
|
|
471
|
+
if (this.options.schema) {
|
|
472
|
+
const context = {
|
|
473
|
+
allValues: payload,
|
|
474
|
+
value: payload
|
|
475
|
+
};
|
|
476
|
+
const result = await _warlock_js_seal.v.validate(this.options.schema, payload, { context });
|
|
477
|
+
if (!result.isValid) {
|
|
478
|
+
this.amqpChannel.nack(msg, false, false);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
payload = result.data;
|
|
482
|
+
}
|
|
483
|
+
const metadata = {
|
|
484
|
+
messageId: msg.properties.messageId || content.metadata?.messageId || (0, node_crypto.randomUUID)(),
|
|
485
|
+
timestamp: new Date(msg.properties.timestamp || content.metadata?.timestamp),
|
|
486
|
+
correlationId: msg.properties.correlationId || content.metadata?.correlationId,
|
|
487
|
+
replyTo: msg.properties.replyTo,
|
|
488
|
+
priority: msg.properties.priority,
|
|
489
|
+
headers: msg.properties.headers,
|
|
490
|
+
retryCount: msg.properties.headers?.["x-retry-count"] || 0,
|
|
491
|
+
originalChannel: this.name
|
|
492
|
+
};
|
|
493
|
+
const message = {
|
|
494
|
+
metadata,
|
|
495
|
+
payload,
|
|
496
|
+
raw: msg
|
|
497
|
+
};
|
|
498
|
+
await handler(message, {
|
|
499
|
+
ack: async () => {
|
|
500
|
+
if (!ackHandled) {
|
|
501
|
+
ackHandled = true;
|
|
502
|
+
this.amqpChannel.ack(msg);
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
nack: async (requeue = true) => {
|
|
506
|
+
if (!ackHandled) {
|
|
507
|
+
ackHandled = true;
|
|
508
|
+
this.amqpChannel.nack(msg, false, requeue);
|
|
509
|
+
}
|
|
510
|
+
},
|
|
511
|
+
reject: async () => {
|
|
512
|
+
if (!ackHandled) {
|
|
513
|
+
ackHandled = true;
|
|
514
|
+
this.amqpChannel.reject(msg, false);
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
reply: async (replyPayload) => {
|
|
518
|
+
if (msg.properties.replyTo) {
|
|
519
|
+
const replyContent = JSON.stringify({
|
|
520
|
+
payload: replyPayload,
|
|
521
|
+
metadata: {
|
|
522
|
+
messageId: (0, node_crypto.randomUUID)(),
|
|
523
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
524
|
+
correlationId: msg.properties.correlationId
|
|
525
|
+
}
|
|
526
|
+
});
|
|
527
|
+
this.amqpChannel.sendToQueue(msg.properties.replyTo, Buffer.from(replyContent), { correlationId: msg.properties.correlationId });
|
|
528
|
+
}
|
|
529
|
+
},
|
|
530
|
+
retry: async (delay) => {
|
|
531
|
+
if (ackHandled) return;
|
|
532
|
+
ackHandled = true;
|
|
533
|
+
const retryCount = (metadata.retryCount || 0) + 1;
|
|
534
|
+
if (retryCount > (options?.retry?.maxRetries ?? 3)) {
|
|
535
|
+
if (options?.deadLetter) await this.sendToDeadLetter(message, options.deadLetter.channel);
|
|
536
|
+
this.amqpChannel.ack(msg);
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
const headers = {
|
|
540
|
+
...msg.properties.headers,
|
|
541
|
+
"x-retry-count": retryCount
|
|
542
|
+
};
|
|
543
|
+
if (delay) headers["x-delay"] = delay;
|
|
544
|
+
this.amqpChannel.sendToQueue(this.name, msg.content, {
|
|
545
|
+
...msg.properties,
|
|
546
|
+
headers
|
|
547
|
+
});
|
|
548
|
+
this.amqpChannel.ack(msg);
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
if (!ackHandled) this.amqpChannel.ack(msg);
|
|
552
|
+
} catch (error) {
|
|
553
|
+
if (ackHandled) return;
|
|
554
|
+
if (options?.retry) if ((msg.properties.headers?.["x-retry-count"] || 0) < options.retry.maxRetries) this.amqpChannel.nack(msg, false, true);
|
|
555
|
+
else if (options.deadLetter) this.amqpChannel.nack(msg, false, false);
|
|
556
|
+
else this.amqpChannel.reject(msg, false);
|
|
557
|
+
else this.amqpChannel.nack(msg, false, false);
|
|
558
|
+
}
|
|
559
|
+
}, consumerOptions);
|
|
560
|
+
const subscription = new RabbitMQSubscription(subscriptionId, this.name, consumerTag, this.amqpChannel);
|
|
561
|
+
this.subscriptions.set(subscriptionId, subscription);
|
|
562
|
+
return subscription;
|
|
563
|
+
}
|
|
564
|
+
/**
|
|
565
|
+
* Unsubscribe by consumer ID
|
|
566
|
+
*/
|
|
567
|
+
async unsubscribeById(consumerId) {
|
|
568
|
+
const subscription = this.subscriptions.get(consumerId);
|
|
569
|
+
if (subscription) {
|
|
570
|
+
await subscription.unsubscribe();
|
|
571
|
+
this.subscriptions.delete(consumerId);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Stop consuming messages on this channel.
|
|
576
|
+
* Cancels all active subscriptions gracefully.
|
|
577
|
+
*/
|
|
578
|
+
async stopConsuming() {
|
|
579
|
+
const cancellations = Array.from(this.subscriptions.values()).map((sub) => sub.unsubscribe());
|
|
580
|
+
await Promise.all(cancellations);
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Send message to dead-letter queue
|
|
584
|
+
*/
|
|
585
|
+
async sendToDeadLetter(message, deadLetterChannel) {
|
|
586
|
+
const content = JSON.stringify({
|
|
587
|
+
payload: message.payload,
|
|
588
|
+
metadata: {
|
|
589
|
+
...message.metadata,
|
|
590
|
+
originalChannel: this.name
|
|
591
|
+
}
|
|
592
|
+
});
|
|
593
|
+
this.amqpChannel.sendToQueue(deadLetterChannel, Buffer.from(content), { persistent: true });
|
|
594
|
+
}
|
|
595
|
+
/**
|
|
596
|
+
* Request-response pattern
|
|
597
|
+
*/
|
|
598
|
+
async request(payload, options) {
|
|
599
|
+
await this.assert();
|
|
600
|
+
const correlationId = (0, node_crypto.randomUUID)();
|
|
601
|
+
const timeout = options?.timeout ?? 3e4;
|
|
602
|
+
const { queue: replyQueue } = await this.amqpChannel.assertQueue("", {
|
|
603
|
+
exclusive: true,
|
|
604
|
+
autoDelete: true
|
|
605
|
+
});
|
|
606
|
+
return new Promise((resolve, reject) => {
|
|
607
|
+
const timeoutId = setTimeout(() => {
|
|
608
|
+
reject(/* @__PURE__ */ new Error(`Request timeout after ${timeout}ms`));
|
|
609
|
+
}, timeout);
|
|
610
|
+
this.amqpChannel.consume(replyQueue, (msg) => {
|
|
611
|
+
if (msg?.properties.correlationId === correlationId) {
|
|
612
|
+
clearTimeout(timeoutId);
|
|
613
|
+
resolve(JSON.parse(msg.content.toString()).payload);
|
|
614
|
+
}
|
|
615
|
+
}, { noAck: true });
|
|
616
|
+
const messageContent = JSON.stringify({
|
|
617
|
+
payload,
|
|
618
|
+
metadata: {
|
|
619
|
+
messageId: (0, node_crypto.randomUUID)(),
|
|
620
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
621
|
+
correlationId
|
|
622
|
+
}
|
|
623
|
+
});
|
|
624
|
+
this.amqpChannel.sendToQueue(this.name, Buffer.from(messageContent), {
|
|
625
|
+
correlationId,
|
|
626
|
+
replyTo: replyQueue,
|
|
627
|
+
expiration: timeout.toString(),
|
|
628
|
+
...options
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Register response handler for RPC
|
|
634
|
+
*/
|
|
635
|
+
async respond(handler) {
|
|
636
|
+
return this.subscribe(async (message, ctx) => {
|
|
637
|
+
const response = await handler(message, ctx);
|
|
638
|
+
await ctx.reply(response);
|
|
639
|
+
await ctx.ack();
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
/**
|
|
643
|
+
* Get queue statistics
|
|
644
|
+
*/
|
|
645
|
+
async stats() {
|
|
646
|
+
await this.assert();
|
|
647
|
+
const queueInfo = await this.amqpChannel.checkQueue(this.name);
|
|
648
|
+
return {
|
|
649
|
+
name: this.name,
|
|
650
|
+
messageCount: queueInfo.messageCount,
|
|
651
|
+
consumerCount: queueInfo.consumerCount
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Purge all messages
|
|
656
|
+
*/
|
|
657
|
+
async purge() {
|
|
658
|
+
await this.assert();
|
|
659
|
+
return (await this.amqpChannel.purgeQueue(this.name)).messageCount;
|
|
660
|
+
}
|
|
661
|
+
/**
|
|
662
|
+
* Check if queue exists
|
|
663
|
+
*/
|
|
664
|
+
async exists() {
|
|
665
|
+
try {
|
|
666
|
+
await this.amqpChannel.checkQueue(this.name);
|
|
667
|
+
return true;
|
|
668
|
+
} catch {
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
/**
|
|
673
|
+
* Delete the queue
|
|
674
|
+
*/
|
|
675
|
+
async delete() {
|
|
676
|
+
for (const subscription of this.subscriptions.values()) await subscription.unsubscribe();
|
|
677
|
+
this.subscriptions.clear();
|
|
678
|
+
try {
|
|
679
|
+
await this.amqpChannel.deleteQueue(this.name);
|
|
680
|
+
} catch {}
|
|
681
|
+
this.asserted = false;
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
/**
|
|
685
|
+
* RabbitMQ Subscription Implementation
|
|
686
|
+
*/
|
|
687
|
+
var RabbitMQSubscription = class {
|
|
688
|
+
constructor(id, channel, consumerTag, amqpChannel) {
|
|
689
|
+
this._isActive = true;
|
|
690
|
+
this.id = id;
|
|
691
|
+
this.channel = channel;
|
|
692
|
+
this.consumerTag = consumerTag;
|
|
693
|
+
this.amqpChannel = amqpChannel;
|
|
694
|
+
}
|
|
695
|
+
async unsubscribe() {
|
|
696
|
+
if (!this._isActive) return;
|
|
697
|
+
await this.amqpChannel.cancel(this.consumerTag);
|
|
698
|
+
this._isActive = false;
|
|
699
|
+
}
|
|
700
|
+
async pause() {
|
|
701
|
+
await this.amqpChannel.cancel(this.consumerTag);
|
|
702
|
+
}
|
|
703
|
+
async resume() {
|
|
704
|
+
throw new Error("Resume is not supported for RabbitMQ. Please create a new subscription.");
|
|
705
|
+
}
|
|
706
|
+
isActive() {
|
|
707
|
+
return this._isActive;
|
|
708
|
+
}
|
|
709
|
+
};
|
|
710
|
+
|
|
711
|
+
//#endregion
|
|
712
|
+
//#region ../../@warlock.js/herald/src/drivers/rabbitmq/rabbitmq-driver.ts
|
|
713
|
+
var rabbitmq_driver_exports = /* @__PURE__ */ __exportAll({ RabbitMQDriver: () => RabbitMQDriver });
|
|
714
|
+
/**
|
|
715
|
+
* Cached amqplib module (loaded once, reused)
|
|
716
|
+
*/
|
|
717
|
+
let amqplibModule;
|
|
718
|
+
/**
|
|
719
|
+
* Module availability flag
|
|
720
|
+
*/
|
|
721
|
+
let isModuleExists = null;
|
|
722
|
+
/**
|
|
723
|
+
* Installation instructions for amqplib
|
|
724
|
+
*/
|
|
725
|
+
const AMQPLIB_INSTALL_INSTRUCTIONS = `
|
|
726
|
+
RabbitMQ driver requires the amqplib package.
|
|
727
|
+
Install it with:
|
|
728
|
+
|
|
729
|
+
npx warlock add herald --driver=rabbitmq
|
|
730
|
+
|
|
731
|
+
Or manually:
|
|
732
|
+
|
|
733
|
+
npm install amqplib
|
|
734
|
+
pnpm add amqplib
|
|
735
|
+
yarn add amqplib
|
|
736
|
+
`.trim();
|
|
737
|
+
/**
|
|
738
|
+
* Load amqplib module
|
|
739
|
+
*/
|
|
740
|
+
async function loadAmqplibModule() {
|
|
741
|
+
try {
|
|
742
|
+
amqplibModule = await import("amqplib");
|
|
743
|
+
isModuleExists = true;
|
|
744
|
+
} catch {
|
|
745
|
+
isModuleExists = false;
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
loadAmqplibModule();
|
|
749
|
+
/**
|
|
750
|
+
* RabbitMQ Driver
|
|
751
|
+
*
|
|
752
|
+
* Implementation of BrokerDriverContract for RabbitMQ/AMQP.
|
|
753
|
+
*
|
|
754
|
+
* **Important:** This driver requires the `amqplib` package to be installed.
|
|
755
|
+
* Install it with: `npx warlock add herald --driver=rabbitmq` or `npm install amqplib`
|
|
756
|
+
*
|
|
757
|
+
* @example
|
|
758
|
+
* ```typescript
|
|
759
|
+
* const driver = new RabbitMQDriver({
|
|
760
|
+
* driver: "rabbitmq",
|
|
761
|
+
* host: "localhost",
|
|
762
|
+
* port: 5672,
|
|
763
|
+
* username: "guest",
|
|
764
|
+
* password: "guest",
|
|
765
|
+
* });
|
|
766
|
+
*
|
|
767
|
+
* await driver.connect();
|
|
768
|
+
* const channel = driver.channel("user.created");
|
|
769
|
+
* ```
|
|
770
|
+
*/
|
|
771
|
+
var RabbitMQDriver = class {
|
|
772
|
+
/**
|
|
773
|
+
* Create a new RabbitMQ driver
|
|
774
|
+
*
|
|
775
|
+
* @param options - RabbitMQ connection options
|
|
776
|
+
*/
|
|
777
|
+
constructor(options) {
|
|
778
|
+
this.name = "rabbitmq";
|
|
779
|
+
this.consumers = [];
|
|
780
|
+
this.events = new node_events.EventEmitter();
|
|
781
|
+
this.channels = /* @__PURE__ */ new Map();
|
|
782
|
+
this.connection = null;
|
|
783
|
+
this.amqpChannel = null;
|
|
784
|
+
this._isConnected = false;
|
|
785
|
+
this.options = options;
|
|
786
|
+
}
|
|
787
|
+
/**
|
|
788
|
+
* Whether connected to RabbitMQ
|
|
789
|
+
*/
|
|
790
|
+
get isConnected() {
|
|
791
|
+
return this._isConnected;
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Subscribe the given consumer class to the driver
|
|
795
|
+
*
|
|
796
|
+
* @param consumer - Consumer class to subscribe
|
|
797
|
+
*
|
|
798
|
+
* @example
|
|
799
|
+
* ```typescript
|
|
800
|
+
* driver.subscribe(UserUpdatedConsumer);
|
|
801
|
+
* ```
|
|
802
|
+
*/
|
|
803
|
+
subscribe(Consumer) {
|
|
804
|
+
if (this.isConnected) this.channel(Consumer.eventName).subscribe(prepareConsumerSubscription(Consumer, (error, eventName) => {
|
|
805
|
+
this.events.emit("error", error, eventName);
|
|
806
|
+
}), { consumerId: Consumer.consumerId });
|
|
807
|
+
else this.consumers.push(Consumer);
|
|
808
|
+
return () => {
|
|
809
|
+
this.unsubscribe(Consumer);
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Unsubscribe the given consumer
|
|
814
|
+
*/
|
|
815
|
+
unsubscribe(Consumer) {
|
|
816
|
+
if (this.isConnected) this.channel(Consumer.eventName).unsubscribeById(Consumer.consumerId);
|
|
817
|
+
const index = this.consumers.indexOf(Consumer);
|
|
818
|
+
if (index > -1) this.consumers.splice(index, 1);
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* Publish the given event message.
|
|
822
|
+
* Auto-creates the channel if it hasn't been accessed before.
|
|
823
|
+
*/
|
|
824
|
+
publish(event) {
|
|
825
|
+
this.channel(event.eventName).publish(event.serialize());
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Connect to RabbitMQ
|
|
829
|
+
*/
|
|
830
|
+
async connect() {
|
|
831
|
+
if (isModuleExists === false) throw new Error(`amqplib is not installed.\n\n${AMQPLIB_INSTALL_INSTRUCTIONS}`);
|
|
832
|
+
if (isModuleExists === null) {
|
|
833
|
+
await loadAmqplibModule();
|
|
834
|
+
if (!isModuleExists) throw new Error(`amqplib is not installed.\n\n${AMQPLIB_INSTALL_INSTRUCTIONS}`);
|
|
835
|
+
}
|
|
836
|
+
try {
|
|
837
|
+
const url = this.buildConnectionUrl();
|
|
838
|
+
const connectOptions = {
|
|
839
|
+
heartbeat: this.options.heartbeat ?? 60,
|
|
840
|
+
timeout: this.options.connectionTimeout,
|
|
841
|
+
...this.options.clientOptions
|
|
842
|
+
};
|
|
843
|
+
this.connection = await amqplibModule.connect(url, connectOptions);
|
|
844
|
+
this.amqpChannel = await this.connection.createChannel();
|
|
845
|
+
if (this.options.prefetch) await this.amqpChannel.prefetch(this.options.prefetch);
|
|
846
|
+
this._isConnected = true;
|
|
847
|
+
this.events.emit("connected");
|
|
848
|
+
for (const consumer of this.consumers) this.subscribe(consumer);
|
|
849
|
+
this.consumers.length = 0;
|
|
850
|
+
this.connection.on("close", () => {
|
|
851
|
+
this._isConnected = false;
|
|
852
|
+
this.events.emit("disconnected");
|
|
853
|
+
if (this.options.reconnect !== false) this.handleReconnect();
|
|
854
|
+
});
|
|
855
|
+
this.connection.on("error", (error) => {
|
|
856
|
+
this.events.emit("error", error);
|
|
857
|
+
});
|
|
858
|
+
} catch (error) {
|
|
859
|
+
this._isConnected = false;
|
|
860
|
+
throw new Error(`Failed to connect to RabbitMQ: ${error instanceof Error ? error.message : String(error)}`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
/**
|
|
864
|
+
* Build connection URL from options
|
|
865
|
+
*/
|
|
866
|
+
buildConnectionUrl() {
|
|
867
|
+
if (this.options.uri) return this.options.uri;
|
|
868
|
+
const protocol = "amqp";
|
|
869
|
+
const host = this.options.host ?? "localhost";
|
|
870
|
+
const port = this.options.port ?? 5672;
|
|
871
|
+
const vhost = this.options.vhost ?? "/";
|
|
872
|
+
return `${protocol}://${this.options.username ?? "guest"}:${this.options.password ?? "guest"}@${host}:${port}/${encodeURIComponent(vhost)}`;
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Handle reconnection
|
|
876
|
+
*/
|
|
877
|
+
async handleReconnect() {
|
|
878
|
+
const delay = this.options.reconnectDelay ?? 5e3;
|
|
879
|
+
let attempt = 0;
|
|
880
|
+
const tryReconnect = async () => {
|
|
881
|
+
attempt++;
|
|
882
|
+
this.events.emit("reconnecting", attempt);
|
|
883
|
+
try {
|
|
884
|
+
await this.connect();
|
|
885
|
+
} catch {
|
|
886
|
+
setTimeout(tryReconnect, delay);
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
setTimeout(tryReconnect, delay);
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* Disconnect from RabbitMQ
|
|
893
|
+
*/
|
|
894
|
+
async disconnect() {
|
|
895
|
+
if (this.amqpChannel) {
|
|
896
|
+
try {
|
|
897
|
+
await this.amqpChannel.close();
|
|
898
|
+
} catch {}
|
|
899
|
+
this.amqpChannel = null;
|
|
900
|
+
}
|
|
901
|
+
if (this.connection) {
|
|
902
|
+
try {
|
|
903
|
+
await this.connection.close();
|
|
904
|
+
} catch {}
|
|
905
|
+
this.connection = null;
|
|
906
|
+
}
|
|
907
|
+
this._isConnected = false;
|
|
908
|
+
this.events.emit("disconnected");
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Register event listener
|
|
912
|
+
*/
|
|
913
|
+
on(event, listener) {
|
|
914
|
+
this.events.on(event, listener);
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* Remove event listener
|
|
918
|
+
*/
|
|
919
|
+
off(event, listener) {
|
|
920
|
+
this.events.off(event, listener);
|
|
921
|
+
}
|
|
922
|
+
/**
|
|
923
|
+
* Get or create a channel
|
|
924
|
+
*/
|
|
925
|
+
channel(name, options) {
|
|
926
|
+
const existing = this.channels.get(name);
|
|
927
|
+
if (existing) return existing;
|
|
928
|
+
const channel = new RabbitMQChannel(name, this.amqpChannel, options);
|
|
929
|
+
this.channels.set(name, channel);
|
|
930
|
+
return channel;
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Start consuming messages
|
|
934
|
+
*/
|
|
935
|
+
async startConsuming() {}
|
|
936
|
+
/**
|
|
937
|
+
* Stop consuming messages from all subscribed channels.
|
|
938
|
+
* Gracefully cancels all active consumers.
|
|
939
|
+
*/
|
|
940
|
+
async stopConsuming() {
|
|
941
|
+
const stops = Array.from(this.channels.values()).map((channel) => channel.stopConsuming());
|
|
942
|
+
await Promise.all(stops);
|
|
943
|
+
}
|
|
944
|
+
/**
|
|
945
|
+
* Health check
|
|
946
|
+
*/
|
|
947
|
+
async healthCheck() {
|
|
948
|
+
if (!this._isConnected || !this.connection) return {
|
|
949
|
+
healthy: false,
|
|
950
|
+
error: "Not connected to RabbitMQ"
|
|
951
|
+
};
|
|
952
|
+
const start = Date.now();
|
|
953
|
+
try {
|
|
954
|
+
await this.amqpChannel.checkQueue("amq.rabbitmq.reply-to").catch(() => {});
|
|
955
|
+
return {
|
|
956
|
+
healthy: true,
|
|
957
|
+
latency: Date.now() - start
|
|
958
|
+
};
|
|
959
|
+
} catch (error) {
|
|
960
|
+
return {
|
|
961
|
+
healthy: false,
|
|
962
|
+
error: error instanceof Error ? error.message : String(error),
|
|
963
|
+
latency: Date.now() - start
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
/**
|
|
968
|
+
* Get all channel names
|
|
969
|
+
*/
|
|
970
|
+
getChannelNames() {
|
|
971
|
+
return Array.from(this.channels.keys());
|
|
972
|
+
}
|
|
973
|
+
/**
|
|
974
|
+
* Close a specific channel
|
|
975
|
+
*/
|
|
976
|
+
async closeChannel(name) {
|
|
977
|
+
const channel = this.channels.get(name);
|
|
978
|
+
if (channel) {
|
|
979
|
+
await channel.delete();
|
|
980
|
+
this.channels.delete(name);
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Get the raw AMQP channel (for advanced use)
|
|
985
|
+
*/
|
|
986
|
+
getRawChannel() {
|
|
987
|
+
return this.amqpChannel;
|
|
988
|
+
}
|
|
989
|
+
/**
|
|
990
|
+
* Get the raw connection (for advanced use)
|
|
991
|
+
*/
|
|
992
|
+
getRawConnection() {
|
|
993
|
+
return this.connection;
|
|
994
|
+
}
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
//#endregion
|
|
998
|
+
//#region ../../@warlock.js/herald/src/utils/connect-to-broker.ts
|
|
999
|
+
/**
|
|
1000
|
+
* Connect to a message broker and register it.
|
|
1001
|
+
*
|
|
1002
|
+
* This is a high-level utility function that simplifies connection setup
|
|
1003
|
+
* for most projects. It handles driver instantiation, connection,
|
|
1004
|
+
* broker creation, and automatic registration.
|
|
1005
|
+
*
|
|
1006
|
+
* **Supported Drivers:**
|
|
1007
|
+
* - `rabbitmq` (default) - RabbitMQ/AMQP driver
|
|
1008
|
+
* - `kafka` - Apache Kafka driver (coming soon)
|
|
1009
|
+
*
|
|
1010
|
+
* @param options - Connection configuration options
|
|
1011
|
+
* @returns A connected and registered Broker instance
|
|
1012
|
+
* @throws {Error} If connection fails or driver is not implemented
|
|
1013
|
+
*
|
|
1014
|
+
* @example
|
|
1015
|
+
* ```typescript
|
|
1016
|
+
* // RabbitMQ connection
|
|
1017
|
+
* const broker = await connectToBroker({
|
|
1018
|
+
* driver: "rabbitmq",
|
|
1019
|
+
* host: "localhost",
|
|
1020
|
+
* port: 5672,
|
|
1021
|
+
* username: "guest",
|
|
1022
|
+
* password: "guest",
|
|
1023
|
+
* });
|
|
1024
|
+
*
|
|
1025
|
+
* // Use the broker
|
|
1026
|
+
* await broker.channel("user.created").publish({ userId: 1 });
|
|
1027
|
+
* ```
|
|
1028
|
+
*
|
|
1029
|
+
* @example
|
|
1030
|
+
* ```typescript
|
|
1031
|
+
* // Multiple brokers
|
|
1032
|
+
* await connectToBroker({
|
|
1033
|
+
* driver: "rabbitmq",
|
|
1034
|
+
* name: "notifications",
|
|
1035
|
+
* isDefault: true,
|
|
1036
|
+
* host: process.env.RABBITMQ_HOST,
|
|
1037
|
+
* });
|
|
1038
|
+
*
|
|
1039
|
+
* await connectToBroker({
|
|
1040
|
+
* driver: "rabbitmq",
|
|
1041
|
+
* name: "analytics",
|
|
1042
|
+
* host: process.env.ANALYTICS_RABBITMQ_HOST,
|
|
1043
|
+
* });
|
|
1044
|
+
*
|
|
1045
|
+
* // Use default broker
|
|
1046
|
+
* herald().channel("notifications").publish({ ... });
|
|
1047
|
+
*
|
|
1048
|
+
* // Use specific broker
|
|
1049
|
+
* herald("analytics").channel("events").publish({ ... });
|
|
1050
|
+
* ```
|
|
1051
|
+
*/
|
|
1052
|
+
async function connectToBroker(options) {
|
|
1053
|
+
const driverType = options.driver ?? "rabbitmq";
|
|
1054
|
+
const brokerName = options.name ?? "default";
|
|
1055
|
+
const isDefault = options.isDefault ?? true;
|
|
1056
|
+
let driver;
|
|
1057
|
+
switch (driverType) {
|
|
1058
|
+
case "rabbitmq": {
|
|
1059
|
+
const rabbitOptions = options;
|
|
1060
|
+
const { RabbitMQDriver } = await Promise.resolve().then(() => rabbitmq_driver_exports);
|
|
1061
|
+
driver = new RabbitMQDriver(rabbitOptions);
|
|
1062
|
+
break;
|
|
1063
|
+
}
|
|
1064
|
+
case "kafka": throw new Error("Kafka driver is not yet implemented. Coming soon! For now, please use RabbitMQ.");
|
|
1065
|
+
default: throw new Error(`Unknown driver: "${driverType}". Supported drivers: rabbitmq, kafka`);
|
|
1066
|
+
}
|
|
1067
|
+
const broker = brokerRegistry.register({
|
|
1068
|
+
name: brokerName,
|
|
1069
|
+
driver,
|
|
1070
|
+
isDefault
|
|
1071
|
+
});
|
|
1072
|
+
try {
|
|
1073
|
+
await driver.connect();
|
|
1074
|
+
} catch (error) {
|
|
1075
|
+
throw new Error(`Failed to connect to ${driverType}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1076
|
+
}
|
|
1077
|
+
return broker;
|
|
1078
|
+
}
|
|
1079
|
+
/**
|
|
1080
|
+
* Get a broker by name or the default one.
|
|
1081
|
+
*
|
|
1082
|
+
* This is the main entry point for using brokers in your application.
|
|
1083
|
+
* Named after the package — `herald()` carries your messages!
|
|
1084
|
+
*
|
|
1085
|
+
* @param name - Optional broker name (uses default if not provided)
|
|
1086
|
+
* @returns Broker instance
|
|
1087
|
+
* @throws MissingBrokerError if broker not found
|
|
1088
|
+
*
|
|
1089
|
+
* @example
|
|
1090
|
+
* // Get default broker
|
|
1091
|
+
* const channel = herald().channel("user.created");
|
|
1092
|
+
* await channel.publish({ userId: 1 });
|
|
1093
|
+
*
|
|
1094
|
+
* // Get specific broker
|
|
1095
|
+
* const analyticsChannel = herald("analytics").channel("events");
|
|
1096
|
+
* await analyticsChannel.publish({ event: "page_view" });
|
|
1097
|
+
*
|
|
1098
|
+
* // Subscribe to messages
|
|
1099
|
+
* herald()
|
|
1100
|
+
* .channel<UserPayload>("user.created")
|
|
1101
|
+
* .subscribe(async (message, ctx) => {
|
|
1102
|
+
* console.log("User created:", message.payload);
|
|
1103
|
+
* await ctx.ack();
|
|
1104
|
+
* });
|
|
1105
|
+
* ```
|
|
1106
|
+
*/
|
|
1107
|
+
function herald(name) {
|
|
1108
|
+
return brokerRegistry.get(name);
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Get channel instance for the given name from default broker.
|
|
1112
|
+
*
|
|
1113
|
+
* Shorthand for `herald().channel(name, options)`.
|
|
1114
|
+
*
|
|
1115
|
+
* @param name - Channel name
|
|
1116
|
+
* @param options - Optional channel options
|
|
1117
|
+
* @returns Channel instance
|
|
1118
|
+
* @throws MissingBrokerError if broker not found
|
|
1119
|
+
*
|
|
1120
|
+
* @example
|
|
1121
|
+
* ```typescript
|
|
1122
|
+
* const channel = heraldChannel("user.created");
|
|
1123
|
+
* await channel.publish({ userId: 1 });
|
|
1124
|
+
* ```
|
|
1125
|
+
*/
|
|
1126
|
+
function heraldChannel(name, options) {
|
|
1127
|
+
return herald().channel(name, options);
|
|
1128
|
+
}
|
|
1129
|
+
/**
|
|
1130
|
+
* Publish an EventMessage to the default broker.
|
|
1131
|
+
*
|
|
1132
|
+
* @param event - Event message to publish
|
|
1133
|
+
* @returns Promise that resolves when the event is published
|
|
1134
|
+
* @throws Error if the broker is not connected
|
|
1135
|
+
*
|
|
1136
|
+
* @example
|
|
1137
|
+
* ```typescript
|
|
1138
|
+
* await publishEvent(new UserUpdatedEvent({ id: 1, name: "John Doe" }));
|
|
1139
|
+
* ```
|
|
1140
|
+
*/
|
|
1141
|
+
async function publishEvent(event) {
|
|
1142
|
+
return herald().publish(event);
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Subscribe an EventConsumer class to the default broker.
|
|
1146
|
+
*
|
|
1147
|
+
* @param Consumer - Event consumer class
|
|
1148
|
+
* @returns Unsubscribe function
|
|
1149
|
+
* @throws MissingBrokerError if broker not found
|
|
1150
|
+
*
|
|
1151
|
+
* @example
|
|
1152
|
+
* ```typescript
|
|
1153
|
+
* await subscribeConsumer(UserUpdatedConsumer);
|
|
1154
|
+
* ```
|
|
1155
|
+
*/
|
|
1156
|
+
async function subscribeConsumer(Consumer) {
|
|
1157
|
+
return herald().subscribe(Consumer);
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
//#endregion
|
|
1161
|
+
//#region ../../@warlock.js/herald/src/decorators/consumable.ts
|
|
1162
|
+
const pendingSubscribers = /* @__PURE__ */ new Set();
|
|
1163
|
+
/**
|
|
1164
|
+
* Register the consumer to the broker
|
|
1165
|
+
*/
|
|
1166
|
+
function Consumable(options) {
|
|
1167
|
+
return function(target) {
|
|
1168
|
+
const brokerName = options?.broker;
|
|
1169
|
+
try {
|
|
1170
|
+
const currentBroker = brokerRegistry.get(brokerName);
|
|
1171
|
+
if (currentBroker?.isConnected) currentBroker.subscribe(target);
|
|
1172
|
+
else pendingSubscribers.add({
|
|
1173
|
+
Consumer: target,
|
|
1174
|
+
options
|
|
1175
|
+
});
|
|
1176
|
+
} catch {
|
|
1177
|
+
pendingSubscribers.add({
|
|
1178
|
+
Consumer: target,
|
|
1179
|
+
options
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
};
|
|
1183
|
+
}
|
|
1184
|
+
brokerRegistry.on("connected", (broker) => {
|
|
1185
|
+
for (const { Consumer, options } of pendingSubscribers) {
|
|
1186
|
+
if (options?.broker && broker.name !== options.broker) continue;
|
|
1187
|
+
broker.subscribe(Consumer);
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
//#endregion
|
|
1192
|
+
//#region ../../@warlock.js/herald/src/message-managers/event-consumer.ts
|
|
1193
|
+
/**
|
|
1194
|
+
* This class is used to be part of the Herald Event Consumer Manager.
|
|
1195
|
+
* It should be used to consume events from Either RabbitMQ or Kafka through Herald
|
|
1196
|
+
*
|
|
1197
|
+
* It's highly recommended using it instead of declaring manual channel namd and subscribing to event
|
|
1198
|
+
*/
|
|
1199
|
+
var EventConsumer = class {
|
|
1200
|
+
static get consumerId() {
|
|
1201
|
+
if (!this._consumerId) this._consumerId = (0, crypto.randomUUID)();
|
|
1202
|
+
return this._consumerId;
|
|
1203
|
+
}
|
|
1204
|
+
get eventName() {
|
|
1205
|
+
return this.constructor.eventName;
|
|
1206
|
+
}
|
|
1207
|
+
/**
|
|
1208
|
+
* Determine whether this is accepted version to be used by this consumer
|
|
1209
|
+
*/
|
|
1210
|
+
static isAcceptedVersion(version) {
|
|
1211
|
+
if (this.minVersion && version < this.minVersion) return false;
|
|
1212
|
+
if (this.maxVersion && version > this.maxVersion) return false;
|
|
1213
|
+
return true;
|
|
1214
|
+
}
|
|
1215
|
+
/**
|
|
1216
|
+
* Validate the given data
|
|
1217
|
+
*/
|
|
1218
|
+
async validate(data) {
|
|
1219
|
+
if (!this.schema) return;
|
|
1220
|
+
return await _warlock_js_seal.v.validate(this.schema, data);
|
|
1221
|
+
}
|
|
1222
|
+
};
|
|
1223
|
+
/**
|
|
1224
|
+
* A shorthand to define an event consumer without declaring an entire class
|
|
1225
|
+
*/
|
|
1226
|
+
function defineConsumer(eventName, options) {
|
|
1227
|
+
const Class = class AnnouncedConsumer extends EventConsumer {
|
|
1228
|
+
constructor(..._args) {
|
|
1229
|
+
super(..._args);
|
|
1230
|
+
this.schema = options.schema;
|
|
1231
|
+
}
|
|
1232
|
+
static {
|
|
1233
|
+
this.eventName = eventName;
|
|
1234
|
+
}
|
|
1235
|
+
async handle(payload, event) {
|
|
1236
|
+
if (options.validate) {
|
|
1237
|
+
const result = await options.validate(payload);
|
|
1238
|
+
if (!result || !result.isValid) return;
|
|
1239
|
+
}
|
|
1240
|
+
return options.handle(payload, event);
|
|
1241
|
+
}
|
|
1242
|
+
};
|
|
1243
|
+
Consumable()(Class);
|
|
1244
|
+
return Class;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
//#endregion
|
|
1248
|
+
//#region ../../@warlock.js/herald/src/message-managers/event-message.ts
|
|
1249
|
+
var EventMessage = class {
|
|
1250
|
+
/**
|
|
1251
|
+
* Data that will be sent with the event (Payload)
|
|
1252
|
+
*/
|
|
1253
|
+
toJSON() {
|
|
1254
|
+
if (!this.data) throw new Error(`no Data is defined for Event: ${this.eventName}`);
|
|
1255
|
+
return this.data;
|
|
1256
|
+
}
|
|
1257
|
+
constructor(data) {
|
|
1258
|
+
this.data = data;
|
|
1259
|
+
}
|
|
1260
|
+
/**
|
|
1261
|
+
* Serialize the event to be ready for publishing.
|
|
1262
|
+
* Delegates payload resolution to toJSON() — override toJSON() to customize.
|
|
1263
|
+
*
|
|
1264
|
+
* @throws Error if toJSON() throws (e.g. no data provided)
|
|
1265
|
+
*/
|
|
1266
|
+
serialize() {
|
|
1267
|
+
return {
|
|
1268
|
+
payload: this.toJSON(),
|
|
1269
|
+
metadata: this.metadata,
|
|
1270
|
+
messageId: this.messageId ?? (0, crypto.randomUUID)(),
|
|
1271
|
+
eventName: this.eventName,
|
|
1272
|
+
version: this.version,
|
|
1273
|
+
occurredAt: /* @__PURE__ */ new Date(),
|
|
1274
|
+
__through: "EventMessage"
|
|
1275
|
+
};
|
|
1276
|
+
}
|
|
1277
|
+
};
|
|
1278
|
+
/**
|
|
1279
|
+
* A shorthand to define an event without declaring an entire class.
|
|
1280
|
+
*
|
|
1281
|
+
* This factory function creates an EventMessage subclass that transforms
|
|
1282
|
+
* input data (IncomingData) into a different output format (OutgoingData).
|
|
1283
|
+
*
|
|
1284
|
+
* @template IncomingData - The type of data passed to the constructor
|
|
1285
|
+
* @template OutgoingData - The type of data returned by toJSON()
|
|
1286
|
+
*
|
|
1287
|
+
* @example
|
|
1288
|
+
* ```typescript
|
|
1289
|
+
* const UserCreatedEvent = defineEvent<User, { id: number; name: string }>(
|
|
1290
|
+
* "user.created",
|
|
1291
|
+
* { toJSON: (user) => user.only(["id", "name"]) }
|
|
1292
|
+
* );
|
|
1293
|
+
*
|
|
1294
|
+
* publishEvent(new UserCreatedEvent(user));
|
|
1295
|
+
* ```
|
|
1296
|
+
*/
|
|
1297
|
+
function defineEvent(eventName, options = {}) {
|
|
1298
|
+
return class AnnouncedEvent extends EventMessage {
|
|
1299
|
+
constructor(data) {
|
|
1300
|
+
super(data);
|
|
1301
|
+
this.eventName = eventName;
|
|
1302
|
+
this.schema = options.schema;
|
|
1303
|
+
}
|
|
1304
|
+
toJSON() {
|
|
1305
|
+
if (!options.toJSON) return this.data;
|
|
1306
|
+
return options.toJSON(this.data);
|
|
1307
|
+
}
|
|
1308
|
+
};
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
//#endregion
|
|
1312
|
+
//#region ../../@warlock.js/herald/src/use-case-broadcast.ts
|
|
1313
|
+
/**
|
|
1314
|
+
* Channel adapter that publishes use-case broadcast events onto a herald broker.
|
|
1315
|
+
*
|
|
1316
|
+
* Register it in the use-cases config so successful use cases fan out to the bus:
|
|
1317
|
+
*
|
|
1318
|
+
* @example
|
|
1319
|
+
* // src/config/use-cases.ts
|
|
1320
|
+
* import { heraldBroadcast } from "@warlock.js/herald";
|
|
1321
|
+
*
|
|
1322
|
+
* export default {
|
|
1323
|
+
* broadcast: {
|
|
1324
|
+
* enabled: true,
|
|
1325
|
+
* channels: [heraldBroadcast({ broker: "default" })],
|
|
1326
|
+
* },
|
|
1327
|
+
* } satisfies UseCaseConfigurations;
|
|
1328
|
+
*
|
|
1329
|
+
* @param options - Optional broker name (defaults to the default broker)
|
|
1330
|
+
*/
|
|
1331
|
+
function heraldBroadcast(options) {
|
|
1332
|
+
return { async broadcast(event) {
|
|
1333
|
+
await herald(options?.broker).channel(event.event).publish(event.payload);
|
|
1334
|
+
} };
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
//#endregion
|
|
1338
|
+
exports.Broker = Broker;
|
|
1339
|
+
exports.Consumable = Consumable;
|
|
1340
|
+
exports.EventConsumer = EventConsumer;
|
|
1341
|
+
exports.EventMessage = EventMessage;
|
|
1342
|
+
exports.MissingBrokerError = MissingBrokerError;
|
|
1343
|
+
exports.RabbitMQChannel = RabbitMQChannel;
|
|
1344
|
+
exports.RabbitMQDriver = RabbitMQDriver;
|
|
1345
|
+
exports.brokerRegistry = brokerRegistry;
|
|
1346
|
+
exports.connectToBroker = connectToBroker;
|
|
1347
|
+
exports.defineConsumer = defineConsumer;
|
|
1348
|
+
exports.defineEvent = defineEvent;
|
|
1349
|
+
exports.herald = herald;
|
|
1350
|
+
exports.heraldBroadcast = heraldBroadcast;
|
|
1351
|
+
exports.heraldChannel = heraldChannel;
|
|
1352
|
+
exports.pendingSubscribers = pendingSubscribers;
|
|
1353
|
+
exports.publishEvent = publishEvent;
|
|
1354
|
+
exports.subscribeConsumer = subscribeConsumer;
|
|
1355
|
+
//# sourceMappingURL=index.cjs.map
|