@pendo/agent 2.291.3 → 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.3_';
3908
- var PACKAGE_VERSION = '2.291.3';
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
  /**
@@ -12196,6 +12203,92 @@ class LocalStorageEventBuffer {
12196
12203
  }
12197
12204
  var localStorageEventBuffer = new LocalStorageEventBuffer();
12198
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
+
12199
12292
  var defaultTrackName = '_PENDO_UNNAMED_';
12200
12293
  var SILO_AVG_COMPRESSION_RATIO = 5;
12201
12294
  /**
@@ -12257,21 +12350,7 @@ function collectEvent(type, props, url, name, eventProperties, context) {
12257
12350
  agenticEventQueue.push(event);
12258
12351
  return;
12259
12352
  }
12260
- eventQueue.push(attachMonitoringData(store, event));
12261
- }
12262
- function attachMonitoringData(store, event) {
12263
- try {
12264
- if (!store.getters['monitoring/shouldFlush']())
12265
- return event;
12266
- var payload = store.getters['monitoring/payload']();
12267
- store.dispatch('monitoring/flush');
12268
- if (!_.isEmpty(payload)) {
12269
- event.ops = payload;
12270
- }
12271
- }
12272
- catch (e) {
12273
- }
12274
- return event;
12353
+ eventQueue.push(event);
12275
12354
  }
12276
12355
  // @const {Event.type[]}
12277
12356
  var WHITELIST_FREE_NPS = ['load', 'meta', 'identify'];
@@ -12710,7 +12789,7 @@ function createSendQueue(options, send, guaranteedSend) {
12710
12789
  }
12711
12790
  });
12712
12791
  queue.onTimeout = function () {
12713
- store.commit('monitoring/incrementCounter', options.beacon + 'GifFailures');
12792
+ PerformanceMonitor$1.track(`${options.beacon}-gif-failure`);
12714
12793
  };
12715
12794
  queue.retryPending = true;
12716
12795
  return queue;
@@ -13002,7 +13081,7 @@ var handleEmbeddedData = function (src) {
13002
13081
  return src + '';
13003
13082
  };
13004
13083
  function getAttributeValue(element, attrName) {
13005
- return element.getAttribute ? element.getAttribute(attrName) : element[attrName];
13084
+ return store.getters['nodeAttr/getAttribute']()(element, attrName);
13006
13085
  }
13007
13086
  var extractAttribute = function (element, attrName, type) {
13008
13087
  if (!element || !element.nodeName)
@@ -21054,10 +21133,6 @@ class UpdateRunner {
21054
21133
  }
21055
21134
  }
21056
21135
  complete(time) {
21057
- store.commit('monitoring/setTimer', {
21058
- name: 'guideUpdateTimeMS',
21059
- time: this.updateTotalTime
21060
- });
21061
21136
  store.commit('guideUpdate/completeUpdate', time);
21062
21137
  this.cancel();
21063
21138
  }
@@ -26540,6 +26615,11 @@ var EventRouter = function () {
26540
26615
  var config = configParam.value;
26541
26616
  var cases = casesParam.value;
26542
26617
  var conditionResult = evaluateCondition(conditionType, config);
26618
+ Events.trigger('appUsage', {
26619
+ type: 'conditionalSplitResult',
26620
+ elementId: evt.srcElement.id,
26621
+ conditionResult
26622
+ });
26543
26623
  var matchingCase = _.find(cases, function (caseItem) {
26544
26624
  return caseItem.value === conditionResult;
26545
26625
  });
@@ -26607,16 +26687,27 @@ var GuideActivity = (function () {
26607
26687
  var isObject = _.isObject;
26608
26688
  var findWhere = _.findWhere;
26609
26689
  const _guideResolvers = [];
26690
+ var conditionalSplitEventQueue = [];
26610
26691
  return {
26611
26692
  type: GA_TYPE,
26612
26693
  handler(evtData) {
26613
26694
  var appData = evtData.data[0];
26695
+ if (appData && appData.type === 'conditionalSplitResult') {
26696
+ finalizeConditionalSplitEvent(appData);
26697
+ return;
26698
+ }
26614
26699
  var browserEvt = evtData.data[1];
26615
26700
  if (!shouldHandleEvent(appData, browserEvt))
26616
26701
  return;
26617
26702
  var activityEvent = transformToGuideActivity(appData);
26618
26703
  if (activityEvent) {
26619
- 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
+ }
26620
26711
  }
26621
26712
  },
26622
26713
  registerGuideResolver(callBack) {
@@ -26629,6 +26720,22 @@ var GuideActivity = (function () {
26629
26720
  }
26630
26721
  }
26631
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
+ }
26632
26739
  function isClickEvent(browserEvt) {
26633
26740
  return browserEvt && browserEvt.type === 'click';
26634
26741
  }
@@ -26745,58 +26852,7 @@ var GuideActivity = (function () {
26745
26852
  ];
26746
26853
  }
26747
26854
  if (element.actions !== undefined) {
26748
- return _.map(element.actions, function (action) {
26749
- switch (action.action) {
26750
- case 'automation':
26751
- var automationIds = findWhere(action.parameters, { name: 'automationIds' });
26752
- return {
26753
- action: action.action,
26754
- automationIds
26755
- };
26756
- case 'openLink':
26757
- var url = findWhere(action.parameters, { name: 'url' });
26758
- var target = findWhere(action.parameters, { name: 'target' });
26759
- return {
26760
- action: action.action,
26761
- url: sanitizeUrl(url.value),
26762
- target: target.value
26763
- };
26764
- case 'submitPollAndGoToStep':
26765
- case 'goToStep':
26766
- var guideStepId = findWhere(action.parameters, { name: 'goToStepId' });
26767
- return {
26768
- action: action.action,
26769
- guideStepId: guideStepId.value
26770
- };
26771
- case 'guideSnoozed':
26772
- var duration = findWhere(action.parameters, { name: 'snooze_duration' });
26773
- var timeUnit = findWhere(action.parameters, { name: 'time_unit' });
26774
- return {
26775
- action: action.action,
26776
- duration: duration.value,
26777
- timeUnit: timeUnit.value
26778
- };
26779
- case 'showGuide':
26780
- case 'launchGuide':
26781
- var guideId = action.parameters[0];
26782
- var stepId = action.parameters[1] || {};
26783
- return {
26784
- action: action.action,
26785
- guideId: guideId.value,
26786
- stepId: stepId.value
26787
- };
26788
- case 'advanceGuide':
26789
- case 'previousStep':
26790
- case 'submitPoll':
26791
- case 'dismissGuide':
26792
- case 'confirmation':
26793
- return {
26794
- action: action.action
26795
- };
26796
- default:
26797
- return {};
26798
- }
26799
- });
26855
+ return _.map(element.actions, processAction);
26800
26856
  }
26801
26857
  if (uiElement.templateName === 'pendo_task_list_item') {
26802
26858
  if (_.isArray(element.templateChildren) && element.templateChildren[uiElement.templateChildIndex]) {
@@ -26808,6 +26864,79 @@ var GuideActivity = (function () {
26808
26864
  }
26809
26865
  return [];
26810
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
+ }
26811
26940
  function transformToGuideActivity(appData) {
26812
26941
  var activeGuideObj = getGuideForEvent(appData);
26813
26942
  if (!activeGuideObj)
@@ -27006,14 +27135,14 @@ var didAccountIdentityChange = function (props) {
27006
27135
  function initIdentityEvents() {
27007
27136
  if (initialized)
27008
27137
  return;
27009
- Events.identify.on(function (event) {
27138
+ const collectIdentifyEvent = function (event) {
27010
27139
  var props = event.data[0];
27011
27140
  if (wasAnonymousVisitorIdentified(props) || didAccountIdentityChange(props)) {
27012
27141
  collectEvent('identify', props);
27013
27142
  flushLater(); // unconditionally on next tick
27014
27143
  }
27015
- });
27016
- Events.metadata.on(function (event) {
27144
+ };
27145
+ const collectMetadataEvent = function (event) {
27017
27146
  // Check to see if this Application is sending metadata to the backend using
27018
27147
  // different mechanism (like Segment webhooks)
27019
27148
  var blockAgentMetadata = isMetadataBlocked();
@@ -27024,9 +27153,17 @@ function initIdentityEvents() {
27024
27153
  flushLater(); // unconditionally on next tick
27025
27154
  }
27026
27155
  }
27027
- });
27156
+ };
27157
+ Events.identify.on(collectIdentifyEvent);
27158
+ Events.metadata.on(collectMetadataEvent);
27028
27159
  Events.identify.on(handleIdentifyEvent);
27029
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
+ };
27030
27167
  }
27031
27168
 
27032
27169
  /*
@@ -27237,7 +27374,7 @@ class ShadowDomManager extends EventTarget {
27237
27374
  }
27238
27375
  initialize() {
27239
27376
  this.unpatchAttachShadow = this.patchAttachShadow(Element);
27240
- this.findShadowRoots(document);
27377
+ this.findShadowRoots(document, 5000, (shadowRoot) => this.triggerAttachShadow(shadowRoot));
27241
27378
  }
27242
27379
  triggerAttachShadow(shadowRoot) {
27243
27380
  this.cache.add(shadowRoot);
@@ -27246,7 +27383,7 @@ class ShadowDomManager extends EventTarget {
27246
27383
  shadowRoot
27247
27384
  });
27248
27385
  }
27249
- findShadowRoots(document, maxPerSearch = 5000) {
27386
+ findShadowRoots(document, maxPerSearch = 5000, onShadowRootFound) {
27250
27387
  if (!_.isFunction(document.createNodeIterator))
27251
27388
  return;
27252
27389
  let nodesChecked = 0;
@@ -27262,7 +27399,7 @@ class ShadowDomManager extends EventTarget {
27262
27399
  while ((currentNode = iterator.nextNode())) {
27263
27400
  nodesChecked++;
27264
27401
  if (currentNode && currentNode.shadowRoot) {
27265
- this.triggerAttachShadow(currentNode.shadowRoot);
27402
+ onShadowRootFound(currentNode.shadowRoot);
27266
27403
  queue.push(currentNode.shadowRoot);
27267
27404
  this.foundShadowRoots = true;
27268
27405
  }
@@ -27383,7 +27520,8 @@ function registerEventHandlers({ events = [] }) {
27383
27520
  let initializeCounter = 0;
27384
27521
  const initializeImmediately = 'initializeImmediately';
27385
27522
  /**
27386
- * 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`.
27387
27525
  *
27388
27526
  * @access public
27389
27527
  * @function
@@ -27529,13 +27667,14 @@ const initialize = makeSafe(function (options) {
27529
27667
  teardownFns.push(forwardInternalEvents(Events));
27530
27668
  registerEventHandlers(options);
27531
27669
  teardownFns.push(initGuides(observer)); // this is safe. loadGuides actually does the loading.
27532
- initIdentityEvents(); // setup identify and meta event listeners
27670
+ teardownFns.push(initIdentityEvents()); // setup identify and meta event listeners
27533
27671
  teardownFns.push(wirePage());
27534
27672
  teardownFns.push(initializePlugins());
27535
27673
  teardownFns.push(launchDesignerOrPreview(options));
27536
27674
  Events.appUsage.on(GuideActivity.handler);
27537
27675
  Events.appUsage.on(ResourceCenterActivity.handler);
27538
27676
  teardownFns.push(flushEvery(SEND_INTERVAL));
27677
+ teardownFns.push(PerformanceMonitor$1.initialize());
27539
27678
  }
27540
27679
  Events.appHidden.on(() => {
27541
27680
  flushNow(true, { hidden: true });
@@ -29028,11 +29167,14 @@ var attrActionEnum = {
29028
29167
  href: ['Replace', 'ForceSecure'],
29029
29168
  protocol: ['ForceSecure']
29030
29169
  };
29031
- function UrlAttrTransform(attr, action, data) {
29170
+ const TARGETS = ['location', 'attribute', 'both'];
29171
+ const DEFAULT_TARGET = 'location';
29172
+ function UrlTransform(attr, action, data, target) {
29032
29173
  if (_.isObject(attr)) {
29033
29174
  action = attr.action;
29034
29175
  data = attr.data;
29035
29176
  attr = attr.attr;
29177
+ target = attr.target;
29036
29178
  }
29037
29179
  if (!attrActionEnum[attr]) {
29038
29180
  var validAttrs = _.keys(attrActionEnum).join(', ');
@@ -29042,11 +29184,16 @@ function UrlAttrTransform(attr, action, data) {
29042
29184
  var validActions = attrActionEnum[attr].join(', ');
29043
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: ...}');
29044
29186
  }
29187
+ // validate 'target'
29188
+ if (TARGETS.indexOf(target) < 0) {
29189
+ target = DEFAULT_TARGET;
29190
+ }
29045
29191
  this.attr = attr;
29046
29192
  this.action = action;
29047
29193
  this.data = data;
29194
+ this.target = target || DEFAULT_TARGET;
29048
29195
  }
29049
- UrlAttrTransform.prototype.applyToURL = function (url) {
29196
+ UrlTransform.prototype.applyTo = function (url) {
29050
29197
  var actionImpls = {
29051
29198
  AllowOnlyKeys(url, attr, data) {
29052
29199
  if (_.isFunction(data)) {
@@ -29119,26 +29266,26 @@ UrlAttrTransform.prototype.applyToURL = function (url) {
29119
29266
  };
29120
29267
  return actionImpls[this.action](url, this.attr, this.data);
29121
29268
  };
29122
- UrlAttrTransform.prototype.toString = function () {
29269
+ UrlTransform.prototype.toString = function () {
29123
29270
  return this.attr + ' - ' + this.action + ' - ' + JSON.stringify(this.data);
29124
29271
  };
29125
29272
  // Static method, not on the instance
29126
- UrlAttrTransform.fromJSON = function (obj) {
29127
- if (obj instanceof UrlAttrTransform) {
29273
+ UrlTransform.fromJSON = function (obj) {
29274
+ if (obj instanceof UrlTransform) {
29128
29275
  return obj;
29129
29276
  }
29130
29277
  // Validate input is an object
29131
29278
  if (!_.isObject(obj)) {
29132
- 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"}}');
29133
29280
  }
29134
29281
  // Validate required properties
29135
29282
  if (!obj.attr) {
29136
29283
  var allValidAttrs = _.keys(attrActionEnum).join(', ');
29137
- 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"}}');
29138
29285
  }
29139
29286
  if (!obj.action) {
29140
29287
  var attrValidActions = attrActionEnum[obj.attr] ? attrActionEnum[obj.attr].join(', ') : 'AllowOnlyKeys, ExcludeKeys, AddTo, Replace, Clear';
29141
- 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"}}');
29142
29289
  }
29143
29290
  // Validate attribute and action combination
29144
29291
  if (!attrActionEnum[obj.attr]) {
@@ -29177,7 +29324,7 @@ UrlAttrTransform.fromJSON = function (obj) {
29177
29324
  }
29178
29325
  }
29179
29326
  try {
29180
- return new UrlAttrTransform(obj.attr, obj.action, obj.data);
29327
+ return new UrlTransform(obj.attr, obj.action, obj.data, obj.target);
29181
29328
  }
29182
29329
  catch (error) {
29183
29330
  // If it's already a detailed error from our validation, re-throw it
@@ -29186,7 +29333,7 @@ UrlAttrTransform.fromJSON = function (obj) {
29186
29333
  throw error;
29187
29334
  }
29188
29335
  // Otherwise, wrap the original error with context
29189
- 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));
29190
29337
  }
29191
29338
  };
29192
29339
 
@@ -29196,13 +29343,13 @@ function isURL(u) {
29196
29343
  var LocationModule = (function (global) {
29197
29344
  var createElectronTranforms = function (appname, resourcesPath, directory, userHome) {
29198
29345
  return [
29199
- new UrlAttrTransform('pathname', 'Replace', function (path) {
29346
+ new UrlTransform('pathname', 'Replace', function (path) {
29200
29347
  var newPath = path.replace(resourcesPath, appname);
29201
29348
  newPath = newPath.replace(directory, appname);
29202
29349
  newPath = newPath.replace(userHome, '');
29203
29350
  return newPath;
29204
29351
  }),
29205
- new UrlAttrTransform('href', 'ForceSecure')
29352
+ new UrlTransform('href', 'ForceSecure')
29206
29353
  ];
29207
29354
  };
29208
29355
  var DEFAULT_SRC_FN = function () {
@@ -29241,17 +29388,23 @@ var LocationModule = (function (global) {
29241
29388
  },
29242
29389
  addTransforms(context, transforms) {
29243
29390
  _.each(transforms, function (f) {
29244
- 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
+ }
29245
29398
  });
29246
29399
  },
29247
29400
  addInternalTransforms(context, transforms) {
29248
29401
  _.each(transforms, function (f) {
29249
- context.commit('addInternalTransform', UrlAttrTransform.fromJSON(f));
29402
+ context.commit('addInternalTransform', UrlTransform.fromJSON(f));
29250
29403
  });
29251
29404
  },
29252
29405
  removeInternalTransforms(context, transforms) {
29253
29406
  _.each(transforms, function (f) {
29254
- context.commit('removeInternalTransform', UrlAttrTransform.fromJSON(f));
29407
+ context.commit('removeInternalTransform', UrlTransform.fromJSON(f));
29255
29408
  });
29256
29409
  },
29257
29410
  startPolling(context) {
@@ -29279,6 +29432,7 @@ var LocationModule = (function (global) {
29279
29432
  },
29280
29433
  clearTransforms(state) {
29281
29434
  state.transforms = [];
29435
+ store.commit('nodeAttr/clearURLTransforms');
29282
29436
  },
29283
29437
  clearInternalTransforms(state) {
29284
29438
  state.internalTransforms = [];
@@ -29334,7 +29488,7 @@ var LocationModule = (function (global) {
29334
29488
  };
29335
29489
  var transformUrl = function (transforms, url) {
29336
29490
  return _.reduce(transforms, function (tUrl, f) {
29337
- return f.applyToURL(tUrl);
29491
+ return f.applyTo(tUrl);
29338
29492
  }, url);
29339
29493
  };
29340
29494
  var urlGetter = function (state) {
@@ -32551,10 +32705,12 @@ var GuideUpdateModule = (function () {
32551
32705
  state.scheduledUpdate = scheduledUpdate;
32552
32706
  },
32553
32707
  startUpdate(state, time) {
32708
+ PerformanceMonitor$1.startTimer('guide-loop');
32554
32709
  state.needsUpdate = false;
32555
32710
  state.updateId = time;
32556
32711
  },
32557
32712
  completeUpdate(state, time) {
32713
+ PerformanceMonitor$1.stopTimer('guide-loop');
32558
32714
  state.updateId = null;
32559
32715
  state.updateCompleteTime = time;
32560
32716
  }
@@ -32663,75 +32819,6 @@ var HealthCheckModule = (function () {
32663
32819
  };
32664
32820
  })();
32665
32821
 
32666
- var MonitoringModule = (function () {
32667
- var state = {
32668
- lastFlush: getNow(),
32669
- flushInterval: 5 * 60 * 1000,
32670
- counters: {
32671
- ptmGifFailures: 0,
32672
- guideGifFailures: 0
32673
- },
32674
- timers: {}
32675
- };
32676
- var actions = {
32677
- flush(context) {
32678
- _.each(context.state.counters, function (value, counterName) {
32679
- context.commit('resetCounter', counterName);
32680
- });
32681
- _.each(context.state.timers, function (value, timerName) {
32682
- context.commit('resetTimer', timerName);
32683
- });
32684
- context.commit('setLastFlush', context.getters.now());
32685
- }
32686
- };
32687
- var mutations = {
32688
- setLastFlush(state, lastFlush) {
32689
- state.lastFlush = lastFlush;
32690
- },
32691
- incrementCounter(state, counterName) {
32692
- if (state.counters[counterName] === undefined)
32693
- return;
32694
- state.counters[counterName]++;
32695
- },
32696
- resetCounter(state, counterName) {
32697
- if (state.counters[counterName] === undefined)
32698
- return;
32699
- state.counters[counterName] = 0;
32700
- },
32701
- setTimer(state, { name, time } = {}) {
32702
- if (!name || time == null)
32703
- return;
32704
- state.timers[name] = time;
32705
- },
32706
- resetTimer(state, timerName) {
32707
- delete state.timers[timerName];
32708
- }
32709
- };
32710
- var getters = {
32711
- shouldFlush(state, getters) {
32712
- return (getters.now() - state.lastFlush) >= state.flushInterval;
32713
- },
32714
- payload(state) {
32715
- var counters = _.reduce(state.counters, function (obj, value, key) {
32716
- if (value > 0) {
32717
- obj[key] = value;
32718
- }
32719
- return obj;
32720
- }, {});
32721
- return _.extend({}, counters, state.timers);
32722
- },
32723
- now() {
32724
- return getNow();
32725
- }
32726
- };
32727
- return {
32728
- state,
32729
- actions,
32730
- mutations,
32731
- getters
32732
- };
32733
- })();
32734
-
32735
32822
  var MetadataModule = (function () {
32736
32823
  var state = {
32737
32824
  visitor: {},
@@ -32920,7 +33007,6 @@ const DebuggerModule = (() => {
32920
33007
  context.commit('setTabId', EventTracer.getTabId());
32921
33008
  context.commit('setInstallType', getInstallType());
32922
33009
  const isTop = isLeader() && window.top === window; // only a leader frame that is a top window can launch debugger
32923
- state.isTop !== isTop;
32924
33010
  context.commit('setIsTop', isTop);
32925
33011
  context.commit('isLeader', isLeader());
32926
33012
  if (isTop) {
@@ -32971,6 +33057,8 @@ const DebuggerModule = (() => {
32971
33057
  context.commit('enableEventLogging', enabled);
32972
33058
  },
32973
33059
  eventCaptured: (context, data) => {
33060
+ if (pendo$1.designerEnabled || isInPreviewMode() || isInDesignerPreviewMode())
33061
+ return;
32974
33062
  if (!context.state.isTop) {
32975
33063
  crossFrameChannel.postMessage({
32976
33064
  action: 'debugger/receiveEventCaptured',
@@ -33209,6 +33297,68 @@ const DebuggerModule = (() => {
33209
33297
  };
33210
33298
  })();
33211
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
+
33212
33362
  function registerModules(store) {
33213
33363
  store.registerModule('debugger', DebuggerModule);
33214
33364
  store.registerModule('errorLog', ErrorLogModule);
@@ -33217,9 +33367,9 @@ function registerModules(store) {
33217
33367
  store.registerModule('guideState', GuideStateModule);
33218
33368
  store.registerModule('healthCheck', HealthCheckModule);
33219
33369
  store.registerModule('location', LocationModule);
33220
- store.registerModule('monitoring', MonitoringModule);
33221
33370
  store.registerModule('metadata', MetadataModule);
33222
33371
  store.registerModule('preview', PreviewModule);
33372
+ store.registerModule('nodeAttr', NodeAttrModule);
33223
33373
  }
33224
33374
 
33225
33375
  const IFrameMonitor = (function () {
@@ -36262,7 +36412,7 @@ const EventProperties = (function () {
36262
36412
  return [];
36263
36413
  }
36264
36414
  return [
36265
- new UrlAttrTransform('search', 'AddTo', function () { return collectPageEventProperties(); })
36415
+ new UrlTransform('search', 'AddTo', function () { return collectPageEventProperties(); })
36266
36416
  ];
36267
36417
  }
36268
36418
  function createPageEventProperties() {
@@ -47304,6 +47454,7 @@ class SessionRecorder {
47304
47454
  this.sendQueue = new SendQueue(bind(this.transport.send, this.transport));
47305
47455
  this.sendQueue.onTimeout = bind(this.sendFailure, this, 'SEND_TIMEOUT');
47306
47456
  this.isNewSession = false;
47457
+ this.isCheckingVisitorEligibility = false;
47307
47458
  this.addConfigOptions();
47308
47459
  this.config = configReader.get(RECORDING_CONFIG);
47309
47460
  this.onRecordingStart = this.pendo._.isFunction(configReader.get(RECORDING_CONFIG_ON_RECORDING_START))
@@ -47406,19 +47557,32 @@ class SessionRecorder {
47406
47557
  clearTimeout(this._changeIdentityTimer);
47407
47558
  this.visitorId = options.visitor.id;
47408
47559
  this.accountId = options.account.id;
47409
- return this.fetchVisitorConfig().then(visitorConfig => {
47410
- if ((!visitorConfig || !visitorConfig.enable) && this.isRecording()) {
47411
- this.stop();
47412
- }
47413
- else if (visitorConfig && visitorConfig.enable && !this.isRecording()) {
47414
- this._startRecordingForVisitor(visitorConfig);
47415
- }
47416
- }).catch((e) => {
47417
- this.api.log.critical('Failed to re-fetch recording config', { error: e });
47418
- this.logStopReason('VISITOR_CONFIG_ERROR');
47419
- });
47560
+ return this.checkVisitorEligibility();
47420
47561
  }
47421
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
+ }
47422
47586
  snapshot() {
47423
47587
  if (!this.isRecording())
47424
47588
  return;
@@ -47680,13 +47844,24 @@ class SessionRecorder {
47680
47844
  if ((!prevLastEmitTime && isMeta)) {
47681
47845
  skipUserInteractionCheck = true;
47682
47846
  }
47683
- 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
+ }
47684
47854
  const inactivityDuration = this.pendo._.get(this.visitorConfig, 'inactivityDuration', THIRTY_MINUTES);
47685
47855
  if (prevLastEmitTime && event.timestamp - prevLastEmitTime > inactivityDuration && !isSnapshot && !isMeta) {
47686
- this.send();
47687
- this.clearSessionInfo();
47688
- this.snapshot();
47689
- 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;
47690
47865
  }
47691
47866
  /*
47692
47867
  * META events and Snapshots are special and need to be packaged together. This is made possible by
@@ -47721,8 +47896,16 @@ class SessionRecorder {
47721
47896
  const exceedsEventFreq = this.eventsSinceLastKeyFrame >= this.pendo._.get(this.visitorConfig, 'keyframeEventFrequency', 4548); // Defaults determined by BE originally, shouldn't really ever be used
47722
47897
  const exceedsRecordingSizeLimit = this.currentRecordingSize >= this.pendo._.get(this.visitorConfig, 'recordingSizeLimit', ONE_HUNDRED_MB_IN_BYTES);
47723
47898
  if (exceeds24Hours || (exceedsTimeFreq && exceedsEventFreq) || exceedsRecordingSizeLimit) {
47724
- this.currentRecordingSize = 0;
47725
- 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
+ }
47726
47909
  }
47727
47910
  }
47728
47911
  }
@@ -48961,17 +49144,14 @@ class ConsoleCaptureBuffer {
48961
49144
  constructor(pendo, pluginAPI) {
48962
49145
  this.pendo = pendo;
48963
49146
  this.pluginAPI = pluginAPI;
48964
- this.infoEvents = [];
48965
- this.warnEvents = [];
48966
- this.errorEvents = [];
49147
+ this.events = [];
48967
49148
  this.lastEvent = null;
48968
- this.devLogEvent = null;
48969
49149
  this.tokens = TOKEN_MAX_SIZE;
48970
49150
  this.lastRefillTime = this.pluginAPI.util.getNow();
48971
49151
  this.nextRefillTime = this.lastRefillTime + TOKEN_REFRESH_INTERVAL;
48972
49152
  }
48973
49153
  isEmpty() {
48974
- return this.infoEvents.length === 0 && this.warnEvents.length === 0 && this.errorEvents.length === 0;
49154
+ return this.events.length === 0;
48975
49155
  }
48976
49156
  refillTokens() {
48977
49157
  const now = this.pluginAPI.util.getNow();
@@ -48985,10 +49165,8 @@ class ConsoleCaptureBuffer {
48985
49165
  }
48986
49166
  }
48987
49167
  clear() {
49168
+ this.events = [];
48988
49169
  this.lastEvent = null;
48989
- this.infoEvents.length = 0;
48990
- this.warnEvents.length = 0;
48991
- this.errorEvents.length = 0;
48992
49170
  }
48993
49171
  push(event) {
48994
49172
  const { devLogLevel } = event;
@@ -48998,50 +49176,16 @@ class ConsoleCaptureBuffer {
48998
49176
  if (this.tokens === 0)
48999
49177
  return false;
49000
49178
  this.lastEvent = event;
49001
- this.processEvent(event);
49179
+ this.events.push(event);
49002
49180
  this.tokens = Math.max(this.tokens - 1, 0);
49003
49181
  return true;
49004
49182
  }
49005
- processEvent(event) {
49006
- switch (event.devLogLevel) {
49007
- case 'info':
49008
- this.infoEvents.push(event);
49009
- break;
49010
- case 'warn':
49011
- this.warnEvents.push(event);
49012
- break;
49013
- case 'error':
49014
- this.errorEvents.push(event);
49015
- break;
49016
- }
49017
- }
49018
- createDevLogEventWithPayload({ events, level }) {
49019
- if (events.length === 0)
49020
- return;
49021
- const totalCount = this.pendo._.reduce(events, (sum, event) => sum + (event.devLogCount || 1), 0);
49022
- return Object.assign(Object.assign({}, this.devLogEvent), { devLogLevel: level, devLogCount: totalCount, devLogPayload: this.pendo.compress(events) });
49023
- }
49024
- generateJZB() {
49025
- const payload = [];
49026
- this.pendo._.each(DEV_LOG_LEVELS, level => {
49027
- const events = this[`${level}Events`];
49028
- if (events.length === 0)
49029
- return;
49030
- const devLogEventWithPayload = this.createDevLogEventWithPayload({
49031
- events,
49032
- level
49033
- });
49034
- payload.push(devLogEventWithPayload);
49035
- });
49036
- this.devLogEvent = null;
49037
- return this.pendo.compress(payload);
49038
- }
49039
- pack(url) {
49183
+ pack() {
49040
49184
  if (this.isEmpty())
49041
- return [];
49042
- const jzb = this.generateJZB();
49185
+ return;
49186
+ const jzb = this.pendo.compress(this.events);
49043
49187
  this.clear();
49044
- return [{ url, jzb }];
49188
+ return jzb;
49045
49189
  }
49046
49190
  }
49047
49191
 
@@ -55825,12 +55969,11 @@ function ConsoleCapture() {
55825
55969
  initialize,
55826
55970
  teardown,
55827
55971
  addIntercepts,
55828
- createDevLog,
55972
+ createConsoleEvent,
55829
55973
  captureStackTrace,
55830
55974
  send,
55831
55975
  onAppHidden,
55832
55976
  onAppUnloaded,
55833
- onEventCaptured,
55834
55977
  onPtmPaused,
55835
55978
  onPtmUnpaused,
55836
55979
  get buffer() {
@@ -55864,7 +56007,6 @@ function ConsoleCapture() {
55864
56007
  pluginAPI.Events.ready.on(addIntercepts);
55865
56008
  pluginAPI.Events.appUnloaded.on(onAppUnloaded);
55866
56009
  pluginAPI.Events.appHidden.on(onAppHidden);
55867
- pluginAPI.Events.eventCaptured.on(onEventCaptured);
55868
56010
  pluginAPI.Events['ptm:paused'].on(onPtmPaused);
55869
56011
  pluginAPI.Events['ptm:unpaused'].on(onPtmUnpaused);
55870
56012
  pluginAPI.log.info('Console logs are being captured');
@@ -55875,36 +56017,19 @@ function ConsoleCapture() {
55875
56017
  function onAppUnloaded() {
55876
56018
  send({ unload: true });
55877
56019
  }
55878
- /**
55879
- * Handles eventCaptured events to monitor recordingId changes and/or link devLog events with recordingId
55880
- *
55881
- * @param {object} event - eventCaptured event
55882
- */
55883
- function onEventCaptured(event) {
55884
- if (!event || !event.data || !event.data.length || !buffer.devLogEvent)
55885
- return;
55886
- const capturedEvent = event.data[0];
55887
- const { type, recordingId, recordingSessionId } = capturedEvent;
55888
- if (type === DEV_LOG_TYPE || type === 'identify' || !recordingId || !recordingSessionId)
55889
- return;
55890
- const { recordingId: devLogRecordingId } = buffer.devLogEvent;
55891
- // if recordingId has changed, send current buffer immediately to separate recording sessions
55892
- if (devLogRecordingId && recordingId !== devLogRecordingId) {
55893
- send();
55894
- return;
55895
- }
55896
- }
55897
56020
  function onPtmPaused() {
55898
56021
  isPtmPaused = true;
55899
56022
  }
