@warlock.js/herald 4.0.174 → 4.1.2

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
@@ -0,0 +1,291 @@
1
+ import { prepareConsumerSubscription } from "../../message-managers/prepare-consumer-subscription.mjs";
2
+ import { RabbitMQChannel } from "./rabbitmq-channel.mjs";
3
+ import { EventEmitter } from "node:events";
4
+
5
+ //#region ../../@warlock.js/herald/src/drivers/rabbitmq/rabbitmq-driver.ts
6
+ /**
7
+ * Cached amqplib module (loaded once, reused)
8
+ */
9
+ let amqplibModule;
10
+ /**
11
+ * Module availability flag
12
+ */
13
+ let isModuleExists = null;
14
+ /**
15
+ * Installation instructions for amqplib
16
+ */
17
+ const AMQPLIB_INSTALL_INSTRUCTIONS = `
18
+ RabbitMQ driver requires the amqplib package.
19
+ Install it with:
20
+
21
+ npx warlock add herald --driver=rabbitmq
22
+
23
+ Or manually:
24
+
25
+ npm install amqplib
26
+ pnpm add amqplib
27
+ yarn add amqplib
28
+ `.trim();
29
+ /**
30
+ * Load amqplib module
31
+ */
32
+ async function loadAmqplibModule() {
33
+ try {
34
+ amqplibModule = await import("amqplib");
35
+ isModuleExists = true;
36
+ } catch {
37
+ isModuleExists = false;
38
+ }
39
+ }
40
+ loadAmqplibModule();
41
+ /**
42
+ * RabbitMQ Driver
43
+ *
44
+ * Implementation of BrokerDriverContract for RabbitMQ/AMQP.
45
+ *
46
+ * **Important:** This driver requires the `amqplib` package to be installed.
47
+ * Install it with: `npx warlock add herald --driver=rabbitmq` or `npm install amqplib`
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const driver = new RabbitMQDriver({
52
+ * driver: "rabbitmq",
53
+ * host: "localhost",
54
+ * port: 5672,
55
+ * username: "guest",
56
+ * password: "guest",
57
+ * });
58
+ *
59
+ * await driver.connect();
60
+ * const channel = driver.channel("user.created");
61
+ * ```
62
+ */
63
+ var RabbitMQDriver = class {
64
+ /**
65
+ * Create a new RabbitMQ driver
66
+ *
67
+ * @param options - RabbitMQ connection options
68
+ */
69
+ constructor(options) {
70
+ this.name = "rabbitmq";
71
+ this.consumers = [];
72
+ this.events = new EventEmitter();
73
+ this.channels = /* @__PURE__ */ new Map();
74
+ this.connection = null;
75
+ this.amqpChannel = null;
76
+ this._isConnected = false;
77
+ this.options = options;
78
+ }
79
+ /**
80
+ * Whether connected to RabbitMQ
81
+ */
82
+ get isConnected() {
83
+ return this._isConnected;
84
+ }
85
+ /**
86
+ * Subscribe the given consumer class to the driver
87
+ *
88
+ * @param consumer - Consumer class to subscribe
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * driver.subscribe(UserUpdatedConsumer);
93
+ * ```
94
+ */
95
+ subscribe(Consumer) {
96
+ if (this.isConnected) this.channel(Consumer.eventName).subscribe(prepareConsumerSubscription(Consumer, (error, eventName) => {
97
+ this.events.emit("error", error, eventName);
98
+ }), { consumerId: Consumer.consumerId });
99
+ else this.consumers.push(Consumer);
100
+ return () => {
101
+ this.unsubscribe(Consumer);
102
+ };
103
+ }
104
+ /**
105
+ * Unsubscribe the given consumer
106
+ */
107
+ unsubscribe(Consumer) {
108
+ if (this.isConnected) this.channel(Consumer.eventName).unsubscribeById(Consumer.consumerId);
109
+ const index = this.consumers.indexOf(Consumer);
110
+ if (index > -1) this.consumers.splice(index, 1);
111
+ }
112
+ /**
113
+ * Publish the given event message.
114
+ * Auto-creates the channel if it hasn't been accessed before.
115
+ */
116
+ publish(event) {
117
+ this.channel(event.eventName).publish(event.serialize());
118
+ }
119
+ /**
120
+ * Connect to RabbitMQ
121
+ */
122
+ async connect() {
123
+ if (isModuleExists === false) throw new Error(`amqplib is not installed.\n\n${AMQPLIB_INSTALL_INSTRUCTIONS}`);
124
+ if (isModuleExists === null) {
125
+ await loadAmqplibModule();
126
+ if (!isModuleExists) throw new Error(`amqplib is not installed.\n\n${AMQPLIB_INSTALL_INSTRUCTIONS}`);
127
+ }
128
+ try {
129
+ const url = this.buildConnectionUrl();
130
+ const connectOptions = {
131
+ heartbeat: this.options.heartbeat ?? 60,
132
+ timeout: this.options.connectionTimeout,
133
+ ...this.options.clientOptions
134
+ };
135
+ this.connection = await amqplibModule.connect(url, connectOptions);
136
+ this.amqpChannel = await this.connection.createChannel();
137
+ if (this.options.prefetch) await this.amqpChannel.prefetch(this.options.prefetch);
138
+ this._isConnected = true;
139
+ this.events.emit("connected");
140
+ for (const consumer of this.consumers) this.subscribe(consumer);
141
+ this.consumers.length = 0;
142
+ this.connection.on("close", () => {
143
+ this._isConnected = false;
144
+ this.events.emit("disconnected");
145
+ if (this.options.reconnect !== false) this.handleReconnect();
146
+ });
147
+ this.connection.on("error", (error) => {
148
+ this.events.emit("error", error);
149
+ });
150
+ } catch (error) {
151
+ this._isConnected = false;
152
+ throw new Error(`Failed to connect to RabbitMQ: ${error instanceof Error ? error.message : String(error)}`);
153
+ }
154
+ }
155
+ /**
156
+ * Build connection URL from options
157
+ */
158
+ buildConnectionUrl() {
159
+ if (this.options.uri) return this.options.uri;
160
+ const protocol = "amqp";
161
+ const host = this.options.host ?? "localhost";
162
+ const port = this.options.port ?? 5672;
163
+ const vhost = this.options.vhost ?? "/";
164
+ return `${protocol}://${this.options.username ?? "guest"}:${this.options.password ?? "guest"}@${host}:${port}/${encodeURIComponent(vhost)}`;
165
+ }
166
+ /**
167
+ * Handle reconnection
168
+ */
169
+ async handleReconnect() {
170
+ const delay = this.options.reconnectDelay ?? 5e3;
171
+ let attempt = 0;
172
+ const tryReconnect = async () => {
173
+ attempt++;
174
+ this.events.emit("reconnecting", attempt);
175
+ try {
176
+ await this.connect();
177
+ } catch {
178
+ setTimeout(tryReconnect, delay);
179
+ }
180
+ };
181
+ setTimeout(tryReconnect, delay);
182
+ }
183
+ /**
184
+ * Disconnect from RabbitMQ
185
+ */
186
+ async disconnect() {
187
+ if (this.amqpChannel) {
188
+ try {
189
+ await this.amqpChannel.close();
190
+ } catch {}
191
+ this.amqpChannel = null;
192
+ }
193
+ if (this.connection) {
194
+ try {
195
+ await this.connection.close();
196
+ } catch {}
197
+ this.connection = null;
198
+ }
199
+ this._isConnected = false;
200
+ this.events.emit("disconnected");
201
+ }
202
+ /**
203
+ * Register event listener
204
+ */
205
+ on(event, listener) {
206
+ this.events.on(event, listener);
207
+ }
208
+ /**
209
+ * Remove event listener
210
+ */
211
+ off(event, listener) {
212
+ this.events.off(event, listener);
213
+ }
214
+ /**
215
+ * Get or create a channel
216
+ */
217
+ channel(name, options) {
218
+ const existing = this.channels.get(name);
219
+ if (existing) return existing;
220
+ const channel = new RabbitMQChannel(name, this.amqpChannel, options);
221
+ this.channels.set(name, channel);
222
+ return channel;
223
+ }
224
+ /**
225
+ * Start consuming messages
226
+ */
227
+ async startConsuming() {}
228
+ /**
229
+ * Stop consuming messages from all subscribed channels.
230
+ * Gracefully cancels all active consumers.
231
+ */
232
+ async stopConsuming() {
233
+ const stops = Array.from(this.channels.values()).map((channel) => channel.stopConsuming());
234
+ await Promise.all(stops);
235
+ }
236
+ /**
237
+ * Health check
238
+ */
239
+ async healthCheck() {
240
+ if (!this._isConnected || !this.connection) return {
241
+ healthy: false,
242
+ error: "Not connected to RabbitMQ"
243
+ };
244
+ const start = Date.now();
245
+ try {
246
+ await this.amqpChannel.checkQueue("amq.rabbitmq.reply-to").catch(() => {});
247
+ return {
248
+ healthy: true,
249
+ latency: Date.now() - start
250
+ };
251
+ } catch (error) {
252
+ return {
253
+ healthy: false,
254
+ error: error instanceof Error ? error.message : String(error),
255
+ latency: Date.now() - start
256
+ };
257
+ }
258
+ }
259
+ /**
260
+ * Get all channel names
261
+ */
262
+ getChannelNames() {
263
+ return Array.from(this.channels.keys());
264
+ }
265
+ /**
266
+ * Close a specific channel
267
+ */
268
+ async closeChannel(name) {
269
+ const channel = this.channels.get(name);
270
+ if (channel) {
271
+ await channel.delete();
272
+ this.channels.delete(name);
273
+ }
274
+ }
275
+ /**
276
+ * Get the raw AMQP channel (for advanced use)
277
+ */
278
+ getRawChannel() {
279
+ return this.amqpChannel;
280
+ }
281
+ /**
282
+ * Get the raw connection (for advanced use)
283
+ */
284
+ getRawConnection() {
285
+ return this.connection;
286
+ }
287
+ };
288
+
289
+ //#endregion
290
+ export { RabbitMQDriver };
291
+ //# sourceMappingURL=rabbitmq-driver.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rabbitmq-driver.mjs","names":[],"sources":["../../../../../../../@warlock.js/herald/src/drivers/rabbitmq/rabbitmq-driver.ts"],"sourcesContent":["import { EventEmitter } from \"node:events\";\r\nimport type { BrokerDriverContract, ChannelContract } from \"../../contracts\";\r\nimport { EventMessage } from \"../../message-managers/event-message\";\r\nimport { EventConsumerClass } from \"../../message-managers/types\";\r\nimport type {\r\n BrokerDriverType,\r\n BrokerEvent,\r\n BrokerEventListener,\r\n ChannelOptions,\r\n HealthCheckResult,\r\n RabbitMQConnectionOptions,\r\n} from \"../../types\";\r\nimport { prepareConsumerSubscription } from \"./../../message-managers/prepare-consumer-subscription\";\r\nimport { RabbitMQChannel } from \"./rabbitmq-channel\";\r\n\r\n// ============================================================\r\n// Lazy-loaded amqplib Module\r\n// ============================================================\r\n\r\n/**\r\n * Cached amqplib module (loaded once, reused)\r\n */\r\nlet amqplibModule: typeof import(\"amqplib\");\r\n\r\n/**\r\n * Module availability flag\r\n */\r\nlet isModuleExists: boolean | null = null;\r\n\r\n/**\r\n * Installation instructions for amqplib\r\n */\r\nconst AMQPLIB_INSTALL_INSTRUCTIONS = `\r\nRabbitMQ driver requires the amqplib package.\r\nInstall it with:\r\n\r\n npx warlock add herald --driver=rabbitmq\r\n\r\nOr manually:\r\n\r\n npm install amqplib\r\n pnpm add amqplib\r\n yarn add amqplib\r\n`.trim();\r\n\r\n/**\r\n * Load amqplib module\r\n */\r\nasync function loadAmqplibModule() {\r\n try {\r\n amqplibModule = await import(\"amqplib\");\r\n isModuleExists = true;\r\n } catch {\r\n isModuleExists = false;\r\n }\r\n}\r\n\r\n// Kick off eager loading immediately\r\nloadAmqplibModule();\r\n\r\n// ============================================================\r\n// RabbitMQ Driver\r\n// ============================================================\r\n\r\n/**\r\n * RabbitMQ Driver\r\n *\r\n * Implementation of BrokerDriverContract for RabbitMQ/AMQP.\r\n *\r\n * **Important:** This driver requires the `amqplib` package to be installed.\r\n * Install it with: `npx warlock add herald --driver=rabbitmq` or `npm install amqplib`\r\n *\r\n * @example\r\n * ```typescript\r\n * const driver = new RabbitMQDriver({\r\n * driver: \"rabbitmq\",\r\n * host: \"localhost\",\r\n * port: 5672,\r\n * username: \"guest\",\r\n * password: \"guest\",\r\n * });\r\n *\r\n * await driver.connect();\r\n * const channel = driver.channel(\"user.created\");\r\n * ```\r\n */\r\nexport class RabbitMQDriver implements BrokerDriverContract {\r\n public readonly name = \"rabbitmq\" as const;\r\n\r\n public readonly consumers: EventConsumerClass[] = [];\r\n\r\n private readonly options: RabbitMQConnectionOptions;\r\n private readonly events = new EventEmitter();\r\n private readonly channels = new Map<string, ChannelContract<any>>();\r\n\r\n private connection: any = null;\r\n private amqpChannel: any = null;\r\n private _isConnected = false;\r\n\r\n /**\r\n * Create a new RabbitMQ driver\r\n *\r\n * @param options - RabbitMQ connection options\r\n */\r\n public constructor(options: RabbitMQConnectionOptions) {\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * Whether connected to RabbitMQ\r\n */\r\n public get isConnected(): boolean {\r\n return this._isConnected;\r\n }\r\n\r\n /**\r\n * Subscribe the given consumer class to the driver\r\n *\r\n * @param consumer - Consumer class to subscribe\r\n *\r\n * @example\r\n * ```typescript\r\n * driver.subscribe(UserUpdatedConsumer);\r\n * ```\r\n */\r\n public subscribe(Consumer: EventConsumerClass) {\r\n if (this.isConnected) {\r\n this.channel(Consumer.eventName).subscribe(\r\n prepareConsumerSubscription(Consumer, (error, eventName) => {\r\n this.events.emit(\"error\", error, eventName);\r\n }),\r\n {\r\n consumerId: Consumer.consumerId,\r\n },\r\n );\r\n } else {\r\n this.consumers.push(Consumer);\r\n }\r\n\r\n return () => {\r\n this.unsubscribe(Consumer);\r\n };\r\n }\r\n\r\n /**\r\n * Unsubscribe the given consumer\r\n */\r\n public unsubscribe(Consumer: EventConsumerClass): void {\r\n if (this.isConnected) {\r\n this.channel(Consumer.eventName).unsubscribeById(Consumer.consumerId);\r\n }\r\n const index = this.consumers.indexOf(Consumer);\r\n if (index > -1) {\r\n this.consumers.splice(index, 1);\r\n }\r\n }\r\n\r\n /**\r\n * Publish the given event message.\r\n * Auto-creates the channel if it hasn't been accessed before.\r\n */\r\n public publish<TPayload = Record<string, any>>(event: EventMessage<TPayload>): void {\r\n this.channel(event.eventName).publish(event.serialize());\r\n }\r\n\r\n /**\r\n * Connect to RabbitMQ\r\n */\r\n public async connect(): Promise<void> {\r\n // Check if amqplib is installed\r\n if (isModuleExists === false) {\r\n throw new Error(`amqplib is not installed.\\n\\n${AMQPLIB_INSTALL_INSTRUCTIONS}`);\r\n }\r\n\r\n // Wait for module to load if still loading\r\n if (isModuleExists === null) {\r\n await loadAmqplibModule();\r\n if (!isModuleExists) {\r\n throw new Error(`amqplib is not installed.\\n\\n${AMQPLIB_INSTALL_INSTRUCTIONS}`);\r\n }\r\n }\r\n\r\n try {\r\n // Build connection URL\r\n const url = this.buildConnectionUrl();\r\n\r\n // Build connection options merging our options with native client options\r\n const connectOptions = {\r\n heartbeat: this.options.heartbeat ?? 60,\r\n timeout: this.options.connectionTimeout,\r\n // Merge native amqplib client options\r\n ...this.options.clientOptions,\r\n };\r\n\r\n // Connect using cached module\r\n this.connection = await amqplibModule.connect(url, connectOptions);\r\n\r\n // Create channel\r\n this.amqpChannel = await this.connection.createChannel();\r\n\r\n // Set prefetch if specified\r\n if (this.options.prefetch) {\r\n await this.amqpChannel.prefetch(this.options.prefetch);\r\n }\r\n\r\n this._isConnected = true;\r\n this.events.emit(\"connected\");\r\n\r\n for (const consumer of this.consumers) {\r\n this.subscribe(consumer);\r\n }\r\n\r\n this.consumers.length = 0;\r\n\r\n // Handle connection close\r\n this.connection.on(\"close\", () => {\r\n this._isConnected = false;\r\n this.events.emit(\"disconnected\");\r\n\r\n if (this.options.reconnect !== false) {\r\n this.handleReconnect();\r\n }\r\n });\r\n\r\n // Handle errors\r\n this.connection.on(\"error\", (error: Error) => {\r\n this.events.emit(\"error\", error);\r\n });\r\n } catch (error) {\r\n this._isConnected = false;\r\n throw new Error(\r\n `Failed to connect to RabbitMQ: ${error instanceof Error ? error.message : String(error)}`,\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Build connection URL from options\r\n */\r\n private buildConnectionUrl(): string {\r\n if (this.options.uri) {\r\n return this.options.uri;\r\n }\r\n\r\n const protocol = \"amqp\";\r\n const host = this.options.host ?? \"localhost\";\r\n const port = this.options.port ?? 5672;\r\n const vhost = this.options.vhost ?? \"/\";\r\n const username = this.options.username ?? \"guest\";\r\n const password = this.options.password ?? \"guest\";\r\n\r\n const encodedVhost = encodeURIComponent(vhost);\r\n\r\n return `${protocol}://${username}:${password}@${host}:${port}/${encodedVhost}`;\r\n }\r\n\r\n /**\r\n * Handle reconnection\r\n */\r\n private async handleReconnect(): Promise<void> {\r\n const delay = this.options.reconnectDelay ?? 5000;\r\n let attempt = 0;\r\n\r\n const tryReconnect = async () => {\r\n attempt++;\r\n this.events.emit(\"reconnecting\", attempt);\r\n\r\n try {\r\n await this.connect();\r\n } catch {\r\n setTimeout(tryReconnect, delay);\r\n }\r\n };\r\n\r\n setTimeout(tryReconnect, delay);\r\n }\r\n\r\n /**\r\n * Disconnect from RabbitMQ\r\n */\r\n public async disconnect(): Promise<void> {\r\n if (this.amqpChannel) {\r\n try {\r\n await this.amqpChannel.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n this.amqpChannel = null;\r\n }\r\n\r\n if (this.connection) {\r\n try {\r\n await this.connection.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n this.connection = null;\r\n }\r\n\r\n this._isConnected = false;\r\n this.events.emit(\"disconnected\");\r\n }\r\n\r\n /**\r\n * Register event listener\r\n */\r\n public on(event: BrokerEvent, listener: BrokerEventListener): void {\r\n this.events.on(event, listener as any);\r\n }\r\n\r\n /**\r\n * Remove event listener\r\n */\r\n public off(event: BrokerEvent, listener: BrokerEventListener): void {\r\n this.events.off(event, listener as any);\r\n }\r\n\r\n /**\r\n * Get or create a channel\r\n */\r\n public channel<TPayload = unknown>(\r\n name: string,\r\n options?: ChannelOptions<TPayload>,\r\n ): ChannelContract<TPayload> {\r\n // Check cache\r\n const existing = this.channels.get(name);\r\n if (existing) {\r\n return existing as ChannelContract<TPayload>;\r\n }\r\n\r\n // Create new channel\r\n const channel = new RabbitMQChannel<TPayload>(name, this.amqpChannel, options);\r\n\r\n this.channels.set(name, channel);\r\n return channel;\r\n }\r\n\r\n /**\r\n * Start consuming messages\r\n */\r\n public async startConsuming(): Promise<void> {\r\n // Channels start consuming when subscribe() is called\r\n // This method is for batch start if needed\r\n }\r\n\r\n /**\r\n * Stop consuming messages from all subscribed channels.\r\n * Gracefully cancels all active consumers.\r\n */\r\n public async stopConsuming(): Promise<void> {\r\n const stops = Array.from(this.channels.values()).map(channel =>\r\n (channel as RabbitMQChannel<any>).stopConsuming(),\r\n );\r\n await Promise.all(stops);\r\n }\r\n\r\n /**\r\n * Health check\r\n */\r\n public async healthCheck(): Promise<HealthCheckResult> {\r\n if (!this._isConnected || !this.connection) {\r\n return {\r\n healthy: false,\r\n error: \"Not connected to RabbitMQ\",\r\n };\r\n }\r\n\r\n const start = Date.now();\r\n\r\n try {\r\n // Simple check - verify channel is open\r\n await this.amqpChannel.checkQueue(\"amq.rabbitmq.reply-to\").catch(() => {\r\n // Queue might not exist, but if we get here, connection is alive\r\n });\r\n\r\n return {\r\n healthy: true,\r\n latency: Date.now() - start,\r\n };\r\n } catch (error) {\r\n return {\r\n healthy: false,\r\n error: error instanceof Error ? error.message : String(error),\r\n latency: Date.now() - start,\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get all channel names\r\n */\r\n public getChannelNames(): string[] {\r\n return Array.from(this.channels.keys());\r\n }\r\n\r\n /**\r\n * Close a specific channel\r\n */\r\n public async closeChannel(name: string): Promise<void> {\r\n const channel = this.channels.get(name);\r\n if (channel) {\r\n await channel.delete();\r\n this.channels.delete(name);\r\n }\r\n }\r\n\r\n /**\r\n * Get the raw AMQP channel (for advanced use)\r\n */\r\n public getRawChannel(): any {\r\n return this.amqpChannel;\r\n }\r\n\r\n /**\r\n * Get the raw connection (for advanced use)\r\n */\r\n public getRawConnection(): any {\r\n return this.connection;\r\n }\r\n}\r\n"],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { DeadLetterOptions, RetryOptions, SubscribeOptions } from "./types/subscribe.types.mjs";
2
+ import { ChannelOptions, ChannelStats } from "./types/channel.types.mjs";
3
+ import { BaseConnectionOptions, BrokerConfigurations, ConnectionOptions, KafkaClientOptions, KafkaConnectionOptions, RabbitMQClientOptions, RabbitMQConnectionOptions, RabbitMQSocketOptions } from "./types/connection.types.mjs";
4
+ import { BrokerDriverType, BrokerEvent, BrokerEventListener, HealthCheckResult } from "./types/driver.types.mjs";
5
+ import { Message, MessageContext, MessageHandler, MessageMetadata, ResponseHandler, Subscription } from "./types/message.types.mjs";
6
+ import { PublishOptions, RequestOptions } from "./types/publish.types.mjs";
7
+ import { ChannelContract } from "./contracts/channel.contract.mjs";
8
+ import { EventMessage, defineEvent } from "./message-managers/event-message.mjs";
9
+ import { EventConsumer, defineConsumer } from "./message-managers/event-consumer.mjs";
10
+ import { ConsumedEventMessage, EventConsumerClass } from "./message-managers/types.mjs";
11
+ import { BrokerDriverContract } from "./contracts/broker-driver.contract.mjs";
12
+ import { Broker, BrokerOptions } from "./communicators/broker.mjs";
13
+ import { BrokerRegistryEvent, BrokerRegistryListener } from "./types/registry.types.mjs";
14
+ import { MissingBrokerError, brokerRegistry } from "./communicators/broker-registry.mjs";
15
+ import { RabbitMQDriver } from "./drivers/rabbitmq/rabbitmq-driver.mjs";
16
+ import { RabbitMQChannel } from "./drivers/rabbitmq/rabbitmq-channel.mjs";
17
+ import { connectToBroker, herald, heraldChannel, publishEvent, subscribeConsumer } from "./utils/connect-to-broker.mjs";
18
+ import { Consumable, ConsumableOptions, pendingSubscribers } from "./decorators/consumable.mjs";
19
+ import { UseCaseBroadcastEvent, heraldBroadcast } from "./use-case-broadcast.mjs";
20
+ export { BaseConnectionOptions, Broker, BrokerConfigurations, BrokerDriverContract, BrokerDriverType, BrokerEvent, BrokerEventListener, BrokerOptions, BrokerRegistryEvent, BrokerRegistryListener, ChannelContract, ChannelOptions, ChannelStats, ConnectionOptions, Consumable, ConsumableOptions, ConsumedEventMessage, DeadLetterOptions, EventConsumer, EventConsumerClass, EventMessage, HealthCheckResult, KafkaClientOptions, KafkaConnectionOptions, Message, MessageContext, MessageHandler, MessageMetadata, MissingBrokerError, PublishOptions, RabbitMQChannel, RabbitMQClientOptions, RabbitMQConnectionOptions, RabbitMQDriver, RabbitMQSocketOptions, RequestOptions, ResponseHandler, RetryOptions, SubscribeOptions, Subscription, UseCaseBroadcastEvent, brokerRegistry, connectToBroker, defineConsumer, defineEvent, herald, heraldBroadcast, heraldChannel, pendingSubscribers, publishEvent, subscribeConsumer };
package/esm/index.mjs ADDED
@@ -0,0 +1,16 @@
1
+ import { Broker } from "./communicators/broker.mjs";
2
+ import { MissingBrokerError, brokerRegistry } from "./communicators/broker-registry.mjs";
3
+ import "./communicators/index.mjs";
4
+ import { RabbitMQChannel } from "./drivers/rabbitmq/rabbitmq-channel.mjs";
5
+ import { RabbitMQDriver } from "./drivers/rabbitmq/rabbitmq-driver.mjs";
6
+ import "./drivers/index.mjs";
7
+ import { connectToBroker, herald, heraldChannel, publishEvent, subscribeConsumer } from "./utils/connect-to-broker.mjs";
8
+ import "./utils/index.mjs";
9
+ import { Consumable, pendingSubscribers } from "./decorators/consumable.mjs";
10
+ import "./decorators/index.mjs";
11
+ import { EventConsumer, defineConsumer } from "./message-managers/event-consumer.mjs";
12
+ import { EventMessage, defineEvent } from "./message-managers/event-message.mjs";
13
+ import "./message-managers/index.mjs";
14
+ import { heraldBroadcast } from "./use-case-broadcast.mjs";
15
+
16
+ export { Broker, Consumable, EventConsumer, EventMessage, MissingBrokerError, RabbitMQChannel, RabbitMQDriver, brokerRegistry, connectToBroker, defineConsumer, defineEvent, herald, heraldBroadcast, heraldChannel, pendingSubscribers, publishEvent, subscribeConsumer };
@@ -0,0 +1,61 @@
1
+ import { ConsumedEventMessage, EventConsumerClass } from "./types.mjs";
2
+ import { ObjectValidator, ValidationResult } from "@warlock.js/seal";
3
+
4
+ //#region ../../@warlock.js/herald/src/message-managers/event-consumer.d.ts
5
+ declare abstract class EventConsumer<Payload = Record<string, any>> {
6
+ /**
7
+ * Event name
8
+ */
9
+ static eventName: string;
10
+ private static _consumerId?;
11
+ static get consumerId(): string;
12
+ get eventName(): string;
13
+ /**
14
+ * Min version accepted to be consumed by this class
15
+ */
16
+ static minVersion?: number;
17
+ /**
18
+ * Max version accepted to be consumed by this class
19
+ */
20
+ static maxVersion?: number;
21
+ /**
22
+ * Payload validation to auto reject the received event before accessing it in the handle method
23
+ */
24
+ schema?: ObjectValidator;
25
+ /**
26
+ * The method that will be called when the event is received
27
+ */
28
+ abstract handle(payload: Payload, event: ConsumedEventMessage): Promise<void>;
29
+ /**
30
+ * Determine whether this is accepted version to be used by this consumer
31
+ */
32
+ static isAcceptedVersion(version: number): boolean;
33
+ /**
34
+ * Validate the given data
35
+ */
36
+ validate(data: Payload): Promise<ValidationResult | void>;
37
+ }
38
+ /**
39
+ * Define Consumer options
40
+ */
41
+ type ConsumerOptions<Payload> = {
42
+ /**
43
+ * Payload validation to auto reject the received event before accessing it in the handle method
44
+ */
45
+ schema?: ObjectValidator;
46
+ /**
47
+ * Handle data
48
+ */
49
+ handle: (payload: Payload, event: ConsumedEventMessage) => Promise<void>;
50
+ /**
51
+ * Validate the payload before executing `handle`
52
+ */
53
+ validate?: (payload: Payload) => Promise<ValidationResult | boolean>;
54
+ };
55
+ /**
56
+ * A shorthand to define an event consumer without declaring an entire class
57
+ */
58
+ declare function defineConsumer<Payload = Record<string, any>>(eventName: string, options: ConsumerOptions<Payload>): EventConsumerClass;
59
+ //#endregion
60
+ export { EventConsumer, defineConsumer };
61
+ //# sourceMappingURL=event-consumer.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-consumer.d.mts","names":[],"sources":["../../../../../../@warlock.js/herald/src/message-managers/event-consumer.ts"],"mappings":";;;;uBAWsB,aAAA,WAAwB,MAAA;EAqCZ;;;EAAA,OAjClB,SAAA;EAAA,eAEC,WAAA;EAAA,WAEG,UAAA,CAAA;EAAA,IAOP,SAAA,CAAA;EAqCkC;;;EAAA,OA9B/B,UAAA;EAhBC;;;EAAA,OAqBD,UAAA;EAAA;;;EAKP,MAAA,GAAS,eAAA;EAKgB;;;EAAA,SAAhB,MAAA,CAAO,OAAA,EAAS,OAAA,EAAS,KAAA,EAAO,oBAAA,GAAuB,OAAA;EAAA;;;EAAA,OAKzD,iBAAA,CAAkB,OAAA;EAUJ;;;EAAf,QAAA,CAAS,IAAA,EAAM,OAAA,GAAU,OAAA,CAAQ,gBAAA;AAAA;AAAgB;AAK/D;;AAL+D,KAU3D,eAAA;EAIM;;;EAAT,MAAA,GAAS,eAAA;EAQY;;;EAJrB,MAAA,GAAS,OAAA,EAAS,OAAA,EAAS,KAAA,EAAO,oBAAA,KAAyB,OAAA;EAInB;;;EAAxC,QAAA,IAAY,OAAA,EAAS,OAAA,KAAY,OAAA,CAAQ,gBAAA;AAAA;;;;iBAM3B,cAAA,WAAyB,MAAA,cAAA,CACvC,SAAA,UACA,OAAA,EAAS,eAAA,CAAgB,OAAA,IACxB,kBAAA"}
@@ -0,0 +1,63 @@
1
+ import { Consumable } from "../decorators/consumable.mjs";
2
+ import "../decorators/index.mjs";
3
+ import { v } from "@warlock.js/seal";
4
+ import { randomUUID } from "crypto";
5
+
6
+ //#region ../../@warlock.js/herald/src/message-managers/event-consumer.ts
7
+ /**
8
+ * This class is used to be part of the Herald Event Consumer Manager.
9
+ * It should be used to consume events from Either RabbitMQ or Kafka through Herald
10
+ *
11
+ * It's highly recommended using it instead of declaring manual channel namd and subscribing to event
12
+ */
13
+ var EventConsumer = class {
14
+ static get consumerId() {
15
+ if (!this._consumerId) this._consumerId = randomUUID();
16
+ return this._consumerId;
17
+ }
18
+ get eventName() {
19
+ return this.constructor.eventName;
20
+ }
21
+ /**
22
+ * Determine whether this is accepted version to be used by this consumer
23
+ */
24
+ static isAcceptedVersion(version) {
25
+ if (this.minVersion && version < this.minVersion) return false;
26
+ if (this.maxVersion && version > this.maxVersion) return false;
27
+ return true;
28
+ }
29
+ /**
30
+ * Validate the given data
31
+ */
32
+ async validate(data) {
33
+ if (!this.schema) return;
34
+ return await v.validate(this.schema, data);
35
+ }
36
+ };
37
+ /**
38
+ * A shorthand to define an event consumer without declaring an entire class
39
+ */
40
+ function defineConsumer(eventName, options) {
41
+ const Class = class AnnouncedConsumer extends EventConsumer {
42
+ constructor(..._args) {
43
+ super(..._args);
44
+ this.schema = options.schema;
45
+ }
46
+ static {
47
+ this.eventName = eventName;
48
+ }
49
+ async handle(payload, event) {
50
+ if (options.validate) {
51
+ const result = await options.validate(payload);
52
+ if (!result || !result.isValid) return;
53
+ }
54
+ return options.handle(payload, event);
55
+ }
56
+ };
57
+ Consumable()(Class);
58
+ return Class;
59
+ }
60
+
61
+ //#endregion
62
+ export { EventConsumer, defineConsumer };
63
+ //# sourceMappingURL=event-consumer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-consumer.mjs","names":[],"sources":["../../../../../../@warlock.js/herald/src/message-managers/event-consumer.ts"],"sourcesContent":["/**\n * This class is used to be part of the Herald Event Consumer Manager.\n * It should be used to consume events from Either RabbitMQ or Kafka through Herald\n *\n * It's highly recommended using it instead of declaring manual channel namd and subscribing to event\n */\nimport { v, ValidationResult, type ObjectValidator } from \"@warlock.js/seal\";\nimport { randomUUID } from \"crypto\";\nimport { Consumable } from \"../decorators\";\nimport { ConsumedEventMessage, EventConsumerClass } from \"./types\";\n\nexport abstract class EventConsumer<Payload = Record<string, any>> {\n /**\n * Event name\n */\n public static eventName: string;\n\n private static _consumerId?: string;\n\n public static get consumerId(): string {\n if (!this._consumerId) {\n this._consumerId = randomUUID();\n }\n return this._consumerId;\n }\n\n public get eventName() {\n return (this.constructor as typeof EventConsumer).eventName;\n }\n\n /**\n * Min version accepted to be consumed by this class\n */\n public static minVersion?: number;\n\n /**\n * Max version accepted to be consumed by this class\n */\n public static maxVersion?: number;\n\n /**\n * Payload validation to auto reject the received event before accessing it in the handle method\n */\n public schema?: ObjectValidator;\n\n /**\n * The method that will be called when the event is received\n */\n public abstract handle(payload: Payload, event: ConsumedEventMessage): Promise<void>;\n\n /**\n * Determine whether this is accepted version to be used by this consumer\n */\n public static isAcceptedVersion(version: number): boolean {\n if (this.minVersion && version < this.minVersion) return false;\n if (this.maxVersion && version > this.maxVersion) return false;\n\n return true;\n }\n\n /**\n * Validate the given data\n */\n public async validate(data: Payload): Promise<ValidationResult | void> {\n if (!this.schema) return;\n\n return await v.validate(this.schema, data);\n }\n}\n\n/**\n * Define Consumer options\n */\ntype ConsumerOptions<Payload> = {\n /**\n * Payload validation to auto reject the received event before accessing it in the handle method\n */\n schema?: ObjectValidator;\n /**\n * Handle data\n */\n handle: (payload: Payload, event: ConsumedEventMessage) => Promise<void>;\n /**\n * Validate the payload before executing `handle`\n */\n validate?: (payload: Payload) => Promise<ValidationResult | boolean>;\n};\n\n/**\n * A shorthand to define an event consumer without declaring an entire class\n */\nexport function defineConsumer<Payload = Record<string, any>>(\n eventName: string,\n options: ConsumerOptions<Payload>,\n): EventConsumerClass {\n const Class = class AnnouncedConsumer extends EventConsumer<Payload> {\n public static eventName = eventName;\n public schema = options.schema;\n\n public async handle(payload: Payload, event: ConsumedEventMessage) {\n if (options.validate) {\n const result = await options.validate(payload);\n if (!result || !(result as ValidationResult).isValid) return;\n }\n\n return options.handle(payload, event);\n }\n };\n\n Consumable()(Class as EventConsumerClass);\n\n return Class as EventConsumerClass;\n}\n"],"mappings":";;;;;;;;;;;;AAWA,IAAsB,gBAAtB,MAAmE;CAQjE,WAAkB,aAAqB;EACrC,IAAI,CAAC,KAAK,aACR,KAAK,cAAc,WAAW;EAEhC,OAAO,KAAK;CACd;CAEA,IAAW,YAAY;EACrB,OAAQ,KAAK,YAAqC;CACpD;;;;CAyBA,OAAc,kBAAkB,SAA0B;EACxD,IAAI,KAAK,cAAc,UAAU,KAAK,YAAY,OAAO;EACzD,IAAI,KAAK,cAAc,UAAU,KAAK,YAAY,OAAO;EAEzD,OAAO;CACT;;;;CAKA,MAAa,SAAS,MAAiD;EACrE,IAAI,CAAC,KAAK,QAAQ;EAElB,OAAO,MAAM,EAAE,SAAS,KAAK,QAAQ,IAAI;CAC3C;AACF;;;;AAuBA,SAAgB,eACd,WACA,SACoB;CACpB,MAAM,QAAQ,MAAM,0BAA0B,cAAuB;;;iBAEnD,QAAQ;;;oBADE;;EAG1B,MAAa,OAAO,SAAkB,OAA6B;GACjE,IAAI,QAAQ,UAAU;IACpB,MAAM,SAAS,MAAM,QAAQ,SAAS,OAAO;IAC7C,IAAI,CAAC,UAAU,CAAE,OAA4B,SAAS;GACxD;GAEA,OAAO,QAAQ,OAAO,SAAS,KAAK;EACtC;CACF;CAEA,WAAW,EAAE,KAA2B;CAExC,OAAO;AACT"}
@@ -0,0 +1,87 @@
1
+ import { ObjectValidator } from "@warlock.js/seal";
2
+ import { GenericObject } from "@mongez/reinforcements";
3
+
4
+ //#region ../../@warlock.js/herald/src/message-managers/event-message.d.ts
5
+ declare abstract class EventMessage<TPayload = Record<string, any>> {
6
+ protected data?: TPayload | undefined;
7
+ /**
8
+ * Event Name
9
+ */
10
+ abstract eventName: string;
11
+ /**
12
+ * Event version
13
+ */
14
+ version?: number;
15
+ /**
16
+ * Additional metadata (if any)
17
+ */
18
+ metadata?: Record<string, any>;
19
+ /**
20
+ * Event Message id
21
+ */
22
+ messageId?: string;
23
+ /**
24
+ * Schema of payload that will be used to determine whether this event should be published
25
+ */
26
+ schema?: ObjectValidator;
27
+ /**
28
+ * Data that will be sent with the event (Payload)
29
+ */
30
+ toJSON(): TPayload;
31
+ constructor(data?: TPayload | undefined);
32
+ /**
33
+ * Serialize the event to be ready for publishing.
34
+ * Delegates payload resolution to toJSON() — override toJSON() to customize.
35
+ *
36
+ * @throws Error if toJSON() throws (e.g. no data provided)
37
+ */
38
+ serialize(): {
39
+ payload: TPayload;
40
+ metadata: Record<string, any> | undefined;
41
+ messageId: string;
42
+ eventName: string;
43
+ version: number | undefined;
44
+ occurredAt: Date;
45
+ __through: string;
46
+ };
47
+ }
48
+ type EventOptions<T> = {
49
+ /**
50
+ * Shapen the data that will be used
51
+ */
52
+ toJSON?: (data: T) => GenericObject;
53
+ /**
54
+ * Validation schema
55
+ */
56
+ schema?: ObjectValidator;
57
+ };
58
+ /**
59
+ * Represents an EventMessage class constructor.
60
+ *
61
+ * @template TIncoming - The type of data accepted by the constructor
62
+ * @template TOutgoing - The type of data returned by toJSON() (defaults to TIncoming)
63
+ */
64
+ type EventMessageClass<TIncoming = Record<string, any>, TOutgoing = TIncoming> = new (data?: TIncoming) => EventMessage<TOutgoing>;
65
+ /**
66
+ * A shorthand to define an event without declaring an entire class.
67
+ *
68
+ * This factory function creates an EventMessage subclass that transforms
69
+ * input data (IncomingData) into a different output format (OutgoingData).
70
+ *
71
+ * @template IncomingData - The type of data passed to the constructor
72
+ * @template OutgoingData - The type of data returned by toJSON()
73
+ *
74
+ * @example
75
+ * ```typescript
76
+ * const UserCreatedEvent = defineEvent<User, { id: number; name: string }>(
77
+ * "user.created",
78
+ * { toJSON: (user) => user.only(["id", "name"]) }
79
+ * );
80
+ *
81
+ * publishEvent(new UserCreatedEvent(user));
82
+ * ```
83
+ */
84
+ declare function defineEvent<IncomingData = unknown, OutgoingData = unknown>(eventName: string, options?: EventOptions<IncomingData>): EventMessageClass<IncomingData, OutgoingData>;
85
+ //#endregion
86
+ export { EventMessage, defineEvent };
87
+ //# sourceMappingURL=event-message.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-message.d.mts","names":[],"sources":["../../../../../../@warlock.js/herald/src/message-managers/event-message.ts"],"mappings":";;;;uBAUsB,YAAA,YAAwB,MAAA;EAAA,UAqCf,IAAA,GAAO,QAAA;EAbpB;;;EAAA,SApBA,SAAA;;;;EAKT,OAAA;EAT0B;;;EAc1B,QAAA,GAAW,MAAA;EAVF;;;EAeT,SAAA;EAAA;;;EAKA,MAAA,GAAS,eAAA;EAKC;;;EAAV,MAAA,CAAA,GAAU,QAAA;cAQY,IAAA,GAAO,QAAA;;;;;;;EAQ7B,SAAA,CAAA;;;;;;;;;;KAeJ,YAAA;EAQM;;;EAJT,MAAA,IAAU,IAAA,EAAM,CAAA,KAAM,aAAA;EAAtB;;;EAIA,MAAA,GAAS,eAAA;AAAA;;;AAAe;AAAA;;;KASrB,iBAAA,aAA8B,MAAA,2BAAiC,SAAA,SAClE,IAAA,GAAO,SAAA,KACJ,YAAA,CAAa,SAAA;;;;;;;;;;;;;;;;AAAS;AAqB3B;;;iBAAgB,WAAA,gDAAA,CACd,SAAA,UACA,OAAA,GAAS,YAAA,CAAa,YAAA,IACrB,iBAAA,CAAkB,YAAA,EAAc,YAAA"}
@@ -0,0 +1,68 @@
1
+ import { randomUUID } from "crypto";
2
+
3
+ //#region ../../@warlock.js/herald/src/message-managers/event-message.ts
4
+ var EventMessage = class {
5
+ /**
6
+ * Data that will be sent with the event (Payload)
7
+ */
8
+ toJSON() {
9
+ if (!this.data) throw new Error(`no Data is defined for Event: ${this.eventName}`);
10
+ return this.data;
11
+ }
12
+ constructor(data) {
13
+ this.data = data;
14
+ }
15
+ /**
16
+ * Serialize the event to be ready for publishing.
17
+ * Delegates payload resolution to toJSON() — override toJSON() to customize.
18
+ *
19
+ * @throws Error if toJSON() throws (e.g. no data provided)
20
+ */
21
+ serialize() {
22
+ return {
23
+ payload: this.toJSON(),
24
+ metadata: this.metadata,
25
+ messageId: this.messageId ?? randomUUID(),
26
+ eventName: this.eventName,
27
+ version: this.version,
28
+ occurredAt: /* @__PURE__ */ new Date(),
29
+ __through: "EventMessage"
30
+ };
31
+ }
32
+ };
33
+ /**
34
+ * A shorthand to define an event without declaring an entire class.
35
+ *
36
+ * This factory function creates an EventMessage subclass that transforms
37
+ * input data (IncomingData) into a different output format (OutgoingData).
38
+ *
39
+ * @template IncomingData - The type of data passed to the constructor
40
+ * @template OutgoingData - The type of data returned by toJSON()
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * const UserCreatedEvent = defineEvent<User, { id: number; name: string }>(
45
+ * "user.created",
46
+ * { toJSON: (user) => user.only(["id", "name"]) }
47
+ * );
48
+ *
49
+ * publishEvent(new UserCreatedEvent(user));
50
+ * ```
51
+ */
52
+ function defineEvent(eventName, options = {}) {
53
+ return class AnnouncedEvent extends EventMessage {
54
+ constructor(data) {
55
+ super(data);
56
+ this.eventName = eventName;
57
+ this.schema = options.schema;
58
+ }
59
+ toJSON() {
60
+ if (!options.toJSON) return this.data;
61
+ return options.toJSON(this.data);
62
+ }
63
+ };
64
+ }
65
+
66
+ //#endregion
67
+ export { EventMessage, defineEvent };
68
+ //# sourceMappingURL=event-message.mjs.map