UNPKG

@glowlabs-org/events-sdk

Version:

Typed event SDK for Glow, powered by RabbitMQ and Zod.

88 lines (87 loc) 3.79 kB
"use strict"; 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 }; }