@bloomneo/appkit 1.5.1 → 1.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/AGENTS.md +195 -0
  2. package/CHANGELOG.md +253 -0
  3. package/README.md +147 -799
  4. package/bin/commands/generate.js +7 -7
  5. package/cookbook/README.md +26 -0
  6. package/cookbook/api-key-service.ts +106 -0
  7. package/cookbook/auth-protected-crud.ts +112 -0
  8. package/cookbook/file-upload-pipeline.ts +113 -0
  9. package/cookbook/multi-tenant-saas.ts +87 -0
  10. package/cookbook/real-time-chat.ts +121 -0
  11. package/dist/auth/auth.d.ts +21 -4
  12. package/dist/auth/auth.d.ts.map +1 -1
  13. package/dist/auth/auth.js +56 -44
  14. package/dist/auth/auth.js.map +1 -1
  15. package/dist/auth/defaults.d.ts +1 -1
  16. package/dist/auth/defaults.js +35 -35
  17. package/dist/cache/cache.d.ts +29 -6
  18. package/dist/cache/cache.d.ts.map +1 -1
  19. package/dist/cache/cache.js +72 -44
  20. package/dist/cache/cache.js.map +1 -1
  21. package/dist/cache/defaults.js +25 -25
  22. package/dist/cache/index.d.ts +19 -10
  23. package/dist/cache/index.d.ts.map +1 -1
  24. package/dist/cache/index.js +21 -18
  25. package/dist/cache/index.js.map +1 -1
  26. package/dist/config/defaults.d.ts +1 -1
  27. package/dist/config/defaults.js +8 -8
  28. package/dist/config/index.d.ts +3 -3
  29. package/dist/config/index.js +4 -4
  30. package/dist/database/adapters/mongoose.js +2 -2
  31. package/dist/database/adapters/prisma.js +2 -2
  32. package/dist/database/defaults.d.ts +1 -1
  33. package/dist/database/defaults.js +4 -4
  34. package/dist/database/index.js +2 -2
  35. package/dist/database/index.js.map +1 -1
  36. package/dist/email/defaults.js +20 -20
  37. package/dist/error/defaults.d.ts +1 -1
  38. package/dist/error/defaults.js +12 -12
  39. package/dist/error/error.d.ts +12 -0
  40. package/dist/error/error.d.ts.map +1 -1
  41. package/dist/error/error.js +19 -0
  42. package/dist/error/error.js.map +1 -1
  43. package/dist/error/index.d.ts +14 -3
  44. package/dist/error/index.d.ts.map +1 -1
  45. package/dist/error/index.js +14 -3
  46. package/dist/error/index.js.map +1 -1
  47. package/dist/event/defaults.js +30 -30
  48. package/dist/logger/defaults.d.ts +1 -1
  49. package/dist/logger/defaults.js +40 -40
  50. package/dist/logger/index.d.ts +1 -0
  51. package/dist/logger/index.d.ts.map +1 -1
  52. package/dist/logger/index.js.map +1 -1
  53. package/dist/logger/logger.d.ts +8 -0
  54. package/dist/logger/logger.d.ts.map +1 -1
  55. package/dist/logger/logger.js +13 -3
  56. package/dist/logger/logger.js.map +1 -1
  57. package/dist/logger/transports/console.js +1 -1
  58. package/dist/logger/transports/http.d.ts +1 -1
  59. package/dist/logger/transports/http.js +1 -1
  60. package/dist/logger/transports/webhook.d.ts +1 -1
  61. package/dist/logger/transports/webhook.js +1 -1
  62. package/dist/queue/defaults.d.ts +2 -2
  63. package/dist/queue/defaults.js +38 -38
  64. package/dist/security/defaults.d.ts +1 -1
  65. package/dist/security/defaults.js +29 -29
  66. package/dist/security/index.d.ts +1 -1
  67. package/dist/security/index.js +3 -3
  68. package/dist/security/security.d.ts +1 -1
  69. package/dist/security/security.js +4 -4
  70. package/dist/storage/defaults.js +19 -19
  71. package/dist/util/defaults.d.ts +1 -1
  72. package/dist/util/defaults.js +34 -34
  73. package/dist/util/env.d.ts +35 -0
  74. package/dist/util/env.d.ts.map +1 -0
  75. package/dist/util/env.js +50 -0
  76. package/dist/util/env.js.map +1 -0
  77. package/dist/util/errors.d.ts +52 -0
  78. package/dist/util/errors.d.ts.map +1 -0
  79. package/dist/util/errors.js +82 -0
  80. package/dist/util/errors.js.map +1 -0
  81. package/examples/.env.example +80 -0
  82. package/examples/README.md +16 -0
  83. package/examples/auth.ts +228 -0
  84. package/examples/cache.ts +36 -0
  85. package/examples/config.ts +45 -0
  86. package/examples/database.ts +69 -0
  87. package/examples/email.ts +53 -0
  88. package/examples/error.ts +50 -0
  89. package/examples/event.ts +42 -0
  90. package/examples/logger.ts +41 -0
  91. package/examples/queue.ts +58 -0
  92. package/examples/security.ts +46 -0
  93. package/examples/storage.ts +44 -0
  94. package/examples/util.ts +47 -0
  95. package/llms.txt +591 -0
  96. package/package.json +19 -10
  97. package/src/auth/README.md +850 -0
  98. package/src/cache/README.md +756 -0
  99. package/src/config/README.md +604 -0
  100. package/src/database/README.md +818 -0
  101. package/src/email/README.md +759 -0
  102. package/src/error/README.md +660 -0
  103. package/src/event/README.md +729 -0
  104. package/src/logger/README.md +435 -0
  105. package/src/queue/README.md +851 -0
  106. package/src/security/README.md +612 -0
  107. package/src/storage/README.md +1008 -0
  108. package/src/util/README.md +955 -0
  109. package/bin/templates/backend/docs/APPKIT_CLI.md +0 -507
  110. package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +0 -61
  111. package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +0 -2539
