tspace-spear 1.2.2 → 1.2.4

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 (57) hide show
  1. package/README.md +304 -23
  2. package/dist/lib/core/const/index.d.ts +153 -0
  3. package/dist/lib/core/const/index.js +105 -0
  4. package/dist/lib/core/const/index.js.map +1 -0
  5. package/dist/lib/core/decorators/context.d.ts +16 -9
  6. package/dist/lib/core/decorators/context.js +85 -59
  7. package/dist/lib/core/decorators/context.js.map +1 -1
  8. package/dist/lib/core/decorators/headers.d.ts +2 -2
  9. package/dist/lib/core/decorators/headers.js +1 -1
  10. package/dist/lib/core/decorators/headers.js.map +1 -1
  11. package/dist/lib/core/decorators/methods.d.ts +7 -7
  12. package/dist/lib/core/decorators/methods.js.map +1 -1
  13. package/dist/lib/core/decorators/middleware.d.ts +3 -3
  14. package/dist/lib/core/decorators/middleware.js +2 -2
  15. package/dist/lib/core/decorators/middleware.js.map +1 -1
  16. package/dist/lib/core/decorators/statusCode.d.ts +1 -1
  17. package/dist/lib/core/decorators/statusCode.js.map +1 -1
  18. package/dist/lib/core/decorators/swagger.d.ts +1 -1
  19. package/dist/lib/core/decorators/swagger.js.map +1 -1
  20. package/dist/lib/core/server/express.d.ts +295 -0
  21. package/dist/lib/core/server/express.js +1356 -0
  22. package/dist/lib/core/server/express.js.map +1 -0
  23. package/dist/lib/core/server/fast-router.d.ts +133 -0
  24. package/dist/lib/core/server/fast-router.js +277 -0
  25. package/dist/lib/core/server/fast-router.js.map +1 -0
  26. package/dist/lib/core/server/index.d.ts +43 -36
  27. package/dist/lib/core/server/index.js +300 -426
  28. package/dist/lib/core/server/index.js.map +1 -1
  29. package/dist/lib/core/server/net/index.d.ts +20 -0
  30. package/dist/lib/core/server/net/index.js +393 -0
  31. package/dist/lib/core/server/net/index.js.map +1 -0
  32. package/dist/lib/core/server/parser-factory.d.ts +20 -12
  33. package/dist/lib/core/server/parser-factory.js +283 -196
  34. package/dist/lib/core/server/parser-factory.js.map +1 -1
  35. package/dist/lib/core/server/request.d.ts +2 -0
  36. package/dist/lib/core/server/request.js +7 -0
  37. package/dist/lib/core/server/request.js.map +1 -0
  38. package/dist/lib/core/server/response.d.ts +6 -0
  39. package/dist/lib/core/server/response.js +168 -0
  40. package/dist/lib/core/server/response.js.map +1 -0
  41. package/dist/lib/core/server/router.d.ts +2 -2
  42. package/dist/lib/core/server/router.js +2 -1
  43. package/dist/lib/core/server/router.js.map +1 -1
  44. package/dist/lib/core/server/uWS/index.d.ts +30 -0
  45. package/dist/lib/core/server/uWS/index.js +357 -0
  46. package/dist/lib/core/server/uWS/index.js.map +1 -0
  47. package/dist/lib/core/types/index.d.ts +159 -38
  48. package/dist/lib/core/utils/index.d.ts +12 -0
  49. package/dist/lib/core/utils/index.js +137 -0
  50. package/dist/lib/core/utils/index.js.map +1 -0
  51. package/dist/lib/index.d.ts +3 -3
  52. package/dist/lib/index.js +3 -2
  53. package/dist/lib/index.js.map +1 -1
  54. package/package.json +27 -11
  55. package/dist/tests/benchmark.test.d.ts +0 -1
  56. package/dist/tests/benchmark.test.js +0 -145
  57. package/dist/tests/benchmark.test.js.map +0 -1
@@ -38,15 +38,20 @@ 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
- const url_1 = require("url");
47
47
  const on_finished_1 = __importDefault(require("on-finished"));
48
48
  const ws_1 = __importDefault(require("ws"));
49
+ const net_1 = __importDefault(require("net"));
49
50
  const parser_factory_1 = require("./parser-factory");
