tspace-spear 1.2.9 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +342 -200
  2. package/dist/cli/generators/app/index.js +1 -1
  3. package/dist/cli/generators/app/index.js.map +1 -1
  4. package/dist/cli/generators/controller/template.d.ts +1 -1
  5. package/dist/cli/generators/controller/template.js +11 -9
  6. package/dist/cli/generators/controller/template.js.map +1 -1
  7. package/dist/cli/index.js +10 -8
  8. package/dist/cli/index.js.map +1 -1
  9. package/dist/lib/core/client/index.js +18 -6
  10. package/dist/lib/core/client/index.js.map +1 -1
  11. package/dist/lib/core/client/types.d.ts +4 -3
  12. package/dist/lib/core/compiler/generator.js +8 -4
  13. package/dist/lib/core/compiler/generator.js.map +1 -1
  14. package/dist/lib/core/compiler/pre-routes.js +10 -2
  15. package/dist/lib/core/compiler/pre-routes.js.map +1 -1
  16. package/dist/lib/core/const/index.d.ts +12 -0
  17. package/dist/lib/core/const/index.js +6 -1
  18. package/dist/lib/core/const/index.js.map +1 -1
  19. package/dist/lib/core/decorators/controller.js +2 -1
  20. package/dist/lib/core/decorators/controller.js.map +1 -1
  21. package/dist/lib/core/decorators/headers.js +1 -0
  22. package/dist/lib/core/decorators/headers.js.map +1 -1
  23. package/dist/lib/core/decorators/index.d.ts +1 -0
  24. package/dist/lib/core/decorators/index.js +1 -0
  25. package/dist/lib/core/decorators/index.js.map +1 -1
  26. package/dist/lib/core/decorators/methods.js +4 -3
  27. package/dist/lib/core/decorators/methods.js.map +1 -1
  28. package/dist/lib/core/decorators/middleware.d.ts +46 -2
  29. package/dist/lib/core/decorators/middleware.js +100 -15
  30. package/dist/lib/core/decorators/middleware.js.map +1 -1
  31. package/dist/lib/core/decorators/service.d.ts +28 -0
  32. package/dist/lib/core/decorators/service.js +36 -0
  33. package/dist/lib/core/decorators/service.js.map +1 -0
  34. package/dist/lib/core/decorators/statusCode.js.map +1 -1
  35. package/dist/lib/core/decorators/swagger.js +4 -3
  36. package/dist/lib/core/decorators/swagger.js.map +1 -1
  37. package/dist/lib/core/exception/index.d.ts +68 -0
  38. package/dist/lib/core/exception/index.js +123 -0
  39. package/dist/lib/core/exception/index.js.map +1 -0
  40. package/dist/lib/core/metadata/index.d.ts +6 -0
  41. package/dist/lib/core/metadata/index.js +13 -0
  42. package/dist/lib/core/metadata/index.js.map +1 -0
  43. package/dist/lib/core/server/fast-router.d.ts +5 -5
  44. package/dist/lib/core/server/fast-router.js +2 -2
  45. package/dist/lib/core/server/fast-router.js.map +1 -1
  46. package/dist/lib/core/server/http/index.d.ts +22 -0
  47. package/dist/lib/core/server/http/index.js +220 -0
  48. package/dist/lib/core/server/http/index.js.map +1 -0
  49. package/dist/lib/core/server/index.d.ts +35 -7
  50. package/dist/lib/core/server/index.js +252 -75
  51. package/dist/lib/core/server/index.js.map +1 -1
  52. package/dist/lib/core/server/net/index.d.ts +13 -8
  53. package/dist/lib/core/server/net/index.js +2 -2
  54. package/dist/lib/core/server/net/index.js.map +1 -1
  55. package/dist/lib/core/server/parser-factory.d.ts +3 -4
  56. package/dist/lib/core/server/parser-factory.js +52 -143
  57. package/dist/lib/core/server/parser-factory.js.map +1 -1
  58. package/dist/lib/core/server/response.d.ts +1 -2
  59. package/dist/lib/core/server/response.js +173 -171
  60. package/dist/lib/core/server/response.js.map +1 -1
  61. package/dist/lib/core/server/uWS/index.d.ts +2 -3
  62. package/dist/lib/core/server/uWS/index.js +15 -10
  63. package/dist/lib/core/server/uWS/index.js.map +1 -1
  64. package/dist/lib/core/types/index.d.ts +25 -8
  65. package/dist/lib/core/utils/index.d.ts +3 -3
  66. package/dist/lib/core/utils/index.js +19 -16
  67. package/dist/lib/core/utils/index.js.map +1 -1
  68. package/package.json +35 -12
