@zerooneit/expressive-tea 1.3.0-beta.6 → 2.0.0
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/.swcrc +61 -0
- package/README.md +564 -174
- package/classes/Boot.d.ts +89 -2
- package/classes/Boot.js +149 -31
- package/classes/Engine.d.ts +58 -10
- package/classes/Engine.js +69 -9
- package/classes/EngineRegistry.d.ts +154 -0
- package/classes/EngineRegistry.js +247 -0
- package/classes/LoadBalancer.js +2 -5
- package/classes/ProxyRoute.js +5 -5
- package/classes/Settings.d.ts +31 -2
- package/classes/Settings.js +64 -11
- package/decorators/annotations.d.ts +1 -1
- package/decorators/annotations.js +17 -17
- package/decorators/env.d.ts +145 -0
- package/decorators/env.js +177 -0
- package/decorators/health.d.ts +115 -0
- package/decorators/health.js +124 -0
- package/decorators/module.d.ts +15 -16
- package/decorators/module.js +14 -24
- package/decorators/proxy.d.ts +26 -11
- package/decorators/proxy.js +35 -49
- package/decorators/router.d.ts +17 -16
- package/decorators/router.js +31 -53
- package/decorators/server.d.ts +7 -7
- package/decorators/server.js +48 -50
- package/engines/health/index.d.ts +120 -0
- package/engines/health/index.js +179 -0
- package/engines/http/index.d.ts +6 -10
- package/engines/http/index.js +18 -17
- package/engines/index.d.ts +32 -0
- package/engines/index.js +112 -0
- package/engines/socketio/index.d.ts +2 -4
- package/engines/socketio/index.js +14 -7
- package/engines/teacup/index.d.ts +12 -2
- package/engines/teacup/index.js +56 -10
- package/engines/teapot/index.d.ts +12 -2
- package/engines/teapot/index.js +58 -17
- package/engines/websocket/index.d.ts +1 -1
- package/engines/websocket/index.js +8 -3
- package/eslint.config.mjs +138 -0
- package/exceptions/RequestExceptions.d.ts +3 -3
- package/helpers/boot-helper.d.ts +4 -4
- package/helpers/boot-helper.js +27 -22
- package/helpers/decorators.js +7 -6
- package/helpers/promise-helper.d.ts +1 -1
- package/helpers/promise-helper.js +1 -2
- package/helpers/server.d.ts +31 -5
- package/helpers/server.js +98 -60
- package/helpers/teapot-helper.d.ts +2 -3
- package/helpers/teapot-helper.js +34 -8
- package/helpers/websocket-helper.d.ts +1 -3
- package/helpers/websocket-helper.js +3 -3
- package/interfaces/index.d.ts +1 -1
- package/inversify.config.d.ts +4 -4
- package/inversify.config.js +1 -1
- package/libs/utilities.d.ts +21910 -0
- package/libs/utilities.js +420 -0
- package/mixins/module.d.ts +45 -0
- package/mixins/module.js +71 -0
- package/mixins/proxy.d.ts +46 -0
- package/mixins/proxy.js +86 -0
- package/mixins/route.d.ts +48 -0
- package/mixins/route.js +96 -0
- package/package.json +85 -73
- package/services/DependencyInjection.d.ts +94 -8
- package/services/DependencyInjection.js +121 -3
- package/services/WebsocketService.d.ts +2 -4
- package/services/WebsocketService.js +5 -3
- package/types/core.d.ts +14 -0
- package/types/core.js +2 -0
- package/types/injection-types.d.ts +6 -0
- package/types/injection-types.js +10 -0
- package/types/inversify.d.ts +5 -0
- package/types/inversify.js +3 -0
- package/.eslintrc.js +0 -44
- package/tsconfig.linter.json +0 -24
- package/tslint-to-eslint-config.log +0 -12
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment variable loading options with optional transformation
|
|
3
|
+
* @interface EnvOptions
|
|
4
|
+
* @template T - Type of transformed environment variables
|
|
5
|
+
* @since 2.0.0
|
|
6
|
+
*/
|
|
7
|
+
export interface EnvOptions<T = Record<string, string>> {
|
|
8
|
+
/**
|
|
9
|
+
* Path to the .env file (relative to project root)
|
|
10
|
+
* @default '.env'
|
|
11
|
+
*/
|
|
12
|
+
path?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Whether to override existing environment variables
|
|
15
|
+
* @default false
|
|
16
|
+
*/
|
|
17
|
+
override?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* List of required environment variables (will throw if missing)
|
|
20
|
+
*/
|
|
21
|
+
required?: string[];
|
|
22
|
+
/**
|
|
23
|
+
* Whether to ignore missing .env file
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
silent?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Optional transformation function for type-safe environment variables.
|
|
29
|
+
* Receives parsed env vars and returns transformed result.
|
|
30
|
+
* Use with validation libraries like Zod for runtime type safety.
|
|
31
|
+
*
|
|
32
|
+
* @param env - Parsed environment variables
|
|
33
|
+
* @returns Transformed and validated environment variables
|
|
34
|
+
* @since 2.0.1
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* import { z } from 'zod';
|
|
38
|
+
* const EnvSchema = z.object({
|
|
39
|
+
* PORT: z.string().transform(Number),
|
|
40
|
+
* DATABASE_URL: z.string().url()
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* @Env({
|
|
44
|
+
* transform: (env) => EnvSchema.parse(env),
|
|
45
|
+
* onTransformError: 'throw'
|
|
46
|
+
* })
|
|
47
|
+
*/
|
|
48
|
+
transform?: (env: Record<string, string>) => T;
|
|
49
|
+
/**
|
|
50
|
+
* Behavior when transform function throws an error
|
|
51
|
+
* - 'throw': Re-throw error immediately (fail-fast, recommended for production)
|
|
52
|
+
* - 'warn': Log warning and continue with unvalidated env
|
|
53
|
+
* - 'ignore': Silent failure, continue without transform
|
|
54
|
+
*
|
|
55
|
+
* @default 'throw'
|
|
56
|
+
* @since 2.0.1
|
|
57
|
+
*/
|
|
58
|
+
onTransformError?: 'throw' | 'warn' | 'ignore';
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Retrieve transformed environment variables from global storage.
|
|
62
|
+
* Returns null if no transform was applied.
|
|
63
|
+
*
|
|
64
|
+
* @template T - Type of transformed environment variables
|
|
65
|
+
* @returns Transformed environment variables or null
|
|
66
|
+
* @since 2.0.1
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* const env = getTransformedEnv<MyEnvType>();
|
|
70
|
+
* if (env) {
|
|
71
|
+
* console.log(env.PORT); // Type-safe access
|
|
72
|
+
* }
|
|
73
|
+
*/
|
|
74
|
+
export declare function getTransformedEnv<T>(): T | null;
|
|
75
|
+
/**
|
|
76
|
+
* Class decorator to load environment variables from .env files before application initialization.
|
|
77
|
+
*
|
|
78
|
+
* This decorator can be stacked to load multiple .env files in order, allowing for
|
|
79
|
+
* environment-specific overrides (e.g., .env → .env.local → .env.production).
|
|
80
|
+
*
|
|
81
|
+
* Environment variables are loaded BEFORE the Settings singleton is initialized,
|
|
82
|
+
* ensuring they're available during the entire application lifecycle.
|
|
83
|
+
*
|
|
84
|
+
* **New in v2.0.1:** Optional type-safe transformation with validation libraries like Zod.
|
|
85
|
+
*
|
|
86
|
+
* @decorator {ClassDecorator} Env - Load environment variables from .env file
|
|
87
|
+
* @template T - Type of transformed environment variables
|
|
88
|
+
* @param options - Environment loading options
|
|
89
|
+
* @returns Class decorator function
|
|
90
|
+
* @since 2.0.0
|
|
91
|
+
* @summary Load environment variables from .env files with optional type-safe transformation
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* // Basic usage - load from .env
|
|
95
|
+
* @Env()
|
|
96
|
+
* class MyApp extends Boot {}
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* // Load from custom path with required variables
|
|
100
|
+
* @Env({
|
|
101
|
+
* path: '.env.production',
|
|
102
|
+
* required: ['DATABASE_URL', 'API_KEY']
|
|
103
|
+
* })
|
|
104
|
+
* class MyApp extends Boot {}
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* // Stack multiple .env files (loaded in order)
|
|
108
|
+
* @Env({ path: '.env' })
|
|
109
|
+
* @Env({ path: '.env.local', override: true, silent: true })
|
|
110
|
+
* class MyApp extends Boot {}
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* // Type-safe transformation with Zod
|
|
114
|
+
* import { z } from 'zod';
|
|
115
|
+
*
|
|
116
|
+
* const EnvSchema = z.object({
|
|
117
|
+
* PORT: z.string().transform(Number),
|
|
118
|
+
* DATABASE_URL: z.string().url(),
|
|
119
|
+
* API_KEY: z.string().min(32)
|
|
120
|
+
* });
|
|
121
|
+
*
|
|
122
|
+
* type Env = z.infer<typeof EnvSchema>;
|
|
123
|
+
*
|
|
124
|
+
* @Env<Env>({
|
|
125
|
+
* path: '.env',
|
|
126
|
+
* required: ['DATABASE_URL', 'API_KEY'],
|
|
127
|
+
* transform: (env) => EnvSchema.parse(env),
|
|
128
|
+
* onTransformError: 'throw' // Fail fast on invalid env
|
|
129
|
+
* })
|
|
130
|
+
* class MyApp extends Boot {
|
|
131
|
+
* constructor() {
|
|
132
|
+
* super();
|
|
133
|
+
* // Access type-safe env
|
|
134
|
+
* const env = Settings.getInstance().getEnv<Env>();
|
|
135
|
+
* console.log(env.PORT); // Type: number
|
|
136
|
+
* }
|
|
137
|
+
* }
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* // In your .env file:
|
|
141
|
+
* // DATABASE_URL=postgres://localhost:5432/mydb
|
|
142
|
+
* // API_KEY="secret-key-with-special-chars"
|
|
143
|
+
* // PORT=3000
|
|
144
|
+
*/
|
|
145
|
+
export declare function Env<T = Record<string, string>>(options?: EnvOptions<T>): ClassDecorator;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getTransformedEnv = getTransformedEnv;
|
|
4
|
+
exports.Env = Env;
|
|
5
|
+
const fs_1 = require("fs");
|
|
6
|
+
const path_1 = require("path");
|
|
7
|
+
const dotenv = require("dotenv");
|
|
8
|
+
/**
|
|
9
|
+
* Global storage for transformed environment variables.
|
|
10
|
+
* Set by @Env decorator when transform option is provided.
|
|
11
|
+
* @private
|
|
12
|
+
*/
|
|
13
|
+
let transformedEnv = null;
|
|
14
|
+
/**
|
|
15
|
+
* Store transformed environment variables in global storage.
|
|
16
|
+
* Used internally by @Env decorator.
|
|
17
|
+
*
|
|
18
|
+
* @template T - Type of transformed environment variables
|
|
19
|
+
* @param env - Transformed environment variables
|
|
20
|
+
* @private
|
|
21
|
+
* @since 2.0.1
|
|
22
|
+
*/
|
|
23
|
+
function storeTransformedEnv(env) {
|
|
24
|
+
transformedEnv = env;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Retrieve transformed environment variables from global storage.
|
|
28
|
+
* Returns null if no transform was applied.
|
|
29
|
+
*
|
|
30
|
+
* @template T - Type of transformed environment variables
|
|
31
|
+
* @returns Transformed environment variables or null
|
|
32
|
+
* @since 2.0.1
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* const env = getTransformedEnv<MyEnvType>();
|
|
36
|
+
* if (env) {
|
|
37
|
+
* console.log(env.PORT); // Type-safe access
|
|
38
|
+
* }
|
|
39
|
+
*/
|
|
40
|
+
function getTransformedEnv() {
|
|
41
|
+
return transformedEnv;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Load environment variables from a .env file using dotenv.
|
|
45
|
+
* Supports transformation and validation via optional transform function.
|
|
46
|
+
*
|
|
47
|
+
* @template T - Type of transformed environment variables
|
|
48
|
+
* @param options - Environment loading options
|
|
49
|
+
* @throws {Error} If required variables are missing, file is not found (when not silent), or transform fails (when onTransformError='throw')
|
|
50
|
+
* @since 2.0.0
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
function loadEnvFile(options) {
|
|
54
|
+
const { path: envPath = '.env', override = false, required = [], silent = false, transform, onTransformError = 'throw' } = options;
|
|
55
|
+
const fullPath = (0, path_1.resolve)(process.cwd(), envPath);
|
|
56
|
+
// Check if file exists
|
|
57
|
+
if (!(0, fs_1.existsSync)(fullPath)) {
|
|
58
|
+
if (!silent) {
|
|
59
|
+
throw new Error(`Environment file not found: ${fullPath}`);
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
// Load with dotenv
|
|
64
|
+
const result = dotenv.config({
|
|
65
|
+
path: fullPath,
|
|
66
|
+
override
|
|
67
|
+
});
|
|
68
|
+
if (result.error && !silent) {
|
|
69
|
+
throw new Error(`Failed to load ${fullPath}: ${result.error.message}`);
|
|
70
|
+
}
|
|
71
|
+
// Validate required variables
|
|
72
|
+
const missing = required.filter((key) => process.env[key] === undefined);
|
|
73
|
+
if (missing.length > 0) {
|
|
74
|
+
throw new Error(`Missing required environment variables: ${missing.join(', ')}`);
|
|
75
|
+
}
|
|
76
|
+
// Apply transform if provided
|
|
77
|
+
if (transform) {
|
|
78
|
+
try {
|
|
79
|
+
const envVars = result.parsed || {};
|
|
80
|
+
const transformed = transform(envVars);
|
|
81
|
+
// Store transformed result globally
|
|
82
|
+
storeTransformedEnv(transformed);
|
|
83
|
+
return transformed;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
const errorMsg = `Environment transformation failed: ${error.message}`;
|
|
87
|
+
if (onTransformError === 'throw') {
|
|
88
|
+
throw new Error(errorMsg);
|
|
89
|
+
}
|
|
90
|
+
else if (onTransformError === 'warn') {
|
|
91
|
+
console.warn(`[Expressive Tea] ${errorMsg}`);
|
|
92
|
+
if (error.stack) {
|
|
93
|
+
console.warn(error.stack);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// 'ignore' - do nothing, return undefined
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Class decorator to load environment variables from .env files before application initialization.
|
|
103
|
+
*
|
|
104
|
+
* This decorator can be stacked to load multiple .env files in order, allowing for
|
|
105
|
+
* environment-specific overrides (e.g., .env → .env.local → .env.production).
|
|
106
|
+
*
|
|
107
|
+
* Environment variables are loaded BEFORE the Settings singleton is initialized,
|
|
108
|
+
* ensuring they're available during the entire application lifecycle.
|
|
109
|
+
*
|
|
110
|
+
* **New in v2.0.1:** Optional type-safe transformation with validation libraries like Zod.
|
|
111
|
+
*
|
|
112
|
+
* @decorator {ClassDecorator} Env - Load environment variables from .env file
|
|
113
|
+
* @template T - Type of transformed environment variables
|
|
114
|
+
* @param options - Environment loading options
|
|
115
|
+
* @returns Class decorator function
|
|
116
|
+
* @since 2.0.0
|
|
117
|
+
* @summary Load environment variables from .env files with optional type-safe transformation
|
|
118
|
+
*
|
|
119
|
+
* @example
|
|
120
|
+
* // Basic usage - load from .env
|
|
121
|
+
* @Env()
|
|
122
|
+
* class MyApp extends Boot {}
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* // Load from custom path with required variables
|
|
126
|
+
* @Env({
|
|
127
|
+
* path: '.env.production',
|
|
128
|
+
* required: ['DATABASE_URL', 'API_KEY']
|
|
129
|
+
* })
|
|
130
|
+
* class MyApp extends Boot {}
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* // Stack multiple .env files (loaded in order)
|
|
134
|
+
* @Env({ path: '.env' })
|
|
135
|
+
* @Env({ path: '.env.local', override: true, silent: true })
|
|
136
|
+
* class MyApp extends Boot {}
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* // Type-safe transformation with Zod
|
|
140
|
+
* import { z } from 'zod';
|
|
141
|
+
*
|
|
142
|
+
* const EnvSchema = z.object({
|
|
143
|
+
* PORT: z.string().transform(Number),
|
|
144
|
+
* DATABASE_URL: z.string().url(),
|
|
145
|
+
* API_KEY: z.string().min(32)
|
|
146
|
+
* });
|
|
147
|
+
*
|
|
148
|
+
* type Env = z.infer<typeof EnvSchema>;
|
|
149
|
+
*
|
|
150
|
+
* @Env<Env>({
|
|
151
|
+
* path: '.env',
|
|
152
|
+
* required: ['DATABASE_URL', 'API_KEY'],
|
|
153
|
+
* transform: (env) => EnvSchema.parse(env),
|
|
154
|
+
* onTransformError: 'throw' // Fail fast on invalid env
|
|
155
|
+
* })
|
|
156
|
+
* class MyApp extends Boot {
|
|
157
|
+
* constructor() {
|
|
158
|
+
* super();
|
|
159
|
+
* // Access type-safe env
|
|
160
|
+
* const env = Settings.getInstance().getEnv<Env>();
|
|
161
|
+
* console.log(env.PORT); // Type: number
|
|
162
|
+
* }
|
|
163
|
+
* }
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* // In your .env file:
|
|
167
|
+
* // DATABASE_URL=postgres://localhost:5432/mydb
|
|
168
|
+
* // API_KEY="secret-key-with-special-chars"
|
|
169
|
+
* // PORT=3000
|
|
170
|
+
*/
|
|
171
|
+
function Env(options = {}) {
|
|
172
|
+
return (target) => {
|
|
173
|
+
// Load env file immediately when decorator is applied
|
|
174
|
+
loadEnvFile(options);
|
|
175
|
+
return target;
|
|
176
|
+
};
|
|
177
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { HealthCheck as HealthCheckConfig } from '../engines/health';
|
|
2
|
+
/**
|
|
3
|
+
* Health check decorator options
|
|
4
|
+
* @since 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
export interface HealthCheckOptions {
|
|
7
|
+
/** Array of health checks to register */
|
|
8
|
+
checks: HealthCheckConfig[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Class decorator to register health checks for monitoring and orchestration.
|
|
12
|
+
*
|
|
13
|
+
* Adds standardized health check endpoints:
|
|
14
|
+
* - `/health` - Detailed health status with all checks
|
|
15
|
+
* - `/health/live` - Liveness probe (Kubernetes compatible)
|
|
16
|
+
* - `/health/ready` - Readiness probe (only passes if all critical checks pass)
|
|
17
|
+
*
|
|
18
|
+
* Health checks are executed asynchronously and can be marked as critical for readiness.
|
|
19
|
+
* Non-critical checks only affect the detailed `/health` endpoint.
|
|
20
|
+
*
|
|
21
|
+
* @decorator {ClassDecorator} HealthCheck - Register health checks
|
|
22
|
+
* @param options - Health check configuration
|
|
23
|
+
* @returns Class decorator function
|
|
24
|
+
* @since 2.0.0
|
|
25
|
+
* @summary Register application health checks for monitoring
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Basic health check
|
|
29
|
+
* @HealthCheck({
|
|
30
|
+
* checks: [
|
|
31
|
+
* {
|
|
32
|
+
* name: 'database',
|
|
33
|
+
* check: async () => {
|
|
34
|
+
* const isConnected = await db.ping();
|
|
35
|
+
* return { status: isConnected ? 'pass' : 'fail' };
|
|
36
|
+
* },
|
|
37
|
+
* critical: true,
|
|
38
|
+
* timeout: 5000
|
|
39
|
+
* }
|
|
40
|
+
* ]
|
|
41
|
+
* })
|
|
42
|
+
* class MyApp extends Boot {}
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* // Multiple health checks with different priorities
|
|
46
|
+
* @HealthCheck({
|
|
47
|
+
* checks: [
|
|
48
|
+
* {
|
|
49
|
+
* name: 'database',
|
|
50
|
+
* check: async () => {
|
|
51
|
+
* const connected = await db.ping();
|
|
52
|
+
* return {
|
|
53
|
+
* status: connected ? 'pass' : 'fail',
|
|
54
|
+
* details: { connections: db.poolSize }
|
|
55
|
+
* };
|
|
56
|
+
* },
|
|
57
|
+
* critical: true // Required for readiness
|
|
58
|
+
* },
|
|
59
|
+
* {
|
|
60
|
+
* name: 'cache',
|
|
61
|
+
* check: async () => {
|
|
62
|
+
* const ready = await redis.ping();
|
|
63
|
+
* return {
|
|
64
|
+
* status: ready ? 'pass' : 'warn', // Warn but don't fail
|
|
65
|
+
* details: { cached_items: await redis.dbsize() }
|
|
66
|
+
* };
|
|
67
|
+
* },
|
|
68
|
+
* critical: false // Optional, won't block readiness
|
|
69
|
+
* },
|
|
70
|
+
* {
|
|
71
|
+
* name: 'external_api',
|
|
72
|
+
* check: async () => {
|
|
73
|
+
* try {
|
|
74
|
+
* const response = await fetch('https://api.example.com/status');
|
|
75
|
+
* return { status: response.ok ? 'pass' : 'warn' };
|
|
76
|
+
* } catch (error) {
|
|
77
|
+
* return {
|
|
78
|
+
* status: 'warn',
|
|
79
|
+
* error: error.message
|
|
80
|
+
* };
|
|
81
|
+
* }
|
|
82
|
+
* },
|
|
83
|
+
* timeout: 3000
|
|
84
|
+
* }
|
|
85
|
+
* ]
|
|
86
|
+
* })
|
|
87
|
+
* class MyApp extends Boot {}
|
|
88
|
+
*
|
|
89
|
+
* @example
|
|
90
|
+
* // Kubernetes deployment.yaml example
|
|
91
|
+
* // spec:
|
|
92
|
+
* // containers:
|
|
93
|
+
* // - name: my-app
|
|
94
|
+
* // livenessProbe:
|
|
95
|
+
* // httpGet:
|
|
96
|
+
* // path: /health/live
|
|
97
|
+
* // port: 3000
|
|
98
|
+
* // initialDelaySeconds: 30
|
|
99
|
+
* // periodSeconds: 10
|
|
100
|
+
* // readinessProbe:
|
|
101
|
+
* // httpGet:
|
|
102
|
+
* // path: /health/ready
|
|
103
|
+
* // port: 3000
|
|
104
|
+
* // initialDelaySeconds: 5
|
|
105
|
+
* // periodSeconds: 5
|
|
106
|
+
*/
|
|
107
|
+
export declare function HealthCheck(options: HealthCheckOptions): ClassDecorator;
|
|
108
|
+
/**
|
|
109
|
+
* Get health checks from a class
|
|
110
|
+
* @param target - Target class
|
|
111
|
+
* @returns Array of health checks
|
|
112
|
+
* @since 2.0.0
|
|
113
|
+
* @internal
|
|
114
|
+
*/
|
|
115
|
+
export declare function getHealthChecks(target: any): HealthCheckConfig[];
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HealthCheck = HealthCheck;
|
|
4
|
+
exports.getHealthChecks = getHealthChecks;
|
|
5
|
+
const commons_1 = require("@expressive-tea/commons");
|
|
6
|
+
/**
|
|
7
|
+
* Health check decorator metadata key
|
|
8
|
+
* @private
|
|
9
|
+
*/
|
|
10
|
+
const HEALTH_CHECKS_KEY = 'expressive-tea:health-checks';
|
|
11
|
+
/**
|
|
12
|
+
* Class decorator to register health checks for monitoring and orchestration.
|
|
13
|
+
*
|
|
14
|
+
* Adds standardized health check endpoints:
|
|
15
|
+
* - `/health` - Detailed health status with all checks
|
|
16
|
+
* - `/health/live` - Liveness probe (Kubernetes compatible)
|
|
17
|
+
* - `/health/ready` - Readiness probe (only passes if all critical checks pass)
|
|
18
|
+
*
|
|
19
|
+
* Health checks are executed asynchronously and can be marked as critical for readiness.
|
|
20
|
+
* Non-critical checks only affect the detailed `/health` endpoint.
|
|
21
|
+
*
|
|
22
|
+
* @decorator {ClassDecorator} HealthCheck - Register health checks
|
|
23
|
+
* @param options - Health check configuration
|
|
24
|
+
* @returns Class decorator function
|
|
25
|
+
* @since 2.0.0
|
|
26
|
+
* @summary Register application health checks for monitoring
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* // Basic health check
|
|
30
|
+
* @HealthCheck({
|
|
31
|
+
* checks: [
|
|
32
|
+
* {
|
|
33
|
+
* name: 'database',
|
|
34
|
+
* check: async () => {
|
|
35
|
+
* const isConnected = await db.ping();
|
|
36
|
+
* return { status: isConnected ? 'pass' : 'fail' };
|
|
37
|
+
* },
|
|
38
|
+
* critical: true,
|
|
39
|
+
* timeout: 5000
|
|
40
|
+
* }
|
|
41
|
+
* ]
|
|
42
|
+
* })
|
|
43
|
+
* class MyApp extends Boot {}
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* // Multiple health checks with different priorities
|
|
47
|
+
* @HealthCheck({
|
|
48
|
+
* checks: [
|
|
49
|
+
* {
|
|
50
|
+
* name: 'database',
|
|
51
|
+
* check: async () => {
|
|
52
|
+
* const connected = await db.ping();
|
|
53
|
+
* return {
|
|
54
|
+
* status: connected ? 'pass' : 'fail',
|
|
55
|
+
* details: { connections: db.poolSize }
|
|
56
|
+
* };
|
|
57
|
+
* },
|
|
58
|
+
* critical: true // Required for readiness
|
|
59
|
+
* },
|
|
60
|
+
* {
|
|
61
|
+
* name: 'cache',
|
|
62
|
+
* check: async () => {
|
|
63
|
+
* const ready = await redis.ping();
|
|
64
|
+
* return {
|
|
65
|
+
* status: ready ? 'pass' : 'warn', // Warn but don't fail
|
|
66
|
+
* details: { cached_items: await redis.dbsize() }
|
|
67
|
+
* };
|
|
68
|
+
* },
|
|
69
|
+
* critical: false // Optional, won't block readiness
|
|
70
|
+
* },
|
|
71
|
+
* {
|
|
72
|
+
* name: 'external_api',
|
|
73
|
+
* check: async () => {
|
|
74
|
+
* try {
|
|
75
|
+
* const response = await fetch('https://api.example.com/status');
|
|
76
|
+
* return { status: response.ok ? 'pass' : 'warn' };
|
|
77
|
+
* } catch (error) {
|
|
78
|
+
* return {
|
|
79
|
+
* status: 'warn',
|
|
80
|
+
* error: error.message
|
|
81
|
+
* };
|
|
82
|
+
* }
|
|
83
|
+
* },
|
|
84
|
+
* timeout: 3000
|
|
85
|
+
* }
|
|
86
|
+
* ]
|
|
87
|
+
* })
|
|
88
|
+
* class MyApp extends Boot {}
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* // Kubernetes deployment.yaml example
|
|
92
|
+
* // spec:
|
|
93
|
+
* // containers:
|
|
94
|
+
* // - name: my-app
|
|
95
|
+
* // livenessProbe:
|
|
96
|
+
* // httpGet:
|
|
97
|
+
* // path: /health/live
|
|
98
|
+
* // port: 3000
|
|
99
|
+
* // initialDelaySeconds: 30
|
|
100
|
+
* // periodSeconds: 10
|
|
101
|
+
* // readinessProbe:
|
|
102
|
+
* // httpGet:
|
|
103
|
+
* // path: /health/ready
|
|
104
|
+
* // port: 3000
|
|
105
|
+
* // initialDelaySeconds: 5
|
|
106
|
+
* // periodSeconds: 5
|
|
107
|
+
*/
|
|
108
|
+
function HealthCheck(options) {
|
|
109
|
+
return (target) => {
|
|
110
|
+
// Store health checks in metadata
|
|
111
|
+
commons_1.Metadata.set(HEALTH_CHECKS_KEY, options.checks, target);
|
|
112
|
+
return target;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get health checks from a class
|
|
117
|
+
* @param target - Target class
|
|
118
|
+
* @returns Array of health checks
|
|
119
|
+
* @since 2.0.0
|
|
120
|
+
* @internal
|
|
121
|
+
*/
|
|
122
|
+
function getHealthChecks(target) {
|
|
123
|
+
return commons_1.Metadata.get(HEALTH_CHECKS_KEY, target) || [];
|
|
124
|
+
}
|
package/decorators/module.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { type
|
|
2
|
-
import { type
|
|
1
|
+
import { type ExpressiveTeaModuleProps } from '@expressive-tea/commons';
|
|
2
|
+
import { type Constructor } from '../types/core';
|
|
3
|
+
import { type ModulizedClass } from '../mixins/module';
|
|
3
4
|
/**
|
|
4
5
|
* @typedef {Object} ExpressiveTeaModuleProps
|
|
5
6
|
* @property {Object[]} controllers Controllers Assigned to Module
|
|
@@ -13,23 +14,21 @@ import { type ExpressiveTeaModuleProps } from '@expressive-tea/commons/interface
|
|
|
13
14
|
* Module Decorator is a Class Decorator which is help to register a Module into Expressive Tea. A module is a
|
|
14
15
|
* placeholder over a mountpoint. We can considerate a module like a container which provide isolation and modularity
|
|
15
16
|
* for our project. This module can be mounted in different applications and will move all the controller routes too.
|
|
17
|
+
*
|
|
16
18
|
* @decorator {ClassDecorator} Module - Module Class Register Decorator
|
|
17
|
-
* @
|
|
19
|
+
* @template TBase - The base constructor type being decorated
|
|
20
|
+
* @param {ExpressiveTeaModuleProps} options - Module configuration options
|
|
21
|
+
* @returns {(target: TBase) => ModulizedClass<TBase>} Decorator function that returns a modulized class
|
|
18
22
|
* @summary Module Decorator
|
|
23
|
+
*
|
|
19
24
|
* @example
|
|
20
25
|
* {REPLACE-AT}Module({
|
|
21
|
-
* controllers: [],
|
|
22
|
-
* providers: [],
|
|
23
|
-
* mountpoint: '/'
|
|
26
|
+
* controllers: [UserController],
|
|
27
|
+
* providers: [UserService],
|
|
28
|
+
* mountpoint: '/api'
|
|
24
29
|
* })
|
|
25
|
-
* class
|
|
30
|
+
* class ApiModule {}
|
|
31
|
+
*
|
|
32
|
+
* @since 1.0.0
|
|
26
33
|
*/
|
|
27
|
-
export declare function Module
|
|
28
|
-
new (...args: any[]): {
|
|
29
|
-
[x: string]: any;
|
|
30
|
-
readonly settings: ExpressiveTeaModuleProps;
|
|
31
|
-
readonly router: Router;
|
|
32
|
-
readonly controllers: any[];
|
|
33
|
-
__register(server: Express): void;
|
|
34
|
-
};
|
|
35
|
-
} & T;
|
|
34
|
+
export declare function Module<TBase extends Constructor = Constructor>(options: ExpressiveTeaModuleProps): (target: TBase) => ModulizedClass<TBase>;
|
package/decorators/module.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Module =
|
|
4
|
-
const
|
|
5
|
-
const lodash_1 = require("lodash");
|
|
6
|
-
const DependencyInjection_1 = require("../services/DependencyInjection");
|
|
3
|
+
exports.Module = Module;
|
|
4
|
+
const module_1 = require("../mixins/module");
|
|
7
5
|
/**
|
|
8
6
|
* @typedef {Object} ExpressiveTeaModuleProps
|
|
9
7
|
* @property {Object[]} controllers Controllers Assigned to Module
|
|
@@ -17,33 +15,25 @@ const DependencyInjection_1 = require("../services/DependencyInjection");
|
|
|
17
15
|
* Module Decorator is a Class Decorator which is help to register a Module into Expressive Tea. A module is a
|
|
18
16
|
* placeholder over a mountpoint. We can considerate a module like a container which provide isolation and modularity
|
|
19
17
|
* for our project. This module can be mounted in different applications and will move all the controller routes too.
|
|
18
|
+
*
|
|
20
19
|
* @decorator {ClassDecorator} Module - Module Class Register Decorator
|
|
21
|
-
* @
|
|
20
|
+
* @template TBase - The base constructor type being decorated
|
|
21
|
+
* @param {ExpressiveTeaModuleProps} options - Module configuration options
|
|
22
|
+
* @returns {(target: TBase) => ModulizedClass<TBase>} Decorator function that returns a modulized class
|
|
22
23
|
* @summary Module Decorator
|
|
24
|
+
*
|
|
23
25
|
* @example
|
|
24
26
|
* {REPLACE-AT}Module({
|
|
25
|
-
* controllers: [],
|
|
26
|
-
* providers: [],
|
|
27
|
-
* mountpoint: '/'
|
|
27
|
+
* controllers: [UserController],
|
|
28
|
+
* providers: [UserService],
|
|
29
|
+
* mountpoint: '/api'
|
|
28
30
|
* })
|
|
29
|
-
* class
|
|
31
|
+
* class ApiModule {}
|
|
32
|
+
*
|
|
33
|
+
* @since 1.0.0
|
|
30
34
|
*/
|
|
31
35
|
function Module(options) {
|
|
32
36
|
return (Module) => {
|
|
33
|
-
return
|
|
34
|
-
constructor(...args) {
|
|
35
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
36
|
-
super(...args);
|
|
37
|
-
this.router = (0, express_1.Router)();
|
|
38
|
-
this.settings = options;
|
|
39
|
-
(0, lodash_1.each)(this.settings.providers, (P) => { DependencyInjection_1.default.setProvider(P); });
|
|
40
|
-
this.controllers = (0, lodash_1.map)(this.settings.controllers, C => new C());
|
|
41
|
-
}
|
|
42
|
-
__register(server) {
|
|
43
|
-
(0, lodash_1.each)(this.controllers, c => c.__mount(this.router));
|
|
44
|
-
server.use(this.settings.mountpoint, this.router);
|
|
45
|
-
}
|
|
46
|
-
};
|
|
37
|
+
return (0, module_1.Modulize)(Module, options);
|
|
47
38
|
};
|
|
48
39
|
}
|
|
49
|
-
exports.Module = Module;
|