@message-in-the-middle/core
Version:
Framework-agnostic middleware pattern for message queue processing. Core package with all middlewares.
109 lines • 3.93 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.SchemaRegistryMiddleware = exports.SchemaRegistry = void 0;
class SchemaRegistry {
schemas = new Map();
options;
constructor(options = {}) {
this.options = {
identifierField: options.identifierField ?? 'type',
strict: options.strict ?? true,
errorFormatter: options.errorFormatter ?? this.defaultErrorFormatter,
};
}
register(messageType, validator) {
if (typeof messageType !== 'string' || messageType.length === 0) {
throw new Error('Message type must be a non-empty string');
}
if (messageType.length > 256) {
throw new Error('Message type too long (max 256 characters)');
}
const forbidden = ['__proto__', 'constructor', 'prototype'];
if (forbidden.includes(messageType)) {
throw new Error(`Forbidden message type: ${messageType}`);
}
if (this.schemas.has(messageType)) {
throw new Error(`Schema already registered for message type: ${messageType}`);
}
const wrappedValidator = this.wrapValidator(validator);
this.schemas.set(messageType, wrappedValidator);
return this;
}
registerMany(schemas) {
for (const [messageType, validator] of Object.entries(schemas)) {
this.register(messageType, validator);
}
return this;
}
get(messageType) {
return this.schemas.get(messageType);
}
has(messageType) {
return this.schemas.has(messageType);
}
unregister(messageType) {
return this.schemas.delete(messageType);
}
getRegisteredTypes() {
return Array.from(this.schemas.keys());
}
clear() {
this.schemas.clear();
}
getIdentifierField() {
return this.options.identifierField;
}
async validate(message) {
const messageType = message?.[this.options.identifierField];
if (!messageType) {
if (this.options.strict) {
throw new Error(`Message missing identifier field '${this.options.identifierField}'`);
}
return message;
}
const validator = this.schemas.get(messageType);
if (!validator) {
if (this.options.strict) {
throw new Error(`No schema registered for message type '${messageType}'. ` +
`Registered types: ${this.getRegisteredTypes().join(', ')}`);
}
return message;
}
try {
const validatedMessage = await validator(message);
return validatedMessage;
}
catch (error) {
const formattedError = this.options.errorFormatter(error, messageType);
throw formattedError;
}
}
wrapValidator(validator) {
if (typeof validator === 'object' && validator !== null && 'parse' in validator) {
const schema = validator;
return (message) => {
return schema.parse(message);
};
}
return validator;
}
defaultErrorFormatter(error, messageType) {
return new Error(`Schema validation failed for message type '${messageType}': ${error.message}`);
}
}
exports.SchemaRegistry = SchemaRegistry;
class SchemaRegistryMiddleware {
registry;
constructor(registry) {
this.registry = registry;
}
async process(context, next) {
const messageType = context.message?.[this.registry.getIdentifierField()];
context.message = await this.registry.validate(context.message);
context.metadata.schemaValidated = true;
context.metadata.schemaMessageType = messageType;
await next();
}
}
exports.SchemaRegistryMiddleware = SchemaRegistryMiddleware;
//# sourceMappingURL=schema-registry.middleware.js.map