@@ -43,6 +43,10 @@ const response_1 = require("./response");
43
43
  const compiler_1 = require("../compiler");
44
44
  const uWS_1 = require("./uWS");
45
45
  const net_2 = require("./net");
46
+ const http_2 = require("./http");
47
+ const metadata_1 = require("../metadata");
48
+ const EMPTY = Object.freeze(Object.create(null));
49
+ const EMPTY_ARRAY = Object.freeze([]);
46
50
  /**
47
51
  *
48
52
  * The 'Spear' class is used to create a server and handle HTTP requests.
@@ -64,7 +68,12 @@ class Spear {
64
68
  _middlewares;
65
69
  _router = new fast_router_1.FastRouter();
66
70
  _parser = new parser_factory_1.ParserFactory();
67
- _globalPrefix = '';
71
+ _globalPrefix = {
72
+ path: '',
73
+ options: {
74
+ exclude: []
75
+ }
76
+ };
68
77
  _adapter = { kind: 'http', server: http_1.default };
69
78
  _cluster;
70
79
  _cors;
@@ -110,7 +119,7 @@ class Spear {
110
119
  if (cluster)
111
120
  this.useCluster(cluster);
112
121
  if (adapter)
113
- this.useAdater(adapter);
122
+ this.useAdapter(adapter);
114
123
  if (globalPrefix)
115
124
  this.useGlobalPrefix(globalPrefix);
116
125
  // Ensure controllers is NOT an array and has the required shape
@@ -190,24 +199,55 @@ class Spear {
190
199
  /**
191
200
  * The 'useGlobalPrefix' method is used to sets a global prefix for all routes in the router.
192
201
  *
193
- * If `globalPrefix` is `null` or `undefined`, it will default to an empty string,
194
- * meaning no prefix will be applied.
202
+ * This prefix will be prepended to every route path in the application.
203
+ * If `path` is `null`, `undefined`, or an empty string, no global prefix
204
+ * will be applied.
205
+ *
206
+ * @param {string} path - The global route prefix.
207
+ *
208
+ * @param {Object} [options] - Additional configuration options.
209
+ * @param {Array<{ path: string; method?: TMethod | TMethod[] }>} [options.exclude]
210
+ * Routes to exclude from the global prefix.
211
+ *
212
+ * @returns {this} Returns the current router instance for chaining.
195
213
  *
196
- * @param {string | null} globalPrefix - The base path prefix to apply to all routes.
197
- * @returns {this} Returns the current instance for method chaining.
214
+ * @example
215
+ * import Spear from "tspace-spear";
216
+ * const app = new Spear()
217
+ *
218
+ * app.useGlobalPrefix('/api');
219
+ *
220
+ * @example
221
+ * app.useGlobalPrefix('/api', {
222
+ * exclude: [
223
+ * { path: '/health' },
224
+ * { path: '/auth/login', method: 'POST' }
225
+ * ]
226
+ * });
198
227
  */
