@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.
Files changed (262) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +902 -0
  3. package/bin/appkit.js +71 -0
  4. package/bin/commands/generate.js +1050 -0
  5. package/bin/templates/backend/README.md.template +39 -0
  6. package/bin/templates/backend/api.http.template +0 -0
  7. package/bin/templates/backend/docs/APPKIT_CLI.md +507 -0
  8. package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +61 -0
  9. package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +2539 -0
  10. package/bin/templates/backend/package.json.template +34 -0
  11. package/bin/templates/backend/src/api/features/welcome/welcome.http.template +29 -0
  12. package/bin/templates/backend/src/api/features/welcome/welcome.route.ts.template +36 -0
  13. package/bin/templates/backend/src/api/features/welcome/welcome.service.ts.template +88 -0
  14. package/bin/templates/backend/src/api/features/welcome/welcome.types.ts.template +18 -0
  15. package/bin/templates/backend/src/api/lib/api-router.ts.template +84 -0
  16. package/bin/templates/backend/src/api/server.ts.template +188 -0
  17. package/bin/templates/backend/tsconfig.api.json.template +24 -0
  18. package/bin/templates/backend/tsconfig.json.template +40 -0
  19. package/bin/templates/feature/feature.http.template +63 -0
  20. package/bin/templates/feature/feature.route.ts.template +36 -0
  21. package/bin/templates/feature/feature.service.ts.template +81 -0
  22. package/bin/templates/feature/feature.types.ts.template +23 -0
  23. package/bin/templates/feature-db/feature.http.template +63 -0
  24. package/bin/templates/feature-db/feature.model.ts.template +74 -0
  25. package/bin/templates/feature-db/feature.route.ts.template +58 -0
  26. package/bin/templates/feature-db/feature.service.ts.template +231 -0
  27. package/bin/templates/feature-db/feature.types.ts.template +25 -0
  28. package/bin/templates/feature-db/schema-addition.prisma.template +9 -0
  29. package/bin/templates/feature-db/seeding/README.md.template +57 -0
  30. package/bin/templates/feature-db/seeding/feature.seed.js.template +67 -0
  31. package/bin/templates/feature-user/schema-addition.prisma.template +19 -0
  32. package/bin/templates/feature-user/user.http.template +157 -0
  33. package/bin/templates/feature-user/user.model.ts.template +244 -0
  34. package/bin/templates/feature-user/user.route.ts.template +379 -0
  35. package/bin/templates/feature-user/user.seed.js.template +182 -0
  36. package/bin/templates/feature-user/user.service.ts.template +426 -0
  37. package/bin/templates/feature-user/user.types.ts.template +127 -0
  38. package/dist/auth/auth.d.ts +182 -0
  39. package/dist/auth/auth.d.ts.map +1 -0
  40. package/dist/auth/auth.js +477 -0
  41. package/dist/auth/auth.js.map +1 -0
  42. package/dist/auth/defaults.d.ts +104 -0
  43. package/dist/auth/defaults.d.ts.map +1 -0
  44. package/dist/auth/defaults.js +374 -0
  45. package/dist/auth/defaults.js.map +1 -0
  46. package/dist/auth/index.d.ts +70 -0
  47. package/dist/auth/index.d.ts.map +1 -0
  48. package/dist/auth/index.js +94 -0
  49. package/dist/auth/index.js.map +1 -0
  50. package/dist/cache/cache.d.ts +118 -0
  51. package/dist/cache/cache.d.ts.map +1 -0
  52. package/dist/cache/cache.js +249 -0
  53. package/dist/cache/cache.js.map +1 -0
  54. package/dist/cache/defaults.d.ts +63 -0
  55. package/dist/cache/defaults.d.ts.map +1 -0
  56. package/dist/cache/defaults.js +193 -0
  57. package/dist/cache/defaults.js.map +1 -0
  58. package/dist/cache/index.d.ts +101 -0
  59. package/dist/cache/index.d.ts.map +1 -0
  60. package/dist/cache/index.js +203 -0
  61. package/dist/cache/index.js.map +1 -0
  62. package/dist/cache/strategies/memory.d.ts +138 -0
  63. package/dist/cache/strategies/memory.d.ts.map +1 -0
  64. package/dist/cache/strategies/memory.js +348 -0
  65. package/dist/cache/strategies/memory.js.map +1 -0
  66. package/dist/cache/strategies/redis.d.ts +105 -0
  67. package/dist/cache/strategies/redis.d.ts.map +1 -0
  68. package/dist/cache/strategies/redis.js +318 -0
  69. package/dist/cache/strategies/redis.js.map +1 -0
  70. package/dist/config/config.d.ts +62 -0
  71. package/dist/config/config.d.ts.map +1 -0
  72. package/dist/config/config.js +107 -0
  73. package/dist/config/config.js.map +1 -0
  74. package/dist/config/defaults.d.ts +44 -0
  75. package/dist/config/defaults.d.ts.map +1 -0
  76. package/dist/config/defaults.js +217 -0
  77. package/dist/config/defaults.js.map +1 -0
  78. package/dist/config/index.d.ts +105 -0
  79. package/dist/config/index.d.ts.map +1 -0
  80. package/dist/config/index.js +163 -0
  81. package/dist/config/index.js.map +1 -0
  82. package/dist/database/adapters/mongoose.d.ts +106 -0
  83. package/dist/database/adapters/mongoose.d.ts.map +1 -0
  84. package/dist/database/adapters/mongoose.js +480 -0
  85. package/dist/database/adapters/mongoose.js.map +1 -0
  86. package/dist/database/adapters/prisma.d.ts +106 -0
  87. package/dist/database/adapters/prisma.d.ts.map +1 -0
  88. package/dist/database/adapters/prisma.js +494 -0
  89. package/dist/database/adapters/prisma.js.map +1 -0
  90. package/dist/database/defaults.d.ts +87 -0
  91. package/dist/database/defaults.d.ts.map +1 -0
  92. package/dist/database/defaults.js +271 -0
  93. package/dist/database/defaults.js.map +1 -0
  94. package/dist/database/index.d.ts +137 -0
  95. package/dist/database/index.d.ts.map +1 -0
  96. package/dist/database/index.js +490 -0
  97. package/dist/database/index.js.map +1 -0
  98. package/dist/email/defaults.d.ts +100 -0
  99. package/dist/email/defaults.d.ts.map +1 -0
  100. package/dist/email/defaults.js +400 -0
  101. package/dist/email/defaults.js.map +1 -0
  102. package/dist/email/email.d.ts +139 -0
  103. package/dist/email/email.d.ts.map +1 -0
  104. package/dist/email/email.js +316 -0
  105. package/dist/email/email.js.map +1 -0
  106. package/dist/email/index.d.ts +176 -0
  107. package/dist/email/index.d.ts.map +1 -0
  108. package/dist/email/index.js +251 -0
  109. package/dist/email/index.js.map +1 -0
  110. package/dist/email/strategies/console.d.ts +90 -0
  111. package/dist/email/strategies/console.d.ts.map +1 -0
  112. package/dist/email/strategies/console.js +268 -0
  113. package/dist/email/strategies/console.js.map +1 -0
  114. package/dist/email/strategies/resend.d.ts +84 -0
  115. package/dist/email/strategies/resend.d.ts.map +1 -0
  116. package/dist/email/strategies/resend.js +266 -0
  117. package/dist/email/strategies/resend.js.map +1 -0
  118. package/dist/email/strategies/smtp.d.ts +77 -0
  119. package/dist/email/strategies/smtp.d.ts.map +1 -0
  120. package/dist/email/strategies/smtp.js +286 -0
  121. package/dist/email/strategies/smtp.js.map +1 -0
  122. package/dist/error/defaults.d.ts +40 -0
  123. package/dist/error/defaults.d.ts.map +1 -0
  124. package/dist/error/defaults.js +75 -0
  125. package/dist/error/defaults.js.map +1 -0
  126. package/dist/error/error.d.ts +140 -0
  127. package/dist/error/error.d.ts.map +1 -0
  128. package/dist/error/error.js +200 -0
  129. package/dist/error/error.js.map +1 -0
  130. package/dist/error/index.d.ts +145 -0
  131. package/dist/error/index.d.ts.map +1 -0
  132. package/dist/error/index.js +145 -0
  133. package/dist/error/index.js.map +1 -0
  134. package/dist/event/defaults.d.ts +111 -0
  135. package/dist/event/defaults.d.ts.map +1 -0
  136. package/dist/event/defaults.js +378 -0
  137. package/dist/event/defaults.js.map +1 -0
  138. package/dist/event/event.d.ts +171 -0
  139. package/dist/event/event.d.ts.map +1 -0
  140. package/dist/event/event.js +391 -0
  141. package/dist/event/event.js.map +1 -0
  142. package/dist/event/index.d.ts +173 -0
  143. package/dist/event/index.d.ts.map +1 -0
  144. package/dist/event/index.js +302 -0
  145. package/dist/event/index.js.map +1 -0
  146. package/dist/event/strategies/memory.d.ts +122 -0
  147. package/dist/event/strategies/memory.d.ts.map +1 -0
  148. package/dist/event/strategies/memory.js +331 -0
  149. package/dist/event/strategies/memory.js.map +1 -0
  150. package/dist/event/strategies/redis.d.ts +115 -0
  151. package/dist/event/strategies/redis.d.ts.map +1 -0
  152. package/dist/event/strategies/redis.js +434 -0
  153. package/dist/event/strategies/redis.js.map +1 -0
  154. package/dist/index.d.ts +58 -0
  155. package/dist/index.d.ts.map +1 -0
  156. package/dist/index.js +72 -0
  157. package/dist/index.js.map +1 -0
  158. package/dist/logger/defaults.d.ts +67 -0
  159. package/dist/logger/defaults.d.ts.map +1 -0
  160. package/dist/logger/defaults.js +213 -0
  161. package/dist/logger/defaults.js.map +1 -0
  162. package/dist/logger/index.d.ts +84 -0
  163. package/dist/logger/index.d.ts.map +1 -0
  164. package/dist/logger/index.js +101 -0
  165. package/dist/logger/index.js.map +1 -0
  166. package/dist/logger/logger.d.ts +165 -0
  167. package/dist/logger/logger.d.ts.map +1 -0
  168. package/dist/logger/logger.js +843 -0
  169. package/dist/logger/logger.js.map +1 -0
  170. package/dist/logger/transports/console.d.ts +102 -0
  171. package/dist/logger/transports/console.d.ts.map +1 -0
  172. package/dist/logger/transports/console.js +276 -0
  173. package/dist/logger/transports/console.js.map +1 -0
  174. package/dist/logger/transports/database.d.ts +153 -0
  175. package/dist/logger/transports/database.d.ts.map +1 -0
  176. package/dist/logger/transports/database.js +539 -0
  177. package/dist/logger/transports/database.js.map +1 -0
  178. package/dist/logger/transports/file.d.ts +146 -0
  179. package/dist/logger/transports/file.d.ts.map +1 -0
  180. package/dist/logger/transports/file.js +464 -0
  181. package/dist/logger/transports/file.js.map +1 -0
  182. package/dist/logger/transports/http.d.ts +128 -0
  183. package/dist/logger/transports/http.d.ts.map +1 -0
  184. package/dist/logger/transports/http.js +401 -0
  185. package/dist/logger/transports/http.js.map +1 -0
  186. package/dist/logger/transports/webhook.d.ts +152 -0
  187. package/dist/logger/transports/webhook.d.ts.map +1 -0
  188. package/dist/logger/transports/webhook.js +485 -0
  189. package/dist/logger/transports/webhook.js.map +1 -0
  190. package/dist/queue/defaults.d.ts +66 -0
  191. package/dist/queue/defaults.d.ts.map +1 -0
  192. package/dist/queue/defaults.js +205 -0
  193. package/dist/queue/defaults.js.map +1 -0
  194. package/dist/queue/index.d.ts +124 -0
  195. package/dist/queue/index.d.ts.map +1 -0
  196. package/dist/queue/index.js +116 -0
  197. package/dist/queue/index.js.map +1 -0
  198. package/dist/queue/queue.d.ts +156 -0
  199. package/dist/queue/queue.d.ts.map +1 -0
  200. package/dist/queue/queue.js +387 -0
  201. package/dist/queue/queue.js.map +1 -0
  202. package/dist/queue/transports/database.d.ts +165 -0
  203. package/dist/queue/transports/database.d.ts.map +1 -0
  204. package/dist/queue/transports/database.js +595 -0
  205. package/dist/queue/transports/database.js.map +1 -0
  206. package/dist/queue/transports/memory.d.ts +143 -0
  207. package/dist/queue/transports/memory.d.ts.map +1 -0
  208. package/dist/queue/transports/memory.js +415 -0
  209. package/dist/queue/transports/memory.js.map +1 -0
  210. package/dist/queue/transports/redis.d.ts +203 -0
  211. package/dist/queue/transports/redis.d.ts.map +1 -0
  212. package/dist/queue/transports/redis.js +744 -0
  213. package/dist/queue/transports/redis.js.map +1 -0
  214. package/dist/security/defaults.d.ts +64 -0
  215. package/dist/security/defaults.d.ts.map +1 -0
  216. package/dist/security/defaults.js +159 -0
  217. package/dist/security/defaults.js.map +1 -0
  218. package/dist/security/index.d.ts +110 -0
  219. package/dist/security/index.d.ts.map +1 -0
  220. package/dist/security/index.js +160 -0
  221. package/dist/security/index.js.map +1 -0
  222. package/dist/security/security.d.ts +138 -0
  223. package/dist/security/security.d.ts.map +1 -0
  224. package/dist/security/security.js +419 -0
  225. package/dist/security/security.js.map +1 -0
  226. package/dist/storage/defaults.d.ts +79 -0
  227. package/dist/storage/defaults.d.ts.map +1 -0
  228. package/dist/storage/defaults.js +358 -0
  229. package/dist/storage/defaults.js.map +1 -0
  230. package/dist/storage/index.d.ts +153 -0
  231. package/dist/storage/index.d.ts.map +1 -0
  232. package/dist/storage/index.js +242 -0
  233. package/dist/storage/index.js.map +1 -0
  234. package/dist/storage/storage.d.ts +151 -0
  235. package/dist/storage/storage.d.ts.map +1 -0
  236. package/dist/storage/storage.js +439 -0
  237. package/dist/storage/storage.js.map +1 -0
  238. package/dist/storage/strategies/local.d.ts +117 -0
  239. package/dist/storage/strategies/local.d.ts.map +1 -0
  240. package/dist/storage/strategies/local.js +368 -0
  241. package/dist/storage/strategies/local.js.map +1 -0
  242. package/dist/storage/strategies/r2.d.ts +130 -0
  243. package/dist/storage/strategies/r2.d.ts.map +1 -0
  244. package/dist/storage/strategies/r2.js +470 -0
  245. package/dist/storage/strategies/r2.js.map +1 -0
  246. package/dist/storage/strategies/s3.d.ts +121 -0
  247. package/dist/storage/strategies/s3.d.ts.map +1 -0
  248. package/dist/storage/strategies/s3.js +461 -0
  249. package/dist/storage/strategies/s3.js.map +1 -0
  250. package/dist/util/defaults.d.ts +77 -0
  251. package/dist/util/defaults.d.ts.map +1 -0
  252. package/dist/util/defaults.js +193 -0
  253. package/dist/util/defaults.js.map +1 -0
  254. package/dist/util/index.d.ts +97 -0
  255. package/dist/util/index.d.ts.map +1 -0
  256. package/dist/util/index.js +165 -0
  257. package/dist/util/index.js.map +1 -0
  258. package/dist/util/util.d.ts +145 -0
  259. package/dist/util/util.d.ts.map +1 -0
  260. package/dist/util/util.js +481 -0
  261. package/dist/util/util.js.map +1 -0
  262. package/package.json +234 -0
