@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.
Files changed (134) hide show
  1. package/LICENSE.md +21 -21
  2. package/README.md +66 -66
  3. package/lib/CHANGELOG.md +774 -774
  4. package/lib/README.md +66 -66
  5. package/lib/cjs/application/application-factory.js +6 -0
  6. package/lib/cjs/application/bootstrap.js +117 -213
  7. package/lib/cjs/config/define-config.js +1 -1
  8. package/lib/cjs/config/env-field-builders.js +47 -0
  9. package/lib/cjs/config/index.js +7 -1
  10. package/lib/cjs/framework-version.js +10 -0
  11. package/lib/cjs/lazy-loading/index.js +5 -1
  12. package/lib/cjs/lazy-loading/lazy-module-helpers.js +49 -0
  13. package/lib/cjs/middleware/index.js +8 -9
  14. package/lib/cjs/middleware/middleware-service.js +68 -12
  15. package/lib/cjs/middleware/presets-standalone.js +93 -0
  16. package/lib/cjs/provider/db-in-memory/adapter/in-memory.adapter.js +23 -0
  17. package/lib/cjs/provider/db-in-memory/index.js +11 -1
  18. package/lib/cjs/provider/db-in-memory/query/query-engine.js +28 -0
  19. package/lib/cjs/provider/db-in-memory/schema/decorators.js +18 -0
  20. package/lib/cjs/provider/db-in-memory/storage/index.js +3 -1
  21. package/lib/cjs/provider/db-in-memory/storage/memory-store.js +72 -1
  22. package/lib/cjs/provider/logger/logger.banner.js +40 -31
  23. package/lib/cjs/provider/logger/logger.config.js +11 -1
  24. package/lib/cjs/provider/logger/logger.formatter.js +22 -1
  25. package/lib/cjs/provider/logger/logger.provider.js +59 -9
  26. package/lib/cjs/provider/logger/transports/console.transport.js +69 -6
  27. package/lib/cjs/provider/logger/transports/file.transport.js +27 -18
  28. package/lib/cjs/provider/logger/utils/log-levels.js +6 -5
  29. package/lib/cjs/provider/validation/adapters/index.js +12 -5
  30. package/lib/cjs/provider/validation/adapters/yup.adapter.js +118 -0
  31. package/lib/cjs/provider/validation/adapters/zod.adapter.js +137 -0
  32. package/lib/cjs/provider/validation/index.js +5 -1
  33. package/lib/cjs/render/adapters/react-adapter.js +14 -14
  34. package/lib/cjs/render/features/type-generator.js +30 -30
  35. package/lib/cjs/render/features/view-debugger.js +75 -55
  36. package/lib/cjs/testing/fluent-request.js +7 -0
  37. package/lib/cjs/testing/snapshot-request.js +2 -0
  38. package/lib/cjs/types/application/application-factory.d.ts +6 -0
  39. package/lib/cjs/types/application/bootstrap.d.ts +196 -24
  40. package/lib/cjs/types/config/config.interfaces.d.ts +7 -1
  41. package/lib/cjs/types/config/env-field-builders.d.ts +39 -0
  42. package/lib/cjs/types/config/index.d.ts +1 -1
  43. package/lib/cjs/types/framework-version.d.ts +7 -0
  44. package/lib/cjs/types/lazy-loading/index.d.ts +1 -0
  45. package/lib/cjs/types/lazy-loading/lazy-module-helpers.d.ts +42 -0
  46. package/lib/cjs/types/middleware/index.d.ts +1 -1
  47. package/lib/cjs/types/middleware/middleware-service.d.ts +21 -0
  48. package/lib/cjs/types/middleware/presets-standalone.d.ts +75 -0
  49. package/lib/cjs/types/provider/db-in-memory/adapter/in-memory.adapter.d.ts +2 -0
  50. package/lib/cjs/types/provider/db-in-memory/index.d.ts +9 -1
  51. package/lib/cjs/types/provider/db-in-memory/query/query-engine.d.ts +14 -1
  52. package/lib/cjs/types/provider/db-in-memory/schema/decorators.d.ts +14 -0
  53. package/lib/cjs/types/provider/db-in-memory/storage/index.d.ts +1 -1
  54. package/lib/cjs/types/provider/db-in-memory/storage/memory-store.d.ts +34 -0
  55. package/lib/cjs/types/provider/logger/logger.banner.d.ts +1 -1
  56. package/lib/cjs/types/provider/logger/logger.config.d.ts +7 -0
  57. package/lib/cjs/types/provider/logger/logger.formatter.d.ts +10 -0
  58. package/lib/cjs/types/provider/logger/logger.provider.d.ts +32 -1
  59. package/lib/cjs/types/provider/logger/transports/console.transport.d.ts +7 -0
  60. package/lib/cjs/types/provider/logger/utils/log-levels.d.ts +3 -3
  61. package/lib/cjs/types/provider/validation/adapters/index.d.ts +7 -4
  62. package/lib/cjs/types/provider/validation/adapters/yup.adapter.d.ts +65 -0
  63. package/lib/cjs/types/provider/validation/adapters/zod.adapter.d.ts +84 -0
  64. package/lib/cjs/types/provider/validation/index.d.ts +1 -1
  65. package/lib/cjs/types/render/features/view-debugger.d.ts +10 -0
  66. package/lib/cjs/types/testing/testing.interfaces.d.ts +31 -6
  67. package/lib/esm/application/application-factory.js +6 -0
  68. package/lib/esm/application/bootstrap.js +117 -213
  69. package/lib/esm/config/define-config.js +1 -1
  70. package/lib/esm/config/env-field-builders.js +48 -0
  71. package/lib/esm/config/index.js +6 -1
  72. package/lib/esm/framework-version.js +7 -0
  73. package/lib/esm/lazy-loading/index.js +2 -0
  74. package/lib/esm/lazy-loading/lazy-module-helpers.js +45 -0
  75. package/lib/esm/middleware/index.js +3 -2
  76. package/lib/esm/middleware/middleware-service.js +68 -12
  77. package/lib/esm/middleware/presets-standalone.js +87 -0
  78. package/lib/esm/provider/db-in-memory/adapter/in-memory.adapter.js +23 -0
  79. package/lib/esm/provider/db-in-memory/index.js +9 -1
  80. package/lib/esm/provider/db-in-memory/query/query-engine.js +28 -0
  81. package/lib/esm/provider/db-in-memory/schema/decorators.js +18 -0
  82. package/lib/esm/provider/db-in-memory/storage/index.js +1 -1
  83. package/lib/esm/provider/db-in-memory/storage/memory-store.js +75 -0
  84. package/lib/esm/provider/logger/logger.banner.js +40 -31
  85. package/lib/esm/provider/logger/logger.config.js +12 -2
  86. package/lib/esm/provider/logger/logger.formatter.js +22 -1
  87. package/lib/esm/provider/logger/logger.provider.js +61 -10
  88. package/lib/esm/provider/logger/transports/console.transport.js +69 -6
  89. package/lib/esm/provider/logger/transports/file.transport.js +27 -18
  90. package/lib/esm/provider/logger/utils/log-levels.js +6 -5
  91. package/lib/esm/provider/validation/adapters/index.js +7 -4
  92. package/lib/esm/provider/validation/adapters/yup.adapter.js +111 -0
  93. package/lib/esm/provider/validation/adapters/zod.adapter.js +130 -0
  94. package/lib/esm/provider/validation/index.js +1 -1
  95. package/lib/esm/render/adapters/react-adapter.js +14 -14
  96. package/lib/esm/render/features/type-generator.js +30 -30
  97. package/lib/esm/render/features/view-debugger.js +75 -55
  98. package/lib/esm/testing/fluent-request.js +7 -0
  99. package/lib/esm/testing/snapshot-request.js +2 -0
  100. package/lib/esm/types/application/application-factory.d.ts +6 -0
  101. package/lib/esm/types/application/bootstrap.d.ts +196 -24
  102. package/lib/esm/types/config/config.interfaces.d.ts +7 -1
  103. package/lib/esm/types/config/env-field-builders.d.ts +39 -0
  104. package/lib/esm/types/config/index.d.ts +1 -1
  105. package/lib/esm/types/framework-version.d.ts +7 -0
  106. package/lib/esm/types/lazy-loading/index.d.ts +1 -0
  107. package/lib/esm/types/lazy-loading/lazy-module-helpers.d.ts +42 -0
  108. package/lib/esm/types/middleware/index.d.ts +1 -1
  109. package/lib/esm/types/middleware/middleware-service.d.ts +21 -0
  110. package/lib/esm/types/middleware/presets-standalone.d.ts +75 -0
  111. package/lib/esm/types/provider/db-in-memory/adapter/in-memory.adapter.d.ts +2 -0
  112. package/lib/esm/types/provider/db-in-memory/index.d.ts +9 -1
  113. package/lib/esm/types/provider/db-in-memory/query/query-engine.d.ts +14 -1
  114. package/lib/esm/types/provider/db-in-memory/schema/decorators.d.ts +14 -0
  115. package/lib/esm/types/provider/db-in-memory/storage/index.d.ts +1 -1
  116. package/lib/esm/types/provider/db-in-memory/storage/memory-store.d.ts +34 -0
  117. package/lib/esm/types/provider/logger/logger.banner.d.ts +1 -1
  118. package/lib/esm/types/provider/logger/logger.config.d.ts +7 -0
  119. package/lib/esm/types/provider/logger/logger.formatter.d.ts +10 -0
  120. package/lib/esm/types/provider/logger/logger.provider.d.ts +32 -1
  121. package/lib/esm/types/provider/logger/transports/console.transport.d.ts +7 -0
  122. package/lib/esm/types/provider/logger/utils/log-levels.d.ts +3 -3
  123. package/lib/esm/types/provider/validation/adapters/index.d.ts +7 -4
  124. package/lib/esm/types/provider/validation/adapters/yup.adapter.d.ts +65 -0
  125. package/lib/esm/types/provider/validation/adapters/zod.adapter.d.ts +84 -0
  126. package/lib/esm/types/provider/validation/index.d.ts +1 -1
  127. package/lib/esm/types/render/features/view-debugger.d.ts +10 -0
  128. package/lib/esm/types/testing/testing.interfaces.d.ts +31 -6
  129. package/lib/package.json +23 -8
  130. package/package.json +23 -8
  131. package/lib/cjs/middleware/middleware-presets.js +0 -294
  132. package/lib/cjs/types/middleware/middleware-presets.d.ts +0 -90
  133. package/lib/esm/middleware/middleware-presets.js +0 -286
  134. package/lib/esm/types/middleware/middleware-presets.d.ts +0 -90
