@amplitude/session-replay-browser 1.50.4 → 1.51.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/README.md +15 -0
- package/lib/cjs/config/joined-config.d.ts.map +1 -1
- package/lib/cjs/config/joined-config.js +6 -25
- package/lib/cjs/config/joined-config.js.map +1 -1
- package/lib/cjs/config/types.d.ts +2 -25
- package/lib/cjs/config/types.d.ts.map +1 -1
- package/lib/cjs/config/types.js +0 -2
- package/lib/cjs/config/types.js.map +1 -1
- package/lib/cjs/events/base-events-store.d.ts +8 -5
- package/lib/cjs/events/base-events-store.d.ts.map +1 -1
- package/lib/cjs/events/base-events-store.js +51 -55
- package/lib/cjs/events/base-events-store.js.map +1 -1
- package/lib/cjs/events/event-compressor.d.ts.map +1 -1
- package/lib/cjs/events/event-compressor.js +4 -2
- package/lib/cjs/events/event-compressor.js.map +1 -1
- package/lib/cjs/events/events-idb-store.d.ts.map +1 -1
- package/lib/cjs/events/events-idb-store.js +23 -8
- package/lib/cjs/events/events-idb-store.js.map +1 -1
- package/lib/cjs/events/events-manager.d.ts.map +1 -1
- package/lib/cjs/events/events-manager.js +18 -6
- package/lib/cjs/events/events-manager.js.map +1 -1
- package/lib/cjs/helpers.d.ts +1 -16
- package/lib/cjs/helpers.d.ts.map +1 -1
- package/lib/cjs/helpers.js +8 -166
- package/lib/cjs/helpers.js.map +1 -1
- package/lib/cjs/observers.d.ts.map +1 -1
- package/lib/cjs/observers.js +2 -22
- package/lib/cjs/observers.js.map +1 -1
- package/lib/cjs/session-replay.d.ts.map +1 -1
- package/lib/cjs/session-replay.js +6 -35
- package/lib/cjs/session-replay.js.map +1 -1
- package/lib/cjs/track-destination.d.ts.map +1 -1
- package/lib/cjs/track-destination.js +6 -5
- package/lib/cjs/track-destination.js.map +1 -1
- package/lib/cjs/utils/utf8-byte-length.d.ts +17 -0
- package/lib/cjs/utils/utf8-byte-length.d.ts.map +1 -0
- package/lib/cjs/utils/utf8-byte-length.js +104 -0
- package/lib/cjs/utils/utf8-byte-length.js.map +1 -0
- 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/cjs/worker/index.js +1 -1
- package/lib/esm/config/joined-config.d.ts.map +1 -1
- package/lib/esm/config/joined-config.js +6 -25
- package/lib/esm/config/joined-config.js.map +1 -1
- package/lib/esm/config/types.d.ts +2 -25
- package/lib/esm/config/types.d.ts.map +1 -1
- package/lib/esm/config/types.js +1 -1
- package/lib/esm/config/types.js.map +1 -1
- package/lib/esm/events/base-events-store.d.ts +8 -5
- package/lib/esm/events/base-events-store.d.ts.map +1 -1
- package/lib/esm/events/base-events-store.js +51 -55
- package/lib/esm/events/base-events-store.js.map +1 -1
- package/lib/esm/events/event-compressor.d.ts.map +1 -1
- package/lib/esm/events/event-compressor.js +4 -2
- package/lib/esm/events/event-compressor.js.map +1 -1
- package/lib/esm/events/events-idb-store.d.ts.map +1 -1
- package/lib/esm/events/events-idb-store.js +23 -8
- package/lib/esm/events/events-idb-store.js.map +1 -1
- package/lib/esm/events/events-manager.d.ts.map +1 -1
- package/lib/esm/events/events-manager.js +18 -6
- package/lib/esm/events/events-manager.js.map +1 -1
- package/lib/esm/helpers.d.ts +1 -16
- package/lib/esm/helpers.d.ts.map +1 -1
- package/lib/esm/helpers.js +7 -161
- package/lib/esm/helpers.js.map +1 -1
- package/lib/esm/observers.d.ts.map +1 -1
- package/lib/esm/observers.js +2 -22
- package/lib/esm/observers.js.map +1 -1
- package/lib/esm/session-replay.d.ts.map +1 -1
- package/lib/esm/session-replay.js +8 -37
- package/lib/esm/session-replay.js.map +1 -1
- package/lib/esm/track-destination.d.ts.map +1 -1
- package/lib/esm/track-destination.js +6 -5
- package/lib/esm/track-destination.js.map +1 -1
- package/lib/esm/utils/utf8-byte-length.d.ts +17 -0
- package/lib/esm/utils/utf8-byte-length.d.ts.map +1 -0
- package/lib/esm/utils/utf8-byte-length.js +97 -0
- package/lib/esm/utils/utf8-byte-length.js.map +1 -0
- 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/esm/worker/index.js +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/lib/scripts/worker-min.js +1 -1
- package/lib/scripts/worker-min.js.gz +0 -0
- package/package.json +3 -2
- package/lib/cjs/utils/get-input-type.d.ts +0 -9
- package/lib/cjs/utils/get-input-type.d.ts.map +0 -1
- package/lib/cjs/utils/get-input-type.js +0 -25
- package/lib/cjs/utils/get-input-type.js.map +0 -1
- package/lib/esm/utils/get-input-type.d.ts +0 -9
- package/lib/esm/utils/get-input-type.d.ts.map +0 -1
- package/lib/esm/utils/get-input-type.js +0 -20
- package/lib/esm/utils/get-input-type.js.map +0 -1
package/lib/cjs/helpers.js
CHANGED
|
@@ -1,130 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getDebugConfig = exports.getStorageSize = exports.getPageUrl = exports.validateUGCFilterRules = exports.generateSessionReplayId = exports.getCurrentUrl = exports.
|
|
3
|
+
exports.getDebugConfig = exports.getStorageSize = exports.getPageUrl = exports.validateUGCFilterRules = exports.generateSessionReplayId = exports.getCurrentUrl = exports.getServerUrl = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var analytics_core_1 = require("@amplitude/analytics-core");
|
|
6
|
-
var types_1 = require("./config/types");
|
|
7
6
|
var constants_1 = require("./constants");
|
|
8
|
-
var
|
|
7
|
+
var session_replay_dom_privacy_1 = require("@amplitude/session-replay-dom-privacy");
|
|
9
8
|
var server_url_1 = require("./utils/server-url");
|
|
10
9
|
Object.defineProperty(exports, "getServerUrl", { enumerable: true, get: function () { return server_url_1.getServerUrl; } });
|
|
11
|
-
/**
|
|
12
|
-
* Light: Subset of inputs (sensitive types only — password, hidden, email, tel, cc-*)
|
|
13
|
-
* Medium: All inputs (form fields), text is NOT masked
|
|
14
|
-
* Conservative: All inputs and all texts
|
|
15
|
-
*/
|
|
16
|
-
var isMaskedForLevel = function (elementType, level, element) {
|
|
17
|
-
switch (level) {
|
|
18
|
-
case 'light': {
|
|
19
|
-
if (elementType !== 'input') {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
var inputType = element ? (0, get_input_type_1.getInputType)(element) : '';
|
|
23
|
-
/* istanbul ignore if */ // TODO(lew): For some reason it's impossible to test this.
|
|
24
|
-
if (!inputType) {
|
|
25
|
-
return false;
|
|
26
|
-
}
|
|
27
|
-
if (['password', 'hidden', 'email', 'tel'].includes(inputType)) {
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
if (element.autocomplete.startsWith('cc-')) {
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
case 'medium':
|
|
36
|
-
return elementType === 'input';
|
|
37
|
-
case 'conservative':
|
|
38
|
-
return true;
|
|
39
|
-
default:
|
|
40
|
-
return isMaskedForLevel(elementType, types_1.DEFAULT_MASK_LEVEL, element);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
/**
|
|
44
|
-
* Returns the effective mask level for a given URL by checking `urlMaskLevels`
|
|
45
|
-
* (first match wins) and falling back to `defaultMaskLevel`.
|
|
46
|
-
*/
|
|
47
|
-
var getEffectiveMaskLevel = function (url, config) {
|
|
48
|
-
var e_1, _a;
|
|
49
|
-
var _b;
|
|
50
|
-
if (url && config.urlMaskLevels) {
|
|
51
|
-
try {
|
|
52
|
-
for (var _c = tslib_1.__values(config.urlMaskLevels), _d = _c.next(); !_d.done; _d = _c.next()) {
|
|
53
|
-
var rule = _d.value;
|
|
54
|
-
if (globToRegex(rule.match).test(url)) {
|
|
55
|
-
return rule.maskLevel;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
60
|
-
finally {
|
|
61
|
-
try {
|
|
62
|
-
if (_d && !_d.done && (_a = _c.return)) _a.call(_c);
|
|
63
|
-
}
|
|
64
|
-
finally { if (e_1) throw e_1.error; }
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
return (_b = config.defaultMaskLevel) !== null && _b !== void 0 ? _b : types_1.DEFAULT_MASK_LEVEL;
|
|
68
|
-
};
|
|
69
|
-
exports.getEffectiveMaskLevel = getEffectiveMaskLevel;
|
|
70
|
-
/**
|
|
71
|
-
* Checks if the given element set to be masked by rrweb
|
|
72
|
-
*
|
|
73
|
-
* Priority is:
|
|
74
|
-
* 1. [In code] Element/class based masking/unmasking <> [Config based] Selector based masking/unmasking
|
|
75
|
-
* 2. Use app defaults
|
|
76
|
-
*/
|
|
77
|
-
var isMasked = function (elementType, config, element, currentUrl) {
|
|
78
|
-
var _a, _b;
|
|
79
|
-
if (config === void 0) { config = { defaultMaskLevel: types_1.DEFAULT_MASK_LEVEL }; }
|
|
80
|
-
if (element) {
|
|
81
|
-
// Element or parent is explicitly instrumented in code to mask
|
|
82
|
-
if (element.closest('.' + constants_1.MASK_TEXT_CLASS)) {
|
|
83
|
-
return true;
|
|
84
|
-
}
|
|
85
|
-
// Config has override for mask
|
|
86
|
-
var shouldMask = ((_a = config.maskSelector) !== null && _a !== void 0 ? _a : []).some(function (selector) { return element.closest(selector); });
|
|
87
|
-
if (shouldMask) {
|
|
88
|
-
return true;
|
|
89
|
-
}
|
|
90
|
-
// Code or config has override to unmask
|
|
91
|
-
if (element.closest('.' + constants_1.UNMASK_TEXT_CLASS)) {
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
// Here we are probably sent an element, but we want to match if they have a
|
|
95
|
-
// parent with an unmask selector.
|
|
96
|
-
var shouldUnmask = ((_b = config.unmaskSelector) !== null && _b !== void 0 ? _b : []).some(function (selector) { return element.closest(selector); });
|
|
97
|
-
if (shouldUnmask) {
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return isMaskedForLevel(elementType, (0, exports.getEffectiveMaskLevel)(currentUrl, config), element);
|
|
102
|
-
};
|
|
103
|
-
exports.isMasked = isMasked;
|
|
104
|
-
var maskFn = function (elementType, config, getCurrentUrl) {
|
|
105
|
-
return function (text, element) {
|
|
106
|
-
return (0, exports.isMasked)(elementType, config, element, getCurrentUrl === null || getCurrentUrl === void 0 ? void 0 : getCurrentUrl()) ? text.replace(/[^\s]/g, '*') : text;
|
|
107
|
-
};
|
|
108
|
-
};
|
|
109
|
-
exports.maskFn = maskFn;
|
|
110
|
-
var maskAttributeFn = function (config, getCurrentUrl) {
|
|
111
|
-
return function (key, value, element) {
|
|
112
|
-
var _a;
|
|
113
|
-
// Never mask style — rrweb has a separate styleDiff path for attribute mutations
|
|
114
|
-
// that reads directly from the DOM, bypassing maskAttributeFn.
|
|
115
|
-
if (key === 'style')
|
|
116
|
-
return value;
|
|
117
|
-
// Short-circuit: only proceed if this attribute is in the allowlist.
|
|
118
|
-
if (!((_a = config === null || config === void 0 ? void 0 : config.maskAttributes) !== null && _a !== void 0 ? _a : []).includes(key))
|
|
119
|
-
return value;
|
|
120
|
-
// Use 'input' for form elements so that `medium` (which masks inputs but not text)
|
|
121
|
-
// still masks attributes on inputs/selects/textareas. For non-form elements, use
|
|
122
|
-
// 'text' so medium leaves them visible.
|
|
123
|
-
var elementType = ['INPUT', 'SELECT', 'TEXTAREA'].includes(element.tagName) ? 'input' : 'text';
|
|
124
|
-
return (0, exports.isMasked)(elementType, config, element, getCurrentUrl === null || getCurrentUrl === void 0 ? void 0 : getCurrentUrl()) ? value.replace(/[^\s]/g, '*') : value;
|
|
125
|
-
};
|
|
126
|
-
};
|
|
127
|
-
exports.maskAttributeFn = maskAttributeFn;
|
|
128
10
|
var getCurrentUrl = function () {
|
|
129
11
|
var globalScope = (0, analytics_core_1.getGlobalScope)();
|
|
130
12
|
return (globalScope === null || globalScope === void 0 ? void 0 : globalScope.location) ? globalScope.location.href : '';
|
|
@@ -142,46 +24,6 @@ var isValidGlobUrl = function (globUrl) {
|
|
|
142
24
|
return false;
|
|
143
25
|
return true;
|
|
144
26
|
};
|
|
145
|
-
var globRegexCache = new Map();
|
|
146
|
-
var globToRegex = function (glob) {
|
|
147
|
-
var cached = globRegexCache.get(glob);
|
|
148
|
-
if (cached)
|
|
149
|
-
return cached;
|
|
150
|
-
// Glob → regex conversion. Glob substitution must happen BEFORE regex escaping so that
|
|
151
|
-
// the escaper does not corrupt the glob characters (e.g. turning `/**` into `/\*\*`).
|
|
152
|
-
// Placeholder tokens use null bytes, which are illegal in HTTP URLs and therefore can
|
|
153
|
-
// never collide with real pattern content.
|
|
154
|
-
//
|
|
155
|
-
// Substitution order (most-specific first):
|
|
156
|
-
// trailing /** → (/.*)? — path with or without a trailing slash/subpath
|
|
157
|
-
// middle /**/ → /(.*\/)? — zero-or-more path segments (including zero)
|
|
158
|
-
// bare ** → .* — graceful fallback for ** not adjacent to /
|
|
159
|
-
// single * → .* — any characters (preserves existing behaviour)
|
|
160
|
-
// ? → . — single character wildcard
|
|
161
|
-
var T_TRAILING = '\x00TRAIL\x00';
|
|
162
|
-
var T_MIDDLE = '\x00MID\x00';
|
|
163
|
-
var T_DSTAR = '\x00DS\x00';
|
|
164
|
-
var T_STAR = '\x00ST\x00';
|
|
165
|
-
var T_QUEST = '\x00QU\x00';
|
|
166
|
-
var s = glob;
|
|
167
|
-
s = s.replace(/\/\*\*$/, T_TRAILING); // trailing /**
|
|
168
|
-
s = s.replace(/\/\*\*\//g, T_MIDDLE); // /**/ in the middle
|
|
169
|
-
s = s.replace(/\*\*/g, T_DSTAR); // bare ** (e.g. **.example.com)
|
|
170
|
-
s = s.replace(/\*/g, T_STAR); // single *
|
|
171
|
-
s = s.replace(/\?/g, T_QUEST); // ?
|
|
172
|
-
// Escape all remaining regex special characters.
|
|
173
|
-
s = s.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
174
|
-
// Expand tokens into their regex equivalents.
|
|
175
|
-
// Use split/join (not regex) to avoid the no-control-regex lint rule on the token strings.
|
|
176
|
-
s = s.split(T_TRAILING).join('(/.*)?'); // /** → optional /anything
|
|
177
|
-
s = s.split(T_MIDDLE).join('/(.*\\/)?'); // /**/ → /zero-or-more-segments/
|
|
178
|
-
s = s.split(T_DSTAR).join('.*'); // bare ** → .*
|
|
179
|
-
s = s.split(T_STAR).join('.*'); // * → .*
|
|
180
|
-
s = s.split(T_QUEST).join('.'); // ? → .
|
|
181
|
-
var regex = new RegExp("^".concat(s, "$"));
|
|
182
|
-
globRegexCache.set(glob, regex);
|
|
183
|
-
return regex;
|
|
184
|
-
};
|
|
185
27
|
var validateUGCFilterRules = function (ugcFilterRules) {
|
|
186
28
|
// validate ugcFilterRules
|
|
187
29
|
if (!ugcFilterRules.every(function (rule) { return typeof rule.selector === 'string' && typeof rule.replacement === 'string'; })) {
|
|
@@ -194,29 +36,29 @@ var validateUGCFilterRules = function (ugcFilterRules) {
|
|
|
194
36
|
};
|
|
195
37
|
exports.validateUGCFilterRules = validateUGCFilterRules;
|
|
196
38
|
var getPageUrl = function (pageUrl, ugcFilterRules) {
|
|
197
|
-
var
|
|
39
|
+
var e_1, _a;
|
|
198
40
|
try {
|
|
199
41
|
// apply ugcFilterRules, order is important, first rule wins
|
|
200
42
|
for (var ugcFilterRules_1 = tslib_1.__values(ugcFilterRules), ugcFilterRules_1_1 = ugcFilterRules_1.next(); !ugcFilterRules_1_1.done; ugcFilterRules_1_1 = ugcFilterRules_1.next()) {
|
|
201
43
|
var rule = ugcFilterRules_1_1.value;
|
|
202
|
-
var regex = globToRegex(rule.selector);
|
|
44
|
+
var regex = (0, session_replay_dom_privacy_1.globToRegex)(rule.selector);
|
|
203
45
|
if (regex.test(pageUrl)) {
|
|
204
46
|
return pageUrl.replace(regex, rule.replacement);
|
|
205
47
|
}
|
|
206
48
|
}
|
|
207
49
|
}
|
|
208
|
-
catch (
|
|
50
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
209
51
|
finally {
|
|
210
52
|
try {
|
|
211
53
|
if (ugcFilterRules_1_1 && !ugcFilterRules_1_1.done && (_a = ugcFilterRules_1.return)) _a.call(ugcFilterRules_1);
|
|
212
54
|
}
|
|
213
|
-
finally { if (
|
|
55
|
+
finally { if (e_1) throw e_1.error; }
|
|
214
56
|
}
|
|
215
57
|
return pageUrl;
|
|
216
58
|
};
|
|
217
59
|
exports.getPageUrl = getPageUrl;
|
|
218
60
|
var getStorageSize = function () { return tslib_1.__awaiter(void 0, void 0, void 0, function () {
|
|
219
|
-
var globalScope, _a, usage, quota, usageDetails, totalStorageSize, percentOfQuota,
|
|
61
|
+
var globalScope, _a, usage, quota, usageDetails, totalStorageSize, percentOfQuota, e_2;
|
|
220
62
|
return tslib_1.__generator(this, function (_b) {
|
|
221
63
|
switch (_b.label) {
|
|
222
64
|
case 0:
|
|
@@ -231,7 +73,7 @@ var getStorageSize = function () { return tslib_1.__awaiter(void 0, void 0, void
|
|
|
231
73
|
return [2 /*return*/, { totalStorageSize: totalStorageSize, percentOfQuota: percentOfQuota, usageDetails: JSON.stringify(usageDetails) }];
|
|
232
74
|
case 2: return [3 /*break*/, 4];
|
|
233
75
|
case 3:
|
|
234
|
-
|
|
76
|
+
e_2 = _b.sent();
|
|
235
77
|
return [3 /*break*/, 4];
|
|
236
78
|
case 4: return [2 /*return*/, { totalStorageSize: 0, percentOfQuota: 0, usageDetails: '' }];
|
|
237
79
|
}
|
package/lib/cjs/helpers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\nimport { DEFAULT_MASK_LEVEL, MaskLevel, PrivacyConfig, SessionReplayJoinedConfig, UGCFilterRule } from './config/types';\nimport { KB_SIZE, MASK_TEXT_CLASS, UNMASK_TEXT_CLASS } from './constants';\nimport { StorageData } from './typings/session-replay';\nimport { getInputType } from './utils/get-input-type';\nexport { getServerUrl } from './utils/server-url';\n\ntype ChromeStorageEstimate = {\n quota?: number;\n usage?: number;\n usageDetails?: { [key: string]: number };\n};\n\n/**\n * Light: Subset of inputs (sensitive types only — password, hidden, email, tel, cc-*)\n * Medium: All inputs (form fields), text is NOT masked\n * Conservative: All inputs and all texts\n */\nconst isMaskedForLevel = (elementType: 'input' | 'text', level: MaskLevel, element: HTMLElement | null): boolean => {\n switch (level) {\n case 'light': {\n if (elementType !== 'input') {\n return false;\n }\n\n const inputType = element ? getInputType(element) : '';\n /* istanbul ignore if */ // TODO(lew): For some reason it's impossible to test this.\n if (!inputType) {\n return false;\n }\n\n if (['password', 'hidden', 'email', 'tel'].includes(inputType)) {\n return true;\n }\n\n if ((element as HTMLInputElement).autocomplete.startsWith('cc-')) {\n return true;\n }\n\n return false;\n }\n case 'medium':\n return elementType === 'input';\n case 'conservative':\n return true;\n default:\n return isMaskedForLevel(elementType, DEFAULT_MASK_LEVEL, element);\n }\n};\n\n/**\n * Returns the effective mask level for a given URL by checking `urlMaskLevels`\n * (first match wins) and falling back to `defaultMaskLevel`.\n */\nexport const getEffectiveMaskLevel = (url: string | undefined, config: PrivacyConfig): MaskLevel => {\n if (url && config.urlMaskLevels) {\n for (const rule of config.urlMaskLevels) {\n if (globToRegex(rule.match).test(url)) {\n return rule.maskLevel;\n }\n }\n }\n return config.defaultMaskLevel ?? DEFAULT_MASK_LEVEL;\n};\n\n/**\n * Checks if the given element set to be masked by rrweb\n *\n * Priority is:\n * 1. [In code] Element/class based masking/unmasking <> [Config based] Selector based masking/unmasking\n * 2. Use app defaults\n */\nexport const isMasked = (\n elementType: 'input' | 'text',\n config: PrivacyConfig = { defaultMaskLevel: DEFAULT_MASK_LEVEL },\n element: HTMLElement | null,\n currentUrl?: string,\n): boolean => {\n if (element) {\n // Element or parent is explicitly instrumented in code to mask\n if (element.closest('.' + MASK_TEXT_CLASS)) {\n return true;\n }\n\n // Config has override for mask\n const shouldMask = (config.maskSelector ?? []).some((selector) => element.closest(selector));\n if (shouldMask) {\n return true;\n }\n\n // Code or config has override to unmask\n if (element.closest('.' + UNMASK_TEXT_CLASS)) {\n return false;\n }\n\n // Here we are probably sent an element, but we want to match if they have a\n // parent with an unmask selector.\n const shouldUnmask = (config.unmaskSelector ?? []).some((selector) => element.closest(selector));\n if (shouldUnmask) {\n return false;\n }\n }\n\n return isMaskedForLevel(elementType, getEffectiveMaskLevel(currentUrl, config), element);\n};\n\nexport const maskFn =\n (elementType: 'text' | 'input', config?: PrivacyConfig, getCurrentUrl?: () => string) =>\n (text: string, element: HTMLElement | null): string => {\n return isMasked(elementType, config, element, getCurrentUrl?.()) ? text.replace(/[^\\s]/g, '*') : text;\n };\n\nexport const maskAttributeFn = (config?: PrivacyConfig, getCurrentUrl?: () => string) => {\n return (key: string, value: string, element: HTMLElement): string => {\n // Never mask style — rrweb has a separate styleDiff path for attribute mutations\n // that reads directly from the DOM, bypassing maskAttributeFn.\n if (key === 'style') return value;\n\n // Short-circuit: only proceed if this attribute is in the allowlist.\n if (!(config?.maskAttributes ?? []).includes(key)) return value;\n\n // Use 'input' for form elements so that `medium` (which masks inputs but not text)\n // still masks attributes on inputs/selects/textareas. For non-form elements, use\n // 'text' so medium leaves them visible.\n const elementType = ['INPUT', 'SELECT', 'TEXTAREA'].includes(element.tagName) ? 'input' : 'text';\n return isMasked(elementType, config, element, getCurrentUrl?.()) ? value.replace(/[^\\s]/g, '*') : value;\n };\n};\n\nexport const getCurrentUrl = () => {\n const globalScope = getGlobalScope();\n return globalScope?.location ? globalScope.location.href : '';\n};\n\nexport const generateSessionReplayId = (sessionId: string | number, deviceId: string): string => {\n return `${deviceId}/${sessionId}`;\n};\n\nconst isValidGlobUrl = (globUrl: string): boolean => {\n if (typeof globUrl !== 'string' || globUrl.trim() === '') return false;\n const urlPattern = /^\\/|^https?:\\/\\/[^\\s]+$/;\n if (!urlPattern.test(globUrl)) return false;\n return true;\n};\n\nconst globRegexCache = new Map<string, RegExp>();\n\nconst globToRegex = (glob: string): RegExp => {\n const cached = globRegexCache.get(glob);\n if (cached) return cached;\n\n // Glob → regex conversion. Glob substitution must happen BEFORE regex escaping so that\n // the escaper does not corrupt the glob characters (e.g. turning `/**` into `/\\*\\*`).\n // Placeholder tokens use null bytes, which are illegal in HTTP URLs and therefore can\n // never collide with real pattern content.\n //\n // Substitution order (most-specific first):\n // trailing /** → (/.*)? — path with or without a trailing slash/subpath\n // middle /**/ → /(.*\\/)? — zero-or-more path segments (including zero)\n // bare ** → .* — graceful fallback for ** not adjacent to /\n // single * → .* — any characters (preserves existing behaviour)\n // ? → . — single character wildcard\n const T_TRAILING = '\\x00TRAIL\\x00';\n const T_MIDDLE = '\\x00MID\\x00';\n const T_DSTAR = '\\x00DS\\x00';\n const T_STAR = '\\x00ST\\x00';\n const T_QUEST = '\\x00QU\\x00';\n\n let s = glob;\n s = s.replace(/\\/\\*\\*$/, T_TRAILING); // trailing /**\n s = s.replace(/\\/\\*\\*\\//g, T_MIDDLE); // /**/ in the middle\n s = s.replace(/\\*\\*/g, T_DSTAR); // bare ** (e.g. **.example.com)\n s = s.replace(/\\*/g, T_STAR); // single *\n s = s.replace(/\\?/g, T_QUEST); // ?\n\n // Escape all remaining regex special characters.\n s = s.replace(/[.+^${}()|[\\]\\\\]/g, '\\\\$&');\n\n // Expand tokens into their regex equivalents.\n // Use split/join (not regex) to avoid the no-control-regex lint rule on the token strings.\n s = s.split(T_TRAILING).join('(/.*)?'); // /** → optional /anything\n s = s.split(T_MIDDLE).join('/(.*\\\\/)?'); // /**/ → /zero-or-more-segments/\n s = s.split(T_DSTAR).join('.*'); // bare ** → .*\n s = s.split(T_STAR).join('.*'); // * → .*\n s = s.split(T_QUEST).join('.'); // ? → .\n\n const regex = new RegExp(`^${s}$`);\n globRegexCache.set(glob, regex);\n return regex;\n};\n\nexport const validateUGCFilterRules = (ugcFilterRules: UGCFilterRule[]) => {\n // validate ugcFilterRules\n if (!ugcFilterRules.every((rule) => typeof rule.selector === 'string' && typeof rule.replacement === 'string')) {\n throw new Error('ugcFilterRules must be an array of objects with selector and replacement properties');\n }\n\n // validate ugcFilterRules are valid globs\n if (!ugcFilterRules.every((rule) => isValidGlobUrl(rule.selector))) {\n throw new Error('ugcFilterRules must be an array of objects with valid globs');\n }\n};\n\nexport const getPageUrl = (pageUrl: string, ugcFilterRules: UGCFilterRule[]) => {\n // apply ugcFilterRules, order is important, first rule wins\n for (const rule of ugcFilterRules) {\n const regex = globToRegex(rule.selector);\n\n if (regex.test(pageUrl)) {\n return pageUrl.replace(regex, rule.replacement);\n }\n }\n\n return pageUrl;\n};\n\nexport const getStorageSize = async (): Promise<StorageData> => {\n try {\n const globalScope = getGlobalScope();\n if (globalScope) {\n const { usage, quota, usageDetails }: ChromeStorageEstimate = await globalScope.navigator.storage.estimate();\n const totalStorageSize = usage ? Math.round(usage / KB_SIZE) : 0;\n const percentOfQuota = usage && quota ? Math.round((usage / quota + Number.EPSILON) * 1000) / 1000 : 0;\n return { totalStorageSize, percentOfQuota, usageDetails: JSON.stringify(usageDetails) };\n }\n } catch (e) {\n // swallow\n }\n return { totalStorageSize: 0, percentOfQuota: 0, usageDetails: '' };\n};\n\nexport const getDebugConfig = (config: SessionReplayJoinedConfig): SessionReplayJoinedConfig => {\n const debugConfig = {\n ...config,\n };\n const { apiKey } = debugConfig;\n debugConfig.apiKey = `****${apiKey.substring(apiKey.length - 4)}`;\n return debugConfig;\n};\n"]}
|
|
1
|
+
{"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"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","sourcesContent":["import { getGlobalScope } from '@amplitude/analytics-core';\nimport { SessionReplayJoinedConfig, UGCFilterRule } from './config/types';\nimport { KB_SIZE } from './constants';\nimport { StorageData } from './typings/session-replay';\nimport { globToRegex } from '@amplitude/session-replay-dom-privacy';\nexport { getServerUrl } from './utils/server-url';\n\ntype ChromeStorageEstimate = {\n quota?: number;\n usage?: number;\n usageDetails?: { [key: string]: number };\n};\n\nexport const getCurrentUrl = () => {\n const globalScope = getGlobalScope();\n return globalScope?.location ? globalScope.location.href : '';\n};\n\nexport const generateSessionReplayId = (sessionId: string | number, deviceId: string): string => {\n return `${deviceId}/${sessionId}`;\n};\n\nconst isValidGlobUrl = (globUrl: string): boolean => {\n if (typeof globUrl !== 'string' || globUrl.trim() === '') return false;\n const urlPattern = /^\\/|^https?:\\/\\/[^\\s]+$/;\n if (!urlPattern.test(globUrl)) return false;\n return true;\n};\n\nexport const validateUGCFilterRules = (ugcFilterRules: UGCFilterRule[]) => {\n // validate ugcFilterRules\n if (!ugcFilterRules.every((rule) => typeof rule.selector === 'string' && typeof rule.replacement === 'string')) {\n throw new Error('ugcFilterRules must be an array of objects with selector and replacement properties');\n }\n\n // validate ugcFilterRules are valid globs\n if (!ugcFilterRules.every((rule) => isValidGlobUrl(rule.selector))) {\n throw new Error('ugcFilterRules must be an array of objects with valid globs');\n }\n};\n\nexport const getPageUrl = (pageUrl: string, ugcFilterRules: UGCFilterRule[]) => {\n // apply ugcFilterRules, order is important, first rule wins\n for (const rule of ugcFilterRules) {\n const regex = globToRegex(rule.selector);\n\n if (regex.test(pageUrl)) {\n return pageUrl.replace(regex, rule.replacement);\n }\n }\n\n return pageUrl;\n};\n\nexport const getStorageSize = async (): Promise<StorageData> => {\n try {\n const globalScope = getGlobalScope();\n if (globalScope) {\n const { usage, quota, usageDetails }: ChromeStorageEstimate = await globalScope.navigator.storage.estimate();\n const totalStorageSize = usage ? Math.round(usage / KB_SIZE) : 0;\n const percentOfQuota = usage && quota ? Math.round((usage / quota + Number.EPSILON) * 1000) / 1000 : 0;\n return { totalStorageSize, percentOfQuota, usageDetails: JSON.stringify(usageDetails) };\n }\n } catch (e) {\n // swallow\n }\n return { totalStorageSize: 0, percentOfQuota: 0, usageDetails: '' };\n};\n\nexport const getDebugConfig = (config: SessionReplayJoinedConfig): SessionReplayJoinedConfig => {\n const debugConfig = {\n ...config,\n };\n const { apiKey } = debugConfig;\n debugConfig.apiKey = `****${apiKey.substring(apiKey.length - 4)}`;\n return debugConfig;\n};\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observers.d.ts","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAA+C,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"observers.d.ts","sourceRoot":"","sources":["../../src/observers.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAA+C,MAAM,wBAAwB,CAAC;AAGjH,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;AA8BD,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/cjs/observers.js
CHANGED
|
@@ -4,6 +4,7 @@ exports.NetworkObservers = void 0;
|
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
5
|
var analytics_core_1 = require("@amplitude/analytics-core");
|
|
6
6
|
var network_body_capture_1 = require("./network-body-capture");
|
|
7
|
+
var utf8_byte_length_1 = require("./utils/utf8-byte-length");
|
|
7
8
|
var DEFAULT_MAX_BODY_SIZE_BYTES = 10240; // 10KB
|
|
8
9
|
var BINARY_CONTENT_TYPE_PREFIXES = ['image/', 'audio/', 'video/', 'application/octet-stream', 'font/'];
|
|
9
10
|
function isBinaryContentType(contentType) {
|
|
@@ -29,28 +30,7 @@ function serializeRequestBody(body) {
|
|
|
29
30
|
return undefined;
|
|
30
31
|
}
|
|
31
32
|
function truncateToByteLimit(str, maxBytes) {
|
|
32
|
-
|
|
33
|
-
return { value: str, truncated: false };
|
|
34
|
-
}
|
|
35
|
-
// Binary search for the longest prefix whose UTF-8 byte length fits within maxBytes.
|
|
36
|
-
// Cap hi at maxBytes since each UTF-8 character is at least 1 byte, so no more than
|
|
37
|
-
// maxBytes characters can ever fit — this avoids large intermediate Blob allocations.
|
|
38
|
-
var lo = 0;
|
|
39
|
-
var hi = Math.min(str.length, maxBytes);
|
|
40
|
-
while (lo < hi) {
|
|
41
|
-
var mid = Math.ceil((lo + hi) / 2);
|
|
42
|
-
if (new Blob([str.slice(0, mid)]).size <= maxBytes) {
|
|
43
|
-
lo = mid;
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
hi = mid - 1;
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
// Avoid splitting a surrogate pair: if lo landed after a high surrogate, back up one position
|
|
50
|
-
if (lo > 0 && str.charCodeAt(lo - 1) >= 0xd800 && str.charCodeAt(lo - 1) <= 0xdbff) {
|
|
51
|
-
lo -= 1;
|
|
52
|
-
}
|
|
53
|
-
return { value: str.slice(0, lo), truncated: true };
|
|
33
|
+
return (0, utf8_byte_length_1.truncateUtf8)(str, maxBytes);
|
|
54
34
|
}
|
|
55
35
|
var NetworkObservers = /** @class */ (function () {
|
|
56
36
|
function NetworkObservers() {
|
package/lib/cjs/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\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"]}
|
|
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';\nimport { truncateUtf8 } from './utils/utf8-byte-length';\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 return truncateUtf8(str, maxBytes);\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"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"session-replay.d.ts","sourceRoot":"","sources":["../../src/session-replay.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SessionReplay = void 0;
|
|
4
4
|
var tslib_1 = require("tslib");
|
|
5
|
+
var session_replay_dom_privacy_1 = require("@amplitude/session-replay-dom-privacy");
|
|
5
6
|
var analytics_core_1 = require("@amplitude/analytics-core");
|
|
6
7
|
// Import only specific types to avoid pulling in the entire rrweb-types package
|
|
7
8
|
var rrweb_types_1 = require("@amplitude/rrweb-types");
|
|
@@ -777,42 +778,12 @@ var SessionReplay = /** @class */ (function () {
|
|
|
777
778
|
return shouldRecord;
|
|
778
779
|
};
|
|
779
780
|
SessionReplay.prototype.getBlockSelectors = function () {
|
|
780
|
-
var _a
|
|
781
|
-
|
|
782
|
-
// Empty arrays cause errors when being evaluated in Safari.
|
|
783
|
-
// Force the selector to be undefined if it's an empty array.
|
|
784
|
-
var blockSelector = (_c = (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.privacyConfig) === null || _b === void 0 ? void 0 : _b.blockSelector) !== null && _c !== void 0 ? _c : [];
|
|
785
|
-
if (blockSelector.length === 0) {
|
|
786
|
-
return undefined;
|
|
787
|
-
}
|
|
788
|
-
return blockSelector;
|
|
781
|
+
var _a;
|
|
782
|
+
return (0, session_replay_dom_privacy_1.getBlockSelectors)((_a = this.config) === null || _a === void 0 ? void 0 : _a.privacyConfig);
|
|
789
783
|
};
|
|
790
784
|
SessionReplay.prototype.getMaskTextSelectors = function () {
|
|
791
|
-
var _a
|
|
792
|
-
|
|
793
|
-
var effectiveLevel = privacyConfig ? (0, helpers_1.getEffectiveMaskLevel)(this.currentPageUrl, privacyConfig) : undefined;
|
|
794
|
-
if (effectiveLevel === 'conservative') {
|
|
795
|
-
return '*';
|
|
796
|
-
}
|
|
797
|
-
// If any urlMaskLevels rule uses 'conservative', always route all text nodes
|
|
798
|
-
// through maskTextFn so the dynamic URL getter can decide at call time.
|
|
799
|
-
// Without this, rrweb's static maskTextSelector would miss text nodes when
|
|
800
|
-
// the user navigates from a non-conservative page to a conservative one.
|
|
801
|
-
if ((_b = privacyConfig === null || privacyConfig === void 0 ? void 0 : privacyConfig.urlMaskLevels) === null || _b === void 0 ? void 0 : _b.some(function (rule) { return rule.maskLevel === 'conservative'; })) {
|
|
802
|
-
return '*';
|
|
803
|
-
}
|
|
804
|
-
// If defaultMaskLevel is 'conservative' and URL rules exist, always route text through
|
|
805
|
-
// maskTextFn — a page matching no rule falls back to the conservative default, and
|
|
806
|
-
// rrweb must be set up at start to call maskTextFn for those text nodes.
|
|
807
|
-
var urlMaskLevels = privacyConfig === null || privacyConfig === void 0 ? void 0 : privacyConfig.urlMaskLevels;
|
|
808
|
-
if ((privacyConfig === null || privacyConfig === void 0 ? void 0 : privacyConfig.defaultMaskLevel) === 'conservative' && urlMaskLevels && urlMaskLevels.length > 0) {
|
|
809
|
-
return '*';
|
|
810
|
-
}
|
|
811
|
-
var maskSelector = privacyConfig === null || privacyConfig === void 0 ? void 0 : privacyConfig.maskSelector;
|
|
812
|
-
if (!maskSelector) {
|
|
813
|
-
return;
|
|
814
|
-
}
|
|
815
|
-
return maskSelector;
|
|
785
|
+
var _a;
|
|
786
|
+
return (0, session_replay_dom_privacy_1.getMaskTextSelectors)((_a = this.config) === null || _a === void 0 ? void 0 : _a.privacyConfig, this.currentPageUrl);
|
|
816
787
|
};
|
|
817
788
|
SessionReplay.prototype.getRecordingPlugins = function (loggingConfig) {
|
|
818
789
|
var _a, _b, _c, _d, _e, _f;
|
|
@@ -1064,7 +1035,7 @@ var SessionReplay = /** @class */ (function () {
|
|
|
1064
1035
|
SessionReplay.prototype.buildRRWebRecordOptions = function (config, hooks, emit, errorLogPrefix) {
|
|
1065
1036
|
var _this = this;
|
|
1066
1037
|
var privacyConfig = config.privacyConfig;
|
|
1067
|
-
return tslib_1.__assign(tslib_1.__assign({ emit: emit, inlineStylesheet: config.shouldInlineStylesheet, inlineImages: config.inlineImages, hooks: hooks
|
|
1038
|
+
return tslib_1.__assign(tslib_1.__assign(tslib_1.__assign(tslib_1.__assign({ emit: emit, inlineStylesheet: config.shouldInlineStylesheet, inlineImages: config.inlineImages, hooks: hooks }, (0, session_replay_dom_privacy_1.createPrivacyRecorderOptions)(privacyConfig, function () { return _this.currentPageUrl; })), { applyBackgroundColorToBlockedElements: config.applyBackgroundColorToBlockedElements }), (config.fullSnapshotIntervalMs !== undefined && { checkoutEveryNms: config.fullSnapshotIntervalMs })), { recordCanvas: false, captureAdoptedStyleSheets: config.captureAdoptedStyleSheets,
|
|
1068
1039
|
// Strip nodes that are never rendered by the rrweb replay player.
|
|
1069
1040
|
// None of these affect visual fidelity; omitting them reduces snapshot size.
|
|
1070
1041
|
slimDOMOptions: {
|