mailchannels-sdk 1.3.1 → 1.4.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.
Files changed (30) hide show
  1. package/.agents/skills/mailchannels-js/SKILL.md +4 -0
  2. package/.agents/skills/mailchannels-js/resources/attachments.md +7 -7
  3. package/.agents/skills/mailchannels-js/resources/clients-and-transport.md +5 -5
  4. package/.agents/skills/mailchannels-js/resources/custom-headers.md +4 -4
  5. package/.agents/skills/mailchannels-js/resources/dkim.md +10 -10
  6. package/.agents/skills/mailchannels-js/resources/domain-checks.md +6 -6
  7. package/.agents/skills/mailchannels-js/resources/error-handling.md +5 -5
  8. package/.agents/skills/mailchannels-js/resources/metrics-and-usage.md +9 -9
  9. package/.agents/skills/mailchannels-js/resources/overview.md +5 -5
  10. package/.agents/skills/mailchannels-js/resources/plugins/nodemailer.md +112 -0
  11. package/.agents/skills/mailchannels-js/resources/sending.md +10 -10
  12. package/.agents/skills/mailchannels-js/resources/sub-accounts.md +7 -7
  13. package/.agents/skills/mailchannels-js/resources/suppressions.md +4 -4
  14. package/.agents/skills/mailchannels-js/resources/templates.md +5 -5
  15. package/.agents/skills/mailchannels-js/resources/testing.md +52 -5
  16. package/.agents/skills/mailchannels-js/resources/unsubscribe.md +3 -3
  17. package/.agents/skills/mailchannels-js/resources/webhooks.md +8 -8
  18. package/README.md +71 -0
  19. package/dist/_chunks/mailchannels.d.mts +2094 -0
  20. package/dist/_chunks/mailchannels.mjs +2201 -0
  21. package/dist/_chunks/{simulate.mjs → simulator.mjs} +7 -39
  22. package/dist/cli/index.mjs +266 -13
  23. package/dist/mailchannels.d.mts +1 -2093
  24. package/dist/mailchannels.mjs +1 -2199
  25. package/dist/plugins/nodemailer/index.d.mts +33 -0
  26. package/dist/plugins/nodemailer/index.mjs +145 -0
  27. package/dist/simulator/index.d.mts +20 -0
  28. package/dist/simulator/index.mjs +2 -0
  29. package/package.json +18 -6
  30. package/dist/cli/index.d.mts +0 -1
@@ -1,5 +1,4 @@
1
1
  import { randomBytes, randomUUID } from "node:crypto";
2
- import { parseArgs } from "node:util";
3
2
  import { createServer } from "node:http";
4
3
  const SIMULATOR_SIGNING_KEY_ID = "simulator-default";
5
4
  const JSON_HEADERS = { "content-type": "application/json" };
@@ -798,7 +797,7 @@ const DEFAULT_PORT = 8787;
798
797
  const createSimulator = (options = {}) => {
799
798
  const { host = DEFAULT_HOST, port = DEFAULT_PORT } = options;
800
799
  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.`);
800
+ console.error(`[Simulator] Invalid port '${port}': must be an integer between 0 and 65535.`);
802
801
  process.exit(1);
803
802
  }
804
803
  const logRequests = !options.silent;
@@ -824,6 +823,7 @@ const createSimulator = (options = {}) => {
824
823
  },
825
824
  async close() {
826
825
  await new Promise((resolve, reject) => {
826
+ console.info("[Simulator] Shutting down simulator...");
827
827
  for (const socket of sockets) socket.destroy();
828
828
  server.close((error) => error ? reject(error) : resolve());
829
829
  });
@@ -840,47 +840,15 @@ const createSimulator = (options = {}) => {
840
840
  server.off("error", onError);
841
841
  resolve();
842
842
  });
843
+ }).catch((error) => {
844
+ console.error(`[Simulator] ${error.message}`);
845
+ process.exit(1);
843
846
  });
844
847
  const address = server.address();
845
848
  serverUrl = `http://${nextHost}:${typeof address === "object" && address ? address.port : nextPort}`;
846
- if (logRequests) console.info("[Simulator]", `listening on ${serverUrl}`);
849
+ if (logRequests) console.info(`[Simulator] listening on ${serverUrl}`);
847
850
  return serverUrl;
848
851
  }
849
852
  };
