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
|
@@ -4510,11 +4510,9 @@
|
|
|
4510
4510
|
|
|
4511
4511
|
var Config = {
|
|
4512
4512
|
DEBUG: false,
|
|
4513
|
-
LIB_VERSION: '2.
|
|
4513
|
+
LIB_VERSION: '2.57.1'
|
|
4514
4514
|
};
|
|
4515
4515
|
|
|
4516
|
-
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
4517
|
-
|
|
4518
4516
|
// since es6 imports are static and we run unit tests from the console, window won't be defined when importing this file
|
|
4519
4517
|
var win;
|
|
4520
4518
|
if (typeof(window) === 'undefined') {
|
|
@@ -4534,6 +4532,370 @@
|
|
|
4534
4532
|
win = window;
|
|
4535
4533
|
}
|
|
4536
4534
|
|
|
4535
|
+
var setImmediate = win['setImmediate'];
|
|
4536
|
+
var builtInProp, cycle, schedulingQueue,
|
|
4537
|
+
ToString = Object.prototype.toString,
|
|
4538
|
+
timer = (typeof setImmediate !== 'undefined') ?
|
|
4539
|
+
function timer(fn) { return setImmediate(fn); } :
|
|
4540
|
+
setTimeout;
|
|
4541
|
+
|
|
4542
|
+
// dammit, IE8.
|
|
4543
|
+
try {
|
|
4544
|
+
Object.defineProperty({},'x',{});
|
|
4545
|
+
builtInProp = function builtInProp(obj,name,val,config) {
|
|
4546
|
+
return Object.defineProperty(obj,name,{
|
|
4547
|
+
value: val,
|
|
4548
|
+
writable: true,
|
|
4549
|
+
configurable: config !== false
|
|
4550
|
+
});
|
|
4551
|
+
};
|
|
4552
|
+
}
|
|
4553
|
+
catch (err) {
|
|
4554
|
+
builtInProp = function builtInProp(obj,name,val) {
|
|
4555
|
+
obj[name] = val;
|
|
4556
|
+
return obj;
|
|
4557
|
+
};
|
|
4558
|
+
}
|
|
4559
|
+
|
|
4560
|
+
// Note: using a queue instead of array for efficiency
|
|
4561
|
+
schedulingQueue = (function Queue() {
|
|
4562
|
+
var first, last, item;
|
|
4563
|
+
|
|
4564
|
+
function Item(fn,self) {
|
|
4565
|
+
this.fn = fn;
|
|
4566
|
+
this.self = self;
|
|
4567
|
+
this.next = void 0;
|
|
4568
|
+
}
|
|
4569
|
+
|
|
4570
|
+
return {
|
|
4571
|
+
add: function add(fn,self) {
|
|
4572
|
+
item = new Item(fn,self);
|
|
4573
|
+
if (last) {
|
|
4574
|
+
last.next = item;
|
|
4575
|
+
}
|
|
4576
|
+
else {
|
|
4577
|
+
first = item;
|
|
4578
|
+
}
|
|
4579
|
+
last = item;
|
|
4580
|
+
item = void 0;
|
|
4581
|
+
},
|
|
4582
|
+
drain: function drain() {
|
|
4583
|
+
var f = first;
|
|
4584
|
+
first = last = cycle = void 0;
|
|
4585
|
+
|
|
4586
|
+
while (f) {
|
|
4587
|
+
f.fn.call(f.self);
|
|
4588
|
+
f = f.next;
|
|
4589
|
+
}
|
|
4590
|
+
}
|
|
4591
|
+
};
|
|
4592
|
+
})();
|
|
4593
|
+
|
|
4594
|
+
function schedule(fn,self) {
|
|
4595
|
+
schedulingQueue.add(fn,self);
|
|
4596
|
+
if (!cycle) {
|
|
4597
|
+
cycle = timer(schedulingQueue.drain);
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
|
|
4601
|
+
// promise duck typing
|
|
4602
|
+
function isThenable(o) {
|
|
4603
|
+
var _then, oType = typeof o;
|
|
4604
|
+
|
|
4605
|
+
if (o !== null && (oType === 'object' || oType === 'function')) {
|
|
4606
|
+
_then = o.then;
|
|
4607
|
+
}
|
|
4608
|
+
return typeof _then === 'function' ? _then : false;
|
|
4609
|
+
}
|
|
4610
|
+
|
|
4611
|
+
function notify() {
|
|
4612
|
+
for (var i=0; i<this.chain.length; i++) {
|
|
4613
|
+
notifyIsolated(
|
|
4614
|
+
this,
|
|
4615
|
+
(this.state === 1) ? this.chain[i].success : this.chain[i].failure,
|
|
4616
|
+
this.chain[i]
|
|
4617
|
+
);
|
|
4618
|
+
}
|
|
4619
|
+
this.chain.length = 0;
|
|
4620
|
+
}
|
|
4621
|
+
|
|
4622
|
+
// NOTE: This is a separate function to isolate
|
|
4623
|
+
// the `try..catch` so that other code can be
|
|
4624
|
+
// optimized better
|
|
4625
|
+
function notifyIsolated(self,cb,chain) {
|
|
4626
|
+
var ret, _then;
|
|
4627
|
+
try {
|
|
4628
|
+
if (cb === false) {
|
|
4629
|
+
chain.reject(self.msg);
|
|
4630
|
+
}
|
|
4631
|
+
else {
|
|
4632
|
+
if (cb === true) {
|
|
4633
|
+
ret = self.msg;
|
|
4634
|
+
}
|
|
4635
|
+
else {
|
|
4636
|
+
ret = cb.call(void 0,self.msg);
|
|
4637
|
+
}
|
|
4638
|
+
|
|
4639
|
+
if (ret === chain.promise) {
|
|
4640
|
+
chain.reject(TypeError('Promise-chain cycle'));
|
|
4641
|
+
}
|
|
4642
|
+
// eslint-disable-next-line no-cond-assign
|
|
4643
|
+
else if (_then = isThenable(ret)) {
|
|
4644
|
+
_then.call(ret,chain.resolve,chain.reject);
|
|
4645
|
+
}
|
|
4646
|
+
else {
|
|
4647
|
+
chain.resolve(ret);
|
|
4648
|
+
}
|
|
4649
|
+
}
|
|
4650
|
+
}
|
|
4651
|
+
catch (err) {
|
|
4652
|
+
chain.reject(err);
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
|
|
4656
|
+
function resolve(msg) {
|
|
4657
|
+
var _then, self = this;
|
|
4658
|
+
|
|
4659
|
+
// already triggered?
|
|
4660
|
+
if (self.triggered) { return; }
|
|
4661
|
+
|
|
4662
|
+
self.triggered = true;
|
|
4663
|
+
|
|
4664
|
+
// unwrap
|
|
4665
|
+
if (self.def) {
|
|
4666
|
+
self = self.def;
|
|
4667
|
+
}
|
|
4668
|
+
|
|
4669
|
+
try {
|
|
4670
|
+
// eslint-disable-next-line no-cond-assign
|
|
4671
|
+
if (_then = isThenable(msg)) {
|
|
4672
|
+
schedule(function(){
|
|
4673
|
+
var defWrapper = new MakeDefWrapper(self);
|
|
4674
|
+
try {
|
|
4675
|
+
_then.call(msg,
|
|
4676
|
+
function $resolve$(){ resolve.apply(defWrapper,arguments); },
|
|
4677
|
+
function $reject$(){ reject.apply(defWrapper,arguments); }
|
|
4678
|
+
);
|
|
4679
|
+
}
|
|
4680
|
+
catch (err) {
|
|
4681
|
+
reject.call(defWrapper,err);
|
|
4682
|
+
}
|
|
4683
|
+
});
|
|
4684
|
+
}
|
|
4685
|
+
else {
|
|
4686
|
+
self.msg = msg;
|
|
4687
|
+
self.state = 1;
|
|
4688
|
+
if (self.chain.length > 0) {
|
|
4689
|
+
schedule(notify,self);
|
|
4690
|
+
}
|
|
4691
|
+
}
|
|
4692
|
+
}
|
|
4693
|
+
catch (err) {
|
|
4694
|
+
reject.call(new MakeDefWrapper(self),err);
|
|
4695
|
+
}
|
|
4696
|
+
}
|
|
4697
|
+
|
|
4698
|
+
function reject(msg) {
|
|
4699
|
+
var self = this;
|
|
4700
|
+
|
|
4701
|
+
// already triggered?
|
|
4702
|
+
if (self.triggered) { return; }
|
|
4703
|
+
|
|
4704
|
+
self.triggered = true;
|
|
4705
|
+
|
|
4706
|
+
// unwrap
|
|
4707
|
+
if (self.def) {
|
|
4708
|
+
self = self.def;
|
|
4709
|
+
}
|
|
4710
|
+
|
|
4711
|
+
self.msg = msg;
|
|
4712
|
+
self.state = 2;
|
|
4713
|
+
if (self.chain.length > 0) {
|
|
4714
|
+
schedule(notify,self);
|
|
4715
|
+
}
|
|
4716
|
+
}
|
|
4717
|
+
|
|
4718
|
+
function iteratePromises(Constructor,arr,resolver,rejecter) {
|
|
4719
|
+
for (var idx=0; idx<arr.length; idx++) {
|
|
4720
|
+
(function IIFE(idx){
|
|
4721
|
+
Constructor.resolve(arr[idx])
|
|
4722
|
+
.then(
|
|
4723
|
+
function $resolver$(msg){
|
|
4724
|
+
resolver(idx,msg);
|
|
4725
|
+
},
|
|
4726
|
+
rejecter
|
|
4727
|
+
);
|
|
4728
|
+
})(idx);
|
|
4729
|
+
}
|
|
4730
|
+
}
|
|
4731
|
+
|
|
4732
|
+
function MakeDefWrapper(self) {
|
|
4733
|
+
this.def = self;
|
|
4734
|
+
this.triggered = false;
|
|
4735
|
+
}
|
|
4736
|
+
|
|
4737
|
+
function MakeDef(self) {
|
|
4738
|
+
this.promise = self;
|
|
4739
|
+
this.state = 0;
|
|
4740
|
+
this.triggered = false;
|
|
4741
|
+
this.chain = [];
|
|
4742
|
+
this.msg = void 0;
|
|
4743
|
+
}
|
|
4744
|
+
|
|
4745
|
+
function NpoPromise(executor) {
|
|
4746
|
+
if (typeof executor !== 'function') {
|
|
4747
|
+
throw TypeError('Not a function');
|
|
4748
|
+
}
|
|
4749
|
+
|
|
4750
|
+
if (this['__NPO__'] !== 0) {
|
|
4751
|
+
throw TypeError('Not a promise');
|
|
4752
|
+
}
|
|
4753
|
+
|
|
4754
|
+
// instance shadowing the inherited "brand"
|
|
4755
|
+
// to signal an already "initialized" promise
|
|
4756
|
+
this['__NPO__'] = 1;
|
|
4757
|
+
|
|
4758
|
+
var def = new MakeDef(this);
|
|
4759
|
+
|
|
4760
|
+
this['then'] = function then(success,failure) {
|
|
4761
|
+
var o = {
|
|
4762
|
+
success: typeof success === 'function' ? success : true,
|
|
4763
|
+
failure: typeof failure === 'function' ? failure : false
|
|
4764
|
+
};
|
|
4765
|
+
// Note: `then(..)` itself can be borrowed to be used against
|
|
4766
|
+
// a different promise constructor for making the chained promise,
|
|
4767
|
+
// by substituting a different `this` binding.
|
|
4768
|
+
o.promise = new this.constructor(function extractChain(resolve,reject) {
|
|
4769
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
4770
|
+
throw TypeError('Not a function');
|
|
4771
|
+
}
|
|
4772
|
+
|
|
4773
|
+
o.resolve = resolve;
|
|
4774
|
+
o.reject = reject;
|
|
4775
|
+
});
|
|
4776
|
+
def.chain.push(o);
|
|
4777
|
+
|
|
4778
|
+
if (def.state !== 0) {
|
|
4779
|
+
schedule(notify,def);
|
|
4780
|
+
}
|
|
4781
|
+
|
|
4782
|
+
return o.promise;
|
|
4783
|
+
};
|
|
4784
|
+
this['catch'] = function $catch$(failure) {
|
|
4785
|
+
return this.then(void 0,failure);
|
|
4786
|
+
};
|
|
4787
|
+
|
|
4788
|
+
try {
|
|
4789
|
+
executor.call(
|
|
4790
|
+
void 0,
|
|
4791
|
+
function publicResolve(msg){
|
|
4792
|
+
resolve.call(def,msg);
|
|
4793
|
+
},
|
|
4794
|
+
function publicReject(msg) {
|
|
4795
|
+
reject.call(def,msg);
|
|
4796
|
+
}
|
|
4797
|
+
);
|
|
4798
|
+
}
|
|
4799
|
+
catch (err) {
|
|
4800
|
+
reject.call(def,err);
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
|
|
4804
|
+
var PromisePrototype = builtInProp({},'constructor',NpoPromise,
|
|
4805
|
+
/*configurable=*/false
|
|
4806
|
+
);
|
|
4807
|
+
|
|
4808
|
+
// Note: Android 4 cannot use `Object.defineProperty(..)` here
|
|
4809
|
+
NpoPromise.prototype = PromisePrototype;
|
|
4810
|
+
|
|
4811
|
+
// built-in "brand" to signal an "uninitialized" promise
|
|
4812
|
+
builtInProp(PromisePrototype,'__NPO__',0,
|
|
4813
|
+
/*configurable=*/false
|
|
4814
|
+
);
|
|
4815
|
+
|
|
4816
|
+
builtInProp(NpoPromise,'resolve',function Promise$resolve(msg) {
|
|
4817
|
+
var Constructor = this;
|
|
4818
|
+
|
|
4819
|
+
// spec mandated checks
|
|
4820
|
+
// note: best "isPromise" check that's practical for now
|
|
4821
|
+
if (msg && typeof msg === 'object' && msg['__NPO__'] === 1) {
|
|
4822
|
+
return msg;
|
|
4823
|
+
}
|
|
4824
|
+
|
|
4825
|
+
return new Constructor(function executor(resolve,reject){
|
|
4826
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
4827
|
+
throw TypeError('Not a function');
|
|
4828
|
+
}
|
|
4829
|
+
|
|
4830
|
+
resolve(msg);
|
|
4831
|
+
});
|
|
4832
|
+
});
|
|
4833
|
+
|
|
4834
|
+
builtInProp(NpoPromise,'reject',function Promise$reject(msg) {
|
|
4835
|
+
return new this(function executor(resolve,reject){
|
|
4836
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
4837
|
+
throw TypeError('Not a function');
|
|
4838
|
+
}
|
|
4839
|
+
|
|
4840
|
+
reject(msg);
|
|
4841
|
+
});
|
|
4842
|
+
});
|
|
4843
|
+
|
|
4844
|
+
builtInProp(NpoPromise,'all',function Promise$all(arr) {
|
|
4845
|
+
var Constructor = this;
|
|
4846
|
+
|
|
4847
|
+
// spec mandated checks
|
|
4848
|
+
if (ToString.call(arr) !== '[object Array]') {
|
|
4849
|
+
return Constructor.reject(TypeError('Not an array'));
|
|
4850
|
+
}
|
|
4851
|
+
if (arr.length === 0) {
|
|
4852
|
+
return Constructor.resolve([]);
|
|
4853
|
+
}
|
|
4854
|
+
|
|
4855
|
+
return new Constructor(function executor(resolve,reject){
|
|
4856
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
4857
|
+
throw TypeError('Not a function');
|
|
4858
|
+
}
|
|
4859
|
+
|
|
4860
|
+
var len = arr.length, msgs = Array(len), count = 0;
|
|
4861
|
+
|
|
4862
|
+
iteratePromises(Constructor,arr,function resolver(idx,msg) {
|
|
4863
|
+
msgs[idx] = msg;
|
|
4864
|
+
if (++count === len) {
|
|
4865
|
+
resolve(msgs);
|
|
4866
|
+
}
|
|
4867
|
+
},reject);
|
|
4868
|
+
});
|
|
4869
|
+
});
|
|
4870
|
+
|
|
4871
|
+
builtInProp(NpoPromise,'race',function Promise$race(arr) {
|
|
4872
|
+
var Constructor = this;
|
|
4873
|
+
|
|
4874
|
+
// spec mandated checks
|
|
4875
|
+
if (ToString.call(arr) !== '[object Array]') {
|
|
4876
|
+
return Constructor.reject(TypeError('Not an array'));
|
|
4877
|
+
}
|
|
4878
|
+
|
|
4879
|
+
return new Constructor(function executor(resolve,reject){
|
|
4880
|
+
if (typeof resolve !== 'function' || typeof reject !== 'function') {
|
|
4881
|
+
throw TypeError('Not a function');
|
|
4882
|
+
}
|
|
4883
|
+
|
|
4884
|
+
iteratePromises(Constructor,arr,function resolver(idx,msg){
|
|
4885
|
+
resolve(msg);
|
|
4886
|
+
},reject);
|
|
4887
|
+
});
|
|
4888
|
+
});
|
|
4889
|
+
|
|
4890
|
+
var PromisePolyfill;
|
|
4891
|
+
if (typeof Promise !== 'undefined' && Promise.toString().indexOf('[native code]') !== -1) {
|
|
4892
|
+
PromisePolyfill = Promise;
|
|
4893
|
+
} else {
|
|
4894
|
+
PromisePolyfill = NpoPromise;
|
|
4895
|
+
}
|
|
4896
|
+
|
|
4897
|
+
/* eslint camelcase: "off", eqeqeq: "off" */
|
|
4898
|
+
|
|
4537
4899
|
// Maximum allowed session recording length
|
|
4538
4900
|
var MAX_RECORDING_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
4539
4901
|
// Maximum allowed value for minimum session recording length
|
|
@@ -5566,7 +5928,7 @@
|
|
|
5566
5928
|
|
|
5567
5929
|
var supported = true;
|
|
5568
5930
|
try {
|
|
5569
|
-
storage = storage ||
|
|
5931
|
+
storage = storage || win.localStorage;
|
|
5570
5932
|
var key = '__mplss_' + cheap_guid(8),
|
|
5571
5933
|
val = 'xyz';
|
|
5572
5934
|
storage.setItem(key, val);
|
|
@@ -5598,7 +5960,7 @@
|
|
|
5598
5960
|
|
|
5599
5961
|
get: function(name) {
|
|
5600
5962
|
try {
|
|
5601
|
-
return
|
|
5963
|
+
return win.localStorage.getItem(name);
|
|
5602
5964
|
} catch (err) {
|
|
5603
5965
|
_.localStorage.error(err);
|
|
5604
5966
|
}
|
|
@@ -5616,7 +5978,7 @@
|
|
|
5616
5978
|
|
|
5617
5979
|
set: function(name, value) {
|
|
5618
5980
|
try {
|
|
5619
|
-
|
|
5981
|
+
win.localStorage.setItem(name, value);
|
|
5620
5982
|
} catch (err) {
|
|
5621
5983
|
_.localStorage.error(err);
|
|
5622
5984
|
}
|
|
@@ -5624,7 +5986,7 @@
|
|
|
5624
5986
|
|
|
5625
5987
|
remove: function(name) {
|
|
5626
5988
|
try {
|
|
5627
|
-
|
|
5989
|
+
win.localStorage.removeItem(name);
|
|
5628
5990
|
} catch (err) {
|
|
5629
5991
|
_.localStorage.error(err);
|
|
5630
5992
|
}
|
|
@@ -5663,7 +6025,7 @@
|
|
|
5663
6025
|
|
|
5664
6026
|
function makeHandler(element, new_handler, old_handlers) {
|
|
5665
6027
|
var handler = function(event) {
|
|
5666
|
-
event = event || fixEvent(
|
|
6028
|
+
event = event || fixEvent(win.event);
|
|
5667
6029
|
|
|
5668
6030
|
// this basically happens in firefox whenever another script
|
|
5669
6031
|
// overwrites the onload callback and doesn't pass the event
|
|
@@ -6218,6 +6580,7 @@
|
|
|
6218
6580
|
_['info']['browser'] = _.info.browser;
|
|
6219
6581
|
_['info']['browserVersion'] = _.info.browserVersion;
|
|
6220
6582
|
_['info']['properties'] = _.info.properties;
|
|
6583
|
+
_['NPO'] = NpoPromise;
|
|
6221
6584
|
|
|
6222
6585
|
/**
|
|
6223
6586
|
* GDPR utils
|
|
@@ -6415,121 +6778,175 @@
|
|
|
6415
6778
|
this.storage = options.storage || window.localStorage;
|
|
6416
6779
|
this.pollIntervalMS = options.pollIntervalMS || 100;
|
|
6417
6780
|
this.timeoutMS = options.timeoutMS || 2000;
|
|
6781
|
+
|
|
6782
|
+
// dependency-inject promise implementation for testing purposes
|
|
6783
|
+
this.promiseImpl = options.promiseImpl || PromisePolyfill;
|
|
6418
6784
|
};
|
|
6419
6785
|
|
|
6420
6786
|
// pass in a specific pid to test contention scenarios; otherwise
|
|
6421
6787
|
// it is chosen randomly for each acquisition attempt
|
|
6422
|
-
SharedLock.prototype.withLock = function(lockedCB,
|
|
6423
|
-
|
|
6424
|
-
|
|
6425
|
-
|
|
6426
|
-
|
|
6427
|
-
|
|
6428
|
-
var i = pid || (new Date().getTime() + '|' + Math.random());
|
|
6429
|
-
var startTime = new Date().getTime();
|
|
6788
|
+
SharedLock.prototype.withLock = function(lockedCB, pid) {
|
|
6789
|
+
var Promise = this.promiseImpl;
|
|
6790
|
+
return new Promise(_.bind(function (resolve, reject) {
|
|
6791
|
+
var i = pid || (new Date().getTime() + '|' + Math.random());
|
|
6792
|
+
var startTime = new Date().getTime();
|
|
6430
6793
|
|
|
6431
|
-
|
|
6432
|
-
|
|
6433
|
-
|
|
6434
|
-
|
|
6794
|
+
var key = this.storageKey;
|
|
6795
|
+
var pollIntervalMS = this.pollIntervalMS;
|
|
6796
|
+
var timeoutMS = this.timeoutMS;
|
|
6797
|
+
var storage = this.storage;
|
|
6435
6798
|
|
|
6436
|
-
|
|
6437
|
-
|
|
6438
|
-
|
|
6799
|
+
var keyX = key + ':X';
|
|
6800
|
+
var keyY = key + ':Y';
|
|
6801
|
+
var keyZ = key + ':Z';
|
|
6439
6802
|
|
|
6440
|
-
|
|
6441
|
-
|
|
6442
|
-
|
|
6803
|
+
var delay = function(cb) {
|
|
6804
|
+
if (new Date().getTime() - startTime > timeoutMS) {
|
|
6805
|
+
logger$4.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
|
|
6806
|
+
storage.removeItem(keyZ);
|
|
6807
|
+
storage.removeItem(keyY);
|
|
6808
|
+
loop();
|
|
6809
|
+
return;
|
|
6810
|
+
}
|
|
6811
|
+
setTimeout(function() {
|
|
6812
|
+
try {
|
|
6813
|
+
cb();
|
|
6814
|
+
} catch(err) {
|
|
6815
|
+
reject(err);
|
|
6816
|
+
}
|
|
6817
|
+
}, pollIntervalMS * (Math.random() + 0.1));
|
|
6818
|
+
};
|
|
6443
6819
|
|
|
6444
|
-
|
|
6445
|
-
|
|
6446
|
-
logger$4.error('Timeout waiting for mutex on ' + key + '; clearing lock. [' + i + ']');
|
|
6447
|
-
storage.removeItem(keyZ);
|
|
6448
|
-
storage.removeItem(keyY);
|
|
6449
|
-
loop();
|
|
6450
|
-
return;
|
|
6451
|
-
}
|
|
6452
|
-
setTimeout(function() {
|
|
6453
|
-
try {
|
|
6820
|
+
var waitFor = function(predicate, cb) {
|
|
6821
|
+
if (predicate()) {
|
|
6454
6822
|
cb();
|
|
6455
|
-
}
|
|
6456
|
-
|
|
6823
|
+
} else {
|
|
6824
|
+
delay(function() {
|
|
6825
|
+
waitFor(predicate, cb);
|
|
6826
|
+
});
|
|
6457
6827
|
}
|
|
6458
|
-
}
|
|
6459
|
-
};
|
|
6460
|
-
|
|
6461
|
-
var waitFor = function(predicate, cb) {
|
|
6462
|
-
if (predicate()) {
|
|
6463
|
-
cb();
|
|
6464
|
-
} else {
|
|
6465
|
-
delay(function() {
|
|
6466
|
-
waitFor(predicate, cb);
|
|
6467
|
-
});
|
|
6468
|
-
}
|
|
6469
|
-
};
|
|
6828
|
+
};
|
|
6470
6829
|
|
|
6471
|
-
|
|
6472
|
-
|
|
6473
|
-
|
|
6474
|
-
|
|
6475
|
-
} else {
|
|
6476
|
-
storage.setItem(keyY, i);
|
|
6477
|
-
if (storage.getItem(keyY) === i) {
|
|
6478
|
-
return true;
|
|
6830
|
+
var getSetY = function() {
|
|
6831
|
+
var valY = storage.getItem(keyY);
|
|
6832
|
+
if (valY && valY !== i) { // if Y == i then this process already has the lock (useful for test cases)
|
|
6833
|
+
return false;
|
|
6479
6834
|
} else {
|
|
6480
|
-
|
|
6481
|
-
|
|
6835
|
+
storage.setItem(keyY, i);
|
|
6836
|
+
if (storage.getItem(keyY) === i) {
|
|
6837
|
+
return true;
|
|
6838
|
+
} else {
|
|
6839
|
+
if (!localStorageSupported(storage, true)) {
|
|
6840
|
+
reject(new Error('localStorage support dropped while acquiring lock'));
|
|
6841
|
+
}
|
|
6842
|
+
return false;
|
|
6482
6843
|
}
|
|
6483
|
-
return false;
|
|
6484
6844
|
}
|
|
6485
|
-
}
|
|
6486
|
-
};
|
|
6487
|
-
|
|
6488
|
-
var loop = function() {
|
|
6489
|
-
storage.setItem(keyX, i);
|
|
6845
|
+
};
|
|
6490
6846
|
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
criticalSection();
|
|
6494
|
-
return;
|
|
6495
|
-
}
|
|
6847
|
+
var loop = function() {
|
|
6848
|
+
storage.setItem(keyX, i);
|
|
6496
6849
|
|
|
6497
|
-
|
|
6498
|
-
if (storage.getItem(
|
|
6499
|
-
|
|
6850
|
+
waitFor(getSetY, function() {
|
|
6851
|
+
if (storage.getItem(keyX) === i) {
|
|
6852
|
+
criticalSection();
|
|
6500
6853
|
return;
|
|
6501
6854
|
}
|
|
6502
|
-
|
|
6503
|
-
|
|
6504
|
-
|
|
6855
|
+
|
|
6856
|
+
delay(function() {
|
|
6857
|
+
if (storage.getItem(keyY) !== i) {
|
|
6858
|
+
loop();
|
|
6859
|
+
return;
|
|
6860
|
+
}
|
|
6861
|
+
waitFor(function() {
|
|
6862
|
+
return !storage.getItem(keyZ);
|
|
6863
|
+
}, criticalSection);
|
|
6864
|
+
});
|
|
6505
6865
|
});
|
|
6506
|
-
}
|
|
6507
|
-
|
|
6866
|
+
};
|
|
6867
|
+
|
|
6868
|
+
var criticalSection = function() {
|
|
6869
|
+
storage.setItem(keyZ, '1');
|
|
6870
|
+
var removeLock = function () {
|
|
6871
|
+
storage.removeItem(keyZ);
|
|
6872
|
+
if (storage.getItem(keyY) === i) {
|
|
6873
|
+
storage.removeItem(keyY);
|
|
6874
|
+
}
|
|
6875
|
+
if (storage.getItem(keyX) === i) {
|
|
6876
|
+
storage.removeItem(keyX);
|
|
6877
|
+
}
|
|
6878
|
+
};
|
|
6879
|
+
|
|
6880
|
+
lockedCB()
|
|
6881
|
+
.then(function (ret) {
|
|
6882
|
+
removeLock();
|
|
6883
|
+
resolve(ret);
|
|
6884
|
+
})
|
|
6885
|
+
.catch(function (err) {
|
|
6886
|
+
removeLock();
|
|
6887
|
+
reject(err);
|
|
6888
|
+
});
|
|
6889
|
+
};
|
|
6508
6890
|
|
|
6509
|
-
var criticalSection = function() {
|
|
6510
|
-
storage.setItem(keyZ, '1');
|
|
6511
6891
|
try {
|
|
6512
|
-
|
|
6513
|
-
|
|
6514
|
-
|
|
6515
|
-
|
|
6516
|
-
storage.removeItem(keyY);
|
|
6517
|
-
}
|
|
6518
|
-
if (storage.getItem(keyX) === i) {
|
|
6519
|
-
storage.removeItem(keyX);
|
|
6892
|
+
if (localStorageSupported(storage, true)) {
|
|
6893
|
+
loop();
|
|
6894
|
+
} else {
|
|
6895
|
+
throw new Error('localStorage support check failed');
|
|
6520
6896
|
}
|
|
6897
|
+
} catch(err) {
|
|
6898
|
+
reject(err);
|
|
6521
6899
|
}
|
|
6522
|
-
};
|
|
6900
|
+
}, this));
|
|
6901
|
+
};
|
|
6523
6902
|
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6903
|
+
/**
|
|
6904
|
+
* @typedef {import('./wrapper').StorageWrapper}
|
|
6905
|
+
*/
|
|
6906
|
+
|
|
6907
|
+
/**
|
|
6908
|
+
* @type {StorageWrapper}
|
|
6909
|
+
*/
|
|
6910
|
+
var LocalStorageWrapper = function (storageOverride) {
|
|
6911
|
+
this.storage = storageOverride || localStorage;
|
|
6912
|
+
};
|
|
6913
|
+
|
|
6914
|
+
LocalStorageWrapper.prototype.init = function () {
|
|
6915
|
+
return PromisePolyfill.resolve();
|
|
6916
|
+
};
|
|
6917
|
+
|
|
6918
|
+
LocalStorageWrapper.prototype.setItem = function (key, value) {
|
|
6919
|
+
return new PromisePolyfill(_.bind(function (resolve, reject) {
|
|
6920
|
+
try {
|
|
6921
|
+
this.storage.setItem(key, value);
|
|
6922
|
+
} catch (e) {
|
|
6923
|
+
reject(e);
|
|
6529
6924
|
}
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6925
|
+
resolve();
|
|
6926
|
+
}, this));
|
|
6927
|
+
};
|
|
6928
|
+
|
|
6929
|
+
LocalStorageWrapper.prototype.getItem = function (key) {
|
|
6930
|
+
return new PromisePolyfill(_.bind(function (resolve, reject) {
|
|
6931
|
+
var item;
|
|
6932
|
+
try {
|
|
6933
|
+
item = this.storage.getItem(key);
|
|
6934
|
+
} catch (e) {
|
|
6935
|
+
reject(e);
|
|
6936
|
+
}
|
|
6937
|
+
resolve(item);
|
|
6938
|
+
}, this));
|
|
6939
|
+
};
|
|
6940
|
+
|
|
6941
|
+
LocalStorageWrapper.prototype.removeItem = function (key) {
|
|
6942
|
+
return new PromisePolyfill(_.bind(function (resolve, reject) {
|
|
6943
|
+
try {
|
|
6944
|
+
this.storage.removeItem(key);
|
|
6945
|
+
} catch (e) {
|
|
6946
|
+
reject(e);
|
|
6947
|
+
}
|
|
6948
|
+
resolve();
|
|
6949
|
+
}, this));
|
|
6533
6950
|
};
|
|
6534
6951
|
|
|
6535
6952
|
var logger$3 = console_with_prefix('batch');
|
|
@@ -6550,19 +6967,38 @@
|
|
|
6550
6967
|
* to data loss in some situations).
|
|
6551
6968
|
* @constructor
|
|
6552
6969
|
*/
|
|
6553
|
-
var RequestQueue = function(storageKey, options) {
|
|
6970
|
+
var RequestQueue = function (storageKey, options) {
|
|
6554
6971
|
options = options || {};
|
|
6555
6972
|
this.storageKey = storageKey;
|
|
6556
6973
|
this.usePersistence = options.usePersistence;
|
|
6557
6974
|
if (this.usePersistence) {
|
|
6558
|
-
this.
|
|
6559
|
-
this.lock = new SharedLock(storageKey, {storage:
|
|
6975
|
+
this.queueStorage = options.queueStorage || new LocalStorageWrapper();
|
|
6976
|
+
this.lock = new SharedLock(storageKey, { storage: options.sharedLockStorage || window.localStorage });
|
|
6977
|
+
this.queueStorage.init();
|
|
6560
6978
|
}
|
|
6561
6979
|
this.reportError = options.errorReporter || _.bind(logger$3.error, logger$3);
|
|
6562
6980
|
|
|
6563
6981
|
this.pid = options.pid || null; // pass pid to test out storage lock contention scenarios
|
|
6564
6982
|
|
|
6565
6983
|
this.memQueue = [];
|
|
6984
|
+
this.initialized = false;
|
|
6985
|
+
};
|
|
6986
|
+
|
|
6987
|
+
RequestQueue.prototype.ensureInit = function () {
|
|
6988
|
+
if (this.initialized) {
|
|
6989
|
+
return PromisePolyfill.resolve();
|
|
6990
|
+
}
|
|
6991
|
+
|
|
6992
|
+
return this.queueStorage
|
|
6993
|
+
.init()
|
|
6994
|
+
.then(_.bind(function () {
|
|
6995
|
+
this.initialized = true;
|
|
6996
|
+
}, this))
|
|
6997
|
+
.catch(_.bind(function (err) {
|
|
6998
|
+
this.reportError('Error initializing queue persistence. Disabling persistence', err);
|
|
6999
|
+
this.initialized = true;
|
|
7000
|
+
this.usePersistence = false;
|
|
7001
|
+
}, this));
|
|
6566
7002
|
};
|
|
6567
7003
|
|
|
6568
7004
|
/**
|
|
@@ -6577,7 +7013,7 @@
|
|
|
6577
7013
|
* failure of the enqueue operation; it is asynchronous because the localStorage
|
|
6578
7014
|
* lock is asynchronous.
|
|
6579
7015
|
*/
|
|
6580
|
-
RequestQueue.prototype.enqueue = function(item, flushInterval
|
|
7016
|
+
RequestQueue.prototype.enqueue = function (item, flushInterval) {
|
|
6581
7017
|
var queueEntry = {
|
|
6582
7018
|
'id': cheap_guid(),
|
|
6583
7019
|
'flushAfter': new Date().getTime() + flushInterval * 2,
|
|
@@ -6586,33 +7022,37 @@
|
|
|
6586
7022
|
|
|
6587
7023
|
if (!this.usePersistence) {
|
|
6588
7024
|
this.memQueue.push(queueEntry);
|
|
6589
|
-
|
|
6590
|
-
cb(true);
|
|
6591
|
-
}
|
|
7025
|
+
return PromisePolyfill.resolve(true);
|
|
6592
7026
|
} else {
|
|
6593
|
-
|
|
6594
|
-
|
|
6595
|
-
|
|
6596
|
-
|
|
6597
|
-
|
|
6598
|
-
|
|
6599
|
-
|
|
7027
|
+
|
|
7028
|
+
var enqueueItem = _.bind(function () {
|
|
7029
|
+
return this.ensureInit()
|
|
7030
|
+
.then(_.bind(function () {
|
|
7031
|
+
return this.readFromStorage();
|
|
7032
|
+
}, this))
|
|
7033
|
+
.then(_.bind(function (storedQueue) {
|
|
7034
|
+
storedQueue.push(queueEntry);
|
|
7035
|
+
return this.saveToStorage(storedQueue);
|
|
7036
|
+
}, this))
|
|
7037
|
+
.then(_.bind(function (succeeded) {
|
|
6600
7038
|
// only add to in-memory queue when storage succeeds
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
}, this)
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
7039
|
+
if (succeeded) {
|
|
7040
|
+
this.memQueue.push(queueEntry);
|
|
7041
|
+
}
|
|
7042
|
+
return succeeded;
|
|
7043
|
+
}, this))
|
|
7044
|
+
.catch(_.bind(function (err) {
|
|
7045
|
+
this.reportError('Error enqueueing item', err, item);
|
|
7046
|
+
return false;
|
|
7047
|
+
}, this));
|
|
7048
|
+
}, this);
|
|
7049
|
+
|
|
7050
|
+
return this.lock
|
|
7051
|
+
.withLock(enqueueItem, this.pid)
|
|
7052
|
+
.catch(_.bind(function (err) {
|
|
7053
|
+
this.reportError('Error acquiring storage lock', err);
|
|
7054
|
+
return false;
|
|
7055
|
+
}, this));
|
|
6616
7056
|
}
|
|
6617
7057
|
};
|
|
6618
7058
|
|
|
@@ -6622,31 +7062,41 @@
|
|
|
6622
7062
|
* in the persisted queue (items where the 'flushAfter' time has
|
|
6623
7063
|
* already passed).
|
|
6624
7064
|
*/
|
|
6625
|
-
RequestQueue.prototype.fillBatch = function(batchSize) {
|
|
7065
|
+
RequestQueue.prototype.fillBatch = function (batchSize) {
|
|
6626
7066
|
var batch = this.memQueue.slice(0, batchSize);
|
|
6627
7067
|
if (this.usePersistence && batch.length < batchSize) {
|
|
6628
7068
|
// don't need lock just to read events; localStorage is thread-safe
|
|
6629
7069
|
// and the worst that could happen is a duplicate send of some
|
|
6630
7070
|
// orphaned events, which will be deduplicated on the server side
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
_.
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
7071
|
+
return this.ensureInit()
|
|
7072
|
+
.then(_.bind(function () {
|
|
7073
|
+
return this.readFromStorage();
|
|
7074
|
+
}, this))
|
|
7075
|
+
.then(_.bind(function (storedQueue) {
|
|
7076
|
+
if (storedQueue.length) {
|
|
7077
|
+
// item IDs already in batch; don't duplicate out of storage
|
|
7078
|
+
var idsInBatch = {}; // poor man's Set
|
|
7079
|
+
_.each(batch, function (item) {
|
|
7080
|
+
idsInBatch[item['id']] = true;
|
|
7081
|
+
});
|
|
7082
|
+
|
|
7083
|
+
for (var i = 0; i < storedQueue.length; i++) {
|
|
7084
|
+
var item = storedQueue[i];
|
|
7085
|
+
if (new Date().getTime() > item['flushAfter'] && !idsInBatch[item['id']]) {
|
|
7086
|
+
item.orphaned = true;
|
|
7087
|
+
batch.push(item);
|
|
7088
|
+
if (batch.length >= batchSize) {
|
|
7089
|
+
break;
|
|
7090
|
+
}
|
|
7091
|
+
}
|
|
6644
7092
|
}
|
|
6645
7093
|
}
|
|
6646
|
-
|
|
6647
|
-
|
|
7094
|
+
|
|
7095
|
+
return batch;
|
|
7096
|
+
}, this));
|
|
7097
|
+
} else {
|
|
7098
|
+
return PromisePolyfill.resolve(batch);
|
|
6648
7099
|
}
|
|
6649
|
-
return batch;
|
|
6650
7100
|
};
|
|
6651
7101
|
|
|
6652
7102
|
/**
|
|
@@ -6654,9 +7104,9 @@
|
|
|
6654
7104
|
* also remove any item without a valid id (e.g., malformed
|
|
6655
7105
|
* storage entries).
|
|
6656
7106
|
*/
|
|
6657
|
-
var filterOutIDsAndInvalid = function(items, idSet) {
|
|
7107
|
+
var filterOutIDsAndInvalid = function (items, idSet) {
|
|
6658
7108
|
var filteredItems = [];
|
|
6659
|
-
_.each(items, function(item) {
|
|
7109
|
+
_.each(items, function (item) {
|
|
6660
7110
|
if (item['id'] && !idSet[item['id']]) {
|
|
6661
7111
|
filteredItems.push(item);
|
|
6662
7112
|
}
|
|
@@ -6668,78 +7118,80 @@
|
|
|
6668
7118
|
* Remove items with matching IDs from both in-memory queue
|
|
6669
7119
|
* and persisted queue
|
|
6670
7120
|
*/
|
|
6671
|
-
RequestQueue.prototype.removeItemsByID = function(ids
|
|
7121
|
+
RequestQueue.prototype.removeItemsByID = function (ids) {
|
|
6672
7122
|
var idSet = {}; // poor man's Set
|
|
6673
|
-
_.each(ids, function(id) {
|
|
7123
|
+
_.each(ids, function (id) {
|
|
7124
|
+
idSet[id] = true;
|
|
7125
|
+
});
|
|
6674
7126
|
|
|
6675
7127
|
this.memQueue = filterOutIDsAndInvalid(this.memQueue, idSet);
|
|
6676
7128
|
if (!this.usePersistence) {
|
|
6677
|
-
|
|
6678
|
-
cb(true);
|
|
6679
|
-
}
|
|
7129
|
+
return PromisePolyfill.resolve(true);
|
|
6680
7130
|
} else {
|
|
6681
|
-
var removeFromStorage = _.bind(function() {
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
7131
|
+
var removeFromStorage = _.bind(function () {
|
|
7132
|
+
return this.ensureInit()
|
|
7133
|
+
.then(_.bind(function () {
|
|
7134
|
+
return this.readFromStorage();
|
|
7135
|
+
}, this))
|
|
7136
|
+
.then(_.bind(function (storedQueue) {
|
|
7137
|
+
storedQueue = filterOutIDsAndInvalid(storedQueue, idSet);
|
|
7138
|
+
return this.saveToStorage(storedQueue);
|
|
7139
|
+
}, this))
|
|
7140
|
+
.then(_.bind(function () {
|
|
7141
|
+
return this.readFromStorage();
|
|
7142
|
+
}, this))
|
|
7143
|
+
.then(_.bind(function (storedQueue) {
|
|
7144
|
+
// an extra check: did storage report success but somehow
|
|
7145
|
+
// the items are still there?
|
|
6692
7146
|
for (var i = 0; i < storedQueue.length; i++) {
|
|
6693
7147
|
var item = storedQueue[i];
|
|
6694
7148
|
if (item['id'] && !!idSet[item['id']]) {
|
|
6695
|
-
|
|
6696
|
-
return false;
|
|
7149
|
+
throw new Error('Item not removed from storage');
|
|
6697
7150
|
}
|
|
6698
7151
|
}
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
7152
|
+
return true;
|
|
7153
|
+
}, this))
|
|
7154
|
+
.catch(_.bind(function (err) {
|
|
7155
|
+
this.reportError('Error removing items', err, ids);
|
|
7156
|
+
return false;
|
|
7157
|
+
}, this));
|
|
6705
7158
|
}, this);
|
|
6706
7159
|
|
|
6707
|
-
this.lock
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6721
|
-
|
|
6722
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
7160
|
+
return this.lock
|
|
7161
|
+
.withLock(removeFromStorage, this.pid)
|
|
7162
|
+
.catch(_.bind(function (err) {
|
|
7163
|
+
this.reportError('Error acquiring storage lock', err);
|
|
7164
|
+
if (!localStorageSupported(this.queueStorage.storage, true)) {
|
|
7165
|
+
// Looks like localStorage writes have stopped working sometime after
|
|
7166
|
+
// initialization (probably full), and so nobody can acquire locks
|
|
7167
|
+
// anymore. Consider it temporarily safe to remove items without the
|
|
7168
|
+
// lock, since nobody's writing successfully anyway.
|
|
7169
|
+
return removeFromStorage()
|
|
7170
|
+
.then(_.bind(function (success) {
|
|
7171
|
+
if (!success) {
|
|
7172
|
+
// OK, we couldn't even write out the smaller queue. Try clearing it
|
|
7173
|
+
// entirely.
|
|
7174
|
+
return this.queueStorage.removeItem(this.storageKey).then(function () {
|
|
7175
|
+
return success;
|
|
7176
|
+
});
|
|
7177
|
+
}
|
|
7178
|
+
return success;
|
|
7179
|
+
}, this))
|
|
7180
|
+
.catch(_.bind(function (err) {
|
|
7181
|
+
this.reportError('Error clearing queue', err);
|
|
7182
|
+
return false;
|
|
7183
|
+
}, this));
|
|
7184
|
+
} else {
|
|
7185
|
+
return false;
|
|
6729
7186
|
}
|
|
6730
|
-
}
|
|
6731
|
-
if (cb) {
|
|
6732
|
-
cb(succeeded);
|
|
6733
|
-
}
|
|
6734
|
-
}, this), this.pid);
|
|
7187
|
+
}, this));
|
|
6735
7188
|
}
|
|
6736
|
-
|
|
6737
7189
|
};
|
|
6738
7190
|
|
|
6739
7191
|
// internal helper for RequestQueue.updatePayloads
|
|
6740
|
-
var updatePayloads = function(existingItems, itemsToUpdate) {
|
|
7192
|
+
var updatePayloads = function (existingItems, itemsToUpdate) {
|
|
6741
7193
|
var newItems = [];
|
|
6742
|
-
_.each(existingItems, function(item) {
|
|
7194
|
+
_.each(existingItems, function (item) {
|
|
6743
7195
|
var id = item['id'];
|
|
6744
7196
|
if (id in itemsToUpdate) {
|
|
6745
7197
|
var newPayload = itemsToUpdate[id];
|
|
@@ -6759,79 +7211,95 @@
|
|
|
6759
7211
|
* Update payloads of given items in both in-memory queue and
|
|
6760
7212
|
* persisted queue. Items set to null are removed from queues.
|
|
6761
7213
|
*/
|
|
6762
|
-
RequestQueue.prototype.updatePayloads = function(itemsToUpdate
|
|
7214
|
+
RequestQueue.prototype.updatePayloads = function (itemsToUpdate) {
|
|
6763
7215
|
this.memQueue = updatePayloads(this.memQueue, itemsToUpdate);
|
|
6764
7216
|
if (!this.usePersistence) {
|
|
6765
|
-
|
|
6766
|
-
cb(true);
|
|
6767
|
-
}
|
|
7217
|
+
return PromisePolyfill.resolve(true);
|
|
6768
7218
|
} else {
|
|
6769
|
-
this.lock
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
|
|
6773
|
-
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6781
|
-
|
|
6782
|
-
|
|
6783
|
-
this
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
7219
|
+
return this.lock
|
|
7220
|
+
.withLock(_.bind(function lockAcquired() {
|
|
7221
|
+
return this.ensureInit()
|
|
7222
|
+
.then(_.bind(function () {
|
|
7223
|
+
return this.readFromStorage();
|
|
7224
|
+
}, this))
|
|
7225
|
+
.then(_.bind(function (storedQueue) {
|
|
7226
|
+
storedQueue = updatePayloads(storedQueue, itemsToUpdate);
|
|
7227
|
+
return this.saveToStorage(storedQueue);
|
|
7228
|
+
}, this))
|
|
7229
|
+
.catch(_.bind(function (err) {
|
|
7230
|
+
this.reportError('Error updating items', itemsToUpdate, err);
|
|
7231
|
+
return false;
|
|
7232
|
+
}, this));
|
|
7233
|
+
}, this), this.pid)
|
|
7234
|
+
.catch(_.bind(function (err) {
|
|
7235
|
+
this.reportError('Error acquiring storage lock', err);
|
|
7236
|
+
return false;
|
|
7237
|
+
}, this));
|
|
6788
7238
|
}
|
|
6789
|
-
|
|
6790
7239
|
};
|
|
6791
7240
|
|
|
6792
7241
|
/**
|
|
6793
7242
|
* Read and parse items array from localStorage entry, handling
|
|
6794
7243
|
* malformed/missing data if necessary.
|
|
6795
7244
|
*/
|
|
6796
|
-
RequestQueue.prototype.readFromStorage = function() {
|
|
6797
|
-
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
if (
|
|
6803
|
-
|
|
6804
|
-
storageEntry
|
|
7245
|
+
RequestQueue.prototype.readFromStorage = function () {
|
|
7246
|
+
return this.ensureInit()
|
|
7247
|
+
.then(_.bind(function () {
|
|
7248
|
+
return this.queueStorage.getItem(this.storageKey);
|
|
7249
|
+
}, this))
|
|
7250
|
+
.then(_.bind(function (storageEntry) {
|
|
7251
|
+
if (storageEntry) {
|
|
7252
|
+
storageEntry = JSONParse(storageEntry);
|
|
7253
|
+
if (!_.isArray(storageEntry)) {
|
|
7254
|
+
this.reportError('Invalid storage entry:', storageEntry);
|
|
7255
|
+
storageEntry = null;
|
|
7256
|
+
}
|
|
6805
7257
|
}
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
7258
|
+
return storageEntry || [];
|
|
7259
|
+
}, this))
|
|
7260
|
+
.catch(_.bind(function (err) {
|
|
7261
|
+
this.reportError('Error retrieving queue', err);
|
|
7262
|
+
return [];
|
|
7263
|
+
}, this));
|
|
6812
7264
|
};
|
|
6813
7265
|
|
|
6814
7266
|
/**
|
|
6815
7267
|
* Serialize the given items array to localStorage.
|
|
6816
7268
|
*/
|
|
6817
|
-
RequestQueue.prototype.saveToStorage = function(queue) {
|
|
7269
|
+
RequestQueue.prototype.saveToStorage = function (queue) {
|
|
6818
7270
|
try {
|
|
6819
|
-
|
|
6820
|
-
return true;
|
|
7271
|
+
var serialized = JSONStringify(queue);
|
|
6821
7272
|
} catch (err) {
|
|
6822
|
-
this.reportError('Error
|
|
6823
|
-
return false;
|
|
7273
|
+
this.reportError('Error serializing queue', err);
|
|
7274
|
+
return PromisePolyfill.resolve(false);
|
|
6824
7275
|
}
|
|
7276
|
+
|
|
7277
|
+
return this.ensureInit()
|
|
7278
|
+
.then(_.bind(function () {
|
|
7279
|
+
return this.queueStorage.setItem(this.storageKey, serialized);
|
|
7280
|
+
}, this))
|
|
7281
|
+
.then(function () {
|
|
7282
|
+
return true;
|
|
7283
|
+
})
|
|
7284
|
+
.catch(_.bind(function (err) {
|
|
7285
|
+
this.reportError('Error saving queue', err);
|
|
7286
|
+
return false;
|
|
7287
|
+
}, this));
|
|
6825
7288
|
};
|
|
6826
7289
|
|
|
6827
7290
|
/**
|
|
6828
7291
|
* Clear out queues (memory and localStorage).
|
|
6829
7292
|
*/
|
|
6830
|
-
RequestQueue.prototype.clear = function() {
|
|
7293
|
+
RequestQueue.prototype.clear = function () {
|
|
6831
7294
|
this.memQueue = [];
|
|
6832
7295
|
|
|
6833
7296
|
if (this.usePersistence) {
|
|
6834
|
-
this.
|
|
7297
|
+
return this.ensureInit()
|
|
7298
|
+
.then(_.bind(function () {
|
|
7299
|
+
return this.queueStorage.removeItem(this.storageKey);
|
|
7300
|
+
}, this));
|
|
7301
|
+
} else {
|
|
7302
|
+
return PromisePolyfill.resolve();
|
|
6835
7303
|
}
|
|
6836
7304
|
};
|
|
6837
7305
|
|
|
@@ -6850,7 +7318,8 @@
|
|
|
6850
7318
|
this.errorReporter = options.errorReporter;
|
|
6851
7319
|
this.queue = new RequestQueue(storageKey, {
|
|
6852
7320
|
errorReporter: _.bind(this.reportError, this),
|
|
6853
|
-
|
|
7321
|
+
queueStorage: options.queueStorage,
|
|
7322
|
+
sharedLockStorage: options.sharedLockStorage,
|
|
6854
7323
|
usePersistence: options.usePersistence
|
|
6855
7324
|
});
|
|
6856
7325
|
|
|
@@ -6878,8 +7347,8 @@
|
|
|
6878
7347
|
/**
|
|
6879
7348
|
* Add one item to queue.
|
|
6880
7349
|
*/
|
|
6881
|
-
RequestBatcher.prototype.enqueue = function(item
|
|
6882
|
-
this.queue.enqueue(item, this.flushInterval
|
|
7350
|
+
RequestBatcher.prototype.enqueue = function(item) {
|
|
7351
|
+
return this.queue.enqueue(item, this.flushInterval);
|
|
6883
7352
|
};
|
|
6884
7353
|
|
|
6885
7354
|
/**
|
|
@@ -6889,7 +7358,7 @@
|
|
|
6889
7358
|
RequestBatcher.prototype.start = function() {
|
|
6890
7359
|
this.stopped = false;
|
|
6891
7360
|
this.consecutiveRemovalFailures = 0;
|
|
6892
|
-
this.flush();
|
|
7361
|
+
return this.flush();
|
|
6893
7362
|
};
|
|
6894
7363
|
|
|
6895
7364
|
/**
|
|
@@ -6907,7 +7376,7 @@
|
|
|
6907
7376
|
* Clear out queue.
|
|
6908
7377
|
*/
|
|
6909
7378
|
RequestBatcher.prototype.clear = function() {
|
|
6910
|
-
this.queue.clear();
|
|
7379
|
+
return this.queue.clear();
|
|
6911
7380
|
};
|
|
6912
7381
|
|
|
6913
7382
|
/**
|
|
@@ -6938,6 +7407,17 @@
|
|
|
6938
7407
|
}
|
|
6939
7408
|
};
|
|
6940
7409
|
|
|
7410
|
+
/**
|
|
7411
|
+
* Send a request using the sendRequest callback, but promisified.
|
|
7412
|
+
* TODO: sendRequest should be promisified in the first place.
|
|
7413
|
+
*/
|
|
7414
|
+
RequestBatcher.prototype.sendRequestPromise = function(data, options) {
|
|
7415
|
+
return new PromisePolyfill(_.bind(function(resolve) {
|
|
7416
|
+
this.sendRequest(data, options, resolve);
|
|
7417
|
+
}, this));
|
|
7418
|
+
};
|
|
7419
|
+
|
|
7420
|
+
|
|
6941
7421
|
/**
|
|
6942
7422
|
* Flush one batch to network. Depending on success/failure modes, it will either
|
|
6943
7423
|
* remove the batch from the queue or leave it in for retry, and schedule the next
|
|
@@ -6949,183 +7429,191 @@
|
|
|
6949
7429
|
* sendBeacon offers no callbacks or status indications)
|
|
6950
7430
|
*/
|
|
6951
7431
|
RequestBatcher.prototype.flush = function(options) {
|
|
6952
|
-
|
|
7432
|
+
if (this.requestInProgress) {
|
|
7433
|
+
logger$2.log('Flush: Request already in progress');
|
|
7434
|
+
return PromisePolyfill.resolve();
|
|
7435
|
+
}
|
|
6953
7436
|
|
|
6954
|
-
|
|
6955
|
-
logger$2.log('Flush: Request already in progress');
|
|
6956
|
-
return;
|
|
6957
|
-
}
|
|
7437
|
+
this.requestInProgress = true;
|
|
6958
7438
|
|
|
6959
|
-
|
|
6960
|
-
|
|
6961
|
-
|
|
6962
|
-
|
|
6963
|
-
|
|
6964
|
-
|
|
6965
|
-
|
|
6966
|
-
|
|
6967
|
-
|
|
6968
|
-
|
|
6969
|
-
|
|
6970
|
-
var
|
|
6971
|
-
|
|
6972
|
-
|
|
6973
|
-
|
|
6974
|
-
|
|
6975
|
-
|
|
6976
|
-
// sends each event (regardless of which version originally queued
|
|
6977
|
-
// it for sending)
|
|
6978
|
-
if (payload['event'] && payload['properties']) {
|
|
6979
|
-
payload['properties'] = _.extend(
|
|
6980
|
-
{},
|
|
6981
|
-
payload['properties'],
|
|
6982
|
-
{'mp_sent_by_lib_version': Config.LIB_VERSION}
|
|
6983
|
-
);
|
|
7439
|
+
options = options || {};
|
|
7440
|
+
var timeoutMS = this.libConfig['batch_request_timeout_ms'];
|
|
7441
|
+
var startTime = new Date().getTime();
|
|
7442
|
+
var currentBatchSize = this.batchSize;
|
|
7443
|
+
|
|
7444
|
+
return this.queue.fillBatch(currentBatchSize)
|
|
7445
|
+
.then(_.bind(function(batch) {
|
|
7446
|
+
|
|
7447
|
+
// if there's more items in the queue than the batch size, attempt
|
|
7448
|
+
// to flush again after the current batch is done.
|
|
7449
|
+
var attemptSecondaryFlush = batch.length === currentBatchSize;
|
|
7450
|
+
var dataForRequest = [];
|
|
7451
|
+
var transformedItems = {};
|
|
7452
|
+
_.each(batch, function(item) {
|
|
7453
|
+
var payload = item['payload'];
|
|
7454
|
+
if (this.beforeSendHook && !item.orphaned) {
|
|
7455
|
+
payload = this.beforeSendHook(payload);
|
|
6984
7456
|
}
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
7457
|
+
if (payload) {
|
|
7458
|
+
// mp_sent_by_lib_version prop captures which lib version actually
|
|
7459
|
+
// sends each event (regardless of which version originally queued
|
|
7460
|
+
// it for sending)
|
|
7461
|
+
if (payload['event'] && payload['properties']) {
|
|
7462
|
+
payload['properties'] = _.extend(
|
|
7463
|
+
{},
|
|
7464
|
+
payload['properties'],
|
|
7465
|
+
{'mp_sent_by_lib_version': Config.LIB_VERSION}
|
|
7466
|
+
);
|
|
7467
|
+
}
|
|
7468
|
+
var addPayload = true;
|
|
7469
|
+
var itemId = item['id'];
|
|
7470
|
+
if (itemId) {
|
|
7471
|
+
if ((this.itemIdsSentSuccessfully[itemId] || 0) > 5) {
|
|
7472
|
+
this.reportError('[dupe] item ID sent too many times, not sending', {
|
|
7473
|
+
item: item,
|
|
7474
|
+
batchSize: batch.length,
|
|
7475
|
+
timesSent: this.itemIdsSentSuccessfully[itemId]
|
|
7476
|
+
});
|
|
7477
|
+
addPayload = false;
|
|
7478
|
+
}
|
|
7479
|
+
} else {
|
|
7480
|
+
this.reportError('[dupe] found item with no ID', {item: item});
|
|
6995
7481
|
}
|
|
6996
|
-
} else {
|
|
6997
|
-
this.reportError('[dupe] found item with no ID', {item: item});
|
|
6998
|
-
}
|
|
6999
7482
|
|
|
7000
|
-
|
|
7001
|
-
|
|
7483
|
+
if (addPayload) {
|
|
7484
|
+
dataForRequest.push(payload);
|
|
7485
|
+
}
|
|
7002
7486
|
}
|
|
7003
|
-
|
|
7004
|
-
|
|
7005
|
-
}, this);
|
|
7006
|
-
if (dataForRequest.length < 1) {
|
|
7007
|
-
this.resetFlush();
|
|
7008
|
-
return; // nothing to do
|
|
7009
|
-
}
|
|
7010
|
-
|
|
7011
|
-
this.requestInProgress = true;
|
|
7487
|
+
transformedItems[item['id']] = payload;
|
|
7488
|
+
}, this);
|
|
7012
7489
|
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7490
|
+
if (dataForRequest.length < 1) {
|
|
7491
|
+
this.requestInProgress = false;
|
|
7492
|
+
this.resetFlush();
|
|
7493
|
+
return PromisePolyfill.resolve(); // nothing to do
|
|
7494
|
+
}
|
|
7017
7495
|
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
this.queue.updatePayloads(transformedItems);
|
|
7025
|
-
} else if (
|
|
7026
|
-
_.isObject(res) &&
|
|
7027
|
-
res.error === 'timeout' &&
|
|
7028
|
-
new Date().getTime() - startTime >= timeoutMS
|
|
7029
|
-
) {
|
|
7030
|
-
this.reportError('Network timeout; retrying');
|
|
7031
|
-
this.flush();
|
|
7032
|
-
} else if (
|
|
7033
|
-
_.isObject(res) &&
|
|
7034
|
-
(
|
|
7035
|
-
res.httpStatusCode >= 500
|
|
7036
|
-
|| res.httpStatusCode === 429
|
|
7037
|
-
|| (res.httpStatusCode <= 0 && !isOnline())
|
|
7038
|
-
|| res.error === 'timeout'
|
|
7496
|
+
var removeItemsFromQueue = _.bind(function () {
|
|
7497
|
+
return this.queue
|
|
7498
|
+
.removeItemsByID(
|
|
7499
|
+
_.map(batch, function (item) {
|
|
7500
|
+
return item['id'];
|
|
7501
|
+
})
|
|
7039
7502
|
)
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
|
|
7051
|
-
|
|
7052
|
-
|
|
7053
|
-
this.batchSize = Math.min(this.batchSize, halvedBatchSize, batch.length - 1);
|
|
7054
|
-
this.reportError('413 response; reducing batch size to ' + this.batchSize);
|
|
7055
|
-
this.resetFlush();
|
|
7056
|
-
} else {
|
|
7057
|
-
this.reportError('Single-event request too large; dropping', batch);
|
|
7058
|
-
this.resetBatchSize();
|
|
7059
|
-
removeItemsFromQueue = true;
|
|
7060
|
-
}
|
|
7061
|
-
} else {
|
|
7062
|
-
// successful network request+response; remove each item in batch from queue
|
|
7063
|
-
// (even if it was e.g. a 400, in which case retrying won't help)
|
|
7064
|
-
removeItemsFromQueue = true;
|
|
7065
|
-
}
|
|
7066
|
-
|
|
7067
|
-
if (removeItemsFromQueue) {
|
|
7068
|
-
this.queue.removeItemsByID(
|
|
7069
|
-
_.map(batch, function(item) { return item['id']; }),
|
|
7070
|
-
_.bind(function(succeeded) {
|
|
7071
|
-
if (succeeded) {
|
|
7072
|
-
this.consecutiveRemovalFailures = 0;
|
|
7073
|
-
if (this.flushOnlyOnInterval && !attemptSecondaryFlush) {
|
|
7074
|
-
this.resetFlush(); // schedule next batch with a delay
|
|
7075
|
-
} else {
|
|
7076
|
-
this.flush(); // handle next batch if the queue isn't empty
|
|
7503
|
+
.then(_.bind(function (succeeded) {
|
|
7504
|
+
// client-side dedupe
|
|
7505
|
+
_.each(batch, _.bind(function(item) {
|
|
7506
|
+
var itemId = item['id'];
|
|
7507
|
+
if (itemId) {
|
|
7508
|
+
this.itemIdsSentSuccessfully[itemId] = this.itemIdsSentSuccessfully[itemId] || 0;
|
|
7509
|
+
this.itemIdsSentSuccessfully[itemId]++;
|
|
7510
|
+
if (this.itemIdsSentSuccessfully[itemId] > 5) {
|
|
7511
|
+
this.reportError('[dupe] item ID sent too many times', {
|
|
7512
|
+
item: item,
|
|
7513
|
+
batchSize: batch.length,
|
|
7514
|
+
timesSent: this.itemIdsSentSuccessfully[itemId]
|
|
7515
|
+
});
|
|
7077
7516
|
}
|
|
7078
7517
|
} else {
|
|
7079
|
-
this.reportError('
|
|
7080
|
-
if (++this.consecutiveRemovalFailures > 5) {
|
|
7081
|
-
this.reportError('Too many queue failures; disabling batching system.');
|
|
7082
|
-
this.stopAllBatching();
|
|
7083
|
-
} else {
|
|
7084
|
-
this.resetFlush();
|
|
7085
|
-
}
|
|
7518
|
+
this.reportError('[dupe] found item with no ID while removing', {item: item});
|
|
7086
7519
|
}
|
|
7087
|
-
}, this)
|
|
7088
|
-
);
|
|
7520
|
+
}, this));
|
|
7089
7521
|
|
|
7090
|
-
|
|
7091
|
-
|
|
7092
|
-
|
|
7093
|
-
|
|
7094
|
-
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
this.reportError('[dupe] item ID sent too many times', {
|
|
7098
|
-
item: item,
|
|
7099
|
-
batchSize: batch.length,
|
|
7100
|
-
timesSent: this.itemIdsSentSuccessfully[itemId]
|
|
7101
|
-
});
|
|
7522
|
+
if (succeeded) {
|
|
7523
|
+
this.consecutiveRemovalFailures = 0;
|
|
7524
|
+
if (this.flushOnlyOnInterval && !attemptSecondaryFlush) {
|
|
7525
|
+
this.resetFlush(); // schedule next batch with a delay
|
|
7526
|
+
return PromisePolyfill.resolve();
|
|
7527
|
+
} else {
|
|
7528
|
+
return this.flush(); // handle next batch if the queue isn't empty
|
|
7102
7529
|
}
|
|
7103
7530
|
} else {
|
|
7104
|
-
this.
|
|
7531
|
+
if (++this.consecutiveRemovalFailures > 5) {
|
|
7532
|
+
this.reportError('Too many queue failures; disabling batching system.');
|
|
7533
|
+
this.stopAllBatching();
|
|
7534
|
+
} else {
|
|
7535
|
+
this.resetFlush();
|
|
7536
|
+
}
|
|
7537
|
+
return PromisePolyfill.resolve();
|
|
7105
7538
|
}
|
|
7106
7539
|
}, this));
|
|
7107
|
-
|
|
7540
|
+
}, this);
|
|
7108
7541
|
|
|
7109
|
-
|
|
7110
|
-
this.
|
|
7111
|
-
|
|
7542
|
+
var batchSendCallback = _.bind(function(res) {
|
|
7543
|
+
this.requestInProgress = false;
|
|
7544
|
+
|
|
7545
|
+
try {
|
|
7546
|
+
|
|
7547
|
+
// handle API response in a try-catch to make sure we can reset the
|
|
7548
|
+
// flush operation if something goes wrong
|
|
7549
|
+
|
|
7550
|
+
if (options.unloading) {
|
|
7551
|
+
// update persisted data to include hook transformations
|
|
7552
|
+
return this.queue.updatePayloads(transformedItems);
|
|
7553
|
+
} else if (
|
|
7554
|
+
_.isObject(res) &&
|
|
7555
|
+
res.error === 'timeout' &&
|
|
7556
|
+
new Date().getTime() - startTime >= timeoutMS
|
|
7557
|
+
) {
|
|
7558
|
+
this.reportError('Network timeout; retrying');
|
|
7559
|
+
return this.flush();
|
|
7560
|
+
} else if (
|
|
7561
|
+
_.isObject(res) &&
|
|
7562
|
+
(
|
|
7563
|
+
res.httpStatusCode >= 500
|
|
7564
|
+
|| res.httpStatusCode === 429
|
|
7565
|
+
|| (res.httpStatusCode <= 0 && !isOnline())
|
|
7566
|
+
|| res.error === 'timeout'
|
|
7567
|
+
)
|
|
7568
|
+
) {
|
|
7569
|
+
// network or API error, or 429 Too Many Requests, retry
|
|
7570
|
+
var retryMS = this.flushInterval * 2;
|
|
7571
|
+
if (res.retryAfter) {
|
|
7572
|
+
retryMS = (parseInt(res.retryAfter, 10) * 1000) || retryMS;
|
|
7573
|
+
}
|
|
7574
|
+
retryMS = Math.min(MAX_RETRY_INTERVAL_MS, retryMS);
|
|
7575
|
+
this.reportError('Error; retry in ' + retryMS + ' ms');
|
|
7576
|
+
this.scheduleFlush(retryMS);
|
|
7577
|
+
return PromisePolyfill.resolve();
|
|
7578
|
+
} else if (_.isObject(res) && res.httpStatusCode === 413) {
|
|
7579
|
+
// 413 Payload Too Large
|
|
7580
|
+
if (batch.length > 1) {
|
|
7581
|
+
var halvedBatchSize = Math.max(1, Math.floor(currentBatchSize / 2));
|
|
7582
|
+
this.batchSize = Math.min(this.batchSize, halvedBatchSize, batch.length - 1);
|
|
7583
|
+
this.reportError('413 response; reducing batch size to ' + this.batchSize);
|
|
7584
|
+
this.resetFlush();
|
|
7585
|
+
return PromisePolyfill.resolve();
|
|
7586
|
+
} else {
|
|
7587
|
+
this.reportError('Single-event request too large; dropping', batch);
|
|
7588
|
+
this.resetBatchSize();
|
|
7589
|
+
return removeItemsFromQueue();
|
|
7590
|
+
}
|
|
7591
|
+
} else {
|
|
7592
|
+
// successful network request+response; remove each item in batch from queue
|
|
7593
|
+
// (even if it was e.g. a 400, in which case retrying won't help)
|
|
7594
|
+
return removeItemsFromQueue();
|
|
7595
|
+
}
|
|
7596
|
+
} catch(err) {
|
|
7597
|
+
this.reportError('Error handling API response', err);
|
|
7598
|
+
this.resetFlush();
|
|
7599
|
+
}
|
|
7600
|
+
}, this);
|
|
7601
|
+
var requestOptions = {
|
|
7602
|
+
method: 'POST',
|
|
7603
|
+
verbose: true,
|
|
7604
|
+
ignore_json_errors: true, // eslint-disable-line camelcase
|
|
7605
|
+
timeout_ms: timeoutMS // eslint-disable-line camelcase
|
|
7606
|
+
};
|
|
7607
|
+
if (options.unloading) {
|
|
7608
|
+
requestOptions.transport = 'sendBeacon';
|
|
7112
7609
|
}
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
};
|
|
7120
|
-
if (options.unloading) {
|
|
7121
|
-
requestOptions.transport = 'sendBeacon';
|
|
7122
|
-
}
|
|
7123
|
-
logger$2.log('MIXPANEL REQUEST:', dataForRequest);
|
|
7124
|
-
this.sendRequest(dataForRequest, requestOptions, batchSendCallback);
|
|
7125
|
-
} catch(err) {
|
|
7126
|
-
this.reportError('Error flushing request queue', err);
|
|
7127
|
-
this.resetFlush();
|
|
7128
|
-
}
|
|
7610
|
+
logger$2.log('MIXPANEL REQUEST:', dataForRequest);
|
|
7611
|
+
return this.sendRequestPromise(dataForRequest, requestOptions).then(batchSendCallback);
|
|
7612
|
+
}, this))
|
|
7613
|
+
.catch(_.bind(function(err) {
|
|
7614
|
+
this.reportError('Error flushing request queue', err);
|
|
7615
|
+
this.resetFlush();
|
|
7616
|
+
}, this));
|
|
7129
7617
|
};
|
|
7130
7618
|
|
|
7131
7619
|
/**
|