@sailboat-computer/event-bus
Version:
Standardized event bus for sailboat computer v3 with resilience features and offline capabilities
172 lines (147 loc) • 3.48 kB
text/typescript
/**
* Event schema validation
*/
import Ajv from 'ajv';
import { EventEnvelope } from './types';
// Define JSON Schema type
type JSONSchemaType<T> = {
type: string;
properties?: Record<string, any>;
required?: string[];
additionalProperties?: boolean;
[key: string]: any;
};
import { logger } from './utils';
/**
* Schema registry for event validation
*/
export class SchemaRegistry {
/**
* Ajv instance
*/
private ajv: any;
/**
* Schema map
*/
private schemas: Map<string, JSONSchemaType<any>> = new Map();
/**
* Constructor
*/
constructor() {
this.ajv = new Ajv({
allErrors: true,
removeAdditional: false,
useDefaults: true,
coerceTypes: false
});
}
/**
* Register a schema for an event type
*
* @param eventType - Event type
* @param schema - JSON schema
*/
registerSchema<T>(eventType: string, schema: JSONSchemaType<T>): void {
if (this.schemas.has(eventType)) {
logger.warn(`Schema for event type ${eventType} already registered, overwriting`);
// Remove existing schema before adding a new one
this.ajv.removeSchema(eventType);
}
this.schemas.set(eventType, schema);
this.ajv.addSchema(schema, eventType);
logger.debug(`Registered schema for event type ${eventType}`);
}
/**
* Validate an event against its schema
*
* @param event - Event envelope
* @returns Validation result
*/
validate(event: EventEnvelope): ValidationResult {
const eventType = event.type;
if (!this.schemas.has(eventType)) {
return {
valid: true,
errors: null
};
}
const validate = this.ajv.getSchema(eventType);
if (!validate) {
logger.warn(`Schema for event type ${eventType} not found in AJV`);
return {
valid: true,
errors: null
};
}
const valid = validate(event.data);
if (!valid) {
return {
valid: false,
errors: validate.errors || null
};
}
return {
valid: true,
errors: null
};
}
/**
* Check if a schema is registered for an event type
*
* @param eventType - Event type
* @returns True if a schema is registered
*/
hasSchema(eventType: string): boolean {
return this.schemas.has(eventType);
}
/**
* Get a schema for an event type
*
* @param eventType - Event type
* @returns JSON schema or null if not found
*/
getSchema(eventType: string): JSONSchemaType<any> | null {
return this.schemas.get(eventType) || null;
}
/**
* Remove a schema for an event type
*
* @param eventType - Event type
*/
removeSchema(eventType: string): void {
if (this.schemas.has(eventType)) {
this.schemas.delete(eventType);
this.ajv.removeSchema(eventType);
logger.debug(`Removed schema for event type ${eventType}`);
}
}
/**
* Clear all schemas
*/
clearSchemas(): void {
this.schemas.clear();
this.ajv.removeSchema();
logger.debug('Cleared all schemas');
}
}
/**
* Validation result
*/
export interface ValidationResult {
/**
* Whether the validation passed
*/
valid: boolean;
/**
* Validation errors
*/
errors: any[] | null;
}
/**
* Create a schema registry
*
* @returns Schema registry
*/
export function createSchemaRegistry(): SchemaRegistry {
return new SchemaRegistry();
}