import * as z from "zod" import { defineAction } from "../../automation/actions" import { getResendApi, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS } from "./lib" const RESEND_ACCOUNT = "resend" const SUBSCRIPTION_SCHEMA = z.enum(["opt_in", "opt_out"]) const VISIBILITY_SCHEMA = z.enum(["public", "private"]) const TOPIC_WIRE_SCHEMA = z.object({ created_at: z.string(), default_subscription: SUBSCRIPTION_SCHEMA, description: z.string().optional(), id: z.string(), name: z.string(), object: z.literal("topic").optional(), visibility: VISIBILITY_SCHEMA, }) const TOPIC_SCHEMA = TOPIC_WIRE_SCHEMA.transform( ({ created_at: createdAt, default_subscription: defaultSubscription, ...topic }) => ({ ...topic, createdAt, defaultSubscription }), ) const TOPIC_LIST_WIRE_SCHEMA = z.object({ data: TOPIC_WIRE_SCHEMA.array(), has_more: z.boolean(), object: z.literal("list"), }) const TOPIC_LIST_SCHEMA = z .object({ data: TOPIC_SCHEMA.array(), has_more: z.boolean(), object: z.literal("list"), }) .transform(({ has_more: hasMore, ...result }) => ({ ...result, hasMore })) const TOPIC_MUTATION_SCHEMA = z.object({ id: z.string(), object: z.literal("topic"), }) const TOPIC_DELETE_SCHEMA = z.object({ deleted: z.boolean(), id: z.string(), object: z.literal("topic"), }) const PAGINATION_SCHEMA = z .object({ after: z.string().min(1).optional(), before: z.string().min(1).optional(), limit: z.number().int().min(1).max(100).prefault(20), }) .refine(({ after, before }) => !(after && before), { message: "after and before cannot be used together.", }) const TOPIC_MUTABLE_FIELDS = { /** Description shown with the topic. */ description: z.string().max(200).optional(), /** Topic name. */ name: z.string().min(1).max(50).optional(), /** Visibility on Resend's unsubscribe page. */ visibility: VISIBILITY_SCHEMA.optional(), } const CREATE_TOPIC_FIELDS_SCHEMA = z.object({ ...TOPIC_MUTABLE_FIELDS, name: TOPIC_MUTABLE_FIELDS.name.unwrap(), visibility: VISIBILITY_SCHEMA.prefault("private"), }) const UPDATE_TOPIC_PATCH_SCHEMA = z .object(TOPIC_MUTABLE_FIELDS) .refine((patch) => Object.keys(patch).length > 0, { message: "Provide at least one topic field to update.", }) /** Creates a Resend subscription topic. */ export const createResendTopic = defineAction("Create Resend topic") .describe("Creates a subscription topic with a fixed default preference.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input( CREATE_TOPIC_FIELDS_SCHEMA.extend({ /** Default preference for contacts without an explicit choice. */ defaultSubscription: SUBSCRIPTION_SCHEMA, }), ) .output(TOPIC_MUTATION_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler(async ({ account, input }) => { const { defaultSubscription, ...topic } = input return await getResendApi(account).call("topics", { body: { ...topic, default_subscription: defaultSubscription }, httpMethod: "POST", responseSchema: TOPIC_MUTATION_SCHEMA, }) }) /** Lists Resend subscription topics with cursor pagination. */ export const listResendTopics = defineAction("List Resend topics") .describe("Lists Resend subscription topics with cursor pagination.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(PAGINATION_SCHEMA) .output(TOPIC_LIST_SCHEMA) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getResendApi(account).call("topics", { query: input, responseSchema: TOPIC_LIST_WIRE_SCHEMA, }), ) /** Retrieves one Resend subscription topic. */ export const getResendTopic = defineAction("Get Resend topic") .describe("Retrieves one Resend subscription topic by ID.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(z.object({ topicId: z.string().min(1) })) .output(TOPIC_SCHEMA) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getResendApi(account).call( `topics/${encodeURIComponent(input.topicId)}`, { responseSchema: TOPIC_WIRE_SCHEMA }, ), ) /** Updates mutable fields of a Resend subscription topic. */ export const updateResendTopic = defineAction("Update Resend topic") .describe("Updates the name, description, or visibility of a topic.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input( z.object({ patch: UPDATE_TOPIC_PATCH_SCHEMA, topicId: z.string().min(1), }), ) .output(TOPIC_MUTATION_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call( `topics/${encodeURIComponent(input.topicId)}`, { body: input.patch, httpMethod: "PATCH", responseSchema: TOPIC_MUTATION_SCHEMA, }, ), ) /** Permanently deletes a Resend subscription topic. */ export const deleteResendTopic = defineAction("Delete Resend topic") .describe("Permanently deletes a Resend subscription topic.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(z.object({ topicId: z.string().min(1) })) .output(TOPIC_DELETE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call( `topics/${encodeURIComponent(input.topicId)}`, { httpMethod: "DELETE", responseSchema: TOPIC_DELETE_SCHEMA }, ), )