51
+ const fast_router_1 = require("./fast-router");
52
+ const response_1 = require("./response");
53
+ const uWS_1 = require("./uWS");
54
+ const net_2 = require("./net");
50
55
  /**
51
56
  *
52
57
  * The 'Spear' class is used to create a server and handle HTTP requests.
@@ -66,9 +71,10 @@ const parser_factory_1 = require("./parser-factory");
66
71
  class Spear {
67
72
  _controllers;
68
73
  _middlewares;
69
- _globalPrefix;
70
- _router = (0, find_my_way_1.default)();
74
+ _router = new fast_router_1.FastRouter();
71
75
  _parser = new parser_factory_1.ParserFactory();
76
+ _globalPrefix = '';
77
+ _adapter = { kind: 'http', server: http_1.default };
72
78
  _cluster;
73
79
  _cors;
74
80
  _swagger = {
@@ -76,7 +82,7 @@ class Spear {
76
82
  path: '/api/docs',
77
83
  servers: [
78
84
  {
79
- url: 'http://localhost:3000'
85
+ url: '/'
80
86
  }
81
87
  ],
82
88
  tags: [],
@@ -87,9 +93,11 @@ class Spear {
87
93
  }
88
94
  };
89
95
  _swaggerSpecs = [];
90
- _wss;
91
- _ws;
92
- _wsOptions;
96
+ _ws = {
97
+ handler: null,
98
+ server: null,
99
+ options: null
100
+ };
93
101
  _errorHandler = null;
94
102
  _globalMiddlewares = [];
95
103
  _formatResponse = null;
@@ -102,14 +110,17 @@ class Spear {
102
110
  ms: 1000 * 60 * 10
103
111
  }
104
112
  };
105
- constructor({ controllers, middlewares, globalPrefix, logger, cluster } = {}) {
113
+ constructor({ controllers, middlewares, globalPrefix, logger, cluster, adapter } = {}) {
114
+ this._controllers = controllers;
115
+ this._middlewares = middlewares;
106
116
  if (logger)
107
117
  this.useLogger();
108
118
  if (cluster)
109
119
  this.useCluster(cluster);
110
- this._controllers = controllers;
111
- this._middlewares = middlewares;
112
- this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
120
+ if (adapter)
121
+ this.useAdater(adapter);
122
+ if (globalPrefix)
123
+ this.useGlobalPrefix(globalPrefix);
113
124
  }
114
125
  /**
115
126
  * The get 'instance' method is used to get the instance of Spear.
@@ -122,7 +133,7 @@ class Spear {
122
133
  /**
123
134
  * The get 'routers' method is used get the all routers.
124
135
  *
125
- * @returns {Instance<findMyWayRouter.HTTPVersion.V1>}
136
+ * @returns {FastRouter}
126
137
  */
