mailchannels-sdk 1.3.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:
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.5.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" };
@@ -313,8 +314,8 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
313
314
  }
314
315
  targetKey.status = "rotated";
315
316
  targetKey.status_modified_at = currentTimestamp();
316
- targetKey.gracePeriodExpiresAt = new Date(Date.now() + 10080 * 60 * 1e3).toISOString();
317
- 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();
318
319
  const newKey = createDkimKey(domain, body?.new_key?.selector || createId("selector"));
319
320
  account.dkimKeysByDomain.set(domain, [...keys, newKey]);
320
321
  sendJson(response, 201, {
@@ -627,7 +628,7 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
627
628
  end_time: url.searchParams.get("end_time") || currentTimestamp(),
628
629
  open: summary.open,
629
630
  open_tracking_delivered: summary.openTrackingDelivered,
630
- 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()
631
632
  });
632
633
  return;
633
634
  }
@@ -646,7 +647,7 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
646
647
  delivered: summary.delivered,
647
648
  end_time: url.searchParams.get("end_time") || currentTimestamp(),
648
649
  processed: summary.processed,
649
- 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()
650
651
  });
651
652
  return;
652
653
  }
@@ -659,7 +660,7 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
659
660
  unsubscribed: createMetricsBuckets(summary.unsubscribed)
660
661
  },
661
662
  end_time: url.searchParams.get("end_time") || currentTimestamp(),
662
- 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(),
663
664
  unsubscribe_delivered: summary.unsubscribeDelivered,
664
665
  unsubscribed: summary.unsubscribed
665
666
  });
@@ -678,7 +679,7 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
678
679
  dropped: summary.dropped,
679
680
  end_time: url.searchParams.get("end_time") || currentTimestamp(),
680
681
  processed: summary.processed,
681
- 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()
682
683
  });
683
684
  return;
684
685
  }
@@ -718,7 +719,7 @@ const createEmailApiHandler = ({ logRequests = true } = {}) => {
718
719
  limit,
719
720
  offset,
720
721
  senders: senders.slice(offset, offset + limit),
721
- start_time: (/* @__PURE__ */ new Date(Date.now() - 720 * 60 * 60 * 1e3)).toISOString(),
722
+ start_time: (/* @__PURE__ */ new Date(Date.now() - 2592e6)).toISOString(),
722
723
  total: senders.length
723
724
  });
724
725
  return;
@@ -796,6 +797,10 @@ const DEFAULT_HOST = "127.0.0.1";
796
797
  const DEFAULT_PORT = 8787;
797
798
  const createSimulator = (options = {}) => {
798
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
+ }
799
804
  const logRequests = !options.silent;
800
805
  const emailApi = createEmailApiHandler({ logRequests });
801
806
  const server = createServer(async (request, response) => {
@@ -843,4 +848,39 @@ const createSimulator = (options = {}) => {
843
848
  }
844
849
  };
845
850
  };
846
- 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.
@@ -1893,7 +1891,7 @@ declare class SubAccounts {
1893
1891
  readonly limits: SubAccountsLimits;
1894
1892
  constructor(mailchannels: MailChannelsClient);
1895
1893
  /**
1896
- * 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.
1897
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.
1898
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.
1899
1897
  * @example
@@ -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.3.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.")
@@ -2170,13 +2169,12 @@ const guessContentType = (filename) => {
2170
2169
  };
2171
2170
  var Attachment = class Attachment {
2172
2171
  static fromBytes(data, options) {
2173
- const { filename, type, contentId, disposition = "attachment" } = options;
2172
+ const { filename, type, contentId } = options;
2174
2173
  return {
2175
2174
  content: base64Content(data),
2176
2175
  filename: decodeURIComponent(filename) || "attachment",
2177
2176
  type: type || guessContentType(filename),
2178
- contentId,
2179
- disposition
2177
+ contentId
2180
2178
  };
2181
2179
  }
2182
2180
  static async fromBlob(blob, options) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mailchannels-sdk",
3
- "version": "1.3.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
49
  "obuild": "^0.4.38",
50
- "oxlint": "^1.74.0",
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.3",
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 {};