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