tspace-spear 1.2.1-beta.1 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +41 -11
  2. package/dist/lib/core/decorators/context.d.ts +91 -0
  3. package/dist/lib/core/decorators/context.js +86 -0
  4. package/dist/lib/core/decorators/context.js.map +1 -1
  5. package/dist/lib/core/decorators/controller.d.ts +34 -0
  6. package/dist/lib/core/decorators/controller.js +33 -0
  7. package/dist/lib/core/decorators/controller.js.map +1 -1
  8. package/dist/lib/core/decorators/headers.d.ts +31 -0
  9. package/dist/lib/core/decorators/headers.js +29 -0
  10. package/dist/lib/core/decorators/headers.js.map +1 -1
  11. package/dist/lib/core/decorators/index.d.ts +9 -0
  12. package/dist/lib/core/decorators/methods.d.ts +82 -0
  13. package/dist/lib/core/decorators/methods.js +78 -1
  14. package/dist/lib/core/decorators/methods.js.map +1 -1
  15. package/dist/lib/core/decorators/middleware.d.ts +39 -0
  16. package/dist/lib/core/decorators/middleware.js +37 -0
  17. package/dist/lib/core/decorators/middleware.js.map +1 -1
  18. package/dist/lib/core/decorators/statusCode.d.ts +26 -0
  19. package/dist/lib/core/decorators/statusCode.js +24 -0
  20. package/dist/lib/core/decorators/statusCode.js.map +1 -1
  21. package/dist/lib/core/decorators/swagger.d.ts +36 -0
  22. package/dist/lib/core/decorators/swagger.js +35 -1
  23. package/dist/lib/core/decorators/swagger.js.map +1 -1
  24. package/dist/lib/core/server/index.d.ts +292 -0
  25. package/dist/lib/core/server/index.js +363 -359
  26. package/dist/lib/core/server/index.js.map +1 -1
  27. package/dist/lib/core/server/parser-factory.d.ts +23 -0
  28. package/dist/lib/core/server/parser-factory.js.map +1 -1
  29. package/dist/lib/core/server/router.d.ts +99 -0
  30. package/dist/lib/core/server/router.js +34 -0
  31. package/dist/lib/core/server/router.js.map +1 -1
  32. package/dist/lib/core/types/index.d.ts +228 -0
  33. package/dist/lib/index.d.ts +11 -0
  34. package/dist/tests/benchmark.test.d.ts +1 -0
  35. package/dist/tests/benchmark.test.js +2 -11
  36. package/dist/tests/benchmark.test.js.map +1 -1
  37. package/package.json +12 -10
  38. package/dist/lib/core/server/radix-router.js +0 -65
  39. package/dist/lib/core/server/radix-router.js.map +0 -1
@@ -15,18 +15,30 @@ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (
15
15
  }) : function(o, v) {
16
16
  o["default"] = v;
17
17
  });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
25
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
26
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
27
37
  };
28
38
  Object.defineProperty(exports, "__esModule", { value: true });
29
39
  exports.Spear = exports.Application = void 0;
40
+ const http_1 = __importStar(require("http"));
41
+ const find_my_way_1 = __importDefault(require("find-my-way"));
30
42
  const cluster_1 = __importDefault(require("cluster"));
31
43
  const os_1 = __importDefault(require("os"));
32
44
  const fs_1 = __importDefault(require("fs"));
@@ -34,8 +46,6 @@ const path_1 = __importDefault(require("path"));
34
46
  const url_1 = require("url");
35
47
  const on_finished_1 = __importDefault(require("on-finished"));
36
48
  const ws_1 = __importDefault(require("ws"));
37
- const http_1 = __importStar(require("http"));
38
- const find_my_way_1 = __importDefault(require("find-my-way"));
39
49
  const parser_factory_1 = require("./parser-factory");
