tspace-spear 1.2.3 → 1.2.5-beta.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.
Files changed (63) hide show
  1. package/README.md +268 -19
  2. package/dist/lib/core/client/index.d.ts +23 -0
  3. package/dist/lib/core/client/index.js +45 -0
  4. package/dist/lib/core/client/index.js.map +1 -0
  5. package/dist/lib/core/compiler/generator.d.ts +18 -0
  6. package/dist/lib/core/compiler/generator.js +142 -0
  7. package/dist/lib/core/compiler/generator.js.map +1 -0
  8. package/dist/lib/core/compiler/index.d.ts +14 -0
  9. package/dist/lib/core/compiler/index.js +11 -0
  10. package/dist/lib/core/compiler/index.js.map +1 -0
  11. package/dist/lib/core/compiler/pre-routes.d.ts +3 -0
  12. package/dist/lib/core/compiler/pre-routes.js +5 -0
  13. package/dist/lib/core/compiler/pre-routes.js.map +1 -0
  14. package/dist/lib/core/compiler/types.d.ts +12 -0
  15. package/dist/lib/core/compiler/types.js +3 -0
  16. package/dist/lib/core/compiler/types.js.map +1 -0
  17. package/dist/lib/core/const/index.d.ts +153 -0
  18. package/dist/lib/core/const/index.js +105 -0
  19. package/dist/lib/core/const/index.js.map +1 -0
  20. package/dist/lib/core/decorators/context.d.ts +16 -9
  21. package/dist/lib/core/decorators/context.js +85 -59
  22. package/dist/lib/core/decorators/context.js.map +1 -1
  23. package/dist/lib/core/decorators/headers.d.ts +2 -2
  24. package/dist/lib/core/decorators/headers.js +1 -1
  25. package/dist/lib/core/decorators/headers.js.map +1 -1
  26. package/dist/lib/core/decorators/methods.d.ts +7 -7
  27. package/dist/lib/core/decorators/methods.js.map +1 -1
  28. package/dist/lib/core/decorators/middleware.d.ts +3 -3
  29. package/dist/lib/core/decorators/middleware.js +2 -2
  30. package/dist/lib/core/decorators/middleware.js.map +1 -1
  31. package/dist/lib/core/decorators/statusCode.d.ts +1 -1
  32. package/dist/lib/core/decorators/statusCode.js.map +1 -1
  33. package/dist/lib/core/decorators/swagger.d.ts +1 -1
  34. package/dist/lib/core/decorators/swagger.js.map +1 -1
  35. package/dist/lib/core/server/fast-router.d.ts +133 -0
  36. package/dist/lib/core/server/fast-router.js +277 -0
  37. package/dist/lib/core/server/fast-router.js.map +1 -0
  38. package/dist/lib/core/server/index.d.ts +39 -37
  39. package/dist/lib/core/server/index.js +201 -501
  40. package/dist/lib/core/server/index.js.map +1 -1
  41. package/dist/lib/core/server/net/index.d.ts +20 -0
  42. package/dist/lib/core/server/net/index.js +393 -0
  43. package/dist/lib/core/server/net/index.js.map +1 -0
  44. package/dist/lib/core/server/parser-factory.d.ts +10 -11
  45. package/dist/lib/core/server/parser-factory.js +259 -437
  46. package/dist/lib/core/server/parser-factory.js.map +1 -1
  47. package/dist/lib/core/server/response.d.ts +6 -0
  48. package/dist/lib/core/server/response.js +168 -0
  49. package/dist/lib/core/server/response.js.map +1 -0
  50. package/dist/lib/core/server/router.d.ts +2 -12
  51. package/dist/lib/core/server/router.js +2 -13
  52. package/dist/lib/core/server/router.js.map +1 -1
  53. package/dist/lib/core/server/uWS/index.d.ts +30 -0
  54. package/dist/lib/core/server/uWS/index.js +357 -0
  55. package/dist/lib/core/server/uWS/index.js.map +1 -0
  56. package/dist/lib/core/types/index.d.ts +150 -48
  57. package/dist/lib/core/utils/index.d.ts +12 -0
  58. package/dist/lib/core/utils/index.js +137 -0
  59. package/dist/lib/core/utils/index.js.map +1 -0
  60. package/dist/lib/index.d.ts +4 -3
  61. package/dist/lib/index.js +4 -2
  62. package/dist/lib/index.js.map +1 -1
  63. package/package.json +20 -14
@@ -38,14 +38,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.Spear = exports.Application = void 0;
40
40
  const http_1 = __importStar(require("http"));
41
- const find_my_way_1 = __importDefault(require("find-my-way"));
41
+ const const_1 = require("../const");
42
+ const stream_1 = require("stream");
42
43
  const cluster_1 = __importDefault(require("cluster"));
43
44
  const os_1 = __importDefault(require("os"));
44
45
  const fs_1 = __importDefault(require("fs"));
45
46
  const path_1 = __importDefault(require("path"));
46
47
  const on_finished_1 = __importDefault(require("on-finished"));
47
48
  const ws_1 = __importDefault(require("ws"));
49
+ const net_1 = __importDefault(require("net"));
48
50
  const parser_factory_1 = require("./parser-factory");
