import type { ListmonkClient } from "@listmonk-ops/openapi"; import { type PinnedHttpSender, type ResolvedWebhookAddress } from "./webhook-transport.js"; export type CheckLevel = "pass" | "warn" | "fail"; export interface CampaignPreflightCheck { id: string; level: CheckLevel; message: string; details?: Record; } export interface CampaignPreflightResult { campaignId: number; campaignName: string; /** Listmonk revision token that delivery operations must re-check. */ campaignUpdatedAt: string; status: string; audienceEstimate: number; checkedAt: string; checks: CampaignPreflightCheck[]; summary: { pass: number; warn: number; fail: number; }; } export interface CampaignPreflightOptions { maxAudience?: number; checkLinks?: boolean; linkCheckTimeoutMs?: number; /** Network seams for link checks; production uses DNS and node:http(s). */ linkCheck?: LinkCheckOptions; } /** * Returns true for loopback names and literal addresses that are not globally * routable. Despite the historical name, this deliberately blocks all * special-purpose ranges, including documentation, benchmarking, multicast, * and reserved space. Other hostnames are resolved and checked separately. */ export declare function isPrivateHost(hostname: string): boolean; /** * Validate that a URL is safe to fetch: must be http(s), not target a * private/internal host. */ export declare function isSafeFetchUrl(url: string): { safe: boolean; reason?: string; }; /** Resolves every address of a hostname; the default uses the system resolver. */ export type HostAddressLookup = (hostname: string) => Promise[]>; export type PublicHostResolution = Readonly<{ safe: true; addresses: readonly ResolvedWebhookAddress[]; }> | Readonly<{ safe: false; reason: string; }>; /** * Resolve the addresses that a pinned connection to `url` may use. The URL * must pass the static policy and every resolved address must be globally * routable. Callers connect only to the returned addresses, so a later lookup * cannot rebind the host. Rejects when DNS fails or yields no usable address, * so callers fail closed instead of treating an unverified host as safe. */ export declare function resolvePublicHostAddresses(url: string, lookupHost?: HostAddressLookup): Promise; /** * Validate a URL and every address its host currently resolves to. DNS * failures fail closed. Validation alone cannot stop DNS rebinding: the later * connection must be pinned to the validated addresses, as `checkLink` and * webhook delivery do. */ export declare function isSafeFetchUrlAsync(url: string): Promise<{ safe: boolean; reason?: string; }>; export interface LinkCheckResult { url: string; ok: boolean; status?: number; error?: string; } /** Network seams for link checks; tests inject them to avoid real traffic. */ export interface LinkCheckOptions { lookupHost?: HostAddressLookup; send?: PinnedHttpSender; } /** * Check one link with HEAD (GET after a 405/501) and at most five manually * followed redirects. Every hop is revalidated against the URL policy, * resolved once, and connected only to the validated addresses, so DNS * rebinding cannot steer a request to a private or metadata address. Hosts * that cannot be resolved are reported as unverifiable and never fetched. * Results carry policy reasons, status codes, and local error codes only. */ export declare function checkLink(url: string, timeoutMs: number, options?: LinkCheckOptions): Promise; export declare function runCampaignPreflight(client: ListmonkClient, campaignId: number, options?: CampaignPreflightOptions): Promise; export declare const DEFAULT_ENGAGEMENT_OBSERVATION_SECONDS = 3600; export interface DeliverabilityGuardOptions { bounceThreshold?: number; openRateThreshold?: number; clickRateThreshold?: number; pauseOnBreach?: boolean; /** Minimum sent count before engagement breaches are evaluated (default 100). */ minimumSent?: number; /** Minimum campaign age before evaluating engagement (default one hour). */ minimumObservationSeconds?: number; /** Engagement remains advisory unless both pause flags are explicit. */ pauseOnEngagementBreach?: boolean; now?: () => Date; } export interface DeliverabilityGuardResult { campaignId: number; campaignName: string; status: string; checkedAt: string; metrics: { sent: number; toSend: number; views: number; clicks: number; bounces: number; bounceRate: number; openRate: number; clickRate: number; }; thresholds: { bounceRate: number; openRate: number; clickRate: number; }; breaches: string[]; paused: boolean; } export declare function evaluateDeliverabilityGuard(client: ListmonkClient, campaignId: number, options?: DeliverabilityGuardOptions): Promise;