apitally 0.6.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. package/dist/common/client.cjs +559 -0
  2. package/dist/common/client.cjs.map +1 -0
  3. package/dist/common/client.d.cts +38 -0
  4. package/dist/common/client.d.ts +38 -0
  5. package/dist/common/client.js +526 -0
  6. package/dist/common/client.js.map +1 -0
  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/logging.cjs +41 -0
  14. package/dist/common/logging.cjs.map +1 -0
  15. package/dist/common/logging.d.cts +11 -0
  16. package/{lib/types → dist}/common/logging.d.ts +6 -3
  17. package/dist/common/logging.js +18 -0
  18. package/dist/common/logging.js.map +1 -0
  19. package/dist/common/packageVersions.cjs +42 -0
  20. package/dist/common/packageVersions.cjs.map +1 -0
  21. package/dist/common/packageVersions.d.cts +3 -0
  22. package/dist/common/packageVersions.d.ts +3 -0
  23. package/dist/common/packageVersions.js +18 -0
  24. package/dist/common/packageVersions.js.map +1 -0
  25. package/dist/common/paramValidation.cjs +43 -0
  26. package/dist/common/paramValidation.cjs.map +1 -0
  27. package/dist/common/paramValidation.d.cts +4 -0
  28. package/dist/common/paramValidation.d.ts +4 -0
  29. package/dist/common/paramValidation.js +19 -0
  30. package/dist/common/paramValidation.js.map +1 -0
  31. package/dist/common/requestCounter.cjs +111 -0
  32. package/dist/common/requestCounter.cjs.map +1 -0
  33. package/dist/common/requestCounter.d.cts +18 -0
  34. package/{lib/types → dist}/common/requestCounter.d.ts +7 -3
  35. package/dist/common/requestCounter.js +92 -0
  36. package/dist/common/requestCounter.js.map +1 -0
  37. package/dist/common/serverErrorCounter.cjs +133 -0
  38. package/dist/common/serverErrorCounter.cjs.map +1 -0
  39. package/dist/common/serverErrorCounter.d.cts +20 -0
  40. package/{lib/types → dist}/common/serverErrorCounter.d.ts +7 -3
  41. package/dist/common/serverErrorCounter.js +104 -0
  42. package/dist/common/serverErrorCounter.js.map +1 -0
  43. package/dist/common/types.cjs +19 -0
  44. package/dist/common/types.cjs.map +1 -0
  45. package/dist/common/types.d.cts +83 -0
  46. package/{lib/types → dist}/common/types.d.ts +25 -15
  47. package/dist/common/types.js +1 -0
  48. package/dist/common/types.js.map +1 -0
  49. package/dist/common/validationErrorCounter.cjs +76 -0
  50. package/dist/common/validationErrorCounter.cjs.map +1 -0
  51. package/dist/common/validationErrorCounter.d.cts +14 -0
  52. package/{lib/types → dist}/common/validationErrorCounter.d.ts +7 -3
  53. package/dist/common/validationErrorCounter.js +57 -0
  54. package/dist/common/validationErrorCounter.js.map +1 -0
  55. package/dist/express/index.cjs +898 -0
  56. package/dist/express/index.cjs.map +1 -0
  57. package/dist/express/index.d.cts +5 -0
  58. package/dist/express/index.d.ts +5 -0
  59. package/dist/express/index.js +862 -0
  60. package/dist/express/index.js.map +1 -0
  61. package/dist/express/listEndpoints.cjs +144 -0
  62. package/dist/express/listEndpoints.cjs.map +1 -0
  63. package/dist/express/listEndpoints.d.cts +3 -0
  64. package/dist/express/listEndpoints.d.ts +3 -0
  65. package/dist/express/listEndpoints.js +125 -0
  66. package/dist/express/listEndpoints.js.map +1 -0
  67. package/dist/express/middleware.cjs +896 -0
  68. package/dist/express/middleware.cjs.map +1 -0
  69. package/dist/express/middleware.d.cts +14 -0
  70. package/dist/express/middleware.d.ts +14 -0
  71. package/dist/express/middleware.js +862 -0
  72. package/dist/express/middleware.js.map +1 -0
  73. package/dist/fastify/index.cjs +754 -0
  74. package/dist/fastify/index.cjs.map +1 -0
  75. package/dist/fastify/index.d.cts +5 -0
  76. package/dist/fastify/index.d.ts +5 -0
  77. package/dist/fastify/index.js +718 -0
  78. package/dist/fastify/index.js.map +1 -0
  79. package/dist/fastify/plugin.cjs +748 -0
  80. package/dist/fastify/plugin.cjs.map +1 -0
  81. package/dist/fastify/plugin.d.cts +18 -0
  82. package/dist/fastify/plugin.d.ts +18 -0
  83. package/dist/fastify/plugin.js +718 -0
  84. package/dist/fastify/plugin.js.map +1 -0
  85. package/dist/index.cjs +2 -0
  86. package/dist/index.cjs.map +1 -0
  87. package/dist/index.d.cts +2 -0
  88. package/dist/index.d.ts +2 -0
  89. package/dist/index.js +1 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/koa/index.cjs +718 -0
  92. package/dist/koa/index.cjs.map +1 -0
  93. package/dist/koa/index.d.cts +5 -0
  94. package/dist/koa/index.d.ts +5 -0
  95. package/dist/koa/index.js +682 -0
  96. package/dist/koa/index.js.map +1 -0
  97. package/dist/koa/middleware.cjs +718 -0
  98. package/dist/koa/middleware.cjs.map +1 -0
  99. package/dist/koa/middleware.d.cts +8 -0
  100. package/dist/koa/middleware.d.ts +8 -0
  101. package/dist/koa/middleware.js +682 -0
  102. package/dist/koa/middleware.js.map +1 -0
  103. package/dist/nestjs/index.cjs +898 -0
  104. package/dist/nestjs/index.cjs.map +1 -0
  105. package/dist/nestjs/index.d.cts +5 -0
  106. package/dist/nestjs/index.d.ts +5 -0
  107. package/dist/nestjs/index.js +862 -0
  108. package/dist/nestjs/index.js.map +1 -0
  109. package/package.json +55 -92
  110. package/lib/cjs/common/client.cjs +0 -334
  111. package/lib/cjs/common/client.cjs.map +0 -1
  112. package/lib/cjs/common/logging.cjs +0 -17
  113. package/lib/cjs/common/logging.cjs.map +0 -1
  114. package/lib/cjs/common/packageVersions.cjs +0 -16
  115. package/lib/cjs/common/packageVersions.cjs.map +0 -1
  116. package/lib/cjs/common/paramValidation.cjs +0 -16
  117. package/lib/cjs/common/paramValidation.cjs.map +0 -1
  118. package/lib/cjs/common/requestCounter.cjs +0 -108
  119. package/lib/cjs/common/requestCounter.cjs.map +0 -1
  120. package/lib/cjs/common/serverErrorCounter.cjs +0 -154
  121. package/lib/cjs/common/serverErrorCounter.cjs.map +0 -1
  122. package/lib/cjs/common/types.cjs +0 -6
  123. package/lib/cjs/common/types.cjs.map +0 -1
  124. package/lib/cjs/common/validationErrorCounter.cjs +0 -60
  125. package/lib/cjs/common/validationErrorCounter.cjs.map +0 -1
  126. package/lib/cjs/express/index.cjs +0 -13
  127. package/lib/cjs/express/index.cjs.map +0 -1
  128. package/lib/cjs/express/listEndpoints.cjs +0 -177
  129. package/lib/cjs/express/listEndpoints.cjs.map +0 -1
  130. package/lib/cjs/express/middleware.cjs +0 -189
  131. package/lib/cjs/express/middleware.cjs.map +0 -1
  132. package/lib/cjs/fastify/index.cjs +0 -14
  133. package/lib/cjs/fastify/index.cjs.map +0 -1
  134. package/lib/cjs/fastify/plugin.cjs +0 -171
  135. package/lib/cjs/fastify/plugin.cjs.map +0 -1
  136. package/lib/cjs/index.cjs +0 -6
  137. package/lib/cjs/index.cjs.map +0 -1
  138. package/lib/cjs/koa/index.cjs +0 -13
  139. package/lib/cjs/koa/index.cjs.map +0 -1
  140. package/lib/cjs/koa/middleware.cjs +0 -139
  141. package/lib/cjs/koa/middleware.cjs.map +0 -1
  142. package/lib/cjs/nestjs/index.cjs +0 -13
  143. package/lib/cjs/nestjs/index.cjs.map +0 -1
  144. package/lib/esm/common/client.js +0 -324
  145. package/lib/esm/common/client.js.map +0 -1
  146. package/lib/esm/common/logging.js +0 -11
  147. package/lib/esm/common/logging.js.map +0 -1
  148. package/lib/esm/common/packageVersions.js +0 -10
  149. package/lib/esm/common/packageVersions.js.map +0 -1
  150. package/lib/esm/common/paramValidation.js +0 -9
  151. package/lib/esm/common/paramValidation.js.map +0 -1
  152. package/lib/esm/common/requestCounter.js +0 -102
  153. package/lib/esm/common/requestCounter.js.map +0 -1
  154. package/lib/esm/common/serverErrorCounter.js +0 -143
  155. package/lib/esm/common/serverErrorCounter.js.map +0 -1
  156. package/lib/esm/common/types.js +0 -1
  157. package/lib/esm/common/types.js.map +0 -1
  158. package/lib/esm/common/validationErrorCounter.js +0 -54
  159. package/lib/esm/common/validationErrorCounter.js.map +0 -1
  160. package/lib/esm/express/index.js +0 -1
  161. package/lib/esm/express/index.js.map +0 -1
  162. package/lib/esm/express/listEndpoints.js +0 -170
  163. package/lib/esm/express/listEndpoints.js.map +0 -1
  164. package/lib/esm/express/middleware.js +0 -182
  165. package/lib/esm/express/middleware.js.map +0 -1
  166. package/lib/esm/fastify/index.js +0 -1
  167. package/lib/esm/fastify/index.js.map +0 -1
  168. package/lib/esm/fastify/plugin.js +0 -164
  169. package/lib/esm/fastify/plugin.js.map +0 -1
  170. package/lib/esm/index.js +0 -1
  171. package/lib/esm/index.js.map +0 -1
  172. package/lib/esm/koa/index.js +0 -1
  173. package/lib/esm/koa/index.js.map +0 -1
  174. package/lib/esm/koa/middleware.js +0 -132
  175. package/lib/esm/koa/middleware.js.map +0 -1
  176. package/lib/esm/nestjs/index.js +0 -1
  177. package/lib/esm/nestjs/index.js.map +0 -1
  178. package/lib/types/common/client.d.ts +0 -33
  179. package/lib/types/common/client.d.ts.map +0 -1
  180. package/lib/types/common/logging.d.ts.map +0 -1
  181. package/lib/types/common/packageVersions.d.ts +0 -2
  182. package/lib/types/common/packageVersions.d.ts.map +0 -1
  183. package/lib/types/common/paramValidation.d.ts +0 -3
  184. package/lib/types/common/paramValidation.d.ts.map +0 -1
  185. package/lib/types/common/requestCounter.d.ts.map +0 -1
  186. package/lib/types/common/serverErrorCounter.d.ts.map +0 -1
  187. package/lib/types/common/types.d.ts.map +0 -1
  188. package/lib/types/common/validationErrorCounter.d.ts.map +0 -1
  189. package/lib/types/express/index.d.ts +0 -2
  190. package/lib/types/express/index.d.ts.map +0 -1
  191. package/lib/types/express/listEndpoints.d.ts +0 -3
  192. package/lib/types/express/listEndpoints.d.ts.map +0 -1
  193. package/lib/types/express/middleware.d.ts +0 -10
  194. package/lib/types/express/middleware.d.ts.map +0 -1
  195. package/lib/types/fastify/index.d.ts +0 -2
  196. package/lib/types/fastify/index.d.ts.map +0 -1
  197. package/lib/types/fastify/plugin.d.ts +0 -15
  198. package/lib/types/fastify/plugin.d.ts.map +0 -1
  199. package/lib/types/index.d.ts +0 -2
  200. package/lib/types/index.d.ts.map +0 -1
  201. package/lib/types/koa/index.d.ts +0 -2
  202. package/lib/types/koa/index.d.ts.map +0 -1
  203. package/lib/types/koa/middleware.d.ts +0 -4
  204. package/lib/types/koa/middleware.d.ts.map +0 -1
  205. package/lib/types/nestjs/index.d.ts +0 -2
  206. package/lib/types/nestjs/index.d.ts.map +0 -1