51
+ const fast_router_1 = require("./fast-router");
52
+ const response_1 = require("./response");
53
+ const compiler_1 = require("../compiler");
54
+ const uWS_1 = require("./uWS");
55
+ const net_2 = require("./net");
49
56
  /**
50
57
  *
51
58
  * The 'Spear' class is used to create a server and handle HTTP requests.
@@ -65,10 +72,10 @@ const parser_factory_1 = require("./parser-factory");
65
72
  class Spear {
66
73
  _controllers;
67
74
  _middlewares;
68
- _globalPrefix;
69
- _router = (0, find_my_way_1.default)();
75
+ _router = new fast_router_1.FastRouter();
70
76
  _parser = new parser_factory_1.ParserFactory();
71
- _adapter = http_1.default;
77
+ _globalPrefix = '';
78
+ _adapter = { kind: 'http', server: http_1.default };
72
79
  _cluster;
73
80
  _cors;
74
81
  _swagger = {
@@ -76,7 +83,7 @@ class Spear {
76
83
  path: '/api/docs',
77
84
  servers: [
78
85
  {
79
- url: 'http://localhost:3000'
86
+ url: '/'
80
87
  }
81
88
  ],
82
89
  tags: [],
@@ -87,9 +94,11 @@ class Spear {
87
94
  }
88
95
  };
89
96
  _swaggerSpecs = [];
90
- _wss;
91
- _ws;
92
- _wsOptions;
97
+ _ws = {
98
+ handler: null,
99
+ server: null,
100
+ options: null
101
+ };
93
102
  _errorHandler = null;
94
103
  _globalMiddlewares = [];
95
104
  _formatResponse = null;
@@ -102,16 +111,18 @@ class Spear {
102
111
  ms: 1000 * 60 * 10
103
112
  }
104
113
  };
114
+ _generateRoutes;
105
115
  constructor({ controllers, middlewares, globalPrefix, logger, cluster, adapter } = {}) {
116
+ this._controllers = controllers;
117
+ this._middlewares = middlewares;
106
118
  if (logger)
107
119
  this.useLogger();
108
120
  if (cluster)
109
121
  this.useCluster(cluster);
110
122
  if (adapter)
111
123
  this.useAdater(adapter);
112
- this._controllers = controllers;
113
- this._middlewares = middlewares;
114
- this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
124
+ if (globalPrefix)
125
+ this.useGlobalPrefix(globalPrefix);
115
126
  }
116
127
  /**
117
128
  * The get 'instance' method is used to get the instance of Spear.
@@ -124,11 +135,15 @@ class Spear {
124
135
  /**
125
136
  * The get 'routers' method is used get the all routers.
126
137
  *
127
- * @returns {Instance<findMyWayRouter.HTTPVersion.V1>}
138
+ * @returns {FastRouter}
128
139
  */
129
140
  get routers() {
130
141
  return this._router;
131
142
  }
143
+ useGenerateRouteType(options) {
144
+ this._generateRoutes = options;
145
+ return this;
146
+ }
132
147
  /**
133
148
  * The 'ws' method is used to creates the WebSocket server.
134
149
  *
@@ -137,8 +152,8 @@ class Spear {
137
152
  * @returns {this}
138
153
  */
139
154
  ws(handlers, options) {
140
- this._ws = handlers();
141
- this._wsOptions = options ?? {};
155
+ this._ws.handler = handlers();
156
+ this._ws.options = options ?? {};
142
157
  return this;
143
158
  }
144
159
  /**
@@ -153,16 +168,38 @@ class Spear {
153
168
  this._globalMiddlewares.push(middleware);
154
169
  return this;
155
170
  }
171
+ /**
172
+ * The 'useGlobalPrefix' method is used to sets a global prefix for all routes in the router.
173
+ *
174
+ * If `globalPrefix` is `null` or `undefined`, it will default to an empty string,
175
+ * meaning no prefix will be applied.
176
+ *
177
+ * @param {string | null} globalPrefix - The base path prefix to apply to all routes.
178
+ * @returns {this} Returns the current instance for method chaining.
179
+ */
180
+ useGlobalPrefix(globalPrefix) {
181
+ this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
182
+ return this;
183
+ }
156
184
  /**
157
185
  * The 'useAdater' method is used to switch between different server implementations,
158
186
  * such as the native Node.js HTTP server or uWebSockets.js (uWS).
159
187
  *
160
- * @param {T.Adapter} adapter - The adapter instance (e.g., HTTP or uWS).
188
+ * @param {T.AdapterServer} adapter - The adapter instance (e.g., HTTP or uWS).
161
189
  * @returns {this} Returns the current instance for chaining
162
190
  */
163
191
  useAdater(adapter) {
164
- this._adapter = adapter;
165
- this._parser.useAdater(adapter);
192
+ if (adapter === http_1.default) {
193
+ this._adapter = { kind: 'http', server: adapter };
194
+ }
195
+ else if (adapter === net_1.default) {
196
+ this._adapter = { kind: 'net', server: adapter };
197
+ }
198
+ else {
199
+ //@ts-ignore
200
+ this._adapter = { kind: 'uWS', server: adapter };
201
+ }
202
+ this._parser.useAdater(this._adapter);
166
203
  return this;
167
204
  }
