@aoxborrow/registrar-client 0.10.1 → 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 CHANGED
@@ -187,6 +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
193
  | `getDnssec` | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ᵐ | ✗ |
191
194
  | `disableDnssec` | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ᵐ | ✗ |
192
195
  | `getEmailForwarding` | ✓ | ✓ | ✓ | ✗ | ✗ | ✓ | ✓ | ✗ | ✗ | ✗ᵐ | ✗ |
@@ -232,6 +235,16 @@ not a claim of available API endpoints. See [101domain](docs/registrars/101domai
232
235
  - **ʲ** Dynadot and NameSilo forward the whole domain (a single `@` rule);
233
236
  clearing a forward restores the default nameservers.
234
237
  - **ᵏ** Gandi web redirects are per host, apex included; creating one replaces the host's DNS record.
238
+ - **ⁿ** Transfer-out approval answers a transfer another registrar requested with
239
+ the auth code. Dynadot's is sandbox-verified only up to "no such order" (its
240
+ sandbox can't start an outbound transfer); a real pending one's read was
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.
235
248
  - **ˡ** GoDaddy domain forwarding uses the v2 customer-scoped API, so it requires
236
249
  a `customerId` (the customer UUID, or a numeric shopper ID the client resolves to
237
250
  the UUID via an sso-key-only lookup); the old v1 forwarding route was removed.
@@ -335,6 +348,19 @@ the registrant); Namecheap requires all four roles; Dynadot and Porkbun use the
335
348
  account's default WHOIS contact. Transfers carry over the existing contacts, so
336
349
  most providers need only the auth code (`TransferDomainInput`).
337
350
 
351
+ Going the other way, a sold or departing name leaves with its auth code
352
+ (`getAuthCode`). When the buyer's registrar requests it, the losing registrar
353
+ holds the transfer for a few days unless the owner answers it; where the API
354
+ allows, `getTransferOut` reads that request and `approveTransferOut` /
355
+ `rejectTransferOut` answer it:
356
+
357
+ ```ts
358
+ if (provider.supports(Feature.GetTransferOut)) {
359
+ const transfer = await provider.getTransferOut('example.com'); // null if none
360
+ if (transfer?.status === 'pending') await provider.approveTransferOut('example.com');
361
+ }
362
+ ```
363
+
338
364
  > `registerDomain` is implemented for **every bundled provider**, and `transferIn`
339
365
  > for every one whose API has a transfer endpoint (all except **Cloudflare** and
340
366
  > **NameBright**). `registerDomain` has been exercised live against several
package/dist/index.cjs CHANGED
@@ -685,6 +685,12 @@ var Feature = {
685
685
  // --- extended (opt-in; declared per provider) ---
686
686
  GetAuthCode: "getAuthCode",
687
687
  // retrieve transfer auth/EPP code (transfer out)
688
+ GetTransferOut: "getTransferOut",
689
+ // read an outbound transfer another registrar requested
690
+ ApproveTransferOut: "approveTransferOut",
691
+ // release a pending outbound transfer now
692
+ RejectTransferOut: "rejectTransferOut",
693
+ // refuse a pending outbound transfer
688
694
  GetDnssec: "getDnssec",
689
695
  // read whether DNSSEC is enabled (DS / key records)
690
696
  DisableDnssec: "disableDnssec",
@@ -720,6 +726,9 @@ var CORE_FEATURES = [
720
726
  ];
721
727
  var EXTENDED_FEATURES = [
722
728
  Feature.GetAuthCode,
729
+ Feature.GetTransferOut,
730
+ Feature.ApproveTransferOut,
731
+ Feature.RejectTransferOut,
723
732
  Feature.GetDnssec,
724
733
  Feature.DisableDnssec,
725
734
  Feature.GetEmailForwarding,
@@ -752,6 +761,9 @@ var FEATURE_CALLS = {
752
761
  getDnsRecords: { intent: "read", optsIndex: 1 },
753
762
  setDnsRecords: { intent: "write", optsIndex: 2 },
754
763
  getAuthCode: { intent: "write", optsIndex: 1 },
764
+ getTransferOut: { intent: "read", optsIndex: 1 },
765
+ approveTransferOut: { intent: "write", optsIndex: 1 },
766
+ rejectTransferOut: { intent: "write", optsIndex: 1 },
755
767
  getDnssec: { intent: "read", optsIndex: 1 },
756
768
  disableDnssec: { intent: "write", optsIndex: 1 },
757
769
  getEmailForwarding: { intent: "read", optsIndex: 1 },
@@ -913,6 +925,15 @@ var BaseRegistrar = class {
913
925
  getAuthCode(_domainName, _opts) {
914
926
  return this.notImplemented("getAuthCode");
915
927
  }
928
+ getTransferOut(_domainName, _opts) {
929
+ return this.notImplemented("getTransferOut");
930
+ }
931
+ approveTransferOut(_domainName, _opts) {
932
+ return this.notImplemented("approveTransferOut");
933
+ }
934
+ rejectTransferOut(_domainName, _opts) {
935
+ return this.notImplemented("rejectTransferOut");
936
+ }
916
937
  getDnssec(_domainName, _opts) {
917
938
  return this.notImplemented("getDnssec");
918
939
  }
@@ -2333,11 +2354,16 @@ var DynadotRegistrar = class extends BaseRegistrar {
2333
2354
  ];
2334
2355
  static supportsSandbox = true;
2335
2356
  // api-sandbox.dynadot.com mirrors the API
2336
- // On top of core: transfer-out auth code, DNSSEC read/disable, and email +
2337
- // domain (URL) forwarding. All implemented on the v2 transport and verified in
2338
- // the Dynadot sandbox.
2357
+ // On top of core: transfer-out auth code and approval, DNSSEC read/disable,
2358
+ // and email + domain (URL) forwarding. All implemented on the v2 transport and
2359
+ // verified in the Dynadot sandbox (transfer-out approval only up to "no such
2360
+ // order": the sandbox can't create an outbound transfer; a real pending
2361
+ // one's read was checked in production).
2339
2362
  static extendedFeatures = [
2340
2363
  Feature.GetAuthCode,
2364
+ Feature.GetTransferOut,
2365
+ Feature.ApproveTransferOut,
2366
+ Feature.RejectTransferOut,
2341
2367
  Feature.GetDnssec,
2342
2368
  Feature.DisableDnssec,
2343
2369
  Feature.GetEmailForwarding,
@@ -2808,6 +2834,64 @@ ${bodyStr}`;
2808
2834
  );
2809
2835
  return data.auth_code ?? "";
2810
2836
  }
2837
+ /**
2838
+ * The domain's latest outbound transfer via GET .../transfer_status with
2839
+ * `transfer_type=transfer_away`, which lists its transfer-away orders. Dynadot
2840
+ * answers 404 when there are none (and for a domain not in the account), read
2841
+ * here as null. An open order wins over a newer finished one, so approve and
2842
+ * reject act on the order that can still be answered.
2843
+ */
2844
+ async getTransferOut(domainName, opts) {
2845
+ let env;
2846
+ try {
2847
+ env = await this.signedRequest(
2848
+ "GET",
2849
+ `/restful/v2/domains/${encodeURIComponent(domainName)}/transfer_status?transfer_type=transfer_away`,
2850
+ void 0,
2851
+ opts
2852
+ );
2853
+ } catch (error) {
2854
+ if (error instanceof NotFoundError) return null;
2855
+ throw error;
2856
+ }
2857
+ if (env.code === 404) return null;
2858
+ if (!isOk(env.code)) {
2859
+ throw new Error(env.error?.description ?? env.message ?? "Dynadot request failed");
2860
+ }
2861
+ const transfers = (env.data?.domain_transfer_status_list ?? []).map(toTransferOut).sort((a, b) => (b.requestedDate?.getTime() ?? 0) - (a.requestedDate?.getTime() ?? 0));
2862
+ return transfers.find((t) => OPEN_TRANSFER_OUT.has(t.status)) ?? transfers[0] ?? null;
2863
+ }
2864
+ /**
2865
+ * Approves the pending outbound transfer, releasing the domain now instead of
2866
+ * after Dynadot's 5-day wait. Answers the order `getTransferOut` reports.
2867
+ */
2868
+ async approveTransferOut(domainName, opts) {
2869
+ return this.authorizeTransferAway(domainName, true, opts);
2870
+ }
2871
+ // Denies the pending outbound transfer; the domain stays in the account.
2872
+ async rejectTransferOut(domainName, opts) {
2873
+ return this.authorizeTransferAway(domainName, false, opts);
2874
+ }
2875
+ // POST /orders/{order_id}/authorize_transfer_away with `approve` true or
2876
+ // false. Dynadot answers 409 when the order is no longer waiting on the owner.
2877
+ async authorizeTransferAway(domainName, approve, opts) {
2878
+ let transfer;
2879
+ try {
2880
+ transfer = await this.getTransferOut(domainName, opts);
2881
+ } catch (error) {
2882
+ return { success: false, message: toRegistrarError(error).message };
2883
+ }
2884
+ if (!transfer?.id) {
2885
+ return { success: false, message: `No transfer-out of ${domainName} to answer` };
2886
+ }
2887
+ return this.mutate(
2888
+ "POST",
2889
+ `/restful/v2/orders/${encodeURIComponent(transfer.id)}/authorize_transfer_away`,
2890
+ { domain_name: domainName, approve },
2891
+ `Transfer of ${domainName} ${approve ? "approved" : "rejected"}`,
2892
+ opts
2893
+ );
2894
+ }
2811
2895
  /**
2812
2896
  * DNSSEC status via GET .../dnssec, which returns a list of DS records
2813
2897
  * (`dnssec_info_list`); a non-empty list means DNSSEC is enabled. `algorithm`
@@ -2956,6 +3040,32 @@ async function hmacSha256Base64(secret, message) {
2956
3040
  for (const byte of new Uint8Array(signature)) binary += String.fromCharCode(byte);
2957
3041
  return btoa(binary);
2958
3042
  }
3043
+ var OPEN_TRANSFER_OUT = /* @__PURE__ */ new Set(["pending", "unknown"]);
3044
+ function toTransferOut(t) {
3045
+ const rawStatus = t.transfer_status ?? "";
3046
+ let status = transferOutStatus(rawStatus);
3047
+ if (status === "unknown" && msDate(t.order_completed_date)) status = "completed";
3048
+ return {
3049
+ status,
3050
+ rawStatus,
3051
+ id: str(t.order_id) || void 0,
3052
+ requestedDate: msDate(t.order_created_date),
3053
+ expirationDate: msDate(t.expiration_date),
3054
+ completedDate: msDate(t.order_completed_date)
3055
+ };
3056
+ }
3057
+ function transferOutStatus(label) {
3058
+ const s = label.toLowerCase();
3059
+ if (/den(y|ied)|reject|refus|not approved|nack/.test(s)) return "rejected";
3060
+ if (/cancel|fail|expire|time ?out|abort/.test(s)) return "cancelled";
3061
+ if (/complete|transferr?ed|success/.test(s)) return "completed";
3062
+ if (/pend|wait|await|need|request|none|new|process|hold|review/.test(s)) return "pending";
3063
+ if (/approv|authori[sz]|accept/.test(s)) return "approved";
3064
+ return "unknown";
3065
+ }
3066
+ function msDate(ms) {
3067
+ return typeof ms === "number" && ms > 0 ? new Date(ms) : null;
3068
+ }
2959
3069
  function isOk(code) {
2960
3070
  return code != null && code >= 200 && code < 300;
2961
3071
  }
@@ -3102,9 +3212,9 @@ var GandiRegistrar = class extends BaseRegistrar {
3102
3212
  ];
3103
3213
  // Gandi's v5 API offers a sandbox at api.sandbox.gandi.net (separate account)
3104
3214
  static supportsSandbox = true;
3105
- // On top of core: transfer-out auth code (`authinfo`), DNSSEC read/disable
3106
- // (LiveDNS keys), email forwarding, and web/URL forwarding (webredirs;
3107
- // 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
3108
3218
  // individual (natural-person) registrants — Gandi keeps WHOIS obfuscation on
3109
3219
  // for GDPR regardless of the request.
3110
3220
  static extendedFeatures = [
@@ -3114,7 +3224,10 @@ var GandiRegistrar = class extends BaseRegistrar {
3114
3224
  Feature.GetEmailForwarding,
3115
3225
  Feature.SetEmailForwarding,
3116
3226
  Feature.GetDomainForwarding,
3117
- Feature.SetDomainForwarding
3227
+ Feature.SetDomainForwarding,
3228
+ Feature.GetTransferOut,
3229
+ Feature.ApproveTransferOut,
3230
+ Feature.RejectTransferOut
3118
3231
  ];
3119
3232
  constructor(credentials, options) {
3120
3233
  super(
@@ -3366,6 +3479,58 @@ var GandiRegistrar = class extends BaseRegistrar {
3366
3479
  });
3367
3480
  return d.authinfo ?? "";
3368
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
+ }
3369
3534
  /**
3370
3535
  * DNSSEC status via Gandi LiveDNS keys (GET /livedns/domains/{fqdn}/keys). A
3371
3536
  * non-deleted key means DNSSEC is enabled; each key's `ds` field is the full DS
@@ -3733,6 +3898,17 @@ var GandiRegistrar = class extends BaseRegistrar {
3733
3898
  }
3734
3899
  }
3735
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
+ }
3736
3912
  function toGandiContact(c) {
3737
3913
  const contact = {
3738
3914
  given: c.firstName,
@@ -3873,17 +4049,22 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
3873
4049
  ];
3874
4050
  // GoDaddy's OTE ("Operational Test Environment") is its sandbox
3875
4051
  static supportsSandbox = true;
3876
- // Beyond core: domain forwarding and transfer-out auth code (the `authCode`
3877
- // field on the v1 domain-detail response). Forwarding uses the v2
3878
- // customer-scoped route /v2/customers/{customerId}/domains/forwards/{fqdn}
3879
- // (the old v1 /v1/domains/forwards routes now 404); it requires `customerId`
3880
- // to be configured — verified live 2026-08-29. DNSSEC has no dedicated
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
3881
4059
  // endpoint (DS records go through the generic DNS API), and there's no
3882
4060
  // glue-record, email, or push-webhook API.
3883
4061
  static extendedFeatures = [
3884
4062
  Feature.GetAuthCode,
3885
4063
  Feature.GetDomainForwarding,
3886
- Feature.SetDomainForwarding
4064
+ Feature.SetDomainForwarding,
4065
+ Feature.GetTransferOut,
4066
+ Feature.ApproveTransferOut,
4067
+ Feature.RejectTransferOut
3887
4068
  ];
3888
4069
  // true → prefer the v3 API (production + PAT). false → all calls use v1
3889
4070
  // (sso-key auth, or OTE/sandbox where v3 does not exist).
@@ -4484,7 +4665,7 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
4484
4665
  const configured = this.credentials.customerId?.trim();
4485
4666
  if (!configured) {
4486
4667
  throw new ConfigurationError(
4487
- `${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).`
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).`
4488
4669
  );
4489
4670
  }
4490
4671
  if (UUID_RE.test(configured)) return configured;
@@ -4584,6 +4765,52 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
4584
4765
  forwardPath(customerId, fqdn) {
4585
4766
  return `/v2/customers/${encodeURIComponent(customerId)}/domains/forwards/${encodeURIComponent(fqdn)}`;
4586
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
+ }
4587
4814
  /**
4588
4815
  * Transfer authorization (EPP) code. GoDaddy exposes it as the `authCode`
4589
4816
  * field on the v1 domain-detail response; transfers were never moved to v3,
@@ -4640,6 +4867,41 @@ var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
4640
4867
  }
4641
4868
  }
4642
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
+ }
4643
4905
  function fromGoDaddyContact(c) {
4644
4906
  if (!c) return void 0;
4645
4907
  const a = c.addressMailing ?? {};