tspace-spear 1.2.1-beta.1 → 1.2.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.
@@ -79,7 +79,7 @@ class Spear {
79
79
  _wss;
80
80
  _ws;
81
81
  _wsOptions;
82
- _swaggerAdditional = [];
82
+ _swaggerSpecs = [];
83
83
  _errorHandler = null;
84
84
  _globalMiddlewares = [];
85
85
  _formatResponse = null;
@@ -97,7 +97,6 @@ class Spear {
97
97
  this.useLogger();
98
98
  if (cluster)
99
99
  this.useCluster(cluster);
100
- this._cluster = cluster;
101
100
  this._controllers = controllers;
102
101
  this._middlewares = middlewares;
103
102
  this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
@@ -260,11 +259,11 @@ class Spear {
260
259
  }
261
260
  this._globalMiddlewares.push((ctx, next) => {
262
261
  const { req } = ctx;
263
- const contentType = req?.headers['content-type'];
264
- const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
265
262
  if (req.method === 'GET') {
266
263
  return next();
267
264
  }
265
+ const contentType = req?.headers['content-type'];
266
+ const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
268
267
  if (!isFileUpload)
269
268
  return next();
270
269
  if (req?.files != null)
@@ -379,7 +378,7 @@ class Spear {
379
378
  const origin = req.headers?.origin ?? null;
380
379
  if (origin == null)
381
380
  return;
382
- res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, OPTIONS');
381
+ res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS');
383
382
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
384
383
  if (Array.isArray(origins) && origins.length) {
385
384
  for (const o of origins) {
@@ -404,27 +403,6 @@ class Spear {
404
403
  });
405
404
  return this;
406
405
  }
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
406
  /**
429
407
  * The 'response' method is used to format the response
430
408
  *
@@ -435,18 +413,6 @@ class Spear {
435
413
  this._formatResponse = format;
436
414
  return this;
437
415
  }
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
416
  /**
451
417
  * The 'catch' method is middleware that is specifically designed to handle errors.
452
418
  *
@@ -459,34 +425,10 @@ class Spear {
459
425
  this._errorHandler = error;
460
426
  return this;
461
427
  }
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
428
  /**
487
429
  * The 'notfound' method is middleware that is specifically designed to handle errors notfound that occur during the processing of requests
488
430
  *
489
- * @param {function} notfound
431
+ * @param {function} fn
490
432
  * @returns
491
433
  */
492
434
  notfound(fn) {
@@ -583,7 +525,7 @@ class Spear {
583
525
  return this;
584
526
  }
585
527
  /**
586
- * The 'all' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' methods.
528
+ * The 'head' method is used to add the request handler to the router for 'HEAD' methods.
587
529
  *
588
530
  * @param {string} path
589
531
  * @callback {...Function[]} handlers of the middlewares
@@ -591,14 +533,29 @@ class Spear {
591
533
  * @property {function} next - go to next function
592
534
  * @returns {this}
593
535
  */
594
- all(path, ...handlers) {
536
+ head(path, ...handlers) {
595
537
  this._onListeners.push(() => {
596
- return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
538
+ return this._router.head(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
539
+ });
540
+ return this;
541
+ }
542
+ /**
543
+ * The 'head' method is used to add the request handler to the router for 'HEAD' methods.
544
+ *
545
+ * @param {string} path
546
+ * @callback {...Function[]} handlers of the middlewares
547
+ * @property {object} ctx - context { req , res , query , params , cookies , files , body}
548
+ * @property {function} next - go to next function
549
+ * @returns {this}
550
+ */
551
+ options(path, ...handlers) {
552
+ this._onListeners.push(() => {
553
+ return this._router.options(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
597
554
  });
598
555
  return this;
599
556
  }
600
557
  /**
601
- * The 'any' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' methods.
558
+ * The 'any' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
602
559
  *
603
560
  * @param {string} path
604
561
  * @callback {...Function[]} handlers of the middlewares
@@ -612,6 +569,21 @@ class Spear {
612
569
  });
613
570
  return this;
614
571
  }
572
+ /**
573
+ * The 'all' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
574
+ *
575
+ * @param {string} path
576
+ * @callback {...Function[]} handlers of the middlewares
577
+ * @property {object} ctx - context { req , res , query , params , cookies , files , body}
578
+ * @property {function} next - go to next function
579
+ * @returns {this}
580
+ */
581
+ all(path, ...handlers) {
582
+ this._onListeners.push(() => {
583
+ return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
584
+ });
585
+ return this;
586
+ }
615
587
  _clusterMode({ server, port, hostname, callback }) {
616
588
  if (cluster_1.default.isPrimary) {
617
589
  const numCPUs = os_1.default.cpus().length;
@@ -688,8 +660,8 @@ class Spear {
688
660
  for (const { method, path, handler } of Array.from(routers)) {
689
661
  const find = Array.from(swaggers).find(s => s.handler === handler);
690
662
  if (find != null) {
691
- this._swaggerAdditional = [
692
- ...this._swaggerAdditional,
663
+ this._swaggerSpecs = [
664
+ ...this._swaggerSpecs,
693
665
  {
694
666
  ...find,
695
667
  path: this._normalizePath(this._globalPrefix, prefixPath, path),
@@ -712,8 +684,8 @@ class Spear {
712
684
  for (const { method, path, handler } of Array.from(routers)) {
713
685
  const find = Array.from(swaggers).find(s => s.handler === handler);
714
686
  if (find != null) {
715
- this._swaggerAdditional = [
716
- ...this._swaggerAdditional,
687
+ this._swaggerSpecs = [
688
+ ...this._swaggerSpecs,
717
689
  {
718
690
  ...find,
719
691
  path: this._normalizePath(this._globalPrefix, prefixPath, path),
@@ -743,7 +715,7 @@ class Spear {
743
715
  }
744
716
  const middleware = maybeMiddleware;
745
717
  if (typeof middleware !== "function") {
746
- console.log(`\x1b[31m[ControllerLoader ERROR]\x1b[0m \x1b[36m${file}\x1b[0m must export a controller class`);
718
+ console.log(`\x1b[31m[MiddlewareLoader ERROR]\x1b[0m \x1b[36m${file}\x1b[0m must export a middleware`);
747
719
  continue;
748
720
  }
749
721
  this.use(middleware);
@@ -758,296 +730,276 @@ class Spear {
758
730
  }
759
731
  _customizeResponse(req, res) {
760
732
  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);
733
+ response.json = (results) => {
734
+ if (res.writableEnded)
735
+ return;
736
+ if (typeof results === 'string') {
737
+ if (!res.headersSent) {
738
+ res.writeHead(200, { 'Content-Type': 'text/plain' });
956
739
  }
957
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
740
+ return res.end(results);
741
+ }
742
+ if (!res.headersSent) {
743
+ res.writeHead(200, { 'Content-Type': 'application/json' });
744
+ }
745
+ if (results == null) {
958
746
  if (this._formatResponse != null) {
959
- return ctx.res.end(JSON.stringify(this._formatResponse({
960
- message: err?.message,
961
- }, ctx.res.statusCode), null, 2));
747
+ return res.end(JSON.stringify(this._formatResponse(null, res.statusCode), null, 2));
962
748
  }
963
- return ctx.res.end(JSON.stringify({
964
- message: err?.message,
965
- }, null, 2));
749
+ return res.end();
966
750
  }
967
- if (this._errorHandler != null) {
968
- return this._errorHandler(new Error(NEXT_MESSAGE), ctx);
751
+ if (this._formatResponse != null) {
752
+ return res.end(JSON.stringify(this._formatResponse({
753
+ ...results
754
+ }, res.statusCode), null, 2));
969
755
  }
970
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
756
+ return res.end(JSON.stringify({
757
+ ...results,
758
+ }, null, 2));
759
+ };
760
+ response.send = (results) => {
761
+ if (res.writableEnded)
762
+ return;
763
+ res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' });
764
+ return res.end(results);
765
+ };
766
+ response.html = (results) => {
767
+ if (res.writableEnded)
768
+ return;
769
+ res.writeHead(res.statusCode, { 'Content-Type': 'text/html' });
770
+ return res.end(results);
771
+ };
772
+ response.error = (err) => {
773
+ const statusCandidates = [
774
+ err?.response?.data?.code,
775
+ err?.code,
776
+ err?.status,
777
+ err?.statusCode,
778
+ err?.response?.data?.statusCode
779
+ ];
780
+ let code = statusCandidates
781
+ .map(v => Number(v))
782
+ .find(v => Number.isFinite(v) && v >= 400) ?? 500;
783
+ const message = err?.response?.data?.errorMessage ??
784
+ err?.response?.data?.message ??
785
+ err?.message ??
786
+ `The request '${req.url}' resulted in a server error.`;
787
+ response.status(code);
788
+ const payload = { message };
789
+ if (this._formatResponse) {
790
+ return res.end(JSON.stringify(this._formatResponse(payload, code)));
791
+ }
792
+ return res.end(JSON.stringify(payload));
793
+ };
794
+ response.ok = (results) => {
795
+ return response.json(results == null ? {} : results);
796
+ };
797
+ response.created = (results) => {
798
+ response.status(201);
799
+ return response.json(results == null ? {} : results);
800
+ };
801
+ response.accepted = (results) => {
802
+ response.status(202);
803
+ return response.json(results == null ? {} : results);
804
+ };
805
+ response.noContent = () => {
806
+ response.status(204);
807
+ return res.end();
808
+ };
809
+ response.badRequest = (message) => {
810
+ if (res.writableEnded)
811
+ return;
812
+ response.status(400);
813
+ message = message ?? `The request '${req.url}' resulted in a bad request. Please review the data and try again.`;
971
814
  if (this._formatResponse != null) {
972
- return ctx.res.end(JSON.stringify(this._formatResponse({
973
- message: NEXT_MESSAGE
974
- }, ctx.res.statusCode), null, 2));
815
+ return res.end(JSON.stringify(this._formatResponse({ message }, 400), null, 2));
975
816
  }
976
- return ctx.res.end(JSON.stringify({
977
- message: NEXT_MESSAGE
817
+ return res.end(JSON.stringify({
818
+ message: message
819
+ }, null, 2));
820
+ };
821
+ response.unauthorized = (message) => {
822
+ response.status(401);
823
+ message = message ?? `The request '${req.url}' is unauthorized. Please verify.`;
824
+ if (this._formatResponse != null) {
825
+ return res.end(JSON.stringify(this._formatResponse({ message }, 401), null, 2));
826
+ }
827
+ return res.end(JSON.stringify({
828
+ message
829
+ }, null, 2));
830
+ };
831
+ response.paymentRequired = (message) => {
832
+ response.status(402);
833
+ message = message ?? `The request '${req.url}' requires payment. Please proceed with payment.`;
834
+ if (this._formatResponse != null) {
835
+ return res.end(JSON.stringify(this._formatResponse({ message }, 402), null, 2));
836
+ }
837
+ return res.end(JSON.stringify({
838
+ message
839
+ }, null, 2));
840
+ };
841
+ response.forbidden = (message) => {
842
+ response.status(403);
843
+ message = message ?? `The request '${req.url}' is forbidden. Please check the permissions or access rights.`;
844
+ if (this._formatResponse != null) {
845
+ return res.end(JSON.stringify(this._formatResponse({ message }, 403), null, 2));
846
+ }
847
+ return res.end(JSON.stringify({
848
+ message
849
+ }, null, 2));
850
+ };
851
+ response.notFound = (message) => {
852
+ response.status(404);
853
+ message = message ?? `The request '${req.url}' was not found. Please re-check the your url again.`;
854
+ if (this._formatResponse != null) {
855
+ return res.end(JSON.stringify(this._formatResponse({ message }, 404), null, 2));
856
+ }
857
+ return res.end(JSON.stringify({
858
+ message
978
859
  }, null, 2));
979
860
  };
861
+ response.unprocessable = (message) => {
862
+ response.status(422);
863
+ message = message ?? `The request to '${req.url}' failed validation.`;
864
+ if (this._formatResponse != null) {
865
+ return res.end(JSON.stringify(this._formatResponse({ message }, 422), null, 2));
866
+ }
867
+ return res.end(JSON.stringify({
868
+ message
869
+ }, null, 2));
870
+ };
871
+ response.tooManyRequests = (message) => {
872
+ response.status(429);
873
+ message = message ?? `The request '${req.url}' is too many request. Please wait and try agian.`;
874
+ if (this._formatResponse != null) {
875
+ return res.end(JSON.stringify(this._formatResponse({ message }, 429), null, 2));
876
+ }
877
+ return res.end(JSON.stringify({
878
+ message
879
+ }, null, 2));
880
+ };
881
+ response.serverError = (message) => {
882
+ response.status(500);
883
+ message = message ?? `The request '${req.url}' resulted in a server error. Please investigate.`;
884
+ if (this._formatResponse != null) {
885
+ return res.end(JSON.stringify(this._formatResponse({ message }, 500), null, 2));
886
+ }
887
+ return res.end(JSON.stringify({
888
+ message
889
+ }, null, 2));
890
+ };
891
+ response.status = (code) => {
892
+ res.writeHead(code, { 'Content-Type': 'application/json' });
893
+ return res;
894
+ };
895
+ response.setCookies = (cookies) => {
896
+ for (const [key, v] of Object.entries(cookies)) {
897
+ if (typeof v === 'string') {
898
+ res.setHeader('Set-Cookie', `${key}=${v}`);
899
+ continue;
900
+ }
901
+ if (v.value === '' || v.value == null)
902
+ continue;
903
+ let str = `${key}=${v.value}`;
904
+ if (v.sameSite != null) {
905
+ str += ` ;SameSite=${v.sameSite}`;
906
+ }
907
+ if (v.domain != null) {
908
+ str += ` ;Domain=${v.domain}`;
909
+ }
910
+ if (v.httpOnly != null) {
911
+ str += ` ;HttpOnly`;
912
+ }
913
+ if (v.secure != null) {
914
+ str += ` ;Secure`;
915
+ }
916
+ if (v.expires != null) {
917
+ str += ` ;Expires=${v.expires.toUTCString()}`;
918
+ }
919
+ res.setHeader('Set-Cookie', str);
920
+ }
921
+ };
922
+ return response;
980
923
  }
981
- _wrapHandlers = (...handlers) => {
924
+ _wrapHandlers(...handlers) {
982
925
  return (req, res, params) => {
983
926
  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));
927
+ try {
928
+ const response = this._customizeResponse(req, res);
929
+ const request = req;
930
+ request.params = params;
931
+ const body = request.body;
932
+ const files = request.files;
933
+ const cookies = request.cookies;
934
+ const headers = request.headers;
935
+ const url = new url_1.URL(req.url, "http://localhost");
936
+ const query = Object.fromEntries(url.searchParams);
937
+ const RecordOrEmptyRecord = (data) => {
938
+ if (data == null)
939
+ return {};
940
+ return Object.keys(data).length ? data : {};
941
+ };
942
+ const ctx = {
943
+ req: request,
944
+ res: response,
945
+ headers: RecordOrEmptyRecord(headers),
946
+ params: RecordOrEmptyRecord(params),
947
+ query: RecordOrEmptyRecord(query),
948
+ body: RecordOrEmptyRecord(body),
949
+ files: RecordOrEmptyRecord(files),
950
+ cookies: RecordOrEmptyRecord(cookies)
951
+ };
952
+ if (index === handlers.length - 1) {
953
+ return this._wrapResponse(handlers[index]
954
+ .bind(handlers[index]))(ctx, this._nextFunction(ctx));
955
+ }
956
+ return handlers[index](ctx, () => {
957
+ return nextHandler(index + 1);
958
+ });
959
+ }
960
+ catch (err) {
961
+ const ctx = {
962
+ req,
963
+ res: this._customizeResponse(req, res),
964
+ params: Object.keys(params).length ? params : {},
965
+ headers: {},
966
+ query: {},
967
+ body: {},
968
+ files: {},
969
+ cookies: {}
970
+ };
971
+ return this._nextFunction(ctx)(err);
1011
972
  }
1012
- return handlers[index](ctx, () => {
1013
- return nextHandler(index + 1);
1014
- });
1015
973
  };
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
- }
974
+ if (res.writableEnded)
975
+ return;
976
+ return nextHandler();
1034
977
  };
1035
- };
978
+ }
1036
979
  _wrapResponse(handler) {
1037
980
  return (ctx, next) => {
1038
981
  Promise.resolve(handler(ctx, next))
1039
982
  .then(result => {
1040
983
  if (ctx.res.writableEnded)
1041
984
  return;
1042
- if (result instanceof http_1.ServerResponse)
985
+ if (result instanceof http_1.ServerResponse) {
986
+ if (result?.end) {
987
+ result.end();
988
+ }
1043
989
  return;
1044
- if (result == null)
990
+ }
991
+ if (result == null) {
992
+ if (!ctx.res.headersSent) {
993
+ ctx.res.writeHead(204, { 'Content-Type': 'text/plain' });
994
+ }
995
+ ctx.res.end();
1045
996
  return;
997
+ }
1046
998
  if (typeof result === 'string') {
1047
999
  if (!ctx.res.headersSent) {
1048
1000
  ctx.res.writeHead(200, { 'Content-Type': 'text/plain' });
1049
1001
  }
1050
- ctx.res.end(result);
1002
+ ctx.res.end(result ?? '');
1051
1003
  return;
1052
1004
  }
1053
1005
  if (this._formatResponse != null) {
@@ -1062,26 +1014,58 @@ class Spear {
1062
1014
  if (!ctx.res.headersSent) {
1063
1015
  ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
1064
1016
  }
1065
- ctx.res.end(JSON.stringify(formattedResult, null, 2));
1017
+ ctx.res.end(JSON.stringify(formattedResult));
1066
1018
  return;
1067
1019
  }
1068
1020
  if (!ctx.res.headersSent) {
1069
1021
  ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
1070
1022
  }
1071
- ctx.res.end(result == null ? undefined : JSON.stringify(result, null, 2));
1023
+ ctx.res.end(JSON.stringify(result));
1072
1024
  return;
1073
1025
  })
1074
1026
  .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;
1027
+ return this._nextFunction(ctx)(err);
1082
1028
  });
1083
1029
  };
1084
1030
  }
1031
+ _nextFunction(ctx) {
1032
+ const NEXT_MESSAGE = "The 'next' function does not have any subsequent function.";
1033
+ return (err) => {
1034
+ if (err != null) {
1035
+ if (this._errorHandler != null) {
1036
+ const callback = this._errorHandler(err, ctx);
1037
+ if (callback == null || !(callback instanceof http_1.ServerResponse)) {
1038
+ ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1039
+ return ctx.res.end(JSON.stringify({
1040
+ message: err?.message
1041
+ }, null, 2));
1042
+ }
1043
+ return callback;
1044
+ }
1045
+ ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1046
+ if (this._formatResponse != null) {
1047
+ return ctx.res.end(JSON.stringify(this._formatResponse({
1048
+ message: err?.message,
1049
+ }, ctx.res.statusCode), null, 2));
1050
+ }
1051
+ return ctx.res.end(JSON.stringify({
1052
+ message: err?.message
1053
+ }, null, 2));
1054
+ }
1055
+ if (this._errorHandler != null) {
1056
+ return this._errorHandler(new Error(NEXT_MESSAGE), ctx);
1057
+ }
1058
+ ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1059
+ if (this._formatResponse != null) {
1060
+ return ctx.res.end(JSON.stringify(this._formatResponse({
1061
+ message: NEXT_MESSAGE
1062
+ }, ctx.res.statusCode), null, 2));
1063
+ }
1064
+ return ctx.res.end(JSON.stringify({
1065
+ message: NEXT_MESSAGE
1066
+ }, null, 2));
1067
+ };
1068
+ }
1085
1069
  async _createServer() {
1086
1070
  await this._registerMiddlewares();
1087
1071
  await this._registerControllers();
@@ -1090,10 +1074,10 @@ class Spear {
1090
1074
  const server = http_1.default.createServer({ maxHeaderSize: 1024 * 1024 }, cors
1091
1075
  ? (req, res) => {
1092
1076
  cors(req, res);
1093
- lookup(req, res);
1077
+ return lookup(req, res);
1094
1078
  }
1095
1079
  : (req, res) => {
1096
- lookup(req, res);
1080
+ return lookup(req, res);
1097
1081
  });
1098
1082
  if (this._ws) {
1099
1083
  this._wss = new ws_1.default.Server({ server, ...this._wsOptions });
@@ -1133,7 +1117,7 @@ class Spear {
1133
1117
  .routes.filter(r => ["GET", "POST", "PUT", "PATCH", "DELETE"].includes(r.method));
1134
1118
  const { path, html, staticSwaggerHandler, staticUrl } = this._parser.swagger({
1135
1119
  ...this._swagger,
1136
- specs: this._swaggerAdditional,
1120
+ specs: this._swaggerSpecs,
1137
1121
  routes
1138
1122
  });
1139
1123
  this._router.get(staticUrl, staticSwaggerHandler);