feat: add client rule registry
This commit is contained in:
85
plugin/core/rules.ts
Normal file
85
plugin/core/rules.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
import { BUILTIN_RULE, CodecError, parseRuleMessage } from "../../../Yonexus.Protocol/src/index.js";
|
||||
|
||||
export type ClientRuleProcessor = (message: string) => unknown;
|
||||
|
||||
export class ClientRuleRegistryError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = "ClientRuleRegistryError";
|
||||
}
|
||||
}
|
||||
|
||||
export interface ClientRuleRegistry {
|
||||
readonly size: number;
|
||||
registerRule(rule: string, processor: ClientRuleProcessor): void;
|
||||
hasRule(rule: string): boolean;
|
||||
dispatch(raw: string): boolean;
|
||||
getRules(): readonly string[];
|
||||
}
|
||||
|
||||
export class YonexusClientRuleRegistry implements ClientRuleRegistry {
|
||||
private readonly rules = new Map<string, ClientRuleProcessor>();
|
||||
|
||||
get size(): number {
|
||||
return this.rules.size;
|
||||
}
|
||||
|
||||
registerRule(rule: string, processor: ClientRuleProcessor): void {
|
||||
const normalizedRule = this.normalizeRule(rule);
|
||||
if (this.rules.has(normalizedRule)) {
|
||||
throw new ClientRuleRegistryError(
|
||||
`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 = parseRuleMessage(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 ClientRuleRegistryError("Rule identifier must be a non-empty string");
|
||||
}
|
||||
|
||||
if (normalizedRule === BUILTIN_RULE) {
|
||||
throw new ClientRuleRegistryError(
|
||||
`Rule identifier '${BUILTIN_RULE}' is reserved`
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
parseRuleMessage(`${normalizedRule}::probe`);
|
||||
} catch (error) {
|
||||
if (error instanceof CodecError) {
|
||||
throw new ClientRuleRegistryError(error.message);
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
return normalizedRule;
|
||||
}
|
||||
}
|
||||
|
||||
export function createClientRuleRegistry(): ClientRuleRegistry {
|
||||
return new YonexusClientRuleRegistry();
|
||||
}
|
||||
@@ -31,6 +31,13 @@ export {
|
||||
type YonexusClientRuntimeState,
|
||||
type YonexusClientPhase
|
||||
} from "./core/runtime.js";
|
||||
export {
|
||||
createClientRuleRegistry,
|
||||
YonexusClientRuleRegistry,
|
||||
ClientRuleRegistryError,
|
||||
type ClientRuleRegistry,
|
||||
type ClientRuleProcessor
|
||||
} from "./core/rules.js";
|
||||
|
||||
export interface YonexusClientPluginManifest {
|
||||
readonly name: "Yonexus.Client";
|
||||
|
||||
Reference in New Issue
Block a user