import { z } from "npm:zod"; export const IngredientSchema = z.object({ quantity: z.string().describe( "e.g., '2', '1/2', or an empty string for 'to taste'", ), unit: z.string().describe('e.g., "g", "tbsp", "cup"'), name: z.string().describe('e.g., "sugar", "flour"'), // note: z.string().describe('optional, e.g., "sifted", "chopped finely"'), }); export type Ingredient = z.infer; export const IngredientGroupSchema = z.object({ name: z.string(), items: z.array(IngredientSchema), }); export type IngredientGroup = z.infer; const recipeSchema = z.object({ name: z.string(), content: z.object({ _type: z.literal("Recipe"), name: z.string().describe( "Title of the Recipe, without the name of the website or author", ), description: z.string().describe( "Optional, short description of the recipe", ), image: z.string().describe("URL of the main image of the recipe"), author: z.object({ _type: z.literal("Person"), name: z.string().describe("author of the Recipe (optional)"), }), recipeEngredient: z.array(z.string()) .describe("List of ingredients"), recipeInstructions: z.array(z.string()).describe("List of instructions"), recipeYield: z.number().describe("Amount of Portions"), prepTime: z.number().describe("Preparation time in minutes"), cookTime: z.number().describe("Cooking time in minutes"), }), }); export type Recipe = z.infer; const noRecipeSchema = z.object({ errorMessages: z.array(z.string()).describe( "List of error messages, if no recipe was found", ), }); export const recipeResponseSchema = z.union([recipeSchema, noRecipeSchema]); export function isValidRecipe( recipe: | Recipe | null | undefined, ) { return recipe?.content?.recipeIngredient?.length && recipe?.content?.recipeIngredient.length > 1 && recipe?.content?.recipeInstructions?.length && recipe.name?.length; } export default recipeSchema;