node-server-engine 1.10.9 → 1.10.11

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.
@@ -12,13 +12,13 @@ const defaultMiddleware = exports.defaultMiddleware = [(0, _middleware.healthChe
12
12
  }), (0, _express.urlencoded)({
13
13
  extended: true,
14
14
  limit: process.env.REQUEST_SIZE_LIMIT ?? '1mb'
15
- }), (0, _middleware.metrics)(), (0, _middleware.version)()].filter(val => val);
15
+ }), _middleware.requestMetrics, (0, _middleware.metrics)(), (0, _middleware.version)()].filter(val => val);
16
16
 
17
17
  // Error handling middleware that are run after the main business logic endpoints
18
18
  const defaultErrorMiddleware = exports.defaultErrorMiddleware = [_middleware.error, _middleware.fourOhFour];
19
19
 
20
20
  // Middleware that are served on the secondary port
21
- const secondaryMiddleware = exports.secondaryMiddleware = [(0, _middleware.metrics)(), (0, _middleware.version)(), (0, _middleware.swaggerDocs)()];
21
+ const secondaryMiddleware = exports.secondaryMiddleware = [_middleware.requestMetrics, (0, _middleware.metrics)(), (0, _middleware.version)(), (0, _middleware.swaggerDocs)()];
22
22
 
23
23
  // Add request logging to middleware chain when reporting is not silenced
