@glowlabs-org/events-sdk 0.1.1 โ†’ 1.0.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 (39) hide show
  1. package/README.md +311 -151
  2. package/dist/admin.d.ts +7 -6
  3. package/dist/admin.js +6 -6
  4. package/dist/base-event.d.ts +65 -0
  5. package/dist/base-event.js +22 -0
  6. package/dist/emitter.d.ts +17 -0
  7. package/dist/emitter.js +67 -0
  8. package/dist/event-registry.d.ts +2 -0
  9. package/dist/event-registry.js +27 -0
  10. package/dist/index.d.ts +2 -5
  11. package/dist/index.js +2 -5
  12. package/dist/listener.d.ts +14 -0
  13. package/dist/listener.js +88 -0
  14. package/dist/schemas/application-created.v1.d.ts +25 -0
  15. package/dist/schemas/application-created.v1.js +12 -0
  16. package/dist/schemas/audit-pfees-paid.v1.d.ts +15 -0
  17. package/dist/schemas/audit-pfees-paid.v1.js +14 -0
  18. package/dist/schemas/audit-pfees-paid.v2.d.ts +15 -0
  19. package/dist/schemas/audit-pfees-paid.v2.js +14 -0
  20. package/dist/schemas/audit-pushed.v1.d.ts +15 -0
  21. package/dist/schemas/audit-pushed.v1.js +13 -0
  22. package/dist/schemas/audit-slashed.v1.d.ts +12 -0
  23. package/dist/schemas/audit-slashed.v1.js +10 -0
  24. package/dist/types.d.ts +32 -21
  25. package/dist/utils.d.ts +20 -0
  26. package/dist/utils.js +45 -0
  27. package/dist/zones.d.ts +7 -0
  28. package/dist/zones.js +8 -0
  29. package/package.json +1 -1
  30. package/dist/consumer.d.ts +0 -14
  31. package/dist/consumer.js +0 -84
  32. package/dist/glow-listener.d.ts +0 -31
  33. package/dist/glow-listener.js +0 -74
  34. package/dist/producer.d.ts +0 -8
  35. package/dist/producer.js +0 -46
  36. package/dist/schemas/auditPushed.d.ts +0 -69
  37. package/dist/schemas/auditPushed.js +0 -42
  38. package/dist/typed-emitter.d.ts +0 -7
  39. package/dist/typed-emitter.js +0 -17
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Glow Events SDK
2
2
 
3
- A TypeScript-first SDK for consuming and emitting typed events on the Glow platform, powered by RabbitMQ (fanout exchange). Provides runtime validation and type inference using Zod schemas.
3
+ A TypeScript-first SDK for consuming and emitting typed events on the Glow platform, powered by RabbitMQ (topic exchange). Provides runtime validation and type inference using Zod schemas.
4
4
 
5
5
  ---
6
6
 
@@ -12,190 +12,351 @@ A TypeScript-first SDK for consuming and emitting typed events on the Glow platf
12
12
  pnpm install @glowlabs-org/events-sdk
