Middleware
Middleware runs in order and receives immutable context for each event.
Contract
export interface MiddlewareContext<E extends ChatEventType = ChatEventType> {
readonly engine: IChatEngine;
readonly event: ChatEvent<E>;
}
export type NextFn = (modifiedEvent?: ChatEvent) => Promise<ChatEvent>;
export type Middleware = (ctx: MiddlewareContext, next: NextFn) => Promise<ChatEvent>;Pipeline Behavior
event -> middleware[0] -> middleware[1] -> ... -> middleware[n] -> final event
- pass no argument to next() to forward event
- pass next(modifiedEvent) to replace event downstream
- skip next() to short-circuitMessage Validation Middleware
import type { ChatEvent, Middleware } from '@kaira/chat-core';
import { createChatError } from '@kaira/chat-core';
export const messageValidationMiddleware: Middleware = async (ctx, next) => {
if (ctx.event.type !== 'message:sent') {
return next();
}
const message = ctx.event.message;
const isText = message.type === 'text';
const hasText =
isText && typeof message.content === 'string' && message.content.trim().length > 0;
if (!hasText) {
throw createChatError('validation', 'Text message cannot be empty');
}
const normalizedEvent: ChatEvent<'message:sent'> = {
...ctx.event,
message: {
...message,
content: message.content.trim(),
},
};
return next(normalizedEvent);
};