@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
JavaScript
;
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;