13
13
  ```
14
14
 
15
- ### 2. Available Events
15
+ ---
16
+
17
+ ## ๐Ÿ“š Zones
18
+
19
+ The following zones are currently available:
20
+
21
+ | Zone ID | Zone Name |
22
+ | ------- | ------------------ |
23
+ | 0 | All Zones |
24
+ | 1 | Clean Grid Project |
25
+ | 2 | Coming Soon Zone |
26
+
27
+ - Use `zoneId: 0` (with `zoneName: "All Zones"`) to listen to all zones. **Emitters must be constructed with a specific zoneId (not 0).**
28
+ - The SDK types and runtime validation now officially support `zoneId: 0` and `zoneName: "All Zones"` everywhere for listeners, but not for emitters.
29
+
30
+ ---
31
+
32
+ ## ๐Ÿ“ฆ Event Types & Versions
33
+
34
+ Currently supported event types and versions:
35
+
36
+ | Event Name | Version | Payload Type | Description |
37
+ | --------------------- | ------- | ----------------------------- | -------------------------------------- |
38
+ | `audit.pushed` | 1 | `AuditPushedV1Payload` | Emitted when an audit is pushed |
39
+ | `audit.slashed` | 1 | `AuditSlashedV1Payload` | Emitted when a farm is slashed |
40
+ | `audit.pfees.paid` | 1 | `AuditPfeesPaidV1Payload` | Paid (by applicationId) |
41
+ | `audit.pfees.paid` | 2 | `AuditPfeesPaidV2Payload` | Paid (by farmId) |
42
+ | `application.created` | 1 | `ApplicationCreatedV1Payload` | Emitted when an application is created |
43
+
44
+ ---
45
+
46
+ ## ๐Ÿ“ Event Payload Schemas
47
+
48
+ ### `audit.pushed` v1
49
+
50
+ ```ts
51
+ export interface AuditPushedV1Payload {
52
+ farmId: string; // bytes32 hex string (0x...)
53
+ protocolFeeUSDPrice_12Decimals: string; // uint256 (decimal) โˆ’ 12 implied decimals
54
+ expectedProduction_12Decimals: string; // uint256 (decimal) โˆ’ 12 implied decimals
55
+ }
56
+ ```
57
+
58
+ **Validation:**
59
+
60
+ - `farmId` must be a 32-byte hex string (e.g., `0x...`).
61
+ - `protocolFeeUSDPrice_12Decimals` and `expectedProduction_12Decimals` must be decimal strings representing unsigned big integers.
62
+
63
+ ### `audit.slashed` v1
64
+
65
+ ```ts
66
+ export interface AuditSlashedV1Payload {
67
+ farmId: string; // bytes32 hex string (0x...)
68
+ slasher: string; // Ethereum address (0x...)
69
+ }
70
+ ```
71
+
72
+ **Validation:**
73
+
74
+ - `farmId` must be a 32-byte hex string (e.g., `0x...`).
75
+ - `slasher` must be a valid Ethereum address (0x...40 hex chars).
76
+
77
+ ### `audit.pfees.paid` v1 (by applicationId)
78
+
79
+ ```ts
80
+ export interface AuditPfeesPaidV1Payload {
81
+ applicationId: string; // bytes32 hex string (0x...)
82
+ payer: string; // Ethereum address (0x...)
83
+ amount_12Decimals: string; // uint256 (decimal) โˆ’ 12 implied decimals
84
+ }
85
+ ```
16
86
 
17
- | Event Name | Payload Type | Description |
18
- | -------------- | ------------------ | -------------------------------- |
19
- | `AuditPushed` | `AuditPushedEvent` | Emitted when an audit is pushed |
20
- | `AuditSlashed` | `AuditSlashedId` | Emitted when an audit is slashed |
87
+ **Validation:**
21
88
 
22
- #### Event Payload Types
89
+ - `applicationId` must be a 32-byte hex string (e.g., `0x...`).
90
+ - `payer` must be a valid Ethereum address (0x...40 hex chars).
91
+ - `amount_12Decimals` must be a decimal string representing an unsigned big integer (12 implied decimals).
92
+
93
+ ### `audit.pfees.paid` v2 (by farmId)
23
94
 
24
95
  ```ts
