apitally 0.21.3 → 0.21.4

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 (179) hide show
  1. package/dist/adonisjs/configure.cjs +6 -21
  2. package/dist/adonisjs/configure.cjs.map +1 -1
  3. package/dist/adonisjs/configure.js +3 -18
  4. package/dist/adonisjs/configure.js.map +1 -1
  5. package/dist/adonisjs/index.cjs +2 -70
  6. package/dist/adonisjs/index.cjs.map +1 -1
  7. package/dist/adonisjs/index.js +1 -66
  8. package/dist/adonisjs/index.js.map +1 -1
  9. package/dist/adonisjs/middleware.cjs +11 -116
  10. package/dist/adonisjs/middleware.cjs.map +1 -1
  11. package/dist/adonisjs/middleware.js +6 -101
  12. package/dist/adonisjs/middleware.js.map +1 -1
  13. package/dist/adonisjs/provider.cjs +14 -5668
  14. package/dist/adonisjs/provider.cjs.map +1 -1
  15. package/dist/adonisjs/provider.js +6 -5676
  16. package/dist/adonisjs/provider.js.map +1 -1
  17. package/dist/common/client.cjs +26 -792
  18. package/dist/common/client.cjs.map +1 -1
  19. package/dist/common/client.js +19 -785
  20. package/dist/common/client.js.map +1 -1
  21. package/dist/common/consumerRegistry.cjs +3 -5
  22. package/dist/common/consumerRegistry.cjs.map +1 -1
  23. package/dist/common/consumerRegistry.js +3 -5
  24. package/dist/common/consumerRegistry.js.map +1 -1
  25. package/dist/common/headers.cjs +0 -2
  26. package/dist/common/headers.cjs.map +1 -1
  27. package/dist/common/headers.js +0 -2
  28. package/dist/common/headers.js.map +1 -1
  29. package/dist/common/logging.cjs +0 -2
  30. package/dist/common/logging.cjs.map +1 -1
  31. package/dist/common/logging.js +0 -2
  32. package/dist/common/logging.js.map +1 -1
  33. package/dist/common/packageVersions.cjs +2 -4
  34. package/dist/common/packageVersions.cjs.map +1 -1
  35. package/dist/common/packageVersions.js +2 -10
  36. package/dist/common/packageVersions.js.map +1 -1
  37. package/dist/common/paramValidation.cjs +0 -2
  38. package/dist/common/paramValidation.cjs.map +1 -1
  39. package/dist/common/paramValidation.js +0 -2
  40. package/dist/common/paramValidation.js.map +1 -1
  41. package/dist/common/requestCounter.cjs +2 -4
  42. package/dist/common/requestCounter.cjs.map +1 -1
  43. package/dist/common/requestCounter.js +2 -4
  44. package/dist/common/requestCounter.js.map +1 -1
  45. package/dist/common/requestLogger.cjs +39 -162
  46. package/dist/common/requestLogger.cjs.map +1 -1
  47. package/dist/common/requestLogger.js +33 -156
  48. package/dist/common/requestLogger.js.map +1 -1
  49. package/dist/common/response.cjs +0 -2
  50. package/dist/common/response.cjs.map +1 -1
  51. package/dist/common/response.js +0 -2
  52. package/dist/common/response.js.map +1 -1
  53. package/dist/common/sentry.cjs +1 -3
  54. package/dist/common/sentry.cjs.map +1 -1
  55. package/dist/common/sentry.js +1 -3
  56. package/dist/common/sentry.js.map +1 -1
  57. package/dist/common/serverErrorCounter.cjs +7 -36
  58. package/dist/common/serverErrorCounter.cjs.map +1 -1
  59. package/dist/common/serverErrorCounter.js +6 -25
  60. package/dist/common/serverErrorCounter.js.map +1 -1
  61. package/dist/common/tempGzipFile.cjs +9 -11
  62. package/dist/common/tempGzipFile.cjs.map +1 -1
  63. package/dist/common/tempGzipFile.js +9 -11
  64. package/dist/common/tempGzipFile.js.map +1 -1
  65. package/dist/common/types.cjs +0 -2
  66. package/dist/common/types.cjs.map +1 -1
  67. package/dist/common/validationErrorCounter.cjs +3 -5
  68. package/dist/common/validationErrorCounter.cjs.map +1 -1
  69. package/dist/common/validationErrorCounter.js +3 -5
  70. package/dist/common/validationErrorCounter.js.map +1 -1
  71. package/dist/elysia/index.cjs +2 -1573
  72. package/dist/elysia/index.cjs.map +1 -1
  73. package/dist/elysia/index.js +2 -1577
  74. package/dist/elysia/index.js.map +1 -1
  75. package/dist/elysia/plugin.cjs +26 -1432
  76. package/dist/elysia/plugin.cjs.map +1 -1
  77. package/dist/elysia/plugin.js +15 -1416
  78. package/dist/elysia/plugin.js.map +1 -1
  79. package/dist/elysia/utils.cjs +3 -24
  80. package/dist/elysia/utils.cjs.map +1 -1
  81. package/dist/elysia/utils.js +1 -25
  82. package/dist/elysia/utils.js.map +1 -1
  83. package/dist/express/index.cjs +3 -6354
  84. package/dist/express/index.cjs.map +1 -1
  85. package/dist/express/index.js +1 -6367
  86. package/dist/express/index.js.map +1 -1
  87. package/dist/express/middleware.cjs +38 -6070
  88. package/dist/express/middleware.cjs.map +1 -1
  89. package/dist/express/middleware.js +13 -6061
  90. package/dist/express/middleware.js.map +1 -1
  91. package/dist/express/utils.cjs +17 -19
  92. package/dist/express/utils.cjs.map +1 -1
  93. package/dist/express/utils.js +17 -19
  94. package/dist/express/utils.js.map +1 -1
  95. package/dist/fastify/index.cjs +3 -6124
  96. package/dist/fastify/index.cjs.map +1 -1
  97. package/dist/fastify/index.js +2 -6148
  98. package/dist/fastify/index.js.map +1 -1
  99. package/dist/fastify/plugin.cjs +35 -5889
  100. package/dist/fastify/plugin.cjs.map +1 -1
  101. package/dist/fastify/plugin.js +13 -5893
  102. package/dist/fastify/plugin.js.map +1 -1
  103. package/dist/h3/index.cjs +3 -1545
  104. package/dist/h3/index.cjs.map +1 -1
  105. package/dist/h3/index.js +1 -1537
  106. package/dist/h3/index.js.map +1 -1
  107. package/dist/h3/plugin.cjs +27 -1423
  108. package/dist/h3/plugin.cjs.map +1 -1
  109. package/dist/h3/plugin.js +15 -1406
  110. package/dist/h3/plugin.js.map +1 -1
  111. package/dist/h3/utils.cjs +3 -24
  112. package/dist/h3/utils.cjs.map +1 -1
  113. package/dist/h3/utils.js +1 -25
  114. package/dist/h3/utils.js.map +1 -1
  115. package/dist/hapi/index.cjs +3 -5988
  116. package/dist/hapi/index.cjs.map +1 -1
  117. package/dist/hapi/index.js +2 -6012
  118. package/dist/hapi/index.js.map +1 -1
  119. package/dist/hapi/plugin.cjs +27 -5847
  120. package/dist/hapi/plugin.cjs.map +1 -1
  121. package/dist/hapi/plugin.js +15 -5851
  122. package/dist/hapi/plugin.js.map +1 -1
  123. package/dist/hapi/utils.cjs +3 -24
  124. package/dist/hapi/utils.cjs.map +1 -1
  125. package/dist/hapi/utils.js +1 -25
  126. package/dist/hapi/utils.js.map +1 -1
  127. package/dist/hono/index.cjs +3 -1571
  128. package/dist/hono/index.cjs.map +1 -1
  129. package/dist/hono/index.js +1 -1563
  130. package/dist/hono/index.js.map +1 -1
  131. package/dist/hono/middleware.cjs +21 -1460
  132. package/dist/hono/middleware.cjs.map +1 -1
  133. package/dist/hono/middleware.js +9 -1443
  134. package/dist/hono/middleware.js.map +1 -1
  135. package/dist/hono/utils.cjs +3 -24
  136. package/dist/hono/utils.cjs.map +1 -1
  137. package/dist/hono/utils.js +1 -27
  138. package/dist/hono/utils.js.map +1 -1
  139. package/dist/koa/index.cjs +3 -1484
  140. package/dist/koa/index.cjs.map +1 -1
  141. package/dist/koa/index.js +1 -1476
  142. package/dist/koa/index.js.map +1 -1
  143. package/dist/koa/middleware.cjs +19 -1313
  144. package/dist/koa/middleware.cjs.map +1 -1
  145. package/dist/koa/middleware.js +8 -1297
  146. package/dist/koa/middleware.js.map +1 -1
  147. package/dist/loggers/console.cjs +4 -6
  148. package/dist/loggers/console.cjs.map +1 -1
  149. package/dist/loggers/console.js +4 -6
  150. package/dist/loggers/console.js.map +1 -1
  151. package/dist/loggers/hapi.cjs +3 -5
  152. package/dist/loggers/hapi.cjs.map +1 -1
  153. package/dist/loggers/hapi.js +3 -5
  154. package/dist/loggers/hapi.js.map +1 -1
  155. package/dist/loggers/index.cjs +10 -4732
  156. package/dist/loggers/index.cjs.map +1 -1
  157. package/dist/loggers/index.js +5 -4743
  158. package/dist/loggers/index.js.map +1 -1
  159. package/dist/loggers/nestjs.cjs +5 -39
  160. package/dist/loggers/nestjs.cjs.map +1 -1
  161. package/dist/loggers/nestjs.js +4 -36
  162. package/dist/loggers/nestjs.js.map +1 -1
  163. package/dist/loggers/pino.cjs +29 -4448
  164. package/dist/loggers/pino.cjs.map +1 -1
  165. package/dist/loggers/pino.js +7 -4451
  166. package/dist/loggers/pino.js.map +1 -1
  167. package/dist/loggers/utils.cjs +1 -3
  168. package/dist/loggers/utils.cjs.map +1 -1
  169. package/dist/loggers/utils.js +1 -3
  170. package/dist/loggers/utils.js.map +1 -1
  171. package/dist/loggers/winston.cjs +6 -44
  172. package/dist/loggers/winston.cjs.map +1 -1
  173. package/dist/loggers/winston.js +4 -40
  174. package/dist/loggers/winston.js.map +1 -1
  175. package/dist/nestjs/index.cjs +17 -17786
  176. package/dist/nestjs/index.cjs.map +1 -1
  177. package/dist/nestjs/index.js +20 -17814
  178. package/dist/nestjs/index.js.map +1 -1
  179. package/package.json +1 -1
