@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
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
|
|
14
|
+
* Synchronously load `.env` files into `process.env`.
|
|
15
15
|
*
|
|
16
|
-
* Call this
|
|
17
|
-
*
|
|
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
|
-
*
|
|
20
|
+
* ### How it works
|
|
20
21
|
*
|
|
21
|
-
*
|
|
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
|
-
* //
|
|
24
|
-
* import {
|
|
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
|
-
*
|
|
50
|
+
* @example Custom file mapping
|
|
51
|
+
* ```typescript
|
|
27
52
|
* loadEnvSync({
|
|
28
53
|
* files: {
|
|
29
|
-
* development: ".env
|
|
30
|
-
* production:
|
|
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
|
-
* @
|
|
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 =
|
|
@@ -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
|
};
|
package/lib/cjs/config/index.js
CHANGED
|
@@ -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
|
// ============================================================================
|