@abejarano/ts-express-server 1.7.3 → 1.7.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.
@@ -33,6 +33,7 @@ export interface ServerResponse {
33
33
  secure?: boolean;
34
34
  sameSite?: "lax" | "strict" | "none";
35
35
  }): this;
36
+ download?(path: string, filename?: string, callback?: (err?: unknown) => void): this;
36
37
  end(body?: unknown): void | Promise<void>;
37
38
  }
38
39
  export type ServerHandlerInput = ServerHandler | ServerHandler[] | ServerRouter;
@@ -10,6 +10,9 @@ type MultipartFile = {
10
10
  name: string;
11
11
  size: number;
12
12
  type: string;
13
+ slice?: (start?: number, end?: number) => {
14
+ arrayBuffer(): Promise<ArrayBuffer>;
15
+ };
13
16
  arrayBuffer(): Promise<ArrayBuffer>;
14
17
  lastModified?: number;
15
18
  };
@@ -5,7 +5,7 @@ exports.getFiles = getFiles;
5
5
  exports.getFile = getFile;
6
6
  const ServerTypes_1 = require("../abstract/ServerTypes");
7
7
  class BunResponse {
8
- constructor(cookieJar, handlerTimeoutMs) {
8
+ constructor(cookieJar, handlerTimeoutMs, cookieDefaults) {
9
9
  this.statusCode = 200;
10
10
  this.statusExplicitlySet = false;
11
11
  this.headers = new Headers();
@@ -14,16 +14,26 @@ class BunResponse {
14
14
  this.ended = false;
15
15
  this.cookieJar = cookieJar;
16
16
  this.handlerTimeoutMs = handlerTimeoutMs;
17
+ this.cookieDefaults = cookieDefaults;
17
18
  this.endPromise = new Promise((resolve) => {
18
19
  this.resolveEnd = resolve;
19
20
  });
20
21
  }
21
22
  status(code) {
23
+ if (this.ended) {
24
+ return this;
25
+ }
22
26
  this.statusExplicitlySet = true;
23
27
  this.statusCode = code;
24
28
  return this;
25
29
  }
26
30
  set(name, value) {
31
+ if (this.ended) {
32
+ return this;
33
+ }
34
+ if (hasInvalidHeaderValue(value)) {
35
+ return this;
36
+ }
27
37
  if (name.toLowerCase() === "set-cookie") {
28
38
  this.setCookies.push(value);
29
39
  return this;
@@ -35,14 +45,24 @@ class BunResponse {
35
45
  return this.set(name, value);
36
46
  }
37
47
  cookie(name, value, options = {}) {
48
+ if (this.ended) {
49
+ return this;
50
+ }
51
+ if (!isValidCookieName(name)) {
52
+ return this;
53
+ }
54
+ const resolvedOptions = applyCookieDefaults(options, this.cookieDefaults);
38
55
  if (this.cookieJar && typeof this.cookieJar.set === "function") {
39
- this.cookieJar.set(name, value, toCookieJarOptions(options));
56
+ this.cookieJar.set(name, value, toCookieJarOptions(resolvedOptions));
40
57
  return this;
41
58
  }
42
- this.setCookies.push(serializeCookie(name, value, options));
59
+ this.setCookies.push(serializeCookie(name, value, resolvedOptions));
43
60
  return this;
44
61
  }
45
62
  json(body) {
63
+ if (this.ended) {
64
+ return;
65
+ }
46
66
  if (!this.headers.has("content-type")) {
47
67
  this.headers.set("content-type", "application/json");
48
68
  }
@@ -51,6 +71,9 @@ class BunResponse {
51
71
  this.resolveEnd?.();
52
72
  }
53
73
  send(body) {
74
+ if (this.ended) {
75
+ return;
76
+ }
54
77
  if (body instanceof Response) {
55
78
  this.rawResponse = body;
56
79
  this.ended = true;
@@ -77,6 +100,9 @@ class BunResponse {
77
100
  this.resolveEnd?.();
78
101
  }
79
102
  end(body) {
103
+ if (this.ended) {
104
+ return;
105
+ }
80
106
  if (body !== undefined) {
81
107
  this.send(body);
82
108
  return;
@@ -195,10 +221,11 @@ class BunRouter {
195
221
  }, timeoutMs);
196
222
  })
197
223
  : null;
224
+ const runPromise = runHandlers(handlers, req, res);
198
225
  try {
199
226
  chainCompleted = timeoutPromise
200
- ? await Promise.race([runHandlers(handlers, req, res), timeoutPromise])
201
- : await runHandlers(handlers, req, res);
227
+ ? await Promise.race([runPromise, timeoutPromise])
228
+ : await runPromise;
202
229
  }
203
230
  catch (error) {
204
231
  if (!res.isEnded()) {
@@ -213,6 +240,7 @@ class BunRouter {
213
240
  }
214
241
  }
215
242
  if (timedOut) {
243
+ runPromise.catch(() => { });
216
244
  done();
217
245
  return;
218
246
  }
@@ -278,6 +306,7 @@ class BunApp extends BunRouter {
278
306
  constructor() {
279
307
  super(...arguments);
280
308
  this.settings = new Map();
309
+ this.activeRequests = 0;
281
310
  }
282
311
  set(key, value) {
283
312
  this.settings.set(key, value);
@@ -289,12 +318,29 @@ class BunApp extends BunRouter {
289
318
  return async (request, server) => {
290
319
  const client = server?.requestIP?.(request);
291
320
  const cookieJar = request.cookies;
292
- const handlerTimeoutMs = this.get("handlerTimeoutMs");
293
- const trustProxy = this.get("trustProxy") === true;
294
- const ipOverride = trustProxy ? undefined : client?.address;
295
- const req = createRequest(request, ipOverride);
296
- const res = new BunResponse(cookieJar, typeof handlerTimeoutMs === "number" ? handlerTimeoutMs : undefined);
297
- await this.handle(req, res, () => undefined);
321
+ const handlerTimeoutSetting = this.get("handlerTimeoutMs");
322
+ const handlerTimeoutMs = handlerTimeoutSetting === undefined
323
+ ? DEFAULT_HANDLER_TIMEOUT_MS
324
+ : typeof handlerTimeoutSetting === "number"
325
+ ? handlerTimeoutSetting
326
+ : undefined;
327
+ const trustProxy = resolveTrustProxySetting(this.get("trustProxy"));
328
+ const maxConcurrentRequests = Number(this.get("maxConcurrentRequests") ?? 0);
329
+ if (maxConcurrentRequests > 0 && this.activeRequests >= maxConcurrentRequests) {
330
+ return new Response(JSON.stringify({ message: "Server busy" }), {
331
+ status: 503,
332
+ headers: [["content-type", "application/json"]],
333
+ });
334
+ }
335
+ this.activeRequests += 1;
336
+ const req = createRequest(request, client?.address, trustProxy);
337
+ const res = new BunResponse(cookieJar, handlerTimeoutMs, resolveCookieDefaults(this.get("cookieDefaults")));
338
+ try {
339
+ await this.handle(req, res, () => undefined);
340
+ }
341
+ finally {
342
+ this.activeRequests = Math.max(0, this.activeRequests - 1);
343
+ }
298
344
  if (!res.isEnded()) {
299
345
  res.status(204).end();
300
346
  }
@@ -314,6 +360,9 @@ class BunAdapter {
314
360
  }
315
361
  configure(app, _port) {
316
362
  const bunApp = app;
363
+ if (shouldEnableSecurityHeaders(bunApp.get("securityHeaders"))) {
364
+ bunApp.use(createSecurityHeadersMiddleware(bunApp));
365
+ }
317
366
  bunApp.use(createMultipartBodyParser(bunApp));
318
367
  bunApp.use(createJsonBodyParser(bunApp));
319
368
  bunApp.use(createUrlEncodedBodyParser(bunApp));
@@ -336,7 +385,7 @@ class BunAdapter {
336
385
  exports.BunAdapter = BunAdapter;
337
386
  const createJsonBodyParser = (app) => {
338
387
  return async (req, res, next) => {
339
- if (!req.raw || req.body !== undefined) {
388
+ if (!req.raw || req.body !== undefined || req.files !== undefined) {
340
389
  return next();
341
390
  }
342
391
  const method = String(req.method || "").toUpperCase();
@@ -357,9 +406,19 @@ const createJsonBodyParser = (app) => {
357
406
  return;
358
407
  }
359
408
  try {
360
- req.body = await req.raw.json();
409
+ if (contentLength === undefined) {
410
+ const text = await readBodyTextWithLimit(req.raw, limit);
411
+ req.body = JSON.parse(text);
412
+ }
413
+ else {
414
+ req.body = await req.raw.json();
415
+ }
361
416
  }
362
- catch {
417
+ catch (error) {
418
+ if (error?.status === 413) {
419
+ res.status(413).json({ message: "Payload too large" });
420
+ return;
421
+ }
363
422
  res.status(400).json({ message: "Invalid JSON" });
364
423
  return;
365
424
  }
@@ -371,6 +430,10 @@ const createUrlEncodedBodyParser = (app) => {
371
430
  if (!req.raw || req.body !== undefined) {
372
431
  return next();
373
432
  }
433
+ const method = String(req.method || "").toUpperCase();
434
+ if (method === "GET" || method === "HEAD") {
435
+ return next();
436
+ }
374
437
  const contentType = String(req.headers["content-type"] || "");
375
438
  if (!contentType.includes("application/x-www-form-urlencoded")) {
376
439
  return next();
@@ -381,14 +444,26 @@ const createUrlEncodedBodyParser = (app) => {
381
444
  res.status(413).json({ message: "Payload too large" });
382
445
  return;
383
446
  }
384
- const text = await req.raw.text();
385
- req.body = Object.fromEntries(new URLSearchParams(text));
447
+ try {
448
+ const text = contentLength === undefined
449
+ ? await readBodyTextWithLimit(req.raw, limit)
450
+ : await req.raw.text();
451
+ req.body = Object.fromEntries(new URLSearchParams(text));
452
+ }
453
+ catch (error) {
454
+ if (error?.status === 413) {
455
+ res.status(413).json({ message: "Payload too large" });
456
+ return;
457
+ }
458
+ res.status(400).json({ message: "Invalid form data" });
459
+ return;
460
+ }
386
461
  next();
387
462
  };
388
463
  };
389
464
  const createMultipartBodyParser = (app) => {
390
465
  return async (req, res, next) => {
391
- if (!req.raw || req.body !== undefined) {
466
+ if (!req.raw || req.body !== undefined || req.files !== undefined) {
392
467
  return next();
393
468
  }
394
469
  const contentType = String(req.headers["content-type"] || "");
@@ -398,6 +473,11 @@ const createMultipartBodyParser = (app) => {
398
473
  const options = normalizeMultipartOptions(app.get("multipart"));
399
474
  const lengthHeader = req.headers["content-length"];
400
475
  const contentLength = parseContentLength(lengthHeader);
476
+ // If content-length is missing, body size limits can't be enforced pre-read.
477
+ if (contentLength === undefined) {
478
+ res.status(411).json({ message: "Length required" });
479
+ return;
480
+ }
401
481
  if (contentLength !== undefined && contentLength > options.maxBodyBytes) {
402
482
  res.status(413).json({ message: "Payload too large" });
403
483
  return;
@@ -417,17 +497,34 @@ const createMultipartBodyParser = (app) => {
417
497
  res.status(415).json({ message: "Unsupported media type" });
418
498
  return;
419
499
  }
500
+ if (options.validateFile) {
501
+ const isValid = await options.validateFile(value);
502
+ if (!isValid) {
503
+ res.status(415).json({ message: "Unsupported media type" });
504
+ return;
505
+ }
506
+ }
507
+ if (options.allowedFileSignatures) {
508
+ const signatureAllowed = await isAllowedFileSignature(value, options.allowedFileSignatures);
509
+ if (!signatureAllowed) {
510
+ res.status(415).json({ message: "Unsupported media type" });
511
+ return;
512
+ }
513
+ }
420
514
  fileCount += 1;
421
515
  if (fileCount > options.maxFiles) {
422
516
  res.status(413).json({ message: "Payload too large" });
423
517
  return;
424
518
  }
425
- const bucket = files[key];
426
- if (bucket) {
427
- bucket.push(value);
519
+ const existing = files[key];
520
+ if (!existing) {
521
+ files[key] = value;
522
+ }
523
+ else if (Array.isArray(existing)) {
524
+ existing.push(value);
428
525
  }
429
526
  else {
430
- files[key] = [value];
527
+ files[key] = [existing, value];
431
528
  }
432
529
  continue;
433
530
  }
@@ -457,10 +554,11 @@ const createMultipartBodyParser = (app) => {
457
554
  next();
458
555
  };
459
556
  };
460
- function createRequest(request, ipOverride) {
557
+ function createRequest(request, remoteAddress, trustProxy) {
461
558
  const url = new URL(request.url);
462
559
  const headers = toHeaderRecord(request.headers);
463
560
  const query = toQueryRecord(url.searchParams);
561
+ const ip = resolveClientIp(headers, remoteAddress, trustProxy);
464
562
  return {
465
563
  method: request.method.toUpperCase(),
466
564
  path: normalizePath(url.pathname),
@@ -469,7 +567,7 @@ function createRequest(request, ipOverride) {
469
567
  query,
470
568
  headers,
471
569
  cookies: parseCookies(headers.cookie),
472
- ip: ipOverride || extractIp(headers),
570
+ ip,
473
571
  raw: request,
474
572
  };
475
573
  }
@@ -557,6 +655,10 @@ function toCookieJarOptions(options) {
557
655
  };
558
656
  }
559
657
  function getBodyLimit(app) {
658
+ const configured = app.get("bodyLimit");
659
+ if (typeof configured === "number" && Number.isFinite(configured)) {
660
+ return configured;
661
+ }
560
662
  return normalizeMultipartOptions(app.get("multipart")).maxBodyBytes;
561
663
  }
562
664
  function normalizeMultipartOptions(input) {
@@ -569,6 +671,8 @@ function normalizeMultipartOptions(input) {
569
671
  maxFileBytes: value.maxFileBytes ?? DEFAULT_MULTIPART_OPTIONS.maxFileBytes,
570
672
  maxFiles: value.maxFiles ?? DEFAULT_MULTIPART_OPTIONS.maxFiles,
571
673
  allowedMimeTypes: value.allowedMimeTypes,
674
+ allowedFileSignatures: value.allowedFileSignatures,
675
+ validateFile: value.validateFile,
572
676
  };
573
677
  }
574
678
  function isMimeAllowed(type, allowed) {
@@ -629,9 +733,12 @@ function parseContentLength(header) {
629
733
  const parsed = Number.parseInt(value, 10);
630
734
  return Number.isNaN(parsed) ? undefined : parsed;
631
735
  }
736
+ const DEFAULT_HANDLER_TIMEOUT_MS = 30000;
632
737
  function readSetCookieHeaders(headers) {
633
738
  const bunHeaders = headers;
634
- const setCookieFromApi = bunHeaders.getSetCookie?.() ?? bunHeaders.getAll?.("Set-Cookie");
739
+ const setCookieFromApi = bunHeaders.getSetCookie?.() ??
740
+ bunHeaders.getAll?.("set-cookie") ??
741
+ bunHeaders.getAll?.("Set-Cookie");
635
742
  if (setCookieFromApi && setCookieFromApi.length > 0) {
636
743
  return setCookieFromApi;
637
744
  }
@@ -645,7 +752,11 @@ function readSetCookieHeaders(headers) {
645
752
  }
646
753
  function getFiles(req, field) {
647
754
  const map = (req.files ?? {});
648
- return map[field] ?? [];
755
+ const entry = map[field];
756
+ if (!entry) {
757
+ return [];
758
+ }
759
+ return Array.isArray(entry) ? entry : [entry];
649
760
  }
650
761
  function getFile(req, field) {
651
762
  return getFiles(req, field)[0];
@@ -657,6 +768,78 @@ function extractIp(headers) {
657
768
  }
658
769
  return headers["x-real-ip"];
659
770
  }
771
+ function resolveClientIp(headers, remoteAddress, trustProxy) {
772
+ const normalizedRemote = normalizeIp(remoteAddress);
773
+ if (!trustProxy) {
774
+ return normalizedRemote;
775
+ }
776
+ if (trustProxy === true) {
777
+ return extractIp(headers) || normalizedRemote;
778
+ }
779
+ if (typeof trustProxy === "function") {
780
+ return trustProxy(normalizedRemote)
781
+ ? extractIp(headers) || normalizedRemote
782
+ : normalizedRemote;
783
+ }
784
+ const trusted = isTrustedProxy(normalizedRemote, trustProxy);
785
+ return trusted ? extractIp(headers) || normalizedRemote : normalizedRemote;
786
+ }
787
+ function normalizeIp(ip) {
788
+ if (!ip) {
789
+ return undefined;
790
+ }
791
+ if (ip.includes(".") && ip.includes(":")) {
792
+ return ip.split(":")[0];
793
+ }
794
+ return ip;
795
+ }
796
+ function isTrustedProxy(ip, allowlist) {
797
+ if (!ip) {
798
+ return false;
799
+ }
800
+ for (const entry of allowlist) {
801
+ if (entry.includes("/")) {
802
+ if (matchesCidr(ip, entry)) {
803
+ return true;
804
+ }
805
+ continue;
806
+ }
807
+ if (entry === ip) {
808
+ return true;
809
+ }
810
+ }
811
+ return false;
812
+ }
813
+ function matchesCidr(ip, cidr) {
814
+ const [range, bitsString] = cidr.split("/");
815
+ if (!range || !bitsString) {
816
+ return false;
817
+ }
818
+ if (range.includes(":") || ip.includes(":")) {
819
+ return range === ip;
820
+ }
821
+ const bits = Number(bitsString);
822
+ if (!Number.isFinite(bits) || bits < 0 || bits > 32) {
823
+ return false;
824
+ }
825
+ const ipValue = ipv4ToInt(ip);
826
+ const rangeValue = ipv4ToInt(range);
827
+ if (ipValue === null || rangeValue === null) {
828
+ return false;
829
+ }
830
+ const mask = bits === 0 ? 0 : ~((1 << (32 - bits)) - 1);
831
+ return (ipValue & mask) === (rangeValue & mask);
832
+ }
833
+ function ipv4ToInt(ip) {
834
+ const parts = ip.split(".").map((part) => Number(part));
835
+ if (parts.length !== 4 || parts.some((part) => part < 0 || part > 255)) {
836
+ return null;
837
+ }
838
+ return ((parts[0] << 24) +
839
+ (parts[1] << 16) +
840
+ (parts[2] << 8) +
841
+ parts[3]) >>> 0;
842
+ }
660
843
  function normalizePath(path) {
661
844
  if (!path.startsWith("/")) {
662
845
  path = `/${path}`;
@@ -689,7 +872,12 @@ function matchPath(routePath, requestPath) {
689
872
  const routePart = routeParts[index];
690
873
  const requestPart = requestParts[index];
691
874
  if (routePart.startsWith(":")) {
692
- params[routePart.slice(1)] = decodeURIComponent(requestPart);
875
+ try {
876
+ params[routePart.slice(1)] = decodeURIComponent(requestPart);
877
+ }
878
+ catch {
879
+ return null;
880
+ }
693
881
  continue;
694
882
  }
695
883
  if (routePart !== requestPart) {
@@ -709,6 +897,128 @@ function normalizeHandlers(inputs) {
709
897
  }
710
898
  return handlers;
711
899
  }
900
+ function applyCookieDefaults(options, defaults) {
901
+ if (!defaults) {
902
+ return options;
903
+ }
904
+ const applyTo = defaults.applyTo ?? "session";
905
+ const isSession = options.maxAge === undefined && options.expires === undefined;
906
+ if (applyTo !== "all" && !isSession) {
907
+ return options;
908
+ }
909
+ return { ...defaults.options, ...options };
910
+ }
911
+ function resolveCookieDefaults(input) {
912
+ if (!input || typeof input !== "object") {
913
+ return undefined;
914
+ }
915
+ const defaults = input;
916
+ if (!defaults.options || typeof defaults.options !== "object") {
917
+ return undefined;
918
+ }
919
+ return defaults;
920
+ }
921
+ function resolveTrustProxySetting(input) {
922
+ if (input === true || input === false) {
923
+ return input;
924
+ }
925
+ if (Array.isArray(input) && input.every((entry) => typeof entry === "string")) {
926
+ return input;
927
+ }
928
+ if (typeof input === "function") {
929
+ return input;
930
+ }
931
+ return undefined;
932
+ }
933
+ function hasInvalidHeaderValue(value) {
934
+ return value.includes("\r") || value.includes("\n");
935
+ }
936
+ function shouldEnableSecurityHeaders(setting) {
937
+ if (typeof setting === "boolean") {
938
+ return setting;
939
+ }
940
+ return process?.env?.NODE_ENV === "production";
941
+ }
942
+ function createSecurityHeadersMiddleware(_app) {
943
+ return (_req, res, next) => {
944
+ res.set("x-content-type-options", "nosniff");
945
+ res.set("referrer-policy", "no-referrer");
946
+ res.set("x-frame-options", "DENY");
947
+ next();
948
+ };
949
+ }
950
+ async function readBodyTextWithLimit(request, limit) {
951
+ const bytes = await readBodyBytesWithLimit(request, limit);
952
+ return new TextDecoder().decode(bytes);
953
+ }
954
+ async function readBodyBytesWithLimit(request, limit) {
955
+ if (!request.body) {
956
+ return new Uint8Array();
957
+ }
958
+ const reader = request.body.getReader();
959
+ const chunks = [];
960
+ let total = 0;
961
+ while (true) {
962
+ const { done, value } = await reader.read();
963
+ if (done) {
964
+ break;
965
+ }
966
+ if (value) {
967
+ total += value.length;
968
+ if (total > limit) {
969
+ const error = new Error("Payload too large");
970
+ error.status = 413;
971
+ throw error;
972
+ }
973
+ chunks.push(value);
974
+ }
975
+ }
976
+ const result = new Uint8Array(total);
977
+ let offset = 0;
978
+ for (const chunk of chunks) {
979
+ result.set(chunk, offset);
980
+ offset += chunk.length;
981
+ }
982
+ return result;
983
+ }
984
+ async function isAllowedFileSignature(file, allowed) {
985
+ const head = typeof file.slice === "function" ? file.slice(0, 32) : file;
986
+ const buffer = new Uint8Array(await head.arrayBuffer());
987
+ for (const kind of allowed) {
988
+ if (matchesSignature(buffer, kind)) {
989
+ return true;
990
+ }
991
+ }
992
+ return false;
993
+ }
994
+ function matchesSignature(buffer, kind) {
995
+ if (kind === "png") {
996
+ return (buffer.length >= 8 &&
997
+ buffer[0] === 0x89 &&
998
+ buffer[1] === 0x50 &&
999
+ buffer[2] === 0x4e &&
1000
+ buffer[3] === 0x47 &&
1001
+ buffer[4] === 0x0d &&
1002
+ buffer[5] === 0x0a &&
1003
+ buffer[6] === 0x1a &&
1004
+ buffer[7] === 0x0a);
1005
+ }
1006
+ if (kind === "pdf") {
1007
+ return (buffer.length >= 4 &&
1008
+ buffer[0] === 0x25 &&
1009
+ buffer[1] === 0x50 &&
1010
+ buffer[2] === 0x44 &&
1011
+ buffer[3] === 0x46);
1012
+ }
1013
+ // jpg/jpeg
1014
+ return (buffer.length >= 3 &&
1015
+ buffer[0] === 0xff &&
1016
+ buffer[1] === 0xd8 &&
1017
+ buffer[2] === 0xff);
1018
+ }
1019
+ function isValidCookieName(name) {
1020
+ return /^[!#$%&'*+\-.^_|~0-9A-Za-z]+$/.test(name);
1021
+ }
712
1022
  async function runHandlers(handlers, req, res) {
713
1023
  let index = 0;
714
1024
  const dispatch = async () => {
@@ -24,41 +24,6 @@ class FileUploadModule extends abstract_1.BaseServerModule {
24
24
  app.use(fileUpload(this.fileUploadOptions));
25
25
  return;
26
26
  }
27
- app.use(createBunFileUploadMiddleware());
28
27
  }
29
28
  }
30
29
  exports.FileUploadModule = FileUploadModule;
31
- const createBunFileUploadMiddleware = () => {
32
- return async (req, _res, next) => {
33
- if (!req.raw || req.files) {
34
- return next();
35
- }
36
- const contentType = String(req.headers?.["content-type"] || "");
37
- if (!contentType.includes("multipart/form-data")) {
38
- return next();
39
- }
40
- try {
41
- const formData = await req.raw.formData();
42
- const files = {};
43
- const body = {};
44
- formData.forEach((value, key) => {
45
- if (value instanceof File) {
46
- files[key] = value;
47
- }
48
- else {
49
- body[key] = value;
50
- }
51
- });
52
- if (Object.keys(files).length) {
53
- req.files = files;
54
- }
55
- if (Object.keys(body).length && req.body === undefined) {
56
- req.body = body;
57
- }
58
- }
59
- catch {
60
- // Ignore malformed multipart payloads.
61
- }
62
- next();
63
- };
64
- };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@abejarano/ts-express-server",
3
3
  "author": "angel bejarano / angel.bejarano@jaspesoft.com",
4
- "version": "1.7.3",
4
+ "version": "1.7.4",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "files": [