mixpanel-browser 2.70.0 → 2.71.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.
Files changed (37) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/build.sh +1 -0
  3. package/dist/mixpanel-core.cjs.d.ts +440 -0
  4. package/dist/mixpanel-core.cjs.js +847 -50
  5. package/dist/mixpanel-recorder.js +5 -3
  6. package/dist/mixpanel-recorder.min.js +1 -1
  7. package/dist/mixpanel-recorder.min.js.map +1 -1
  8. package/dist/mixpanel-with-async-recorder.cjs.d.ts +440 -0
  9. package/dist/mixpanel-with-async-recorder.cjs.js +847 -50
  10. package/dist/mixpanel-with-recorder.d.ts +440 -0
  11. package/dist/mixpanel-with-recorder.js +851 -52
  12. package/dist/mixpanel-with-recorder.min.d.ts +440 -0
  13. package/dist/mixpanel-with-recorder.min.js +1 -1
  14. package/dist/mixpanel.amd.d.ts +440 -0
  15. package/dist/mixpanel.amd.js +851 -52
  16. package/dist/mixpanel.cjs.d.ts +440 -0
  17. package/dist/mixpanel.cjs.js +851 -52
  18. package/dist/mixpanel.globals.js +847 -50
  19. package/dist/mixpanel.min.js +170 -153
  20. package/dist/mixpanel.module.d.ts +440 -0
  21. package/dist/mixpanel.module.js +851 -52
  22. package/dist/mixpanel.umd.d.ts +440 -0
  23. package/dist/mixpanel.umd.js +851 -52
  24. package/dist/rrweb-compiled.js +4 -2
  25. package/package.json +2 -19
  26. package/rollup.config.mjs +28 -4
  27. package/src/autocapture/deadclick.js +254 -0
  28. package/src/autocapture/index.js +237 -41
  29. package/src/autocapture/shadow-dom-observer.js +100 -0
  30. package/src/autocapture/utils.js +230 -3
  31. package/src/config.js +1 -1
  32. package/src/flags/index.js +32 -12
  33. package/src/index.d.ts +16 -3
  34. package/src/loaders/loader-module-core.d.ts +1 -0
  35. package/src/loaders/loader-module-with-async-recorder.d.ts +1 -0
  36. package/src/loaders/loader-module.d.ts +1 -0
  37. package/src/utils.js +15 -0
@@ -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.70.0",
3
+ "version": "2.71.0",
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.1"
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
+ };