@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/nest/index.cjs
CHANGED
|
@@ -50,6 +50,33 @@ var import_rxjs = require("rxjs");
|
|
|
50
50
|
|
|
51
51
|
// src/context.ts
|
|
52
52
|
var import_node_async_hooks = require("async_hooks");
|
|
53
|
+
var import_node_crypto = require("crypto");
|
|
54
|
+
|
|
55
|
+
// src/protocol/index.ts
|
|
56
|
+
var INGEST_PATH = "/api/v1/events";
|
|
57
|
+
var CHECKIN_PATH = "/api/v1/checkins/";
|
|
58
|
+
var CHECKIN_SLUG = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
59
|
+
var CHECKIN_STATUSES = ["in_progress", "ok", "error"];
|
|
60
|
+
var LIMITS = {
|
|
61
|
+
batchBytes: 1e6,
|
|
62
|
+
batchEvents: 500,
|
|
63
|
+
messageLength: 2e3,
|
|
64
|
+
stringLength: 2e3,
|
|
65
|
+
attrsBytes: 16384,
|
|
66
|
+
bodyBytes: 16384,
|
|
67
|
+
headers: 50,
|
|
68
|
+
frames: 50,
|
|
69
|
+
contextFrames: 10,
|
|
70
|
+
contextLines: 5,
|
|
71
|
+
contextLineLength: 300,
|
|
72
|
+
causeDepth: 5,
|
|
73
|
+
depth: 6,
|
|
74
|
+
arrayItems: 50,
|
|
75
|
+
requestIdLength: 128,
|
|
76
|
+
checkInIdLength: 64
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// src/context.ts
|
|
53
80
|
var STATE_KEY = /* @__PURE__ */ Symbol.for("@oconde/radar");
|
|
54
81
|
function globalState() {
|
|
55
82
|
const holder = globalThis;
|
|
@@ -66,6 +93,11 @@ function currentContext() {
|
|
|
66
93
|
function runWithContext(context, fn) {
|
|
67
94
|
return globalState().storage.run(context, fn);
|
|
68
95
|
}
|
|
96
|
+
var SAFE_REQUEST_ID = /^[\x21-\x7e]+$/;
|
|
97
|
+
function requestIdFrom(incoming) {
|
|
98
|
+
const value = incoming?.trim();
|
|
99
|
+
return value && value.length <= LIMITS.requestIdLength && SAFE_REQUEST_ID.test(value) ? value : (0, import_node_crypto.randomUUID)();
|
|
100
|
+
}
|
|
69
101
|
|
|
70
102
|
// src/client.ts
|
|
71
103
|
var import_node_crypto3 = require("crypto");
|
|
@@ -73,31 +105,10 @@ var import_node_os = require("os");
|
|
|
73
105
|
var import_node_perf_hooks3 = require("perf_hooks");
|
|
74
106
|
|
|
75
107
|
// src/express.ts
|
|
76
|
-
var import_node_crypto2 = require("crypto");
|
|
77
108
|
var import_node_perf_hooks = require("perf_hooks");
|
|
78
109
|
|
|
79
|
-
// src/protocol/index.ts
|
|
80
|
-
var INGEST_PATH = "/api/v1/events";
|
|
81
|
-
var LIMITS = {
|
|
82
|
-
batchBytes: 1e6,
|
|
83
|
-
batchEvents: 500,
|
|
84
|
-
messageLength: 2e3,
|
|
85
|
-
stringLength: 2e3,
|
|
86
|
-
attrsBytes: 16384,
|
|
87
|
-
bodyBytes: 16384,
|
|
88
|
-
headers: 50,
|
|
89
|
-
frames: 50,
|
|
90
|
-
contextFrames: 10,
|
|
91
|
-
contextLines: 5,
|
|
92
|
-
contextLineLength: 300,
|
|
93
|
-
causeDepth: 5,
|
|
94
|
-
depth: 6,
|
|
95
|
-
arrayItems: 50,
|
|
96
|
-
requestIdLength: 128
|
|
97
|
-
};
|
|
98
|
-
|
|
99
110
|
// src/redact.ts
|
|
100
|
-
var
|
|
111
|
+
var import_node_crypto2 = require("crypto");
|
|
101
112
|
var SECRET_WORDS = /* @__PURE__ */ new Set([
|
|
102
113
|
"pass",
|
|
103
114
|
"passwd",
|
|
@@ -143,16 +154,16 @@ function fingerprintSlot() {
|
|
|
143
154
|
const holder = globalThis;
|
|
144
155
|
let slot = holder[KEY_SLOT];
|
|
145
156
|
if (!slot) {
|
|
146
|
-
slot = { key: (0,
|
|
157
|
+
slot = { key: (0, import_node_crypto2.randomBytes)(32) };
|
|
147
158
|
holder[KEY_SLOT] = slot;
|
|
148
159
|
}
|
|
149
160
|
return slot;
|
|
150
161
|
}
|
|
151
162
|
function setFingerprintSecret(secret) {
|
|
152
|
-
fingerprintSlot().key = secret ? (0,
|
|
163
|
+
fingerprintSlot().key = secret ? (0, import_node_crypto2.createHash)("sha256").update(`@oconde/radar:${secret}`).digest() : (0, import_node_crypto2.randomBytes)(32);
|
|
153
164
|
}
|
|
154
165
|
function fingerprint(value) {
|
|
155
|
-
return (0,
|
|
166
|
+
return (0, import_node_crypto2.createHmac)("sha256", fingerprintSlot().key).update(value).digest("hex").slice(0, 8);
|
|
156
167
|
}
|
|
157
168
|
function words(name) {
|
|
158
169
|
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
@@ -294,7 +305,44 @@ function requestPath(req) {
|
|
|
294
305
|
}
|
|
295
306
|
function requestRoute(req, route) {
|
|
296
307
|
if (route) return route;
|
|
297
|
-
|
|
308
|
+
if (typeof req.route?.path !== "string") return void 0;
|
|
309
|
+
const base = typeof req.baseUrl === "string" ? req.baseUrl.replace(/\/+$/, "") : "";
|
|
310
|
+
if (!base) return req.route.path;
|
|
311
|
+
return req.route.path === "/" ? base : `${base}${req.route.path}`;
|
|
312
|
+
}
|
|
313
|
+
var ROUTE_PARAM = /^:([A-Za-z_$][\w$]*)/;
|
|
314
|
+
function pathSegments(value) {
|
|
315
|
+
const segments = value.split("/");
|
|
316
|
+
while (segments.length > 1 && segments[segments.length - 1] === "") segments.pop();
|
|
317
|
+
return segments;
|
|
318
|
+
}
|
|
319
|
+
function decodeSegment(value) {
|
|
320
|
+
try {
|
|
321
|
+
return decodeURIComponent(value);
|
|
322
|
+
} catch {
|
|
323
|
+
return value;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function routeParams(req, path) {
|
|
327
|
+
if (typeof req.route?.path !== "string") return {};
|
|
328
|
+
const pattern = pathSegments(`${typeof req.baseUrl === "string" ? req.baseUrl : ""}${req.route.path}`).filter((segment, index) => index > 0 || segment !== "");
|
|
329
|
+
const values = pathSegments(path);
|
|
330
|
+
if (pattern.length > values.length) return {};
|
|
331
|
+
const offset = values.length - pattern.length;
|
|
332
|
+
const result = {};
|
|
333
|
+
for (let index = 0; index < pattern.length; index++) {
|
|
334
|
+
const value = values[offset + index];
|
|
335
|
+
const param = ROUTE_PARAM.exec(pattern[index]);
|
|
336
|
+
if (!param) {
|
|
337
|
+
if (pattern[index] !== value) return {};
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
if (value) result[param[1]] = decodeSegment(value);
|
|
341
|
+
}
|
|
342
|
+
return result;
|
|
343
|
+
}
|
|
344
|
+
function pathParams(req, path) {
|
|
345
|
+
return { ...routeParams(req, path), ...req.params ?? {} };
|
|
298
346
|
}
|
|
299
347
|
function redactPath(path, params, mode) {
|
|
300
348
|
if (mode === "none" || !params) return path;
|
|
@@ -311,7 +359,7 @@ function safeUrl(req, mode) {
|
|
|
311
359
|
const queryStart = url.indexOf("?");
|
|
312
360
|
const path = queryStart >= 0 ? url.slice(0, queryStart) : url;
|
|
313
361
|
const query = queryStart >= 0 ? url.slice(queryStart) : "";
|
|
314
|
-
return redactUrl(redactPath(path, req
|
|
362
|
+
return redactUrl(redactPath(path, pathParams(req, path), mode) + query, mode);
|
|
315
363
|
}
|
|
316
364
|
function redactUrl(url, mode) {
|
|
317
365
|
const queryStart = url.indexOf("?");
|
|
@@ -379,8 +427,7 @@ function createMiddleware(logger) {
|
|
|
379
427
|
return (req, res, next) => {
|
|
380
428
|
let context;
|
|
381
429
|
try {
|
|
382
|
-
const
|
|
383
|
-
const requestId = incoming && incoming.length <= LIMITS.requestIdLength ? incoming : (0, import_node_crypto2.randomUUID)();
|
|
430
|
+
const requestId = requestIdFrom(headerValue(req.headers["x-request-id"]));
|
|
384
431
|
context = { requestId, startedAt: import_node_perf_hooks.performance.now(), req };
|
|
385
432
|
res.setHeader("x-request-id", requestId);
|
|
386
433
|
if (logger.shouldLogRequest(req)) res.once("finish", () => logger.logHttpRequest(context, res.statusCode));
|
|
@@ -395,6 +442,12 @@ function createMiddleware(logger) {
|
|
|
395
442
|
// src/options.ts
|
|
396
443
|
var DEFAULT_ENDPOINT = "https://radar-ingest.oconde.dev";
|
|
397
444
|
var LEVELS = ["debug", "info", "warn", "error"];
|
|
445
|
+
function resolveTraceFetch(value) {
|
|
446
|
+
if (!value) return { enabled: false, propagateTo: [] };
|
|
447
|
+
if (value === true) return { enabled: true, propagateTo: [] };
|
|
448
|
+
const targets2 = Array.isArray(value.propagateTo) ? value.propagateTo : [];
|
|
449
|
+
return { enabled: true, propagateTo: targets2.filter((target) => typeof target === "string" ? target.trim() !== "" : target instanceof RegExp) };
|
|
450
|
+
}
|
|
398
451
|
function resolveOptions(options = {}, env = process.env) {
|
|
399
452
|
const key = options.key?.trim() || void 0;
|
|
400
453
|
return {
|
|
@@ -410,6 +463,7 @@ function resolveOptions(options = {}, env = process.env) {
|
|
|
410
463
|
logRequests: options.logRequests ?? true,
|
|
411
464
|
ignorePaths: options.ignorePaths ?? ["/healthz", "/health"],
|
|
412
465
|
captureUnhandled: options.captureUnhandled ?? true,
|
|
466
|
+
traceFetch: resolveTraceFetch(options.traceFetch),
|
|
413
467
|
debug: options.debug ?? false
|
|
414
468
|
};
|
|
415
469
|
}
|
|
@@ -471,16 +525,144 @@ var import_promises = require("fs/promises");
|
|
|
471
525
|
var import_node_module = require("module");
|
|
472
526
|
var import_node_path = require("path");
|
|
473
527
|
var import_node_url = require("url");
|
|
528
|
+
|
|
529
|
+
// src/function-name.ts
|
|
530
|
+
var IDENTIFIER = "[A-Za-z_$][\\w$]*";
|
|
531
|
+
var MAX_LINES_UP = 400;
|
|
532
|
+
var MAX_HEADER_LINES = 6;
|
|
533
|
+
var MAX_NAME_LENGTH = 100;
|
|
534
|
+
var MAX_COMMENT_LINES = 40;
|
|
535
|
+
var CONTROL_BLOCK = /^\s*(?:\}\s*)?(?:if|else|for|while|do|switch|try|catch|finally|with)\b/;
|
|
536
|
+
var CLASS_BODY = new RegExp(`\\bclass\\s+${IDENTIFIER}`);
|
|
537
|
+
var MARKUP_BOUNDARY = /<\/?(?:script|style|template)\b/i;
|
|
538
|
+
var KEYWORDS = /* @__PURE__ */ new Set(["if", "for", "while", "switch", "catch", "function", "return", "new", "typeof", "await", "else", "do", "try"]);
|
|
539
|
+
var NAME_PATTERNS = [
|
|
540
|
+
new RegExp(`\\bfunction\\s*\\*?\\s*(${IDENTIFIER})\\s*\\(`),
|
|
541
|
+
new RegExp(`(${IDENTIFIER})\\s*[:=]\\s*(?:async\\s+)?function\\b`),
|
|
542
|
+
new RegExp(`(${IDENTIFIER})\\s*(?::\\s*[^=]+)?=\\s*(?:async\\s+)?(?:\\([^]*\\)|${IDENTIFIER})\\s*(?::\\s*[^=]+)?=>\\s*$`),
|
|
543
|
+
new RegExp(`(?:^|[{,(]\\s*)(${IDENTIFIER})\\s*:\\s*(?:async\\s+)?(?:\\([^]*\\)|${IDENTIFIER})\\s*(?::\\s*[^=]+)?=>\\s*$`)
|
|
544
|
+
];
|
|
545
|
+
var METHOD_PATTERN = new RegExp(`^\\s*(?:(?:export|default|public|private|protected|static|async|override|readonly|get|set)\\s+)*\\*?\\s*(${IDENTIFIER})\\s*(?:<[^>]*>)?\\s*\\([^]*\\)\\s*(?::\\s*[^{]+)?$`);
|
|
546
|
+
var ANONYMOUS_FUNCTION = /\bfunction\b|=>/;
|
|
547
|
+
var STRING_LITERAL = /(["'`])(?:\\.|(?!\1)[^\\])*\1/g;
|
|
548
|
+
function lineCommentAt(text, from) {
|
|
549
|
+
for (let index = text.indexOf("//", from); index >= 0; index = text.indexOf("//", index + 1)) {
|
|
550
|
+
if (index === 0 || text[index - 1] !== ":") return index;
|
|
551
|
+
}
|
|
552
|
+
return -1;
|
|
553
|
+
}
|
|
554
|
+
function blockCommentAt(text, from) {
|
|
555
|
+
for (let index = text.indexOf("/*", from); index >= 0; index = text.indexOf("/*", index + 1)) {
|
|
556
|
+
if (index === 0 || text[index - 1] !== "\\") return index;
|
|
557
|
+
}
|
|
558
|
+
return -1;
|
|
559
|
+
}
|
|
560
|
+
function stripComments(text, insideComment, closesLater) {
|
|
561
|
+
let code = "";
|
|
562
|
+
let position = 0;
|
|
563
|
+
let inside = insideComment;
|
|
564
|
+
while (position < text.length) {
|
|
565
|
+
if (inside) {
|
|
566
|
+
const end = text.indexOf("*/", position);
|
|
567
|
+
if (end < 0) return { code, insideComment: true };
|
|
568
|
+
inside = false;
|
|
569
|
+
position = end + 2;
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
const block = blockCommentAt(text, position);
|
|
573
|
+
const line = lineCommentAt(text, position);
|
|
574
|
+
if (line >= 0 && (block < 0 || line < block)) return { code: code + text.slice(position, line), insideComment: false };
|
|
575
|
+
if (block < 0) return { code: code + text.slice(position), insideComment: false };
|
|
576
|
+
if (text.indexOf("*/", block + 2) < 0 && !closesLater()) {
|
|
577
|
+
code += text.slice(position, block + 2);
|
|
578
|
+
position = block + 2;
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
code += text.slice(position, block);
|
|
582
|
+
inside = true;
|
|
583
|
+
position = block + 2;
|
|
584
|
+
}
|
|
585
|
+
return { code, insideComment: inside };
|
|
586
|
+
}
|
|
587
|
+
function codeLines(lines, from, start, column) {
|
|
588
|
+
const plain = lines.slice(from, start + 1).map((line) => line.replace(STRING_LITERAL, '""'));
|
|
589
|
+
const closesWithin = (index) => plain.slice(index + 1, index + 1 + MAX_COMMENT_LINES).some((line) => line.includes("*/"));
|
|
590
|
+
const result = [];
|
|
591
|
+
let insideComment = false;
|
|
592
|
+
for (let index = 0; index < plain.length; index++) {
|
|
593
|
+
const text = index === plain.length - 1 && column ? (lines[start] ?? "").slice(0, Math.max(0, column - 1)).replace(STRING_LITERAL, '""') : plain[index] ?? "";
|
|
594
|
+
const stripped = stripComments(text, insideComment, () => closesWithin(index));
|
|
595
|
+
insideComment = stripped.insideComment;
|
|
596
|
+
result.push(stripped.code);
|
|
597
|
+
}
|
|
598
|
+
return result;
|
|
599
|
+
}
|
|
600
|
+
function parenBalance(text) {
|
|
601
|
+
let balance = 0;
|
|
602
|
+
for (const char of text) {
|
|
603
|
+
if (char === "(") balance += 1;
|
|
604
|
+
else if (char === ")") balance -= 1;
|
|
605
|
+
}
|
|
606
|
+
return balance;
|
|
607
|
+
}
|
|
608
|
+
function headerOf(code, index, prefix) {
|
|
609
|
+
let header = prefix;
|
|
610
|
+
for (let previous = index - 1; previous >= 0 && previous >= index - MAX_HEADER_LINES; previous--) {
|
|
611
|
+
if (header.trim() !== "" && parenBalance(header) >= 0) break;
|
|
612
|
+
header = `${code[previous] ?? ""} ${header}`;
|
|
613
|
+
}
|
|
614
|
+
return header;
|
|
615
|
+
}
|
|
616
|
+
function nameIn(header) {
|
|
617
|
+
const patterns = ANONYMOUS_FUNCTION.test(header) ? NAME_PATTERNS : [...NAME_PATTERNS, METHOD_PATTERN];
|
|
618
|
+
for (const pattern of patterns) {
|
|
619
|
+
const name = pattern.exec(header)?.[1];
|
|
620
|
+
if (name && !KEYWORDS.has(name)) return name.slice(0, MAX_NAME_LENGTH);
|
|
621
|
+
}
|
|
622
|
+
return void 0;
|
|
623
|
+
}
|
|
624
|
+
function enclosingFunctionName(lines, line, column) {
|
|
625
|
+
const start = line - 1;
|
|
626
|
+
if (start < 0 || start >= lines.length) return void 0;
|
|
627
|
+
const from = Math.max(0, start - MAX_LINES_UP - MAX_HEADER_LINES);
|
|
628
|
+
const cleaned = codeLines(lines, from, start, column);
|
|
629
|
+
let depth = 0;
|
|
630
|
+
for (let index = start; index >= 0 && index >= start - MAX_LINES_UP; index--) {
|
|
631
|
+
if (index < start && MARKUP_BOUNDARY.test(lines[index] ?? "")) return void 0;
|
|
632
|
+
const code = cleaned[index - from] ?? "";
|
|
633
|
+
for (let position = code.length - 1; position >= 0; position--) {
|
|
634
|
+
const char = code[position];
|
|
635
|
+
if (char === "}") {
|
|
636
|
+
depth += 1;
|
|
637
|
+
} else if (char === "{") {
|
|
638
|
+
if (depth > 0) {
|
|
639
|
+
depth -= 1;
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
const header = headerOf(cleaned, index - from, code.slice(0, position));
|
|
643
|
+
if (CLASS_BODY.test(header)) return void 0;
|
|
644
|
+
if (CONTROL_BLOCK.test(header)) continue;
|
|
645
|
+
const name = nameIn(header);
|
|
646
|
+
if (name) return name;
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
return void 0;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// src/source.ts
|
|
474
654
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
475
655
|
var MAX_CACHED_FILES = 50;
|
|
476
656
|
var MAPPING_URL = /\/\/[#@]\s*sourceMappingURL=(\S+)\s*$/;
|
|
477
657
|
var cache = /* @__PURE__ */ new Map();
|
|
478
|
-
async function enrichFrames(frames, cwd = process.cwd()) {
|
|
658
|
+
async function enrichFrames(frames, cwd = process.cwd(), options = {}) {
|
|
479
659
|
const result = [];
|
|
480
660
|
let withContext = 0;
|
|
481
661
|
for (const frame of frames) {
|
|
482
662
|
try {
|
|
483
|
-
const { frame:
|
|
663
|
+
const { frame: located, lines, original } = await mapFrame(frame, options.mapLocator);
|
|
664
|
+
const named = options.inferFunctionNames && original && !located.fn && lines && located.line ? withInferredName(located, lines) : located;
|
|
665
|
+
const mapped = named.inApp && isLibraryPath(named.file) ? { ...named, inApp: false } : named;
|
|
484
666
|
let next = mapped;
|
|
485
667
|
if (mapped.inApp && lines && mapped.line && withContext < LIMITS.contextFrames) {
|
|
486
668
|
const context = contextFor(lines, mapped.line);
|
|
@@ -496,29 +678,36 @@ async function enrichFrames(frames, cwd = process.cwd()) {
|
|
|
496
678
|
}
|
|
497
679
|
return result;
|
|
498
680
|
}
|
|
499
|
-
async function enrichException(exception, cwd = process.cwd()) {
|
|
500
|
-
const frames = await enrichFrames(exception.frames, cwd);
|
|
681
|
+
async function enrichException(exception, cwd = process.cwd(), options = {}) {
|
|
682
|
+
const frames = await enrichFrames(exception.frames, cwd, options);
|
|
501
683
|
if (!exception.cause) return { ...exception, frames };
|
|
502
|
-
return { ...exception, frames, cause: await enrichException(exception.cause, cwd) };
|
|
684
|
+
return { ...exception, frames, cause: await enrichException(exception.cause, cwd, options) };
|
|
685
|
+
}
|
|
686
|
+
function withInferredName(frame, lines) {
|
|
687
|
+
const fn = enclosingFunctionName(lines, frame.line ?? 0, frame.col);
|
|
688
|
+
return fn ? { ...frame, fn } : frame;
|
|
689
|
+
}
|
|
690
|
+
function isLibraryPath(file) {
|
|
691
|
+
return file.includes("/node_modules/") || file.includes("\\node_modules\\");
|
|
503
692
|
}
|
|
504
693
|
function relativize(file, cwd) {
|
|
505
694
|
if (!(0, import_node_path.isAbsolute)(file)) return file;
|
|
506
695
|
const path = (0, import_node_path.relative)(cwd, file);
|
|
507
696
|
return path.startsWith("..") || (0, import_node_path.isAbsolute)(path) ? file : path.split(import_node_path.sep).join("/");
|
|
508
697
|
}
|
|
509
|
-
async function mapFrame(frame) {
|
|
510
|
-
if (!frame.line || !(0, import_node_path.isAbsolute)(frame.file)) return { frame, lines: null };
|
|
511
|
-
const file = await loadFile(frame.file);
|
|
512
|
-
if (!file) return { frame, lines: null };
|
|
513
|
-
if (!file.map) return { frame, lines: file.lines };
|
|
698
|
+
async function mapFrame(frame, locator) {
|
|
699
|
+
if (!frame.line || !(0, import_node_path.isAbsolute)(frame.file)) return { frame, lines: null, original: false };
|
|
700
|
+
const file = await loadFile(frame.file, locator);
|
|
701
|
+
if (!file) return { frame, lines: null, original: false };
|
|
702
|
+
if (!file.map) return { frame, lines: file.lines, original: false };
|
|
514
703
|
const entry = file.map.map.findEntry(frame.line - 1, (frame.col ?? 1) - 1);
|
|
515
|
-
if (typeof entry.originalSource !== "string" || typeof entry.originalLine !== "number") return { frame, lines: file.lines };
|
|
704
|
+
if (typeof entry.originalSource !== "string" || typeof entry.originalLine !== "number") return { frame, lines: file.lines, original: false };
|
|
516
705
|
const originalPath = resolveSource(file.map, entry.originalSource);
|
|
517
706
|
const mapped = { ...frame, file: originalPath, line: entry.originalLine + 1, col: (entry.originalColumn ?? 0) + 1 };
|
|
518
707
|
const lines = embeddedLines(file.map, file.map.sources.indexOf(entry.originalSource));
|
|
519
|
-
if (lines) return { frame: mapped, lines };
|
|
708
|
+
if (lines) return { frame: mapped, lines, original: true };
|
|
520
709
|
const original = await loadFile(originalPath);
|
|
521
|
-
return { frame: mapped, lines: original?.lines ?? null };
|
|
710
|
+
return { frame: mapped, lines: original?.lines ?? null, original: true };
|
|
522
711
|
}
|
|
523
712
|
function embeddedLines(map, index) {
|
|
524
713
|
if (index < 0) return null;
|
|
@@ -550,26 +739,27 @@ function resolveSource(map, source) {
|
|
|
550
739
|
}
|
|
551
740
|
return (0, import_node_path.isAbsolute)(source) ? source : (0, import_node_path.resolve)(map.baseDir, source);
|
|
552
741
|
}
|
|
553
|
-
function loadFile(path) {
|
|
554
|
-
const
|
|
742
|
+
function loadFile(path, locator) {
|
|
743
|
+
const key = locator ? `${path}\0located` : path;
|
|
744
|
+
const cached = cache.get(key);
|
|
555
745
|
if (cached) {
|
|
556
|
-
cache.delete(
|
|
557
|
-
cache.set(
|
|
746
|
+
cache.delete(key);
|
|
747
|
+
cache.set(key, cached);
|
|
558
748
|
return cached;
|
|
559
749
|
}
|
|
560
|
-
const pending = readSource(path).catch(() => null);
|
|
561
|
-
cache.set(
|
|
750
|
+
const pending = readSource(path, locator).catch(() => null);
|
|
751
|
+
cache.set(key, pending);
|
|
562
752
|
if (cache.size > MAX_CACHED_FILES) {
|
|
563
753
|
const oldest = cache.keys().next().value;
|
|
564
754
|
if (oldest !== void 0) cache.delete(oldest);
|
|
565
755
|
}
|
|
566
756
|
return pending;
|
|
567
757
|
}
|
|
568
|
-
async function readSource(path) {
|
|
758
|
+
async function readSource(path, locator) {
|
|
569
759
|
const text = await readText(path);
|
|
570
760
|
if (text === null) return null;
|
|
571
761
|
const lines = text.split(/\r?\n/);
|
|
572
|
-
return { lines, map: await loadMap(path, lines) };
|
|
762
|
+
return { lines, map: await loadMap(path, lines, locator) };
|
|
573
763
|
}
|
|
574
764
|
async function readText(path) {
|
|
575
765
|
try {
|
|
@@ -580,7 +770,7 @@ async function readText(path) {
|
|
|
580
770
|
return null;
|
|
581
771
|
}
|
|
582
772
|
}
|
|
583
|
-
async function loadMap(path, lines) {
|
|
773
|
+
async function loadMap(path, lines, locator) {
|
|
584
774
|
let url;
|
|
585
775
|
for (let index = lines.length - 1; index >= 0 && index >= lines.length - 5; index--) {
|
|
586
776
|
const match = MAPPING_URL.exec(lines[index] ?? "");
|
|
@@ -589,10 +779,15 @@ async function loadMap(path, lines) {
|
|
|
589
779
|
break;
|
|
590
780
|
}
|
|
591
781
|
}
|
|
592
|
-
if (!url) return null;
|
|
593
782
|
let raw;
|
|
594
783
|
let mapDir;
|
|
595
|
-
|
|
784
|
+
const located = url ? null : locator ? locator(path) : `${path}.map`;
|
|
785
|
+
if (located) {
|
|
786
|
+
raw = await readText(located);
|
|
787
|
+
mapDir = (0, import_node_path.dirname)(located);
|
|
788
|
+
} else if (!url) {
|
|
789
|
+
return null;
|
|
790
|
+
} else if (url.startsWith("data:")) {
|
|
596
791
|
const comma = url.indexOf(",");
|
|
597
792
|
const encoded = url.slice(comma + 1);
|
|
598
793
|
raw = url.slice(0, comma).endsWith(";base64") ? Buffer.from(encoded, "base64").toString("utf8") : decodeURIComponent(encoded);
|
|
@@ -621,12 +816,13 @@ async function loadMap(path, lines) {
|
|
|
621
816
|
var import_node_url2 = require("url");
|
|
622
817
|
var WITH_FUNCTION = /^at (?:async )?(.+?) \((.+)\)$/;
|
|
623
818
|
var LOCATION = /^(.+):(\d+):(\d+)$/;
|
|
819
|
+
var MAX_FRAME_LINE = 1e3;
|
|
624
820
|
function parseStack(stack) {
|
|
625
821
|
if (!stack) return [];
|
|
626
822
|
const frames = [];
|
|
627
823
|
for (const raw of stack.split("\n")) {
|
|
628
824
|
const line = raw.trim();
|
|
629
|
-
if (!line.startsWith("at ")) continue;
|
|
825
|
+
if (line.length > MAX_FRAME_LINE || !line.startsWith("at ")) continue;
|
|
630
826
|
const withFunction = WITH_FUNCTION.exec(line);
|
|
631
827
|
const fn = withFunction?.[1];
|
|
632
828
|
const location = withFunction ? withFunction[2] ?? "" : line.replace(/^at (?:async )?/, "");
|
|
@@ -685,10 +881,6 @@ function safeStringify(value) {
|
|
|
685
881
|
}
|
|
686
882
|
}
|
|
687
883
|
|
|
688
|
-
// src/transport.ts
|
|
689
|
-
var import_node_util2 = require("util");
|
|
690
|
-
var import_node_zlib = require("zlib");
|
|
691
|
-
|
|
692
884
|
// src/util.ts
|
|
693
885
|
var import_node_perf_hooks2 = require("perf_hooks");
|
|
694
886
|
function describeError(error) {
|
|
@@ -698,7 +890,101 @@ function elapsedSince(startedAt) {
|
|
|
698
890
|
return Math.round(import_node_perf_hooks2.performance.now() - startedAt);
|
|
699
891
|
}
|
|
700
892
|
|
|
893
|
+
// src/trace-fetch.ts
|
|
894
|
+
var WRAPPED = /* @__PURE__ */ Symbol.for("@oconde/radar.fetch");
|
|
895
|
+
var REQUEST_ID_HEADER = "x-request-id";
|
|
896
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
897
|
+
var TELEGRAM_BOT = /^bot\d{5,}:[\w-]{20,}$/;
|
|
898
|
+
var TOKEN_LIKE = /^[\w\-.~+=]{20,}$/;
|
|
899
|
+
function secretSegment(segment) {
|
|
900
|
+
if (UUID.test(segment)) return false;
|
|
901
|
+
if (TELEGRAM_BOT.test(segment) || looksLikeCredential(segment)) return true;
|
|
902
|
+
return TOKEN_LIKE.test(segment) && /[A-Za-z]/.test(segment) && /\d/.test(segment);
|
|
903
|
+
}
|
|
904
|
+
function outboundPath(pathname, mode) {
|
|
905
|
+
if (mode === "none") return pathname;
|
|
906
|
+
return pathname.split("/").map((segment) => {
|
|
907
|
+
let decoded = segment;
|
|
908
|
+
try {
|
|
909
|
+
decoded = decodeURIComponent(segment);
|
|
910
|
+
} catch {
|
|
911
|
+
decoded = segment;
|
|
912
|
+
}
|
|
913
|
+
return secretSegment(decoded) ? maskValue(decoded, false) : segment;
|
|
914
|
+
}).join("/");
|
|
915
|
+
}
|
|
916
|
+
function failureOf(error) {
|
|
917
|
+
const message = describeError(error);
|
|
918
|
+
const cause = error instanceof Error ? error.cause : void 0;
|
|
919
|
+
const detail = typeof cause?.code === "string" ? cause.code : typeof cause?.message === "string" ? cause.message : "";
|
|
920
|
+
return detail && !message.includes(detail) ? `${message} (${detail})` : message;
|
|
921
|
+
}
|
|
922
|
+
function urlOf(input) {
|
|
923
|
+
try {
|
|
924
|
+
if (typeof input === "string") return new URL(input);
|
|
925
|
+
if (input instanceof URL) return input;
|
|
926
|
+
return new URL(input.url);
|
|
927
|
+
} catch {
|
|
928
|
+
return null;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
function originOf(value) {
|
|
932
|
+
try {
|
|
933
|
+
return new URL(value).origin;
|
|
934
|
+
} catch {
|
|
935
|
+
return null;
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
function targets(url, target) {
|
|
939
|
+
if (target instanceof RegExp) return target.test(url.host);
|
|
940
|
+
const wanted = target.trim().toLowerCase();
|
|
941
|
+
if (wanted.startsWith(".")) return url.hostname.toLowerCase().endsWith(wanted);
|
|
942
|
+
return url.host.toLowerCase() === wanted || url.hostname.toLowerCase() === wanted;
|
|
943
|
+
}
|
|
944
|
+
function isPlainObject(value) {
|
|
945
|
+
if (value === null || typeof value !== "object") return false;
|
|
946
|
+
const prototype = Object.getPrototypeOf(value);
|
|
947
|
+
return prototype === Object.prototype || prototype === null;
|
|
948
|
+
}
|
|
949
|
+
function withRequestId(input, init, requestId) {
|
|
950
|
+
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
|
|
951
|
+
if (headers.has(REQUEST_ID_HEADER)) return [input, init];
|
|
952
|
+
headers.set(REQUEST_ID_HEADER, requestId);
|
|
953
|
+
if (init === void 0 || isPlainObject(init)) return [input, { ...init, headers }];
|
|
954
|
+
return [new Request(input, init), { headers }];
|
|
955
|
+
}
|
|
956
|
+
function traceFetch(config) {
|
|
957
|
+
const current = globalThis.fetch;
|
|
958
|
+
if (typeof current !== "function" || current[WRAPPED]) return () => void 0;
|
|
959
|
+
const original = current;
|
|
960
|
+
const radarOrigin = originOf(config.endpoint);
|
|
961
|
+
const traced = async (input, init) => {
|
|
962
|
+
const url = urlOf(input);
|
|
963
|
+
if (!url || url.origin === radarOrigin || url.protocol !== "http:" && url.protocol !== "https:") return original(input, init);
|
|
964
|
+
const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
965
|
+
const context = currentContext();
|
|
966
|
+
const [nextInput, nextInit] = context && config.propagateTo.some((target) => targets(url, target)) ? withRequestId(input, init, context.requestId) : [input, init];
|
|
967
|
+
const attrs = { method, host: url.host, path: outboundPath(url.pathname, config.redact) };
|
|
968
|
+
const started = performance.now();
|
|
969
|
+
try {
|
|
970
|
+
const response = await original(nextInput, nextInit);
|
|
971
|
+
config.record(response.status >= 500 ? "error" : response.status >= 400 ? "warn" : "info", { ...attrs, status: response.status, durationMs: elapsedSince(started) });
|
|
972
|
+
return response;
|
|
973
|
+
} catch (error) {
|
|
974
|
+
config.record("error", { ...attrs, error: failureOf(error), durationMs: elapsedSince(started) });
|
|
975
|
+
throw error;
|
|
976
|
+
}
|
|
977
|
+
};
|
|
978
|
+
Object.defineProperty(traced, WRAPPED, { value: original });
|
|
979
|
+
globalThis.fetch = traced;
|
|
980
|
+
return () => {
|
|
981
|
+
if (globalThis.fetch === traced) globalThis.fetch = original;
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
|
|
701
985
|
// src/transport.ts
|
|
986
|
+
var import_node_util2 = require("util");
|
|
987
|
+
var import_node_zlib = require("zlib");
|
|
702
988
|
var gzipAsync = (0, import_node_util2.promisify)(import_node_zlib.gzip);
|
|
703
989
|
var MAX_BATCH_BYTES = 9e5;
|
|
704
990
|
var GZIP_THRESHOLD_BYTES = 8192;
|
|
@@ -721,6 +1007,7 @@ var Transport = class {
|
|
|
721
1007
|
inFlight = null;
|
|
722
1008
|
nextAttemptAt = 0;
|
|
723
1009
|
backoffMs = 0;
|
|
1010
|
+
unauthorizedUntil = 0;
|
|
724
1011
|
stopped = false;
|
|
725
1012
|
warned = /* @__PURE__ */ new Set();
|
|
726
1013
|
timer;
|
|
@@ -810,10 +1097,8 @@ var Transport = class {
|
|
|
810
1097
|
this.nextAttemptAt = Date.now() + Math.max(this.backoffMs, outcome.retryAfterMs ?? 0);
|
|
811
1098
|
return false;
|
|
812
1099
|
case "unauthorized":
|
|
813
|
-
this.droppedCount += events.length
|
|
814
|
-
this.
|
|
815
|
-
this.nextAttemptAt = Date.now() + UNAUTHORIZED_PAUSE_MS;
|
|
816
|
-
this.warn("unauthorized", `chave do Radar recusada (${outcome.status}); envios pausados por 10 minutos`, true);
|
|
1100
|
+
this.droppedCount += events.length;
|
|
1101
|
+
this.pauseUnauthorized(outcome.status);
|
|
817
1102
|
return false;
|
|
818
1103
|
case "rejected":
|
|
819
1104
|
this.droppedCount += events.length;
|
|
@@ -821,6 +1106,47 @@ var Transport = class {
|
|
|
821
1106
|
return true;
|
|
822
1107
|
}
|
|
823
1108
|
}
|
|
1109
|
+
async checkIn(slug, payload) {
|
|
1110
|
+
if (this.stopped || !this.config.checkInBaseUrl || Date.now() < this.unauthorizedUntil) return;
|
|
1111
|
+
let response;
|
|
1112
|
+
try {
|
|
1113
|
+
response = await fetch(this.config.checkInBaseUrl + encodeURIComponent(slug), {
|
|
1114
|
+
method: "POST",
|
|
1115
|
+
headers: this.headers(),
|
|
1116
|
+
body: JSON.stringify(payload),
|
|
1117
|
+
redirect: "error",
|
|
1118
|
+
signal: AbortSignal.timeout(this.config.requestTimeoutMs ?? 5e3)
|
|
1119
|
+
});
|
|
1120
|
+
} catch (error) {
|
|
1121
|
+
this.warn("checkin_network", `check-in no Radar falhou: ${describeError(error)}`, false);
|
|
1122
|
+
return;
|
|
1123
|
+
}
|
|
1124
|
+
await response.arrayBuffer().catch(() => void 0);
|
|
1125
|
+
if (response.ok) return;
|
|
1126
|
+
if (response.status === 401 || response.status === 403) {
|
|
1127
|
+
this.pauseUnauthorized(response.status);
|
|
1128
|
+
return;
|
|
1129
|
+
}
|
|
1130
|
+
if (response.status === 404) {
|
|
1131
|
+
this.warn(`checkin_unknown_${slug}`, `tarefa agendada "${slug}" n\xE3o existe no projeto da chave; crie o monitor com esse slug no Radar`, true);
|
|
1132
|
+
return;
|
|
1133
|
+
}
|
|
1134
|
+
this.warn(`checkin_rejected_${response.status}`, `check-in recusado pelo Radar (${response.status})`, false);
|
|
1135
|
+
}
|
|
1136
|
+
pauseUnauthorized(status) {
|
|
1137
|
+
this.droppedCount += this.queue.length;
|
|
1138
|
+
this.queue = [];
|
|
1139
|
+
this.unauthorizedUntil = Date.now() + UNAUTHORIZED_PAUSE_MS;
|
|
1140
|
+
this.nextAttemptAt = Math.max(this.nextAttemptAt, this.unauthorizedUntil);
|
|
1141
|
+
this.warn("unauthorized", `chave do Radar recusada (${status}); envios pausados por 10 minutos`, true);
|
|
1142
|
+
}
|
|
1143
|
+
headers() {
|
|
1144
|
+
return {
|
|
1145
|
+
"content-type": "application/json",
|
|
1146
|
+
authorization: `Bearer ${this.config.key}`,
|
|
1147
|
+
"user-agent": `${this.config.sdk.name}/${this.config.sdk.version}`
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
824
1150
|
requeue(events) {
|
|
825
1151
|
this.queue.unshift(...events);
|
|
826
1152
|
const overflow = this.queue.length - this.maxQueue;
|
|
@@ -832,11 +1158,7 @@ var Transport = class {
|
|
|
832
1158
|
async post(batch) {
|
|
833
1159
|
const json = JSON.stringify(batch);
|
|
834
1160
|
const compress = Buffer.byteLength(json) > GZIP_THRESHOLD_BYTES;
|
|
835
|
-
const headers =
|
|
836
|
-
"content-type": "application/json",
|
|
837
|
-
authorization: `Bearer ${this.config.key}`,
|
|
838
|
-
"user-agent": `${this.config.sdk.name}/${this.config.sdk.version}`
|
|
839
|
-
};
|
|
1161
|
+
const headers = this.headers();
|
|
840
1162
|
if (compress) headers["content-encoding"] = "gzip";
|
|
841
1163
|
let response;
|
|
842
1164
|
try {
|
|
@@ -890,15 +1212,19 @@ function withTimeout(promise, ms) {
|
|
|
890
1212
|
|
|
891
1213
|
// src/version.ts
|
|
892
1214
|
var SDK_NAME = "@oconde/radar";
|
|
893
|
-
var SDK_VERSION = true ? "0.
|
|
1215
|
+
var SDK_VERSION = true ? "0.3.0" : "0.0.0-dev";
|
|
894
1216
|
|
|
895
1217
|
// src/client.ts
|
|
1218
|
+
var MAX_CHECK_IN_DURATION_MS = 7 * 864e5;
|
|
1219
|
+
var MAX_CHECK_IN_ENVIRONMENT = 64;
|
|
1220
|
+
var MAX_CHECK_IN_RELEASE = 128;
|
|
896
1221
|
var RadarClient = class {
|
|
897
1222
|
options = resolveOptions({}, {});
|
|
898
1223
|
transport = null;
|
|
899
1224
|
initialized = false;
|
|
900
1225
|
closed = false;
|
|
901
1226
|
captured = /* @__PURE__ */ new WeakSet();
|
|
1227
|
+
restoreFetch = null;
|
|
902
1228
|
get isEnabled() {
|
|
903
1229
|
return this.transport !== null && !this.closed;
|
|
904
1230
|
}
|
|
@@ -921,10 +1247,19 @@ var RadarClient = class {
|
|
|
921
1247
|
sdk: { name: SDK_NAME, version: SDK_VERSION },
|
|
922
1248
|
environment: this.options.environment,
|
|
923
1249
|
...this.options.release ? { release: this.options.release } : {},
|
|
1250
|
+
checkInBaseUrl: this.options.endpoint + CHECKIN_PATH,
|
|
924
1251
|
onWarning: (message, always) => always ? this.writeWarning(message) : this.debugWarn(message)
|
|
925
1252
|
});
|
|
926
1253
|
}
|
|
927
1254
|
if (this.options.captureUnhandled && (this.transport || this.options.console)) installProcessHandlers(this);
|
|
1255
|
+
if (this.options.traceFetch.enabled && (this.transport || this.options.console)) {
|
|
1256
|
+
this.restoreFetch = traceFetch({
|
|
1257
|
+
endpoint: this.options.endpoint,
|
|
1258
|
+
propagateTo: this.options.traceFetch.propagateTo,
|
|
1259
|
+
redact: this.options.redact,
|
|
1260
|
+
record: (level, attrs) => this.safely(() => this.emitLog(level, "http.client", attrs))
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
928
1263
|
} catch (error) {
|
|
929
1264
|
this.debugWarn(`radar.init falhou: ${describeError(error)}`);
|
|
930
1265
|
}
|
|
@@ -982,6 +1317,51 @@ var RadarClient = class {
|
|
|
982
1317
|
if (currentContext()) return execute();
|
|
983
1318
|
return runWithContext({ requestId: (0, import_node_crypto3.randomUUID)(), startedAt: import_node_perf_hooks3.performance.now() }, execute);
|
|
984
1319
|
}
|
|
1320
|
+
withContext(fn, options = {}) {
|
|
1321
|
+
return runWithContext({ requestId: requestIdFrom(options.requestId), startedAt: import_node_perf_hooks3.performance.now() }, fn);
|
|
1322
|
+
}
|
|
1323
|
+
async checkIn(slug, status, options = {}) {
|
|
1324
|
+
try {
|
|
1325
|
+
if (!this.transport || this.closed) return;
|
|
1326
|
+
if (typeof slug !== "string" || !CHECKIN_SLUG.test(slug)) {
|
|
1327
|
+
this.debugWarn(`slug de tarefa agendada inv\xE1lido: ${String(slug).slice(0, 80)}`);
|
|
1328
|
+
return;
|
|
1329
|
+
}
|
|
1330
|
+
if (!CHECKIN_STATUSES.includes(status)) return;
|
|
1331
|
+
const checkInId = typeof options.checkInId === "string" && options.checkInId ? options.checkInId.slice(0, LIMITS.checkInIdLength) : void 0;
|
|
1332
|
+
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;
|
|
1333
|
+
const environment = this.options.environment.slice(0, MAX_CHECK_IN_ENVIRONMENT) || "production";
|
|
1334
|
+
const release = this.options.release?.slice(0, MAX_CHECK_IN_RELEASE);
|
|
1335
|
+
const payload = {
|
|
1336
|
+
status,
|
|
1337
|
+
...checkInId ? { checkInId } : {},
|
|
1338
|
+
...durationMs !== void 0 ? { durationMs } : {},
|
|
1339
|
+
environment,
|
|
1340
|
+
...release ? { release } : {}
|
|
1341
|
+
};
|
|
1342
|
+
await this.transport.checkIn(slug, payload);
|
|
1343
|
+
} catch (error) {
|
|
1344
|
+
this.debugWarn(`check-in falhou: ${describeError(error)}`);
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
async cron(slug, fn) {
|
|
1348
|
+
const checkInId = (0, import_node_crypto3.randomUUID)();
|
|
1349
|
+
const started = import_node_perf_hooks3.performance.now();
|
|
1350
|
+
const execute = async () => {
|
|
1351
|
+
await this.checkIn(slug, "in_progress", { checkInId });
|
|
1352
|
+
try {
|
|
1353
|
+
const result = await fn();
|
|
1354
|
+
await this.checkIn(slug, "ok", { checkInId, durationMs: elapsedSince(started) });
|
|
1355
|
+
return result;
|
|
1356
|
+
} catch (error) {
|
|
1357
|
+
this.captureError(error, { cron: slug });
|
|
1358
|
+
await this.checkIn(slug, "error", { checkInId, durationMs: elapsedSince(started) });
|
|
1359
|
+
throw error;
|
|
1360
|
+
}
|
|
1361
|
+
};
|
|
1362
|
+
if (currentContext()) return execute();
|
|
1363
|
+
return this.withContext(execute);
|
|
1364
|
+
}
|
|
985
1365
|
middleware() {
|
|
986
1366
|
return createMiddleware(this);
|
|
987
1367
|
}
|
|
@@ -999,7 +1379,7 @@ var RadarClient = class {
|
|
|
999
1379
|
() => this.emitLog(level, "http.request", {
|
|
1000
1380
|
method: (req.method ?? "GET").toUpperCase(),
|
|
1001
1381
|
...route ? { route } : {},
|
|
1002
|
-
...this.options.requestDetail === "full" ? { path: redactPath(requestPath(req), req
|
|
1382
|
+
...this.options.requestDetail === "full" ? { path: redactPath(requestPath(req), pathParams(req, requestPath(req)), this.options.redact) } : {},
|
|
1003
1383
|
status,
|
|
1004
1384
|
durationMs: elapsedSince(context.startedAt)
|
|
1005
1385
|
})
|
|
@@ -1016,6 +1396,8 @@ var RadarClient = class {
|
|
|
1016
1396
|
async close(timeoutMs = 2e3) {
|
|
1017
1397
|
if (this.closed) return;
|
|
1018
1398
|
await this.flush(timeoutMs);
|
|
1399
|
+
this.restoreFetch?.();
|
|
1400
|
+
this.restoreFetch = null;
|
|
1019
1401
|
this.closed = true;
|
|
1020
1402
|
this.transport?.stop();
|
|
1021
1403
|
}
|
|
@@ -1036,31 +1418,40 @@ var RadarClient = class {
|
|
|
1036
1418
|
this.print(event);
|
|
1037
1419
|
this.transport?.enqueue(event);
|
|
1038
1420
|
}
|
|
1421
|
+
captureException(exception, options = {}) {
|
|
1422
|
+
this.safely(() => this.emitException(exception, options));
|
|
1423
|
+
}
|
|
1039
1424
|
emitError(error, data, level, handled) {
|
|
1040
1425
|
if (this.closed) return;
|
|
1041
1426
|
if (typeof error === "object" && error !== null) {
|
|
1042
1427
|
if (this.captured.has(error)) return;
|
|
1043
1428
|
this.captured.add(error);
|
|
1044
1429
|
}
|
|
1430
|
+
this.emitException(exceptionInfo(error), { level, handled, ...data !== void 0 ? { attrs: data } : {} }, error instanceof Error ? error.stack : void 0);
|
|
1431
|
+
}
|
|
1432
|
+
emitException(exception, options, stack) {
|
|
1433
|
+
if (this.closed) return;
|
|
1045
1434
|
const context = currentContext();
|
|
1046
1435
|
const mode = this.options.redact;
|
|
1436
|
+
const request = options.request === null ? void 0 : options.request ?? (context?.req ? requestInfo(context.req, mode, { route: context.route }, this.options.requestDetail) : void 0);
|
|
1437
|
+
const runtime = options.runtime === void 0 ? runtimeInfo() : options.runtime;
|
|
1047
1438
|
const event = {
|
|
1048
1439
|
type: "error",
|
|
1049
1440
|
ts: Date.now(),
|
|
1050
|
-
level,
|
|
1051
|
-
handled,
|
|
1052
|
-
exception
|
|
1441
|
+
level: options.level ?? "error",
|
|
1442
|
+
handled: options.handled ?? true,
|
|
1443
|
+
exception,
|
|
1053
1444
|
...context ? { requestId: context.requestId } : {},
|
|
1054
|
-
...
|
|
1445
|
+
...request ? { request } : {},
|
|
1055
1446
|
...context?.user ? { user: context.user } : {},
|
|
1056
|
-
runtime:
|
|
1057
|
-
...
|
|
1447
|
+
...runtime ? { runtime } : {},
|
|
1448
|
+
...options.attrs !== void 0 ? { attrs: prepareAttrs(options.attrs, mode) } : {}
|
|
1058
1449
|
};
|
|
1059
|
-
this.print(event,
|
|
1450
|
+
this.print(event, stack);
|
|
1060
1451
|
if (!this.transport) return;
|
|
1061
1452
|
this.transport.enqueue(
|
|
1062
|
-
() => enrichException(event.exception).then(
|
|
1063
|
-
(
|
|
1453
|
+
() => enrichException(event.exception, process.cwd(), options.enrich).then(
|
|
1454
|
+
(enriched) => ({ ...event, exception: enriched }),
|
|
1064
1455
|
() => event
|
|
1065
1456
|
)
|
|
1066
1457
|
);
|