@pendo/agent 2.350.1 → 2.350.2
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/dist/dom.esm.js +1 -1
- package/dist/pendo.module.js +168 -105
- package/dist/pendo.module.min.js +9 -9
- package/dist/servers.json +8 -8
- package/package.json +1 -1
package/dist/dom.esm.js
CHANGED
|
@@ -7592,7 +7592,7 @@ function applyMatrix2dRect(matrix2d, rect) {
|
|
|
7592
7592
|
return transformedRect;
|
|
7593
7593
|
}
|
|
7594
7594
|
|
|
7595
|
-
var VERSION = '2.350.
|
|
7595
|
+
var VERSION = '2.350.2_';
|
|
7596
7596
|
|
|
7597
7597
|
var decodeURIComponent = _.isFunction(globalScope.decodeURIComponent) ? globalScope.decodeURIComponent : _.identity;
|
|
7598
7598
|
|
package/dist/pendo.module.js
CHANGED
|
@@ -4175,8 +4175,8 @@ let SERVER = '';
|
|
|
4175
4175
|
let ASSET_HOST = '';
|
|
4176
4176
|
let ASSET_PATH = '';
|
|
4177
4177
|
let DESIGNER_SERVER = '';
|
|
4178
|
-
let VERSION = '2.350.
|
|
4179
|
-
let PACKAGE_VERSION = '2.350.
|
|
4178
|
+
let VERSION = '2.350.2_';
|
|
4179
|
+
let PACKAGE_VERSION = '2.350.2';
|
|
4180
4180
|
let LOADER = 'xhr';
|
|
4181
4181
|
/* eslint-enable web-sdk-eslint-rules/no-gulp-env-references */
|
|
4182
4182
|
/**
|
|
@@ -12686,6 +12686,139 @@ var getURL = function () {
|
|
|
12686
12686
|
return pendoDotUrl.get();
|
|
12687
12687
|
};
|
|
12688
12688
|
|
|
12689
|
+
const UNSENT_EVENTS_KEY = 'unsentEvents';
|
|
12690
|
+
const UNSENT_EVENTS_MAX_AGE = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
12691
|
+
function mergeEvents(target, source) {
|
|
12692
|
+
_.each(source, (requests, key) => {
|
|
12693
|
+
target[key] = (target[key] || []).concat(requests);
|
|
12694
|
+
});
|
|
12695
|
+
return target;
|
|
12696
|
+
}
|
|
12697
|
+
function migrateLegacyCookieEvents(events) {
|
|
12698
|
+
const cookieValue = get_pendo_cookie(UNSENT_EVENTS_KEY);
|
|
12699
|
+
if (cookieValue) {
|
|
12700
|
+
try {
|
|
12701
|
+
mergeEvents(events, JSON.parse(cookieValue));
|
|
12702
|
+
}
|
|
12703
|
+
catch (e) { }
|
|
12704
|
+
clearCookie(getPendoCookieKey(UNSENT_EVENTS_KEY));
|
|
12705
|
+
}
|
|
12706
|
+
}
|
|
12707
|
+
// the quota error doesn't say how much space is left, so halving keeps the
|
|
12708
|
+
// number of retries during pagehide small
|
|
12709
|
+
function dropOldestHalf(events) {
|
|
12710
|
+
const entries = [];
|
|
12711
|
+
_.each(events, (requests, key) => {
|
|
12712
|
+
_.each(requests, (request) => {
|
|
12713
|
+
entries.push({ key, request });
|
|
12714
|
+
});
|
|
12715
|
+
});
|
|
12716
|
+
const newest = _.sortBy(entries, (entry) => entry.request.ts).slice(Math.ceil(entries.length / 2));
|
|
12717
|
+
const remaining = {};
|
|
12718
|
+
_.each(newest, (entry) => {
|
|
12719
|
+
remaining[entry.key] = remaining[entry.key] || [];
|
|
12720
|
+
remaining[entry.key].push(entry.request);
|
|
12721
|
+
});
|
|
12722
|
+
return remaining;
|
|
12723
|
+
}
|
|
12724
|
+
class LocalStorageEventBuffer {
|
|
12725
|
+
constructor() {
|
|
12726
|
+
this.events = {};
|
|
12727
|
+
this.saved = [];
|
|
12728
|
+
}
|
|
12729
|
+
send(key, queue) {
|
|
12730
|
+
const data = this.events[key];
|
|
12731
|
+
if (!data)
|
|
12732
|
+
return;
|
|
12733
|
+
let request = data.shift();
|
|
12734
|
+
while (request) {
|
|
12735
|
+
queue.push(request);
|
|
12736
|
+
request = data.shift();
|
|
12737
|
+
}
|
|
12738
|
+
delete this.events[key];
|
|
12739
|
+
}
|
|
12740
|
+
save(key, sendQueue) {
|
|
12741
|
+
const previous = _.find(this.saved, (entry) => entry.sendQueue === sendQueue);
|
|
12742
|
+
if (!previous && sendQueue.queue.length === 0)
|
|
12743
|
+
return;
|
|
12744
|
+
this.saved = _.without(this.saved, previous);
|
|
12745
|
+
if (sendQueue.queue.length > 0) {
|
|
12746
|
+
this.saved.push({ key, sendQueue, requests: sendQueue.queue.slice() });
|
|
12747
|
+
}
|
|
12748
|
+
this.dirty = true;
|
|
12749
|
+
}
|
|
12750
|
+
writeIfDirty() {
|
|
12751
|
+
if (this.dirty) {
|
|
12752
|
+
this.write();
|
|
12753
|
+
}
|
|
12754
|
+
}
|
|
12755
|
+
remove(sendQueue, request) {
|
|
12756
|
+
const entry = _.find(this.saved, (candidate) => candidate.sendQueue === sendQueue);
|
|
12757
|
+
if (!entry)
|
|
12758
|
+
return;
|
|
12759
|
+
const index = _.indexOf(entry.requests, request);
|
|
12760
|
+
if (index < 0)
|
|
12761
|
+
return;
|
|
12762
|
+
entry.requests.splice(index, 1);
|
|
12763
|
+
if (entry.requests.length === 0) {
|
|
12764
|
+
this.saved = _.without(this.saved, entry);
|
|
12765
|
+
}
|
|
12766
|
+
// rewriting the whole copy after every send is quadratic while a long backlog drains, so leave
|
|
12767
|
+
// it for the next flushNow
|
|
12768
|
+
this.dirty = true;
|
|
12769
|
+
}
|
|
12770
|
+
read(storage) {
|
|
12771
|
+
/**
|
|
12772
|
+
* If enabled, Pendo will write pending events to localStorage before unloading the page
|
|
12773
|
+
* rather than attempting to send them. The events will be sent the next time the page is loaded.
|
|
12774
|
+
*
|
|
12775
|
+
* @name _pendo_unsentEvents
|
|
12776
|
+
* @category Cookies/localStorage
|
|
12777
|
+
* @access public
|
|
12778
|
+
* @label UNSENT_EVENTS_KEY
|
|
12779
|
+
*/
|
|
12780
|
+
storage.registry.addLocal(UNSENT_EVENTS_KEY, {
|
|
12781
|
+
localStorageOnly: true,
|
|
12782
|
+
duration: UNSENT_EVENTS_MAX_AGE
|
|
12783
|
+
});
|
|
12784
|
+
try {
|
|
12785
|
+
this.events = JSON.parse(storage.read(UNSENT_EVENTS_KEY) || '{}');
|
|
12786
|
+
}
|
|
12787
|
+
catch (e) {
|
|
12788
|
+
this.events = {};
|
|
12789
|
+
}
|
|
12790
|
+
migrateLegacyCookieEvents(this.events);
|
|
12791
|
+
storage.clear(UNSENT_EVENTS_KEY);
|
|
12792
|
+
this.storage = storage;
|
|
12793
|
+
this.saved = [];
|
|
12794
|
+
this.dirty = false;
|
|
12795
|
+
}
|
|
12796
|
+
// requests still in flight at teardown can finish afterwards, and a flushNow after that would
|
|
12797
|
+
// write the saved copy under a key built from the now-cleared apiKey
|
|
12798
|
+
teardown() {
|
|
12799
|
+
this.storage = undefined;
|
|
12800
|
+
this.saved = [];
|
|
12801
|
+
this.dirty = false;
|
|
12802
|
+
}
|
|
12803
|
+
write(storage = this.storage) {
|
|
12804
|
+
if (!storage)
|
|
12805
|
+
return;
|
|
12806
|
+
this.dirty = false;
|
|
12807
|
+
let events = mergeEvents({}, this.events);
|
|
12808
|
+
_.each(this.saved, (entry) => {
|
|
12809
|
+
mergeEvents(events, { [entry.key]: entry.requests });
|
|
12810
|
+
});
|
|
12811
|
+
while (_.size(events) > 0) {
|
|
12812
|
+
if (storage.write(UNSENT_EVENTS_KEY, JSON.stringify(events), UNSENT_EVENTS_MAX_AGE)) {
|
|
12813
|
+
return;
|
|
12814
|
+
}
|
|
12815
|
+
events = dropOldestHalf(events);
|
|
12816
|
+
}
|
|
12817
|
+
storage.clear(UNSENT_EVENTS_KEY);
|
|
12818
|
+
}
|
|
12819
|
+
}
|
|
12820
|
+
var localStorageEventBuffer = new LocalStorageEventBuffer();
|
|
12821
|
+
|
|
12689
12822
|
var buffers = {
|
|
12690
12823
|
/* : events: Event[] */
|
|
12691
12824
|
/* : silos: Silo[] */
|
|
@@ -12749,7 +12882,7 @@ function callLater(callback, n) {
|
|
|
12749
12882
|
* @access public
|
|
12750
12883
|
* @category Events
|
|
12751
12884
|
* @param {Object} [options] - Options passed through to each queue's flush method
|
|
12752
|
-
* @param {boolean} [options.unload] - signals the page is unloading; drains the send queue using sendBeacon or sync XHR for reliable last-chance delivery
|
|
12885
|
+
* @param {boolean} [options.unload] - signals the page is unloading; drains the send queue using sendBeacon or sync XHR for reliable last-chance delivery. With `analytics.localStorageUnload` enabled, unsent events are saved to localStorage instead and sent on the next page load, or on the next flush if the page doesn't unload
|
|
12753
12886
|
* @param {boolean} [options.hidden] - signals the page is being hidden; drains the send queue immediately rather than waiting for the next timer tick
|
|
12754
12887
|
* @returns {void}
|
|
12755
12888
|
* @example
|
|
@@ -12762,6 +12895,7 @@ function flushNow(options) {
|
|
|
12762
12895
|
queue.flush(options);
|
|
12763
12896
|
}
|
|
12764
12897
|
});
|
|
12898
|
+
localStorageEventBuffer.writeIfDirty();
|
|
12765
12899
|
}
|
|
12766
12900
|
catch (e) {
|
|
12767
12901
|
log.critical('unhandled error while flushing event cache', { error: e });
|
|
@@ -12927,9 +13061,12 @@ class SendQueue {
|
|
|
12927
13061
|
this.stopped = false;
|
|
12928
13062
|
}
|
|
12929
13063
|
push(...payload) {
|
|
12930
|
-
this.
|
|
13064
|
+
this.enqueue(...payload);
|
|
12931
13065
|
this.next();
|
|
12932
13066
|
}
|
|
13067
|
+
enqueue(...payload) {
|
|
13068
|
+
this.queue.push(...payload);
|
|
13069
|
+
}
|
|
12933
13070
|
next() {
|
|
12934
13071
|
if (this.queue.length && this.pending.size < 1) {
|
|
12935
13072
|
const nextPayload = this.queue[0];
|
|
@@ -12953,6 +13090,9 @@ class SendQueue {
|
|
|
12953
13090
|
if (index >= 0) {
|
|
12954
13091
|
this.queue.splice(index, 1);
|
|
12955
13092
|
}
|
|
13093
|
+
if (this.onPass) {
|
|
13094
|
+
this.onPass(payload);
|
|
13095
|
+
}
|
|
12956
13096
|
if (!this.stopped && dequeue) {
|
|
12957
13097
|
this.next();
|
|
12958
13098
|
}
|
|
@@ -13015,97 +13155,6 @@ class SendQueue {
|
|
|
13015
13155
|
}
|
|
13016
13156
|
}
|
|
13017
13157
|
|
|
13018
|
-
const UNSENT_EVENTS_KEY = 'unsentEvents';
|
|
13019
|
-
const UNSENT_EVENTS_MAX_AGE = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
13020
|
-
function mergeEvents(target, source) {
|
|
13021
|
-
_.each(source, (requests, key) => {
|
|
13022
|
-
target[key] = (target[key] || []).concat(requests);
|
|
13023
|
-
});
|
|
13024
|
-
return target;
|
|
13025
|
-
}
|
|
13026
|
-
function migrateLegacyCookieEvents(events) {
|
|
13027
|
-
const cookieValue = get_pendo_cookie(UNSENT_EVENTS_KEY);
|
|
13028
|
-
if (cookieValue) {
|
|
13029
|
-
try {
|
|
13030
|
-
mergeEvents(events, JSON.parse(cookieValue));
|
|
13031
|
-
}
|
|
13032
|
-
catch (e) { }
|
|
13033
|
-
clearCookie(getPendoCookieKey(UNSENT_EVENTS_KEY));
|
|
13034
|
-
}
|
|
13035
|
-
}
|
|
13036
|
-
// the quota error doesn't say how much space is left, so halving keeps the
|
|
13037
|
-
// number of retries during pagehide small
|
|
13038
|
-
function dropOldestHalf(events) {
|
|
13039
|
-
const entries = [];
|
|
13040
|
-
_.each(events, (requests, key) => {
|
|
13041
|
-
_.each(requests, (request) => {
|
|
13042
|
-
entries.push({ key, request });
|
|
13043
|
-
});
|
|
13044
|
-
});
|
|
13045
|
-
const newest = _.sortBy(entries, (entry) => entry.request.ts).slice(Math.ceil(entries.length / 2));
|
|
13046
|
-
const remaining = {};
|
|
13047
|
-
_.each(newest, (entry) => {
|
|
13048
|
-
remaining[entry.key] = remaining[entry.key] || [];
|
|
13049
|
-
remaining[entry.key].push(entry.request);
|
|
13050
|
-
});
|
|
13051
|
-
return remaining;
|
|
13052
|
-
}
|
|
13053
|
-
class LocalStorageEventBuffer {
|
|
13054
|
-
constructor() {
|
|
13055
|
-
this.events = {};
|
|
13056
|
-
}
|
|
13057
|
-
send(key, queue) {
|
|
13058
|
-
const data = this.events[key];
|
|
13059
|
-
if (!data)
|
|
13060
|
-
return;
|
|
13061
|
-
let request = data.shift();
|
|
13062
|
-
while (request) {
|
|
13063
|
-
queue.push(request);
|
|
13064
|
-
request = data.shift();
|
|
13065
|
-
}
|
|
13066
|
-
delete this.events[key];
|
|
13067
|
-
}
|
|
13068
|
-
push(key, obj) {
|
|
13069
|
-
const data = this.events[key] || [];
|
|
13070
|
-
data.push(obj);
|
|
13071
|
-
this.events[key] = data;
|
|
13072
|
-
}
|
|
13073
|
-
read(storage) {
|
|
13074
|
-
/**
|
|
13075
|
-
* If enabled, Pendo will write pending events to localStorage before unloading the page
|
|
13076
|
-
* rather than attempting to send them. The events will be sent the next time the page is loaded.
|
|
13077
|
-
*
|
|
13078
|
-
* @name _pendo_unsentEvents
|
|
13079
|
-
* @category Cookies/localStorage
|
|
13080
|
-
* @access public
|
|
13081
|
-
* @label UNSENT_EVENTS_KEY
|
|
13082
|
-
*/
|
|
13083
|
-
storage.registry.addLocal(UNSENT_EVENTS_KEY, {
|
|
13084
|
-
localStorageOnly: true,
|
|
13085
|
-
duration: UNSENT_EVENTS_MAX_AGE
|
|
13086
|
-
});
|
|
13087
|
-
try {
|
|
13088
|
-
this.events = JSON.parse(storage.read(UNSENT_EVENTS_KEY) || '{}');
|
|
13089
|
-
}
|
|
13090
|
-
catch (e) {
|
|
13091
|
-
this.events = {};
|
|
13092
|
-
}
|
|
13093
|
-
migrateLegacyCookieEvents(this.events);
|
|
13094
|
-
storage.clear(UNSENT_EVENTS_KEY);
|
|
13095
|
-
}
|
|
13096
|
-
write(storage) {
|
|
13097
|
-
let events = this.events;
|
|
13098
|
-
while (_.size(events) > 0) {
|
|
13099
|
-
if (storage.write(UNSENT_EVENTS_KEY, JSON.stringify(events), UNSENT_EVENTS_MAX_AGE)) {
|
|
13100
|
-
return;
|
|
13101
|
-
}
|
|
13102
|
-
events = dropOldestHalf(events);
|
|
13103
|
-
}
|
|
13104
|
-
storage.clear(UNSENT_EVENTS_KEY);
|
|
13105
|
-
}
|
|
13106
|
-
}
|
|
13107
|
-
var localStorageEventBuffer = new LocalStorageEventBuffer();
|
|
13108
|
-
|
|
13109
13158
|
/*
|
|
13110
13159
|
* These implementations are meant to help keep the agent's metrics usage aligned with
|
|
13111
13160
|
* the expectations for the backend's open telemetry.
|
|
@@ -13807,6 +13856,10 @@ class SendQueueSplitter {
|
|
|
13807
13856
|
const args = _.toArray(arguments);
|
|
13808
13857
|
_.each(this.queues, (queue) => queue.push.apply(queue, args));
|
|
13809
13858
|
}
|
|
13859
|
+
enqueue() {
|
|
13860
|
+
const args = _.toArray(arguments);
|
|
13861
|
+
_.each(this.queues, (queue) => queue.enqueue.apply(queue, args));
|
|
13862
|
+
}
|
|
13810
13863
|
drain() {
|
|
13811
13864
|
const args = _.toArray(arguments);
|
|
13812
13865
|
return q.all(_.map(this.queues, (queue) => queue.drain.apply(queue, args)));
|
|
@@ -13826,12 +13879,6 @@ function createSendQueue(options, send, guaranteedSend) {
|
|
|
13826
13879
|
rt: failureCount
|
|
13827
13880
|
});
|
|
13828
13881
|
}
|
|
13829
|
-
if (options.localStorageUnload && isUnload) {
|
|
13830
|
-
if (i === 0) {
|
|
13831
|
-
localStorageEventBuffer.push(options.beacon, request);
|
|
13832
|
-
}
|
|
13833
|
-
return q.resolve();
|
|
13834
|
-
}
|
|
13835
13882
|
if (isUnload && queue.failed()) {
|
|
13836
13883
|
return q.reject();
|
|
13837
13884
|
}
|
|
@@ -13845,6 +13892,11 @@ function createSendQueue(options, send, guaranteedSend) {
|
|
|
13845
13892
|
queue.onTimeout = function () {
|
|
13846
13893
|
performanceMonitor.count(BEACON_GIF_FAILURES[options.beacon]);
|
|
13847
13894
|
};
|
|
13895
|
+
if (options.localStorageUnload && i === 0) {
|
|
13896
|
+
queue.onPass = function (request) {
|
|
13897
|
+
localStorageEventBuffer.remove(queue, request);
|
|
13898
|
+
};
|
|
13899
|
+
}
|
|
13848
13900
|
queue.allowDrainWhileFailed = true;
|
|
13849
13901
|
queue.retryPending = true;
|
|
13850
13902
|
return queue;
|
|
@@ -13876,6 +13928,8 @@ class EventQueue {
|
|
|
13876
13928
|
this.silos = options.silos || [];
|
|
13877
13929
|
this.packageSilos = options.packageSilos;
|
|
13878
13930
|
this.processSilos = options.processSilos;
|
|
13931
|
+
this.beacon = options.beacon;
|
|
13932
|
+
this.localStorageUnload = options.localStorageUnload;
|
|
13879
13933
|
this.sendQueue = createSendQueue(options);
|
|
13880
13934
|
}
|
|
13881
13935
|
// silent skips the ptm lifecycle events, for a queue paused alongside the main one so they fire once.
|
|
@@ -13911,6 +13965,9 @@ class EventQueue {
|
|
|
13911
13965
|
}
|
|
13912
13966
|
flush({ unload = false, hidden = false } = {}) {
|
|
13913
13967
|
const { cache, silos } = this;
|
|
13968
|
+
if (!unload && !hidden) {
|
|
13969
|
+
this.savedOnUnload = false;
|
|
13970
|
+
}
|
|
13914
13971
|
if (cache.length === 0 && silos.length === 0 && this.sendQueue.isEmpty())
|
|
13915
13972
|
return;
|
|
13916
13973
|
const leavingPage = unload || hidden;
|
|
@@ -13926,7 +13983,15 @@ class EventQueue {
|
|
|
13926
13983
|
requests.push(silo);
|
|
13927
13984
|
});
|
|
13928
13985
|
}, this);
|
|
13929
|
-
if
|
|
13986
|
+
// queued but not sent: if the page stays alive the next flush sends them, and each one that goes out is
|
|
13987
|
+
// removed from the saved copy. a real unload is followed by a hidden flush (unload/pagehide fire before
|
|
13988
|
+
// visibilitychange), which has to save as well rather than send everything that was just saved
|
|
13989
|
+
if (this.localStorageUnload && (unload || (hidden && this.savedOnUnload))) {
|
|
13990
|
+
this.savedOnUnload = true;
|
|
13991
|
+
this.sendQueue.enqueue(...requests);
|
|
13992
|
+
localStorageEventBuffer.save(this.beacon, this.sendQueue.queues[0]);
|
|
13993
|
+
}
|
|
13994
|
+
else if (unload || hidden) {
|
|
13930
13995
|
this.sendQueue.drain(requests, unload);
|
|
13931
13996
|
}
|
|
13932
13997
|
else {
|
|
@@ -31341,6 +31406,7 @@ function initialize(options) {
|
|
|
31341
31406
|
}
|
|
31342
31407
|
const localStorageUnloadEnabled = ConfigReader.get('analytics.localStorageUnload');
|
|
31343
31408
|
localStorageEventBuffer.read(agentStorage);
|
|
31409
|
+
teardownFns.push(() => localStorageEventBuffer.teardown());
|
|
31344
31410
|
teardownFns.push(initializeEventBuffer({
|
|
31345
31411
|
localStorageUnload: localStorageUnloadEnabled
|
|
31346
31412
|
}));
|
|
@@ -31410,9 +31476,6 @@ function initialize(options) {
|
|
|
31410
31476
|
teardownFns.push(() => Events.appHidden.off(flushOnAppHidden));
|
|
31411
31477
|
const flushOnAppUnloaded = () => {
|
|
31412
31478
|
flushNow({ unload: true });
|
|
31413
|
-
if (localStorageUnloadEnabled) {
|
|
31414
|
-
localStorageEventBuffer.write(agentStorage);
|
|
31415
|
-
}
|
|
31416
31479
|
};
|
|
31417
31480
|
Events.appUnloaded.on(flushOnAppUnloaded);
|
|
31418
31481
|
teardownFns.push(() => Events.appUnloaded.off(flushOnAppUnloaded));
|