@bloomneo/appkit 1.2.9
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 +21 -0
- package/README.md +902 -0
- package/bin/appkit.js +71 -0
- package/bin/commands/generate.js +1050 -0
- package/bin/templates/backend/README.md.template +39 -0
- package/bin/templates/backend/api.http.template +0 -0
- package/bin/templates/backend/docs/APPKIT_CLI.md +507 -0
- package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +61 -0
- package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +2539 -0
- package/bin/templates/backend/package.json.template +34 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.http.template +29 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.route.ts.template +36 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.service.ts.template +88 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.types.ts.template +18 -0
- package/bin/templates/backend/src/api/lib/api-router.ts.template +84 -0
- package/bin/templates/backend/src/api/server.ts.template +188 -0
- package/bin/templates/backend/tsconfig.api.json.template +24 -0
- package/bin/templates/backend/tsconfig.json.template +40 -0
- package/bin/templates/feature/feature.http.template +63 -0
- package/bin/templates/feature/feature.route.ts.template +36 -0
- package/bin/templates/feature/feature.service.ts.template +81 -0
- package/bin/templates/feature/feature.types.ts.template +23 -0
- package/bin/templates/feature-db/feature.http.template +63 -0
- package/bin/templates/feature-db/feature.model.ts.template +74 -0
- package/bin/templates/feature-db/feature.route.ts.template +58 -0
- package/bin/templates/feature-db/feature.service.ts.template +231 -0
- package/bin/templates/feature-db/feature.types.ts.template +25 -0
- package/bin/templates/feature-db/schema-addition.prisma.template +9 -0
- package/bin/templates/feature-db/seeding/README.md.template +57 -0
- package/bin/templates/feature-db/seeding/feature.seed.js.template +67 -0
- package/bin/templates/feature-user/schema-addition.prisma.template +19 -0
- package/bin/templates/feature-user/user.http.template +157 -0
- package/bin/templates/feature-user/user.model.ts.template +244 -0
- package/bin/templates/feature-user/user.route.ts.template +379 -0
- package/bin/templates/feature-user/user.seed.js.template +182 -0
- package/bin/templates/feature-user/user.service.ts.template +426 -0
- package/bin/templates/feature-user/user.types.ts.template +127 -0
- package/dist/auth/auth.d.ts +182 -0
- package/dist/auth/auth.d.ts.map +1 -0
- package/dist/auth/auth.js +477 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/defaults.d.ts +104 -0
- package/dist/auth/defaults.d.ts.map +1 -0
- package/dist/auth/defaults.js +374 -0
- package/dist/auth/defaults.js.map +1 -0
- package/dist/auth/index.d.ts +70 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +94 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/cache/cache.d.ts +118 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +249 -0
- package/dist/cache/cache.js.map +1 -0
- package/dist/cache/defaults.d.ts +63 -0
- package/dist/cache/defaults.d.ts.map +1 -0
- package/dist/cache/defaults.js +193 -0
- package/dist/cache/defaults.js.map +1 -0
- package/dist/cache/index.d.ts +101 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +203 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/strategies/memory.d.ts +138 -0
- package/dist/cache/strategies/memory.d.ts.map +1 -0
- package/dist/cache/strategies/memory.js +348 -0
- package/dist/cache/strategies/memory.js.map +1 -0
- package/dist/cache/strategies/redis.d.ts +105 -0
- package/dist/cache/strategies/redis.d.ts.map +1 -0
- package/dist/cache/strategies/redis.js +318 -0
- package/dist/cache/strategies/redis.js.map +1 -0
- package/dist/config/config.d.ts +62 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +107 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/defaults.d.ts +44 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +217 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +105 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +163 -0
- package/dist/config/index.js.map +1 -0
- package/dist/database/adapters/mongoose.d.ts +106 -0
- package/dist/database/adapters/mongoose.d.ts.map +1 -0
- package/dist/database/adapters/mongoose.js +480 -0
- package/dist/database/adapters/mongoose.js.map +1 -0
- package/dist/database/adapters/prisma.d.ts +106 -0
- package/dist/database/adapters/prisma.d.ts.map +1 -0
- package/dist/database/adapters/prisma.js +494 -0
- package/dist/database/adapters/prisma.js.map +1 -0
- package/dist/database/defaults.d.ts +87 -0
- package/dist/database/defaults.d.ts.map +1 -0
- package/dist/database/defaults.js +271 -0
- package/dist/database/defaults.js.map +1 -0
- package/dist/database/index.d.ts +137 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/index.js +490 -0
- package/dist/database/index.js.map +1 -0
- package/dist/email/defaults.d.ts +100 -0
- package/dist/email/defaults.d.ts.map +1 -0
- package/dist/email/defaults.js +400 -0
- package/dist/email/defaults.js.map +1 -0
- package/dist/email/email.d.ts +139 -0
- package/dist/email/email.d.ts.map +1 -0
- package/dist/email/email.js +316 -0
- package/dist/email/email.js.map +1 -0
- package/dist/email/index.d.ts +176 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +251 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/strategies/console.d.ts +90 -0
- package/dist/email/strategies/console.d.ts.map +1 -0
- package/dist/email/strategies/console.js +268 -0
- package/dist/email/strategies/console.js.map +1 -0
- package/dist/email/strategies/resend.d.ts +84 -0
- package/dist/email/strategies/resend.d.ts.map +1 -0
- package/dist/email/strategies/resend.js +266 -0
- package/dist/email/strategies/resend.js.map +1 -0
- package/dist/email/strategies/smtp.d.ts +77 -0
- package/dist/email/strategies/smtp.d.ts.map +1 -0
- package/dist/email/strategies/smtp.js +286 -0
- package/dist/email/strategies/smtp.js.map +1 -0
- package/dist/error/defaults.d.ts +40 -0
- package/dist/error/defaults.d.ts.map +1 -0
- package/dist/error/defaults.js +75 -0
- package/dist/error/defaults.js.map +1 -0
- package/dist/error/error.d.ts +140 -0
- package/dist/error/error.d.ts.map +1 -0
- package/dist/error/error.js +200 -0
- package/dist/error/error.js.map +1 -0
- package/dist/error/index.d.ts +145 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +145 -0
- package/dist/error/index.js.map +1 -0
- package/dist/event/defaults.d.ts +111 -0
- package/dist/event/defaults.d.ts.map +1 -0
- package/dist/event/defaults.js +378 -0
- package/dist/event/defaults.js.map +1 -0
- package/dist/event/event.d.ts +171 -0
- package/dist/event/event.d.ts.map +1 -0
- package/dist/event/event.js +391 -0
- package/dist/event/event.js.map +1 -0
- package/dist/event/index.d.ts +173 -0
- package/dist/event/index.d.ts.map +1 -0
- package/dist/event/index.js +302 -0
- package/dist/event/index.js.map +1 -0
- package/dist/event/strategies/memory.d.ts +122 -0
- package/dist/event/strategies/memory.d.ts.map +1 -0
- package/dist/event/strategies/memory.js +331 -0
- package/dist/event/strategies/memory.js.map +1 -0
- package/dist/event/strategies/redis.d.ts +115 -0
- package/dist/event/strategies/redis.d.ts.map +1 -0
- package/dist/event/strategies/redis.js +434 -0
- package/dist/event/strategies/redis.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/logger/defaults.d.ts +67 -0
- package/dist/logger/defaults.d.ts.map +1 -0
- package/dist/logger/defaults.js +213 -0
- package/dist/logger/defaults.js.map +1 -0
- package/dist/logger/index.d.ts +84 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +101 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/logger.d.ts +165 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/dist/logger/logger.js +843 -0
- package/dist/logger/logger.js.map +1 -0
- package/dist/logger/transports/console.d.ts +102 -0
- package/dist/logger/transports/console.d.ts.map +1 -0
- package/dist/logger/transports/console.js +276 -0
- package/dist/logger/transports/console.js.map +1 -0
- package/dist/logger/transports/database.d.ts +153 -0
- package/dist/logger/transports/database.d.ts.map +1 -0
- package/dist/logger/transports/database.js +539 -0
- package/dist/logger/transports/database.js.map +1 -0
- package/dist/logger/transports/file.d.ts +146 -0
- package/dist/logger/transports/file.d.ts.map +1 -0
- package/dist/logger/transports/file.js +464 -0
- package/dist/logger/transports/file.js.map +1 -0
- package/dist/logger/transports/http.d.ts +128 -0
- package/dist/logger/transports/http.d.ts.map +1 -0
- package/dist/logger/transports/http.js +401 -0
- package/dist/logger/transports/http.js.map +1 -0
- package/dist/logger/transports/webhook.d.ts +152 -0
- package/dist/logger/transports/webhook.d.ts.map +1 -0
- package/dist/logger/transports/webhook.js +485 -0
- package/dist/logger/transports/webhook.js.map +1 -0
- package/dist/queue/defaults.d.ts +66 -0
- package/dist/queue/defaults.d.ts.map +1 -0
- package/dist/queue/defaults.js +205 -0
- package/dist/queue/defaults.js.map +1 -0
- package/dist/queue/index.d.ts +124 -0
- package/dist/queue/index.d.ts.map +1 -0
- package/dist/queue/index.js +116 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/queue.d.ts +156 -0
- package/dist/queue/queue.d.ts.map +1 -0
- package/dist/queue/queue.js +387 -0
- package/dist/queue/queue.js.map +1 -0
- package/dist/queue/transports/database.d.ts +165 -0
- package/dist/queue/transports/database.d.ts.map +1 -0
- package/dist/queue/transports/database.js +595 -0
- package/dist/queue/transports/database.js.map +1 -0
- package/dist/queue/transports/memory.d.ts +143 -0
- package/dist/queue/transports/memory.d.ts.map +1 -0
- package/dist/queue/transports/memory.js +415 -0
- package/dist/queue/transports/memory.js.map +1 -0
- package/dist/queue/transports/redis.d.ts +203 -0
- package/dist/queue/transports/redis.d.ts.map +1 -0
- package/dist/queue/transports/redis.js +744 -0
- package/dist/queue/transports/redis.js.map +1 -0
- package/dist/security/defaults.d.ts +64 -0
- package/dist/security/defaults.d.ts.map +1 -0
- package/dist/security/defaults.js +159 -0
- package/dist/security/defaults.js.map +1 -0
- package/dist/security/index.d.ts +110 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +160 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/security.d.ts +138 -0
- package/dist/security/security.d.ts.map +1 -0
- package/dist/security/security.js +419 -0
- package/dist/security/security.js.map +1 -0
- package/dist/storage/defaults.d.ts +79 -0
- package/dist/storage/defaults.d.ts.map +1 -0
- package/dist/storage/defaults.js +358 -0
- package/dist/storage/defaults.js.map +1 -0
- package/dist/storage/index.d.ts +153 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +242 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/storage.d.ts +151 -0
- package/dist/storage/storage.d.ts.map +1 -0
- package/dist/storage/storage.js +439 -0
- package/dist/storage/storage.js.map +1 -0
- package/dist/storage/strategies/local.d.ts +117 -0
- package/dist/storage/strategies/local.d.ts.map +1 -0
- package/dist/storage/strategies/local.js +368 -0
- package/dist/storage/strategies/local.js.map +1 -0
- package/dist/storage/strategies/r2.d.ts +130 -0
- package/dist/storage/strategies/r2.d.ts.map +1 -0
- package/dist/storage/strategies/r2.js +470 -0
- package/dist/storage/strategies/r2.js.map +1 -0
- package/dist/storage/strategies/s3.d.ts +121 -0
- package/dist/storage/strategies/s3.d.ts.map +1 -0
- package/dist/storage/strategies/s3.js +461 -0
- package/dist/storage/strategies/s3.js.map +1 -0
- package/dist/util/defaults.d.ts +77 -0
- package/dist/util/defaults.d.ts.map +1 -0
- package/dist/util/defaults.js +193 -0
- package/dist/util/defaults.js.map +1 -0
- package/dist/util/index.d.ts +97 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +165 -0
- package/dist/util/index.js.map +1 -0
- package/dist/util/util.d.ts +145 -0
- package/dist/util/util.d.ts.map +1 -0
- package/dist/util/util.js +481 -0
- package/dist/util/util.js.map +1 -0
- package/package.json +234 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resend email strategy with modern API and automatic retry logic
|
|
3
|
+
* @module @bloomneo/appkit/email
|
|
4
|
+
* @file src/email/strategies/resend.ts
|
|
5
|
+
*
|
|
6
|
+
* @llm-rule WHEN: App has RESEND_API_KEY environment variable for modern email sending
|
|
7
|
+
* @llm-rule AVOID: Manual Resend setup - this handles API calls, retry logic, and error handling
|
|
8
|
+
* @llm-rule NOTE: Modern email provider with excellent deliverability and developer experience
|
|
9
|
+
*/
|
|
10
|
+
import type { EmailStrategy, EmailData, EmailResult } from '../email.js';
|
|
11
|
+
import type { EmailConfig } from '../defaults.js';
|
|
12
|
+
/**
|
|
13
|
+
* Resend email strategy with modern API and reliability features
|
|
14
|
+
*/
|
|
15
|
+
export declare class ResendStrategy implements EmailStrategy {
|
|
16
|
+
private config;
|
|
17
|
+
private apiKey;
|
|
18
|
+
private baseURL;
|
|
19
|
+
private timeout;
|
|
20
|
+
/**
|
|
21
|
+
* Creates Resend strategy with direct environment access (like auth pattern)
|
|
22
|
+
* @llm-rule WHEN: Email initialization with RESEND_API_KEY detected
|
|
23
|
+
* @llm-rule AVOID: Manual Resend configuration - environment detection handles this
|
|
24
|
+
*/
|
|
25
|
+
constructor(config: EmailConfig);
|
|
26
|
+
/**
|
|
27
|
+
* Sends email via Resend API with automatic retry and error handling
|
|
28
|
+
* @llm-rule WHEN: Sending emails through Resend service
|
|
29
|
+
* @llm-rule AVOID: Manual API calls - this handles all Resend complexity
|
|
30
|
+
* @llm-rule NOTE: Includes retry logic, proper error handling, and response parsing
|
|
31
|
+
*/
|
|
32
|
+
send(data: EmailData): Promise<EmailResult>;
|
|
33
|
+
/**
|
|
34
|
+
* Disconnects Resend strategy (no-op for HTTP API)
|
|
35
|
+
* @llm-rule WHEN: App shutdown or email cleanup
|
|
36
|
+
* @llm-rule AVOID: Expecting cleanup behavior - Resend is stateless HTTP API
|
|
37
|
+
*/
|
|
38
|
+
disconnect(): Promise<void>;
|
|
39
|
+
/**
|
|
40
|
+
* Converts EmailData to Resend API format
|
|
41
|
+
*/
|
|
42
|
+
private convertToResendFormat;
|
|
43
|
+
/**
|
|
44
|
+
* Formats single email address for Resend API
|
|
45
|
+
*/
|
|
46
|
+
private formatEmailAddress;
|
|
47
|
+
/**
|
|
48
|
+
* Formats multiple email addresses for Resend API
|
|
49
|
+
*/
|
|
50
|
+
private formatEmailAddresses;
|
|
51
|
+
/**
|
|
52
|
+
* Formats attachments for Resend API
|
|
53
|
+
*/
|
|
54
|
+
private formatAttachments;
|
|
55
|
+
/**
|
|
56
|
+
* Encodes attachment content for Resend API
|
|
57
|
+
*/
|
|
58
|
+
private encodeAttachmentContent;
|
|
59
|
+
/**
|
|
60
|
+
* Guesses content type from filename
|
|
61
|
+
*/
|
|
62
|
+
private guessContentType;
|
|
63
|
+
/**
|
|
64
|
+
* Sends email with retry logic and exponential backoff
|
|
65
|
+
*/
|
|
66
|
+
private sendWithRetry;
|
|
67
|
+
/**
|
|
68
|
+
* Makes HTTP request to Resend API
|
|
69
|
+
*/
|
|
70
|
+
private makeResendRequest;
|
|
71
|
+
/**
|
|
72
|
+
* Fetch with timeout support
|
|
73
|
+
*/
|
|
74
|
+
private fetchWithTimeout;
|
|
75
|
+
/**
|
|
76
|
+
* Parses Resend API errors into user-friendly messages
|
|
77
|
+
*/
|
|
78
|
+
private parseResendError;
|
|
79
|
+
/**
|
|
80
|
+
* Sleep for specified milliseconds
|
|
81
|
+
*/
|
|
82
|
+
private sleep;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=resend.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend.d.ts","sourceRoot":"","sources":["../../../src/email/strategies/resend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAiC,MAAM,aAAa,CAAC;AACxG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;GAEG;AACH,qBAAa,cAAe,YAAW,aAAa;IAClD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,OAAO,CAAS;IAExB;;;;OAIG;gBACS,MAAM,EAAE,WAAW;IAW/B;;;;;OAKG;IACG,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IA0BjD;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAOjC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAkC7B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAK5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACH,OAAO,CAAC,uBAAuB;IAO/B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAwBxB;;OAEG;YACW,aAAa;IA4B3B;;OAEG;YACW,iBAAiB;IAqB/B;;OAEG;YACW,gBAAgB;IAuB9B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAmCxB;;OAEG;IACH,OAAO,CAAC,KAAK;CAGd"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resend email strategy with modern API and automatic retry logic
|
|
3
|
+
* @module @bloomneo/appkit/email
|
|
4
|
+
* @file src/email/strategies/resend.ts
|
|
5
|
+
*
|
|
6
|
+
* @llm-rule WHEN: App has RESEND_API_KEY environment variable for modern email sending
|
|
7
|
+
* @llm-rule AVOID: Manual Resend setup - this handles API calls, retry logic, and error handling
|
|
8
|
+
* @llm-rule NOTE: Modern email provider with excellent deliverability and developer experience
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Resend email strategy with modern API and reliability features
|
|
12
|
+
*/
|
|
13
|
+
export class ResendStrategy {
|
|
14
|
+
config;
|
|
15
|
+
apiKey;
|
|
16
|
+
baseURL;
|
|
17
|
+
timeout;
|
|
18
|
+
/**
|
|
19
|
+
* Creates Resend strategy with direct environment access (like auth pattern)
|
|
20
|
+
* @llm-rule WHEN: Email initialization with RESEND_API_KEY detected
|
|
21
|
+
* @llm-rule AVOID: Manual Resend configuration - environment detection handles this
|
|
22
|
+
*/
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.apiKey = config.resend.apiKey;
|
|
26
|
+
this.baseURL = config.resend.baseURL;
|
|
27
|
+
this.timeout = config.resend.timeout;
|
|
28
|
+
if (!this.apiKey) {
|
|
29
|
+
throw new Error('Resend API key is required. Set RESEND_API_KEY environment variable.');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Sends email via Resend API with automatic retry and error handling
|
|
34
|
+
* @llm-rule WHEN: Sending emails through Resend service
|
|
35
|
+
* @llm-rule AVOID: Manual API calls - this handles all Resend complexity
|
|
36
|
+
* @llm-rule NOTE: Includes retry logic, proper error handling, and response parsing
|
|
37
|
+
*/
|
|
38
|
+
async send(data) {
|
|
39
|
+
try {
|
|
40
|
+
// Convert to Resend API format
|
|
41
|
+
const resendPayload = this.convertToResendFormat(data);
|
|
42
|
+
// Send via Resend API with retry logic
|
|
43
|
+
const result = await this.sendWithRetry(resendPayload);
|
|
44
|
+
return {
|
|
45
|
+
success: true,
|
|
46
|
+
messageId: result.id,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
const errorMessage = this.parseResendError(error);
|
|
51
|
+
if (this.config.environment.isDevelopment) {
|
|
52
|
+
console.error(`[AppKit] Resend error:`, errorMessage);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
success: false,
|
|
56
|
+
error: errorMessage,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Disconnects Resend strategy (no-op for HTTP API)
|
|
62
|
+
* @llm-rule WHEN: App shutdown or email cleanup
|
|
63
|
+
* @llm-rule AVOID: Expecting cleanup behavior - Resend is stateless HTTP API
|
|
64
|
+
*/
|
|
65
|
+
async disconnect() {
|
|
66
|
+
// Resend is HTTP API, no persistent connection to close
|
|
67
|
+
return Promise.resolve();
|
|
68
|
+
}
|
|
69
|
+
// Private helper methods
|
|
70
|
+
/**
|
|
71
|
+
* Converts EmailData to Resend API format
|
|
72
|
+
*/
|
|
73
|
+
convertToResendFormat(data) {
|
|
74
|
+
const payload = {
|
|
75
|
+
from: this.formatEmailAddress(data.from),
|
|
76
|
+
to: this.formatEmailAddresses(data.to),
|
|
77
|
+
subject: data.subject,
|
|
78
|
+
};
|
|
79
|
+
// Add content
|
|
80
|
+
if (data.html) {
|
|
81
|
+
payload.html = data.html;
|
|
82
|
+
}
|
|
83
|
+
if (data.text) {
|
|
84
|
+
payload.text = data.text;
|
|
85
|
+
}
|
|
86
|
+
// Add optional fields
|
|
87
|
+
if (data.replyTo) {
|
|
88
|
+
payload.reply_to = this.formatEmailAddress(data.replyTo);
|
|
89
|
+
}
|
|
90
|
+
if (data.cc) {
|
|
91
|
+
payload.cc = this.formatEmailAddresses(data.cc);
|
|
92
|
+
}
|
|
93
|
+
if (data.bcc) {
|
|
94
|
+
payload.bcc = this.formatEmailAddresses(data.bcc);
|
|
95
|
+
}
|
|
96
|
+
// Add attachments
|
|
97
|
+
if (data.attachments && data.attachments.length > 0) {
|
|
98
|
+
payload.attachments = this.formatAttachments(data.attachments);
|
|
99
|
+
}
|
|
100
|
+
return payload;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Formats single email address for Resend API
|
|
104
|
+
*/
|
|
105
|
+
formatEmailAddress(address) {
|
|
106
|
+
if (typeof address === 'string') {
|
|
107
|
+
return address;
|
|
108
|
+
}
|
|
109
|
+
if (address.name) {
|
|
110
|
+
return `${address.name} <${address.email}>`;
|
|
111
|
+
}
|
|
112
|
+
return address.email;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Formats multiple email addresses for Resend API
|
|
116
|
+
*/
|
|
117
|
+
formatEmailAddresses(addresses) {
|
|
118
|
+
const addressArray = Array.isArray(addresses) ? addresses : [addresses];
|
|
119
|
+
return addressArray.map(addr => this.formatEmailAddress(addr));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Formats attachments for Resend API
|
|
123
|
+
*/
|
|
124
|
+
formatAttachments(attachments) {
|
|
125
|
+
return attachments.map(attachment => ({
|
|
126
|
+
filename: attachment.filename,
|
|
127
|
+
content: this.encodeAttachmentContent(attachment.content),
|
|
128
|
+
type: attachment.contentType || this.guessContentType(attachment.filename),
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Encodes attachment content for Resend API
|
|
133
|
+
*/
|
|
134
|
+
encodeAttachmentContent(content) {
|
|
135
|
+
if (Buffer.isBuffer(content)) {
|
|
136
|
+
return content.toString('base64');
|
|
137
|
+
}
|
|
138
|
+
return Buffer.from(content).toString('base64');
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Guesses content type from filename
|
|
142
|
+
*/
|
|
143
|
+
guessContentType(filename) {
|
|
144
|
+
const ext = filename.split('.').pop()?.toLowerCase();
|
|
145
|
+
const mimeTypes = {
|
|
146
|
+
pdf: 'application/pdf',
|
|
147
|
+
doc: 'application/msword',
|
|
148
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
149
|
+
xls: 'application/vnd.ms-excel',
|
|
150
|
+
xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
151
|
+
png: 'image/png',
|
|
152
|
+
jpg: 'image/jpeg',
|
|
153
|
+
jpeg: 'image/jpeg',
|
|
154
|
+
gif: 'image/gif',
|
|
155
|
+
txt: 'text/plain',
|
|
156
|
+
html: 'text/html',
|
|
157
|
+
css: 'text/css',
|
|
158
|
+
js: 'application/javascript',
|
|
159
|
+
json: 'application/json',
|
|
160
|
+
zip: 'application/zip',
|
|
161
|
+
};
|
|
162
|
+
return mimeTypes[ext || ''] || 'application/octet-stream';
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Sends email with retry logic and exponential backoff
|
|
166
|
+
*/
|
|
167
|
+
async sendWithRetry(payload, maxRetries = 3) {
|
|
168
|
+
let lastError;
|
|
169
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
170
|
+
try {
|
|
171
|
+
return await this.makeResendRequest(payload);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
lastError = error;
|
|
175
|
+
// Don't retry on client errors (4xx)
|
|
176
|
+
if (error.status && error.status >= 400 && error.status < 500) {
|
|
177
|
+
throw error;
|
|
178
|
+
}
|
|
179
|
+
if (attempt < maxRetries) {
|
|
180
|
+
const delay = 1000 * Math.pow(2, attempt - 1); // Exponential backoff
|
|
181
|
+
console.warn(`[AppKit] Resend attempt ${attempt} failed, retrying in ${delay}ms:`, error.message);
|
|
182
|
+
await this.sleep(delay);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
throw lastError;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Makes HTTP request to Resend API
|
|
190
|
+
*/
|
|
191
|
+
async makeResendRequest(payload) {
|
|
192
|
+
const url = `${this.baseURL}/emails`;
|
|
193
|
+
const response = await this.fetchWithTimeout(url, {
|
|
194
|
+
method: 'POST',
|
|
195
|
+
headers: {
|
|
196
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
197
|
+
'Content-Type': 'application/json',
|
|
198
|
+
'User-Agent': 'VoilaJSX-AppKit-Email/1.0.0',
|
|
199
|
+
},
|
|
200
|
+
body: JSON.stringify(payload),
|
|
201
|
+
});
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
const errorData = await response.json().catch(() => ({}));
|
|
204
|
+
throw new Error(`Resend API error (${response.status}): ${errorData.message || response.statusText}`);
|
|
205
|
+
}
|
|
206
|
+
return await response.json();
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Fetch with timeout support
|
|
210
|
+
*/
|
|
211
|
+
async fetchWithTimeout(url, options) {
|
|
212
|
+
const controller = new AbortController();
|
|
213
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
214
|
+
try {
|
|
215
|
+
const response = await fetch(url, {
|
|
216
|
+
...options,
|
|
217
|
+
signal: controller.signal,
|
|
218
|
+
});
|
|
219
|
+
clearTimeout(timeoutId);
|
|
220
|
+
return response;
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
clearTimeout(timeoutId);
|
|
224
|
+
if (error.name === 'AbortError') {
|
|
225
|
+
throw new Error(`Resend API request timeout after ${this.timeout}ms`);
|
|
226
|
+
}
|
|
227
|
+
throw error;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Parses Resend API errors into user-friendly messages
|
|
232
|
+
*/
|
|
233
|
+
parseResendError(error) {
|
|
234
|
+
if (error.message) {
|
|
235
|
+
// Extract useful error information
|
|
236
|
+
const message = error.message.toLowerCase();
|
|
237
|
+
if (message.includes('unauthorized') || message.includes('invalid api key')) {
|
|
238
|
+
return 'Invalid Resend API key. Check RESEND_API_KEY environment variable.';
|
|
239
|
+
}
|
|
240
|
+
if (message.includes('rate limit')) {
|
|
241
|
+
return 'Resend API rate limit exceeded. Please try again later.';
|
|
242
|
+
}
|
|
243
|
+
if (message.includes('quota') || message.includes('usage limit')) {
|
|
244
|
+
return 'Resend API quota exceeded. Check your account limits.';
|
|
245
|
+
}
|
|
246
|
+
if (message.includes('domain') || message.includes('verify')) {
|
|
247
|
+
return 'Email domain not verified in Resend. Please verify your sending domain.';
|
|
248
|
+
}
|
|
249
|
+
if (message.includes('timeout')) {
|
|
250
|
+
return 'Resend API request timeout. Please try again.';
|
|
251
|
+
}
|
|
252
|
+
if (message.includes('network') || message.includes('connection')) {
|
|
253
|
+
return 'Network error connecting to Resend API. Please check your internet connection.';
|
|
254
|
+
}
|
|
255
|
+
return error.message;
|
|
256
|
+
}
|
|
257
|
+
return 'Unknown Resend API error occurred';
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Sleep for specified milliseconds
|
|
261
|
+
*/
|
|
262
|
+
sleep(ms) {
|
|
263
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=resend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resend.js","sourceRoot":"","sources":["../../../src/email/strategies/resend.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH;;GAEG;AACH,MAAM,OAAO,cAAc;IACjB,MAAM,CAAc;IACpB,MAAM,CAAS;IACf,OAAO,CAAS;IAChB,OAAO,CAAS;IAExB;;;;OAIG;IACH,YAAY,MAAmB;QAC7B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAO,CAAC,MAAM,CAAC;QACpC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC;QACtC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAO,CAAC,OAAO,CAAC;QAEtC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACjB,MAAM,IAAI,KAAK,CAAC,sEAAsE,CAAC,CAAC;QAC1F,CAAC;IACH,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,IAAI,CAAC,IAAe;QACxB,IAAI,CAAC;YACH,+BAA+B;YAC/B,MAAM,aAAa,GAAG,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;YAEvD,uCAAuC;YACvC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;YAEvD,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,SAAS,EAAE,MAAM,CAAC,EAAE;aACrB,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,YAAY,GAAG,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;YAElD,IAAI,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,aAAa,EAAE,CAAC;gBAC1C,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,YAAY,CAAC,CAAC;YACxD,CAAC;YAED,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,YAAY;aACpB,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,UAAU;QACd,wDAAwD;QACxD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3B,CAAC;IAED,yBAAyB;IAEzB;;OAEG;IACK,qBAAqB,CAAC,IAAe;QAC3C,MAAM,OAAO,GAAQ;YACnB,IAAI,EAAE,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAK,CAAC;YACzC,EAAE,EAAE,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,OAAO,EAAE,IAAI,CAAC,OAAO;SACtB,CAAC;QAEF,cAAc;QACd,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QAC3B,CAAC;QACD,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QAC3B,CAAC;QAED,sBAAsB;QACtB,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC3D,CAAC;QACD,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClD,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACpD,CAAC;QAED,kBAAkB;QAClB,IAAI,IAAI,CAAC,WAAW,IAAI,IAAI,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpD,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QACjE,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACK,kBAAkB,CAAC,OAA8B;QACvD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;YAChC,OAAO,OAAO,CAAC;QACjB,CAAC;QAED,IAAI,OAAO,CAAC,IAAI,EAAE,CAAC;YACjB,OAAO,GAAG,OAAO,CAAC,IAAI,KAAK,OAAO,CAAC,KAAK,GAAG,CAAC;QAC9C,CAAC;QAED,OAAO,OAAO,CAAC,KAAK,CAAC;IACvB,CAAC;IAED;;OAEG;IACK,oBAAoB,CAAC,SAA4D;QACvF,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;QACxE,OAAO,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC;IACjE,CAAC;IAED;;OAEG;IACK,iBAAiB,CAAC,WAA8B;QACtD,OAAO,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YACpC,QAAQ,EAAE,UAAU,CAAC,QAAQ;YAC7B,OAAO,EAAE,IAAI,CAAC,uBAAuB,CAAC,UAAU,CAAC,OAAO,CAAC;YACzD,IAAI,EAAE,UAAU,CAAC,WAAW,IAAI,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,QAAQ,CAAC;SAC3E,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;OAEG;IACK,uBAAuB,CAAC,OAAwB;QACtD,IAAI,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,OAAO,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACjD,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,QAAgB;QACvC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,WAAW,EAAE,CAAC;QAErD,MAAM,SAAS,GAA2B;YACxC,GAAG,EAAE,iBAAiB;YACtB,GAAG,EAAE,oBAAoB;YACzB,IAAI,EAAE,yEAAyE;YAC/E,GAAG,EAAE,0BAA0B;YAC/B,IAAI,EAAE,mEAAmE;YACzE,GAAG,EAAE,WAAW;YAChB,GAAG,EAAE,YAAY;YACjB,IAAI,EAAE,YAAY;YAClB,GAAG,EAAE,WAAW;YAChB,GAAG,EAAE,YAAY;YACjB,IAAI,EAAE,WAAW;YACjB,GAAG,EAAE,UAAU;YACf,EAAE,EAAE,wBAAwB;YAC5B,IAAI,EAAE,kBAAkB;YACxB,GAAG,EAAE,iBAAiB;SACvB,CAAC;QAEF,OAAO,SAAS,CAAC,GAAG,IAAI,EAAE,CAAC,IAAI,0BAA0B,CAAC;IAC5D,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,aAAa,CAAC,OAAY,EAAE,aAAqB,CAAC;QAC9D,IAAI,SAAgB,CAAC;QAErB,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;YACvD,IAAI,CAAC;gBACH,OAAO,MAAM,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;YAC/C,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBACpB,SAAS,GAAG,KAAc,CAAC;gBAE3B,qCAAqC;gBACrC,IAAI,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;oBAC9D,MAAM,KAAK,CAAC;gBACd,CAAC;gBAED,IAAI,OAAO,GAAG,UAAU,EAAE,CAAC;oBACzB,MAAM,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,sBAAsB;oBACrE,OAAO,CAAC,IAAI,CACV,2BAA2B,OAAO,wBAAwB,KAAK,KAAK,EACpE,KAAK,CAAC,OAAO,CACd,CAAC;oBACF,MAAM,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC1B,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,SAAU,CAAC;IACnB,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,iBAAiB,CAAC,OAAY;QAC1C,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,OAAO,SAAS,CAAC;QAErC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,GAAG,EAAE;YAChD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,eAAe,EAAE,UAAU,IAAI,CAAC,MAAM,EAAE;gBACxC,cAAc,EAAE,kBAAkB;gBAClC,YAAY,EAAE,6BAA6B;aAC5C;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;SAC9B,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,CAAC,CAAQ,CAAC;YACjE,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,CAAC,MAAM,MAAM,SAAS,CAAC,OAAO,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;QACxG,CAAC;QAED,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,gBAAgB,CAAC,GAAW,EAAE,OAAY;QACtD,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAErE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,GAAG,OAAO;gBACV,MAAM,EAAE,UAAU,CAAC,MAAM;aAC1B,CAAC,CAAC;YAEH,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,OAAO,QAAQ,CAAC;QAClB,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,YAAY,CAAC,SAAS,CAAC,CAAC;YAExB,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBAChC,MAAM,IAAI,KAAK,CAAC,oCAAoC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YACxE,CAAC;YAED,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,KAAU;QACjC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAClB,mCAAmC;YACnC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;YAE5C,IAAI,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC;gBAC5E,OAAO,oEAAoE,CAAC;YAC9E,CAAC;YAED,IAAI,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBACnC,OAAO,yDAAyD,CAAC;YACnE,CAAC;YAED,IAAI,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC;gBACjE,OAAO,uDAAuD,CAAC;YACjE,CAAC;YAED,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC7D,OAAO,yEAAyE,CAAC;YACnF,CAAC;YAED,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;gBAChC,OAAO,+CAA+C,CAAC;YACzD,CAAC;YAED,IAAI,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;gBAClE,OAAO,gFAAgF,CAAC;YAC1F,CAAC;YAED,OAAO,KAAK,CAAC,OAAO,CAAC;QACvB,CAAC;QAED,OAAO,mCAAmC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,EAAU;QACtB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IACzD,CAAC;CACF"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SMTP email strategy with nodemailer and connection pooling
|
|
3
|
+
* @module @bloomneo/appkit/email
|
|
4
|
+
* @file src/email/strategies/smtp.ts
|
|
5
|
+
*
|
|
6
|
+
* @llm-rule WHEN: App has SMTP_HOST environment variable for universal email sending
|
|
7
|
+
* @llm-rule AVOID: Manual SMTP setup - this handles connection pooling, authentication, and reliability
|
|
8
|
+
* @llm-rule NOTE: Universal email strategy that works with any SMTP server (Gmail, Outlook, etc.)
|
|
9
|
+
*/
|
|
10
|
+
import type { EmailStrategy, EmailData, EmailResult } from '../email.js';
|
|
11
|
+
import type { EmailConfig } from '../defaults.js';
|
|
12
|
+
/**
|
|
13
|
+
* SMTP email strategy with connection pooling and reliability features
|
|
14
|
+
*/
|
|
15
|
+
export declare class SmtpStrategy implements EmailStrategy {
|
|
16
|
+
private config;
|
|
17
|
+
private transporter;
|
|
18
|
+
private connected;
|
|
19
|
+
/**
|
|
20
|
+
* Creates SMTP strategy with direct environment access (like auth pattern)
|
|
21
|
+
* @llm-rule WHEN: Email initialization with SMTP_HOST detected
|
|
22
|
+
* @llm-rule AVOID: Manual SMTP configuration - environment detection handles this
|
|
23
|
+
*/
|
|
24
|
+
constructor(config: EmailConfig);
|
|
25
|
+
/**
|
|
26
|
+
* Sends email via SMTP with automatic connection management
|
|
27
|
+
* @llm-rule WHEN: Sending emails through SMTP servers
|
|
28
|
+
* @llm-rule AVOID: Manual SMTP calls - this handles all connection complexity
|
|
29
|
+
* @llm-rule NOTE: Includes connection pooling, authentication, and error handling
|
|
30
|
+
*/
|
|
31
|
+
send(data: EmailData): Promise<EmailResult>;
|
|
32
|
+
/**
|
|
33
|
+
* Disconnects SMTP strategy gracefully
|
|
34
|
+
* @llm-rule WHEN: App shutdown or email cleanup
|
|
35
|
+
* @llm-rule AVOID: Abrupt disconnection - graceful shutdown prevents connection issues
|
|
36
|
+
*/
|
|
37
|
+
disconnect(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Ensures SMTP transporter is connected
|
|
40
|
+
*/
|
|
41
|
+
private ensureConnected;
|
|
42
|
+
/**
|
|
43
|
+
* Converts EmailData to nodemailer format
|
|
44
|
+
*/
|
|
45
|
+
private convertToSmtpFormat;
|
|
46
|
+
/**
|
|
47
|
+
* Formats single email address for nodemailer
|
|
48
|
+
*/
|
|
49
|
+
private formatEmailAddress;
|
|
50
|
+
/**
|
|
51
|
+
* Formats multiple email addresses for nodemailer
|
|
52
|
+
*/
|
|
53
|
+
private formatEmailAddresses;
|
|
54
|
+
/**
|
|
55
|
+
* Formats attachments for nodemailer
|
|
56
|
+
*/
|
|
57
|
+
private formatAttachments;
|
|
58
|
+
/**
|
|
59
|
+
* Guesses content type from filename
|
|
60
|
+
*/
|
|
61
|
+
private guessContentType;
|
|
62
|
+
/**
|
|
63
|
+
* Parses SMTP errors into user-friendly messages
|
|
64
|
+
*/
|
|
65
|
+
private parseSmtpError;
|
|
66
|
+
/**
|
|
67
|
+
* Gets SMTP connection info for debugging
|
|
68
|
+
*/
|
|
69
|
+
getConnectionInfo(): {
|
|
70
|
+
host: string;
|
|
71
|
+
port: number;
|
|
72
|
+
secure: boolean;
|
|
73
|
+
authenticated: boolean;
|
|
74
|
+
connected: boolean;
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=smtp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"smtp.d.ts","sourceRoot":"","sources":["../../../src/email/strategies/smtp.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,WAAW,EAAiC,MAAM,aAAa,CAAC;AACxG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;GAEG;AACH,qBAAa,YAAa,YAAW,aAAa;IAChD,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,SAAS,CAAkB;IAEnC;;;;OAIG;gBACS,MAAM,EAAE,WAAW;IAI/B;;;;;OAKG;IACG,IAAI,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,CAAC,WAAW,CAAC;IA6BjD;;;;OAIG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAkBjC;;OAEG;YACW,eAAe;IA6C7B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAkC3B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAY1B;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAK5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAwBxB;;OAEG;IACH,OAAO,CAAC,cAAc;IA6EtB;;OAEG;IACH,iBAAiB,IAAI;QACnB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE,OAAO,CAAC;QAChB,aAAa,EAAE,OAAO,CAAC;QACvB,SAAS,EAAE,OAAO,CAAC;KACpB;CAWF"}
|