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 && typeof raw === "object" ? raw : {}) as Record; const issues: string[] = []; const rawMainHost = source.mainHost; if (!isNonEmptyString(rawMainHost)) { issues.push("mainHost is required"); } else if (!isValidWsUrl(rawMainHost.trim())) { issues.push("mainHost must be a valid ws:// or wss:// URL"); } const rawIdentifier = source.identifier; if (!isNonEmptyString(rawIdentifier)) { issues.push("identifier is required"); } const rawNotifyBotToken = source.notifyBotToken; if (!isNonEmptyString(rawNotifyBotToken)) { issues.push("notifyBotToken is required"); } const rawAdminUserId = source.adminUserId; if (!isNonEmptyString(rawAdminUserId)) { issues.push("adminUserId is required"); } if (issues.length > 0) { throw new YonexusClientConfigError(issues); } const mainHost = (rawMainHost as string).trim(); const identifier = (rawIdentifier as string).trim(); const notifyBotToken = (rawNotifyBotToken as string).trim(); const adminUserId = (rawAdminUserId as string).trim(); return { mainHost, identifier, notifyBotToken, adminUserId }; }