@sprucelabs/mercury-event-emitter
Version:
Types for Mercury!
183 lines (182 loc) • 6.59 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const error_1 = __importDefault(require("@sprucelabs/error"));
const schema_1 = require("@sprucelabs/schema");
const spruce_event_utils_1 = require("@sprucelabs/spruce-event-utils");
const SpruceError_1 = __importDefault(require("./errors/SpruceError"));
class AbstractEventEmitter {
constructor(contract, options) {
this.listenersByEvent = {};
this.eventContract = contract;
this.shouldEmitSequentally = options?.shouldEmitSequentally ?? false;
}
async emit(eventName, payload, cb) {
const { actualPayload, actualCallback } = this.normalizePayloadAndCallback(payload, cb);
const eventSignature = spruce_event_utils_1.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 = async (listenerCb, idx) => {
let response = await this.emitOne({
idx,
listenerCb,
payload: actualPayload,
totalContracts: listeners.length,
actualCallback,
});
if (responseSchema && !response.errors) {
try {
this.validateResponsePayload(responseSchema, response.payload ?? {}, 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 = await emitOneAndValidate(listener, idx);
responses.push(response);
idx++;
}
}
else {
responses = await Promise.all(listeners.map(async (listenerCb, idx) => emitOneAndValidate(listenerCb, idx)));
}
return {
totalContracts: listeners.length,
totalResponses: listeners.length,
totalErrors,
responses,
};
}
async emitAndFlattenResponses(eventName, payload, cb) {
const results = await this.emit(eventName, payload, cb);
if (results.totalResponses === 0) {
return [];
}
const { payloads, errors } = spruce_event_utils_1.eventResponseUtil.getAllResponsePayloadsAndErrors(results, SpruceError_1.default);
if (errors?.[0]) {
throw errors[0];
}
return payloads;
}
async emitOne(options) {
let responsePayload;
let error;
try {
responsePayload = await options.listenerCb(options.payload);
}
catch (err) {
if (err instanceof error_1.default) {
error = err;
}
else {
error = new SpruceError_1.default({
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];
}
await 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 = spruce_event_utils_1.eventContractUtil.unifyContracts([
this.eventContract,
contract,
]);
}
validateEmitPayload(schema, actualPayload, eventName) {
if (schema) {
try {
(0, schema_1.validateSchemaValues)(schema, actualPayload ?? {});
}
catch (err) {
throw new SpruceError_1.default({
code: 'INVALID_PAYLOAD',
originalError: err,
eventName,
});
}
}
}
validateResponsePayload(schema, actualPayload, eventName) {
if (schema) {
try {
(0, schema_1.validateSchemaValues)(schema, actualPayload ?? {});
}
catch (err) {
throw new SpruceError_1.default({
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 };
}
async on(eventName, cb) {
spruce_event_utils_1.eventContractUtil.getSignatureByName(this.eventContract, eventName);
if (!this.listenersByEvent[eventName]) {
this.listenersByEvent[eventName] = [];
}
this.listenersByEvent[eventName].push(cb);
}
async off(eventName, cb) {
if (cb) {
let numForgotten = 0;
this.listenersByEvent[eventName] = this.listenersByEvent[eventName]?.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;
}
}
}
exports.default = AbstractEventEmitter;