@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,286 @@
1
+ /**
2
+ * SMTP email strategy with nodemailer and connection pooling
3
+ * @module @bloomneo/appkit/email
4
+ * @file src/email/strategies/smtp.ts
5
+ *
6
+ * @llm-rule WHEN: App has SMTP_HOST environment variable for universal email sending
7
+ * @llm-rule AVOID: Manual SMTP setup - this handles connection pooling, authentication, and reliability
8
+ * @llm-rule NOTE: Universal email strategy that works with any SMTP server (Gmail, Outlook, etc.)
9
+ */
10
+ /**
11
+ * SMTP email strategy with connection pooling and reliability features
12
+ */
13
+ export class SmtpStrategy {
14
+ config;
15
+ transporter = null;
16
+ connected = false;
17
+ /**
18
+ * Creates SMTP strategy with direct environment access (like auth pattern)
19
+ * @llm-rule WHEN: Email initialization with SMTP_HOST detected
20
+ * @llm-rule AVOID: Manual SMTP configuration - environment detection handles this
21
+ */
22
+ constructor(config) {
23
+ this.config = config;
24
+ }
25
+ /**
26
+ * Sends email via SMTP with automatic connection management
27
+ * @llm-rule WHEN: Sending emails through SMTP servers
28
+ * @llm-rule AVOID: Manual SMTP calls - this handles all connection complexity
29
+ * @llm-rule NOTE: Includes connection pooling, authentication, and error handling
30
+ */
31
+ async send(data) {
32
+ try {
33
+ // Ensure transporter is connected
34
+ await this.ensureConnected();
35
+ // Convert to nodemailer format
36
+ const mailOptions = this.convertToSmtpFormat(data);
37
+ // Send via SMTP
38
+ const result = await this.transporter.sendMail(mailOptions);
39
+ return {
40
+ success: true,
41
+ messageId: result.messageId,
42
+ };
43
+ }
44
+ catch (error) {
45
+ const errorMessage = this.parseSmtpError(error);
46
+ if (this.config.environment.isDevelopment) {
47
+ console.error(`[AppKit] SMTP error:`, errorMessage);
48
+ }
49
+ return {
50
+ success: false,
51
+ error: errorMessage,
52
+ };
53
+ }
54
+ }
55
+ /**
56
+ * Disconnects SMTP strategy gracefully
57
+ * @llm-rule WHEN: App shutdown or email cleanup
58
+ * @llm-rule AVOID: Abrupt disconnection - graceful shutdown prevents connection issues
59
+ */
60
+ async disconnect() {
61
+ if (!this.connected || !this.transporter)
62
+ return;
63
+ try {
64
+ await this.transporter.close();
65
+ this.connected = false;
66
+ this.transporter = null;
67
+ if (this.config.environment.isDevelopment) {
68
+ console.log(`👋 [AppKit] SMTP disconnected`);
69
+ }
70
+ }
71
+ catch (error) {
72
+ console.error(`⚠️ [AppKit] SMTP disconnect error:`, error.message);
73
+ }
74
+ }
75
+ // Private helper methods
76
+ /**
77
+ * Ensures SMTP transporter is connected
78
+ */
79
+ async ensureConnected() {
80
+ if (this.connected && this.transporter)
81
+ return;
82
+ try {
83
+ // Dynamic import for nodemailer
84
+ const nodemailer = await import('nodemailer');
85
+ const smtpConfig = this.config.smtp;
86
+ // Create transporter with connection pooling
87
+ this.transporter = nodemailer.createTransport({
88
+ host: smtpConfig.host,
89
+ port: smtpConfig.port,
90
+ secure: smtpConfig.secure,
91
+ auth: smtpConfig.auth.user && smtpConfig.auth.pass ? {
92
+ user: smtpConfig.auth.user,
93
+ pass: smtpConfig.auth.pass,
94
+ } : undefined,
95
+ pool: smtpConfig.pool,
96
+ maxConnections: 5,
97
+ maxMessages: 100,
98
+ rateDelta: 1000,
99
+ rateLimit: 10,
100
+ connectionTimeout: smtpConfig.timeout,
101
+ socketTimeout: smtpConfig.timeout,
102
+ // Security options
103
+ tls: {
104
+ rejectUnauthorized: this.config.environment.isProduction,
105
+ },
106
+ });
107
+ // Verify connection
108
+ await this.transporter.verify();
109
+ this.connected = true;
110
+ if (this.config.environment.isDevelopment) {
111
+ console.log(`✅ [AppKit] SMTP connected to ${smtpConfig.host}:${smtpConfig.port}`);
112
+ }
113
+ }
114
+ catch (error) {
115
+ this.connected = false;
116
+ this.transporter = null;
117
+ throw new Error(`SMTP connection failed: ${error.message}`);
118
+ }
119
+ }
120
+ /**
121
+ * Converts EmailData to nodemailer format
122
+ */
123
+ convertToSmtpFormat(data) {
124
+ const mailOptions = {
125
+ from: this.formatEmailAddress(data.from),
126
+ to: this.formatEmailAddresses(data.to),
127
+ subject: data.subject,
128
+ };
129
+ // Add content
130
+ if (data.html) {
131
+ mailOptions.html = data.html;
132
+ }
133
+ if (data.text) {
134
+ mailOptions.text = data.text;
135
+ }
136
+ // Add optional fields
137
+ if (data.replyTo) {
138
+ mailOptions.replyTo = this.formatEmailAddress(data.replyTo);
139
+ }
140
+ if (data.cc) {
141
+ mailOptions.cc = this.formatEmailAddresses(data.cc);
142
+ }
143
+ if (data.bcc) {
144
+ mailOptions.bcc = this.formatEmailAddresses(data.bcc);
145
+ }
146
+ // Add attachments
147
+ if (data.attachments && data.attachments.length > 0) {
148
+ mailOptions.attachments = this.formatAttachments(data.attachments);
149
+ }
150
+ return mailOptions;
151
+ }
152
+ /**
153
+ * Formats single email address for nodemailer
154
+ */
155
+ formatEmailAddress(address) {
156
+ if (typeof address === 'string') {
157
+ return address;
158
+ }
159
+ if (address.name) {
160
+ return `"${address.name}" <${address.email}>`;
161
+ }
162
+ return address.email;
163
+ }
164
+ /**
165
+ * Formats multiple email addresses for nodemailer
166
+ */
167
+ formatEmailAddresses(addresses) {
168
+ const addressArray = Array.isArray(addresses) ? addresses : [addresses];
169
+ return addressArray.map(addr => this.formatEmailAddress(addr)).join(', ');
170
+ }
171
+ /**
172
+ * Formats attachments for nodemailer
173
+ */
174
+ formatAttachments(attachments) {
175
+ return attachments.map(attachment => ({
176
+ filename: attachment.filename,
177
+ content: attachment.content,
178
+ contentType: attachment.contentType || this.guessContentType(attachment.filename),
179
+ }));
180
+ }
181
+ /**
182
+ * Guesses content type from filename
183
+ */
184
+ guessContentType(filename) {
185
+ const ext = filename.split('.').pop()?.toLowerCase();
186
+ const mimeTypes = {
187
+ pdf: 'application/pdf',
188
+ doc: 'application/msword',
189
+ docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
190
+ xls: 'application/vnd.ms-excel',
191
+ xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
192
+ png: 'image/png',
193
+ jpg: 'image/jpeg',
194
+ jpeg: 'image/jpeg',
195
+ gif: 'image/gif',
196
+ txt: 'text/plain',
197
+ html: 'text/html',
198
+ css: 'text/css',
199
+ js: 'application/javascript',
200
+ json: 'application/json',
201
+ zip: 'application/zip',
202
+ };
203
+ return mimeTypes[ext || ''] || 'application/octet-stream';
204
+ }
205
+ /**
206
+ * Parses SMTP errors into user-friendly messages
207
+ */
208
+ parseSmtpError(error) {
209
+ if (error.message) {
210
+ const message = error.message.toLowerCase();
211
+ // Authentication errors
212
+ if (message.includes('authentication') || message.includes('invalid credentials') ||
213
+ message.includes('username') || message.includes('password')) {
214
+ return 'SMTP authentication failed. Check SMTP_USER and SMTP_PASS environment variables.';
215
+ }
216
+ // Connection errors
217
+ if (message.includes('connection') || message.includes('connect') ||
218
+ message.includes('econnrefused') || message.includes('timeout')) {
219
+ return 'SMTP connection failed. Check SMTP_HOST and SMTP_PORT environment variables.';
220
+ }
221
+ // TLS/SSL errors
222
+ if (message.includes('tls') || message.includes('ssl') || message.includes('secure')) {
223
+ return 'SMTP TLS/SSL error. Check SMTP_SECURE environment variable or server settings.';
224
+ }
225
+ // Rate limiting
226
+ if (message.includes('rate') || message.includes('limit') || message.includes('quota')) {
227
+ return 'SMTP rate limit exceeded. Please try again later.';
228
+ }
229
+ // Recipient errors
230
+ if (message.includes('recipient') || message.includes('address') ||
231
+ message.includes('mailbox') || message.includes('does not exist')) {
232
+ return 'Invalid recipient email address or mailbox does not exist.';
233
+ }
234
+ // Sender errors
235
+ if (message.includes('sender') || message.includes('from') ||
236
+ message.includes('not authorized') || message.includes('relay')) {
237
+ return 'SMTP sender not authorized. Check FROM address and server relay settings.';
238
+ }
239
+ // Message size errors
240
+ if (message.includes('size') || message.includes('too large') || message.includes('exceeded')) {
241
+ return 'Email message too large. Reduce attachment size or content length.';
242
+ }
243
+ // DNS errors
244
+ if (message.includes('dns') || message.includes('hostname') || message.includes('resolve')) {
245
+ return 'DNS resolution failed. Check SMTP_HOST value.';
246
+ }
247
+ // Port errors
248
+ if (message.includes('port') || message.includes('refused') || message.includes('unreachable')) {
249
+ return 'SMTP port connection failed. Check SMTP_PORT value and firewall settings.';
250
+ }
251
+ return error.message;
252
+ }
253
+ // Check for specific error codes
254
+ if (error.code) {
255
+ switch (error.code) {
256
+ case 'ECONNREFUSED':
257
+ return 'SMTP connection refused. Check SMTP_HOST and SMTP_PORT.';
258
+ case 'ENOTFOUND':
259
+ return 'SMTP host not found. Check SMTP_HOST value.';
260
+ case 'ETIMEDOUT':
261
+ return 'SMTP connection timeout. Check network connectivity.';
262
+ case 'ECONNRESET':
263
+ return 'SMTP connection reset. Server may be overloaded.';
264
+ case 'ESOCKET':
265
+ return 'SMTP socket error. Check network settings.';
266
+ default:
267
+ return `SMTP error: ${error.code}`;
268
+ }
269
+ }
270
+ return 'Unknown SMTP error occurred';
271
+ }
272
+ /**
273
+ * Gets SMTP connection info for debugging
274
+ */
275
+ getConnectionInfo() {
276
+ const smtpConfig = this.config.smtp;
277
+ return {
278
+ host: smtpConfig.host,
279
+ port: smtpConfig.port,
280
+ secure: smtpConfig.secure,
281
+ authenticated: !!(smtpConfig.auth.user && smtpConfig.auth.pass),
282
+ connected: this.connected,
283
+ };
284
+ }
285
+ }
286
+ //# sourceMappingURL=smtp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"smtp.js","sourceRoot":"","sources":["../../../src/email/strategies/smtp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Smart defaults and environment validation for error handling
3
+ * @module @bloomneo/appkit/error
4
+ * @file src/error/defaults.ts
5
+ *
6
+ * @llm-rule WHEN: App startup - need to configure error handling behavior and messages
7
+ * @llm-rule AVOID: Calling multiple times - expensive environment parsing, use lazy loading in get()
8
+ * @llm-rule NOTE: Called once at startup, cached globally for performance
9
+ */
10
+ export interface ErrorMessages {
11
+ badRequest: string;
12
+ unauthorized: string;
13
+ forbidden: string;
14
+ notFound: string;
15
+ conflict: string;
16
+ serverError: string;
17
+ }
18
+ export interface MiddlewareConfig {
19
+ showStack: boolean;
20
+ logErrors: boolean;
21
+ }
22
+ export interface EnvironmentConfig {
23
+ isDevelopment: boolean;
24
+ isProduction: boolean;
25
+ isTest: boolean;
26
+ nodeEnv: string;
27
+ }
28
+ export interface ErrorConfig {
29
+ messages: ErrorMessages;
30
+ middleware: MiddlewareConfig;
31
+ environment: EnvironmentConfig;
32
+ }
33
+ /**
34
+ * Gets smart defaults using VOILA_ERROR_* environment variables
35
+ * @llm-rule WHEN: App startup to get production-ready error configuration
36
+ * @llm-rule AVOID: Calling repeatedly - expensive validation, cache the result
37
+ * @llm-rule NOTE: Automatically configures dev vs production error behavior
38
+ */
39
+ export declare function getSmartDefaults(): ErrorConfig;
40
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/error/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,aAAa,CAAC;IACxB,UAAU,EAAE,gBAAgB,CAAC;IAC7B,WAAW,EAAE,iBAAiB,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,WAAW,CAiC9C"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Smart defaults and environment validation for error handling
3
+ * @module @bloomneo/appkit/error
4
+ * @file src/error/defaults.ts
5
+ *
6
+ * @llm-rule WHEN: App startup - need to configure error handling behavior and messages
7
+ * @llm-rule AVOID: Calling multiple times - expensive environment parsing, use lazy loading in get()
8
+ * @llm-rule NOTE: Called once at startup, cached globally for performance
9
+ */
10
+ /**
11
+ * Gets smart defaults using VOILA_ERROR_* environment variables
12
+ * @llm-rule WHEN: App startup to get production-ready error configuration
13
+ * @llm-rule AVOID: Calling repeatedly - expensive validation, cache the result
14
+ * @llm-rule NOTE: Automatically configures dev vs production error behavior
15
+ */
16
+ export function getSmartDefaults() {
17
+ validateEnvironment();
18
+ const nodeEnv = process.env.NODE_ENV || 'development';
19
+ const isDevelopment = nodeEnv === 'development';
20
+ const isProduction = nodeEnv === 'production';
21
+ const isTest = nodeEnv === 'test';
22
+ return {
23
+ // Error message defaults with environment awareness
24
+ messages: {
25
+ badRequest: 'Bad Request',
26
+ unauthorized: process.env.VOILA_AUTH_MESSAGE || 'Authentication required',
27
+ forbidden: 'Access denied',
28
+ notFound: 'Not found',
29
+ conflict: 'Conflict',
30
+ serverError: isDevelopment ? 'Internal server error' : 'Server error',
31
+ },
32
+ // Error handling behavior with smart defaults
33
+ middleware: {
34
+ showStack: process.env.VOILA_ERROR_STACK === 'true' || isDevelopment,
35
+ logErrors: process.env.VOILA_ERROR_LOG !== 'false',
36
+ },
37
+ // Environment information
38
+ environment: {
39
+ isDevelopment,
40
+ isProduction,
41
+ isTest,
42
+ nodeEnv,
43
+ },
44
+ };
45
+ }
46
+ /**
47
+ * Validates environment variables for error configuration
48
+ * @llm-rule WHEN: App startup to ensure proper error environment configuration
49
+ * @llm-rule AVOID: Skipping validation - improper config causes silent failures
50
+ * @llm-rule NOTE: Validates essential boolean environment variables and NODE_ENV only
51
+ */
52
+ function validateEnvironment() {
53
+ // Validate VOILA_ERROR_STACK (essential for security)
54
+ const errorStack = process.env.VOILA_ERROR_STACK;
55
+ if (errorStack && !['true', 'false'].includes(errorStack.toLowerCase())) {
56
+ throw new Error(`Invalid VOILA_ERROR_STACK: "${errorStack}". Must be "true" or "false"`);
57
+ }
58
+ // Validate VOILA_ERROR_LOG (essential for debugging)
59
+ const errorLog = process.env.VOILA_ERROR_LOG;
60
+ if (errorLog && !['true', 'false'].includes(errorLog.toLowerCase())) {
61
+ throw new Error(`Invalid VOILA_ERROR_LOG: "${errorLog}". Must be "true" or "false"`);
62
+ }
63
+ // Validate NODE_ENV (essential for environment detection)
64
+ const nodeEnv = process.env.NODE_ENV;
65
+ if (nodeEnv && !['development', 'production', 'test', 'staging'].includes(nodeEnv)) {
66
+ console.warn(`[VoilaJSX AppKit] Unusual NODE_ENV: "${nodeEnv}". ` +
67
+ `Expected: development, production, test, or staging`);
68
+ }
69
+ // Essential production safety check
70
+ if (nodeEnv === 'production' && process.env.VOILA_ERROR_STACK === 'true') {
71
+ console.warn(`[VoilaJSX AppKit] Security warning: VOILA_ERROR_STACK=true in production. ` +
72
+ `Stack traces may expose internal application structure. Consider setting to false.`);
73
+ }
74
+ }
75
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../src/error/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6BH;;;;;GAKG;AACH,MAAM,UAAU,gBAAgB;IAC9B,mBAAmB,EAAE,CAAC;IAEtB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa,CAAC;IACtD,MAAM,aAAa,GAAG,OAAO,KAAK,aAAa,CAAC;IAChD,MAAM,YAAY,GAAG,OAAO,KAAK,YAAY,CAAC;IAC9C,MAAM,MAAM,GAAG,OAAO,KAAK,MAAM,CAAC;IAElC,OAAO;QACL,oDAAoD;QACpD,QAAQ,EAAE;YACR,UAAU,EAAE,aAAa;YACzB,YAAY,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,yBAAyB;YACzE,SAAS,EAAE,eAAe;YAC1B,QAAQ,EAAE,WAAW;YACrB,QAAQ,EAAE,UAAU;YACpB,WAAW,EAAE,aAAa,CAAC,CAAC,CAAC,uBAAuB,CAAC,CAAC,CAAC,cAAc;SACtE;QAED,8CAA8C;QAC9C,UAAU,EAAE;YACV,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,MAAM,IAAI,aAAa;YACpE,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,OAAO;SACnD;QAED,0BAA0B;QAC1B,WAAW,EAAE;YACX,aAAa;YACb,YAAY;YACZ,MAAM;YACN,OAAO;SACR;KACF,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,SAAS,mBAAmB;IAC1B,sDAAsD;IACtD,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IACjD,IAAI,UAAU,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,UAAU,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CACb,+BAA+B,UAAU,8BAA8B,CACxE,CAAC;IACJ,CAAC;IAED,qDAAqD;IACrD,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC7C,IAAI,QAAQ,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CACb,6BAA6B,QAAQ,8BAA8B,CACpE,CAAC;IACJ,CAAC;IAED,0DAA0D;IAC1D,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;IACrC,IAAI,OAAO,IAAI,CAAC,CAAC,aAAa,EAAE,YAAY,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;QACnF,OAAO,CAAC,IAAI,CACV,wCAAwC,OAAO,KAAK;YACpD,qDAAqD,CACtD,CAAC;IACJ,CAAC;IAED,oCAAoC;IACpC,IAAI,OAAO,KAAK,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,MAAM,EAAE,CAAC;QACzE,OAAO,CAAC,IAAI,CACV,4EAA4E;YAC5E,oFAAoF,CACrF,CAAC;IACJ,CAAC;AACH,CAAC"}
@@ -0,0 +1,140 @@
1
+ /**
2
+ * Core error class with semantic HTTP status codes and middleware
3
+ * @module @bloomneo/appkit/error
4
+ * @file src/error/error.ts
5
+ *
6
+ * @llm-rule WHEN: Building apps that need semantic HTTP error handling
7
+ * @llm-rule AVOID: Using directly - always get instance via errorClass.get()
8
+ * @llm-rule NOTE: Provides semantic error creation (badRequest, unauthorized) and Express middleware
9
+ */
10
+ import type { ErrorConfig } from './defaults.js';
11
+ export interface AppError extends Error {
12
+ statusCode: number;
13
+ type: string;
14
+ }
15
+ export interface ExpressRequest {
16
+ [key: string]: any;
17
+ }
18
+ export interface ExpressResponse {
19
+ status: (code: number) => ExpressResponse;
20
+ json: (data: any) => void;
21
+ }
22
+ export interface ExpressNextFunction {
23
+ (error?: any): void;
24
+ }
25
+ export type ExpressErrorHandler = (error: AppError, req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => void;
26
+ export type AsyncRouteHandler = (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => Promise<any>;
27
+ export interface ErrorHandlerOptions {
28
+ showStack?: boolean;
29
+ logErrors?: boolean;
30
+ }
31
+ /**
32
+ * Error class with semantic HTTP status codes and middleware functionality
33
+ */
34
+ export declare class ErrorClass {
35
+ config: ErrorConfig;
36
+ constructor(config: ErrorConfig);
37
+ /**
38
+ * Creates a 400 Bad Request error
39
+ * @llm-rule WHEN: Client sends invalid input data (missing fields, wrong format)
40
+ * @llm-rule AVOID: Using for server-side validation errors - use conflict() instead
41
+ * @llm-rule NOTE: EXAMPLES: missing email, invalid JSON, malformed request body
42
+ * @llm-rule NOTE: PATTERN: if (!req.body.email) throw error.badRequest('Email required');
43
+ */
44
+ badRequest(message?: string): AppError;
45
+ /**
46
+ * Creates a 401 Unauthorized error
47
+ * @llm-rule WHEN: Authentication is required but missing or invalid
48
+ * @llm-rule AVOID: Using for permission issues - use forbidden() for access control
49
+ * @llm-rule NOTE: EXAMPLES: missing token, invalid token, expired session
50
+ * @llm-rule NOTE: PATTERN: if (!token) throw error.unauthorized('Token required');
51
+ */
52
+ unauthorized(message?: string): AppError;
53
+ /**
54
+ * Creates a 403 Forbidden error
55
+ * @llm-rule WHEN: User is authenticated but lacks permission for the action
56
+ * @llm-rule AVOID: Using for authentication issues - use unauthorized() instead
57
+ * @llm-rule NOTE: EXAMPLES: insufficient role, blocked user, admin-only endpoint
58
+ * @llm-rule NOTE: PATTERN: if (!user.isAdmin) throw error.forbidden('Admin access required');
59
+ */
60
+ forbidden(message?: string): AppError;
61
+ /**
62
+ * Creates a 404 Not Found error
63
+ * @llm-rule WHEN: Requested resource does not exist
64
+ * @llm-rule AVOID: Using for business logic failures - use conflict() instead
65
+ * @llm-rule NOTE: EXAMPLES: user not found, post not found, missing file
66
+ * @llm-rule NOTE: PATTERN: if (!user) throw error.notFound('User not found');
67
+ */
68
+ notFound(message?: string): AppError;
69
+ /**
70
+ * Creates a 409 Conflict error
71
+ * @llm-rule WHEN: Business logic conflicts or resource already exists
72
+ * @llm-rule AVOID: Using for validation errors - use badRequest() for input validation
73
+ * @llm-rule NOTE: EXAMPLES: email already exists, duplicate username, state conflicts
74
+ * @llm-rule NOTE: PATTERN: if (existingUser) throw error.conflict('Email already registered');
75
+ */
76
+ conflict(message?: string): AppError;
77
+ /**
78
+ * Creates a 500 Server Error
79
+ * @llm-rule WHEN: Internal server failures like database errors, external API failures
80
+ * @llm-rule AVOID: Using for business logic issues - use appropriate 4xx errors instead
81
+ * @llm-rule NOTE: EXAMPLES: database connection failure, external API timeout, file system errors
82
+ * @llm-rule NOTE: PATTERN: catch (dbError) { throw error.serverError('Database unavailable'); }
83
+ */
84
+ serverError(message?: string): AppError;
85
+ /**
86
+ * Creates a custom error with any status code
87
+ * @llm-rule WHEN: Need custom HTTP status codes not covered by semantic methods
88
+ * @llm-rule AVOID: Using for standard HTTP codes - use semantic methods instead
89
+ * @llm-rule NOTE: EXAMPLES: 429 rate limit, 503 service unavailable, 418 teapot
90
+ * @llm-rule NOTE: PATTERN: error.createError(429, 'Rate limit exceeded', 'RATE_LIMIT');
91
+ */
92
+ createError(statusCode: number, message: string, type?: string): AppError;
93
+ /**
94
+ * Creates production-ready Express error handling middleware
95
+ * @llm-rule WHEN: Setting up Express app error handling - use as last middleware
96
+ * @llm-rule AVOID: Using multiple error handlers - this should be the final middleware
97
+ * @llm-rule NOTE: MIDDLEWARE SETUP: app.use(error.handleErrors()); // Must be LAST
98
+ * @llm-rule NOTE: AUTO-FEATURES: dev vs prod responses, stack trace hiding, error logging
99
+ */
100
+ handleErrors(options?: ErrorHandlerOptions): ExpressErrorHandler;
101
+ /**
102
+ * Wraps async route handlers to catch errors automatically
103
+ * @llm-rule WHEN: Creating async Express route handlers that might throw errors
104
+ * @llm-rule AVOID: Manual try/catch in every route - this handles it automatically
105
+ * @llm-rule NOTE: ASYNC PATTERN: app.post('/route', error.asyncRoute(async (req, res) => {...}));
106
+ * @llm-rule NOTE: ERROR FLOW: thrown errors → automatically caught → sent to handleErrors middleware
107
+ */
108
+ asyncRoute(fn: AsyncRouteHandler): (req: ExpressRequest, res: ExpressResponse, next: ExpressNextFunction) => void;
109
+ /**
110
+ * Checks if error is a 4xx client error
111
+ * @llm-rule WHEN: Need to categorize errors for logging or metrics
112
+ * @llm-rule AVOID: Manual status code checking - this handles the logic
113
+ * @llm-rule NOTE: CLIENT ERRORS: 400-499 status codes (user's fault)
114
+ */
115
+ isClientError(error: AppError): boolean;
116
+ /**
117
+ * Checks if error is a 5xx server error
118
+ * @llm-rule WHEN: Need to categorize errors for monitoring or alerting
119
+ * @llm-rule AVOID: Manual status code checking - this handles the logic
120
+ * @llm-rule NOTE: SERVER ERRORS: 500-599 status codes (server's fault)
121
+ */
122
+ isServerError(error: AppError): boolean;
123
+ /**
124
+ * Gets current environment detection info
125
+ * @llm-rule WHEN: Need to check current environment configuration
126
+ * @llm-rule AVOID: Direct process.env access - this provides parsed config
127
+ */
128
+ getEnvironmentInfo(): {
129
+ isDevelopment: boolean;
130
+ isProduction: boolean;
131
+ nodeEnv: string;
132
+ };
133
+ /**
134
+ * Gets current error configuration
135
+ * @llm-rule WHEN: Debugging error setup or inspecting current settings
136
+ * @llm-rule AVOID: Accessing config directly - this provides clean interface
137
+ */
138
+ getConfig(): ErrorConfig;
139
+ }
140
+ //# sourceMappingURL=error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../../src/error/error.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,MAAM,WAAW,QAAS,SAAQ,KAAK;IACrC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,eAAe,CAAC;IAC1C,IAAI,EAAE,CAAC,IAAI,EAAE,GAAG,KAAK,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,mBAAmB;IAClC,CAAC,KAAK,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,MAAM,mBAAmB,GAAG,CAChC,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,mBAAmB,KACtB,IAAI,CAAC;AAEV,MAAM,MAAM,iBAAiB,GAAG,CAC9B,GAAG,EAAE,cAAc,EACnB,GAAG,EAAE,eAAe,EACpB,IAAI,EAAE,mBAAmB,KACtB,OAAO,CAAC,GAAG,CAAC,CAAC;AAElB,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,UAAU;IACd,MAAM,EAAE,WAAW,CAAC;gBAEf,MAAM,EAAE,WAAW;IAI/B;;;;;;OAMG;IACH,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOtC;;;;;;OAMG;IACH,YAAY,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOxC;;;;;;OAMG;IACH,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOrC;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOpC;;;;;;OAMG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOpC;;;;;;OAMG;IACH,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOvC;;;;;;OAMG;IACH,WAAW,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ;IAOzE;;;;;;OAMG;IACH,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,mBAAmB;IAyCpE;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,EAAE,iBAAiB,GAAG,CAAC,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,eAAe,EAAE,IAAI,EAAE,mBAAmB,KAAK,IAAI;IAMjH;;;;;OAKG;IACH,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAIvC;;;;;OAKG;IACH,aAAa,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO;IAIvC;;;;OAIG;IACH,kBAAkB,IAAI;QAAE,aAAa,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAQxF;;;;OAIG;IACH,SAAS,IAAI,WAAW;CAGzB"}