@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,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>;
|
package/mixins/module.js
ADDED
|
@@ -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>;
|
package/mixins/proxy.js
ADDED
|
@@ -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
|
+
}
|