package/lib/README.md CHANGED
@@ -1,66 +1,66 @@
1
- <div align="center">
2
- <a href="https://expresso-ts.com">
3
- <img src="https://github.com/expressots/expressots/blob/main/media/expressots.png" alt="ExpressoTS" width="120">
4
- </a>
5
-
6
- <h1>@expressots/core</h1>
7
-
8
- <p>A TypeScript framework for building server-side applications — fast to code, lean to run, simple to deploy.</p>
9
-
10
- <p>
11
- <a href="https://www.npmjs.com/package/@expressots/core"><img src="https://img.shields.io/npm/v/@expressots/core?style=flat&color=0d0d0d" alt="npm"></a>
12
- <a href="https://github.com/expressots/expressots/blob/main/LICENSE.md"><img src="https://img.shields.io/github/license/expressots/expressots?style=flat&color=0d0d0d" alt="License"></a>
13
- <a href="https://discord.com/invite/PyPJfGK"><img src="https://img.shields.io/badge/Discord-join-0d0d0d?logo=discord&logoColor=white" alt="Discord"></a>
14
- </p>
15
-
16
- <p>
17
- <a href="https://doc.expresso-ts.com">Documentation</a> ·
18
- <a href="https://doc.expresso-ts.com/docs/core/first-steps">Getting Started</a> ·
19
- <a href="https://discord.com/invite/PyPJfGK">Community</a>
20
- </p>
21
- </div>
22
-
23
- ---
24
-
25
- ## Install
26
-
27
- ```bash
28
- npm i @expressots/core
29
- ```
30
-
31
- ## What This Package Does
32
-
33
- ExpressoTS Core is the foundation of the ExpressoTS framework. It provides dependency injection, routing, middleware orchestration, lifecycle hooks, interceptors, guards, error handling, and the application bootstrap — everything needed to structure and run a server-side TypeScript application with minimal boilerplate.
34
-
35
- ## Quick Look
36
-
37
- ```typescript
38
- // app.provider.ts
39
- import { AppFactory } from "@expressots/core";
40
- import { App } from "./app";
41
-
42
- async function bootstrap() {
43
- const app = await AppFactory.create(App);
44
- await app.listen(3000, "development");
45
- }
46
-
47
- bootstrap();
48
- ```
49
-
50
- ## Documentation
51
-
52
- For guides, API reference, architecture patterns, and examples visit **[doc.expresso-ts.com](https://doc.expresso-ts.com)**.
53
-
54
- ## Contributing
55
-
56
- See the [Contributing Guide](https://github.com/expressots/expressots/blob/main/CONTRIBUTING.md) for how to get involved.
57
-
58
- ## Support
59
-
60
- - [GitHub Sponsors](https://github.com/sponsors/expressots)
61
- - [Discord](https://discord.com/invite/PyPJfGK)
62
- - [Report an Issue](https://github.com/expressots/expressots/issues)
63
-
64
- ## License
65
-
66
- MIT — see [LICENSE](./LICENSE.md).
1
+ <div align="center">
2
+ <a href="https://expresso-ts.com">
3
+ <img src="https://github.com/expressots/expressots/blob/main/media/expressots.png" alt="ExpressoTS" width="120">
4
+ </a>
5
+
6
+ <h1>@expressots/core</h1>
7
+
8
+ <p>A TypeScript framework for building server-side applications — fast to code, lean to run, simple to deploy.</p>
9
+
10
+ <p>
11
+ <a href="https://www.npmjs.com/package/@expressots/core"><img src="https://img.shields.io/npm/v/@expressots/core?style=flat&color=0d0d0d" alt="npm"></a>
12
+ <a href="https://github.com/expressots/expressots/blob/main/LICENSE.md"><img src="https://img.shields.io/github/license/expressots/expressots?style=flat&color=0d0d0d" alt="License"></a>
13
+ <a href="https://discord.com/invite/PyPJfGK"><img src="https://img.shields.io/badge/Discord-join-0d0d0d?logo=discord&logoColor=white" alt="Discord"></a>
14
+ </p>
15
+
16
+ <p>
17
+ <a href="https://doc.expresso-ts.com">Documentation</a> ·
18
+ <a href="https://doc.expresso-ts.com/docs/core/first-steps">Getting Started</a> ·
19
+ <a href="https://discord.com/invite/PyPJfGK">Community</a>
20
+ </p>
21
+ </div>
22
+
23
+ ---
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ npm i @expressots/core
29
+ ```
30
+
31
+ ## What This Package Does
32
+
33
+ ExpressoTS Core is the foundation of the ExpressoTS framework. It provides dependency injection, routing, middleware orchestration, lifecycle hooks, interceptors, guards, error handling, and the application bootstrap — everything needed to structure and run a server-side TypeScript application with minimal boilerplate.
34
+
35
+ ## Quick Look
36
+
37
+ ```typescript
38
+ // app.provider.ts
39
+ import { AppFactory } from "@expressots/core";
40
+ import { App } from "./app";
41
+
42
+ async function bootstrap() {
43
+ const app = await AppFactory.create(App);
44
+ await app.listen(3000, "development");
45
+ }
46
+
47
+ bootstrap();
48
+ ```
49
+
50
+ ## Documentation
51
+
52
+ For guides, API reference, architecture patterns, and examples visit **[doc.expresso-ts.com](https://doc.expresso-ts.com)**.
53
+
54
+ ## Contributing
55
+
56
+ See the [Contributing Guide](https://github.com/expressots/expressots/blob/main/CONTRIBUTING.md) for how to get involved.
57
+
58
+ ## Support
59
+
60
+ - [GitHub Sponsors](https://github.com/sponsors/expressots)
61
+ - [Discord](https://discord.com/invite/PyPJfGK)
62
+ - [Report an Issue](https://github.com/expressots/expressots/issues)
63
+
64
+ ## License
65
+
66
+ MIT — see [LICENSE](./LICENSE.md).
@@ -122,6 +122,12 @@ class AppFactory {
122
122
  *
123
123
  * @throws {Error} If webServerType is not a valid constructor
124
124
  *
125
+ * @deprecated Use `bootstrap()` from `@expressots/core` instead. `bootstrap()`
126
+ * additionally handles environment file loading, port configuration,
127
+ * graceful shutdown, the startup banner, and configuration validation.
128
+ * `AppFactory.create()` will keep working for advanced use cases but is
129
+ * scheduled for removal in a future major release.
130
+ *
125
131
  * @public API
126
132
  */
127
133
  static async create(webServerType) {
@@ -11,35 +11,75 @@ const logger_provider_js_1 = require("../provider/logger/logger.provider.js");
11
11
  const fs_1 = __importDefault(require("fs"));
12
12
  const path_1 = __importDefault(require("path"));
13
13
  /**
14
- * Synchronously load .env files BEFORE defineConfig() resolves.
14
+ * Synchronously load `.env` files into `process.env`.
15
15
  *
16
- * Call this at the top of your config file to ensure environment variables
17
- * are available when defineConfig() runs.
16
+ * Call this **before** `bootstrap()` or `defineConfig()` so that every
17
+ * environment variable is available when the rest of the application
18
+ * resolves configuration, binds services, or reads `Env.*` builders.
18
19
  *
19
- * @param options - Environment file configuration
20
+ * ### How it works
20
21
  *
21
- * @example
22
+ * 1. Reads `process.env.NODE_ENV` (defaults to `"development"`).
23
+ * 2. Loads files in priority order (last file wins on conflicts):
24
+ * `.env` → `.env.local` → `.env.{env}.local` → `.env.{env}`
25
+ * 3. If `options.files` maps the current environment to a custom name
26
+ * (e.g. `{ production: ".env.prod" }`), that name replaces `.env.{env}`.
27
+ * 4. Missing files are silently skipped.
28
+ * 5. Sets `process.env._EXPRESSOTS_ENV_LOADED = "true"` so subsequent
29
+ * calls are no-ops (unless `force: true`).
30
+ *
31
+ * ### Important
32
+ *
33
+ * `NODE_ENV` must already be set in the **shell** (or inherited from
34
+ * the process) before `loadEnvSync()` runs. The function reads
35
+ * `process.env.NODE_ENV` to decide which file to load; it does **not**
36
+ * derive the environment from file contents.
37
+ *
38
+ * @param options - See {@link LoadEnvSyncOptions} for all fields.
39
+ *
40
+ * @example Zero-config (recommended for most apps)
22
41
  * ```typescript
23
- * // config.ts
24
- * import { defineConfig, Env, loadEnvSync } from "@expressots/core";
42
+ * // src/main.ts
43
+ * import { bootstrap, loadEnvSync } from "@expressots/core";
44
+ * import { App } from "./app";
45
+ *
46
+ * loadEnvSync(); // .env + .env.development (or .env.production, etc.)
47
+ * void bootstrap(App);
48
+ * ```
25
49
  *
26
- * // Load .env files first
50
+ * @example Custom file mapping
51
+ * ```typescript
27
52
  * loadEnvSync({
28
53
  * files: {
29
- * development: ".env.dev",
30
- * production: ".env.prod",
54
+ * development: ".env",
55
+ * production: ".env.prod",
56
+ * staging: ".env.staging",
31
57
  * },
32
58
  * });
59
+ * // With NODE_ENV=production -> .env + .env.prod
60
+ * // With NODE_ENV=development -> .env (mapped to itself, so loaded once)
61
+ * ```
62
+ *
63
+ * @example With defineConfig()
64
+ * ```typescript
65
+ * // src/config.ts
66
+ * import { defineConfig, Env, loadEnvSync } from "@expressots/core";
67
+ *
68
+ * loadEnvSync({ files: { production: ".env.prod" } });
33
69
  *
34
- * // Now defineConfig() will read from loaded .env files
35
70
  * export const appConfig = defineConfig({
36
- * server: {
37
- * port: Env.port("PORT", { default: 3000 }),
38
- * },
71
+ * server: { port: Env.port("PORT", { default: 3000 }) },
39
72
  * });
40
73
  * ```
41
74
  *
42
- * @public API
75
+ * @example Force reload (hot-module replacement)
76
+ * ```typescript
77
+ * loadEnvSync({ force: true });
78
+ * ```
79
+ *
80
+ * @see {@link LoadEnvSyncOptions} for option details
81
+ * @see {@link BootstrapOptions.envFileConfig} for the bootstrap()-level alternative
82
+ * @public
43
83
  */
44
84
  function loadEnvSync(options) {
45
85
  // Skip if already loaded (unless force reload)
@@ -102,26 +142,26 @@ const CI_PLATFORM_MAP = new Map([
102
142
  const PLATFORM_HINTS = new Map([
103
143
  [
104
144
  "GitHub Actions",
105
- (missingVars) => `
106
- 🔧 GitHub Actions Setup:
107
- - Go to: Settings → Secrets and variables → Actions
108
- - Add repository secrets for: ${missingVars}
145
+ (missingVars) => `
146
+ 🔧 GitHub Actions Setup:
147
+ - Go to: Settings → Secrets and variables → Actions
148
+ - Add repository secrets for: ${missingVars}
109
149
  - Use: \${{ secrets.VARIABLE_NAME }} in workflow files`,
110
150
  ],
111
151
  [
112
152
  "GitLab CI",
113
- (missingVars) => `
114
- 🔧 GitLab CI Setup:
115
- - Go to: Settings → CI/CD → Variables
116
- - Add CI/CD variables for: ${missingVars}
153
+ (missingVars) => `
154
+ 🔧 GitLab CI Setup:
155
+ - Go to: Settings → CI/CD → Variables
156
+ - Add CI/CD variables for: ${missingVars}
117
157
  - Use: $VARIABLE_NAME in .gitlab-ci.yml`,
118
158
  ],
119
159
  [
120
160
  "Jenkins",
121
- (missingVars) => `
122
- 🔧 Jenkins Setup:
123
- - Configure: Manage Jenkins → Credentials
124
- - Add credentials for: ${missingVars}
161
+ (missingVars) => `
162
+ 🔧 Jenkins Setup:
163
+ - Configure: Manage Jenkins → Credentials
164
+ - Add credentials for: ${missingVars}
125
165
  - Use: env.VARIABLE_NAME in pipeline`,
126
166
  ],
127
167
  ]);
@@ -186,14 +226,14 @@ function getPlatformHint(platform, missing) {
186
226
  class EnvFileNotFoundError extends Error {
187
227
  constructor(fileName, environment) {
188
228
  const template = getEnvTemplate(environment);
189
- super(`
190
- ❌ Missing required environment file: ${fileName}
191
-
192
- 💡 Create ${fileName} with:
193
- ${template}
194
-
195
- 📖 Docs: https://expresso-ts.com/docs/env
196
- 🔍 Check existing files: ls -la .env*
229
+ super(`
230
+ ❌ Missing required environment file: ${fileName}
231
+
232
+ 💡 Create ${fileName} with:
233
+ ${template}
234
+
235
+ 📖 Docs: https://expresso-ts.com/docs/env
236
+ 🔍 Check existing files: ls -la .env*
197
237
  `.trim());
198
238
  this.name = "EnvFileNotFoundError";
199
239
  }
@@ -206,20 +246,20 @@ class CIEnvValidationError extends Error {
206
246
  constructor(missing, environment) {
207
247
  const ciPlatform = detectCIPlatform();
208
248
  const platformHint = getPlatformHint(ciPlatform, missing);
209
- super(`
210
- ❌ CI/CD Environment Validation Failed
211
-
212
- Missing required environment variables in ${environment}:
213
- ${missing.map((key) => ` • ${key}`).join("\n")}
214
-
215
- ${platformHint}
216
-
217
- 💡 Action Required:
218
- 1. Add missing variables to your CI/CD platform secrets
219
- 2. Ensure variables are available in ${environment} environment
220
- 3. Check variable names match exactly (case-sensitive)
221
-
222
- 📖 Docs: https://expresso-ts.com/docs/ci-cd
249
+ super(`
250
+ ❌ CI/CD Environment Validation Failed
251
+
252
+ Missing required environment variables in ${environment}:
253
+ ${missing.map((key) => ` • ${key}`).join("\n")}
254
+
255
+ ${platformHint}
256
+
257
+ 💡 Action Required:
258
+ 1. Add missing variables to your CI/CD platform secrets
259
+ 2. Ensure variables are available in ${environment} environment
260
+ 3. Check variable names match exactly (case-sensitive)
261
+
262
+ 📖 Docs: https://expresso-ts.com/docs/ci-cd
223
263
  `.trim());
224
264
  this.name = "CIEnvValidationError";
225
265
  }
@@ -230,16 +270,16 @@ ${platformHint}
230
270
  */
231
271
  class EnvValidationError extends Error {
232
272
  constructor(missing, fileName) {
233
- super(`
234
- ❌ Environment validation failed
235
-
236
- Missing values in ${fileName}:
237
- ${missing.map((key) => ` • ${key} (required but empty)`).join("\n")}
238
-
239
- 💡 Add values to ${fileName}:
240
- ${missing.map((key) => ` ${key}=your-value-here`).join("\n")}
241
-
242
- 📖 Docs: https://expresso-ts.com/docs/env
273
+ super(`
274
+ ❌ Environment validation failed
275
+
276
+ Missing values in ${fileName}:
277
+ ${missing.map((key) => ` • ${key} (required but empty)`).join("\n")}
278
+
279
+ 💡 Add values to ${fileName}:
280
+ ${missing.map((key) => ` ${key}=your-value-here`).join("\n")}
281
+
282
+ 📖 Docs: https://expresso-ts.com/docs/env
243
283
  `.trim());
244
284
  this.name = "EnvValidationError";
245
285
  }
@@ -251,8 +291,8 @@ ${missing.map((key) => ` ${key}=your-value-here`).join("\n")}
251
291
  * @private
252
292
  */
253
293
  function getEnvTemplate(environment) {
254
- return `PORT=3000
255
- NODE_ENV=${environment}
294
+ return `PORT=3000
295
+ NODE_ENV=${environment}
256
296
  # Add your environment variables here`;
257
297
  }
258
298
  /**
@@ -417,11 +457,11 @@ async function loadAndValidateEnvironment(currentEnvironment, envFileConfig) {
417
457
  // 🎯 STEP 2: Determine and load the file for the current environment
418
458
  // Only ONE file is loaded at runtime - the file for the current environment
419
459
  const envFileName = envFileConfig.files?.[currentEnvironment] ?? `.env.${currentEnvironment}`;
420
- // Load optional files silently
460
+ // Load optional files silently (override: true so later files win)
421
461
  const optionalFiles = [".env", ".env.local", `${envFileName}.local`];
422
462
  for (const file of optionalFiles) {
423
463
  try {
424
- (0, shared_1.config)({ path: file });
464
+ (0, shared_1.config)({ path: file, override: true });
425
465
  result.loaded.push(file);
426
466
  }
427
467
  catch {
@@ -464,8 +504,8 @@ async function loadAndValidateEnvironment(currentEnvironment, envFileConfig) {
464
504
  }
465
505
  }
466
506
  else {
467
- // Load the file
468
- (0, shared_1.config)({ path: envFileName });
507
+ // Load the environment-specific file (highest priority, overrides all previous)
508
+ (0, shared_1.config)({ path: envFileName, override: true });
469
509
  result.loaded.push(envFileName);
470
510
  }
471
511
  // Validate variables have values
@@ -529,151 +569,6 @@ async function readPackageJson() {
529
569
  return _packageCache;
530
570
  }
531
571
  }
532
- /**
533
- * Bootstrap the ExpressoTS application with zero configuration.
534
- *
535
- * @layer public
536
- * @audience application-developers
537
- * @concept bootstrap
538
- * @difficulty beginner
539
- *
540
- * @summary Quick Start
541
- * The simplest way to start your application:
542
- * ```typescript
543
- * await bootstrap(App);
544
- * ```
545
- *
546
- * This function orchestrates 8 critical startup phases:
547
- * 1. Environment detection (CI/CD vs local)
548
- * 2. Smart .env loading with opt-in behavior
549
- * 3. Port determination (priority chain)
550
- * 4. Package.json metadata extraction
551
- * 5. DI container initialization via AppFactory
552
- * 6. Environment injection into app instance
553
- * 7. API version detection from decorators
554
- * 8. Server startup with graceful shutdown
555
- *
556
- * @param AppClass - Application class extending AppExpress
557
- * @param options - Optional bootstrap configuration
558
- * @returns Promise resolving to IWebServerPublic instance
559
- *
560
- * @example
561
- * ```typescript
562
- * // Simplest usage - zero config (no .env file loading)
563
- * await bootstrap(App);
564
- *
565
- * // With overrides (still no .env file loading)
566
- * await bootstrap(App, {
567
- * port: 4000,
568
- * appName: "My API",
569
- * appVersion: "2.0.0"
570
- * });
571
- *
572
- * // Opt-in to .env file loading and auto-creation
573
- * await bootstrap(App, {
574
- * currentEnvironment: "development",
575
- * envFileConfig: {
576
- * files: {
577
- * development: ".env.dev",
578
- * production: ".env.prod"
579
- * },
580
- * required: ["DATABASE_URL", "API_KEY"],
581
- * autoCreateTemplate: true, // Explicitly enable file creation
582
- * validateValues: true
583
- * }
584
- * });
585
- *
586
- * // Auto-assign port (useful for testing)
587
- * await bootstrap(App, { port: 0 });
588
- * ```
589
- *
590
- * @layer internal
591
- * @audience framework-developers
592
- *
593
- * **Internal Architecture**
594
- *
595
- * Bootstrap orchestrates 8 critical steps:
596
- * 1. Environment detection (CI/CD vs local)
597
- * 2. Smart .env loading with opt-in behavior
598
- * 3. Port determination (priority chain)
599
- * 4. Package.json metadata extraction
600
- * 5. DI container initialization via AppFactory
601
- * 6. Environment injection into app instance
602
- * 7. API version detection from decorators
603
- * 8. Server startup with graceful shutdown
604
- *
605
- * **Design Decisions**
606
- * - Opt-in .env loading prevents breaking changes for containerized deployments
607
- * - Port 0 support enables parallel testing without conflicts
608
- * - Early validation fails fast with actionable error messages
609
- * - CI/CD auto-detection provides zero-config for containerized environments
610
- *
611
- * **Performance Characteristics**
612
- * - Startup time: ~8-25ms typical (optimized)
613
- * - Environment loading: ~2-5ms (file I/O)
614
- * - Package.json read: ~1-2ms first call, cached thereafter
615
- * - CI detection: cached after first call
616
- * - App instantiation: ~5-10ms (DI container setup)
617
- * - Logger instances: lazy initialization (only created when needed)
618
- *
619
- * @see {@link loadAndValidateEnvironment} for environment loading logic
620
- * @see {@link AppFactory.create} for DI container initialization
621
- * @see {@link determinePort} for port resolution logic
622
- *
623
- * @layer advanced
624
- * @audience power-users
625
- *
626
- * **Advanced Patterns**
627
- *
628
- * Multi-environment setup with validation:
629
- * ```typescript
630
- * await bootstrap(App, {
631
- * currentEnvironment: process.env.NODE_ENV || "development",
632
- * envFileConfig: {
633
- * files: {
634
- * development: ".env.dev",
635
- * staging: ".env.staging",
636
- * production: ".env.prod"
637
- * },
638
- * required: ["DATABASE_URL", "JWT_SECRET"],
639
- * autoCreateTemplate: true,
640
- * validateValues: process.env.NODE_ENV === "production"
641
- * }
642
- * });
643
- * ```
644
- *
645
- * Containerized deployment (Docker/K8s):
646
- * ```typescript
647
- * await bootstrap(App, {
648
- * envFileConfig: {
649
- * skipFileLoading: true, // Use process.env only
650
- * required: ["DATABASE_URL", "REDIS_URL"]
651
- * }
652
- * });
653
- * ```
654
- *
655
- * Testing with dynamic ports:
656
- * ```typescript
657
- * const server = await bootstrap(App, { port: 0 });
658
- * const actualPort = server.port; // Use in tests
659
- * ```
660
- *
661
- * @troubleshooting
662
- * **Common Issues**
663
- * - ❌ PORT not detected → Use options.port or envFileConfig
664
- * - ❌ CI validation fails → Check platform secrets configuration
665
- * - ❌ Template not created → Set autoCreateTemplate: true explicitly
666
- * - ❌ Port already in use → Use port: 0 for testing
667
- *
668
- * @performance
669
- * - Async initialization: ~5-15ms (typical)
670
- * - Package.json read: ~2ms first call, cached thereafter
671
- * - CI detection: cached after first call
672
- * - Port binding: varies by OS
673
- * - Total startup: ~8-25ms (optimized with caching)
674
- *
675
- * @public API
676
- */
677
572
  /**
678
573
  * Type guard to check if argument is BootstrapConfig.
679
574
  * Detects config objects by checking for the required structure.
@@ -714,9 +609,7 @@ function transformConfigToOptions(config) {
714
609
  envFileConfig: config.bootstrap?.envFileConfig,
715
610
  };
716
611
  }
717
- /**
718
- * Implementation.
719
- */
612
+ /** @internal Implementation overload. */
720
613
  async function bootstrap(AppClass, optionsOrConfig) {
721
614
  let logger;
722
615
  // Note: Path resolution is auto-initialized as a side effect when
@@ -776,6 +669,17 @@ async function bootstrap(AppClass, optionsOrConfig) {
776
669
  // STEP 5: Create app instance
777
670
  // App's globalConfiguration() will run in constructor
778
671
  // initEnvironment() can skip if .env already loaded
672
+ //
673
+ // Activate log buffering BEFORE constructing the app so any logs the
674
+ // application emits during construction (e.g. inside `globalConfiguration()`
675
+ // or `configureServices()`) are captured and replayed in the right order
676
+ // after the startup banner. AppClass extends AppExpress for the standard
677
+ // adapter, so the static method is inherited; we duck-type to avoid a
678
+ // hard dependency on adapter-express from core.
679
+ const appClassWithBuffering = AppClass;
680
+ if (typeof appClassWithBuffering.startLogBuffering === "function") {
681
+ appClassWithBuffering.startLogBuffering();
682
+ }
779
683
  const app = await application_factory_js_1.AppFactory.create(AppClass);
780
684
  // Set environment on app instance (for this.environment access)
781
685
  app.environment =
@@ -122,7 +122,7 @@ class ConfigInstance {
122
122
  this._options = {
123
123
  validateOnAccess: true,
124
124
  throwOnError: process.env.NODE_ENV === "production",
125
- logLevel: "info",
125
+ logLevel: "warn",
126
126
  ...options,
127
127
  };
128
128
  // Lazy resolution: Don't resolve immediately
@@ -28,6 +28,8 @@
28
28
  */
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
30
  exports.Env = void 0;
31
+ exports.envIs = isEnvironment;
32
+ exports.envWhen = envWhen;
31
33
  exports.envString = string;
32
34
  exports.envNumber = number;
33
35
  exports.envBoolean = boolean;
@@ -389,6 +391,49 @@ function array(envVar, options = {}) {
389
391
  *
390
392
  * @public API
391
393
  */
394
+ /**
395
+ * Returns true when the current Node environment (`NODE_ENV`) matches the
396
+ * supplied name. Pass an array to match any of several names. The comparison
397
+ * is case-insensitive and falls back to `"development"` when `NODE_ENV` is
398
+ * unset, mirroring the convention used elsewhere in the framework.
399
+ *
400
+ * ```ts
401
+ * const config = defineConfig({
402
+ * server: { port: when(Env.is("production"), 443, 3000) },
403
+ * });
404
+ * ```
405
+ *
406
+ * @public API
407
+ */
408
+ function isEnvironment(name) {
409
+ const current = (process.env.NODE_ENV ?? "development").toLowerCase();
410
+ const targets = Array.isArray(name) ? name : [name];
411
+ return targets.some((target) => target.toLowerCase() === current);
412
+ }
413
+ /**
414
+ * Conditional helper for environment-specific config values.
415
+ *
416
+ * `when(condition, value, fallback)` returns `value` when `condition` is
417
+ * truthy, otherwise `fallback`. The condition can be either a boolean
418
+ * (typically the result of `Env.is(...)`) or a callable that's evaluated
419
+ * lazily. The latter form lets you defer side-effectful checks until the
420
+ * config is actually resolved.
421
+ *
422
+ * ```ts
423
+ * const config = defineConfig({
424
+ * logging: {
425
+ * level: when(Env.is("production"), "info", "debug"),
426
+ * pretty: when(() => process.env.NO_COLOR !== "1", true, false),
427
+ * },
428
+ * });
429
+ * ```
430
+ *
431
+ * @public API
432
+ */
433
+ function envWhen(condition, value, fallback) {
434
+ const ok = typeof condition === "function" ? condition() : condition;
435
+ return ok ? value : fallback;
436
+ }
392
437
  exports.Env = {
393
438
  string,
394
439
  number,
@@ -399,4 +444,6 @@ exports.Env = {
399
444
  secret,
400
445
  json,
401
446
  array,
447
+ is: isEnvironment,
448
+ when: envWhen,
402
449
  };
@@ -57,7 +57,7 @@
57
57
  * ```
58
58
  */
59
59
  Object.defineProperty(exports, "__esModule", { value: true });
60
- exports.isSecretValue = exports.createSecretValue = exports.envArray = exports.envJson = exports.envSecret = exports.envPort = exports.envUrl = exports.envEnum = exports.envBoolean = exports.envNumber = exports.envString = exports.Env = exports.defineConfig = void 0;
60
+ exports.isSecretValue = exports.createSecretValue = exports.envWhen = exports.envIs = exports.envArray = exports.envJson = exports.envSecret = exports.envPort = exports.envUrl = exports.envEnum = exports.envBoolean = exports.envNumber = exports.envString = exports.Env = exports.defineConfig = void 0;
61
61
  // ============================================================================
62
62
  // Core Exports
63
63
  // ============================================================================
@@ -75,6 +75,12 @@ Object.defineProperty(exports, "envPort", { enumerable: true, get: function () {
75
75
  Object.defineProperty(exports, "envSecret", { enumerable: true, get: function () { return env_field_builders_js_1.envSecret; } });
76
76
  Object.defineProperty(exports, "envJson", { enumerable: true, get: function () { return env_field_builders_js_1.envJson; } });
77
77
  Object.defineProperty(exports, "envArray", { enumerable: true, get: function () { return env_field_builders_js_1.envArray; } });
78
+ // Environment-aware value selection helpers.
79
+ // The canonical user-facing API for `when` is `Env.when(...)` to avoid
80
+ // colliding with the middleware module's `when()` helper. We still expose
81
+ // the underlying functions under disambiguated names for advanced use.
82
+ Object.defineProperty(exports, "envIs", { enumerable: true, get: function () { return env_field_builders_js_1.envIs; } });
83
+ Object.defineProperty(exports, "envWhen", { enumerable: true, get: function () { return env_field_builders_js_1.envWhen; } });
78
84
  // ============================================================================
79
85
  // Secret Value
80
86
  // ============================================================================