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