import fs from 'node:fs/promises'; import path from 'node:path'; const ITALIAN_MONTH_NAMES = [ 'GENNAIO', 'FEBBRAIO', 'MARZO', 'APRILE', 'MAGGIO', 'GIUGNO', 'LUGLIO', 'AGOSTO', 'SETTEMBRE', 'OTTOBRE', 'NOVEMBRE', 'DICEMBRE' ]; function sanitizePathSegment(value) { const normalized = String(value || '').trim(); if (!normalized) { return ''; } if (normalized === '.' || normalized === '..' || normalized.includes('..') || /[\\/]/.test(normalized)) { throw new Error('Invalid race storage path segment'); } return normalized; } function parseRelativeStorageSegments(value) { const normalized = String(value || '').trim().replace(/\\/g, '/'); if (!normalized) { return []; } return normalized .split('/') .map((segment) => segment.trim()) .filter(Boolean) .map((segment) => sanitizePathSegment(segment)); } export function normalizeRaceFolderName(value) { return String(value || '') .trim() .replace(/[<>:"/\\|?*]/g, ' ') .replace(/\s+/g, ' ') .toUpperCase(); } export function buildMonthFolder(year, monthIndex) { const safeYear = sanitizePathSegment(year); const normalizedMonthIndex = Number(monthIndex); if (!safeYear || Number.isNaN(normalizedMonthIndex) || normalizedMonthIndex < 1 || normalizedMonthIndex > 12) { return ''; } return `${String(normalizedMonthIndex).padStart(2, '0')}.${ITALIAN_MONTH_NAMES[normalizedMonthIndex - 1]}`; } export function buildRaceStorage(storageInput = {}) { const relativeSegments = parseRelativeStorageSegments(storageInput.relativeDir); const relativeYear = relativeSegments[0] || ''; const relativeMonthFolder = relativeSegments.length >= 3 ? relativeSegments[1] : ''; const relativeRaceFolder = relativeSegments.length >= 3 ? relativeSegments[2] : (relativeSegments.length >= 2 ? relativeSegments[1] : ''); const year = sanitizePathSegment(storageInput.year || relativeYear); const monthFolder = sanitizePathSegment(storageInput.monthFolder || relativeMonthFolder); const raceFolder = sanitizePathSegment(normalizeRaceFolderName(storageInput.raceFolder || relativeRaceFolder)); if (!year || !monthFolder || !raceFolder) { return null; } return { year, monthFolder, raceFolder, relativeDir: path.posix.join(year, monthFolder, raceFolder) }; } export async function resolveRacePklAvailability({ pklRoot, race }) { if (!pklRoot) { return { available: false, reasonCode: 'PKL_ROOT_NOT_CONFIGURED', message: 'The PKL root is not configured for this FaceAI environment.', storage: null }; } const storage = buildRaceStorage(race?.storage || race); if (!storage) { return { available: false, reasonCode: 'MISSING_RACE_STORAGE', message: 'The legacy handoff did not provide the folder metadata required to resolve FaceAI data for this race.', storage: null }; } const raceDir = path.join(pklRoot, storage.year, storage.monthFolder, storage.raceFolder); let entries; try { entries = await fs.readdir(raceDir, { withFileTypes: true }); } catch (error) { if (error?.code === 'ENOENT') { return { available: false, reasonCode: 'RACE_DIRECTORY_NOT_FOUND', message: `No FaceAI dataset directory exists for ${storage.relativeDir}.`, storage, raceDir }; } throw error; } const pklEntry = entries .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith('.pkl')) .sort((left, right) => left.name.localeCompare(right.name, 'en'))[0]; if (!pklEntry) { return { available: false, reasonCode: 'PKL_FILE_NOT_FOUND', message: `The race directory ${storage.relativeDir} exists, but it does not contain any .pkl file.`, storage, raceDir }; } return { available: true, reasonCode: null, message: `Using ${storage.relativeDir}/${pklEntry.name}`, storage, raceDir, pklPath: path.join(raceDir, pklEntry.name), pklFileName: pklEntry.name }; }