te.js 1.2.0 → 1.3.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/example/index.js +1 -1
- package/example/targets/index.target.js +3 -7
- package/example/tejas.config.json +0 -4
- package/package.json +1 -1
- package/server/ammo/dispatch-helper.js +63 -13
- package/server/ammo.js +230 -15
- package/server/endpoint.js +53 -0
- package/server/handler.js +87 -72
- package/server/target.js +100 -19
- package/server/targets/path-validator.js +21 -0
- package/server/targets/registry.js +15 -4
- package/server/targets/shoot-validator.js +21 -0
- package/te.js +137 -129
package/example/index.js
CHANGED
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
import { Target } from 'te.js';
|
|
1
|
+
import { Target, TejError } from 'te.js';
|
|
2
2
|
|
|
3
3
|
const target = new Target();
|
|
4
4
|
|
|
5
|
-
target.register('/
|
|
6
|
-
throw new
|
|
7
|
-
ammo.fire({
|
|
8
|
-
status: 200,
|
|
9
|
-
body: 'Hello, World!'
|
|
10
|
-
});
|
|
5
|
+
target.register('/', (ammo) => {
|
|
6
|
+
throw new TejError(500, 'Hello')
|
|
11
7
|
});
|
package/package.json
CHANGED
|
@@ -1,48 +1,98 @@
|
|
|
1
1
|
import status from 'statuses';
|
|
2
2
|
|
|
3
3
|
const formattedData = (data) => {
|
|
4
|
-
if (
|
|
4
|
+
if (data === null || data === undefined) return '';
|
|
5
|
+
|
|
6
|
+
if (typeof data === 'object') {
|
|
7
|
+
try {
|
|
8
|
+
return JSON.stringify(data);
|
|
9
|
+
} catch (error) {
|
|
10
|
+
return String(data);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
5
14
|
if (typeof data === 'string') return data;
|
|
6
|
-
if (typeof data === 'number') return status[data];
|
|
7
|
-
|
|
15
|
+
if (typeof data === 'number') return status[data] || String(data);
|
|
16
|
+
|
|
17
|
+
return String(data);
|
|
8
18
|
};
|
|
9
19
|
|
|
10
20
|
const statusAndData = (args) => {
|
|
11
|
-
|
|
21
|
+
// Handle no arguments
|
|
22
|
+
if (!args || args.length === 0) {
|
|
12
23
|
return {
|
|
13
24
|
statusCode: 204,
|
|
14
25
|
data: status(204),
|
|
15
26
|
contentType: 'text/plain',
|
|
16
27
|
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Handle single argument
|
|
31
|
+
if (args.length === 1) {
|
|
32
|
+
const arg = args[0];
|
|
17
33
|
|
|
18
|
-
|
|
34
|
+
// If it's a number, treat as status code
|
|
35
|
+
if (typeof arg === 'number') {
|
|
36
|
+
return {
|
|
37
|
+
statusCode: arg,
|
|
38
|
+
data: status(arg) || String(arg),
|
|
39
|
+
contentType: 'text/plain',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Otherwise treat as data
|
|
19
44
|
return {
|
|
20
|
-
statusCode:
|
|
21
|
-
data:
|
|
22
|
-
contentType:
|
|
45
|
+
statusCode: 200,
|
|
46
|
+
data: formattedData(arg),
|
|
47
|
+
contentType: contentType(arg),
|
|
23
48
|
};
|
|
49
|
+
}
|
|
24
50
|
|
|
51
|
+
// Handle multiple arguments
|
|
25
52
|
let statusCode = 200;
|
|
26
53
|
let data = args[0];
|
|
27
|
-
|
|
54
|
+
|
|
55
|
+
// If first argument is a number, treat as status code
|
|
56
|
+
if (typeof args[0] === 'number') {
|
|
28
57
|
statusCode = args[0];
|
|
29
58
|
data = args[1];
|
|
30
|
-
|
|
59
|
+
} else {
|
|
60
|
+
// If first argument is not a number, check if second is
|
|
61
|
+
if (typeof args[1] === 'number') {
|
|
62
|
+
statusCode = args[1];
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// If data is undefined, use status message
|
|
67
|
+
if (data === undefined) {
|
|
68
|
+
data = status[statusCode] || String(statusCode);
|
|
31
69
|
}
|
|
32
70
|
|
|
71
|
+
// If third argument is provided, it's the content type
|
|
72
|
+
const customContentType = args.length > 2 ? args[2] : null;
|
|
73
|
+
|
|
33
74
|
return {
|
|
34
75
|
statusCode,
|
|
35
76
|
data: formattedData(data),
|
|
36
|
-
contentType: contentType(data),
|
|
77
|
+
contentType: customContentType || contentType(data),
|
|
37
78
|
};
|
|
38
79
|
};
|
|
39
80
|
|
|
40
81
|
const contentType = (data) => {
|
|
82
|
+
if (data === null || data === undefined) return 'text/plain';
|
|
83
|
+
|
|
41
84
|
switch (typeof data) {
|
|
42
85
|
case 'object':
|
|
43
86
|
return 'application/json';
|
|
44
87
|
case 'string':
|
|
45
|
-
|
|
88
|
+
// Check if string is HTML
|
|
89
|
+
if (
|
|
90
|
+
data.trim().toLowerCase().startsWith('<!DOCTYPE') ||
|
|
91
|
+
data.trim().toLowerCase().startsWith('<html')
|
|
92
|
+
) {
|
|
93
|
+
return 'text/html';
|
|
94
|
+
}
|
|
95
|
+
return 'text/plain';
|
|
46
96
|
case 'number':
|
|
47
97
|
return 'text/plain';
|
|
48
98
|
default:
|
|
@@ -50,4 +100,4 @@ const contentType = (data) => {
|
|
|
50
100
|
}
|
|
51
101
|
};
|
|
52
102
|
|
|
53
|
-
export { statusAndData, contentType };
|
|
103
|
+
export { statusAndData, contentType, formattedData };
|
package/server/ammo.js
CHANGED
|
@@ -8,7 +8,32 @@ import html from '../utils/tejas-entrypoint-html.js';
|
|
|
8
8
|
import ammoEnhancer from './ammo/enhancer.js';
|
|
9
9
|
import TejError from './error.js';
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Ammo class for handling HTTP requests and responses.
|
|
13
|
+
*
|
|
14
|
+
* @description
|
|
15
|
+
* Ammo is a utility class that simplifies HTTP request handling and response generation.
|
|
16
|
+
* It provides methods for processing requests, sending responses, and handling errors.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
*
|
|
20
|
+
* if (ammo.GET) {
|
|
21
|
+
* ammo.fire(200, { message: 'Hello World' });
|
|
22
|
+
* } else {
|
|
23
|
+
* ammo.notAllowed();
|
|
24
|
+
* }
|
|
25
|
+
*/
|
|
11
26
|
class Ammo {
|
|
27
|
+
/**
|
|
28
|
+
* Creates a new Ammo instance.
|
|
29
|
+
*
|
|
30
|
+
* @param {http.IncomingMessage} req - The HTTP request object
|
|
31
|
+
* @param {http.ServerResponse} res - The HTTP response object
|
|
32
|
+
*
|
|
33
|
+
* @description
|
|
34
|
+
* Initializes a new Ammo instance with the provided request and response objects.
|
|
35
|
+
* Sets up default values for various properties that will be populated by the enhance method.
|
|
36
|
+
*/
|
|
12
37
|
constructor(req, res) {
|
|
13
38
|
this.req = req;
|
|
14
39
|
this.res = res;
|
|
@@ -39,6 +64,24 @@ class Ammo {
|
|
|
39
64
|
this.dispatchedData = undefined;
|
|
40
65
|
}
|
|
41
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Enhances the Ammo instance with request data and sets HTTP method flags.
|
|
69
|
+
*
|
|
70
|
+
* @description
|
|
71
|
+
* This method processes the request and sets various properties on the Ammo instance:
|
|
72
|
+
* - HTTP method flags (GET, POST, PUT, etc.)
|
|
73
|
+
* - Request data (IP, headers, payload, method)
|
|
74
|
+
* - URL data (protocol, hostname, path, endpoint, fullURL)
|
|
75
|
+
*
|
|
76
|
+
* This method should be called before using any other Ammo methods.
|
|
77
|
+
*
|
|
78
|
+
* @returns {Promise<void>} A promise that resolves when enhancement is complete
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* const ammo = new Ammo(req, res);
|
|
82
|
+
* await ammo.enhance();
|
|
83
|
+
* // Now you can use ammo.GET, ammo.path, etc.
|
|
84
|
+
*/
|
|
42
85
|
async enhance() {
|
|
43
86
|
await ammoEnhancer(this);
|
|
44
87
|
|
|
@@ -51,6 +94,52 @@ class Ammo {
|
|
|
51
94
|
this.OPTIONS = this.method === 'OPTIONS';
|
|
52
95
|
}
|
|
53
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Sends a response to the client with the specified data and status code.
|
|
99
|
+
*
|
|
100
|
+
* @param {number|any} [arg1] - If a number, treated as status code. Otherwise treated as data to send.
|
|
101
|
+
* @param {any} [arg2] - If arg1 is a number, this is the data to send. Otherwise ignored.
|
|
102
|
+
* @param {string} [arg3] - Optional content type override.
|
|
103
|
+
*
|
|
104
|
+
* @description
|
|
105
|
+
* The fire method is flexible and can handle different argument patterns:
|
|
106
|
+
*
|
|
107
|
+
* 1. No arguments: Sends a 204 No Content response
|
|
108
|
+
* 2. Single number: Sends a response with the given status code
|
|
109
|
+
* 3. Single non-number: Sends a 200 OK response with the given data
|
|
110
|
+
* 4. Two arguments (number, data): Sends a response with the given status code and data
|
|
111
|
+
* 5. Three arguments: Sends a response with the given status code, data, and content type
|
|
112
|
+
*
|
|
113
|
+
* The fire method can be used with any HTTP status code, including error codes (4xx, 5xx).
|
|
114
|
+
* For error responses, you can use either fire() or throw(). The main difference is that
|
|
115
|
+
* throw() can accept an Error instance and has special handling for it, while fire() only
|
|
116
|
+
* accepts status codes, strings, or other data types.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* // Send a 200 OK response with JSON data
|
|
120
|
+
* ammo.fire(200, { message: 'Success' });
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* // Send a 404 Not Found response with custom message
|
|
124
|
+
* ammo.fire(404, 'Resource not found');
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* // Send a 500 Internal Server Error response
|
|
128
|
+
* ammo.fire(500, 'Something went wrong');
|
|
129
|
+
*
|
|
130
|
+
* @example
|
|
131
|
+
* // Send HTML content with custom content type
|
|
132
|
+
* ammo.fire(200, '<html><body>Hello</body></html>', 'text/html');
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* // Send just a status code (will use default status message)
|
|
136
|
+
* ammo.fire(204);
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* // Send just data (will use 200 status code)
|
|
140
|
+
* ammo.fire({ message: 'Success' });
|
|
141
|
+
* ammo.fire('Hello World');
|
|
142
|
+
*/
|
|
54
143
|
fire() {
|
|
55
144
|
const { statusCode, data, contentType } = statusAndData(arguments);
|
|
56
145
|
const contentTypeHeader = { 'Content-Type': contentType };
|
|
@@ -62,50 +151,176 @@ class Ammo {
|
|
|
62
151
|
this.res.end();
|
|
63
152
|
}
|
|
64
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Throws a 404 Not Found error.
|
|
156
|
+
*
|
|
157
|
+
* @description
|
|
158
|
+
* This is a convenience method that throws a 404 Not Found error.
|
|
159
|
+
* It's equivalent to calling `throw(404) ` or `fire(404)`.
|
|
160
|
+
*
|
|
161
|
+
* @throws {TejError} Always throws a TejError with status code 404
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* // If resource not found
|
|
165
|
+
* if (!resource) {
|
|
166
|
+
* ammo.notFound();
|
|
167
|
+
* }
|
|
168
|
+
*/
|
|
65
169
|
notFound() {
|
|
66
170
|
throw new TejError(404, 'Not Found');
|
|
67
171
|
}
|
|
68
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Throws a 405 Method Not Allowed error.
|
|
175
|
+
*
|
|
176
|
+
* @description
|
|
177
|
+
* This is a convenience method that throws a 405 Method Not Allowed error.
|
|
178
|
+
* It's equivalent to calling `throw(405)` or `fire(405)`.
|
|
179
|
+
*
|
|
180
|
+
* @throws {TejError} Always throws a TejError with status code 405
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* // If method not allowed
|
|
184
|
+
* if (!allowedMethods.includes(ammo.method)) {
|
|
185
|
+
* ammo.notAllowed();
|
|
186
|
+
* }
|
|
187
|
+
*/
|
|
69
188
|
notAllowed() {
|
|
70
189
|
throw new TejError(405, 'Method Not Allowed');
|
|
71
190
|
}
|
|
72
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Throws a 401 Unauthorized error.
|
|
194
|
+
*
|
|
195
|
+
* @description
|
|
196
|
+
* This is a convenience method that throws a 401 Unauthorized error.
|
|
197
|
+
* It's equivalent to calling `throw(401) ` or `fire(401)`.
|
|
198
|
+
*
|
|
199
|
+
* @throws {TejError} Always throws a TejError with status code 401
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* // If user is not authenticated
|
|
203
|
+
* if (!user) {
|
|
204
|
+
* ammo.unauthorized();
|
|
205
|
+
* }
|
|
206
|
+
*/
|
|
73
207
|
unauthorized() {
|
|
74
208
|
throw new TejError(401, 'Unauthorized');
|
|
75
209
|
}
|
|
76
210
|
|
|
211
|
+
/**
|
|
212
|
+
* Sends the default entry point HTML.
|
|
213
|
+
*
|
|
214
|
+
* @description
|
|
215
|
+
* This method sends the default HTML entry point for the application.
|
|
216
|
+
* It's typically used as a fallback when no specific route is matched.
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* // In a catch-all route
|
|
220
|
+
* ammo.defaultEntry();
|
|
221
|
+
*/
|
|
77
222
|
defaultEntry() {
|
|
78
223
|
this.fire(html);
|
|
79
224
|
}
|
|
80
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Throws an error response with appropriate status code and message.
|
|
228
|
+
*
|
|
229
|
+
* @param {number|Error|string} [arg1] - Status code, Error object, or error message
|
|
230
|
+
* @param {string} [arg2] - Error message (only used when arg1 is a status code)
|
|
231
|
+
*
|
|
232
|
+
* @description
|
|
233
|
+
* The throw method is flexible and can handle different argument patterns:
|
|
234
|
+
*
|
|
235
|
+
* 1. No arguments: Sends a 500 Internal Server Error response
|
|
236
|
+
* 2. Status code: Sends a response with the given status code and default message
|
|
237
|
+
* 3. Status code and message: Sends a response with the given status code and message
|
|
238
|
+
* 4. Error object: Extracts status code and message from the error
|
|
239
|
+
* 5. String: Treats as error message with 500 status code
|
|
240
|
+
*
|
|
241
|
+
* The key difference between throw() and fire() is that throw() can accept an Error instance
|
|
242
|
+
* and has special handling for it. Internally, throw() still calls fire() to send the response.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* // Throw a 404 Not Found error
|
|
246
|
+
* ammo.throw(404);
|
|
247
|
+
*
|
|
248
|
+
* @example
|
|
249
|
+
* // Throw a 404 Not Found error with custom message
|
|
250
|
+
* ammo.throw(404, 'Resource not found');
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* // Throw an error from an Error object
|
|
254
|
+
* ammo.throw(new Error('Something went wrong'));
|
|
255
|
+
*
|
|
256
|
+
* @example
|
|
257
|
+
* // Throw an error with a custom message
|
|
258
|
+
* ammo.throw('Something went wrong');
|
|
259
|
+
*/
|
|
81
260
|
throw() {
|
|
82
|
-
|
|
83
|
-
const
|
|
261
|
+
// Handle different argument patterns
|
|
262
|
+
const args = Array.from(arguments);
|
|
84
263
|
|
|
85
|
-
|
|
264
|
+
// Case 1: No arguments provided
|
|
265
|
+
if (args.length === 0) {
|
|
266
|
+
this.fire(500, 'Internal Server Error');
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
86
269
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
270
|
+
// Case 2: First argument is a status code
|
|
271
|
+
if (isStatusCode(args[0])) {
|
|
272
|
+
const statusCode = args[0];
|
|
273
|
+
const message = args[1] || toStatusMessage(statusCode);
|
|
274
|
+
this.fire(statusCode, message);
|
|
90
275
|
return;
|
|
91
276
|
}
|
|
92
277
|
|
|
93
|
-
|
|
94
|
-
|
|
278
|
+
// Case 3.1: First argument is an instance of TejError
|
|
279
|
+
if (args[0] instanceof TejError) {
|
|
280
|
+
const error = args[0];
|
|
281
|
+
const statusCode = error.code;
|
|
282
|
+
const message = error.message;
|
|
95
283
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
284
|
+
this.fire(statusCode, message);
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Case 3.2: First argument is an Error object
|
|
289
|
+
if (args[0] instanceof Error) {
|
|
290
|
+
const error = args[0];
|
|
291
|
+
|
|
292
|
+
// Check if error message is a numeric status code
|
|
293
|
+
if (!isNaN(parseInt(error.message))) {
|
|
294
|
+
const statusCode = parseInt(error.message);
|
|
295
|
+
const message = toStatusMessage(statusCode) || toStatusMessage(500);
|
|
296
|
+
this.fire(statusCode, message);
|
|
100
297
|
return;
|
|
101
298
|
}
|
|
102
299
|
|
|
103
|
-
|
|
104
|
-
|
|
300
|
+
// Use error message as status code if it's a valid status code string
|
|
301
|
+
const statusCode = toStatusCode(error.message);
|
|
302
|
+
if (statusCode) {
|
|
303
|
+
this.fire(statusCode, error.message);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Default error handling
|
|
308
|
+
this.fire(500, error.message);
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Case 4: First argument is a string or other value
|
|
313
|
+
const errorValue = args[0];
|
|
314
|
+
|
|
315
|
+
// Check if the string represents a status code
|
|
316
|
+
const statusCode = toStatusCode(errorValue);
|
|
317
|
+
if (statusCode) {
|
|
318
|
+
this.fire(statusCode, toStatusMessage(statusCode));
|
|
105
319
|
return;
|
|
106
320
|
}
|
|
107
321
|
|
|
108
|
-
|
|
322
|
+
// Default case: treat as error message
|
|
323
|
+
this.fire(500, errorValue.toString());
|
|
109
324
|
}
|
|
110
325
|
}
|
|
111
326
|
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import isMiddlewareValid from './targets/middleware-validator.js';
|
|
2
|
+
import { isPathValid, standardizePath } from './targets/path-validator.js';
|
|
3
|
+
import isShootValid from './targets/shoot-validator.js';
|
|
4
|
+
|
|
5
|
+
class Endpoint {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.path = '';
|
|
8
|
+
this.middlewares = [];
|
|
9
|
+
this.handler = null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
setPath(base, path) {
|
|
13
|
+
const standardizedBase = standardizePath(base);
|
|
14
|
+
const standardizedPath = standardizePath(path);
|
|
15
|
+
|
|
16
|
+
let fullPath = `${standardizedBase}${standardizedPath}`;
|
|
17
|
+
if (fullPath.length === 0) fullPath = '/';
|
|
18
|
+
|
|
19
|
+
if (!isPathValid(fullPath)) return this;
|
|
20
|
+
|
|
21
|
+
this.path = fullPath;
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
setMiddlewares(middlewares) {
|
|
26
|
+
const validMiddlewares = middlewares.filter(isMiddlewareValid);
|
|
27
|
+
if (validMiddlewares.length === 0) return this;
|
|
28
|
+
|
|
29
|
+
this.middlewares = this.middlewares.concat(validMiddlewares);
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setHandler(handler) {
|
|
34
|
+
if (!isShootValid(handler)) return this;
|
|
35
|
+
this.handler = handler;
|
|
36
|
+
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
getPath() {
|
|
41
|
+
return this.path;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
getMiddlewares() {
|
|
45
|
+
return this.middlewares;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
getHandler() {
|
|
49
|
+
return this.handler;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export default Endpoint;
|
package/server/handler.js
CHANGED
|
@@ -1,72 +1,87 @@
|
|
|
1
|
-
import { env } from 'tej-env';
|
|
2
|
-
import TejLogger from 'tej-logger';
|
|
3
|
-
import logHttpRequest from '../utils/request-logger.js';
|
|
4
|
-
|
|
5
|
-
import Ammo from './ammo.js';
|
|
6
|
-
import TejError from './error.js';
|
|
7
|
-
import TargetRegistry from './targets/registry.js';
|
|
8
|
-
|
|
9
|
-
const targetRegistry = new TargetRegistry();
|
|
10
|
-
const errorLogger = new TejLogger('Tejas.Exception');
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
1
|
+
import { env } from 'tej-env';
|
|
2
|
+
import TejLogger from 'tej-logger';
|
|
3
|
+
import logHttpRequest from '../utils/request-logger.js';
|
|
4
|
+
|
|
5
|
+
import Ammo from './ammo.js';
|
|
6
|
+
import TejError from './error.js';
|
|
7
|
+
import TargetRegistry from './targets/registry.js';
|
|
8
|
+
|
|
9
|
+
const targetRegistry = new TargetRegistry();
|
|
10
|
+
const errorLogger = new TejLogger('Tejas.Exception');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Executes the middleware and handler chain for a given target.
|
|
14
|
+
*
|
|
15
|
+
* @param {Object} target - The target endpoint object.
|
|
16
|
+
* @param {Ammo} ammo - The Ammo instance containing request and response objects.
|
|
17
|
+
* @returns {Promise<void>} A promise that resolves when the chain execution is complete.
|
|
18
|
+
*/
|
|
19
|
+
const executeChain = async (target, ammo) => {
|
|
20
|
+
let i = 0;
|
|
21
|
+
|
|
22
|
+
const chain = targetRegistry.globalMiddlewares.concat(
|
|
23
|
+
target.getMiddlewares(),
|
|
24
|
+
);
|
|
25
|
+
chain.push(target.getHandler());
|
|
26
|
+
|
|
27
|
+
const next = async () => {
|
|
28
|
+
const middleware = chain[i];
|
|
29
|
+
i++;
|
|
30
|
+
|
|
31
|
+
const args =
|
|
32
|
+
middleware.length === 3 ? [ammo.req, ammo.res, next] : [ammo, next];
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
await middleware(...args);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
errorHandler(ammo, err);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
await next();
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Handles errors by logging them and sending an appropriate response.
|
|
46
|
+
*
|
|
47
|
+
* @param {Ammo} ammo - The Ammo instance containing request and response objects.
|
|
48
|
+
* @param {Error} err - The error object to handle.
|
|
49
|
+
*/
|
|
50
|
+
const errorHandler = (ammo, err) => {
|
|
51
|
+
if (env('LOG_EXCEPTIONS')) errorLogger.error(err);
|
|
52
|
+
|
|
53
|
+
if (err instanceof TejError) return ammo.throw(err);
|
|
54
|
+
return ammo.throw(err);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Main request handler function.
|
|
59
|
+
*
|
|
60
|
+
* @param {http.IncomingMessage} req - The HTTP request object.
|
|
61
|
+
* @param {http.ServerResponse} res - The HTTP response object.
|
|
62
|
+
* @returns {Promise<void>} A promise that resolves when the request handling is complete.
|
|
63
|
+
*/
|
|
64
|
+
const handler = async (req, res) => {
|
|
65
|
+
const url = req.url.split('?')[0];
|
|
66
|
+
const target = targetRegistry.aim(url);
|
|
67
|
+
const ammo = new Ammo(req, res);
|
|
68
|
+
|
|
69
|
+
try {
|
|
70
|
+
if (target) {
|
|
71
|
+
await ammo.enhance();
|
|
72
|
+
|
|
73
|
+
if (env('LOG_HTTP_REQUESTS')) logHttpRequest(ammo);
|
|
74
|
+
await executeChain(target, ammo);
|
|
75
|
+
} else {
|
|
76
|
+
if (req.url === '/') {
|
|
77
|
+
ammo.defaultEntry();
|
|
78
|
+
} else {
|
|
79
|
+
errorHandler(ammo, new TejError(404, `URL not found: ${url}`));
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} catch (err) {
|
|
83
|
+
errorHandler(ammo, err);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export default handler;
|
package/server/target.js
CHANGED
|
@@ -1,52 +1,133 @@
|
|
|
1
|
+
import TejLogger from 'tej-logger';
|
|
2
|
+
|
|
1
3
|
import isMiddlewareValid from './targets/middleware-validator.js';
|
|
2
|
-
import
|
|
4
|
+
import Endpoint from './endpoint.js';
|
|
3
5
|
|
|
6
|
+
import TargetRegistry from './targets/registry.js';
|
|
4
7
|
const targetRegistry = new TargetRegistry();
|
|
5
8
|
|
|
6
|
-
const
|
|
7
|
-
if (typeof endpoint !== 'string') return false;
|
|
8
|
-
if (endpoint.length === 0) return false;
|
|
9
|
-
return endpoint[0] === '/';
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
const isShootValid = (shoot) => typeof shoot === 'function';
|
|
9
|
+
const logger = new TejLogger('Target');
|
|
13
10
|
|
|
11
|
+
/**
|
|
12
|
+
* Target class represents a base routing configuration for endpoints. Think of it as router in express.
|
|
13
|
+
* It provides functionality to set base paths, add middleware, and register endpoints.
|
|
14
|
+
*
|
|
15
|
+
* @class
|
|
16
|
+
* @example
|
|
17
|
+
* // Create a new target for user-related endpoints
|
|
18
|
+
* const userTarget = new Target('/user');
|
|
19
|
+
*
|
|
20
|
+
* // Add middleware that applies to all user endpoints
|
|
21
|
+
* userTarget.midair(authMiddleware, loggingMiddleware);
|
|
22
|
+
*
|
|
23
|
+
* // Register endpoints
|
|
24
|
+
* userTarget.register('/profile', (ammo) => {
|
|
25
|
+
* // Handle GET /user/profile
|
|
26
|
+
* ammo.fire({ name: 'John Doe' });
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* userTarget.register('/settings', authMiddleware, (ammo) => {
|
|
30
|
+
* // Handle GET /user/settings with additional auth middleware
|
|
31
|
+
* ammo.fire({ theme: 'dark' });
|
|
32
|
+
* });
|
|
33
|
+
*/
|
|
14
34
|
class Target {
|
|
35
|
+
/**
|
|
36
|
+
* Creates a new Target instance.
|
|
37
|
+
*
|
|
38
|
+
* @param {string} [base=''] - The base path for all endpoints registered under this target.
|
|
39
|
+
* Must start with '/' if provided.
|
|
40
|
+
* @example
|
|
41
|
+
* const apiTarget = new Target('/api');
|
|
42
|
+
* const userTarget = new Target('/user');
|
|
43
|
+
*/
|
|
15
44
|
constructor(base = '') {
|
|
16
45
|
this.base = base;
|
|
17
46
|
this.targetMiddlewares = [];
|
|
18
47
|
}
|
|
19
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Sets the base path for the target.
|
|
51
|
+
*
|
|
52
|
+
* @param {string} base - The base path to set. Must start with '/'.
|
|
53
|
+
* @returns {void}
|
|
54
|
+
* @example
|
|
55
|
+
* const target = new Target();
|
|
56
|
+
* target.base('/api/v1');
|
|
57
|
+
*/
|
|
20
58
|
base(base) {
|
|
21
59
|
if (!base || !base.startsWith('/')) return;
|
|
22
60
|
this.base = base;
|
|
23
61
|
}
|
|
24
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Adds middleware functions to the target.
|
|
65
|
+
* These middleware functions will be applied to all endpoints registered under this target.
|
|
66
|
+
*
|
|
67
|
+
* @param {...Function} middlewares - One or more middleware functions to add.
|
|
68
|
+
* @returns {void}
|
|
69
|
+
* @example
|
|
70
|
+
* // Add authentication middleware to all endpoints
|
|
71
|
+
* target.midair(authMiddleware);
|
|
72
|
+
*
|
|
73
|
+
* // Add multiple middleware functions
|
|
74
|
+
* target.midair(loggingMiddleware, errorHandler, rateLimiter);
|
|
75
|
+
*/
|
|
25
76
|
midair() {
|
|
26
77
|
if (!arguments) return;
|
|
27
78
|
const middlewares = [...arguments];
|
|
79
|
+
|
|
28
80
|
const validMiddlewares = middlewares.filter(isMiddlewareValid);
|
|
29
81
|
this.targetMiddlewares = this.targetMiddlewares.concat(validMiddlewares);
|
|
30
82
|
}
|
|
31
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Registers a new endpoint under this target.
|
|
86
|
+
*
|
|
87
|
+
* @param {string} path - The path for the endpoint, relative to the base path.
|
|
88
|
+
* @param {...Function} [middlewares] - Optional middleware functions specific to this endpoint.
|
|
89
|
+
* @param {Function} shoot - The handler function for the endpoint.
|
|
90
|
+
* @returns {void}
|
|
91
|
+
* @throws {Error} If there's an error registering the endpoint.
|
|
92
|
+
* @example
|
|
93
|
+
* // Register a simple endpoint
|
|
94
|
+
* target.register('/hello', (ammo) => {
|
|
95
|
+
* ammo.fire({ message: 'Hello World' });
|
|
96
|
+
* });
|
|
97
|
+
*
|
|
98
|
+
* // Register an endpoint with specific middleware
|
|
99
|
+
* target.register('/protected', authMiddleware, (ammo) => {
|
|
100
|
+
* ammo.fire({ data: 'Protected data' });
|
|
101
|
+
* });
|
|
102
|
+
*
|
|
103
|
+
* // Register an endpoint with multiple middleware
|
|
104
|
+
* target.register('/api/data',
|
|
105
|
+
* authMiddleware,
|
|
106
|
+
* rateLimiter,
|
|
107
|
+
* (ammo) => {
|
|
108
|
+
* ammo.fire({ data: 'Rate limited data' });
|
|
109
|
+
* }
|
|
110
|
+
* );
|
|
111
|
+
*/
|
|
32
112
|
register() {
|
|
33
113
|
let args = arguments;
|
|
34
114
|
if (!args) return;
|
|
35
115
|
|
|
36
|
-
const
|
|
37
|
-
if (!isEndpointValid(endpoint)) return;
|
|
38
|
-
|
|
116
|
+
const path = args[0];
|
|
39
117
|
const shoot = args[args.length - 1];
|
|
40
|
-
if (!isShootValid(shoot)) return;
|
|
41
|
-
|
|
42
118
|
const middlewares = Array.from(args).slice(1, args.length - 1);
|
|
43
|
-
const validMiddlewares = middlewares.filter(isMiddlewareValid);
|
|
44
119
|
|
|
45
|
-
|
|
46
|
-
endpoint
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
120
|
+
try {
|
|
121
|
+
const endpoint = new Endpoint();
|
|
122
|
+
endpoint
|
|
123
|
+
.setPath(this.base, path)
|
|
124
|
+
.setMiddlewares(middlewares)
|
|
125
|
+
.setHandler(shoot);
|
|
126
|
+
|
|
127
|
+
targetRegistry.targets.push(endpoint);
|
|
128
|
+
} catch (error) {
|
|
129
|
+
logger.error(`Error registering target ${path}: ${error.message}`);
|
|
130
|
+
}
|
|
50
131
|
}
|
|
51
132
|
}
|
|
52
133
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import TejLogger from 'tej-logger';
|
|
2
|
+
|
|
3
|
+
const logger = new TejLogger('PathValidator');
|
|
4
|
+
|
|
5
|
+
const standardizePath = (path) => {
|
|
6
|
+
if (!path || path.length === 0) return '';
|
|
7
|
+
|
|
8
|
+
let standardized = path.startsWith('/') ? path : `/${path}`;
|
|
9
|
+
return standardized.endsWith('/') ? standardized.slice(0, -1) : standardized;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const isPathValid = (path) => {
|
|
13
|
+
if (typeof path !== 'string') {
|
|
14
|
+
logger.error(`Path ${path} should be a string. Skipping...`);
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return true;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export { isPathValid, standardizePath };
|
|
@@ -32,13 +32,24 @@ class TargetRegistry {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
aim(
|
|
35
|
+
aim(endpoint) {
|
|
36
36
|
return this.targets.find((target) => {
|
|
37
|
-
return (
|
|
38
|
-
target.endpoint === endpoint
|
|
39
|
-
);
|
|
37
|
+
return target.getPath() === endpoint;
|
|
40
38
|
});
|
|
41
39
|
}
|
|
40
|
+
|
|
41
|
+
getAllEndpoints(grouped) {
|
|
42
|
+
if (grouped) {
|
|
43
|
+
return this.targets.reduce((acc, target) => {
|
|
44
|
+
const group = target.getPath().split('/')[1];
|
|
45
|
+
if (!acc[group]) acc[group] = [];
|
|
46
|
+
acc[group].push(target.getPath());
|
|
47
|
+
return acc;
|
|
48
|
+
}, {});
|
|
49
|
+
} else {
|
|
50
|
+
return this.targets.map((target) => target.getPath());
|
|
51
|
+
}
|
|
52
|
+
}
|
|
42
53
|
}
|
|
43
54
|
|
|
44
55
|
export default TargetRegistry;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import TejLogger from 'tej-logger';
|
|
2
|
+
import Ammo from '../ammo.js';
|
|
3
|
+
const logger = new TejLogger('ShootValidator');
|
|
4
|
+
|
|
5
|
+
const isShootValid = (shoot) => {
|
|
6
|
+
if (typeof shoot !== 'function') {
|
|
7
|
+
logger.error(`Shoot ${shoot} should be a function. Skipping...`);
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Shoot should have 1 parameter, and it must be an instance of Ammo
|
|
12
|
+
if (shoot.length !== 1) {
|
|
13
|
+
logger.error(`Shoot function must have 1 parameter. Skipping...`);
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
return true;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export default isShootValid;
|
package/te.js
CHANGED
|
@@ -1,129 +1,137 @@
|
|
|
1
|
-
import { createServer } from 'node:http';
|
|
2
|
-
|
|
3
|
-
import { env, setEnv } from 'tej-env';
|
|
4
|
-
import TejLogger from 'tej-logger';
|
|
5
|
-
import database from './database/index.js';
|
|
6
|
-
|
|
7
|
-
import TargetRegistry from './server/targets/registry.js';
|
|
8
|
-
|
|
9
|
-
import { loadConfigFile, standardizeObj } from './utils/configuration.js';
|
|
10
|
-
|
|
11
|
-
import targetHandler from './server/handler.js';
|
|
12
|
-
import { findTargetFiles } from './utils/auto-register.js';
|
|
13
|
-
import { pathToFileURL } from 'node:url';
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
*
|
|
22
|
-
* @param {
|
|
23
|
-
* @param {
|
|
24
|
-
* @param {Boolean} args.log.
|
|
25
|
-
* @param {
|
|
26
|
-
* @param {String} args.db.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Tejas.instance
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
this.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
*
|
|
39
|
-
* @param {
|
|
40
|
-
* @param {String} args.
|
|
41
|
-
* @param {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
if (!
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
|
|
3
|
+
import { env, setEnv } from 'tej-env';
|
|
4
|
+
import TejLogger from 'tej-logger';
|
|
5
|
+
import database from './database/index.js';
|
|
6
|
+
|
|
7
|
+
import TargetRegistry from './server/targets/registry.js';
|
|
8
|
+
|
|
9
|
+
import { loadConfigFile, standardizeObj } from './utils/configuration.js';
|
|
10
|
+
|
|
11
|
+
import targetHandler from './server/handler.js';
|
|
12
|
+
import { findTargetFiles } from './utils/auto-register.js';
|
|
13
|
+
import { pathToFileURL } from 'node:url';
|
|
14
|
+
import { log } from 'node:console';
|
|
15
|
+
|
|
16
|
+
const logger = new TejLogger('Tejas');
|
|
17
|
+
const targetRegistry = new TargetRegistry();
|
|
18
|
+
|
|
19
|
+
class Tejas {
|
|
20
|
+
/*
|
|
21
|
+
* Constructor for Tejas
|
|
22
|
+
* @param {Object} args - Arguments for Tejas
|
|
23
|
+
* @param {Number} args.port - Port to run Tejas on
|
|
24
|
+
* @param {Boolean} args.log.http_requests - Whether to log incoming HTTP requests
|
|
25
|
+
* @param {Boolean} args.log.exceptions - Whether to log exceptions
|
|
26
|
+
* @param {String} args.db.type - Database type. It can be 'mongodb', 'mysql', 'postgres', 'sqlite'
|
|
27
|
+
* @param {String} args.db.uri - Connection URI string for the database
|
|
28
|
+
*/
|
|
29
|
+
constructor(args) {
|
|
30
|
+
if (Tejas.instance) return Tejas.instance;
|
|
31
|
+
Tejas.instance = this;
|
|
32
|
+
|
|
33
|
+
this.generateConfiguration(args);
|
|
34
|
+
this.registerTargetsDir();
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/*
|
|
38
|
+
* Connect to a database
|
|
39
|
+
* @param {Object}
|
|
40
|
+
* @param {String} args.db - Database type. It can be 'mongodb', 'mysql', 'postgres', 'sqlite'
|
|
41
|
+
* @param {String} args.uri - Connection URI string for the database
|
|
42
|
+
* @param {Object} args.options - Options for the database connection
|
|
43
|
+
*/
|
|
44
|
+
connectDatabase(args) {
|
|
45
|
+
const db = env('DB_TYPE');
|
|
46
|
+
const uri = env('DB_URI');
|
|
47
|
+
|
|
48
|
+
if (!db) return;
|
|
49
|
+
if (!uri) {
|
|
50
|
+
logger.error(
|
|
51
|
+
`Tejas could not connect to ${db} as it couldn't find a connection URI. See our documentation for more information.`,
|
|
52
|
+
false,
|
|
53
|
+
);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const connect = database[db];
|
|
58
|
+
if (!connect) {
|
|
59
|
+
logger.error(
|
|
60
|
+
`Tejas could not connect to ${db} as it is not supported. See our documentation for more information.`,
|
|
61
|
+
false,
|
|
62
|
+
);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
connect(uri, {}, (error) => {
|
|
67
|
+
if (error) {
|
|
68
|
+
logger.error(
|
|
69
|
+
`Tejas could not connect to ${db}. Error: ${error}`,
|
|
70
|
+
false,
|
|
71
|
+
);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
logger.info(`Tejas connected to ${db} successfully.`);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
generateConfiguration(options) {
|
|
80
|
+
const configVars = standardizeObj(loadConfigFile());
|
|
81
|
+
const envVars = standardizeObj(process.env);
|
|
82
|
+
const userVars = standardizeObj(options);
|
|
83
|
+
|
|
84
|
+
const config = { ...configVars, ...envVars, ...userVars };
|
|
85
|
+
for (const key in config) {
|
|
86
|
+
if (config.hasOwnProperty(key)) {
|
|
87
|
+
setEnv(key, config[key]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Load defaults
|
|
92
|
+
if (!env('PORT')) setEnv('PORT', 1403);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
midair() {
|
|
96
|
+
if (!arguments) return;
|
|
97
|
+
targetRegistry.addGlobalMiddleware(...arguments);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
registerTargetsDir() {
|
|
101
|
+
findTargetFiles()
|
|
102
|
+
.then((targetFiles) => {
|
|
103
|
+
if (targetFiles) {
|
|
104
|
+
for (const file of targetFiles) {
|
|
105
|
+
import(pathToFileURL(`${file.parentPath}/${file.name}`));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
})
|
|
109
|
+
.catch((err) => {
|
|
110
|
+
logger.error(
|
|
111
|
+
`Tejas could not register target files. Error: ${err}`,
|
|
112
|
+
false,
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
takeoff() {
|
|
118
|
+
this.engine = createServer(targetHandler);
|
|
119
|
+
this.engine.listen(env('PORT'), () => {
|
|
120
|
+
logger.info(`Took off from port ${env('PORT')}`);
|
|
121
|
+
this.connectDatabase();
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const listAllEndpoints = (grouped = false) => {
|
|
127
|
+
return targetRegistry.getAllEndpoints(grouped);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export { default as Target } from './server/target.js';
|
|
131
|
+
export { default as TejFileUploader } from './server/files/uploader.js';
|
|
132
|
+
export { default as TejError } from './server/error.js';
|
|
133
|
+
export { listAllEndpoints };
|
|
134
|
+
export default Tejas;
|
|
135
|
+
|
|
136
|
+
// TODO Ability to register a target (route) from tejas instance
|
|
137
|
+
// TODO tejas as CLI tool
|