168
205
  /**
@@ -201,13 +238,13 @@ class Spear {
201
238
  ? `\x1b[32m${statusCode}\x1b[0m`
202
239
  : `\x1b[31m${statusCode}\x1b[0m`;
203
240
  };
204
- if (exceptPath instanceof RegExp && exceptPath.test(String(req.url)))
241
+ if (exceptPath instanceof RegExp && exceptPath.test(req.url))
205
242
  return next();
206
- if (Array.isArray(exceptPath) && exceptPath.some(v => String(req.url) === v))
243
+ if (Array.isArray(exceptPath) && exceptPath.some(v => req.url === v))
207
244
  return next();
208
245
  if (methods != null &&
209
246
  methods.length &&
210
- !methods.some(v => v.toLocaleLowerCase() === String(req.method).toLocaleLowerCase())) {
247
+ !methods.some(v => v.toLowerCase() === req.method.toLowerCase())) {
211
248
  return next();
212
249
  }
213
250
  const startTime = process.hrtime();
@@ -216,7 +253,7 @@ class Spear {
216
253
  `[\x1b[1m\x1b[34mINFO\x1b[0m]`,
217
254
  `\x1b[34m${new Date().toJSON()}\x1b[0m`,
218
255
  `\x1b[33m${req.method}\x1b[0m`,
219
- `${decodeURIComponent(String(req.url))}`,
256
+ `${decodeURIComponent(req.url)}`,
220
257
  `${statusCode(res)}`,
221
258
  `${diffTime(startTime)}`,
222
259
  ].join(" "));
@@ -234,28 +271,25 @@ class Spear {
234
271
  useBodyParser({ except } = {}) {
235
272
  this._globalMiddlewares.push((ctx, next) => {
236
273
  const { req, res } = ctx;
237
- const contentType = req?.headers['content-type'] ?? '';
238
- const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
239
- const isCanParserBody = contentType.includes('application/json') ||
240
- contentType.includes('application/x-www-form-urlencoded');
241
- if (except != null &&
242
- Array.isArray(except) &&
243
- except.some(v => v.toLocaleLowerCase() === (req.method)?.toLocaleLowerCase())) {
274
+ if (Array.isArray(except) &&
275
+ except.some(v => v.toLowerCase() === (req.method).toLowerCase())) {
244
276
  return next();
245
277
  }
246
- if (isFileUpload)
278
+ const contentType = req?.headers['content-type'] ?? null;
279
+ if (contentType == null)
247
280
  return next();
248
- if (!isCanParserBody)
281
+ const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
282
+ if (isFileUpload)
249
283
  return next();
250
284
  if (req?.body != null)
251
285
  return next();
252
286
  Promise.resolve(this._parser.body(req, res))
253
- .then(r => {
254
- req.body = r;
287
+ .then(body => {
288
+ req.body = body;
255
289
  return next();
256
290
  })
257
291
  .catch(err => {
258
- return this._nextFunction(ctx)(err);
292
+ return this._nextError(ctx)(err);
259
293
  });
260
294
  });
261
295
  return this;
@@ -264,11 +298,11 @@ class Spear {
264
298
  * The 'useFileUpload' method is a middleware used to handler file uploads. It adds a file upload of incoming HTTP requests.
265
299
  *
266
300
  * @param {?Object}
267
- * @property {?number} limits
301
+ * @property {?number} limits // bytes. default Infinity
268
302
  * @property {?string} tempFileDir
269
303
  * @property {?Object} removeTempFile
270
304
  * @property {boolean} removeTempFile.remove
271
- * @property {number} removeTempFile.ms
305
+ * @property {number} removeTempFile.ms
272
306
  * @returns
273
307
  */
274
308
  useFileUpload({ limit, tempFileDir, removeTempFile } = {}) {
@@ -300,7 +334,7 @@ class Spear {
300
334
  return next();
301
335
  })
302
336
  .catch(err => {
303
- return this._nextFunction(ctx)(err);
337
+ return this._nextError(ctx)(err);
304
338
  });
305
339
  });
306
340
  return this;
@@ -363,6 +397,9 @@ class Spear {
363
397
  callback = hostname;
364
398
  }
365
399
  const server = await this._createServer();
