import re filePath = 'apps/backend/src/routes/pages.ts' with open(filePath, 'r') as f: content = f.read() # 1. Update imports old_imports = """import { LocalizedStringSchema, HeroSectionPayloadSchema, CategoriesSectionPayloadSchema, ProductsSectionPayloadSchema, BannerSectionPayloadSchema, TextSectionPayloadSchema, AnnouncementBarSectionPayloadSchema, NewsletterSectionPayloadSchema, EditorialTileSectionPayloadSchema, EditorialCarouselSectionPayloadSchema, HeroArticlePayloadSchema, RichTextPayloadSchema, ScentNotesBreakdownPayloadSchema, QuoteCalloutPayloadSchema, ProductCarouselPayloadSchema, AccordionSectionPayloadSchema, ContactInfoSectionPayloadSchema, ContactFormSectionPayloadSchema } from "@homaroma/types";""" new_imports = """import { LocalizedStringSchema, HeroSectionPayloadSchema, CategoriesSectionPayloadSchema, ProductsSectionPayloadSchema, BannerSectionPayloadSchema, TextSectionPayloadSchema, AnnouncementBarSectionPayloadSchema, NewsletterSectionPayloadSchema, EditorialTileSectionPayloadSchema, EditorialCarouselSectionPayloadSchema, HeroArticleSectionPayloadSchema, RichTextSectionPayloadSchema, ScentNotesBreakdownSectionPayloadSchema, QuoteCalloutSectionPayloadSchema, ProductCarouselSectionPayloadSchema, AccordionSectionPayloadSchema, ContactInfoSectionPayloadSchema, ContactFormSectionPayloadSchema } from "@homaroma/types"; import { z } from "zod";""" content = content.replace(old_imports, new_imports) # 2. Remove manual validation functions # From 'function isLocalizedStringValue' up to 'const RESERVED_SLUGS' start_idx = content.find('function isLocalizedStringValue') end_idx = content.find('const RESERVED_SLUGS') if start_idx != -1 and end_idx != -1: content = content[:start_idx] + content[end_idx:] # 3. Refactor validateSectionPayload old_validate_func = """function validateSectionPayload(type: string, payload: any): string | null { if (!payload || typeof payload !== 'object') { return "Payload must be a JSON object"; } switch (type.toUpperCase()) { case 'HERO': { const res = HeroSectionPayloadSchema.safeParse(payload); if (!res.success) return res.error.issues.map((e: any) => `${e.path.join('.')}: ${e.message}`).join(', '); break; } case 'CATEGORIES': { const res = CategoriesSectionPayloadSchema.safeParse(payload); if (!res.success) return res.error.issues.map((e: any) => `${e.path.join('.')}: ${e.message}`).join(', '); break; } case 'PRODUCTS': { const res = ProductsSectionPayloadSchema.safeParse(payload); if (!res.success) return res.error.issues.map((e: any) => `${e.path.join('.')}: ${e.message}`).join(', '); break; } case 'BANNER': { const res = BannerSectionPayloadSchema.safeParse(payload); if (!res.success) return res.error.issues.map((e: any) => `${e.path.join('.')}: ${e.message}`).join(', '); break; } case 'TEXT': { const res = TextSectionPayloadSchema.safeParse(payload); if (!res.success) return res.error.issues.map((e: any) => `${e.path.join('.')}: ${e.message}`).join(', '); break; } case 'ANNOUNCEMENT_BAR': { const res = AnnouncementBarSectionPayloadSchema.safeParse(payload); if (!res.success) return res.error.issues.map((e: any) => `${e.path.join('.')}: ${e.message}`).join(', '); break; } case 'NEWSLETTER': { const res = NewsletterSectionPayloadSchema.safeParse(payload); if (!res.success) return res.error.issues.map((e: any) => `${e.path.join('.')}: ${e.message}`).join(', '); break; } case 'EDITORIAL_TILE': { const res = EditorialTileSectionPayloadSchema.safeParse(payload); if (!res.success) return res.error.issues.map((e: any) => `${e.path.join('.')}: ${e.message}`).join(', '); break; } case 'EDITORIAL_CAROUSEL': { const res = EditorialCarouselSectionPayloadSchema.safeParse(payload); if (!res.success) return res.error.issues.map((e: any) => `${e.path.join('.')}: ${e.message}`).join(', '); break; } case 'HERO_ARTICLE': { const error = validateHeroArticlePayload(payload); if (error) return error; break; } case 'RICH_TEXT': { const error = validateRichTextPayload(payload); if (error) return error; break; } case 'SCENT_NOTES_BREAKDOWN': { const error = validateScentNotesBreakdownPayload(payload); if (error) return error; break; } case 'QUOTE_CALLOUT': { const error = validateQuoteCalloutPayload(payload); if (error) return error; break; } case 'PRODUCT_CAROUSEL': { const error = validateProductCarouselPayload(payload); if (error) return error; break; } case 'ACCORDION': { const error = validateAccordionPayload(payload); if (error) return error; break; } case 'CONTACT_INFO': { const error = validateContactInfoPayload(payload); if (error) return error; break; } case 'CONTACT_FORM': { const error = validateContactFormPayload(payload); if (error) return error; break; } default: return `Unknown section type: ${type}`; } return null; }""" new_validate_func = """function validateSectionPayload(type: string, payload: any): { success: boolean; issues?: z.ZodIssue[] } { if (!payload || typeof payload !== 'object') { return { success: false, issues: [{ code: 'custom', message: "Payload must be a JSON object", path: [] }] as z.ZodIssue[] }; } let schema: z.ZodTypeAny; switch (type.toUpperCase()) { case 'HERO': schema = HeroSectionPayloadSchema; break; case 'CATEGORIES': schema = CategoriesSectionPayloadSchema; break; case 'PRODUCTS': schema = ProductsSectionPayloadSchema; break; case 'BANNER': schema = BannerSectionPayloadSchema; break; case 'TEXT': schema = TextSectionPayloadSchema; break; case 'ANNOUNCEMENT_BAR': schema = AnnouncementBarSectionPayloadSchema; break; case 'NEWSLETTER': schema = NewsletterSectionPayloadSchema; break; case 'EDITORIAL_TILE': schema = EditorialTileSectionPayloadSchema; break; case 'EDITORIAL_CAROUSEL': schema = EditorialCarouselSectionPayloadSchema; break; case 'HERO_ARTICLE': schema = HeroArticleSectionPayloadSchema; break; case 'RICH_TEXT': schema = RichTextSectionPayloadSchema; break; case 'SCENT_NOTES_BREAKDOWN': schema = ScentNotesBreakdownSectionPayloadSchema; break; case 'QUOTE_CALLOUT': schema = QuoteCalloutSectionPayloadSchema; break; case 'PRODUCT_CAROUSEL': schema = ProductCarouselSectionPayloadSchema; break; case 'ACCORDION': schema = AccordionSectionPayloadSchema; break; case 'CONTACT_INFO': schema = ContactInfoSectionPayloadSchema; break; case 'CONTACT_FORM': schema = ContactFormSectionPayloadSchema; break; default: return { success: false, issues: [{ code: 'custom', message: `Unknown section type: ${type}`, path: [] }] as z.ZodIssue[] }; } const res = schema.safeParse(payload); if (!res.success) { return { success: false, issues: res.error.issues }; } return { success: true }; }""" # I need to use re.sub for validateSectionPayload in case of slight whitespace differences, but a simple find+replace is safer if exact. # Let's just use re.sub with a regex that captures everything from `function validateSectionPayload(type: string, payload: any): string | null {` to the end of the file. pattern = r'function validateSectionPayload\(type: string, payload: any\): string \| null \{[\s\S]*' content = re.sub(pattern, new_validate_func, content) # 4. Update error handling in POST and PUT # POST old_post_err = """ const validationError = validateSectionPayload(type, payload); if (validationError) { set.status = 400; return { error: validationError }; }""" new_post_err = """ const validationResult = validateSectionPayload(type, payload); if (!validationResult.success) { set.status = 400; return { error: "Validation failed", details: validationResult.issues }; }""" content = content.replace(old_post_err, new_post_err) # PUT old_put_err = """ const validationError = validateSectionPayload(validationType, payload); if (validationError) { set.status = 400; return { error: validationError }; }""" new_put_err = """ const validationResult = validateSectionPayload(validationType, payload); if (!validationResult.success) { set.status = 400; return { error: "Validation failed", details: validationResult.issues }; }""" content = content.replace(old_put_err, new_put_err) with open(filePath, 'w') as f: f.write(content)