mailchannels-sdk 1.2.0 → 1.3.1

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.
@@ -45,12 +45,11 @@ const attachment = await Attachment.fromBlob(blob, { filename: 'hello.txt' })
45
45
 
46
46
  ### Inline Images (CID References)
47
47
 
48
- Pass `disposition: 'inline'` and a `contentId` to embed an image inside the HTML body:
48
+ Pass a `contentId` to embed an image inside the HTML body:
49
49
 
50
50
  ```ts
51
51
  const logo = Attachment.fromBytes(bytes, {
52
52
  filename: 'logo.png',
53
- disposition: 'inline',
54
53
  contentId: 'company-logo'
55
54
  })
56
55
 
@@ -72,7 +71,6 @@ Both `fromBytes` and `fromBlob` accept an `AttachmentOptions` object:
72
71
  | `filename` | `string` | Required. MIME type is inferred from it when `type` is omitted. |
73
72
  | `type` | `string` | MIME type. Inferred from `filename` if omitted. For `fromBlob`, defaults to the Blob's own `type`. |
74
73
  | `contentId` | `string` | For `cid:` inline image references. |
75
- | `disposition` | `'attachment' \| 'inline'` | Defaults to `'attachment'`. |
76
74
 
77
75
  ### Awaiting Attachments Lazily
78
76
 
@@ -4,8 +4,6 @@ Sub-accounts are first-class on MailChannels. Use them for tenants, customers, o
4
4
  senders so that one customer's reputation, limits, and bad traffic don't contaminate the
5
5
  parent account or other tenants.
6
6
 
7
- > Sub-accounts are only available on parent accounts on the 100K and higher plans.
8
-
9
7
  ### Handles
10
8
 
11
9
  A handle uniquely identifies a sub-account. Rules:
@@ -10,8 +10,8 @@ import { MailChannels } from 'mailchannels-sdk'
10
10
 
11
11
  const mc = new MailChannels('YOUR-API-KEY')
12
12
 
13
- const { success, error } = await mc.suppressions.create({
14
- entries: [
13
+ const { success, error } = await mc.suppressions.create(
14
+ [
15
15
  {
16
16
  recipient: 'out@example.net',
17
17
  types: ['non-transactional'], // optional; defaults to non-transactional
@@ -22,8 +22,8 @@ const { success, error } = await mc.suppressions.create({
22
22
  types: ['transactional', 'non-transactional']
23
23
  }
24
24
  ],
25
- addToSubAccounts: true // parent only; copies entries to every sub-account
26
- })
25
+ { addToSubAccounts: true } // parent only; copies entries to every sub-account
26
+ )
27
27
  ```
28
28
 
29
29
  Constraints:
@@ -83,9 +83,9 @@ that recipient regardless of origin.
83
83
 
84
84
  ### Patterns
85
85
 
86
- - **Preference center opt-out**: set `type` according to the email category, e.g. `non-transactional` for marketing
87
- emails, `transactional` for order updates, and so on.
86
+ - **Preference center opt-out**: set `types` according to the email category, e.g. `non-transactional` for marketing
87
+ emails, `transactional` for order updates, and so on.
88
88
  Configure `addToSubAccounts` depending on whether the preference applies to all sub-accounts or just the parent account.
89
89
  Add a note to indicate the source, e.g. "Opted out via preference center".
90
- - **Migrating from another ESP**: bulk-create with `addToSubAccounts: true` so every tenant
90
+ - **Migrating from another ESP**: bulk-create with option `addToSubAccounts: true` so every tenant
91
91
  inherits the list.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  [![TypeScript][typescript-src]][typescript-href]
10
10
  [![Node.js][node-src]][node-href]
11
11
 
12
- > Built and tested against Email API `1.4.0`
12
+ > Built and tested against Email API `1.6.0`
13
13
 
