@pendo/agent 2.291.4 → 2.292.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -463,6 +463,9 @@ function shouldUseUnminifiedAgent(config, debuggingEnabled) {
463
463
  return !isExtension(config) && isMinifiedAgent(config) && debuggingEnabled;
464
464
  }
465
465
  function findAgentScriptTag(scripts = [], apiKey) {
466
+ if (document.currentScript) {
467
+ return document.currentScript;
468
+ }
466
469
  const regex = /^https:\/\/[\w\-.]*cdn[\w\-.]*\.(pendo-dev\.com|pendo\.io)\/agent\/static\/([\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}|PENDO_API_KEY)\/pendo\.js$/g;
467
470
  for (let i = 0; i < scripts.length; i++) {
468
471
  const script = scripts[i];
@@ -3687,6 +3690,10 @@ function globalBucket(env) {
3687
3690
  }
3688
3691
 
3689
3692
  function isNativeCode(fn) { return /native/.test(fn); }
3693
+ const detectNativeBrowserAPI = (name) => {
3694
+ const fn = _.get(window, name);
3695
+ return (fn && isNativeCode(fn));
3696
+ };
3690
3697
 
3691
3698
  var isOldIE = function (olderThan, tVersion) {
3692
3699
  olderThan = olderThan || 10;
@@ -3904,8 +3911,8 @@ var SERVER = '';
3904
3911
  var ASSET_HOST = '';
3905
3912
  var ASSET_PATH = '';
3906
3913
  var DESIGNER_SERVER = '';
3907
- var VERSION = '2.291.4_';
3908
- var PACKAGE_VERSION = '2.291.4';
3914
+ var VERSION = '2.292.0_';
3915
+ var PACKAGE_VERSION = '2.292.0';
3909
3916
  var LOADER = 'xhr';
3910
3917
  /* eslint-enable agent-eslint-rules/no-gulp-env-references */
3911
3918
  /**
@@ -10133,6 +10140,17 @@ var removeNode = function (domNode) {
10133
10140
  domNode.parentNode.removeChild(domNode);
10134
10141
  }
10135
10142
  };
10143
+ var getElements = _.compose(function (arrLike) {
10144
+ return Array.prototype.slice.call(arrLike);
10145
+ }, function (tag, document) {
10146
+ try {
10147
+ return SizzleProxy(tag, document);
10148
+ }
10149
+ catch (e) {
10150
+ writeMessage('error using sizzle: ' + e);
10151
+ return document.getElementsByTagName(tag);
10152
+ }
10153
+ });
10136
10154
  var pickBestBODY = function (b1, b2) {
10137
10155
  try {
10138
10156
  // check children.length, check Height, check Width?
@@ -10146,9 +10164,10 @@ var pickBestBODY = function (b1, b2) {
10146
10164
  return 0;
10147
10165
  }
10148
10166
  };
10149
- var getBody = function (_document = document) {
10167
+ var getBody = function (_document) {
10168
+ _document = _document || document;
10150
10169
  try {
10151
- var bds = Array.prototype.slice.call(_document.getElementsByTagName('body'));
10170
+ var bds = getElements('body', _document);
10152
10171
  if (bds && bds.length > 1) {
10153
10172
  bds.sort(pickBestBODY);
10154
10173
  return bds[0] || _document.body;
@@ -12184,6 +12203,92 @@ class LocalStorageEventBuffer {
12184
12203
  }
12185
12204
  var localStorageEventBuffer = new LocalStorageEventBuffer();
12186
12205
 
12206
+ class PerformanceMonitor {
12207
+ constructor() {
12208
+ this._isPerformanceApiAvailable = this._checkPerformanceApi();
12209
+ this._measuringPerformance = false;
12210
+ this._marks = {};
12211
+ this._measures = {};
12212
+ }
12213
+ initialize() {
12214
+ this._measuringPerformance = true;
12215
+ this._markPerformance('initialize');
12216
+ // temporary clear interval while working on send implementation
12217
+ this.interval = setInterval(() => {
12218
+ this._clearMarksAndMeasures();
12219
+ }, 1000 * 60 * 10);
12220
+ return () => {
12221
+ this.teardown();
12222
+ };
12223
+ }
12224
+ teardown() {
12225
+ this._measuringPerformance = false;
12226
+ this._markPerformance('teardown');
12227
+ this._clearMarksAndMeasures();
12228
+ clearInterval(this.interval);
12229
+ }
12230
+ startTimer(name) {
12231
+ this._markPerformance(`${name}-start`);
12232
+ }
12233
+ stopTimer(name) {
12234
+ this._markPerformance(`${name}-stop`);
12235
+ this._measurePerformance(name);
12236
+ }
12237
+ track(name) {
12238
+ this._markPerformance(name);
12239
+ }
12240
+ send() {
12241
+ performance.getEntries();
12242
+ // TODO: implement filter/send process
12243
+ this._clearMarksAndMeasures();
12244
+ }
12245
+ _checkPerformanceApi() {
12246
+ return detectNativeBrowserAPI('performance.mark') &&
12247
+ detectNativeBrowserAPI('performance.measure') &&
12248
+ detectNativeBrowserAPI('performance.getEntries') &&
12249
+ detectNativeBrowserAPI('performance.clearMarks') &&
12250
+ detectNativeBrowserAPI('performance.clearMeasures');
12251
+ }
12252
+ _shouldMeasurePerformance() {
12253
+ return this._isPerformanceApiAvailable && this._measuringPerformance;
12254
+ }
12255
+ _markPerformance(name) {
12256
+ if (!this._shouldMeasurePerformance())
12257
+ return;
12258
+ name = `pendo-${name}`;
12259
+ performance.mark(name);
12260
+ this._count(this._marks, name);
12261
+ }
12262
+ _measurePerformance(name) {
12263
+ if (!this._shouldMeasurePerformance())
12264
+ return;
12265
+ name = `pendo-${name}`;
12266
+ const startMark = `${name}-start`;
12267
+ const stopMark = `${name}-stop`;
12268
+ performance.measure(name, startMark, stopMark);
12269
+ this._count(this._measures, name);
12270
+ }
12271
+ _count(registry, name) {
12272
+ if (!registry[name]) {
12273
+ registry[name] = 1;
12274
+ }
12275
+ else {
12276
+ registry[name]++;
12277
+ }
12278
+ }
12279
+ _clearMarksAndMeasures() {
12280
+ _.each(this._marks, (count, name) => {
12281
+ performance.clearMarks(name);
12282
+ });
12283
+ _.each(this._measures, (count, name) => {
12284
+ performance.clearMeasures(name);
12285
+ });
12286
+ this._marks = {};
12287
+ this._measures = {};
12288
+ }
12289
+ }
12290
+ var PerformanceMonitor$1 = new PerformanceMonitor();
12291
+
12187
12292
  var defaultTrackName = '_PENDO_UNNAMED_';
12188
12293
  var SILO_AVG_COMPRESSION_RATIO = 5;
12189
12294
  /**
@@ -12245,21 +12350,7 @@ function collectEvent(type, props, url, name, eventProperties, context) {
12245
12350
  agenticEventQueue.push(event);
12246
12351
  return;
12247
12352
  }
12248
- eventQueue.push(attachMonitoringData(store, event));
12249
- }
12250
- function attachMonitoringData(store, event) {
12251
- try {
12252
- if (!store.getters['monitoring/shouldFlush']())
12253
- return event;
12254
- var payload = store.getters['monitoring/payload']();
12255
- store.dispatch('monitoring/flush');
12256
- if (!_.isEmpty(payload)) {
12257
- event.ops = payload;
12258
- }
12259
- }
12260
- catch (e) {
12261
- }
12262
- return event;
12353
+ eventQueue.push(event);
12263
12354
  }
12264
12355
  // @const {Event.type[]}
12265
12356
  var WHITELIST_FREE_NPS = ['load', 'meta', 'identify'];
@@ -12698,7 +12789,7 @@ function createSendQueue(options, send, guaranteedSend) {
12698
12789
  }
12699
12790
  });
12700
12791
  queue.onTimeout = function () {
12701
- store.commit('monitoring/incrementCounter', options.beacon + 'GifFailures');
12792
+ PerformanceMonitor$1.track(`${options.beacon}-gif-failure`);
12702
12793
  };
12703
12794
  queue.retryPending = true;
12704
12795
  return queue;
@@ -12990,7 +13081,7 @@ var handleEmbeddedData = function (src) {
12990
13081
  return src + '';
12991
13082
  };
12992
13083
  function getAttributeValue(element, attrName) {
12993
- return element.getAttribute ? element.getAttribute(attrName) : element[attrName];
13084
+ return store.getters['nodeAttr/getAttribute']()(element, attrName);
12994
13085
  }
12995
13086
  var extractAttribute = function (element, attrName, type) {
12996
13087
  if (!element || !element.nodeName)
@@ -21042,10 +21133,6 @@ class UpdateRunner {
21042
21133
  }
21043
21134
  }
21044
21135
  complete(time) {
21045
- store.commit('monitoring/setTimer', {
21046
- name: 'guideUpdateTimeMS',
21047
- time: this.updateTotalTime
21048
- });
21049
21136
  store.commit('guideUpdate/completeUpdate', time);
21050
21137
  this.cancel();
21051
21138
  }
@@ -23917,13 +24004,8 @@ class GuideCache {
23917
24004
  return;
23918
24005
  cachedStep.seenState = lastGuideStepSeen.state;
23919
24006
  cachedStep.seenReason = lastGuideStepSeen.seenReason;
23920
- cachedStep.lastSeenAt = lastGuideStepSeen.time;
23921
- if (lastGuideStepSeen.dismissCount != null) {
23922
- cachedStep.dismissCount = lastGuideStepSeen.dismissCount;
23923
- }
23924
- if (lastGuideStepSeen.snoozeEndTime != null) {
23925
- cachedStep.snoozeEndTime = lastGuideStepSeen.snoozeEndTime;
23926
- }
24007
+ cachedStep.dismissCount = lastGuideStepSeen.dismissCount;
24008
+ cachedStep.snoozeEndTime = lastGuideStepSeen.snoozeEndTime;
23927
24009
  return this.save(cachedGuide);
23928
24010
  }
23929
24011
  list(visitorId, now = getNow()) {
@@ -26533,6 +26615,11 @@ var EventRouter = function () {
26533
26615
  var config = configParam.value;
26534
26616
  var cases = casesParam.value;
26535
26617
  var conditionResult = evaluateCondition(conditionType, config);
26618
+ Events.trigger('appUsage', {
26619
+ type: 'conditionalSplitResult',
26620
+ elementId: evt.srcElement.id,
26621
+ conditionResult
26622
+ });
26536
26623
  var matchingCase = _.find(cases, function (caseItem) {
26537
26624
  return caseItem.value === conditionResult;
26538
26625
  });
@@ -26600,16 +26687,27 @@ var GuideActivity = (function () {
26600
26687
  var isObject = _.isObject;
26601
26688
  var findWhere = _.findWhere;
26602
26689
  const _guideResolvers = [];
26690
+ var conditionalSplitEventQueue = [];
26603
26691
  return {
26604
26692
  type: GA_TYPE,
26605
26693
  handler(evtData) {
26606
26694
  var appData = evtData.data[0];
26695
+ if (appData && appData.type === 'conditionalSplitResult') {
26696
+ finalizeConditionalSplitEvent(appData);
26697
+ return;
26698
+ }
26607
26699
  var browserEvt = evtData.data[1];
26608
26700
  if (!shouldHandleEvent(appData, browserEvt))
26609
26701
  return;
26610
26702
  var activityEvent = transformToGuideActivity(appData);
26611
26703
  if (activityEvent) {
26612
- stageGuideEvent(activityEvent);
26704
+ const firstAction = _.get(activityEvent, ['props', 'ui_element_actions', 0, 'action']);
26705
+ if (firstAction === 'conditionalSplit') {
26706
+ conditionalSplitEventQueue.push(activityEvent);
26707
+ }
26708
+ else {
26709
+ stageGuideEvent(activityEvent);
26710
+ }
26613
26711
  }
26614
26712
  },
26615
26713
  registerGuideResolver(callBack) {
@@ -26622,6 +26720,22 @@ var GuideActivity = (function () {
26622
26720
  }
26623
26721
  }
26624
26722
  };
26723
+ function finalizeConditionalSplitEvent(evtData) {
26724
+ const { conditionResult, elementId } = evtData;
26725
+ const matchingEventIndex = _.findIndex(conditionalSplitEventQueue, (event) => {
26726
+ return event.props.ui_element_id === elementId;
26727
+ });
26728
+ if (matchingEventIndex === -1)
26729
+ return;
26730
+ const matchingEvent = conditionalSplitEventQueue.splice(matchingEventIndex, 1)[0];
26731
+ const conditionalSplitAction = matchingEvent.props.ui_element_actions[0];
26732
+ conditionalSplitAction.condition_result = conditionResult;
26733
+ const cases = conditionalSplitAction.cases;
26734
+ const resultCase = cases[conditionResult];
26735
+ matchingEvent.props.ui_element_actions = matchingEvent.props.ui_element_actions.concat(resultCase);
26736
+ delete conditionalSplitAction.cases;
26737
+ stageGuideEvent(matchingEvent);
26738
+ }
26625
26739
  function isClickEvent(browserEvt) {
26626
26740
  return browserEvt && browserEvt.type === 'click';
26627
26741
  }
@@ -26738,58 +26852,7 @@ var GuideActivity = (function () {
26738
26852
  ];
26739
26853
  }
26740
26854
  if (element.actions !== undefined) {
26741
- return _.map(element.actions, function (action) {
26742
- switch (action.action) {
26743
- case 'automation':
26744
- var automationIds = findWhere(action.parameters, { name: 'automationIds' });
26745
- return {
26746
- action: action.action,
26747
- automationIds
26748
- };
26749
- case 'openLink':
26750
- var url = findWhere(action.parameters, { name: 'url' });
26751
- var target = findWhere(action.parameters, { name: 'target' });
26752
- return {
26753
- action: action.action,
26754
- url: sanitizeUrl(url.value),
26755
- target: target.value
26756
- };
26757
- case 'submitPollAndGoToStep':
26758
- case 'goToStep':
26759
- var guideStepId = findWhere(action.parameters, { name: 'goToStepId' });
26760
- return {
26761
- action: action.action,
26762
- guideStepId: guideStepId.value
26763
- };
26764
- case 'guideSnoozed':
26765
- var duration = findWhere(action.parameters, { name: 'snooze_duration' });
26766
- var timeUnit = findWhere(action.parameters, { name: 'time_unit' });
26767
- return {
26768
- action: action.action,
26769
- duration: duration.value,
26770
- timeUnit: timeUnit.value
26771
- };
26772
- case 'showGuide':
26773
- case 'launchGuide':
26774
- var guideId = action.parameters[0];
26775
- var stepId = action.parameters[1] || {};
26776
- return {
26777
- action: action.action,
26778
- guideId: guideId.value,
26779
- stepId: stepId.value
26780
- };
26781
- case 'advanceGuide':
26782
- case 'previousStep':
26783
- case 'submitPoll':
26784
- case 'dismissGuide':
26785
- case 'confirmation':
26786
- return {
26787
- action: action.action
26788
- };
26789
- default:
26790
- return {};
26791
- }
26792
- });
26855
+ return _.map(element.actions, processAction);
26793
26856
  }
26794
26857
  if (uiElement.templateName === 'pendo_task_list_item') {
26795
26858
  if (_.isArray(element.templateChildren) && element.templateChildren[uiElement.templateChildIndex]) {
@@ -26801,6 +26864,79 @@ var GuideActivity = (function () {
26801
26864
  }
26802
26865
  return [];
26803
26866
  }
26867
+ function processAction(action) {
26868
+ switch (action.action) {
26869
+ case 'automation':
26870
+ var automationIds = findWhere(action.parameters, { name: 'automationIds' });
26871
+ return {
26872
+ action: action.action,
26873
+ automationIds
26874
+ };
26875
+ case 'openLink':
26876
+ var url = findWhere(action.parameters, { name: 'url' });
26877
+ var target = findWhere(action.parameters, { name: 'target' });
26878
+ return {
26879
+ action: action.action,
26880
+ url: sanitizeUrl(url.value),
26881
+ target: target.value
26882
+ };
26883
+ case 'submitPollAndGoToStep':
26884
+ case 'goToStep':
26885
+ var guideStepId = findWhere(action.parameters, { name: 'goToStepId' });
26886
+ return {
26887
+ action: action.action,
26888
+ guideStepId: guideStepId.value
26889
+ };
26890
+ case 'guideSnoozed':
26891
+ var duration = findWhere(action.parameters, { name: 'snooze_duration' });
26892
+ var timeUnit = findWhere(action.parameters, { name: 'time_unit' });
26893
+ return {
26894
+ action: action.action,
26895
+ duration: duration.value,
26896
+ timeUnit: timeUnit.value
26897
+ };
26898
+ case 'showGuide':
26899
+ case 'launchGuide':
26900
+ var guideId = action.parameters[0];
26901
+ var stepId = action.parameters[1] || {};
26902
+ return {
26903
+ action: action.action,
26904
+ guideId: guideId.value,
26905
+ stepId: stepId.value
26906
+ };
26907
+ case 'conditionalSplit':
26908
+ var name = findWhere(action.parameters, { name: 'name' });
26909
+ var conditionType = findWhere(action.parameters, { name: 'conditionType' });
26910
+ var config = findWhere(action.parameters, { name: 'config' });
26911
+ var cases = findWhere(action.parameters, { name: 'cases' });
26912
+ var processedCases = _.reduce(cases.value, function (acc, c) {
26913
+ acc[c.value] = _.map(c.actions, function (a) {
26914
+ var processedAction = processAction(a);
26915
+ processedAction.source = a.source;
26916
+ return processedAction;
26917
+ });
26918
+ return acc;
26919
+ }, {});
26920
+ return {
26921
+ action: action.action,
26922
+ name: name.value,
26923
+ condition_type: conditionType.value,
26924
+ element_selector: config.value.elementSelector,
26925
+ feature_id: config.value.featureId,
26926
+ cases: processedCases // will be removed in finalizeConditionalSplitEvent
26927
+ };
26928
+ case 'advanceGuide':
26929
+ case 'previousStep':
26930
+ case 'submitPoll':
26931
+ case 'dismissGuide':
26932
+ case 'confirmation':
26933
+ return {
26934
+ action: action.action
26935
+ };
26936
+ default:
26937
+ return {};
26938
+ }
26939
+ }
26804
26940
  function transformToGuideActivity(appData) {
26805
26941
  var activeGuideObj = getGuideForEvent(appData);
26806
26942
  if (!activeGuideObj)
@@ -26999,14 +27135,14 @@ var didAccountIdentityChange = function (props) {
26999
27135
  function initIdentityEvents() {
27000
27136
  if (initialized)
27001
27137
  return;
27002
- Events.identify.on(function (event) {
27138
+ const collectIdentifyEvent = function (event) {
27003
27139
  var props = event.data[0];
27004
27140
  if (wasAnonymousVisitorIdentified(props) || didAccountIdentityChange(props)) {
27005
27141
  collectEvent('identify', props);
27006
27142
  flushLater(); // unconditionally on next tick
27007
27143
  }
27008
- });
27009
- Events.metadata.on(function (event) {
27144
+ };
27145
+ const collectMetadataEvent = function (event) {
27010
27146
  // Check to see if this Application is sending metadata to the backend using
27011
27147
  // different mechanism (like Segment webhooks)
27012
27148
  var blockAgentMetadata = isMetadataBlocked();
@@ -27017,9 +27153,17 @@ function initIdentityEvents() {
27017
27153
  flushLater(); // unconditionally on next tick
27018
27154
  }
27019
27155
  }
27020
- });
27156
+ };
27157
+ Events.identify.on(collectIdentifyEvent);
27158
+ Events.metadata.on(collectMetadataEvent);
27021
27159
  Events.identify.on(handleIdentifyEvent);
27022
27160
  initialized = true;
27161
+ return function () {
27162
+ Events.identify.off(collectIdentifyEvent);
27163
+ Events.metadata.off(collectMetadataEvent);
27164
+ Events.identify.off(handleIdentifyEvent);
27165
+ initialized = false;
27166
+ };
27023
27167
  }
27024
27168
 
27025
27169
  /*
@@ -27230,7 +27374,7 @@ class ShadowDomManager extends EventTarget {
27230
27374
  }
27231
27375
  initialize() {
27232
27376
  this.unpatchAttachShadow = this.patchAttachShadow(Element);
27233
- this.findShadowRoots(document);
27377
+ this.findShadowRoots(document, 5000, (shadowRoot) => this.triggerAttachShadow(shadowRoot));
27234
27378
  }
27235
27379
  triggerAttachShadow(shadowRoot) {
27236
27380
  this.cache.add(shadowRoot);
@@ -27239,7 +27383,7 @@ class ShadowDomManager extends EventTarget {
27239
27383
  shadowRoot
27240
27384
  });
27241
27385
  }
27242
- findShadowRoots(document, maxPerSearch = 5000) {
27386
+ findShadowRoots(document, maxPerSearch = 5000, onShadowRootFound) {
27243
27387
  if (!_.isFunction(document.createNodeIterator))
27244
27388
  return;
27245
27389
  let nodesChecked = 0;
@@ -27255,7 +27399,7 @@ class ShadowDomManager extends EventTarget {
27255
27399
  while ((currentNode = iterator.nextNode())) {
27256
27400
  nodesChecked++;
27257
27401
  if (currentNode && currentNode.shadowRoot) {
27258
- this.triggerAttachShadow(currentNode.shadowRoot);
27402
+ onShadowRootFound(currentNode.shadowRoot);
27259
27403
  queue.push(currentNode.shadowRoot);
27260
27404
  this.foundShadowRoots = true;
27261
27405
  }
@@ -27376,7 +27520,8 @@ function registerEventHandlers({ events = [] }) {
27376
27520
  let initializeCounter = 0;
27377
27521
  const initializeImmediately = 'initializeImmediately';
27378
27522
  /**
27379
- * Initializes the Pendo Agent in a browser window.
27523
+ * Initializes the Pendo agent in a browser window. This function only needs to be called once per page load. Any subsequent calls, without first having called `teardown`, will be ignored.
27524
+ * Once the agent has been initialized, any updates to identity or metadata should be done using `identify` or `updateOptions`.
27380
27525
  *
27381
27526
  * @access public
27382
27527
  * @function
@@ -27522,13 +27667,14 @@ const initialize = makeSafe(function (options) {
27522
27667
  teardownFns.push(forwardInternalEvents(Events));
27523
27668
  registerEventHandlers(options);
27524
27669
  teardownFns.push(initGuides(observer)); // this is safe. loadGuides actually does the loading.
27525
- initIdentityEvents(); // setup identify and meta event listeners
27670
+ teardownFns.push(initIdentityEvents()); // setup identify and meta event listeners
27526
27671
  teardownFns.push(wirePage());
27527
27672
  teardownFns.push(initializePlugins());
27528
27673
  teardownFns.push(launchDesignerOrPreview(options));
27529
27674
  Events.appUsage.on(GuideActivity.handler);
27530
27675
  Events.appUsage.on(ResourceCenterActivity.handler);
27531
27676
  teardownFns.push(flushEvery(SEND_INTERVAL));
27677
+ teardownFns.push(PerformanceMonitor$1.initialize());
27532
27678
  }
27533
27679
  Events.appHidden.on(() => {
27534
27680
  flushNow(true, { hidden: true });
@@ -29021,11 +29167,14 @@ var attrActionEnum = {
29021
29167
  href: ['Replace', 'ForceSecure'],
29022
29168
  protocol: ['ForceSecure']
29023
29169
  };
29024
- function UrlAttrTransform(attr, action, data) {
29170
+ const TARGETS = ['location', 'attribute', 'both'];
29171
+ const DEFAULT_TARGET = 'location';
29172
+ function UrlTransform(attr, action, data, target) {
29025
29173
  if (_.isObject(attr)) {
29026
29174
  action = attr.action;
29027
29175
  data = attr.data;
29028
29176
  attr = attr.attr;
29177
+ target = attr.target;
29029
29178
  }
29030
29179
  if (!attrActionEnum[attr]) {
29031
29180
  var validAttrs = _.keys(attrActionEnum).join(', ');
@@ -29035,11 +29184,16 @@ function UrlAttrTransform(attr, action, data) {
29035
29184
  var validActions = attrActionEnum[attr].join(', ');
29036
29185
  throw new Error('Invalid transform action: "' + action + '" for attribute "' + attr + '". Valid actions for "' + attr + '" are: ' + validActions + '. Example: {attr: "' + attr + '", action: "' + attrActionEnum[attr][0] + '", data: ...}');
29037
29186
  }
29187
+ // validate 'target'
29188
+ if (TARGETS.indexOf(target) < 0) {
29189
+ target = DEFAULT_TARGET;
29190
+ }
29038
29191
  this.attr = attr;
29039
29192
  this.action = action;
29040
29193
  this.data = data;
29194
+ this.target = target || DEFAULT_TARGET;
29041
29195
  }
29042
- UrlAttrTransform.prototype.applyToURL = function (url) {
29196
+ UrlTransform.prototype.applyTo = function (url) {
29043
29197
  var actionImpls = {
29044
29198
  AllowOnlyKeys(url, attr, data) {
29045
29199
  if (_.isFunction(data)) {
@@ -29112,26 +29266,26 @@ UrlAttrTransform.prototype.applyToURL = function (url) {
29112
29266
  };
29113
29267
  return actionImpls[this.action](url, this.attr, this.data);
29114
29268
  };
29115
- UrlAttrTransform.prototype.toString = function () {
29269
+ UrlTransform.prototype.toString = function () {
29116
29270
  return this.attr + ' - ' + this.action + ' - ' + JSON.stringify(this.data);
29117
29271
  };
29118
29272
  // Static method, not on the instance
29119
- UrlAttrTransform.fromJSON = function (obj) {
29120
- if (obj instanceof UrlAttrTransform) {
29273
+ UrlTransform.fromJSON = function (obj) {
29274
+ if (obj instanceof UrlTransform) {
29121
29275
  return obj;
29122
29276
  }
29123
29277
  // Validate input is an object
29124
29278
  if (!_.isObject(obj)) {
29125
- throw new Error('UrlAttrTransform.fromJSON expects an object. Received: ' + typeof obj + '. Example: {attr: "search", action: "AddTo", data: {key: "value"}}');
29279
+ throw new Error('UrlTransform.fromJSON expects an object. Received: ' + typeof obj + '. Example: {attr: "search", action: "AddTo", data: {key: "value"}}');
29126
29280
  }
29127
29281
  // Validate required properties
29128
29282
  if (!obj.attr) {
29129
29283
  var allValidAttrs = _.keys(attrActionEnum).join(', ');
29130
- throw new Error('UrlAttrTransform is missing required "attr" property. Valid attributes are: ' + allValidAttrs + '. Example: {attr: "search", action: "AddTo", data: {key: "value"}}');
29284
+ throw new Error('UrlTransform is missing required "attr" property. Valid attributes are: ' + allValidAttrs + '. Example: {attr: "search", action: "AddTo", data: {key: "value"}}');
29131
29285
  }
29132
29286
  if (!obj.action) {
29133
29287
  var attrValidActions = attrActionEnum[obj.attr] ? attrActionEnum[obj.attr].join(', ') : 'AllowOnlyKeys, ExcludeKeys, AddTo, Replace, Clear';
29134
- throw new Error('UrlAttrTransform is missing required "action" property. Valid actions depend on the attribute. For "' + obj.attr + '" attribute, valid actions are: ' + attrValidActions + '. Example: {attr: "' + obj.attr + '", action: "AddTo", data: {key: "value"}}');
29288
+ throw new Error('UrlTransform is missing required "action" property. Valid actions depend on the attribute. For "' + obj.attr + '" attribute, valid actions are: ' + attrValidActions + '. Example: {attr: "' + obj.attr + '", action: "AddTo", data: {key: "value"}}');
29135
29289
  }
29136
29290
  // Validate attribute and action combination
29137
29291
  if (!attrActionEnum[obj.attr]) {
@@ -29170,7 +29324,7 @@ UrlAttrTransform.fromJSON = function (obj) {
29170
29324
  }
29171
29325
  }
29172
29326
  try {
29173
- return new UrlAttrTransform(obj.attr, obj.action, obj.data);
29327
+ return new UrlTransform(obj.attr, obj.action, obj.data, obj.target);
29174
29328
  }
29175
29329
  catch (error) {
29176
29330
  // If it's already a detailed error from our validation, re-throw it
@@ -29179,7 +29333,7 @@ UrlAttrTransform.fromJSON = function (obj) {
29179
29333
  throw error;
29180
29334
  }
29181
29335
  // Otherwise, wrap the original error with context
29182
- throw new Error('Failed to create UrlAttrTransform: ' + error.message + '. Transform object: ' + JSON.stringify(obj));
29336
+ throw new Error('Failed to create UrlTransform: ' + error.message + '. Transform object: ' + JSON.stringify(obj));
29183
29337
  }
29184
29338
  };
29185
29339
 
@@ -29189,13 +29343,13 @@ function isURL(u) {
29189
29343
  var LocationModule = (function (global) {
29190
29344
  var createElectronTranforms = function (appname, resourcesPath, directory, userHome) {
29191
29345
  return [
29192
- new UrlAttrTransform('pathname', 'Replace', function (path) {
29346
+ new UrlTransform('pathname', 'Replace', function (path) {
29193
29347
  var newPath = path.replace(resourcesPath, appname);
29194
29348
  newPath = newPath.replace(directory, appname);
29195
29349
  newPath = newPath.replace(userHome, '');
29196
29350
  return newPath;
29197
29351
  }),
29198
- new UrlAttrTransform('href', 'ForceSecure')
29352
+ new UrlTransform('href', 'ForceSecure')
29199
29353
  ];
29200
29354
  };
29201
29355
  var DEFAULT_SRC_FN = function () {
@@ -29234,17 +29388,23 @@ var LocationModule = (function (global) {
29234
29388
  },
29235
29389
  addTransforms(context, transforms) {
29236
29390
  _.each(transforms, function (f) {
29237
- context.commit('addTransform', UrlAttrTransform.fromJSON(f));
29391
+ const t = UrlTransform.fromJSON(f);
29392
+ if (t.target === 'location' || t.target === 'both') {
29393
+ context.commit('addTransform', t);
29394
+ }
29395
+ if (t.target === 'attribute' || t.target === 'both') {
29396
+ store.dispatch('nodeAttr/addAttrTransform', t);
29397
+ }
29238
29398
  });
29239
29399
  },
29240
29400
  addInternalTransforms(context, transforms) {
29241
29401
  _.each(transforms, function (f) {
29242
- context.commit('addInternalTransform', UrlAttrTransform.fromJSON(f));
29402
+ context.commit('addInternalTransform', UrlTransform.fromJSON(f));
29243
29403
  });
29244
29404
  },
29245
29405
  removeInternalTransforms(context, transforms) {
29246
29406
  _.each(transforms, function (f) {
29247
- context.commit('removeInternalTransform', UrlAttrTransform.fromJSON(f));
29407
+ context.commit('removeInternalTransform', UrlTransform.fromJSON(f));
29248
29408
  });
29249
29409
  },
29250
29410
  startPolling(context) {
@@ -29272,6 +29432,7 @@ var LocationModule = (function (global) {
29272
29432
  },
29273
29433
  clearTransforms(state) {
29274
29434
  state.transforms = [];
29435
+ store.commit('nodeAttr/clearURLTransforms');
29275
29436
  },
29276
29437
  clearInternalTransforms(state) {
29277
29438
  state.internalTransforms = [];
@@ -29327,7 +29488,7 @@ var LocationModule = (function (global) {
29327
29488
  };
29328
29489
  var transformUrl = function (transforms, url) {
29329
29490
  return _.reduce(transforms, function (tUrl, f) {
29330
- return f.applyToURL(tUrl);
29491
+ return f.applyTo(tUrl);
29331
29492
  }, url);
29332
29493
  };
29333
29494
  var urlGetter = function (state) {
@@ -32544,10 +32705,12 @@ var GuideUpdateModule = (function () {
32544
32705
  state.scheduledUpdate = scheduledUpdate;
32545
32706
  },
32546
32707
  startUpdate(state, time) {
32708
+ PerformanceMonitor$1.startTimer('guide-loop');
32547
32709
  state.needsUpdate = false;
32548
32710
  state.updateId = time;
32549
32711
  },
32550
32712
  completeUpdate(state, time) {
32713
+ PerformanceMonitor$1.stopTimer('guide-loop');
32551
32714
  state.updateId = null;
32552
32715
  state.updateCompleteTime = time;
32553
32716
  }
@@ -32656,75 +32819,6 @@ var HealthCheckModule = (function () {
32656
32819
  };
32657
32820
  })();
32658
32821
 
32659
- var MonitoringModule = (function () {
32660
- var state = {
32661
- lastFlush: getNow(),
32662
- flushInterval: 5 * 60 * 1000,
32663
- counters: {
32664
- ptmGifFailures: 0,
32665
- guideGifFailures: 0
32666
- },
32667
- timers: {}
32668
- };
32669
- var actions = {
32670
- flush(context) {
32671
- _.each(context.state.counters, function (value, counterName) {
32672
- context.commit('resetCounter', counterName);
32673
- });
32674
- _.each(context.state.timers, function (value, timerName) {
32675
- context.commit('resetTimer', timerName);
32676
- });
32677
- context.commit('setLastFlush', context.getters.now());
32678
- }
32679
- };
32680
- var mutations = {
32681
- setLastFlush(state, lastFlush) {
32682
- state.lastFlush = lastFlush;
32683
- },
32684
- incrementCounter(state, counterName) {
32685
- if (state.counters[counterName] === undefined)
32686
- return;
32687
- state.counters[counterName]++;
32688
- },
32689
- resetCounter(state, counterName) {
32690
- if (state.counters[counterName] === undefined)
32691
- return;
32692
- state.counters[counterName] = 0;
32693
- },
32694
- setTimer(state, { name, time } = {}) {
32695
- if (!name || time == null)
32696
- return;
32697
- state.timers[name] = time;
32698
- },
32699
- resetTimer(state, timerName) {
32700
- delete state.timers[timerName];
32701
- }
32702
- };
32703
- var getters = {
32704
- shouldFlush(state, getters) {
32705
- return (getters.now() - state.lastFlush) >= state.flushInterval;
32706
- },
32707
- payload(state) {
32708
- var counters = _.reduce(state.counters, function (obj, value, key) {
32709
- if (value > 0) {
32710
- obj[key] = value;
32711
- }
32712
- return obj;
32713
- }, {});
32714
- return _.extend({}, counters, state.timers);
32715
- },
32716
- now() {
32717
- return getNow();
32718
- }
32719
- };
32720
- return {
32721
- state,
32722
- actions,
32723
- mutations,
32724
- getters
32725
- };
32726
- })();
32727
-
32728
32822
  var MetadataModule = (function () {
32729
32823
  var state = {
32730
32824
  visitor: {},
@@ -32913,7 +33007,6 @@ const DebuggerModule = (() => {
32913
33007
  context.commit('setTabId', EventTracer.getTabId());
32914
33008
  context.commit('setInstallType', getInstallType());
32915
33009
  const isTop = isLeader() && window.top === window; // only a leader frame that is a top window can launch debugger
32916
- state.isTop !== isTop;
32917
33010
  context.commit('setIsTop', isTop);
32918
33011
  context.commit('isLeader', isLeader());
32919
33012
  if (isTop) {
@@ -32964,6 +33057,8 @@ const DebuggerModule = (() => {
32964
33057
  context.commit('enableEventLogging', enabled);
32965
33058
  },
32966
33059
  eventCaptured: (context, data) => {
33060
+ if (pendo$1.designerEnabled || isInPreviewMode() || isInDesignerPreviewMode())
33061
+ return;
32967
33062
  if (!context.state.isTop) {
32968
33063
  crossFrameChannel.postMessage({
32969
33064
  action: 'debugger/receiveEventCaptured',
@@ -33202,6 +33297,68 @@ const DebuggerModule = (() => {
33202
33297
  };
33203
33298
  })();
33204
33299
 
33300
+ const NodeAttrModule = (function (global) {
33301
+ const state = {
33302
+ transforms: []
33303
+ };
33304
+ const actions = {
33305
+ addAttrTransform(context, urlTransform) {
33306
+ context.commit('addTransform', urlTransform);
33307
+ }
33308
+ };
33309
+ const mutations = {
33310
+ addTransform(state, transform) {
33311
+ state.transforms.push(transform);
33312
+ },
33313
+ clearURLTransforms(state) {
33314
+ // XXX if ever support non url transforms for attributes then we'll need to check the Transform type
33315
+ // or determine some other way how to purge only URL transforms as Location API provides a way for these to be
33316
+ // cleared via the API.
33317
+ state.transforms = [];
33318
+ }
33319
+ };
33320
+ const applyTransforms = (transforms, url) => {
33321
+ return _.reduce(transforms, function (tUrl, f) {
33322
+ return f.applyTo(tUrl);
33323
+ }, url);
33324
+ };
33325
+ const resolveAttrValueAsURL = (attrValue) => {
33326
+ // let's let the browser do this work for us...
33327
+ const a = document.createElement('a'); // this handles relative urls nicely
33328
+ a.href = attrValue;
33329
+ return new URL(a.href);
33330
+ };
33331
+ const getters = {
33332
+ getAttribute(state) {
33333
+ return function (element, attrName) {
33334
+ let attrValue = element.getAttribute ? element.getAttribute(attrName) : element[attrName];
33335
+ if (_.size(state.transforms) < 1) {
33336
+ return attrValue;
33337
+ }
33338
+ try {
33339
+ let url = resolveAttrValueAsURL(attrValue);
33340
+ url = _.reduce([
33341
+ _.partial(applyTransforms, state.transforms)
33342
+ ], (url, fn) => {
33343
+ return fn(url);
33344
+ }, url);
33345
+ return url.href;
33346
+ }
33347
+ catch (e) {
33348
+ log.critical(e);
33349
+ return attrValue;
33350
+ }
33351
+ };
33352
+ }
33353
+ };
33354
+ return {
33355
+ state,
33356
+ actions,
33357
+ mutations,
33358
+ getters
33359
+ };
33360
+ })();
33361
+
33205
33362
  function registerModules(store) {
33206
33363
  store.registerModule('debugger', DebuggerModule);
33207
33364
  store.registerModule('errorLog', ErrorLogModule);
@@ -33210,9 +33367,9 @@ function registerModules(store) {
33210
33367
  store.registerModule('guideState', GuideStateModule);
33211
33368
  store.registerModule('healthCheck', HealthCheckModule);
33212
33369
  store.registerModule('location', LocationModule);
33213
- store.registerModule('monitoring', MonitoringModule);
33214
33370
  store.registerModule('metadata', MetadataModule);
33215
33371
  store.registerModule('preview', PreviewModule);
33372
+ store.registerModule('nodeAttr', NodeAttrModule);
33216
33373
  }
33217
33374
 
33218
33375
  const IFrameMonitor = (function () {
@@ -36255,7 +36412,7 @@ const EventProperties = (function () {
36255
36412
  return [];
36256
36413
  }
36257
36414
  return [
36258
- new UrlAttrTransform('search', 'AddTo', function () { return collectPageEventProperties(); })
36415
+ new UrlTransform('search', 'AddTo', function () { return collectPageEventProperties(); })
36259
36416
  ];
36260
36417
  }
36261
36418
  function createPageEventProperties() {
@@ -47297,6 +47454,7 @@ class SessionRecorder {
47297
47454
  this.sendQueue = new SendQueue(bind(this.transport.send, this.transport));
47298
47455
  this.sendQueue.onTimeout = bind(this.sendFailure, this, 'SEND_TIMEOUT');
47299
47456
  this.isNewSession = false;
47457
+ this.isCheckingVisitorEligibility = false;
47300
47458
  this.addConfigOptions();
47301
47459
  this.config = configReader.get(RECORDING_CONFIG);
47302
47460
  this.onRecordingStart = this.pendo._.isFunction(configReader.get(RECORDING_CONFIG_ON_RECORDING_START))
@@ -47399,19 +47557,32 @@ class SessionRecorder {
47399
47557
  clearTimeout(this._changeIdentityTimer);
47400
47558
  this.visitorId = options.visitor.id;
47401
47559
  this.accountId = options.account.id;
47402
- return this.fetchVisitorConfig().then(visitorConfig => {
47403
- if ((!visitorConfig || !visitorConfig.enable) && this.isRecording()) {
47404
- this.stop();
47405
- }
47406
- else if (visitorConfig && visitorConfig.enable && !this.isRecording()) {
47407
- this._startRecordingForVisitor(visitorConfig);
47408
- }
47409
- }).catch((e) => {
47410
- this.api.log.critical('Failed to re-fetch recording config', { error: e });
47411
- this.logStopReason('VISITOR_CONFIG_ERROR');
47412
- });
47560
+ return this.checkVisitorEligibility();
47413
47561
  }
47414
47562
  }
47563
+ checkVisitorEligibility(continuationCallback) {
47564
+ if (!this.isRecording() && this.pendo._.isNull(this.visitorId) && this.pendo._.isNull(this.accountId))
47565
+ return;
47566
+ this.isCheckingVisitorEligibility = true;
47567
+ return this.fetchVisitorConfig().then(visitorConfig => {
47568
+ if ((!visitorConfig || !visitorConfig.enable) && this.isRecording()) {
47569
+ this.stop();
47570
+ }
47571
+ if (visitorConfig && visitorConfig.enable && this.isRecording()) {
47572
+ if (continuationCallback) {
47573
+ continuationCallback.call(this);
47574
+ }
47575
+ }
47576
+ if (visitorConfig && visitorConfig.enable && !this.isRecording()) {
47577
+ this._startRecordingForVisitor(visitorConfig);
47578
+ }
47579
+ }).catch((e) => {
47580
+ this.api.log.critical('Failed to re-fetch recording config', { error: e });
47581
+ this.logStopReason('VISITOR_CONFIG_ERROR');
47582
+ }).finally(() => {
47583
+ this.isCheckingVisitorEligibility = false;
47584
+ });
47585
+ }
47415
47586
  snapshot() {
47416
47587
  if (!this.isRecording())
47417
47588
  return;
@@ -47673,13 +47844,24 @@ class SessionRecorder {
47673
47844
  if ((!prevLastEmitTime && isMeta)) {
47674
47845
  skipUserInteractionCheck = true;
47675
47846
  }
47676
- this.storeLastUserInteractionEventInformation(event, this.visitorId, this.accountId, skipUserInteractionCheck);
47847
+ // Ensures events emitted while the visitor eligibility check is in flight are compared against events emitted
47848
+ // before the visitor eligibility check occurred. This means these events which fall outside of the inactivity
47849
+ // duration will not be placed in the buffer and captured. This is ok since they occurred outside of the
47850
+ // inactivity period and before the next snapshot.
47851
+ if (!this.isCheckingVisitorEligibility) {
47852
+ this.storeLastUserInteractionEventInformation(event, this.visitorId, this.accountId, skipUserInteractionCheck);
47853
+ }
47677
47854
  const inactivityDuration = this.pendo._.get(this.visitorConfig, 'inactivityDuration', THIRTY_MINUTES);
47678
47855
  if (prevLastEmitTime && event.timestamp - prevLastEmitTime > inactivityDuration && !isSnapshot && !isMeta) {
47679
- this.send();
47680
- this.clearSessionInfo();
47681
- this.snapshot();
47682
- return; // ignore this current event, since we're about to get a fresh snapshot
47856
+ if (!this.isCheckingVisitorEligibility) {
47857
+ const continuationCallback = function () {
47858
+ this.send();
47859
+ this.clearSessionInfo();
47860
+ this.snapshot();
47861
+ };
47862
+ this.checkVisitorEligibility(continuationCallback);
47863
+ }
47864
+ return;
47683
47865
  }
47684
47866
  /*
47685
47867
  * META events and Snapshots are special and need to be packaged together. This is made possible by
@@ -47714,8 +47896,16 @@ class SessionRecorder {
47714
47896
  const exceedsEventFreq = this.eventsSinceLastKeyFrame >= this.pendo._.get(this.visitorConfig, 'keyframeEventFrequency', 4548); // Defaults determined by BE originally, shouldn't really ever be used
47715
47897
  const exceedsRecordingSizeLimit = this.currentRecordingSize >= this.pendo._.get(this.visitorConfig, 'recordingSizeLimit', ONE_HUNDRED_MB_IN_BYTES);
47716
47898
  if (exceeds24Hours || (exceedsTimeFreq && exceedsEventFreq) || exceedsRecordingSizeLimit) {
47717
- this.currentRecordingSize = 0;
47718
- this.snapshot();
47899
+ const continuationCallback = function () {
47900
+ this.currentRecordingSize = 0;
47901
+ this.snapshot();
47902
+ };
47903
+ if (exceeds24Hours && !this.isCheckingVisitorEligibility) {
47904
+ this.checkVisitorEligibility(continuationCallback);
47905
+ }
47906
+ else {
47907
+ continuationCallback.call(this);
47908
+ }
47719
47909
  }
47720
47910
  }
47721
47911
  }
@@ -48954,17 +49144,14 @@ class ConsoleCaptureBuffer {
48954
49144
  constructor(pendo, pluginAPI) {
48955
49145
  this.pendo = pendo;
48956
49146
  this.pluginAPI = pluginAPI;
48957
- this.infoEvents = [];
48958
- this.warnEvents = [];
48959
- this.errorEvents = [];
49147
+ this.events = [];
48960
49148
  this.lastEvent = null;
48961
- this.devLogEvent = null;
48962
49149
  this.tokens = TOKEN_MAX_SIZE;
48963
49150
  this.lastRefillTime = this.pluginAPI.util.getNow();
48964
49151
  this.nextRefillTime = this.lastRefillTime + TOKEN_REFRESH_INTERVAL;
48965
49152
  }
48966
49153
  isEmpty() {
48967
- return this.infoEvents.length === 0 && this.warnEvents.length === 0 && this.errorEvents.length === 0;
49154
+ return this.events.length === 0;
48968
49155
  }
48969
49156
  refillTokens() {
48970
49157
  const now = this.pluginAPI.util.getNow();
@@ -48978,10 +49165,8 @@ class ConsoleCaptureBuffer {
48978
49165
  }
48979
49166
  }
48980
49167
  clear() {
49168
+ this.events = [];
48981
49169
  this.lastEvent = null;
48982
- this.infoEvents.length = 0;
48983
- this.warnEvents.length = 0;
48984
- this.errorEvents.length = 0;
48985
49170
  }
48986
49171
  push(event) {
48987
49172
  const { devLogLevel } = event;
@@ -48991,50 +49176,16 @@ class ConsoleCaptureBuffer {
48991
49176
  if (this.tokens === 0)
48992
49177
  return false;
48993
49178
  this.lastEvent = event;
48994
- this.processEvent(event);
49179
+ this.events.push(event);
48995
49180
  this.tokens = Math.max(this.tokens - 1, 0);
48996
49181
  return true;
48997
49182
  }
48998
- processEvent(event) {
48999
- switch (event.devLogLevel) {
49000
- case 'info':
49001
- this.infoEvents.push(event);
49002
- break;
49003
- case 'warn':
49004
- this.warnEvents.push(event);
49005
- break;
49006
- case 'error':
49007
- this.errorEvents.push(event);
49008
- break;
49009
- }
49010
- }
49011
- createDevLogEventWithPayload({ events, level }) {
49012
- if (events.length === 0)
49013
- return;
49014
- const totalCount = this.pendo._.reduce(events, (sum, event) => sum + (event.devLogCount || 1), 0);
49015
- return Object.assign(Object.assign({}, this.devLogEvent), { devLogLevel: level, devLogCount: totalCount, devLogPayload: this.pendo.compress(events) });
49016
- }
49017
- generateJZB() {
49018
- const payload = [];
49019
- this.pendo._.each(DEV_LOG_LEVELS, level => {
49020
- const events = this[`${level}Events`];
49021
- if (events.length === 0)
49022
- return;
49023
- const devLogEventWithPayload = this.createDevLogEventWithPayload({
49024
- events,
49025
- level
49026
- });
49027
- payload.push(devLogEventWithPayload);
49028
- });
49029
- this.devLogEvent = null;
49030
- return this.pendo.compress(payload);
49031
- }
49032
- pack(url) {
49183
+ pack() {
49033
49184
  if (this.isEmpty())
49034
- return [];
49035
- const jzb = this.generateJZB();
49185
+ return;
49186
+ const jzb = this.pendo.compress(this.events);
49036
49187
  this.clear();
49037
- return [{ url, jzb }];
49188
+ return jzb;
49038
49189
  }
49039
49190
  }
49040
49191
 
@@ -55818,12 +55969,11 @@ function ConsoleCapture() {
55818
55969
  initialize,
55819
55970
  teardown,
55820
55971
  addIntercepts,
55821
- createDevLog,
55972
+ createConsoleEvent,
55822
55973
  captureStackTrace,
55823
55974
  send,
55824
55975
  onAppHidden,
55825
55976
  onAppUnloaded,
55826
- onEventCaptured,
55827
55977
  onPtmPaused,
55828
55978
  onPtmUnpaused,
55829
55979
  get buffer() {
@@ -55857,7 +56007,6 @@ function ConsoleCapture() {
55857
56007
  pluginAPI.Events.ready.on(addIntercepts);
55858
56008
  pluginAPI.Events.appUnloaded.on(onAppUnloaded);
55859
56009
  pluginAPI.Events.appHidden.on(onAppHidden);
55860
- pluginAPI.Events.eventCaptured.on(onEventCaptured);
55861
56010
  pluginAPI.Events['ptm:paused'].on(onPtmPaused);
55862
56011
  pluginAPI.Events['ptm:unpaused'].on(onPtmUnpaused);
55863
56012
  pluginAPI.log.info('Console logs are being captured');
@@ -55868,36 +56017,19 @@ function ConsoleCapture() {
55868
56017
  function onAppUnloaded() {
55869
56018
  send({ unload: true });
55870
56019
  }
55871
- /**
55872
- * Handles eventCaptured events to monitor recordingId changes and/or link devLog events with recordingId
55873
- *
55874
- * @param {object} event - eventCaptured event
55875
- */
55876
- function onEventCaptured(event) {
55877
- if (!event || !event.data || !event.data.length || !buffer.devLogEvent)
55878
- return;
55879
- const capturedEvent = event.data[0];
55880
- const { type, recordingId, recordingSessionId } = capturedEvent;
55881
- if (type === DEV_LOG_TYPE || type === 'identify' || !recordingId || !recordingSessionId)
55882
- return;
55883
- const { recordingId: devLogRecordingId } = buffer.devLogEvent;
55884
- // if recordingId has changed, send current buffer immediately to separate recording sessions
55885
- if (devLogRecordingId && recordingId !== devLogRecordingId) {
55886
- send();
55887
- return;
55888
- }
55889
- }
55890
56020
  function onPtmPaused() {
55891
56021
  isPtmPaused = true;
55892
56022
  }
55893
56023
  function onPtmUnpaused() {
55894
56024
  isPtmPaused = false;
55895
- if (!buffer.devLogEvent) {
55896
- buffer.devLogEvent = createDevLogEvent();
56025
+ if (!buffer.isEmpty()) {
56026
+ for (const event of buffer.events) {
56027
+ pluginAPI.Events.eventCaptured.trigger(event);
56028
+ }
56029
+ send();
55897
56030
  }
55898
- pluginAPI.Events.eventCaptured.trigger(buffer.devLogEvent);
55899
56031
  }
55900
- function createDevLogEvent() {
56032
+ function createDevLogEnvelope() {
55901
56033
  return {
55902
56034
  visitorId: globalPendo.get_visitor_id(),
55903
56035
  accountId: globalPendo.get_account_id(),
@@ -55916,14 +56048,14 @@ function ConsoleCapture() {
55916
56048
  // slice 1 to omit originalFn
55917
56049
  const args = _.toArray(arguments).slice(1);
55918
56050
  originalFn.apply(console, args);
55919
- createDevLog(args, methodName);
56051
+ createConsoleEvent(args, methodName);
55920
56052
  });
55921
56053
  console[methodName]._pendoUnwrap = function () {
55922
56054
  console[methodName] = originalMethod;
55923
56055
  };
55924
56056
  });
55925
56057
  }
55926
- function createDevLog(args, methodName) {
56058
+ function createConsoleEvent(args, methodName) {
55927
56059
  if (!args || args.length === 0)
55928
56060
  return;
55929
56061
  // stringify args
@@ -55951,22 +56083,16 @@ function ConsoleCapture() {
55951
56083
  buffer.lastEvent.devLogCount++;
55952
56084
  return;
55953
56085
  }
55954
- const devLog = {
55955
- devLogLevel: methodName === 'log' ? 'info' : methodName,
55956
- devLogMsg: scrubPII(message),
55957
- devLogTrace: stackTrace,
55958
- devLogTimestamp: pluginAPI.util.getNow(),
55959
- devLogCount: 1
55960
- };
55961
- const wasAccepted = buffer.push(devLog);
56086
+ const devLogEnvelope = createDevLogEnvelope();
56087
+ const consoleEvent = Object.assign(Object.assign({}, devLogEnvelope), { devLogLevel: methodName === 'log' ? 'info' : methodName, devLogMessage: scrubPII(message), devLogTrace: stackTrace, devLogCount: 1 });
56088
+ const wasAccepted = buffer.push(consoleEvent);
55962
56089
  if (wasAccepted) {
55963
- if (buffer.devLogEvent === null && !isPtmPaused) {
55964
- buffer.devLogEvent = createDevLogEvent();
55965
- pluginAPI.Events.eventCaptured.trigger(buffer.devLogEvent);
56090
+ if (!isPtmPaused) {
56091
+ pluginAPI.Events.eventCaptured.trigger(consoleEvent);
55966
56092
  }
55967
56093
  updateLastLog(logKey);
55968
56094
  }
55969
- return devLog;
56095
+ return consoleEvent;
55970
56096
  }
55971
56097
  function captureStackTrace(maxStackFrames) {
55972
56098
  try {
@@ -55986,19 +56112,23 @@ function ConsoleCapture() {
55986
56112
  function send({ unload = false, hidden = false } = {}) {
55987
56113
  if (!buffer || buffer.isEmpty())
55988
56114
  return;
56115
+ if (!globalPendo.isSendingEvents()) {
56116
+ buffer.clear();
56117
+ return;
56118
+ }
55989
56119
  const queryParams = {
55990
56120
  v: globalPendo.VERSION,
55991
56121
  ct: pluginAPI.util.getNow()
55992
56122
  };
55993
56123
  const url = pluginAPI.transmit.buildBaseDataUrl('devlog', globalPendo.apiKey, queryParams);
55994
- const payloads = buffer.pack(url);
55995
- if (!payloads || payloads.length === 0)
56124
+ const jzb = buffer.pack();
56125
+ if (!jzb)
55996
56126
  return;
55997
56127
  if (unload || hidden) {
55998
- sendQueue.drain(payloads, unload);
56128
+ sendQueue.drain([{ url, jzb }], unload);
55999
56129
  }
56000
56130
  else {
56001
- sendQueue.push(...payloads);
56131
+ sendQueue.push({ url, jzb });
56002
56132
  }
56003
56133
  }
56004
56134
  function teardown() {
@@ -56014,7 +56144,6 @@ function ConsoleCapture() {
56014
56144
  }
56015
56145
  pluginAPI.Events.appHidden.off(onAppHidden);
56016
56146
  pluginAPI.Events.appUnloaded.off(onAppUnloaded);
56017
- pluginAPI.Events.eventCaptured.off(onEventCaptured);
56018
56147
  pluginAPI.Events['ptm:paused'].off(onPtmPaused);
56019
56148
  pluginAPI.Events['ptm:unpaused'].off(onPtmUnpaused);
56020
56149
  _.each(CONSOLE_METHODS, function (methodName) {