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
@@ -214,14 +214,116 @@ var RequestCounter = _RequestCounter;
214
214
  // src/common/requestLogger.ts
215
215
  var import_async_lock = __toESM(require("async-lock"), 1);
216
216
  var import_buffer2 = require("buffer");
217
- var import_crypto2 = require("crypto");
217
+ var import_crypto3 = require("crypto");
218
218
  var import_fs2 = require("fs");
219
219
  var import_os2 = require("os");
220
220
  var import_path2 = require("path");
221
221
 
222
+ // src/common/sentry.ts
223
+ var sentry;
224
+ (async () => {
225
+ try {
226
+ sentry = await import("@sentry/node");
227
+ } catch (e) {
228
+ }
229
+ })();
230
+ function getSentryEventId() {
231
+ if (sentry && sentry.lastEventId) {
232
+ return sentry.lastEventId();
233
+ }
234
+ return void 0;
235
+ }
236
+ __name(getSentryEventId, "getSentryEventId");
237
+
238
+ // src/common/serverErrorCounter.ts
239
+ var import_crypto = require("crypto");
240
+ var MAX_MSG_LENGTH = 2048;
241
+ var MAX_STACKTRACE_LENGTH = 65536;
242
+ var _ServerErrorCounter = class _ServerErrorCounter {
243
+ errorCounts;
244
+ errorDetails;
245
+ sentryEventIds;
246
+ constructor() {
247
+ this.errorCounts = /* @__PURE__ */ new Map();
248
+ this.errorDetails = /* @__PURE__ */ new Map();
249
+ this.sentryEventIds = /* @__PURE__ */ new Map();
250
+ }
251
+ addServerError(serverError) {
252
+ const key = this.getKey(serverError);
253
+ if (!this.errorDetails.has(key)) {
254
+ this.errorDetails.set(key, serverError);
255
+ }
256
+ this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
257
+ const sentryEventId = getSentryEventId();
258
+ if (sentryEventId) {
259
+ this.sentryEventIds.set(key, sentryEventId);
260
+ }
261
+ }
262
+ getAndResetServerErrors() {
263
+ const data = [];
264
+ this.errorCounts.forEach((count, key) => {
265
+ const serverError = this.errorDetails.get(key);
266
+ if (serverError) {
267
+ data.push({
268
+ consumer: serverError.consumer || null,
269
+ method: serverError.method,
270
+ path: serverError.path,
271
+ type: serverError.type,
272
+ msg: truncateExceptionMessage(serverError.msg),
273
+ traceback: truncateExceptionStackTrace(serverError.traceback),
274
+ sentry_event_id: this.sentryEventIds.get(key) || null,
275
+ error_count: count
276
+ });
277
+ }
278
+ });
279
+ this.errorCounts.clear();
280
+ this.errorDetails.clear();
281
+ return data;
282
+ }
283
+ getKey(serverError) {
284
+ const hashInput = [
285
+ serverError.consumer || "",
286
+ serverError.method.toUpperCase(),
287
+ serverError.path,
288
+ serverError.type,
289
+ serverError.msg.trim(),
290
+ serverError.traceback.trim()
291
+ ].join("|");
292
+ return (0, import_crypto.createHash)("md5").update(hashInput).digest("hex");
293
+ }
294
+ };
295
+ __name(_ServerErrorCounter, "ServerErrorCounter");
296
+ var ServerErrorCounter = _ServerErrorCounter;
297
+ function truncateExceptionMessage(msg) {
298
+ if (msg.length <= MAX_MSG_LENGTH) {
299
+ return msg;
300
+ }
301
+ const suffix = "... (truncated)";
302
+ const cutoff = MAX_MSG_LENGTH - suffix.length;
303
+ return msg.substring(0, cutoff) + suffix;
304
+ }
305
+ __name(truncateExceptionMessage, "truncateExceptionMessage");
306
+ function truncateExceptionStackTrace(stack) {
307
+ const suffix = "... (truncated) ...";
308
+ const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
309
+ const lines = stack.trim().split("\n");
310
+ const truncatedLines = [];
311
+ let length = 0;
312
+ for (const line of lines) {
313
+ if (length + line.length + 1 > cutoff) {
314
+ truncatedLines.push(suffix);
315
+ break;
316
+ }
317
+ truncatedLines.push(line);
318
+ length += line.length + 1;
319
+ }
320
+ return truncatedLines.join("\n");
321
+ }
322
+ __name(truncateExceptionStackTrace, "truncateExceptionStackTrace");
323
+
222
324
  // src/common/tempGzipFile.ts
223
325
  var import_buffer = require("buffer");