25
- // AuditPushedEvent
26
- import type { AuditPushedEvent } from "@glowlabs-org/events-sdk";
27
- // type AuditPushedEvent = {
28
- // farmId: string;
29
- // protocolFeeUSDPrice_12Decimals: string;
30
- // tokenPrices: { token: string; priceWAD_usd12Decimals: string }[];
31
- // expectedProduction_12Decimals: string;
32
- // glowRewardSplits: { receiver: string; percent: number }[];
33
- // cashRewardSplits: { receiver: string; percent: number }[];
34
- // }
35
-
36
- // AuditSlashedId
37
- // type AuditSlashedId = `0x${string}`;
96
+ export interface AuditPfeesPaidV2Payload {
97
+ farmId: string; // bytes32 hex string (0x...)
98
+ payer: string; // Ethereum address (0x...)
99
+ amount_12Decimals: string; // uint256 (decimal) โˆ’ 12 implied decimals
100
+ }
38
101
  ```
39
102
 
103
+ **Validation:**
104
+
105
+ - `farmId` must be a 32-byte hex string (e.g., `0x...`).
106
+ - `payer` must be a valid Ethereum address (0x...40 hex chars).
107
+ - `amount_12Decimals` must be a decimal string representing an unsigned big integer (12 implied decimals).
108
+
109
+ ### `application.created` v1
110
+
111
+ ```ts
112
+ export interface ApplicationCreatedV1Payload {
113
+ gcaAddress: string; // Ethereum address (0x...)
114
+ lat: number;
115
+ lng: number;
116
+ estimatedCostOfPowerPerKWh: number;
117
+ estimatedKWhGeneratedPerYear: number;
118
+ installerCompanyName: string;
119
+ }
120
+ ```
121
+
122
+ **Validation:**
123
+
124
+ - `gcaAddress` must be a valid Ethereum address (0x...40 hex chars).
125
+ - `lat` and `lng` are numbers (coordinates).
126
+ - `estimatedCostOfPowerPerKWh` and `estimatedKWhGeneratedPerYear` are numbers.
127
+ - `installerCompanyName` is a string.
128
+
40
129
  ---
41
130
 
42
131
  ## โœจ Usage Example
43
132
 
44
- ### Listen to Events
133
+ ### Listen to Specific Event Types/Versions
45
134
 
46
135
  ```ts
47
- import { createGlowListener } from "@glowlabs-org/events-sdk";
136
+ import { createGlowEventListener } from "@glowlabs-org/events-sdk";
137
+
138
+ const listener = createGlowEventListener({
139
+ username: "listener",
140
+ password: "your-password-here",
141
+ zoneId: 1,
142
+ queueName: "my.precreated.queue",
143
+ });
48
144
 
49
- async function main() {
50
- const sdk = await createGlowListener({
51
- username: "listener", // or "admin"
52
- password: "your-password-here",
53
- queueName: "my.precreated.queue", // REQUIRED: must be pre-created and bound by an admin
54
- // Optionally specify a custom exchange:
55
- // exchange: "glow.audit.v2.exchange",
56
- });
145
+ listener.onEvent("audit.pushed", 1, (event) => {
146
+ console.log("Received audit.pushed v1:", event.farmId);
147
+ });
57
148
 
58
- sdk.on("AuditPushed", (event) => {
59
- console.log("Received AuditPushed:", event.farmId);
60
- });
149
+ listener.onEvent("audit.slashed", 1, (event) => {
150
+ console.log("Received audit.slashed v1:", event.farmId, event.slasher);
151
+ });
61
152
 
62
- sdk.on("AuditSlashed", (id) => {
63
- console.log("Received AuditSlashed:", id);
64
- });
153
+ listener.onEvent("audit.pfees.paid", 1, (event) => {
154
+ console.log(
155
+ "Received audit.pfees.paid v1:",
156
+ event.applicationId,
157
+ event.payer,
158
+ event.amount_12Decimals
159
+ );
160
+ });
65
161
 
66
- await sdk.startListener();
162
+ listener.onEvent("audit.pfees.paid", 2, (event) => {
163
+ console.log(
164
+ "Received audit.pfees.paid v2:",
165
+ event.farmId,
166
+ event.payer,
167
+ event.amount_12Decimals
168
+ );
169
+ });
67
170
 
68
- // To stop listening:
69
- // await sdk.stopListener();
70
- }
171
+ listener.onEvent("application.created", 1, (event) => {
172
+ console.log(
173
+ "Received application.created v1:",
174
+ event.gcaAddress,
175
+ event.lat,
176
+ event.lng,
177
+ event.estimatedCostOfPowerPerKWh,
178
+ event.estimatedKWhGeneratedPerYear,
179
+ event.installerCompanyName
180
+ );
181
+ });
71
182
 
72
- main();
183
+ await listener.start();
184
+ // To stop listening:
185
+ // await listener.stop();
73
186
  ```
74
187
 
75
188
  ### Emit Events (Admin Only)
76
189
 
77
190
  ```ts