127
138
  get routers() {
128
139
  return this._router;
@@ -135,8 +146,8 @@ class Spear {
135
146
  * @returns {this}
136
147
  */
137
148
  ws(handlers, options) {
138
- this._ws = handlers();
139
- this._wsOptions = options ?? {};
149
+ this._ws.handler = handlers();
150
+ this._ws.options = options ?? {};
140
151
  return this;
141
152
  }
142
153
  /**
@@ -151,6 +162,40 @@ class Spear {
151
162
  this._globalMiddlewares.push(middleware);
152
163
  return this;
153
164
  }
165
+ /**
166
+ * The 'useGlobalPrefix' method is used to sets a global prefix for all routes in the router.
167
+ *
168
+ * If `globalPrefix` is `null` or `undefined`, it will default to an empty string,
169
+ * meaning no prefix will be applied.
170
+ *
171
+ * @param {string | null} globalPrefix - The base path prefix to apply to all routes.
172
+ * @returns {this} Returns the current instance for method chaining.
173
+ */
174
+ useGlobalPrefix(globalPrefix) {
175
+ this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
176
+ return this;
177
+ }
178
+ /**
179
+ * The 'useAdater' method is used to switch between different server implementations,
180
+ * such as the native Node.js HTTP server or uWebSockets.js (uWS).
181
+ *
182
+ * @param {T.AdapterServer} adapter - The adapter instance (e.g., HTTP or uWS).
183
+ * @returns {this} Returns the current instance for chaining
184
+ */
185
+ useAdater(adapter) {
186
+ if (adapter === http_1.default) {
187
+ this._adapter = { kind: 'http', server: adapter };
188
+ }
189
+ else if (adapter === net_1.default) {
190
+ this._adapter = { kind: 'net', server: adapter };
191
+ }
192
+ else {
193
+ //@ts-ignore
194
+ this._adapter = { kind: 'uWS', server: adapter };
195
+ }
196
+ this._parser.useAdater(this._adapter);
197
+ return this;
198
+ }
154
199
  /**
155
200
  * The 'useCluster' method is used cluster run the server
156
201
  *
@@ -187,13 +232,13 @@ class Spear {
187
232
  ? `\x1b[32m${statusCode}\x1b[0m`
188
233
  : `\x1b[31m${statusCode}\x1b[0m`;
189
234
  };
190
- if (exceptPath instanceof RegExp && exceptPath.test(String(req.url)))
235
+ if (exceptPath instanceof RegExp && exceptPath.test(req.url))
191
236
  return next();
192
- if (Array.isArray(exceptPath) && exceptPath.some(v => String(req.url) === v))
237
+ if (Array.isArray(exceptPath) && exceptPath.some(v => req.url === v))
193
238
  return next();
194
239
  if (methods != null &&
195
240
  methods.length &&
196
- !methods.some(v => v.toLocaleLowerCase() === String(req.method).toLocaleLowerCase())) {
241
+ !methods.some(v => v.toLowerCase() === req.method.toLowerCase())) {
197
242
  return next();
198
243
  }
199
244
  const startTime = process.hrtime();
@@ -202,7 +247,7 @@ class Spear {
202
247
  `[\x1b[1m\x1b[34mINFO\x1b[0m]`,
203
248
  `\x1b[34m${new Date().toJSON()}\x1b[0m`,
204
249
  `\x1b[33m${req.method}\x1b[0m`,
205
- `${decodeURIComponent(String(req.url))}`,
250
+ `${decodeURIComponent(req.url)}`,
206
251
  `${statusCode(res)}`,
207
252
  `${diffTime(startTime)}`,
208
253
  ].join(" "));
@@ -219,29 +264,26 @@ class Spear {
219
264
  */
220
265
  useBodyParser({ except } = {}) {
221
266
  this._globalMiddlewares.push((ctx, next) => {
222
- const { req } = ctx;
223
- const contentType = req?.headers['content-type'] ?? '';
224
- const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
225
- const isCanParserBody = contentType.includes('application/json') ||
226
- contentType.includes('application/x-www-form-urlencoded');
227
- if (except != null &&
228
- Array.isArray(except) &&
229
- except.some(v => v.toLocaleLowerCase() === (req.method)?.toLocaleLowerCase())) {
267
+ const { req, res } = ctx;
268
+ if (Array.isArray(except) &&
269
+ except.some(v => v.toLowerCase() === (req.method).toLowerCase())) {
230
270
  return next();
231
271
  }
232
- if (isFileUpload)
272
+ const contentType = req?.headers['content-type'] ?? null;
273
+ if (contentType == null)
233
274
  return next();
234
- if (!isCanParserBody)
275
+ const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
276
+ if (isFileUpload)
235
277
  return next();
236
278
  if (req?.body != null)
237
279
  return next();
238
- Promise.resolve(this._parser.body(req))
239
- .then(r => {
240
- req.body = r;
280
+ Promise.resolve(this._parser.body(req, res))
281
+ .then(body => {
282
+ req.body = body;
241
283
  return next();
242
284
  })
243
285
  .catch(err => {
244
- return this._nextFunction(ctx)(err);
286
+ return this._nextError(ctx)(err);
245
287
  });
246
288
  });
247
289
  return this;
@@ -250,11 +292,11 @@ class Spear {
250
292
  * The 'useFileUpload' method is a middleware used to handler file uploads. It adds a file upload of incoming HTTP requests.
251
293
  *
252
294
  * @param {?Object}
253
- * @property {?number} limits
295
+ * @property {?number} limits // bytes. default Infinity
254
296
  * @property {?string} tempFileDir
255
297
  * @property {?Object} removeTempFile
256
298
  * @property {boolean} removeTempFile.remove
257
- * @property {number} removeTempFile.ms
299
+ * @property {number} removeTempFile.ms
258
300
  * @returns
259
301
  */
260
302
  useFileUpload({ limit, tempFileDir, removeTempFile } = {}) {
@@ -268,7 +310,7 @@ class Spear {
268
310
  this._fileUploadOptions.removeTempFile = removeTempFile;
269
311
  }
270
312
  this._globalMiddlewares.push((ctx, next) => {
271
- const { req } = ctx;
313
+ const { req, res } = ctx;
272
314
  if (req.method === 'GET') {
273
315
  return next();
274
316
  }
@@ -279,14 +321,14 @@ class Spear {
279
321
  if (req?.files != null)
280
322
  return next();
281
323
  Promise
282
- .resolve(this._parser.files(req, this._fileUploadOptions))
324
+ .resolve(this._parser.files({ req, res, options: this._fileUploadOptions }))
283
325
  .then(r => {
284
326
  req.files = r.files;
285
327
  req.body = r.body;
286
328
  return next();
287
329
  })
288
330
  .catch(err => {
289
- return this._nextFunction(ctx)(err);
331
+ return this._nextError(ctx)(err);
290
332
  });
291
333
  });
292
334
  return this;
@@ -359,6 +401,22 @@ class Spear {
359
401
  });
360
402
  return server;
361
403
  }
404
+ if (this._adapter.kind === 'uWS') {
405
+ const handler = () => {
406
+ this._onListeners.forEach(listener => listener());
407
+ if (this._swagger.use) {
408
+ this._swaggerHandler();
409
+ }
410
+ callback?.({ server, port });
411
+ };
412
+ if (hostname) {
413
+ server.listen(port, String(hostname), handler);
414
+ }
415
+ else {
416
+ server.listen(port, handler);
417
+ }
418
+ return server;
419
+ }
362
420
  const args = hostname
363
421
  ? [port, hostname, () => callback?.({ server, port: port })]
364
422
  : [port, () => callback?.({ server, port: port })];
@@ -369,10 +427,6 @@ class Spear {
369
427
  this._swaggerHandler();
370
428
  }
371
429
  });
372
- server.on('error', (_) => {
373
- port = Math.floor(Math.random() * 8999) + 1000;
374
- server.listen(port);
375
- });
376
430
  return server;
377
431
  }
378
432
  /**
@@ -443,20 +497,10 @@ class Spear {
443
497
  */
444
498
  notfound(fn) {
445
499
  const handler = ({ req, res }) => {
446
- return fn({
447
- req,
448
- res: this._customizeResponse(req, res),
449
- headers: {},
450
- query: {},
451
- files: {},
452
- body: {},
453
- params: {},
454
- cookies: {}
455
- });
500
+ const ctx = this._createContext({ req, res, ps: {} });
501
+ return fn(ctx);
456
502
  };
457
- this._onListeners.push(() => {
458
- return this.all('*', ...this._globalMiddlewares, handler);
459
- });
503
+ this.all('*', handler);
460
504
  return this;
461
505
  }
462
506
  /**
@@ -565,7 +609,7 @@ class Spear {
565
609
  return this;
566
610
  }
567
611
  /**
568
- * The 'any' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
612
+ * The 'all' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
569
613
  *
570
614
  * @param {string} path
571
615
  * @callback {...Function[]} handlers of the middlewares
@@ -573,58 +617,12 @@ class Spear {
573
617
  * @property {function} next - go to next function
574
618
  * @returns {this}
575
619
  */
576
- any(path, ...handlers) {
577
- this._onListeners.push(() => {
578
- return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
579
- });
580
- return this;
581
- }
582
- /**
583
- * The 'all' method is used to add the request handler to the router for 'GET' 'POST' 'PUT' 'PATCH' 'DELETE' 'HEAD' 'OPTIONS' methods.
584
- *
585
- * @param {string} path
586
- * @callback {...Function[]} handlers of the middlewares
587
- * @property {object} ctx - context { req , res , query , params , cookies , files , body}
588
- * @property {function} next - go to next function
589
- * @returns {this}
590
- */
591
620
  all(path, ...handlers) {
592
621
  this._onListeners.push(() => {
593
622
  return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
594
623
  });
595
624
  return this;
596
625
  }
597
- _clusterMode({ server, port, hostname, callback }) {
598
- if (cluster_1.default.isPrimary) {
599
- const numCPUs = os_1.default.cpus().length;
600
- const maxWorkers = typeof this._cluster === 'boolean' || this._cluster == null
601
- ? numCPUs
602
- : this._cluster;
603
- for (let i = 0; i < maxWorkers; i++) {
604
- cluster_1.default.fork();
605
- }
606
- cluster_1.default.on('exit', () => {
607
- cluster_1.default.fork();
608
- });
609
- }
610
- if (cluster_1.default.isWorker) {
611
- const args = hostname
612
- ? [port, hostname, () => callback?.({ server, port: port })]
613
- : [port, () => callback?.({ server, port: port })];
614
- server.listen(...args);
615
- server.on('listening', () => {
616
- this._onListeners.forEach(listener => listener());
617
- if (this._swagger.use) {
618
- this._swaggerHandler();
619
- }
620
- });
621
- server.on('error', (_) => {
622
- port = Math.floor(Math.random() * 8999) + 1000;
623
- server.listen(port);
624
- });
625
- }
626
- return;
627
- }
628
626
  async _import(dir, pattern) {
629
627
  const directories = fs_1.default.readdirSync(dir, { withFileTypes: true });
630
628
  const files = (await Promise.all(directories.map((directory) => {
@@ -738,382 +736,258 @@ class Spear {
738
736
  }
739
737
  return;
740
738
  }
741
- _customizeResponse(req, res) {
742
- const response = res;
743
- response.json = (results) => {
744
- if (res.writableEnded)
745
- return;
746
- if (typeof results === 'string') {
747
- if (!res.headersSent) {
748
- res.writeHead(200, { 'Content-Type': 'text/plain' });
749
- }
750
- return res.end(results);
751
- }
752
- if (!res.headersSent) {
753
- res.writeHead(200, { 'Content-Type': 'application/json' });
754
- }
755
- if (results == null) {
756
- if (this._formatResponse != null) {
757
- return res.end(JSON.stringify(this._formatResponse(null, res.statusCode), null, 2));
758
- }
759
- return res.end();
760
- }
761
- if (this._formatResponse != null) {
762
- return res.end(JSON.stringify(this._formatResponse({
763
- ...results
764
- }, res.statusCode), null, 2));
765
- }
766
- return res.end(JSON.stringify({
767
- ...results,
768
- }, null, 2));
769
- };
770
- response.send = (results) => {
771
- if (res.writableEnded)
772
- return;
773
- res.writeHead(res.statusCode, { 'Content-Type': 'text/plain' });
774
- return res.end(results);
775
- };
776
- response.html = (results) => {
777
- if (res.writableEnded)
778
- return;
779
- res.writeHead(res.statusCode, { 'Content-Type': 'text/html' });
780
- return res.end(results);
781
- };
782
- response.error = (err) => {
783
- const statusCandidates = [
784
- err?.response?.data?.code,
785
- err?.code,
786
- err?.status,
787
- err?.statusCode,
788
- err?.response?.data?.statusCode
789
- ];
790
- let code = statusCandidates
791
- .map(v => Number(v))
792
- .find(v => Number.isFinite(v) && v >= 400) ?? 500;
793
- const message = err?.response?.data?.errorMessage ??
794
- err?.response?.data?.message ??
795
- err?.message ??
796
- `The request '${req.url}' resulted in a server error.`;
797
- response.status(code);
798
- const payload = { message };
799
- if (this._formatResponse) {
800
- return res.end(JSON.stringify(this._formatResponse(payload, code)));
801
- }
802
- return res.end(JSON.stringify(payload));
803
- };
804
- response.ok = (results) => {
805
- return response.json(results == null ? {} : results);
806
- };
807
- response.created = (results) => {
808
- response.status(201);
809
- return response.json(results == null ? {} : results);
810
- };
811
- response.accepted = (results) => {
812
- response.status(202);
813
- return response.json(results == null ? {} : results);
814
- };
815
- response.noContent = () => {
816
- response.status(204);
817
- return res.end();
818
- };
819
- response.badRequest = (message) => {
820
- if (res.writableEnded)
821
- return;
822
- response.status(400);
823
- message = message ?? `The request '${req.url}' resulted in a bad request. Please review the data and try again.`;
824
- if (this._formatResponse != null) {
825
- return res.end(JSON.stringify(this._formatResponse({ message }, 400), null, 2));
826
- }
827
- return res.end(JSON.stringify({
828
- message: message
829
- }, null, 2));
830
- };
831
- response.unauthorized = (message) => {
832
- response.status(401);
833
- message = message ?? `The request '${req.url}' is unauthorized. Please verify.`;
834
- if (this._formatResponse != null) {
835
- return res.end(JSON.stringify(this._formatResponse({ message }, 401), null, 2));
836
- }
837
- return res.end(JSON.stringify({
838
- message
839
- }, null, 2));
840
- };
841
- response.paymentRequired = (message) => {
842
- response.status(402);
843
- message = message ?? `The request '${req.url}' requires payment. Please proceed with payment.`;
844
- if (this._formatResponse != null) {
845
- return res.end(JSON.stringify(this._formatResponse({ message }, 402), null, 2));
846
- }
847
- return res.end(JSON.stringify({
848
- message
849
- }, null, 2));
850
- };
851
- response.forbidden = (message) => {
852
- response.status(403);
853
- message = message ?? `The request '${req.url}' is forbidden. Please check the permissions or access rights.`;
854
- if (this._formatResponse != null) {
855
- return res.end(JSON.stringify(this._formatResponse({ message }, 403), null, 2));
856
- }
857
- return res.end(JSON.stringify({
858
- message
859
- }, null, 2));
860
- };
861
- response.notFound = (message) => {
862
- response.status(404);
863
- message = message ?? `The request '${req.url}' was not found. Please re-check the your url again.`;
864
- if (this._formatResponse != null) {
865
- return res.end(JSON.stringify(this._formatResponse({ message }, 404), null, 2));
866
- }
867
- return res.end(JSON.stringify({
868
- message
869
- }, null, 2));
870
- };
871
- response.unprocessable = (message) => {
872
- response.status(422);
873
- message = message ?? `The request to '${req.url}' failed validation.`;
874
- if (this._formatResponse != null) {
875
- return res.end(JSON.stringify(this._formatResponse({ message }, 422), null, 2));
876
- }
877
- return res.end(JSON.stringify({
878
- message
879
- }, null, 2));
880
- };
881
- response.tooManyRequests = (message) => {
882
- response.status(429);
883
- message = message ?? `The request '${req.url}' is too many request. Please wait and try agian.`;
884
- if (this._formatResponse != null) {
885
- return res.end(JSON.stringify(this._formatResponse({ message }, 429), null, 2));
886
- }
887
- return res.end(JSON.stringify({
888
- message
889
- }, null, 2));
890
- };
891
- response.serverError = (message) => {
892
- response.status(500);
893
- message = message ?? `The request '${req.url}' resulted in a server error. Please investigate.`;
894
- if (this._formatResponse != null) {
895
- return res.end(JSON.stringify(this._formatResponse({ message }, 500), null, 2));
896
- }
897
- return res.end(JSON.stringify({
898
- message
899
- }, null, 2));
900
- };
901
- response.status = (code) => {
902
- res.writeHead(code, { 'Content-Type': 'application/json' });
903
- return res;
904
- };
905
- response.setCookies = (cookies) => {
906
- for (const [key, v] of Object.entries(cookies)) {
907
- if (typeof v === 'string') {
908
- res.setHeader('Set-Cookie', `${key}=${v}`);
909
- continue;
910
- }
911
- if (v.value === '' || v.value == null)
912
- continue;
913
- let str = `${key}=${v.value}`;
914
- if (v.sameSite != null) {
915
- str += ` ;SameSite=${v.sameSite}`;
916
- }
917
- if (v.domain != null) {
918
- str += ` ;Domain=${v.domain}`;
919
- }
920
- if (v.httpOnly != null) {
921
- str += ` ;HttpOnly`;
922
- }
923
- if (v.secure != null) {
924
- str += ` ;Secure`;
925
- }
926
- if (v.expires != null) {
927
- str += ` ;Expires=${v.expires.toUTCString()}`;
928
- }
929
- res.setHeader('Set-Cookie', str);
930
- }
931
- };
932
- return response;
933
- }
934
739
  _wrapHandlers(...handlers) {
935
- return (req, res, params) => {
936
- const nextHandler = (index = 0) => {
740
+ return (req, res, ps) => {
741
+ const dispatch = (index = 0) => {
742
+ const handler = handlers[index];
743
+ if (!handler)
744
+ return;
745
+ const ctx = this._createContext({ req, res, ps });
937
746
  try {
938
- const response = this._customizeResponse(req, res);
939
- const request = req;
940
- request.params = params;
941
- const body = request.body;
942
- const files = request.files;
943
- const cookies = request.cookies;
944
- const headers = request.headers;
945
- const url = new url_1.URL(req.url, "http://localhost");
946
- const query = Object.fromEntries(url.searchParams);
947
- const RecordOrEmptyRecord = (data) => {
948
- if (data == null)
949
- return {};
950
- return Object.keys(data).length ? data : {};
951
- };
952
- const ctx = {
953
- req: request,
954
- res: response,
955
- headers: RecordOrEmptyRecord(headers),
956
- params: RecordOrEmptyRecord(params),
957
- query: RecordOrEmptyRecord(query),
958
- body: RecordOrEmptyRecord(body),
959
- files: RecordOrEmptyRecord(files),
960
- cookies: RecordOrEmptyRecord(cookies)
961
- };
962
- if (index === handlers.length - 1) {
963
- return this._wrapResponse(handlers[index]
964
- .bind(handlers[index]))(ctx, this._nextFunction(ctx));
747
+ const next = () => dispatch(index + 1);
748
+ const isLast = index === handlers.length - 1;
749
+ if (isLast) {
750
+ return this._wrapResponse(handler)(ctx, this._nextError(ctx));
965
751
  }
966
- return handlers[index](ctx, () => {
967
- return nextHandler(index + 1);
968
- });
752
+ return handler(ctx, next);
969
753
  }
970
754
  catch (err) {
971
- const ctx = {
972
- req,
973
- res: this._customizeResponse(req, res),
974
- params: Object.keys(params).length ? params : {},
975
- headers: {},
976
- query: {},
977
- body: {},
978
- files: {},
979
- cookies: {}
980
- };
981
- return this._nextFunction(ctx)(err);
755
+ return this._nextError(ctx)(err);
982
756
  }
983
757
  };
984
- if (res.writableEnded)
985
- return;
986
- return nextHandler();
758
+ return dispatch();
987
759
  };
988
760
  }
989
761
  _wrapResponse(handler) {
990
762
  return (ctx, next) => {
991
763
  Promise.resolve(handler(ctx, next))
992
764
  .then(result => {
993
- if (ctx.res.writableEnded)
765
+ if (ctx.res.writableEnded) {
994
766
  return;
767
+ }
995
768
  if (result instanceof http_1.ServerResponse) {
996
- if (result?.end) {
997
- result.end();
998
- }
999
769
  return;
1000
770
  }
1001
- if (result == null) {
1002
- if (!ctx.res.headersSent) {
1003
- ctx.res.writeHead(204, { 'Content-Type': 'text/plain' });
1004
- }
1005
- ctx.res.end();
771
+ if (result instanceof stream_1.Stream) {
1006
772
  return;
1007
773
  }
1008
- if (typeof result === 'string') {
1009
- if (!ctx.res.headersSent) {
1010
- ctx.res.writeHead(200, { 'Content-Type': 'text/plain' });
1011
- }
1012
- ctx.res.end(result ?? '');
774
+ if (result == null) {
775
+ ctx.res.noContent();
1013
776
  return;
1014
777
  }
1015
- if (this._formatResponse != null) {
1016
- const formattedResult = this._formatResponse(result, ctx.res.statusCode);
1017
- if (typeof formattedResult === 'string') {
1018
- if (!ctx.res.headersSent) {
1019
- ctx.res.writeHead(200, { 'Content-Type': 'text/plain' });
1020
- }
1021
- ctx.res.end(formattedResult);
1022
- return;
1023
- }
1024
- if (!ctx.res.headersSent) {
1025
- ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
1026
- }
1027
- ctx.res.end(JSON.stringify(formattedResult));
778
+ if (typeof result === 'string') {
779
+ ctx.res.send(result);
1028
780
  return;
1029
781
  }
1030
- if (!ctx.res.headersSent) {
1031
- ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
1032
- }
1033
- ctx.res.end(JSON.stringify(result));
782
+ ctx.res.json(result);
1034
783
  return;
1035
784
  })
1036
- .catch(err => {
1037
- return this._nextFunction(ctx)(err);
1038
- });
785
+ .catch(err => next(err));
1039
786
  };
1040
787
  }
1041
- _nextFunction(ctx) {
788
+ _nextError(ctx) {
1042
789
  const NEXT_MESSAGE = "The 'next' function does not have any subsequent function.";
1043
790
  return (err) => {
1044
- if (err != null) {
1045
- if (this._errorHandler != null) {
1046
- const callback = this._errorHandler(err, ctx);
1047
- if (callback == null || !(callback instanceof http_1.ServerResponse)) {
1048
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1049
- return ctx.res.end(JSON.stringify({
1050
- message: err?.message
1051
- }, null, 2));
1052
- }
1053
- return callback;
1054
- }
1055
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1056
- if (this._formatResponse != null) {
1057
- return ctx.res.end(JSON.stringify(this._formatResponse({
1058
- message: err?.message,
1059
- }, ctx.res.statusCode), null, 2));
1060
- }
1061
- return ctx.res.end(JSON.stringify({
1062
- message: err?.message
1063
- }, null, 2));
1064
- }
791
+ const errorMessage = err?.message || NEXT_MESSAGE;
1065
792
  if (this._errorHandler != null) {
1066
- return this._errorHandler(new Error(NEXT_MESSAGE), ctx);
793
+ return this._errorHandler(new Error(errorMessage), ctx);
794
+ }
795
+ if (!ctx.res.headersSent) {
796
+ ctx.res.writeHead(500, const_1.HEADER_CONTENT_TYPES['json']);
1067
797
  }
1068
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1069
798
  if (this._formatResponse != null) {
1070
- return ctx.res.end(JSON.stringify(this._formatResponse({
1071
- message: NEXT_MESSAGE
1072
- }, ctx.res.statusCode), null, 2));
799
+ ctx.res.end(JSON.stringify(this._formatResponse({
800
+ message: errorMessage
801
+ }, ctx.res.statusCode)));
802
+ return;
1073
803
  }
1074
- return ctx.res.end(JSON.stringify({
1075
- message: NEXT_MESSAGE
1076
- }, null, 2));
804
+ ctx.res.end(JSON.stringify({
805
+ message: errorMessage
806
+ }));
807
+ return;
1077
808
  };
1078
809
  }
810
+ _clusterMode({ server, port, hostname, callback }) {
811
+ if (cluster_1.default.isPrimary) {
812
+ const numCPUs = os_1.default.cpus().length;
813
+ const maxWorkers = typeof this._cluster === 'boolean' || this._cluster == null
814
+ ? numCPUs
815
+ : this._cluster;
816
+ for (let i = 0; i < maxWorkers; i++) {
817
+ cluster_1.default.fork();
818
+ }
819
+ cluster_1.default.on('exit', () => {
820
+ cluster_1.default.fork();
821
+ });
822
+ }
823
+ if (cluster_1.default.isWorker) {
824
+ if (this._adapter.kind === 'uWS') {
825
+ const handler = () => {
826
+ this._onListeners.forEach(listener => listener());
827
+ if (this._swagger.use) {
828
+ this._swaggerHandler();
829
+ }
830
+ callback?.({ server, port });
831
+ };
832
+ if (hostname) {
833
+ server.listen(port, hostname, handler);
834
+ return server;
835
+ }
836
+ server.listen(port, handler);
837
+ return server;
838
+ }
839
+ const args = hostname
840
+ ? [port, hostname, () => callback?.({ server, port: port })]
841
+ : [port, () => callback?.({ server, port: port })];
842
+ server.listen(...args);
843
+ server.on('listening', () => {
844
+ this._onListeners.forEach(listener => listener());
845
+ if (this._swagger.use) {
846
+ this._swaggerHandler();
847
+ }
848
+ });
849
+ server.on('error', (_) => {
850
+ port = Math.floor(Math.random() * 8999) + 1000;
851
+ server.listen(port);
852
+ });
853
+ }
854
+ return;
855
+ }
1079
856
  async _createServer() {
1080
857
  await this._registerMiddlewares();
1081
858
  await this._registerControllers();
1082
859
  const lookup = this._router.lookup.bind(this._router);
1083
860
  const cors = this._cors;
1084
- const server = http_1.default.createServer({ maxHeaderSize: 1024 * 1024 }, cors
1085
- ? (req, res) => {
1086
- cors(req, res);
861
+ const adapter = this._adapter;
862
+ if (adapter.kind === 'uWS') {
863
+ const server = adapter.server.App();
864
+ server.any('/*', (uwsRes, uwsReq) => {
865
+ const { req, res } = (0, uWS_1.uWSAdaptRequestResponse)(uwsReq, uwsRes);
866
+ if (cors)
867
+ cors(req, res);
1087
868
  return lookup(req, res);
869
+ });
870
+ if (this._ws?.handler) {
871
+ server.ws('/*', {
872
+ open: (ws) => {
873
+ this._ws.handler?.connection?.(ws);
874
+ },
875
+ message: (ws, message) => {
876
+ this._ws.handler?.message?.(ws, Buffer.from(message));
877
+ },
878
+ close: (ws, code, message) => {
879
+ this._ws.handler?.close?.(ws, code, Buffer.from(message));
880
+ }
881
+ });
1088
882
  }
1089
- : (req, res) => {
1090
- return lookup(req, res);
883
+ return server;
884
+ }
885
+ if (adapter.kind === 'net') {
886
+ const server = net_1.default.createServer((socket) => {
887
+ (0, net_2.netAdaptRequestResponse)(socket, (req, res) => {
888
+ if (cors)
889
+ cors(req, res);
890
+ return lookup(req, res);
891
+ });
1091
892
  });
1092
- if (this._ws) {
1093
- this._wss = new ws_1.default.Server({ server, ...this._wsOptions });
1094
- this._wss.on('connection', (ws) => {
1095
- if (this._ws?.connection) {
1096
- this._ws.connection(ws);
893
+ if (this._ws?.handler) {
894
+ this._ws.server = new ws_1.default.Server({ server, ...this._ws.options });
895
+ this._ws.server.on('connection', (ws) => {
896
+ if (this._ws.handler?.connection) {
897
+ this._ws.handler.connection(ws);
898
+ }
899
+ ws.on('message', (data) => {
900
+ this._ws.handler?.message?.(ws, data);
901
+ });
902
+ ws.on('close', (code, reason) => {
903
+ if (this._ws.handler?.close) {
904
+ this._ws.handler?.close(ws, code, reason);
905
+ }
906
+ });
907
+ ws.on('error', (err) => {
908
+ if (this._ws.handler?.error) {
909
+ this._ws.handler.error(ws, err);
910
+ }
911
+ });
912
+ });
913
+ }
914
+ return server;
915
+ }
916
+ const server = http_1.default.createServer((req, res) => {
917
+ if (cors)
918
+ cors(req, res);
919
+ return lookup(req, res);
920
+ });
921
+ if (this._ws?.handler) {
922
+ this._ws.server = new ws_1.default.Server({ server, ...this._ws.options });
923
+ this._ws.server.on('connection', (ws) => {
924
+ if (this._ws.handler?.connection) {
925
+ this._ws.handler.connection(ws);
1097
926
  }
1098
927
  ws.on('message', (data) => {
1099
- if (this._ws?.message) {
1100
- this._ws.message(ws, data);
1101
- }
928
+ this._ws.handler?.message?.(ws, data);
1102
929
  });
1103
930
  ws.on('close', (code, reason) => {
1104
- if (this._ws?.close) {
1105
- this._ws.close(ws, code, reason);
931
+ if (this._ws.handler?.close) {
932
+ this._ws.handler?.close(ws, code, reason);
1106
933
  }
1107
934
  });
1108
935
  ws.on('error', (err) => {
1109
- if (this._ws?.error) {
1110
- this._ws.error(ws, err);
936
+ if (this._ws.handler?.error) {
937
+ this._ws.handler.error(ws, err);
1111
938
  }
1112
939
  });
1113
940
  });
1114
941
  }
1115
942
  return server;
1116
943
  }
944
+ _createContext({ req, res, ps }) {
945
+ const request = req;
946
+ const response = (0, response_1.Response)(req, res, {
947
+ formatResponse: this._formatResponse,
948
+ isUwebSocket: this._adapter.kind === 'uWS'
949
+ });
950
+ const headers = req.headers;
951
+ const params = ps;
952
+ const body = request.body;
953
+ const files = request.files;
954
+ const cookies = request.cookies;
955
+ const query = this._parser.queryString(req.url) || {};
956
+ const xff = headers['x-forwarded-for'];
957
+ const xrip = headers['x-real-ip'];
958
+ const cfip = headers['cf-connecting-ip'];
959
+ let ips = [];
960
+ if (cfip) {
961
+ ips = Array.isArray(cfip) ? cfip : [cfip];
962
+ }
963
+ else if (xff) {
964
+ ips = Array.isArray(xff) ? xff : [xff];
965
+ }
966
+ else if (xrip) {
967
+ ips = Array.isArray(xrip) ? xrip : [xrip];
968
+ }
969
+ else {
970
+ const addr = req.socket?.remoteAddress;
971
+ ips = addr ? [addr] : [];
972
+ }
973
+ const ip = (ips.length ? ips[0] : null);
974
+ request.params = params;
975
+ request.query = query;
976
+ request.ip = ip;
977
+ request.ips = ips;
978
+ return {
979
+ req: request,
980
+ res: response,
981
+ headers: headers || Object.create(null),
982
+ params: params || Object.create(null),
983
+ query,
984
+ body: body || Object.create(null),
985
+ files: files || Object.create(null),
986
+ cookies: cookies || Object.create(null),
987
+ ip,
988
+ ips
989
+ };
990
+ }
1117
991
  _normalizePath(...paths) {
1118
992
  const path = paths
1119
993
  .join('/')
@@ -1141,10 +1015,10 @@ class Spear {
1141
1015
  routes
1142
1016
  });
1143
1017
  this._router.get(staticUrl, staticSwaggerHandler);
1144
- this._router.get(path, (_, res) => {
1145
- res.writeHead(200, { 'Content-Type': 'text/html' });
1146
- res.write(html);
1147
- return res.end();
1018
+ this._router.get(path, (req, res) => {
1019
+ res.writeHead(200, const_1.HEADER_CONTENT_TYPES['html']);
1020
+ res.end(html);
1021
+ return;
1148
1022
  });
1149
1023
  return;
1150
1024
  }