/**
 * @bobfrankston/mailx-types
 * Shared type definitions for the mailx email client.
 * This is the contract between client and server.
 */
export { CONTACT_RULES } from "./contact-rules.js";
export type { MailxApi } from "./mailx-api.js";
export { addContactsDenylistEntry, addContactsPreferredEntry, type PreferredContactEntry, type CloudReadFn, type CloudWriteFn, } from "./contacts-config.js";
export { expandRecipients, splitRecipients, isAddressToken, extractAddress, } from "./groups.js";
export type { GroupMap, RecipientToken, ExpansionResult } from "./groups.js";
export { REMINDER_STATE_FILE, normalizeReminderState, mergeReminderStates, reminderStatesEqual, } from "./reminder-state.js";
export type { ReminderState } from "./reminder-state.js";
export { extractInlineImages } from "./mime-inline-images.js";
export { buildMimeMessage } from "./mime-build.js";
export type { BuildMimeOptions, BuiltMime, MimeAttachment } from "./mime-build.js";
export type { InlineImagePart } from "./mime-inline-images.js";
/** Supported authentication methods */
export type AuthMethod = "password" | "oauth2";
/** Mail account configuration */
export interface AccountConfig {
    id: string; /** Unique account identifier (e.g., "iecc", "gmail-bob") */
    name: string; /** Sender name for From header (e.g., "Bob Frankston") */
    label?: string; /** UI label for account list (e.g., "Gmail"). Falls back to name if not set */
    email: string; /** Email address */
    imap: {
        host: string;
        port: number;
        tls: boolean;
        auth: AuthMethod;
        user: string;
        password?: string; /** For password auth */
        oauthClientId?: string; /** For OAuth2 */
    };
    smtp: {
        host: string;
        port: number;
        tls: boolean;
        auth: AuthMethod;
        user: string;
        password?: string;
    };
    enabled: boolean;
    primary?: boolean; /** Catch-all "this is my main account" — default source for Calendar / Tasks / Contacts when no per-feature override set. */
    primaryCalendar?: boolean; /** Per-feature override: use this account's Google Calendar. Falls back to `primary` if unset. */
    primaryTasks?: boolean; /** Per-feature override: use this account's Google Tasks. Falls back to `primary` if unset. */
    primaryContacts?: boolean; /** Per-feature override: use this account's Google Contacts. Falls back to `primary` if unset. */
    defaultSend?: boolean; /** Use this account's SMTP when From doesn't match any account */
    syncContacts?: boolean; /** Sync contacts even when account is disabled (contacts-only Gmail) */
    relayDomains?: string[]; /** Domains to skip in Delivered-To chain (e.g., ["relay.aaz.lt"]) */
    identityDomains?: string[]; /** Domains where Delivered-To address should become the reply From (e.g., ["bob.ma", "bobf.frankston.com"]) */
    spam?: string; /** IMAP folder path for "Mark as spam" button (e.g., "_spam"). Button hidden when not set. */
    signature?: string; /** Legacy: HTML signature appended to all outgoing messages (new + reply + forward). Plain text or HTML allowed. Superseded by `sig`. */
    sig?: AccountSignature; /** Per-account signature object. Initially appended only to NEW messages; later options will cover replies/forwards. */
}
/** Signature configuration in accounts.jsonc. Initial shape carries `text`
 *  only; `html: true` reserved for future support of raw HTML signatures. */