@@ -1,11 +1,8 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  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
6
  var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
10
7
  var __export = (target, all) => {
11
8
  for (var name in all)
@@ -19,18 +16,7 @@ var __copyProps = (to, from, except, desc) => {
19
16
  }
20
17
  return to;
21
18
  };
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
19
  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/h3/plugin.ts
34
20
  var plugin_exports = {};
35
21
  __export(plugin_exports, {
36
22
  apitallyPlugin: () => apitallyPlugin,
@@ -38,1407 +24,25 @@ __export(plugin_exports, {
38
24
  });
39
25
  module.exports = __toCommonJS(plugin_exports);
40
26
  var import_h3 = require("h3");
41
- var import_node_async_hooks = require("async_hooks");
42
- var import_node_perf_hooks = require("perf_hooks");
43
-
44
- // src/common/client.ts
45
- var import_fetch_retry = __toESM(require("fetch-retry"), 1);
46
- var import_node_crypto5 = require("crypto");
47
-
48
- // src/common/consumerRegistry.ts
49
- var consumerFromStringOrObject = /* @__PURE__ */ __name((consumer) => {
50
- var _a2, _b;
51
- if (typeof consumer === "string") {
52
- consumer = String(consumer).trim().substring(0, 128);
53
- return consumer ? {
54
- identifier: consumer
55
- } : null;
56
- } else {
57
- consumer.identifier = String(consumer.identifier).trim().substring(0, 128);
58
- consumer.name = (_a2 = consumer.name) == null ? void 0 : _a2.trim().substring(0, 64);
59
- consumer.group = (_b = consumer.group) == null ? void 0 : _b.trim().substring(0, 64);
60
- return consumer.identifier ? consumer : null;
61
- }
62
- }, "consumerFromStringOrObject");
63
- var _ConsumerRegistry = class _ConsumerRegistry {
64
- consumers;
65
- updated;
66
- constructor() {
67
- this.consumers = /* @__PURE__ */ new Map();
68
- this.updated = /* @__PURE__ */ new Set();
69
- }
70
- addOrUpdateConsumer(consumer) {
71
- if (!consumer || !consumer.name && !consumer.group) {
72
- return;
73
- }
74
- const existing = this.consumers.get(consumer.identifier);
75
- if (!existing) {
76
- this.consumers.set(consumer.identifier, consumer);
77
- this.updated.add(consumer.identifier);
78
- } else {
79
- if (consumer.name && consumer.name !== existing.name) {
80
- existing.name = consumer.name;
81
- this.updated.add(consumer.identifier);
82
- }
83
- if (consumer.group && consumer.group !== existing.group) {
84
- existing.group = consumer.group;
85
- this.updated.add(consumer.identifier);
86
- }
87
- }
88
- }
89
- getAndResetUpdatedConsumers() {
90
- const data = [];
91
- this.updated.forEach((identifier) => {
92
- const consumer = this.consumers.get(identifier);
93
- if (consumer) {
94
- data.push(consumer);
95
- }
96
- });
97
- this.updated.clear();
98
- return data;
99
- }
100
- };
101
- __name(_ConsumerRegistry, "ConsumerRegistry");
102
- var ConsumerRegistry = _ConsumerRegistry;
103
-
104
- // src/common/logging.ts
105
- var import_winston = require("winston");
106
- function getLogger() {
107
- return (0, import_winston.createLogger)({
108
- level: process.env.APITALLY_DEBUG ? "debug" : "warn",
109
- format: import_winston.format.combine(import_winston.format.colorize(), import_winston.format.timestamp(), import_winston.format.printf((info) => `${info.timestamp} ${info.level}: ${info.message}`)),
110
- transports: [
111
- new import_winston.transports.Console()
112
- ]
113
- });
114
- }
115
- __name(getLogger, "getLogger");
116
-
117
- // src/common/paramValidation.ts
118
- function isValidClientId(clientId) {
119
- 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;
120
- return regexExp.test(clientId);
121
- }
122
- __name(isValidClientId, "isValidClientId");
123
- function isValidEnv(env) {
124
- const regexExp = /^[\w-]{1,32}$/;
125
- return regexExp.test(env);
126
- }
127
- __name(isValidEnv, "isValidEnv");
128
-
129
- // src/common/requestCounter.ts
130
- var _RequestCounter = class _RequestCounter {
131
- requestCounts;
132
- requestSizeSums;
133
- responseSizeSums;
134
- responseTimes;
135
- requestSizes;
136
- responseSizes;
137
- constructor() {
138
- this.requestCounts = /* @__PURE__ */ new Map();
139
- this.requestSizeSums = /* @__PURE__ */ new Map();
140
- this.responseSizeSums = /* @__PURE__ */ new Map();
141
- this.responseTimes = /* @__PURE__ */ new Map();
142
- this.requestSizes = /* @__PURE__ */ new Map();
143
- this.responseSizes = /* @__PURE__ */ new Map();
144
- }
145
- getKey(requestInfo) {
146
- return [
147
- requestInfo.consumer || "",
148
- requestInfo.method.toUpperCase(),
149
- requestInfo.path,
150
- requestInfo.statusCode
151
- ].join("|");
152
- }
153
- addRequest(requestInfo) {
154
- const key = this.getKey(requestInfo);
155
- this.requestCounts.set(key, (this.requestCounts.get(key) || 0) + 1);
156
- if (!this.responseTimes.has(key)) {
157
- this.responseTimes.set(key, /* @__PURE__ */ new Map());
158
- }
159
- const responseTimeMap = this.responseTimes.get(key);
160
- const responseTimeMsBin = Math.floor(requestInfo.responseTime / 10) * 10;
161
- responseTimeMap.set(responseTimeMsBin, (responseTimeMap.get(responseTimeMsBin) || 0) + 1);
162
- if (requestInfo.requestSize !== void 0) {
163
- requestInfo.requestSize = Number(requestInfo.requestSize);
164
- this.requestSizeSums.set(key, (this.requestSizeSums.get(key) || 0) + requestInfo.requestSize);
165
- if (!this.requestSizes.has(key)) {
166
- this.requestSizes.set(key, /* @__PURE__ */ new Map());
167
- }
168
- const requestSizeMap = this.requestSizes.get(key);
169
- const requestSizeKbBin = Math.floor(requestInfo.requestSize / 1e3);
170
- requestSizeMap.set(requestSizeKbBin, (requestSizeMap.get(requestSizeKbBin) || 0) + 1);
171
- }
172
- if (requestInfo.responseSize !== void 0) {
173
- requestInfo.responseSize = Number(requestInfo.responseSize);
174
- this.responseSizeSums.set(key, (this.responseSizeSums.get(key) || 0) + requestInfo.responseSize);
175
- if (!this.responseSizes.has(key)) {
176
- this.responseSizes.set(key, /* @__PURE__ */ new Map());
177
- }
178
- const responseSizeMap = this.responseSizes.get(key);
179
- const responseSizeKbBin = Math.floor(requestInfo.responseSize / 1e3);
180
- responseSizeMap.set(responseSizeKbBin, (responseSizeMap.get(responseSizeKbBin) || 0) + 1);
181
- }
182
- }
183
- getAndResetRequests() {
184
- const data = [];
185
- this.requestCounts.forEach((count, key) => {
186
- const [consumer, method, path, statusCodeStr] = key.split("|");
187
- const responseTimes = this.responseTimes.get(key) || /* @__PURE__ */ new Map();
188
- const requestSizes = this.requestSizes.get(key) || /* @__PURE__ */ new Map();
189
- const responseSizes = this.responseSizes.get(key) || /* @__PURE__ */ new Map();
190
- data.push({
191
- consumer: consumer || null,
192
- method,
193
- path,
194
- status_code: parseInt(statusCodeStr),
195
- request_count: count,
196
- request_size_sum: this.requestSizeSums.get(key) || 0,
197
- response_size_sum: this.responseSizeSums.get(key) || 0,
198
- response_times: Object.fromEntries(responseTimes),
199
- request_sizes: Object.fromEntries(requestSizes),
200
- response_sizes: Object.fromEntries(responseSizes)
201
- });
202
- });
203
- this.requestCounts.clear();
204
- this.requestSizeSums.clear();
205
- this.responseSizeSums.clear();
206
- this.responseTimes.clear();
207
- this.requestSizes.clear();
208
- this.responseSizes.clear();
209
- return data;
210
- }
211
- };
212
- __name(_RequestCounter, "RequestCounter");
213
- var RequestCounter = _RequestCounter;
214
-
215
- // src/common/requestLogger.ts
216
- var import_async_lock = __toESM(require("async-lock"), 1);
217
- var import_node_buffer2 = require("buffer");
218
- var import_node_crypto3 = require("crypto");
219
- var import_node_fs2 = require("fs");
220
- var import_node_os2 = require("os");
221
- var import_node_path2 = require("path");
222
-
223
- // src/common/sentry.ts
224
- var sentry;
225
- (async () => {
226
- try {
227
- sentry = await import("@sentry/node");
228
- } catch (e) {
229
- }
230
- })();
231
- function getSentryEventId() {
232
- if (sentry && sentry.lastEventId) {
233
- return sentry.lastEventId();
234
- }
235
- return void 0;
236
- }
237
- __name(getSentryEventId, "getSentryEventId");
238
-
239
- // src/common/serverErrorCounter.ts
240
- var import_node_crypto = require("crypto");
241
- var MAX_MSG_LENGTH = 2048;
242
- var MAX_STACKTRACE_LENGTH = 65536;
243
- var _ServerErrorCounter = class _ServerErrorCounter {
244
- errorCounts;
245
- errorDetails;
246
- sentryEventIds;
247
- constructor() {
248
- this.errorCounts = /* @__PURE__ */ new Map();
249
- this.errorDetails = /* @__PURE__ */ new Map();
250
- this.sentryEventIds = /* @__PURE__ */ new Map();
251
- }
252
- addServerError(serverError) {
253
- const key = this.getKey(serverError);
254
- if (!this.errorDetails.has(key)) {
255
- this.errorDetails.set(key, serverError);
256
- }
257
- this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
258
- const sentryEventId = getSentryEventId();
259
- if (sentryEventId) {
260
- this.sentryEventIds.set(key, sentryEventId);
261
- }
262
- }
263
- getAndResetServerErrors() {
264
- const data = [];
265
- this.errorCounts.forEach((count, key) => {
266
- const serverError = this.errorDetails.get(key);
267
- if (serverError) {
268
- data.push({
269
- consumer: serverError.consumer || null,
270
- method: serverError.method,
271
- path: serverError.path,
272
- type: serverError.type,
273
- msg: truncateExceptionMessage(serverError.msg),
274
- traceback: truncateExceptionStackTrace(serverError.traceback),
275
- sentry_event_id: this.sentryEventIds.get(key) || null,
276
- error_count: count
277
- });
278
- }
279
- });
280
- this.errorCounts.clear();
281
- this.errorDetails.clear();
282
- this.sentryEventIds.clear();
283
- return data;
284
- }
285
- getKey(serverError) {
286
- const hashInput = [
287
- serverError.consumer || "",
288
- serverError.method.toUpperCase(),
289
- serverError.path,
290
- serverError.type,
291
- serverError.msg.trim(),
292
- serverError.traceback.trim()
293
- ].join("|");
294
- return (0, import_node_crypto.createHash)("md5").update(hashInput).digest("hex");
295
- }
296
- };
297
- __name(_ServerErrorCounter, "ServerErrorCounter");
298
- var ServerErrorCounter = _ServerErrorCounter;
299
- function truncateExceptionMessage(msg) {
300
- if (msg.length <= MAX_MSG_LENGTH) {
301
- return msg;
302
- }
303
- const suffix = "... (truncated)";
304
- const cutoff = MAX_MSG_LENGTH - suffix.length;
305
- return msg.substring(0, cutoff) + suffix;
306
- }
307
- __name(truncateExceptionMessage, "truncateExceptionMessage");
308
- function truncateExceptionStackTrace(stack) {
309
- const suffix = "... (truncated) ...";
310
- const cutoff = MAX_STACKTRACE_LENGTH - suffix.length;
311
- const lines = stack.trim().split("\n");
312
- const truncatedLines = [];
313
- let length = 0;
314
- for (const line of lines) {
315
- if (length + line.length + 1 > cutoff) {
316
- truncatedLines.push(suffix);
317
- break;
318
- }
319
- truncatedLines.push(line);
320
- length += line.length + 1;
321
- }
322
- return truncatedLines.join("\n");
323
- }
324
- __name(truncateExceptionStackTrace, "truncateExceptionStackTrace");
325
-
326
- // src/common/tempGzipFile.ts
327
- var import_node_buffer = require("buffer");
328
- var import_node_crypto2 = require("crypto");
329
- var import_node_fs = require("fs");
330
- var import_promises = require("fs/promises");
331
- var import_node_os = require("os");
332
- var import_node_path = require("path");
333
- var import_node_zlib = require("zlib");
334
- var _TempGzipFile = class _TempGzipFile {
335
- uuid;
336
- filePath;
337
- gzip;
338
- writeStream;
339
- readyPromise;
340
- closedPromise;
341
- constructor() {
342
- this.uuid = (0, import_node_crypto2.randomUUID)();
343
- this.filePath = (0, import_node_path.join)((0, import_node_os.tmpdir)(), `apitally-${this.uuid}.gz`);
344
- this.writeStream = (0, import_node_fs.createWriteStream)(this.filePath);
345
- this.readyPromise = new Promise((resolve, reject) => {
346
- this.writeStream.once("ready", resolve);
347
- this.writeStream.once("error", reject);
348
- });
349
- this.closedPromise = new Promise((resolve, reject) => {
350
- this.writeStream.once("close", resolve);
351
- this.writeStream.once("error", reject);
352
- });
353
- this.gzip = (0, import_node_zlib.createGzip)();
354
- this.gzip.pipe(this.writeStream);
355
- }
356
- get size() {
357
- return this.writeStream.bytesWritten;
358
- }
359
- async writeLine(data) {
360
- await this.readyPromise;
361
- return new Promise((resolve, reject) => {
362
- this.gzip.write(import_node_buffer.Buffer.concat([
363
- data,
364
- import_node_buffer.Buffer.from("\n")
365
- ]), (error) => {
366
- if (error) {
367
- reject(error);
368
- } else {
369
- resolve();
370
- }
371
- });
372
- });
373
- }
374
- async getContent() {
375
- return new Promise((resolve, reject) => {
376
- (0, import_node_fs.readFile)(this.filePath, (error, data) => {
377
- if (error) {
378
- reject(error);
379
- } else {
380
- resolve(data);
381
- }
382
- });
383
- });
384
- }
385
- async close() {
386
- await new Promise((resolve) => {
387
- this.gzip.end(() => {
388
- resolve();
389
- });
390
- });
391
- await this.closedPromise;
392
- }
393
- async delete() {
394
- await this.close();
395
- await (0, import_promises.unlink)(this.filePath);
396
- }
397
- };
398
- __name(_TempGzipFile, "TempGzipFile");
399
- var TempGzipFile = _TempGzipFile;
400
-
401
- // src/common/requestLogger.ts
402
- var MAX_BODY_SIZE = 5e4;
403
- var MAX_FILE_SIZE = 1e6;
404
- var MAX_FILES = 50;
405
- var MAX_PENDING_WRITES = 100;
406
- var MAX_LOG_MSG_LENGTH = 2048;
407
- var BODY_TOO_LARGE = import_node_buffer2.Buffer.from("<body too large>");
408
- var BODY_MASKED = import_node_buffer2.Buffer.from("<masked>");
409
- var MASKED = "******";
410
- var ALLOWED_CONTENT_TYPES = [
411
- "application/json",
412
- "application/problem+json",
413
- "application/vnd.api+json",
414
- "text/plain",
415
- "text/html"
416
- ];
417
- var EXCLUDE_PATH_PATTERNS = [
418
- /\/_?healthz?$/i,
419
- /\/_?health[_-]?checks?$/i,
420
- /\/_?heart[_-]?beats?$/i,
421
- /\/ping$/i,
422
- /\/ready$/i,
423
- /\/live$/i
424
- ];
425
- var EXCLUDE_USER_AGENT_PATTERNS = [
426
- /health[-_ ]?check/i,
427
- /microsoft-azure-application-lb/i,
428
- /googlehc/i,
429
- /kube-probe/i
430
- ];
431
- var MASK_QUERY_PARAM_PATTERNS = [
432
- /auth/i,
433
- /api-?key/i,
434
- /secret/i,
435
- /token/i,
436
- /password/i,
437
- /pwd/i
438
- ];
439
- var MASK_HEADER_PATTERNS = [
440
- /auth/i,
441
- /api-?key/i,
442
- /secret/i,
443
- /token/i,
444
- /cookie/i
445
- ];
446
- var MASK_BODY_FIELD_PATTERNS = [
447
- /password/i,
448
- /pwd/i,
449
- /token/i,
450
- /secret/i,
451
- /auth/i,
452
- /card[-_ ]?number/i,
453
- /ccv/i,
454
- /ssn/i
455
- ];
456
- var DEFAULT_CONFIG = {
457
- enabled: false,
458
- logQueryParams: true,
459
- logRequestHeaders: false,
460
- logRequestBody: false,
461
- logResponseHeaders: true,
462
- logResponseBody: false,
463
- logException: true,
464
- captureLogs: false,
465
- maskQueryParams: [],
466
- maskHeaders: [],
467
- maskBodyFields: [],
468
- excludePaths: []
469
- };
470
- var _RequestLogger = class _RequestLogger {
471
- config;
472
- enabled;
473
- suspendUntil = null;
474
- pendingWrites = [];
475
- currentFile = null;
476
- files = [];
477
- maintainIntervalId;
478
- lock = new import_async_lock.default();
479
- constructor(config) {
480
- this.config = {
481
- ...DEFAULT_CONFIG,
482
- ...config
483
- };
484
- this.enabled = this.config.enabled && checkWritableFs();
485
- if (this.enabled) {
486
- this.maintainIntervalId = setInterval(() => {
487
- this.maintain();
488
- }, 1e3);
489
- }
490
- }
491
- get maxBodySize() {
492
- return MAX_BODY_SIZE;
493
- }
494
- shouldExcludePath(urlPath) {
495
- const patterns = [
496
- ...this.config.excludePaths,
497
- ...EXCLUDE_PATH_PATTERNS
498
- ];
499
- return matchPatterns(urlPath, patterns);
500
- }
501
- shouldExcludeUserAgent(userAgent) {
502
- return userAgent ? matchPatterns(userAgent, EXCLUDE_USER_AGENT_PATTERNS) : false;
503
- }
504
- shouldMaskQueryParam(name) {
505
- const patterns = [
506
- ...this.config.maskQueryParams,
507
- ...MASK_QUERY_PARAM_PATTERNS
508
- ];
509
- return matchPatterns(name, patterns);
510
- }
511
- shouldMaskHeader(name) {
512
- const patterns = [
513
- ...this.config.maskHeaders,
514
- ...MASK_HEADER_PATTERNS
515
- ];
516
- return matchPatterns(name, patterns);
517
- }
518
- shouldMaskBodyField(name) {
519
- const patterns = [
520
- ...this.config.maskBodyFields,
521
- ...MASK_BODY_FIELD_PATTERNS
522
- ];
523
- return matchPatterns(name, patterns);
524
- }
525
- hasSupportedContentType(headers) {
526
- var _a2;
527
- const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
528
- return this.isSupportedContentType(contentType);
529
- }
530
- hasJsonContentType(headers) {
531
- var _a2;
532
- const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
533
- return contentType ? /\bjson\b/i.test(contentType) : null;
534
- }
535
- isSupportedContentType(contentType) {
536
- return typeof contentType === "string" && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
537
- }
538
- maskQueryParams(search) {
539
- const params = new URLSearchParams(search);
540
- for (const [key] of params) {
541
- if (this.shouldMaskQueryParam(key)) {
542
- params.set(key, MASKED);
543
- }
544
- }
545
- return params.toString();
546
- }
547
- maskHeaders(headers) {
548
- return headers.map(([k, v]) => [
549
- k,
550
- this.shouldMaskHeader(k) ? MASKED : v
551
- ]);
552
- }
553
- maskBody(data) {
554
- if (typeof data === "object" && data !== null && !Array.isArray(data)) {
555
- const result = {};
556
- for (const [key, value] of Object.entries(data)) {
557
- if (typeof value === "string" && this.shouldMaskBodyField(key)) {
558
- result[key] = MASKED;
559
- } else {
560
- result[key] = this.maskBody(value);
561
- }
562
- }
563
- return result;
564
- }
565
- if (Array.isArray(data)) {
566
- return data.map((item) => this.maskBody(item));
567
- }
568
- return data;
569
- }
570
- applyMasking(item) {
571
- if (this.config.maskRequestBodyCallback && item.request.body && item.request.body !== BODY_TOO_LARGE) {
572
- try {
573
- const maskedBody = this.config.maskRequestBodyCallback(item.request);
574
- item.request.body = maskedBody ?? BODY_MASKED;
575
- } catch {
576
- item.request.body = void 0;
577
- }
578
- }
579
- if (this.config.maskResponseBodyCallback && item.response.body && item.response.body !== BODY_TOO_LARGE) {
580
- try {
581
- const maskedBody = this.config.maskResponseBodyCallback(item.request, item.response);
582
- item.response.body = maskedBody ?? BODY_MASKED;
583
- } catch {
584
- item.response.body = void 0;
585
- }
586
- }
587
- if (item.request.body && item.request.body.length > MAX_BODY_SIZE) {
588
- item.request.body = BODY_TOO_LARGE;
589
- }
590
- if (item.response.body && item.response.body.length > MAX_BODY_SIZE) {
591
- item.response.body = BODY_TOO_LARGE;
592
- }
593
- for (const key of [
594
- "request",
595
- "response"
596
- ]) {
597
- const bodyData = item[key].body;
598
- if (!bodyData || bodyData === BODY_TOO_LARGE || bodyData === BODY_MASKED) {
599
- continue;
600
- }
601
- const headers = item[key].headers;
602
- const hasJsonContent = this.hasJsonContentType(headers);
603
- if (hasJsonContent === null || hasJsonContent) {
604
- try {
605
- const parsedBody = JSON.parse(bodyData.toString());
606
- const maskedBody = this.maskBody(parsedBody);
607
- item[key].body = import_node_buffer2.Buffer.from(JSON.stringify(maskedBody));
608
- } catch {
609
- }
610
- }
611
- }
612
- item.request.headers = this.config.logRequestHeaders ? this.maskHeaders(item.request.headers) : [];
613
- item.response.headers = this.config.logResponseHeaders ? this.maskHeaders(item.response.headers) : [];
614
- const url = new URL(item.request.url);
615
- url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
616
- item.request.url = url.toString();
617
- return item;
618
- }
619
- logRequest(request, response, error, logs) {
620
- var _a2, _b, _c;
621
- if (!this.enabled || this.suspendUntil !== null) return;
622
- const url = new URL(request.url);
623
- const path = request.path ?? url.pathname;
624
- const userAgent = (_a2 = request.headers.find(([k]) => k.toLowerCase() === "user-agent")) == null ? void 0 : _a2[1];
625
- if (this.shouldExcludePath(path) || this.shouldExcludeUserAgent(userAgent) || (((_c = (_b = this.config).excludeCallback) == null ? void 0 : _c.call(_b, request, response)) ?? false)) {
626
- return;
627
- }
628
- if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
629
- request.body = void 0;
630
- }
631
- if (!this.config.logResponseBody || !this.hasSupportedContentType(response.headers)) {
632
- response.body = void 0;
633
- }
634
- if (request.size !== void 0 && request.size < 0) {
635
- request.size = void 0;
636
- }
637
- if (response.size !== void 0 && response.size < 0) {
638
- response.size = void 0;
639
- }
640
- const item = {
641
- uuid: (0, import_node_crypto3.randomUUID)(),
642
- request,
643
- response,
644
- exception: error && this.config.logException ? {
645
- type: error.name,
646
- message: truncateExceptionMessage(error.message),
647
- stacktrace: truncateExceptionStackTrace(error.stack || ""),
648
- sentryEventId: getSentryEventId()
649
- } : void 0
650
- };
651
- if (logs && logs.length > 0) {
652
- item.logs = logs.map((log) => ({
653
- timestamp: log.timestamp,
654
- logger: log.logger,
655
- level: log.level,
656
- message: truncateLogMessage(log.message)
657
- }));
658
- }
659
- this.pendingWrites.push(item);
660
- if (this.pendingWrites.length > MAX_PENDING_WRITES) {
661
- this.pendingWrites.shift();
662
- }
663
- }
664
- async writeToFile() {
665
- if (!this.enabled || this.pendingWrites.length === 0) {
666
- return;
667
- }
668
- return this.lock.acquire("file", async () => {
669
- if (!this.currentFile) {
670
- this.currentFile = new TempGzipFile();
671
- }
672
- while (this.pendingWrites.length > 0) {
673
- let item = this.pendingWrites.shift();
674
- if (item) {
675
- item = this.applyMasking(item);
676
- const finalItem = {
677
- uuid: item.uuid,
678
- request: skipEmptyValues(item.request),
679
- response: skipEmptyValues(item.response),
680
- exception: item.exception,
681
- logs: item.logs
682
- };
683
- [
684
- finalItem.request.body,
685
- finalItem.response.body
686
- ].forEach((body) => {
687
- if (body) {
688
- body.toJSON = function() {
689
- return this.toString("base64");
690
- };
691
- }
692
- });
693
- await this.currentFile.writeLine(import_node_buffer2.Buffer.from(JSON.stringify(finalItem)));
694
- }
695
- }
696
- });
697
- }
698
- getFile() {
699
- return this.files.shift();
700
- }
701
- retryFileLater(file) {
702
- this.files.unshift(file);
703
- }
704
- async rotateFile() {
705
- return this.lock.acquire("file", async () => {
706
- if (this.currentFile) {
707
- await this.currentFile.close();
708
- this.files.push(this.currentFile);
709
- this.currentFile = null;
710
- }
711
- });
712
- }
713
- async maintain() {
714
- await this.writeToFile();
715
- if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {
716
- await this.rotateFile();
717
- }
718
- while (this.files.length > MAX_FILES) {
719
- const file = this.files.shift();
720
- file == null ? void 0 : file.delete();
721
- }
722
- if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {
723
- this.suspendUntil = null;
724
- }
725
- }
726
- async clear() {
727
- this.pendingWrites = [];
728
- await this.rotateFile();
729
- this.files.forEach((file) => {
730
- file.delete();
731
- });
732
- this.files = [];
733
- }
734
- async close() {
735
- this.enabled = false;
736
- await this.clear();
737
- if (this.maintainIntervalId) {
738
- clearInterval(this.maintainIntervalId);
739
- }
740
- }
741
- };
742
- __name(_RequestLogger, "RequestLogger");
743
- var RequestLogger = _RequestLogger;
744
- function convertHeaders(headers) {
745
- if (!headers) {
746
- return [];
747
- }
748
- if (headers instanceof Headers) {
749
- return Array.from(headers.entries());
750
- }
751
- return Object.entries(headers).flatMap(([key, value]) => {
752
- if (value === void 0) {
753
- return [];
754
- }
755
- if (Array.isArray(value)) {
756
- return value.map((v) => [
757
- key,
758
- v
759
- ]);
760
- }
761
- return [
762
- [
763
- key,
764
- value.toString()
765
- ]
766
- ];
767
- });
768
- }
769
- __name(convertHeaders, "convertHeaders");
770
- function matchPatterns(value, patterns) {
771
- return patterns.some((pattern) => {
772
- return pattern.test(value);
773
- });
774
- }
775
- __name(matchPatterns, "matchPatterns");
776
- function skipEmptyValues(data) {
777
- return Object.fromEntries(Object.entries(data).filter(([_, v]) => {
778
- if (v == null || Number.isNaN(v)) return false;
779
- if (Array.isArray(v) || import_node_buffer2.Buffer.isBuffer(v) || typeof v === "string") {
780
- return v.length > 0;
781
- }
782
- return true;
783
- }));
784
- }
785
- __name(skipEmptyValues, "skipEmptyValues");
786
- function truncateLogMessage(msg) {
787
- if (msg.length > MAX_LOG_MSG_LENGTH) {
788
- const suffix = "... (truncated)";
789
- return msg.slice(0, MAX_LOG_MSG_LENGTH - suffix.length) + suffix;
790
- }
791
- return msg;
792
- }
793
- __name(truncateLogMessage, "truncateLogMessage");
794
- function checkWritableFs() {
795
- try {
796
- const testPath = (0, import_node_path2.join)((0, import_node_os2.tmpdir)(), `apitally-${(0, import_node_crypto3.randomUUID)()}`);
797
- (0, import_node_fs2.writeFileSync)(testPath, "test");
798
- (0, import_node_fs2.unlinkSync)(testPath);
799
- return true;
800
- } catch (error) {
801
- return false;
802
- }
803
- }
804
- __name(checkWritableFs, "checkWritableFs");
805
-
806
- // src/common/validationErrorCounter.ts
807
- var import_node_crypto4 = require("crypto");
808
- var _ValidationErrorCounter = class _ValidationErrorCounter {
809
- errorCounts;
810
- errorDetails;
811
- constructor() {
812
- this.errorCounts = /* @__PURE__ */ new Map();
813
- this.errorDetails = /* @__PURE__ */ new Map();
814
- }
815
- addValidationError(validationError) {
816
- const key = this.getKey(validationError);
817
- if (!this.errorDetails.has(key)) {
818
- this.errorDetails.set(key, validationError);
819
- }
820
- this.errorCounts.set(key, (this.errorCounts.get(key) || 0) + 1);
821
- }
822
- getAndResetValidationErrors() {
823
- const data = [];
824
- this.errorCounts.forEach((count, key) => {
825
- const validationError = this.errorDetails.get(key);
826
- if (validationError) {
827
- data.push({
828
- consumer: validationError.consumer || null,
829
- method: validationError.method,
830
- path: validationError.path,
831
- loc: validationError.loc.split(".").filter(Boolean),
832
- msg: validationError.msg,
833
- type: validationError.type,
834
- error_count: count
835
- });
836
- }
837
- });
838
- this.errorCounts.clear();
839
- this.errorDetails.clear();
840
- return data;
841
- }
842
- getKey(validationError) {
843
- const hashInput = [
844
- validationError.consumer || "",
845
- validationError.method.toUpperCase(),
846
- validationError.path,
847
- validationError.loc.split(".").filter(Boolean),
848
- validationError.msg.trim(),
849
- validationError.type
850
- ].join("|");
851
- return (0, import_node_crypto4.createHash)("md5").update(hashInput).digest("hex");
852
- }
853
- };
854
- __name(_ValidationErrorCounter, "ValidationErrorCounter");
855
- var ValidationErrorCounter = _ValidationErrorCounter;
856
-
857
- // src/common/client.ts
858
- var SYNC_INTERVAL = 6e4;
859
- var INITIAL_SYNC_INTERVAL = 1e4;
860
- var INITIAL_SYNC_INTERVAL_DURATION = 36e5;
861
- var MAX_QUEUE_TIME = 36e5;
862
- var _a;
863
- var HTTPError = (_a = class extends Error {
864
- response;
865
- constructor(response) {
866
- const reason = response.status ? `status code ${response.status}` : "an unknown error";
867
- super(`Request failed with ${reason}`);
868
- this.response = response;
869
- }
870
- }, __name(_a, "HTTPError"), _a);
871
- var _ApitallyClient = class _ApitallyClient {
872
- clientId;
873
- env;
874
- instanceUuid;
875
- syncDataQueue;
876
- syncIntervalId;
877
- startupData;
878
- startupDataSent = false;
879
- enabled = true;
880
- requestCounter;
881
- requestLogger;
882
- validationErrorCounter;
883
- serverErrorCounter;
884
- consumerRegistry;
885
- logger;
886
- constructor({ clientId, env = "dev", requestLogging, requestLoggingConfig, logger }) {
887
- if (_ApitallyClient.instance) {
888
- throw new Error("Apitally client is already initialized");
889
- }
890
- this.logger = logger ?? getLogger();
891
- if (!isValidClientId(clientId)) {
892
- this.logger.error(`Invalid Apitally client ID '${clientId}' (expecting hexadecimal UUID format)`);
893
- this.enabled = false;
894
- }
895
- if (!isValidEnv(env)) {
896
- this.logger.error(`Invalid Apitally env '${env}' (expecting 1-32 alphanumeric characters and hyphens only)`);
897
- this.enabled = false;
898
- }
899
- if (requestLoggingConfig && !requestLogging) {
900
- console.warn("requestLoggingConfig is deprecated, use requestLogging instead.");
901
- }
902
- _ApitallyClient.instance = this;
903
- this.clientId = clientId;
904
- this.env = env;
905
- this.instanceUuid = (0, import_node_crypto5.randomUUID)();
906
- this.syncDataQueue = [];
907
- this.requestCounter = new RequestCounter();
908
- this.requestLogger = new RequestLogger(requestLogging ?? requestLoggingConfig);
909
- this.validationErrorCounter = new ValidationErrorCounter();
910
- this.serverErrorCounter = new ServerErrorCounter();
911
- this.consumerRegistry = new ConsumerRegistry();
912
- this.handleShutdown = this.handleShutdown.bind(this);
913
- }
914
- static getInstance() {
915
- if (!_ApitallyClient.instance) {
916
- throw new Error("Apitally client is not initialized");
917
- }
918
- return _ApitallyClient.instance;
919
- }
920
- isEnabled() {
921
- return this.enabled;
922
- }
923
- static async shutdown() {
924
- if (_ApitallyClient.instance) {
925
- await _ApitallyClient.instance.handleShutdown();
926
- }
927
- }
928
- async handleShutdown() {
929
- this.enabled = false;
930
- this.stopSync();
931
- await this.sendSyncData();
932
- await this.sendLogData();
933
- await this.requestLogger.close();
934
- _ApitallyClient.instance = void 0;
935
- }
936
- getHubUrlPrefix() {
937
- const baseURL = process.env.APITALLY_HUB_BASE_URL || "https://hub.apitally.io";
938
- const version = "v2";
939
- return `${baseURL}/${version}/${this.clientId}/${this.env}/`;
940
- }
941
- async sendData(url, payload) {
942
- const fetchWithRetry = (0, import_fetch_retry.default)(fetch, {
943
- retries: 3,
944
- retryDelay: 1e3,
945
- retryOn: [
946
- 408,
947
- 429,
948
- 500,
949
- 502,
950
- 503,
951
- 504
952
- ]
953
- });
954
- const response = await fetchWithRetry(this.getHubUrlPrefix() + url, {
955
- method: "POST",
956
- body: JSON.stringify(payload),
957
- headers: {
958
- "Content-Type": "application/json"
959
- }
960
- });
961
- if (!response.ok) {
962
- throw new HTTPError(response);
963
- }
964
- }
965
- startSync() {
966
- if (!this.enabled) {
967
- return;
968
- }
969
- this.sync();
970
- this.syncIntervalId = setInterval(() => {
971
- this.sync();
972
- }, INITIAL_SYNC_INTERVAL);
973
- setTimeout(() => {
974
- if (this.syncIntervalId) {
975
- clearInterval(this.syncIntervalId);
976
- this.syncIntervalId = setInterval(() => {
977
- this.sync();
978
- }, SYNC_INTERVAL);
979
- }
980
- }, INITIAL_SYNC_INTERVAL_DURATION);
981
- }
982
- async sync() {
983
- try {
984
- const promises = [
985
- this.sendSyncData(),
986
- this.sendLogData()
987
- ];
988
- if (!this.startupDataSent) {
989
- promises.push(this.sendStartupData());
990
- }
991
- await Promise.all(promises);
992
- } catch (error) {
993
- this.logger.error("Error while syncing with Apitally Hub", {
994
- error
995
- });
996
- }
997
- }
998
- stopSync() {
999
- if (this.syncIntervalId) {
1000
- clearInterval(this.syncIntervalId);
1001
- this.syncIntervalId = void 0;
1002
- }
1003
- }
1004
- setStartupData(data) {
1005
- this.startupData = data;
1006
- this.startupDataSent = false;
1007
- }
1008
- async sendStartupData() {
1009
- if (this.startupData) {
1010
- this.logger.debug("Sending startup data to Apitally Hub");
1011
- const payload = {
1012
- instance_uuid: this.instanceUuid,
1013
- message_uuid: (0, import_node_crypto5.randomUUID)(),
1014
- ...this.startupData
1015
- };
1016
- try {
1017
- await this.sendData("startup", payload);
1018
- this.startupDataSent = true;
1019
- } catch (error) {
1020
- const handled = this.handleHubError(error);
1021
- if (!handled) {
1022
- this.logger.error(error.message);
1023
- this.logger.debug("Error while sending startup data to Apitally Hub (will retry)", {
1024
- error
1025
- });
1026
- }
1027
- }
1028
- }
1029
- }
1030
- async sendSyncData() {
1031
- this.logger.debug("Synchronizing data with Apitally Hub");
1032
- const newPayload = {
1033
- timestamp: Date.now() / 1e3,
1034
- instance_uuid: this.instanceUuid,
1035
- message_uuid: (0, import_node_crypto5.randomUUID)(),
1036
- requests: this.requestCounter.getAndResetRequests(),
1037
- validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
1038
- server_errors: this.serverErrorCounter.getAndResetServerErrors(),
1039
- consumers: this.consumerRegistry.getAndResetUpdatedConsumers()
1040
- };
1041
- this.syncDataQueue.push(newPayload);
1042
- let i = 0;
1043
- while (this.syncDataQueue.length > 0) {
1044
- const payload = this.syncDataQueue.shift();
1045
- if (payload) {
1046
- try {
1047
- if (Date.now() - payload.timestamp * 1e3 <= MAX_QUEUE_TIME) {
1048
- if (i > 0) {
1049
- await this.randomDelay();
1050
- }
1051
- await this.sendData("sync", payload);
1052
- i += 1;
1053
- }
1054
- } catch (error) {
1055
- const handled = this.handleHubError(error);
1056
- if (!handled) {
1057
- this.logger.debug("Error while synchronizing data with Apitally Hub (will retry)", {
1058
- error
1059
- });
1060
- this.syncDataQueue.push(payload);
1061
- break;
1062
- }
1063
- }
1064
- }
1065
- }
1066
- }
1067
- async sendLogData() {
1068
- this.logger.debug("Sending request log data to Apitally Hub");
1069
- await this.requestLogger.rotateFile();
1070
- const fetchWithRetry = (0, import_fetch_retry.default)(fetch, {
1071
- retries: 3,
1072
- retryDelay: 1e3,
1073
- retryOn: [
1074
- 408,
1075
- 429,
1076
- 500,
1077
- 502,
1078
- 503,
1079
- 504
1080
- ]
1081
- });
1082
- let i = 0;
1083
- let logFile;
1084
- while (logFile = this.requestLogger.getFile()) {
1085
- if (i > 0) {
1086
- await this.randomDelay();
1087
- }
1088
- try {
1089
- const response = await fetchWithRetry(`${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`, {
1090
- method: "POST",
1091
- body: await logFile.getContent()
1092
- });
1093
- if (response.status === 402 && response.headers.has("Retry-After")) {
1094
- const retryAfter = parseInt(response.headers.get("Retry-After") ?? "0");
1095
- if (retryAfter > 0) {
1096
- this.requestLogger.suspendUntil = Date.now() + retryAfter * 1e3;
1097
- this.requestLogger.clear();
1098
- return;
1099
- }
1100
- }
1101
- if (!response.ok) {
1102
- throw new HTTPError(response);
1103
- }
1104
- logFile.delete();
1105
- } catch (error) {
1106
- this.requestLogger.retryFileLater(logFile);
1107
- break;
1108
- }
1109
- i++;
1110
- if (i >= 10) break;
1111
- }
1112
- }
1113
- handleHubError(error) {
1114
- if (error instanceof HTTPError) {
1115
- if (error.response.status === 404) {
1116
- this.logger.error(`Invalid Apitally client ID: '${this.clientId}'`);
1117
- this.enabled = false;
1118
- this.stopSync();
1119
- return true;
1120
- }
1121
- if (error.response.status === 422) {
1122
- this.logger.error("Received validation error from Apitally Hub");
1123
- return true;
1124
- }
1125
- }
1126
- return false;
1127
- }
1128
- async randomDelay() {
1129
- const delay = 100 + Math.random() * 400;
1130
- await new Promise((resolve) => setTimeout(resolve, delay));
1131
- }
1132
- };
1133
- __name(_ApitallyClient, "ApitallyClient");
1134
- __publicField(_ApitallyClient, "instance");
1135
- var ApitallyClient = _ApitallyClient;
1136
-
1137
- // src/common/headers.ts
1138
- function parseContentLength(contentLength) {
1139
- if (contentLength === void 0 || contentLength === null) {
1140
- return void 0;
1141
- }
1142
- if (typeof contentLength === "number") {
1143
- return contentLength;
1144
- }
1145
- if (typeof contentLength === "string") {
1146
- const parsed = parseInt(contentLength);
1147
- return isNaN(parsed) ? void 0 : parsed;
1148
- }
1149
- if (Array.isArray(contentLength)) {
1150
- return parseContentLength(contentLength[0]);
1151
- }
1152
- return void 0;
1153
- }
1154
- __name(parseContentLength, "parseContentLength");
1155
- function mergeHeaders(base, merge) {
1156
- const mergedHeaders = new Headers(base);
1157
- for (const [name, value] of merge) if (name === "set-cookie") mergedHeaders.append(name, value);
1158
- else mergedHeaders.set(name, value);
1159
- return mergedHeaders;
1160
- }
1161
- __name(mergeHeaders, "mergeHeaders");
1162
-
1163
- // src/common/response.ts
1164
- async function measureResponseSize(response, tee = true) {
1165
- const [newResponse1, newResponse2] = tee ? teeResponse(response) : [
1166
- response,
1167
- response
1168
- ];
1169
- let size = 0;
1170
- if (newResponse2.body) {
1171
- let done = false;
1172
- const reader = newResponse2.body.getReader();
1173
- while (!done) {
1174
- const result = await reader.read();
1175
- done = result.done;
1176
- if (!done && result.value) {
1177
- size += result.value.byteLength;
1178
- }
1179
- }
1180
- }
1181
- return [
1182
- size,
1183
- newResponse1
1184
- ];
1185
- }
1186
- __name(measureResponseSize, "measureResponseSize");
1187
- async function getResponseBody(response, tee = true) {
1188
- const [newResponse1, newResponse2] = tee ? teeResponse(response) : [
1189
- response,
1190
- response
1191
- ];
1192
- const responseBuffer = Buffer.from(await newResponse2.arrayBuffer());
1193
- return [
1194
- responseBuffer,
1195
- newResponse1
1196
- ];
1197
- }
1198
- __name(getResponseBody, "getResponseBody");
1199
- function teeResponse(response) {
1200
- if (!response.body) {
1201
- return [
1202
- response,
1203
- response
1204
- ];
1205
- }
1206
- const [stream1, stream2] = response.body.tee();
1207
- const newResponse1 = new Response(stream1, {
1208
- status: response.status,
1209
- statusText: response.statusText,
1210
- headers: response.headers
1211
- });
1212
- const newResponse2 = new Response(stream2, {
1213
- status: response.status,
1214
- statusText: response.statusText,
1215
- headers: response.headers
1216
- });
1217
- return [
1218
- newResponse1,
1219
- newResponse2
1220
- ];
1221
- }
1222
- __name(teeResponse, "teeResponse");
1223
-
1224
- // src/loggers/console.ts
1225
- var import_node_util = require("util");
1226
- var MAX_BUFFER_SIZE = 1e3;
1227
- var isPatched = false;
1228
- var globalLogsContext;
1229
- function patchConsole(logsContext) {
1230
- globalLogsContext = logsContext;
1231
- if (isPatched) {
1232
- return;
1233
- }
1234
- const logMethods = [
1235
- "log",
1236
- "warn",
1237
- "error",
1238
- "info",
1239
- "debug"
1240
- ];
1241
- logMethods.forEach((method) => {
1242
- const originalMethod = console[method];
1243
- console[method] = function(...args) {
1244
- captureLog(method, args);
1245
- return originalMethod.apply(console, args);
1246
- };
1247
- });
1248
- isPatched = true;
1249
- }
1250
- __name(patchConsole, "patchConsole");
1251
- function captureLog(level, args) {
1252
- const logs = globalLogsContext == null ? void 0 : globalLogsContext.getStore();
1253
- if (logs && logs.length < MAX_BUFFER_SIZE) {
1254
- logs.push({
1255
- timestamp: Date.now() / 1e3,
1256
- logger: "console",
1257
- level,
1258
- message: (0, import_node_util.format)(...args)
1259
- });
1260
- }
1261
- }
1262
- __name(captureLog, "captureLog");
1263
-
1264
- // src/loggers/hapi.ts
1265
- var import_node_util2 = require("util");
1266
-
1267
- // src/loggers/utils.ts
1268
- var import_node_util3 = require("util");
1269
- function formatMessage(message, ...args) {
1270
- return [
1271
- message,
1272
- ...args
1273
- ].map(formatArg).filter((arg) => arg !== "").join("\n");
1274
- }
1275
- __name(formatMessage, "formatMessage");
1276
- function removeKeys(obj, keys) {
1277
- return Object.fromEntries(Object.entries(obj).filter(([key]) => !keys.includes(key)));
1278
- }
1279
- __name(removeKeys, "removeKeys");
1280
- function formatArg(arg) {
1281
- if (typeof arg === "string") {
1282
- return arg.trim();
1283
- }
1284
- if (arg instanceof Error) {
1285
- return (0, import_node_util3.format)(arg).trim();
1286
- }
1287
- if (arg === void 0 || arg === null || isEmptyObject(arg)) {
1288
- return "";
1289
- }
1290
- try {
1291
- return JSON.stringify(arg);
1292
- } catch {
1293
- return (0, import_node_util3.format)(arg).trim();
1294
- }
1295
- }
1296
- __name(formatArg, "formatArg");
1297
- function isEmptyObject(obj) {
1298
- return obj !== null && typeof obj === "object" && Object.getPrototypeOf(obj) === Object.prototype && Object.keys(obj).length === 0;
1299
- }
1300
- __name(isEmptyObject, "isEmptyObject");
1301
-
1302
- // src/loggers/pino.ts
1303
- var originalStreamSym = Symbol.for("apitally.originalStream");
1304
-
1305
- // src/loggers/winston.ts
1306
- var MAX_BUFFER_SIZE2 = 1e3;
1307
- var isPatched2 = false;
1308
- var globalLogsContext2;
1309
- async function patchWinston(logsContext) {
1310
- var _a2, _b;
1311
- globalLogsContext2 = logsContext;
1312
- if (isPatched2) {
1313
- return;
1314
- }
1315
- try {
1316
- const loggerModule = await import("winston/lib/winston/logger.js");
1317
- if ((_b = (_a2 = loggerModule.default) == null ? void 0 : _a2.prototype) == null ? void 0 : _b.write) {
1318
- const originalWrite = loggerModule.default.prototype.write;
1319
- loggerModule.default.prototype.write = function(info) {
1320
- captureLog2(info);
1321
- return originalWrite.call(this, info);
1322
- };
1323
- }
1324
- } catch {
1325
- }
1326
- isPatched2 = true;
1327
- }
1328
- __name(patchWinston, "patchWinston");
1329
- function captureLog2(info) {
1330
- const logs = globalLogsContext2 == null ? void 0 : globalLogsContext2.getStore();
1331
- if (!logs || !info || logs.length >= MAX_BUFFER_SIZE2) {
1332
- return;
1333
- }
1334
- try {
1335
- const rest = removeKeys(info, [
1336
- "timestamp",
1337
- "level",
1338
- "message",
1339
- "splat"
1340
- ]);
1341
- const formattedMessage = formatMessage(info.message, rest);
1342
- if (formattedMessage) {
1343
- logs.push({
1344
- timestamp: parseTimestamp(info.timestamp),
1345
- level: info.level || "info",
1346
- message: formattedMessage.trim()
1347
- });
1348
- }
1349
- } catch (e) {
1350
- }
1351
- }
1352
- __name(captureLog2, "captureLog");
1353
- function parseTimestamp(timestamp) {
1354
- if (timestamp) {
1355
- const ts = new Date(timestamp).getTime();
1356
- if (!isNaN(ts)) {
1357
- return ts / 1e3;
1358
- }
1359
- }
1360
- return Date.now() / 1e3;
1361
- }
1362
- __name(parseTimestamp, "parseTimestamp");
1363
-
1364
- // src/common/packageVersions.ts
1365
- var import_node_module = require("module");
1366
- var import_meta = {};
1367
- function getPackageVersion(name) {
1368
- const packageJsonPath = `${name}/package.json`;
1369
- try {
1370
- return require(packageJsonPath).version || null;
1371
- } catch (error) {
1372
- try {
1373
- const _require = (0, import_node_module.createRequire)(import_meta.url);
1374
- return _require(packageJsonPath).version || null;
1375
- } catch (error2) {
1376
- return null;
1377
- }
1378
- }
1379
- }
1380
- __name(getPackageVersion, "getPackageVersion");
1381
-
1382
- // src/h3/utils.ts
1383
- function getAppInfo(h3, appVersion) {
1384
- const versions = [];
1385
- if (process.versions.node) {
1386
- versions.push([
1387
- "nodejs",
1388
- process.versions.node
1389
- ]);
1390
- }
1391
- if (process.versions.bun) {
1392
- versions.push([
1393
- "bun",
1394
- process.versions.bun
1395
- ]);
1396
- }
1397
- const h3Version = getPackageVersion("h3");
1398
- const apitallyVersion = getPackageVersion("../..");
1399
- if (h3Version) {
1400
- versions.push([
1401
- "h3",
1402
- h3Version
1403
- ]);
1404
- }
1405
- if (apitallyVersion) {
1406
- versions.push([
1407
- "apitally",
1408
- apitallyVersion
1409
- ]);
1410
- }
1411
- if (appVersion) {
1412
- versions.push([
1413
- "app",
1414
- appVersion
1415
- ]);
1416
- }
1417
- return {
1418
- paths: h3._routes.map((route) => ({
1419
- method: route.method || "",
1420
- path: route.route || ""
1421
- })).filter((route) => route.method && route.path && ![
1422
- "HEAD",
1423
- "OPTIONS"
1424
- ].includes(route.method.toUpperCase())),
1425
- versions: Object.fromEntries(versions),
1426
- client: "js:h3"
1427
- };
1428
- }
1429
- __name(getAppInfo, "getAppInfo");
1430
-
1431
- // src/h3/plugin.ts
1432
- var REQUEST_TIMESTAMP_SYMBOL = Symbol("apitally.requestTimestamp");
1433
- var REQUEST_BODY_SYMBOL = Symbol("apitally.requestBody");
1434
- var jsonHeaders = new Headers({
27
+ var import_node_async_hooks = require("node:async_hooks");
28
+ var import_node_perf_hooks = require("node:perf_hooks");
29
+ var import_client = require("../common/client.js");
30
+ var import_consumerRegistry = require("../common/consumerRegistry.js");
31
+ var import_headers = require("../common/headers.js");
32
+ var import_requestLogger = require("../common/requestLogger.js");
33
+ var import_response = require("../common/response.js");
34
+ var import_loggers = require("../loggers/index.js");
35
+ var import_utils = require("./utils.js");
36
+ const REQUEST_TIMESTAMP_SYMBOL = Symbol("apitally.requestTimestamp");
37
+ const REQUEST_BODY_SYMBOL = Symbol("apitally.requestBody");
38
+ const jsonHeaders = new Headers({
1435
39
  "content-type": "application/json;charset=UTF-8"
1436
40
  });
1437
- var apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
1438
- const client = new ApitallyClient(config);
41
+ const apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
42
+ const client = new import_client.ApitallyClient(config);
1439
43
  const logsContext = new import_node_async_hooks.AsyncLocalStorage();
1440
44
  const setStartupData = /* @__PURE__ */ __name((attempt = 1) => {
1441
- const appInfo = getAppInfo(app, config.appVersion);
45
+ const appInfo = (0, import_utils.getAppInfo)(app, config.appVersion);
1442
46
  if (appInfo.paths.length > 0 || attempt >= 10) {
1443
47
  client.setStartupData(appInfo);
1444
48
  client.startSync();
@@ -1448,23 +52,23 @@ var apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
1448
52
  }, "setStartupData");
1449
53
  setTimeout(() => setStartupData(), 500);
1450
54
  if (client.requestLogger.config.captureLogs) {
1451
- patchConsole(logsContext);
1452
- patchWinston(logsContext);
55
+ (0, import_loggers.patchConsole)(logsContext);
56
+ (0, import_loggers.patchWinston)(logsContext);
1453
57
  }
1454
58
  const handleResponse = /* @__PURE__ */ __name(async (event, response, error) => {
1455
- var _a2, _b;
59
+ var _a, _b;
1456
60
  if (event.req.method.toUpperCase() === "OPTIONS") {
1457
61
  return response;
1458
62
  }
1459
63
  const startTime = event.context[REQUEST_TIMESTAMP_SYMBOL];
1460
64
  const responseTime = startTime ? import_node_perf_hooks.performance.now() - startTime : 0;
1461
- const path = (_a2 = event.context.matchedRoute) == null ? void 0 : _a2.route;
65
+ const path = (_a = event.context.matchedRoute) == null ? void 0 : _a.route;
1462
66
  const statusCode = (response == null ? void 0 : response.status) || (error == null ? void 0 : error.status) || 500;
1463
- const requestSize = parseContentLength(event.req.headers.get("content-length"));
67
+ const requestSize = (0, import_headers.parseContentLength)(event.req.headers.get("content-length"));
1464
68
  let responseSize = 0;
1465
69
  let newResponse = response;
1466
70
  if (response) {
1467
- [responseSize, newResponse] = await measureResponseSize(response);
71
+ [responseSize, newResponse] = await (0, import_response.measureResponseSize)(response);
1468
72
  }
1469
73
  const consumer = getConsumer(event);
1470
74
  client.consumerRegistry.addOrUpdateConsumer(consumer);
@@ -1503,11 +107,11 @@ var apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
1503
107
  }
1504
108
  }
1505
109
  if (client.requestLogger.enabled) {
1506
- const responseHeaders = response ? response.headers : (error == null ? void 0 : error.headers) ? mergeHeaders(jsonHeaders, error.headers) : jsonHeaders;
110
+ const responseHeaders = response ? response.headers : (error == null ? void 0 : error.headers) ? (0, import_headers.mergeHeaders)(jsonHeaders, error.headers) : jsonHeaders;
1507
111
  const responseContentType = responseHeaders.get("content-type");
1508
112
  let responseBody;
1509
113
  if (newResponse && client.requestLogger.config.logResponseBody && client.requestLogger.isSupportedContentType(responseContentType)) {
1510
- [responseBody, newResponse] = await getResponseBody(newResponse);
114
+ [responseBody, newResponse] = await (0, import_response.getResponseBody)(newResponse);
1511
115
  } else if (error && client.requestLogger.config.logResponseBody) {
1512
116
  responseBody = Buffer.from(JSON.stringify(error.toJSON()));
1513
117
  }
@@ -1517,14 +121,14 @@ var apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
1517
121
  method: event.req.method,
1518
122
  path,
1519
123
  url: event.req.url,
1520
- headers: convertHeaders(Object.fromEntries(event.req.headers.entries())),
124
+ headers: (0, import_requestLogger.convertHeaders)(Object.fromEntries(event.req.headers.entries())),
1521
125
  size: requestSize,
1522
126
  consumer: consumer == null ? void 0 : consumer.identifier,
1523
127
  body: event.context[REQUEST_BODY_SYMBOL]
1524
128
  }, {
1525
129
  statusCode,
1526
130
  responseTime: responseTime / 1e3,
1527
- headers: convertHeaders(Object.fromEntries(responseHeaders.entries())),
131
+ headers: (0, import_requestLogger.convertHeaders)(Object.fromEntries(responseHeaders.entries())),
1528
132
  size: responseSize,
1529
133
  body: responseBody
1530
134
  }, (error == null ? void 0 : error.cause) instanceof Error ? error.cause : void 0, logs);
@@ -1535,7 +139,7 @@ var apitallyPlugin = (0, import_h3.definePlugin)((app, config) => {
1535
139
  logsContext.enterWith([]);
1536
140
  event.context[REQUEST_TIMESTAMP_SYMBOL] = import_node_perf_hooks.performance.now();
1537
141
  const requestContentType = event.req.headers.get("content-type");
1538
- const requestSize = parseContentLength(event.req.headers.get("content-length")) ?? 0;
142
+ const requestSize = (0, import_headers.parseContentLength)(event.req.headers.get("content-length")) ?? 0;
1539
143
  if (client.requestLogger.enabled && client.requestLogger.config.logRequestBody && client.requestLogger.isSupportedContentType(requestContentType) && requestSize <= client.requestLogger.maxBodySize) {
1540
144
  const clonedRequest = event.req.clone();
1541
145
  const requestBody = Buffer.from(await clonedRequest.arrayBuffer());
@@ -1558,7 +162,7 @@ __name(setConsumer, "setConsumer");
1558
162
  function getConsumer(event) {
1559
163
  const consumer = event.context.apitallyConsumer;
1560
164
  if (consumer) {
1561
- return consumerFromStringOrObject(consumer);
165
+ return (0, import_consumerRegistry.consumerFromStringOrObject)(consumer);
1562
166
  }
1563
167
  return null;
1564
168
  }