@sprucelabs/mercury-event-emitter
Version:
Types for Mercury!
199 lines (198 loc) • 8.14 kB
JavaScript
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());
});
};
import AbstractSpruceError from '@sprucelabs/error';
import { validateSchemaValues } from '@sprucelabs/schema';
import { eventContractUtil, eventResponseUtil, } from '@sprucelabs/spruce-event-utils';
import SpruceError from './errors/SpruceError.js';
export default class AbstractEventEmitter {
constructor(contract, options) {
var _a;
this.listenersByEvent = {};
this.eventContract = contract;
this.shouldEmitSequentally = (_a = options === null || options === void 0 ? void 0 : options.shouldEmitSequentally) !== null && _a !== void 0 ? _a : false;
}
emit(eventName, payload, cb) {
return __awaiter(this, void 0, void 0, function* () {
const { actualPayload, actualCallback } = this.normalizePayloadAndCallback(payload, cb);
const eventSignature = eventContractUtil.getSignatureByName(this.eventContract, eventName);
const emitSchema = eventSignature.emitPayloadSchema;
const responseSchema = eventSignature.responsePayloadSchema;
this.validateEmitPayload(emitSchema, actualPayload, eventName);
const listeners = this.listenersByEvent[eventName] || [];
let totalErrors = 0;
const emitOneAndValidate = (listenerCb, idx) => __awaiter(this, void 0, void 0, function* () {
var _a;
let response = yield this.emitOne({
idx,
listenerCb,
payload: actualPayload,
totalContracts: listeners.length,
actualCallback,
});
if (responseSchema && !response.errors) {
try {
this.validateResponsePayload(responseSchema, (_a = response.payload) !== null && _a !== void 0 ? _a : {}, eventName);
}
catch (err) {
response = {
errors: [err],
};
}
}
if (response.errors) {
totalErrors += response.errors.length;
}
return response;
});
let responses;
if (this.shouldEmitSequentally) {
responses = [];
let idx = 0;
for (const listener of listeners) {
const response = yield emitOneAndValidate(listener, idx);
responses.push(response);
idx++;
}
}
else {
responses = yield Promise.all(listeners.map((listenerCb, idx) => __awaiter(this, void 0, void 0, function* () { return emitOneAndValidate(listenerCb, idx); })));
}
return {
totalContracts: listeners.length,
totalResponses: listeners.length,
totalErrors,
responses,
};
});
}
emitAndFlattenResponses(eventName, payload, cb) {
return __awaiter(this, void 0, void 0, function* () {
const results = yield this.emit(eventName, payload, cb);
if (results.totalResponses === 0) {
return [];
}
const { payloads, errors } = eventResponseUtil.getAllResponsePayloadsAndErrors(results, SpruceError);
if (errors === null || errors === void 0 ? void 0 : errors[0]) {
throw errors[0];
}
return payloads;
});
}
emitOne(options) {
return __awaiter(this, void 0, void 0, function* () {
let responsePayload;
let error;
try {
responsePayload = yield options.listenerCb(options.payload);
}
catch (err) {
if (err instanceof AbstractSpruceError) {
error = err;
}
else {
error = new SpruceError({
code: 'LISTENER_ERROR',
originalError: err,
listenerIdx: options.idx,
});
}
}
if (typeof options.actualCallback === 'function') {
const emitCallbackPayload = {};
if (responsePayload) {
emitCallbackPayload.payload = responsePayload;
}
if (error) {
emitCallbackPayload.errors = [error];
}
yield options.actualCallback(emitCallbackPayload);
}
const response = {
payload: responsePayload,
};
if (error) {
response.errors = [error];
}
return response;
});
}
listenCount(eventName) {
return (this.listenersByEvent[eventName] || []).length;
}
mixinContract(contract) {
this.eventContract = eventContractUtil.unifyContracts([
this.eventContract,
contract,
]);
}
validateEmitPayload(schema, actualPayload, eventName) {
if (schema) {
try {
validateSchemaValues(schema, actualPayload !== null && actualPayload !== void 0 ? actualPayload : {});
}
catch (err) {
throw new SpruceError({
code: 'INVALID_PAYLOAD',
originalError: err,
eventName,
});
}
}
}
validateResponsePayload(schema, actualPayload, eventName) {
if (schema) {
try {
validateSchemaValues(schema, actualPayload !== null && actualPayload !== void 0 ? actualPayload : {});
}
catch (err) {
throw new SpruceError({
code: 'INVALID_RESPONSE_PAYLOAD',
originalError: err,
eventName,
});
}
}
}
normalizePayloadAndCallback(payload, cb) {
const actualPayload = typeof payload !== 'function' ? payload : undefined;
const actualCallback = typeof payload === 'function' ? payload : cb;
return { actualPayload, actualCallback };
}
on(eventName, cb) {
return __awaiter(this, void 0, void 0, function* () {
eventContractUtil.getSignatureByName(this.eventContract, eventName);
if (!this.listenersByEvent[eventName]) {
this.listenersByEvent[eventName] = [];
}
this.listenersByEvent[eventName].push(cb);
});
}
off(eventName, cb) {
return __awaiter(this, void 0, void 0, function* () {
var _a;
if (cb) {
let numForgotten = 0;
this.listenersByEvent[eventName] = (_a = this.listenersByEvent[eventName]) === null || _a === void 0 ? void 0 : _a.filter((listener) => {
if (listener === cb) {
numForgotten++;
return false;
}
return true;
});
return numForgotten;
}
else {
const total = (this.listenersByEvent[eventName] || []).length;
delete this.listenersByEvent[eventName];
return total;
}
});
}
}