@amplitude/session-replay-browser 1.47.5 → 1.48.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/lib/cjs/config/types.d.ts +3 -2
- package/lib/cjs/config/types.d.ts.map +1 -1
- package/lib/cjs/config/types.js.map +1 -1
- package/lib/cjs/network-body-capture.d.ts +21 -0
- package/lib/cjs/network-body-capture.d.ts.map +1 -0
- package/lib/cjs/network-body-capture.js +47 -0
- package/lib/cjs/network-body-capture.js.map +1 -0
- package/lib/cjs/observers.d.ts +3 -2
- package/lib/cjs/observers.d.ts.map +1 -1
- package/lib/cjs/observers.js +5 -4
- package/lib/cjs/observers.js.map +1 -1
- package/lib/cjs/version.d.ts +1 -1
- package/lib/cjs/version.js +1 -1
- package/lib/cjs/version.js.map +1 -1
- package/lib/esm/config/types.d.ts +3 -2
- package/lib/esm/config/types.d.ts.map +1 -1
- package/lib/esm/config/types.js.map +1 -1
- package/lib/esm/network-body-capture.d.ts +21 -0
- package/lib/esm/network-body-capture.d.ts.map +1 -0
- package/lib/esm/network-body-capture.js +40 -0
- package/lib/esm/network-body-capture.js.map +1 -0
- package/lib/esm/observers.d.ts +3 -2
- package/lib/esm/observers.d.ts.map +1 -1
- package/lib/esm/observers.js +5 -4
- package/lib/esm/observers.js.map +1 -1
- package/lib/esm/version.d.ts +1 -1
- package/lib/esm/version.js +1 -1
- package/lib/esm/version.js.map +1 -1
- package/lib/scripts/index-min.js +1 -1
- package/lib/scripts/index-min.js.gz +0 -0
- package/lib/scripts/index-min.js.map +1 -1
- package/lib/scripts/observers-min.js +1 -1
- package/lib/scripts/observers-min.js.gz +0 -0
- package/lib/scripts/observers-min.js.map +1 -1
- package/lib/scripts/session-replay-browser-min.js +1 -1
- package/lib/scripts/session-replay-browser-min.js.gz +0 -0
- package/lib/scripts/session-replay-browser-min.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { pruneJson } from '@amplitude/analytics-core';
|
|
2
|
+
export var isBodyCaptureEnabled = function (config) {
|
|
3
|
+
if (config === undefined) {
|
|
4
|
+
return false;
|
|
5
|
+
}
|
|
6
|
+
if (typeof config === 'boolean') {
|
|
7
|
+
return config;
|
|
8
|
+
}
|
|
9
|
+
return config.enabled !== false;
|
|
10
|
+
};
|
|
11
|
+
export var getBodyMaskingLists = function (config) {
|
|
12
|
+
var _a;
|
|
13
|
+
if (typeof config === 'object' && config !== null) {
|
|
14
|
+
return {
|
|
15
|
+
allowlist: (_a = config.allowlist) !== null && _a !== void 0 ? _a : [],
|
|
16
|
+
excludelist: config.excludelist || config.blocklist || [],
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
return { allowlist: [], excludelist: [] };
|
|
20
|
+
};
|
|
21
|
+
export var applyBodyMasking = function (serialized, allowlist, excludelist) {
|
|
22
|
+
if (allowlist.length === 0 && excludelist.length === 0) {
|
|
23
|
+
return serialized;
|
|
24
|
+
}
|
|
25
|
+
var effectiveAllowlist = allowlist.length > 0 ? allowlist : ['/**'];
|
|
26
|
+
try {
|
|
27
|
+
var json = JSON.parse(serialized);
|
|
28
|
+
pruneJson(json, effectiveAllowlist, excludelist);
|
|
29
|
+
return JSON.stringify(json);
|
|
30
|
+
}
|
|
31
|
+
catch (_a) {
|
|
32
|
+
return serialized;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
export var captureSerializedBody = function (serialized, config, maxBytes, truncate) {
|
|
36
|
+
var _a = getBodyMaskingLists(config), allowlist = _a.allowlist, excludelist = _a.excludelist;
|
|
37
|
+
var masked = applyBodyMasking(serialized, allowlist, excludelist);
|
|
38
|
+
return truncate(masked, maxBytes);
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=network-body-capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"network-body-capture.js","sourceRoot":"","sources":["../../src/network-body-capture.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAYtD,MAAM,CAAC,IAAM,oBAAoB,GAAG,UAAC,MAAyC;IAC5E,IAAI,MAAM,KAAK,SAAS,EAAE;QACxB,OAAO,KAAK,CAAC;KACd;IACD,IAAI,OAAO,MAAM,KAAK,SAAS,EAAE;QAC/B,OAAO,MAAM,CAAC;KACf;IACD,OAAO,MAAM,CAAC,OAAO,KAAK,KAAK,CAAC;AAClC,CAAC,CAAC;AAEF,MAAM,CAAC,IAAM,mBAAmB,GAAG,UACjC,MAAyC;;IAEzC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,EAAE;QACjD,OAAO;YACL,SAAS,EAAE,MAAA,MAAM,CAAC,SAAS,mCAAI,EAAE;YACjC,WAAW,EAAE,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,SAAS,IAAI,EAAE;SAC1D,CAAC;KACH;IACD,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC;AAC5C,CAAC,CAAC;AAEF,MAAM,CAAC,IAAM,gBAAgB,GAAG,UAAC,UAAkB,EAAE,SAAmB,EAAE,WAAqB;IAC7F,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,EAAE;QACtD,OAAO,UAAU,CAAC;KACnB;IAED,IAAM,kBAAkB,GAAG,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;IAEtE,IAAI;QACF,IAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,CAA4B,CAAC;QAC/D,SAAS,CAAC,IAAI,EAAE,kBAAkB,EAAE,WAAW,CAAC,CAAC;QACjD,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;KAC7B;IAAC,WAAM;QACN,OAAO,UAAU,CAAC;KACnB;AACH,CAAC,CAAC;AAEF,MAAM,CAAC,IAAM,qBAAqB,GAAG,UACnC,UAAkB,EAClB,MAAyC,EACzC,QAAgB,EAChB,QAA+E;IAEzE,IAAA,KAA6B,mBAAmB,CAAC,MAAM,CAAC,EAAtD,SAAS,eAAA,EAAE,WAAW,iBAAgC,CAAC;IAC/D,IAAM,MAAM,GAAG,gBAAgB,CAAC,UAAU,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;IACpE,OAAO,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AACpC,CAAC,CAAC","sourcesContent":["import { pruneJson } from '@amplitude/analytics-core';\n\nexport type BodyCaptureRuleConfig =\n | boolean\n | {\n enabled?: boolean;\n allowlist?: string[];\n /** @deprecated use excludelist */\n blocklist?: string[];\n excludelist?: string[];\n };\n\nexport const isBodyCaptureEnabled = (config: BodyCaptureRuleConfig | undefined): boolean => {\n if (config === undefined) {\n return false;\n }\n if (typeof config === 'boolean') {\n return config;\n }\n return config.enabled !== false;\n};\n\nexport const getBodyMaskingLists = (\n config: BodyCaptureRuleConfig | undefined,\n): { allowlist: string[]; excludelist: string[] } => {\n if (typeof config === 'object' && config !== null) {\n return {\n allowlist: config.allowlist ?? [],\n excludelist: config.excludelist || config.blocklist || [],\n };\n }\n return { allowlist: [], excludelist: [] };\n};\n\nexport const applyBodyMasking = (serialized: string, allowlist: string[], excludelist: string[]): string => {\n if (allowlist.length === 0 && excludelist.length === 0) {\n return serialized;\n }\n\n const effectiveAllowlist = allowlist.length > 0 ? allowlist : ['/**'];\n\n try {\n const json = JSON.parse(serialized) as Record<string, unknown>;\n pruneJson(json, effectiveAllowlist, excludelist);\n return JSON.stringify(json);\n } catch {\n return serialized;\n }\n};\n\nexport const captureSerializedBody = (\n serialized: string,\n config: BodyCaptureRuleConfig | undefined,\n maxBytes: number,\n truncate: (value: string, max: number) => { value: string; truncated: boolean },\n): { value: string; truncated: boolean } => {\n const { allowlist, excludelist } = getBodyMaskingLists(config);\n const masked = applyBodyMasking(serialized, allowlist, excludelist);\n return truncate(masked, maxBytes);\n};\n"]}
|
package/lib/esm/observers.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type BodyCaptureRuleConfig } from './network-body-capture';
|
|
1
2
|
export type ResponseBodyStatus = 'captured' | 'truncated' | 'skipped_binary' | 'error';
|
|
2
3
|
export interface NetworkRequestEvent {
|
|
3
4
|
timestamp: number;
|
|
@@ -18,8 +19,8 @@ export interface NetworkRequestEvent {
|
|
|
18
19
|
}
|
|
19
20
|
export type NetworkEventCallback = (event: NetworkRequestEvent) => void;
|
|
20
21
|
export interface NetworkBodyConfig {
|
|
21
|
-
request?:
|
|
22
|
-
response?:
|
|
22
|
+
request?: BodyCaptureRuleConfig;
|
|
23
|
+
response?: BodyCaptureRuleConfig;
|
|
23
24
|
maxBodySizeBytes?: number;
|
|
24
25
|
}
|
|
25
26
|
export interface NetworkConfig {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observers.d.ts","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,WAAW,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAEvF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"observers.d.ts","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAA+C,MAAM,wBAAwB,CAAC;AAEjH,MAAM,MAAM,kBAAkB,GAAG,UAAU,GAAG,WAAW,GAAG,gBAAgB,GAAG,OAAO,CAAC;AAEvF,MAAM,WAAW,mBAAmB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;AAExE,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAkDD,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,aAAa,CAA6B;IAClD,OAAO,CAAC,aAAa,CAAC,CAAuB;IAC7C,OAAO,CAAC,aAAa,CAAC,CAAgB;IAEtC,KAAK,CAAC,aAAa,EAAE,oBAAoB,EAAE,aAAa,CAAC,EAAE,aAAa;IAMxE,IAAI;IAOJ,SAAS,CAAC,WAAW,CAAC,KAAK,EAAE,mBAAmB;IAIhD,OAAO,CAAC,YAAY;CAkGrB"}
|
package/lib/esm/observers.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { __awaiter, __generator } from "tslib";
|
|
2
2
|
import { getGlobalScope } from '@amplitude/analytics-core';
|
|
3
|
+
import { captureSerializedBody, isBodyCaptureEnabled } from './network-body-capture';
|
|
3
4
|
var DEFAULT_MAX_BODY_SIZE_BYTES = 10240; // 10KB
|
|
4
5
|
var BINARY_CONTENT_TYPE_PREFIXES = ['image/', 'audio/', 'video/', 'application/octet-stream', 'font/'];
|
|
5
6
|
function isBinaryContentType(contentType) {
|
|
@@ -92,11 +93,11 @@ var NetworkObservers = /** @class */ (function () {
|
|
|
92
93
|
requestHeaders: init === null || init === void 0 ? void 0 : init.headers,
|
|
93
94
|
};
|
|
94
95
|
bodyConfig = (_a = this.networkConfig) === null || _a === void 0 ? void 0 : _a.body;
|
|
95
|
-
if (bodyConfig
|
|
96
|
+
if (bodyConfig && isBodyCaptureEnabled(bodyConfig.request)) {
|
|
96
97
|
serialized = serializeRequestBody(init === null || init === void 0 ? void 0 : init.body);
|
|
97
98
|
if (serialized !== undefined) {
|
|
98
99
|
maxBytes = (_b = bodyConfig.maxBodySizeBytes) !== null && _b !== void 0 ? _b : DEFAULT_MAX_BODY_SIZE_BYTES;
|
|
99
|
-
requestEvent.requestBody =
|
|
100
|
+
requestEvent.requestBody = captureSerializedBody(serialized, bodyConfig.request, maxBytes, truncateToByteLimit).value;
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
_c.label = 1;
|
|
@@ -113,7 +114,7 @@ var NetworkObservers = /** @class */ (function () {
|
|
|
113
114
|
headers_1[key] = value;
|
|
114
115
|
});
|
|
115
116
|
requestEvent.responseHeaders = headers_1;
|
|
116
|
-
if (bodyConfig
|
|
117
|
+
if (bodyConfig && isBodyCaptureEnabled(bodyConfig.response)) {
|
|
117
118
|
contentType = headers_1['content-type'] || null;
|
|
118
119
|
if (isBinaryContentType(contentType)) {
|
|
119
120
|
requestEvent.responseBodyStatus = 'skipped_binary';
|
|
@@ -125,7 +126,7 @@ var NetworkObservers = /** @class */ (function () {
|
|
|
125
126
|
cloned.text().then(function (text) {
|
|
126
127
|
var _a;
|
|
127
128
|
var maxBytes = (_a = bodyConfig.maxBodySizeBytes) !== null && _a !== void 0 ? _a : DEFAULT_MAX_BODY_SIZE_BYTES;
|
|
128
|
-
var _b =
|
|
129
|
+
var _b = captureSerializedBody(text, bodyConfig.response, maxBytes, truncateToByteLimit), value = _b.value, truncated = _b.truncated;
|
|
129
130
|
requestEvent.responseBody = value;
|
|
130
131
|
requestEvent.responseBodyStatus = truncated ? 'truncated' : 'captured';
|
|
131
132
|
_this.notifyEvent(requestEvent);
|
package/lib/esm/observers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observers.js","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\n\nexport type ResponseBodyStatus = 'captured' | 'truncated' | 'skipped_binary' | 'error';\n\nexport interface NetworkRequestEvent {\n timestamp: number;\n type: 'fetch';\n method: string;\n url: string;\n status?: number;\n duration?: number;\n requestHeaders?: Record<string, string>;\n responseHeaders?: Record<string, string>;\n requestBody?: string;\n responseBody?: string;\n responseBodyStatus?: ResponseBodyStatus;\n error?: {\n name: string;\n message: string;\n };\n}\n\nexport type NetworkEventCallback = (event: NetworkRequestEvent) => void;\n\nexport interface NetworkBodyConfig {\n request?: boolean;\n response?: boolean;\n maxBodySizeBytes?: number;\n}\n\nexport interface NetworkConfig {\n enabled: boolean;\n body?: NetworkBodyConfig;\n}\n\nconst DEFAULT_MAX_BODY_SIZE_BYTES = 10240; // 10KB\n\nconst BINARY_CONTENT_TYPE_PREFIXES = ['image/', 'audio/', 'video/', 'application/octet-stream', 'font/'];\n\nfunction isBinaryContentType(contentType: string | null): boolean {\n if (!contentType) return false;\n return BINARY_CONTENT_TYPE_PREFIXES.some((prefix) => contentType.toLowerCase().startsWith(prefix));\n}\n\nfunction serializeRequestBody(body: BodyInit | null | undefined): string | undefined {\n if (body === null || body === undefined) return undefined;\n if (typeof body === 'string') return body;\n if (body instanceof URLSearchParams) return body.toString();\n if (body instanceof FormData) {\n const parts: string[] = [];\n body.forEach((value, key) => {\n parts.push(`${key}=${typeof value === 'string' ? value : '[File]'}`);\n });\n return parts.join('&');\n }\n // Blob, ArrayBuffer, ArrayBufferView, ReadableStream — skip\n return undefined;\n}\n\nfunction truncateToByteLimit(str: string, maxBytes: number): { value: string; truncated: boolean } {\n if (new Blob([str]).size <= maxBytes) {\n return { value: str, truncated: false };\n }\n // Binary search for the longest prefix whose UTF-8 byte length fits within maxBytes.\n // Cap hi at maxBytes since each UTF-8 character is at least 1 byte, so no more than\n // maxBytes characters can ever fit — this avoids large intermediate Blob allocations.\n let lo = 0;\n let hi = Math.min(str.length, maxBytes);\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (new Blob([str.slice(0, mid)]).size <= maxBytes) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n // Avoid splitting a surrogate pair: if lo landed after a high surrogate, back up one position\n if (lo > 0 && str.charCodeAt(lo - 1) >= 0xd800 && str.charCodeAt(lo - 1) <= 0xdbff) {\n lo -= 1;\n }\n return { value: str.slice(0, lo), truncated: true };\n}\n\nexport class NetworkObservers {\n private fetchObserver: (() => void) | null = null;\n private eventCallback?: NetworkEventCallback;\n private networkConfig?: NetworkConfig;\n\n start(eventCallback: NetworkEventCallback, networkConfig?: NetworkConfig) {\n this.eventCallback = eventCallback;\n this.networkConfig = networkConfig;\n this.observeFetch();\n }\n\n stop() {\n this.fetchObserver?.();\n this.fetchObserver = null;\n this.eventCallback = undefined;\n this.networkConfig = undefined;\n }\n\n protected notifyEvent(event: NetworkRequestEvent) {\n this.eventCallback?.(event);\n }\n\n private observeFetch() {\n const globalScope = getGlobalScope();\n if (!globalScope) return;\n\n const originalFetch = globalScope.fetch;\n if (!originalFetch) return;\n\n globalScope.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {\n const startTime = Date.now();\n const requestEvent: NetworkRequestEvent = {\n timestamp: startTime,\n type: 'fetch',\n method: init?.method || 'GET', // Fetch API defaulted to GET when no method is provided\n url: input.toString(),\n requestHeaders: init?.headers as Record<string, string>,\n };\n\n // Capture request body if configured\n const bodyConfig = this.networkConfig?.body;\n if (bodyConfig?.request) {\n const serialized = serializeRequestBody(init?.body);\n if (serialized !== undefined) {\n const maxBytes = bodyConfig.maxBodySizeBytes ?? DEFAULT_MAX_BODY_SIZE_BYTES;\n requestEvent.requestBody = truncateToByteLimit(serialized, maxBytes).value;\n }\n }\n\n try {\n const response = await originalFetch(input, init);\n const endTime = Date.now();\n\n requestEvent.status = response.status;\n requestEvent.duration = endTime - startTime;\n\n // Convert Headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n requestEvent.responseHeaders = headers;\n\n if (bodyConfig?.response) {\n const contentType = headers['content-type'] || null;\n if (isBinaryContentType(contentType)) {\n requestEvent.responseBodyStatus = 'skipped_binary';\n this.notifyEvent(requestEvent);\n } else {\n const cloned = response.clone();\n // Read body without blocking the response return to the caller\n cloned.text().then(\n (text) => {\n const maxBytes = bodyConfig.maxBodySizeBytes ?? DEFAULT_MAX_BODY_SIZE_BYTES;\n const { value, truncated } = truncateToByteLimit(text, maxBytes);\n requestEvent.responseBody = value;\n requestEvent.responseBodyStatus = truncated ? 'truncated' : 'captured';\n this.notifyEvent(requestEvent);\n },\n () => {\n requestEvent.responseBodyStatus = 'error';\n this.notifyEvent(requestEvent);\n },\n );\n }\n } else {\n this.notifyEvent(requestEvent);\n }\n\n return response;\n } catch (error) {\n const endTime = Date.now();\n requestEvent.duration = endTime - startTime;\n\n // Capture error information\n const typedError = error as Error;\n requestEvent.error = {\n name: typedError.name || 'UnknownError',\n message: typedError.message || 'An unknown error occurred',\n };\n\n this.notifyEvent(requestEvent);\n throw error;\n }\n };\n\n this.fetchObserver = () => {\n globalScope.fetch = originalFetch;\n };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"observers.js","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\n\nimport { type BodyCaptureRuleConfig, captureSerializedBody, isBodyCaptureEnabled } from './network-body-capture';\n\nexport type ResponseBodyStatus = 'captured' | 'truncated' | 'skipped_binary' | 'error';\n\nexport interface NetworkRequestEvent {\n timestamp: number;\n type: 'fetch';\n method: string;\n url: string;\n status?: number;\n duration?: number;\n requestHeaders?: Record<string, string>;\n responseHeaders?: Record<string, string>;\n requestBody?: string;\n responseBody?: string;\n responseBodyStatus?: ResponseBodyStatus;\n error?: {\n name: string;\n message: string;\n };\n}\n\nexport type NetworkEventCallback = (event: NetworkRequestEvent) => void;\n\nexport interface NetworkBodyConfig {\n request?: BodyCaptureRuleConfig;\n response?: BodyCaptureRuleConfig;\n maxBodySizeBytes?: number;\n}\n\nexport interface NetworkConfig {\n enabled: boolean;\n body?: NetworkBodyConfig;\n}\n\nconst DEFAULT_MAX_BODY_SIZE_BYTES = 10240; // 10KB\n\nconst BINARY_CONTENT_TYPE_PREFIXES = ['image/', 'audio/', 'video/', 'application/octet-stream', 'font/'];\n\nfunction isBinaryContentType(contentType: string | null): boolean {\n if (!contentType) return false;\n return BINARY_CONTENT_TYPE_PREFIXES.some((prefix) => contentType.toLowerCase().startsWith(prefix));\n}\n\nfunction serializeRequestBody(body: BodyInit | null | undefined): string | undefined {\n if (body === null || body === undefined) return undefined;\n if (typeof body === 'string') return body;\n if (body instanceof URLSearchParams) return body.toString();\n if (body instanceof FormData) {\n const parts: string[] = [];\n body.forEach((value, key) => {\n parts.push(`${key}=${typeof value === 'string' ? value : '[File]'}`);\n });\n return parts.join('&');\n }\n // Blob, ArrayBuffer, ArrayBufferView, ReadableStream — skip\n return undefined;\n}\n\nfunction truncateToByteLimit(str: string, maxBytes: number): { value: string; truncated: boolean } {\n if (new Blob([str]).size <= maxBytes) {\n return { value: str, truncated: false };\n }\n // Binary search for the longest prefix whose UTF-8 byte length fits within maxBytes.\n // Cap hi at maxBytes since each UTF-8 character is at least 1 byte, so no more than\n // maxBytes characters can ever fit — this avoids large intermediate Blob allocations.\n let lo = 0;\n let hi = Math.min(str.length, maxBytes);\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (new Blob([str.slice(0, mid)]).size <= maxBytes) {\n lo = mid;\n } else {\n hi = mid - 1;\n }\n }\n // Avoid splitting a surrogate pair: if lo landed after a high surrogate, back up one position\n if (lo > 0 && str.charCodeAt(lo - 1) >= 0xd800 && str.charCodeAt(lo - 1) <= 0xdbff) {\n lo -= 1;\n }\n return { value: str.slice(0, lo), truncated: true };\n}\n\nexport class NetworkObservers {\n private fetchObserver: (() => void) | null = null;\n private eventCallback?: NetworkEventCallback;\n private networkConfig?: NetworkConfig;\n\n start(eventCallback: NetworkEventCallback, networkConfig?: NetworkConfig) {\n this.eventCallback = eventCallback;\n this.networkConfig = networkConfig;\n this.observeFetch();\n }\n\n stop() {\n this.fetchObserver?.();\n this.fetchObserver = null;\n this.eventCallback = undefined;\n this.networkConfig = undefined;\n }\n\n protected notifyEvent(event: NetworkRequestEvent) {\n this.eventCallback?.(event);\n }\n\n private observeFetch() {\n const globalScope = getGlobalScope();\n if (!globalScope) return;\n\n const originalFetch = globalScope.fetch;\n if (!originalFetch) return;\n\n globalScope.fetch = async (input: RequestInfo | URL, init?: RequestInit) => {\n const startTime = Date.now();\n const requestEvent: NetworkRequestEvent = {\n timestamp: startTime,\n type: 'fetch',\n method: init?.method || 'GET', // Fetch API defaulted to GET when no method is provided\n url: input.toString(),\n requestHeaders: init?.headers as Record<string, string>,\n };\n\n // Capture request body if configured\n const bodyConfig = this.networkConfig?.body;\n if (bodyConfig && isBodyCaptureEnabled(bodyConfig.request)) {\n const serialized = serializeRequestBody(init?.body);\n if (serialized !== undefined) {\n const maxBytes = bodyConfig.maxBodySizeBytes ?? DEFAULT_MAX_BODY_SIZE_BYTES;\n requestEvent.requestBody = captureSerializedBody(\n serialized,\n bodyConfig.request,\n maxBytes,\n truncateToByteLimit,\n ).value;\n }\n }\n\n try {\n const response = await originalFetch(input, init);\n const endTime = Date.now();\n\n requestEvent.status = response.status;\n requestEvent.duration = endTime - startTime;\n\n // Convert Headers\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n headers[key] = value;\n });\n requestEvent.responseHeaders = headers;\n\n if (bodyConfig && isBodyCaptureEnabled(bodyConfig.response)) {\n const contentType = headers['content-type'] || null;\n if (isBinaryContentType(contentType)) {\n requestEvent.responseBodyStatus = 'skipped_binary';\n this.notifyEvent(requestEvent);\n } else {\n const cloned = response.clone();\n // Read body without blocking the response return to the caller\n cloned.text().then(\n (text) => {\n const maxBytes = bodyConfig.maxBodySizeBytes ?? DEFAULT_MAX_BODY_SIZE_BYTES;\n const { value, truncated } = captureSerializedBody(\n text,\n bodyConfig.response,\n maxBytes,\n truncateToByteLimit,\n );\n requestEvent.responseBody = value;\n requestEvent.responseBodyStatus = truncated ? 'truncated' : 'captured';\n this.notifyEvent(requestEvent);\n },\n () => {\n requestEvent.responseBodyStatus = 'error';\n this.notifyEvent(requestEvent);\n },\n );\n }\n } else {\n this.notifyEvent(requestEvent);\n }\n\n return response;\n } catch (error) {\n const endTime = Date.now();\n requestEvent.duration = endTime - startTime;\n\n // Capture error information\n const typedError = error as Error;\n requestEvent.error = {\n name: typedError.name || 'UnknownError',\n message: typedError.message || 'An unknown error occurred',\n };\n\n this.notifyEvent(requestEvent);\n throw error;\n }\n };\n\n this.fetchObserver = () => {\n globalScope.fetch = originalFetch;\n };\n }\n}\n"]}
|
package/lib/esm/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "1.
|
|
1
|
+
export declare const VERSION = "1.48.1";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/lib/esm/version.js
CHANGED
package/lib/esm/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,MAAM,CAAC,IAAM,OAAO,GAAG,QAAQ,CAAC","sourcesContent":["// Autogenerated by `pnpm version-file`. DO NOT EDIT\nexport const VERSION = '1.
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,MAAM,CAAC,IAAM,OAAO,GAAG,QAAQ,CAAC","sourcesContent":["// Autogenerated by `pnpm version-file`. DO NOT EDIT\nexport const VERSION = '1.48.1';\n"]}
|