40
50
  /**
41
51
  *
@@ -76,10 +86,10 @@ class Spear {
76
86
  version: "1.0.0"
77
87
  }
78
88
  };
89
+ _swaggerSpecs = [];
79
90
  _wss;
80
91
  _ws;
81
92
  _wsOptions;
82
- _swaggerAdditional = [];
83
93
  _errorHandler = null;
84
94
  _globalMiddlewares = [];
85
95
  _formatResponse = null;
@@ -97,7 +107,6 @@ class Spear {
97
107
  this.useLogger();
98
108
  if (cluster)
99
109
  this.useCluster(cluster);
100
- this._cluster = cluster;
101
110
  this._controllers = controllers;
102
111
  this._middlewares = middlewares;
103
112
  this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
@@ -260,11 +269,11 @@ class Spear {
260
269
  }
261
270
  this._globalMiddlewares.push((ctx, next) => {
262
271
  const { req } = ctx;
263
- const contentType = req?.headers['content-type'];
264
- const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
265
272
  if (req.method === 'GET') {
266
273
  return next();
267
274
  }
275
+ const contentType = req?.headers['content-type'];
276
+ const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
268
277
  if (!isFileUpload)
269
278
  return next();
270
279
  if (req?.files != null)
@@ -379,7 +388,7 @@ class Spear {
379
388
  const origin = req.headers?.origin ?? null;
380
389
  if (origin == null)
381
390
  return;
382
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
391
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS');
383
392
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
384
393
  if (Array.isArray(origins) && origins.length) {
385
394
  for (const o of origins) {
@@ -404,27 +413,6 @@ class Spear {
404
413
  });
405
414
  return this;
406
415
  }
407
- /**
408
- * The 'enableCors' is used to enable the cors origins on the server.
409
- *
410
- * @params {Object}
411
- * @property {(string | RegExp)[]} origins
412
- * @property {boolean} credentials
413
- * @returns
414
- */
415
- enableCors({ origins, credentials } = {}) {
416
- return this.cors({ origins, credentials });
417
- }
418
- /**
419
- * The 'formaT.Response' method is used to format the response
420
- *
421
- * @param {function} format
422
- * @returns
423
- */
424
- formatResponse(format) {
425
- this._formatResponse = format;
426
- return this;
427
- }
428
416
  /**
429
417
  * The 'response' method is used to format the response
430
418
  *
@@ -435,18 +423,6 @@ class Spear {
435
423
  this._formatResponse = format;
436
424
  return this;
437
425
  }
438
- /**
439
- * The 'errorHandler' method is middleware that is specifically designed to handle errors.
440
- *
441
- * that occur during the processing of requests
442
- *
443
- * @param {function} error
444
- * @returns
445
- */
446
- errorHandler(error) {
447
- this._errorHandler = error;
448
- return this;
449
- }
450
426
  /**
451
427
  * The 'catch' method is middleware that is specifically designed to handle errors.
452
428
  *
@@ -459,34 +435,10 @@ class Spear {
459
435
  this._errorHandler = error;
460
436
  return this;
461
437
  }
462
- /**
463
- * The 'notFoundHandler' method is middleware that is specifically designed to handle errors notfound that occur during the processing of requests
464
- *
465
- * @param {function} notfound
466
- * @returns
467
- */
468
- notFoundHandler(fn) {
469
- const handler = ({ req, res }) => {
470
- return fn({
471
- req,
472
- res: this._customizeResponse(req, res),
473
- headers: {},
474
- query: {},
475
- files: {},
476
- body: {},
477
- params: {},
478
- cookies: {}
479
- });
480
- };
481
- this._onListeners.push(() => {
482
- return this.all('*', ...this._globalMiddlewares, handler);
483
- });
484
- return this;
485
- }
486
438
  /**
487
439
  * The 'notfound' method is middleware that is specifically designed to handle errors notfound that occur during the processing of requests
488
440
  *
489
- * @param {function} notfound
441
+ * @param {function} fn
490
442
  * @returns
491
443
  */
