tspace-spear 1.2.3 → 1.2.5

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 (48) hide show
  1. package/README.md +268 -19
  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/fast-router.d.ts +133 -0
  21. package/dist/lib/core/server/fast-router.js +277 -0
  22. package/dist/lib/core/server/fast-router.js.map +1 -0
  23. package/dist/lib/core/server/index.d.ts +34 -37
  24. package/dist/lib/core/server/index.js +192 -501
  25. package/dist/lib/core/server/index.js.map +1 -1
  26. package/dist/lib/core/server/net/index.d.ts +20 -0
  27. package/dist/lib/core/server/net/index.js +393 -0
  28. package/dist/lib/core/server/net/index.js.map +1 -0
  29. package/dist/lib/core/server/parser-factory.d.ts +10 -11
  30. package/dist/lib/core/server/parser-factory.js +259 -437
  31. package/dist/lib/core/server/parser-factory.js.map +1 -1
  32. package/dist/lib/core/server/response.d.ts +6 -0
  33. package/dist/lib/core/server/response.js +168 -0
  34. package/dist/lib/core/server/response.js.map +1 -0
  35. package/dist/lib/core/server/router.d.ts +2 -12
  36. package/dist/lib/core/server/router.js +2 -13
  37. package/dist/lib/core/server/router.js.map +1 -1
  38. package/dist/lib/core/server/uWS/index.d.ts +30 -0
  39. package/dist/lib/core/server/uWS/index.js +357 -0
  40. package/dist/lib/core/server/uWS/index.js.map +1 -0
  41. package/dist/lib/core/types/index.d.ts +144 -43
  42. package/dist/lib/core/utils/index.d.ts +12 -0
  43. package/dist/lib/core/utils/index.js +137 -0
  44. package/dist/lib/core/utils/index.js.map +1 -0
  45. package/dist/lib/index.d.ts +3 -3
  46. package/dist/lib/index.js +3 -2
  47. package/dist/lib/index.js.map +1 -1
  48. package/package.json +19 -14
@@ -38,14 +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
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 uWS_1 = require("./uWS");
54
+ const net_2 = require("./net");
49
55
  /**
50
56
  *
51
57
  * The 'Spear' class is used to create a server and handle HTTP requests.
@@ -65,10 +71,10 @@ const parser_factory_1 = require("./parser-factory");
65
71
  class Spear {
66
72
  _controllers;
67
73
  _middlewares;
68
- _globalPrefix;
69
- _router = (0, find_my_way_1.default)();
74
+ _router = new fast_router_1.FastRouter();
70
75
  _parser = new parser_factory_1.ParserFactory();
71
- _adapter = http_1.default;
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;
@@ -103,15 +111,16 @@ class Spear {
103
111
  }
104
112
  };
105
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
120
  if (adapter)
111
121
  this.useAdater(adapter);
112
- this._controllers = controllers;
113
- this._middlewares = middlewares;
114
- this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
122
+ if (globalPrefix)
123
+ this.useGlobalPrefix(globalPrefix);
115
124
  }
116
125
  /**
117
126
  * The get 'instance' method is used to get the instance of Spear.
@@ -124,7 +133,7 @@ class Spear {
124
133
  /**
125
134
  * The get 'routers' method is used get the all routers.
126
135
  *
127
- * @returns {Instance<findMyWayRouter.HTTPVersion.V1>}
136
+ * @returns {FastRouter}
128
137
  */