24
24
  if (!process.env.SILENCE_REPORT) {
@@ -1 +1 @@
1
- {"version":3,"file":"Server.const.js","names":["_express","require","_middleware","defaultMiddleware","exports","healthCheck","json","limit","process","env","REQUEST_SIZE_LIMIT","urlencoded","extended","metrics","version","filter","val","defaultErrorMiddleware","error","fourOhFour","secondaryMiddleware","swaggerDocs","SILENCE_REPORT","unshift","requestLogger","shutdownSignals"],"sources":["../../../src/entities/Server/Server.const.ts"],"sourcesContent":["import { json, urlencoded, ErrorRequestHandler, RequestHandler } from 'express';\nimport {\n metrics,\n error,\n fourOhFour,\n healthCheck,\n version,\n swaggerDocs,\n requestLogger\n} from 'middleware';\n\n// Default middleware that are applied first thing on the primary port, before the business logic endpoints\nexport const defaultMiddleware: Array<RequestHandler> = [\n healthCheck(),\n json({ limit: process.env.REQUEST_SIZE_LIMIT ?? '1mb' }),\n urlencoded({\n extended: true,\n limit: process.env.REQUEST_SIZE_LIMIT ?? '1mb'\n }),\n metrics(),\n version()\n].filter((val) => val);\n\n// Error handling middleware that are run after the main business logic endpoints\nexport const defaultErrorMiddleware: Array<\n RequestHandler | ErrorRequestHandler\n> = [error, fourOhFour];\n\n// Middleware that are served on the secondary port\nexport const secondaryMiddleware: Array<RequestHandler> = [\n metrics(),\n version(),\n swaggerDocs()\n];\n\n// Add request logging to middleware chain when reporting is not silenced\nif (!process.env.SILENCE_REPORT) {\n defaultMiddleware.unshift(requestLogger());\n secondaryMiddleware.unshift(requestLogger());\n}\n\nexport const shutdownSignals = ['SIGTERM', 'SIGUSR2', 'SIGINT'];\n"],"mappings":";;;;;;AAAA,IAAAA,QAAA,GAAAC,OAAA;AACA,IAAAC,WAAA,GAAAD,OAAA;AAUA;AACO,MAAME,iBAAwC,GAAAC,OAAA,CAAAD,iBAAA,GAAG,CACtD,IAAAE,uBAAW,EAAC,CAAC,EACb,IAAAC,aAAI,EAAC;EAAEC,KAAK,EAAEC,OAAO,CAACC,GAAG,CAACC,kBAAkB,IAAI;AAAM,CAAC,CAAC,EACxD,IAAAC,mBAAU,EAAC;EACTC,QAAQ,EAAE,IAAI;EACdL,KAAK,EAAEC,OAAO,CAACC,GAAG,CAACC,kBAAkB,IAAI;AAC3C,CAAC,CAAC,EACF,IAAAG,mBAAO,EAAC,CAAC,EACT,IAAAC,mBAAO,EAAC,CAAC,CACV,CAACC,MAAM,CAAEC,GAAG,IAAKA,GAAG,CAAC;;AAEtB;AACO,MAAMC,sBAEZ,GAAAb,OAAA,CAAAa,sBAAA,GAAG,CAACC,iBAAK,EAAEC,sBAAU,CAAC;;AAEvB;AACO,MAAMC,mBAA0C,GAAAhB,OAAA,CAAAgB,mBAAA,GAAG,CACxD,IAAAP,mBAAO,EAAC,CAAC,EACT,IAAAC,mBAAO,EAAC,CAAC,EACT,IAAAO,uBAAW,EAAC,CAAC,CACd;;AAED;AACA,IAAI,CAACb,OAAO,CAACC,GAAG,CAACa,cAAc,EAAE;EAC/BnB,iBAAiB,CAACoB,OAAO,CAAC,IAAAC,yBAAa,EAAC,CAAC,CAAC;EAC1CJ,mBAAmB,CAACG,OAAO,CAAC,IAAAC,yBAAa,EAAC,CAAC,CAAC;AAC9C;AAEO,MAAMC,eAAe,GAAArB,OAAA,CAAAqB,eAAA,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"Server.const.js","names":["_express","require","_middleware","defaultMiddleware","exports","healthCheck","json","limit","process","env","REQUEST_SIZE_LIMIT","urlencoded","extended","requestMetrics","metrics","version","filter","val","defaultErrorMiddleware","error","fourOhFour","secondaryMiddleware","swaggerDocs","SILENCE_REPORT","unshift","requestLogger","shutdownSignals"],"sources":["../../../src/entities/Server/Server.const.ts"],"sourcesContent":["import { json, urlencoded, ErrorRequestHandler, RequestHandler } from 'express';\nimport {\n metrics,\n error,\n fourOhFour,\n healthCheck,\n version,\n swaggerDocs,\n requestLogger,\n requestMetrics\n} from 'middleware';\n\n// Default middleware that are applied first thing on the primary port, before the business logic endpoints\nexport const defaultMiddleware: Array<RequestHandler> = [\n healthCheck(),\n json({ limit: process.env.REQUEST_SIZE_LIMIT ?? '1mb' }),\n urlencoded({\n extended: true,\n limit: process.env.REQUEST_SIZE_LIMIT ?? '1mb'\n }),\n requestMetrics,\n metrics(),\n version()\n].filter((val) => val);\n\n// Error handling middleware that are run after the main business logic endpoints\nexport const defaultErrorMiddleware: Array<\n RequestHandler | ErrorRequestHandler\n> = [error, fourOhFour];\n\n// Middleware that are served on the secondary port\nexport const secondaryMiddleware: Array<RequestHandler> = [\n requestMetrics,\n metrics(),\n version(),\n swaggerDocs()\n];\n\n// Add request logging to middleware chain when reporting is not silenced\nif (!process.env.SILENCE_REPORT) {\n defaultMiddleware.unshift(requestLogger());\n secondaryMiddleware.unshift(requestLogger());\n}\n\nexport const shutdownSignals = ['SIGTERM', 'SIGUSR2', 'SIGINT'];\n"],"mappings":";;;;;;AAAA,IAAAA,QAAA,GAAAC,OAAA;AACA,IAAAC,WAAA,GAAAD,OAAA;AAWA;AACO,MAAME,iBAAwC,GAAAC,OAAA,CAAAD,iBAAA,GAAG,CACtD,IAAAE,uBAAW,EAAC,CAAC,EACb,IAAAC,aAAI,EAAC;EAAEC,KAAK,EAAEC,OAAO,CAACC,GAAG,CAACC,kBAAkB,IAAI;AAAM,CAAC,CAAC,EACxD,IAAAC,mBAAU,EAAC;EACTC,QAAQ,EAAE,IAAI;EACdL,KAAK,EAAEC,OAAO,CAACC,GAAG,CAACC,kBAAkB,IAAI;AAC3C,CAAC,CAAC,EACFG,0BAAc,EACd,IAAAC,mBAAO,EAAC,CAAC,EACT,IAAAC,mBAAO,EAAC,CAAC,CACV,CAACC,MAAM,CAAEC,GAAG,IAAKA,GAAG,CAAC;;AAEtB;AACO,MAAMC,sBAEZ,GAAAd,OAAA,CAAAc,sBAAA,GAAG,CAACC,iBAAK,EAAEC,sBAAU,CAAC;;AAEvB;AACO,MAAMC,mBAA0C,GAAAjB,OAAA,CAAAiB,mBAAA,GAAG,CACxDR,0BAAc,EACd,IAAAC,mBAAO,EAAC,CAAC,EACT,IAAAC,mBAAO,EAAC,CAAC,EACT,IAAAO,uBAAW,EAAC,CAAC,CACd;;AAED;AACA,IAAI,CAACd,OAAO,CAACC,GAAG,CAACc,cAAc,EAAE;EAC/BpB,iBAAiB,CAACqB,OAAO,CAAC,IAAAC,yBAAa,EAAC,CAAC,CAAC;EAC1CJ,mBAAmB,CAACG,OAAO,CAAC,IAAAC,yBAAa,EAAC,CAAC,CAAC;AAC9C;AAEO,MAAMC,eAAe,GAAAtB,OAAA,CAAAsB,eAAA,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,CAAC","ignoreList":[]}
@@ -1,3 +1,6 @@
1
- import { Counter, Histogram } from 'prom-client';
1
+ import { Counter, Histogram, Gauge } from 'prom-client';
2
2
  export declare const httpRequestCount: Counter<"method" | "path" | "service" | "status">;
3
3
  export declare const httpRequestDuration: Histogram<"method" | "path" | "service" | "status">;
4
+ export declare const httpInFlight: Gauge<string>;
5
+ export declare const httpErrorCount: Counter<"method" | "service" | "status">;
6
+ export declare const httpResponseSize: Histogram<"method" | "path" | "service" | "status">;
@@ -3,20 +3,47 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.httpRequestDuration = exports.httpRequestCount = void 0;
6
+ exports.httpResponseSize = exports.httpRequestDuration = exports.httpRequestCount = exports.httpInFlight = exports.httpErrorCount = void 0;
7
7
  var _promClient = require("prom-client");
8
8
  var _const = require("../const");
9
+ // Register metrics to both the instance-level registry and the service-level
10
+ // registry so `/metrics/service` can expose aggregated/service-wide metrics.
9
11
  const httpRequestCount = exports.httpRequestCount = new _promClient.Counter({
10
12
  name: 'http_requests_total',
11
13
  help: 'Total count of request received',
12
- registers: [_const.instanceRegistry],
14
+ registers: [_const.instanceRegistry, _const.serviceRegistry],
13
15
  labelNames: ['service', 'path', 'method', 'status']
14
16
  });
15
17
  const httpRequestDuration = exports.httpRequestDuration = new _promClient.Histogram({
16
18
  name: 'http_requests_duration',
17
19
  help: 'Duration of the request received',
18
- registers: [_const.instanceRegistry],
20
+ registers: [_const.instanceRegistry, _const.serviceRegistry],
19
21
  labelNames: ['service', 'path', 'method', 'status'],
20
- buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.5, 1, 2.5, 5, 10]
22
+ // Reduced buckets to lower cardinality while keeping useful latency ranges
23
+ buckets: [0.01, 0.05, 0.1, 0.5, 1, 5]
24
+ });
25
+
26
+ // Gauge for currently in-flight requests (simple, no labels to reduce cardinality)
27
+ const httpInFlight = exports.httpInFlight = new _promClient.Gauge({
28
+ name: 'http_requests_in_flight',
29
+ help: 'Number of requests currently being processed by this instance',
30
+ registers: [_const.instanceRegistry]
31
+ });
32
+
33
+ // Counter for errors (4xx/5xx) — aggregated by status class and method
34
+ const httpErrorCount = exports.httpErrorCount = new _promClient.Counter({
35
+ name: 'http_requests_errors_total',
36
+ help: 'Total count of error responses',
37
+ registers: [_const.instanceRegistry, _const.serviceRegistry],
38
+ labelNames: ['service', 'status', 'method']
39
+ });
40
+
41
+ // Response size histogram (bytes). Uses instance + service registries.
42
+ const httpResponseSize = exports.httpResponseSize = new _promClient.Histogram({
43
+ name: 'http_response_size_bytes',
44
+ help: 'Size of HTTP responses in bytes',
45
+ registers: [_const.instanceRegistry, _const.serviceRegistry],
46
+ labelNames: ['service', 'path', 'method', 'status'],
47
+ buckets: [200, 500, 1500, 5000]
21
48
  });
22
49
  //# sourceMappingURL=request.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"request.js","names":["_promClient","require","_const","httpRequestCount","exports","Counter","name","help","registers","instanceRegistry","labelNames","httpRequestDuration","Histogram","buckets"],"sources":["../../src/metrics/request.ts"],"sourcesContent":["import { Counter, Histogram } from 'prom-client';\nimport { instanceRegistry } from 'const';\n\nexport const httpRequestCount = new Counter({\n name: 'http_requests_total',\n help: 'Total count of request received',\n registers: [instanceRegistry],\n labelNames: ['service', 'path', 'method', 'status']\n});\n\nexport const httpRequestDuration = new Histogram({\n name: 'http_requests_duration',\n help: 'Duration of the request received',\n registers: [instanceRegistry],\n labelNames: ['service', 'path', 'method', 'status'],\n buckets: [0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 0.5, 1, 2.5, 5, 10]\n});\n"],"mappings":";;;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AACA,IAAAC,MAAA,GAAAD,OAAA;AAEO,MAAME,gBAAgB,GAAAC,OAAA,CAAAD,gBAAA,GAAG,IAAIE,mBAAO,CAAC;EAC1CC,IAAI,EAAE,qBAAqB;EAC3BC,IAAI,EAAE,iCAAiC;EACvCC,SAAS,EAAE,CAACC,uBAAgB,CAAC;EAC7BC,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;AACpD,CAAC,CAAC;AAEK,MAAMC,mBAAmB,GAAAP,OAAA,CAAAO,mBAAA,GAAG,IAAIC,qBAAS,CAAC;EAC/CN,IAAI,EAAE,wBAAwB;EAC9BC,IAAI,EAAE,kCAAkC;EACxCC,SAAS,EAAE,CAACC,uBAAgB,CAAC;EAC7BC,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC;EACnDG,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,EAAE;AACxE,CAAC,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"request.js","names":["_promClient","require","_const","httpRequestCount","exports","Counter","name","help","registers","instanceRegistry","serviceRegistry","labelNames","httpRequestDuration","Histogram","buckets","httpInFlight","Gauge","httpErrorCount","httpResponseSize"],"sources":["../../src/metrics/request.ts"],"sourcesContent":["import { Counter, Histogram, Gauge } from 'prom-client';\nimport { instanceRegistry, serviceRegistry } from 'const';\n\n// Register metrics to both the instance-level registry and the service-level\n// registry so `/metrics/service` can expose aggregated/service-wide metrics.\nexport const httpRequestCount = new Counter({\n name: 'http_requests_total',\n help: 'Total count of request received',\n registers: [instanceRegistry, serviceRegistry],\n labelNames: ['service', 'path', 'method', 'status']\n});\n\nexport const httpRequestDuration = new Histogram({\n name: 'http_requests_duration',\n help: 'Duration of the request received',\n registers: [instanceRegistry, serviceRegistry],\n labelNames: ['service', 'path', 'method', 'status'],\n // Reduced buckets to lower cardinality while keeping useful latency ranges\n buckets: [0.01, 0.05, 0.1, 0.5, 1, 5]\n});\n\n// Gauge for currently in-flight requests (simple, no labels to reduce cardinality)\nexport const httpInFlight = new Gauge({\n name: 'http_requests_in_flight',\n help: 'Number of requests currently being processed by this instance',\n registers: [instanceRegistry]\n});\n\n// Counter for errors (4xx/5xx) — aggregated by status class and method\nexport const httpErrorCount = new Counter({\n name: 'http_requests_errors_total',\n help: 'Total count of error responses',\n registers: [instanceRegistry, serviceRegistry],\n labelNames: ['service', 'status', 'method']\n});\n\n// Response size histogram (bytes). Uses instance + service registries.\nexport const httpResponseSize = new Histogram({\n name: 'http_response_size_bytes',\n help: 'Size of HTTP responses in bytes',\n registers: [instanceRegistry, serviceRegistry],\n labelNames: ['service', 'path', 'method', 'status'],\n buckets: [200, 500, 1500, 5000]\n});\n"],"mappings":";;;;;;AAAA,IAAAA,WAAA,GAAAC,OAAA;AACA,IAAAC,MAAA,GAAAD,OAAA;AAEA;AACA;AACO,MAAME,gBAAgB,GAAAC,OAAA,CAAAD,gBAAA,GAAG,IAAIE,mBAAO,CAAC;EAC1CC,IAAI,EAAE,qBAAqB;EAC3BC,IAAI,EAAE,iCAAiC;EACvCC,SAAS,EAAE,CAACC,uBAAgB,EAAEC,sBAAe,CAAC;EAC9CC,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ;AACpD,CAAC,CAAC;AAEK,MAAMC,mBAAmB,GAAAR,OAAA,CAAAQ,mBAAA,GAAG,IAAIC,qBAAS,CAAC;EAC/CP,IAAI,EAAE,wBAAwB;EAC9BC,IAAI,EAAE,kCAAkC;EACxCC,SAAS,EAAE,CAACC,uBAAgB,EAAEC,sBAAe,CAAC;EAC9CC,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC;EACnD;EACAG,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AACtC,CAAC,CAAC;;AAEF;AACO,MAAMC,YAAY,GAAAX,OAAA,CAAAW,YAAA,GAAG,IAAIC,iBAAK,CAAC;EACpCV,IAAI,EAAE,yBAAyB;EAC/BC,IAAI,EAAE,+DAA+D;EACrEC,SAAS,EAAE,CAACC,uBAAgB;AAC9B,CAAC,CAAC;;AAEF;AACO,MAAMQ,cAAc,GAAAb,OAAA,CAAAa,cAAA,GAAG,IAAIZ,mBAAO,CAAC;EACxCC,IAAI,EAAE,4BAA4B;EAClCC,IAAI,EAAE,gCAAgC;EACtCC,SAAS,EAAE,CAACC,uBAAgB,EAAEC,sBAAe,CAAC;EAC9CC,UAAU,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,QAAQ;AAC5C,CAAC,CAAC;;AAEF;AACO,MAAMO,gBAAgB,GAAAd,OAAA,CAAAc,gBAAA,GAAG,IAAIL,qBAAS,CAAC;EAC5CP,IAAI,EAAE,0BAA0B;EAChCC,IAAI,EAAE,iCAAiC;EACvCC,SAAS,EAAE,CAACC,uBAAgB,EAAEC,sBAAe,CAAC;EAC9CC,UAAU,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,CAAC;EACnDG,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI;AAChC,CAAC,CAAC","ignoreList":[]}
@@ -11,6 +11,7 @@ var _path = _interopRequireDefault(require("path"));
11
11
  var _utils = require("../../utils");
12
12
  var _WebError = require("../../entities/WebError");
13
13
  var _validate = require("./validate");
14
+ var _bytes = _interopRequireDefault(require("bytes"));
14
15
  const namespace = 'engine:middleware:multiPartFileUploader';
15
16
  const TEMP_DIR = _path.default.join(process.env.FILE_STORAGE_FOLDER || './uploads', 'temp_chunks');
16
17
  const FILE_DIR = _path.default.join(process.env.FILE_STORAGE_FOLDER || './uploads', 'completed_files');
@@ -92,7 +93,7 @@ function multiPartFileUploader(filesOption, validator) {
92
93
  }
93
94
  }));
