apitally 0.7.0 → 0.8.1

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 (64) hide show
  1. package/dist/common/client.cjs +75 -30
  2. package/dist/common/client.cjs.map +1 -1
  3. package/dist/common/client.d.cts +10 -8
  4. package/dist/common/client.d.ts +10 -8
  5. package/dist/common/client.js +75 -30
  6. package/dist/common/client.js.map +1 -1
  7. package/dist/common/consumerRegistry.cjs +86 -0
  8. package/dist/common/consumerRegistry.cjs.map +1 -0
  9. package/dist/common/consumerRegistry.d.cts +14 -0
  10. package/dist/common/consumerRegistry.d.ts +14 -0
  11. package/dist/common/consumerRegistry.js +63 -0
  12. package/dist/common/consumerRegistry.js.map +1 -0
  13. package/dist/common/types.cjs.map +1 -1
  14. package/dist/common/types.d.cts +12 -5
  15. package/dist/common/types.d.ts +12 -5
  16. package/dist/express/index.cjs +113 -49
  17. package/dist/express/index.cjs.map +1 -1
  18. package/dist/express/index.d.cts +2 -2
  19. package/dist/express/index.d.ts +2 -2
  20. package/dist/express/index.js +113 -49
  21. package/dist/express/index.js.map +1 -1
  22. package/dist/express/listEndpoints.cjs +16 -13
  23. package/dist/express/listEndpoints.cjs.map +1 -1
  24. package/dist/express/listEndpoints.d.cts +33 -2
  25. package/dist/express/listEndpoints.d.ts +33 -2
  26. package/dist/express/listEndpoints.js +16 -13
  27. package/dist/express/listEndpoints.js.map +1 -1
  28. package/dist/express/middleware.cjs +113 -49
  29. package/dist/express/middleware.cjs.map +1 -1
  30. package/dist/express/middleware.d.cts +5 -5
  31. package/dist/express/middleware.d.ts +5 -5
  32. package/dist/express/middleware.js +113 -49
  33. package/dist/express/middleware.js.map +1 -1
  34. package/dist/fastify/index.cjs +106 -37
  35. package/dist/fastify/index.cjs.map +1 -1
  36. package/dist/fastify/index.d.cts +2 -2
  37. package/dist/fastify/index.d.ts +2 -2
  38. package/dist/fastify/index.js +106 -37
  39. package/dist/fastify/index.js.map +1 -1
  40. package/dist/fastify/plugin.cjs +106 -37
  41. package/dist/fastify/plugin.cjs.map +1 -1
  42. package/dist/fastify/plugin.d.cts +3 -3
  43. package/dist/fastify/plugin.d.ts +3 -3
  44. package/dist/fastify/plugin.js +106 -37
  45. package/dist/fastify/plugin.js.map +1 -1
  46. package/dist/koa/index.cjs +98 -35
  47. package/dist/koa/index.cjs.map +1 -1
  48. package/dist/koa/index.d.cts +2 -2
  49. package/dist/koa/index.d.ts +2 -2
  50. package/dist/koa/index.js +98 -35
  51. package/dist/koa/index.js.map +1 -1
  52. package/dist/koa/middleware.cjs +98 -35
  53. package/dist/koa/middleware.cjs.map +1 -1
  54. package/dist/koa/middleware.d.cts +1 -1
  55. package/dist/koa/middleware.d.ts +1 -1
  56. package/dist/koa/middleware.js +98 -35
  57. package/dist/koa/middleware.js.map +1 -1
  58. package/dist/nestjs/index.cjs +113 -49
  59. package/dist/nestjs/index.cjs.map +1 -1
  60. package/dist/nestjs/index.d.cts +2 -2
  61. package/dist/nestjs/index.d.ts +2 -2
  62. package/dist/nestjs/index.js +113 -49
  63. package/dist/nestjs/index.js.map +1 -1
  64. package/package.json +2 -2
@@ -44,6 +44,62 @@ var import_perf_hooks = require("perf_hooks");
44
44
  var import_crypto3 = require("crypto");
45
45
  var import_fetch_retry = __toESM(require("fetch-retry"), 1);
46
46
 
47
+ // src/common/consumerRegistry.ts
48
+ var consumerFromStringOrObject = /* @__PURE__ */ __name((consumer) => {
49
+ var _a2, _b;
50
+ if (typeof consumer === "string") {
51
+ consumer = String(consumer).trim().substring(0, 128);
52
+ return consumer ? {
53
+ identifier: consumer
54
+ } : null;
55
+ } else {
56
+ consumer.identifier = String(consumer.identifier).trim().substring(0, 128);
57
+ consumer.name = (_a2 = consumer.name) == null ? void 0 : _a2.trim().substring(0, 64);
58
+ consumer.group = (_b = consumer.group) == null ? void 0 : _b.trim().substring(0, 64);
59
+ return consumer.identifier ? consumer : null;
60
+ }
61
+ }, "consumerFromStringOrObject");
62
+ var _ConsumerRegistry = class _ConsumerRegistry {
63
+ consumers;
64
+ updated;
65
+ constructor() {
66
+ this.consumers = /* @__PURE__ */ new Map();
67
+ this.updated = /* @__PURE__ */ new Set();
68
+ }
69
+ addOrUpdateConsumer(consumer) {
70
+ if (!consumer || !consumer.name && !consumer.group) {
71
+ return;
72
+ }
73
+ const existing = this.consumers.get(consumer.identifier);
74
+ if (!existing) {
75
+ this.consumers.set(consumer.identifier, consumer);
76
+ this.updated.add(consumer.identifier);
77
+ } else {
78
+ if (consumer.name && consumer.name !== existing.name) {
79
+ existing.name = consumer.name;
80
+ this.updated.add(consumer.identifier);
81
+ }
82
+ if (consumer.group && consumer.group !== existing.group) {
83
+ existing.group = consumer.group;
84
+ this.updated.add(consumer.identifier);
85
+ }
86
+ }
87
+ }
88
+ getAndResetUpdatedConsumers() {
89
+ const data = [];
90
+ this.updated.forEach((identifier) => {
91
+ const consumer = this.consumers.get(identifier);
92
+ if (consumer) {
93
+ data.push(consumer);
94
+ }
95
+ });
96
+ this.updated.clear();
97
+ return data;
98
+ }
99
+ };
100
+ __name(_ConsumerRegistry, "ConsumerRegistry");
101
+ var ConsumerRegistry = _ConsumerRegistry;
102
+
47
103
  // src/common/logging.ts
48
104
  var import_winston = require("winston");
