@fmontoya/aws-ses-adapter 1.1.0 → 1.2.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.
package/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 Fabian Montoya
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Fabian Montoya
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -76,8 +76,10 @@ import { init } from '@fmontoya/aws-ses-adapter';
76
76
 
77
77
  init({
78
78
  region: 'us-east-1',
79
- accessKeyId: process.env.AWS_ACCESS_KEY_ID,
80
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
79
+ credentials: {
80
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
81
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
82
+ },
81
83
  defaultFrom: 'noreply@example.com',
82
84
  });
83
85
  ```
@@ -103,12 +105,14 @@ console.log('Sent! Message ID:', result.messageId);
103
105
 
104
106
  ### Via `init()` options
105
107
 
106
- | Option | Type | Required | Description |
107
- | ----------------- | -------- | -------- | ------------------------------------------------------------- |
108
- | `region` | `string` | Yes\* | AWS region where SES is configured (e.g. `us-east-1`). |
109
- | `accessKeyId` | `string` | Yes\* | AWS access key ID. |
110
- | `secretAccessKey` | `string` | Yes\* | AWS secret access key. |
111
- | `defaultFrom` | `string` | No | Default "From" address used when `from` is omitted per-email. |
108
+ | Option | Type | Required | Description |
109
+ | -------------------------------- | -------- | -------- | ------------------------------------------------------------- |
110
+ | `region` | `string` | Yes\* | AWS region where SES is configured (e.g. `us-east-1`). |
111
+ | `credentials.accessKeyId` | `string` | Yes\* | AWS access key ID. |
112
+ | `credentials.secretAccessKey` | `string` | Yes\* | AWS secret access key. |
113
+ | `defaultFrom` | `string` | No | Default "From" address used when `from` is omitted per-email. |
114
+ | `accessKeyId` _(deprecated)_ | `string` | — | Use `credentials.accessKeyId` instead. |
115
+ | `secretAccessKey` _(deprecated)_ | `string` | — | Use `credentials.secretAccessKey` instead. |
112
116
 
113
117
  \* Required unless the corresponding environment variable is set.
114
118
 
@@ -143,8 +147,10 @@ import { init } from '@fmontoya/aws-ses-adapter';
143
147
 
144
148
  init({
145
149
  region: 'us-east-1',
146
- accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
147
- secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
150
+ credentials: {
151
+ accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
152
+ secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
153
+ },
148
154
  defaultFrom: 'noreply@example.com',
149
155
  });
150
156
  ```
@@ -283,8 +289,10 @@ import { SesAdapter } from '@fmontoya/aws-ses-adapter';
283
289
 
284
290
  const adapter = new SesAdapter({
285
291
  region: 'eu-west-1',
286
- accessKeyId: process.env.AWS_ACCESS_KEY_ID,
287
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
292
+ credentials: {
293
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
294
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
295
+ },
288
296
  defaultFrom: 'noreply@myapp.com',
289
297
  });
290
298
 
@@ -399,8 +407,10 @@ import { EmailService } from './email.service';
399
407
  const { SesAdapter } = require('@fmontoya/aws-ses-adapter');
400
408
  return new SesAdapter({
401
409
  region: process.env.AWS_SES_REGION,
402
- accessKeyId: process.env.AWS_ACCESS_KEY_ID,
403
- secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
410
+ credentials: {
411
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID,
412
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
413
+ },
404
414
  defaultFrom: process.env.AWS_SES_FROM_EMAIL,
405
415
  });
406
416
  },
package/dist/index.d.mts CHANGED
@@ -9,10 +9,13 @@
9
9
  *
10
10
  * @example
11
11
  * ```ts
12
+ * // Recommended: use the credentials object
12
13
  * const config: SesAdapterConfig = {
13
14
  * region: 'us-east-1',
14
- * accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
15
- * secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
15
+ * credentials: {
16
+ * accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
17
+ * secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
18
+ * },
16
19
  * defaultFrom: 'noreply@example.com',
17
20
  * };
18
21
  * ```
