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
@@ -211,14 +211,116 @@ var RequestCounter = _RequestCounter;
211
211
  // src/common/requestLogger.ts
212
212
  var import_async_lock = __toESM(require("async-lock"), 1);
213
213
  var import_buffer2 = require("buffer");
214
- var import_crypto2 = require("crypto");
214
+ var import_crypto3 = require("crypto");
215
215
  var import_fs2 = require("fs");
216
216
  var import_os2 = require("os");
217
217
  var import_path2 = require("path");
218
218
 
219
+ // src/common/sentry.ts
220
+ var sentry;
221
+ (async () => {
222
+ try {
223
+ sentry = await import("@sentry/node");
224
+ } catch (e) {
225
+ }
226
+ })();
227
+ function getSentryEventId() {
228
+ if (sentry && sentry.lastEventId) {
229
+ return sentry.lastEventId();
230
+ }
231
+ return void 0;
232
+ }
233
+ __name(getSentryEventId, "getSentryEventId");
234
+
235
+ // src/common/serverErrorCounter.ts
236
+ var import_crypto = require("crypto");
237
+ var MAX_MSG_LENGTH = 2048;
238
+ var MAX_STACKTRACE_LENGTH = 65536;
239
+ var _ServerErrorCounter = class _ServerErrorCounter {
240
+ errorCounts;
241
+ errorDetails;
242
+ sentryEventIds;
243
+ constructor() {
244
+ this.errorCounts = /* @__PURE__ */ new Map();
245
+ this.errorDetails = /* @__PURE__ */ new Map();
246
+ this.sentryEventIds = /* @__PURE__ */ new Map();
247
+ }
248
+ addServerError(serverError) {
249
+ const key = this.getKey(serverError);
250
+ if (!this.errorDetails.has(key)) {
251
+ this.errorDetails.set(key, serverError);
252
+ }
253
+ this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
254
+ const sentryEventId = getSentryEventId();
255
+ if (sentryEventId) {
256
+ this.sentryEventIds.set(key, sentryEventId);
257
+ }
258
+ }
259
+ getAndResetServerErrors() {
260
+ const data = [];
261
+ this.errorCounts.forEach((count, key) => {
262
+ const serverError = this.errorDetails.get(key);
263
+ if (serverError) {
264
+ data.push({
265
+ consumer: serverError.consumer || null,
266
+ method: serverError.method,
267
+ path: serverError.path,
268
+ type: serverError.type,
269
+ msg: truncateExceptionMessage(serverError.msg),
270
+ traceback: truncateExceptionStackTrace(serverError.traceback),
271
+ sentry_event_id: this.sentryEventIds.get(key) || null,
272
+ error_count: count
273
+ });
274
+ }
275
+ });
276
+ this.errorCounts.clear();
277
+ this.errorDetails.clear();
278
+ return data;
279
+ }
280
+ getKey(serverError) {
281
+ const hashInput = [
282
+ serverError.consumer || "",
283
+ serverError.method.toUpperCase(),
284
+ serverError.path,
285
+ serverError.type,
286
+ serverError.msg.trim(),
287
+ serverError.traceback.trim()
288
+ ].join("|");
289
+ return (0, import_crypto.createHash)("md5").update(hashInput).digest("hex");
290
+ }
291
+ };
292
+ __name(_ServerErrorCounter, "ServerErrorCounter");
293
+ var ServerErrorCounter = _ServerErrorCounter;
294
+ function truncateExceptionMessage(msg) {
295
+ if (msg.length <= MAX_MSG_LENGTH) {
296
+ return msg;
297
+ }
298
+ const suffix = "... (truncated)";
299
+ const cutoff = MAX_MSG_LENGTH - suffix.length;
300
+ return msg.substring(0, cutoff) + suffix;
301
+ }
302
+ __name(truncateExceptionMessage, "truncateExceptionMessage");
303
+ function truncateExceptionStackTrace(stack) {
304
+ const suffix = "... (truncated) ...";
305
+ const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
306
+ const lines = stack.trim().split("\n");
307
+ const truncatedLines = [];
308
+ let length = 0;
309
+ for (const line of lines) {
310
+ if (length + line.length + 1 > cutoff) {
311
+ truncatedLines.push(suffix);
312
+ break;
313
+ }
314
+ truncatedLines.push(line);
315
+ length += line.length + 1;
316
+ }
317
+ return truncatedLines.join("\n");
318
+ }
319
+ __name(truncateExceptionStackTrace, "truncateExceptionStackTrace");
320
+
219
321
  // src/common/tempGzipFile.ts
220
322
  var import_buffer = require("buffer");
221
- var import_crypto = require("crypto");
323
+ var import_crypto2 = require("crypto");
222
324
  var import_fs = require("fs");
223
325
  var import_os = require("os");
224
326
  var import_path = require("path");
