mixpanel-browser 2.70.0 → 2.71.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/.claude/settings.local.json +9 -0
- package/CHANGELOG.md +11 -0
- package/build.sh +1 -0
- package/dist/mixpanel-core.cjs.d.ts +440 -0
- package/dist/mixpanel-core.cjs.js +849 -50
- package/dist/mixpanel-recorder.js +5 -3
- package/dist/mixpanel-recorder.min.js +1 -1
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-with-async-recorder.cjs.d.ts +440 -0
- package/dist/mixpanel-with-async-recorder.cjs.js +849 -50
- package/dist/mixpanel-with-recorder.d.ts +440 -0
- package/dist/mixpanel-with-recorder.js +853 -52
- package/dist/mixpanel-with-recorder.min.d.ts +440 -0
- package/dist/mixpanel-with-recorder.min.js +1 -1
- package/dist/mixpanel.amd.d.ts +440 -0
- package/dist/mixpanel.amd.js +853 -52
- package/dist/mixpanel.cjs.d.ts +440 -0
- package/dist/mixpanel.cjs.js +853 -52
- package/dist/mixpanel.globals.js +849 -50
- package/dist/mixpanel.min.js +170 -153
- package/dist/mixpanel.module.d.ts +440 -0
- package/dist/mixpanel.module.js +853 -52
- package/dist/mixpanel.umd.d.ts +440 -0
- package/dist/mixpanel.umd.js +853 -52
- package/dist/rrweb-compiled.js +4 -2
- package/package.json +2 -19
- package/rollup.config.mjs +28 -4
- package/src/autocapture/deadclick.js +254 -0
- package/src/autocapture/index.js +239 -41
- package/src/autocapture/shadow-dom-observer.js +100 -0
- package/src/autocapture/utils.js +230 -3
- package/src/config.js +1 -1
- package/src/flags/index.js +32 -12
- package/src/index.d.ts +16 -3
- package/src/loaders/loader-module-core.d.ts +1 -0
- package/src/loaders/loader-module-with-async-recorder.d.ts +1 -0
- package/src/loaders/loader-module.d.ts +1 -0
- package/src/utils.js +15 -0
package/dist/rrweb-compiled.js
CHANGED
|
@@ -1578,7 +1578,8 @@ function snapshot(n2, options) {
|
|
|
1578
1578
|
week: true,
|
|
1579
1579
|
textarea: true,
|
|
1580
1580
|
select: true,
|
|
1581
|
-
password: true
|
|
1581
|
+
password: true,
|
|
1582
|
+
hidden: true
|
|
1582
1583
|
} : maskAllInputs === false ? {
|
|
1583
1584
|
password: true
|
|
1584
1585
|
} : maskAllInputs;
|
|
@@ -15194,7 +15195,8 @@ function record(options) {
|
|
|
15194
15195
|
week: true,
|
|
15195
15196
|
textarea: true,
|
|
15196
15197
|
select: true,
|
|
15197
|
-
password: true
|
|
15198
|
+
password: true,
|
|
15199
|
+
hidden: true
|
|
15198
15200
|
} : _maskInputOptions !== void 0 ? _maskInputOptions : {
|
|
15199
15201
|
password: true
|
|
15200
15202
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mixpanel-browser",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.71.1",
|
|
4
4
|
"description": "The official Mixpanel JavaScript browser client library",
|
|
5
5
|
"main": "dist/mixpanel.cjs.js",
|
|
6
6
|
"module": "dist/mixpanel.module.js",
|
|
@@ -23,23 +23,6 @@
|
|
|
23
23
|
"validate": "npm ls"
|
|
24
24
|
},
|
|
25
25
|
"types": "./src/index.d.ts",
|
|
26
|
-
"exports": {
|
|
27
|
-
"./src/loaders/loader-module-core": {
|
|
28
|
-
"types": "./src/index.d.ts",
|
|
29
|
-
"import": "./src/loaders/loader-module-core.js",
|
|
30
|
-
"require": "./src/loaders/loader-module-core.js"
|
|
31
|
-
},
|
|
32
|
-
"./src/loaders/loader-module-with-async-recorder": {
|
|
33
|
-
"types": "./src/index.d.ts",
|
|
34
|
-
"import": "./src/loaders/loader-module-with-async-recorder.js",
|
|
35
|
-
"require": "./src/loaders/loader-module-with-async-recorder.js"
|
|
36
|
-
},
|
|
37
|
-
".": {
|
|
38
|
-
"types": "./src/index.d.ts",
|
|
39
|
-
"import": "./dist/mixpanel.module.js",
|
|
40
|
-
"require": "./dist/mixpanel.cjs.js"
|
|
41
|
-
}
|
|
42
|
-
},
|
|
43
26
|
"repository": {
|
|
44
27
|
"type": "git",
|
|
45
28
|
"url": "https://github.com/mixpanel/mixpanel-js.git"
|
|
@@ -83,6 +66,6 @@
|
|
|
83
66
|
"webpack": "1.12.2"
|
|
84
67
|
},
|
|
85
68
|
"dependencies": {
|
|
86
|
-
"@mixpanel/rrweb": "2.0.0-alpha.18.
|
|
69
|
+
"@mixpanel/rrweb": "2.0.0-alpha.18.2"
|
|
87
70
|
}
|
|
88
71
|
}
|
package/rollup.config.mjs
CHANGED
|
@@ -3,6 +3,8 @@ import swc from '@rollup/plugin-swc';
|
|
|
3
3
|
import esbuild from 'rollup-plugin-esbuild';
|
|
4
4
|
import alias from '@rollup/plugin-alias';
|
|
5
5
|
import closureCompiler from '@ampproject/rollup-plugin-closure-compiler';
|
|
6
|
+
import fs from 'fs';
|
|
7
|
+
import path from 'path';
|
|
6
8
|
|
|
7
9
|
const COMPILED_RRWEB_PATH = 'build/rrweb-compiled.js';
|
|
8
10
|
|
|
@@ -12,6 +14,24 @@ const aliasRrweb = () => alias({
|
|
|
12
14
|
]
|
|
13
15
|
});
|
|
14
16
|
|
|
17
|
+
const copyTypes = () => ({
|
|
18
|
+
name: 'copy-types',
|
|
19
|
+
generateBundle(_options, bundle) {
|
|
20
|
+
try {
|
|
21
|
+
for (const builtFileName of Object.keys(bundle)) {
|
|
22
|
+
const buildDir = 'build';
|
|
23
|
+
const types = fs.readFileSync('src/index.d.ts', 'utf8');
|
|
24
|
+
const builtFile = path.basename(builtFileName, path.extname(builtFileName)) + '.d.ts';
|
|
25
|
+
fs.writeFileSync(path.join(buildDir, builtFile), types);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
} catch (err) {
|
|
29
|
+
console.warn('Could not copy type files:', err.message);
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
15
35
|
const COMMON_CLOSURE_FLAGS = {
|
|
16
36
|
compilation_level: 'ADVANCED_OPTIMIZATIONS',
|
|
17
37
|
language_in: 'ECMASCRIPT5',
|
|
@@ -140,7 +160,8 @@ const ALL_BUILDS = [
|
|
|
140
160
|
browser: true,
|
|
141
161
|
main: true,
|
|
142
162
|
jsnext: true,
|
|
143
|
-
})
|
|
163
|
+
}),
|
|
164
|
+
copyTypes(),
|
|
144
165
|
],
|
|
145
166
|
},
|
|
146
167
|
|
|
@@ -176,7 +197,8 @@ const ALL_BUILDS = [
|
|
|
176
197
|
browser: true,
|
|
177
198
|
main: true,
|
|
178
199
|
jsnext: true,
|
|
179
|
-
})
|
|
200
|
+
}),
|
|
201
|
+
copyTypes(),
|
|
180
202
|
],
|
|
181
203
|
},
|
|
182
204
|
|
|
@@ -197,7 +219,8 @@ const ALL_BUILDS = [
|
|
|
197
219
|
browser: true,
|
|
198
220
|
main: true,
|
|
199
221
|
jsnext: true,
|
|
200
|
-
})
|
|
222
|
+
}),
|
|
223
|
+
copyTypes(),
|
|
201
224
|
],
|
|
202
225
|
},
|
|
203
226
|
{
|
|
@@ -215,7 +238,8 @@ const ALL_BUILDS = [
|
|
|
215
238
|
browser: true,
|
|
216
239
|
main: true,
|
|
217
240
|
jsnext: true,
|
|
218
|
-
})
|
|
241
|
+
}),
|
|
242
|
+
copyTypes(),
|
|
219
243
|
],
|
|
220
244
|
}
|
|
221
245
|
];
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { EV_CHANGE, EV_HASHCHANGE, EV_INPUT, EV_SCROLLEND, EV_SELECT, EV_SUBMIT, EV_TOGGLE, isDefinitelyNonInteractive, logger } from './utils';
|
|
2
|
+
import { ShadowDOMObserver } from './shadow-dom-observer';
|
|
3
|
+
|
|
4
|
+
/** @const */ var DEFAULT_DEAD_CLICK_TIMEOUT_MS = 500;
|
|
5
|
+
/** @const */ var INTERACTION_EVENTS = [EV_CHANGE, EV_INPUT, EV_SUBMIT, EV_SELECT, EV_TOGGLE];
|
|
6
|
+
/** @const */ var LAYOUT_EVENTS = [EV_SCROLLEND];
|
|
7
|
+
/** @const */ var NAVIGATION_EVENTS = [EV_HASHCHANGE];
|
|
8
|
+
/** @const */ var MUTATION_OBSERVER_CONFIG = {
|
|
9
|
+
characterData: true,
|
|
10
|
+
childList: true,
|
|
11
|
+
subtree: true,
|
|
12
|
+
attributes: true,
|
|
13
|
+
attributeFilter: ['style', 'class', 'hidden', 'checked', 'selected', 'value', 'display', 'visibility']
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
function DeadClickTracker(onDeadClickCallback) {
|
|
18
|
+
this.eventListeners = [];
|
|
19
|
+
this.mutationObserver = null;
|
|
20
|
+
this.shadowDOMObserver = null;
|
|
21
|
+
|
|
22
|
+
this.isTracking = false;
|
|
23
|
+
this.lastChangeEventTimestamp = 0;
|
|
24
|
+
this.pendingClicks = [];
|
|
25
|
+
this.onDeadClickCallback = onDeadClickCallback;
|
|
26
|
+
this.processingActive = false;
|
|
27
|
+
this.processingTimeout = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
DeadClickTracker.prototype.addClick = function(event) {
|
|
32
|
+
var element = this.shadowDOMObserver && this.shadowDOMObserver.getEventTarget(event);
|
|
33
|
+
|
|
34
|
+
if (!element) {
|
|
35
|
+
element = event['target'] || event['srcElement'];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!element || isDefinitelyNonInteractive(element)) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (this.shadowDOMObserver) {
|
|
43
|
+
this.shadowDOMObserver.observeFromEvent(event);
|
|
44
|
+
}
|
|
45
|
+
this.pendingClicks.push({
|
|
46
|
+
element: element,
|
|
47
|
+
event: event,
|
|
48
|
+
timestamp: Date.now()
|
|
49
|
+
});
|
|
50
|
+
return true;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
DeadClickTracker.prototype.trackClick = function(event, config) {
|
|
54
|
+
if (!this.isTracking) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
var added = this.addClick(event);
|
|
59
|
+
if (added) {
|
|
60
|
+
this.triggerProcessing(config);
|
|
61
|
+
}
|
|
62
|
+
return added;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
DeadClickTracker.prototype.getDeadClicks = function(config) {
|
|
66
|
+
if (this.pendingClicks.length === 0) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
var timeoutMs = config['timeout_ms'];
|
|
71
|
+
var now = Date.now();
|
|
72
|
+
var clicksToEvaluate = this.pendingClicks.slice(); // Copy array
|
|
73
|
+
this.pendingClicks = []; // Clear original
|
|
74
|
+
|
|
75
|
+
var deadClicks = [];
|
|
76
|
+
|
|
77
|
+
for (var i = 0; i < clicksToEvaluate.length; i++) {
|
|
78
|
+
var click = clicksToEvaluate[i];
|
|
79
|
+
|
|
80
|
+
if (now - click.timestamp >= timeoutMs) {
|
|
81
|
+
// Click has exceeded timeout, check if it's dead by looking for changes after this specific click
|
|
82
|
+
if (!this.hasChangesAfter(click.timestamp)) {
|
|
83
|
+
deadClicks.push(click);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
// Still pending - add back
|
|
87
|
+
this.pendingClicks.push(click);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return deadClicks;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
DeadClickTracker.prototype.hasChangesAfter = function(timestamp) {
|
|
95
|
+
// 100ms tolerance for race condition between when we record the click and the change event
|
|
96
|
+
return this.lastChangeEventTimestamp >= (timestamp - 100);
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
DeadClickTracker.prototype.recordChangeEvent = function() {
|
|
100
|
+
this.lastChangeEventTimestamp = Date.now();
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
DeadClickTracker.prototype.triggerProcessing = function(config) {
|
|
104
|
+
// Prevent multiple concurrent processing chains
|
|
105
|
+
if (this.processingActive) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
this.processingActive = true;
|
|
109
|
+
this.processRecursively(config);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
DeadClickTracker.prototype.processRecursively = function(config) {
|
|
113
|
+
if (!this.isTracking || !this.onDeadClickCallback) {
|
|
114
|
+
this.processingActive = false;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
var timeoutMs = config['timeout_ms'];
|
|
119
|
+
var self = this;
|
|
120
|
+
|
|
121
|
+
this.processingTimeout = setTimeout(function() {
|
|
122
|
+
if (!self.processingActive) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
var deadClicks = self.getDeadClicks(config);
|
|
127
|
+
|
|
128
|
+
for (var i = 0; i < deadClicks.length; i++) {
|
|
129
|
+
self.onDeadClickCallback(deadClicks[i].event);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (self.pendingClicks.length > 0) {
|
|
133
|
+
self.processRecursively(config);
|
|
134
|
+
} else {
|
|
135
|
+
self.processingActive = false;
|
|
136
|
+
}
|
|
137
|
+
}, timeoutMs);
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
DeadClickTracker.prototype.startTracking = function() {
|
|
141
|
+
if (this.isTracking) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
this.isTracking = true;
|
|
146
|
+
|
|
147
|
+
var self = this;
|
|
148
|
+
|
|
149
|
+
INTERACTION_EVENTS.forEach(function(event) {
|
|
150
|
+
var handler = function() {
|
|
151
|
+
self.recordChangeEvent();
|
|
152
|
+
};
|
|
153
|
+
document.addEventListener(event, handler, { capture: true, passive: true });
|
|
154
|
+
self.eventListeners.push({ target: document, event: event, handler: handler, options: { capture: true, passive: true } });
|
|
155
|
+
});
|
|
156
|
+
NAVIGATION_EVENTS.forEach(function(event) {
|
|
157
|
+
var handler = function() {
|
|
158
|
+
self.recordChangeEvent();
|
|
159
|
+
};
|
|
160
|
+
window.addEventListener(event, handler);
|
|
161
|
+
self.eventListeners.push({ target: window, event: event, handler: handler });
|
|
162
|
+
});
|
|
163
|
+
LAYOUT_EVENTS.forEach(function(event) {
|
|
164
|
+
var handler = function() {
|
|
165
|
+
self.recordChangeEvent();
|
|
166
|
+
};
|
|
167
|
+
window.addEventListener(event, handler, { passive: true });
|
|
168
|
+
self.eventListeners.push({ target: window, event: event, handler: handler, options: { passive: true } });
|
|
169
|
+
});
|
|
170
|
+
var selectionHandler = function() {
|
|
171
|
+
self.recordChangeEvent();
|
|
172
|
+
};
|
|
173
|
+
document.addEventListener('selectionchange', selectionHandler);
|
|
174
|
+
self.eventListeners.push({ target: document, event: 'selectionchange', handler: selectionHandler });
|
|
175
|
+
|
|
176
|
+
// Set up MutationObserver
|
|
177
|
+
if (window.MutationObserver) {
|
|
178
|
+
try {
|
|
179
|
+
this.mutationObserver = new window.MutationObserver(function() {
|
|
180
|
+
self.recordChangeEvent();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
this.mutationObserver.observe(document.body || document.documentElement, MUTATION_OBSERVER_CONFIG);
|
|
184
|
+
} catch (e) {
|
|
185
|
+
logger.critical('Error while setting up mutation observer', e);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Set up Shadow DOM observer
|
|
190
|
+
if (window.customElements) {
|
|
191
|
+
try {
|
|
192
|
+
this.shadowDOMObserver = new ShadowDOMObserver(
|
|
193
|
+
function() {
|
|
194
|
+
self.recordChangeEvent();
|
|
195
|
+
},
|
|
196
|
+
MUTATION_OBSERVER_CONFIG
|
|
197
|
+
);
|
|
198
|
+
this.shadowDOMObserver.start();
|
|
199
|
+
} catch (e) {
|
|
200
|
+
logger.critical('Error while setting up shadow DOM observer', e);
|
|
201
|
+
this.shadowDOMObserver = null;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
};
|
|
205
|
+
|
|
206
|
+
DeadClickTracker.prototype.stopTracking = function() {
|
|
207
|
+
if (!this.isTracking) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
this.isTracking = false;
|
|
212
|
+
this.pendingClicks = [];
|
|
213
|
+
this.lastChangeEventTimestamp = 0;
|
|
214
|
+
this.processingActive = false;
|
|
215
|
+
|
|
216
|
+
if (this.processingTimeout) {
|
|
217
|
+
clearTimeout(this.processingTimeout);
|
|
218
|
+
this.processingTimeout = null;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Remove all event listeners
|
|
222
|
+
for (var i = 0; i < this.eventListeners.length; i++) {
|
|
223
|
+
var listener = this.eventListeners[i];
|
|
224
|
+
try {
|
|
225
|
+
listener.target.removeEventListener(listener.event, listener.handler, listener.options);
|
|
226
|
+
} catch (e) {
|
|
227
|
+
logger.critical('Error while removing event listener', e);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
this.eventListeners = [];
|
|
231
|
+
|
|
232
|
+
if (this.mutationObserver) {
|
|
233
|
+
try {
|
|
234
|
+
this.mutationObserver.disconnect();
|
|
235
|
+
} catch (e) {
|
|
236
|
+
logger.critical('Error while disconnecting mutation observer', e);
|
|
237
|
+
}
|
|
238
|
+
this.mutationObserver = null;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (this.shadowDOMObserver) {
|
|
242
|
+
try {
|
|
243
|
+
this.shadowDOMObserver.stop();
|
|
244
|
+
} catch (e) {
|
|
245
|
+
logger.critical('Error while stopping shadow DOM observer', e);
|
|
246
|
+
}
|
|
247
|
+
this.shadowDOMObserver = null;
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
export {
|
|
252
|
+
DeadClickTracker,
|
|
253
|
+
DEFAULT_DEAD_CLICK_TIMEOUT_MS
|
|
254
|
+
};
|