@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,729 @@
1
+ # @bloomneo/appkit - Event 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 event-driven architecture that just works with automatic
7
+ > Redis/Memory strategy
8
+
9
+ **One function** returns an event system with automatic Redis detection. Zero
10
+ configuration needed, production-ready distribution by default, with built-in
11
+ wildcard patterns and event history.
12
+
13
+ ## 🚀 Why Choose This?
14
+
15
+ - **⚡ One Function** - Just `eventClass.get()`, everything else is automatic
16
+ - **🔄 Auto Strategy** - REDIS_URL → Distributed, No Redis → Memory
17
+ - **🔧 Zero Configuration** - Smart defaults for everything
18
+ - **🌟 Wildcard Events** - Listen to `user.*` patterns automatically
19
+ - **📚 Event History** - Built-in replay and debugging
20
+ - **⚖️ Scales Perfectly** - Development → Production with no code changes
21
+ - **🤖 AI-Ready** - Optimized for LLM code generation
22
+
23
+ ## 📦 Installation
24
+
25
+ ```bash
26
+ npm install @bloomneo/appkit
27
+ ```
28
+
29
+ ## 🏃‍♂️ Quick Start (30 seconds)
30
+
31
+ ### Basic Events (Memory Strategy)
32
+
33
+ ```typescript
34
+ import { eventClass } from '@bloomneo/appkit/event';
35
+
36
+ const events = eventClass.get();
37
+
38
+ // Listen to events
39
+ events.on('user.signup', (data) => {
40
+ console.log('New user:', data.email);
41
+ });
42
+
43
+ // Emit events
44
+ await events.emit('user.signup', {
45
+ email: 'john@example.com',
46
+ userId: 123,
47
+ });
48
+ ```
49
+
50
+ ### Distributed Events (Redis Strategy)
51
+
52
+ ```bash
53
+ # Just set Redis URL - everything else is automatic
54
+ REDIS_URL=redis://localhost:6379
55
+ ```
56
+
57
+ ```typescript
58
+ import { eventClass } from '@bloomneo/appkit/event';
59
+
60
+ const events = eventClass.get();
61
+
62
+ // Same code - now distributed across all servers!
63
+ events.on('order.completed', (data) => {
64
+ console.log('Order completed:', data.orderId);
65
+ });
66
+
67
+ await events.emit('order.completed', {
68
+ orderId: 'order-123',
69
+ amount: 99.99,
70
+ });
71
+ ```
72
+
73
+ **That's it!** Events automatically work across all your servers.
74
+
75
+ ## 🧠 Mental Model
76
+
77
+ ### **Strategy Auto-Detection**
78
+
79
+ This is the core innovation. Environment variables determine strategy:
80
+
81
+ ```bash
82
+ # Development/Single Server
83
+ # (no Redis URL)
84
+ → Memory Strategy: Fast local events
85
+
86
+ # Production/Multi-Server
87
+ REDIS_URL=redis://localhost:6379
88
+ → Redis Strategy: Distributed events across all servers
89
+ ```
90
+
91
+ ### **Namespace Isolation**
92
+
93
+ ```typescript
94
+ // Different parts of your app can use separate event channels
95
+ const userEvents = eventClass.get('users'); // users:*
96
+ const orderEvents = eventClass.get('orders'); // orders:*
97
+ const emailEvents = eventClass.get('emails'); // emails:*
98
+
99
+ // Events don't interfere with each other
100
+ userEvents.emit('created', data); // → users:created
101
+ orderEvents.emit('created', data); // → orders:created
102
+ ```
103
+
104
+ ## 📖 Complete API Reference
105
+
106
+ ### Core Function
107
+
108
+ ```typescript
109
+ const events = eventClass.get(namespace?); // One function, everything you need
110
+ ```
111
+
112
+ ### Event Methods
113
+
114
+ ```typescript
115
+ // Listen to events
116
+ events.on('event.name', (data) => {
117
+ /* handler */
118
+ });
119
+ events.once('event.name', (data) => {
120
+ /* one-time handler */
121
+ });
122
+
123
+ // Emit events
124
+ await events.emit('event.name', { key: 'value' });
125
+ await events.emitBatch([
126
+ { event: 'user.created', data: { userId: 1 } },
127
+ { event: 'user.created', data: { userId: 2 } },
128
+ ]);
129
+
130
+ // Remove listeners
131
+ events.off('event.name'); // Remove all listeners
132
+ events.off('event.name', handler); // Remove specific listener
133
+
134
+ // Wildcard patterns
135
+ events.on('user.*', (eventName, data) => {
136
+ console.log(`User event: ${eventName}`, data);
137
+ });
138
+ ```
139
+
140
+ ### Utility Methods
141
+
142
+ ```typescript
143
+ // Get event history for debugging
144
+ const history = await events.history('user.created', 10); // Last 10 events
145
+
146
+ // Get current listeners
147
+ const listeners = events.getListeners('user.*');
148
+
149
+ // Get strategy info
150
+ const strategy = events.getStrategy(); // 'redis' or 'memory'
151
+ const config = events.getConfig();
152
+
153
+ // Cleanup
154
+ await events.disconnect();
155
+ ```
156
+
157
+ ### Global Methods
158
+
159
+ ```typescript
160
+ // System-wide operations
161
+ eventClass.getStrategy(); // Current strategy
162
+ eventClass.getActiveNamespaces(); // All active namespaces
163
+ eventClass.hasRedis(); // Check if Redis available
164
+
165
+ // Broadcast to all namespaces (use sparingly)
166
+ await eventClass.broadcast('system.shutdown');
167
+
168
+ // Cleanup for testing
169
+ await eventClass.clear();
170
+ await eventClass.reset(newConfig);
171
+ ```
172
+
173
+ ## 🎯 Usage Examples
174
+
175
+ ### **Express API with Events**
176
+
177
+ ```typescript
178
+ import express from 'express';
179
+ import { eventClass } from '@bloomneo/appkit/event';
180
+
181
+ const app = express();
182
+ const events = eventClass.get('api');
183
+
184
+ // Listen to user events
185
+ events.on('user.created', async (user) => {
186
+ console.log('📧 Sending welcome email to:', user.email);
187
+ await sendWelcomeEmail(user);
188
+ });
189
+
190
+ events.on('user.*', (eventName, data) => {
191
+ console.log(`👤 User event: ${eventName}`, data);
192
+ });
193
+
194
+ // User registration endpoint
195
+ app.post('/register', async (req, res) => {
196
+ const { email, password } = req.body;
197
+
198
+ try {
199
+ // Create user
200
+ const user = await createUser({ email, password });
201
+
202
+ // Emit event - triggers welcome email automatically
203
+ await events.emit('user.created', {
204
+ userId: user.id,
205
+ email: user.email,
206
+ source: 'registration',
207
+ });
208
+
209
+ res.json({ success: true, userId: user.id });
210
+ } catch (error) {
211
+ await events.emit('user.registration.failed', {
212
+ email,
213
+ error: error.message,
214
+ timestamp: new Date().toISOString(),
215
+ });
216
+
217
+ res.status(400).json({ error: error.message });
218
+ }
219
+ });
220
+
221
+ app.listen(3000, () => {
222
+ console.log('🚀 Server started with events:', events.getStrategy());
223
+ });
224
+ ```
225
+
226
+ ### **Microservices Communication**
227
+
228
+ ```typescript
229
+ // User Service
230
+ import { eventClass } from '@bloomneo/appkit/event';
231
+
232
+ const userEvents = eventClass.get('users');
233
+
234
+ export class UserService {
235
+ async createUser(userData) {
236
+ const user = await this.db.create(userData);
237
+
238
+ // Notify other services
239
+ await userEvents.emit('user.created', {
240
+ userId: user.id,
241
+ email: user.email,
242
+ plan: user.plan,
243
+ createdAt: user.createdAt,
244
+ });
245
+
246
+ return user;
247
+ }
248
+ }
249
+ ```
250
+
251
+ ```typescript
252
+ // Email Service (separate server/container)
253
+ import { eventClass } from '@bloomneo/appkit/event';
254
+
255
+ const emailEvents = eventClass.get('emails');
256
+ const userEvents = eventClass.get('users'); // Same namespace, distributed
257
+
258
+ // Listen to user events from User Service
259
+ userEvents.on('user.created', async (userData) => {
260
+ await emailEvents.emit('send.welcome', {
261
+ to: userData.email,
262
+ userId: userData.userId,
263
+ plan: userData.plan,
264
+ });
265
+ });
266
+
267
+ // Process email queue
268
+ emailEvents.on('send.*', async (eventName, emailData) => {
269
+ const emailType = eventName.split('.')[1]; // 'welcome', 'reset', etc.
270
+ await sendEmail(emailType, emailData);
271
+ });
272
+ ```
273
+
274
+ ### **Background Jobs & Queues**
275
+
276
+ ```typescript
277
+ import { eventClass } from '@bloomneo/appkit/event';
278
+
279
+ const jobEvents = eventClass.get('jobs');
280
+
281
+ // Job processor
282
+ class JobProcessor {
283
+ constructor() {
284
+ jobEvents.on('job.email.*', this.processEmailJob.bind(this));
285
+ jobEvents.on('job.image.*', this.processImageJob.bind(this));
286
+ }
287
+
288
+ async processEmailJob(eventName, jobData) {
289
+ const jobType = eventName.split('.')[2];
290
+
291
+ try {
292
+ await this.sendEmail(jobType, jobData);
293
+ await jobEvents.emit('job.completed', {
294
+ jobId: jobData.jobId,
295
+ type: 'email',
296
+ completedAt: new Date().toISOString(),
297
+ });
298
+ } catch (error) {
299
+ await jobEvents.emit('job.failed', {
300
+ jobId: jobData.jobId,
301
+ error: error.message,
302
+ retryCount: jobData.retryCount || 0,
303
+ });
304
+ }
305
+ }
306
+ }
307
+
308
+ // Usage
309
+ await jobEvents.emit('job.email.welcome', {
310
+ jobId: crypto.randomUUID(),
311
+ email: 'user@example.com',
312
+ name: 'John',
313
+ });
314
+ ```
315
+
316
+ ## 🧪 Testing
317
+
318
+ ```typescript
319
+ import { eventClass } from '@bloomneo/appkit/event';
320
+
321
+ describe('Events', () => {
322
+ afterEach(() => eventClass.clear()); // Essential cleanup
323
+
324
+ test('basic event flow', async () => {
325
+ const events = eventClass.get('test');
326
+
327
+ const received = [];
328
+ events.on('user.created', (data) => received.push(data));
329
+
330
+ await events.emit('user.created', { userId: 123 });
331
+
332
+ expect(received[0].userId).toBe(123);
333
+ });
334
+ });
335
+ ```
336
+
337
+ ## ⚠️ Common Mistakes
338
+
339
+ ### **1. Wrong Event Names**
340
+
341
+ ```typescript
342
+ // ❌ Bad patterns
343
+ events.on('userCreated', handler); // Use dots
344
+ events.on('USER_CREATED', handler); // Use lowercase
345
+ events.on('created', handler); // Too generic
346
+
347
+ // ✅ Good patterns
348
+ events.on('user.created', handler);
349
+ events.on('order.payment.failed', handler);
350
+ ```
351
+
352
+ ### **2. Missing Cleanup**
353
+
354
+ ```typescript
355
+ // ❌ Memory leaks in tests
356
+ test('my test', () => {
357
+ const events = eventClass.get('test');
358
+ // Missing: await eventClass.clear();
359
+ });
360
+
361
+ // ✅ Always clean up
362
+ afterEach(() => eventClass.clear());
363
+ ```
364
+
365
+ ### **3. Memory Strategy in Production**
366
+
367
+ ```typescript
368
+ // ❌ Single server only
369
+ // Without REDIS_URL, events don't work across servers
370
+
371
+ // ✅ Set Redis for distributed events
372
+ REDIS_URL=redis://localhost:6379
373
+ ```
374
+
375
+ ### **4. Ignoring Emit Failures**
376
+
377
+ ```typescript
378
+ // ❌ Silent failures
379
+ await events.emit('user.created', data);
380
+
381
+ // ✅ Check results
382
+ const result = await events.emit('user.created', data);
383
+ if (!result) console.error('Event failed');
384
+ ```
385
+
386
+ ## 🚨 Error Handling
387
+
388
+ ### **Basic Pattern**
389
+
390
+ ```typescript
391
+ events.on('payment.process', async (payment) => {
392
+ try {
393
+ await processPayment(payment);
394
+ await events.emit('payment.completed', payment);
395
+ } catch (error) {
396
+ await events.emit('payment.failed', {
397
+ ...payment,
398
+ error: error.message,
399
+ });
400
+ }
401
+ });
402
+ ```
403
+
404
+ ### **Redis Connection Errors**
405
+
406
+ ```typescript
407
+ async function emitSafely(event, data) {
408
+ const result = await events.emit(event, data);
409
+
410
+ if (!result) {
411
+ console.warn(`Event failed: ${event}`, data);
412
+ // Store for retry or use fallback
413
+ await storeFailedEvent(event, data);
414
+ }
415
+
416
+ return result;
417
+ }
418
+ ```
419
+
420
+ ## 🔧 Startup Validation
421
+
422
+ ### **Basic Validation**
423
+
424
+ ```typescript
425
+ import { eventClass } from '@bloomneo/appkit/event';
426
+
427
+ async function startApp() {
428
+ // Validate events at startup
429
+ eventClass.validateConfig();
430
+
431
+ const strategy = eventClass.getStrategy();
432
+ console.log(`🚀 Events: ${strategy} strategy`);
433
+
434
+ app.listen(3000);
435
+ }
436
+ ```
437
+
438
+ ### **Production Checks**
439
+
440
+ ```typescript
441
+ if (process.env.NODE_ENV === 'production' && !eventClass.hasRedis()) {
442
+ throw new Error('Redis required in production for distributed events');
443
+ }
444
+ ```
445
+
446
+ ### **Health Check**
447
+
448
+ ```typescript
449
+ app.get('/health/events', (req, res) => {
450
+ const stats = eventClass.getStats();
451
+
452
+ res.json({
453
+ status: 'healthy',
454
+ strategy: eventClass.getStrategy(),
455
+ connected: stats.connected,
456
+ redis: eventClass.hasRedis(),
457
+ });
458
+ });
459
+ ```
460
+
461
+ ## 🌍 Environment Variables
462
+
463
+ ### Basic Configuration
464
+
465
+ ```bash
466
+ # Strategy selection (auto-detected)
467
+ REDIS_URL=redis://localhost:6379 # Enables Redis strategy
468
+ # No REDIS_URL = Memory strategy
469
+
470
+ # Service identification
471
+ BLOOM_SERVICE_NAME=my-app # Used in namespacing
472
+ BLOOM_EVENT_NAMESPACE=production # Custom namespace
473
+
474
+ # Event history
475
+ BLOOM_EVENT_HISTORY_ENABLED=true # Default: true
476
+ BLOOM_EVENT_HISTORY_SIZE=100 # Default: 100
477
+ ```
478
+
479
+ ### Redis Configuration (Advanced)
480
+
481
+ ```bash
482
+ # Redis connection
483
+ REDIS_URL=redis://localhost:6379
484
+ REDIS_PASSWORD=your-redis-password
485
+
486
+ # Redis event settings
487
+ BLOOM_EVENT_REDIS_RETRIES=3 # Default: 3
488
+ BLOOM_EVENT_REDIS_RETRY_DELAY=1000 # Default: 1000ms
489
+ BLOOM_EVENT_REDIS_CONNECT_TIMEOUT=10000 # Default: 10s
490
+ BLOOM_EVENT_REDIS_COMMAND_TIMEOUT=5000 # Default: 5s
491
+ BLOOM_EVENT_REDIS_PREFIX=events # Default: events
492
+ ```
493
+
494
+ ### Memory Configuration (Advanced)
495
+
496
+ ```bash
497
+ # Memory strategy settings
498
+ BLOOM_EVENT_MEMORY_MAX_LISTENERS=1000 # Default: 1000
499
+ BLOOM_EVENT_MEMORY_HISTORY=100 # Default: 100
500
+ BLOOM_EVENT_MEMORY_CHECK_INTERVAL=30000 # Default: 30s
501
+ BLOOM_EVENT_MEMORY_GC=true # Default: true
502
+ ```
503
+
504
+ ## 🎨 Event Patterns
505
+
506
+ ### **Naming Conventions**
507
+
508
+ ```typescript
509
+ // ✅ Good event names
510
+ 'user.created'; // entity.action
511
+ 'order.payment.failed'; // entity.context.action
512
+ 'email.sent'; // service.action
513
+ 'notification.push.delivered'; // service.type.action
514
+
515
+ // ❌ Avoid these patterns
516
+ 'userCreated'; // Use dots for hierarchy
517
+ 'USER_CREATED'; // Use lowercase
518
+ 'created'; // Too generic
519
+ 'user-created'; // Use dots, not dashes
520
+ ```
521
+
522
+ ### **Wildcard Patterns**
523
+
524
+ ```typescript
525
+ // Listen to patterns
526
+ events.on('user.*', handler); // All user events
527
+ events.on('*.created', handler); // All creation events
528
+ events.on('order.*.failed', handler); // All order failures
529
+
530
+ // Event hierarchy examples
531
+ 'user.created' → Matches: user.*
532
+ 'user.updated.profile' → Matches: user.*, user.updated.*
533
+ 'order.payment.failed' → Matches: order.*, order.payment.*, *.failed
534
+ ```
535
+
536
+ ### **Data Structure Conventions**
537
+
538
+ ```typescript
539
+ // ✅ Good event data structure
540
+ await events.emit('user.created', {
541
+ // Always include entity ID
542
+ userId: 123,
543
+
544
+ // Include relevant entity data
545
+ email: 'user@example.com',
546
+ plan: 'premium',
547
+
548
+ // Include context
549
+ source: 'registration',
550
+ ip: '192.168.1.1',
551
+
552
+ // Include timing
553
+ createdAt: new Date().toISOString(),
554
+ });
555
+ ```
556
+
557
+ ## 🔄 Development vs Production
558
+
559
+ ### **Development Mode**
560
+
561
+ ```bash
562
+ # No Redis URL = Memory strategy
563
+ NODE_ENV=development
564
+ ```
565
+
566
+ ```typescript
567
+ // Fast local events, detailed logging
568
+ const events = eventClass.get();
569
+ await events.emit('test.event', { data: 'value' });
570
+ // ✅ [AppKit] Event emitted: test.event { data: 'value' }
571
+ ```
572
+
573
+ ### **Production Mode**
574
+
575
+ ```bash
576
+ # Redis URL = Distributed strategy
577
+ NODE_ENV=production
578
+ REDIS_URL=redis://production-redis:6379
579
+ ```
580
+
581
+ ```typescript
582
+ // Same code - now distributed across all servers
583
+ const events = eventClass.get();
584
+ await events.emit('test.event', { data: 'value' });
585
+ // Events work across all server instances automatically
586
+ ```
587
+
588
+ ## 🤖 LLM Guidelines
589
+
590
+ ### **Essential Patterns**
591
+
592
+ ```typescript
593
+ // ✅ ALWAYS use these patterns
594
+ import { eventClass } from '@bloomneo/appkit/event';
595
+ const events = eventClass.get('namespace');
596
+
597
+ // ✅ Event listening
598
+ events.on('entity.action', (data) => {
599
+ // Handle event
600
+ });
601
+
602
+ // ✅ Event emitting
603
+ await events.emit('entity.action', {
604
+ entityId: 123,
605
+ timestamp: new Date().toISOString(),
606
+ });
607
+
608
+ // ✅ Wildcard patterns
609
+ events.on('user.*', (eventName, data) => {
610
+ console.log(`User event: ${eventName}`, data);
611
+ });
612
+
613
+ // ✅ One-time listeners
614
+ events.once('app.ready', () => {
615
+ console.log('App is ready');
616
+ });
617
+
618
+ // ✅ Cleanup
619
+ events.off('event.name');
620
+ events.off('event.name', specificHandler);
621
+ ```
622
+
623
+ ### **Anti-Patterns to Avoid**
624
+
625
+ ```typescript
626
+ // ❌ DON'T create EventClass directly
627
+ import { EventClass } from '@bloomneo/appkit/event';
628
+ const events = new EventClass(config, namespace); // Wrong!
629
+
630
+ // ❌ DON'T forget to handle async properly
631
+ events.emit('event', data); // Missing await!
632
+
633
+ // ❌ DON'T use bad event names
634
+ events.on('userCreated', handler); // Use dots: 'user.created'
635
+ events.on('USER_CREATED', handler); // Use lowercase
636
+ events.on('created', handler); // Too generic
637
+
638
+ // ❌ DON'T forget cleanup in tests
639
+ test('my test', () => {
640
+ // ... test code
641
+ // Missing: await eventClass.clear();
642
+ });
643
+
644
+ // ❌ DON'T emit events without data structure
645
+ await events.emit('user.created', userId); // Should be object with userId property
646
+ ```
647
+
648
+ ### **Common Patterns**
649
+
650
+ ```typescript
651
+ // Emit with proper data structure
652
+ await events.emit('user.created', {
653
+ userId: user.id,
654
+ email: user.email,
655
+ source: 'registration',
656
+ createdAt: new Date().toISOString(),
657
+ });
658
+
659
+ // Handle errors in event listeners
660
+ events.on('payment.process', async (payment) => {
661
+ try {
662
+ await processPayment(payment);
663
+ await events.emit('payment.completed', payment);
664
+ } catch (error) {
665
+ await events.emit('payment.failed', {
666
+ ...payment,
667
+ error: error.message,
668
+ });
669
+ }
670
+ });
671
+
672
+ // Use namespaces for organization
673
+ const userEvents = eventClass.get('users');
674
+ const orderEvents = eventClass.get('orders');
675
+ const emailEvents = eventClass.get('emails');
676
+
677
+ // Batch operations for efficiency
678
+ await events.emitBatch([
679
+ { event: 'user.created', data: { userId: 1 } },
680
+ { event: 'user.created', data: { userId: 2 } },
681
+ ]);
682
+ ```
683
+
684
+ ## 📈 Performance
685
+
686
+ - **Memory Strategy**: ~0.01ms per event (local EventEmitter)
687
+ - **Redis Strategy**: ~2-5ms per event (network + serialization)
688
+ - **Wildcard Matching**: ~0.1ms per pattern check
689
+ - **Event History**: <1MB per 1000 events
690
+ - **Memory Usage**: <10MB baseline per namespace
691
+
692
+ ## 🔍 TypeScript Support
693
+
694
+ Full TypeScript support with comprehensive interfaces:
695
+
696
+ ```typescript
697
+ import type {
698
+ Event,
699
+ EventHandler,
700
+ WildcardHandler,
701
+ BatchEvent,
702
+ EventHistoryEntry,
703
+ } from '@bloomneo/appkit/event';
704
+
705
+ // Strongly typed event handling
706
+ const events: Event = eventClass.get('users');
707
+
708
+ const handler: EventHandler = (data: UserData) => {
709
+ console.log('User created:', data.email);
710
+ };
711
+
712
+ const wildcardHandler: WildcardHandler = (eventName: string, data: any) => {
713
+ console.log(`Event ${eventName}:`, data);
714
+ };
715
+
716
+ events.on('user.created', handler);
717
+ events.on('user.*', wildcardHandler);
718
+ ```
719
+
720
+ ## 📄 License
721
+
722
+ MIT © [Bloomneo](https://github.com/bloomneo)
723
+
724
+ ---
725
+
726
+ <p align="center">
727
+ <strong>Built with ❤️ by the <a href="https://github.com/bloomneo">Bloomneo Team</a></strong><br>
728
+ Because event-driven architecture should be simple, not rocket science.
729
+ </p>