78
- import { createGlowListener } from "@glowlabs-org/events-sdk";
79
-
80
- async function main() {
81
- const sdk = await createGlowListener({
82
- username: "admin",
83
- password: "your-password-here",
84
- queueName: "my.precreated.queue", // REQUIRED: must be pre-created and bound by an admin
85
- // Optionally specify a custom exchange:
86
- // exchange: "glow.audit.v2.exchange",
87
- });
88
-
89
- await sdk.emitAuditPushed?.({
191
+ import { createGlowEventEmitter } from "@glowlabs-org/events-sdk";
192
+
193
+ // You must construct the emitter with a specific zoneId (not 0)
194
+ const emitter = createGlowEventEmitter({
195
+ username: "admin",
196
+ password: "your-password-here",
197
+ zoneId: 1, // must be a specific zone
198
+ });
199
+
200
+ await emitter.emit({
201
+ eventType: "audit.pushed",
202
+ schemaVersion: 1,
203
+ payload: {
90
204
  farmId: "0x...",
91
205
  protocolFeeUSDPrice_12Decimals: "...",
92
- tokenPrices: [],
93
206
  expectedProduction_12Decimals: "...",
94
- glowRewardSplits: [],
95
- cashRewardSplits: [],
96
- });
97
-
98
- sdk.emitAuditSlashed?.("0x...");
99
- }
207
+ },
208
+ });
100
209
 
101
- main();
102
- ```
210
+ await emitter.emit({
211
+ eventType: "audit.slashed",
212
+ schemaVersion: 1,
213
+ payload: {
214
+ farmId: "0x...",
215
+ slasher: "0x...",
216
+ },
217
+ });
103
218
 
104
- ---
219
+ await emitter.emit({
220
+ eventType: "audit.pfees.paid",
221
+ schemaVersion: 1,
222
+ payload: {
223
+ applicationId: "0x...",
224
+ payer: "0x...",
225
+ amount_12Decimals: "1000000000000",
226
+ },
227
+ });
105
228
 
106
- ## ๐Ÿ“ API Reference
229
+ await emitter.emit({
230
+ eventType: "audit.pfees.paid",
231
+ schemaVersion: 2,
232
+ payload: {
233
+ farmId: "0x...",
234
+ payer: "0x...",
235
+ amount_12Decimals: "1000000000000",
236
+ },
237
+ });
107
238
 
108
- ### `createGlowListener({ username, password, exchange?, queueName })`
239
+ await emitter.emit({
240
+ eventType: "application.created",
241
+ schemaVersion: 1,
242
+ payload: {
243
+ gcaAddress: "0x...",
244
+ lat: 45.5017,
245
+ lng: -73.5673,
246
+ estimatedCostOfPowerPerKWh: 0.12,
247
+ estimatedKWhGeneratedPerYear: 10000,
248
+ installerCompanyName: "SolarCo",
249
+ },
250
+ });
109
251
 
110
- Returns a Promise resolving to an object with:
252
+ await emitter.disconnect();
253
+ ```
111
254
 
112
- - `.on(event, listener)` โ€” Listen to typed events
113
- - `.off(event, listener)` โ€” Remove a listener
114
- - `.emitAuditPushed(payload)` โ€” Emit an AuditPushed event (if permitted)
115
- - `.emitAuditSlashed(id)` โ€” Emit an AuditSlashed event (if permitted)
116
- - `.startListener()` โ€” Start listening to events
117
- - `.stopListener()` โ€” Stop listening to events
255
+ > **Note:**
256
+ >
257
+ > - The emitter will automatically publish each event to both the global (zone 0) and the specific zone exchange.
258
+ > - You cannot construct an emitter for zoneId: 0, and you cannot specify zoneId per emit call.
118
259
 
119
- #### Options
260
+ ### ๐ŸŒ Listening to All Zones
120
261
 
121
- - `username` (string, required)
122
- - `password` (string, required)
123
- - `exchange` (string, optional): RabbitMQ exchange name (default: `glow.audit.v1.exchange`)
124
- - `queueName` (string, required): Use a pre-created queue (must be set up by an admin)
125
- - **Naming Requirement:** Both `queueName` and `exchange` must be dot-separated (e.g., 'glow.audit.v1.exchange').
262
+ You can listen to **all zones at once** by passing `zoneId: 0` to the listener. **Emitters must always use a specific zone.**
126
263
 
