@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.
@@ -1,5 +1,5 @@
1
1
  import { RequestEvent, RequestHandler, Handle, HandleServerError } from '@sveltejs/kit';
2
- import { R as RadarClient } from '../client-CZHtsQAd.cjs';
2
+ import { R as RadarClient } from '../client-BB-to8cr.cjs';
3
3
  import '../protocol/index.cjs';
4
4
 
5
5
  declare function moveClientSourceMaps(buildDir?: string): number;
@@ -1,5 +1,5 @@
1
1
  import { RequestEvent, RequestHandler, Handle, HandleServerError } from '@sveltejs/kit';
2
- import { R as RadarClient } from '../client-DbBsXgpj.js';
2
+ import { R as RadarClient } from '../client-BS798K3S.js';
3
3
  import '../protocol/index.js';
4
4
 
5
5
  declare function moveClientSourceMaps(buildDir?: string): number;
@@ -1,7 +1,7 @@
1
1
  // src/sveltekit/index.ts
2
2
  import { existsSync as existsSync2 } from "fs";
3
3
  import { resolve as resolve3, sep as sep3 } from "path";
4
- import { performance as performance4 } from "perf_hooks";
4
+ import { performance as performance5 } from "perf_hooks";
5
5
 
6
6
  // src/context.ts
7
7
  import { AsyncLocalStorage } from "async_hooks";
