@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.cjs
CHANGED
|
@@ -32,29 +32,13 @@ var import_node_perf_hooks3 = require("perf_hooks");
|
|
|
32
32
|
|
|
33
33
|
// src/context.ts
|
|
34
34
|
var import_node_async_hooks = require("async_hooks");
|
|
35
|
-
var
|
|
36
|
-
function globalState() {
|
|
37
|
-
const holder = globalThis;
|
|
38
|
-
let state2 = holder[STATE_KEY];
|
|
39
|
-
if (!state2) {
|
|
40
|
-
state2 = { storage: new import_node_async_hooks.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
|
-
|
|
52
|
-
// src/express.ts
|
|
53
|
-
var import_node_crypto2 = require("crypto");
|
|
54
|
-
var import_node_perf_hooks = require("perf_hooks");
|
|
35
|
+
var import_node_crypto = require("crypto");
|
|
55
36
|
|
|
56
37
|
// src/protocol/index.ts
|
|
57
38
|
var INGEST_PATH = "/api/v1/events";
|
|
39
|
+
var CHECKIN_PATH = "/api/v1/checkins/";
|
|
40
|
+
var CHECKIN_SLUG = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
41
|
+
var CHECKIN_STATUSES = ["in_progress", "ok", "error"];
|
|
58
42
|
var LIMITS = {
|
|
59
43
|
batchBytes: 1e6,
|
|
60
44
|
batchEvents: 500,
|
|
@@ -70,11 +54,38 @@ var LIMITS = {
|
|
|
70
54
|
causeDepth: 5,
|
|
71
55
|
depth: 6,
|
|
72
56
|
arrayItems: 50,
|
|
73
|
-
requestIdLength: 128
|
|
57
|
+
requestIdLength: 128,
|
|
58
|
+
checkInIdLength: 64
|
|
74
59
|
};
|
|
75
60
|
|
|
61
|
+
// src/context.ts
|
|
62
|
+
var STATE_KEY = /* @__PURE__ */ Symbol.for("@oconde/radar");
|
|
63
|
+
function globalState() {
|
|
64
|
+
const holder = globalThis;
|
|
65
|
+
let state2 = holder[STATE_KEY];
|
|
66
|
+
if (!state2) {
|
|
67
|
+
state2 = { storage: new import_node_async_hooks.AsyncLocalStorage() };
|
|
68
|
+
holder[STATE_KEY] = state2;
|
|
69
|
+
}
|
|
70
|
+
return state2;
|
|
71
|
+
}
|
|
72
|
+
function currentContext() {
|
|
73
|
+
return globalState().storage.getStore();
|
|
74
|
+
}
|
|
75
|
+
function runWithContext(context, fn) {
|
|
76
|
+
return globalState().storage.run(context, fn);
|
|
77
|
+
}
|
|
78
|
+
var SAFE_REQUEST_ID = /^[\x21-\x7e]+$/;
|
|
79
|
+
function requestIdFrom(incoming) {
|
|
80
|
+
const value = incoming?.trim();
|
|
81
|
+
return value && value.length <= LIMITS.requestIdLength && SAFE_REQUEST_ID.test(value) ? value : (0, import_node_crypto.randomUUID)();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// src/express.ts
|
|
85
|
+
var import_node_perf_hooks = require("perf_hooks");
|
|
86
|
+
|
|
76
87
|
// src/redact.ts
|
|
77
|
-
var
|
|
88
|
+
var import_node_crypto2 = require("crypto");
|
|
78
89
|
var SECRET_WORDS = /* @__PURE__ */ new Set([
|
|
79
90
|
"pass",
|
|
80
91
|
"passwd",
|
|
@@ -120,16 +131,16 @@ function fingerprintSlot() {
|
|
|
120
131
|
const holder = globalThis;
|
|
121
132
|
let slot = holder[KEY_SLOT];
|
|
122
133
|
if (!slot) {
|
|
123
|
-
slot = { key: (0,
|
|
134
|
+
slot = { key: (0, import_node_crypto2.randomBytes)(32) };
|
|
124
135
|
holder[KEY_SLOT] = slot;
|
|
125
136
|
}
|
|
126
137
|
return slot;
|
|
127
138
|
}
|
|
128
139
|
function setFingerprintSecret(secret) {
|
|
129
|
-
fingerprintSlot().key = secret ? (0,
|
|
140
|
+
fingerprintSlot().key = secret ? (0, import_node_crypto2.createHash)("sha256").update(`@oconde/radar:${secret}`).digest() : (0, import_node_crypto2.randomBytes)(32);
|
|
130
141
|
}
|
|
131
142
|
function fingerprint(value) {
|
|
132
|
-
return (0,
|
|
143
|
+
return (0, import_node_crypto2.createHmac)("sha256", fingerprintSlot().key).update(value).digest("hex").slice(0, 8);
|
|
133
144
|
}
|
|
134
145
|
function words(name) {
|
|
135
146
|
return name.replace(/([a-z0-9])([A-Z])/g, "$1 $2").toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
@@ -271,7 +282,44 @@ function requestPath(req) {
|
|
|
271
282
|
}
|
|
272
283
|
function requestRoute(req, route) {
|
|
273
284
|
if (route) return route;
|
|
274
|
-
|
|
285
|
+
if (typeof req.route?.path !== "string") return void 0;
|
|
286
|
+
const base = typeof req.baseUrl === "string" ? req.baseUrl.replace(/\/+$/, "") : "";
|
|
287
|
+
if (!base) return req.route.path;
|
|
288
|
+
return req.route.path === "/" ? base : `${base}${req.route.path}`;
|
|
289
|
+
}
|
|
290
|
+
var ROUTE_PARAM = /^:([A-Za-z_$][\w$]*)/;
|
|
291
|
+
function pathSegments(value) {
|
|
292
|
+
const segments = value.split("/");
|
|
293
|
+
while (segments.length > 1 && segments[segments.length - 1] === "") segments.pop();
|
|
294
|
+
return segments;
|
|
295
|
+
}
|
|
296
|
+
function decodeSegment(value) {
|
|
297
|
+
try {
|
|
298
|
+
return decodeURIComponent(value);
|
|
299
|
+
} catch {
|
|
300
|
+
return value;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function routeParams(req, path) {
|
|
304
|
+
if (typeof req.route?.path !== "string") return {};
|
|
305
|
+
const pattern = pathSegments(`${typeof req.baseUrl === "string" ? req.baseUrl : ""}${req.route.path}`).filter((segment, index) => index > 0 || segment !== "");
|
|
306
|
+
const values = pathSegments(path);
|
|
307
|
+
if (pattern.length > values.length) return {};
|
|
308
|
+
const offset = values.length - pattern.length;
|
|
309
|
+
const result = {};
|
|
310
|
+
for (let index = 0; index < pattern.length; index++) {
|
|
311
|
+
const value = values[offset + index];
|
|
312
|
+
const param = ROUTE_PARAM.exec(pattern[index]);
|
|
313
|
+
if (!param) {
|
|
314
|
+
if (pattern[index] !== value) return {};
|
|
315
|
+
continue;
|
|
316
|
+
}
|
|
317
|
+
if (value) result[param[1]] = decodeSegment(value);
|
|
318
|
+
}
|
|
319
|
+
return result;
|
|
320
|
+
}
|
|
321
|
+
function pathParams(req, path) {
|
|
322
|
+
return { ...routeParams(req, path), ...req.params ?? {} };
|
|
275
323
|
}
|
|
276
324
|
function redactPath(path, params, mode) {
|
|
277
325
|
if (mode === "none" || !params) return path;
|
|
@@ -288,7 +336,7 @@ function safeUrl(req, mode) {
|
|
|
288
336
|
const queryStart = url.indexOf("?");
|
|
289
337
|
const path = queryStart >= 0 ? url.slice(0, queryStart) : url;
|
|
290
338
|
const query = queryStart >= 0 ? url.slice(queryStart) : "";
|
|
291
|
-
return redactUrl(redactPath(path, req
|
|
339
|
+
return redactUrl(redactPath(path, pathParams(req, path), mode) + query, mode);
|
|
292
340
|
}
|
|
293
341
|
function redactUrl(url, mode) {
|
|
294
342
|
const queryStart = url.indexOf("?");
|
|
@@ -356,8 +404,7 @@ function createMiddleware(logger) {
|
|
|
356
404
|
return (req, res, next) => {
|
|
357
405
|
let context;
|
|
358
406
|
try {
|
|
359
|
-
const
|
|
360
|
-
const requestId = incoming && incoming.length <= LIMITS.requestIdLength ? incoming : (0, import_node_crypto2.randomUUID)();
|
|
407
|
+
const requestId = requestIdFrom(headerValue(req.headers["x-request-id"]));
|
|
361
408
|
context = { requestId, startedAt: import_node_perf_hooks.performance.now(), req };
|
|
362
409
|
res.setHeader("x-request-id", requestId);
|
|
363
410
|
if (logger.shouldLogRequest(req)) res.once("finish", () => logger.logHttpRequest(context, res.statusCode));
|
|
@@ -372,6 +419,12 @@ function createMiddleware(logger) {
|
|
|
372
419
|
// src/options.ts
|
|
373
420
|
var DEFAULT_ENDPOINT = "https://radar-ingest.oconde.dev";
|
|
374
421
|
var LEVELS = ["debug", "info", "warn", "error"];
|
|
422
|
+
function resolveTraceFetch(value) {
|
|
423
|
+
if (!value) return { enabled: false, propagateTo: [] };
|
|
424
|
+
if (value === true) return { enabled: true, propagateTo: [] };
|
|
425
|
+
const targets2 = Array.isArray(value.propagateTo) ? value.propagateTo : [];
|
|
426
|
+
return { enabled: true, propagateTo: targets2.filter((target) => typeof target === "string" ? target.trim() !== "" : target instanceof RegExp) };
|
|
427
|
+
}
|
|
375
428
|
function resolveOptions(options = {}, env = process.env) {
|
|
376
429
|
const key = options.key?.trim() || void 0;
|
|
377
430
|
return {
|
|
@@ -387,6 +440,7 @@ function resolveOptions(options = {}, env = process.env) {
|
|
|
387
440
|
logRequests: options.logRequests ?? true,
|
|
388
441
|
ignorePaths: options.ignorePaths ?? ["/healthz", "/health"],
|
|
389
442
|
captureUnhandled: options.captureUnhandled ?? true,
|
|
443
|
+
traceFetch: resolveTraceFetch(options.traceFetch),
|
|
390
444
|
debug: options.debug ?? false
|
|
391
445
|
};
|
|
392
446
|
}
|
|
@@ -448,16 +502,144 @@ var import_promises = require("fs/promises");
|
|
|
448
502
|
var import_node_module = require("module");
|
|
449
503
|
var import_node_path = require("path");
|
|
450
504
|
var import_node_url = require("url");
|
|
505
|
+
|
|
506
|
+
// src/function-name.ts
|
|
507
|
+
var IDENTIFIER = "[A-Za-z_$][\\w$]*";
|
|
508
|
+
var MAX_LINES_UP = 400;
|
|
509
|
+
var MAX_HEADER_LINES = 6;
|
|
510
|
+
var MAX_NAME_LENGTH = 100;
|
|
511
|
+
var MAX_COMMENT_LINES = 40;
|
|
512
|
+
var CONTROL_BLOCK = /^\s*(?:\}\s*)?(?:if|else|for|while|do|switch|try|catch|finally|with)\b/;
|
|
513
|
+
var CLASS_BODY = new RegExp(`\\bclass\\s+${IDENTIFIER}`);
|
|
514
|
+
var MARKUP_BOUNDARY = /<\/?(?:script|style|template)\b/i;
|
|
515
|
+
var KEYWORDS = /* @__PURE__ */ new Set(["if", "for", "while", "switch", "catch", "function", "return", "new", "typeof", "await", "else", "do", "try"]);
|
|
516
|
+
var NAME_PATTERNS = [
|
|
517
|
+
new RegExp(`\\bfunction\\s*\\*?\\s*(${IDENTIFIER})\\s*\\(`),
|
|
518
|
+
new RegExp(`(${IDENTIFIER})\\s*[:=]\\s*(?:async\\s+)?function\\b`),
|
|
519
|
+
new RegExp(`(${IDENTIFIER})\\s*(?::\\s*[^=]+)?=\\s*(?:async\\s+)?(?:\\([^]*\\)|${IDENTIFIER})\\s*(?::\\s*[^=]+)?=>\\s*$`),
|
|
520
|
+
new RegExp(`(?:^|[{,(]\\s*)(${IDENTIFIER})\\s*:\\s*(?:async\\s+)?(?:\\([^]*\\)|${IDENTIFIER})\\s*(?::\\s*[^=]+)?=>\\s*$`)
|
|
521
|
+
];
|
|
522
|
+
var METHOD_PATTERN = new RegExp(`^\\s*(?:(?:export|default|public|private|protected|static|async|override|readonly|get|set)\\s+)*\\*?\\s*(${IDENTIFIER})\\s*(?:<[^>]*>)?\\s*\\([^]*\\)\\s*(?::\\s*[^{]+)?$`);
|
|
523
|
+
var ANONYMOUS_FUNCTION = /\bfunction\b|=>/;
|
|
524
|
+
var STRING_LITERAL = /(["'`])(?:\\.|(?!\1)[^\\])*\1/g;
|
|
525
|
+
function lineCommentAt(text, from) {
|
|
526
|
+
for (let index = text.indexOf("//", from); index >= 0; index = text.indexOf("//", index + 1)) {
|
|
527
|
+
if (index === 0 || text[index - 1] !== ":") return index;
|
|
528
|
+
}
|
|
529
|
+
return -1;
|
|
530
|
+
}
|
|
531
|
+
function blockCommentAt(text, from) {
|
|
532
|
+
for (let index = text.indexOf("/*", from); index >= 0; index = text.indexOf("/*", index + 1)) {
|
|
533
|
+
if (index === 0 || text[index - 1] !== "\\") return index;
|
|
534
|
+
}
|
|
535
|
+
return -1;
|
|
536
|
+
}
|
|
537
|
+
function stripComments(text, insideComment, closesLater) {
|
|
538
|
+
let code = "";
|
|
539
|
+
let position = 0;
|
|
540
|
+
let inside = insideComment;
|
|
541
|
+
while (position < text.length) {
|
|
542
|
+
if (inside) {
|
|
543
|
+
const end = text.indexOf("*/", position);
|
|
544
|
+
if (end < 0) return { code, insideComment: true };
|
|
545
|
+
inside = false;
|
|
546
|
+
position = end + 2;
|
|
547
|
+
continue;
|
|
548
|
+
}
|
|
549
|
+
const block = blockCommentAt(text, position);
|
|
550
|
+
const line = lineCommentAt(text, position);
|
|
551
|
+
if (line >= 0 && (block < 0 || line < block)) return { code: code + text.slice(position, line), insideComment: false };
|
|
552
|
+
if (block < 0) return { code: code + text.slice(position), insideComment: false };
|
|
553
|
+
if (text.indexOf("*/", block + 2) < 0 && !closesLater()) {
|
|
554
|
+
code += text.slice(position, block + 2);
|
|
555
|
+
position = block + 2;
|
|
556
|
+
continue;
|
|
557
|
+
}
|
|
558
|
+
code += text.slice(position, block);
|
|
559
|
+
inside = true;
|
|
560
|
+
position = block + 2;
|
|
561
|
+
}
|
|
562
|
+
return { code, insideComment: inside };
|
|
563
|
+
}
|
|
564
|
+
function codeLines(lines, from, start, column) {
|
|
565
|
+
const plain = lines.slice(from, start + 1).map((line) => line.replace(STRING_LITERAL, '""'));
|
|
566
|
+
const closesWithin = (index) => plain.slice(index + 1, index + 1 + MAX_COMMENT_LINES).some((line) => line.includes("*/"));
|
|
567
|
+
const result = [];
|
|
568
|
+
let insideComment = false;
|
|
569
|
+
for (let index = 0; index < plain.length; index++) {
|
|
570
|
+
const text = index === plain.length - 1 && column ? (lines[start] ?? "").slice(0, Math.max(0, column - 1)).replace(STRING_LITERAL, '""') : plain[index] ?? "";
|
|
571
|
+
const stripped = stripComments(text, insideComment, () => closesWithin(index));
|
|
572
|
+
insideComment = stripped.insideComment;
|
|
573
|
+
result.push(stripped.code);
|
|
574
|
+
}
|
|
575
|
+
return result;
|
|
576
|
+
}
|
|
577
|
+
function parenBalance(text) {
|
|
578
|
+
let balance = 0;
|
|
579
|
+
for (const char of text) {
|
|
580
|
+
if (char === "(") balance += 1;
|
|
581
|
+
else if (char === ")") balance -= 1;
|
|
582
|
+
}
|
|
583
|
+
return balance;
|
|
584
|
+
}
|
|
585
|
+
function headerOf(code, index, prefix) {
|
|
586
|
+
let header = prefix;
|
|
587
|
+
for (let previous = index - 1; previous >= 0 && previous >= index - MAX_HEADER_LINES; previous--) {
|
|
588
|
+
if (header.trim() !== "" && parenBalance(header) >= 0) break;
|
|
589
|
+
header = `${code[previous] ?? ""} ${header}`;
|
|
590
|
+
}
|
|
591
|
+
return header;
|
|
592
|
+
}
|
|
593
|
+
function nameIn(header) {
|
|
594
|
+
const patterns = ANONYMOUS_FUNCTION.test(header) ? NAME_PATTERNS : [...NAME_PATTERNS, METHOD_PATTERN];
|
|
595
|
+
for (const pattern of patterns) {
|
|
596
|
+
const name = pattern.exec(header)?.[1];
|
|
597
|
+
if (name && !KEYWORDS.has(name)) return name.slice(0, MAX_NAME_LENGTH);
|
|
598
|
+
}
|
|
599
|
+
return void 0;
|
|
600
|
+
}
|
|
601
|
+
function enclosingFunctionName(lines, line, column) {
|
|
602
|
+
const start = line - 1;
|
|
603
|
+
if (start < 0 || start >= lines.length) return void 0;
|
|
604
|
+
const from = Math.max(0, start - MAX_LINES_UP - MAX_HEADER_LINES);
|
|
605
|
+
const cleaned = codeLines(lines, from, start, column);
|
|
606
|
+
let depth = 0;
|
|
607
|
+
for (let index = start; index >= 0 && index >= start - MAX_LINES_UP; index--) {
|
|
608
|
+
if (index < start && MARKUP_BOUNDARY.test(lines[index] ?? "")) return void 0;
|
|
609
|
+
const code = cleaned[index - from] ?? "";
|
|
610
|
+
for (let position = code.length - 1; position >= 0; position--) {
|
|
611
|
+
const char = code[position];
|
|
612
|
+
if (char === "}") {
|
|
613
|
+
depth += 1;
|
|
614
|
+
} else if (char === "{") {
|
|
615
|
+
if (depth > 0) {
|
|
616
|
+
depth -= 1;
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
const header = headerOf(cleaned, index - from, code.slice(0, position));
|
|
620
|
+
if (CLASS_BODY.test(header)) return void 0;
|
|
621
|
+
if (CONTROL_BLOCK.test(header)) continue;
|
|
622
|
+
const name = nameIn(header);
|
|
623
|
+
if (name) return name;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
return void 0;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// src/source.ts
|
|
451
631
|
var MAX_FILE_BYTES = 2 * 1024 * 1024;
|
|
452
632
|
var MAX_CACHED_FILES = 50;
|
|
453
633
|
var MAPPING_URL = /\/\/[#@]\s*sourceMappingURL=(\S+)\s*$/;
|
|
454
634
|
var cache = /* @__PURE__ */ new Map();
|
|
455
|
-
async function enrichFrames(frames, cwd = process.cwd()) {
|
|
635
|
+
async function enrichFrames(frames, cwd = process.cwd(), options = {}) {
|
|
456
636
|
const result = [];
|
|
457
637
|
let withContext = 0;
|
|
458
638
|
for (const frame of frames) {
|
|
459
639
|
try {
|
|
460
|
-
const { frame:
|
|
640
|
+
const { frame: located, lines, original } = await mapFrame(frame, options.mapLocator);
|
|
641
|
+
const named = options.inferFunctionNames && original && !located.fn && lines && located.line ? withInferredName(located, lines) : located;
|
|
642
|
+
const mapped = named.inApp && isLibraryPath(named.file) ? { ...named, inApp: false } : named;
|
|
461
643
|
let next = mapped;
|
|
462
644
|
if (mapped.inApp && lines && mapped.line && withContext < LIMITS.contextFrames) {
|
|
463
645
|
const context = contextFor(lines, mapped.line);
|
|
@@ -473,29 +655,36 @@ async function enrichFrames(frames, cwd = process.cwd()) {
|
|
|
473
655
|
}
|
|
474
656
|
return result;
|
|
475
657
|
}
|
|
476
|
-
async function enrichException(exception, cwd = process.cwd()) {
|
|
477
|
-
const frames = await enrichFrames(exception.frames, cwd);
|
|
658
|
+
async function enrichException(exception, cwd = process.cwd(), options = {}) {
|
|
659
|
+
const frames = await enrichFrames(exception.frames, cwd, options);
|
|
478
660
|
if (!exception.cause) return { ...exception, frames };
|
|
479
|
-
return { ...exception, frames, cause: await enrichException(exception.cause, cwd) };
|
|
661
|
+
return { ...exception, frames, cause: await enrichException(exception.cause, cwd, options) };
|
|
662
|
+
}
|
|
663
|
+
function withInferredName(frame, lines) {
|
|
664
|
+
const fn = enclosingFunctionName(lines, frame.line ?? 0, frame.col);
|
|
665
|
+
return fn ? { ...frame, fn } : frame;
|
|
666
|
+
}
|
|
667
|
+
function isLibraryPath(file) {
|
|
668
|
+
return file.includes("/node_modules/") || file.includes("\\node_modules\\");
|
|
480
669
|
}
|
|
481
670
|
function relativize(file, cwd) {
|
|
482
671
|
if (!(0, import_node_path.isAbsolute)(file)) return file;
|
|
483
672
|
const path = (0, import_node_path.relative)(cwd, file);
|
|
484
673
|
return path.startsWith("..") || (0, import_node_path.isAbsolute)(path) ? file : path.split(import_node_path.sep).join("/");
|
|
485
674
|
}
|
|
486
|
-
async function mapFrame(frame) {
|
|
487
|
-
if (!frame.line || !(0, import_node_path.isAbsolute)(frame.file)) return { frame, lines: null };
|
|
488
|
-
const file = await loadFile(frame.file);
|
|
489
|
-
if (!file) return { frame, lines: null };
|
|
490
|
-
if (!file.map) return { frame, lines: file.lines };
|
|
675
|
+
async function mapFrame(frame, locator) {
|
|
676
|
+
if (!frame.line || !(0, import_node_path.isAbsolute)(frame.file)) return { frame, lines: null, original: false };
|
|
677
|
+
const file = await loadFile(frame.file, locator);
|
|
678
|
+
if (!file) return { frame, lines: null, original: false };
|
|
679
|
+
if (!file.map) return { frame, lines: file.lines, original: false };
|
|
491
680
|
const entry = file.map.map.findEntry(frame.line - 1, (frame.col ?? 1) - 1);
|
|
492
|
-
if (typeof entry.originalSource !== "string" || typeof entry.originalLine !== "number") return { frame, lines: file.lines };
|
|
681
|
+
if (typeof entry.originalSource !== "string" || typeof entry.originalLine !== "number") return { frame, lines: file.lines, original: false };
|
|
493
682
|
const originalPath = resolveSource(file.map, entry.originalSource);
|
|
494
683
|
const mapped = { ...frame, file: originalPath, line: entry.originalLine + 1, col: (entry.originalColumn ?? 0) + 1 };
|
|
495
684
|
const lines = embeddedLines(file.map, file.map.sources.indexOf(entry.originalSource));
|
|
496
|
-
if (lines) return { frame: mapped, lines };
|
|
685
|
+
if (lines) return { frame: mapped, lines, original: true };
|
|
497
686
|
const original = await loadFile(originalPath);
|
|
498
|
-
return { frame: mapped, lines: original?.lines ?? null };
|
|
687
|
+
return { frame: mapped, lines: original?.lines ?? null, original: true };
|
|
499
688
|
}
|
|
500
689
|
function embeddedLines(map, index) {
|
|
501
690
|
if (index < 0) return null;
|
|
@@ -527,26 +716,27 @@ function resolveSource(map, source) {
|
|
|
527
716
|
}
|
|
528
717
|
return (0, import_node_path.isAbsolute)(source) ? source : (0, import_node_path.resolve)(map.baseDir, source);
|
|
529
718
|
}
|
|
530
|
-
function loadFile(path) {
|
|
531
|
-
const
|
|
719
|
+
function loadFile(path, locator) {
|
|
720
|
+
const key = locator ? `${path}\0located` : path;
|
|
721
|
+
const cached = cache.get(key);
|
|
532
722
|
if (cached) {
|
|
533
|
-
cache.delete(
|
|
534
|
-
cache.set(
|
|
723
|
+
cache.delete(key);
|
|
724
|
+
cache.set(key, cached);
|
|
535
725
|
return cached;
|
|
536
726
|
}
|
|
537
|
-
const pending = readSource(path).catch(() => null);
|
|
538
|
-
cache.set(
|
|
727
|
+
const pending = readSource(path, locator).catch(() => null);
|
|
728
|
+
cache.set(key, pending);
|
|
539
729
|
if (cache.size > MAX_CACHED_FILES) {
|
|
540
730
|
const oldest = cache.keys().next().value;
|
|
541
731
|
if (oldest !== void 0) cache.delete(oldest);
|
|
542
732
|
}
|
|
543
733
|
return pending;
|
|
544
734
|
}
|
|
545
|
-
async function readSource(path) {
|
|
735
|
+
async function readSource(path, locator) {
|
|
546
736
|
const text = await readText(path);
|
|
547
737
|
if (text === null) return null;
|
|
548
738
|
const lines = text.split(/\r?\n/);
|
|
549
|
-
return { lines, map: await loadMap(path, lines) };
|
|
739
|
+
return { lines, map: await loadMap(path, lines, locator) };
|
|
550
740
|
}
|
|
551
741
|
async function readText(path) {
|
|
552
742
|
try {
|
|
@@ -557,7 +747,7 @@ async function readText(path) {
|
|
|
557
747
|
return null;
|
|
558
748
|
}
|
|
559
749
|
}
|
|
560
|
-
async function loadMap(path, lines) {
|
|
750
|
+
async function loadMap(path, lines, locator) {
|
|
561
751
|
let url;
|
|
562
752
|
for (let index = lines.length - 1; index >= 0 && index >= lines.length - 5; index--) {
|
|
563
753
|
const match = MAPPING_URL.exec(lines[index] ?? "");
|
|
@@ -566,10 +756,15 @@ async function loadMap(path, lines) {
|
|
|
566
756
|
break;
|
|
567
757
|
}
|
|
568
758
|
}
|
|
569
|
-
if (!url) return null;
|
|
570
759
|
let raw;
|
|
571
760
|
let mapDir;
|
|
572
|
-
|
|
761
|
+
const located = url ? null : locator ? locator(path) : `${path}.map`;
|
|
762
|
+
if (located) {
|
|
763
|
+
raw = await readText(located);
|
|
764
|
+
mapDir = (0, import_node_path.dirname)(located);
|
|
765
|
+
} else if (!url) {
|
|
766
|
+
return null;
|
|
767
|
+
} else if (url.startsWith("data:")) {
|
|
573
768
|
const comma = url.indexOf(",");
|
|
574
769
|
const encoded = url.slice(comma + 1);
|
|
575
770
|
raw = url.slice(0, comma).endsWith(";base64") ? Buffer.from(encoded, "base64").toString("utf8") : decodeURIComponent(encoded);
|
|
@@ -598,12 +793,13 @@ async function loadMap(path, lines) {
|
|
|
598
793
|
var import_node_url2 = require("url");
|
|
599
794
|
var WITH_FUNCTION = /^at (?:async )?(.+?) \((.+)\)$/;
|
|
600
795
|
var LOCATION = /^(.+):(\d+):(\d+)$/;
|
|
796
|
+
var MAX_FRAME_LINE = 1e3;
|
|
601
797
|
function parseStack(stack) {
|
|
602
798
|
if (!stack) return [];
|
|
603
799
|
const frames = [];
|
|
604
800
|
for (const raw of stack.split("\n")) {
|
|
605
801
|
const line = raw.trim();
|
|
606
|
-
if (!line.startsWith("at ")) continue;
|
|
802
|
+
if (line.length > MAX_FRAME_LINE || !line.startsWith("at ")) continue;
|
|
607
803
|
const withFunction = WITH_FUNCTION.exec(line);
|
|
608
804
|
const fn = withFunction?.[1];
|
|
609
805
|
const location = withFunction ? withFunction[2] ?? "" : line.replace(/^at (?:async )?/, "");
|
|
@@ -662,10 +858,6 @@ function safeStringify(value) {
|
|
|
662
858
|
}
|
|
663
859
|
}
|
|
664
860
|
|
|
665
|
-
// src/transport.ts
|
|
666
|
-
var import_node_util2 = require("util");
|
|
667
|
-
var import_node_zlib = require("zlib");
|
|
668
|
-
|
|
669
861
|
// src/util.ts
|
|
670
862
|
var import_node_perf_hooks2 = require("perf_hooks");
|
|
671
863
|
function describeError(error) {
|
|
@@ -675,7 +867,101 @@ function elapsedSince(startedAt) {
|
|
|
675
867
|
return Math.round(import_node_perf_hooks2.performance.now() - startedAt);
|
|
676
868
|
}
|
|
677
869
|
|
|
870
|
+
// src/trace-fetch.ts
|
|
871
|
+
var WRAPPED = /* @__PURE__ */ Symbol.for("@oconde/radar.fetch");
|
|
872
|
+
var REQUEST_ID_HEADER = "x-request-id";
|
|
873
|
+
var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
874
|
+
var TELEGRAM_BOT = /^bot\d{5,}:[\w-]{20,}$/;
|
|
875
|
+
var TOKEN_LIKE = /^[\w\-.~+=]{20,}$/;
|
|
876
|
+
function secretSegment(segment) {
|
|
877
|
+
if (UUID.test(segment)) return false;
|
|
878
|
+
if (TELEGRAM_BOT.test(segment) || looksLikeCredential(segment)) return true;
|
|
879
|
+
return TOKEN_LIKE.test(segment) && /[A-Za-z]/.test(segment) && /\d/.test(segment);
|
|
880
|
+
}
|
|
881
|
+
function outboundPath(pathname, mode) {
|
|
882
|
+
if (mode === "none") return pathname;
|
|
883
|
+
return pathname.split("/").map((segment) => {
|
|
884
|
+
let decoded = segment;
|
|
885
|
+
try {
|
|
886
|
+
decoded = decodeURIComponent(segment);
|
|
887
|
+
} catch {
|
|
888
|
+
decoded = segment;
|
|
889
|
+
}
|
|
890
|
+
return secretSegment(decoded) ? maskValue(decoded, false) : segment;
|
|
891
|
+
}).join("/");
|
|
892
|
+
}
|
|
893
|
+
function failureOf(error) {
|
|
894
|
+
const message = describeError(error);
|
|
895
|
+
const cause = error instanceof Error ? error.cause : void 0;
|
|
896
|
+
const detail = typeof cause?.code === "string" ? cause.code : typeof cause?.message === "string" ? cause.message : "";
|
|
897
|
+
return detail && !message.includes(detail) ? `${message} (${detail})` : message;
|
|
898
|
+
}
|
|
899
|
+
function urlOf(input) {
|
|
900
|
+
try {
|
|
901
|
+
if (typeof input === "string") return new URL(input);
|
|
902
|
+
if (input instanceof URL) return input;
|
|
903
|
+
return new URL(input.url);
|
|
904
|
+
} catch {
|
|
905
|
+
return null;
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
function originOf(value) {
|
|
909
|
+
try {
|
|
910
|
+
return new URL(value).origin;
|
|
911
|
+
} catch {
|
|
912
|
+
return null;
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function targets(url, target) {
|
|
916
|
+
if (target instanceof RegExp) return target.test(url.host);
|
|
917
|
+
const wanted = target.trim().toLowerCase();
|
|
918
|
+
if (wanted.startsWith(".")) return url.hostname.toLowerCase().endsWith(wanted);
|
|
919
|
+
return url.host.toLowerCase() === wanted || url.hostname.toLowerCase() === wanted;
|
|
920
|
+
}
|
|
921
|
+
function isPlainObject(value) {
|
|
922
|
+
if (value === null || typeof value !== "object") return false;
|
|
923
|
+
const prototype = Object.getPrototypeOf(value);
|
|
924
|
+
return prototype === Object.prototype || prototype === null;
|
|
925
|
+
}
|
|
926
|
+
function withRequestId(input, init, requestId) {
|
|
927
|
+
const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
|
|
928
|
+
if (headers.has(REQUEST_ID_HEADER)) return [input, init];
|
|
929
|
+
headers.set(REQUEST_ID_HEADER, requestId);
|
|
930
|
+
if (init === void 0 || isPlainObject(init)) return [input, { ...init, headers }];
|
|
931
|
+
return [new Request(input, init), { headers }];
|
|
932
|
+
}
|
|
933
|
+
function traceFetch(config) {
|
|
934
|
+
const current = globalThis.fetch;
|
|
935
|
+
if (typeof current !== "function" || current[WRAPPED]) return () => void 0;
|
|
936
|
+
const original = current;
|
|
937
|
+
const radarOrigin = originOf(config.endpoint);
|
|
938
|
+
const traced = async (input, init) => {
|
|
939
|
+
const url = urlOf(input);
|
|
940
|
+
if (!url || url.origin === radarOrigin || url.protocol !== "http:" && url.protocol !== "https:") return original(input, init);
|
|
941
|
+
const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
942
|
+
const context = currentContext();
|
|
943
|
+
const [nextInput, nextInit] = context && config.propagateTo.some((target) => targets(url, target)) ? withRequestId(input, init, context.requestId) : [input, init];
|
|
944
|
+
const attrs = { method, host: url.host, path: outboundPath(url.pathname, config.redact) };
|
|
945
|
+
const started = performance.now();
|
|
946
|
+
try {
|
|
947
|
+
const response = await original(nextInput, nextInit);
|
|
948
|
+
config.record(response.status >= 500 ? "error" : response.status >= 400 ? "warn" : "info", { ...attrs, status: response.status, durationMs: elapsedSince(started) });
|
|
949
|
+
return response;
|
|
950
|
+
} catch (error) {
|
|
951
|
+
config.record("error", { ...attrs, error: failureOf(error), durationMs: elapsedSince(started) });
|
|
952
|
+
throw error;
|
|
953
|
+
}
|
|
954
|
+
};
|
|
955
|
+
Object.defineProperty(traced, WRAPPED, { value: original });
|
|
956
|
+
globalThis.fetch = traced;
|
|
957
|
+
return () => {
|
|
958
|
+
if (globalThis.fetch === traced) globalThis.fetch = original;
|
|
959
|
+
};
|
|
960
|
+
}
|
|
961
|
+
|
|
678
962
|
// src/transport.ts
|
|
963
|
+
var import_node_util2 = require("util");
|
|
964
|
+
var import_node_zlib = require("zlib");
|
|
679
965
|
var gzipAsync = (0, import_node_util2.promisify)(import_node_zlib.gzip);
|
|
680
966
|
var MAX_BATCH_BYTES = 9e5;
|
|
681
967
|
var GZIP_THRESHOLD_BYTES = 8192;
|
|
@@ -698,6 +984,7 @@ var Transport = class {
|
|
|
698
984
|
inFlight = null;
|
|
699
985
|
nextAttemptAt = 0;
|
|
700
986
|
backoffMs = 0;
|
|
987
|
+
unauthorizedUntil = 0;
|
|
701
988
|
stopped = false;
|
|
702
989
|
warned = /* @__PURE__ */ new Set();
|
|
703
990
|
timer;
|
|
@@ -787,10 +1074,8 @@ var Transport = class {
|
|
|
787
1074
|
this.nextAttemptAt = Date.now() + Math.max(this.backoffMs, outcome.retryAfterMs ?? 0);
|
|
788
1075
|
return false;
|
|
789
1076
|
case "unauthorized":
|
|
790
|
-
this.droppedCount += events.length
|
|
791
|
-
this.
|
|
792
|
-
this.nextAttemptAt = Date.now() + UNAUTHORIZED_PAUSE_MS;
|
|
793
|
-
this.warn("unauthorized", `chave do Radar recusada (${outcome.status}); envios pausados por 10 minutos`, true);
|
|
1077
|
+
this.droppedCount += events.length;
|
|
1078
|
+
this.pauseUnauthorized(outcome.status);
|
|
794
1079
|
return false;
|
|
795
1080
|
case "rejected":
|
|
796
1081
|
this.droppedCount += events.length;
|
|
@@ -798,6 +1083,47 @@ var Transport = class {
|
|
|
798
1083
|
return true;
|
|
799
1084
|
}
|
|
800
1085
|
}
|
|
1086
|
+
async checkIn(slug, payload) {
|
|
1087
|
+
if (this.stopped || !this.config.checkInBaseUrl || Date.now() < this.unauthorizedUntil) return;
|
|
1088
|
+
let response;
|
|
1089
|
+
try {
|
|
1090
|
+
response = await fetch(this.config.checkInBaseUrl + encodeURIComponent(slug), {
|
|
1091
|
+
method: "POST",
|
|
1092
|
+
headers: this.headers(),
|
|
1093
|
+
body: JSON.stringify(payload),
|
|
1094
|
+
redirect: "error",
|
|
1095
|
+
signal: AbortSignal.timeout(this.config.requestTimeoutMs ?? 5e3)
|
|
1096
|
+
});
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
this.warn("checkin_network", `check-in no Radar falhou: ${describeError(error)}`, false);
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
await response.arrayBuffer().catch(() => void 0);
|
|
1102
|
+
if (response.ok) return;
|
|
1103
|
+
if (response.status === 401 || response.status === 403) {
|
|
1104
|
+
this.pauseUnauthorized(response.status);
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
if (response.status === 404) {
|
|
1108
|
+
this.warn(`checkin_unknown_${slug}`, `tarefa agendada "${slug}" n\xE3o existe no projeto da chave; crie o monitor com esse slug no Radar`, true);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
this.warn(`checkin_rejected_${response.status}`, `check-in recusado pelo Radar (${response.status})`, false);
|
|
1112
|
+
}
|
|
1113
|
+
pauseUnauthorized(status) {
|
|
1114
|
+
this.droppedCount += this.queue.length;
|
|
1115
|
+
this.queue = [];
|
|
1116
|
+
this.unauthorizedUntil = Date.now() + UNAUTHORIZED_PAUSE_MS;
|
|
1117
|
+
this.nextAttemptAt = Math.max(this.nextAttemptAt, this.unauthorizedUntil);
|
|
1118
|
+
this.warn("unauthorized", `chave do Radar recusada (${status}); envios pausados por 10 minutos`, true);
|
|
1119
|
+
}
|
|
1120
|
+
headers() {
|
|
1121
|
+
return {
|
|
1122
|
+
"content-type": "application/json",
|
|
1123
|
+
authorization: `Bearer ${this.config.key}`,
|
|
1124
|
+
"user-agent": `${this.config.sdk.name}/${this.config.sdk.version}`
|
|
1125
|
+
};
|
|
1126
|
+
}
|
|
801
1127
|
requeue(events) {
|
|
802
1128
|
this.queue.unshift(...events);
|
|
803
1129
|
const overflow = this.queue.length - this.maxQueue;
|
|
@@ -809,11 +1135,7 @@ var Transport = class {
|
|
|
809
1135
|
async post(batch) {
|
|
810
1136
|
const json = JSON.stringify(batch);
|
|
811
1137
|
const compress = Buffer.byteLength(json) > GZIP_THRESHOLD_BYTES;
|
|
812
|
-
const headers =
|
|
813
|
-
"content-type": "application/json",
|
|
814
|
-
authorization: `Bearer ${this.config.key}`,
|
|
815
|
-
"user-agent": `${this.config.sdk.name}/${this.config.sdk.version}`
|
|
816
|
-
};
|
|
1138
|
+
const headers = this.headers();
|
|
817
1139
|
if (compress) headers["content-encoding"] = "gzip";
|
|
818
1140
|
let response;
|
|
819
1141
|
try {
|
|
@@ -867,15 +1189,19 @@ function withTimeout(promise, ms) {
|
|
|
867
1189
|
|
|
868
1190
|
// src/version.ts
|
|
869
1191
|
var SDK_NAME = "@oconde/radar";
|
|
870
|
-
var SDK_VERSION = true ? "0.
|
|
1192
|
+
var SDK_VERSION = true ? "0.3.0" : "0.0.0-dev";
|
|
871
1193
|
|
|
872
1194
|
// src/client.ts
|
|
1195
|
+
var MAX_CHECK_IN_DURATION_MS = 7 * 864e5;
|
|
1196
|
+
var MAX_CHECK_IN_ENVIRONMENT = 64;
|
|
1197
|
+
var MAX_CHECK_IN_RELEASE = 128;
|
|
873
1198
|
var RadarClient = class {
|
|
874
1199
|
options = resolveOptions({}, {});
|
|
875
1200
|
transport = null;
|
|
876
1201
|
initialized = false;
|
|
877
1202
|
closed = false;
|
|
878
1203
|
captured = /* @__PURE__ */ new WeakSet();
|
|
1204
|
+
restoreFetch = null;
|
|
879
1205
|
get isEnabled() {
|
|
880
1206
|
return this.transport !== null && !this.closed;
|
|
881
1207
|
}
|
|
@@ -898,10 +1224,19 @@ var RadarClient = class {
|
|
|
898
1224
|
sdk: { name: SDK_NAME, version: SDK_VERSION },
|
|
899
1225
|
environment: this.options.environment,
|
|
900
1226
|
...this.options.release ? { release: this.options.release } : {},
|
|
1227
|
+
checkInBaseUrl: this.options.endpoint + CHECKIN_PATH,
|
|
901
1228
|
onWarning: (message, always) => always ? this.writeWarning(message) : this.debugWarn(message)
|
|
902
1229
|
});
|
|
903
1230
|
}
|
|
904
1231
|
if (this.options.captureUnhandled && (this.transport || this.options.console)) installProcessHandlers(this);
|
|
1232
|
+
if (this.options.traceFetch.enabled && (this.transport || this.options.console)) {
|
|
1233
|
+
this.restoreFetch = traceFetch({
|
|
1234
|
+
endpoint: this.options.endpoint,
|
|
1235
|
+
propagateTo: this.options.traceFetch.propagateTo,
|
|
1236
|
+
redact: this.options.redact,
|
|
1237
|
+
record: (level, attrs) => this.safely(() => this.emitLog(level, "http.client", attrs))
|
|
1238
|
+
});
|
|
1239
|
+
}
|
|
905
1240
|
} catch (error) {
|
|
906
1241
|
this.debugWarn(`radar.init falhou: ${describeError(error)}`);
|
|
907
1242
|
}
|
|
@@ -959,6 +1294,51 @@ var RadarClient = class {
|
|
|
959
1294
|
if (currentContext()) return execute();
|
|
960
1295
|
return runWithContext({ requestId: (0, import_node_crypto3.randomUUID)(), startedAt: import_node_perf_hooks3.performance.now() }, execute);
|
|
961
1296
|
}
|
|
1297
|
+
withContext(fn, options = {}) {
|
|
1298
|
+
return runWithContext({ requestId: requestIdFrom(options.requestId), startedAt: import_node_perf_hooks3.performance.now() }, fn);
|
|
1299
|
+
}
|
|
1300
|
+
async checkIn(slug, status, options = {}) {
|
|
1301
|
+
try {
|
|
1302
|
+
if (!this.transport || this.closed) return;
|
|
1303
|
+
if (typeof slug !== "string" || !CHECKIN_SLUG.test(slug)) {
|
|
1304
|
+
this.debugWarn(`slug de tarefa agendada inv\xE1lido: ${String(slug).slice(0, 80)}`);
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
if (!CHECKIN_STATUSES.includes(status)) return;
|
|
1308
|
+
const checkInId = typeof options.checkInId === "string" && options.checkInId ? options.checkInId.slice(0, LIMITS.checkInIdLength) : void 0;
|
|
1309
|
+
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;
|
|
1310
|
+
const environment = this.options.environment.slice(0, MAX_CHECK_IN_ENVIRONMENT) || "production";
|
|
1311
|
+
const release = this.options.release?.slice(0, MAX_CHECK_IN_RELEASE);
|
|
1312
|
+
const payload = {
|
|
1313
|
+
status,
|
|
1314
|
+
...checkInId ? { checkInId } : {},
|
|
1315
|
+
...durationMs !== void 0 ? { durationMs } : {},
|
|
1316
|
+
environment,
|
|
1317
|
+
...release ? { release } : {}
|
|
1318
|
+
};
|
|
1319
|
+
await this.transport.checkIn(slug, payload);
|
|
1320
|
+
} catch (error) {
|
|
1321
|
+
this.debugWarn(`check-in falhou: ${describeError(error)}`);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
async cron(slug, fn) {
|
|
1325
|
+
const checkInId = (0, import_node_crypto3.randomUUID)();
|
|
1326
|
+
const started = import_node_perf_hooks3.performance.now();
|
|
1327
|
+
const execute = async () => {
|
|
1328
|
+
await this.checkIn(slug, "in_progress", { checkInId });
|
|
1329
|
+
try {
|
|
1330
|
+
const result = await fn();
|
|
1331
|
+
await this.checkIn(slug, "ok", { checkInId, durationMs: elapsedSince(started) });
|
|
1332
|
+
return result;
|
|
1333
|
+
} catch (error) {
|
|
1334
|
+
this.captureError(error, { cron: slug });
|
|
1335
|
+
await this.checkIn(slug, "error", { checkInId, durationMs: elapsedSince(started) });
|
|
1336
|
+
throw error;
|
|
1337
|
+
}
|
|
1338
|
+
};
|
|
1339
|
+
if (currentContext()) return execute();
|
|
1340
|
+
return this.withContext(execute);
|
|
1341
|
+
}
|
|
962
1342
|
middleware() {
|
|
963
1343
|
return createMiddleware(this);
|
|
964
1344
|
}
|
|
@@ -976,7 +1356,7 @@ var RadarClient = class {
|
|
|
976
1356
|
() => this.emitLog(level, "http.request", {
|
|
977
1357
|
method: (req.method ?? "GET").toUpperCase(),
|
|
978
1358
|
...route ? { route } : {},
|
|
979
|
-
...this.options.requestDetail === "full" ? { path: redactPath(requestPath(req), req
|
|
1359
|
+
...this.options.requestDetail === "full" ? { path: redactPath(requestPath(req), pathParams(req, requestPath(req)), this.options.redact) } : {},
|
|
980
1360
|
status,
|
|
981
1361
|
durationMs: elapsedSince(context.startedAt)
|
|
982
1362
|
})
|
|
@@ -993,6 +1373,8 @@ var RadarClient = class {
|
|
|
993
1373
|
async close(timeoutMs = 2e3) {
|
|
994
1374
|
if (this.closed) return;
|
|
995
1375
|
await this.flush(timeoutMs);
|
|
1376
|
+
this.restoreFetch?.();
|
|
1377
|
+
this.restoreFetch = null;
|
|
996
1378
|
this.closed = true;
|
|
997
1379
|
this.transport?.stop();
|
|
998
1380
|
}
|
|
@@ -1013,31 +1395,40 @@ var RadarClient = class {
|
|
|
1013
1395
|
this.print(event);
|
|
1014
1396
|
this.transport?.enqueue(event);
|
|
1015
1397
|
}
|
|
1398
|
+
captureException(exception, options = {}) {
|
|
1399
|
+
this.safely(() => this.emitException(exception, options));
|
|
1400
|
+
}
|
|
1016
1401
|
emitError(error, data, level, handled) {
|
|
1017
1402
|
if (this.closed) return;
|
|
1018
1403
|
if (typeof error === "object" && error !== null) {
|
|
1019
1404
|
if (this.captured.has(error)) return;
|
|
1020
1405
|
this.captured.add(error);
|
|
1021
1406
|
}
|
|
1407
|
+
this.emitException(exceptionInfo(error), { level, handled, ...data !== void 0 ? { attrs: data } : {} }, error instanceof Error ? error.stack : void 0);
|
|
1408
|
+
}
|
|
1409
|
+
emitException(exception, options, stack) {
|
|
1410
|
+
if (this.closed) return;
|
|
1022
1411
|
const context = currentContext();
|
|
1023
1412
|
const mode = this.options.redact;
|
|
1413
|
+
const request = options.request === null ? void 0 : options.request ?? (context?.req ? requestInfo(context.req, mode, { route: context.route }, this.options.requestDetail) : void 0);
|
|
1414
|
+
const runtime = options.runtime === void 0 ? runtimeInfo() : options.runtime;
|
|
1024
1415
|
const event = {
|
|
1025
1416
|
type: "error",
|
|
1026
1417
|
ts: Date.now(),
|
|
1027
|
-
level,
|
|
1028
|
-
handled,
|
|
1029
|
-
exception
|
|
1418
|
+
level: options.level ?? "error",
|
|
1419
|
+
handled: options.handled ?? true,
|
|
1420
|
+
exception,
|
|
1030
1421
|
...context ? { requestId: context.requestId } : {},
|
|
1031
|
-
...
|
|
1422
|
+
...request ? { request } : {},
|
|
1032
1423
|
...context?.user ? { user: context.user } : {},
|
|
1033
|
-
runtime:
|
|
1034
|
-
...
|
|
1424
|
+
...runtime ? { runtime } : {},
|
|
1425
|
+
...options.attrs !== void 0 ? { attrs: prepareAttrs(options.attrs, mode) } : {}
|
|
1035
1426
|
};
|
|
1036
|
-
this.print(event,
|
|
1427
|
+
this.print(event, stack);
|
|
1037
1428
|
if (!this.transport) return;
|
|
1038
1429
|
this.transport.enqueue(
|
|
1039
|
-
() => enrichException(event.exception).then(
|
|
1040
|
-
(
|
|
1430
|
+
() => enrichException(event.exception, process.cwd(), options.enrich).then(
|
|
1431
|
+
(enriched) => ({ ...event, exception: enriched }),
|
|
1041
1432
|
() => event
|
|
1042
1433
|
)
|
|
1043
1434
|
);
|