import { XMLBuilder, XMLParser, XMLValidator } from 'fast-xml-parser'; import { collectionSchema, type Recipe } from './recipe'; export function parseImport(raw: string, format: 'json' | 'xml') { if (Buffer.byteLength(raw, 'utf8') > 2 * 1024 * 1024) throw new Error('Importdatei ist größer als 2 MB.'); let value: unknown; if (format === 'json') value = JSON.parse(raw); else { if (/ /\.(recipe|ingredient|step|timer|category|tag|ref)$|\.images\.image$/.test(String(path)), }); const root = parser.parse(raw).hamham; if (!root) throw new Error('XML benötigt das Wurzelelement hamham.'); const num = (x: unknown, fallback = 0) => (x === undefined || x === '' ? fallback : Number(x)); const bool = (x: unknown) => x === 'true'; const decode = (s: string) => s .replace(/</g, '<') .replace(/>/g, '>') .replace(/"/g, '"') .replace(/'/g, "'") .replace(/&/g, '&'); const decodeTree = (v: unknown): unknown => typeof v === 'string' ? decode(v) : Array.isArray(v) ? v.map(decodeTree) : v && typeof v === 'object' ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, decodeTree(x)])) : v; const recipes = (root.recipes?.recipe ?? []).map((r: Record) => ({ ...r, servings: num(r.servings), prepMinutes: num(r.prepMinutes), cookMinutes: num(r.cookMinutes), restMinutes: num(r.restMinutes), published: bool(r.published), categories: r.categories?.category ?? [], tags: r.tags?.tag ?? [], images: r.images?.image ?? [], ingredients: (r.ingredients?.ingredient ?? []).map((i: Record) => ({ ...i, amount: i.amount === '' || i.amount === undefined ? null : Number(i.amount), optional: bool(i.optional), })), steps: (r.steps?.step ?? []).map((s: Record) => ({ ...s, ingredients: s.ingredients?.ref ?? [], timers: (s.timers?.timer ?? []).map((t: Record) => ({ ...t, seconds: num(t.seconds), })), })), nutrition: r.nutrition && typeof r.nutrition === 'object' ? Object.fromEntries(Object.entries(r.nutrition).map(([k, v]) => [k, num(v)])) : null, })); value = { version: Number(root['@_version']), recipes: decodeTree(recipes) }; } return collectionSchema.parse(value); } export function exportRecipes(recipes: Recipe[], format: 'json' | 'xml') { if (format === 'json') return JSON.stringify({ version: 1, recipes }, null, 2); const data = recipes.map((r) => ({ ...r, categories: { category: r.categories }, tags: { tag: r.tags }, images: { image: r.images }, ingredients: { ingredient: r.ingredients.map((i) => ({ ...i, amount: i.amount ?? '' })) }, steps: { step: r.steps.map((s) => ({ ...s, ingredients: { ref: s.ingredients }, timers: { timer: s.timers }, })), }, nutrition: r.nutrition ?? '', })); return ( '\n' + new XMLBuilder({ ignoreAttributes: false, format: true }).build({ hamham: { '@_version': 1, recipes: { recipe: data } }, }) ); }