129
138
  get routers() {
130
139
  return this._router;
@@ -137,8 +146,8 @@ class Spear {
137
146
  * @returns {this}
138
147
  */
139
148
  ws(handlers, options) {
140
- this._ws = handlers();
141
- this._wsOptions = options ?? {};
149
+ this._ws.handler = handlers();
150
+ this._ws.options = options ?? {};
142
151
  return this;
143
152
  }
144
153
  /**
@@ -153,16 +162,38 @@ class Spear {
153
162
  this._globalMiddlewares.push(middleware);
154
163
  return this;
155
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
+ }
156
178
  /**
157
179
  * The 'useAdater' method is used to switch between different server implementations,
158
180
  * such as the native Node.js HTTP server or uWebSockets.js (uWS).
159
181
  *
160
- * @param {T.Adapter} adapter - The adapter instance (e.g., HTTP or uWS).
182
+ * @param {T.AdapterServer} adapter - The adapter instance (e.g., HTTP or uWS).
161
183
  * @returns {this} Returns the current instance for chaining
162
184
  */
163
185
  useAdater(adapter) {
164
- this._adapter = adapter;
165
- this._parser.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);
166
197
  return this;
167
198
  }
168
199
  /**
@@ -201,13 +232,13 @@ class Spear {
201
232
  ? `\x1b[32m${statusCode}\x1b[0m`
202
233
  : `\x1b[31m${statusCode}\x1b[0m`;
203
234
  };
204
- if (exceptPath instanceof RegExp && exceptPath.test(String(req.url)))
235
+ if (exceptPath instanceof RegExp && exceptPath.test(req.url))
205
236
  return next();
206
- if (Array.isArray(exceptPath) && exceptPath.some(v => String(req.url) === v))
237
+ if (Array.isArray(exceptPath) && exceptPath.some(v => req.url === v))
207
238
  return next();
208
239
  if (methods != null &&
209
240
  methods.length &&
210
- !methods.some(v => v.toLocaleLowerCase() === String(req.method).toLocaleLowerCase())) {
241
+ !methods.some(v => v.toLowerCase() === req.method.toLowerCase())) {
211
242
  return next();
212
243
  }
213
244
  const startTime = process.hrtime();
@@ -216,7 +247,7 @@ class Spear {
216
247
  `[\x1b[1m\x1b[34mINFO\x1b[0m]`,
217
248
  `\x1b[34m${new Date().toJSON()}\x1b[0m`,
218
249
  `\x1b[33m${req.method}\x1b[0m`,
219
- `${decodeURIComponent(String(req.url))}`,
250
+ `${decodeURIComponent(req.url)}`,
220
251
  `${statusCode(res)}`,
221
252
  `${diffTime(startTime)}`,
222
253
  ].join(" "));
@@ -234,28 +265,25 @@ class Spear {
234
265
  useBodyParser({ except } = {}) {
235
266
  this._globalMiddlewares.push((ctx, next) => {
236
267
  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())) {
268
+ if (Array.isArray(except) &&
269
+ except.some(v => v.toLowerCase() === (req.method).toLowerCase())) {
244
270
  return next();
245
271
  }
246
- if (isFileUpload)
272
+ const contentType = req?.headers['content-type'] ?? null;
273
+ if (contentType == null)
247
274
  return next();
248
- if (!isCanParserBody)
275
+ const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
276
+ if (isFileUpload)
249
277
  return next();
250
278
  if (req?.body != null)
251
279
  return next();
252
280
  Promise.resolve(this._parser.body(req, res))
253
- .then(r => {
254
- req.body = r;
281
+ .then(body => {
282
+ req.body = body;
255
283
  return next();
256
284
  })
257
285
  .catch(err => {
258
- return this._nextFunction(ctx)(err);
286
+ return this._nextError(ctx)(err);
259
287
  });
260
288
  });
261
289
  return this;
@@ -264,11 +292,11 @@ class Spear {
264
292
  * The 'useFileUpload' method is a middleware used to handler file uploads. It adds a file upload of incoming HTTP requests.
265
293
  *
266
294
  * @param {?Object}
267
- * @property {?number} limits
295
+ * @property {?number} limits // bytes. default Infinity
268
296
  * @property {?string} tempFileDir
269
297
  * @property {?Object} removeTempFile
270
298
  * @property {boolean} removeTempFile.remove
271
- * @property {number} removeTempFile.ms
299
+ * @property {number} removeTempFile.ms
272
300
  * @returns
273
301
  */
274
302
  useFileUpload({ limit, tempFileDir, removeTempFile } = {}) {
@@ -300,7 +328,7 @@ class Spear {
300
328
  return next();
301
329
  })
302
330
  .catch(err => {
303
- return this._nextFunction(ctx)(err);
331
+ return this._nextError(ctx)(err);
304
332
  });
305
333
  });
306
334
  return this;
@@ -373,7 +401,7 @@ class Spear {
373
401
  });
374
402
  return server;
375
403
  }