14
14
  Node.js SDK to integrate [MailChannels Email API](https://docs.mailchannels.com/email-api) into your JavaScript or TypeScript server-side applications.
15
15
 
@@ -273,7 +273,7 @@ pnpm test:watch
273
273
  # Run typecheck
274
274
  pnpm test:types
275
275
 
276
- # Refresh API parity fixtures, specs, and README version note
276
+ # Refresh API parity fixtures and README version note
277
277
  pnpm parity:fixtures
278
278
 
279
279
  # Run the local simulator
@@ -1,4 +1,5 @@
1
1
  import { randomBytes, randomUUID } from "node:crypto";
2
+ import { parseArgs } from "node:util";
2
3
  import { createServer } from "node:http";
3
4
  const SIMULATOR_SIGNING_KEY_ID = "simulator-default";
4
5
  const JSON_HEADERS = { "content-type": "application/json" };
@@ -55,6 +56,7 @@ const createMetricsBuckets = (count) => [{
55
56
  const createAccountState = (apiKey) => ({
56
57
  apiKey,
57
58
  customerHandle: createId("customer"),
59
+ limit: { sends: 1e5 },
58
60
  customTrackingDomains: [],
59
61
  dkimKeysByDomain: /* @__PURE__ */ new Map(),
60
62
  messages: [],
@@ -312,8 +314,8 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
312
314
  }
313
315
  targetKey.status = "rotated";
314
316
  targetKey.status_modified_at = currentTimestamp();
315
- targetKey.gracePeriodExpiresAt = new Date(Date.now() + 10080 * 60 * 1e3).toISOString();
316
- targetKey.retiresAt = new Date(Date.now() + 720 * 60 * 60 * 1e3).toISOString();
317
+ targetKey.gracePeriodExpiresAt = new Date(Date.now() + 6048e5).toISOString();
318
+ targetKey.retiresAt = new Date(Date.now() + 2592e6).toISOString();
317
319
  const newKey = createDkimKey(domain, body?.new_key?.selector || createId("selector"));
318
320
  account.dkimKeysByDomain.set(domain, [...keys, newKey]);
319
321
  sendJson(response, 201, {
@@ -600,10 +602,13 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
600
602
  return;
601
603
  }
602
604
  if (suffix === "usage" && method === "GET") {
605
+ const subLimitSends = subAccount.limit?.sends ?? -1;
606
+ const monthlyLimit = subLimitSends === -1 ? account.limit.sends : subLimitSends;
603
607
  sendJson(response, 200, {
604
608
  period_end_date: currentTimestamp(),
605
609
  period_start_date: new Date((/* @__PURE__ */ new Date()).setDate(1)).toISOString(),
606
- total_usage: subAccount.usage
610
+ total_usage: subAccount.usage,
611
+ monthly_limit: monthlyLimit
607
612
  });
608
613
  return;
609
614
  }
@@ -623,7 +628,7 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
623
628
  end_time: url.searchParams.get("end_time") || currentTimestamp(),
624
629
  open: summary.open,
625
630
  open_tracking_delivered: summary.openTrackingDelivered,
626
- start_time: url.searchParams.get("start_time") || (/* @__PURE__ */ new Date(Date.now() - 720 * 60 * 60 * 1e3)).toISOString()
631
+ start_time: url.searchParams.get("start_time") || (/* @__PURE__ */ new Date(Date.now() - 2592e6)).toISOString()
627
632
  });
628
633
  return;
629
634
  }
@@ -642,7 +647,7 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
642
647
  delivered: summary.delivered,
643
648
  end_time: url.searchParams.get("end_time") || currentTimestamp(),
644
649
  processed: summary.processed,
645
- start_time: url.searchParams.get("start_time") || (/* @__PURE__ */ new Date(Date.now() - 720 * 60 * 60 * 1e3)).toISOString()
650
+ start_time: url.searchParams.get("start_time") || (/* @__PURE__ */ new Date(Date.now() - 2592e6)).toISOString()
646
651
  });
647
652
  return;
648
653
  }
@@ -655,7 +660,7 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
655
660
  unsubscribed: createMetricsBuckets(summary.unsubscribed)
656
661
  },
657
662
  end_time: url.searchParams.get("end_time") || currentTimestamp(),
658
- start_time: url.searchParams.get("start_time") || (/* @__PURE__ */ new Date(Date.now() - 720 * 60 * 60 * 1e3)).toISOString(),
663
+ start_time: url.searchParams.get("start_time") || (/* @__PURE__ */ new Date(Date.now() - 2592e6)).toISOString(),
659
664
  unsubscribe_delivered: summary.unsubscribeDelivered,
660
665
  unsubscribed: summary.unsubscribed
661
666
  });
@@ -674,7 +679,7 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
674
679
  dropped: summary.dropped,
675
680
  end_time: url.searchParams.get("end_time") || currentTimestamp(),
676
681
  processed: summary.processed,
677
- start_time: url.searchParams.get("start_time") || (/* @__PURE__ */ new Date(Date.now() - 720 * 60 * 60 * 1e3)).toISOString()
682
+ start_time: url.searchParams.get("start_time") || (/* @__PURE__ */ new Date(Date.now() - 2592e6)).toISOString()
678
683
  });
679
684
  return;
680
685
  }
@@ -714,16 +719,27 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
714
719
  limit,
715
720
  offset,
716
721
  senders: senders.slice(offset, offset + limit),
717
- start_time: (/* @__PURE__ */ new Date(Date.now() - 720 * 60 * 60 * 1e3)).toISOString(),
722
+ start_time: (/* @__PURE__ */ new Date(Date.now() - 2592e6)).toISOString(),
718
723
  total: senders.length