127
- ---
264
+ #### Listen to All Zones
128
265
 
129
- ## ๐Ÿ” Permissions & Credentials
266
+ ```ts
267
+ import { createGlowEventListener } from "@glowlabs-org/events-sdk";
130
268
 
131
- - **Listener credentials:** Can only subscribe to events. Cannot emit events or create new queues.
132
- - **Admin credentials:** Can subscribe, emit events, and create/bind new queues and exchanges.
269
+ const listener = createGlowEventListener({
270
+ username: "listener",
271
+ password: "your-password-here",
272
+ zoneId: 0, // special value for all zones
273
+ queueName: "my.precreated.queue",
274
+ });
133
275
 
134
- If you try to emit with listener credentials, the SDK will throw an error.
276
+ listener.onEvent("audit.pushed", 1, (event) => {
277
+ console.log("Received audit.pushed v1 from any zone:", event.farmId);
278
+ });
135
279
 
136
- ---
280
+ listener.onEvent("audit.slashed", 1, (event) => {
281
+ console.log(
282
+ "Received audit.slashed v1 from any zone:",
283
+ event.farmId,
284
+ event.slasher
285
+ );
286
+ });
137
287
 
138
- ## ๐Ÿ› ๏ธ Advanced: Admin & Queue Management
288
+ listener.onEvent("audit.pfees.paid", 1, (event) => {
289
+ console.log(
290
+ "Received audit.pfees.paid v1 from any zone:",
291
+ event.applicationId,
292
+ event.payer,
293
+ event.amount_12Decimals
294
+ );
295
+ });
139
296
 
140
- The SDK exposes helpers for programmatically creating, binding, and deleting exchanges and queues (admin credentials required). Use these for pre-creating queues for listeners, bootstrapping environments, or advanced queue management.
297
+ listener.onEvent("audit.pfees.paid", 2, (event) => {
298
+ console.log(
299
+ "Received audit.pfees.paid v2 from any zone:",
300
+ event.farmId,
301
+ event.payer,
302
+ event.amount_12Decimals
303
+ );
304
+ });
141
305
 
142
- ### `createExchange(options)`
306
+ listener.onEvent("application.created", 1, (event) => {
307
+ console.log(
308
+ "Received application.created v1 from any zone:",
309
+ event.gcaAddress,
310
+ event.lat,
311
+ event.lng,
312
+ event.estimatedCostOfPowerPerKWh,
313
+ event.estimatedKWhGeneratedPerYear,
314
+ event.installerCompanyName
315
+ );
316
+ });
143
317
 
144
- ### `bindQueueToExchange(options)`
318
+ await listener.start();
319
+ // To stop listening:
320
+ // await listener.stop();
321
+ ```
145
322
 
146
- ### `deleteExchange(options)`
323
+ ---
147
324
 
148
- ### `deleteQueue(options)`
325
+ ## ๐Ÿงช Validation & Error Handling
149
326
 
150
- See the end of this README for full admin/queue management usage examples.
327
+ - All events are validated at runtime using Zod schemas.
328
+ - If you emit or process an event with a `zoneName` that does not match the `zoneId`, an error is thrown. `zoneId: 0` and `zoneName: "All Zones"` are a valid pairing.
329
+ - If you emit or process an event with a `schemaVersion` for which no schema exists (e.g., `audit.pushed` v2), an error is thrown.
330
+ - If the payload does not match the schema, an error is thrown.
151
331
 
152
332
  ---
153
333
 
154
- ## ๐Ÿ“š More Details
334
+ ## ๐Ÿ” Permissions & Credentials
155
335
 
156
- ### Event Payload Zod Schema
336
+ - **Listener credentials:** Can only subscribe to events. Cannot emit events or create new queues.
337
+ - **Admin credentials:** Can subscribe, emit events, and create/bind new queues and exchanges.
157
338
 
