@expressive-tea/core 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.
Files changed (94) hide show
  1. package/.gitattributes +4 -0
  2. package/.swcrc +61 -0
  3. package/LICENSE +201 -0
  4. package/README.md +627 -0
  5. package/banner.png +0 -0
  6. package/classes/Boot.d.ts +145 -0
  7. package/classes/Boot.js +223 -0
  8. package/classes/Engine.d.ts +63 -0
  9. package/classes/Engine.js +90 -0
  10. package/classes/EngineRegistry.d.ts +154 -0
  11. package/classes/EngineRegistry.js +247 -0
  12. package/classes/LoadBalancer.d.ts +8 -0
  13. package/classes/LoadBalancer.js +28 -0
  14. package/classes/ProxyRoute.d.ts +14 -0
  15. package/classes/ProxyRoute.js +40 -0
  16. package/classes/Settings.d.ts +128 -0
  17. package/classes/Settings.js +172 -0
  18. package/decorators/annotations.d.ts +91 -0
  19. package/decorators/annotations.js +132 -0
  20. package/decorators/env.d.ts +145 -0
  21. package/decorators/env.js +177 -0
  22. package/decorators/health.d.ts +115 -0
  23. package/decorators/health.js +124 -0
  24. package/decorators/module.d.ts +34 -0
  25. package/decorators/module.js +39 -0
  26. package/decorators/proxy.d.ts +28 -0
  27. package/decorators/proxy.js +60 -0
  28. package/decorators/router.d.ts +199 -0
  29. package/decorators/router.js +252 -0
  30. package/decorators/server.d.ts +92 -0
  31. package/decorators/server.js +247 -0
  32. package/engines/constants/constants.d.ts +2 -0
  33. package/engines/constants/constants.js +5 -0
  34. package/engines/health/index.d.ts +120 -0
  35. package/engines/health/index.js +179 -0
  36. package/engines/http/index.d.ts +12 -0
  37. package/engines/http/index.js +59 -0
  38. package/engines/index.d.ts +32 -0
  39. package/engines/index.js +112 -0
  40. package/engines/socketio/index.d.ts +7 -0
  41. package/engines/socketio/index.js +30 -0
  42. package/engines/teacup/index.d.ts +27 -0
  43. package/engines/teacup/index.js +136 -0
  44. package/engines/teapot/index.d.ts +32 -0
  45. package/engines/teapot/index.js +167 -0
  46. package/engines/websocket/index.d.ts +9 -0
  47. package/engines/websocket/index.js +39 -0
  48. package/eslint.config.mjs +138 -0
  49. package/exceptions/BootLoaderExceptions.d.ts +26 -0
  50. package/exceptions/BootLoaderExceptions.js +31 -0
  51. package/exceptions/RequestExceptions.d.ts +75 -0
  52. package/exceptions/RequestExceptions.js +89 -0
  53. package/helpers/boot-helper.d.ts +7 -0
  54. package/helpers/boot-helper.js +84 -0
  55. package/helpers/decorators.d.ts +1 -0
  56. package/helpers/decorators.js +15 -0
  57. package/helpers/promise-helper.d.ts +1 -0
  58. package/helpers/promise-helper.js +6 -0
  59. package/helpers/server.d.ts +35 -0
  60. package/helpers/server.js +141 -0
  61. package/helpers/teapot-helper.d.ts +18 -0
  62. package/helpers/teapot-helper.js +88 -0
  63. package/helpers/websocket-helper.d.ts +3 -0
  64. package/helpers/websocket-helper.js +20 -0
  65. package/images/announcement-01.png +0 -0
  66. package/images/logo-sticky-01.png +0 -0
  67. package/images/logo-wp-01.png +0 -0
  68. package/images/logo.png +0 -0
  69. package/images/zero-oneit.png +0 -0
  70. package/interfaces/index.d.ts +4 -0
  71. package/interfaces/index.js +2 -0
  72. package/inversify.config.d.ts +9 -0
  73. package/inversify.config.js +8 -0
  74. package/libs/classNames.d.ts +1 -0
  75. package/libs/classNames.js +4 -0
  76. package/libs/utilities.d.ts +21910 -0
  77. package/libs/utilities.js +420 -0
  78. package/mixins/module.d.ts +45 -0
  79. package/mixins/module.js +71 -0
  80. package/mixins/proxy.d.ts +46 -0
  81. package/mixins/proxy.js +86 -0
  82. package/mixins/route.d.ts +48 -0
  83. package/mixins/route.js +96 -0
  84. package/package.json +137 -0
  85. package/services/DependencyInjection.d.ts +159 -0
  86. package/services/DependencyInjection.js +201 -0
  87. package/services/WebsocketService.d.ts +18 -0
  88. package/services/WebsocketService.js +47 -0
  89. package/types/core.d.ts +14 -0
  90. package/types/core.js +2 -0
  91. package/types/injection-types.d.ts +6 -0
  92. package/types/injection-types.js +10 -0
  93. package/types/inversify.d.ts +5 -0
  94. package/types/inversify.js +3 -0
