apitally 0.11.4 → 0.12.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 (81) hide show
  1. package/README.md +27 -14
  2. package/dist/common/client.cjs +130 -110
  3. package/dist/common/client.cjs.map +1 -1
  4. package/dist/common/client.d.cts +2 -0
  5. package/dist/common/client.d.ts +2 -0
  6. package/dist/common/client.js +125 -105
  7. package/dist/common/client.js.map +1 -1
  8. package/dist/common/consumerRegistry.cjs.map +1 -1
  9. package/dist/common/consumerRegistry.js.map +1 -1
  10. package/dist/common/logging.cjs.map +1 -1
  11. package/dist/common/logging.js.map +1 -1
  12. package/dist/common/packageVersions.cjs.map +1 -1
  13. package/dist/common/packageVersions.js.map +1 -1
  14. package/dist/common/paramValidation.cjs.map +1 -1
  15. package/dist/common/paramValidation.js.map +1 -1
  16. package/dist/common/requestCounter.cjs.map +1 -1
  17. package/dist/common/requestCounter.js.map +1 -1
  18. package/dist/common/requestLogger.cjs +63 -7
  19. package/dist/common/requestLogger.cjs.map +1 -1
  20. package/dist/common/requestLogger.d.cts +3 -1
  21. package/dist/common/requestLogger.d.ts +3 -1
  22. package/dist/common/requestLogger.js +63 -7
  23. package/dist/common/requestLogger.js.map +1 -1
  24. package/dist/common/sentry.cjs +55 -0
  25. package/dist/common/sentry.cjs.map +1 -0
  26. package/dist/common/sentry.d.cts +6 -0
  27. package/dist/common/sentry.d.ts +6 -0
  28. package/dist/common/sentry.js +22 -0
  29. package/dist/common/sentry.js.map +1 -0
  30. package/dist/common/serverErrorCounter.cjs +58 -45
  31. package/dist/common/serverErrorCounter.cjs.map +1 -1
  32. package/dist/common/serverErrorCounter.d.cts +3 -6
  33. package/dist/common/serverErrorCounter.d.ts +3 -6
  34. package/dist/common/serverErrorCounter.js +53 -45
  35. package/dist/common/serverErrorCounter.js.map +1 -1
  36. package/dist/common/tempGzipFile.cjs +2 -2
  37. package/dist/common/tempGzipFile.cjs.map +1 -1
  38. package/dist/common/tempGzipFile.js +2 -2
  39. package/dist/common/tempGzipFile.js.map +1 -1
  40. package/dist/common/types.cjs.map +1 -1
  41. package/dist/common/validationErrorCounter.cjs.map +1 -1
  42. package/dist/common/validationErrorCounter.js.map +1 -1
  43. package/dist/express/index.cjs +139 -112
  44. package/dist/express/index.cjs.map +1 -1
  45. package/dist/express/index.js +134 -107
  46. package/dist/express/index.js.map +1 -1
  47. package/dist/express/middleware.cjs +139 -112
  48. package/dist/express/middleware.cjs.map +1 -1
  49. package/dist/express/middleware.js +134 -107
  50. package/dist/express/middleware.js.map +1 -1
  51. package/dist/express/utils.cjs.map +1 -1
  52. package/dist/express/utils.js.map +1 -1
  53. package/dist/fastify/index.cjs +136 -113
  54. package/dist/fastify/index.cjs.map +1 -1
  55. package/dist/fastify/index.js +131 -108
  56. package/dist/fastify/index.js.map +1 -1
  57. package/dist/fastify/plugin.cjs +136 -113
  58. package/dist/fastify/plugin.cjs.map +1 -1
  59. package/dist/fastify/plugin.js +131 -108
  60. package/dist/fastify/plugin.js.map +1 -1
  61. package/dist/hono/index.cjs +137 -112
  62. package/dist/hono/index.cjs.map +1 -1
  63. package/dist/hono/index.js +132 -107
  64. package/dist/hono/index.js.map +1 -1
  65. package/dist/hono/middleware.cjs +137 -112
  66. package/dist/hono/middleware.cjs.map +1 -1
  67. package/dist/hono/middleware.js +132 -107
  68. package/dist/hono/middleware.js.map +1 -1
  69. package/dist/koa/index.cjs +137 -111
  70. package/dist/koa/index.cjs.map +1 -1
  71. package/dist/koa/index.js +132 -106
  72. package/dist/koa/index.js.map +1 -1
  73. package/dist/koa/middleware.cjs +137 -111
  74. package/dist/koa/middleware.cjs.map +1 -1
  75. package/dist/koa/middleware.js +132 -106
  76. package/dist/koa/middleware.js.map +1 -1
  77. package/dist/nestjs/index.cjs +139 -112
  78. package/dist/nestjs/index.cjs.map +1 -1
  79. package/dist/nestjs/index.js +134 -107
  80. package/dist/nestjs/index.js.map +1 -1
  81. package/package.json +1 -1
