@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.
- package/README.md +311 -151
- package/dist/admin.d.ts +7 -6
- package/dist/admin.js +6 -6
- package/dist/base-event.d.ts +65 -0
- package/dist/base-event.js +22 -0
- package/dist/emitter.d.ts +17 -0
- package/dist/emitter.js +67 -0
- package/dist/event-registry.d.ts +2 -0
- package/dist/event-registry.js +27 -0
- package/dist/index.d.ts +2 -5
- package/dist/index.js +2 -5
- package/dist/listener.d.ts +14 -0
- package/dist/listener.js +88 -0
- package/dist/schemas/application-created.v1.d.ts +25 -0
- package/dist/schemas/application-created.v1.js +12 -0
- package/dist/schemas/audit-pfees-paid.v1.d.ts +15 -0
- package/dist/schemas/audit-pfees-paid.v1.js +14 -0
- package/dist/schemas/audit-pfees-paid.v2.d.ts +15 -0
- package/dist/schemas/audit-pfees-paid.v2.js +14 -0
- package/dist/schemas/audit-pushed.v1.d.ts +15 -0
- package/dist/schemas/audit-pushed.v1.js +13 -0
- package/dist/schemas/audit-slashed.v1.d.ts +12 -0
- package/dist/schemas/audit-slashed.v1.js +10 -0
- package/dist/types.d.ts +32 -21
- package/dist/utils.d.ts +20 -0
- package/dist/utils.js +45 -0
- package/dist/zones.d.ts +7 -0
- package/dist/zones.js +8 -0
- package/package.json +1 -1
- package/dist/consumer.d.ts +0 -14
- package/dist/consumer.js +0 -84
- package/dist/glow-listener.d.ts +0 -31
- package/dist/glow-listener.js +0 -74
- package/dist/producer.d.ts +0 -8
- package/dist/producer.js +0 -46
- package/dist/schemas/auditPushed.d.ts +0 -69
- package/dist/schemas/auditPushed.js +0 -42
- package/dist/typed-emitter.d.ts +0 -7
- 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 (
|
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
|
-
|
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
|
-
|
18
|
-
| -------------- | ------------------ | -------------------------------- |
|
19
|
-
| `AuditPushed` | `AuditPushedEvent` | Emitted when an audit is pushed |
|
20
|
-
| `AuditSlashed` | `AuditSlashedId` | Emitted when an audit is slashed |
|
87
|
+
**Validation:**
|
21
88
|
|
22
|
-
|
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
|
-
|
26
|
-
|
27
|
-
//
|
28
|
-
|
29
|
-
|
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
|
133
|
+
### Listen to Specific Event Types/Versions
|
45
134
|
|
46
135
|
```ts
|
47
|
-
import {
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
59
|
-
|
60
|
-
|
149
|
+
listener.onEvent("audit.slashed", 1, (event) => {
|
150
|
+
console.log("Received audit.slashed v1:", event.farmId, event.slasher);
|
151
|
+
});
|
61
152
|
|
62
|
-
|
63
|
-
|
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
|
-
|
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
|
-
|
69
|
-
|
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
|
-
|
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 {
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
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
|
-
|
95
|
-
|
96
|
-
});
|
97
|
-
|
98
|
-
sdk.emitAuditSlashed?.("0x...");
|
99
|
-
}
|
207
|
+
},
|
208
|
+
});
|
100
209
|
|
101
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
252
|
+
await emitter.disconnect();
|
253
|
+
```
|
111
254
|
|
112
|
-
|
113
|
-
|
114
|
-
-
|
115
|
-
-
|
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
|
-
|
260
|
+
### ๐ Listening to All Zones
|
120
261
|
|
121
|
-
|
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
|
-
|
266
|
+
```ts
|
267
|
+
import { createGlowEventListener } from "@glowlabs-org/events-sdk";
|
130
268
|
|
131
|
-
|
132
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
318
|
+
await listener.start();
|
319
|
+
// To stop listening:
|
320
|
+
// await listener.stop();
|
321
|
+
```
|
145
322
|
|
146
|
-
|
323
|
+
---
|
147
324
|
|
148
|
-
|
325
|
+
## ๐งช Validation & Error Handling
|
149
326
|
|
150
|
-
|
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
|
-
##
|
334
|
+
## ๐ Permissions & Credentials
|
155
335
|
|
156
|
-
|
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
|
-
|
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
|
-
##
|
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
|
-
###
|
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.
|
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.
|
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 {
|
393
|
+
import { bindQueueToExchange } from "@glowlabs-org/events-sdk";
|
244
394
|
|
245
|
-
await
|
395
|
+
await bindQueueToExchange({
|
246
396
|
username: "admin",
|
247
397
|
password: "your-admin-password",
|
248
|
-
exchange: "glow.
|
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 {
|
407
|
+
import { createGlowEventListener } from "@glowlabs-org/events-sdk";
|
257
408
|
|
258
|
-
const
|
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/
|
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
|
-
|
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
|
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.
|
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 = "
|
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
|
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 = "
|
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
|
}
|