import { DEFAULT_ACCOUNT_BINDING, isIntegrationAccountReference, serializedIntegrationAccountDefinition, } from "./integrations" import type { IntegrationAccountRequirement } from "@automate.ax/catalog/authoring" import { getAutomationPlanningContext } from "./runtime" import { isSignal } from "./signal-protocol" /** * Records one integration account use and returns its planned selection. * * @param value - Static or signal-backed account selection. * @param requirements - Alternative service requirements accepted by the use. * @throws When the account selection does not match an allowed service. */ export function planIntegrationAccountUse( value: unknown, requirements: readonly IntegrationAccountRequirement[], ): { binding: string; dynamic: false } | { dynamic: true } { if (requirements.length > 1 && !isIntegrationAccountReference(value)) { throw new Error( "Actions with alternative account services require a typed integration account reference.", ) } const { serviceId, ...selection } = getPlannedAccountSelection( value, requirements, ) const requirement = requirements.find( (candidate) => candidate.serviceId === serviceId, )! getAutomationPlanningContext()?.accountUses.push({ ...selection, connectionOptions: requirement.connectionOptions, serviceId, }) return selection } /** * Classifies an account input without evaluating signal transforms. * * @param value - Account selection supplied by an action or event function. * @param requirements - Alternative services accepted by the caller. * @throws When the account selection does not match an allowed service. */ function getPlannedAccountSelection( value: unknown, requirements: readonly IntegrationAccountRequirement[], ) { if (isSignal(value)) { return { dynamic: true, serviceId: requirements[0]!.serviceId } as const } if (value === undefined || typeof value === "string") { return { binding: value ?? DEFAULT_ACCOUNT_BINDING, dynamic: false, serviceId: requirements[0]!.serviceId, } as const } if (!isIntegrationAccountReference(value)) { throw new Error("Integration account input is not a named account binding.") } const definition = value[serializedIntegrationAccountDefinition] if ( !requirements.some( (requirement) => requirement.serviceId === definition.serviceId, ) ) { throw new Error( `Expected an account for one of ${requirements.map(({ serviceId }) => serviceId).join(", ")}, but received a ${definition.serviceId} account.`, ) } return { binding: definition.binding, dynamic: false, serviceId: definition.serviceId, } as const }