@zerooneit/expressive-tea 1.3.0-beta.5 → 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/.gitattributes +4 -0
- package/.swcrc +61 -0
- package/README.md +564 -174
- package/classes/Boot.d.ts +94 -3
- package/classes/Boot.js +171 -51
- package/classes/Engine.d.ts +59 -10
- package/classes/Engine.js +72 -11
- package/classes/EngineRegistry.d.ts +154 -0
- package/classes/EngineRegistry.js +247 -0
- package/classes/LoadBalancer.js +2 -5
- package/classes/ProxyRoute.d.ts +3 -3
- 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 -15
- package/decorators/module.js +14 -23
- package/decorators/proxy.d.ts +26 -11
- package/decorators/proxy.js +35 -45
- package/decorators/router.d.ts +17 -16
- package/decorators/router.js +32 -52
- package/decorators/server.d.ts +8 -8
- 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 -7
- package/engines/http/index.js +22 -17
- package/engines/index.d.ts +32 -0
- package/engines/index.js +112 -0
- package/engines/socketio/index.d.ts +2 -1
- package/engines/socketio/index.js +16 -6
- package/engines/teacup/index.d.ts +13 -0
- package/engines/teacup/index.js +61 -11
- package/engines/teapot/index.d.ts +15 -2
- package/engines/teapot/index.js +61 -13
- package/engines/websocket/index.d.ts +4 -1
- package/engines/websocket/index.js +10 -2
- package/eslint.config.mjs +138 -0
- package/exceptions/RequestExceptions.d.ts +3 -3
- package/helpers/boot-helper.d.ts +6 -6
- package/helpers/boot-helper.js +30 -24
- 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 +32 -6
- package/helpers/server.js +101 -61
- package/helpers/teapot-helper.d.ts +5 -8
- package/helpers/teapot-helper.js +39 -11
- package/helpers/websocket-helper.d.ts +3 -5
- 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 +91 -69
- package/services/DependencyInjection.d.ts +95 -7
- package/services/DependencyInjection.js +123 -5
- package/services/WebsocketService.d.ts +4 -6
- 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
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import ExpressiveTeaEngine from './Engine';
|
|
2
|
+
/**
|
|
3
|
+
* Engine constructor type with static canRegister method
|
|
4
|
+
*/
|
|
5
|
+
export type EngineConstructor = typeof ExpressiveTeaEngine;
|
|
6
|
+
/**
|
|
7
|
+
* Metadata for registered engines
|
|
8
|
+
* @interface EngineMetadata
|
|
9
|
+
* @since 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
export interface EngineMetadata {
|
|
12
|
+
/** Engine class constructor */
|
|
13
|
+
engine: EngineConstructor;
|
|
14
|
+
/** Unique engine name */
|
|
15
|
+
name: string;
|
|
16
|
+
/** Engine version (semver) */
|
|
17
|
+
version: string;
|
|
18
|
+
/** Initialization priority (lower runs first) */
|
|
19
|
+
priority: number;
|
|
20
|
+
/** Array of engine names this engine depends on */
|
|
21
|
+
dependencies: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Engine Registry - Manages engine registration, dependency resolution, and initialization order
|
|
25
|
+
*
|
|
26
|
+
* Replaces hardcoded engine arrays with an extensible plugin system that:
|
|
27
|
+
* - Registers engines with metadata (name, version, priority, dependencies)
|
|
28
|
+
* - Resolves engine dependencies using topological sort
|
|
29
|
+
* - Detects circular dependencies
|
|
30
|
+
* - Filters engines based on their `canRegister()` method
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* // Register a custom engine
|
|
35
|
+
* EngineRegistry.register({
|
|
36
|
+
* engine: MyCustomEngine,
|
|
37
|
+
* name: 'custom',
|
|
38
|
+
* version: '1.0.0',
|
|
39
|
+
* priority: 15,
|
|
40
|
+
* dependencies: ['http']
|
|
41
|
+
* });
|
|
42
|
+
*
|
|
43
|
+
* // Get engines in dependency order
|
|
44
|
+
* const engines = EngineRegistry.getRegisteredEngines(context, settings);
|
|
45
|
+
* ```
|
|
46
|
+
*
|
|
47
|
+
* @class EngineRegistry
|
|
48
|
+
* @since 2.0.0
|
|
49
|
+
*/
|
|
50
|
+
export default class EngineRegistry {
|
|
51
|
+
private static engines;
|
|
52
|
+
/**
|
|
53
|
+
* Register an engine with metadata
|
|
54
|
+
*
|
|
55
|
+
* @param {EngineMetadata} metadata - Engine metadata including name, version, priority, and dependencies
|
|
56
|
+
* @throws {Error} If engine with same name is already registered
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* EngineRegistry.register({
|
|
61
|
+
* engine: HTTPEngine,
|
|
62
|
+
* name: 'http',
|
|
63
|
+
* version: '2.0.0',
|
|
64
|
+
* priority: 0,
|
|
65
|
+
* dependencies: []
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
static register(metadata: EngineMetadata): void;
|
|
70
|
+
/**
|
|
71
|
+
* Unregister an engine by name
|
|
72
|
+
*
|
|
73
|
+
* @param {string} name - Engine name to unregister
|
|
74
|
+
* @returns {boolean} True if engine was unregistered, false if not found
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* EngineRegistry.unregister('custom');
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
static unregister(name: string): boolean;
|
|
82
|
+
/**
|
|
83
|
+
* Clear all registered engines (useful for testing)
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* EngineRegistry.clear();
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
static clear(): void;
|
|
91
|
+
/**
|
|
92
|
+
* Get all registered engine metadata
|
|
93
|
+
*
|
|
94
|
+
* @returns {EngineMetadata[]} Array of all registered engine metadata
|
|
95
|
+
*/
|
|
96
|
+
static getAllEngines(): EngineMetadata[];
|
|
97
|
+
/**
|
|
98
|
+
* Get engine metadata by name
|
|
99
|
+
*
|
|
100
|
+
* @param {string} name - Engine name
|
|
101
|
+
* @returns {EngineMetadata | undefined} Engine metadata or undefined if not found
|
|
102
|
+
*/
|
|
103
|
+
static getEngine(name: string): EngineMetadata | undefined;
|
|
104
|
+
/**
|
|
105
|
+
* Get registered engines filtered by canRegister() and sorted by dependency order
|
|
106
|
+
*
|
|
107
|
+
* This method:
|
|
108
|
+
* 1. Filters engines using their static `canRegister()` method
|
|
109
|
+
* 2. Validates all dependencies are registered
|
|
110
|
+
* 3. Detects circular dependencies
|
|
111
|
+
* 4. Sorts engines using topological sort (dependencies first)
|
|
112
|
+
* 5. Applies priority sorting within same dependency level
|
|
113
|
+
*
|
|
114
|
+
* @param {unknown} context - Boot context to pass to canRegister()
|
|
115
|
+
* @param {unknown} settings - Settings to pass to canRegister()
|
|
116
|
+
* @returns {EngineConstructor[]} Array of engine constructors in initialization order
|
|
117
|
+
* @throws {Error} If circular dependency is detected
|
|
118
|
+
* @throws {Error} If required dependency is not registered
|
|
119
|
+
*
|
|
120
|
+
* @example
|
|
121
|
+
* ```typescript
|
|
122
|
+
* const engines = EngineRegistry.getRegisteredEngines(boot, settings);
|
|
123
|
+
* // Returns: [HTTPEngine, SocketIOEngine, TeapotEngine] in dependency order
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
static getRegisteredEngines(context?: unknown, settings?: unknown): EngineConstructor[];
|
|
127
|
+
/**
|
|
128
|
+
* Validate that all dependencies are registered
|
|
129
|
+
*
|
|
130
|
+
* @private
|
|
131
|
+
* @param {EngineMetadata[]} engines - Engines to validate
|
|
132
|
+
* @throws {Error} If a dependency is not registered
|
|
133
|
+
*/
|
|
134
|
+
private static validateDependencies;
|
|
135
|
+
/**
|
|
136
|
+
* Detect circular dependencies using depth-first search
|
|
137
|
+
*
|
|
138
|
+
* @private
|
|
139
|
+
* @param {EngineMetadata[]} engines - Engines to check
|
|
140
|
+
* @throws {Error} If circular dependency is detected
|
|
141
|
+
*/
|
|
142
|
+
private static detectCircularDependencies;
|
|
143
|
+
/**
|
|
144
|
+
* Topological sort with priority ordering
|
|
145
|
+
*
|
|
146
|
+
* Uses Kahn's algorithm for topological sorting, with priority-based ordering
|
|
147
|
+
* for engines at the same dependency level.
|
|
148
|
+
*
|
|
149
|
+
* @private
|
|
150
|
+
* @param {EngineMetadata[]} engines - Engines to sort
|
|
151
|
+
* @returns {EngineMetadata[]} Sorted engines (dependencies first, then by priority)
|
|
152
|
+
*/
|
|
153
|
+
private static topologicalSort;
|
|
154
|
+
}
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
/**
|
|
4
|
+
* Engine Registry - Manages engine registration, dependency resolution, and initialization order
|
|
5
|
+
*
|
|
6
|
+
* Replaces hardcoded engine arrays with an extensible plugin system that:
|
|
7
|
+
* - Registers engines with metadata (name, version, priority, dependencies)
|
|
8
|
+
* - Resolves engine dependencies using topological sort
|
|
9
|
+
* - Detects circular dependencies
|
|
10
|
+
* - Filters engines based on their `canRegister()` method
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // Register a custom engine
|
|
15
|
+
* EngineRegistry.register({
|
|
16
|
+
* engine: MyCustomEngine,
|
|
17
|
+
* name: 'custom',
|
|
18
|
+
* version: '1.0.0',
|
|
19
|
+
* priority: 15,
|
|
20
|
+
* dependencies: ['http']
|
|
21
|
+
* });
|
|
22
|
+
*
|
|
23
|
+
* // Get engines in dependency order
|
|
24
|
+
* const engines = EngineRegistry.getRegisteredEngines(context, settings);
|
|
25
|
+
* ```
|
|
26
|
+
*
|
|
27
|
+
* @class EngineRegistry
|
|
28
|
+
* @since 2.0.0
|
|
29
|
+
*/
|
|
30
|
+
class EngineRegistry {
|
|
31
|
+
/**
|
|
32
|
+
* Register an engine with metadata
|
|
33
|
+
*
|
|
34
|
+
* @param {EngineMetadata} metadata - Engine metadata including name, version, priority, and dependencies
|
|
35
|
+
* @throws {Error} If engine with same name is already registered
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* EngineRegistry.register({
|
|
40
|
+
* engine: HTTPEngine,
|
|
41
|
+
* name: 'http',
|
|
42
|
+
* version: '2.0.0',
|
|
43
|
+
* priority: 0,
|
|
44
|
+
* dependencies: []
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
static register(metadata) {
|
|
49
|
+
if (this.engines.has(metadata.name)) {
|
|
50
|
+
throw new Error(`Engine "${metadata.name}" is already registered`);
|
|
51
|
+
}
|
|
52
|
+
this.engines.set(metadata.name, metadata);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Unregister an engine by name
|
|
56
|
+
*
|
|
57
|
+
* @param {string} name - Engine name to unregister
|
|
58
|
+
* @returns {boolean} True if engine was unregistered, false if not found
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```typescript
|
|
62
|
+
* EngineRegistry.unregister('custom');
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
static unregister(name) {
|
|
66
|
+
return this.engines.delete(name);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Clear all registered engines (useful for testing)
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```typescript
|
|
73
|
+
* EngineRegistry.clear();
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
static clear() {
|
|
77
|
+
this.engines.clear();
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get all registered engine metadata
|
|
81
|
+
*
|
|
82
|
+
* @returns {EngineMetadata[]} Array of all registered engine metadata
|
|
83
|
+
*/
|
|
84
|
+
static getAllEngines() {
|
|
85
|
+
return Array.from(this.engines.values());
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Get engine metadata by name
|
|
89
|
+
*
|
|
90
|
+
* @param {string} name - Engine name
|
|
91
|
+
* @returns {EngineMetadata | undefined} Engine metadata or undefined if not found
|
|
92
|
+
*/
|
|
93
|
+
static getEngine(name) {
|
|
94
|
+
return this.engines.get(name);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get registered engines filtered by canRegister() and sorted by dependency order
|
|
98
|
+
*
|
|
99
|
+
* This method:
|
|
100
|
+
* 1. Filters engines using their static `canRegister()` method
|
|
101
|
+
* 2. Validates all dependencies are registered
|
|
102
|
+
* 3. Detects circular dependencies
|
|
103
|
+
* 4. Sorts engines using topological sort (dependencies first)
|
|
104
|
+
* 5. Applies priority sorting within same dependency level
|
|
105
|
+
*
|
|
106
|
+
* @param {unknown} context - Boot context to pass to canRegister()
|
|
107
|
+
* @param {unknown} settings - Settings to pass to canRegister()
|
|
108
|
+
* @returns {EngineConstructor[]} Array of engine constructors in initialization order
|
|
109
|
+
* @throws {Error} If circular dependency is detected
|
|
110
|
+
* @throws {Error} If required dependency is not registered
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```typescript
|
|
114
|
+
* const engines = EngineRegistry.getRegisteredEngines(boot, settings);
|
|
115
|
+
* // Returns: [HTTPEngine, SocketIOEngine, TeapotEngine] in dependency order
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
static getRegisteredEngines(context, settings) {
|
|
119
|
+
// Filter engines by canRegister()
|
|
120
|
+
const availableEngines = Array.from(this.engines.values())
|
|
121
|
+
.filter(metadata => metadata.engine.canRegister(context, settings));
|
|
122
|
+
// Validate dependencies
|
|
123
|
+
this.validateDependencies(availableEngines);
|
|
124
|
+
// Detect circular dependencies
|
|
125
|
+
this.detectCircularDependencies(availableEngines);
|
|
126
|
+
// Topological sort with priority
|
|
127
|
+
const sorted = this.topologicalSort(availableEngines);
|
|
128
|
+
return sorted.map(metadata => metadata.engine);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Validate that all dependencies are registered
|
|
132
|
+
*
|
|
133
|
+
* @private
|
|
134
|
+
* @param {EngineMetadata[]} engines - Engines to validate
|
|
135
|
+
* @throws {Error} If a dependency is not registered
|
|
136
|
+
*/
|
|
137
|
+
static validateDependencies(engines) {
|
|
138
|
+
const engineNames = new Set(engines.map(e => e.name));
|
|
139
|
+
for (const engine of engines) {
|
|
140
|
+
for (const dep of engine.dependencies) {
|
|
141
|
+
if (!engineNames.has(dep)) {
|
|
142
|
+
throw new Error(`Engine "${engine.name}" depends on "${dep}" which is not registered or cannot be registered`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Detect circular dependencies using depth-first search
|
|
149
|
+
*
|
|
150
|
+
* @private
|
|
151
|
+
* @param {EngineMetadata[]} engines - Engines to check
|
|
152
|
+
* @throws {Error} If circular dependency is detected
|
|
153
|
+
*/
|
|
154
|
+
static detectCircularDependencies(engines) {
|
|
155
|
+
const visited = new Set();
|
|
156
|
+
const recursionStack = new Set();
|
|
157
|
+
const engineMap = new Map(engines.map(e => [e.name, e]));
|
|
158
|
+
const visit = (engineName, path) => {
|
|
159
|
+
if (recursionStack.has(engineName)) {
|
|
160
|
+
const cycle = [...path, engineName].join(' -> ');
|
|
161
|
+
throw new Error(`Circular dependency detected: ${cycle}`);
|
|
162
|
+
}
|
|
163
|
+
if (visited.has(engineName)) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
visited.add(engineName);
|
|
167
|
+
recursionStack.add(engineName);
|
|
168
|
+
const engine = engineMap.get(engineName);
|
|
169
|
+
if (engine) {
|
|
170
|
+
for (const dep of engine.dependencies) {
|
|
171
|
+
visit(dep, [...path, engineName]);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
recursionStack.delete(engineName);
|
|
175
|
+
};
|
|
176
|
+
for (const engine of engines) {
|
|
177
|
+
if (!visited.has(engine.name)) {
|
|
178
|
+
visit(engine.name, []);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Topological sort with priority ordering
|
|
184
|
+
*
|
|
185
|
+
* Uses Kahn's algorithm for topological sorting, with priority-based ordering
|
|
186
|
+
* for engines at the same dependency level.
|
|
187
|
+
*
|
|
188
|
+
* @private
|
|
189
|
+
* @param {EngineMetadata[]} engines - Engines to sort
|
|
190
|
+
* @returns {EngineMetadata[]} Sorted engines (dependencies first, then by priority)
|
|
191
|
+
*/
|
|
192
|
+
static topologicalSort(engines) {
|
|
193
|
+
var _a;
|
|
194
|
+
const engineMap = new Map(engines.map(e => [e.name, e]));
|
|
195
|
+
const inDegree = new Map();
|
|
196
|
+
const dependents = new Map();
|
|
197
|
+
// Initialize in-degree and dependents
|
|
198
|
+
for (const engine of engines) {
|
|
199
|
+
inDegree.set(engine.name, 0);
|
|
200
|
+
dependents.set(engine.name, []);
|
|
201
|
+
}
|
|
202
|
+
// Build dependency graph
|
|
203
|
+
for (const engine of engines) {
|
|
204
|
+
for (const dep of engine.dependencies) {
|
|
205
|
+
inDegree.set(engine.name, (inDegree.get(engine.name) || 0) + 1);
|
|
206
|
+
(_a = dependents.get(dep)) === null || _a === void 0 ? void 0 : _a.push(engine.name);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// Priority queue for engines with no dependencies
|
|
210
|
+
const queue = [];
|
|
211
|
+
for (const engine of engines) {
|
|
212
|
+
if (inDegree.get(engine.name) === 0) {
|
|
213
|
+
queue.push(engine);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Sort queue by priority
|
|
217
|
+
queue.sort((a, b) => a.priority - b.priority);
|
|
218
|
+
const sorted = [];
|
|
219
|
+
while (queue.length > 0) {
|
|
220
|
+
// Remove engine with lowest priority
|
|
221
|
+
const current = queue.shift();
|
|
222
|
+
sorted.push(current);
|
|
223
|
+
// Reduce in-degree for dependents
|
|
224
|
+
const deps = dependents.get(current.name) || [];
|
|
225
|
+
for (const depName of deps) {
|
|
226
|
+
const newDegree = (inDegree.get(depName) || 0) - 1;
|
|
227
|
+
inDegree.set(depName, newDegree);
|
|
228
|
+
if (newDegree === 0) {
|
|
229
|
+
const engine = engineMap.get(depName);
|
|
230
|
+
if (engine) {
|
|
231
|
+
// Insert sorted by priority
|
|
232
|
+
const insertIndex = queue.findIndex(e => e.priority > engine.priority);
|
|
233
|
+
if (insertIndex === -1) {
|
|
234
|
+
queue.push(engine);
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
queue.splice(insertIndex, 0, engine);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return sorted;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
EngineRegistry.engines = new Map();
|
|
247
|
+
exports.default = EngineRegistry;
|
package/classes/LoadBalancer.js
CHANGED
|
@@ -5,11 +5,8 @@ class LoadBalancer {
|
|
|
5
5
|
* @offset should be used for unit testing and nothing else.
|
|
6
6
|
*/
|
|
7
7
|
constructor(count, offset = 0) {
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
for (let i = 0; i < this.bins.length; i++) {
|
|
11
|
-
this.bins[i] = offset;
|
|
12
|
-
}
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
9
|
+
this.bins = new Array(count).fill(offset);
|
|
13
10
|
}
|
|
14
11
|
pick() {
|
|
15
12
|
const a = Math.trunc(Math.random() * this.bins.length);
|
package/classes/ProxyRoute.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { RequestHandler } from 'express';
|
|
1
|
+
import { type RequestHandler } from 'express';
|
|
2
2
|
export default class ProxyRoute {
|
|
3
3
|
readonly registeredOn: string;
|
|
4
4
|
private balancer;
|
|
5
|
-
private servers;
|
|
6
|
-
private clients;
|
|
5
|
+
private readonly servers;
|
|
6
|
+
private readonly clients;
|
|
7
7
|
private lastServerSelected;
|
|
8
8
|
constructor(registeredOn: string);
|
|
9
9
|
hasClients(): boolean;
|
package/classes/ProxyRoute.js
CHANGED
|
@@ -2,19 +2,19 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const proxy = require("express-http-proxy");
|
|
4
4
|
const LoadBalancer_1 = require("./LoadBalancer");
|
|
5
|
-
const
|
|
5
|
+
const utilities_1 = require("../libs/utilities");
|
|
6
6
|
class ProxyRoute {
|
|
7
7
|
constructor(registeredOn) {
|
|
8
|
+
this.registeredOn = registeredOn;
|
|
8
9
|
this.servers = [];
|
|
9
10
|
this.clients = [];
|
|
10
11
|
this.lastServerSelected = 0;
|
|
11
|
-
this.registeredOn = registeredOn;
|
|
12
12
|
}
|
|
13
13
|
hasClients() {
|
|
14
|
-
return (0,
|
|
14
|
+
return (0, utilities_1.size)(this.clients) > 0;
|
|
15
15
|
}
|
|
16
16
|
isClientOnRoute(teacupId) {
|
|
17
|
-
return (0,
|
|
17
|
+
return (0, utilities_1.includes)(this.clients, teacupId);
|
|
18
18
|
}
|
|
19
19
|
registerServer(address, teacupId) {
|
|
20
20
|
this.servers.push({ teacupId, address });
|
|
@@ -22,7 +22,7 @@ class ProxyRoute {
|
|
|
22
22
|
this.balancer = new LoadBalancer_1.default(this.servers.length, this.lastServerSelected);
|
|
23
23
|
}
|
|
24
24
|
unregisterServer(teacupId) {
|
|
25
|
-
const index = (0,
|
|
25
|
+
const index = (0, utilities_1.indexOf)(this.clients, teacupId);
|
|
26
26
|
this.servers.splice(index, 1);
|
|
27
27
|
this.clients.splice(index, 1);
|
|
28
28
|
this.balancer = new LoadBalancer_1.default(this.servers.length);
|
package/classes/Settings.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExpressiveTeaServerProps } from '@expressive-tea/commons
|
|
1
|
+
import { ExpressiveTeaServerProps } from '@expressive-tea/commons';
|
|
2
2
|
/**
|
|
3
3
|
* Declare the properties which the server will save into settings, is a semi dynamic object since is allowed to save
|
|
4
4
|
* any property but is contains only one defined property to keep the port of the server.
|
|
@@ -26,10 +26,11 @@ declare class Settings {
|
|
|
26
26
|
* one time at the application starts.
|
|
27
27
|
*
|
|
28
28
|
* @static
|
|
29
|
+
* @param {boolean} [resetIsolated=true] - If true, also reset all isolated contexts
|
|
29
30
|
* @summary Reset Singleton instance
|
|
30
31
|
* @memberof Settings
|
|
31
32
|
*/
|
|
32
|
-
static reset(): void;
|
|
33
|
+
static reset(resetIsolated?: boolean): void;
|
|
33
34
|
/**
|
|
34
35
|
* Get Current Singleton Instance or Created if not exists. If a new instance is created it will created with default
|
|
35
36
|
* options.
|
|
@@ -95,5 +96,33 @@ declare class Settings {
|
|
|
95
96
|
* @summary Merge Options
|
|
96
97
|
*/
|
|
97
98
|
merge(options?: ExpressiveTeaServerProps): void;
|
|
99
|
+
/**
|
|
100
|
+
* Get environment variables with optional type safety.
|
|
101
|
+
*
|
|
102
|
+
* Returns transformed environment variables if a transform function was provided
|
|
103
|
+
* to the @Env decorator, otherwise returns process.env.
|
|
104
|
+
*
|
|
105
|
+
* Use this method for type-safe access to environment variables when using
|
|
106
|
+
* the @Env decorator with a transform function (e.g., Zod validation).
|
|
107
|
+
*
|
|
108
|
+
* @template T - Type of environment variables (defaults to NodeJS.ProcessEnv)
|
|
109
|
+
* @returns Typed environment variables
|
|
110
|
+
* @since 2.0.1
|
|
111
|
+
* @summary Get type-safe environment variables
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* // Basic usage (returns process.env)
|
|
115
|
+
* const env = Settings.getInstance().getEnv();
|
|
116
|
+
* console.log(env.NODE_ENV);
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* // With type-safe transform from @Env decorator
|
|
120
|
+
* type Env = { PORT: number; DATABASE_URL: string };
|
|
121
|
+
*
|
|
122
|
+
* const env = Settings.getInstance().getEnv<Env>();
|
|
123
|
+
* console.log(env.PORT); // Type: number (transformed)
|
|
124
|
+
* console.log(env.DATABASE_URL); // Type: string (validated)
|
|
125
|
+
*/
|
|
126
|
+
getEnv<T = Record<string, string>>(): T;
|
|
98
127
|
}
|
|
99
128
|
export default Settings;
|
package/classes/Settings.js
CHANGED
|
@@ -2,10 +2,11 @@
|
|
|
2
2
|
var Settings_1;
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
const
|
|
5
|
+
const utilities_1 = require("../libs/utilities");
|
|
6
|
+
const commons_1 = require("@expressive-tea/commons");
|
|
6
7
|
const inversify_1 = require("inversify");
|
|
7
|
-
const object_helper_1 = require("@expressive-tea/commons/helpers/object-helper");
|
|
8
8
|
const server_1 = require("../helpers/server");
|
|
9
|
+
const env_1 = require("../decorators/env");
|
|
9
10
|
/**
|
|
10
11
|
* Declare the properties which the server will save into settings, is a semi dynamic object since is allowed to save
|
|
11
12
|
* any property but is contains only one defined property to keep the port of the server.
|
|
@@ -32,11 +33,15 @@ let Settings = Settings_1 = class Settings {
|
|
|
32
33
|
* one time at the application starts.
|
|
33
34
|
*
|
|
34
35
|
* @static
|
|
36
|
+
* @param {boolean} [resetIsolated=true] - If true, also reset all isolated contexts
|
|
35
37
|
* @summary Reset Singleton instance
|
|
36
38
|
* @memberof Settings
|
|
37
39
|
*/
|
|
38
|
-
static reset() {
|
|
39
|
-
|
|
40
|
+
static reset(resetIsolated = true) {
|
|
41
|
+
Settings_1.instance = undefined;
|
|
42
|
+
if (resetIsolated) {
|
|
43
|
+
Settings_1.isolatedContext.clear();
|
|
44
|
+
}
|
|
40
45
|
}
|
|
41
46
|
/**
|
|
42
47
|
* Get Current Singleton Instance or Created if not exists. If a new instance is created it will created with default
|
|
@@ -47,23 +52,34 @@ let Settings = Settings_1 = class Settings {
|
|
|
47
52
|
* @memberof Settings
|
|
48
53
|
* @summary Get Singleton Instance.
|
|
49
54
|
*/
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
56
|
static getInstance(ctx) {
|
|
51
57
|
if (ctx) {
|
|
52
|
-
const context = (0,
|
|
58
|
+
const context = (0, commons_1.nameOfClass)(ctx);
|
|
53
59
|
if (!Settings_1.isolatedContext.has(context)) {
|
|
54
|
-
Settings_1.isolatedContext.set(context, new Settings_1(
|
|
60
|
+
Settings_1.isolatedContext.set(context, new Settings_1(undefined, true));
|
|
55
61
|
}
|
|
56
62
|
return Settings_1.isolatedContext.get(context);
|
|
57
63
|
}
|
|
58
64
|
return Settings_1.instance || new Settings_1();
|
|
59
65
|
}
|
|
60
|
-
constructor(options = {
|
|
66
|
+
constructor(options = {}, isIsolated = false) {
|
|
67
|
+
/**
|
|
68
|
+
* Server configuration options.
|
|
69
|
+
*
|
|
70
|
+
* @private
|
|
71
|
+
* @type {ExpressiveTeaServerProps}
|
|
72
|
+
* @memberof Settings
|
|
73
|
+
*/
|
|
74
|
+
this.options = { port: 3000, securePort: 4443 };
|
|
61
75
|
if (Settings_1.instance && !isIsolated) {
|
|
62
76
|
return Settings_1.instance;
|
|
63
77
|
}
|
|
64
78
|
const settingsFile = (0, server_1.fileSettings)();
|
|
65
|
-
this.options = Object.assign({},
|
|
66
|
-
|
|
79
|
+
this.options = Object.assign(Object.assign(Object.assign({}, this.options), settingsFile.config), options);
|
|
80
|
+
if (!isIsolated) {
|
|
81
|
+
Settings_1.instance = this;
|
|
82
|
+
}
|
|
67
83
|
}
|
|
68
84
|
/**
|
|
69
85
|
* It will return the latest snapshot options registered at the time that this method is called, as Expressive Tea
|
|
@@ -85,8 +101,9 @@ let Settings = Settings_1 = class Settings {
|
|
|
85
101
|
* @memberof Settings
|
|
86
102
|
* @summary Retrieve an option
|
|
87
103
|
*/
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
88
105
|
get(settingName) {
|
|
89
|
-
return
|
|
106
|
+
return (0, utilities_1.get)(this.options, settingName, null);
|
|
90
107
|
}
|
|
91
108
|
/**
|
|
92
109
|
* Initialize or Edit a application options, this is only for in run options, as explained above initialization
|
|
@@ -97,8 +114,9 @@ let Settings = Settings_1 = class Settings {
|
|
|
97
114
|
* @memberof Settings
|
|
98
115
|
* @summary Initialize an option.
|
|
99
116
|
*/
|
|
117
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
100
118
|
set(settingName, value) {
|
|
101
|
-
|
|
119
|
+
(0, utilities_1.set)(this.options, settingName, value);
|
|
102
120
|
}
|
|
103
121
|
/**
|
|
104
122
|
* This Merge multiple options at the same time, this can edit or create the options.
|
|
@@ -110,7 +128,42 @@ let Settings = Settings_1 = class Settings {
|
|
|
110
128
|
merge(options = { port: 3000, securePort: 4443 }) {
|
|
111
129
|
this.options = Object.assign(this.options, options);
|
|
112
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Get environment variables with optional type safety.
|
|
133
|
+
*
|
|
134
|
+
* Returns transformed environment variables if a transform function was provided
|
|
135
|
+
* to the @Env decorator, otherwise returns process.env.
|
|
136
|
+
*
|
|
137
|
+
* Use this method for type-safe access to environment variables when using
|
|
138
|
+
* the @Env decorator with a transform function (e.g., Zod validation).
|
|
139
|
+
*
|
|
140
|
+
* @template T - Type of environment variables (defaults to NodeJS.ProcessEnv)
|
|
141
|
+
* @returns Typed environment variables
|
|
142
|
+
* @since 2.0.1
|
|
143
|
+
* @summary Get type-safe environment variables
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* // Basic usage (returns process.env)
|
|
147
|
+
* const env = Settings.getInstance().getEnv();
|
|
148
|
+
* console.log(env.NODE_ENV);
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* // With type-safe transform from @Env decorator
|
|
152
|
+
* type Env = { PORT: number; DATABASE_URL: string };
|
|
153
|
+
*
|
|
154
|
+
* const env = Settings.getInstance().getEnv<Env>();
|
|
155
|
+
* console.log(env.PORT); // Type: number (transformed)
|
|
156
|
+
* console.log(env.DATABASE_URL); // Type: string (validated)
|
|
157
|
+
*/
|
|
158
|
+
getEnv() {
|
|
159
|
+
const transformed = (0, env_1.getTransformedEnv)();
|
|
160
|
+
if (transformed !== null) {
|
|
161
|
+
return transformed;
|
|
162
|
+
}
|
|
163
|
+
return process.env;
|
|
164
|
+
}
|
|
113
165
|
};
|
|
166
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
114
167
|
Settings.isolatedContext = new Map();
|
|
115
168
|
Settings = Settings_1 = tslib_1.__decorate([
|
|
116
169
|
(0, inversify_1.injectable)(),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ParameterDecorator } from '@expressive-tea/commons
|
|
1
|
+
import { type ParameterDecorator } from '@expressive-tea/commons';
|
|
2
2
|
/**
|
|
3
3
|
* Is passing directly to the decorated argument described <a href="http://expressjs.com/en/4x/api.html#req">here</a>.
|
|
4
4
|
* @decorator {ParameterDecorator} request - Assign express Request instance to parameter.
|