import type { Encodable } from "@automate.ax/codec" import type { Vercel } from "@vercel/sdk" import * as z from "zod" import { defineAction } from "../../../automation/actions" import { getVercelApi, vercelAccessTokenAccountOptions } from "../lib/api" import { vercelObjectSchema } from "../lib/schemas" const VERCEL_BRANCH_MATCHER_SCHEMA = z.object({ pattern: z.string().min(1), type: z.enum(["endsWith", "equals", "startsWith"]), }) const VERCEL_CUSTOM_ENVIRONMENT_BASE_SCHEMA = z.looseObject({ createdAt: z.number(), id: z.string(), slug: z.string(), type: z.enum(["development", "preview", "production"]), updatedAt: z.number(), }) const VERCEL_CUSTOM_ENVIRONMENT_SCHEMA = vercelObjectSchema< Awaited> & Encodable >(VERCEL_CUSTOM_ENVIRONMENT_BASE_SCHEMA) const VERCEL_CUSTOM_ENVIRONMENTS_SCHEMA = vercelObjectSchema< Awaited< ReturnType > & Encodable >( z.looseObject({ accountLimit: z.looseObject({ total: z.number().int().nonnegative() }), environments: VERCEL_CUSTOM_ENVIRONMENT_BASE_SCHEMA.array(), }), ) const VERCEL_CUSTOM_ENVIRONMENT_SCOPE_SCHEMA = z.object({ /** Project ID or name. */ project: z.string().min(1), /** Team ID for a team-owned project. */ teamId: z.string().min(1).optional(), }) /** Lists custom deployment environments for one Vercel project. */ export const listVercelCustomEnvironments = defineAction( "List Vercel custom environments", ) .describe("Lists custom deployment environments for one project.") .account("vercel", vercelAccessTokenAccountOptions()) .input( VERCEL_CUSTOM_ENVIRONMENT_SCOPE_SCHEMA.extend({ /** Filter by Git branch. */ gitBranch: z.string().min(1).optional(), }), ) .output(VERCEL_CUSTOM_ENVIRONMENTS_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getVercelApi(account).environment.getProjectsByIdOrNameCustomEnvironments({ idOrName: input.project, ...(input.gitBranch && { gitBranch: input.gitBranch }), ...(input.teamId && { teamId: input.teamId }), }), ) /** Gets one Vercel custom deployment environment. */ export const getVercelCustomEnvironment = defineAction( "Get Vercel custom environment", ) .describe("Gets one custom deployment environment by ID or slug.") .account("vercel", vercelAccessTokenAccountOptions()) .input( VERCEL_CUSTOM_ENVIRONMENT_SCOPE_SCHEMA.extend({ /** Custom environment ID or slug. */ environment: z.string().min(1), }), ) .output(VERCEL_CUSTOM_ENVIRONMENT_SCHEMA) .retry({ replaySafety: "safe" }) .handler(async ({ account, input }) => getVercelApi(account).environment.getCustomEnvironment({ environmentSlugOrId: input.environment, idOrName: input.project, ...(input.teamId && { teamId: input.teamId }), }), ) /** Creates a custom deployment environment for one Vercel project. */ export const createVercelCustomEnvironment = defineAction( "Create Vercel custom environment", ) .describe("Creates a custom deployment environment for one project.") .account("vercel", vercelAccessTokenAccountOptions()) .input( VERCEL_CUSTOM_ENVIRONMENT_SCOPE_SCHEMA.extend({ /** Optional Git branch matching rule. */ branchMatcher: VERCEL_BRANCH_MATCHER_SCHEMA.optional(), /** Environment ID or name whose variables should be copied. */ copyEnvironmentVariablesFrom: z.string().min(1).optional(), /** Human-readable purpose. */ description: z.string().optional(), /** URL-safe custom environment name. */ slug: z.string().min(1), }), ) .output(VERCEL_CUSTOM_ENVIRONMENT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getVercelApi(account).environment.createCustomEnvironment({ idOrName: input.project, requestBody: { slug: input.slug, ...(input.branchMatcher && { branchMatcher: input.branchMatcher }), ...(input.copyEnvironmentVariablesFrom && { copyEnvVarsFrom: input.copyEnvironmentVariablesFrom, }), ...(input.description !== undefined && { description: input.description, }), }, ...(input.teamId && { teamId: input.teamId }), }), ) /** Updates a Vercel custom deployment environment. */ export const updateVercelCustomEnvironment = defineAction( "Update Vercel custom environment", ) .describe("Updates one custom deployment environment.") .account("vercel", vercelAccessTokenAccountOptions()) .input( VERCEL_CUSTOM_ENVIRONMENT_SCOPE_SCHEMA.extend({ /** Replacement Git branch matching rule, or `null` to clear it. */ branchMatcher: VERCEL_BRANCH_MATCHER_SCHEMA.nullable().optional(), /** Replacement human-readable purpose. */ description: z.string().optional(), /** Custom environment ID or slug. */ environment: z.string().min(1), /** Replacement URL-safe name. */ slug: z.string().min(1).optional(), }), ) .output(VERCEL_CUSTOM_ENVIRONMENT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getVercelApi(account).environment.updateCustomEnvironment({ environmentSlugOrId: input.environment, idOrName: input.project, requestBody: { ...(input.branchMatcher !== undefined && { branchMatcher: input.branchMatcher, }), ...(input.description !== undefined && { description: input.description, }), ...(input.slug && { slug: input.slug }), }, ...(input.teamId && { teamId: input.teamId }), }), ) /** Deletes a Vercel custom deployment environment. */ export const deleteVercelCustomEnvironment = defineAction( "Delete Vercel custom environment", ) .describe("Deletes one custom deployment environment.") .account("vercel", vercelAccessTokenAccountOptions()) .input( VERCEL_CUSTOM_ENVIRONMENT_SCOPE_SCHEMA.extend({ /** Also delete variables left without any environment assignment. */ deleteUnassignedEnvironmentVariables: z.boolean().prefault(false), /** Custom environment ID or slug. */ environment: z.string().min(1), }), ) .output(VERCEL_CUSTOM_ENVIRONMENT_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => getVercelApi(account).environment.removeCustomEnvironment({ environmentSlugOrId: input.environment, idOrName: input.project, requestBody: { deleteUnassignedEnvironmentVariables: input.deleteUnassignedEnvironmentVariables, }, ...(input.teamId && { teamId: input.teamId }), }), )