@@ -0,0 +1,2539 @@
1
+ # VoilaJSX AppKit - LLM Guide: Core Philosophy & Universal Patterns 🤖
2
+
3
+ > **Essential foundation for AI agents to generate perfect VoilaJSX AppKit
4
+ > code**
5
+
6
+ ## CRITICAL: Read This First
7
+
8
+ This section establishes the core patterns that ALL VoilaJSX AppKit code must
9
+ follow. Every example is production-ready. Every pattern is tested. Every rule
10
+ is absolute.
11
+
12
+ ---
13
+
14
+ ## WHEN TO USE VoilaJSX AppKit
15
+
16
+ ✅ **ALWAYS use AppKit when:**
17
+
18
+ - Building Node.js backend applications
19
+ - Need authentication with roles/permissions
20
+ - Require file storage, caching, queuing, or email
21
+ - Want environment-driven configuration
22
+ - Building multi-tenant or multi-org applications
23
+ - Need production-ready security features
24
+ - Want zero-configuration startup
25
+
26
+ ❌ **NEVER use AppKit when:**
27
+
28
+ - Building frontend React/Vue applications
29
+ - Creating CLI tools or desktop applications
30
+ - Need real-time WebSocket servers as primary feature
31
+ - Building pure microservices with single responsibility
32
+ - Working with non-Node.js environments
33
+
34
+ ---
35
+
36
+ ## COMPLETE MODULE REFERENCE (All 12 Modules)
37
+
38
+ **MEMORIZE this table - it's your single source of truth:**
39
+
40
+ | Module | Import | Class | Object Name | Optional Param | When to Use Param |
41
+ | ------------ | ----------------------------------------------------------- | --------------- | ----------- | ----------------- | --------------------------------- |
42
+ | **Util** | `import { utilClass } from '@bloomneo/appkit/util'` | `utilClass` | `util` | ❌ None | N/A |
43
+ | **Config** | `import { configClass } from '@bloomneo/appkit/config'` | `configClass` | `config` | ❌ None | N/A |
44
+ | **Auth** | `import { authClass } from '@bloomneo/appkit/auth'` | `authClass` | `auth` | ❌ None | N/A |
45
+ | **Logger** | `import { loggerClass } from '@bloomneo/appkit/logger'` | `loggerClass` | `logger` | ✅ `(component?)` | Component-specific logging |
46
+ | **Database** | `import { databaseClass } from '@bloomneo/appkit/database'` | `databaseClass` | `database` | ❌ None | N/A |
47
+ | **Cache** | `import { cacheClass } from '@bloomneo/appkit/cache'` | `cacheClass` | `cache` | ✅ `(namespace?)` | Custom namespace (default: 'app') |
48
+ | **Storage** | `import { storageClass } from '@bloomneo/appkit/storage'` | `storageClass` | `storage` | ❌ None | N/A |
49
+ | **Queue** | `import { queueClass } from '@bloomneo/appkit/queue'` | `queueClass` | `queue` | ❌ None | N/A |
50
+ | **Email** | `import { emailClass } from '@bloomneo/appkit/email'` | `emailClass` | `email` | ❌ None | N/A |
51
+ | **Error** | `import { errorClass } from '@bloomneo/appkit/error'` | `errorClass` | `error` | ❌ None | N/A |
52
+ | **Security** | `import { securityClass } from '@bloomneo/appkit/security'` | `securityClass` | `security` | ❌ None | N/A |
53
+ | **Event** | `import { eventClass } from '@bloomneo/appkit/event'` | `eventClass` | `event` | ✅ `(namespace?)` | Namespace isolation for events |
54
+
55
+ ---
56
+
57
+ ## THE ONE FUNCTION RULE
58
+
59
+ **EVERY module follows the same pattern:**
60
+
61
+ ```javascript
62
+ // ALWAYS use this pattern for EVERY module
63
+ const module = moduleClass.get();
64
+
65
+ // Standard usage - MEMORIZE these exact patterns
66
+ const util = utilClass.get();
67
+ const config = configClass.get();
68
+ const auth = authClass.get();
69
+ const database = databaseClass.get();
70
+ const storage = storageClass.get();
71
+ const queue = queueClass.get();
72
+ const email = emailClass.get();
73
+ const error = errorClass.get();
74
+ const security = securityClass.get();
75
+
76
+ // With optional parameters
77
+ const logger = loggerClass.get('api'); // Component-specific
78
+ const cache = cacheClass.get(); // Default 'app' namespace
79
+ const userCache = cacheClass.get('users'); // Custom namespace
80
+ const event = eventClass.get('notifications'); // Event namespace
81
+ ```
82
+
83
+ **RULE: Never call constructors directly. Always use .get()**
84
+
85
+ ❌ **NEVER do this:**
86
+
87
+ ```javascript
88
+ new AuthClass();
89
+ new DatabaseService();
90
+ new ConfigManager();
91
+ ```
92
+
93
+ ✅ **ALWAYS do this:**
94
+
95
+ ```javascript
96
+ const auth = authClass.get();
97
+ const database = databaseClass.get();
98
+ const config = configClass.get();
99
+ ```
100
+
101
+ ---
102
+
103
+ ## MODULE CATEGORIES & DECISION TREE
104
+
105
+ ### **Infrastructure (4 modules) - Use First**
106
+
107
+ - **Auth**: JWT tokens, role-based permissions, middleware
108
+ - **Database**: Multi-tenant database operations, ORM integration
109
+ - **Security**: CSRF protection, rate limiting, input sanitization, encryption
110
+ - **Error**: HTTP error handling, status codes, middleware
111
+
112
+ ### **Data & Communication (5 modules) - Use Second**
113
+
114
+ - **Cache**: Redis/Memory caching with namespaces
115
+ - **Storage**: File storage (local/S3/R2), CDN integration
116
+ - **Queue**: Background job processing, scheduled tasks
117
+ - **Email**: Multi-provider email sending, templates
118
+ - **Event**: Real-time events, pub/sub messaging
119
+
120
+ ### **Developer Experience (3 modules) - Use Third**
121
+
122
+ - **Util**: Safe object access, array operations, string utilities, performance
123
+ helpers
124
+ - **Config**: Environment variable parsing, application configuration
125
+ - **Logger**: Structured logging, multiple transports
126
+
127
+ ---
128
+
129
+ ## IMPORT PATTERNS
130
+
131
+ **ALWAYS use direct module imports for best tree-shaking:**
132
+
133
+ ✅ **BEST (Perfect tree-shaking):**
134
+
135
+ ```javascript
136
+ import { utilClass } from '@bloomneo/appkit/util';
137
+ import { authClass } from '@bloomneo/appkit/auth';
138
+ import { configClass } from '@bloomneo/appkit/config';
139
+ ```
140
+
141
+ ✅ **GOOD (Still tree-shakable):**
142
+
143
+ ```javascript
144
+ import { utilClass, authClass, configClass } from '@bloomneo/appkit';
145
+ ```
146
+
147
+ ❌ **AVOID (Poor tree-shaking):**
148
+
149
+ ```javascript
150
+ import * as appkit from '@bloomneo/appkit';
151
+ ```
152
+
153
+ ---
154
+
155
+ ## ENVIRONMENT-DRIVEN SCALING
156
+
157
+ **AppKit automatically scales based on environment variables:**
158
+
159
+ ### **Development (Zero Config)**
160
+
161
+ ```bash
162
+ # No environment variables needed
163
+ npm start
164
+ ```
165
+
166
+ - Memory cache/queue
167
+ - Local file storage
168
+ - Console logging
169
+ - Single database
170
+
171
+ ### **Production (Auto-Detection)**
172
+
173
+ ```bash
174
+ # Set these - everything scales automatically
175
+ REDIS_URL=redis://... # → Distributed cache/queue
176
+ DATABASE_URL=postgres://... # → Database logging/queue
177
+ AWS_S3_BUCKET=bucket # → Cloud storage
178
+ RESEND_API_KEY=re_... # → Email service
179
+ ```
180
+
181
+ ---
182
+
183
+ ## UNIVERSAL ERROR HANDLING PATTERNS
184
+
185
+ ### **Pattern 1: Safe Access with Defaults**
186
+
187
+ ```javascript
188
+ // ALWAYS use util.get() for safe property access
189
+ const util = utilClass.get();
190
+
191
+ ❌ const name = user.profile.name; // Can crash
192
+ ✅ const name = util.get(user, 'profile.name', 'Guest'); // Never crashes
193
+ ```
194
+
195
+ ### **Pattern 2: Try-Catch with Specific Errors**
196
+
197
+ ```javascript
198
+ const error = errorClass.get();
199
+
200
+ try {
201
+ await someOperation();
202
+ } catch (err) {
203
+ if (err.message.includes('validation')) {
204
+ throw error.badRequest('Invalid input data');
205
+ }
206
+ if (err.message.includes('permission')) {
207
+ throw error.forbidden('Access denied');
208
+ }
209
+ if (err.message.includes('not found')) {
210
+ throw error.notFound('Resource not found');
211
+ }
212
+ // Default to server error
213
+ throw error.serverError('Operation failed');
214
+ }
215
+ ```
216
+
217
+ ### **Pattern 3: Startup Validation**
218
+
219
+ ```javascript
220
+ // ALWAYS validate configuration at app startup
221
+ try {
222
+ const auth = authClass.get();
223
+ const config = configClass.get();
224
+
225
+ // Validate required config
226
+ config.getRequired('database.url');
227
+
228
+ console.log('✅ App validation passed');
229
+ } catch (error) {
230
+ console.error('❌ App validation failed:', error.message);
231
+ process.exit(1);
232
+ }
233
+ ```
234
+
235
+ ---
236
+
237
+ ## VARIABLE NAMING CONVENTIONS
238
+
239
+ **ALWAYS use singular naming that matches the module name for clarity:**
240
+
241
+ ### **Standard Pattern**
242
+
243
+ ```javascript
244
+ const util = utilClass.get(); // Singular: 'util'
245
+ const config = configClass.get(); // Singular: 'config'
246
+ const auth = authClass.get(); // Singular: 'auth'
247
+ const database = databaseClass.get(); // Singular: 'database'
248
+ const storage = storageClass.get(); // Singular: 'storage'
249
+ const queue = queueClass.get(); // Singular: 'queue'
250
+ const email = emailClass.get(); // Singular: 'email'
251
+ const error = errorClass.get(); // Singular: 'error'
252
+ const security = securityClass.get(); // Singular: 'security'
253
+ const logger = loggerClass.get(); // Singular: 'logger'
254
+ const cache = cacheClass.get(); // Singular: 'cache' + namespace
255
+ const event = eventClass.get(); // Singular: 'event'
256
+ ```
257
+
258
+ ### **Namespaced Instances**
259
+
260
+ ```javascript
261
+ // Logger, Cache, and Event allow parameters for organization
262
+
263
+ // Logger - component-specific logging
264
+ const logger = loggerClass.get('api');
265
+ const dbLogger = loggerClass.get('database');
266
+
267
+ // Cache - ALWAYS add namespace suffix
268
+ const userCache = cacheClass.get('users');
269
+ const sessionCache = cacheClass.get('sessions');
270
+
271
+ // Event - namespace for event isolation
272
+ const userEvent = eventClass.get('users');
273
+ const orderEvent = eventClass.get('orders');
274
+ ```
275
+
276
+ ---
277
+
278
+ ## FRAMEWORK INTEGRATION PATTERNS
279
+
280
+ ### **Express Pattern**
281
+
282
+ ```javascript
283
+ import express from 'express';
284
+ import { authClass, errorClass, loggerClass } from '@bloomneo/appkit';
285
+
286
+ const app = express();
287
+ const auth = authClass.get();
288
+ const error = errorClass.get();
289
+ const logger = loggerClass.get('app');
290
+
291
+ // ALWAYS use this middleware order
292
+ app.use(express.json());
293
+ app.use(auth.requireLogin()); // Auth first
294
+ app.use('/api', routes); // Routes second
295
+ app.use(error.handleErrors()); // Error handling LAST
296
+
297
+ // ALWAYS use asyncRoute wrapper
298
+ app.post(
299
+ '/users',
300
+ error.asyncRoute(async (req, res) => {
301
+ // Errors automatically handled
302
+ })
303
+ );
304
+ ```
305
+
306
+ ### **Fastify Pattern**
307
+
308
+ ```javascript
309
+ import Fastify from 'fastify';
310
+ import { authClass, errorClass } from '@bloomneo/appkit';
311
+
312
+ const fastify = Fastify();
313
+ const auth = authClass.get();
314
+ const error = errorClass.get();
315
+
316
+ // ALWAYS set error handler
317
+ fastify.setErrorHandler((error, request, reply) => {
318
+ const appError = error.statusCode ? error : error.serverError(error.message);
319
+ reply.status(appError.statusCode).send({
320
+ error: appError.type,
321
+ message: appError.message,
322
+ });
323
+ });
324
+
325
+ // ALWAYS use preHandler for auth
326
+ fastify.get(
327
+ '/protected',
328
+ {
329
+ preHandler: auth.requireRole('admin.tenant'),
330
+ },
331
+ async (request, reply) => {
332
+ // Route handler
333
+ }
334
+ );
335
+ ```
336
+
337
+ ---
338
+
339
+ ## MODULE INTERACTION RULES
340
+
341
+ ### **Dependency Order (ALWAYS follow this order):**
342
+
343
+ 1. **Util** - No dependencies, use first
344
+ 2. **Config** - No dependencies, use for configuration
345
+ 3. **Logger** - Depends on Config
346
+ 4. **Error** - Depends on Config and Logger
347
+ 5. **Auth** - Depends on Config and Error
348
+ 6. **Security** - Depends on Config and Error
349
+ 7. **Database** - Depends on Config, Logger, and Error
350
+ 8. **Cache** - Depends on Config and Logger
351
+ 9. **Storage** - Depends on Config, Logger, and Error
352
+ 10. **Email** - Depends on Config, Logger, and Error
353
+ 11. **Queue** - Depends on Config, Logger, and Error
354
+ 12. **Event** - Depends on Config, Logger, and Error
355
+
356
+ ### **Safe Initialization Pattern**
357
+
358
+ ```javascript
359
+ // ALWAYS initialize in this order
360
+ async function initializeApp() {
361
+ try {
362
+ // 1. Core utilities first
363
+ const config = configClass.get();
364
+ const logger = loggerClass.get('init');
365
+
366
+ // 2. Validate configuration
367
+ config.getRequired('database.url');
368
+
369
+ // 3. Initialize database
370
+ const database = databaseClass.get();
371
+
372
+ // 4. Initialize other services
373
+ const cache = cacheClass.get('app');
374
+ const queue = queueClass.get();
375
+
376
+ logger.info('✅ App initialized successfully');
377
+ } catch (error) {
378
+ console.error('❌ App initialization failed:', error.message);
379
+ process.exit(1);
380
+ }
381
+ }
382
+ ```
383
+
384
+ ---
385
+
386
+ ## TESTING PATTERNS
387
+
388
+ ### **Module Reset Between Tests**
389
+
390
+ ```javascript
391
+ import {
392
+ utilClass,
393
+ loggerClass,
394
+ cacheClass,
395
+ configClass,
396
+ } from '@bloomneo/appkit';
397
+
398
+ describe('App Tests', () => {
399
+ afterEach(async () => {
400
+ // ALWAYS reset module state between tests
401
+ utilClass.clearCache();
402
+ await loggerClass.clear();
403
+ await cacheClass.clear();
404
+ configClass.clearCache();
405
+ });
406
+
407
+ test('should process data safely', () => {
408
+ const util = utilClass.get();
409
+ const result = util.get({ user: { name: 'John' } }, 'user.name');
410
+ expect(result).toBe('John');
411
+ });
412
+ });
413
+ ```
414
+
415
+ ---
416
+
417
+ ## PRODUCTION DEPLOYMENT PATTERNS
418
+
419
+ ### **Environment Validation**
420
+
421
+ ```javascript
422
+ // ALWAYS validate environment at startup
423
+ function validateProductionEnv() {
424
+ if (process.env.NODE_ENV !== 'production') return;
425
+
426
+ const required = ['VOILA_AUTH_SECRET', 'DATABASE_URL', 'REDIS_URL'];
427
+ const missing = required.filter((key) => !process.env[key]);
428
+
429
+ if (missing.length > 0) {
430
+ console.error('❌ Missing required environment variables:', missing);
431
+ process.exit(1);
432
+ }
433
+
434
+ console.log('✅ Production environment validated');
435
+ }
436
+ ```
437
+
438
+ ### **Graceful Shutdown**
439
+
440
+ ```javascript
441
+ // ALWAYS implement graceful shutdown
442
+ async function gracefulShutdown() {
443
+ console.log('🔄 Shutting down gracefully...');
444
+
445
+ try {
446
+ await database.disconnect();
447
+ await queue.close();
448
+ await logger.flush();
449
+
450
+ console.log('✅ Shutdown complete');
451
+ process.exit(0);
452
+ } catch (error) {
453
+ console.error('❌ Shutdown error:', error);
454
+ process.exit(1);
455
+ }
456
+ }
457
+
458
+ process.on('SIGTERM', gracefulShutdown);
459
+ process.on('SIGINT', gracefulShutdown);
460
+ ```
461
+
462
+ # Essential Modules 🚀
463
+
464
+ > **Core modules every application needs - Util, Config, Auth, Logger**
465
+
466
+ ## 🛠️ UTIL MODULE - ALL 12 METHODS
467
+
468
+ ### When to Use
469
+
470
+ ✅ **Safe property access, array operations, string utilities, performance
471
+ helpers**
472
+ ❌ **Complex data transformations, DOM manipulation, heavy math**
473
+
474
+ ### Core Pattern
475
+
476
+ ```javascript
477
+ import { utilClass } from '@bloomneo/appkit/util';
478
+ const util = utilClass.get();
479
+ ```
480
+
481
+ ### Complete API (All 12 Methods)
482
+
483
+ ```javascript
484
+ // 1. get() - Safe property access (NEVER crashes)
485
+ const name = util.get(user, 'profile.name', 'Guest');
486
+ const items = util.get(response, 'data.items', []);
487
+ const nested = util.get(obj, 'users[0].addresses[1].city', 'N/A');
488
+
489
+ // 2. isEmpty() - Universal empty check
490
+ if (util.isEmpty(req.body.email)) throw error.badRequest('Email required');
491
+ util.isEmpty(null); // → true
492
+ util.isEmpty({}); // → true
493
+ util.isEmpty([]); // → true
494
+ util.isEmpty(''); // → true
495
+ util.isEmpty(' '); // → true (whitespace only)
496
+ util.isEmpty(0); // → false (number is not empty)
497
+ util.isEmpty(false); // → false (boolean is not empty)
498
+
499
+ // 3. slugify() - URL-safe strings
500
+ const slug = util.slugify('Product Name!'); // → 'product-name'
501
+ const userSlug = util.slugify('User@Email.com'); // → 'user-email-com'
502
+
503
+ // 4. chunk() - Split arrays into batches
504
+ const batches = util.chunk(largeArray, 100);
505
+ util.chunk([1, 2, 3, 4, 5, 6], 2); // → [[1,2], [3,4], [5,6]]
506
+
507
+ // 5. debounce() - Prevent excessive function calls
508
+ const debouncedSearch = util.debounce(searchAPI, 300);
509
+ const saveSettings = util.debounce(saveToStorage, 1000);
510
+
511
+ // 6. pick() - Extract specific object properties
512
+ const publicUser = util.pick(user, ['id', 'name', 'email']);
513
+
514
+ // 7. unique() - Remove duplicates
515
+ const uniqueIds = util.unique([1, 2, 2, 3, 3, 4]); // → [1, 2, 3, 4]
516
+
517
+ // 8. clamp() - Constrain numbers to range
518
+ const volume = util.clamp(userInput, 0, 1); // Audio volume
519
+ util.clamp(150, 0, 100); // → 100 (max limit)
520
+ util.clamp(-10, 0, 100); // → 0 (min limit)
521
+
522
+ // 9. formatBytes() - Human-readable file sizes
523
+ const size = util.formatBytes(1048576); // → '1 MB'
524
+ util.formatBytes(1024); // → '1 KB'
525
+
526
+ // 10. truncate() - Smart text cutting
527
+ const preview = util.truncate(longText, { length: 100, preserveWords: true });
528
+
529
+ // 11. sleep() - Promise-based delays
530
+ await util.sleep(1000); // Wait 1 second
531
+
532
+ // 12. uuid() - Generate unique identifiers
533
+ const sessionId = util.uuid(); // → 'f47ac10b-58cc-4372-a567-0e02b2c3d479'
534
+ ```
535
+
536
+ ---
537
+
538
+ ## ⚙️ CONFIG MODULE
539
+
540
+ ### When to Use
541
+
542
+ ✅ **Environment variables, application settings, startup validation**
543
+ ❌ **Runtime configuration changes, user preferences**
544
+
545
+ ### Core Pattern
546
+
547
+ ```javascript
548
+ import { configClass } from '@bloomneo/appkit/config';
549
+ const config = configClass.get();
550
+ ```
551
+
552
+ ### Environment Variable Convention
553
+
554
+ **UPPER_SNAKE_CASE** automatically becomes nested dot notation:
555
+
556
+ ```bash
557
+ # Environment Variable → Config Path
558
+ DATABASE_HOST=localhost → config.get('database.host')
559
+ DATABASE_CONNECTION_POOL_SIZE=10 → config.get('database.connection.pool_size')
560
+ STRIPE_API_KEYS_PUBLIC=pk_test_123 → config.get('stripe.api.keys.public')
561
+ FEATURES_ANALYTICS_ENABLED=true → config.get('features.analytics.enabled')
562
+ ```
563
+
564
+ ### Essential Patterns
565
+
566
+ ```javascript
567
+ // 1. Required values (throws if missing)
568
+ const dbUrl = config.getRequired('database.url');
569
+ const authSecret = config.getRequired('auth.secret');
570
+
571
+ // 2. Optional with defaults
572
+ const port = config.get('server.port', 3000);
573
+ const timeout = config.get('api.timeout', 5000);
574
+
575
+ // 3. Environment detection
576
+ const isDev = config.isDevelopment();
577
+ const isProd = config.isProduction();
578
+
579
+ // 4. Startup validation
580
+ try {
581
+ config.getRequired('database.url');
582
+ config.getRequired('auth.secret');
583
+
584
+ if (config.isProduction()) {
585
+ config.getRequired('redis.url');
586
+ }
587
+
588
+ console.log('✅ Configuration validation passed');
589
+ } catch (error) {
590
+ console.error('❌ Configuration validation failed:', error.message);
591
+ process.exit(1);
592
+ }
593
+ ```
594
+
595
+ ---
596
+
597
+ ## 🔐 AUTH MODULE
598
+
599
+ ### When to Use
600
+
601
+ ✅ **Dual token system, JWT operations, role-level permissions, API security, password hashing**
602
+ ❌ **Frontend authentication, OAuth providers, session storage**
603
+
604
+ ### Core Pattern
605
+
606
+ ```javascript
607
+ import { authClass } from '@bloomneo/appkit/auth';
608
+ const auth = authClass.get();
609
+ ```
610
+
611
+ ### Dual Token System (CRITICAL - Two Distinct Types)
612
+
613
+ ```javascript
614
+ // ✅ LOGIN TOKENS - For user authentication (mobile/web)
615
+ const loginToken = auth.generateLoginToken({
616
+ userId: 123, // REQUIRED - unique user identifier
617
+ role: 'admin', // REQUIRED - role name (admin, user, moderator)
618
+ level: 'tenant', // REQUIRED - level within role (basic, tenant, org, system)
619
+ }, '7d'); // Short-medium expiry
620
+
621
+ // ✅ API TOKENS - For service authentication (webhooks/integrations)
622
+ const apiToken = auth.generateApiToken({
623
+ keyId: 'webhook_service', // REQUIRED - service identifier
624
+ role: 'service', // REQUIRED - role name
625
+ level: 'external', // REQUIRED - level within role
626
+ }, '1y'); // Long expiry
627
+
628
+ // ❌ WRONG - Don't mix these up
629
+ auth.generateLoginToken({ keyId: 'test' }); // keyId is for API tokens
630
+ auth.generateApiToken({ userId: 123 }); // userId is for login tokens
631
+ ```
632
+
633
+ ### Role Hierarchy (Built-in Inheritance)
634
+
635
+ ```
636
+ admin.system > admin.org > admin.tenant >
637
+ moderator.manage > moderator.approve > moderator.review >
638
+ user.max > user.pro > user.basic
639
+ ```
640
+
641
+ ### Essential Auth Patterns
642
+
643
+ ```javascript
644
+ // 1. User route protection (login tokens only)
645
+ app.get('/profile', auth.requireLoginToken(), handler);
646
+ app.get('/admin', auth.requireLoginToken(), auth.requireUserRoles(['admin.tenant']), handler);
647
+
648
+ // 2. API route protection (API tokens only)
649
+ app.post('/webhook/data', auth.requireApiToken(), handler);
650
+
651
+ // 3. Safe user extraction (works with both token types)
652
+ const user = auth.user(req);
653
+ if (!user) throw error.unauthorized('Authentication required');
654
+
655
+ // 4. Role hierarchy checking
656
+ const userRoleLevel = `${user.role}.${user.level}`;
657
+ if (!auth.hasRole(userRoleLevel, 'admin.tenant')) {
658
+ throw error.forbidden('Admin access required');
659
+ }
660
+
661
+ // 5. Permission checking (action:scope format)
662
+ if (!auth.can(user, 'manage:tenant')) {
663
+ throw error.forbidden('Insufficient permissions');
664
+ }
665
+
666
+ // 6. Password handling
667
+ const hashedPassword = await auth.hashPassword(plainPassword);
668
+ const isValid = await auth.comparePassword(plainPassword, hashedPassword);
669
+
670
+ // 7. Token operations (works with both types)
671
+ const payload = auth.verifyToken(token);
672
+ ```
673
+
674
+ ### Complete Authentication Flow
675
+
676
+ ```javascript
677
+ // User login endpoint (generates login token)
678
+ app.post(
679
+ '/auth/login',
680
+ error.asyncRoute(async (req, res) => {
681
+ const { email, password } = req.body;
682
+
683
+ if (util.isEmpty(email)) throw error.badRequest('Email required');
684
+ if (util.isEmpty(password)) throw error.badRequest('Password required');
685
+
686
+ const user = await database.user.findUnique({ where: { email } });
687
+ if (!user) throw error.unauthorized('Invalid credentials');
688
+
689
+ const isValid = await auth.comparePassword(password, user.password);
690
+ if (!isValid) throw error.unauthorized('Invalid credentials');
691
+
692
+ // Generate login token for user authentication
693
+ const loginToken = auth.generateLoginToken({
694
+ userId: user.id,
695
+ role: user.role,
696
+ level: user.level,
697
+ });
698
+
699
+ res.json({
700
+ token: loginToken,
701
+ user: util.pick(user, ['id', 'email', 'name']),
702
+ });
703
+ })
704
+ );
705
+
706
+ // API token creation endpoint (admin-only)
707
+ app.post(
708
+ '/admin/api-tokens',
709
+ auth.requireLoginToken(),
710
+ auth.requireUserRoles(['admin.tenant']),
711
+ error.asyncRoute(async (req, res) => {
712
+ const { keyId, permissions } = req.body;
713
+
714
+ // Generate API token for service authentication
715
+ const apiToken = auth.generateApiToken({
716
+ keyId,
717
+ role: 'service',
718
+ level: 'external',
719
+ permissions,
720
+ }, '1y');
721
+
722
+ // Store token info in database (store hash, not plain token)
723
+ const hashedToken = await auth.hashPassword(apiToken);
724
+ await database.apiToken.create({
725
+ data: { keyId, token: hashedToken, permissions },
726
+ });
727
+
728
+ res.json({ apiToken }); // Return once for client to save
729
+ })
730
+ );
731
+ ```
732
+
733
+ ---
734
+
735
+ ## 📝 LOGGER MODULE
736
+
737
+ ### When to Use
738
+
739
+ ✅ **Structured logging, error tracking, performance monitoring**
740
+ ❌ **Simple console.log, debugging only**
741
+
742
+ ### Core Pattern
743
+
744
+ ```javascript
745
+ import { loggerClass } from '@bloomneo/appkit/logger';
746
+ const logger = loggerClass.get();
747
+ ```
748
+
749
+ ### Auto-Transport Selection
750
+
751
+ ```bash
752
+ # Development → Console
753
+ # Production with DATABASE_URL → Database
754
+ # Production with REDIS_URL → Redis
755
+ # Production with VOILA_LOGGER_HTTP_URL → HTTP endpoint
756
+ ```
757
+
758
+ ### Essential Patterns
759
+
760
+ ```javascript
761
+ // 1. Component-specific loggers
762
+ const logger = loggerClass.get('api');
763
+ const dbLogger = loggerClass.get('database');
764
+
765
+ // 2. Structured logging with context
766
+ logger.info('User created', {
767
+ userId: user.id,
768
+ email: user.email,
769
+ timestamp: Date.now(),
770
+ });
771
+
772
+ // 3. Error logging
773
+ try {
774
+ await riskyOperation();
775
+ } catch (err) {
776
+ logger.error('Operation failed', {
777
+ error: err.message,
778
+ stack: err.stack,
779
+ userId: req.user?.id,
780
+ });
781
+ throw error.serverError('Operation failed');
782
+ }
783
+
784
+ // 4. Request logging middleware
785
+ app.use((req, res, next) => {
786
+ req.requestId = util.uuid();
787
+ req.logger = logger.child({
788
+ requestId: req.requestId,
789
+ method: req.method,
790
+ url: req.url,
791
+ });
792
+
793
+ const startTime = Date.now();
794
+ res.on('finish', () => {
795
+ req.logger.info('Request completed', {
796
+ statusCode: res.statusCode,
797
+ duration: Date.now() - startTime,
798
+ });
799
+ });
800
+
801
+ next();
802
+ });
803
+ ```
804
+
805
+ ---
806
+
807
+ ## ESSENTIAL MODULES INTEGRATION
808
+
809
+ ```javascript
810
+ // Complete API endpoint using all 4 essential modules
811
+ import { utilClass } from '@bloomneo/appkit/util';
812
+ import { configClass } from '@bloomneo/appkit/config';
813
+ import { authClass } from '@bloomneo/appkit/auth';
814
+ import { loggerClass } from '@bloomneo/appkit/logger';
815
+ import { errorClass } from '@bloomneo/appkit/error';
816
+
817
+ const util = utilClass.get();
818
+ const config = configClass.get();
819
+ const auth = authClass.get();
820
+ const logger = loggerClass.get('api');
821
+ const error = errorClass.get();
822
+
823
+ // User profile update endpoint
824
+ app.put(
825
+ '/api/profile',
826
+ auth.requireLogin(),
827
+ error.asyncRoute(async (req, res) => {
828
+ const user = auth.user(req);
829
+ const requestLogger = logger.child({
830
+ userId: user.userId,
831
+ requestId: util.uuid(),
832
+ });
833
+
834
+ // Safe data extraction
835
+ const name = util.get(req.body, 'name', '').trim();
836
+ const bio = util.get(req.body, 'bio', '').trim();
837
+
838
+ // Input validation
839
+ if (util.isEmpty(name)) {
840
+ requestLogger.warn('Profile update failed - empty name');
841
+ throw error.badRequest('Name is required');
842
+ }
843
+
844
+ // Length validation from config
845
+ const maxBioLength = config.get('user.bio.maxLength', 500);
846
+ if (bio.length > maxBioLength) {
847
+ throw error.badRequest(`Bio too long (max ${maxBioLength} characters)`);
848
+ }
849
+
850
+ // Create slug for profile URL
851
+ const slug = util.slugify(name);
852
+
853
+ // Update user data
854
+ const updatedUser = await database.user.update({
855
+ where: { id: user.userId },
856
+ data: { name, bio: util.isEmpty(bio) ? null : bio, slug },
857
+ });
858
+
859
+ requestLogger.info('Profile updated successfully');
860
+
861
+ res.json({
862
+ success: true,
863
+ user: util.pick(updatedUser, ['id', 'name', 'bio', 'slug']),
864
+ });
865
+ })
866
+ );
867
+ ```
868
+
869
+ # Infrastructure Modules 📊
870
+
871
+ > **Data persistence, communication, and background processing - Database,
872
+ > Cache, Storage, Queue, Email**
873
+
874
+ ## 🗄️ DATABASE MODULE
875
+
876
+ ### When to Use
877
+
878
+ ✅ **Persistent data, user data, content, multi-tenant apps, cross-cloud
879
+ orgs**
880
+ ❌ **Temporary data, files, caching, session storage**
881
+
882
+ ### Core Pattern
883
+
884
+ ```javascript
885
+ import { databaseClass } from '@bloomneo/appkit/database';
886
+ const database = databaseClass.get();
887
+ ```
888
+
889
+ ### Progressive Scaling (CRITICAL)
890
+
891
+ ```javascript
892
+ // Day 1: Single database
893
+ const database = await databaseClass.get();
894
+ const users = await database.user.findMany();
895
+
896
+ // Month 6: Multi-tenant (zero code changes!)
897
+ // Just add: VOILA_DB_TENANT=auto
898
+ const database = await databaseClass.get(); // Now auto-filtered by tenant
899
+
900
+ // Year 1: Multi-org (still zero code changes!)
901
+ // Add: ORG_ACME=postgresql://acme.aws.com/db
902
+ const acmeDatabase = await databaseClass.org('acme').get();
903
+ ```
904
+
905
+ ### Essential API (3 Core Patterns)
906
+
907
+ ```javascript
908
+ // 1. Normal user access (single or tenant-filtered)
909
+ const database = await databaseClass.get();
910
+ const users = await database.user.findMany(); // Auto-filtered if tenant mode
911
+
912
+ // 2. Admin access (all tenants)
913
+ const dbTenants = await databaseClass.getTenants();
914
+ const allUsers = await dbTenants.user.findMany(); // Cross-tenant
915
+
916
+ // 3. Organization-specific access
917
+ const acmeDatabase = await databaseClass.org('acme').get();
918
+ const acmeUsers = await acmeDatabase.user.findMany();
919
+ ```
920
+
921
+ ### MANDATORY Schema Requirements
922
+
923
+ ```sql
924
+ -- ✅ EVERY table MUST include tenant_id from Day 1 (nullable for future)
925
+ CREATE TABLE users (
926
+ id uuid PRIMARY KEY,
927
+ email text UNIQUE,
928
+ name text,
929
+ tenant_id text, -- MANDATORY: nullable for future
930
+ created_at timestamp DEFAULT now(),
931
+
932
+ INDEX idx_users_tenant (tenant_id) -- MANDATORY: performance index
933
+ );
934
+
935
+ CREATE TABLE posts (
936
+ id uuid PRIMARY KEY,
937
+ title text,
938
+ content text,
939
+ user_id uuid REFERENCES users(id),
940
+ tenant_id text, -- MANDATORY: on EVERY table
941
+ created_at timestamp DEFAULT now(),
942
+
943
+ INDEX idx_posts_tenant (tenant_id) -- MANDATORY: on EVERY table
944
+ );
945
+ ```
946
+
947
+ ---
948
+
949
+ ## 💾 CACHE MODULE
950
+
951
+ ### When to Use
952
+
953
+ ✅ **Speed up database queries, session data, API responses, computed
954
+ results**
955
+ ❌ **Permanent data, files, transactions, sensitive data without encryption**
956
+
957
+ ### Core Pattern
958
+
959
+ ```javascript
960
+ import { cacheClass } from '@bloomneo/appkit/cache';
961
+ const cache = cacheClass.get(); // Default 'app' namespace
962
+ ```
963
+
964
+ ### Auto-Strategy Detection
965
+
966
+ ```bash
967
+ # Development → Memory cache with LRU
968
+ # Production → Redis (if REDIS_URL) → Memory
969
+ ```
970
+
971
+ ### Essential API (5 Core Methods)
972
+
973
+ ```javascript
974
+ // 1. set() - Store with TTL (seconds)
975
+ await cache.set('user:123', userData, 3600); // 1 hour TTL
976
+
977
+ // 2. get() - Retrieve (null if not found/expired)
978
+ const user = await cache.get('user:123');
979
+
980
+ // 3. getOrSet() - Get or compute and cache
981
+ const weather = await cache.getOrSet(
982
+ `weather:${city}`,
983
+ async () => {
984
+ return await fetchWeatherAPI(city); // Only runs on cache miss
985
+ },
986
+ 1800 // 30 minutes
987
+ );
988
+
989
+ // 4. delete() - Remove specific key
990
+ await cache.delete('user:123');
991
+
992
+ // 5. clear() - Clear entire namespace
993
+ await cache.clear();
994
+ ```
995
+
996
+ ### Namespace Isolation
997
+
998
+ ```javascript
999
+ // ALWAYS use specific namespaces - completely isolated
1000
+ const userCache = cacheClass.get('users');
1001
+ const sessionCache = cacheClass.get('sessions');
1002
+ const apiCache = cacheClass.get('external-api');
1003
+
1004
+ await userCache.set('123', userData);
1005
+ await sessionCache.set('123', sessionData); // Different from user:123
1006
+ ```
1007
+
1008
+ ---
1009
+
1010
+ ## 📁 STORAGE MODULE
1011
+
1012
+ ### When to Use
1013
+
1014
+ ✅ **File uploads, documents, images, videos, CDN integration**
1015
+ ❌ **Configuration data, temporary data, session storage**
1016
+
1017
+ ### Core Pattern
1018
+
1019
+ ```javascript
1020
+ import { storageClass } from '@bloomneo/appkit/storage';
1021
+ const storage = storageClass.get();
1022
+ ```
1023
+
1024
+ ### Auto-Strategy Detection
1025
+
1026
+ ```bash
1027
+ # Development → Local files in ./uploads/
1028
+ # Production → R2 (if CLOUDFLARE_R2_BUCKET) → S3 (if AWS_S3_BUCKET) → Local
1029
+ ```
1030
+
1031
+ ### Essential API (4 Core Methods)
1032
+
1033
+ ```javascript
1034
+ // 1. put() - Upload files
1035
+ await storage.put('avatars/user123.jpg', imageBuffer);
1036
+ await storage.put('docs/contract.pdf', pdfBuffer, {
1037
+ contentType: 'application/pdf',
1038
+ cacheControl: 'public, max-age=3600',
1039
+ });
1040
+
1041
+ // 2. get() - Download files
1042
+ const buffer = await storage.get('avatars/user123.jpg');
1043
+ if (!buffer) throw error.notFound('File not found');
1044
+
1045
+ // 3. delete() - Remove files
1046
+ await storage.delete('temp/old-file.jpg');
1047
+
1048
+ // 4. url() - Get public URLs
1049
+ const url = storage.url('avatars/user123.jpg');
1050
+ // Local: /uploads/avatars/user123.jpg
1051
+ // S3: https://bucket.s3.region.amazonaws.com/avatars/user123.jpg
1052
+ // R2: https://cdn.example.com/avatars/user123.jpg
1053
+ ```
1054
+
1055
+ ---
1056
+
1057
+ ## 🚀 QUEUE MODULE
1058
+
1059
+ ### When to Use
1060
+
1061
+ ✅ **Background jobs, emails, file processing, webhooks, scheduled tasks**
1062
+ ❌ **Real-time operations, simple sync operations, immediate responses**
1063
+
1064
+ ### Core Pattern
1065
+
1066
+ ```javascript
1067
+ import { queueClass } from '@bloomneo/appkit/queue';
1068
+ const queue = queueClass.get();
1069
+ ```
1070
+
1071
+ ### Auto-Transport Detection
1072
+
1073
+ ```bash
1074
+ # Development → Memory queue
1075
+ # Production → Redis (if REDIS_URL) → Database (if DATABASE_URL) → Memory
1076
+ ```
1077
+
1078
+ ### Essential API (3 Core Methods)
1079
+
1080
+ ```javascript
1081
+ // 1. add() - Add jobs to queue
1082
+ await queue.add('email', {
1083
+ to: 'user@example.com',
1084
+ subject: 'Welcome!',
1085
+ body: 'Thanks for signing up',
1086
+ });
1087
+
1088
+ await queue.add(
1089
+ 'image-resize',
1090
+ {
1091
+ input: 'uploads/large.jpg',
1092
+ output: 'thumbnails/thumb.jpg',
1093
+ width: 200,
1094
+ },
1095
+ {
1096
+ delay: 5000, // Start in 5 seconds
1097
+ attempts: 3, // Retry 3 times
1098
+ }
1099
+ );
1100
+
1101
+ // 2. process() - Handle jobs
1102
+ queue.process('email', async (data) => {
1103
+ await sendEmail(data.to, data.subject, data.body);
1104
+ return { sent: true };
1105
+ });
1106
+
1107
+ queue.process('image-resize', async (data) => {
1108
+ await resizeImage(data.input, data.output, data.width);
1109
+ return { resized: true };
1110
+ });
1111
+
1112
+ // 3. schedule() - Delayed jobs
1113
+ await queue.schedule(
1114
+ 'reminder',
1115
+ {
1116
+ userId: 123,
1117
+ message: 'Your trial ends soon!',
1118
+ },
1119
+ 7 * 24 * 60 * 60 * 1000
1120
+ ); // 7 days
1121
+ ```
1122
+
1123
+ ---
1124
+
1125
+ ## 📧 EMAIL MODULE
1126
+
1127
+ ### When to Use
1128
+
1129
+ ✅ **Transactional emails, notifications, templates, multi-provider support**
1130
+ ❌ **Marketing emails, bulk campaigns, newsletter management**
1131
+
1132
+ ### Core Pattern
1133
+
1134
+ ```javascript
1135
+ import { emailClass } from '@bloomneo/appkit/email';
1136
+ const email = emailClass.get();
1137
+ ```
1138
+
1139
+ ### Auto-Provider Detection
1140
+
1141
+ ```bash
1142
+ # Production → Resend (if RESEND_API_KEY) → SMTP (if SMTP_HOST) → Console
1143
+ ```
1144
+
1145
+ ### Essential API (3 Core Methods)
1146
+
1147
+ ```javascript
1148
+ // 1. send() - Basic email sending
1149
+ await email.send({
1150
+ to: 'user@example.com',
1151
+ subject: 'Welcome to our app!',
1152
+ text: 'Thanks for signing up.',
1153
+ html: '<h1>Thanks for signing up!</h1>',
1154
+ });
1155
+
1156
+ // 2. sendTemplate() - Template-based emails
1157
+ await email.sendTemplate('welcome', {
1158
+ to: user.email,
1159
+ name: user.name,
1160
+ activationLink: `https://app.com/activate/${user.token}`,
1161
+ });
1162
+
1163
+ // 3. Queue integration (recommended for production)
1164
+ await queue.add('email', {
1165
+ template: 'password-reset',
1166
+ to: user.email,
1167
+ resetLink: resetUrl,
1168
+ });
1169
+
1170
+ queue.process('email', async (data) => {
1171
+ if (data.template) {
1172
+ return await email.sendTemplate(data.template, data);
1173
+ } else {
1174
+ return await email.send(data);
1175
+ }
1176
+ });
1177
+ ```
1178
+
1179
+ ---
1180
+
1181
+ ## INFRASTRUCTURE INTEGRATION
1182
+
1183
+ ```javascript
1184
+ // File upload with complete infrastructure integration
1185
+ import { utilClass } from '@bloomneo/appkit/util';
1186
+ import { authClass } from '@bloomneo/appkit/auth';
1187
+ import { databaseClass } from '@bloomneo/appkit/database';
1188
+ import { storageClass } from '@bloomneo/appkit/storage';
1189
+ import { cacheClass } from '@bloomneo/appkit/cache';
1190
+ import { queueClass } from '@bloomneo/appkit/queue';
1191
+ import { errorClass } from '@bloomneo/appkit/error';
1192
+ import { loggerClass } from '@bloomneo/appkit/logger';
1193
+
1194
+ const util = utilClass.get();
1195
+ const auth = authClass.get();
1196
+ const storage = storageClass.get();
1197
+ const queue = queueClass.get();
1198
+ const error = errorClass.get();
1199
+ const logger = loggerClass.get('upload');
1200
+
1201
+ // File upload endpoint
1202
+ app.post(
1203
+ '/api/upload',
1204
+ auth.requireLogin(),
1205
+ upload.single('file'),
1206
+ error.asyncRoute(async (req, res) => {
1207
+ const user = auth.user(req);
1208
+ const userCache = cacheClass.get('users');
1209
+
1210
+ if (!req.file) {
1211
+ throw error.badRequest('No file uploaded');
1212
+ }
1213
+
1214
+ // 1. Store file in storage (auto-detects Local/S3/R2)
1215
+ const fileKey = `uploads/${user.userId}/${Date.now()}-${util.slugify(req.file.originalname)}`;
1216
+ await storage.put(fileKey, req.file.buffer, {
1217
+ contentType: req.file.mimetype,
1218
+ });
1219
+
1220
+ // 2. Save to database (auto-filtered by tenant if enabled)
1221
+ const database = await databaseClass.get();
1222
+ const file = await database.file.create({
1223
+ data: {
1224
+ key: fileKey,
1225
+ name: req.file.originalname,
1226
+ size: req.file.size,
1227
+ contentType: req.file.mimetype,
1228
+ userId: user.userId,
1229
+ },
1230
+ });
1231
+
1232
+ // 3. Queue background processing
1233
+ await queue.add('process-upload', {
1234
+ fileId: file.id,
1235
+ fileKey: fileKey,
1236
+ userId: user.userId,
1237
+ });
1238
+
1239
+ // 4. Clear user's file cache
1240
+ await userCache.delete(`files:${user.userId}`);
1241
+
1242
+ // 5. Log activity
1243
+ logger.info('File uploaded', {
1244
+ fileId: file.id,
1245
+ userId: user.userId,
1246
+ size: util.formatBytes(req.file.size),
1247
+ });
1248
+
1249
+ res.json({
1250
+ success: true,
1251
+ file: {
1252
+ id: file.id,
1253
+ url: storage.url(fileKey),
1254
+ name: req.file.originalname,
1255
+ size: util.formatBytes(req.file.size),
1256
+ },
1257
+ });
1258
+ })
1259
+ );
1260
+
1261
+ // Background file processor
1262
+ queue.process('process-upload', async (data) => {
1263
+ const database = await databaseClass.get();
1264
+ const email = emailClass.get();
1265
+ const processingLogger = loggerClass.get('processing');
1266
+
1267
+ try {
1268
+ // Get file buffer for processing
1269
+ const buffer = await storage.get(data.fileKey);
1270
+
1271
+ // Process file (resize, convert, scan, etc.)
1272
+ const processedBuffer = await processFile(buffer);
1273
+
1274
+ // Store processed version
1275
+ const processedKey = data.fileKey.replace('uploads/', 'processed/');
1276
+ await storage.put(processedKey, processedBuffer);
1277
+
1278
+ // Update database
1279
+ await database.file.update({
1280
+ where: { id: data.fileId },
1281
+ data: {
1282
+ processed: true,
1283
+ processedKey,
1284
+ processedAt: new Date(),
1285
+ },
1286
+ });
1287
+
1288
+ // Clear cache
1289
+ const userCache = cacheClass.get('users');
1290
+ await userCache.delete(`files:${data.userId}`);
1291
+
1292
+ // Send notification email
1293
+ await queue.add('file-processed-email', {
1294
+ userId: data.userId,
1295
+ fileId: data.fileId,
1296
+ });
1297
+
1298
+ processingLogger.info('File processed successfully', {
1299
+ fileId: data.fileId,
1300
+ userId: data.userId,
1301
+ });
1302
+
1303
+ return { processed: true, processedKey };
1304
+ } catch (error) {
1305
+ processingLogger.error('File processing failed', {
1306
+ fileId: data.fileId,
1307
+ error: error.message,
1308
+ });
1309
+ throw error;
1310
+ }
1311
+ });
1312
+ ```
1313
+
1314
+ # System Modules 🛡️
1315
+
1316
+ > **Application security, error handling, and real-time communication - Error,
1317
+ > Security, Event**
1318
+
1319
+ ## ⚠️ ERROR MODULE
1320
+
1321
+ ### When to Use
1322
+
1323
+ ✅ **HTTP APIs, status codes, middleware integration, client responses**
1324
+ ❌ **CLI applications, non-HTTP servers, simple utilities**
1325
+
1326
+ ### Core Pattern
1327
+
1328
+ ```javascript
1329
+ import { errorClass } from '@bloomneo/appkit/error';
1330
+ const error = errorClass.get();
1331
+ ```
1332
+
1333
+ ### HTTP Status Code Mapping (CRITICAL)
1334
+
1335
+ ```javascript
1336
+ // ✅ CORRECT - Use these exact error types for specific situations
1337
+ error.badRequest('message'); // 400 - Client input errors
1338
+ error.unauthorized('message'); // 401 - Authentication required
1339
+ error.forbidden('message'); // 403 - Access denied (user authenticated, no permission)
1340
+ error.notFound('message'); // 404 - Resource doesn't exist
1341
+ error.conflict('message'); // 409 - Business logic conflicts (duplicate email)
1342
+ error.serverError('message'); // 500 - Internal server errors
1343
+
1344
+ // ❌ WRONG - Don't use wrong error types
1345
+ throw error.serverError('Email required'); // Should be badRequest
1346
+ throw error.badRequest('Database failed'); // Should be serverError
1347
+ throw error.unauthorized('Admin required'); // Should be forbidden
1348
+ ```
1349
+
1350
+ ### Essential API (4 Core Patterns)
1351
+
1352
+ ```javascript
1353
+ // 1. Input validation (400 errors)
1354
+ if (!req.body.email) {
1355
+ throw error.badRequest('Email is required');
1356
+ }
1357
+ if (!email.includes('@')) {
1358
+ throw error.badRequest('Invalid email format');
1359
+ }
1360
+
1361
+ // 2. Authentication checks (401 errors)
1362
+ if (!token) {
1363
+ throw error.unauthorized('Authentication token required');
1364
+ }
1365
+ if (tokenExpired) {
1366
+ throw error.unauthorized('Session expired. Please login again.');
1367
+ }
1368
+
1369
+ // 3. Permission checks (403 errors)
1370
+ if (!user.isAdmin) {
1371
+ throw error.forbidden('Admin access required');
1372
+ }
1373
+ if (user.status === 'suspended') {
1374
+ throw error.forbidden('Account suspended');
1375
+ }
1376
+
1377
+ // 4. Resource checks (404 errors)
1378
+ const user = await database.user.findUnique({ where: { id } });
1379
+ if (!user) {
1380
+ throw error.notFound('User not found');
1381
+ }
1382
+ ```
1383
+
1384
+ ### Framework Integration
1385
+
1386
+ ```javascript
1387
+ // Express - Error handling middleware (MUST be last)
1388
+ app.use(error.handleErrors());
1389
+
1390
+ // Express - Async route wrapper
1391
+ app.post(
1392
+ '/users',
1393
+ error.asyncRoute(async (req, res) => {
1394
+ // Errors automatically handled
1395
+ if (!req.body.email) throw error.badRequest('Email required');
1396
+ res.json({ success: true });
1397
+ })
1398
+ );
1399
+
1400
+ // Fastify - Error handler setup
1401
+ fastify.setErrorHandler((err, request, reply) => {
1402
+ const appError = err.statusCode ? err : error.serverError(err.message);
1403
+ reply.status(appError.statusCode).send({
1404
+ error: appError.type,
1405
+ message: appError.message,
1406
+ });
1407
+ });
1408
+ ```
1409
+
1410
+ ---
1411
+
1412
+ ## 🔒 SECURITY MODULE
1413
+
1414
+ ### When to Use
1415
+
1416
+ ✅ **Web forms, CSRF protection, rate limiting, input sanitization,
1417
+ encryption**
1418
+ ❌ **CLI applications, API-only services, read-only applications**
1419
+
1420
+ ### Core Pattern
1421
+
1422
+ ```javascript
1423
+ import { securityClass } from '@bloomneo/appkit/security';
1424
+ const security = securityClass.get();
1425
+ ```
1426
+
1427
+ ### Required Environment Variables
1428
+
1429
+ ```bash
1430
+ # CRITICAL - Required for startup
1431
+ VOILA_SECURITY_CSRF_SECRET=your-csrf-secret-key-2024-minimum-32-chars
1432
+ VOILA_SECURITY_ENCRYPTION_KEY=64-char-hex-key-for-aes256-encryption
1433
+ ```
1434
+
1435
+ ### Essential API (4 Core Methods)
1436
+
1437
+ ```javascript
1438
+ // 1. CSRF Protection (CRITICAL: Session middleware MUST come first)
1439
+ app.use(session({ secret: process.env.SESSION_SECRET }));
1440
+ app.use(security.forms()); // CSRF protection for all routes
1441
+
1442
+ // Generate CSRF token for forms
1443
+ app.get('/form', (req, res) => {
1444
+ const csrfToken = req.csrfToken();
1445
+ res.render('form', { csrfToken });
1446
+ });
1447
+
1448
+ // 2. Rate Limiting
1449
+ app.use('/api', security.requests()); // Default: 100 requests per 15 minutes
1450
+ app.use('/auth', security.requests(5, 3600000)); // 5 requests per hour
1451
+
1452
+ // 3. Input Sanitization
1453
+ const safeName = security.input(req.body.name, { maxLength: 50 });
1454
+ const safeEmail = security.input(req.body.email?.toLowerCase());
1455
+ const safeHtml = security.html(req.body.content, {
1456
+ allowedTags: ['p', 'b', 'i', 'a'],
1457
+ });
1458
+
1459
+ // 4. Data Encryption (AES-256-GCM)
1460
+ const encryptedSSN = security.encrypt(user.ssn);
1461
+ const encryptedPhone = security.encrypt(user.phone);
1462
+
1463
+ // Decrypt for authorized access
1464
+ const originalSSN = security.decrypt(encryptedSSN);
1465
+ const originalPhone = security.decrypt(encryptedPhone);
1466
+ ```
1467
+
1468
+ ---
1469
+
1470
+ ## 🚀 EVENT MODULE
1471
+
1472
+ ### When to Use
1473
+
1474
+ ✅ **Real-time features, WebSocket connections, pub/sub messaging, live
1475
+ notifications**
1476
+ ❌ **HTTP APIs, file transfers, database operations, background jobs**
1477
+
1478
+ ### Core Pattern
1479
+
1480
+ ```javascript
1481
+ import { eventClass } from '@bloomneo/appkit/event';
1482
+ const event = eventClass.get();
1483
+ ```
1484
+
1485
+ ### Auto-Strategy Detection
1486
+
1487
+ ```bash
1488
+ # Development → Memory-based event emitter
1489
+ # Production → Redis pub/sub (if REDIS_URL) → Memory
1490
+ ```
1491
+
1492
+ ### Essential API (6 Core Methods)
1493
+
1494
+ ```javascript
1495
+ // 1. on() - Listen to events
1496
+ event.on('user.login', (data) => {
1497
+ console.log(`User ${data.userId} logged in`);
1498
+ });
1499
+
1500
+ // 2. emit() - Send events
1501
+ await event.emit('user.login', {
1502
+ userId: 123,
1503
+ timestamp: Date.now(),
1504
+ ip: req.ip,
1505
+ });
1506
+
1507
+ // 3. Wildcard patterns
1508
+ event.on('user.*', (eventName, data) => {
1509
+ console.log(`User event: ${eventName}`, data);
1510
+ });
1511
+
1512
+ // 4. once() - One-time listeners
1513
+ event.once('app.ready', () => {
1514
+ console.log('Application is ready');
1515
+ });
1516
+
1517
+ // 5. off() - Remove listeners
1518
+ event.off('user.login'); // Remove all listeners
1519
+ event.off('user.login', specificHandler); // Remove specific listener
1520
+
1521
+ // 6. namespace() - Isolated event channels
1522
+ const userEvent = eventClass.get('users');
1523
+ const orderEvent = eventClass.get('orders');
1524
+
1525
+ userEvent.emit('created', data); // → users:created
1526
+ orderEvent.emit('created', data); // → orders:created
1527
+ ```
1528
+
1529
+ ---
1530
+
1531
+ ## SYSTEM MODULES INTEGRATION
1532
+
1533
+ ```javascript
1534
+ // Secure real-time application with all system modules
1535
+ import express from 'express';
1536
+ import session from 'express-session';
1537
+ import { createServer } from 'http';
1538
+ import { Server } from 'socket.io';
1539
+
1540
+ import { utilClass } from '@bloomneo/appkit/util';
1541
+ import { authClass } from '@bloomneo/appkit/auth';
1542
+ import { errorClass } from '@bloomneo/appkit/error';
1543
+ import { securityClass } from '@bloomneo/appkit/security';
1544
+ import { eventClass } from '@bloomneo/appkit/event';
1545
+ import { loggerClass } from '@bloomneo/appkit/logger';
1546
+
1547
+ const app = express();
1548
+ const server = createServer(app);
1549
+ const io = new Server(server);
1550
+
1551
+ const util = utilClass.get();
1552
+ const auth = authClass.get();
1553
+ const error = errorClass.get();
1554
+ const security = securityClass.get();
1555
+ const event = eventClass.get('app');
1556
+ const logger = loggerClass.get('app');
1557
+
1558
+ // Security middleware setup (ORDER CRITICAL)
1559
+ app.use(express.json({ limit: '10mb' }));
1560
+ app.use(
1561
+ session({
1562
+ secret: config.getRequired('session.secret'),
1563
+ resave: false,
1564
+ saveUninitialized: false,
1565
+ cookie: { secure: config.isProduction(), httpOnly: true },
1566
+ })
1567
+ );
1568
+ app.use(security.forms()); // CSRF protection
1569
+ app.use('/api', security.requests(100, 900000)); // Rate limiting
1570
+
1571
+ // Real-time secure messaging endpoint
1572
+ app.post(
1573
+ '/api/messages',
1574
+ auth.requireLogin(),
1575
+ error.asyncRoute(async (req, res) => {
1576
+ const user = auth.user(req);
1577
+ const { content, roomId } = req.body;
1578
+
1579
+ // Input validation
1580
+ if (util.isEmpty(content)) {
1581
+ throw error.badRequest('Message content required');
1582
+ }
1583
+ if (util.isEmpty(roomId)) {
1584
+ throw error.badRequest('Room ID required');
1585
+ }
1586
+
1587
+ // Sanitize content
1588
+ const safeContent = security.html(content, {
1589
+ allowedTags: ['b', 'i', 'em', 'strong'],
1590
+ maxLength: 1000,
1591
+ });
1592
+
1593
+ // Check permissions
1594
+ const database = databaseClass.get();
1595
+ const room = await database.room.findFirst({
1596
+ where: {
1597
+ id: roomId,
1598
+ members: { some: { userId: user.userId } },
1599
+ },
1600
+ });
1601
+
1602
+ if (!room) {
1603
+ throw error.forbidden('Access to room denied');
1604
+ }
1605
+
1606
+ // Save message
1607
+ const message = await database.message.create({
1608
+ data: {
1609
+ content: safeContent,
1610
+ userId: user.userId,
1611
+ roomId,
1612
+ tenant_id: user.tenant_id,
1613
+ },
1614
+ include: {
1615
+ user: { select: { id: true, name: true } },
1616
+ },
1617
+ });
1618
+
1619
+ // Emit real-time event
1620
+ await event.emit('message.created', {
1621
+ messageId: message.id,
1622
+ content: message.content,
1623
+ user: message.user,
1624
+ roomId,
1625
+ timestamp: message.createdAt,
1626
+ });
1627
+
1628
+ // Send via WebSocket
1629
+ io.to(`room:${roomId}`).emit('new-message', {
1630
+ id: message.id,
1631
+ content: message.content,
1632
+ user: message.user,
1633
+ timestamp: message.createdAt,
1634
+ });
1635
+
1636
+ logger.info('Message sent', {
1637
+ messageId: message.id,
1638
+ userId: user.userId,
1639
+ roomId,
1640
+ });
1641
+
1642
+ res.json({
1643
+ success: true,
1644
+ message: {
1645
+ id: message.id,
1646
+ content: message.content,
1647
+ user: message.user,
1648
+ timestamp: message.createdAt,
1649
+ },
1650
+ });
1651
+ })
1652
+ );
1653
+
1654
+ // WebSocket authentication and real-time handling
1655
+ io.use(async (socket, next) => {
1656
+ try {
1657
+ const token = socket.handshake.auth.token;
1658
+ const user = auth.verifyToken(token);
1659
+ socket.user = user;
1660
+ next();
1661
+ } catch (err) {
1662
+ next(new Error('Authentication failed'));
1663
+ }
1664
+ });
1665
+
1666
+ io.on('connection', (socket) => {
1667
+ const user = socket.user;
1668
+
1669
+ logger.info('User connected', {
1670
+ userId: user.userId,
1671
+ socketId: socket.id,
1672
+ });
1673
+
1674
+ // Join user to their rooms
1675
+ socket.join(`user:${user.userId}`);
1676
+
1677
+ socket.on('join-room', async (roomId) => {
1678
+ // Verify user can join room
1679
+ const database = databaseClass.get();
1680
+ const room = await database.room.findFirst({
1681
+ where: {
1682
+ id: roomId,
1683
+ members: { some: { userId: user.userId } },
1684
+ },
1685
+ });
1686
+
1687
+ if (room) {
1688
+ await socket.join(`room:${roomId}`);
1689
+ socket.emit('joined-room', { roomId });
1690
+
1691
+ logger.info('User joined room', {
1692
+ userId: user.userId,
1693
+ roomId,
1694
+ });
1695
+ } else {
1696
+ socket.emit('error', { message: 'Access denied to room' });
1697
+ }
1698
+ });
1699
+
1700
+ socket.on('disconnect', () => {
1701
+ logger.info('User disconnected', { userId: user.userId });
1702
+ });
1703
+ });
1704
+
1705
+ // Event listeners for cross-service communication
1706
+ event.on('user.registered', async (data) => {
1707
+ // Send welcome notification
1708
+ io.to(`user:${data.userId}`).emit('notification', {
1709
+ type: 'welcome',
1710
+ message: 'Welcome to our platform!',
1711
+ timestamp: new Date(),
1712
+ });
1713
+ });
1714
+
1715
+ // Error handling middleware (MUST be last)
1716
+ app.use(error.handleErrors());
1717
+
1718
+ server.listen(3000, () => {
1719
+ logger.info('🚀 Server started with real-time support', { port: 3000 });
1720
+ });
1721
+ ```
1722
+
1723
+ # Production & Complete Examples 🚀
1724
+
1725
+ > **Production deployment, complete application examples, and comprehensive
1726
+ > guidance**
1727
+
1728
+ ## ENVIRONMENT VALIDATION
1729
+
1730
+ ### Production Environment Variables
1731
+
1732
+ ```bash
1733
+ # ✅ Framework (Required in production)
1734
+ NODE_ENV=production
1735
+ VOILA_AUTH_SECRET=your-super-secure-jwt-secret-key-minimum-32-chars
1736
+ VOILA_SECURITY_CSRF_SECRET=your-csrf-secret-key-minimum-32-chars
1737
+ VOILA_SECURITY_ENCRYPTION_KEY=64-char-hex-encryption-key-for-aes256
1738
+
1739
+ # ✅ Services (Required)
1740
+ DATABASE_URL=postgresql://user:password@host:5432/database
1741
+ REDIS_URL=redis://user:password@host:6379
1742
+
1743
+ # ✅ Email (Choose one)
1744
+ RESEND_API_KEY=re_your_api_key
1745
+ # OR SMTP_HOST=smtp.gmail.com
1746
+
1747
+ # ✅ Storage (Choose one)
1748
+ CLOUDFLARE_R2_BUCKET=your-bucket
1749
+ # OR AWS_S3_BUCKET=your-bucket
1750
+
1751
+ # ✅ Application
1752
+ APP_NAME=Your Production App
1753
+ APP_URL=https://yourapp.com
1754
+ ```
1755
+
1756
+ ### Startup Validation Pattern
1757
+
1758
+ ```javascript
1759
+ // ALWAYS validate environment at startup
1760
+ function validateProductionEnv() {
1761
+ if (process.env.NODE_ENV !== 'production') return;
1762
+
1763
+ const required = ['VOILA_AUTH_SECRET', 'DATABASE_URL', 'REDIS_URL'];
1764
+ const missing = required.filter((key) => !process.env[key]);
1765
+
1766
+ if (missing.length > 0) {
1767
+ console.error('❌ Missing required environment variables:', missing);
1768
+ process.exit(1);
1769
+ }
1770
+
1771
+ console.log('✅ Production environment validated');
1772
+ }
1773
+
1774
+ // App initialization
1775
+ async function initializeApp() {
1776
+ try {
1777
+ validateProductionEnv();
1778
+
1779
+ const config = configClass.get();
1780
+ const logger = loggerClass.get('init');
1781
+
1782
+ // Validate configuration
1783
+ config.getRequired('database.url');
1784
+ config.getRequired('auth.secret');
1785
+
1786
+ // Initialize database
1787
+ const database = databaseClass.get();
1788
+ await database.$queryRaw`SELECT 1`;
1789
+
1790
+ logger.info('✅ App initialized successfully');
1791
+ } catch (error) {
1792
+ console.error('❌ App initialization failed:', error.message);
1793
+ process.exit(1);
1794
+ }
1795
+ }
1796
+ ```
1797
+
1798
+ ---
1799
+
1800
+ ## GRACEFUL SHUTDOWN
1801
+
1802
+ ```javascript
1803
+ // ALWAYS implement graceful shutdown
1804
+ async function gracefulShutdown(signal) {
1805
+ console.log(`🔄 Received ${signal}, shutting down gracefully...`);
1806
+
1807
+ try {
1808
+ // Close server first (stop accepting new connections)
1809
+ if (server) {
1810
+ await new Promise((resolve) => server.close(resolve));
1811
+ }
1812
+
1813
+ // Close services in reverse dependency order
1814
+ await queueClass.get().close(); // Stop processing jobs
1815
+ await databaseClass.disconnect(); // Close DB connections
1816
+ await loggerClass.get().flush(); // Write remaining logs
1817
+
1818
+ console.log('✅ Graceful shutdown completed');
1819
+ process.exit(0);
1820
+ } catch (error) {
1821
+ console.error('❌ Shutdown error:', error);
1822
+ process.exit(1);
1823
+ }
1824
+ }
1825
+
1826
+ process.on('SIGTERM', gracefulShutdown);
1827
+ process.on('SIGINT', gracefulShutdown);
1828
+ process.on('SIGUSR2', gracefulShutdown); // For PM2
1829
+ ```
1830
+
1831
+ ---
1832
+
1833
+ ## MODULE INITIALIZATION ORDER
1834
+
1835
+ ```javascript
1836
+ // ALWAYS follow this exact order:
1837
+ // 1. Util (no dependencies)
1838
+ // 2. Config (no dependencies)
1839
+ // 3. Logger (depends on Config)
1840
+ // 4. Error (depends on Config + Logger)
1841
+ // 5. Auth (depends on Config + Error)
1842
+ // 6. Security (depends on Config + Error)
1843
+ // 7. Database (depends on Config + Logger + Error)
1844
+ // 8. Cache (depends on Config + Logger)
1845
+ // 9. Storage (depends on Config + Logger + Error)
1846
+ // 10. Email (depends on Config + Logger + Error)
1847
+ // 11. Queue (depends on Config + Logger + Error)
1848
+ // 12. Event (depends on Config + Logger + Error)
1849
+
1850
+ async function initializeApp() {
1851
+ try {
1852
+ // 1-2. Core utilities first
1853
+ const config = configClass.get();
1854
+ const util = utilClass.get();
1855
+
1856
+ // 3-4. Logging and error handling
1857
+ const logger = loggerClass.get('init');
1858
+ const error = errorClass.get();
1859
+
1860
+ // 5-6. Security modules
1861
+ const auth = authClass.get();
1862
+ const security = securityClass.get();
1863
+
1864
+ // 7. Database
1865
+ const database = databaseClass.get();
1866
+
1867
+ // 8-12. Infrastructure services
1868
+ const cache = cacheClass.get('app');
1869
+ const storage = storageClass.get();
1870
+ const email = emailClass.get();
1871
+ const queue = queueClass.get();
1872
+ const event = eventClass.get('app');
1873
+
1874
+ logger.info('✅ All modules initialized successfully');
1875
+ return {
1876
+ config,
1877
+ util,
1878
+ logger,
1879
+ error,
1880
+ auth,
1881
+ security,
1882
+ database,
1883
+ cache,
1884
+ storage,
1885
+ email,
1886
+ queue,
1887
+ event,
1888
+ };
1889
+ } catch (error) {
1890
+ console.error('❌ Module initialization failed:', error.message);
1891
+ process.exit(1);
1892
+ }
1893
+ }
1894
+ ```
1895
+
1896
+ ---
1897
+
1898
+ ## COMPLETE PRODUCTION APPLICATION
1899
+
1900
+ ```javascript
1901
+ // Complete production-ready Express application
1902
+ import express from 'express';
1903
+ import session from 'express-session';
1904
+ import multer from 'multer';
1905
+ import { createServer } from 'http';
1906
+
1907
+ // Import all AppKit modules
1908
+ import { utilClass } from '@bloomneo/appkit/util';
1909
+ import { configClass } from '@bloomneo/appkit/config';
1910
+ import { authClass } from '@bloomneo/appkit/auth';
1911
+ import { loggerClass } from '@bloomneo/appkit/logger';
1912
+ import { errorClass } from '@bloomneo/appkit/error';
1913
+ import { securityClass } from '@bloomneo/appkit/security';
1914
+ import { databaseClass } from '@bloomneo/appkit/database';
1915
+ import { cacheClass } from '@bloomneo/appkit/cache';
1916
+ import { storageClass } from '@bloomneo/appkit/storage';
1917
+ import { queueClass } from '@bloomneo/appkit/queue';
1918
+ import { emailClass } from '@bloomneo/appkit/email';
1919
+ import { eventClass } from '@bloomneo/appkit/event';
1920
+
1921
+ const app = express();
1922
+ const server = createServer(app);
1923
+ const upload = multer({ storage: multer.memoryStorage() });
1924
+
1925
+ // Initialize modules in correct order
1926
+ const util = utilClass.get();
1927
+ const config = configClass.get();
1928
+ const logger = loggerClass.get('app');
1929
+ const error = errorClass.get();
1930
+ const auth = authClass.get();
1931
+ const security = securityClass.get();
1932
+ const storage = storageClass.get();
1933
+ const queue = queueClass.get();
1934
+ const email = emailClass.get();
1935
+ const event = eventClass.get('app');
1936
+
1937
+ // Startup validation
1938
+ validateProductionEnv();
1939
+ await initializeApp();
1940
+
1941
+ // Middleware setup (ORDER CRITICAL)
1942
+ app.use(express.json({ limit: '10mb' }));
1943
+ app.use(
1944
+ session({
1945
+ secret: config.getRequired('session.secret'),
1946
+ resave: false,
1947
+ saveUninitialized: false,
1948
+ cookie: {
1949
+ secure: config.isProduction(),
1950
+ httpOnly: true,
1951
+ maxAge: 24 * 60 * 60 * 1000, // 24 hours
1952
+ },
1953
+ })
1954
+ );
1955
+
1956
+ app.use(security.forms()); // CSRF protection
1957
+ app.use('/api', security.requests(100, 900000)); // Rate limiting
1958
+
1959
+ // Request logging
1960
+ app.use((req, res, next) => {
1961
+ req.requestId = util.uuid();
1962
+ req.logger = logger.child({
1963
+ requestId: req.requestId,
1964
+ method: req.method,
1965
+ url: req.url,
1966
+ });
1967
+
1968
+ const startTime = Date.now();
1969
+ res.on('finish', () => {
1970
+ req.logger.info('Request completed', {
1971
+ statusCode: res.statusCode,
1972
+ duration: Date.now() - startTime,
1973
+ });
1974
+ });
1975
+
1976
+ next();
1977
+ });
1978
+
1979
+ // Health check
1980
+ app.get('/health', async (req, res) => {
1981
+ try {
1982
+ const database = databaseClass.get();
1983
+ await database.$queryRaw`SELECT 1`;
1984
+
1985
+ res.json({
1986
+ status: 'ok',
1987
+ timestamp: new Date().toISOString(),
1988
+ environment: config.getEnvironment(),
1989
+ });
1990
+ } catch (error) {
1991
+ res.status(503).json({
1992
+ status: 'error',
1993
+ error: error.message,
1994
+ });
1995
+ }
1996
+ });
1997
+
1998
+ // Authentication endpoints
1999
+ app.post(
2000
+ '/auth/register',
2001
+ error.asyncRoute(async (req, res) => {
2002
+ const { email, password, name } = req.body;
2003
+
2004
+ // Input validation
2005
+ if (util.isEmpty(email)) throw error.badRequest('Email required');
2006
+ if (!email.includes('@')) throw error.badRequest('Invalid email format');
2007
+ if (util.isEmpty(password)) throw error.badRequest('Password required');
2008
+ if (password.length < 8)
2009
+ throw error.badRequest('Password must be 8+ characters');
2010
+ if (util.isEmpty(name)) throw error.badRequest('Name required');
2011
+
2012
+ // Sanitize inputs
2013
+ const safeEmail = security.input(email.toLowerCase());
2014
+ const safeName = security.input(name, { maxLength: 50 });
2015
+
2016
+ // Check for existing user
2017
+ const database = databaseClass.get();
2018
+ const existingUser = await database.user.findUnique({
2019
+ where: { email: safeEmail },
2020
+ });
2021
+
2022
+ if (existingUser) {
2023
+ throw error.conflict('Email already registered');
2024
+ }
2025
+
2026
+ // Create user
2027
+ const hashedPassword = await auth.hashPassword(password);
2028
+ const user = await database.user.create({
2029
+ data: {
2030
+ email: safeEmail,
2031
+ name: safeName,
2032
+ password: hashedPassword,
2033
+ role: 'user',
2034
+ level: 'basic',
2035
+ },
2036
+ });
2037
+
2038
+ // Generate token
2039
+ const token = auth.signToken({
2040
+ userId: user.id,
2041
+ role: user.role,
2042
+ level: user.level,
2043
+ });
2044
+
2045
+ // Queue welcome email
2046
+ await queue.add('welcome-email', {
2047
+ userId: user.id,
2048
+ email: user.email,
2049
+ name: user.name,
2050
+ });
2051
+
2052
+ // Emit event
2053
+ await event.emit('user.registered', {
2054
+ userId: user.id,
2055
+ email: user.email,
2056
+ timestamp: new Date(),
2057
+ });
2058
+
2059
+ req.logger.info('User registered', { userId: user.id });
2060
+
2061
+ res.json({
2062
+ success: true,
2063
+ token,
2064
+ user: util.pick(user, ['id', 'email', 'name']),
2065
+ });
2066
+ })
2067
+ );
2068
+
2069
+ app.post(
2070
+ '/auth/login',
2071
+ error.asyncRoute(async (req, res) => {
2072
+ const { email, password } = req.body;
2073
+
2074
+ if (util.isEmpty(email)) throw error.badRequest('Email required');
2075
+ if (util.isEmpty(password)) throw error.badRequest('Password required');
2076
+
2077
+ const database = databaseClass.get();
2078
+ const user = await database.user.findUnique({
2079
+ where: { email: email.toLowerCase() },
2080
+ });
2081
+
2082
+ if (!user) throw error.unauthorized('Invalid credentials');
2083
+
2084
+ const isValid = await auth.comparePassword(password, user.password);
2085
+ if (!isValid) throw error.unauthorized('Invalid credentials');
2086
+
2087
+ const token = auth.signToken({
2088
+ userId: user.id,
2089
+ role: user.role,
2090
+ level: user.level,
2091
+ });
2092
+
2093
+ // Cache user data
2094
+ const userCache = cacheClass.get('users');
2095
+ await userCache.set(`user:${user.id}`, user, 3600);
2096
+
2097
+ req.logger.info('User logged in', { userId: user.id });
2098
+
2099
+ res.json({
2100
+ success: true,
2101
+ token,
2102
+ user: util.pick(user, ['id', 'email', 'name']),
2103
+ });
2104
+ })
2105
+ );
2106
+
2107
+ // File upload endpoint
2108
+ app.post(
2109
+ '/api/upload',
2110
+ auth.requireLogin(),
2111
+ upload.single('file'),
2112
+ error.asyncRoute(async (req, res) => {
2113
+ if (!req.file) throw error.badRequest('No file uploaded');
2114
+
2115
+ const user = auth.user(req);
2116
+
2117
+ // Validate file
2118
+ const allowedTypes = ['image/jpeg', 'image/png', 'application/pdf'];
2119
+ if (!allowedTypes.includes(req.file.mimetype)) {
2120
+ throw error.badRequest('File type not allowed');
2121
+ }
2122
+
2123
+ const maxSize = 10 * 1024 * 1024; // 10MB
2124
+ if (req.file.size > maxSize) {
2125
+ throw error.badRequest('File too large (max 10MB)');
2126
+ }
2127
+
2128
+ // Store file
2129
+ const fileKey = `uploads/${user.userId}/${Date.now()}-${util.slugify(req.file.originalname)}`;
2130
+ await storage.put(fileKey, req.file.buffer, {
2131
+ contentType: req.file.mimetype,
2132
+ });
2133
+
2134
+ // Save to database
2135
+ const database = databaseClass.get();
2136
+ const file = await database.file.create({
2137
+ data: {
2138
+ key: fileKey,
2139
+ name: req.file.originalname,
2140
+ size: req.file.size,
2141
+ contentType: req.file.mimetype,
2142
+ userId: user.userId,
2143
+ },
2144
+ });
2145
+
2146
+ // Queue processing
2147
+ await queue.add('process-file', {
2148
+ fileId: file.id,
2149
+ fileKey,
2150
+ userId: user.userId,
2151
+ });
2152
+
2153
+ req.logger.info('File uploaded', {
2154
+ fileId: file.id,
2155
+ size: util.formatBytes(req.file.size),
2156
+ });
2157
+
2158
+ res.json({
2159
+ success: true,
2160
+ file: {
2161
+ id: file.id,
2162
+ url: storage.url(fileKey),
2163
+ name: req.file.originalname,
2164
+ size: util.formatBytes(req.file.size),
2165
+ },
2166
+ });
2167
+ })
2168
+ );
2169
+
2170
+ // Protected user endpoint
2171
+ app.get(
2172
+ '/api/profile',
2173
+ auth.requireLogin(),
2174
+ error.asyncRoute(async (req, res) => {
2175
+ const user = auth.user(req);
2176
+ const userCache = cacheClass.get('users');
2177
+
2178
+ // Try cache first
2179
+ let userData = await userCache.get(`user:${user.userId}`);
2180
+
2181
+ if (!userData) {
2182
+ const database = databaseClass.get();
2183
+ userData = await database.user.findUnique({
2184
+ where: { id: user.userId },
2185
+ select: { id: true, email: true, name: true, createdAt: true },
2186
+ });
2187
+
2188
+ if (userData) {
2189
+ await userCache.set(`user:${user.userId}`, userData, 3600);
2190
+ }
2191
+ }
2192
+
2193
+ if (!userData) throw error.notFound('User not found');
2194
+
2195
+ res.json(userData);
2196
+ })
2197
+ );
2198
+
2199
+ // Admin endpoint
2200
+ app.get(
2201
+ '/api/admin/users',
2202
+ auth.requireRole('admin.tenant'),
2203
+ error.asyncRoute(async (req, res) => {
2204
+ const dbTenants = databaseClass.getTenants();
2205
+ const users = await dbTenants.user.findMany({
2206
+ select: {
2207
+ id: true,
2208
+ email: true,
2209
+ name: true,
2210
+ role: true,
2211
+ level: true,
2212
+ createdAt: true,
2213
+ },
2214
+ });
2215
+
2216
+ res.json(users);
2217
+ })
2218
+ );
2219
+
2220
+ // Background job processors
2221
+ queue.process('welcome-email', async (data) => {
2222
+ try {
2223
+ await email.send({
2224
+ to: data.email,
2225
+ subject: `Welcome to ${config.get('app.name', 'Our Platform')}!`,
2226
+ html: `
2227
+ <h1>Welcome ${data.name}!</h1>
2228
+ <p>Thanks for joining us. We're excited to have you on board!</p>
2229
+ <a href="${config.get('app.url')}/dashboard">Get Started</a>
2230
+ `,
2231
+ });
2232
+
2233
+ logger.info('Welcome email sent', { userId: data.userId });
2234
+ return { sent: true };
2235
+ } catch (error) {
2236
+ logger.error('Welcome email failed', {
2237
+ userId: data.userId,
2238
+ error: error.message,
2239
+ });
2240
+ throw error;
2241
+ }
2242
+ });
2243
+
2244
+ queue.process('process-file', async (data) => {
2245
+ const processingLogger = loggerClass.get('processing');
2246
+
2247
+ try {
2248
+ // Simulate file processing
2249
+ await util.sleep(2000);
2250
+
2251
+ const database = databaseClass.get();
2252
+ await database.file.update({
2253
+ where: { id: data.fileId },
2254
+ data: { processed: true, processedAt: new Date() },
2255
+ });
2256
+
2257
+ processingLogger.info('File processed', { fileId: data.fileId });
2258
+ return { processed: true };
2259
+ } catch (error) {
2260
+ processingLogger.error('File processing failed', {
2261
+ fileId: data.fileId,
2262
+ error: error.message,
2263
+ });
2264
+ throw error;
2265
+ }
2266
+ });
2267
+
2268
+ // Error handling middleware (MUST be last)
2269
+ app.use(error.handleErrors());
2270
+
2271
+ // Start server
2272
+ const port = config.get('server.port', 3000);
2273
+ const host = config.get('server.host', '0.0.0.0');
2274
+
2275
+ server.listen(port, host, () => {
2276
+ logger.info('🌟 Server ready', { port, host });
2277
+ });
2278
+
2279
+ // Graceful shutdown
2280
+ process.on('SIGTERM', gracefulShutdown);
2281
+ process.on('SIGINT', gracefulShutdown);
2282
+ ```
2283
+
2284
+ ---
2285
+
2286
+ ## TESTING PATTERNS
2287
+
2288
+ ### Test Setup
2289
+
2290
+ ```javascript
2291
+ import {
2292
+ utilClass,
2293
+ loggerClass,
2294
+ cacheClass,
2295
+ configClass,
2296
+ databaseClass,
2297
+ } from '@bloomneo/appkit';
2298
+
2299
+ describe('AppKit Application', () => {
2300
+ beforeEach(() => {
2301
+ // Force test configuration
2302
+ configClass.reset({
2303
+ database: { url: 'memory://test' },
2304
+ cache: { strategy: 'memory' },
2305
+ logging: { level: 'silent' },
2306
+ });
2307
+ });
2308
+
2309
+ afterEach(async () => {
2310
+ // ALWAYS reset module state between tests
2311
+ utilClass.clearCache();
2312
+ await loggerClass.clear();
2313
+ await cacheClass.clear();
2314
+ configClass.clearCache();
2315
+ await databaseClass.clear();
2316
+ });
2317
+
2318
+ test('should handle user registration', async () => {
2319
+ const util = utilClass.get();
2320
+ const auth = authClass.get();
2321
+
2322
+ const userData = {
2323
+ email: 'test@example.com',
2324
+ name: 'Test User',
2325
+ };
2326
+
2327
+ const email = util.get(userData, 'email');
2328
+ expect(email).toBe('test@example.com');
2329
+
2330
+ const token = auth.signToken({
2331
+ userId: 123,
2332
+ role: 'user',
2333
+ level: 'basic',
2334
+ });
2335
+
2336
+ expect(token).toBeDefined();
2337
+ });
2338
+ });
2339
+ ```
2340
+
2341
+ ---
2342
+
2343
+ ## DOCKER DEPLOYMENT
2344
+
2345
+ ### Dockerfile
2346
+
2347
+ ```dockerfile
2348
+ FROM node:18-alpine
2349
+
2350
+ WORKDIR /app
2351
+
2352
+ # Copy package files
2353
+ COPY package*.json ./
2354
+ RUN npm ci --only=production
2355
+
2356
+ # Copy application
2357
+ COPY . .
2358
+
2359
+ # Health check
2360
+ HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
2361
+ CMD node -e "require('http').get('http://localhost:3000/health', (res) => { process.exit(res.statusCode === 200 ? 0 : 1) })"
2362
+
2363
+ EXPOSE 3000
2364
+ CMD ["node", "server.js"]
2365
+ ```
2366
+
2367
+ ### Docker Compose
2368
+
2369
+ ```yaml
2370
+ version: '3.8'
2371
+ services:
2372
+ app:
2373
+ build: .
2374
+ environment:
2375
+ - NODE_ENV=production
2376
+ - DATABASE_URL=postgresql://user:pass@postgres:5432/app
2377
+ - REDIS_URL=redis://redis:6379
2378
+ - VOILA_AUTH_SECRET=${VOILA_AUTH_SECRET}
2379
+ - VOILA_SECURITY_CSRF_SECRET=${VOILA_SECURITY_CSRF_SECRET}
2380
+ ports:
2381
+ - '3000:3000'
2382
+ depends_on:
2383
+ - postgres
2384
+ - redis
2385
+ restart: unless-stopped
2386
+
2387
+ postgres:
2388
+ image: postgres:15-alpine
2389
+ environment:
2390
+ POSTGRES_DB: app
2391
+ POSTGRES_USER: user
2392
+ POSTGRES_PASSWORD: pass
2393
+ volumes:
2394
+ - postgres_data:/var/lib/postgresql/data
2395
+
2396
+ redis:
2397
+ image: redis:7-alpine
2398
+ volumes:
2399
+ - redis_data:/data
2400
+
2401
+ volumes:
2402
+ postgres_data:
2403
+ redis_data:
2404
+ ```
2405
+
2406
+ ---
2407
+
2408
+ ## COMMON LLM MISTAKES TO AVOID
2409
+
2410
+ ```javascript
2411
+ // ❌ WRONG - Unsafe property access
2412
+ const name = user.profile.name; // Can crash on undefined
2413
+
2414
+ // ✅ CORRECT - Safe access with default
2415
+ const name = util.get(user, 'profile.name', 'Guest');
2416
+
2417
+ // ❌ WRONG - Missing required token fields
2418
+ auth.signToken({ userId: 123 }); // Missing role/level
2419
+
2420
+ // ✅ CORRECT - All required fields
2421
+ auth.signToken({ userId: 123, role: 'admin', level: 'tenant' });
2422
+
2423
+ // ❌ WRONG - Wrong error types
2424
+ throw error.serverError('Email required'); // Should be badRequest
2425
+
2426
+ // ✅ CORRECT - Semantic error types
2427
+ throw error.badRequest('Email required');
2428
+ throw error.unauthorized('Login required');
2429
+ throw error.forbidden('Admin access required');
2430
+
2431
+ // ❌ WRONG - Missing tenant_id in schema
2432
+ CREATE TABLE users (id, email, name); // Will break multi-tenancy
2433
+
2434
+ // ✅ CORRECT - Future-proof schema
2435
+ CREATE TABLE users (
2436
+ id uuid PRIMARY KEY,
2437
+ email text,
2438
+ name text,
2439
+ tenant_id text, -- MANDATORY
2440
+ INDEX idx_users_tenant (tenant_id) -- MANDATORY
2441
+ );
2442
+
2443
+ // ❌ WRONG - No startup validation
2444
+ app.listen(3000, () => {
2445
+ console.log('Server started'); // No validation!
2446
+ });
2447
+
2448
+ // ✅ CORRECT - Validate before starting
2449
+ validateProductionEnv();
2450
+ initializeApp().then(() => {
2451
+ app.listen(3000, () => {
2452
+ console.log('✅ Server ready');
2453
+ });
2454
+ });
2455
+
2456
+ // ❌ WRONG - No graceful shutdown
2457
+ process.exit(0); // Abrupt shutdown
2458
+
2459
+ // ✅ CORRECT - Graceful shutdown
2460
+ process.on('SIGTERM', gracefulShutdown);
2461
+
2462
+ // ❌ WRONG - No test cleanup
2463
+ test('should work', () => {
2464
+ // No cleanup between tests!
2465
+ });
2466
+
2467
+ // ✅ CORRECT - Proper test cleanup
2468
+ afterEach(async () => {
2469
+ await loggerClass.clear();
2470
+ configClass.clearCache();
2471
+ });
2472
+ ```
2473
+
2474
+ ---
2475
+
2476
+ ## 📋 COMPREHENSIVE CHECKLIST FOR LLMs
2477
+
2478
+ ### **Module Usage**
2479
+
2480
+ - [ ] Always use `moduleClass.get()` pattern
2481
+ - [ ] Use exact object names from reference table (singular, matches module
2482
+ name)
2483
+ - [ ] Use namespace suffixes only for cache
2484
+ (`userCache = cacheClass.get('users')`)
2485
+ - [ ] Follow dependency order for initialization
2486
+
2487
+ ### **Essential Modules**
2488
+
2489
+ - [ ] Use `util.get()` for safe property access
2490
+ - [ ] Include `userId`, `role`, `level` in JWT tokens (all required)
2491
+ - [ ] Use `config.getRequired()` for critical configuration
2492
+ - [ ] Follow UPPER_SNAKE_CASE → dot.notation convention
2493
+ - [ ] Use component-specific loggers (`loggerClass.get('component')`)
2494
+ - [ ] Log with structured data, not just messages
2495
+
2496
+ ### **Infrastructure Modules**
2497
+
2498
+ - [ ] Include `tenant_id` field in ALL database tables with index
2499
+ - [ ] Use appropriate database access pattern (normal vs admin vs org)
2500
+ - [ ] Use specific cache namespaces with getOrSet pattern
2501
+ - [ ] Queue heavy operations instead of blocking requests
2502
+ - [ ] Handle file uploads with proper error handling
2503
+ - [ ] Cache frequently accessed data with appropriate TTL
2504
+
2505
+ ### **System Modules**
2506
+
2507
+ - [ ] Use semantic error types (`badRequest`, `unauthorized`, `forbidden`,
2508
+ `notFound`, `serverError`)
2509
+ - [ ] Put session middleware BEFORE CSRF protection
2510
+ - [ ] Put error handling middleware LAST in Express
2511
+ - [ ] Wrap async routes with `error.asyncRoute()`
2512
+ - [ ] Always sanitize user input with `security.input()` and `security.html()`
2513
+ - [ ] Set required security environment variables
2514
+
2515
+ ### **Production Deployment**
2516
+
2517
+ - [ ] Validate environment variables at startup
2518
+ - [ ] Implement graceful shutdown for all production apps
2519
+ - [ ] Set up proper middleware order (session → CSRF → rate limiting → error
2520
+ handling)
2521
+ - [ ] Test database connections before starting server
2522
+ - [ ] Use structured logging with request IDs
2523
+ - [ ] Handle Docker signals properly (SIGTERM, SIGINT)
2524
+ - [ ] Set all required environment variables for production
2525
+
2526
+ ### **Testing**
2527
+
2528
+ - [ ] Reset module state between tests (`utilClass.clearCache()`, etc.)
2529
+ - [ ] Use proper test cleanup in afterEach
2530
+ - [ ] Force test configuration with `configClass.reset()`
2531
+ - [ ] Clean up resources (database, cache, logger)
2532
+
2533
+ ### **Framework Integration**
2534
+
2535
+ - [ ] Use `auth.requireLogin()` and `auth.requireRole()` middleware correctly
2536
+ - [ ] Apply rate limiting on public endpoints
2537
+ - [ ] Encrypt sensitive data before storing in database
2538
+ - [ ] Log important operations with structured data
2539
+ - [ ] Implement proper health check endpoints