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