/**
 * Cloudflare DNS provider — zone and record management via Cloudflare API v4
 *
 * Env vars:
 *   CLOUDFLARE_API_TOKEN   — API token with Zone:Edit + DNS:Edit permissions
 *   CLOUDFLARE_ACCOUNT_ID  — Account ID (required for zone creation)
 */
import type { DnsProvider, DomainInventoryProvider } from "./registrar.js";
import { type CloudflareConfig } from "./cloudflare-auth.js";
export type { CloudflareConfig };
export declare class CloudflareApiError extends Error {
    statusCode: number;
    providerCode?: string | undefined;
    constructor(message: string, statusCode: number, providerCode?: string | undefined);
}
export interface CloudflareZone {
    id: string;
    name: string;
    status: string;
    nameservers: string[];
    original_nameservers?: string[];
}
export interface CloudflareRecord {
    id?: string;
    type: string;
    name: string;
    content: string;
    ttl: number;
    priority?: number;
    proxied?: boolean;
}
/** One new DNS-only CNAME. Both the zone identity and full record name are explicit. */
export interface CloudflareCreateRecordInput {
    zoneId: string;
    zoneName: string;
    type: "CNAME";
    name: string;
    content: string;
    ttl: number;
    proxied: false;
}
export interface CloudflareCreateRecordReceipt {
    created: true;
    provider: "cloudflare";
    zone: {
        id: string;
        name: string;
    };
    record: CloudflareRecord & {
        id: string;
    };
}
export type CloudflareOriginTlsMode = "strict" | "full";
export type CloudflareObservedOriginTlsMode = "off" | "flexible" | "full" | "strict" | "origin_pull";
export declare function getConfig(): CloudflareConfig;
export declare function boundedResponseText(response: Response, maxBytes: number, signal: AbortSignal): Promise<string>;
/**
 * Create exactly one absent CNAME. Never updates, deletes or retries a write.
 * Cloudflare's CNAME collision check protects against competing address/CNAME
 * creation after preflight; this is not a general record compare-and-swap API.
 * A failed or ambiguous POST must be reconciled with a provider read before a
 * caller chooses another operation. Even an identical existing record refuses.
 */
export declare function createRecordIfAbsent(input: CloudflareCreateRecordInput, config?: CloudflareConfig): Promise<CloudflareCreateRecordReceipt>;
/** One existing live record changed in place. Zone and record IDs and the zone name are explicit. */
export interface CloudflareUpdateRecordInput {
    zoneId: string;
    zoneName: string;
    recordId: string;
    /**
     * Fields to change (at least one of content, ttl, proxied). `expectContent` is a
     * read-only precondition: when given, a record whose current content differs is
     * refused before any write.
     */
    update: {
        content?: string;
        ttl?: number;
        proxied?: boolean;
        expectContent?: string;
    };
}
export interface CloudflareRecordUpdateReceipt {
    changed: true;
    provider: "cloudflare";
    zone: {
        id: string;
        name: string;
    };
    /** The exact pre-change record; a rollback applies the same fields back to this state. */
    previous: CloudflareRecord & {
        id: string;
    };
    record: CloudflareRecord & {
        id: string;
    };
}
/**
 * Change one existing record IN PLACE by record ID: one PATCH of only the fields
 * given, then a readback that must match. The record ID, type and name are
 * preserved; nothing is deleted, recreated or retried. Cloudflare validates the
 * record's own content semantics. A failed or ambiguous call must be reconciled
 * with a provider read before another attempt.
 */
export declare function updateRecordInPlace(input: CloudflareUpdateRecordInput, config?: CloudflareConfig): Promise<CloudflareRecordUpdateReceipt>;
export declare function listZones(config?: CloudflareConfig): Promise<CloudflareZone[]>;
export declare function getZone(domain: string, config?: CloudflareConfig): Promise<CloudflareZone | null>;
export declare function createZone(domain: string, config?: CloudflareConfig): Promise<CloudflareZone>;
/**
 * Find-or-create a Cloudflare zone and return its assigned nameservers.
 * Idempotent: reuses an existing zone for the domain if present. The returned
 * nameservers are what the registrar should be delegated to when Cloudflare is
 * the selected DNS provider.
 *
 * `deps` is injectable for testing; defaults to the real getZone/createZone.
 */
export declare function ensureZone(domain: string, config?: CloudflareConfig, deps?: {
    getZone: (d: string, c?: CloudflareConfig) => Promise<CloudflareZone | null>;
    createZone: (d: string, c?: CloudflareConfig) => Promise<CloudflareZone>;
}): Promise<CloudflareZone>;
export declare function deleteZone(zoneId: string, config?: CloudflareConfig): Promise<void>;
/** Read the zone's current edge-to-origin TLS mode. */
export declare function getZoneOriginTlsMode(zoneId: string, config?: CloudflareConfig): Promise<CloudflareObservedOriginTlsMode>;
/**
 * Apply one explicit zone TLS intent and read it back. A zone already using
 * strict or origin-pull mode is never silently weakened. Automatic SSL/TLS is
 * a separate Cloudflare setting and is deliberately not changed here.
 */
