import { BUILTIN_RULE, CodecError, parseRewrittenRuleMessage } from "../../../Yonexus.Protocol/src/index.js"; export type ServerRuleProcessor = (message: string) => unknown; export class ServerRuleRegistryError extends Error { constructor(message: string) { super(message); this.name = "ServerRuleRegistryError"; } } export interface ServerRuleRegistry { readonly size: number; registerRule(rule: string, processor: ServerRuleProcessor): void; hasRule(rule: string): boolean; dispatch(raw: string): boolean; getRules(): readonly string[]; } export class YonexusServerRuleRegistry implements ServerRuleRegistry { private readonly rules = new Map(); get size(): number { return this.rules.size; } registerRule(rule: string, processor: ServerRuleProcessor): void { const normalizedRule = this.normalizeRule(rule); if (this.rules.has(normalizedRule)) { throw new ServerRuleRegistryError( `Rule '${normalizedRule}' is already registered` ); } this.rules.set(normalizedRule, processor); } hasRule(rule: string): boolean { return this.rules.has(rule.trim()); } dispatch(raw: string): boolean { const parsed = parseRewrittenRuleMessage(raw); const processor = this.rules.get(parsed.ruleIdentifier); if (!processor) { return false; } processor(raw); return true; } getRules(): readonly string[] { return [...this.rules.keys()]; } private normalizeRule(rule: string): string { const normalizedRule = rule.trim(); if (!normalizedRule) { throw new ServerRuleRegistryError("Rule identifier must be a non-empty string"); } if (normalizedRule === BUILTIN_RULE) { throw new ServerRuleRegistryError( `Rule identifier '${BUILTIN_RULE}' is reserved` ); } try { parseRewrittenRuleMessage(`${normalizedRule}::sender::probe`); } catch (error) { if (error instanceof CodecError) { throw new ServerRuleRegistryError(error.message); } throw error; } return normalizedRule; } } export function createServerRuleRegistry(): ServerRuleRegistry { return new YonexusServerRuleRegistry(); }