@logistically/events
Version:
A production-ready event-driven architecture library for NestJS with Redis Streams, comprehensive batching, reliable consumption, and enterprise-grade features.
99 lines (98 loc) • 4.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Payload = exports.MessagePattern = exports.EventPublisher = void 0;
const event_routing_1 = require("../event-routing");
const event_types_1 = require("../event-types");
const rxjs_1 = require("rxjs");
// Note: The 'prefix' in routing is used for legacy/compatibility purposes and is ignored for stream routing in the Redis Streams transport.
function getNamespaceFromType(eventType) {
return eventType.split('.')[0];
}
function getStreamForEventType(eventType) {
const namespace = getNamespaceFromType(eventType);
return `${namespace}-events`;
}
class EventPublisher {
constructor(transports, options, routingConfig = event_routing_1.default) {
this.transports = transports;
this.options = options;
this.routingConfig = routingConfig;
if (!options || !options.originServiceName) {
throw new Error('originServiceName must be provided in EventPublisherOptions');
}
if (!options.validator) {
throw new Error('validator must be provided in EventPublisherOptions');
}
// Initialize validator
this.validator = options.validator;
// Set event namespace - use provided one or derive from service name
this.eventNamespace = options.eventNamespace || options.originServiceName;
}
getRouteForEvent(eventType) {
return this.routingConfig.routes.find(route => route.pattern.test(eventType));
}
getTransportForEvent(eventType) {
const route = this.getRouteForEvent(eventType);
if (route) {
return { transport: this.transports[route.transport], prefix: route.prefix };
}
return { transport: this.transports[event_routing_1.default.default] };
}
async publish(eventType, body) {
// Validate event body using the validator
const validation = this.validator.validate(eventType, body);
if (!validation.valid) {
throw new Error(`Invalid event body for type ${eventType}: ${validation.error}`);
}
const { transport, prefix } = this.getTransportForEvent(eventType);
const finalEventType = prefix ? `${prefix}${eventType}` : eventType;
const stream = getStreamForEventType(eventType);
// Create the envelope with proper header generation
const envelope = (0, event_types_1.createEventEnvelope)(eventType, this.options.originServiceName, body, this.eventNamespace);
// If the transport supports stream override, pass it
if (typeof transport.dispatchEvent === 'function') {
await transport.dispatchEvent({ pattern: finalEventType, data: envelope }, { stream });
}
else {
await (0, rxjs_1.firstValueFrom)(transport.emit(finalEventType, envelope));
}
}
// Convenience method for publishing with custom validator
async publishWithValidator(eventType, body, validator) {
const tempValidator = this.validator;
this.validator = validator;
try {
await this.publish(eventType, body);
}
finally {
this.validator = tempValidator;
}
}
// Method to register schemas with the default validator
registerSchema(eventType, schema) {
if (this.validator instanceof event_types_1.DefaultEventValidator) {
this.validator.registerSchema(eventType, schema);
}
else {
throw new Error('Cannot register schema with custom validator');
}
}
// Close method for proper resource cleanup
async close() {
// Close all transports
for (const transport of Object.values(this.transports)) {
if (transport && typeof transport.close === 'function') {
try {
await transport.close();
}
catch (error) {
console.warn('Error closing transport:', error);
}
}
}
}
}
exports.EventPublisher = EventPublisher;
var microservices_1 = require("@nestjs/microservices");
Object.defineProperty(exports, "MessagePattern", { enumerable: true, get: function () { return microservices_1.MessagePattern; } });
Object.defineProperty(exports, "Payload", { enumerable: true, get: function () { return microservices_1.Payload; } });