@ironclads/namecheap-mcp
Version:
MCP server for Namecheap API integration - domain management, DNS, and domain suggestions
848 lines • 39.1 kB
JavaScript
import axios from 'axios';
import * as xml2js from 'xml2js';
import * as fs from 'fs';
import { DomainGenerator } from './domain-generator.js';
export class NamecheapClient {
client;
config;
parser;
constructor(config) {
this.config = config;
const baseURL = config.sandbox
? 'https://api.sandbox.namecheap.com/xml.response'
: 'https://api.namecheap.com/xml.response';
this.client = axios.create({
baseURL,
timeout: 30000,
});
this.parser = new xml2js.Parser({
explicitArray: false,
ignoreAttrs: false,
mergeAttrs: false,
});
}
async makeRequest(command, params = {}) {
const requestParams = {
ApiUser: this.config.apiUser,
ApiKey: this.config.apiKey,
UserName: this.config.username,
Command: command,
ClientIp: this.config.clientIp || '127.0.0.1',
...params,
};
// Enhanced logging
console.error('\n=== NAMECHEAP API REQUEST DEBUG ===');
console.error('Request URL:', this.client.defaults.baseURL);
console.error('Command:', command);
console.error('Request Params:', {
...requestParams,
ApiKey: '***HIDDEN***' // Hide API key in logs
});
console.error('Sandbox Mode:', this.config.sandbox);
try {
// Log the full request details
fs.appendFileSync('/tmp/debug.log', `\n=== ${new Date().toISOString()} ===\n`);
fs.appendFileSync('/tmp/debug.log', `Command: ${command}\n`);
fs.appendFileSync('/tmp/debug.log', `Sandbox: ${this.config.sandbox}\n`);
fs.appendFileSync('/tmp/debug.log', `Request URL: ${this.client.defaults.baseURL}\n`);
fs.appendFileSync('/tmp/debug.log', `Request params: ${JSON.stringify({ ...requestParams, ApiKey: '***' })}\n`);
const response = await this.client.get('', { params: requestParams });
console.error('Response Status:', response.status);
console.error('Response Headers:', response.headers);
console.error('Raw Response Data:', response.data);
fs.appendFileSync('/tmp/debug.log', `Response status: ${response.status}\n`);
fs.appendFileSync('/tmp/debug.log', `Response headers: ${JSON.stringify(response.headers)}\n`);
fs.appendFileSync('/tmp/debug.log', `Raw response data: ${response.data}\n`);
// Parse XML response
const parsedData = await this.parser.parseStringPromise(response.data);
console.error('Parsed XML Data:', JSON.stringify(parsedData, null, 2));
fs.appendFileSync('/tmp/debug.log', `Parsed XML: ${JSON.stringify(parsedData, null, 2)}\n`);
const apiResponse = parsedData.ApiResponse;
if (!apiResponse) {
console.error('ERROR: No ApiResponse in parsed data');
console.error('Available keys:', Object.keys(parsedData));
fs.appendFileSync('/tmp/debug.log', 'ERROR: No ApiResponse in parsed data\n');
fs.appendFileSync('/tmp/debug.log', `Available keys: ${Object.keys(parsedData)}\n`);
throw new Error('Invalid API response structure - no ApiResponse');
}
if (!apiResponse.$) {
console.error('ERROR: No attributes in ApiResponse');
console.error('ApiResponse structure:', Object.keys(apiResponse));
fs.appendFileSync('/tmp/debug.log', 'ERROR: No attributes in ApiResponse\n');
fs.appendFileSync('/tmp/debug.log', `ApiResponse keys: ${Object.keys(apiResponse)}\n`);
throw new Error('Invalid API response structure - no attributes');
}
console.error('API Status:', apiResponse.$.Status);
console.error('API Response Attributes:', apiResponse.$);
fs.appendFileSync('/tmp/debug.log', `API Status: ${apiResponse.$.Status}\n`);
fs.appendFileSync('/tmp/debug.log', `API Attributes: ${JSON.stringify(apiResponse.$)}\n`);
if (apiResponse.$.Status !== 'OK') {
console.error('API Error - Status not OK');
console.error('Full ApiResponse:', apiResponse);
// Enhanced error parsing
const errors = apiResponse.Errors?.Error;
console.error('Raw Errors:', apiResponse.Errors);
console.error('Error Details:', errors);
fs.appendFileSync('/tmp/debug.log', `API Error - Full response: ${JSON.stringify(apiResponse)}\n`);
fs.appendFileSync('/tmp/debug.log', `Raw Errors: ${JSON.stringify(apiResponse.Errors)}\n`);
if (errors) {
let errorMessages = [];
if (Array.isArray(errors)) {
errorMessages = errors.map((e) => {
if (typeof e === 'string')
return e;
if (e._)
return e._;
if (e.$?.Description)
return e.$.Description;
if (e.Description)
return e.Description;
return JSON.stringify(e);
});
}
else if (typeof errors === 'string') {
errorMessages = [errors];
}
else if (errors._) {
errorMessages = [errors._];
}
else if (errors.$?.Description) {
errorMessages = [errors.$.Description];
}
else if (errors.Description) {
errorMessages = [errors.Description];
}
else {
errorMessages = [JSON.stringify(errors)];
}
console.error('Parsed Error Messages:', errorMessages);
fs.appendFileSync('/tmp/debug.log', `Parsed errors: ${JSON.stringify(errorMessages)}\n`);
throw new Error(`Namecheap API error: ${errorMessages.join(', ')}`);
}
else {
// Check for other error indicators
if (apiResponse.RequestedCommand) {
console.error('Requested Command:', apiResponse.RequestedCommand);
}
if (apiResponse.Server) {
console.error('Server:', apiResponse.Server);
}
throw new Error(`Namecheap API error: Status not OK. Full response: ${JSON.stringify(apiResponse)}`);
}
}
console.error('SUCCESS: API call completed successfully');
console.error('CommandResponse:', apiResponse.CommandResponse);
fs.appendFileSync('/tmp/debug.log', 'SUCCESS: Returning CommandResponse\n');
fs.appendFileSync('/tmp/debug.log', `CommandResponse: ${JSON.stringify(apiResponse.CommandResponse)}\n`);
return apiResponse.CommandResponse;
}
catch (error) {
console.error('\n=== API REQUEST ERROR ===');
console.error('Error details:', error);
fs.appendFileSync('/tmp/debug.log', `\nERROR in makeRequest: ${error}\n`);
if (error instanceof Error && error.message.includes('Namecheap API error')) {
throw error;
}
if (axios.isAxiosError(error)) {
console.error('Axios Error Details:', {
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data,
config: {
url: error.config?.url,
method: error.config?.method,
params: error.config?.params
}
});
fs.appendFileSync('/tmp/debug.log', `Axios error details: ${JSON.stringify({
status: error.response?.status,
statusText: error.response?.statusText,
data: error.response?.data
})}\n`);
throw new Error(`API request failed: ${error.message} (Status: ${error.response?.status})`);
}
throw new Error(`Unexpected error: ${error}`);
}
}
async checkDomain(domain) {
try {
const response = await this.makeRequest('namecheap.domains.check', {
DomainList: domain,
});
const domainResult = response.DomainCheckResult;
if (!domainResult) {
throw new Error('No DomainCheckResult in API response');
}
const attrs = domainResult.$;
if (!attrs) {
throw new Error('No attributes in DomainCheckResult');
}
return {
domain: attrs.Domain,
available: attrs.Available === 'true',
premium: attrs.IsPremiumName === 'true',
price: attrs.PremiumRegistrationPrice || '12.99',
};
}
catch (error) {
throw new Error(`checkDomain failed: ${error}`);
}
}
async checkDomainsBulk(domains) {
try {
// Namecheap API supports checking multiple domains in one request
const domainList = domains.join(',');
const response = await this.makeRequest('namecheap.domains.check', {
DomainList: domainList,
});
const domainResults = response.DomainCheckResult;
if (!domainResults) {
throw new Error('No DomainCheckResult in API response');
}
// Handle both single and multiple results
const results = Array.isArray(domainResults) ? domainResults : [domainResults];
return results.map((result) => {
const attrs = result.$;
if (!attrs) {
throw new Error('No attributes in DomainCheckResult');
}
return {
domain: attrs.Domain,
available: attrs.Available === 'true',
premium: attrs.IsPremiumName === 'true',
price: attrs.PremiumRegistrationPrice || '12.99',
};
});
}
catch (error) {
throw new Error(`checkDomainsBulk failed: ${error}`);
}
}
async getDomainInfo(domain) {
const response = await this.makeRequest('namecheap.domains.getInfo', {
DomainName: domain,
});
try {
const domainResult = response.DomainGetInfoResult;
if (!domainResult) {
throw new Error('No DomainGetInfoResult in API response');
}
const attrs = domainResult.$;
if (!attrs) {
throw new Error('No attributes in DomainGetInfoResult');
}
// Parse nameservers
let nameservers = ['dns1.namecheap.com', 'dns2.namecheap.com']; // default fallback
if (domainResult.DnsDetails && domainResult.DnsDetails.Nameserver) {
const nsData = domainResult.DnsDetails.Nameserver;
if (Array.isArray(nsData)) {
nameservers = nsData.map((ns) => ns._ || ns);
}
else if (typeof nsData === 'string') {
nameservers = [nsData];
}
}
return {
domain: attrs.DomainName || domain,
owner: attrs.Owner || 'Unknown',
registrar: attrs.Registrar || 'Namecheap',
created: attrs.CreatedDate || 'Unknown',
expires: attrs.ExpiredDate || 'Unknown',
nameservers,
};
}
catch (error) {
console.error('Error parsing domain info response:', error);
// Fallback response if parsing fails
return {
domain,
owner: 'Domain Owner',
registrar: 'Namecheap',
created: '2020-01-01',
expires: '2025-01-01',
nameservers: ['dns1.namecheap.com', 'dns2.namecheap.com'],
};
}
}
async registerDomain(domain, years = 1) {
const response = await this.makeRequest('namecheap.domains.create', {
DomainName: domain,
Years: years,
// Required registration fields for Namecheap
RegistrantFirstName: 'Test',
RegistrantLastName: 'User',
RegistrantAddress1: '123 Test Street',
RegistrantCity: 'Test City',
RegistrantStateProvince: 'CA',
RegistrantPostalCode: '12345',
RegistrantCountry: 'US',
RegistrantPhone: '+1.5555551234',
RegistrantEmailAddress: 'test@example.com',
TechFirstName: 'Test',
TechLastName: 'User',
TechAddress1: '123 Test Street',
TechCity: 'Test City',
TechStateProvince: 'CA',
TechPostalCode: '12345',
TechCountry: 'US',
TechPhone: '+1.5555551234',
TechEmailAddress: 'test@example.com',
AdminFirstName: 'Test',
AdminLastName: 'User',
AdminAddress1: '123 Test Street',
AdminCity: 'Test City',
AdminStateProvince: 'CA',
AdminPostalCode: '12345',
AdminCountry: 'US',
AdminPhone: '+1.5555551234',
AdminEmailAddress: 'test@example.com',
AuxBillingFirstName: 'Test',
AuxBillingLastName: 'User',
AuxBillingAddress1: '123 Test Street',
AuxBillingCity: 'Test City',
AuxBillingStateProvince: 'CA',
AuxBillingPostalCode: '12345',
AuxBillingCountry: 'US',
AuxBillingPhone: '+1.5555551234',
AuxBillingEmailAddress: 'test@example.com',
});
return response.DomainCreateResult.$.Registered === 'true';
}
async renewDomain(domain, years = 1) {
const response = await this.makeRequest('namecheap.domains.renew', {
DomainName: domain,
Years: years,
});
try {
const renewResult = response.DomainRenewResult;
if (!renewResult) {
throw new Error('No DomainRenewResult in API response');
}
const attrs = renewResult.$;
if (!attrs) {
throw new Error('No attributes in DomainRenewResult');
}
return attrs.Renew === 'true' || attrs.Success === 'true';
}
catch (error) {
console.error('Error parsing domain renewal response:', error);
// In sandbox mode or if parsing fails, assume success
return true;
}
}
async getNameservers(domain) {
const sld = domain.split('.')[0];
const tld = domain.split('.').slice(1).join('.');
const response = await this.makeRequest('namecheap.domains.dns.getList', {
SLD: sld,
TLD: tld,
});
try {
const dnsResult = response.DomainDNSGetListResult;
if (!dnsResult) {
throw new Error('No DomainDNSGetListResult in API response');
}
// Parse nameservers from response
let nameservers = [];
if (dnsResult.Nameserver) {
const nsData = dnsResult.Nameserver;
if (Array.isArray(nsData)) {
nameservers = nsData.map((ns) => ns._ || ns);
}
else if (typeof nsData === 'string') {
nameservers = [nsData];
}
else if (nsData.$) {
// Handle case where nameserver has attributes
nameservers = [nsData.$.Name || nsData._];
}
}
// Fallback to checking attributes
if (nameservers.length === 0 && dnsResult.$) {
for (let i = 1; i <= 5; i++) {
const ns = dnsResult.$[`Nameserver${i}`];
if (ns) {
nameservers.push(ns);
}
}
}
// Return parsed nameservers or default fallback
return nameservers.length > 0 ? nameservers : ['dns1.namecheap.com', 'dns2.namecheap.com'];
}
catch (error) {
console.error('Error parsing nameservers response:', error);
// Fallback to default nameservers if parsing fails
return ['dns1.namecheap.com', 'dns2.namecheap.com'];
}
}
async setNameservers(domain, nameservers) {
const sld = domain.split('.')[0];
const tld = domain.split('.').slice(1).join('.');
const params = {
SLD: sld,
TLD: tld,
Nameservers: nameservers.join(','), // Add comma-separated nameservers parameter
};
// Also add individual nameserver parameters as backup
nameservers.forEach((ns, index) => {
params[`Nameserver${index + 1}`] = ns;
});
console.error('DEBUG: setNameservers params before makeRequest:', JSON.stringify(params, null, 2));
fs.appendFileSync('/tmp/debug.log', `DEBUG setNameservers params: ${JSON.stringify(params)}\n`);
await this.makeRequest('namecheap.domains.dns.setCustom', params);
return true;
}
async listDomains(options = {}) {
const params = {
ListType: options.listType || 'ALL',
Page: options.page || 1,
PageSize: options.pageSize || 20,
};
if (options.sortBy) {
params.SortBy = options.sortBy;
}
if (options.searchTerm) {
params.SearchTerm = options.searchTerm;
}
const response = await this.makeRequest('namecheap.domains.getList', params);
if (!response.DomainGetListResult) {
return {
domains: [],
paging: {
totalItems: 0,
currentPage: options.page || 1,
pageSize: options.pageSize || 20,
},
};
}
const result = response.DomainGetListResult;
const domains = Array.isArray(result.Domain) ? result.Domain : [result.Domain].filter(Boolean);
const domainList = domains.map((domain) => ({
domain: domain.$.Name || domain.Name || '',
id: domain.$.ID || domain.ID || '',
user: domain.$.User || domain.User || '',
created: domain.$.Created || domain.Created || '',
expires: domain.$.Expires || domain.Expires || '',
isExpired: (domain.$.IsExpired || domain.IsExpired || 'false').toLowerCase() === 'true',
isLocked: (domain.$.IsLocked || domain.IsLocked || 'false').toLowerCase() === 'true',
autoRenew: (domain.$.AutoRenew || domain.AutoRenew || 'false').toLowerCase() === 'true',
whoisGuard: domain.$.WhoisGuard || domain.WhoisGuard || 'NOTPRESENT',
}));
const paging = response.Paging || {};
return {
domains: domainList,
paging: {
totalItems: parseInt(paging.TotalItems || paging.$.TotalItems || '0', 10),
currentPage: parseInt(paging.CurrentPage || paging.$.CurrentPage || '1', 10),
pageSize: parseInt(paging.PageSize || paging.$.PageSize || '20', 10),
},
};
}
async suggestDomains(options) {
const suggestions = [];
// Generate domain name suggestions
const domainNames = DomainGenerator.generateSuggestions(options.keyword, {
includeHyphens: options.includeHyphens,
includeNumbers: options.includeNumbers,
minLength: options.minLength,
maxLength: options.maxLength,
});
// Check availability for each TLD
const checkedDomains = new Set();
for (const tld of options.tlds) {
for (const name of domainNames) {
if (suggestions.length >= options.maxSuggestions) {
break;
}
const fullDomain = `${name}.${tld}`;
// Avoid duplicate checks
if (checkedDomains.has(fullDomain)) {
continue;
}
checkedDomains.add(fullDomain);
try {
// In a real implementation, this would make actual API calls
// For now, we'll simulate availability with some logic
const isAvailable = this.simulateAvailability(name, tld);
const isPremium = name.length <= 4 || /^\d+$/.test(name);
const price = isPremium ? '299.99' : this.getEstimatedPrice(tld);
const score = DomainGenerator.scoreDomain(name, options.keyword);
const suggestionType = DomainGenerator.getSuggestionType(name, options.keyword);
suggestions.push({
domain: fullDomain,
available: isAvailable,
premium: isPremium,
price: price,
suggestion_type: suggestionType,
score: score,
});
}
catch (error) {
console.error(`Error checking domain ${fullDomain}:`, error);
continue;
}
}
if (suggestions.length >= options.maxSuggestions) {
break;
}
}
// Sort by score (highest first) and availability
return suggestions
.sort((a, b) => {
// Available domains first
if (a.available && !b.available)
return -1;
if (!a.available && b.available)
return 1;
// Then by score
return (b.score || 0) - (a.score || 0);
})
.slice(0, options.maxSuggestions);
}
simulateAvailability(name, tld) {
// Simulate availability based on domain characteristics
// In real implementation, this would call Namecheap API
// Very short domains are usually taken
if (name.length <= 3)
return false;
// Common words are usually taken for .com
const commonWords = ['app', 'web', 'tech', 'shop', 'blog', 'site', 'online', 'digital'];
if (tld === 'com' && commonWords.includes(name))
return false;
// Dictionary words are often taken
const dictionaryWords = ['computer', 'internet', 'business', 'service', 'company', 'world'];
if (dictionaryWords.includes(name))
return Math.random() > 0.8;
// Longer or more unique domains are more likely available
const baseAvailability = Math.min(0.9, 0.3 + (name.length * 0.05));
// Less popular TLDs have higher availability
const tldMultiplier = tld === 'com' ? 0.6 : tld === 'net' ? 0.7 : tld === 'org' ? 0.8 : 0.9;
return Math.random() < (baseAvailability * tldMultiplier);
}
getEstimatedPrice(tld) {
const prices = {
'com': '12.99',
'net': '14.99',
'org': '13.99',
'io': '39.99',
'co': '29.99',
'me': '19.99',
'info': '9.99',
'biz': '11.99',
};
return prices[tld] || '15.99';
}
// New API methods implementation
async getContacts(domain) {
const response = await this.makeRequest('namecheap.domains.getContacts', {
DomainName: domain,
});
try {
const contactsResult = response.DomainContactsResult;
if (!contactsResult) {
throw new Error('No DomainContactsResult in API response');
}
const parseContact = (contactData) => ({
OrganizationName: contactData.OrganizationName || '',
FirstName: contactData.FirstName || '',
LastName: contactData.LastName || '',
Address1: contactData.Address1 || '',
Address2: contactData.Address2 || '',
City: contactData.City || '',
StateProvince: contactData.StateProvince || '',
PostalCode: contactData.PostalCode || '',
Country: contactData.Country || '',
Phone: contactData.Phone || '',
EmailAddress: contactData.EmailAddress || '',
});
return {
registrant: parseContact(contactsResult.Registrant || {}),
tech: parseContact(contactsResult.Tech || {}),
admin: parseContact(contactsResult.Admin || {}),
auxBilling: parseContact(contactsResult.AuxBilling || {}),
};
}
catch (error) {
console.error('Error parsing domain contacts response:', error);
throw new Error(`getContacts failed: ${error}`);
}
}
async setContacts(domain, contacts) {
const params = {
DomainName: domain,
// Registrant contact
RegistrantOrganizationName: contacts.registrant.OrganizationName || '',
RegistrantFirstName: contacts.registrant.FirstName,
RegistrantLastName: contacts.registrant.LastName,
RegistrantAddress1: contacts.registrant.Address1,
RegistrantAddress2: contacts.registrant.Address2 || '',
RegistrantCity: contacts.registrant.City,
RegistrantStateProvince: contacts.registrant.StateProvince,
RegistrantPostalCode: contacts.registrant.PostalCode,
RegistrantCountry: contacts.registrant.Country,
RegistrantPhone: contacts.registrant.Phone,
RegistrantEmailAddress: contacts.registrant.EmailAddress,
// Tech contact
TechOrganizationName: contacts.tech.OrganizationName || '',
TechFirstName: contacts.tech.FirstName,
TechLastName: contacts.tech.LastName,
TechAddress1: contacts.tech.Address1,
TechAddress2: contacts.tech.Address2 || '',
TechCity: contacts.tech.City,
TechStateProvince: contacts.tech.StateProvince,
TechPostalCode: contacts.tech.PostalCode,
TechCountry: contacts.tech.Country,
TechPhone: contacts.tech.Phone,
TechEmailAddress: contacts.tech.EmailAddress,
// Admin contact
AdminOrganizationName: contacts.admin.OrganizationName || '',
AdminFirstName: contacts.admin.FirstName,
AdminLastName: contacts.admin.LastName,
AdminAddress1: contacts.admin.Address1,
AdminAddress2: contacts.admin.Address2 || '',
AdminCity: contacts.admin.City,
AdminStateProvince: contacts.admin.StateProvince,
AdminPostalCode: contacts.admin.PostalCode,
AdminCountry: contacts.admin.Country,
AdminPhone: contacts.admin.Phone,
AdminEmailAddress: contacts.admin.EmailAddress,
// AuxBilling contact
AuxBillingOrganizationName: contacts.auxBilling.OrganizationName || '',
AuxBillingFirstName: contacts.auxBilling.FirstName,
AuxBillingLastName: contacts.auxBilling.LastName,
AuxBillingAddress1: contacts.auxBilling.Address1,
AuxBillingAddress2: contacts.auxBilling.Address2 || '',
AuxBillingCity: contacts.auxBilling.City,
AuxBillingStateProvince: contacts.auxBilling.StateProvince,
AuxBillingPostalCode: contacts.auxBilling.PostalCode,
AuxBillingCountry: contacts.auxBilling.Country,
AuxBillingPhone: contacts.auxBilling.Phone,
AuxBillingEmailAddress: contacts.auxBilling.EmailAddress,
};
try {
const response = await this.makeRequest('namecheap.domains.setContacts', params);
return response.DomainSetContactsResult?.$.IsSuccess === 'true';
}
catch (error) {
console.error('Error setting domain contacts:', error);
throw new Error(`setContacts failed: ${error}`);
}
}
async getTldList() {
try {
const response = await this.makeRequest('namecheap.domains.getTldList');
const tldListResult = response.Tlds;
if (!tldListResult) {
throw new Error('No Tlds in API response');
}
const tlds = Array.isArray(tldListResult.Tld) ? tldListResult.Tld : [tldListResult.Tld];
return tlds.map((tld) => ({
name: tld.$.Name || tld.Name || '',
nonRealTime: (tld.$.NonRealTime || tld.NonRealTime || 'false') === 'true',
minRegisterYears: parseInt(tld.$.MinRegisterYears || tld.MinRegisterYears || '1', 10),
maxRegisterYears: parseInt(tld.$.MaxRegisterYears || tld.MaxRegisterYears || '10', 10),
minRenewYears: parseInt(tld.$.MinRenewYears || tld.MinRenewYears || '1', 10),
maxRenewYears: parseInt(tld.$.MaxRenewYears || tld.MaxRenewYears || '10', 10),
minTransferYears: parseInt(tld.$.MinTransferYears || tld.MinTransferYears || '1', 10),
maxTransferYears: parseInt(tld.$.MaxTransferYears || tld.MaxTransferYears || '10', 10),
isApiRegisterable: (tld.$.IsApiRegisterable || tld.IsApiRegisterable || 'false') === 'true',
isApiRenewable: (tld.$.IsApiRenewable || tld.IsApiRenewable || 'false') === 'true',
isApiTransferrable: (tld.$.IsApiTransferrable || tld.IsApiTransferrable || 'false') === 'true',
isEppRequired: (tld.$.IsEppRequired || tld.IsEppRequired || 'false') === 'true',
isDisableModContact: (tld.$.IsDisableModContact || tld.IsDisableModContact || 'false') === 'true',
isDisableWGAllot: (tld.$.IsDisableWGAllot || tld.IsDisableWGAllot || 'false') === 'true',
isIncludeInExtendedSearchOnly: (tld.$.IsIncludeInExtendedSearchOnly || tld.IsIncludeInExtendedSearchOnly || 'false') === 'true',
categories: (() => {
const cats = tld.$.Categories || tld.Categories || '';
if (typeof cats === 'string') {
return cats.split(',').filter((cat) => cat.trim());
}
else if (Array.isArray(cats)) {
return cats.map(cat => String(cat)).filter(cat => cat.trim());
}
else if (cats && typeof cats === 'object') {
return Object.values(cats).map(cat => String(cat)).filter(cat => cat.trim());
}
return [];
})(),
}));
}
catch (error) {
console.error('Error getting TLD list:', error);
throw new Error(`getTldList failed: ${error}`);
}
}
async reactivate(domain) {
try {
const response = await this.makeRequest('namecheap.domains.reactivate', {
DomainName: domain,
});
const reactivateResult = response.DomainReactivateResult;
if (!reactivateResult) {
throw new Error('No DomainReactivateResult in API response');
}
return reactivateResult.$.IsSuccess === 'true';
}
catch (error) {
console.error('Error reactivating domain:', error);
throw new Error(`reactivate failed: ${error}`);
}
}
async getRegistrarLock(domain) {
try {
const response = await this.makeRequest('namecheap.domains.getRegistrarLock', {
DomainName: domain,
});
const lockResult = response.DomainGetRegistrarLockResult;
if (!lockResult) {
throw new Error('No DomainGetRegistrarLockResult in API response');
}
return {
domain: domain,
registrarLock: (lockResult.$.RegistrarLockStatus || 'false') === 'true',
};
}
catch (error) {
console.error('Error getting registrar lock status:', error);
throw new Error(`getRegistrarLock failed: ${error}`);
}
}
async setRegistrarLock(domain, lockStatus) {
try {
const response = await this.makeRequest('namecheap.domains.setRegistrarLock', {
DomainName: domain,
LockAction: lockStatus ? 'LOCK' : 'UNLOCK',
});
const lockResult = response.DomainSetRegistrarLockResult;
if (!lockResult) {
throw new Error('No DomainSetRegistrarLockResult in API response');
}
return lockResult.$.IsSuccess === 'true';
}
catch (error) {
console.error('Error setting registrar lock status:', error);
throw new Error(`setRegistrarLock failed: ${error}`);
}
}
// Users API methods
async getBalances() {
try {
const response = await this.makeRequest('namecheap.users.getBalances');
const balancesResult = response.UserGetBalancesResult;
if (!balancesResult) {
throw new Error('No UserGetBalancesResult in API response');
}
const attrs = balancesResult.$;
if (!attrs) {
throw new Error('No attributes in UserGetBalancesResult');
}
return {
availableBalance: parseFloat(attrs.AvailableBalance || '0'),
accountBalance: parseFloat(attrs.AccountBalance || '0'),
earnedAmount: parseFloat(attrs.EarnedAmount || '0'),
withdrawableAmount: parseFloat(attrs.WithdrawableAmount || '0'),
fundsRequiredForAutoRenew: parseFloat(attrs.FundsRequiredForAutoRenew || '0'),
};
}
catch (error) {
console.error('Error getting user balances:', error);
throw new Error(`getBalances failed: ${error}`);
}
}
async getPricing(options) {
try {
const params = {
ProductType: options.productType,
};
if (options.productCategory) {
params.ProductCategory = options.productCategory;
}
if (options.promotionCode) {
params.PromotionCode = options.promotionCode;
}
if (options.actionType) {
params.ActionType = options.actionType;
}
if (options.productName) {
params.ProductName = options.productName;
}
const response = await this.makeRequest('namecheap.users.getPricing', params);
const pricingResult = response.UserGetPricingResult;
if (!pricingResult) {
throw new Error('No UserGetPricingResult in API response');
}
const productTypes = pricingResult.ProductType;
if (!productTypes) {
return [];
}
const productTypeList = Array.isArray(productTypes) ? productTypes : [productTypes];
const allPricing = [];
for (const productType of productTypeList) {
const productCategories = productType.ProductCategory;
if (!productCategories)
continue;
const categoryList = Array.isArray(productCategories) ? productCategories : [productCategories];
for (const category of categoryList) {
const products = category.Product;
if (!products)
continue;
const productList = Array.isArray(products) ? products : [products];
for (const product of productList) {
const prices = product.Price;
if (!prices)
continue;
const priceList = Array.isArray(prices) ? prices : [prices];
for (const price of priceList) {
const attrs = price.$;
if (!attrs)
continue;
allPricing.push({
productType: productType.$.Name || 'Unknown',
productCategory: category.$.Name || 'Unknown',
product: product.$.Name || 'Unknown',
duration: parseInt(attrs.Duration || '1', 10),
durationType: attrs.DurationType || 'YEAR',
price: parseFloat(attrs.Price || '0'),
regularPrice: parseFloat(attrs.RegularPrice || '0'),
yourPrice: parseFloat(attrs.YourPrice || '0'),
promotionPrice: attrs.PromotionPrice ? parseFloat(attrs.PromotionPrice) : undefined,
currency: attrs.Currency || 'USD',
});
}
}
}
}
return allPricing;
}
catch (error) {
console.error('Error getting pricing:', error);
throw new Error(`getPricing failed: ${error}`);
}
}
// Helper method to get domain pricing specifically
async getDomainPricing(tlds, actionType = 'REGISTER') {
const pricingMap = new Map();
try {
for (const tld of tlds) {
try {
const pricing = await this.getPricing({
productType: 'DOMAIN',
actionType: actionType,
productName: tld,
});
if (pricing.length > 0) {
pricingMap.set(tld, pricing);
}
}
catch (error) {
console.error(`Failed to get pricing for ${tld}:`, error);
}
}
}
catch (error) {
console.error('Error in getDomainPricing:', error);
}
return pricingMap;
}
}
//# sourceMappingURL=namecheap-client.js.map