export function sanitizeString( input: string | null | undefined, maxLength: number = 1000 ): string | null { if (input === null || input === undefined) { return null; } const trimmed = input.trim(); if (trimmed.length > maxLength) { throw new Error(`Input exceeds maximum length of ${maxLength}`); } return trimmed; } export function sanitizeUrl(url: string | null | undefined): string | null { if (!url) { return null; } return url.trim(); } export function sanitizeText( input: string | null | undefined, maxLength: number = 10000 ): string | null { if (input === null || input === undefined) { return null; } const trimmed = input.trim(); if (trimmed.length > maxLength) { throw new Error(`Text exceeds maximum length of ${maxLength}`); } return trimmed; } export function sanitizePrice(price: string | null | undefined): number | null { if (!price) { return null; } const parsed = parseFloat(price.trim()); if (isNaN(parsed)) { throw new Error('Invalid price format'); } if (parsed < 0) { throw new Error('Price cannot be negative'); } return parsed; } export function sanitizeColor(color: string | null | undefined): string | null { if (!color) { return null; } return color.trim(); } export function sanitizeCurrency(currency: string | null | undefined): string { if (!currency) { return 'DKK'; } return currency.trim().toUpperCase(); } export function sanitizeUsername(username: string): string { const trimmed = username.trim().toLowerCase(); if (trimmed.length < 3 || trimmed.length > 50) { throw new Error('Username must be between 3 and 50 characters'); } return trimmed; } export function sanitizeId(id: string | null | undefined): string { if (!id) { throw new Error('ID is required'); } const trimmed = id.trim(); if (trimmed.length === 0 || trimmed.length > 100) { throw new Error('Invalid ID'); } return trimmed; } export function sanitizeToken(token: string | null | undefined): string { if (!token) { throw new Error('Token is required'); } const trimmed = token.trim(); if (trimmed.length === 0 || trimmed.length > 100) { throw new Error('Invalid token'); } return trimmed; } // Zod schemas for type-safe form validation import { z } from 'zod'; import { CURRENCIES } from '$lib/utils/currency'; export const currencySchema = z.enum(CURRENCIES); export const itemSchema = z.object({ title: z.string().min(1, 'Title is required').max(255), description: z.preprocess(val => (val === '' ? null : val), z.string().max(2000).optional().nullable()), link: z.preprocess(val => (val === '' ? null : val), z.string().url('Invalid URL').optional().nullable()), imageUrl: z.preprocess(val => (val === '' ? null : val), z.string().url('Invalid image URL').optional().nullable()), price: z.coerce.number().nonnegative().optional().nullable(), currency: currencySchema.default('DKK'), color: z.preprocess(val => (val === '' ? null : val), z.string().optional().nullable()), order: z.coerce.number().int().nonnegative().optional() }); export const updateItemSchema = itemSchema.extend({ itemId: z.string().min(1, 'Item ID is required') }); export const wishlistSchema = z.object({ title: z.string().min(1, 'Title is required').max(255), description: z.string().max(2000).optional().nullable(), color: z.string().optional().nullable(), theme: z.string().optional().nullable().transform((val) => val || 'none'), endDate: z.coerce.date().optional().nullable() }); export const reorderSchema = z.array( z.object({ id: z.string(), order: z.number().int().nonnegative() }) ); export type ItemFormData = z.infer; export type UpdateItemFormData = z.infer; export type WishlistFormData = z.infer;