import { z } from "zod"; import { ActionCalledFromServerError } from "../../../core/errors/errors-data.js"; import { AstroError } from "../../../core/errors/errors.js"; import { isActionAPIContext } from "../utils.js"; import { ActionError, ActionInputError, callSafely } from "./shared.js"; export * from "./shared.js"; function defineAction({ accept, input: inputSchema, handler }) { const serverHandler = accept === "form" ? getFormServerHandler(handler, inputSchema) : getJsonServerHandler(handler, inputSchema); async function safeServerHandler(unparsedInput) { if (typeof this === "function" || !isActionAPIContext(this)) { throw new AstroError(ActionCalledFromServerError); } return callSafely(() => serverHandler(unparsedInput, this)); } Object.assign(safeServerHandler, { orThrow(unparsedInput) { if (typeof this === "function") { throw new AstroError(ActionCalledFromServerError); } return serverHandler(unparsedInput, this); } }); return safeServerHandler; } function getFormServerHandler(handler, inputSchema) { return async (unparsedInput, context) => { if (!(unparsedInput instanceof FormData)) { throw new ActionError({ code: "UNSUPPORTED_MEDIA_TYPE", message: "This action only accepts FormData." }); } if (!inputSchema) return await handler(unparsedInput, context); const baseSchema = unwrapBaseObjectSchema(inputSchema, unparsedInput); const parsed = await inputSchema.safeParseAsync( baseSchema instanceof z.ZodObject ? formDataToObject(unparsedInput, baseSchema) : unparsedInput ); if (!parsed.success) { throw new ActionInputError(parsed.error.issues); } return await handler(parsed.data, context); }; } function getJsonServerHandler(handler, inputSchema) { return async (unparsedInput, context) => { if (unparsedInput instanceof FormData) { throw new ActionError({ code: "UNSUPPORTED_MEDIA_TYPE", message: "This action only accepts JSON." }); } if (!inputSchema) return await handler(unparsedInput, context); const parsed = await inputSchema.safeParseAsync(unparsedInput); if (!parsed.success) { throw new ActionInputError(parsed.error.issues); } return await handler(parsed.data, context); }; } function formDataToObject(formData, schema) { const obj = schema._def.unknownKeys === "passthrough" ? Object.fromEntries(formData.entries()) : {}; for (const [key, baseValidator] of Object.entries(schema.shape)) { let validator = baseValidator; while (validator instanceof z.ZodOptional || validator instanceof z.ZodNullable || validator instanceof z.ZodDefault) { if (validator instanceof z.ZodDefault && !formData.has(key)) { obj[key] = validator._def.defaultValue(); } validator = validator._def.innerType; } if (!formData.has(key) && key in obj) { continue; } else if (validator instanceof z.ZodBoolean) { const val = formData.get(key); obj[key] = val === "true" ? true : val === "false" ? false : formData.has(key); } else if (validator instanceof z.ZodArray) { obj[key] = handleFormDataGetAll(key, formData, validator); } else { obj[key] = handleFormDataGet(key, formData, validator, baseValidator); } } return obj; } function handleFormDataGetAll(key, formData, validator) { const entries = Array.from(formData.getAll(key)); const elementValidator = validator._def.type; if (elementValidator instanceof z.ZodNumber) { return entries.map(Number); } else if (elementValidator instanceof z.ZodBoolean) { return entries.map(Boolean); } return entries; } function handleFormDataGet(key, formData, validator, baseValidator) { const value = formData.get(key); if (!value) { return baseValidator instanceof z.ZodOptional ? void 0 : null; } return validator instanceof z.ZodNumber ? Number(value) : value; } function unwrapBaseObjectSchema(schema, unparsedInput) { while (schema instanceof z.ZodEffects || schema instanceof z.ZodPipeline) { if (schema instanceof z.ZodEffects) { schema = schema._def.schema; } if (schema instanceof z.ZodPipeline) { schema = schema._def.in; } } if (schema instanceof z.ZodDiscriminatedUnion) { const typeKey = schema._def.discriminator; const typeValue = unparsedInput.get(typeKey); if (typeof typeValue !== "string") return schema; const objSchema = schema._def.optionsMap.get(typeValue); if (!objSchema) return schema; return objSchema; } return schema; } export { defineAction, formDataToObject };