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.
- package/README.md +342 -200
- package/dist/cli/generators/app/index.js +1 -1
- package/dist/cli/generators/app/index.js.map +1 -1
- package/dist/cli/generators/controller/template.d.ts +1 -1
- package/dist/cli/generators/controller/template.js +11 -9
- package/dist/cli/generators/controller/template.js.map +1 -1
- package/dist/cli/index.js +10 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/lib/core/client/index.js +18 -6
- package/dist/lib/core/client/index.js.map +1 -1
- package/dist/lib/core/client/types.d.ts +4 -3
- package/dist/lib/core/compiler/generator.js +8 -4
- package/dist/lib/core/compiler/generator.js.map +1 -1
- package/dist/lib/core/compiler/pre-routes.js +10 -2
- package/dist/lib/core/compiler/pre-routes.js.map +1 -1
- package/dist/lib/core/const/index.d.ts +12 -0
- package/dist/lib/core/const/index.js +6 -1
- package/dist/lib/core/const/index.js.map +1 -1
- package/dist/lib/core/decorators/controller.js +2 -1
- package/dist/lib/core/decorators/controller.js.map +1 -1
- package/dist/lib/core/decorators/headers.js +1 -0
- package/dist/lib/core/decorators/headers.js.map +1 -1
- package/dist/lib/core/decorators/index.d.ts +1 -0
- package/dist/lib/core/decorators/index.js +1 -0
- package/dist/lib/core/decorators/index.js.map +1 -1
- package/dist/lib/core/decorators/methods.js +4 -3
- package/dist/lib/core/decorators/methods.js.map +1 -1
- package/dist/lib/core/decorators/middleware.d.ts +46 -2
- package/dist/lib/core/decorators/middleware.js +100 -15
- package/dist/lib/core/decorators/middleware.js.map +1 -1
- package/dist/lib/core/decorators/service.d.ts +28 -0
- package/dist/lib/core/decorators/service.js +36 -0
- package/dist/lib/core/decorators/service.js.map +1 -0
- package/dist/lib/core/decorators/statusCode.js.map +1 -1
- package/dist/lib/core/decorators/swagger.js +4 -3
- package/dist/lib/core/decorators/swagger.js.map +1 -1
- package/dist/lib/core/exception/index.d.ts +68 -0
- package/dist/lib/core/exception/index.js +123 -0
- package/dist/lib/core/exception/index.js.map +1 -0
- package/dist/lib/core/metadata/index.d.ts +6 -0
- package/dist/lib/core/metadata/index.js +13 -0
- package/dist/lib/core/metadata/index.js.map +1 -0
- package/dist/lib/core/server/fast-router.d.ts +5 -5
- package/dist/lib/core/server/fast-router.js +2 -2
- package/dist/lib/core/server/fast-router.js.map +1 -1
- package/dist/lib/core/server/http/index.d.ts +22 -0
- package/dist/lib/core/server/http/index.js +220 -0
- package/dist/lib/core/server/http/index.js.map +1 -0
- package/dist/lib/core/server/index.d.ts +35 -7
- package/dist/lib/core/server/index.js +252 -75
- package/dist/lib/core/server/index.js.map +1 -1
- package/dist/lib/core/server/net/index.d.ts +13 -8
- package/dist/lib/core/server/net/index.js +2 -2
- package/dist/lib/core/server/net/index.js.map +1 -1
- package/dist/lib/core/server/parser-factory.d.ts +3 -4
- package/dist/lib/core/server/parser-factory.js +52 -143
- package/dist/lib/core/server/parser-factory.js.map +1 -1
- package/dist/lib/core/server/response.d.ts +1 -2
- package/dist/lib/core/server/response.js +173 -171
- package/dist/lib/core/server/response.js.map +1 -1
- package/dist/lib/core/server/uWS/index.d.ts +2 -3
- package/dist/lib/core/server/uWS/index.js +15 -10
- package/dist/lib/core/server/uWS/index.js.map +1 -1
- package/dist/lib/core/types/index.d.ts +25 -8
- package/dist/lib/core/utils/index.d.ts +3 -3
- package/dist/lib/core/utils/index.js +19 -16
- package/dist/lib/core/utils/index.js.map +1 -1
- 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.
|
|
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
|
-
*
|
|
194
|
-
*
|
|
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
|
-
* @
|
|
197
|
-
*
|
|
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(
|
|
200
|
-
this._globalPrefix =
|
|
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 '
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
721
|
-
const prefixPath = Reflect.getMetadata(
|
|
722
|
-
const routers = Reflect.getMetadata(
|
|
723
|
-
const swaggers = Reflect.getMetadata(
|
|
724
|
-
|
|
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(
|
|
777
|
+
path: this._normalizePath(globalPrefix, prefixPath, path),
|
|
734
778
|
method
|
|
735
779
|
}
|
|
736
780
|
];
|
|
737
781
|
}
|
|
738
|
-
this[method](this._normalizePath(
|
|
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 =
|
|
745
|
-
const prefixPath = Reflect.getMetadata(
|
|
746
|
-
const routers = Reflect.getMetadata(
|
|
747
|
-
const swaggers = Reflect.getMetadata(
|
|
748
|
-
|
|
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(
|
|
804
|
+
path: this._normalizePath(globalPrefix, prefixPath, path),
|
|
758
805
|
method
|
|
759
806
|
}
|
|
760
807
|
];
|
|
761
808
|
}
|
|
762
|
-
this[method](this._normalizePath(
|
|
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.
|
|
975
|
+
ctx.res.end(result);
|
|
857
976
|
return;
|
|
858
977
|
}
|
|
859
978
|
ctx.res.json(result);
|
|
860
979
|
return;
|
|
861
980
|
})
|
|
862
|
-
.catch(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(
|
|
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
|
-
},
|
|
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
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
this._ws
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
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
|
-
|
|
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
|
|
1061
|
-
params: params
|
|
1193
|
+
headers: headers ?? EMPTY,
|
|
1194
|
+
params: params ?? EMPTY,
|
|
1062
1195
|
query,
|
|
1063
|
-
body: body
|
|
1064
|
-
files: files
|
|
1065
|
-
cookies: cookies
|
|
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
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
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 {
|