import {z} from 'zod' export type CiraLanguageCode = 'en_US' | 'fr_CA' | 'en' | 'fr'; export type CiraCanadianPresenceRequirement = 'CCT' | 'LGR' | 'RES' | 'ABO' | 'CCO' | 'ASS' | 'PRT' | 'TDM' | 'TRD' | 'PLT' | 'TRS' | 'LAM' | 'EDU' | 'HOP' | 'GOV' | 'INB' | 'OMK' | 'MAJ'; export type DomainContactCiraRequestBody = {registrantAgreementAcknowledged: true, preferredLanguageCode: CiraLanguageCode, canadianPresenceRequirement: CiraCanadianPresenceRequirement}; export type DomainOwnerContactTldSpecificRequestBody = {ca?: DomainContactCiraRequestBody | undefined}; export type ContactPhoneRequestBody = {number: string, dialingPrefix: string}; export type DomainOwnerContactRequestBody = {tldData?: DomainOwnerContactTldSpecificRequestBody | undefined, id?: string | undefined, firstName?: string | undefined, lastName?: string | undefined, organization?: string | undefined, countryCode?: string | undefined, street?: string | undefined, city?: string | undefined, postalCode?: string | undefined, stateProvince?: string | undefined, phone?: ContactPhoneRequestBody | undefined, email?: string | undefined}; export type DomainRegistrationRequestBodyOwner = {type: 'SELF', contact?: DomainOwnerContactRequestBody | undefined}; export type DomainContactRequestBody = {id?: string | undefined, firstName?: string | undefined, lastName?: string | undefined, organization?: string | undefined, countryCode?: string | undefined, street?: string | undefined, city?: string | undefined, postalCode?: string | undefined, stateProvince?: string | undefined, phone?: ContactPhoneRequestBody | undefined, email?: string | undefined}; export type DomainDnsContactsRequestBody = {admin?: DomainContactRequestBody | undefined, tech?: DomainContactRequestBody | undefined, billing?: DomainContactRequestBody | undefined}; export type DomainDnsRecordCreateRequestBody = {subName?: string | undefined, type: 'A' | 'AAAA' | 'ALIAS' | 'CAA' | 'CNAME' | 'DNSKEY' | 'DS' | 'HTTPS' | 'IPSECKEY' | 'MX' | 'NAPTR' | 'NS' | 'PTR' | 'SOA' | 'SPF' | 'SRV' | 'SSHFP' | 'SVCB' | 'TLSA' | 'TXT', values: Array, ttl: number, $upsert?: ('REPLACE' | 'APPEND' | 'DISALLOWED') | undefined, $forceCompatibility: boolean}; export type DnssecAlgorithm = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 23 | 252 | 253 | 254; export type DnssecDigestType = 1 | 2 | 3 | 4 | 5 | 6; export type DomainDnsSecruityCreateRequestBodyDnssecItem = {keyTag: number, algorithm: DnssecAlgorithm, digestType: DnssecDigestType, digest: string}; export type DomainDnsSecruityCreateRequestBodyDnssec = {items: Array}; export type DomainRegistrationRequestBodyDns = {authorizationCode?: string | undefined, period?: number | undefined, contacts?: DomainDnsContactsRequestBody | undefined, $useExistingNameservers?: boolean | undefined, nameservers?: Array | undefined, records?: Array | undefined, dnssec?: DomainDnsSecruityCreateRequestBodyDnssec | undefined}; export type DomainFlagStatus = 'ENABLED' | 'DISABLED'; export type DomainFlagUpdateRequestBodyFlag = {status: DomainFlagStatus}; export type DomainFlagUpdateRequestBody = {DNS_RESOLUTION?: DomainFlagUpdateRequestBodyFlag | undefined, DNS_TRANSFER_OUT?: DomainFlagUpdateRequestBodyFlag | undefined, DNS_DELETE?: DomainFlagUpdateRequestBodyFlag | undefined, DNS_UPDATE?: DomainFlagUpdateRequestBodyFlag | undefined, DNS_RENEW?: DomainFlagUpdateRequestBodyFlag | undefined, DNS_WHOIS_PROXY?: DomainFlagUpdateRequestBodyFlag | undefined}; export type DomainRegistrationRequestBody = {name: string, owner?: DomainRegistrationRequestBodyOwner | undefined, dns?: DomainRegistrationRequestBodyDns | undefined, flags?: DomainFlagUpdateRequestBody | undefined}; export type CreateApiDomainsArgs = {$preview: boolean, body: DomainRegistrationRequestBody}; export type CreateApiDomainsOptions = {baseUrl?: string | undefined, bearer?: string | undefined, fetch?: typeof fetch | undefined}; export const platformOperationStatus = z.enum(["PREVIEW", "QUEUED", "PROCESSING", "FAILED", "COMPLETED", "CANCELLED", "AWAITING_UPDATES"]); export const contactAssignment = z.object({new: z.boolean(), id: z.string().optional()}); export const nonRegistrantContacts = z.object({admin: contactAssignment.optional(), billing: contactAssignment.optional(), tech: contactAssignment.optional()}); export const operationDependencyError = z.object({code: z.string(), message: z.string()}); export const platformOperationDomainRegister = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DOMAIN_REGISTER"), parameters: z.object({owner: contactAssignment, contacts: nonRegistrantContacts.optional(), period: z.number().int().optional()}), error: operationDependencyError.optional()}); export const platformOperationDomainDnsRenew = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DOMAIN_DNS_RENEW"), parameters: z.object({period: z.number().int()}), error: operationDependencyError.optional()}); export const dnsRecordType = z.enum(["A", "AAAA", "ALIAS", "CAA", "CNAME", "DNSKEY", "DS", "HTTPS", "IPSECKEY", "MX", "NAPTR", "NS", "PTR", "SOA", "SPF", "SRV", "SSHFP", "SVCB", "TLSA", "TXT"]); export const platformOperationDnsRecordCreate = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DNS_RECORD_CREATE"), parameters: z.object({subName: z.string().optional(), type: dnsRecordType, values: z.array(z.string()), ttl: z.number().int()}), result: z.object({id: z.string()}).optional(), error: operationDependencyError.optional()}); export const platformOperationDnsRecordUpdate = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DNS_RECORD_UPDATE"), parameters: z.object({id: z.string(), values: z.array(z.string()).optional(), ttl: z.number().int().optional()}), error: operationDependencyError.optional()}); export const platformOperationDnsRecordDelete = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DNS_RECORD_DELETE"), parameters: z.object({id: z.string()}), error: operationDependencyError.optional()}); export const platformOperationDomainSetNameServers = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DOMAIN_SET_NAMESERVERS"), parameters: z.object({items: z.array(z.string()).optional()}), error: operationDependencyError.optional()}); export const platformOperationDomainDnsSecuritySetDnssec = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DOMAIN_DNS_SECURITY_SET_DNSSEC"), parameters: z.object({domain: z.string().optional()}), error: operationDependencyError.optional()}); export const platformOperationDomainDnsSecurityDeleteDnssec = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DOMAIN_DNS_SECURITY_DELETE_DNSSEC"), parameters: z.object({domain: z.string().optional()}), error: operationDependencyError.optional()}); export const platformOperationDomainDnsSecurityUpdateZone = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DOMAIN_DNS_SECURITY_UPDATE_ZONE"), parameters: z.object({domain: z.string().optional()}), error: operationDependencyError.optional()}); export const domainFlagLocks = z.record(z.string(), z.boolean()); export const platformOperationDomainFlagLockUpdate = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DOMAIN_FLAG_LOCK_UPDATE"), parameters: z.object({locks: domainFlagLocks.optional(), policyLocks: domainFlagLocks.optional()}), error: operationDependencyError.optional()}); export const platformOperationDomainDnsContactSet = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("DOMAIN_DNS_CONTACT_SET"), parameters: z.object({owner: contactAssignment.optional(), admin: contactAssignment.optional(), billing: contactAssignment.optional(), tech: contactAssignment.optional()}), error: operationDependencyError.optional()}); export const contactPurpose = z.enum(["OWNER", "ADMIN", "TECH", "BILLING"]); export const platformOperationContactCreate = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("CONTACT_CREATE"), parameters: z.object({purposes: z.array(contactPurpose)}), result: z.object({id: z.string()}).optional(), error: operationDependencyError.optional()}); export const webhookType = z.enum(["OPERATION_FINISHED", "OPERATION_ACTION_REQUIRED", "OPERATION_CREATED"]); export const platformOperationWebhookCreate = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("WEBHOOK_CREATE"), parameters: z.object({url: z.string(), type: webhookType}), result: z.object({id: z.string()}).optional(), error: operationDependencyError.optional()}); export const platformOperationWebhookDelete = z.object({id: z.string(), "@type": z.literal("unstoppabledomains.com/partner.v3.PlatformOperation"), status: platformOperationStatus, type: z.literal("WEBHOOK_DELETE"), parameters: z.object({id: z.string()}), error: operationDependencyError.optional()}); export const platformOperationDependency = z.discriminatedUnion("type", [platformOperationDomainRegister, platformOperationDomainDnsRenew, platformOperationDnsRecordCreate, platformOperationDnsRecordUpdate, platformOperationDnsRecordDelete, platformOperationDomainSetNameServers, platformOperationDomainDnsSecuritySetDnssec, platformOperationDomainDnsSecurityDeleteDnssec, platformOperationDomainDnsSecurityUpdateZone, platformOperationDomainFlagLockUpdate, platformOperationDomainDnsContactSet, platformOperationContactCreate, platformOperationWebhookCreate, platformOperationWebhookDelete]); export const operationCheckResponse = z.object({"@type": z.literal("unstoppabledomains.com/partner.v3.Operation"), id: z.string(), status: z.enum(["PREVIEW", "QUEUED", "PROCESSING", "FAILED", "COMPLETED", "CANCELLED", "AWAITING_UPDATES"]), type: z.enum(["ADMIN_GENERIC", "DOMAIN_ADMIN_UPDATE", "DOMAIN_RENEW", "ACCOUNT_UPDATE", "DOMAIN_DNS_RECORD_CREATE", "DOMAIN_DNS_RECORD_UPDATE", "DOMAIN_DNS_RECORD_DELETE", "DOMAIN_DNS_RECORD_BATCH", "DOMAIN_DNS_SECURITY_ENABLE", "DOMAIN_DNS_SECURITY_DELETE", "DOMAIN_DNS_NAME_SERVER_UPDATE", "DOMAIN_HOSTING_UPDATE", "DOMAIN_HOSTING_DISABLE", "DOMAIN_TRANSFER_OUT", "DOMAIN_TRANSFER_OUT_TOGGLE", "DOMAIN_FLAGS_UPDATE", "DOMAIN_CONTACTS_UPDATE", "CONTACT_CREATE", "DOMAIN_SUGGESTION_CREATE", "DOMAIN_SUGGESTION_REFRESH", "DOMAIN_ADMIN_DNS_ZONE_MIGRATE"]), domain: z.string().optional(), createdAtTimestamp: z.number(), lastUpdatedTimestamp: z.number(), validUntilTimestamp: z.number().optional(), dependencies: z.array(platformOperationDependency)}); export const domainOperationResponse = z.object({"@type": z.literal("unstoppabledomains.com/partner.v3.DomainOperationResult"), operation: operationCheckResponse}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * Register or transfer in a domain * * POST /domains */ export const createApiDomains = async (args: CreateApiDomainsArgs, options?: CreateApiDomainsOptions) => { const {baseUrl = "https://api.unstoppabledomains.com/partner/v3", fetch: fetchFn = fetch, bearer} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('content-type', "application/json") headers.set('accept', "application/json") if (args.$preview != null) query.append("$preview", String(args.$preview)) if (bearer !== undefined) headers.set('authorization', `Bearer ${bearer}`) const url = `${baseUrl.replace(/\/+$/, '')}/domains${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "POST", headers, body: JSON.stringify(args.body)}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(domainOperationResponse, await res.json()) };