@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.
- package/LICENSE +21 -0
- package/README.md +902 -0
- package/bin/appkit.js +71 -0
- package/bin/commands/generate.js +1050 -0
- package/bin/templates/backend/README.md.template +39 -0
- package/bin/templates/backend/api.http.template +0 -0
- package/bin/templates/backend/docs/APPKIT_CLI.md +507 -0
- package/bin/templates/backend/docs/APPKIT_COMMENTS_GUIDELINES.md +61 -0
- package/bin/templates/backend/docs/APPKIT_LLM_GUIDE.md +2539 -0
- package/bin/templates/backend/package.json.template +34 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.http.template +29 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.route.ts.template +36 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.service.ts.template +88 -0
- package/bin/templates/backend/src/api/features/welcome/welcome.types.ts.template +18 -0
- package/bin/templates/backend/src/api/lib/api-router.ts.template +84 -0
- package/bin/templates/backend/src/api/server.ts.template +188 -0
- package/bin/templates/backend/tsconfig.api.json.template +24 -0
- package/bin/templates/backend/tsconfig.json.template +40 -0
- package/bin/templates/feature/feature.http.template +63 -0
- package/bin/templates/feature/feature.route.ts.template +36 -0
- package/bin/templates/feature/feature.service.ts.template +81 -0
- package/bin/templates/feature/feature.types.ts.template +23 -0
- package/bin/templates/feature-db/feature.http.template +63 -0
- package/bin/templates/feature-db/feature.model.ts.template +74 -0
- package/bin/templates/feature-db/feature.route.ts.template +58 -0
- package/bin/templates/feature-db/feature.service.ts.template +231 -0
- package/bin/templates/feature-db/feature.types.ts.template +25 -0
- package/bin/templates/feature-db/schema-addition.prisma.template +9 -0
- package/bin/templates/feature-db/seeding/README.md.template +57 -0
- package/bin/templates/feature-db/seeding/feature.seed.js.template +67 -0
- package/bin/templates/feature-user/schema-addition.prisma.template +19 -0
- package/bin/templates/feature-user/user.http.template +157 -0
- package/bin/templates/feature-user/user.model.ts.template +244 -0
- package/bin/templates/feature-user/user.route.ts.template +379 -0
- package/bin/templates/feature-user/user.seed.js.template +182 -0
- package/bin/templates/feature-user/user.service.ts.template +426 -0
- package/bin/templates/feature-user/user.types.ts.template +127 -0
- package/dist/auth/auth.d.ts +182 -0
- package/dist/auth/auth.d.ts.map +1 -0
- package/dist/auth/auth.js +477 -0
- package/dist/auth/auth.js.map +1 -0
- package/dist/auth/defaults.d.ts +104 -0
- package/dist/auth/defaults.d.ts.map +1 -0
- package/dist/auth/defaults.js +374 -0
- package/dist/auth/defaults.js.map +1 -0
- package/dist/auth/index.d.ts +70 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +94 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/cache/cache.d.ts +118 -0
- package/dist/cache/cache.d.ts.map +1 -0
- package/dist/cache/cache.js +249 -0
- package/dist/cache/cache.js.map +1 -0
- package/dist/cache/defaults.d.ts +63 -0
- package/dist/cache/defaults.d.ts.map +1 -0
- package/dist/cache/defaults.js +193 -0
- package/dist/cache/defaults.js.map +1 -0
- package/dist/cache/index.d.ts +101 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +203 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/cache/strategies/memory.d.ts +138 -0
- package/dist/cache/strategies/memory.d.ts.map +1 -0
- package/dist/cache/strategies/memory.js +348 -0
- package/dist/cache/strategies/memory.js.map +1 -0
- package/dist/cache/strategies/redis.d.ts +105 -0
- package/dist/cache/strategies/redis.d.ts.map +1 -0
- package/dist/cache/strategies/redis.js +318 -0
- package/dist/cache/strategies/redis.js.map +1 -0
- package/dist/config/config.d.ts +62 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +107 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/defaults.d.ts +44 -0
- package/dist/config/defaults.d.ts.map +1 -0
- package/dist/config/defaults.js +217 -0
- package/dist/config/defaults.js.map +1 -0
- package/dist/config/index.d.ts +105 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +163 -0
- package/dist/config/index.js.map +1 -0
- package/dist/database/adapters/mongoose.d.ts +106 -0
- package/dist/database/adapters/mongoose.d.ts.map +1 -0
- package/dist/database/adapters/mongoose.js +480 -0
- package/dist/database/adapters/mongoose.js.map +1 -0
- package/dist/database/adapters/prisma.d.ts +106 -0
- package/dist/database/adapters/prisma.d.ts.map +1 -0
- package/dist/database/adapters/prisma.js +494 -0
- package/dist/database/adapters/prisma.js.map +1 -0
- package/dist/database/defaults.d.ts +87 -0
- package/dist/database/defaults.d.ts.map +1 -0
- package/dist/database/defaults.js +271 -0
- package/dist/database/defaults.js.map +1 -0
- package/dist/database/index.d.ts +137 -0
- package/dist/database/index.d.ts.map +1 -0
- package/dist/database/index.js +490 -0
- package/dist/database/index.js.map +1 -0
- package/dist/email/defaults.d.ts +100 -0
- package/dist/email/defaults.d.ts.map +1 -0
- package/dist/email/defaults.js +400 -0
- package/dist/email/defaults.js.map +1 -0
- package/dist/email/email.d.ts +139 -0
- package/dist/email/email.d.ts.map +1 -0
- package/dist/email/email.js +316 -0
- package/dist/email/email.js.map +1 -0
- package/dist/email/index.d.ts +176 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/email/index.js +251 -0
- package/dist/email/index.js.map +1 -0
- package/dist/email/strategies/console.d.ts +90 -0
- package/dist/email/strategies/console.d.ts.map +1 -0
- package/dist/email/strategies/console.js +268 -0
- package/dist/email/strategies/console.js.map +1 -0
- package/dist/email/strategies/resend.d.ts +84 -0
- package/dist/email/strategies/resend.d.ts.map +1 -0
- package/dist/email/strategies/resend.js +266 -0
- package/dist/email/strategies/resend.js.map +1 -0
- package/dist/email/strategies/smtp.d.ts +77 -0
- package/dist/email/strategies/smtp.d.ts.map +1 -0
- package/dist/email/strategies/smtp.js +286 -0
- package/dist/email/strategies/smtp.js.map +1 -0
- package/dist/error/defaults.d.ts +40 -0
- package/dist/error/defaults.d.ts.map +1 -0
- package/dist/error/defaults.js +75 -0
- package/dist/error/defaults.js.map +1 -0
- package/dist/error/error.d.ts +140 -0
- package/dist/error/error.d.ts.map +1 -0
- package/dist/error/error.js +200 -0
- package/dist/error/error.js.map +1 -0
- package/dist/error/index.d.ts +145 -0
- package/dist/error/index.d.ts.map +1 -0
- package/dist/error/index.js +145 -0
- package/dist/error/index.js.map +1 -0
- package/dist/event/defaults.d.ts +111 -0
- package/dist/event/defaults.d.ts.map +1 -0
- package/dist/event/defaults.js +378 -0
- package/dist/event/defaults.js.map +1 -0
- package/dist/event/event.d.ts +171 -0
- package/dist/event/event.d.ts.map +1 -0
- package/dist/event/event.js +391 -0
- package/dist/event/event.js.map +1 -0
- package/dist/event/index.d.ts +173 -0
- package/dist/event/index.d.ts.map +1 -0
- package/dist/event/index.js +302 -0
- package/dist/event/index.js.map +1 -0
- package/dist/event/strategies/memory.d.ts +122 -0
- package/dist/event/strategies/memory.d.ts.map +1 -0
- package/dist/event/strategies/memory.js +331 -0
- package/dist/event/strategies/memory.js.map +1 -0
- package/dist/event/strategies/redis.d.ts +115 -0
- package/dist/event/strategies/redis.d.ts.map +1 -0
- package/dist/event/strategies/redis.js +434 -0
- package/dist/event/strategies/redis.js.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/logger/defaults.d.ts +67 -0
- package/dist/logger/defaults.d.ts.map +1 -0
- package/dist/logger/defaults.js +213 -0
- package/dist/logger/defaults.js.map +1 -0
- package/dist/logger/index.d.ts +84 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/logger/index.js +101 -0
- package/dist/logger/index.js.map +1 -0
- package/dist/logger/logger.d.ts +165 -0
- package/dist/logger/logger.d.ts.map +1 -0
- package/dist/logger/logger.js +843 -0
- package/dist/logger/logger.js.map +1 -0
- package/dist/logger/transports/console.d.ts +102 -0
- package/dist/logger/transports/console.d.ts.map +1 -0
- package/dist/logger/transports/console.js +276 -0
- package/dist/logger/transports/console.js.map +1 -0
- package/dist/logger/transports/database.d.ts +153 -0
- package/dist/logger/transports/database.d.ts.map +1 -0
- package/dist/logger/transports/database.js +539 -0
- package/dist/logger/transports/database.js.map +1 -0
- package/dist/logger/transports/file.d.ts +146 -0
- package/dist/logger/transports/file.d.ts.map +1 -0
- package/dist/logger/transports/file.js +464 -0
- package/dist/logger/transports/file.js.map +1 -0
- package/dist/logger/transports/http.d.ts +128 -0
- package/dist/logger/transports/http.d.ts.map +1 -0
- package/dist/logger/transports/http.js +401 -0
- package/dist/logger/transports/http.js.map +1 -0
- package/dist/logger/transports/webhook.d.ts +152 -0
- package/dist/logger/transports/webhook.d.ts.map +1 -0
- package/dist/logger/transports/webhook.js +485 -0
- package/dist/logger/transports/webhook.js.map +1 -0
- package/dist/queue/defaults.d.ts +66 -0
- package/dist/queue/defaults.d.ts.map +1 -0
- package/dist/queue/defaults.js +205 -0
- package/dist/queue/defaults.js.map +1 -0
- package/dist/queue/index.d.ts +124 -0
- package/dist/queue/index.d.ts.map +1 -0
- package/dist/queue/index.js +116 -0
- package/dist/queue/index.js.map +1 -0
- package/dist/queue/queue.d.ts +156 -0
- package/dist/queue/queue.d.ts.map +1 -0
- package/dist/queue/queue.js +387 -0
- package/dist/queue/queue.js.map +1 -0
- package/dist/queue/transports/database.d.ts +165 -0
- package/dist/queue/transports/database.d.ts.map +1 -0
- package/dist/queue/transports/database.js +595 -0
- package/dist/queue/transports/database.js.map +1 -0
- package/dist/queue/transports/memory.d.ts +143 -0
- package/dist/queue/transports/memory.d.ts.map +1 -0
- package/dist/queue/transports/memory.js +415 -0
- package/dist/queue/transports/memory.js.map +1 -0
- package/dist/queue/transports/redis.d.ts +203 -0
- package/dist/queue/transports/redis.d.ts.map +1 -0
- package/dist/queue/transports/redis.js +744 -0
- package/dist/queue/transports/redis.js.map +1 -0
- package/dist/security/defaults.d.ts +64 -0
- package/dist/security/defaults.d.ts.map +1 -0
- package/dist/security/defaults.js +159 -0
- package/dist/security/defaults.js.map +1 -0
- package/dist/security/index.d.ts +110 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/security/index.js +160 -0
- package/dist/security/index.js.map +1 -0
- package/dist/security/security.d.ts +138 -0
- package/dist/security/security.d.ts.map +1 -0
- package/dist/security/security.js +419 -0
- package/dist/security/security.js.map +1 -0
- package/dist/storage/defaults.d.ts +79 -0
- package/dist/storage/defaults.d.ts.map +1 -0
- package/dist/storage/defaults.js +358 -0
- package/dist/storage/defaults.js.map +1 -0
- package/dist/storage/index.d.ts +153 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +242 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/storage.d.ts +151 -0
- package/dist/storage/storage.d.ts.map +1 -0
- package/dist/storage/storage.js +439 -0
- package/dist/storage/storage.js.map +1 -0
- package/dist/storage/strategies/local.d.ts +117 -0
- package/dist/storage/strategies/local.d.ts.map +1 -0
- package/dist/storage/strategies/local.js +368 -0
- package/dist/storage/strategies/local.js.map +1 -0
- package/dist/storage/strategies/r2.d.ts +130 -0
- package/dist/storage/strategies/r2.d.ts.map +1 -0
- package/dist/storage/strategies/r2.js +470 -0
- package/dist/storage/strategies/r2.js.map +1 -0
- package/dist/storage/strategies/s3.d.ts +121 -0
- package/dist/storage/strategies/s3.d.ts.map +1 -0
- package/dist/storage/strategies/s3.js +461 -0
- package/dist/storage/strategies/s3.js.map +1 -0
- package/dist/util/defaults.d.ts +77 -0
- package/dist/util/defaults.d.ts.map +1 -0
- package/dist/util/defaults.js +193 -0
- package/dist/util/defaults.js.map +1 -0
- package/dist/util/index.d.ts +97 -0
- package/dist/util/index.d.ts.map +1 -0
- package/dist/util/index.js +165 -0
- package/dist/util/index.js.map +1 -0
- package/dist/util/util.d.ts +145 -0
- package/dist/util/util.d.ts.map +1 -0
- package/dist/util/util.js +481 -0
- package/dist/util/util.js.map +1 -0
- 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"}
|