@@ -57,10 +57,10 @@ function requestIdFrom(incoming) {
57
57
  // src/client.ts
58
58
  import { randomUUID as randomUUID2 } from "crypto";
59
59
  import { hostname } from "os";
60
- import { performance as performance3 } from "perf_hooks";
60
+ import { performance as performance4 } from "perf_hooks";
61
61
 
62
62
  // src/express.ts
63
- import { performance } from "perf_hooks";
63
+ import { performance as performance2 } from "perf_hooks";
64
64
 
65
65
  // src/redact.ts
66
66
  import { createHash, createHmac, randomBytes } from "crypto";
@@ -260,7 +260,10 @@ function requestPath(req) {
260
260
  }
261
261
  function requestRoute(req, route) {
262
262
  if (route) return route;
263
- return typeof req.route?.path === "string" ? req.route.path : void 0;
263
+ if (typeof req.route?.path !== "string") return void 0;
264
+ const base = typeof req.baseUrl === "string" ? req.baseUrl.replace(/\/+$/, "") : "";
265
+ if (!base) return req.route.path;
266
+ return req.route.path === "/" ? base : `${base}${req.route.path}`;
264
267
  }
265
268
  var ROUTE_PARAM = /^:([A-Za-z_$][\w$]*)/;
266
269
  function pathSegments(value) {
@@ -380,7 +383,7 @@ function createMiddleware(logger) {
380
383
  let context;
381
384
  try {
382
385
  const requestId = requestIdFrom(headerValue(req.headers["x-request-id"]));
383
- context = { requestId, startedAt: performance.now(), req };
386
+ context = { requestId, startedAt: performance2.now(), req };
384
387
  res.setHeader("x-request-id", requestId);
385
388
  if (logger.shouldLogRequest(req)) res.once("finish", () => logger.logHttpRequest(context, res.statusCode));
386
389
  } catch {
@@ -394,6 +397,12 @@ function createMiddleware(logger) {
394
397
  // src/options.ts
395
398
  var DEFAULT_ENDPOINT = "https://radar-ingest.oconde.dev";
396
399
  var LEVELS = ["debug", "info", "warn", "error"];
400
+ function resolveTraceFetch(value) {
401
+ if (!value) return { enabled: false, propagateTo: [] };
402
+ if (value === true) return { enabled: true, propagateTo: [] };
403
+ const targets2 = Array.isArray(value.propagateTo) ? value.propagateTo : [];
404
+ return { enabled: true, propagateTo: targets2.filter((target) => typeof target === "string" ? target.trim() !== "" : target instanceof RegExp) };
405
+ }
397
406
  function resolveOptions(options = {}, env = process.env) {
398
407
  const key = options.key?.trim() || void 0;
399
408
  return {
@@ -409,6 +418,7 @@ function resolveOptions(options = {}, env = process.env) {
409
418
  logRequests: options.logRequests ?? true,
410
419
  ignorePaths: options.ignorePaths ?? ["/healthz", "/health"],
411
420
  captureUnhandled: options.captureUnhandled ?? true,
421
+ traceFetch: resolveTraceFetch(options.traceFetch),
412
422
  debug: options.debug ?? false
413
423
  };
414
424
  }
@@ -476,6 +486,7 @@ var IDENTIFIER = "[A-Za-z_$][\\w$]*";
476
486
  var MAX_LINES_UP = 400;
477
487
  var MAX_HEADER_LINES = 6;
478
488
  var MAX_NAME_LENGTH = 100;
489
+ var MAX_COMMENT_LINES = 40;
479
490
  var CONTROL_BLOCK = /^\s*(?:\}\s*)?(?:if|else|for|while|do|switch|try|catch|finally|with)\b/;
480
491
  var CLASS_BODY = new RegExp(`\\bclass\\s+${IDENTIFIER}`);
481
492
  var MARKUP_BOUNDARY = /<\/?(?:script|style|template)\b/i;
@@ -488,8 +499,58 @@ var NAME_PATTERNS = [
488
499
  ];
489
500
  var METHOD_PATTERN = new RegExp(`^\\s*(?:(?:export|default|public|private|protected|static|async|override|readonly|get|set)\\s+)*\\*?\\s*(${IDENTIFIER})\\s*(?:<[^>]*>)?\\s*\\([^]*\\)\\s*(?::\\s*[^{]+)?$`);
490
501
  var ANONYMOUS_FUNCTION = /\bfunction\b|=>/;
491
- function codeOnly(text) {
492
- return text.replace(/(["'`])(?:\\.|(?!\1)[^\\])*\1/g, '""').replace(/\/\*.*?\*\//g, "").replace(/(^|[^:])\/\/.*$/, "$1");
502
+ var STRING_LITERAL = /(["'`])(?:\\.|(?!\1)[^\\])*\1/g;
503
+ function lineCommentAt(text, from) {
504
+ for (let index = text.indexOf("//", from); index >= 0; index = text.indexOf("//", index + 1)) {
505
+ if (index === 0 || text[index - 1] !== ":") return index;
506
+ }
507
+ return -1;
508
+ }
509
+ function blockCommentAt(text, from) {
510
+ for (let index = text.indexOf("/*", from); index >= 0; index = text.indexOf("/*", index + 1)) {
511
+ if (index === 0 || text[index - 1] !== "\\") return index;
512
+ }
513
+ return -1;
514
+ }
515
+ function stripComments(text, insideComment, closesLater) {
516
+ let code = "";
517
+ let position = 0;
518
+ let inside = insideComment;
519
+ while (position < text.length) {
520
+ if (inside) {
521
+ const end = text.indexOf("*/", position);
522
+ if (end < 0) return { code, insideComment: true };
523
+ inside = false;
524
+ position = end + 2;
525
+ continue;
526
+ }
527
+ const block = blockCommentAt(text, position);
528
+ const line = lineCommentAt(text, position);
529
+ if (line >= 0 && (block < 0 || line < block)) return { code: code + text.slice(position, line), insideComment: false };
530
+ if (block < 0) return { code: code + text.slice(position), insideComment: false };
531
+ if (text.indexOf("*/", block + 2) < 0 && !closesLater()) {
532
+ code += text.slice(position, block + 2);
533
+ position = block + 2;
534
+ continue;
535
+ }
536
+ code += text.slice(position, block);
537
+ inside = true;
538
+ position = block + 2;
539
+ }
540
+ return { code, insideComment: inside };
541
+ }
542
+ function codeLines(lines, from, start, column) {
543
+ const plain = lines.slice(from, start + 1).map((line) => line.replace(STRING_LITERAL, '""'));
544
+ const closesWithin = (index) => plain.slice(index + 1, index + 1 + MAX_COMMENT_LINES).some((line) => line.includes("*/"));
545
+ const result = [];
546
+ let insideComment = false;
547
+ for (let index = 0; index < plain.length; index++) {
548
+ const text = index === plain.length - 1 && column ? (lines[start] ?? "").slice(0, Math.max(0, column - 1)).replace(STRING_LITERAL, '""') : plain[index] ?? "";
549
+ const stripped = stripComments(text, insideComment, () => closesWithin(index));
550
+ insideComment = stripped.insideComment;
551
+ result.push(stripped.code);
552
+ }
553
+ return result;
493
554
  }
494
555
  function parenBalance(text) {
495
556
  let balance = 0;
@@ -499,11 +560,11 @@ function parenBalance(text) {
499
560
  }
500
561
  return balance;
501
562
  }
502
- function headerOf(lines, index, prefix) {
563
+ function headerOf(code, index, prefix) {
503
564
  let header = prefix;
504
565
  for (let previous = index - 1; previous >= 0 && previous >= index - MAX_HEADER_LINES; previous--) {
505
566
  if (header.trim() !== "" && parenBalance(header) >= 0) break;
506
- header = `${codeOnly(lines[previous] ?? "")} ${header}`;
567
+ header = `${code[previous] ?? ""} ${header}`;
507
568
  }
508
569
  return header;
509
570
  }
@@ -518,11 +579,12 @@ function nameIn(header) {
518
579
  function enclosingFunctionName(lines, line, column) {
519
580
  const start = line - 1;
520
581
  if (start < 0 || start >= lines.length) return void 0;
582
+ const from = Math.max(0, start - MAX_LINES_UP - MAX_HEADER_LINES);
583
+ const cleaned = codeLines(lines, from, start, column);
521
584
  let depth = 0;
522
585
  for (let index = start; index >= 0 && index >= start - MAX_LINES_UP; index--) {
523
- const raw = lines[index] ?? "";
524
- if (index < start && MARKUP_BOUNDARY.test(raw)) return void 0;
525
- const code = codeOnly(index === start && column ? raw.slice(0, Math.max(0, column - 1)) : raw);
586
+ if (index < start && MARKUP_BOUNDARY.test(lines[index] ?? "")) return void 0;
587
+ const code = cleaned[index - from] ?? "";
526
588
  for (let position = code.length - 1; position >= 0; position--) {
527
589
  const char = code[position];
528
590
  if (char === "}") {
@@ -532,7 +594,7 @@ function enclosingFunctionName(lines, line, column) {
532
594
  depth -= 1;
533
595
  continue;
534
596
  }
535
- const header = headerOf(lines, index, code.slice(0, position));
597
+ const header = headerOf(cleaned, index - from, code.slice(0, position));
536
598
  if (CLASS_BODY.test(header)) return void 0;
537
599
  if (CONTROL_BLOCK.test(header)) continue;
538
600
  const name = nameIn(header);
@@ -797,20 +859,110 @@ function safeStringify(value) {
797
859
  }
798
860
  }
799
861
 
800
- // src/transport.ts
801
- import { promisify } from "util";
802
- import { gzip } from "zlib";
803
-
804
862
  // src/util.ts
805
- import { performance as performance2 } from "perf_hooks";
863
+ import { performance as performance3 } from "perf_hooks";
806
864
  function describeError(error) {
807
865
  return error instanceof Error ? error.message : String(error);
808
866
  }
809
867
  function elapsedSince(startedAt) {
810
- return Math.round(performance2.now() - startedAt);
868
+ return Math.round(performance3.now() - startedAt);
869
+ }
870
+
871
+ // src/trace-fetch.ts
872
+ var WRAPPED = /* @__PURE__ */ Symbol.for("@oconde/radar.fetch");
873
+ var REQUEST_ID_HEADER = "x-request-id";
874
+ var UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
875
+ var TELEGRAM_BOT = /^bot\d{5,}:[\w-]{20,}$/;
876
+ var TOKEN_LIKE = /^[\w\-.~+=]{20,}$/;
877
+ function secretSegment(segment) {
878
+ if (UUID.test(segment)) return false;
879
+ if (TELEGRAM_BOT.test(segment) || looksLikeCredential(segment)) return true;
880
+ return TOKEN_LIKE.test(segment) && /[A-Za-z]/.test(segment) && /\d/.test(segment);
881
+ }
882
+ function outboundPath(pathname, mode) {
883
+ if (mode === "none") return pathname;
884
+ return pathname.split("/").map((segment) => {
885
+ let decoded = segment;
886
+ try {
887
+ decoded = decodeURIComponent(segment);
888
+ } catch {
889
+ decoded = segment;
890
+ }
891
+ return secretSegment(decoded) ? maskValue(decoded, false) : segment;
892
+ }).join("/");
893
+ }
894
+ function failureOf(error) {
895
+ const message = describeError(error);
896
+ const cause = error instanceof Error ? error.cause : void 0;
897
+ const detail = typeof cause?.code === "string" ? cause.code : typeof cause?.message === "string" ? cause.message : "";
898
+ return detail && !message.includes(detail) ? `${message} (${detail})` : message;
899
+ }
900
+ function urlOf(input) {
901
+ try {
902
+ if (typeof input === "string") return new URL(input);
903
+ if (input instanceof URL) return input;
904
+ return new URL(input.url);
905
+ } catch {
906
+ return null;
907
+ }
908
+ }
909
+ function originOf(value) {
910
+ try {
911
+ return new URL(value).origin;
912
+ } catch {
913
+ return null;
914
+ }
915
+ }
916
+ function targets(url, target) {
917
+ if (target instanceof RegExp) return target.test(url.host);
918
+ const wanted = target.trim().toLowerCase();
919
+ if (wanted.startsWith(".")) return url.hostname.toLowerCase().endsWith(wanted);
920
+ return url.host.toLowerCase() === wanted || url.hostname.toLowerCase() === wanted;
921
+ }
922
+ function isPlainObject(value) {
923
+ if (value === null || typeof value !== "object") return false;
924
+ const prototype = Object.getPrototypeOf(value);
925
+ return prototype === Object.prototype || prototype === null;
926
+ }
927
+ function withRequestId(input, init, requestId) {
928
+ const headers = new Headers(init?.headers ?? (input instanceof Request ? input.headers : void 0));
929
+ if (headers.has(REQUEST_ID_HEADER)) return [input, init];
930
+ headers.set(REQUEST_ID_HEADER, requestId);
931
+ if (init === void 0 || isPlainObject(init)) return [input, { ...init, headers }];
932
+ return [new Request(input, init), { headers }];
933
+ }
934
+ function traceFetch(config) {
935
+ const current = globalThis.fetch;
936
+ if (typeof current !== "function" || current[WRAPPED]) return () => void 0;
937
+ const original = current;
938
+ const radarOrigin = originOf(config.endpoint);
939
+ const traced = async (input, init) => {
940
+ const url = urlOf(input);
941
+ if (!url || url.origin === radarOrigin || url.protocol !== "http:" && url.protocol !== "https:") return original(input, init);
942
+ const method = (init?.method ?? (input instanceof Request ? input.method : "GET")).toUpperCase();
943
+ const context = currentContext();
944
+ const [nextInput, nextInit] = context && config.propagateTo.some((target) => targets(url, target)) ? withRequestId(input, init, context.requestId) : [input, init];
945
+ const attrs = { method, host: url.host, path: outboundPath(url.pathname, config.redact) };
946
+ const started = performance.now();
947
+ try {
948
+ const response = await original(nextInput, nextInit);
949
+ config.record(response.status >= 500 ? "error" : response.status >= 400 ? "warn" : "info", { ...attrs, status: response.status, durationMs: elapsedSince(started) });
950
+ return response;
951
+ } catch (error) {
952
+ config.record("error", { ...attrs, error: failureOf(error), durationMs: elapsedSince(started) });
953
+ throw error;
954
+ }
955
+ };
956
+ Object.defineProperty(traced, WRAPPED, { value: original });
957
+ globalThis.fetch = traced;
958
+ return () => {
959
+ if (globalThis.fetch === traced) globalThis.fetch = original;
960
+ };
811
961
  }
812
962
 
813
963
  // src/transport.ts
964
+ import { promisify } from "util";
965
+ import { gzip } from "zlib";
814
966
  var gzipAsync = promisify(gzip);
815
967
  var MAX_BATCH_BYTES = 9e5;
816
968
  var GZIP_THRESHOLD_BYTES = 8192;
@@ -1038,7 +1190,7 @@ function withTimeout(promise, ms) {
1038
1190
 
1039
1191
  // src/version.ts
1040
1192
  var SDK_NAME = "@oconde/radar";
1041
- var SDK_VERSION = true ? "0.2.0" : "0.0.0-dev";
1193
+ var SDK_VERSION = true ? "0.3.0" : "0.0.0-dev";
1042
1194
 
1043
1195
  // src/client.ts
1044
1196
  var MAX_CHECK_IN_DURATION_MS = 7 * 864e5;
@@ -1050,6 +1202,7 @@ var RadarClient = class {
1050
1202
  initialized = false;
1051
1203
  closed = false;
1052
1204
  captured = /* @__PURE__ */ new WeakSet();
1205
+ restoreFetch = null;
1053
1206
  get isEnabled() {
1054
1207
  return this.transport !== null && !this.closed;
1055
1208
  }
@@ -1077,6 +1230,14 @@ var RadarClient = class {
1077
1230
  });
1078
1231
  }
1079
1232
  if (this.options.captureUnhandled && (this.transport || this.options.console)) installProcessHandlers(this);
1233
+ if (this.options.traceFetch.enabled && (this.transport || this.options.console)) {
1234
+ this.restoreFetch = traceFetch({
1235
+ endpoint: this.options.endpoint,
1236
+ propagateTo: this.options.traceFetch.propagateTo,
1237
+ redact: this.options.redact,
1238
+ record: (level, attrs) => this.safely(() => this.emitLog(level, "http.client", attrs))
1239
+ });
1240
+ }
1080
1241
  } catch (error) {
1081
1242
  this.debugWarn(`radar.init falhou: ${describeError(error)}`);
1082
1243
  }
@@ -1120,7 +1281,7 @@ var RadarClient = class {
1120
1281
  }
1121
1282
  async track(name, fn, data) {
1122
1283
  const execute = async () => {
1123
- const started = performance3.now();
1284
+ const started = performance4.now();
1124
1285
  try {
1125
1286
  const result = await fn();
1126
1287
  this.info(name, { ...data, durationMs: elapsedSince(started), ok: true });
@@ -1132,10 +1293,10 @@ var RadarClient = class {
1132
1293
  }
1133
1294
  };
1134
1295
  if (currentContext()) return execute();
1135
- return runWithContext({ requestId: randomUUID2(), startedAt: performance3.now() }, execute);
1296
+ return runWithContext({ requestId: randomUUID2(), startedAt: performance4.now() }, execute);
1136
1297
  }
1137
1298
  withContext(fn, options = {}) {
1138
- return runWithContext({ requestId: requestIdFrom(options.requestId), startedAt: performance3.now() }, fn);
1299
+ return runWithContext({ requestId: requestIdFrom(options.requestId), startedAt: performance4.now() }, fn);
1139
1300
  }
1140
1301
  async checkIn(slug, status, options = {}) {
1141
1302
  try {
@@ -1163,7 +1324,7 @@ var RadarClient = class {
1163
1324
  }
1164
1325
  async cron(slug, fn) {
1165
1326
  const checkInId = randomUUID2();
1166
- const started = performance3.now();
1327
+ const started = performance4.now();
1167
1328
  const execute = async () => {
1168
1329
  await this.checkIn(slug, "in_progress", { checkInId });
1169
1330
  try {
@@ -1213,6 +1374,8 @@ var RadarClient = class {
1213
1374
  async close(timeoutMs = 2e3) {
1214
1375
  if (this.closed) return;
1215
1376
  await this.flush(timeoutMs);
1377
+ this.restoreFetch?.();
1378
+ this.restoreFetch = null;
1216
1379
  this.closed = true;
1217
1380
  this.transport?.stop();
1218
1381
  }
@@ -1594,9 +1757,9 @@ function contextFor2(event, chooseRequestId) {
1594
1757
  };
1595
1758
  const route = routeName(event.route.id);
1596
1759
  const chosen = chooseRequestId?.(event);
1597
- return { requestId: requestIdFrom(chosen || event.request.headers.get("x-request-id")), startedAt: performance4.now(), req, ...route ? { route } : {} };
1760
+ return { requestId: requestIdFrom(chosen || event.request.headers.get("x-request-id")), startedAt: performance5.now(), req, ...route ? { route } : {} };
1598
1761
  }
1599
- function withRequestId(response, requestId) {
1762
+ function withRequestId2(response, requestId) {
1600
1763
  try {
1601
1764
  response.headers.set("x-request-id", requestId);
1602
1765
  return response;
@@ -1628,7 +1791,7 @@ function radarHandle(options = {}) {
1628
1791
  if (client.shouldLogRequest(req)) client.logHttpRequest(context, 500);
1629
1792
  throw error;
1630
1793
  }
1631
- const tagged = withRequestId(response, context.requestId);
1794
+ const tagged = withRequestId2(response, context.requestId);
1632
1795
  if (client.shouldLogRequest(req)) client.logHttpRequest(context, tagged.status);
1633
1796
  return tagged;
1634
1797
  });