@expressots/core 4.0.0-preview.1 → 4.0.0-preview.3
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.md +21 -21
- package/README.md +66 -66
- package/lib/CHANGELOG.md +774 -774
- package/lib/README.md +66 -66
- package/lib/cjs/application/application-factory.js +6 -0
- package/lib/cjs/application/bootstrap.js +117 -213
- package/lib/cjs/config/define-config.js +1 -1
- package/lib/cjs/config/env-field-builders.js +47 -0
- package/lib/cjs/config/index.js +7 -1
- package/lib/cjs/framework-version.js +10 -0
- package/lib/cjs/lazy-loading/index.js +5 -1
- package/lib/cjs/lazy-loading/lazy-module-helpers.js +49 -0
- package/lib/cjs/middleware/index.js +8 -9
- package/lib/cjs/middleware/middleware-service.js +68 -12
- package/lib/cjs/middleware/presets-standalone.js +93 -0
- package/lib/cjs/provider/db-in-memory/adapter/in-memory.adapter.js +23 -0
- package/lib/cjs/provider/db-in-memory/index.js +11 -1
- package/lib/cjs/provider/db-in-memory/query/query-engine.js +28 -0
- package/lib/cjs/provider/db-in-memory/schema/decorators.js +18 -0
- package/lib/cjs/provider/db-in-memory/storage/index.js +3 -1
- package/lib/cjs/provider/db-in-memory/storage/memory-store.js +72 -1
- package/lib/cjs/provider/logger/logger.banner.js +40 -31
- package/lib/cjs/provider/logger/logger.config.js +11 -1
- package/lib/cjs/provider/logger/logger.formatter.js +22 -1
- package/lib/cjs/provider/logger/logger.provider.js +59 -9
- package/lib/cjs/provider/logger/transports/console.transport.js +69 -6
- package/lib/cjs/provider/logger/transports/file.transport.js +27 -18
- package/lib/cjs/provider/logger/utils/log-levels.js +6 -5
- package/lib/cjs/provider/validation/adapters/index.js +12 -5
- package/lib/cjs/provider/validation/adapters/yup.adapter.js +118 -0
- package/lib/cjs/provider/validation/adapters/zod.adapter.js +137 -0
- package/lib/cjs/provider/validation/index.js +5 -1
- package/lib/cjs/render/adapters/react-adapter.js +14 -14
- package/lib/cjs/render/features/type-generator.js +30 -30
- package/lib/cjs/render/features/view-debugger.js +75 -55
- package/lib/cjs/testing/fluent-request.js +7 -0
- package/lib/cjs/testing/snapshot-request.js +2 -0
- package/lib/cjs/types/application/application-factory.d.ts +6 -0
- package/lib/cjs/types/application/bootstrap.d.ts +196 -24
- package/lib/cjs/types/config/config.interfaces.d.ts +7 -1
- package/lib/cjs/types/config/env-field-builders.d.ts +39 -0
- package/lib/cjs/types/config/index.d.ts +1 -1
- package/lib/cjs/types/framework-version.d.ts +7 -0
- package/lib/cjs/types/lazy-loading/index.d.ts +1 -0
- package/lib/cjs/types/lazy-loading/lazy-module-helpers.d.ts +42 -0
- package/lib/cjs/types/middleware/index.d.ts +1 -1
- package/lib/cjs/types/middleware/middleware-service.d.ts +21 -0
- package/lib/cjs/types/middleware/presets-standalone.d.ts +75 -0
- package/lib/cjs/types/provider/db-in-memory/adapter/in-memory.adapter.d.ts +2 -0
- package/lib/cjs/types/provider/db-in-memory/index.d.ts +9 -1
- package/lib/cjs/types/provider/db-in-memory/query/query-engine.d.ts +14 -1
- package/lib/cjs/types/provider/db-in-memory/schema/decorators.d.ts +14 -0
- package/lib/cjs/types/provider/db-in-memory/storage/index.d.ts +1 -1
- package/lib/cjs/types/provider/db-in-memory/storage/memory-store.d.ts +34 -0
- package/lib/cjs/types/provider/logger/logger.banner.d.ts +1 -1
- package/lib/cjs/types/provider/logger/logger.config.d.ts +7 -0
- package/lib/cjs/types/provider/logger/logger.formatter.d.ts +10 -0
- package/lib/cjs/types/provider/logger/logger.provider.d.ts +32 -1
- package/lib/cjs/types/provider/logger/transports/console.transport.d.ts +7 -0
- package/lib/cjs/types/provider/logger/utils/log-levels.d.ts +3 -3
- package/lib/cjs/types/provider/validation/adapters/index.d.ts +7 -4
- package/lib/cjs/types/provider/validation/adapters/yup.adapter.d.ts +65 -0
- package/lib/cjs/types/provider/validation/adapters/zod.adapter.d.ts +84 -0
- package/lib/cjs/types/provider/validation/index.d.ts +1 -1
- package/lib/cjs/types/render/features/view-debugger.d.ts +10 -0
- package/lib/cjs/types/testing/testing.interfaces.d.ts +31 -6
- package/lib/esm/application/application-factory.js +6 -0
- package/lib/esm/application/bootstrap.js +117 -213
- package/lib/esm/config/define-config.js +1 -1
- package/lib/esm/config/env-field-builders.js +48 -0
- package/lib/esm/config/index.js +6 -1
- package/lib/esm/framework-version.js +7 -0
- package/lib/esm/lazy-loading/index.js +2 -0
- package/lib/esm/lazy-loading/lazy-module-helpers.js +45 -0
- package/lib/esm/middleware/index.js +3 -2
- package/lib/esm/middleware/middleware-service.js +68 -12
- package/lib/esm/middleware/presets-standalone.js +87 -0
- package/lib/esm/provider/db-in-memory/adapter/in-memory.adapter.js +23 -0
- package/lib/esm/provider/db-in-memory/index.js +9 -1
- package/lib/esm/provider/db-in-memory/query/query-engine.js +28 -0
- package/lib/esm/provider/db-in-memory/schema/decorators.js +18 -0
- package/lib/esm/provider/db-in-memory/storage/index.js +1 -1
- package/lib/esm/provider/db-in-memory/storage/memory-store.js +75 -0
- package/lib/esm/provider/logger/logger.banner.js +40 -31
- package/lib/esm/provider/logger/logger.config.js +12 -2
- package/lib/esm/provider/logger/logger.formatter.js +22 -1
- package/lib/esm/provider/logger/logger.provider.js +61 -10
- package/lib/esm/provider/logger/transports/console.transport.js +69 -6
- package/lib/esm/provider/logger/transports/file.transport.js +27 -18
- package/lib/esm/provider/logger/utils/log-levels.js +6 -5
- package/lib/esm/provider/validation/adapters/index.js +7 -4
- package/lib/esm/provider/validation/adapters/yup.adapter.js +111 -0
- package/lib/esm/provider/validation/adapters/zod.adapter.js +130 -0
- package/lib/esm/provider/validation/index.js +1 -1
- package/lib/esm/render/adapters/react-adapter.js +14 -14
- package/lib/esm/render/features/type-generator.js +30 -30
- package/lib/esm/render/features/view-debugger.js +75 -55
- package/lib/esm/testing/fluent-request.js +7 -0
- package/lib/esm/testing/snapshot-request.js +2 -0
- package/lib/esm/types/application/application-factory.d.ts +6 -0
- package/lib/esm/types/application/bootstrap.d.ts +196 -24
- package/lib/esm/types/config/config.interfaces.d.ts +7 -1
- package/lib/esm/types/config/env-field-builders.d.ts +39 -0
- package/lib/esm/types/config/index.d.ts +1 -1
- package/lib/esm/types/framework-version.d.ts +7 -0
- package/lib/esm/types/lazy-loading/index.d.ts +1 -0
- package/lib/esm/types/lazy-loading/lazy-module-helpers.d.ts +42 -0
- package/lib/esm/types/middleware/index.d.ts +1 -1
- package/lib/esm/types/middleware/middleware-service.d.ts +21 -0
- package/lib/esm/types/middleware/presets-standalone.d.ts +75 -0
- package/lib/esm/types/provider/db-in-memory/adapter/in-memory.adapter.d.ts +2 -0
- package/lib/esm/types/provider/db-in-memory/index.d.ts +9 -1
- package/lib/esm/types/provider/db-in-memory/query/query-engine.d.ts +14 -1
- package/lib/esm/types/provider/db-in-memory/schema/decorators.d.ts +14 -0
- package/lib/esm/types/provider/db-in-memory/storage/index.d.ts +1 -1
- package/lib/esm/types/provider/db-in-memory/storage/memory-store.d.ts +34 -0
- package/lib/esm/types/provider/logger/logger.banner.d.ts +1 -1
- package/lib/esm/types/provider/logger/logger.config.d.ts +7 -0
- package/lib/esm/types/provider/logger/logger.formatter.d.ts +10 -0
- package/lib/esm/types/provider/logger/logger.provider.d.ts +32 -1
- package/lib/esm/types/provider/logger/transports/console.transport.d.ts +7 -0
- package/lib/esm/types/provider/logger/utils/log-levels.d.ts +3 -3
- package/lib/esm/types/provider/validation/adapters/index.d.ts +7 -4
- package/lib/esm/types/provider/validation/adapters/yup.adapter.d.ts +65 -0
- package/lib/esm/types/provider/validation/adapters/zod.adapter.d.ts +84 -0
- package/lib/esm/types/provider/validation/index.d.ts +1 -1
- package/lib/esm/types/render/features/view-debugger.d.ts +10 -0
- package/lib/esm/types/testing/testing.interfaces.d.ts +31 -6
- package/lib/package.json +23 -8
- package/package.json +23 -8
- package/lib/cjs/middleware/middleware-presets.js +0 -294
- package/lib/cjs/types/middleware/middleware-presets.d.ts +0 -90
- package/lib/esm/middleware/middleware-presets.js +0 -286
- package/lib/esm/types/middleware/middleware-presets.d.ts +0 -90
|
@@ -4,35 +4,75 @@ import { Logger } from "../provider/logger/logger.provider.js";
|
|
|
4
4
|
import fs from "fs";
|
|
5
5
|
import path from "path";
|
|
6
6
|
/**
|
|
7
|
-
* Synchronously load
|
|
7
|
+
* Synchronously load `.env` files into `process.env`.
|
|
8
8
|
*
|
|
9
|
-
* Call this
|
|
10
|
-
*
|
|
9
|
+
* Call this **before** `bootstrap()` or `defineConfig()` so that every
|
|
10
|
+
* environment variable is available when the rest of the application
|
|
11
|
+
* resolves configuration, binds services, or reads `Env.*` builders.
|
|
11
12
|
*
|
|
12
|
-
*
|
|
13
|
+
* ### How it works
|
|
13
14
|
*
|
|
14
|
-
*
|
|
15
|
+
* 1. Reads `process.env.NODE_ENV` (defaults to `"development"`).
|
|
16
|
+
* 2. Loads files in priority order (last file wins on conflicts):
|
|
17
|
+
* `.env` → `.env.local` → `.env.{env}.local` → `.env.{env}`
|
|
18
|
+
* 3. If `options.files` maps the current environment to a custom name
|
|
19
|
+
* (e.g. `{ production: ".env.prod" }`), that name replaces `.env.{env}`.
|
|
20
|
+
* 4. Missing files are silently skipped.
|
|
21
|
+
* 5. Sets `process.env._EXPRESSOTS_ENV_LOADED = "true"` so subsequent
|
|
22
|
+
* calls are no-ops (unless `force: true`).
|
|
23
|
+
*
|
|
24
|
+
* ### Important
|
|
25
|
+
*
|
|
26
|
+
* `NODE_ENV` must already be set in the **shell** (or inherited from
|
|
27
|
+
* the process) before `loadEnvSync()` runs. The function reads
|
|
28
|
+
* `process.env.NODE_ENV` to decide which file to load; it does **not**
|
|
29
|
+
* derive the environment from file contents.
|
|
30
|
+
*
|
|
31
|
+
* @param options - See {@link LoadEnvSyncOptions} for all fields.
|
|
32
|
+
*
|
|
33
|
+
* @example Zero-config (recommended for most apps)
|
|
15
34
|
* ```typescript
|
|
16
|
-
* //
|
|
17
|
-
* import {
|
|
35
|
+
* // src/main.ts
|
|
36
|
+
* import { bootstrap, loadEnvSync } from "@expressots/core";
|
|
37
|
+
* import { App } from "./app";
|
|
38
|
+
*
|
|
39
|
+
* loadEnvSync(); // .env + .env.development (or .env.production, etc.)
|
|
40
|
+
* void bootstrap(App);
|
|
41
|
+
* ```
|
|
18
42
|
*
|
|
19
|
-
*
|
|
43
|
+
* @example Custom file mapping
|
|
44
|
+
* ```typescript
|
|
20
45
|
* loadEnvSync({
|
|
21
46
|
* files: {
|
|
22
|
-
* development: ".env
|
|
23
|
-
* production:
|
|
47
|
+
* development: ".env",
|
|
48
|
+
* production: ".env.prod",
|
|
49
|
+
* staging: ".env.staging",
|
|
24
50
|
* },
|
|
25
51
|
* });
|
|
52
|
+
* // With NODE_ENV=production -> .env + .env.prod
|
|
53
|
+
* // With NODE_ENV=development -> .env (mapped to itself, so loaded once)
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @example With defineConfig()
|
|
57
|
+
* ```typescript
|
|
58
|
+
* // src/config.ts
|
|
59
|
+
* import { defineConfig, Env, loadEnvSync } from "@expressots/core";
|
|
60
|
+
*
|
|
61
|
+
* loadEnvSync({ files: { production: ".env.prod" } });
|
|
26
62
|
*
|
|
27
|
-
* // Now defineConfig() will read from loaded .env files
|
|
28
63
|
* export const appConfig = defineConfig({
|
|
29
|
-
* server: {
|
|
30
|
-
* port: Env.port("PORT", { default: 3000 }),
|
|
31
|
-
* },
|
|
64
|
+
* server: { port: Env.port("PORT", { default: 3000 }) },
|
|
32
65
|
* });
|
|
33
66
|
* ```
|
|
34
67
|
*
|
|
35
|
-
* @
|
|
68
|
+
* @example Force reload (hot-module replacement)
|
|
69
|
+
* ```typescript
|
|
70
|
+
* loadEnvSync({ force: true });
|
|
71
|
+
* ```
|
|
72
|
+
*
|
|
73
|
+
* @see {@link LoadEnvSyncOptions} for option details
|
|
74
|
+
* @see {@link BootstrapOptions.envFileConfig} for the bootstrap()-level alternative
|
|
75
|
+
* @public
|
|
36
76
|
*/
|
|
37
77
|
export function loadEnvSync(options) {
|
|
38
78
|
// Skip if already loaded (unless force reload)
|
|
@@ -95,26 +135,26 @@ const CI_PLATFORM_MAP = new Map([
|
|
|
95
135
|
const PLATFORM_HINTS = new Map([
|
|
96
136
|
[
|
|
97
137
|
"GitHub Actions",
|
|
98
|
-
(missingVars) => `
|
|
99
|
-
🔧 GitHub Actions Setup:
|
|
100
|
-
- Go to: Settings → Secrets and variables → Actions
|
|
101
|
-
- Add repository secrets for: ${missingVars}
|
|
138
|
+
(missingVars) => `
|
|
139
|
+
🔧 GitHub Actions Setup:
|
|
140
|
+
- Go to: Settings → Secrets and variables → Actions
|
|
141
|
+
- Add repository secrets for: ${missingVars}
|
|
102
142
|
- Use: \${{ secrets.VARIABLE_NAME }} in workflow files`,
|
|
103
143
|
],
|
|
104
144
|
[
|
|
105
145
|
"GitLab CI",
|
|
106
|
-
(missingVars) => `
|
|
107
|
-
🔧 GitLab CI Setup:
|
|
108
|
-
- Go to: Settings → CI/CD → Variables
|
|
109
|
-
- Add CI/CD variables for: ${missingVars}
|
|
146
|
+
(missingVars) => `
|
|
147
|
+
🔧 GitLab CI Setup:
|
|
148
|
+
- Go to: Settings → CI/CD → Variables
|
|
149
|
+
- Add CI/CD variables for: ${missingVars}
|
|
110
150
|
- Use: $VARIABLE_NAME in .gitlab-ci.yml`,
|
|
111
151
|
],
|
|
112
152
|
[
|
|
113
153
|
"Jenkins",
|
|
114
|
-
(missingVars) => `
|
|
115
|
-
🔧 Jenkins Setup:
|
|
116
|
-
- Configure: Manage Jenkins → Credentials
|
|
117
|
-
- Add credentials for: ${missingVars}
|
|
154
|
+
(missingVars) => `
|
|
155
|
+
🔧 Jenkins Setup:
|
|
156
|
+
- Configure: Manage Jenkins → Credentials
|
|
157
|
+
- Add credentials for: ${missingVars}
|
|
118
158
|
- Use: env.VARIABLE_NAME in pipeline`,
|
|
119
159
|
],
|
|
120
160
|
]);
|
|
@@ -179,14 +219,14 @@ function getPlatformHint(platform, missing) {
|
|
|
179
219
|
class EnvFileNotFoundError extends Error {
|
|
180
220
|
constructor(fileName, environment) {
|
|
181
221
|
const template = getEnvTemplate(environment);
|
|
182
|
-
super(`
|
|
183
|
-
❌ Missing required environment file: ${fileName}
|
|
184
|
-
|
|
185
|
-
💡 Create ${fileName} with:
|
|
186
|
-
${template}
|
|
187
|
-
|
|
188
|
-
📖 Docs: https://expresso-ts.com/docs/env
|
|
189
|
-
🔍 Check existing files: ls -la .env*
|
|
222
|
+
super(`
|
|
223
|
+
❌ Missing required environment file: ${fileName}
|
|
224
|
+
|
|
225
|
+
💡 Create ${fileName} with:
|
|
226
|
+
${template}
|
|
227
|
+
|
|
228
|
+
📖 Docs: https://expresso-ts.com/docs/env
|
|
229
|
+
🔍 Check existing files: ls -la .env*
|
|
190
230
|
`.trim());
|
|
191
231
|
this.name = "EnvFileNotFoundError";
|
|
192
232
|
}
|
|
@@ -199,20 +239,20 @@ class CIEnvValidationError extends Error {
|
|
|
199
239
|
constructor(missing, environment) {
|
|
200
240
|
const ciPlatform = detectCIPlatform();
|
|
201
241
|
const platformHint = getPlatformHint(ciPlatform, missing);
|
|
202
|
-
super(`
|
|
203
|
-
❌ CI/CD Environment Validation Failed
|
|
204
|
-
|
|
205
|
-
Missing required environment variables in ${environment}:
|
|
206
|
-
${missing.map((key) => ` • ${key}`).join("\n")}
|
|
207
|
-
|
|
208
|
-
${platformHint}
|
|
209
|
-
|
|
210
|
-
💡 Action Required:
|
|
211
|
-
1. Add missing variables to your CI/CD platform secrets
|
|
212
|
-
2. Ensure variables are available in ${environment} environment
|
|
213
|
-
3. Check variable names match exactly (case-sensitive)
|
|
214
|
-
|
|
215
|
-
📖 Docs: https://expresso-ts.com/docs/ci-cd
|
|
242
|
+
super(`
|
|
243
|
+
❌ CI/CD Environment Validation Failed
|
|
244
|
+
|
|
245
|
+
Missing required environment variables in ${environment}:
|
|
246
|
+
${missing.map((key) => ` • ${key}`).join("\n")}
|
|
247
|
+
|
|
248
|
+
${platformHint}
|
|
249
|
+
|
|
250
|
+
💡 Action Required:
|
|
251
|
+
1. Add missing variables to your CI/CD platform secrets
|
|
252
|
+
2. Ensure variables are available in ${environment} environment
|
|
253
|
+
3. Check variable names match exactly (case-sensitive)
|
|
254
|
+
|
|
255
|
+
📖 Docs: https://expresso-ts.com/docs/ci-cd
|
|
216
256
|
`.trim());
|
|
217
257
|
this.name = "CIEnvValidationError";
|
|
218
258
|
}
|
|
@@ -223,16 +263,16 @@ ${platformHint}
|
|
|
223
263
|
*/
|
|
224
264
|
class EnvValidationError extends Error {
|
|
225
265
|
constructor(missing, fileName) {
|
|
226
|
-
super(`
|
|
227
|
-
❌ Environment validation failed
|
|
228
|
-
|
|
229
|
-
Missing values in ${fileName}:
|
|
230
|
-
${missing.map((key) => ` • ${key} (required but empty)`).join("\n")}
|
|
231
|
-
|
|
232
|
-
💡 Add values to ${fileName}:
|
|
233
|
-
${missing.map((key) => ` ${key}=your-value-here`).join("\n")}
|
|
234
|
-
|
|
235
|
-
📖 Docs: https://expresso-ts.com/docs/env
|
|
266
|
+
super(`
|
|
267
|
+
❌ Environment validation failed
|
|
268
|
+
|
|
269
|
+
Missing values in ${fileName}:
|
|
270
|
+
${missing.map((key) => ` • ${key} (required but empty)`).join("\n")}
|
|
271
|
+
|
|
272
|
+
💡 Add values to ${fileName}:
|
|
273
|
+
${missing.map((key) => ` ${key}=your-value-here`).join("\n")}
|
|
274
|
+
|
|
275
|
+
📖 Docs: https://expresso-ts.com/docs/env
|
|
236
276
|
`.trim());
|
|
237
277
|
this.name = "EnvValidationError";
|
|
238
278
|
}
|
|
@@ -244,8 +284,8 @@ ${missing.map((key) => ` ${key}=your-value-here`).join("\n")}
|
|
|
244
284
|
* @private
|
|
245
285
|
*/
|
|
246
286
|
function getEnvTemplate(environment) {
|
|
247
|
-
return `PORT=3000
|
|
248
|
-
NODE_ENV=${environment}
|
|
287
|
+
return `PORT=3000
|
|
288
|
+
NODE_ENV=${environment}
|
|
249
289
|
# Add your environment variables here`;
|
|
250
290
|
}
|
|
251
291
|
/**
|
|
@@ -410,11 +450,11 @@ async function loadAndValidateEnvironment(currentEnvironment, envFileConfig) {
|
|
|
410
450
|
// 🎯 STEP 2: Determine and load the file for the current environment
|
|
411
451
|
// Only ONE file is loaded at runtime - the file for the current environment
|
|
412
452
|
const envFileName = envFileConfig.files?.[currentEnvironment] ?? `.env.${currentEnvironment}`;
|
|
413
|
-
// Load optional files silently
|
|
453
|
+
// Load optional files silently (override: true so later files win)
|
|
414
454
|
const optionalFiles = [".env", ".env.local", `${envFileName}.local`];
|
|
415
455
|
for (const file of optionalFiles) {
|
|
416
456
|
try {
|
|
417
|
-
config({ path: file });
|
|
457
|
+
config({ path: file, override: true });
|
|
418
458
|
result.loaded.push(file);
|
|
419
459
|
}
|
|
420
460
|
catch {
|
|
@@ -457,8 +497,8 @@ async function loadAndValidateEnvironment(currentEnvironment, envFileConfig) {
|
|
|
457
497
|
}
|
|
458
498
|
}
|
|
459
499
|
else {
|
|
460
|
-
// Load the file
|
|
461
|
-
config({ path: envFileName });
|
|
500
|
+
// Load the environment-specific file (highest priority, overrides all previous)
|
|
501
|
+
config({ path: envFileName, override: true });
|
|
462
502
|
result.loaded.push(envFileName);
|
|
463
503
|
}
|
|
464
504
|
// Validate variables have values
|
|
@@ -522,151 +562,6 @@ async function readPackageJson() {
|
|
|
522
562
|
return _packageCache;
|
|
523
563
|
}
|
|
524
564
|
}
|
|
525
|
-
/**
|
|
526
|
-
* Bootstrap the ExpressoTS application with zero configuration.
|
|
527
|
-
*
|
|
528
|
-
* @layer public
|
|
529
|
-
* @audience application-developers
|
|
530
|
-
* @concept bootstrap
|
|
531
|
-
* @difficulty beginner
|
|
532
|
-
*
|
|
533
|
-
* @summary Quick Start
|
|
534
|
-
* The simplest way to start your application:
|
|
535
|
-
* ```typescript
|
|
536
|
-
* await bootstrap(App);
|
|
537
|
-
* ```
|
|
538
|
-
*
|
|
539
|
-
* This function orchestrates 8 critical startup phases:
|
|
540
|
-
* 1. Environment detection (CI/CD vs local)
|
|
541
|
-
* 2. Smart .env loading with opt-in behavior
|
|
542
|
-
* 3. Port determination (priority chain)
|
|
543
|
-
* 4. Package.json metadata extraction
|
|
544
|
-
* 5. DI container initialization via AppFactory
|
|
545
|
-
* 6. Environment injection into app instance
|
|
546
|
-
* 7. API version detection from decorators
|
|
547
|
-
* 8. Server startup with graceful shutdown
|
|
548
|
-
*
|
|
549
|
-
* @param AppClass - Application class extending AppExpress
|
|
550
|
-
* @param options - Optional bootstrap configuration
|
|
551
|
-
* @returns Promise resolving to IWebServerPublic instance
|
|
552
|
-
*
|
|
553
|
-
* @example
|
|
554
|
-
* ```typescript
|
|
555
|
-
* // Simplest usage - zero config (no .env file loading)
|
|
556
|
-
* await bootstrap(App);
|
|
557
|
-
*
|
|
558
|
-
* // With overrides (still no .env file loading)
|
|
559
|
-
* await bootstrap(App, {
|
|
560
|
-
* port: 4000,
|
|
561
|
-
* appName: "My API",
|
|
562
|
-
* appVersion: "2.0.0"
|
|
563
|
-
* });
|
|
564
|
-
*
|
|
565
|
-
* // Opt-in to .env file loading and auto-creation
|
|
566
|
-
* await bootstrap(App, {
|
|
567
|
-
* currentEnvironment: "development",
|
|
568
|
-
* envFileConfig: {
|
|
569
|
-
* files: {
|
|
570
|
-
* development: ".env.dev",
|
|
571
|
-
* production: ".env.prod"
|
|
572
|
-
* },
|
|
573
|
-
* required: ["DATABASE_URL", "API_KEY"],
|
|
574
|
-
* autoCreateTemplate: true, // Explicitly enable file creation
|
|
575
|
-
* validateValues: true
|
|
576
|
-
* }
|
|
577
|
-
* });
|
|
578
|
-
*
|
|
579
|
-
* // Auto-assign port (useful for testing)
|
|
580
|
-
* await bootstrap(App, { port: 0 });
|
|
581
|
-
* ```
|
|
582
|
-
*
|
|
583
|
-
* @layer internal
|
|
584
|
-
* @audience framework-developers
|
|
585
|
-
*
|
|
586
|
-
* **Internal Architecture**
|
|
587
|
-
*
|
|
588
|
-
* Bootstrap orchestrates 8 critical steps:
|
|
589
|
-
* 1. Environment detection (CI/CD vs local)
|
|
590
|
-
* 2. Smart .env loading with opt-in behavior
|
|
591
|
-
* 3. Port determination (priority chain)
|
|
592
|
-
* 4. Package.json metadata extraction
|
|
593
|
-
* 5. DI container initialization via AppFactory
|
|
594
|
-
* 6. Environment injection into app instance
|
|
595
|
-
* 7. API version detection from decorators
|
|
596
|
-
* 8. Server startup with graceful shutdown
|
|
597
|
-
*
|
|
598
|
-
* **Design Decisions**
|
|
599
|
-
* - Opt-in .env loading prevents breaking changes for containerized deployments
|
|
600
|
-
* - Port 0 support enables parallel testing without conflicts
|
|
601
|
-
* - Early validation fails fast with actionable error messages
|
|
602
|
-
* - CI/CD auto-detection provides zero-config for containerized environments
|
|
603
|
-
*
|
|
604
|
-
* **Performance Characteristics**
|
|
605
|
-
* - Startup time: ~8-25ms typical (optimized)
|
|
606
|
-
* - Environment loading: ~2-5ms (file I/O)
|
|
607
|
-
* - Package.json read: ~1-2ms first call, cached thereafter
|
|
608
|
-
* - CI detection: cached after first call
|
|
609
|
-
* - App instantiation: ~5-10ms (DI container setup)
|
|
610
|
-
* - Logger instances: lazy initialization (only created when needed)
|
|
611
|
-
*
|
|
612
|
-
* @see {@link loadAndValidateEnvironment} for environment loading logic
|
|
613
|
-
* @see {@link AppFactory.create} for DI container initialization
|
|
614
|
-
* @see {@link determinePort} for port resolution logic
|
|
615
|
-
*
|
|
616
|
-
* @layer advanced
|
|
617
|
-
* @audience power-users
|
|
618
|
-
*
|
|
619
|
-
* **Advanced Patterns**
|
|
620
|
-
*
|
|
621
|
-
* Multi-environment setup with validation:
|
|
622
|
-
* ```typescript
|
|
623
|
-
* await bootstrap(App, {
|
|
624
|
-
* currentEnvironment: process.env.NODE_ENV || "development",
|
|
625
|
-
* envFileConfig: {
|
|
626
|
-
* files: {
|
|
627
|
-
* development: ".env.dev",
|
|
628
|
-
* staging: ".env.staging",
|
|
629
|
-
* production: ".env.prod"
|
|
630
|
-
* },
|
|
631
|
-
* required: ["DATABASE_URL", "JWT_SECRET"],
|
|
632
|
-
* autoCreateTemplate: true,
|
|
633
|
-
* validateValues: process.env.NODE_ENV === "production"
|
|
634
|
-
* }
|
|
635
|
-
* });
|
|
636
|
-
* ```
|
|
637
|
-
*
|
|
638
|
-
* Containerized deployment (Docker/K8s):
|
|
639
|
-
* ```typescript
|
|
640
|
-
* await bootstrap(App, {
|
|
641
|
-
* envFileConfig: {
|
|
642
|
-
* skipFileLoading: true, // Use process.env only
|
|
643
|
-
* required: ["DATABASE_URL", "REDIS_URL"]
|
|
644
|
-
* }
|
|
645
|
-
* });
|
|
646
|
-
* ```
|
|
647
|
-
*
|
|
648
|
-
* Testing with dynamic ports:
|
|
649
|
-
* ```typescript
|
|
650
|
-
* const server = await bootstrap(App, { port: 0 });
|
|
651
|
-
* const actualPort = server.port; // Use in tests
|
|
652
|
-
* ```
|
|
653
|
-
*
|
|
654
|
-
* @troubleshooting
|
|
655
|
-
* **Common Issues**
|
|
656
|
-
* - ❌ PORT not detected → Use options.port or envFileConfig
|
|
657
|
-
* - ❌ CI validation fails → Check platform secrets configuration
|
|
658
|
-
* - ❌ Template not created → Set autoCreateTemplate: true explicitly
|
|
659
|
-
* - ❌ Port already in use → Use port: 0 for testing
|
|
660
|
-
*
|
|
661
|
-
* @performance
|
|
662
|
-
* - Async initialization: ~5-15ms (typical)
|
|
663
|
-
* - Package.json read: ~2ms first call, cached thereafter
|
|
664
|
-
* - CI detection: cached after first call
|
|
665
|
-
* - Port binding: varies by OS
|
|
666
|
-
* - Total startup: ~8-25ms (optimized with caching)
|
|
667
|
-
*
|
|
668
|
-
* @public API
|
|
669
|
-
*/
|
|
670
565
|
/**
|
|
671
566
|
* Type guard to check if argument is BootstrapConfig.
|
|
672
567
|
* Detects config objects by checking for the required structure.
|
|
@@ -707,9 +602,7 @@ function transformConfigToOptions(config) {
|
|
|
707
602
|
envFileConfig: config.bootstrap?.envFileConfig,
|
|
708
603
|
};
|
|
709
604
|
}
|
|
710
|
-
/**
|
|
711
|
-
* Implementation.
|
|
712
|
-
*/
|
|
605
|
+
/** @internal Implementation overload. */
|
|
713
606
|
export async function bootstrap(AppClass, optionsOrConfig) {
|
|
714
607
|
let logger;
|
|
715
608
|
// Note: Path resolution is auto-initialized as a side effect when
|
|
@@ -769,6 +662,17 @@ export async function bootstrap(AppClass, optionsOrConfig) {
|
|
|
769
662
|
// STEP 5: Create app instance
|
|
770
663
|
// App's globalConfiguration() will run in constructor
|
|
771
664
|
// initEnvironment() can skip if .env already loaded
|
|
665
|
+
//
|
|
666
|
+
// Activate log buffering BEFORE constructing the app so any logs the
|
|
667
|
+
// application emits during construction (e.g. inside `globalConfiguration()`
|
|
668
|
+
// or `configureServices()`) are captured and replayed in the right order
|
|
669
|
+
// after the startup banner. AppClass extends AppExpress for the standard
|
|
670
|
+
// adapter, so the static method is inherited; we duck-type to avoid a
|
|
671
|
+
// hard dependency on adapter-express from core.
|
|
672
|
+
const appClassWithBuffering = AppClass;
|
|
673
|
+
if (typeof appClassWithBuffering.startLogBuffering === "function") {
|
|
674
|
+
appClassWithBuffering.startLogBuffering();
|
|
675
|
+
}
|
|
772
676
|
const app = await AppFactory.create(AppClass);
|
|
773
677
|
// Set environment on app instance (for this.environment access)
|
|
774
678
|
app.environment =
|
|
@@ -377,6 +377,49 @@ function array(envVar, options = {}) {
|
|
|
377
377
|
*
|
|
378
378
|
* @public API
|
|
379
379
|
*/
|
|
380
|
+
/**
|
|
381
|
+
* Returns true when the current Node environment (`NODE_ENV`) matches the
|
|
382
|
+
* supplied name. Pass an array to match any of several names. The comparison
|
|
383
|
+
* is case-insensitive and falls back to `"development"` when `NODE_ENV` is
|
|
384
|
+
* unset, mirroring the convention used elsewhere in the framework.
|
|
385
|
+
*
|
|
386
|
+
* ```ts
|
|
387
|
+
* const config = defineConfig({
|
|
388
|
+
* server: { port: when(Env.is("production"), 443, 3000) },
|
|
389
|
+
* });
|
|
390
|
+
* ```
|
|
391
|
+
*
|
|
392
|
+
* @public API
|
|
393
|
+
*/
|
|
394
|
+
function isEnvironment(name) {
|
|
395
|
+
const current = (process.env.NODE_ENV ?? "development").toLowerCase();
|
|
396
|
+
const targets = Array.isArray(name) ? name : [name];
|
|
397
|
+
return targets.some((target) => target.toLowerCase() === current);
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Conditional helper for environment-specific config values.
|
|
401
|
+
*
|
|
402
|
+
* `when(condition, value, fallback)` returns `value` when `condition` is
|
|
403
|
+
* truthy, otherwise `fallback`. The condition can be either a boolean
|
|
404
|
+
* (typically the result of `Env.is(...)`) or a callable that's evaluated
|
|
405
|
+
* lazily. The latter form lets you defer side-effectful checks until the
|
|
406
|
+
* config is actually resolved.
|
|
407
|
+
*
|
|
408
|
+
* ```ts
|
|
409
|
+
* const config = defineConfig({
|
|
410
|
+
* logging: {
|
|
411
|
+
* level: when(Env.is("production"), "info", "debug"),
|
|
412
|
+
* pretty: when(() => process.env.NO_COLOR !== "1", true, false),
|
|
413
|
+
* },
|
|
414
|
+
* });
|
|
415
|
+
* ```
|
|
416
|
+
*
|
|
417
|
+
* @public API
|
|
418
|
+
*/
|
|
419
|
+
function envWhen(condition, value, fallback) {
|
|
420
|
+
const ok = typeof condition === "function" ? condition() : condition;
|
|
421
|
+
return ok ? value : fallback;
|
|
422
|
+
}
|
|
380
423
|
export const Env = {
|
|
381
424
|
string,
|
|
382
425
|
number,
|
|
@@ -387,6 +430,11 @@ export const Env = {
|
|
|
387
430
|
secret,
|
|
388
431
|
json,
|
|
389
432
|
array,
|
|
433
|
+
is: isEnvironment,
|
|
434
|
+
when: envWhen,
|
|
390
435
|
};
|
|
436
|
+
// Re-export the conditional helpers as top-level free functions so users can
|
|
437
|
+
// import them without going through the `Env` namespace if they prefer.
|
|
438
|
+
export { isEnvironment as envIs, envWhen };
|
|
391
439
|
// Re-export individual builders for tree-shaking
|
|
392
440
|
export { string as envString, number as envNumber, boolean as envBoolean, enumField as envEnum, url as envUrl, port as envPort, secret as envSecret, json as envJson, array as envArray, };
|
package/lib/esm/config/index.js
CHANGED
|
@@ -60,7 +60,12 @@
|
|
|
60
60
|
// ============================================================================
|
|
61
61
|
export { defineConfig, Env } from "./define-config.js";
|
|
62
62
|
// Individual field builders (for tree-shaking)
|
|
63
|
-
export { envString, envNumber, envBoolean, envEnum, envUrl, envPort, envSecret, envJson, envArray,
|
|
63
|
+
export { envString, envNumber, envBoolean, envEnum, envUrl, envPort, envSecret, envJson, envArray,
|
|
64
|
+
// Environment-aware value selection helpers.
|
|
65
|
+
// The canonical user-facing API for `when` is `Env.when(...)` to avoid
|
|
66
|
+
// colliding with the middleware module's `when()` helper. We still expose
|
|
67
|
+
// the underlying functions under disambiguated names for advanced use.
|
|
68
|
+
envIs, envWhen, } from "./env-field-builders.js";
|
|
64
69
|
// ============================================================================
|
|
65
70
|
// Secret Value
|
|
66
71
|
// ============================================================================
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Framework version string surfaced in startup banners and diagnostics.
|
|
3
|
+
*
|
|
4
|
+
* This file is auto-synced from the root `package.json` by
|
|
5
|
+
* `scripts/sync-version.js` before each build. Do not edit by hand.
|
|
6
|
+
*/
|
|
7
|
+
export const FRAMEWORK_VERSION = "4.0.0-preview.3";
|
|
@@ -38,6 +38,8 @@
|
|
|
38
38
|
// Lazy Module
|
|
39
39
|
// ============================================================================
|
|
40
40
|
export { LazyModule, CreateLazyModule, createLazyModule, isLazyModule, getModuleName, LAZY_MODULE_METADATA_KEY, } from "./lazy-module.js";
|
|
41
|
+
// Free-function wrappers for the LazyModule chain methods.
|
|
42
|
+
export { withPreloadHint, withLazyConfig } from "./lazy-module-helpers.js";
|
|
41
43
|
// ============================================================================
|
|
42
44
|
// Lazy Module Loader
|
|
43
45
|
// ============================================================================
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Standalone (free-function) wrappers around `LazyModule` chain methods.
|
|
3
|
+
*
|
|
4
|
+
* The fluent API (`module.withPreloadHint(...).withLazyConfig(...)`) remains
|
|
5
|
+
* the recommended style. These helpers exist so users can compose lazy
|
|
6
|
+
* configurations with point-free style or apply the same hint to a list of
|
|
7
|
+
* modules:
|
|
8
|
+
*
|
|
9
|
+
* ```ts
|
|
10
|
+
* import { CreateLazyModule, withPreloadHint, withLazyConfig } from "@expressots/core";
|
|
11
|
+
*
|
|
12
|
+
* const Admin = withPreloadHint(
|
|
13
|
+
* CreateLazyModule([AdminController]),
|
|
14
|
+
* "low",
|
|
15
|
+
* );
|
|
16
|
+
*
|
|
17
|
+
* const Analytics = withLazyConfig(
|
|
18
|
+
* CreateLazyModule([AnalyticsController]),
|
|
19
|
+
* { preloadHint: "medium", prefetchAfterIdle: 5000 },
|
|
20
|
+
* );
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @public API
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Set a preload hint on the supplied lazy module and return it.
|
|
27
|
+
*
|
|
28
|
+
* Equivalent to `module.withPreloadHint(hint)`. Returned reference is the same
|
|
29
|
+
* instance — no copy is made.
|
|
30
|
+
*
|
|
31
|
+
* @public API
|
|
32
|
+
*/
|
|
33
|
+
export function withPreloadHint(module, hint) {
|
|
34
|
+
return module.withPreloadHint(hint);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Merge the supplied partial config into the lazy module and return it.
|
|
38
|
+
*
|
|
39
|
+
* Equivalent to `module.withLazyConfig(config)`.
|
|
40
|
+
*
|
|
41
|
+
* @public API
|
|
42
|
+
*/
|
|
43
|
+
export function withLazyConfig(module, config) {
|
|
44
|
+
return module.withLazyConfig(config);
|
|
45
|
+
}
|
|
@@ -4,8 +4,6 @@ export { Middleware, ExpressoMiddleware, } from "./middleware-service.js";
|
|
|
4
4
|
export { middlewareResolver, isMiddlewareAvailable, isPackageAvailable, resolvePackage, getAvailableMiddleware, getRegisteredMiddleware, clearMiddlewareCache, getPackageName, MIDDLEWARE_REGISTRY, } from "./middleware-resolver.js";
|
|
5
5
|
// Middleware profiler
|
|
6
6
|
export { MiddlewareProfiler, } from "./middleware-profiler.js";
|
|
7
|
-
// Middleware presets
|
|
8
|
-
export { MIDDLEWARE_PRESETS, getPreset, getPresetNames, getPresetsByTag, createPreset, mergePresets, } from "./middleware-presets.js";
|
|
9
7
|
// Content Negotiation exports
|
|
10
8
|
export { ContentNegotiationService, FormatterRegistry, AcceptHeaderParser, JsonFormatter, XmlFormatter, CsvFormatter, YamlFormatter, PlainTextFormatter, } from "./content-negotiation/index.js";
|
|
11
9
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -13,6 +11,9 @@ export { ContentNegotiationService, FormatterRegistry, AcceptHeaderParser, JsonF
|
|
|
13
11
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
14
12
|
// V4 Middleware utilities
|
|
15
13
|
export { use, compose, when, parallel, timeout } from "./middleware-utils.js";
|
|
14
|
+
// V4 Standalone preset helpers (free-function wrappers around
|
|
15
|
+
// Middleware.definePreset / Middleware.applyPreset)
|
|
16
|
+
export { definePreset, applyPreset, getStandalonePresetNames, clearStandalonePresets, } from "./presets-standalone.js";
|
|
16
17
|
// V4 Middleware registry
|
|
17
18
|
export { MiddlewareRegistry, getMiddlewareRegistry, resetMiddlewareRegistry, } from "./middleware-registry.js";
|
|
18
19
|
// V4 Upload registry (for @FileUpload decorator integration)
|