@bloomneo/appkit 1.5.1 → 1.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +195 -0
- package/CHANGELOG.md +253 -0
- package/README.md +147 -799
- package/bin/commands/generate.js +7 -7
- package/cookbook/README.md +26 -0
- package/cookbook/api-key-service.ts +106 -0
- package/cookbook/auth-protected-crud.ts +112 -0
- package/cookbook/file-upload-pipeline.ts +113 -0
- package/cookbook/multi-tenant-saas.ts +87 -0
- package/cookbook/real-time-chat.ts +121 -0
- package/dist/auth/auth.d.ts +21 -4
- package/dist/auth/auth.d.ts.map +1 -1
- package/dist/auth/auth.js +56 -44
- package/dist/auth/auth.js.map +1 -1
- package/dist/auth/defaults.d.ts +1 -1
- package/dist/auth/defaults.js +35 -35
- package/dist/cache/cache.d.ts +29 -6
- package/dist/cache/cache.d.ts.map +1 -1
- package/dist/cache/cache.js +72 -44
- package/dist/cache/cache.js.map +1 -1
- package/dist/cache/defaults.js +25 -25
- package/dist/cache/index.d.ts +19 -10
- package/dist/cache/index.d.ts.map +1 -1
- package/dist/cache/index.js +21 -18
- package/dist/cache/index.js.map +1 -1
- package/dist/config/defaults.d.ts +1 -1
- package/dist/config/defaults.js +8 -8
- package/dist/config/index.d.ts +3 -3
- package/dist/config/index.js +4 -4
- package/dist/database/adapters/mongoose.js +2 -2
- package/dist/database/adapters/prisma.js +2 -2
- package/dist/database/defaults.d.ts +1 -1
- package/dist/database/defaults.js +4 -4
- package/dist/database/index.js +2 -2
- package/dist/database/index.js.map +1 -1
- package/dist/email/defaults.js +20 -20
- package/dist/error/defaults.d.ts +1 -1
- package/dist/error/defaults.js +12 -12
- package/dist/error/error.d.ts +12 -0
- package/dist/error/error.d.ts.map +1 -1
- package/dist/error/error.js +19 -0
- package/dist/error/error.js.map +1 -1
- package/dist/error/index.d.ts +14 -3
- package/dist/error/index.d.ts.map +1 -1
- package/dist/error/index.js +14 -3
- package/dist/error/index.js.map +1 -1
- package/dist/event/defaults.js +30 -30
- package/dist/logger/defaults.d.ts +1 -1
- package/dist/logger/defaults.js +40 -40
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/logger/logger.d.ts +8 -0
- package/dist/logger/logger.d.ts.map +1 -1
- package/dist/logger/logger.js +13 -3
- package/dist/logger/logger.js.map +1 -1
- package/dist/logger/transports/console.js +1 -1
- package/dist/logger/transports/http.d.ts +1 -1
- package/dist/logger/transports/http.js +1 -1
- package/dist/logger/transports/webhook.d.ts +1 -1
- package/dist/logger/transports/webhook.js +1 -1
- package/dist/queue/defaults.d.ts +2 -2
- package/dist/queue/defaults.js +38 -38
- package/dist/security/defaults.d.ts +1 -1
- package/dist/security/defaults.js +29 -29
- package/dist/security/index.d.ts +1 -1
- package/dist/security/index.js +3 -3
- package/dist/security/security.d.ts +1 -1
- package/dist/security/security.js +4 -4
- package/dist/storage/defaults.js +19 -19
- package/dist/util/defaults.d.ts +1 -1
- package/dist/util/defaults.js +34 -34
- package/dist/util/env.d.ts +35 -0
- package/dist/util/env.d.ts.map +1 -0
- package/dist/util/env.js +50 -0
- package/dist/util/env.js.map +1 -0
- package/dist/util/errors.d.ts +52 -0
- package/dist/util/errors.d.ts.map +1 -0
- package/dist/util/errors.js +82 -0
- package/dist/util/errors.js.map +1 -0
- package/examples/.env.example +80 -0
- package/examples/README.md +16 -0
- package/examples/auth.ts +228 -0
- package/examples/cache.ts +36 -0
- package/examples/config.ts +45 -0
- package/examples/database.ts +69 -0
- package/examples/email.ts +53 -0
- package/examples/error.ts +50 -0
- package/examples/event.ts +42 -0
- package/examples/logger.ts +41 -0
- package/examples/queue.ts +58 -0
- package/examples/security.ts +46 -0
- package/examples/storage.ts +44 -0
- package/examples/util.ts +47 -0
- package/llms.txt +591 -0
- package/package.json +19 -10
- package/src/auth/README.md +850 -0
- package/src/cache/README.md +756 -0
- package/src/config/README.md +604 -0
- package/src/database/README.md +818 -0
- package/src/email/README.md +759 -0
- package/src/error/README.md +660 -0
- package/src/event/README.md +729 -0
- package/src/logger/README.md +435 -0
- package/src/queue/README.md +851 -0
- package/src/security/README.md +612 -0
- package/src/storage/README.md +1008 -0
- package/src/util/README.md +955 -0
- package/bin/templates/backend/docs/APPKIT_CLI.md +0 -507
- package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +0 -61
- package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +0 -2539
package/dist/cache/cache.js
CHANGED
|
@@ -9,6 +9,36 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { RedisStrategy } from './strategies/redis.js';
|
|
11
11
|
import { MemoryStrategy } from './strategies/memory.js';
|
|
12
|
+
/**
|
|
13
|
+
* Thrown by all cache operations when the underlying strategy fails.
|
|
14
|
+
* Catch this in your route/service and decide whether to fall back to the
|
|
15
|
+
* database, re-throw, or log — the cache module never makes that call for you.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* import { CacheError } from '@bloomneo/appkit/cache';
|
|
19
|
+
*
|
|
20
|
+
* try {
|
|
21
|
+
* const user = await cache.get<User>('user:123');
|
|
22
|
+
* } catch (err) {
|
|
23
|
+
* if (err instanceof CacheError) {
|
|
24
|
+
* logger.warn('Cache unavailable, falling back to DB', { code: err.code });
|
|
25
|
+
* return await db.user.findUnique({ where: { id: 123 } });
|
|
26
|
+
* }
|
|
27
|
+
* throw err; // re-throw unrelated errors
|
|
28
|
+
* }
|
|
29
|
+
*/
|
|
30
|
+
export class CacheError extends Error {
|
|
31
|
+
/** Machine-readable error code, e.g. 'CACHE_GET_FAILED', 'CACHE_CONNECT_FAILED' */
|
|
32
|
+
code;
|
|
33
|
+
constructor(message, options) {
|
|
34
|
+
super(`[@bloomneo/appkit/cache] ${message}`);
|
|
35
|
+
this.name = 'CacheError';
|
|
36
|
+
this.code = options?.code ?? 'CACHE_ERROR';
|
|
37
|
+
if (options?.cause) {
|
|
38
|
+
this.cause = options.cause;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
12
42
|
/**
|
|
13
43
|
* Cache class with automatic strategy selection and ultra-simple API
|
|
14
44
|
*/
|
|
@@ -49,12 +79,11 @@ export class CacheClass {
|
|
|
49
79
|
await this.strategy.connect();
|
|
50
80
|
this.connected = true;
|
|
51
81
|
if (this.config.environment.isDevelopment) {
|
|
52
|
-
console.log(
|
|
82
|
+
console.log(`[@bloomneo/appkit/cache] Connected using ${this.config.strategy} strategy.`);
|
|
53
83
|
}
|
|
54
84
|
}
|
|
55
|
-
catch (
|
|
56
|
-
|
|
57
|
-
throw error;
|
|
85
|
+
catch (cause) {
|
|
86
|
+
throw new CacheError(`Failed to connect using ${this.config.strategy} strategy: ${cause.message}`, { code: 'CACHE_CONNECT_FAILED', cause });
|
|
58
87
|
}
|
|
59
88
|
}
|
|
60
89
|
/**
|
|
@@ -69,11 +98,13 @@ export class CacheClass {
|
|
|
69
98
|
await this.strategy.disconnect();
|
|
70
99
|
this.connected = false;
|
|
71
100
|
if (this.config.environment.isDevelopment) {
|
|
72
|
-
console.log(
|
|
101
|
+
console.log(`[@bloomneo/appkit/cache] Disconnected.`);
|
|
73
102
|
}
|
|
74
103
|
}
|
|
75
|
-
catch (
|
|
76
|
-
|
|
104
|
+
catch (cause) {
|
|
105
|
+
// Disconnect errors are non-fatal — log a warning but don't throw.
|
|
106
|
+
// Throwing during shutdown can mask the original reason the app is shutting down.
|
|
107
|
+
console.warn(`[@bloomneo/appkit/cache] Disconnect warning:`, cause.message);
|
|
77
108
|
}
|
|
78
109
|
}
|
|
79
110
|
/**
|
|
@@ -85,13 +116,15 @@ export class CacheClass {
|
|
|
85
116
|
async get(key) {
|
|
86
117
|
this.validateKey(key);
|
|
87
118
|
await this.ensureConnected();
|
|
119
|
+
const prefixedKey = this.buildKey(key);
|
|
88
120
|
try {
|
|
89
|
-
const prefixedKey = this.buildKey(key);
|
|
90
121
|
return await this.strategy.get(prefixedKey);
|
|
91
122
|
}
|
|
92
|
-
catch (
|
|
93
|
-
|
|
94
|
-
|
|
123
|
+
catch (cause) {
|
|
124
|
+
throw new CacheError(`get failed for key "${key}": ${cause.message}`, {
|
|
125
|
+
code: 'CACHE_GET_FAILED',
|
|
126
|
+
cause,
|
|
127
|
+
});
|
|
95
128
|
}
|
|
96
129
|
}
|
|
97
130
|
/**
|
|
@@ -104,14 +137,16 @@ export class CacheClass {
|
|
|
104
137
|
this.validateKey(key);
|
|
105
138
|
this.validateValue(value);
|
|
106
139
|
await this.ensureConnected();
|
|
140
|
+
const prefixedKey = this.buildKey(key);
|
|
141
|
+
const cacheTTL = ttl ?? this.config.defaultTTL;
|
|
107
142
|
try {
|
|
108
|
-
const prefixedKey = this.buildKey(key);
|
|
109
|
-
const cacheTTL = ttl ?? this.config.defaultTTL;
|
|
110
143
|
return await this.strategy.set(prefixedKey, value, cacheTTL);
|
|
111
144
|
}
|
|
112
|
-
catch (
|
|
113
|
-
|
|
114
|
-
|
|
145
|
+
catch (cause) {
|
|
146
|
+
throw new CacheError(`set failed for key "${key}": ${cause.message}`, {
|
|
147
|
+
code: 'CACHE_SET_FAILED',
|
|
148
|
+
cause,
|
|
149
|
+
});
|
|
115
150
|
}
|
|
116
151
|
}
|
|
117
152
|
/**
|
|
@@ -122,13 +157,15 @@ export class CacheClass {
|
|
|
122
157
|
async delete(key) {
|
|
123
158
|
this.validateKey(key);
|
|
124
159
|
await this.ensureConnected();
|
|
160
|
+
const prefixedKey = this.buildKey(key);
|
|
125
161
|
try {
|
|
126
|
-
const prefixedKey = this.buildKey(key);
|
|
127
162
|
return await this.strategy.delete(prefixedKey);
|
|
128
163
|
}
|
|
129
|
-
catch (
|
|
130
|
-
|
|
131
|
-
|
|
164
|
+
catch (cause) {
|
|
165
|
+
throw new CacheError(`delete failed for key "${key}": ${cause.message}`, {
|
|
166
|
+
code: 'CACHE_DELETE_FAILED',
|
|
167
|
+
cause,
|
|
168
|
+
});
|
|
132
169
|
}
|
|
133
170
|
}
|
|
134
171
|
/**
|
|
@@ -140,7 +177,6 @@ export class CacheClass {
|
|
|
140
177
|
async clear() {
|
|
141
178
|
await this.ensureConnected();
|
|
142
179
|
try {
|
|
143
|
-
// Get all keys in this namespace and delete them
|
|
144
180
|
const pattern = this.buildKey('*');
|
|
145
181
|
const keys = await this.strategy.keys(pattern);
|
|
146
182
|
if (keys.length === 0)
|
|
@@ -148,9 +184,11 @@ export class CacheClass {
|
|
|
148
184
|
const deleted = await this.strategy.deleteMany(keys);
|
|
149
185
|
return deleted === keys.length;
|
|
150
186
|
}
|
|
151
|
-
catch (
|
|
152
|
-
|
|
153
|
-
|
|
187
|
+
catch (cause) {
|
|
188
|
+
throw new CacheError(`clear failed for namespace "${this.namespace}": ${cause.message}`, {
|
|
189
|
+
code: 'CACHE_CLEAR_FAILED',
|
|
190
|
+
cause,
|
|
191
|
+
});
|
|
154
192
|
}
|
|
155
193
|
}
|
|
156
194
|
/**
|
|
@@ -166,15 +204,9 @@ export class CacheClass {
|
|
|
166
204
|
return existing;
|
|
167
205
|
}
|
|
168
206
|
// Generate new value and cache it
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
return value;
|
|
173
|
-
}
|
|
174
|
-
catch (error) {
|
|
175
|
-
console.error(`[AppKit] Cache getOrSet factory error for key "${key}":`, error.message);
|
|
176
|
-
throw error; // Re-throw factory errors
|
|
177
|
-
}
|
|
207
|
+
const value = await factory(); // factory errors propagate as-is — not wrapped in CacheError
|
|
208
|
+
await this.set(key, value, ttl);
|
|
209
|
+
return value;
|
|
178
210
|
}
|
|
179
211
|
/**
|
|
180
212
|
* Gets current cache strategy name for debugging
|
|
@@ -218,31 +250,27 @@ export class CacheClass {
|
|
|
218
250
|
*/
|
|
219
251
|
validateKey(key) {
|
|
220
252
|
if (!key || typeof key !== 'string') {
|
|
221
|
-
throw new
|
|
253
|
+
throw new CacheError('Cache key must be a non-empty string', { code: 'CACHE_INVALID_KEY' });
|
|
222
254
|
}
|
|
223
255
|
if (key.length > 250) {
|
|
224
|
-
throw new
|
|
256
|
+
throw new CacheError('Cache key too long (max 250 characters)', { code: 'CACHE_INVALID_KEY' });
|
|
225
257
|
}
|
|
226
258
|
if (key.includes('\n') || key.includes('\r')) {
|
|
227
|
-
throw new
|
|
259
|
+
throw new CacheError('Cache key cannot contain newline characters', { code: 'CACHE_INVALID_KEY' });
|
|
228
260
|
}
|
|
229
261
|
if (key.includes(':')) {
|
|
230
|
-
throw new
|
|
262
|
+
throw new CacheError('Cache key cannot contain colon characters (reserved for namespacing)', { code: 'CACHE_INVALID_KEY' });
|
|
231
263
|
}
|
|
232
264
|
}
|
|
233
|
-
/**
|
|
234
|
-
* Validates cache value
|
|
235
|
-
*/
|
|
236
265
|
validateValue(value) {
|
|
237
266
|
if (value === undefined) {
|
|
238
|
-
throw new
|
|
267
|
+
throw new CacheError('Cannot cache undefined values', { code: 'CACHE_INVALID_VALUE' });
|
|
239
268
|
}
|
|
240
|
-
// Check if value can be serialized
|
|
241
269
|
try {
|
|
242
270
|
JSON.stringify(value);
|
|
243
271
|
}
|
|
244
|
-
catch
|
|
245
|
-
throw new
|
|
272
|
+
catch {
|
|
273
|
+
throw new CacheError('Value must be JSON serializable', { code: 'CACHE_INVALID_VALUE' });
|
|
246
274
|
}
|
|
247
275
|
}
|
|
248
276
|
}
|
package/dist/cache/cache.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/cache/cache.ts"],"names":[],"mappings":"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"}
|
package/dist/cache/defaults.js
CHANGED
|
@@ -25,24 +25,24 @@ export function getSmartDefaults() {
|
|
|
25
25
|
// Strategy selection with smart detection
|
|
26
26
|
strategy,
|
|
27
27
|
// Key management with service identification
|
|
28
|
-
keyPrefix: process.env.
|
|
29
|
-
namespace: process.env.
|
|
28
|
+
keyPrefix: process.env.BLOOM_CACHE_PREFIX || process.env.BLOOM_SERVICE_NAME || 'app',
|
|
29
|
+
namespace: process.env.BLOOM_CACHE_NAMESPACE || 'default',
|
|
30
30
|
// TTL configuration with environment awareness
|
|
31
|
-
defaultTTL: parseInt(process.env.
|
|
31
|
+
defaultTTL: parseInt(process.env.BLOOM_CACHE_TTL || (isProduction ? '3600' : '300')), // 1hr prod, 5min dev
|
|
32
32
|
// Redis configuration (only used when strategy is 'redis')
|
|
33
33
|
redis: {
|
|
34
34
|
url: process.env.REDIS_URL || 'redis://localhost:6379',
|
|
35
35
|
password: process.env.REDIS_PASSWORD,
|
|
36
|
-
maxRetries: parseInt(process.env.
|
|
37
|
-
retryDelay: parseInt(process.env.
|
|
38
|
-
connectTimeout: parseInt(process.env.
|
|
39
|
-
commandTimeout: parseInt(process.env.
|
|
36
|
+
maxRetries: parseInt(process.env.BLOOM_CACHE_REDIS_RETRIES || '3'),
|
|
37
|
+
retryDelay: parseInt(process.env.BLOOM_CACHE_REDIS_RETRY_DELAY || '1000'),
|
|
38
|
+
connectTimeout: parseInt(process.env.BLOOM_CACHE_REDIS_CONNECT_TIMEOUT || '10000'),
|
|
39
|
+
commandTimeout: parseInt(process.env.BLOOM_CACHE_REDIS_COMMAND_TIMEOUT || '5000'),
|
|
40
40
|
},
|
|
41
41
|
// Memory configuration (only used when strategy is 'memory')
|
|
42
42
|
memory: {
|
|
43
|
-
maxItems: parseInt(process.env.
|
|
44
|
-
maxSizeBytes: parseInt(process.env.
|
|
45
|
-
checkInterval: parseInt(process.env.
|
|
43
|
+
maxItems: parseInt(process.env.BLOOM_CACHE_MEMORY_MAX_ITEMS || '10000'),
|
|
44
|
+
maxSizeBytes: parseInt(process.env.BLOOM_CACHE_MEMORY_MAX_SIZE || '100000000'), // 100MB
|
|
45
|
+
checkInterval: parseInt(process.env.BLOOM_CACHE_MEMORY_CHECK_INTERVAL || '60000'), // 1 minute
|
|
46
46
|
},
|
|
47
47
|
// Environment information
|
|
48
48
|
environment: {
|
|
@@ -61,7 +61,7 @@ export function getSmartDefaults() {
|
|
|
61
61
|
*/
|
|
62
62
|
function detectCacheStrategy() {
|
|
63
63
|
// Explicit override wins (for testing/debugging)
|
|
64
|
-
const explicit = process.env.
|
|
64
|
+
const explicit = process.env.BLOOM_CACHE_STRATEGY?.toLowerCase();
|
|
65
65
|
if (explicit === 'redis' || explicit === 'memory') {
|
|
66
66
|
return explicit;
|
|
67
67
|
}
|
|
@@ -89,28 +89,28 @@ function validateEnvironment() {
|
|
|
89
89
|
throw new Error(`Invalid REDIS_URL: "${redisUrl}". Must start with redis:// or rediss://`);
|
|
90
90
|
}
|
|
91
91
|
// Validate cache strategy if explicitly set
|
|
92
|
-
const strategy = process.env.
|
|
92
|
+
const strategy = process.env.BLOOM_CACHE_STRATEGY;
|
|
93
93
|
if (strategy && !['redis', 'memory'].includes(strategy.toLowerCase())) {
|
|
94
|
-
throw new Error(`Invalid
|
|
94
|
+
throw new Error(`Invalid BLOOM_CACHE_STRATEGY: "${strategy}". Must be "redis" or "memory"`);
|
|
95
95
|
}
|
|
96
96
|
// Validate numeric values
|
|
97
|
-
validateNumericEnv('
|
|
98
|
-
validateNumericEnv('
|
|
99
|
-
validateNumericEnv('
|
|
100
|
-
validateNumericEnv('
|
|
101
|
-
validateNumericEnv('
|
|
102
|
-
validateNumericEnv('
|
|
103
|
-
validateNumericEnv('
|
|
104
|
-
validateNumericEnv('
|
|
97
|
+
validateNumericEnv('BLOOM_CACHE_TTL', 1, 86400 * 7); // 1 second to 1 week
|
|
98
|
+
validateNumericEnv('BLOOM_CACHE_REDIS_RETRIES', 0, 10);
|
|
99
|
+
validateNumericEnv('BLOOM_CACHE_REDIS_RETRY_DELAY', 100, 10000);
|
|
100
|
+
validateNumericEnv('BLOOM_CACHE_REDIS_CONNECT_TIMEOUT', 1000, 60000);
|
|
101
|
+
validateNumericEnv('BLOOM_CACHE_REDIS_COMMAND_TIMEOUT', 1000, 30000);
|
|
102
|
+
validateNumericEnv('BLOOM_CACHE_MEMORY_MAX_ITEMS', 100, 1000000);
|
|
103
|
+
validateNumericEnv('BLOOM_CACHE_MEMORY_MAX_SIZE', 1000000, 1000000000); // 1MB to 1GB
|
|
104
|
+
validateNumericEnv('BLOOM_CACHE_MEMORY_CHECK_INTERVAL', 10000, 300000); // 10s to 5min
|
|
105
105
|
// Validate key prefix
|
|
106
|
-
const keyPrefix = process.env.
|
|
106
|
+
const keyPrefix = process.env.BLOOM_CACHE_PREFIX;
|
|
107
107
|
if (keyPrefix && !/^[a-zA-Z0-9_-]+$/.test(keyPrefix)) {
|
|
108
|
-
throw new Error(`Invalid
|
|
108
|
+
throw new Error(`Invalid BLOOM_CACHE_PREFIX: "${keyPrefix}". Must contain only letters, numbers, underscores, and hyphens`);
|
|
109
109
|
}
|
|
110
110
|
// Validate namespace
|
|
111
|
-
const namespace = process.env.
|
|
111
|
+
const namespace = process.env.BLOOM_CACHE_NAMESPACE;
|
|
112
112
|
if (namespace && !/^[a-zA-Z0-9_-]+$/.test(namespace)) {
|
|
113
|
-
throw new Error(`Invalid
|
|
113
|
+
throw new Error(`Invalid BLOOM_CACHE_NAMESPACE: "${namespace}". Must contain only letters, numbers, underscores, and hyphens`);
|
|
114
114
|
}
|
|
115
115
|
// Production-specific validations
|
|
116
116
|
const nodeEnv = process.env.NODE_ENV;
|
package/dist/cache/index.d.ts
CHANGED
|
@@ -10,13 +10,19 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { type CacheConfig } from './defaults.js';
|
|
12
12
|
export interface Cache {
|
|
13
|
-
get(key: string): Promise<
|
|
14
|
-
set(key: string, value:
|
|
13
|
+
get<T = unknown>(key: string): Promise<T | null>;
|
|
14
|
+
set<T = unknown>(key: string, value: T, ttl?: number): Promise<boolean>;
|
|
15
15
|
delete(key: string): Promise<boolean>;
|
|
16
16
|
clear(): Promise<boolean>;
|
|
17
|
-
getOrSet(key: string, factory: () => Promise<
|
|
17
|
+
getOrSet<T = unknown>(key: string, factory: () => Promise<T>, ttl?: number): Promise<T>;
|
|
18
18
|
getStrategy(): string;
|
|
19
|
-
getConfig():
|
|
19
|
+
getConfig(): {
|
|
20
|
+
strategy: string;
|
|
21
|
+
keyPrefix: string;
|
|
22
|
+
namespace: string;
|
|
23
|
+
defaultTTL: number;
|
|
24
|
+
connected: boolean;
|
|
25
|
+
};
|
|
20
26
|
}
|
|
21
27
|
/**
|
|
22
28
|
* Get cache instance for specific namespace - the only function you need to learn
|
|
@@ -27,11 +33,14 @@ export interface Cache {
|
|
|
27
33
|
*/
|
|
28
34
|
declare function get(namespace?: string): Cache;
|
|
29
35
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
36
|
+
* Disconnect all cache instances and reset internal state.
|
|
37
|
+
* Use this for full teardown (e.g., end-of-suite cleanup).
|
|
38
|
+
* For clearing cached data between individual tests use flushAll().
|
|
39
|
+
*
|
|
40
|
+
* @llm-rule WHEN: End-of-test-suite teardown or app restart
|
|
41
|
+
* @llm-rule AVOID: Calling between individual tests — use flushAll() instead
|
|
33
42
|
*/
|
|
34
|
-
declare function
|
|
43
|
+
declare function disconnectAll(): Promise<void>;
|
|
35
44
|
/**
|
|
36
45
|
* Reset cache configuration (useful for testing)
|
|
37
46
|
* @llm-rule WHEN: Testing cache logic with different environment configurations
|
|
@@ -86,7 +95,7 @@ declare function shutdown(): Promise<void>;
|
|
|
86
95
|
*/
|
|
87
96
|
export declare const cacheClass: {
|
|
88
97
|
readonly get: typeof get;
|
|
89
|
-
readonly
|
|
98
|
+
readonly disconnectAll: typeof disconnectAll;
|
|
90
99
|
readonly reset: typeof reset;
|
|
91
100
|
readonly getStrategy: typeof getStrategy;
|
|
92
101
|
readonly getActiveNamespaces: typeof getActiveNamespaces;
|
|
@@ -96,6 +105,6 @@ export declare const cacheClass: {
|
|
|
96
105
|
readonly shutdown: typeof shutdown;
|
|
97
106
|
};
|
|
98
107
|
export type { CacheConfig } from './defaults.js';
|
|
99
|
-
export { CacheClass } from './cache.js';
|
|
108
|
+
export { CacheClass, CacheError } from './cache.js';
|
|
100
109
|
export default cacheClass;
|
|
101
110
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAMnE,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AAMnE,MAAM,WAAW,KAAK;IACpB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IACjD,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACxE,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACtC,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1B,QAAQ,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACxF,WAAW,IAAI,MAAM,CAAC;IACtB,SAAS,IAAI;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,CAAC;CACjH;AAED;;;;;;GAMG;AACH,iBAAS,GAAG,CAAC,SAAS,GAAE,MAAc,GAAG,KAAK,CA8B7C;AAED;;;;;;;GAOG;AACH,iBAAe,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAc5C;AAED;;;;GAIG;AACH,iBAAe,KAAK,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAWpE;AAED;;;;GAIG;AACH,iBAAS,WAAW,IAAI,MAAM,CAK7B;AAED;;;;GAIG;AACH,iBAAS,mBAAmB,IAAI,MAAM,EAAE,CAEvC;AAED;;;;GAIG;AACH,iBAAS,SAAS,IAAI;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;CACrB,CAYA;AAED;;;;GAIG;AACH,iBAAS,QAAQ,IAAI,OAAO,CAE3B;AAED;;;;;GAKG;AACH,iBAAe,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,CAc1C;AAED;;;;GAIG;AACH,iBAAe,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CASvC;AAED;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;CAab,CAAC;AAGX,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAGpD,eAAe,UAAU,CAAC"}
|
package/dist/cache/index.js
CHANGED
|
@@ -40,21 +40,24 @@ function get(namespace = 'app') {
|
|
|
40
40
|
const cacheInstance = new CacheClass(globalConfig, namespace);
|
|
41
41
|
// Auto-connect on first use
|
|
42
42
|
cacheInstance.connect().catch((error) => {
|
|
43
|
-
console.error(`[
|
|
43
|
+
console.error(`[@bloomneo/appkit/cache] Auto-connect failed for namespace "${namespace}":`, error.message);
|
|
44
44
|
});
|
|
45
45
|
namedCaches.set(namespace, cacheInstance);
|
|
46
46
|
return cacheInstance;
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
49
|
+
* Disconnect all cache instances and reset internal state.
|
|
50
|
+
* Use this for full teardown (e.g., end-of-suite cleanup).
|
|
51
|
+
* For clearing cached data between individual tests use flushAll().
|
|
52
|
+
*
|
|
53
|
+
* @llm-rule WHEN: End-of-test-suite teardown or app restart
|
|
54
|
+
* @llm-rule AVOID: Calling between individual tests — use flushAll() instead
|
|
52
55
|
*/
|
|
53
|
-
async function
|
|
56
|
+
async function disconnectAll() {
|
|
54
57
|
const disconnectPromises = [];
|
|
55
58
|
for (const [namespace, cache] of namedCaches) {
|
|
56
59
|
disconnectPromises.push(cache.disconnect().catch((error) => {
|
|
57
|
-
console.error(`[
|
|
60
|
+
console.error(`[@bloomneo/appkit/cache] Disconnect failed for namespace "${namespace}":`, error.message);
|
|
58
61
|
}));
|
|
59
62
|
}
|
|
60
63
|
await Promise.all(disconnectPromises);
|
|
@@ -67,8 +70,8 @@ async function clear() {
|
|
|
67
70
|
* @llm-rule AVOID: Using in production - only for tests and development
|
|
68
71
|
*/
|
|
69
72
|
async function reset(newConfig) {
|
|
70
|
-
//
|
|
71
|
-
await
|
|
73
|
+
// Disconnect existing instances before reconfiguring
|
|
74
|
+
await disconnectAll();
|
|
72
75
|
// Reset configuration
|
|
73
76
|
if (newConfig) {
|
|
74
77
|
const defaults = getSmartDefaults();
|
|
@@ -138,7 +141,7 @@ async function flushAll() {
|
|
|
138
141
|
return results.every(result => result === true);
|
|
139
142
|
}
|
|
140
143
|
catch (error) {
|
|
141
|
-
console.error('[
|
|
144
|
+
console.error('[@bloomneo/appkit/cache] flushAll error:', error.message);
|
|
142
145
|
return false;
|
|
143
146
|
}
|
|
144
147
|
}
|
|
@@ -148,13 +151,13 @@ async function flushAll() {
|
|
|
148
151
|
* @llm-rule AVOID: Abrupt process exit - graceful shutdown prevents data loss
|
|
149
152
|
*/
|
|
150
153
|
async function shutdown() {
|
|
151
|
-
console.log('
|
|
154
|
+
console.log('[@bloomneo/appkit/cache] Graceful shutdown starting...');
|
|
152
155
|
try {
|
|
153
|
-
await
|
|
154
|
-
console.log('
|
|
156
|
+
await disconnectAll();
|
|
157
|
+
console.log('[@bloomneo/appkit/cache] Shutdown complete.');
|
|
155
158
|
}
|
|
156
159
|
catch (error) {
|
|
157
|
-
console.error('
|
|
160
|
+
console.error('[@bloomneo/appkit/cache] Shutdown error:', error.message);
|
|
158
161
|
}
|
|
159
162
|
}
|
|
160
163
|
/**
|
|
@@ -164,16 +167,16 @@ export const cacheClass = {
|
|
|
164
167
|
// Core method (like auth.get())
|
|
165
168
|
get,
|
|
166
169
|
// Utility methods
|
|
167
|
-
clear
|
|
170
|
+
disconnectAll, // Disconnects all instances + resets state. Use flushAll() to clear data between tests.
|
|
168
171
|
reset,
|
|
169
172
|
getStrategy,
|
|
170
173
|
getActiveNamespaces,
|
|
171
174
|
getConfig,
|
|
172
175
|
hasRedis,
|
|
173
|
-
flushAll,
|
|
176
|
+
flushAll, // Clears cached data across all namespaces. Use between individual tests.
|
|
174
177
|
shutdown,
|
|
175
178
|
};
|
|
176
|
-
export { CacheClass } from './cache.js';
|
|
179
|
+
export { CacheClass, CacheError } from './cache.js';
|
|
177
180
|
// Default export
|
|
178
181
|
export default cacheClass;
|
|
179
182
|
// Auto-setup graceful shutdown handlers
|
|
@@ -188,13 +191,13 @@ if (typeof process !== 'undefined') {
|
|
|
188
191
|
process.on('SIGINT', shutdownHandler);
|
|
189
192
|
// Handle uncaught errors
|
|
190
193
|
process.on('uncaughtException', (error) => {
|
|
191
|
-
console.error('[
|
|
194
|
+
console.error('[@bloomneo/appkit/cache] Uncaught exception:', error);
|
|
192
195
|
shutdown().finally(() => {
|
|
193
196
|
process.exit(1);
|
|
194
197
|
});
|
|
195
198
|
});
|
|
196
199
|
process.on('unhandledRejection', (reason) => {
|
|
197
|
-
console.error('[
|
|
200
|
+
console.error('[@bloomneo/appkit/cache] Unhandled rejection:', reason);
|
|
198
201
|
shutdown().finally(() => {
|
|
199
202
|
process.exit(1);
|
|
200
203
|
});
|
package/dist/cache/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAoB,MAAM,eAAe,CAAC;AAEnE,4DAA4D;AAC5D,IAAI,YAAY,GAAuB,IAAI,CAAC;AAC5C,MAAM,WAAW,GAAG,IAAI,GAAG,EAAsB,CAAC;AAYlD;;;;;;GAMG;AACH,SAAS,GAAG,CAAC,YAAoB,KAAK;IACpC,qBAAqB;IACrB,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,KAAK,CAAC,8EAA8E,CAAC,CAAC;IAClG,CAAC;IAED,2DAA2D;IAC3D,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,gBAAgB,EAAE,CAAC;IACpC,CAAC;IAED,mCAAmC;IACnC,IAAI,WAAW,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/B,OAAO,WAAW,CAAC,GAAG,CAAC,SAAS,CAAE,CAAC;IACrC,CAAC;IAED,0CAA0C;IAC1C,MAAM,aAAa,GAAG,IAAI,UAAU,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IAE9D,4BAA4B;IAC5B,aAAa,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACtC,OAAO,CAAC,KAAK,CAAC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -25,7 +25,7 @@ export interface AppConfig extends ConfigValue {
|
|
|
25
25
|
* @llm-rule AVOID: Calling repeatedly - validates environment each time, expensive operation
|
|
26
26
|
* @llm-rule NOTE: Called once at startup, cached globally for performance
|
|
27
27
|
* @llm-rule CONVENTION: Only processes non-framework variables for user config
|
|
28
|
-
* @llm-rule CONVENTION: Variables with
|
|
28
|
+
* @llm-rule CONVENTION: Variables with BLOOM_* and FLUX_* are AppKit internal
|
|
29
29
|
*/
|
|
30
30
|
export declare function buildConfigFromEnv(): AppConfig;
|
|
31
31
|
/**
|
package/dist/config/defaults.js
CHANGED
|
@@ -70,7 +70,7 @@ function validateEnvironment() {
|
|
|
70
70
|
}
|
|
71
71
|
// Validate common required variables in production
|
|
72
72
|
if (nodeEnv === 'production') {
|
|
73
|
-
const requiredProdVars = ['
|
|
73
|
+
const requiredProdVars = ['BLOOM_SERVICE_NAME'];
|
|
74
74
|
const missing = requiredProdVars.filter(varName => !process.env[varName]);
|
|
75
75
|
if (missing.length > 0) {
|
|
76
76
|
console.warn(`[Bloomneo AppKit] Missing recommended production environment variables: ${missing.join(', ')}`);
|
|
@@ -81,11 +81,11 @@ function validateEnvironment() {
|
|
|
81
81
|
* Check if environment variable is a framework variable that should be ignored
|
|
82
82
|
* @llm-rule WHEN: Filtering out framework variables from app config parsing
|
|
83
83
|
* @llm-rule AVOID: Parsing framework variables as app config - they serve different purposes
|
|
84
|
-
* @llm-rule NOTE: Bloomneo AppKit uses
|
|
84
|
+
* @llm-rule NOTE: Bloomneo AppKit uses BLOOM_* and FLUX_* for internal configuration
|
|
85
85
|
*/
|
|
86
86
|
function isFrameworkVariable(envKey) {
|
|
87
87
|
const frameworkPrefixes = [
|
|
88
|
-
'
|
|
88
|
+
'BLOOM_', // Bloomneo AppKit framework configuration
|
|
89
89
|
'FLUX_', // Flux Framework internal variables
|
|
90
90
|
'NODE_', // Node.js environment variables
|
|
91
91
|
'npm_', // npm variables
|
|
@@ -156,20 +156,20 @@ function validateEnvVarFormat(envKey) {
|
|
|
156
156
|
* @llm-rule AVOID: Calling repeatedly - validates environment each time, expensive operation
|
|
157
157
|
* @llm-rule NOTE: Called once at startup, cached globally for performance
|
|
158
158
|
* @llm-rule CONVENTION: Only processes non-framework variables for user config
|
|
159
|
-
* @llm-rule CONVENTION: Variables with
|
|
159
|
+
* @llm-rule CONVENTION: Variables with BLOOM_* and FLUX_* are AppKit internal
|
|
160
160
|
*/
|
|
161
161
|
export function buildConfigFromEnv() {
|
|
162
162
|
validateEnvironment();
|
|
163
163
|
const config = {
|
|
164
164
|
app: {
|
|
165
|
-
name: process.env.
|
|
165
|
+
name: process.env.BLOOM_SERVICE_NAME || process.env.npm_package_name || 'voila-app',
|
|
166
166
|
environment: process.env.NODE_ENV || 'development',
|
|
167
167
|
port: process.env.PORT ? parseInt(process.env.PORT, 10) : undefined,
|
|
168
168
|
host: process.env.HOST || undefined,
|
|
169
169
|
},
|
|
170
170
|
};
|
|
171
171
|
// Process ONLY user configuration variables using UPPER_SNAKE_CASE convention
|
|
172
|
-
// IMPORTANT: Framework variables (
|
|
172
|
+
// IMPORTANT: Framework variables (BLOOM_*, FLUX_*) are NOT processed as user config
|
|
173
173
|
for (const envKey in process.env) {
|
|
174
174
|
// Skip framework variables and system variables
|
|
175
175
|
if (isFrameworkVariable(envKey) || isSystemVariable(envKey)) {
|
|
@@ -195,8 +195,8 @@ export function validateConfig(config) {
|
|
|
195
195
|
// Production-specific validations
|
|
196
196
|
if (environment === 'production') {
|
|
197
197
|
if (!config.app.name || config.app.name === 'voila-app') {
|
|
198
|
-
throw new Error('
|
|
199
|
-
'Set environment variable:
|
|
198
|
+
throw new Error('BLOOM_SERVICE_NAME is required in production. ' +
|
|
199
|
+
'Set environment variable: BLOOM_SERVICE_NAME=your-app-name');
|
|
200
200
|
}
|
|
201
201
|
}
|
|
202
202
|
// Port validation
|
package/dist/config/index.d.ts
CHANGED
|
@@ -9,14 +9,14 @@
|
|
|
9
9
|
* @llm-rule NOTE: Common pattern - configClass.get() → config.get('path', default) → use value
|
|
10
10
|
*
|
|
11
11
|
* CRITICAL UNDERSCORE CONVENTION:
|
|
12
|
-
* -
|
|
12
|
+
* - BLOOM_* and FLUX_* = Framework internal variables (NOT parsed as app config)
|
|
13
13
|
* - Everything else = Your app config (parsed into config object)
|
|
14
14
|
*
|
|
15
15
|
* Examples:
|
|
16
|
-
* ✅
|
|
16
|
+
* ✅ BLOOM_AUTH_SECRET=secret → Framework internal (not in config object)
|
|
17
17
|
* ✅ DATABASE_HOST=localhost → config.get('database.host')
|
|
18
18
|
* ✅ REDIS_URL=redis://local → config.get('redis.url')
|
|
19
|
-
* ❌
|
|
19
|
+
* ❌ BLOOM_DATABASE_HOST=localhost → Framework var (won't be parsed as app config)
|
|
20
20
|
*/
|
|
21
21
|
import { ConfigClass } from './config.js';
|
|
22
22
|
import { type ConfigValue } from './defaults.js';
|