@@ -191,6 +191,108 @@ import { unlinkSync as unlinkSync2, writeFileSync } from "fs";
191
191
  import { tmpdir as tmpdir2 } from "os";
192
192
  import { join as join2 } from "path";
193
193
 
194
+ // src/common/sentry.ts
195
+ var sentry;
196
+ (async () => {
197
+ try {
198
+ sentry = await import("@sentry/node");
199
+ } catch (e) {
200
+ }
201
+ })();
202
+ function getSentryEventId() {
203
+ if (sentry && sentry.lastEventId) {
204
+ return sentry.lastEventId();
205
+ }
206
+ return void 0;
207
+ }
208
+ __name(getSentryEventId, "getSentryEventId");
209
+
210
+ // src/common/serverErrorCounter.ts
211
+ import { createHash } from "crypto";
212
+ var MAX_MSG_LENGTH = 2048;
213
+ var MAX_STACKTRACE_LENGTH = 65536;
214
+ var _ServerErrorCounter = class _ServerErrorCounter {
215
+ errorCounts;
216
+ errorDetails;
217
+ sentryEventIds;
218
+ constructor() {
219
+ this.errorCounts = /* @__PURE__ */ new Map();
220
+ this.errorDetails = /* @__PURE__ */ new Map();
221
+ this.sentryEventIds = /* @__PURE__ */ new Map();
222
+ }
223
+ addServerError(serverError) {
224
+ const key = this.getKey(serverError);
225
+ if (!this.errorDetails.has(key)) {
226
+ this.errorDetails.set(key, serverError);
227
+ }
228
+ this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
229
+ const sentryEventId = getSentryEventId();
230
+ if (sentryEventId) {
231
+ this.sentryEventIds.set(key, sentryEventId);
232
+ }
233
+ }
234
+ getAndResetServerErrors() {
235
+ const data = [];
236
+ this.errorCounts.forEach((count, key) => {
237
+ const serverError = this.errorDetails.get(key);
238
+ if (serverError) {
239
+ data.push({
240
+ consumer: serverError.consumer || null,
241
+ method: serverError.method,
242
+ path: serverError.path,
243
+ type: serverError.type,
244
+ msg: truncateExceptionMessage(serverError.msg),
245
+ traceback: truncateExceptionStackTrace(serverError.traceback),
246
+ sentry_event_id: this.sentryEventIds.get(key) || null,
247
+ error_count: count
248
+ });
249
+ }
250
+ });
251
+ this.errorCounts.clear();
252
+ this.errorDetails.clear();
253
+ return data;
254
+ }
255
+ getKey(serverError) {
256
+ const hashInput = [
257
+ serverError.consumer || "",
258
+ serverError.method.toUpperCase(),
259
+ serverError.path,
260
+ serverError.type,
261
+ serverError.msg.trim(),
262
+ serverError.traceback.trim()
263
+ ].join("|");
264
+ return createHash("md5").update(hashInput).digest("hex");
265
+ }
266
+ };
267
+ __name(_ServerErrorCounter, "ServerErrorCounter");
268
+ var ServerErrorCounter = _ServerErrorCounter;
269
+ function truncateExceptionMessage(msg) {
270
+ if (msg.length <= MAX_MSG_LENGTH) {
271
+ return msg;
272
+ }
273
+ const suffix = "... (truncated)";
274
+ const cutoff = MAX_MSG_LENGTH - suffix.length;
275
+ return msg.substring(0, cutoff) + suffix;
276
+ }
277
+ __name(truncateExceptionMessage, "truncateExceptionMessage");
278
+ function truncateExceptionStackTrace(stack) {
279
+ const suffix = "... (truncated) ...";
280
+ const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
281
+ const lines = stack.trim().split("\n");
282
+ const truncatedLines = [];
283
+ let length = 0;
284
+ for (const line of lines) {
285
+ if (length + line.length + 1 > cutoff) {
286
+ truncatedLines.push(suffix);
287
+ break;
288
+ }
289
+ truncatedLines.push(line);
290
+ length += line.length + 1;
291
+ }
292
+ return truncatedLines.join("\n");
293
+ }
294
+ __name(truncateExceptionStackTrace, "truncateExceptionStackTrace");
295
+
194
296
  // src/common/tempGzipFile.ts
195
297
  import { Buffer as Buffer2 } from "buffer";
196
298
  import { randomUUID } from "crypto";
@@ -213,9 +315,9 @@ var _TempGzipFile = class _TempGzipFile {
213
315
  this.writeStream.once("ready", resolve);
214
316
  this.writeStream.once("error", reject);
215
317
  });
