/** * source-selection.ts, which source reads the mailbox * (docs/inbound-email.md §3.4d, "Selection is automatic"). * * The whole point of this file is that adopting Google is the ENTIRE setup for * the common case. Google adopted and the configured mail account is a Gmail * account means the Gmail source; anything else means IMAP; and * `surfaces.email.inbound.source` can force either, because every flag in this * design ships as a real configurable feature rather than a switch. * * Two properties, both deliberate. * * **It reads nothing.** Every input arrives as a value. There is no config * read, no credential lookup and no network call in this module, so the * decision is a pure function of three facts and a test can state each of them * directly. The alternative, a selector that goes and finds out for itself, * is a selector that cannot be exercised without a machine that has Google * adopted on it. * * **Forcing `gmail` where Gmail cannot work is REFUSED, not quietly served * over IMAP.** That is already the shipped contract: the description on * `surfaces.email.inbound.source` says so in the words the owner reads in * their settings, "Forcing 'gmail' without adopted Google credentials, or on * an account that is not a Gmail account, is refused rather than quietly * served over IMAP." Silently substituting the other source would leave them * with a setting that says one thing while the daemon does another, which is the same * silent-degradation failure §3.4b refuses everywhere else. So the refusal is * a result shape the caller must handle, carrying the step that fixes it. */ import type { InboundEmailSource } from '../../config/schema-types-surfaces.js'; /** The two sources, as a running watcher is built from one. */ export type InboundMailSourceKind = 'imap' | 'gmail'; /** * The facts the decision is made from. * * `configured` is the declared config type rather than a restated * `'auto' | 'gmail' | 'imap'`: a hand-copied union is a second declaration * that can drift from the schema, and a fourth value added there would then * silently fall through a `switch` here instead of failing to compile. */ export interface InboundSourceSelectionInput { /** `surfaces.email.inbound.source`, already read by the caller. */ readonly configured: InboundEmailSource; /** * Whether a Gmail source is available to read with. * * NOT "whether Google credentials are adopted", which is what this was * called and what its messages claimed. It used to be set from * `options.gmail !== undefined`, the presence of an injected Gmail source * BUILDER, and no composition passed one, so it was permanently false and * an owner who had connected Google was told "no Google credentials have * been adopted", which sent them to look for a credential that was already * there. * * It is now the answer from a composition that actually opened the * credential and asked Google for the mailbox * (`resolveGmailInboundReader`), and when it is false the reason arrives * alongside it in `gmailUnavailable` rather than being guessed at here. */ readonly googleAdopted: boolean; /** * Why no Gmail source is available, in the composition's own words. * * A VALUE, so this module still reads nothing and decides nothing it was not * told, the property the header states. Present only when `googleAdopted` * is false, and carried into the message the owner reads, because the three * conditions behind that boolean need three different actions: no account * connected, a grant Google refused, or a network that could not be reached. * Absent, the messages fall back to naming every possibility, which is what * a selector handed one bare boolean can honestly say. */ readonly gmailUnavailable?: string | undefined; /** * Whether the configured mail account is a Gmail account. * * Separate from `googleAdopted` because the two genuinely come apart: an * owner can have adopted Google for calendar and drive while the mailbox the * inbound watcher is pointed at lives on their own domain, and Gmail's history * API cannot read that mailbox. */ readonly mailAccountIsGmail: boolean; } /** Why the selection came out the way it did. Named, so nothing parses prose. */ export type InboundSourceBasis = /** The owner named this source explicitly. */ 'forced' /** `auto`: Google is adopted and the mail account is a Gmail account. */ | 'google-adopted' /** `auto`: no Google credentials, so there is no Gmail API to use. */ | 'google-not-adopted' /** `auto`: Google is adopted, but this mailbox is not a Gmail one. */ | 'not-a-gmail-account'; /** Why a forced selection could not be honoured. */ export type InboundSourceRefusal = 'gmail-forced-without-google' | 'gmail-forced-on-non-gmail-account'; export interface InboundSourceSelected { readonly kind: 'selected'; readonly source: InboundMailSourceKind; readonly basis: InboundSourceBasis; /** One sentence for the owner, naming what was chosen and why. */ readonly detail: string; } export interface InboundSourceRefused { readonly kind: 'refused'; readonly reason: InboundSourceRefusal; readonly detail: string; /** The one remedial step. */ readonly fix: string; } /** * A selection, or a refusal to make one. * * A union rather than a source plus an `ok` flag: a caller that forgot to * check the flag would start a Gmail source against a mailbox Gmail cannot * read, and there is no source on the refused arm to start. */ export type InboundSourceSelection = InboundSourceSelected | InboundSourceRefused; /** * Pick the source for one mailbox. * * Pure. Every input is supplied; nothing here reads configuration, opens a * credential store or asks a network. */ export declare function selectInboundMailSource(input: InboundSourceSelectionInput): InboundSourceSelection; //# sourceMappingURL=source-selection.d.ts.map