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