@@ -23,14 +26,36 @@ interface SesAdapterConfig {
23
26
  * Falls back to the `AWS_SES_REGION` environment variable if not provided.
24
27
  */
25
28
  region?: string;
29
+ /**
30
+ * AWS credentials for authentication.
31
+ * Takes precedence over the deprecated top-level `accessKeyId` and `secretAccessKey` fields.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * credentials: {
36
+ * accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
37
+ * secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
38
+ * }
39
+ * ```
40
+ */
41
+ credentials?: {
42
+ /** AWS access key ID. Required when `credentials` is provided. */
43
+ accessKeyId: string;
44
+ /** AWS secret access key. Required when `credentials` is provided. */
45
+ secretAccessKey: string;
46
+ };
26
47
  /**
27
48
  * AWS access key ID for authentication.
28
49
  * Falls back to the `AWS_ACCESS_KEY_ID` environment variable if not provided.
50
+ *
51
+ * @deprecated Use `credentials.accessKeyId` instead.
29
52
  */
30
53
  accessKeyId?: string;
31
54
  /**
32
55
  * AWS secret access key for authentication.
33
56
  * Falls back to the `AWS_SECRET_ACCESS_KEY` environment variable if not provided.
57
+ *
58
+ * @deprecated Use `credentials.secretAccessKey` instead.
34
59
  */
35
60
  secretAccessKey?: string;
36
61
  /**
@@ -359,7 +384,7 @@ declare class SesAdapter {
359
384
  *
360
385
  * @example
361
386
  * ```ts
362
- * import { sendEmail } from 'aws-ses-adapter';
387
+ * import { sendEmail } from '@fmontoya/aws-ses-adapter';
363
388
  *
364
389
  * // Calling sendEmail before init() throws this error
365
390
  * try {
@@ -383,7 +408,7 @@ declare class SesNotInitializedError extends Error {
383
408
  *
384
409
  * @example
385
410
  * ```ts
386
- * import { init } from 'aws-ses-adapter';
411
+ * import { init } from '@fmontoya/aws-ses-adapter';
387
412
  *
388
413
  * try {
389
414
  * init({}); // No region, no env vars set
@@ -408,7 +433,7 @@ declare class SesConfigError extends Error {
408
433
  *
409
434
  * @example
410
435
  * ```ts
411
- * import { sendEmail, SesSendError } from 'aws-ses-adapter';
436
+ * import { sendEmail, SesSendError } from '@fmontoya/aws-ses-adapter';
412
437
  *
413
438
  * try {
414
439
  * await sendEmail({ to: 'user@example.com', subject: 'Hi', text: 'Hello' });
@@ -435,7 +460,7 @@ declare class SesSendError extends Error {
435
460
  *
436
461
  * @example
437
462
  * ```ts
438
- * import { sendEmail, SesValidationError } from 'aws-ses-adapter';
463
+ * import { sendEmail, SesValidationError } from '@fmontoya/aws-ses-adapter';
439
464
  *
440
465
  * try {
441
466
  * // Missing both html and text
@@ -465,19 +490,21 @@ declare class SesValidationError extends Error {
465
490
  *
466
491
  * **1. Initialize once** (at application startup):
467
492
  * ```ts
468
- * import { init } from 'aws-ses-adapter';
493
+ * import { init } from '@fmontoya/aws-ses-adapter';
469
494
  *
470
495
  * init({
471
496
  * region: 'us-east-1',
472
- * accessKeyId: 'YOUR_KEY',
473
- * secretAccessKey: 'YOUR_SECRET',
497
+ * credentials: {
498
+ * accessKeyId: 'YOUR_KEY',
499
+ * secretAccessKey: 'YOUR_SECRET',
500
+ * },
474
501
  * defaultFrom: 'noreply@example.com',
475
502
  * });
476
503
  * ```
477
504
  *
478
505
  * **2. Use anywhere** in your application:
479
506
  * ```ts
480
- * import { sendEmail } from 'aws-ses-adapter';
507
+ * import { sendEmail } from '@fmontoya/aws-ses-adapter';
481
508
  *
482
509
  * await sendEmail({
483
510
  * to: 'user@example.com',
@@ -503,8 +530,9 @@ declare class SesValidationError extends Error {
503
530
  * which is useful for reconfiguration during testing.
504
531
  *
505
532
  * Credentials are resolved in the following order:
506
- * 1. Values provided in the `config` argument.
507
- * 2. Environment variables (`AWS_SES_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`).
533
+ * 1. `config.credentials.accessKeyId` / `config.credentials.secretAccessKey`
534
+ * 2. `config.accessKeyId` / `config.secretAccessKey` _(deprecated)_
535
+ * 3. Environment variables (`AWS_SES_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)
508
536
  *
509
537
  * @param config - Optional configuration. Omit any field to fall back to environment variables.
510
538
  * @throws {SesConfigError} When required credentials cannot be resolved.
@@ -514,8 +542,10 @@ declare class SesValidationError extends Error {
514
542
  * // Using explicit credentials
515
543
  * init({
516
544
  * region: 'us-east-1',
517
- * accessKeyId: process.env.MY_KEY_ID,
518
- * secretAccessKey: process.env.MY_SECRET,
545
+ * credentials: {
546
+ * accessKeyId: process.env.MY_KEY_ID,
547
+ * secretAccessKey: process.env.MY_SECRET,
548
+ * },
519
549
  * defaultFrom: 'no-reply@myapp.com',
520
550
  * });
521
551
  *
@@ -538,7 +568,7 @@ declare function init(config?: SesAdapterConfig): void;
538
568
  *
539
569
  * @example
540
570
  * ```ts
541
- * import { sendEmail } from 'aws-ses-adapter';
571
+ * import { sendEmail } from '@fmontoya/aws-ses-adapter';
542
572
  *
543
573
  * const result = await sendEmail({
544
574
  * to: 'alice@example.com',
@@ -569,7 +599,7 @@ declare function sendEmail(options: SendEmailOptions): Promise<SendEmailResult>;
569
599
  *
570
600
  * @example
571
601
  * ```ts
572
- * import { sendEmailWithAttachments } from 'aws-ses-adapter';
602
+ * import { sendEmailWithAttachments } from '@fmontoya/aws-ses-adapter';
573
603
  * import { readFileSync } from 'fs';
574
604
  *
575
605
  * const result = await sendEmailWithAttachments({
@@ -603,7 +633,7 @@ declare function sendEmailWithAttachments(options: SendEmailWithAttachmentsOptio
603
633
  *
604
634
  * @example
605
635
  * ```ts
606
- * import { sendRawEmail } from 'aws-ses-adapter';
636
+ * import { sendRawEmail } from '@fmontoya/aws-ses-adapter';
607
637
  *
608
638
  * const mime = [
609
639
  * 'From: sender@example.com',
@@ -626,7 +656,7 @@ declare function sendRawEmail(rawMessage: string): Promise<SendEmailResult>;
626
656
  *
627
657
  * @example
628
658
  * ```ts
629
- * import { isInitialized } from 'aws-ses-adapter';
659
+ * import { isInitialized } from '@fmontoya/aws-ses-adapter';
630
660
  *
631
661
  * if (!isInitialized()) {
632
662
  * init();
@@ -642,7 +672,7 @@ declare function isInitialized(): boolean;
642
672
  *
643
673
  * @example
644
674
  * ```ts
645
- * import { hasDefaultFrom } from 'aws-ses-adapter';
675
+ * import { hasDefaultFrom } from '@fmontoya/aws-ses-adapter';
646
676
  *
647
677
  * console.log(hasDefaultFrom()); // => true
648
678
  * ```
@@ -657,7 +687,7 @@ declare function hasDefaultFrom(): boolean;
657
687
  *
658
688
  * @example
659
689
  * ```ts
660
- * import { getDefaultFrom } from 'aws-ses-adapter';
690
+ * import { getDefaultFrom } from '@fmontoya/aws-ses-adapter';
661
691
  *
662
692
  * const from = getDefaultFrom();
663
693
  * // => 'noreply@example.com' or undefined
@@ -672,7 +702,7 @@ declare function getDefaultFrom(): string | undefined;
672
702
  *
673
703
  * @example
674
704
  * ```ts
675
- * import { getRegion } from 'aws-ses-adapter';
705
+ * import { getRegion } from '@fmontoya/aws-ses-adapter';
676
706
  *
677
707
  * console.log(getRegion()); // => 'us-east-1'
678
708
  * ```
package/dist/index.d.ts CHANGED
@@ -9,10 +9,13 @@
9
9
  *
10
10
  * @example
11
11
  * ```ts
12
+ * // Recommended: use the credentials object
12
13
  * const config: SesAdapterConfig = {
13
14
  * region: 'us-east-1',
14
- * accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
15
- * secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
15
+ * credentials: {
16
+ * accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
17
+ * secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
18
+ * },
16
19
  * defaultFrom: 'noreply@example.com',
17
20
  * };
18
21
  * ```
@@ -23,14 +26,36 @@ interface SesAdapterConfig {
23
26
  * Falls back to the `AWS_SES_REGION` environment variable if not provided.
24
27
  */
25
28
  region?: string;
29
+ /**
30
+ * AWS credentials for authentication.
31
+ * Takes precedence over the deprecated top-level `accessKeyId` and `secretAccessKey` fields.
32
+ *
33
+ * @example
34
+ * ```ts
35
+ * credentials: {
36
+ * accessKeyId: 'AKIAIOSFODNN7EXAMPLE',
37
+ * secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',
38
+ * }
39
+ * ```
40
+ */
41
+ credentials?: {
42
+ /** AWS access key ID. Required when `credentials` is provided. */
43
+ accessKeyId: string;
44
+ /** AWS secret access key. Required when `credentials` is provided. */
45
+ secretAccessKey: string;
46
+ };
26
47
  /**
27
48
  * AWS access key ID for authentication.
28
49
  * Falls back to the `AWS_ACCESS_KEY_ID` environment variable if not provided.
50
+ *
51
+ * @deprecated Use `credentials.accessKeyId` instead.
29
52
  */
30
53
  accessKeyId?: string;
31
54
  /**
32
55
  * AWS secret access key for authentication.
33
56
  * Falls back to the `AWS_SECRET_ACCESS_KEY` environment variable if not provided.
57
+ *
58
+ * @deprecated Use `credentials.secretAccessKey` instead.
34
59
  */
35
60
  secretAccessKey?: string;
36
61
  /**
@@ -359,7 +384,7 @@ declare class SesAdapter {
359
384
  *
360
385
  * @example
361
386
  * ```ts
362
- * import { sendEmail } from 'aws-ses-adapter';
387
+ * import { sendEmail } from '@fmontoya/aws-ses-adapter';
363
388
  *
364
389
  * // Calling sendEmail before init() throws this error
365
390
  * try {
@@ -383,7 +408,7 @@ declare class SesNotInitializedError extends Error {
383
408
  *
384
409
  * @example
385
410
  * ```ts
386
- * import { init } from 'aws-ses-adapter';
411
+ * import { init } from '@fmontoya/aws-ses-adapter';
387
412
  *
388
413
  * try {
389
414
  * init({}); // No region, no env vars set
@@ -408,7 +433,7 @@ declare class SesConfigError extends Error {
408
433
  *
409
434
  * @example
410
435
  * ```ts
411
- * import { sendEmail, SesSendError } from 'aws-ses-adapter';
436
+ * import { sendEmail, SesSendError } from '@fmontoya/aws-ses-adapter';
412
437
  *
413
438
  * try {
414
439
  * await sendEmail({ to: 'user@example.com', subject: 'Hi', text: 'Hello' });
@@ -435,7 +460,7 @@ declare class SesSendError extends Error {
435
460
  *
436
461
  * @example
437
462
  * ```ts
438
- * import { sendEmail, SesValidationError } from 'aws-ses-adapter';
463
+ * import { sendEmail, SesValidationError } from '@fmontoya/aws-ses-adapter';
439
464
  *
440
465
  * try {
441
466
  * // Missing both html and text
@@ -465,19 +490,21 @@ declare class SesValidationError extends Error {
465
490
  *
466
491
  * **1. Initialize once** (at application startup):
467
492
  * ```ts
468
- * import { init } from 'aws-ses-adapter';
493
+ * import { init } from '@fmontoya/aws-ses-adapter';
469
494
  *
470
495
  * init({
471
496
  * region: 'us-east-1',
472
- * accessKeyId: 'YOUR_KEY',
473
- * secretAccessKey: 'YOUR_SECRET',
497
+ * credentials: {
498
+ * accessKeyId: 'YOUR_KEY',
499
+ * secretAccessKey: 'YOUR_SECRET',
500
+ * },
474
501
  * defaultFrom: 'noreply@example.com',
475
502
  * });
476
503
  * ```
477
504
  *
478
505
  * **2. Use anywhere** in your application:
479
506
  * ```ts
480
- * import { sendEmail } from 'aws-ses-adapter';
507
+ * import { sendEmail } from '@fmontoya/aws-ses-adapter';
481
508
  *
482
509
  * await sendEmail({
483
510
  * to: 'user@example.com',
@@ -503,8 +530,9 @@ declare class SesValidationError extends Error {
503
530
  * which is useful for reconfiguration during testing.
504
531
  *
505
532
  * Credentials are resolved in the following order:
506
- * 1. Values provided in the `config` argument.
507
- * 2. Environment variables (`AWS_SES_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`).
533
+ * 1. `config.credentials.accessKeyId` / `config.credentials.secretAccessKey`
534
+ * 2. `config.accessKeyId` / `config.secretAccessKey` _(deprecated)_
535
+ * 3. Environment variables (`AWS_SES_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)
508
536
  *
509
537
  * @param config - Optional configuration. Omit any field to fall back to environment variables.
510
538
  * @throws {SesConfigError} When required credentials cannot be resolved.
@@ -514,8 +542,10 @@ declare class SesValidationError extends Error {
514
542
  * // Using explicit credentials
515
543
  * init({
516
544
  * region: 'us-east-1',
517
- * accessKeyId: process.env.MY_KEY_ID,
518
- * secretAccessKey: process.env.MY_SECRET,
545
+ * credentials: {
546
+ * accessKeyId: process.env.MY_KEY_ID,
547
+ * secretAccessKey: process.env.MY_SECRET,
548
+ * },
519
549
  * defaultFrom: 'no-reply@myapp.com',
520
550
  * });
521
551
  *
@@ -538,7 +568,7 @@ declare function init(config?: SesAdapterConfig): void;
538
568
  *
539
569
  * @example
540
570
  * ```ts
541
- * import { sendEmail } from 'aws-ses-adapter';
571
+ * import { sendEmail } from '@fmontoya/aws-ses-adapter';
542
572
  *
543
573
  * const result = await sendEmail({
544
574
  * to: 'alice@example.com',
@@ -569,7 +599,7 @@ declare function sendEmail(options: SendEmailOptions): Promise<SendEmailResult>;
569
599
  *
570
600
  * @example
571
601
  * ```ts
572
- * import { sendEmailWithAttachments } from 'aws-ses-adapter';
602
+ * import { sendEmailWithAttachments } from '@fmontoya/aws-ses-adapter';
573
603
  * import { readFileSync } from 'fs';
574
604
  *
575
605
  * const result = await sendEmailWithAttachments({
@@ -603,7 +633,7 @@ declare function sendEmailWithAttachments(options: SendEmailWithAttachmentsOptio
603
633
  *
604
634
  * @example
605
635
  * ```ts
606
- * import { sendRawEmail } from 'aws-ses-adapter';
636
+ * import { sendRawEmail } from '@fmontoya/aws-ses-adapter';
607
637
  *
608
638
  * const mime = [
609
639
  * 'From: sender@example.com',
@@ -626,7 +656,7 @@ declare function sendRawEmail(rawMessage: string): Promise<SendEmailResult>;
626
656
  *
627
657
  * @example
628
658
  * ```ts
629
- * import { isInitialized } from 'aws-ses-adapter';
659
+ * import { isInitialized } from '@fmontoya/aws-ses-adapter';
630
660
  *
631
661
  * if (!isInitialized()) {
632
662
  * init();
@@ -642,7 +672,7 @@ declare function isInitialized(): boolean;
642
672
  *
643
673
  * @example
644
674
  * ```ts
645
- * import { hasDefaultFrom } from 'aws-ses-adapter';
675
+ * import { hasDefaultFrom } from '@fmontoya/aws-ses-adapter';
646
676
  *
647
677
  * console.log(hasDefaultFrom()); // => true
648
678
  * ```
@@ -657,7 +687,7 @@ declare function hasDefaultFrom(): boolean;
657
687
  *
658
688
  * @example
659
689
  * ```ts
660
- * import { getDefaultFrom } from 'aws-ses-adapter';
690
+ * import { getDefaultFrom } from '@fmontoya/aws-ses-adapter';
661
691
  *
662
692
  * const from = getDefaultFrom();
663
693
  * // => 'noreply@example.com' or undefined
@@ -672,7 +702,7 @@ declare function getDefaultFrom(): string | undefined;
672
702
  *
673
703
  * @example
674
704
  * ```ts
675
- * import { getRegion } from 'aws-ses-adapter';
705
+ * import { getRegion } from '@fmontoya/aws-ses-adapter';
676
706
  *
677
707
  * console.log(getRegion()); // => 'us-east-1'
678
708
  * ```
package/dist/index.js CHANGED
@@ -58,8 +58,8 @@ var SesValidationError = class extends Error {
58
58
  // src/adapter.ts
59
59
  function resolveConfig(config = {}) {
60
60
  const region = config.region ?? process.env["AWS_SES_REGION"];
61
- const accessKeyId = config.accessKeyId ?? process.env["AWS_ACCESS_KEY_ID"];
62
- const secretAccessKey = config.secretAccessKey ?? process.env["AWS_SECRET_ACCESS_KEY"];
61
+ const accessKeyId = config.credentials?.accessKeyId ?? config.accessKeyId ?? process.env["AWS_ACCESS_KEY_ID"];
62
+ const secretAccessKey = config.credentials?.secretAccessKey ?? config.secretAccessKey ?? process.env["AWS_SECRET_ACCESS_KEY"];
63
63
  const defaultFrom = config.defaultFrom ?? process.env["AWS_SES_FROM_EMAIL"];
64
64
  if (!region) {
65
65
  throw new SesConfigError(
@@ -68,12 +68,12 @@ function resolveConfig(config = {}) {
68
68
  }
69
69
  if (!accessKeyId) {
70
70
  throw new SesConfigError(
71
- "AWS access key ID is required. Provide it via config.accessKeyId or the AWS_ACCESS_KEY_ID environment variable."
71
+ "AWS access key ID is required. Provide it via config.credentials.accessKeyId, or the AWS_ACCESS_KEY_ID environment variable."
72
72
  );
73
73
  }
74
74
  if (!secretAccessKey) {
75
75
  throw new SesConfigError(
76
- "AWS secret access key is required. Provide it via config.secretAccessKey or the AWS_SECRET_ACCESS_KEY environment variable."
76
+ "AWS secret access key is required. Provide it via config.credentials.secretAccessKey, or the AWS_SECRET_ACCESS_KEY environment variable."
77
77
  );
78
78
  }
79
79
  return { region, accessKeyId, secretAccessKey, defaultFrom };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/adapter.ts","../src/index.ts"],"names":["SESClient","SendRawEmailCommand","SendEmailCommand"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module client\n * Factory and utilities for building an AWS SESClient instance\n * from a resolved adapter configuration.\n */\n\nimport { SESClient } from '@aws-sdk/client-ses';\nimport type { ResolvedSesAdapterConfig } from './types';\n\n/**\n * Creates and returns a configured {@link SESClient} instance using the\n * resolved adapter configuration.\n *\n * @param config - The resolved configuration containing region and credentials.\n * @returns A ready-to-use AWS SESClient.\n *\n * @example\n * ```ts\n * const client = buildSesClient({\n * region: 'us-east-1',\n * accessKeyId: 'AKIAIOSFODNN7EXAMPLE',\n * secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',\n * });\n * ```\n *\n * @internal\n */\nexport function buildSesClient(config: ResolvedSesAdapterConfig): SESClient {\n return new SESClient({\n region: config.region,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n });\n}\n","/**\n * @module errors\n * Custom error classes for the aws-ses-adapter library.\n * All errors extend the native `Error` class and include a `name` property\n * suitable for programmatic error identification.\n */\n\n/**\n * Thrown when an operation is attempted before the adapter has been initialized\n * via {@link init}.\n *\n * @example\n * ```ts\n * import { sendEmail } from 'aws-ses-adapter';\n *\n * // Calling sendEmail before init() throws this error\n * try {\n * await sendEmail({ to: 'user@example.com', subject: 'Hi', text: 'Hello' });\n * } catch (err) {\n * if (err instanceof SesNotInitializedError) {\n * console.error('Call init() first');\n * }\n * }\n * ```\n */\nexport class SesNotInitializedError extends Error {\n /** @override */\n readonly name = 'SesNotInitializedError' as const;\n\n constructor() {\n super(\n 'SES Adapter has not been initialized. Call init() before using any adapter methods.',\n );\n }\n}\n\n/**\n * Thrown when the configuration provided to {@link init} is invalid or incomplete.\n * This typically happens when required credentials are missing from both the config\n * object and the environment variables.\n *\n * @example\n * ```ts\n * import { init } from 'aws-ses-adapter';\n *\n * try {\n * init({}); // No region, no env vars set\n * } catch (err) {\n * if (err instanceof SesConfigError) {\n * console.error('Config error:', err.message);\n * }\n * }\n * ```\n */\nexport class SesConfigError extends Error {\n /** @override */\n readonly name = 'SesConfigError' as const;\n\n /**\n * @param message - Description of the configuration issue.\n */\n constructor(message: string) {\n super(message);\n }\n}\n\n/**\n * Thrown when an email send operation fails at the AWS SES level.\n * The original AWS error is available via the `cause` property (ES2022+).\n *\n * @example\n * ```ts\n * import { sendEmail, SesSendError } from 'aws-ses-adapter';\n *\n * try {\n * await sendEmail({ to: 'user@example.com', subject: 'Hi', text: 'Hello' });\n * } catch (err) {\n * if (err instanceof SesSendError) {\n * console.error('Send failed:', err.message);\n * console.error('Original cause:', err.cause);\n * }\n * }\n * ```\n */\nexport class SesSendError extends Error {\n /** @override */\n readonly name = 'SesSendError' as const;\n\n /**\n * @param message - Description of the send failure.\n * @param cause - The underlying error from AWS SDK.\n */\n constructor(message: string, cause?: unknown) {\n super(message, { cause });\n }\n}\n\n/**\n * Thrown when {@link SendEmailOptions} are invalid, such as missing both\n * `html` and `text`, or having an invalid email address format.\n *\n * @example\n * ```ts\n * import { sendEmail, SesValidationError } from 'aws-ses-adapter';\n *\n * try {\n * // Missing both html and text\n * await sendEmail({ to: 'user@example.com', subject: 'Hi' });\n * } catch (err) {\n * if (err instanceof SesValidationError) {\n * console.error('Validation error:', err.message);\n * }\n * }\n * ```\n */\nexport class SesValidationError extends Error {\n /** @override */\n readonly name = 'SesValidationError' as const;\n\n /**\n * @param message - Description of the validation failure.\n */\n constructor(message: string) {\n super(message);\n }\n}\n","/**\n * @module adapter\n * Core SesAdapter class that wraps the AWS SES client and provides\n * simplified email sending methods.\n */\n\nimport type {\n SendEmailCommandInput,\n SendRawEmailCommandInput,\n SESClient,\n} from '@aws-sdk/client-ses';\nimport { SendEmailCommand, SendRawEmailCommand } from '@aws-sdk/client-ses';\nimport { buildSesClient } from './client';\nimport { SesConfigError, SesSendError, SesValidationError } from './errors';\nimport type {\n EmailAttachment,\n ResolvedSesAdapterConfig,\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n\n/**\n * Resolves the adapter configuration by merging user-provided values with\n * environment variable fallbacks.\n *\n * @param config - Optional user-provided configuration.\n * @returns The resolved configuration with all required fields populated.\n * @throws {SesConfigError} When required fields cannot be resolved from config or env vars.\n *\n * @internal\n */\nfunction resolveConfig(\n config: SesAdapterConfig = {},\n): ResolvedSesAdapterConfig {\n const region = config.region ?? process.env['AWS_SES_REGION'];\n const accessKeyId = config.accessKeyId ?? process.env['AWS_ACCESS_KEY_ID'];\n const secretAccessKey =\n config.secretAccessKey ?? process.env['AWS_SECRET_ACCESS_KEY'];\n const defaultFrom = config.defaultFrom ?? process.env['AWS_SES_FROM_EMAIL'];\n\n if (!region) {\n throw new SesConfigError(\n 'AWS SES region is required. Provide it via config.region or the AWS_SES_REGION environment variable.',\n );\n }\n if (!accessKeyId) {\n throw new SesConfigError(\n 'AWS access key ID is required. Provide it via config.accessKeyId or the AWS_ACCESS_KEY_ID environment variable.',\n );\n }\n if (!secretAccessKey) {\n throw new SesConfigError(\n 'AWS secret access key is required. Provide it via config.secretAccessKey or the AWS_SECRET_ACCESS_KEY environment variable.',\n );\n }\n\n return { region, accessKeyId, secretAccessKey, defaultFrom };\n}\n\n/**\n * Normalizes a value that can be a single string or an array of strings\n * into a guaranteed array.\n *\n * @param value - A string or array of strings.\n * @returns An array of strings.\n *\n * @internal\n */\nfunction toArray(value: string | string[]): string[] {\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * Encodes a MIME header value using RFC 2047 Base64 encoded-word syntax\n * when the value contains non-ASCII characters.\n *\n * @param value - The raw header value (e.g. a subject line).\n * @returns The value as-is if it is pure ASCII, otherwise `=?UTF-8?B?...?=`.\n *\n * @internal\n */\nfunction encodeHeaderValue(value: string): string {\n // If no non-ASCII characters are present, return as-is\n if (!/[\\u0080-\\uFFFF]/.test(value)) return value;\n return `=?UTF-8?B?${Buffer.from(value, 'utf-8').toString('base64')}?=`;\n}\n\n/**\n * Builds a multipart/mixed MIME email message ready to be sent via\n * `SendRawEmailCommand`.\n *\n * The body section uses multipart/alternative when both `html` and `text` are\n * supplied so that email clients can choose the best representation.\n *\n * @internal\n */\nfunction buildMimeMessage(\n options: SendEmailWithAttachmentsOptions,\n from: string,\n): string {\n const mixedBoundary = `----=_Mixed_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const altBoundary = `----=_Alt_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n\n const lines: string[] = [];\n\n // RFC 2822 headers\n lines.push(`From: ${from}`);\n lines.push(`To: ${toArray(options.to).join(', ')}`);\n if (options.cc) lines.push(`Cc: ${toArray(options.cc).join(', ')}`);\n if (options.bcc) lines.push(`Bcc: ${toArray(options.bcc).join(', ')}`);\n if (options.replyTo)\n lines.push(`Reply-To: ${toArray(options.replyTo).join(', ')}`);\n lines.push(`Subject: ${encodeHeaderValue(options.subject)}`);\n lines.push('MIME-Version: 1.0');\n lines.push(`Content-Type: multipart/mixed; boundary=\"${mixedBoundary}\"`);\n lines.push('');\n\n // ── Body part ────────────────────────────────────────────────────────────\n if (options.html && options.text) {\n // Both formats: wrap in multipart/alternative\n lines.push(`--${mixedBoundary}`);\n lines.push(\n `Content-Type: multipart/alternative; boundary=\"${altBoundary}\"`,\n );\n lines.push('');\n\n lines.push(`--${altBoundary}`);\n lines.push('Content-Type: text/plain; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.text);\n lines.push('');\n\n lines.push(`--${altBoundary}`);\n lines.push('Content-Type: text/html; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.html);\n lines.push('');\n\n lines.push(`--${altBoundary}--`);\n lines.push('');\n } else if (options.html) {\n lines.push(`--${mixedBoundary}`);\n lines.push('Content-Type: text/html; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.html);\n lines.push('');\n } else if (options.text) {\n lines.push(`--${mixedBoundary}`);\n lines.push('Content-Type: text/plain; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.text);\n lines.push('');\n }\n\n // ── Attachment parts ─────────────────────────────────────────────────────\n for (const attachment of options.attachments) {\n const encoded = encodeAttachment(attachment);\n const safeName = attachment.filename.replace(/\"/g, '');\n\n lines.push(`--${mixedBoundary}`);\n lines.push(`Content-Type: ${attachment.contentType}; name=\"${safeName}\"`);\n lines.push('Content-Transfer-Encoding: base64');\n lines.push(`Content-Disposition: attachment; filename=\"${safeName}\"`);\n lines.push('');\n // RFC 2045: base64 lines must not exceed 76 characters\n lines.push(encoded.match(/.{1,76}/g)?.join('\\r\\n') ?? encoded);\n lines.push('');\n }\n\n lines.push(`--${mixedBoundary}--`);\n\n return lines.join('\\r\\n');\n}\n\n/**\n * Encodes an attachment's content as a base64 string.\n *\n * @param attachment - The attachment to encode.\n * @returns Base64-encoded string.\n *\n * @internal\n */\nfunction encodeAttachment(attachment: EmailAttachment): string {\n if (Buffer.isBuffer(attachment.content)) {\n return attachment.content.toString('base64');\n }\n return Buffer.from(attachment.content).toString('base64');\n}\n\n/**\n * Core adapter class that wraps the AWS SES client and provides a simplified\n * API for sending emails.\n *\n * This class is not meant to be instantiated directly by consumers.\n * Use the singleton functions exported from the main module instead.\n *\n * @see {@link init} to initialize the singleton.\n * @see {@link sendEmail} to send a standard email.\n * @see {@link sendRawEmail} to send a raw MIME email.\n */\nexport class SesAdapter {\n /** @internal */\n private readonly sesClient: SESClient;\n\n /** @internal */\n private readonly config: ResolvedSesAdapterConfig;\n\n /**\n * Creates a new SesAdapter instance.\n *\n * @param userConfig - Optional configuration. Missing fields fall back to environment variables.\n * @throws {SesConfigError} When required configuration fields are missing.\n */\n constructor(userConfig: SesAdapterConfig = {}) {\n this.config = resolveConfig(userConfig);\n this.sesClient = buildSesClient(this.config);\n }\n\n /**\n * Validates that at least one body content field (`html` or `text`) is provided.\n *\n * @param options - Object containing optional `html` and `text` fields.\n * @throws {SesValidationError} When neither field is present.\n *\n * @internal\n */\n private validateBodyOptions(options: { html?: string; text?: string }): void {\n if (!options.html && !options.text) {\n throw new SesValidationError(\n 'At least one of \"html\" or \"text\" must be provided.',\n );\n }\n }\n\n /**\n * Resolves the sender address from the per-call option or the adapter default.\n *\n * @param from - The per-call \"from\" address, if provided.\n * @returns The resolved sender address.\n * @throws {SesValidationError} When no sender address can be determined.\n *\n * @internal\n */\n private resolveFrom(from?: string): string {\n const resolved = from ?? this.config.defaultFrom;\n if (!resolved) {\n throw new SesValidationError(\n 'A sender address is required. Provide \"from\" in the options or set \"defaultFrom\" during init().',\n );\n }\n return resolved;\n }\n\n /**\n * Dispatches a raw MIME message via `SendRawEmailCommand`.\n *\n * @param rawMessage - The raw MIME string to send.\n * @param errorContext - Human-readable context prepended to error messages.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @internal\n */\n private async dispatchRawEmail(\n rawMessage: string,\n errorContext: string,\n ): Promise<SendEmailResult> {\n const params: SendRawEmailCommandInput = {\n RawMessage: {\n Data: Buffer.from(rawMessage),\n },\n };\n\n try {\n const command = new SendRawEmailCommand(params);\n const response = await this.sesClient.send(command);\n\n return {\n success: true,\n messageId: response.MessageId,\n };\n } catch (error) {\n throw new SesSendError(\n `${errorContext}: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n /**\n * Sends an email using AWS SES.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * If `options.from` is not specified, the `defaultFrom` configured during\n * initialization is used. If neither is available, the call throws.\n *\n * @param options - Email sending options.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When required email fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const result = await adapter.sendEmail({\n * to: ['alice@example.com', 'bob@example.com'],\n * subject: 'Hello!',\n * html: '<p>Hello World</p>',\n * text: 'Hello World',\n * cc: 'manager@example.com',\n * });\n * ```\n */\n async sendEmail(options: SendEmailOptions): Promise<SendEmailResult> {\n this.validateBodyOptions(options);\n\n const from = this.resolveFrom(options.from);\n\n const toAddresses = toArray(options.to);\n\n const params: SendEmailCommandInput = {\n Source: from,\n Destination: {\n ToAddresses: toAddresses,\n CcAddresses: options.cc ? toArray(options.cc) : undefined,\n BccAddresses: options.bcc ? toArray(options.bcc) : undefined,\n },\n Message: {\n Subject: {\n Data: options.subject,\n Charset: 'UTF-8',\n },\n Body: {\n Html: options.html\n ? { Data: options.html, Charset: 'UTF-8' }\n : undefined,\n Text: options.text\n ? { Data: options.text, Charset: 'UTF-8' }\n : undefined,\n },\n },\n ReplyToAddresses: options.replyTo ? toArray(options.replyTo) : undefined,\n };\n\n try {\n const command = new SendEmailCommand(params);\n const response = await this.sesClient.send(command);\n\n return {\n success: true,\n messageId: response.MessageId,\n };\n } catch (error) {\n throw new SesSendError(\n `Failed to send email to ${toAddresses.join(', ')}: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n /**\n * Sends a raw MIME email using AWS SES.\n *\n * Use this method when you need full control over the email format, such as\n * sending emails with attachments or custom headers.\n *\n * @param rawMessage - The raw MIME email message as a string.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When the raw message is empty.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const mimeMessage = [\n * 'From: sender@example.com',\n * 'To: recipient@example.com',\n * 'Subject: Test',\n * 'MIME-Version: 1.0',\n * 'Content-Type: text/plain',\n * '',\n * 'Hello World',\n * ].join('\\r\\n');\n *\n * const result = await adapter.sendRawEmail(mimeMessage);\n * ```\n */\n async sendRawEmail(rawMessage: string): Promise<SendEmailResult> {\n if (!rawMessage || rawMessage.trim().length === 0) {\n throw new SesValidationError('rawMessage cannot be empty.');\n }\n\n return this.dispatchRawEmail(rawMessage, 'Failed to send raw email');\n }\n\n /**\n * Sends an email with one or more file attachments using AWS SES.\n *\n * Internally builds a multipart/mixed MIME message and dispatches it via\n * `SendRawEmailCommand`, giving you full attachment support without needing\n * to craft raw MIME by hand.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * The `options.attachments` array must contain at least one item.\n *\n * @param options - Email options including the attachments array.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When required fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const result = await adapter.sendEmailWithAttachments({\n * to: 'alice@example.com',\n * subject: 'Your invoice',\n * html: '<p>Please find the invoice attached.</p>',\n * attachments: [\n * {\n * filename: 'invoice.pdf',\n * content: fs.readFileSync('./invoice.pdf'),\n * contentType: 'application/pdf',\n * },\n * ],\n * });\n * ```\n */\n async sendEmailWithAttachments(\n options: SendEmailWithAttachmentsOptions,\n ): Promise<SendEmailResult> {\n this.validateBodyOptions(options);\n\n if (!options.attachments || options.attachments.length === 0) {\n throw new SesValidationError(\n 'At least one attachment must be provided in SendEmailWithAttachmentsOptions.attachments.',\n );\n }\n\n const from = this.resolveFrom(options.from);\n const toAddresses = toArray(options.to);\n const rawMessage = buildMimeMessage(options, from);\n\n return this.dispatchRawEmail(\n rawMessage,\n `Failed to send email with attachments to ${toAddresses.join(', ')}`,\n );\n }\n\n /**\n * Returns whether the adapter has a default \"From\" address configured.\n *\n * @returns `true` if a default from address is available.\n *\n * @example\n * ```ts\n * if (adapter.hasDefaultFrom()) {\n * console.log('Default from:', adapter.getDefaultFrom());\n * }\n * ```\n */\n hasDefaultFrom(): boolean {\n return !!this.config.defaultFrom;\n }\n\n /**\n * Returns the default \"From\" email address, or `undefined` if not set.\n *\n * @returns The default sender address, or `undefined`.\n *\n * @example\n * ```ts\n * const from = adapter.getDefaultFrom();\n * // => 'noreply@example.com' or undefined\n * ```\n */\n getDefaultFrom(): string | undefined {\n return this.config.defaultFrom;\n }\n\n /**\n * Returns the AWS region the adapter is configured to use.\n *\n * @returns The AWS region string.\n *\n * @example\n * ```ts\n * console.log(adapter.getRegion()); // => 'us-east-1'\n * ```\n */\n getRegion(): string {\n return this.config.region;\n }\n}\n","/**\n * @module aws-ses-adapter\n *\n * A lightweight adapter that simplifies the AWS SES email sending API for Node.js.\n *\n * ## Quick Start\n *\n * **1. Initialize once** (at application startup):\n * ```ts\n * import { init } from 'aws-ses-adapter';\n *\n * init({\n * region: 'us-east-1',\n * accessKeyId: 'YOUR_KEY',\n * secretAccessKey: 'YOUR_SECRET',\n * defaultFrom: 'noreply@example.com',\n * });\n * ```\n *\n * **2. Use anywhere** in your application:\n * ```ts\n * import { sendEmail } from 'aws-ses-adapter';\n *\n * await sendEmail({\n * to: 'user@example.com',\n * subject: 'Welcome!',\n * html: '<h1>Welcome</h1>',\n * });\n * ```\n *\n * ## Environment Variables\n *\n * If credentials are not passed to `init()`, the adapter reads:\n * - `AWS_SES_REGION`\n * - `AWS_ACCESS_KEY_ID`\n * - `AWS_SECRET_ACCESS_KEY`\n * - `AWS_SES_FROM_EMAIL` (optional default sender)\n */\n\nimport { SesAdapter } from './adapter';\nimport { SesNotInitializedError } from './errors';\nimport type {\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n\n// ─── Singleton state ──────────────────────────────────────────────────────────\n\n/** @internal */\nlet _instance: SesAdapter | null = null;\n\n/**\n * Returns the current singleton instance.\n * @throws {SesNotInitializedError} If `init()` has not been called yet.\n * @internal\n */\nfunction getInstance(): SesAdapter {\n if (!_instance) {\n throw new SesNotInitializedError();\n }\n return _instance;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Initializes the SES Adapter singleton.\n *\n * Must be called **once** before any other function in this module.\n * Calling `init()` again will replace the existing singleton instance,\n * which is useful for reconfiguration during testing.\n *\n * Credentials are resolved in the following order:\n * 1. Values provided in the `config` argument.\n * 2. Environment variables (`AWS_SES_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`).\n *\n * @param config - Optional configuration. Omit any field to fall back to environment variables.\n * @throws {SesConfigError} When required credentials cannot be resolved.\n *\n * @example\n * ```ts\n * // Using explicit credentials\n * init({\n * region: 'us-east-1',\n * accessKeyId: process.env.MY_KEY_ID,\n * secretAccessKey: process.env.MY_SECRET,\n * defaultFrom: 'no-reply@myapp.com',\n * });\n *\n * // Relying entirely on environment variables\n * init();\n * ```\n */\nexport function init(config: SesAdapterConfig = {}): void {\n _instance = new SesAdapter(config);\n}\n\n/**\n * Sends an email using the initialized SES Adapter.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * If `options.from` is not specified, the `defaultFrom` set during {@link init} is used.\n *\n * @param options - Email sending options.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When required email fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendEmail } from 'aws-ses-adapter';\n *\n * const result = await sendEmail({\n * to: 'alice@example.com',\n * subject: 'Hello Alice',\n * html: '<p>Hi Alice!</p>',\n * text: 'Hi Alice!',\n * cc: 'manager@example.com',\n * bcc: ['audit@example.com'],\n * replyTo: 'support@example.com',\n * });\n *\n * console.log(result.messageId);\n * ```\n */\nexport async function sendEmail(\n options: SendEmailOptions,\n): Promise<SendEmailResult> {\n return getInstance().sendEmail(options);\n}\n\n/**\n * Sends an email with one or more file attachments using the initialized SES Adapter.\n *\n * Internally builds a multipart/mixed MIME message, so you don't need to\n * construct raw MIME yourself. At least one of `options.html` or `options.text`\n * must be provided, and `options.attachments` must contain at least one item.\n *\n * @param options - Email options including the attachments array.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When required fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendEmailWithAttachments } from 'aws-ses-adapter';\n * import { readFileSync } from 'fs';\n *\n * const result = await sendEmailWithAttachments({\n * to: 'alice@example.com',\n * subject: 'Your invoice',\n * html: '<p>Please find the invoice attached.</p>',\n * attachments: [\n * {\n * filename: 'invoice.pdf',\n * content: readFileSync('./invoice.pdf'),\n * contentType: 'application/pdf',\n * },\n * ],\n * });\n *\n * console.log(result.messageId);\n * ```\n */\nexport async function sendEmailWithAttachments(\n options: SendEmailWithAttachmentsOptions,\n): Promise<SendEmailResult> {\n return getInstance().sendEmailWithAttachments(options);\n}\n\n/**\n * Sends a raw MIME email using the initialized SES Adapter.\n *\n * Use this when you need full control over the email, such as including\n * attachments or custom MIME headers.\n *\n * @param rawMessage - The raw MIME email message as a string.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When the raw message is empty.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendRawEmail } from 'aws-ses-adapter';\n *\n * const mime = [\n * 'From: sender@example.com',\n * 'To: recipient@example.com',\n * 'Subject: File attached',\n * 'MIME-Version: 1.0',\n * 'Content-Type: text/plain',\n * '',\n * 'Please find the attachment.',\n * ].join('\\r\\n');\n *\n * const result = await sendRawEmail(mime);\n * ```\n */\nexport async function sendRawEmail(\n rawMessage: string,\n): Promise<SendEmailResult> {\n return getInstance().sendRawEmail(rawMessage);\n}\n\n/**\n * Returns whether the singleton has been initialized via {@link init}.\n *\n * @returns `true` if `init()` has been called successfully.\n *\n * @example\n * ```ts\n * import { isInitialized } from 'aws-ses-adapter';\n *\n * if (!isInitialized()) {\n * init();\n * }\n * ```\n */\nexport function isInitialized(): boolean {\n return _instance !== null;\n}\n\n/**\n * Returns whether a default \"From\" address is configured in the singleton.\n *\n * @returns `true` if a default sender address is set.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { hasDefaultFrom } from 'aws-ses-adapter';\n *\n * console.log(hasDefaultFrom()); // => true\n * ```\n */\nexport function hasDefaultFrom(): boolean {\n return getInstance().hasDefaultFrom();\n}\n\n/**\n * Returns the default \"From\" email address configured in the singleton,\n * or `undefined` if none was set.\n *\n * @returns The default sender address, or `undefined`.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { getDefaultFrom } from 'aws-ses-adapter';\n *\n * const from = getDefaultFrom();\n * // => 'noreply@example.com' or undefined\n * ```\n */\nexport function getDefaultFrom(): string | undefined {\n return getInstance().getDefaultFrom();\n}\n\n/**\n * Returns the AWS region the singleton is configured to use.\n *\n * @returns The AWS region string (e.g. `'us-east-1'`).\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { getRegion } from 'aws-ses-adapter';\n *\n * console.log(getRegion()); // => 'us-east-1'\n * ```\n */\nexport function getRegion(): string {\n return getInstance().getRegion();\n}\n\n// ─── Re-exports ───────────────────────────────────────────────────────────────\n\n/**\n * Re-export the core adapter class for advanced use cases such as\n * creating multiple independent instances or building framework integrations\n * (e.g., a NestJS module).\n */\nexport { SesAdapter } from './adapter';\n\n/** Re-export all custom error classes for consumer-side `instanceof` checks. */\nexport {\n SesConfigError,\n SesNotInitializedError,\n SesSendError,\n SesValidationError,\n} from './errors';\n\n/** Re-export all public types and interfaces. */\nexport type {\n EmailAttachment,\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/adapter.ts","../src/index.ts"],"names":["SESClient","SendRawEmailCommand","SendEmailCommand"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module client\n * Factory and utilities for building an AWS SESClient instance\n * from a resolved adapter configuration.\n */\n\nimport { SESClient } from '@aws-sdk/client-ses';\nimport type { ResolvedSesAdapterConfig } from './types';\n\n/**\n * Creates and returns a configured {@link SESClient} instance using the\n * resolved adapter configuration.\n *\n * @param config - The resolved configuration containing region and credentials.\n * @returns A ready-to-use AWS SESClient.\n *\n * @example\n * ```ts\n * const client = buildSesClient({\n * region: 'us-east-1',\n * accessKeyId: 'AKIAIOSFODNN7EXAMPLE',\n * secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',\n * });\n * ```\n *\n * @internal\n */\nexport function buildSesClient(config: ResolvedSesAdapterConfig): SESClient {\n return new SESClient({\n region: config.region,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n });\n}\n","/**\n * @module errors\n * Custom error classes for the aws-ses-adapter library.\n * All errors extend the native `Error` class and include a `name` property\n * suitable for programmatic error identification.\n */\n\n/**\n * Thrown when an operation is attempted before the adapter has been initialized\n * via {@link init}.\n *\n * @example\n * ```ts\n * import { sendEmail } from '@fmontoya/aws-ses-adapter';\n *\n * // Calling sendEmail before init() throws this error\n * try {\n * await sendEmail({ to: 'user@example.com', subject: 'Hi', text: 'Hello' });\n * } catch (err) {\n * if (err instanceof SesNotInitializedError) {\n * console.error('Call init() first');\n * }\n * }\n * ```\n */\nexport class SesNotInitializedError extends Error {\n /** @override */\n readonly name = 'SesNotInitializedError' as const;\n\n constructor() {\n super(\n 'SES Adapter has not been initialized. Call init() before using any adapter methods.',\n );\n }\n}\n\n/**\n * Thrown when the configuration provided to {@link init} is invalid or incomplete.\n * This typically happens when required credentials are missing from both the config\n * object and the environment variables.\n *\n * @example\n * ```ts\n * import { init } from '@fmontoya/aws-ses-adapter';\n *\n * try {\n * init({}); // No region, no env vars set\n * } catch (err) {\n * if (err instanceof SesConfigError) {\n * console.error('Config error:', err.message);\n * }\n * }\n * ```\n */\nexport class SesConfigError extends Error {\n /** @override */\n readonly name = 'SesConfigError' as const;\n\n /**\n * @param message - Description of the configuration issue.\n */\n constructor(message: string) {\n super(message);\n }\n}\n\n/**\n * Thrown when an email send operation fails at the AWS SES level.\n * The original AWS error is available via the `cause` property (ES2022+).\n *\n * @example\n * ```ts\n * import { sendEmail, SesSendError } from '@fmontoya/aws-ses-adapter';\n *\n * try {\n * await sendEmail({ to: 'user@example.com', subject: 'Hi', text: 'Hello' });\n * } catch (err) {\n * if (err instanceof SesSendError) {\n * console.error('Send failed:', err.message);\n * console.error('Original cause:', err.cause);\n * }\n * }\n * ```\n */\nexport class SesSendError extends Error {\n /** @override */\n readonly name = 'SesSendError' as const;\n\n /**\n * @param message - Description of the send failure.\n * @param cause - The underlying error from AWS SDK.\n */\n constructor(message: string, cause?: unknown) {\n super(message, { cause });\n }\n}\n\n/**\n * Thrown when {@link SendEmailOptions} are invalid, such as missing both\n * `html` and `text`, or having an invalid email address format.\n *\n * @example\n * ```ts\n * import { sendEmail, SesValidationError } from '@fmontoya/aws-ses-adapter';\n *\n * try {\n * // Missing both html and text\n * await sendEmail({ to: 'user@example.com', subject: 'Hi' });\n * } catch (err) {\n * if (err instanceof SesValidationError) {\n * console.error('Validation error:', err.message);\n * }\n * }\n * ```\n */\nexport class SesValidationError extends Error {\n /** @override */\n readonly name = 'SesValidationError' as const;\n\n /**\n * @param message - Description of the validation failure.\n */\n constructor(message: string) {\n super(message);\n }\n}\n","/**\n * @module adapter\n * Core SesAdapter class that wraps the AWS SES client and provides\n * simplified email sending methods.\n */\n\nimport type {\n SendEmailCommandInput,\n SendRawEmailCommandInput,\n SESClient,\n} from '@aws-sdk/client-ses';\nimport { SendEmailCommand, SendRawEmailCommand } from '@aws-sdk/client-ses';\nimport { buildSesClient } from './client';\nimport { SesConfigError, SesSendError, SesValidationError } from './errors';\nimport type {\n EmailAttachment,\n ResolvedSesAdapterConfig,\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n\n/**\n * Resolves the adapter configuration by merging user-provided values with\n * environment variable fallbacks.\n *\n * @param config - Optional user-provided configuration.\n * @returns The resolved configuration with all required fields populated.\n * @throws {SesConfigError} When required fields cannot be resolved from config or env vars.\n *\n * @internal\n */\nfunction resolveConfig(\n config: SesAdapterConfig = {},\n): ResolvedSesAdapterConfig {\n const region = config.region ?? process.env['AWS_SES_REGION'];\n const accessKeyId =\n config.credentials?.accessKeyId ??\n config.accessKeyId ??\n process.env['AWS_ACCESS_KEY_ID'];\n const secretAccessKey =\n config.credentials?.secretAccessKey ??\n config.secretAccessKey ??\n process.env['AWS_SECRET_ACCESS_KEY'];\n const defaultFrom = config.defaultFrom ?? process.env['AWS_SES_FROM_EMAIL'];\n\n if (!region) {\n throw new SesConfigError(\n 'AWS SES region is required. Provide it via config.region or the AWS_SES_REGION environment variable.',\n );\n }\n if (!accessKeyId) {\n throw new SesConfigError(\n 'AWS access key ID is required. Provide it via config.credentials.accessKeyId, or the AWS_ACCESS_KEY_ID environment variable.',\n );\n }\n if (!secretAccessKey) {\n throw new SesConfigError(\n 'AWS secret access key is required. Provide it via config.credentials.secretAccessKey, or the AWS_SECRET_ACCESS_KEY environment variable.',\n );\n }\n\n return { region, accessKeyId, secretAccessKey, defaultFrom };\n}\n\n/**\n * Normalizes a value that can be a single string or an array of strings\n * into a guaranteed array.\n *\n * @param value - A string or array of strings.\n * @returns An array of strings.\n *\n * @internal\n */\nfunction toArray(value: string | string[]): string[] {\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * Encodes a MIME header value using RFC 2047 Base64 encoded-word syntax\n * when the value contains non-ASCII characters.\n *\n * @param value - The raw header value (e.g. a subject line).\n * @returns The value as-is if it is pure ASCII, otherwise `=?UTF-8?B?...?=`.\n *\n * @internal\n */\nfunction encodeHeaderValue(value: string): string {\n // If no non-ASCII characters are present, return as-is\n if (!/[\\u0080-\\uFFFF]/.test(value)) return value;\n return `=?UTF-8?B?${Buffer.from(value, 'utf-8').toString('base64')}?=`;\n}\n\n/**\n * Builds a multipart/mixed MIME email message ready to be sent via\n * `SendRawEmailCommand`.\n *\n * The body section uses multipart/alternative when both `html` and `text` are\n * supplied so that email clients can choose the best representation.\n *\n * @internal\n */\nfunction buildMimeMessage(\n options: SendEmailWithAttachmentsOptions,\n from: string,\n): string {\n const mixedBoundary = `----=_Mixed_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const altBoundary = `----=_Alt_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n\n const lines: string[] = [];\n\n // RFC 2822 headers\n lines.push(`From: ${from}`);\n lines.push(`To: ${toArray(options.to).join(', ')}`);\n if (options.cc) lines.push(`Cc: ${toArray(options.cc).join(', ')}`);\n if (options.bcc) lines.push(`Bcc: ${toArray(options.bcc).join(', ')}`);\n if (options.replyTo)\n lines.push(`Reply-To: ${toArray(options.replyTo).join(', ')}`);\n lines.push(`Subject: ${encodeHeaderValue(options.subject)}`);\n lines.push('MIME-Version: 1.0');\n lines.push(`Content-Type: multipart/mixed; boundary=\"${mixedBoundary}\"`);\n lines.push('');\n\n // ── Body part ────────────────────────────────────────────────────────────\n if (options.html && options.text) {\n // Both formats: wrap in multipart/alternative\n lines.push(`--${mixedBoundary}`);\n lines.push(\n `Content-Type: multipart/alternative; boundary=\"${altBoundary}\"`,\n );\n lines.push('');\n\n lines.push(`--${altBoundary}`);\n lines.push('Content-Type: text/plain; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.text);\n lines.push('');\n\n lines.push(`--${altBoundary}`);\n lines.push('Content-Type: text/html; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.html);\n lines.push('');\n\n lines.push(`--${altBoundary}--`);\n lines.push('');\n } else if (options.html) {\n lines.push(`--${mixedBoundary}`);\n lines.push('Content-Type: text/html; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.html);\n lines.push('');\n } else if (options.text) {\n lines.push(`--${mixedBoundary}`);\n lines.push('Content-Type: text/plain; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.text);\n lines.push('');\n }\n\n // ── Attachment parts ─────────────────────────────────────────────────────\n for (const attachment of options.attachments) {\n const encoded = encodeAttachment(attachment);\n const safeName = attachment.filename.replace(/\"/g, '');\n\n lines.push(`--${mixedBoundary}`);\n lines.push(`Content-Type: ${attachment.contentType}; name=\"${safeName}\"`);\n lines.push('Content-Transfer-Encoding: base64');\n lines.push(`Content-Disposition: attachment; filename=\"${safeName}\"`);\n lines.push('');\n // RFC 2045: base64 lines must not exceed 76 characters\n lines.push(encoded.match(/.{1,76}/g)?.join('\\r\\n') ?? encoded);\n lines.push('');\n }\n\n lines.push(`--${mixedBoundary}--`);\n\n return lines.join('\\r\\n');\n}\n\n/**\n * Encodes an attachment's content as a base64 string.\n *\n * @param attachment - The attachment to encode.\n * @returns Base64-encoded string.\n *\n * @internal\n */\nfunction encodeAttachment(attachment: EmailAttachment): string {\n if (Buffer.isBuffer(attachment.content)) {\n return attachment.content.toString('base64');\n }\n return Buffer.from(attachment.content).toString('base64');\n}\n\n/**\n * Core adapter class that wraps the AWS SES client and provides a simplified\n * API for sending emails.\n *\n * This class is not meant to be instantiated directly by consumers.\n * Use the singleton functions exported from the main module instead.\n *\n * @see {@link init} to initialize the singleton.\n * @see {@link sendEmail} to send a standard email.\n * @see {@link sendRawEmail} to send a raw MIME email.\n */\nexport class SesAdapter {\n /** @internal */\n private readonly sesClient: SESClient;\n\n /** @internal */\n private readonly config: ResolvedSesAdapterConfig;\n\n /**\n * Creates a new SesAdapter instance.\n *\n * @param userConfig - Optional configuration. Missing fields fall back to environment variables.\n * @throws {SesConfigError} When required configuration fields are missing.\n */\n constructor(userConfig: SesAdapterConfig = {}) {\n this.config = resolveConfig(userConfig);\n this.sesClient = buildSesClient(this.config);\n }\n\n /**\n * Validates that at least one body content field (`html` or `text`) is provided.\n *\n * @param options - Object containing optional `html` and `text` fields.\n * @throws {SesValidationError} When neither field is present.\n *\n * @internal\n */\n private validateBodyOptions(options: { html?: string; text?: string }): void {\n if (!options.html && !options.text) {\n throw new SesValidationError(\n 'At least one of \"html\" or \"text\" must be provided.',\n );\n }\n }\n\n /**\n * Resolves the sender address from the per-call option or the adapter default.\n *\n * @param from - The per-call \"from\" address, if provided.\n * @returns The resolved sender address.\n * @throws {SesValidationError} When no sender address can be determined.\n *\n * @internal\n */\n private resolveFrom(from?: string): string {\n const resolved = from ?? this.config.defaultFrom;\n if (!resolved) {\n throw new SesValidationError(\n 'A sender address is required. Provide \"from\" in the options or set \"defaultFrom\" during init().',\n );\n }\n return resolved;\n }\n\n /**\n * Dispatches a raw MIME message via `SendRawEmailCommand`.\n *\n * @param rawMessage - The raw MIME string to send.\n * @param errorContext - Human-readable context prepended to error messages.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @internal\n */\n private async dispatchRawEmail(\n rawMessage: string,\n errorContext: string,\n ): Promise<SendEmailResult> {\n const params: SendRawEmailCommandInput = {\n RawMessage: {\n Data: Buffer.from(rawMessage),\n },\n };\n\n try {\n const command = new SendRawEmailCommand(params);\n const response = await this.sesClient.send(command);\n\n return {\n success: true,\n messageId: response.MessageId,\n };\n } catch (error) {\n throw new SesSendError(\n `${errorContext}: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n /**\n * Sends an email using AWS SES.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * If `options.from` is not specified, the `defaultFrom` configured during\n * initialization is used. If neither is available, the call throws.\n *\n * @param options - Email sending options.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When required email fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const result = await adapter.sendEmail({\n * to: ['alice@example.com', 'bob@example.com'],\n * subject: 'Hello!',\n * html: '<p>Hello World</p>',\n * text: 'Hello World',\n * cc: 'manager@example.com',\n * });\n * ```\n */\n async sendEmail(options: SendEmailOptions): Promise<SendEmailResult> {\n this.validateBodyOptions(options);\n\n const from = this.resolveFrom(options.from);\n\n const toAddresses = toArray(options.to);\n\n const params: SendEmailCommandInput = {\n Source: from,\n Destination: {\n ToAddresses: toAddresses,\n CcAddresses: options.cc ? toArray(options.cc) : undefined,\n BccAddresses: options.bcc ? toArray(options.bcc) : undefined,\n },\n Message: {\n Subject: {\n Data: options.subject,\n Charset: 'UTF-8',\n },\n Body: {\n Html: options.html\n ? { Data: options.html, Charset: 'UTF-8' }\n : undefined,\n Text: options.text\n ? { Data: options.text, Charset: 'UTF-8' }\n : undefined,\n },\n },\n ReplyToAddresses: options.replyTo ? toArray(options.replyTo) : undefined,\n };\n\n try {\n const command = new SendEmailCommand(params);\n const response = await this.sesClient.send(command);\n\n return {\n success: true,\n messageId: response.MessageId,\n };\n } catch (error) {\n throw new SesSendError(\n `Failed to send email to ${toAddresses.join(', ')}: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n /**\n * Sends a raw MIME email using AWS SES.\n *\n * Use this method when you need full control over the email format, such as\n * sending emails with attachments or custom headers.\n *\n * @param rawMessage - The raw MIME email message as a string.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When the raw message is empty.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const mimeMessage = [\n * 'From: sender@example.com',\n * 'To: recipient@example.com',\n * 'Subject: Test',\n * 'MIME-Version: 1.0',\n * 'Content-Type: text/plain',\n * '',\n * 'Hello World',\n * ].join('\\r\\n');\n *\n * const result = await adapter.sendRawEmail(mimeMessage);\n * ```\n */\n async sendRawEmail(rawMessage: string): Promise<SendEmailResult> {\n if (!rawMessage || rawMessage.trim().length === 0) {\n throw new SesValidationError('rawMessage cannot be empty.');\n }\n\n return this.dispatchRawEmail(rawMessage, 'Failed to send raw email');\n }\n\n /**\n * Sends an email with one or more file attachments using AWS SES.\n *\n * Internally builds a multipart/mixed MIME message and dispatches it via\n * `SendRawEmailCommand`, giving you full attachment support without needing\n * to craft raw MIME by hand.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * The `options.attachments` array must contain at least one item.\n *\n * @param options - Email options including the attachments array.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When required fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const result = await adapter.sendEmailWithAttachments({\n * to: 'alice@example.com',\n * subject: 'Your invoice',\n * html: '<p>Please find the invoice attached.</p>',\n * attachments: [\n * {\n * filename: 'invoice.pdf',\n * content: fs.readFileSync('./invoice.pdf'),\n * contentType: 'application/pdf',\n * },\n * ],\n * });\n * ```\n */\n async sendEmailWithAttachments(\n options: SendEmailWithAttachmentsOptions,\n ): Promise<SendEmailResult> {\n this.validateBodyOptions(options);\n\n if (!options.attachments || options.attachments.length === 0) {\n throw new SesValidationError(\n 'At least one attachment must be provided in SendEmailWithAttachmentsOptions.attachments.',\n );\n }\n\n const from = this.resolveFrom(options.from);\n const toAddresses = toArray(options.to);\n const rawMessage = buildMimeMessage(options, from);\n\n return this.dispatchRawEmail(\n rawMessage,\n `Failed to send email with attachments to ${toAddresses.join(', ')}`,\n );\n }\n\n /**\n * Returns whether the adapter has a default \"From\" address configured.\n *\n * @returns `true` if a default from address is available.\n *\n * @example\n * ```ts\n * if (adapter.hasDefaultFrom()) {\n * console.log('Default from:', adapter.getDefaultFrom());\n * }\n * ```\n */\n hasDefaultFrom(): boolean {\n return !!this.config.defaultFrom;\n }\n\n /**\n * Returns the default \"From\" email address, or `undefined` if not set.\n *\n * @returns The default sender address, or `undefined`.\n *\n * @example\n * ```ts\n * const from = adapter.getDefaultFrom();\n * // => 'noreply@example.com' or undefined\n * ```\n */\n getDefaultFrom(): string | undefined {\n return this.config.defaultFrom;\n }\n\n /**\n * Returns the AWS region the adapter is configured to use.\n *\n * @returns The AWS region string.\n *\n * @example\n * ```ts\n * console.log(adapter.getRegion()); // => 'us-east-1'\n * ```\n */\n getRegion(): string {\n return this.config.region;\n }\n}\n","/**\n * @module aws-ses-adapter\n *\n * A lightweight adapter that simplifies the AWS SES email sending API for Node.js.\n *\n * ## Quick Start\n *\n * **1. Initialize once** (at application startup):\n * ```ts\n * import { init } from '@fmontoya/aws-ses-adapter';\n *\n * init({\n * region: 'us-east-1',\n * credentials: {\n * accessKeyId: 'YOUR_KEY',\n * secretAccessKey: 'YOUR_SECRET',\n * },\n * defaultFrom: 'noreply@example.com',\n * });\n * ```\n *\n * **2. Use anywhere** in your application:\n * ```ts\n * import { sendEmail } from '@fmontoya/aws-ses-adapter';\n *\n * await sendEmail({\n * to: 'user@example.com',\n * subject: 'Welcome!',\n * html: '<h1>Welcome</h1>',\n * });\n * ```\n *\n * ## Environment Variables\n *\n * If credentials are not passed to `init()`, the adapter reads:\n * - `AWS_SES_REGION`\n * - `AWS_ACCESS_KEY_ID`\n * - `AWS_SECRET_ACCESS_KEY`\n * - `AWS_SES_FROM_EMAIL` (optional default sender)\n */\n\nimport { SesAdapter } from './adapter';\nimport { SesNotInitializedError } from './errors';\nimport type {\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n\n// ─── Singleton state ──────────────────────────────────────────────────────────\n\n/** @internal */\nlet _instance: SesAdapter | null = null;\n\n/**\n * Returns the current singleton instance.\n * @throws {SesNotInitializedError} If `init()` has not been called yet.\n * @internal\n */\nfunction getInstance(): SesAdapter {\n if (!_instance) {\n throw new SesNotInitializedError();\n }\n return _instance;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Initializes the SES Adapter singleton.\n *\n * Must be called **once** before any other function in this module.\n * Calling `init()` again will replace the existing singleton instance,\n * which is useful for reconfiguration during testing.\n *\n * Credentials are resolved in the following order:\n * 1. `config.credentials.accessKeyId` / `config.credentials.secretAccessKey`\n * 2. `config.accessKeyId` / `config.secretAccessKey` _(deprecated)_\n * 3. Environment variables (`AWS_SES_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)\n *\n * @param config - Optional configuration. Omit any field to fall back to environment variables.\n * @throws {SesConfigError} When required credentials cannot be resolved.\n *\n * @example\n * ```ts\n * // Using explicit credentials\n * init({\n * region: 'us-east-1',\n * credentials: {\n * accessKeyId: process.env.MY_KEY_ID,\n * secretAccessKey: process.env.MY_SECRET,\n * },\n * defaultFrom: 'no-reply@myapp.com',\n * });\n *\n * // Relying entirely on environment variables\n * init();\n * ```\n */\nexport function init(config: SesAdapterConfig = {}): void {\n _instance = new SesAdapter(config);\n}\n\n/**\n * Sends an email using the initialized SES Adapter.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * If `options.from` is not specified, the `defaultFrom` set during {@link init} is used.\n *\n * @param options - Email sending options.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When required email fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendEmail } from '@fmontoya/aws-ses-adapter';\n *\n * const result = await sendEmail({\n * to: 'alice@example.com',\n * subject: 'Hello Alice',\n * html: '<p>Hi Alice!</p>',\n * text: 'Hi Alice!',\n * cc: 'manager@example.com',\n * bcc: ['audit@example.com'],\n * replyTo: 'support@example.com',\n * });\n *\n * console.log(result.messageId);\n * ```\n */\nexport async function sendEmail(\n options: SendEmailOptions,\n): Promise<SendEmailResult> {\n return getInstance().sendEmail(options);\n}\n\n/**\n * Sends an email with one or more file attachments using the initialized SES Adapter.\n *\n * Internally builds a multipart/mixed MIME message, so you don't need to\n * construct raw MIME yourself. At least one of `options.html` or `options.text`\n * must be provided, and `options.attachments` must contain at least one item.\n *\n * @param options - Email options including the attachments array.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When required fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendEmailWithAttachments } from '@fmontoya/aws-ses-adapter';\n * import { readFileSync } from 'fs';\n *\n * const result = await sendEmailWithAttachments({\n * to: 'alice@example.com',\n * subject: 'Your invoice',\n * html: '<p>Please find the invoice attached.</p>',\n * attachments: [\n * {\n * filename: 'invoice.pdf',\n * content: readFileSync('./invoice.pdf'),\n * contentType: 'application/pdf',\n * },\n * ],\n * });\n *\n * console.log(result.messageId);\n * ```\n */\nexport async function sendEmailWithAttachments(\n options: SendEmailWithAttachmentsOptions,\n): Promise<SendEmailResult> {\n return getInstance().sendEmailWithAttachments(options);\n}\n\n/**\n * Sends a raw MIME email using the initialized SES Adapter.\n *\n * Use this when you need full control over the email, such as including\n * attachments or custom MIME headers.\n *\n * @param rawMessage - The raw MIME email message as a string.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When the raw message is empty.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendRawEmail } from '@fmontoya/aws-ses-adapter';\n *\n * const mime = [\n * 'From: sender@example.com',\n * 'To: recipient@example.com',\n * 'Subject: File attached',\n * 'MIME-Version: 1.0',\n * 'Content-Type: text/plain',\n * '',\n * 'Please find the attachment.',\n * ].join('\\r\\n');\n *\n * const result = await sendRawEmail(mime);\n * ```\n */\nexport async function sendRawEmail(\n rawMessage: string,\n): Promise<SendEmailResult> {\n return getInstance().sendRawEmail(rawMessage);\n}\n\n/**\n * Returns whether the singleton has been initialized via {@link init}.\n *\n * @returns `true` if `init()` has been called successfully.\n *\n * @example\n * ```ts\n * import { isInitialized } from '@fmontoya/aws-ses-adapter';\n *\n * if (!isInitialized()) {\n * init();\n * }\n * ```\n */\nexport function isInitialized(): boolean {\n return _instance !== null;\n}\n\n/**\n * Returns whether a default \"From\" address is configured in the singleton.\n *\n * @returns `true` if a default sender address is set.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { hasDefaultFrom } from '@fmontoya/aws-ses-adapter';\n *\n * console.log(hasDefaultFrom()); // => true\n * ```\n */\nexport function hasDefaultFrom(): boolean {\n return getInstance().hasDefaultFrom();\n}\n\n/**\n * Returns the default \"From\" email address configured in the singleton,\n * or `undefined` if none was set.\n *\n * @returns The default sender address, or `undefined`.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { getDefaultFrom } from '@fmontoya/aws-ses-adapter';\n *\n * const from = getDefaultFrom();\n * // => 'noreply@example.com' or undefined\n * ```\n */\nexport function getDefaultFrom(): string | undefined {\n return getInstance().getDefaultFrom();\n}\n\n/**\n * Returns the AWS region the singleton is configured to use.\n *\n * @returns The AWS region string (e.g. `'us-east-1'`).\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { getRegion } from '@fmontoya/aws-ses-adapter';\n *\n * console.log(getRegion()); // => 'us-east-1'\n * ```\n */\nexport function getRegion(): string {\n return getInstance().getRegion();\n}\n\n// ─── Re-exports ───────────────────────────────────────────────────────────────\n\n/**\n * Re-export the core adapter class for advanced use cases such as\n * creating multiple independent instances or building framework integrations\n * (e.g., a NestJS module).\n */\nexport { SesAdapter } from './adapter';\n\n/** Re-export all custom error classes for consumer-side `instanceof` checks. */\nexport {\n SesConfigError,\n SesNotInitializedError,\n SesSendError,\n SesValidationError,\n} from './errors';\n\n/** Re-export all public types and interfaces. */\nexport type {\n EmailAttachment,\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n"]}
package/dist/index.mjs CHANGED
@@ -56,8 +56,8 @@ var SesValidationError = class extends Error {
56
56
  // src/adapter.ts
57
57
  function resolveConfig(config = {}) {
58
58
  const region = config.region ?? process.env["AWS_SES_REGION"];
59
- const accessKeyId = config.accessKeyId ?? process.env["AWS_ACCESS_KEY_ID"];
60
- const secretAccessKey = config.secretAccessKey ?? process.env["AWS_SECRET_ACCESS_KEY"];
59
+ const accessKeyId = config.credentials?.accessKeyId ?? config.accessKeyId ?? process.env["AWS_ACCESS_KEY_ID"];
60
+ const secretAccessKey = config.credentials?.secretAccessKey ?? config.secretAccessKey ?? process.env["AWS_SECRET_ACCESS_KEY"];
61
61
  const defaultFrom = config.defaultFrom ?? process.env["AWS_SES_FROM_EMAIL"];
62
62
  if (!region) {
63
63
  throw new SesConfigError(
@@ -66,12 +66,12 @@ function resolveConfig(config = {}) {
66
66
  }
67
67
  if (!accessKeyId) {
68
68
  throw new SesConfigError(
69
- "AWS access key ID is required. Provide it via config.accessKeyId or the AWS_ACCESS_KEY_ID environment variable."
69
+ "AWS access key ID is required. Provide it via config.credentials.accessKeyId, or the AWS_ACCESS_KEY_ID environment variable."
70
70
  );
71
71
  }
72
72
  if (!secretAccessKey) {
73
73
  throw new SesConfigError(
74
- "AWS secret access key is required. Provide it via config.secretAccessKey or the AWS_SECRET_ACCESS_KEY environment variable."
74
+ "AWS secret access key is required. Provide it via config.credentials.secretAccessKey, or the AWS_SECRET_ACCESS_KEY environment variable."
75
75
  );
76
76
  }
77
77
  return { region, accessKeyId, secretAccessKey, defaultFrom };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/adapter.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @module client\n * Factory and utilities for building an AWS SESClient instance\n * from a resolved adapter configuration.\n */\n\nimport { SESClient } from '@aws-sdk/client-ses';\nimport type { ResolvedSesAdapterConfig } from './types';\n\n/**\n * Creates and returns a configured {@link SESClient} instance using the\n * resolved adapter configuration.\n *\n * @param config - The resolved configuration containing region and credentials.\n * @returns A ready-to-use AWS SESClient.\n *\n * @example\n * ```ts\n * const client = buildSesClient({\n * region: 'us-east-1',\n * accessKeyId: 'AKIAIOSFODNN7EXAMPLE',\n * secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',\n * });\n * ```\n *\n * @internal\n */\nexport function buildSesClient(config: ResolvedSesAdapterConfig): SESClient {\n return new SESClient({\n region: config.region,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n });\n}\n","/**\n * @module errors\n * Custom error classes for the aws-ses-adapter library.\n * All errors extend the native `Error` class and include a `name` property\n * suitable for programmatic error identification.\n */\n\n/**\n * Thrown when an operation is attempted before the adapter has been initialized\n * via {@link init}.\n *\n * @example\n * ```ts\n * import { sendEmail } from 'aws-ses-adapter';\n *\n * // Calling sendEmail before init() throws this error\n * try {\n * await sendEmail({ to: 'user@example.com', subject: 'Hi', text: 'Hello' });\n * } catch (err) {\n * if (err instanceof SesNotInitializedError) {\n * console.error('Call init() first');\n * }\n * }\n * ```\n */\nexport class SesNotInitializedError extends Error {\n /** @override */\n readonly name = 'SesNotInitializedError' as const;\n\n constructor() {\n super(\n 'SES Adapter has not been initialized. Call init() before using any adapter methods.',\n );\n }\n}\n\n/**\n * Thrown when the configuration provided to {@link init} is invalid or incomplete.\n * This typically happens when required credentials are missing from both the config\n * object and the environment variables.\n *\n * @example\n * ```ts\n * import { init } from 'aws-ses-adapter';\n *\n * try {\n * init({}); // No region, no env vars set\n * } catch (err) {\n * if (err instanceof SesConfigError) {\n * console.error('Config error:', err.message);\n * }\n * }\n * ```\n */\nexport class SesConfigError extends Error {\n /** @override */\n readonly name = 'SesConfigError' as const;\n\n /**\n * @param message - Description of the configuration issue.\n */\n constructor(message: string) {\n super(message);\n }\n}\n\n/**\n * Thrown when an email send operation fails at the AWS SES level.\n * The original AWS error is available via the `cause` property (ES2022+).\n *\n * @example\n * ```ts\n * import { sendEmail, SesSendError } from 'aws-ses-adapter';\n *\n * try {\n * await sendEmail({ to: 'user@example.com', subject: 'Hi', text: 'Hello' });\n * } catch (err) {\n * if (err instanceof SesSendError) {\n * console.error('Send failed:', err.message);\n * console.error('Original cause:', err.cause);\n * }\n * }\n * ```\n */\nexport class SesSendError extends Error {\n /** @override */\n readonly name = 'SesSendError' as const;\n\n /**\n * @param message - Description of the send failure.\n * @param cause - The underlying error from AWS SDK.\n */\n constructor(message: string, cause?: unknown) {\n super(message, { cause });\n }\n}\n\n/**\n * Thrown when {@link SendEmailOptions} are invalid, such as missing both\n * `html` and `text`, or having an invalid email address format.\n *\n * @example\n * ```ts\n * import { sendEmail, SesValidationError } from 'aws-ses-adapter';\n *\n * try {\n * // Missing both html and text\n * await sendEmail({ to: 'user@example.com', subject: 'Hi' });\n * } catch (err) {\n * if (err instanceof SesValidationError) {\n * console.error('Validation error:', err.message);\n * }\n * }\n * ```\n */\nexport class SesValidationError extends Error {\n /** @override */\n readonly name = 'SesValidationError' as const;\n\n /**\n * @param message - Description of the validation failure.\n */\n constructor(message: string) {\n super(message);\n }\n}\n","/**\n * @module adapter\n * Core SesAdapter class that wraps the AWS SES client and provides\n * simplified email sending methods.\n */\n\nimport type {\n SendEmailCommandInput,\n SendRawEmailCommandInput,\n SESClient,\n} from '@aws-sdk/client-ses';\nimport { SendEmailCommand, SendRawEmailCommand } from '@aws-sdk/client-ses';\nimport { buildSesClient } from './client';\nimport { SesConfigError, SesSendError, SesValidationError } from './errors';\nimport type {\n EmailAttachment,\n ResolvedSesAdapterConfig,\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n\n/**\n * Resolves the adapter configuration by merging user-provided values with\n * environment variable fallbacks.\n *\n * @param config - Optional user-provided configuration.\n * @returns The resolved configuration with all required fields populated.\n * @throws {SesConfigError} When required fields cannot be resolved from config or env vars.\n *\n * @internal\n */\nfunction resolveConfig(\n config: SesAdapterConfig = {},\n): ResolvedSesAdapterConfig {\n const region = config.region ?? process.env['AWS_SES_REGION'];\n const accessKeyId = config.accessKeyId ?? process.env['AWS_ACCESS_KEY_ID'];\n const secretAccessKey =\n config.secretAccessKey ?? process.env['AWS_SECRET_ACCESS_KEY'];\n const defaultFrom = config.defaultFrom ?? process.env['AWS_SES_FROM_EMAIL'];\n\n if (!region) {\n throw new SesConfigError(\n 'AWS SES region is required. Provide it via config.region or the AWS_SES_REGION environment variable.',\n );\n }\n if (!accessKeyId) {\n throw new SesConfigError(\n 'AWS access key ID is required. Provide it via config.accessKeyId or the AWS_ACCESS_KEY_ID environment variable.',\n );\n }\n if (!secretAccessKey) {\n throw new SesConfigError(\n 'AWS secret access key is required. Provide it via config.secretAccessKey or the AWS_SECRET_ACCESS_KEY environment variable.',\n );\n }\n\n return { region, accessKeyId, secretAccessKey, defaultFrom };\n}\n\n/**\n * Normalizes a value that can be a single string or an array of strings\n * into a guaranteed array.\n *\n * @param value - A string or array of strings.\n * @returns An array of strings.\n *\n * @internal\n */\nfunction toArray(value: string | string[]): string[] {\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * Encodes a MIME header value using RFC 2047 Base64 encoded-word syntax\n * when the value contains non-ASCII characters.\n *\n * @param value - The raw header value (e.g. a subject line).\n * @returns The value as-is if it is pure ASCII, otherwise `=?UTF-8?B?...?=`.\n *\n * @internal\n */\nfunction encodeHeaderValue(value: string): string {\n // If no non-ASCII characters are present, return as-is\n if (!/[\\u0080-\\uFFFF]/.test(value)) return value;\n return `=?UTF-8?B?${Buffer.from(value, 'utf-8').toString('base64')}?=`;\n}\n\n/**\n * Builds a multipart/mixed MIME email message ready to be sent via\n * `SendRawEmailCommand`.\n *\n * The body section uses multipart/alternative when both `html` and `text` are\n * supplied so that email clients can choose the best representation.\n *\n * @internal\n */\nfunction buildMimeMessage(\n options: SendEmailWithAttachmentsOptions,\n from: string,\n): string {\n const mixedBoundary = `----=_Mixed_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const altBoundary = `----=_Alt_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n\n const lines: string[] = [];\n\n // RFC 2822 headers\n lines.push(`From: ${from}`);\n lines.push(`To: ${toArray(options.to).join(', ')}`);\n if (options.cc) lines.push(`Cc: ${toArray(options.cc).join(', ')}`);\n if (options.bcc) lines.push(`Bcc: ${toArray(options.bcc).join(', ')}`);\n if (options.replyTo)\n lines.push(`Reply-To: ${toArray(options.replyTo).join(', ')}`);\n lines.push(`Subject: ${encodeHeaderValue(options.subject)}`);\n lines.push('MIME-Version: 1.0');\n lines.push(`Content-Type: multipart/mixed; boundary=\"${mixedBoundary}\"`);\n lines.push('');\n\n // ── Body part ────────────────────────────────────────────────────────────\n if (options.html && options.text) {\n // Both formats: wrap in multipart/alternative\n lines.push(`--${mixedBoundary}`);\n lines.push(\n `Content-Type: multipart/alternative; boundary=\"${altBoundary}\"`,\n );\n lines.push('');\n\n lines.push(`--${altBoundary}`);\n lines.push('Content-Type: text/plain; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.text);\n lines.push('');\n\n lines.push(`--${altBoundary}`);\n lines.push('Content-Type: text/html; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.html);\n lines.push('');\n\n lines.push(`--${altBoundary}--`);\n lines.push('');\n } else if (options.html) {\n lines.push(`--${mixedBoundary}`);\n lines.push('Content-Type: text/html; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.html);\n lines.push('');\n } else if (options.text) {\n lines.push(`--${mixedBoundary}`);\n lines.push('Content-Type: text/plain; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.text);\n lines.push('');\n }\n\n // ── Attachment parts ─────────────────────────────────────────────────────\n for (const attachment of options.attachments) {\n const encoded = encodeAttachment(attachment);\n const safeName = attachment.filename.replace(/\"/g, '');\n\n lines.push(`--${mixedBoundary}`);\n lines.push(`Content-Type: ${attachment.contentType}; name=\"${safeName}\"`);\n lines.push('Content-Transfer-Encoding: base64');\n lines.push(`Content-Disposition: attachment; filename=\"${safeName}\"`);\n lines.push('');\n // RFC 2045: base64 lines must not exceed 76 characters\n lines.push(encoded.match(/.{1,76}/g)?.join('\\r\\n') ?? encoded);\n lines.push('');\n }\n\n lines.push(`--${mixedBoundary}--`);\n\n return lines.join('\\r\\n');\n}\n\n/**\n * Encodes an attachment's content as a base64 string.\n *\n * @param attachment - The attachment to encode.\n * @returns Base64-encoded string.\n *\n * @internal\n */\nfunction encodeAttachment(attachment: EmailAttachment): string {\n if (Buffer.isBuffer(attachment.content)) {\n return attachment.content.toString('base64');\n }\n return Buffer.from(attachment.content).toString('base64');\n}\n\n/**\n * Core adapter class that wraps the AWS SES client and provides a simplified\n * API for sending emails.\n *\n * This class is not meant to be instantiated directly by consumers.\n * Use the singleton functions exported from the main module instead.\n *\n * @see {@link init} to initialize the singleton.\n * @see {@link sendEmail} to send a standard email.\n * @see {@link sendRawEmail} to send a raw MIME email.\n */\nexport class SesAdapter {\n /** @internal */\n private readonly sesClient: SESClient;\n\n /** @internal */\n private readonly config: ResolvedSesAdapterConfig;\n\n /**\n * Creates a new SesAdapter instance.\n *\n * @param userConfig - Optional configuration. Missing fields fall back to environment variables.\n * @throws {SesConfigError} When required configuration fields are missing.\n */\n constructor(userConfig: SesAdapterConfig = {}) {\n this.config = resolveConfig(userConfig);\n this.sesClient = buildSesClient(this.config);\n }\n\n /**\n * Validates that at least one body content field (`html` or `text`) is provided.\n *\n * @param options - Object containing optional `html` and `text` fields.\n * @throws {SesValidationError} When neither field is present.\n *\n * @internal\n */\n private validateBodyOptions(options: { html?: string; text?: string }): void {\n if (!options.html && !options.text) {\n throw new SesValidationError(\n 'At least one of \"html\" or \"text\" must be provided.',\n );\n }\n }\n\n /**\n * Resolves the sender address from the per-call option or the adapter default.\n *\n * @param from - The per-call \"from\" address, if provided.\n * @returns The resolved sender address.\n * @throws {SesValidationError} When no sender address can be determined.\n *\n * @internal\n */\n private resolveFrom(from?: string): string {\n const resolved = from ?? this.config.defaultFrom;\n if (!resolved) {\n throw new SesValidationError(\n 'A sender address is required. Provide \"from\" in the options or set \"defaultFrom\" during init().',\n );\n }\n return resolved;\n }\n\n /**\n * Dispatches a raw MIME message via `SendRawEmailCommand`.\n *\n * @param rawMessage - The raw MIME string to send.\n * @param errorContext - Human-readable context prepended to error messages.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @internal\n */\n private async dispatchRawEmail(\n rawMessage: string,\n errorContext: string,\n ): Promise<SendEmailResult> {\n const params: SendRawEmailCommandInput = {\n RawMessage: {\n Data: Buffer.from(rawMessage),\n },\n };\n\n try {\n const command = new SendRawEmailCommand(params);\n const response = await this.sesClient.send(command);\n\n return {\n success: true,\n messageId: response.MessageId,\n };\n } catch (error) {\n throw new SesSendError(\n `${errorContext}: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n /**\n * Sends an email using AWS SES.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * If `options.from` is not specified, the `defaultFrom` configured during\n * initialization is used. If neither is available, the call throws.\n *\n * @param options - Email sending options.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When required email fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const result = await adapter.sendEmail({\n * to: ['alice@example.com', 'bob@example.com'],\n * subject: 'Hello!',\n * html: '<p>Hello World</p>',\n * text: 'Hello World',\n * cc: 'manager@example.com',\n * });\n * ```\n */\n async sendEmail(options: SendEmailOptions): Promise<SendEmailResult> {\n this.validateBodyOptions(options);\n\n const from = this.resolveFrom(options.from);\n\n const toAddresses = toArray(options.to);\n\n const params: SendEmailCommandInput = {\n Source: from,\n Destination: {\n ToAddresses: toAddresses,\n CcAddresses: options.cc ? toArray(options.cc) : undefined,\n BccAddresses: options.bcc ? toArray(options.bcc) : undefined,\n },\n Message: {\n Subject: {\n Data: options.subject,\n Charset: 'UTF-8',\n },\n Body: {\n Html: options.html\n ? { Data: options.html, Charset: 'UTF-8' }\n : undefined,\n Text: options.text\n ? { Data: options.text, Charset: 'UTF-8' }\n : undefined,\n },\n },\n ReplyToAddresses: options.replyTo ? toArray(options.replyTo) : undefined,\n };\n\n try {\n const command = new SendEmailCommand(params);\n const response = await this.sesClient.send(command);\n\n return {\n success: true,\n messageId: response.MessageId,\n };\n } catch (error) {\n throw new SesSendError(\n `Failed to send email to ${toAddresses.join(', ')}: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n /**\n * Sends a raw MIME email using AWS SES.\n *\n * Use this method when you need full control over the email format, such as\n * sending emails with attachments or custom headers.\n *\n * @param rawMessage - The raw MIME email message as a string.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When the raw message is empty.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const mimeMessage = [\n * 'From: sender@example.com',\n * 'To: recipient@example.com',\n * 'Subject: Test',\n * 'MIME-Version: 1.0',\n * 'Content-Type: text/plain',\n * '',\n * 'Hello World',\n * ].join('\\r\\n');\n *\n * const result = await adapter.sendRawEmail(mimeMessage);\n * ```\n */\n async sendRawEmail(rawMessage: string): Promise<SendEmailResult> {\n if (!rawMessage || rawMessage.trim().length === 0) {\n throw new SesValidationError('rawMessage cannot be empty.');\n }\n\n return this.dispatchRawEmail(rawMessage, 'Failed to send raw email');\n }\n\n /**\n * Sends an email with one or more file attachments using AWS SES.\n *\n * Internally builds a multipart/mixed MIME message and dispatches it via\n * `SendRawEmailCommand`, giving you full attachment support without needing\n * to craft raw MIME by hand.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * The `options.attachments` array must contain at least one item.\n *\n * @param options - Email options including the attachments array.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When required fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const result = await adapter.sendEmailWithAttachments({\n * to: 'alice@example.com',\n * subject: 'Your invoice',\n * html: '<p>Please find the invoice attached.</p>',\n * attachments: [\n * {\n * filename: 'invoice.pdf',\n * content: fs.readFileSync('./invoice.pdf'),\n * contentType: 'application/pdf',\n * },\n * ],\n * });\n * ```\n */\n async sendEmailWithAttachments(\n options: SendEmailWithAttachmentsOptions,\n ): Promise<SendEmailResult> {\n this.validateBodyOptions(options);\n\n if (!options.attachments || options.attachments.length === 0) {\n throw new SesValidationError(\n 'At least one attachment must be provided in SendEmailWithAttachmentsOptions.attachments.',\n );\n }\n\n const from = this.resolveFrom(options.from);\n const toAddresses = toArray(options.to);\n const rawMessage = buildMimeMessage(options, from);\n\n return this.dispatchRawEmail(\n rawMessage,\n `Failed to send email with attachments to ${toAddresses.join(', ')}`,\n );\n }\n\n /**\n * Returns whether the adapter has a default \"From\" address configured.\n *\n * @returns `true` if a default from address is available.\n *\n * @example\n * ```ts\n * if (adapter.hasDefaultFrom()) {\n * console.log('Default from:', adapter.getDefaultFrom());\n * }\n * ```\n */\n hasDefaultFrom(): boolean {\n return !!this.config.defaultFrom;\n }\n\n /**\n * Returns the default \"From\" email address, or `undefined` if not set.\n *\n * @returns The default sender address, or `undefined`.\n *\n * @example\n * ```ts\n * const from = adapter.getDefaultFrom();\n * // => 'noreply@example.com' or undefined\n * ```\n */\n getDefaultFrom(): string | undefined {\n return this.config.defaultFrom;\n }\n\n /**\n * Returns the AWS region the adapter is configured to use.\n *\n * @returns The AWS region string.\n *\n * @example\n * ```ts\n * console.log(adapter.getRegion()); // => 'us-east-1'\n * ```\n */\n getRegion(): string {\n return this.config.region;\n }\n}\n","/**\n * @module aws-ses-adapter\n *\n * A lightweight adapter that simplifies the AWS SES email sending API for Node.js.\n *\n * ## Quick Start\n *\n * **1. Initialize once** (at application startup):\n * ```ts\n * import { init } from 'aws-ses-adapter';\n *\n * init({\n * region: 'us-east-1',\n * accessKeyId: 'YOUR_KEY',\n * secretAccessKey: 'YOUR_SECRET',\n * defaultFrom: 'noreply@example.com',\n * });\n * ```\n *\n * **2. Use anywhere** in your application:\n * ```ts\n * import { sendEmail } from 'aws-ses-adapter';\n *\n * await sendEmail({\n * to: 'user@example.com',\n * subject: 'Welcome!',\n * html: '<h1>Welcome</h1>',\n * });\n * ```\n *\n * ## Environment Variables\n *\n * If credentials are not passed to `init()`, the adapter reads:\n * - `AWS_SES_REGION`\n * - `AWS_ACCESS_KEY_ID`\n * - `AWS_SECRET_ACCESS_KEY`\n * - `AWS_SES_FROM_EMAIL` (optional default sender)\n */\n\nimport { SesAdapter } from './adapter';\nimport { SesNotInitializedError } from './errors';\nimport type {\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n\n// ─── Singleton state ──────────────────────────────────────────────────────────\n\n/** @internal */\nlet _instance: SesAdapter | null = null;\n\n/**\n * Returns the current singleton instance.\n * @throws {SesNotInitializedError} If `init()` has not been called yet.\n * @internal\n */\nfunction getInstance(): SesAdapter {\n if (!_instance) {\n throw new SesNotInitializedError();\n }\n return _instance;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Initializes the SES Adapter singleton.\n *\n * Must be called **once** before any other function in this module.\n * Calling `init()` again will replace the existing singleton instance,\n * which is useful for reconfiguration during testing.\n *\n * Credentials are resolved in the following order:\n * 1. Values provided in the `config` argument.\n * 2. Environment variables (`AWS_SES_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`).\n *\n * @param config - Optional configuration. Omit any field to fall back to environment variables.\n * @throws {SesConfigError} When required credentials cannot be resolved.\n *\n * @example\n * ```ts\n * // Using explicit credentials\n * init({\n * region: 'us-east-1',\n * accessKeyId: process.env.MY_KEY_ID,\n * secretAccessKey: process.env.MY_SECRET,\n * defaultFrom: 'no-reply@myapp.com',\n * });\n *\n * // Relying entirely on environment variables\n * init();\n * ```\n */\nexport function init(config: SesAdapterConfig = {}): void {\n _instance = new SesAdapter(config);\n}\n\n/**\n * Sends an email using the initialized SES Adapter.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * If `options.from` is not specified, the `defaultFrom` set during {@link init} is used.\n *\n * @param options - Email sending options.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When required email fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendEmail } from 'aws-ses-adapter';\n *\n * const result = await sendEmail({\n * to: 'alice@example.com',\n * subject: 'Hello Alice',\n * html: '<p>Hi Alice!</p>',\n * text: 'Hi Alice!',\n * cc: 'manager@example.com',\n * bcc: ['audit@example.com'],\n * replyTo: 'support@example.com',\n * });\n *\n * console.log(result.messageId);\n * ```\n */\nexport async function sendEmail(\n options: SendEmailOptions,\n): Promise<SendEmailResult> {\n return getInstance().sendEmail(options);\n}\n\n/**\n * Sends an email with one or more file attachments using the initialized SES Adapter.\n *\n * Internally builds a multipart/mixed MIME message, so you don't need to\n * construct raw MIME yourself. At least one of `options.html` or `options.text`\n * must be provided, and `options.attachments` must contain at least one item.\n *\n * @param options - Email options including the attachments array.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When required fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendEmailWithAttachments } from 'aws-ses-adapter';\n * import { readFileSync } from 'fs';\n *\n * const result = await sendEmailWithAttachments({\n * to: 'alice@example.com',\n * subject: 'Your invoice',\n * html: '<p>Please find the invoice attached.</p>',\n * attachments: [\n * {\n * filename: 'invoice.pdf',\n * content: readFileSync('./invoice.pdf'),\n * contentType: 'application/pdf',\n * },\n * ],\n * });\n *\n * console.log(result.messageId);\n * ```\n */\nexport async function sendEmailWithAttachments(\n options: SendEmailWithAttachmentsOptions,\n): Promise<SendEmailResult> {\n return getInstance().sendEmailWithAttachments(options);\n}\n\n/**\n * Sends a raw MIME email using the initialized SES Adapter.\n *\n * Use this when you need full control over the email, such as including\n * attachments or custom MIME headers.\n *\n * @param rawMessage - The raw MIME email message as a string.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When the raw message is empty.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendRawEmail } from 'aws-ses-adapter';\n *\n * const mime = [\n * 'From: sender@example.com',\n * 'To: recipient@example.com',\n * 'Subject: File attached',\n * 'MIME-Version: 1.0',\n * 'Content-Type: text/plain',\n * '',\n * 'Please find the attachment.',\n * ].join('\\r\\n');\n *\n * const result = await sendRawEmail(mime);\n * ```\n */\nexport async function sendRawEmail(\n rawMessage: string,\n): Promise<SendEmailResult> {\n return getInstance().sendRawEmail(rawMessage);\n}\n\n/**\n * Returns whether the singleton has been initialized via {@link init}.\n *\n * @returns `true` if `init()` has been called successfully.\n *\n * @example\n * ```ts\n * import { isInitialized } from 'aws-ses-adapter';\n *\n * if (!isInitialized()) {\n * init();\n * }\n * ```\n */\nexport function isInitialized(): boolean {\n return _instance !== null;\n}\n\n/**\n * Returns whether a default \"From\" address is configured in the singleton.\n *\n * @returns `true` if a default sender address is set.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { hasDefaultFrom } from 'aws-ses-adapter';\n *\n * console.log(hasDefaultFrom()); // => true\n * ```\n */\nexport function hasDefaultFrom(): boolean {\n return getInstance().hasDefaultFrom();\n}\n\n/**\n * Returns the default \"From\" email address configured in the singleton,\n * or `undefined` if none was set.\n *\n * @returns The default sender address, or `undefined`.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { getDefaultFrom } from 'aws-ses-adapter';\n *\n * const from = getDefaultFrom();\n * // => 'noreply@example.com' or undefined\n * ```\n */\nexport function getDefaultFrom(): string | undefined {\n return getInstance().getDefaultFrom();\n}\n\n/**\n * Returns the AWS region the singleton is configured to use.\n *\n * @returns The AWS region string (e.g. `'us-east-1'`).\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { getRegion } from 'aws-ses-adapter';\n *\n * console.log(getRegion()); // => 'us-east-1'\n * ```\n */\nexport function getRegion(): string {\n return getInstance().getRegion();\n}\n\n// ─── Re-exports ───────────────────────────────────────────────────────────────\n\n/**\n * Re-export the core adapter class for advanced use cases such as\n * creating multiple independent instances or building framework integrations\n * (e.g., a NestJS module).\n */\nexport { SesAdapter } from './adapter';\n\n/** Re-export all custom error classes for consumer-side `instanceof` checks. */\nexport {\n SesConfigError,\n SesNotInitializedError,\n SesSendError,\n SesValidationError,\n} from './errors';\n\n/** Re-export all public types and interfaces. */\nexport type {\n EmailAttachment,\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n"]}
1
+ {"version":3,"sources":["../src/client.ts","../src/errors.ts","../src/adapter.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * @module client\n * Factory and utilities for building an AWS SESClient instance\n * from a resolved adapter configuration.\n */\n\nimport { SESClient } from '@aws-sdk/client-ses';\nimport type { ResolvedSesAdapterConfig } from './types';\n\n/**\n * Creates and returns a configured {@link SESClient} instance using the\n * resolved adapter configuration.\n *\n * @param config - The resolved configuration containing region and credentials.\n * @returns A ready-to-use AWS SESClient.\n *\n * @example\n * ```ts\n * const client = buildSesClient({\n * region: 'us-east-1',\n * accessKeyId: 'AKIAIOSFODNN7EXAMPLE',\n * secretAccessKey: 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY',\n * });\n * ```\n *\n * @internal\n */\nexport function buildSesClient(config: ResolvedSesAdapterConfig): SESClient {\n return new SESClient({\n region: config.region,\n credentials: {\n accessKeyId: config.accessKeyId,\n secretAccessKey: config.secretAccessKey,\n },\n });\n}\n","/**\n * @module errors\n * Custom error classes for the aws-ses-adapter library.\n * All errors extend the native `Error` class and include a `name` property\n * suitable for programmatic error identification.\n */\n\n/**\n * Thrown when an operation is attempted before the adapter has been initialized\n * via {@link init}.\n *\n * @example\n * ```ts\n * import { sendEmail } from '@fmontoya/aws-ses-adapter';\n *\n * // Calling sendEmail before init() throws this error\n * try {\n * await sendEmail({ to: 'user@example.com', subject: 'Hi', text: 'Hello' });\n * } catch (err) {\n * if (err instanceof SesNotInitializedError) {\n * console.error('Call init() first');\n * }\n * }\n * ```\n */\nexport class SesNotInitializedError extends Error {\n /** @override */\n readonly name = 'SesNotInitializedError' as const;\n\n constructor() {\n super(\n 'SES Adapter has not been initialized. Call init() before using any adapter methods.',\n );\n }\n}\n\n/**\n * Thrown when the configuration provided to {@link init} is invalid or incomplete.\n * This typically happens when required credentials are missing from both the config\n * object and the environment variables.\n *\n * @example\n * ```ts\n * import { init } from '@fmontoya/aws-ses-adapter';\n *\n * try {\n * init({}); // No region, no env vars set\n * } catch (err) {\n * if (err instanceof SesConfigError) {\n * console.error('Config error:', err.message);\n * }\n * }\n * ```\n */\nexport class SesConfigError extends Error {\n /** @override */\n readonly name = 'SesConfigError' as const;\n\n /**\n * @param message - Description of the configuration issue.\n */\n constructor(message: string) {\n super(message);\n }\n}\n\n/**\n * Thrown when an email send operation fails at the AWS SES level.\n * The original AWS error is available via the `cause` property (ES2022+).\n *\n * @example\n * ```ts\n * import { sendEmail, SesSendError } from '@fmontoya/aws-ses-adapter';\n *\n * try {\n * await sendEmail({ to: 'user@example.com', subject: 'Hi', text: 'Hello' });\n * } catch (err) {\n * if (err instanceof SesSendError) {\n * console.error('Send failed:', err.message);\n * console.error('Original cause:', err.cause);\n * }\n * }\n * ```\n */\nexport class SesSendError extends Error {\n /** @override */\n readonly name = 'SesSendError' as const;\n\n /**\n * @param message - Description of the send failure.\n * @param cause - The underlying error from AWS SDK.\n */\n constructor(message: string, cause?: unknown) {\n super(message, { cause });\n }\n}\n\n/**\n * Thrown when {@link SendEmailOptions} are invalid, such as missing both\n * `html` and `text`, or having an invalid email address format.\n *\n * @example\n * ```ts\n * import { sendEmail, SesValidationError } from '@fmontoya/aws-ses-adapter';\n *\n * try {\n * // Missing both html and text\n * await sendEmail({ to: 'user@example.com', subject: 'Hi' });\n * } catch (err) {\n * if (err instanceof SesValidationError) {\n * console.error('Validation error:', err.message);\n * }\n * }\n * ```\n */\nexport class SesValidationError extends Error {\n /** @override */\n readonly name = 'SesValidationError' as const;\n\n /**\n * @param message - Description of the validation failure.\n */\n constructor(message: string) {\n super(message);\n }\n}\n","/**\n * @module adapter\n * Core SesAdapter class that wraps the AWS SES client and provides\n * simplified email sending methods.\n */\n\nimport type {\n SendEmailCommandInput,\n SendRawEmailCommandInput,\n SESClient,\n} from '@aws-sdk/client-ses';\nimport { SendEmailCommand, SendRawEmailCommand } from '@aws-sdk/client-ses';\nimport { buildSesClient } from './client';\nimport { SesConfigError, SesSendError, SesValidationError } from './errors';\nimport type {\n EmailAttachment,\n ResolvedSesAdapterConfig,\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n\n/**\n * Resolves the adapter configuration by merging user-provided values with\n * environment variable fallbacks.\n *\n * @param config - Optional user-provided configuration.\n * @returns The resolved configuration with all required fields populated.\n * @throws {SesConfigError} When required fields cannot be resolved from config or env vars.\n *\n * @internal\n */\nfunction resolveConfig(\n config: SesAdapterConfig = {},\n): ResolvedSesAdapterConfig {\n const region = config.region ?? process.env['AWS_SES_REGION'];\n const accessKeyId =\n config.credentials?.accessKeyId ??\n config.accessKeyId ??\n process.env['AWS_ACCESS_KEY_ID'];\n const secretAccessKey =\n config.credentials?.secretAccessKey ??\n config.secretAccessKey ??\n process.env['AWS_SECRET_ACCESS_KEY'];\n const defaultFrom = config.defaultFrom ?? process.env['AWS_SES_FROM_EMAIL'];\n\n if (!region) {\n throw new SesConfigError(\n 'AWS SES region is required. Provide it via config.region or the AWS_SES_REGION environment variable.',\n );\n }\n if (!accessKeyId) {\n throw new SesConfigError(\n 'AWS access key ID is required. Provide it via config.credentials.accessKeyId, or the AWS_ACCESS_KEY_ID environment variable.',\n );\n }\n if (!secretAccessKey) {\n throw new SesConfigError(\n 'AWS secret access key is required. Provide it via config.credentials.secretAccessKey, or the AWS_SECRET_ACCESS_KEY environment variable.',\n );\n }\n\n return { region, accessKeyId, secretAccessKey, defaultFrom };\n}\n\n/**\n * Normalizes a value that can be a single string or an array of strings\n * into a guaranteed array.\n *\n * @param value - A string or array of strings.\n * @returns An array of strings.\n *\n * @internal\n */\nfunction toArray(value: string | string[]): string[] {\n return Array.isArray(value) ? value : [value];\n}\n\n/**\n * Encodes a MIME header value using RFC 2047 Base64 encoded-word syntax\n * when the value contains non-ASCII characters.\n *\n * @param value - The raw header value (e.g. a subject line).\n * @returns The value as-is if it is pure ASCII, otherwise `=?UTF-8?B?...?=`.\n *\n * @internal\n */\nfunction encodeHeaderValue(value: string): string {\n // If no non-ASCII characters are present, return as-is\n if (!/[\\u0080-\\uFFFF]/.test(value)) return value;\n return `=?UTF-8?B?${Buffer.from(value, 'utf-8').toString('base64')}?=`;\n}\n\n/**\n * Builds a multipart/mixed MIME email message ready to be sent via\n * `SendRawEmailCommand`.\n *\n * The body section uses multipart/alternative when both `html` and `text` are\n * supplied so that email clients can choose the best representation.\n *\n * @internal\n */\nfunction buildMimeMessage(\n options: SendEmailWithAttachmentsOptions,\n from: string,\n): string {\n const mixedBoundary = `----=_Mixed_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const altBoundary = `----=_Alt_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n\n const lines: string[] = [];\n\n // RFC 2822 headers\n lines.push(`From: ${from}`);\n lines.push(`To: ${toArray(options.to).join(', ')}`);\n if (options.cc) lines.push(`Cc: ${toArray(options.cc).join(', ')}`);\n if (options.bcc) lines.push(`Bcc: ${toArray(options.bcc).join(', ')}`);\n if (options.replyTo)\n lines.push(`Reply-To: ${toArray(options.replyTo).join(', ')}`);\n lines.push(`Subject: ${encodeHeaderValue(options.subject)}`);\n lines.push('MIME-Version: 1.0');\n lines.push(`Content-Type: multipart/mixed; boundary=\"${mixedBoundary}\"`);\n lines.push('');\n\n // ── Body part ────────────────────────────────────────────────────────────\n if (options.html && options.text) {\n // Both formats: wrap in multipart/alternative\n lines.push(`--${mixedBoundary}`);\n lines.push(\n `Content-Type: multipart/alternative; boundary=\"${altBoundary}\"`,\n );\n lines.push('');\n\n lines.push(`--${altBoundary}`);\n lines.push('Content-Type: text/plain; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.text);\n lines.push('');\n\n lines.push(`--${altBoundary}`);\n lines.push('Content-Type: text/html; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.html);\n lines.push('');\n\n lines.push(`--${altBoundary}--`);\n lines.push('');\n } else if (options.html) {\n lines.push(`--${mixedBoundary}`);\n lines.push('Content-Type: text/html; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.html);\n lines.push('');\n } else if (options.text) {\n lines.push(`--${mixedBoundary}`);\n lines.push('Content-Type: text/plain; charset=UTF-8');\n lines.push('Content-Transfer-Encoding: 8bit');\n lines.push('');\n lines.push(options.text);\n lines.push('');\n }\n\n // ── Attachment parts ─────────────────────────────────────────────────────\n for (const attachment of options.attachments) {\n const encoded = encodeAttachment(attachment);\n const safeName = attachment.filename.replace(/\"/g, '');\n\n lines.push(`--${mixedBoundary}`);\n lines.push(`Content-Type: ${attachment.contentType}; name=\"${safeName}\"`);\n lines.push('Content-Transfer-Encoding: base64');\n lines.push(`Content-Disposition: attachment; filename=\"${safeName}\"`);\n lines.push('');\n // RFC 2045: base64 lines must not exceed 76 characters\n lines.push(encoded.match(/.{1,76}/g)?.join('\\r\\n') ?? encoded);\n lines.push('');\n }\n\n lines.push(`--${mixedBoundary}--`);\n\n return lines.join('\\r\\n');\n}\n\n/**\n * Encodes an attachment's content as a base64 string.\n *\n * @param attachment - The attachment to encode.\n * @returns Base64-encoded string.\n *\n * @internal\n */\nfunction encodeAttachment(attachment: EmailAttachment): string {\n if (Buffer.isBuffer(attachment.content)) {\n return attachment.content.toString('base64');\n }\n return Buffer.from(attachment.content).toString('base64');\n}\n\n/**\n * Core adapter class that wraps the AWS SES client and provides a simplified\n * API for sending emails.\n *\n * This class is not meant to be instantiated directly by consumers.\n * Use the singleton functions exported from the main module instead.\n *\n * @see {@link init} to initialize the singleton.\n * @see {@link sendEmail} to send a standard email.\n * @see {@link sendRawEmail} to send a raw MIME email.\n */\nexport class SesAdapter {\n /** @internal */\n private readonly sesClient: SESClient;\n\n /** @internal */\n private readonly config: ResolvedSesAdapterConfig;\n\n /**\n * Creates a new SesAdapter instance.\n *\n * @param userConfig - Optional configuration. Missing fields fall back to environment variables.\n * @throws {SesConfigError} When required configuration fields are missing.\n */\n constructor(userConfig: SesAdapterConfig = {}) {\n this.config = resolveConfig(userConfig);\n this.sesClient = buildSesClient(this.config);\n }\n\n /**\n * Validates that at least one body content field (`html` or `text`) is provided.\n *\n * @param options - Object containing optional `html` and `text` fields.\n * @throws {SesValidationError} When neither field is present.\n *\n * @internal\n */\n private validateBodyOptions(options: { html?: string; text?: string }): void {\n if (!options.html && !options.text) {\n throw new SesValidationError(\n 'At least one of \"html\" or \"text\" must be provided.',\n );\n }\n }\n\n /**\n * Resolves the sender address from the per-call option or the adapter default.\n *\n * @param from - The per-call \"from\" address, if provided.\n * @returns The resolved sender address.\n * @throws {SesValidationError} When no sender address can be determined.\n *\n * @internal\n */\n private resolveFrom(from?: string): string {\n const resolved = from ?? this.config.defaultFrom;\n if (!resolved) {\n throw new SesValidationError(\n 'A sender address is required. Provide \"from\" in the options or set \"defaultFrom\" during init().',\n );\n }\n return resolved;\n }\n\n /**\n * Dispatches a raw MIME message via `SendRawEmailCommand`.\n *\n * @param rawMessage - The raw MIME string to send.\n * @param errorContext - Human-readable context prepended to error messages.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @internal\n */\n private async dispatchRawEmail(\n rawMessage: string,\n errorContext: string,\n ): Promise<SendEmailResult> {\n const params: SendRawEmailCommandInput = {\n RawMessage: {\n Data: Buffer.from(rawMessage),\n },\n };\n\n try {\n const command = new SendRawEmailCommand(params);\n const response = await this.sesClient.send(command);\n\n return {\n success: true,\n messageId: response.MessageId,\n };\n } catch (error) {\n throw new SesSendError(\n `${errorContext}: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n /**\n * Sends an email using AWS SES.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * If `options.from` is not specified, the `defaultFrom` configured during\n * initialization is used. If neither is available, the call throws.\n *\n * @param options - Email sending options.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When required email fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const result = await adapter.sendEmail({\n * to: ['alice@example.com', 'bob@example.com'],\n * subject: 'Hello!',\n * html: '<p>Hello World</p>',\n * text: 'Hello World',\n * cc: 'manager@example.com',\n * });\n * ```\n */\n async sendEmail(options: SendEmailOptions): Promise<SendEmailResult> {\n this.validateBodyOptions(options);\n\n const from = this.resolveFrom(options.from);\n\n const toAddresses = toArray(options.to);\n\n const params: SendEmailCommandInput = {\n Source: from,\n Destination: {\n ToAddresses: toAddresses,\n CcAddresses: options.cc ? toArray(options.cc) : undefined,\n BccAddresses: options.bcc ? toArray(options.bcc) : undefined,\n },\n Message: {\n Subject: {\n Data: options.subject,\n Charset: 'UTF-8',\n },\n Body: {\n Html: options.html\n ? { Data: options.html, Charset: 'UTF-8' }\n : undefined,\n Text: options.text\n ? { Data: options.text, Charset: 'UTF-8' }\n : undefined,\n },\n },\n ReplyToAddresses: options.replyTo ? toArray(options.replyTo) : undefined,\n };\n\n try {\n const command = new SendEmailCommand(params);\n const response = await this.sesClient.send(command);\n\n return {\n success: true,\n messageId: response.MessageId,\n };\n } catch (error) {\n throw new SesSendError(\n `Failed to send email to ${toAddresses.join(', ')}: ${error instanceof Error ? error.message : String(error)}`,\n error,\n );\n }\n }\n\n /**\n * Sends a raw MIME email using AWS SES.\n *\n * Use this method when you need full control over the email format, such as\n * sending emails with attachments or custom headers.\n *\n * @param rawMessage - The raw MIME email message as a string.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When the raw message is empty.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const mimeMessage = [\n * 'From: sender@example.com',\n * 'To: recipient@example.com',\n * 'Subject: Test',\n * 'MIME-Version: 1.0',\n * 'Content-Type: text/plain',\n * '',\n * 'Hello World',\n * ].join('\\r\\n');\n *\n * const result = await adapter.sendRawEmail(mimeMessage);\n * ```\n */\n async sendRawEmail(rawMessage: string): Promise<SendEmailResult> {\n if (!rawMessage || rawMessage.trim().length === 0) {\n throw new SesValidationError('rawMessage cannot be empty.');\n }\n\n return this.dispatchRawEmail(rawMessage, 'Failed to send raw email');\n }\n\n /**\n * Sends an email with one or more file attachments using AWS SES.\n *\n * Internally builds a multipart/mixed MIME message and dispatches it via\n * `SendRawEmailCommand`, giving you full attachment support without needing\n * to craft raw MIME by hand.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * The `options.attachments` array must contain at least one item.\n *\n * @param options - Email options including the attachments array.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesValidationError} When required fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * const result = await adapter.sendEmailWithAttachments({\n * to: 'alice@example.com',\n * subject: 'Your invoice',\n * html: '<p>Please find the invoice attached.</p>',\n * attachments: [\n * {\n * filename: 'invoice.pdf',\n * content: fs.readFileSync('./invoice.pdf'),\n * contentType: 'application/pdf',\n * },\n * ],\n * });\n * ```\n */\n async sendEmailWithAttachments(\n options: SendEmailWithAttachmentsOptions,\n ): Promise<SendEmailResult> {\n this.validateBodyOptions(options);\n\n if (!options.attachments || options.attachments.length === 0) {\n throw new SesValidationError(\n 'At least one attachment must be provided in SendEmailWithAttachmentsOptions.attachments.',\n );\n }\n\n const from = this.resolveFrom(options.from);\n const toAddresses = toArray(options.to);\n const rawMessage = buildMimeMessage(options, from);\n\n return this.dispatchRawEmail(\n rawMessage,\n `Failed to send email with attachments to ${toAddresses.join(', ')}`,\n );\n }\n\n /**\n * Returns whether the adapter has a default \"From\" address configured.\n *\n * @returns `true` if a default from address is available.\n *\n * @example\n * ```ts\n * if (adapter.hasDefaultFrom()) {\n * console.log('Default from:', adapter.getDefaultFrom());\n * }\n * ```\n */\n hasDefaultFrom(): boolean {\n return !!this.config.defaultFrom;\n }\n\n /**\n * Returns the default \"From\" email address, or `undefined` if not set.\n *\n * @returns The default sender address, or `undefined`.\n *\n * @example\n * ```ts\n * const from = adapter.getDefaultFrom();\n * // => 'noreply@example.com' or undefined\n * ```\n */\n getDefaultFrom(): string | undefined {\n return this.config.defaultFrom;\n }\n\n /**\n * Returns the AWS region the adapter is configured to use.\n *\n * @returns The AWS region string.\n *\n * @example\n * ```ts\n * console.log(adapter.getRegion()); // => 'us-east-1'\n * ```\n */\n getRegion(): string {\n return this.config.region;\n }\n}\n","/**\n * @module aws-ses-adapter\n *\n * A lightweight adapter that simplifies the AWS SES email sending API for Node.js.\n *\n * ## Quick Start\n *\n * **1. Initialize once** (at application startup):\n * ```ts\n * import { init } from '@fmontoya/aws-ses-adapter';\n *\n * init({\n * region: 'us-east-1',\n * credentials: {\n * accessKeyId: 'YOUR_KEY',\n * secretAccessKey: 'YOUR_SECRET',\n * },\n * defaultFrom: 'noreply@example.com',\n * });\n * ```\n *\n * **2. Use anywhere** in your application:\n * ```ts\n * import { sendEmail } from '@fmontoya/aws-ses-adapter';\n *\n * await sendEmail({\n * to: 'user@example.com',\n * subject: 'Welcome!',\n * html: '<h1>Welcome</h1>',\n * });\n * ```\n *\n * ## Environment Variables\n *\n * If credentials are not passed to `init()`, the adapter reads:\n * - `AWS_SES_REGION`\n * - `AWS_ACCESS_KEY_ID`\n * - `AWS_SECRET_ACCESS_KEY`\n * - `AWS_SES_FROM_EMAIL` (optional default sender)\n */\n\nimport { SesAdapter } from './adapter';\nimport { SesNotInitializedError } from './errors';\nimport type {\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n\n// ─── Singleton state ──────────────────────────────────────────────────────────\n\n/** @internal */\nlet _instance: SesAdapter | null = null;\n\n/**\n * Returns the current singleton instance.\n * @throws {SesNotInitializedError} If `init()` has not been called yet.\n * @internal\n */\nfunction getInstance(): SesAdapter {\n if (!_instance) {\n throw new SesNotInitializedError();\n }\n return _instance;\n}\n\n// ─── Public API ───────────────────────────────────────────────────────────────\n\n/**\n * Initializes the SES Adapter singleton.\n *\n * Must be called **once** before any other function in this module.\n * Calling `init()` again will replace the existing singleton instance,\n * which is useful for reconfiguration during testing.\n *\n * Credentials are resolved in the following order:\n * 1. `config.credentials.accessKeyId` / `config.credentials.secretAccessKey`\n * 2. `config.accessKeyId` / `config.secretAccessKey` _(deprecated)_\n * 3. Environment variables (`AWS_SES_REGION`, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)\n *\n * @param config - Optional configuration. Omit any field to fall back to environment variables.\n * @throws {SesConfigError} When required credentials cannot be resolved.\n *\n * @example\n * ```ts\n * // Using explicit credentials\n * init({\n * region: 'us-east-1',\n * credentials: {\n * accessKeyId: process.env.MY_KEY_ID,\n * secretAccessKey: process.env.MY_SECRET,\n * },\n * defaultFrom: 'no-reply@myapp.com',\n * });\n *\n * // Relying entirely on environment variables\n * init();\n * ```\n */\nexport function init(config: SesAdapterConfig = {}): void {\n _instance = new SesAdapter(config);\n}\n\n/**\n * Sends an email using the initialized SES Adapter.\n *\n * At least one of `options.html` or `options.text` must be provided.\n * If `options.from` is not specified, the `defaultFrom` set during {@link init} is used.\n *\n * @param options - Email sending options.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When required email fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendEmail } from '@fmontoya/aws-ses-adapter';\n *\n * const result = await sendEmail({\n * to: 'alice@example.com',\n * subject: 'Hello Alice',\n * html: '<p>Hi Alice!</p>',\n * text: 'Hi Alice!',\n * cc: 'manager@example.com',\n * bcc: ['audit@example.com'],\n * replyTo: 'support@example.com',\n * });\n *\n * console.log(result.messageId);\n * ```\n */\nexport async function sendEmail(\n options: SendEmailOptions,\n): Promise<SendEmailResult> {\n return getInstance().sendEmail(options);\n}\n\n/**\n * Sends an email with one or more file attachments using the initialized SES Adapter.\n *\n * Internally builds a multipart/mixed MIME message, so you don't need to\n * construct raw MIME yourself. At least one of `options.html` or `options.text`\n * must be provided, and `options.attachments` must contain at least one item.\n *\n * @param options - Email options including the attachments array.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When required fields are missing or invalid.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendEmailWithAttachments } from '@fmontoya/aws-ses-adapter';\n * import { readFileSync } from 'fs';\n *\n * const result = await sendEmailWithAttachments({\n * to: 'alice@example.com',\n * subject: 'Your invoice',\n * html: '<p>Please find the invoice attached.</p>',\n * attachments: [\n * {\n * filename: 'invoice.pdf',\n * content: readFileSync('./invoice.pdf'),\n * contentType: 'application/pdf',\n * },\n * ],\n * });\n *\n * console.log(result.messageId);\n * ```\n */\nexport async function sendEmailWithAttachments(\n options: SendEmailWithAttachmentsOptions,\n): Promise<SendEmailResult> {\n return getInstance().sendEmailWithAttachments(options);\n}\n\n/**\n * Sends a raw MIME email using the initialized SES Adapter.\n *\n * Use this when you need full control over the email, such as including\n * attachments or custom MIME headers.\n *\n * @param rawMessage - The raw MIME email message as a string.\n * @returns A promise resolving to a {@link SendEmailResult}.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n * @throws {SesValidationError} When the raw message is empty.\n * @throws {SesSendError} When the AWS SES API call fails.\n *\n * @example\n * ```ts\n * import { sendRawEmail } from '@fmontoya/aws-ses-adapter';\n *\n * const mime = [\n * 'From: sender@example.com',\n * 'To: recipient@example.com',\n * 'Subject: File attached',\n * 'MIME-Version: 1.0',\n * 'Content-Type: text/plain',\n * '',\n * 'Please find the attachment.',\n * ].join('\\r\\n');\n *\n * const result = await sendRawEmail(mime);\n * ```\n */\nexport async function sendRawEmail(\n rawMessage: string,\n): Promise<SendEmailResult> {\n return getInstance().sendRawEmail(rawMessage);\n}\n\n/**\n * Returns whether the singleton has been initialized via {@link init}.\n *\n * @returns `true` if `init()` has been called successfully.\n *\n * @example\n * ```ts\n * import { isInitialized } from '@fmontoya/aws-ses-adapter';\n *\n * if (!isInitialized()) {\n * init();\n * }\n * ```\n */\nexport function isInitialized(): boolean {\n return _instance !== null;\n}\n\n/**\n * Returns whether a default \"From\" address is configured in the singleton.\n *\n * @returns `true` if a default sender address is set.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { hasDefaultFrom } from '@fmontoya/aws-ses-adapter';\n *\n * console.log(hasDefaultFrom()); // => true\n * ```\n */\nexport function hasDefaultFrom(): boolean {\n return getInstance().hasDefaultFrom();\n}\n\n/**\n * Returns the default \"From\" email address configured in the singleton,\n * or `undefined` if none was set.\n *\n * @returns The default sender address, or `undefined`.\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { getDefaultFrom } from '@fmontoya/aws-ses-adapter';\n *\n * const from = getDefaultFrom();\n * // => 'noreply@example.com' or undefined\n * ```\n */\nexport function getDefaultFrom(): string | undefined {\n return getInstance().getDefaultFrom();\n}\n\n/**\n * Returns the AWS region the singleton is configured to use.\n *\n * @returns The AWS region string (e.g. `'us-east-1'`).\n * @throws {SesNotInitializedError} If {@link init} has not been called.\n *\n * @example\n * ```ts\n * import { getRegion } from '@fmontoya/aws-ses-adapter';\n *\n * console.log(getRegion()); // => 'us-east-1'\n * ```\n */\nexport function getRegion(): string {\n return getInstance().getRegion();\n}\n\n// ─── Re-exports ───────────────────────────────────────────────────────────────\n\n/**\n * Re-export the core adapter class for advanced use cases such as\n * creating multiple independent instances or building framework integrations\n * (e.g., a NestJS module).\n */\nexport { SesAdapter } from './adapter';\n\n/** Re-export all custom error classes for consumer-side `instanceof` checks. */\nexport {\n SesConfigError,\n SesNotInitializedError,\n SesSendError,\n SesValidationError,\n} from './errors';\n\n/** Re-export all public types and interfaces. */\nexport type {\n EmailAttachment,\n SendEmailOptions,\n SendEmailResult,\n SendEmailWithAttachmentsOptions,\n SesAdapterConfig,\n} from './types';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fmontoya/aws-ses-adapter",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "A simple adapter to simplify the AWS SES email sending API",
5
5
  "author": "FabianMontoya <https://github.com/FabianMontoya>",
6
6
  "publishConfig": {
@@ -12,7 +12,7 @@
12
12
  },
13
13
  "license": "MIT",
14
14
  "engines": {
15
- "node": ">=20.0.0"
15
+ "node": ">=20.17.0"
16
16
  },
17
17
  "keywords": [
18
18
  "aws",
@@ -40,21 +40,23 @@
40
40
  "dist"
41
41
  ],
42
42
  "dependencies": {
43
- "@aws-sdk/client-ses": "^3.996.0"
43
+ "@aws-sdk/client-ses": "^3.1005.0"
44
44
  },
45
45
  "devDependencies": {
46
+ "@commitlint/cli": "^20.4.3",
47
+ "@commitlint/config-conventional": "^20.4.3",
46
48
  "@eslint/js": "^10.0.1",
47
- "@types/node": "^24.10.13",
48
- "eslint": "^10.0.2",
49
+ "@types/node": "^24.12.0",
50
+ "eslint": "^10.0.3",
49
51
  "eslint-config-prettier": "^10.1.8",
50
52
  "eslint-plugin-prettier": "^5.5.5",
51
- "globals": "^17.3.0",
53
+ "globals": "^17.4.0",
52
54
  "husky": "^9.1.7",
53
- "lint-staged": "^16.2.7",
55
+ "lint-staged": "^16.3.2",
54
56
  "prettier": "^3.8.1",
55
- "tsup": "^8.0.0",
57
+ "tsup": "^8.5.1",
56
58
  "typescript": "^5.9.3",
57
- "typescript-eslint": "^8.56.1"
59
+ "typescript-eslint": "^8.57.0"
58
60
  },
59
61
  "lint-staged": {
60
62
  "*.{js,jsx,ts,tsx}": [