94
95
  }
95
- if (filesOption?.maxSize && req.file.size > parseInt(filesOption?.maxSize, 10)) {
96
+ if (filesOption?.maxSize && req.file.size > ((0, _bytes.default)(filesOption.maxSize) || 0)) {
96
97
  return next(new _WebError.WebError({
97
98
  statusCode: 400,
98
99
  errorCode: 'chunk-too-large',
@@ -187,12 +188,18 @@ async function mergeChunks(filename, totalChunks, sessionFolder, finalFilePath)
187
188
  finalFilePath
188
189
  }
189
190
  });
190
- for (let i = 0; i < parseInt(totalChunks, 10); i++) {
191
+ const totalChunksNum = parseInt(totalChunks, 10);
192
+ const chunks = [];
193
+
194
+ // Read all chunks into memory first
195
+ for (let i = 0; i < totalChunksNum; i++) {
191
196
  const partPath = _path.default.join(sessionFolder, `${filename}.part${i}`);
192
197
  try {
193
198
  const chunkData = await _fsExtra.default.readFile(partPath);
194
- writeStream.write(chunkData);
199
+ chunks.push(chunkData);
195
200
  } catch (error) {
201
+ // Cleanup on error
202
+ await _fsExtra.default.remove(sessionFolder).catch(cleanupErr => (0, _utils.reportError)(`Error cleaning up folder ${sessionFolder}: ${cleanupErr.message}`));
196
203
  throw new _WebError.WebError({
197
204
  statusCode: 500,
198
205
  errorCode: 'chunk-read-failed',
@@ -202,19 +209,39 @@ async function mergeChunks(filename, totalChunks, sessionFolder, finalFilePath)
202
209
  }
203
210
  });
204
211
  }
212
+ }
213
+
214
+ // Write all chunks sequentially
215
+ for (const chunkData of chunks) {
216
+ writeStream.write(chunkData);
217
+ }
218
+
219
+ // Remove temp files
220
+ for (let i = 0; i < totalChunksNum; i++) {
221
+ const partPath = _path.default.join(sessionFolder, `${filename}.part${i}`);
205
222
  _fsExtra.default.remove(partPath).catch(err => (0, _utils.reportError)(`Error removing part ${partPath}: ${err.message}`));
206
223
  }
207
224
  writeStream.end();
208
225
  return new Promise((resolve, reject) => {
209
- writeStream.on('finish', () => resolve(undefined));
210
- writeStream.on('error', error => reject(new _WebError.WebError({
211
- statusCode: 500,
212
- errorCode: 'file-merge-error',
213
- message: 'Error merging file chunks',
214
- data: {
215
- error: error.message
216
- }
217
- })));
226
+ writeStream.on('finish', () => {
227
+ // Clean up temp folder after successful merge
228
+ _fsExtra.default.remove(sessionFolder).then(() => resolve(undefined)).catch(cleanupErr => {
229
+ (0, _utils.reportError)(`Error cleaning up temp folder ${sessionFolder}: ${cleanupErr.message}`);
230
+ resolve(undefined); // Don't fail the merge if cleanup fails
231
+ });
232
+ });
233
+ writeStream.on('error', error => {
234
+ // Cleanup on write error
235
+ _fsExtra.default.remove(sessionFolder).catch(cleanupErr => (0, _utils.reportError)(`Error cleaning up folder after write error: ${cleanupErr.message}`));
236
+ reject(new _WebError.WebError({
237
+ statusCode: 500,
238
+ errorCode: 'file-merge-error',
239
+ message: 'Error merging file chunks',
240
+ data: {
241
+ error: error.message
242
+ }
243
+ }));
244
+ });
218
245
  });
219
246
  }
220
247
  //# sourceMappingURL=multiPartFileUploader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"multiPartFileUploader.js","names":["_multer","_interopRequireDefault","require","_fsExtra","_path","_utils","_WebError","_validate","namespace","TEMP_DIR","path","join","process","env","FILE_STORAGE_FOLDER","FILE_DIR","fs","ensureDirSync","storage","multer","memoryStorage","multiPartFileUploader","filesOption","validator","req","res","next","reportDebug","message","uploadSingle","single","err","reportError","WebError","statusCode","errorCode","data","error","validate","filename","totalChunks","chunkIndex","uniqueID","body","undefined","file","receivedChunk","maxSize","size","parseInt","receivedSize","sessionFolder","chunkPath","writeFile","buffer","writeErr","uploadedChunks","readdir","length","finalFilePath","mergeChunks","multipartFile","isPending","originalname","filePath","mergeErr","writeStream","createWriteStream","i","partPath","chunkData","readFile","write","remove","catch","end","Promise","resolve","reject","on"],"sources":["../../../src/middleware/multiPartFileUploader/multiPartFileUploader.ts"],"sourcesContent":["import { Request, Response, NextFunction } from 'express';\nimport multer from 'multer';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { Schema } from 'express-validator';\nimport { reportDebug, reportError } from 'utils';\nimport { WebError } from 'entities/WebError';\nimport { validate } from './validate';\nimport { MultiPartFileUploaderConfig } from './multiPartFileUploader.types';\n\nconst namespace = 'engine:middleware:multiPartFileUploader';\n\nconst TEMP_DIR: string = path.join(\n process.env.FILE_STORAGE_FOLDER || './uploads',\n 'temp_chunks'\n);\nconst FILE_DIR: string = path.join(\n process.env.FILE_STORAGE_FOLDER || './uploads',\n 'completed_files'\n);\n\nfs.ensureDirSync(FILE_DIR);\nfs.ensureDirSync(TEMP_DIR);\n\nconst storage = multer.memoryStorage();\n\nexport function multiPartFileUploader(\n filesOption: MultiPartFileUploaderConfig,\n validator: Schema\n) {\n return async (\n req: Request,\n res: Response,\n next: NextFunction\n ): Promise<void> => {\n try {\n reportDebug({ namespace, message: 'Starting file upload middleware' });\n\n const uploadSingle = multer({ storage }).single('file');\n uploadSingle(req, res, async (err) => {\n if (err) {\n reportError('Error uploading chunk');\n return next(\n new WebError({\n statusCode: 500,\n errorCode: 'upload-failed',\n message: 'Error uploading file chunk',\n data: { error: err.message }\n })\n );\n }\n\n try {\n reportDebug({ namespace, message: 'Validating request' });\n await validate(req, validator);\n } catch (error) {\n reportDebug({\n namespace,\n message: 'Validation failed',\n data: { error }\n });\n return next(\n new WebError({\n statusCode: 400,\n errorCode: 'validation-failed',\n message: 'Validation failed for file upload',\n data: { error: (error as Error).message }\n })\n );\n }\n\n const { filename, totalChunks, chunkIndex, uniqueID } = req.body;\n reportDebug({\n namespace,\n message: 'Received chunk data',\n data: { filename, totalChunks, chunkIndex, uniqueID }\n });\n\n if (\n !filename ||\n !totalChunks ||\n chunkIndex === undefined ||\n !uniqueID ||\n !req.file\n ) {\n return next(\n new WebError({\n statusCode: 400,\n errorCode: 'missing-field',\n message: 'Missing required fields',\n data: {\n filename,\n totalChunks,\n chunkIndex,\n uniqueID,\n receivedChunk: !!req.file\n }\n })\n );\n }\n\n if (\n filesOption?.maxSize &&\n req.file.size > parseInt(filesOption?.maxSize, 10)\n ) {\n return next(\n new WebError({\n statusCode: 400,\n errorCode: 'chunk-too-large',\n message: 'Uploaded chunk exceeds the maximum allowed size',\n data: {\n maxSize: filesOption.maxSize,\n receivedSize: req.file.size\n }\n })\n );\n }\n\n const sessionFolder = path.join(TEMP_DIR, uniqueID);\n fs.ensureDirSync(sessionFolder);\n const chunkPath = path.join(\n sessionFolder,\n `${filename}.part${chunkIndex}`\n );\n\n try {\n reportDebug({\n namespace,\n message: 'Writing chunk to disk',\n data: { chunkPath }\n });\n await fs.writeFile(chunkPath, req.file.buffer);\n } catch (writeErr) {\n return next(\n new WebError({\n statusCode: 500,\n errorCode: 'chunk-write-failed',\n message: 'Failed to write chunk',\n data: { chunkIndex, error: (writeErr as Error).message }\n })\n );\n }\n\n const uploadedChunks = await fs.readdir(sessionFolder);\n reportDebug({\n namespace,\n message: 'Uploaded chunk count',\n data: { uploadedChunks: uploadedChunks.length, totalChunks }\n });\n\n if (uploadedChunks.length === parseInt(totalChunks, 10)) {\n try {\n const finalFilePath = path.join(\n FILE_DIR,\n `${uniqueID}_${filename}`\n );\n await mergeChunks(\n filename,\n totalChunks,\n sessionFolder,\n finalFilePath\n );\n req.multipartFile = {\n isPending: false,\n originalname: filename,\n uniqueID,\n filePath: finalFilePath\n };\n } catch (mergeErr) {\n return next(\n new WebError({\n statusCode: 500,\n errorCode: 'file-merge-failed',\n message: 'Error merging file chunks',\n data: { error: (mergeErr as Error).message }\n })\n );\n }\n return next();\n }\n\n req.multipartFile = {\n isPending: true,\n originalname: filename,\n uniqueID,\n chunkIndex,\n totalChunks\n };\n next();\n });\n } catch (error) {\n next(\n new WebError({\n statusCode: 500,\n errorCode: 'unexpected-error',\n message: 'Unexpected error in file upload',\n data: { error: (error as Error).message }\n })\n );\n }\n };\n}\n\nasync function mergeChunks(\n filename: string,\n totalChunks: string | number,\n sessionFolder: string,\n finalFilePath: string\n): Promise<void> {\n const writeStream = fs.createWriteStream(finalFilePath);\n reportDebug({\n namespace,\n message: 'Merging file chunks',\n data: { finalFilePath }\n });\n\n for (let i = 0; i < parseInt(totalChunks as string, 10); i++) {\n const partPath = path.join(sessionFolder, `${filename}.part${i}`);\n try {\n const chunkData = await fs.readFile(partPath);\n writeStream.write(chunkData);\n } catch (error) {\n throw new WebError({\n statusCode: 500,\n errorCode: 'chunk-read-failed',\n message: `Error reading chunk ${i}`,\n data: { error: (error as Error).message }\n });\n }\n fs.remove(partPath).catch((err: Error) =>\n reportError(`Error removing part ${partPath}: ${err.message}`)\n );\n }\n\n writeStream.end();\n return new Promise<void>((resolve, reject) => {\n writeStream.on('finish', () => resolve(undefined));\n writeStream.on('error', (error) =>\n reject(\n new WebError({\n statusCode: 500,\n errorCode: 'file-merge-error',\n message: 'Error merging file chunks',\n data: { error: (error as Error).message }\n })\n )\n );\n });\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"multiPartFileUploader.js","names":["_multer","_interopRequireDefault","require","_fsExtra","_path","_utils","_WebError","_validate","_bytes","namespace","TEMP_DIR","path","join","process","env","FILE_STORAGE_FOLDER","FILE_DIR","fs","ensureDirSync","storage","multer","memoryStorage","multiPartFileUploader","filesOption","validator","req","res","next","reportDebug","message","uploadSingle","single","err","reportError","WebError","statusCode","errorCode","data","error","validate","filename","totalChunks","chunkIndex","uniqueID","body","undefined","file","receivedChunk","maxSize","size","bytes","receivedSize","sessionFolder","chunkPath","writeFile","buffer","writeErr","uploadedChunks","readdir","length","parseInt","finalFilePath","mergeChunks","multipartFile","isPending","originalname","filePath","mergeErr","writeStream","createWriteStream","totalChunksNum","chunks","i","partPath","chunkData","readFile","push","remove","catch","cleanupErr","write","end","Promise","resolve","reject","on","then"],"sources":["../../../src/middleware/multiPartFileUploader/multiPartFileUploader.ts"],"sourcesContent":["import { Request, Response, NextFunction } from 'express';\nimport multer from 'multer';\nimport fs from 'fs-extra';\nimport path from 'path';\nimport { Schema } from 'express-validator';\nimport { reportDebug, reportError } from 'utils';\nimport { WebError } from 'entities/WebError';\nimport { validate } from './validate';\nimport { MultiPartFileUploaderConfig } from './multiPartFileUploader.types';\nimport bytes from 'bytes';\n\nconst namespace = 'engine:middleware:multiPartFileUploader';\n\nconst TEMP_DIR: string = path.join(\n process.env.FILE_STORAGE_FOLDER || './uploads',\n 'temp_chunks'\n);\nconst FILE_DIR: string = path.join(\n process.env.FILE_STORAGE_FOLDER || './uploads',\n 'completed_files'\n);\n\nfs.ensureDirSync(FILE_DIR);\nfs.ensureDirSync(TEMP_DIR);\n\nconst storage = multer.memoryStorage();\n\nexport function multiPartFileUploader(\n filesOption: MultiPartFileUploaderConfig,\n validator: Schema\n) {\n return async (\n req: Request,\n res: Response,\n next: NextFunction\n ): Promise<void> => {\n try {\n reportDebug({ namespace, message: 'Starting file upload middleware' });\n\n const uploadSingle = multer({ storage }).single('file');\n uploadSingle(req, res, async (err) => {\n if (err) {\n reportError('Error uploading chunk');\n return next(\n new WebError({\n statusCode: 500,\n errorCode: 'upload-failed',\n message: 'Error uploading file chunk',\n data: { error: err.message }\n })\n );\n }\n\n try {\n reportDebug({ namespace, message: 'Validating request' });\n await validate(req, validator);\n } catch (error) {\n reportDebug({\n namespace,\n message: 'Validation failed',\n data: { error }\n });\n return next(\n new WebError({\n statusCode: 400,\n errorCode: 'validation-failed',\n message: 'Validation failed for file upload',\n data: { error: (error as Error).message }\n })\n );\n }\n\n const { filename, totalChunks, chunkIndex, uniqueID } = req.body;\n reportDebug({\n namespace,\n message: 'Received chunk data',\n data: { filename, totalChunks, chunkIndex, uniqueID }\n });\n\n if (\n !filename ||\n !totalChunks ||\n chunkIndex === undefined ||\n !uniqueID ||\n !req.file\n ) {\n return next(\n new WebError({\n statusCode: 400,\n errorCode: 'missing-field',\n message: 'Missing required fields',\n data: {\n filename,\n totalChunks,\n chunkIndex,\n uniqueID,\n receivedChunk: !!req.file\n }\n })\n );\n }\n\n if (\n filesOption?.maxSize &&\n req.file.size > (bytes(filesOption.maxSize) || 0)\n ) {\n return next(\n new WebError({\n statusCode: 400,\n errorCode: 'chunk-too-large',\n message: 'Uploaded chunk exceeds the maximum allowed size',\n data: {\n maxSize: filesOption.maxSize,\n receivedSize: req.file.size\n }\n })\n );\n }\n\n const sessionFolder = path.join(TEMP_DIR, uniqueID);\n fs.ensureDirSync(sessionFolder);\n const chunkPath = path.join(\n sessionFolder,\n `${filename}.part${chunkIndex}`\n );\n\n try {\n reportDebug({\n namespace,\n message: 'Writing chunk to disk',\n data: { chunkPath }\n });\n await fs.writeFile(chunkPath, req.file.buffer);\n } catch (writeErr) {\n return next(\n new WebError({\n statusCode: 500,\n errorCode: 'chunk-write-failed',\n message: 'Failed to write chunk',\n data: { chunkIndex, error: (writeErr as Error).message }\n })\n );\n }\n\n const uploadedChunks = await fs.readdir(sessionFolder);\n reportDebug({\n namespace,\n message: 'Uploaded chunk count',\n data: { uploadedChunks: uploadedChunks.length, totalChunks }\n });\n\n if (uploadedChunks.length === parseInt(totalChunks, 10)) {\n try {\n const finalFilePath = path.join(\n FILE_DIR,\n `${uniqueID}_${filename}`\n );\n await mergeChunks(\n filename,\n totalChunks,\n sessionFolder,\n finalFilePath\n );\n req.multipartFile = {\n isPending: false,\n originalname: filename,\n uniqueID,\n filePath: finalFilePath\n };\n } catch (mergeErr) {\n return next(\n new WebError({\n statusCode: 500,\n errorCode: 'file-merge-failed',\n message: 'Error merging file chunks',\n data: { error: (mergeErr as Error).message }\n })\n );\n }\n return next();\n }\n\n req.multipartFile = {\n isPending: true,\n originalname: filename,\n uniqueID,\n chunkIndex,\n totalChunks\n };\n next();\n });\n } catch (error) {\n next(\n new WebError({\n statusCode: 500,\n errorCode: 'unexpected-error',\n message: 'Unexpected error in file upload',\n data: { error: (error as Error).message }\n })\n );\n }\n };\n}\n\nasync function mergeChunks(\n filename: string,\n totalChunks: string | number,\n sessionFolder: string,\n finalFilePath: string\n): Promise<void> {\n const writeStream = fs.createWriteStream(finalFilePath);\n reportDebug({\n namespace,\n message: 'Merging file chunks',\n data: { finalFilePath }\n });\n\n const totalChunksNum = parseInt(totalChunks as string, 10);\n const chunks: Buffer[] = [];\n\n // Read all chunks into memory first\n for (let i = 0; i < totalChunksNum; i++) {\n const partPath = path.join(sessionFolder, `${filename}.part${i}`);\n try {\n const chunkData = await fs.readFile(partPath);\n chunks.push(chunkData);\n } catch (error) {\n // Cleanup on error\n await fs.remove(sessionFolder).catch((cleanupErr: Error) =>\n reportError(`Error cleaning up folder ${sessionFolder}: ${cleanupErr.message}`)\n );\n throw new WebError({\n statusCode: 500,\n errorCode: 'chunk-read-failed',\n message: `Error reading chunk ${i}`,\n data: { error: (error as Error).message }\n });\n }\n }\n\n // Write all chunks sequentially\n for (const chunkData of chunks) {\n writeStream.write(chunkData);\n }\n\n // Remove temp files\n for (let i = 0; i < totalChunksNum; i++) {\n const partPath = path.join(sessionFolder, `${filename}.part${i}`);\n fs.remove(partPath).catch((err: Error) =>\n reportError(`Error removing part ${partPath}: ${err.message}`)\n );\n }\n\n writeStream.end();\n return new Promise<void>((resolve, reject) => {\n writeStream.on('finish', () => {\n // Clean up temp folder after successful merge\n fs.remove(sessionFolder)\n .then(() => resolve(undefined))\n .catch((cleanupErr: Error) => {\n reportError(\n `Error cleaning up temp folder ${sessionFolder}: ${cleanupErr.message}`\n );\n resolve(undefined); // Don't fail the merge if cleanup fails\n });\n });\n writeStream.on('error', (error) => {\n // Cleanup on write error\n fs.remove(sessionFolder).catch((cleanupErr: Error) =>\n reportError(`Error cleaning up folder after write error: ${cleanupErr.message}`)\n );\n reject(\n new WebError({\n statusCode: 500,\n errorCode: 'file-merge-error',\n message: 'Error merging file chunks',\n data: { error: (error as Error).message }\n })\n );\n });\n });\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,2 +1,3 @@
1
- /** Middleware that gets metrics on each request */
2
- export declare const requestMetrics: import("express").RequestHandler<import("express-serve-static-core").ParamsDictionary, any, any, import("qs").ParsedQs, Record<string, any>>;
1
+ import { Request, Response, NextFunction } from 'express';
2
+ /** Composite middleware that tracks in-flight requests, errors, response size and timings */
3
+ export declare const requestMetrics: (request: Request, response: Response, next: NextFunction) => void;
@@ -7,15 +7,78 @@ Object.defineProperty(exports, "__esModule", {
7
7
  exports.requestMetrics = void 0;
8
8
  var _responseTime = _interopRequireDefault(require("response-time"));
9
9
  var _metrics = require("../../metrics");
10
- /** Middleware that gets metrics on each request */
11
- const requestMetrics = exports.requestMetrics = (0, _responseTime.default)((request, response, time) => {
12
- const labels = {
13
- path: request.route?.path ?? request.path,
14
- method: request.method,
15
- status: response.statusCode ? `${response.statusCode.toString().substring(0, 1)}XX` : '',
16
- service: process.env.CHART
10
+ const excludedPrefixes = ['/swagger', '/docs', '/api-docs', '/metrics', '/favicon.ico', '/.well-known'];
11
+
12
+ /** Composite middleware that tracks in-flight requests, errors, response size and timings */
13
+ const requestMetrics = (request, response, next) => {
14
+ const path = request.route?.path ?? request.path;
15
+
16
+ // Skip operational/static routes
17
+ for (const prefix of excludedPrefixes) {
18
+ if (path.startsWith(prefix)) return next();
19
+ }
20
+
21
+ // Increment in-flight
22
+ try {
23
+ _metrics.httpInFlight.inc();
24
+ } catch {}
25
+
26
+ // Ensure we decrement on finish/close (only once)
27
+ let inflightDecremented = false;
28
+ const cleanup = () => {
29
+ if (inflightDecremented) return;
30
+ inflightDecremented = true;
31
+ try {
32
+ _metrics.httpInFlight.dec();
33
+ } catch {}
17
34
  };
18
- _metrics.httpRequestCount.inc(labels);
19
- _metrics.httpRequestDuration.observe(labels, time / 1000);
20
- });
35
+ response.once('finish', () => {
36
+ // record errors
37
+ const statusCode = response.statusCode ?? 0;
38
+ const statusClass = statusCode ? `${statusCode.toString().substring(0, 1)}XX` : '';
39
+ if (statusCode >= 400) {
40
+ try {
41
+ _metrics.httpErrorCount.inc({
42
+ service: process.env.CHART,
43
+ status: statusClass,
44
+ method: request.method
45
+ });
46
+ } catch {}
47
+ }
48
+
49
+ // observe response size if content-length header available
50
+ const lenHeader = response.getHeader('content-length');
51
+ const len = lenHeader ? parseInt(String(lenHeader), 10) : NaN;
52
+ if (!Number.isNaN(len)) {
53
+ try {
54
+ _metrics.httpResponseSize.observe({
55
+ service: process.env.CHART,
56
+ path,
57
+ method: request.method,
58
+ status: statusClass
59
+ }, len);
60
+ } catch {}
61
+ }
62
+ cleanup();
63
+ });
64
+ response.once('close', cleanup);
65
+
66
+ // Use response-time to record duration and request counts
67
+ const rt = (0, _responseTime.default)((req, res, time) => {
68
+ const labels = {
69
+ path,
70
+ method: request.method,
71
+ status: res.statusCode ? `${res.statusCode.toString().substring(0, 1)}XX` : '',
72
+ service: process.env.CHART || 'unknown'
73
+ };
74
+ try {
75
+ _metrics.httpRequestCount.inc(labels);
76
+ _metrics.httpRequestDuration.observe(labels, time / 1000);
77
+ } catch {}
78
+ });
79
+
80
+ // Call the response-time middleware which will call next when done
81
+ rt(request, response, next);
82
+ };
83
+ exports.requestMetrics = requestMetrics;
21
84
  //# sourceMappingURL=requestMetrics.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"requestMetrics.js","names":["_responseTime","_interopRequireDefault","require","_metrics","requestMetrics","exports","responseTime","request","response","time","labels","path","route","method","status","statusCode","toString","substring","service","process","env","CHART","httpRequestCount","inc","httpRequestDuration","observe"],"sources":["../../../src/middleware/requestMetrics/requestMetrics.ts"],"sourcesContent":["import { Request, Response } from 'express';\nimport responseTime from 'response-time';\nimport { httpRequestCount, httpRequestDuration } from 'metrics';\n\n/** Middleware that gets metrics on each request */\nexport const requestMetrics = responseTime(\n (request: Request, response: Response, time: number) => {\n const labels = {\n path: (request.route?.path ?? request.path) as string,\n method: request.method,\n status: response.statusCode\n ? `${response.statusCode.toString().substring(0, 1)}XX`\n : '',\n service: process.env.CHART\n };\n\n httpRequestCount.inc(labels);\n httpRequestDuration.observe(labels, time / 1000);\n }\n);\n"],"mappings":";;;;;;;AACA,IAAAA,aAAA,GAAAC,sBAAA,CAAAC,OAAA;AACA,IAAAC,QAAA,GAAAD,OAAA;AAEA;AACO,MAAME,cAAc,GAAAC,OAAA,CAAAD,cAAA,GAAG,IAAAE,qBAAY,EACxC,CAACC,OAAgB,EAAEC,QAAkB,EAAEC,IAAY,KAAK;EACtD,MAAMC,MAAM,GAAG;IACbC,IAAI,EAAGJ,OAAO,CAACK,KAAK,EAAED,IAAI,IAAIJ,OAAO,CAACI,IAAe;IACrDE,MAAM,EAAEN,OAAO,CAACM,MAAM;IACtBC,MAAM,EAAEN,QAAQ,CAACO,UAAU,GACvB,GAAGP,QAAQ,CAACO,UAAU,CAACC,QAAQ,CAAC,CAAC,CAACC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,GACrD,EAAE;IACNC,OAAO,EAAEC,OAAO,CAACC,GAAG,CAACC;EACvB,CAAC;EAEDC,yBAAgB,CAACC,GAAG,CAACb,MAAM,CAAC;EAC5Bc,4BAAmB,CAACC,OAAO,CAACf,MAAM,EAAED,IAAI,GAAG,IAAI,CAAC;AAClD,CACF,CAAC","ignoreList":[]}
1
+ {"version":3,"file":"requestMetrics.js","names":["_responseTime","_interopRequireDefault","require","_metrics","excludedPrefixes","requestMetrics","request","response","next","path","route","prefix","startsWith","httpInFlight","inc","inflightDecremented","cleanup","dec","once","statusCode","statusClass","toString","substring","httpErrorCount","service","process","env","CHART","status","method","lenHeader","getHeader","len","parseInt","String","NaN","Number","isNaN","httpResponseSize","observe","rt","responseTime","req","res","time","labels","httpRequestCount","httpRequestDuration","exports"],"sources":["../../../src/middleware/requestMetrics/requestMetrics.ts"],"sourcesContent":["import { Request, Response, NextFunction } from 'express';\nimport responseTime from 'response-time';\nimport {\n httpRequestCount,\n httpRequestDuration,\n httpInFlight,\n httpErrorCount,\n httpResponseSize\n} from 'metrics';\n\nconst excludedPrefixes = ['/swagger', '/docs', '/api-docs', '/metrics', '/favicon.ico', '/.well-known'];\n\n/** Composite middleware that tracks in-flight requests, errors, response size and timings */\nexport const requestMetrics = (request: Request, response: Response, next: NextFunction) => {\n const path = (request.route?.path ?? request.path) as string;\n\n // Skip operational/static routes\n for (const prefix of excludedPrefixes) {\n if (path.startsWith(prefix)) return next();\n }\n\n // Increment in-flight\n try {\n httpInFlight.inc();\n } catch {}\n\n // Ensure we decrement on finish/close (only once)\n let inflightDecremented = false;\n const cleanup = () => {\n if (inflightDecremented) return;\n inflightDecremented = true;\n try {\n httpInFlight.dec();\n } catch {}\n };\n\n response.once('finish', () => {\n // record errors\n const statusCode = response.statusCode ?? 0;\n const statusClass = statusCode ? `${statusCode.toString().substring(0, 1)}XX` : '';\n if (statusCode >= 400) {\n try {\n httpErrorCount.inc({ service: process.env.CHART, status: statusClass, method: request.method });\n } catch {}\n }\n\n // observe response size if content-length header available\n const lenHeader = response.getHeader('content-length');\n const len = lenHeader ? parseInt(String(lenHeader), 10) : NaN;\n if (!Number.isNaN(len)) {\n try {\n httpResponseSize.observe({ service: process.env.CHART, path, method: request.method, status: statusClass }, len);\n } catch {}\n }\n\n cleanup();\n });\n\n response.once('close', cleanup);\n\n // Use response-time to record duration and request counts\n const rt = responseTime((req: Request, res: Response, time: number) => {\n const labels = {\n path,\n method: request.method,\n status: res.statusCode ? `${res.statusCode.toString().substring(0, 1)}XX` : '',\n service: process.env.CHART || 'unknown'\n };\n\n try {\n httpRequestCount.inc(labels);\n httpRequestDuration.observe(labels, time / 1000);\n } catch {}\n });\n\n // Call the response-time middleware which will call next when done\n rt(request as Request, response as Response, next as NextFunction);\n};\n"],"mappings":"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","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-server-engine",
3
- "version": "1.10.9",
3
+ "version": "1.10.11",
4
4
  "description": "Framework used to develop backend services. This package ships with a lot of features to standardize the creation of services, letting you focus on the business logic.",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {