import type { TriggerDefinition } from "../types" import type { HttpTriggerScope } from "./core-http" import * as z from "zod/mini" const MAX_EMAIL_LOCAL_PART_BYTES = 64 const EMAIL_SCHEMA = z.email() const MAILHOOK_PRESENTATION_CONFIG_SCHEMA = z.object({ scope: z.prefault(z.enum(["trigger", "automation", "project"]), "trigger"), }) export const PLATFORM_EMAIL_DOMAIN = "automations.automate.ax" export const mailhookEmailReceivedTriggerDefinition = { getPrimary: ({ automationId, config, hookSlot, projectId, scopePath }) => ({ copyable: true, value: getMailhookAddress({ automationId, hookSlot, projectId, scope: MAILHOOK_PRESENTATION_CONFIG_SCHEMA.parse(config).scope, scopePath, }), }), icon: "webhook", name: "Mailhook", type: "mailhook.email.received", } as const satisfies TriggerDefinition export type MailhookScope = HttpTriggerScope export interface MailhookAddressOptions { automationId: string hookSlot: number plusPath?: string projectId: string scope: MailhookScope scopePath?: readonly number[] } /** * Returns the unique inbound address for one deployed mailhook. * * Add `+suffix` before the `@` to carry a routing value into the event. * * @param options - Mailhook identity and sharing scope. * @throws When the generated local part exceeds the email size limit. */ export function getMailhookAddress(options: MailhookAddressOptions) { const localPart = `${getMailhookAddressKey(options)}${options.plusPath ? `+${options.plusPath}` : ""}` if (new TextEncoder().encode(localPart).length > MAX_EMAIL_LOCAL_PART_BYTES) { throw new RangeError("Mailhook address local part exceeds 64 bytes.") } return EMAIL_SCHEMA.parse(`${localPart}@${PLATFORM_EMAIL_DOMAIN}`) } /** * Returns the local-part router key for one configured mailhook scope. * * @param options - Mailhook identity and sharing scope. */ export function getMailhookAddressKey(options: MailhookAddressOptions) { if (options.scope === "project") return `p${typeIdSuffix(options.projectId)}` if (options.scope === "automation") { return `a${typeIdSuffix(options.automationId)}` } return `a${typeIdSuffix(options.automationId)}.${[ ...(options.scopePath ?? []), options.hookSlot, ] .map((segment) => segment.toString(36)) .join(".")}` } /** * Removes the redundant TypeID prefix from an address router. * * @param id - Project or automation TypeID. */ function typeIdSuffix(id: string) { return id.includes("_") ? id.slice(id.indexOf("_") + 1) : id }