export interface AccountSignature {
    text: string; /** Plain-text signature body. Newlines preserved. Appended to NEW messages with the standard "-- " RFC 3676 separator. */
    html?: boolean; /** Future flag: when true, `text` is treated as raw HTML rather than escaped plain text. Currently ignored. */
}
/** Standard IMAP special-use folder types */
export type SpecialUse = "inbox" | "sent" | "drafts" | "trash" | "junk" | "archive" | "all";
/** A mailbox folder */
export interface Folder {
    id: number;
    accountId: string;
    path: string; /** IMAP path e.g., "INBOX", "INBOX/Projects" */
    name: string; /** Display name */
    specialUse: SpecialUse; /** Standard folder type, or null for custom */
    delimiter: string; /** IMAP hierarchy delimiter */
    totalCount: number;
    unreadCount: number;
    children: Folder[]; /** Nested subfolders */
}
interface FlagBearing {
    flags: string[];
}
export declare const seenOf: (m: {
    flags?: readonly string[];
}) => boolean;
export declare const flaggedOf: (m: {
    flags?: readonly string[];
}) => boolean;
export declare const answeredOf: (m: {
    flags?: readonly string[];
}) => boolean;
export declare const draftOf: (m: {
    flags?: readonly string[];
}) => boolean;
export declare const deletedOf: (m: {
    flags?: readonly string[];
}) => boolean;
export declare const setSeen: (m: FlagBearing, state: boolean) => void;
export declare const setFlagged: (m: FlagBearing, state: boolean) => void;
export declare const setAnswered: (m: FlagBearing, state: boolean) => void;
export declare const setDraft: (m: FlagBearing, state: boolean) => void;
export declare const toggleSeen: (m: FlagBearing) => void;
export declare const toggleFlagged: (m: FlagBearing) => void;
/** Email address with optional display name */
export interface EmailAddress {
    name: string; /** Display name, may be empty */
    address: string; /** Email address */
}
/** Message envelope (headers only, for list display) */
export interface MessageEnvelope {
    id: number; /** Local store ID */
    accountId: string;
    folderId: number;
    folderName?: string; /** Leaf folder name; populated by cross-folder search so the UI can tag each hit */
    uid: number; /** IMAP UID (server-side identity; changes on move, UIDVALIDITY bump) */
    uuid?: string; /** Stable local identity, minted once at first-sight; never changes */
    messageId: string; /** RFC Message-ID header */
    inReplyTo: string; /** For threading */
    references: string[]; /** For threading */
    threadId?: string; /** Computed thread id (root Message-ID of the conversation) */
    date: number; /** Epoch ms — per-account arrival time (IMAP INTERNALDATE / Gmail internalDate) */
    sentDate?: number; /** Epoch ms — the message's own Date: header (send time); falls back to date */
    subject: string;
    from: EmailAddress;
    to: EmailAddress[];
    cc: EmailAddress[];
    flags: string[]; /** IMAP flags: ["\\Seen", "\\Flagged"] */
    size: number;
    hasAttachments: boolean;
    isReplied?: boolean; /** Local DB knows the user (or anyone in this account) replied — derived from In-Reply-To linkage, independent of \Answered */
    preview: string; /** First ~200 chars of body text */
    bodyPath?: string; /** Local body location: "idb:..." or "gmail:<id>" */
    providerId?: string; /** Native server id (Gmail hex id, Outlook Graph id) — bypasses UID→id pagination on body fetch */
    pending?: boolean; /** True when a queued local action (move/flag/delete) hasn't been ACK'd by the server yet — UI renders pink */
}
/** Full message with body content */
export interface Message extends MessageEnvelope {
    bodyHtml: string; /** HTML body */
    bodyText: string; /** Plain text body */
    attachments: Attachment[];
}
/** File attachment metadata */
export interface Attachment {
    id: number;
    filename: string;
    mimeType: string;
    size: number;
    contentId: string; /** For inline attachments (cid:) */
}
/** Paginated list response */
export interface PagedResult<T> {
    items: T[];
    total: number;
    page: number;
    pageSize: number;
}
/** Message list query parameters */
export interface MessageQuery {
    accountId: string;
    folderId: number;
    page?: number;
    pageSize?: number;
    sort?: "date" | "from" | "subject";
    sortDir?: "asc" | "desc";
    /** Which date drives the "date" sort + the date column: the Date: header
     *  (sent, default) or the per-account arrival time (received). */
    dateBasis?: "sent" | "received";
    search?: string;
    /** Restrict to messages with the \Flagged flag set (whole-folder, not
     *  just the currently-rendered page — lets the "show flagged" filter
     *  find stars on messages that haven't been paged in yet). */
    flaggedOnly?: boolean;
}
/** Compose/send a message */
export interface ComposeMessage {
    from: string; /** Account ID to send from */
    to: EmailAddress[];
    cc?: EmailAddress[];
    bcc?: EmailAddress[];
    subject: string;
    bodyHtml: string;
    bodyText?: string;
    inReplyTo?: string; /** Message-ID of message being replied to */
    references?: string[];
    attachments?: {
        filename: string;
        data: string;
        mimeType: string;
    }[];
}
export type QueueStatus = "pending" | "sending" | "sent" | "failed";
export interface QueueItem {
    id: number;
    status: QueueStatus;
    createdAt: number;
    sendAfter: number; /** Epoch ms, for deferred send */
    attempts: number;
    lastAttempt: number;
    error: string;
    subject: string;
    to: EmailAddress[];
}
export type WsEvent = {
    type: "newMessage";
    accountId: string;
    folderId: number;
    message: MessageEnvelope;
} | {
    type: "messageDeleted";
    accountId: string;
    folderId: number;
    uid: number;
} | {
    type: "messageMoved";
    accountId: string;
    fromFolderId: number;
    toFolderId: number;
    uid: number;
} | {
    type: "folderCountsChanged";
    accountId: string;
    counts: Record<number, {
        total: number;
        unread: number;
    }>;
} | {
    type: "folderSynced";
    accountId: string;
    entries: {
        folderId: number;
        syncedAt: number;
    }[];
} | {
    type: "syncProgress";
    accountId: string;
    phase: string;
    progress: number;
} | {
    type: "queueUpdate";
    pending: number;
    sending: number;
    failed: number;
} | {
    type: "connected";
} | {
    type: "reload";
} | {
    type: "error";
    message: string;
} | {
    type: "accountError";
    accountId: string;
    error: string;
    hint: string;
    isOAuth: boolean;
};
export interface MailxSettings {
    accounts: AccountConfig[];
    ui: {
        theme: "system" | "dark" | "light";
        editor: "quill" | "tiptap";
        folderWidth: number;
        listViewerSplit: number; /** Percentage for message list height */
        fontSize: number;
    };
    sync: {
        intervalMinutes: number;
        historyDays: number; /** 0 = all history */
        prefetch: boolean; /** Download message bodies during sync (default true) */
    };
    store: {
        basePath: string; /** Where message bodies are stored */
        compressionBoundaryDays: number; /** Messages older than this get compressed */
    };
    autocomplete?: AutocompleteSettings;
}
export interface AutocompleteSettings {
    enabled: boolean;
    provider: "ollama" | "claude" | "openai" | "off";
    ollamaUrl: string;
    ollamaModel: string;
    cloudApiKey: string;
    cloudModel: string;
    debounceMs: number;
    maxTokens: number;
    /** Per-feature opt-in for non-autocomplete AI helpers. All default false
     *  per user preference (2026-04-21): AI features should be controlled by
     *  a flag, initially OFF in settings. Provider config is shared with
     *  autocomplete (provider, cloudApiKey, cloudModel, etc.). */
    translateEnabled?: boolean;
    proofreadEnabled?: boolean;
}
/** AI provider API keys. Lives at the top of `accounts.jsonc` alongside
 *  `accounts:` so all secrets are co-located in one cloud-synced file. The
 *  keys field replaces `preferences.jsonc.autocomplete.cloudApiKey` (which
 *  was tied to the *active* provider) so a user can set both Anthropic and
 *  OpenAI keys once and switch providers without re-entering anything. */