719
724
  });
720
725
  return;
721
726
  }
722
727
  if (url.pathname === "/tx/v1/usage" && method === "GET") {
728
+ let totalUsage = account.messages.length;
729
+ let monthlyLimit = account.limit.sends;
730
+ if (scopeHandle) {
731
+ const subAccount = account.subAccounts.get(scopeHandle);
732
+ if (subAccount) {
733
+ totalUsage = subAccount.usage;
734
+ const subLimitSends = subAccount.limit?.sends ?? -1;
735
+ monthlyLimit = subLimitSends === -1 ? account.limit.sends : subLimitSends;
736
+ }
737
+ }
723
738
  sendJson(response, 200, {
724
739
  period_end_date: currentTimestamp(),
725
740
  period_start_date: new Date((/* @__PURE__ */ new Date()).setDate(1)).toISOString(),
726
- total_usage: account.messages.length
741
+ total_usage: totalUsage,
742
+ monthly_limit: monthlyLimit
727
743
  });
728
744
  return;
729
745
  }
@@ -781,6 +797,10 @@ const DEFAULT_HOST = "127.0.0.1";
781
797
  const DEFAULT_PORT = 8787;
782
798
  const createSimulator = (options = {}) => {
783
799
  const { host = DEFAULT_HOST, port = DEFAULT_PORT } = options;
800
+ if (port !== void 0 && (isNaN(port) || port < 0 || port > 65535)) {
801
+ console.error("[Simulator]", `Invalid port '${port}': must be an integer between 0 and 65535.`);
802
+ process.exit(1);
803
+ }
784
804
  const logRequests = !options.silent;
785
805
  const emailApi = createEmailApiHandler({ logRequests });
786
806
  const server = createServer(async (request, response) => {
@@ -828,4 +848,39 @@ const createSimulator = (options = {}) => {
828
848
  }
829
849
  };
830
850
  };
831
- export { createSimulator };
851
+ var simulate_default = async (args) => {
852
+ const { MAILCHANNELS_SIMULATOR_PORT, MAILCHANNELS_SIMULATOR_HOST } = process.env;
853
+ const { values } = parseArgs({
854
+ args,
855
+ options: {
856
+ port: {
857
+ type: "string",
858
+ short: "p",
859
+ default: MAILCHANNELS_SIMULATOR_PORT
860
+ },
861
+ host: {
862
+ type: "string",
863
+ short: "h",
864
+ default: MAILCHANNELS_SIMULATOR_HOST
865
+ },
866
+ silent: {
867
+ type: "boolean",
868
+ short: "s",
869
+ default: false
870
+ }
871
+ }
872
+ });
873
+ const simulator = createSimulator({
874
+ host: values.host,
875
+ port: values.port !== void 0 ? Number.parseInt(values.port, 10) : void 0,
876
+ silent: values.silent
877
+ });
878
+ await simulator.listen();
879
+ const shutdown = async () => {
880
+ await simulator.close();
881
+ process.exit(0);
882
+ };
883
+ process.on("SIGINT", shutdown);
884
+ process.on("SIGTERM", shutdown);
885
+ };
886
+ export { simulate_default as default };
@@ -0,0 +1 @@
1
+ export {}
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ const LOGGER_NAME = "[MailChannels-CLI]";
3
+ console.info = console.info.bind(console.info, LOGGER_NAME);
4
+ console.error = console.error.bind(console.error, LOGGER_NAME);
5
+ const [command, ...args] = process.argv.slice(2);
6
+ switch (command) {
7
+ case "simulate":
8
+ const { default: simulate } = await import("../_chunks/simulate.mjs");
9
+ await simulate(args);
10
+ break;
11
+ default:
12
+ console.error(`Unknown command: ${command}`);
13
+ process.exit(1);
14
+ }
15
+ export {};
@@ -121,13 +121,11 @@ interface EmailsSendAttachment {
121
121
  */
122
122
  type?: string;
123
123
  /**
124
- * The `Content-ID` header value for inline attachments, referenced from HTML with `cid:`.
124
+ * A unique identifier for this attachment.
125
+ *
126
+ * When set, the attachment is embedded inline in the message body (`Content-Disposition: inline`) instead of offered as a downloadable attachment, and can be referenced from HTML content via a `cid:` URI, e.g. `<img src="cid:logo123">` refers to an attachment with content_id: `logo123`. (RFC 2392). Must be unique across all attachments in the request. Max length is 255 characters.
125
127
  */
126
128
  contentId?: string;
127
- /**
128
- * The `Content-Disposition` header value for the attachment.
129
- */
130
- disposition?: "attachment" | "inline";
131
129
  }
132
130
  interface EmailsSendTracking {
133
131
  /**
@@ -193,7 +191,7 @@ interface EmailsSendContent {
193
191
  */
194
192
  value: string;
195
193
  }
196
- type EmailsSendRecipientInput = EmailsSendRecipient[] | EmailsSendRecipient | string[] | string;
194
+ type EmailsSendRecipientInput = EmailsSendRecipient | string | (EmailsSendRecipient | string)[];
197
195
  interface EmailsSendDkim {
198
196
  /**
199
197
  * Domain used for DKIM signing.
@@ -1440,8 +1438,15 @@ interface SubAccountsUsage {
1440
1438
  startDate?: string;
1441
1439
  /**
1442
1440
  * The total usage for the current billing period.
1441
+ * @example 5000
1443
1442
  */
1444
1443
  total: number;
1444
+ /**
1445
+ * The effective monthly limit for the current billing period. A limit of zero means the account cannot send any messages.
1446
+ * For sub-accounts with no explicit limit set (i.e., -1), the monthly limit for the parent account is returned.
1447
+ * @example 10000
1448
+ */
1449
+ monthlyLimit: number;
1445
1450
  }
1446
1451
  type SubAccountsUsageResponse = DataResponse<SubAccountsUsage>;
1447
1452
  interface MetricsEngagement {
@@ -1612,8 +1617,15 @@ type MetricsUsageResponse = DataResponse<{
1612
1617
  startDate?: string;
1613
1618
  /**
1614
1619
  * The total usage for the current billing period.
1620
+ * @example 5000
1615
1621
  */
1616
1622
  total: number;
1623
+ /**
1624
+ * The effective monthly limit for the current billing period. A limit of zero means the account cannot send any messages.
1625
+ * For sub-accounts with no explicit limit set (i.e., -1), the monthly limit for the parent account is returned.
1626
+ * @example 10000
1627
+ */
1628
+ monthlyLimit: number;
1617
1629
  }>;
1618
1630
  type MetricsSendersType = "sub-accounts" | "campaigns";
1619
1631
  interface MetricsSendersOptions {
@@ -1696,6 +1708,21 @@ interface MetricsOptions {
1696
1708
  interval?: "hour" | "day" | "week" | "month";
1697
1709
  }
1698
1710
  type SuppressionsTypes = "transactional" | "non-transactional";
1711
+ interface SuppressionsCreateEntry {
1712
+ /**
1713
+ * Must be less than `1024` characters.
1714
+ */
1715
+ notes?: string;
1716
+ /**
1717
+ * The email address to suppress. Must be a valid email address format and less than `255` characters.
1718
+ */
1719
+ recipient: string;
1720
+ /**
1721
+ * An array of types of suppression to apply to the recipient.
1722
+ * @default ["non-transactional"]
1723
+ */
1724
+ types?: SuppressionsTypes[];
1725
+ }
1699
1726
  interface SuppressionsCreateOptions {
1700
1727
  /**
1701
1728
  * If true, the parent account creates suppression entries for all associated sub-accounts. This field is only applicable to parent accounts. Sub-accounts cannot create entries for other sub-accounts.
@@ -1704,22 +1731,9 @@ interface SuppressionsCreateOptions {
1704
1731
  addToSubAccounts?: boolean;
1705
1732
  /**
1706
1733
  * The total number of suppression entries to create, for the parent and/or its sub-accounts, must not exceed `1000`.
1734
+ * @deprecated Use `create(entries, options?)` instead.
1707
1735
  */
1708
- entries: {
1709
- /**
1710
- * Must be less than `1024` characters.
1711
- */
1712
- notes?: string;
1713
- /**
1714
- * The email address to suppress. Must be a valid email address format and less than `255` characters.
1715
- */
1716
- recipient: string;
1717
- /**
1718
- * An array of types of suppression to apply to the recipient.
1719
- * @default ["non-transactional"]
1720
- */
1721
- types?: SuppressionsTypes[];
1722
- }[];
1736
+ entries: SuppressionsCreateEntry[];
1723
1737
  }
1724
1738
  type SuppressionsSource = "api" | "unsubscribe_link" | "list_unsubscribe" | "hard_bounce" | "spam_complaint" | "all";
1725
1739
  interface SuppressionsListOptions {
@@ -1877,7 +1891,7 @@ declare class SubAccounts {
1877
1891
  readonly limits: SubAccountsLimits;
1878
1892
  constructor(mailchannels: MailChannelsClient);
1879
1893
  /**
1880
- * Creates a new sub-account under the parent account. Each sub-account must have a unique handle composed solely of lowercase alphanumeric characters. If no handle is provided, a random handle will be generated. Note that Sub-accounts are only available to parent accounts on 100K and higher plans.
1894
+ * Creates a new sub-account under the parent account. Each sub-account must have a unique handle composed solely of lowercase alphanumeric characters. If no handle is provided, a random handle will be generated.
1881
1895
  * @param companyName - The name of the company associated with the sub-account. This name is used for display purposes only and does not affect the functionality of the sub-account. The length must be between 3 and 128 characters.
1882
1896
  * @param handle - A unique name for the sub-account to be created. The length must be between 3 and 128 characters, and it may contain only lowercase letters and numbers. If not provided, a random handle will be generated.
1883
1897
  * @example
@@ -2025,14 +2039,21 @@ declare class Suppressions {
2025
2039
  constructor(mailchannels: MailChannelsClient);
2026
2040
  /**
2027
2041
  * Creates suppression entries for the specified account. Parent accounts can create suppression entries for all associated sub-accounts. If `types` is not provided, it defaults to `non-transactional`. The operation is atomic, meaning all entries are successfully added or none are added if an error occurs.
2028
- * @param options - The details of the suppression entries to create.
2042
+ * @param entries - The total number of suppression entries to create, for the parent and/or its sub-accounts, must not exceed `1000`.
2043
+ * @param options - The options of the suppression entries to create.
2029
2044
  * @example
2030
2045
  * ```ts
2031
2046
  * const mailchannels = new MailChannels('your-api-key')
2032
- * const { success, error } = await mailchannels.suppressions.create({
2033
- * // ...
2034
- * });
2047
+ * const { success, error } = await mailchannels.suppressions.create([
2048
+ * {
2049
+ * notes: "test",
2050
+ * recipient: "name@example.com",
2051
+ * types: ["transactional"]
2052
+ * }
2053
+ * ], { addToSubAccounts: false });
2035
2054
  */
2055
+ create(entries: SuppressionsCreateEntry[], options?: Omit<SuppressionsCreateOptions, "entries">): Promise<SuccessResponse>;
2056
+ /** @deprecated Use positional params `create(entries, options?)` instead. */
2036
2057
  create(options: SuppressionsCreateOptions): Promise<SuccessResponse>;
2037
2058
  /**
2038
2059
  * Deletes suppression entry associated with the account based on the specified recipient and source.
@@ -2070,4 +2091,4 @@ declare class MailChannels extends MailChannelsClient {
2070
2091
  readonly suppressions: Suppressions;
2071
2092
  constructor(key: string, options?: MailChannelsClientOptions);
2072
2093
  }
2073
- export { Attachment, type DataResponse, Domains, type DomainsCheckOptions, type DomainsCheckResponse, type DomainsCheckVerdict, type DomainsCustomTrackingCreateResponse, type DomainsCustomTrackingDnsSetupRequired, type DomainsCustomTrackingDomain, type DomainsCustomTrackingListOptions, type DomainsCustomTrackingListResponse, type DomainsCustomTrackingScope, type DomainsCustomTrackingUpdateOptions, type DomainsCustomTrackingUpdateResponse, type DomainsCustomTrackingWithDnsSetupRequired, type DomainsDkimCreateOptions, type DomainsDkimCreateResponse, type DomainsDkimKey, type DomainsDkimKeyStatus, type DomainsDkimListOptions, type DomainsDkimListResponse, type DomainsDkimRotateOptions, type DomainsDkimRotateResponse, type DomainsDkimUpdateStatusOptions, Emails, type EmailsQueueResponse, type EmailsSendAsyncResponse, type EmailsSendAttachment, type EmailsSendContent, type EmailsSendDkim, type EmailsSendOptions, type EmailsSendPersonalization, type EmailsSendRecipient, type EmailsSendRecipientInput, type EmailsSendResponse, type EmailsSendTemplate, type EmailsSendTemplateType, type EmailsSendTemplateValue, type EmailsSendTracking, type ErrorResponse, type ErrorType, MailChannels, MailChannelsClient, type MailChannelsClientOptions, Metrics, type MetricsBucket, type MetricsEngagement, type MetricsEngagementResponse, type MetricsOptions, type MetricsPerformance, type MetricsPerformanceResponse, type MetricsRecipientBehaviour, type MetricsRecipientBehaviourResponse, type MetricsSenders, type MetricsSendersOptions, type MetricsSendersResponse, type MetricsSendersType, type MetricsUsageResponse, type MetricsVolume, type MetricsVolumeResponse, type SubAccount, SubAccounts, type SubAccountsAccount, type SubAccountsApiKey, type SubAccountsApiKeysCreateResponse, type SubAccountsApiKeysListOptions, type SubAccountsApiKeysListResponse, type SubAccountsCreateApiKeyResponse, type SubAccountsCreateResponse, type SubAccountsCreateSmtpPasswordResponse, type SubAccountsLimit, type SubAccountsLimitResponse, type SubAccountsLimitsGetResponse, type SubAccountsLimitsSetOptions, type SubAccountsListApiKeyOptions, type SubAccountsListApiKeyResponse, type SubAccountsListOptions, type SubAccountsListResponse, type SubAccountsListSmtpPasswordResponse, type SubAccountsSmtpPassword, type SubAccountsSmtpPasswordsCreateResponse, type SubAccountsSmtpPasswordsListResponse, type SubAccountsUsage, type SubAccountsUsageResponse, type SuccessResponse, Suppressions, type SuppressionsCreateOptions, type SuppressionsListEntry, type SuppressionsListOptions, type SuppressionsListResponse, type SuppressionsSource, type SuppressionsTypes, type WebhookEvent, type WebhookEventClick, type WebhookEventComplained, type WebhookEventDelivered, type WebhookEventDropped, type WebhookEventHardBounced, type WebhookEventOpen, type WebhookEventProcessed, type WebhookEventSoftBounced, type WebhookEventTest, type WebhookEventType, type WebhookEventUnsubscribed, Webhooks, type WebhooksBatch, type WebhooksBatchResponseStatus, type WebhooksBatchStatus, type WebhooksBatchesOptions, type WebhooksBatchesResponse, type WebhooksListResponse, type WebhooksResendBatch, type WebhooksResendBatchResponse, type WebhooksSigningKeyResponse, type WebhooksValidateResponse, type WebhooksVerifyOptions, type WebhooksVerifyResponse };
2094
+ export { Attachment, type DataResponse, Domains, type DomainsCheckOptions, type DomainsCheckResponse, type DomainsCheckVerdict, type DomainsCustomTrackingCreateResponse, type DomainsCustomTrackingDnsSetupRequired, type DomainsCustomTrackingDomain, type DomainsCustomTrackingListOptions, type DomainsCustomTrackingListResponse, type DomainsCustomTrackingScope, type DomainsCustomTrackingUpdateOptions, type DomainsCustomTrackingUpdateResponse, type DomainsCustomTrackingWithDnsSetupRequired, type DomainsDkimCreateOptions, type DomainsDkimCreateResponse, type DomainsDkimKey, type DomainsDkimKeyStatus, type DomainsDkimListOptions, type DomainsDkimListResponse, type DomainsDkimRotateOptions, type DomainsDkimRotateResponse, type DomainsDkimUpdateStatusOptions, Emails, type EmailsQueueResponse, type EmailsSendAsyncResponse, type EmailsSendAttachment, type EmailsSendContent, type EmailsSendDkim, type EmailsSendOptions, type EmailsSendPersonalization, type EmailsSendRecipient, type EmailsSendRecipientInput, type EmailsSendResponse, type EmailsSendTemplate, type EmailsSendTemplateType, type EmailsSendTemplateValue, type EmailsSendTracking, type ErrorResponse, type ErrorType, MailChannels, MailChannelsClient, type MailChannelsClientOptions, Metrics, type MetricsBucket, type MetricsEngagement, type MetricsEngagementResponse, type MetricsOptions, type MetricsPerformance, type MetricsPerformanceResponse, type MetricsRecipientBehaviour, type MetricsRecipientBehaviourResponse, type MetricsSenders, type MetricsSendersOptions, type MetricsSendersResponse, type MetricsSendersType, type MetricsUsageResponse, type MetricsVolume, type MetricsVolumeResponse, type SubAccount, SubAccounts, type SubAccountsAccount, type SubAccountsApiKey, type SubAccountsApiKeysCreateResponse, type SubAccountsApiKeysListOptions, type SubAccountsApiKeysListResponse, type SubAccountsCreateApiKeyResponse, type SubAccountsCreateResponse, type SubAccountsCreateSmtpPasswordResponse, type SubAccountsLimit, type SubAccountsLimitResponse, type SubAccountsLimitsGetResponse, type SubAccountsLimitsSetOptions, type SubAccountsListApiKeyOptions, type SubAccountsListApiKeyResponse, type SubAccountsListOptions, type SubAccountsListResponse, type SubAccountsListSmtpPasswordResponse, type SubAccountsSmtpPassword, type SubAccountsSmtpPasswordsCreateResponse, type SubAccountsSmtpPasswordsListResponse, type SubAccountsUsage, type SubAccountsUsageResponse, type SuccessResponse, Suppressions, type SuppressionsCreateEntry, type SuppressionsCreateOptions, type SuppressionsListEntry, type SuppressionsListOptions, type SuppressionsListResponse, type SuppressionsSource, type SuppressionsTypes, type WebhookEvent, type WebhookEventClick, type WebhookEventComplained, type WebhookEventDelivered, type WebhookEventDropped, type WebhookEventHardBounced, type WebhookEventOpen, type WebhookEventProcessed, type WebhookEventSoftBounced, type WebhookEventTest, type WebhookEventType, type WebhookEventUnsubscribed, Webhooks, type WebhooksBatch, type WebhooksBatchResponseStatus, type WebhooksBatchStatus, type WebhooksBatchesOptions, type WebhooksBatchesResponse, type WebhooksListResponse, type WebhooksResendBatch, type WebhooksResendBatchResponse, type WebhooksSigningKeyResponse, type WebhooksValidateResponse, type WebhooksVerifyOptions, type WebhooksVerifyResponse };
@@ -2,7 +2,7 @@ import { $fetch } from "ofetch";
2
2
  import { subtle } from "node:crypto";
3
3
  import { Buffer } from "node:buffer";
4
4
  import mime from "mime";
5
- var version = "1.2.0";
5
+ var version = "1.3.1";
6
6
  var MailChannelsClient = class MailChannelsClient {
7
7
  static DEFAULT_BASE_URL = "https://api.mailchannels.net";
8
8
  static DEFAULT_TIMEOUT = 12e4;
@@ -225,8 +225,7 @@ const mapAttachment = (attachment) => ({
225
225
  content: attachment.content,
226
226
  filename: attachment.filename,
227
227
  type: attachment.type,
228
- content_id: attachment.contentId,
229
- disposition: attachment.disposition
228
+ content_id: attachment.contentId
230
229
  });
231
230
  const mapPersonalization = (personalization, index, rootTemplateData) => {
232
231
  const to = parseArrayRecipients(personalization.to);
@@ -1239,7 +1238,7 @@ var Webhooks = class Webhooks {
1239
1238
  if (dates.createdAfter && dates.createdBefore) {
1240
1239
  const createdAfter = Date.parse(dates.createdAfter);
1241
1240
  const createdBefore = Date.parse(dates.createdBefore);
1242
- const maxRangeMs = 744 * 60 * 60 * 1e3;
1241
+ const maxRangeMs = 26784e5;
1243
1242
  if (createdBefore <= createdAfter) return {
1244
1243
  data: null,
1245
1244
  error: createValidationError("createdBefore must be later than createdAfter.")
@@ -1743,7 +1742,8 @@ var SubAccounts = class SubAccounts {
1743
1742
  data: clean({
1744
1743
  endDate: response.period_end_date,
1745
1744
  startDate: response.period_start_date,
1746
- total: response.total_usage
1745
+ total: response.total_usage,
1746
+ monthlyLimit: response.monthly_limit
1747
1747
  }),
1748
1748
  error: null
1749
1749
  };
@@ -1989,7 +1989,8 @@ var Metrics = class {
1989
1989
  data: clean({
1990
1990
  endDate: response.period_end_date,
1991
1991
  startDate: response.period_start_date,
1992
- total: response.total_usage
1992
+ total: response.total_usage,
1993
+ monthlyLimit: response.monthly_limit
1993
1994
  }),
1994
1995
  error: null
1995
1996
  };
@@ -2049,9 +2050,10 @@ var Suppressions = class {
2049
2050
  constructor(mailchannels) {
2050
2051
  this.mailchannels = mailchannels;
2051
2052
  }
2052
- async create(options) {
2053
+ async create(entriesOrOptions, options) {
2053
2054
  let error = null;
2054
- const { addToSubAccounts, entries } = options;
2055
+ const entries = Array.isArray(entriesOrOptions) ? entriesOrOptions : entriesOrOptions.entries;
2056
+ const createOptions = Array.isArray(entriesOrOptions) ? options : entriesOrOptions;
2055
2057
  if (entries.length > 1e3) {
2056
2058
  error = createValidationError("The number of suppression entries must not exceed 1000.");
2057
2059
  return {
@@ -2060,7 +2062,7 @@ var Suppressions = class {
2060
2062
  };
2061
2063
  }
2062
2064
  const payload = {
2063
- add_to_sub_accounts: addToSubAccounts,
2065
+ add_to_sub_accounts: createOptions?.addToSubAccounts,
2064
2066
  suppression_entries: entries.map((entry) => ({
2065
2067
  notes: entry.notes,
2066
2068
  recipient: entry.recipient,
@@ -2167,13 +2169,12 @@ const guessContentType = (filename) => {
2167
2169
  };
2168
2170
  var Attachment = class Attachment {
2169
2171
  static fromBytes(data, options) {
2170
- const { filename, type, contentId, disposition = "attachment" } = options;
2172
+ const { filename, type, contentId } = options;
2171
2173
  return {
2172
2174
  content: base64Content(data),
2173
2175
  filename: decodeURIComponent(filename) || "attachment",
2174
2176
  type: type || guessContentType(filename),
2175
- contentId,
2176
- disposition
2177
+ contentId
2177
2178
  };
2178
2179
  }
2179
2180
  static async fromBlob(blob, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mailchannels-sdk",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Node.js SDK to integrate MailChannels Email API into your JavaScript or TypeScript server-side applications.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,8 +29,8 @@
29
29
  },
30
30
  "types": "./dist/mailchannels.d.mts",
31
31
  "bin": {
32
- "mailchannels-sdk": "./dist/cli.mjs",
33
- "mailchannels": "./dist/cli.mjs"
32
+ "mailchannels-sdk": "./dist/cli/index.mjs",
33
+ "mailchannels": "./dist/cli/index.mjs"
34
34
  },
35
35
  "files": [
36
36
  "dist",
@@ -43,16 +43,16 @@
43
43
  "devDependencies": {
44
44
  "@stylistic/eslint-plugin": "^5.10.0",
45
45
  "@types/markdown-it": "^14.1.2",
46
- "@types/node": "^26.1.1",
46
+ "@types/node": "^26.1.2",
47
47
  "@vitest/coverage-v8": "^4.1.10",
48
48
  "changelogen": "^0.6.2",
49
- "obuild": "^0.4.37",
50
- "oxlint": "^1.73.0",
49
+ "obuild": "^0.4.38",
50
+ "oxlint": "^1.76.0",
51
51
  "scule": "^1.3.0",
52
52
  "typescript": "^6.0.3",
53
53
  "vitepress": "^2.0.0-alpha.18",
54
- "vitepress-plugin-group-icons": "^1.7.5",
55
- "vitepress-plugin-llms": "^1.13.2",
54
+ "vitepress-plugin-group-icons": "^1.7.6",
55
+ "vitepress-plugin-llms": "^1.13.4",
56
56
  "vitest": "^4.1.10"
57
57
  },
58
58
  "engines": {
@@ -62,7 +62,7 @@
62
62
  "build": "obuild",
63
63
  "parity:fixtures": "node scripts/generate-parity-fixtures.ts",
64
64
  "release": "pnpm lint && pnpm test && pnpm build && changelogen --bump",
65
- "simulate": "node src/cli.ts simulate",
65
+ "simulate": "node src/cli/index.ts simulate",
66
66
  "lint": "oxlint",
67
67
  "lint:fix": "oxlint --fix",
68
68
  "test": "vitest run --reporter=verbose --coverage",
package/dist/cli.d.mts DELETED
@@ -1 +0,0 @@
1
- export { };
package/dist/cli.mjs DELETED
@@ -1,50 +0,0 @@
1
- #!/usr/bin/env node
2
- import { parseArgs } from "node:util";
3
- const LOGGER_NAME = "[MailChannels-CLI]";
4
- console.info = console.info.bind(console.info, LOGGER_NAME);
5
- console.error = console.error.bind(console.error, LOGGER_NAME);
6
- const [command, ...args] = process.argv.slice(2);
7
- switch (command) {
8
- case "simulate":
9
- const { createSimulator } = await import("./_chunks/simulator.mjs");
10
- const { MAILCHANNELS_SIMULATOR_PORT, MAILCHANNELS_SIMULATOR_HOST } = process.env;
11
- const { values } = parseArgs({
12
- args,
13
- options: {
14
- port: {
15
- type: "string",
16
- short: "p",
17
- default: MAILCHANNELS_SIMULATOR_PORT
18
- },
19
- host: {
20
- type: "string",
21
- short: "h",
22
- default: MAILCHANNELS_SIMULATOR_HOST
23
- },
24
- silent: {
25
- type: "boolean",
26
- short: "s",
27
- default: false
28
- }
29
- }
30
- });
31
- const port = values.port !== void 0 ? Number.parseInt(values.port, 10) : void 0;
32
- if (port !== void 0 && (isNaN(port) || port < 0 || port > 65535)) {
33
- console.error("[Simulator]", `Invalid port "${values.port}": must be an integer between 0 and 65535.`);
34
- process.exit(1);
35
- }
36
- const simulator = createSimulator({
37
- host: values.host,
38
- port,
39
- silent: values.silent
40
- });
41
- await simulator.listen();
42
- const shutdown = async () => {
43
- await simulator.close();
44
- process.exit(0);
45
- };
46
- process.on("SIGINT", shutdown);
47
- process.on("SIGTERM", shutdown);
48
- break;
49
- }
50
- export {};