UNPKG

@hasna/domains

Version:

Domain portfolio, registrar, marketplace, and DNS management for AI agents — CLI + MCP + HTTP API + SDK, shared account API and PostgreSQL

217 lines • 10.4 kB
/** * 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