apitally 0.11.5 → 0.13.0

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 (101) hide show
  1. package/README.md +43 -2
  2. package/dist/adonisjs/index.cjs +41 -0
  3. package/dist/adonisjs/index.cjs.map +1 -0
  4. package/dist/adonisjs/index.d.cts +14 -0
  5. package/dist/adonisjs/index.d.ts +14 -0
  6. package/dist/adonisjs/index.js +17 -0
  7. package/dist/adonisjs/index.js.map +1 -0
  8. package/dist/adonisjs/middleware.cjs +214 -0
  9. package/dist/adonisjs/middleware.cjs.map +1 -0
  10. package/dist/adonisjs/middleware.d.cts +21 -0
  11. package/dist/adonisjs/middleware.d.ts +21 -0
  12. package/dist/adonisjs/middleware.js +185 -0
  13. package/dist/adonisjs/middleware.js.map +1 -0
  14. package/dist/adonisjs/provider.cjs +1083 -0
  15. package/dist/adonisjs/provider.cjs.map +1 -0
  16. package/dist/adonisjs/provider.d.cts +28 -0
  17. package/dist/adonisjs/provider.d.ts +28 -0
  18. package/dist/adonisjs/provider.js +1057 -0
  19. package/dist/adonisjs/provider.js.map +1 -0
  20. package/dist/common/client.cjs +117 -106
  21. package/dist/common/client.cjs.map +1 -1
  22. package/dist/common/client.js +112 -101
  23. package/dist/common/client.js.map +1 -1
  24. package/dist/common/consumerRegistry.cjs.map +1 -1
  25. package/dist/common/consumerRegistry.js.map +1 -1
  26. package/dist/common/logging.cjs.map +1 -1
  27. package/dist/common/logging.js.map +1 -1
  28. package/dist/common/packageVersions.cjs.map +1 -1
  29. package/dist/common/packageVersions.js.map +1 -1
  30. package/dist/common/paramValidation.cjs.map +1 -1
  31. package/dist/common/paramValidation.js.map +1 -1
  32. package/dist/common/requestCounter.cjs.map +1 -1
  33. package/dist/common/requestCounter.js.map +1 -1
  34. package/dist/common/requestLogger.cjs +55 -2
  35. package/dist/common/requestLogger.cjs.map +1 -1
  36. package/dist/common/requestLogger.d.cts +2 -1
  37. package/dist/common/requestLogger.d.ts +2 -1
  38. package/dist/common/requestLogger.js +55 -2
  39. package/dist/common/requestLogger.js.map +1 -1
  40. package/dist/common/sentry.cjs +55 -0
  41. package/dist/common/sentry.cjs.map +1 -0
  42. package/dist/common/sentry.d.cts +6 -0
  43. package/dist/common/sentry.d.ts +6 -0
  44. package/dist/common/sentry.js +22 -0
  45. package/dist/common/sentry.js.map +1 -0
  46. package/dist/common/serverErrorCounter.cjs +58 -45
  47. package/dist/common/serverErrorCounter.cjs.map +1 -1
  48. package/dist/common/serverErrorCounter.d.cts +3 -6
  49. package/dist/common/serverErrorCounter.d.ts +3 -6
  50. package/dist/common/serverErrorCounter.js +53 -45
  51. package/dist/common/serverErrorCounter.js.map +1 -1
  52. package/dist/common/tempGzipFile.cjs.map +1 -1
  53. package/dist/common/tempGzipFile.js.map +1 -1
  54. package/dist/common/types.cjs.map +1 -1
  55. package/dist/common/utils.cjs +48 -0
  56. package/dist/common/utils.cjs.map +1 -0
  57. package/dist/common/utils.d.cts +5 -0
  58. package/dist/common/utils.d.ts +5 -0
  59. package/dist/common/utils.js +25 -0
  60. package/dist/common/utils.js.map +1 -0
  61. package/dist/common/validationErrorCounter.cjs.map +1 -1
  62. package/dist/common/validationErrorCounter.js.map +1 -1
  63. package/dist/express/index.cjs +143 -111
  64. package/dist/express/index.cjs.map +1 -1
  65. package/dist/express/index.js +138 -106
  66. package/dist/express/index.js.map +1 -1
  67. package/dist/express/middleware.cjs +143 -111
  68. package/dist/express/middleware.cjs.map +1 -1
  69. package/dist/express/middleware.js +138 -106
  70. package/dist/express/middleware.js.map +1 -1
  71. package/dist/express/utils.cjs.map +1 -1
  72. package/dist/express/utils.js.map +1 -1
  73. package/dist/fastify/index.cjs +139 -112
  74. package/dist/fastify/index.cjs.map +1 -1
  75. package/dist/fastify/index.js +134 -107
  76. package/dist/fastify/index.js.map +1 -1
  77. package/dist/fastify/plugin.cjs +139 -112
  78. package/dist/fastify/plugin.cjs.map +1 -1
  79. package/dist/fastify/plugin.js +134 -107
  80. package/dist/fastify/plugin.js.map +1 -1
  81. package/dist/hono/index.cjs +138 -108
  82. package/dist/hono/index.cjs.map +1 -1
  83. package/dist/hono/index.js +133 -103
  84. package/dist/hono/index.js.map +1 -1
  85. package/dist/hono/middleware.cjs +138 -108
  86. package/dist/hono/middleware.cjs.map +1 -1
  87. package/dist/hono/middleware.js +133 -103
  88. package/dist/hono/middleware.js.map +1 -1
  89. package/dist/koa/index.cjs +122 -109
  90. package/dist/koa/index.cjs.map +1 -1
  91. package/dist/koa/index.js +117 -104
  92. package/dist/koa/index.js.map +1 -1
  93. package/dist/koa/middleware.cjs +122 -109
  94. package/dist/koa/middleware.cjs.map +1 -1
  95. package/dist/koa/middleware.js +117 -104
  96. package/dist/koa/middleware.js.map +1 -1
  97. package/dist/nestjs/index.cjs +143 -111
  98. package/dist/nestjs/index.cjs.map +1 -1
  99. package/dist/nestjs/index.js +138 -106
  100. package/dist/nestjs/index.js.map +1 -1
  101. package/package.json +46 -1
