@obsrviq/tracker 0.6.0 → 0.7.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/dist/index.js CHANGED
@@ -792,6 +792,63 @@ function tokenBucket(rate2, burst) {
792
792
  };
793
793
  }
794
794
 
795
+ // src/trace.ts
796
+ function randomHex(bytes) {
797
+ const arr = new Uint8Array(bytes);
798
+ if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
799
+ crypto.getRandomValues(arr);
800
+ } else {
801
+ for (let i = 0; i < bytes; i++) arr[i] = Math.random() * 256 | 0;
802
+ }
803
+ let s = "";
804
+ for (let i = 0; i < bytes; i++) s += arr[i].toString(16).padStart(2, "0");
805
+ return s;
806
+ }
807
+ function newTrace() {
808
+ const traceId = randomHex(16);
809
+ const spanId = randomHex(8);
810
+ return { traceId, spanId, header: `00-${traceId}-${spanId}-01` };
811
+ }
812
+ function traceTarget(rawUrl, cfg) {
813
+ if (!cfg.tracePropagation) return false;
814
+ let u;
815
+ try {
816
+ u = new URL(rawUrl, typeof location !== "undefined" ? location.href : void 0);
817
+ } catch {
818
+ return false;
819
+ }
820
+ if (u.protocol !== "http:" && u.protocol !== "https:") return false;
821
+ if (typeof location !== "undefined" && u.origin === location.origin) return true;
822
+ return hostAllowed(u.hostname, cfg.traceHosts);
823
+ }
824
+ function hostAllowed(hostname, hosts) {
825
+ const h = hostname.toLowerCase();
826
+ for (const raw of hosts) {
827
+ let entry = String(raw).trim().toLowerCase();
828
+ if (!entry) continue;
829
+ if (entry.includes("://")) {
830
+ try {
831
+ entry = new URL(entry).hostname;
832
+ } catch {
833
+ }
834
+ }
835
+ entry = entry.replace(/^\*?\./, "");
836
+ if (h === entry || h.endsWith("." + entry)) return true;
837
+ }
838
+ return false;
839
+ }
840
+ function injectFetchTrace(input, init, header) {
841
+ const hasInitHeaders = !!init && "headers" in init && init.headers != null;
842
+ if (input instanceof Request && !hasInitHeaders) {
843
+ const h2 = new Headers(input.headers);
844
+ if (!h2.has("traceparent")) h2.set("traceparent", header);
845
+ return { input: new Request(input, { headers: h2 }), init };
846
+ }
847
+ const h = new Headers(init?.headers ?? void 0);
848
+ if (!h.has("traceparent")) h.set("traceparent", header);
849
+ return { input, init: { ...init || {}, headers: h } };
850
+ }
851
+
795
852
  // src/network.ts
796
853
  var BODY_CAP = 4 * 1024;
797
854
  var NET_RATE = 50;
@@ -807,6 +864,7 @@ var HEADER_ALLOW = /* @__PURE__ */ new Set([
807
864
  "content-encoding",
808
865
  "x-request-id",
809
866
  "x-correlation-id",
867
+ "traceparent",
810
868
  "server",
811
869
  "vary"
812
870
  ]);
@@ -1041,6 +1099,17 @@ function wrapFetch(ctx, tr, emitNet) {
1041
1099
  const perfStart = perfNow();
1042
1100
  const method = (init?.method || (input instanceof Request ? input.method : "GET")).toUpperCase();
1043
1101
  const url = redactUrl(rawUrl);
1102
+ let traceId;
1103
+ if (traceTarget(rawUrl, ctx.config)) {
1104
+ try {
1105
+ const tr2 = newTrace();
1106
+ const inj = injectFetchTrace(input, init, tr2.header);
1107
+ input = inj.input;
1108
+ init = inj.init;
1109
+ traceId = tr2.traceId;
1110
+ } catch {
1111
+ }
1112
+ }
1044
1113
  const reqHeaders = pickHeaders(
1045
1114
  ctx.config.redactHeaders,
1046
1115
  headerEntries(init?.headers ?? (input instanceof Request ? input.headers : void 0))
@@ -1063,6 +1132,7 @@ function wrapFetch(ctx, tr, emitNet) {
1063
1132
  ok: false,
1064
1133
  resourceType: "fetch",
1065
1134
  requestHeaders: reqHeaders,
1135
+ traceId,
1066
1136
  timing,
1067
1137
  ...partial
1068
1138
  };
@@ -1144,6 +1214,14 @@ function wrapXhr(ctx, tr, emitNet) {
1144
1214
  meta.startT = ctx.clock.now();
1145
1215
  meta.perfStart = perfNow();
1146
1216
  if (ctx.config.captureNetworkBodies) meta.body = stringifyBody(body);
1217
+ if (traceTarget(meta.rawUrl, ctx.config)) {
1218
+ try {
1219
+ const tr2 = newTrace();
1220
+ this.setRequestHeader("traceparent", tr2.header);
1221
+ meta.traceId = tr2.traceId;
1222
+ } catch {
1223
+ }
1224
+ }
1147
1225
  const finish = (error) => {
1148
1226
  const endT = ctx.clock.now();
1149
1227
  const base = {
@@ -1178,6 +1256,7 @@ function wrapXhr(ctx, tr, emitNet) {
1178
1256
  ok: status >= 200 && status < 400,
1179
1257
  resourceType: "xhr",
1180
1258
  requestHeaders: meta.reqHeaders,
1259
+ traceId: meta.traceId,
1181
1260
  responseHeaders: respHeaders,
1182
1261
  responseSize,
1183
1262
  requestBody: ctx.config.captureNetworkBodies ? bodyPreview(meta.body, void 0) : void 0,
@@ -2179,6 +2258,8 @@ var DEFAULTS = {
2179
2258
  noPrivacy: false,
2180
2259
  captureNetworkBodies: false,
2181
2260
  redactHeaders: ["authorization", "cookie", "set-cookie"],
2261
+ tracePropagation: false,
2262
+ traceHosts: [],
2182
2263
  sampleRate: 1,
2183
2264
  recordCanvas: false,
2184
2265
  endpoint: "https://in.lumera.app",