@@ -0,0 +1,145 @@
1
+ import 'reflect-metadata';
2
+ import { type Express } from 'express';
3
+ import { type ExpressiveTeaApplication } from '@expressive-tea/commons';
4
+ import Settings from './Settings';
5
+ import { Container, type Newable, type ServiceIdentifier } from 'inversify';
6
+ /**
7
+ * Expressive Tea Application interface is the response from an started application, contains the express application
8
+ * and a node http server instance.
9
+ * @typedef {Object} ExpressiveTeaApplication
10
+ * @property {Express} application - Express Application Instance
11
+ * @property { HTTPServer } server - HTTP Server Object
12
+ * @summary Application Interface
13
+ */
14
+ /**
15
+ * <b>Bootstrap Server Engine Class</b> is an abstract class to provide the Expressive Tea engine and bootstraps tools.
16
+ * This is containing the logic and full functionality of Expressive Tea and only can be extended.
17
+ *
18
+ * @abstract
19
+ * @class Boot
20
+ * @summary Bootstrap Engine Class
21
+ */
22
+ declare abstract class Boot {
23
+ /**
24
+ * Maintain a reference to Singleton instance of Settings, if settings still does not initialized it will created
25
+ * automatically when extended class create a new instance.
26
+ *
27
+ * @type {Settings}
28
+ * @public
29
+ * @summary Server Settings instance reference
30
+ */
31
+ settings: Settings;
32
+ /**
33
+ * Automatically create an Express application instance which will be user to configure over all the boot stages.
34
+ * @type {Express}
35
+ * @private
36
+ * @readonly
37
+ * @summary Express Application instance internal property.
38
+ */
39
+ private readonly server;
40
+ private readonly containerDI;
41
+ /**
42
+ * Engine instances for cleanup during shutdown
43
+ * @type {ExpressiveTeaEngine[]}
44
+ * @private
45
+ * @since 2.0.0
46
+ */
47
+ private engines;
48
+ constructor();
49
+ /**
50
+ * Get Express Application
51
+ * @returns Express
52
+ */
53
+ getApplication(): Express;
54
+ /**
55
+ * Get the Dependency Injection container for this application instance.
56
+ * This container is scoped to the application and inherits from the global container.
57
+ *
58
+ * @returns {Container} The application's DI container
59
+ * @since 2.0.0
60
+ * @summary Get application DI container
61
+ *
62
+ * @example
63
+ * class MyApp extends Boot {
64
+ * async start() {
65
+ * const container = this.getContainer();
66
+ * container.bind(MyService).toSelf();
67
+ * return super.start();
68
+ * }
69
+ * }
70
+ */
71
+ getContainer(): Container;
72
+ /**
73
+ * Register a provider in the application's DI container.
74
+ * Providers registered here are available to all modules and engines in this application.
75
+ *
76
+ * @template T
77
+ * @param {ServiceIdentifier<T>} identifier - The service identifier (class or token)
78
+ * @param {Newable<T>} provider - The provider class to bind
79
+ * @returns {void}
80
+ * @since 2.0.0
81
+ * @summary Register a DI provider
82
+ *
83
+ * @example
84
+ * class MyApp extends Boot {
85
+ * constructor() {
86
+ * super();
87
+ * this.registerProvider(DatabaseService, DatabaseService);
88
+ * this.registerProvider('API_KEY', ApiKeyProvider);
89
+ * }
90
+ * }
91
+ */
92
+ registerProvider<T>(identifier: ServiceIdentifier<T>, provider: Newable<T>): void;
93
+ /**
94
+ * Register a constant value in the application's DI container.
95
+ *
96
+ * @template T
97
+ * @param {ServiceIdentifier<T>} identifier - The service identifier (usually a string or symbol)
98
+ * @param {T} value - The constant value to bind
99
+ * @returns {void}
100
+ * @since 2.0.0
101
+ * @summary Register a constant value
102
+ *
103
+ * @example
104
+ * class MyApp extends Boot {
105
+ * constructor() {
106
+ * super();
107
+ * this.registerConstant('DATABASE_URL', process.env.DATABASE_URL);
108
+ * this.registerConstant('MAX_CONNECTIONS', 100);
109
+ * }
110
+ * }
111
+ */
112
+ registerConstant<T>(identifier: ServiceIdentifier<T>, value: T): void;
113
+ /**
114
+ * Bootstrap and verify that all the required plugins are correctly configured and proceed to attach all the
115
+ * registered modules. <b>Remember</b> this is the unique method that must be decorated for the Register Module
116
+ * decorator.
117
+ * @summary Initialize and Bootstrap Server.
118
+ * @returns {Promise<ExpressiveTeaApplication>}
119
+ */
120
+ start(): Promise<ExpressiveTeaApplication>;
121
+ /**
122
+ * Gracefully stop the application and all engines
123
+ *
124
+ * Calls the stop() lifecycle method on all registered engines in reverse order
125
+ * (opposite of initialization order) to ensure proper cleanup.
126
+ *
127
+ * @returns {Promise<void>} Promise that resolves when all engines have stopped
128
+ * @since 2.0.0
129
+ * @summary Graceful shutdown
130
+ *
131
+ * @example
132
+ * ```typescript
133
+ * const app = new MyApp();
134
+ * await app.start();
135
+ *
136
+ * // Later, during shutdown
137
+ * await app.stop();
138
+ * ```
139
+ */
140
+ stop(): Promise<void>;
141
+ private initializeEngines;
142
+ private initializeHttp;
143
+ private initializeContainer;
144
+ }
145
+ export default Boot;
@@ -0,0 +1,223 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ require("reflect-metadata");
4
+ const inversify_config_1 = require("../inversify.config");
5
+ const express = require("express");
6
+ const Engine_1 = require("./Engine");
7
+ const Settings_1 = require("./Settings");
8
+ const fs = require("node:fs");
9
+ const http = require("node:http");
10
+ const https = require("node:https");
11
+ const inversify_1 = require("inversify");
12
+ const injection_types_1 = require("../types/injection-types");
13
+ const EngineRegistry_1 = require("./EngineRegistry");
14
+ /**
15
+ * Expressive Tea Application interface is the response from an started application, contains the express application
16
+ * and a node http server instance.
17
+ * @typedef {Object} ExpressiveTeaApplication
18
+ * @property {Express} application - Express Application Instance
19
+ * @property { HTTPServer } server - HTTP Server Object
20
+ * @summary Application Interface
21
+ */
22
+ /**
23
+ * <b>Bootstrap Server Engine Class</b> is an abstract class to provide the Expressive Tea engine and bootstraps tools.
24
+ * This is containing the logic and full functionality of Expressive Tea and only can be extended.
25
+ *
26
+ * @abstract
27
+ * @class Boot
28
+ * @summary Bootstrap Engine Class
29
+ */
30
+ class Boot {
31
+ constructor() {
32
+ /**
33
+ * Automatically create an Express application instance which will be user to configure over all the boot stages.
34
+ * @type {Express}
35
+ * @private
36
+ * @readonly
37
+ * @summary Express Application instance internal property.
38
+ */
39
+ this.server = express();
40
+ this.containerDI = new inversify_1.Container({ parent: inversify_config_1.default });
41
+ /**
42
+ * Engine instances for cleanup during shutdown
43
+ * @type {ExpressiveTeaEngine[]}
44
+ * @private
45
+ * @since 2.0.0
46
+ */
47
+ this.engines = [];
48
+ this.settings = Settings_1.default.getInstance(this);
49
+ }
50
+ /**
51
+ * Get Express Application
52
+ * @returns Express
53
+ */
54
+ getApplication() {
55
+ return this.server;
56
+ }
57
+ /**
58
+ * Get the Dependency Injection container for this application instance.
59
+ * This container is scoped to the application and inherits from the global container.
60
+ *
61
+ * @returns {Container} The application's DI container
62
+ * @since 2.0.0
63
+ * @summary Get application DI container
64
+ *
65
+ * @example
66
+ * class MyApp extends Boot {
67
+ * async start() {
68
+ * const container = this.getContainer();
69
+ * container.bind(MyService).toSelf();
70
+ * return super.start();
71
+ * }
72
+ * }
73
+ */
74
+ getContainer() {
75
+ return this.containerDI;
76
+ }
77
+ /**
78
+ * Register a provider in the application's DI container.
79
+ * Providers registered here are available to all modules and engines in this application.
80
+ *
81
+ * @template T
82
+ * @param {ServiceIdentifier<T>} identifier - The service identifier (class or token)
83
+ * @param {Newable<T>} provider - The provider class to bind
84
+ * @returns {void}
85
+ * @since 2.0.0
86
+ * @summary Register a DI provider
87
+ *
88
+ * @example
89
+ * class MyApp extends Boot {
90
+ * constructor() {
91
+ * super();
92
+ * this.registerProvider(DatabaseService, DatabaseService);
93
+ * this.registerProvider('API_KEY', ApiKeyProvider);
94
+ * }
95
+ * }
96
+ */
97
+ registerProvider(identifier, provider) {
98
+ if (!this.containerDI.isBound(identifier)) {
99
+ this.containerDI.bind(identifier).to(provider);
100
+ }
101
+ }
102
+ /**
103
+ * Register a constant value in the application's DI container.
104
+ *
105
+ * @template T
106
+ * @param {ServiceIdentifier<T>} identifier - The service identifier (usually a string or symbol)
107
+ * @param {T} value - The constant value to bind
108
+ * @returns {void}
109
+ * @since 2.0.0
110
+ * @summary Register a constant value
111
+ *
112
+ * @example
113
+ * class MyApp extends Boot {
114
+ * constructor() {
115
+ * super();
116
+ * this.registerConstant('DATABASE_URL', process.env.DATABASE_URL);
117
+ * this.registerConstant('MAX_CONNECTIONS', 100);
118
+ * }
119
+ * }
120
+ */
121
+ registerConstant(identifier, value) {
122
+ if (!this.containerDI.isBound(identifier)) {
123
+ this.containerDI.bind(identifier).toConstantValue(value);
124
+ }
125
+ }
126
+ /**
127
+ * Bootstrap and verify that all the required plugins are correctly configured and proceed to attach all the
128
+ * registered modules. <b>Remember</b> this is the unique method that must be decorated for the Register Module
129
+ * decorator.
130
+ * @summary Initialize and Bootstrap Server.
131
+ * @returns {Promise<ExpressiveTeaApplication>}
132
+ */
133
+ async start() {
134
+ // Initialize Server
135
+ const [server, secureServer] = this.initializeHttp();
136
+ // Injectables
137
+ this.initializeContainer(server, secureServer);
138
+ // Lazy load engines to avoid circular dependency
139
+ // This ensures engines are only loaded when needed
140
+ if (EngineRegistry_1.default.getAllEngines().length === 0) {
141
+ await Promise.resolve().then(() => require('../engines'));
142
+ }
143
+ // Get registered engines from EngineRegistry (automatically filtered and sorted by dependencies)
144
+ const registeredEngines = EngineRegistry_1.default.getRegisteredEngines(this, this.settings);
145
+ this.initializeEngines(registeredEngines);
146
+ // Resolve Engines
147
+ const readyEngines = registeredEngines.map(Engine => {
148
+ const instance = this.containerDI.get(Engine);
149
+ return instance;
150
+ });
151
+ // Store engine instances for cleanup
152
+ this.engines = readyEngines;
153
+ // Initialize Engines
154
+ try {
155
+ await Engine_1.default.exec(readyEngines.reverse(), 'init');
156
+ await Engine_1.default.exec(readyEngines, 'start');
157
+ return ({ application: this.server, server, secureServer });
158
+ }
159
+ catch (e) {
160
+ // If anything failed during engine initialization or start, ensure servers are closed to avoid leaking
161
+ server === null || server === void 0 ? void 0 : server.close();
162
+ secureServer === null || secureServer === void 0 ? void 0 : secureServer.close();
163
+ throw e;
164
+ }
165
+ }
166
+ /**
167
+ * Gracefully stop the application and all engines
168
+ *
169
+ * Calls the stop() lifecycle method on all registered engines in reverse order
170
+ * (opposite of initialization order) to ensure proper cleanup.
171
+ *
172
+ * @returns {Promise<void>} Promise that resolves when all engines have stopped
173
+ * @since 2.0.0
174
+ * @summary Graceful shutdown
175
+ *
176
+ * @example
177
+ * ```typescript
178
+ * const app = new MyApp();
179
+ * await app.start();
180
+ *
181
+ * // Later, during shutdown
182
+ * await app.stop();
183
+ * ```
184
+ */
185
+ async stop() {
186
+ if (this.engines.length === 0) {
187
+ return;
188
+ }
189
+ // Stop engines in reverse order (opposite of initialization)
190
+ await Engine_1.default.exec([...this.engines].reverse(), 'stop');
191
+ // Clear engine references
192
+ this.engines = [];
193
+ }
194
+ initializeEngines(registeredEngines) {
195
+ for (const Engine of registeredEngines) {
196
+ this.containerDI.bind(Engine).to(Engine);
197
+ }
198
+ }
199
+ initializeHttp() {
200
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
201
+ const privateKey = this.settings.get('privateKey');
202
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
203
+ const certificate = this.settings.get('certificate');
204
+ const server = http.createServer(this.server);
205
+ const secureServer = privateKey &&
206
+ certificate
207
+ ? https.createServer({
208
+ cert: fs.readFileSync(certificate).toString('utf-8'),
209
+ key: fs.readFileSync(privateKey).toString('utf-8')
210
+ }, this.server)
211
+ : undefined;
212
+ return [server, secureServer];
213
+ }
214
+ initializeContainer(server, secureServer) {
215
+ this.containerDI.bind(injection_types_1.TYPES.Server).toConstantValue(server);
216
+ if (secureServer) {
217
+ this.containerDI.bind(injection_types_1.TYPES.SecureServer).toConstantValue(secureServer);
218
+ }
219
+ this.containerDI.bind(injection_types_1.TYPES.Context).toConstantValue(this);
220
+ this.containerDI.bind(injection_types_1.TYPES.Settings).toConstantValue(this.settings);
221
+ }
222
+ }
223
+ exports.default = Boot;
@@ -0,0 +1,63 @@
1
+ import Settings from './Settings';
2
+ import type Boot from './Boot';
3
+ import { Server as HttpServer } from 'node:http';
4
+ import { Server as HttpsServer } from 'node:https';
5
+ export default class ExpressiveTeaEngine {
6
+ protected readonly context: Boot;
7
+ protected readonly server: HttpServer;
8
+ protected readonly serverSecure: HttpsServer;
9
+ protected readonly settings: Settings;
10
+ constructor(context: Boot, server: HttpServer, serverSecure: HttpsServer, settings: Settings);
11
+ /**
12
+ * Execute a lifecycle method across all registered engines
13
+ *
14
+ * @param {ExpressiveTeaEngine[]} availableEngines - Array of engine instances
15
+ * @param {string} method - Method name to execute (e.g., 'init', 'start', 'stop')
16
+ * @returns {Promise<unknown[]>} Array of results from all engines
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * await ExpressiveTeaEngine.exec(engines, 'init');
21
+ * await ExpressiveTeaEngine.exec(engines, 'start');
22
+ * ```
23
+ * @since 1.0.0
24
+ */
25
+ static exec(availableEngines: ExpressiveTeaEngine[], method: string): Promise<unknown[]>;
26
+ /**
27
+ * Determine if this engine can be registered in the current context
28
+ *
29
+ * Override this method to conditionally enable/disable the engine based on
30
+ * settings or environment. Default returns false to prevent accidental registration.
31
+ *
32
+ * @param {Boot} [ctx] - Boot context
33
+ * @param {Settings} [settings] - Application settings
34
+ * @returns {boolean} True if engine can be registered
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * static canRegister(ctx?: Boot, settings?: Settings): boolean {
39
+ * return settings?.get('enableMyEngine') === true;
40
+ * }
41
+ * ```
42
+ * @since 1.0.0
43
+ */
44
+ static canRegister(ctx?: Boot, settings?: Settings): boolean;
45
+ /**
46
+ * Graceful shutdown lifecycle method
47
+ *
48
+ * Override this method to implement cleanup logic when the application stops.
49
+ * This is called during graceful shutdown to close connections, clean up resources, etc.
50
+ *
51
+ * @returns {Promise<void>} Promise that resolves when cleanup is complete
52
+ *
53
+ * @example
54
+ * ```typescript
55
+ * async stop(): Promise<void> {
56
+ * await this.closeConnections();
57
+ * await this.cleanup();
58
+ * }
59
+ * ```
60
+ * @since 2.0.0
61
+ */
62
+ stop(): Promise<void>;
63
+ }
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const inversify_1 = require("inversify");
5
+ const Settings_1 = require("./Settings");
6
+ const node_http_1 = require("node:http");
7
+ const node_https_1 = require("node:https");
8
+ const injection_types_1 = require("../types/injection-types");
9
+ let ExpressiveTeaEngine = class ExpressiveTeaEngine {
10
+ constructor(context, server, serverSecure, settings) {
11
+ this.context = context;
12
+ this.server = server;
13
+ this.serverSecure = serverSecure;
14
+ this.settings = settings;
15
+ }
16
+ /**
17
+ * Execute a lifecycle method across all registered engines
18
+ *
19
+ * @param {ExpressiveTeaEngine[]} availableEngines - Array of engine instances
20
+ * @param {string} method - Method name to execute (e.g., 'init', 'start', 'stop')
21
+ * @returns {Promise<unknown[]>} Array of results from all engines
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * await ExpressiveTeaEngine.exec(engines, 'init');
26
+ * await ExpressiveTeaEngine.exec(engines, 'start');
27
+ * ```
28
+ * @since 1.0.0
29
+ */
30
+ static exec(availableEngines, method) {
31
+ return Promise.all(availableEngines
32
+ .filter(engine => typeof engine[method] === 'function')
33
+ .map(engine => (engine[method])()));
34
+ }
35
+ /**
36
+ * Determine if this engine can be registered in the current context
37
+ *
38
+ * Override this method to conditionally enable/disable the engine based on
39
+ * settings or environment. Default returns false to prevent accidental registration.
40
+ *
41
+ * @param {Boot} [ctx] - Boot context
42
+ * @param {Settings} [settings] - Application settings
43
+ * @returns {boolean} True if engine can be registered
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * static canRegister(ctx?: Boot, settings?: Settings): boolean {
48
+ * return settings?.get('enableMyEngine') === true;
49
+ * }
50
+ * ```
51
+ * @since 1.0.0
52
+ */
53
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
54
+ static canRegister(ctx, settings) {
55
+ return false;
56
+ }
57
+ /**
58
+ * Graceful shutdown lifecycle method
59
+ *
60
+ * Override this method to implement cleanup logic when the application stops.
61
+ * This is called during graceful shutdown to close connections, clean up resources, etc.
62
+ *
63
+ * @returns {Promise<void>} Promise that resolves when cleanup is complete
64
+ *
65
+ * @example
66
+ * ```typescript
67
+ * async stop(): Promise<void> {
68
+ * await this.closeConnections();
69
+ * await this.cleanup();
70
+ * }
71
+ * ```
72
+ * @since 2.0.0
73
+ */
74
+ async stop() {
75
+ // Default implementation does nothing
76
+ // Engines can override to implement cleanup
77
+ }
78
+ };
79
+ ExpressiveTeaEngine = tslib_1.__decorate([
80
+ (0, inversify_1.injectable)(),
81
+ tslib_1.__param(0, (0, inversify_1.inject)(injection_types_1.TYPES.Context)),
82
+ tslib_1.__param(1, (0, inversify_1.inject)(injection_types_1.TYPES.Server)),
83
+ tslib_1.__param(2, (0, inversify_1.inject)(injection_types_1.TYPES.SecureServer)),
84
+ tslib_1.__param(2, (0, inversify_1.optional)()),
85
+ tslib_1.__param(3, (0, inversify_1.inject)(injection_types_1.TYPES.Settings)),
86
+ tslib_1.__metadata("design:paramtypes", [Function, node_http_1.Server,
87
+ node_https_1.Server,
88
+ Settings_1.default])
89
+ ], ExpressiveTeaEngine);
90
+ exports.default = ExpressiveTeaEngine;
@@ -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
+ }