376
- if ('App' in this._adapter) {
404
+ if (this._adapter.kind === 'uWS') {
377
405
  const handler = () => {
378
406
  this._onListeners.forEach(listener => listener());
379
407
  if (this._swagger.use) {
@@ -469,21 +497,10 @@ class Spear {
469
497
  */
470
498
  notfound(fn) {
471
499
  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
- });
500
+ const ctx = this._createContext({ req, res, ps: {} });
501
+ return fn(ctx);
483
502
  };
484
- this._onListeners.push(() => {
485
- return this.all('*', ...this._globalMiddlewares, handler);
486
- });
503
+ this.all('*', handler);
487
504
  return this;
488
505
  }
489
506
  /**
@@ -592,7 +609,7 @@ class Spear {
592
609
  return this;
593
610
  }
594
611
  /**
595
- * 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.
596
613
  *
597
614
  * @param {string} path
598
615
  * @callback {...Function[]} handlers of the middlewares
@@ -600,21 +617,6 @@ class Spear {
600
617
  * @property {function} next - go to next function
601
618
  * @returns {this}
602
619
  */
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
620
  all(path, ...handlers) {
619
621
  this._onListeners.push(() => {
620
622
  return this._router.all(this._normalizePath(this._globalPrefix, path), this._wrapHandlers(...this._globalMiddlewares, ...handlers));
@@ -734,239 +736,23 @@ class Spear {
734
736
  }
735
737
  return;
736
738
  }
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
739
  _wrapHandlers(...handlers) {
931
740
  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
741
  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
- };
742
+ const handler = handlers[index];
743
+ if (!handler)
744
+ return;
745
+ const ctx = this._createContext({ req, res, ps });
959
746
  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));
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 Promise.resolve(handler(ctx, () => dispatch(index + 1))).catch(err => this._nextFunction(ctx)(err));
752
+ return handler(ctx, next);
967
753
  }
968
754
  catch (err) {
969
- return this._nextFunction(ctx)(err);
755
+ return this._nextError(ctx)(err);
970
756
  }
971
757
  };
972
758
  return dispatch();
@@ -976,88 +762,47 @@ class Spear {
976
762
  return (ctx, next) => {
977
763
  Promise.resolve(handler(ctx, next))
978
764
  .then(result => {
979
- if (ctx.res.writableEnded)
765
+ if (ctx.res.writableEnded) {
980
766
  return;
767
+ }
981
768
  if (result instanceof http_1.ServerResponse) {
982
- if (result?.end) {
983
- result.end();
984
- }
985
769
  return;
986
770
  }
987
- if (result == null) {
988
- if (!ctx.res.headersSent) {
989
- ctx.res.writeHead(204, { 'Content-Type': 'text/plain' });
990
- }
991
- ctx.res.end();
771
+ if (result instanceof stream_1.Stream) {
992
772
  return;
993
773
  }
994
- if (typeof result === 'string') {
995
- ctx.res.end(result ?? '');
774
+ if (result == null) {
775
+ ctx.res.noContent();
996
776
  return;
997
777
  }
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));
778
+ if (typeof result === 'string') {
779
+ ctx.res.send(result);
1011
780
  return;
1012
781
  }
1013
- if (!ctx.res.headersSent) {
1014
- ctx.res.writeHead(200, { 'Content-Type': 'application/json' });
1015
- }
1016
- ctx.res.end(JSON.stringify(result));
782
+ ctx.res.json(result);
1017
783
  return;
1018
784
  })
1019
- .catch(err => {
1020
- return this._nextFunction(ctx)(err);
1021
- });
785
+ .catch(err => next(err));
1022
786
  };
1023
787
  }
1024
- _nextFunction(ctx) {
788
+ _nextError(ctx) {
1025
789
  const NEXT_MESSAGE = "The 'next' function does not have any subsequent function.";
1026
790
  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
- }
791
+ const errorMessage = err?.message || NEXT_MESSAGE;
1049
792
  if (this._errorHandler != null) {
1050
- return this._errorHandler(new Error(NEXT_MESSAGE), ctx);
793
+ return this._errorHandler(err, ctx);
794
+ }
795
+ if (!ctx.res.headersSent) {
796
+ ctx.res.writeHead(500, const_1.HEADER_CONTENT_TYPES['json']);
1051
797
  }
1052
- ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1053
798
  if (this._formatResponse != null) {
1054
799
  ctx.res.end(JSON.stringify(this._formatResponse({
1055
- message: NEXT_MESSAGE
800
+ message: errorMessage
1056
801
  }, ctx.res.statusCode)));
1057
802
  return;
1058
803
  }
1059
804
  ctx.res.end(JSON.stringify({
1060
- message: NEXT_MESSAGE
805
+ message: errorMessage
1061
806
  }));
1062
807
  return;
1063
808
  };
