import { IDefaultTemplate, ICourierMessage } from '@fonderie/core/types'; import { ILocaleSettings } from '@fonderie/core'; import { IStoreAdapter } from '@fonderie/store'; type MessageLogStatus = 'pending' | 'sent' | 'failed' | 'delivered' | 'opened' | 'clicked' | 'bounced' | 'spam'; interface IMessageLog { id: string; messageType: string; channel: string; recipient: string; locale: string | null; /** The version sent: a saved row's tag, a built-in's language, or the system locale. */ resolvedLocale: string | null; status: MessageLogStatus; error: string | null; attempts: number; provider: string | null; providerMessageId: string | null; openedAt: string | null; clickedAt: string | null; bouncedAt: string | null; bounceReason: string | null; createdAt: string; sentAt: string | null; } interface IMessageStats { /** Successfully handed to the provider in the window. */ sent: number; /** Rejected by the provider in the window. */ failed: number; /** * Logged but never resolved either way. Should be ~0; a growing number * means sends are being started and abandoned. */ pending: number; sentAt: Date | null; failedAt: Date | null; /** The most recent provider error, which is usually the whole diagnosis. */ lastError: string | null; } /** * What actually happened to outbound messages, over the last `hours`. * * This is the ONLY record of whether a message was sent, and reading it is not * optional for anyone who wants to know. A send failure is caught by the * dispatcher and deliberately not rethrown — a bad address must not poison the * event — so the event bus marks its row `processed` whether the message left * or not. An SMTP rejection and a clean send are indistinguishable in the * queue, and `deadLetters()` stays empty no matter how badly email is failing. * * Note `sent` means the provider ACCEPTED it. A provider that accepts and then * bounces asynchronously (an unverified sending domain, typically) looks like * success here; its dashboard is the source of truth for that. */ declare function messageStats(store: IStoreAdapter, options?: { hours?: number; }): Promise; type DefaultTemplateMap = Record; interface ISendResult { providerMessageId?: string; } interface ICourierChannel { name: string; send(message: ICourierMessage, template: IRenderedTemplate): Promise; } interface IRenderedTemplate { subject?: string; html?: string; text: string; locale?: string; } interface ITemplateResolver { resolve(type: string, data: Record, locale?: string): Promise; /** Receives the app's locales at install (core owns them). Optional for custom resolvers. */ setLocales?(settings: ILocaleSettings): void; } export { messageStats as m }; export type { DefaultTemplateMap as D, ITemplateResolver as I, MessageLogStatus as M, ICourierChannel as a, IRenderedTemplate as b, ISendResult as c, IMessageLog as d, IMessageStats as e };