@@ -231,7 +333,7 @@ var _TempGzipFile = class _TempGzipFile {
231
333
  readyPromise;
232
334
  closedPromise;
233
335
  constructor() {
234
- this.uuid = (0, import_crypto.randomUUID)();
336
+ this.uuid = (0, import_crypto2.randomUUID)();
235
337
  this.filePath = (0, import_path.join)((0, import_os.tmpdir)(), `apitally-${this.uuid}.gz`);
236
338
  this.writeStream = (0, import_fs.createWriteStream)(this.filePath);
237
339
  this.readyPromise = new Promise((resolve, reject) => {
@@ -338,6 +440,7 @@ var DEFAULT_CONFIG = {
338
440
  logRequestBody: false,
339
441
  logResponseHeaders: true,
340
442
  logResponseBody: false,
443
+ logException: true,
341
444
  maskQueryParams: [],
342
445
  maskHeaders: [],
343
446
  excludePaths: []
@@ -410,7 +513,7 @@ var _RequestLogger = class _RequestLogger {
410
513
  this.shouldMaskHeader(k) ? MASKED : v
411
514
  ]);
412
515
  }
413
- logRequest(request, response) {
516
+ logRequest(request, response, error) {
414
517
  var _a2, _b, _c;
415
518
  if (!this.enabled || this.suspendUntil !== null) return;
416
519
  const url = new URL(request.url);
@@ -456,9 +559,15 @@ var _RequestLogger = class _RequestLogger {
456
559
  request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
457
560
  response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
458
561
  const item = {
459
- uuid: (0, import_crypto2.randomUUID)(),
562
+ uuid: (0, import_crypto3.randomUUID)(),
460
563
  request: skipEmptyValues(request),
461
- response: skipEmptyValues(response)
564
+ response: skipEmptyValues(response),
565
+ exception: error && this.config.logException ? {
566
+ type: error.name,
567
+ message: truncateExceptionMessage(error.message),
568
+ stacktrace: truncateExceptionStackTrace(error.stack || ""),
569
+ sentryEventId: getSentryEventId()
570
+ } : null
462
571
  };
463
572
  [
464
573
  item.request.body,
@@ -610,7 +719,7 @@ function skipEmptyValues(data) {
610
719
  __name(skipEmptyValues, "skipEmptyValues");
611
720
  function checkWritableFs() {
612
721
  try {
613
- const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto2.randomUUID)()}`);
722
+ const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto3.randomUUID)()}`);
614
723
  (0, import_fs2.writeFileSync)(testPath, "test");
615
724
  (0, import_fs2.unlinkSync)(testPath);
616
725
  return true;
@@ -620,104 +729,6 @@ function checkWritableFs() {
620
729
  }
621
730
  __name(checkWritableFs, "checkWritableFs");
622
731
 
623
- // src/common/serverErrorCounter.ts
624
- var import_crypto3 = require("crypto");
625
- var MAX_MSG_LENGTH = 2048;
626
- var MAX_STACKTRACE_LENGTH = 65536;
627
- var _ServerErrorCounter = class _ServerErrorCounter {
628
- errorCounts;
629
- errorDetails;
630
- sentryEventIds;
631
- sentry;
632
- constructor() {
633
- this.errorCounts = /* @__PURE__ */ new Map();
634
- this.errorDetails = /* @__PURE__ */ new Map();
635
- this.sentryEventIds = /* @__PURE__ */ new Map();
636
- this.tryImportSentry();
637
- }
638
- addServerError(serverError) {
639
- const key = this.getKey(serverError);
640
- if (!this.errorDetails.has(key)) {
641
- this.errorDetails.set(key, serverError);
642
- }
643
- this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
644
- this.captureSentryEventId(key);
645
- }
646
- getAndResetServerErrors() {
647
- const data = [];
648
- this.errorCounts.forEach((count, key) => {
649
- const serverError = this.errorDetails.get(key);
650
- if (serverError) {
651
- data.push({
652
- consumer: serverError.consumer || null,
653
- method: serverError.method,
654
- path: serverError.path,
655
- type: serverError.type,
656
- msg: this.getTruncatedMessage(serverError.msg),
657
- traceback: this.getTruncatedStack(serverError.traceback),
658
- sentry_event_id: this.sentryEventIds.get(key) || null,
659
- error_count: count
660
- });
661
- }
662
- });
663
- this.errorCounts.clear();
664
- this.errorDetails.clear();
665
- return data;
666
- }
667
- getKey(serverError) {
668
- const hashInput = [
669
- serverError.consumer || "",
670
- serverError.method.toUpperCase(),
671
- serverError.path,
672
- serverError.type,
673
- serverError.msg.trim(),
674
- serverError.traceback.trim()
675
- ].join("|");
676
- return (0, import_crypto3.createHash)("md5").update(hashInput).digest("hex");
677
- }
678
- getTruncatedMessage(msg) {
679
- msg = msg.trim();
680
- if (msg.length <= MAX_MSG_LENGTH) {
681
- return msg;
682
- }
683
- const suffix = "... (truncated)";
684
- const cutoff = MAX_MSG_LENGTH - suffix.length;
685
- return msg.substring(0, cutoff) + suffix;
686
- }
687
- getTruncatedStack(stack) {
688
- const suffix = "... (truncated) ...";
689
- const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
690
- const lines = stack.trim().split("\n");
691
- const truncatedLines = [];
692
- let length = 0;
693
- for (const line of lines) {
694
- if (length + line.length + 1 > cutoff) {
695
- truncatedLines.push(suffix);
696
- break;
697
- }
698
- truncatedLines.push(line);
699
- length += line.length + 1;
700
- }
701
- return truncatedLines.join("\n");
702
- }
703
- captureSentryEventId(serverErrorKey) {
704
- if (this.sentry && this.sentry.lastEventId) {
705
- const eventId = this.sentry.lastEventId();
706
- if (eventId) {
707
- this.sentryEventIds.set(serverErrorKey, eventId);
708
- }
709
- }
710
- }
711
- async tryImportSentry() {
712
- try {
713
- this.sentry = await import("@sentry/node");
714
- } catch (e) {
715
- }
716
- }
717
- };
718
- __name(_ServerErrorCounter, "ServerErrorCounter");
719
- var ServerErrorCounter = _ServerErrorCounter;
720
-
721
732
  // src/common/validationErrorCounter.ts
722
733
  var import_crypto4 = require("crypto");
723
734
  var _ValidationErrorCounter = class _ValidationErrorCounter {
@@ -803,7 +814,7 @@ var _ApitallyClient = class _ApitallyClient {
803
814
  throw new Error("Apitally client is already initialized");
804
815
  }
805
816
  if (!isValidClientId(clientId)) {
806
- throw new Error(`Invalid client ID '${clientId}' (expecting hexadeciaml UUID format)`);
817
+ throw new Error(`Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`);
807
818
  }
808
819
  if (!isValidEnv(env)) {
809
820
  throw new Error(`Invalid env '${env}' (expecting 1-32 alphanumeric lowercase characters and hyphens only)`);
@@ -1059,6 +1070,25 @@ function getPackageVersion(name) {
1059
1070
  }
1060
1071
  __name(getPackageVersion, "getPackageVersion");
1061
1072
 
1073
+ // src/common/utils.ts
1074
+ function parseContentLength(contentLength) {
1075
+ if (contentLength === void 0 || contentLength === null) {
1076
+ return void 0;
1077
+ }
1078
+ if (typeof contentLength === "number") {
1079
+ return contentLength;
1080
+ }
1081
+ if (typeof contentLength === "string") {
1082
+ const parsed = parseInt(contentLength);
1083
+ return isNaN(parsed) ? void 0 : parsed;
1084
+ }
1085
+ if (Array.isArray(contentLength)) {
1086
+ return parseContentLength(contentLength[0]);
1087
+ }
1088
+ return void 0;
1089
+ }
1090
+ __name(parseContentLength, "parseContentLength");
1091
+
1062
1092
  // src/fastify/plugin.ts
1063
1093
  var apitallyPlugin = /* @__PURE__ */ __name(async (fastify, config) => {
1064
1094
  const client = new ApitallyClient(config);
@@ -1106,11 +1136,8 @@ var apitallyPlugin = /* @__PURE__ */ __name(async (fastify, config) => {
1106
1136
  if (client.isEnabled() && request.method.toUpperCase() !== "OPTIONS") {
1107
1137
  const consumer = getConsumer(request);
1108
1138
  const path = "routeOptions" in request ? request.routeOptions.url : request.routerPath;
1109
- const requestSize = request.headers["content-length"];
1110
- let responseSize = reply.getHeader("content-length");
1111
- if (Array.isArray(responseSize)) {
1112
- responseSize = responseSize[0];
1113
- }
1139
+ const requestSize = parseContentLength(request.headers["content-length"]);
1140
+ const responseSize = parseContentLength(reply.getHeader("content-length"));
1114
1141
  const responseTime = getResponseTime(reply);
1115
1142
  client.consumerRegistry.addOrUpdateConsumer(consumer);
1116
1143
  client.requestCounter.addRequest({
@@ -1165,7 +1192,7 @@ var apitallyPlugin = /* @__PURE__ */ __name(async (fastify, config) => {
1165
1192
  headers: convertHeaders(reply.getHeaders()),
1166
1193
  size: Number(responseSize),
1167
1194
  body: convertBody(reply.payload, (_a2 = reply.getHeader("content-type")) == null ? void 0 : _a2.toString())
1168
- });
1195
+ }, reply.serverError);
1169
1196
  }
1170
1197
  }
1171
1198
  done();