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
@@ -4,55 +4,41 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ParserFactory = void 0;
7
+ const busboy_1 = __importDefault(require("busboy"));
7
8
  const fs_1 = __importDefault(require("fs"));
8
9
  const path_1 = __importDefault(require("path"));
9
10
  const mime_types_1 = __importDefault(require("mime-types"));
10
11
  const crypto_1 = __importDefault(require("crypto"));
11
12
  const swagger_ui_dist_1 = __importDefault(require("swagger-ui-dist"));
12
- const querystring_1 = __importDefault(require("querystring"));
13
13
  const string_decoder_1 = require("string_decoder");
14
- const busboy_1 = __importDefault(require("busboy"));
14
+ const fast_querystring_1 = __importDefault(require("fast-querystring"));
15
+ const utils_1 = require("../utils");
16
+ const uWS_1 = require("./uWS");
17
+ const net_1 = require("./net");
15
18
  class ParserFactory {
16
- isUWS = false;
19
+ uWebStockets = false;
20
+ net = false;
17
21
  useAdater(adapter) {
18
- if ('App' in adapter) {
19
- this.isUWS = true;
22
+ if (adapter.kind === 'uWS') {
23
+ this.uWebStockets = true;
24
+ }
25
+ if (adapter.kind === 'net') {
26
+ this.net = true;
20
27
  }
28
+ return this;
21
29
  }
22
30
  queryString(url) {
23
- const query = {};
24
- let i = url.indexOf('?');
31
+ const i = url.indexOf("?");
25
32
  if (i === -1)
26
- return query;
27
- i++;
28
- let key = '';
29
- let value = '';
30
- let mode = 0;
31
- for (; i < url.length; i++) {
32
- const c = url[i];
33
- if (c === '#')
34
- break;
35
- if (c === '=') {
36
- mode = 1;
37
- }
38
- else if (c === '&') {
39
- if (key)
40
- query[key] = value;
41
- key = '';
42
- value = '';
43
- mode = 0;
44
- }
45
- else {
46
- mode ? (value += c) : (key += c);
47
- }
48
- }
49
- if (key)
50
- query[key] = value;
51
- return query;
33
+ return {};
34
+ return fast_querystring_1.default.parse(url.slice(i + 1));
52
35
  }
53
- async files({ req, res, options }) {
54
- if (this.isUWS) {
55
- return this.uWSfiles({ req, res, options });
36
+ async files({ req, res, options, }) {
37
+ if (this.uWebStockets) {
38
+ return (0, uWS_1.uWSfiles)({ req, res, options });
39
+ }
40
+ if (this.net) {
41
+ return (0, net_1.netFiles)(req, options);
56
42
  }
57
43
  const temp = options.tempFileDir;
58
44
  if (!fs_1.default.existsSync(temp)) {
@@ -65,7 +51,7 @@ class ParserFactory {
65
51
  const body = {};
66
52
  const files = {};
67
53
  const fileWritePromises = [];
68
- const bb = (0, busboy_1.default)({ headers: req.headers, defParamCharset: 'utf8' });
54
+ const bb = (0, busboy_1.default)({ headers: req.headers, defParamCharset: "utf8" });
69
55
  const removeTemp = (fileTemp, ms) => {
70
56
  const remove = () => {
71
57
  try {
@@ -75,16 +61,16 @@ class ParserFactory {
75
61
  };
76
62
  setTimeout(remove, ms);
77
63
  };
78
- bb.on('file', (fieldName, fileData, info) => {
64
+ bb.on("file", (fieldName, fileData, info) => {
79
65
  const { filename, mimeType } = info;
80
- const extension = mime_types_1.default.extension(mimeType)
81
- || path_1.default.extname(filename).replace('.', '')
82
- || 'bin';
83
- const tempFilename = crypto_1.default.randomBytes(16).toString('hex');
66
+ const extension = mime_types_1.default.extension(mimeType) ||
67
+ path_1.default.extname(filename).replace(".", "") ||
68
+ "bin";
69
+ const tempFilename = crypto_1.default.randomBytes(16).toString("hex");
84
70
  const filePath = path_1.default.join(path_1.default.resolve(), `${temp}/${tempFilename}`);
85
71
  const writeStream = fs_1.default.createWriteStream(filePath);
86
72
  let fileSize = 0;
87
- fileData.on('data', (data) => {
73
+ fileData.on("data", (data) => {
88
74
  fileSize += data.length;
89
75
  if (fileSize > options.limit) {
90
76
  fileData.unpipe(writeStream);
@@ -94,7 +80,7 @@ class ParserFactory {
94
80
  });
95
81
  const fileWritePromise = new Promise((resolve, reject) => {
96
82
  fileData.pipe(writeStream);
97
- writeStream.on('finish', () => {
83
+ writeStream.on("finish", () => {
98
84
  const file = {
99
85
  name: filename,
100
86
  tempFilePath: filePath,
@@ -106,26 +92,27 @@ class ParserFactory {
106
92
  bytes: fileSize,
107
93
  kb: fileSize / 1024,
108
94
  mb: fileSize / 1024 / 1024,
109
- gb: fileSize / 1024 / 1024 / 1024
95
+ gb: fileSize / 1024 / 1024 / 1024,
110
96
  },
111
97
  write: (to) => {
112
98
  return new Promise((resolve, reject) => {
113
- fs_1.default.createReadStream(filePath)
99
+ fs_1.default
100
+ .createReadStream(filePath)
114
101
  .pipe(fs_1.default.createWriteStream(to))
115
- .on('finish', () => {
102
+ .on("finish", () => {
116
103
  return resolve(null);
117
104
  })
118
- .on('error', (err) => {
105
+ .on("error", (err) => {
119
106
  return reject(err);
120
107
  });
121
108
  });
122
109
  },
123
110
  remove: () => {
124
- return new Promise(resolve => setTimeout(() => {
111
+ return new Promise((resolve) => setTimeout(() => {
125
112
  fs_1.default.unlinkSync(filePath);
126
113
  return resolve(null);
127
114
  }, 100));
128
- }
115
+ },
129
116
  };
130
117
  if (files[fieldName] == null) {
131
118
  files[fieldName] = [];
@@ -136,55 +123,56 @@ class ParserFactory {
136
123
  }
137
124
  return resolve(null);
138
125
  });
139
- writeStream.on('error', reject);
126
+ writeStream.on("error", reject);
140
127
  });
141
128
  fileWritePromises.push(fileWritePromise);
142
129
  });
143
- bb.on('field', (name, value) => {
130
+ bb.on("field", (name, value) => {
144
131
  body[name] = value;
145
132
  });
146
- bb.on('finish', () => {
133
+ bb.on("finish", () => {
147
134
  Promise.all(fileWritePromises)
148
135
  .then(() => {
149
136
  return resolve({
150
137
  files,
151
- body
138
+ body,
152
139
  });
153
140
  })
154
141
  .catch((err) => {
155
142
  return reject(err);
156
143
  });
157
144
  });
158
- bb.on('error', (err) => {
145
+ bb.on("error", (err) => {
159
146
  return reject(err);
160
147
  });
161
148
  req.pipe(bb);
162
149
  });
163
150
  }
164
151
  async body(req, res) {
165
- if (this.isUWS) {
166
- return await this.uWSBody(res);
152
+ if (this.uWebStockets) {
153
+ return (await (0, uWS_1.uWSBody)(req, res));
154
+ }
155
+ if (this.net) {
156
+ return (await (0, net_1.netBody)(req));
167
157
  }
168
158
  return new Promise((resolve, reject) => {
169
- const decoder = new string_decoder_1.StringDecoder('utf-8');
170
- let payload = '';
171
- req.on('data', (data) => {
159
+ const decoder = new string_decoder_1.StringDecoder("utf-8");
160
+ let payload = "";
161
+ req.on("data", (data) => {
172
162
  payload += decoder.write(data);
173
163
  });
174
- req.on('end', () => {
164
+ req.on("end", async () => {
165
+ payload += decoder.end();
166
+ const contentType = req.headers["content-type"]?.toLowerCase() || null;
175
167
  try {
176
- const isUrlEncoded = req.headers['content-type']?.includes('x-www-form-urlencoded');
177
- if (isUrlEncoded) {
178
- return resolve(querystring_1.default.parse(payload));
179
- }
180
- payload += decoder.end();
181
- return resolve(JSON.parse(payload));
168
+ const body = await (0, utils_1.normalizeRequestBody)({ contentType, payload });
169
+ return resolve(body);
182
170
  }
183
- catch (e) {
184
- return resolve({});
171
+ catch (err) {
172
+ return reject(err);
185
173
  }
186
174
  });
187
- req.on('error', (err) => {
175
+ req.on("error", (err) => {
188
176
  return reject(err);
189
177
  });
190
178
  });
@@ -194,15 +182,15 @@ class ParserFactory {
194
182
  const cookieString = req.headers?.cookie;
195
183
  if (cookieString == null)
196
184
  return null;
197
- for (const cookie of cookieString.split(';')) {
198
- const [name, value] = cookie.split('=').map((v) => v.trim());
185
+ for (const cookie of cookieString.split(";")) {
186
+ const [name, value] = cookie.split("=").map((v) => v.trim());
199
187
  cookies[name] = decodeURIComponent(value);
200
188
  }
201
189
  for (const name of Object.keys(cookies)) {
202
190
  const cookie = cookies[name];
203
- if (!cookie.startsWith('Expires='))
191
+ if (!cookie.startsWith("Expires="))
204
192
  continue;
205
- const expiresString = cookie.replace('Expires=', '');
193
+ const expiresString = cookie.replace("Expires=", "");
206
194
  const expiresDate = new Date(expiresString);
207
195
  if (isNaN(expiresDate.getTime()) || expiresDate < new Date()) {
208
196
  delete cookies[name];
@@ -214,9 +202,9 @@ class ParserFactory {
214
202
  const spec = {
215
203
  openapi: "3.1.0",
216
204
  info: doc.info ?? {
217
- title: 'API Documentation',
205
+ title: "API Documentation",
218
206
  description: "Documentation",
219
- version: '1.0.0'
207
+ version: "1.0.0",
220
208
  },
221
209
  components: {
222
210
  securitySchemes: {
@@ -224,14 +212,14 @@ class ParserFactory {
224
212
  type: "http",
225
213
  scheme: "bearer",
226
214
  name: "Authorization",
227
- description: "Enter your token in the format : 'Bearer {TOKEN}'"
215
+ description: "Enter your token in the format : 'Bearer {TOKEN}'",
228
216
  },
229
217
  cookies: {
230
218
  type: "apiKey",
231
219
  in: "header",
232
220
  name: "Cookie",
233
- description: "Enter your cookies in the headers"
234
- }
221
+ description: "Enter your cookies in the headers",
222
+ },
235
223
  },
236
224
  },
237
225
  servers: doc.servers,
@@ -246,25 +234,24 @@ class ParserFactory {
246
234
  content: {
247
235
  "application/json": {
248
236
  schema: {
249
- type: 'object',
237
+ type: "object",
250
238
  properties: {
251
239
  message: {
252
- example: "success"
253
- }
254
- }
255
- }
256
- }
257
- }
258
- }
240
+ example: "success",
241
+ },
242
+ },
243
+ },
244
+ },
245
+ },
246
+ },
259
247
  };
260
248
  for (const r of routes) {
261
- if (r.path === '*')
249
+ if (r.path === "*")
262
250
  continue;
263
251
  const path = r.path.replace(/:(\w+)/g, "{$1}");
264
- const method = r.method.toLocaleLowerCase();
265
- const swagger = (doc.specs ?? []).find(s => {
266
- return s.path === r.path &&
267
- s.method.toLocaleLowerCase() === method;
252
+ const method = r.method.toLowerCase();
253
+ const swagger = (doc.specs ?? []).find((s) => {
254
+ return s.path === r.path && s.method.toLowerCase() === method;
268
255
  });
269
256
  const decoratedOnly = doc.options?.decoratedOnly ?? false;
270
257
  if ((swagger == null && decoratedOnly) || swagger?.disabled) {
@@ -272,15 +259,15 @@ class ParserFactory {
272
259
  }
273
260
  if (paths[path] == null) {
274
261
  paths[path] = {
275
- [method]: {}
262
+ [method]: {},
276
263
  };
277
264
  }
278
265
  const spec = {};
279
266
  const tags = /\/api\/v\d+/.test(r.path)
280
- ? r.path.split('/')[3]
267
+ ? r.path.split("/")[3]
281
268
  : /\/api/.test(r.path)
282
- ? r.path.split('/')[2]
283
- : r.path.split('/')[1];
269
+ ? r.path.split("/")[2]
270
+ : r.path.split("/")[1];
284
271
  spec.parameters = [];
285
272
  spec.responses = {};
286
273
  spec.tags = [];
@@ -294,135 +281,42 @@ class ParserFactory {
294
281
  content: {
295
282
  "application/json": {
296
283
  schema: {
297
- type: 'object',
284
+ type: "object",
298
285
  properties: response.example == null
299
286
  ? {}
300
- : Object.keys(response.example)
301
- .reduce((prev, key) => {
302
- prev[key] = { example: (response?.example ?? {})[key] ?? {} };
287
+ : Object.keys(response.example).reduce((prev, key) => {
288
+ prev[key] = {
289
+ example: (response?.example ?? {})[key] ?? {},
290
+ };
303
291
  return prev;
304
- }, {})
305
- }
306
- }
307
- }
292
+ }, {}),
293
+ },
294
+ },
295
+ },
308
296
  };
309
297
  }
310
298
  spec.responses = {
311
- ...responses
299
+ ...responses,
312
300
  };
313
301
  }
314
- if (swagger != null) {
302
+ if (swagger == null) {
315
303
  spec.tags = [
316
- swagger.tags == null
317
- ? tags == null || tags === '' || /^:[^:]*$/.test(tags) ? 'default' : tags
318
- : swagger.tags
304
+ tags == null || tags === "" || /^:[^:]*$/.test(tags)
305
+ ? "default"
306
+ : tags,
319
307
  ];
320
- if (swagger.bearerToken) {
321
- spec.security = [{ "BearerToken": [] }];
322
- }
323
- if (swagger.summary != null) {
324
- spec.summary = swagger.summary;
325
- }
326
- if (swagger.description != null) {
327
- spec.description = swagger.description;
328
- }
329
308
  if (Array.isArray(r.params) && Array.from(r.params).length) {
330
- spec.parameters = Array.from(r?.params).map(p => {
309
+ spec.parameters = Array.from(r.params).map((p) => {
331
310
  return {
332
311
  name: p,
333
312
  in: "path",
334
313
  required: true,
335
314
  schema: {
336
- type: "string"
337
- }
315
+ type: "string",
316
+ },
338
317
  };
339
318
  });
340
319
  }
341
- if (swagger.query != null) {
342
- spec.parameters = [
343
- ...spec.parameters,
344
- ...Object.entries(swagger.query).map(([k, v]) => {
345
- return {
346
- name: k,
347
- in: "query",
348
- required: v.required == null ? false : true,
349
- schema: {
350
- type: v.type
351
- }
352
- };
353
- })
354
- ];
355
- }
356
- if (swagger.cookies != null) {
357
- spec.parameters = [
358
- ...spec.parameters,
359
- ...[{
360
- name: "Cookie",
361
- in: "header",
362
- required: swagger.cookies.required == null ? false : true,
363
- schema: {
364
- type: "string"
365
- },
366
- example: swagger.cookies.names.map((v, i) => `${v}={value${i + 1}}`).join(' ; '),
367
- description: swagger.cookies?.description
368
- }]
369
- ];
370
- }
371
- if (swagger.body != null) {
372
- spec.requestBody = {
373
- description: swagger.body?.description == null ? "description" : swagger.body.description,
374
- required: swagger.body?.required == null ? false : true,
375
- content: {
376
- "application/json": {
377
- schema: {
378
- type: "object",
379
- properties: swagger.body.properties
380
- }
381
- }
382
- }
383
- };
384
- }
385
- if (swagger.files != null) {
386
- spec.requestBody = {
387
- description: swagger.files?.description == null ? "description" : swagger.files.description,
388
- required: swagger.files?.required ?? false,
389
- content: {
390
- "multipart/form-data": {
391
- schema: {
392
- type: "object",
393
- properties: swagger.files.properties
394
- }
395
- }
396
- }
397
- };
398
- }
399
- if (swagger.responses != null) {
400
- const responses = {};
401
- for (const response of swagger.responses) {
402
- if (response == null || !Object.keys(response).length)
403
- continue;
404
- responses[`${response.status}`] = {
405
- description: response.description,
406
- content: {
407
- "application/json": {
408
- schema: {
409
- type: 'object',
410
- properties: response.example == null
411
- ? {}
412
- : Object.keys(response.example)
413
- .reduce((prev, key) => {
414
- prev[key] = { example: (response?.example ?? {})[key] ?? {} };
415
- return prev;
416
- }, {})
417
- }
418
- }
419
- }
420
- };
421
- }
422
- spec.responses = {
423
- ...responses
424
- };
425
- }
426
320
  if (!Object.keys(spec.responses).length) {
427
321
  spec.responses = defaultSpecResponse;
428
322
  }
@@ -430,19 +324,146 @@ class ParserFactory {
430
324
  continue;
431
325
  }
432
326
  spec.tags = [
433
- tags == null || tags === '' || /^:[^:]*$/.test(tags) ? 'default' : tags
327
+ swagger.tags == null
328
+ ? tags == null || tags === "" || /^:[^:]*$/.test(tags)
329
+ ? "default"
330
+ : tags
331
+ : swagger.tags,
434
332
  ];
435
- if (Array.isArray(r.params) && Array.from(r.params).length) {
436
- spec.parameters = Array.from(r.params).map(p => {
333
+ if (swagger.bearerToken) {
334
+ spec.security = [{ BearerToken: [] }];
335
+ }
336
+ if (swagger.summary != null) {
337
+ spec.summary = swagger.summary;
338
+ }
339
+ if (swagger.description != null) {
340
+ spec.description = swagger.description;
341
+ }
342
+ if (swagger.params != null) {
343
+ const params = Object.entries(swagger.params).map(([k, v]) => {
344
+ return {
345
+ name: k,
346
+ in: "path",
347
+ required: v?.required === true,
348
+ description: v?.description,
349
+ example: v?.example || v?.enum,
350
+ schema: {
351
+ type: v?.type ?? "string",
352
+ }
353
+ };
354
+ });
355
+ spec.parameters = [
356
+ ...(spec.parameters ?? []),
357
+ ...params,
358
+ ];
359
+ }
360
+ else if (Array.isArray(r.params) && Array.from(r.params).length) {
361
+ spec.parameters = Array.from(r?.params).map((p) => {
437
362
  return {
438
363
  name: p,
439
364
  in: "path",
440
365
  required: true,
441
366
  schema: {
442
- type: "string"
367
+ type: "string",
368
+ },
369
+ };
370
+ });
371
+ }
372
+ if (swagger.query != null) {
373
+ const queryParams = Object.entries(swagger.query).map(([k, v]) => {
374
+ return {
375
+ name: k,
376
+ in: "query",
377
+ required: v?.required === true,
378
+ description: v?.description,
379
+ example: v?.example || v?.enum,
380
+ schema: {
381
+ type: v?.type ?? "string",
443
382
  }
444
383
  };
445
384
  });
385
+ spec.parameters = [
386
+ ...(spec.parameters ?? []),
387
+ ...queryParams,
388
+ ];
389
+ }
390
+ if (swagger.cookies != null) {
391
+ spec.parameters = [
392
+ ...spec.parameters,
393
+ ...[
394
+ {
395
+ name: "Cookie",
396
+ in: "header",
397
+ required: swagger.cookies.required === true,
398
+ schema: {
399
+ type: "string",
400
+ },
401
+ example: swagger.cookies.names
402
+ .map((v, i) => `${v}={value${i + 1}}`)
403
+ .join(" ; "),
404
+ description: swagger.cookies?.description,
405
+ },
406
+ ],
407
+ ];
408
+ }
409
+ if (swagger.body != null) {
410
+ spec.requestBody = {
411
+ description: swagger.body.description,
412
+ required: swagger.body?.required === true,
413
+ content: {
414
+ "application/json": {
415
+ schema: {
416
+ type: "object",
417
+ properties: swagger.body.properties,
418
+ required: Object.entries(swagger.body.properties)
419
+ .filter(([_, v]) => v.required)
420
+ .map(([key]) => key)
421
+ },
422
+ },
423
+ },
424
+ };
425
+ }
426
+ if (swagger.files != null) {
427
+ spec.requestBody = {
428
+ description: swagger.files.description,
429
+ required: swagger.files?.required === true,
430
+ content: {
431
+ "multipart/form-data": {
432
+ schema: {
433
+ type: "object",
434
+ properties: swagger.files.properties,
435
+ },
436
+ },
437
+ },
438
+ };
439
+ }
440
+ if (swagger.responses != null) {
441
+ const responses = {};
442
+ for (const response of swagger.responses) {
443
+ if (response == null || !Object.keys(response).length)
444
+ continue;
445
+ responses[`${response.status}`] = {
446
+ description: response.description,
447
+ content: {
448
+ "application/json": {
449
+ schema: {
450
+ type: "object",
451
+ properties: response.example == null
452
+ ? {}
453
+ : Object.keys(response.example).reduce((prev, key) => {
454
+ prev[key] = {
455
+ example: (response?.example ?? {})[key] ?? {},
456
+ };
457
+ return prev;
458
+ }, {}),
459
+ },
460
+ },
461
+ },
462
+ };
463
+ }
464
+ spec.responses = {
465
+ ...responses,
466
+ };
446
467
  }
447
468
  if (!Object.keys(spec.responses).length) {
448
469
  spec.responses = defaultSpecResponse;
@@ -453,20 +474,17 @@ class ParserFactory {
453
474
  };
454
475
  spec.paths = specPaths(doc.routes ?? []);
455
476
  const normalizePath = (...paths) => {
456
- const path = paths
457
- .join('/')
458
- .replace(/\/+/g, '/')
459
- .replace(/\/+$/, '');
460
- const normalizedPath = path.startsWith('/') ? path : `/${path}`;
477
+ const path = paths.join("/").replace(/\/+/g, "/").replace(/\/+$/, "");
478
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
461
479
  return /\/api\/api/.test(normalizedPath)
462
480
  ? normalizedPath.replace(/\/api\/api\//, "/api/")
463
481
  : normalizedPath;
464
482
  };
465
- const STATIC_URL = '/swagger-ui';
466
- const iconURL = normalizePath(doc.staticUrl ?? '', `${STATIC_URL}/favicon-32x32.png`).replace(/^\/(http[s]?:\/{0,2})/, '$1');
467
- const cssURL = normalizePath(doc.staticUrl ?? '', `${STATIC_URL}/swagger-ui.css`).replace(/^\/(http[s]?:\/{0,2})/, '$1');
468
- const scriptBundle = normalizePath(doc.staticUrl ?? '', `${STATIC_URL}/swagger-ui-bundle.js`).replace(/^\/(http[s]?:\/{0,2})/, '$1');
469
- const scriptStandalonePreset = normalizePath(doc.staticUrl ?? '', `${STATIC_URL}/swagger-ui-standalone-preset.js`).replace(/^\/(http[s]?:\/{0,2})/, '$1');
483
+ const STATIC_URL = "/swagger-ui";
484
+ const iconURL = normalizePath(doc.staticUrl ?? "", `${STATIC_URL}/favicon-32x32.png`).replace(/^\/(http[s]?:\/{0,2})/, "$1");
485
+ const cssURL = normalizePath(doc.staticUrl ?? "", `${STATIC_URL}/swagger-ui.css`).replace(/^\/(http[s]?:\/{0,2})/, "$1");
486
+ const scriptBundle = normalizePath(doc.staticUrl ?? "", `${STATIC_URL}/swagger-ui-bundle.js`).replace(/^\/(http[s]?:\/{0,2})/, "$1");
487
+ const scriptStandalonePreset = normalizePath(doc.staticUrl ?? "", `${STATIC_URL}/swagger-ui-standalone-preset.js`).replace(/^\/(http[s]?:\/{0,2})/, "$1");
470
488
  const html = `
471
489
  <!DOCTYPE html>
472
490
  <html lang="en">
@@ -494,7 +512,7 @@ class ParserFactory {
494
512
  presets: [SwaggerUIBundle.presets.apis, SwaggerUIStandalonePreset],
495
513
  spec : ${JSON.stringify(spec)},
496
514
  withCredentials: ${doc.options?.withCredentials ?? "true"},
497
- layout: "${doc.options?.layout ?? 'StandaloneLayout'}",
515
+ layout: "${doc.options?.layout ?? "StandaloneLayout"}",
498
516
  filter: "${doc.options?.filter ?? "false"}",
499
517
  docExpansion: "${doc.options?.docExpansion ?? "list"}",
500
518
  deepLinking: "${doc.options?.deepLinking ?? "true"}",
@@ -506,233 +524,37 @@ class ParserFactory {
506
524
  </html>
507
525
  `;
508
526
  const staticSwaggerHandler = (req, res, params) => {
509
- const swaggerUiPath = swagger_ui_dist_1.default.getAbsoluteFSPath();
510
- const mimeTypes = {
511
- '.html': 'text/html',
512
- '.css': 'text/css',
513
- '.js': 'application/javascript',
514
- '.png': 'image/png',
515
- '.jpg': 'image/jpeg',
516
- '.gif': 'image/gif',
517
- '.svg': 'image/svg+xml',
518
- '.json': 'application/json'
519
- };
520
- const requestedFilePath = params['*'];
521
- const filePath = path_1.default.join(swaggerUiPath, requestedFilePath);
522
- const extname = path_1.default.extname(filePath);
523
- const contentType = mimeTypes[extname] || 'text/html';
524
527
  try {
528
+ const swaggerUiPath = swagger_ui_dist_1.default.getAbsoluteFSPath();
529
+ const mimeTypes = {
530
+ ".html": "text/html",
531
+ ".css": "text/css",
532
+ ".js": "application/javascript",
533
+ ".png": "image/png",
534
+ ".jpg": "image/jpeg",
535
+ ".gif": "image/gif",
536
+ ".svg": "image/svg+xml",
537
+ ".json": "application/json",
538
+ };
539
+ const requestedFilePath = params["*"];
540
+ const filePath = path_1.default.join(swaggerUiPath, requestedFilePath);
541
+ const extname = path_1.default.extname(filePath);
542
+ const contentType = mimeTypes[extname] || "text/html";
525
543
  const content = fs_1.default.readFileSync(filePath);
526
- res.writeHead(200, { 'Content-Type': contentType });
527
- return res.end(content, 'utf-8');
544
+ res.writeHead(200, { "Content-Type": contentType });
545
+ return res.end(content, "utf-8");
528
546
  }
529
547
  catch (err) {
530
- res.writeHead(404, { 'Content-Type': contentType });
531
- return res.end(`The file '${requestedFilePath}' is not exists`);
548
+ res.writeHead(404, { "Content-Type": "text/plain" });
549
+ return res.end("Not found");
532
550
  }
533
551
  };
534
552
  return {
535
553
  path: doc.path,
536
554
  staticUrl: `${STATIC_URL}/*`,
537
555
  staticSwaggerHandler,
538
- html
539
- };
540
- }
541
- async uWSfiles({ req, res, options }) {
542
- const temp = options.tempFileDir;
543
- if (!fs_1.default.existsSync(temp)) {
544
- try {
545
- fs_1.default.mkdirSync(temp, { recursive: true });
546
- }
547
- catch { }
548
- }
549
- const removeTemp = (fileTemp, ms) => {
550
- const remove = () => {
551
- try {
552
- fs_1.default.unlinkSync(fileTemp);
553
- }
554
- catch (err) { }
555
- };
556
- setTimeout(remove, ms);
556
+ html,
557
557
  };
558
- const contentType = req.headers['content-type'] ?? '';
559
- const boundary = contentType.split('boundary=')[1];
560
- if (!boundary) {
561
- throw new Error('Invalid multipart/form-data (no boundary)');
562
- }
563
- const boundaryBuf = Buffer.from(`\r\n--${boundary}`);
564
- return new Promise((resolve, reject) => {
565
- let body = {};
566
- let files = {};
567
- let buffer = Buffer.alloc(0);
568
- let currentFileStream = null;
569
- let file = null;
570
- let headerParsed = false;
571
- let aborted = false;
572
- const fail = (err) => {
573
- if (aborted)
574
- return;
575
- aborted = true;
576
- try {
577
- currentFileStream?.destroy();
578
- }
579
- catch { }
580
- try {
581
- file?.tempFilePath && fs_1.default.unlinkSync(file.tempFilePath);
582
- }
583
- catch { }
584
- //@ts-ignore
585
- try {
586
- res.uwsRes.close();
587
- }
588
- catch { }
589
- return reject(err);
590
- };
591
- //@ts-ignore
592
- res.uwsRes.onData((chunk, isLast) => {
593
- if (aborted)
594
- return;
595
- const data = Buffer.from(new Uint8Array(chunk));
596
- //@ts-ignore
597
- buffer = buffer.length === 0 ? data : Buffer.concat([buffer, data]);
598
- try {
599
- while (true) {
600
- if (!headerParsed) {
601
- const headerEnd = buffer.indexOf('\r\n\r\n');
602
- if (headerEnd === -1)
603
- break;
604
- const header = buffer.slice(0, headerEnd).toString();
605
- buffer = buffer.slice(headerEnd + 4);
606
- const disposition = header.match(/name="([^"]+)"(?:; filename="([^"]+)")?/);
607
- if (!disposition)
608
- continue;
609
- const fieldName = disposition[1];
610
- const fileName = disposition[2];
611
- if (!fileName) {
612
- const nextBoundary = buffer.indexOf(boundaryBuf);
613
- if (nextBoundary === -1)
614
- break;
615
- const value = buffer.slice(0, nextBoundary).toString().trim();
616
- body[fieldName] = value;
617
- buffer = buffer.slice(nextBoundary + boundaryBuf.length);
618
- continue;
619
- }
620
- const contentTypeMatch = header.match(/Content-Type: ([^\r\n]+)/);
621
- const mimetype = contentTypeMatch ? contentTypeMatch[1] : 'application/octet-stream';
622
- const extension = mime_types_1.default.extension(mimetype)
623
- || path_1.default.extname(fileName).replace('.', '')
624
- || 'bin';
625
- const tempFilename = crypto_1.default.randomBytes(16).toString('hex');
626
- const filePath = path_1.default.join(path_1.default.resolve(), `${temp}/${tempFilename}`);
627
- currentFileStream = fs_1.default.createWriteStream(filePath);
628
- file = {
629
- name: fileName,
630
- tempFilePath: filePath,
631
- tempFileName: tempFilename,
632
- mimetype: mimetype,
633
- extension: extension,
634
- size: 0,
635
- sizes: {
636
- bytes: 0,
637
- kb: 0,
638
- mb: 0,
639
- gb: 0
640
- },
641
- write: (to) => {
642
- return new Promise((resolve, reject) => {
643
- fs_1.default.createReadStream(filePath)
644
- .pipe(fs_1.default.createWriteStream(to))
645
- .on('finish', () => {
646
- return resolve(null);
647
- })
648
- .on('error', (err) => {
649
- return reject(err);
650
- });
651
- });
652
- },
653
- remove: () => {
654
- return new Promise(resolve => setTimeout(() => {
655
- fs_1.default.unlinkSync(filePath);
656
- return resolve(null);
657
- }, 100));
658
- }
659
- };
660
- if (!files[fieldName])
661
- files[fieldName] = [];
662
- files[fieldName].push(file);
663
- if (options.removeTempFile.remove) {
664
- removeTemp(filePath, options.removeTempFile.ms);
665
- }
666
- headerParsed = true;
667
- }
668
- const boundaryIndex = buffer.indexOf(boundaryBuf);
669
- if (boundaryIndex === -1) {
670
- const safeLength = buffer.length - boundaryBuf.length;
671
- if (safeLength > 0) {
672
- const writeChunk = buffer.slice(0, safeLength);
673
- currentFileStream.write(writeChunk);
674
- file.size += writeChunk.length;
675
- file.sizes = {
676
- bytes: file.size,
677
- kb: file.size / 1024,
678
- mb: file.size / 1024 / 1024,
679
- gb: file.size / 1024 / 1024 / 1024
680
- };
681
- if (file.size > options.limit) {
682
- return fail(new Error(`File too large (limit ${options.limit} bytes)`));
683
- }
684
- buffer = buffer.slice(safeLength);
685
- }
686
- break;
687
- }
688
- const filePart = buffer.slice(0, boundaryIndex);
689
- currentFileStream.write(filePart);
690
- file.size += filePart.length;
691
- file.sizes = {
692
- bytes: file.size,
693
- kb: file.size / 1024,
694
- mb: file.size / 1024 / 1024,
695
- gb: file.size / 1024 / 1024 / 1024
696
- };
697
- if (file.size > options.limit) {
698
- return fail(new Error(`File too large (limit ${options.limit} bytes)`));
699
- }
700
- currentFileStream.end();
701
- currentFileStream = null;
702
- file = null;
703
- buffer = buffer.slice(boundaryIndex + boundaryBuf.length);
704
- headerParsed = false;
705
- }
706
- if (isLast && !aborted) {
707
- if (currentFileStream)
708
- currentFileStream.end();
709
- return resolve({ body, files });
710
- }
711
- }
712
- catch (err) {
713
- return fail(err);
714
- }
715
- });
716
- });
717
- }
718
- uWSBody(res) {
719
- return new Promise((resolve, reject) => {
720
- let buffer = [];
721
- //@ts-ignore
722
- res.uwsRes.onData((chunk, isLast) => {
723
- buffer.push(Buffer.from(chunk));
724
- if (isLast) {
725
- const body = Buffer.concat(buffer).toString();
726
- try {
727
- const json = JSON.parse(body);
728
- return resolve(json);
729
- }
730
- catch (err) {
731
- return reject(err);
732
- }
733
- }
734
- });
735
- });
736
558
  }
737
559
  }
738
560
  exports.ParserFactory = ParserFactory;