@aoxborrow/registrar-client 0.4.0 → 0.6.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/README.md +120 -31
- package/dist/index.cjs +544 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +544 -45
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/registrars/index.ts +2 -0
- package/src/registrars/namecheap.ts +4 -1
- package/src/registrars/namecom.ts +704 -0
package/dist/index.js
CHANGED
|
@@ -1026,8 +1026,8 @@ var CloudflareRegistrar = class _CloudflareRegistrar extends BaseRegistrar {
|
|
|
1026
1026
|
available: c?.registrable ?? false
|
|
1027
1027
|
};
|
|
1028
1028
|
if (c?.tier) availability.premium = c.tier === "premium";
|
|
1029
|
-
const
|
|
1030
|
-
if (
|
|
1029
|
+
const price2 = toPrice(c?.pricing?.registration_cost);
|
|
1030
|
+
if (price2 != null) availability.price = price2;
|
|
1031
1031
|
if (c?.pricing?.currency) availability.currency = c.pricing.currency;
|
|
1032
1032
|
return availability;
|
|
1033
1033
|
});
|
|
@@ -1789,14 +1789,14 @@ ${bodyStr}`;
|
|
|
1789
1789
|
opts
|
|
1790
1790
|
);
|
|
1791
1791
|
for (const r of data.domain_result_list ?? []) {
|
|
1792
|
-
const
|
|
1792
|
+
const price2 = oneYearPrice(r.price_list);
|
|
1793
1793
|
results.push({
|
|
1794
1794
|
domainName: r.domain_name ?? "",
|
|
1795
1795
|
available: r.available === "Yes",
|
|
1796
1796
|
premium: r.premium != null ? r.premium.toLowerCase() === "yes" : void 0,
|
|
1797
|
-
price:
|
|
1798
|
-
currency:
|
|
1799
|
-
period:
|
|
1797
|
+
price: price2?.registration,
|
|
1798
|
+
currency: price2?.currency,
|
|
1799
|
+
period: price2 ? 1 : void 0
|
|
1800
1800
|
});
|
|
1801
1801
|
}
|
|
1802
1802
|
}
|
|
@@ -1833,13 +1833,13 @@ ${bodyStr}`;
|
|
|
1833
1833
|
opts
|
|
1834
1834
|
);
|
|
1835
1835
|
const result = (data.domain_result_list ?? [])[0];
|
|
1836
|
-
const
|
|
1836
|
+
const price2 = oneYearPrice(result?.price_list);
|
|
1837
1837
|
return {
|
|
1838
1838
|
tld,
|
|
1839
|
-
currency:
|
|
1840
|
-
registration:
|
|
1841
|
-
renewal:
|
|
1842
|
-
transfer:
|
|
1839
|
+
currency: price2?.currency ?? "USD",
|
|
1840
|
+
registration: price2?.registration,
|
|
1841
|
+
renewal: price2?.renewal,
|
|
1842
|
+
transfer: price2?.transfer
|
|
1843
1843
|
};
|
|
1844
1844
|
}
|
|
1845
1845
|
/**
|
|
@@ -2555,17 +2555,17 @@ var GandiRegistrar = class extends BaseRegistrar {
|
|
|
2555
2555
|
const products = resp.products ?? [];
|
|
2556
2556
|
const product = products.find((p) => p.process === "create") ?? products[0];
|
|
2557
2557
|
const status = product?.status ?? "";
|
|
2558
|
-
const
|
|
2558
|
+
const price2 = product?.prices?.[0];
|
|
2559
2559
|
const result = {
|
|
2560
2560
|
domainName: product?.name ?? domainName,
|
|
2561
2561
|
available: status.startsWith("available"),
|
|
2562
|
-
premium: status.includes("premium") ||
|
|
2562
|
+
premium: status.includes("premium") || price2?.type === "premium"
|
|
2563
2563
|
};
|
|
2564
|
-
const amount = pickPrice(
|
|
2564
|
+
const amount = pickPrice(price2);
|
|
2565
2565
|
if (amount != null) result.price = amount;
|
|
2566
2566
|
if (resp.currency) result.currency = resp.currency;
|
|
2567
|
-
if (
|
|
2568
|
-
result.period =
|
|
2567
|
+
if (price2?.duration_unit === "y" && price2.min_duration != null) {
|
|
2568
|
+
result.period = price2.min_duration;
|
|
2569
2569
|
}
|
|
2570
2570
|
results.push(result);
|
|
2571
2571
|
}
|
|
@@ -3003,9 +3003,9 @@ function fromGandiContact(c) {
|
|
|
3003
3003
|
country: c.country ?? ""
|
|
3004
3004
|
};
|
|
3005
3005
|
}
|
|
3006
|
-
function pickPrice(
|
|
3007
|
-
if (!
|
|
3008
|
-
const value =
|
|
3006
|
+
function pickPrice(price2) {
|
|
3007
|
+
if (!price2) return void 0;
|
|
3008
|
+
const value = price2.price_after_taxes ?? price2.price_before_taxes;
|
|
3009
3009
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3010
3010
|
}
|
|
3011
3011
|
function parseDsRecord(ds) {
|
|
@@ -4089,13 +4089,13 @@ var NameBrightRegistrar = class extends BaseRegistrar {
|
|
|
4089
4089
|
{ path: `purchase/availability/${encodeURIComponent(domainName)}` },
|
|
4090
4090
|
opts
|
|
4091
4091
|
);
|
|
4092
|
-
const
|
|
4092
|
+
const price2 = res.Promotion?.PromotionPrice ?? res.UnitPrice;
|
|
4093
4093
|
const result = {
|
|
4094
4094
|
domainName: res.DomainName ?? domainName,
|
|
4095
4095
|
available: res.Status === "AvailableForRegistration"
|
|
4096
4096
|
};
|
|
4097
|
-
if (typeof
|
|
4098
|
-
result.price =
|
|
4097
|
+
if (typeof price2 === "number" && Number.isFinite(price2) && price2 > 0) {
|
|
4098
|
+
result.price = price2;
|
|
4099
4099
|
result.currency = "USD";
|
|
4100
4100
|
}
|
|
4101
4101
|
results.push(result);
|
|
@@ -4417,7 +4417,10 @@ var NamecheapRegistrar = class extends BaseRegistrar {
|
|
|
4417
4417
|
static configFields = [
|
|
4418
4418
|
{ name: "username", label: "Username", type: "text", required: true },
|
|
4419
4419
|
{ name: "apiKey", label: "API Key", type: "password", required: true },
|
|
4420
|
-
|
|
4420
|
+
// Required: Namecheap rejects any request whose ClientIp isn't on the
|
|
4421
|
+
// account's whitelist, so a placeholder like 0.0.0.0 only defers the
|
|
4422
|
+
// failure to the first call. Make the user supply the whitelisted IP.
|
|
4423
|
+
{ name: "clientIp", label: "Client IP", type: "text", required: true }
|
|
4421
4424
|
];
|
|
4422
4425
|
// Namecheap runs a sandbox at api.sandbox.namecheap.com (separate account at
|
|
4423
4426
|
// sandbox.namecheap.com with its own API key)
|
|
@@ -4925,9 +4928,9 @@ function splitDomain(domain) {
|
|
|
4925
4928
|
const dot = domain.indexOf(".");
|
|
4926
4929
|
return dot === -1 ? { sld: domain, tld: "" } : { sld: domain.slice(0, dot), tld: domain.slice(dot + 1) };
|
|
4927
4930
|
}
|
|
4928
|
-
function toPrice2(
|
|
4929
|
-
if (!
|
|
4930
|
-
const n = Number(
|
|
4931
|
+
function toPrice2(price2) {
|
|
4932
|
+
if (!price2?.["@_Price"]) return void 0;
|
|
4933
|
+
const n = Number(price2["@_Price"]);
|
|
4931
4934
|
return Number.isFinite(n) ? n : void 0;
|
|
4932
4935
|
}
|
|
4933
4936
|
function fromNcContact(c) {
|
|
@@ -4966,6 +4969,501 @@ function toNcContactParams(prefix, c) {
|
|
|
4966
4969
|
return params;
|
|
4967
4970
|
}
|
|
4968
4971
|
|
|
4972
|
+
// src/registrars/namecom.ts
|
|
4973
|
+
var NamecomHttpClient = class extends HttpClient {
|
|
4974
|
+
async request(req) {
|
|
4975
|
+
try {
|
|
4976
|
+
return await super.request(req);
|
|
4977
|
+
} catch (error) {
|
|
4978
|
+
if (error instanceof ParsingError) throw new ParsingError("namecom: invalid JSON response");
|
|
4979
|
+
if (error instanceof ConnectionError)
|
|
4980
|
+
throw new ConnectionError("namecom: network request failed");
|
|
4981
|
+
throw error;
|
|
4982
|
+
}
|
|
4983
|
+
}
|
|
4984
|
+
async toStatusError(response, url) {
|
|
4985
|
+
await response.body?.cancel().catch(() => void 0);
|
|
4986
|
+
const error = await super.toStatusError(
|
|
4987
|
+
new Response(null, {
|
|
4988
|
+
status: response.status,
|
|
4989
|
+
headers: response.headers
|
|
4990
|
+
}),
|
|
4991
|
+
url
|
|
4992
|
+
);
|
|
4993
|
+
if (error instanceof RateLimitError) {
|
|
4994
|
+
const retryAfter = response.headers.get("retry-after");
|
|
4995
|
+
const seconds = retryAfter == null ? NaN : Number(retryAfter);
|
|
4996
|
+
const date = retryAfter == null ? NaN : Date.parse(retryAfter);
|
|
4997
|
+
const reset = response.headers.get("x-ratelimit-reset");
|
|
4998
|
+
error.retryAfter = Number.isFinite(seconds) ? Math.max(0, seconds) : Number.isFinite(date) ? Math.max(0, (date - Date.now()) / 1e3) : reset != null && Number.isFinite(Number(reset)) ? Math.max(0, Number(reset) - Date.now() / 1e3) : void 0;
|
|
4999
|
+
}
|
|
5000
|
+
return error;
|
|
5001
|
+
}
|
|
5002
|
+
};
|
|
5003
|
+
var NamecomRegistrar = class extends BaseRegistrar {
|
|
5004
|
+
name = "namecom";
|
|
5005
|
+
static displayName = "Name.com";
|
|
5006
|
+
static website = "name.com";
|
|
5007
|
+
static supportsSandbox = true;
|
|
5008
|
+
static configFields = [
|
|
5009
|
+
{ name: "username", label: "Username", type: "text", required: true },
|
|
5010
|
+
{ name: "apiToken", label: "API token", type: "password", required: true }
|
|
5011
|
+
];
|
|
5012
|
+
static helpText = "Get an API token at https://www.name.com/account/settings/api. Accounts with 2FA must enable API Access. Sandbox requires your -test username and a separate sandbox token; select the sandbox environment explicitly.";
|
|
5013
|
+
static extendedFeatures = [Feature.GetAuthCode];
|
|
5014
|
+
requiresNameserversFetch = false;
|
|
5015
|
+
constructor(credentials, options) {
|
|
5016
|
+
const username = credentials.username ?? "";
|
|
5017
|
+
if (username.includes(":"))
|
|
5018
|
+
throw new ConfigurationError("namecom: username cannot contain a colon");
|
|
5019
|
+
const auth = btoa(
|
|
5020
|
+
Array.from(
|
|
5021
|
+
new TextEncoder().encode(`${username}:${credentials.apiToken ?? ""}`),
|
|
5022
|
+
(byte) => String.fromCharCode(byte)
|
|
5023
|
+
).join("")
|
|
5024
|
+
);
|
|
5025
|
+
const config = {
|
|
5026
|
+
baseUrl: selectBaseUrl("namecom", options?.environment, {
|
|
5027
|
+
production: "https://api.name.com/core/v1",
|
|
5028
|
+
sandbox: "https://api.dev.name.com/core/v1"
|
|
5029
|
+
}),
|
|
5030
|
+
headers: { Authorization: `Basic ${auth}` }
|
|
5031
|
+
};
|
|
5032
|
+
super(credentials, config, options);
|
|
5033
|
+
this.http = new NamecomHttpClient({ ...config, options: this.options });
|
|
5034
|
+
}
|
|
5035
|
+
async testConnection(opts) {
|
|
5036
|
+
try {
|
|
5037
|
+
await this.http.request({ path: "/domains", query: { perPage: 1, page: 1 }, ...opts });
|
|
5038
|
+
return { success: true, message: "Connection successful" };
|
|
5039
|
+
} catch (error) {
|
|
5040
|
+
return { success: false, message: toRegistrarError(error).message };
|
|
5041
|
+
}
|
|
5042
|
+
}
|
|
5043
|
+
async listDomains(opts) {
|
|
5044
|
+
const { search, detailed: _detailed, ...requestOpts } = opts ?? {};
|
|
5045
|
+
const domains = await this.pages("/domains", "domains", requestOpts);
|
|
5046
|
+
return filterDomains(
|
|
5047
|
+
domains.map((d) => this.toDomain(d)),
|
|
5048
|
+
search
|
|
5049
|
+
);
|
|
5050
|
+
}
|
|
5051
|
+
async getDomain(domainName, opts) {
|
|
5052
|
+
return this.toDomain(await this.rawDomain(domainName, opts));
|
|
5053
|
+
}
|
|
5054
|
+
async getNameservers(domainName, opts) {
|
|
5055
|
+
return (await this.getDomain(domainName, opts)).nameservers;
|
|
5056
|
+
}
|
|
5057
|
+
async getAuthCode(domainName, opts) {
|
|
5058
|
+
const result = await this.http.request({
|
|
5059
|
+
path: `${domainPath(domainName)}:getAuthCode`,
|
|
5060
|
+
...opts
|
|
5061
|
+
});
|
|
5062
|
+
if (!result?.authCode) throw new InvalidResponseError("namecom: missing authorization code");
|
|
5063
|
+
return result.authCode;
|
|
5064
|
+
}
|
|
5065
|
+
async checkAvailability(domainNames, opts) {
|
|
5066
|
+
const results = [];
|
|
5067
|
+
for (let i = 0; i < domainNames.length; i += 50) {
|
|
5068
|
+
const res = await this.http.request({
|
|
5069
|
+
method: "POST",
|
|
5070
|
+
path: "/domains:checkAvailability",
|
|
5071
|
+
body: {
|
|
5072
|
+
domainNames: domainNames.slice(i, i + 50).map(normalizeDomain),
|
|
5073
|
+
purchaseType: "registration"
|
|
5074
|
+
},
|
|
5075
|
+
...opts
|
|
5076
|
+
});
|
|
5077
|
+
if (!Array.isArray(res?.results))
|
|
5078
|
+
throw new InvalidResponseError("namecom: missing availability results");
|
|
5079
|
+
results.push(
|
|
5080
|
+
...res.results.map((r) => ({
|
|
5081
|
+
domainName: r.domainName,
|
|
5082
|
+
available: (r.purchasable ?? false) && (!r.purchaseType || r.purchaseType === "registration"),
|
|
5083
|
+
premium: r.premium ?? false,
|
|
5084
|
+
price: price(r.purchasePrice),
|
|
5085
|
+
currency: "USD"
|
|
5086
|
+
// Discovery quotes the TLD's minimum term, which isn't always one year.
|
|
5087
|
+
}))
|
|
5088
|
+
);
|
|
5089
|
+
}
|
|
5090
|
+
return results;
|
|
5091
|
+
}
|
|
5092
|
+
async getPricing(tldOrDomain, opts) {
|
|
5093
|
+
const name = normalizeDomain(tldOrDomain);
|
|
5094
|
+
if (name.startsWith(".") || !name.includes(".")) {
|
|
5095
|
+
const tld = name.replace(/^\./, "");
|
|
5096
|
+
const entries = await this.pages("/tldpricing", "pricing", opts, {
|
|
5097
|
+
tlds: tld,
|
|
5098
|
+
duration: 1
|
|
5099
|
+
});
|
|
5100
|
+
const entry = entries.find((p) => p.tld.toLowerCase() === tld && p.duration === 1);
|
|
5101
|
+
if (!entry) throw new NotFoundError(`namecom: no one-year pricing for .${tld}`);
|
|
5102
|
+
return {
|
|
5103
|
+
tld,
|
|
5104
|
+
currency: "USD",
|
|
5105
|
+
registration: price(entry.registrationPrice),
|
|
5106
|
+
renewal: price(entry.renewalPrice),
|
|
5107
|
+
transfer: price(entry.transferInPrice)
|
|
5108
|
+
};
|
|
5109
|
+
}
|
|
5110
|
+
const result = await this.domainPricing(name, 1, opts);
|
|
5111
|
+
return {
|
|
5112
|
+
tld: name.slice(name.indexOf(".") + 1),
|
|
5113
|
+
currency: "USD",
|
|
5114
|
+
registration: price(result.purchasePrice),
|
|
5115
|
+
renewal: price(result.renewalPrice),
|
|
5116
|
+
transfer: price(result.transferPrice)
|
|
5117
|
+
};
|
|
5118
|
+
}
|
|
5119
|
+
async registerDomain(domainName, input, opts) {
|
|
5120
|
+
const years = validYears(input.years ?? 1);
|
|
5121
|
+
if (!input.contacts.registrant)
|
|
5122
|
+
throw new ConfigurationError("namecom: a registrant contact is required");
|
|
5123
|
+
const contacts = toContacts(input.contacts);
|
|
5124
|
+
const availability = (await this.checkAvailability([domainName], opts)).find(
|
|
5125
|
+
(result) => normalizeDomain(result.domainName) === normalizeDomain(domainName)
|
|
5126
|
+
);
|
|
5127
|
+
if (!availability?.available)
|
|
5128
|
+
return { success: false, message: "namecom: domain is not available for registration" };
|
|
5129
|
+
const pricing = await this.domainPricing(domainName, years, opts);
|
|
5130
|
+
const purchasePrice = purchaseQuote(pricing, "purchasePrice", availability.premium);
|
|
5131
|
+
return this.purchase(
|
|
5132
|
+
"/domains",
|
|
5133
|
+
{
|
|
5134
|
+
domain: {
|
|
5135
|
+
domainName: normalizeDomain(domainName),
|
|
5136
|
+
contacts,
|
|
5137
|
+
nameservers: input.nameservers,
|
|
5138
|
+
privacyEnabled: input.privacy,
|
|
5139
|
+
autorenewEnabled: input.autoRenew
|
|
5140
|
+
},
|
|
5141
|
+
purchaseType: "registration",
|
|
5142
|
+
years,
|
|
5143
|
+
purchasePrice
|
|
5144
|
+
},
|
|
5145
|
+
domainName,
|
|
5146
|
+
"registration",
|
|
5147
|
+
opts
|
|
5148
|
+
);
|
|
5149
|
+
}
|
|
5150
|
+
async renewDomain(domainName, years = 1, opts) {
|
|
5151
|
+
validYears(years);
|
|
5152
|
+
const pricing = await this.domainPricing(domainName, years, opts);
|
|
5153
|
+
return this.purchase(
|
|
5154
|
+
`${domainPath(domainName)}:renew`,
|
|
5155
|
+
{
|
|
5156
|
+
years,
|
|
5157
|
+
purchasePrice: purchaseQuote(pricing, "renewalPrice")
|
|
5158
|
+
},
|
|
5159
|
+
domainName,
|
|
5160
|
+
"renewal",
|
|
5161
|
+
opts
|
|
5162
|
+
);
|
|
5163
|
+
}
|
|
5164
|
+
async transferIn(domainName, input, opts) {
|
|
5165
|
+
if (!input.authCode) throw new ConfigurationError("namecom: an authorization code is required");
|
|
5166
|
+
if (input.years != null || input.contacts != null || input.autoRenew != null) {
|
|
5167
|
+
throw new ConfigurationError(
|
|
5168
|
+
"namecom: transferIn does not accept years, contacts or autoRenew; the registry determines the term"
|
|
5169
|
+
);
|
|
5170
|
+
}
|
|
5171
|
+
const pricing = await this.domainPricing(domainName, void 0, opts);
|
|
5172
|
+
return this.purchase(
|
|
5173
|
+
"/transfers",
|
|
5174
|
+
{
|
|
5175
|
+
domainName: normalizeDomain(domainName),
|
|
5176
|
+
authCode: input.authCode,
|
|
5177
|
+
privacyEnabled: input.privacy,
|
|
5178
|
+
purchasePrice: purchaseQuote(pricing, "transferPrice")
|
|
5179
|
+
},
|
|
5180
|
+
domainName,
|
|
5181
|
+
"transfer",
|
|
5182
|
+
opts
|
|
5183
|
+
);
|
|
5184
|
+
}
|
|
5185
|
+
setAutoRenew(domainName, enabled, opts) {
|
|
5186
|
+
return this.patchDomain(domainName, { autorenewEnabled: enabled }, opts);
|
|
5187
|
+
}
|
|
5188
|
+
lockDomain(domainName, opts) {
|
|
5189
|
+
return this.patchDomain(domainName, { locked: true }, opts);
|
|
5190
|
+
}
|
|
5191
|
+
unlockDomain(domainName, opts) {
|
|
5192
|
+
return this.patchDomain(domainName, { locked: false }, opts);
|
|
5193
|
+
}
|
|
5194
|
+
setPrivacy(domainName, enabled, opts) {
|
|
5195
|
+
return this.patchDomain(domainName, { privacyEnabled: enabled }, opts);
|
|
5196
|
+
}
|
|
5197
|
+
updateNameservers(domainName, nameservers, opts) {
|
|
5198
|
+
return this.mutate(
|
|
5199
|
+
{ method: "POST", path: `${domainPath(domainName)}:setNameservers`, body: { nameservers } },
|
|
5200
|
+
opts
|
|
5201
|
+
);
|
|
5202
|
+
}
|
|
5203
|
+
async getContacts(domainName, opts) {
|
|
5204
|
+
const result = {};
|
|
5205
|
+
const raw = await this.rawDomain(domainName, opts);
|
|
5206
|
+
for (const role of CONTACT_ROLES) {
|
|
5207
|
+
const c = raw.contacts?.[role];
|
|
5208
|
+
if (c)
|
|
5209
|
+
result[role] = {
|
|
5210
|
+
firstName: c.firstName ?? "",
|
|
5211
|
+
lastName: c.lastName ?? "",
|
|
5212
|
+
organization: c.companyName ?? void 0,
|
|
5213
|
+
email: c.email ?? "",
|
|
5214
|
+
phone: c.phone ?? "",
|
|
5215
|
+
fax: c.fax ?? void 0,
|
|
5216
|
+
address1: c.address1 ?? "",
|
|
5217
|
+
address2: c.address2 ?? void 0,
|
|
5218
|
+
city: c.city ?? "",
|
|
5219
|
+
state: c.state ?? void 0,
|
|
5220
|
+
postalCode: c.zip ?? "",
|
|
5221
|
+
country: c.country ?? ""
|
|
5222
|
+
};
|
|
5223
|
+
}
|
|
5224
|
+
return result;
|
|
5225
|
+
}
|
|
5226
|
+
updateContacts(domainName, contacts, opts) {
|
|
5227
|
+
return this.mutate(
|
|
5228
|
+
{
|
|
5229
|
+
method: "POST",
|
|
5230
|
+
path: `${domainPath(domainName)}:setContacts`,
|
|
5231
|
+
body: { contacts: toContacts(contacts) }
|
|
5232
|
+
},
|
|
5233
|
+
opts
|
|
5234
|
+
);
|
|
5235
|
+
}
|
|
5236
|
+
async getDnsRecords(domainName, opts) {
|
|
5237
|
+
return (await this.pages(`${domainPath(domainName)}/records`, "records", opts)).map(fromRecord);
|
|
5238
|
+
}
|
|
5239
|
+
// No bulk replace endpoint. Reconcile by id: preserve identical records, update
|
|
5240
|
+
// existing type/host pairs, create additions, then delete stale records. This
|
|
5241
|
+
// avoids duplicate-record errors and does not empty the zone before creation.
|
|
5242
|
+
async setDnsRecords(domainName, records, opts) {
|
|
5243
|
+
const desired = records.map(toRecord).filter((record, index, all) => all.findIndex((other) => sameRecord(other, record)) === index);
|
|
5244
|
+
try {
|
|
5245
|
+
const path = `${domainPath(domainName)}/records`;
|
|
5246
|
+
const existing = await this.pages(path, "records", opts);
|
|
5247
|
+
if (existing.some((r) => !Number.isInteger(r.id) || r.id <= 0))
|
|
5248
|
+
throw new InvalidResponseError("namecom: DNS response missing record ids");
|
|
5249
|
+
const remaining = [...existing];
|
|
5250
|
+
const pending = [];
|
|
5251
|
+
for (const record of desired) {
|
|
5252
|
+
const index = remaining.findIndex((r) => sameRecord(r, record));
|
|
5253
|
+
if (index >= 0) remaining.splice(index, 1);
|
|
5254
|
+
else if (!pending.some((r) => sameRecord(r, record))) pending.push(record);
|
|
5255
|
+
}
|
|
5256
|
+
for (const record of pending) {
|
|
5257
|
+
const index = remaining.findIndex((r) => recordKey(r) === recordKey(record));
|
|
5258
|
+
const old = index >= 0 ? remaining.splice(index, 1)[0] : void 0;
|
|
5259
|
+
await this.http.request({
|
|
5260
|
+
...opts,
|
|
5261
|
+
retries: 0,
|
|
5262
|
+
method: old ? "PUT" : "POST",
|
|
5263
|
+
path: old ? `${path}/${old.id}` : path,
|
|
5264
|
+
body: record
|
|
5265
|
+
});
|
|
5266
|
+
}
|
|
5267
|
+
for (const old of remaining) {
|
|
5268
|
+
await this.http.request({
|
|
5269
|
+
...opts,
|
|
5270
|
+
retries: 0,
|
|
5271
|
+
method: "DELETE",
|
|
5272
|
+
path: `${path}/${old.id}`
|
|
5273
|
+
});
|
|
5274
|
+
}
|
|
5275
|
+
return { success: true, message: "DNS records updated successfully" };
|
|
5276
|
+
} catch (error) {
|
|
5277
|
+
return {
|
|
5278
|
+
success: false,
|
|
5279
|
+
message: `${toRegistrarError(error).message}. DNS replacement is not atomic; read the zone before retrying.`
|
|
5280
|
+
};
|
|
5281
|
+
}
|
|
5282
|
+
}
|
|
5283
|
+
patchDomain(domainName, body, opts) {
|
|
5284
|
+
return this.mutate({ method: "PATCH", path: domainPath(domainName), body }, opts);
|
|
5285
|
+
}
|
|
5286
|
+
rawDomain(domainName, opts) {
|
|
5287
|
+
return this.http.request({ path: domainPath(domainName), ...opts });
|
|
5288
|
+
}
|
|
5289
|
+
domainPricing(domainName, years, opts) {
|
|
5290
|
+
return this.http.request({
|
|
5291
|
+
path: `${domainPath(domainName)}:getPricing`,
|
|
5292
|
+
query: { years },
|
|
5293
|
+
...opts
|
|
5294
|
+
});
|
|
5295
|
+
}
|
|
5296
|
+
toDomain(d) {
|
|
5297
|
+
if (!d?.domainName)
|
|
5298
|
+
throw new InvalidResponseError("namecom: domain response missing domainName");
|
|
5299
|
+
return createDomain({
|
|
5300
|
+
domainName: normalizeDomain(d.domainName),
|
|
5301
|
+
registrar: this.name,
|
|
5302
|
+
// Core exposes locks, not a lifecycle status or a scheduled renewal date.
|
|
5303
|
+
status: (d.locks ?? []).join(","),
|
|
5304
|
+
createdDate: d.createDate,
|
|
5305
|
+
expirationDate: d.expireDate,
|
|
5306
|
+
autoRenew: d.autorenewEnabled ?? false,
|
|
5307
|
+
locked: d.locked ?? false,
|
|
5308
|
+
privacy: d.privacyEnabled ?? false,
|
|
5309
|
+
nameservers: d.nameservers
|
|
5310
|
+
});
|
|
5311
|
+
}
|
|
5312
|
+
async pages(path, key, opts, query) {
|
|
5313
|
+
const result = [];
|
|
5314
|
+
let page = 1;
|
|
5315
|
+
while (true) {
|
|
5316
|
+
const res = await this.http.request({
|
|
5317
|
+
path,
|
|
5318
|
+
query: { perPage: 1e3, page, ...query },
|
|
5319
|
+
...opts
|
|
5320
|
+
});
|
|
5321
|
+
const items = res?.[key];
|
|
5322
|
+
if (!res || items != null && !Array.isArray(items))
|
|
5323
|
+
throw new InvalidResponseError("namecom: invalid list response");
|
|
5324
|
+
result.push(...items ?? []);
|
|
5325
|
+
const next = res.nextPage;
|
|
5326
|
+
if (next == null || next === 0) return result;
|
|
5327
|
+
if (!Number.isInteger(next) || next <= page)
|
|
5328
|
+
throw new InvalidResponseError("namecom: invalid pagination cursor");
|
|
5329
|
+
page = next;
|
|
5330
|
+
}
|
|
5331
|
+
}
|
|
5332
|
+
async mutate(req, opts) {
|
|
5333
|
+
try {
|
|
5334
|
+
await this.http.request({ ...req, ...opts, retries: 0 });
|
|
5335
|
+
return { success: true, message: "Domain updated successfully" };
|
|
5336
|
+
} catch (error) {
|
|
5337
|
+
return { success: false, message: toRegistrarError(error).message };
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5340
|
+
async purchase(path, body, domainName, operation, opts) {
|
|
5341
|
+
try {
|
|
5342
|
+
const res = await this.http.request({
|
|
5343
|
+
method: "POST",
|
|
5344
|
+
path,
|
|
5345
|
+
body,
|
|
5346
|
+
...opts,
|
|
5347
|
+
retries: 0
|
|
5348
|
+
});
|
|
5349
|
+
const returnedName = res?.domain?.domainName ?? res?.transfer?.domainName;
|
|
5350
|
+
if (!Number.isInteger(res?.order) || (res.order ?? 0) <= 0 || !returnedName || normalizeDomain(returnedName) !== normalizeDomain(domainName)) {
|
|
5351
|
+
throw new InvalidResponseError(
|
|
5352
|
+
"namecom: purchase response missing order or matching domain"
|
|
5353
|
+
);
|
|
5354
|
+
}
|
|
5355
|
+
return {
|
|
5356
|
+
success: true,
|
|
5357
|
+
message: `Domain ${operation} request accepted (order ${res.order}); verify final domain/transfer status`
|
|
5358
|
+
};
|
|
5359
|
+
} catch (error) {
|
|
5360
|
+
const e = toRegistrarError(error);
|
|
5361
|
+
const uncertain = e.status < 0 || e.status === 408 || e.status >= 500 || e.name === "AbortError" || e instanceof ParsingError;
|
|
5362
|
+
return {
|
|
5363
|
+
success: false,
|
|
5364
|
+
message: e.message + (uncertain ? ". Outcome unknown; check Name.com orders and domain/transfer status before retrying." : "")
|
|
5365
|
+
};
|
|
5366
|
+
}
|
|
5367
|
+
}
|
|
5368
|
+
};
|
|
5369
|
+
var CONTACT_ROLES = ["registrant", "admin", "tech", "billing"];
|
|
5370
|
+
function toContacts(contacts) {
|
|
5371
|
+
const result = {};
|
|
5372
|
+
for (const role of CONTACT_ROLES) {
|
|
5373
|
+
const c = contacts[role];
|
|
5374
|
+
if (!c) continue;
|
|
5375
|
+
const required = [
|
|
5376
|
+
"firstName",
|
|
5377
|
+
"lastName",
|
|
5378
|
+
"email",
|
|
5379
|
+
"phone",
|
|
5380
|
+
"address1",
|
|
5381
|
+
"city",
|
|
5382
|
+
"state",
|
|
5383
|
+
"postalCode",
|
|
5384
|
+
"country"
|
|
5385
|
+
];
|
|
5386
|
+
if (required.some((k) => !c[k]?.trim()))
|
|
5387
|
+
throw new ConfigurationError(`namecom: ${role} must be a complete contact, including state`);
|
|
5388
|
+
result[role] = {
|
|
5389
|
+
firstName: c.firstName,
|
|
5390
|
+
lastName: c.lastName,
|
|
5391
|
+
companyName: c.organization,
|
|
5392
|
+
email: c.email,
|
|
5393
|
+
phone: c.phone.replace(".", ""),
|
|
5394
|
+
fax: c.fax?.replace(".", ""),
|
|
5395
|
+
address1: c.address1,
|
|
5396
|
+
address2: c.address2,
|
|
5397
|
+
city: c.city,
|
|
5398
|
+
state: c.state,
|
|
5399
|
+
zip: c.postalCode,
|
|
5400
|
+
country: c.country.toUpperCase()
|
|
5401
|
+
};
|
|
5402
|
+
}
|
|
5403
|
+
if (Object.keys(result).length === 0)
|
|
5404
|
+
throw new ConfigurationError("namecom: at least one contact is required");
|
|
5405
|
+
return result;
|
|
5406
|
+
}
|
|
5407
|
+
function domainPath(domainName) {
|
|
5408
|
+
return `/domains/${encodeURIComponent(normalizeDomain(domainName))}`;
|
|
5409
|
+
}
|
|
5410
|
+
function validYears(years) {
|
|
5411
|
+
if (!Number.isInteger(years) || years < 1 || years > 10)
|
|
5412
|
+
throw new ConfigurationError("namecom: years must be an integer from 1 to 10");
|
|
5413
|
+
return years;
|
|
5414
|
+
}
|
|
5415
|
+
function price(value) {
|
|
5416
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
|
|
5417
|
+
}
|
|
5418
|
+
function purchaseQuote(pricing, field, premium = false) {
|
|
5419
|
+
const value = price(pricing?.[field]);
|
|
5420
|
+
if (value == null)
|
|
5421
|
+
throw new RegistrarError(
|
|
5422
|
+
`namecom: ${field} unavailable for the requested term; no purchase submitted`
|
|
5423
|
+
);
|
|
5424
|
+
return premium || pricing.premium ? value : void 0;
|
|
5425
|
+
}
|
|
5426
|
+
var DNS_TYPES = /* @__PURE__ */ new Set(["A", "AAAA", "ANAME", "CNAME", "MX", "NS", "SRV", "TXT"]);
|
|
5427
|
+
function toRecord(r) {
|
|
5428
|
+
const type = r.type.toUpperCase();
|
|
5429
|
+
if (!DNS_TYPES.has(type))
|
|
5430
|
+
throw new ConfigurationError(`namecom: DNS type ${type} is not supported`);
|
|
5431
|
+
const ttl = r.ttl ?? 300;
|
|
5432
|
+
if (!Number.isInteger(ttl) || ttl < 300)
|
|
5433
|
+
throw new ConfigurationError("namecom: DNS TTL must be an integer of at least 300 seconds");
|
|
5434
|
+
for (const value of [r.priority, r.weight, r.port]) {
|
|
5435
|
+
if (value != null && (!Number.isInteger(value) || value < 0 || value > 65535))
|
|
5436
|
+
throw new ConfigurationError("namecom: invalid DNS priority, weight or port");
|
|
5437
|
+
}
|
|
5438
|
+
if (!r.value.trim()) throw new ConfigurationError("namecom: DNS answer is required");
|
|
5439
|
+
return {
|
|
5440
|
+
type,
|
|
5441
|
+
host: !r.name || r.name === "@" ? "" : r.name,
|
|
5442
|
+
answer: type === "SRV" ? `${r.weight ?? 0} ${r.port ?? 0} ${r.value}` : r.value,
|
|
5443
|
+
ttl,
|
|
5444
|
+
...["MX", "SRV"].includes(type) ? { priority: r.priority ?? 0 } : {}
|
|
5445
|
+
};
|
|
5446
|
+
}
|
|
5447
|
+
function fromRecord(r) {
|
|
5448
|
+
const type = r.type.toUpperCase();
|
|
5449
|
+
const record = { type, name: r.host || "@", value: r.answer, ttl: r.ttl };
|
|
5450
|
+
if (type === "MX" || type === "SRV") record.priority = r.priority ?? 0;
|
|
5451
|
+
if (type === "SRV") {
|
|
5452
|
+
const match = /^(\d+)\s+(\d+)\s+(.+)$/.exec(r.answer);
|
|
5453
|
+
if (!match) throw new InvalidResponseError("namecom: invalid SRV answer");
|
|
5454
|
+
record.weight = Number(match[1]);
|
|
5455
|
+
record.port = Number(match[2]);
|
|
5456
|
+
record.value = match[3];
|
|
5457
|
+
}
|
|
5458
|
+
return record;
|
|
5459
|
+
}
|
|
5460
|
+
function recordKey(r) {
|
|
5461
|
+
return `${r.type.toUpperCase()}\0${(r.host === "@" ? "" : r.host ?? "").toLowerCase()}`;
|
|
5462
|
+
}
|
|
5463
|
+
function sameRecord(a, b) {
|
|
5464
|
+
return recordKey(a) === recordKey(b) && a.answer === b.answer && (a.ttl ?? 300) === (b.ttl ?? 300) && (a.priority ?? 0) === (b.priority ?? 0);
|
|
5465
|
+
}
|
|
5466
|
+
|
|
4969
5467
|
// src/registrars/namesilo.ts
|
|
4970
5468
|
var NameSiloRegistrar = class extends BaseRegistrar {
|
|
4971
5469
|
name = "namesilo";
|
|
@@ -5156,13 +5654,13 @@ var NameSiloRegistrar = class extends BaseRegistrar {
|
|
|
5156
5654
|
if (!node || typeof node !== "object") {
|
|
5157
5655
|
throw new NotFoundError(`NameSilo: no pricing found for TLD '${tld}'`);
|
|
5158
5656
|
}
|
|
5159
|
-
const
|
|
5657
|
+
const price2 = node;
|
|
5160
5658
|
return {
|
|
5161
5659
|
tld,
|
|
5162
5660
|
currency: "USD",
|
|
5163
|
-
registration: toPrice3(
|
|
5164
|
-
renewal: toPrice3(
|
|
5165
|
-
transfer: toPrice3(
|
|
5661
|
+
registration: toPrice3(price2.registration),
|
|
5662
|
+
renewal: toPrice3(price2.renew),
|
|
5663
|
+
transfer: toPrice3(price2.transfer)
|
|
5166
5664
|
};
|
|
5167
5665
|
}
|
|
5168
5666
|
/**
|
|
@@ -5579,8 +6077,8 @@ function extractAvailEntries(group) {
|
|
|
5579
6077
|
const premiumRaw = o.premium ?? o["@premium"] ?? o["@_premium"];
|
|
5580
6078
|
const durationRaw = o.duration ?? o["@duration"] ?? o["@_duration"];
|
|
5581
6079
|
const entry = { name };
|
|
5582
|
-
const
|
|
5583
|
-
if (
|
|
6080
|
+
const price2 = toPrice3(priceRaw);
|
|
6081
|
+
if (price2 != null && price2 > 0) entry.price = price2;
|
|
5584
6082
|
if (premiumRaw != null && text(premiumRaw) !== "") entry.premium = isYes(premiumRaw);
|
|
5585
6083
|
const duration = toPrice3(durationRaw);
|
|
5586
6084
|
if (duration != null && duration > 0) entry.duration = duration;
|
|
@@ -5824,17 +6322,17 @@ var PorkbunRegistrar = class extends BaseRegistrar {
|
|
|
5824
6322
|
async tldPricing(tld, opts) {
|
|
5825
6323
|
const res = await this.call("/pricing/get", {}, opts);
|
|
5826
6324
|
if (!isOk3(res)) throw new Error(res.message ?? "API request failed");
|
|
5827
|
-
const
|
|
5828
|
-
if (!
|
|
6325
|
+
const price2 = res.pricing?.[tld];
|
|
6326
|
+
if (!price2) {
|
|
5829
6327
|
throw new NotFoundError(`${this.name}: no pricing found for TLD .${tld}`);
|
|
5830
6328
|
}
|
|
5831
6329
|
return {
|
|
5832
6330
|
tld,
|
|
5833
6331
|
currency: "USD",
|
|
5834
6332
|
// Porkbun prices are always quoted in USD
|
|
5835
|
-
registration: toPrice4(
|
|
5836
|
-
renewal: toPrice4(
|
|
5837
|
-
transfer: toPrice4(
|
|
6333
|
+
registration: toPrice4(price2.registration),
|
|
6334
|
+
renewal: toPrice4(price2.renewal),
|
|
6335
|
+
transfer: toPrice4(price2.transfer)
|
|
5838
6336
|
};
|
|
5839
6337
|
}
|
|
5840
6338
|
/**
|
|
@@ -5874,14 +6372,14 @@ var PorkbunRegistrar = class extends BaseRegistrar {
|
|
|
5874
6372
|
);
|
|
5875
6373
|
if (!isOk3(res)) throw new Error(res.message ?? "API request failed");
|
|
5876
6374
|
const info = res.response ?? {};
|
|
5877
|
-
const
|
|
6375
|
+
const price2 = toPrice4(info.price);
|
|
5878
6376
|
results.push({
|
|
5879
6377
|
domainName,
|
|
5880
6378
|
available: info.avail === "yes",
|
|
5881
6379
|
premium: info.premium === "yes",
|
|
5882
|
-
price,
|
|
5883
|
-
currency:
|
|
5884
|
-
period:
|
|
6380
|
+
price: price2,
|
|
6381
|
+
currency: price2 != null ? "USD" : void 0,
|
|
6382
|
+
period: price2 != null ? 1 : void 0
|
|
5885
6383
|
});
|
|
5886
6384
|
}
|
|
5887
6385
|
return results;
|
|
@@ -6567,8 +7065,8 @@ var SpaceshipRegistrar = class extends BaseRegistrar {
|
|
|
6567
7065
|
...opts
|
|
6568
7066
|
});
|
|
6569
7067
|
}
|
|
6570
|
-
const keep = new Set(items.map((r) =>
|
|
6571
|
-
const stale = existing.filter((r) => (r.group?.type ?? "custom") === "custom").filter((r) => !keep.has(
|
|
7068
|
+
const keep = new Set(items.map((r) => recordKey2(r.type, r.name)));
|
|
7069
|
+
const stale = existing.filter((r) => (r.group?.type ?? "custom") === "custom").filter((r) => !keep.has(recordKey2(r.type, r.name))).map(({ group: _group, ...rr }) => rr);
|
|
6572
7070
|
if (stale.length > 0) {
|
|
6573
7071
|
await this.http.request({
|
|
6574
7072
|
method: "DELETE",
|
|
@@ -6727,7 +7225,7 @@ function toSpaceshipRecord(r) {
|
|
|
6727
7225
|
}
|
|
6728
7226
|
return record;
|
|
6729
7227
|
}
|
|
6730
|
-
function
|
|
7228
|
+
function recordKey2(type, name) {
|
|
6731
7229
|
return `${(type ?? "").toUpperCase()}\0${name ?? "@"}`;
|
|
6732
7230
|
}
|
|
6733
7231
|
function fromSpaceshipContact(c) {
|
|
@@ -6771,6 +7269,7 @@ var registrars = {
|
|
|
6771
7269
|
godaddy: GoDaddyRegistrar,
|
|
6772
7270
|
namebright: NameBrightRegistrar,
|
|
6773
7271
|
namecheap: NamecheapRegistrar,
|
|
7272
|
+
namecom: NamecomRegistrar,
|
|
6774
7273
|
namesilo: NameSiloRegistrar,
|
|
6775
7274
|
porkbun: PorkbunRegistrar,
|
|
6776
7275
|
spaceship: SpaceshipRegistrar
|
|
@@ -6780,6 +7279,6 @@ function createRegistrar(name, credentials, options) {
|
|
|
6780
7279
|
return new RegistrarClass(credentials, options);
|
|
6781
7280
|
}
|
|
6782
7281
|
|
|
6783
|
-
export { ALL_FEATURES, AbortError, AuthenticationError, AuthorizationError, BaseRegistrar, CORE_FEATURES, CloudflareRegistrar, ConfigurationError, ConnectionError, ConsentRequiredError, DEFAULT_OPTIONS, DynadotRegistrar, EXTENDED_FEATURES, Feature, GandiRegistrar, GoDaddyRegistrar, HttpClient, InvalidResponseError, MASKED_FORWARD_UNSUPPORTED, NameBrightRegistrar, NameSiloRegistrar, NamecheapRegistrar, NotFoundError, NotImplementedError, ParsingError, PorkbunRegistrar, RateLimitError, RegistrarClient, RegistrarError, RetryableRegistrarError, SpaceshipRegistrar, TimeoutError, USER_AGENT, backoffDelay, createDomain, createRegistrar, ensureArray, filterDomains, isCoreFeature, listPortfolio, normalizeDomain, normalizeNameservers, parseDate, parseXml, registrars, requireConsent, selectBaseUrl, settableForwards, sleep, toRegistrarError };
|
|
7282
|
+
export { ALL_FEATURES, AbortError, AuthenticationError, AuthorizationError, BaseRegistrar, CORE_FEATURES, CloudflareRegistrar, ConfigurationError, ConnectionError, ConsentRequiredError, DEFAULT_OPTIONS, DynadotRegistrar, EXTENDED_FEATURES, Feature, GandiRegistrar, GoDaddyRegistrar, HttpClient, InvalidResponseError, MASKED_FORWARD_UNSUPPORTED, NameBrightRegistrar, NameSiloRegistrar, NamecheapRegistrar, NamecomRegistrar, NotFoundError, NotImplementedError, ParsingError, PorkbunRegistrar, RateLimitError, RegistrarClient, RegistrarError, RetryableRegistrarError, SpaceshipRegistrar, TimeoutError, USER_AGENT, backoffDelay, createDomain, createRegistrar, ensureArray, filterDomains, isCoreFeature, listPortfolio, normalizeDomain, normalizeNameservers, parseDate, parseXml, registrars, requireConsent, selectBaseUrl, settableForwards, sleep, toRegistrarError };
|
|
6784
7283
|
//# sourceMappingURL=index.js.map
|
|
6785
7284
|
//# sourceMappingURL=index.js.map
|