export interface AiKeys {
    anthropic?: string;
    openai?: string;
}
export interface AutocompleteRequest {
    subject: string;
    to: string;
    bodyText: string;
    cursorOffset: number;
}
export interface AutocompleteResponse {
    suggestion: string;
}
export interface AiTransformRequest {
    /** translate = render in `targetLang`; proofread = corrected version
     *  with grammar/spelling fixes; summarize = short paragraph summary;
     *  extractEvent = parse natural-language event description into a
     *  structured calendar event (title/start/end/location/notes). */
    action: "translate" | "proofread" | "summarize" | "extractEvent";
    text: string;
    /** ISO-639-1 (or BCP-47) language code for translate. Defaults to "en". */
    targetLang?: string;
    /** Caller's "now" hint, ISO 8601 (e.g. 2026-05-05T18:30:00). Used by
     *  extractEvent to resolve relative dates ("tomorrow at 3pm"). The
     *  service can't trust its own clock — the user might be on a phone
     *  in a different timezone — so the caller supplies it. */
    nowISO?: string;
}
/** Structured calendar event fields parsed from a natural-language
 *  description by aiTransform({ action: "extractEvent" }). All times are
 *  ISO 8601 in the caller's local timezone (no `Z` suffix); the client
 *  shapes them into Google Calendar's `dates=` URL parameter. */
export interface ExtractedEvent {
    title: string;
    startISO?: string;
    endISO?: string;
    allDay?: boolean;
    /** IANA zone the times are wall-clock in, when the source text stated one
     *  (e.g. an invitation with "(Malaysia Time - Kuala Lumpur)"). Client
     *  passes it to Google Calendar as `ctz=`. Absent = user's local zone. */
    timeZone?: string;
    location?: string;
    notes?: string;
}
export interface AiTransformResponse {
    /** Transformed text. Empty when AI is disabled / provider error / feature
     *  not enabled — caller should treat empty as "no result". For
     *  extractEvent this holds the JSON-stringified ExtractedEvent so the
     *  response shape stays single-field across all actions. */
    text: string;
    /** Parsed ExtractedEvent, populated only for extractEvent. Lets the
     *  client skip the JSON.parse step (the service already validated).
     *  First event when the text described several. */
    event?: ExtractedEvent;
    /** All extracted events — the text may describe more than one. */
    events?: ExtractedEvent[];
    /** Optional reason for empty result, surfaced to UI status bar. */
    reason?: string;
}
/** Body storage backend interface -- implementations are swappable.
 *  Uses Uint8Array, not Node's Buffer: mailx-types is a zero-dep, browser-
 *  capable package, and the browser client (DOM lib, no @types/node) imports
 *  it. Buffer extends Uint8Array, so Node implementations (FileMessageStore)
 *  still satisfy this; the browser side gets a real, node-free type. */
