@glowlabs-org/events-sdk
Version:
Typed event SDK for Glow, powered by RabbitMQ and Zod.
88 lines (87 loc) • 3.79 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createGlowEventListener = createGlowEventListener;
const amqplib_1 = __importDefault(require("amqplib"));
const event_registry_1 = require("./event-registry");
const utils_1 = require("./utils");
function createGlowEventListener({ username, password, zoneId, queueName, exchangePrefix = "glow.zone-", host = "turntable.proxy.rlwy.net:50784", }) {
let amqpConnection = null;
let amqpChannel = null;
let internalQueueName;
let consumerTag = null;
// Use a special exchange for all zones if zoneId is 0
const exchangeName = `${exchangePrefix}${zoneId}.events`;
const eventHandlers = [];
function buildUrl() {
return (0, utils_1.buildAmqpUrl)({ username, password, host });
}
async function connectIfNeeded(bindingPattern) {
if (!amqpConnection) {
amqpConnection = (await amqplib_1.default.connect(buildUrl()));
amqpChannel = (await amqpConnection.createChannel());
}
if (amqpChannel) {
if (queueName) {
internalQueueName = queueName;
}
else {
await amqpChannel.assertExchange(exchangeName, "topic", {
durable: true,
});
const q = await amqpChannel.assertQueue("", { exclusive: true });
internalQueueName = q.queue;
await amqpChannel.bindQueue(internalQueueName, exchangeName, bindingPattern);
}
}
}
function onEvent(eventType, schemaVersion, handler) {
const bindingPattern = `${String(eventType)}.${String(schemaVersion)}`;
eventHandlers.push({
bindingPattern,
handler: (event, meta) => handler(event),
});
}
async function start() {
for (const { bindingPattern } of eventHandlers) {
await connectIfNeeded(bindingPattern);
}
if (!internalQueueName)
throw new Error("Queue not initialized");
consumerTag = (await amqpChannel.consume(internalQueueName, (msg) => {
if (msg) {
try {
const decoded = JSON.parse(msg.content.toString());
const [eventType, versionStr] = msg.fields.routingKey.split(".v");
const schemaVersion = `v${versionStr}`;
(0, utils_1.validateZoneNameAndId)(decoded.zoneId, decoded.zoneName);
(0, utils_1.validateEventPayload)(eventType, schemaVersion, decoded);
const event = (0, event_registry_1.getEventSchema)(eventType, schemaVersion).parse(decoded);
const handlerEntry = eventHandlers.find((h) => h.bindingPattern === `${eventType}.${schemaVersion}`);
if (handlerEntry)
handlerEntry.handler(event, { eventType, schemaVersion });
amqpChannel.ack(msg);
}
catch (error) {
console.error("Failed to process event:", error);
amqpChannel.nack(msg, false, false);
}
}
}, { noAck: false })).consumerTag;
}
async function stop() {
if (amqpChannel && consumerTag && internalQueueName) {
await amqpChannel.cancel(consumerTag);
consumerTag = null;
}
if (amqpConnection) {
await amqpConnection.close();
amqpConnection = null;
amqpChannel = null;
internalQueueName = undefined;
}
}
return { onEvent, start, stop };
}