@aoxborrow/registrar-client 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1076,8 +1076,8 @@ var CloudflareRegistrar = class _CloudflareRegistrar extends BaseRegistrar {
1076
1076
  message: `${domainName} is not a zone in this account; DNS is managed via the Zones API`
1077
1077
  };
1078
1078
  }
1079
- const dnsPath = `/zones/${encodeURIComponent(zone.id)}/dns_records`;
1080
- const list = await this.http.request({ path: dnsPath, ...opts });
1079
+ const dnsPath2 = `/zones/${encodeURIComponent(zone.id)}/dns_records`;
1080
+ const list = await this.http.request({ path: dnsPath2, ...opts });
1081
1081
  if (!list.success) {
1082
1082
  return {
1083
1083
  success: false,
@@ -1088,7 +1088,7 @@ var CloudflareRegistrar = class _CloudflareRegistrar extends BaseRegistrar {
1088
1088
  if (!rec.id) continue;
1089
1089
  const del = await this.http.request({
1090
1090
  method: "DELETE",
1091
- path: `${dnsPath}/${encodeURIComponent(rec.id)}`,
1091
+ path: `${dnsPath2}/${encodeURIComponent(rec.id)}`,
1092
1092
  ...opts
1093
1093
  });
1094
1094
  if (!del.success) {
@@ -1104,7 +1104,7 @@ var CloudflareRegistrar = class _CloudflareRegistrar extends BaseRegistrar {
1104
1104
  if (type === "NS" && name === zone.name) continue;
1105
1105
  const create = await this.http.request({
1106
1106
  method: "POST",
1107
- path: dnsPath,
1107
+ path: dnsPath2,
1108
1108
  body: toCfDnsBody(record, type, name),
1109
1109
  ...opts
1110
1110
  });
@@ -1773,6 +1773,527 @@ function toContact(c) {
1773
1773
  return contact;
1774
1774
  }
1775
1775
 
1776
+ // src/registrars/domain101.ts
1777
+ var Domain101HttpClient = class extends HttpClient {
1778
+ async request(req) {
1779
+ try {
1780
+ return await super.request(req);
1781
+ } catch (error) {
1782
+ if (error instanceof AuthorizationError) {
1783
+ const scope = requiredScope(req);
1784
+ if (!scope || error.providerCode !== "INSUFFICIENT_PERMISSIONS") throw error;
1785
+ const scoped = new AuthorizationError(
1786
+ `101domain: this API key does not have the ${scope} scope. Create a key with ${scope} to ${SCOPE_PURPOSE[scope]}.`
1787
+ );
1788
+ scoped.providerCode = error.providerCode;
1789
+ throw scoped;
1790
+ }
1791
+ if (error instanceof OutcomeUnknownError)
1792
+ throw new OutcomeUnknownError(
1793
+ "101domain: the registrar did not confirm this change. Re-read the domain before trying again.",
1794
+ { feature: error.feature }
1795
+ );
1796
+ if (error instanceof ParsingError) throw new ParsingError("101domain: invalid JSON response");
1797
+ if (error instanceof ConnectionError)
1798
+ throw new ConnectionError("101domain: network request failed", { notSent: error.notSent });
1799
+ throw error;
1800
+ }
1801
+ }
1802
+ async toStatusError(response, url) {
1803
+ const detail = await errorDetail(response);
1804
+ const error = await super.toStatusError(
1805
+ new Response(null, { status: response.status, headers: response.headers }),
1806
+ url
1807
+ );
1808
+ if (detail) {
1809
+ error.message = `101domain: ${detail.message}`;
1810
+ error.providerCode = detail.code;
1811
+ } else if (response.status === 403) {
1812
+ error.message = "101domain: the request was refused before reaching the API (403 without an API error)";
1813
+ }
1814
+ return error;
1815
+ }
1816
+ };
1817
+ var SCOPE_PURPOSE = {
1818
+ domains_read: "read the domain portfolio",
1819
+ domains_write: "change URL forwarding",
1820
+ dns_read: "read DNS records and nameservers",
1821
+ dns_write: "change DNS records and nameservers"
1822
+ };
1823
+ function requiredScope(req) {
1824
+ const write = !REST_SAFE_METHODS.includes((req.method ?? "GET").toUpperCase());
1825
+ if (req.path.startsWith("/dns/")) return write ? "dns_write" : "dns_read";
1826
+ if (req.path === "/domains" || /^\/domains\/[^/]+(?:\/forwarding)?$/.test(req.path))
1827
+ return write ? "domains_write" : "domains_read";
1828
+ return void 0;
1829
+ }
1830
+ async function errorDetail(response) {
1831
+ try {
1832
+ const body = await response.json();
1833
+ if (typeof body?.code !== "string" || typeof body.message !== "string" || !body.message)
1834
+ return null;
1835
+ return { code: body.code, message: body.message.replace(/\s+/g, " ").slice(0, 200) };
1836
+ } catch {
1837
+ return null;
1838
+ }
1839
+ }
1840
+ var Domain101Registrar = class extends BaseRegistrar {
1841
+ name = "101domain";
1842
+ static safeMethods = REST_SAFE_METHODS;
1843
+ static displayName = "101domain";
1844
+ static website = "101domain.com";
1845
+ static supportsSandbox = false;
1846
+ static configFields = [
1847
+ { name: "apiKey", label: "API key", type: "password", required: true }
1848
+ ];
1849
+ static helpText = "Create a Bearer API key under My Account > Developer Tools - API & MCP. Use domains_read and dns_read for reads; dns_write for DNS/nameservers and domains_write for forwarding. Only a primary account with 2FA or SSO can create keys. Keys expire after at most one year. Registration, renewal, auto-renew, privacy, lock, contacts and transfers are not available through this API.";
1850
+ static extendedFeatures = [
1851
+ Feature.GetDomainForwarding,
1852
+ Feature.SetDomainForwarding
1853
+ ];
1854
+ constructor(credentials, options) {
1855
+ const apiKey = credentials.apiKey?.trim() ?? "";
1856
+ if (/[\r\n]/.test(apiKey)) throw new ConfigurationError("101domain: invalid API key format");
1857
+ const config = {
1858
+ baseUrl: selectBaseUrl("101domain", options?.environment, {
1859
+ production: "https://api.101domain.com/v1"
1860
+ }),
1861
+ headers: { Authorization: `Bearer ${apiKey}` }
1862
+ };
1863
+ super(credentials, config, options);
1864
+ this.http = new Domain101HttpClient({
1865
+ ...config,
1866
+ safeMethods: REST_SAFE_METHODS,
1867
+ options: this.options
1868
+ });
1869
+ }
1870
+ async testConnection(opts) {
1871
+ try {
1872
+ await this.request({
1873
+ path: "/domains",
1874
+ query: { page: 1, per_page: 1 },
1875
+ ...opts
1876
+ });
1877
+ return { success: true, message: "Connection successful" };
1878
+ } catch (error) {
1879
+ return { success: false, message: toRegistrarError(error).message };
1880
+ }
1881
+ }
1882
+ async listDomains(opts) {
1883
+ const { search, detailed: _detailed, ...requestOpts } = opts ?? {};
1884
+ const result = [];
1885
+ const seen = /* @__PURE__ */ new Set();
1886
+ for (let page = 1; ; page++) {
1887
+ const response = await this.request({
1888
+ path: "/domains",
1889
+ query: { page, per_page: 50 },
1890
+ ...requestOpts
1891
+ });
1892
+ if (!Array.isArray(response.data)) throw invalid("domain list");
1893
+ const pagination = response.meta?.pagination;
1894
+ if (!pagination || pagination.current_page !== page || !Number.isInteger(pagination.total_pages) || pagination.total_pages < 0 || pagination.total_pages > 1e5 || !Number.isInteger(pagination.total) || pagination.total < 0)
1895
+ throw invalid("pagination");
1896
+ for (const raw of response.data) {
1897
+ const domain = toDomain(raw);
1898
+ if (seen.has(domain.domainName)) throw invalid("duplicate domain across pages");
1899
+ seen.add(domain.domainName);
1900
+ result.push(domain);
1901
+ }
1902
+ if (page >= pagination.total_pages) {
1903
+ if (result.length !== pagination.total) throw invalid("incomplete portfolio");
1904
+ return filterDomains(
1905
+ result.filter((d) => !d.deleted),
1906
+ search
1907
+ );
1908
+ }
1909
+ if (!response.data.length) throw invalid("empty intermediate page");
1910
+ }
1911
+ }
1912
+ async getDomain(domainName, opts) {
1913
+ const domain = toDomain(
1914
+ (await this.request({ path: domainPath(domainName), ...opts })).data
1915
+ );
1916
+ if (domain.domainName !== normalizeDomain(domainName)) throw invalid("domain identity");
1917
+ return domain;
1918
+ }
1919
+ async getNameservers(domainName, opts) {
1920
+ const raw = (await this.request({ path: `${dnsPath(domainName)}/nameservers`, ...opts })).data;
1921
+ if (!Array.isArray(raw) || raw.some((n) => typeof n !== "string")) throw invalid("nameservers");
1922
+ return raw.map((n) => n.toLowerCase());
1923
+ }
1924
+ async updateNameservers(domainName, nameservers, opts) {
1925
+ const normalized = nameservers.map((n) => n.trim().toLowerCase());
1926
+ if (normalized.length < 2 || normalized.length > 13 || new Set(normalized).size !== normalized.length || normalized.some((n) => !/^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9-]+$/.test(n)))
1927
+ throw new ConfigurationError("101domain: provide 2\u201313 distinct nameserver hostnames");
1928
+ try {
1929
+ const response = await this.request({
1930
+ ...opts,
1931
+ method: "PUT",
1932
+ path: `${dnsPath(domainName)}/nameservers`,
1933
+ body: { nameservers: normalized }
1934
+ });
1935
+ if (response.data?.change_status === "pending")
1936
+ return {
1937
+ success: true,
1938
+ pending: true,
1939
+ message: "Nameserver change submitted; pending registry processing. Re-read nameservers to confirm completion."
1940
+ };
1941
+ if (response.data?.change_status === "failed")
1942
+ return { success: false, message: "101domain rejected the nameserver change" };
1943
+ if (response.data?.change_status !== "completed")
1944
+ throw new OutcomeUnknownError(
1945
+ "101domain: invalid nameserver change confirmation. Re-read nameservers before trying again."
1946
+ );
1947
+ return { success: true, message: "Nameservers already configured as requested" };
1948
+ } catch (error) {
1949
+ return failed(error);
1950
+ }
1951
+ }
1952
+ async checkAvailability(domainNames, opts) {
1953
+ const names = [...new Set(domainNames.map(normalizeDomain))];
1954
+ const result = [];
1955
+ for (const domains of chunks(names, 50)) {
1956
+ const response = await this.request({
1957
+ ...opts,
1958
+ method: "POST",
1959
+ path: "/domains/bulk-search",
1960
+ body: { domains, pricing_term: 1 }
1961
+ });
1962
+ if (!Array.isArray(response.data) || (response.invalid?.length ?? 0) > 0)
1963
+ throw invalid("availability results");
1964
+ const found = /* @__PURE__ */ new Set();
1965
+ for (const raw of response.data) {
1966
+ if (typeof raw.domain_name !== "string" || typeof raw.available !== "boolean")
1967
+ throw invalid("availability result");
1968
+ const domainName = normalizeDomain(raw.domain_name);
1969
+ if (!domains.includes(domainName) || found.has(domainName))
1970
+ throw invalid("availability identity");
1971
+ found.add(domainName);
1972
+ const price2 = oneYear(raw.pricing);
1973
+ result.push({
1974
+ domainName,
1975
+ available: raw.available,
1976
+ premium: price2?.premium,
1977
+ price: amount(price2?.register),
1978
+ renewalPrice: amount(price2?.renew),
1979
+ currency: price2?.currency,
1980
+ period: 1
1981
+ });
1982
+ }
1983
+ if (found.size !== domains.length) throw invalid("incomplete availability results");
1984
+ }
1985
+ return result;
1986
+ }
1987
+ async getPricing(tldOrDomain, opts) {
1988
+ const name = normalizeDomain(tldOrDomain);
1989
+ if (name.startsWith(".") || !name.includes(".")) {
1990
+ const tld = name.replace(/^\./, "");
1991
+ const response2 = await this.request({
1992
+ path: `/tlds/${encodeURIComponent("." + tld)}`,
1993
+ query: { pricing_terms: "1" },
1994
+ ...opts
1995
+ });
1996
+ const price3 = oneYear(response2.data?.pricing);
1997
+ if (!price3) throw new NotFoundError("101domain: no one-year pricing for this TLD");
1998
+ return {
1999
+ tld,
2000
+ currency: price3.currency,
2001
+ registration: amount(price3.register),
2002
+ renewal: amount(price3.renew),
2003
+ transfer: amount(price3.transfer)
2004
+ };
2005
+ }
2006
+ const response = await this.request({
2007
+ path: "/domains/search",
2008
+ query: { domain_name: name, pricing_terms: "1" },
2009
+ ...opts
2010
+ });
2011
+ const raw = response.data;
2012
+ if (!raw || normalizeDomain(raw.domain_name) !== name) throw invalid("pricing identity");
2013
+ const price2 = oneYear(raw.pricing);
2014
+ if (!price2) throw new NotFoundError("101domain: pricing unavailable for this domain");
2015
+ return {
2016
+ tld: name.slice(name.indexOf(".") + 1),
2017
+ currency: price2.currency,
2018
+ registration: amount(price2.register),
2019
+ renewal: amount(price2.renew),
2020
+ transfer: amount(price2.transfer)
2021
+ };
2022
+ }
2023
+ async getDnsRecords(domainName, opts) {
2024
+ return (await this.rawRecords(domainName, opts)).map(fromRecord);
2025
+ }
2026
+ // Reconcile by content-derived IDs. Preserve unchanged records, edit by ID, create additions
2027
+ // before removing stale records, and stop on any failed batch. Never retry an
2028
+ // uncertain write. This replaces the custom records, not the apex NS delegation.
2029
+ async setDnsRecords(domainName, records, opts) {
2030
+ const desired = records.map((r) => recordSpec(r, domainName));
2031
+ const unique = desired.filter(
2032
+ (r, i) => desired.findIndex((other) => recordSignature(other) === recordSignature(r)) === i
2033
+ );
2034
+ let changed = false;
2035
+ try {
2036
+ const stale = (await this.rawRecords(domainName, opts)).map((raw) => ({ raw, spec: recordSpec(fromRecord(raw), domainName, false) })).filter(({ spec }) => !(spec.type === "NS" && spec.name === ""));
2037
+ const additions = [];
2038
+ const edits = [];
2039
+ for (const record of unique) {
2040
+ const index = stale.findIndex(
2041
+ ({ spec }) => recordSignature(spec) === recordSignature(record)
2042
+ );
2043
+ if (index >= 0) stale.splice(index, 1);
2044
+ else {
2045
+ const priorIndex = stale.findIndex(
2046
+ ({ spec }) => spec.type === record.type && spec.name === record.name
2047
+ );
2048
+ const prior = priorIndex >= 0 ? stale.splice(priorIndex, 1)[0].raw : void 0;
2049
+ if (prior?.proxied) record.proxied = true;
2050
+ if (record.ttl === 1 && record.proxied !== true)
2051
+ throw new ConfigurationError(
2052
+ "101domain: automatic TTL requires an existing proxied SWA record"
2053
+ );
2054
+ if (prior) {
2055
+ const edit = { ...record, id: prior.id };
2056
+ delete edit.name;
2057
+ delete edit.type;
2058
+ edits.push(edit);
2059
+ } else additions.push(record);
2060
+ }
2061
+ }
2062
+ for (const batch of chunks(edits, 25)) {
2063
+ const response = await this.request({
2064
+ ...opts,
2065
+ method: "PATCH",
2066
+ path: `${dnsPath(domainName)}/records`,
2067
+ body: { records: batch }
2068
+ });
2069
+ if (!Array.isArray(response.data) || response.data.length !== batch.length || new Set(response.data.map((r) => r?.old_id)).size !== batch.length || response.data.some(
2070
+ (r) => !r || !batch.some((edit) => edit.id === r.old_id) || typeof r.id !== "string" || !r.id
2071
+ ))
2072
+ throw new OutcomeUnknownError(
2073
+ "101domain: invalid DNS edit confirmation. Re-read the zone before trying again."
2074
+ );
2075
+ changed = true;
2076
+ }
2077
+ for (const batch of chunks(additions, 25)) {
2078
+ await this.request({
2079
+ ...opts,
2080
+ method: "POST",
2081
+ path: `${dnsPath(domainName)}/records`,
2082
+ body: { records: batch }
2083
+ });
2084
+ changed = true;
2085
+ }
2086
+ for (const batch of chunks(stale, 25)) {
2087
+ await this.request({
2088
+ ...opts,
2089
+ method: "DELETE",
2090
+ path: `${dnsPath(domainName)}/records`,
2091
+ body: { ids: batch.map(({ raw }) => raw.id) }
2092
+ });
2093
+ changed = true;
2094
+ }
2095
+ return { success: true, message: "DNS records updated successfully" };
2096
+ } catch (error) {
2097
+ const result = failed(error);
2098
+ if (!changed && !result.outcome) return result;
2099
+ return {
2100
+ ...result,
2101
+ message: `${result.message}. DNS replacement is not atomic; read the zone before retrying.`
2102
+ };
2103
+ }
2104
+ }
2105
+ async getDomainForwarding(domainName, opts) {
2106
+ const raw = (await this.request({
2107
+ path: domainPath(domainName),
2108
+ ...opts
2109
+ })).data;
2110
+ if (!raw || normalizeDomain(raw.domain_name) !== normalizeDomain(domainName))
2111
+ throw invalid("domain identity");
2112
+ const forward = raw.web_forwarding;
2113
+ if (forward === null || forward?.destination === null && forward.type === null) return [];
2114
+ if (!forward || typeof forward.destination !== "string" || forward.type !== "301" && forward.type !== "cloak")
2115
+ throw invalid("forwarding");
2116
+ return [
2117
+ {
2118
+ host: "@",
2119
+ url: forward.destination,
2120
+ type: forward.type === "301" ? "permanent" : "masked"
2121
+ }
2122
+ ];
2123
+ }
2124
+ async setDomainForwarding(domainName, forwards, opts) {
2125
+ if (forwards.length > 1 || forwards.some((f) => f.host !== "@" || f.type !== "permanent"))
2126
+ throw new ConfigurationError(
2127
+ "101domain: only one permanent apex forwarding rule is supported"
2128
+ );
2129
+ if (forwards.length) {
2130
+ let url;
2131
+ try {
2132
+ url = new URL(forwards[0].url);
2133
+ } catch {
2134
+ throw new ConfigurationError("101domain: forwarding needs a valid HTTP(S) URL");
2135
+ }
2136
+ if (!["http:", "https:"].includes(url.protocol) || url.username || url.password)
2137
+ throw new ConfigurationError("101domain: forwarding needs a valid HTTP(S) URL");
2138
+ }
2139
+ try {
2140
+ const existing = await this.getDomainForwarding(domainName, opts);
2141
+ const path = `${domainPath(domainName)}/forwarding`;
2142
+ if (!forwards.length) {
2143
+ if (existing.length) await this.request({ ...opts, method: "DELETE", path });
2144
+ } else if (existing[0]?.url !== forwards[0].url || existing[0]?.type !== "permanent") {
2145
+ await this.request({
2146
+ ...opts,
2147
+ method: existing.length ? "PATCH" : "POST",
2148
+ path,
2149
+ body: { destination: forwards[0].url, type: "301" }
2150
+ });
2151
+ }
2152
+ return {
2153
+ success: true,
2154
+ message: forwards.length ? "URL forwarding updated" : "URL forwarding cleared"
2155
+ };
2156
+ } catch (error) {
2157
+ return failed(error);
2158
+ }
2159
+ }
2160
+ async request(req) {
2161
+ const raw = await this.http.request(req);
2162
+ if (!raw || typeof raw !== "object" || raw.status !== "success" || !("data" in raw)) {
2163
+ if (req.call?.intent === "write" && !REST_SAFE_METHODS.includes(req.method ?? "GET"))
2164
+ throw new OutcomeUnknownError(
2165
+ "101domain: invalid write confirmation. Re-read the domain before trying again."
2166
+ );
2167
+ throw invalid("response envelope");
2168
+ }
2169
+ return raw;
2170
+ }
2171
+ async rawRecords(domainName, opts) {
2172
+ const raw = (await this.request({ path: `${dnsPath(domainName)}/records`, ...opts })).data;
2173
+ if (!Array.isArray(raw) || raw.some(
2174
+ (r) => !r || typeof r.id !== "string" || !r.id || typeof r.name !== "string" || typeof r.value !== "string" || typeof r.type !== "string" || !Number.isInteger(r.ttl)
2175
+ ))
2176
+ throw invalid("DNS records");
2177
+ if (new Set(raw.map((r) => r.id)).size !== raw.length) throw invalid("duplicate DNS ids");
2178
+ return raw;
2179
+ }
2180
+ };
2181
+ function invalid(part) {
2182
+ return new InvalidResponseError(`101domain: invalid ${part}`);
2183
+ }
2184
+ function failed(error) {
2185
+ return {
2186
+ success: false,
2187
+ message: toRegistrarError(error).message,
2188
+ ...error instanceof OutcomeUnknownError ? { outcome: "unknown" } : {}
2189
+ };
2190
+ }
2191
+ function domainPath(name) {
2192
+ return `/domains/${encodeURIComponent(normalizeDomain(name))}`;
2193
+ }
2194
+ function dnsPath(name) {
2195
+ return `/dns/${encodeURIComponent(normalizeDomain(name))}`;
2196
+ }
2197
+ function chunks(items, size) {
2198
+ const result = [];
2199
+ for (let i = 0; i < items.length; i += size) result.push(items.slice(i, i + size));
2200
+ return result;
2201
+ }
2202
+ function oneYear(pricing) {
2203
+ if (pricing == null) return void 0;
2204
+ if (!Array.isArray(pricing)) throw invalid("pricing");
2205
+ return pricing.find((p) => p?.term_years === 1);
2206
+ }
2207
+ function amount(raw) {
2208
+ if (raw == null) return void 0;
2209
+ if (!/^\d+(?:\.\d+)?$/.test(raw)) throw invalid("price");
2210
+ const value = Number(raw);
2211
+ if (!Number.isFinite(value)) throw invalid("price");
2212
+ return value;
2213
+ }
2214
+ function toDomain(raw) {
2215
+ if (!raw || typeof raw.domain_name !== "string" || !raw.domain_name.trim())
2216
+ throw invalid("domain");
2217
+ return createDomain({
2218
+ domainName: normalizeDomain(raw.domain_name),
2219
+ registrar: "101domain",
2220
+ status: raw.status,
2221
+ createdDate: raw.registered_at,
2222
+ expirationDate: raw.expires_at,
2223
+ autoRenew: raw.auto_renew,
2224
+ locked: raw.registry_statuses?.some((s) => /^clientTransferProhibited$/i.test(s)),
2225
+ nameservers: raw.nameservers,
2226
+ deleted: !isHeld(raw)
2227
+ });
2228
+ }
2229
+ var GONE = /* @__PURE__ */ new Set(["DELETED", "CANCELLED", "DENIED", "XFER_AWAY"]);
2230
+ function isHeld(raw) {
2231
+ const status = raw.status?.toUpperCase() ?? "";
2232
+ return !GONE.has(status) && !status.startsWith("XFER_IN_") && raw.registered_at != null;
2233
+ }
2234
+ function fromRecord(raw) {
2235
+ const common = { name: raw.name || "@", type: raw.type, ttl: raw.ttl };
2236
+ if (raw.type === "MX") {
2237
+ const match = /^(\d+)\s+(.+)$/.exec(raw.value);
2238
+ if (!match) throw invalid("MX record");
2239
+ return { ...common, value: match[2], priority: Number(match[1]) };
2240
+ }
2241
+ if (raw.type === "SRV") {
2242
+ const match = /^(\d+)\s+(\d+)\s+(\d+)\s+(.+)$/.exec(raw.value);
2243
+ if (!match) throw invalid("SRV record");
2244
+ return {
2245
+ ...common,
2246
+ value: match[4],
2247
+ priority: Number(match[1]),
2248
+ weight: Number(match[2]),
2249
+ port: Number(match[3])
2250
+ };
2251
+ }
2252
+ return { ...common, value: raw.value };
2253
+ }
2254
+ function recordSpec(record, domain, strict = true) {
2255
+ const type = record.type.toUpperCase();
2256
+ const zone = normalizeDomain(domain);
2257
+ let name = record.name.trim().toLowerCase().replace(/\.$/, "");
2258
+ if (name === zone || name === "@") name = "";
2259
+ else if (name.endsWith("." + zone)) name = name.slice(0, -zone.length - 1);
2260
+ const reject = (message) => {
2261
+ if (strict) throw new ConfigurationError(`101domain: ${message}`);
2262
+ };
2263
+ if (!["A", "AAAA", "CNAME", "NS", "MX", "TXT", "SRV", "CAA"].includes(type) || type === "NS" && !name)
2264
+ reject("unsupported DNS type or apex NS record");
2265
+ const ttl = record.ttl ?? 3600;
2266
+ if (!Number.isInteger(ttl) || ttl !== 1 && ttl < 300)
2267
+ reject("DNS TTL must be at least 300 seconds");
2268
+ const result = { name, type, ttl };
2269
+ if (type === "MX" || type === "SRV") {
2270
+ if (!Number.isInteger(record.priority) || record.priority < 0 || record.priority > 65535)
2271
+ reject("MX/SRV priority is required");
2272
+ result.priority = record.priority;
2273
+ result.target = record.value;
2274
+ if (type === "SRV") {
2275
+ if (![record.weight, record.port].every((n) => Number.isInteger(n) && n >= 0 && n <= 65535))
2276
+ reject("SRV weight and port are required");
2277
+ result.weight = record.weight;
2278
+ result.port = record.port;
2279
+ }
2280
+ } else if (type === "CAA") {
2281
+ const match = /^(\d+)\s+(\w+)\s+"([\s\S]*)"$/.exec(record.value);
2282
+ if (!match || Number(match[1]) > 255) {
2283
+ reject("CAA needs flags, tag and quoted value");
2284
+ result.value = record.value;
2285
+ } else {
2286
+ result.flag = Number(match[1]);
2287
+ result.tag = match[2];
2288
+ result.value = match[3];
2289
+ }
2290
+ } else result.value = record.value;
2291
+ return result;
2292
+ }
2293
+ function recordSignature(record) {
2294
+ return JSON.stringify(Object.entries(record).sort(([a], [b]) => a.localeCompare(b)));
2295
+ }
2296
+
1776
2297
  // src/registrars/dynadot.ts
1777
2298
  var DYNADOT_DEFAULT_NS = ["ns1.dynadot.com", "ns2.dynadot.com"];
1778
2299
  var SEARCH_BATCH_SIZE = 100;
@@ -2450,12 +2971,12 @@ function isPrivacyOn(privacy) {
2450
2971
  }
2451
2972
  function oneYearPrice(prices) {
2452
2973
  if (!prices?.length) return void 0;
2453
- const oneYear = prices.find((p) => /\b1\s*year\b/i.test(p.unit ?? "")) ?? prices[0];
2974
+ const oneYear2 = prices.find((p) => /\b1\s*year\b/i.test(p.unit ?? "")) ?? prices[0];
2454
2975
  return {
2455
- currency: oneYear.currency,
2456
- registration: toNumber(oneYear.registration_price),
2457
- renewal: toNumber(oneYear.renewal_price),
2458
- transfer: toNumber(oneYear.transfer_price)
2976
+ currency: oneYear2.currency,
2977
+ registration: toNumber(oneYear2.registration_price),
2978
+ renewal: toNumber(oneYear2.renewal_price),
2979
+ transfer: toNumber(oneYear2.transfer_price)
2459
2980
  };
2460
2981
  }
2461
2982
  function extractNameservers(glue) {
@@ -2745,8 +3266,8 @@ var GandiRegistrar = class extends BaseRegistrar {
2745
3266
  available: status.startsWith("available"),
2746
3267
  premium: status.includes("premium") || price2?.type === "premium"
2747
3268
  };
2748
- const amount = pickPrice(price2);
2749
- if (amount != null) result.price = amount;
3269
+ const amount2 = pickPrice(price2);
3270
+ if (amount2 != null) result.price = amount2;
2750
3271
  if (resp.currency) result.currency = resp.currency;
2751
3272
  if (price2?.duration_unit === "y" && price2.min_duration != null) {
2752
3273
  result.period = price2.min_duration;
@@ -5259,7 +5780,7 @@ var NamecomRegistrar = class extends BaseRegistrar {
5259
5780
  }
5260
5781
  async getAuthCode(domainName, opts) {
5261
5782
  const result = await this.http.request({
5262
- path: `${domainPath(domainName)}:getAuthCode`,
5783
+ path: `${domainPath2(domainName)}:getAuthCode`,
5263
5784
  ...opts
5264
5785
  });
5265
5786
  if (!result?.authCode) throw new InvalidResponseError("namecom: missing authorization code");
@@ -5354,7 +5875,7 @@ var NamecomRegistrar = class extends BaseRegistrar {
5354
5875
  validYears(years);
5355
5876
  const pricing = await this.domainPricing(domainName, years, opts);
5356
5877
  return this.purchase(
5357
- `${domainPath(domainName)}:renew`,
5878
+ `${domainPath2(domainName)}:renew`,
5358
5879
  {
5359
5880
  years,
5360
5881
  purchasePrice: purchaseQuote(pricing, "renewalPrice")
@@ -5399,7 +5920,7 @@ var NamecomRegistrar = class extends BaseRegistrar {
5399
5920
  }
5400
5921
  updateNameservers(domainName, nameservers, opts) {
5401
5922
  return this.mutate(
5402
- { method: "POST", path: `${domainPath(domainName)}:setNameservers`, body: { nameservers } },
5923
+ { method: "POST", path: `${domainPath2(domainName)}:setNameservers`, body: { nameservers } },
5403
5924
  opts
5404
5925
  );
5405
5926
  }
@@ -5430,14 +5951,14 @@ var NamecomRegistrar = class extends BaseRegistrar {
5430
5951
  return this.mutate(
5431
5952
  {
5432
5953
  method: "POST",
5433
- path: `${domainPath(domainName)}:setContacts`,
5954
+ path: `${domainPath2(domainName)}:setContacts`,
5434
5955
  body: { contacts: toContacts(contacts) }
5435
5956
  },
5436
5957
  opts
5437
5958
  );
5438
5959
  }
5439
5960
  async getDnsRecords(domainName, opts) {
5440
- return (await this.pages(`${domainPath(domainName)}/records`, "records", opts)).map(fromRecord);
5961
+ return (await this.pages(`${domainPath2(domainName)}/records`, "records", opts)).map(fromRecord2);
5441
5962
  }
5442
5963
  // No bulk replace endpoint. Reconcile by id: preserve identical records, update
5443
5964
  // existing type/host pairs, create additions, then delete stale records. This
@@ -5445,7 +5966,7 @@ var NamecomRegistrar = class extends BaseRegistrar {
5445
5966
  async setDnsRecords(domainName, records, opts) {
5446
5967
  const desired = records.map(toRecord).filter((record, index, all) => all.findIndex((other) => sameRecord(other, record)) === index);
5447
5968
  try {
5448
- const path = `${domainPath(domainName)}/records`;
5969
+ const path = `${domainPath2(domainName)}/records`;
5449
5970
  const existing = await this.pages(path, "records", opts);
5450
5971
  if (existing.some((r) => !Number.isInteger(r.id) || r.id <= 0))
5451
5972
  throw new InvalidResponseError("namecom: DNS response missing record ids");
@@ -5482,14 +6003,14 @@ var NamecomRegistrar = class extends BaseRegistrar {
5482
6003
  }
5483
6004
  }
5484
6005
  patchDomain(domainName, body, opts) {
5485
- return this.mutate({ method: "PATCH", path: domainPath(domainName), body }, opts);
6006
+ return this.mutate({ method: "PATCH", path: domainPath2(domainName), body }, opts);
5486
6007
  }
5487
6008
  rawDomain(domainName, opts) {
5488
- return this.http.request({ path: domainPath(domainName), ...opts });
6009
+ return this.http.request({ path: domainPath2(domainName), ...opts });
5489
6010
  }
5490
6011
  domainPricing(domainName, years, opts) {
5491
6012
  return this.http.request({
5492
- path: `${domainPath(domainName)}:getPricing`,
6013
+ path: `${domainPath2(domainName)}:getPricing`,
5493
6014
  query: { years },
5494
6015
  ...opts
5495
6016
  });
@@ -5605,7 +6126,7 @@ function toContacts(contacts) {
5605
6126
  throw new ConfigurationError("namecom: at least one contact is required");
5606
6127
  return result;
5607
6128
  }
5608
- function domainPath(domainName) {
6129
+ function domainPath2(domainName) {
5609
6130
  return `/domains/${encodeURIComponent(normalizeDomain(domainName))}`;
5610
6131
  }
5611
6132
  function validYears(years) {
@@ -5645,7 +6166,7 @@ function toRecord(r) {
5645
6166
  ...["MX", "SRV"].includes(type) ? { priority: r.priority ?? 0 } : {}
5646
6167
  };
5647
6168
  }
5648
- function fromRecord(r) {
6169
+ function fromRecord2(r) {
5649
6170
  const type = r.type.toUpperCase();
5650
6171
  const record = { type, name: r.host || "@", value: r.answer, ttl: r.ttl };
5651
6172
  if (type === "MX" || type === "SRV") record.priority = r.priority ?? 0;
@@ -7466,22 +7987,23 @@ function toSpaceshipContact(c) {
7466
7987
 
7467
7988
  // src/registrars/index.ts
7468
7989
  var registrars = {
7469
- cloudflare: CloudflareRegistrar,
7470
- dynadot: DynadotRegistrar,
7471
- gandi: GandiRegistrar,
7472
- godaddy: GoDaddyRegistrar,
7473
- namebright: NameBrightRegistrar,
7474
- namecheap: NamecheapRegistrar,
7475
- namecom: NamecomRegistrar,
7476
- namesilo: NameSiloRegistrar,
7477
- porkbun: PorkbunRegistrar,
7478
- spaceship: SpaceshipRegistrar
7990
+ "101domain": Domain101Registrar,
7991
+ "cloudflare": CloudflareRegistrar,
7992
+ "dynadot": DynadotRegistrar,
7993
+ "gandi": GandiRegistrar,
7994
+ "godaddy": GoDaddyRegistrar,
7995
+ "namebright": NameBrightRegistrar,
7996
+ "namecheap": NamecheapRegistrar,
7997
+ "namecom": NamecomRegistrar,
7998
+ "namesilo": NameSiloRegistrar,
7999
+ "porkbun": PorkbunRegistrar,
8000
+ "spaceship": SpaceshipRegistrar
7479
8001
  };
7480
8002
  function createRegistrar(name, credentials, options) {
7481
8003
  const RegistrarClass = registrars[name];
7482
8004
  return new RegistrarClass(credentials, options);
7483
8005
  }
7484
8006
 
7485
- export { ALL_FEATURES, AbortError, AuthenticationError, AuthorizationError, BaseRegistrar, CORE_FEATURES, CloudflareRegistrar, ConfigurationError, ConnectionError, ConsentRequiredError, DEFAULT_OPTIONS, DynadotRegistrar, EXTENDED_FEATURES, FEATURE_CALLS, Feature, GandiRegistrar, GoDaddyRegistrar, HttpClient, InvalidResponseError, MASKED_FORWARD_UNSUPPORTED, NameBrightRegistrar, NameSiloRegistrar, NamecheapRegistrar, NamecomRegistrar, NotFoundError, NotImplementedError, OutcomeUnknownError, ParsingError, PorkbunRegistrar, REST_SAFE_METHODS, RateLimitError, RegistrarClient, RegistrarError, RetryableRegistrarError, SpaceshipRegistrar, TimeoutError, USER_AGENT, asRead, backoffDelay, createDomain, createRegistrar, ensureArray, filterDomains, isCoreFeature, listPortfolio, markNotSent, normalizeDomain, normalizeNameservers, parseDate, parseXml, registrars, requireConsent, selectBaseUrl, settableForwards, sleep, toRegistrarError, wasNotSent };
8007
+ export { ALL_FEATURES, AbortError, AuthenticationError, AuthorizationError, BaseRegistrar, CORE_FEATURES, CloudflareRegistrar, ConfigurationError, ConnectionError, ConsentRequiredError, DEFAULT_OPTIONS, Domain101Registrar, DynadotRegistrar, EXTENDED_FEATURES, FEATURE_CALLS, Feature, GandiRegistrar, GoDaddyRegistrar, HttpClient, InvalidResponseError, MASKED_FORWARD_UNSUPPORTED, NameBrightRegistrar, NameSiloRegistrar, NamecheapRegistrar, NamecomRegistrar, NotFoundError, NotImplementedError, OutcomeUnknownError, ParsingError, PorkbunRegistrar, REST_SAFE_METHODS, RateLimitError, RegistrarClient, RegistrarError, RetryableRegistrarError, SpaceshipRegistrar, TimeoutError, USER_AGENT, asRead, backoffDelay, createDomain, createRegistrar, ensureArray, filterDomains, isCoreFeature, listPortfolio, markNotSent, normalizeDomain, normalizeNameservers, parseDate, parseXml, registrars, requireConsent, selectBaseUrl, settableForwards, sleep, toRegistrarError, wasNotSent };
7486
8008
  //# sourceMappingURL=index.js.map
7487
8009
  //# sourceMappingURL=index.js.map