( values: UserConfigValues, schema: UserConfigSchema, )
| 344 | * Validate user configuration values against DXT user_config schema |
| 345 | */ |
| 346 | export function validateUserConfig( |
| 347 | values: UserConfigValues, |
| 348 | schema: UserConfigSchema, |
| 349 | ): { valid: boolean; errors: string[] } { |
| 350 | const errors: string[] = [] |
| 351 | |
| 352 | // Check each field in the schema |
| 353 | for (const [key, fieldSchema] of Object.entries(schema)) { |
| 354 | const value = values[key] |
| 355 | |
| 356 | // Check required fields |
| 357 | if (fieldSchema.required && (value === undefined || value === '')) { |
| 358 | errors.push(`${fieldSchema.title || key} is required but not provided`) |
| 359 | continue |
| 360 | } |
| 361 | |
| 362 | // Skip validation for optional fields that aren't provided |
| 363 | if (value === undefined || value === '') { |
| 364 | continue |
| 365 | } |
| 366 | |
| 367 | // Type validation |
| 368 | if (fieldSchema.type === 'string') { |
| 369 | if (Array.isArray(value)) { |
| 370 | // String arrays are allowed if multiple: true |
| 371 | if (!fieldSchema.multiple) { |
| 372 | errors.push( |
| 373 | `${fieldSchema.title || key} must be a string, not an array`, |
| 374 | ) |
| 375 | } else if (!value.every(v => typeof v === 'string')) { |
| 376 | errors.push(`${fieldSchema.title || key} must be an array of strings`) |
| 377 | } |
| 378 | } else if (typeof value !== 'string') { |
| 379 | errors.push(`${fieldSchema.title || key} must be a string`) |
| 380 | } |
| 381 | } else if (fieldSchema.type === 'number' && typeof value !== 'number') { |
| 382 | errors.push(`${fieldSchema.title || key} must be a number`) |
| 383 | } else if (fieldSchema.type === 'boolean' && typeof value !== 'boolean') { |
| 384 | errors.push(`${fieldSchema.title || key} must be a boolean`) |
| 385 | } else if ( |
| 386 | (fieldSchema.type === 'file' || fieldSchema.type === 'directory') && |
| 387 | typeof value !== 'string' |
| 388 | ) { |
| 389 | errors.push(`${fieldSchema.title || key} must be a path string`) |
| 390 | } |
| 391 | |
| 392 | // Number range validation |
| 393 | if (fieldSchema.type === 'number' && typeof value === 'number') { |
| 394 | if (fieldSchema.min !== undefined && value < fieldSchema.min) { |
| 395 | errors.push( |
| 396 | `${fieldSchema.title || key} must be at least ${fieldSchema.min}`, |
| 397 | ) |
| 398 | } |
| 399 | if (fieldSchema.max !== undefined && value > fieldSchema.max) { |
| 400 | errors.push( |
| 401 | `${fieldSchema.title || key} must be at most ${fieldSchema.max}`, |
| 402 | ) |
| 403 | } |
no test coverage detected