@obsrviq/tracker 0.6.0 → 0.7.1
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 +105 -0
- package/dist/index.js.map +1 -1
- package/dist/obsrviq.global.js +39 -39
- package/dist/obsrviq.global.js.map +1 -1
- package/package.json +2 -2
- package/src/context.ts +4 -0
- package/src/index.ts +2 -0
- package/src/network.ts +38 -0
- package/src/trace.ts +130 -0
package/dist/index.js
CHANGED
|
@@ -792,6 +792,83 @@ 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 traceIdOf(header) {
|
|
841
|
+
if (!header) return null;
|
|
842
|
+
const parts = header.split("-");
|
|
843
|
+
return parts.length >= 2 && /^[0-9a-f]{32}$/.test(parts[1]) ? parts[1] : null;
|
|
844
|
+
}
|
|
845
|
+
function existingFetchTraceparent(input, init) {
|
|
846
|
+
const hasInitHeaders = !!init && "headers" in init && init.headers != null;
|
|
847
|
+
const src = hasInitHeaders ? init.headers : input instanceof Request ? input.headers : void 0;
|
|
848
|
+
if (!src) return null;
|
|
849
|
+
try {
|
|
850
|
+
return new Headers(src).get("traceparent");
|
|
851
|
+
} catch {
|
|
852
|
+
return null;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
function applyFetchTrace(input, init) {
|
|
856
|
+
const existing = existingFetchTraceparent(input, init);
|
|
857
|
+
if (existing) {
|
|
858
|
+
return { input, init, traceId: traceIdOf(existing) ?? "" };
|
|
859
|
+
}
|
|
860
|
+
const tr = newTrace();
|
|
861
|
+
const hasInitHeaders = !!init && "headers" in init && init.headers != null;
|
|
862
|
+
if (input instanceof Request && !hasInitHeaders) {
|
|
863
|
+
const h2 = new Headers(input.headers);
|
|
864
|
+
h2.set("traceparent", tr.header);
|
|
865
|
+
return { input: new Request(input, { headers: h2 }), init, traceId: tr.traceId };
|
|
866
|
+
}
|
|
867
|
+
const h = new Headers(init?.headers ?? void 0);
|
|
868
|
+
h.set("traceparent", tr.header);
|
|
869
|
+
return { input, init: { ...init || {}, headers: h }, traceId: tr.traceId };
|
|
870
|
+
}
|
|
871
|
+
|
|
795
872
|
// src/network.ts
|
|
796
873
|
var BODY_CAP = 4 * 1024;
|
|
797
874
|
var NET_RATE = 50;
|
|
@@ -807,6 +884,7 @@ var HEADER_ALLOW = /* @__PURE__ */ new Set([
|
|
|
807
884
|
"content-encoding",
|
|
808
885
|
"x-request-id",
|
|
809
886
|
"x-correlation-id",
|
|
887
|
+
"traceparent",
|
|
810
888
|
"server",
|
|
811
889
|
"vary"
|
|
812
890
|
]);
|
|
@@ -1041,6 +1119,16 @@ function wrapFetch(ctx, tr, emitNet) {
|
|
|
1041
1119
|
const perfStart = perfNow();
|
|
1042
1120
|
const method = (init?.method || (input instanceof Request ? input.method : "GET")).toUpperCase();
|
|
1043
1121
|
const url = redactUrl(rawUrl);
|
|
1122
|
+
let traceId;
|
|
1123
|
+
if (traceTarget(rawUrl, ctx.config)) {
|
|
1124
|
+
try {
|
|
1125
|
+
const inj = applyFetchTrace(input, init);
|
|
1126
|
+
input = inj.input;
|
|
1127
|
+
init = inj.init;
|
|
1128
|
+
traceId = inj.traceId || void 0;
|
|
1129
|
+
} catch {
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1044
1132
|
const reqHeaders = pickHeaders(
|
|
1045
1133
|
ctx.config.redactHeaders,
|
|
1046
1134
|
headerEntries(init?.headers ?? (input instanceof Request ? input.headers : void 0))
|
|
@@ -1063,6 +1151,7 @@ function wrapFetch(ctx, tr, emitNet) {
|
|
|
1063
1151
|
ok: false,
|
|
1064
1152
|
resourceType: "fetch",
|
|
1065
1153
|
requestHeaders: reqHeaders,
|
|
1154
|
+
traceId,
|
|
1066
1155
|
timing,
|
|
1067
1156
|
...partial
|
|
1068
1157
|
};
|
|
@@ -1144,6 +1233,19 @@ function wrapXhr(ctx, tr, emitNet) {
|
|
|
1144
1233
|
meta.startT = ctx.clock.now();
|
|
1145
1234
|
meta.perfStart = perfNow();
|
|
1146
1235
|
if (ctx.config.captureNetworkBodies) meta.body = stringifyBody(body);
|
|
1236
|
+
if (traceTarget(meta.rawUrl, ctx.config)) {
|
|
1237
|
+
try {
|
|
1238
|
+
const appSet = meta.reqHeaders["traceparent"];
|
|
1239
|
+
if (appSet) {
|
|
1240
|
+
meta.traceId = traceIdOf(appSet) ?? void 0;
|
|
1241
|
+
} else {
|
|
1242
|
+
const tr2 = newTrace();
|
|
1243
|
+
this.setRequestHeader("traceparent", tr2.header);
|
|
1244
|
+
meta.traceId = tr2.traceId;
|
|
1245
|
+
}
|
|
1246
|
+
} catch {
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1147
1249
|
const finish = (error) => {
|
|
1148
1250
|
const endT = ctx.clock.now();
|
|
1149
1251
|
const base = {
|
|
@@ -1178,6 +1280,7 @@ function wrapXhr(ctx, tr, emitNet) {
|
|
|
1178
1280
|
ok: status >= 200 && status < 400,
|
|
1179
1281
|
resourceType: "xhr",
|
|
1180
1282
|
requestHeaders: meta.reqHeaders,
|
|
1283
|
+
traceId: meta.traceId,
|
|
1181
1284
|
responseHeaders: respHeaders,
|
|
1182
1285
|
responseSize,
|
|
1183
1286
|
requestBody: ctx.config.captureNetworkBodies ? bodyPreview(meta.body, void 0) : void 0,
|
|
@@ -2179,6 +2282,8 @@ var DEFAULTS = {
|
|
|
2179
2282
|
noPrivacy: false,
|
|
2180
2283
|
captureNetworkBodies: false,
|
|
2181
2284
|
redactHeaders: ["authorization", "cookie", "set-cookie"],
|
|
2285
|
+
tracePropagation: false,
|
|
2286
|
+
traceHosts: [],
|
|
2182
2287
|
sampleRate: 1,
|
|
2183
2288
|
recordCanvas: false,
|
|
2184
2289
|
endpoint: "https://in.lumera.app",
|