158
- ```ts
159
- import { z } from "zod";
160
-
161
- export const AuditPushedDataZ = z
162
- .object({
163
- farmId: z.string().regex(/^0x[0-9a-fA-F]{64}$/, "bytes32 hex string"),
164
- protocolFeeUSDPrice_12Decimals: z
165
- .string()
166
- .regex(/^[0-9]+$/, "uint256 (decimal) โˆ’ 12 implied decimals"),
167
- tokenPrices: z.array(
168
- z.object({
169
- token: z.string().regex(/^0x[0-9a-fA-F]{40}$/, "ERCโ€‘20 address"),
170
- priceWAD_usd12Decimals: z.string().regex(/^[0-9]+$/),
171
- })
172
- ),
173
- expectedProduction_12Decimals: z.string().regex(/^[0-9]+$/),
174
- glowRewardSplits: z.array(
175
- z.object({
176
- receiver: z.string().regex(/^0x[0-9a-fA-F]{40}$/),
177
- percent: z.number().min(0).max(100),
178
- })
179
- ),
180
- cashRewardSplits: z.array(
181
- z.object({
182
- receiver: z.string().regex(/^0x[0-9a-fA-F]{40}$/),
183
- percent: z.number().min(0).max(100),
184
- })
185
- ),
186
- })
187
- .strict();
188
-
189
- export type AuditPushedData = z.infer<typeof AuditPushedDataZ>;
190
- ```
339
+ If you try to emit with listener credentials, the SDK will throw an error.
191
340
 
192
341
  ---
193
342
 
194
- ## ๐Ÿ—๏ธ Admin: Exchange and Queue Management
343
+ ## ๐Ÿ› ๏ธ Admin & Queue Management
195
344
 
196
345
  The SDK exposes helpers for programmatically creating, binding, and deleting exchanges and queues (admin credentials required). Use these for pre-creating queues for listeners, bootstrapping environments, or advanced queue management.
197
346
 
198
- ### Usage Example
347
+ ### `createExchange(options)`
348
+
349
+ Creates a topic exchange (default: `exchangeType = "topic"`).
350
+
351
+ ### `bindQueueToExchange(options)`
352
+
353
+ Binds a queue to a topic exchange. You can specify a `routingKey` for fine-grained event filtering:
354
+
355
+ - `routingKey = "#"` (default): all events
356
+ - `routingKey = "audit.pushed.v1"`: only audit.pushed v1 events
357
+ - `routingKey = "audit.pushed.*"`: all versions of audit.pushed
358
+
359
+ #### Example
199
360
 
200
361
  ```ts
201
362
  import {
@@ -208,26 +369,15 @@ import {
208
369
  await createExchange({
209
370
  username: "admin",
210
371
  password: "your-password-here",
211
- exchange: "glow.audit.v1.exchange",
372
+ exchange: "glow.zone-1.events",
212
373
  });
213
374
 
214
375
  await bindQueueToExchange({
215
376
  username: "admin",
216
377
  password: "your-password-here",
217
- exchange: "glow.audit.v1.exchange",
218
- queue: "glow-listener-queue",
219
- });
220
-
221
- await deleteExchange({
222
- username: "admin",
223
- password: "your-password-here",
224
- exchange: "glow.audit.v1.exchange",
225
- });
226
-
227
- await deleteQueue({
228
- username: "admin",
229
- password: "your-password-here",
378
+ exchange: "glow.zone-1.events",
230
379
  queue: "glow-listener-queue",
380
+ routingKey: "audit.pushed.v1", // only audit.pushed v1 events
231
381
  });
232
382
  ```
233
383
 
@@ -240,27 +390,27 @@ If your listener credentials only have `read` permission (no `configure`), you m
240
390
  ### 1. Admin: Pre-create and bind the queue
241
391
 
242
392
  ```ts
243
- import { createAndBindQueue } from "@glowlabs-org/events-sdk";
393
+ import { bindQueueToExchange } from "@glowlabs-org/events-sdk";
244
394
 
245
- await createAndBindQueue({
395
+ await bindQueueToExchange({
246
396
  username: "admin",
247
397
  password: "your-admin-password",
248
- exchange: "glow.audit.v1.exchange",
398
+ exchange: "glow.zone-1.events",
249
399
  queue: "my.precreated.queue",
400
+ routingKey: "audit.pushed.v1", // only audit.pushed v1 events
250
401
  });
251
402
  ```