400
+ if (this._generateRoutes) {
401
+ await new compiler_1.Compiler().generateRoutes(this._generateRoutes);
402
+ }
366
403
  if (this._cluster != null &&
367
404
  this._cluster || typeof this._cluster === 'number') {
368
405
  this._clusterMode({
@@ -373,7 +410,7 @@ class Spear {
373
410
  });
374
411
  return server;
375
412
  }
376
- if ('App' in this._adapter) {
413
+ if (this._adapter.kind === 'uWS') {
377
414
  const handler = () => {
378
415
  this._onListeners.forEach(listener => listener());
379
416
  if (this._swagger.use) {
@@ -469,21 +506,10 @@ class Spear {
469
506
  */
470
507
  notfound(fn) {
471
508
  const handler = ({ req, res }) => {
472
- return fn({
473
- req,
474
- res: this._customizeResponse(req, res),
475
- headers: {},
476
- query: {},
477
- files: {},
478
- body: {},
479
- params: {},
480
- cookies: {},
481
- ip: null
482
- });
509
+ const ctx = this._createContext({ req, res, ps: {} });
510
+ return fn(ctx);
483
511
  };
484
- this._onListeners.push(() => {
485
- return this.all('*', ...this._globalMiddlewares, handler);
486
- });
512
+ this.all('*', handler);
487
513
  return this;
488
514
  }
489
515
  /**
@@ -592,7 +618,7 @@ class Spear {
592
618
  return this;
593
619
  }
594
620
  /**
595
- * The 'any' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
621
+ * The 'all' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
596
622
  *
597
623
  * @param {string} path
598
624
  * @callback {...Function[]} handlers of the middlewares
@@ -600,21 +626,6 @@ class Spear {
600
626
  * @property {function} next - go to next function
601
627
  * @returns {this}
602
628
  */
603
- any(path, ...handlers) {
604
- this._onListeners.push(() => {
605
- return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
606
- });
607
- return this;
608
- }
609
- /**
610
- * The 'all' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
611
- *
612
- * @param {string} path
613
- * @callback {...Function[]} handlers of the middlewares
614
- * @property {object} ctx - context { req , res , query , params , cookies , files , body}
615
- * @property {function} next - go to next function
616
- * @returns {this}
617
- */
618
629
  all(path, ...handlers) {
619
630
  this._onListeners.push(() => {
620
631
  return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
@@ -734,239 +745,23 @@ class Spear {
734
745
  }
735
746
  return;
736
747
  }
737
- _customizeResponse(req, res) {
738
- const response = res;
739
- response.json = (results) => {
740
- if (res.writableEnded)
741
- return;
742
- if (typeof results === 'string') {
743
- if (!res.headersSent) {
744
- res.writeHead(200, { 'Content-Type': 'text/plain' });
745
- }
746
- return res.end(results);
747
- }
748
- if (!res.headersSent) {
749
- res.writeHead(200, { 'Content-Type': 'application/json' });
750
- }
751
- if (results == null) {
752
- if (this._formatResponse != null) {
753
- return res.end(JSON.stringify(this._formatResponse(null, res.statusCode)));
754
- }
755
- return res.end();
756
- }
757
- if (this._formatResponse != null) {
758
- return res.end(JSON.stringify(this._formatResponse({
759
- ...results
760
- }, res.statusCode)));
761
- }
762
- return res.end(JSON.stringify({
763
- ...results,
764
- }));
765
- };
766
- response.send = (results) => {
767
- if (res.writableEnded)
768
- return;
769
- res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' });
770
- return res.end(results);
771
- };
772
- response.html = (results) => {
773
- if (res.writableEnded)
774
- return;
775
- res.writeHead(res.statusCode, { 'Content-Type': 'text/html' });
776
- return res.end(results);
777
- };
778
- response.error = (err) => {
779
- const statusCandidates = [
780
- err?.response?.data?.code,
781
- err?.code,
782
- err?.status,
783
- err?.statusCode,
784
- err?.response?.data?.statusCode
785
- ];
786
- let code = statusCandidates
787
- .map(v => Number(v))
788
- .find(v => Number.isFinite(v) && v >= 400) ?? 500;
789
- const message = err?.response?.data?.errorMessage ??
790
- err?.response?.data?.message ??
791
- err?.message ??
792
- `The request '${req.url}' resulted in a server error.`;
793
- response.status(code);
794
- const payload = { message };
795
- if (this._formatResponse) {
796
- return res.end(JSON.stringify(this._formatResponse(payload, code)));
797
- }
798
- return res.end(JSON.stringify(payload));
799
- };
800
- response.ok = (results) => {
801
- return response.json(results == null ? {} : results);
802
- };
803
- response.created = (results) => {
804
- response.status(201);
805
- return response.json(results == null ? {} : results);
806
- };
807
- response.accepted = (results) => {
808
- response.status(202);
809
- return response.json(results == null ? {} : results);
810
- };
811
- response.noContent = () => {
812
- response.status(204);
813
- return res.end();
814
- };
815
- response.badRequest = (message) => {
816
- if (res.writableEnded)
817
- return;
818
- response.status(400);
819
- message = message ?? `The request '${req.url}' resulted in a bad request. Please review the data and try again.`;
820
- if (this._formatResponse != null) {
821
- return res.end(JSON.stringify(this._formatResponse({ message }, 400)));
822
- }
823
- return res.end(JSON.stringify({
824
- message: message
825
- }));
826
- };
827
- response.unauthorized = (message) => {
828
- response.status(401);
829
- message = message ?? `The request '${req.url}' is unauthorized. Please verify.`;
830
- if (this._formatResponse != null) {
831
- return res.end(JSON.stringify(this._formatResponse({ message }, 401)));
832
- }
833
- return res.end(JSON.stringify({
834
- message
835
- }));
836
- };
837
- response.paymentRequired = (message) => {
838
- response.status(402);
839
- message = message ?? `The request '${req.url}' requires payment. Please proceed with payment.`;
840
- if (this._formatResponse != null) {
841
- return res.end(JSON.stringify(this._formatResponse({ message }, 402)));
842
- }
843
- return res.end(JSON.stringify({
844
- message
845
- }));
846
- };
847
- response.forbidden = (message) => {
848
- response.status(403);
849
- message = message ?? `The request '${req.url}' is forbidden. Please check the permissions or access rights.`;
850
- if (this._formatResponse != null) {
851
- return res.end(JSON.stringify(this._formatResponse({ message }, 403)));
852
- }
853
- return res.end(JSON.stringify({
854
- message
855
- }));
856
- };
857
- response.notFound = (message) => {
858
- response.status(404);
859
- message = message ?? `The request '${req.url}' was not found. Please re-check the your url again.`;
860
- if (this._formatResponse != null) {
861
- return res.end(JSON.stringify(this._formatResponse({ message }, 404)));
862
- }
863
- return res.end(JSON.stringify({
864
- message
865
- }));
866
- };
867
- response.unprocessable = (message) => {
868
- response.status(422);
869
- message = message ?? `The request to '${req.url}' failed validation.`;
870
- if (this._formatResponse != null) {
871
- return res.end(JSON.stringify(this._formatResponse({ message }, 422)));
872
- }
873
- return res.end(JSON.stringify({
874
- message
875
- }));
876
- };
877
- response.tooManyRequests = (message) => {
878
- response.status(429);
879
- message = message ?? `The request '${req.url}' is too many request. Please wait and try agian.`;
880
- if (this._formatResponse != null) {
881
- return res.end(JSON.stringify(this._formatResponse({ message }, 429)));
882
- }
883
- return res.end(JSON.stringify({
884
- message
885
- }));
886
- };
887
- response.serverError = (message) => {
888
- response.status(500);
889
- message = message ?? `The request '${req.url}' resulted in a server error. Please investigate.`;
890
- if (this._formatResponse != null) {
891
- return res.end(JSON.stringify(this._formatResponse({ message }, 500)));
892
- }
893
- return res.end(JSON.stringify({
894
- message
895
- }));
896
- };
897
- response.status = (code) => {
898
- res.writeHead(code, { 'Content-Type': 'application/json' });
899
- return res;
900
- };
901
- response.setCookies = (cookies) => {
902
- for (const [key, v] of Object.entries(cookies)) {
903
- if (typeof v === 'string') {
904
- res.setHeader('Set-Cookie', `${key}=${v}`);
905
- continue;
906
- }
907
- if (v.value === '' || v.value == null)
908
- continue;
909
- let str = `${key}=${v.value}`;
910
- if (v.sameSite != null) {
911
- str += ` ;SameSite=${v.sameSite}`;
912
- }
913
- if (v.domain != null) {
914
- str += ` ;Domain=${v.domain}`;
915
- }
916
- if (v.httpOnly != null) {
917
- str += ` ;HttpOnly`;
918
- }
919
- if (v.secure != null) {
920
- str += ` ;Secure`;
921
- }
922
- if (v.expires != null) {
923
- str += ` ;Expires=${v.expires.toUTCString()}`;
924
- }
925
- res.setHeader('Set-Cookie', str);
926
- }
927
- };
928
- return response;
929
- }
930
748
  _wrapHandlers(...handlers) {
931
749
  return (req, res, ps) => {
932
- if (res.writableEnded)
933
- return;
934
- const request = req;
935
- const response = this._customizeResponse(req, res);
936
- const params = ps;
937
- const headers = req.headers;
938
- const query = this._parser.queryString(req.url);
939
- const xff = headers['x-forwarded-for'];
940
- const ip = (Array.isArray(xff) ? xff[0] : xff)?.split(',')[0]?.trim()
941
- || (Array.isArray(headers['x-real-ip']) ? headers['x-real-ip'][0] : headers['x-real-ip'])
942
- || (Array.isArray(headers['cf-connecting-ip']) ? headers['cf-connecting-ip'][0] : headers['cf-connecting-ip'])
943
- || null;
944
750
  const dispatch = (index = 0) => {
945
- const body = request.body;
946
- const files = request.files;
947
- const cookies = request.cookies;
948
- const ctx = {
949
- req: request,
950
- res: response,
951
- headers: headers ?? {},
952
- params: params ?? {},
953
- query: query ?? {},
954
- body: body ?? {},
955
- files: files ?? {},
956
- cookies: cookies ?? {},
957
- ip: ip ?? null
958
- };
751
+ const handler = handlers[index];
752
+ if (!handler)
753
+ return;
754
+ const ctx = this._createContext({ req, res, ps });
959
755
  try {
960
- const handler = handlers[index];
961
- if (!handler)
962
- return;
963
- if (index === handlers.length - 1) {
964
- return Promise.resolve(this._wrapResponse(handler)(ctx, this._nextFunction(ctx))).catch(err => this._nextFunction(ctx)(err));
756
+ const next = () => dispatch(index + 1);
757
+ const isLast = index === handlers.length - 1;
758
+ if (isLast) {
759
+ return this._wrapResponse(handler)(ctx, this._nextError(ctx));
965
760
  }
966
- return Promise.resolve(handler(ctx, () => dispatch(index + 1))).catch(err => this._nextFunction(ctx)(err));
761
+ return handler(ctx, next);
967
762
  }
968
763
  catch (err) {
969
- return this._nextFunction(ctx)(err);
764
+ return this._nextError(ctx)(err);
970
765
  }
971
766
  };
972
767
  return dispatch();
@@ -976,88 +771,47 @@ class Spear {
976
771
  return (ctx, next) => {
977
772
  Promise.resolve(handler(ctx, next))
978
773
  .then(result => {
979
- if (ctx.res.writableEnded)
774
+ if (ctx.res.writableEnded) {
980
775
  return;
776
+ }
981
777
  if (result instanceof http_1.ServerResponse) {
982
- if (result?.end) {
983
- result.end();
984
- }
985
778
  return;
986
779
  }
987
- if (result == null) {
988
- if (!ctx.res.headersSent) {
989
- ctx.res.writeHead(204, { 'Content-Type': 'text/plain' });
990
- }
991
- ctx.res.end();
780
+ if (result instanceof stream_1.Stream) {
992
781
  return;
993
782
  }
994
- if (typeof result === 'string') {
995
- ctx.res.end(result ?? '');
783
+ if (result == null) {
784
+ ctx.res.noContent();
996
785
  return;
997
786
  }
998
- if (this._formatResponse != null) {
999
- const formattedResult = this._formatResponse(result, ctx.res.statusCode);
1000
- if (typeof formattedResult === 'string') {
1001
- if (!ctx.res.headersSent) {
1002
- ctx.res.writeHead(200, { 'Content-Type': 'text/plain' });
1003
- }
1004
- ctx.res.end(formattedResult);
1005
- return;
1006
- }
1007
- if (!ctx.res.headersSent) {
1008
- ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
1009
- }
1010
- ctx.res.end(JSON.stringify(formattedResult));
787
+ if (typeof result === 'string') {
788
+ ctx.res.send(result);
1011
789
  return;
1012
790
  }
1013
- if (!ctx.res.headersSent) {
1014
- ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
1015
- }
1016
- ctx.res.end(JSON.stringify(result));
791
+ ctx.res.json(result);
1017
792
  return;
1018
793
  })
1019
- .catch(err => {
1020
- return this._nextFunction(ctx)(err);
1021
- });
794
+ .catch(err => next(err));
1022
795
  };
1023
796
  }
1024
- _nextFunction(ctx) {
797
+ _nextError(ctx) {
1025
798
  const NEXT_MESSAGE = "The 'next' function does not have any subsequent function.";
1026
799
  return (err) => {
1027
- if (err != null) {
1028
- if (this._errorHandler != null) {
1029
- const callback = this._errorHandler(err, ctx);
1030
- if (callback == null || !(callback instanceof http_1.ServerResponse)) {
1031
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1032
- return ctx.res.end(JSON.stringify({
1033
- message: err?.message
1034
- }));
1035
- }
1036
- return callback;
1037
- }
1038
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1039
- if (this._formatResponse != null) {
1040
- return ctx.res.end(JSON.stringify(this._formatResponse({
1041
- message: err?.message,
1042
- }, ctx.res.statusCode)));
1043
- }
1044
- ctx.res.end(JSON.stringify({
1045
- message: err?.message
1046
- }));
1047
- return;
1048
- }
800
+ const errorMessage = err?.message || NEXT_MESSAGE;
1049
801
  if (this._errorHandler != null) {
1050
- return this._errorHandler(new Error(NEXT_MESSAGE), ctx);
802
+ return this._errorHandler(err, ctx);
803
+ }
804
+ if (!ctx.res.headersSent) {
805
+ ctx.res.writeHead(500, const_1.HEADER_CONTENT_TYPES['json']);
1051
806
  }
1052
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1053
807
  if (this._formatResponse != null) {
1054
808
  ctx.res.end(JSON.stringify(this._formatResponse({
1055
- message: NEXT_MESSAGE
809
+ message: errorMessage
1056
810
  }, ctx.res.statusCode)));
1057
811
  return;
1058
812
  }
1059
813
  ctx.res.end(JSON.stringify({
1060
- message: NEXT_MESSAGE
814
+ message: errorMessage
1061
815
  }));
1062
816
  return;
1063
817
  };
@@ -1076,7 +830,7 @@ class Spear {
1076
830
  });
1077
831
  }
1078
832
  if (cluster_1.default.isWorker) {
1079
- if ('App' in this._adapter) {
833
+ if (this._adapter.kind === 'uWS') {
1080
834
  const handler = () => {
1081
835
  this._onListeners.forEach(listener => listener());
1082
836
  if (this._swagger.use) {
@@ -1114,188 +868,134 @@ class Spear {
1114
868
  const lookup = this._router.lookup.bind(this._router);
1115
869
  const cors = this._cors;
1116
870
  const adapter = this._adapter;
1117
- if ('App' in adapter) {
1118
- const server = adapter.App();
871
+ if (adapter.kind === 'uWS') {
872
+ const server = adapter.server.App();
1119
873
  server.any('/*', (uwsRes, uwsReq) => {
1120
- const { req, res } = this._uWSRequestResponse(uwsReq, uwsRes);
874
+ const { req, res } = (0, uWS_1.uWSAdaptRequestResponse)(uwsReq, uwsRes);
1121
875
  if (cors)
1122
876
  cors(req, res);
1123
877
  return lookup(req, res);
1124
878
  });
1125
- if (this._ws) {
879
+ if (this._ws?.handler) {
1126
880
  server.ws('/*', {
1127
881
  open: (ws) => {
1128
- this._ws?.connection?.(ws);
882
+ this._ws.handler?.connection?.(ws);
1129
883
  },
1130
884
  message: (ws, message) => {
1131
- this._ws?.message?.(ws, Buffer.from(message));
885
+ this._ws.handler?.message?.(ws, Buffer.from(message));
1132
886
  },
1133
887
  close: (ws, code, message) => {
1134
- this._ws?.close?.(ws, code, Buffer.from(message));
888
+ this._ws.handler?.close?.(ws, code, Buffer.from(message));
1135
889
  }
1136
890
  });
1137
891
  }
1138
892
  return server;
1139
893
  }
894
+ if (adapter.kind === 'net') {
895
+ const server = net_1.default.createServer((socket) => {
896
+ (0, net_2.netAdaptRequestResponse)(socket, (req, res) => {
897
+ if (cors)
898
+ cors(req, res);
899
+ return lookup(req, res);
900
+ });
901
+ });
902
+ if (this._ws?.handler) {
903
+ this._ws.server = new ws_1.default.Server({ server, ...this._ws.options });
904
+ this._ws.server.on('connection', (ws) => {
905
+ if (this._ws.handler?.connection) {
906
+ this._ws.handler.connection(ws);
907
+ }
908
+ ws.on('message', (data) => {
909
+ this._ws.handler?.message?.(ws, data);
910
+ });
911
+ ws.on('close', (code, reason) => {
912
+ if (this._ws.handler?.close) {
913
+ this._ws.handler?.close(ws, code, reason);
914
+ }
915
+ });
916
+ ws.on('error', (err) => {
917
+ if (this._ws.handler?.error) {
918
+ this._ws.handler.error(ws, err);
919
+ }
920
+ });
921
+ });
922
+ }
923
+ return server;
924
+ }
1140
925
  const server = http_1.default.createServer((req, res) => {
1141
926
  if (cors)
1142
927
  cors(req, res);
1143
928
  return lookup(req, res);
1144
929
  });
1145
- if (this._ws) {
1146
- this._wss = new ws_1.default.Server({ server, ...this._wsOptions });
1147
- this._wss.on('connection', (ws) => {
1148
- if (this._ws?.connection) {
1149
- this._ws.connection(ws);
930
+ if (this._ws?.handler) {
931
+ this._ws.server = new ws_1.default.Server({ server, ...this._ws.options });
932
+ this._ws.server.on('connection', (ws) => {
933
+ if (this._ws.handler?.connection) {
934
+ this._ws.handler.connection(ws);
1150
935
  }
1151
936
  ws.on('message', (data) => {
1152
- this._ws?.message?.(ws, data);
937
+ this._ws.handler?.message?.(ws, data);
1153
938
  });
1154
939
  ws.on('close', (code, reason) => {
1155
- if (this._ws?.close) {
1156
- this._ws.close(ws, code, reason);
940
+ if (this._ws.handler?.close) {
941
+ this._ws.handler?.close(ws, code, reason);
1157
942
  }
1158
943
  });
1159
944
  ws.on('error', (err) => {
1160
- if (this._ws?.error) {
1161
- this._ws.error(ws, err);
945
+ if (this._ws.handler?.error) {
946
+ this._ws.handler.error(ws, err);
1162
947
  }
1163
948
  });
1164
949
  });
1165
950
  }
1166
951
  return server;
1167
952
  }
1168
- _uWSRequestResponse(uwsReq, uwsRes) {
1169
- const req = {
1170
- method: String(uwsReq.getMethod()).toLocaleUpperCase(),
1171
- url: uwsReq.getUrl() + (uwsReq.getQuery() ? `?${uwsReq.getQuery()}` : ''),
1172
- headers: {}
1173
- };
1174
- uwsReq.forEach((key, value) => req.headers[key] = value);
1175
- const res = {
1176
- writeHeader: (key, value) => {
1177
- if (!res.aborted) {
1178
- uwsRes.writeHeader(key, value);
1179
- }
1180
- return res;
1181
- },
1182
- setHeader: (key, value) => {
1183
- if (!res.aborted) {
1184
- uwsRes.writeHeader(key, value);
1185
- }
1186
- return res;
1187
- },
1188
- writeHead(status, context) {
1189
- res.writeHeaders = {
1190
- ...res.writeHeaders,
1191
- [status]: context
1192
- };
1193
- res.headersSent = true;
1194
- res.statusCode = status;
1195
- return res;
1196
- },
1197
- _writeHead(status, context) {
1198
- const statusMessages = {
1199
- 100: 'Continue',
1200
- 101: 'Switching Protocols',
1201
- 102: 'Processing',
1202
- 200: 'OK',
1203
- 201: 'Created',
1204
- 202: 'Accepted',
1205
- 203: 'Non-Authoritative Information',
1206
- 204: 'No Content',
1207
- 205: 'Reset Content',
1208
- 206: 'Partial Content',
1209
- 207: 'Multi-Status',
1210
- 208: 'Already Reported',
1211
- 226: 'IM Used',
1212
- 300: 'Multiple Choices',
1213
- 301: 'Moved Permanently',
1214
- 302: 'Found',
1215
- 303: 'See Other',
1216
- 304: 'Not Modified',
1217
- 305: 'Use Proxy',
1218
- 306: '(Unused)',
1219
- 307: 'Temporary Redirect',
1220
- 308: 'Permanent Redirect',
1221
- 400: 'Bad Request',
1222
- 401: 'Unauthorized',
1223
- 402: 'Payment Required',
1224
- 403: 'Forbidden',
1225
- 404: 'Not Found',
1226
- 405: 'Method Not Allowed',
1227
- 406: 'Not Acceptable',
1228
- 407: 'Proxy Authentication Required',
1229
- 408: 'Request Timeout',
1230
- 409: 'Conflict',
1231
- 410: 'Gone',
1232
- 411: 'Length Required',
1233
- 412: 'Precondition Failed',
1234
- 413: 'Payload Too Large',
1235
- 414: 'URI Too Long',
1236
- 415: 'Unsupported Media Type',
1237
- 416: 'Range Not Satisfiable',
1238
- 417: 'Expectation Failed',
1239
- 418: 'I\'m a teapot',
1240
- 421: 'Misdirected Request',
1241
- 422: 'Unprocessable Entity',
1242
- 423: 'Locked',
1243
- 424: 'Failed Dependency',
1244
- 425: 'Too Early',
1245
- 426: 'Upgrade Required',
1246
- 428: 'Precondition Required',
1247
- 429: 'Too Many Requests',
1248
- 431: 'Request Header Fields Too Large',
1249
- 451: 'Unavailable For Legal Reasons',
1250
- 500: 'Internal Server Error',
1251
- 501: 'Not Implemented',
1252
- 502: 'Bad Gateway',
1253
- 503: 'Service Unavailable',
1254
- 504: 'Gateway Timeout',
1255
- 505: 'HTTP Version Not Supported',
1256
- 506: 'Variant Also Negotiates',
1257
- 507: 'Insufficient Storage',
1258
- 508: 'Loop Detected',
1259
- 510: 'Not Extended',
1260
- 511: 'Network Authentication Required'
1261
- };
1262
- const statusMessage = statusMessages[status] || statusMessages[500];
1263
- res.uwsRes.writeStatus(`${status} ${statusMessage}`);
1264
- res.uwsRes.writeHeader(Object.keys(context)[0], Object.values(context)[0]);
1265
- return res;
1266
- },
1267
- writeStatus: (status) => {
1268
- if (!res.aborted) {
1269
- res.uwsRes.writeStatus(status);
1270
- }
1271
- return res;
1272
- },
1273
- end: (str) => {
1274
- if (res.aborted) {
1275
- return;
1276
- }
1277
- uwsRes.cork(() => {
1278
- if (!res.aborted) {
1279
- res.aborted = true;
1280
- for (const h in res.writeHeaders) {
1281
- //@ts-ignore
1282
- res._writeHead(h, res.writeHeaders[h]);
1283
- }
1284
- uwsRes.end(str);
1285
- return;
1286
- }
1287
- });
1288
- },
1289
- aborted: false,
1290
- writeHeaders: {},
1291
- headersSent: false,
1292
- statusCode: 200,
1293
- uwsRes,
1294
- };
1295
- uwsRes.onAborted(() => {
1296
- res.aborted = true;
953
+ _createContext({ req, res, ps }) {
954
+ const request = req;
955
+ const response = (0, response_1.Response)(req, res, {
956
+ formatResponse: this._formatResponse,
957
+ isUwebSocket: this._adapter.kind === 'uWS'
1297
958
  });
1298
- return { req, res };
959
+ const headers = req.headers;
960
+ const params = ps;
961
+ const body = request.body;
962
+ const files = request.files;
963
+ const cookies = request.cookies;
964
+ const query = this._parser.queryString(req.url) || {};
965
+ const xff = headers['x-forwarded-for'];
966
+ const xrip = headers['x-real-ip'];
967
+ const cfip = headers['cf-connecting-ip'];
968
+ let ips = [];
969
+ if (cfip) {
970
+ ips = Array.isArray(cfip) ? cfip : [cfip];
971
+ }
972
+ else if (xff) {
973
+ ips = Array.isArray(xff) ? xff : [xff];
974
+ }
975
+ else if (xrip) {
976
+ ips = Array.isArray(xrip) ? xrip : [xrip];
977
+ }
978
+ else {
979
+ const addr = req.socket?.remoteAddress;
980
+ ips = addr ? [addr] : [];
981
+ }
982
+ const ip = (ips.length ? ips[0] : null);
983
+ request.params = params;
984
+ request.query = query;
985
+ request.ip = ip;
986
+ request.ips = ips;
987
+ return {
988
+ req: request,
989
+ res: response,
990
+ headers: headers || Object.create(null),
991
+ params: params || Object.create(null),
992
+ query,
993
+ body: body || Object.create(null),
994
+ files: files || Object.create(null),
995
+ cookies: cookies || Object.create(null),
996
+ ip,
997
+ ips
998
+ };
1299
999
  }
1300
1000
  _normalizePath(...paths) {
1301
1001
  const path = paths
@@ -1324,8 +1024,8 @@ class Spear {
1324
1024
  routes
1325
1025
  });
1326
1026
  this._router.get(staticUrl, staticSwaggerHandler);
1327
- this._router.get(path, (_, res) => {
1328
- res.writeHead(200, { 'Content-Type': 'text/html' });
1027
+ this._router.get(path, (req, res) => {
1028
+ res.writeHead(200, const_1.HEADER_CONTENT_TYPES['html']);
1329
1029
  res.end(html);
1330
1030
  return;
1331
1031
  });