@@ -1076,7 +821,7 @@ class Spear {
1076
821
  });
1077
822
  }
1078
823
  if (cluster_1.default.isWorker) {
1079
- if ('App' in this._adapter) {
824
+ if (this._adapter.kind === 'uWS') {
1080
825
  const handler = () => {
1081
826
  this._onListeners.forEach(listener => listener());
1082
827
  if (this._swagger.use) {
@@ -1114,25 +859,56 @@ class Spear {
1114
859
  const lookup = this._router.lookup.bind(this._router);
1115
860
  const cors = this._cors;
1116
861
  const adapter = this._adapter;
1117
- if ('App' in adapter) {
1118
- const server = adapter.App();
862
+ if (adapter.kind === 'uWS') {
863
+ const server = adapter.server.App();
1119
864
  server.any('/*', (uwsRes, uwsReq) => {
1120
- const { req, res } = this._uWSRequestResponse(uwsReq, uwsRes);
865
+ const { req, res } = (0, uWS_1.uWSAdaptRequestResponse)(uwsReq, uwsRes);
1121
866
  if (cors)
1122
867
  cors(req, res);
1123
868
  return lookup(req, res);
1124
869
  });
1125
- if (this._ws) {
870
+ if (this._ws?.handler) {
1126
871
  server.ws('/*', {
1127
872
  open: (ws) => {
1128
- this._ws?.connection?.(ws);
873
+ this._ws.handler?.connection?.(ws);
1129
874
  },
1130
875
  message: (ws, message) => {
1131
- this._ws?.message?.(ws, Buffer.from(message));
876
+ this._ws.handler?.message?.(ws, Buffer.from(message));
1132
877
  },
1133
878
  close: (ws, code, message) => {
1134
- this._ws?.close?.(ws, code, Buffer.from(message));
879
+ this._ws.handler?.close?.(ws, code, Buffer.from(message));
880
+ }
881
+ });
882
+ }
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
+ });
892
+ });
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);
1135
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
+ });
1136
912
  });
1137
913
  }
1138
914
  return server;
@@ -1142,160 +918,75 @@ class Spear {
1142
918
  cors(req, res);
1143
919
  return lookup(req, res);
1144
920
  });
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);
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);
1150
926
  }
1151
927
  ws.on('message', (data) => {
1152
- this._ws?.message?.(ws, data);
928
+ this._ws.handler?.message?.(ws, data);
1153
929
  });
1154
930
  ws.on('close', (code, reason) => {
1155
- if (this._ws?.close) {
1156
- this._ws.close(ws, code, reason);
931
+ if (this._ws.handler?.close) {
932
+ this._ws.handler?.close(ws, code, reason);
1157
933
  }
1158
934
  });
1159
935
  ws.on('error', (err) => {
1160
- if (this._ws?.error) {
1161
- this._ws.error(ws, err);
936
+ if (this._ws.handler?.error) {
937
+ this._ws.handler.error(ws, err);
1162
938
  }
1163
939
  });
1164
940
  });
1165
941
  }
1166
942
  return server;
1167
943
  }
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;
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'
1297
949
  });
1298
- return { req, res };
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
+ };
1299
990
  }
1300
991
  _normalizePath(...paths) {
1301
992
  const path = paths
@@ -1324,8 +1015,8 @@ class Spear {
1324
1015
  routes
1325
1016
  });
1326
1017
  this._router.get(staticUrl, staticSwaggerHandler);
1327
- this._router.get(path, (_, res) => {
1328
- res.writeHead(200, { 'Content-Type': 'text/html' });
1018
+ this._router.get(path, (req, res) => {
1019
+ res.writeHead(200, const_1.HEADER_CONTENT_TYPES['html']);
1329
1020
  res.end(html);
1330
1021
  return;
1331
1022
  });