224
- var import_crypto = require("crypto");
326
+ var import_crypto2 = require("crypto");
225
327
  var import_fs = require("fs");
226
328
  var import_os = require("os");
227
329
  var import_path = require("path");
@@ -234,7 +336,7 @@ var _TempGzipFile = class _TempGzipFile {
234
336
  readyPromise;
235
337
  closedPromise;
236
338
  constructor() {
237
- this.uuid = (0, import_crypto.randomUUID)();
339
+ this.uuid = (0, import_crypto2.randomUUID)();
238
340
  this.filePath = (0, import_path.join)((0, import_os.tmpdir)(), `apitally-${this.uuid}.gz`);
239
341
  this.writeStream = (0, import_fs.createWriteStream)(this.filePath);
240
342
  this.readyPromise = new Promise((resolve, reject) => {
@@ -341,6 +443,7 @@ var DEFAULT_CONFIG = {
341
443
  logRequestBody: false,
342
444
  logResponseHeaders: true,
343
445
  logResponseBody: false,
446
+ logException: true,
344
447
  maskQueryParams: [],
345
448
  maskHeaders: [],
346
449
  excludePaths: []
@@ -413,7 +516,7 @@ var _RequestLogger = class _RequestLogger {
413
516
  this.shouldMaskHeader(k) ? MASKED : v
414
517
  ]);
415
518
  }
416
- logRequest(request, response) {
519
+ logRequest(request, response, error) {
417
520
  var _a2, _b, _c;
418
521
  if (!this.enabled || this.suspendUntil !== null) return;
419
522
  const url = new URL(request.url);
@@ -459,9 +562,15 @@ var _RequestLogger = class _RequestLogger {
459
562
  request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
460
563
  response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
461
564
  const item = {
462
- uuid: (0, import_crypto2.randomUUID)(),
565
+ uuid: (0, import_crypto3.randomUUID)(),
463
566
  request: skipEmptyValues(request),
464
- response: skipEmptyValues(response)
567
+ response: skipEmptyValues(response),
568
+ exception: error && this.config.logException ? {
569
+ type: error.name,
570
+ message: truncateExceptionMessage(error.message),
571
+ stacktrace: truncateExceptionStackTrace(error.stack || ""),
572
+ sentryEventId: getSentryEventId()
573
+ } : null
465
574
  };
466
575
  [
467
576
  item.request.body,
@@ -581,7 +690,7 @@ function skipEmptyValues(data) {
581
690
  __name(skipEmptyValues, "skipEmptyValues");
582
691
  function checkWritableFs() {
583
692
  try {
584
- const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto2.randomUUID)()}`);
693
+ const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto3.randomUUID)()}`);
585
694
  (0, import_fs2.writeFileSync)(testPath, "test");
586
695
  (0, import_fs2.unlinkSync)(testPath);
587
696
  return true;
@@ -591,104 +700,6 @@ function checkWritableFs() {
591
700
  }
592
701
  __name(checkWritableFs, "checkWritableFs");
593
702
 
594
- // src/common/serverErrorCounter.ts
595
- var import_crypto3 = require("crypto");
596
- var MAX_MSG_LENGTH = 2048;
597
- var MAX_STACKTRACE_LENGTH = 65536;
598
- var _ServerErrorCounter = class _ServerErrorCounter {
599
- errorCounts;
600
- errorDetails;
601
- sentryEventIds;
602
- sentry;
603
- constructor() {
604
- this.errorCounts = /* @__PURE__ */ new Map();
605
- this.errorDetails = /* @__PURE__ */ new Map();
606
- this.sentryEventIds = /* @__PURE__ */ new Map();
607
- this.tryImportSentry();
608
- }
609
- addServerError(serverError) {
610
- const key = this.getKey(serverError);
611
- if (!this.errorDetails.has(key)) {
612
- this.errorDetails.set(key, serverError);
613
- }
614
- this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
615
- this.captureSentryEventId(key);
616
- }
617
- getAndResetServerErrors() {
618
- const data = [];
619
- this.errorCounts.forEach((count, key) => {
620
- const serverError = this.errorDetails.get(key);
621
- if (serverError) {
622
- data.push({
623
- consumer: serverError.consumer || null,
624
- method: serverError.method,
625
- path: serverError.path,
626
- type: serverError.type,
627
- msg: this.getTruncatedMessage(serverError.msg),
628
- traceback: this.getTruncatedStack(serverError.traceback),
629
- sentry_event_id: this.sentryEventIds.get(key) || null,
630
- error_count: count
631
- });
632
- }
633
- });
634
- this.errorCounts.clear();
635
- this.errorDetails.clear();
636
- return data;
637
- }
638
- getKey(serverError) {
639
- const hashInput = [
640
- serverError.consumer || "",
641
- serverError.method.toUpperCase(),
642
- serverError.path,
643
- serverError.type,
644
- serverError.msg.trim(),
645
- serverError.traceback.trim()
646
- ].join("|");
647
- return (0, import_crypto3.createHash)("md5").update(hashInput).digest("hex");
648
- }
649
- getTruncatedMessage(msg) {
650
- msg = msg.trim();
651
- if (msg.length <= MAX_MSG_LENGTH) {
652
- return msg;
653
- }
654
- const suffix = "... (truncated)";
655
- const cutoff = MAX_MSG_LENGTH - suffix.length;
656
- return msg.substring(0, cutoff) + suffix;
657
- }
658
- getTruncatedStack(stack) {
659
- const suffix = "... (truncated) ...";
660
- const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
661
- const lines = stack.trim().split("\n");
662
- const truncatedLines = [];
663
- let length = 0;
664
- for (const line of lines) {
665
- if (length + line.length + 1 > cutoff) {
666
- truncatedLines.push(suffix);
667
- break;
668
- }
669
- truncatedLines.push(line);
670
- length += line.length + 1;
671
- }
672
- return truncatedLines.join("\n");
673
- }
674
- captureSentryEventId(serverErrorKey) {
675
- if (this.sentry && this.sentry.lastEventId) {
676
- const eventId = this.sentry.lastEventId();
677
- if (eventId) {
678
- this.sentryEventIds.set(serverErrorKey, eventId);
679
- }
680
- }
681
- }
682
- async tryImportSentry() {
683
- try {
684
- this.sentry = await import("@sentry/node");
685
- } catch (e) {
686
- }
687
- }
688
- };
689
- __name(_ServerErrorCounter, "ServerErrorCounter");
690
- var ServerErrorCounter = _ServerErrorCounter;
691
-
692
703
  // src/common/validationErrorCounter.ts
693
704
  var import_crypto4 = require("crypto");
694
705
  var _ValidationErrorCounter = class _ValidationErrorCounter {
@@ -774,7 +785,7 @@ var _ApitallyClient = class _ApitallyClient {
774
785
  throw new Error("Apitally client is already initialized");
775
786
  }
776
787
  if (!isValidClientId(clientId)) {
777
- throw new Error(`Invalid client ID '${clientId}' (expecting hexadeciaml UUID format)`);
788
+ throw new Error(`Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`);
778
789
  }
779
790
  if (!isValidEnv(env)) {
780
791
  throw new Error(`Invalid env '${env}' (expecting 1-32 alphanumeric lowercase characters and hyphens only)`);
@@ -1030,6 +1041,25 @@ function getPackageVersion(name) {
1030
1041
  }
1031
1042
  __name(getPackageVersion, "getPackageVersion");
1032
1043
 
1044
+ // src/common/utils.ts
1045
+ function parseContentLength(contentLength) {
1046
+ if (contentLength === void 0 || contentLength === null) {
1047
+ return void 0;
1048
+ }
1049
+ if (typeof contentLength === "number") {
1050
+ return contentLength;
1051
+ }
1052
+ if (typeof contentLength === "string") {
1053
+ const parsed = parseInt(contentLength);
1054
+ return isNaN(parsed) ? void 0 : parsed;
1055
+ }
1056
+ if (Array.isArray(contentLength)) {
1057
+ return parseContentLength(contentLength[0]);
1058
+ }
1059
+ return void 0;
1060
+ }
1061
+ __name(parseContentLength, "parseContentLength");
1062
+
1033
1063
  // src/hono/middleware.ts
1034
1064
  var useApitally = /* @__PURE__ */ __name((app, config) => {
1035
1065
  const client = new ApitallyClient(config);
@@ -1050,7 +1080,7 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
1050
1080
  let response;
1051
1081
  const responseTime = import_perf_hooks.performance.now() - startTime;
1052
1082
  const [responseSize, newResponse] = await measureResponseSize(c.res);
1053
- const requestSize = c.req.header("Content-Length");
1083
+ const requestSize = parseContentLength(c.req.header("content-length"));
1054
1084
  const consumer = getConsumer(c);
1055
1085
  client.consumerRegistry.addOrUpdateConsumer(consumer);
1056
1086
  client.requestCounter.addRequest({
@@ -1113,7 +1143,7 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
1113
1143
  headers: convertHeaders(c.res.headers),
1114
1144
  size: responseSize,
1115
1145
  body: responseBody
1116
- });
1146
+ }, c.error);
1117
1147
  }
1118
1148
  c.res = response;
1119
1149
  };