850
853
  };
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 };
854
+ export { createSimulator };
@@ -1,15 +1,268 @@
1
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
- }
2
+ import { Attachment, Emails, MailChannelsClient, name, version } from "../_chunks/mailchannels.mjs";
3
+ import { createSimulator } from "../_chunks/simulator.mjs";
4
+ import { defineCommand, runMain } from "citty";
5
+ var simulate_default = defineCommand({
6
+ meta: {
7
+ name: "simulate",
8
+ description: "Start a local MailChannels simulator server"
9
+ },
10
+ args: {
11
+ port: {
12
+ type: "string",
13
+ alias: "p"
14
+ },
15
+ host: {
16
+ type: "string",
17
+ alias: "h"
18
+ },
19
+ silent: {
20
+ type: "boolean",
21
+ alias: "s",
22
+ default: false
23
+ }
24
+ },
25
+ async run({ args }) {
26
+ const { MAILCHANNELS_SIMULATOR_HOST, MAILCHANNELS_SIMULATOR_PORT } = process.env;
27
+ const host = args.host || MAILCHANNELS_SIMULATOR_HOST;
28
+ const port = args.port || MAILCHANNELS_SIMULATOR_PORT;
29
+ const simulator = createSimulator({
30
+ host,
31
+ port: port !== void 0 ? Number.parseInt(port, 10) : void 0,
32
+ silent: args.silent
33
+ });
34
+ await simulator.listen();
35
+ const shutdown = async () => {
36
+ await simulator.close();
37
+ };
38
+ process.on("SIGINT", shutdown);
39
+ process.on("SIGTERM", shutdown);
40
+ }
41
+ });
42
+ const emailArgs = {
43
+ "api-key": {
44
+ type: "string",
45
+ alias: "k"
46
+ },
47
+ "campaign-id": { type: "string" },
48
+ "dkim-domain": { type: "string" },
49
+ "dkim-selector": { type: "string" },
50
+ "dkim-private-key": { type: "string" },
51
+ "envelope-from": { type: "string" },
52
+ "tracking-click": {
53
+ type: "boolean",
54
+ default: false
55
+ },
56
+ "tracking-click-custom-domain-name": {
57
+ type: "string",
58
+ valueHint: "name"
59
+ },
60
+ "tracking-open": {
61
+ type: "boolean",
62
+ default: false
63
+ },
64
+ "tracking-open-custom-domain-name": {
65
+ type: "string",
66
+ valueHint: "name"
67
+ },
68
+ "from": {
69
+ type: "string",
70
+ required: true
71
+ },
72
+ "headers": {
73
+ type: "string",
74
+ alias: "h",
75
+ valueHint: "JSON"
76
+ },
77
+ "to": {
78
+ type: "string",
79
+ required: true
80
+ },
81
+ "subject": {
82
+ type: "string",
83
+ required: true
84
+ },
85
+ "text": {
86
+ type: "string",
87
+ default: ""
88
+ },
89
+ "html": {
90
+ type: "string",
91
+ default: ""
92
+ },
93
+ "cc": { type: "string" },
94
+ "bcc": { type: "string" },
95
+ "reply-to": { type: "string" },
96
+ "attachments": {
97
+ type: "boolean",
98
+ alias: "a",
99
+ default: false
100
+ },
101
+ "transactional": {
102
+ type: "boolean",
103
+ default: true
104
+ },
105
+ "unsubscribe-custom-domain-name": {
106
+ type: "string",
107
+ valueHint: "name"
108
+ }
109
+ };
110
+ const getApiKey = (apiKey) => {
111
+ const { MAILCHANNELS_API_KEY } = process.env;
112
+ const key = MAILCHANNELS_API_KEY || apiKey;
113
+ if (!key) {
114
+ console.error("[Emails] Missing API key. Provide it using the '--api-key' flag or set the 'MAILCHANNELS_API_KEY' environment variable.");
115
+ process.exit(1);
116
+ }
117
+ return key;
118
+ };
119
+ const parseAttachments = async (rawAttachments) => {
120
+ if (!rawAttachments) return;
121
+ if (process.stdin.isTTY) {
122
+ console.error("[Emails] '--attachments' requires JSON input piped to stdin.");
123
+ process.exit(1);
124
+ }
125
+ let input = "";
126
+ for await (const chunk of process.stdin) input += chunk;
127
+ let inputAttachments = [];
128
+ try {
129
+ inputAttachments = JSON.parse(input);
130
+ } catch {
131
+ console.error("[Emails] JSON input for '--attachments' is not valid.");
132
+ process.exit(1);
133
+ }
134
+ return inputAttachments.map((attachment, index) => {
135
+ if (!attachment.content || !attachment.filename) {
136
+ console.error("[Emails] Attachment at index " + index + " is missing required fields 'content' or 'filename'.");
137
+ process.exit(1);
138
+ }
139
+ if (attachment.type) return {
140
+ content: attachment.content,
141
+ filename: attachment.filename,
142
+ type: attachment.type,
143
+ contentId: attachment.content_id
144
+ };
145
+ return Attachment.fromBytes(Buffer.from(attachment.content, "base64"), {
146
+ filename: attachment.filename,
147
+ type: attachment.type,
148
+ contentId: attachment.content_id
149
+ });
150
+ });
151
+ };
152
+ const parseHeaders = (rawHeaders) => {
153
+ if (!rawHeaders) return;
154
+ try {
155
+ return JSON.parse(rawHeaders);
156
+ } catch {
157
+ console.error("[Emails] Invalid JSON for '--headers'");
158
+ process.exit(1);
159
+ }
160
+ };
161
+ const parseOptions = async (args) => {
162
+ const attachments = await parseAttachments(args["attachments"]);
163
+ const headers = parseHeaders(args["headers"]);
164
+ return {
165
+ attachments,
166
+ campaignId: args["campaign-id"],
167
+ dkim: {
168
+ domain: args["dkim-domain"],
169
+ selector: args["dkim-selector"],
170
+ privateKey: args["dkim-private-key"]
171
+ },
172
+ envelopeFrom: args["envelope-from"],
173
+ tracking: {
174
+ click: {
175
+ customDomainName: args["tracking-click-custom-domain-name"],
176
+ enable: args["tracking-click"] || Boolean(args["tracking-click-custom-domain-name"])
177
+ },
178
+ open: {
179
+ customDomainName: args["tracking-open-custom-domain-name"],
180
+ enable: args["tracking-open"] || Boolean(args["tracking-open-custom-domain-name"])
181
+ }
182
+ },
183
+ headers,
184
+ from: args["from"],
185
+ to: args["to"].split(",").map((r) => r.trim()),
186
+ subject: args["subject"],
187
+ cc: args["cc"]?.split(",").map((r) => r.trim()),
188
+ bcc: args["bcc"]?.split(",").map((r) => r.trim()),
189
+ replyTo: args["reply-to"],
190
+ html: args["html"],
191
+ text: args["text"],
192
+ transactional: args["transactional"],
193
+ unsubscribe: { customDomainName: args["unsubscribe-custom-domain-name"] }
194
+ };
195
+ };
196
+ var send_default = defineCommand({
197
+ meta: {
198
+ name: "send",
199
+ description: "Send an email using the MailChannels Email API"
200
+ },
201
+ args: {
202
+ ...emailArgs,
203
+ "dry-run": {
204
+ type: "boolean",
205
+ default: false
206
+ }
207
+ },
208
+ async run({ args }) {
209
+ const apiKey = getApiKey(args["api-key"]);
210
+ const client = new MailChannelsClient(apiKey);
211
+ const emails = new Emails(client);
212
+ const sendOptions = await parseOptions(args);
213
+ const { error } = await emails.send(sendOptions, args["dry-run"]);
214
+ if (error) {
215
+ console.error(`[Emails] ${error.message}`);
216
+ process.exit(1);
217
+ }
218
+ console.info("[Emails] Email sent successfully.");
219
+ }
220
+ });
221
+ var queue_default = defineCommand({
222
+ meta: {
223
+ name: "queue",
224
+ description: "Queue an email to be sent using the MailChannels Email API"
225
+ },
226
+ args: emailArgs,
227
+ async run({ args }) {
228
+ const apiKey = getApiKey(args["api-key"]);
229
+ const client = new MailChannelsClient(apiKey);
230
+ const emails = new Emails(client);
231
+ const sendOptions = await parseOptions(args);
232
+ const { error } = await emails.queue(sendOptions);
233
+ if (error) {
234
+ console.error(`[Emails] ${error.message}`);
235
+ process.exit(1);
236
+ }
237
+ console.info("[Emails] Email queued successfully.");
238
+ }
239
+ });
240
+ var emails_default = defineCommand({
241
+ meta: {
242
+ name: "emails",
243
+ description: "Commands for sending emails using the MailChannels Email API"
244
+ },
245
+ args: {},
246
+ subCommands: {
247
+ send: send_default,
248
+ queue: queue_default
249
+ }
250
+ });
251
+ const main = defineCommand({
252
+ meta: {
253
+ name,
254
+ description: "MailChannels CLI",
255
+ version
256
+ },
257
+ subCommands: {
258
+ simulate: simulate_default,
259
+ emails: emails_default
260
+ },
261
+ setup() {
262
+ const LOGGER_NAME = "[MailChannels-CLI]";
263
+ console.info = console.info.bind(console, LOGGER_NAME);
264
+ console.error = console.error.bind(console, LOGGER_NAME);
265
+ }
266
+ });
267
+ runMain(main);
15
268
  export {};