import { z } from 'zod'; /** * What the model must return, enforced rather than hoped for. * * Constraining the shape is the difference between a bad draft and a crash: * the SDK validates the response against this before any of it reaches the * database, so a model that answers in prose or invents a field becomes a * caught error rather than a row full of nonsense. * * Every field the model may decline to answer is nullable, because it is told * to say nothing rather than guess. A null category means it did not recognise * one, which is a better answer than a wrong one and is exactly what the review * queue exists to resolve. */ /** Beyond anything this shop sells, so an absurd number is caught here. */ const MAX_SUGGESTED_PRICE_CENTS = 1_000_000; export const DraftSchema = z.object({ /** A short title. Until this lands, the item is named for its submission. */ name: z.string().min(1).max(200), /** The storefront body, rendered with html:false like every other stored body. */ description: z.string().min(1).max(4000), /** * Chosen from the categories it was given, or null. The prompt supplies the * closed set; this cannot enforce membership, so applyDraft checks the answer * against the real table before writing anything. */ category: z.string().nullable(), /** Also from a supplied set, and also checked on the way in rather than here. */ tags: z.array(z.string()), /** * Cents, or null when it will not guess. Integer and bounded at both ends, * because a fractional, negative or absurd figure reaching the review queue * is a number somebody has to notice is wrong — and being trustworthy at a * glance is that queue's whole job. */ suggestedPriceCents: z .number() .int() .min(0) .max(MAX_SUGGESTED_PRICE_CENTS) .nullable() }); export type DraftResult = z.infer;