mixpanel-browser 2.56.0 → 2.57.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -0
- package/LICENSE +1 -15
- package/build.sh +3 -3
- package/dist/mixpanel-core.cjs.js +898 -410
- package/dist/mixpanel-recorder.js +897 -409
- package/dist/mixpanel-recorder.min.js +10 -10
- package/dist/mixpanel-recorder.min.js.map +1 -1
- package/dist/mixpanel-with-async-recorder.cjs.js +898 -410
- package/dist/mixpanel.amd.js +898 -410
- package/dist/mixpanel.cjs.js +898 -410
- package/dist/mixpanel.globals.js +898 -410
- package/dist/mixpanel.min.js +122 -112
- package/dist/mixpanel.module.js +898 -410
- package/dist/mixpanel.umd.js +898 -410
- package/package.json +1 -1
- package/src/config.js +1 -1
- package/src/externs.js +14 -0
- package/src/gdpr-utils.js +2 -1
- package/src/mixpanel-core.js +3 -2
- package/src/promise-polyfill.js +379 -0
- package/src/recorder/index.js +2 -1
- package/src/recorder/session-recording.js +2 -1
- package/src/request-batcher.js +185 -164
- package/src/request-queue.js +200 -147
- package/src/shared-lock.js +104 -98
- package/src/storage/local-storage.js +53 -0
- package/src/storage/wrapper.js +14 -0
- package/src/utils.js +16 -33
- package/src/window.js +20 -0
- package/dist/mixpanel.min.js.map +0 -8
package/dist/mixpanel.cjs.js
CHANGED
|
@@ -4509,11 +4509,9 @@ var IncrementalSource = /* @__PURE__ */ ((IncrementalSource2) => {
|
|
|
4509
4509
|
|
|
4510
4510
|
var Config = {
|
|
4511
4511
|
DEBUG: false,
|
|
4512
|
-
LIB_VERSION: '2.
|
|
4512
|
+
LIB_VERSION: '2.57.1'
|
|
4513
4513
|
};
|
|
4514
4514
|
|
|
4515
|
-
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
4516
|
-
|
|
4517
4515
|
// since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
|
|
4518
4516
|
var win;
|
|
4519
4517
|
if (typeof(window) === 'undefined') {
|
|
@@ -4533,6 +4531,370 @@ if (typeof(window) === 'undefined') {
|
|
|
4533
4531
|
win = window;
|
|
4534
4532
|
}
|
|
4535
4533
|
|
|
4534
|
+
var setImmediate = win['setImmediate'];
|
|
4535
|
+
var builtInProp, cycle, schedulingQueue,
|
|
4536
|
+
ToString = Object.prototype.toString,
|
|
4537
|
+
timer = (typeof setImmediate !== 'undefined') ?
|
|
4538
|
+
function timer(fn) { return setImmediate(fn); } :
|
|
4539
|
+
setTimeout;
|
|
4540
|
+
|
|
4541
|
+
// dammit, IE8.
|
|
4542
|
+
try {
|
|
4543
|
+
Object.defineProperty({},'x',{});
|
|
4544
|
+
builtInProp = function builtInProp(obj,name,val,config) {
|
|
4545
|
+
return Object.defineProperty(obj,name,{
|
|
4546
|
+
value: val,
|
|
4547
|
+
writable: true,
|
|
4548
|
+
configurable: config !== false
|
|
4549
|
+
});
|
|
4550
|
+
};
|
|
4551
|
+
}
|
|
4552
|
+
catch (err) {
|
|
4553
|
+
builtInProp = function builtInProp(obj,name,val) {
|
|
4554
|
+
obj[name] = val;
|
|
4555
|
+
return obj;
|
|
4556
|
+
};
|
|
4557
|
+
}
|
|
4558
|
+
|
|
4559
|
+
// Note: using a queue instead of array for efficiency
|
|
4560
|
+
schedulingQueue = (function Queue() {
|
|
4561
|
+
var first, last, item;
|
|
4562
|
+
|
|
4563
|
+
function Item(fn,self) {
|
|
4564
|
+
this.fn = fn;
|
|
4565
|
+
this.self = self;
|
|
4566
|
+
this.next = void 0;
|
|
4567
|
+
}
|
|
4568
|
+
|
|
4569
|
+
return {
|
|
4570
|
+
add: function add(fn,self) {
|
|
4571
|
+
item = new Item(fn,self);
|
|
4572
|
+
if (last) {
|
|
4573
|
+
last.next = item;
|
|
4574
|
+
}
|
|
4575
|
+
else {
|
|
4576
|
+
first = item;
|
|
4577
|
+
}
|
|
4578
|
+
last = item;
|
|
4579
|
+
item = void 0;
|
|
4580
|
+
},
|
|
4581
|
+
drain: function drain() {
|
|
4582
|
+
var f = first;
|
|
4583
|
+
first = last = cycle = void 0;
|
|
4584
|
+
|
|
4585
|
+
while (f) {
|
|
4586
|
+
f.fn.call(f.self);
|
|
4587
|
+
f = f.next;
|
|
4588
|
+
}
|
|
4589
|
+
}
|
|
4590
|
+
};
|
|
4591
|
+
})();
|
|
4592
|
+
|
|
4593
|
+
function schedule(fn,self) {
|
|
4594
|
+
schedulingQueue.add(fn,self);
|
|
4595
|
+
if (!cycle) {
|
|
4596
|
+
cycle = timer(schedulingQueue.drain);
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
|
|
4600
|
+
// promise duck typing
|
|
4601
|
+
function isThenable(o) {
|
|
4602
|
+
var _then, oType = typeof o;
|
|
4603
|
+
|
|
4604
|
+
if (o !== null && (oType === 'object' || oType === 'function')) {
|
|
4605
|
+
_then = o.then;
|
|
4606
|
+
}
|
|
4607
|
+
return typeof _then === 'function' ? _then : false;
|
|
4608
|
+
}
|
|
4609
|
+
|
|
4610
|
+
function notify() {
|
|
4611
|
+
for (var i=0; i<this.chain.length; i++) {
|
|
4612
|
+
notifyIsolated(
|
|
4613
|
+
this,
|
|
4614
|
+
(this.state === 1) ? this.chain[i].success : this.chain[i].failure,
|
|
4615
|
+
this.chain[i]
|
|
4616
|
+
);
|
|
4617
|
+
}
|
|
4618
|
+
this.chain.length = 0;
|
|
4619
|
+
}
|
|
4620
|
+
|
|
4621
|
+
// NOTE: This is a separate function to isolate
|
|
4622
|
+
// the `try..catch` so that other code can be
|
|
4623
|
+
// optimized better
|
|
4624
|
+
function notifyIsolated(self,cb,chain) {
|
|
4625
|
+
var ret, _then;
|
|
4626
|
+
try {
|
|
4627
|
+
if (cb === false) {
|
|
4628
|
+
chain.reject(self.msg);
|
|
4629
|
+
}
|
|
4630
|
+
else {
|
|
4631
|
+
if (cb === true) {
|
|
4632
|
+
ret = self.msg;
|
|
4633
|
+
}
|
|
4634
|
+
else {
|
|
4635
|
+
ret = cb.call(void 0,self.msg);
|
|
4636
|
+
}
|
|
4637
|
+
|
|
4638
|
+
if (ret === chain.promise) {
|
|
4639
|
+
chain.reject(TypeError('Promise-chain cycle'));
|
|
4640
|
+
}
|
|
4641
|
+
// eslint-disable-next-line no-cond-assign
|
|
4642
|
+
else if (_then = isThenable(ret)) {
|
|
4643
|
+
_then.call(ret,chain.resolve,chain.reject);
|
|
4644
|
+
}
|
|
4645
|
+
else {
|
|
4646
|
+
chain.resolve(ret);
|
|
4647
|
+
}
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
catch (err) {
|
|
4651
|
+
chain.reject(err);
|
|
4652
|
+
}
|
|
4653
|
+
}
|
|
4654
|
+
|
|
4655
|
+
function resolve(msg) {
|
|
4656
|
+
var _then, self = this;
|
|
4657
|
+
|
|
4658
|
+
// already triggered?
|
|
4659
|
+
if (self.triggered) { return; }
|
|
4660
|
+
|
|
4661
|
+
self.triggered = true;
|
|
4662
|
+
|
|
4663
|
+
// unwrap
|
|
4664
|
+
if (self.def) {
|
|
4665
|
+
self = self.def;
|
|
4666
|
+
}
|
|
4667
|
+
|
|
4668
|
+
try {
|
|
4669
|
+
// eslint-disable-next-line no-cond-assign
|
|
4670
|
+
if (_then = isThenable(msg)) {
|
|
4671
|
+
schedule(function(){
|
|
4672
|
+
var defWrapper = new MakeDefWrapper(self);
|
|
4673
|
+
try {
|
|
4674
|
+
_then.call(msg,
|
|
4675
|
+
function $resolve$(){ resolve.apply(defWrapper,arguments); },
|
|
4676
|
+
function $reject$(){ reject.apply(defWrapper,arguments); }
|
|
4677
|
+
);
|
|
4678
|
+
}
|
|
4679
|
+
catch (err) {
|
|
4680
|
+
reject.call(defWrapper,err);
|
|
4681
|
+
}
|
|
4682
|
+
});
|
|
4683
|
+
}
|
|
4684
|
+
else {
|
|
4685
|
+
self.msg = msg;
|
|
4686
|
+
self.state = 1;
|
|
4687
|
+
if (self.chain.length > 0) {
|
|
4688
|
+
schedule(notify,self);
|
|
4689
|
+
}
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
catch (err) {
|
|
4693
|
+
reject.call(new MakeDefWrapper(self),err);
|
|
4694
|
+
}
|
|
4695
|
+
}
|
|
4696
|
+
|
|
4697
|
+
function reject(msg) {
|
|
4698
|
+
var self = this;
|
|
4699
|
+
|
|
4700
|
+
// already triggered?
|
|
4701
|
+
if (self.triggered) { return; }
|
|
4702
|
+
|
|
4703
|
+
self.triggered = true;
|
|
4704
|
+
|
|
4705
|
+
// unwrap
|
|
4706
|
+
if (self.def) {
|
|
4707
|
+
self = self.def;
|
|
4708
|
+
}
|
|
4709
|
+
|
|
4710
|
+
self.msg = msg;
|
|
4711
|
+
self.state = 2;
|
|
4712
|
+
if (self.chain.length > 0) {
|
|
4713
|
+
schedule(notify,self);
|
|
4714
|
+
}
|
|
4715
|
+
}
|
|
4716
|
+
|
|
4717
|
+
function iteratePromises(Constructor,arr,resolver,rejecter) {
|
|
4718
|
+
for (var idx=0; idx<arr.length; idx++) {
|
|
4719
|
+
(function IIFE(idx){
|
|
4720
|
+
Constructor.resolve(arr[idx])
|
|
4721
|
+
.then(
|
|
4722
|
+
function $resolver$(msg){
|
|
4723
|
+
resolver(idx,msg);
|
|
4724
|
+
},
|
|
4725
|
+
rejecter
|
|
4726
|
+
);
|
|
4727
|
+
})(idx);
|
|
4728
|
+
}
|
|
4729
|
+
}
|
|
4730
|
+
|
|
4731
|
+
function MakeDefWrapper(self) {
|
|
4732
|
+
this.def = self;
|
|
4733
|
+
this.triggered = false;
|
|
4734
|
+
}
|
|
4735
|
+
|
|
4736
|
+
function MakeDef(self) {
|
|
4737
|
+
this.promise = self;
|
|
4738
|
+
this.state = 0;
|
|
4739
|
+
this.triggered = false;
|
|
4740
|
+
this.chain = [];
|
|
4741
|
+
this.msg = void 0;
|
|
4742
|
+
}
|
|
4743
|
+
|
|
4744
|
+
function NpoPromise(executor) {
|
|
4745
|
+
if (typeof executor !== 'function') {
|
|
4746
|
+
throw TypeError('Not a function');
|
|
4747
|
+
}
|
|
4748
|
+
|
|
4749
|
+
if (this['__NPO__'] !== 0) {
|
|
4750
|
+
throw TypeError('Not a promise');
|
|
4751
|
+
}
|
|
4752
|
+
|
|
4753
|
+
// instance shadowing the inherited "brand"
|
|
4754
|
+
// to signal an already "initialized" promise
|
|
4755
|
+
this['__NPO__'] = 1;
|
|
4756
|
+
|
|
4757
|
+
var def = new MakeDef(this);
|
|
4758
|
+
|
|
4759
|
+
this['then'] = function then(success,failure) {
|
|
4760
|
+
var o = {
|
|
4761
|
+
success: typeof success === 'function' ? success : true,
|
|
4762
|
+
failure: typeof failure === 'function' ? failure : false
|
|
4763
|
+
};
|
|
4764
|
+
// Note: `then(..)` itself can be borrowed to be used against
|
|
4765
|
+
// a different promise constructor for making the chained promise,
|
|
4766
|
+
// by substituting a different `this` binding.
|
|
4767
|
+
o.promise = new this.constructor(function extractChain(resolve,reject) {
|
|
4768
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
4769
|
+
throw TypeError('Not a function');
|
|
4770
|
+
}
|
|
4771
|
+
|
|
4772
|
+
o.resolve = resolve;
|
|
4773
|
+
o.reject = reject;
|
|
4774
|
+
});
|
|
4775
|
+
def.chain.push(o);
|
|
4776
|
+
|
|
4777
|
+
if (def.state !== 0) {
|
|
4778
|
+
schedule(notify,def);
|
|
4779
|
+
}
|
|
4780
|
+
|
|
4781
|
+
return o.promise;
|
|
4782
|
+
};
|
|
4783
|
+
this['catch'] = function $catch$(failure) {
|
|
4784
|
+
return this.then(void 0,failure);
|
|
4785
|
+
};
|
|
4786
|
+
|
|
4787
|
+
try {
|
|
4788
|
+
executor.call(
|
|
4789
|
+
void 0,
|
|
4790
|
+
function publicResolve(msg){
|
|
4791
|
+
resolve.call(def,msg);
|
|
4792
|
+
},
|
|
4793
|
+
function publicReject(msg) {
|
|
4794
|
+
reject.call(def,msg);
|
|
4795
|
+
}
|
|
4796
|
+
);
|
|
4797
|
+
}
|
|
4798
|
+
catch (err) {
|
|
4799
|
+
reject.call(def,err);
|
|
4800
|
+
}
|
|
4801
|
+
}
|
|
4802
|
+
|
|
4803
|
+
var PromisePrototype = builtInProp({},'constructor',NpoPromise,
|
|
4804
|
+
/*configurable=*/false
|
|
4805
|
+
);
|
|
4806
|
+
|
|
4807
|
+
// Note: Android 4 cannot use `Object.defineProperty(..)` here
|
|
4808
|
+
NpoPromise.prototype = PromisePrototype;
|
|
4809
|
+
|
|
4810
|
+
// built-in "brand" to signal an "uninitialized" promise
|
|
4811
|
+
builtInProp(PromisePrototype,'__NPO__',0,
|
|
4812
|
+
/*configurable=*/false
|
|
4813
|
+
);
|
|
4814
|
+
|
|
4815
|
+
builtInProp(NpoPromise,'resolve',function Promise$resolve(msg) {
|
|
4816
|
+
var Constructor = this;
|
|
4817
|
+
|
|
4818
|
+
// spec mandated checks
|
|
4819
|
+
// note: best "isPromise" check that's practical for now
|
|
4820
|
+
if (msg && typeof msg === 'object' && msg['__NPO__'] === 1) {
|
|
4821
|
+
return msg;
|
|
4822
|
+
}
|
|
4823
|
+
|
|
4824
|
+
return new Constructor(function executor(resolve,reject){
|
|
4825
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
4826
|
+
throw TypeError('Not a function');
|
|
4827
|
+
}
|
|
4828
|
+
|
|
4829
|
+
resolve(msg);
|
|
4830
|
+
});
|
|
4831
|
+
});
|
|
4832
|
+
|
|
4833
|
+
builtInProp(NpoPromise,'reject',function Promise$reject(msg) {
|
|
4834
|
+
return new this(function executor(resolve,reject){
|
|
4835
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
4836
|
+
throw TypeError('Not a function');
|
|
4837
|
+
}
|
|
4838
|
+
|
|
4839
|
+
reject(msg);
|
|
4840
|
+
});
|
|
4841
|
+
});
|
|
4842
|
+
|
|
4843
|
+
builtInProp(NpoPromise,'all',function Promise$all(arr) {
|
|
4844
|
+
var Constructor = this;
|
|
4845
|
+
|
|
4846
|
+
// spec mandated checks
|
|
4847
|
+
if (ToString.call(arr) !== '[object Array]') {
|
|
4848
|
+
return Constructor.reject(TypeError('Not an array'));
|
|
4849
|
+
}
|
|
4850
|
+
if (arr.length === 0) {
|
|
4851
|
+
return Constructor.resolve([]);
|
|
4852
|
+
}
|
|
4853
|
+
|
|
4854
|
+
return new Constructor(function executor(resolve,reject){
|
|
4855
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
4856
|
+
throw TypeError('Not a function');
|
|
4857
|
+
}
|
|
4858
|
+
|
|
4859
|
+
var len = arr.length, msgs = Array(len), count = 0;
|
|
4860
|
+
|
|
4861
|
+
iteratePromises(Constructor,arr,function resolver(idx,msg) {
|
|
4862
|
+
msgs[idx] = msg;
|
|
4863
|
+
if (++count === len) {
|
|
4864
|
+
resolve(msgs);
|
|
4865
|
+
}
|
|
4866
|
+
},reject);
|
|
4867
|
+
});
|
|
4868
|
+
});
|
|
4869
|
+
|
|
4870
|
+
builtInProp(NpoPromise,'race',function Promise$race(arr) {
|
|
4871
|
+
var Constructor = this;
|
|
4872
|
+
|
|
4873
|
+
// spec mandated checks
|
|
4874
|
+
if (ToString.call(arr) !== '[object Array]') {
|
|
4875
|
+
return Constructor.reject(TypeError('Not an array'));
|
|
4876
|
+
}
|
|
4877
|
+
|
|
4878
|
+
return new Constructor(function executor(resolve,reject){
|
|
4879
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
4880
|
+
throw TypeError('Not a function');
|
|
4881
|
+
}
|
|
4882
|
+
|
|
4883
|
+
iteratePromises(Constructor,arr,function resolver(idx,msg){
|
|
4884
|
+
resolve(msg);
|
|
4885
|
+
},reject);
|
|
4886
|
+
});
|
|
4887
|
+
});
|
|
4888
|
+
|
|
4889
|
+
var PromisePolyfill;
|
|
4890
|
+
if (typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]') !== -1) {
|
|
4891
|
+
PromisePolyfill = Promise;
|
|
4892
|
+
} else {
|
|
4893
|
+
PromisePolyfill = NpoPromise;
|
|
4894
|
+
}
|
|
4895
|
+
|
|
4896
|
+
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
4897
|
+
|
|
4536
4898
|
// Maximum allowed session recording length
|
|
4537
4899
|
var MAX_RECORDING_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
4538
4900
|
// Maximum allowed value for minimum session recording length
|
|
@@ -5594,7 +5956,7 @@ var localStorageSupported = function(storage, forceCheck) {
|
|
|
5594
5956
|
|
|
5595
5957
|
var supported = true;
|
|
5596
5958
|
try {
|
|
5597
|
-
storage = storage ||
|
|
5959
|
+
storage = storage || win.localStorage;
|
|
5598
5960
|
var key = '__mplss_' + cheap_guid(8),
|
|
5599
5961
|
val = 'xyz';
|
|
5600
5962
|
storage.setItem(key, val);
|
|
@@ -5626,7 +5988,7 @@ _.localStorage = {
|
|
|
5626
5988
|
|
|
5627
5989
|
get: function(name) {
|
|
5628
5990
|
try {
|
|
5629
|
-
return
|
|
5991
|
+
return win.localStorage.getItem(name);
|
|
5630
5992
|
} catch (err) {
|
|
5631
5993
|
_.localStorage.error(err);
|
|
5632
5994
|
}
|
|
@@ -5644,7 +6006,7 @@ _.localStorage = {
|
|
|
5644
6006
|
|
|
5645
6007
|
set: function(name, value) {
|
|
5646
6008
|
try {
|
|
5647
|
-
|
|
6009
|
+
win.localStorage.setItem(name, value);
|
|
5648
6010
|
} catch (err) {
|
|
5649
6011
|
_.localStorage.error(err);
|
|
5650
6012
|
}
|
|
@@ -5652,7 +6014,7 @@ _.localStorage = {
|
|
|
5652
6014
|
|
|
5653
6015
|
remove: function(name) {
|
|
5654
6016
|
try {
|
|
5655
|
-
|
|
6017
|
+
win.localStorage.removeItem(name);
|
|
5656
6018
|
} catch (err) {
|
|
5657
6019
|
_.localStorage.error(err);
|
|
5658
6020
|
}
|
|
@@ -5691,7 +6053,7 @@ _.register_event = (function() {
|
|
|
5691
6053
|
|
|
5692
6054
|
function makeHandler(element, new_handler, old_handlers) {
|
|
5693
6055
|
var handler = function(event) {
|
|
5694
|
-
event = event || fixEvent(
|
|
6056
|
+
event = event || fixEvent(win.event);
|
|
5695
6057
|
|
|
5696
6058
|
// this basically happens in firefox whenever another script
|
|
5697
6059
|
// overwrites the onload callback and doesn't pass the event
|
|
@@ -6246,6 +6608,7 @@ _['info']['device'] = _.info.device;
|
|
|
6246
6608
|
_['info']['browser'] = _.info.browser;
|
|
6247
6609
|
_['info']['browserVersion'] = _.info.browserVersion;
|
|
6248
6610
|
_['info']['properties'] = _.info.properties;
|
|
6611
|
+
_['NPO'] = NpoPromise;
|
|
6249
6612
|
|
|
6250
6613
|
/**
|
|
6251
6614
|
* GDPR utils
|
|
@@ -6575,121 +6938,175 @@ var SharedLock = function(key, options) {
|
|
|
6575
6938
|
this.storage = options.storage || window.localStorage;
|
|
6576
6939
|
this.pollIntervalMS = options.pollIntervalMS || 100;
|
|
6577
6940
|
this.timeoutMS = options.timeoutMS || 2000;
|
|
6941
|
+
|
|
6942
|
+
// dependency-inject promise implementation for testing purposes
|
|
6943
|
+
this.promiseImpl = options.promiseImpl || PromisePolyfill;
|
|
6578
6944
|
};
|
|
6579
6945
|
|
|
6580
6946
|
// pass in a specific pid to test contention scenarios; otherwise
|
|
6581
6947
|
// it is chosen randomly for each acquisition attempt
|
|
6582
|
-
SharedLock.prototype.withLock = function(lockedCB,
|
|
6583
|
-
|
|
6584
|
-
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
var i = pid || (new Date().getTime() + '|' + Math.random());
|
|
6589
|
-
var startTime = new Date().getTime();
|
|
6948
|
+
SharedLock.prototype.withLock = function(lockedCB, pid) {
|
|
6949
|
+
var Promise = this.promiseImpl;
|
|
6950
|
+
return new Promise(_.bind(function (resolve, reject) {
|
|
6951
|
+
var i = pid || (new Date().getTime() + '|' + Math.random());
|
|
6952
|
+
var startTime = new Date().getTime();
|
|
6590
6953
|
|
|
6591
|
-
|
|
6592
|
-
|
|
6593
|
-
|
|
6594
|
-
|
|
6954
|
+
var key = this.storageKey;
|
|
6955
|
+
var pollIntervalMS = this.pollIntervalMS;
|
|
6956
|
+
var timeoutMS = this.timeoutMS;
|
|
6957
|
+
var storage = this.storage;
|
|
6595
6958
|
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6959
|
+
var keyX = key + ':X';
|
|
6960
|
+
var keyY = key + ':Y';
|
|
6961
|
+
var keyZ = key + ':Z';
|
|
6599
6962
|
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6963
|
+
var delay = function(cb) {
|
|
6964
|
+
if (new Date().getTime() - startTime > timeoutMS) {
|
|
6965
|
+
logger$4.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
|
|
6966
|
+
storage.removeItem(keyZ);
|
|
6967
|
+
storage.removeItem(keyY);
|
|
6968
|
+
loop();
|
|
6969
|
+
return;
|
|
6970
|
+
}
|
|
6971
|
+
setTimeout(function() {
|
|
6972
|
+
try {
|
|
6973
|
+
cb();
|
|
6974
|
+
} catch(err) {
|
|
6975
|
+
reject(err);
|
|
6976
|
+
}
|
|
6977
|
+
}, pollIntervalMS * (Math.random() + 0.1));
|
|
6978
|
+
};
|
|
6603
6979
|
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
logger$4.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
|
|
6607
|
-
storage.removeItem(keyZ);
|
|
6608
|
-
storage.removeItem(keyY);
|
|
6609
|
-
loop();
|
|
6610
|
-
return;
|
|
6611
|
-
}
|
|
6612
|
-
setTimeout(function() {
|
|
6613
|
-
try {
|
|
6980
|
+
var waitFor = function(predicate, cb) {
|
|
6981
|
+
if (predicate()) {
|
|
6614
6982
|
cb();
|
|
6615
|
-
}
|
|
6616
|
-
|
|
6983
|
+
} else {
|
|
6984
|
+
delay(function() {
|
|
6985
|
+
waitFor(predicate, cb);
|
|
6986
|
+
});
|
|
6617
6987
|
}
|
|
6618
|
-
}
|
|
6619
|
-
};
|
|
6620
|
-
|
|
6621
|
-
var waitFor = function(predicate, cb) {
|
|
6622
|
-
if (predicate()) {
|
|
6623
|
-
cb();
|
|
6624
|
-
} else {
|
|
6625
|
-
delay(function() {
|
|
6626
|
-
waitFor(predicate, cb);
|
|
6627
|
-
});
|
|
6628
|
-
}
|
|
6629
|
-
};
|
|
6988
|
+
};
|
|
6630
6989
|
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
} else {
|
|
6636
|
-
storage.setItem(keyY, i);
|
|
6637
|
-
if (storage.getItem(keyY) === i) {
|
|
6638
|
-
return true;
|
|
6990
|
+
var getSetY = function() {
|
|
6991
|
+
var valY = storage.getItem(keyY);
|
|
6992
|
+
if (valY && valY !== i) { // if Y == i then this process already has the lock (useful for test cases)
|
|
6993
|
+
return false;
|
|
6639
6994
|
} else {
|
|
6640
|
-
|
|
6641
|
-
|
|
6995
|
+
storage.setItem(keyY, i);
|
|
6996
|
+
if (storage.getItem(keyY) === i) {
|
|
6997
|
+
return true;
|
|
6998
|
+
} else {
|
|
6999
|
+
if (!localStorageSupported(storage, true)) {
|
|
7000
|
+
reject(new Error('localStorage support dropped while acquiring lock'));
|
|
7001
|
+
}
|
|
7002
|
+
return false;
|
|
6642
7003
|
}
|
|
6643
|
-
return false;
|
|
6644
7004
|
}
|
|
6645
|
-
}
|
|
6646
|
-
};
|
|
6647
|
-
|
|
6648
|
-
var loop = function() {
|
|
6649
|
-
storage.setItem(keyX, i);
|
|
7005
|
+
};
|
|
6650
7006
|
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
criticalSection();
|
|
6654
|
-
return;
|
|
6655
|
-
}
|
|
7007
|
+
var loop = function() {
|
|
7008
|
+
storage.setItem(keyX, i);
|
|
6656
7009
|
|
|
6657
|
-
|
|
6658
|
-
if (storage.getItem(
|
|
6659
|
-
|
|
7010
|
+
waitFor(getSetY, function() {
|
|
7011
|
+
if (storage.getItem(keyX) === i) {
|
|
7012
|
+
criticalSection();
|
|
6660
7013
|
return;
|
|
6661
7014
|
}
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
7015
|
+
|
|
7016
|
+
delay(function() {
|
|
7017
|
+
if (storage.getItem(keyY) !== i) {
|
|
7018
|
+
loop();
|
|
7019
|
+
return;
|
|
7020
|
+
}
|
|
7021
|
+
waitFor(function() {
|
|
7022
|
+
return !storage.getItem(keyZ);
|
|
7023
|
+
}, criticalSection);
|
|
7024
|
+
});
|
|
6665
7025
|
});
|
|
6666
|
-
}
|
|
6667
|
-
|
|
7026
|
+
};
|
|
7027
|
+
|
|
7028
|
+
var criticalSection = function() {
|
|
7029
|
+
storage.setItem(keyZ, '1');
|
|
7030
|
+
var removeLock = function () {
|
|
7031
|
+
storage.removeItem(keyZ);
|
|
7032
|
+
if (storage.getItem(keyY) === i) {
|
|
7033
|
+
storage.removeItem(keyY);
|
|
7034
|
+
}
|
|
7035
|
+
if (storage.getItem(keyX) === i) {
|
|
7036
|
+
storage.removeItem(keyX);
|
|
7037
|
+
}
|
|
7038
|
+
};
|
|
7039
|
+
|
|
7040
|
+
lockedCB()
|
|
7041
|
+
.then(function (ret) {
|
|
7042
|
+
removeLock();
|
|
7043
|
+
resolve(ret);
|
|
7044
|
+
})
|
|
7045
|
+
.catch(function (err) {
|
|
7046
|
+
removeLock();
|
|
7047
|
+
reject(err);
|
|
7048
|
+
});
|
|
7049
|
+
};
|
|
6668
7050
|
|
|
6669
|
-
var criticalSection = function() {
|
|
6670
|
-
storage.setItem(keyZ, '1');
|
|
6671
7051
|
try {
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
|
|
6675
|
-
|
|
6676
|
-
storage.removeItem(keyY);
|
|
6677
|
-
}
|
|
6678
|
-
if (storage.getItem(keyX) === i) {
|
|
6679
|
-
storage.removeItem(keyX);
|
|
7052
|
+
if (localStorageSupported(storage, true)) {
|
|
7053
|
+
loop();
|
|
7054
|
+
} else {
|
|
7055
|
+
throw new Error('localStorage support check failed');
|
|
6680
7056
|
}
|
|
7057
|
+
} catch(err) {
|
|
7058
|
+
reject(err);
|
|
6681
7059
|
}
|
|
6682
|
-
};
|
|
7060
|
+
}, this));
|
|
7061
|
+
};
|
|
6683
7062
|
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
7063
|
+
/**
|
|
7064
|
+
* @typedef {import('./wrapper').StorageWrapper}
|
|
7065
|
+
*/
|
|
7066
|
+
|
|
7067
|
+
/**
|
|
7068
|
+
* @type {StorageWrapper}
|
|
7069
|
+
*/
|
|
7070
|
+
var LocalStorageWrapper = function (storageOverride) {
|
|
7071
|
+
this.storage = storageOverride || localStorage;
|
|
7072
|
+
};
|
|
7073
|
+
|
|
7074
|
+
LocalStorageWrapper.prototype.init = function () {
|
|
7075
|
+
return PromisePolyfill.resolve();
|
|
7076
|
+
};
|
|
7077
|
+
|
|
7078
|
+
LocalStorageWrapper.prototype.setItem = function (key, value) {
|
|
7079
|
+
return new PromisePolyfill(_.bind(function (resolve, reject) {
|
|
7080
|
+
try {
|
|
7081
|
+
this.storage.setItem(key, value);
|
|
7082
|
+
} catch (e) {
|
|
7083
|
+
reject(e);
|
|
6689
7084
|
}
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
7085
|
+
resolve();
|
|
7086
|
+
}, this));
|
|
7087
|
+
};
|
|
7088
|
+
|
|
7089
|
+
LocalStorageWrapper.prototype.getItem = function (key) {
|
|
7090
|
+
return new PromisePolyfill(_.bind(function (resolve, reject) {
|
|
7091
|
+
var item;
|
|
7092
|
+
try {
|
|
7093
|
+
item = this.storage.getItem(key);
|
|
7094
|
+
} catch (e) {
|
|
7095
|
+
reject(e);
|
|
7096
|
+
}
|
|
7097
|
+
resolve(item);
|
|
7098
|
+
}, this));
|
|
7099
|
+
};
|
|
7100
|
+
|
|
7101
|
+
LocalStorageWrapper.prototype.removeItem = function (key) {
|
|
7102
|
+
return new PromisePolyfill(_.bind(function (resolve, reject) {
|
|
7103
|
+
try {
|
|
7104
|
+
this.storage.removeItem(key);
|
|
7105
|
+
} catch (e) {
|
|
7106
|
+
reject(e);
|
|
7107
|
+
}
|
|
7108
|
+
resolve();
|
|
7109
|
+
}, this));
|
|
6693
7110
|
};
|
|
6694
7111
|
|
|
6695
7112
|
var logger$3 = console_with_prefix('batch');
|
|
@@ -6710,19 +7127,38 @@ var logger$3 = console_with_prefix('batch');
|
|
|
6710
7127
|
* to data loss in some situations).
|
|
6711
7128
|
* @constructor
|
|
6712
7129
|
*/
|
|
6713
|
-
var RequestQueue = function(storageKey, options) {
|
|
7130
|
+
var RequestQueue = function (storageKey, options) {
|
|
6714
7131
|
options = options || {};
|
|
6715
7132
|
this.storageKey = storageKey;
|
|
6716
7133
|
this.usePersistence = options.usePersistence;
|
|
6717
7134
|
if (this.usePersistence) {
|
|
6718
|
-
this.
|
|
6719
|
-
this.lock = new SharedLock(storageKey, {storage:
|
|
7135
|
+
this.queueStorage = options.queueStorage || new LocalStorageWrapper();
|
|
7136
|
+
this.lock = new SharedLock(storageKey, { storage: options.sharedLockStorage || window.localStorage });
|
|
7137
|
+
this.queueStorage.init();
|
|
6720
7138
|
}
|
|
6721
7139
|
this.reportError = options.errorReporter || _.bind(logger$3.error, logger$3);
|
|
6722
7140
|
|
|
6723
7141
|
this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
|
|
6724
7142
|
|
|
6725
7143
|
this.memQueue = [];
|
|
7144
|
+
this.initialized = false;
|
|
7145
|
+
};
|
|
7146
|
+
|
|
7147
|
+
RequestQueue.prototype.ensureInit = function () {
|
|
7148
|
+
if (this.initialized) {
|
|
7149
|
+
return PromisePolyfill.resolve();
|
|
7150
|
+
}
|
|
7151
|
+
|
|
7152
|
+
return this.queueStorage
|
|
7153
|
+
.init()
|
|
7154
|
+
.then(_.bind(function () {
|
|
7155
|
+
this.initialized = true;
|
|
7156
|
+
}, this))
|
|
7157
|
+
.catch(_.bind(function (err) {
|
|
7158
|
+
this.reportError('Error initializing queue persistence. Disabling persistence', err);
|
|
7159
|
+
this.initialized = true;
|
|
7160
|
+
this.usePersistence = false;
|
|
7161
|
+
}, this));
|
|
6726
7162
|
};
|
|
6727
7163
|
|
|
6728
7164
|
/**
|
|
@@ -6737,7 +7173,7 @@ var RequestQueue = function(storageKey, options) {
|
|
|
6737
7173
|
* failure of the enqueue operation; it is asynchronous because the localStorage
|
|
6738
7174
|
* lock is asynchronous.
|
|
6739
7175
|
*/
|
|
6740
|
-
RequestQueue.prototype.enqueue = function(item, flushInterval
|
|
7176
|
+
RequestQueue.prototype.enqueue = function (item, flushInterval) {
|
|
6741
7177
|
var queueEntry = {
|
|
6742
7178
|
'id': cheap_guid(),
|
|
6743
7179
|
'flushAfter': new Date().getTime() + flushInterval * 2,
|
|
@@ -6746,33 +7182,37 @@ RequestQueue.prototype.enqueue = function(item, flushInterval, cb) {
|
|
|
6746
7182
|
|
|
6747
7183
|
if (!this.usePersistence) {
|
|
6748
7184
|
this.memQueue.push(queueEntry);
|
|
6749
|
-
|
|
6750
|
-
cb(true);
|
|
6751
|
-
}
|
|
7185
|
+
return PromisePolyfill.resolve(true);
|
|
6752
7186
|
} else {
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
7187
|
+
|
|
7188
|
+
var enqueueItem = _.bind(function () {
|
|
7189
|
+
return this.ensureInit()
|
|
7190
|
+
.then(_.bind(function () {
|
|
7191
|
+
return this.readFromStorage();
|
|
7192
|
+
}, this))
|
|
7193
|
+
.then(_.bind(function (storedQueue) {
|
|
7194
|
+
storedQueue.push(queueEntry);
|
|
7195
|
+
return this.saveToStorage(storedQueue);
|
|
7196
|
+
}, this))
|
|
7197
|
+
.then(_.bind(function (succeeded) {
|
|
6760
7198
|
// only add to in-memory queue when storage succeeds
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
}, this)
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
7199
|
+
if (succeeded) {
|
|
7200
|
+
this.memQueue.push(queueEntry);
|
|
7201
|
+
}
|
|
7202
|
+
return succeeded;
|
|
7203
|
+
}, this))
|
|
7204
|
+
.catch(_.bind(function (err) {
|
|
7205
|
+
this.reportError('Error enqueueing item', err, item);
|
|
7206
|
+
return false;
|
|
7207
|
+
}, this));
|
|
7208
|
+
}, this);
|
|
7209
|
+
|
|
7210
|
+
return this.lock
|
|
7211
|
+
.withLock(enqueueItem, this.pid)
|
|
7212
|
+
.catch(_.bind(function (err) {
|
|
7213
|
+
this.reportError('Error acquiring storage lock', err);
|
|
7214
|
+
return false;
|
|
7215
|
+
}, this));
|
|
6776
7216
|
}
|
|
6777
7217
|
};
|
|
6778
7218
|
|
|
@@ -6782,31 +7222,41 @@ RequestQueue.prototype.enqueue = function(item, flushInterval, cb) {
|
|
|
6782
7222
|
* in the persisted queue (items where the 'flushAfter' time has
|
|
6783
7223
|
* already passed).
|
|
6784
7224
|
*/
|
|
6785
|
-
RequestQueue.prototype.fillBatch = function(batchSize) {
|
|
7225
|
+
RequestQueue.prototype.fillBatch = function (batchSize) {
|
|
6786
7226
|
var batch = this.memQueue.slice(0, batchSize);
|
|
6787
7227
|
if (this.usePersistence && batch.length < batchSize) {
|
|
6788
7228
|
// don't need lock just to read events; localStorage is thread-safe
|
|
6789
7229
|
// and the worst that could happen is a duplicate send of some
|
|
6790
7230
|
// orphaned events, which will be deduplicated on the server side
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
_.
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
7231
|
+
return this.ensureInit()
|
|
7232
|
+
.then(_.bind(function () {
|
|
7233
|
+
return this.readFromStorage();
|
|
7234
|
+
}, this))
|
|
7235
|
+
.then(_.bind(function (storedQueue) {
|
|
7236
|
+
if (storedQueue.length) {
|
|
7237
|
+
// item IDs already in batch; don't duplicate out of storage
|
|
7238
|
+
var idsInBatch = {}; // poor man's Set
|
|
7239
|
+
_.each(batch, function (item) {
|
|
7240
|
+
idsInBatch[item['id']] = true;
|
|
7241
|
+
});
|
|
7242
|
+
|
|
7243
|
+
for (var i = 0; i < storedQueue.length; i++) {
|
|
7244
|
+
var item = storedQueue[i];
|
|
7245
|
+
if (new Date().getTime() > item['flushAfter'] && !idsInBatch[item['id']]) {
|
|
7246
|
+
item.orphaned = true;
|
|
7247
|
+
batch.push(item);
|
|
7248
|
+
if (batch.length >= batchSize) {
|
|
7249
|
+
break;
|
|
7250
|
+
}
|
|
7251
|
+
}
|
|
6804
7252
|
}
|
|
6805
7253
|
}
|
|
6806
|
-
|
|
6807
|
-
|
|
7254
|
+
|
|
7255
|
+
return batch;
|
|
7256
|
+
}, this));
|
|
7257
|
+
} else {
|
|
7258
|
+
return PromisePolyfill.resolve(batch);
|
|
6808
7259
|
}
|
|
6809
|
-
return batch;
|
|
6810
7260
|
};
|
|
6811
7261
|
|
|
6812
7262
|
/**
|
|
@@ -6814,9 +7264,9 @@ RequestQueue.prototype.fillBatch = function(batchSize) {
|
|
|
6814
7264
|
* also remove any item without a valid id (e.g., malformed
|
|
6815
7265
|
* storage entries).
|
|
6816
7266
|
*/
|
|
6817
|
-
var filterOutIDsAndInvalid = function(items, idSet) {
|
|
7267
|
+
var filterOutIDsAndInvalid = function (items, idSet) {
|
|
6818
7268
|
var filteredItems = [];
|
|
6819
|
-
_.each(items, function(item) {
|
|
7269
|
+
_.each(items, function (item) {
|
|
6820
7270
|
if (item['id'] && !idSet[item['id']]) {
|
|
6821
7271
|
filteredItems.push(item);
|
|
6822
7272
|
}
|
|
@@ -6828,78 +7278,80 @@ var filterOutIDsAndInvalid = function(items, idSet) {
|
|
|
6828
7278
|
* Remove items with matching IDs from both in-memory queue
|
|
6829
7279
|
* and persisted queue
|
|
6830
7280
|
*/
|
|
6831
|
-
RequestQueue.prototype.removeItemsByID = function(ids
|
|
7281
|
+
RequestQueue.prototype.removeItemsByID = function (ids) {
|
|
6832
7282
|
var idSet = {}; // poor man's Set
|
|
6833
|
-
_.each(ids, function(id) {
|
|
7283
|
+
_.each(ids, function (id) {
|
|
7284
|
+
idSet[id] = true;
|
|
7285
|
+
});
|
|
6834
7286
|
|
|
6835
7287
|
this.memQueue = filterOutIDsAndInvalid(this.memQueue, idSet);
|
|
6836
7288
|
if (!this.usePersistence) {
|
|
6837
|
-
|
|
6838
|
-
cb(true);
|
|
6839
|
-
}
|
|
7289
|
+
return PromisePolyfill.resolve(true);
|
|
6840
7290
|
} else {
|
|
6841
|
-
var removeFromStorage = _.bind(function() {
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
|
|
6847
|
-
|
|
6848
|
-
|
|
6849
|
-
|
|
6850
|
-
|
|
6851
|
-
|
|
7291
|
+
var removeFromStorage = _.bind(function () {
|
|
7292
|
+
return this.ensureInit()
|
|
7293
|
+
.then(_.bind(function () {
|
|
7294
|
+
return this.readFromStorage();
|
|
7295
|
+
}, this))
|
|
7296
|
+
.then(_.bind(function (storedQueue) {
|
|
7297
|
+
storedQueue = filterOutIDsAndInvalid(storedQueue, idSet);
|
|
7298
|
+
return this.saveToStorage(storedQueue);
|
|
7299
|
+
}, this))
|
|
7300
|
+
.then(_.bind(function () {
|
|
7301
|
+
return this.readFromStorage();
|
|
7302
|
+
}, this))
|
|
7303
|
+
.then(_.bind(function (storedQueue) {
|
|
7304
|
+
// an extra check: did storage report success but somehow
|
|
7305
|
+
// the items are still there?
|
|
6852
7306
|
for (var i = 0; i < storedQueue.length; i++) {
|
|
6853
7307
|
var item = storedQueue[i];
|
|
6854
7308
|
if (item['id'] && !!idSet[item['id']]) {
|
|
6855
|
-
|
|
6856
|
-
return false;
|
|
7309
|
+
throw new Error('Item not removed from storage');
|
|
6857
7310
|
}
|
|
6858
7311
|
}
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
|
|
6864
|
-
|
|
7312
|
+
return true;
|
|
7313
|
+
}, this))
|
|
7314
|
+
.catch(_.bind(function (err) {
|
|
7315
|
+
this.reportError('Error removing items', err, ids);
|
|
7316
|
+
return false;
|
|
7317
|
+
}, this));
|
|
6865
7318
|
}, this);
|
|
6866
7319
|
|
|
6867
|
-
this.lock
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
|
|
6885
|
-
|
|
6886
|
-
|
|
6887
|
-
|
|
6888
|
-
|
|
7320
|
+
return this.lock
|
|
7321
|
+
.withLock(removeFromStorage, this.pid)
|
|
7322
|
+
.catch(_.bind(function (err) {
|
|
7323
|
+
this.reportError('Error acquiring storage lock', err);
|
|
7324
|
+
if (!localStorageSupported(this.queueStorage.storage, true)) {
|
|
7325
|
+
// Looks like localStorage writes have stopped working sometime after
|
|
7326
|
+
// initialization (probably full), and so nobody can acquire locks
|
|
7327
|
+
// anymore. Consider it temporarily safe to remove items without the
|
|
7328
|
+
// lock, since nobody's writing successfully anyway.
|
|
7329
|
+
return removeFromStorage()
|
|
7330
|
+
.then(_.bind(function (success) {
|
|
7331
|
+
if (!success) {
|
|
7332
|
+
// OK, we couldn't even write out the smaller queue. Try clearing it
|
|
7333
|
+
// entirely.
|
|
7334
|
+
return this.queueStorage.removeItem(this.storageKey).then(function () {
|
|
7335
|
+
return success;
|
|
7336
|
+
});
|
|
7337
|
+
}
|
|
7338
|
+
return success;
|
|
7339
|
+
}, this))
|
|
7340
|
+
.catch(_.bind(function (err) {
|
|
7341
|
+
this.reportError('Error clearing queue', err);
|
|
7342
|
+
return false;
|
|
7343
|
+
}, this));
|
|
7344
|
+
} else {
|
|
7345
|
+
return false;
|
|
6889
7346
|
}
|
|
6890
|
-
}
|
|
6891
|
-
if (cb) {
|
|
6892
|
-
cb(succeeded);
|
|
6893
|
-
}
|
|
6894
|
-
}, this), this.pid);
|
|
7347
|
+
}, this));
|
|
6895
7348
|
}
|
|
6896
|
-
|
|
6897
7349
|
};
|
|
6898
7350
|
|
|
6899
7351
|
// internal helper for RequestQueue.updatePayloads
|
|
6900
|
-
var updatePayloads = function(existingItems, itemsToUpdate) {
|
|
7352
|
+
var updatePayloads = function (existingItems, itemsToUpdate) {
|
|
6901
7353
|
var newItems = [];
|
|
6902
|
-
_.each(existingItems, function(item) {
|
|
7354
|
+
_.each(existingItems, function (item) {
|
|
6903
7355
|
var id = item['id'];
|
|
6904
7356
|
if (id in itemsToUpdate) {
|
|
6905
7357
|
var newPayload = itemsToUpdate[id];
|
|
@@ -6919,79 +7371,95 @@ var updatePayloads = function(existingItems, itemsToUpdate) {
|
|
|
6919
7371
|
* Update payloads of given items in both in-memory queue and
|
|
6920
7372
|
* persisted queue. Items set to null are removed from queues.
|
|
6921
7373
|
*/
|
|
6922
|
-
RequestQueue.prototype.updatePayloads = function(itemsToUpdate
|
|
7374
|
+
RequestQueue.prototype.updatePayloads = function (itemsToUpdate) {
|
|
6923
7375
|
this.memQueue = updatePayloads(this.memQueue, itemsToUpdate);
|
|
6924
7376
|
if (!this.usePersistence) {
|
|
6925
|
-
|
|
6926
|
-
cb(true);
|
|
6927
|
-
}
|
|
7377
|
+
return PromisePolyfill.resolve(true);
|
|
6928
7378
|
} else {
|
|
6929
|
-
this.lock
|
|
6930
|
-
|
|
6931
|
-
|
|
6932
|
-
|
|
6933
|
-
|
|
6934
|
-
|
|
6935
|
-
|
|
6936
|
-
|
|
6937
|
-
|
|
6938
|
-
|
|
6939
|
-
|
|
6940
|
-
|
|
6941
|
-
|
|
6942
|
-
|
|
6943
|
-
this
|
|
6944
|
-
|
|
6945
|
-
|
|
6946
|
-
|
|
6947
|
-
|
|
7379
|
+
return this.lock
|
|
7380
|
+
.withLock(_.bind(function lockAcquired() {
|
|
7381
|
+
return this.ensureInit()
|
|
7382
|
+
.then(_.bind(function () {
|
|
7383
|
+
return this.readFromStorage();
|
|
7384
|
+
}, this))
|
|
7385
|
+
.then(_.bind(function (storedQueue) {
|
|
7386
|
+
storedQueue = updatePayloads(storedQueue, itemsToUpdate);
|
|
7387
|
+
return this.saveToStorage(storedQueue);
|
|
7388
|
+
}, this))
|
|
7389
|
+
.catch(_.bind(function (err) {
|
|
7390
|
+
this.reportError('Error updating items', itemsToUpdate, err);
|
|
7391
|
+
return false;
|
|
7392
|
+
}, this));
|
|
7393
|
+
}, this), this.pid)
|
|
7394
|
+
.catch(_.bind(function (err) {
|
|
7395
|
+
this.reportError('Error acquiring storage lock', err);
|
|
7396
|
+
return false;
|
|
7397
|
+
}, this));
|
|
6948
7398
|
}
|
|
6949
|
-
|
|
6950
7399
|
};
|
|
6951
7400
|
|
|
6952
7401
|
/**
|
|
6953
7402
|
* Read and parse items array from localStorage entry, handling
|
|
6954
7403
|
* malformed/missing data if necessary.
|
|
6955
7404
|
*/
|
|
6956
|
-
RequestQueue.prototype.readFromStorage = function() {
|
|
6957
|
-
|
|
6958
|
-
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
if (
|
|
6963
|
-
|
|
6964
|
-
storageEntry
|
|
7405
|
+
RequestQueue.prototype.readFromStorage = function () {
|
|
7406
|
+
return this.ensureInit()
|
|
7407
|
+
.then(_.bind(function () {
|
|
7408
|
+
return this.queueStorage.getItem(this.storageKey);
|
|
7409
|
+
}, this))
|
|
7410
|
+
.then(_.bind(function (storageEntry) {
|
|
7411
|
+
if (storageEntry) {
|
|
7412
|
+
storageEntry = JSONParse(storageEntry);
|
|
7413
|
+
if (!_.isArray(storageEntry)) {
|
|
7414
|
+
this.reportError('Invalid storage entry:', storageEntry);
|
|
7415
|
+
storageEntry = null;
|
|
7416
|
+
}
|
|
6965
7417
|
}
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
|
|
6971
|
-
|
|
7418
|
+
return storageEntry || [];
|
|
7419
|
+
}, this))
|
|
7420
|
+
.catch(_.bind(function (err) {
|
|
7421
|
+
this.reportError('Error retrieving queue', err);
|
|
7422
|
+
return [];
|
|
7423
|
+
}, this));
|
|
6972
7424
|
};
|
|
6973
7425
|
|
|
6974
7426
|
/**
|
|
6975
7427
|
* Serialize the given items array to localStorage.
|
|
6976
7428
|
*/
|
|
6977
|
-
RequestQueue.prototype.saveToStorage = function(queue) {
|
|
7429
|
+
RequestQueue.prototype.saveToStorage = function (queue) {
|
|
6978
7430
|
try {
|
|
6979
|
-
|
|
6980
|
-
return true;
|
|
7431
|
+
var serialized = JSONStringify(queue);
|
|
6981
7432
|
} catch (err) {
|
|
6982
|
-
this.reportError('Error
|
|
6983
|
-
return false;
|
|
7433
|
+
this.reportError('Error serializing queue', err);
|
|
7434
|
+
return PromisePolyfill.resolve(false);
|
|
6984
7435
|
}
|
|
7436
|
+
|
|
7437
|
+
return this.ensureInit()
|
|
7438
|
+
.then(_.bind(function () {
|
|
7439
|
+
return this.queueStorage.setItem(this.storageKey, serialized);
|
|
7440
|
+
}, this))
|
|
7441
|
+
.then(function () {
|
|
7442
|
+
return true;
|
|
7443
|
+
})
|
|
7444
|
+
.catch(_.bind(function (err) {
|
|
7445
|
+
this.reportError('Error saving queue', err);
|
|
7446
|
+
return false;
|
|
7447
|
+
}, this));
|
|
6985
7448
|
};
|
|
6986
7449
|
|
|
6987
7450
|
/**
|
|
6988
7451
|
* Clear out queues (memory and localStorage).
|
|
6989
7452
|
*/
|
|
6990
|
-
RequestQueue.prototype.clear = function() {
|
|
7453
|
+
RequestQueue.prototype.clear = function () {
|
|
6991
7454
|
this.memQueue = [];
|
|
6992
7455
|
|
|
6993
7456
|
if (this.usePersistence) {
|
|
6994
|
-
this.
|
|
7457
|
+
return this.ensureInit()
|
|
7458
|
+
.then(_.bind(function () {
|
|
7459
|
+
return this.queueStorage.removeItem(this.storageKey);
|
|
7460
|
+
}, this));
|
|
7461
|
+
} else {
|
|
7462
|
+
return PromisePolyfill.resolve();
|
|
6995
7463
|
}
|
|
6996
7464
|
};
|
|
6997
7465
|
|
|
@@ -7010,7 +7478,8 @@ var RequestBatcher = function(storageKey, options) {
|
|
|
7010
7478
|
this.errorReporter = options.errorReporter;
|
|
7011
7479
|
this.queue = new RequestQueue(storageKey, {
|
|
7012
7480
|
errorReporter: _.bind(this.reportError, this),
|
|
7013
|
-
|
|
7481
|
+
queueStorage: options.queueStorage,
|
|
7482
|
+
sharedLockStorage: options.sharedLockStorage,
|
|
7014
7483
|
usePersistence: options.usePersistence
|
|
7015
7484
|
});
|
|
7016
7485
|
|
|
@@ -7038,8 +7507,8 @@ var RequestBatcher = function(storageKey, options) {
|
|
|
7038
7507
|
/**
|
|
7039
7508
|
* Add one item to queue.
|
|
7040
7509
|
*/
|
|
7041
|
-
RequestBatcher.prototype.enqueue = function(item
|
|
7042
|
-
this.queue.enqueue(item, this.flushInterval
|
|
7510
|
+
RequestBatcher.prototype.enqueue = function(item) {
|
|
7511
|
+
return this.queue.enqueue(item, this.flushInterval);
|
|
7043
7512
|
};
|
|
7044
7513
|
|
|
7045
7514
|
/**
|
|
@@ -7049,7 +7518,7 @@ RequestBatcher.prototype.enqueue = function(item, cb) {
|
|
|
7049
7518
|
RequestBatcher.prototype.start = function() {
|
|
7050
7519
|
this.stopped = false;
|
|
7051
7520
|
this.consecutiveRemovalFailures = 0;
|
|
7052
|
-
this.flush();
|
|
7521
|
+
return this.flush();
|
|
7053
7522
|
};
|
|
7054
7523
|
|
|
7055
7524
|
/**
|
|
@@ -7067,7 +7536,7 @@ RequestBatcher.prototype.stop = function() {
|
|
|
7067
7536
|
* Clear out queue.
|
|
7068
7537
|
*/
|
|
7069
7538
|
RequestBatcher.prototype.clear = function() {
|
|
7070
|
-
this.queue.clear();
|
|
7539
|
+
return this.queue.clear();
|
|
7071
7540
|
};
|
|
7072
7541
|
|
|
7073
7542
|
/**
|
|
@@ -7098,6 +7567,17 @@ RequestBatcher.prototype.scheduleFlush = function(flushMS) {
|
|
|
7098
7567
|
}
|
|
7099
7568
|
};
|
|
7100
7569
|
|
|
7570
|
+
/**
|
|
7571
|
+
* Send a request using the sendRequest callback, but promisified.
|
|
7572
|
+
* TODO: sendRequest should be promisified in the first place.
|
|
7573
|
+
*/
|
|
7574
|
+
RequestBatcher.prototype.sendRequestPromise = function(data, options) {
|
|
7575
|
+
return new PromisePolyfill(_.bind(function(resolve) {
|
|
7576
|
+
this.sendRequest(data, options, resolve);
|
|
7577
|
+
}, this));
|
|
7578
|
+
};
|
|
7579
|
+
|
|
7580
|
+
|
|
7101
7581
|
/**
|
|
7102
7582
|
* Flush one batch to network. Depending on success/failure modes, it will either
|
|
7103
7583
|
* remove the batch from the queue or leave it in for retry, and schedule the next
|
|
@@ -7109,183 +7589,191 @@ RequestBatcher.prototype.scheduleFlush = function(flushMS) {
|
|
|
7109
7589
|
* sendBeacon offers no callbacks or status indications)
|
|
7110
7590
|
*/
|
|
7111
7591
|
RequestBatcher.prototype.flush = function(options) {
|
|
7112
|
-
|
|
7592
|
+
if (this.requestInProgress) {
|
|
7593
|
+
logger$2.log('Flush: Request already in progress');
|
|
7594
|
+
return PromisePolyfill.resolve();
|
|
7595
|
+
}
|
|
7113
7596
|
|
|
7114
|
-
|
|
7115
|
-
logger$2.log('Flush: Request already in progress');
|
|
7116
|
-
return;
|
|
7117
|
-
}
|
|
7597
|
+
this.requestInProgress = true;
|
|
7118
7598
|
|
|
7119
|
-
|
|
7120
|
-
|
|
7121
|
-
|
|
7122
|
-
|
|
7123
|
-
|
|
7124
|
-
|
|
7125
|
-
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
var
|
|
7131
|
-
|
|
7132
|
-
|
|
7133
|
-
|
|
7134
|
-
|
|
7135
|
-
|
|
7136
|
-
// sends each event (regardless of which version originally queued
|
|
7137
|
-
// it for sending)
|
|
7138
|
-
if (payload['event'] && payload['properties']) {
|
|
7139
|
-
payload['properties'] = _.extend(
|
|
7140
|
-
{},
|
|
7141
|
-
payload['properties'],
|
|
7142
|
-
{'mp_sent_by_lib_version': Config.LIB_VERSION}
|
|
7143
|
-
);
|
|
7599
|
+
options = options || {};
|
|
7600
|
+
var timeoutMS = this.libConfig['batch_request_timeout_ms'];
|
|
7601
|
+
var startTime = new Date().getTime();
|
|
7602
|
+
var currentBatchSize = this.batchSize;
|
|
7603
|
+
|
|
7604
|
+
return this.queue.fillBatch(currentBatchSize)
|
|
7605
|
+
.then(_.bind(function(batch) {
|
|
7606
|
+
|
|
7607
|
+
// if there's more items in the queue than the batch size, attempt
|
|
7608
|
+
// to flush again after the current batch is done.
|
|
7609
|
+
var attemptSecondaryFlush = batch.length === currentBatchSize;
|
|
7610
|
+
var dataForRequest = [];
|
|
7611
|
+
var transformedItems = {};
|
|
7612
|
+
_.each(batch, function(item) {
|
|
7613
|
+
var payload = item['payload'];
|
|
7614
|
+
if (this.beforeSendHook && !item.orphaned) {
|
|
7615
|
+
payload = this.beforeSendHook(payload);
|
|
7144
7616
|
}
|
|
7145
|
-
|
|
7146
|
-
|
|
7147
|
-
|
|
7148
|
-
|
|
7149
|
-
|
|
7150
|
-
|
|
7151
|
-
|
|
7152
|
-
|
|
7153
|
-
|
|
7154
|
-
|
|
7617
|
+
if (payload) {
|
|
7618
|
+
// mp_sent_by_lib_version prop captures which lib version actually
|
|
7619
|
+
// sends each event (regardless of which version originally queued
|
|
7620
|
+
// it for sending)
|
|
7621
|
+
if (payload['event'] && payload['properties']) {
|
|
7622
|
+
payload['properties'] = _.extend(
|
|
7623
|
+
{},
|
|
7624
|
+
payload['properties'],
|
|
7625
|
+
{'mp_sent_by_lib_version': Config.LIB_VERSION}
|
|
7626
|
+
);
|
|
7627
|
+
}
|
|
7628
|
+
var addPayload = true;
|
|
7629
|
+
var itemId = item['id'];
|
|
7630
|
+
if (itemId) {
|
|
7631
|
+
if ((this.itemIdsSentSuccessfully[itemId] || 0) > 5) {
|
|
7632
|
+
this.reportError('[dupe] item ID sent too many times, not sending', {
|
|
7633
|
+
item: item,
|
|
7634
|
+
batchSize: batch.length,
|
|
7635
|
+
timesSent: this.itemIdsSentSuccessfully[itemId]
|
|
7636
|
+
});
|
|
7637
|
+
addPayload = false;
|
|
7638
|
+
}
|
|
7639
|
+
} else {
|
|
7640
|
+
this.reportError('[dupe] found item with no ID', {item: item});
|
|
7155
7641
|
}
|
|
7156
|
-
} else {
|
|
7157
|
-
this.reportError('[dupe] found item with no ID', {item: item});
|
|
7158
|
-
}
|
|
7159
7642
|
|
|
7160
|
-
|
|
7161
|
-
|
|
7643
|
+
if (addPayload) {
|
|
7644
|
+
dataForRequest.push(payload);
|
|
7645
|
+
}
|
|
7162
7646
|
}
|
|
7163
|
-
|
|
7164
|
-
|
|
7165
|
-
}, this);
|
|
7166
|
-
if (dataForRequest.length < 1) {
|
|
7167
|
-
this.resetFlush();
|
|
7168
|
-
return; // nothing to do
|
|
7169
|
-
}
|
|
7170
|
-
|
|
7171
|
-
this.requestInProgress = true;
|
|
7172
|
-
|
|
7173
|
-
var batchSendCallback = _.bind(function(res) {
|
|
7174
|
-
this.requestInProgress = false;
|
|
7647
|
+
transformedItems[item['id']] = payload;
|
|
7648
|
+
}, this);
|
|
7175
7649
|
|
|
7176
|
-
|
|
7650
|
+
if (dataForRequest.length < 1) {
|
|
7651
|
+
this.requestInProgress = false;
|
|
7652
|
+
this.resetFlush();
|
|
7653
|
+
return PromisePolyfill.resolve(); // nothing to do
|
|
7654
|
+
}
|
|
7177
7655
|
|
|
7178
|
-
|
|
7179
|
-
|
|
7180
|
-
|
|
7181
|
-
|
|
7182
|
-
|
|
7183
|
-
|
|
7184
|
-
this.queue.updatePayloads(transformedItems);
|
|
7185
|
-
} else if (
|
|
7186
|
-
_.isObject(res) &&
|
|
7187
|
-
res.error === 'timeout' &&
|
|
7188
|
-
new Date().getTime() - startTime >= timeoutMS
|
|
7189
|
-
) {
|
|
7190
|
-
this.reportError('Network timeout; retrying');
|
|
7191
|
-
this.flush();
|
|
7192
|
-
} else if (
|
|
7193
|
-
_.isObject(res) &&
|
|
7194
|
-
(
|
|
7195
|
-
res.httpStatusCode >= 500
|
|
7196
|
-
|| res.httpStatusCode === 429
|
|
7197
|
-
|| (res.httpStatusCode <= 0 && !isOnline())
|
|
7198
|
-
|| res.error === 'timeout'
|
|
7656
|
+
var removeItemsFromQueue = _.bind(function () {
|
|
7657
|
+
return this.queue
|
|
7658
|
+
.removeItemsByID(
|
|
7659
|
+
_.map(batch, function (item) {
|
|
7660
|
+
return item['id'];
|
|
7661
|
+
})
|
|
7199
7662
|
)
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
7212
|
-
|
|
7213
|
-
this.batchSize = Math.min(this.batchSize, halvedBatchSize, batch.length - 1);
|
|
7214
|
-
this.reportError('413 response; reducing batch size to ' + this.batchSize);
|
|
7215
|
-
this.resetFlush();
|
|
7216
|
-
} else {
|
|
7217
|
-
this.reportError('Single-event request too large; dropping', batch);
|
|
7218
|
-
this.resetBatchSize();
|
|
7219
|
-
removeItemsFromQueue = true;
|
|
7220
|
-
}
|
|
7221
|
-
} else {
|
|
7222
|
-
// successful network request+response; remove each item in batch from queue
|
|
7223
|
-
// (even if it was e.g. a 400, in which case retrying won't help)
|
|
7224
|
-
removeItemsFromQueue = true;
|
|
7225
|
-
}
|
|
7226
|
-
|
|
7227
|
-
if (removeItemsFromQueue) {
|
|
7228
|
-
this.queue.removeItemsByID(
|
|
7229
|
-
_.map(batch, function(item) { return item['id']; }),
|
|
7230
|
-
_.bind(function(succeeded) {
|
|
7231
|
-
if (succeeded) {
|
|
7232
|
-
this.consecutiveRemovalFailures = 0;
|
|
7233
|
-
if (this.flushOnlyOnInterval && !attemptSecondaryFlush) {
|
|
7234
|
-
this.resetFlush(); // schedule next batch with a delay
|
|
7235
|
-
} else {
|
|
7236
|
-
this.flush(); // handle next batch if the queue isn't empty
|
|
7663
|
+
.then(_.bind(function (succeeded) {
|
|
7664
|
+
// client-side dedupe
|
|
7665
|
+
_.each(batch, _.bind(function(item) {
|
|
7666
|
+
var itemId = item['id'];
|
|
7667
|
+
if (itemId) {
|
|
7668
|
+
this.itemIdsSentSuccessfully[itemId] = this.itemIdsSentSuccessfully[itemId] || 0;
|
|
7669
|
+
this.itemIdsSentSuccessfully[itemId]++;
|
|
7670
|
+
if (this.itemIdsSentSuccessfully[itemId] > 5) {
|
|
7671
|
+
this.reportError('[dupe] item ID sent too many times', {
|
|
7672
|
+
item: item,
|
|
7673
|
+
batchSize: batch.length,
|
|
7674
|
+
timesSent: this.itemIdsSentSuccessfully[itemId]
|
|
7675
|
+
});
|
|
7237
7676
|
}
|
|
7238
7677
|
} else {
|
|
7239
|
-
this.reportError('
|
|
7240
|
-
if (++this.consecutiveRemovalFailures > 5) {
|
|
7241
|
-
this.reportError('Too many queue failures; disabling batching system.');
|
|
7242
|
-
this.stopAllBatching();
|
|
7243
|
-
} else {
|
|
7244
|
-
this.resetFlush();
|
|
7245
|
-
}
|
|
7678
|
+
this.reportError('[dupe] found item with no ID while removing', {item: item});
|
|
7246
7679
|
}
|
|
7247
|
-
}, this)
|
|
7248
|
-
);
|
|
7680
|
+
}, this));
|
|
7249
7681
|
|
|
7250
|
-
|
|
7251
|
-
|
|
7252
|
-
|
|
7253
|
-
|
|
7254
|
-
|
|
7255
|
-
|
|
7256
|
-
|
|
7257
|
-
this.reportError('[dupe] item ID sent too many times', {
|
|
7258
|
-
item: item,
|
|
7259
|
-
batchSize: batch.length,
|
|
7260
|
-
timesSent: this.itemIdsSentSuccessfully[itemId]
|
|
7261
|
-
});
|
|
7682
|
+
if (succeeded) {
|
|
7683
|
+
this.consecutiveRemovalFailures = 0;
|
|
7684
|
+
if (this.flushOnlyOnInterval && !attemptSecondaryFlush) {
|
|
7685
|
+
this.resetFlush(); // schedule next batch with a delay
|
|
7686
|
+
return PromisePolyfill.resolve();
|
|
7687
|
+
} else {
|
|
7688
|
+
return this.flush(); // handle next batch if the queue isn't empty
|
|
7262
7689
|
}
|
|
7263
7690
|
} else {
|
|
7264
|
-
this.
|
|
7691
|
+
if (++this.consecutiveRemovalFailures > 5) {
|
|
7692
|
+
this.reportError('Too many queue failures; disabling batching system.');
|
|
7693
|
+
this.stopAllBatching();
|
|
7694
|
+
} else {
|
|
7695
|
+
this.resetFlush();
|
|
7696
|
+
}
|
|
7697
|
+
return PromisePolyfill.resolve();
|
|
7265
7698
|
}
|
|
7266
7699
|
}, this));
|
|
7267
|
-
|
|
7700
|
+
}, this);
|
|
7268
7701
|
|
|
7269
|
-
|
|
7270
|
-
this.
|
|
7271
|
-
|
|
7702
|
+
var batchSendCallback = _.bind(function(res) {
|
|
7703
|
+
this.requestInProgress = false;
|
|
7704
|
+
|
|
7705
|
+
try {
|
|
7706
|
+
|
|
7707
|
+
// handle API response in a try-catch to make sure we can reset the
|
|
7708
|
+
// flush operation if something goes wrong
|
|
7709
|
+
|
|
7710
|
+
if (options.unloading) {
|
|
7711
|
+
// update persisted data to include hook transformations
|
|
7712
|
+
return this.queue.updatePayloads(transformedItems);
|
|
7713
|
+
} else if (
|
|
7714
|
+
_.isObject(res) &&
|
|
7715
|
+
res.error === 'timeout' &&
|
|
7716
|
+
new Date().getTime() - startTime >= timeoutMS
|
|
7717
|
+
) {
|
|
7718
|
+
this.reportError('Network timeout; retrying');
|
|
7719
|
+
return this.flush();
|
|
7720
|
+
} else if (
|
|
7721
|
+
_.isObject(res) &&
|
|
7722
|
+
(
|
|
7723
|
+
res.httpStatusCode >= 500
|
|
7724
|
+
|| res.httpStatusCode === 429
|
|
7725
|
+
|| (res.httpStatusCode <= 0 && !isOnline())
|
|
7726
|
+
|| res.error === 'timeout'
|
|
7727
|
+
)
|
|
7728
|
+
) {
|
|
7729
|
+
// network or API error, or 429 Too Many Requests, retry
|
|
7730
|
+
var retryMS = this.flushInterval * 2;
|
|
7731
|
+
if (res.retryAfter) {
|
|
7732
|
+
retryMS = (parseInt(res.retryAfter, 10) * 1000) || retryMS;
|
|
7733
|
+
}
|
|
7734
|
+
retryMS = Math.min(MAX_RETRY_INTERVAL_MS, retryMS);
|
|
7735
|
+
this.reportError('Error; retry in ' + retryMS + ' ms');
|
|
7736
|
+
this.scheduleFlush(retryMS);
|
|
7737
|
+
return PromisePolyfill.resolve();
|
|
7738
|
+
} else if (_.isObject(res) && res.httpStatusCode === 413) {
|
|
7739
|
+
// 413 Payload Too Large
|
|
7740
|
+
if (batch.length > 1) {
|
|
7741
|
+
var halvedBatchSize = Math.max(1, Math.floor(currentBatchSize / 2));
|
|
7742
|
+
this.batchSize = Math.min(this.batchSize, halvedBatchSize, batch.length - 1);
|
|
7743
|
+
this.reportError('413 response; reducing batch size to ' + this.batchSize);
|
|
7744
|
+
this.resetFlush();
|
|
7745
|
+
return PromisePolyfill.resolve();
|
|
7746
|
+
} else {
|
|
7747
|
+
this.reportError('Single-event request too large; dropping', batch);
|
|
7748
|
+
this.resetBatchSize();
|
|
7749
|
+
return removeItemsFromQueue();
|
|
7750
|
+
}
|
|
7751
|
+
} else {
|
|
7752
|
+
// successful network request+response; remove each item in batch from queue
|
|
7753
|
+
// (even if it was e.g. a 400, in which case retrying won't help)
|
|
7754
|
+
return removeItemsFromQueue();
|
|
7755
|
+
}
|
|
7756
|
+
} catch(err) {
|
|
7757
|
+
this.reportError('Error handling API response', err);
|
|
7758
|
+
this.resetFlush();
|
|
7759
|
+
}
|
|
7760
|
+
}, this);
|
|
7761
|
+
var requestOptions = {
|
|
7762
|
+
method: 'POST',
|
|
7763
|
+
verbose: true,
|
|
7764
|
+
ignore_json_errors: true, // eslint-disable-line camelcase
|
|
7765
|
+
timeout_ms: timeoutMS // eslint-disable-line camelcase
|
|
7766
|
+
};
|
|
7767
|
+
if (options.unloading) {
|
|
7768
|
+
requestOptions.transport = 'sendBeacon';
|
|
7272
7769
|
}
|
|
7273
|
-
|
|
7274
|
-
|
|
7275
|
-
|
|
7276
|
-
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
};
|
|
7280
|
-
if (options.unloading) {
|
|
7281
|
-
requestOptions.transport = 'sendBeacon';
|
|
7282
|
-
}
|
|
7283
|
-
logger$2.log('MIXPANEL REQUEST:', dataForRequest);
|
|
7284
|
-
this.sendRequest(dataForRequest, requestOptions, batchSendCallback);
|
|
7285
|
-
} catch(err) {
|
|
7286
|
-
this.reportError('Error flushing request queue', err);
|
|
7287
|
-
this.resetFlush();
|
|
7288
|
-
}
|
|
7770
|
+
logger$2.log('MIXPANEL REQUEST:', dataForRequest);
|
|
7771
|
+
return this.sendRequestPromise(dataForRequest, requestOptions).then(batchSendCallback);
|
|
7772
|
+
}, this))
|
|
7773
|
+
.catch(_.bind(function(err) {
|
|
7774
|
+
this.reportError('Error flushing request queue', err);
|
|
7775
|
+
this.resetFlush();
|
|
7776
|
+
}, this));
|
|
7289
7777
|
};
|
|
7290
7778
|
|
|
7291
7779
|
/**
|
|
@@ -9908,7 +10396,7 @@ MixpanelLib.prototype._track_or_batch = function(options, callback) {
|
|
|
9908
10396
|
}, this);
|
|
9909
10397
|
|
|
9910
10398
|
if (this._batch_requests && !should_send_immediately) {
|
|
9911
|
-
batcher.enqueue(truncated_data
|
|
10399
|
+
batcher.enqueue(truncated_data).then(function(succeeded) {
|
|
9912
10400
|
if (succeeded) {
|
|
9913
10401
|
callback(1, truncated_data);
|
|
9914
10402
|
} else {
|