export interface MessageStore {
    putMessage(accountId: string, folderId: number, uid: number, raw: Uint8Array): Promise<string>;
    getMessage(accountId: string, folderId: number, uid: number): Promise<Uint8Array>;
    deleteMessage(accountId: string, folderId: number, uid: number): Promise<void>;
    hasMessage(accountId: string, folderId: number, uid: number): Promise<boolean>;
}
/** Sanitize HTML for safe display — strips scripts, inline handlers, remote images, forms, iframes. */
export declare function sanitizeHtml(html: string): {
    html: string;
    hasRemoteContent: boolean;
};
/**
 * Encode a header VALUE as RFC 2047 encoded-words when it contains non-ASCII.
 *
 * Why this has to exist: a header carrying raw 8-bit UTF-8 makes the whole
 * message an EAI message (RFC 6530), which can only be relayed over SMTPUTF8
 * (RFC 6531). Plenty of receivers still don't advertise that extension, and a
 * sending MTA that can't downgrade has no choice but to bounce. mailx built
 * every outgoing header with bare interpolation — `Subject: ${msg.subject}` —
 * so ONE curly apostrophe was enough to make a message undeliverable:
 *
 *   Subject: Re: … One of Canada’s Fastest Fiber Networks     (U+2019, raw UTF-8)
 *   → <alland@soundbytesradio.com>: EAI message but server 17.57.152.5
 *     does not support SMTPUTF8 … This is a permanent error
 *
 * (Bob 2026-08-08, qmail bounce from gal.iecc.com — iCloud, Zoho and
 * land-com all refused it; herot.com, gmail and bob.ma took it, so it looked
 * like a partial failure.) Reply subjects inherit whatever punctuation the
 * original used, and Word/paste smart quotes do the rest, so this fires
 * constantly and silently.
 *
 * Pure-ASCII values are returned untouched — encoding them would be legal but
 * needlessly unreadable in every mail client's raw-source view.
 *
 * Base64 rather than Q-encoding: it is uniform (no per-character escaping
 * rules that differ between phrase and unstructured context) and never
 * produces a token needing further escaping. Chunks are split so each
 * encoded-word stays inside RFC 2047's 75-character limit, split on CHARACTER
 * boundaries so a multi-byte sequence is never cut in half.
 */
export declare function encodeHeaderWord(text: string): string;
/**
 * Encode an address header value (From/To/Cc/Bcc/Reply-To), encoding only the
 * DISPLAY NAME of each address. The addr-spec must stay literal: encoding it
 * would corrupt the address, and a non-ASCII addr-spec is genuinely EAI and
 * cannot be represented any other way.
 *
 * Splits on commas outside angle brackets and quotes so a display name
 * containing a comma ("Frankston, Bob" <x@y>) survives.
 */
export declare function encodeAddressHeader(value: string): string;
/** Encode text as RFC 2045 quoted-printable. */
export declare function encodeQuotedPrintable(text: string): string;
/** Render an HTML document as a plain-text approximation suitable for the
 *  text/plain alternative part of a multipart/alternative outgoing MIME
 *  message. Not a full HTML-to-text engine — just enough to give non-HTML
 *  clients (plain-text readers, spam filters scoring on text/plain, people
 *  who turned HTML off) a readable fallback. Preserves line breaks for
 *  `<br>` / `</p>` / `</div>` / `<li>`, strips all other tags, decodes the
 *  common HTML entities, and collapses runs of whitespace.
 *
 *  Spam filters (SpamAssassin, Rspamd) penalise HTML-only mail aggressively;
 *  shipping a real text part typically drops the score by 1–2 points. Also
 *  matches the behaviour of every other mainstream mail client — sending a
 *  text/html part alone marks mailx as an outlier in mail logs. */
export declare function htmlToPlainText(html: string): string;
/** Parse search query into structured conditions.
 *  Supports qualifiers: from:, to:, subject:, date:, has:attachment,
 *  is:flagged, is:unread, is:read. Unqualified terms search across subject /
 *  from / preview. Returns { conditions, params } for SQL WHERE clause with
 *  LIKE plus structured predicates (flags_json LIKE, has_attachments=1, date
 *  range comparisons).
 *
 *  Date syntax (matches Gmail-ish conventions):
 *    - date:2026-04-22         exact day
 *    - date:2026-04            month
 *    - date:>2026-04-01        after
 *    - date:<2026-04-01        before
 *    - date:2026-04-01..2026-04-30   range
 *    - date:today / yesterday / last7 / last30
 */
export declare function parseSearchQuery(query: string): {
    conditions: string[];
    params: (string | number)[];
};
//# sourceMappingURL=index.d.ts.map