216
- this.closedPromise = new Promise((resolve) => {
318
+ this.closedPromise = new Promise((resolve, reject) => {
217
319
  this.writeStream.once("close", resolve);
218
- this.writeStream.once("error", resolve);
320
+ this.writeStream.once("error", reject);
219
321
  });
220
322
  this.gzip = createGzip();
221
323
  this.gzip.pipe(this.writeStream);
@@ -313,6 +415,7 @@ var DEFAULT_CONFIG = {
313
415
  logRequestBody: false,
314
416
  logResponseHeaders: true,
315
417
  logResponseBody: false,
418
+ logException: true,
316
419
  maskQueryParams: [],
317
420
  maskHeaders: [],
318
421
  excludePaths: []
@@ -365,7 +468,10 @@ var _RequestLogger = class _RequestLogger {
365
468
  hasSupportedContentType(headers) {
366
469
  var _a2;
367
470
  const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
368
- return contentType !== void 0 && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
471
+ return this.isSupportedContentType(contentType);
472
+ }
473
+ isSupportedContentType(contentType) {
474
+ return typeof contentType === "string" && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
369
475
  }
370
476
  maskQueryParams(search) {
371
477
  const params = new URLSearchParams(search);
@@ -382,7 +488,7 @@ var _RequestLogger = class _RequestLogger {
382
488
  this.shouldMaskHeader(k) ? MASKED : v
383
489
  ]);
384
490
  }
385
- logRequest(request, response) {
491
+ logRequest(request, response, error) {
386
492
  var _a2, _b, _c;
387
493
  if (!this.enabled || this.suspendUntil !== null) return;
388
494
  const url = new URL(request.url);
@@ -393,8 +499,6 @@ var _RequestLogger = class _RequestLogger {
393
499
  }
394
500
  url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
395
501
  request.url = url.toString();
396
- request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
397
- response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
398
502
  if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
399
503
  request.body = void 0;
400
504
  } else if (request.body) {
@@ -427,10 +531,18 @@ var _RequestLogger = class _RequestLogger {
427
531
  }
428
532
  }
429
533
  }
534
+ request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
535
+ response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
430
536
  const item = {
431
537
  uuid: randomUUID2(),
432
538
  request: skipEmptyValues(request),
433
- response: skipEmptyValues(response)
539
+ response: skipEmptyValues(response),
540
+ exception: error && this.config.logException ? {
541
+ type: error.name,
542
+ message: truncateExceptionMessage(error.message),
543
+ stacktrace: truncateExceptionStackTrace(error.stack || ""),
544
+ sentryEventId: getSentryEventId()
545
+ } : null
434
546
  };
435
547
  [
436
548
  item.request.body,
@@ -560,104 +672,6 @@ function checkWritableFs() {
560
672
  }
561
673
  __name(checkWritableFs, "checkWritableFs");
562
674
 
563
- // src/common/serverErrorCounter.ts
564
- import { createHash } from "crypto";
565
- var MAX_MSG_LENGTH = 2048;
566
- var MAX_STACKTRACE_LENGTH = 65536;
567
- var _ServerErrorCounter = class _ServerErrorCounter {
568
- errorCounts;
569
- errorDetails;
570
- sentryEventIds;
571
- sentry;
572
- constructor() {
573
- this.errorCounts = /* @__PURE__ */ new Map();
574
- this.errorDetails = /* @__PURE__ */ new Map();
575
- this.sentryEventIds = /* @__PURE__ */ new Map();
576
- this.tryImportSentry();
577
- }
578
- addServerError(serverError) {
579
- const key = this.getKey(serverError);
580
- if (!this.errorDetails.has(key)) {
581
- this.errorDetails.set(key, serverError);
582
- }
583
- this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
584
- this.captureSentryEventId(key);
585
- }
586
- getAndResetServerErrors() {
587
- const data = [];
588
- this.errorCounts.forEach((count, key) => {
589
- const serverError = this.errorDetails.get(key);
590
- if (serverError) {
591
- data.push({
592
- consumer: serverError.consumer || null,
593
- method: serverError.method,
594
- path: serverError.path,
595
- type: serverError.type,
596
- msg: this.getTruncatedMessage(serverError.msg),
597
- traceback: this.getTruncatedStack(serverError.traceback),
598
- sentry_event_id: this.sentryEventIds.get(key) || null,
599
- error_count: count
600
- });
601
- }
602
- });
603
- this.errorCounts.clear();
604
- this.errorDetails.clear();
605
- return data;
606
- }
607
- getKey(serverError) {
608
- const hashInput = [
609
- serverError.consumer || "",
610
- serverError.method.toUpperCase(),
611
- serverError.path,
612
- serverError.type,
613
- serverError.msg.trim(),
614
- serverError.traceback.trim()
615
- ].join("|");
616
- return createHash("md5").update(hashInput).digest("hex");
617
- }
618
- getTruncatedMessage(msg) {
619
- msg = msg.trim();
620
- if (msg.length <= MAX_MSG_LENGTH) {
621
- return msg;
622
- }
623
- const suffix = "... (truncated)";
624
- const cutoff = MAX_MSG_LENGTH - suffix.length;
625
- return msg.substring(0, cutoff) + suffix;
626
- }
627
- getTruncatedStack(stack) {
628
- const suffix = "... (truncated) ...";
629
- const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
630
- const lines = stack.trim().split("\n");
631
- const truncatedLines = [];
632
- let length = 0;
633
- for (const line of lines) {
634
- if (length + line.length + 1 > cutoff) {
635
- truncatedLines.push(suffix);
636
- break;
637
- }
638
- truncatedLines.push(line);
639
- length += line.length + 1;
640
- }
641
- return truncatedLines.join("\n");
642
- }
643
- captureSentryEventId(serverErrorKey) {
644
- if (this.sentry && this.sentry.lastEventId) {
645
- const eventId = this.sentry.lastEventId();
646
- if (eventId) {
647
- this.sentryEventIds.set(serverErrorKey, eventId);
648
- }
649
- }
650
- }
651
- async tryImportSentry() {
652
- try {
653
- this.sentry = await import("@sentry/node");
654
- } catch (e) {
655
- }
656
- }
657
- };
658
- __name(_ServerErrorCounter, "ServerErrorCounter");
659
- var ServerErrorCounter = _ServerErrorCounter;
660
-
661
675
  // src/common/validationErrorCounter.ts
662
676
  import { createHash as createHash2 } from "crypto";
663
677
  var _ValidationErrorCounter = class _ValidationErrorCounter {
@@ -731,6 +745,7 @@ var _ApitallyClient = class _ApitallyClient {
731
745
  syncIntervalId;
732
746
  startupData;
733
747
  startupDataSent = false;
748
+ enabled = true;
734
749
  requestCounter;
735
750
  requestLogger;
736
751
  validationErrorCounter;
@@ -767,12 +782,16 @@ var _ApitallyClient = class _ApitallyClient {
767
782
  }
768
783
  return _ApitallyClient.instance;
769
784
  }
785
+ isEnabled() {
786
+ return this.enabled;
787
+ }
770
788
  static async shutdown() {
771
789
  if (_ApitallyClient.instance) {
772
790
  await _ApitallyClient.instance.handleShutdown();
773
791
  }
774
792
  }
775
793
  async handleShutdown() {
794
+ this.enabled = false;
776
795
  this.stopSync();
777
796
  await this.sendSyncData();
778
797
  await this.sendLogData();
@@ -956,6 +975,7 @@ var _ApitallyClient = class _ApitallyClient {
956
975
  if (error instanceof HTTPError) {
957
976
  if (error.response.status === 404) {
958
977
  this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);
978
+ this.enabled = false;
959
979
  this.stopSync();
960
980
  return true;
961
981
  }
@@ -1003,6 +1023,10 @@ var useApitally = /* @__PURE__ */ __name((app, config) => {
1003
1023
  }, "useApitally");
1004
1024
  var getMiddleware = /* @__PURE__ */ __name((client) => {
1005
1025
  return async (c, next) => {
1026
+ if (!client.isEnabled()) {
1027
+ await next();
1028
+ return;
1029
+ }
1006
1030
  const startTime = performance.now();
1007
1031
  await next();
1008
1032
  let response;
@@ -1048,10 +1072,11 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
1048
1072
  let requestBody;
1049
1073
  let responseBody;
1050
1074
  let newResponse2 = response;
1075
+ const responseContentType = c.res.headers.get("content-type");
1051
1076
  if (client.requestLogger.config.logRequestBody) {
1052
1077
  requestBody = Buffer.from(await c.req.arrayBuffer());
1053
1078
  }
1054
- if (client.requestLogger.config.logResponseBody) {
1079
+ if (client.requestLogger.config.logResponseBody && client.requestLogger.isSupportedContentType(responseContentType)) {
1055
1080
  [responseBody, newResponse2] = await getResponseBody(response);
1056
1081
  response = newResponse2;
1057
1082
  }
@@ -1070,7 +1095,7 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
1070
1095
  headers: convertHeaders(c.res.headers),
1071
1096
  size: responseSize,
1072
1097
  body: responseBody
1073
- });
1098
+ }, c.error);
1074
1099
  }
1075
1100
  c.res = response;
1076
1101
  };