import { encodableSchema, type Encodable } from "@automate.ax/codec" import { ClickUpApiError, getClickUpApi as getClickUpContractApi, } from "@automate.ax/integration-contracts/clickup" import * as z from "zod" import { retryableActionError, terminalActionError, } from "../../../automation/actions" import type { ResolvedIntegrationAccount } from "../../../automation/integrations" /** ClickUp service ID used by every packaged action. */ export const CLICKUP_ACCOUNT = "clickup" export const CLICKUP_ACCOUNT_REQUIREMENT = { connections: [ { connectionMethodId: "oauth" }, { connectionMethodId: "personal-token" }, ], } as const export const CLICKUP_ID_SCHEMA = z .string() .trim() .regex(/^[A-Za-z0-9_-]+$/, "Use a ClickUp ID, not a path or URL.") export const CLICKUP_NUMERIC_ID_SCHEMA = z.union([ z.number().int().nonnegative(), z.string().regex(/^\d+$/), ]) export const CLICKUP_TIMESTAMP_SCHEMA = z .union([z.number().int().nonnegative(), z.string().regex(/^\d+$/)]) .transform(Number) export const CLICKUP_VOID_RESPONSE_SCHEMA = z .unknown() .transform(() => undefined) /** * Creates a ClickUp client whose failures carry action retry disposition. * * @param account - Resolved ClickUp integration account. */ export function getClickUpApi( account: ResolvedIntegrationAccount<"clickup">, ): ReturnType { const api = getClickUpContractApi(account) const request = api.request.bind(api) api.request = async (path, options) => { try { return await request(path, options) } catch (error) { if (!(error instanceof ClickUpApiError)) throw error if (error.status === 429) { throw retryableActionError(error, { retryAt: error.rateLimitReset === undefined ? undefined : new Date(error.rateLimitReset * 1_000), }) } throw error.status < 500 ? terminalActionError(error) : error } } return api } /** * Omits undefined properties before public input becomes a provider body. * * @param value - Parsed public input fields. */ export function compactClickUpInput(value: Record): Encodable { return encodableSchema.parse( Object.fromEntries( Object.entries(value).filter(([, item]) => item !== undefined), ), ) }