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