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
@@ -8,19 +8,24 @@ type ApitallyConfig = {
8
8
  appVersion?: string;
9
9
  logger?: Logger;
10
10
  };
11
+ type ApitallyConsumer = {
12
+ identifier: string;
13
+ name?: string | null;
14
+ group?: string | null;
15
+ };
11
16
  type PathInfo = {
12
17
  method: string;
13
18
  path: string;
14
19
  };
15
- type AppInfo = {
20
+ type StartupData = {
16
21
  paths: PathInfo[];
17
22
  versions: Record<string, string>;
18
23
  client: string;
19
24
  };
20
- type AppInfoPayload = {
25
+ type StartupPayload = {
21
26
  instance_uuid: string;
22
27
  message_uuid: string;
23
- } & AppInfo;
28
+ } & StartupData;
24
29
  type ConsumerMethodPath = {
25
30
  consumer?: string | null;
26
31
  method: string;
@@ -64,13 +69,15 @@ type ServerErrorsItem = ConsumerMethodPath & {
64
69
  sentry_event_id: string | null;
65
70
  error_count: number;
66
71
  };
67
- type RequestsDataPayload = {
72
+ type ConsumerItem = ApitallyConsumer;
73
+ type SyncPayload = {
68
74
  time_offset: number;
69
75
  instance_uuid: string;
70
76
  message_uuid: string;
71
77
  requests: Array<RequestsItem>;
72
78
  validation_errors: Array<ValidationErrorsItem>;
73
79
  server_errors: Array<ServerErrorsItem>;
80
+ consumers: Array<ConsumerItem>;
74
81
  };
75
82
 
76
- export type { ApitallyConfig, AppInfo, AppInfoPayload, ConsumerMethodPath, PathInfo, RequestInfo, RequestsDataPayload, RequestsItem, ServerError, ServerErrorsItem, ValidationError, ValidationErrorsItem };
83
+ export type { ApitallyConfig, ApitallyConsumer, ConsumerItem, ConsumerMethodPath, PathInfo, RequestInfo, RequestsItem, ServerError, ServerErrorsItem, StartupData, StartupPayload, SyncPayload, ValidationError, ValidationErrorsItem };
@@ -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");