@@ -215,14 +215,116 @@ var RequestCounter = _RequestCounter;
215
215
  // src/common/requestLogger.ts
216
216
  var import_async_lock = __toESM(require("async-lock"), 1);
217
217
  var import_buffer2 = require("buffer");
218
- var import_crypto2 = require("crypto");
218
+ var import_crypto3 = require("crypto");
219
219
  var import_fs2 = require("fs");
220
220
  var import_os2 = require("os");
221
221
  var import_path2 = require("path");
222
222
 
223
+ // src/common/sentry.ts
224
+ var sentry;
225
+ (async () => {
226
+ try {
227
+ sentry = await import("@sentry/node");
228
+ } catch (e) {
229
+ }
230
+ })();
231
+ function getSentryEventId() {
232
+ if (sentry && sentry.lastEventId) {
233
+ return sentry.lastEventId();
234
+ }
235
+ return void 0;
236
+ }
237
+ __name(getSentryEventId, "getSentryEventId");
238
+
239
+ // src/common/serverErrorCounter.ts
240
+ var import_crypto = require("crypto");
241
+ var MAX_MSG_LENGTH = 2048;
242
+ var MAX_STACKTRACE_LENGTH = 65536;
243
+ var _ServerErrorCounter = class _ServerErrorCounter {
244
+ errorCounts;
245
+ errorDetails;
246
+ sentryEventIds;
247
+ constructor() {
248
+ this.errorCounts = /* @__PURE__ */ new Map();
249
+ this.errorDetails = /* @__PURE__ */ new Map();
250
+ this.sentryEventIds = /* @__PURE__ */ new Map();
251
+ }
252
+ addServerError(serverError) {
253
+ const key = this.getKey(serverError);
254
+ if (!this.errorDetails.has(key)) {
255
+ this.errorDetails.set(key, serverError);
256
+ }
257
+ this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
258
+ const sentryEventId = getSentryEventId();
259
+ if (sentryEventId) {
260
+ this.sentryEventIds.set(key, sentryEventId);
261
+ }
262
+ }
263
+ getAndResetServerErrors() {
264
+ const data = [];
265
+ this.errorCounts.forEach((count, key) => {
266
+ const serverError = this.errorDetails.get(key);
267
+ if (serverError) {
268
+ data.push({
269
+ consumer: serverError.consumer || null,
270
+ method: serverError.method,
271
+ path: serverError.path,
272
+ type: serverError.type,
273
+ msg: truncateExceptionMessage(serverError.msg),
274
+ traceback: truncateExceptionStackTrace(serverError.traceback),
275
+ sentry_event_id: this.sentryEventIds.get(key) || null,
276
+ error_count: count
277
+ });
278
+ }
279
+ });
280
+ this.errorCounts.clear();
281
+ this.errorDetails.clear();
282
+ return data;
283
+ }
284
+ getKey(serverError) {
285
+ const hashInput = [
286
+ serverError.consumer || "",
287
+ serverError.method.toUpperCase(),
288
+ serverError.path,
289
+ serverError.type,
290
+ serverError.msg.trim(),
291
+ serverError.traceback.trim()
292
+ ].join("|");
293
+ return (0, import_crypto.createHash)("md5").update(hashInput).digest("hex");
294
+ }
295
+ };
296
+ __name(_ServerErrorCounter, "ServerErrorCounter");
297
+ var ServerErrorCounter = _ServerErrorCounter;
298
+ function truncateExceptionMessage(msg) {
299
+ if (msg.length <= MAX_MSG_LENGTH) {
300
+ return msg;
301
+ }
302
+ const suffix = "... (truncated)";
303
+ const cutoff = MAX_MSG_LENGTH - suffix.length;
304
+ return msg.substring(0, cutoff) + suffix;
305
+ }
306
+ __name(truncateExceptionMessage, "truncateExceptionMessage");
307
+ function truncateExceptionStackTrace(stack) {
308
+ const suffix = "... (truncated) ...";
309
+ const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
310
+ const lines = stack.trim().split("\n");
311
+ const truncatedLines = [];
312
+ let length = 0;
313
+ for (const line of lines) {
314
+ if (length + line.length + 1 > cutoff) {
315
+ truncatedLines.push(suffix);
316
+ break;
317
+ }
318
+ truncatedLines.push(line);
319
+ length += line.length + 1;
320
+ }
321
+ return truncatedLines.join("\n");
322
+ }
323
+ __name(truncateExceptionStackTrace, "truncateExceptionStackTrace");
324
+
223
325
  // src/common/tempGzipFile.ts
