mailchannels-sdk 0.8.0 → 1.1.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.
@@ -1,10 +1,8 @@
1
1
  import { $fetch } from "ofetch";
2
2
  import { subtle } from "node:crypto";
3
3
  import { Buffer } from "node:buffer";
4
- import { basename } from "node:path";
5
- import { readFile } from "node:fs/promises";
6
4
  import mime from "mime";
7
- var version = "0.8.0";
5
+ var version = "1.1.0";
8
6
  var MailChannelsClient = class MailChannelsClient {
9
7
  static DEFAULT_BASE_URL = "https://api.mailchannels.net";
10
8
  static DEFAULT_TIMEOUT = 12e4;
@@ -70,10 +68,22 @@ var MailChannelsClient = class MailChannelsClient {
70
68
  });
71
69
  }
72
70
  };
73
- const createError = (message, statusCode = null) => {
71
+ const STATUS_ERROR_TYPE_MAP = {
72
+ [400]: "invalid_request_error",
73
+ [401]: "authentication_error",
74
+ [403]: "permission_error",
75
+ [404]: "not_found",
76
+ [409]: "conflict_error",
77
+ [413]: "payload_too_large_error",
78
+ [422]: "unprocessable_entity_error",
79
+ [429]: "rate_limit_error",
80
+ [500]: "internal_server_error"
81
+ };
82
+ const createError = (message, statusCode = null, type) => {
74
83
  return {
75
84
  message,
76
- statusCode
85
+ statusCode,
86
+ type
77
87
  };
78
88
  };
79
89
  const getStatusError = (response, errors = {}) => {
@@ -83,15 +93,18 @@ const getStatusError = (response, errors = {}) => {
83
93
  if (typeof payload === "string") details = payload;
84
94
  else if (payload?.message) details = payload.message;
85
95
  else if (Array.isArray(payload?.errors) && payload.errors?.length) details = payload.errors.join(", ");
86
- return createError(details ? `${statusText} ${details}` : statusText, response.status ?? null);
96
+ return createError(details ? `${statusText} ${details}` : statusText, response.status ?? null, STATUS_ERROR_TYPE_MAP[response.status] || "api_error");
87
97
  };
88
98
  const getResultError = (e, fallback) => {
89
- return createError(e instanceof Error ? e.message : fallback);
99
+ return createError(e instanceof Error ? e.message : fallback, null, "application_error");
100
+ };
101
+ const createValidationError = (message) => {
102
+ return createError(message, null, "validation_error");
90
103
  };
91
104
  const validatePagination = (pagination = {}) => {
92
105
  const { limit, offset, max } = pagination;
93
- if (typeof limit === "number" && (limit < 1 || max && limit > max)) return createError("The limit value " + (max ? `must be between 1 and ${max}.` : "is invalid. Only positive values are allowed."));
94
- if (typeof offset === "number" && offset < 0) return createError("Offset must be greater than or equal to 0.");
106
+ if (typeof limit === "number" && (limit < 1 || max && limit > max)) return createValidationError("The limit value " + (max ? `must be between 1 and ${max}.` : "is invalid. Only positive values are allowed."));
107
+ if (typeof offset === "number" && offset < 0) return createValidationError("Offset must be greater than or equal to 0.");
95
108
  return null;
96
109
  };
97
110
  const clean = (data) => {
@@ -147,7 +160,7 @@ const parseArrayRecipients = (recipients) => {
147
160
  return filtered.length > 0 ? filtered : void 0;
148
161
  };
149
162
  const stripPemHeaders = (pem) => pem.replace(/-----[^-]+-----|\s|#.*$/gm, "");
150
- const RESERVED_HEADER_NAMES = new Set([
163
+ const RESERVED_HEADER_NAMES = /* @__PURE__ */ new Set([
151
164
  "authentication-results",
152
165
  "bcc",
153
166
  "cc",
@@ -243,7 +256,8 @@ const buildSendPayload = async (options) => {
243
256
  const { from, html, text } = options;
244
257
  const contentTypes = options.content ? new Set(options.content.map((item) => item.type.toLowerCase())) : void 0;
245
258
  const parsedFrom = parseRecipient(from);
246
- if (!parsedFrom || !parsedFrom.email) return "No sender provided. Use the `from` option to specify a sender";
259
+ if (!parsedFrom || !parsedFrom.email) return "No sender provided. Use the 'from' option to specify a sender.";
260
+ if (!options.subject || typeof options.subject !== "string" || !options.subject.trim()) return "No subject provided. Use the 'subject' option to specify a subject.";
247
261
  if (!text && !html && (!options.content || !options.content.length)) return "No email content provided";
248
262
  if (html && contentTypes?.has("text/html")) return "Cannot provide both 'html' and a 'content' entry with type 'text/html'.";
249
263
  if (text && contentTypes?.has("text/plain")) return "Cannot provide both 'text' and a 'content' entry with type 'text/plain'.";
@@ -337,7 +351,7 @@ var Emails = class {
337
351
  let error = null;
338
352
  const payload = await buildSendPayload(options);
339
353
  if (typeof payload === "string") {
340
- error = createError(payload);
354
+ error = createValidationError(payload);
341
355
  return {
342
356
  data: null,
343
357
  error
@@ -410,80 +424,6 @@ const mapDkimKey = (key) => ({
410
424
  status: key.status,
411
425
  statusModifiedAt: key.status_modified_at
412
426
  });
413
- var Domains = class {
414
- mailchannels;
415
- dkim;
416
- constructor(mailchannels) {
417
- this.mailchannels = mailchannels;
418
- this.dkim = new DomainsDkim(mailchannels);
419
- }
420
- async check(domain, options) {
421
- let error = null;
422
- if (!domain) {
423
- error = createError("No domain provided.");
424
- return {
425
- data: null,
426
- error
427
- };
428
- }
429
- const dkimOptions = options?.dkim ? Array.isArray(options.dkim) ? options.dkim : [options.dkim] : void 0;
430
- if (dkimOptions && dkimOptions.length > 10) {
431
- error = createError("A maximum of 10 DKIM settings can be provided.");
432
- return {
433
- data: null,
434
- error
435
- };
436
- }
437
- if (dkimOptions?.find(({ privateKey, selector }) => privateKey && !selector)) {
438
- error = createError("DKIM settings with a privateKey must also include a selector.");
439
- return {
440
- data: null,
441
- error
442
- };
443
- }
444
- const payload = {
445
- dkim_settings: dkimOptions?.map((dkim) => ({
446
- dkim_domain: dkim.domain,
447
- dkim_private_key: dkim.privateKey ? stripPemHeaders(dkim.privateKey) : void 0,
448
- dkim_selector: dkim.selector
449
- })),
450
- domain,
451
- sender_id: options?.senderId
452
- };
453
- const response = await this.mailchannels.post("/tx/v1/check-domain", {
454
- body: payload,
455
- onResponseError: async ({ response }) => {
456
- error = getStatusError(response, {
457
- [400]: "Bad Request.",
458
- [403]: "User does not have access to this feature."
459
- });
460
- }
461
- }).catch((e) => {
462
- error ||= getResultError(e, "Failed to check domain.");
463
- return null;
464
- });
465
- if (!response) return {
466
- data: null,
467
- error
468
- };
469
- return {
470
- data: clean({
471
- dkim: response.check_results.dkim.map((dkimResults) => ({
472
- domain: dkimResults.dkim_domain,
473
- keyStatus: dkimResults.dkim_key_status,
474
- selector: dkimResults.dkim_selector,
475
- reason: dkimResults.reason,
476
- verdict: dkimResults.verdict
477
- })),
478
- domainLockdown: response.check_results.domain_lockdown,
479
- senderDomain: response.check_results.sender_domain,
480
- spf: response.check_results.spf,
481
- references: response.references
482
- }),
483
- error: null
484
- };
485
- }
486
- };
487
427
  var DomainsDkim = class {
488
428
  mailchannels;
489
429
  constructor(mailchannels) {
@@ -492,14 +432,14 @@ var DomainsDkim = class {
492
432
  async create(domain, options) {
493
433
  let error = null;
494
434
  if (!domain) {
495
- error = createError("No domain provided.");
435
+ error = createValidationError("No domain provided.");
496
436
  return {
497
437
  data: null,
498
438
  error
499
439
  };
500
440
  }
501
441
  if (!options.selector || options.selector.length > 63) {
502
- error = createError("Selector must be between 1 and 63 characters.");
442
+ error = createValidationError("Selector must be between 1 and 63 characters.");
503
443
  return {
504
444
  data: null,
505
445
  error
@@ -510,7 +450,7 @@ var DomainsDkim = class {
510
450
  key_length: options.length,
511
451
  selector: options.selector
512
452
  };
513
- const response = await this.mailchannels.post(`/tx/v1/domains/${domain}/dkim-keys`, {
453
+ const response = await this.mailchannels.post(`/tx/v1/domains/${encodeURIComponent(domain)}/dkim-keys`, {
514
454
  body: payload,
515
455
  onResponseError: async ({ response }) => {
516
456
  error = getStatusError(response, {
@@ -534,14 +474,14 @@ var DomainsDkim = class {
534
474
  async list(domain, options) {
535
475
  let error = null;
536
476
  if (!domain) {
537
- error = createError("No domain provided.");
477
+ error = createValidationError("No domain provided.");
538
478
  return {
539
479
  data: null,
540
480
  error
541
481
  };
542
482
  }
543
483
  if (options?.selector && options.selector.length > 63) {
544
- error = createError("Selector must be between 1 and 63 characters.");
484
+ error = createValidationError("Selector must be between 1 and 63 characters.");
545
485
  return {
546
486
  data: null,
547
487
  error
@@ -562,7 +502,7 @@ var DomainsDkim = class {
562
502
  limit: options?.limit,
563
503
  include_dns_record: options?.includeDnsRecord
564
504
  };
565
- const response = await this.mailchannels.get(`/tx/v1/domains/${domain}/dkim-keys`, {
505
+ const response = await this.mailchannels.get(`/tx/v1/domains/${encodeURIComponent(domain)}/dkim-keys`, {
566
506
  query: payload,
567
507
  onResponseError: async ({ response }) => {
568
508
  error = getStatusError(response, { [400]: "Bad Request." });
@@ -583,21 +523,21 @@ var DomainsDkim = class {
583
523
  async updateStatus(domain, options) {
584
524
  let error = null;
585
525
  if (!domain) {
586
- error = createError("No domain provided.");
526
+ error = createValidationError("No domain provided.");
587
527
  return {
588
528
  success: false,
589
529
  error
590
530
  };
591
531
  }
592
532
  if (!options.selector || options.selector.length > 63) {
593
- error = createError("Selector must be between 1 and 63 characters.");
533
+ error = createValidationError("Selector must be between 1 and 63 characters.");
594
534
  return {
595
535
  success: false,
596
536
  error
597
537
  };
598
538
  }
599
539
  const payload = { status: options.status };
600
- await this.mailchannels.patch(`/tx/v1/domains/${domain}/dkim-keys/${options.selector}`, {
540
+ await this.mailchannels.patch(`/tx/v1/domains/${encodeURIComponent(domain)}/dkim-keys/${encodeURIComponent(options.selector)}`, {
601
541
  body: payload,
602
542
  onResponseError: async ({ response }) => {
603
543
  error = getStatusError(response, {
@@ -616,28 +556,28 @@ var DomainsDkim = class {
616
556
  async rotate(domain, selector, options) {
617
557
  let error = null;
618
558
  if (!domain) {
619
- error = createError("No domain provided.");
559
+ error = createValidationError("No domain provided.");
620
560
  return {
621
561
  data: null,
622
562
  error
623
563
  };
624
564
  }
625
565
  if (!selector || selector.length > 63) {
626
- error = createError("Selector must be between 1 and 63 characters.");
566
+ error = createValidationError("Selector must be between 1 and 63 characters.");
627
567
  return {
628
568
  data: null,
629
569
  error
630
570
  };
631
571
  }
632
572
  if (!options.newKey.selector || options.newKey.selector.length > 63) {
633
- error = createError("New key selector must be between 1 and 63 characters.");
573
+ error = createValidationError("New key selector must be between 1 and 63 characters.");
634
574
  return {
635
575
  data: null,
636
576
  error
637
577
  };
638
578
  }
639
579
  const payload = { new_key: { selector: options.newKey.selector } };
640
- const response = await this.mailchannels.post(`/tx/v1/domains/${domain}/dkim-keys/${selector}/rotate`, {
580
+ const response = await this.mailchannels.post(`/tx/v1/domains/${encodeURIComponent(domain)}/dkim-keys/${encodeURIComponent(selector)}/rotate`, {
641
581
  body: payload,
642
582
  onResponseError: async ({ response }) => {
643
583
  error = getStatusError(response, {
@@ -663,6 +603,80 @@ var DomainsDkim = class {
663
603
  };
664
604
  }
665
605
  };
606
+ var Domains = class {
607
+ mailchannels;
608
+ dkim;
609
+ constructor(mailchannels) {
610
+ this.mailchannels = mailchannels;
611
+ this.dkim = new DomainsDkim(mailchannels);
612
+ }
613
+ async check(domain, options) {
614
+ let error = null;
615
+ if (!domain) {
616
+ error = createValidationError("No domain provided.");
617
+ return {
618
+ data: null,
619
+ error
620
+ };
621
+ }
622
+ const dkimOptions = options?.dkim ? Array.isArray(options.dkim) ? options.dkim : [options.dkim] : void 0;
623
+ if (dkimOptions && dkimOptions.length > 10) {
624
+ error = createValidationError("A maximum of 10 DKIM settings can be provided.");
625
+ return {
626
+ data: null,
627
+ error
628
+ };
629
+ }
630
+ if (dkimOptions?.find(({ privateKey, selector }) => privateKey && !selector)) {
631
+ error = createValidationError("DKIM settings with a privateKey must also include a selector.");
632
+ return {
633
+ data: null,
634
+ error
635
+ };
636
+ }
637
+ const payload = {
638
+ dkim_settings: dkimOptions?.map((dkim) => ({
639
+ dkim_domain: dkim.domain,
640
+ dkim_private_key: dkim.privateKey ? stripPemHeaders(dkim.privateKey) : void 0,
641
+ dkim_selector: dkim.selector
642
+ })),
643
+ domain,
644
+ sender_id: options?.senderId
645
+ };
646
+ const response = await this.mailchannels.post("/tx/v1/check-domain", {
647
+ body: payload,
648
+ onResponseError: async ({ response }) => {
649
+ error = getStatusError(response, {
650
+ [400]: "Bad Request.",
651
+ [403]: "User does not have access to this feature."
652
+ });
653
+ }
654
+ }).catch((e) => {
655
+ error ||= getResultError(e, "Failed to check domain.");
656
+ return null;
657
+ });
658
+ if (!response) return {
659
+ data: null,
660
+ error
661
+ };
662
+ return {
663
+ data: clean({
664
+ dkim: response.check_results.dkim.map((dkimResults) => ({
665
+ domain: dkimResults.dkim_domain,
666
+ keyStatus: dkimResults.dkim_key_status,
667
+ selector: dkimResults.dkim_selector,
668
+ reason: dkimResults.reason,
669
+ verdict: dkimResults.verdict
670
+ })),
671
+ domainLockdown: response.check_results.domain_lockdown,
672
+ senderDomain: response.check_results.sender_domain,
673
+ spf: response.check_results.spf,
674
+ references: response.references
675
+ }),
676
+ error: null
677
+ };
678
+ }
679
+ };
666
680
  const HMAC_SHA256 = {
667
681
  name: "HMAC",
668
682
  hash: "SHA-256"
@@ -735,6 +749,35 @@ async function isValidWebhook(options) {
735
749
  const key = await subtle.importKey(format, publicKeyBuffer, ED25519, false, ["verify"]);
736
750
  return subtle.verify(ED25519.name, key, webhookSignatureBuffer, encoder.encode(signingString));
737
751
  }
752
+ const ISO_DATE_REGEX = /^\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})$/;
753
+ const ISO_DATE_ONLY_REGEX = /^\d{4}-\d{2}-\d{2}$/;
754
+ const formatDateInput = (value) => {
755
+ if (value instanceof Date) {
756
+ if (Number.isNaN(value.getTime())) return null;
757
+ return value.toISOString();
758
+ }
759
+ if (typeof value === "string") {
760
+ if (!ISO_DATE_REGEX.test(value) && !ISO_DATE_ONLY_REGEX.test(value) || Number.isNaN(Date.parse(value))) return null;
761
+ if (ISO_DATE_REGEX.test(value)) return new Date(value).toISOString();
762
+ }
763
+ return value;
764
+ };
765
+ const parseDateInputs = (options) => {
766
+ const dates = {};
767
+ const keys = Object.keys(options);
768
+ for (const key of keys) {
769
+ const formatted = formatDateInput(options?.[key]);
770
+ if (formatted === null) return {
771
+ dates: null,
772
+ error: createValidationError(`The '${key}' value is not a valid date.`)
773
+ };
774
+ dates[key] = formatted;
775
+ }
776
+ return {
777
+ dates,
778
+ error: null
779
+ };
780
+ };
738
781
  var Webhooks = class Webhooks {
739
782
  mailchannels;
740
783
  constructor(mailchannels) {
@@ -743,14 +786,14 @@ var Webhooks = class Webhooks {
743
786
  async create(endpoint) {
744
787
  let error = null;
745
788
  if (!endpoint) {
746
- error = createError("No endpoint provided.");
789
+ error = createValidationError("No endpoint provided.");
747
790
  return {
748
791
  success: false,
749
792
  error
750
793
  };
751
794
  }
752
795
  if (endpoint.length > 8e3) {
753
- error = createError("The endpoint exceeds the maximum length of 8000 characters.");
796
+ error = createValidationError("The endpoint exceeds the maximum length of 8000 characters.");
754
797
  return {
755
798
  success: false,
756
799
  error
@@ -827,7 +870,7 @@ var Webhooks = class Webhooks {
827
870
  async validate(requestId) {
828
871
  let error = null;
829
872
  if (requestId && requestId.length > 28) {
830
- error = createError("The request id should not exceed 28 characters.");
873
+ error = createValidationError("The request id should not exceed 28 characters.");
831
874
  return {
832
875
  data: null,
833
876
  error
@@ -860,7 +903,7 @@ var Webhooks = class Webhooks {
860
903
  static async verify(options) {
861
904
  let error = null;
862
905
  if (!await isValidWebhook(options).catch(() => false)) {
863
- error = createError("Invalid webhook signature.");
906
+ error = createValidationError("Invalid webhook signature.");
864
907
  return {
865
908
  data: null,
866
909
  error
@@ -869,7 +912,7 @@ var Webhooks = class Webhooks {
869
912
  try {
870
913
  const payload = JSON.parse(options.payload);
871
914
  if (!Array.isArray(payload)) {
872
- error = createError("Invalid webhook payload.");
915
+ error = createValidationError("Invalid webhook payload.");
873
916
  return {
874
917
  data: null,
875
918
  error
@@ -894,7 +937,7 @@ var Webhooks = class Webhooks {
894
937
  error: null
895
938
  };
896
939
  } catch {
897
- error = createError("Invalid webhook payload.");
940
+ error = createValidationError("Invalid webhook payload.");
898
941
  return {
899
942
  data: null,
900
943
  error
@@ -916,37 +959,37 @@ var Webhooks = class Webhooks {
916
959
  };
917
960
  if (options?.statuses && options.statuses.length > 6) return {
918
961
  data: null,
919
- error: createError("A maximum of 6 status filters can be provided.")
962
+ error: createValidationError("A maximum of 6 status filters can be provided.")
920
963
  };
921
964
  if (options?.statuses && new Set(options.statuses).size !== options.statuses.length) return {
922
965
  data: null,
923
- error: createError("Status filters must be unique.")
966
+ error: createValidationError("Status filters must be unique.")
924
967
  };
925
- if (options?.createdAfter && Number.isNaN(Date.parse(options.createdAfter))) return {
926
- data: null,
927
- error: createError("createdAfter must be a valid date string.")
928
- };
929
- if (options?.createdBefore && Number.isNaN(Date.parse(options.createdBefore))) return {
968
+ const { dates, error: dateError } = parseDateInputs({
969
+ createdAfter: options?.createdAfter,
970
+ createdBefore: options?.createdBefore
971
+ });
972
+ if (!dates || dateError) return {
930
973
  data: null,
931
- error: createError("createdBefore must be a valid date string.")
974
+ error: dateError
932
975
  };
933
- if (options?.createdAfter && options?.createdBefore) {
934
- const createdAfter = Date.parse(options.createdAfter);
935
- const createdBefore = Date.parse(options.createdBefore);
976
+ if (dates.createdAfter && dates.createdBefore) {
977
+ const createdAfter = Date.parse(dates.createdAfter);
978
+ const createdBefore = Date.parse(dates.createdBefore);
936
979
  const maxRangeMs = 744 * 60 * 60 * 1e3;
937
980
  if (createdBefore <= createdAfter) return {
938
981
  data: null,
939
- error: createError("createdBefore must be later than createdAfter.")
982
+ error: createValidationError("createdBefore must be later than createdAfter.")
940
983
  };
941
984
  if (createdBefore - createdAfter > maxRangeMs) return {
942
985
  data: null,
943
- error: createError("The time range between createdAfter and createdBefore must not exceed 31 days.")
986
+ error: createValidationError("The time range between createdAfter and createdBefore must not exceed 31 days.")
944
987
  };
945
988
  }
946
989
  const response = await this.mailchannels.get("/tx/v1/webhook-batch", {
947
990
  query: {
948
- created_after: options?.createdAfter,
949
- created_before: options?.createdBefore,
991
+ created_after: dates.createdAfter,
992
+ created_before: dates.createdBefore,
950
993
  statuses: options?.statuses,
951
994
  webhook: options?.webhook,
952
995
  limit: options?.limit,
@@ -979,7 +1022,7 @@ var Webhooks = class Webhooks {
979
1022
  }
980
1023
  async resendBatch(batchId) {
981
1024
  let error = null;
982
- const response = await this.mailchannels.post(`/tx/v1/webhook-batch/${batchId}/resend`, { onResponseError: async ({ response }) => {
1025
+ const response = await this.mailchannels.post(`/tx/v1/webhook-batch/${encodeURIComponent(batchId)}/resend`, { onResponseError: async ({ response }) => {
983
1026
  error = getStatusError(response, {
984
1027
  [400]: "Bad Request. The batch ID is invalid.",
985
1028
  [404]: `The batch '${batchId}' is not found for the customer.`
@@ -1006,161 +1049,21 @@ var Webhooks = class Webhooks {
1006
1049
  };
1007
1050
  }
1008
1051
  };
1009
- var SubAccounts = class SubAccounts {
1052
+ var SubAccountsApiKeys = class {
1010
1053
  mailchannels;
1011
- static COMPANY_PATTERN = /^.{3,128}$/;
1012
- static HANDLE_PATTERN = /^[a-z0-9]{3,128}$/;
1013
1054
  constructor(mailchannels) {
1014
1055
  this.mailchannels = mailchannels;
1015
1056
  }
1016
- async create(companyName, handle) {
1017
- let error = null;
1018
- if (!SubAccounts.COMPANY_PATTERN.test(companyName)) {
1019
- error = createError("Invalid company name. Company name must be between 3 and 128 characters.");
1020
- return {
1021
- data: null,
1022
- error
1023
- };
1024
- }
1025
- if (handle) {
1026
- if (!SubAccounts.HANDLE_PATTERN.test(handle)) {
1027
- error = createError("Invalid handle. Sub-account handle must be between 3 and 128 characters and contain only lowercase letters and numbers.");
1028
- return {
1029
- data: null,
1030
- error
1031
- };
1032
- }
1033
- }
1034
- const response = await this.mailchannels.post("/tx/v1/sub-account", {
1035
- body: {
1036
- company_name: companyName,
1037
- handle
1038
- },
1039
- onResponseError: async ({ response }) => {
1040
- error = getStatusError(response, {
1041
- [403]: "The parent account does not have permission to create sub-accounts.",
1042
- [409]: `Sub-account with handle '${handle}' already exists.`
1043
- });
1044
- }
1045
- }).catch((e) => {
1046
- error ||= getResultError(e, "Failed to create sub-account.");
1047
- return null;
1048
- });
1049
- if (!response) return {
1050
- data: null,
1051
- error
1052
- };
1053
- return {
1054
- data: clean({
1055
- companyName: response.company_name,
1056
- enabled: response.enabled,
1057
- handle: response.handle
1058
- }),
1059
- error: null
1060
- };
1061
- }
1062
- async list(options) {
1063
- let error = null;
1064
- error = validatePagination({
1065
- ...options,
1066
- max: 1e3
1067
- });
1068
- if (error) return {
1069
- data: null,
1070
- error
1071
- };
1072
- const response = await this.mailchannels.get("/tx/v1/sub-account", {
1073
- query: options,
1074
- onResponseError: async ({ response }) => {
1075
- error = getStatusError(response);
1076
- }
1077
- }).catch((e) => {
1078
- error ||= getResultError(e, "Failed to fetch sub-accounts.");
1079
- return null;
1080
- });
1081
- if (!response) return {
1082
- data: null,
1083
- error
1084
- };
1085
- return {
1086
- data: clean(response.map((account) => ({
1087
- companyName: account.company_name,
1088
- enabled: account.enabled,
1089
- handle: account.handle
1090
- }))),
1091
- error: null
1092
- };
1093
- }
1094
- async delete(handle) {
1057
+ async create(handle) {
1095
1058
  let error = null;
1096
1059
  if (!handle) {
1097
- error = createError("No handle provided.");
1098
- return {
1099
- success: false,
1100
- error
1101
- };
1102
- }
1103
- await this.mailchannels.delete(`/tx/v1/sub-account/${handle}`, { onResponseError: async ({ response }) => {
1104
- error = getStatusError(response);
1105
- } }).catch((e) => {
1106
- error ||= getResultError(e, "Failed to delete sub-account.");
1107
- });
1108
- return {
1109
- success: !error,
1110
- error
1111
- };
1112
- }
1113
- async suspend(handle) {
1114
- let error = null;
1115
- if (!handle) {
1116
- error = createError("No handle provided.");
1117
- return {
1118
- success: false,
1119
- error
1120
- };
1121
- }
1122
- await this.mailchannels.post(`/tx/v1/sub-account/${handle}/suspend`, { onResponseError: async ({ response }) => {
1123
- error = getStatusError(response, { [404]: `The specified sub-account '${handle}' does not exist.` });
1124
- } }).catch((e) => {
1125
- error ||= getResultError(e, "Failed to suspend sub-account.");
1126
- });
1127
- return {
1128
- success: !error,
1129
- error
1130
- };
1131
- }
1132
- async activate(handle) {
1133
- let error = null;
1134
- if (!handle) {
1135
- error = createError("No handle provided.");
1136
- return {
1137
- success: false,
1138
- error
1139
- };
1140
- }
1141
- await this.mailchannels.post(`/tx/v1/sub-account/${handle}/activate`, { onResponseError: async ({ response }) => {
1142
- error = getStatusError(response, {
1143
- [403]: "The parent account does not have permission to activate the sub-account.",
1144
- [404]: `The specified sub-account '${handle}' does not exist.`
1145
- });
1146
- } }).catch((e) => {
1147
- error ||= getResultError(e, "Failed to activate sub-account.");
1148
- });
1149
- return {
1150
- success: !error,
1151
- error
1152
- };
1153
- }
1154
- async createApiKey(handle) {
1155
- let error = null;
1156
- if (!handle) {
1157
- error = createError("No handle provided.");
1060
+ error = createValidationError("No handle provided.");
1158
1061
  return {
1159
1062
  data: null,
1160
1063
  error
1161
1064
  };
1162
1065
  }
1163
- const response = await this.mailchannels.post(`/tx/v1/sub-account/${handle}/api-key`, { onResponseError: async ({ response }) => {
1066
+ const response = await this.mailchannels.post(`/tx/v1/sub-account/${encodeURIComponent(handle)}/api-key`, { onResponseError: async ({ response }) => {
1164
1067
  error = getStatusError(response, {
1165
1068
  [403]: "You can't create API keys for this sub-account.",
1166
1069
  [404]: `Sub-account with handle '${handle}' not found.`,
@@ -1179,10 +1082,10 @@ var SubAccounts = class SubAccounts {
1179
1082
  error: null
1180
1083
  };
1181
1084
  }
1182
- async listApiKeys(handle, options) {
1085
+ async list(handle, options) {
1183
1086
  let error = null;
1184
1087
  if (!handle) {
1185
- error = createError("No handle provided.");
1088
+ error = createValidationError("No handle provided.");
1186
1089
  return {
1187
1090
  data: null,
1188
1091
  error
@@ -1196,9 +1099,12 @@ var SubAccounts = class SubAccounts {
1196
1099
  data: null,
1197
1100
  error
1198
1101
  };
1199
- const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/api-key`, { onResponseError: async ({ response }) => {
1200
- error = getStatusError(response, { [404]: `Sub-account with handle '${handle}' not found.` });
1201
- } }).catch((e) => {
1102
+ const response = await this.mailchannels.get(`/tx/v1/sub-account/${encodeURIComponent(handle)}/api-key`, {
1103
+ query: options,
1104
+ onResponseError: async ({ response }) => {
1105
+ error = getStatusError(response, { [404]: `Sub-account with handle '${handle}' not found.` });
1106
+ }
1107
+ }).catch((e) => {
1202
1108
  error ||= getResultError(e, "Failed to fetch sub-account API keys.");
1203
1109
  return null;
1204
1110
  });
@@ -1214,16 +1120,16 @@ var SubAccounts = class SubAccounts {
1214
1120
  error: null
1215
1121
  };
1216
1122
  }
1217
- async deleteApiKey(handle, id) {
1123
+ async delete(handle, id) {
1218
1124
  let error = null;
1219
1125
  if (!handle) {
1220
- error = createError("No handle provided.");
1126
+ error = createValidationError("No handle provided.");
1221
1127
  return {
1222
1128
  success: false,
1223
1129
  error
1224
1130
  };
1225
1131
  }
1226
- await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/api-key/${id}`, { onResponseError: async ({ response }) => {
1132
+ await this.mailchannels.delete(`/tx/v1/sub-account/${encodeURIComponent(handle)}/api-key/${encodeURIComponent(id)}`, { onResponseError: async ({ response }) => {
1227
1133
  error = getStatusError(response, { [400]: "Missing or invalid API key ID." });
1228
1134
  } }).catch((e) => {
1229
1135
  error ||= getResultError(e, "Failed to delete sub-account API key.");
@@ -1233,16 +1139,22 @@ var SubAccounts = class SubAccounts {
1233
1139
  error
1234
1140
  };
1235
1141
  }
1236
- async createSmtpPassword(handle) {
1142
+ };
1143
+ var SubAccountsSmtpPasswords = class {
1144
+ mailchannels;
1145
+ constructor(mailchannels) {
1146
+ this.mailchannels = mailchannels;
1147
+ }
1148
+ async create(handle) {
1237
1149
  let error = null;
1238
1150
  if (!handle) {
1239
- error = createError("No handle provided.");
1151
+ error = createValidationError("No handle provided.");
1240
1152
  return {
1241
1153
  data: null,
1242
1154
  error
1243
1155
  };
1244
1156
  }
1245
- const response = await this.mailchannels.post(`/tx/v1/sub-account/${handle}/smtp-password`, { onResponseError: async ({ response }) => {
1157
+ const response = await this.mailchannels.post(`/tx/v1/sub-account/${encodeURIComponent(handle)}/smtp-password`, { onResponseError: async ({ response }) => {
1246
1158
  error = getStatusError(response, {
1247
1159
  [403]: "You can't create SMTP passwords for this sub-account.",
1248
1160
  [404]: `Sub-account with handle '${handle}' not found.`,
@@ -1265,16 +1177,16 @@ var SubAccounts = class SubAccounts {
1265
1177
  error: null
1266
1178
  };
1267
1179
  }
1268
- async listSmtpPasswords(handle) {
1180
+ async list(handle) {
1269
1181
  let error = null;
1270
1182
  if (!handle) {
1271
- error = createError("No handle provided.");
1183
+ error = createValidationError("No handle provided.");
1272
1184
  return {
1273
1185
  data: null,
1274
1186
  error
1275
1187
  };
1276
1188
  }
1277
- const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/smtp-password`, { onResponseError: async ({ response }) => {
1189
+ const response = await this.mailchannels.get(`/tx/v1/sub-account/${encodeURIComponent(handle)}/smtp-password`, { onResponseError: async ({ response }) => {
1278
1190
  error = getStatusError(response, { [404]: `Sub-account with handle '${handle}' not found.` });
1279
1191
  } }).catch((e) => {
1280
1192
  error ||= getResultError(e, "Failed to fetch sub-account SMTP passwords.");
@@ -1293,16 +1205,16 @@ var SubAccounts = class SubAccounts {
1293
1205
  error: null
1294
1206
  };
1295
1207
  }
1296
- async deleteSmtpPassword(handle, id) {
1208
+ async delete(handle, id) {
1297
1209
  let error = null;
1298
1210
  if (!handle) {
1299
- error = createError("No handle provided.");
1211
+ error = createValidationError("No handle provided.");
1300
1212
  return {
1301
1213
  success: false,
1302
1214
  error
1303
1215
  };
1304
1216
  }
1305
- await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/smtp-password/${id}`, { onResponseError: async ({ response }) => {
1217
+ await this.mailchannels.delete(`/tx/v1/sub-account/${encodeURIComponent(handle)}/smtp-password/${encodeURIComponent(id)}`, { onResponseError: async ({ response }) => {
1306
1218
  error = getStatusError(response, { [400]: "Missing or invalid SMTP password ID." });
1307
1219
  } }).catch((e) => {
1308
1220
  error ||= getResultError(e, "Failed to delete sub-account SMTP password.");
@@ -1312,16 +1224,22 @@ var SubAccounts = class SubAccounts {
1312
1224
  error
1313
1225
  };
1314
1226
  }
1315
- async getLimit(handle) {
1227
+ };
1228
+ var SubAccountsLimits = class {
1229
+ mailchannels;
1230
+ constructor(mailchannels) {
1231
+ this.mailchannels = mailchannels;
1232
+ }
1233
+ async get(handle) {
1316
1234
  let error = null;
1317
1235
  if (!handle) {
1318
- error = createError("No handle provided.");
1236
+ error = createValidationError("No handle provided.");
1319
1237
  return {
1320
1238
  data: null,
1321
1239
  error
1322
1240
  };
1323
1241
  }
1324
- const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/limit`, { onResponseError: async ({ response }) => {
1242
+ const response = await this.mailchannels.get(`/tx/v1/sub-account/${encodeURIComponent(handle)}/limit`, { onResponseError: async ({ response }) => {
1325
1243
  error = getStatusError(response, { [404]: `Sub-account with handle '${handle}' not found.` });
1326
1244
  } }).catch((e) => {
1327
1245
  error ||= getResultError(e, "Failed to fetch sub-account limit.");
@@ -1336,24 +1254,24 @@ var SubAccounts = class SubAccounts {
1336
1254
  error: null
1337
1255
  };
1338
1256
  }
1339
- async setLimit(handle, limit) {
1257
+ async set(handle, options) {
1340
1258
  let error = null;
1341
1259
  if (!handle) {
1342
- error = createError("No handle provided.");
1260
+ error = createValidationError("No handle provided.");
1343
1261
  return {
1344
1262
  success: false,
1345
1263
  error
1346
1264
  };
1347
1265
  }
1348
- if (limit.sends < 0) {
1349
- error = createError("The sends value must be at least 0.");
1266
+ if (options.sends < 0) {
1267
+ error = createValidationError("The sends value must be at least 0.");
1350
1268
  return {
1351
1269
  success: false,
1352
1270
  error
1353
1271
  };
1354
1272
  }
1355
- await this.mailchannels.put(`/tx/v1/sub-account/${handle}/limit`, {
1356
- body: limit,
1273
+ await this.mailchannels.put(`/tx/v1/sub-account/${encodeURIComponent(handle)}/limit`, {
1274
+ body: options,
1357
1275
  onResponseError: async ({ response }) => {
1358
1276
  error = getStatusError(response, {
1359
1277
  [400]: "Bad Request.",
@@ -1368,16 +1286,16 @@ var SubAccounts = class SubAccounts {
1368
1286
  error
1369
1287
  };
1370
1288
  }
1371
- async deleteLimit(handle) {
1289
+ async delete(handle) {
1372
1290
  let error = null;
1373
1291
  if (!handle) {
1374
- error = createError("No handle provided.");
1292
+ error = createValidationError("No handle provided.");
1375
1293
  return {
1376
1294
  success: false,
1377
1295
  error
1378
1296
  };
1379
1297
  }
1380
- await this.mailchannels.delete(`/tx/v1/sub-account/${handle}/limit`, { onResponseError: async ({ response }) => {
1298
+ await this.mailchannels.delete(`/tx/v1/sub-account/${encodeURIComponent(handle)}/limit`, { onResponseError: async ({ response }) => {
1381
1299
  error = getStatusError(response, { [404]: `Sub-account with handle '${handle}' not found.` });
1382
1300
  } }).catch((e) => {
1383
1301
  error ||= getResultError(e, "Failed to delete sub-account limit.");
@@ -1387,16 +1305,168 @@ var SubAccounts = class SubAccounts {
1387
1305
  error
1388
1306
  };
1389
1307
  }
1308
+ };
1309
+ var SubAccounts = class SubAccounts {
1310
+ mailchannels;
1311
+ static COMPANY_PATTERN = /^.{3,128}$/;
1312
+ static HANDLE_PATTERN = /^[a-z0-9]{3,128}$/;
1313
+ apiKeys;
1314
+ smtpPasswords;
1315
+ limits;
1316
+ constructor(mailchannels) {
1317
+ this.mailchannels = mailchannels;
1318
+ this.apiKeys = new SubAccountsApiKeys(mailchannels);
1319
+ this.smtpPasswords = new SubAccountsSmtpPasswords(mailchannels);
1320
+ this.limits = new SubAccountsLimits(mailchannels);
1321
+ }
1322
+ async create(companyName, handle) {
1323
+ let error = null;
1324
+ if (!SubAccounts.COMPANY_PATTERN.test(companyName)) {
1325
+ error = createValidationError("Invalid company name. Company name must be between 3 and 128 characters.");
1326
+ return {
1327
+ data: null,
1328
+ error
1329
+ };
1330
+ }
1331
+ if (handle) {
1332
+ if (!SubAccounts.HANDLE_PATTERN.test(handle)) {
1333
+ error = createValidationError("Invalid handle. Sub-account handle must be between 3 and 128 characters and contain only lowercase letters and numbers.");
1334
+ return {
1335
+ data: null,
1336
+ error
1337
+ };
1338
+ }
1339
+ }
1340
+ const response = await this.mailchannels.post("/tx/v1/sub-account", {
1341
+ body: {
1342
+ company_name: companyName,
1343
+ handle
1344
+ },
1345
+ onResponseError: async ({ response }) => {
1346
+ error = getStatusError(response, {
1347
+ [403]: "The parent account does not have permission to create sub-accounts.",
1348
+ [409]: `Sub-account with handle '${handle}' already exists.`
1349
+ });
1350
+ }
1351
+ }).catch((e) => {
1352
+ error ||= getResultError(e, "Failed to create sub-account.");
1353
+ return null;
1354
+ });
1355
+ if (!response) return {
1356
+ data: null,
1357
+ error
1358
+ };
1359
+ return {
1360
+ data: clean({
1361
+ companyName: response.company_name,
1362
+ enabled: response.enabled,
1363
+ handle: response.handle
1364
+ }),
1365
+ error: null
1366
+ };
1367
+ }
1368
+ async list(options) {
1369
+ let error = null;
1370
+ error = validatePagination({
1371
+ ...options,
1372
+ max: 1e3
1373
+ });
1374
+ if (error) return {
1375
+ data: null,
1376
+ error
1377
+ };
1378
+ const response = await this.mailchannels.get("/tx/v1/sub-account", {
1379
+ query: options,
1380
+ onResponseError: async ({ response }) => {
1381
+ error = getStatusError(response);
1382
+ }
1383
+ }).catch((e) => {
1384
+ error ||= getResultError(e, "Failed to fetch sub-accounts.");
1385
+ return null;
1386
+ });
1387
+ if (!response) return {
1388
+ data: null,
1389
+ error
1390
+ };
1391
+ return {
1392
+ data: clean(response.map((account) => ({
1393
+ companyName: account.company_name,
1394
+ enabled: account.enabled,
1395
+ handle: account.handle
1396
+ }))),
1397
+ error: null
1398
+ };
1399
+ }
1400
+ async delete(handle) {
1401
+ let error = null;
1402
+ if (!handle) {
1403
+ error = createValidationError("No handle provided.");
1404
+ return {
1405
+ success: false,
1406
+ error
1407
+ };
1408
+ }
1409
+ await this.mailchannels.delete(`/tx/v1/sub-account/${encodeURIComponent(handle)}`, { onResponseError: async ({ response }) => {
1410
+ error = getStatusError(response);
1411
+ } }).catch((e) => {
1412
+ error ||= getResultError(e, "Failed to delete sub-account.");
1413
+ });
1414
+ return {
1415
+ success: !error,
1416
+ error
1417
+ };
1418
+ }
1419
+ async suspend(handle) {
1420
+ let error = null;
1421
+ if (!handle) {
1422
+ error = createValidationError("No handle provided.");
1423
+ return {
1424
+ success: false,
1425
+ error
1426
+ };
1427
+ }
1428
+ await this.mailchannels.post(`/tx/v1/sub-account/${encodeURIComponent(handle)}/suspend`, { onResponseError: async ({ response }) => {
1429
+ error = getStatusError(response, { [404]: `The specified sub-account '${handle}' does not exist.` });
1430
+ } }).catch((e) => {
1431
+ error ||= getResultError(e, "Failed to suspend sub-account.");
1432
+ });
1433
+ return {
1434
+ success: !error,
1435
+ error
1436
+ };
1437
+ }
1438
+ async activate(handle) {
1439
+ let error = null;
1440
+ if (!handle) {
1441
+ error = createValidationError("No handle provided.");
1442
+ return {
1443
+ success: false,
1444
+ error
1445
+ };
1446
+ }
1447
+ await this.mailchannels.post(`/tx/v1/sub-account/${encodeURIComponent(handle)}/activate`, { onResponseError: async ({ response }) => {
1448
+ error = getStatusError(response, {
1449
+ [403]: "The parent account does not have permission to activate the sub-account.",
1450
+ [404]: `The specified sub-account '${handle}' does not exist.`
1451
+ });
1452
+ } }).catch((e) => {
1453
+ error ||= getResultError(e, "Failed to activate sub-account.");
1454
+ });
1455
+ return {
1456
+ success: !error,
1457
+ error
1458
+ };
1459
+ }
1390
1460
  async getUsage(handle) {
1391
1461
  let error = null;
1392
1462
  if (!handle) {
1393
- error = createError("No handle provided.");
1463
+ error = createValidationError("No handle provided.");
1394
1464
  return {
1395
1465
  data: null,
1396
1466
  error
1397
1467
  };
1398
1468
  }
1399
- const response = await this.mailchannels.get(`/tx/v1/sub-account/${handle}/usage`, { onResponseError: async ({ response }) => {
1469
+ const response = await this.mailchannels.get(`/tx/v1/sub-account/${encodeURIComponent(handle)}/usage`, { onResponseError: async ({ response }) => {
1400
1470
  error = getStatusError(response, { [404]: `Sub-account with handle '${handle}' not found.` });
1401
1471
  } }).catch((e) => {
1402
1472
  error ||= getResultError(e, "Failed to fetch sub-account usage.");
@@ -1415,6 +1485,33 @@ var SubAccounts = class SubAccounts {
1415
1485
  error: null
1416
1486
  };
1417
1487
  }
1488
+ createApiKey(...args) {
1489
+ return this.apiKeys.create(...args);
1490
+ }
1491
+ listApiKeys(...args) {
1492
+ return this.apiKeys.list(...args);
1493
+ }
1494
+ deleteApiKey(...args) {
1495
+ return this.apiKeys.delete(...args);
1496
+ }
1497
+ createSmtpPassword(...args) {
1498
+ return this.smtpPasswords.create(...args);
1499
+ }
1500
+ listSmtpPasswords(...args) {
1501
+ return this.smtpPasswords.list(...args);
1502
+ }
1503
+ deleteSmtpPassword(...args) {
1504
+ return this.smtpPasswords.delete(...args);
1505
+ }
1506
+ setLimit(...args) {
1507
+ return this.limits.set(...args);
1508
+ }
1509
+ getLimit(...args) {
1510
+ return this.limits.get(...args);
1511
+ }
1512
+ deleteLimit(...args) {
1513
+ return this.limits.delete(...args);
1514
+ }
1418
1515
  };
1419
1516
  const mapBucket = (bucket) => {
1420
1517
  return {
@@ -1429,10 +1526,18 @@ var Metrics = class {
1429
1526
  }
1430
1527
  async engagement(options) {
1431
1528
  let error = null;
1529
+ const { dates, error: dateError } = parseDateInputs({
1530
+ startTime: options?.startTime,
1531
+ endTime: options?.endTime
1532
+ });
1533
+ if (!dates || dateError) return {
1534
+ data: null,
1535
+ error: dateError
1536
+ };
1432
1537
  const response = await this.mailchannels.get("/tx/v1/metrics/engagement", {
1433
1538
  query: {
1434
- start_time: options?.startTime,
1435
- end_time: options?.endTime,
1539
+ start_time: dates.startTime,
1540
+ end_time: dates.endTime,
1436
1541
  campaign_id: options?.campaignId,
1437
1542
  interval: options?.interval
1438
1543
  },
@@ -1467,10 +1572,18 @@ var Metrics = class {
1467
1572
  }
1468
1573
  async performance(options) {
1469
1574
  let error = null;
1575
+ const { dates, error: dateError } = parseDateInputs({
1576
+ startTime: options?.startTime,
1577
+ endTime: options?.endTime
1578
+ });
1579
+ if (!dates || dateError) return {
1580
+ data: null,
1581
+ error: dateError
1582
+ };
1470
1583
  const response = await this.mailchannels.get("/tx/v1/metrics/performance", {
1471
1584
  query: {
1472
- start_time: options?.startTime,
1473
- end_time: options?.endTime,
1585
+ start_time: dates.startTime,
1586
+ end_time: dates.endTime,
1474
1587
  campaign_id: options?.campaignId,
1475
1588
  interval: options?.interval
1476
1589
  },
@@ -1503,10 +1616,18 @@ var Metrics = class {
1503
1616
  }
1504
1617
  async recipientBehaviour(options) {
1505
1618
  let error = null;
1619
+ const { dates, error: dateError } = parseDateInputs({
1620
+ startTime: options?.startTime,
1621
+ endTime: options?.endTime
1622
+ });
1623
+ if (!dates || dateError) return {
1624
+ data: null,
1625
+ error: dateError
1626
+ };
1506
1627
  const response = await this.mailchannels.get("/tx/v1/metrics/recipient-behaviour", {
1507
1628
  query: {
1508
- start_time: options?.startTime,
1509
- end_time: options?.endTime,
1629
+ start_time: dates.startTime,
1630
+ end_time: dates.endTime,
1510
1631
  campaign_id: options?.campaignId,
1511
1632
  interval: options?.interval
1512
1633
  },
@@ -1537,10 +1658,18 @@ var Metrics = class {
1537
1658
  }
1538
1659
  async volume(options) {
1539
1660
  let error = null;
1661
+ const { dates, error: dateError } = parseDateInputs({
1662
+ startTime: options?.startTime,
1663
+ endTime: options?.endTime
1664
+ });
1665
+ if (!dates || dateError) return {
1666
+ data: null,
1667
+ error: dateError
1668
+ };
1540
1669
  const response = await this.mailchannels.get("/tx/v1/metrics/volume", {
1541
1670
  query: {
1542
- start_time: options?.startTime,
1543
- end_time: options?.endTime,
1671
+ start_time: dates.startTime,
1672
+ end_time: dates.endTime,
1544
1673
  campaign_id: options?.campaignId,
1545
1674
  interval: options?.interval
1546
1675
  },
@@ -1594,6 +1723,14 @@ var Metrics = class {
1594
1723
  }
1595
1724
  async senders(type, options) {
1596
1725
  let error = null;
1726
+ const { dates, error: dateError } = parseDateInputs({
1727
+ startTime: options?.startTime,
1728
+ endTime: options?.endTime
1729
+ });
1730
+ if (!dates || dateError) return {
1731
+ data: null,
1732
+ error: dateError
1733
+ };
1597
1734
  error = validatePagination({
1598
1735
  ...options,
1599
1736
  max: 1e3
@@ -1602,10 +1739,10 @@ var Metrics = class {
1602
1739
  data: null,
1603
1740
  error
1604
1741
  };
1605
- const response = await this.mailchannels.get(`/tx/v1/metrics/senders/${type}`, {
1742
+ const response = await this.mailchannels.get(`/tx/v1/metrics/senders/${encodeURIComponent(type)}`, {
1606
1743
  query: {
1607
- start_time: options?.startTime,
1608
- end_time: options?.endTime,
1744
+ start_time: dates.startTime,
1745
+ end_time: dates.endTime,
1609
1746
  limit: options?.limit,
1610
1747
  offset: options?.offset,
1611
1748
  sort_order: options?.sortOrder
@@ -1643,7 +1780,7 @@ var Suppressions = class {
1643
1780
  let error = null;
1644
1781
  const { addToSubAccounts, entries } = options;
1645
1782
  if (entries.length > 1e3) {
1646
- error = createError("The number of suppression entries must not exceed 1000.");
1783
+ error = createValidationError("The number of suppression entries must not exceed 1000.");
1647
1784
  return {
1648
1785
  success: false,
1649
1786
  error
@@ -1676,7 +1813,7 @@ var Suppressions = class {
1676
1813
  }
1677
1814
  async delete(recipient, source) {
1678
1815
  let error = null;
1679
- await this.mailchannels.delete(`/tx/v1/suppression-list/recipients/${recipient}`, {
1816
+ await this.mailchannels.delete(`/tx/v1/suppression-list/recipients/${encodeURIComponent(recipient)}`, {
1680
1817
  query: { source },
1681
1818
  onResponseError: async ({ response }) => {
1682
1819
  error = getStatusError(response, { [400]: "Bad Request." });
@@ -1692,12 +1829,20 @@ var Suppressions = class {
1692
1829
  async list(options) {
1693
1830
  let error = null;
1694
1831
  if (options?.recipient && options.recipient.length > 255) {
1695
- error = createError("The recipient must not exceed 255 characters.");
1832
+ error = createValidationError("The recipient must not exceed 255 characters.");
1696
1833
  return {
1697
1834
  data: null,
1698
1835
  error
1699
1836
  };
1700
1837
  }
1838
+ const { dates, error: dateError } = parseDateInputs({
1839
+ createdBefore: options?.createdBefore,
1840
+ createdAfter: options?.createdAfter
1841
+ });
1842
+ if (!dates || dateError) return {
1843
+ data: null,
1844
+ error: dateError
1845
+ };
1701
1846
  error = validatePagination({
1702
1847
  ...options,
1703
1848
  max: 1e3
@@ -1709,8 +1854,8 @@ var Suppressions = class {
1709
1854
  const payload = {
1710
1855
  recipient: options?.recipient,
1711
1856
  source: options?.source,
1712
- created_before: options?.createdBefore,
1713
- created_after: options?.createdAfter,
1857
+ created_before: dates.createdBefore,
1858
+ created_after: dates.createdAfter,
1714
1859
  limit: options?.limit,
1715
1860
  offset: options?.offset
1716
1861
  };
@@ -1758,41 +1903,12 @@ var Attachment = class Attachment {
1758
1903
  disposition
1759
1904
  };
1760
1905
  }
1761
- static async fromFile(path, options) {
1762
- try {
1763
- const data = await readFile(path);
1764
- return Attachment.fromBytes(data, {
1765
- filename: basename(path.toString()),
1766
- ...options
1767
- });
1768
- } catch (error) {
1769
- throw new Error(`Unable to read attachment file: ${path}`, { cause: error });
1770
- }
1771
- }
1772
- static async fromUrl(url, options) {
1773
- try {
1774
- let contentType;
1775
- const data = await $fetch(url, {
1776
- responseType: "arrayBuffer",
1777
- timeout: 12e4,
1778
- onResponse: ({ response }) => {
1779
- const contentTypeHeader = response.headers.get("content-type");
1780
- if (contentTypeHeader) contentType = contentTypeHeader.split(";", 1)[0]?.trim();
1781
- }
1782
- });
1783
- return Attachment.fromBytes(data, {
1784
- type: contentType,
1785
- filename: basename(new URL(url).pathname),
1786
- ...options
1787
- });
1788
- } catch (error) {
1789
- throw new Error(`Unable to fetch attachment from URL: ${url}`, { cause: error });
1790
- }
1791
- }
1792
- static inlineFile(path, options) {
1793
- return Attachment.fromFile(path, {
1794
- ...options,
1795
- disposition: "inline"
1906
+ static async fromBlob(blob, options) {
1907
+ if (!(blob instanceof Blob)) throw new Error("Unable to create attachment: expected a Blob");
1908
+ const bytes = await blob.arrayBuffer();
1909
+ return Attachment.fromBytes(bytes, {
1910
+ type: blob.type,
1911
+ ...options
1796
1912
  });
1797
1913
  }
1798
1914
  };