client-base/src/middleware/websocket-middleware.ts

165 lines
4.2 KiB
TypeScript

import type {
WebSocketConnectionMiddleware,
WebSocketMessageMiddleware,
WebSocketReceiveMiddleware,
MiddlewareConfig,
MiddlewareContext,
} from './types';
import type { WebSocketConfig, WebSocketMessage } from '../websocket/types';
import { MiddlewareError } from '../errors';
/**
* Middleware chain executor for WebSocket connections
*/
export class WebSocketMiddlewareChain {
private connectionMiddleware: Array<{
middleware: WebSocketConnectionMiddleware;
config: MiddlewareConfig;
}> = [];
private messageMiddleware: Array<{
middleware: WebSocketMessageMiddleware;
config: MiddlewareConfig;
}> = [];
private receiveMiddleware: Array<{
middleware: WebSocketReceiveMiddleware;
config: MiddlewareConfig;
}> = [];
/**
* Add connection middleware
*/
addConnectionMiddleware(
middleware: WebSocketConnectionMiddleware,
config: MiddlewareConfig = {},
): void {
this.connectionMiddleware.push({ middleware, config });
this.sortByPriority(this.connectionMiddleware);
}
/**
* Add message middleware
*/
addMessageMiddleware(
middleware: WebSocketMessageMiddleware,
config: MiddlewareConfig = {},
): void {
this.messageMiddleware.push({ middleware, config });
this.sortByPriority(this.messageMiddleware);
}
/**
* Add receive middleware
*/
addReceiveMiddleware(
middleware: WebSocketReceiveMiddleware,
config: MiddlewareConfig = {},
): void {
this.receiveMiddleware.push({ middleware, config });
this.sortByPriority(this.receiveMiddleware);
}
/**
* Execute connection middleware chain
*/
async executeConnection(config: WebSocketConfig): Promise<WebSocketConfig> {
const context = this.createContext(config.metadata);
let current = config;
for (const { middleware, config: mwConfig } of this.connectionMiddleware) {
if (mwConfig.enabled === false) {
continue;
}
try {
current = await middleware(current, context);
} catch (error) {
throw new MiddlewareError(
`Connection middleware failed: ${mwConfig.name || 'unknown'}`,
mwConfig.name || 'unknown',
error,
);
}
}
return current;
}
/**
* Execute message middleware chain
*/
async executeMessage<T>(message: WebSocketMessage<T>): Promise<WebSocketMessage<T>> {
const context = this.createContext();
let current = message;
for (const { middleware, config: mwConfig } of this.messageMiddleware) {
if (mwConfig.enabled === false) {
continue;
}
try {
current = await middleware(current, context);
} catch (error) {
throw new MiddlewareError(
`Message middleware failed: ${mwConfig.name || 'unknown'}`,
mwConfig.name || 'unknown',
error,
);
}
}
return current;
}
/**
* Execute receive middleware chain
*/
async executeReceive<T>(
event: string,
data: T,
): Promise<{ event: string; data: T }> {
const context = this.createContext();
let current = { event, data };
for (const { middleware, config: mwConfig } of this.receiveMiddleware) {
if (mwConfig.enabled === false) {
continue;
}
try {
current = await middleware(current.event, current.data, context);
} catch (error) {
throw new MiddlewareError(
`Receive middleware failed: ${mwConfig.name || 'unknown'}`,
mwConfig.name || 'unknown',
error,
);
}
}
return current;
}
/**
* Sort middleware by priority (higher first)
*/
private sortByPriority<T extends { config: MiddlewareConfig }>(middleware: Array<T>): void {
middleware.sort((a, b) => (b.config.priority || 0) - (a.config.priority || 0));
}
/**
* Create middleware execution context
*/
private createContext(metadata: Record<string, unknown> = {}): MiddlewareContext {
return {
metadata: { ...metadata },
abort: (reason: string) => {
throw new MiddlewareError(`Middleware chain aborted: ${reason}`, 'chain');
},
skip: () => {
// No-op - actual implementation would use control flow
},
};
}
}