252
403
 
253
404
  ### 2. Listener: Consume from the pre-created queue
254
405
 
255
406
  ```ts
256
- import { createGlowListener } from "@glowlabs-org/events-sdk";
407
+ import { createGlowEventListener } from "@glowlabs-org/events-sdk";
257
408
 
258
- const sdk = await createGlowListener({
409
+ const listener = createGlowEventListener({
259
410
  username: "listener",
260
411
  password: "your-listener-password",
412
+ zoneId: 1,
261
413
  queueName: "my.precreated.queue",
262
- // Optionally specify a custom exchange:
263
- // exchange: "glow.audit.v1.exchange",
264
414
  });
265
415
  ```
266
416
 
@@ -269,9 +419,19 @@ const sdk = await createGlowListener({
269
419
 
270
420
  ---
271
421
 
272
- ## ๐Ÿงฉ Advanced: Multiple Listeners/Producers
422
+ ## ๐Ÿงฉ Advanced: Multiple Listeners/Emitters
423
+
424
+ You can create multiple listeners or emitters in the same process, each with its own configuration (e.g., for different credentials, exchanges, or RabbitMQ URLs). This is useful for multi-tenant, multi-topic, or advanced scenarios. **Every listener receives every event for the bound routing key(s).**
425
+
426
+ ---
427
+
428
+ ## ๐Ÿงช Extending Event Types
429
+
430
+ To add new event types or versions:
273
431
 
274
- You can create multiple listeners or producers in the same process, each with its own configuration (e.g., for different credentials, exchanges, or RabbitMQ URLs). This is useful for multi-tenant, multi-topic, or advanced scenarios. **Every listener receives every event for the bound exchange.**
432
+ 1. Create a new schema in `src/schemas/`.
433
+ 2. Add the event type and version to `eventTypeRegistry` in `src/event-registry.ts`.
434
+ 3. Update the base event type in `src/base-event.ts` if needed.
275
435
 
276
436
  ---
277
437
 
package/dist/admin.d.ts CHANGED
@@ -7,32 +7,33 @@ export interface CreateAndBindQueueOptions {
7
7
  exchangeType?: string;
8
8
  queueOptions?: amqp.Options.AssertQueue;
9
9
  exchangeOptions?: amqp.Options.AssertExchange;
10
+ routingKey?: string;
10
11
  }
11
12
  /**
12
- * Create a RabbitMQ exchange (admin credentials required).
13
+ * Create a RabbitMQ topic exchange (admin credentials required).
13
14
  *
14
15
  * @param options - Connection and exchange options
15
16
  * @returns Promise<void>
16
17
  */
17
- export declare function createExchange({ username, password, exchange, exchangeType, exchangeOptions, }: Omit<CreateAndBindQueueOptions, "queue" | "queueOptions">): Promise<void>;
18
+ export declare function createExchange({ username, password, exchange, exchangeType, exchangeOptions, }: Omit<CreateAndBindQueueOptions, "queue" | "queueOptions" | "routingKey">): Promise<void>;
18
19
  /**
19
- * Create a RabbitMQ queue and bind it to an exchange (admin credentials required).
20
+ * Create a RabbitMQ queue and bind it to a topic exchange (admin credentials required).
20
21
  *
21
22
  * @param options - Connection, queue, and exchange options
22
23
  * @returns Promise<void>
23
24
  */
24
- export declare function bindQueueToExchange({ username, password, exchange, queue, exchangeType, queueOptions, exchangeOptions, }: CreateAndBindQueueOptions): Promise<void>;
25
+ export declare function bindQueueToExchange({ username, password, exchange, queue, exchangeType, queueOptions, exchangeOptions, routingKey, }: CreateAndBindQueueOptions): Promise<void>;
25
26
  /**
26
27
  * Delete a RabbitMQ exchange (admin credentials required).
27
28
  *
28
29
  * @param options - Connection and exchange options
29
30
  * @returns Promise<void>
30
31
  */
31
- export declare function deleteExchange({ username, password, exchange, }: Omit<CreateAndBindQueueOptions, "queue" | "queueOptions" | "exchangeType" | "exchangeOptions">): Promise<void>;
32
+ export declare function deleteExchange({ username, password, exchange, }: Omit<CreateAndBindQueueOptions, "queue" | "queueOptions" | "exchangeType" | "exchangeOptions" | "routingKey">): Promise<void>;
32
33
  /**
33
34
  * Delete a RabbitMQ queue (admin credentials required).
34
35
  *
35
36
  * @param options - Connection and queue options
36
37
  * @returns Promise<void>
37
38
  */
38
- export declare function deleteQueue({ username, password, queue, }: Omit<CreateAndBindQueueOptions, "exchange" | "exchangeType" | "exchangeOptions" | "queueOptions">): Promise<void>;
39
+ export declare function deleteQueue({ username, password, queue, }: Omit<CreateAndBindQueueOptions, "exchange" | "exchangeType" | "exchangeOptions" | "queueOptions" | "routingKey">): Promise<void>;
package/dist/admin.js CHANGED
@@ -12,16 +12,16 @@ function validateName(name, type) {
12
12
  // Require at least two segments separated by dots, only alphanumerics, dashes, underscores, and dots
13
13
  const pattern = /^[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/;
14
14
  if (!pattern.test(name)) {
15
- throw new Error(`${type} name '${name}' is invalid. Must be dot-separated (e.g., 'glow.audit.v1.exchange').`);
15
+ throw new Error(`${type} name '${name}' is invalid. Must be dot-separated (e.g., 'glow.zone-1.events').`);
16
16
  }
17
17
  }
18
18
  /**
19
- * Create a RabbitMQ exchange (admin credentials required).
19
+ * Create a RabbitMQ topic exchange (admin credentials required).
20
20
  *
21
21
  * @param options - Connection and exchange options
22
22
  * @returns Promise<void>
23
23
  */
24
- async function createExchange({ username, password, exchange, exchangeType = "fanout", exchangeOptions = { durable: true }, }) {
24
+ async function createExchange({ username, password, exchange, exchangeType = "topic", exchangeOptions = { durable: true }, }) {
25
25
  validateName(exchange, "exchange");
26
26
  const url = new URL(`amqp://${username}:${password}@turntable.proxy.rlwy.net:50784`);
27
27
  const conn = await amqplib_1.default.connect(url.toString());
@@ -31,12 +31,12 @@ async function createExchange({ username, password, exchange, exchangeType = "fa
31
31
  await conn.close();
32
32
  }
33
33
  /**
34
- * Create a RabbitMQ queue and bind it to an exchange (admin credentials required).
34
+ * Create a RabbitMQ queue and bind it to a topic exchange (admin credentials required).
35
35
  *
36
36
  * @param options - Connection, queue, and exchange options
37
37
  * @returns Promise<void>
38
38
  */
39
- async function bindQueueToExchange({ username, password, exchange, queue, exchangeType = "fanout", queueOptions = { durable: false }, exchangeOptions = { durable: true }, }) {
39
+ async function bindQueueToExchange({ username, password, exchange, queue, exchangeType = "topic", queueOptions = { durable: false }, exchangeOptions = { durable: true }, routingKey = "#", }) {
40
40
  validateName(exchange, "exchange");
41
41
  validateName(queue, "queue");
42
42
  const url = new URL(`amqp://${username}:${password}@turntable.proxy.rlwy.net:50784`);
@@ -44,7 +44,7 @@ async function bindQueueToExchange({ username, password, exchange, queue, exchan
44
44
  const channel = await conn.createChannel();
45
45
  await channel.assertExchange(exchange, exchangeType, exchangeOptions);
46
46
  await channel.assertQueue(queue, queueOptions);
47
- await channel.bindQueue(queue, exchange, "");
47
+ await channel.bindQueue(queue, exchange, routingKey);
48
48
  await channel.close();
49
49
  await conn.close();
50
50
  }