@pendo/agent 2.298.2 → 2.299.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.
- package/README.md +1 -0
- package/dist/dom.esm.js +4 -3
- package/dist/pendo.module.js +650 -140
- package/dist/pendo.module.min.js +10 -10
- package/dist/servers.json +7 -7
- package/index.js +1 -1
- package/package.json +1 -1
package/dist/pendo.module.js
CHANGED
|
@@ -3151,7 +3151,6 @@ var ConfigReader = (function () {
|
|
|
3151
3151
|
* @type {boolean}
|
|
3152
3152
|
*/
|
|
3153
3153
|
addOption('enableDebugEvents', [SNIPPET_SRC, PENDO_CONFIG_SRC]);
|
|
3154
|
-
addOption('freeNPSData');
|
|
3155
3154
|
addOption('eventPropertyConfigurations');
|
|
3156
3155
|
/**
|
|
3157
3156
|
* By default, a click event property rule can be matched against the element that is clicked on or its
|
|
@@ -3539,7 +3538,9 @@ var ConfigReader = (function () {
|
|
|
3539
3538
|
addOption('enableAllEmbeddedGuideEvents', [PENDO_CONFIG_SRC], false);
|
|
3540
3539
|
// Form Validation
|
|
3541
3540
|
addOption('formValidation', [PENDO_CONFIG_SRC], false);
|
|
3541
|
+
// Performance Metrics
|
|
3542
3542
|
addOption('performanceMetricsEnabled', [SNIPPET_SRC, PENDO_CONFIG_SRC], true);
|
|
3543
|
+
addOption('sendPerformanceMetrics', [SNIPPET_SRC, PENDO_CONFIG_SRC], false);
|
|
3543
3544
|
}
|
|
3544
3545
|
initializeOptions();
|
|
3545
3546
|
var sourceGetters = {};
|
|
@@ -3904,8 +3905,8 @@ let SERVER = '';
|
|
|
3904
3905
|
let ASSET_HOST = '';
|
|
3905
3906
|
let ASSET_PATH = '';
|
|
3906
3907
|
let DESIGNER_SERVER = '';
|
|
3907
|
-
let VERSION = '2.
|
|
3908
|
-
let PACKAGE_VERSION = '2.
|
|
3908
|
+
let VERSION = '2.299.0_';
|
|
3909
|
+
let PACKAGE_VERSION = '2.299.0';
|
|
3909
3910
|
let LOADER = 'xhr';
|
|
3910
3911
|
/* eslint-enable agent-eslint-rules/no-gulp-env-references */
|
|
3911
3912
|
/**
|
|
@@ -9792,18 +9793,18 @@ function writeErrorPOST(msg) {
|
|
|
9792
9793
|
*/
|
|
9793
9794
|
function writeMetricsPOST(payload) {
|
|
9794
9795
|
try {
|
|
9795
|
-
const url = `${HOST}/data/agentmetrics
|
|
9796
|
-
log.debug(`
|
|
9796
|
+
const url = `${HOST}/data/agentmetrics/${pendo$1.apiKey}`;
|
|
9797
|
+
log.debug(`Sending ${JSON.stringify(payload)} to ${url} using ${fetchKeepalive.supported() ? 'fetch' : 'xhr'}`);
|
|
9797
9798
|
if (fetchKeepalive.supported()) {
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
|
|
9802
|
-
|
|
9803
|
-
|
|
9799
|
+
fetch(url, {
|
|
9800
|
+
method: 'POST',
|
|
9801
|
+
keepalive: true,
|
|
9802
|
+
body: JSON.stringify(payload),
|
|
9803
|
+
headers: { 'Content-Type': 'application/json' }
|
|
9804
|
+
});
|
|
9804
9805
|
}
|
|
9805
9806
|
else {
|
|
9806
|
-
|
|
9807
|
+
ajax.postJSON(url, payload);
|
|
9807
9808
|
}
|
|
9808
9809
|
}
|
|
9809
9810
|
catch (e) {
|
|
@@ -11275,7 +11276,7 @@ const trimString = (str, limit) => {
|
|
|
11275
11276
|
return trimSurrogate(str.substring(0, limit));
|
|
11276
11277
|
};
|
|
11277
11278
|
function getTextValue(elem, limit) {
|
|
11278
|
-
if (elem.tagName && ['textarea', 'input']
|
|
11279
|
+
if (elem.tagName && _.contains(['textarea', 'input'], elem.tagName.toLowerCase())) {
|
|
11279
11280
|
return trimString(elem.value, limit);
|
|
11280
11281
|
}
|
|
11281
11282
|
return getText(elem, limit);
|
|
@@ -12225,27 +12226,50 @@ var localStorageEventBuffer = new LocalStorageEventBuffer();
|
|
|
12225
12226
|
*
|
|
12226
12227
|
* See https://opentelemetry.io/docs/concepts/signals/metrics/#metric-instruments
|
|
12227
12228
|
*/
|
|
12228
|
-
const
|
|
12229
|
-
|
|
12230
|
-
return
|
|
12231
|
-
|
|
12232
|
-
return _.reduce(entries, (count, e) => {
|
|
12233
|
-
const incr = e.detail && e.detail.incr ? e.detail.incr : 1;
|
|
12234
|
-
return count + incr;
|
|
12235
|
-
}, 0);
|
|
12236
|
-
}
|
|
12237
|
-
return 0;
|
|
12229
|
+
const counter = (entries) => {
|
|
12230
|
+
return _.reduce(entries, (count, entry) => {
|
|
12231
|
+
return count + _.get(entry, 'detail.increment', 1);
|
|
12232
|
+
}, 0);
|
|
12238
12233
|
};
|
|
12239
12234
|
|
|
12240
|
-
const
|
|
12241
|
-
const
|
|
12242
|
-
|
|
12243
|
-
|
|
12244
|
-
|
|
12245
|
-
|
|
12235
|
+
const GUIDE_LOOP_TIMEOUT = 'pendo-guide-loop-timeout';
|
|
12236
|
+
const BEACON_GIF_FAILURES = {
|
|
12237
|
+
ptm: 'pendo-ptm-gif-failure',
|
|
12238
|
+
guide: 'pendo-guide-gif-failure',
|
|
12239
|
+
poll: 'pendo-poll-gif-failure',
|
|
12240
|
+
agentic: 'pendo-agentic-gif-failure'
|
|
12241
|
+
};
|
|
12242
|
+
const GUIDE_LOOP_TIMER = 'pendo-guide-loop';
|
|
12243
|
+
const EVENT_CAPTURED_TIMER = 'pendo-event-captured';
|
|
12244
|
+
const INITIALIZE = 'pendo-initialize';
|
|
12245
|
+
const TEARDOWN = 'pendo-teardown';
|
|
12246
|
+
const METRICS = {};
|
|
12247
|
+
METRICS[GUIDE_LOOP_TIMEOUT] = {
|
|
12248
|
+
name: GUIDE_LOOP_TIMEOUT,
|
|
12249
|
+
type: 'counter',
|
|
12250
|
+
instrument: counter
|
|
12251
|
+
};
|
|
12252
|
+
_.each(BEACON_GIF_FAILURES, (name) => {
|
|
12253
|
+
METRICS[name] = {
|
|
12254
|
+
name,
|
|
12255
|
+
type: 'counter',
|
|
12256
|
+
instrument: counter
|
|
12257
|
+
};
|
|
12258
|
+
});
|
|
12259
|
+
METRICS[GUIDE_LOOP_TIMER] = {
|
|
12260
|
+
name: GUIDE_LOOP_TIMER
|
|
12261
|
+
};
|
|
12262
|
+
METRICS[EVENT_CAPTURED_TIMER] = {
|
|
12263
|
+
name: EVENT_CAPTURED_TIMER
|
|
12264
|
+
};
|
|
12265
|
+
METRICS[INITIALIZE] = {
|
|
12266
|
+
name: INITIALIZE
|
|
12267
|
+
};
|
|
12268
|
+
METRICS[TEARDOWN] = {
|
|
12269
|
+
name: TEARDOWN
|
|
12246
12270
|
};
|
|
12247
12271
|
|
|
12248
|
-
const
|
|
12272
|
+
const PERFORMANCE_SEND_INTERVAL = 1000 * 60 * 10; // 10 minutes
|
|
12249
12273
|
class PerformanceMonitor {
|
|
12250
12274
|
constructor() {
|
|
12251
12275
|
this._isPerformanceApiAvailable = this._checkPerformanceApi();
|
|
@@ -12255,42 +12279,55 @@ class PerformanceMonitor {
|
|
|
12255
12279
|
}
|
|
12256
12280
|
initialize() {
|
|
12257
12281
|
this._measuringPerformance = true;
|
|
12258
|
-
this._markPerformance(
|
|
12282
|
+
this._markPerformance(INITIALIZE);
|
|
12259
12283
|
this.interval = setInterval(() => {
|
|
12260
12284
|
this.send();
|
|
12261
|
-
},
|
|
12285
|
+
}, PERFORMANCE_SEND_INTERVAL);
|
|
12262
12286
|
return () => {
|
|
12263
12287
|
this.teardown();
|
|
12264
12288
|
};
|
|
12265
12289
|
}
|
|
12266
12290
|
teardown() {
|
|
12291
|
+
this._markPerformance(TEARDOWN);
|
|
12292
|
+
this.send();
|
|
12267
12293
|
this._measuringPerformance = false;
|
|
12268
12294
|
this._clearMarksAndMeasures();
|
|
12269
12295
|
clearInterval(this.interval);
|
|
12270
12296
|
}
|
|
12271
|
-
startTimer(name) {
|
|
12272
|
-
this._markPerformance(`${name}-start
|
|
12297
|
+
startTimer(name, detail = null) {
|
|
12298
|
+
this._markPerformance(`${name}-start`, { detail });
|
|
12273
12299
|
}
|
|
12274
|
-
stopTimer(name) {
|
|
12275
|
-
this._markPerformance(`${name}-stop
|
|
12276
|
-
this._measurePerformance(name);
|
|
12300
|
+
stopTimer(name, detail = null) {
|
|
12301
|
+
this._markPerformance(`${name}-stop`, { detail });
|
|
12302
|
+
this._measurePerformance(name, { detail });
|
|
12277
12303
|
}
|
|
12278
|
-
count(name,
|
|
12279
|
-
|
|
12280
|
-
|
|
12281
|
-
});
|
|
12304
|
+
count(name, increment = 1) {
|
|
12305
|
+
const detail = increment > 1 ? { type: 'counter', increment } : null;
|
|
12306
|
+
this._markPerformance(name, { detail });
|
|
12282
12307
|
}
|
|
12283
12308
|
send() {
|
|
12284
|
-
const metrics = _.reduce(
|
|
12309
|
+
const metrics = _.reduce(METRICS, (acc, metric) => {
|
|
12310
|
+
const { name, type, instrument } = metric;
|
|
12311
|
+
if (!instrument)
|
|
12312
|
+
return acc;
|
|
12285
12313
|
const entries = performance.getEntriesByName(name);
|
|
12286
|
-
const value =
|
|
12287
|
-
|
|
12314
|
+
const value = instrument(entries);
|
|
12315
|
+
if (type === 'counter' && value > 0) {
|
|
12316
|
+
acc.push({
|
|
12317
|
+
name,
|
|
12318
|
+
type,
|
|
12319
|
+
value,
|
|
12320
|
+
timestamp: Date.now()
|
|
12321
|
+
});
|
|
12322
|
+
}
|
|
12288
12323
|
return acc;
|
|
12289
12324
|
}, []);
|
|
12290
12325
|
const payload = _.filter(metrics, (metric) => {
|
|
12291
12326
|
return metric.value > 0;
|
|
12292
12327
|
});
|
|
12293
12328
|
this._clearMarksAndMeasures();
|
|
12329
|
+
if (!ConfigReader.get('sendPerformanceMetrics'))
|
|
12330
|
+
return;
|
|
12294
12331
|
if (_.size(payload)) {
|
|
12295
12332
|
writeMetricsPOST(payload);
|
|
12296
12333
|
}
|
|
@@ -12307,39 +12344,34 @@ class PerformanceMonitor {
|
|
|
12307
12344
|
_shouldMeasurePerformance() {
|
|
12308
12345
|
return this._isPerformanceApiAvailable && this._measuringPerformance;
|
|
12309
12346
|
}
|
|
12310
|
-
_markPerformance(name,
|
|
12347
|
+
_markPerformance(name, metadata) {
|
|
12311
12348
|
if (!this._shouldMeasurePerformance())
|
|
12312
12349
|
return;
|
|
12313
|
-
name
|
|
12314
|
-
performance.mark(name);
|
|
12350
|
+
performance.mark(name, metadata);
|
|
12315
12351
|
this.marks.add(name);
|
|
12316
12352
|
}
|
|
12317
|
-
_measurePerformance(name) {
|
|
12353
|
+
_measurePerformance(name, metadata) {
|
|
12318
12354
|
if (!this._shouldMeasurePerformance())
|
|
12319
12355
|
return;
|
|
12320
|
-
name = `pendo-${name}`;
|
|
12321
12356
|
const startMark = `${name}-start`;
|
|
12322
12357
|
const stopMark = `${name}-stop`;
|
|
12323
12358
|
if (performance.getEntriesByName(startMark).length && performance.getEntriesByName(stopMark).length) {
|
|
12324
|
-
performance.measure(name, startMark, stopMark);
|
|
12359
|
+
performance.measure(name, startMark, stopMark, metadata);
|
|
12325
12360
|
this.measures.add(name);
|
|
12326
12361
|
}
|
|
12327
12362
|
}
|
|
12328
12363
|
_clearMarksAndMeasures() {
|
|
12329
|
-
|
|
12330
|
-
// eslint-disable-next-line agent-eslint-rules/no-array-foreach
|
|
12331
|
-
this.marks.forEach(name => {
|
|
12364
|
+
for (const name of this.marks) {
|
|
12332
12365
|
performance.clearMarks(name);
|
|
12333
|
-
}
|
|
12366
|
+
}
|
|
12334
12367
|
this.marks.clear();
|
|
12335
|
-
|
|
12336
|
-
this.measures.forEach(name => {
|
|
12368
|
+
for (const name of this.measures) {
|
|
12337
12369
|
performance.clearMeasures(name);
|
|
12338
|
-
}
|
|
12370
|
+
}
|
|
12339
12371
|
this.measures.clear();
|
|
12340
12372
|
}
|
|
12341
12373
|
}
|
|
12342
|
-
const
|
|
12374
|
+
const performanceMonitor = new PerformanceMonitor();
|
|
12343
12375
|
|
|
12344
12376
|
var defaultTrackName = '_PENDO_UNNAMED_';
|
|
12345
12377
|
var SILO_AVG_COMPRESSION_RATIO = 5;
|
|
@@ -12391,9 +12423,6 @@ function collectEvent(type, props, url, name, eventProperties, context) {
|
|
|
12391
12423
|
if (!isURLValid(event.url)) {
|
|
12392
12424
|
return;
|
|
12393
12425
|
}
|
|
12394
|
-
if (!eventIsWhitelisted(event)) {
|
|
12395
|
-
return;
|
|
12396
|
-
}
|
|
12397
12426
|
if (type === 'track') {
|
|
12398
12427
|
trackEventQueue.push(event);
|
|
12399
12428
|
return;
|
|
@@ -12404,19 +12433,6 @@ function collectEvent(type, props, url, name, eventProperties, context) {
|
|
|
12404
12433
|
}
|
|
12405
12434
|
eventQueue.push(event);
|
|
12406
12435
|
}
|
|
12407
|
-
// @const {Event.type[]}
|
|
12408
|
-
var WHITELIST_FREE_NPS = ['load', 'meta', 'identify'];
|
|
12409
|
-
/**
|
|
12410
|
-
* @access private
|
|
12411
|
-
* @param {Event} event to consider
|
|
12412
|
-
* @returns {boolean} whether {event} is allowed
|
|
12413
|
-
*/
|
|
12414
|
-
function eventIsWhitelisted(event) {
|
|
12415
|
-
if (ConfigReader.get('freeNPSData')) {
|
|
12416
|
-
return _.contains(WHITELIST_FREE_NPS, event.type);
|
|
12417
|
-
}
|
|
12418
|
-
return true;
|
|
12419
|
-
}
|
|
12420
12436
|
function pipeline() {
|
|
12421
12437
|
var args = _.toArray(arguments);
|
|
12422
12438
|
return function generatedPipeline(obj, next) {
|
|
@@ -12841,7 +12857,7 @@ function createSendQueue(options, send, guaranteedSend) {
|
|
|
12841
12857
|
}
|
|
12842
12858
|
});
|
|
12843
12859
|
queue.onTimeout = function () {
|
|
12844
|
-
|
|
12860
|
+
performanceMonitor.count(BEACON_GIF_FAILURES[options.beacon]);
|
|
12845
12861
|
};
|
|
12846
12862
|
queue.retryPending = true;
|
|
12847
12863
|
return queue;
|
|
@@ -13240,11 +13256,12 @@ function attributeSerializer(context, node) {
|
|
|
13240
13256
|
|
|
13241
13257
|
function childIndexSerializer(context, node) {
|
|
13242
13258
|
if (node.parentNode && node.parentNode.childNodes) {
|
|
13243
|
-
|
|
13259
|
+
const nodes = _.chain(node.parentNode.childNodes);
|
|
13244
13260
|
context.myIndex = nodes.indexOf(node).value();
|
|
13245
|
-
|
|
13261
|
+
const childNodes = nodes.filter(function (n) {
|
|
13246
13262
|
return n.nodeType == ELEMENT;
|
|
13247
|
-
})
|
|
13263
|
+
});
|
|
13264
|
+
context.childIndex = childNodes.indexOf(node).value();
|
|
13248
13265
|
}
|
|
13249
13266
|
return context;
|
|
13250
13267
|
}
|
|
@@ -13373,7 +13390,7 @@ var getValidTarget = function (node) {
|
|
|
13373
13390
|
*/
|
|
13374
13391
|
var handle_event = function (evt) {
|
|
13375
13392
|
try {
|
|
13376
|
-
|
|
13393
|
+
performanceMonitor.startTimer(EVENT_CAPTURED_TIMER);
|
|
13377
13394
|
if (dom.data.get(evt, 'counted'))
|
|
13378
13395
|
return;
|
|
13379
13396
|
dom.data.set(evt, 'counted', true);
|
|
@@ -13414,7 +13431,7 @@ var handle_event = function (evt) {
|
|
|
13414
13431
|
log.critical('pendo.io while handling event', { error: e });
|
|
13415
13432
|
}
|
|
13416
13433
|
finally {
|
|
13417
|
-
|
|
13434
|
+
performanceMonitor.stopTimer(EVENT_CAPTURED_TIMER);
|
|
13418
13435
|
}
|
|
13419
13436
|
};
|
|
13420
13437
|
function getClickEventProperties(target) {
|
|
@@ -19408,6 +19425,9 @@ var BuildingBlockResourceCenter = (function () {
|
|
|
19408
19425
|
webWidgetWrapper(provider, 'messenger:on', 'unreadMessages', function (unreadCount) {
|
|
19409
19426
|
updateNotificationBubbleCount(unreadCount, 'chat');
|
|
19410
19427
|
});
|
|
19428
|
+
if (resourceCenter.isInProgress() && !resourceCenter.attributes.doNotResume) {
|
|
19429
|
+
handleNativeIntegrationContinuation(resourceCenter.activeModule);
|
|
19430
|
+
}
|
|
19411
19431
|
}
|
|
19412
19432
|
// Zendesk Native Chat API - https://developer.zendesk.com/embeddables/docs/widget/chat_api_migration
|
|
19413
19433
|
function configureZendeskChatSettings(integrationObj) {
|
|
@@ -20399,17 +20419,18 @@ var GuideStateModule = (function () {
|
|
|
20399
20419
|
updateLastGuideStepSeen(context, lastGuideStepSeen) {
|
|
20400
20420
|
if (lastGuideStepSeen.visitorId && lastGuideStepSeen.visitorId !== context.getters.visitorId())
|
|
20401
20421
|
return;
|
|
20402
|
-
|
|
20422
|
+
// Embedded guides should not update the lastGuideStepSeen in order to not interfere with auto guide display
|
|
20423
|
+
// Embedded guides are not included in getters.guideList
|
|
20424
|
+
const shouldUpdateLastGuideStepSeen = _.some(context.getters.guideList(), function ({ id }) {
|
|
20403
20425
|
return id === lastGuideStepSeen.guideId;
|
|
20404
20426
|
});
|
|
20405
20427
|
if (lastGuideStepSeen.guideStepId) {
|
|
20406
20428
|
context.commit('setStepState', lastGuideStepSeen);
|
|
20407
20429
|
}
|
|
20408
|
-
if (
|
|
20409
|
-
|
|
20430
|
+
if (shouldUpdateLastGuideStepSeen) {
|
|
20431
|
+
context.commit('setLastGuideStepSeen', lastGuideStepSeen);
|
|
20432
|
+
pendo$1.lastGuideStepSeen = lastGuideStepSeen;
|
|
20410
20433
|
}
|
|
20411
|
-
context.commit('setLastGuideStepSeen', lastGuideStepSeen);
|
|
20412
|
-
pendo$1.lastGuideStepSeen = lastGuideStepSeen;
|
|
20413
20434
|
if (guideCache) {
|
|
20414
20435
|
guideCache.update(lastGuideStepSeen);
|
|
20415
20436
|
}
|
|
@@ -21385,7 +21406,7 @@ class UpdateRunner {
|
|
|
21385
21406
|
monitor.start();
|
|
21386
21407
|
promise = currentPhase.process(monitor);
|
|
21387
21408
|
if (!promise && monitor.isTimeExceeded()) {
|
|
21388
|
-
|
|
21409
|
+
performanceMonitor.count(GUIDE_LOOP_TIMEOUT);
|
|
21389
21410
|
if (currentPhase.handleProcessTimeExceeded) {
|
|
21390
21411
|
currentPhase.handleProcessTimeExceeded(monitor);
|
|
21391
21412
|
}
|
|
@@ -26553,10 +26574,34 @@ const PENDO_HEADERS_KEY = '_pendoHeaders';
|
|
|
26553
26574
|
function getTransformedUrl(url) {
|
|
26554
26575
|
return store.getters['networkUrl/getTransformedUrl']()(url);
|
|
26555
26576
|
}
|
|
26577
|
+
const HEADERS = 'headers';
|
|
26578
|
+
const DENIED_HEADERS = {
|
|
26579
|
+
'authorization': true,
|
|
26580
|
+
'cookie': true,
|
|
26581
|
+
'set-cookie': true,
|
|
26582
|
+
'x-api-key': true,
|
|
26583
|
+
'x-forwarded-for': true,
|
|
26584
|
+
'proxy-authorization': true,
|
|
26585
|
+
'signature': true,
|
|
26586
|
+
'forwarded': true,
|
|
26587
|
+
'referrer': true
|
|
26588
|
+
};
|
|
26589
|
+
const XSRF = 'xsrf';
|
|
26590
|
+
const CSRF = 'csrf';
|
|
26591
|
+
const MAX_RESPONSE_SIZE = 1024 * 1024 * 2; // 2MB
|
|
26592
|
+
const MAX_RESPONSE_SIZE_MESSAGE = '[Body too large to capture (over 2 MB)]';
|
|
26556
26593
|
class NetworkRequestIntercept {
|
|
26557
|
-
|
|
26594
|
+
// Headers that should never be captured by pendo for PII/security reasons.
|
|
26595
|
+
isDeniedHeader(key) {
|
|
26596
|
+
const normalizedKey = key.toLowerCase();
|
|
26597
|
+
return DENIED_HEADERS[normalizedKey] || normalizedKey.indexOf(CSRF) !== -1 || normalizedKey.indexOf(XSRF) !== -1;
|
|
26598
|
+
}
|
|
26599
|
+
entriesToObject(entries, type) {
|
|
26558
26600
|
const obj = {};
|
|
26559
26601
|
for (const [key, value] of entries) {
|
|
26602
|
+
if (type === HEADERS && this.isDeniedHeader(key)) {
|
|
26603
|
+
continue;
|
|
26604
|
+
}
|
|
26560
26605
|
obj[key] = value;
|
|
26561
26606
|
}
|
|
26562
26607
|
return obj;
|
|
@@ -26580,11 +26625,14 @@ class NetworkRequestIntercept {
|
|
|
26580
26625
|
extractHeaders(headers = {}) {
|
|
26581
26626
|
let result = {};
|
|
26582
26627
|
if (headers instanceof Headers) {
|
|
26583
|
-
result = this.entriesToObject(headers.entries());
|
|
26628
|
+
result = this.entriesToObject(headers.entries(), HEADERS);
|
|
26584
26629
|
}
|
|
26585
26630
|
else if (typeof headers === 'object') {
|
|
26586
26631
|
for (const key in headers) {
|
|
26587
26632
|
if (headers.hasOwnProperty(key)) {
|
|
26633
|
+
if (this.isDeniedHeader(key)) {
|
|
26634
|
+
continue;
|
|
26635
|
+
}
|
|
26588
26636
|
result[key] = headers[key];
|
|
26589
26637
|
}
|
|
26590
26638
|
}
|
|
@@ -26601,17 +26649,25 @@ class NetworkRequestIntercept {
|
|
|
26601
26649
|
safelyReadBody(config, type) {
|
|
26602
26650
|
return __awaiter(this, void 0, void 0, function* () {
|
|
26603
26651
|
try {
|
|
26604
|
-
const contentType = config.headers.get('content-type') || '';
|
|
26605
|
-
|
|
26606
|
-
|
|
26607
|
-
|
|
26608
|
-
|
|
26609
|
-
|
|
26652
|
+
const contentType = config.headers.get('content-type') || 'unknown';
|
|
26653
|
+
const contentLengthHeader = config.headers.get('content-length');
|
|
26654
|
+
// Check if content type is readable (text or JSON)
|
|
26655
|
+
const isReadableContent = contentType.indexOf('text/') !== -1 || contentType.indexOf('application/json') !== -1;
|
|
26656
|
+
if (!isReadableContent) {
|
|
26657
|
+
const typeDisplay = contentType;
|
|
26658
|
+
return `[Binary content: ${typeDisplay}]`;
|
|
26659
|
+
}
|
|
26660
|
+
if (contentLengthHeader) {
|
|
26661
|
+
const size = parseInt(contentLengthHeader, 10);
|
|
26662
|
+
if (size > MAX_RESPONSE_SIZE) {
|
|
26663
|
+
return MAX_RESPONSE_SIZE_MESSAGE;
|
|
26664
|
+
}
|
|
26610
26665
|
}
|
|
26611
|
-
|
|
26612
|
-
|
|
26613
|
-
return
|
|
26666
|
+
const text = yield config.text();
|
|
26667
|
+
if (text.length > MAX_RESPONSE_SIZE) {
|
|
26668
|
+
return MAX_RESPONSE_SIZE_MESSAGE;
|
|
26614
26669
|
}
|
|
26670
|
+
return text;
|
|
26615
26671
|
}
|
|
26616
26672
|
catch (e) {
|
|
26617
26673
|
return `[Unable to read ${type} body]`;
|
|
@@ -26622,7 +26678,7 @@ class NetworkRequestIntercept {
|
|
|
26622
26678
|
let headers = {};
|
|
26623
26679
|
if (config.headers) {
|
|
26624
26680
|
const headerEntries = Array.from(config.headers.entries());
|
|
26625
|
-
headers = this.entriesToObject(headerEntries);
|
|
26681
|
+
headers = this.entriesToObject(headerEntries, HEADERS);
|
|
26626
26682
|
}
|
|
26627
26683
|
return headers;
|
|
26628
26684
|
}
|
|
@@ -26793,7 +26849,8 @@ class NetworkRequestIntercept {
|
|
|
26793
26849
|
if (xhr.readyState === 4) { // Request completed
|
|
26794
26850
|
try {
|
|
26795
26851
|
const headers = networkInterceptor.parseXHRResponseHeaders(xhr.getAllResponseHeaders());
|
|
26796
|
-
const { status, statusText,
|
|
26852
|
+
const { status, statusText, _url } = xhr;
|
|
26853
|
+
const body = networkInterceptor.safelyReadXHRResponse(xhr, headers);
|
|
26797
26854
|
_.each(networkInterceptor.callbackFns, ({ response }) => {
|
|
26798
26855
|
if (_.isFunction(response)) {
|
|
26799
26856
|
response({
|
|
@@ -26822,6 +26879,21 @@ class NetworkRequestIntercept {
|
|
|
26822
26879
|
return networkInterceptor._originalXHRSend.apply(this, arguments);
|
|
26823
26880
|
};
|
|
26824
26881
|
}
|
|
26882
|
+
safelyReadXHRResponse(xhr, headers) {
|
|
26883
|
+
const networkInterceptor = this;
|
|
26884
|
+
const size = networkInterceptor.estimateResponseSize(headers['content-length'], xhr.responseText);
|
|
26885
|
+
if (size > MAX_RESPONSE_SIZE) {
|
|
26886
|
+
return MAX_RESPONSE_SIZE_MESSAGE;
|
|
26887
|
+
}
|
|
26888
|
+
return xhr.responseText;
|
|
26889
|
+
}
|
|
26890
|
+
estimateResponseSize(contentLength, responseText) {
|
|
26891
|
+
const parsedContentLength = parseInt(contentLength, 10);
|
|
26892
|
+
if (!_.isNumber(parsedContentLength) || isNaN(parsedContentLength)) {
|
|
26893
|
+
return responseText.length;
|
|
26894
|
+
}
|
|
26895
|
+
return parsedContentLength;
|
|
26896
|
+
}
|
|
26825
26897
|
patchNetwork() {
|
|
26826
26898
|
const networkInterceptor = this;
|
|
26827
26899
|
if (networkInterceptor._networkPatched)
|
|
@@ -28357,7 +28429,7 @@ const initialize = makeSafe(function (options) {
|
|
|
28357
28429
|
Events.appUsage.on(GuideActivity.handler);
|
|
28358
28430
|
Events.appUsage.on(ResourceCenterActivity.handler);
|
|
28359
28431
|
teardownFns.push(flushEvery(SEND_INTERVAL));
|
|
28360
|
-
teardownFns.push(
|
|
28432
|
+
teardownFns.push(performanceMonitor.initialize());
|
|
28361
28433
|
}
|
|
28362
28434
|
Events.appHidden.on(() => {
|
|
28363
28435
|
flushNow(true, { hidden: true });
|
|
@@ -29398,8 +29470,10 @@ function track(name, props) {
|
|
|
29398
29470
|
collectEventHelper({ type: 'track', name, props });
|
|
29399
29471
|
}
|
|
29400
29472
|
|
|
29473
|
+
const privacyFilterCache = new Map();
|
|
29401
29474
|
/**
|
|
29402
29475
|
* Method to manually track agentic events. Uses agent functions for IDs and context.
|
|
29476
|
+
* Automatically applies privacy filters if agentId is provided and agent configuration exists.
|
|
29403
29477
|
*
|
|
29404
29478
|
* @access public
|
|
29405
29479
|
* @category Events
|
|
@@ -29418,8 +29492,64 @@ function track(name, props) {
|
|
|
29418
29492
|
* })
|
|
29419
29493
|
*/
|
|
29420
29494
|
function trackAgent(type, props) {
|
|
29495
|
+
const filteredTypes = ['prompt', 'system_response'];
|
|
29496
|
+
if (_.contains(filteredTypes, type)) {
|
|
29497
|
+
props = applyPrivacyFilters(props);
|
|
29498
|
+
}
|
|
29421
29499
|
collectEventHelper({ type, name: 'agentic', props });
|
|
29422
29500
|
}
|
|
29501
|
+
/**
|
|
29502
|
+
* Apply privacy filters to event properties based on agent configuration
|
|
29503
|
+
* Optimized for maximum performance with caching
|
|
29504
|
+
* @param {Object} props - Event properties to filter
|
|
29505
|
+
* @returns {Object} - Filtered properties
|
|
29506
|
+
*/
|
|
29507
|
+
function applyPrivacyFilters(props) {
|
|
29508
|
+
if (!(props === null || props === void 0 ? void 0 : props.agentId)) {
|
|
29509
|
+
return props;
|
|
29510
|
+
}
|
|
29511
|
+
try {
|
|
29512
|
+
const agents = ConfigReader.get('aiAgents', []);
|
|
29513
|
+
const agent = (agents === null || agents === void 0 ? void 0 : agents.find(agent => agent.id === props.agentId)) || null;
|
|
29514
|
+
const content = props.content;
|
|
29515
|
+
if (!content) {
|
|
29516
|
+
return props;
|
|
29517
|
+
}
|
|
29518
|
+
if (!(agent === null || agent === void 0 ? void 0 : agent.privacyFilters)) {
|
|
29519
|
+
return props;
|
|
29520
|
+
}
|
|
29521
|
+
const privacyFilter = getCachedRegex(agent.privacyFilters);
|
|
29522
|
+
if (!privacyFilter) {
|
|
29523
|
+
return props;
|
|
29524
|
+
}
|
|
29525
|
+
const filteredContent = content.replace(privacyFilter, '<redacted>');
|
|
29526
|
+
if (filteredContent === content) {
|
|
29527
|
+
return props;
|
|
29528
|
+
}
|
|
29529
|
+
return Object.assign(Object.assign({}, props), { content: filteredContent });
|
|
29530
|
+
}
|
|
29531
|
+
catch (error) {
|
|
29532
|
+
console.error('Error applying privacy filters', error);
|
|
29533
|
+
return props;
|
|
29534
|
+
}
|
|
29535
|
+
}
|
|
29536
|
+
/**
|
|
29537
|
+
* Get cached regex or compile and cache new one
|
|
29538
|
+
*/
|
|
29539
|
+
function getCachedRegex(filterPattern) {
|
|
29540
|
+
let regex = privacyFilterCache.get(filterPattern);
|
|
29541
|
+
if (!regex) {
|
|
29542
|
+
try {
|
|
29543
|
+
regex = new RegExp(filterPattern, 'gmi');
|
|
29544
|
+
privacyFilterCache.set(filterPattern, regex);
|
|
29545
|
+
}
|
|
29546
|
+
catch (e) {
|
|
29547
|
+
privacyFilterCache.set(filterPattern, null);
|
|
29548
|
+
return null;
|
|
29549
|
+
}
|
|
29550
|
+
}
|
|
29551
|
+
return regex;
|
|
29552
|
+
}
|
|
29423
29553
|
|
|
29424
29554
|
/**
|
|
29425
29555
|
* Checks visitor and account metadata in the current Pendo installation. Either logs to console
|
|
@@ -33411,12 +33541,12 @@ var GuideUpdateModule = (function () {
|
|
|
33411
33541
|
state.scheduledUpdate = scheduledUpdate;
|
|
33412
33542
|
},
|
|
33413
33543
|
startUpdate(state, time) {
|
|
33414
|
-
|
|
33544
|
+
performanceMonitor.startTimer(GUIDE_LOOP_TIMER);
|
|
33415
33545
|
state.needsUpdate = false;
|
|
33416
33546
|
state.updateId = time;
|
|
33417
33547
|
},
|
|
33418
33548
|
completeUpdate(state, time) {
|
|
33419
|
-
|
|
33549
|
+
performanceMonitor.stopTimer(GUIDE_LOOP_TIMER);
|
|
33420
33550
|
state.updateId = null;
|
|
33421
33551
|
state.updateCompleteTime = time;
|
|
33422
33552
|
}
|
|
@@ -38201,7 +38331,7 @@ const EmbeddedGuides = (function () {
|
|
|
38201
38331
|
return { guide, step };
|
|
38202
38332
|
}
|
|
38203
38333
|
function initializeEmbeddedGuides() {
|
|
38204
|
-
|
|
38334
|
+
restoreFromPreviousGuides();
|
|
38205
38335
|
pluginApi.guides.registerDisplayableGuides('embeddedGuides', embeddedGuides);
|
|
38206
38336
|
applyEmbeddedGuideBehaviors();
|
|
38207
38337
|
}
|
|
@@ -38216,7 +38346,7 @@ const EmbeddedGuides = (function () {
|
|
|
38216
38346
|
// When a guides payload is received, the embedded guides list is cleared and cached in _oldEmbeddedGuides.
|
|
38217
38347
|
// This function replaces guides in the new payload with guides from the previous payload to prevent re-rendering.
|
|
38218
38348
|
// Additionally, if guides from the previous payload are showing, and they are not in the new payload, this function hides them.
|
|
38219
|
-
function
|
|
38349
|
+
function restoreFromPreviousGuides() {
|
|
38220
38350
|
_.each(_oldEmbeddedGuides, function (oldGuide) {
|
|
38221
38351
|
const newGuideIndex = _.findIndex(embeddedGuides, function (guide) {
|
|
38222
38352
|
return guide.id === oldGuide.id;
|
|
@@ -54690,9 +54820,7 @@ const ErrorStackParser = {
|
|
|
54690
54820
|
// if a location was matched, pass it to extractLocation() otherwise pop the last token
|
|
54691
54821
|
const locationParts = this.extractLocation(location ? location[1] : tokens.pop() || '');
|
|
54692
54822
|
const functionName = tokens.join(' ') || undefined;
|
|
54693
|
-
const fileName = ['eval'
|
|
54694
|
-
? undefined
|
|
54695
|
-
: locationParts[0];
|
|
54823
|
+
const fileName = (locationParts[0] === 'eval' || locationParts[0] === '<anonymous>') ? undefined : locationParts[0];
|
|
54696
54824
|
frames.push(new StackFrame({
|
|
54697
54825
|
functionName,
|
|
54698
54826
|
fileName,
|
|
@@ -54734,17 +54862,29 @@ const PII_PATTERN = {
|
|
|
54734
54862
|
ip: /\b(?:\d{1,3}\.){3}\d{1,3}\b/g,
|
|
54735
54863
|
ssn: /\b\d{3}-\d{2}-\d{4}\b/g,
|
|
54736
54864
|
creditCard: /\b(?:\d[ -]*?){13,16}\b/g,
|
|
54737
|
-
httpsUrls: /https:\/\/[^\s]+/g
|
|
54865
|
+
httpsUrls: /https:\/\/[^\s]+/g,
|
|
54866
|
+
email: /\b\S+@[\w-]+\.[\w-]+\b/g
|
|
54738
54867
|
};
|
|
54739
54868
|
const PII_REPLACEMENT = '*'.repeat(10);
|
|
54869
|
+
const JSON_PII_KEYS = ['password', 'email', 'key', 'token', 'auth', 'authentication', 'phone', 'address', 'ssn'];
|
|
54870
|
+
const UNABLE_TO_DISPLAY_BODY = '[Unable to display body: detected PII could not be redacted]';
|
|
54871
|
+
const joinedKeys = JSON_PII_KEYS.join('|');
|
|
54872
|
+
const keyPattern = `"([^"]*(?:${joinedKeys})[^"]*)"`;
|
|
54873
|
+
// only scrub strings, numbers, and booleans
|
|
54874
|
+
const acceptedValues = '("([^"\\\\]*(?:\\\\.[^"\\\\]*)*")|(\\d+)|(true|false))';
|
|
54740
54875
|
/**
|
|
54741
54876
|
* Truncates a string to the max length
|
|
54742
54877
|
* @access private
|
|
54743
54878
|
* @param {String} string
|
|
54744
54879
|
* @returns {String}
|
|
54745
54880
|
*/
|
|
54746
|
-
function truncate(string) {
|
|
54747
|
-
|
|
54881
|
+
function truncate(string, includeEllipsis = false) {
|
|
54882
|
+
if (string.length <= MAX_LENGTH)
|
|
54883
|
+
return string;
|
|
54884
|
+
if (includeEllipsis) {
|
|
54885
|
+
return string.slice(0, MAX_LENGTH).concat('...');
|
|
54886
|
+
}
|
|
54887
|
+
return string.slice(0, MAX_LENGTH);
|
|
54748
54888
|
}
|
|
54749
54889
|
/**
|
|
54750
54890
|
* Generate a log key for a console log separated by pipes
|
|
@@ -54758,13 +54898,13 @@ function generateLogKey(methodName, message, stackTrace) {
|
|
|
54758
54898
|
return `${methodName}|${message}|${stackTrace}`;
|
|
54759
54899
|
}
|
|
54760
54900
|
/**
|
|
54761
|
-
* Checks if a string has 2 or more digits
|
|
54901
|
+
* Checks if a string has 2 or more digits, a https URL, or an email address
|
|
54762
54902
|
* @access private
|
|
54763
54903
|
* @param {String} string
|
|
54764
54904
|
* @returns {Boolean}
|
|
54765
54905
|
*/
|
|
54766
54906
|
function mightContainPII(string) {
|
|
54767
|
-
return /[\d]{2,}|https
|
|
54907
|
+
return /[\d]{2,}|https:\/\/|@/.test(string);
|
|
54768
54908
|
}
|
|
54769
54909
|
/**
|
|
54770
54910
|
* Scrub basic PII from a string and replace it with a placeholder
|
|
@@ -54772,12 +54912,60 @@ function mightContainPII(string) {
|
|
|
54772
54912
|
* @param {String} string
|
|
54773
54913
|
* @returns {String}
|
|
54774
54914
|
*/
|
|
54775
|
-
function scrubPII(string) {
|
|
54915
|
+
function scrubPII({ string, _ }) {
|
|
54776
54916
|
if (!string || typeof string !== 'string')
|
|
54777
54917
|
return string;
|
|
54778
54918
|
if (!mightContainPII(string))
|
|
54779
54919
|
return string;
|
|
54780
|
-
return
|
|
54920
|
+
return _.reduce(_.values(PII_PATTERN), (str, pattern) => str.replace(pattern, PII_REPLACEMENT), string);
|
|
54921
|
+
}
|
|
54922
|
+
/**
|
|
54923
|
+
* Scrub PII from a stringified JSON object
|
|
54924
|
+
* @access private
|
|
54925
|
+
* @param {String} string
|
|
54926
|
+
* @returns {String}
|
|
54927
|
+
*/
|
|
54928
|
+
function scrubJsonPII(string) {
|
|
54929
|
+
const fullScrubRegex = new RegExp(`${keyPattern}\\s*:\\s*[\\{\\[]`, 'i');
|
|
54930
|
+
if (fullScrubRegex.test(string)) {
|
|
54931
|
+
return UNABLE_TO_DISPLAY_BODY;
|
|
54932
|
+
}
|
|
54933
|
+
const keyValueScrubRegex = new RegExp(`${keyPattern}\\s*:\\s*${acceptedValues}`, 'gi');
|
|
54934
|
+
return string.replace(keyValueScrubRegex, (match, key) => `"${key}":"${PII_REPLACEMENT}"`);
|
|
54935
|
+
}
|
|
54936
|
+
/**
|
|
54937
|
+
* Checks if a string is a JSON object
|
|
54938
|
+
* @access private
|
|
54939
|
+
* @param {String} string
|
|
54940
|
+
* @param {String} contentType
|
|
54941
|
+
* @returns {Boolean}
|
|
54942
|
+
*/
|
|
54943
|
+
function mightContainJson(string, contentType) {
|
|
54944
|
+
if (!string || typeof string !== 'string')
|
|
54945
|
+
return false;
|
|
54946
|
+
const firstChar = string.charAt(0);
|
|
54947
|
+
if (contentType && contentType.indexOf('application/json') !== -1) {
|
|
54948
|
+
return true;
|
|
54949
|
+
}
|
|
54950
|
+
return firstChar === '{' || firstChar === '[';
|
|
54951
|
+
}
|
|
54952
|
+
/**
|
|
54953
|
+
* Mask sensitive fields in a string
|
|
54954
|
+
* @access private
|
|
54955
|
+
* @param {String} string
|
|
54956
|
+
* @param {String} contentType
|
|
54957
|
+
* @returns {String}
|
|
54958
|
+
*/
|
|
54959
|
+
function maskSensitiveFields({ string, contentType, _ }) {
|
|
54960
|
+
if (!string || typeof string !== 'string')
|
|
54961
|
+
return string;
|
|
54962
|
+
if (mightContainJson(string, contentType)) {
|
|
54963
|
+
string = scrubJsonPII(string);
|
|
54964
|
+
}
|
|
54965
|
+
if (mightContainPII(string)) {
|
|
54966
|
+
string = scrubPII({ string, _ });
|
|
54967
|
+
}
|
|
54968
|
+
return string;
|
|
54781
54969
|
}
|
|
54782
54970
|
|
|
54783
54971
|
/**
|
|
@@ -54952,11 +55140,10 @@ function createCspViolationMessage(blockedURI, directive, isReportOnly, original
|
|
|
54952
55140
|
}
|
|
54953
55141
|
}
|
|
54954
55142
|
|
|
54955
|
-
const DEV_LOG_LEVELS = ['info', 'warn', 'error'];
|
|
54956
55143
|
const TOKEN_MAX_SIZE = 100;
|
|
54957
55144
|
const TOKEN_REFILL_RATE = 10;
|
|
54958
55145
|
const TOKEN_REFRESH_INTERVAL = 1000;
|
|
54959
|
-
class
|
|
55146
|
+
class DevlogBuffer {
|
|
54960
55147
|
constructor(pendo, pluginAPI) {
|
|
54961
55148
|
this.pendo = pendo;
|
|
54962
55149
|
this.pluginAPI = pluginAPI;
|
|
@@ -54985,9 +55172,6 @@ class ConsoleCaptureBuffer {
|
|
|
54985
55172
|
this.lastEvent = null;
|
|
54986
55173
|
}
|
|
54987
55174
|
push(event) {
|
|
54988
|
-
const { devLogLevel } = event;
|
|
54989
|
-
if (!this.pendo._.contains(DEV_LOG_LEVELS, devLogLevel))
|
|
54990
|
-
return false;
|
|
54991
55175
|
this.refillTokens();
|
|
54992
55176
|
if (this.tokens === 0)
|
|
54993
55177
|
return false;
|
|
@@ -55804,7 +55988,6 @@ var ConfigReader = (function () {
|
|
|
55804
55988
|
* @type {boolean}
|
|
55805
55989
|
*/
|
|
55806
55990
|
addOption('enableDebugEvents', [SNIPPET_SRC, PENDO_CONFIG_SRC]);
|
|
55807
|
-
addOption('freeNPSData');
|
|
55808
55991
|
addOption('eventPropertyConfigurations');
|
|
55809
55992
|
/**
|
|
55810
55993
|
* By default, a click event property rule can be matched against the element that is clicked on or its
|
|
@@ -56192,7 +56375,9 @@ var ConfigReader = (function () {
|
|
|
56192
56375
|
addOption('enableAllEmbeddedGuideEvents', [PENDO_CONFIG_SRC], false);
|
|
56193
56376
|
// Form Validation
|
|
56194
56377
|
addOption('formValidation', [PENDO_CONFIG_SRC], false);
|
|
56378
|
+
// Performance Metrics
|
|
56195
56379
|
addOption('performanceMetricsEnabled', [SNIPPET_SRC, PENDO_CONFIG_SRC], true);
|
|
56380
|
+
addOption('sendPerformanceMetrics', [SNIPPET_SRC, PENDO_CONFIG_SRC], false);
|
|
56196
56381
|
}
|
|
56197
56382
|
initializeOptions();
|
|
56198
56383
|
var sourceGetters = {};
|
|
@@ -56981,7 +57166,7 @@ else {
|
|
|
56981
57166
|
var SEND_INTERVAL = 2 * 60 * 1000;
|
|
56982
57167
|
var ENCODED_EVENT_MAX_LENGTH = 1900;
|
|
56983
57168
|
|
|
56984
|
-
class
|
|
57169
|
+
class DevlogTransport {
|
|
56985
57170
|
constructor(globalPendo, pluginAPI) {
|
|
56986
57171
|
this.pendo = globalPendo;
|
|
56987
57172
|
this.pluginAPI = pluginAPI;
|
|
@@ -57025,7 +57210,7 @@ class ConsoleTransport {
|
|
|
57025
57210
|
}
|
|
57026
57211
|
else if (options.keepalive && this.pluginAPI.transmit.sendBeacon.supported()) {
|
|
57027
57212
|
const result = this.pluginAPI.transmit.sendBeacon(url, new Blob([options.body]));
|
|
57028
|
-
return result ? Promise$2.resolve() : Promise$2.reject(new Error('sendBeacon failed to send
|
|
57213
|
+
return result ? Promise$2.resolve() : Promise$2.reject(new Error('sendBeacon failed to send devlog data'));
|
|
57029
57214
|
}
|
|
57030
57215
|
else {
|
|
57031
57216
|
return this.pendo.ajax.post(url, options.body);
|
|
@@ -57060,6 +57245,17 @@ class ConsoleTransport {
|
|
|
57060
57245
|
}
|
|
57061
57246
|
}
|
|
57062
57247
|
|
|
57248
|
+
const DEV_LOG_TYPE = 'devlog';
|
|
57249
|
+
function createDevLogEnvelope(pluginAPI, globalPendo) {
|
|
57250
|
+
return {
|
|
57251
|
+
browser_time: pluginAPI.util.getNow(),
|
|
57252
|
+
url: globalPendo.url.get(),
|
|
57253
|
+
visitorId: globalPendo.get_visitor_id(),
|
|
57254
|
+
accountId: globalPendo.get_account_id(),
|
|
57255
|
+
type: DEV_LOG_TYPE
|
|
57256
|
+
};
|
|
57257
|
+
}
|
|
57258
|
+
|
|
57063
57259
|
function ConsoleCapture() {
|
|
57064
57260
|
let pluginAPI;
|
|
57065
57261
|
let _;
|
|
@@ -57071,7 +57267,6 @@ function ConsoleCapture() {
|
|
|
57071
57267
|
let isPtmPaused;
|
|
57072
57268
|
const CAPTURE_CONSOLE_CONFIG = 'captureConsoleLogs';
|
|
57073
57269
|
const CONSOLE_METHODS = ['log', 'warn', 'error', 'info'];
|
|
57074
|
-
const DEV_LOG_TYPE = 'devlog';
|
|
57075
57270
|
const DEV_LOG_SUB_TYPE = 'console';
|
|
57076
57271
|
// deduplicate logs
|
|
57077
57272
|
let lastLogKey = '';
|
|
@@ -57107,8 +57302,8 @@ function ConsoleCapture() {
|
|
|
57107
57302
|
if (!captureConsoleEnabled)
|
|
57108
57303
|
return;
|
|
57109
57304
|
globalPendo = pendo;
|
|
57110
|
-
buffer = new
|
|
57111
|
-
transport = new
|
|
57305
|
+
buffer = new DevlogBuffer(pendo, pluginAPI);
|
|
57306
|
+
transport = new DevlogTransport(pendo, pluginAPI);
|
|
57112
57307
|
sendQueue = new SendQueue(transport.sendRequest.bind(transport));
|
|
57113
57308
|
sendInterval = setInterval(() => {
|
|
57114
57309
|
if (!sendQueue.failed()) {
|
|
@@ -57141,16 +57336,6 @@ function ConsoleCapture() {
|
|
|
57141
57336
|
send();
|
|
57142
57337
|
}
|
|
57143
57338
|
}
|
|
57144
|
-
function createDevLogEnvelope() {
|
|
57145
|
-
return {
|
|
57146
|
-
visitorId: globalPendo.get_visitor_id(),
|
|
57147
|
-
accountId: globalPendo.get_account_id(),
|
|
57148
|
-
type: DEV_LOG_TYPE,
|
|
57149
|
-
subType: DEV_LOG_SUB_TYPE,
|
|
57150
|
-
browser_time: pluginAPI.util.getNow(),
|
|
57151
|
-
url: globalPendo.url.get()
|
|
57152
|
-
};
|
|
57153
|
-
}
|
|
57154
57339
|
function readyHandler() {
|
|
57155
57340
|
addIntercepts();
|
|
57156
57341
|
pluginAPI.attachEventInternal(window, 'securitypolicyviolation', securityPolicyViolationFn);
|
|
@@ -57186,7 +57371,7 @@ function ConsoleCapture() {
|
|
|
57186
57371
|
}
|
|
57187
57372
|
}
|
|
57188
57373
|
function createConsoleEvent(args, methodName, { skipStackTrace = false, skipScrubPII = false } = {}) {
|
|
57189
|
-
if (!args || args.length === 0)
|
|
57374
|
+
if (!args || args.length === 0 || !_.contains(CONSOLE_METHODS, methodName))
|
|
57190
57375
|
return;
|
|
57191
57376
|
// stringify args
|
|
57192
57377
|
let message = _.compact(_.map(args, arg => {
|
|
@@ -57216,8 +57401,8 @@ function ConsoleCapture() {
|
|
|
57216
57401
|
buffer.lastEvent.devLogCount++;
|
|
57217
57402
|
return;
|
|
57218
57403
|
}
|
|
57219
|
-
const devLogEnvelope = createDevLogEnvelope();
|
|
57220
|
-
const consoleEvent = Object.assign(Object.assign({}, devLogEnvelope), { devLogLevel: methodName === 'log' ? 'info' : methodName, devLogMessage: skipScrubPII ? message : scrubPII(message), devLogTrace: stackTrace, devLogCount: 1 });
|
|
57404
|
+
const devLogEnvelope = createDevLogEnvelope(pluginAPI, globalPendo);
|
|
57405
|
+
const consoleEvent = Object.assign(Object.assign({}, devLogEnvelope), { subType: DEV_LOG_SUB_TYPE, devLogLevel: methodName === 'log' ? 'info' : methodName, devLogMessage: skipScrubPII ? message : scrubPII({ string: message, _ }), devLogTrace: stackTrace, devLogCount: 1 });
|
|
57221
57406
|
const wasAccepted = buffer.push(consoleEvent);
|
|
57222
57407
|
if (wasAccepted) {
|
|
57223
57408
|
if (!isPtmPaused) {
|
|
@@ -57253,7 +57438,7 @@ function ConsoleCapture() {
|
|
|
57253
57438
|
v: globalPendo.VERSION,
|
|
57254
57439
|
ct: pluginAPI.util.getNow()
|
|
57255
57440
|
};
|
|
57256
|
-
const url = pluginAPI.transmit.buildBaseDataUrl(
|
|
57441
|
+
const url = pluginAPI.transmit.buildBaseDataUrl(DEV_LOG_TYPE, globalPendo.apiKey, queryParams);
|
|
57257
57442
|
const jzb = buffer.pack();
|
|
57258
57443
|
if (!jzb)
|
|
57259
57444
|
return;
|
|
@@ -57291,4 +57476,329 @@ function ConsoleCapture() {
|
|
|
57291
57476
|
}
|
|
57292
57477
|
}
|
|
57293
57478
|
|
|
57294
|
-
|
|
57479
|
+
function NetworkCapture() {
|
|
57480
|
+
let pluginAPI;
|
|
57481
|
+
let globalPendo;
|
|
57482
|
+
let requestMap = {};
|
|
57483
|
+
let buffer;
|
|
57484
|
+
let sendQueue;
|
|
57485
|
+
let sendInterval;
|
|
57486
|
+
let transport;
|
|
57487
|
+
let isPtmPaused;
|
|
57488
|
+
let requestBodyCb;
|
|
57489
|
+
let responseBodyCb;
|
|
57490
|
+
let pendoDevlogBaseUrl;
|
|
57491
|
+
const CAPTURE_NETWORK_CONFIG = 'captureNetworkRequests';
|
|
57492
|
+
const NETWORK_SUB_TYPE = 'network';
|
|
57493
|
+
const NETWORK_LOGS_CONFIG = 'networkLogs';
|
|
57494
|
+
const NETWORK_LOGS_CONFIG_ALLOWED_REQUEST_HEADERS = 'networkLogs.allowedRequestHeaders';
|
|
57495
|
+
const NETWORK_LOGS_CONFIG_ALLOWED_RESPONSE_HEADERS = 'networkLogs.allowedResponseHeaders';
|
|
57496
|
+
const NETWORK_LOGS_CONFIG_CAPTURE_REQUEST_BODY = 'networkLogs.captureRequestBody';
|
|
57497
|
+
const NETWORK_LOGS_CONFIG_CAPTURE_RESPONSE_BODY = 'networkLogs.captureResponseBody';
|
|
57498
|
+
const allowedRequestHeaders = {
|
|
57499
|
+
'content-type': true, 'content-length': true, 'accept': true, 'accept-language': true
|
|
57500
|
+
};
|
|
57501
|
+
const allowedResponseHeaders = {
|
|
57502
|
+
'cache-control': true, 'content-length': true, 'content-type': true, 'content-language': true
|
|
57503
|
+
};
|
|
57504
|
+
return {
|
|
57505
|
+
name: 'NetworkCapture',
|
|
57506
|
+
initialize,
|
|
57507
|
+
teardown,
|
|
57508
|
+
handleRequest,
|
|
57509
|
+
handleResponse,
|
|
57510
|
+
handleError,
|
|
57511
|
+
startCapture,
|
|
57512
|
+
createNetworkEvent,
|
|
57513
|
+
send,
|
|
57514
|
+
onPtmPaused,
|
|
57515
|
+
onPtmUnpaused,
|
|
57516
|
+
onAppHidden,
|
|
57517
|
+
onAppUnloaded,
|
|
57518
|
+
addConfigOptions,
|
|
57519
|
+
processHeaderConfig,
|
|
57520
|
+
extractHeaders,
|
|
57521
|
+
setupBodyCallbacks,
|
|
57522
|
+
processBody,
|
|
57523
|
+
processRequestBody,
|
|
57524
|
+
processResponseBody,
|
|
57525
|
+
get requestMap() {
|
|
57526
|
+
return requestMap;
|
|
57527
|
+
},
|
|
57528
|
+
get buffer() {
|
|
57529
|
+
return buffer;
|
|
57530
|
+
},
|
|
57531
|
+
get sendQueue() {
|
|
57532
|
+
return sendQueue;
|
|
57533
|
+
},
|
|
57534
|
+
get isPtmPaused() {
|
|
57535
|
+
return isPtmPaused;
|
|
57536
|
+
},
|
|
57537
|
+
get allowedRequestHeaders() {
|
|
57538
|
+
return allowedRequestHeaders;
|
|
57539
|
+
},
|
|
57540
|
+
get allowedResponseHeaders() {
|
|
57541
|
+
return allowedResponseHeaders;
|
|
57542
|
+
},
|
|
57543
|
+
get requestBodyCb() {
|
|
57544
|
+
return requestBodyCb;
|
|
57545
|
+
},
|
|
57546
|
+
get responseBodyCb() {
|
|
57547
|
+
return responseBodyCb;
|
|
57548
|
+
}
|
|
57549
|
+
};
|
|
57550
|
+
function initialize(pendo, PluginAPI) {
|
|
57551
|
+
pluginAPI = PluginAPI;
|
|
57552
|
+
globalPendo = pendo;
|
|
57553
|
+
const { ConfigReader } = pluginAPI;
|
|
57554
|
+
ConfigReader.addOption(CAPTURE_NETWORK_CONFIG, [ConfigReader.sources.PENDO_CONFIG_SRC], false);
|
|
57555
|
+
const captureNetworkEnabled = ConfigReader.get(CAPTURE_NETWORK_CONFIG);
|
|
57556
|
+
if (!captureNetworkEnabled)
|
|
57557
|
+
return;
|
|
57558
|
+
buffer = new DevlogBuffer(pendo, pluginAPI);
|
|
57559
|
+
transport = new DevlogTransport(pendo, pluginAPI);
|
|
57560
|
+
sendQueue = new SendQueue(transport.sendRequest.bind(transport));
|
|
57561
|
+
addConfigOptions();
|
|
57562
|
+
processHeaderConfig(NETWORK_LOGS_CONFIG_ALLOWED_REQUEST_HEADERS, allowedRequestHeaders);
|
|
57563
|
+
processHeaderConfig(NETWORK_LOGS_CONFIG_ALLOWED_RESPONSE_HEADERS, allowedResponseHeaders);
|
|
57564
|
+
setupBodyCallbacks();
|
|
57565
|
+
sendInterval = setInterval(() => {
|
|
57566
|
+
if (!sendQueue.failed()) {
|
|
57567
|
+
send();
|
|
57568
|
+
}
|
|
57569
|
+
}, SEND_INTERVAL);
|
|
57570
|
+
sendQueue.start();
|
|
57571
|
+
pluginAPI.Events.ready.on(startCapture);
|
|
57572
|
+
pluginAPI.Events['ptm:paused'].on(onPtmPaused);
|
|
57573
|
+
pluginAPI.Events['ptm:unpaused'].on(onPtmUnpaused);
|
|
57574
|
+
pluginAPI.Events.appHidden.on(onAppHidden);
|
|
57575
|
+
pluginAPI.Events.appUnloaded.on(onAppUnloaded);
|
|
57576
|
+
pendoDevlogBaseUrl = pluginAPI.transmit.buildBaseDataUrl(DEV_LOG_TYPE, globalPendo.apiKey);
|
|
57577
|
+
}
|
|
57578
|
+
function addConfigOptions() {
|
|
57579
|
+
const { ConfigReader } = pluginAPI;
|
|
57580
|
+
ConfigReader.addOption(NETWORK_LOGS_CONFIG, [ConfigReader.sources.SNIPPET_SRC], {});
|
|
57581
|
+
/**
|
|
57582
|
+
* Additional request headers to capture in network logs.
|
|
57583
|
+
* Default headers are: `['content-type', 'content-length', 'accept', 'accept-language']`
|
|
57584
|
+
*
|
|
57585
|
+
* @access public
|
|
57586
|
+
* @category Config/Network Logs
|
|
57587
|
+
* @name networkLogs.allowedRequestHeaders
|
|
57588
|
+
* @default []
|
|
57589
|
+
* @type {string[]}
|
|
57590
|
+
*/
|
|
57591
|
+
ConfigReader.addOption(NETWORK_LOGS_CONFIG_ALLOWED_REQUEST_HEADERS, [ConfigReader.sources.SNIPPET_SRC], []);
|
|
57592
|
+
/**
|
|
57593
|
+
* Additional response headers to capture in network logs.
|
|
57594
|
+
* Default headers are: `['cache-control', 'content-length', 'content-type', 'content-language']`
|
|
57595
|
+
*
|
|
57596
|
+
* @access public
|
|
57597
|
+
* @category Config/Network Logs
|
|
57598
|
+
* @name networkLogs.allowedResponseHeaders
|
|
57599
|
+
* @default []
|
|
57600
|
+
* @type {string[]}
|
|
57601
|
+
*/
|
|
57602
|
+
ConfigReader.addOption(NETWORK_LOGS_CONFIG_ALLOWED_RESPONSE_HEADERS, [ConfigReader.sources.SNIPPET_SRC], []);
|
|
57603
|
+
/**
|
|
57604
|
+
* Callback function to process/modify request bodies before capturing them. If not provided, the request body will not be captured.
|
|
57605
|
+
*
|
|
57606
|
+
* @access public
|
|
57607
|
+
* @category Config/Network Logs
|
|
57608
|
+
* @name networkLogs.captureRequestBody
|
|
57609
|
+
* @default undefined
|
|
57610
|
+
* @type {function}
|
|
57611
|
+
*/
|
|
57612
|
+
ConfigReader.addOption(NETWORK_LOGS_CONFIG_CAPTURE_REQUEST_BODY, [ConfigReader.sources.SNIPPET_SRC], undefined);
|
|
57613
|
+
/**
|
|
57614
|
+
* Callback function to process/modify response bodies before capturing them. If not provided, the response body will not be captured.
|
|
57615
|
+
*
|
|
57616
|
+
* @access public
|
|
57617
|
+
* @category Config/Network Logs
|
|
57618
|
+
* @name networkLogs.captureResponseBody
|
|
57619
|
+
* @default undefined
|
|
57620
|
+
* @type {function}
|
|
57621
|
+
*/
|
|
57622
|
+
ConfigReader.addOption(NETWORK_LOGS_CONFIG_CAPTURE_RESPONSE_BODY, [ConfigReader.sources.SNIPPET_SRC], undefined);
|
|
57623
|
+
}
|
|
57624
|
+
function processHeaderConfig(configHeaderName, targetHeaders) {
|
|
57625
|
+
const configHeaders = pluginAPI.ConfigReader.get(configHeaderName);
|
|
57626
|
+
if (!configHeaders || configHeaders.length === 0)
|
|
57627
|
+
return;
|
|
57628
|
+
globalPendo._.each(configHeaders, (header) => {
|
|
57629
|
+
if (!header || typeof header !== 'string')
|
|
57630
|
+
return;
|
|
57631
|
+
targetHeaders[header.toLowerCase()] = true;
|
|
57632
|
+
});
|
|
57633
|
+
}
|
|
57634
|
+
function setupBodyCallbacks() {
|
|
57635
|
+
const config = pluginAPI.ConfigReader.get(NETWORK_LOGS_CONFIG);
|
|
57636
|
+
if (!config)
|
|
57637
|
+
return;
|
|
57638
|
+
if (globalPendo._.isFunction(config.captureRequestBody)) {
|
|
57639
|
+
requestBodyCb = config.captureRequestBody;
|
|
57640
|
+
}
|
|
57641
|
+
if (globalPendo._.isFunction(config.captureResponseBody)) {
|
|
57642
|
+
responseBodyCb = config.captureResponseBody;
|
|
57643
|
+
}
|
|
57644
|
+
}
|
|
57645
|
+
function onPtmPaused() {
|
|
57646
|
+
isPtmPaused = true;
|
|
57647
|
+
}
|
|
57648
|
+
function onPtmUnpaused() {
|
|
57649
|
+
isPtmPaused = false;
|
|
57650
|
+
if (!buffer.isEmpty()) {
|
|
57651
|
+
for (const event of buffer.events) {
|
|
57652
|
+
pluginAPI.Events.eventCaptured.trigger(event);
|
|
57653
|
+
}
|
|
57654
|
+
send();
|
|
57655
|
+
}
|
|
57656
|
+
}
|
|
57657
|
+
function onAppHidden() {
|
|
57658
|
+
send({ hidden: true });
|
|
57659
|
+
}
|
|
57660
|
+
function onAppUnloaded() {
|
|
57661
|
+
send({ unload: true });
|
|
57662
|
+
}
|
|
57663
|
+
function startCapture() {
|
|
57664
|
+
pluginAPI.NetworkRequest.on({ request: handleRequest, response: handleResponse, error: handleError });
|
|
57665
|
+
}
|
|
57666
|
+
function teardown() {
|
|
57667
|
+
if (sendInterval) {
|
|
57668
|
+
clearInterval(sendInterval);
|
|
57669
|
+
}
|
|
57670
|
+
if (sendQueue) {
|
|
57671
|
+
sendQueue.stop();
|
|
57672
|
+
}
|
|
57673
|
+
if (buffer) {
|
|
57674
|
+
buffer.clear();
|
|
57675
|
+
}
|
|
57676
|
+
pluginAPI.NetworkRequest.off({ request: handleRequest, response: handleResponse, error: handleError });
|
|
57677
|
+
requestMap = {};
|
|
57678
|
+
pluginAPI.Events['ptm:paused'].off(onPtmPaused);
|
|
57679
|
+
pluginAPI.Events['ptm:unpaused'].off(onPtmUnpaused);
|
|
57680
|
+
pluginAPI.Events.appHidden.off(onAppHidden);
|
|
57681
|
+
pluginAPI.Events.appUnloaded.off(onAppUnloaded);
|
|
57682
|
+
}
|
|
57683
|
+
function handleRequest(request) {
|
|
57684
|
+
requestMap[request.requestId] = request;
|
|
57685
|
+
}
|
|
57686
|
+
function handleResponse(response) {
|
|
57687
|
+
if (!response)
|
|
57688
|
+
return;
|
|
57689
|
+
const request = requestMap[response.requestId];
|
|
57690
|
+
if (!request)
|
|
57691
|
+
return;
|
|
57692
|
+
// Skip capturing successful devlog events to avoid infinite loops
|
|
57693
|
+
if (request.url.indexOf(pendoDevlogBaseUrl) !== -1 && response.status === 200) {
|
|
57694
|
+
delete requestMap[response.requestId];
|
|
57695
|
+
return;
|
|
57696
|
+
}
|
|
57697
|
+
// push an empty network event to the buffer to ensure that we have a token available
|
|
57698
|
+
const networkEvent = {};
|
|
57699
|
+
const wasAccepted = buffer.push(networkEvent);
|
|
57700
|
+
if (!wasAccepted) {
|
|
57701
|
+
// Token limit reached, remove request from request map
|
|
57702
|
+
delete requestMap[response.requestId];
|
|
57703
|
+
return;
|
|
57704
|
+
}
|
|
57705
|
+
globalPendo._.extend(networkEvent, createNetworkEvent({
|
|
57706
|
+
request,
|
|
57707
|
+
response
|
|
57708
|
+
}));
|
|
57709
|
+
if (!isPtmPaused) {
|
|
57710
|
+
pluginAPI.Events.eventCaptured.trigger(networkEvent);
|
|
57711
|
+
}
|
|
57712
|
+
delete requestMap[response.requestId];
|
|
57713
|
+
}
|
|
57714
|
+
function handleError({ error, context }) {
|
|
57715
|
+
if (error.requestId && requestMap[error.requestId]) {
|
|
57716
|
+
delete requestMap[error.requestId];
|
|
57717
|
+
}
|
|
57718
|
+
if (context.indexOf('fetch') !== -1) {
|
|
57719
|
+
pluginAPI.log.debug('[NetworkCapture] Fetch tracking failed:', error);
|
|
57720
|
+
}
|
|
57721
|
+
if (context.indexOf('xhr') !== -1) {
|
|
57722
|
+
pluginAPI.log.debug('[NetworkCapture] XHR tracking failed:', error);
|
|
57723
|
+
}
|
|
57724
|
+
}
|
|
57725
|
+
function extractHeaders(headers, allowedHeaders) {
|
|
57726
|
+
const { keys, reduce } = globalPendo._;
|
|
57727
|
+
if (!headers || !allowedHeaders)
|
|
57728
|
+
return [];
|
|
57729
|
+
return reduce(keys(headers), (acc, key) => {
|
|
57730
|
+
const normalizedKey = key.toLowerCase();
|
|
57731
|
+
if (allowedHeaders[normalizedKey]) {
|
|
57732
|
+
acc.push(`${key}: ${headers[key]}`);
|
|
57733
|
+
}
|
|
57734
|
+
return acc;
|
|
57735
|
+
}, []);
|
|
57736
|
+
}
|
|
57737
|
+
function processBody(body, contentType) {
|
|
57738
|
+
if (!body || typeof body !== 'string')
|
|
57739
|
+
return '';
|
|
57740
|
+
const processedBody = maskSensitiveFields({ string: body, contentType, _: globalPendo._ });
|
|
57741
|
+
return truncate(processedBody, true);
|
|
57742
|
+
}
|
|
57743
|
+
function processRequestBody({ request }) {
|
|
57744
|
+
if (!request || !request.body || !requestBodyCb)
|
|
57745
|
+
return '';
|
|
57746
|
+
try {
|
|
57747
|
+
const body = requestBodyCb(request.body, { request });
|
|
57748
|
+
const contentType = globalPendo._.get(request, 'headers.content-type', '');
|
|
57749
|
+
return processBody(body, contentType);
|
|
57750
|
+
}
|
|
57751
|
+
catch (error) {
|
|
57752
|
+
pluginAPI.log.error('[NetworkCapture] Error processing request body', error);
|
|
57753
|
+
return '[Failed to process request body]';
|
|
57754
|
+
}
|
|
57755
|
+
}
|
|
57756
|
+
function processResponseBody({ response }) {
|
|
57757
|
+
if (!response || !response.body || !responseBodyCb)
|
|
57758
|
+
return '';
|
|
57759
|
+
try {
|
|
57760
|
+
const body = responseBodyCb(response.body, { response });
|
|
57761
|
+
const contentType = globalPendo._.get(response, 'headers.content-type', '');
|
|
57762
|
+
return processBody(body, contentType);
|
|
57763
|
+
}
|
|
57764
|
+
catch (error) {
|
|
57765
|
+
pluginAPI.log.error('[NetworkCapture] Error processing response body', error);
|
|
57766
|
+
return '[Failed to process response body]';
|
|
57767
|
+
}
|
|
57768
|
+
}
|
|
57769
|
+
function createNetworkEvent({ request, response }) {
|
|
57770
|
+
const devLogEnvelope = createDevLogEnvelope(pluginAPI, globalPendo);
|
|
57771
|
+
const requestHeaders = extractHeaders(request.headers, allowedRequestHeaders);
|
|
57772
|
+
const responseHeaders = extractHeaders(response.headers, allowedResponseHeaders);
|
|
57773
|
+
const networkEvent = Object.assign(Object.assign({}, devLogEnvelope), { subType: NETWORK_SUB_TYPE, devLogMethod: request.method, devLogStatusCode: response.status, devLogRequestUrl: request.url, devLogRequestHeaders: requestHeaders, devLogResponseHeaders: responseHeaders, devLogCount: 1 });
|
|
57774
|
+
if (requestBodyCb) {
|
|
57775
|
+
networkEvent.devLogRequestBody = processRequestBody({ request });
|
|
57776
|
+
}
|
|
57777
|
+
if (responseBodyCb) {
|
|
57778
|
+
networkEvent.devLogResponseBody = processResponseBody({ response });
|
|
57779
|
+
}
|
|
57780
|
+
return networkEvent;
|
|
57781
|
+
}
|
|
57782
|
+
function send({ unload = false, hidden = false } = {}) {
|
|
57783
|
+
if (!buffer || buffer.isEmpty())
|
|
57784
|
+
return;
|
|
57785
|
+
if (!globalPendo.isSendingEvents()) {
|
|
57786
|
+
buffer.clear();
|
|
57787
|
+
return;
|
|
57788
|
+
}
|
|
57789
|
+
const jzb = buffer.pack();
|
|
57790
|
+
const queryParams = {
|
|
57791
|
+
v: globalPendo.VERSION,
|
|
57792
|
+
ct: pluginAPI.util.getNow()
|
|
57793
|
+
};
|
|
57794
|
+
const url = pluginAPI.transmit.buildBaseDataUrl(DEV_LOG_TYPE, globalPendo.apiKey, queryParams);
|
|
57795
|
+
if (unload || hidden) {
|
|
57796
|
+
sendQueue.drain([{ url, jzb }], unload);
|
|
57797
|
+
}
|
|
57798
|
+
else {
|
|
57799
|
+
sendQueue.push({ url, jzb });
|
|
57800
|
+
}
|
|
57801
|
+
}
|
|
57802
|
+
}
|
|
57803
|
+
|
|
57804
|
+
export { ConsoleCapture, Feedback, GuideMarkdown, NetworkCapture, createReplayPlugin as Replay, TextCapture, VocPortal, loadAgent };
|