@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/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 price = toPrice(c?.pricing?.registration_cost);
1032
- if (price != null) availability.price = price;
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 price = oneYearPrice(r.price_list);
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: price?.registration,
1800
- currency: price?.currency,
1801
- period: price ? 1 : void 0
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 price = oneYearPrice(result?.price_list);
1838
+ const price2 = oneYearPrice(result?.price_list);
1839
1839
  return {
1840
1840
  tld,
1841
- currency: price?.currency ?? "USD",
1842
- registration: price?.registration,
1843
- renewal: price?.renewal,
1844
- transfer: price?.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 price = product?.prices?.[0];
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") || price?.type === "premium"
2564
+ premium: status.includes("premium") || price2?.type === "premium"
2565
2565
  };
2566
- const amount = pickPrice(price);
2566
+ const amount = pickPrice(price2);
2567
2567
  if (amount != null) result.price = amount;
2568
2568
  if (resp.currency) result.currency = resp.currency;
2569
- if (price?.duration_unit === "y" && price.min_duration != null) {
2570
- result.period = price.min_duration;
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(price) {
3009
- if (!price) return void 0;
3010
- const value = price.price_after_taxes ?? price.price_before_taxes;
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 price = res.Promotion?.PromotionPrice ?? res.UnitPrice;
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 price === "number" && Number.isFinite(price) && price > 0) {
4100
- result.price = 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);
@@ -4419,7 +4419,10 @@ var NamecheapRegistrar = class extends BaseRegistrar {
4419
4419
  static configFields = [
4420
4420
  { name: "username", label: "Username", type: "text", required: true },
4421
4421
  { name: "apiKey", label: "API Key", type: "password", required: true },
4422
- { name: "clientIp", label: "Client IP", type: "text", required: false, default: "0.0.0.0" }
4422
+ // Required: Namecheap rejects any request whose ClientIp isn't on the
4423
+ // account's whitelist, so a placeholder like 0.0.0.0 only defers the
4424
+ // failure to the first call. Make the user supply the whitelisted IP.
4425
+ { name: "clientIp", label: "Client IP", type: "text", required: true }
4423
4426
  ];
4424
4427
  // Namecheap runs a sandbox at api.sandbox.namecheap.com (separate account at
4425
4428
  // sandbox.namecheap.com with its own API key)
@@ -4927,9 +4930,9 @@ function splitDomain(domain) {
4927
4930
  const dot = domain.indexOf(".");
4928
4931
  return dot === -1 ? { sld: domain, tld: "" } : { sld: domain.slice(0, dot), tld: domain.slice(dot + 1) };
4929
4932
  }
4930
- function toPrice2(price) {
4931
- if (!price?.["@_Price"]) return void 0;
4932
- const n = Number(price["@_Price"]);
4933
+ function toPrice2(price2) {
4934
+ if (!price2?.["@_Price"]) return void 0;
4935
+ const n = Number(price2["@_Price"]);
4933
4936
  return Number.isFinite(n) ? n : void 0;
4934
4937
  }
4935
4938
  function fromNcContact(c) {
@@ -4968,6 +4971,501 @@ function toNcContactParams(prefix, c) {
4968
4971
  return params;
4969
4972
  }
4970
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
+
4971
5469
  // src/registrars/namesilo.ts
4972
5470
  var NameSiloRegistrar = class extends BaseRegistrar {
4973
5471
  name = "namesilo";
@@ -5158,13 +5656,13 @@ var NameSiloRegistrar = class extends BaseRegistrar {
5158
5656
  if (!node || typeof node !== "object") {
5159
5657
  throw new NotFoundError(`NameSilo: no pricing found for TLD '${tld}'`);
5160
5658
  }
5161
- const price = node;
5659
+ const price2 = node;
5162
5660
  return {
5163
5661
  tld,
5164
5662
  currency: "USD",
5165
- registration: toPrice3(price.registration),
5166
- renewal: toPrice3(price.renew),
5167
- transfer: toPrice3(price.transfer)
5663
+ registration: toPrice3(price2.registration),
5664
+ renewal: toPrice3(price2.renew),
5665
+ transfer: toPrice3(price2.transfer)
5168
5666
  };
5169
5667
  }
5170
5668
  /**
@@ -5581,8 +6079,8 @@ function extractAvailEntries(group) {
5581
6079
  const premiumRaw = o.premium ?? o["@premium"] ?? o["@_premium"];
5582
6080
  const durationRaw = o.duration ?? o["@duration"] ?? o["@_duration"];
5583
6081
  const entry = { name };
5584
- const price = toPrice3(priceRaw);
5585
- if (price != null && price > 0) entry.price = price;
6082
+ const price2 = toPrice3(priceRaw);
6083
+ if (price2 != null && price2 > 0) entry.price = price2;
5586
6084
  if (premiumRaw != null && text(premiumRaw) !== "") entry.premium = isYes(premiumRaw);
5587
6085
  const duration = toPrice3(durationRaw);
5588
6086
  if (duration != null && duration > 0) entry.duration = duration;
@@ -5826,17 +6324,17 @@ var PorkbunRegistrar = class extends BaseRegistrar {
5826
6324
  async tldPricing(tld, opts) {
5827
6325
  const res = await this.call("/pricing/get", {}, opts);
5828
6326
  if (!isOk3(res)) throw new Error(res.message ?? "API request failed");
5829
- const price = res.pricing?.[tld];
5830
- if (!price) {
6327
+ const price2 = res.pricing?.[tld];
6328
+ if (!price2) {
5831
6329
  throw new NotFoundError(`${this.name}: no pricing found for TLD .${tld}`);
5832
6330
  }
5833
6331
  return {
5834
6332
  tld,
5835
6333
  currency: "USD",
5836
6334
  // Porkbun prices are always quoted in USD
5837
- registration: toPrice4(price.registration),
5838
- renewal: toPrice4(price.renewal),
5839
- transfer: toPrice4(price.transfer)
6335
+ registration: toPrice4(price2.registration),
6336
+ renewal: toPrice4(price2.renewal),
6337
+ transfer: toPrice4(price2.transfer)
5840
6338
  };
5841
6339
  }
5842
6340
  /**
@@ -5876,14 +6374,14 @@ var PorkbunRegistrar = class extends BaseRegistrar {
5876
6374
  );
5877
6375
  if (!isOk3(res)) throw new Error(res.message ?? "API request failed");
5878
6376
  const info = res.response ?? {};
5879
- const price = toPrice4(info.price);
6377
+ const price2 = toPrice4(info.price);
5880
6378
  results.push({
5881
6379
  domainName,
5882
6380
  available: info.avail === "yes",
5883
6381
  premium: info.premium === "yes",
5884
- price,
5885
- currency: price != null ? "USD" : void 0,
5886
- period: price != null ? 1 : void 0
6382
+ price: price2,
6383
+ currency: price2 != null ? "USD" : void 0,
6384
+ period: price2 != null ? 1 : void 0
5887
6385
  });
5888
6386
  }
5889
6387
  return results;
@@ -6569,8 +7067,8 @@ var SpaceshipRegistrar = class extends BaseRegistrar {
6569
7067
  ...opts
6570
7068
  });
6571
7069
  }
6572
- const keep = new Set(items.map((r) => recordKey(r.type, r.name)));
6573
- const stale = existing.filter((r) => (r.group?.type ?? "custom") === "custom").filter((r) => !keep.has(recordKey(r.type, r.name))).map(({ group: _group, ...rr }) => rr);
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);
6574
7072
  if (stale.length > 0) {
6575
7073
  await this.http.request({
6576
7074
  method: "DELETE",
@@ -6729,7 +7227,7 @@ function toSpaceshipRecord(r) {
6729
7227
  }
6730
7228
  return record;
6731
7229
  }
6732
- function recordKey(type, name) {
7230
+ function recordKey2(type, name) {
6733
7231
  return `${(type ?? "").toUpperCase()}\0${name ?? "@"}`;
6734
7232
  }
6735
7233
  function fromSpaceshipContact(c) {
@@ -6773,6 +7271,7 @@ var registrars = {
6773
7271
  godaddy: GoDaddyRegistrar,
6774
7272
  namebright: NameBrightRegistrar,
6775
7273
  namecheap: NamecheapRegistrar,
7274
+ namecom: NamecomRegistrar,
6776
7275
  namesilo: NameSiloRegistrar,
6777
7276
  porkbun: PorkbunRegistrar,
6778
7277
  spaceship: SpaceshipRegistrar
@@ -6804,6 +7303,7 @@ exports.MASKED_FORWARD_UNSUPPORTED = MASKED_FORWARD_UNSUPPORTED;
6804
7303
  exports.NameBrightRegistrar = NameBrightRegistrar;
6805
7304
  exports.NameSiloRegistrar = NameSiloRegistrar;
6806
7305
  exports.NamecheapRegistrar = NamecheapRegistrar;
7306
+ exports.NamecomRegistrar = NamecomRegistrar;
6807
7307
  exports.NotFoundError = NotFoundError;
6808
7308
  exports.NotImplementedError = NotImplementedError;
6809
7309
  exports.ParsingError = ParsingError;