199
- useGlobalPrefix(globalPrefix) {
200
- this._globalPrefix = globalPrefix == null ? '' : globalPrefix.replace(/^\/+|\/+$/g, '');
228
+ useGlobalPrefix(path, options) {
229
+ this._globalPrefix.path = path == null
230
+ ? ''
231
+ : path.replace(/^\/+|\/+$/g, '');
232
+ this._globalPrefix.options.exclude = (options?.exclude ?? []).map(route => {
233
+ const method = route.methods == null || route.methods === '*'
234
+ ? '*'
235
+ : route.methods.map(m => m.toUpperCase());
236
+ return {
237
+ path: route.path.replace(/^\/+|\/+$/g, ''),
238
+ method
239
+ };
240
+ });
201
241
  return this;
202
242
  }
203
243
  /**
204
- * The 'useAdater' method is used to switch between different server implementations,
244
+ * The 'useAdapter' method is used to switch between different server implementations,
205
245
  * such as the native Node.js HTTP server or uWebSockets.js (uWS).
206
246
  *
207
247
  * @param {T.AdapterServer} adapter - The adapter instance (e.g., HTTP or uWS).
208
248
  * @returns {this} Returns the current instance for chaining
209
249
  */
210
- useAdater(adapter) {
250
+ useAdapter(adapter) {
211
251
  if (adapter === http_1.default) {
212
252
  this._adapter = { kind: 'http', server: adapter };
213
253
  }
@@ -218,7 +258,7 @@ class Spear {
218
258
  //@ts-ignore
219
259
  this._adapter = { kind: 'uWS', server: adapter };
220
260
  }
221
- this._parser.useAdater(this._adapter);
261
+ this._parser.useAdapter(this._adapter);
222
262
  return this;
223
263
  }
224
264
  /**
@@ -267,6 +307,7 @@ class Spear {
267
307
  return next();
268
308
  }
269
309
  const startTime = process.hrtime();
310
+ //@ts-ignore
270
311
  (0, on_finished_1.default)(res, () => {
271
312
  console.log([
272
313
  `[\x1b[1m\x1b[34mINFO\x1b[0m]`,
@@ -382,7 +423,7 @@ class Spear {
382
423
  useRouter(router) {
383
424
  const routes = router.routes;
384
425
  for (const { path, method, handlers } of routes) {
385
- this[method](this._normalizePath(this._globalPrefix, path), ...handlers);
426
+ this[method](this._normalizePath(this._resolveGlobalPrefix({ path, method }), path), ...handlers);
386
427
  }
387
428
  return this;
388
429
  }
@@ -417,7 +458,7 @@ class Spear {
417
458
  }
418
459
  const server = await this._createServer();
419
460
  if (this._generatePreRouteTypes) {
420
- await new compiler_1.Compiler().generateRoutes(this._globalPrefix, this._generatePreRouteTypes);
461
+ await new compiler_1.Compiler().generateRoutes(this._globalPrefix.path, this._generatePreRouteTypes);
421
462
  }
422
463
  if (this._cluster != null &&
423
464
  this._cluster || typeof this._cluster === 'number') {
@@ -542,7 +583,7 @@ class Spear {
542
583
  */
543
584
  get(path, ...handlers) {
544
585
  this._onListeners.push(() => {
545
- return this._router.get(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
586
+ return this._router.get(this._normalizePath(this._resolveGlobalPrefix({ path, method: 'get' }), path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
546
587
  });
547
588
  return this;
548
589
  }
@@ -557,7 +598,7 @@ class Spear {
557
598
  */
558
599
  post(path, ...handlers) {
559
600
  this._onListeners.push(() => {
560
- return this._router.post(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
601
+ return this._router.post(this._normalizePath(this._resolveGlobalPrefix({ path, method: 'post' }), path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
561
602
  });
562
603
  return this;
563
604
  }
@@ -572,7 +613,7 @@ class Spear {
572
613
  */
573
614
  put(path, ...handlers) {
574
615
  this._onListeners.push(() => {
575
- return this._router.put(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
616
+ return this._router.put(this._normalizePath(this._resolveGlobalPrefix({ path, method: 'put' }), path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
576
617
  });
577
618
  return this;
578
619
  }
@@ -587,7 +628,7 @@ class Spear {
587
628
  */
588
629
  patch(path, ...handlers) {
589
630
  this._onListeners.push(() => {
590
- return this._router.patch(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
631
+ return this._router.patch(this._normalizePath(this._resolveGlobalPrefix({ path, method: 'patch' }), path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
591
632
  });
592
633
  return this;
593
634
  }
@@ -602,7 +643,7 @@ class Spear {
602
643
  */
603
644
  delete(path, ...handlers) {
604
645
  this._onListeners.push(() => {
605
- return this._router.delete(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
646
+ return this._router.delete(this._normalizePath(this._resolveGlobalPrefix({ path, method: 'delete' }), path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
606
647
  });
607
648
  return this;
608
649
  }
@@ -617,7 +658,7 @@ class Spear {
617
658
  */
618
659
  head(path, ...handlers) {
619
660
  this._onListeners.push(() => {
620
- return this._router.head(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
661
+ return this._router.head(this._normalizePath(this._resolveGlobalPrefix({ path, method: 'head' }), path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
621
662
  });
622
663
  return this;
623
664
  }
@@ -632,7 +673,7 @@ class Spear {
632
673
  */
633
674
  options(path, ...handlers) {
634
675
  this._onListeners.push(() => {
635
- return this._router.options(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
676
+ return this._router.options(this._normalizePath(this._resolveGlobalPrefix({ path, method: 'options' }), path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
636
677
  });
637
678
  return this;
638
679
  }
@@ -647,7 +688,7 @@ class Spear {
647
688
  */
648
689
  all(path, ...handlers) {
649
690
  this._onListeners.push(() => {
650
- return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
691
+ return this._router.all(this._normalizePath(this._resolveGlobalPrefix({ path, method: 'all' }), path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
651
692
  });
652
693
  return this;
653
694
  }
@@ -717,52 +758,130 @@ class Spear {
717
758
  console.log(`\x1b[31m[ControllerLoader ERROR]\x1b[0m \x1b[36m${file}\x1b[0m must export a controller class`);
718
759
  continue;
719
760
  }
720
- const controllerInstance = new controller();
721
- const prefixPath = Reflect.getMetadata("controllers", controller) ?? '';
722
- const routers = Reflect.getMetadata("routers", controller) ?? [];
723
- const swaggers = Reflect.getMetadata("swaggers", controller) ?? [];
724
- if (prefixPath == null)
725
- continue;
761
+ const controllerInstance = this._createController(controller);
762
+ const prefixPath = Reflect.getMetadata(metadata_1.CONTROLLER_METADATA, controller) ?? '';
763
+ const routers = Reflect.getMetadata(metadata_1.ROUTE_METADATA, controller) ?? [];
764
+ const swaggers = Reflect.getMetadata(metadata_1.SWAGGER_METADATA, controller) ?? [];
765
+ const middlewares = Reflect.getMetadata(metadata_1.MIDDLEWARE_METADATA, controller) ?? [];
726
766
  for (const { method, path, handler } of Array.from(routers)) {
727
767
  const find = Array.from(swaggers).find(s => s.handler === handler);
728
768
  if (find != null) {
769
+ const globalPrefix = this._resolveGlobalPrefix({
770
+ path: this._normalizePath(prefixPath, path),
771
+ method
772
+ });
729
773
  this._swaggerSpecs = [
730
774
  ...this._swaggerSpecs,
731
775
  {
732
776
  ...find,
733
- path: this._normalizePath(this._globalPrefix, prefixPath, path),
777
+ path: this._normalizePath(globalPrefix, prefixPath, path),
734
778
  method
735
779
  }
736
780
  ];
737
781
  }
738
- this[method](this._normalizePath(this._globalPrefix, prefixPath, path), controllerInstance[String(handler)].bind(controllerInstance));
782
+ this[method](this._normalizePath(prefixPath, path), ...this._normalizeMiddlewares(middlewares), controllerInstance[String(handler)].bind(controllerInstance));
739
783
  }
740
784
  }
741
785
  return;
742
786
  }
743
787
  for (const controller of this._controllers) {
744
- const controllerInstance = new controller();
745
- const prefixPath = Reflect.getMetadata("controllers", controller) ?? '';
746
- const routers = Reflect.getMetadata("routers", controller) ?? [];
747
- const swaggers = Reflect.getMetadata("swaggers", controller) ?? [];
748
- if (prefixPath == null)
749
- continue;
788
+ const controllerInstance = this._createController(controller);
789
+ const prefixPath = Reflect.getMetadata(metadata_1.CONTROLLER_METADATA, controller) ?? '';
790
+ const routers = Reflect.getMetadata(metadata_1.ROUTE_METADATA, controller) ?? [];
791
+ const swaggers = Reflect.getMetadata(metadata_1.SWAGGER_METADATA, controller) ?? [];
792
+ const middlewares = Reflect.getMetadata(metadata_1.MIDDLEWARE_METADATA, controller) ?? [];
750
793
  for (const { method, path, handler } of Array.from(routers)) {
751
794
  const find = Array.from(swaggers).find(s => s.handler === handler);
752
795
  if (find != null) {
796
+ const globalPrefix = this._resolveGlobalPrefix({
797
+ path: this._normalizePath(prefixPath, path),
798
+ method
799
+ });
753
800
  this._swaggerSpecs = [
754
801
  ...this._swaggerSpecs,
755
802
  {
756
803
  ...find,
757
- path: this._normalizePath(this._globalPrefix, prefixPath, path),
804
+ path: this._normalizePath(globalPrefix, prefixPath, path),
758
805
  method
759
806
  }
760
807
  ];
761
808
  }
762
- this[method](this._normalizePath(this._globalPrefix, prefixPath, path), controllerInstance[String(handler)].bind(controllerInstance));
809
+ this[method](this._normalizePath(prefixPath, path), ...this._normalizeMiddlewares(middlewares), controllerInstance[String(handler)].bind(controllerInstance));
763
810
  }
764
811
  }
765
812
  }
813
+ _createController(ControllerClass) {
814
+ const services = Reflect.getMetadata(metadata_1.SERVICE_METADATA, ControllerClass) ?? [];
815
+ const constructorTypes = Reflect.getMetadata(metadata_1.PARAMTYPES_METADATA, ControllerClass) ?? [];
816
+ if (!constructorTypes.length) {
817
+ return new ControllerClass();
818
+ }
819
+ if (!services.length) {
820
+ throw new Error(`\x1b[31m[ServiceLoader ERROR]\x1b[0m \x1b[36m${ControllerClass.name}\x1b[0m requires dependencies but no @Service() decorator was found`);
821
+ }
822
+ const serviceMap = new Map();
823
+ for (const ServiceClass of services) {
824
+ if (typeof ServiceClass !== 'function') {
825
+ throw new Error(`\x1b[31m[ServiceLoader ERROR]\x1b[0m Invalid service in @Service() of ${ControllerClass.name}`);
826
+ }
827
+ serviceMap.set(ServiceClass, new ServiceClass());
828
+ }
829
+ const injections = [];
830
+ const available = services.length
831
+ ? services
832
+ .map((s) => s.name)
833
+ : 'None';
834
+ for (const DependencyClass of constructorTypes) {
835
+ const service = serviceMap.get(DependencyClass);
836
+ if (!service) {
837
+ throw new Error([
838
+ '\x1b[31m[ServiceLoader ERROR]\x1b[0m',
839
+ '',
840
+ `\x1b[36mController\x1b[0m : ${ControllerClass.name}`,
841
+ `\x1b[36mDependency\x1b[0m : ${DependencyClass.name}`,
842
+ `\x1b[36mAvailable \x1b[0m : ${available.join(', ')}`,
843
+ '',
844
+ '\x1b[33mHint\x1b[0m',
845
+ '@Service([',
846
+ ` ${available.join(',\n ')},`,
847
+ ` ${DependencyClass.name}`,
848
+ '])',
849
+ '',
850
+ `Register '${DependencyClass.name}' in @Service()`
851
+ ].join('\n'));
852
+ }
853
+ injections.push(service);
854
+ }
855
+ return new ControllerClass(...injections);
856
+ }
857
+ _normalizeMiddlewares = (mids) => {
858
+ const result = [];
859
+ const visit = (item) => {
860
+ if (Array.isArray(item)) {
861
+ item.forEach(visit);
862
+ return;
863
+ }
864
+ if (!item)
865
+ return;
866
+ if (typeof item === "function") {
867
+ const proto = item.prototype;
868
+ if (proto && proto !== Object.prototype) {
869
+ const instance = new item();
870
+ Object.getOwnPropertyNames(proto)
871
+ .filter(name => name !== "constructor")
872
+ .forEach(name => {
873
+ if (typeof instance[name] === "function") {
874
+ result.push(instance[name].bind(instance));
875
+ }
876
+ });
877
+ return;
878
+ }
879
+ result.push(item);
880
+ }
881
+ };
882
+ mids.forEach(visit);
883
+ return result;
884
+ };
766
885
  async _registerMiddlewares() {
767
886
  if (this._middlewares == null)
768
887
  return;
@@ -853,13 +972,15 @@ class Spear {
853
972
  return;
854
973
  }
855
974
  if (typeof result === 'string') {
856
- ctx.res.send(result);
975
+ ctx.res.end(result);
857
976
  return;
858
977
  }
859
978
  ctx.res.json(result);
860
979
  return;
861
980
  })
862
- .catch(err => next(err));
981
+ .catch(err => {
982
+ return next(err);
983
+ });
863
984
  };
864
985
  }
865
986
  _nextError(ctx) {
@@ -867,20 +988,28 @@ class Spear {
867
988
  return (err) => {
868
989
  if (ctx.res.writableEnded)
869
990
  return;
991
+ let statusCode = typeof err?.statusCode === 'number' &&
992
+ Number.isFinite(err.statusCode)
993
+ ? err.statusCode
994
+ : 500;
870
995
  const errorMessage = err?.message || NEXT_MESSAGE;
871
996
  if (this._errorHandler != null) {
872
997
  return this._errorHandler(err, ctx);
873
998
  }
874
999
  if (!ctx.res.headersSent) {
875
- ctx.res.writeHead(500, const_1.HEADER_CONTENT_TYPES['json']);
1000
+ ctx.res.writeHead(statusCode, const_1.HEADER_CONTENT_TYPES['json']);
1001
+ }
1002
+ else {
1003
+ statusCode = ctx.res.statusCode;
876
1004
  }
877
1005
  if (this._formatResponse != null) {
878
1006
  ctx.res.end(JSON.stringify(this._formatResponse({
879
1007
  message: errorMessage
880
- }, ctx.res.statusCode)));
1008
+ }, statusCode)));
881
1009
  return;
882
1010
  }
883
1011
  ctx.res.end(JSON.stringify({
1012
+ statusCode: statusCode,
884
1013
  message: errorMessage
885
1014
  }));
886
1015
  return;
@@ -992,33 +1121,37 @@ class Spear {
992
1121
  }
993
1122
  return server;
994
1123
  }
995
- const server = http_1.default.createServer((req, res) => {
996
- if (cors)
997
- cors(req, res);
998
- return lookup(req, res);
999
- });
1000
- if (this._ws?.handler) {
1001
- this._ws.server = new ws_1.default.Server({ server, ...this._ws.options });
1002
- this._ws.server.on('connection', (ws) => {
1003
- if (this._ws.handler?.connection) {
1004
- this._ws.handler.connection(ws);
1005
- }
1006
- ws.on('message', (data) => {
1007
- this._ws.handler?.message?.(ws, data);
1008
- });
1009
- ws.on('close', (code, reason) => {
1010
- if (this._ws.handler?.close) {
1011
- this._ws.handler?.close(ws, code, reason);
1012
- }
1013
- });
1014
- ws.on('error', (err) => {
1015
- if (this._ws.handler?.error) {
1016
- this._ws.handler.error(ws, err);
1124
+ if (adapter.kind === 'http') {
1125
+ const server = http_1.default.createServer((httpReq, httpRes) => {
1126
+ const { req, res } = (0, http_2.httpAdaptRequestResponse)(httpReq, httpRes);
1127
+ if (cors)
1128
+ cors(req, res);
1129
+ return lookup(req, res);
1130
+ });
1131
+ if (this._ws?.handler) {
1132
+ this._ws.server = new ws_1.default.Server({ server, ...this._ws.options });
1133
+ this._ws.server.on('connection', (ws) => {
1134
+ if (this._ws.handler?.connection) {
1135
+ this._ws.handler.connection(ws);
1017
1136
  }
1137
+ ws.on('message', (data) => {
1138
+ this._ws.handler?.message?.(ws, data);
1139
+ });
1140
+ ws.on('close', (code, reason) => {
1141
+ if (this._ws.handler?.close) {
1142
+ this._ws.handler?.close(ws, code, reason);
1143
+ }
1144
+ });
1145
+ ws.on('error', (err) => {
1146
+ if (this._ws.handler?.error) {
1147
+ this._ws.handler.error(ws, err);
1148
+ }
1149
+ });
1018
1150
  });
1019
- });
1151
+ }
1152
+ return server;
1020
1153
  }
1021
- return server;
1154
+ throw new Error(`Unsupported adapter`);
1022
1155
  }
1023
1156
  _createContext({ req, res, ps }) {
1024
1157
  const request = req;
@@ -1047,7 +1180,7 @@ class Spear {
1047
1180
  }
1048
1181
  else {
1049
1182
  const addr = req.socket?.remoteAddress;
1050
- ips = addr ? [addr] : [];
1183
+ ips = addr ? [addr] : EMPTY_ARRAY;
1051
1184
  }
1052
1185
  const ip = (ips.length ? ips[0] : null);
1053
1186
  request.params = params;
@@ -1057,25 +1190,37 @@ class Spear {
1057
1190
  return {
1058
1191
  req: request,
1059
1192
  res: response,
1060
- headers: headers || Object.create(null),
1061
- params: params || Object.create(null),
1193
+ headers: headers ?? EMPTY,
1194
+ params: params ?? EMPTY,
1062
1195
  query,
1063
- body: body || Object.create(null),
1064
- files: files || Object.create(null),
1065
- cookies: cookies || Object.create(null),
1196
+ body: body ?? EMPTY,
1197
+ files: files ?? EMPTY,
1198
+ cookies: cookies ?? EMPTY,
1066
1199
  ip,
1067
1200
  ips
1068
1201
  };
1069
1202
  }
1070
1203
  _normalizePath(...paths) {
1071
1204
  const path = paths
1205
+ .filter(Boolean)
1072
1206
  .join('/')
1073
1207
  .replace(/\/+/g, '/')
1074
1208
  .replace(/\/+$/, '');
1075
- const normalizedPath = path.startsWith('/') ? path : `/${path}`;
1076
- return /\/api\/api/.test(normalizedPath)
1077
- ? normalizedPath.replace(/\/api\/api\//, "/api/")
1078
- : normalizedPath;
1209
+ let normalizedPath = path.startsWith('/')
1210
+ ? path
1211
+ : `/${path}`;
1212
+ const globalPrefix = this._globalPrefix.path
1213
+ .replace(/^\/+|\/+$/g, '');
1214
+ if (globalPrefix) {
1215
+ const duplicatedPrefix = `/${globalPrefix}/${globalPrefix}/`;
1216
+ normalizedPath =
1217
+ normalizedPath.replace(duplicatedPrefix, `/${globalPrefix}/`);
1218
+ if (normalizedPath ===
1219
+ `/${globalPrefix}/${globalPrefix}`) {
1220
+ normalizedPath = `/${globalPrefix}`;
1221
+ }
1222
+ }
1223
+ return normalizedPath || '/';
1079
1224
  }
1080
1225
  async _swaggerHandler() {
1081
1226
  const routes = this.routers
@@ -1102,6 +1247,38 @@ class Spear {
1102
1247
  });
1103
1248
  return;
1104
1249
  }
1250
+ _resolveGlobalPrefix({ path, method }) {
1251
+ const globalPrefix = this._globalPrefix.path;
1252
+ if (!globalPrefix) {
1253
+ return '';
1254
+ }
1255
+ if (path === '*') {
1256
+ return `/${globalPrefix}`;
1257
+ }
1258
+ const cleanPath = path.replace(/^\/+|\/+$/g, '');
1259
+ const upperMethod = method.toUpperCase();
1260
+ const exclude = this._globalPrefix.options.exclude;
1261
+ const isExcluded = exclude.some(route => {
1262
+ const methods = route.method ?? '*';
1263
+ if (methods !== '*' &&
1264
+ !methods.includes(upperMethod)) {
1265
+ return false;
1266
+ }
1267
+ const routePath = route.path.replace(/^\/+|\/+$/g, '');
1268
+ if (routePath === cleanPath) {
1269
+ return true;
1270
+ }
1271
+ if (routePath.endsWith('/*')) {
1272
+ const basePath = routePath.slice(0, -2);
1273
+ return (cleanPath === basePath ||
1274
+ cleanPath.startsWith(basePath + '/'));
1275
+ }
1276
+ return false;
1277
+ });
1278
+ return isExcluded
1279
+ ? ''
1280
+ : `/${globalPrefix}`;
1281
+ }
1105
1282
  }
1106
1283
  exports.Spear = Spear;
1107
1284
  class Application extends Spear {