tspace-spear 1.2.2 → 1.2.3

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.
@@ -43,7 +43,6 @@ const cluster_1 = __importDefault(require("cluster"));
43
43
  const os_1 = __importDefault(require("os"));
44
44
  const fs_1 = __importDefault(require("fs"));
45
45
  const path_1 = __importDefault(require("path"));
46
- const url_1 = require("url");
47
46
  const on_finished_1 = __importDefault(require("on-finished"));
48
47
  const ws_1 = __importDefault(require("ws"));
49
48
  const parser_factory_1 = require("./parser-factory");
@@ -69,6 +68,7 @@ class Spear {
69
68
  _globalPrefix;
70
69
  _router = (0, find_my_way_1.default)();
71
70
  _parser = new parser_factory_1.ParserFactory();
71
+ _adapter = http_1.default;
72
72
  _cluster;
73
73
  _cors;
74
74
  _swagger = {
@@ -102,11 +102,13 @@ class Spear {
102
102
  ms: 1000 * 60 * 10
103
103
  }
104
104
  };
105
- constructor({ controllers, middlewares, globalPrefix, logger, cluster } = {}) {
105
+ constructor({ controllers, middlewares, globalPrefix, logger, cluster, adapter } = {}) {
106
106
  if (logger)
107
107
  this.useLogger();
108
108
  if (cluster)
109
109
  this.useCluster(cluster);
110
+ if (adapter)
111
+ this.useAdater(adapter);
110
112
  this._controllers = controllers;
111
113
  this._middlewares = middlewares;
112
114
  this._globalPrefix = globalPrefix == null ? '' : globalPrefix;
@@ -151,6 +153,18 @@ class Spear {
151
153
  this._globalMiddlewares.push(middleware);
152
154
  return this;
153
155
  }
156
+ /**
157
+ * The 'useAdater' method is used to switch between different server implementations,
158
+ * such as the native Node.js HTTP server or uWebSockets.js (uWS).
159
+ *
160
+ * @param {T.Adapter} adapter - The adapter instance (e.g., HTTP or uWS).
161
+ * @returns {this} Returns the current instance for chaining
162
+ */
163
+ useAdater(adapter) {
164
+ this._adapter = adapter;
165
+ this._parser.useAdater(adapter);
166
+ return this;
167
+ }
154
168
  /**
155
169
  * The 'useCluster' method is used cluster run the server
156
170
  *
@@ -219,7 +233,7 @@ class Spear {
219
233
  */
220
234
  useBodyParser({ except } = {}) {
221
235
  this._globalMiddlewares.push((ctx, next) => {
222
- const { req } = ctx;
236
+ const { req, res } = ctx;
223
237
  const contentType = req?.headers['content-type'] ?? '';
224
238
  const isFileUpload = contentType && contentType.startsWith('multipart/form-data');
225
239
  const isCanParserBody = contentType.includes('application/json') ||
@@ -235,7 +249,7 @@ class Spear {
235
249
  return next();
236
250
  if (req?.body != null)
237
251
  return next();
238
- Promise.resolve(this._parser.body(req))
252
+ Promise.resolve(this._parser.body(req, res))
239
253
  .then(r => {
240
254
  req.body = r;
241
255
  return next();
@@ -268,7 +282,7 @@ class Spear {
268
282
  this._fileUploadOptions.removeTempFile = removeTempFile;
269
283
  }
270
284
  this._globalMiddlewares.push((ctx, next) => {
271
- const { req } = ctx;
285
+ const { req, res } = ctx;
272
286
  if (req.method === 'GET') {
273
287
  return next();
274
288
  }
@@ -279,7 +293,7 @@ class Spear {
279
293
  if (req?.files != null)
280
294
  return next();
281
295
  Promise
282
- .resolve(this._parser.files(req, this._fileUploadOptions))
296
+ .resolve(this._parser.files({ req, res, options: this._fileUploadOptions }))
283
297
  .then(r => {
284
298
  req.files = r.files;
285
299
  req.body = r.body;
@@ -359,6 +373,22 @@ class Spear {
359
373
  });
360
374
  return server;
361
375
  }
376
+ if ('App' in this._adapter) {
377
+ const handler = () => {
378
+ this._onListeners.forEach(listener => listener());
379
+ if (this._swagger.use) {
380
+ this._swaggerHandler();
381
+ }
382
+ callback?.({ server, port });
383
+ };
384
+ if (hostname) {
385
+ server.listen(port, String(hostname), handler);
386
+ }
387
+ else {
388
+ server.listen(port, handler);
389
+ }
390
+ return server;
391
+ }
362
392
  const args = hostname
363
393
  ? [port, hostname, () => callback?.({ server, port: port })]
364
394
  : [port, () => callback?.({ server, port: port })];
@@ -369,10 +399,6 @@ class Spear {
369
399
  this._swaggerHandler();
370
400
  }
371
401
  });
372
- server.on('error', (_) => {
373
- port = Math.floor(Math.random() * 8999) + 1000;
374
- server.listen(port);
375
- });
376
402
  return server;
377
403
  }
378
404
  /**
@@ -451,7 +477,8 @@ class Spear {
451
477
  files: {},
452
478
  body: {},
453
479
  params: {},
454
- cookies: {}
480
+ cookies: {},
481
+ ip: null
455
482
  });
456
483
  };
457
484
  this._onListeners.push(() => {
@@ -594,37 +621,6 @@ class Spear {
594
621
  });
595
622
  return this;
596
623
  }
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
624
  async _import(dir, pattern) {
629
625
  const directories = fs_1.default.readdirSync(dir, { withFileTypes: true });
630
626
  const files = (await Promise.all(directories.map((directory) => {
@@ -754,18 +750,18 @@ class Spear {
754
750
  }
755
751
  if (results == null) {
756
752
  if (this._formatResponse != null) {
757
- return res.end(JSON.stringify(this._formatResponse(null, res.statusCode), null, 2));
753
+ return res.end(JSON.stringify(this._formatResponse(null, res.statusCode)));
758
754
  }
759
755
  return res.end();
760
756
  }
761
757
  if (this._formatResponse != null) {
762
758
  return res.end(JSON.stringify(this._formatResponse({
763
759
  ...results
764
- }, res.statusCode), null, 2));
760
+ }, res.statusCode)));
765
761
  }
766
762
  return res.end(JSON.stringify({
767
763
  ...results,
768
- }, null, 2));
764
+ }));
769
765
  };
770
766
  response.send = (results) => {
771
767
  if (res.writableEnded)
@@ -822,81 +818,81 @@ class Spear {
822
818
  response.status(400);
823
819
  message = message ?? `The request '${req.url}' resulted in a bad request. Please review the data and try again.`;
824
820
  if (this._formatResponse != null) {
825
- return res.end(JSON.stringify(this._formatResponse({ message }, 400), null, 2));
821
+ return res.end(JSON.stringify(this._formatResponse({ message }, 400)));
826
822
  }
827
823
  return res.end(JSON.stringify({
828
824
  message: message
829
- }, null, 2));
825
+ }));
830
826
  };
831
827
  response.unauthorized = (message) => {
832
828
  response.status(401);
833
829
  message = message ?? `The request '${req.url}' is unauthorized. Please verify.`;
834
830
  if (this._formatResponse != null) {
835
- return res.end(JSON.stringify(this._formatResponse({ message }, 401), null, 2));
831
+ return res.end(JSON.stringify(this._formatResponse({ message }, 401)));
836
832
  }
837
833
  return res.end(JSON.stringify({
838
834
  message
839
- }, null, 2));
835
+ }));
840
836
  };
841
837
  response.paymentRequired = (message) => {
842
838
  response.status(402);
843
839
  message = message ?? `The request '${req.url}' requires payment. Please proceed with payment.`;
844
840
  if (this._formatResponse != null) {
845
- return res.end(JSON.stringify(this._formatResponse({ message }, 402), null, 2));
841
+ return res.end(JSON.stringify(this._formatResponse({ message }, 402)));
846
842
  }
847
843
  return res.end(JSON.stringify({
848
844
  message
849
- }, null, 2));
845
+ }));
850
846
  };
851
847
  response.forbidden = (message) => {
852
848
  response.status(403);
853
849
  message = message ?? `The request '${req.url}' is forbidden. Please check the permissions or access rights.`;
854
850
  if (this._formatResponse != null) {
855
- return res.end(JSON.stringify(this._formatResponse({ message }, 403), null, 2));
851
+ return res.end(JSON.stringify(this._formatResponse({ message }, 403)));
856
852
  }
857
853
  return res.end(JSON.stringify({
858
854
  message
859
- }, null, 2));
855
+ }));
860
856
  };
861
857
  response.notFound = (message) => {
862
858
  response.status(404);
863
859
  message = message ?? `The request '${req.url}' was not found. Please re-check the your url again.`;
864
860
  if (this._formatResponse != null) {
865
- return res.end(JSON.stringify(this._formatResponse({ message }, 404), null, 2));
861
+ return res.end(JSON.stringify(this._formatResponse({ message }, 404)));
866
862
  }
867
863
  return res.end(JSON.stringify({
868
864
  message
869
- }, null, 2));
865
+ }));
870
866
  };
871
867
  response.unprocessable = (message) => {
872
868
  response.status(422);
873
869
  message = message ?? `The request to '${req.url}' failed validation.`;
874
870
  if (this._formatResponse != null) {
875
- return res.end(JSON.stringify(this._formatResponse({ message }, 422), null, 2));
871
+ return res.end(JSON.stringify(this._formatResponse({ message }, 422)));
876
872
  }
877
873
  return res.end(JSON.stringify({
878
874
  message
879
- }, null, 2));
875
+ }));
880
876
  };
881
877
  response.tooManyRequests = (message) => {
882
878
  response.status(429);
883
879
  message = message ?? `The request '${req.url}' is too many request. Please wait and try agian.`;
884
880
  if (this._formatResponse != null) {
885
- return res.end(JSON.stringify(this._formatResponse({ message }, 429), null, 2));
881
+ return res.end(JSON.stringify(this._formatResponse({ message }, 429)));
886
882
  }
887
883
  return res.end(JSON.stringify({
888
884
  message
889
- }, null, 2));
885
+ }));
890
886
  };
891
887
  response.serverError = (message) => {
892
888
  response.status(500);
893
889
  message = message ?? `The request '${req.url}' resulted in a server error. Please investigate.`;
894
890
  if (this._formatResponse != null) {
895
- return res.end(JSON.stringify(this._formatResponse({ message }, 500), null, 2));
891
+ return res.end(JSON.stringify(this._formatResponse({ message }, 500)));
896
892
  }
897
893
  return res.end(JSON.stringify({
898
894
  message
899
- }, null, 2));
895
+ }));
900
896
  };
901
897
  response.status = (code) => {
902
898
  res.writeHead(code, { 'Content-Type': 'application/json' });
@@ -932,58 +928,48 @@ class Spear {
932
928
  return response;
933
929
  }
934
930
  _wrapHandlers(...handlers) {
935
- return (req, res, params) => {
936
- const nextHandler = (index = 0) => {
931
+ 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
+ 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
+ };
937
959
  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
- };
960
+ const handler = handlers[index];
961
+ if (!handler)
962
+ return;
962
963
  if (index === handlers.length - 1) {
963
- return this._wrapResponse(handlers[index]
964
- .bind(handlers[index]))(ctx, this._nextFunction(ctx));
964
+ return Promise.resolve(this._wrapResponse(handler)(ctx, this._nextFunction(ctx))).catch(err => this._nextFunction(ctx)(err));
965
965
  }
966
- return handlers[index](ctx, () => {
967
- return nextHandler(index + 1);
968
- });
966
+ return Promise.resolve(handler(ctx, () => dispatch(index + 1))).catch(err => this._nextFunction(ctx)(err));
969
967
  }
970
968
  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
969
  return this._nextFunction(ctx)(err);
982
970
  }
983
971
  };
984
- if (res.writableEnded)
985
- return;
986
- return nextHandler();
972
+ return dispatch();
987
973
  };
988
974
  }
989
975
  _wrapResponse(handler) {
@@ -1006,9 +992,6 @@ class Spear {
1006
992
  return;
1007
993
  }
1008
994
  if (typeof result === 'string') {
1009
- if (!ctx.res.headersSent) {
1010
- ctx.res.writeHead(200, { 'Content-Type': 'text/plain' });
1011
- }
1012
995
  ctx.res.end(result ?? '');
1013
996
  return;
1014
997
  }
@@ -1048,7 +1031,7 @@ class Spear {
1048
1031
  ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1049
1032
  return ctx.res.end(JSON.stringify({
1050
1033
  message: err?.message
1051
- }, null, 2));
1034
+ }));
1052
1035
  }
1053
1036
  return callback;
1054
1037
  }
@@ -1056,39 +1039,109 @@ class Spear {
1056
1039
  if (this._formatResponse != null) {
1057
1040
  return ctx.res.end(JSON.stringify(this._formatResponse({
1058
1041
  message: err?.message,
1059
- }, ctx.res.statusCode), null, 2));
1042
+ }, ctx.res.statusCode)));
1060
1043
  }
1061
- return ctx.res.end(JSON.stringify({
1044
+ ctx.res.end(JSON.stringify({
1062
1045
  message: err?.message
1063
- }, null, 2));
1046
+ }));
1047
+ return;
1064
1048
  }
1065
1049
  if (this._errorHandler != null) {
1066
1050
  return this._errorHandler(new Error(NEXT_MESSAGE), ctx);
1067
1051
  }
1068
1052
  ctx.res.writeHead(500, { 'Content-Type': 'application/json' });
1069
1053
  if (this._formatResponse != null) {
1070
- return ctx.res.end(JSON.stringify(this._formatResponse({
1054
+ ctx.res.end(JSON.stringify(this._formatResponse({
1071
1055
  message: NEXT_MESSAGE
1072
- }, ctx.res.statusCode), null, 2));
1056
+ }, ctx.res.statusCode)));
1057
+ return;
1073
1058
  }
1074
- return ctx.res.end(JSON.stringify({
1059
+ ctx.res.end(JSON.stringify({
1075
1060
  message: NEXT_MESSAGE
1076
- }, null, 2));
1061
+ }));
1062
+ return;
1077
1063
  };
1078
1064
  }
1065
+ _clusterMode({ server, port, hostname, callback }) {
1066
+ if (cluster_1.default.isPrimary) {
1067
+ const numCPUs = os_1.default.cpus().length;
1068
+ const maxWorkers = typeof this._cluster === 'boolean' || this._cluster == null
1069
+ ? numCPUs
1070
+ : this._cluster;
1071
+ for (let i = 0; i < maxWorkers; i++) {
1072
+ cluster_1.default.fork();
1073
+ }
1074
+ cluster_1.default.on('exit', () => {
1075
+ cluster_1.default.fork();
1076
+ });
1077
+ }
1078
+ if (cluster_1.default.isWorker) {
1079
+ if ('App' in this._adapter) {
1080
+ const handler = () => {
1081
+ this._onListeners.forEach(listener => listener());
1082
+ if (this._swagger.use) {
1083
+ this._swaggerHandler();
1084
+ }
1085
+ callback?.({ server, port });
1086
+ };
1087
+ if (hostname) {
1088
+ server.listen(port, hostname, handler);
1089
+ return server;
1090
+ }
1091
+ server.listen(port, handler);
1092
+ return server;
1093
+ }
1094
+ const args = hostname
1095
+ ? [port, hostname, () => callback?.({ server, port: port })]
1096
+ : [port, () => callback?.({ server, port: port })];
1097
+ server.listen(...args);
1098
+ server.on('listening', () => {
1099
+ this._onListeners.forEach(listener => listener());
1100
+ if (this._swagger.use) {
1101
+ this._swaggerHandler();
1102
+ }
1103
+ });
1104
+ server.on('error', (_) => {
1105
+ port = Math.floor(Math.random() * 8999) + 1000;
1106
+ server.listen(port);
1107
+ });
1108
+ }
1109
+ return;
1110
+ }
1079
1111
  async _createServer() {
1080
1112
  await this._registerMiddlewares();
1081
1113
  await this._registerControllers();
1082
1114
  const lookup = this._router.lookup.bind(this._router);
1083
1115
  const cors = this._cors;
1084
- const server = http_1.default.createServer({ maxHeaderSize: 1024 * 1024 }, cors
1085
- ? (req, res) => {
1086
- cors(req, res);
1087
- return lookup(req, res);
1088
- }
1089
- : (req, res) => {
1116
+ const adapter = this._adapter;
1117
+ if ('App' in adapter) {
1118
+ const server = adapter.App();
1119
+ server.any('/*', (uwsRes, uwsReq) => {
1120
+ const { req, res } = this._uWSRequestResponse(uwsReq, uwsRes);
1121
+ if (cors)
1122
+ cors(req, res);
1090
1123
  return lookup(req, res);
1091
1124
  });
1125
+ if (this._ws) {
1126
+ server.ws('/*', {
1127
+ open: (ws) => {
1128
+ this._ws?.connection?.(ws);
1129
+ },
1130
+ message: (ws, message) => {
1131
+ this._ws?.message?.(ws, Buffer.from(message));
1132
+ },
1133
+ close: (ws, code, message) => {
1134
+ this._ws?.close?.(ws, code, Buffer.from(message));
1135
+ }
1136
+ });
1137
+ }
1138
+ return server;
1139
+ }
1140
+ const server = http_1.default.createServer((req, res) => {
1141
+ if (cors)
1142
+ cors(req, res);
1143
+ return lookup(req, res);
1144
+ });
1092
1145
  if (this._ws) {
1093
1146
  this._wss = new ws_1.default.Server({ server, ...this._wsOptions });
1094
1147
  this._wss.on('connection', (ws) => {
@@ -1096,9 +1149,7 @@ class Spear {
1096
1149
  this._ws.connection(ws);
1097
1150
  }
1098
1151
  ws.on('message', (data) => {
1099
- if (this._ws?.message) {
1100
- this._ws.message(ws, data);
1101
- }
1152
+ this._ws?.message?.(ws, data);
1102
1153
  });
1103
1154
  ws.on('close', (code, reason) => {
1104
1155
  if (this._ws?.close) {
@@ -1114,6 +1165,138 @@ class Spear {
1114
1165
  }
1115
1166
  return server;
1116
1167
  }
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;
1297
+ });
1298
+ return { req, res };
1299
+ }
1117
1300
  _normalizePath(...paths) {
1118
1301
  const path = paths
1119
1302
  .join('/')
@@ -1143,8 +1326,8 @@ class Spear {
1143
1326
  this._router.get(staticUrl, staticSwaggerHandler);
1144
1327
  this._router.get(path, (_, res) => {
1145
1328
  res.writeHead(200, { 'Content-Type': 'text/html' });
1146
- res.write(html);
1147
- return res.end();
1329
+ res.end(html);
1330
+ return;
1148
1331
  });
1149
1332
  return;
1150
1333
  }