@aoxborrow/registrar-client 0.11.0 → 0.12.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 +10 -7
- package/dist/index.cjs +163 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +163 -11
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/registrars/gandi.ts +91 -3
- package/src/registrars/godaddy.ts +136 -8
package/README.md
CHANGED
|
@@ -187,9 +187,9 @@ are the guaranteed contract; **extended** methods are opt-in per provider.
|
|
|
187
187
|
| Extended method | CF | DY | GA | GD | NB | NC | NS | PB | SP | NM | 101 |
|
|
188
188
|
| --------------------- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
|
|
189
189
|
| `getAuthCode` | ✗ | ✓ | ✓ | ✓ | ✓ | ✗ⁱ | ✗ⁱ | ✗ | ✓ | ✓ | ✗ |
|
|
190
|
-
| `getTransferOut` | ✗ | ✓ⁿ |
|
|
191
|
-
| `approveTransferOut` | ✗ | ✓ⁿ |
|
|
192
|
-
| `rejectTransferOut` | ✗ | ✓ⁿ |
|
|
190
|
+
| `getTransferOut` | ✗ | ✓ⁿ | ✓ⁿ | ✓ⁿ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
|
|
191
|
+
| `approveTransferOut` | ✗ | ✓ⁿ | ✓ⁿ | ✓ⁿ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ |
|
|
192
|
+
| `rejectTransferOut` | ✗ | ✓ⁿ | ✓ⁿ | ✓ⁿ | ✗ | ✗ | ✗ | ✗ | ✗ | ✗ⁿ | ✗ |
|
|
193
193
|
| `getDnssec` | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ᵐ | ✗ |
|
|
194
194
|
| `disableDnssec` | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ᵐ | ✗ |
|
|
195
195
|
| `getEmailForwarding` | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ᵐ | ✗ |
|
|
@@ -238,10 +238,13 @@ not a claim of available API endpoints. See [101domain](docs/registrars/101domai
|
|
|
238
238
|
- **ⁿ** Transfer-out approval answers a transfer another registrar requested with
|
|
239
239
|
the auth code. Dynadot's is sandbox-verified only up to "no such order" (its
|
|
240
240
|
sandbox can't start an outbound transfer); a real pending one's read was
|
|
241
|
-
checked in production. GoDaddy
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
241
|
+
checked in production. GoDaddy's uses the v2 customer-scoped API, so it needs a
|
|
242
|
+
`customerId` as forwarding does (ˡ); it reads the domain's recent actions, with
|
|
243
|
+
no expiry date. Gandi has no transfer-out to read: a `pendingTransfer` status
|
|
244
|
+
reads as pending, with no dates, and approving sends the current auth code
|
|
245
|
+
back. Neither has been tried on a real transfer yet. Name.com can only reject
|
|
246
|
+
(not wired up); the rest leave it to the dashboard or the transfer email, and
|
|
247
|
+
release the domain after about 5 days. See each registrar's doc.
|
|
245
248
|
- **ˡ** GoDaddy domain forwarding uses the v2 customer-scoped API, so it requires
|
|
246
249
|
a `customerId` (the customer UUID, or a numeric shopper ID the client resolves to
|
|
247
250
|
the UUID via an sso-key-only lookup); the old v1 forwarding route was removed.
|
package/dist/index.cjs
CHANGED
|
@@ -3212,9 +3212,9 @@ var GandiRegistrar = class extends BaseRegistrar {
|
|
|
3212
3212
|
];
|
|
3213
3213
|
// Gandi's v5 API offers a sandbox at api.sandbox.gandi.net (separate account)
|
|
3214
3214
|
static supportsSandbox = true;
|
|
3215
|
-
// On top of core: transfer-out auth code (`authinfo`), DNSSEC
|
|
3216
|
-
// (LiveDNS keys), email forwarding, and web/URL forwarding
|
|
3217
|
-
// per host, apex included). Note core `setPrivacy` disabling is a no-op for
|
|
3215
|
+
// On top of core: transfer-out auth code (`authinfo`) and approval, DNSSEC
|
|
3216
|
+
// read/disable (LiveDNS keys), email forwarding, and web/URL forwarding
|
|
3217
|
+
// (webredirs; per host, apex included). Note core `setPrivacy` disabling is a no-op for
|
|
3218
3218
|
// individual (natural-person) registrants — Gandi keeps WHOIS obfuscation on
|
|
3219
3219
|
// for GDPR regardless of the request.
|
|
3220
3220
|
static extendedFeatures = [
|
|
@@ -3224,7 +3224,10 @@ var GandiRegistrar = class extends BaseRegistrar {
|
|
|
3224
3224
|
Feature.GetEmailForwarding,
|
|
3225
3225
|
Feature.SetEmailForwarding,
|
|
3226
3226
|
Feature.GetDomainForwarding,
|
|
3227
|
-
Feature.SetDomainForwarding
|
|
3227
|
+
Feature.SetDomainForwarding,
|
|
3228
|
+
Feature.GetTransferOut,
|
|
3229
|
+
Feature.ApproveTransferOut,
|
|
3230
|
+
Feature.RejectTransferOut
|
|
3228
3231
|
];
|
|
3229
3232
|
constructor(credentials, options) {
|
|
3230
3233
|
super(
|
|
@@ -3476,6 +3479,58 @@ var GandiRegistrar = class extends BaseRegistrar {
|
|
|
3476
3479
|
});
|
|
3477
3480
|
return d.authinfo ?? "";
|
|
3478
3481
|
}
|
|
3482
|
+
/**
|
|
3483
|
+
* The domain's pending outbound transfer. Gandi has no transfer-out resource
|
|
3484
|
+
* to read: a domain in the account carries the documented `pendingTransfer`
|
|
3485
|
+
* EPP status while another registrar's request waits (inbound transfers live
|
|
3486
|
+
* under /domain/transferin, so on a domain you hold it's outbound — inferred,
|
|
3487
|
+
* not documented). Gandi reports no dates for it, and nothing once it's
|
|
3488
|
+
* answered or done, so this reads `pending` or null.
|
|
3489
|
+
*/
|
|
3490
|
+
async getTransferOut(domainName, opts) {
|
|
3491
|
+
const d = await this.http.request({
|
|
3492
|
+
path: `/domain/domains/${domainName}`,
|
|
3493
|
+
...opts
|
|
3494
|
+
});
|
|
3495
|
+
return pendingTransferOut(d);
|
|
3496
|
+
}
|
|
3497
|
+
/**
|
|
3498
|
+
* Approves the pending outbound transfer, releasing the domain now instead of
|
|
3499
|
+
* after Gandi's five days: POST .../transferout with `accept: true` and the
|
|
3500
|
+
* domain's current auth code, which Gandi requires back (→ 202).
|
|
3501
|
+
*/
|
|
3502
|
+
async approveTransferOut(domainName, opts) {
|
|
3503
|
+
return this.answerTransferOut(domainName, true, opts);
|
|
3504
|
+
}
|
|
3505
|
+
// Declines the pending outbound transfer (`accept: false`, no reason); the
|
|
3506
|
+
// domain stays in the account.
|
|
3507
|
+
async rejectTransferOut(domainName, opts) {
|
|
3508
|
+
return this.answerTransferOut(domainName, false, opts);
|
|
3509
|
+
}
|
|
3510
|
+
// One read for both the pending status and the auth code the answer needs.
|
|
3511
|
+
async answerTransferOut(domainName, accept, opts) {
|
|
3512
|
+
let d;
|
|
3513
|
+
try {
|
|
3514
|
+
d = await this.http.request({
|
|
3515
|
+
path: `/domain/domains/${domainName}`,
|
|
3516
|
+
...opts
|
|
3517
|
+
});
|
|
3518
|
+
} catch (error) {
|
|
3519
|
+
return { success: false, message: toRegistrarError(error).message };
|
|
3520
|
+
}
|
|
3521
|
+
if (!pendingTransferOut(d)) {
|
|
3522
|
+
return { success: false, message: `No transfer-out of ${domainName} to answer` };
|
|
3523
|
+
}
|
|
3524
|
+
return this.mutate(
|
|
3525
|
+
{
|
|
3526
|
+
method: "POST",
|
|
3527
|
+
path: `/domain/domains/${domainName}/transferout`,
|
|
3528
|
+
body: { accept, authinfo: d.authinfo ?? "" }
|
|
3529
|
+
},
|
|
3530
|
+
`Transfer of ${domainName} ${accept ? "approved" : "rejected"}`,
|
|
3531
|
+
opts
|
|
3532
|
+
);
|
|
3533
|
+
}
|
|
3479
3534
|
/**
|
|
3480
3535
|
* DNSSEC status via Gandi LiveDNS keys (GET /livedns/domains/{fqdn}/keys). A
|
|
3481
3536
|
* non-deleted key means DNSSEC is enabled; each key's `ds` field is the full DS
|
|
@@ -3843,6 +3898,17 @@ var GandiRegistrar = class extends BaseRegistrar {
|
|
|
3843
3898
|
}
|
|
3844
3899
|
}
|
|
3845
3900
|
};
|
|
3901
|
+
function pendingTransferOut(d) {
|
|
3902
|
+
const statuses = Array.isArray(d.status) ? d.status : d.status ? [d.status] : [];
|
|
3903
|
+
if (!statuses.includes("pendingTransfer")) return null;
|
|
3904
|
+
return {
|
|
3905
|
+
status: "pending",
|
|
3906
|
+
rawStatus: "pendingTransfer",
|
|
3907
|
+
requestedDate: null,
|
|
3908
|
+
expirationDate: null,
|
|
3909
|
+
completedDate: null
|
|
3910
|
+
};
|
|
3911
|
+
}
|
|
3846
3912
|
function toGandiContact(c) {
|
|
3847
3913
|
const contact = {
|
|
3848
3914
|
given: c.firstName,
|
|
@@ -3983,17 +4049,22 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
|
|
|
3983
4049
|
];
|
|
3984
4050
|
// GoDaddy's OTE ("Operational Test Environment") is its sandbox
|
|
3985
4051
|
static supportsSandbox = true;
|
|
3986
|
-
// Beyond core: domain forwarding
|
|
3987
|
-
// field on the v1 domain-detail response)
|
|
3988
|
-
// customer-scoped route
|
|
3989
|
-
//
|
|
3990
|
-
//
|
|
4052
|
+
// Beyond core: domain forwarding, transfer-out auth code (the `authCode`
|
|
4053
|
+
// field on the v1 domain-detail response) and transfer-out approval.
|
|
4054
|
+
// Forwarding uses the v2 customer-scoped route
|
|
4055
|
+
// /v2/customers/{customerId}/domains/forwards/{fqdn} (the old v1
|
|
4056
|
+
// /v1/domains/forwards routes now 404); it requires `customerId` to be
|
|
4057
|
+
// configured — verified live 2026-08-29. Transfer-out approval is v2
|
|
4058
|
+
// customer-scoped too, so it needs `customerId` as well. DNSSEC has no dedicated
|
|
3991
4059
|
// endpoint (DS records go through the generic DNS API), and there's no
|
|
3992
4060
|
// glue-record, email, or push-webhook API.
|
|
3993
4061
|
static extendedFeatures = [
|
|
3994
4062
|
Feature.GetAuthCode,
|
|
3995
4063
|
Feature.GetDomainForwarding,
|
|
3996
|
-
Feature.SetDomainForwarding
|
|
4064
|
+
Feature.SetDomainForwarding,
|
|
4065
|
+
Feature.GetTransferOut,
|
|
4066
|
+
Feature.ApproveTransferOut,
|
|
4067
|
+
Feature.RejectTransferOut
|
|
3997
4068
|
];
|
|
3998
4069
|
// true → prefer the v3 API (production + PAT). false → all calls use v1
|
|
3999
4070
|
// (sso-key auth, or OTE/sandbox where v3 does not exist).
|
|
@@ -4594,7 +4665,7 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
|
|
|
4594
4665
|
const configured = this.credentials.customerId?.trim();
|
|
4595
4666
|
if (!configured) {
|
|
4596
4667
|
throw new ConfigurationError(
|
|
4597
|
-
`${this.name}: domain forwarding
|
|
4668
|
+
`${this.name}: domain forwarding and transfer-out approval use the v2 customer-scoped API, which requires a customer ID. Set \`customerId\` to your GoDaddy customer UUID (or your numeric shopper/account ID, which will be resolved to the UUID).`
|
|
4598
4669
|
);
|
|
4599
4670
|
}
|
|
4600
4671
|
if (UUID_RE.test(configured)) return configured;
|
|
@@ -4694,6 +4765,52 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
|
|
|
4694
4765
|
forwardPath(customerId, fqdn) {
|
|
4695
4766
|
return `/v2/customers/${encodeURIComponent(customerId)}/domains/forwards/${encodeURIComponent(fqdn)}`;
|
|
4696
4767
|
}
|
|
4768
|
+
// the v2 customer-scoped path for a domain
|
|
4769
|
+
domainPathV2(customerId, domainName) {
|
|
4770
|
+
return `/v2/customers/${encodeURIComponent(customerId)}/domains/${encodeURIComponent(domainName)}`;
|
|
4771
|
+
}
|
|
4772
|
+
/**
|
|
4773
|
+
* The domain's latest outbound transfer, read from its recent actions
|
|
4774
|
+
* (GET /v2/customers/{customerId}/domains/{domain}/actions): the TRANSFER_OUT
|
|
4775
|
+
* action, and an accept or reject logged after it. Null when there is no
|
|
4776
|
+
* TRANSFER_OUT on record. GoDaddy reports no expiry; left alone, it releases
|
|
4777
|
+
* the domain within seven days. Shapes from the published v2 spec; a real
|
|
4778
|
+
* transfer-out is not yet seen live.
|
|
4779
|
+
*/
|
|
4780
|
+
async getTransferOut(domainName, opts) {
|
|
4781
|
+
const customerId = await this.resolveCustomerId(opts);
|
|
4782
|
+
const actions = await this.http.request({
|
|
4783
|
+
path: `${this.domainPathV2(customerId, domainName)}/actions`,
|
|
4784
|
+
...opts
|
|
4785
|
+
});
|
|
4786
|
+
return toTransferOut2(Array.isArray(actions) ? actions : []);
|
|
4787
|
+
}
|
|
4788
|
+
/**
|
|
4789
|
+
* Approves the pending outbound transfer, releasing the domain now
|
|
4790
|
+
* (POST .../transferOutAccept → 202). GoDaddy answers 409 when none is
|
|
4791
|
+
* pending.
|
|
4792
|
+
*/
|
|
4793
|
+
async approveTransferOut(domainName, opts) {
|
|
4794
|
+
return this.answerTransferOut(domainName, "transferOutAccept", "approved", opts);
|
|
4795
|
+
}
|
|
4796
|
+
// Rejects the pending outbound transfer (POST .../transferOutReject, with no
|
|
4797
|
+
// reason); the domain stays in the account.
|
|
4798
|
+
async rejectTransferOut(domainName, opts) {
|
|
4799
|
+
return this.answerTransferOut(domainName, "transferOutReject", "rejected", opts);
|
|
4800
|
+
}
|
|
4801
|
+
async answerTransferOut(domainName, action, answer, opts) {
|
|
4802
|
+
let customerId;
|
|
4803
|
+
try {
|
|
4804
|
+
customerId = await this.resolveCustomerId(opts);
|
|
4805
|
+
} catch (error) {
|
|
4806
|
+
return { success: false, message: toRegistrarError(error).message };
|
|
4807
|
+
}
|
|
4808
|
+
return this.mutate(
|
|
4809
|
+
{ method: "POST", path: `${this.domainPathV2(customerId, domainName)}/${action}` },
|
|
4810
|
+
`Transfer of ${domainName} ${answer}`,
|
|
4811
|
+
opts
|
|
4812
|
+
);
|
|
4813
|
+
}
|
|
4697
4814
|
/**
|
|
4698
4815
|
* Transfer authorization (EPP) code. GoDaddy exposes it as the `authCode`
|
|
4699
4816
|
* field on the v1 domain-detail response; transfers were never moved to v3,
|
|
@@ -4750,6 +4867,41 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
|
|
|
4750
4867
|
}
|
|
4751
4868
|
}
|
|
4752
4869
|
};
|
|
4870
|
+
function toTransferOut2(actions) {
|
|
4871
|
+
const time = (a) => parseDate(a.createdAt)?.getTime() ?? 0;
|
|
4872
|
+
const byLatest = (types) => actions.filter((a) => types.includes(a.type ?? "")).sort((a, b) => time(b) - time(a));
|
|
4873
|
+
const transfer = byLatest(["TRANSFER_OUT"])[0];
|
|
4874
|
+
if (!transfer) return null;
|
|
4875
|
+
const answer = byLatest(["TRANSFER_OUT_ACCEPT", "TRANSFER_OUT_REJECT"]).find(
|
|
4876
|
+
(a) => time(a) >= time(transfer) && !/^(FAILED|CANCELLED)$/i.test(a.status ?? "")
|
|
4877
|
+
);
|
|
4878
|
+
const rejected = answer?.type === "TRANSFER_OUT_REJECT";
|
|
4879
|
+
const rawStatus = transfer.status ?? "";
|
|
4880
|
+
let status;
|
|
4881
|
+
switch (rawStatus.toUpperCase()) {
|
|
4882
|
+
case "ACCEPTED":
|
|
4883
|
+
case "AWAITING":
|
|
4884
|
+
case "PENDING":
|
|
4885
|
+
status = !answer ? "pending" : rejected ? "rejected" : "approved";
|
|
4886
|
+
break;
|
|
4887
|
+
case "SUCCESS":
|
|
4888
|
+
status = "completed";
|
|
4889
|
+
break;
|
|
4890
|
+
case "FAILED":
|
|
4891
|
+
case "CANCELLED":
|
|
4892
|
+
status = rejected ? "rejected" : "cancelled";
|
|
4893
|
+
break;
|
|
4894
|
+
default:
|
|
4895
|
+
status = "unknown";
|
|
4896
|
+
}
|
|
4897
|
+
return {
|
|
4898
|
+
status,
|
|
4899
|
+
rawStatus,
|
|
4900
|
+
requestedDate: parseDate(transfer.createdAt),
|
|
4901
|
+
expirationDate: null,
|
|
4902
|
+
completedDate: status === "completed" ? parseDate(transfer.completedAt) : null
|
|
4903
|
+
};
|
|
4904
|
+
}
|
|
4753
4905
|
function fromGoDaddyContact(c) {
|
|
4754
4906
|
if (!c) return void 0;
|
|
4755
4907
|
const a = c.addressMailing ?? {};
|