export type MailProfileProtocol = "smtp" | "imap"; export type MailProfileEditSection = MailProfileProtocol; export type MailProfilePanelMode = "all" | "server" | "credentials"; export type MailProfileEditTarget = | {kind: "create";} | {kind: "profile";} | {kind: "server";protocol: MailProfileProtocol;serverId?: string;} | {kind: "credentials";protocol: MailProfileProtocol;serverId?: string;credentialId?: string;}; export type MailProfileTransportLike = { host?: string | null; }; export type MailProfileTreeProfileLike = { id: string; imap?: MailProfileTransportLike | null; }; export type MailProfileChildDescriptor = { kind: "server" | "credential"; id: string; protocol: MailProfileProtocol; }; export type MailProfileTransportCredentialsLike = { username?: string | null; password?: string | null; }; export type MailProfileTargetedUpdateParts = { profile: Record; smtp: Record; imap: Record | null; credentials: { smtp: MailProfileTransportCredentialsLike; imap: MailProfileTransportCredentialsLike; }; clearImap: boolean; }; export function mailProfileChildDescriptors(profile: MailProfileTreeProfileLike): MailProfileChildDescriptor[] { const children: MailProfileChildDescriptor[] = [ { kind: "server", id: `server:${profile.id}:smtp`, protocol: "smtp" }, { kind: "credential", id: `credential:${profile.id}:smtp`, protocol: "smtp" }, { kind: "server", id: `server:${profile.id}:imap`, protocol: "imap" } ]; if (profile.imap) children.push({ kind: "credential", id: `credential:${profile.id}:imap`, protocol: "imap" }); return children; } export function mailProfileEditTargetInitialSection(target: MailProfileEditTarget): MailProfileEditSection { if (target.kind === "server" || target.kind === "credentials") return target.protocol; return "smtp"; } export function mailProfileEditTargetPanelMode(target: MailProfileEditTarget): MailProfilePanelMode | null { if (target.kind === "create") return "all"; if (target.kind === "server") return "server"; if (target.kind === "credentials") return "credentials"; return null; } export function mailProfileEditTargetVisibleSections(target: MailProfileEditTarget): MailProfileEditSection[] { if (target.kind === "server" || target.kind === "credentials") return [target.protocol]; if (target.kind === "create") return ["smtp", "imap"]; return []; } export function mailProfileEditTargetShowsProfileFields(target: MailProfileEditTarget): boolean { return target.kind === "create" || target.kind === "profile"; } export function mailProfileEditTargetShowsSettingsPanel(target: MailProfileEditTarget): boolean { return target.kind !== "profile"; } /** * Keep the PATCH authority surface equal to the focused editor. Profile and * server edits must not accidentally replay credential fields merely because * the form draft contains their displayed values. */ export function mailProfileTargetedUpdatePayload( target: MailProfileEditTarget, parts: MailProfileTargetedUpdateParts ): Record { if (target.kind === "profile") return parts.profile; if (target.kind === "server") { if (target.protocol === "smtp") return { smtp: parts.smtp }; return parts.imap === null ? { imap: null, clear_imap: parts.clearImap } : { imap: parts.imap }; } if (target.kind === "credentials") { return { credentials: { [target.protocol]: parts.credentials[target.protocol] } }; } throw new Error("Create is not an update target"); } /** Omit blank create credentials so profile-write remains independent. */ export function mailProfileCreateCredentialsPayload( smtp: MailProfileTransportCredentialsLike, imap: MailProfileTransportCredentialsLike ): {smtp?: MailProfileTransportCredentialsLike;imap?: MailProfileTransportCredentialsLike;} | undefined { const populated = (value: MailProfileTransportCredentialsLike): MailProfileTransportCredentialsLike => Object.fromEntries( Object.entries(value).filter(([, item]) => item !== null && item !== undefined && item !== "") ); const smtpCredentials = populated(smtp); const imapCredentials = populated(imap); const result: {smtp?: MailProfileTransportCredentialsLike;imap?: MailProfileTransportCredentialsLike;} = {}; if (Object.keys(smtpCredentials).length > 0) result.smtp = smtpCredentials; if (Object.keys(imapCredentials).length > 0) result.imap = imapCredentials; return Object.keys(result).length > 0 ? result : undefined; }