49
105
  var getLogger = /* @__PURE__ */ __name(() => {
@@ -321,13 +377,14 @@ var _ApitallyClient = class _ApitallyClient {
321
377
  clientId;
322
378
  env;
323
379
  instanceUuid;
324
- requestsDataQueue;
380
+ syncDataQueue;
325
381
  syncIntervalId;
326
- appInfo;
327
- appInfoSent = false;
382
+ startupData;
383
+ startupDataSent = false;
328
384
  requestCounter;
329
385
  validationErrorCounter;
330
386
  serverErrorCounter;
387
+ consumerRegistry;
331
388
  logger;
332
389
  constructor({ clientId, env = "dev", logger }) {
333
390
  if (_ApitallyClient.instance) {
@@ -343,10 +400,11 @@ var _ApitallyClient = class _ApitallyClient {
343
400
  this.clientId = clientId;
344
401
  this.env = env;
345
402
  this.instanceUuid = (0, import_crypto3.randomUUID)();
346
- this.requestsDataQueue = [];
403
+ this.syncDataQueue = [];
347
404
  this.requestCounter = new RequestCounter();
348
405
  this.validationErrorCounter = new ValidationErrorCounter();
349
406
  this.serverErrorCounter = new ServerErrorCounter();
407
+ this.consumerRegistry = new ConsumerRegistry();
350
408
  this.logger = logger || getLogger();
351
409
  this.startSync();
352
410
  this.handleShutdown = this.handleShutdown.bind(this);
@@ -364,15 +422,15 @@ var _ApitallyClient = class _ApitallyClient {
364
422
  }
365
423
  async handleShutdown() {
366
424
  this.stopSync();
367
- await this.sendRequestsData();
425
+ await this.sendSyncData();
368
426
  _ApitallyClient.instance = void 0;
369
427
  }
370
428
  getHubUrlPrefix() {
371
429
  const baseURL = process.env.APITALLY_HUB_BASE_URL || "https://hub.apitally.io";
372
- const version = "v1";
430
+ const version = "v2";
373
431
  return `${baseURL}/${version}/${this.clientId}/${this.env}/`;
374
432
  }
375
- async makeHubRequest(url, payload) {
433
+ async sendData(url, payload) {
376
434
  const fetchWithRetry = (0, import_fetch_retry.default)(fetch, {
377
435
  retries: 3,
378
436
  retryDelay: 1e3,
@@ -411,10 +469,10 @@ var _ApitallyClient = class _ApitallyClient {
411
469
  async sync() {
412
470
  try {
413
471
  const promises = [
414
- this.sendRequestsData()
472
+ this.sendSyncData()
415
473
  ];
416
- if (!this.appInfoSent) {
417
- promises.push(this.sendAppInfo());
474
+ if (!this.startupDataSent) {
475
+ promises.push(this.sendStartupData());
418
476
  }
419
477
  await Promise.all(promises);
420
478
  } catch (error) {
@@ -429,62 +487,63 @@ var _ApitallyClient = class _ApitallyClient {
429
487
  this.syncIntervalId = void 0;
430
488
  }
431
489
  }
432
- setAppInfo(appInfo) {
433
- this.appInfo = appInfo;
434
- this.appInfoSent = false;
435
- this.sendAppInfo();
490
+ setStartupData(data) {
491
+ this.startupData = data;
492
+ this.startupDataSent = false;
493
+ this.sendStartupData();
436
494
  }
437
- async sendAppInfo() {
438
- if (this.appInfo) {
439
- this.logger.debug("Sending app info to Apitally Hub");
495
+ async sendStartupData() {
496
+ if (this.startupData) {
497
+ this.logger.debug("Sending startup data to Apitally Hub");
440
498
  const payload = {
441
499
  instance_uuid: this.instanceUuid,
442
500
  message_uuid: (0, import_crypto3.randomUUID)(),
443
- ...this.appInfo
501
+ ...this.startupData
444
502
  };
445
503
  try {
446
- await this.makeHubRequest("info", payload);
447
- this.appInfoSent = true;
504
+ await this.sendData("startup", payload);
505
+ this.startupDataSent = true;
448
506
  } catch (error) {
449
507
  const handled = this.handleHubError(error);
450
508
  if (!handled) {
451
509
  this.logger.error(error.message);
452
- this.logger.debug("Error while sending app info to Apitally Hub (will retry)", {
510
+ this.logger.debug("Error while sending startup data to Apitally Hub (will retry)", {
453
511
  error
454
512
  });
455
513
  }
456
514
  }
457
515
  }
458
516
  }
459
- async sendRequestsData() {
460
- this.logger.debug("Sending requests data to Apitally Hub");
517
+ async sendSyncData() {
518
+ this.logger.debug("Synchronizing data with Apitally Hub");
461
519
  const newPayload = {
462
520
  time_offset: 0,
463
521
  instance_uuid: this.instanceUuid,
464
522
  message_uuid: (0, import_crypto3.randomUUID)(),
465
523
  requests: this.requestCounter.getAndResetRequests(),
466
524
  validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
467
- server_errors: this.serverErrorCounter.getAndResetServerErrors()
525
+ server_errors: this.serverErrorCounter.getAndResetServerErrors(),
526
+ consumers: this.consumerRegistry.getAndResetUpdatedConsumers()
468
527
  };
469
- this.requestsDataQueue.push([
528
+ this.syncDataQueue.push([
470
529
  Date.now(),
471
530
  newPayload
472
531
  ]);
473
532
  const failedItems = [];
474
- while (this.requestsDataQueue.length > 0) {
475
- const queueItem = this.requestsDataQueue.shift();
533
+ while (this.syncDataQueue.length > 0) {
534
+ const queueItem = this.syncDataQueue.shift();
476
535
  if (queueItem) {
477
536
  const [time, payload] = queueItem;
478
537
  try {
479
538
  const timeOffset = Date.now() - time;
480
539
  if (timeOffset <= MAX_QUEUE_TIME) {
481
540
  payload.time_offset = timeOffset / 1e3;
482
- await this.makeHubRequest("requests", payload);
541
+ await this.sendData("sync", payload);
483
542
  }
484
543
  } catch (error) {
485
544
  const handled = this.handleHubError(error);
486
545
  if (!handled) {
487
- this.logger.debug("Error while sending requests data to Apitally Hub (will retry)", {
546
+ this.logger.debug("Error while synchronizing data with Apitally Hub (will retry)", {
488
547
  error
489
548
  });
490
549
  failedItems.push(queueItem);
@@ -492,7 +551,7 @@ var _ApitallyClient = class _ApitallyClient {
492
551
  }
493
552
  }
494
553
  }
495
- this.requestsDataQueue = failedItems;
554
+ this.syncDataQueue = failedItems;
496
555
  }
497
556
  handleHubError(error) {
498
557
  if (error instanceof HTTPError) {
@@ -527,9 +586,10 @@ function getPackageVersion(name) {
527
586
  __name(getPackageVersion, "getPackageVersion");
528
587
 
529
588
  // src/express/listEndpoints.js
530
- var regExpToParseExpressPathRegExp = /^\/\^\\\/(?:(:?[\w\\.-]*(?:\\\/:?[\w\\.-]*)*)|(\(\?:\(\[\^\\\/]\+\?\)\)))\\\/.*/;
531
- var regExpToReplaceExpressPathRegExpParams = /\(\?:\(\[\^\\\/]\+\?\)\)/;
532
- var regexpExpressParamRegexp = /\(\?:\(\[\^\\\/]\+\?\)\)/g;
589
+ var regExpToParseExpressPathRegExp = /^\/\^\\\/(?:(:?[\w\\.-]*(?:\\\/:?[\w\\.-]*)*)|(\(\?:\([^)]+\)\)))\\\/.*/;
590
+ var regExpToReplaceExpressPathRegExpParams = /\(\?:\([^)]+\)\)/;
591
+ var regexpExpressParamRegexp = /\(\?:\([^)]+\)\)/g;
592
+ var regexpExpressPathParamRegexp = /(:[^)]+)\([^)]+\)/g;
533
593
  var EXPRESS_ROOT_PATH_REGEXP_VALUE = "/^\\/?(?=\\/|$)/i";
534
594
  var STACK_ITEM_VALID_NAMES = [
535
595
  "router",
@@ -560,7 +620,7 @@ var parseExpressRoute = /* @__PURE__ */ __name(function(route, basePath) {
560
620
  const endpoints = paths.map((path) => {
561
621
  const completePath = basePath && path === "/" ? basePath : `${basePath}${path}`;
562
622
  const endpoint = {
563
- path: completePath,
623
+ path: completePath.replace(regexpExpressPathParamRegexp, "$1"),
564
624
  methods: getRouteMethods(route),
565
625
  middlewares: getRouteMiddlewares(route)
566
626
  };
@@ -569,8 +629,8 @@ var parseExpressRoute = /* @__PURE__ */ __name(function(route, basePath) {
569
629
  return endpoints;
570
630
  }, "parseExpressRoute");
571
631
  var parseExpressPath = /* @__PURE__ */ __name(function(expressPathRegExp, params) {
572
- let expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(expressPathRegExp);
573
632
  let parsedRegExp = expressPathRegExp.toString();
633
+ let expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp);
574
634
  let paramIndex = 0;
575
635
  while (hasParams(parsedRegExp)) {
576
636
  const paramName = params[paramIndex].name;
@@ -589,13 +649,15 @@ var parseEndpoints = /* @__PURE__ */ __name(function(app, basePath, endpoints) {
589
649
  endpoints = endpoints || [];
590
650
  basePath = basePath || "";
591
651
  if (!stack) {
592
- endpoints = addEndpoints(endpoints, [
593
- {
594
- path: basePath,
595
- methods: [],
596
- middlewares: []
597
- }
598
- ]);
652
+ if (endpoints.length) {
653
+ endpoints = addEndpoints(endpoints, [
654
+ {
655
+ path: basePath,
656
+ methods: [],
657
+ middlewares: []
658
+ }
659
+ ]);
660
+ }
599
661
  } else {
600
662
  endpoints = parseStack(stack, basePath, endpoints);
601
663
  }
@@ -603,7 +665,7 @@ var parseEndpoints = /* @__PURE__ */ __name(function(app, basePath, endpoints) {
603
665
  }, "parseEndpoints");
604
666
  var addEndpoints = /* @__PURE__ */ __name(function(currentEndpoints, endpointsToAdd) {
605
667
  endpointsToAdd.forEach((newEndpoint) => {
606
- const existingEndpoint = currentEndpoints.find((item) => item.path === newEndpoint.path);
668
+ const existingEndpoint = currentEndpoints.find((endpoint) => endpoint.path === newEndpoint.path);
607
669
  if (existingEndpoint !== void 0) {
608
670
  const newMethods = newEndpoint.methods.filter((method) => !existingEndpoint.methods.includes(method));
609
671
  existingEndpoint.methods = existingEndpoint.methods.concat(newMethods);
@@ -651,7 +713,7 @@ var useApitally = /* @__PURE__ */ __name((app, config) => {
651
713
  const middleware = getMiddleware(app, client);
652
714
  app.use(middleware);
653
715
  setTimeout(() => {
654
- client.setAppInfo(getAppInfo(app, config.appVersion));
716
+ client.setStartupData(getAppInfo(app, config.appVersion));
655
717
  }, 1e3);
656
718
  }, "useApitally");
657
719
  var getMiddleware = /* @__PURE__ */ __name((app, client) => {
@@ -680,8 +742,9 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
680
742
  if (req.route) {
681
743
  const responseTime = import_perf_hooks.performance.now() - startTime;
682
744
  const consumer = getConsumer(req);
745
+ client.consumerRegistry.addOrUpdateConsumer(consumer);
683
746
  client.requestCounter.addRequest({
684
- consumer,
747
+ consumer: consumer == null ? void 0 : consumer.identifier,
685
748
  method: req.method,
686
749
  path: req.route.path,
687
750
  statusCode: res.statusCode,
@@ -702,7 +765,7 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
702
765
  }
703
766
  validationErrors.forEach((error) => {
704
767
  client.validationErrorCounter.addValidationError({
705
- consumer,
768
+ consumer: consumer == null ? void 0 : consumer.identifier,
706
769
  method: req.method,
707
770
  path: req.route.path,
708
771
  ...error
@@ -712,7 +775,7 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
712
775
  if (res.statusCode === 500 && res.locals.serverError) {
713
776
  const serverError = res.locals.serverError;
714
777
  client.serverErrorCounter.addServerError({
715
- consumer,
778
+ consumer: consumer == null ? void 0 : consumer.identifier,
716
779
  method: req.method,
717
780
  path: req.route.path,
718
781
  type: serverError.name,
@@ -742,9 +805,10 @@ var getMiddleware = /* @__PURE__ */ __name((app, client) => {
742
805
  }, "getMiddleware");
743
806
  var getConsumer = /* @__PURE__ */ __name((req) => {
744
807
  if (req.apitallyConsumer) {
745
- return String(req.apitallyConsumer);
808
+ return consumerFromStringOrObject(req.apitallyConsumer);
746
809
  } else if (req.consumerIdentifier) {
747
- return String(req.consumerIdentifier);
810
+ process.emitWarning("The consumerIdentifier property on the request object is deprecated. Use apitallyConsumer instead.", "DeprecationWarning");
811
+ return consumerFromStringOrObject(req.consumerIdentifier);
748
812
  }
749
813
  return null;
750
814
  }, "getConsumer");
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/nestjs/index.ts","../../src/express/middleware.ts","../../src/common/client.ts","../../src/common/logging.ts","../../src/common/paramValidation.ts","../../src/common/requestCounter.ts","../../src/common/serverErrorCounter.ts","../../src/common/validationErrorCounter.ts","../../src/common/packageVersions.ts","../../src/express/listEndpoints.js"],"sourcesContent":["export { useApitally } from \"../express/index.js\";\n","import type { Express, NextFunction, Request, Response } from \"express\";\nimport { performance } from \"perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { getPackageVersion } from \"../common/packageVersions.js\";\nimport { ApitallyConfig, AppInfo, ValidationError } from \"../common/types.js\";\nimport listEndpoints from \"./listEndpoints.js\";\n\ndeclare module \"express\" {\n interface Request {\n apitallyConsumer?: string;\n consumerIdentifier?: string; // For backwards compatibility\n }\n}\n\nexport const useApitally = (app: Express, config: ApitallyConfig) => {\n const client = new ApitallyClient(config);\n const middleware = getMiddleware(app, client);\n app.use(middleware);\n setTimeout(() => {\n client.setAppInfo(getAppInfo(app, config.appVersion));\n }, 1000);\n};\n\nconst getMiddleware = (app: Express, client: ApitallyClient) => {\n const validatorInstalled = getPackageVersion(\"express-validator\") !== null;\n const celebrateInstalled = getPackageVersion(\"celebrate\") !== null;\n const nestInstalled = getPackageVersion(\"@nestjs/core\") !== null;\n const classValidatorInstalled = getPackageVersion(\"class-validator\") !== null;\n let errorHandlerConfigured = false;\n\n return (req: Request, res: Response, next: NextFunction) => {\n if (!errorHandlerConfigured) {\n // Add error handling middleware to the bottom of the stack when handling the first request\n app.use(\n (err: Error, req: Request, res: Response, next: NextFunction): void => {\n res.locals.serverError = err;\n next(err);\n },\n );\n errorHandlerConfigured = true;\n }\n try {\n const startTime = performance.now();\n const originalJson = res.json;\n res.json = (body) => {\n res.locals.body = body;\n return originalJson.call(res, body);\n };\n res.on(\"finish\", () => {\n try {\n if (req.route) {\n const responseTime = performance.now() - startTime;\n const consumer = getConsumer(req);\n client.requestCounter.addRequest({\n consumer: consumer,\n method: req.method,\n path: req.route.path,\n statusCode: res.statusCode,\n responseTime: responseTime,\n requestSize: req.get(\"content-length\"),\n responseSize: res.get(\"content-length\"),\n });\n if (\n (res.statusCode === 400 || res.statusCode === 422) &&\n res.locals.body\n ) {\n const validationErrors: ValidationError[] = [];\n if (validatorInstalled) {\n validationErrors.push(\n ...extractExpressValidatorErrors(res.locals.body),\n );\n }\n if (celebrateInstalled) {\n validationErrors.push(\n ...extractCelebrateErrors(res.locals.body),\n );\n }\n if (nestInstalled && classValidatorInstalled) {\n validationErrors.push(\n ...extractNestValidationErrors(res.locals.body),\n );\n }\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer,\n method: req.method,\n path: req.route.path,\n ...error,\n });\n });\n }\n if (res.statusCode === 500 && res.locals.serverError) {\n const serverError = res.locals.serverError as Error;\n client.serverErrorCounter.addServerError({\n consumer: consumer,\n method: req.method,\n path: req.route.path,\n type: serverError.name,\n msg: serverError.message,\n traceback: serverError.stack || \"\",\n });\n }\n }\n } catch (error) {\n client.logger.error(\n \"Error while logging request in Apitally middleware.\",\n { request: req, response: res, error },\n );\n }\n });\n } catch (error) {\n client.logger.error(\"Error in Apitally middleware.\", {\n request: req,\n response: res,\n error,\n });\n } finally {\n next();\n }\n };\n};\n\nconst getConsumer = (req: Request) => {\n if (req.apitallyConsumer) {\n return String(req.apitallyConsumer);\n } else if (req.consumerIdentifier) {\n // For backwards compatibility\n return String(req.consumerIdentifier);\n }\n return null;\n};\n\nconst extractExpressValidatorErrors = (responseBody: any) => {\n const errors: ValidationError[] = [];\n if (\n responseBody &&\n responseBody.errors &&\n Array.isArray(responseBody.errors)\n ) {\n responseBody.errors.forEach((error: any) => {\n if (error.location && error.path && error.msg && error.type) {\n errors.push({\n loc: `${error.location}.${error.path}`,\n msg: error.msg,\n type: error.type,\n });\n }\n });\n }\n return errors;\n};\n\nconst extractCelebrateErrors = (responseBody: any) => {\n const errors: ValidationError[] = [];\n if (responseBody && responseBody.validation) {\n Object.values(responseBody.validation).forEach((error: any) => {\n if (\n error.source &&\n error.keys &&\n Array.isArray(error.keys) &&\n error.message\n ) {\n error.keys.forEach((key: string) => {\n errors.push({\n loc: `${error.source}.${key}`,\n msg: subsetJoiMessage(error.message, key),\n type: \"\",\n });\n });\n }\n });\n }\n return errors;\n};\n\nconst extractNestValidationErrors = (responseBody: any) => {\n const errors: ValidationError[] = [];\n if (responseBody && Array.isArray(responseBody.message)) {\n responseBody.message.forEach((message: any) => {\n errors.push({\n loc: \"\",\n msg: message,\n type: \"\",\n });\n });\n }\n return errors;\n};\n\nconst subsetJoiMessage = (message: string, key: string) => {\n const messageWithKey = message\n .split(\". \")\n .find((message) => message.includes(`\"${key}\"`));\n return messageWithKey ? messageWithKey : message;\n};\n\nconst getAppInfo = (app: Express, appVersion?: string): AppInfo => {\n const versions: Array<[string, string]> = [\n [\"nodejs\", process.version.replace(/^v/, \"\")],\n ];\n const expressVersion = getPackageVersion(\"express\");\n const apitallyVersion = getPackageVersion(\"../..\");\n if (expressVersion) {\n versions.push([\"express\", expressVersion]);\n }\n if (apitallyVersion) {\n versions.push([\"apitally\", apitallyVersion]);\n }\n if (appVersion) {\n versions.push([\"app\", appVersion]);\n }\n return {\n paths: listEndpoints(app),\n versions: Object.fromEntries(versions),\n client: \"js:express\",\n };\n};\n","import { randomUUID } from \"crypto\";\nimport fetchRetry from \"fetch-retry\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport {\n ApitallyConfig,\n AppInfo,\n AppInfoPayload,\n RequestsDataPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private requestsDataQueue: Array<[number, RequestsDataPayload]>;\n private syncIntervalId?: NodeJS.Timeout;\n public appInfo?: AppInfo;\n private appInfoSent: boolean = false;\n\n public requestCounter: RequestCounter;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public logger: Logger;\n\n constructor({ clientId, env = \"dev\", logger }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n if (!isValidClientId(clientId)) {\n throw new Error(\n `Invalid client ID '${clientId}' (expecting hexadeciaml UUID format)`,\n );\n }\n if (!isValidEnv(env)) {\n throw new Error(\n `Invalid env '${env}' (expecting 1-32 alphanumeric lowercase characters and hyphens only)`,\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = randomUUID();\n this.requestsDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.logger = logger || getLogger();\n\n this.startSync();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.stopSync();\n await this.sendRequestsData();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v1\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async makeHubRequest(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n private startSync() {\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendRequestsData()];\n if (!this.appInfoSent) {\n promises.push(this.sendAppInfo());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setAppInfo(appInfo: AppInfo) {\n this.appInfo = appInfo;\n this.appInfoSent = false;\n this.sendAppInfo();\n }\n\n private async sendAppInfo() {\n if (this.appInfo) {\n this.logger.debug(\"Sending app info to Apitally Hub\");\n const payload: AppInfoPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.appInfo,\n };\n try {\n await this.makeHubRequest(\"info\", payload);\n this.appInfoSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending app info to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendRequestsData() {\n this.logger.debug(\"Sending requests data to Apitally Hub\");\n const newPayload: RequestsDataPayload = {\n time_offset: 0,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n };\n this.requestsDataQueue.push([Date.now(), newPayload]);\n\n const failedItems: [number, RequestsDataPayload][] = [];\n while (this.requestsDataQueue.length > 0) {\n const queueItem = this.requestsDataQueue.shift();\n if (queueItem) {\n const [time, payload] = queueItem;\n try {\n const timeOffset = Date.now() - time;\n if (timeOffset <= MAX_QUEUE_TIME) {\n payload.time_offset = timeOffset / 1000.0; // In seconds\n await this.makeHubRequest(\"requests\", payload);\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while sending requests data to Apitally Hub (will retry)\",\n { error },\n );\n failedItems.push(queueItem);\n }\n }\n }\n }\n this.requestsDataQueue = failedItems;\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n}\n","import { createLogger, format, transports } from \"winston\";\n\nexport interface Logger {\n debug: (message: string, meta?: object) => void;\n info: (message: string, meta?: object) => void;\n warn: (message: string, meta?: object) => void;\n error: (message: string, meta?: object) => void;\n}\n\nexport const getLogger = () => {\n return createLogger({\n level: process.env.APITALLY_DEBUG ? \"debug\" : \"warn\",\n format: format.combine(\n format.colorize(),\n format.timestamp(),\n format.printf(\n (info) => `${info.timestamp} ${info.level}: ${info.message}`,\n ),\n ),\n transports: [new transports.Console()],\n });\n};\n","export function isValidClientId(clientId: string): boolean {\n const regexExp =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n return regexExp.test(clientId);\n}\n\nexport function isValidEnv(env: string): boolean {\n const regexExp = /^[\\w-]{1,32}$/;\n return regexExp.test(env);\n}\n","import { RequestInfo, RequestsItem } from \"./types.js\";\n\nexport default class RequestCounter {\n private requestCounts: Map<string, number>;\n private requestSizeSums: Map<string, number>;\n private responseSizeSums: Map<string, number>;\n private responseTimes: Map<string, Map<number, number>>;\n private requestSizes: Map<string, Map<number, number>>;\n private responseSizes: Map<string, Map<number, number>>;\n\n constructor() {\n this.requestCounts = new Map<string, number>();\n this.requestSizeSums = new Map<string, number>();\n this.responseSizeSums = new Map<string, number>();\n this.responseTimes = new Map<string, Map<number, number>>();\n this.requestSizes = new Map<string, Map<number, number>>();\n this.responseSizes = new Map<string, Map<number, number>>();\n }\n\n private getKey(requestInfo: RequestInfo) {\n return [\n requestInfo.consumer || \"\",\n requestInfo.method.toUpperCase(),\n requestInfo.path,\n requestInfo.statusCode,\n ].join(\"|\");\n }\n\n addRequest(requestInfo: RequestInfo) {\n const key = this.getKey(requestInfo);\n\n // Increment request count\n this.requestCounts.set(key, (this.requestCounts.get(key) || 0) + 1);\n\n // Add response time\n if (!this.responseTimes.has(key)) {\n this.responseTimes.set(key, new Map<number, number>());\n }\n const responseTimeMap = this.responseTimes.get(key)!;\n const responseTimeMsBin = Math.floor(requestInfo.responseTime / 10) * 10; // Rounded to nearest 10ms\n responseTimeMap.set(\n responseTimeMsBin,\n (responseTimeMap.get(responseTimeMsBin) || 0) + 1,\n );\n\n // Add request size\n if (requestInfo.requestSize !== undefined) {\n requestInfo.requestSize = Number(requestInfo.requestSize);\n this.requestSizeSums.set(\n key,\n (this.requestSizeSums.get(key) || 0) + requestInfo.requestSize,\n );\n if (!this.requestSizes.has(key)) {\n this.requestSizes.set(key, new Map<number, number>());\n }\n const requestSizeMap = this.requestSizes.get(key)!;\n const requestSizeKbBin = Math.floor(requestInfo.requestSize / 1000); // Rounded down to nearest KB\n requestSizeMap.set(\n requestSizeKbBin,\n (requestSizeMap.get(requestSizeKbBin) || 0) + 1,\n );\n }\n\n // Add response size\n if (requestInfo.responseSize !== undefined) {\n requestInfo.responseSize = Number(requestInfo.responseSize);\n this.responseSizeSums.set(\n key,\n (this.responseSizeSums.get(key) || 0) + requestInfo.responseSize,\n );\n if (!this.responseSizes.has(key)) {\n this.responseSizes.set(key, new Map<number, number>());\n }\n const responseSizeMap = this.responseSizes.get(key)!;\n const responseSizeKbBin = Math.floor(requestInfo.responseSize / 1000); // Rounded down to nearest KB\n responseSizeMap.set(\n responseSizeKbBin,\n (responseSizeMap.get(responseSizeKbBin) || 0) + 1,\n );\n }\n }\n\n getAndResetRequests() {\n const data: Array<RequestsItem> = [];\n this.requestCounts.forEach((count, key) => {\n const [consumer, method, path, statusCodeStr] = key.split(\"|\");\n const responseTimes =\n this.responseTimes.get(key) || new Map<number, number>();\n const requestSizes =\n this.requestSizes.get(key) || new Map<number, number>();\n const responseSizes =\n this.responseSizes.get(key) || new Map<number, number>();\n data.push({\n consumer: consumer || null,\n method,\n path,\n status_code: parseInt(statusCodeStr),\n request_count: count,\n request_size_sum: this.requestSizeSums.get(key) || 0,\n response_size_sum: this.responseSizeSums.get(key) || 0,\n response_times: Object.fromEntries(responseTimes),\n request_sizes: Object.fromEntries(requestSizes),\n response_sizes: Object.fromEntries(responseSizes),\n });\n });\n\n // Reset the counts and times\n this.requestCounts.clear();\n this.requestSizeSums.clear();\n this.responseSizeSums.clear();\n this.responseTimes.clear();\n this.requestSizes.clear();\n this.responseSizes.clear();\n\n return data;\n }\n}\n","import type * as Sentry from \"@sentry/node\";\nimport { createHash } from \"crypto\";\n\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n private sentryEventIds: Map<string, string>;\n private sentry: typeof Sentry | undefined;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n this.sentryEventIds = new Map();\n this.tryImportSentry();\n }\n\n public addServerError(serverError: ConsumerMethodPath & ServerError) {\n const key = this.getKey(serverError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, serverError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n this.captureSentryEventId(key);\n }\n\n public getAndResetServerErrors() {\n const data: Array<ServerErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const serverError = this.errorDetails.get(key);\n if (serverError) {\n data.push({\n consumer: serverError.consumer || null,\n method: serverError.method,\n path: serverError.path,\n type: serverError.type,\n msg: this.getTruncatedMessage(serverError.msg),\n traceback: this.getTruncatedStack(serverError.traceback),\n sentry_event_id: this.sentryEventIds.get(key) || null,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(serverError: ConsumerMethodPath & ServerError) {\n const hashInput = [\n serverError.consumer || \"\",\n serverError.method.toUpperCase(),\n serverError.path,\n serverError.type,\n serverError.msg.trim(),\n serverError.traceback.trim(),\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n\n private getTruncatedMessage(msg: string) {\n msg = msg.trim();\n if (msg.length <= MAX_MSG_LENGTH) {\n return msg;\n }\n const suffix = \"... (truncated)\";\n const cutoff = MAX_MSG_LENGTH - suffix.length;\n return msg.substring(0, cutoff) + suffix;\n }\n\n private getTruncatedStack(stack: string) {\n const suffix = \"... (truncated) ...\";\n const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n const lines = stack.trim().split(\"\\n\");\n const truncatedLines: string[] = [];\n let length = 0;\n for (const line of lines) {\n if (length + line.length + 1 > cutoff) {\n truncatedLines.push(suffix);\n break;\n }\n truncatedLines.push(line);\n length += line.length + 1;\n }\n return truncatedLines.join(\"\\n\");\n }\n\n private captureSentryEventId(serverErrorKey: string) {\n if (this.sentry && this.sentry.lastEventId) {\n const eventId = this.sentry.lastEventId();\n if (eventId) {\n this.sentryEventIds.set(serverErrorKey, eventId);\n }\n }\n }\n\n private async tryImportSentry() {\n try {\n this.sentry = await import(\"@sentry/node\");\n } catch (e) {\n // Sentry SDK is not installed, ignore\n }\n }\n}\n","import { createHash } from \"crypto\";\n\nimport {\n ConsumerMethodPath,\n ValidationError,\n ValidationErrorsItem,\n} from \"./types.js\";\n\nexport default class ValidationErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ValidationError>;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n }\n\n public addValidationError(\n validationError: ConsumerMethodPath & ValidationError,\n ) {\n const key = this.getKey(validationError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, validationError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n }\n\n public getAndResetValidationErrors() {\n const data: Array<ValidationErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const validationError = this.errorDetails.get(key);\n if (validationError) {\n data.push({\n consumer: validationError.consumer || null,\n method: validationError.method,\n path: validationError.path,\n loc: validationError.loc.split(\".\"),\n msg: validationError.msg,\n type: validationError.type,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(validationError: ConsumerMethodPath & ValidationError) {\n const hashInput = [\n validationError.consumer || \"\",\n validationError.method.toUpperCase(),\n validationError.path,\n validationError.loc,\n validationError.msg.trim(),\n validationError.type,\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n}\n","import { createRequire } from \"module\";\n\nexport function getPackageVersion(name: string): string | null {\n try {\n const _require = createRequire(import.meta.url);\n return _require(`${name}/package.json`).version || null;\n } catch (error) {\n return null;\n }\n}\n","// Adapted from https://github.com/AlbertoFdzM/express-list-endpoints/blob/ed926dd5186761e7b721ad20938ce6eb52d104d3/src/index.js\n\nconst regExpToParseExpressPathRegExp =\n /^\\/\\^\\\\\\/(?:(:?[\\w\\\\.-]*(?:\\\\\\/:?[\\w\\\\.-]*)*)|(\\(\\?:\\(\\[\\^\\\\\\/]\\+\\?\\)\\)))\\\\\\/.*/;\nconst regExpToReplaceExpressPathRegExpParams = /\\(\\?:\\(\\[\\^\\\\\\/]\\+\\?\\)\\)/;\nconst regexpExpressParamRegexp = /\\(\\?:\\(\\[\\^\\\\\\/]\\+\\?\\)\\)/g;\n\nconst EXPRESS_ROOT_PATH_REGEXP_VALUE = \"/^\\\\/?(?=\\\\/|$)/i\";\nconst STACK_ITEM_VALID_NAMES = [\"router\", \"bound dispatch\", \"mounted_app\"];\n\n/**\n * Returns all the verbs detected for the passed route\n */\nconst getRouteMethods = function (route) {\n let methods = Object.keys(route.methods);\n\n methods = methods.filter((method) => method !== \"_all\");\n methods = methods.map((method) => method.toUpperCase());\n\n return methods;\n};\n\n/**\n * Returns the names (or anonymous) of all the middlewares attached to the\n * passed route\n * @param {Object} route\n * @returns {string[]}\n */\nconst getRouteMiddlewares = function (route) {\n return route.stack.map((item) => {\n return item.handle.name || \"anonymous\";\n });\n};\n\n/**\n * Returns true if found regexp related with express params\n * @param {string} expressPathRegExp\n * @returns {boolean}\n */\nconst hasParams = function (expressPathRegExp) {\n return regexpExpressParamRegexp.test(expressPathRegExp);\n};\n\n/**\n * @param {Object} route Express route object to be parsed\n * @param {string} basePath The basePath the route is on\n * @return {Object[]} Endpoints info\n */\nconst parseExpressRoute = function (route, basePath) {\n const paths = [];\n\n if (Array.isArray(route.path)) {\n paths.push(...route.path);\n } else {\n paths.push(route.path);\n }\n\n const endpoints = paths.map((path) => {\n const completePath =\n basePath && path === \"/\" ? basePath : `${basePath}${path}`;\n\n const endpoint = {\n path: completePath,\n methods: getRouteMethods(route),\n middlewares: getRouteMiddlewares(route),\n };\n\n return endpoint;\n });\n\n return endpoints;\n};\n\n/**\n * @param {RegExp} expressPathRegExp\n * @param {Object[]} params\n * @returns {string}\n */\nconst parseExpressPath = function (expressPathRegExp, params) {\n let expressPathRegExpExec =\n regExpToParseExpressPathRegExp.exec(expressPathRegExp);\n let parsedRegExp = expressPathRegExp.toString();\n let paramIndex = 0;\n\n while (hasParams(parsedRegExp)) {\n const paramName = params[paramIndex].name;\n const paramId = `:${paramName}`;\n\n parsedRegExp = parsedRegExp.replace(\n regExpToReplaceExpressPathRegExpParams,\n paramId,\n );\n\n paramIndex++;\n }\n\n if (parsedRegExp !== expressPathRegExp.toString()) {\n expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp);\n }\n\n const parsedPath = expressPathRegExpExec[1].replace(/\\\\\\//g, \"/\");\n\n return parsedPath;\n};\n\n/**\n * @param {Object} app\n * @param {string} [basePath]\n * @param {Object[]} [endpoints]\n * @returns {Object[]}\n */\nconst parseEndpoints = function (app, basePath, endpoints) {\n const stack = app.stack || (app._router && app._router.stack);\n\n endpoints = endpoints || [];\n basePath = basePath || \"\";\n\n if (!stack) {\n endpoints = addEndpoints(endpoints, [\n {\n path: basePath,\n methods: [],\n middlewares: [],\n },\n ]);\n } else {\n endpoints = parseStack(stack, basePath, endpoints);\n }\n\n return endpoints;\n};\n\n/**\n * Ensures the path of the new endpoints isn't yet in the array.\n * If the path is already in the array merges the endpoints with the existing\n * one, if not, it adds them to the array.\n *\n * @param {Object[]} currentEndpoints Array of current endpoints\n * @param {Object[]} endpointsToAdd New endpoints to be added to the array\n * @returns {Object[]} Updated endpoints array\n */\nconst addEndpoints = function (currentEndpoints, endpointsToAdd) {\n endpointsToAdd.forEach((newEndpoint) => {\n const existingEndpoint = currentEndpoints.find(\n (item) => item.path === newEndpoint.path,\n );\n\n if (existingEndpoint !== undefined) {\n const newMethods = newEndpoint.methods.filter(\n (method) => !existingEndpoint.methods.includes(method),\n );\n\n existingEndpoint.methods = existingEndpoint.methods.concat(newMethods);\n } else {\n currentEndpoints.push(newEndpoint);\n }\n });\n\n return currentEndpoints;\n};\n\n/**\n * @param {Object} stack\n * @param {string} basePath\n * @param {Object[]} endpoints\n * @returns {Object[]}\n */\nconst parseStack = function (stack, basePath, endpoints) {\n stack.forEach((stackItem) => {\n if (stackItem.route) {\n const newEndpoints = parseExpressRoute(stackItem.route, basePath);\n\n endpoints = addEndpoints(endpoints, newEndpoints);\n } else if (STACK_ITEM_VALID_NAMES.includes(stackItem.name)) {\n const isExpressPathRegexp = regExpToParseExpressPathRegExp.test(\n stackItem.regexp,\n );\n\n let newBasePath = basePath;\n\n if (isExpressPathRegexp) {\n const parsedPath = parseExpressPath(stackItem.regexp, stackItem.keys);\n\n newBasePath += `/${parsedPath}`;\n } else if (\n !stackItem.path &&\n stackItem.regexp &&\n stackItem.regexp.toString() !== EXPRESS_ROOT_PATH_REGEXP_VALUE\n ) {\n const regExpPath = ` RegExp(${stackItem.regexp}) `;\n\n newBasePath += `/${regExpPath}`;\n }\n\n endpoints = parseEndpoints(stackItem.handle, newBasePath, endpoints);\n }\n });\n\n return endpoints;\n};\n\nconst getEndpoints = function (app) {\n const endpoints = parseEndpoints(app);\n return endpoints.flatMap((route) =>\n route.methods\n .filter((method) => ![\"HEAD\", \"OPTIONS\"].includes(method.toUpperCase()))\n .map((method) => ({\n method,\n path: route.path,\n })),\n );\n};\n\nexport default getEndpoints;\n"],"mappings":"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","names":["import_crypto","getLogger","createLogger","level","process","env","APITALLY_DEBUG","format","combine","colorize","timestamp","printf","info","message","transports","Console","isValidClientId","clientId","regexExp","test","isValidEnv","env","RequestCounter","requestCounts","requestSizeSums","responseSizeSums","responseTimes","requestSizes","responseSizes","constructor","Map","getKey","requestInfo","consumer","method","toUpperCase","path","statusCode","join","addRequest","key","set","get","has","responseTimeMap","responseTimeMsBin","Math","floor","responseTime","requestSize","undefined","Number","requestSizeMap","requestSizeKbBin","responseSize","responseSizeMap","responseSizeKbBin","getAndResetRequests","data","forEach","count","statusCodeStr","split","push","status_code","parseInt","request_count","request_size_sum","response_size_sum","response_times","Object","fromEntries","request_sizes","response_sizes","clear","MAX_MSG_LENGTH","MAX_STACKTRACE_LENGTH","ServerErrorCounter","errorCounts","errorDetails","sentryEventIds","sentry","constructor","Map","tryImportSentry","addServerError","serverError","key","getKey","has","set","get","captureSentryEventId","getAndResetServerErrors","data","forEach","count","push","consumer","method","path","type","msg","getTruncatedMessage","traceback","getTruncatedStack","sentry_event_id","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest","length","suffix","cutoff","substring","stack","lines","split","truncatedLines","line","serverErrorKey","lastEventId","eventId","e","import_crypto","ValidationErrorCounter","errorCounts","errorDetails","constructor","Map","addValidationError","validationError","key","getKey","has","set","get","getAndResetValidationErrors","data","forEach","count","push","consumer","method","path","loc","split","msg","type","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest","SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","constructor","reason","status","ApitallyClient","clientId","env","instanceUuid","requestsDataQueue","syncIntervalId","appInfo","appInfoSent","requestCounter","validationErrorCounter","serverErrorCounter","logger","instance","isValidClientId","isValidEnv","randomUUID","RequestCounter","ValidationErrorCounter","ServerErrorCounter","getLogger","startSync","handleShutdown","bind","getInstance","shutdown","stopSync","sendRequestsData","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","makeHubRequest","url","payload","fetchWithRetry","fetchRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","sync","setInterval","setTimeout","clearInterval","promises","push","sendAppInfo","Promise","all","error","setAppInfo","debug","instance_uuid","message_uuid","handled","handleHubError","message","newPayload","time_offset","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","Date","now","failedItems","length","queueItem","shift","time","timeOffset","getPackageVersion","name","_require","createRequire","url","version","error","regExpToParseExpressPathRegExp","regExpToReplaceExpressPathRegExpParams","regexpExpressParamRegexp","EXPRESS_ROOT_PATH_REGEXP_VALUE","STACK_ITEM_VALID_NAMES","getRouteMethods","route","methods","Object","keys","filter","method","map","toUpperCase","getRouteMiddlewares","stack","item","handle","name","hasParams","expressPathRegExp","test","parseExpressRoute","basePath","paths","Array","isArray","path","push","endpoints","completePath","endpoint","middlewares","parseExpressPath","params","expressPathRegExpExec","exec","parsedRegExp","toString","paramIndex","paramName","paramId","replace","parsedPath","parseEndpoints","app","_router","addEndpoints","parseStack","currentEndpoints","endpointsToAdd","forEach","newEndpoint","existingEndpoint","find","undefined","newMethods","includes","concat","stackItem","newEndpoints","isExpressPathRegexp","regexp","newBasePath","regExpPath","getEndpoints","flatMap","useApitally","app","config","client","ApitallyClient","middleware","getMiddleware","use","setTimeout","setAppInfo","getAppInfo","appVersion","validatorInstalled","getPackageVersion","celebrateInstalled","nestInstalled","classValidatorInstalled","errorHandlerConfigured","req","res","next","err","locals","serverError","startTime","performance","now","originalJson","json","body","call","on","route","responseTime","consumer","getConsumer","requestCounter","addRequest","method","path","statusCode","requestSize","get","responseSize","validationErrors","push","extractExpressValidatorErrors","extractCelebrateErrors","extractNestValidationErrors","forEach","error","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","message","traceback","stack","logger","request","response","apitallyConsumer","String","consumerIdentifier","responseBody","errors","Array","isArray","location","loc","validation","Object","values","source","keys","key","subsetJoiMessage","messageWithKey","split","find","includes","versions","process","version","replace","expressVersion","apitallyVersion","paths","listEndpoints","fromEntries"]}
1
+ {"version":3,"sources":["../../src/nestjs/index.ts","../../src/express/middleware.ts","../../src/common/client.ts","../../src/common/consumerRegistry.ts","../../src/common/logging.ts","../../src/common/paramValidation.ts","../../src/common/requestCounter.ts","../../src/common/serverErrorCounter.ts","../../src/common/validationErrorCounter.ts","../../src/common/packageVersions.ts","../../src/express/listEndpoints.js"],"sourcesContent":["export type { ApitallyConsumer } from \"../common/types.js\";\nexport { useApitally } from \"../express/index.js\";\n","import type { Express, NextFunction, Request, Response, Router } from \"express\";\nimport { performance } from \"perf_hooks\";\n\nimport { ApitallyClient } from \"../common/client.js\";\nimport { consumerFromStringOrObject } from \"../common/consumerRegistry.js\";\nimport { getPackageVersion } from \"../common/packageVersions.js\";\nimport {\n ApitallyConfig,\n ApitallyConsumer,\n StartupData,\n ValidationError,\n} from \"../common/types.js\";\nimport listEndpoints from \"./listEndpoints.js\";\n\ndeclare module \"express\" {\n interface Request {\n apitallyConsumer?: ApitallyConsumer | string | null;\n consumerIdentifier?: ApitallyConsumer | string | null; // For backwards compatibility\n }\n}\n\nexport const useApitally = (app: Express | Router, config: ApitallyConfig) => {\n const client = new ApitallyClient(config);\n const middleware = getMiddleware(app, client);\n app.use(middleware);\n setTimeout(() => {\n client.setStartupData(getAppInfo(app, config.appVersion));\n }, 1000);\n};\n\nconst getMiddleware = (app: Express | Router, client: ApitallyClient) => {\n const validatorInstalled = getPackageVersion(\"express-validator\") !== null;\n const celebrateInstalled = getPackageVersion(\"celebrate\") !== null;\n const nestInstalled = getPackageVersion(\"@nestjs/core\") !== null;\n const classValidatorInstalled = getPackageVersion(\"class-validator\") !== null;\n let errorHandlerConfigured = false;\n\n return (req: Request, res: Response, next: NextFunction) => {\n if (!errorHandlerConfigured) {\n // Add error handling middleware to the bottom of the stack when handling the first request\n app.use(\n (err: Error, req: Request, res: Response, next: NextFunction): void => {\n res.locals.serverError = err;\n next(err);\n },\n );\n errorHandlerConfigured = true;\n }\n try {\n const startTime = performance.now();\n const originalJson = res.json;\n res.json = (body) => {\n res.locals.body = body;\n return originalJson.call(res, body);\n };\n res.on(\"finish\", () => {\n try {\n if (req.route) {\n const responseTime = performance.now() - startTime;\n const consumer = getConsumer(req);\n client.consumerRegistry.addOrUpdateConsumer(consumer);\n client.requestCounter.addRequest({\n consumer: consumer?.identifier,\n method: req.method,\n path: req.route.path,\n statusCode: res.statusCode,\n responseTime: responseTime,\n requestSize: req.get(\"content-length\"),\n responseSize: res.get(\"content-length\"),\n });\n if (\n (res.statusCode === 400 || res.statusCode === 422) &&\n res.locals.body\n ) {\n const validationErrors: ValidationError[] = [];\n if (validatorInstalled) {\n validationErrors.push(\n ...extractExpressValidatorErrors(res.locals.body),\n );\n }\n if (celebrateInstalled) {\n validationErrors.push(\n ...extractCelebrateErrors(res.locals.body),\n );\n }\n if (nestInstalled && classValidatorInstalled) {\n validationErrors.push(\n ...extractNestValidationErrors(res.locals.body),\n );\n }\n validationErrors.forEach((error) => {\n client.validationErrorCounter.addValidationError({\n consumer: consumer?.identifier,\n method: req.method,\n path: req.route.path,\n ...error,\n });\n });\n }\n if (res.statusCode === 500 && res.locals.serverError) {\n const serverError = res.locals.serverError as Error;\n client.serverErrorCounter.addServerError({\n consumer: consumer?.identifier,\n method: req.method,\n path: req.route.path,\n type: serverError.name,\n msg: serverError.message,\n traceback: serverError.stack || \"\",\n });\n }\n }\n } catch (error) {\n client.logger.error(\n \"Error while logging request in Apitally middleware.\",\n { request: req, response: res, error },\n );\n }\n });\n } catch (error) {\n client.logger.error(\"Error in Apitally middleware.\", {\n request: req,\n response: res,\n error,\n });\n } finally {\n next();\n }\n };\n};\n\nconst getConsumer = (req: Request) => {\n if (req.apitallyConsumer) {\n return consumerFromStringOrObject(req.apitallyConsumer);\n } else if (req.consumerIdentifier) {\n // For backwards compatibility\n process.emitWarning(\n \"The consumerIdentifier property on the request object is deprecated. Use apitallyConsumer instead.\",\n \"DeprecationWarning\",\n );\n return consumerFromStringOrObject(req.consumerIdentifier);\n }\n return null;\n};\n\nconst extractExpressValidatorErrors = (responseBody: any) => {\n const errors: ValidationError[] = [];\n if (\n responseBody &&\n responseBody.errors &&\n Array.isArray(responseBody.errors)\n ) {\n responseBody.errors.forEach((error: any) => {\n if (error.location && error.path && error.msg && error.type) {\n errors.push({\n loc: `${error.location}.${error.path}`,\n msg: error.msg,\n type: error.type,\n });\n }\n });\n }\n return errors;\n};\n\nconst extractCelebrateErrors = (responseBody: any) => {\n const errors: ValidationError[] = [];\n if (responseBody && responseBody.validation) {\n Object.values(responseBody.validation).forEach((error: any) => {\n if (\n error.source &&\n error.keys &&\n Array.isArray(error.keys) &&\n error.message\n ) {\n error.keys.forEach((key: string) => {\n errors.push({\n loc: `${error.source}.${key}`,\n msg: subsetJoiMessage(error.message, key),\n type: \"\",\n });\n });\n }\n });\n }\n return errors;\n};\n\nconst extractNestValidationErrors = (responseBody: any) => {\n const errors: ValidationError[] = [];\n if (responseBody && Array.isArray(responseBody.message)) {\n responseBody.message.forEach((message: any) => {\n errors.push({\n loc: \"\",\n msg: message,\n type: \"\",\n });\n });\n }\n return errors;\n};\n\nconst subsetJoiMessage = (message: string, key: string) => {\n const messageWithKey = message\n .split(\". \")\n .find((message) => message.includes(`\"${key}\"`));\n return messageWithKey ? messageWithKey : message;\n};\n\nconst getAppInfo = (\n app: Express | Router,\n appVersion?: string,\n): StartupData => {\n const versions: Array<[string, string]> = [\n [\"nodejs\", process.version.replace(/^v/, \"\")],\n ];\n const expressVersion = getPackageVersion(\"express\");\n const apitallyVersion = getPackageVersion(\"../..\");\n if (expressVersion) {\n versions.push([\"express\", expressVersion]);\n }\n if (apitallyVersion) {\n versions.push([\"apitally\", apitallyVersion]);\n }\n if (appVersion) {\n versions.push([\"app\", appVersion]);\n }\n return {\n paths: listEndpoints(app),\n versions: Object.fromEntries(versions),\n client: \"js:express\",\n };\n};\n","import { randomUUID } from \"crypto\";\nimport fetchRetry from \"fetch-retry\";\nimport ConsumerRegistry from \"./consumerRegistry.js\";\nimport { Logger, getLogger } from \"./logging.js\";\nimport { isValidClientId, isValidEnv } from \"./paramValidation.js\";\nimport RequestCounter from \"./requestCounter.js\";\nimport ServerErrorCounter from \"./serverErrorCounter.js\";\nimport {\n ApitallyConfig,\n StartupData,\n StartupPayload,\n SyncPayload,\n} from \"./types.js\";\nimport ValidationErrorCounter from \"./validationErrorCounter.js\";\n\nconst SYNC_INTERVAL = 60000; // 60 seconds\nconst INITIAL_SYNC_INTERVAL = 10000; // 10 seconds\nconst INITIAL_SYNC_INTERVAL_DURATION = 3600000; // 1 hour\nconst MAX_QUEUE_TIME = 3.6e6; // 1 hour\n\nclass HTTPError extends Error {\n public response: Response;\n\n constructor(response: Response) {\n const reason = response.status\n ? `status code ${response.status}`\n : \"an unknown error\";\n super(`Request failed with ${reason}`);\n this.response = response;\n }\n}\n\nexport class ApitallyClient {\n private clientId: string;\n private env: string;\n\n private static instance?: ApitallyClient;\n private instanceUuid: string;\n private syncDataQueue: Array<[number, SyncPayload]>;\n private syncIntervalId?: NodeJS.Timeout;\n public startupData?: StartupData;\n private startupDataSent: boolean = false;\n\n public requestCounter: RequestCounter;\n public validationErrorCounter: ValidationErrorCounter;\n public serverErrorCounter: ServerErrorCounter;\n public consumerRegistry: ConsumerRegistry;\n public logger: Logger;\n\n constructor({ clientId, env = \"dev\", logger }: ApitallyConfig) {\n if (ApitallyClient.instance) {\n throw new Error(\"Apitally client is already initialized\");\n }\n if (!isValidClientId(clientId)) {\n throw new Error(\n `Invalid client ID '${clientId}' (expecting hexadeciaml UUID format)`,\n );\n }\n if (!isValidEnv(env)) {\n throw new Error(\n `Invalid env '${env}' (expecting 1-32 alphanumeric lowercase characters and hyphens only)`,\n );\n }\n\n ApitallyClient.instance = this;\n this.clientId = clientId;\n this.env = env;\n this.instanceUuid = randomUUID();\n this.syncDataQueue = [];\n this.requestCounter = new RequestCounter();\n this.validationErrorCounter = new ValidationErrorCounter();\n this.serverErrorCounter = new ServerErrorCounter();\n this.consumerRegistry = new ConsumerRegistry();\n this.logger = logger || getLogger();\n\n this.startSync();\n this.handleShutdown = this.handleShutdown.bind(this);\n }\n\n public static getInstance() {\n if (!ApitallyClient.instance) {\n throw new Error(\"Apitally client is not initialized\");\n }\n return ApitallyClient.instance;\n }\n\n public static async shutdown() {\n if (ApitallyClient.instance) {\n await ApitallyClient.instance.handleShutdown();\n }\n }\n\n public async handleShutdown() {\n this.stopSync();\n await this.sendSyncData();\n ApitallyClient.instance = undefined;\n }\n\n private getHubUrlPrefix() {\n const baseURL =\n process.env.APITALLY_HUB_BASE_URL || \"https://hub.apitally.io\";\n const version = \"v2\";\n return `${baseURL}/${version}/${this.clientId}/${this.env}/`;\n }\n\n private async sendData(url: string, payload: any) {\n const fetchWithRetry = fetchRetry(fetch, {\n retries: 3,\n retryDelay: 1000,\n retryOn: [408, 429, 500, 502, 503, 504],\n });\n const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {\n method: \"POST\",\n body: JSON.stringify(payload),\n headers: { \"Content-Type\": \"application/json\" },\n });\n if (!response.ok) {\n throw new HTTPError(response);\n }\n }\n\n private startSync() {\n this.sync();\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, INITIAL_SYNC_INTERVAL);\n setTimeout(() => {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = setInterval(() => {\n this.sync();\n }, SYNC_INTERVAL);\n }, INITIAL_SYNC_INTERVAL_DURATION);\n }\n\n private async sync() {\n try {\n const promises = [this.sendSyncData()];\n if (!this.startupDataSent) {\n promises.push(this.sendStartupData());\n }\n await Promise.all(promises);\n } catch (error) {\n this.logger.error(\"Error while syncing with Apitally Hub\", {\n error,\n });\n }\n }\n\n private stopSync() {\n if (this.syncIntervalId) {\n clearInterval(this.syncIntervalId);\n this.syncIntervalId = undefined;\n }\n }\n\n public setStartupData(data: StartupData) {\n this.startupData = data;\n this.startupDataSent = false;\n this.sendStartupData();\n }\n\n private async sendStartupData() {\n if (this.startupData) {\n this.logger.debug(\"Sending startup data to Apitally Hub\");\n const payload: StartupPayload = {\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n ...this.startupData,\n };\n try {\n await this.sendData(\"startup\", payload);\n this.startupDataSent = true;\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.error((error as Error).message);\n this.logger.debug(\n \"Error while sending startup data to Apitally Hub (will retry)\",\n { error },\n );\n }\n }\n }\n }\n\n private async sendSyncData() {\n this.logger.debug(\"Synchronizing data with Apitally Hub\");\n const newPayload: SyncPayload = {\n time_offset: 0,\n instance_uuid: this.instanceUuid,\n message_uuid: randomUUID(),\n requests: this.requestCounter.getAndResetRequests(),\n validation_errors:\n this.validationErrorCounter.getAndResetValidationErrors(),\n server_errors: this.serverErrorCounter.getAndResetServerErrors(),\n consumers: this.consumerRegistry.getAndResetUpdatedConsumers(),\n };\n this.syncDataQueue.push([Date.now(), newPayload]);\n\n const failedItems: [number, SyncPayload][] = [];\n while (this.syncDataQueue.length > 0) {\n const queueItem = this.syncDataQueue.shift();\n if (queueItem) {\n const [time, payload] = queueItem;\n try {\n const timeOffset = Date.now() - time;\n if (timeOffset <= MAX_QUEUE_TIME) {\n payload.time_offset = timeOffset / 1000.0; // In seconds\n await this.sendData(\"sync\", payload);\n }\n } catch (error) {\n const handled = this.handleHubError(error);\n if (!handled) {\n this.logger.debug(\n \"Error while synchronizing data with Apitally Hub (will retry)\",\n { error },\n );\n failedItems.push(queueItem);\n }\n }\n }\n }\n this.syncDataQueue = failedItems;\n }\n\n private handleHubError(error: unknown) {\n if (error instanceof HTTPError) {\n if (error.response.status === 404) {\n this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);\n this.stopSync();\n return true;\n }\n if (error.response.status === 422) {\n this.logger.error(\"Received validation error from Apitally Hub\");\n return true;\n }\n }\n return false;\n }\n}\n","import { ApitallyConsumer } from \"./types.js\";\n\nexport const consumerFromStringOrObject = (\n consumer: ApitallyConsumer | string,\n) => {\n if (typeof consumer === \"string\") {\n consumer = String(consumer).trim().substring(0, 128);\n return consumer ? { identifier: consumer } : null;\n } else {\n consumer.identifier = String(consumer.identifier).trim().substring(0, 128);\n consumer.name = consumer.name?.trim().substring(0, 64);\n consumer.group = consumer.group?.trim().substring(0, 64);\n return consumer.identifier ? consumer : null;\n }\n};\n\nexport default class ConsumerRegistry {\n private consumers: Map<string, ApitallyConsumer>;\n private updated: Set<string>;\n\n constructor() {\n this.consumers = new Map();\n this.updated = new Set();\n }\n\n public addOrUpdateConsumer(consumer?: ApitallyConsumer | null) {\n if (!consumer || (!consumer.name && !consumer.group)) {\n return;\n }\n const existing = this.consumers.get(consumer.identifier);\n if (!existing) {\n this.consumers.set(consumer.identifier, consumer);\n this.updated.add(consumer.identifier);\n } else {\n if (consumer.name && consumer.name !== existing.name) {\n existing.name = consumer.name;\n this.updated.add(consumer.identifier);\n }\n if (consumer.group && consumer.group !== existing.group) {\n existing.group = consumer.group;\n this.updated.add(consumer.identifier);\n }\n }\n }\n\n public getAndResetUpdatedConsumers() {\n const data: Array<ApitallyConsumer> = [];\n this.updated.forEach((identifier) => {\n const consumer = this.consumers.get(identifier);\n if (consumer) {\n data.push(consumer);\n }\n });\n this.updated.clear();\n return data;\n }\n}\n","import { createLogger, format, transports } from \"winston\";\n\nexport interface Logger {\n debug: (message: string, meta?: object) => void;\n info: (message: string, meta?: object) => void;\n warn: (message: string, meta?: object) => void;\n error: (message: string, meta?: object) => void;\n}\n\nexport const getLogger = () => {\n return createLogger({\n level: process.env.APITALLY_DEBUG ? \"debug\" : \"warn\",\n format: format.combine(\n format.colorize(),\n format.timestamp(),\n format.printf(\n (info) => `${info.timestamp} ${info.level}: ${info.message}`,\n ),\n ),\n transports: [new transports.Console()],\n });\n};\n","export function isValidClientId(clientId: string): boolean {\n const regexExp =\n /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n return regexExp.test(clientId);\n}\n\nexport function isValidEnv(env: string): boolean {\n const regexExp = /^[\\w-]{1,32}$/;\n return regexExp.test(env);\n}\n","import { RequestInfo, RequestsItem } from \"./types.js\";\n\nexport default class RequestCounter {\n private requestCounts: Map<string, number>;\n private requestSizeSums: Map<string, number>;\n private responseSizeSums: Map<string, number>;\n private responseTimes: Map<string, Map<number, number>>;\n private requestSizes: Map<string, Map<number, number>>;\n private responseSizes: Map<string, Map<number, number>>;\n\n constructor() {\n this.requestCounts = new Map<string, number>();\n this.requestSizeSums = new Map<string, number>();\n this.responseSizeSums = new Map<string, number>();\n this.responseTimes = new Map<string, Map<number, number>>();\n this.requestSizes = new Map<string, Map<number, number>>();\n this.responseSizes = new Map<string, Map<number, number>>();\n }\n\n private getKey(requestInfo: RequestInfo) {\n return [\n requestInfo.consumer || \"\",\n requestInfo.method.toUpperCase(),\n requestInfo.path,\n requestInfo.statusCode,\n ].join(\"|\");\n }\n\n addRequest(requestInfo: RequestInfo) {\n const key = this.getKey(requestInfo);\n\n // Increment request count\n this.requestCounts.set(key, (this.requestCounts.get(key) || 0) + 1);\n\n // Add response time\n if (!this.responseTimes.has(key)) {\n this.responseTimes.set(key, new Map<number, number>());\n }\n const responseTimeMap = this.responseTimes.get(key)!;\n const responseTimeMsBin = Math.floor(requestInfo.responseTime / 10) * 10; // Rounded to nearest 10ms\n responseTimeMap.set(\n responseTimeMsBin,\n (responseTimeMap.get(responseTimeMsBin) || 0) + 1,\n );\n\n // Add request size\n if (requestInfo.requestSize !== undefined) {\n requestInfo.requestSize = Number(requestInfo.requestSize);\n this.requestSizeSums.set(\n key,\n (this.requestSizeSums.get(key) || 0) + requestInfo.requestSize,\n );\n if (!this.requestSizes.has(key)) {\n this.requestSizes.set(key, new Map<number, number>());\n }\n const requestSizeMap = this.requestSizes.get(key)!;\n const requestSizeKbBin = Math.floor(requestInfo.requestSize / 1000); // Rounded down to nearest KB\n requestSizeMap.set(\n requestSizeKbBin,\n (requestSizeMap.get(requestSizeKbBin) || 0) + 1,\n );\n }\n\n // Add response size\n if (requestInfo.responseSize !== undefined) {\n requestInfo.responseSize = Number(requestInfo.responseSize);\n this.responseSizeSums.set(\n key,\n (this.responseSizeSums.get(key) || 0) + requestInfo.responseSize,\n );\n if (!this.responseSizes.has(key)) {\n this.responseSizes.set(key, new Map<number, number>());\n }\n const responseSizeMap = this.responseSizes.get(key)!;\n const responseSizeKbBin = Math.floor(requestInfo.responseSize / 1000); // Rounded down to nearest KB\n responseSizeMap.set(\n responseSizeKbBin,\n (responseSizeMap.get(responseSizeKbBin) || 0) + 1,\n );\n }\n }\n\n getAndResetRequests() {\n const data: Array<RequestsItem> = [];\n this.requestCounts.forEach((count, key) => {\n const [consumer, method, path, statusCodeStr] = key.split(\"|\");\n const responseTimes =\n this.responseTimes.get(key) || new Map<number, number>();\n const requestSizes =\n this.requestSizes.get(key) || new Map<number, number>();\n const responseSizes =\n this.responseSizes.get(key) || new Map<number, number>();\n data.push({\n consumer: consumer || null,\n method,\n path,\n status_code: parseInt(statusCodeStr),\n request_count: count,\n request_size_sum: this.requestSizeSums.get(key) || 0,\n response_size_sum: this.responseSizeSums.get(key) || 0,\n response_times: Object.fromEntries(responseTimes),\n request_sizes: Object.fromEntries(requestSizes),\n response_sizes: Object.fromEntries(responseSizes),\n });\n });\n\n // Reset the counts and times\n this.requestCounts.clear();\n this.requestSizeSums.clear();\n this.responseSizeSums.clear();\n this.responseTimes.clear();\n this.requestSizes.clear();\n this.responseSizes.clear();\n\n return data;\n }\n}\n","import type * as Sentry from \"@sentry/node\";\nimport { createHash } from \"crypto\";\n\nimport { ConsumerMethodPath, ServerError, ServerErrorsItem } from \"./types.js\";\n\nconst MAX_MSG_LENGTH = 2048;\nconst MAX_STACKTRACE_LENGTH = 65536;\n\nexport default class ServerErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ServerError>;\n private sentryEventIds: Map<string, string>;\n private sentry: typeof Sentry | undefined;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n this.sentryEventIds = new Map();\n this.tryImportSentry();\n }\n\n public addServerError(serverError: ConsumerMethodPath & ServerError) {\n const key = this.getKey(serverError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, serverError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n this.captureSentryEventId(key);\n }\n\n public getAndResetServerErrors() {\n const data: Array<ServerErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const serverError = this.errorDetails.get(key);\n if (serverError) {\n data.push({\n consumer: serverError.consumer || null,\n method: serverError.method,\n path: serverError.path,\n type: serverError.type,\n msg: this.getTruncatedMessage(serverError.msg),\n traceback: this.getTruncatedStack(serverError.traceback),\n sentry_event_id: this.sentryEventIds.get(key) || null,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(serverError: ConsumerMethodPath & ServerError) {\n const hashInput = [\n serverError.consumer || \"\",\n serverError.method.toUpperCase(),\n serverError.path,\n serverError.type,\n serverError.msg.trim(),\n serverError.traceback.trim(),\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n\n private getTruncatedMessage(msg: string) {\n msg = msg.trim();\n if (msg.length <= MAX_MSG_LENGTH) {\n return msg;\n }\n const suffix = \"... (truncated)\";\n const cutoff = MAX_MSG_LENGTH - suffix.length;\n return msg.substring(0, cutoff) + suffix;\n }\n\n private getTruncatedStack(stack: string) {\n const suffix = \"... (truncated) ...\";\n const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;\n const lines = stack.trim().split(\"\\n\");\n const truncatedLines: string[] = [];\n let length = 0;\n for (const line of lines) {\n if (length + line.length + 1 > cutoff) {\n truncatedLines.push(suffix);\n break;\n }\n truncatedLines.push(line);\n length += line.length + 1;\n }\n return truncatedLines.join(\"\\n\");\n }\n\n private captureSentryEventId(serverErrorKey: string) {\n if (this.sentry && this.sentry.lastEventId) {\n const eventId = this.sentry.lastEventId();\n if (eventId) {\n this.sentryEventIds.set(serverErrorKey, eventId);\n }\n }\n }\n\n private async tryImportSentry() {\n try {\n this.sentry = await import(\"@sentry/node\");\n } catch (e) {\n // Sentry SDK is not installed, ignore\n }\n }\n}\n","import { createHash } from \"crypto\";\n\nimport {\n ConsumerMethodPath,\n ValidationError,\n ValidationErrorsItem,\n} from \"./types.js\";\n\nexport default class ValidationErrorCounter {\n private errorCounts: Map<string, number>;\n private errorDetails: Map<string, ConsumerMethodPath & ValidationError>;\n\n constructor() {\n this.errorCounts = new Map();\n this.errorDetails = new Map();\n }\n\n public addValidationError(\n validationError: ConsumerMethodPath & ValidationError,\n ) {\n const key = this.getKey(validationError);\n if (!this.errorDetails.has(key)) {\n this.errorDetails.set(key, validationError);\n }\n this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);\n }\n\n public getAndResetValidationErrors() {\n const data: Array<ValidationErrorsItem> = [];\n this.errorCounts.forEach((count, key) => {\n const validationError = this.errorDetails.get(key);\n if (validationError) {\n data.push({\n consumer: validationError.consumer || null,\n method: validationError.method,\n path: validationError.path,\n loc: validationError.loc.split(\".\"),\n msg: validationError.msg,\n type: validationError.type,\n error_count: count,\n });\n }\n });\n this.errorCounts.clear();\n this.errorDetails.clear();\n return data;\n }\n\n private getKey(validationError: ConsumerMethodPath & ValidationError) {\n const hashInput = [\n validationError.consumer || \"\",\n validationError.method.toUpperCase(),\n validationError.path,\n validationError.loc,\n validationError.msg.trim(),\n validationError.type,\n ].join(\"|\");\n return createHash(\"md5\").update(hashInput).digest(\"hex\");\n }\n}\n","import { createRequire } from \"module\";\n\nexport function getPackageVersion(name: string): string | null {\n try {\n const _require = createRequire(import.meta.url);\n return _require(`${name}/package.json`).version || null;\n } catch (error) {\n return null;\n }\n}\n","// Adapted from https://github.com/AlbertoFdzM/express-list-endpoints/blob/305535d43008b46f34e18b01947762e039af6d2d/src/index.js\n\n/**\n * @typedef {Object} Route\n * @property {Object} methods\n * @property {string | string[]} path\n * @property {any[]} stack\n *\n * @typedef {Object} Endpoint\n * @property {string} path Path name\n * @property {string[]} methods Methods handled\n * @property {string[]} middlewares Mounted middlewares\n */\n\nconst regExpToParseExpressPathRegExp =\n /^\\/\\^\\\\\\/(?:(:?[\\w\\\\.-]*(?:\\\\\\/:?[\\w\\\\.-]*)*)|(\\(\\?:\\([^)]+\\)\\)))\\\\\\/.*/;\nconst regExpToReplaceExpressPathRegExpParams = /\\(\\?:\\([^)]+\\)\\)/;\nconst regexpExpressParamRegexp = /\\(\\?:\\([^)]+\\)\\)/g;\nconst regexpExpressPathParamRegexp = /(:[^)]+)\\([^)]+\\)/g;\n\nconst EXPRESS_ROOT_PATH_REGEXP_VALUE = \"/^\\\\/?(?=\\\\/|$)/i\";\nconst STACK_ITEM_VALID_NAMES = [\"router\", \"bound dispatch\", \"mounted_app\"];\n\n/**\n * Returns all the verbs detected for the passed route\n * @param {Route} route\n */\nconst getRouteMethods = function (route) {\n let methods = Object.keys(route.methods);\n\n methods = methods.filter((method) => method !== \"_all\");\n methods = methods.map((method) => method.toUpperCase());\n\n return methods;\n};\n\n/**\n * Returns the names (or anonymous) of all the middlewares attached to the\n * passed route\n * @param {Route} route\n * @returns {string[]}\n */\nconst getRouteMiddlewares = function (route) {\n return route.stack.map((item) => {\n return item.handle.name || \"anonymous\";\n });\n};\n\n/**\n * Returns true if found regexp related with express params\n * @param {string} expressPathRegExp\n * @returns {boolean}\n */\nconst hasParams = function (expressPathRegExp) {\n return regexpExpressParamRegexp.test(expressPathRegExp);\n};\n\n/**\n * @param {Route} route Express route object to be parsed\n * @param {string} basePath The basePath the route is on\n * @return {Endpoint[]} Endpoints info\n */\nconst parseExpressRoute = function (route, basePath) {\n const paths = [];\n\n if (Array.isArray(route.path)) {\n paths.push(...route.path);\n } else {\n paths.push(route.path);\n }\n\n /** @type {Endpoint[]} */\n const endpoints = paths.map((path) => {\n const completePath =\n basePath && path === \"/\" ? basePath : `${basePath}${path}`;\n\n /** @type {Endpoint} */\n const endpoint = {\n path: completePath.replace(regexpExpressPathParamRegexp, \"$1\"),\n methods: getRouteMethods(route),\n middlewares: getRouteMiddlewares(route),\n };\n\n return endpoint;\n });\n\n return endpoints;\n};\n\n/**\n * @param {RegExp} expressPathRegExp\n * @param {any[]} params\n * @returns {string}\n */\nconst parseExpressPath = function (expressPathRegExp, params) {\n let parsedRegExp = expressPathRegExp.toString();\n let expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp);\n let paramIndex = 0;\n\n while (hasParams(parsedRegExp)) {\n const paramName = params[paramIndex].name;\n const paramId = `:${paramName}`;\n\n parsedRegExp = parsedRegExp.replace(\n regExpToReplaceExpressPathRegExpParams,\n paramId,\n );\n\n paramIndex++;\n }\n\n if (parsedRegExp !== expressPathRegExp.toString()) {\n expressPathRegExpExec = regExpToParseExpressPathRegExp.exec(parsedRegExp);\n }\n\n const parsedPath = expressPathRegExpExec[1].replace(/\\\\\\//g, \"/\");\n\n return parsedPath;\n};\n\n/**\n * @param {import('express').Express | import('express').Router | any} app\n * @param {string} [basePath]\n * @param {Endpoint[]} [endpoints]\n * @returns {Endpoint[]}\n */\nconst parseEndpoints = function (app, basePath, endpoints) {\n const stack = app.stack || (app._router && app._router.stack);\n\n endpoints = endpoints || [];\n basePath = basePath || \"\";\n\n if (!stack) {\n if (endpoints.length) {\n endpoints = addEndpoints(endpoints, [\n {\n path: basePath,\n methods: [],\n middlewares: [],\n },\n ]);\n }\n } else {\n endpoints = parseStack(stack, basePath, endpoints);\n }\n\n return endpoints;\n};\n\n/**\n * Ensures the path of the new endpoints isn't yet in the array.\n * If the path is already in the array merges the endpoints with the existing\n * one, if not, it adds them to the array.\n *\n * @param {Endpoint[]} currentEndpoints Array of current endpoints\n * @param {Endpoint[]} endpointsToAdd New endpoints to be added to the array\n * @returns {Endpoint[]} Updated endpoints array\n */\nconst addEndpoints = function (currentEndpoints, endpointsToAdd) {\n endpointsToAdd.forEach((newEndpoint) => {\n const existingEndpoint = currentEndpoints.find(\n (endpoint) => endpoint.path === newEndpoint.path,\n );\n\n if (existingEndpoint !== undefined) {\n const newMethods = newEndpoint.methods.filter(\n (method) => !existingEndpoint.methods.includes(method),\n );\n\n existingEndpoint.methods = existingEndpoint.methods.concat(newMethods);\n } else {\n currentEndpoints.push(newEndpoint);\n }\n });\n\n return currentEndpoints;\n};\n\n/**\n * @param {any[]} stack\n * @param {string} basePath\n * @param {Endpoint[]} endpoints\n * @returns {Endpoint[]}\n */\nconst parseStack = function (stack, basePath, endpoints) {\n stack.forEach((stackItem) => {\n if (stackItem.route) {\n const newEndpoints = parseExpressRoute(stackItem.route, basePath);\n\n endpoints = addEndpoints(endpoints, newEndpoints);\n } else if (STACK_ITEM_VALID_NAMES.includes(stackItem.name)) {\n const isExpressPathRegexp = regExpToParseExpressPathRegExp.test(\n stackItem.regexp,\n );\n\n let newBasePath = basePath;\n\n if (isExpressPathRegexp) {\n const parsedPath = parseExpressPath(stackItem.regexp, stackItem.keys);\n\n newBasePath += `/${parsedPath}`;\n } else if (\n !stackItem.path &&\n stackItem.regexp &&\n stackItem.regexp.toString() !== EXPRESS_ROOT_PATH_REGEXP_VALUE\n ) {\n const regExpPath = ` RegExp(${stackItem.regexp}) `;\n\n newBasePath += `/${regExpPath}`;\n }\n\n endpoints = parseEndpoints(stackItem.handle, newBasePath, endpoints);\n }\n });\n\n return endpoints;\n};\n\nconst getEndpoints = function (app) {\n const endpoints = parseEndpoints(app);\n return endpoints.flatMap((route) =>\n route.methods\n .filter((method) => ![\"HEAD\", \"OPTIONS\"].includes(method.toUpperCase()))\n .map((method) => ({\n method,\n path: route.path,\n })),\n );\n};\n\nexport default getEndpoints;\n"],"mappings":"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","names":["import_crypto","consumerFromStringOrObject","consumer","_a","String","trim","substring","identifier","name","group","ConsumerRegistry","consumers","updated","constructor","Map","Set","addOrUpdateConsumer","existing","get","set","add","getAndResetUpdatedConsumers","data","forEach","push","clear","getLogger","createLogger","level","process","env","APITALLY_DEBUG","format","combine","colorize","timestamp","printf","info","message","transports","Console","isValidClientId","clientId","regexExp","test","isValidEnv","env","RequestCounter","requestCounts","requestSizeSums","responseSizeSums","responseTimes","requestSizes","responseSizes","constructor","Map","getKey","requestInfo","consumer","method","toUpperCase","path","statusCode","join","addRequest","key","set","get","has","responseTimeMap","responseTimeMsBin","Math","floor","responseTime","requestSize","undefined","Number","requestSizeMap","requestSizeKbBin","responseSize","responseSizeMap","responseSizeKbBin","getAndResetRequests","data","forEach","count","statusCodeStr","split","push","status_code","parseInt","request_count","request_size_sum","response_size_sum","response_times","Object","fromEntries","request_sizes","response_sizes","clear","MAX_MSG_LENGTH","MAX_STACKTRACE_LENGTH","ServerErrorCounter","errorCounts","errorDetails","sentryEventIds","sentry","constructor","Map","tryImportSentry","addServerError","serverError","key","getKey","has","set","get","captureSentryEventId","getAndResetServerErrors","data","forEach","count","push","consumer","method","path","type","msg","getTruncatedMessage","traceback","getTruncatedStack","sentry_event_id","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest","length","suffix","cutoff","substring","stack","lines","split","truncatedLines","line","serverErrorKey","lastEventId","eventId","e","import_crypto","ValidationErrorCounter","errorCounts","errorDetails","constructor","Map","addValidationError","validationError","key","getKey","has","set","get","getAndResetValidationErrors","data","forEach","count","push","consumer","method","path","loc","split","msg","type","error_count","clear","hashInput","toUpperCase","trim","join","createHash","update","digest","SYNC_INTERVAL","INITIAL_SYNC_INTERVAL","INITIAL_SYNC_INTERVAL_DURATION","MAX_QUEUE_TIME","HTTPError","Error","response","constructor","reason","status","ApitallyClient","clientId","env","instanceUuid","syncDataQueue","syncIntervalId","startupData","startupDataSent","requestCounter","validationErrorCounter","serverErrorCounter","consumerRegistry","logger","instance","isValidClientId","isValidEnv","randomUUID","RequestCounter","ValidationErrorCounter","ServerErrorCounter","ConsumerRegistry","getLogger","startSync","handleShutdown","bind","getInstance","shutdown","stopSync","sendSyncData","undefined","getHubUrlPrefix","baseURL","process","APITALLY_HUB_BASE_URL","version","sendData","url","payload","fetchWithRetry","fetchRetry","fetch","retries","retryDelay","retryOn","method","body","JSON","stringify","headers","ok","sync","setInterval","setTimeout","clearInterval","promises","push","sendStartupData","Promise","all","error","setStartupData","data","debug","instance_uuid","message_uuid","handled","handleHubError","message","newPayload","time_offset","requests","getAndResetRequests","validation_errors","getAndResetValidationErrors","server_errors","getAndResetServerErrors","consumers","getAndResetUpdatedConsumers","Date","now","failedItems","length","queueItem","shift","time","timeOffset","getPackageVersion","name","_require","createRequire","url","version","error","regExpToParseExpressPathRegExp","regExpToReplaceExpressPathRegExpParams","regexpExpressParamRegexp","regexpExpressPathParamRegexp","EXPRESS_ROOT_PATH_REGEXP_VALUE","STACK_ITEM_VALID_NAMES","getRouteMethods","route","methods","Object","keys","filter","method","map","toUpperCase","getRouteMiddlewares","stack","item","handle","name","hasParams","expressPathRegExp","test","parseExpressRoute","basePath","paths","Array","isArray","path","push","endpoints","completePath","endpoint","replace","middlewares","parseExpressPath","params","parsedRegExp","toString","expressPathRegExpExec","exec","paramIndex","paramName","paramId","parsedPath","parseEndpoints","app","_router","length","addEndpoints","parseStack","currentEndpoints","endpointsToAdd","forEach","newEndpoint","existingEndpoint","find","undefined","newMethods","includes","concat","stackItem","newEndpoints","isExpressPathRegexp","regexp","newBasePath","regExpPath","getEndpoints","flatMap","useApitally","app","config","client","ApitallyClient","middleware","getMiddleware","use","setTimeout","setStartupData","getAppInfo","appVersion","validatorInstalled","getPackageVersion","celebrateInstalled","nestInstalled","classValidatorInstalled","errorHandlerConfigured","req","res","next","err","locals","serverError","startTime","performance","now","originalJson","json","body","call","on","route","responseTime","consumer","getConsumer","consumerRegistry","addOrUpdateConsumer","requestCounter","addRequest","identifier","method","path","statusCode","requestSize","get","responseSize","validationErrors","push","extractExpressValidatorErrors","extractCelebrateErrors","extractNestValidationErrors","forEach","error","validationErrorCounter","addValidationError","serverErrorCounter","addServerError","type","name","msg","message","traceback","stack","logger","request","response","apitallyConsumer","consumerFromStringOrObject","consumerIdentifier","process","emitWarning","responseBody","errors","Array","isArray","location","loc","validation","Object","values","source","keys","key","subsetJoiMessage","messageWithKey","split","find","includes","versions","version","replace","expressVersion","apitallyVersion","paths","listEndpoints","fromEntries"]}
@@ -1,5 +1,5 @@
1
+ export { ApitallyConsumer } from '../common/types.cjs';
1
2
  export { useApitally } from '../express/middleware.cjs';
2
- import 'express';
3
- import '../common/types.cjs';
4
3
  import '../common/logging.cjs';
5
4
  import 'winston';
5
+ import 'express';