492
444
  notfound(fn) {
@@ -583,7 +535,7 @@ class Spear {
583
535
  return this;
584
536
  }
585
537
  /**
586
- * The 'all' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' methods.
538
+ * The 'head' method is used to add the request handler to the router for 'HEAD' methods.
587
539
  *
588
540
  * @param {string} path
589
541
  * @callback {...Function[]} handlers of the middlewares
@@ -591,14 +543,29 @@ class Spear {
591
543
  * @property {function} next - go to next function
592
544
  * @returns {this}
593
545
  */
594
- all(path, ...handlers) {
546
+ head(path, ...handlers) {
595
547
  this._onListeners.push(() => {
596
- return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
548
+ return this._router.head(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
597
549
  });
598
550
  return this;
599
551
  }
600
552
  /**
601
- * The 'any' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' methods.
553
+ * The 'head' method is used to add the request handler to the router for 'HEAD' methods.
554
+ *
555
+ * @param {string} path
556
+ * @callback {...Function[]} handlers of the middlewares
557
+ * @property {object} ctx - context { req , res , query , params , cookies , files , body}
558
+ * @property {function} next - go to next function
559
+ * @returns {this}
560
+ */
561
+ options(path, ...handlers) {
562
+ this._onListeners.push(() => {
563
+ return this._router.options(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
564
+ });
565
+ return this;
566
+ }
567
+ /**
568
+ * The 'any' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
602
569
  *
603
570
  * @param {string} path
604
571
  * @callback {...Function[]} handlers of the middlewares
@@ -612,6 +579,21 @@ class Spear {
612
579
  });
613
580
  return this;
614
581
  }
582
+ /**
583
+ * The 'all' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
584
+ *
585
+ * @param {string} path
586
+ * @callback {...Function[]} handlers of the middlewares
587
+ * @property {object} ctx - context { req , res , query , params , cookies , files , body}
588
+ * @property {function} next - go to next function
589
+ * @returns {this}
590
+ */
591
+ all(path, ...handlers) {
592
+ this._onListeners.push(() => {
593
+ return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
594
+ });
595
+ return this;
596
+ }
615
597
  _clusterMode({ server, port, hostname, callback }) {
616
598
  if (cluster_1.default.isPrimary) {
617
599
  const numCPUs = os_1.default.cpus().length;
@@ -688,8 +670,8 @@ class Spear {
688
670
  for (const { method, path, handler } of Array.from(routers)) {
689
671
  const find = Array.from(swaggers).find(s => s.handler === handler);
690
672
  if (find != null) {
691
- this._swaggerAdditional = [
692
- ...this._swaggerAdditional,
673
+ this._swaggerSpecs = [
674
+ ...this._swaggerSpecs,
693
675
  {
694
676
  ...find,
695
677
  path: this._normalizePath(this._globalPrefix, prefixPath, path),
@@ -712,8 +694,8 @@ class Spear {
712
694
  for (const { method, path, handler } of Array.from(routers)) {
713
695
  const find = Array.from(swaggers).find(s => s.handler === handler);
714
696
  if (find != null) {
715
- this._swaggerAdditional = [
716
- ...this._swaggerAdditional,
697
+ this._swaggerSpecs = [
698
+ ...this._swaggerSpecs,
717
699
  {
718
700
  ...find,
719
701
  path: this._normalizePath(this._globalPrefix, prefixPath, path),
@@ -743,7 +725,7 @@ class Spear {
743
725
  }
744
726
  const middleware = maybeMiddleware;
745
727
  if (typeof middleware !== "function") {
746
- console.log(`\x1b[31m[ControllerLoader ERROR]\x1b[0m \x1b[36m${file}\x1b[0m must export a controller class`);
728
+ console.log(`\x1b[31m[MiddlewareLoader ERROR]\x1b[0m \x1b[36m${file}\x1b[0m must export a middleware`);
747
729
  continue;
748
730
  }
749
731
  this.use(middleware);
@@ -758,296 +740,276 @@ class Spear {
758
740
  }
759
741
  _customizeResponse(req, res) {
760
742
  const response = res;
761
- // response.json = (results ?: Record<string,any>) => {
762
- // if (res.writableEnded) return;
763
- // if(typeof results === 'string') {
764
- // if(!res.headersSent) {
765
- // res.writeHead(200, { 'Content-Type': 'text/plain' })
766
- // }
767
- // return res.end(results)
768
- // }
769
- // if(!res.headersSent) {
770
- // res.writeHead(200, { 'Content-Type': 'application/json' })
771
- // }
772
- // if(results == null) {
773
- // if(this._formatResponse != null) {
774
- // return res.end(JSON.stringify(this._formatResponse(null, res.statusCode),null,2))
775
- // }
776
- // return res.end()
777
- // }
778
- // if(this._formatResponse != null) {
779
- // return res.end(JSON.stringify(
780
- // this._formatResponse({
781
- // ...results
782
- // }, res.statusCode) ,null, 2)
783
- // )
784
- // }
785
- // return res.end(JSON.stringify({
786
- // ...results,
787
- // },null,2))
788
- // }
789
- // response.send = (results : string) => {
790
- // if (res.writableEnded) return;
791
- // res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' })
792
- // return res.end(results)
793
- // }
794
- // response.html = (results : string) => {
795
- // if (res.writableEnded) return;
796
- // res.writeHead(res.statusCode, {'Content-Type': 'text/html'})
797
- // return res.end(results)
798
- // }
799
- // response.error = (err ) => {
800
- // let code =
801
- // +err.response?.data?.code ||
802
- // +err.code ||
803
- // +err.status ||
804
- // +err.statusCode ||
805
- // +err.response?.data?.statusCode ||
806
- // 500;
807
- // code = (code == null || typeof code !== 'number') ? 500 : Number.isNaN(code) ? 500 : code < 400 ? 500 : code
808
- // const message =
809
- // err.response?.data?.errorMessage ||
810
- // err.response?.data?.message ||
811
- // err.message ||
812
- // `The url '${req.url}' resulted in a server error. Please investigate.`
813
- // ;
814
- // response.status(code as any)
815
- // if(this._formatResponse != null) {
816
- // return res.end(JSON.stringify(this._formatResponse({ message }, code) ,null,2))
817
- // }
818
- // return res.end(JSON.stringify({
819
- // message : message
820
- // },null,2))
821
- // }
822
- // response.ok = (results ?: Record<string,any> ) => {
823
- // return response.json(results == null ? {} : results)
824
- // }
825
- // response.created = (results ?: Record<string,any>) => {
826
- // response.status(201)
827
- // return response.json(results == null ? {} : results)
828
- // }
829
- // response.accepted = (results ?: Record<string,any>) => {
830
- // response.status(202)
831
- // return response.json(results == null ? {} : results)
832
- // }
833
- // response.noContent = () => {
834
- // response.status(204)
835
- // return res.end()
836
- // }
837
- // response.badRequest = (message ?: string) => {
838
- // if (res.writableEnded) return;
839
- // response.status(400)
840
- // message = message ?? `The url '${req.url}' resulted in a bad request. Please review the data and try again.`
841
- // if(this._formatResponse != null) {
842
- // return res.end(JSON.stringify(this._formatResponse({ message }, 400) ,null,2))
843
- // }
844
- // return res.end(JSON.stringify({
845
- // message : message
846
- // },null,2))
847
- // }
848
- // response.unauthorized = (message ?: string) => {
849
- // response.status(401)
850
- // message = message ?? `The url '${req.url}' is unauthorized. Please verify.`
851
- // if(this._formatResponse != null) {
852
- // return res.end(JSON.stringify(this._formatResponse({ message }, 401) ,null,2))
853
- // }
854
- // return res.end(JSON.stringify({
855
- // message
856
- // },null,2))
857
- // }
858
- // response.paymentRequired = (message ?: string) => {
859
- // response.status(402)
860
- // message = message ?? `The url '${req.url}' requires payment. Please proceed with payment.`
861
- // if(this._formatResponse != null) {
862
- // return res.end(JSON.stringify(this._formatResponse({ message }, 402) ,null,2))
863
- // }
864
- // return res.end(JSON.stringify({
865
- // message
866
- // },null,2))
867
- // }
868
- // response.forbidden = (message ?: string) => {
869
- // response.status(403)
870
- // message = message ?? `The url '${req.url}' is forbidden. Please check the permissions or access rights.`
871
- // if(this._formatResponse != null) {
872
- // return res.end(JSON.stringify(this._formatResponse({ message }, 403) ,null,2))
873
- // }
874
- // return res.end(JSON.stringify({
875
- // message
876
- // },null,2))
877
- // }
878
- // response.notFound = (message ?: string) => {
879
- // response.status(404)
880
- // message = message ?? `The url '${req.url}' was not found. Please re-check the your url again.`
881
- // if(this._formatResponse != null) {
882
- // return res.end(JSON.stringify(this._formatResponse({ message }, 404) ,null,2))
883
- // }
884
- // return res.end(JSON.stringify({
885
- // message
886
- // },null,2))
887
- // }
888
- // response.tooManyRequests = (message ?: string) => {
889
- // response.status(429)
890
- // message = message ?? `The url '${req.url}' is too many request. Please wait and try agian.`
891
- // if(this._formatResponse != null) {
892
- // return res.end(JSON.stringify(this._formatResponse({ message }, 404) ,null,2))
893
- // }
894
- // return res.end(JSON.stringify({
895
- // message
896
- // },null,2))
897
- // }
898
- // response.serverError = (message ?: string) => {
899
- // response.status(500)
900
- // message = message ?? `The url '${req.url}' resulted in a server error. Please investigate.`
901
- // if(this._formatResponse != null) {
902
- // return res.end(JSON.stringify(this._formatResponse({ message }, 500) ,null,2))
903
- // }
904
- // return res.end(JSON.stringify({
905
- // message
906
- // },null,2))
907
- // }
908
- // response.status = (code : number) => {
909
- // res.writeHead(code, { 'Content-Type': 'application/json' })
910
- // return res as unknown as {
911
- // json : (data?: { [key: string]: any }) => void;
912
- // send : (message : string) => void;
913
- // }
914
- // }
915
- // response.setCookies = (cookies : Record<string,string | {
916
- // value : string
917
- // sameSite ?: 'Strict' | 'Lax' | 'None'
918
- // domain ?: string
919
- // secure ?: boolean
920
- // httpOnly ?: boolean
921
- // expires ?: Date
922
- // }> ) => {
923
- // for(const [key,v] of Object.entries(cookies)) {
924
- // if(typeof v === 'string') {
925
- // res.setHeader('Set-Cookie', `${key}=${v}`);
926
- // continue
927
- // }
928
- // if(v.value === '' || v.value == null) continue
929
- // let str = `${key}=${v.value}`
930
- // if(v.sameSite != null) {
931
- // str += ` ;SameSite=${v.sameSite}`
932
- // }
933
- // if(v.domain != null) {
934
- // str += ` ;Domain=${v.domain}`
935
- // }
936
- // if(v.httpOnly != null) {
937
- // str += ` ;HttpOnly`
938
- // }
939
- // if(v.secure != null) {
940
- // str += ` ;Secure`
941
- // }
942
- // if(v.expires != null) {
943
- // str += ` ;Expires=${v.expires.toUTCString()}`
944
- // }
945
- // res.setHeader('Set-Cookie', str);
946
- // }
947
- // }
948
- return response;
949
- }
950
- _nextFunction(ctx) {
951
- const NEXT_MESSAGE = "The 'next' function does not have any subsequent function.";
952
- return (err) => {
953
- if (err != null) {
954
- if (this._errorHandler != null) {
955
- return this._errorHandler(err, ctx);
743
+ response.json = (results) => {
744
+ if (res.writableEnded)
745
+ return;
746
+ if (typeof results === 'string') {
747
+ if (!res.headersSent) {
748
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
956
749
  }
957
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
750
+ return res.end(results);
751
+ }
752
+ if (!res.headersSent) {
753
+ res.writeHead(200, { 'Content-Type': 'application/json' });
754
+ }
755
+ if (results == null) {
958
756
  if (this._formatResponse != null) {
959
- return ctx.res.end(JSON.stringify(this._formatResponse({
960
- message: err?.message,
961
- }, ctx.res.statusCode), null, 2));
757
+ return res.end(JSON.stringify(this._formatResponse(null, res.statusCode), null, 2));
962
758
  }
963
- return ctx.res.end(JSON.stringify({
964
- message: err?.message,
965
- }, null, 2));
759
+ return res.end();
966
760
  }
967
- if (this._errorHandler != null) {
968
- return this._errorHandler(new Error(NEXT_MESSAGE), ctx);
761
+ if (this._formatResponse != null) {
762
+ return res.end(JSON.stringify(this._formatResponse({
763
+ ...results
764
+ }, res.statusCode), null, 2));
969
765
  }
970
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
766
+ return res.end(JSON.stringify({
767
+ ...results,
768
+ }, null, 2));
769
+ };
770
+ response.send = (results) => {
771
+ if (res.writableEnded)
772
+ return;
773
+ res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' });
774
+ return res.end(results);
775
+ };
776
+ response.html = (results) => {
777
+ if (res.writableEnded)
778
+ return;
779
+ res.writeHead(res.statusCode, { 'Content-Type': 'text/html' });
780
+ return res.end(results);
781
+ };
782
+ response.error = (err) => {
783
+ const statusCandidates = [
784
+ err?.response?.data?.code,
785
+ err?.code,
786
+ err?.status,
787
+ err?.statusCode,
788
+ err?.response?.data?.statusCode
789
+ ];
790
+ let code = statusCandidates
791
+ .map(v => Number(v))
792
+ .find(v => Number.isFinite(v) && v >= 400) ?? 500;
793
+ const message = err?.response?.data?.errorMessage ??
794
+ err?.response?.data?.message ??
795
+ err?.message ??
796
+ `The request '${req.url}' resulted in a server error.`;
797
+ response.status(code);
798
+ const payload = { message };
799
+ if (this._formatResponse) {
800
+ return res.end(JSON.stringify(this._formatResponse(payload, code)));
801
+ }
802
+ return res.end(JSON.stringify(payload));
803
+ };
804
+ response.ok = (results) => {
805
+ return response.json(results == null ? {} : results);
806
+ };
807
+ response.created = (results) => {
808
+ response.status(201);
809
+ return response.json(results == null ? {} : results);
810
+ };
811
+ response.accepted = (results) => {
812
+ response.status(202);
813
+ return response.json(results == null ? {} : results);
814
+ };
815
+ response.noContent = () => {
816
+ response.status(204);
817
+ return res.end();
818
+ };
819
+ response.badRequest = (message) => {
820
+ if (res.writableEnded)
821
+ return;
822
+ response.status(400);
823
+ message = message ?? `The request '${req.url}' resulted in a bad request. Please review the data and try again.`;
971
824
  if (this._formatResponse != null) {
972
- return ctx.res.end(JSON.stringify(this._formatResponse({
973
- message: NEXT_MESSAGE
974
- }, ctx.res.statusCode), null, 2));
825
+ return res.end(JSON.stringify(this._formatResponse({ message }, 400), null, 2));
975
826
  }
976
- return ctx.res.end(JSON.stringify({
977
- message: NEXT_MESSAGE
827
+ return res.end(JSON.stringify({
828
+ message: message
978
829
  }, null, 2));
979
830
  };
831
+ response.unauthorized = (message) => {
832
+ response.status(401);
833
+ message = message ?? `The request '${req.url}' is unauthorized. Please verify.`;
834
+ if (this._formatResponse != null) {
835
+ return res.end(JSON.stringify(this._formatResponse({ message }, 401), null, 2));
836
+ }
837
+ return res.end(JSON.stringify({
838
+ message
839
+ }, null, 2));
840
+ };
841
+ response.paymentRequired = (message) => {
842
+ response.status(402);
843
+ message = message ?? `The request '${req.url}' requires payment. Please proceed with payment.`;
844
+ if (this._formatResponse != null) {
845
+ return res.end(JSON.stringify(this._formatResponse({ message }, 402), null, 2));
846
+ }
847
+ return res.end(JSON.stringify({
848
+ message
849
+ }, null, 2));
850
+ };
851
+ response.forbidden = (message) => {
852
+ response.status(403);
853
+ message = message ?? `The request '${req.url}' is forbidden. Please check the permissions or access rights.`;
854
+ if (this._formatResponse != null) {
855
+ return res.end(JSON.stringify(this._formatResponse({ message }, 403), null, 2));
856
+ }
857
+ return res.end(JSON.stringify({
858
+ message
859
+ }, null, 2));
860
+ };
861
+ response.notFound = (message) => {
862
+ response.status(404);
863
+ message = message ?? `The request '${req.url}' was not found. Please re-check the your url again.`;
864
+ if (this._formatResponse != null) {
865
+ return res.end(JSON.stringify(this._formatResponse({ message }, 404), null, 2));
866
+ }
867
+ return res.end(JSON.stringify({
868
+ message
869
+ }, null, 2));
870
+ };
871
+ response.unprocessable = (message) => {
872
+ response.status(422);
873
+ message = message ?? `The request to '${req.url}' failed validation.`;
874
+ if (this._formatResponse != null) {
875
+ return res.end(JSON.stringify(this._formatResponse({ message }, 422), null, 2));
876
+ }
877
+ return res.end(JSON.stringify({
878
+ message
879
+ }, null, 2));
880
+ };
881
+ response.tooManyRequests = (message) => {
882
+ response.status(429);
883
+ message = message ?? `The request '${req.url}' is too many request. Please wait and try agian.`;
884
+ if (this._formatResponse != null) {
885
+ return res.end(JSON.stringify(this._formatResponse({ message }, 429), null, 2));
886
+ }
887
+ return res.end(JSON.stringify({
888
+ message
889
+ }, null, 2));
890
+ };
891
+ response.serverError = (message) => {
892
+ response.status(500);
893
+ message = message ?? `The request '${req.url}' resulted in a server error. Please investigate.`;
894
+ if (this._formatResponse != null) {
895
+ return res.end(JSON.stringify(this._formatResponse({ message }, 500), null, 2));
896
+ }
897
+ return res.end(JSON.stringify({
898
+ message
899
+ }, null, 2));
900
+ };
901
+ response.status = (code) => {
902
+ res.writeHead(code, { 'Content-Type': 'application/json' });
903
+ return res;
904
+ };
905
+ response.setCookies = (cookies) => {
906
+ for (const [key, v] of Object.entries(cookies)) {
907
+ if (typeof v === 'string') {
908
+ res.setHeader('Set-Cookie', `${key}=${v}`);
909
+ continue;
910
+ }
911
+ if (v.value === '' || v.value == null)
912
+ continue;
913
+ let str = `${key}=${v.value}`;
914
+ if (v.sameSite != null) {
915
+ str += ` ;SameSite=${v.sameSite}`;
916
+ }
917
+ if (v.domain != null) {
918
+ str += ` ;Domain=${v.domain}`;
919
+ }
920
+ if (v.httpOnly != null) {
921
+ str += ` ;HttpOnly`;
922
+ }
923
+ if (v.secure != null) {
924
+ str += ` ;Secure`;
925
+ }
926
+ if (v.expires != null) {
927
+ str += ` ;Expires=${v.expires.toUTCString()}`;
928
+ }
929
+ res.setHeader('Set-Cookie', str);
930
+ }
931
+ };
932
+ return response;
980
933
  }
981
- _wrapHandlers = (...handlers) => {
934
+ _wrapHandlers(...handlers) {
982
935
  return (req, res, params) => {
983
936
  const nextHandler = (index = 0) => {
984
- const response = this._customizeResponse(req, res);
985
- const request = req;
986
- request.params = params;
987
- const body = request.body;
988
- const files = request.files;
989
- const cookies = request.cookies;
990
- const headers = request.headers;
991
- const url = new url_1.URL(req.url, "http://localhost");
992
- const query = Object.fromEntries(url.searchParams);
993
- const RecordOrEmptyRecord = (data) => {
994
- if (data == null)
995
- return {};
996
- return Object.keys(data).length ? data : {};
997
- };
998
- const ctx = {
999
- req: request,
1000
- res: response,
1001
- headers: RecordOrEmptyRecord(headers),
1002
- params: RecordOrEmptyRecord(params),
1003
- query: RecordOrEmptyRecord(query),
1004
- body: RecordOrEmptyRecord(body),
1005
- files: RecordOrEmptyRecord(files),
1006
- cookies: RecordOrEmptyRecord(cookies)
1007
- };
1008
- if (index === handlers.length - 1) {
1009
- return this._wrapResponse(handlers[index]
1010
- .bind(handlers[index]))(ctx, this._nextFunction(ctx));
937
+ try {
938
+ const response = this._customizeResponse(req, res);
939
+ const request = req;
940
+ request.params = params;
941
+ const body = request.body;
942
+ const files = request.files;
943
+ const cookies = request.cookies;
944
+ const headers = request.headers;
945
+ const url = new url_1.URL(req.url, "http://localhost");
946
+ const query = Object.fromEntries(url.searchParams);
947
+ const RecordOrEmptyRecord = (data) => {
948
+ if (data == null)
949
+ return {};
950
+ return Object.keys(data).length ? data : {};
951
+ };
952
+ const ctx = {
953
+ req: request,
954
+ res: response,
955
+ headers: RecordOrEmptyRecord(headers),
956
+ params: RecordOrEmptyRecord(params),
957
+ query: RecordOrEmptyRecord(query),
958
+ body: RecordOrEmptyRecord(body),
959
+ files: RecordOrEmptyRecord(files),
960
+ cookies: RecordOrEmptyRecord(cookies)
961
+ };
962
+ if (index === handlers.length - 1) {
963
+ return this._wrapResponse(handlers[index]
964
+ .bind(handlers[index]))(ctx, this._nextFunction(ctx));
965
+ }
966
+ return handlers[index](ctx, () => {
967
+ return nextHandler(index + 1);
968
+ });
969
+ }
970
+ catch (err) {
971
+ const ctx = {
972
+ req,
973
+ res: this._customizeResponse(req, res),
974
+ params: Object.keys(params).length ? params : {},
975
+ headers: {},
976
+ query: {},
977
+ body: {},
978
+ files: {},
979
+ cookies: {}
980
+ };
981
+ return this._nextFunction(ctx)(err);
1011
982
  }
1012
- return handlers[index](ctx, () => {
1013
- return nextHandler(index + 1);
1014
- });
1015
983
  };
1016
- try {
1017
- if (res.writableEnded)
1018
- return;
1019
- return nextHandler();
1020
- }
1021
- catch (err) {
1022
- const ctx = {
1023
- req,
1024
- res: this._customizeResponse(req, res),
1025
- params: Object.keys(params).length ? params : {},
1026
- headers: {},
1027
- query: {},
1028
- body: {},
1029
- files: {},
1030
- cookies: {}
1031
- };
1032
- return this._nextFunction(ctx)(err);
1033
- }
984
+ if (res.writableEnded)
985
+ return;
986
+ return nextHandler();
1034
987
  };
1035
- };
988
+ }
1036
989
  _wrapResponse(handler) {
1037
990
  return (ctx, next) => {
1038
991
  Promise.resolve(handler(ctx, next))
1039
992
  .then(result => {
1040
993
  if (ctx.res.writableEnded)
1041
994
  return;
1042
- if (result instanceof http_1.ServerResponse)
995
+ if (result instanceof http_1.ServerResponse) {
996
+ if (result?.end) {
997
+ result.end();
998
+ }
1043
999
  return;
1044
- if (result == null)
1000
+ }
1001
+ if (result == null) {
1002
+ if (!ctx.res.headersSent) {
1003
+ ctx.res.writeHead(204, { 'Content-Type': 'text/plain' });
1004
+ }
1005
+ ctx.res.end();
1045
1006
  return;
1007
+ }
1046
1008
  if (typeof result === 'string') {
1047
1009
  if (!ctx.res.headersSent) {
1048
1010
  ctx.res.writeHead(200, { 'Content-Type': 'text/plain' });
1049
1011
  }
1050
- ctx.res.end(result);
1012
+ ctx.res.end(result ?? '');
1051
1013
  return;
1052
1014
  }
1053
1015
  if (this._formatResponse != null) {
@@ -1062,26 +1024,58 @@ class Spear {
1062
1024
  if (!ctx.res.headersSent) {
1063
1025
  ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
1064
1026
  }
1065
- ctx.res.end(JSON.stringify(formattedResult, null, 2));
1027
+ ctx.res.end(JSON.stringify(formattedResult));
1066
1028
  return;
1067
1029
  }
1068
1030
  if (!ctx.res.headersSent) {
1069
1031
  ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
1070
1032
  }
1071
- ctx.res.end(result == null ? undefined : JSON.stringify(result, null, 2));
1033
+ ctx.res.end(JSON.stringify(result));
1072
1034
  return;
1073
1035
  })
1074
1036
  .catch(err => {
1075
- if (ctx.res.writableEnded)
1076
- return;
1077
- if (!ctx.res.headersSent) {
1078
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1079
- }
1080
- ctx.res.end(JSON.stringify({ message: err?.message || 'Internal Server Error' }));
1081
- return;
1037
+ return this._nextFunction(ctx)(err);
1082
1038
  });
1083
1039
  };
1084
1040
  }
1041
+ _nextFunction(ctx) {
1042
+ const NEXT_MESSAGE = "The 'next' function does not have any subsequent function.";
1043
+ return (err) => {
1044
+ if (err != null) {
1045
+ if (this._errorHandler != null) {
1046
+ const callback = this._errorHandler(err, ctx);
1047
+ if (callback == null || !(callback instanceof http_1.ServerResponse)) {
1048
+ ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1049
+ return ctx.res.end(JSON.stringify({
1050
+ message: err?.message
1051
+ }, null, 2));
1052
+ }
1053
+ return callback;
1054
+ }
1055
+ ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1056
+ if (this._formatResponse != null) {
1057
+ return ctx.res.end(JSON.stringify(this._formatResponse({
1058
+ message: err?.message,
1059
+ }, ctx.res.statusCode), null, 2));
1060
+ }
1061
+ return ctx.res.end(JSON.stringify({
1062
+ message: err?.message
1063
+ }, null, 2));
1064
+ }
1065
+ if (this._errorHandler != null) {
1066
+ return this._errorHandler(new Error(NEXT_MESSAGE), ctx);
1067
+ }
1068
+ ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1069
+ if (this._formatResponse != null) {
1070
+ return ctx.res.end(JSON.stringify(this._formatResponse({
1071
+ message: NEXT_MESSAGE
1072
+ }, ctx.res.statusCode), null, 2));
1073
+ }
1074
+ return ctx.res.end(JSON.stringify({
1075
+ message: NEXT_MESSAGE
1076
+ }, null, 2));
1077
+ };
1078
+ }
1085
1079
  async _createServer() {
1086
1080
  await this._registerMiddlewares();
1087
1081
  await this._registerControllers();
@@ -1090,10 +1084,10 @@ class Spear {
1090
1084
  const server = http_1.default.createServer({ maxHeaderSize: 1024 * 1024 }, cors
1091
1085
  ? (req, res) => {
1092
1086
  cors(req, res);
1093
- lookup(req, res);
1087
+ return lookup(req, res);
1094
1088
  }
1095
1089
  : (req, res) => {
1096
- lookup(req, res);
1090
+ return lookup(req, res);
1097
1091
  });
1098
1092
  if (this._ws) {
1099
1093
  this._wss = new ws_1.default.Server({ server, ...this._wsOptions });
@@ -1126,18 +1120,28 @@ class Spear {
1126
1120
  .replace(/\/+/g, '/')
1127
1121
  .replace(/\/+$/, '');
1128
1122
  const normalizedPath = path.startsWith('/') ? path : `/${path}`;
1129
- return /\/api\/api/.test(normalizedPath) ? normalizedPath.replace(/\/api\/api\//, "/api/") : normalizedPath;
1123
+ return /\/api\/api/.test(normalizedPath)
1124
+ ? normalizedPath.replace(/\/api\/api\//, "/api/")
1125
+ : normalizedPath;
1130
1126
  }
1131
1127
  _swaggerHandler() {
1132
1128
  const routes = this.routers
1133
- .routes.filter(r => ["GET", "POST", "PUT", "PATCH", "DELETE"].includes(r.method));
1129
+ .routes
1130
+ .filter(r => {
1131
+ return [
1132
+ "GET", "POST",
1133
+ "PUT", "PATCH",
1134
+ "DELETE",
1135
+ "HEAD", "OPTIONS"
1136
+ ].includes(r.method);
1137
+ });
1134
1138
  const { path, html, staticSwaggerHandler, staticUrl } = this._parser.swagger({
1135
1139
  ...this._swagger,
1136
- specs: this._swaggerAdditional,
1140
+ specs: this._swaggerSpecs,
1137
1141
  routes
1138
1142
  });
1139
1143
  this._router.get(staticUrl, staticSwaggerHandler);
1140
- this._router.get(String(path), (req, res) => {
1144
+ this._router.get(path, (_, res) => {
1141
1145
  res.writeHead(200, { 'Content-Type': 'text/html' });
1142
1146
  res.write(html);
1143
1147
  return res.end();