@@ -0,0 +1,748 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
10
+ var __export = (target, all) => {
11
+ for (var name in all)
12
+ __defProp(target, name, { get: all[name], enumerable: true });
13
+ };
14
+ var __copyProps = (to, from, except, desc) => {
15
+ if (from && typeof from === "object" || typeof from === "function") {
16
+ for (let key of __getOwnPropNames(from))
17
+ if (!__hasOwnProp.call(to, key) && key !== except)
18
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
23
+ // If the importer is in node compatibility mode or this is not an ESM
24
+ // file that has been converted to a CommonJS file using a Babel-
25
+ // compatible transform (i.e. "__esModule" has not been set), then set
26
+ // "default" to the CommonJS "module.exports" for node compatibility.
27
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
28
+ mod
29
+ ));
30
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
31
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
32
+
33
+ // src/fastify/plugin.ts
34
+ var plugin_exports = {};
35
+ __export(plugin_exports, {
36
+ default: () => plugin_default
37
+ });
38
+ module.exports = __toCommonJS(plugin_exports);
39
+ var import_fastify_plugin = __toESM(require("fastify-plugin"), 1);
40
+
41
+ // src/common/client.ts
42
+ var import_crypto3 = require("crypto");
43
+ var import_fetch_retry = __toESM(require("fetch-retry"), 1);
44
+
45
+ // src/common/consumerRegistry.ts
46
+ var consumerFromStringOrObject = /* @__PURE__ */ __name((consumer) => {
47
+ var _a2, _b;
48
+ if (typeof consumer === "string") {
49
+ consumer = String(consumer).trim().substring(0, 128);
50
+ return consumer ? {
51
+ identifier: consumer
52
+ } : null;
53
+ } else {
54
+ consumer.identifier = String(consumer.identifier).trim().substring(0, 128);
55
+ consumer.name = (_a2 = consumer.name) == null ? void 0 : _a2.trim().substring(0, 64);
56
+ consumer.group = (_b = consumer.group) == null ? void 0 : _b.trim().substring(0, 64);
57
+ return consumer.identifier ? consumer : null;
58
+ }
59
+ }, "consumerFromStringOrObject");
60
+ var _ConsumerRegistry = class _ConsumerRegistry {
61
+ consumers;
62
+ updated;
63
+ constructor() {
64
+ this.consumers = /* @__PURE__ */ new Map();
65
+ this.updated = /* @__PURE__ */ new Set();
66
+ }
67
+ addOrUpdateConsumer(consumer) {
68
+ if (!consumer || !consumer.name && !consumer.group) {
69
+ return;
70
+ }
71
+ const existing = this.consumers.get(consumer.identifier);
72
+ if (!existing) {
73
+ this.consumers.set(consumer.identifier, consumer);
74
+ this.updated.add(consumer.identifier);
75
+ } else {
76
+ if (consumer.name && consumer.name !== existing.name) {
77
+ existing.name = consumer.name;
78
+ this.updated.add(consumer.identifier);
79
+ }
80
+ if (consumer.group && consumer.group !== existing.group) {
81
+ existing.group = consumer.group;
82
+ this.updated.add(consumer.identifier);
83
+ }
84
+ }
85
+ }
86
+ getAndResetUpdatedConsumers() {
87
+ const data = [];
88
+ this.updated.forEach((identifier) => {
89
+ const consumer = this.consumers.get(identifier);
90
+ if (consumer) {
91
+ data.push(consumer);
92
+ }
93
+ });
94
+ this.updated.clear();
95
+ return data;
96
+ }
97
+ };
98
+ __name(_ConsumerRegistry, "ConsumerRegistry");
99
+ var ConsumerRegistry = _ConsumerRegistry;
100
+
101
+ // src/common/logging.ts
102
+ var import_winston = require("winston");
103
+ var getLogger = /* @__PURE__ */ __name(() => {
104
+ return (0, import_winston.createLogger)({
105
+ level: process.env.APITALLY_DEBUG ? "debug" : "warn",
106
+ format: import_winston.format.combine(import_winston.format.colorize(), import_winston.format.timestamp(), import_winston.format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`)),
107
+ transports: [
108
+ new import_winston.transports.Console()
109
+ ]
110
+ });
111
+ }, "getLogger");
112
+
113
+ // src/common/paramValidation.ts
114
+ function isValidClientId(clientId) {
115
+ const regexExp = /^[0-9a-f]{8}-[0-9a-f]{4}-[4][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
116
+ return regexExp.test(clientId);
117
+ }
118
+ __name(isValidClientId, "isValidClientId");
119
+ function isValidEnv(env) {
120
+ const regexExp = /^[\w-]{1,32}$/;
121
+ return regexExp.test(env);
122
+ }
123
+ __name(isValidEnv, "isValidEnv");
124
+
125
+ // src/common/requestCounter.ts
126
+ var _RequestCounter = class _RequestCounter {
127
+ requestCounts;
128
+ requestSizeSums;
129
+ responseSizeSums;
130
+ responseTimes;
131
+ requestSizes;
132
+ responseSizes;
133
+ constructor() {
134
+ this.requestCounts = /* @__PURE__ */ new Map();
135
+ this.requestSizeSums = /* @__PURE__ */ new Map();
136
+ this.responseSizeSums = /* @__PURE__ */ new Map();
137
+ this.responseTimes = /* @__PURE__ */ new Map();
138
+ this.requestSizes = /* @__PURE__ */ new Map();
139
+ this.responseSizes = /* @__PURE__ */ new Map();
140
+ }
141
+ getKey(requestInfo) {
142
+ return [
143
+ requestInfo.consumer || "",
144
+ requestInfo.method.toUpperCase(),
145
+ requestInfo.path,
146
+ requestInfo.statusCode
147
+ ].join("|");
148
+ }
149
+ addRequest(requestInfo) {
150
+ const key = this.getKey(requestInfo);
151
+ this.requestCounts.set(key, (this.requestCounts.get(key) || 0) + 1);
152
+ if (!this.responseTimes.has(key)) {
153
+ this.responseTimes.set(key, /* @__PURE__ */ new Map());
154
+ }
155
+ const responseTimeMap = this.responseTimes.get(key);
156
+ const responseTimeMsBin = Math.floor(requestInfo.responseTime / 10) * 10;
157
+ responseTimeMap.set(responseTimeMsBin, (responseTimeMap.get(responseTimeMsBin) || 0) + 1);
158
+ if (requestInfo.requestSize !== void 0) {
159
+ requestInfo.requestSize = Number(requestInfo.requestSize);
160
+ this.requestSizeSums.set(key, (this.requestSizeSums.get(key) || 0) + requestInfo.requestSize);
161
+ if (!this.requestSizes.has(key)) {
162
+ this.requestSizes.set(key, /* @__PURE__ */ new Map());
163
+ }
164
+ const requestSizeMap = this.requestSizes.get(key);
165
+ const requestSizeKbBin = Math.floor(requestInfo.requestSize / 1e3);
166
+ requestSizeMap.set(requestSizeKbBin, (requestSizeMap.get(requestSizeKbBin) || 0) + 1);
167
+ }
168
+ if (requestInfo.responseSize !== void 0) {
169
+ requestInfo.responseSize = Number(requestInfo.responseSize);
170
+ this.responseSizeSums.set(key, (this.responseSizeSums.get(key) || 0) + requestInfo.responseSize);
171
+ if (!this.responseSizes.has(key)) {
172
+ this.responseSizes.set(key, /* @__PURE__ */ new Map());
173
+ }
174
+ const responseSizeMap = this.responseSizes.get(key);
175
+ const responseSizeKbBin = Math.floor(requestInfo.responseSize / 1e3);
176
+ responseSizeMap.set(responseSizeKbBin, (responseSizeMap.get(responseSizeKbBin) || 0) + 1);
177
+ }
178
+ }
179
+ getAndResetRequests() {
180
+ const data = [];
181
+ this.requestCounts.forEach((count, key) => {
182
+ const [consumer, method, path, statusCodeStr] = key.split("|");
183
+ const responseTimes = this.responseTimes.get(key) || /* @__PURE__ */ new Map();
184
+ const requestSizes = this.requestSizes.get(key) || /* @__PURE__ */ new Map();
185
+ const responseSizes = this.responseSizes.get(key) || /* @__PURE__ */ new Map();
186
+ data.push({
187
+ consumer: consumer || null,
188
+ method,
189
+ path,
190
+ status_code: parseInt(statusCodeStr),
191
+ request_count: count,
192
+ request_size_sum: this.requestSizeSums.get(key) || 0,
193
+ response_size_sum: this.responseSizeSums.get(key) || 0,
194
+ response_times: Object.fromEntries(responseTimes),
195
+ request_sizes: Object.fromEntries(requestSizes),
196
+ response_sizes: Object.fromEntries(responseSizes)
197
+ });
198
+ });
199
+ this.requestCounts.clear();
200
+ this.requestSizeSums.clear();
201
+ this.responseSizeSums.clear();
202
+ this.responseTimes.clear();
203
+ this.requestSizes.clear();
204
+ this.responseSizes.clear();
205
+ return data;
206
+ }
207
+ };
208
+ __name(_RequestCounter, "RequestCounter");
209
+ var RequestCounter = _RequestCounter;
210
+
211
+ // src/common/serverErrorCounter.ts
212
+ var import_crypto = require("crypto");
213
+ var MAX_MSG_LENGTH = 2048;
214
+ var MAX_STACKTRACE_LENGTH = 65536;
215
+ var _ServerErrorCounter = class _ServerErrorCounter {
216
+ errorCounts;
217
+ errorDetails;
218
+ sentryEventIds;
219
+ sentry;
220
+ constructor() {
221
+ this.errorCounts = /* @__PURE__ */ new Map();
222
+ this.errorDetails = /* @__PURE__ */ new Map();
223
+ this.sentryEventIds = /* @__PURE__ */ new Map();
224
+ this.tryImportSentry();
225
+ }
226
+ addServerError(serverError) {
227
+ const key = this.getKey(serverError);
228
+ if (!this.errorDetails.has(key)) {
229
+ this.errorDetails.set(key, serverError);
230
+ }
231
+ this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
232
+ this.captureSentryEventId(key);
233
+ }
234
+ getAndResetServerErrors() {
235
+ const data = [];
236
+ this.errorCounts.forEach((count, key) => {
237
+ const serverError = this.errorDetails.get(key);
238
+ if (serverError) {
239
+ data.push({
240
+ consumer: serverError.consumer || null,
241
+ method: serverError.method,
242
+ path: serverError.path,
243
+ type: serverError.type,
244
+ msg: this.getTruncatedMessage(serverError.msg),
245
+ traceback: this.getTruncatedStack(serverError.traceback),
246
+ sentry_event_id: this.sentryEventIds.get(key) || null,
247
+ error_count: count
248
+ });
249
+ }
250
+ });
251
+ this.errorCounts.clear();
252
+ this.errorDetails.clear();
253
+ return data;
254
+ }
255
+ getKey(serverError) {
256
+ const hashInput = [
257
+ serverError.consumer || "",
258
+ serverError.method.toUpperCase(),
259
+ serverError.path,
260
+ serverError.type,
261
+ serverError.msg.trim(),
262
+ serverError.traceback.trim()
263
+ ].join("|");
264
+ return (0, import_crypto.createHash)("md5").update(hashInput).digest("hex");
265
+ }
266
+ getTruncatedMessage(msg) {
267
+ msg = msg.trim();
268
+ if (msg.length <= MAX_MSG_LENGTH) {
269
+ return msg;
270
+ }
271
+ const suffix = "... (truncated)";
272
+ const cutoff = MAX_MSG_LENGTH - suffix.length;
273
+ return msg.substring(0, cutoff) + suffix;
274
+ }
275
+ getTruncatedStack(stack) {
276
+ const suffix = "... (truncated) ...";
277
+ const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
278
+ const lines = stack.trim().split("\n");
279
+ const truncatedLines = [];
280
+ let length = 0;
281
+ for (const line of lines) {
282
+ if (length + line.length + 1 > cutoff) {
283
+ truncatedLines.push(suffix);
284
+ break;
285
+ }
286
+ truncatedLines.push(line);
287
+ length += line.length + 1;
288
+ }
289
+ return truncatedLines.join("\n");
290
+ }
291
+ captureSentryEventId(serverErrorKey) {
292
+ if (this.sentry && this.sentry.lastEventId) {
293
+ const eventId = this.sentry.lastEventId();
294
+ if (eventId) {
295
+ this.sentryEventIds.set(serverErrorKey, eventId);
296
+ }
297
+ }
298
+ }
299
+ async tryImportSentry() {
300
+ try {
301
+ this.sentry = await import("@sentry/node");
302
+ } catch (e) {
303
+ }
304
+ }
305
+ };
306
+ __name(_ServerErrorCounter, "ServerErrorCounter");
307
+ var ServerErrorCounter = _ServerErrorCounter;
308
+
309
+ // src/common/validationErrorCounter.ts
310
+ var import_crypto2 = require("crypto");
311
+ var _ValidationErrorCounter = class _ValidationErrorCounter {
312
+ errorCounts;
313
+ errorDetails;
314
+ constructor() {
315
+ this.errorCounts = /* @__PURE__ */ new Map();
316
+ this.errorDetails = /* @__PURE__ */ new Map();
317
+ }
318
+ addValidationError(validationError) {
319
+ const key = this.getKey(validationError);
320
+ if (!this.errorDetails.has(key)) {
321
+ this.errorDetails.set(key, validationError);
322
+ }
323
+ this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
324
+ }
325
+ getAndResetValidationErrors() {
326
+ const data = [];
327
+ this.errorCounts.forEach((count, key) => {
328
+ const validationError = this.errorDetails.get(key);
329
+ if (validationError) {
330
+ data.push({
331
+ consumer: validationError.consumer || null,
332
+ method: validationError.method,
333
+ path: validationError.path,
334
+ loc: validationError.loc.split("."),
335
+ msg: validationError.msg,
336
+ type: validationError.type,
337
+ error_count: count
338
+ });
339
+ }
340
+ });
341
+ this.errorCounts.clear();
342
+ this.errorDetails.clear();
343
+ return data;
344
+ }
345
+ getKey(validationError) {
346
+ const hashInput = [
347
+ validationError.consumer || "",
348
+ validationError.method.toUpperCase(),
349
+ validationError.path,
350
+ validationError.loc,
351
+ validationError.msg.trim(),
352
+ validationError.type
353
+ ].join("|");
354
+ return (0, import_crypto2.createHash)("md5").update(hashInput).digest("hex");
355
+ }
356
+ };
357
+ __name(_ValidationErrorCounter, "ValidationErrorCounter");
358
+ var ValidationErrorCounter = _ValidationErrorCounter;
359
+
360
+ // src/common/client.ts
361
+ var SYNC_INTERVAL = 6e4;
362
+ var INITIAL_SYNC_INTERVAL = 1e4;
363
+ var INITIAL_SYNC_INTERVAL_DURATION = 36e5;
364
+ var MAX_QUEUE_TIME = 36e5;
365
+ var _a;
366
+ var HTTPError = (_a = class extends Error {
367
+ response;
368
+ constructor(response) {
369
+ const reason = response.status ? `status code ${response.status}` : "an unknown error";
370
+ super(`Request failed with ${reason}`);
371
+ this.response = response;
372
+ }
373
+ }, __name(_a, "HTTPError"), _a);
374
+ var _ApitallyClient = class _ApitallyClient {
375
+ clientId;
376
+ env;
377
+ instanceUuid;
378
+ syncDataQueue;
379
+ syncIntervalId;
380
+ startupData;
381
+ startupDataSent = false;
382
+ requestCounter;
383
+ validationErrorCounter;
384
+ serverErrorCounter;
385
+ consumerRegistry;
386
+ logger;
387
+ constructor({ clientId, env = "dev", logger }) {
388
+ if (_ApitallyClient.instance) {
389
+ throw new Error("Apitally client is already initialized");
390
+ }
391
+ if (!isValidClientId(clientId)) {
392
+ throw new Error(`Invalid client ID '${clientId}' (expecting hexadeciaml UUID format)`);
393
+ }
394
+ if (!isValidEnv(env)) {
395
+ throw new Error(`Invalid env '${env}' (expecting 1-32 alphanumeric lowercase characters and hyphens only)`);
396
+ }
397
+ _ApitallyClient.instance = this;
398
+ this.clientId = clientId;
399
+ this.env = env;
400
+ this.instanceUuid = (0, import_crypto3.randomUUID)();
401
+ this.syncDataQueue = [];
402
+ this.requestCounter = new RequestCounter();
403
+ this.validationErrorCounter = new ValidationErrorCounter();
404
+ this.serverErrorCounter = new ServerErrorCounter();
405
+ this.consumerRegistry = new ConsumerRegistry();
406
+ this.logger = logger || getLogger();
407
+ this.startSync();
408
+ this.handleShutdown = this.handleShutdown.bind(this);
409
+ }
410
+ static getInstance() {
411
+ if (!_ApitallyClient.instance) {
412
+ throw new Error("Apitally client is not initialized");
413
+ }
414
+ return _ApitallyClient.instance;
415
+ }
416
+ static async shutdown() {
417
+ if (_ApitallyClient.instance) {
418
+ await _ApitallyClient.instance.handleShutdown();
419
+ }
420
+ }
421
+ async handleShutdown() {
422
+ this.stopSync();
423
+ await this.sendSyncData();
424
+ _ApitallyClient.instance = void 0;
425
+ }
426
+ getHubUrlPrefix() {
427
+ const baseURL = process.env.APITALLY_HUB_BASE_URL || "https://hub.apitally.io";
428
+ const version = "v2";
429
+ return `${baseURL}/${version}/${this.clientId}/${this.env}/`;
430
+ }
431
+ async sendData(url, payload) {
432
+ const fetchWithRetry = (0, import_fetch_retry.default)(fetch, {
433
+ retries: 3,
434
+ retryDelay: 1e3,
435
+ retryOn: [
436
+ 408,
437
+ 429,
438
+ 500,
439
+ 502,
440
+ 503,
441
+ 504
442
+ ]
443
+ });
444
+ const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {
445
+ method: "POST",
446
+ body: JSON.stringify(payload),
447
+ headers: {
448
+ "Content-Type": "application/json"
449
+ }
450
+ });
451
+ if (!response.ok) {
452
+ throw new HTTPError(response);
453
+ }
454
+ }
455
+ startSync() {
456
+ this.sync();
457
+ this.syncIntervalId = setInterval(() => {
458
+ this.sync();
459
+ }, INITIAL_SYNC_INTERVAL);
460
+ setTimeout(() => {
461
+ clearInterval(this.syncIntervalId);
462
+ this.syncIntervalId = setInterval(() => {
463
+ this.sync();
464
+ }, SYNC_INTERVAL);
465
+ }, INITIAL_SYNC_INTERVAL_DURATION);
466
+ }
467
+ async sync() {
468
+ try {
469
+ const promises = [
470
+ this.sendSyncData()
471
+ ];
472
+ if (!this.startupDataSent) {
473
+ promises.push(this.sendStartupData());
474
+ }
475
+ await Promise.all(promises);
476
+ } catch (error) {
477
+ this.logger.error("Error while syncing with Apitally Hub", {
478
+ error
479
+ });
480
+ }
481
+ }
482
+ stopSync() {
483
+ if (this.syncIntervalId) {
484
+ clearInterval(this.syncIntervalId);
485
+ this.syncIntervalId = void 0;
486
+ }
487
+ }
488
+ setStartupData(data) {
489
+ this.startupData = data;
490
+ this.startupDataSent = false;
491
+ this.sendStartupData();
492
+ }
493
+ async sendStartupData() {
494
+ if (this.startupData) {
495
+ this.logger.debug("Sending startup data to Apitally Hub");
496
+ const payload = {
497
+ instance_uuid: this.instanceUuid,
498
+ message_uuid: (0, import_crypto3.randomUUID)(),
499
+ ...this.startupData
500
+ };
501
+ try {
502
+ await this.sendData("startup", payload);
503
+ this.startupDataSent = true;
504
+ } catch (error) {
505
+ const handled = this.handleHubError(error);
506
+ if (!handled) {
507
+ this.logger.error(error.message);
508
+ this.logger.debug("Error while sending startup data to Apitally Hub (will retry)", {
509
+ error
510
+ });
511
+ }
512
+ }
513
+ }
514
+ }
515
+ async sendSyncData() {
516
+ this.logger.debug("Synchronizing data with Apitally Hub");
517
+ const newPayload = {
518
+ time_offset: 0,
519
+ instance_uuid: this.instanceUuid,
520
+ message_uuid: (0, import_crypto3.randomUUID)(),
521
+ requests: this.requestCounter.getAndResetRequests(),
522
+ validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
523
+ server_errors: this.serverErrorCounter.getAndResetServerErrors(),
524
+ consumers: this.consumerRegistry.getAndResetUpdatedConsumers()
525
+ };
526
+ this.syncDataQueue.push([
527
+ Date.now(),
528
+ newPayload
529
+ ]);
530
+ const failedItems = [];
531
+ while (this.syncDataQueue.length > 0) {
532
+ const queueItem = this.syncDataQueue.shift();
533
+ if (queueItem) {
534
+ const [time, payload] = queueItem;
535
+ try {
536
+ const timeOffset = Date.now() - time;
537
+ if (timeOffset <= MAX_QUEUE_TIME) {
538
+ payload.time_offset = timeOffset / 1e3;
539
+ await this.sendData("sync", payload);
540
+ }
541
+ } catch (error) {
542
+ const handled = this.handleHubError(error);
543
+ if (!handled) {
544
+ this.logger.debug("Error while synchronizing data with Apitally Hub (will retry)", {
545
+ error
546
+ });
547
+ failedItems.push(queueItem);
548
+ }
549
+ }
550
+ }
551
+ }
552
+ this.syncDataQueue = failedItems;
553
+ }
554
+ handleHubError(error) {
555
+ if (error instanceof HTTPError) {
556
+ if (error.response.status === 404) {
557
+ this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);
558
+ this.stopSync();
559
+ return true;
560
+ }
561
+ if (error.response.status === 422) {
562
+ this.logger.error("Received validation error from Apitally Hub");
563
+ return true;
564
+ }
565
+ }
566
+ return false;
567
+ }
568
+ };
569
+ __name(_ApitallyClient, "ApitallyClient");
570
+ __publicField(_ApitallyClient, "instance");
571
+ var ApitallyClient = _ApitallyClient;
572
+
573
+ // src/common/packageVersions.ts
574
+ var import_module = require("module");
575
+ var import_meta = {};
576
+ function getPackageVersion(name) {
577
+ try {
578
+ const _require = (0, import_module.createRequire)(import_meta.url);
579
+ return _require(`${name}/package.json`).version || null;
580
+ } catch (error) {
581
+ return null;
582
+ }
583
+ }
584
+ __name(getPackageVersion, "getPackageVersion");
585
+
586
+ // src/fastify/plugin.ts
587
+ var apitallyPlugin = /* @__PURE__ */ __name(async (fastify, config) => {
588
+ const client = new ApitallyClient(config);
589
+ const routes = [];
590
+ fastify.decorateRequest("apitallyConsumer", null);
591
+ fastify.decorateRequest("consumerIdentifier", null);
592
+ fastify.decorateReply("payload", null);
593
+ fastify.addHook("onRoute", (routeOptions) => {
594
+ const methods = Array.isArray(routeOptions.method) ? routeOptions.method : [
595
+ routeOptions.method
596
+ ];
597
+ methods.forEach((method) => {
598
+ if (![
599
+ "HEAD",
600
+ "OPTIONS"
601
+ ].includes(method.toUpperCase())) {
602
+ routes.push({
603
+ method: method.toUpperCase(),
604
+ path: routeOptions.url
605
+ });
606
+ }
607
+ });
608
+ });
609
+ fastify.addHook("onReady", () => {
610
+ client.setStartupData(getAppInfo(routes, config.appVersion));
611
+ });
612
+ fastify.addHook("onClose", async () => {
613
+ await client.handleShutdown();
614
+ });
615
+ fastify.addHook("onSend", (request, reply, payload, done) => {
616
+ try {
617
+ reply.payload = JSON.parse(payload);
618
+ } catch (error) {
619
+ }
620
+ done();
621
+ });
622
+ fastify.addHook("onError", (request, reply, error, done) => {
623
+ if (!error.statusCode || error.statusCode === 500) {
624
+ reply.serverError = error;
625
+ }
626
+ done();
627
+ });
628
+ fastify.addHook("onResponse", (request, reply, done) => {
629
+ if (request.method.toUpperCase() !== "OPTIONS") {
630
+ const consumer = getConsumer(request);
631
+ const path = "routeOptions" in request ? request.routeOptions.url : request.routerPath;
632
+ const requestSize = request.headers["content-length"];
633
+ let responseSize = reply.getHeader("content-length");
634
+ if (Array.isArray(responseSize)) {
635
+ responseSize = responseSize[0];
636
+ }
637
+ client.consumerRegistry.addOrUpdateConsumer(consumer);
638
+ client.requestCounter.addRequest({
639
+ consumer: consumer == null ? void 0 : consumer.identifier,
640
+ method: request.method,
641
+ path,
642
+ statusCode: reply.statusCode,
643
+ responseTime: getResponseTime(reply),
644
+ requestSize,
645
+ responseSize
646
+ });
647
+ if ((reply.statusCode === 400 || reply.statusCode === 422) && reply.payload && (!reply.payload.code || reply.payload.code === "FST_ERR_VALIDATION") && typeof reply.payload.message === "string") {
648
+ const validationErrors = extractAjvErrors(reply.payload.message);
649
+ validationErrors.forEach((error) => {
650
+ client.validationErrorCounter.addValidationError({
651
+ consumer: consumer == null ? void 0 : consumer.identifier,
652
+ method: request.method,
653
+ path,
654
+ ...error
655
+ });
656
+ });
657
+ }
658
+ if (reply.statusCode === 500 && reply.serverError) {
659
+ client.serverErrorCounter.addServerError({
660
+ consumer: consumer == null ? void 0 : consumer.identifier,
661
+ method: request.method,
662
+ path,
663
+ type: reply.serverError.name,
664
+ msg: reply.serverError.message,
665
+ traceback: reply.serverError.stack || ""
666
+ });
667
+ }
668
+ }
669
+ done();
670
+ });
671
+ }, "apitallyPlugin");
672
+ var getAppInfo = /* @__PURE__ */ __name((routes, appVersion) => {
673
+ const versions = [
674
+ [
675
+ "nodejs",
676
+ process.version.replace(/^v/, "")
677
+ ]
678
+ ];
679
+ const fastifyVersion = getPackageVersion("fastify");
680
+ const apitallyVersion = getPackageVersion("../..");
681
+ if (fastifyVersion) {
682
+ versions.push([
683
+ "fastify",
684
+ fastifyVersion
685
+ ]);
686
+ }
687
+ if (apitallyVersion) {
688
+ versions.push([
689
+ "apitally",
690
+ apitallyVersion
691
+ ]);
692
+ }
693
+ if (appVersion) {
694
+ versions.push([
695
+ "app",
696
+ appVersion
697
+ ]);
698
+ }
699
+ return {
700
+ paths: routes,
701
+ versions: Object.fromEntries(versions),
702
+ client: "js:fastify"
703
+ };
704
+ }, "getAppInfo");
705
+ var getConsumer = /* @__PURE__ */ __name((request) => {
706
+ if (request.apitallyConsumer) {
707
+ return consumerFromStringOrObject(request.apitallyConsumer);
708
+ } else if (request.consumerIdentifier) {
709
+ process.emitWarning("The consumerIdentifier property on the request object is deprecated. Use apitallyConsumer instead.", "DeprecationWarning");
710
+ return consumerFromStringOrObject(request.consumerIdentifier);
711
+ }
712
+ return null;
713
+ }, "getConsumer");
714
+ var getResponseTime = /* @__PURE__ */ __name((reply) => {
715
+ if (reply.elapsedTime !== void 0) {
716
+ return reply.elapsedTime;
717
+ } else if (reply.getResponseTime !== void 0) {
718
+ return reply.getResponseTime();
719
+ }
720
+ return 0;
721
+ }, "getResponseTime");
722
+ var extractAjvErrors = /* @__PURE__ */ __name((message) => {
723
+ const regex = /(?<=^|, )((?:headers|params|query|querystring|body)[/.][^ ]+)(?= )/g;
724
+ const matches = [];
725
+ let match;
726
+ while ((match = regex.exec(message)) !== null) {
727
+ matches.push({
728
+ match: match[0],
729
+ index: match.index
730
+ });
731
+ }
732
+ return matches.map((m, i) => {
733
+ const endIndex = i + 1 < matches.length ? matches[i + 1].index - 2 : message.length;
734
+ const matchSplit = m.match.split(/[/.]/);
735
+ if (matchSplit[0] === "querystring") {
736
+ matchSplit[0] = "query";
737
+ }
738
+ return {
739
+ loc: matchSplit.join("."),
740
+ msg: message.substring(m.index, endIndex),
741
+ type: ""
742
+ };
743
+ });
744
+ }, "extractAjvErrors");
745
+ var plugin_default = (0, import_fastify_plugin.default)(apitallyPlugin, {
746
+ name: "apitally"
747
+ });
748
+ //# sourceMappingURL=plugin.cjs.map