tspace-spear 1.0.9 → 1.1.1
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/build/lib/core/decorators/context.js +25 -49
- package/build/lib/core/decorators/context.js.map +1 -1
- package/build/lib/core/decorators/headers.js +3 -14
- package/build/lib/core/decorators/headers.js.map +1 -1
- package/build/lib/core/decorators/statusCode.js +3 -14
- package/build/lib/core/decorators/statusCode.js.map +1 -1
- package/build/lib/core/decorators/swagger.js +4 -1
- package/build/lib/core/decorators/swagger.js.map +1 -1
- package/build/lib/core/server/index.d.ts +32 -20
- package/build/lib/core/server/index.js +275 -262
- package/build/lib/core/server/index.js.map +1 -1
- package/build/lib/core/server/parser-factory.js +85 -91
- package/build/lib/core/server/parser-factory.js.map +1 -1
- package/build/lib/core/server/router.js +1 -3
- package/build/lib/core/server/router.js.map +1 -1
- package/build/lib/core/types/index.d.ts +7 -0
- package/build/tests/benchmark.test.js +50 -71
- package/build/tests/benchmark.test.js.map +1 -1
- package/package.json +60 -61
|
@@ -22,15 +22,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
25
|
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
26
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
27
|
};
|
|
@@ -62,87 +53,41 @@ const parser_factory_1 = require("./parser-factory");
|
|
|
62
53
|
*
|
|
63
54
|
*/
|
|
64
55
|
class Spear {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
info: {
|
|
78
|
-
title: "API Documentation",
|
|
79
|
-
description: "This is a sample documentation",
|
|
80
|
-
version: "1.0.0"
|
|
56
|
+
_controllers;
|
|
57
|
+
_middlewares;
|
|
58
|
+
_globalPrefix;
|
|
59
|
+
_cluster;
|
|
60
|
+
_router = (0, find_my_way_1.default)();
|
|
61
|
+
_parser = new parser_factory_1.ParserFactory();
|
|
62
|
+
_swagger = {
|
|
63
|
+
use: false,
|
|
64
|
+
path: '/api/docs',
|
|
65
|
+
servers: [
|
|
66
|
+
{
|
|
67
|
+
url: 'http://localhost:3000'
|
|
81
68
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const headers = request.headers;
|
|
105
|
-
const query = Object.assign({}, (0, url_1.parse)(String(req.url), true).query);
|
|
106
|
-
const RecordOrEmptyRecord = (data) => {
|
|
107
|
-
if (data == null)
|
|
108
|
-
return {};
|
|
109
|
-
return Object.keys(data).length ? data : {};
|
|
110
|
-
};
|
|
111
|
-
const ctx = {
|
|
112
|
-
req,
|
|
113
|
-
res: response,
|
|
114
|
-
headers: RecordOrEmptyRecord(headers),
|
|
115
|
-
params: RecordOrEmptyRecord(params),
|
|
116
|
-
query: RecordOrEmptyRecord(query),
|
|
117
|
-
body: RecordOrEmptyRecord(body),
|
|
118
|
-
files: RecordOrEmptyRecord(files),
|
|
119
|
-
cookies: RecordOrEmptyRecord(cookies)
|
|
120
|
-
};
|
|
121
|
-
if (index === handlers.length - 1) {
|
|
122
|
-
return this._wrapResponse(handlers[index].bind(handlers[index]))(ctx, this._nextFunction(ctx));
|
|
123
|
-
}
|
|
124
|
-
return handlers[index](ctx, () => {
|
|
125
|
-
return runHandler(index + 1);
|
|
126
|
-
});
|
|
127
|
-
};
|
|
128
|
-
try {
|
|
129
|
-
runHandler();
|
|
130
|
-
}
|
|
131
|
-
catch (err) {
|
|
132
|
-
const ctx = {
|
|
133
|
-
req,
|
|
134
|
-
res: this._customizeResponse(req, res),
|
|
135
|
-
params: Object.keys(params).length ? params : {},
|
|
136
|
-
headers: {},
|
|
137
|
-
query: {},
|
|
138
|
-
body: {},
|
|
139
|
-
files: {},
|
|
140
|
-
cookies: {}
|
|
141
|
-
};
|
|
142
|
-
return this._nextFunction(ctx)(err);
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
};
|
|
69
|
+
],
|
|
70
|
+
tags: [],
|
|
71
|
+
info: {
|
|
72
|
+
title: "API Documentation",
|
|
73
|
+
description: "This is a sample documentation",
|
|
74
|
+
version: "1.0.0"
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
_swaggerAdditional = [];
|
|
78
|
+
_errorHandler = null;
|
|
79
|
+
_globalMiddlewares = [];
|
|
80
|
+
_formatResponse = null;
|
|
81
|
+
_onListeners = [];
|
|
82
|
+
_fileUploadOptions = {
|
|
83
|
+
limit: Infinity,
|
|
84
|
+
tempFileDir: 'tmp',
|
|
85
|
+
removeTempFile: {
|
|
86
|
+
remove: false,
|
|
87
|
+
ms: 1000 * 60 * 10
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
constructor({ controllers, middlewares, globalPrefix, logger, cluster } = {}) {
|
|
146
91
|
if (logger)
|
|
147
92
|
this.useLogger();
|
|
148
93
|
this._cluster = cluster;
|
|
@@ -150,9 +95,19 @@ class Spear {
|
|
|
150
95
|
this._middlewares = middlewares;
|
|
151
96
|
this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
|
|
152
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* The get 'instance' method is used to get the instance of Spear.
|
|
100
|
+
*
|
|
101
|
+
* @returns {this}
|
|
102
|
+
*/
|
|
153
103
|
get instance() {
|
|
154
104
|
return this;
|
|
155
105
|
}
|
|
106
|
+
/**
|
|
107
|
+
* The get 'routers' method is used get the all routers.
|
|
108
|
+
*
|
|
109
|
+
* @returns {Instance<findMyWayRouter.HTTPVersion.V1>}
|
|
110
|
+
*/
|
|
156
111
|
get routers() {
|
|
157
112
|
return this._router;
|
|
158
113
|
}
|
|
@@ -217,38 +172,27 @@ class Spear {
|
|
|
217
172
|
return this;
|
|
218
173
|
}
|
|
219
174
|
/**
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
* @property {boolean} removeTempFile.remove
|
|
227
|
-
* @property {number} removeTempFile.ms
|
|
228
|
-
* @returns
|
|
229
|
-
*/
|
|
230
|
-
useFileUpload({ limit, tempFileDir, removeTempFile } = {}) {
|
|
231
|
-
if (limit != null) {
|
|
232
|
-
this._fileUploadOptions.limit = limit;
|
|
233
|
-
}
|
|
234
|
-
if (tempFileDir != null) {
|
|
235
|
-
this._fileUploadOptions.tempFileDir = tempFileDir;
|
|
236
|
-
}
|
|
237
|
-
if (removeTempFile != null) {
|
|
238
|
-
this._fileUploadOptions.removeTempFile = removeTempFile;
|
|
239
|
-
}
|
|
175
|
+
* The 'useBodyParser' method is a middleware used to parse the request body of incoming HTTP requests.
|
|
176
|
+
* @param {object?}
|
|
177
|
+
* @property {array?} except the body parser with some methods
|
|
178
|
+
* @returns {this}
|
|
179
|
+
*/
|
|
180
|
+
useBodyParser({ except } = {}) {
|
|
240
181
|
this._globalMiddlewares.push(({ req }, next) => {
|
|
241
|
-
const contentType = req
|
|
182
|
+
const contentType = req?.headers['content-type'];
|
|
242
183
|
const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
|
|
243
|
-
|
|
244
|
-
|
|
184
|
+
if (except != null && Array.isArray(except)) {
|
|
185
|
+
if (except.some(v => v.toLocaleLowerCase() === (req.method)?.toLocaleLowerCase())) {
|
|
186
|
+
return next();
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (isFileUpload)
|
|
245
190
|
return next();
|
|
246
|
-
if (
|
|
191
|
+
if (req?.body != null)
|
|
247
192
|
return next();
|
|
248
|
-
Promise.resolve(this._parser.
|
|
193
|
+
Promise.resolve(this._parser.body(req))
|
|
249
194
|
.then(r => {
|
|
250
|
-
req.
|
|
251
|
-
req._bodyParser = r.body;
|
|
195
|
+
req.body = r;
|
|
252
196
|
return next();
|
|
253
197
|
})
|
|
254
198
|
.catch(_ => next());
|
|
@@ -256,21 +200,40 @@ class Spear {
|
|
|
256
200
|
return this;
|
|
257
201
|
}
|
|
258
202
|
/**
|
|
259
|
-
* The '
|
|
203
|
+
* The 'useFileUpload' method is a middleware used to handler file uploads. It adds a file upload of incoming HTTP requests.
|
|
260
204
|
*
|
|
261
|
-
* @
|
|
205
|
+
* @param {?Object}
|
|
206
|
+
* @property {?number} limits
|
|
207
|
+
* @property {?string} tempFileDir
|
|
208
|
+
* @property {?Object} removeTempFile
|
|
209
|
+
* @property {boolean} removeTempFile.remove
|
|
210
|
+
* @property {number} removeTempFile.ms
|
|
211
|
+
* @returns
|
|
262
212
|
*/
|
|
263
|
-
|
|
213
|
+
useFileUpload({ limit, tempFileDir, removeTempFile } = {}) {
|
|
214
|
+
if (limit != null) {
|
|
215
|
+
this._fileUploadOptions.limit = limit;
|
|
216
|
+
}
|
|
217
|
+
if (tempFileDir != null) {
|
|
218
|
+
this._fileUploadOptions.tempFileDir = tempFileDir;
|
|
219
|
+
}
|
|
220
|
+
if (removeTempFile != null) {
|
|
221
|
+
this._fileUploadOptions.removeTempFile = removeTempFile;
|
|
222
|
+
}
|
|
264
223
|
this._globalMiddlewares.push(({ req }, next) => {
|
|
265
|
-
const contentType = req
|
|
224
|
+
const contentType = req?.headers['content-type'];
|
|
266
225
|
const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
|
|
267
|
-
if (
|
|
226
|
+
if (req.method === 'GET') {
|
|
268
227
|
return next();
|
|
269
|
-
|
|
228
|
+
}
|
|
229
|
+
if (!isFileUpload)
|
|
270
230
|
return next();
|
|
271
|
-
|
|
231
|
+
if (req?.files != null)
|
|
232
|
+
return next();
|
|
233
|
+
Promise.resolve(this._parser.files({ req, options: this._fileUploadOptions }))
|
|
272
234
|
.then(r => {
|
|
273
|
-
req.
|
|
235
|
+
req.files = r.files;
|
|
236
|
+
req.body = r.body;
|
|
274
237
|
return next();
|
|
275
238
|
})
|
|
276
239
|
.catch(_ => next());
|
|
@@ -284,9 +247,9 @@ class Spear {
|
|
|
284
247
|
*/
|
|
285
248
|
useCookiesParser() {
|
|
286
249
|
this._globalMiddlewares.push(({ req }, next) => {
|
|
287
|
-
if (
|
|
250
|
+
if (req?.cookies != null)
|
|
288
251
|
return next();
|
|
289
|
-
req.
|
|
252
|
+
req.cookies = this._parser.cookies(req);
|
|
290
253
|
return next();
|
|
291
254
|
});
|
|
292
255
|
return this;
|
|
@@ -318,10 +281,10 @@ class Spear {
|
|
|
318
281
|
useSwagger({ path, servers, info, tags } = {}) {
|
|
319
282
|
this._swagger = {
|
|
320
283
|
use: true,
|
|
321
|
-
path: path
|
|
322
|
-
servers: servers
|
|
323
|
-
tags: tags
|
|
324
|
-
info: info
|
|
284
|
+
path: path ?? this._swagger.path,
|
|
285
|
+
servers: servers ?? this._swagger.servers,
|
|
286
|
+
tags: tags ?? this._swagger.tags,
|
|
287
|
+
info: info ?? this._swagger.info
|
|
325
288
|
};
|
|
326
289
|
return this;
|
|
327
290
|
}
|
|
@@ -332,35 +295,32 @@ class Spear {
|
|
|
332
295
|
* @param {function} callback
|
|
333
296
|
* @returns
|
|
334
297
|
*/
|
|
335
|
-
listen() {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
this._cluster || typeof this._cluster === 'number') {
|
|
345
|
-
this._clusterMode(server, Number(port), callback);
|
|
346
|
-
return;
|
|
347
|
-
}
|
|
348
|
-
server.listen(port == null ? 3000 : port, () => {
|
|
349
|
-
if (callback)
|
|
350
|
-
callback({ server, port });
|
|
351
|
-
});
|
|
352
|
-
server.on('listening', () => {
|
|
353
|
-
this._onListeners.forEach(listener => listener());
|
|
354
|
-
if (this._swagger.use) {
|
|
355
|
-
this._swaggerHandler();
|
|
356
|
-
}
|
|
357
|
-
});
|
|
358
|
-
server.on('error', (_) => {
|
|
359
|
-
port = Math.floor(Math.random() * 8999) + 1000;
|
|
360
|
-
server.listen(port);
|
|
361
|
-
});
|
|
298
|
+
async listen(port = 3000, callback) {
|
|
299
|
+
if (arguments.length === 1 && typeof port === 'function') {
|
|
300
|
+
callback = port;
|
|
301
|
+
port = 3000;
|
|
302
|
+
}
|
|
303
|
+
const server = await this._createServer();
|
|
304
|
+
if (this._cluster != null &&
|
|
305
|
+
this._cluster || typeof this._cluster === 'number') {
|
|
306
|
+
this._clusterMode(server, Number(port), callback);
|
|
362
307
|
return;
|
|
308
|
+
}
|
|
309
|
+
server.listen(port == null ? 3000 : port, () => {
|
|
310
|
+
if (callback)
|
|
311
|
+
callback({ server, port });
|
|
312
|
+
});
|
|
313
|
+
server.on('listening', () => {
|
|
314
|
+
this._onListeners.forEach(listener => listener());
|
|
315
|
+
if (this._swagger.use) {
|
|
316
|
+
this._swaggerHandler();
|
|
317
|
+
}
|
|
363
318
|
});
|
|
319
|
+
server.on('error', (_) => {
|
|
320
|
+
port = Math.floor(Math.random() * 8999) + 1000;
|
|
321
|
+
server.listen(port);
|
|
322
|
+
});
|
|
323
|
+
return;
|
|
364
324
|
}
|
|
365
325
|
/**
|
|
366
326
|
* The 'enableCors' is used to enable the cors origins on the server.
|
|
@@ -372,8 +332,7 @@ class Spear {
|
|
|
372
332
|
*/
|
|
373
333
|
enableCors({ origins, credentials } = {}) {
|
|
374
334
|
this._globalMiddlewares.push(({ req, res }, next) => {
|
|
375
|
-
|
|
376
|
-
const origin = (_a = req.headers) === null || _a === void 0 ? void 0 : _a.origin;
|
|
335
|
+
const origin = req.headers?.origin;
|
|
377
336
|
if (origin == null)
|
|
378
337
|
return next();
|
|
379
338
|
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
|
|
@@ -421,9 +380,9 @@ class Spear {
|
|
|
421
380
|
* @param {function} notfound
|
|
422
381
|
* @returns
|
|
423
382
|
*/
|
|
424
|
-
notFoundHandler(
|
|
383
|
+
notFoundHandler(fn) {
|
|
425
384
|
const handler = ({ req, res }) => {
|
|
426
|
-
return
|
|
385
|
+
return fn({
|
|
427
386
|
req,
|
|
428
387
|
res: this._customizeResponse(req, res),
|
|
429
388
|
headers: {},
|
|
@@ -435,14 +394,10 @@ class Spear {
|
|
|
435
394
|
});
|
|
436
395
|
};
|
|
437
396
|
this._onListeners.push(() => {
|
|
438
|
-
return this.
|
|
397
|
+
return this.all('*', ...this._globalMiddlewares, handler);
|
|
439
398
|
});
|
|
440
399
|
return this;
|
|
441
400
|
}
|
|
442
|
-
low(path, ...handlers) {
|
|
443
|
-
this._router.get(this._normalizePath(this._globalPrefix, path), (req, res) => res.end('hello world!'));
|
|
444
|
-
return this;
|
|
445
|
-
}
|
|
446
401
|
/**
|
|
447
402
|
* The 'get' method is used to add the request handler to the router for the 'GET' method.
|
|
448
403
|
*
|
|
@@ -565,57 +520,33 @@ class Spear {
|
|
|
565
520
|
}
|
|
566
521
|
return;
|
|
567
522
|
}
|
|
568
|
-
_import(dir, pattern) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
const
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
return directory.isDirectory() ? this._import(newDir) : newDir;
|
|
575
|
-
}
|
|
576
|
-
return directory.isDirectory()
|
|
577
|
-
? this._import(newDir)
|
|
578
|
-
: pattern.test(directory.name)
|
|
579
|
-
? newDir
|
|
580
|
-
: null;
|
|
581
|
-
}))).filter(d => d != null);
|
|
582
|
-
return [].concat(...files);
|
|
583
|
-
});
|
|
584
|
-
}
|
|
585
|
-
_registerControllers() {
|
|
586
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
587
|
-
var _a, _b, _c, _d, _e, _f;
|
|
588
|
-
if (this._controllers == null)
|
|
589
|
-
return;
|
|
590
|
-
if (!Array.isArray(this._controllers)) {
|
|
591
|
-
const controllers = yield this._import(this._controllers.folder, this._controllers.name);
|
|
592
|
-
for (const file of controllers) {
|
|
593
|
-
const response = yield Promise.resolve(`${file}`).then(s => __importStar(require(s)));
|
|
594
|
-
const controller = response === null || response === void 0 ? void 0 : response.default;
|
|
595
|
-
const controllerInstance = new controller();
|
|
596
|
-
const prefixPath = (_a = Reflect.getMetadata("controllers", controller)) !== null && _a !== void 0 ? _a : '';
|
|
597
|
-
const routers = (_b = Reflect.getMetadata("routers", controller)) !== null && _b !== void 0 ? _b : [];
|
|
598
|
-
const swaggers = (_c = Reflect.getMetadata("swaggers", controller)) !== null && _c !== void 0 ? _c : [];
|
|
599
|
-
if (prefixPath == null)
|
|
600
|
-
continue;
|
|
601
|
-
for (const { method, path, handler } of Array.from(routers)) {
|
|
602
|
-
const find = Array.from(swaggers).find(s => s.handler === handler);
|
|
603
|
-
if (find != null) {
|
|
604
|
-
this._swaggerAdditional = [
|
|
605
|
-
...this._swaggerAdditional,
|
|
606
|
-
Object.assign(Object.assign({}, find), { path: this._normalizePath(this._globalPrefix, prefixPath, path), method })
|
|
607
|
-
];
|
|
608
|
-
}
|
|
609
|
-
this[method](this._normalizePath(this._globalPrefix, prefixPath, path), this._wrapResponse(controllerInstance[String(handler)].bind(controllerInstance)));
|
|
610
|
-
}
|
|
611
|
-
}
|
|
612
|
-
return;
|
|
523
|
+
async _import(dir, pattern) {
|
|
524
|
+
const directories = fs_1.default.readdirSync(dir, { withFileTypes: true });
|
|
525
|
+
const files = (await Promise.all(directories.map((directory) => {
|
|
526
|
+
const newDir = path_1.default.resolve(String(dir), directory.name);
|
|
527
|
+
if (pattern == null) {
|
|
528
|
+
return directory.isDirectory() ? this._import(newDir) : newDir;
|
|
613
529
|
}
|
|
614
|
-
|
|
530
|
+
return directory.isDirectory()
|
|
531
|
+
? this._import(newDir)
|
|
532
|
+
: pattern.test(directory.name)
|
|
533
|
+
? newDir
|
|
534
|
+
: null;
|
|
535
|
+
}))).filter(d => d != null);
|
|
536
|
+
return [].concat(...files);
|
|
537
|
+
}
|
|
538
|
+
async _registerControllers() {
|
|
539
|
+
if (this._controllers == null)
|
|
540
|
+
return;
|
|
541
|
+
if (!Array.isArray(this._controllers)) {
|
|
542
|
+
const controllers = await this._import(this._controllers.folder, this._controllers.name);
|
|
543
|
+
for (const file of controllers) {
|
|
544
|
+
const response = await Promise.resolve(`${file}`).then(s => __importStar(require(s)));
|
|
545
|
+
const controller = response?.default;
|
|
615
546
|
const controllerInstance = new controller();
|
|
616
|
-
const prefixPath =
|
|
617
|
-
const routers =
|
|
618
|
-
const swaggers =
|
|
547
|
+
const prefixPath = Reflect.getMetadata("controllers", controller) ?? '';
|
|
548
|
+
const routers = Reflect.getMetadata("routers", controller) ?? [];
|
|
549
|
+
const swaggers = Reflect.getMetadata("swaggers", controller) ?? [];
|
|
619
550
|
if (prefixPath == null)
|
|
620
551
|
continue;
|
|
621
552
|
for (const { method, path, handler } of Array.from(routers)) {
|
|
@@ -623,33 +554,58 @@ class Spear {
|
|
|
623
554
|
if (find != null) {
|
|
624
555
|
this._swaggerAdditional = [
|
|
625
556
|
...this._swaggerAdditional,
|
|
626
|
-
|
|
557
|
+
{
|
|
558
|
+
...find,
|
|
559
|
+
path: this._normalizePath(this._globalPrefix, prefixPath, path),
|
|
560
|
+
method
|
|
561
|
+
}
|
|
627
562
|
];
|
|
628
563
|
}
|
|
629
|
-
this[method](this._normalizePath(this._globalPrefix, prefixPath, path),
|
|
564
|
+
this[method](this._normalizePath(this._globalPrefix, prefixPath, path), controllerInstance[String(handler)].bind(controllerInstance));
|
|
630
565
|
}
|
|
631
566
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
567
|
+
return;
|
|
568
|
+
}
|
|
569
|
+
for (const controller of this._controllers) {
|
|
570
|
+
const controllerInstance = new controller();
|
|
571
|
+
const prefixPath = Reflect.getMetadata("controllers", controller) ?? '';
|
|
572
|
+
const routers = Reflect.getMetadata("routers", controller) ?? [];
|
|
573
|
+
const swaggers = Reflect.getMetadata("swaggers", controller) ?? [];
|
|
574
|
+
if (prefixPath == null)
|
|
575
|
+
continue;
|
|
576
|
+
for (const { method, path, handler } of Array.from(routers)) {
|
|
577
|
+
const find = Array.from(swaggers).find(s => s.handler === handler);
|
|
578
|
+
if (find != null) {
|
|
579
|
+
this._swaggerAdditional = [
|
|
580
|
+
...this._swaggerAdditional,
|
|
581
|
+
{
|
|
582
|
+
...find,
|
|
583
|
+
path: this._normalizePath(this._globalPrefix, prefixPath, path),
|
|
584
|
+
method
|
|
585
|
+
}
|
|
586
|
+
];
|
|
644
587
|
}
|
|
645
|
-
|
|
588
|
+
this[method](this._normalizePath(this._globalPrefix, prefixPath, path), controllerInstance[String(handler)].bind(controllerInstance));
|
|
646
589
|
}
|
|
647
|
-
|
|
648
|
-
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
async _registerMiddlewares() {
|
|
593
|
+
if (this._middlewares == null)
|
|
594
|
+
return;
|
|
595
|
+
if (!Array.isArray(this._middlewares)) {
|
|
596
|
+
const middlewares = await this._import(this._middlewares.folder, this._middlewares.name);
|
|
597
|
+
for (const file of middlewares) {
|
|
598
|
+
const response = await Promise.resolve(`${file}`).then(s => __importStar(require(s)));
|
|
599
|
+
const middleware = response?.default;
|
|
649
600
|
this.use(middleware);
|
|
650
601
|
}
|
|
651
602
|
return;
|
|
652
|
-
}
|
|
603
|
+
}
|
|
604
|
+
const middlewares = this._middlewares;
|
|
605
|
+
for (const middleware of middlewares) {
|
|
606
|
+
this.use(middleware);
|
|
607
|
+
}
|
|
608
|
+
return;
|
|
653
609
|
}
|
|
654
610
|
_customizeResponse(req, res) {
|
|
655
611
|
const response = res;
|
|
@@ -670,9 +626,13 @@ class Spear {
|
|
|
670
626
|
return res.end();
|
|
671
627
|
}
|
|
672
628
|
if (this._formatResponse != null) {
|
|
673
|
-
return res.end(JSON.stringify(this._formatResponse(
|
|
629
|
+
return res.end(JSON.stringify(this._formatResponse({
|
|
630
|
+
...results
|
|
631
|
+
}, res.statusCode), null, 2));
|
|
674
632
|
}
|
|
675
|
-
return res.end(JSON.stringify(
|
|
633
|
+
return res.end(JSON.stringify({
|
|
634
|
+
...results,
|
|
635
|
+
}, null, 2));
|
|
676
636
|
};
|
|
677
637
|
response.send = (results) => {
|
|
678
638
|
res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' });
|
|
@@ -683,16 +643,15 @@ class Spear {
|
|
|
683
643
|
return res.end(results);
|
|
684
644
|
};
|
|
685
645
|
response.error = (err) => {
|
|
686
|
-
|
|
687
|
-
let code = +((_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.data) === null || _b === void 0 ? void 0 : _b.code) ||
|
|
646
|
+
let code = +err.response?.data?.code ||
|
|
688
647
|
+err.code ||
|
|
689
648
|
+err.status ||
|
|
690
649
|
+err.statusCode ||
|
|
691
|
-
+
|
|
650
|
+
+err.response?.data?.statusCode ||
|
|
692
651
|
500;
|
|
693
652
|
code = (code == null || typeof code !== 'number') ? 500 : Number.isNaN(code) ? 500 : code < 400 ? 500 : code;
|
|
694
|
-
const message =
|
|
695
|
-
|
|
653
|
+
const message = err.response?.data?.errorMessage ||
|
|
654
|
+
err.response?.data?.message ||
|
|
696
655
|
err.message ||
|
|
697
656
|
`The url '${req.url}' resulted in a server error. Please investigate.`;
|
|
698
657
|
response.status(code);
|
|
@@ -720,7 +679,7 @@ class Spear {
|
|
|
720
679
|
};
|
|
721
680
|
response.badRequest = (message) => {
|
|
722
681
|
response.status(400);
|
|
723
|
-
message = message
|
|
682
|
+
message = message ?? `The url '${req.url}' resulted in a bad request. Please review the data and try again.`;
|
|
724
683
|
if (this._formatResponse != null) {
|
|
725
684
|
return res.end(JSON.stringify(this._formatResponse({ message }, 400), null, 2));
|
|
726
685
|
}
|
|
@@ -730,7 +689,7 @@ class Spear {
|
|
|
730
689
|
};
|
|
731
690
|
response.unauthorized = (message) => {
|
|
732
691
|
response.status(401);
|
|
733
|
-
message = message
|
|
692
|
+
message = message ?? `The url '${req.url}' is unauthorized. Please verify.`;
|
|
734
693
|
if (this._formatResponse != null) {
|
|
735
694
|
return res.end(JSON.stringify(this._formatResponse({ message }, 401), null, 2));
|
|
736
695
|
}
|
|
@@ -740,7 +699,7 @@ class Spear {
|
|
|
740
699
|
};
|
|
741
700
|
response.paymentRequired = (message) => {
|
|
742
701
|
response.status(402);
|
|
743
|
-
message = message
|
|
702
|
+
message = message ?? `The url '${req.url}' requires payment. Please proceed with payment.`;
|
|
744
703
|
if (this._formatResponse != null) {
|
|
745
704
|
return res.end(JSON.stringify(this._formatResponse({ message }, 402), null, 2));
|
|
746
705
|
}
|
|
@@ -750,7 +709,7 @@ class Spear {
|
|
|
750
709
|
};
|
|
751
710
|
response.forbidden = (message) => {
|
|
752
711
|
response.status(403);
|
|
753
|
-
message = message
|
|
712
|
+
message = message ?? `The url '${req.url}' is forbidden. Please check the permissions or access rights.`;
|
|
754
713
|
if (this._formatResponse != null) {
|
|
755
714
|
return res.end(JSON.stringify(this._formatResponse({ message }, 403), null, 2));
|
|
756
715
|
}
|
|
@@ -760,7 +719,7 @@ class Spear {
|
|
|
760
719
|
};
|
|
761
720
|
response.notFound = (message) => {
|
|
762
721
|
response.status(404);
|
|
763
|
-
message = message
|
|
722
|
+
message = message ?? `The url '${req.url}' was not found. Please re-check the your url again`;
|
|
764
723
|
if (this._formatResponse != null) {
|
|
765
724
|
return res.end(JSON.stringify(this._formatResponse({ message }, 404), null, 2));
|
|
766
725
|
}
|
|
@@ -770,7 +729,7 @@ class Spear {
|
|
|
770
729
|
};
|
|
771
730
|
response.serverError = (message) => {
|
|
772
731
|
response.status(500);
|
|
773
|
-
message = message
|
|
732
|
+
message = message ?? `The url '${req.url}' resulted in a server error. Please investigate.`;
|
|
774
733
|
if (this._formatResponse != null) {
|
|
775
734
|
return res.end(JSON.stringify(this._formatResponse({ message }, 500), null, 2));
|
|
776
735
|
}
|
|
@@ -821,11 +780,11 @@ class Spear {
|
|
|
821
780
|
ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
822
781
|
if (this._formatResponse != null) {
|
|
823
782
|
return ctx.res.end(JSON.stringify(this._formatResponse({
|
|
824
|
-
message: err
|
|
783
|
+
message: err?.message,
|
|
825
784
|
}, ctx.res.statusCode), null, 2));
|
|
826
785
|
}
|
|
827
786
|
return ctx.res.end(JSON.stringify({
|
|
828
|
-
message: err
|
|
787
|
+
message: err?.message,
|
|
829
788
|
}, null, 2));
|
|
830
789
|
}
|
|
831
790
|
if (this._errorHandler != null) {
|
|
@@ -842,6 +801,56 @@ class Spear {
|
|
|
842
801
|
}, null, 2));
|
|
843
802
|
};
|
|
844
803
|
}
|
|
804
|
+
_wrapHandlers = (...handlers) => {
|
|
805
|
+
return (req, res, params) => {
|
|
806
|
+
const runHandler = (index = 0) => {
|
|
807
|
+
const response = this._customizeResponse(req, res);
|
|
808
|
+
const request = req;
|
|
809
|
+
const body = request.body;
|
|
810
|
+
const files = request.files;
|
|
811
|
+
const cookies = request.cookies;
|
|
812
|
+
const headers = request.headers;
|
|
813
|
+
const query = { ...(0, url_1.parse)(String(req.url), true).query };
|
|
814
|
+
const RecordOrEmptyRecord = (data) => {
|
|
815
|
+
if (data == null)
|
|
816
|
+
return {};
|
|
817
|
+
return Object.keys(data).length ? data : {};
|
|
818
|
+
};
|
|
819
|
+
const ctx = {
|
|
820
|
+
req,
|
|
821
|
+
res: response,
|
|
822
|
+
headers: RecordOrEmptyRecord(headers),
|
|
823
|
+
params: RecordOrEmptyRecord(params),
|
|
824
|
+
query: RecordOrEmptyRecord(query),
|
|
825
|
+
body: RecordOrEmptyRecord(body),
|
|
826
|
+
files: RecordOrEmptyRecord(files),
|
|
827
|
+
cookies: RecordOrEmptyRecord(cookies)
|
|
828
|
+
};
|
|
829
|
+
if (index === handlers.length - 1) {
|
|
830
|
+
return this._wrapResponse(handlers[index].bind(handlers[index]))(ctx, this._nextFunction(ctx));
|
|
831
|
+
}
|
|
832
|
+
return handlers[index](ctx, () => {
|
|
833
|
+
return runHandler(index + 1);
|
|
834
|
+
});
|
|
835
|
+
};
|
|
836
|
+
try {
|
|
837
|
+
runHandler();
|
|
838
|
+
}
|
|
839
|
+
catch (err) {
|
|
840
|
+
const ctx = {
|
|
841
|
+
req,
|
|
842
|
+
res: this._customizeResponse(req, res),
|
|
843
|
+
params: Object.keys(params).length ? params : {},
|
|
844
|
+
headers: {},
|
|
845
|
+
query: {},
|
|
846
|
+
body: {},
|
|
847
|
+
files: {},
|
|
848
|
+
cookies: {}
|
|
849
|
+
};
|
|
850
|
+
return this._nextFunction(ctx)(err);
|
|
851
|
+
}
|
|
852
|
+
};
|
|
853
|
+
};
|
|
845
854
|
_wrapResponse(handler) {
|
|
846
855
|
return (ctx, next) => {
|
|
847
856
|
Promise.resolve(handler(ctx, next))
|
|
@@ -875,7 +884,8 @@ class Spear {
|
|
|
875
884
|
if (!ctx.res.headersSent) {
|
|
876
885
|
ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
877
886
|
}
|
|
878
|
-
ctx.res.end(JSON.stringify(result, null, 2));
|
|
887
|
+
ctx.res.end(result == null ? undefined : JSON.stringify(result, null, 2));
|
|
888
|
+
return;
|
|
879
889
|
})
|
|
880
890
|
.catch(_ => {
|
|
881
891
|
if (ctx.res.writableEnded)
|
|
@@ -884,22 +894,21 @@ class Spear {
|
|
|
884
894
|
ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
885
895
|
}
|
|
886
896
|
ctx.res.end(JSON.stringify({ error: 'Internal Server Error' }));
|
|
897
|
+
return;
|
|
887
898
|
});
|
|
888
899
|
};
|
|
889
900
|
}
|
|
890
|
-
_createServer() {
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
return this._router.lookup(req, res);
|
|
896
|
-
});
|
|
897
|
-
server.timeout = 0;
|
|
898
|
-
server.keepAliveTimeout = 0;
|
|
899
|
-
server.headersTimeout = 0;
|
|
900
|
-
server.requestTimeout = 0;
|
|
901
|
-
return server;
|
|
901
|
+
async _createServer() {
|
|
902
|
+
await this._registerMiddlewares();
|
|
903
|
+
await this._registerControllers();
|
|
904
|
+
const server = http_1.default.createServer((req, res) => {
|
|
905
|
+
return this._router.lookup(req, res);
|
|
902
906
|
});
|
|
907
|
+
server.timeout = 0;
|
|
908
|
+
server.keepAliveTimeout = 0;
|
|
909
|
+
server.headersTimeout = 0;
|
|
910
|
+
server.requestTimeout = 0;
|
|
911
|
+
return server;
|
|
903
912
|
}
|
|
904
913
|
_normalizePath(...paths) {
|
|
905
914
|
const path = paths
|
|
@@ -912,7 +921,11 @@ class Spear {
|
|
|
912
921
|
_swaggerHandler() {
|
|
913
922
|
const routes = this.routers
|
|
914
923
|
.routes.filter(r => ["GET", "POST", "PUT", "PATCH", "DELETE"].includes(r.method));
|
|
915
|
-
const { path, html, staticSwaggerHandler, staticUrl } = this._parser.swagger(
|
|
924
|
+
const { path, html, staticSwaggerHandler, staticUrl } = this._parser.swagger({
|
|
925
|
+
...this._swagger,
|
|
926
|
+
options: this._swaggerAdditional,
|
|
927
|
+
routes
|
|
928
|
+
});
|
|
916
929
|
this._router.get(staticUrl, staticSwaggerHandler);
|
|
917
930
|
this._router.get(String(path), (req, res) => {
|
|
918
931
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|