apitally 0.9.5 → 0.11.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 +1 -2
- package/dist/common/client.cjs +398 -12
- package/dist/common/client.cjs.map +1 -1
- package/dist/common/client.d.cts +8 -1
- package/dist/common/client.d.ts +8 -1
- package/dist/common/client.js +394 -8
- package/dist/common/client.js.map +1 -1
- package/dist/common/consumerRegistry.d.cts +4 -0
- package/dist/common/consumerRegistry.d.ts +4 -0
- package/dist/common/requestCounter.d.cts +4 -0
- package/dist/common/requestCounter.d.ts +4 -0
- package/dist/common/requestLogger.cjs +419 -0
- package/dist/common/requestLogger.cjs.map +1 -0
- package/dist/common/requestLogger.d.cts +63 -0
- package/dist/common/requestLogger.d.ts +63 -0
- package/dist/common/requestLogger.js +395 -0
- package/dist/common/requestLogger.js.map +1 -0
- package/dist/common/serverErrorCounter.d.cts +4 -0
- package/dist/common/serverErrorCounter.d.ts +4 -0
- package/dist/common/tempGzipFile.cjs +93 -0
- package/dist/common/tempGzipFile.cjs.map +1 -0
- package/dist/common/tempGzipFile.d.cts +17 -0
- package/dist/common/tempGzipFile.d.ts +17 -0
- package/dist/common/tempGzipFile.js +74 -0
- package/dist/common/tempGzipFile.js.map +1 -0
- package/dist/common/types.cjs.map +1 -1
- package/dist/common/types.d.cts +5 -0
- package/dist/common/types.d.ts +5 -0
- package/dist/common/validationErrorCounter.d.cts +4 -0
- package/dist/common/validationErrorCounter.d.ts +4 -0
- package/dist/express/index.cjs +500 -34
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.d.cts +4 -0
- package/dist/express/index.d.ts +4 -0
- package/dist/express/index.js +496 -30
- package/dist/express/index.js.map +1 -1
- package/dist/express/middleware.cjs +500 -34
- package/dist/express/middleware.cjs.map +1 -1
- package/dist/express/middleware.d.cts +4 -0
- package/dist/express/middleware.d.ts +4 -0
- package/dist/express/middleware.js +496 -30
- package/dist/express/middleware.js.map +1 -1
- package/dist/fastify/index.cjs +491 -27
- package/dist/fastify/index.cjs.map +1 -1
- package/dist/fastify/index.d.cts +4 -0
- package/dist/fastify/index.d.ts +4 -0
- package/dist/fastify/index.js +487 -23
- package/dist/fastify/index.js.map +1 -1
- package/dist/fastify/plugin.cjs +491 -27
- package/dist/fastify/plugin.cjs.map +1 -1
- package/dist/fastify/plugin.d.cts +4 -0
- package/dist/fastify/plugin.d.ts +4 -0
- package/dist/fastify/plugin.js +487 -23
- package/dist/fastify/plugin.js.map +1 -1
- package/dist/hono/index.cjs +460 -15
- package/dist/hono/index.cjs.map +1 -1
- package/dist/hono/index.d.cts +4 -0
- package/dist/hono/index.d.ts +4 -0
- package/dist/hono/index.js +456 -11
- package/dist/hono/index.js.map +1 -1
- package/dist/hono/middleware.cjs +460 -15
- package/dist/hono/middleware.cjs.map +1 -1
- package/dist/hono/middleware.d.cts +4 -0
- package/dist/hono/middleware.d.ts +4 -0
- package/dist/hono/middleware.js +456 -11
- package/dist/hono/middleware.js.map +1 -1
- package/dist/koa/index.cjs +475 -15
- package/dist/koa/index.cjs.map +1 -1
- package/dist/koa/index.d.cts +4 -0
- package/dist/koa/index.d.ts +4 -0
- package/dist/koa/index.js +471 -11
- package/dist/koa/index.js.map +1 -1
- package/dist/koa/middleware.cjs +475 -15
- package/dist/koa/middleware.cjs.map +1 -1
- package/dist/koa/middleware.d.cts +4 -0
- package/dist/koa/middleware.d.ts +4 -0
- package/dist/koa/middleware.js +471 -11
- package/dist/koa/middleware.js.map +1 -1
- package/dist/nestjs/index.cjs +536 -41
- package/dist/nestjs/index.cjs.map +1 -1
- package/dist/nestjs/index.d.cts +10 -2
- package/dist/nestjs/index.d.ts +10 -2
- package/dist/nestjs/index.js +534 -37
- package/dist/nestjs/index.js.map +1 -1
- package/package.json +8 -2
package/dist/hono/middleware.cjs
CHANGED
|
@@ -40,7 +40,7 @@ var import_handler = require("hono/utils/handler");
|
|
|
40
40
|
var import_perf_hooks = require("perf_hooks");
|
|
41
41
|
|
|
42
42
|
// src/common/client.ts
|
|
43
|
-
var
|
|
43
|
+
var import_crypto5 = require("crypto");
|
|
44
44
|
var import_fetch_retry = __toESM(require("fetch-retry"), 1);
|
|
45
45
|
|
|
46
46
|
// src/common/consumerRegistry.ts
|
|
@@ -209,8 +209,363 @@ var _RequestCounter = class _RequestCounter {
|
|
|
209
209
|
__name(_RequestCounter, "RequestCounter");
|
|
210
210
|
var RequestCounter = _RequestCounter;
|
|
211
211
|
|
|
212
|
-
// src/common/
|
|
212
|
+
// src/common/requestLogger.ts
|
|
213
|
+
var import_buffer2 = require("buffer");
|
|
214
|
+
var import_crypto2 = require("crypto");
|
|
215
|
+
var import_fs2 = require("fs");
|
|
216
|
+
var import_os2 = require("os");
|
|
217
|
+
var import_path2 = require("path");
|
|
218
|
+
|
|
219
|
+
// src/common/tempGzipFile.ts
|
|
220
|
+
var import_buffer = require("buffer");
|
|
213
221
|
var import_crypto = require("crypto");
|
|
222
|
+
var import_fs = require("fs");
|
|
223
|
+
var import_os = require("os");
|
|
224
|
+
var import_path = require("path");
|
|
225
|
+
var import_zlib = require("zlib");
|
|
226
|
+
var _TempGzipFile = class _TempGzipFile {
|
|
227
|
+
uuid;
|
|
228
|
+
filePath;
|
|
229
|
+
gzip;
|
|
230
|
+
writeStream;
|
|
231
|
+
readyPromise;
|
|
232
|
+
constructor() {
|
|
233
|
+
this.uuid = (0, import_crypto.randomUUID)();
|
|
234
|
+
this.filePath = (0, import_path.join)((0, import_os.tmpdir)(), `apitally-${this.uuid}.gz`);
|
|
235
|
+
this.writeStream = (0, import_fs.createWriteStream)(this.filePath);
|
|
236
|
+
this.readyPromise = new Promise((resolve, reject) => {
|
|
237
|
+
this.writeStream.once("ready", resolve);
|
|
238
|
+
this.writeStream.once("error", reject);
|
|
239
|
+
});
|
|
240
|
+
this.gzip = (0, import_zlib.createGzip)();
|
|
241
|
+
this.gzip.pipe(this.writeStream);
|
|
242
|
+
}
|
|
243
|
+
get size() {
|
|
244
|
+
return this.writeStream.bytesWritten;
|
|
245
|
+
}
|
|
246
|
+
async writeLine(data) {
|
|
247
|
+
await this.readyPromise;
|
|
248
|
+
return new Promise((resolve, reject) => {
|
|
249
|
+
this.gzip.write(import_buffer.Buffer.concat([
|
|
250
|
+
data,
|
|
251
|
+
import_buffer.Buffer.from("\n")
|
|
252
|
+
]), (error) => {
|
|
253
|
+
if (error) {
|
|
254
|
+
reject(error);
|
|
255
|
+
} else {
|
|
256
|
+
resolve();
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
async getContent() {
|
|
262
|
+
return new Promise((resolve, reject) => {
|
|
263
|
+
(0, import_fs.readFile)(this.filePath, (error, data) => {
|
|
264
|
+
if (error) {
|
|
265
|
+
reject(error);
|
|
266
|
+
} else {
|
|
267
|
+
resolve(data);
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
async close() {
|
|
273
|
+
return new Promise((resolve) => {
|
|
274
|
+
this.gzip.end(() => {
|
|
275
|
+
resolve();
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
async delete() {
|
|
280
|
+
await this.close();
|
|
281
|
+
(0, import_fs.unlinkSync)(this.filePath);
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
__name(_TempGzipFile, "TempGzipFile");
|
|
285
|
+
var TempGzipFile = _TempGzipFile;
|
|
286
|
+
|
|
287
|
+
// src/common/requestLogger.ts
|
|
288
|
+
var MAX_BODY_SIZE = 5e4;
|
|
289
|
+
var MAX_FILE_SIZE = 1e6;
|
|
290
|
+
var MAX_FILES = 50;
|
|
291
|
+
var MAX_PENDING_WRITES = 100;
|
|
292
|
+
var BODY_TOO_LARGE = import_buffer2.Buffer.from("<body too large>");
|
|
293
|
+
var BODY_MASKED = import_buffer2.Buffer.from("<masked>");
|
|
294
|
+
var MASKED = "******";
|
|
295
|
+
var ALLOWED_CONTENT_TYPES = [
|
|
296
|
+
"application/json",
|
|
297
|
+
"text/plain"
|
|
298
|
+
];
|
|
299
|
+
var EXCLUDE_PATH_PATTERNS = [
|
|
300
|
+
/\/_?healthz?$/,
|
|
301
|
+
/\/_?health[_-]?checks?$/,
|
|
302
|
+
/\/_?heart[_-]?beats?$/,
|
|
303
|
+
/\/ping$/,
|
|
304
|
+
/\/ready$/,
|
|
305
|
+
/\/live$/
|
|
306
|
+
];
|
|
307
|
+
var MASK_QUERY_PARAM_PATTERNS = [
|
|
308
|
+
/auth/i,
|
|
309
|
+
/api-?key/i,
|
|
310
|
+
/secret/i,
|
|
311
|
+
/token/i,
|
|
312
|
+
/password/i,
|
|
313
|
+
/pwd/i
|
|
314
|
+
];
|
|
315
|
+
var MASK_HEADER_PATTERNS = [
|
|
316
|
+
/auth/i,
|
|
317
|
+
/api-?key/i,
|
|
318
|
+
/secret/i,
|
|
319
|
+
/token/i,
|
|
320
|
+
/cookie/i
|
|
321
|
+
];
|
|
322
|
+
var DEFAULT_CONFIG = {
|
|
323
|
+
enabled: false,
|
|
324
|
+
logQueryParams: true,
|
|
325
|
+
logRequestHeaders: false,
|
|
326
|
+
logRequestBody: false,
|
|
327
|
+
logResponseHeaders: true,
|
|
328
|
+
logResponseBody: false,
|
|
329
|
+
maskQueryParams: [],
|
|
330
|
+
maskHeaders: [],
|
|
331
|
+
excludePaths: []
|
|
332
|
+
};
|
|
333
|
+
var _RequestLogger = class _RequestLogger {
|
|
334
|
+
config;
|
|
335
|
+
enabled;
|
|
336
|
+
suspendUntil = null;
|
|
337
|
+
pendingWrites = [];
|
|
338
|
+
currentFile = null;
|
|
339
|
+
files = [];
|
|
340
|
+
maintainIntervalId;
|
|
341
|
+
constructor(config) {
|
|
342
|
+
this.config = {
|
|
343
|
+
...DEFAULT_CONFIG,
|
|
344
|
+
...config
|
|
345
|
+
};
|
|
346
|
+
this.enabled = this.config.enabled && checkWritableFs();
|
|
347
|
+
if (this.enabled) {
|
|
348
|
+
this.maintainIntervalId = setInterval(() => {
|
|
349
|
+
this.maintain();
|
|
350
|
+
}, 1e3);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
shouldExcludePath(urlPath) {
|
|
354
|
+
const patterns = [
|
|
355
|
+
...this.config.excludePaths,
|
|
356
|
+
...EXCLUDE_PATH_PATTERNS
|
|
357
|
+
];
|
|
358
|
+
return matchPatterns(urlPath, patterns);
|
|
359
|
+
}
|
|
360
|
+
shouldMaskQueryParam(name) {
|
|
361
|
+
const patterns = [
|
|
362
|
+
...this.config.maskQueryParams,
|
|
363
|
+
...MASK_QUERY_PARAM_PATTERNS
|
|
364
|
+
];
|
|
365
|
+
return matchPatterns(name, patterns);
|
|
366
|
+
}
|
|
367
|
+
shouldMaskHeader(name) {
|
|
368
|
+
const patterns = [
|
|
369
|
+
...this.config.maskHeaders,
|
|
370
|
+
...MASK_HEADER_PATTERNS
|
|
371
|
+
];
|
|
372
|
+
return matchPatterns(name, patterns);
|
|
373
|
+
}
|
|
374
|
+
hasSupportedContentType(headers) {
|
|
375
|
+
var _a2;
|
|
376
|
+
const contentType = (_a2 = headers.find(([k]) => k.toLowerCase() === "content-type")) == null ? void 0 : _a2[1];
|
|
377
|
+
return contentType !== void 0 && ALLOWED_CONTENT_TYPES.some((t) => contentType.startsWith(t));
|
|
378
|
+
}
|
|
379
|
+
maskQueryParams(search) {
|
|
380
|
+
const params = new URLSearchParams(search);
|
|
381
|
+
for (const [key] of params) {
|
|
382
|
+
if (this.shouldMaskQueryParam(key)) {
|
|
383
|
+
params.set(key, MASKED);
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return params.toString();
|
|
387
|
+
}
|
|
388
|
+
maskHeaders(headers) {
|
|
389
|
+
return headers.map(([k, v]) => [
|
|
390
|
+
k,
|
|
391
|
+
this.shouldMaskHeader(k) ? MASKED : v
|
|
392
|
+
]);
|
|
393
|
+
}
|
|
394
|
+
logRequest(request, response) {
|
|
395
|
+
var _a2, _b;
|
|
396
|
+
if (!this.enabled || this.suspendUntil !== null) return;
|
|
397
|
+
const url = new URL(request.url);
|
|
398
|
+
const path = request.path ?? url.pathname;
|
|
399
|
+
if (this.shouldExcludePath(path) || (((_b = (_a2 = this.config).excludeCallback) == null ? void 0 : _b.call(_a2, request, response)) ?? false)) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
url.search = this.config.logQueryParams ? this.maskQueryParams(url.search) : "";
|
|
403
|
+
request.url = url.toString();
|
|
404
|
+
request.headers = this.config.logRequestHeaders ? this.maskHeaders(request.headers) : [];
|
|
405
|
+
response.headers = this.config.logResponseHeaders ? this.maskHeaders(response.headers) : [];
|
|
406
|
+
if (!this.config.logRequestBody || !this.hasSupportedContentType(request.headers)) {
|
|
407
|
+
request.body = void 0;
|
|
408
|
+
} else if (request.body) {
|
|
409
|
+
if (request.body.length > MAX_BODY_SIZE) {
|
|
410
|
+
request.body = BODY_TOO_LARGE;
|
|
411
|
+
} else if (this.config.maskRequestBodyCallback) {
|
|
412
|
+
try {
|
|
413
|
+
request.body = this.config.maskRequestBodyCallback(request) ?? BODY_MASKED;
|
|
414
|
+
if (request.body.length > MAX_BODY_SIZE) {
|
|
415
|
+
request.body = BODY_TOO_LARGE;
|
|
416
|
+
}
|
|
417
|
+
} catch {
|
|
418
|
+
request.body = void 0;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
if (!this.config.logResponseBody || !this.hasSupportedContentType(response.headers)) {
|
|
423
|
+
response.body = void 0;
|
|
424
|
+
} else if (response.body) {
|
|
425
|
+
if (response.body.length > MAX_BODY_SIZE) {
|
|
426
|
+
response.body = BODY_TOO_LARGE;
|
|
427
|
+
} else if (this.config.maskResponseBodyCallback) {
|
|
428
|
+
try {
|
|
429
|
+
response.body = this.config.maskResponseBodyCallback(request, response) ?? BODY_MASKED;
|
|
430
|
+
if (response.body.length > MAX_BODY_SIZE) {
|
|
431
|
+
response.body = BODY_TOO_LARGE;
|
|
432
|
+
}
|
|
433
|
+
} catch {
|
|
434
|
+
response.body = void 0;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const item = {
|
|
439
|
+
uuid: (0, import_crypto2.randomUUID)(),
|
|
440
|
+
request: skipEmptyValues(request),
|
|
441
|
+
response: skipEmptyValues(response)
|
|
442
|
+
};
|
|
443
|
+
[
|
|
444
|
+
item.request.body,
|
|
445
|
+
item.response.body
|
|
446
|
+
].forEach((body) => {
|
|
447
|
+
if (body) {
|
|
448
|
+
body.toJSON = function() {
|
|
449
|
+
return this.toString("base64");
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
this.pendingWrites.push(JSON.stringify(item));
|
|
454
|
+
if (this.pendingWrites.length > MAX_PENDING_WRITES) {
|
|
455
|
+
this.pendingWrites.shift();
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
async writeToFile() {
|
|
459
|
+
if (!this.enabled || this.pendingWrites.length === 0) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
if (!this.currentFile) {
|
|
463
|
+
this.currentFile = new TempGzipFile();
|
|
464
|
+
}
|
|
465
|
+
while (this.pendingWrites.length > 0) {
|
|
466
|
+
const item = this.pendingWrites.shift();
|
|
467
|
+
if (item) {
|
|
468
|
+
await this.currentFile.writeLine(import_buffer2.Buffer.from(item));
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
getFile() {
|
|
473
|
+
return this.files.shift();
|
|
474
|
+
}
|
|
475
|
+
retryFileLater(file) {
|
|
476
|
+
this.files.unshift(file);
|
|
477
|
+
}
|
|
478
|
+
async rotateFile() {
|
|
479
|
+
if (this.currentFile) {
|
|
480
|
+
await this.currentFile.close();
|
|
481
|
+
this.files.push(this.currentFile);
|
|
482
|
+
this.currentFile = null;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
async maintain() {
|
|
486
|
+
await this.writeToFile();
|
|
487
|
+
if (this.currentFile && this.currentFile.size > MAX_FILE_SIZE) {
|
|
488
|
+
await this.rotateFile();
|
|
489
|
+
}
|
|
490
|
+
while (this.files.length > MAX_FILES) {
|
|
491
|
+
const file = this.files.shift();
|
|
492
|
+
file == null ? void 0 : file.delete();
|
|
493
|
+
}
|
|
494
|
+
if (this.suspendUntil !== null && this.suspendUntil < Date.now()) {
|
|
495
|
+
this.suspendUntil = null;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
async clear() {
|
|
499
|
+
this.pendingWrites = [];
|
|
500
|
+
await this.rotateFile();
|
|
501
|
+
this.files.forEach((file) => {
|
|
502
|
+
file.delete();
|
|
503
|
+
});
|
|
504
|
+
this.files = [];
|
|
505
|
+
}
|
|
506
|
+
async close() {
|
|
507
|
+
this.enabled = false;
|
|
508
|
+
await this.clear();
|
|
509
|
+
if (this.maintainIntervalId) {
|
|
510
|
+
clearInterval(this.maintainIntervalId);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
__name(_RequestLogger, "RequestLogger");
|
|
515
|
+
var RequestLogger = _RequestLogger;
|
|
516
|
+
function convertHeaders(headers) {
|
|
517
|
+
if (headers instanceof Headers) {
|
|
518
|
+
return Array.from(headers.entries());
|
|
519
|
+
}
|
|
520
|
+
return Object.entries(headers).flatMap(([key, value]) => {
|
|
521
|
+
if (value === void 0) {
|
|
522
|
+
return [];
|
|
523
|
+
}
|
|
524
|
+
if (Array.isArray(value)) {
|
|
525
|
+
return value.map((v) => [
|
|
526
|
+
key,
|
|
527
|
+
v
|
|
528
|
+
]);
|
|
529
|
+
}
|
|
530
|
+
return [
|
|
531
|
+
[
|
|
532
|
+
key,
|
|
533
|
+
value.toString()
|
|
534
|
+
]
|
|
535
|
+
];
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
__name(convertHeaders, "convertHeaders");
|
|
539
|
+
function matchPatterns(value, patterns) {
|
|
540
|
+
return patterns.some((pattern) => {
|
|
541
|
+
return pattern.test(value);
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
__name(matchPatterns, "matchPatterns");
|
|
545
|
+
function skipEmptyValues(data) {
|
|
546
|
+
return Object.fromEntries(Object.entries(data).filter(([_, v]) => {
|
|
547
|
+
if (v == null || Number.isNaN(v)) return false;
|
|
548
|
+
if (Array.isArray(v) || import_buffer2.Buffer.isBuffer(v) || typeof v === "string") {
|
|
549
|
+
return v.length > 0;
|
|
550
|
+
}
|
|
551
|
+
return true;
|
|
552
|
+
}));
|
|
553
|
+
}
|
|
554
|
+
__name(skipEmptyValues, "skipEmptyValues");
|
|
555
|
+
function checkWritableFs() {
|
|
556
|
+
try {
|
|
557
|
+
const testPath = (0, import_path2.join)((0, import_os2.tmpdir)(), `apitally-${(0, import_crypto2.randomUUID)()}`);
|
|
558
|
+
(0, import_fs2.writeFileSync)(testPath, "test");
|
|
559
|
+
(0, import_fs2.unlinkSync)(testPath);
|
|
560
|
+
return true;
|
|
561
|
+
} catch (error) {
|
|
562
|
+
return false;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
__name(checkWritableFs, "checkWritableFs");
|
|
566
|
+
|
|
567
|
+
// src/common/serverErrorCounter.ts
|
|
568
|
+
var import_crypto3 = require("crypto");
|
|
214
569
|
var MAX_MSG_LENGTH = 2048;
|
|
215
570
|
var MAX_STACKTRACE_LENGTH = 65536;
|
|
216
571
|
var _ServerErrorCounter = class _ServerErrorCounter {
|
|
@@ -262,7 +617,7 @@ var _ServerErrorCounter = class _ServerErrorCounter {
|
|
|
262
617
|
serverError.msg.trim(),
|
|
263
618
|
serverError.traceback.trim()
|
|
264
619
|
].join("|");
|
|
265
|
-
return (0,
|
|
620
|
+
return (0, import_crypto3.createHash)("md5").update(hashInput).digest("hex");
|
|
266
621
|
}
|
|
267
622
|
getTruncatedMessage(msg) {
|
|
268
623
|
msg = msg.trim();
|
|
@@ -308,7 +663,7 @@ __name(_ServerErrorCounter, "ServerErrorCounter");
|
|
|
308
663
|
var ServerErrorCounter = _ServerErrorCounter;
|
|
309
664
|
|
|
310
665
|
// src/common/validationErrorCounter.ts
|
|
311
|
-
var
|
|
666
|
+
var import_crypto4 = require("crypto");
|
|
312
667
|
var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
313
668
|
errorCounts;
|
|
314
669
|
errorDetails;
|
|
@@ -352,7 +707,7 @@ var _ValidationErrorCounter = class _ValidationErrorCounter {
|
|
|
352
707
|
validationError.msg.trim(),
|
|
353
708
|
validationError.type
|
|
354
709
|
].join("|");
|
|
355
|
-
return (0,
|
|
710
|
+
return (0, import_crypto4.createHash)("md5").update(hashInput).digest("hex");
|
|
356
711
|
}
|
|
357
712
|
};
|
|
358
713
|
__name(_ValidationErrorCounter, "ValidationErrorCounter");
|
|
@@ -381,11 +736,12 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
381
736
|
startupData;
|
|
382
737
|
startupDataSent = false;
|
|
383
738
|
requestCounter;
|
|
739
|
+
requestLogger;
|
|
384
740
|
validationErrorCounter;
|
|
385
741
|
serverErrorCounter;
|
|
386
742
|
consumerRegistry;
|
|
387
743
|
logger;
|
|
388
|
-
constructor({ clientId, env = "dev", logger }) {
|
|
744
|
+
constructor({ clientId, env = "dev", requestLoggingConfig, logger }) {
|
|
389
745
|
if (_ApitallyClient.instance) {
|
|
390
746
|
throw new Error("Apitally client is already initialized");
|
|
391
747
|
}
|
|
@@ -398,9 +754,10 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
398
754
|
_ApitallyClient.instance = this;
|
|
399
755
|
this.clientId = clientId;
|
|
400
756
|
this.env = env;
|
|
401
|
-
this.instanceUuid = (0,
|
|
757
|
+
this.instanceUuid = (0, import_crypto5.randomUUID)();
|
|
402
758
|
this.syncDataQueue = [];
|
|
403
759
|
this.requestCounter = new RequestCounter();
|
|
760
|
+
this.requestLogger = new RequestLogger(requestLoggingConfig);
|
|
404
761
|
this.validationErrorCounter = new ValidationErrorCounter();
|
|
405
762
|
this.serverErrorCounter = new ServerErrorCounter();
|
|
406
763
|
this.consumerRegistry = new ConsumerRegistry();
|
|
@@ -422,6 +779,8 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
422
779
|
async handleShutdown() {
|
|
423
780
|
this.stopSync();
|
|
424
781
|
await this.sendSyncData();
|
|
782
|
+
await this.sendLogData();
|
|
783
|
+
await this.requestLogger.close();
|
|
425
784
|
_ApitallyClient.instance = void 0;
|
|
426
785
|
}
|
|
427
786
|
getHubUrlPrefix() {
|
|
@@ -468,7 +827,8 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
468
827
|
async sync() {
|
|
469
828
|
try {
|
|
470
829
|
const promises = [
|
|
471
|
-
this.sendSyncData()
|
|
830
|
+
this.sendSyncData(),
|
|
831
|
+
this.sendLogData()
|
|
472
832
|
];
|
|
473
833
|
if (!this.startupDataSent) {
|
|
474
834
|
promises.push(this.sendStartupData());
|
|
@@ -496,7 +856,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
496
856
|
this.logger.debug("Sending startup data to Apitally Hub");
|
|
497
857
|
const payload = {
|
|
498
858
|
instance_uuid: this.instanceUuid,
|
|
499
|
-
message_uuid: (0,
|
|
859
|
+
message_uuid: (0, import_crypto5.randomUUID)(),
|
|
500
860
|
...this.startupData
|
|
501
861
|
};
|
|
502
862
|
try {
|
|
@@ -518,7 +878,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
518
878
|
const newPayload = {
|
|
519
879
|
time_offset: 0,
|
|
520
880
|
instance_uuid: this.instanceUuid,
|
|
521
|
-
message_uuid: (0,
|
|
881
|
+
message_uuid: (0, import_crypto5.randomUUID)(),
|
|
522
882
|
requests: this.requestCounter.getAndResetRequests(),
|
|
523
883
|
validation_errors: this.validationErrorCounter.getAndResetValidationErrors(),
|
|
524
884
|
server_errors: this.serverErrorCounter.getAndResetServerErrors(),
|
|
@@ -537,8 +897,7 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
537
897
|
const timeOffset = Date.now() - time;
|
|
538
898
|
if (timeOffset <= MAX_QUEUE_TIME) {
|
|
539
899
|
if (i > 0) {
|
|
540
|
-
|
|
541
|
-
await new Promise((resolve) => setTimeout(resolve, waitMs));
|
|
900
|
+
await this.randomDelay();
|
|
542
901
|
}
|
|
543
902
|
payload.time_offset = timeOffset / 1e3;
|
|
544
903
|
await this.sendData("sync", payload);
|
|
@@ -557,6 +916,52 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
557
916
|
}
|
|
558
917
|
}
|
|
559
918
|
}
|
|
919
|
+
async sendLogData() {
|
|
920
|
+
this.logger.debug("Sending request log data to Apitally Hub");
|
|
921
|
+
await this.requestLogger.rotateFile();
|
|
922
|
+
const fetchWithRetry = (0, import_fetch_retry.default)(fetch, {
|
|
923
|
+
retries: 3,
|
|
924
|
+
retryDelay: 1e3,
|
|
925
|
+
retryOn: [
|
|
926
|
+
408,
|
|
927
|
+
429,
|
|
928
|
+
500,
|
|
929
|
+
502,
|
|
930
|
+
503,
|
|
931
|
+
504
|
|
932
|
+
]
|
|
933
|
+
});
|
|
934
|
+
let i = 0;
|
|
935
|
+
let logFile;
|
|
936
|
+
while (logFile = this.requestLogger.getFile()) {
|
|
937
|
+
if (i > 0) {
|
|
938
|
+
await this.randomDelay();
|
|
939
|
+
}
|
|
940
|
+
try {
|
|
941
|
+
const response = await fetchWithRetry(`${this.getHubUrlPrefix()}log?uuid=${logFile.uuid}`, {
|
|
942
|
+
method: "POST",
|
|
943
|
+
body: await logFile.getContent()
|
|
944
|
+
});
|
|
945
|
+
if (response.status === 402 && response.headers.has("Retry-After")) {
|
|
946
|
+
const retryAfter = parseInt(response.headers.get("Retry-After") ?? "0");
|
|
947
|
+
if (retryAfter > 0) {
|
|
948
|
+
this.requestLogger.suspendUntil = Date.now() + retryAfter * 1e3;
|
|
949
|
+
this.requestLogger.clear();
|
|
950
|
+
return;
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
if (!response.ok) {
|
|
954
|
+
throw new HTTPError(response);
|
|
955
|
+
}
|
|
956
|
+
logFile.delete();
|
|
957
|
+
} catch (error) {
|
|
958
|
+
this.requestLogger.retryFileLater(logFile);
|
|
959
|
+
break;
|
|
960
|
+
}
|
|
961
|
+
i++;
|
|
962
|
+
if (i >= 10) break;
|
|
963
|
+
}
|
|
964
|
+
}
|
|
560
965
|
handleHubError(error) {
|
|
561
966
|
if (error instanceof HTTPError) {
|
|
562
967
|
if (error.response.status === 404) {
|
|
@@ -571,6 +976,10 @@ var _ApitallyClient = class _ApitallyClient {
|
|
|
571
976
|
}
|
|
572
977
|
return false;
|
|
573
978
|
}
|
|
979
|
+
async randomDelay() {
|
|
980
|
+
const delay = 100 + Math.random() * 200;
|
|
981
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
982
|
+
}
|
|
574
983
|
};
|
|
575
984
|
__name(_ApitallyClient, "ApitallyClient");
|
|
576
985
|
__publicField(_ApitallyClient, "instance");
|
|
@@ -604,13 +1013,13 @@ var useApitally = /* @__PURE__ */ __name((app, config) => {
|
|
|
604
1013
|
}, 1e3);
|
|
605
1014
|
}, "useApitally");
|
|
606
1015
|
var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
607
|
-
const zodInstalled = getPackageVersion("zod") !== null;
|
|
608
1016
|
return async (c, next) => {
|
|
609
1017
|
const startTime = import_perf_hooks.performance.now();
|
|
610
1018
|
await next();
|
|
611
1019
|
let response;
|
|
612
1020
|
const responseTime = import_perf_hooks.performance.now() - startTime;
|
|
613
1021
|
const [responseSize, newResponse] = await measureResponseSize(c.res);
|
|
1022
|
+
const requestSize = c.req.header("Content-Length");
|
|
614
1023
|
const consumer = getConsumer(c);
|
|
615
1024
|
client.consumerRegistry.addOrUpdateConsumer(consumer);
|
|
616
1025
|
client.requestCounter.addRequest({
|
|
@@ -619,11 +1028,11 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
619
1028
|
path: c.req.routePath,
|
|
620
1029
|
statusCode: c.res.status,
|
|
621
1030
|
responseTime,
|
|
622
|
-
requestSize
|
|
1031
|
+
requestSize,
|
|
623
1032
|
responseSize
|
|
624
1033
|
});
|
|
625
1034
|
response = newResponse;
|
|
626
|
-
if (c.res.status === 400
|
|
1035
|
+
if (c.res.status === 400) {
|
|
627
1036
|
const [responseJson, newResponse2] = await getResponseJson(response);
|
|
628
1037
|
const validationErrors = extractZodErrors(responseJson);
|
|
629
1038
|
validationErrors.forEach((error) => {
|
|
@@ -646,6 +1055,34 @@ var getMiddleware = /* @__PURE__ */ __name((client) => {
|
|
|
646
1055
|
traceback: c.error.stack || ""
|
|
647
1056
|
});
|
|
648
1057
|
}
|
|
1058
|
+
if (client.requestLogger.enabled) {
|
|
1059
|
+
let requestBody;
|
|
1060
|
+
let responseBody;
|
|
1061
|
+
let newResponse2 = response;
|
|
1062
|
+
if (client.requestLogger.config.logRequestBody) {
|
|
1063
|
+
requestBody = Buffer.from(await c.req.arrayBuffer());
|
|
1064
|
+
}
|
|
1065
|
+
if (client.requestLogger.config.logResponseBody) {
|
|
1066
|
+
[responseBody, newResponse2] = await getResponseBody(response);
|
|
1067
|
+
response = newResponse2;
|
|
1068
|
+
}
|
|
1069
|
+
client.requestLogger.logRequest({
|
|
1070
|
+
timestamp: Date.now() / 1e3,
|
|
1071
|
+
method: c.req.method,
|
|
1072
|
+
path: c.req.routePath,
|
|
1073
|
+
url: c.req.url,
|
|
1074
|
+
headers: convertHeaders(c.req.header()),
|
|
1075
|
+
size: Number(requestSize),
|
|
1076
|
+
consumer: consumer == null ? void 0 : consumer.identifier,
|
|
1077
|
+
body: requestBody
|
|
1078
|
+
}, {
|
|
1079
|
+
statusCode: c.res.status,
|
|
1080
|
+
responseTime: responseTime / 1e3,
|
|
1081
|
+
headers: convertHeaders(c.res.headers),
|
|
1082
|
+
size: responseSize,
|
|
1083
|
+
body: responseBody
|
|
1084
|
+
});
|
|
1085
|
+
}
|
|
649
1086
|
c.res = response;
|
|
650
1087
|
};
|
|
651
1088
|
}, "getMiddleware");
|
|
@@ -675,6 +1112,14 @@ var measureResponseSize = /* @__PURE__ */ __name(async (response) => {
|
|
|
675
1112
|
newResponse1
|
|
676
1113
|
];
|
|
677
1114
|
}, "measureResponseSize");
|
|
1115
|
+
var getResponseBody = /* @__PURE__ */ __name(async (response) => {
|
|
1116
|
+
const [newResponse1, newResponse2] = await teeResponse(response);
|
|
1117
|
+
const responseBuffer = Buffer.from(await newResponse2.arrayBuffer());
|
|
1118
|
+
return [
|
|
1119
|
+
responseBuffer,
|
|
1120
|
+
newResponse1
|
|
1121
|
+
];
|
|
1122
|
+
}, "getResponseBody");
|
|
678
1123
|
var getResponseJson = /* @__PURE__ */ __name(async (response) => {
|
|
679
1124
|
const contentType = response.headers.get("content-type");
|
|
680
1125
|
if (contentType && contentType.includes("application/json")) {
|