{"version":3,"file":"parse-cRX-yCqd.mjs","names":[],"sources":["../src/api/parse.ts"],"sourcesContent":["/**\n * Request body and query parameter parsing with Zod validation.\n *\n * All API routes should use these utilities instead of `request.json() as T`\n * or raw `url.searchParams.get()` with manual coercion.\n */\n\nimport { z } from \"zod\";\n\nimport { apiError } from \"./error.js\";\n\n/** Maximum allowed JSON request body size (10 MB). */\nconst MAX_BODY_SIZE = 10 * 1024 * 1024;\n\n/**\n * Result of parsing: either the validated data or an error Response.\n * Routes should check `if (result instanceof Response) return result;`\n */\nexport type ParseResult<T> = T | Response;\n\n/**\n * Parse and validate a JSON request body against a Zod schema.\n *\n * Returns the validated data on success, or a 400 Response on failure.\n * Replaces all `(await request.json()) as T` casts.\n */\nexport async function parseBody<T extends z.ZodType>(\n\trequest: Request,\n\tschema: T,\n): Promise<ParseResult<z.infer<T>>> {\n\t// Best-effort size check via Content-Length (can be absent with chunked encoding)\n\tconst contentLength = request.headers.get(\"Content-Length\");\n\tif (contentLength && parseInt(contentLength, 10) > MAX_BODY_SIZE) {\n\t\treturn apiError(\"PAYLOAD_TOO_LARGE\", \"Request body too large\", 413);\n\t}\n\n\tlet raw: unknown;\n\ttry {\n\t\traw = await request.json();\n\t} catch {\n\t\treturn apiError(\"INVALID_JSON\", \"Request body must be valid JSON\", 400);\n\t}\n\n\treturn validate(schema, raw);\n}\n\n/**\n * Parse and validate an optional JSON request body.\n *\n * Returns `defaultValue` if the body is empty, or the validated data if present.\n * For endpoints where the body is optional (e.g., preview-url, confirm).\n */\nexport async function parseOptionalBody<T extends z.ZodType>(\n\trequest: Request,\n\tschema: T,\n\tdefaultValue: z.infer<T>,\n): Promise<ParseResult<z.infer<T>>> {\n\t// Best-effort size check via Content-Length (can be absent with chunked encoding)\n\tconst contentLength = request.headers.get(\"Content-Length\");\n\tif (contentLength && parseInt(contentLength, 10) > MAX_BODY_SIZE) {\n\t\treturn apiError(\"PAYLOAD_TOO_LARGE\", \"Request body too large\", 413);\n\t}\n\n\tlet text: string;\n\ttry {\n\t\ttext = await request.text();\n\t} catch {\n\t\treturn defaultValue;\n\t}\n\n\tif (!text.trim()) {\n\t\treturn defaultValue;\n\t}\n\n\tlet raw: unknown;\n\ttry {\n\t\traw = JSON.parse(text);\n\t} catch {\n\t\treturn apiError(\"INVALID_JSON\", \"Request body must be valid JSON\", 400);\n\t}\n\n\treturn validate(schema, raw);\n}\n\n/**\n * Parse and validate URL search params against a Zod schema.\n *\n * Converts searchParams to a plain object before validation.\n * Zod coercion handles string -> number/boolean conversion.\n * Replaces manual `url.searchParams.get()` + `parseInt()` patterns.\n */\nexport function parseQuery<T extends z.ZodType>(url: URL, schema: T): ParseResult<z.infer<T>> {\n\tconst raw: Record<string, string> = {};\n\tfor (const [key, value] of url.searchParams) {\n\t\traw[key] = value;\n\t}\n\treturn validate(schema, raw);\n}\n\n/**\n * Validate raw data against a schema. Returns data or error Response.\n */\nfunction validate<T extends z.ZodType>(schema: T, data: unknown): ParseResult<z.infer<T>> {\n\tconst result = schema.safeParse(data);\n\n\tif (result.success) {\n\t\treturn result.data;\n\t}\n\n\t// Format Zod errors into a readable structure\n\tconst issues = result.error.issues.map((issue: z.ZodIssue) => ({\n\t\tpath: issue.path.join(\".\"),\n\t\tmessage: issue.message,\n\t}));\n\n\treturn apiError(\"VALIDATION_ERROR\", \"Invalid request data\", 400, { issues });\n}\n\n/**\n * Type guard to check if a ParseResult is an error Response.\n * Usage: `if (isParseError(result)) return result;`\n */\nexport function isParseError<T>(result: ParseResult<T>): result is Response {\n\treturn result instanceof Response;\n}\n"],"mappings":";;;;;AAYA,MAAM,gBAAgB,KAAK,OAAO;;;;;;;AAclC,eAAsB,UACrB,SACA,QACmC;CAEnC,MAAM,gBAAgB,QAAQ,QAAQ,IAAI,iBAAiB;AAC3D,KAAI,iBAAiB,SAAS,eAAe,GAAG,GAAG,cAClD,QAAO,SAAS,qBAAqB,0BAA0B,IAAI;CAGpE,IAAI;AACJ,KAAI;AACH,QAAM,MAAM,QAAQ,MAAM;SACnB;AACP,SAAO,SAAS,gBAAgB,mCAAmC,IAAI;;AAGxE,QAAO,SAAS,QAAQ,IAAI;;;;;;;;AAS7B,eAAsB,kBACrB,SACA,QACA,cACmC;CAEnC,MAAM,gBAAgB,QAAQ,QAAQ,IAAI,iBAAiB;AAC3D,KAAI,iBAAiB,SAAS,eAAe,GAAG,GAAG,cAClD,QAAO,SAAS,qBAAqB,0BAA0B,IAAI;CAGpE,IAAI;AACJ,KAAI;AACH,SAAO,MAAM,QAAQ,MAAM;SACpB;AACP,SAAO;;AAGR,KAAI,CAAC,KAAK,MAAM,CACf,QAAO;CAGR,IAAI;AACJ,KAAI;AACH,QAAM,KAAK,MAAM,KAAK;SACf;AACP,SAAO,SAAS,gBAAgB,mCAAmC,IAAI;;AAGxE,QAAO,SAAS,QAAQ,IAAI;;;;;;;;;AAU7B,SAAgB,WAAgC,KAAU,QAAoC;CAC7F,MAAM,MAA8B,EAAE;AACtC,MAAK,MAAM,CAAC,KAAK,UAAU,IAAI,aAC9B,KAAI,OAAO;AAEZ,QAAO,SAAS,QAAQ,IAAI;;;;;AAM7B,SAAS,SAA8B,QAAW,MAAwC;CACzF,MAAM,SAAS,OAAO,UAAU,KAAK;AAErC,KAAI,OAAO,QACV,QAAO,OAAO;AASf,QAAO,SAAS,oBAAoB,wBAAwB,KAAK,EAAE,QALpD,OAAO,MAAM,OAAO,KAAK,WAAuB;EAC9D,MAAM,MAAM,KAAK,KAAK,IAAI;EAC1B,SAAS,MAAM;EACf,EAAE,EAEwE,CAAC;;;;;;AAO7E,SAAgB,aAAgB,QAA4C;AAC3E,QAAO,kBAAkB"}