Files
Yonexus.Server/plugin/core/rules.ts

90 lines
2.2 KiB
TypeScript

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<string, ServerRuleProcessor>();
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();
}