@loggerjs/browser 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/README.md +2 -2
- package/dist/chunks/http-capture-utils-DbhpUirh.cjs +83 -0
- package/dist/chunks/http-capture-utils-DbhpUirh.cjs.map +1 -0
- package/dist/chunks/http-capture-utils-_hG5wepA.js +48 -0
- package/dist/chunks/http-capture-utils-_hG5wepA.js.map +1 -0
- package/dist/context-propagation-integration.cjs +5 -4
- package/dist/context-propagation-integration.cjs.map +1 -1
- package/dist/context-propagation-integration.d.ts +2 -1
- package/dist/context-propagation-integration.d.ts.map +1 -1
- package/dist/context-propagation-integration.js +2 -1
- package/dist/context-propagation-integration.js.map +1 -1
- package/dist/fetch-integration.cjs +8 -52
- package/dist/fetch-integration.cjs.map +1 -1
- package/dist/fetch-integration.js +1 -45
- package/dist/fetch-integration.js.map +1 -1
- package/dist/http-transport.cjs +90 -16
- package/dist/http-transport.cjs.map +1 -1
- package/dist/http-transport.d.ts +17 -0
- package/dist/http-transport.d.ts.map +1 -1
- package/dist/http-transport.js +90 -16
- package/dist/http-transport.js.map +1 -1
- package/dist/index.cjs +58 -4167
- package/dist/index.d.ts +243 -17
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -4135
- package/dist/indexeddb-offline-queue.cjs +8 -1
- package/dist/indexeddb-offline-queue.cjs.map +1 -1
- package/dist/indexeddb-offline-queue.d.ts.map +1 -1
- package/dist/indexeddb-offline-queue.js +8 -1
- package/dist/indexeddb-offline-queue.js.map +1 -1
- package/dist/indexeddb-transport.cjs +24 -4
- package/dist/indexeddb-transport.cjs.map +1 -1
- package/dist/indexeddb-transport.d.ts.map +1 -1
- package/dist/indexeddb-transport.js +24 -4
- package/dist/indexeddb-transport.js.map +1 -1
- package/dist/offline-first-transport.cjs +2 -1029
- package/dist/offline-first-transport.cjs.map +1 -1
- package/dist/offline-first-transport.js +2 -1029
- package/dist/offline-first-transport.js.map +1 -1
- package/dist/payload-transforms.cjs +3 -3
- package/dist/payload-transforms.cjs.map +1 -1
- package/dist/payload-transforms.d.ts.map +1 -1
- package/dist/payload-transforms.js +1 -1
- package/dist/payload-transforms.js.map +1 -1
- package/dist/websocket-integration.cjs +5 -32
- package/dist/websocket-integration.cjs.map +1 -1
- package/dist/websocket-integration.js +1 -28
- package/dist/websocket-integration.js.map +1 -1
- package/dist/websocket-transport.cjs +1 -1
- package/dist/websocket-transport.cjs.map +1 -1
- package/dist/websocket-transport.js +2 -2
- package/dist/websocket-transport.js.map +1 -1
- package/dist/xhr-integration.cjs +6 -33
- package/dist/xhr-integration.cjs.map +1 -1
- package/dist/xhr-integration.js +1 -28
- package/dist/xhr-integration.js.map +1 -1
- package/package.json +5 -2
- package/dist/index.cjs.map +0 -1
- package/dist/index.js.map +0 -1
package/README.md
CHANGED
|
@@ -24,12 +24,12 @@ import {
|
|
|
24
24
|
browserHttpTransport,
|
|
25
25
|
captureBrowserErrorsIntegration,
|
|
26
26
|
captureConsoleIntegration,
|
|
27
|
-
captureFetchIntegration,
|
|
28
|
-
captureWebVitalsIntegration,
|
|
29
27
|
createLogger,
|
|
30
28
|
memoryBrowserHttpOfflineQueue,
|
|
31
29
|
pageLifecycleIntegration,
|
|
32
30
|
} from "@loggerjs/browser";
|
|
31
|
+
import { captureFetchIntegration } from "@loggerjs/browser/integration-fetch";
|
|
32
|
+
import { captureWebVitalsIntegration } from "@loggerjs/browser/integration-web-vitals";
|
|
33
33
|
import { redactProcessor } from "@loggerjs/processors";
|
|
34
34
|
|
|
35
35
|
const logger = createLogger({
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
//#region src/http-capture-utils.ts
|
|
2
|
+
function nowMs() {
|
|
3
|
+
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
4
|
+
}
|
|
5
|
+
function durationMs(started) {
|
|
6
|
+
return nowMs() - started;
|
|
7
|
+
}
|
|
8
|
+
function sanitizeHttpUrl(rawUrl, sanitizer) {
|
|
9
|
+
const stripped = stripSensitiveUrlParts(rawUrl);
|
|
10
|
+
return sanitizer ? sanitizer(stripped) : stripped;
|
|
11
|
+
}
|
|
12
|
+
function pickAllowedHeaders(headers, allowList) {
|
|
13
|
+
if (!headers || !allowList || allowList.length === 0) return void 0;
|
|
14
|
+
const out = {};
|
|
15
|
+
for (const name of allowList) {
|
|
16
|
+
const value = headers.get(name);
|
|
17
|
+
if (value !== null) out[name.toLowerCase()] = value;
|
|
18
|
+
}
|
|
19
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
20
|
+
}
|
|
21
|
+
function headersFromInit(headers) {
|
|
22
|
+
if (!headers) return void 0;
|
|
23
|
+
try {
|
|
24
|
+
return new Headers(headers);
|
|
25
|
+
} catch {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function shouldSample(sampleRate, random) {
|
|
30
|
+
if (sampleRate >= 1) return true;
|
|
31
|
+
if (sampleRate <= 0) return false;
|
|
32
|
+
return random() < sampleRate;
|
|
33
|
+
}
|
|
34
|
+
function stripSensitiveUrlParts(rawUrl) {
|
|
35
|
+
if (!/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(rawUrl)) return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
|
|
36
|
+
try {
|
|
37
|
+
const url = new URL(rawUrl);
|
|
38
|
+
url.search = "";
|
|
39
|
+
url.hash = "";
|
|
40
|
+
return url.toString();
|
|
41
|
+
} catch {
|
|
42
|
+
return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
Object.defineProperty(exports, "durationMs", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
get: function() {
|
|
49
|
+
return durationMs;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
Object.defineProperty(exports, "headersFromInit", {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
get: function() {
|
|
55
|
+
return headersFromInit;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
Object.defineProperty(exports, "nowMs", {
|
|
59
|
+
enumerable: true,
|
|
60
|
+
get: function() {
|
|
61
|
+
return nowMs;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
Object.defineProperty(exports, "pickAllowedHeaders", {
|
|
65
|
+
enumerable: true,
|
|
66
|
+
get: function() {
|
|
67
|
+
return pickAllowedHeaders;
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
Object.defineProperty(exports, "sanitizeHttpUrl", {
|
|
71
|
+
enumerable: true,
|
|
72
|
+
get: function() {
|
|
73
|
+
return sanitizeHttpUrl;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
Object.defineProperty(exports, "shouldSample", {
|
|
77
|
+
enumerable: true,
|
|
78
|
+
get: function() {
|
|
79
|
+
return shouldSample;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
//# sourceMappingURL=http-capture-utils-DbhpUirh.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-capture-utils-DbhpUirh.cjs","names":[],"sources":["../../src/http-capture-utils.ts"],"sourcesContent":["export function nowMs(): number {\n return typeof performance !== \"undefined\" ? performance.now() : Date.now();\n}\n\nexport function durationMs(started: number): number {\n return nowMs() - started;\n}\n\nexport function sanitizeHttpUrl(rawUrl: string, sanitizer?: (url: string) => string): string {\n const stripped = stripSensitiveUrlParts(rawUrl);\n return sanitizer ? sanitizer(stripped) : stripped;\n}\n\nexport function pickAllowedHeaders(\n headers: Headers | undefined,\n allowList: readonly string[] | undefined,\n): Record<string, string> | undefined {\n if (!headers || !allowList || allowList.length === 0) return undefined;\n const out: Record<string, string> = {};\n for (const name of allowList) {\n const value = headers.get(name);\n if (value !== null) out[name.toLowerCase()] = value;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nexport function headersFromInit(headers: HeadersInit | undefined): Headers | undefined {\n if (!headers) return undefined;\n try {\n return new Headers(headers);\n } catch {\n return undefined;\n }\n}\n\nexport function shouldSample(sampleRate: number, random: () => number): boolean {\n if (sampleRate >= 1) return true;\n if (sampleRate <= 0) return false;\n return random() < sampleRate;\n}\n\nfunction stripSensitiveUrlParts(rawUrl: string): string {\n if (!/^[a-zA-Z][a-zA-Z\\d+.-]*:/.test(rawUrl)) {\n return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;\n }\n\n try {\n const url = new URL(rawUrl);\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n } catch {\n return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;\n }\n}\n"],"mappings":";AAAA,SAAgB,QAAgB;CAC9B,OAAO,OAAO,gBAAgB,cAAc,YAAY,IAAI,IAAI,KAAK,IAAI;AAC3E;AAEA,SAAgB,WAAW,SAAyB;CAClD,OAAO,MAAM,IAAI;AACnB;AAEA,SAAgB,gBAAgB,QAAgB,WAA6C;CAC3F,MAAM,WAAW,uBAAuB,MAAM;CAC9C,OAAO,YAAY,UAAU,QAAQ,IAAI;AAC3C;AAEA,SAAgB,mBACd,SACA,WACoC;CACpC,IAAI,CAAC,WAAW,CAAC,aAAa,UAAU,WAAW,GAAG,OAAO,KAAA;CAC7D,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,QAAQ,QAAQ,IAAI,IAAI;EAC9B,IAAI,UAAU,MAAM,IAAI,KAAK,YAAY,KAAK;CAChD;CACA,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,SAAS,IAAI,MAAM,KAAA;AAC7C;AAEA,SAAgB,gBAAgB,SAAuD;CACrF,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,IAAI;EACF,OAAO,IAAI,QAAQ,OAAO;CAC5B,QAAQ;EACN;CACF;AACF;AAEA,SAAgB,aAAa,YAAoB,QAA+B;CAC9E,IAAI,cAAc,GAAG,OAAO;CAC5B,IAAI,cAAc,GAAG,OAAO;CAC5B,OAAO,OAAO,IAAI;AACpB;AAEA,SAAS,uBAAuB,QAAwB;CACtD,IAAI,CAAC,2BAA2B,KAAK,MAAM,GACzC,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC,CAAC,MAAM;CAGvC,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,MAAM;EAC1B,IAAI,SAAS;EACb,IAAI,OAAO;EACX,OAAO,IAAI,SAAS;CACtB,QAAQ;EACN,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC,CAAC,MAAM;CACvC;AACF"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
//#region src/http-capture-utils.ts
|
|
2
|
+
function nowMs() {
|
|
3
|
+
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
4
|
+
}
|
|
5
|
+
function durationMs(started) {
|
|
6
|
+
return nowMs() - started;
|
|
7
|
+
}
|
|
8
|
+
function sanitizeHttpUrl(rawUrl, sanitizer) {
|
|
9
|
+
const stripped = stripSensitiveUrlParts(rawUrl);
|
|
10
|
+
return sanitizer ? sanitizer(stripped) : stripped;
|
|
11
|
+
}
|
|
12
|
+
function pickAllowedHeaders(headers, allowList) {
|
|
13
|
+
if (!headers || !allowList || allowList.length === 0) return void 0;
|
|
14
|
+
const out = {};
|
|
15
|
+
for (const name of allowList) {
|
|
16
|
+
const value = headers.get(name);
|
|
17
|
+
if (value !== null) out[name.toLowerCase()] = value;
|
|
18
|
+
}
|
|
19
|
+
return Object.keys(out).length > 0 ? out : void 0;
|
|
20
|
+
}
|
|
21
|
+
function headersFromInit(headers) {
|
|
22
|
+
if (!headers) return void 0;
|
|
23
|
+
try {
|
|
24
|
+
return new Headers(headers);
|
|
25
|
+
} catch {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
function shouldSample(sampleRate, random) {
|
|
30
|
+
if (sampleRate >= 1) return true;
|
|
31
|
+
if (sampleRate <= 0) return false;
|
|
32
|
+
return random() < sampleRate;
|
|
33
|
+
}
|
|
34
|
+
function stripSensitiveUrlParts(rawUrl) {
|
|
35
|
+
if (!/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(rawUrl)) return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
|
|
36
|
+
try {
|
|
37
|
+
const url = new URL(rawUrl);
|
|
38
|
+
url.search = "";
|
|
39
|
+
url.hash = "";
|
|
40
|
+
return url.toString();
|
|
41
|
+
} catch {
|
|
42
|
+
return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { sanitizeHttpUrl as a, pickAllowedHeaders as i, headersFromInit as n, shouldSample as o, nowMs as r, durationMs as t };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=http-capture-utils-_hG5wepA.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http-capture-utils-_hG5wepA.js","names":[],"sources":["../../src/http-capture-utils.ts"],"sourcesContent":["export function nowMs(): number {\n return typeof performance !== \"undefined\" ? performance.now() : Date.now();\n}\n\nexport function durationMs(started: number): number {\n return nowMs() - started;\n}\n\nexport function sanitizeHttpUrl(rawUrl: string, sanitizer?: (url: string) => string): string {\n const stripped = stripSensitiveUrlParts(rawUrl);\n return sanitizer ? sanitizer(stripped) : stripped;\n}\n\nexport function pickAllowedHeaders(\n headers: Headers | undefined,\n allowList: readonly string[] | undefined,\n): Record<string, string> | undefined {\n if (!headers || !allowList || allowList.length === 0) return undefined;\n const out: Record<string, string> = {};\n for (const name of allowList) {\n const value = headers.get(name);\n if (value !== null) out[name.toLowerCase()] = value;\n }\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nexport function headersFromInit(headers: HeadersInit | undefined): Headers | undefined {\n if (!headers) return undefined;\n try {\n return new Headers(headers);\n } catch {\n return undefined;\n }\n}\n\nexport function shouldSample(sampleRate: number, random: () => number): boolean {\n if (sampleRate >= 1) return true;\n if (sampleRate <= 0) return false;\n return random() < sampleRate;\n}\n\nfunction stripSensitiveUrlParts(rawUrl: string): string {\n if (!/^[a-zA-Z][a-zA-Z\\d+.-]*:/.test(rawUrl)) {\n return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;\n }\n\n try {\n const url = new URL(rawUrl);\n url.search = \"\";\n url.hash = \"\";\n return url.toString();\n } catch {\n return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;\n }\n}\n"],"mappings":";AAAA,SAAgB,QAAgB;CAC9B,OAAO,OAAO,gBAAgB,cAAc,YAAY,IAAI,IAAI,KAAK,IAAI;AAC3E;AAEA,SAAgB,WAAW,SAAyB;CAClD,OAAO,MAAM,IAAI;AACnB;AAEA,SAAgB,gBAAgB,QAAgB,WAA6C;CAC3F,MAAM,WAAW,uBAAuB,MAAM;CAC9C,OAAO,YAAY,UAAU,QAAQ,IAAI;AAC3C;AAEA,SAAgB,mBACd,SACA,WACoC;CACpC,IAAI,CAAC,WAAW,CAAC,aAAa,UAAU,WAAW,GAAG,OAAO,KAAA;CAC7D,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,QAAQ,WAAW;EAC5B,MAAM,QAAQ,QAAQ,IAAI,IAAI;EAC9B,IAAI,UAAU,MAAM,IAAI,KAAK,YAAY,KAAK;CAChD;CACA,OAAO,OAAO,KAAK,GAAG,CAAC,CAAC,SAAS,IAAI,MAAM,KAAA;AAC7C;AAEA,SAAgB,gBAAgB,SAAuD;CACrF,IAAI,CAAC,SAAS,OAAO,KAAA;CACrB,IAAI;EACF,OAAO,IAAI,QAAQ,OAAO;CAC5B,QAAQ;EACN;CACF;AACF;AAEA,SAAgB,aAAa,YAAoB,QAA+B;CAC9E,IAAI,cAAc,GAAG,OAAO;CAC5B,IAAI,cAAc,GAAG,OAAO;CAC5B,OAAO,OAAO,IAAI;AACpB;AAEA,SAAS,uBAAuB,QAAwB;CACtD,IAAI,CAAC,2BAA2B,KAAK,MAAM,GACzC,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC,CAAC,MAAM;CAGvC,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,MAAM;EAC1B,IAAI,SAAS;EACb,IAAI,OAAO;EACX,OAAO,IAAI,SAAS;CACtB,QAAQ;EACN,OAAO,OAAO,MAAM,QAAQ,CAAC,CAAC,CAAC,MAAM;CACvC;AACF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
let _loggerjs_core = require("@loggerjs/core");
|
|
3
|
+
let _loggerjs_core_trace_propagation = require("@loggerjs/core/trace-propagation");
|
|
3
4
|
//#region src/context-propagation-integration.ts
|
|
4
5
|
const defaultActionEvents = [
|
|
5
6
|
"click",
|
|
@@ -17,7 +18,7 @@ function resolveValue(value) {
|
|
|
17
18
|
return typeof value === "function" ? value() : value;
|
|
18
19
|
}
|
|
19
20
|
function normalizeBaggage(value) {
|
|
20
|
-
if (typeof value === "string") return (0,
|
|
21
|
+
if (typeof value === "string") return (0, _loggerjs_core_trace_propagation.parseBaggage)(value);
|
|
21
22
|
return value;
|
|
22
23
|
}
|
|
23
24
|
function actionTargetName(target) {
|
|
@@ -38,9 +39,9 @@ function browserContextPropagationIntegration(options = {}) {
|
|
|
38
39
|
const disposeProvider = (0, _loggerjs_core.addContextProvider)(() => {
|
|
39
40
|
const sessionId = resolveValue(options.sessionId);
|
|
40
41
|
const requestId = options.requestId?.();
|
|
41
|
-
const trace = resolveValue(options.trace) ?? (0,
|
|
42
|
+
const trace = resolveValue(options.trace) ?? (0, _loggerjs_core_trace_propagation.parseTraceparent)(resolveValue(options.traceparent));
|
|
42
43
|
const baggage = normalizeBaggage(resolveValue(options.baggage)) ?? trace?.baggage;
|
|
43
|
-
const traceparent = (0,
|
|
44
|
+
const traceparent = (0, _loggerjs_core_trace_propagation.formatTraceparent)(trace);
|
|
44
45
|
const now = clock();
|
|
45
46
|
const action = lastAction && now - lastAction.time <= actionTtlMs ? {
|
|
46
47
|
actionId: lastAction.id,
|
|
@@ -51,7 +52,7 @@ function browserContextPropagationIntegration(options = {}) {
|
|
|
51
52
|
sessionId,
|
|
52
53
|
requestId,
|
|
53
54
|
traceparent,
|
|
54
|
-
baggage: (0,
|
|
55
|
+
baggage: (0, _loggerjs_core_trace_propagation.formatBaggage)(baggage),
|
|
55
56
|
...action
|
|
56
57
|
};
|
|
57
58
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-propagation-integration.cjs","names":[],"sources":["../src/context-propagation-integration.ts"],"sourcesContent":["import {\n
|
|
1
|
+
{"version":3,"file":"context-propagation-integration.cjs","names":[],"sources":["../src/context-propagation-integration.ts"],"sourcesContent":["import { addContextProvider, type Integration, type TraceContext } from \"@loggerjs/core\";\nimport {\n formatBaggage,\n formatTraceparent,\n parseBaggage,\n parseTraceparent,\n type Baggage,\n} from \"@loggerjs/core/trace-propagation\";\n\nexport type BrowserContextActionEventName = \"change\" | \"click\" | \"input\" | \"keydown\" | \"submit\";\n\nexport interface BrowserContextEventTargetLike {\n addEventListener: typeof globalThis.addEventListener;\n removeEventListener: typeof globalThis.removeEventListener;\n}\n\nexport interface BrowserContextPropagationOptions {\n sessionId?: string | (() => string | undefined);\n requestId?: () => string | undefined;\n trace?: TraceContext | (() => TraceContext | undefined);\n traceparent?: string | (() => string | undefined);\n baggage?: Baggage | string | (() => Baggage | string | undefined);\n actionEvents?: readonly BrowserContextActionEventName[];\n actionTtlMs?: number;\n root?: BrowserContextEventTargetLike;\n clock?: () => number;\n idFactory?: () => string;\n}\n\nconst defaultActionEvents: readonly BrowserContextActionEventName[] = [\n \"click\",\n \"input\",\n \"change\",\n \"submit\",\n];\n\nlet fallbackIdSeq = 0;\n\nfunction defaultIdFactory(): string {\n const cryptoRandomUUID = globalThis.crypto?.randomUUID;\n if (cryptoRandomUUID) return cryptoRandomUUID.call(globalThis.crypto);\n return `${Date.now().toString(36)}-${(fallbackIdSeq++).toString(36)}`;\n}\n\nfunction resolveValue<T>(value: T | (() => T | undefined) | undefined): T | undefined {\n return typeof value === \"function\" ? (value as () => T | undefined)() : value;\n}\n\nfunction normalizeBaggage(value: Baggage | string | undefined): Baggage | undefined {\n if (typeof value === \"string\") return parseBaggage(value);\n return value;\n}\n\nfunction actionTargetName(target: EventTarget | null): string | undefined {\n const item = target as { tagName?: string; id?: string; name?: string } | null;\n if (!item) return undefined;\n return item.id || item.name || item.tagName?.toLowerCase();\n}\n\nexport function browserContextPropagationIntegration(\n options: BrowserContextPropagationOptions = {},\n): Integration {\n const actionEvents = options.actionEvents ?? defaultActionEvents;\n const actionTtlMs = Math.max(0, Math.floor(options.actionTtlMs ?? 5000));\n const clock = options.clock ?? Date.now;\n const idFactory = options.idFactory ?? defaultIdFactory;\n let lastAction:\n | {\n id: string;\n type: string;\n target?: string;\n time: number;\n }\n | undefined;\n\n return {\n name: \"browser-context-propagation\",\n setup() {\n const root = options.root ?? globalThis.document;\n const disposeProvider = addContextProvider(() => {\n const sessionId = resolveValue(options.sessionId);\n const requestId = options.requestId?.();\n const explicitTrace = resolveValue(options.trace);\n const trace = explicitTrace ?? parseTraceparent(resolveValue(options.traceparent));\n const baggage = normalizeBaggage(resolveValue(options.baggage)) ?? trace?.baggage;\n const traceparent = formatTraceparent(trace);\n const now = clock();\n const action =\n lastAction && now - lastAction.time <= actionTtlMs\n ? {\n actionId: lastAction.id,\n actionType: lastAction.type,\n actionTarget: lastAction.target,\n }\n : undefined;\n\n return {\n sessionId,\n requestId,\n traceparent,\n baggage: formatBaggage(baggage),\n ...action,\n };\n });\n\n if (!root?.addEventListener || !root.removeEventListener || actionEvents.length === 0) {\n return disposeProvider;\n }\n\n const onAction = (event: Event) => {\n lastAction = {\n id: idFactory(),\n type: event.type,\n target: actionTargetName(event.target),\n time: clock(),\n };\n };\n\n for (const event of actionEvents) root.addEventListener(event, onAction, true);\n\n return () => {\n for (const event of actionEvents) root.removeEventListener(event, onAction, true);\n disposeProvider();\n };\n },\n };\n}\n"],"mappings":";;;;AA6BA,MAAM,sBAAgE;CACpE;CACA;CACA;CACA;AACF;AAEA,IAAI,gBAAgB;AAEpB,SAAS,mBAA2B;CAClC,MAAM,mBAAmB,WAAW,QAAQ;CAC5C,IAAI,kBAAkB,OAAO,iBAAiB,KAAK,WAAW,MAAM;CACpE,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,IAAI,gBAAA,CAAiB,SAAS,EAAE;AACpE;AAEA,SAAS,aAAgB,OAA6D;CACpF,OAAO,OAAO,UAAU,aAAc,MAA8B,IAAI;AAC1E;AAEA,SAAS,iBAAiB,OAA0D;CAClF,IAAI,OAAO,UAAU,UAAU,QAAA,GAAA,iCAAA,aAAA,CAAoB,KAAK;CACxD,OAAO;AACT;AAEA,SAAS,iBAAiB,QAAgD;CACxE,MAAM,OAAO;CACb,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,OAAO,KAAK,MAAM,KAAK,QAAQ,KAAK,SAAS,YAAY;AAC3D;AAEA,SAAgB,qCACd,UAA4C,CAAC,GAChC;CACb,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,MAAM,cAAc,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,eAAe,GAAI,CAAC;CACvE,MAAM,QAAQ,QAAQ,SAAS,KAAK;CACpC,MAAM,YAAY,QAAQ,aAAa;CACvC,IAAI;CASJ,OAAO;EACL,MAAM;EACN,QAAQ;GACN,MAAM,OAAO,QAAQ,QAAQ,WAAW;GACxC,MAAM,mBAAA,GAAA,eAAA,mBAAA,OAA2C;IAC/C,MAAM,YAAY,aAAa,QAAQ,SAAS;IAChD,MAAM,YAAY,QAAQ,YAAY;IAEtC,MAAM,QADgB,aAAa,QAAQ,KACjB,MAAA,GAAA,iCAAA,iBAAA,CAAsB,aAAa,QAAQ,WAAW,CAAC;IACjF,MAAM,UAAU,iBAAiB,aAAa,QAAQ,OAAO,CAAC,KAAK,OAAO;IAC1E,MAAM,eAAA,GAAA,iCAAA,kBAAA,CAAgC,KAAK;IAC3C,MAAM,MAAM,MAAM;IAClB,MAAM,SACJ,cAAc,MAAM,WAAW,QAAQ,cACnC;KACE,UAAU,WAAW;KACrB,YAAY,WAAW;KACvB,cAAc,WAAW;IAC3B,IACA,KAAA;IAEN,OAAO;KACL;KACA;KACA;KACA,UAAA,GAAA,iCAAA,cAAA,CAAuB,OAAO;KAC9B,GAAG;IACL;GACF,CAAC;GAED,IAAI,CAAC,MAAM,oBAAoB,CAAC,KAAK,uBAAuB,aAAa,WAAW,GAClF,OAAO;GAGT,MAAM,YAAY,UAAiB;IACjC,aAAa;KACX,IAAI,UAAU;KACd,MAAM,MAAM;KACZ,QAAQ,iBAAiB,MAAM,MAAM;KACrC,MAAM,MAAM;IACd;GACF;GAEA,KAAK,MAAM,SAAS,cAAc,KAAK,iBAAiB,OAAO,UAAU,IAAI;GAE7E,aAAa;IACX,KAAK,MAAM,SAAS,cAAc,KAAK,oBAAoB,OAAO,UAAU,IAAI;IAChF,gBAAgB;GAClB;EACF;CACF;AACF"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type Integration, type TraceContext } from "@loggerjs/core";
|
|
2
|
+
import { type Baggage } from "@loggerjs/core/trace-propagation";
|
|
2
3
|
export type BrowserContextActionEventName = "change" | "click" | "input" | "keydown" | "submit";
|
|
3
4
|
export interface BrowserContextEventTargetLike {
|
|
4
5
|
addEventListener: typeof globalThis.addEventListener;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-propagation-integration.d.ts","sourceRoot":"","sources":["../src/context-propagation-integration.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"context-propagation-integration.d.ts","sourceRoot":"","sources":["../src/context-propagation-integration.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,KAAK,WAAW,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACzF,OAAO,EAKL,KAAK,OAAO,EACb,MAAM,kCAAkC,CAAC;AAE1C,MAAM,MAAM,6BAA6B,GAAG,QAAQ,GAAG,OAAO,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEhG,MAAM,WAAW,6BAA6B;IAC5C,gBAAgB,EAAE,OAAO,UAAU,CAAC,gBAAgB,CAAC;IACrD,mBAAmB,EAAE,OAAO,UAAU,CAAC,mBAAmB,CAAC;CAC5D;AAED,MAAM,WAAW,gCAAgC;IAC/C,SAAS,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC;IAChD,SAAS,CAAC,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IACrC,KAAK,CAAC,EAAE,YAAY,GAAG,CAAC,MAAM,YAAY,GAAG,SAAS,CAAC,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,GAAG,CAAC,MAAM,MAAM,GAAG,SAAS,CAAC,CAAC;IAClD,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,CAAC,MAAM,OAAO,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;IAClE,YAAY,CAAC,EAAE,SAAS,6BAA6B,EAAE,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,6BAA6B,CAAC;IACrC,KAAK,CAAC,EAAE,MAAM,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,MAAM,CAAC;CAC1B;AAgCD,wBAAgB,oCAAoC,CAClD,OAAO,GAAE,gCAAqC,GAC7C,WAAW,CAiEb"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { addContextProvider
|
|
1
|
+
import { addContextProvider } from "@loggerjs/core";
|
|
2
|
+
import { formatBaggage, formatTraceparent, parseBaggage, parseTraceparent } from "@loggerjs/core/trace-propagation";
|
|
2
3
|
//#region src/context-propagation-integration.ts
|
|
3
4
|
const defaultActionEvents = [
|
|
4
5
|
"click",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-propagation-integration.js","names":[],"sources":["../src/context-propagation-integration.ts"],"sourcesContent":["import {\n
|
|
1
|
+
{"version":3,"file":"context-propagation-integration.js","names":[],"sources":["../src/context-propagation-integration.ts"],"sourcesContent":["import { addContextProvider, type Integration, type TraceContext } from \"@loggerjs/core\";\nimport {\n formatBaggage,\n formatTraceparent,\n parseBaggage,\n parseTraceparent,\n type Baggage,\n} from \"@loggerjs/core/trace-propagation\";\n\nexport type BrowserContextActionEventName = \"change\" | \"click\" | \"input\" | \"keydown\" | \"submit\";\n\nexport interface BrowserContextEventTargetLike {\n addEventListener: typeof globalThis.addEventListener;\n removeEventListener: typeof globalThis.removeEventListener;\n}\n\nexport interface BrowserContextPropagationOptions {\n sessionId?: string | (() => string | undefined);\n requestId?: () => string | undefined;\n trace?: TraceContext | (() => TraceContext | undefined);\n traceparent?: string | (() => string | undefined);\n baggage?: Baggage | string | (() => Baggage | string | undefined);\n actionEvents?: readonly BrowserContextActionEventName[];\n actionTtlMs?: number;\n root?: BrowserContextEventTargetLike;\n clock?: () => number;\n idFactory?: () => string;\n}\n\nconst defaultActionEvents: readonly BrowserContextActionEventName[] = [\n \"click\",\n \"input\",\n \"change\",\n \"submit\",\n];\n\nlet fallbackIdSeq = 0;\n\nfunction defaultIdFactory(): string {\n const cryptoRandomUUID = globalThis.crypto?.randomUUID;\n if (cryptoRandomUUID) return cryptoRandomUUID.call(globalThis.crypto);\n return `${Date.now().toString(36)}-${(fallbackIdSeq++).toString(36)}`;\n}\n\nfunction resolveValue<T>(value: T | (() => T | undefined) | undefined): T | undefined {\n return typeof value === \"function\" ? (value as () => T | undefined)() : value;\n}\n\nfunction normalizeBaggage(value: Baggage | string | undefined): Baggage | undefined {\n if (typeof value === \"string\") return parseBaggage(value);\n return value;\n}\n\nfunction actionTargetName(target: EventTarget | null): string | undefined {\n const item = target as { tagName?: string; id?: string; name?: string } | null;\n if (!item) return undefined;\n return item.id || item.name || item.tagName?.toLowerCase();\n}\n\nexport function browserContextPropagationIntegration(\n options: BrowserContextPropagationOptions = {},\n): Integration {\n const actionEvents = options.actionEvents ?? defaultActionEvents;\n const actionTtlMs = Math.max(0, Math.floor(options.actionTtlMs ?? 5000));\n const clock = options.clock ?? Date.now;\n const idFactory = options.idFactory ?? defaultIdFactory;\n let lastAction:\n | {\n id: string;\n type: string;\n target?: string;\n time: number;\n }\n | undefined;\n\n return {\n name: \"browser-context-propagation\",\n setup() {\n const root = options.root ?? globalThis.document;\n const disposeProvider = addContextProvider(() => {\n const sessionId = resolveValue(options.sessionId);\n const requestId = options.requestId?.();\n const explicitTrace = resolveValue(options.trace);\n const trace = explicitTrace ?? parseTraceparent(resolveValue(options.traceparent));\n const baggage = normalizeBaggage(resolveValue(options.baggage)) ?? trace?.baggage;\n const traceparent = formatTraceparent(trace);\n const now = clock();\n const action =\n lastAction && now - lastAction.time <= actionTtlMs\n ? {\n actionId: lastAction.id,\n actionType: lastAction.type,\n actionTarget: lastAction.target,\n }\n : undefined;\n\n return {\n sessionId,\n requestId,\n traceparent,\n baggage: formatBaggage(baggage),\n ...action,\n };\n });\n\n if (!root?.addEventListener || !root.removeEventListener || actionEvents.length === 0) {\n return disposeProvider;\n }\n\n const onAction = (event: Event) => {\n lastAction = {\n id: idFactory(),\n type: event.type,\n target: actionTargetName(event.target),\n time: clock(),\n };\n };\n\n for (const event of actionEvents) root.addEventListener(event, onAction, true);\n\n return () => {\n for (const event of actionEvents) root.removeEventListener(event, onAction, true);\n disposeProvider();\n };\n },\n };\n}\n"],"mappings":";;;AA6BA,MAAM,sBAAgE;CACpE;CACA;CACA;CACA;AACF;AAEA,IAAI,gBAAgB;AAEpB,SAAS,mBAA2B;CAClC,MAAM,mBAAmB,WAAW,QAAQ;CAC5C,IAAI,kBAAkB,OAAO,iBAAiB,KAAK,WAAW,MAAM;CACpE,OAAO,GAAG,KAAK,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,IAAI,gBAAA,CAAiB,SAAS,EAAE;AACpE;AAEA,SAAS,aAAgB,OAA6D;CACpF,OAAO,OAAO,UAAU,aAAc,MAA8B,IAAI;AAC1E;AAEA,SAAS,iBAAiB,OAA0D;CAClF,IAAI,OAAO,UAAU,UAAU,OAAO,aAAa,KAAK;CACxD,OAAO;AACT;AAEA,SAAS,iBAAiB,QAAgD;CACxE,MAAM,OAAO;CACb,IAAI,CAAC,MAAM,OAAO,KAAA;CAClB,OAAO,KAAK,MAAM,KAAK,QAAQ,KAAK,SAAS,YAAY;AAC3D;AAEA,SAAgB,qCACd,UAA4C,CAAC,GAChC;CACb,MAAM,eAAe,QAAQ,gBAAgB;CAC7C,MAAM,cAAc,KAAK,IAAI,GAAG,KAAK,MAAM,QAAQ,eAAe,GAAI,CAAC;CACvE,MAAM,QAAQ,QAAQ,SAAS,KAAK;CACpC,MAAM,YAAY,QAAQ,aAAa;CACvC,IAAI;CASJ,OAAO;EACL,MAAM;EACN,QAAQ;GACN,MAAM,OAAO,QAAQ,QAAQ,WAAW;GACxC,MAAM,kBAAkB,yBAAyB;IAC/C,MAAM,YAAY,aAAa,QAAQ,SAAS;IAChD,MAAM,YAAY,QAAQ,YAAY;IAEtC,MAAM,QADgB,aAAa,QAAQ,KACjB,KAAK,iBAAiB,aAAa,QAAQ,WAAW,CAAC;IACjF,MAAM,UAAU,iBAAiB,aAAa,QAAQ,OAAO,CAAC,KAAK,OAAO;IAC1E,MAAM,cAAc,kBAAkB,KAAK;IAC3C,MAAM,MAAM,MAAM;IAClB,MAAM,SACJ,cAAc,MAAM,WAAW,QAAQ,cACnC;KACE,UAAU,WAAW;KACrB,YAAY,WAAW;KACvB,cAAc,WAAW;IAC3B,IACA,KAAA;IAEN,OAAO;KACL;KACA;KACA;KACA,SAAS,cAAc,OAAO;KAC9B,GAAG;IACL;GACF,CAAC;GAED,IAAI,CAAC,MAAM,oBAAoB,CAAC,KAAK,uBAAuB,aAAa,WAAW,GAClF,OAAO;GAGT,MAAM,YAAY,UAAiB;IACjC,aAAa;KACX,IAAI,UAAU;KACd,MAAM,MAAM;KACZ,QAAQ,iBAAiB,MAAM,MAAM;KACrC,MAAM,MAAM;IACd;GACF;GAEA,KAAK,MAAM,SAAS,cAAc,KAAK,iBAAiB,OAAO,UAAU,IAAI;GAE7E,aAAa;IACX,KAAK,MAAM,SAAS,cAAc,KAAK,oBAAoB,OAAO,UAAU,IAAI;IAChF,gBAAgB;GAClB;EACF;CACF;AACF"}
|
|
@@ -1,50 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_http_capture_utils = require("./chunks/http-capture-utils-DbhpUirh.cjs");
|
|
2
3
|
let _loggerjs_core = require("@loggerjs/core");
|
|
3
|
-
//#region src/http-capture-utils.ts
|
|
4
|
-
function nowMs() {
|
|
5
|
-
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
6
|
-
}
|
|
7
|
-
function durationMs(started) {
|
|
8
|
-
return nowMs() - started;
|
|
9
|
-
}
|
|
10
|
-
function sanitizeHttpUrl(rawUrl, sanitizer) {
|
|
11
|
-
const stripped = stripSensitiveUrlParts(rawUrl);
|
|
12
|
-
return sanitizer ? sanitizer(stripped) : stripped;
|
|
13
|
-
}
|
|
14
|
-
function pickAllowedHeaders(headers, allowList) {
|
|
15
|
-
if (!headers || !allowList || allowList.length === 0) return void 0;
|
|
16
|
-
const out = {};
|
|
17
|
-
for (const name of allowList) {
|
|
18
|
-
const value = headers.get(name);
|
|
19
|
-
if (value !== null) out[name.toLowerCase()] = value;
|
|
20
|
-
}
|
|
21
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
22
|
-
}
|
|
23
|
-
function headersFromInit(headers) {
|
|
24
|
-
if (!headers) return void 0;
|
|
25
|
-
try {
|
|
26
|
-
return new Headers(headers);
|
|
27
|
-
} catch {
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
function shouldSample(sampleRate, random) {
|
|
32
|
-
if (sampleRate >= 1) return true;
|
|
33
|
-
if (sampleRate <= 0) return false;
|
|
34
|
-
return random() < sampleRate;
|
|
35
|
-
}
|
|
36
|
-
function stripSensitiveUrlParts(rawUrl) {
|
|
37
|
-
if (!/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(rawUrl)) return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
|
|
38
|
-
try {
|
|
39
|
-
const url = new URL(rawUrl);
|
|
40
|
-
url.search = "";
|
|
41
|
-
url.hash = "";
|
|
42
|
-
return url.toString();
|
|
43
|
-
} catch {
|
|
44
|
-
return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
//#endregion
|
|
48
4
|
//#region src/fetch-integration.ts
|
|
49
5
|
function requestUrl(input) {
|
|
50
6
|
if (typeof input === "string") return input;
|
|
@@ -70,12 +26,12 @@ function captureFetchIntegration(options = {}) {
|
|
|
70
26
|
api.unpatched.fetch ??= original;
|
|
71
27
|
const capture = api.guard((input) => api.capture(input));
|
|
72
28
|
globalThis.fetch = async (input, init) => {
|
|
73
|
-
const started = nowMs();
|
|
74
|
-
const url = sanitizeHttpUrl(requestUrl(input), options.sanitizeUrl);
|
|
29
|
+
const started = require_http_capture_utils.nowMs();
|
|
30
|
+
const url = require_http_capture_utils.sanitizeHttpUrl(requestUrl(input), options.sanitizeUrl);
|
|
75
31
|
const method = requestMethod(input, init);
|
|
76
32
|
try {
|
|
77
33
|
const response = await original(input, init);
|
|
78
|
-
if (response.status >= minStatus || captureAll && shouldSample(sampleRate, random)) capture({
|
|
34
|
+
if (response.status >= minStatus || captureAll && require_http_capture_utils.shouldSample(sampleRate, random)) capture({
|
|
79
35
|
level: response.status >= minStatus ? "warn" : "debug",
|
|
80
36
|
message: `Fetch ${response.status} ${method} ${url}`,
|
|
81
37
|
props: { http: {
|
|
@@ -84,9 +40,9 @@ function captureFetchIntegration(options = {}) {
|
|
|
84
40
|
url,
|
|
85
41
|
status: response.status,
|
|
86
42
|
ok: response.ok,
|
|
87
|
-
durationMs: durationMs(started),
|
|
88
|
-
requestHeaders: pickAllowedHeaders(headersFromInit(init?.headers), options.captureRequestHeaders),
|
|
89
|
-
responseHeaders: pickAllowedHeaders(response.headers, options.captureResponseHeaders)
|
|
43
|
+
durationMs: require_http_capture_utils.durationMs(started),
|
|
44
|
+
requestHeaders: require_http_capture_utils.pickAllowedHeaders(require_http_capture_utils.headersFromInit(init?.headers), options.captureRequestHeaders),
|
|
45
|
+
responseHeaders: require_http_capture_utils.pickAllowedHeaders(response.headers, options.captureResponseHeaders)
|
|
90
46
|
} }
|
|
91
47
|
});
|
|
92
48
|
return response;
|
|
@@ -100,7 +56,7 @@ function captureFetchIntegration(options = {}) {
|
|
|
100
56
|
kind: "fetch",
|
|
101
57
|
method,
|
|
102
58
|
url,
|
|
103
|
-
durationMs: durationMs(started)
|
|
59
|
+
durationMs: require_http_capture_utils.durationMs(started)
|
|
104
60
|
},
|
|
105
61
|
input: (0, _loggerjs_core.normalizeValue)(input, { maxDepth: 3 })
|
|
106
62
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-integration.cjs","names":[],"sources":["../src/
|
|
1
|
+
{"version":3,"file":"fetch-integration.cjs","names":["nowMs","sanitizeHttpUrl","shouldSample","durationMs","pickAllowedHeaders","headersFromInit"],"sources":["../src/fetch-integration.ts"],"sourcesContent":["import {\n normalizeValue,\n type CaptureInput,\n type Integration,\n type IntegrationSetupContext,\n type UnpatchedFunction,\n} from \"@loggerjs/core\";\nimport {\n durationMs,\n headersFromInit,\n nowMs,\n pickAllowedHeaders,\n sanitizeHttpUrl,\n shouldSample,\n} from \"./http-capture-utils\";\n\nexport interface CaptureFetchOptions {\n minStatus?: number;\n captureRequestHeaders?: readonly string[];\n captureResponseHeaders?: readonly string[];\n captureAll?: boolean;\n captureSuccessful?: boolean;\n sampleRate?: number;\n random?: () => number;\n sanitizeUrl?: (url: string) => string;\n}\n\ntype BrowserFetch = typeof fetch;\n\nfunction requestUrl(input: RequestInfo | URL): string {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.toString();\n return input.url;\n}\n\nfunction requestMethod(input: RequestInfo | URL, init?: RequestInit): string {\n if (init?.method) return init.method.toUpperCase();\n if (typeof input === \"object\" && \"method\" in input && input.method)\n return input.method.toUpperCase();\n return \"GET\";\n}\n\nexport function captureFetchIntegration(options: CaptureFetchOptions = {}): Integration {\n const minStatus = options.minStatus ?? 400;\n const captureAll = options.captureAll ?? options.captureSuccessful ?? false;\n const sampleRate = options.sampleRate ?? 1;\n const random = options.random ?? Math.random;\n\n return {\n name: \"capture-fetch\",\n setup(api: IntegrationSetupContext) {\n const current = globalThis.fetch;\n if (!current) return;\n const original =\n (api.unpatched.fetch as BrowserFetch | undefined) ?? current.bind(globalThis);\n api.unpatched.fetch ??= original as UnpatchedFunction;\n\n const capture = api.guard((input: CaptureInput) => api.capture(input));\n\n globalThis.fetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const started = nowMs();\n const url = sanitizeHttpUrl(requestUrl(input), options.sanitizeUrl);\n const method = requestMethod(input, init);\n try {\n const response = await original(input, init);\n const shouldCapture =\n response.status >= minStatus || (captureAll && shouldSample(sampleRate, random));\n if (shouldCapture) {\n capture({\n level: response.status >= minStatus ? \"warn\" : \"debug\",\n message: `Fetch ${response.status} ${method} ${url}`,\n props: {\n http: {\n kind: \"fetch\",\n method,\n url,\n status: response.status,\n ok: response.ok,\n durationMs: durationMs(started),\n requestHeaders: pickAllowedHeaders(\n headersFromInit(init?.headers),\n options.captureRequestHeaders,\n ),\n responseHeaders: pickAllowedHeaders(\n response.headers,\n options.captureResponseHeaders,\n ),\n },\n },\n });\n }\n return response;\n } catch (error) {\n capture({\n level: \"error\",\n message: `Fetch network error ${method} ${url}`,\n error,\n props: {\n http: { kind: \"fetch\", method, url, durationMs: durationMs(started) },\n input: normalizeValue(input, { maxDepth: 3 }),\n },\n });\n throw error;\n }\n };\n\n return () => {\n globalThis.fetch = current;\n };\n },\n };\n}\n"],"mappings":";;;;AA6BA,SAAS,WAAW,OAAkC;CACpD,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,iBAAiB,KAAK,OAAO,MAAM,SAAS;CAChD,OAAO,MAAM;AACf;AAEA,SAAS,cAAc,OAA0B,MAA4B;CAC3E,IAAI,MAAM,QAAQ,OAAO,KAAK,OAAO,YAAY;CACjD,IAAI,OAAO,UAAU,YAAY,YAAY,SAAS,MAAM,QAC1D,OAAO,MAAM,OAAO,YAAY;CAClC,OAAO;AACT;AAEA,SAAgB,wBAAwB,UAA+B,CAAC,GAAgB;CACtF,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,aAAa,QAAQ,cAAc,QAAQ,qBAAqB;CACtE,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,SAAS,QAAQ,UAAU,KAAK;CAEtC,OAAO;EACL,MAAM;EACN,MAAM,KAA8B;GAClC,MAAM,UAAU,WAAW;GAC3B,IAAI,CAAC,SAAS;GACd,MAAM,WACH,IAAI,UAAU,SAAsC,QAAQ,KAAK,UAAU;GAC9E,IAAI,UAAU,UAAU;GAExB,MAAM,UAAU,IAAI,OAAO,UAAwB,IAAI,QAAQ,KAAK,CAAC;GAErE,WAAW,QAAQ,OACjB,OACA,SACsB;IACtB,MAAM,UAAUA,2BAAAA,MAAM;IACtB,MAAM,MAAMC,2BAAAA,gBAAgB,WAAW,KAAK,GAAG,QAAQ,WAAW;IAClE,MAAM,SAAS,cAAc,OAAO,IAAI;IACxC,IAAI;KACF,MAAM,WAAW,MAAM,SAAS,OAAO,IAAI;KAG3C,IADE,SAAS,UAAU,aAAc,cAAcC,2BAAAA,aAAa,YAAY,MAAM,GAE9E,QAAQ;MACN,OAAO,SAAS,UAAU,YAAY,SAAS;MAC/C,SAAS,SAAS,SAAS,OAAO,GAAG,OAAO,GAAG;MAC/C,OAAO,EACL,MAAM;OACJ,MAAM;OACN;OACA;OACA,QAAQ,SAAS;OACjB,IAAI,SAAS;OACb,YAAYC,2BAAAA,WAAW,OAAO;OAC9B,gBAAgBC,2BAAAA,mBACdC,2BAAAA,gBAAgB,MAAM,OAAO,GAC7B,QAAQ,qBACV;OACA,iBAAiBD,2BAAAA,mBACf,SAAS,SACT,QAAQ,sBACV;MACF,EACF;KACF,CAAC;KAEH,OAAO;IACT,SAAS,OAAO;KACd,QAAQ;MACN,OAAO;MACP,SAAS,uBAAuB,OAAO,GAAG;MAC1C;MACA,OAAO;OACL,MAAM;QAAE,MAAM;QAAS;QAAQ;QAAK,YAAYD,2BAAAA,WAAW,OAAO;OAAE;OACpE,QAAA,GAAA,eAAA,eAAA,CAAsB,OAAO,EAAE,UAAU,EAAE,CAAC;MAC9C;KACF,CAAC;KACD,MAAM;IACR;GACF;GAEA,aAAa;IACX,WAAW,QAAQ;GACrB;EACF;CACF;AACF"}
|
|
@@ -1,49 +1,5 @@
|
|
|
1
|
+
import { a as sanitizeHttpUrl, i as pickAllowedHeaders, n as headersFromInit, o as shouldSample, r as nowMs, t as durationMs } from "./chunks/http-capture-utils-_hG5wepA.js";
|
|
1
2
|
import { normalizeValue } from "@loggerjs/core";
|
|
2
|
-
//#region src/http-capture-utils.ts
|
|
3
|
-
function nowMs() {
|
|
4
|
-
return typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
5
|
-
}
|
|
6
|
-
function durationMs(started) {
|
|
7
|
-
return nowMs() - started;
|
|
8
|
-
}
|
|
9
|
-
function sanitizeHttpUrl(rawUrl, sanitizer) {
|
|
10
|
-
const stripped = stripSensitiveUrlParts(rawUrl);
|
|
11
|
-
return sanitizer ? sanitizer(stripped) : stripped;
|
|
12
|
-
}
|
|
13
|
-
function pickAllowedHeaders(headers, allowList) {
|
|
14
|
-
if (!headers || !allowList || allowList.length === 0) return void 0;
|
|
15
|
-
const out = {};
|
|
16
|
-
for (const name of allowList) {
|
|
17
|
-
const value = headers.get(name);
|
|
18
|
-
if (value !== null) out[name.toLowerCase()] = value;
|
|
19
|
-
}
|
|
20
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
21
|
-
}
|
|
22
|
-
function headersFromInit(headers) {
|
|
23
|
-
if (!headers) return void 0;
|
|
24
|
-
try {
|
|
25
|
-
return new Headers(headers);
|
|
26
|
-
} catch {
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
function shouldSample(sampleRate, random) {
|
|
31
|
-
if (sampleRate >= 1) return true;
|
|
32
|
-
if (sampleRate <= 0) return false;
|
|
33
|
-
return random() < sampleRate;
|
|
34
|
-
}
|
|
35
|
-
function stripSensitiveUrlParts(rawUrl) {
|
|
36
|
-
if (!/^[a-zA-Z][a-zA-Z\d+.-]*:/.test(rawUrl)) return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
|
|
37
|
-
try {
|
|
38
|
-
const url = new URL(rawUrl);
|
|
39
|
-
url.search = "";
|
|
40
|
-
url.hash = "";
|
|
41
|
-
return url.toString();
|
|
42
|
-
} catch {
|
|
43
|
-
return rawUrl.split(/[?#]/, 1)[0] ?? rawUrl;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
//#endregion
|
|
47
3
|
//#region src/fetch-integration.ts
|
|
48
4
|
function requestUrl(input) {
|
|
49
5
|
if (typeof input === "string") return input;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fetch-integration.js","names":[],"sources":["../src/
|
|
1
|
+
{"version":3,"file":"fetch-integration.js","names":[],"sources":["../src/fetch-integration.ts"],"sourcesContent":["import {\n normalizeValue,\n type CaptureInput,\n type Integration,\n type IntegrationSetupContext,\n type UnpatchedFunction,\n} from \"@loggerjs/core\";\nimport {\n durationMs,\n headersFromInit,\n nowMs,\n pickAllowedHeaders,\n sanitizeHttpUrl,\n shouldSample,\n} from \"./http-capture-utils\";\n\nexport interface CaptureFetchOptions {\n minStatus?: number;\n captureRequestHeaders?: readonly string[];\n captureResponseHeaders?: readonly string[];\n captureAll?: boolean;\n captureSuccessful?: boolean;\n sampleRate?: number;\n random?: () => number;\n sanitizeUrl?: (url: string) => string;\n}\n\ntype BrowserFetch = typeof fetch;\n\nfunction requestUrl(input: RequestInfo | URL): string {\n if (typeof input === \"string\") return input;\n if (input instanceof URL) return input.toString();\n return input.url;\n}\n\nfunction requestMethod(input: RequestInfo | URL, init?: RequestInit): string {\n if (init?.method) return init.method.toUpperCase();\n if (typeof input === \"object\" && \"method\" in input && input.method)\n return input.method.toUpperCase();\n return \"GET\";\n}\n\nexport function captureFetchIntegration(options: CaptureFetchOptions = {}): Integration {\n const minStatus = options.minStatus ?? 400;\n const captureAll = options.captureAll ?? options.captureSuccessful ?? false;\n const sampleRate = options.sampleRate ?? 1;\n const random = options.random ?? Math.random;\n\n return {\n name: \"capture-fetch\",\n setup(api: IntegrationSetupContext) {\n const current = globalThis.fetch;\n if (!current) return;\n const original =\n (api.unpatched.fetch as BrowserFetch | undefined) ?? current.bind(globalThis);\n api.unpatched.fetch ??= original as UnpatchedFunction;\n\n const capture = api.guard((input: CaptureInput) => api.capture(input));\n\n globalThis.fetch = async (\n input: RequestInfo | URL,\n init?: RequestInit,\n ): Promise<Response> => {\n const started = nowMs();\n const url = sanitizeHttpUrl(requestUrl(input), options.sanitizeUrl);\n const method = requestMethod(input, init);\n try {\n const response = await original(input, init);\n const shouldCapture =\n response.status >= minStatus || (captureAll && shouldSample(sampleRate, random));\n if (shouldCapture) {\n capture({\n level: response.status >= minStatus ? \"warn\" : \"debug\",\n message: `Fetch ${response.status} ${method} ${url}`,\n props: {\n http: {\n kind: \"fetch\",\n method,\n url,\n status: response.status,\n ok: response.ok,\n durationMs: durationMs(started),\n requestHeaders: pickAllowedHeaders(\n headersFromInit(init?.headers),\n options.captureRequestHeaders,\n ),\n responseHeaders: pickAllowedHeaders(\n response.headers,\n options.captureResponseHeaders,\n ),\n },\n },\n });\n }\n return response;\n } catch (error) {\n capture({\n level: \"error\",\n message: `Fetch network error ${method} ${url}`,\n error,\n props: {\n http: { kind: \"fetch\", method, url, durationMs: durationMs(started) },\n input: normalizeValue(input, { maxDepth: 3 }),\n },\n });\n throw error;\n }\n };\n\n return () => {\n globalThis.fetch = current;\n };\n },\n };\n}\n"],"mappings":";;;AA6BA,SAAS,WAAW,OAAkC;CACpD,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,IAAI,iBAAiB,KAAK,OAAO,MAAM,SAAS;CAChD,OAAO,MAAM;AACf;AAEA,SAAS,cAAc,OAA0B,MAA4B;CAC3E,IAAI,MAAM,QAAQ,OAAO,KAAK,OAAO,YAAY;CACjD,IAAI,OAAO,UAAU,YAAY,YAAY,SAAS,MAAM,QAC1D,OAAO,MAAM,OAAO,YAAY;CAClC,OAAO;AACT;AAEA,SAAgB,wBAAwB,UAA+B,CAAC,GAAgB;CACtF,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,aAAa,QAAQ,cAAc,QAAQ,qBAAqB;CACtE,MAAM,aAAa,QAAQ,cAAc;CACzC,MAAM,SAAS,QAAQ,UAAU,KAAK;CAEtC,OAAO;EACL,MAAM;EACN,MAAM,KAA8B;GAClC,MAAM,UAAU,WAAW;GAC3B,IAAI,CAAC,SAAS;GACd,MAAM,WACH,IAAI,UAAU,SAAsC,QAAQ,KAAK,UAAU;GAC9E,IAAI,UAAU,UAAU;GAExB,MAAM,UAAU,IAAI,OAAO,UAAwB,IAAI,QAAQ,KAAK,CAAC;GAErE,WAAW,QAAQ,OACjB,OACA,SACsB;IACtB,MAAM,UAAU,MAAM;IACtB,MAAM,MAAM,gBAAgB,WAAW,KAAK,GAAG,QAAQ,WAAW;IAClE,MAAM,SAAS,cAAc,OAAO,IAAI;IACxC,IAAI;KACF,MAAM,WAAW,MAAM,SAAS,OAAO,IAAI;KAG3C,IADE,SAAS,UAAU,aAAc,cAAc,aAAa,YAAY,MAAM,GAE9E,QAAQ;MACN,OAAO,SAAS,UAAU,YAAY,SAAS;MAC/C,SAAS,SAAS,SAAS,OAAO,GAAG,OAAO,GAAG;MAC/C,OAAO,EACL,MAAM;OACJ,MAAM;OACN;OACA;OACA,QAAQ,SAAS;OACjB,IAAI,SAAS;OACb,YAAY,WAAW,OAAO;OAC9B,gBAAgB,mBACd,gBAAgB,MAAM,OAAO,GAC7B,QAAQ,qBACV;OACA,iBAAiB,mBACf,SAAS,SACT,QAAQ,sBACV;MACF,EACF;KACF,CAAC;KAEH,OAAO;IACT,SAAS,OAAO;KACd,QAAQ;MACN,OAAO;MACP,SAAS,uBAAuB,OAAO,GAAG;MAC1C;MACA,OAAO;OACL,MAAM;QAAE,MAAM;QAAS;QAAQ;QAAK,YAAY,WAAW,OAAO;OAAE;OACpE,OAAO,eAAe,OAAO,EAAE,UAAU,EAAE,CAAC;MAC9C;KACF,CAAC;KACD,MAAM;IACR;GACF;GAEA,aAAa;IACX,WAAW,QAAQ;GACrB;EACF;CACF;AACF"}
|