@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,420 @@
1
+ "use strict";
2
+ /**
3
+ * Native Utility Functions
4
+ *
5
+ * Lightweight replacements for lodash functions using native JavaScript/TypeScript.
6
+ * These utilities provide common object, array, and type manipulation functions
7
+ * without external dependencies.
8
+ *
9
+ * @module libs/utilities
10
+ * @since 2.0.0
11
+ */
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.get = get;
14
+ exports.set = set;
15
+ exports.has = has;
16
+ exports.pick = pick;
17
+ exports.find = find;
18
+ exports.size = size;
19
+ exports.merge = merge;
20
+ exports.isUndefined = isUndefined;
21
+ exports.isNil = isNil;
22
+ exports.isNumber = isNumber;
23
+ exports.sortBy = sortBy;
24
+ exports.orderBy = orderBy;
25
+ exports.indexOf = indexOf;
26
+ exports.includes = includes;
27
+ exports.last = last;
28
+ exports.inRange = inRange;
29
+ exports.chain = chain;
30
+ /* eslint-disable @typescript-eslint/no-explicit-any */
31
+ /**
32
+ * Get a value from an object using a path string or number
33
+ *
34
+ * @param {any} obj - The object to query
35
+ * @param {string | string[] | number} path - The path of the property to get (e.g., 'a.b.c' or ['a', 'b', 'c'] or 0)
36
+ * @param {any} [defaultValue] - The value returned if the resolved value is undefined
37
+ * @returns {any} The resolved value or defaultValue
38
+ *
39
+ * @example
40
+ * get({ a: { b: { c: 3 } } }, 'a.b.c') // => 3
41
+ * get({ a: { b: { c: 3 } } }, 'a.b.x', 'default') // => 'default'
42
+ * get([1, 2, 3], 0) // => 1
43
+ */
44
+ function get(obj, path, defaultValue) {
45
+ if (!obj || (typeof obj !== 'object' && !Array.isArray(obj))) {
46
+ return defaultValue;
47
+ }
48
+ // Handle undefined or null path
49
+ if (path === undefined || path === null) {
50
+ return defaultValue;
51
+ }
52
+ // Handle numeric index directly (for arrays)
53
+ if (typeof path === 'number') {
54
+ const value = obj[path];
55
+ return value === undefined ? defaultValue : value;
56
+ }
57
+ const pathArray = Array.isArray(path) ? path : path.split('.');
58
+ let current = obj;
59
+ for (const key of pathArray) {
60
+ if (current === null || current === undefined) {
61
+ return defaultValue;
62
+ }
63
+ current = current[key];
64
+ }
65
+ return current === undefined ? defaultValue : current;
66
+ }
67
+ /**
68
+ * Set a value in an object using a path string
69
+ *
70
+ * @param {any} obj - The object to modify
71
+ * @param {string | string[]} path - The path of the property to set
72
+ * @param {any} value - The value to set
73
+ * @returns {any} Returns the object
74
+ *
75
+ * @example
76
+ * const obj = {};
77
+ * set(obj, 'a.b.c', 3);
78
+ * // obj is now { a: { b: { c: 3 } } }
79
+ */
80
+ function set(obj, path, value) {
81
+ if (!obj || typeof obj !== 'object') {
82
+ return obj;
83
+ }
84
+ const pathArray = Array.isArray(path) ? path : path.split('.');
85
+ let current = obj;
86
+ for (let i = 0; i < pathArray.length - 1; i++) {
87
+ const key = pathArray[i];
88
+ if (!(key in current) || typeof current[key] !== 'object') {
89
+ current[key] = {};
90
+ }
91
+ current = current[key];
92
+ }
93
+ current[pathArray[pathArray.length - 1]] = value;
94
+ return obj;
95
+ }
96
+ /**
97
+ * Check if a path exists in an object
98
+ *
99
+ * @param {any} obj - The object to query
100
+ * @param {string | string[]} path - The path to check
101
+ * @returns {boolean} Returns true if path exists, else false
102
+ *
103
+ * @example
104
+ * has({ a: { b: 2 } }, 'a.b') // => true
105
+ * has({ a: { b: 2 } }, 'a.c') // => false
106
+ */
107
+ function has(obj, path) {
108
+ if (!obj || typeof obj !== 'object') {
109
+ return false;
110
+ }
111
+ // Handle undefined or null path
112
+ if (path === undefined || path === null) {
113
+ return false;
114
+ }
115
+ const pathArray = Array.isArray(path) ? path : path.split('.');
116
+ let current = obj;
117
+ for (const key of pathArray) {
118
+ if (!current || typeof current !== 'object' || !(key in current)) {
119
+ return false;
120
+ }
121
+ current = current[key];
122
+ }
123
+ return true;
124
+ }
125
+ /**
126
+ * Create an object composed of picked properties
127
+ *
128
+ * @param {any} obj - The source object
129
+ * @param {string[]} keys - The property keys to pick
130
+ * @returns {any} Returns the new object
131
+ *
132
+ * @example
133
+ * pick({ a: 1, b: 2, c: 3 }, ['a', 'c']) // => { a: 1, c: 3 }
134
+ */
135
+ function pick(obj, keys) {
136
+ if (!obj || typeof obj !== 'object') {
137
+ return {};
138
+ }
139
+ const result = {};
140
+ for (const key of keys) {
141
+ if (key in obj) {
142
+ result[key] = obj[key];
143
+ }
144
+ }
145
+ return result;
146
+ }
147
+ /**
148
+ * Find the first element in an array that matches a predicate or object pattern
149
+ *
150
+ * @param {any[]} array - The array to search
151
+ * @param {((item: any) => boolean) | object} predicate - The function or object pattern to match
152
+ * @returns {any} Returns the matched element, else undefined
153
+ *
154
+ * @example
155
+ * find([1, 2, 3, 4], n => n > 2) // => 3
156
+ * find([{ a: 1 }, { a: 2 }], { a: 2 }) // => { a: 2 }
157
+ */
158
+ function find(array, predicate) {
159
+ if (!Array.isArray(array)) {
160
+ return undefined;
161
+ }
162
+ // If predicate is a function, use it directly
163
+ if (typeof predicate === 'function') {
164
+ return array.find(predicate);
165
+ }
166
+ // If predicate is an object, match properties
167
+ if (predicate && typeof predicate === 'object') {
168
+ return array.find((item) => {
169
+ if (!item || typeof item !== 'object') {
170
+ return false;
171
+ }
172
+ return Object.keys(predicate).every((key) => item[key] === predicate[key]);
173
+ });
174
+ }
175
+ return undefined;
176
+ }
177
+ /**
178
+ * Get the size of a collection
179
+ *
180
+ * @param {any} collection - The collection to inspect
181
+ * @returns {number} Returns the collection size
182
+ *
183
+ * @example
184
+ * size([1, 2, 3]) // => 3
185
+ * size({ a: 1, b: 2 }) // => 2
186
+ * size('hello') // => 5
187
+ */
188
+ function size(collection) {
189
+ if (!collection) {
190
+ return 0;
191
+ }
192
+ if (Array.isArray(collection) || typeof collection === 'string') {
193
+ return collection.length;
194
+ }
195
+ if (typeof collection === 'object') {
196
+ return Object.keys(collection).length;
197
+ }
198
+ return 0;
199
+ }
200
+ /**
201
+ * Deep merge objects
202
+ *
203
+ * @param {any} target - The destination object
204
+ * @param {...any} sources - The source objects
205
+ * @returns {any} Returns the merged object
206
+ *
207
+ * @example
208
+ * merge({ a: 1 }, { b: 2 }, { c: 3 }) // => { a: 1, b: 2, c: 3 }
209
+ * merge({ a: { b: 1 } }, { a: { c: 2 } }) // => { a: { b: 1, c: 2 } }
210
+ */
211
+ function merge(target, ...sources) {
212
+ if (!target || typeof target !== 'object') {
213
+ return target;
214
+ }
215
+ for (const source of sources) {
216
+ if (!source || typeof source !== 'object') {
217
+ continue;
218
+ }
219
+ for (const key in source) {
220
+ if (Object.prototype.hasOwnProperty.call(source, key)) {
221
+ const sourceValue = source[key];
222
+ const targetValue = target[key];
223
+ if (sourceValue &&
224
+ typeof sourceValue === 'object' &&
225
+ !Array.isArray(sourceValue) &&
226
+ targetValue &&
227
+ typeof targetValue === 'object' &&
228
+ !Array.isArray(targetValue)) {
229
+ target[key] = merge({}, targetValue, sourceValue);
230
+ }
231
+ else {
232
+ target[key] = sourceValue;
233
+ }
234
+ }
235
+ }
236
+ }
237
+ return target;
238
+ }
239
+ /**
240
+ * Check if a value is undefined
241
+ *
242
+ * @param {any} value - The value to check
243
+ * @returns {boolean} Returns true if value is undefined, else false
244
+ *
245
+ * @example
246
+ * isUndefined(undefined) // => true
247
+ * isUndefined(null) // => false
248
+ */
249
+ function isUndefined(value) {
250
+ return value === undefined;
251
+ }
252
+ /**
253
+ * Check if a value is null or undefined
254
+ *
255
+ * @param {any} value - The value to check
256
+ * @returns {boolean} Returns true if value is null or undefined, else false
257
+ *
258
+ * @example
259
+ * isNil(null) // => true
260
+ * isNil(undefined) // => true
261
+ * isNil(0) // => false
262
+ */
263
+ function isNil(value) {
264
+ return value === null || value === undefined;
265
+ }
266
+ /**
267
+ * Check if a value is a number
268
+ *
269
+ * @param {any} value - The value to check
270
+ * @returns {boolean} Returns true if value is a number, else false
271
+ *
272
+ * @example
273
+ * isNumber(3) // => true
274
+ * isNumber('3') // => false
275
+ * isNumber(NaN) // => true
276
+ */
277
+ function isNumber(value) {
278
+ return typeof value === 'number';
279
+ }
280
+ /**
281
+ * Sort an array by one or more properties
282
+ *
283
+ * @param {any[]} array - The array to sort
284
+ * @param {string | string[]} properties - The property name(s) to sort by
285
+ * @param {('asc'|'desc')[]} [orders] - The sort orders for each property
286
+ * @returns {any[]} Returns the sorted array
287
+ *
288
+ * @example
289
+ * sortBy([{ a: 2 }, { a: 1 }], 'a') // => [{ a: 1 }, { a: 2 }]
290
+ * sortBy([{ a: 2, b: 1 }, { a: 1, b: 2 }], ['a'], ['asc'])
291
+ * // => [{ a: 1, b: 2 }, { a: 2, b: 1 }]
292
+ */
293
+ function sortBy(array, properties, orders = []) {
294
+ if (!Array.isArray(array)) {
295
+ return [];
296
+ }
297
+ const props = Array.isArray(properties) ? properties : [properties];
298
+ return [...array].sort((a, b) => {
299
+ for (let i = 0; i < props.length; i++) {
300
+ const prop = props[i];
301
+ const order = orders[i] || 'asc';
302
+ const aVal = get(a, prop);
303
+ const bVal = get(b, prop);
304
+ if (aVal < bVal) {
305
+ return order === 'asc' ? -1 : 1;
306
+ }
307
+ if (aVal > bVal) {
308
+ return order === 'asc' ? 1 : -1;
309
+ }
310
+ }
311
+ return 0;
312
+ });
313
+ }
314
+ /**
315
+ * Alias for sortBy to match lodash orderBy API
316
+ *
317
+ * @param {any[]} array - The array to sort
318
+ * @param {string[]} properties - The property names to sort by
319
+ * @param {('asc'|'desc')[]} [orders] - The sort orders for each property
320
+ * @returns {any[]} Returns the sorted array
321
+ */
322
+ function orderBy(array, properties, orders = []) {
323
+ return sortBy(array, properties, orders);
324
+ }
325
+ /**
326
+ * Get the index of a value in an array
327
+ *
328
+ * @param {any[]} array - The array to search
329
+ * @param {any} value - The value to search for
330
+ * @returns {number} Returns the index of the value, or -1 if not found
331
+ *
332
+ * @example
333
+ * indexOf([1, 2, 3], 2) // => 1
334
+ * indexOf([1, 2, 3], 4) // => -1
335
+ */
336
+ function indexOf(array, value) {
337
+ if (!Array.isArray(array)) {
338
+ return -1;
339
+ }
340
+ return array.indexOf(value);
341
+ }
342
+ /**
343
+ * Check if an array includes a value
344
+ *
345
+ * @param {any[]} array - The array to search
346
+ * @param {any} value - The value to search for
347
+ * @returns {boolean} Returns true if value is found, else false
348
+ *
349
+ * @example
350
+ * includes([1, 2, 3], 2) // => true
351
+ * includes([1, 2, 3], 4) // => false
352
+ */
353
+ function includes(array, value) {
354
+ if (!Array.isArray(array)) {
355
+ return false;
356
+ }
357
+ return array.includes(value);
358
+ }
359
+ /**
360
+ * Get the last element of an array
361
+ *
362
+ * @param {any[]} array - The array to query
363
+ * @returns {any} Returns the last element of the array, or undefined
364
+ *
365
+ * @example
366
+ * last([1, 2, 3]) // => 3
367
+ * last([]) // => undefined
368
+ */
369
+ function last(array) {
370
+ if (!Array.isArray(array) || array.length === 0) {
371
+ return undefined;
372
+ }
373
+ return array[array.length - 1];
374
+ }
375
+ /**
376
+ * Check if a number is within a range
377
+ *
378
+ * @param {number} value - The number to check
379
+ * @param {number} start - The start of the range (inclusive)
380
+ * @param {number} end - The end of the range (exclusive)
381
+ * @returns {boolean} Returns true if value is in range, else false
382
+ *
383
+ * @example
384
+ * inRange(3, 2, 4) // => true
385
+ * inRange(4, 2, 4) // => false
386
+ * inRange(1, 2, 4) // => false
387
+ */
388
+ function inRange(value, start, end) {
389
+ if (typeof value !== 'number' || typeof start !== 'number' || typeof end !== 'number') {
390
+ return false;
391
+ }
392
+ return value >= start && value < end;
393
+ }
394
+ /**
395
+ * Create a chainable wrapper (simplified lodash chain)
396
+ *
397
+ * @param {any} value - The value to wrap
398
+ * @returns {object} Returns the chainable wrapper
399
+ *
400
+ * @example
401
+ * chain([1, 2, 3])
402
+ * .map(n => n * 2)
403
+ * .filter(n => n > 2)
404
+ * .value()
405
+ * // => [4, 6]
406
+ */
407
+ function chain(value) {
408
+ return {
409
+ value: () => value,
410
+ map: (fn) => chain(Array.isArray(value) ? value.map(fn) : value),
411
+ filter: (fn) => chain(Array.isArray(value) ? value.filter(fn) : value),
412
+ find: (fn) => chain(Array.isArray(value) ? value.find(fn) : undefined),
413
+ sortBy: (properties, orders) => chain(Array.isArray(value) ? sortBy(value, properties, orders) : value),
414
+ size: () => chain(size(value)),
415
+ pick: (keys) => chain(pick(value, keys)),
416
+ get: (path, defaultValue) => chain(get(value, path, defaultValue)),
417
+ has: (path) => chain(has(value, path)),
418
+ thru: (fn) => chain(fn(value))
419
+ };
420
+ }
@@ -0,0 +1,45 @@
1
+ import type { Constructor } from '../types/core';
2
+ import type { ExpressiveTeaModuleProps } from '@expressive-tea/commons';
3
+ import { type Express, Router } from 'express';
4
+ /**
5
+ * Type definition for a modulized class
6
+ * Represents a class that has been enhanced with Expressive Tea module capabilities
7
+ * @template TBase - The base constructor type being extended
8
+ * @since 2.0.0
9
+ */
10
+ export type ModulizedClass<TBase extends Constructor> = TBase & (new (...args: any[]) => {
11
+ /** Module configuration and metadata */
12
+ readonly settings: ExpressiveTeaModuleProps;
13
+ /** Express router for this module */
14
+ readonly router: Router;
15
+ /** Instantiated controllers for this module */
16
+ readonly controllers: any[];
17
+ /** Register module routes with the Express application */
18
+ __register(server: Express): void;
19
+ });
20
+ /**
21
+ * Modulize mixin - Adds Expressive Tea module capabilities to a class
22
+ *
23
+ * Transforms a regular class into an Expressive Tea module with:
24
+ * - Dependency injection support
25
+ * - Express router management
26
+ * - Controller instantiation and registration
27
+ * - Module mounting capabilities
28
+ *
29
+ * @template TBase - The base constructor type to extend
30
+ * @param {TBase} Base - The base class to extend
31
+ * @param {ExpressiveTeaModuleProps} options - Module configuration options
32
+ * @returns {ModulizedClass<TBase>} The enhanced class with module capabilities
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * class MyModule {}
37
+ * const ModulizedMyModule = Modulize(MyModule, {
38
+ * mountpoint: '/api',
39
+ * controllers: [UserController],
40
+ * providers: [UserService]
41
+ * });
42
+ * ```
43
+ * @since 2.0.0
44
+ */
45
+ export declare function Modulize<TBase extends Constructor>(Base: TBase, options: ExpressiveTeaModuleProps): ModulizedClass<TBase>;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Modulize = Modulize;
4
+ const tslib_1 = require("tslib");
5
+ const express_1 = require("express");
6
+ const DependencyInjection_1 = require("../services/DependencyInjection");
7
+ const inversify_1 = require("inversify");
8
+ /**
9
+ * Modulize mixin - Adds Expressive Tea module capabilities to a class
10
+ *
11
+ * Transforms a regular class into an Expressive Tea module with:
12
+ * - Dependency injection support
13
+ * - Express router management
14
+ * - Controller instantiation and registration
15
+ * - Module mounting capabilities
16
+ *
17
+ * @template TBase - The base constructor type to extend
18
+ * @param {TBase} Base - The base class to extend
19
+ * @param {ExpressiveTeaModuleProps} options - Module configuration options
20
+ * @returns {ModulizedClass<TBase>} The enhanced class with module capabilities
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * class MyModule {}
25
+ * const ModulizedMyModule = Modulize(MyModule, {
26
+ * mountpoint: '/api',
27
+ * controllers: [UserController],
28
+ * providers: [UserService]
29
+ * });
30
+ * ```
31
+ * @since 2.0.0
32
+ */
33
+ function Modulize(Base, options) {
34
+ let ExpressiveTeaModule = class ExpressiveTeaModule extends Base {
35
+ constructor(...args) {
36
+ var _a;
37
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
38
+ super(...args);
39
+ this.settings = options;
40
+ this.router = (0, express_1.Router)();
41
+ for (const Provider of (_a = options.providers) !== null && _a !== void 0 ? _a : []) {
42
+ DependencyInjection_1.default.setProvider(Provider);
43
+ }
44
+ this.controllers = options.controllers.map((C) => (0, DependencyInjection_1.getInstanceOf)(C));
45
+ }
46
+ __register(server) {
47
+ for (const controller of this.controllers) {
48
+ controller.__mount(this.router);
49
+ }
50
+ server.use(this.settings.mountpoint, this.router);
51
+ }
52
+ };
53
+ ExpressiveTeaModule = tslib_1.__decorate([
54
+ (0, inversify_1.injectable)('Singleton'),
55
+ (0, inversify_1.injectFromBase)({ extendConstructorArguments: true }),
56
+ tslib_1.__metadata("design:paramtypes", [Object])
57
+ ], ExpressiveTeaModule);
58
+ // Bind the original class to the wrapped class in the DI container
59
+ // This ensures that when the application requests the original module class,
60
+ // it receives an instance of the wrapped ExpressiveTeaModule class instead
61
+ try {
62
+ if (DependencyInjection_1.default.Container.isBound(Base)) {
63
+ DependencyInjection_1.default.Container.unbind(Base);
64
+ }
65
+ DependencyInjection_1.default.Container.bind(Base).to(ExpressiveTeaModule);
66
+ }
67
+ catch (_a) {
68
+ // Binding may fail in some contexts, but that's okay - the class is still usable
69
+ }
70
+ return ExpressiveTeaModule;
71
+ }
@@ -0,0 +1,46 @@
1
+ import { Constructor } from '../types/core';
2
+ import type { Express, RequestHandler } from 'express';
3
+ /**
4
+ * Type definition for a proxified class
5
+ * Represents a class that has been enhanced with Expressive Tea proxy capabilities
6
+ * @template TBase - The base constructor type being extended
7
+ * @since 2.0.0
8
+ */
9
+ export type ProxifiedClass<TBase extends Constructor> = TBase & (new (...args: any[]) => {
10
+ /** Source path to proxy from */
11
+ readonly source: string;
12
+ /** Target URL to proxy to */
13
+ readonly target: string;
14
+ /** Express HTTP proxy request handler */
15
+ readonly proxyHandler: RequestHandler;
16
+ /** Register proxy route with the Express application */
17
+ __register(server: Express): void;
18
+ });
19
+ /**
20
+ * Proxify mixin - Adds Expressive Tea HTTP proxy capabilities to a class
21
+ *
22
+ * Transforms a regular class into an Expressive Tea proxy with:
23
+ * - HTTP proxy configuration
24
+ * - Request/response transformation
25
+ * - Custom headers and options
26
+ * - Automatic proxy registration
27
+ *
28
+ * @template TBase - The base constructor type to extend
29
+ * @param {TBase} Base - The base class to extend
30
+ * @param {string} source - The source path to proxy from (e.g., '/api')
31
+ * @param {string} targetUrl - The target URL to proxy to (e.g., 'http://api.example.com')
32
+ * @returns {ProxifiedClass<TBase>} The enhanced class with proxy capabilities
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * @Proxy('/api', 'http://api.example.com')
37
+ * class ApiProxy {
38
+ * @ProxyHost()
39
+ * getHost() {
40
+ * return 'http://api.example.com';
41
+ * }
42
+ * }
43
+ * ```
44
+ * @since 2.0.0
45
+ */
46
+ export declare function Proxify<TBase extends Constructor>(Base: TBase, source: string, targetUrl: string): ProxifiedClass<TBase>;
@@ -0,0 +1,86 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Proxify = Proxify;
4
+ const tslib_1 = require("tslib");
5
+ const httpProxy = require("express-http-proxy");
6
+ const commons_1 = require("@expressive-tea/commons");
7
+ const commons_2 = require("@expressive-tea/commons");
8
+ const utilities_1 = require("../libs/utilities");
9
+ const commons_3 = require("@expressive-tea/commons");
10
+ const inversify_1 = require("inversify");
11
+ const DependencyInjection_1 = require("../services/DependencyInjection");
12
+ /**
13
+ * Proxify mixin - Adds Expressive Tea HTTP proxy capabilities to a class
14
+ *
15
+ * Transforms a regular class into an Expressive Tea proxy with:
16
+ * - HTTP proxy configuration
17
+ * - Request/response transformation
18
+ * - Custom headers and options
19
+ * - Automatic proxy registration
20
+ *
21
+ * @template TBase - The base constructor type to extend
22
+ * @param {TBase} Base - The base class to extend
23
+ * @param {string} source - The source path to proxy from (e.g., '/api')
24
+ * @param {string} targetUrl - The target URL to proxy to (e.g., 'http://api.example.com')
25
+ * @returns {ProxifiedClass<TBase>} The enhanced class with proxy capabilities
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * @Proxy('/api', 'http://api.example.com')
30
+ * class ApiProxy {
31
+ * @ProxyHost()
32
+ * getHost() {
33
+ * return 'http://api.example.com';
34
+ * }
35
+ * }
36
+ * ```
37
+ * @since 2.0.0
38
+ */
39
+ function Proxify(Base, source, targetUrl) {
40
+ let ExpressiveTeaProxy = class ExpressiveTeaProxy extends Base {
41
+ constructor(...args) {
42
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
43
+ super(...args);
44
+ this.source = source;
45
+ this.target = targetUrl;
46
+ const options = {};
47
+ const host = commons_1.Metadata.get(commons_2.PROXY_SETTING_KEY, this, commons_2.PROXY_METHODS.HOST);
48
+ for (const value of Object.values(commons_2.PROXY_METHODS)) {
49
+ if (value !== commons_2.PROXY_METHODS.HOST) {
50
+ options[value] = commons_1.Metadata.get(commons_2.PROXY_SETTING_KEY, this, value);
51
+ }
52
+ }
53
+ for (const value of Object.values(commons_2.PROXY_PROPERTIES)) {
54
+ const key = commons_1.Metadata.get(commons_2.PROXY_SETTING_KEY, this, value);
55
+ if (!(0, utilities_1.isUndefined)(key)) {
56
+ options[value] = this[key];
57
+ }
58
+ }
59
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
60
+ this.proxyHandler = httpProxy(host ? host.value.bind(this) : this.target);
61
+ }
62
+ __register(server) {
63
+ const proxyMetadata = commons_1.Metadata.get(commons_2.PROXY_SETTING_KEY, (0, commons_3.getClass)(this));
64
+ console.info(`[PROXY - ${proxyMetadata.name}] ${this.source} -> ${this.target}`);
65
+ server.use(this.source, this.proxyHandler);
66
+ }
67
+ };
68
+ ExpressiveTeaProxy = tslib_1.__decorate([
69
+ (0, inversify_1.injectable)('Singleton'),
70
+ (0, inversify_1.injectFromBase)({ extendConstructorArguments: true }),
71
+ tslib_1.__metadata("design:paramtypes", [Object])
72
+ ], ExpressiveTeaProxy);
73
+ // Bind the original class to the wrapped class in the DI container
74
+ // This ensures that when the application requests the original proxy class,
75
+ // it receives an instance of the wrapped ExpressiveTeaProxy class instead
76
+ try {
77
+ if (DependencyInjection_1.default.Container.isBound(Base)) {
78
+ DependencyInjection_1.default.Container.unbind(Base);
79
+ }
80
+ DependencyInjection_1.default.Container.bind(Base).to(ExpressiveTeaProxy);
81
+ }
82
+ catch (_a) {
83
+ // Binding may fail in some contexts, but that's okay - the class is still usable
84
+ }
85
+ return ExpressiveTeaProxy;
86
+ }