90 lines
2.2 KiB
TypeScript
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();
|
|
}
|