diff --git a/src/lib/js/recipeJsonLd.ts b/src/lib/js/recipeJsonLd.ts index d4ce935..1f68b2d 100644 --- a/src/lib/js/recipeJsonLd.ts +++ b/src/lib/js/recipeJsonLd.ts @@ -1,4 +1,4 @@ -function parseTimeToISO8601(timeString: string): string | undefined { +function parseTimeToISO8601(timeString: string | undefined): string | undefined { if (!timeString) return undefined; // Handle common German time formats @@ -66,7 +66,7 @@ export function generateRecipeJsonLd(data: RecipeModelType) { "name": "Alexander Bocken" }, "datePublished": data.dateCreated ? new Date(data.dateCreated).toISOString() : undefined, - "dateModified": data.dateModified || data.updatedAt ? new Date(data.dateModified || data.updatedAt).toISOString() : undefined, + "dateModified": data.dateModified ? new Date(data.dateModified).toISOString() : undefined, "recipeCategory": data.category, "keywords": data.tags?.join(', '), "image": { @@ -98,7 +98,7 @@ export function generateRecipeJsonLd(data: RecipeModelType) { // Extract ingredients if (data.ingredients) { for (const ingredientGroup of data.ingredients) { - if (ingredientGroup.list) { + if ('list' in ingredientGroup && ingredientGroup.list) { for (const ingredient of ingredientGroup.list) { if (ingredient.name) { let ingredientText = ingredient.name; @@ -115,7 +115,7 @@ export function generateRecipeJsonLd(data: RecipeModelType) { // Extract instructions if (data.instructions) { for (const instructionGroup of data.instructions) { - if (instructionGroup.steps) { + if ('steps' in instructionGroup && instructionGroup.steps) { for (let i = 0; i < instructionGroup.steps.length; i++) { jsonLd.recipeInstructions.push({ "@type": "HowToStep", diff --git a/src/lib/server/cache.ts b/src/lib/server/cache.ts index f34e791..59bf059 100644 --- a/src/lib/server/cache.ts +++ b/src/lib/server/cache.ts @@ -312,7 +312,7 @@ export async function invalidateRecipeCaches(): Promise { */ export async function invalidateCospendCaches(usernames: string[], paymentId?: string): Promise { try { - const invalidations: Promise[] = []; + const invalidations: Promise[] = []; // Invalidate balance and debts caches for all affected users for (const username of usernames) { diff --git a/src/lib/server/recipeHelpers.ts b/src/lib/server/recipeHelpers.ts index 6e24bf6..315b95d 100644 --- a/src/lib/server/recipeHelpers.ts +++ b/src/lib/server/recipeHelpers.ts @@ -32,17 +32,18 @@ export function briefQueryConfig(recipeLang: string) { */ export function toBrief(recipe: RecipeModelType, recipeLang: string): BriefRecipeType { if (isEnglish(recipeLang)) { + const en = recipe.translations?.en; return { _id: recipe._id, - name: recipe.translations.en.name, - short_name: recipe.translations.en.short_name, + name: en?.name ?? '', + short_name: en?.short_name ?? '', images: recipe.images?.[0] ? [{ alt: recipe.images[0].alt, mediapath: recipe.images[0].mediapath, color: recipe.images[0].color }] : [], - tags: recipe.translations.en.tags || [], - category: recipe.translations.en.category, + tags: en?.tags || [], + category: en?.category ?? '', icon: recipe.icon, - description: recipe.translations.en.description, + description: en?.description, season: recipe.season || [], dateCreated: recipe.dateCreated, dateModified: recipe.dateModified, diff --git a/src/models/WorkoutSession.ts b/src/models/WorkoutSession.ts index 10beb8c..bb98fb5 100644 --- a/src/models/WorkoutSession.ts +++ b/src/models/WorkoutSession.ts @@ -213,4 +213,5 @@ const WorkoutSessionSchema = new mongoose.Schema( WorkoutSessionSchema.index({ createdBy: 1, startTime: -1 }); WorkoutSessionSchema.index({ templateId: 1 }); -export const WorkoutSession = mongoose.models.WorkoutSession as mongoose.Model ?? mongoose.model("WorkoutSession", WorkoutSessionSchema); \ No newline at end of file +// @ts-expect-error Mongoose model() produces a union type too complex for TS +export const WorkoutSession: mongoose.Model = mongoose.models.WorkoutSession || mongoose.model("WorkoutSession", WorkoutSessionSchema); \ No newline at end of file diff --git a/src/routes/[recipeLang=recipeLang]/+layout.ts b/src/routes/[recipeLang=recipeLang]/+layout.ts index 087e3c5..8cfd32c 100644 --- a/src/routes/[recipeLang=recipeLang]/+layout.ts +++ b/src/routes/[recipeLang=recipeLang]/+layout.ts @@ -1,7 +1,8 @@ import { browser } from '$app/environment'; import { error } from '@sveltejs/kit'; +import type { LayoutLoad } from './$types'; -export async function load({ params, data }) { +export const load: LayoutLoad = async ({ params, data }) => { // Validate recipeLang parameter if (params.recipeLang !== 'rezepte' && params.recipeLang !== 'recipes') { throw error(404, 'Not found'); @@ -31,4 +32,4 @@ export async function load({ params, data }) { recipeLang: params.recipeLang, isOffline: false }; -} +}; diff --git a/src/routes/[recipeLang=recipeLang]/+page.ts b/src/routes/[recipeLang=recipeLang]/+page.ts index 8b00fce..28844fc 100644 --- a/src/routes/[recipeLang=recipeLang]/+page.ts +++ b/src/routes/[recipeLang=recipeLang]/+page.ts @@ -2,8 +2,9 @@ import { browser } from '$app/environment'; import { isOffline, canUseOfflineData } from '$lib/offline/helpers'; import { getAllBriefRecipes, getBriefRecipesBySeason, isOfflineDataAvailable } from '$lib/offline/db'; import { rand_array } from '$lib/js/randomize'; +import type { PageLoad } from './$types'; -export async function load({ data }) { +export const load: PageLoad = async ({ data }) => { // On the server, just pass through the server data unchanged if (!browser) { return { @@ -48,4 +49,4 @@ export async function load({ data }) { ...data, isOffline: false }; -} +}; diff --git a/src/routes/[recipeLang=recipeLang]/[name]/+page.server.ts b/src/routes/[recipeLang=recipeLang]/[name]/+page.server.ts index 85c72c9..1c827b6 100644 --- a/src/routes/[recipeLang=recipeLang]/[name]/+page.server.ts +++ b/src/routes/[recipeLang=recipeLang]/[name]/+page.server.ts @@ -1,5 +1,5 @@ import { redirect, error } from '@sveltejs/kit'; -import type { PageServerLoad } from './$types'; +import type { PageServerLoad, Actions } from './$types'; import { stripHtmlTags } from '$lib/js/stripHtmlTags'; export const load: PageServerLoad = async ({ fetch, params, locals }) => { @@ -31,7 +31,7 @@ export const load: PageServerLoad = async ({ fetch, params, locals }) => { }; }; -export const actions = { +export const actions: Actions = { toggleFavorite: async ({ request, locals, url, fetch }) => { const session = await locals.auth(); diff --git a/src/routes/[recipeLang=recipeLang]/[name]/+page.ts b/src/routes/[recipeLang=recipeLang]/[name]/+page.ts index ec03e36..96dbd21 100644 --- a/src/routes/[recipeLang=recipeLang]/[name]/+page.ts +++ b/src/routes/[recipeLang=recipeLang]/[name]/+page.ts @@ -3,8 +3,9 @@ import { generateRecipeJsonLd } from '$lib/js/recipeJsonLd'; import { isOffline, canUseOfflineData } from '$lib/offline/helpers'; import { getFullRecipe, isOfflineDataAvailable } from '$lib/offline/db'; import { stripHtmlTags } from '$lib/js/stripHtmlTags'; +import type { PageLoad } from './$types'; -export async function load({ fetch, params, url, data }) { +export const load: PageLoad = async ({ fetch, params, url, data }) => { const isEnglish = params.recipeLang === 'recipes'; // Check if we need to load from IndexedDB (offline mode) @@ -200,4 +201,4 @@ export async function load({ fetch, params, url, data }) { strippedDescription, isOffline: isOfflineMode, }; -} +}; diff --git a/src/routes/[recipeLang=recipeLang]/category/[category]/+page.ts b/src/routes/[recipeLang=recipeLang]/category/[category]/+page.ts index eab80f7..021b737 100644 --- a/src/routes/[recipeLang=recipeLang]/category/[category]/+page.ts +++ b/src/routes/[recipeLang=recipeLang]/category/[category]/+page.ts @@ -2,8 +2,9 @@ import { browser } from '$app/environment'; import { isOffline, canUseOfflineData } from '$lib/offline/helpers'; import { getBriefRecipesByCategory, isOfflineDataAvailable } from '$lib/offline/db'; import { rand_array } from '$lib/js/randomize'; +import type { PageLoad } from './$types'; -export async function load({ data, params }) { +export const load: PageLoad = async ({ data, params }) => { // On the server, just pass through the server data unchanged if (!browser) { return { @@ -37,4 +38,4 @@ export async function load({ data, params }) { ...data, isOffline: false }; -} +}; diff --git a/src/routes/[recipeLang=recipeLang]/icon/[icon]/+page.ts b/src/routes/[recipeLang=recipeLang]/icon/[icon]/+page.ts index 8fb19bd..b2882ac 100644 --- a/src/routes/[recipeLang=recipeLang]/icon/[icon]/+page.ts +++ b/src/routes/[recipeLang=recipeLang]/icon/[icon]/+page.ts @@ -2,8 +2,9 @@ import { browser } from '$app/environment'; import { isOffline, canUseOfflineData } from '$lib/offline/helpers'; import { getBriefRecipesByIcon, getAllBriefRecipes, isOfflineDataAvailable } from '$lib/offline/db'; import { rand_array } from '$lib/js/randomize'; +import type { PageLoad } from './$types'; -export async function load({ data, params }) { +export const load: PageLoad = async ({ data, params }) => { // On the server, just pass through the server data unchanged if (!browser) { return { @@ -49,4 +50,4 @@ export async function load({ data, params }) { ...data, isOffline: false }; -} +}; diff --git a/src/routes/[recipeLang=recipeLang]/season/+page.ts b/src/routes/[recipeLang=recipeLang]/season/+page.ts index dd0cfef..7c4ba52 100644 --- a/src/routes/[recipeLang=recipeLang]/season/+page.ts +++ b/src/routes/[recipeLang=recipeLang]/season/+page.ts @@ -2,8 +2,9 @@ import { browser } from '$app/environment'; import { isOffline, canUseOfflineData } from '$lib/offline/helpers'; import { getBriefRecipesBySeason, isOfflineDataAvailable } from '$lib/offline/db'; import { rand_array } from '$lib/js/randomize'; +import type { PageLoad } from './$types'; -export async function load({ data }) { +export const load: PageLoad = async ({ data }) => { // On the server, just pass through the server data unchanged if (!browser) { return { @@ -38,4 +39,4 @@ export async function load({ data }) { ...data, isOffline: false }; -} +}; diff --git a/src/routes/[recipeLang=recipeLang]/season/[month]/+page.ts b/src/routes/[recipeLang=recipeLang]/season/[month]/+page.ts index 4b1a1b9..47a5ea0 100644 --- a/src/routes/[recipeLang=recipeLang]/season/[month]/+page.ts +++ b/src/routes/[recipeLang=recipeLang]/season/[month]/+page.ts @@ -2,8 +2,9 @@ import { browser } from '$app/environment'; import { isOffline, canUseOfflineData } from '$lib/offline/helpers'; import { getBriefRecipesBySeason, isOfflineDataAvailable } from '$lib/offline/db'; import { rand_array } from '$lib/js/randomize'; +import type { PageLoad } from './$types'; -export async function load({ data, params }) { +export const load: PageLoad = async ({ data, params }) => { // On the server, just pass through the server data unchanged if (!browser) { return { @@ -38,4 +39,4 @@ export async function load({ data, params }) { ...data, isOffline: false }; -} +}; diff --git a/src/routes/[recipeLang=recipeLang]/tag/[tag]/+page.ts b/src/routes/[recipeLang=recipeLang]/tag/[tag]/+page.ts index af2e5e0..6c32e1f 100644 --- a/src/routes/[recipeLang=recipeLang]/tag/[tag]/+page.ts +++ b/src/routes/[recipeLang=recipeLang]/tag/[tag]/+page.ts @@ -2,8 +2,9 @@ import { browser } from '$app/environment'; import { isOffline, canUseOfflineData } from '$lib/offline/helpers'; import { getBriefRecipesByTag, isOfflineDataAvailable } from '$lib/offline/db'; import { rand_array } from '$lib/js/randomize'; +import type { PageLoad } from './$types'; -export async function load({ data, params }) { +export const load: PageLoad = async ({ data, params }) => { // On the server, just pass through the server data unchanged if (!browser) { return { @@ -37,4 +38,4 @@ export async function load({ data, params }) { ...data, isOffline: false }; -} +}; diff --git a/src/routes/api/[recipeLang=recipeLang]/items/[name]/+server.ts b/src/routes/api/[recipeLang=recipeLang]/items/[name]/+server.ts index e0f7d2b..d473ea2 100644 --- a/src/routes/api/[recipeLang=recipeLang]/items/[name]/+server.ts +++ b/src/routes/api/[recipeLang=recipeLang]/items/[name]/+server.ts @@ -5,10 +5,8 @@ import { error } from '@sveltejs/kit'; import type { RecipeModelType, IngredientItem, InstructionItem } from '$types/types'; import { isEnglish } from '$lib/server/recipeHelpers'; -type RecipeItem = (IngredientItem | InstructionItem) & { baseRecipeRef?: Record; resolvedRecipe?: Record }; - /** Recursively map populated baseRecipeRef to resolvedRecipe field */ -function mapBaseRecipeRefs(items: RecipeItem[]): RecipeItem[] { +function mapBaseRecipeRefs(items: any[]): any[] { return items.map((item) => { if (item.type === 'reference' && item.baseRecipeRef) { const resolvedRecipe = { ...item.baseRecipeRef }; @@ -131,10 +129,10 @@ export const GET: RequestHandler = async ({ params }) => { }; if (recipe.ingredients) { - recipe.ingredients = mapBaseRecipeRefs(recipe.ingredients as RecipeItem[]); + recipe.ingredients = mapBaseRecipeRefs(recipe.ingredients as any[]); } if (recipe.instructions) { - recipe.instructions = mapBaseRecipeRefs(recipe.instructions as RecipeItem[]); + recipe.instructions = mapBaseRecipeRefs(recipe.instructions as any[]); } // Merge English alt/caption with original image paths diff --git a/src/routes/api/cospend/monthly-expenses/+server.ts b/src/routes/api/cospend/monthly-expenses/+server.ts index 8c43d48..c6c76f0 100644 --- a/src/routes/api/cospend/monthly-expenses/+server.ts +++ b/src/routes/api/cospend/monthly-expenses/+server.ts @@ -76,7 +76,7 @@ export const GET: RequestHandler = async ({ url, locals }) => { } ]; - const results = await Payment.aggregate(pipeline); + const results = await Payment.aggregate(pipeline as any[]); // Transform data into chart-friendly format const monthsMap = new Map(); diff --git a/src/routes/api/cospend/payments/+server.ts b/src/routes/api/cospend/payments/+server.ts index 5f6adaf..130fe47 100644 --- a/src/routes/api/cospend/payments/+server.ts +++ b/src/routes/api/cospend/payments/+server.ts @@ -87,7 +87,7 @@ export const POST: RequestHandler = async ({ request, locals }) => { // Validate personal + equal split method if (splitMethod === 'personal_equal' && splits) { const totalPersonal = splits.reduce((sum: number, split: SplitInput) => { - return sum + (parseFloat(split.personalAmount) || 0); + return sum + (split.personalAmount ?? 0); }, 0); if (totalPersonal > amount) { diff --git a/src/routes/api/cospend/recurring-payments/+server.ts b/src/routes/api/cospend/recurring-payments/+server.ts index f7ae69d..45dfdf4 100644 --- a/src/routes/api/cospend/recurring-payments/+server.ts +++ b/src/routes/api/cospend/recurring-payments/+server.ts @@ -87,7 +87,7 @@ export const POST: RequestHandler = async ({ request, locals }) => { // Validate personal + equal split method if (splitMethod === 'personal_equal' && splits) { const totalPersonal = splits.reduce((sum: number, split: { personalAmount?: number }) => { - return sum + (parseFloat(split.personalAmount) || 0); + return sum + (split.personalAmount ?? 0); }, 0); if (totalPersonal > amount) { diff --git a/src/routes/api/cospend/recurring-payments/[id]/+server.ts b/src/routes/api/cospend/recurring-payments/[id]/+server.ts index b1b7aa3..03374d9 100644 --- a/src/routes/api/cospend/recurring-payments/[id]/+server.ts +++ b/src/routes/api/cospend/recurring-payments/[id]/+server.ts @@ -114,7 +114,7 @@ export const PUT: RequestHandler = async ({ params, request, locals }) => { // Validate personal + equal split method if (splitMethod === 'personal_equal' && splits && amount) { const totalPersonal = splits.reduce((sum: number, split: { personalAmount?: number }) => { - return sum + (parseFloat(split.personalAmount) || 0); + return sum + (split.personalAmount ?? 0); }, 0); if (totalPersonal > amount) { @@ -127,7 +127,7 @@ export const PUT: RequestHandler = async ({ params, request, locals }) => { const updatedPayment = { ...existingPayment.toObject(), ...updateData }; updateData.nextExecutionDate = calculateNextExecutionDate( updatedPayment, - updateData.startDate || existingPayment.startDate + (updateData.startDate || existingPayment.startDate) as Date ); } diff --git a/src/routes/api/generate-alt-text-bulk/+server.ts b/src/routes/api/generate-alt-text-bulk/+server.ts index 71cda09..250a075 100644 --- a/src/routes/api/generate-alt-text-bulk/+server.ts +++ b/src/routes/api/generate-alt-text-bulk/+server.ts @@ -61,6 +61,7 @@ export const POST: RequestHandler = async ({ request, locals }) => { for (const recipe of recipes) { let processed = 0; let failed = 0; + if (!recipe.images) continue; for (let i = 0; i < recipe.images.length; i++) { const image = recipe.images[i]; diff --git a/src/routes/api/recalculate-image-colors/+server.ts b/src/routes/api/recalculate-image-colors/+server.ts index 2e265b3..e1cb8e7 100644 --- a/src/routes/api/recalculate-image-colors/+server.ts +++ b/src/routes/api/recalculate-image-colors/+server.ts @@ -48,6 +48,7 @@ export const POST: RequestHandler = async ({ request, locals }) => { }> = []; for (const recipe of recipes) { + if (!recipe.images?.length) continue; const image = recipe.images[0]; if (!image?.mediapath) continue;