@@ -0,0 +1,759 @@
1
+ # @bloomneo/appkit - Email Module 📧
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@bloomneo/appkit.svg)](https://www.npmjs.com/package/@bloomneo/appkit)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ > Ultra-simple email sending that just works - One function, automatic provider
7
+ > detection, zero configuration
8
+
9
+ **One function** returns an email object with automatic strategy selection. Zero
10
+ configuration needed, production-ready sending by default, with built-in
11
+ template system and development preview.
12
+
13
+ ## 🚀 Why Choose This?
14
+
15
+ - **⚡ One Function** - Just `emailClass.get()`, everything else is automatic
16
+ - **🎯 Auto-Strategy** - RESEND_API_KEY = Resend, SMTP_HOST = SMTP, default =
17
+ Console
18
+ - **🔧 Zero Configuration** - Smart defaults for everything
19
+ - **📄 Built-in Templates** - Welcome, reset password templates included
20
+ - **🎨 Development Preview** - See emails in console with beautiful formatting
21
+ - **🛡️ Production Ready** - Retry logic, error handling, graceful shutdown
22
+ - **🤖 AI-Ready** - Optimized for LLM code generation
23
+
24
+ ## 📦 Installation
25
+
26
+ ```bash
27
+ npm install @bloomneo/appkit
28
+ ```
29
+
30
+ ## 🏃‍♂️ Quick Start (30 seconds)
31
+
32
+ ### 1. Basic Setup (Console Preview)
33
+
34
+ ```typescript
35
+ import { emailClass } from '@bloomneo/appkit/email';
36
+
37
+ const email = emailClass.get();
38
+
39
+ // Send email (shows in console during development)
40
+ await email.send({
41
+ to: 'user@example.com',
42
+ subject: 'Welcome!',
43
+ text: 'Hello world!',
44
+ });
45
+
46
+ // Even simpler
47
+ await emailClass.sendText('user@example.com', 'Hi', 'Hello!');
48
+ ```
49
+
50
+ ### 2. Production Setup (Resend)
51
+
52
+ ```bash
53
+ # Just set API key - automatic Resend strategy
54
+ export RESEND_API_KEY=re_your_api_key_here
55
+ ```
56
+
57
+ ```typescript
58
+ import { emailClass } from '@bloomneo/appkit/email';
59
+
60
+ // Same code, now sends real emails!
61
+ await emailClass.send({
62
+ to: 'user@example.com',
63
+ subject: 'Welcome!',
64
+ html: '<h1>Hello!</h1><p>Welcome to our app!</p>',
65
+ });
66
+ ```
67
+
68
+ **That's it!** No configuration, no setup, just works everywhere.
69
+
70
+ ## 🧠 Mental Model
71
+
72
+ ### **Automatic Strategy Selection**
73
+
74
+ The email module **automatically detects** what you need:
75
+
76
+ | Environment Variable | Strategy | Use Case |
77
+ | -------------------------- | -------- | ---------------------------------- |
78
+ | `RESEND_API_KEY=re_...` | Resend | Modern production (recommended) |
79
+ | `SMTP_HOST=smtp.gmail.com` | SMTP | Universal (Gmail, Outlook, custom) |
80
+ | _No email env vars_ | Console | Development (shows in terminal) |
81
+
82
+ ### **Development → Production**
83
+
84
+ ```typescript
85
+ // Same code works everywhere
86
+ await emailClass.send({
87
+ to: 'user@example.com',
88
+ subject: 'Welcome!',
89
+ text: 'Hello!',
90
+ });
91
+
92
+ // Development: Beautiful console preview
93
+ // Production: Real email via Resend/SMTP
94
+ ```
95
+
96
+ ## 📖 Complete API
97
+
98
+ ### Core Function
99
+
100
+ ```typescript
101
+ const email = emailClass.get(); // One function, everything you need
102
+ ```
103
+
104
+ ### Email Operations
105
+
106
+ ```typescript
107
+ // Full email
108
+ await email.send({
109
+ to: 'user@example.com',
110
+ subject: 'Subject',
111
+ text: 'Plain text',
112
+ html: '<h1>HTML content</h1>',
113
+ });
114
+
115
+ // Simple text
116
+ await email.sendText('user@example.com', 'Subject', 'Text');
117
+
118
+ // HTML email
119
+ await email.sendHtml('user@example.com', 'Subject', '<h1>HTML</h1>');
120
+
121
+ // Template email
122
+ await email.sendTemplate('welcome', {
123
+ to: 'user@example.com',
124
+ name: 'John',
125
+ appName: 'MyApp',
126
+ });
127
+
128
+ // Batch emails
129
+ await email.sendBatch([email1, email2, email3]);
130
+ ```
131
+
132
+ ### Utility Methods
133
+
134
+ ```typescript
135
+ // Debugging
136
+ email.getStrategy(); // 'resend', 'smtp', or 'console'
137
+ emailClass.hasResend(); // true if RESEND_API_KEY set
138
+ emailClass.hasSmtp(); // true if SMTP_HOST set
139
+
140
+ // Convenience
141
+ await emailClass.send(emailData); // Direct send without get()
142
+ ```
143
+
144
+ ## 💡 Simple Examples
145
+
146
+ ### **User Registration Email**
147
+
148
+ ```typescript
149
+ import { emailClass } from '@bloomneo/appkit/email';
150
+
151
+ async function sendWelcomeEmail(user) {
152
+ await emailClass.send({
153
+ to: user.email,
154
+ subject: `Welcome to ${process.env.APP_NAME}!`,
155
+ html: `
156
+ <h1>Welcome ${user.name}!</h1>
157
+ <p>Thanks for joining us. We're excited to have you!</p>
158
+ <a href="${process.env.APP_URL}/dashboard">Get Started</a>
159
+ `,
160
+ text: `Welcome ${user.name}! Thanks for joining us. Visit ${process.env.APP_URL}/dashboard to get started.`,
161
+ });
162
+ }
163
+ ```
164
+
165
+ ### **Password Reset**
166
+
167
+ ```typescript
168
+ import { emailClass } from '@bloomneo/appkit/email';
169
+
170
+ async function sendPasswordReset(user, resetToken) {
171
+ const resetUrl = `${process.env.APP_URL}/reset?token=${resetToken}`;
172
+
173
+ await emailClass.send({
174
+ to: user.email,
175
+ subject: 'Reset your password',
176
+ html: `
177
+ <h2>Reset your password</h2>
178
+ <p>Hi ${user.name},</p>
179
+ <p>Click the link below to reset your password:</p>
180
+ <a href="${resetUrl}">Reset Password</a>
181
+ <p>This link expires in 1 hour.</p>
182
+ <p>If you didn't request this, please ignore this email.</p>
183
+ `,
184
+ text: `Reset your password: ${resetUrl} (expires in 1 hour)`,
185
+ });
186
+ }
187
+ ```
188
+
189
+ ### **Order Confirmation**
190
+
191
+ ```typescript
192
+ import { emailClass } from '@bloomneo/appkit/email';
193
+
194
+ async function sendOrderConfirmation(order) {
195
+ await emailClass.send({
196
+ to: order.customerEmail,
197
+ subject: `Order Confirmation #${order.id}`,
198
+ html: `
199
+ <h1>Order Confirmed!</h1>
200
+ <p>Thanks for your order, ${order.customerName}!</p>
201
+ <h3>Order Details:</h3>
202
+ <ul>
203
+ ${order.items.map((item) => `<li>${item.name} x${item.quantity} - $${item.price}</li>`).join('')}
204
+ </ul>
205
+ <p><strong>Total: $${order.total}</strong></p>
206
+ <p>We'll send you tracking information when your order ships.</p>
207
+ `,
208
+ });
209
+ }
210
+ ```
211
+
212
+ ### **Built-in Templates**
213
+
214
+ ```typescript
215
+ import { emailClass } from '@bloomneo/appkit/email';
216
+
217
+ // Welcome template
218
+ await emailClass.get().sendTemplate('welcome', {
219
+ to: 'user@example.com',
220
+ name: 'John',
221
+ appName: 'MyApp',
222
+ });
223
+
224
+ // Password reset template
225
+ await emailClass.get().sendTemplate('reset', {
226
+ to: 'user@example.com',
227
+ name: 'John',
228
+ resetUrl: 'https://myapp.com/reset?token=abc123',
229
+ appName: 'MyApp',
230
+ });
231
+ ```
232
+
233
+ ## 🧪 Testing
234
+
235
+ ```typescript
236
+ import { emailClass } from '@bloomneo/appkit/email';
237
+
238
+ describe('Email Tests', () => {
239
+ afterEach(async () => {
240
+ await emailClass.clear(); // Clean up between tests
241
+ });
242
+
243
+ test('should send email', async () => {
244
+ // Force console strategy for tests
245
+ await emailClass.reset({
246
+ strategy: 'console',
247
+ from: { name: 'Test App', email: 'test@example.com' },
248
+ });
249
+
250
+ const result = await emailClass.send({
251
+ to: 'user@example.com',
252
+ subject: 'Test',
253
+ text: 'Test message',
254
+ });
255
+
256
+ expect(result.success).toBe(true);
257
+ expect(result.messageId).toBeDefined();
258
+ });
259
+ });
260
+ ```
261
+
262
+ ## ⚠️ Common Mistakes
263
+
264
+ ### **1. Missing Required Fields**
265
+
266
+ ```typescript
267
+ // ❌ DON'T forget subject or content
268
+ await emailClass.send({
269
+ to: 'user@example.com',
270
+ // Missing subject and content!
271
+ });
272
+
273
+ // ✅ DO include all required fields
274
+ await emailClass.send({
275
+ to: 'user@example.com',
276
+ subject: 'Welcome!',
277
+ text: 'Welcome to our app!',
278
+ });
279
+ ```
280
+
281
+ ### **2. Console Strategy in Production**
282
+
283
+ ```typescript
284
+ // ❌ DON'T rely on console strategy in production
285
+ // This only logs emails, doesn't send them!
286
+ process.env.NODE_ENV = 'production';
287
+ // No RESEND_API_KEY or SMTP_HOST set
288
+ await emailClass.send(emailData); // Only logs to console!
289
+
290
+ // ✅ DO set up a real email provider
291
+ process.env.RESEND_API_KEY = 're_your_api_key';
292
+ await emailClass.send(emailData); // Actually sends emails
293
+ ```
294
+
295
+ ### **3. Ignoring Send Results**
296
+
297
+ ```typescript
298
+ // ❌ DON'T ignore send results
299
+ await emailClass.send(emailData); // What if it failed?
300
+
301
+ // ✅ DO check for success/failure
302
+ const result = await emailClass.send(emailData);
303
+ if (!result.success) {
304
+ console.error('Email failed:', result.error);
305
+ // Handle failure appropriately
306
+ }
307
+ ```
308
+
309
+ ### **4. Invalid Email Addresses**
310
+
311
+ ```typescript
312
+ // ❌ DON'T use invalid email formats
313
+ await emailClass.send({
314
+ to: 'not-an-email', // Invalid format
315
+ subject: 'Test',
316
+ text: 'Hello',
317
+ });
318
+
319
+ // ✅ DO validate email addresses
320
+ const isValidEmail = (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
321
+ if (!isValidEmail(user.email)) {
322
+ throw new Error('Invalid email address');
323
+ }
324
+ ```
325
+
326
+ ### **5. Unverified FROM Addresses**
327
+
328
+ ```typescript
329
+ // ❌ DON'T use unverified FROM addresses with Resend
330
+ await emailClass.send({
331
+ from: 'random@example.com', // Unverified domain
332
+ to: 'user@example.com',
333
+ subject: 'Test',
334
+ text: 'Hello',
335
+ });
336
+
337
+ // ✅ DO configure verified FROM address
338
+ process.env.BLOOM_EMAIL_FROM_EMAIL = 'noreply@yourdomain.com'; // Verified
339
+ // FROM automatically set from config
340
+ ```
341
+
342
+ ## 🚨 Error Handling
343
+
344
+ ### **Basic Error Handling**
345
+
346
+ ```typescript
347
+ async function sendWelcomeEmail(user) {
348
+ const result = await emailClass.send({
349
+ to: user.email,
350
+ subject: 'Welcome!',
351
+ text: 'Welcome to our app!',
352
+ });
353
+
354
+ if (!result.success) {
355
+ console.error('Email failed:', result.error);
356
+ // Don't throw - email failure shouldn't break user registration
357
+ return false;
358
+ }
359
+
360
+ console.log('Welcome email sent:', result.messageId);
361
+ return true;
362
+ }
363
+ ```
364
+
365
+ ### **Production Error Handling with Retries**
366
+
367
+ ```typescript
368
+ async function sendCriticalEmail(emailData, maxRetries = 3) {
369
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
370
+ const result = await emailClass.send(emailData);
371
+
372
+ if (result.success) {
373
+ return result;
374
+ }
375
+
376
+ // If it's a client error (bad email, invalid data), don't retry
377
+ if (
378
+ result.error?.includes('Invalid email') ||
379
+ result.error?.includes('Bad Request')
380
+ ) {
381
+ throw new Error(`Email validation failed: ${result.error}`);
382
+ }
383
+
384
+ // Server errors - retry with exponential backoff
385
+ if (attempt < maxRetries) {
386
+ const delay = 1000 * Math.pow(2, attempt - 1);
387
+ console.warn(
388
+ `Email attempt ${attempt} failed, retrying in ${delay}ms:`,
389
+ result.error
390
+ );
391
+ await new Promise((resolve) => setTimeout(resolve, delay));
392
+ }
393
+ }
394
+
395
+ throw new Error(`Email failed after ${maxRetries} attempts`);
396
+ }
397
+ ```
398
+
399
+ ### **Strategy-Specific Error Handling**
400
+
401
+ ```typescript
402
+ async function sendEmailWithContext(emailData) {
403
+ const strategy = emailClass.getStrategy();
404
+ const result = await emailClass.send(emailData);
405
+
406
+ if (!result.success) {
407
+ // Handle strategy-specific errors
408
+ switch (strategy) {
409
+ case 'resend':
410
+ if (result.error?.includes('API key')) {
411
+ throw new Error('Resend API key invalid - check RESEND_API_KEY');
412
+ }
413
+ if (result.error?.includes('domain')) {
414
+ throw new Error('Email domain not verified in Resend');
415
+ }
416
+ break;
417
+
418
+ case 'smtp':
419
+ if (result.error?.includes('authentication')) {
420
+ throw new Error(
421
+ 'SMTP authentication failed - check SMTP_USER/SMTP_PASS'
422
+ );
423
+ }
424
+ if (result.error?.includes('connection')) {
425
+ throw new Error('SMTP connection failed - check SMTP_HOST/SMTP_PORT');
426
+ }
427
+ break;
428
+
429
+ case 'console':
430
+ if (process.env.NODE_ENV === 'production') {
431
+ console.warn(
432
+ 'Using console strategy in production - emails not actually sent'
433
+ );
434
+ }
435
+ break;
436
+ }
437
+
438
+ throw new Error(`Email send failed: ${result.error}`);
439
+ }
440
+
441
+ return result;
442
+ }
443
+ ```
444
+
445
+ ## 🔧 Startup Validation
446
+
447
+ ### **Basic App Startup Validation**
448
+
449
+ ```typescript
450
+ import { emailClass } from '@bloomneo/appkit/email';
451
+
452
+ async function startApp() {
453
+ try {
454
+ // Validate email configuration at startup
455
+ emailClass.validateConfig();
456
+
457
+ const strategy = emailClass.getStrategy();
458
+ console.log(`📧 Email configured with ${strategy} strategy`);
459
+
460
+ // Start your app
461
+ app.listen(3000, () => {
462
+ console.log('🚀 Server started on port 3000');
463
+ });
464
+ } catch (error) {
465
+ console.error('❌ Startup validation failed:', error.message);
466
+ process.exit(1);
467
+ }
468
+ }
469
+ ```
470
+
471
+ ### **Production Startup Validation**
472
+
473
+ ```typescript
474
+ async function validateProductionSetup() {
475
+ if (process.env.NODE_ENV !== 'production') return;
476
+
477
+ // Check if real email provider is configured
478
+ if (!emailClass.hasProvider()) {
479
+ throw new Error(
480
+ 'No email provider configured in production. ' +
481
+ 'Set RESEND_API_KEY or SMTP_HOST environment variable.'
482
+ );
483
+ }
484
+
485
+ // Validate FROM address is set
486
+ if (!process.env.BLOOM_EMAIL_FROM_EMAIL) {
487
+ console.warn(
488
+ '⚠️ No FROM email configured. Using default. ' +
489
+ 'Set BLOOM_EMAIL_FROM_EMAIL for professional emails.'
490
+ );
491
+ }
492
+
493
+ console.log('✅ Email system validated successfully');
494
+ }
495
+ ```
496
+
497
+ ### **Health Check Endpoint**
498
+
499
+ ```typescript
500
+ // Express middleware for email health check
501
+ function emailHealthCheck(req, res) {
502
+ try {
503
+ const config = emailClass.getConfig();
504
+ const hasProvider = emailClass.hasProvider();
505
+
506
+ res.json({
507
+ status: 'ok',
508
+ strategy: config.strategy,
509
+ hasProvider,
510
+ fromEmail: config.fromEmail,
511
+ ready: hasProvider || process.env.NODE_ENV === 'development',
512
+ });
513
+ } catch (error) {
514
+ res.status(500).json({
515
+ status: 'error',
516
+ error: error.message,
517
+ });
518
+ }
519
+ }
520
+
521
+ app.get('/health/email', emailHealthCheck);
522
+ ```
523
+
524
+ ## 🌍 Environment Variables
525
+
526
+ ### **Resend (Recommended)**
527
+
528
+ ```bash
529
+ # Modern email service with great deliverability
530
+ RESEND_API_KEY=re_your_api_key_here
531
+
532
+ # Optional: Custom FROM address
533
+ BLOOM_EMAIL_FROM_EMAIL=noreply@yourdomain.com
534
+ BLOOM_EMAIL_FROM_NAME="Your App Name"
535
+ ```
536
+
537
+ ### **SMTP (Universal)**
538
+
539
+ ```bash
540
+ # Works with Gmail, Outlook, custom servers
541
+ SMTP_HOST=smtp.gmail.com
542
+ SMTP_PORT=587
543
+ SMTP_USER=your-email@gmail.com
544
+ SMTP_PASS=your-app-password
545
+
546
+ # Optional: Security settings
547
+ SMTP_SECURE=false # true for port 465, false for 587
548
+ ```
549
+
550
+ ### **Console (Development)**
551
+
552
+ ```bash
553
+ # No configuration needed!
554
+ # Automatically used when no email provider is set
555
+
556
+ # Optional: Customize console output
557
+ BLOOM_EMAIL_CONSOLE_FORMAT=detailed # or 'simple'
558
+ BLOOM_EMAIL_CONSOLE_PREVIEW=true # Show email content
559
+ ```
560
+
561
+ ## 🔧 Platform Setup
562
+
563
+ ### **Local Development**
564
+
565
+ ```bash
566
+ # No setup needed - beautiful console preview
567
+ npm start
568
+ ```
569
+
570
+ ### **Resend (Recommended)**
571
+
572
+ 1. Sign up at [resend.com](https://resend.com)
573
+ 2. Get your API key
574
+ 3. Set `RESEND_API_KEY=re_your_key`
575
+ 4. Done! ✅
576
+
577
+ ### **Gmail SMTP**
578
+
579
+ ```bash
580
+ # Enable 2FA and create App Password
581
+ SMTP_HOST=smtp.gmail.com
582
+ SMTP_PORT=587
583
+ SMTP_USER=your-email@gmail.com
584
+ SMTP_PASS=your-16-char-app-password
585
+ ```
586
+
587
+ ### **Outlook/Hotmail SMTP**
588
+
589
+ ```bash
590
+ SMTP_HOST=smtp-mail.outlook.com
591
+ SMTP_PORT=587
592
+ SMTP_USER=your-email@outlook.com
593
+ SMTP_PASS=your-password
594
+ ```
595
+
596
+ ### **Custom SMTP Server**
597
+
598
+ ```bash
599
+ SMTP_HOST=mail.yourdomain.com
600
+ SMTP_PORT=587
601
+ SMTP_USER=noreply@yourdomain.com
602
+ SMTP_PASS=your-password
603
+ SMTP_SECURE=false
604
+ ```
605
+
606
+ ## 🚀 Production Deployment
607
+
608
+ ### **Vercel**
609
+
610
+ ```bash
611
+ # Add to Vercel environment variables
612
+ RESEND_API_KEY=re_your_api_key
613
+ BLOOM_EMAIL_FROM_EMAIL=noreply@yourdomain.com
614
+ ```
615
+
616
+ ### **Railway/Heroku**
617
+
618
+ ```bash
619
+ # Add to platform environment variables
620
+ RESEND_API_KEY=re_your_api_key
621
+ ```
622
+
623
+ ### **Docker**
624
+
625
+ ```yaml
626
+ # docker-compose.yml
627
+ services:
628
+ app:
629
+ image: my-app
630
+ environment:
631
+ RESEND_API_KEY: re_your_api_key
632
+ BLOOM_EMAIL_FROM_EMAIL: noreply@yourdomain.com
633
+ ```
634
+
635
+ ### **AWS/VPS**
636
+
637
+ ```bash
638
+ # Add to your deployment script
639
+ export RESEND_API_KEY=re_your_api_key
640
+ export BLOOM_EMAIL_FROM_EMAIL=noreply@yourdomain.com
641
+ ```
642
+
643
+ ## 🤖 LLM Guidelines
644
+
645
+ ### **Essential Patterns**
646
+
647
+ ```typescript
648
+ // ✅ ALWAYS use these patterns
649
+ import { emailClass } from '@bloomneo/appkit/email';
650
+ const email = emailClass.get();
651
+
652
+ // ✅ Basic email sending
653
+ await email.send({
654
+ to: 'user@example.com',
655
+ subject: 'Subject',
656
+ text: 'Plain text content',
657
+ });
658
+
659
+ // ✅ HTML email with fallback
660
+ await email.send({
661
+ to: 'user@example.com',
662
+ subject: 'Subject',
663
+ html: '<h1>HTML content</h1>',
664
+ text: 'Plain text fallback',
665
+ });
666
+
667
+ // ✅ Convenience methods
668
+ await emailClass.sendText('user@example.com', 'Subject', 'Message');
669
+
670
+ // ✅ Template usage
671
+ await email.sendTemplate('welcome', {
672
+ to: 'user@example.com',
673
+ name: 'John',
674
+ appName: 'MyApp',
675
+ });
676
+ ```
677
+
678
+ ### **Anti-Patterns to Avoid**
679
+
680
+ ```typescript
681
+ // ❌ DON'T create email strategies directly
682
+ const resend = new ResendStrategy(); // Use emailClass.get() instead
683
+
684
+ // ❌ DON'T forget error handling
685
+ await email.send(data); // Check result.success
686
+
687
+ // ❌ DON'T send without subject
688
+ await email.send({ to: 'user@example.com', text: 'Hi' }); // Missing subject
689
+
690
+ // ❌ DON'T send without content
691
+ await email.send({ to: 'user@example.com', subject: 'Hi' }); // Missing text/html
692
+
693
+ // ❌ DON'T ignore email validation
694
+ await email.send({ to: 'invalid-email', subject: 'Hi', text: 'Hello' });
695
+ ```
696
+
697
+ ### **Common Patterns**
698
+
699
+ ```typescript
700
+ // Email with error handling
701
+ const result = await emailClass.send({
702
+ to: user.email,
703
+ subject: 'Welcome!',
704
+ text: 'Welcome to our app!',
705
+ });
706
+
707
+ if (!result.success) {
708
+ console.error('Email failed:', result.error);
709
+ }
710
+
711
+ // Conditional email sending
712
+ if (emailClass.hasProvider()) {
713
+ await emailClass.send(emailData);
714
+ } else {
715
+ console.log('No email provider configured');
716
+ }
717
+
718
+ // Batch email sending
719
+ const emails = users.map((user) => ({
720
+ to: user.email,
721
+ subject: 'Newsletter',
722
+ html: newsletterHtml,
723
+ }));
724
+
725
+ await emailClass.get().sendBatch(emails);
726
+ ```
727
+
728
+ ## 📈 Performance
729
+
730
+ - **Resend Strategy**: ~100-500ms per email
731
+ - **SMTP Strategy**: ~200-1000ms per email (depends on server)
732
+ - **Console Strategy**: ~1-5ms (instant logging)
733
+ - **Batch Sending**: Processes 10 emails concurrently by default
734
+ - **Memory Usage**: <2MB baseline usage
735
+
736
+ ## 🔍 TypeScript Support
737
+
738
+ ```typescript
739
+ import type { EmailData, EmailResult } from '@bloomneo/appkit/email';
740
+
741
+ // Strongly typed email operations
742
+ const emailData: EmailData = {
743
+ to: 'user@example.com',
744
+ subject: 'Hello',
745
+ text: 'Hello world!',
746
+ };
747
+
748
+ const result: EmailResult = await emailClass.send(emailData);
749
+ ```
750
+
751
+ ## 📄 License
752
+
753
+ MIT © [Bloomneo](https://github.com/bloomneo)
754
+
755
+ ---
756
+
757
+ <p align="center">
758
+ Built with ❤️ in India by the <a href="https://github.com/orgs/bloomneo/people">Bloomneo Team</a>
759
+ </p>