mixpanel-browser 2.53.0 → 2.54.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/.vscode/launch.json +23 -0
- package/CHANGELOG.md +6 -0
- package/README.md +12 -0
- package/build.sh +2 -0
- package/dist/mixpanel-core.cjs.js +6365 -0
- package/dist/mixpanel-recorder.js +958 -107
- package/dist/mixpanel-recorder.min.js +9 -9
- package/dist/mixpanel-with-async-recorder.cjs.js +6367 -0
- package/dist/mixpanel.amd.js +5310 -518
- package/dist/mixpanel.cjs.js +5310 -518
- package/dist/mixpanel.globals.js +161 -113
- package/dist/mixpanel.min.js +106 -106
- package/dist/mixpanel.umd.js +5310 -518
- package/package.json +1 -2
- package/rollup.config.js +3 -3
- package/src/config.js +1 -1
- package/src/loaders/bundle-loaders.js +22 -0
- package/src/loaders/loader-globals.js +2 -1
- package/src/loaders/loader-module-core.js +7 -0
- package/src/loaders/loader-module-with-async-recorder.js +7 -0
- package/src/loaders/loader-module.js +4 -1
- package/src/mixpanel-core.js +18 -11
- package/src/recorder/index.js +109 -35
- package/src/request-batcher.js +19 -12
- package/src/request-queue.js +112 -88
|
@@ -1432,7 +1432,7 @@
|
|
|
1432
1432
|
return doc.contains(n) || shadowHostInDom(n);
|
|
1433
1433
|
}
|
|
1434
1434
|
|
|
1435
|
-
var EventType = /* @__PURE__ */ ((EventType2) => {
|
|
1435
|
+
var EventType$1 = /* @__PURE__ */ ((EventType2) => {
|
|
1436
1436
|
EventType2[EventType2["DomContentLoaded"] = 0] = "DomContentLoaded";
|
|
1437
1437
|
EventType2[EventType2["Load"] = 1] = "Load";
|
|
1438
1438
|
EventType2[EventType2["FullSnapshot"] = 2] = "FullSnapshot";
|
|
@@ -1441,8 +1441,8 @@
|
|
|
1441
1441
|
EventType2[EventType2["Custom"] = 5] = "Custom";
|
|
1442
1442
|
EventType2[EventType2["Plugin"] = 6] = "Plugin";
|
|
1443
1443
|
return EventType2;
|
|
1444
|
-
})(EventType || {});
|
|
1445
|
-
var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
|
|
1444
|
+
})(EventType$1 || {});
|
|
1445
|
+
var IncrementalSource$1 = /* @__PURE__ */ ((IncrementalSource2) => {
|
|
1446
1446
|
IncrementalSource2[IncrementalSource2["Mutation"] = 0] = "Mutation";
|
|
1447
1447
|
IncrementalSource2[IncrementalSource2["MouseMove"] = 1] = "MouseMove";
|
|
1448
1448
|
IncrementalSource2[IncrementalSource2["MouseInteraction"] = 2] = "MouseInteraction";
|
|
@@ -1461,7 +1461,7 @@
|
|
|
1461
1461
|
IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
|
|
1462
1462
|
IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
|
|
1463
1463
|
return IncrementalSource2;
|
|
1464
|
-
})(IncrementalSource || {});
|
|
1464
|
+
})(IncrementalSource$1 || {});
|
|
1465
1465
|
var MouseInteractions = /* @__PURE__ */ ((MouseInteractions2) => {
|
|
1466
1466
|
MouseInteractions2[MouseInteractions2["MouseUp"] = 0] = "MouseUp";
|
|
1467
1467
|
MouseInteractions2[MouseInteractions2["MouseDown"] = 1] = "MouseDown";
|
|
@@ -2180,10 +2180,10 @@
|
|
|
2180
2180
|
timeOffset: nowTimestamp() - timeBaseline,
|
|
2181
2181
|
});
|
|
2182
2182
|
wrappedCb(typeof DragEvent !== 'undefined' && evt instanceof DragEvent
|
|
2183
|
-
? IncrementalSource.Drag
|
|
2183
|
+
? IncrementalSource$1.Drag
|
|
2184
2184
|
: evt instanceof MouseEvent
|
|
2185
|
-
? IncrementalSource.MouseMove
|
|
2186
|
-
: IncrementalSource.TouchMove);
|
|
2185
|
+
? IncrementalSource$1.MouseMove
|
|
2186
|
+
: IncrementalSource$1.TouchMove);
|
|
2187
2187
|
}), threshold, {
|
|
2188
2188
|
trailing: false,
|
|
2189
2189
|
}));
|
|
@@ -3096,7 +3096,7 @@
|
|
|
3096
3096
|
transformCrossOriginEvent(iframeEl, e) {
|
|
3097
3097
|
var _a;
|
|
3098
3098
|
switch (e.type) {
|
|
3099
|
-
case EventType.FullSnapshot: {
|
|
3099
|
+
case EventType$1.FullSnapshot: {
|
|
3100
3100
|
this.crossOriginIframeMirror.reset(iframeEl);
|
|
3101
3101
|
this.crossOriginIframeStyleMirror.reset(iframeEl);
|
|
3102
3102
|
this.replaceIdOnNode(e.data.node, iframeEl);
|
|
@@ -3105,9 +3105,9 @@
|
|
|
3105
3105
|
this.patchRootIdOnNode(e.data.node, rootId);
|
|
3106
3106
|
return {
|
|
3107
3107
|
timestamp: e.timestamp,
|
|
3108
|
-
type: EventType.IncrementalSnapshot,
|
|
3108
|
+
type: EventType$1.IncrementalSnapshot,
|
|
3109
3109
|
data: {
|
|
3110
|
-
source: IncrementalSource.Mutation,
|
|
3110
|
+
source: IncrementalSource$1.Mutation,
|
|
3111
3111
|
adds: [
|
|
3112
3112
|
{
|
|
3113
3113
|
parentId: this.mirror.getId(iframeEl),
|
|
@@ -3122,21 +3122,21 @@
|
|
|
3122
3122
|
},
|
|
3123
3123
|
};
|
|
3124
3124
|
}
|
|
3125
|
-
case EventType.Meta:
|
|
3126
|
-
case EventType.Load:
|
|
3127
|
-
case EventType.DomContentLoaded: {
|
|
3125
|
+
case EventType$1.Meta:
|
|
3126
|
+
case EventType$1.Load:
|
|
3127
|
+
case EventType$1.DomContentLoaded: {
|
|
3128
3128
|
return false;
|
|
3129
3129
|
}
|
|
3130
|
-
case EventType.Plugin: {
|
|
3130
|
+
case EventType$1.Plugin: {
|
|
3131
3131
|
return e;
|
|
3132
3132
|
}
|
|
3133
|
-
case EventType.Custom: {
|
|
3133
|
+
case EventType$1.Custom: {
|
|
3134
3134
|
this.replaceIds(e.data.payload, iframeEl, ['id', 'parentId', 'previousId', 'nextId']);
|
|
3135
3135
|
return e;
|
|
3136
3136
|
}
|
|
3137
|
-
case EventType.IncrementalSnapshot: {
|
|
3137
|
+
case EventType$1.IncrementalSnapshot: {
|
|
3138
3138
|
switch (e.data.source) {
|
|
3139
|
-
case IncrementalSource.Mutation: {
|
|
3139
|
+
case IncrementalSource$1.Mutation: {
|
|
3140
3140
|
e.data.adds.forEach((n) => {
|
|
3141
3141
|
this.replaceIds(n, iframeEl, [
|
|
3142
3142
|
'parentId',
|
|
@@ -3158,41 +3158,41 @@
|
|
|
3158
3158
|
});
|
|
3159
3159
|
return e;
|
|
3160
3160
|
}
|
|
3161
|
-
case IncrementalSource.Drag:
|
|
3162
|
-
case IncrementalSource.TouchMove:
|
|
3163
|
-
case IncrementalSource.MouseMove: {
|
|
3161
|
+
case IncrementalSource$1.Drag:
|
|
3162
|
+
case IncrementalSource$1.TouchMove:
|
|
3163
|
+
case IncrementalSource$1.MouseMove: {
|
|
3164
3164
|
e.data.positions.forEach((p) => {
|
|
3165
3165
|
this.replaceIds(p, iframeEl, ['id']);
|
|
3166
3166
|
});
|
|
3167
3167
|
return e;
|
|
3168
3168
|
}
|
|
3169
|
-
case IncrementalSource.ViewportResize: {
|
|
3169
|
+
case IncrementalSource$1.ViewportResize: {
|
|
3170
3170
|
return false;
|
|
3171
3171
|
}
|
|
3172
|
-
case IncrementalSource.MediaInteraction:
|
|
3173
|
-
case IncrementalSource.MouseInteraction:
|
|
3174
|
-
case IncrementalSource.Scroll:
|
|
3175
|
-
case IncrementalSource.CanvasMutation:
|
|
3176
|
-
case IncrementalSource.Input: {
|
|
3172
|
+
case IncrementalSource$1.MediaInteraction:
|
|
3173
|
+
case IncrementalSource$1.MouseInteraction:
|
|
3174
|
+
case IncrementalSource$1.Scroll:
|
|
3175
|
+
case IncrementalSource$1.CanvasMutation:
|
|
3176
|
+
case IncrementalSource$1.Input: {
|
|
3177
3177
|
this.replaceIds(e.data, iframeEl, ['id']);
|
|
3178
3178
|
return e;
|
|
3179
3179
|
}
|
|
3180
|
-
case IncrementalSource.StyleSheetRule:
|
|
3181
|
-
case IncrementalSource.StyleDeclaration: {
|
|
3180
|
+
case IncrementalSource$1.StyleSheetRule:
|
|
3181
|
+
case IncrementalSource$1.StyleDeclaration: {
|
|
3182
3182
|
this.replaceIds(e.data, iframeEl, ['id']);
|
|
3183
3183
|
this.replaceStyleIds(e.data, iframeEl, ['styleId']);
|
|
3184
3184
|
return e;
|
|
3185
3185
|
}
|
|
3186
|
-
case IncrementalSource.Font: {
|
|
3186
|
+
case IncrementalSource$1.Font: {
|
|
3187
3187
|
return e;
|
|
3188
3188
|
}
|
|
3189
|
-
case IncrementalSource.Selection: {
|
|
3189
|
+
case IncrementalSource$1.Selection: {
|
|
3190
3190
|
e.data.ranges.forEach((range) => {
|
|
3191
3191
|
this.replaceIds(range, iframeEl, ['start', 'end']);
|
|
3192
3192
|
});
|
|
3193
3193
|
return e;
|
|
3194
3194
|
}
|
|
3195
|
-
case IncrementalSource.AdoptedStyleSheet: {
|
|
3195
|
+
case IncrementalSource$1.AdoptedStyleSheet: {
|
|
3196
3196
|
this.replaceIds(e.data, iframeEl, ['id']);
|
|
3197
3197
|
this.replaceStyleIds(e.data, iframeEl, ['styleIds']);
|
|
3198
3198
|
(_a = e.data.styles) === null || _a === void 0 ? void 0 : _a.forEach((style) => {
|
|
@@ -4143,9 +4143,9 @@
|
|
|
4143
4143
|
wrappedEmit = (e, isCheckout) => {
|
|
4144
4144
|
var _a;
|
|
4145
4145
|
if (((_a = mutationBuffers[0]) === null || _a === void 0 ? void 0 : _a.isFrozen()) &&
|
|
4146
|
-
e.type !== EventType.FullSnapshot &&
|
|
4147
|
-
!(e.type === EventType.IncrementalSnapshot &&
|
|
4148
|
-
e.data.source === IncrementalSource.Mutation)) {
|
|
4146
|
+
e.type !== EventType$1.FullSnapshot &&
|
|
4147
|
+
!(e.type === EventType$1.IncrementalSnapshot &&
|
|
4148
|
+
e.data.source === IncrementalSource$1.Mutation)) {
|
|
4149
4149
|
mutationBuffers.forEach((buf) => buf.unfreeze());
|
|
4150
4150
|
}
|
|
4151
4151
|
if (inEmittingFrame) {
|
|
@@ -4160,12 +4160,12 @@
|
|
|
4160
4160
|
};
|
|
4161
4161
|
window.parent.postMessage(message, '*');
|
|
4162
4162
|
}
|
|
4163
|
-
if (e.type === EventType.FullSnapshot) {
|
|
4163
|
+
if (e.type === EventType$1.FullSnapshot) {
|
|
4164
4164
|
lastFullSnapshotEvent = e;
|
|
4165
4165
|
incrementalSnapshotCount = 0;
|
|
4166
4166
|
}
|
|
4167
|
-
else if (e.type === EventType.IncrementalSnapshot) {
|
|
4168
|
-
if (e.data.source === IncrementalSource.Mutation &&
|
|
4167
|
+
else if (e.type === EventType$1.IncrementalSnapshot) {
|
|
4168
|
+
if (e.data.source === IncrementalSource$1.Mutation &&
|
|
4169
4169
|
e.data.isAttachIframe) {
|
|
4170
4170
|
return;
|
|
4171
4171
|
}
|
|
@@ -4180,21 +4180,21 @@
|
|
|
4180
4180
|
};
|
|
4181
4181
|
const wrappedMutationEmit = (m) => {
|
|
4182
4182
|
wrappedEmit(wrapEvent({
|
|
4183
|
-
type: EventType.IncrementalSnapshot,
|
|
4184
|
-
data: Object.assign({ source: IncrementalSource.Mutation }, m),
|
|
4183
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4184
|
+
data: Object.assign({ source: IncrementalSource$1.Mutation }, m),
|
|
4185
4185
|
}));
|
|
4186
4186
|
};
|
|
4187
4187
|
const wrappedScrollEmit = (p) => wrappedEmit(wrapEvent({
|
|
4188
|
-
type: EventType.IncrementalSnapshot,
|
|
4189
|
-
data: Object.assign({ source: IncrementalSource.Scroll }, p),
|
|
4188
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4189
|
+
data: Object.assign({ source: IncrementalSource$1.Scroll }, p),
|
|
4190
4190
|
}));
|
|
4191
4191
|
const wrappedCanvasMutationEmit = (p) => wrappedEmit(wrapEvent({
|
|
4192
|
-
type: EventType.IncrementalSnapshot,
|
|
4193
|
-
data: Object.assign({ source: IncrementalSource.CanvasMutation }, p),
|
|
4192
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4193
|
+
data: Object.assign({ source: IncrementalSource$1.CanvasMutation }, p),
|
|
4194
4194
|
}));
|
|
4195
4195
|
const wrappedAdoptedStyleSheetEmit = (a) => wrappedEmit(wrapEvent({
|
|
4196
|
-
type: EventType.IncrementalSnapshot,
|
|
4197
|
-
data: Object.assign({ source: IncrementalSource.AdoptedStyleSheet }, a),
|
|
4196
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4197
|
+
data: Object.assign({ source: IncrementalSource$1.AdoptedStyleSheet }, a),
|
|
4198
4198
|
}));
|
|
4199
4199
|
const stylesheetManager = new StylesheetManager({
|
|
4200
4200
|
mutationCb: wrappedMutationEmit,
|
|
@@ -4256,7 +4256,7 @@
|
|
|
4256
4256
|
return;
|
|
4257
4257
|
}
|
|
4258
4258
|
wrappedEmit(wrapEvent({
|
|
4259
|
-
type: EventType.Meta,
|
|
4259
|
+
type: EventType$1.Meta,
|
|
4260
4260
|
data: {
|
|
4261
4261
|
href: window.location.href,
|
|
4262
4262
|
width: getWindowWidth(),
|
|
@@ -4303,7 +4303,7 @@
|
|
|
4303
4303
|
return console.warn('Failed to snapshot the document');
|
|
4304
4304
|
}
|
|
4305
4305
|
wrappedEmit(wrapEvent({
|
|
4306
|
-
type: EventType.FullSnapshot,
|
|
4306
|
+
type: EventType$1.FullSnapshot,
|
|
4307
4307
|
data: {
|
|
4308
4308
|
node,
|
|
4309
4309
|
initialOffset: getWindowScroll(window),
|
|
@@ -4320,52 +4320,52 @@
|
|
|
4320
4320
|
return callbackWrapper(initObservers)({
|
|
4321
4321
|
mutationCb: wrappedMutationEmit,
|
|
4322
4322
|
mousemoveCb: (positions, source) => wrappedEmit(wrapEvent({
|
|
4323
|
-
type: EventType.IncrementalSnapshot,
|
|
4323
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4324
4324
|
data: {
|
|
4325
4325
|
source,
|
|
4326
4326
|
positions,
|
|
4327
4327
|
},
|
|
4328
4328
|
})),
|
|
4329
4329
|
mouseInteractionCb: (d) => wrappedEmit(wrapEvent({
|
|
4330
|
-
type: EventType.IncrementalSnapshot,
|
|
4331
|
-
data: Object.assign({ source: IncrementalSource.MouseInteraction }, d),
|
|
4330
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4331
|
+
data: Object.assign({ source: IncrementalSource$1.MouseInteraction }, d),
|
|
4332
4332
|
})),
|
|
4333
4333
|
scrollCb: wrappedScrollEmit,
|
|
4334
4334
|
viewportResizeCb: (d) => wrappedEmit(wrapEvent({
|
|
4335
|
-
type: EventType.IncrementalSnapshot,
|
|
4336
|
-
data: Object.assign({ source: IncrementalSource.ViewportResize }, d),
|
|
4335
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4336
|
+
data: Object.assign({ source: IncrementalSource$1.ViewportResize }, d),
|
|
4337
4337
|
})),
|
|
4338
4338
|
inputCb: (v) => wrappedEmit(wrapEvent({
|
|
4339
|
-
type: EventType.IncrementalSnapshot,
|
|
4340
|
-
data: Object.assign({ source: IncrementalSource.Input }, v),
|
|
4339
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4340
|
+
data: Object.assign({ source: IncrementalSource$1.Input }, v),
|
|
4341
4341
|
})),
|
|
4342
4342
|
mediaInteractionCb: (p) => wrappedEmit(wrapEvent({
|
|
4343
|
-
type: EventType.IncrementalSnapshot,
|
|
4344
|
-
data: Object.assign({ source: IncrementalSource.MediaInteraction }, p),
|
|
4343
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4344
|
+
data: Object.assign({ source: IncrementalSource$1.MediaInteraction }, p),
|
|
4345
4345
|
})),
|
|
4346
4346
|
styleSheetRuleCb: (r) => wrappedEmit(wrapEvent({
|
|
4347
|
-
type: EventType.IncrementalSnapshot,
|
|
4348
|
-
data: Object.assign({ source: IncrementalSource.StyleSheetRule }, r),
|
|
4347
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4348
|
+
data: Object.assign({ source: IncrementalSource$1.StyleSheetRule }, r),
|
|
4349
4349
|
})),
|
|
4350
4350
|
styleDeclarationCb: (r) => wrappedEmit(wrapEvent({
|
|
4351
|
-
type: EventType.IncrementalSnapshot,
|
|
4352
|
-
data: Object.assign({ source: IncrementalSource.StyleDeclaration }, r),
|
|
4351
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4352
|
+
data: Object.assign({ source: IncrementalSource$1.StyleDeclaration }, r),
|
|
4353
4353
|
})),
|
|
4354
4354
|
canvasMutationCb: wrappedCanvasMutationEmit,
|
|
4355
4355
|
fontCb: (p) => wrappedEmit(wrapEvent({
|
|
4356
|
-
type: EventType.IncrementalSnapshot,
|
|
4357
|
-
data: Object.assign({ source: IncrementalSource.Font }, p),
|
|
4356
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4357
|
+
data: Object.assign({ source: IncrementalSource$1.Font }, p),
|
|
4358
4358
|
})),
|
|
4359
4359
|
selectionCb: (p) => {
|
|
4360
4360
|
wrappedEmit(wrapEvent({
|
|
4361
|
-
type: EventType.IncrementalSnapshot,
|
|
4362
|
-
data: Object.assign({ source: IncrementalSource.Selection }, p),
|
|
4361
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4362
|
+
data: Object.assign({ source: IncrementalSource$1.Selection }, p),
|
|
4363
4363
|
}));
|
|
4364
4364
|
},
|
|
4365
4365
|
customElementCb: (c) => {
|
|
4366
4366
|
wrappedEmit(wrapEvent({
|
|
4367
|
-
type: EventType.IncrementalSnapshot,
|
|
4368
|
-
data: Object.assign({ source: IncrementalSource.CustomElement }, c),
|
|
4367
|
+
type: EventType$1.IncrementalSnapshot,
|
|
4368
|
+
data: Object.assign({ source: IncrementalSource$1.CustomElement }, c),
|
|
4369
4369
|
}));
|
|
4370
4370
|
},
|
|
4371
4371
|
blockClass,
|
|
@@ -4399,7 +4399,7 @@
|
|
|
4399
4399
|
observer: p.observer,
|
|
4400
4400
|
options: p.options,
|
|
4401
4401
|
callback: (payload) => wrappedEmit(wrapEvent({
|
|
4402
|
-
type: EventType.Plugin,
|
|
4402
|
+
type: EventType$1.Plugin,
|
|
4403
4403
|
data: {
|
|
4404
4404
|
plugin: p.name,
|
|
4405
4405
|
payload,
|
|
@@ -4428,7 +4428,7 @@
|
|
|
4428
4428
|
else {
|
|
4429
4429
|
handlers.push(on('DOMContentLoaded', () => {
|
|
4430
4430
|
wrappedEmit(wrapEvent({
|
|
4431
|
-
type: EventType.DomContentLoaded,
|
|
4431
|
+
type: EventType$1.DomContentLoaded,
|
|
4432
4432
|
data: {},
|
|
4433
4433
|
}));
|
|
4434
4434
|
if (recordAfter === 'DOMContentLoaded')
|
|
@@ -4436,7 +4436,7 @@
|
|
|
4436
4436
|
}));
|
|
4437
4437
|
handlers.push(on('load', () => {
|
|
4438
4438
|
wrappedEmit(wrapEvent({
|
|
4439
|
-
type: EventType.Load,
|
|
4439
|
+
type: EventType$1.Load,
|
|
4440
4440
|
data: {},
|
|
4441
4441
|
}));
|
|
4442
4442
|
if (recordAfter === 'load')
|
|
@@ -4459,7 +4459,7 @@
|
|
|
4459
4459
|
throw new Error('please add custom event after start recording');
|
|
4460
4460
|
}
|
|
4461
4461
|
wrappedEmit(wrapEvent({
|
|
4462
|
-
type: EventType.Custom,
|
|
4462
|
+
type: EventType$1.Custom,
|
|
4463
4463
|
data: {
|
|
4464
4464
|
tag,
|
|
4465
4465
|
payload,
|
|
@@ -4477,9 +4477,40 @@
|
|
|
4477
4477
|
};
|
|
4478
4478
|
record.mirror = mirror;
|
|
4479
4479
|
|
|
4480
|
+
var EventType = /* @__PURE__ */ ((EventType2) => {
|
|
4481
|
+
EventType2[EventType2["DomContentLoaded"] = 0] = "DomContentLoaded";
|
|
4482
|
+
EventType2[EventType2["Load"] = 1] = "Load";
|
|
4483
|
+
EventType2[EventType2["FullSnapshot"] = 2] = "FullSnapshot";
|
|
4484
|
+
EventType2[EventType2["IncrementalSnapshot"] = 3] = "IncrementalSnapshot";
|
|
4485
|
+
EventType2[EventType2["Meta"] = 4] = "Meta";
|
|
4486
|
+
EventType2[EventType2["Custom"] = 5] = "Custom";
|
|
4487
|
+
EventType2[EventType2["Plugin"] = 6] = "Plugin";
|
|
4488
|
+
return EventType2;
|
|
4489
|
+
})(EventType || {});
|
|
4490
|
+
var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
|
|
4491
|
+
IncrementalSource2[IncrementalSource2["Mutation"] = 0] = "Mutation";
|
|
4492
|
+
IncrementalSource2[IncrementalSource2["MouseMove"] = 1] = "MouseMove";
|
|
4493
|
+
IncrementalSource2[IncrementalSource2["MouseInteraction"] = 2] = "MouseInteraction";
|
|
4494
|
+
IncrementalSource2[IncrementalSource2["Scroll"] = 3] = "Scroll";
|
|
4495
|
+
IncrementalSource2[IncrementalSource2["ViewportResize"] = 4] = "ViewportResize";
|
|
4496
|
+
IncrementalSource2[IncrementalSource2["Input"] = 5] = "Input";
|
|
4497
|
+
IncrementalSource2[IncrementalSource2["TouchMove"] = 6] = "TouchMove";
|
|
4498
|
+
IncrementalSource2[IncrementalSource2["MediaInteraction"] = 7] = "MediaInteraction";
|
|
4499
|
+
IncrementalSource2[IncrementalSource2["StyleSheetRule"] = 8] = "StyleSheetRule";
|
|
4500
|
+
IncrementalSource2[IncrementalSource2["CanvasMutation"] = 9] = "CanvasMutation";
|
|
4501
|
+
IncrementalSource2[IncrementalSource2["Font"] = 10] = "Font";
|
|
4502
|
+
IncrementalSource2[IncrementalSource2["Log"] = 11] = "Log";
|
|
4503
|
+
IncrementalSource2[IncrementalSource2["Drag"] = 12] = "Drag";
|
|
4504
|
+
IncrementalSource2[IncrementalSource2["StyleDeclaration"] = 13] = "StyleDeclaration";
|
|
4505
|
+
IncrementalSource2[IncrementalSource2["Selection"] = 14] = "Selection";
|
|
4506
|
+
IncrementalSource2[IncrementalSource2["AdoptedStyleSheet"] = 15] = "AdoptedStyleSheet";
|
|
4507
|
+
IncrementalSource2[IncrementalSource2["CustomElement"] = 16] = "CustomElement";
|
|
4508
|
+
return IncrementalSource2;
|
|
4509
|
+
})(IncrementalSource || {});
|
|
4510
|
+
|
|
4480
4511
|
var Config = {
|
|
4481
4512
|
DEBUG: false,
|
|
4482
|
-
LIB_VERSION: '2.
|
|
4513
|
+
LIB_VERSION: '2.54.0'
|
|
4483
4514
|
};
|
|
4484
4515
|
|
|
4485
4516
|
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
@@ -6344,8 +6375,779 @@
|
|
|
6344
6375
|
};
|
|
6345
6376
|
}
|
|
6346
6377
|
|
|
6378
|
+
var logger$3 = console_with_prefix('lock');
|
|
6379
|
+
|
|
6380
|
+
/**
|
|
6381
|
+
* SharedLock: a mutex built on HTML5 localStorage, to ensure that only one browser
|
|
6382
|
+
* window/tab at a time will be able to access shared resources.
|
|
6383
|
+
*
|
|
6384
|
+
* Based on the Alur and Taubenfeld fast lock
|
|
6385
|
+
* (http://www.cs.rochester.edu/research/synchronization/pseudocode/fastlock.html)
|
|
6386
|
+
* with an added timeout to ensure there will be eventual progress in the event
|
|
6387
|
+
* that a window is closed in the middle of the callback.
|
|
6388
|
+
*
|
|
6389
|
+
* Implementation based on the original version by David Wolever (https://github.com/wolever)
|
|
6390
|
+
* at https://gist.github.com/wolever/5fd7573d1ef6166e8f8c4af286a69432.
|
|
6391
|
+
*
|
|
6392
|
+
* @example
|
|
6393
|
+
* const myLock = new SharedLock('some-key');
|
|
6394
|
+
* myLock.withLock(function() {
|
|
6395
|
+
* console.log('I hold the mutex!');
|
|
6396
|
+
* });
|
|
6397
|
+
*
|
|
6398
|
+
* @constructor
|
|
6399
|
+
*/
|
|
6400
|
+
var SharedLock = function(key, options) {
|
|
6401
|
+
options = options || {};
|
|
6402
|
+
|
|
6403
|
+
this.storageKey = key;
|
|
6404
|
+
this.storage = options.storage || window.localStorage;
|
|
6405
|
+
this.pollIntervalMS = options.pollIntervalMS || 100;
|
|
6406
|
+
this.timeoutMS = options.timeoutMS || 2000;
|
|
6407
|
+
};
|
|
6408
|
+
|
|
6409
|
+
// pass in a specific pid to test contention scenarios; otherwise
|
|
6410
|
+
// it is chosen randomly for each acquisition attempt
|
|
6411
|
+
SharedLock.prototype.withLock = function(lockedCB, errorCB, pid) {
|
|
6412
|
+
if (!pid && typeof errorCB !== 'function') {
|
|
6413
|
+
pid = errorCB;
|
|
6414
|
+
errorCB = null;
|
|
6415
|
+
}
|
|
6416
|
+
|
|
6417
|
+
var i = pid || (new Date().getTime() + '|' + Math.random());
|
|
6418
|
+
var startTime = new Date().getTime();
|
|
6419
|
+
|
|
6420
|
+
var key = this.storageKey;
|
|
6421
|
+
var pollIntervalMS = this.pollIntervalMS;
|
|
6422
|
+
var timeoutMS = this.timeoutMS;
|
|
6423
|
+
var storage = this.storage;
|
|
6424
|
+
|
|
6425
|
+
var keyX = key + ':X';
|
|
6426
|
+
var keyY = key + ':Y';
|
|
6427
|
+
var keyZ = key + ':Z';
|
|
6428
|
+
|
|
6429
|
+
var reportError = function(err) {
|
|
6430
|
+
errorCB && errorCB(err);
|
|
6431
|
+
};
|
|
6432
|
+
|
|
6433
|
+
var delay = function(cb) {
|
|
6434
|
+
if (new Date().getTime() - startTime > timeoutMS) {
|
|
6435
|
+
logger$3.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
|
|
6436
|
+
storage.removeItem(keyZ);
|
|
6437
|
+
storage.removeItem(keyY);
|
|
6438
|
+
loop();
|
|
6439
|
+
return;
|
|
6440
|
+
}
|
|
6441
|
+
setTimeout(function() {
|
|
6442
|
+
try {
|
|
6443
|
+
cb();
|
|
6444
|
+
} catch(err) {
|
|
6445
|
+
reportError(err);
|
|
6446
|
+
}
|
|
6447
|
+
}, pollIntervalMS * (Math.random() + 0.1));
|
|
6448
|
+
};
|
|
6449
|
+
|
|
6450
|
+
var waitFor = function(predicate, cb) {
|
|
6451
|
+
if (predicate()) {
|
|
6452
|
+
cb();
|
|
6453
|
+
} else {
|
|
6454
|
+
delay(function() {
|
|
6455
|
+
waitFor(predicate, cb);
|
|
6456
|
+
});
|
|
6457
|
+
}
|
|
6458
|
+
};
|
|
6459
|
+
|
|
6460
|
+
var getSetY = function() {
|
|
6461
|
+
var valY = storage.getItem(keyY);
|
|
6462
|
+
if (valY && valY !== i) { // if Y == i then this process already has the lock (useful for test cases)
|
|
6463
|
+
return false;
|
|
6464
|
+
} else {
|
|
6465
|
+
storage.setItem(keyY, i);
|
|
6466
|
+
if (storage.getItem(keyY) === i) {
|
|
6467
|
+
return true;
|
|
6468
|
+
} else {
|
|
6469
|
+
if (!localStorageSupported(storage, true)) {
|
|
6470
|
+
throw new Error('localStorage support dropped while acquiring lock');
|
|
6471
|
+
}
|
|
6472
|
+
return false;
|
|
6473
|
+
}
|
|
6474
|
+
}
|
|
6475
|
+
};
|
|
6476
|
+
|
|
6477
|
+
var loop = function() {
|
|
6478
|
+
storage.setItem(keyX, i);
|
|
6479
|
+
|
|
6480
|
+
waitFor(getSetY, function() {
|
|
6481
|
+
if (storage.getItem(keyX) === i) {
|
|
6482
|
+
criticalSection();
|
|
6483
|
+
return;
|
|
6484
|
+
}
|
|
6485
|
+
|
|
6486
|
+
delay(function() {
|
|
6487
|
+
if (storage.getItem(keyY) !== i) {
|
|
6488
|
+
loop();
|
|
6489
|
+
return;
|
|
6490
|
+
}
|
|
6491
|
+
waitFor(function() {
|
|
6492
|
+
return !storage.getItem(keyZ);
|
|
6493
|
+
}, criticalSection);
|
|
6494
|
+
});
|
|
6495
|
+
});
|
|
6496
|
+
};
|
|
6497
|
+
|
|
6498
|
+
var criticalSection = function() {
|
|
6499
|
+
storage.setItem(keyZ, '1');
|
|
6500
|
+
try {
|
|
6501
|
+
lockedCB();
|
|
6502
|
+
} finally {
|
|
6503
|
+
storage.removeItem(keyZ);
|
|
6504
|
+
if (storage.getItem(keyY) === i) {
|
|
6505
|
+
storage.removeItem(keyY);
|
|
6506
|
+
}
|
|
6507
|
+
if (storage.getItem(keyX) === i) {
|
|
6508
|
+
storage.removeItem(keyX);
|
|
6509
|
+
}
|
|
6510
|
+
}
|
|
6511
|
+
};
|
|
6512
|
+
|
|
6513
|
+
try {
|
|
6514
|
+
if (localStorageSupported(storage, true)) {
|
|
6515
|
+
loop();
|
|
6516
|
+
} else {
|
|
6517
|
+
throw new Error('localStorage support check failed');
|
|
6518
|
+
}
|
|
6519
|
+
} catch(err) {
|
|
6520
|
+
reportError(err);
|
|
6521
|
+
}
|
|
6522
|
+
};
|
|
6523
|
+
|
|
6524
|
+
var logger$2 = console_with_prefix('batch');
|
|
6525
|
+
|
|
6526
|
+
/**
|
|
6527
|
+
* RequestQueue: queue for batching API requests with localStorage backup for retries.
|
|
6528
|
+
* Maintains an in-memory queue which represents the source of truth for the current
|
|
6529
|
+
* page, but also writes all items out to a copy in the browser's localStorage, which
|
|
6530
|
+
* can be read on subsequent pageloads and retried. For batchability, all the request
|
|
6531
|
+
* items in the queue should be of the same type (events, people updates, group updates)
|
|
6532
|
+
* so they can be sent in a single request to the same API endpoint.
|
|
6533
|
+
*
|
|
6534
|
+
* LocalStorage keying and locking: In order for reloads and subsequent pageloads of
|
|
6535
|
+
* the same site to access the same persisted data, they must share the same localStorage
|
|
6536
|
+
* key (for instance based on project token and queue type). Therefore access to the
|
|
6537
|
+
* localStorage entry is guarded by an asynchronous mutex (SharedLock) to prevent
|
|
6538
|
+
* simultaneously open windows/tabs from overwriting each other's data (which would lead
|
|
6539
|
+
* to data loss in some situations).
|
|
6540
|
+
* @constructor
|
|
6541
|
+
*/
|
|
6542
|
+
var RequestQueue = function(storageKey, options) {
|
|
6543
|
+
options = options || {};
|
|
6544
|
+
this.storageKey = storageKey;
|
|
6545
|
+
this.storage = options.storage || window.localStorage;
|
|
6546
|
+
this.reportError = options.errorReporter || _.bind(logger$2.error, logger$2);
|
|
6547
|
+
this.lock = new SharedLock(storageKey, {storage: this.storage});
|
|
6548
|
+
|
|
6549
|
+
this.usePersistence = options.usePersistence;
|
|
6550
|
+
this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
|
|
6551
|
+
|
|
6552
|
+
this.memQueue = [];
|
|
6553
|
+
};
|
|
6554
|
+
|
|
6555
|
+
/**
|
|
6556
|
+
* Add one item to queues (memory and localStorage). The queued entry includes
|
|
6557
|
+
* the given item along with an auto-generated ID and a "flush-after" timestamp.
|
|
6558
|
+
* It is expected that the item will be sent over the network and dequeued
|
|
6559
|
+
* before the flush-after time; if this doesn't happen it is considered orphaned
|
|
6560
|
+
* (e.g., the original tab where it was enqueued got closed before it could be
|
|
6561
|
+
* sent) and the item can be sent by any tab that finds it in localStorage.
|
|
6562
|
+
*
|
|
6563
|
+
* The final callback param is called with a param indicating success or
|
|
6564
|
+
* failure of the enqueue operation; it is asynchronous because the localStorage
|
|
6565
|
+
* lock is asynchronous.
|
|
6566
|
+
*/
|
|
6567
|
+
RequestQueue.prototype.enqueue = function(item, flushInterval, cb) {
|
|
6568
|
+
var queueEntry = {
|
|
6569
|
+
'id': cheap_guid(),
|
|
6570
|
+
'flushAfter': new Date().getTime() + flushInterval * 2,
|
|
6571
|
+
'payload': item
|
|
6572
|
+
};
|
|
6573
|
+
|
|
6574
|
+
if (!this.usePersistence) {
|
|
6575
|
+
this.memQueue.push(queueEntry);
|
|
6576
|
+
if (cb) {
|
|
6577
|
+
cb(true);
|
|
6578
|
+
}
|
|
6579
|
+
} else {
|
|
6580
|
+
this.lock.withLock(_.bind(function lockAcquired() {
|
|
6581
|
+
var succeeded;
|
|
6582
|
+
try {
|
|
6583
|
+
var storedQueue = this.readFromStorage();
|
|
6584
|
+
storedQueue.push(queueEntry);
|
|
6585
|
+
succeeded = this.saveToStorage(storedQueue);
|
|
6586
|
+
if (succeeded) {
|
|
6587
|
+
// only add to in-memory queue when storage succeeds
|
|
6588
|
+
this.memQueue.push(queueEntry);
|
|
6589
|
+
}
|
|
6590
|
+
} catch(err) {
|
|
6591
|
+
this.reportError('Error enqueueing item', item);
|
|
6592
|
+
succeeded = false;
|
|
6593
|
+
}
|
|
6594
|
+
if (cb) {
|
|
6595
|
+
cb(succeeded);
|
|
6596
|
+
}
|
|
6597
|
+
}, this), _.bind(function lockFailure(err) {
|
|
6598
|
+
this.reportError('Error acquiring storage lock', err);
|
|
6599
|
+
if (cb) {
|
|
6600
|
+
cb(false);
|
|
6601
|
+
}
|
|
6602
|
+
}, this), this.pid);
|
|
6603
|
+
}
|
|
6604
|
+
};
|
|
6605
|
+
|
|
6606
|
+
/**
|
|
6607
|
+
* Read out the given number of queue entries. If this.memQueue
|
|
6608
|
+
* has fewer than batchSize items, then look for "orphaned" items
|
|
6609
|
+
* in the persisted queue (items where the 'flushAfter' time has
|
|
6610
|
+
* already passed).
|
|
6611
|
+
*/
|
|
6612
|
+
RequestQueue.prototype.fillBatch = function(batchSize) {
|
|
6613
|
+
var batch = this.memQueue.slice(0, batchSize);
|
|
6614
|
+
if (this.usePersistence && batch.length < batchSize) {
|
|
6615
|
+
// don't need lock just to read events; localStorage is thread-safe
|
|
6616
|
+
// and the worst that could happen is a duplicate send of some
|
|
6617
|
+
// orphaned events, which will be deduplicated on the server side
|
|
6618
|
+
var storedQueue = this.readFromStorage();
|
|
6619
|
+
if (storedQueue.length) {
|
|
6620
|
+
// item IDs already in batch; don't duplicate out of storage
|
|
6621
|
+
var idsInBatch = {}; // poor man's Set
|
|
6622
|
+
_.each(batch, function(item) { idsInBatch[item['id']] = true; });
|
|
6623
|
+
|
|
6624
|
+
for (var i = 0; i < storedQueue.length; i++) {
|
|
6625
|
+
var item = storedQueue[i];
|
|
6626
|
+
if (new Date().getTime() > item['flushAfter'] && !idsInBatch[item['id']]) {
|
|
6627
|
+
item.orphaned = true;
|
|
6628
|
+
batch.push(item);
|
|
6629
|
+
if (batch.length >= batchSize) {
|
|
6630
|
+
break;
|
|
6631
|
+
}
|
|
6632
|
+
}
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
}
|
|
6636
|
+
return batch;
|
|
6637
|
+
};
|
|
6638
|
+
|
|
6639
|
+
/**
|
|
6640
|
+
* Remove items with matching 'id' from array (immutably)
|
|
6641
|
+
* also remove any item without a valid id (e.g., malformed
|
|
6642
|
+
* storage entries).
|
|
6643
|
+
*/
|
|
6644
|
+
var filterOutIDsAndInvalid = function(items, idSet) {
|
|
6645
|
+
var filteredItems = [];
|
|
6646
|
+
_.each(items, function(item) {
|
|
6647
|
+
if (item['id'] && !idSet[item['id']]) {
|
|
6648
|
+
filteredItems.push(item);
|
|
6649
|
+
}
|
|
6650
|
+
});
|
|
6651
|
+
return filteredItems;
|
|
6652
|
+
};
|
|
6653
|
+
|
|
6654
|
+
/**
|
|
6655
|
+
* Remove items with matching IDs from both in-memory queue
|
|
6656
|
+
* and persisted queue
|
|
6657
|
+
*/
|
|
6658
|
+
RequestQueue.prototype.removeItemsByID = function(ids, cb) {
|
|
6659
|
+
var idSet = {}; // poor man's Set
|
|
6660
|
+
_.each(ids, function(id) { idSet[id] = true; });
|
|
6661
|
+
|
|
6662
|
+
this.memQueue = filterOutIDsAndInvalid(this.memQueue, idSet);
|
|
6663
|
+
if (!this.usePersistence) {
|
|
6664
|
+
if (cb) {
|
|
6665
|
+
cb(true);
|
|
6666
|
+
}
|
|
6667
|
+
} else {
|
|
6668
|
+
var removeFromStorage = _.bind(function() {
|
|
6669
|
+
var succeeded;
|
|
6670
|
+
try {
|
|
6671
|
+
var storedQueue = this.readFromStorage();
|
|
6672
|
+
storedQueue = filterOutIDsAndInvalid(storedQueue, idSet);
|
|
6673
|
+
succeeded = this.saveToStorage(storedQueue);
|
|
6674
|
+
|
|
6675
|
+
// an extra check: did storage report success but somehow
|
|
6676
|
+
// the items are still there?
|
|
6677
|
+
if (succeeded) {
|
|
6678
|
+
storedQueue = this.readFromStorage();
|
|
6679
|
+
for (var i = 0; i < storedQueue.length; i++) {
|
|
6680
|
+
var item = storedQueue[i];
|
|
6681
|
+
if (item['id'] && !!idSet[item['id']]) {
|
|
6682
|
+
this.reportError('Item not removed from storage');
|
|
6683
|
+
return false;
|
|
6684
|
+
}
|
|
6685
|
+
}
|
|
6686
|
+
}
|
|
6687
|
+
} catch(err) {
|
|
6688
|
+
this.reportError('Error removing items', ids);
|
|
6689
|
+
succeeded = false;
|
|
6690
|
+
}
|
|
6691
|
+
return succeeded;
|
|
6692
|
+
}, this);
|
|
6693
|
+
|
|
6694
|
+
this.lock.withLock(function lockAcquired() {
|
|
6695
|
+
var succeeded = removeFromStorage();
|
|
6696
|
+
if (cb) {
|
|
6697
|
+
cb(succeeded);
|
|
6698
|
+
}
|
|
6699
|
+
}, _.bind(function lockFailure(err) {
|
|
6700
|
+
var succeeded = false;
|
|
6701
|
+
this.reportError('Error acquiring storage lock', err);
|
|
6702
|
+
if (!localStorageSupported(this.storage, true)) {
|
|
6703
|
+
// Looks like localStorage writes have stopped working sometime after
|
|
6704
|
+
// initialization (probably full), and so nobody can acquire locks
|
|
6705
|
+
// anymore. Consider it temporarily safe to remove items without the
|
|
6706
|
+
// lock, since nobody's writing successfully anyway.
|
|
6707
|
+
succeeded = removeFromStorage();
|
|
6708
|
+
if (!succeeded) {
|
|
6709
|
+
// OK, we couldn't even write out the smaller queue. Try clearing it
|
|
6710
|
+
// entirely.
|
|
6711
|
+
try {
|
|
6712
|
+
this.storage.removeItem(this.storageKey);
|
|
6713
|
+
} catch(err) {
|
|
6714
|
+
this.reportError('Error clearing queue', err);
|
|
6715
|
+
}
|
|
6716
|
+
}
|
|
6717
|
+
}
|
|
6718
|
+
if (cb) {
|
|
6719
|
+
cb(succeeded);
|
|
6720
|
+
}
|
|
6721
|
+
}, this), this.pid);
|
|
6722
|
+
}
|
|
6723
|
+
|
|
6724
|
+
};
|
|
6725
|
+
|
|
6726
|
+
// internal helper for RequestQueue.updatePayloads
|
|
6727
|
+
var updatePayloads = function(existingItems, itemsToUpdate) {
|
|
6728
|
+
var newItems = [];
|
|
6729
|
+
_.each(existingItems, function(item) {
|
|
6730
|
+
var id = item['id'];
|
|
6731
|
+
if (id in itemsToUpdate) {
|
|
6732
|
+
var newPayload = itemsToUpdate[id];
|
|
6733
|
+
if (newPayload !== null) {
|
|
6734
|
+
item['payload'] = newPayload;
|
|
6735
|
+
newItems.push(item);
|
|
6736
|
+
}
|
|
6737
|
+
} else {
|
|
6738
|
+
// no update
|
|
6739
|
+
newItems.push(item);
|
|
6740
|
+
}
|
|
6741
|
+
});
|
|
6742
|
+
return newItems;
|
|
6743
|
+
};
|
|
6744
|
+
|
|
6745
|
+
/**
|
|
6746
|
+
* Update payloads of given items in both in-memory queue and
|
|
6747
|
+
* persisted queue. Items set to null are removed from queues.
|
|
6748
|
+
*/
|
|
6749
|
+
RequestQueue.prototype.updatePayloads = function(itemsToUpdate, cb) {
|
|
6750
|
+
this.memQueue = updatePayloads(this.memQueue, itemsToUpdate);
|
|
6751
|
+
if (!this.usePersistence) {
|
|
6752
|
+
if (cb) {
|
|
6753
|
+
cb(true);
|
|
6754
|
+
}
|
|
6755
|
+
} else {
|
|
6756
|
+
this.lock.withLock(_.bind(function lockAcquired() {
|
|
6757
|
+
var succeeded;
|
|
6758
|
+
try {
|
|
6759
|
+
var storedQueue = this.readFromStorage();
|
|
6760
|
+
storedQueue = updatePayloads(storedQueue, itemsToUpdate);
|
|
6761
|
+
succeeded = this.saveToStorage(storedQueue);
|
|
6762
|
+
} catch(err) {
|
|
6763
|
+
this.reportError('Error updating items', itemsToUpdate);
|
|
6764
|
+
succeeded = false;
|
|
6765
|
+
}
|
|
6766
|
+
if (cb) {
|
|
6767
|
+
cb(succeeded);
|
|
6768
|
+
}
|
|
6769
|
+
}, this), _.bind(function lockFailure(err) {
|
|
6770
|
+
this.reportError('Error acquiring storage lock', err);
|
|
6771
|
+
if (cb) {
|
|
6772
|
+
cb(false);
|
|
6773
|
+
}
|
|
6774
|
+
}, this), this.pid);
|
|
6775
|
+
}
|
|
6776
|
+
|
|
6777
|
+
};
|
|
6778
|
+
|
|
6779
|
+
/**
|
|
6780
|
+
* Read and parse items array from localStorage entry, handling
|
|
6781
|
+
* malformed/missing data if necessary.
|
|
6782
|
+
*/
|
|
6783
|
+
RequestQueue.prototype.readFromStorage = function() {
|
|
6784
|
+
var storageEntry;
|
|
6785
|
+
try {
|
|
6786
|
+
storageEntry = this.storage.getItem(this.storageKey);
|
|
6787
|
+
if (storageEntry) {
|
|
6788
|
+
storageEntry = JSONParse(storageEntry);
|
|
6789
|
+
if (!_.isArray(storageEntry)) {
|
|
6790
|
+
this.reportError('Invalid storage entry:', storageEntry);
|
|
6791
|
+
storageEntry = null;
|
|
6792
|
+
}
|
|
6793
|
+
}
|
|
6794
|
+
} catch (err) {
|
|
6795
|
+
this.reportError('Error retrieving queue', err);
|
|
6796
|
+
storageEntry = null;
|
|
6797
|
+
}
|
|
6798
|
+
return storageEntry || [];
|
|
6799
|
+
};
|
|
6800
|
+
|
|
6801
|
+
/**
|
|
6802
|
+
* Serialize the given items array to localStorage.
|
|
6803
|
+
*/
|
|
6804
|
+
RequestQueue.prototype.saveToStorage = function(queue) {
|
|
6805
|
+
try {
|
|
6806
|
+
this.storage.setItem(this.storageKey, JSONStringify(queue));
|
|
6807
|
+
return true;
|
|
6808
|
+
} catch (err) {
|
|
6809
|
+
this.reportError('Error saving queue', err);
|
|
6810
|
+
return false;
|
|
6811
|
+
}
|
|
6812
|
+
};
|
|
6813
|
+
|
|
6814
|
+
/**
|
|
6815
|
+
* Clear out queues (memory and localStorage).
|
|
6816
|
+
*/
|
|
6817
|
+
RequestQueue.prototype.clear = function() {
|
|
6818
|
+
this.memQueue = [];
|
|
6819
|
+
|
|
6820
|
+
if (this.usePersistence) {
|
|
6821
|
+
this.storage.removeItem(this.storageKey);
|
|
6822
|
+
}
|
|
6823
|
+
};
|
|
6824
|
+
|
|
6825
|
+
// maximum interval between request retries after exponential backoff
|
|
6826
|
+
var MAX_RETRY_INTERVAL_MS = 10 * 60 * 1000; // 10 minutes
|
|
6827
|
+
|
|
6828
|
+
var logger$1 = console_with_prefix('batch');
|
|
6829
|
+
|
|
6830
|
+
/**
|
|
6831
|
+
* RequestBatcher: manages the queueing, flushing, retry etc of requests of one
|
|
6832
|
+
* type (events, people, groups).
|
|
6833
|
+
* Uses RequestQueue to manage the backing store.
|
|
6834
|
+
* @constructor
|
|
6835
|
+
*/
|
|
6836
|
+
var RequestBatcher = function(storageKey, options) {
|
|
6837
|
+
this.errorReporter = options.errorReporter;
|
|
6838
|
+
this.queue = new RequestQueue(storageKey, {
|
|
6839
|
+
errorReporter: _.bind(this.reportError, this),
|
|
6840
|
+
storage: options.storage,
|
|
6841
|
+
usePersistence: options.usePersistence
|
|
6842
|
+
});
|
|
6843
|
+
|
|
6844
|
+
this.libConfig = options.libConfig;
|
|
6845
|
+
this.sendRequest = options.sendRequestFunc;
|
|
6846
|
+
this.beforeSendHook = options.beforeSendHook;
|
|
6847
|
+
this.stopAllBatching = options.stopAllBatchingFunc;
|
|
6848
|
+
|
|
6849
|
+
// seed variable batch size + flush interval with configured values
|
|
6850
|
+
this.batchSize = this.libConfig['batch_size'];
|
|
6851
|
+
this.flushInterval = this.libConfig['batch_flush_interval_ms'];
|
|
6852
|
+
|
|
6853
|
+
this.stopped = !this.libConfig['batch_autostart'];
|
|
6854
|
+
this.consecutiveRemovalFailures = 0;
|
|
6855
|
+
|
|
6856
|
+
// extra client-side dedupe
|
|
6857
|
+
this.itemIdsSentSuccessfully = {};
|
|
6858
|
+
|
|
6859
|
+
// Make the flush occur at the interval specified by flushIntervalMs, default behavior will attempt consecutive flushes
|
|
6860
|
+
// as long as the queue is not empty. This is useful for high-frequency events like Session Replay where we might end up
|
|
6861
|
+
// in a request loop and get ratelimited by the server.
|
|
6862
|
+
this.flushOnlyOnInterval = options.flushOnlyOnInterval || false;
|
|
6863
|
+
};
|
|
6864
|
+
|
|
6865
|
+
/**
|
|
6866
|
+
* Add one item to queue.
|
|
6867
|
+
*/
|
|
6868
|
+
RequestBatcher.prototype.enqueue = function(item, cb) {
|
|
6869
|
+
this.queue.enqueue(item, this.flushInterval, cb);
|
|
6870
|
+
};
|
|
6871
|
+
|
|
6872
|
+
/**
|
|
6873
|
+
* Start flushing batches at the configured time interval. Must call
|
|
6874
|
+
* this method upon SDK init in order to send anything over the network.
|
|
6875
|
+
*/
|
|
6876
|
+
RequestBatcher.prototype.start = function() {
|
|
6877
|
+
this.stopped = false;
|
|
6878
|
+
this.consecutiveRemovalFailures = 0;
|
|
6879
|
+
this.flush();
|
|
6880
|
+
};
|
|
6881
|
+
|
|
6882
|
+
/**
|
|
6883
|
+
* Stop flushing batches. Can be restarted by calling start().
|
|
6884
|
+
*/
|
|
6885
|
+
RequestBatcher.prototype.stop = function() {
|
|
6886
|
+
this.stopped = true;
|
|
6887
|
+
if (this.timeoutID) {
|
|
6888
|
+
clearTimeout(this.timeoutID);
|
|
6889
|
+
this.timeoutID = null;
|
|
6890
|
+
}
|
|
6891
|
+
};
|
|
6892
|
+
|
|
6893
|
+
/**
|
|
6894
|
+
* Clear out queue.
|
|
6895
|
+
*/
|
|
6896
|
+
RequestBatcher.prototype.clear = function() {
|
|
6897
|
+
this.queue.clear();
|
|
6898
|
+
};
|
|
6899
|
+
|
|
6900
|
+
/**
|
|
6901
|
+
* Restore batch size configuration to whatever is set in the main SDK.
|
|
6902
|
+
*/
|
|
6903
|
+
RequestBatcher.prototype.resetBatchSize = function() {
|
|
6904
|
+
this.batchSize = this.libConfig['batch_size'];
|
|
6905
|
+
};
|
|
6906
|
+
|
|
6907
|
+
/**
|
|
6908
|
+
* Restore flush interval time configuration to whatever is set in the main SDK.
|
|
6909
|
+
*/
|
|
6910
|
+
RequestBatcher.prototype.resetFlush = function() {
|
|
6911
|
+
this.scheduleFlush(this.libConfig['batch_flush_interval_ms']);
|
|
6912
|
+
};
|
|
6913
|
+
|
|
6914
|
+
/**
|
|
6915
|
+
* Schedule the next flush in the given number of milliseconds.
|
|
6916
|
+
*/
|
|
6917
|
+
RequestBatcher.prototype.scheduleFlush = function(flushMS) {
|
|
6918
|
+
this.flushInterval = flushMS;
|
|
6919
|
+
if (!this.stopped) { // don't schedule anymore if batching has been stopped
|
|
6920
|
+
this.timeoutID = setTimeout(_.bind(this.flush, this), this.flushInterval);
|
|
6921
|
+
}
|
|
6922
|
+
};
|
|
6923
|
+
|
|
6924
|
+
/**
|
|
6925
|
+
* Flush one batch to network. Depending on success/failure modes, it will either
|
|
6926
|
+
* remove the batch from the queue or leave it in for retry, and schedule the next
|
|
6927
|
+
* flush. In cases of most network or API failures, it will back off exponentially
|
|
6928
|
+
* when retrying.
|
|
6929
|
+
* @param {Object} [options]
|
|
6930
|
+
* @param {boolean} [options.sendBeacon] - whether to send batch with
|
|
6931
|
+
* navigator.sendBeacon (only useful for sending batches before page unloads, as
|
|
6932
|
+
* sendBeacon offers no callbacks or status indications)
|
|
6933
|
+
*/
|
|
6934
|
+
RequestBatcher.prototype.flush = function(options) {
|
|
6935
|
+
try {
|
|
6936
|
+
|
|
6937
|
+
if (this.requestInProgress) {
|
|
6938
|
+
logger$1.log('Flush: Request already in progress');
|
|
6939
|
+
return;
|
|
6940
|
+
}
|
|
6941
|
+
|
|
6942
|
+
options = options || {};
|
|
6943
|
+
var timeoutMS = this.libConfig['batch_request_timeout_ms'];
|
|
6944
|
+
var startTime = new Date().getTime();
|
|
6945
|
+
var currentBatchSize = this.batchSize;
|
|
6946
|
+
var batch = this.queue.fillBatch(currentBatchSize);
|
|
6947
|
+
// if there's more items in the queue than the batch size, attempt
|
|
6948
|
+
// to flush again after the current batch is done.
|
|
6949
|
+
var attemptSecondaryFlush = batch.length === currentBatchSize;
|
|
6950
|
+
var dataForRequest = [];
|
|
6951
|
+
var transformedItems = {};
|
|
6952
|
+
_.each(batch, function(item) {
|
|
6953
|
+
var payload = item['payload'];
|
|
6954
|
+
if (this.beforeSendHook && !item.orphaned) {
|
|
6955
|
+
payload = this.beforeSendHook(payload);
|
|
6956
|
+
}
|
|
6957
|
+
if (payload) {
|
|
6958
|
+
// mp_sent_by_lib_version prop captures which lib version actually
|
|
6959
|
+
// sends each event (regardless of which version originally queued
|
|
6960
|
+
// it for sending)
|
|
6961
|
+
if (payload['event'] && payload['properties']) {
|
|
6962
|
+
payload['properties'] = _.extend(
|
|
6963
|
+
{},
|
|
6964
|
+
payload['properties'],
|
|
6965
|
+
{'mp_sent_by_lib_version': Config.LIB_VERSION}
|
|
6966
|
+
);
|
|
6967
|
+
}
|
|
6968
|
+
var addPayload = true;
|
|
6969
|
+
var itemId = item['id'];
|
|
6970
|
+
if (itemId) {
|
|
6971
|
+
if ((this.itemIdsSentSuccessfully[itemId] || 0) > 5) {
|
|
6972
|
+
this.reportError('[dupe] item ID sent too many times, not sending', {
|
|
6973
|
+
item: item,
|
|
6974
|
+
batchSize: batch.length,
|
|
6975
|
+
timesSent: this.itemIdsSentSuccessfully[itemId]
|
|
6976
|
+
});
|
|
6977
|
+
addPayload = false;
|
|
6978
|
+
}
|
|
6979
|
+
} else {
|
|
6980
|
+
this.reportError('[dupe] found item with no ID', {item: item});
|
|
6981
|
+
}
|
|
6982
|
+
|
|
6983
|
+
if (addPayload) {
|
|
6984
|
+
dataForRequest.push(payload);
|
|
6985
|
+
}
|
|
6986
|
+
}
|
|
6987
|
+
transformedItems[item['id']] = payload;
|
|
6988
|
+
}, this);
|
|
6989
|
+
if (dataForRequest.length < 1) {
|
|
6990
|
+
this.resetFlush();
|
|
6991
|
+
return; // nothing to do
|
|
6992
|
+
}
|
|
6993
|
+
|
|
6994
|
+
this.requestInProgress = true;
|
|
6995
|
+
|
|
6996
|
+
var batchSendCallback = _.bind(function(res) {
|
|
6997
|
+
this.requestInProgress = false;
|
|
6998
|
+
|
|
6999
|
+
try {
|
|
7000
|
+
|
|
7001
|
+
// handle API response in a try-catch to make sure we can reset the
|
|
7002
|
+
// flush operation if something goes wrong
|
|
7003
|
+
|
|
7004
|
+
var removeItemsFromQueue = false;
|
|
7005
|
+
if (options.unloading) {
|
|
7006
|
+
// update persisted data to include hook transformations
|
|
7007
|
+
this.queue.updatePayloads(transformedItems);
|
|
7008
|
+
} else if (
|
|
7009
|
+
_.isObject(res) &&
|
|
7010
|
+
res.error === 'timeout' &&
|
|
7011
|
+
new Date().getTime() - startTime >= timeoutMS
|
|
7012
|
+
) {
|
|
7013
|
+
this.reportError('Network timeout; retrying');
|
|
7014
|
+
this.flush();
|
|
7015
|
+
} else if (
|
|
7016
|
+
_.isObject(res) &&
|
|
7017
|
+
(res.httpStatusCode >= 500 || res.httpStatusCode === 429 || res.error === 'timeout')
|
|
7018
|
+
) {
|
|
7019
|
+
// network or API error, or 429 Too Many Requests, retry
|
|
7020
|
+
var retryMS = this.flushInterval * 2;
|
|
7021
|
+
if (res.retryAfter) {
|
|
7022
|
+
retryMS = (parseInt(res.retryAfter, 10) * 1000) || retryMS;
|
|
7023
|
+
}
|
|
7024
|
+
retryMS = Math.min(MAX_RETRY_INTERVAL_MS, retryMS);
|
|
7025
|
+
this.reportError('Error; retry in ' + retryMS + ' ms');
|
|
7026
|
+
this.scheduleFlush(retryMS);
|
|
7027
|
+
} else if (_.isObject(res) && res.httpStatusCode === 413) {
|
|
7028
|
+
// 413 Payload Too Large
|
|
7029
|
+
if (batch.length > 1) {
|
|
7030
|
+
var halvedBatchSize = Math.max(1, Math.floor(currentBatchSize / 2));
|
|
7031
|
+
this.batchSize = Math.min(this.batchSize, halvedBatchSize, batch.length - 1);
|
|
7032
|
+
this.reportError('413 response; reducing batch size to ' + this.batchSize);
|
|
7033
|
+
this.resetFlush();
|
|
7034
|
+
} else {
|
|
7035
|
+
this.reportError('Single-event request too large; dropping', batch);
|
|
7036
|
+
this.resetBatchSize();
|
|
7037
|
+
removeItemsFromQueue = true;
|
|
7038
|
+
}
|
|
7039
|
+
} else {
|
|
7040
|
+
// successful network request+response; remove each item in batch from queue
|
|
7041
|
+
// (even if it was e.g. a 400, in which case retrying won't help)
|
|
7042
|
+
removeItemsFromQueue = true;
|
|
7043
|
+
}
|
|
7044
|
+
|
|
7045
|
+
if (removeItemsFromQueue) {
|
|
7046
|
+
this.queue.removeItemsByID(
|
|
7047
|
+
_.map(batch, function(item) { return item['id']; }),
|
|
7048
|
+
_.bind(function(succeeded) {
|
|
7049
|
+
if (succeeded) {
|
|
7050
|
+
this.consecutiveRemovalFailures = 0;
|
|
7051
|
+
if (this.flushOnlyOnInterval && !attemptSecondaryFlush) {
|
|
7052
|
+
this.resetFlush(); // schedule next batch with a delay
|
|
7053
|
+
} else {
|
|
7054
|
+
this.flush(); // handle next batch if the queue isn't empty
|
|
7055
|
+
}
|
|
7056
|
+
} else {
|
|
7057
|
+
this.reportError('Failed to remove items from queue');
|
|
7058
|
+
if (++this.consecutiveRemovalFailures > 5) {
|
|
7059
|
+
this.reportError('Too many queue failures; disabling batching system.');
|
|
7060
|
+
this.stopAllBatching();
|
|
7061
|
+
} else {
|
|
7062
|
+
this.resetFlush();
|
|
7063
|
+
}
|
|
7064
|
+
}
|
|
7065
|
+
}, this)
|
|
7066
|
+
);
|
|
7067
|
+
|
|
7068
|
+
// client-side dedupe
|
|
7069
|
+
_.each(batch, _.bind(function(item) {
|
|
7070
|
+
var itemId = item['id'];
|
|
7071
|
+
if (itemId) {
|
|
7072
|
+
this.itemIdsSentSuccessfully[itemId] = this.itemIdsSentSuccessfully[itemId] || 0;
|
|
7073
|
+
this.itemIdsSentSuccessfully[itemId]++;
|
|
7074
|
+
if (this.itemIdsSentSuccessfully[itemId] > 5) {
|
|
7075
|
+
this.reportError('[dupe] item ID sent too many times', {
|
|
7076
|
+
item: item,
|
|
7077
|
+
batchSize: batch.length,
|
|
7078
|
+
timesSent: this.itemIdsSentSuccessfully[itemId]
|
|
7079
|
+
});
|
|
7080
|
+
}
|
|
7081
|
+
} else {
|
|
7082
|
+
this.reportError('[dupe] found item with no ID while removing', {item: item});
|
|
7083
|
+
}
|
|
7084
|
+
}, this));
|
|
7085
|
+
}
|
|
7086
|
+
|
|
7087
|
+
} catch(err) {
|
|
7088
|
+
this.reportError('Error handling API response', err);
|
|
7089
|
+
this.resetFlush();
|
|
7090
|
+
}
|
|
7091
|
+
}, this);
|
|
7092
|
+
var requestOptions = {
|
|
7093
|
+
method: 'POST',
|
|
7094
|
+
verbose: true,
|
|
7095
|
+
ignore_json_errors: true, // eslint-disable-line camelcase
|
|
7096
|
+
timeout_ms: timeoutMS // eslint-disable-line camelcase
|
|
7097
|
+
};
|
|
7098
|
+
if (options.unloading) {
|
|
7099
|
+
requestOptions.transport = 'sendBeacon';
|
|
7100
|
+
}
|
|
7101
|
+
logger$1.log('MIXPANEL REQUEST:', dataForRequest);
|
|
7102
|
+
this.sendRequest(dataForRequest, requestOptions, batchSendCallback);
|
|
7103
|
+
} catch(err) {
|
|
7104
|
+
this.reportError('Error flushing request queue', err);
|
|
7105
|
+
this.resetFlush();
|
|
7106
|
+
}
|
|
7107
|
+
};
|
|
7108
|
+
|
|
7109
|
+
/**
|
|
7110
|
+
* Log error to global logger and optional user-defined logger.
|
|
7111
|
+
*/
|
|
7112
|
+
RequestBatcher.prototype.reportError = function(msg, err) {
|
|
7113
|
+
logger$1.error.apply(logger$1.error, arguments);
|
|
7114
|
+
if (this.errorReporter) {
|
|
7115
|
+
try {
|
|
7116
|
+
if (!(err instanceof Error)) {
|
|
7117
|
+
err = new Error(msg);
|
|
7118
|
+
}
|
|
7119
|
+
this.errorReporter(msg, err);
|
|
7120
|
+
} catch(err) {
|
|
7121
|
+
logger$1.error(err);
|
|
7122
|
+
}
|
|
7123
|
+
}
|
|
7124
|
+
};
|
|
7125
|
+
|
|
6347
7126
|
var logger = console_with_prefix('recorder');
|
|
6348
|
-
var CompressionStream =
|
|
7127
|
+
var CompressionStream = win['CompressionStream'];
|
|
7128
|
+
|
|
7129
|
+
var RECORDER_BATCHER_LIB_CONFIG = {
|
|
7130
|
+
'batch_size': 1000,
|
|
7131
|
+
'batch_flush_interval_ms': 10 * 1000,
|
|
7132
|
+
'batch_request_timeout_ms': 90 * 1000,
|
|
7133
|
+
'batch_autostart': true
|
|
7134
|
+
};
|
|
7135
|
+
|
|
7136
|
+
var ACTIVE_SOURCES = new Set([
|
|
7137
|
+
IncrementalSource.MouseMove,
|
|
7138
|
+
IncrementalSource.MouseInteraction,
|
|
7139
|
+
IncrementalSource.Scroll,
|
|
7140
|
+
IncrementalSource.ViewportResize,
|
|
7141
|
+
IncrementalSource.Input,
|
|
7142
|
+
IncrementalSource.TouchMove,
|
|
7143
|
+
IncrementalSource.MediaInteraction,
|
|
7144
|
+
IncrementalSource.Drag,
|
|
7145
|
+
IncrementalSource.Selection,
|
|
7146
|
+
]);
|
|
7147
|
+
|
|
7148
|
+
function isUserEvent(ev) {
|
|
7149
|
+
return ev.type === EventType.IncrementalSnapshot && ACTIVE_SOURCES.has(ev.source);
|
|
7150
|
+
}
|
|
6349
7151
|
|
|
6350
7152
|
var MixpanelRecorder = function(mixpanelInstance) {
|
|
6351
7153
|
this._mixpanel = mixpanelInstance;
|
|
@@ -6357,14 +7159,24 @@
|
|
|
6357
7159
|
this.seqNo = 0;
|
|
6358
7160
|
this.replayId = null;
|
|
6359
7161
|
this.replayStartTime = null;
|
|
6360
|
-
this.batchStartTime = null;
|
|
6361
|
-
this.replayLengthMs = 0;
|
|
6362
7162
|
this.sendBatchId = null;
|
|
6363
7163
|
|
|
6364
7164
|
this.idleTimeoutId = null;
|
|
6365
7165
|
this.maxTimeoutId = null;
|
|
6366
7166
|
|
|
6367
7167
|
this.recordMaxMs = MAX_RECORDING_MS;
|
|
7168
|
+
this._initBatcher();
|
|
7169
|
+
};
|
|
7170
|
+
|
|
7171
|
+
|
|
7172
|
+
MixpanelRecorder.prototype._initBatcher = function () {
|
|
7173
|
+
this.batcher = new RequestBatcher('__mprec', {
|
|
7174
|
+
libConfig: RECORDER_BATCHER_LIB_CONFIG,
|
|
7175
|
+
sendRequestFunc: _.bind(this.flushEventsWithOptOut, this),
|
|
7176
|
+
errorReporter: _.bind(this.reportError, this),
|
|
7177
|
+
flushOnlyOnInterval: true,
|
|
7178
|
+
usePersistence: false
|
|
7179
|
+
});
|
|
6368
7180
|
};
|
|
6369
7181
|
|
|
6370
7182
|
// eslint-disable-next-line camelcase
|
|
@@ -6386,12 +7198,11 @@
|
|
|
6386
7198
|
|
|
6387
7199
|
this.recEvents = [];
|
|
6388
7200
|
this.seqNo = 0;
|
|
6389
|
-
this.
|
|
6390
|
-
this.replayStartTime = this.startDate.getTime();
|
|
6391
|
-
this.batchStartTime = this.replayStartTime;
|
|
7201
|
+
this.replayStartTime = null;
|
|
6392
7202
|
|
|
6393
7203
|
this.replayId = _.UUID();
|
|
6394
|
-
|
|
7204
|
+
|
|
7205
|
+
this.batcher.start();
|
|
6395
7206
|
|
|
6396
7207
|
var resetIdleTimeout = _.bind(function () {
|
|
6397
7208
|
clearTimeout(this.idleTimeoutId);
|
|
@@ -6403,20 +7214,22 @@
|
|
|
6403
7214
|
|
|
6404
7215
|
this._stopRecording = record({
|
|
6405
7216
|
'emit': _.bind(function (ev) {
|
|
6406
|
-
this.
|
|
6407
|
-
|
|
6408
|
-
|
|
7217
|
+
this.batcher.enqueue(ev);
|
|
7218
|
+
if (isUserEvent(ev)) {
|
|
7219
|
+
resetIdleTimeout();
|
|
7220
|
+
}
|
|
6409
7221
|
}, this),
|
|
6410
|
-
'
|
|
6411
|
-
'maskTextSelector': this.get_config('record_mask_text_selector'),
|
|
7222
|
+
'blockClass': this.get_config('record_block_class'),
|
|
6412
7223
|
'blockSelector': this.get_config('record_block_selector'),
|
|
7224
|
+
'collectFonts': this.get_config('record_collect_fonts'),
|
|
7225
|
+
'inlineImages': this.get_config('record_inline_images'),
|
|
7226
|
+
'maskAllInputs': true,
|
|
6413
7227
|
'maskTextClass': this.get_config('record_mask_text_class'),
|
|
6414
|
-
'
|
|
7228
|
+
'maskTextSelector': this.get_config('record_mask_text_selector')
|
|
6415
7229
|
});
|
|
6416
7230
|
|
|
6417
7231
|
resetIdleTimeout();
|
|
6418
7232
|
|
|
6419
|
-
this.sendBatchId = setInterval(_.bind(this.flushEventsWithOptOut, this), 10000);
|
|
6420
7233
|
this.maxTimeoutId = setTimeout(_.bind(this.resetRecording, this), this.recordMaxMs);
|
|
6421
7234
|
};
|
|
6422
7235
|
|
|
@@ -6431,10 +7244,9 @@
|
|
|
6431
7244
|
this._stopRecording = null;
|
|
6432
7245
|
}
|
|
6433
7246
|
|
|
6434
|
-
this.
|
|
7247
|
+
this.batcher.flush(); // flush any remaining events
|
|
6435
7248
|
this.replayId = null;
|
|
6436
7249
|
|
|
6437
|
-
clearInterval(this.sendBatchId);
|
|
6438
7250
|
clearTimeout(this.idleTimeoutId);
|
|
6439
7251
|
clearTimeout(this.maxTimeoutId);
|
|
6440
7252
|
};
|
|
@@ -6443,8 +7255,8 @@
|
|
|
6443
7255
|
* Flushes the current batch of events to the server, but passes an opt-out callback to make sure
|
|
6444
7256
|
* we stop recording and dump any queued events if the user has opted out.
|
|
6445
7257
|
*/
|
|
6446
|
-
MixpanelRecorder.prototype.flushEventsWithOptOut = function () {
|
|
6447
|
-
this._flushEvents(_.bind(this._onOptOut, this));
|
|
7258
|
+
MixpanelRecorder.prototype.flushEventsWithOptOut = function (data, options, cb) {
|
|
7259
|
+
this._flushEvents(data, options, cb, _.bind(this._onOptOut, this));
|
|
6448
7260
|
};
|
|
6449
7261
|
|
|
6450
7262
|
MixpanelRecorder.prototype._onOptOut = function (code) {
|
|
@@ -6455,33 +7267,60 @@
|
|
|
6455
7267
|
}
|
|
6456
7268
|
};
|
|
6457
7269
|
|
|
6458
|
-
MixpanelRecorder.prototype._sendRequest = function(reqParams, reqBody) {
|
|
6459
|
-
|
|
7270
|
+
MixpanelRecorder.prototype._sendRequest = function(reqParams, reqBody, callback) {
|
|
7271
|
+
var onSuccess = _.bind(function (response, responseBody) {
|
|
7272
|
+
// Increment sequence counter only if the request was successful to guarantee ordering.
|
|
7273
|
+
// RequestBatcher will always flush the next batch after the previous one succeeds.
|
|
7274
|
+
if (response.status === 200) {
|
|
7275
|
+
this.seqNo++;
|
|
7276
|
+
}
|
|
7277
|
+
|
|
7278
|
+
callback({
|
|
7279
|
+
status: 0,
|
|
7280
|
+
httpStatusCode: response.status,
|
|
7281
|
+
responseBody: responseBody,
|
|
7282
|
+
retryAfter: response.headers.get('Retry-After')
|
|
7283
|
+
});
|
|
7284
|
+
}, this);
|
|
7285
|
+
|
|
7286
|
+
win['fetch'](this.get_config('api_host') + '/' + this.get_config('api_routes')['record'] + '?' + new URLSearchParams(reqParams), {
|
|
6460
7287
|
'method': 'POST',
|
|
6461
7288
|
'headers': {
|
|
6462
7289
|
'Authorization': 'Basic ' + btoa(this.get_config('token') + ':'),
|
|
6463
7290
|
'Content-Type': 'application/octet-stream'
|
|
6464
7291
|
},
|
|
6465
|
-
'body': reqBody
|
|
7292
|
+
'body': reqBody,
|
|
7293
|
+
}).then(function (response) {
|
|
7294
|
+
response.json().then(function (responseBody) {
|
|
7295
|
+
onSuccess(response, responseBody);
|
|
7296
|
+
}).catch(function (error) {
|
|
7297
|
+
callback({error: error});
|
|
7298
|
+
});
|
|
7299
|
+
}).catch(function (error) {
|
|
7300
|
+
callback({error: error});
|
|
6466
7301
|
});
|
|
6467
7302
|
};
|
|
6468
7303
|
|
|
6469
|
-
|
|
6470
|
-
|
|
6471
|
-
|
|
6472
|
-
*/
|
|
6473
|
-
MixpanelRecorder.prototype._flushEvents = addOptOutCheckMixpanelLib(function() {
|
|
6474
|
-
var numEvents = this.recEvents.length;
|
|
7304
|
+
MixpanelRecorder.prototype._flushEvents = addOptOutCheckMixpanelLib(function (data, options, callback) {
|
|
7305
|
+
const numEvents = data.length;
|
|
7306
|
+
|
|
6475
7307
|
if (numEvents > 0) {
|
|
7308
|
+
// each rrweb event has a timestamp - leverage those to get time properties
|
|
7309
|
+
var batchStartTime = data[0].timestamp;
|
|
7310
|
+
if (this.seqNo === 0) {
|
|
7311
|
+
this.replayStartTime = batchStartTime;
|
|
7312
|
+
}
|
|
7313
|
+
var replayLengthMs = data[numEvents - 1].timestamp - this.replayStartTime;
|
|
7314
|
+
|
|
6476
7315
|
var reqParams = {
|
|
6477
7316
|
'distinct_id': String(this._mixpanel.get_distinct_id()),
|
|
6478
|
-
'seq': this.seqNo
|
|
6479
|
-
'batch_start_time':
|
|
7317
|
+
'seq': this.seqNo,
|
|
7318
|
+
'batch_start_time': batchStartTime / 1000,
|
|
6480
7319
|
'replay_id': this.replayId,
|
|
6481
|
-
'replay_length_ms':
|
|
7320
|
+
'replay_length_ms': replayLengthMs,
|
|
6482
7321
|
'replay_start_time': this.replayStartTime / 1000
|
|
6483
7322
|
};
|
|
6484
|
-
var eventsJson = _.JSONEncode(
|
|
7323
|
+
var eventsJson = _.JSONEncode(data);
|
|
6485
7324
|
|
|
6486
7325
|
// send ID management props if they exist
|
|
6487
7326
|
var deviceId = this._mixpanel.get_property('$device_id');
|
|
@@ -6493,8 +7332,6 @@
|
|
|
6493
7332
|
reqParams['$user_id'] = userId;
|
|
6494
7333
|
}
|
|
6495
7334
|
|
|
6496
|
-
this.recEvents = this.recEvents.slice(numEvents);
|
|
6497
|
-
this.batchStartTime = new Date().getTime();
|
|
6498
7335
|
if (CompressionStream) {
|
|
6499
7336
|
var jsonStream = new Blob([eventsJson], {type: 'application/json'}).stream();
|
|
6500
7337
|
var gzipStream = jsonStream.pipeThrough(new CompressionStream('gzip'));
|
|
@@ -6502,15 +7339,29 @@
|
|
|
6502
7339
|
.blob()
|
|
6503
7340
|
.then(_.bind(function(compressedBlob) {
|
|
6504
7341
|
reqParams['format'] = 'gzip';
|
|
6505
|
-
this._sendRequest(reqParams, compressedBlob);
|
|
7342
|
+
this._sendRequest(reqParams, compressedBlob, callback);
|
|
6506
7343
|
}, this));
|
|
6507
7344
|
} else {
|
|
6508
7345
|
reqParams['format'] = 'body';
|
|
6509
|
-
this._sendRequest(reqParams, eventsJson);
|
|
7346
|
+
this._sendRequest(reqParams, eventsJson, callback);
|
|
6510
7347
|
}
|
|
6511
7348
|
}
|
|
6512
7349
|
});
|
|
6513
7350
|
|
|
6514
|
-
|
|
7351
|
+
|
|
7352
|
+
MixpanelRecorder.prototype.reportError = function(msg, err) {
|
|
7353
|
+
logger.error.apply(logger.error, arguments);
|
|
7354
|
+
try {
|
|
7355
|
+
if (!err && !(msg instanceof Error)) {
|
|
7356
|
+
msg = new Error(msg);
|
|
7357
|
+
}
|
|
7358
|
+
this.get_config('error_reporter')(msg, err);
|
|
7359
|
+
} catch(err) {
|
|
7360
|
+
logger.error(err);
|
|
7361
|
+
}
|
|
7362
|
+
};
|
|
7363
|
+
|
|
7364
|
+
|
|
7365
|
+
win['__mp_recorder'] = MixpanelRecorder;
|
|
6515
7366
|
|
|
6516
7367
|
})();
|