export interface YonexusClientConfig { mainHost: string; identifier: string; notifyBotToken: string; adminUserId: string; } export class YonexusClientConfigError extends Error { readonly issues: string[]; constructor(issues: string[]) { super(`Invalid Yonexus.Client config: ${issues.join("; ")}`); this.name = "YonexusClientConfigError"; this.issues = issues; } } function isNonEmptyString(value: unknown): value is string { return typeof value === "string" && value.trim().length > 0; } function isValidWsUrl(value: string): boolean { try { const url = new URL(value); return url.protocol === "ws:" || url.protocol === "wss:"; } catch { return false; } } export function validateYonexusClientConfig(raw: unknown): YonexusClientConfig { const source = raw as Record | null; const issues: string[] = []; const mainHost = source?.mainHost; if (!isNonEmptyString(mainHost)) { issues.push("mainHost is required"); } else if (!isValidWsUrl(mainHost.trim())) { issues.push("mainHost must be a valid ws:// or wss:// URL"); } const identifier = source?.identifier; if (!isNonEmptyString(identifier)) { issues.push("identifier is required"); } const notifyBotToken = source?.notifyBotToken; if (!isNonEmptyString(notifyBotToken)) { issues.push("notifyBotToken is required"); } const adminUserId = source?.adminUserId; if (!isNonEmptyString(adminUserId)) { issues.push("adminUserId is required"); } if (issues.length > 0) { throw new YonexusClientConfigError(issues); } return { mainHost: mainHost.trim(), identifier: identifier.trim(), notifyBotToken: notifyBotToken.trim(), adminUserId: adminUserId.trim() }; }