224
326
  var import_buffer = require("buffer");
225
- var import_crypto = require("crypto");
327
+ var import_crypto2 = require("crypto");
226
328
  var import_fs = require("fs");
227
329
  var import_os = require("os");
228
330
  var import_path = require("path");
@@ -235,7 +337,7 @@ var _TempGzipFile = class _TempGzipFile {
235
337
  readyPromise;
236
338
  closedPromise;
237
339
  constructor() {
238
- this.uuid = (0, import_crypto.randomUUID)();
340
+ this.uuid = (0, import_crypto2.randomUUID)();
239
341
  this.filePath = (0, import_path.join)((0, import_os.tmpdir)(), `apitally-${this.uuid}.gz`);
240
342
  this.writeStream = (0, import_fs.createWriteStream)(this.filePath);
241
343
  this.readyPromise = new Promise((resolve, reject) => {
@@ -342,6 +444,7 @@ var DEFAULT_CONFIG = {
342
444
  logRequestBody: false,
343
445
  logResponseHeaders: true,
344
446
  logResponseBody: false,
447
+ logException: true,
345
448
  maskQueryParams: [],
346
449
  maskHeaders: [],
347
450
  excludePaths: []
@@ -414,7 +517,7 @@ var _RequestLogger = class _RequestLogger {
414
517
  this.shouldMaskHeader(k) ? MASKED : v
415
518
  ]);
416
519
  }
417
- logRequest(request, response) {
520
+ logRequest(request, response, error) {
418
521
  var _a3, _b, _c;
419
522
  if (!this.enabled || this.suspendUntil !== null) return;
420
523
  const url = new URL(request.url);
@@ -460,9 +563,15 @@ var _RequestLogger = class _RequestLogger {
460
563
  request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
461
564
  response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
462
565
  const item = {
463
- uuid: (0, import_crypto2.randomUUID)(),
566
+ uuid: (0, import_crypto3.randomUUID)(),
464
567
  request: skipEmptyValues(request),
465
- response: skipEmptyValues(response)
568
+ response: skipEmptyValues(response),
569
+ exception: error && this.config.logException ? {
570
+ type: error.name,
571
+ message: truncateExceptionMessage(error.message),
572
+ stacktrace: truncateExceptionStackTrace(error.stack || ""),
573
+ sentryEventId: getSentryEventId()
574
+ } : null
466
575
  };
467
576
  [
468
577
  item.request.body,
@@ -614,7 +723,7 @@ function skipEmptyValues(data) {
614
723
  __name(skipEmptyValues, "skipEmptyValues");
615
724
  function checkWritableFs() {
616
725
  try {
617
- const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto2.randomUUID)()}`);
726
+ const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto3.randomUUID)()}`);
618
727
  (0, import_fs2.writeFileSync)(testPath, "test");
619
728
  (0, import_fs2.unlinkSync)(testPath);
620
729
  return true;
@@ -624,104 +733,6 @@ function checkWritableFs() {
624
733
  }
625
734
  __name(checkWritableFs, "checkWritableFs");
626
735
 
627
- // src/common/serverErrorCounter.ts
628
- var import_crypto3 = require("crypto");
629
- var MAX_MSG_LENGTH = 2048;
630
- var MAX_STACKTRACE_LENGTH = 65536;
631
- var _ServerErrorCounter = class _ServerErrorCounter {
632
- errorCounts;
633
- errorDetails;
634
- sentryEventIds;
635
- sentry;
636
- constructor() {
637
- this.errorCounts = /* @__PURE__ */ new Map();
638
- this.errorDetails = /* @__PURE__ */ new Map();
639
- this.sentryEventIds = /* @__PURE__ */ new Map();
640
- this.tryImportSentry();
641
- }
642
- addServerError(serverError) {
643
- const key = this.getKey(serverError);
644
- if (!this.errorDetails.has(key)) {
645
- this.errorDetails.set(key, serverError);
646
- }
647
- this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
648
- this.captureSentryEventId(key);
649
- }
650
- getAndResetServerErrors() {
651
- const data = [];
652
- this.errorCounts.forEach((count, key) => {
653
- const serverError = this.errorDetails.get(key);
654
- if (serverError) {
655
- data.push({
656
- consumer: serverError.consumer || null,
657
- method: serverError.method,
658
- path: serverError.path,
659
- type: serverError.type,
660
- msg: this.getTruncatedMessage(serverError.msg),
661
- traceback: this.getTruncatedStack(serverError.traceback),
662
- sentry_event_id: this.sentryEventIds.get(key) || null,
663
- error_count: count
664
- });
665
- }
666
- });
667
- this.errorCounts.clear();
668
- this.errorDetails.clear();
669
- return data;
670
- }
671
- getKey(serverError) {
672
- const hashInput = [
673
- serverError.consumer || "",
674
- serverError.method.toUpperCase(),
675
- serverError.path,
676
- serverError.type,
677
- serverError.msg.trim(),
678
- serverError.traceback.trim()
679
- ].join("|");
680
- return (0, import_crypto3.createHash)("md5").update(hashInput).digest("hex");
681
- }
682
- getTruncatedMessage(msg) {
683
- msg = msg.trim();
684
- if (msg.length <= MAX_MSG_LENGTH) {
685
- return msg;
686
- }
687
- const suffix = "... (truncated)";
688
- const cutoff = MAX_MSG_LENGTH - suffix.length;
689
- return msg.substring(0, cutoff) + suffix;
690
- }
691
- getTruncatedStack(stack) {
692
- const suffix = "... (truncated) ...";
693
- const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
694
- const lines = stack.trim().split("\n");
695
- const truncatedLines = [];
696
- let length = 0;
697
- for (const line of lines) {
698
- if (length + line.length + 1 > cutoff) {
699
- truncatedLines.push(suffix);
700
- break;
701
- }
702
- truncatedLines.push(line);
703
- length += line.length + 1;
704
- }
705
- return truncatedLines.join("\n");
706
- }
707
- captureSentryEventId(serverErrorKey) {
708
- if (this.sentry && this.sentry.lastEventId) {
709
- const eventId = this.sentry.lastEventId();
710
- if (eventId) {
711
- this.sentryEventIds.set(serverErrorKey, eventId);
712
- }
713
- }
714
- }
715
- async tryImportSentry() {
716
- try {
717
- this.sentry = await import("@sentry/node");
718
- } catch (e) {
719
- }
720
- }
721
- };
722
- __name(_ServerErrorCounter, "ServerErrorCounter");
723
- var ServerErrorCounter = _ServerErrorCounter;
724
-
725
736
  // src/common/validationErrorCounter.ts
726
737
  var import_crypto4 = require("crypto");
727
738
  var _ValidationErrorCounter = class _ValidationErrorCounter {
@@ -807,7 +818,7 @@ var _ApitallyClient = class _ApitallyClient {
807
818
  throw new Error("Apitally client is already initialized");
808
819
  }
809
820
  if (!isValidClientId(clientId)) {
810
- throw new Error(`Invalid client ID '${clientId}' (expecting hexadeciaml UUID format)`);
821
+ throw new Error(`Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`);
811
822
  }
812
823
  if (!isValidEnv(env)) {
813
824
  throw new Error(`Invalid env '${env}' (expecting 1-32 alphanumeric lowercase characters and hyphens only)`);
@@ -1063,6 +1074,25 @@ function getPackageVersion(name) {
1063
1074
  }
1064
1075
  __name(getPackageVersion, "getPackageVersion");
1065
1076
 
1077
+ // src/common/utils.ts
1078
+ function parseContentLength(contentLength) {
1079
+ if (contentLength === void 0 || contentLength === null) {
1080
+ return void 0;
1081
+ }
1082
+ if (typeof contentLength === "number") {
1083
+ return contentLength;
1084
+ }
1085
+ if (typeof contentLength === "string") {
1086
+ const parsed = parseInt(contentLength);
1087
+ return isNaN(parsed) ? void 0 : parsed;
1088
+ }
1089
+ if (Array.isArray(contentLength)) {
1090
+ return parseContentLength(contentLength[0]);
1091
+ }
1092
+ return void 0;
1093
+ }
1094
+ __name(parseContentLength, "parseContentLength");
1095
+
1066
1096
  // src/express/utils.js
1067
1097
  var regExpToParseExpressPathRegExp = /^\/\^\\?\/?(?:(:?[\w\\.-]*(?:\\\/:?[\w\\.-]*)*)|(\(\?:\\?\/?\([^)]+\)\)))\\\/.*/;
1068
1098
  var regExpToReplaceExpressPathRegExpParams = /\(\?:\\?\/?\([^)]+\)\)/;
@@ -1228,6 +1258,8 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
1228
1258
  const path = getRoutePath(req);
1229
1259
  const consumer = getConsumer(req);
1230
1260
  client.consumerRegistry.addOrUpdateConsumer(consumer);
1261
+ const requestSize = parseContentLength(req.get("content-length"));
1262
+ const responseSize = parseContentLength(res.get("content-length"));
1231
1263
  if (path) {
1232
1264
  client.requestCounter.addRequest({
1233
1265
  consumer: consumer == null ? void 0 : consumer.identifier,
@@ -1235,8 +1267,8 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
1235
1267
  path,
1236
1268
  statusCode: res.statusCode,
1237
1269
  responseTime,
1238
- requestSize: req.get("content-length"),
1239
- responseSize: res.get("content-length")
1270
+ requestSize,
1271
+ responseSize
1240
1272
  });
1241
1273
  if ((res.statusCode === 400 || res.statusCode === 422) && res.locals.body) {
1242
1274
  let jsonBody;
@@ -1284,16 +1316,16 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
1284
1316
  path,
1285
1317
  url: `${req.protocol}://${req.host}${req.originalUrl}`,
1286
1318
  headers: convertHeaders(req.headers),
1287
- size: Number(req.get("content-length")),
1319
+ size: requestSize,
1288
1320
  consumer: consumer == null ? void 0 : consumer.identifier,
1289
1321
  body: convertBody(req.body, req.get("content-type"))
1290
1322
  }, {
1291
1323
  statusCode: res.statusCode,
1292
1324
  responseTime: responseTime / 1e3,
1293
1325
  headers: convertHeaders(res.getHeaders()),
1294
- size: Number(res.get("content-length")),
1326
+ size: responseSize,
1295
1327
  body: convertBody(res.locals.body, res.get("content-type"))
1296
- });
1328
+ }, res.locals.serverError);
1297
1329
  }
1298
1330
  } catch (error) {
1299
1331
  client.logger.error("Error while logging request in Apitally middleware.", {