import * as z from "zod" import { defineAction } from "../../automation/actions" import { getResendApi, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS } from "./lib" const RESEND_ACCOUNT = "resend" const DOMAIN_REGION_SCHEMA = z.enum([ "us-east-1", "eu-west-1", "sa-east-1", "ap-northeast-1", ]) const DOMAIN_STATUS_SCHEMA = z.enum([ "pending", "verified", "failed", "not_started", "partially_verified", "partially_failed", ]) const DOMAIN_RECORD_STATUS_SCHEMA = z.enum([ "pending", "verified", "failed", "temporary_failure", "not_started", ]) const DOMAIN_CAPABILITY_SCHEMA = z.enum(["enabled", "disabled"]) const DOMAIN_CAPABILITIES_SCHEMA = z.object({ receiving: DOMAIN_CAPABILITY_SCHEMA.optional(), sending: DOMAIN_CAPABILITY_SCHEMA.optional(), }) const DOMAIN_CAPABILITIES_INPUT_SCHEMA = z .object({ /** Whether this domain accepts inbound email through Resend. */ receiving: DOMAIN_CAPABILITY_SCHEMA.optional(), /** Whether this domain can send email through Resend. */ sending: DOMAIN_CAPABILITY_SCHEMA.optional(), }) .refine( ({ receiving, sending }) => receiving !== "disabled" || sending !== "disabled", "At least one domain capability must be enabled.", ) const DOMAIN_RECORD_BASE_SHAPE = { name: z.string().min(1), priority: z.number().int().optional(), routing_policy: z.string().optional(), status: DOMAIN_RECORD_STATUS_SCHEMA, ttl: z.string().min(1), value: z.string(), } const DOMAIN_RECORD_WIRE_SCHEMA = z.discriminatedUnion("record", [ z.object({ ...DOMAIN_RECORD_BASE_SHAPE, proxy_status: z.enum(["enable", "disable"]).optional(), record: z.literal("SPF"), type: z.enum(["MX", "TXT"]), }), z.object({ ...DOMAIN_RECORD_BASE_SHAPE, proxy_status: z.enum(["enable", "disable"]).optional(), record: z.literal("DKIM"), type: z.enum(["CNAME", "TXT"]), }), z.object({ ...DOMAIN_RECORD_BASE_SHAPE, priority: z.number().int(), record: z.literal("Receiving"), type: z.literal("MX"), }), z.object({ ...DOMAIN_RECORD_BASE_SHAPE, record: z.literal("Tracking"), type: z.literal("CNAME"), }), z.object({ ...DOMAIN_RECORD_BASE_SHAPE, record: z.literal("TrackingCAA"), type: z.literal("CAA"), }), ]) const DOMAIN_RECORD_SCHEMA = DOMAIN_RECORD_WIRE_SCHEMA.transform((record) => { if (record.record === "SPF" || record.record === "DKIM") { const { proxy_status: proxyStatus, routing_policy: routingPolicy, ...result } = record return { ...result, proxyStatus, routingPolicy } } const { routing_policy: routingPolicy, ...result } = record return { ...result, routingPolicy } }) const DOMAIN_WIRE_SCHEMA = z.object({ capabilities: DOMAIN_CAPABILITIES_SCHEMA.optional(), click_tracking: z.boolean().optional(), created_at: z.coerce.date(), id: z.string().min(1), name: z.string().min(1), object: z.literal("domain"), open_tracking: z.boolean().optional(), records: DOMAIN_RECORD_WIRE_SCHEMA.array(), region: DOMAIN_REGION_SCHEMA, status: DOMAIN_STATUS_SCHEMA, tracking_subdomain: z.string().optional(), }) const DOMAIN_SCHEMA = DOMAIN_WIRE_SCHEMA.transform( ({ click_tracking: clickTracking, created_at: createdAt, open_tracking: openTracking, records, tracking_subdomain: trackingSubdomain, ...domain }) => ({ ...domain, clickTracking, createdAt, openTracking, records: records.map((record) => DOMAIN_RECORD_SCHEMA.parse(record)), trackingSubdomain, }), ) const DOMAIN_LIST_ITEM_WIRE_SCHEMA = DOMAIN_WIRE_SCHEMA.omit({ object: true, records: true, tracking_subdomain: true, }) const DOMAIN_LIST_ITEM_SCHEMA = DOMAIN_LIST_ITEM_WIRE_SCHEMA.transform( ({ click_tracking: clickTracking, created_at: createdAt, open_tracking: openTracking, ...domain }) => ({ ...domain, clickTracking, createdAt, openTracking }), ) const CREATE_DOMAIN_RESPONSE_WIRE_SCHEMA = DOMAIN_WIRE_SCHEMA.omit({ object: true, }) const CREATE_DOMAIN_RESPONSE_SCHEMA = CREATE_DOMAIN_RESPONSE_WIRE_SCHEMA.transform( ({ click_tracking: clickTracking, created_at: createdAt, open_tracking: openTracking, records, tracking_subdomain: trackingSubdomain, ...domain }) => ({ ...domain, clickTracking, createdAt, openTracking, records: records.map((record) => DOMAIN_RECORD_SCHEMA.parse(record)), trackingSubdomain, }), ) const DOMAIN_LIST_WIRE_SCHEMA = z.object({ data: DOMAIN_LIST_ITEM_WIRE_SCHEMA.array(), has_more: z.boolean(), object: z.literal("list"), }) const DOMAIN_LIST_SCHEMA = DOMAIN_LIST_WIRE_SCHEMA.transform( ({ data, has_more: hasMore, ...result }) => ({ ...result, data: data.map((domain) => DOMAIN_LIST_ITEM_SCHEMA.parse(domain)), hasMore, }), ) const DOMAIN_MUTATION_SCHEMA = z.object({ id: z.string().min(1), object: z.literal("domain"), }) const DOMAIN_DELETE_SCHEMA = DOMAIN_MUTATION_SCHEMA.extend({ deleted: z.boolean(), }) const DOMAIN_FIELDS_SCHEMA = z.object({ /** Enable click tracking after the tracking subdomain is verified. */ clickTracking: z.boolean().optional(), /** Configure sending and receiving capabilities. */ capabilities: DOMAIN_CAPABILITIES_INPUT_SCHEMA.optional(), /** Enable open tracking after the tracking subdomain is verified. */ openTracking: z.boolean().optional(), /** Tracking subdomain used for click and open tracking. */ trackingSubdomain: z.string().trim().min(1).optional(), /** Require TLS or allow opportunistic fallback. */ tls: z.enum(["opportunistic", "enforced"]).optional(), }) const CREATE_DOMAIN_SCHEMA = DOMAIN_FIELDS_SCHEMA.extend({ /** Optional custom Return-Path subdomain. */ customReturnPath: z.string().trim().min(1).optional(), /** Domain name to add to Resend. */ name: z.string().trim().min(1), /** Resend sending region. */ region: DOMAIN_REGION_SCHEMA.optional(), }) const DOMAIN_PATCH_SCHEMA = DOMAIN_FIELDS_SCHEMA.refine( (patch) => Object.keys(patch).length > 0, "Provide at least one domain field to update.", ) const PAGINATION_SCHEMA = z .object({ /** Return resources after this cursor. */ after: z.string().min(1).optional(), /** Return resources before this cursor. */ before: z.string().min(1).optional(), /** Maximum resources to return. */ limit: z.number().int().min(1).max(100).optional(), }) .refine(({ after, before }) => !(after && before), { message: "after and before cannot be used together.", }) const DOMAIN_CLAIM_RECORD_SCHEMA = z.object({ name: z.string().min(1), ttl: z.string().min(1), type: z.literal("TXT"), value: z.string().min(1), }) const DOMAIN_CLAIM_WIRE_SCHEMA = z.object({ blocked_reason: z .enum(["grace_period", "recent_owner_activity", "pending_scheduled_emails"]) .nullable(), created_at: z.coerce.date(), domain_id: z.string().nullable(), expires_at: z.coerce.date(), failure_reason: z.string().nullable(), id: z.string().min(1), name: z.string().min(1), object: z.literal("domain_claim"), record: DOMAIN_CLAIM_RECORD_SCHEMA, region: DOMAIN_REGION_SCHEMA.nullable(), status: z.enum([ "pending", "verified", "completed", "blocked", "expired", "superseded", "canceled", "failed", ]), }) const DOMAIN_CLAIM_SCHEMA = DOMAIN_CLAIM_WIRE_SCHEMA.transform( ({ blocked_reason: blockedReason, created_at: createdAt, domain_id: domainId, expires_at: expiresAt, failure_reason: failureReason, ...claim }) => ({ ...claim, blockedReason, createdAt, domainId, expiresAt, failureReason, }), ) const CREATE_DOMAIN_CLAIM_SCHEMA = CREATE_DOMAIN_SCHEMA.omit({ capabilities: true, tls: true, }) /** Creates a Resend sending or receiving domain. */ export const createResendDomain = defineAction("Create Resend domain") .describe("Creates a Resend domain and returns its DNS records.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(CREATE_DOMAIN_SCHEMA) .output(CREATE_DOMAIN_RESPONSE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call("domains", { body: toDomainWire(input), httpMethod: "POST", responseSchema: CREATE_DOMAIN_RESPONSE_WIRE_SCHEMA, }), ) /** Lists Resend domains with cursor pagination. */ export const listResendDomains = defineAction("List Resend domains") .describe("Lists Resend sending and receiving domains.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(PAGINATION_SCHEMA) .output(DOMAIN_LIST_SCHEMA) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getResendApi(account).call("domains", { query: input, responseSchema: DOMAIN_LIST_WIRE_SCHEMA, }), ) /** Retrieves one Resend domain by ID. */ export const getResendDomain = defineAction("Get Resend domain") .describe("Retrieves a Resend domain and its current DNS records.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(z.object({ domainId: z.string().min(1) })) .output(DOMAIN_SCHEMA) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getResendApi(account).call( `domains/${encodeURIComponent(input.domainId)}`, { responseSchema: DOMAIN_WIRE_SCHEMA }, ), ) /** Updates tracking, TLS, or capabilities for one Resend domain. */ export const updateResendDomain = defineAction("Update Resend domain") .describe("Updates tracking, TLS, or capabilities for a Resend domain.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input( z.object({ domainId: z.string().min(1), patch: DOMAIN_PATCH_SCHEMA, }), ) .output(DOMAIN_MUTATION_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call( `domains/${encodeURIComponent(input.domainId)}`, { body: toDomainWire(input.patch), httpMethod: "PATCH", responseSchema: DOMAIN_MUTATION_SCHEMA, }, ), ) /** Deletes one Resend domain. */ export const deleteResendDomain = defineAction("Delete Resend domain") .describe("Permanently removes a domain from Resend.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(z.object({ domainId: z.string().min(1) })) .output(DOMAIN_DELETE_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call( `domains/${encodeURIComponent(input.domainId)}`, { httpMethod: "DELETE", responseSchema: DOMAIN_DELETE_SCHEMA }, ), ) /** Starts DNS verification for one Resend domain. */ export const verifyResendDomain = defineAction("Verify Resend domain") .describe("Asks Resend to verify a domain's DNS records.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(z.object({ domainId: z.string().min(1) })) .output(DOMAIN_MUTATION_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call( `domains/${encodeURIComponent(input.domainId)}/verify`, { httpMethod: "POST", responseSchema: DOMAIN_MUTATION_SCHEMA }, ), ) /** Starts a claim for a domain verified by another Resend account. */ export const createResendDomainClaim = defineAction( "Create Resend domain claim", ) .describe("Starts a Resend ownership claim and returns the proof record.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(CREATE_DOMAIN_CLAIM_SCHEMA) .output(DOMAIN_CLAIM_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call("domains/claim", { body: toDomainWire(input), httpMethod: "POST", responseSchema: DOMAIN_CLAIM_WIRE_SCHEMA, }), ) /** Retrieves the current ownership claim for a Resend domain. */ export const getResendDomainClaim = defineAction("Get Resend domain claim") .describe("Retrieves a Resend domain claim and its proof status.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(z.object({ domainId: z.string().min(1) })) .output(DOMAIN_CLAIM_SCHEMA) .retry({ replaySafety: "safe" }) .handler( async ({ account, input }) => await getResendApi(account).call( `domains/${encodeURIComponent(input.domainId)}/claim`, { responseSchema: DOMAIN_CLAIM_WIRE_SCHEMA }, ), ) /** Checks the TXT proof record for a Resend domain claim. */ export const verifyResendDomainClaim = defineAction( "Verify Resend domain claim", ) .describe("Asks Resend to verify a domain ownership claim.") .account(RESEND_ACCOUNT, RESEND_FULL_ACCESS_ACCOUNT_OPTIONS) .input(z.object({ domainId: z.string().min(1) })) .output(DOMAIN_CLAIM_SCHEMA) .retry({ replaySafety: "unsafe" }) .handler( async ({ account, input }) => await getResendApi(account).call( `domains/${encodeURIComponent(input.domainId)}/claim/verify`, { httpMethod: "POST", responseSchema: DOMAIN_CLAIM_WIRE_SCHEMA }, ), ) /** * Converts code-first domain fields to Resend's wire names. * * @param input - Validated domain fields. */ function toDomainWire(input: Partial>) { return { ...(input.capabilities === undefined ? {} : { capabilities: input.capabilities }), ...(input.clickTracking === undefined ? {} : { click_tracking: input.clickTracking }), ...(input.customReturnPath === undefined ? {} : { custom_return_path: input.customReturnPath }), ...(input.name === undefined ? {} : { name: input.name }), ...(input.openTracking === undefined ? {} : { open_tracking: input.openTracking }), ...(input.region === undefined ? {} : { region: input.region }), ...(input.tls === undefined ? {} : { tls: input.tls }), ...(input.trackingSubdomain === undefined ? {} : { tracking_subdomain: input.trackingSubdomain }), } }