CognxSafeTrack
chore: execute Sprint 38 technical debt resolution (Type Safety, Zod validation, Vitest, Mock LLM extracted)
d9879cf | import * as fs from 'fs'; | |
| import * as path from 'path'; | |
| import { fileURLToPath } from 'url'; | |
| import { z } from 'zod'; | |
| const __filename = fileURLToPath(import.meta.url); | |
| const __dirname = path.dirname(__filename); | |
| const SuccessMustIncludeSchema = z.object({ | |
| id: z.string(), | |
| desc: z.string(), | |
| weight: z.number() | |
| }); | |
| const CriteriaSchema = z.object({ | |
| version: z.string().optional(), | |
| type: z.string().optional(), | |
| goal: z.string().optional(), | |
| success: z.object({ | |
| mustInclude: z.array(SuccessMustIncludeSchema), | |
| threshold: z.object({ minScore: z.number(), minMustPass: z.number() }).optional() | |
| }).optional(), | |
| evaluation: z.object({ | |
| tone: z.string().optional(), | |
| format: z.string().optional(), | |
| examples: z.string().optional() | |
| }).optional(), | |
| remediation: z.object({ | |
| dayNumber: z.number().optional(), | |
| hint: z.string().optional() | |
| }).optional() | |
| }); | |
| const TrackDaySchema = z.object({ | |
| dayNumber: z.number(), | |
| title: z.string(), | |
| lessonText: z.string(), | |
| exerciseType: z.string().optional(), | |
| exercisePrompt: z.string().optional(), | |
| exerciseCriteria: CriteriaSchema.optional(), | |
| buttonsJson: z.array(z.object({ id: z.string(), title: z.string() })).optional(), | |
| badges: z.array(z.string()).optional(), | |
| imageUrl: z.string().optional(), | |
| videoUrl: z.string().optional(), | |
| videoCaption: z.string().optional() | |
| }); | |
| const TrackSchema = z.object({ | |
| trackId: z.string(), | |
| title: z.string(), | |
| language: z.enum(['WOLOF', 'FR', 'EN']), | |
| description: z.string().optional(), | |
| totalDays: z.number().optional(), | |
| version: z.string().optional(), | |
| days: z.array(TrackDaySchema) | |
| }); | |
| function validateContent() { | |
| console.log('π Starting Content Validation (Zod Pre-commit)...'); | |
| const contentDir = path.resolve(__dirname, '../content/tracks'); | |
| if (!fs.existsSync(contentDir)) { | |
| console.warn(`β οΈ Content directory not found: ${contentDir}`); | |
| return; | |
| } | |
| const files = fs.readdirSync(contentDir).filter(f => f.endsWith('.json')); | |
| let hasErrors = false; | |
| for (const file of files) { | |
| const filePath = path.join(contentDir, file); | |
| try { | |
| const fileContent = fs.readFileSync(filePath, 'utf-8'); | |
| const jsonData = JSON.parse(fileContent); | |
| const r = TrackSchema.safeParse(jsonData); | |
| if (!r.success) { | |
| console.error(`β Validation Failed: ${file}`); | |
| r.error.issues.forEach((e: z.ZodIssue) => { | |
| console.error(` - Path: ${e.path.join('.')}`); | |
| console.error(` - Error: ${e.message}`); | |
| }); | |
| hasErrors = true; | |
| } else { | |
| console.log(`β Validated: ${file}`); | |
| } | |
| } catch (e: unknown) { | |
| hasErrors = true; | |
| console.error(`β Parse Error in ${file}: ${(e instanceof Error ? e.message : String(e))}`); | |
| } | |
| } | |
| if (hasErrors) { | |
| process.exit(1); | |
| } else { | |
| console.log('π All 10 JSON Track files are strictly valid Zod structures.'); | |
| } | |
| } | |
| validateContent(); | |