import type { PurchaseRecord } from './checkout-flow.js'; /** * How long after a purchase a confirmation is still recognisably ITS * confirmation. * * Generous on purpose. Stores are not instant: some send within seconds, some * batch overnight, and a warehouse cut-off can put hours between the charge and * the mail. Six hours covers the realistic span without being so wide that two * separate purchases at the same merchant on the same day become ambiguous, * and when they ARE ambiguous this refuses to guess rather than picking one. */ export declare const CONFIRMATION_WINDOW_MS: number; export interface InboundMailFacts { /** The envelope sender, as the mail surface parsed it. */ readonly senderAddress: string; readonly receivedAtMs: number; } export type CorrelationResult = { readonly kind: 'matched'; readonly record: PurchaseRecord; readonly senderDomain: string; } | { readonly kind: 'ambiguous'; readonly candidates: readonly PurchaseRecord[]; readonly senderDomain: string; } | { readonly kind: 'unrelated'; readonly reason: string; }; /** The registrable domain of an email address, or null when it has none. */ export declare function senderRegistrableDomain(address: string): string | null; /** * Find the purchase this mail is about, or say it is about none of them. * * Returns `ambiguous` rather than choosing when two purchases at the same * merchant both fit the window. Naming the wrong order in a message about money * is worse than not naming one, and the general inbound path still reports the * mail either way. */ export declare function correlatePurchaseMail(mail: InboundMailFacts, records: readonly PurchaseRecord[], windowMs?: number): CorrelationResult; /** * The three facts worth lifting out of a confirmation, and nothing else. * * ── Why extraction rather than quoting ──────────────────────────────────── * * The body arrives from outside at the exact moment the owner is expecting it, * which makes it the most attractive thing on this whole path for an attacker * to forge. Rendering any span of it into a message the owner reads on their * phone hands whoever wrote it a channel to them. * * So three narrow patterns run over the text, each yielding a short token, and * each token is neutralised before it can be rendered. Nothing else survives. * A body this cannot find a pattern in produces nulls, and the report simply * carries no order number, which is a strictly better outcome than quoting a * line to be helpful. * * Note what is deliberately NOT extracted: the total. We have our own, computed * from integers we parsed, and a total taken from the email would be a number * an attacker chose sitting next to the words "charged to your card". */ export interface ConfirmationFacts { readonly orderNumber: string | null; readonly shipDate: string | null; readonly trackingReference: string | null; } /** * Pull the structured facts out of a confirmation body. * * Takes the raw text and returns only neutralised short tokens. The caller must * never pass the body itself onward; there is deliberately no field on the * result that could carry it. */ export declare function extractConfirmationFacts(body: string): ConfirmationFacts; //# sourceMappingURL=order-correlation.d.ts.map