Skip to main content
Glama
environment-validator.ts3.97 kB
import { z } from 'zod'; import type { AppConfig, ConfigError, Result } from './types.js'; /** * デフォルト設定値 */ const DEFAULT_CONFIG = { logLevel: 'INFO' as const, timeout: 30000, retryMaxAttempts: 3, } as const; /** * 環境変数バリデーター * 設定値の検証とデフォルト値の適用を行います */ export class EnvironmentValidator { /** * 設定スキーマ定義 */ private readonly configSchema = z.object({ lycheeRedmine: z.object({ url: z .string() .min(1, 'LYCHEE_REDMINE_URL must not be empty') .url('Invalid URL format') .refine( (url) => url.startsWith('https://'), 'LYCHEE_REDMINE_URL must use HTTPS protocol' ), apiKey: z .string() .min(1, 'LYCHEE_REDMINE_API_KEY must not be empty') .trim() .refine( (key) => key.length > 0, 'LYCHEE_REDMINE_API_KEY must not be whitespace only' ), }), server: z.object({ logLevel: z .enum(['DEBUG', 'INFO', 'WARN', 'ERROR']) .default(DEFAULT_CONFIG.logLevel), timeout: z .number() .int() .min(0, 'timeout must be non-negative') .default(DEFAULT_CONFIG.timeout), retryMaxAttempts: z .number() .int() .min(0, 'retryMaxAttempts must be non-negative') .default(DEFAULT_CONFIG.retryMaxAttempts), }), }); /** * 設定をバリデーションし、デフォルト値を適用します * @param config - バリデーション対象の設定(部分的でも可) * @returns バリデーション済みの完全な設定、または設定エラー */ validate(config: Partial<AppConfig>): Result<AppConfig, ConfigError> { try { // デフォルト値を適用した設定を準備 const configWithDefaults = { lycheeRedmine: config.lycheeRedmine, server: { logLevel: config.server?.logLevel ?? DEFAULT_CONFIG.logLevel, timeout: config.server?.timeout ?? DEFAULT_CONFIG.timeout, retryMaxAttempts: config.server?.retryMaxAttempts ?? DEFAULT_CONFIG.retryMaxAttempts, }, }; // Zodスキーマでバリデーション実行 const validated = this.configSchema.parse(configWithDefaults); return { ok: true, value: validated as AppConfig, }; } catch (error) { // ZodバリデーションエラーをConfigErrorに変換 if (error instanceof z.ZodError) { return this.convertZodError(error); } // 予期しないエラー return { ok: false, error: { type: 'invalid_format', message: error instanceof Error ? error.message : 'Unknown validation error', }, }; } } /** * ZodErrorをConfigErrorに変換します * @param zodError - Zodバリデーションエラー * @returns 変換されたConfigError */ private convertZodError( zodError: z.ZodError ): Result<AppConfig, ConfigError> { const firstIssue = zodError.issues[0]; if (!firstIssue) { return { ok: false, error: { type: 'invalid_format', message: 'Validation failed', }, }; } const field = firstIssue.path.join('.'); const message = firstIssue.message; // エラーの種類を判定 let errorType: 'missing_required' | 'invalid_format' = 'invalid_format'; if ( firstIssue.code === z.ZodIssueCode.invalid_type && firstIssue.received === 'undefined' ) { errorType = 'missing_required'; } else if ( message.includes('must not be empty') || message.includes('must not be whitespace only') ) { errorType = 'missing_required'; } return { ok: false, error: { type: errorType, message, field, }, }; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/ssoma-dev/mcp-server-lychee-redmine'

If you have feedback or need assistance with the MCP directory API, please join our Discord server