UNPKG

@buildable/messages

Version:

A fully managed messaging service that lets you easily exchange event data across any app or resource.

145 lines (144 loc) 6.12 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createClient = void 0; const crypto_1 = __importDefault(require("crypto")); const lodash_ismatch_1 = __importDefault(require("lodash.ismatch")); const utils_1 = require("../utils"); const apis_1 = require("./apis"); function createClient(secret) { if (!secret) { throw new Error('A valid Buildable secret is required'); } let listeners = {}; const runTransactions = async ({ eventName, txKey, since = Date.now(), sleepTime = 3000, platform, label }) => { //check if got deregistered before running let listener = listeners[(0, utils_1.getListenerId)({ eventName, txKey, platform, label })]; if (!listener) { return; } let page = 1; let hasMorePages = true; while (hasMorePages) { const { rows, totalPages, hasError } = await (0, apis_1.queryEvents)({ secret, eventName, txKey, platform, label, since, txState: "does-not-exist", page, pageSize: 10 }); if (hasError || totalPages <= page) { hasMorePages = false; } else { page++; } for (const event of rows) { const { key, eventName } = event; const { hasError: _hasError } = await (0, apis_1.initTransaction)({ secret, key, txKey, eventName }); if (!_hasError) { let output; let error; let hasError = false; try { output = await listener.handler((0, utils_1.getTransactionInput)(event)); } catch (e) { hasError = true; error = e; } if (hasError) { await (0, apis_1.transact)({ secret, eventName, key, txKey, output: error, state: "failed" }); } else { await (0, apis_1.transact)({ secret, eventName, key, txKey, output, state: "finished" }); } } } } await (0, utils_1.sleep)(sleepTime); //check if got deregistered during sleep before rerunning listener = listeners[(0, utils_1.getListenerId)({ eventName, txKey, platform, label })]; if (!listener) { return; } listener.interval.then(() => runTransactions({ eventName, txKey, since, sleepTime, platform, label })); }; return { emit: async (event, payload = {}) => { if (!event) { throw Error('Need to provide an `event name`'); } return await (0, apis_1.emit)({ secret, event, payload }); }, on: async (eventName, handler, options = {}) => { const { platform: _platform, label: _label, since } = options; if (!_platform != !_label) { //xor throw new Error("Both platform and label is required when passing one of them"); } const topic = await (0, apis_1.getTopic)({ secret, eventName, platform: _platform, label: _label }); const [, , , platform, label,] = topic.split("."); const partialTopic = (0, utils_1.getPartialTopic)({ eventName, platform, label }); const txKey = options.txKey || `js-sdk.${partialTopic}`; const id = (0, utils_1.getListenerId)({ eventName, txKey, platform, label }); if (listeners[id]) { throw new Error(`Listener already exists with for ${partialTopic} with txKey: ${txKey}`); } listeners[id] = { eventName, handler, txKey, options, id, platform, label }; listeners[id].interval = runTransactions({ eventName, txKey, since: since, platform, label }); console.log("listening to:", partialTopic); return { eventName, platform, label, txKey }; }, deregister: (deregisterInfo) => { const removeKeys = Object.keys(listeners).filter(id => { return (0, lodash_ismatch_1.default)((0, utils_1.deconstructListenerId)(id), deregisterInfo); }); for (const key of removeKeys) { if (listeners[key]) { delete listeners[key]; } } return removeKeys.map((key) => (0, utils_1.deconstructListenerId)(key)); }, deregisterAll: () => { listeners = {}; }, verify: (buildableSignature, pipelineSecret, payload, options) => { const { t, v1 } = buildableSignature.split(",").reduce((acc, curr) => { const [key, val] = curr.split("="); acc[key] = val; return acc; }, {}); if ((options === null || options === void 0 ? void 0 : options.tolerance) && Date.now() - t > options.tolerance) { throw new Error("Timestamp has exceeded the tolerance limit"); } const hmac = crypto_1.default.createHmac("sha256", pipelineSecret); const signature = hmac.update(`${t}.${payload}`).digest("hex"); if (signature !== v1) { throw new Error("Invalid signature"); } return true; } }; } exports.createClient = createClient;