mixpanel-browser 2.65.0 → 2.67.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.d.ts ADDED
@@ -0,0 +1,408 @@
1
+ export type Persistence = "cookie" | "localStorage";
2
+
3
+ export type ApiPayloadFormat = "base64" | "json";
4
+
5
+ export type PushItem = Array<string | Dict>;
6
+
7
+ export type Query = string | Element | Element[];
8
+
9
+ export interface Dict {
10
+ [key: string]: any;
11
+ }
12
+
13
+ export interface RequestOptions {
14
+ transport?: "xhr" | "sendBeacon" | undefined;
15
+ send_immediately?: boolean | undefined;
16
+ }
17
+
18
+ export interface XhrHeadersDef {
19
+ [header: string]: any;
20
+ }
21
+
22
+ export interface HasOptedInOutOptions {
23
+ persistence_type: Persistence;
24
+ cookie_prefix: string;
25
+ }
26
+
27
+ export interface ClearOptOutInOutOptions extends HasOptedInOutOptions {
28
+ cookie_expiration: number;
29
+ cross_subdomain_cookie: boolean;
30
+ secure_cookie: boolean;
31
+ }
32
+
33
+ export interface InTrackingOptions extends ClearOptOutInOutOptions {
34
+ track: () => void;
35
+ track_event_name: string;
36
+ track_properties: Dict;
37
+ }
38
+
39
+ export interface OutTrackingOptions extends ClearOptOutInOutOptions {
40
+ delete_user: boolean;
41
+ }
42
+
43
+ export interface RegisterOptions {
44
+ persistent: boolean;
45
+ }
46
+
47
+ export type TrackPageView =
48
+ | boolean
49
+ | "url-with-path"
50
+ | "url-with-path-and-query-string"
51
+ | "full-url";
52
+
53
+ export interface AutocaptureConfig {
54
+ /**
55
+ * When set to `true`, Mixpanel will track element clicks. It will not track textContent unless `capture_text_content` is also set to `true`.
56
+ * @default true
57
+ */
58
+ click?: boolean;
59
+ /**
60
+ * When set to `true`, Mixpanel will track when an input is provided. It will not capture input content.
61
+ * @default true
62
+ */
63
+ input?: boolean;
64
+ /**
65
+ * When set, Mixpanel will collect pageviews when some components of the URL change — including UTM parameters.
66
+ * @default 'full-url'
67
+ */
68
+ pageview?: TrackPageView;
69
+ /**
70
+ * When set, Mixpanel will collect page scrolls at specified scroll intervals.
71
+ * @default true
72
+ */
73
+ scroll?: boolean;
74
+ /**
75
+ * When set to `true`, Mixpanel will track form submissions (but not submission content).
76
+ * @default true
77
+ */
78
+ submit?: boolean;
79
+ /**
80
+ * When set to `true`, Mixpanel will capture the textContent of any element.
81
+ * @default false
82
+ */
83
+ capture_text_content?: boolean;
84
+ /** Enables specification of additional attributes to track. */
85
+ capture_extra_attrs?: string[];
86
+ /**
87
+ * Establishes the scroll depth intervals which trigger `Page Scroll` event.
88
+ * @default [25, 50, 75, 100]
89
+ */
90
+ scroll_depth_percent_checkpoints?: number[];
91
+ /**
92
+ * When set to true, overrides `scroll_depth_percentage_checkpoints` and captures all scroll events.
93
+ * @default false
94
+ */
95
+ scroll_capture_all?: boolean;
96
+ /** Opts out specific pages from Autocapture. */
97
+ block_url_regexes?: RegExp[];
98
+ /** Opts in specific pages to Autocapture. */
99
+ allow_url_regexes?: RegExp[];
100
+ /** Opts out specific classes from Autocapture. */
101
+ block_selectors?: string[];
102
+ /** Opts in specific classes to Autocapture. */
103
+ allow_selectors?: string[];
104
+ /** Opts out specific attributes from Autocapture. */
105
+ block_attrs?: string[];
106
+ /**
107
+ * A user-provided function that determines whether a specific element should be
108
+ * tracked via Autocapture or not. The function receives the element as its first
109
+ * argument, and the DOM event as its second argument, and should return `true` if
110
+ * the element should be tracked (otherwise the element will NOT be tracked).
111
+ */
112
+ allow_element_callback?: (element: Element, event: Event) => boolean;
113
+ /**
114
+ * A user-provided function that determines whether a specific element should be
115
+ * blocked from tracking via Autocapture or not. The function receives the element
116
+ * as its first argument, and the DOM event as its second argument, and should
117
+ * return `true` if the element should be blocked.
118
+ */
119
+ block_element_callback?: (element: Element, event: Event) => boolean;
120
+ }
121
+
122
+ export interface Config {
123
+ api_host: string;
124
+ api_routes: {
125
+ track?: string;
126
+ engage?: string;
127
+ groups?: string;
128
+ };
129
+ api_method: string;
130
+ api_transport: string;
131
+ app_host: string;
132
+ api_payload_format: ApiPayloadFormat;
133
+ autotrack: boolean;
134
+ cdn: string;
135
+ cookie_domain: string;
136
+ cross_site_cookie: boolean;
137
+ cross_subdomain_cookie: boolean;
138
+ persistence: Persistence;
139
+ persistence_name: string;
140
+ cookie_name: string;
141
+ loaded: (mixpanel: Mixpanel) => void;
142
+ store_google: boolean;
143
+ stop_utm_persistence: boolean;
144
+ save_referrer: boolean;
145
+ test: boolean;
146
+ verbose: boolean;
147
+ img: boolean;
148
+ /**
149
+ * @default false
150
+ * @see https://github.com/mixpanel/mixpanel-js/blob/master/doc/readme.io/javascript-full-api-reference.md#mixpanelset_config
151
+ */
152
+ debug: boolean;
153
+ track_links_timeout: number;
154
+ track_pageview: TrackPageView;
155
+ autocapture: boolean | AutocaptureConfig;
156
+ skip_first_touch_marketing: boolean;
157
+ cookie_expiration: number;
158
+ upgrade: boolean;
159
+ disable_persistence: boolean;
160
+ disable_cookie: boolean;
161
+ disable_notifications: boolean;
162
+ secure_cookie: boolean;
163
+ ip: boolean;
164
+ property_blacklist: string[];
165
+ xhr_headers: XhrHeadersDef;
166
+ opt_out_tracking_by_default: boolean;
167
+ opt_out_persistence_by_default: boolean;
168
+ opt_out_tracking_persistence_type: Persistence;
169
+ opt_out_tracking_cookie_prefix: string;
170
+ inapp_protocol: string;
171
+ inapp_link_new_window: boolean;
172
+ ignore_dnt: boolean;
173
+ batch_requests: boolean;
174
+ batch_size: number;
175
+ batch_flush_interval_ms: number;
176
+ batch_request_timeout_ms: number;
177
+ record_block_class: string;
178
+ record_block_selector: string;
179
+ record_collect_fonts: boolean;
180
+ record_idle_timeout_ms: number;
181
+ record_inline_images: boolean;
182
+ record_mask_text_class: string;
183
+ record_mask_text_selector: string;
184
+ record_max_ms: number;
185
+ record_sessions_percent: number;
186
+ record_canvas: boolean;
187
+ }
188
+
189
+ export type VerboseResponse =
190
+ | {
191
+ status: 1;
192
+ error: null;
193
+ }
194
+ | {
195
+ status: 0;
196
+ error: string;
197
+ };
198
+
199
+ export type NormalResponse = 1 | 0;
200
+
201
+ export type Response = VerboseResponse | NormalResponse;
202
+
203
+ export type Callback = (response: Response) => void;
204
+
205
+ export interface People {
206
+ set(prop: string, to: any, callback?: Callback): void;
207
+ set(prop: Dict, callback?: Callback): void;
208
+ set_once(prop: string, to: any, callback?: Callback): void;
209
+ set_once(prop: Dict, callback?: Callback): void;
210
+ unset(prop: string[] | string, callback?: Callback): void;
211
+ increment(prop: string | Dict, callback?: Callback): void;
212
+ increment(prop: string, by: number, callback?: Callback): void;
213
+ remove(prop: string, value: any, callback?: Callback): void;
214
+ remove(prop: Dict, callback?: Callback): void;
215
+ append(prop: string, value: any, callback?: Callback): void;
216
+ append(prop: Dict, callback?: Callback): void;
217
+ union(prop: string, value: any, callback?: Callback): void;
218
+ union(prop: Dict, callback?: Callback): void;
219
+ track_charge(
220
+ amount: number,
221
+ propertiesOrCallback?: Dict | Callback,
222
+ callback?: Callback
223
+ ): void;
224
+ clear_charges(callback?: Callback): void;
225
+ delete_user(): void;
226
+ }
227
+
228
+ export interface Group {
229
+ remove(list_name: string, value: string, callback?: Callback): Group;
230
+ set<Prop extends string | Dict>(
231
+ prop: Prop,
232
+ to?: Prop extends string ? string : undefined,
233
+ callback?: Callback
234
+ ): Group;
235
+ set_once<Prop extends string | Dict>(
236
+ prop: Prop,
237
+ to?: Prop extends string ? string : undefined,
238
+ callback?: Callback
239
+ ): Group;
240
+ union(
241
+ list_name: string,
242
+ values: Array<string | number>,
243
+ callback?: Callback
244
+ ): Group;
245
+ unset(prop: string, callback?: Callback): void;
246
+ }
247
+
248
+ export interface Mixpanel {
249
+ add_group(group_key: string, group_id: string, callback?: Callback): void;
250
+ alias(alias: string, original?: string): void;
251
+ clear_opt_in_out_tracking(options?: Partial<ClearOptOutInOutOptions>): void;
252
+ disable(events?: string[]): void;
253
+ get_config(prop_name?: string): any;
254
+ get_distinct_id(): any;
255
+ get_group(group_key: string, group_id: string): Group;
256
+ get_property(property_name: string): any;
257
+ has_opted_in_tracking(options?: Partial<HasOptedInOutOptions>): boolean;
258
+ has_opted_out_tracking(options?: Partial<HasOptedInOutOptions>): boolean;
259
+ identify(unique_id?: string): any;
260
+ init(token: string, config: Partial<Config>, name: string): Mixpanel;
261
+ opt_in_tracking(options?: Partial<InTrackingOptions>): void;
262
+ opt_out_tracking(options?: Partial<OutTrackingOptions>): void;
263
+ push(item: PushItem): void;
264
+ register(
265
+ props: Dict,
266
+ days_or_options?: number | Partial<RegisterOptions>
267
+ ): void;
268
+ register_once(
269
+ props: Dict,
270
+ default_value?: any,
271
+ days_or_options?: number | Partial<RegisterOptions>
272
+ ): void;
273
+ remove_group(
274
+ group_key: string,
275
+ group_ids: string | string[] | number | number[],
276
+ callback?: Callback
277
+ ): void;
278
+ reset(): void;
279
+ set_config(config: Partial<Config>): void;
280
+ set_group(
281
+ group_key: string,
282
+ group_ids: string | string[] | number | number[],
283
+ callback?: Callback
284
+ ): void;
285
+ time_event(event_name: string): void;
286
+ track(
287
+ event_name: string,
288
+ properties?: Dict,
289
+ optionsOrCallback?: RequestOptions | Callback,
290
+ callback?: Callback
291
+ ): void;
292
+ track_forms(
293
+ query: Query,
294
+ event_name: string,
295
+ properties?: Dict | (() => void)
296
+ ): void;
297
+ track_links(
298
+ query: Query,
299
+ event_name: string,
300
+ properties?: Dict | (() => void)
301
+ ): void;
302
+ track_pageview(
303
+ properties?: Dict,
304
+ options?: { event_name?: string | undefined }
305
+ ): void;
306
+ track_with_groups(
307
+ event_name: string,
308
+ properties: Dict,
309
+ groups: Dict,
310
+ callback?: Callback
311
+ ): void;
312
+ unregister(property: string, options?: Partial<RegisterOptions>): void;
313
+ people: People;
314
+ start_session_recording(): void;
315
+ stop_session_recording(): void;
316
+ get_session_recording_properties(): { $mp_replay_id?: string } | {};
317
+ }
318
+
319
+ export interface OverridedMixpanel extends Mixpanel {
320
+ init(token: string, config: Partial<Config>, name: string): Mixpanel;
321
+ init(token: string, config?: Partial<Config>): undefined;
322
+ }
323
+
324
+ export function add_group(
325
+ group_key: string,
326
+ group_id: string,
327
+ callback?: Callback
328
+ ): void;
329
+ export function alias(alias: string, original?: string): void;
330
+ export function clear_opt_in_out_tracking(
331
+ options?: Partial<ClearOptOutInOutOptions>
332
+ ): void;
333
+ export function disable(events?: string[]): void;
334
+ export function get_config(prop_name?: string): any;
335
+ export function get_distinct_id(): any;
336
+ export function get_group(group_key: string, group_id: string): Group;
337
+ export function get_property(property_name: string): any;
338
+ export function has_opted_in_tracking(
339
+ options?: Partial<HasOptedInOutOptions>
340
+ ): boolean;
341
+ export function has_opted_out_tracking(
342
+ options?: Partial<HasOptedInOutOptions>
343
+ ): boolean;
344
+ export function identify(unique_id?: string): any;
345
+ export function init(
346
+ token: string,
347
+ config: Partial<Config>,
348
+ name: string
349
+ ): Mixpanel;
350
+ export function init(token: string, config?: Partial<Config>): undefined;
351
+ export function opt_in_tracking(options?: Partial<InTrackingOptions>): void;
352
+ export function opt_out_tracking(options?: Partial<OutTrackingOptions>): void;
353
+ export function push(item: PushItem): void;
354
+ export function register(
355
+ props: Dict,
356
+ days_or_options?: number | Partial<RegisterOptions>
357
+ ): void;
358
+ export function register_once(
359
+ props: Dict,
360
+ default_value?: any,
361
+ days_or_options?: number | Partial<RegisterOptions>
362
+ ): void;
363
+ export function remove_group(
364
+ group_key: string,
365
+ group_ids: string | string[] | number | number[],
366
+ callback?: Callback
367
+ ): void;
368
+ export function reset(): void;
369
+ export function set_config(config: Partial<Config>): void;
370
+ export function set_group(
371
+ group_key: string,
372
+ group_ids: string | string[] | number | number[],
373
+ callback?: Callback
374
+ ): void;
375
+ export function time_event(event_name: string): void;
376
+ export function track(
377
+ event_name: string,
378
+ properties?: Dict,
379
+ optionsOrCallback?: RequestOptions | Callback,
380
+ callback?: Callback
381
+ ): void;
382
+ export function track_forms(
383
+ query: Query,
384
+ event_name: string,
385
+ properties?: Dict | (() => void)
386
+ ): void;
387
+ export function track_links(
388
+ query: Query,
389
+ event_name: string,
390
+ properties?: Dict | (() => void)
391
+ ): void;
392
+ export function track_with_groups(
393
+ event_name: string,
394
+ properties: Dict,
395
+ groups: Dict,
396
+ callback?: Callback
397
+ ): void;
398
+ export function unregister(
399
+ property: string,
400
+ options?: Partial<RegisterOptions>
401
+ ): void;
402
+ export const people: People;
403
+ export function get_session_recording_properties():
404
+ | { $mp_replay_id?: string }
405
+ | {};
406
+
407
+ declare const mixpanel: OverridedMixpanel;
408
+ export default mixpanel;
@@ -99,6 +99,7 @@ var DEFAULT_API_ROUTES = {
99
99
  */
100
100
  var DEFAULT_CONFIG = {
101
101
  'api_host': 'https://api-js.mixpanel.com',
102
+ 'api_hosts': {},
102
103
  'api_routes': DEFAULT_API_ROUTES,
103
104
  'api_extra_query_params': {},
104
105
  'api_method': 'POST',
@@ -368,8 +369,11 @@ MixpanelLib.prototype._init = function(token, config, name) {
368
369
  }
369
370
 
370
371
  this.flags = new FeatureFlagManager({
372
+ getFullApiRoute: _.bind(function() {
373
+ return this.get_api_host('flags') + '/' + this.get_config('api_routes')['flags'];
374
+ }, this),
371
375
  getConfigFunc: _.bind(this.get_config, this),
372
- getDistinctIdFunc: _.bind(this.get_distinct_id, this),
376
+ getPropertyFunc: _.bind(this.get_property, this),
373
377
  trackingFunc: _.bind(this.track, this)
374
378
  });
375
379
  this.flags.init();
@@ -484,20 +488,23 @@ MixpanelLib.prototype.start_session_recording = function () {
484
488
 
485
489
  MixpanelLib.prototype.stop_session_recording = function () {
486
490
  if (this._recorder) {
487
- this._recorder['stopRecording']();
491
+ return this._recorder['stopRecording']();
488
492
  }
493
+ return Promise.resolve();
489
494
  };
490
495
 
491
496
  MixpanelLib.prototype.pause_session_recording = function () {
492
497
  if (this._recorder) {
493
- this._recorder['pauseRecording']();
498
+ return this._recorder['pauseRecording']();
494
499
  }
500
+ return Promise.resolve();
495
501
  };
496
502
 
497
503
  MixpanelLib.prototype.resume_session_recording = function () {
498
504
  if (this._recorder) {
499
- this._recorder['resumeRecording']();
505
+ return this._recorder['resumeRecording']();
500
506
  }
507
+ return Promise.resolve();
501
508
  };
502
509
 
503
510
  MixpanelLib.prototype.is_recording_heatmap_data = function () {
@@ -852,11 +859,10 @@ MixpanelLib.prototype.are_batchers_initialized = function() {
852
859
 
853
860
  MixpanelLib.prototype.get_batcher_configs = function() {
854
861
  var queue_prefix = '__mpq_' + this.get_config('token');
855
- var api_routes = this.get_config('api_routes');
856
862
  this._batcher_configs = this._batcher_configs || {
857
- events: {type: 'events', endpoint: '/' + api_routes['track'], queue_key: queue_prefix + '_ev'},
858
- people: {type: 'people', endpoint: '/' + api_routes['engage'], queue_key: queue_prefix + '_pp'},
859
- groups: {type: 'groups', endpoint: '/' + api_routes['groups'], queue_key: queue_prefix + '_gr'}
863
+ events: {type: 'events', api_name: 'track', queue_key: queue_prefix + '_ev'},
864
+ people: {type: 'people', api_name: 'engage', queue_key: queue_prefix + '_pp'},
865
+ groups: {type: 'groups', api_name: 'groups', queue_key: queue_prefix + '_gr'}
860
866
  };
861
867
  return this._batcher_configs;
862
868
  };
@@ -870,8 +876,9 @@ MixpanelLib.prototype.init_batchers = function() {
870
876
  libConfig: this['config'],
871
877
  errorReporter: this.get_config('error_reporter'),
872
878
  sendRequestFunc: _.bind(function(data, options, cb) {
879
+ var api_routes = this.get_config('api_routes');
873
880
  this._send_request(
874
- this.get_config('api_host') + attrs.endpoint,
881
+ this.get_api_host(attrs.api_name) + '/' + api_routes[attrs.api_name],
875
882
  this._encode_data_for_request(data),
876
883
  options,
877
884
  this._prepare_callback(cb, data)
@@ -1097,7 +1104,7 @@ MixpanelLib.prototype.track = addOptOutCheckMixpanelLib(function(event_name, pro
1097
1104
  var ret = this._track_or_batch({
1098
1105
  type: 'events',
1099
1106
  data: data,
1100
- endpoint: this.get_config('api_host') + '/' + this.get_config('api_routes')['track'],
1107
+ endpoint: this.get_api_host('events') + '/' + this.get_config('api_routes')['track'],
1101
1108
  batcher: this.request_batchers.events,
1102
1109
  should_send_immediately: should_send_immediately,
1103
1110
  send_request_options: options
@@ -1599,6 +1606,7 @@ MixpanelLib.prototype.identify = function(
1599
1606
  * Useful for clearing data when a user logs out.
1600
1607
  */
1601
1608
  MixpanelLib.prototype.reset = function() {
1609
+ this.stop_session_recording();
1602
1610
  this['persistence'].clear();
1603
1611
  this._flags.identify_called = false;
1604
1612
  var uuid = _.UUID();
@@ -1606,7 +1614,6 @@ MixpanelLib.prototype.reset = function() {
1606
1614
  'distinct_id': DEVICE_ID_PREFIX + uuid,
1607
1615
  '$device_id': uuid
1608
1616
  }, '');
1609
- this.stop_session_recording();
1610
1617
  this._check_and_start_session_recording();
1611
1618
  };
1612
1619
 
@@ -1918,6 +1925,16 @@ MixpanelLib.prototype.get_property = function(property_name) {
1918
1925
  return this['persistence'].load_prop([property_name]);
1919
1926
  };
1920
1927
 
1928
+ /**
1929
+ * Get the API host for a specific endpoint type, falling back to the default api_host if not specified
1930
+ *
1931
+ * @param {String} endpoint_type The type of endpoint (e.g., "events", "people", "groups")
1932
+ * @returns {String} The API host to use for this endpoint
1933
+ */
1934
+ MixpanelLib.prototype.get_api_host = function(endpoint_type) {
1935
+ return this.get_config('api_hosts')[endpoint_type] || this.get_config('api_host');
1936
+ };
1937
+
1921
1938
  MixpanelLib.prototype.toString = function() {
1922
1939
  var name = this.get_config('name');
1923
1940
  if (name !== PRIMARY_INSTANCE_NAME) {
@@ -2213,6 +2230,7 @@ MixpanelLib.prototype['alias'] = MixpanelLib.protot
2213
2230
  MixpanelLib.prototype['name_tag'] = MixpanelLib.prototype.name_tag;
2214
2231
  MixpanelLib.prototype['set_config'] = MixpanelLib.prototype.set_config;
2215
2232
  MixpanelLib.prototype['get_config'] = MixpanelLib.prototype.get_config;
2233
+ MixpanelLib.prototype['get_api_host'] = MixpanelLib.prototype.get_api_host;
2216
2234
  MixpanelLib.prototype['get_property'] = MixpanelLib.prototype.get_property;
2217
2235
  MixpanelLib.prototype['get_distinct_id'] = MixpanelLib.prototype.get_distinct_id;
2218
2236
  MixpanelLib.prototype['toString'] = MixpanelLib.prototype.toString;
@@ -146,7 +146,7 @@ MixpanelGroup.prototype._send_request = function(data, callback) {
146
146
  return this._mixpanel._track_or_batch({
147
147
  type: 'groups',
148
148
  data: date_encoded_data,
149
- endpoint: this._get_config('api_host') + '/' + this._get_config('api_routes')['groups'],
149
+ endpoint: this._mixpanel.get_api_host('groups') + '/' + this._get_config('api_routes')['groups'],
150
150
  batcher: this._mixpanel.request_batchers.groups
151
151
  }, callback);
152
152
  };
@@ -337,7 +337,7 @@ MixpanelPeople.prototype._send_request = function(data, callback) {
337
337
  return this._mixpanel._track_or_batch({
338
338
  type: 'people',
339
339
  data: date_encoded_data,
340
- endpoint: this._get_config('api_host') + '/' + this._get_config('api_routes')['engage'],
340
+ endpoint: this._mixpanel.get_api_host('people') + '/' + this._get_config('api_routes')['engage'],
341
341
  batcher: this._mixpanel.request_batchers.people
342
342
  }, callback);
343
343
  };
@@ -27,6 +27,7 @@ var MixpanelRecorder = function(mixpanelInstance, rrwebRecord, sharedLockStorage
27
27
  this._flushInactivePromise = this.recordingRegistry.flushInactiveRecordings();
28
28
 
29
29
  this.activeRecording = null;
30
+ this.stopRecordingInProgress = false;
30
31
  };
31
32
 
32
33
  MixpanelRecorder.prototype.startRecording = function(options) {
@@ -75,19 +76,26 @@ MixpanelRecorder.prototype.startRecording = function(options) {
75
76
  };
76
77
 
77
78
  MixpanelRecorder.prototype.stopRecording = function() {
78
- var stopPromise = this._stopCurrentRecording(false);
79
- this.recordingRegistry.clearActiveRecording();
80
- this.activeRecording = null;
81
- return stopPromise;
79
+ // Prevents activeSerializedRecording from being reused when stopping the recording.
80
+ this.stopRecordingInProgress = true;
81
+ return this._stopCurrentRecording(false, true).then(function() {
82
+ return this.recordingRegistry.clearActiveRecording();
83
+ }.bind(this)).then(function() {
84
+ this.stopRecordingInProgress = false;
85
+ }.bind(this));
82
86
  };
83
87
 
84
88
  MixpanelRecorder.prototype.pauseRecording = function() {
85
89
  return this._stopCurrentRecording(false);
86
90
  };
87
91
 
88
- MixpanelRecorder.prototype._stopCurrentRecording = function(skipFlush) {
92
+ MixpanelRecorder.prototype._stopCurrentRecording = function(skipFlush, disableActiveRecording) {
89
93
  if (this.activeRecording) {
90
- return this.activeRecording.stopRecording(skipFlush);
94
+ var stopRecordingPromise = this.activeRecording.stopRecording(skipFlush);
95
+ if (disableActiveRecording) {
96
+ this.activeRecording = null;
97
+ }
98
+ return stopRecordingPromise;
91
99
  }
92
100
  return Promise.resolve();
93
101
  };
@@ -100,7 +108,7 @@ MixpanelRecorder.prototype.resumeRecording = function (startNewIfInactive) {
100
108
 
101
109
  return this.recordingRegistry.getActiveRecording()
102
110
  .then(function (activeSerializedRecording) {
103
- if (activeSerializedRecording) {
111
+ if (activeSerializedRecording && !this.stopRecordingInProgress) {
104
112
  return this.startRecording({activeSerializedRecording: activeSerializedRecording});
105
113
  } else if (startNewIfInactive) {
106
114
  return this.startRecording({shouldStopBatcher: false});
@@ -63,6 +63,13 @@ function isUserEvent(ev) {
63
63
  * @property {string} replayStartUrl
64
64
  */
65
65
 
66
+ /**
67
+ * @typedef {Object} UserIdInfo
68
+ * @property {string} distinct_id
69
+ * @property {string} user_id
70
+ * @property {string} device_id
71
+ */
72
+
66
73
 
67
74
  /**
68
75
  * This class encapsulates a single session recording and its lifecycle.
@@ -118,6 +125,30 @@ var SessionRecording = function(options) {
118
125
  });
119
126
  };
120
127
 
128
+ /**
129
+ * @returns {UserIdInfo}
130
+ */
131
+ SessionRecording.prototype.getUserIdInfo = function () {
132
+ if (this.finalFlushUserIdInfo) {
133
+ return this.finalFlushUserIdInfo;
134
+ }
135
+
136
+ var userIdInfo = {
137
+ 'distinct_id': String(this._mixpanel.get_distinct_id()),
138
+ };
139
+
140
+ // send ID management props if they exist
141
+ var deviceId = this._mixpanel.get_property('$device_id');
142
+ if (deviceId) {
143
+ userIdInfo['$device_id'] = deviceId;
144
+ }
145
+ var userId = this._mixpanel.get_property('$user_id');
146
+ if (userId) {
147
+ userIdInfo['$user_id'] = userId;
148
+ }
149
+ return userIdInfo;
150
+ };
151
+
121
152
  SessionRecording.prototype.unloadPersistedData = function () {
122
153
  this.batcher.stop();
123
154
  return this.batcher.flush()
@@ -242,6 +273,9 @@ SessionRecording.prototype.startRecording = function (shouldStopBatcher) {
242
273
  };
243
274
 
244
275
  SessionRecording.prototype.stopRecording = function (skipFlush) {
276
+ // store the user ID info in case this is getting called in mixpanel.reset()
277
+ this.finalFlushUserIdInfo = this.getUserIdInfo();
278
+
245
279
  if (!this.isRrwebStopped()) {
246
280
  try {
247
281
  this._stopRecording();
@@ -352,8 +386,8 @@ SessionRecording.prototype._sendRequest = function(currentReplayId, reqParams, r
352
386
  retryAfter: response.headers.get('Retry-After')
353
387
  });
354
388
  }.bind(this);
355
-
356
- window['fetch'](this.getConfig('api_host') + '/' + this.getConfig('api_routes')['record'] + '?' + new URLSearchParams(reqParams), {
389
+ var apiHost = (this._mixpanel.get_api_host && this._mixpanel.get_api_host('record')) || this.getConfig('api_host');
390
+ window['fetch'](apiHost + '/' + this.getConfig('api_routes')['record'] + '?' + new URLSearchParams(reqParams), {
357
391
  'method': 'POST',
358
392
  'headers': {
359
393
  'Authorization': 'Basic ' + btoa(this.getConfig('token') + ':'),
@@ -407,7 +441,6 @@ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (da
407
441
  '$current_url': this.batchStartUrl,
408
442
  '$lib_version': Config.LIB_VERSION,
409
443
  'batch_start_time': batchStartTime / 1000,
410
- 'distinct_id': String(this._mixpanel.get_distinct_id()),
411
444
  'mp_lib': 'web',
412
445
  'replay_id': replayId,
413
446
  'replay_length_ms': replayLengthMs,
@@ -416,16 +449,7 @@ SessionRecording.prototype._flushEvents = addOptOutCheckMixpanelLib(function (da
416
449
  'seq': this.seqNo
417
450
  };
418
451
  var eventsJson = JSON.stringify(data);
419
-
420
- // send ID management props if they exist
421
- var deviceId = this._mixpanel.get_property('$device_id');
422
- if (deviceId) {
423
- reqParams['$device_id'] = deviceId;
424
- }
425
- var userId = this._mixpanel.get_property('$user_id');
426
- if (userId) {
427
- reqParams['$user_id'] = userId;
428
- }
452
+ Object.assign(reqParams, this.getUserIdInfo());
429
453
 
430
454
  if (CompressionStream) {
431
455
  var jsonStream = new Blob([eventsJson], {type: 'application/json'}).stream();
package/tunnel.log DELETED
File without changes