UNPKG

@arpinum/messaging

Version:
146 lines (145 loc) 6.35 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.DefaultMessageBus = void 0; const promising_1 = require("@arpinum/promising"); const asserts_1 = require("./asserts"); const defaultOptions = { log: () => { //noop }, exclusiveHandlers: false, ensureAtLeastOneHandler: false, handlersConcurrency: 3, beforePost: [], afterPost: [], beforeHandle: [], afterHandle: [], }; class DefaultMessageBus { constructor(options = {}) { this.validateOptions(options); this.options = Object.assign({}, defaultOptions, options); this.handlerMap = new Map(); this.beforeHandle = (0, promising_1.pipe)(this.options.beforeHandle); this.afterHandle = (0, promising_1.pipe)(this.options.afterHandle); this.beforePost = (0, promising_1.pipe)(this.options.beforePost); this.afterPost = (0, promising_1.pipe)(this.options.afterPost); } validateOptions(options) { (0, asserts_1.assertOptionalFunction)(options.log, "options#log"); (0, asserts_1.assertOptionalBoolean)(options.exclusiveHandlers, "options#exclusiveHandlers"); (0, asserts_1.assertOptionalBoolean)(options.ensureAtLeastOneHandler, "options#ensureAtLeastOneHandler"); (0, asserts_1.assertOptionalNumber)(options.handlersConcurrency, "options#handlersConcurrency"); (0, asserts_1.assertOptionalArray)(options.beforePost, "options#beforePost"); (0, asserts_1.assertOptionalArray)(options.beforeHandle, "options#beforeHandle"); (0, asserts_1.assertOptionalArray)(options.afterHandle, "options#afterHandle"); (0, asserts_1.assertOptionalArray)(options.afterPost, "options#afterPost"); } postAll(messages) { if (messages.length === 0) { return Promise.resolve([]); } if (messages.length === 1) { return this.post(messages[0]).then((r) => [r]); } return (0, promising_1.mapWithOptions)((message) => this.post(message), { concurrency: 3 }, messages); } post(message) { return this.validatedMessage(message) .then(this.beforePost) .then((message) => this.postToHandlers(message)) .then(this.afterPost); } postToHandlers(messageToPost) { this.options.log(`Posting ${messageToPost.type}`); const handlers = this.handlersFor(messageToPost.type); this.checkHandlerRequirements(messageToPost, handlers); if (this.options.exclusiveHandlers) { return this.postForExclusiveHandlers(messageToPost, handlers); } return this.postForStandardHandlers(messageToPost, handlers); } validatedMessage(messageToValidate) { try { (0, asserts_1.assertPresent)(messageToValidate, "message"); (0, asserts_1.assertString)(messageToValidate.type, "message#type"); return Promise.resolve(messageToValidate); } catch (e) { return Promise.reject(e); } } checkHandlerRequirements(messageToCheck, handlers) { if (handlers.length === 0 && this.options.ensureAtLeastOneHandler) { throw new Error(`No handler for ${messageToCheck.type}`); } } postForExclusiveHandlers(messageToPost, handlers) { return __awaiter(this, void 0, void 0, function* () { if (handlers.length === 0) { return Promise.resolve([]); } const result = yield this.handle(messageToPost, handlers[0]); return [result]; }); } postForStandardHandlers(messageToPost, handlers) { if (handlers.length === 0) { return Promise.resolve([]); } if (handlers.length === 1) { return this.handle(messageToPost, handlers[0]).then((r) => [r]); } const handleMessage = (handler) => this.handle(messageToPost, handler); return (0, promising_1.mapWithOptions)(handleMessage, { concurrency: this.options.handlersConcurrency }, handlers); } handle(message, handler) { return this.beforeHandle(message).then(handler).then(this.afterHandle); } register(type, handler) { validateArgs(); this.options.log(`Registering to ${type}`); const handlers = this.handlersFor(type); this.ensureHandlerExclusivity(handlers, type); this.updateHandlers(type, handlers.concat(handler)); return () => this.updateHandlers(type, this.handlersFor(type).filter((h) => h !== handler)); function validateArgs() { (0, asserts_1.assertPresent)(type, "type"); (0, asserts_1.assertFunction)(handler, "handler"); } } ensureHandlerExclusivity(handlers, type) { if (this.options.exclusiveHandlers && handlers.length > 0) { const message = `Won't allow a new handler for type ${type} ` + `since handlers are exclusive`; throw new Error(message); } } unregisterAll(...types) { validateArgs(); types.forEach((type) => this.updateHandlers(type, [])); function validateArgs() { types.forEach((type, i) => (0, asserts_1.assertString)(type, `types[${i}]`)); } } handlerCount(type) { (0, asserts_1.assertString)(type, "type"); return this.handlersFor(type).length; } handlersFor(messageType) { return this.handlerMap.get(messageType) || []; } updateHandlers(messageType, handlers) { this.handlerMap.set(messageType, handlers); } } exports.DefaultMessageBus = DefaultMessageBus;