| 1 | import { existsSync } from 'node:fs'; |
| 2 | import type { TemplateRef, ValidationError, ValidationResult } from '@html-video/core'; |
| 3 | import { capabilities } from './capabilities.js'; |
| 4 | |
| 5 | /** |
| 6 | * Validate a template against the Hyperframes adapter capabilities. |
| 7 | * Cheap & read-only per RFC-01. |
| 8 | */ |
| 9 | export function validate(template: TemplateRef): ValidationResult { |
| 10 | const errors: ValidationError[] = []; |
| 11 | const warnings: ValidationError[] = []; |
| 12 | |
| 13 | if (template.engine !== 'hyperframes') { |
| 14 | errors.push({ |
| 15 | code: 'engine-mismatch', |
| 16 | message: `Template engine "${template.engine}" is not "hyperframes"`, |
| 17 | fix: `Use the @html-video/adapter-${template.engine} adapter instead`, |
| 18 | }); |
| 19 | return { ok: false, errors, warnings }; |
| 20 | } |
| 21 | |
| 22 | if (!template.sourcePath) { |
| 23 | errors.push({ |
| 24 | code: 'missing-source', |
| 25 | message: 'Template has no sourcePath', |
| 26 | }); |
| 27 | } else if (!existsSync(template.sourcePath)) { |
| 28 | errors.push({ |
| 29 | code: 'source-not-found', |
| 30 | message: `Template source not found: ${template.sourcePath}`, |
| 31 | fix: 'Check that the template directory contains the file declared in source_entry', |
| 32 | }); |
| 33 | } |
| 34 | |
| 35 | // Sanity: max resolution from caps |
| 36 | const cap = capabilities; |
| 37 | if (cap.maxResolution.width < 1920) { |
| 38 | warnings.push({ |
| 39 | code: 'low-max-resolution', |
| 40 | message: `Adapter max resolution is ${cap.maxResolution.width}x${cap.maxResolution.height}`, |
| 41 | }); |
| 42 | } |
| 43 | |
| 44 | return { |
| 45 | ok: errors.length === 0, |
| 46 | errors, |
| 47 | warnings, |
| 48 | }; |
| 49 | } |
| 50 |