const fs = require("fs"); const path = require("path"); const repoRoot = path.resolve(__dirname, "..", ".."); const mealsPath = path.join(repoRoot, "data", "meals.json"); function validateThumbnail(meal, index) { if (meal.thumbnail === undefined) { return; } if ( !meal.thumbnail || typeof meal.thumbnail !== "object" || Array.isArray(meal.thumbnail) ) { throw new Error(`Meal ${index} has an invalid "thumbnail" object`); } if (meal.thumbnail.focus === undefined) { return; } const { focus } = meal.thumbnail; if (!focus || typeof focus !== "object" || Array.isArray(focus)) { throw new Error(`Meal ${index} has an invalid "thumbnail.focus" object`); } for (const axis of ["x", "y"]) { if (typeof focus[axis] !== "number" || !Number.isFinite(focus[axis])) { throw new Error( `Meal ${index} has a non-numeric thumbnail focus value for "${axis}"` ); } if (focus[axis] < 0 || focus[axis] > 1) { throw new Error( `Meal ${index} thumbnail focus "${axis}" must be between 0 and 1` ); } } } function validateMeals(meals) { if (!Array.isArray(meals)) { throw new Error("data/meals.json must contain an array"); } const ids = new Set(); for (const [index, meal] of meals.entries()) { if (!meal || typeof meal !== "object") { throw new Error(`Meal at index ${index} must be an object`); } for (const field of ["id", "title", "description"]) { if (typeof meal[field] !== "string" || meal[field].length === 0) { throw new Error(`Meal ${index} is missing required string field "${field}"`); } } if (!/^\d+$/.test(meal.id)) { throw new Error(`Meal ${index} has a non-numeric id "${meal.id}"`); } if (meal.position !== undefined && typeof meal.position !== "string") { throw new Error(`Meal ${index} has a non-string "position" value`); } if (ids.has(meal.id)) { throw new Error(`Duplicate meal id "${meal.id}" found in data/meals.json`); } ids.add(meal.id); validateThumbnail(meal, index); } } function loadMeals() { const meals = JSON.parse(fs.readFileSync(mealsPath, "utf8")); validateMeals(meals); return meals; } function saveMeals(meals) { validateMeals(meals); fs.writeFileSync(mealsPath, `${JSON.stringify(meals, null, 2)}\n`); } function getNextMealId(meals) { if (meals.length === 0) { return "01"; } const nextNumber = Math.max(...meals.map((meal) => Number.parseInt(meal.id, 10))) + 1; const idWidth = Math.max( 2, ...meals.map((meal) => meal.id.length), String(nextNumber).length ); return String(nextNumber).padStart(idWidth, "0"); } module.exports = { getNextMealId, loadMeals, mealsPath, repoRoot, saveMeals, };