@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,318 @@
1
+ /**
2
+ * Redis cache strategy with automatic connection management and retry logic
3
+ * @module @bloomneo/appkit/cache
4
+ * @file src/cache/strategies/redis.ts
5
+ *
6
+ * @llm-rule WHEN: App has REDIS_URL environment variable for distributed caching
7
+ * @llm-rule AVOID: Manual Redis setup - this handles connection, retry, and serialization automatically
8
+ * @llm-rule NOTE: Auto-reconnects on failure, handles JSON serialization, production-ready
9
+ */
10
+ /**
11
+ * Redis cache strategy with enterprise-grade reliability
12
+ */
13
+ export class RedisStrategy {
14
+ config;
15
+ client = null;
16
+ connected = false;
17
+ connectionPromise = null;
18
+ /**
19
+ * Creates Redis strategy with direct environment access (like auth pattern)
20
+ * @llm-rule WHEN: Cache initialization with Redis URL detected
21
+ * @llm-rule AVOID: Manual Redis configuration - environment detection handles this
22
+ */
23
+ constructor(config) {
24
+ this.config = config;
25
+ }
26
+ /**
27
+ * Connects to Redis with automatic retry and connection pooling
28
+ * @llm-rule WHEN: Cache initialization or reconnection after failure
29
+ * @llm-rule AVOID: Manual connection management - this handles all Redis complexity
30
+ */
31
+ async connect() {
32
+ if (this.connected)
33
+ return;
34
+ // Prevent multiple connection attempts
35
+ if (this.connectionPromise) {
36
+ return this.connectionPromise;
37
+ }
38
+ this.connectionPromise = this.establishConnection();
39
+ await this.connectionPromise;
40
+ this.connectionPromise = null;
41
+ }
42
+ /**
43
+ * Establishes Redis connection with retry logic
44
+ */
45
+ async establishConnection() {
46
+ try {
47
+ // Dynamic import for Redis client
48
+ const { createClient } = await import('redis');
49
+ const redisConfig = this.config.redis;
50
+ // Create Redis client with comprehensive configuration
51
+ this.client = createClient({
52
+ url: redisConfig.url,
53
+ password: redisConfig.password,
54
+ socket: {
55
+ connectTimeout: redisConfig.connectTimeout,
56
+ reconnectStrategy: (retries) => {
57
+ if (retries >= redisConfig.maxRetries) {
58
+ console.error(`[AppKit] Redis max retries (${redisConfig.maxRetries}) exceeded`);
59
+ return new Error('Redis connection failed');
60
+ }
61
+ const delay = Math.min(redisConfig.retryDelay * Math.pow(2, retries), 10000);
62
+ console.warn(`[AppKit] Redis reconnecting in ${delay}ms (attempt ${retries + 1})`);
63
+ return delay;
64
+ },
65
+ },
66
+ commandsQueueMaxLength: 1000,
67
+ // Set global command timeout via client options
68
+ isolationPoolOptions: {
69
+ min: 1,
70
+ max: 10,
71
+ },
72
+ });
73
+ // Set up event handlers
74
+ this.setupEventHandlers();
75
+ // Connect to Redis
76
+ await this.client.connect();
77
+ this.connected = true;
78
+ if (this.config.environment.isDevelopment) {
79
+ console.log(`✅ [AppKit] Redis connected to ${this.maskUrl(redisConfig.url)}`);
80
+ }
81
+ }
82
+ catch (error) {
83
+ this.connected = false;
84
+ this.client = null;
85
+ throw new Error(`Redis connection failed: ${error.message}`);
86
+ }
87
+ }
88
+ /**
89
+ * Sets up Redis event handlers for connection management
90
+ */
91
+ setupEventHandlers() {
92
+ if (!this.client)
93
+ return;
94
+ this.client.on('error', (error) => {
95
+ console.error('[AppKit] Redis error:', error.message);
96
+ this.connected = false;
97
+ });
98
+ this.client.on('connect', () => {
99
+ if (this.config.environment.isDevelopment) {
100
+ console.log('🔄 [AppKit] Redis connecting...');
101
+ }
102
+ });
103
+ this.client.on('ready', () => {
104
+ this.connected = true;
105
+ if (this.config.environment.isDevelopment) {
106
+ console.log('✅ [AppKit] Redis ready');
107
+ }
108
+ });
109
+ this.client.on('reconnecting', () => {
110
+ this.connected = false;
111
+ if (this.config.environment.isDevelopment) {
112
+ console.log('🔄 [AppKit] Redis reconnecting...');
113
+ }
114
+ });
115
+ this.client.on('end', () => {
116
+ this.connected = false;
117
+ if (this.config.environment.isDevelopment) {
118
+ console.log('👋 [AppKit] Redis connection ended');
119
+ }
120
+ });
121
+ }
122
+ /**
123
+ * Disconnects from Redis gracefully
124
+ * @llm-rule WHEN: App shutdown or cache cleanup
125
+ * @llm-rule AVOID: Abrupt disconnection - graceful shutdown prevents data loss
126
+ */
127
+ async disconnect() {
128
+ if (!this.client || !this.connected)
129
+ return;
130
+ try {
131
+ await this.client.quit();
132
+ this.connected = false;
133
+ this.client = null;
134
+ }
135
+ catch (error) {
136
+ console.error('[AppKit] Redis disconnect error:', error.message);
137
+ // Force close if graceful quit fails
138
+ if (this.client) {
139
+ this.client.disconnect();
140
+ this.client = null;
141
+ }
142
+ }
143
+ }
144
+ /**
145
+ * Gets value from Redis with automatic JSON deserialization
146
+ * @llm-rule WHEN: Retrieving cached data from distributed Redis cache
147
+ * @llm-rule AVOID: Manual Redis commands - this handles serialization automatically
148
+ */
149
+ async get(key) {
150
+ await this.ensureConnected();
151
+ try {
152
+ const value = await this.client.get(key);
153
+ if (value === null) {
154
+ return null; // Key not found or expired
155
+ }
156
+ // Deserialize JSON value
157
+ return this.deserialize(value);
158
+ }
159
+ catch (error) {
160
+ console.error(`[AppKit] Redis get error for key "${key}":`, error.message);
161
+ return null; // Graceful degradation
162
+ }
163
+ }
164
+ /**
165
+ * Sets value in Redis with TTL and automatic JSON serialization
166
+ * @llm-rule WHEN: Storing data in distributed Redis cache with expiration
167
+ * @llm-rule AVOID: Manual Redis commands - this handles serialization and TTL automatically
168
+ */
169
+ async set(key, value, ttl) {
170
+ await this.ensureConnected();
171
+ try {
172
+ // Serialize value to JSON
173
+ const serialized = this.serialize(value);
174
+ // Set with TTL (Redis EX option expects seconds)
175
+ const result = await this.client.setEx(key, ttl, serialized);
176
+ return result === 'OK';
177
+ }
178
+ catch (error) {
179
+ console.error(`[AppKit] Redis set error for key "${key}":`, error.message);
180
+ return false;
181
+ }
182
+ }
183
+ /**
184
+ * Deletes key from Redis
185
+ * @llm-rule WHEN: Cache invalidation or removing specific cached data
186
+ * @llm-rule AVOID: Manual key management - this handles Redis delete operations
187
+ */
188
+ async delete(key) {
189
+ await this.ensureConnected();
190
+ try {
191
+ const result = await this.client.del(key);
192
+ return result === 1; // Redis returns number of keys deleted
193
+ }
194
+ catch (error) {
195
+ console.error(`[AppKit] Redis delete error for key "${key}":`, error.message);
196
+ return false;
197
+ }
198
+ }
199
+ /**
200
+ * Clears all keys matching pattern (usually namespace-based)
201
+ * @llm-rule WHEN: Namespace-based cache invalidation
202
+ * @llm-rule AVOID: Using FLUSHDB - this only clears specific namespace keys
203
+ */
204
+ async clear() {
205
+ // Note: This is handled by the main cache class using keys() + deleteMany()
206
+ // We don't implement it here to avoid accidental full cache clearing
207
+ throw new Error('Clear operation should be handled by cache class using keys() + deleteMany()');
208
+ }
209
+ /**
210
+ * Checks if key exists in Redis
211
+ * @llm-rule WHEN: Checking cache key existence without retrieving value
212
+ * @llm-rule AVOID: Using get() then checking null - this is more efficient
213
+ */
214
+ async has(key) {
215
+ await this.ensureConnected();
216
+ try {
217
+ const result = await this.client.exists(key);
218
+ return result === 1;
219
+ }
220
+ catch (error) {
221
+ console.error(`[AppKit] Redis has error for key "${key}":`, error.message);
222
+ return false;
223
+ }
224
+ }
225
+ /**
226
+ * Gets all keys matching pattern (for namespace operations)
227
+ * @llm-rule WHEN: Finding all keys in namespace for bulk operations
228
+ * @llm-rule AVOID: Using KEYS in production with large datasets - use SCAN instead
229
+ */
230
+ async keys(pattern = '*') {
231
+ await this.ensureConnected();
232
+ try {
233
+ // Use SCAN instead of KEYS for production safety
234
+ const keys = [];
235
+ let cursor = 0;
236
+ do {
237
+ const result = await this.client.scan(cursor, {
238
+ MATCH: pattern,
239
+ COUNT: 1000, // Scan in batches of 1000
240
+ });
241
+ cursor = result.cursor;
242
+ keys.push(...result.keys);
243
+ } while (cursor !== 0);
244
+ return keys;
245
+ }
246
+ catch (error) {
247
+ console.error(`[AppKit] Redis keys error for pattern "${pattern}":`, error.message);
248
+ return [];
249
+ }
250
+ }
251
+ /**
252
+ * Deletes multiple keys efficiently
253
+ * @llm-rule WHEN: Bulk deletion operations like namespace clearing
254
+ * @llm-rule AVOID: Individual delete calls - batch operations are much faster
255
+ */
256
+ async deleteMany(keys) {
257
+ if (keys.length === 0)
258
+ return 0;
259
+ await this.ensureConnected();
260
+ try {
261
+ // Redis DEL command accepts multiple keys
262
+ const result = await this.client.del(keys);
263
+ return result; // Returns number of keys deleted
264
+ }
265
+ catch (error) {
266
+ console.error(`[AppKit] Redis deleteMany error:`, error.message);
267
+ return 0;
268
+ }
269
+ }
270
+ // Private helper methods
271
+ /**
272
+ * Ensures Redis connection is established
273
+ */
274
+ async ensureConnected() {
275
+ if (!this.connected) {
276
+ await this.connect();
277
+ }
278
+ }
279
+ /**
280
+ * Serializes value to JSON string for Redis storage
281
+ */
282
+ serialize(value) {
283
+ try {
284
+ return JSON.stringify(value);
285
+ }
286
+ catch (error) {
287
+ throw new Error(`Failed to serialize value: ${error.message}`);
288
+ }
289
+ }
290
+ /**
291
+ * Deserializes JSON string from Redis
292
+ */
293
+ deserialize(value) {
294
+ try {
295
+ return JSON.parse(value);
296
+ }
297
+ catch (error) {
298
+ // If it's not valid JSON, return as string (backward compatibility)
299
+ return value;
300
+ }
301
+ }
302
+ /**
303
+ * Masks sensitive parts of Redis URL for logging
304
+ */
305
+ maskUrl(url) {
306
+ try {
307
+ const parsed = new URL(url);
308
+ if (parsed.password) {
309
+ parsed.password = '***';
310
+ }
311
+ return parsed.toString();
312
+ }
313
+ catch {
314
+ return 'redis://***';
315
+ }
316
+ }
317
+ }
318
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../../src/cache/strategies/redis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Smart configuration management with automatic environment variable parsing
3
+ * @module @bloomneo/appkit/config
4
+ * @file src/config/config.ts
5
+ *
6
+ * @llm-rule WHEN: Building apps that need configuration from environment variables
7
+ * @llm-rule AVOID: Manual env parsing or complex config files - this handles it automatically
8
+ * @llm-rule NOTE: Uses UPPER_SNAKE_CASE convention (DATABASE_HOST → config.get('database.host'))
9
+ */
10
+ export declare class ConfigClass {
11
+ private readonly _config;
12
+ /**
13
+ * Creates a new, immutable Config instance
14
+ * @llm-rule WHEN: App startup - need to parse environment variables into structured config
15
+ * @llm-rule AVOID: Calling directly - always use configClass.get() instead
16
+ * @llm-rule NOTE: Config is immutable and deeply frozen for safety
17
+ */
18
+ constructor(initialConfig?: Record<string, any>);
19
+ /**
20
+ * Gets a specific configuration value using dot notation
21
+ * @llm-rule WHEN: Accessing any config value from environment variables
22
+ * @llm-rule AVOID: Accessing process.env directly - use this for type safety and defaults
23
+ * @llm-rule NOTE: Returns typed values (strings, numbers, booleans) automatically
24
+ */
25
+ get<T = any>(path: string, defaultValue?: T): T | undefined;
26
+ /**
27
+ * Checks if a configuration path exists
28
+ * @llm-rule WHEN: Need to conditionally enable features based on config presence
29
+ * @llm-rule AVOID: Using get() !== undefined - this is more explicit and readable
30
+ */
31
+ has(path: string): boolean;
32
+ /**
33
+ * Returns the entire, immutable configuration object
34
+ * @llm-rule WHEN: Debugging config or passing entire config to other modules
35
+ * @llm-rule AVOID: Using in production loops - expensive operation, cache the result
36
+ */
37
+ getAll(): Record<string, any>;
38
+ /**
39
+ * Gets a required configuration value (throws if missing)
40
+ * @llm-rule WHEN: App startup validation for critical config values
41
+ * @llm-rule AVOID: Using in request handlers - expensive error creation
42
+ * @llm-rule NOTE: Use this for database URLs, API keys, and other critical settings
43
+ */
44
+ getRequired<T = any>(path: string): T;
45
+ /**
46
+ * Gets multiple configuration values at once
47
+ * @llm-rule WHEN: Need several related config values for a module
48
+ * @llm-rule AVOID: Multiple get() calls when you need many values - use this for performance
49
+ */
50
+ getMany<T extends Record<string, any>>(paths: {
51
+ [K in keyof T]: string;
52
+ }): T;
53
+ /**
54
+ * Deep freeze helper for immutability
55
+ */
56
+ private deepFreeze;
57
+ /**
58
+ * Converts dot notation path to environment variable name
59
+ */
60
+ private pathToEnvVar;
61
+ }
62
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,qBAAa,WAAW;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAsB;IAE9C;;;;;OAKG;gBACS,aAAa,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM;IAKnD;;;;;OAKG;IACH,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAmB3D;;;;OAIG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;;;OAIG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAI7B;;;;;OAKG;IACH,WAAW,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC;IAWrC;;;;OAIG;IACH,OAAO,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,KAAK,EAAE;SAAG,CAAC,IAAI,MAAM,CAAC,GAAG,MAAM;KAAE,GAAG,CAAC;IAW5E;;OAEG;IACH,OAAO,CAAC,UAAU;IASlB;;OAEG;IACH,OAAO,CAAC,YAAY;CAGrB"}
@@ -0,0 +1,107 @@
1
+ /**
2
+ * Smart configuration management with automatic environment variable parsing
3
+ * @module @bloomneo/appkit/config
4
+ * @file src/config/config.ts
5
+ *
6
+ * @llm-rule WHEN: Building apps that need configuration from environment variables
7
+ * @llm-rule AVOID: Manual env parsing or complex config files - this handles it automatically
8
+ * @llm-rule NOTE: Uses UPPER_SNAKE_CASE convention (DATABASE_HOST → config.get('database.host'))
9
+ */
10
+ export class ConfigClass {
11
+ _config;
12
+ /**
13
+ * Creates a new, immutable Config instance
14
+ * @llm-rule WHEN: App startup - need to parse environment variables into structured config
15
+ * @llm-rule AVOID: Calling directly - always use configClass.get() instead
16
+ * @llm-rule NOTE: Config is immutable and deeply frozen for safety
17
+ */
18
+ constructor(initialConfig = {}) {
19
+ // Make the config object deeply immutable for safety
20
+ this._config = Object.freeze(this.deepFreeze(structuredClone(initialConfig)));
21
+ }
22
+ /**
23
+ * Gets a specific configuration value using dot notation
24
+ * @llm-rule WHEN: Accessing any config value from environment variables
25
+ * @llm-rule AVOID: Accessing process.env directly - use this for type safety and defaults
26
+ * @llm-rule NOTE: Returns typed values (strings, numbers, booleans) automatically
27
+ */
28
+ get(path, defaultValue) {
29
+ if (typeof path !== 'string' || !path) {
30
+ return defaultValue;
31
+ }
32
+ const segments = path.split('.');
33
+ let current = this._config;
34
+ for (const segment of segments) {
35
+ if (current && typeof current === 'object' && segment in current) {
36
+ current = current[segment];
37
+ }
38
+ else {
39
+ return defaultValue;
40
+ }
41
+ }
42
+ return current;
43
+ }
44
+ /**
45
+ * Checks if a configuration path exists
46
+ * @llm-rule WHEN: Need to conditionally enable features based on config presence
47
+ * @llm-rule AVOID: Using get() !== undefined - this is more explicit and readable
48
+ */
49
+ has(path) {
50
+ return this.get(path) !== undefined;
51
+ }
52
+ /**
53
+ * Returns the entire, immutable configuration object
54
+ * @llm-rule WHEN: Debugging config or passing entire config to other modules
55
+ * @llm-rule AVOID: Using in production loops - expensive operation, cache the result
56
+ */
57
+ getAll() {
58
+ return this._config;
59
+ }
60
+ /**
61
+ * Gets a required configuration value (throws if missing)
62
+ * @llm-rule WHEN: App startup validation for critical config values
63
+ * @llm-rule AVOID: Using in request handlers - expensive error creation
64
+ * @llm-rule NOTE: Use this for database URLs, API keys, and other critical settings
65
+ */
66
+ getRequired(path) {
67
+ const value = this.get(path);
68
+ if (value === undefined) {
69
+ throw new Error(`Missing required configuration: "${path}". ` +
70
+ `Set environment variable: ${this.pathToEnvVar(path)}`);
71
+ }
72
+ return value;
73
+ }
74
+ /**
75
+ * Gets multiple configuration values at once
76
+ * @llm-rule WHEN: Need several related config values for a module
77
+ * @llm-rule AVOID: Multiple get() calls when you need many values - use this for performance
78
+ */
79
+ getMany(paths) {
80
+ const result = {};
81
+ for (const [key, path] of Object.entries(paths)) {
82
+ const value = this.get(path);
83
+ if (value !== undefined) {
84
+ result[key] = value;
85
+ }
86
+ }
87
+ return result;
88
+ }
89
+ /**
90
+ * Deep freeze helper for immutability
91
+ */
92
+ deepFreeze(obj) {
93
+ Object.getOwnPropertyNames(obj).forEach((prop) => {
94
+ if (obj[prop] !== null && (typeof obj[prop] === 'object' || typeof obj[prop] === 'function')) {
95
+ this.deepFreeze(obj[prop]);
96
+ }
97
+ });
98
+ return Object.freeze(obj);
99
+ }
100
+ /**
101
+ * Converts dot notation path to environment variable name
102
+ */
103
+ pathToEnvVar(path) {
104
+ return path.split('.').join('_').toUpperCase();
105
+ }
106
+ }
107
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,OAAO,WAAW;IACL,OAAO,CAAsB;IAE9C;;;;;OAKG;IACH,YAAY,gBAAqC,EAAE;QACjD,qDAAqD;QACrD,IAAI,CAAC,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAChF,CAAC;IAED;;;;;OAKG;IACH,GAAG,CAAU,IAAY,EAAE,YAAgB;QACzC,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YACtC,OAAO,YAAY,CAAC;QACtB,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,OAAO,GAAQ,IAAI,CAAC,OAAO,CAAC;QAEhC,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,IAAI,OAAO,EAAE,CAAC;gBACjE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,OAAO,YAAY,CAAC;YACtB,CAAC;QACH,CAAC;QAED,OAAO,OAAY,CAAC;IACtB,CAAC;IAED;;;;OAIG;IACH,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC;IACtC,CAAC;IAED;;;;OAIG;IACH,MAAM;QACJ,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED;;;;;OAKG;IACH,WAAW,CAAU,IAAY;QAC/B,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAI,IAAI,CAAC,CAAC;QAChC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,oCAAoC,IAAI,KAAK;gBAC7C,6BAA6B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CACvD,CAAC;QACJ,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;;;OAIG;IACH,OAAO,CAAgC,KAAiC;QACtE,MAAM,MAAM,GAAG,EAAO,CAAC;QACvB,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YAChD,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAC7B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,MAAM,CAAC,GAAc,CAAC,GAAG,KAAK,CAAC;YACjC,CAAC;QACH,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;OAEG;IACK,UAAU,CAAC,GAAQ;QACzB,MAAM,CAAC,mBAAmB,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE;YAC/C,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,UAAU,CAAC,EAAE,CAAC;gBAC7F,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC,CAAC;QACH,OAAO,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC5B,CAAC;IAED;;OAEG;IACK,YAAY,CAAC,IAAY;QAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,CAAC;CACF"}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Smart defaults and environment validation for configuration management
3
+ * @module @bloomneo/appkit/config
4
+ * @file src/config/defaults.ts
5
+ *
6
+ * @llm-rule WHEN: App startup - need to parse UPPER_SNAKE_CASE environment variables
7
+ * @llm-rule AVOID: Calling multiple times - expensive parsing, use lazy loading in get()
8
+ * @llm-rule NOTE: Called once at startup, cached globally for performance
9
+ */
10
+ export interface ConfigValue {
11
+ [key: string]: string | number | boolean | ConfigValue;
12
+ }
13
+ export interface AppConfig extends ConfigValue {
14
+ app: {
15
+ name: string;
16
+ environment: string;
17
+ port?: number;
18
+ host?: string;
19
+ };
20
+ [key: string]: any;
21
+ }
22
+ /**
23
+ * Builds the entire configuration object from process.env
24
+ * @llm-rule WHEN: App startup to get production-ready configuration from environment
25
+ * @llm-rule AVOID: Calling repeatedly - validates environment each time, expensive operation
26
+ * @llm-rule NOTE: Called once at startup, cached globally for performance
27
+ * @llm-rule CONVENTION: Only processes non-framework variables for user config
28
+ * @llm-rule CONVENTION: Variables with VOILA_* and FLUX_* are AppKit internal
29
+ */
30
+ export declare function buildConfigFromEnv(): AppConfig;
31
+ /**
32
+ * Validates critical configuration at startup
33
+ * @llm-rule WHEN: App startup to ensure required config is present
34
+ * @llm-rule AVOID: Skipping validation - missing config causes runtime errors
35
+ * @llm-rule NOTE: Add your app-specific required config here
36
+ */
37
+ export declare function validateConfig(config: AppConfig): void;
38
+ /**
39
+ * Gets smart defaults with validation
40
+ * @llm-rule WHEN: App startup to get production-ready configuration
41
+ * @llm-rule AVOID: Calling repeatedly - expensive validation, cache the result
42
+ */
43
+ export declare function getSmartDefaults(): AppConfig;
44
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../../src/config/defaults.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,WAAW,WAAW;IAC1B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,WAAW,CAAC;CACxD;AAED,MAAM,WAAW,SAAU,SAAQ,WAAW;IAC5C,GAAG,EAAE;QACH,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAwKD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,IAAI,SAAS,CA6B9C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAmBtD;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,IAAI,SAAS,CAI5C"}