55900
56023
  function onPtmUnpaused() {
55901
56024
  isPtmPaused = false;
55902
- if (!buffer.devLogEvent) {
55903
- buffer.devLogEvent = createDevLogEvent();
56025
+ if (!buffer.isEmpty()) {
56026
+ for (const event of buffer.events) {
56027
+ pluginAPI.Events.eventCaptured.trigger(event);
56028
+ }
56029
+ send();
55904
56030
  }
55905
- pluginAPI.Events.eventCaptured.trigger(buffer.devLogEvent);
55906
56031
  }
55907
- function createDevLogEvent() {
56032
+ function createDevLogEnvelope() {
55908
56033
  return {
55909
56034
  visitorId: globalPendo.get_visitor_id(),
55910
56035
  accountId: globalPendo.get_account_id(),
@@ -55923,14 +56048,14 @@ function ConsoleCapture() {
55923
56048
  // slice 1 to omit originalFn
55924
56049
  const args = _.toArray(arguments).slice(1);
55925
56050
  originalFn.apply(console, args);
55926
- createDevLog(args, methodName);
56051
+ createConsoleEvent(args, methodName);
55927
56052
  });
55928
56053
  console[methodName]._pendoUnwrap = function () {
55929
56054
  console[methodName] = originalMethod;
55930
56055
  };
55931
56056
  });
55932
56057
  }
