@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/dist/index.d.cts CHANGED
@@ -594,6 +594,10 @@ declare class GandiRegistrar extends BaseRegistrar {
594
594
  lockDomain(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
595
595
  unlockDomain(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
596
596
  getAuthCode(domainName: string, opts?: RequestOptions): Promise<string>;
597
+ getTransferOut(domainName: string, opts?: RequestOptions): Promise<TransferOut | null>;
598
+ approveTransferOut(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
599
+ rejectTransferOut(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
600
+ private answerTransferOut;
597
601
  getDnssec(domainName: string, opts?: RequestOptions): Promise<DnssecStatus>;
598
602
  disableDnssec(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
599
603
  getEmailForwarding(domainName: string, opts?: RequestOptions): Promise<EmailForward[]>;
@@ -653,6 +657,11 @@ declare class GoDaddyRegistrar extends BaseRegistrar {
653
657
  getDomainForwarding(domainName: string, opts?: RequestOptions): Promise<DomainForward[]>;
654
658
  setDomainForwarding(domainName: string, forwards: DomainForward[], opts?: RequestOptions): Promise<OperationResult>;
655
659
  private forwardPath;
660
+ private domainPathV2;
661
+ getTransferOut(domainName: string, opts?: RequestOptions): Promise<TransferOut | null>;
662
+ approveTransferOut(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
663
+ rejectTransferOut(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
664
+ private answerTransferOut;
656
665
  getAuthCode(domainName: string, opts?: RequestOptions): Promise<string>;
657
666
  private toDomain;
658
667
  private toDomainV3;
package/dist/index.d.ts CHANGED
@@ -594,6 +594,10 @@ declare class GandiRegistrar extends BaseRegistrar {
594
594
  lockDomain(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
595
595
  unlockDomain(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
596
596
  getAuthCode(domainName: string, opts?: RequestOptions): Promise<string>;
597
+ getTransferOut(domainName: string, opts?: RequestOptions): Promise<TransferOut | null>;
598
+ approveTransferOut(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
599
+ rejectTransferOut(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
600
+ private answerTransferOut;
597
601
  getDnssec(domainName: string, opts?: RequestOptions): Promise<DnssecStatus>;
598
602
  disableDnssec(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
599
603
  getEmailForwarding(domainName: string, opts?: RequestOptions): Promise<EmailForward[]>;
@@ -653,6 +657,11 @@ declare class GoDaddyRegistrar extends BaseRegistrar {
653
657
  getDomainForwarding(domainName: string, opts?: RequestOptions): Promise<DomainForward[]>;
654
658
  setDomainForwarding(domainName: string, forwards: DomainForward[], opts?: RequestOptions): Promise<OperationResult>;
655
659
  private forwardPath;
660
+ private domainPathV2;
661
+ getTransferOut(domainName: string, opts?: RequestOptions): Promise<TransferOut | null>;
662
+ approveTransferOut(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
663
+ rejectTransferOut(domainName: string, opts?: RequestOptions): Promise<OperationResult>;
664
+ private answerTransferOut;
656
665
  getAuthCode(domainName: string, opts?: RequestOptions): Promise<string>;
657
666
  private toDomain;
658
667
  private toDomainV3;
package/dist/index.js CHANGED
@@ -3210,9 +3210,9 @@ var GandiRegistrar = class extends BaseRegistrar {
3210
3210
  ];
3211
3211
  // Gandi's v5 API offers a sandbox at api.sandbox.gandi.net (separate account)
3212
3212
  static supportsSandbox = true;
3213
- // On top of core: transfer-out auth code (`authinfo`), DNSSEC read/disable
3214
- // (LiveDNS keys), email forwarding, and web/URL forwarding (webredirs;
3215
- // per host, apex included). Note core `setPrivacy` disabling is a no-op for
3213
+ // On top of core: transfer-out auth code (`authinfo`) and approval, DNSSEC
3214
+ // read/disable (LiveDNS keys), email forwarding, and web/URL forwarding
3215
+ // (webredirs; per host, apex included). Note core `setPrivacy` disabling is a no-op for
3216
3216
  // individual (natural-person) registrants — Gandi keeps WHOIS obfuscation on
3217
3217
  // for GDPR regardless of the request.
3218
3218
  static extendedFeatures = [
@@ -3222,7 +3222,10 @@ var GandiRegistrar = class extends BaseRegistrar {
3222
3222
  Feature.GetEmailForwarding,
3223
3223
  Feature.SetEmailForwarding,
3224
3224
  Feature.GetDomainForwarding,
3225
- Feature.SetDomainForwarding
3225
+ Feature.SetDomainForwarding,
3226
+ Feature.GetTransferOut,
3227
+ Feature.ApproveTransferOut,
3228
+ Feature.RejectTransferOut
3226
3229
  ];
3227
3230
  constructor(credentials, options) {
3228
3231
  super(
@@ -3474,6 +3477,58 @@ var GandiRegistrar = class extends BaseRegistrar {
3474
3477
  });
3475
3478
  return d.authinfo ?? "";
3476
3479
  }
3480
+ /**
3481
+ * The domain's pending outbound transfer. Gandi has no transfer-out resource
3482
+ * to read: a domain in the account carries the documented `pendingTransfer`
3483
+ * EPP status while another registrar's request waits (inbound transfers live
3484
+ * under /domain/transferin, so on a domain you hold it's outbound — inferred,
3485
+ * not documented). Gandi reports no dates for it, and nothing once it's
3486
+ * answered or done, so this reads `pending` or null.
3487
+ */
3488
+ async getTransferOut(domainName, opts) {
3489
+ const d = await this.http.request({
3490
+ path: `/domain/domains/${domainName}`,
3491
+ ...opts
3492
+ });
3493
+ return pendingTransferOut(d);
3494
+ }
3495
+ /**
3496
+ * Approves the pending outbound transfer, releasing the domain now instead of
3497
+ * after Gandi's five days: POST .../transferout with `accept: true` and the
3498
+ * domain's current auth code, which Gandi requires back (→ 202).
3499
+ */
3500
+ async approveTransferOut(domainName, opts) {
3501
+ return this.answerTransferOut(domainName, true, opts);
3502
+ }
3503
+ // Declines the pending outbound transfer (`accept: false`, no reason); the
3504
+ // domain stays in the account.
3505
+ async rejectTransferOut(domainName, opts) {
3506
+ return this.answerTransferOut(domainName, false, opts);
3507
+ }
3508
+ // One read for both the pending status and the auth code the answer needs.
3509
+ async answerTransferOut(domainName, accept, opts) {
3510
+ let d;
3511
+ try {
3512
+ d = await this.http.request({
3513
+ path: `/domain/domains/${domainName}`,
3514
+ ...opts
3515
+ });
3516
+ } catch (error) {
3517
+ return { success: false, message: toRegistrarError(error).message };
3518
+ }
3519
+ if (!pendingTransferOut(d)) {
3520
+ return { success: false, message: `No transfer-out of ${domainName} to answer` };
3521
+ }
3522
+ return this.mutate(
3523
+ {
3524
+ method: "POST",
3525
+ path: `/domain/domains/${domainName}/transferout`,
3526
+ body: { accept, authinfo: d.authinfo ?? "" }
3527
+ },
3528
+ `Transfer of ${domainName} ${accept ? "approved" : "rejected"}`,
3529
+ opts
3530
+ );
3531
+ }
3477
3532
  /**
3478
3533
  * DNSSEC status via Gandi LiveDNS keys (GET /livedns/domains/{fqdn}/keys). A
3479
3534
  * non-deleted key means DNSSEC is enabled; each key's `ds` field is the full DS
@@ -3841,6 +3896,17 @@ var GandiRegistrar = class extends BaseRegistrar {
3841
3896
  }
3842
3897
  }
3843
3898
  };
3899
+ function pendingTransferOut(d) {
3900
+ const statuses = Array.isArray(d.status) ? d.status : d.status ? [d.status] : [];
3901
+ if (!statuses.includes("pendingTransfer")) return null;
3902
+ return {
3903
+ status: "pending",
3904
+ rawStatus: "pendingTransfer",
3905
+ requestedDate: null,
3906
+ expirationDate: null,
3907
+ completedDate: null
3908
+ };
3909
+ }
3844
3910
  function toGandiContact(c) {
3845
3911
  const contact = {
3846
3912
  given: c.firstName,
@@ -3981,17 +4047,22 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
3981
4047
  ];
3982
4048
  // GoDaddy's OTE ("Operational Test Environment") is its sandbox
3983
4049
  static supportsSandbox = true;
3984
- // Beyond core: domain forwarding and transfer-out auth code (the `authCode`
3985
- // field on the v1 domain-detail response). Forwarding uses the v2
3986
- // customer-scoped route /v2/customers/{customerId}/domains/forwards/{fqdn}
3987
- // (the old v1 /v1/domains/forwards routes now 404); it requires `customerId`
3988
- // to be configured — verified live 2026-08-29. DNSSEC has no dedicated
4050
+ // Beyond core: domain forwarding, transfer-out auth code (the `authCode`
4051
+ // field on the v1 domain-detail response) and transfer-out approval.
4052
+ // Forwarding uses the v2 customer-scoped route
4053
+ // /v2/customers/{customerId}/domains/forwards/{fqdn} (the old v1
4054
+ // /v1/domains/forwards routes now 404); it requires `customerId` to be
4055
+ // configured — verified live 2026-08-29. Transfer-out approval is v2
4056
+ // customer-scoped too, so it needs `customerId` as well. DNSSEC has no dedicated
3989
4057
  // endpoint (DS records go through the generic DNS API), and there's no
3990
4058
  // glue-record, email, or push-webhook API.
3991
4059
  static extendedFeatures = [
3992
4060
  Feature.GetAuthCode,
3993
4061
  Feature.GetDomainForwarding,
3994
- Feature.SetDomainForwarding
4062
+ Feature.SetDomainForwarding,
4063
+ Feature.GetTransferOut,
4064
+ Feature.ApproveTransferOut,
4065
+ Feature.RejectTransferOut
3995
4066
  ];
3996
4067
  // true → prefer the v3 API (production + PAT). false → all calls use v1
3997
4068
  // (sso-key auth, or OTE/sandbox where v3 does not exist).
@@ -4592,7 +4663,7 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
4592
4663
  const configured = this.credentials.customerId?.trim();
4593
4664
  if (!configured) {
4594
4665
  throw new ConfigurationError(
4595
- `${this.name}: domain forwarding uses 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).`
4666
+ `${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).`
4596
4667
  );
4597
4668
  }
4598
4669
  if (UUID_RE.test(configured)) return configured;
@@ -4692,6 +4763,52 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
4692
4763
  forwardPath(customerId, fqdn) {
4693
4764
  return `/v2/customers/${encodeURIComponent(customerId)}/domains/forwards/${encodeURIComponent(fqdn)}`;
4694
4765
  }
4766
+ // the v2 customer-scoped path for a domain
4767
+ domainPathV2(customerId, domainName) {
4768
+ return `/v2/customers/${encodeURIComponent(customerId)}/domains/${encodeURIComponent(domainName)}`;
4769
+ }
4770
+ /**
4771
+ * The domain's latest outbound transfer, read from its recent actions
4772
+ * (GET /v2/customers/{customerId}/domains/{domain}/actions): the TRANSFER_OUT
4773
+ * action, and an accept or reject logged after it. Null when there is no
4774
+ * TRANSFER_OUT on record. GoDaddy reports no expiry; left alone, it releases
4775
+ * the domain within seven days. Shapes from the published v2 spec; a real
4776
+ * transfer-out is not yet seen live.
4777
+ */
4778
+ async getTransferOut(domainName, opts) {
4779
+ const customerId = await this.resolveCustomerId(opts);
4780
+ const actions = await this.http.request({
4781
+ path: `${this.domainPathV2(customerId, domainName)}/actions`,
4782
+ ...opts
4783
+ });
4784
+ return toTransferOut2(Array.isArray(actions) ? actions : []);
4785
+ }
4786
+ /**
4787
+ * Approves the pending outbound transfer, releasing the domain now
4788
+ * (POST .../transferOutAccept → 202). GoDaddy answers 409 when none is
4789
+ * pending.
4790
+ */
4791
+ async approveTransferOut(domainName, opts) {
4792
+ return this.answerTransferOut(domainName, "transferOutAccept", "approved", opts);
4793
+ }
4794
+ // Rejects the pending outbound transfer (POST .../transferOutReject, with no
4795
+ // reason); the domain stays in the account.
4796
+ async rejectTransferOut(domainName, opts) {
4797
+ return this.answerTransferOut(domainName, "transferOutReject", "rejected", opts);
4798
+ }
4799
+ async answerTransferOut(domainName, action, answer, opts) {
4800
+ let customerId;
4801
+ try {
4802
+ customerId = await this.resolveCustomerId(opts);
4803
+ } catch (error) {
4804
+ return { success: false, message: toRegistrarError(error).message };
4805
+ }
4806
+ return this.mutate(
4807
+ { method: "POST", path: `${this.domainPathV2(customerId, domainName)}/${action}` },
4808
+ `Transfer of ${domainName} ${answer}`,
4809
+ opts
4810
+ );
4811
+ }
4695
4812
  /**
4696
4813
  * Transfer authorization (EPP) code. GoDaddy exposes it as the `authCode`
4697
4814
  * field on the v1 domain-detail response; transfers were never moved to v3,
@@ -4748,6 +4865,41 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
4748
4865
  }
4749
4866
  }
4750
4867
  };
4868
+ function toTransferOut2(actions) {
4869
+ const time = (a) => parseDate(a.createdAt)?.getTime() ?? 0;
4870
+ const byLatest = (types) => actions.filter((a) => types.includes(a.type ?? "")).sort((a, b) => time(b) - time(a));
4871
+ const transfer = byLatest(["TRANSFER_OUT"])[0];
4872
+ if (!transfer) return null;
4873
+ const answer = byLatest(["TRANSFER_OUT_ACCEPT", "TRANSFER_OUT_REJECT"]).find(
4874
+ (a) => time(a) >= time(transfer) && !/^(FAILED|CANCELLED)$/i.test(a.status ?? "")
4875
+ );
4876
+ const rejected = answer?.type === "TRANSFER_OUT_REJECT";
4877
+ const rawStatus = transfer.status ?? "";
4878
+ let status;
4879
+ switch (rawStatus.toUpperCase()) {
4880
+ case "ACCEPTED":
4881
+ case "AWAITING":
4882
+ case "PENDING":
4883
+ status = !answer ? "pending" : rejected ? "rejected" : "approved";
4884
+ break;
4885
+ case "SUCCESS":
4886
+ status = "completed";
4887
+ break;
4888
+ case "FAILED":
4889
+ case "CANCELLED":
4890
+ status = rejected ? "rejected" : "cancelled";
4891
+ break;
4892
+ default:
4893
+ status = "unknown";
4894
+ }
4895
+ return {
4896
+ status,
4897
+ rawStatus,
4898
+ requestedDate: parseDate(transfer.createdAt),
4899
+ expirationDate: null,
4900
+ completedDate: status === "completed" ? parseDate(transfer.completedAt) : null
4901
+ };
4902
+ }
4751
4903
  function fromGoDaddyContact(c) {
4752
4904
  if (!c) return void 0;
4753
4905
  const a = c.addressMailing ?? {};