mikromail 0.0.5 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  **Lightweight replacement for Nodemailer, supporting HTML, international symbols, and more**.
4
4
 
5
5
  ![Build Status](https://github.com/mikaelvesavuori/mikromail/workflows/main/badge.svg)
6
+ [![License](https://img.shields.io/badge/license-MIT-green.svg)](https://opensource.org/licenses/MIT)
6
7
 
7
8
  ---
8
9
 
@@ -14,7 +15,7 @@
14
15
 
15
16
  ## Usage
16
17
 
17
- ### Basic importing and usage
18
+ ### Quick Start
18
19
 
19
20
  ```typescript
20
21
  import { MikroMail } from 'MikroMail';
@@ -29,7 +30,7 @@ const emailOptions = {
29
30
  from: 'me@mydomain.com',
30
31
  subject: 'Test Email',
31
32
  text: 'Hello!',
32
- to: 'you@yourdomain.com'
33
+ to: 'you@yourdomain.com' // You can also send to multiple recipients: ['sam@acmecorp.cloud', 'sammy@acmecorp.cloud']
33
34
  };
34
35
 
35
36
  await new MikroMail({ config }).send(emailOptions);
@@ -98,4 +99,4 @@ Some of the tests require faking an SMTP server. Here we use [Mailpit](https://g
98
99
 
99
100
  ## License
100
101
 
101
- MIT
102
+ MIT. See the `LICENSE` file.
package/lib/MikroMail.js CHANGED
@@ -546,23 +546,21 @@ var SMTPClient = class {
546
546
  const messageId = this.generateMessageId();
547
547
  const date = (/* @__PURE__ */ new Date()).toUTCString();
548
548
  const from = options.from || this.config.user;
549
- const { to } = options;
549
+ const recipients = Array.isArray(options.to) ? options.to.join(", ") : options.to;
550
550
  const headers = [
551
551
  `From: ${this.sanitizeHeader(from)}`,
552
- `To: ${this.sanitizeHeader(to)}`,
552
+ `To: ${this.sanitizeHeader(recipients)}`,
553
553
  `Subject: ${this.sanitizeHeader(options.subject)}`,
554
554
  `Message-ID: ${messageId}`,
555
555
  `Date: ${date}`,
556
- "MIME-Version: 1.0",
557
- "Content-Transfer-Encoding: quoted-printable"
556
+ "MIME-Version: 1.0"
558
557
  ];
559
558
  if (options.cc) {
560
559
  const cc = Array.isArray(options.cc) ? options.cc.join(", ") : options.cc;
561
560
  headers.push(`Cc: ${this.sanitizeHeader(cc)}`);
562
561
  }
563
- if (options.replyTo) {
562
+ if (options.replyTo)
564
563
  headers.push(`Reply-To: ${this.sanitizeHeader(options.replyTo)}`);
565
- }
566
564
  if (options.headers) {
567
565
  for (const [name, value] of Object.entries(options.headers)) {
568
566
  if (!/^[a-zA-Z0-9-]+$/.test(name)) continue;
@@ -587,13 +585,11 @@ var SMTPClient = class {
587
585
  \r
588
586
  --${boundary}\r
589
587
  Content-Type: text/plain; charset=utf-8\r
590
- Content-Transfer-Encoding: quoted-printable\r
591
588
  \r
592
589
  ${text || ""}\r
593
590
  \r
594
591
  --${boundary}\r
595
592
  Content-Type: text/html; charset=utf-8\r
596
- Content-Transfer-Encoding: quoted-printable\r
597
593
  \r
598
594
  ${html || ""}\r
599
595
  \r
@@ -646,12 +642,21 @@ ${text || ""}`;
646
642
  error: "Missing required email parameters (from, to, subject, and either text or html)"
647
643
  };
648
644
  }
649
- if (!validateEmail(from) || !validateEmail(to)) {
645
+ if (!validateEmail(from)) {
650
646
  return {
651
647
  success: false,
652
648
  error: "Invalid email address format"
653
649
  };
654
650
  }
651
+ const recipients = Array.isArray(options.to) ? options.to : [options.to];
652
+ for (const recipient of recipients) {
653
+ if (!validateEmail(recipient)) {
654
+ return {
655
+ success: false,
656
+ error: `Invalid recipient email address format: ${recipient}`
657
+ };
658
+ }
659
+ }
655
660
  for (this.retryCount = 0; this.retryCount <= this.config.maxRetries; this.retryCount++) {
656
661
  try {
657
662
  if (this.retryCount > 0) {
@@ -667,21 +672,20 @@ ${text || ""}`;
667
672
  await this.smtpHandshake();
668
673
  }
669
674
  await this.sendCommand(`MAIL FROM:<${from}>`, 250);
670
- await this.sendCommand(`RCPT TO:<${to}>`, 250);
675
+ for (const recipient of recipients)
676
+ await this.sendCommand(`RCPT TO:<${recipient}>`, 250);
671
677
  if (options.cc) {
672
678
  const ccList = Array.isArray(options.cc) ? options.cc : [options.cc];
673
679
  for (const cc of ccList) {
674
- if (validateEmail(cc)) {
680
+ if (validateEmail(cc))
675
681
  await this.sendCommand(`RCPT TO:<${cc}>`, 250);
676
- }
677
682
  }
678
683
  }
679
684
  if (options.bcc) {
680
685
  const bccList = Array.isArray(options.bcc) ? options.bcc : [options.bcc];
681
686
  for (const bcc of bccList) {
682
- if (validateEmail(bcc)) {
687
+ if (validateEmail(bcc))
683
688
  await this.sendCommand(`RCPT TO:<${bcc}>`, 250);
684
- }
685
689
  }
686
690
  }
687
691
  await this.sendCommand("DATA", 354);
@@ -760,9 +764,14 @@ var MikroMail = class {
760
764
  */
761
765
  async send(emailOptions) {
762
766
  try {
763
- const hasMXRecords = await verifyEmailDomain(emailOptions.to);
764
- if (!hasMXRecords)
765
- console.error("Warning: No MX records found for recipient domain");
767
+ const recipients = Array.isArray(emailOptions.to) ? emailOptions.to : [emailOptions.to];
768
+ for (const recipient of recipients) {
769
+ const hasMXRecords = await verifyEmailDomain(recipient);
770
+ if (!hasMXRecords)
771
+ console.error(
772
+ `Warning: No MX records found for recipient domain: ${recipient}`
773
+ );
774
+ }
766
775
  const result = await this.smtpClient.sendEmail(emailOptions);
767
776
  if (result.success) console.log(`Message ID: ${result.messageId}`);
768
777
  else console.error(`Failed to send email: ${result.error}`);
package/lib/MikroMail.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  MikroMail
3
- } from "./chunk-JNBOBHOL.mjs";
3
+ } from "./chunk-422R3NOG.mjs";
4
4
  import "./chunk-YVXB6HCK.mjs";
5
- import "./chunk-NGYJ2CZH.mjs";
5
+ import "./chunk-NGT3KX7A.mjs";
6
6
  import "./chunk-47VXJTWV.mjs";
7
7
  import "./chunk-UDLJWUFN.mjs";
8
8
  export {
package/lib/SMTPClient.js CHANGED
@@ -402,23 +402,21 @@ var SMTPClient = class {
402
402
  const messageId = this.generateMessageId();
403
403
  const date = (/* @__PURE__ */ new Date()).toUTCString();
404
404
  const from = options.from || this.config.user;
405
- const { to } = options;
405
+ const recipients = Array.isArray(options.to) ? options.to.join(", ") : options.to;
406
406
  const headers = [
407
407
  `From: ${this.sanitizeHeader(from)}`,
408
- `To: ${this.sanitizeHeader(to)}`,
408
+ `To: ${this.sanitizeHeader(recipients)}`,
409
409
  `Subject: ${this.sanitizeHeader(options.subject)}`,
410
410
  `Message-ID: ${messageId}`,
411
411
  `Date: ${date}`,
412
- "MIME-Version: 1.0",
413
- "Content-Transfer-Encoding: quoted-printable"
412
+ "MIME-Version: 1.0"
414
413
  ];
415
414
  if (options.cc) {
416
415
  const cc = Array.isArray(options.cc) ? options.cc.join(", ") : options.cc;
417
416
  headers.push(`Cc: ${this.sanitizeHeader(cc)}`);
418
417
  }
419
- if (options.replyTo) {
418
+ if (options.replyTo)
420
419
  headers.push(`Reply-To: ${this.sanitizeHeader(options.replyTo)}`);
421
- }
422
420
  if (options.headers) {
423
421
  for (const [name, value] of Object.entries(options.headers)) {
424
422
  if (!/^[a-zA-Z0-9-]+$/.test(name)) continue;
@@ -443,13 +441,11 @@ var SMTPClient = class {
443
441
  \r
444
442
  --${boundary}\r
445
443
  Content-Type: text/plain; charset=utf-8\r
446
- Content-Transfer-Encoding: quoted-printable\r
447
444
  \r
448
445
  ${text || ""}\r
449
446
  \r
450
447
  --${boundary}\r
451
448
  Content-Type: text/html; charset=utf-8\r
452
- Content-Transfer-Encoding: quoted-printable\r
453
449
  \r
454
450
  ${html || ""}\r
455
451
  \r
@@ -502,12 +498,21 @@ ${text || ""}`;
502
498
  error: "Missing required email parameters (from, to, subject, and either text or html)"
503
499
  };
504
500
  }
505
- if (!validateEmail(from) || !validateEmail(to)) {
501
+ if (!validateEmail(from)) {
506
502
  return {
507
503
  success: false,
508
504
  error: "Invalid email address format"
509
505
  };
510
506
  }
507
+ const recipients = Array.isArray(options.to) ? options.to : [options.to];
508
+ for (const recipient of recipients) {
509
+ if (!validateEmail(recipient)) {
510
+ return {
511
+ success: false,
512
+ error: `Invalid recipient email address format: ${recipient}`
513
+ };
514
+ }
515
+ }
511
516
  for (this.retryCount = 0; this.retryCount <= this.config.maxRetries; this.retryCount++) {
512
517
  try {
513
518
  if (this.retryCount > 0) {
@@ -523,21 +528,20 @@ ${text || ""}`;
523
528
  await this.smtpHandshake();
524
529
  }
525
530
  await this.sendCommand(`MAIL FROM:<${from}>`, 250);
526
- await this.sendCommand(`RCPT TO:<${to}>`, 250);
531
+ for (const recipient of recipients)
532
+ await this.sendCommand(`RCPT TO:<${recipient}>`, 250);
527
533
  if (options.cc) {
528
534
  const ccList = Array.isArray(options.cc) ? options.cc : [options.cc];
529
535
  for (const cc of ccList) {
530
- if (validateEmail(cc)) {
536
+ if (validateEmail(cc))
531
537
  await this.sendCommand(`RCPT TO:<${cc}>`, 250);
532
- }
533
538
  }
534
539
  }
535
540
  if (options.bcc) {
536
541
  const bccList = Array.isArray(options.bcc) ? options.bcc : [options.bcc];
537
542
  for (const bcc of bccList) {
538
- if (validateEmail(bcc)) {
543
+ if (validateEmail(bcc))
539
544
  await this.sendCommand(`RCPT TO:<${bcc}>`, 250);
540
- }
541
545
  }
542
546
  }
543
547
  await this.sendCommand("DATA", 354);
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  SMTPClient
3
- } from "./chunk-NGYJ2CZH.mjs";
3
+ } from "./chunk-NGT3KX7A.mjs";
4
4
  import "./chunk-UDLJWUFN.mjs";
5
5
  export {
6
6
  SMTPClient
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-YVXB6HCK.mjs";
4
4
  import {
5
5
  SMTPClient
6
- } from "./chunk-NGYJ2CZH.mjs";
6
+ } from "./chunk-NGT3KX7A.mjs";
7
7
  import {
8
8
  verifyEmailDomain
9
9
  } from "./chunk-UDLJWUFN.mjs";
@@ -21,9 +21,14 @@ var MikroMail = class {
21
21
  */
22
22
  async send(emailOptions) {
23
23
  try {
24
- const hasMXRecords = await verifyEmailDomain(emailOptions.to);
25
- if (!hasMXRecords)
26
- console.error("Warning: No MX records found for recipient domain");
24
+ const recipients = Array.isArray(emailOptions.to) ? emailOptions.to : [emailOptions.to];
25
+ for (const recipient of recipients) {
26
+ const hasMXRecords = await verifyEmailDomain(recipient);
27
+ if (!hasMXRecords)
28
+ console.error(
29
+ `Warning: No MX records found for recipient domain: ${recipient}`
30
+ );
31
+ }
27
32
  const result = await this.smtpClient.sendEmail(emailOptions);
28
33
  if (result.success) console.log(`Message ID: ${result.messageId}`);
29
34
  else console.error(`Failed to send email: ${result.error}`);
@@ -339,23 +339,21 @@ var SMTPClient = class {
339
339
  const messageId = this.generateMessageId();
340
340
  const date = (/* @__PURE__ */ new Date()).toUTCString();
341
341
  const from = options.from || this.config.user;
342
- const { to } = options;
342
+ const recipients = Array.isArray(options.to) ? options.to.join(", ") : options.to;
343
343
  const headers = [
344
344
  `From: ${this.sanitizeHeader(from)}`,
345
- `To: ${this.sanitizeHeader(to)}`,
345
+ `To: ${this.sanitizeHeader(recipients)}`,
346
346
  `Subject: ${this.sanitizeHeader(options.subject)}`,
347
347
  `Message-ID: ${messageId}`,
348
348
  `Date: ${date}`,
349
- "MIME-Version: 1.0",
350
- "Content-Transfer-Encoding: quoted-printable"
349
+ "MIME-Version: 1.0"
351
350
  ];
352
351
  if (options.cc) {
353
352
  const cc = Array.isArray(options.cc) ? options.cc.join(", ") : options.cc;
354
353
  headers.push(`Cc: ${this.sanitizeHeader(cc)}`);
355
354
  }
356
- if (options.replyTo) {
355
+ if (options.replyTo)
357
356
  headers.push(`Reply-To: ${this.sanitizeHeader(options.replyTo)}`);
358
- }
359
357
  if (options.headers) {
360
358
  for (const [name, value] of Object.entries(options.headers)) {
361
359
  if (!/^[a-zA-Z0-9-]+$/.test(name)) continue;
@@ -380,13 +378,11 @@ var SMTPClient = class {
380
378
  \r
381
379
  --${boundary}\r
382
380
  Content-Type: text/plain; charset=utf-8\r
383
- Content-Transfer-Encoding: quoted-printable\r
384
381
  \r
385
382
  ${text || ""}\r
386
383
  \r
387
384
  --${boundary}\r
388
385
  Content-Type: text/html; charset=utf-8\r
389
- Content-Transfer-Encoding: quoted-printable\r
390
386
  \r
391
387
  ${html || ""}\r
392
388
  \r
@@ -439,12 +435,21 @@ ${text || ""}`;
439
435
  error: "Missing required email parameters (from, to, subject, and either text or html)"
440
436
  };
441
437
  }
442
- if (!validateEmail(from) || !validateEmail(to)) {
438
+ if (!validateEmail(from)) {
443
439
  return {
444
440
  success: false,
445
441
  error: "Invalid email address format"
446
442
  };
447
443
  }
444
+ const recipients = Array.isArray(options.to) ? options.to : [options.to];
445
+ for (const recipient of recipients) {
446
+ if (!validateEmail(recipient)) {
447
+ return {
448
+ success: false,
449
+ error: `Invalid recipient email address format: ${recipient}`
450
+ };
451
+ }
452
+ }
448
453
  for (this.retryCount = 0; this.retryCount <= this.config.maxRetries; this.retryCount++) {
449
454
  try {
450
455
  if (this.retryCount > 0) {
@@ -460,21 +465,20 @@ ${text || ""}`;
460
465
  await this.smtpHandshake();
461
466
  }
462
467
  await this.sendCommand(`MAIL FROM:<${from}>`, 250);
463
- await this.sendCommand(`RCPT TO:<${to}>`, 250);
468
+ for (const recipient of recipients)
469
+ await this.sendCommand(`RCPT TO:<${recipient}>`, 250);
464
470
  if (options.cc) {
465
471
  const ccList = Array.isArray(options.cc) ? options.cc : [options.cc];
466
472
  for (const cc of ccList) {
467
- if (validateEmail(cc)) {
473
+ if (validateEmail(cc))
468
474
  await this.sendCommand(`RCPT TO:<${cc}>`, 250);
469
- }
470
475
  }
471
476
  }
472
477
  if (options.bcc) {
473
478
  const bccList = Array.isArray(options.bcc) ? options.bcc : [options.bcc];
474
479
  for (const bcc of bccList) {
475
- if (validateEmail(bcc)) {
480
+ if (validateEmail(bcc))
476
481
  await this.sendCommand(`RCPT TO:<${bcc}>`, 250);
477
- }
478
482
  }
479
483
  }
480
484
  await this.sendCommand("DATA", 354);
package/lib/index.js CHANGED
@@ -546,23 +546,21 @@ var SMTPClient = class {
546
546
  const messageId = this.generateMessageId();
547
547
  const date = (/* @__PURE__ */ new Date()).toUTCString();
548
548
  const from = options.from || this.config.user;
549
- const { to } = options;
549
+ const recipients = Array.isArray(options.to) ? options.to.join(", ") : options.to;
550
550
  const headers = [
551
551
  `From: ${this.sanitizeHeader(from)}`,
552
- `To: ${this.sanitizeHeader(to)}`,
552
+ `To: ${this.sanitizeHeader(recipients)}`,
553
553
  `Subject: ${this.sanitizeHeader(options.subject)}`,
554
554
  `Message-ID: ${messageId}`,
555
555
  `Date: ${date}`,
556
- "MIME-Version: 1.0",
557
- "Content-Transfer-Encoding: quoted-printable"
556
+ "MIME-Version: 1.0"
558
557
  ];
559
558
  if (options.cc) {
560
559
  const cc = Array.isArray(options.cc) ? options.cc.join(", ") : options.cc;
561
560
  headers.push(`Cc: ${this.sanitizeHeader(cc)}`);
562
561
  }
563
- if (options.replyTo) {
562
+ if (options.replyTo)
564
563
  headers.push(`Reply-To: ${this.sanitizeHeader(options.replyTo)}`);
565
- }
566
564
  if (options.headers) {
567
565
  for (const [name, value] of Object.entries(options.headers)) {
568
566
  if (!/^[a-zA-Z0-9-]+$/.test(name)) continue;
@@ -587,13 +585,11 @@ var SMTPClient = class {
587
585
  \r
588
586
  --${boundary}\r
589
587
  Content-Type: text/plain; charset=utf-8\r
590
- Content-Transfer-Encoding: quoted-printable\r
591
588
  \r
592
589
  ${text || ""}\r
593
590
  \r
594
591
  --${boundary}\r
595
592
  Content-Type: text/html; charset=utf-8\r
596
- Content-Transfer-Encoding: quoted-printable\r
597
593
  \r
598
594
  ${html || ""}\r
599
595
  \r
@@ -646,12 +642,21 @@ ${text || ""}`;
646
642
  error: "Missing required email parameters (from, to, subject, and either text or html)"
647
643
  };
648
644
  }
649
- if (!validateEmail(from) || !validateEmail(to)) {
645
+ if (!validateEmail(from)) {
650
646
  return {
651
647
  success: false,
652
648
  error: "Invalid email address format"
653
649
  };
654
650
  }
651
+ const recipients = Array.isArray(options.to) ? options.to : [options.to];
652
+ for (const recipient of recipients) {
653
+ if (!validateEmail(recipient)) {
654
+ return {
655
+ success: false,
656
+ error: `Invalid recipient email address format: ${recipient}`
657
+ };
658
+ }
659
+ }
655
660
  for (this.retryCount = 0; this.retryCount <= this.config.maxRetries; this.retryCount++) {
656
661
  try {
657
662
  if (this.retryCount > 0) {
@@ -667,21 +672,20 @@ ${text || ""}`;
667
672
  await this.smtpHandshake();
668
673
  }
669
674
  await this.sendCommand(`MAIL FROM:<${from}>`, 250);
670
- await this.sendCommand(`RCPT TO:<${to}>`, 250);
675
+ for (const recipient of recipients)
676
+ await this.sendCommand(`RCPT TO:<${recipient}>`, 250);
671
677
  if (options.cc) {
672
678
  const ccList = Array.isArray(options.cc) ? options.cc : [options.cc];
673
679
  for (const cc of ccList) {
674
- if (validateEmail(cc)) {
680
+ if (validateEmail(cc))
675
681
  await this.sendCommand(`RCPT TO:<${cc}>`, 250);
676
- }
677
682
  }
678
683
  }
679
684
  if (options.bcc) {
680
685
  const bccList = Array.isArray(options.bcc) ? options.bcc : [options.bcc];
681
686
  for (const bcc of bccList) {
682
- if (validateEmail(bcc)) {
687
+ if (validateEmail(bcc))
683
688
  await this.sendCommand(`RCPT TO:<${bcc}>`, 250);
684
- }
685
689
  }
686
690
  }
687
691
  await this.sendCommand("DATA", 354);
@@ -760,9 +764,14 @@ var MikroMail = class {
760
764
  */
761
765
  async send(emailOptions) {
762
766
  try {
763
- const hasMXRecords = await verifyEmailDomain(emailOptions.to);
764
- if (!hasMXRecords)
765
- console.error("Warning: No MX records found for recipient domain");
767
+ const recipients = Array.isArray(emailOptions.to) ? emailOptions.to : [emailOptions.to];
768
+ for (const recipient of recipients) {
769
+ const hasMXRecords = await verifyEmailDomain(recipient);
770
+ if (!hasMXRecords)
771
+ console.error(
772
+ `Warning: No MX records found for recipient domain: ${recipient}`
773
+ );
774
+ }
766
775
  const result = await this.smtpClient.sendEmail(emailOptions);
767
776
  if (result.success) console.log(`Message ID: ${result.messageId}`);
768
777
  else console.error(`Failed to send email: ${result.error}`);
package/lib/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import {
2
2
  MikroMail
3
- } from "./chunk-JNBOBHOL.mjs";
3
+ } from "./chunk-422R3NOG.mjs";
4
4
  import "./chunk-YVXB6HCK.mjs";
5
- import "./chunk-NGYJ2CZH.mjs";
5
+ import "./chunk-NGT3KX7A.mjs";
6
6
  import "./chunk-47VXJTWV.mjs";
7
7
  import "./chunk-UDLJWUFN.mjs";
8
8
  export {
@@ -19,7 +19,7 @@ interface SMTPConfiguration {
19
19
  */
20
20
  interface EmailOptions {
21
21
  from?: string;
22
- to: string;
22
+ to: string | string[];
23
23
  cc?: string | string[];
24
24
  bcc?: string | string[];
25
25
  replyTo?: string;
@@ -19,7 +19,7 @@ interface SMTPConfiguration {
19
19
  */
20
20
  interface EmailOptions {
21
21
  from?: string;
22
- to: string;
22
+ to: string | string[];
23
23
  cc?: string | string[];
24
24
  bcc?: string | string[];
25
25
  replyTo?: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mikromail",
3
3
  "description": "Lightweight replacement for Nodemailer, supporting HTML, international symbols, and more.",
4
- "version": "0.0.5",
4
+ "version": "0.0.7",
5
5
  "author": "Mikael Vesavuori",
6
6
  "license": "MIT",
7
7
  "keywords": [
@@ -41,7 +41,8 @@
41
41
  "clean": "rm -rf lib && rm -rf dist",
42
42
  "lint": "npx @biomejs/biome check --write ./src ./tests",
43
43
  "package": "npm pack",
44
- "prepublishOnly": "npm run build"
44
+ "prepublishOnly": "npm run build",
45
+ "prepare": "husky"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@biomejs/biome": "1",