@glowlabs-org/events-sdk 0.1.0 → 1.0.0
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 +247 -153
- package/dist/admin.d.ts +7 -6
- package/dist/admin.js +6 -6
- package/dist/base-event.d.ts +32 -0
- package/dist/base-event.js +17 -0
- package/dist/emitter.d.ts +18 -0
- package/dist/emitter.js +59 -0
- package/dist/event-registry.d.ts +2 -0
- package/dist/event-registry.js +19 -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/audit-pfees-paid.v1.d.ts +15 -0
- package/dist/schemas/audit-pfees-paid.v1.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 +19 -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 +8 -11
- 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
@@ -0,0 +1,18 @@
|
|
1
|
+
import { EventPayload, EventType, EventVersion } from "./types";
|
2
|
+
interface CreateGlowEventEmitterOptions {
|
3
|
+
username: string;
|
4
|
+
password: string;
|
5
|
+
zoneId: number;
|
6
|
+
exchangePrefix?: string;
|
7
|
+
}
|
8
|
+
interface EmitEventArgs<T extends EventType, V extends EventVersion<T>> {
|
9
|
+
eventType: T;
|
10
|
+
schemaVersion: V;
|
11
|
+
zoneId: number;
|
12
|
+
payload: EventPayload<T, V>;
|
13
|
+
}
|
14
|
+
export declare function createGlowEventEmitter({ username, password, zoneId, exchangePrefix, }: CreateGlowEventEmitterOptions): {
|
15
|
+
emit: <T extends EventType, V extends EventVersion<T>>(args: EmitEventArgs<T, V>) => Promise<void>;
|
16
|
+
disconnect: () => Promise<void>;
|
17
|
+
};
|
18
|
+
export {};
|
package/dist/emitter.js
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.createGlowEventEmitter = createGlowEventEmitter;
|
7
|
+
const amqplib_1 = __importDefault(require("amqplib"));
|
8
|
+
const utils_1 = require("./utils");
|
9
|
+
const uuid_1 = require("uuid");
|
10
|
+
const zones_1 = require("./zones");
|
11
|
+
function createGlowEventEmitter({ username, password, zoneId, exchangePrefix = "glow.zone-", }) {
|
12
|
+
let amqpConnection = null;
|
13
|
+
let amqpChannel = null;
|
14
|
+
// Use a special exchange for all zones if zoneId is 0
|
15
|
+
const exchangeName = `${exchangePrefix}${zoneId}.events`;
|
16
|
+
function buildAmqpUrl() {
|
17
|
+
const url = new URL(`amqp://${username}:${password}@turntable.proxy.rlwy.net:50784`);
|
18
|
+
return url.toString();
|
19
|
+
}
|
20
|
+
async function connectIfNeeded() {
|
21
|
+
if (!amqpConnection) {
|
22
|
+
amqpConnection = (await amqplib_1.default.connect(buildAmqpUrl()));
|
23
|
+
amqpChannel = (await amqpConnection.createChannel());
|
24
|
+
}
|
25
|
+
if (amqpChannel) {
|
26
|
+
await amqpChannel.assertExchange(exchangeName, "topic", {
|
27
|
+
durable: true,
|
28
|
+
});
|
29
|
+
}
|
30
|
+
}
|
31
|
+
async function emit(args) {
|
32
|
+
const { eventType, schemaVersion, zoneId, payload } = args;
|
33
|
+
const zoneName = zones_1.zoneMap[String(zoneId)];
|
34
|
+
if (!zoneName)
|
35
|
+
throw new Error(`Invalid zoneId: ${zoneId}`);
|
36
|
+
const event = {
|
37
|
+
id: (0, uuid_1.v4)(),
|
38
|
+
eventType,
|
39
|
+
schemaVersion,
|
40
|
+
zoneId,
|
41
|
+
zoneName,
|
42
|
+
timeStamp: Date.now(),
|
43
|
+
payload,
|
44
|
+
};
|
45
|
+
(0, utils_1.validateZoneNameAndId)(event.zoneId, event.zoneName);
|
46
|
+
(0, utils_1.validateEventPayload)(event.eventType, event.schemaVersion, event);
|
47
|
+
const routingKey = `${event.eventType}.v${event.schemaVersion}`;
|
48
|
+
await connectIfNeeded();
|
49
|
+
amqpChannel.publish(exchangeName, routingKey, Buffer.from(JSON.stringify(event)), { persistent: true });
|
50
|
+
}
|
51
|
+
async function disconnect() {
|
52
|
+
if (amqpConnection) {
|
53
|
+
await amqpConnection.close();
|
54
|
+
amqpConnection = null;
|
55
|
+
amqpChannel = null;
|
56
|
+
}
|
57
|
+
}
|
58
|
+
return { emit, disconnect };
|
59
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.getEventSchema = getEventSchema;
|
4
|
+
const audit_pushed_v1_1 = require("./schemas/audit-pushed.v1");
|
5
|
+
const audit_slashed_v1_1 = require("./schemas/audit-slashed.v1");
|
6
|
+
const audit_pfees_paid_v1_1 = require("./schemas/audit-pfees-paid.v1");
|
7
|
+
const base_event_1 = require("./base-event");
|
8
|
+
const eventTypeRegistry = {
|
9
|
+
"audit.pushed:v1": base_event_1.baseEventZ.extend({ payload: audit_pushed_v1_1.auditPushedV1PayloadZ }),
|
10
|
+
"audit.slashed:v1": base_event_1.baseEventZ.extend({ payload: audit_slashed_v1_1.auditSlashedV1PayloadZ }),
|
11
|
+
"audit.pfees.paid:v1": base_event_1.baseEventZ.extend({
|
12
|
+
payload: audit_pfees_paid_v1_1.auditPfeesPaidV1PayloadZ,
|
13
|
+
}),
|
14
|
+
// Add more event types/versions here
|
15
|
+
};
|
16
|
+
function getEventSchema(eventType, version) {
|
17
|
+
const key = `${eventType}:v${version}`;
|
18
|
+
return eventTypeRegistry[key];
|
19
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
@@ -14,9 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
15
15
|
};
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
17
|
-
__exportStar(require("./
|
18
|
-
__exportStar(require("./
|
19
|
-
__exportStar(require("./schemas/auditPushed"), exports);
|
20
|
-
__exportStar(require("./glow-listener"), exports);
|
21
|
-
__exportStar(require("./types"), exports);
|
17
|
+
__exportStar(require("./emitter"), exports);
|
18
|
+
__exportStar(require("./listener"), exports);
|
22
19
|
__exportStar(require("./admin"), exports);
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import type { EventType, EventVersion, EventPayload, GlowEvent } from "./types";
|
2
|
+
interface CreateGlowEventListenerOptions {
|
3
|
+
username: string;
|
4
|
+
password: string;
|
5
|
+
zoneId: number;
|
6
|
+
queueName?: string;
|
7
|
+
exchangePrefix?: string;
|
8
|
+
}
|
9
|
+
export declare function createGlowEventListener({ username, password, zoneId, queueName, exchangePrefix, }: CreateGlowEventListenerOptions): {
|
10
|
+
onEvent: <T extends EventType, V extends EventVersion<T>>(eventType: T, schemaVersion: V, handler: (event: GlowEvent<EventPayload<T, V>>) => void) => void;
|
11
|
+
start: () => Promise<void>;
|
12
|
+
stop: () => Promise<void>;
|
13
|
+
};
|
14
|
+
export {};
|
package/dist/listener.js
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
+
};
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
+
exports.createGlowEventListener = createGlowEventListener;
|
7
|
+
const amqplib_1 = __importDefault(require("amqplib"));
|
8
|
+
const event_registry_1 = require("./event-registry");
|
9
|
+
const utils_1 = require("./utils");
|
10
|
+
function createGlowEventListener({ username, password, zoneId, queueName, exchangePrefix = "glow.zone-", }) {
|
11
|
+
let amqpConnection = null;
|
12
|
+
let amqpChannel = null;
|
13
|
+
let internalQueueName;
|
14
|
+
let consumerTag = null;
|
15
|
+
// Use a special exchange for all zones if zoneId is 0
|
16
|
+
const exchangeName = `${exchangePrefix}${zoneId}.events`;
|
17
|
+
const eventHandlers = [];
|
18
|
+
function buildAmqpUrl() {
|
19
|
+
const url = new URL(`amqp://${username}:${password}@turntable.proxy.rlwy.net:50784`);
|
20
|
+
return url.toString();
|
21
|
+
}
|
22
|
+
async function connectIfNeeded(bindingPattern) {
|
23
|
+
if (!amqpConnection) {
|
24
|
+
amqpConnection = (await amqplib_1.default.connect(buildAmqpUrl()));
|
25
|
+
amqpChannel = (await amqpConnection.createChannel());
|
26
|
+
}
|
27
|
+
if (amqpChannel) {
|
28
|
+
if (queueName) {
|
29
|
+
internalQueueName = queueName;
|
30
|
+
}
|
31
|
+
else {
|
32
|
+
await amqpChannel.assertExchange(exchangeName, "topic", {
|
33
|
+
durable: true,
|
34
|
+
});
|
35
|
+
const q = await amqpChannel.assertQueue("", { exclusive: true });
|
36
|
+
internalQueueName = q.queue;
|
37
|
+
await amqpChannel.bindQueue(internalQueueName, exchangeName, bindingPattern);
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
function onEvent(eventType, schemaVersion, handler) {
|
42
|
+
const bindingPattern = `${String(eventType)}.v${String(schemaVersion)}`;
|
43
|
+
eventHandlers.push({
|
44
|
+
bindingPattern,
|
45
|
+
handler: (event, meta) => handler(event),
|
46
|
+
});
|
47
|
+
}
|
48
|
+
async function start() {
|
49
|
+
for (const { bindingPattern } of eventHandlers) {
|
50
|
+
await connectIfNeeded(bindingPattern);
|
51
|
+
}
|
52
|
+
if (!internalQueueName)
|
53
|
+
throw new Error("Queue not initialized");
|
54
|
+
consumerTag = (await amqpChannel.consume(internalQueueName, (msg) => {
|
55
|
+
if (msg) {
|
56
|
+
try {
|
57
|
+
const decoded = JSON.parse(msg.content.toString());
|
58
|
+
const [eventType, versionStr] = msg.fields.routingKey.split(".v");
|
59
|
+
const schemaVersion = Number(versionStr);
|
60
|
+
(0, utils_1.validateZoneNameAndId)(decoded.zoneId, decoded.zoneName);
|
61
|
+
(0, utils_1.validateEventPayload)(eventType, schemaVersion, decoded);
|
62
|
+
const event = (0, event_registry_1.getEventSchema)(eventType, schemaVersion).parse(decoded);
|
63
|
+
const handlerEntry = eventHandlers.find((h) => h.bindingPattern === `${eventType}.v${schemaVersion}`);
|
64
|
+
if (handlerEntry)
|
65
|
+
handlerEntry.handler(event, { eventType, schemaVersion });
|
66
|
+
amqpChannel.ack(msg);
|
67
|
+
}
|
68
|
+
catch (error) {
|
69
|
+
console.error("Failed to process event:", error);
|
70
|
+
amqpChannel.nack(msg, false, false);
|
71
|
+
}
|
72
|
+
}
|
73
|
+
}, { noAck: false })).consumerTag;
|
74
|
+
}
|
75
|
+
async function stop() {
|
76
|
+
if (amqpChannel && consumerTag && internalQueueName) {
|
77
|
+
await amqpChannel.cancel(consumerTag);
|
78
|
+
consumerTag = null;
|
79
|
+
}
|
80
|
+
if (amqpConnection) {
|
81
|
+
await amqpConnection.close();
|
82
|
+
amqpConnection = null;
|
83
|
+
amqpChannel = null;
|
84
|
+
internalQueueName = undefined;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
return { onEvent, start, stop };
|
88
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { z } from "zod";
|
2
|
+
export declare const auditPfeesPaidV1PayloadZ: z.ZodObject<{
|
3
|
+
farmId: z.ZodString;
|
4
|
+
payer: z.ZodString;
|
5
|
+
amount_12Decimals: z.ZodString;
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
7
|
+
farmId: string;
|
8
|
+
payer: string;
|
9
|
+
amount_12Decimals: string;
|
10
|
+
}, {
|
11
|
+
farmId: string;
|
12
|
+
payer: string;
|
13
|
+
amount_12Decimals: string;
|
14
|
+
}>;
|
15
|
+
export type AuditPfeesPaidV1Payload = z.infer<typeof auditPfeesPaidV1PayloadZ>;
|
@@ -0,0 +1,14 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.auditPfeesPaidV1PayloadZ = void 0;
|
4
|
+
const zod_1 = require("zod");
|
5
|
+
const hexBytes32 = /^0x[0-9a-fA-F]{64}$/;
|
6
|
+
const ethAddress = /^0x[0-9a-fA-F]{40}$/;
|
7
|
+
const uint256 = /^[0-9]+$/;
|
8
|
+
exports.auditPfeesPaidV1PayloadZ = zod_1.z.object({
|
9
|
+
farmId: zod_1.z.string().regex(hexBytes32, "bytes32 hex string"),
|
10
|
+
payer: zod_1.z.string().regex(ethAddress, "Ethereum address"),
|
11
|
+
amount_12Decimals: zod_1.z
|
12
|
+
.string()
|
13
|
+
.regex(uint256, "uint256 (decimal) − 12 implied decimals"),
|
14
|
+
});
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { z } from "zod";
|
2
|
+
export declare const auditPushedV1PayloadZ: z.ZodObject<{
|
3
|
+
farmId: z.ZodString;
|
4
|
+
protocolFeeUSDPrice_12Decimals: z.ZodString;
|
5
|
+
expectedProduction_12Decimals: z.ZodString;
|
6
|
+
}, "strip", z.ZodTypeAny, {
|
7
|
+
farmId: string;
|
8
|
+
protocolFeeUSDPrice_12Decimals: string;
|
9
|
+
expectedProduction_12Decimals: string;
|
10
|
+
}, {
|
11
|
+
farmId: string;
|
12
|
+
protocolFeeUSDPrice_12Decimals: string;
|
13
|
+
expectedProduction_12Decimals: string;
|
14
|
+
}>;
|
15
|
+
export type AuditPushedV1Payload = z.infer<typeof auditPushedV1PayloadZ>;
|
@@ -0,0 +1,13 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.auditPushedV1PayloadZ = void 0;
|
4
|
+
const zod_1 = require("zod");
|
5
|
+
const hexBytes32 = /^0x[0-9a-fA-F]{64}$/;
|
6
|
+
const uint256 = /^[0-9]+$/; // unsigned big‑int as decimal string
|
7
|
+
exports.auditPushedV1PayloadZ = zod_1.z.object({
|
8
|
+
farmId: zod_1.z.string().regex(hexBytes32, "bytes32 hex string"),
|
9
|
+
protocolFeeUSDPrice_12Decimals: zod_1.z
|
10
|
+
.string()
|
11
|
+
.regex(uint256, "uint256 (decimal) − 12 implied decimals"),
|
12
|
+
expectedProduction_12Decimals: zod_1.z.string().regex(uint256),
|
13
|
+
});
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { z } from "zod";
|
2
|
+
export declare const auditSlashedV1PayloadZ: z.ZodObject<{
|
3
|
+
farmId: z.ZodString;
|
4
|
+
slasher: z.ZodString;
|
5
|
+
}, "strip", z.ZodTypeAny, {
|
6
|
+
farmId: string;
|
7
|
+
slasher: string;
|
8
|
+
}, {
|
9
|
+
farmId: string;
|
10
|
+
slasher: string;
|
11
|
+
}>;
|
12
|
+
export type AuditSlashedV1Payload = z.infer<typeof auditSlashedV1PayloadZ>;
|
@@ -0,0 +1,10 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.auditSlashedV1PayloadZ = void 0;
|
4
|
+
const zod_1 = require("zod");
|
5
|
+
const hexBytes32 = /^0x[0-9a-fA-F]{64}$/;
|
6
|
+
const ethAddress = /^0x[0-9a-fA-F]{40}$/;
|
7
|
+
exports.auditSlashedV1PayloadZ = zod_1.z.object({
|
8
|
+
farmId: zod_1.z.string().regex(hexBytes32, "bytes32 hex string"),
|
9
|
+
slasher: zod_1.z.string().regex(ethAddress, "Ethereum address"),
|
10
|
+
});
|
package/dist/types.d.ts
CHANGED
@@ -1,23 +1,21 @@
|
|
1
|
-
import type {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
import type { BaseEvent } from "./base-event";
|
2
|
+
import type { AuditPushedV1Payload } from "./schemas/audit-pushed.v1";
|
3
|
+
import type { AuditSlashedV1Payload } from "./schemas/audit-slashed.v1";
|
4
|
+
import type { AuditPfeesPaidV1Payload } from "./schemas/audit-pfees-paid.v1";
|
5
|
+
export type EventType = BaseEvent["eventType"];
|
6
|
+
export type EventVersion<T extends EventType> = T extends "audit.pushed" ? 1 : T extends "audit.slashed" ? 1 : T extends "audit.pfees.paid" ? 1 : never;
|
7
|
+
export interface EventPayloadMap {
|
8
|
+
"audit.pushed": {
|
9
|
+
1: AuditPushedV1Payload;
|
10
|
+
};
|
11
|
+
"audit.slashed": {
|
12
|
+
1: AuditSlashedV1Payload;
|
13
|
+
};
|
14
|
+
"audit.pfees.paid": {
|
15
|
+
1: AuditPfeesPaidV1Payload;
|
16
|
+
};
|
8
17
|
}
|
9
|
-
export type
|
10
|
-
export interface
|
11
|
-
|
12
|
-
password: string;
|
13
|
-
permissions: SdkPermission[];
|
14
|
-
}
|
15
|
-
export type GlowListenerEvent = "AuditPushed" | "AuditSlashed";
|
16
|
-
export interface GlowListener {
|
17
|
-
on: <K extends GlowListenerEvent>(event: K, listener: (...args: GlowEvents[K]) => void) => () => void;
|
18
|
-
off: <K extends GlowListenerEvent>(event: K, listener: (...args: GlowEvents[K]) => void) => void;
|
19
|
-
emitAuditPushed?: (data: AuditPushedEvent) => Promise<void>;
|
20
|
-
emitAuditSlashed?: (id: AuditSlashedId) => void;
|
21
|
-
startListener: () => Promise<void>;
|
22
|
-
stopListener: () => Promise<void>;
|
18
|
+
export type EventPayload<T extends EventType, V extends EventVersion<T>> = T extends keyof EventPayloadMap ? V extends keyof EventPayloadMap[T] ? EventPayloadMap[T][V] : never : never;
|
19
|
+
export interface GlowEvent<TPayload> extends Omit<BaseEvent, "payload"> {
|
20
|
+
payload: TPayload;
|
23
21
|
}
|
package/dist/utils.d.ts
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
/**
|
2
|
+
* Validates that the zoneName matches the zoneId using the zoneMap.
|
3
|
+
* Throws an error if the values are inconsistent.
|
4
|
+
*/
|
5
|
+
export declare function validateZoneNameAndId(zoneId: number, zoneName: string): void;
|
6
|
+
/**
|
7
|
+
* Validates the event payload against the schema for the given eventType and schemaVersion.
|
8
|
+
* Throws an error if the schema does not exist or the payload is invalid.
|
9
|
+
*/
|
10
|
+
export declare function validateEventPayload(eventType: string, schemaVersion: number, payload: any): void;
|
11
|
+
/**
|
12
|
+
* Processes a message object as the listener would, validating zone and schema.
|
13
|
+
* Throws if validation fails, returns the parsed event otherwise.
|
14
|
+
*/
|
15
|
+
export declare function processEventMessage(msg: {
|
16
|
+
content: Buffer;
|
17
|
+
fields: {
|
18
|
+
routingKey: string;
|
19
|
+
};
|
20
|
+
}): any;
|
package/dist/utils.js
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.validateZoneNameAndId = validateZoneNameAndId;
|
4
|
+
exports.validateEventPayload = validateEventPayload;
|
5
|
+
exports.processEventMessage = processEventMessage;
|
6
|
+
const event_registry_1 = require("./event-registry");
|
7
|
+
const zones_1 = require("./zones");
|
8
|
+
/**
|
9
|
+
* Validates that the zoneName matches the zoneId using the zoneMap.
|
10
|
+
* Throws an error if the values are inconsistent.
|
11
|
+
*/
|
12
|
+
function validateZoneNameAndId(zoneId, zoneName) {
|
13
|
+
if (typeof zoneId === "number" &&
|
14
|
+
String(zoneId) in zones_1.zoneMap &&
|
15
|
+
zoneName !== zones_1.zoneMap[String(zoneId)]) {
|
16
|
+
throw new Error(`zoneName '${zoneName}' does not match zoneId '${zoneId}' (expected '${zones_1.zoneMap[String(zoneId)]}')`);
|
17
|
+
}
|
18
|
+
}
|
19
|
+
/**
|
20
|
+
* Validates the event payload against the schema for the given eventType and schemaVersion.
|
21
|
+
* Throws an error if the schema does not exist or the payload is invalid.
|
22
|
+
*/
|
23
|
+
function validateEventPayload(eventType, schemaVersion, payload) {
|
24
|
+
const schema = (0, event_registry_1.getEventSchema)(eventType, schemaVersion);
|
25
|
+
if (!schema)
|
26
|
+
throw new Error(`No schema for event: ${eventType} v${schemaVersion}`);
|
27
|
+
try {
|
28
|
+
schema.parse(payload);
|
29
|
+
}
|
30
|
+
catch (err) {
|
31
|
+
throw new Error(`Payload does not match schema for event: ${eventType} v${schemaVersion}: ${err.message}`);
|
32
|
+
}
|
33
|
+
}
|
34
|
+
/**
|
35
|
+
* Processes a message object as the listener would, validating zone and schema.
|
36
|
+
* Throws if validation fails, returns the parsed event otherwise.
|
37
|
+
*/
|
38
|
+
function processEventMessage(msg) {
|
39
|
+
const decoded = JSON.parse(msg.content.toString());
|
40
|
+
const [eventType, versionStr] = msg.fields.routingKey.split(".v");
|
41
|
+
const schemaVersion = Number(versionStr);
|
42
|
+
validateZoneNameAndId(decoded.zoneId, decoded.zoneName);
|
43
|
+
validateEventPayload(eventType, schemaVersion, decoded);
|
44
|
+
return (0, event_registry_1.getEventSchema)(eventType, schemaVersion).parse(decoded);
|
45
|
+
}
|
package/dist/zones.d.ts
ADDED
package/dist/zones.js
ADDED
package/package.json
CHANGED
@@ -1,18 +1,15 @@
|
|
1
1
|
{
|
2
2
|
"name": "@glowlabs-org/events-sdk",
|
3
|
-
"version": "
|
4
|
-
"description": "Typed event SDK for Glow, powered by
|
3
|
+
"version": "1.0.0",
|
4
|
+
"description": "Typed event SDK for Glow, powered by RabbitMQ and Zod.",
|
5
5
|
"main": "dist/index.js",
|
6
6
|
"types": "dist/index.d.ts",
|
7
7
|
"files": [
|
8
8
|
"dist"
|
9
9
|
],
|
10
|
-
"publishConfig": {
|
11
|
-
"access": "public"
|
12
|
-
},
|
13
10
|
"repository": {
|
14
11
|
"type": "git",
|
15
|
-
"url": "https://github.com/glowlabs-org/glow-events
|
12
|
+
"url": "https://github.com/glowlabs-org/glow-events"
|
16
13
|
},
|
17
14
|
"author": "Lironie <julien@glowlabs.org>",
|
18
15
|
"license": "MIT",
|
@@ -25,12 +22,9 @@
|
|
25
22
|
"typescript",
|
26
23
|
"glow"
|
27
24
|
],
|
28
|
-
"scripts": {
|
29
|
-
"build": "tsc -p tsconfig.json",
|
30
|
-
"prepublishOnly": "npm run build"
|
31
|
-
},
|
32
25
|
"dependencies": {
|
33
26
|
"@kafkajs/confluent-schema-registry": "^3.8.0",
|
27
|
+
"@types/node": "^20.0.0",
|
34
28
|
"amqplib": "^0.10.8",
|
35
29
|
"json-schema": "^0.4.0",
|
36
30
|
"kafkajs": "^2.2.4",
|
@@ -42,5 +36,8 @@
|
|
42
36
|
"@types/amqplib": "^0.10.7",
|
43
37
|
"@types/json-schema": "^7.0.15",
|
44
38
|
"typescript": "^5.8.3"
|
39
|
+
},
|
40
|
+
"scripts": {
|
41
|
+
"build": "tsc -p tsconfig.json"
|
45
42
|
}
|
46
|
-
}
|
43
|
+
}
|
package/dist/consumer.d.ts
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
import { AuditPushedDataZ } from "./schemas/auditPushed";
|
2
|
-
import { z } from "zod";
|
3
|
-
export declare function createAuditPushedConsumer({ username, password, onEvent, skipAssertExchange, queueName, skipAssertQueue, exchange, }: {
|
4
|
-
username: string;
|
5
|
-
password: string;
|
6
|
-
onEvent: (evt: z.infer<typeof AuditPushedDataZ>) => void;
|
7
|
-
skipAssertExchange?: boolean;
|
8
|
-
queueName?: string;
|
9
|
-
skipAssertQueue?: boolean;
|
10
|
-
exchange?: string;
|
11
|
-
}): {
|
12
|
-
start: () => Promise<void>;
|
13
|
-
stop: () => Promise<void>;
|
14
|
-
};
|
package/dist/consumer.js
DELETED
@@ -1,84 +0,0 @@
|
|
1
|
-
"use strict";
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
4
|
-
};
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
6
|
-
exports.createAuditPushedConsumer = createAuditPushedConsumer;
|
7
|
-
const amqplib_1 = __importDefault(require("amqplib"));
|
8
|
-
const auditPushed_1 = require("./schemas/auditPushed");
|
9
|
-
const EXCHANGE = "glow.audit.v1.exchange";
|
10
|
-
function createAuditPushedConsumer({ username, password, onEvent, skipAssertExchange = false, queueName, skipAssertQueue = false, exchange = "glow.audit.v1.exchange", }) {
|
11
|
-
// amqplib types are not always compatible with runtime objects, so we use 'as any' as a workaround
|
12
|
-
let connection = null;
|
13
|
-
let channel = null;
|
14
|
-
let internalQueueName;
|
15
|
-
let consumerTag = null;
|
16
|
-
function buildAmqpUrl() {
|
17
|
-
const url = new URL(`amqp://${username}:${password}@turntable.proxy.rlwy.net:50784`);
|
18
|
-
return url.toString();
|
19
|
-
}
|
20
|
-
async function connect() {
|
21
|
-
if (!connection) {
|
22
|
-
connection = (await amqplib_1.default.connect(buildAmqpUrl()));
|
23
|
-
channel = (await connection.createChannel());
|
24
|
-
}
|
25
|
-
if (channel) {
|
26
|
-
// Strict read-only: skip all asserts/binds, just use the provided queueName
|
27
|
-
if (skipAssertQueue) {
|
28
|
-
if (!queueName) {
|
29
|
-
throw new Error("[Glow SDK] In strict read-only mode (skipAssertQueue: true), you must provide a queueName.");
|
30
|
-
}
|
31
|
-
internalQueueName = queueName;
|
32
|
-
// Do not assert or bind anything
|
33
|
-
return;
|
34
|
-
}
|
35
|
-
// Default pattern: assert exchange, create/bind queue
|
36
|
-
if (!skipAssertExchange) {
|
37
|
-
await channel.assertExchange(exchange, "fanout", { durable: true });
|
38
|
-
}
|
39
|
-
const q = await channel.assertQueue(queueName !== null && queueName !== void 0 ? queueName : "", {
|
40
|
-
exclusive: !queueName,
|
41
|
-
});
|
42
|
-
internalQueueName = q.queue;
|
43
|
-
await channel.bindQueue(internalQueueName, exchange, "");
|
44
|
-
}
|
45
|
-
}
|
46
|
-
async function start() {
|
47
|
-
try {
|
48
|
-
await connect();
|
49
|
-
if (!internalQueueName)
|
50
|
-
throw new Error("Queue not initialized");
|
51
|
-
const { consumerTag: tag } = await channel.consume(internalQueueName, (msg) => {
|
52
|
-
if (msg) {
|
53
|
-
try {
|
54
|
-
const decoded = JSON.parse(msg.content.toString());
|
55
|
-
onEvent(auditPushed_1.AuditPushedDataZ.parse(decoded));
|
56
|
-
channel.ack(msg);
|
57
|
-
}
|
58
|
-
catch (error) {
|
59
|
-
console.error("Failed to process audit pushed event:", error);
|
60
|
-
channel.nack(msg, false, false);
|
61
|
-
}
|
62
|
-
}
|
63
|
-
}, { noAck: false });
|
64
|
-
consumerTag = tag;
|
65
|
-
}
|
66
|
-
catch (error) {
|
67
|
-
console.error("Failed to start audit pushed consumer:", error);
|
68
|
-
throw error;
|
69
|
-
}
|
70
|
-
}
|
71
|
-
async function stop() {
|
72
|
-
if (channel && consumerTag && internalQueueName) {
|
73
|
-
await channel.cancel(consumerTag);
|
74
|
-
consumerTag = null;
|
75
|
-
}
|
76
|
-
if (connection) {
|
77
|
-
await connection.close();
|
78
|
-
connection = null;
|
79
|
-
channel = null;
|
80
|
-
internalQueueName = undefined;
|
81
|
-
}
|
82
|
-
}
|
83
|
-
return { start, stop };
|
84
|
-
}
|
package/dist/glow-listener.d.ts
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
import type { GlowListener } from "./types";
|
2
|
-
/**
|
3
|
-
* Options for creating a GlowListener instance.
|
4
|
-
*
|
5
|
-
* - Use admin credentials to emit events and create/bind queues/exchanges.
|
6
|
-
* - Use listener credentials for read-only event consumption (with pre-created queues).
|
7
|
-
* - Specify a custom exchange for multi-exchange support.
|
8
|
-
*/
|
9
|
-
interface GlowListenerOptions {
|
10
|
-
/** Username for RabbitMQ authentication (admin or listener) */
|
11
|
-
username: string;
|
12
|
-
/** Password for RabbitMQ authentication */
|
13
|
-
password: string;
|
14
|
-
/**
|
15
|
-
* The pre-created queue to use for the listener. Must be set up by an admin.
|
16
|
-
*/
|
17
|
-
queueName: string;
|
18
|
-
/**
|
19
|
-
* The pre-created exchange to use. Must be set up by an admin. Defaults to 'glow.audit.v1.exchange'.
|
20
|
-
*/
|
21
|
-
exchange?: string;
|
22
|
-
}
|
23
|
-
/**
|
24
|
-
* Create a GlowListener instance for consuming and emitting typed events on the Glow platform.
|
25
|
-
*
|
26
|
-
* - Use admin credentials to emit events and manage queues/exchanges.
|
27
|
-
* - Use listener credentials for read-only event consumption.
|
28
|
-
* - Supports specifying a custom exchange for multi-exchange scenarios.
|
29
|
-
*/
|
30
|
-
export declare function createGlowListener({ username, password, queueName, exchange, }: GlowListenerOptions): Promise<GlowListener>;
|
31
|
-
export type { GlowListener } from "./types";
|