export declare function ensureZoneOriginTlsMode(zoneId: string, requested: CloudflareOriginTlsMode, config?: CloudflareConfig): Promise<{
    mode: CloudflareOriginTlsMode | "origin_pull";
    changed: boolean;
    downgradeRefused: boolean;
}>;
/**
 * Zone settings this package may read or change, with their accepted values.
 * Deliberately an allowlist: a setting is added here with its values before any
 * command can reach it, and nothing else on the zone is ever written.
 */
export declare const CLOUDFLARE_ZONE_SETTINGS: {
    /** Scrape Shield email-address obfuscation, as Cloudflare reports it. */
    readonly email_obfuscation: readonly ["on", "off"];
};
export type CloudflareZoneSettingName = keyof typeof CLOUDFLARE_ZONE_SETTINGS;
/** Read one allowlisted zone setting exactly as the provider reports it. */
export declare function getZoneSetting(zoneId: string, setting: string, config?: CloudflareConfig): Promise<string>;
/**
 * Set one allowlisted zone setting to an explicit value and read it back.
 * A zone already at the requested value costs one read and no write. A change
 * is a single PATCH of that setting only; no other setting is touched, and a
 * readback that does not match the requested value fails the operation.
 */
export declare function ensureZoneSetting(zoneId: string, setting: string, value: string, config?: CloudflareConfig): Promise<{
    setting: CloudflareZoneSettingName;
    value: string;
    changed: boolean;
}>;
/** Retry only contradictory/incomplete enumeration evidence, never a provider write. */
export declare class CloudflareDnsPaginationError extends Error {
    constructor(reason: string, page: number, count: number, metadata?: Record<string, unknown>);
}
export declare const DNS_MAIL_RECONCILIATION_TIMEOUT_MS = 25000;
export interface DnsReadOptions {
    assertLease?: () => Promise<void>;
    deadline?: number;
}
/** A fresh complete enumeration per attempt; no records survive a rejected attempt. */
export declare function listRecords(zoneId: string, config?: CloudflareConfig, options?: DnsReadOptions): Promise<CloudflareRecord[]>;
export declare function upsertRecord(zoneId: string, record: CloudflareRecord, config?: CloudflareConfig): Promise<void>;
/** Reconcile only the explicitly named DNS record groups, leaving every other group untouched. */
export declare function reconcileRecords(zoneId: string, records: CloudflareRecord[], config?: CloudflareConfig): Promise<void>;
export interface DnsRecordGroupState {
    hash_scheme: "domains-dns-group-v1";
    zone_id: string;
    record_name: string;
    record_type: string;
    records: Array<{
        id: string;
        type: string;
        name: string;
        value: string;
        ttl: number;
        priority: number | null;
        proxied: boolean | null;
    }>;
    records_sha256: string;
}
/** Public hash contract: SHA256 of UTF-8 JSON of the returned fields except records_sha256.
 * Object keys use the order below; records sort by provider id (JS lexical order).
 * The complete provider population is read before selecting a group; never truncate.
 */
export declare function getDnsRecordGroupState(zoneId: string, type: string, name: string, config?: CloudflareConfig, options?: DnsReadOptions): Promise<DnsRecordGroupState>;
/** Guarded single-group reconciliation retains exact matching records and their ids.
 * The caller holds the service zone lock. Cloudflare offers no atomic CAS against
 * external writers; compare immediately before writes and verify the complete result.
 */
export declare function reconcileGuardedRecords(zoneId: string, desired: CloudflareRecord[], expectedHash: string, config?: CloudflareConfig, assertLease?: () => Promise<void>): Promise<DnsRecordGroupState>;
/** Ensure mail authorization without replacing existing record groups. */
export declare function reconcileMailRecords(zoneId: string, records: CloudflareRecord[], config?: CloudflareConfig, assertLease?: () => Promise<void>, operationDeadline?: number): Promise<CloudflareRecord[]>;
export declare function deleteRecord(zoneId: string, recordId: string, config?: CloudflareConfig): Promise<void>;
export declare function deleteRecordByNameType(zoneId: string, name: string, type: string, config?: CloudflareConfig): Promise<void>;
export declare function createCloudflareProvider(config?: CloudflareConfig): DnsProvider & DomainInventoryProvider;
export interface CloudflareWorkerDomain {
    id?: string;
    zone_id?: string;
    zone_name?: string;
    hostname: string;
    service: string;
    environment?: string;
    enabled?: boolean;
}
export declare function listWorkerCustomDomains(config?: CloudflareConfig): Promise<CloudflareWorkerDomain[]>;
export declare function bindWorkerCustomDomain(hostname: string, zoneId: string, workerName?: string, config?: CloudflareConfig): Promise<CloudflareWorkerDomain>;
export declare function workerCustomDomainReady(hostname: string, zoneId: string, workerName?: string, config?: CloudflareConfig, fetchImpl?: typeof fetch, readiness?: {
    path?: string;
    header?: string;
    value?: string;
}): Promise<boolean>;
//# sourceMappingURL=cloudflare.d.ts.map