@warlock.js/herald 4.0.174 → 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.
Files changed (236) hide show
  1. package/README.md +36 -33
  2. package/cjs/index.cjs +1355 -0
  3. package/cjs/index.cjs.map +1 -0
  4. package/esm/communicators/broker-registry.d.mts +157 -0
  5. package/esm/communicators/broker-registry.d.mts.map +1 -0
  6. package/esm/communicators/broker-registry.mjs +204 -0
  7. package/esm/communicators/broker-registry.mjs.map +1 -0
  8. package/esm/communicators/broker.d.mts +104 -0
  9. package/esm/communicators/broker.d.mts.map +1 -0
  10. package/esm/communicators/broker.mjs +104 -0
  11. package/esm/communicators/broker.mjs.map +1 -0
  12. package/esm/communicators/index.d.mts +2 -0
  13. package/esm/communicators/index.mjs +4 -0
  14. package/esm/contracts/broker-driver.contract.d.mts +200 -0
  15. package/esm/contracts/broker-driver.contract.d.mts.map +1 -0
  16. package/esm/contracts/channel.contract.d.mts +197 -0
  17. package/esm/contracts/channel.contract.d.mts.map +1 -0
  18. package/esm/contracts/index.d.mts +2 -0
  19. package/esm/decorators/consumable.d.mts +17 -0
  20. package/esm/decorators/consumable.d.mts.map +1 -0
  21. package/esm/decorators/consumable.mjs +36 -0
  22. package/esm/decorators/consumable.mjs.map +1 -0
  23. package/esm/decorators/index.d.mts +1 -0
  24. package/esm/decorators/index.mjs +3 -0
  25. package/esm/drivers/index.d.mts +2 -0
  26. package/esm/drivers/index.mjs +5 -0
  27. package/esm/drivers/rabbitmq/index.d.mts +2 -0
  28. package/esm/drivers/rabbitmq/index.mjs +4 -0
  29. package/esm/drivers/rabbitmq/rabbitmq-channel.d.mts +85 -0
  30. package/esm/drivers/rabbitmq/rabbitmq-channel.d.mts.map +1 -0
  31. package/esm/drivers/rabbitmq/rabbitmq-channel.mjs +352 -0
  32. package/esm/drivers/rabbitmq/rabbitmq-channel.mjs.map +1 -0
  33. package/esm/drivers/rabbitmq/rabbitmq-driver.d.mts +130 -0
  34. package/esm/drivers/rabbitmq/rabbitmq-driver.d.mts.map +1 -0
  35. package/esm/drivers/rabbitmq/rabbitmq-driver.mjs +291 -0
  36. package/esm/drivers/rabbitmq/rabbitmq-driver.mjs.map +1 -0
  37. package/esm/index.d.mts +20 -0
  38. package/esm/index.mjs +16 -0
  39. package/esm/message-managers/event-consumer.d.mts +61 -0
  40. package/esm/message-managers/event-consumer.d.mts.map +1 -0
  41. package/esm/message-managers/event-consumer.mjs +63 -0
  42. package/esm/message-managers/event-consumer.mjs.map +1 -0
  43. package/esm/message-managers/event-message.d.mts +87 -0
  44. package/esm/message-managers/event-message.d.mts.map +1 -0
  45. package/esm/message-managers/event-message.mjs +68 -0
  46. package/esm/message-managers/event-message.mjs.map +1 -0
  47. package/esm/message-managers/index.d.mts +3 -0
  48. package/esm/message-managers/index.mjs +4 -0
  49. package/esm/message-managers/prepare-consumer-subscription.mjs +42 -0
  50. package/esm/message-managers/prepare-consumer-subscription.mjs.map +1 -0
  51. package/esm/message-managers/types.d.mts +23 -0
  52. package/esm/message-managers/types.d.mts.map +1 -0
  53. package/esm/types/channel.types.d.mts +23 -0
  54. package/esm/types/channel.types.d.mts.map +1 -0
  55. package/esm/types/connection.types.d.mts +67 -0
  56. package/esm/types/connection.types.d.mts.map +1 -0
  57. package/esm/types/driver.types.d.mts +13 -0
  58. package/esm/types/driver.types.d.mts.map +1 -0
  59. package/esm/types/index.d.mts +7 -0
  60. package/esm/types/message.types.d.mts +37 -0
  61. package/esm/types/message.types.d.mts.map +1 -0
  62. package/esm/types/publish.types.d.mts +16 -0
  63. package/esm/types/publish.types.d.mts.map +1 -0
  64. package/esm/types/registry.types.d.mts +8 -0
  65. package/esm/types/registry.types.d.mts.map +1 -0
  66. package/esm/types/subscribe.types.d.mts +21 -0
  67. package/esm/types/subscribe.types.d.mts.map +1 -0
  68. package/esm/use-case-broadcast.d.mts +41 -0
  69. package/esm/use-case-broadcast.d.mts.map +1 -0
  70. package/esm/use-case-broadcast.mjs +30 -0
  71. package/esm/use-case-broadcast.mjs.map +1 -0
  72. package/esm/utils/{connect-to-broker.d.ts → connect-to-broker.d.mts} +15 -10
  73. package/esm/utils/connect-to-broker.d.mts.map +1 -0
  74. package/esm/utils/connect-to-broker.mjs +168 -0
  75. package/esm/utils/connect-to-broker.mjs.map +1 -0
  76. package/esm/utils/index.d.mts +1 -0
  77. package/esm/utils/index.mjs +3 -0
  78. package/llms-full.txt +581 -0
  79. package/llms.txt +12 -0
  80. package/package.json +46 -46
  81. package/skills/README.md +21 -0
  82. package/skills/consume-message/SKILL.md +217 -0
  83. package/skills/herald-basics/SKILL.md +96 -0
  84. package/skills/publish-message/SKILL.md +172 -0
  85. package/skills/request-and-respond/SKILL.md +74 -0
  86. package/cjs/communicators/broker-registry.d.ts +0 -155
  87. package/cjs/communicators/broker-registry.d.ts.map +0 -1
  88. package/cjs/communicators/broker-registry.js +0 -206
  89. package/cjs/communicators/broker-registry.js.map +0 -1
  90. package/cjs/communicators/broker.d.ts +0 -100
  91. package/cjs/communicators/broker.d.ts.map +0 -1
  92. package/cjs/communicators/broker.js +0 -105
  93. package/cjs/communicators/broker.js.map +0 -1
  94. package/cjs/communicators/index.d.ts +0 -3
  95. package/cjs/communicators/index.d.ts.map +0 -1
  96. package/cjs/contracts/broker-driver.contract.d.ts +0 -196
  97. package/cjs/contracts/broker-driver.contract.d.ts.map +0 -1
  98. package/cjs/contracts/channel.contract.d.ts +0 -191
  99. package/cjs/contracts/channel.contract.d.ts.map +0 -1
  100. package/cjs/contracts/index.d.ts +0 -3
  101. package/cjs/contracts/index.d.ts.map +0 -1
  102. package/cjs/decorators/consumable.d.ts +0 -13
  103. package/cjs/decorators/consumable.d.ts.map +0 -1
  104. package/cjs/decorators/consumable.js +0 -33
  105. package/cjs/decorators/consumable.js.map +0 -1
  106. package/cjs/decorators/index.d.ts +0 -2
  107. package/cjs/decorators/index.d.ts.map +0 -1
  108. package/cjs/drivers/index.d.ts +0 -2
  109. package/cjs/drivers/index.d.ts.map +0 -1
  110. package/cjs/drivers/rabbitmq/index.d.ts +0 -3
  111. package/cjs/drivers/rabbitmq/index.d.ts.map +0 -1
  112. package/cjs/drivers/rabbitmq/rabbitmq-channel.d.ts +0 -79
  113. package/cjs/drivers/rabbitmq/rabbitmq-channel.d.ts.map +0 -1
  114. package/cjs/drivers/rabbitmq/rabbitmq-channel.js +0 -414
  115. package/cjs/drivers/rabbitmq/rabbitmq-channel.js.map +0 -1
  116. package/cjs/drivers/rabbitmq/rabbitmq-driver.d.ts +0 -124
  117. package/cjs/drivers/rabbitmq/rabbitmq-driver.d.ts.map +0 -1
  118. package/cjs/drivers/rabbitmq/rabbitmq-driver.js +0 -347
  119. package/cjs/drivers/rabbitmq/rabbitmq-driver.js.map +0 -1
  120. package/cjs/index.d.ts +0 -47
  121. package/cjs/index.d.ts.map +0 -1
  122. package/cjs/index.js +0 -1
  123. package/cjs/index.js.map +0 -1
  124. package/cjs/message-managers/event-consumer.d.ts +0 -64
  125. package/cjs/message-managers/event-consumer.d.ts.map +0 -1
  126. package/cjs/message-managers/event-consumer.js +0 -71
  127. package/cjs/message-managers/event-consumer.js.map +0 -1
  128. package/cjs/message-managers/event-message.d.ts +0 -90
  129. package/cjs/message-managers/event-message.d.ts.map +0 -1
  130. package/cjs/message-managers/event-message.js +0 -84
  131. package/cjs/message-managers/event-message.js.map +0 -1
  132. package/cjs/message-managers/index.d.ts +0 -4
  133. package/cjs/message-managers/index.d.ts.map +0 -1
  134. package/cjs/message-managers/prepare-consumer-subscription.d.ts +0 -4
  135. package/cjs/message-managers/prepare-consumer-subscription.d.ts.map +0 -1
  136. package/cjs/message-managers/prepare-consumer-subscription.js +0 -40
  137. package/cjs/message-managers/prepare-consumer-subscription.js.map +0 -1
  138. package/cjs/message-managers/types.d.ts +0 -20
  139. package/cjs/message-managers/types.d.ts.map +0 -1
  140. package/cjs/types/channel.types.d.ts +0 -19
  141. package/cjs/types/channel.types.d.ts.map +0 -1
  142. package/cjs/types/connection.types.d.ts +0 -65
  143. package/cjs/types/connection.types.d.ts.map +0 -1
  144. package/cjs/types/driver.types.d.ts +0 -10
  145. package/cjs/types/driver.types.d.ts.map +0 -1
  146. package/cjs/types/index.d.ts +0 -8
  147. package/cjs/types/index.d.ts.map +0 -1
  148. package/cjs/types/message.types.d.ts +0 -34
  149. package/cjs/types/message.types.d.ts.map +0 -1
  150. package/cjs/types/publish.types.d.ts +0 -13
  151. package/cjs/types/publish.types.d.ts.map +0 -1
  152. package/cjs/types/registry.types.d.ts +0 -4
  153. package/cjs/types/registry.types.d.ts.map +0 -1
  154. package/cjs/types/subscribe.types.d.ts +0 -18
  155. package/cjs/types/subscribe.types.d.ts.map +0 -1
  156. package/cjs/utils/connect-to-broker.d.ts +0 -131
  157. package/cjs/utils/connect-to-broker.d.ts.map +0 -1
  158. package/cjs/utils/connect-to-broker.js +0 -171
  159. package/cjs/utils/connect-to-broker.js.map +0 -1
  160. package/cjs/utils/index.d.ts +0 -2
  161. package/cjs/utils/index.d.ts.map +0 -1
  162. package/esm/communicators/broker-registry.d.ts +0 -155
  163. package/esm/communicators/broker-registry.d.ts.map +0 -1
  164. package/esm/communicators/broker-registry.js +0 -206
  165. package/esm/communicators/broker-registry.js.map +0 -1
  166. package/esm/communicators/broker.d.ts +0 -100
  167. package/esm/communicators/broker.d.ts.map +0 -1
  168. package/esm/communicators/broker.js +0 -105
  169. package/esm/communicators/broker.js.map +0 -1
  170. package/esm/communicators/index.d.ts +0 -3
  171. package/esm/communicators/index.d.ts.map +0 -1
  172. package/esm/contracts/broker-driver.contract.d.ts +0 -196
  173. package/esm/contracts/broker-driver.contract.d.ts.map +0 -1
  174. package/esm/contracts/channel.contract.d.ts +0 -191
  175. package/esm/contracts/channel.contract.d.ts.map +0 -1
  176. package/esm/contracts/index.d.ts +0 -3
  177. package/esm/contracts/index.d.ts.map +0 -1
  178. package/esm/decorators/consumable.d.ts +0 -13
  179. package/esm/decorators/consumable.d.ts.map +0 -1
  180. package/esm/decorators/consumable.js +0 -33
  181. package/esm/decorators/consumable.js.map +0 -1
  182. package/esm/decorators/index.d.ts +0 -2
  183. package/esm/decorators/index.d.ts.map +0 -1
  184. package/esm/drivers/index.d.ts +0 -2
  185. package/esm/drivers/index.d.ts.map +0 -1
  186. package/esm/drivers/rabbitmq/index.d.ts +0 -3
  187. package/esm/drivers/rabbitmq/index.d.ts.map +0 -1
  188. package/esm/drivers/rabbitmq/rabbitmq-channel.d.ts +0 -79
  189. package/esm/drivers/rabbitmq/rabbitmq-channel.d.ts.map +0 -1
  190. package/esm/drivers/rabbitmq/rabbitmq-channel.js +0 -414
  191. package/esm/drivers/rabbitmq/rabbitmq-channel.js.map +0 -1
  192. package/esm/drivers/rabbitmq/rabbitmq-driver.d.ts +0 -124
  193. package/esm/drivers/rabbitmq/rabbitmq-driver.d.ts.map +0 -1
  194. package/esm/drivers/rabbitmq/rabbitmq-driver.js +0 -347
  195. package/esm/drivers/rabbitmq/rabbitmq-driver.js.map +0 -1
  196. package/esm/index.d.ts +0 -47
  197. package/esm/index.d.ts.map +0 -1
  198. package/esm/index.js +0 -1
  199. package/esm/index.js.map +0 -1
  200. package/esm/message-managers/event-consumer.d.ts +0 -64
  201. package/esm/message-managers/event-consumer.d.ts.map +0 -1
  202. package/esm/message-managers/event-consumer.js +0 -71
  203. package/esm/message-managers/event-consumer.js.map +0 -1
  204. package/esm/message-managers/event-message.d.ts +0 -90
  205. package/esm/message-managers/event-message.d.ts.map +0 -1
  206. package/esm/message-managers/event-message.js +0 -84
  207. package/esm/message-managers/event-message.js.map +0 -1
  208. package/esm/message-managers/index.d.ts +0 -4
  209. package/esm/message-managers/index.d.ts.map +0 -1
  210. package/esm/message-managers/prepare-consumer-subscription.d.ts +0 -4
  211. package/esm/message-managers/prepare-consumer-subscription.d.ts.map +0 -1
  212. package/esm/message-managers/prepare-consumer-subscription.js +0 -40
  213. package/esm/message-managers/prepare-consumer-subscription.js.map +0 -1
  214. package/esm/message-managers/types.d.ts +0 -20
  215. package/esm/message-managers/types.d.ts.map +0 -1
  216. package/esm/types/channel.types.d.ts +0 -19
  217. package/esm/types/channel.types.d.ts.map +0 -1
  218. package/esm/types/connection.types.d.ts +0 -65
  219. package/esm/types/connection.types.d.ts.map +0 -1
  220. package/esm/types/driver.types.d.ts +0 -10
  221. package/esm/types/driver.types.d.ts.map +0 -1
  222. package/esm/types/index.d.ts +0 -8
  223. package/esm/types/index.d.ts.map +0 -1
  224. package/esm/types/message.types.d.ts +0 -34
  225. package/esm/types/message.types.d.ts.map +0 -1
  226. package/esm/types/publish.types.d.ts +0 -13
  227. package/esm/types/publish.types.d.ts.map +0 -1
  228. package/esm/types/registry.types.d.ts +0 -4
  229. package/esm/types/registry.types.d.ts.map +0 -1
  230. package/esm/types/subscribe.types.d.ts +0 -18
  231. package/esm/types/subscribe.types.d.ts.map +0 -1
  232. package/esm/utils/connect-to-broker.d.ts.map +0 -1
  233. package/esm/utils/connect-to-broker.js +0 -171
  234. package/esm/utils/connect-to-broker.js.map +0 -1
  235. package/esm/utils/index.d.ts +0 -2
  236. 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