@oconde/radar 0.2.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 +10 -0
- package/README.md +8 -0
- package/dist/{client-CZHtsQAd.d.cts → client-BB-to8cr.d.cts} +11 -0
- package/dist/{client-DbBsXgpj.d.ts → client-BS798K3S.d.ts} +11 -0
- package/dist/index.cjs +177 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +186 -23
- package/dist/index.js.map +1 -1
- package/dist/nest/index.cjs +177 -14
- 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 +186 -23
- package/dist/nest/index.js.map +1 -1
- package/dist/sveltekit/index.cjs +179 -16
- package/dist/sveltekit/index.cjs.map +1 -1
- package/dist/sveltekit/index.d.cts +1 -1
- package/dist/sveltekit/index.d.ts +1 -1
- package/dist/sveltekit/index.js +190 -27
- package/dist/sveltekit/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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";
|
|
@@ -55,7 +55,7 @@ function requestIdFrom(incoming) {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
// src/express.ts
|
|
58
|
-
import { performance } from "perf_hooks";
|
|
58
|
+
import { performance as performance2 } from "perf_hooks";
|
|
59
59
|
|
|
60
60
|
// src/redact.ts
|
|
61
61
|
import { createHash, createHmac, randomBytes } from "crypto";
|
|
@@ -255,7 +255,10 @@ function requestPath(req) {
|
|
|
255
255
|
}
|
|
256
256
|
function requestRoute(req, route) {
|
|
257
257
|
if (route) return route;
|
|
258
|
-
|
|
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}`;
|
|
259
262
|
}
|
|
260
263
|
var ROUTE_PARAM = /^:([A-Za-z_$][\w$]*)/;
|
|
261
264
|
function pathSegments(value) {
|
|
@@ -375,7 +378,7 @@ function createMiddleware(logger) {
|
|
|
375
378
|
let context;
|
|
376
379
|
try {
|
|
377
380
|
const requestId = requestIdFrom(headerValue(req.headers["x-request-id"]));
|
|
378
|
-
context = { requestId, startedAt:
|
|
381
|
+
context = { requestId, startedAt: performance2.now(), req };
|
|
379
382
|
res.setHeader("x-request-id", requestId);
|
|
380
383
|
if (logger.shouldLogRequest(req)) res.once("finish", () => logger.logHttpRequest(context, res.statusCode));
|
|
381
384
|
} catch {
|
|
@@ -389,6 +392,12 @@ function createMiddleware(logger) {
|
|
|
389
392
|
// src/options.ts
|
|
390
393
|
var DEFAULT_ENDPOINT = "https://radar-ingest.oconde.dev";
|
|
391
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
|
+
}
|
|
392
401
|
function resolveOptions(options = {}, env = process.env) {
|
|
393
402
|
const key = options.key?.trim() || void 0;
|
|
394
403
|
return {
|
|
@@ -404,6 +413,7 @@ function resolveOptions(options = {}, env = process.env) {
|
|
|
404
413
|
logRequests: options.logRequests ?? true,
|
|
405
414
|
ignorePaths: options.ignorePaths ?? ["/healthz", "/health"],
|
|
406
415
|
captureUnhandled: options.captureUnhandled ?? true,
|
|
416
|
+
traceFetch: resolveTraceFetch(options.traceFetch),
|
|
407
417
|
debug: options.debug ?? false
|
|
408
418
|
};
|
|
409
419
|
}
|
|
@@ -471,6 +481,7 @@ var IDENTIFIER = "[A-Za-z_$][\\w$]*";
|
|
|
471
481
|
var MAX_LINES_UP = 400;
|
|
472
482
|
var MAX_HEADER_LINES = 6;
|
|
473
483
|
var MAX_NAME_LENGTH = 100;
|
|
484
|
+
var MAX_COMMENT_LINES = 40;
|
|
474
485
|
var CONTROL_BLOCK = /^\s*(?:\}\s*)?(?:if|else|for|while|do|switch|try|catch|finally|with)\b/;
|
|
475
486
|
var CLASS_BODY = new RegExp(`\\bclass\\s+${IDENTIFIER}`);
|
|
476
487
|
var MARKUP_BOUNDARY = /<\/?(?:script|style|template)\b/i;
|
|
@@ -483,8 +494,58 @@ var NAME_PATTERNS = [
|
|
|
483
494
|
];
|
|
484
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*[^{]+)?$`);
|
|
485
496
|
var ANONYMOUS_FUNCTION = /\bfunction\b|=>/;
|
|
486
|
-
|
|
487
|
-
|
|
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;
|
|
488
549
|
}
|
|
489
550
|
function parenBalance(text) {
|
|
490
551
|
let balance = 0;
|
|
@@ -494,11 +555,11 @@ function parenBalance(text) {
|
|
|
494
555
|
}
|
|
495
556
|
return balance;
|
|
496
557
|
}
|
|
497
|
-
function headerOf(
|
|
558
|
+
function headerOf(code, index, prefix) {
|
|
498
559
|
let header = prefix;
|
|
499
560
|
for (let previous = index - 1; previous >= 0 && previous >= index - MAX_HEADER_LINES; previous--) {
|
|
500
561
|
if (header.trim() !== "" && parenBalance(header) >= 0) break;
|
|
501
|
-
header = `${
|
|
562
|
+
header = `${code[previous] ?? ""} ${header}`;
|
|
502
563
|
}
|
|
503
564
|
return header;
|
|
504
565
|
}
|
|
@@ -513,11 +574,12 @@ function nameIn(header) {
|
|
|
513
574
|
function enclosingFunctionName(lines, line, column) {
|
|
514
575
|
const start = line - 1;
|
|
515
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);
|
|
516
579
|
let depth = 0;
|
|
517
580
|
for (let index = start; index >= 0 && index >= start - MAX_LINES_UP; index--) {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
const code = codeOnly(index === start && column ? raw.slice(0, Math.max(0, column - 1)) : raw);
|
|
581
|
+
if (index < start && MARKUP_BOUNDARY.test(lines[index] ?? "")) return void 0;
|
|
582
|
+
const code = cleaned[index - from] ?? "";
|
|
521
583
|
for (let position = code.length - 1; position >= 0; position--) {
|
|
522
584
|
const char = code[position];
|
|
523
585
|
if (char === "}") {
|
|
@@ -527,7 +589,7 @@ function enclosingFunctionName(lines, line, column) {
|
|
|
527
589
|
depth -= 1;
|
|
528
590
|
continue;
|
|
529
591
|
}
|
|
530
|
-
const header = headerOf(
|
|
592
|
+
const header = headerOf(cleaned, index - from, code.slice(0, position));
|
|
531
593
|
if (CLASS_BODY.test(header)) return void 0;
|
|
532
594
|
if (CONTROL_BLOCK.test(header)) continue;
|
|
533
595
|
const name = nameIn(header);
|
|
@@ -769,20 +831,110 @@ function safeStringify(value) {
|
|
|
769
831
|
}
|
|
770
832
|
}
|
|
771
833
|
|
|
772
|
-
// src/transport.ts
|
|
773
|
-
import { promisify } from "util";
|
|
774
|
-
import { gzip } from "zlib";
|
|
775
|
-
|
|
776
834
|
// src/util.ts
|
|
777
|
-
import { performance as
|
|
835
|
+
import { performance as performance3 } from "perf_hooks";
|
|
778
836
|
function describeError(error) {
|
|
779
837
|
return error instanceof Error ? error.message : String(error);
|
|
780
838
|
}
|
|
781
839
|
function elapsedSince(startedAt) {
|
|
782
|
-
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
|
+
};
|
|
783
933
|
}
|
|
784
934
|
|
|
785
935
|
// src/transport.ts
|
|
936
|
+
import { promisify } from "util";
|
|
937
|
+
import { gzip } from "zlib";
|
|
786
938
|
var gzipAsync = promisify(gzip);
|
|
787
939
|
var MAX_BATCH_BYTES = 9e5;
|
|
788
940
|
var GZIP_THRESHOLD_BYTES = 8192;
|
|
@@ -1010,7 +1162,7 @@ function withTimeout(promise, ms) {
|
|
|
1010
1162
|
|
|
1011
1163
|
// src/version.ts
|
|
1012
1164
|
var SDK_NAME = "@oconde/radar";
|
|
1013
|
-
var SDK_VERSION = true ? "0.
|
|
1165
|
+
var SDK_VERSION = true ? "0.3.0" : "0.0.0-dev";
|
|
1014
1166
|
|
|
1015
1167
|
// src/client.ts
|
|
1016
1168
|
var MAX_CHECK_IN_DURATION_MS = 7 * 864e5;
|
|
@@ -1022,6 +1174,7 @@ var RadarClient = class {
|
|
|
1022
1174
|
initialized = false;
|
|
1023
1175
|
closed = false;
|
|
1024
1176
|
captured = /* @__PURE__ */ new WeakSet();
|
|
1177
|
+
restoreFetch = null;
|
|
1025
1178
|
get isEnabled() {
|
|
1026
1179
|
return this.transport !== null && !this.closed;
|
|
1027
1180
|
}
|
|
@@ -1049,6 +1202,14 @@ var RadarClient = class {
|
|
|
1049
1202
|
});
|
|
1050
1203
|
}
|
|
1051
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
|
+
}
|
|
1052
1213
|
} catch (error) {
|
|
1053
1214
|
this.debugWarn(`radar.init falhou: ${describeError(error)}`);
|
|
1054
1215
|
}
|
|
@@ -1092,7 +1253,7 @@ var RadarClient = class {
|
|
|
1092
1253
|
}
|
|
1093
1254
|
async track(name, fn, data) {
|
|
1094
1255
|
const execute = async () => {
|
|
1095
|
-
const started =
|
|
1256
|
+
const started = performance4.now();
|
|
1096
1257
|
try {
|
|
1097
1258
|
const result = await fn();
|
|
1098
1259
|
this.info(name, { ...data, durationMs: elapsedSince(started), ok: true });
|
|
@@ -1104,10 +1265,10 @@ var RadarClient = class {
|
|
|
1104
1265
|
}
|
|
1105
1266
|
};
|
|
1106
1267
|
if (currentContext()) return execute();
|
|
1107
|
-
return runWithContext({ requestId: randomUUID2(), startedAt:
|
|
1268
|
+
return runWithContext({ requestId: randomUUID2(), startedAt: performance4.now() }, execute);
|
|
1108
1269
|
}
|
|
1109
1270
|
withContext(fn, options = {}) {
|
|
1110
|
-
return runWithContext({ requestId: requestIdFrom(options.requestId), startedAt:
|
|
1271
|
+
return runWithContext({ requestId: requestIdFrom(options.requestId), startedAt: performance4.now() }, fn);
|
|
1111
1272
|
}
|
|
1112
1273
|
async checkIn(slug, status, options = {}) {
|
|
1113
1274
|
try {
|
|
@@ -1135,7 +1296,7 @@ var RadarClient = class {
|
|
|
1135
1296
|
}
|
|
1136
1297
|
async cron(slug, fn) {
|
|
1137
1298
|
const checkInId = randomUUID2();
|
|
1138
|
-
const started =
|
|
1299
|
+
const started = performance4.now();
|
|
1139
1300
|
const execute = async () => {
|
|
1140
1301
|
await this.checkIn(slug, "in_progress", { checkInId });
|
|
1141
1302
|
try {
|
|
@@ -1185,6 +1346,8 @@ var RadarClient = class {
|
|
|
1185
1346
|
async close(timeoutMs = 2e3) {
|
|
1186
1347
|
if (this.closed) return;
|
|
1187
1348
|
await this.flush(timeoutMs);
|
|
1349
|
+
this.restoreFetch?.();
|
|
1350
|
+
this.restoreFetch = null;
|
|
1188
1351
|
this.closed = true;
|
|
1189
1352
|
this.transport?.stop();
|
|
1190
1353
|
}
|