55933
- function createDevLog(args, methodName) {
56058
+ function createConsoleEvent(args, methodName) {
55934
56059
  if (!args || args.length === 0)
55935
56060
  return;
55936
56061
  // stringify args
@@ -55958,22 +56083,16 @@ function ConsoleCapture() {
55958
56083
  buffer.lastEvent.devLogCount++;
55959
56084
  return;
55960
56085
  }
55961
- const devLog = {
55962
- devLogLevel: methodName === 'log' ? 'info' : methodName,
55963
- devLogMsg: scrubPII(message),
55964
- devLogTrace: stackTrace,
55965
- devLogTimestamp: pluginAPI.util.getNow(),
55966
- devLogCount: 1
55967
- };
55968
- 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);
55969
56089
  if (wasAccepted) {
55970
- if (buffer.devLogEvent === null && !isPtmPaused) {
55971
- buffer.devLogEvent = createDevLogEvent();
55972
- pluginAPI.Events.eventCaptured.trigger(buffer.devLogEvent);
56090
+ if (!isPtmPaused) {
56091
+ pluginAPI.Events.eventCaptured.trigger(consoleEvent);
55973
56092
  }
55974
56093
  updateLastLog(logKey);
55975
56094
  }
55976
- return devLog;
56095
+ return consoleEvent;
55977
56096
  }
55978
56097
  function captureStackTrace(maxStackFrames) {
55979
56098
  try {
@@ -55993,19 +56112,23 @@ function ConsoleCapture() {
55993
56112
  function send({ unload = false, hidden = false } = {}) {
55994
56113
  if (!buffer || buffer.isEmpty())
55995
56114
  return;
56115
+ if (!globalPendo.isSendingEvents()) {
56116
+ buffer.clear();
56117
+ return;
56118
+ }
55996
56119
  const queryParams = {
55997
56120
  v: globalPendo.VERSION,
55998
56121
  ct: pluginAPI.util.getNow()
55999
56122
  };
56000
56123
  const url = pluginAPI.transmit.buildBaseDataUrl('devlog', globalPendo.apiKey, queryParams);
56001
- const payloads = buffer.pack(url);
56002
- if (!payloads || payloads.length === 0)
56124
+ const jzb = buffer.pack();
56125
+ if (!jzb)
56003
56126
  return;
56004
56127
  if (unload || hidden) {
56005
- sendQueue.drain(payloads, unload);
56128
+ sendQueue.drain([{ url, jzb }], unload);
56006
56129
  }
56007
56130
  else {
56008
- sendQueue.push(...payloads);
56131
+ sendQueue.push({ url, jzb });
56009
56132
  }
56010
56133
  }
56011
56134
  function teardown() {
@@ -56021,7 +56144,6 @@ function ConsoleCapture() {
56021
56144
  }
56022
56145
  pluginAPI.Events.appHidden.off(onAppHidden);
56023
56146
  pluginAPI.Events.appUnloaded.off(onAppUnloaded);
56024
- pluginAPI.Events.eventCaptured.off(onEventCaptured);
56025
56147
  pluginAPI.Events['ptm:paused'].off(onPtmPaused);
56026
56148
  pluginAPI.Events['ptm:unpaused'].off(onPtmUnpaused);
56027
56149
  _.each(CONSOLE_METHODS, function (methodName) {