@oconde/radar 0.1.0 → 0.3.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/CHANGELOG.md +38 -0
- package/PROTOCOL.md +43 -7
- package/README.md +65 -61
- package/dist/bin/radar-sourcemaps.js +244 -0
- package/dist/browser/index.cjs +173 -0
- package/dist/browser/index.cjs.map +1 -0
- package/dist/browser/index.d.cts +30 -0
- package/dist/browser/index.d.ts +30 -0
- package/dist/browser/index.js +152 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/{client-ZpQHpeAu.d.ts → client-BB-to8cr.d.cts} +39 -2
- package/dist/{client-DbNex6tj.d.cts → client-BS798K3S.d.ts} +39 -2
- package/dist/index.cjs +469 -78
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +470 -79
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +469 -78
- package/dist/nest/index.cjs.map +1 -1
- package/dist/nest/index.d.cts +1 -1
- package/dist/nest/index.d.ts +1 -1
- package/dist/nest/index.js +472 -81
- package/dist/nest/index.js.map +1 -1
- package/dist/protocol/index.cjs +16 -3
- package/dist/protocol/index.cjs.map +1 -1
- package/dist/protocol/index.d.cts +27 -2
- package/dist/protocol/index.d.ts +27 -2
- package/dist/protocol/index.js +11 -2
- package/dist/protocol/index.js.map +1 -1
- package/dist/sveltekit/index.cjs +2003 -0
- package/dist/sveltekit/index.cjs.map +1 -0
- package/dist/sveltekit/index.d.cts +28 -0
- package/dist/sveltekit/index.d.ts +28 -0
- package/dist/sveltekit/index.js +1973 -0
- package/dist/sveltekit/index.js.map +1 -0
- package/package.json +39 -4
package/dist/index.js
CHANGED
|
@@ -1,33 +1,17 @@
|
|
|
1
1
|
// src/client.ts
|
|
2
2
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
3
3
|
import { hostname } from "os";
|
|
4
|
-
import { performance as
|
|
4
|
+
import { performance as performance4 } from "perf_hooks";
|
|
5
5
|
|
|
6
6
|
// src/context.ts
|
|
7
7
|
import { AsyncLocalStorage } from "async_hooks";
|
|
8
|
-
var STATE_KEY = /* @__PURE__ */ Symbol.for("@oconde/radar");
|
|
9
|
-
function globalState() {
|
|
10
|
-
const holder = globalThis;
|
|
11
|
-
let state2 = holder[STATE_KEY];
|
|
12
|
-
if (!state2) {
|
|
13
|
-
state2 = { storage: new AsyncLocalStorage() };
|
|
14
|
-
holder[STATE_KEY] = state2;
|
|
15
|
-
}
|
|
16
|
-
return state2;
|
|
17
|
-
}
|
|
18
|
-
function currentContext() {
|
|
19
|
-
return globalState().storage.getStore();
|
|
20
|
-
}
|
|
21
|
-
function runWithContext(context, fn) {
|
|
22
|
-
return globalState().storage.run(context, fn);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// src/express.ts
|
|
26
8
|
import { randomUUID } from "crypto";
|
|
27
|
-
import { performance } from "perf_hooks";
|
|
28
9
|
|
|
29
10
|
// src/protocol/index.ts
|
|
30
11
|
var INGEST_PATH = "/api/v1/events";
|
|
12
|
+
var CHECKIN_PATH = "/api/v1/checkins/";
|
|
13
|
+
var CHECKIN_SLUG = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
14
|
+
var CHECKIN_STATUSES = ["in_progress", "ok", "error"];
|
|
31
15
|
var LIMITS = {
|
|
32
16
|
batchBytes: 1e6,
|
|
33
17
|
batchEvents: 500,
|
|
@@ -43,9 +27,36 @@ var LIMITS = {
|
|
|
43
27
|
causeDepth: 5,
|
|
44
28
|
depth: 6,
|
|
45
29
|
arrayItems: 50,
|
|
46
|
-
requestIdLength: 128
|
|
30
|
+
requestIdLength: 128,
|
|
31
|
+
checkInIdLength: 64
|
|
47
32
|
};
|
|
48
33
|
|
|
34
|
+
// src/context.ts
|
|
35
|
+
var STATE_KEY = /* @__PURE__ */ Symbol.for("@oconde/radar");
|
|
36
|
+
function globalState() {
|
|
37
|
+
const holder = globalThis;
|
|
38
|
+
let state2 = holder[STATE_KEY];
|
|
39
|
+
if (!state2) {
|
|
40
|
+
state2 = { storage: new AsyncLocalStorage() };
|
|
41
|
+
holder[STATE_KEY] = state2;
|
|
42
|
+
}
|
|
43
|
+
return state2;
|
|
44
|
+
}
|
|
45
|
+
function currentContext() {
|
|
46
|
+
return globalState().storage.getStore();
|
|
47
|
+
}
|
|
48
|
+
function runWithContext(context, fn) {
|
|
49
|
+
return globalState().storage.run(context, fn);
|
|
50
|
+
}
|
|
51
|
+
var SAFE_REQUEST_ID = /^[\x21-\x7e]+$/;
|
|
52
|
+
function requestIdFrom(incoming) {
|
|
53
|
+
const value = incoming?.trim();
|
|
54
|
+
return value && value.length <= LIMITS.requestIdLength && SAFE_REQUEST_ID.test(value) ? value : randomUUID();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/express.ts
|
|
58
|
+
import { performance as performance2 } from "perf_hooks";
|
|
59
|
+
|
|
49
60
|
// src/redact.ts
|
|
50
61
|
import { createHash, createHmac, randomBytes } from "crypto";
|
|
51
62
|
var SECRET_WORDS = /* @__PURE__ */ new Set([
|
|
@@ -244,7 +255,44 @@ function requestPath(req) {
|
|
|
244
255
|
}
|
|
245
256
|
function requestRoute(req, route) {
|
|
246
257
|
if (route) return route;
|
|
247
|
-
|
|
258
|
+
if (typeof req.route?.path !== "string") return void 0;
|
|
259
|
+
const base = typeof req.baseUrl === "string" ? req.baseUrl.replace(/\/+$/, "") : "";
|
|
260
|
+
if (!base) return req.route.path;
|
|
261
|
+
return req.route.path === "/" ? base : `${base}${req.route.path}`;
|
|
262
|
+
}
|
|
263
|
+
var ROUTE_PARAM = /^:([A-Za-z_$][\w$]*)/;
|
|
264
|
+
function pathSegments(value) {
|
|
265
|
+
const segments = value.split("/");
|
|
266
|
+
while (segments.length > 1 && segments[segments.length - 1] === "") segments.pop();
|
|
267
|
+
return segments;
|
|
268
|
+
}
|
|
269
|
+
function decodeSegment(value) {
|
|
270
|
+
try {
|
|
271
|
+
return decodeURIComponent(value);
|
|
272
|
+
} catch {
|
|
273
|
+
return value;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function routeParams(req, path) {
|
|
277
|
+
if (typeof req.route?.path !== "string") return {};
|
|
278
|
+
const pattern = pathSegments(`${typeof req.baseUrl === "string" ? req.baseUrl : ""}${req.route.path}`).filter((segment, index) => index > 0 || segment !== "");
|
|
279
|
+
const values = pathSegments(path);
|
|
280
|
+
if (pattern.length > values.length) return {};
|
|
281
|
+
const offset = values.length - pattern.length;
|
|
282
|
+
const result = {};
|
|
283
|
+
for (let index = 0; index < pattern.length; index++) {
|
|
284
|
+
const value = values[offset + index];
|
|
285
|
+
const param = ROUTE_PARAM.exec(pattern[index]);
|
|
286
|
+
if (!param) {
|
|
287
|
+
if (pattern[index] !== value) return {};
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (value) result[param[1]] = decodeSegment(value);
|
|
291
|
+
}
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
function pathParams(req, path) {
|
|
295
|
+
return { ...routeParams(req, path), ...req.params ?? {} };
|
|
248
296
|
}
|
|
249
297
|
function redactPath(path, params, mode) {
|
|
250
298
|
if (mode === "none" || !params) return path;
|
|
@@ -261,7 +309,7 @@ function safeUrl(req, mode) {
|
|
|
261
309
|
const queryStart = url.indexOf("?");
|
|
262
310
|
const path = queryStart >= 0 ? url.slice(0, queryStart) : url;
|
|
263
311
|
const query = queryStart >= 0 ? url.slice(queryStart) : "";
|
|
264
|
-
return redactUrl(redactPath(path, req
|
|
312
|
+
return redactUrl(redactPath(path, pathParams(req, path), mode) + query, mode);
|
|
265
313
|
}
|
|
266
314
|
function redactUrl(url, mode) {
|
|
267
315
|
const queryStart = url.indexOf("?");
|
|
@@ -329,9 +377,8 @@ function createMiddleware(logger) {
|
|
|
329
377
|
return (req, res, next) => {
|
|
330
378
|
let context;
|
|
331
379
|
try {
|
|
332
|
-
const
|
|
333
|
-
|
|
334
|
-
context = { requestId, startedAt: performance.now(), req };
|
|
380
|
+
const requestId = requestIdFrom(headerValue(req.headers["x-request-id"]));
|
|
381
|
+
context = { requestId, startedAt: performance2.now(), req };
|
|
335
382
|
res.setHeader("x-request-id", requestId);
|
|
336
383
|
if (logger.shouldLogRequest(req)) res.once("finish", () => logger.logHttpRequest(context, res.statusCode));
|
|
337
384
|
} catch {
|
|
@@ -345,6 +392,12 @@ function createMiddleware(logger) {
|
|
|
345
392
|
// src/options.ts
|
|
346
393
|
var DEFAULT_ENDPOINT = "https://radar-ingest.oconde.dev";
|
|
347
394
|
var LEVELS = ["debug", "info", "warn", "error"];
|
|
395
|
+
function resolveTraceFetch(value) {
|
|
396
|
+
if (!value) return { enabled: false, propagateTo: [] };
|
|
397
|
+
if (value === true) return { enabled: true, propagateTo: [] };
|
|
398
|
+
const targets2 = Array.isArray(value.propagateTo) ? value.propagateTo : [];
|
|
399
|
+
return { enabled: true, propagateTo: targets2.filter((target) => typeof target === "string" ? target.trim() !== "" : target instanceof RegExp) };
|
|
400
|
+
}
|
|
348
401
|
function resolveOptions(options = {}, env = process.env) {
|
|
349
402
|
const key = options.key?.trim() || void 0;
|
|
350
403
|
return {
|
|
@@ -360,6 +413,7 @@ function resolveOptions(options = {}, env = process.env) {
|
|
|
360
413
|
logRequests: options.logRequests ?? true,
|
|
361
414
|
ignorePaths: options.ignorePaths ?? ["/healthz", "/health"],
|
|
362
415
|
captureUnhandled: options.captureUnhandled ?? true,
|
|
416
|
+
traceFetch: resolveTraceFetch(options.traceFetch),
|
|
363
417
|
debug: options.debug ?? false
|
|
364
418
|
};
|
|
365
419
|
}
|
|
@@ -421,16 +475,144 @@ import { readFile, stat } from "fs/promises";
|
|
|
421
475
|
import { SourceMap } from "module";
|
|
422
476
|
import { dirname, isAbsolute, relative, resolve, sep } from "path";
|
|
423
477
|
import { fileURLToPath } from "url";
|
|
478
|
+
|
|
479
|
+
// src/function-name.ts
|
|
480
|
+
var IDENTIFIER = "[A-Za-z_$][\\w$]*";
|
|
481
|
+
var MAX_LINES_UP = 400;
|
|
482
|
+
var MAX_HEADER_LINES = 6;
|
|
483
|
+
var MAX_NAME_LENGTH = 100;
|
|
484
|
+
var MAX_COMMENT_LINES = 40;
|
|
485
|
+
var CONTROL_BLOCK = /^\s*(?:\}\s*)?(?:if|else|for|while|do|switch|try|catch|finally|with)\b/;
|
|
486
|
+
var CLASS_BODY = new RegExp(`\\bclass\\s+${IDENTIFIER}`);
|
|
487
|
+
var MARKUP_BOUNDARY = /<\/?(?:script|style|template)\b/i;
|
|
488
|
+
var KEYWORDS = /* @__PURE__ */ new Set(["if", "for", "while", "switch", "catch", "function", "return", "new", "typeof", "await", "else", "do", "try"]);
|
|
489
|
+
var NAME_PATTERNS = [
|
|
490
|
+
new RegExp(`\\bfunction\\s*\\*?\\s*(${IDENTIFIER})\\s*\\(`),
|
|
491
|
+
new RegExp(`(${IDENTIFIER})\\s*[:=]\\s*(?:async\\s+)?function\\b`),
|
|
492
|
+
new RegExp(`(${IDENTIFIER})\\s*(?::\\s*[^=]+)?=\\s*(?:async\\s+)?(?:\\([^]*\\)|${IDENTIFIER})\\s*(?::\\s*[^=]+)?=>\\s*$`),
|
|
493
|
+
new RegExp(`(?:^|[{,(]\\s*)(${IDENTIFIER})\\s*:\\s*(?:async\\s+)?(?:\\([^]*\\)|${IDENTIFIER})\\s*(?::\\s*[^=]+)?=>\\s*$`)
|
|
494
|
+
];
|
|
495
|
+
var METHOD_PATTERN = new RegExp(`^\\s*(?:(?:export|default|public|private|protected|static|async|override|readonly|get|set)\\s+)*\\*?\\s*(${IDENTIFIER})\\s*(?:<[^>]*>)?\\s*\\([^]*\\)\\s*(?::\\s*[^{]+)?$`);
|
|
496
|
+
var ANONYMOUS_FUNCTION = /\bfunction\b|=>/;
|
|
497
|
+
var STRING_LITERAL = /(["'`])(?:\\.|(?!\1)[^\\])*\1/g;
|
|
498
|
+
function lineCommentAt(text, from) {
|
|
499
|
+
for (let index = text.indexOf("//", from); index >= 0; index = text.indexOf("//", index + 1)) {
|
|
500
|
+
if (index === 0 || text[index - 1] !== ":") return index;
|
|
501
|
+
}
|
|
502
|
+
return -1;
|
|
503
|
+
}
|
|
504
|
+
function blockCommentAt(text, from) {
|
|
505
|
+
for (let index = text.indexOf("/*", from); index >= 0; index = text.indexOf("/*", index + 1)) {
|
|
506
|
+
if (index === 0 || text[index - 1] !== "\\") return index;
|
|
507
|
+
}
|
|
508
|
+
return -1;
|
|
509
|
+
}
|
|
510
|
+
function stripComments(text, insideComment, closesLater) {
|
|
511
|
+
let code = "";
|
|
512
|
+
let position = 0;
|
|
513
|
+
let inside = insideComment;
|
|
514
|
+
while (position < text.length) {
|
|
515
|
+
if (inside) {
|
|
516
|
+
const end = text.indexOf("*/", position);
|
|
517
|
+
if (end < 0) return { code, insideComment: true };
|
|
518
|
+
inside = false;
|
|
519
|
+
position = end + 2;
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
const block = blockCommentAt(text, position);
|
|
523
|
+
const line = lineCommentAt(text, position);
|
|
524
|
+
if (line >= 0 && (block < 0 || line < block)) return { code: code + text.slice(position, line), insideComment: false };
|
|
525
|
+
if (block < 0) return { code: code + text.slice(position), insideComment: false };
|
|
526
|
+
if (text.indexOf("*/", block + 2) < 0 && !closesLater()) {
|
|
527
|
+
code += text.slice(position, block + 2);
|
|
528
|
+
position = block + 2;
|
|
529
|
+
continue;
|
|
530
|
+
}
|
|
531
|
+
code += text.slice(position, block);
|
|
532
|
+
inside = true;
|
|
533
|
+
position = block + 2;
|
|
534
|
+
}
|
|
535
|
+
return { code, insideComment: inside };
|
|
536
|
+
}
|
|
537
|
+
function codeLines(lines, from, start, column) {
|
|
538
|
+
const plain = lines.slice(from, start + 1).map((line) => line.replace(STRING_LITERAL, '""'));
|
|
539
|
+
const closesWithin = (index) => plain.slice(index + 1, index + 1 + MAX_COMMENT_LINES).some((line) => line.includes("*/"));
|
|
540
|
+
const result = [];
|
|
541
|
+
let insideComment = false;
|
|
542
|
+
for (let index = 0; index < plain.length; index++) {
|
|
543
|
+
const text = index === plain.length - 1 && column ? (lines[start] ?? "").slice(0, Math.max(0, column - 1)).replace(STRING_LITERAL, '""') : plain[index] ?? "";
|
|
544
|
+
const stripped = stripComments(text, insideComment, () => closesWithin(index));
|
|
545
|
+
insideComment = stripped.insideComment;
|
|
546
|
+
result.push(stripped.code);
|
|
547
|
+
}
|
|
548
|
+
return result;
|
|
549
|
+
}
|
|
550
|
+
function parenBalance(text) {
|
|
551
|
+
let balance = 0;
|
|
552
|
+
for (const char of text) {
|
|
553
|
+
if (char === "(") balance += 1;
|
|
554
|
+
else if (char === ")") balance -= 1;
|
|
555
|
+
}
|
|
556
|
+
return balance;
|
|
557
|
+
}
|
|
558
|
+
function headerOf(code, index, prefix) {
|
|
559
|
+
let header = prefix;
|
|
560
|
+
for (let previous = index - 1; previous >= 0 && previous >= index - MAX_HEADER_LINES; previous--) {
|
|
561
|
+
if (header.trim() !== "" && parenBalance(header) >= 0) break;
|
|
562
|
+
header = `${code[previous] ?? ""} ${header}`;
|
|
563
|
+
}
|
|
564
|
+
return header;
|
|
565
|
+
}
|
|
566
|
+
function nameIn(header) {
|
|
567
|
+
const patterns = ANONYMOUS_FUNCTION.test(header) ? NAME_PATTERNS : [...NAME_PATTERNS, METHOD_PATTERN];
|
|
568
|
+
for (const pattern of patterns) {
|
|
569
|
+
const name = pattern.exec(header)?.[1];
|
|
570
|
+
if (name && !KEYWORDS.has(name)) return name.slice(0, MAX_NAME_LENGTH);
|
|
571
|
+
}
|
|
572
|
+
return void 0;
|
|
573
|
+
}
|
|
574
|
+
function enclosingFunctionName(lines, line, column) {
|
|
575
|
+
const start = line - 1;
|
|
576
|
+
if (start < 0 || start >= lines.length) return void 0;
|
|
577
|
+
const from = Math.max(0, start - MAX_LINES_UP - MAX_HEADER_LINES);
|
|
578
|
+
const cleaned = codeLines(lines, from, start, column);
|
|
579
|
+
let depth = 0;
|
|
580
|
+
for (let index = start; index >= 0 && index >= start - MAX_LINES_UP; index--) {
|
|
581
|
+
if (index < start && MARKUP_BOUNDARY.test(lines[index] ?? "")) return void 0;
|
|
582
|
+
const code = cleaned[index - from] ?? "";
|
|
583
|
+
for (let position = code.length - 1; position >= 0; position--) {
|
|
584
|
+
const char = code[position];
|
|
585
|
+
if (char === "}") {
|
|
586
|
+
depth += 1;
|
|
587
|
+
} else if (char === "{") {
|
|
588
|
+
if (depth > 0) {
|
|
589
|
+
depth -= 1;
|
|
590
|
+
continue;
|
|
591
|
+
}
|
|
592
|
+
const header = headerOf(cleaned, index - from, code.slice(0, position));
|
|
593
|
+
if (CLASS_BODY.test(header)) return void 0;
|
|
594
|
+
if (CONTROL_BLOCK.test(header)) continue;
|
|
595
|
+
const name = nameIn(header);
|
|
596
|
+
if (name) return name;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
return void 0;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
// src/source.ts
|
|
424
604
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
425
605
|
var MAX_CACHED_FILES = 50;
|
|
426
606
|
var MAPPING_URL = /\/\/[#@]\s*sourceMappingURL=(\S+)\s*$/;
|
|
427
607
|
var cache = /* @__PURE__ */ new Map();
|
|
428
|
-
async function enrichFrames(frames, cwd = process.cwd()) {
|
|
608
|
+
async function enrichFrames(frames, cwd = process.cwd(), options = {}) {
|
|
429
609
|
const result = [];
|
|
430
610
|
let withContext = 0;
|
|
431
611
|
for (const frame of frames) {
|
|
432
612
|
try {
|
|
433
|
-
const { frame:
|
|
613
|
+
const { frame: located, lines, original } = await mapFrame(frame, options.mapLocator);
|
|
614
|
+
const named = options.inferFunctionNames && original && !located.fn && lines && located.line ? withInferredName(located, lines) : located;
|
|
615
|
+
const mapped = named.inApp && isLibraryPath(named.file) ? { ...named, inApp: false } : named;
|
|
434
616
|
let next = mapped;
|
|
435
617
|
if (mapped.inApp && lines && mapped.line && withContext < LIMITS.contextFrames) {
|
|
436
618
|
const context = contextFor(lines, mapped.line);
|
|
@@ -446,29 +628,36 @@ async function enrichFrames(frames, cwd = process.cwd()) {
|
|
|
446
628
|
}
|
|
447
629
|
return result;
|
|
448
630
|
}
|
|
449
|
-
async function enrichException(exception, cwd = process.cwd()) {
|
|
450
|
-
const frames = await enrichFrames(exception.frames, cwd);
|
|
631
|
+
async function enrichException(exception, cwd = process.cwd(), options = {}) {
|
|
632
|
+
const frames = await enrichFrames(exception.frames, cwd, options);
|
|
451
633
|
if (!exception.cause) return { ...exception, frames };
|
|
452
|
-
return { ...exception, frames, cause: await enrichException(exception.cause, cwd) };
|
|
634
|
+
return { ...exception, frames, cause: await enrichException(exception.cause, cwd, options) };
|
|
635
|
+
}
|
|
636
|
+
function withInferredName(frame, lines) {
|
|
637
|
+
const fn = enclosingFunctionName(lines, frame.line ?? 0, frame.col);
|
|
638
|
+
return fn ? { ...frame, fn } : frame;
|
|
639
|
+
}
|
|
640
|
+
function isLibraryPath(file) {
|
|
641
|
+
return file.includes("/node_modules/") || file.includes("\\node_modules\\");
|
|
453
642
|
}
|
|
454
643
|
function relativize(file, cwd) {
|
|
455
644
|
if (!isAbsolute(file)) return file;
|
|
456
645
|
const path = relative(cwd, file);
|
|
457
646
|
return path.startsWith("..") || isAbsolute(path) ? file : path.split(sep).join("/");
|
|
458
647
|
}
|
|
459
|
-
async function mapFrame(frame) {
|
|
460
|
-
if (!frame.line || !isAbsolute(frame.file)) return { frame, lines: null };
|
|
461
|
-
const file = await loadFile(frame.file);
|
|
462
|
-
if (!file) return { frame, lines: null };
|
|
463
|
-
if (!file.map) return { frame, lines: file.lines };
|
|
648
|
+
async function mapFrame(frame, locator) {
|
|
649
|
+
if (!frame.line || !isAbsolute(frame.file)) return { frame, lines: null, original: false };
|
|
650
|
+
const file = await loadFile(frame.file, locator);
|
|
651
|
+
if (!file) return { frame, lines: null, original: false };
|
|
652
|
+
if (!file.map) return { frame, lines: file.lines, original: false };
|
|
464
653
|
const entry = file.map.map.findEntry(frame.line - 1, (frame.col ?? 1) - 1);
|
|
465
|
-
if (typeof entry.originalSource !== "string" || typeof entry.originalLine !== "number") return { frame, lines: file.lines };
|
|
654
|
+
if (typeof entry.originalSource !== "string" || typeof entry.originalLine !== "number") return { frame, lines: file.lines, original: false };
|
|
466
655
|
const originalPath = resolveSource(file.map, entry.originalSource);
|
|
467
656
|
const mapped = { ...frame, file: originalPath, line: entry.originalLine + 1, col: (entry.originalColumn ?? 0) + 1 };
|
|
468
657
|
const lines = embeddedLines(file.map, file.map.sources.indexOf(entry.originalSource));
|
|
469
|
-
if (lines) return { frame: mapped, lines };
|
|
658
|
+
if (lines) return { frame: mapped, lines, original: true };
|
|
470
659
|
const original = await loadFile(originalPath);
|
|
471
|
-
return { frame: mapped, lines: original?.lines ?? null };
|
|
660
|
+
return { frame: mapped, lines: original?.lines ?? null, original: true };
|
|
472
661
|
}
|
|
473
662
|
function embeddedLines(map, index) {
|
|
474
663
|
if (index < 0) return null;
|
|
@@ -500,26 +689,27 @@ function resolveSource(map, source) {
|
|
|
500
689
|
}
|
|
501
690
|
return isAbsolute(source) ? source : resolve(map.baseDir, source);
|
|
502
691
|
}
|
|
503
|
-
function loadFile(path) {
|
|
504
|
-
const
|
|
692
|
+
function loadFile(path, locator) {
|
|
693
|
+
const key = locator ? `${path}\0located` : path;
|
|
694
|
+
const cached = cache.get(key);
|
|
505
695
|
if (cached) {
|
|
506
|
-
cache.delete(
|
|
507
|
-
cache.set(
|
|
696
|
+
cache.delete(key);
|
|
697
|
+
cache.set(key, cached);
|
|
508
698
|
return cached;
|
|
509
699
|
}
|
|
510
|
-
const pending = readSource(path).catch(() => null);
|
|
511
|
-
cache.set(
|
|
700
|
+
const pending = readSource(path, locator).catch(() => null);
|
|
701
|
+
cache.set(key, pending);
|
|
512
702
|
if (cache.size > MAX_CACHED_FILES) {
|
|
513
703
|
const oldest = cache.keys().next().value;
|
|
514
704
|
if (oldest !== void 0) cache.delete(oldest);
|
|
515
705
|
}
|
|
516
706
|
return pending;
|
|
517
707
|
}
|
|
518
|
-
async function readSource(path) {
|
|
708
|
+
async function readSource(path, locator) {
|
|
519
709
|
const text = await readText(path);
|
|
520
710
|
if (text === null) return null;
|
|
521
711
|
const lines = text.split(/\r?\n/);
|
|
522
|
-
return { lines, map: await loadMap(path, lines) };
|
|
712
|
+
return { lines, map: await loadMap(path, lines, locator) };
|
|
523
713
|
}
|
|
524
714
|
async function readText(path) {
|
|
525
715
|
try {
|
|
@@ -530,7 +720,7 @@ async function readText(path) {
|
|
|
530
720
|
return null;
|
|
531
721
|
}
|
|
532
722
|
}
|
|
533
|
-
async function loadMap(path, lines) {
|
|
723
|
+
async function loadMap(path, lines, locator) {
|
|
534
724
|
let url;
|
|
535
725
|
for (let index = lines.length - 1; index >= 0 && index >= lines.length - 5; index--) {
|
|
536
726
|
const match = MAPPING_URL.exec(lines[index] ?? "");
|
|
@@ -539,10 +729,15 @@ async function loadMap(path, lines) {
|
|
|
539
729
|
break;
|
|
540
730
|
}
|
|
541
731
|
}
|
|
542
|
-
if (!url) return null;
|
|
543
732
|
let raw;
|
|
544
733
|
let mapDir;
|
|
545
|
-
|
|
734
|
+
const located = url ? null : locator ? locator(path) : `${path}.map`;
|
|
735
|
+
if (located) {
|
|
736
|
+
raw = await readText(located);
|
|
737
|
+
mapDir = dirname(located);
|
|
738
|
+
} else if (!url) {
|
|
739
|
+
return null;
|
|
740
|
+
} else if (url.startsWith("data:")) {
|
|
546
741
|
const comma = url.indexOf(",");
|
|
547
742
|
const encoded = url.slice(comma + 1);
|
|
548
743
|
raw = url.slice(0, comma).endsWith(";base64") ? Buffer.from(encoded, "base64").toString("utf8") : decodeURIComponent(encoded);
|
|
@@ -571,12 +766,13 @@ async function loadMap(path, lines) {
|
|
|
571
766
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
572
767
|
var WITH_FUNCTION = /^at (?:async )?(.+?) \((.+)\)$/;
|
|
573
768
|
var LOCATION = /^(.+):(\d+):(\d+)$/;
|
|
769
|
+
var MAX_FRAME_LINE = 1e3;
|
|
574
770
|
function parseStack(stack) {
|
|
575
771
|
if (!stack) return [];
|
|
576
772
|
const frames = [];
|
|
577
773
|
for (const raw of stack.split("\n")) {
|
|
578
774
|
const line = raw.trim();
|
|
579
|
-
if (!line.startsWith("at ")) continue;
|
|
775
|
+
if (line.length > MAX_FRAME_LINE || !line.startsWith("at ")) continue;
|
|
580
776
|
const withFunction = WITH_FUNCTION.exec(line);
|
|
581
777
|
const fn = withFunction?.[1];
|
|
582
778
|
const location = withFunction ? withFunction[2] ?? "" : line.replace(/^at (?:async )?/, "");
|
|
@@ -635,20 +831,110 @@ function safeStringify(value) {
|
|
|
635
831
|
}
|
|
636
832
|
}
|
|
637
833
|
|
|
638
|
-
// src/transport.ts
|
|
639
|
-
import { promisify } from "util";
|
|
640
|
-
import { gzip } from "zlib";
|
|
641
|
-
|
|
642
834
|
// src/util.ts
|
|
643
|
-
import { performance as
|
|
835
|
+
import { performance as performance3 } from "perf_hooks";
|
|
644
836
|
function describeError(error) {
|
|
645
837
|
return error instanceof Error ? error.message : String(error);
|
|
646
838
|
}
|
|
647
839
|
function elapsedSince(startedAt) {
|
|
648
|
-
return Math.round(
|
|
840
|
+
return Math.round(performance3.now() - startedAt);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
// src/trace-fetch.ts
|
|
844
|
+
var WRAPPED = /* @__PURE__ */ Symbol.for("@oconde/radar.fetch");
|
|
845
|
+
var REQUEST_ID_HEADER = "x-request-id";
|
|
846
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
847
|
+
var TELEGRAM_BOT = /^bot\d{5,}:[\w-]{20,}$/;
|
|
848
|
+
var TOKEN_LIKE = /^[\w\-.~+=]{20,}$/;
|
|
849
|
+
function secretSegment(segment) {
|
|
850
|
+
if (UUID.test(segment)) return false;
|
|
851
|
+
if (TELEGRAM_BOT.test(segment) || looksLikeCredential(segment)) return true;
|
|
852
|
+
return TOKEN_LIKE.test(segment) && /[A-Za-z]/.test(segment) && /\d/.test(segment);
|
|
853
|
+
}
|
|
854
|
+
function outboundPath(pathname, mode) {
|
|
855
|
+
if (mode === "none") return pathname;
|
|
856
|
+
return pathname.split("/").map((segment) => {
|
|
857
|
+
let decoded = segment;
|
|
858
|
+
try {
|
|
859
|
+
decoded = decodeURIComponent(segment);
|
|
860
|
+
} catch {
|
|
861
|
+
decoded = segment;
|
|
862
|
+
}
|
|
863
|
+
return secretSegment(decoded) ? maskValue(decoded, false) : segment;
|
|
864
|
+
}).join("/");
|
|
865
|
+
}
|
|
866
|
+
function failureOf(error) {
|
|
867
|
+
const message = describeError(error);
|
|
868
|
+
const cause = error instanceof Error ? error.cause : void 0;
|
|
869
|
+
const detail = typeof cause?.code === "string" ? cause.code : typeof cause?.message === "string" ? cause.message : "";
|
|
870
|
+
return detail && !message.includes(detail) ? `${message} (${detail})` : message;
|
|
871
|
+
}
|
|
872
|
+
function urlOf(input) {
|
|
873
|
+
try {
|
|
874
|
+
if (typeof input === "string") return new URL(input);
|
|
875
|
+
if (input instanceof URL) return input;
|
|
876
|
+
return new URL(input.url);
|
|
877
|
+
} catch {
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
function originOf(value) {
|
|
882
|
+
try {
|
|
883
|
+
return new URL(value).origin;
|
|
884
|
+
} catch {
|
|
885
|
+
return null;
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
function targets(url, target) {
|
|
889
|
+
if (target instanceof RegExp) return target.test(url.host);
|
|
890
|
+
const wanted = target.trim().toLowerCase();
|
|
891
|
+
if (wanted.startsWith(".")) return url.hostname.toLowerCase().endsWith(wanted);
|
|
892
|
+
return url.host.toLowerCase() === wanted || url.hostname.toLowerCase() === wanted;
|
|
893
|
+
}
|
|
894
|
+
function isPlainObject(value) {
|
|
895
|
+
if (value === null || typeof value !== "object") return false;
|
|
896
|
+
const prototype = Object.getPrototypeOf(value);
|
|
897
|
+
return prototype === Object.prototype || prototype === null;
|
|
898
|
+
}
|
|
899
|
+
function withRequestId(input, init, requestId) {
|
|
900
|
+
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
|
|
901
|
+
if (headers.has(REQUEST_ID_HEADER)) return [input, init];
|
|
902
|
+
headers.set(REQUEST_ID_HEADER, requestId);
|
|
903
|
+
if (init === void 0 || isPlainObject(init)) return [input, { ...init, headers }];
|
|
904
|
+
return [new Request(input, init), { headers }];
|
|
905
|
+
}
|
|
906
|
+
function traceFetch(config) {
|
|
907
|
+
const current = globalThis.fetch;
|
|
908
|
+
if (typeof current !== "function" || current[WRAPPED]) return () => void 0;
|
|
909
|
+
const original = current;
|
|
910
|
+
const radarOrigin = originOf(config.endpoint);
|
|
911
|
+
const traced = async (input, init) => {
|
|
912
|
+
const url = urlOf(input);
|
|
913
|
+
if (!url || url.origin === radarOrigin || url.protocol !== "http:" && url.protocol !== "https:") return original(input, init);
|
|
914
|
+
const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
915
|
+
const context = currentContext();
|
|
916
|
+
const [nextInput, nextInit] = context && config.propagateTo.some((target) => targets(url, target)) ? withRequestId(input, init, context.requestId) : [input, init];
|
|
917
|
+
const attrs = { method, host: url.host, path: outboundPath(url.pathname, config.redact) };
|
|
918
|
+
const started = performance.now();
|
|
919
|
+
try {
|
|
920
|
+
const response = await original(nextInput, nextInit);
|
|
921
|
+
config.record(response.status >= 500 ? "error" : response.status >= 400 ? "warn" : "info", { ...attrs, status: response.status, durationMs: elapsedSince(started) });
|
|
922
|
+
return response;
|
|
923
|
+
} catch (error) {
|
|
924
|
+
config.record("error", { ...attrs, error: failureOf(error), durationMs: elapsedSince(started) });
|
|
925
|
+
throw error;
|
|
926
|
+
}
|
|
927
|
+
};
|
|
928
|
+
Object.defineProperty(traced, WRAPPED, { value: original });
|
|
929
|
+
globalThis.fetch = traced;
|
|
930
|
+
return () => {
|
|
931
|
+
if (globalThis.fetch === traced) globalThis.fetch = original;
|
|
932
|
+
};
|
|
649
933
|
}
|
|
650
934
|
|
|
651
935
|
// src/transport.ts
|
|
936
|
+
import { promisify } from "util";
|
|
937
|
+
import { gzip } from "zlib";
|
|
652
938
|
var gzipAsync = promisify(gzip);
|
|
653
939
|
var MAX_BATCH_BYTES = 9e5;
|
|
654
940
|
var GZIP_THRESHOLD_BYTES = 8192;
|
|
@@ -671,6 +957,7 @@ var Transport = class {
|
|
|
671
957
|
inFlight = null;
|
|
672
958
|
nextAttemptAt = 0;
|
|
673
959
|
backoffMs = 0;
|
|
960
|
+
unauthorizedUntil = 0;
|
|
674
961
|
stopped = false;
|
|
675
962
|
warned = /* @__PURE__ */ new Set();
|
|
676
963
|
timer;
|
|
@@ -760,10 +1047,8 @@ var Transport = class {
|
|
|
760
1047
|
this.nextAttemptAt = Date.now() + Math.max(this.backoffMs, outcome.retryAfterMs ?? 0);
|
|
761
1048
|
return false;
|
|
762
1049
|
case "unauthorized":
|
|
763
|
-
this.droppedCount += events.length
|
|
764
|
-
this.
|
|
765
|
-
this.nextAttemptAt = Date.now() + UNAUTHORIZED_PAUSE_MS;
|
|
766
|
-
this.warn("unauthorized", `chave do Radar recusada (${outcome.status}); envios pausados por 10 minutos`, true);
|
|
1050
|
+
this.droppedCount += events.length;
|
|
1051
|
+
this.pauseUnauthorized(outcome.status);
|
|
767
1052
|
return false;
|
|
768
1053
|
case "rejected":
|
|
769
1054
|
this.droppedCount += events.length;
|
|
@@ -771,6 +1056,47 @@ var Transport = class {
|
|
|
771
1056
|
return true;
|
|
772
1057
|
}
|
|
773
1058
|
}
|
|
1059
|
+
async checkIn(slug, payload) {
|
|
1060
|
+
if (this.stopped || !this.config.checkInBaseUrl || Date.now() < this.unauthorizedUntil) return;
|
|
1061
|
+
let response;
|
|
1062
|
+
try {
|
|
1063
|
+
response = await fetch(this.config.checkInBaseUrl + encodeURIComponent(slug), {
|
|
1064
|
+
method: "POST",
|
|
1065
|
+
headers: this.headers(),
|
|
1066
|
+
body: JSON.stringify(payload),
|
|
1067
|
+
redirect: "error",
|
|
1068
|
+
signal: AbortSignal.timeout(this.config.requestTimeoutMs ?? 5e3)
|
|
1069
|
+
});
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
this.warn("checkin_network", `check-in no Radar falhou: ${describeError(error)}`, false);
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
await response.arrayBuffer().catch(() => void 0);
|
|
1075
|
+
if (response.ok) return;
|
|
1076
|
+
if (response.status === 401 || response.status === 403) {
|
|
1077
|
+
this.pauseUnauthorized(response.status);
|
|
1078
|
+
return;
|
|
1079
|
+
}
|
|
1080
|
+
if (response.status === 404) {
|
|
1081
|
+
this.warn(`checkin_unknown_${slug}`, `tarefa agendada "${slug}" n\xE3o existe no projeto da chave; crie o monitor com esse slug no Radar`, true);
|
|
1082
|
+
return;
|
|
1083
|
+
}
|
|
1084
|
+
this.warn(`checkin_rejected_${response.status}`, `check-in recusado pelo Radar (${response.status})`, false);
|
|
1085
|
+
}
|
|
1086
|
+
pauseUnauthorized(status) {
|
|
1087
|
+
this.droppedCount += this.queue.length;
|
|
1088
|
+
this.queue = [];
|
|
1089
|
+
this.unauthorizedUntil = Date.now() + UNAUTHORIZED_PAUSE_MS;
|
|
1090
|
+
this.nextAttemptAt = Math.max(this.nextAttemptAt, this.unauthorizedUntil);
|
|
1091
|
+
this.warn("unauthorized", `chave do Radar recusada (${status}); envios pausados por 10 minutos`, true);
|
|
1092
|
+
}
|
|
1093
|
+
headers() {
|
|
1094
|
+
return {
|
|
1095
|
+
"content-type": "application/json",
|
|
1096
|
+
authorization: `Bearer ${this.config.key}`,
|
|
1097
|
+
"user-agent": `${this.config.sdk.name}/${this.config.sdk.version}`
|
|
1098
|
+
};
|
|
1099
|
+
}
|
|
774
1100
|
requeue(events) {
|
|
775
1101
|
this.queue.unshift(...events);
|
|
776
1102
|
const overflow = this.queue.length - this.maxQueue;
|
|
@@ -782,11 +1108,7 @@ var Transport = class {
|
|
|
782
1108
|
async post(batch) {
|
|
783
1109
|
const json = JSON.stringify(batch);
|
|
784
1110
|
const compress = Buffer.byteLength(json) > GZIP_THRESHOLD_BYTES;
|
|
785
|
-
const headers =
|
|
786
|
-
"content-type": "application/json",
|
|
787
|
-
authorization: `Bearer ${this.config.key}`,
|
|
788
|
-
"user-agent": `${this.config.sdk.name}/${this.config.sdk.version}`
|
|
789
|
-
};
|
|
1111
|
+
const headers = this.headers();
|
|
790
1112
|
if (compress) headers["content-encoding"] = "gzip";
|
|
791
1113
|
let response;
|
|
792
1114
|
try {
|
|
@@ -840,15 +1162,19 @@ function withTimeout(promise, ms) {
|
|
|
840
1162
|
|
|
841
1163
|
// src/version.ts
|
|
842
1164
|
var SDK_NAME = "@oconde/radar";
|
|
843
|
-
var SDK_VERSION = true ? "0.
|
|
1165
|
+
var SDK_VERSION = true ? "0.3.0" : "0.0.0-dev";
|
|
844
1166
|
|
|
845
1167
|
// src/client.ts
|
|
1168
|
+
var MAX_CHECK_IN_DURATION_MS = 7 * 864e5;
|
|
1169
|
+
var MAX_CHECK_IN_ENVIRONMENT = 64;
|
|
1170
|
+
var MAX_CHECK_IN_RELEASE = 128;
|
|
846
1171
|
var RadarClient = class {
|
|
847
1172
|
options = resolveOptions({}, {});
|
|
848
1173
|
transport = null;
|
|
849
1174
|
initialized = false;
|
|
850
1175
|
closed = false;
|
|
851
1176
|
captured = /* @__PURE__ */ new WeakSet();
|
|
1177
|
+
restoreFetch = null;
|
|
852
1178
|
get isEnabled() {
|
|
853
1179
|
return this.transport !== null && !this.closed;
|
|
854
1180
|
}
|
|
@@ -871,10 +1197,19 @@ var RadarClient = class {
|
|
|
871
1197
|
sdk: { name: SDK_NAME, version: SDK_VERSION },
|
|
872
1198
|
environment: this.options.environment,
|
|
873
1199
|
...this.options.release ? { release: this.options.release } : {},
|
|
1200
|
+
checkInBaseUrl: this.options.endpoint + CHECKIN_PATH,
|
|
874
1201
|
onWarning: (message, always) => always ? this.writeWarning(message) : this.debugWarn(message)
|
|
875
1202
|
});
|
|
876
1203
|
}
|
|
877
1204
|
if (this.options.captureUnhandled && (this.transport || this.options.console)) installProcessHandlers(this);
|
|
1205
|
+
if (this.options.traceFetch.enabled && (this.transport || this.options.console)) {
|
|
1206
|
+
this.restoreFetch = traceFetch({
|
|
1207
|
+
endpoint: this.options.endpoint,
|
|
1208
|
+
propagateTo: this.options.traceFetch.propagateTo,
|
|
1209
|
+
redact: this.options.redact,
|
|
1210
|
+
record: (level, attrs) => this.safely(() => this.emitLog(level, "http.client", attrs))
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
878
1213
|
} catch (error) {
|
|
879
1214
|
this.debugWarn(`radar.init falhou: ${describeError(error)}`);
|
|
880
1215
|
}
|
|
@@ -918,7 +1253,7 @@ var RadarClient = class {
|
|
|
918
1253
|
}
|
|
919
1254
|
async track(name, fn, data) {
|
|
920
1255
|
const execute = async () => {
|
|
921
|
-
const started =
|
|
1256
|
+
const started = performance4.now();
|
|
922
1257
|
try {
|
|
923
1258
|
const result = await fn();
|
|
924
1259
|
this.info(name, { ...data, durationMs: elapsedSince(started), ok: true });
|
|
@@ -930,7 +1265,52 @@ var RadarClient = class {
|
|
|
930
1265
|
}
|
|
931
1266
|
};
|
|
932
1267
|
if (currentContext()) return execute();
|
|
933
|
-
return runWithContext({ requestId: randomUUID2(), startedAt:
|
|
1268
|
+
return runWithContext({ requestId: randomUUID2(), startedAt: performance4.now() }, execute);
|
|
1269
|
+
}
|
|
1270
|
+
withContext(fn, options = {}) {
|
|
1271
|
+
return runWithContext({ requestId: requestIdFrom(options.requestId), startedAt: performance4.now() }, fn);
|
|
1272
|
+
}
|
|
1273
|
+
async checkIn(slug, status, options = {}) {
|
|
1274
|
+
try {
|
|
1275
|
+
if (!this.transport || this.closed) return;
|
|
1276
|
+
if (typeof slug !== "string" || !CHECKIN_SLUG.test(slug)) {
|
|
1277
|
+
this.debugWarn(`slug de tarefa agendada inv\xE1lido: ${String(slug).slice(0, 80)}`);
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
if (!CHECKIN_STATUSES.includes(status)) return;
|
|
1281
|
+
const checkInId = typeof options.checkInId === "string" && options.checkInId ? options.checkInId.slice(0, LIMITS.checkInIdLength) : void 0;
|
|
1282
|
+
const durationMs = typeof options.durationMs === "number" && Number.isFinite(options.durationMs) && options.durationMs >= 0 ? Math.min(Math.round(options.durationMs), MAX_CHECK_IN_DURATION_MS) : void 0;
|
|
1283
|
+
const environment = this.options.environment.slice(0, MAX_CHECK_IN_ENVIRONMENT) || "production";
|
|
1284
|
+
const release = this.options.release?.slice(0, MAX_CHECK_IN_RELEASE);
|
|
1285
|
+
const payload = {
|
|
1286
|
+
status,
|
|
1287
|
+
...checkInId ? { checkInId } : {},
|
|
1288
|
+
...durationMs !== void 0 ? { durationMs } : {},
|
|
1289
|
+
environment,
|
|
1290
|
+
...release ? { release } : {}
|
|
1291
|
+
};
|
|
1292
|
+
await this.transport.checkIn(slug, payload);
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
this.debugWarn(`check-in falhou: ${describeError(error)}`);
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
async cron(slug, fn) {
|
|
1298
|
+
const checkInId = randomUUID2();
|
|
1299
|
+
const started = performance4.now();
|
|
1300
|
+
const execute = async () => {
|
|
1301
|
+
await this.checkIn(slug, "in_progress", { checkInId });
|
|
1302
|
+
try {
|
|
1303
|
+
const result = await fn();
|
|
1304
|
+
await this.checkIn(slug, "ok", { checkInId, durationMs: elapsedSince(started) });
|
|
1305
|
+
return result;
|
|
1306
|
+
} catch (error) {
|
|
1307
|
+
this.captureError(error, { cron: slug });
|
|
1308
|
+
await this.checkIn(slug, "error", { checkInId, durationMs: elapsedSince(started) });
|
|
1309
|
+
throw error;
|
|
1310
|
+
}
|
|
1311
|
+
};
|
|
1312
|
+
if (currentContext()) return execute();
|
|
1313
|
+
return this.withContext(execute);
|
|
934
1314
|
}
|
|
935
1315
|
middleware() {
|
|
936
1316
|
return createMiddleware(this);
|
|
@@ -949,7 +1329,7 @@ var RadarClient = class {
|
|
|
949
1329
|
() => this.emitLog(level, "http.request", {
|
|
950
1330
|
method: (req.method ?? "GET").toUpperCase(),
|
|
951
1331
|
...route ? { route } : {},
|
|
952
|
-
...this.options.requestDetail === "full" ? { path: redactPath(requestPath(req), req
|
|
1332
|
+
...this.options.requestDetail === "full" ? { path: redactPath(requestPath(req), pathParams(req, requestPath(req)), this.options.redact) } : {},
|
|
953
1333
|
status,
|
|
954
1334
|
durationMs: elapsedSince(context.startedAt)
|
|
955
1335
|
})
|
|
@@ -966,6 +1346,8 @@ var RadarClient = class {
|
|
|
966
1346
|
async close(timeoutMs = 2e3) {
|
|
967
1347
|
if (this.closed) return;
|
|
968
1348
|
await this.flush(timeoutMs);
|
|
1349
|
+
this.restoreFetch?.();
|
|
1350
|
+
this.restoreFetch = null;
|
|
969
1351
|
this.closed = true;
|
|
970
1352
|
this.transport?.stop();
|
|
971
1353
|
}
|
|
@@ -986,31 +1368,40 @@ var RadarClient = class {
|
|
|
986
1368
|
this.print(event);
|
|
987
1369
|
this.transport?.enqueue(event);
|
|
988
1370
|
}
|
|
1371
|
+
captureException(exception, options = {}) {
|
|
1372
|
+
this.safely(() => this.emitException(exception, options));
|
|
1373
|
+
}
|
|
989
1374
|
emitError(error, data, level, handled) {
|
|
990
1375
|
if (this.closed) return;
|
|
991
1376
|
if (typeof error === "object" && error !== null) {
|
|
992
1377
|
if (this.captured.has(error)) return;
|
|
993
1378
|
this.captured.add(error);
|
|
994
1379
|
}
|
|
1380
|
+
this.emitException(exceptionInfo(error), { level, handled, ...data !== void 0 ? { attrs: data } : {} }, error instanceof Error ? error.stack : void 0);
|
|
1381
|
+
}
|
|
1382
|
+
emitException(exception, options, stack) {
|
|
1383
|
+
if (this.closed) return;
|
|
995
1384
|
const context = currentContext();
|
|
996
1385
|
const mode = this.options.redact;
|
|
1386
|
+
const request = options.request === null ? void 0 : options.request ?? (context?.req ? requestInfo(context.req, mode, { route: context.route }, this.options.requestDetail) : void 0);
|
|
1387
|
+
const runtime = options.runtime === void 0 ? runtimeInfo() : options.runtime;
|
|
997
1388
|
const event = {
|
|
998
1389
|
type: "error",
|
|
999
1390
|
ts: Date.now(),
|
|
1000
|
-
level,
|
|
1001
|
-
handled,
|
|
1002
|
-
exception
|
|
1391
|
+
level: options.level ?? "error",
|
|
1392
|
+
handled: options.handled ?? true,
|
|
1393
|
+
exception,
|
|
1003
1394
|
...context ? { requestId: context.requestId } : {},
|
|
1004
|
-
...
|
|
1395
|
+
...request ? { request } : {},
|
|
1005
1396
|
...context?.user ? { user: context.user } : {},
|
|
1006
|
-
runtime:
|
|
1007
|
-
...
|
|
1397
|
+
...runtime ? { runtime } : {},
|
|
1398
|
+
...options.attrs !== void 0 ? { attrs: prepareAttrs(options.attrs, mode) } : {}
|
|
1008
1399
|
};
|
|
1009
|
-
this.print(event,
|
|
1400
|
+
this.print(event, stack);
|
|
1010
1401
|
if (!this.transport) return;
|
|
1011
1402
|
this.transport.enqueue(
|
|
1012
|
-
() => enrichException(event.exception).then(
|
|
1013
|
-
(
|
|
1403
|
+
() => enrichException(event.exception, process.cwd(), options.enrich).then(
|
|
1404
|
+
(enriched) => ({ ...event, exception: enriched }),
|
|
1014
1405
|
() => event
|
|
1015
1406
|
)
|
|
1016
1407
|
);
|