@pendo/agent 2.293.1 → 2.294.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/dist/debugger-plugin.min.js +1 -1
- package/dist/dom.esm.js +1 -1
- package/dist/pendo.debugger.min.js +8 -8
- package/dist/pendo.module.js +432 -37
- package/dist/pendo.module.min.js +10 -8
- package/package.json +1 -1
package/dist/pendo.module.js
CHANGED
|
@@ -3908,8 +3908,8 @@ var SERVER = '';
|
|
|
3908
3908
|
var ASSET_HOST = '';
|
|
3909
3909
|
var ASSET_PATH = '';
|
|
3910
3910
|
var DESIGNER_SERVER = '';
|
|
3911
|
-
var VERSION = '2.
|
|
3912
|
-
var PACKAGE_VERSION = '2.
|
|
3911
|
+
var VERSION = '2.294.0_';
|
|
3912
|
+
var PACKAGE_VERSION = '2.294.0';
|
|
3913
3913
|
var LOADER = 'xhr';
|
|
3914
3914
|
/* eslint-enable agent-eslint-rules/no-gulp-env-references */
|
|
3915
3915
|
/**
|
|
@@ -12231,6 +12231,7 @@ class PerformanceMonitor {
|
|
|
12231
12231
|
return detectNativeBrowserAPI('performance.mark') &&
|
|
12232
12232
|
detectNativeBrowserAPI('performance.measure') &&
|
|
12233
12233
|
detectNativeBrowserAPI('performance.getEntries') &&
|
|
12234
|
+
detectNativeBrowserAPI('performance.getEntriesByName') &&
|
|
12234
12235
|
detectNativeBrowserAPI('performance.clearMarks') &&
|
|
12235
12236
|
detectNativeBrowserAPI('performance.clearMeasures');
|
|
12236
12237
|
}
|
|
@@ -12250,8 +12251,10 @@ class PerformanceMonitor {
|
|
|
12250
12251
|
name = `pendo-${name}`;
|
|
12251
12252
|
const startMark = `${name}-start`;
|
|
12252
12253
|
const stopMark = `${name}-stop`;
|
|
12253
|
-
performance.
|
|
12254
|
-
|
|
12254
|
+
if (performance.getEntriesByName(startMark).length && performance.getEntriesByName(stopMark).length) {
|
|
12255
|
+
performance.measure(name, startMark, stopMark);
|
|
12256
|
+
this._count(this._measures, name);
|
|
12257
|
+
}
|
|
12255
12258
|
}
|
|
12256
12259
|
_count(registry, name) {
|
|
12257
12260
|
if (!registry[name]) {
|
|
@@ -13304,6 +13307,7 @@ var getValidTarget = function (node) {
|
|
|
13304
13307
|
*/
|
|
13305
13308
|
var handle_event = function (evt) {
|
|
13306
13309
|
try {
|
|
13310
|
+
PerformanceMonitor$1.startTimer('event-captured');
|
|
13307
13311
|
if (dom.data.get(evt, 'counted'))
|
|
13308
13312
|
return;
|
|
13309
13313
|
dom.data.set(evt, 'counted', true);
|
|
@@ -13343,6 +13347,9 @@ var handle_event = function (evt) {
|
|
|
13343
13347
|
catch (e) {
|
|
13344
13348
|
log.critical('pendo.io while handling event', { error: e });
|
|
13345
13349
|
}
|
|
13350
|
+
finally {
|
|
13351
|
+
PerformanceMonitor$1.stopTimer('event-captured');
|
|
13352
|
+
}
|
|
13346
13353
|
};
|
|
13347
13354
|
function getClickEventProperties(target) {
|
|
13348
13355
|
const eventPropertyHandler = getEventPropertyHandler(target);
|
|
@@ -17866,6 +17873,9 @@ function getAllowedAttributes(attributeKeyValueMap, stepId, guideId, type) {
|
|
|
17866
17873
|
function buildNodeFromJSON(json, step, guides) {
|
|
17867
17874
|
step = step || { id: 'unknown', guideId: 'unknown' };
|
|
17868
17875
|
json.props = getAllowedAttributes(json.props, step.id, step.guideId, json.type);
|
|
17876
|
+
if (step.isDarkMode && json.darkModeProps) {
|
|
17877
|
+
json.darkModeProps = getAllowedAttributes(json.darkModeProps, step.id, step.guideId, json.type);
|
|
17878
|
+
}
|
|
17869
17879
|
var curNode = dom(document.createElement(json.type));
|
|
17870
17880
|
// APP-81040 calling code in the pendo app (and possibly elsewhere) depends on
|
|
17871
17881
|
// curNode.getParent() returning non-null in some cases. The fact that it used to
|
|
@@ -17885,7 +17895,11 @@ function buildNodeFromJSON(json, step, guides) {
|
|
|
17885
17895
|
}
|
|
17886
17896
|
_.each(json.props, function (propValue, propKey) {
|
|
17887
17897
|
if (propKey === 'style') {
|
|
17888
|
-
|
|
17898
|
+
let nodeStyle = json.props.style;
|
|
17899
|
+
if (step.isDarkMode && json.darkModeProps) {
|
|
17900
|
+
nodeStyle = Object.assign(Object.assign({}, json.props.style), json.darkModeProps.style);
|
|
17901
|
+
}
|
|
17902
|
+
curNode.css(nodeStyle);
|
|
17889
17903
|
}
|
|
17890
17904
|
else if (propKey === 'data-pendo-code-block' && propValue === true && !ConfigReader.get('preventCodeInjection')) {
|
|
17891
17905
|
const htmlString = step.getContent();
|
|
@@ -17910,13 +17924,23 @@ function buildNodeFromJSON(json, step, guides) {
|
|
|
17910
17924
|
if (nonce) {
|
|
17911
17925
|
curNode.attr('nonce', nonce);
|
|
17912
17926
|
}
|
|
17927
|
+
let css = json.css;
|
|
17928
|
+
if (json.forDarkMode) {
|
|
17929
|
+
const darkModeSelector = _.get(step, 'attributes.darkMode.selector', '');
|
|
17930
|
+
css = _.map(css, function (rule) {
|
|
17931
|
+
return {
|
|
17932
|
+
styles: rule.styles,
|
|
17933
|
+
selector: `${darkModeSelector} ${rule.selector}`
|
|
17934
|
+
};
|
|
17935
|
+
});
|
|
17936
|
+
}
|
|
17913
17937
|
// TODO: make this render building-block pseudo-styles properly for IE7-8. This current functionality allows guides to render in IE but there are lots of styling problems.
|
|
17914
17938
|
// `curNode.text` specifically breaks in IE8 since style tags text attributes are read only. From researching `node.styleSheet.cssText` is the correct way to do it.
|
|
17915
17939
|
if (curNode.styleSheet) {
|
|
17916
|
-
curNode.styleSheet.cssText = buildStyleTagContent(
|
|
17940
|
+
curNode.styleSheet.cssText = buildStyleTagContent(css);
|
|
17917
17941
|
}
|
|
17918
17942
|
else {
|
|
17919
|
-
curNode.text(buildStyleTagContent(
|
|
17943
|
+
curNode.text(buildStyleTagContent(css));
|
|
17920
17944
|
}
|
|
17921
17945
|
}
|
|
17922
17946
|
if (json.svgWidgetId) {
|
|
@@ -20614,6 +20638,21 @@ class AutoDisplayPhase {
|
|
|
20614
20638
|
}
|
|
20615
20639
|
}
|
|
20616
20640
|
|
|
20641
|
+
function syncColorMode(step, rerender = false) {
|
|
20642
|
+
const darkModeSelector = _.get(step, 'attributes.darkMode.selector', '');
|
|
20643
|
+
if (!darkModeSelector)
|
|
20644
|
+
return;
|
|
20645
|
+
const isDarkMode = pendo$1.Sizzle(darkModeSelector).length > 0;
|
|
20646
|
+
const darkModeChanged = step.isDarkMode !== isDarkMode;
|
|
20647
|
+
if (darkModeChanged) {
|
|
20648
|
+
step.isDarkMode = isDarkMode;
|
|
20649
|
+
if (rerender) {
|
|
20650
|
+
step.hide();
|
|
20651
|
+
step.show(step.seenReason);
|
|
20652
|
+
}
|
|
20653
|
+
}
|
|
20654
|
+
}
|
|
20655
|
+
|
|
20617
20656
|
/*
|
|
20618
20657
|
* Guide Loop
|
|
20619
20658
|
*
|
|
@@ -20812,6 +20851,7 @@ function stepShowingProc(guide, step) {
|
|
|
20812
20851
|
step.attributes.currentTextZoomFontSize = currentBrowserFontSize;
|
|
20813
20852
|
}
|
|
20814
20853
|
}
|
|
20854
|
+
syncColorMode(step, true);
|
|
20815
20855
|
if (step.elementPathRule && targetElement && !SizzleProxy.matchesSelector(targetElement, step.elementPathRule)) {
|
|
20816
20856
|
step.hide();
|
|
20817
20857
|
return;
|
|
@@ -26340,53 +26380,152 @@ class NetworkRequestIntercept {
|
|
|
26340
26380
|
extractXHRHeaders(xhr) {
|
|
26341
26381
|
return xhr[PENDO_HEADERS_KEY] || {};
|
|
26342
26382
|
}
|
|
26343
|
-
|
|
26383
|
+
generateRequestId() {
|
|
26384
|
+
const $stringLength = 8;
|
|
26385
|
+
return 'req_' + Date.now() + '_' + pendo$1.randomString($stringLength);
|
|
26386
|
+
}
|
|
26387
|
+
safelyReadResponse(response) {
|
|
26388
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
26389
|
+
try {
|
|
26390
|
+
const contentType = response.headers.get('content-type') || '';
|
|
26391
|
+
if (contentType.indexOf('application/json') !== -1) {
|
|
26392
|
+
return yield response.json();
|
|
26393
|
+
}
|
|
26394
|
+
else if (contentType.indexOf('text/') !== -1) {
|
|
26395
|
+
return yield response.text();
|
|
26396
|
+
}
|
|
26397
|
+
else {
|
|
26398
|
+
// For binary or unknown content types, just capture metadata
|
|
26399
|
+
return `[Binary content: ${contentType}]`;
|
|
26400
|
+
}
|
|
26401
|
+
}
|
|
26402
|
+
catch (e) {
|
|
26403
|
+
return '[Unable to read response body]';
|
|
26404
|
+
}
|
|
26405
|
+
});
|
|
26406
|
+
}
|
|
26407
|
+
extractResponseHeaders(response) {
|
|
26408
|
+
let headers = {};
|
|
26409
|
+
if (response.headers) {
|
|
26410
|
+
const headerEntries = Array.from(response.headers.entries());
|
|
26411
|
+
headers = this.entriesToObject(headerEntries);
|
|
26412
|
+
}
|
|
26413
|
+
return headers;
|
|
26414
|
+
}
|
|
26415
|
+
parseXHRResponseHeaders(headerString) {
|
|
26416
|
+
const headers = {};
|
|
26417
|
+
if (!headerString)
|
|
26418
|
+
return headers;
|
|
26419
|
+
const lines = headerString.split('\r\n');
|
|
26420
|
+
_.each(lines, line => {
|
|
26421
|
+
const parts = line.split(': ');
|
|
26422
|
+
if (parts.length === 2) {
|
|
26423
|
+
headers[parts[0]] = parts[1];
|
|
26424
|
+
}
|
|
26425
|
+
});
|
|
26426
|
+
return headers;
|
|
26427
|
+
}
|
|
26428
|
+
patchFetch() {
|
|
26344
26429
|
const networkInterceptor = this;
|
|
26345
|
-
if (networkInterceptor._networkPatched)
|
|
26346
|
-
return;
|
|
26347
|
-
networkInterceptor._networkPatched = true;
|
|
26348
|
-
networkInterceptor._originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
26349
|
-
XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
|
|
26350
|
-
this[PENDO_HEADERS_KEY] = this[PENDO_HEADERS_KEY] || {};
|
|
26351
|
-
this[PENDO_HEADERS_KEY][header] = value;
|
|
26352
|
-
return networkInterceptor._originalSetRequestHeader.apply(this, arguments);
|
|
26353
|
-
};
|
|
26354
26430
|
networkInterceptor._originalFetch = window.fetch;
|
|
26355
26431
|
window.fetch = function (...args) {
|
|
26356
26432
|
return __awaiter(this, void 0, void 0, function* () {
|
|
26357
26433
|
const [url, config = {}] = args;
|
|
26358
26434
|
const method = config.method || 'GET';
|
|
26435
|
+
const requestId = networkInterceptor.generateRequestId();
|
|
26436
|
+
// Capture request data
|
|
26359
26437
|
try {
|
|
26360
26438
|
const headers = networkInterceptor.extractHeaders(config.headers);
|
|
26361
26439
|
const body = networkInterceptor.safelySerializeBody(config.body);
|
|
26362
26440
|
_.each(networkInterceptor.callbackFns, ({ request }) => {
|
|
26363
26441
|
if (_.isFunction(request))
|
|
26364
|
-
request({ method, url, body, headers });
|
|
26442
|
+
request({ requestId, method, url, body, headers });
|
|
26365
26443
|
});
|
|
26366
26444
|
}
|
|
26367
26445
|
catch (e) {
|
|
26368
26446
|
_.each(networkInterceptor.callbackFns, ({ error }) => {
|
|
26369
26447
|
if (_.isFunction(error))
|
|
26370
|
-
error({ context: '
|
|
26448
|
+
error({ context: 'fetchRequest', error: e });
|
|
26449
|
+
});
|
|
26450
|
+
}
|
|
26451
|
+
// Make the actual fetch call and capture response
|
|
26452
|
+
try {
|
|
26453
|
+
const res = yield networkInterceptor._originalFetch.apply(this, args);
|
|
26454
|
+
// Clone the response to avoid consuming the body
|
|
26455
|
+
const responseClone = res.clone();
|
|
26456
|
+
try {
|
|
26457
|
+
// Capture response data
|
|
26458
|
+
const responseBody = yield networkInterceptor.safelyReadResponse(responseClone);
|
|
26459
|
+
const { status, statusText } = res;
|
|
26460
|
+
const headers = networkInterceptor.extractResponseHeaders(res);
|
|
26461
|
+
_.each(networkInterceptor.callbackFns, ({ response }) => {
|
|
26462
|
+
if (_.isFunction(response)) {
|
|
26463
|
+
response({
|
|
26464
|
+
requestId,
|
|
26465
|
+
status,
|
|
26466
|
+
statusText,
|
|
26467
|
+
body: responseBody,
|
|
26468
|
+
headers,
|
|
26469
|
+
url,
|
|
26470
|
+
method
|
|
26471
|
+
});
|
|
26472
|
+
}
|
|
26473
|
+
});
|
|
26474
|
+
}
|
|
26475
|
+
catch (e) {
|
|
26476
|
+
_.each(networkInterceptor.callbackFns, ({ error }) => {
|
|
26477
|
+
if (_.isFunction(error))
|
|
26478
|
+
error({ context: 'fetchResponse', error: e });
|
|
26479
|
+
});
|
|
26480
|
+
}
|
|
26481
|
+
return res;
|
|
26482
|
+
}
|
|
26483
|
+
catch (error) {
|
|
26484
|
+
// Handle network errors
|
|
26485
|
+
_.each(networkInterceptor.callbackFns, ({ response }) => {
|
|
26486
|
+
if (_.isFunction(response)) {
|
|
26487
|
+
response({
|
|
26488
|
+
requestId,
|
|
26489
|
+
status: 0,
|
|
26490
|
+
statusText: 'Network Error',
|
|
26491
|
+
headers: {},
|
|
26492
|
+
body: null,
|
|
26493
|
+
url,
|
|
26494
|
+
method,
|
|
26495
|
+
error: error.message
|
|
26496
|
+
});
|
|
26497
|
+
}
|
|
26371
26498
|
});
|
|
26499
|
+
throw error;
|
|
26372
26500
|
}
|
|
26373
|
-
return networkInterceptor._originalFetch.apply(this, args);
|
|
26374
26501
|
});
|
|
26375
26502
|
};
|
|
26503
|
+
}
|
|
26504
|
+
patchXHR() {
|
|
26505
|
+
const networkInterceptor = this;
|
|
26506
|
+
networkInterceptor._originalSetRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
|
|
26376
26507
|
networkInterceptor._originalXHROpen = XMLHttpRequest.prototype.open;
|
|
26377
26508
|
networkInterceptor._originalXHRSend = XMLHttpRequest.prototype.send;
|
|
26509
|
+
XMLHttpRequest.prototype.setRequestHeader = function (header, value) {
|
|
26510
|
+
this[PENDO_HEADERS_KEY] = this[PENDO_HEADERS_KEY] || {};
|
|
26511
|
+
this[PENDO_HEADERS_KEY][header] = value;
|
|
26512
|
+
return networkInterceptor._originalSetRequestHeader.apply(this, arguments);
|
|
26513
|
+
};
|
|
26378
26514
|
XMLHttpRequest.prototype.open = function (method, url) {
|
|
26379
|
-
this._method = method;
|
|
26515
|
+
this._method = method;
|
|
26380
26516
|
this._url = url;
|
|
26517
|
+
this._requestId = networkInterceptor.generateRequestId();
|
|
26381
26518
|
return networkInterceptor._originalXHROpen.apply(this, arguments);
|
|
26382
26519
|
};
|
|
26383
26520
|
XMLHttpRequest.prototype.send = function (body) {
|
|
26521
|
+
const xhr = this;
|
|
26522
|
+
const headers = networkInterceptor.extractXHRHeaders(this);
|
|
26523
|
+
const safeBody = networkInterceptor.safelySerializeBody(body);
|
|
26384
26524
|
try {
|
|
26385
|
-
const headers = networkInterceptor.extractXHRHeaders(this);
|
|
26386
|
-
const safeBody = networkInterceptor.safelySerializeBody(body);
|
|
26387
26525
|
_.each(networkInterceptor.callbackFns, ({ request }) => {
|
|
26388
26526
|
if (_.isFunction(request)) {
|
|
26389
26527
|
request({
|
|
26528
|
+
requestId: this._requestId,
|
|
26390
26529
|
method: this._method || 'GET',
|
|
26391
26530
|
url: this._url,
|
|
26392
26531
|
body: safeBody,
|
|
@@ -26398,7 +26537,7 @@ class NetworkRequestIntercept {
|
|
|
26398
26537
|
catch (e) {
|
|
26399
26538
|
_.each(networkInterceptor.callbackFns, ({ error }) => {
|
|
26400
26539
|
if (_.isFunction(error))
|
|
26401
|
-
error({ context: '
|
|
26540
|
+
error({ context: 'xhrRequest', error: e });
|
|
26402
26541
|
});
|
|
26403
26542
|
}
|
|
26404
26543
|
// Clean up custom headers after the request completes
|
|
@@ -26407,9 +26546,49 @@ class NetworkRequestIntercept {
|
|
|
26407
26546
|
delete this[PENDO_HEADERS_KEY];
|
|
26408
26547
|
}, { once: true });
|
|
26409
26548
|
}
|
|
26549
|
+
// Set up response tracking
|
|
26550
|
+
const originalOnReadyStateChange = xhr.onreadystatechange;
|
|
26551
|
+
xhr.onreadystatechange = function () {
|
|
26552
|
+
if (xhr.readyState === 4) { // Request completed
|
|
26553
|
+
try {
|
|
26554
|
+
const headers = networkInterceptor.parseXHRResponseHeaders(xhr.getAllResponseHeaders());
|
|
26555
|
+
const { status, statusText, responseText: body, _url } = xhr;
|
|
26556
|
+
_.each(networkInterceptor.callbackFns, ({ response }) => {
|
|
26557
|
+
if (_.isFunction(response)) {
|
|
26558
|
+
response({
|
|
26559
|
+
requestId: xhr._requestId,
|
|
26560
|
+
status,
|
|
26561
|
+
statusText,
|
|
26562
|
+
headers,
|
|
26563
|
+
body,
|
|
26564
|
+
url: _url,
|
|
26565
|
+
method: xhr._method || 'GET'
|
|
26566
|
+
});
|
|
26567
|
+
}
|
|
26568
|
+
});
|
|
26569
|
+
}
|
|
26570
|
+
catch (e) {
|
|
26571
|
+
_.each(networkInterceptor.callbackFns, ({ error }) => {
|
|
26572
|
+
if (_.isFunction(error))
|
|
26573
|
+
error({ context: 'xhrResponse', error: e });
|
|
26574
|
+
});
|
|
26575
|
+
}
|
|
26576
|
+
}
|
|
26577
|
+
if (originalOnReadyStateChange) {
|
|
26578
|
+
originalOnReadyStateChange.apply(this, arguments);
|
|
26579
|
+
}
|
|
26580
|
+
};
|
|
26410
26581
|
return networkInterceptor._originalXHRSend.apply(this, arguments);
|
|
26411
26582
|
};
|
|
26412
26583
|
}
|
|
26584
|
+
patchNetwork() {
|
|
26585
|
+
const networkInterceptor = this;
|
|
26586
|
+
if (networkInterceptor._networkPatched)
|
|
26587
|
+
return;
|
|
26588
|
+
networkInterceptor._networkPatched = true;
|
|
26589
|
+
networkInterceptor.patchFetch();
|
|
26590
|
+
networkInterceptor.patchXHR();
|
|
26591
|
+
}
|
|
26413
26592
|
on({ request, response, error } = {}) {
|
|
26414
26593
|
if (!this.callbackFns) {
|
|
26415
26594
|
this.callbackFns = [];
|
|
@@ -26417,7 +26596,7 @@ class NetworkRequestIntercept {
|
|
|
26417
26596
|
if (_.isFunction(request) || _.isFunction(response) || _.isFunction(error)) {
|
|
26418
26597
|
this.callbackFns.push({ request, response, error });
|
|
26419
26598
|
}
|
|
26420
|
-
if (!this._networkPatched
|
|
26599
|
+
if (!this._networkPatched) {
|
|
26421
26600
|
this.patchNetwork();
|
|
26422
26601
|
}
|
|
26423
26602
|
return this;
|
|
@@ -27237,6 +27416,8 @@ const PluginAPI = {
|
|
|
27237
27416
|
collectEvent
|
|
27238
27417
|
},
|
|
27239
27418
|
attachEvent,
|
|
27419
|
+
attachEventInternal,
|
|
27420
|
+
detachEventInternal,
|
|
27240
27421
|
ConfigReader,
|
|
27241
27422
|
Events,
|
|
27242
27423
|
EventTracer,
|
|
@@ -28201,6 +28382,9 @@ var BuildingBlockGuides = (function () {
|
|
|
28201
28382
|
function renderGuideFromJSON(json, step, guides, options) {
|
|
28202
28383
|
options = options || {};
|
|
28203
28384
|
var guide = step.getGuide();
|
|
28385
|
+
if (step.isDarkMode === undefined) {
|
|
28386
|
+
syncColorMode(step);
|
|
28387
|
+
}
|
|
28204
28388
|
var containerJSON = findGuideContainerJSON(json);
|
|
28205
28389
|
var isResourceCenter = _.get(guide, 'attributes.resourceCenter');
|
|
28206
28390
|
if (isResourceCenter) {
|
|
@@ -31068,6 +31252,10 @@ var GuideDisplay = (function () {
|
|
|
31068
31252
|
if (step.type !== 'whatsnew' && !step.isShown()) {
|
|
31069
31253
|
return false;
|
|
31070
31254
|
}
|
|
31255
|
+
if (isGuideRequestPending()) {
|
|
31256
|
+
log.info('guides are loading.', { contexts: ['guides', 'loading'] });
|
|
31257
|
+
return false;
|
|
31258
|
+
}
|
|
31071
31259
|
step.unlock();
|
|
31072
31260
|
step._show(reason);
|
|
31073
31261
|
return step.isShown();
|
|
@@ -31552,6 +31740,7 @@ function GuideStep(guide) {
|
|
|
31552
31740
|
step.seenState = 'active';
|
|
31553
31741
|
setSeenTime(getNow());
|
|
31554
31742
|
var seenProps = {
|
|
31743
|
+
color_mode: step.isDarkMode ? 'dark' : 'default',
|
|
31555
31744
|
last_updated_at: step.lastUpdatedAt
|
|
31556
31745
|
};
|
|
31557
31746
|
var pollTypes = this.getStepPollTypes(guide, step);
|
|
@@ -33243,7 +33432,7 @@ const DebuggerModule = (() => {
|
|
|
33243
33432
|
location: { type: SYNC_TYPES.BOTTOM_UP, defaultValue: {} }
|
|
33244
33433
|
};
|
|
33245
33434
|
const actions = {
|
|
33246
|
-
init: (context) => {
|
|
33435
|
+
init: (context, buffer) => {
|
|
33247
33436
|
context.commit('setFrameId', EventTracer.getFrameId());
|
|
33248
33437
|
context.commit('setTabId', EventTracer.getTabId());
|
|
33249
33438
|
context.commit('setInstallType', getInstallType());
|
|
@@ -33263,6 +33452,7 @@ const DebuggerModule = (() => {
|
|
|
33263
33452
|
frameId: context.state.frameId
|
|
33264
33453
|
});
|
|
33265
33454
|
}
|
|
33455
|
+
_.each(buffer.events, (event) => eventCapturedFn(event));
|
|
33266
33456
|
},
|
|
33267
33457
|
join: (context, data) => {
|
|
33268
33458
|
if (context.state.frameId === data.frameId)
|
|
@@ -36956,12 +37146,18 @@ function disableDebugging() {
|
|
|
36956
37146
|
_.extend(debug, debugging);
|
|
36957
37147
|
|
|
36958
37148
|
const waitForLeader = (Events, store, q) => {
|
|
37149
|
+
const buffer = {
|
|
37150
|
+
events: []
|
|
37151
|
+
};
|
|
37152
|
+
Events.on('eventCaptured', (evt) => {
|
|
37153
|
+
buffer.events.push(evt);
|
|
37154
|
+
});
|
|
36959
37155
|
if (store.getters['frames/leaderExists']()) {
|
|
36960
|
-
return q.resolve();
|
|
37156
|
+
return q.resolve(buffer);
|
|
36961
37157
|
}
|
|
36962
37158
|
const deferred = q.defer();
|
|
36963
37159
|
Events.on('leaderChanged', () => {
|
|
36964
|
-
deferred.resolve();
|
|
37160
|
+
deferred.resolve(buffer);
|
|
36965
37161
|
});
|
|
36966
37162
|
return deferred.promise;
|
|
36967
37163
|
};
|
|
@@ -36976,8 +37172,8 @@ const DebuggerLauncher = (function () {
|
|
|
36976
37172
|
function initialize(pendo, PluginAPI) {
|
|
36977
37173
|
const { Events, q } = PluginAPI;
|
|
36978
37174
|
store = PluginAPI.store;
|
|
36979
|
-
waitForLeader(Events, store, q).then(() => {
|
|
36980
|
-
store.dispatch('debugger/init');
|
|
37175
|
+
waitForLeader(Events, store, q).then((buffer) => {
|
|
37176
|
+
store.dispatch('debugger/init', buffer);
|
|
36981
37177
|
if (store.getters['frames/isLeader']()) {
|
|
36982
37178
|
startDebuggingModuleIfEnabled();
|
|
36983
37179
|
}
|
|
@@ -44343,13 +44539,15 @@ class MutationBuffer {
|
|
|
44343
44539
|
index.childNodes(textarea),
|
|
44344
44540
|
(cn) => index.textContent(cn) || ""
|
|
44345
44541
|
).join("");
|
|
44542
|
+
const needsMask = needMaskingText(textarea, this.maskTextClass, this.maskTextSelector, true);
|
|
44346
44543
|
item.attributes.value = maskInputValue({
|
|
44347
44544
|
element: textarea,
|
|
44348
44545
|
maskInputOptions: this.maskInputOptions,
|
|
44349
44546
|
tagName: textarea.tagName,
|
|
44350
44547
|
type: getInputType(textarea),
|
|
44351
44548
|
value,
|
|
44352
|
-
maskInputFn: this.maskInputFn
|
|
44549
|
+
maskInputFn: this.maskInputFn,
|
|
44550
|
+
needsMask
|
|
44353
44551
|
});
|
|
44354
44552
|
});
|
|
44355
44553
|
__publicField(this, "processMutation", (m) => {
|
|
@@ -52555,6 +52753,12 @@ class SessionRecorder {
|
|
|
52555
52753
|
return;
|
|
52556
52754
|
clearTimeout(this._changeIdentityTimer);
|
|
52557
52755
|
if (this.isRecording()) {
|
|
52756
|
+
this.logStopReason('IDENTITY_CHANGED', {
|
|
52757
|
+
previousVisitorId: this.visitorId,
|
|
52758
|
+
newVisitorId: identifyEvent.data[0].visitor_id,
|
|
52759
|
+
previousAccountId: this.accountId,
|
|
52760
|
+
newAccountId: identifyEvent.data[0].account_id
|
|
52761
|
+
});
|
|
52558
52762
|
this.stop();
|
|
52559
52763
|
}
|
|
52560
52764
|
this.visitorId = identifyEvent.data[0].visitor_id;
|
|
@@ -52650,7 +52854,7 @@ class SessionRecorder {
|
|
|
52650
52854
|
this._start();
|
|
52651
52855
|
}
|
|
52652
52856
|
_markEvents(events) {
|
|
52653
|
-
if (!this.recordingId)
|
|
52857
|
+
if (!this.recordingId || !this.isRecording())
|
|
52654
52858
|
return;
|
|
52655
52859
|
for (var e of events) {
|
|
52656
52860
|
if ((e.visitor_id === this.visitorId || e.visitorId === this.visitorId) && e.type !== 'identify') {
|
|
@@ -52968,6 +53172,8 @@ class SessionRecorder {
|
|
|
52968
53172
|
}
|
|
52969
53173
|
}
|
|
52970
53174
|
addRecordingId(event) {
|
|
53175
|
+
if (!this.isRecording())
|
|
53176
|
+
return;
|
|
52971
53177
|
if (!this.recordingId || !event || !event.data || !event.data.length)
|
|
52972
53178
|
return;
|
|
52973
53179
|
var capturedEvent = event.data[0];
|
|
@@ -52976,6 +53182,10 @@ class SessionRecorder {
|
|
|
52976
53182
|
if (capturedEvent.visitor_id !== this.visitorId && capturedEvent.visitorId !== this.visitorId) {
|
|
52977
53183
|
// visitor id has already diverged from the agent, we'll stop sending events and just return here
|
|
52978
53184
|
this.api.log.warn('Visitor id has diverged from agent');
|
|
53185
|
+
this.logStopReason('VISITOR_ID_DIVERGED', {
|
|
53186
|
+
agentVisitorId: capturedEvent.visitor_id || capturedEvent.visitorId,
|
|
53187
|
+
recordingVisitorId: this.visitorId
|
|
53188
|
+
});
|
|
52979
53189
|
this.stop();
|
|
52980
53190
|
return;
|
|
52981
53191
|
}
|
|
@@ -54155,6 +54365,168 @@ function scrubPII(string) {
|
|
|
54155
54365
|
return Object.values(PII_PATTERN).reduce((str, pattern) => str.replace(pattern, PII_REPLACEMENT), string);
|
|
54156
54366
|
}
|
|
54157
54367
|
|
|
54368
|
+
/**
|
|
54369
|
+
* Determines the type of resource that was blocked based on the blocked URI and CSP directive.
|
|
54370
|
+
*
|
|
54371
|
+
* @param {string} blockedURI - The URI that was blocked by the CSP policy (can be 'inline', 'eval', or a URL)
|
|
54372
|
+
* @param {string} directive - The CSP directive that caused the violation (e.g., 'script-src', 'style-src')
|
|
54373
|
+
* @returns {string} A human-readable description of the resource type
|
|
54374
|
+
*
|
|
54375
|
+
* @example
|
|
54376
|
+
* getResourceType('inline', 'script-src') // returns 'inline script'
|
|
54377
|
+
* getResourceType('https://example.com/script.js', 'script-src') // returns 'script'
|
|
54378
|
+
* getResourceType('https://example.com/image.jpg', 'img-src') // returns 'image'
|
|
54379
|
+
* getResourceType('https://example.com/worker.js', 'worker-src') // returns 'worker'
|
|
54380
|
+
*/
|
|
54381
|
+
function getResourceType(blockedURI, directive) {
|
|
54382
|
+
if (!directive || typeof directive !== 'string')
|
|
54383
|
+
return 'resource';
|
|
54384
|
+
const d = directive.toLowerCase();
|
|
54385
|
+
if (blockedURI === 'inline') {
|
|
54386
|
+
if (d.includes('script-src-attr')) {
|
|
54387
|
+
return 'inline event handler';
|
|
54388
|
+
}
|
|
54389
|
+
if (d.includes('style-src-attr')) {
|
|
54390
|
+
return 'inline style attribute';
|
|
54391
|
+
}
|
|
54392
|
+
if (d.includes('script')) {
|
|
54393
|
+
return 'inline script';
|
|
54394
|
+
}
|
|
54395
|
+
if (d.includes('style')) {
|
|
54396
|
+
return 'inline style';
|
|
54397
|
+
}
|
|
54398
|
+
}
|
|
54399
|
+
if (blockedURI === 'eval') {
|
|
54400
|
+
return 'eval script execution';
|
|
54401
|
+
}
|
|
54402
|
+
if (d.includes('worker'))
|
|
54403
|
+
return 'worker';
|
|
54404
|
+
if (d.includes('script')) {
|
|
54405
|
+
return d.includes('elem') ? 'script element' : 'script';
|
|
54406
|
+
}
|
|
54407
|
+
if (d.includes('style')) {
|
|
54408
|
+
return d.includes('elem') ? 'style element' : 'stylesheet';
|
|
54409
|
+
}
|
|
54410
|
+
if (d.includes('img'))
|
|
54411
|
+
return 'image';
|
|
54412
|
+
if (d.includes('font'))
|
|
54413
|
+
return 'font';
|
|
54414
|
+
if (d.includes('connect'))
|
|
54415
|
+
return 'network request';
|
|
54416
|
+
if (d.includes('media'))
|
|
54417
|
+
return 'media resource';
|
|
54418
|
+
if (d.includes('frame-ancestors'))
|
|
54419
|
+
return 'display of your page in a frame';
|
|
54420
|
+
if (d.includes('frame'))
|
|
54421
|
+
return 'frame';
|
|
54422
|
+
if (d.includes('manifest'))
|
|
54423
|
+
return 'manifest';
|
|
54424
|
+
if (d.includes('base-uri'))
|
|
54425
|
+
return 'base URI';
|
|
54426
|
+
if (d.includes('form-action'))
|
|
54427
|
+
return 'form submission';
|
|
54428
|
+
return 'resource';
|
|
54429
|
+
}
|
|
54430
|
+
/**
|
|
54431
|
+
* Finds a specific directive in a CSP policy string and returns it with its value.
|
|
54432
|
+
*
|
|
54433
|
+
* @param {string} policy - The complete original CSP policy string (semicolon-separated directives)
|
|
54434
|
+
* @param {string} directiveName - The name of the directive to find (e.g., 'script-src', 'style-src')
|
|
54435
|
+
* @returns {string} The complete directive with its value if found, empty string otherwise
|
|
54436
|
+
*
|
|
54437
|
+
* @example
|
|
54438
|
+
* getDirective('script-src \'self\'; style-src \'self\'', 'script-src') // returns 'script-src \'self\''
|
|
54439
|
+
*/
|
|
54440
|
+
function getDirective(policy, directiveName) {
|
|
54441
|
+
if (!policy || !directiveName)
|
|
54442
|
+
return '';
|
|
54443
|
+
const needle = directiveName.toLowerCase();
|
|
54444
|
+
for (const directive of policy.split(';')) {
|
|
54445
|
+
const trimmed = directive.trim();
|
|
54446
|
+
const lower = trimmed.toLowerCase();
|
|
54447
|
+
if (lower === needle || lower.startsWith(`${needle} `)) {
|
|
54448
|
+
return trimmed;
|
|
54449
|
+
}
|
|
54450
|
+
}
|
|
54451
|
+
return '';
|
|
54452
|
+
}
|
|
54453
|
+
/**
|
|
54454
|
+
* Determines the appropriate article (A/An) for a given phrase based on its first letter.
|
|
54455
|
+
*
|
|
54456
|
+
* @param {string} phrase - The phrase to determine the article for
|
|
54457
|
+
* @returns {string} Either 'A' or 'An' based on whether the phrase starts with a vowel sound
|
|
54458
|
+
*
|
|
54459
|
+
* @example
|
|
54460
|
+
* getArticle('script') // returns 'A'
|
|
54461
|
+
* getArticle('image') // returns 'An'
|
|
54462
|
+
* getArticle('stylesheet') // returns 'A'
|
|
54463
|
+
* getArticle('') // returns 'A' (fallback for empty string)
|
|
54464
|
+
* getArticle(undefined) // returns 'A' (fallback for undefined)
|
|
54465
|
+
*/
|
|
54466
|
+
function getArticle(phrase) {
|
|
54467
|
+
if (!phrase || typeof phrase !== 'string')
|
|
54468
|
+
return 'A';
|
|
54469
|
+
const c = phrase.trim().charAt(0).toLowerCase();
|
|
54470
|
+
return 'aeiou'.includes(c) ? 'An' : 'A';
|
|
54471
|
+
}
|
|
54472
|
+
/**
|
|
54473
|
+
* Returns the original blocked URI when it looks like a URL or scheme,
|
|
54474
|
+
* otherwise returns an empty string for special cases.
|
|
54475
|
+
*
|
|
54476
|
+
* @param {string} blockedURI - The URI that was blocked (can be 'inline', 'eval', or a URL/scheme)
|
|
54477
|
+
* @returns {string} The blocked URI or an empty string for special cases like 'inline' or 'eval'
|
|
54478
|
+
*
|
|
54479
|
+
* @example
|
|
54480
|
+
* formatBlockedUri('https://example.com/script.js') // returns 'https://example.com/script.js'
|
|
54481
|
+
* formatBlockedUri('inline') // returns ''
|
|
54482
|
+
* formatBlockedUri('blob') // returns 'blob'
|
|
54483
|
+
*/
|
|
54484
|
+
function formatBlockedUri(blockedURI) {
|
|
54485
|
+
if (!blockedURI || blockedURI === 'inline' || blockedURI === 'eval')
|
|
54486
|
+
return '';
|
|
54487
|
+
return blockedURI;
|
|
54488
|
+
}
|
|
54489
|
+
/**
|
|
54490
|
+
* Formats a CSP violation message for console output.
|
|
54491
|
+
*
|
|
54492
|
+
* @param {string} blockedURI - The URI that was blocked by the CSP policy (can be 'inline', 'eval', or a URL)
|
|
54493
|
+
* @param {string} directive - The CSP directive that caused the violation (e.g., 'script-src', 'frame-ancestors')
|
|
54494
|
+
* @param {boolean} isReportOnly - Whether this is a report-only policy violation (adds " (Report-Only)" to message)
|
|
54495
|
+
* @param {string} originalPolicy - The complete CSP policy that was violated
|
|
54496
|
+
* @returns {string} A formatted readable CSP violation message
|
|
54497
|
+
*
|
|
54498
|
+
* @example
|
|
54499
|
+
* createCspViolationMessage(
|
|
54500
|
+
* 'https://example.com/script.js',
|
|
54501
|
+
* 'script-src',
|
|
54502
|
+
* false,
|
|
54503
|
+
* 'script-src \'self\'; style-src \'self\''
|
|
54504
|
+
* )
|
|
54505
|
+
* // returns: "Content Security Policy: A script from https://example.com/script.js was blocked by your site's `script-src 'self'` policy.\nCurrent CSP: \"script-src 'self'; style-src 'self'\"."
|
|
54506
|
+
*/
|
|
54507
|
+
function createCspViolationMessage(blockedURI, directive, isReportOnly, originalPolicy) {
|
|
54508
|
+
if (!directive || typeof directive !== 'string') {
|
|
54509
|
+
return 'Content Security Policy: Unknown violation occurred.';
|
|
54510
|
+
}
|
|
54511
|
+
try {
|
|
54512
|
+
const reportOnlyText = isReportOnly ? ' (Report-Only)' : '';
|
|
54513
|
+
// special case for frame-ancestors since it doesn't fit our template at all
|
|
54514
|
+
if ((directive === null || directive === void 0 ? void 0 : directive.toLowerCase()) === 'frame-ancestors') {
|
|
54515
|
+
return `Content Security Policy${reportOnlyText}: The display of ${blockedURI} in a frame was blocked because an ancestor violates your site's frame-ancestors policy.\nCurrent CSP: "${originalPolicy}".`;
|
|
54516
|
+
}
|
|
54517
|
+
const resourceType = getResourceType(blockedURI, directive);
|
|
54518
|
+
const article = getArticle(resourceType);
|
|
54519
|
+
const source = formatBlockedUri(blockedURI);
|
|
54520
|
+
const directiveValue = getDirective(originalPolicy, directive);
|
|
54521
|
+
const policyDisplay = directiveValue || directive;
|
|
54522
|
+
const resourceDescription = `${article} ${resourceType}${source ? ` from ${source}` : ''}`;
|
|
54523
|
+
return `Content Security Policy${reportOnlyText}: ${resourceDescription} was blocked by your site's \`${policyDisplay}\` policy.\nCurrent CSP: "${originalPolicy}".`;
|
|
54524
|
+
}
|
|
54525
|
+
catch (error) {
|
|
54526
|
+
return `Content Security Policy: Error formatting violation message: ${error.message}`;
|
|
54527
|
+
}
|
|
54528
|
+
}
|
|
54529
|
+
|
|
54158
54530
|
const DEV_LOG_LEVELS = ['info', 'warn', 'error'];
|
|
54159
54531
|
const TOKEN_MAX_SIZE = 100;
|
|
54160
54532
|
const TOKEN_REFILL_RATE = 10;
|
|
@@ -56289,6 +56661,7 @@ function ConsoleCapture() {
|
|
|
56289
56661
|
onAppUnloaded,
|
|
56290
56662
|
onPtmPaused,
|
|
56291
56663
|
onPtmUnpaused,
|
|
56664
|
+
securityPolicyViolationFn,
|
|
56292
56665
|
get buffer() {
|
|
56293
56666
|
return buffer;
|
|
56294
56667
|
},
|
|
@@ -56317,7 +56690,7 @@ function ConsoleCapture() {
|
|
|
56317
56690
|
}
|
|
56318
56691
|
}, SEND_INTERVAL);
|
|
56319
56692
|
sendQueue.start();
|
|
56320
|
-
pluginAPI.Events.ready.on(
|
|
56693
|
+
pluginAPI.Events.ready.on(readyHandler);
|
|
56321
56694
|
pluginAPI.Events.appUnloaded.on(onAppUnloaded);
|
|
56322
56695
|
pluginAPI.Events.appHidden.on(onAppHidden);
|
|
56323
56696
|
pluginAPI.Events['ptm:paused'].on(onPtmPaused);
|
|
@@ -56352,6 +56725,10 @@ function ConsoleCapture() {
|
|
|
56352
56725
|
url: globalPendo.url.get()
|
|
56353
56726
|
};
|
|
56354
56727
|
}
|
|
56728
|
+
function readyHandler() {
|
|
56729
|
+
addIntercepts();
|
|
56730
|
+
pluginAPI.attachEventInternal(window, 'securitypolicyviolation', securityPolicyViolationFn);
|
|
56731
|
+
}
|
|
56355
56732
|
function addIntercepts() {
|
|
56356
56733
|
_.each(CONSOLE_METHODS, function (methodName) {
|
|
56357
56734
|
const originalMethod = console[methodName];
|
|
@@ -56368,7 +56745,21 @@ function ConsoleCapture() {
|
|
|
56368
56745
|
};
|
|
56369
56746
|
});
|
|
56370
56747
|
}
|
|
56371
|
-
function
|
|
56748
|
+
function securityPolicyViolationFn(evt) {
|
|
56749
|
+
if (!evt || typeof evt !== 'object')
|
|
56750
|
+
return;
|
|
56751
|
+
const { blockedURI, violatedDirective, effectiveDirective, disposition, originalPolicy } = evt;
|
|
56752
|
+
const directive = violatedDirective || effectiveDirective;
|
|
56753
|
+
const isReportOnly = disposition === 'report';
|
|
56754
|
+
const message = createCspViolationMessage(blockedURI, directive, isReportOnly, originalPolicy);
|
|
56755
|
+
if (isReportOnly) {
|
|
56756
|
+
createConsoleEvent([message], 'warn', { skipStackTrace: true, skipScrubPII: true });
|
|
56757
|
+
}
|
|
56758
|
+
else {
|
|
56759
|
+
createConsoleEvent([message], 'error', { skipStackTrace: true, skipScrubPII: true });
|
|
56760
|
+
}
|
|
56761
|
+
}
|
|
56762
|
+
function createConsoleEvent(args, methodName, { skipStackTrace = false, skipScrubPII = false } = {}) {
|
|
56372
56763
|
if (!args || args.length === 0)
|
|
56373
56764
|
return;
|
|
56374
56765
|
// stringify args
|
|
@@ -56385,8 +56776,11 @@ function ConsoleCapture() {
|
|
|
56385
56776
|
if (!message)
|
|
56386
56777
|
return;
|
|
56387
56778
|
// capture stack trace
|
|
56388
|
-
|
|
56389
|
-
|
|
56779
|
+
let stackTrace = '';
|
|
56780
|
+
if (!skipStackTrace) {
|
|
56781
|
+
const maxStackFrames = methodName === 'error' ? 4 : 1;
|
|
56782
|
+
stackTrace = captureStackTrace(maxStackFrames);
|
|
56783
|
+
}
|
|
56390
56784
|
// truncate message and stack trace
|
|
56391
56785
|
message = truncate(message);
|
|
56392
56786
|
stackTrace = truncate(stackTrace);
|
|
@@ -56397,7 +56791,7 @@ function ConsoleCapture() {
|
|
|
56397
56791
|
return;
|
|
56398
56792
|
}
|
|
56399
56793
|
const devLogEnvelope = createDevLogEnvelope();
|
|
56400
|
-
const consoleEvent = Object.assign(Object.assign({}, devLogEnvelope), { devLogLevel: methodName === 'log' ? 'info' : methodName, devLogMessage: scrubPII(message), devLogTrace: stackTrace, devLogCount: 1 });
|
|
56794
|
+
const consoleEvent = Object.assign(Object.assign({}, devLogEnvelope), { devLogLevel: methodName === 'log' ? 'info' : methodName, devLogMessage: skipScrubPII ? message : scrubPII(message), devLogTrace: stackTrace, devLogCount: 1 });
|
|
56401
56795
|
const wasAccepted = buffer.push(consoleEvent);
|
|
56402
56796
|
if (wasAccepted) {
|
|
56403
56797
|
if (!isPtmPaused) {
|
|
@@ -56459,6 +56853,7 @@ function ConsoleCapture() {
|
|
|
56459
56853
|
pluginAPI.Events.appUnloaded.off(onAppUnloaded);
|
|
56460
56854
|
pluginAPI.Events['ptm:paused'].off(onPtmPaused);
|
|
56461
56855
|
pluginAPI.Events['ptm:unpaused'].off(onPtmUnpaused);
|
|
56856
|
+
pluginAPI.detachEventInternal(window, 'securitypolicyviolation', securityPolicyViolationFn);
|
|
56462
56857
|
_.each(CONSOLE_METHODS, function (methodName) {
|
|
56463
56858
|
if (!console[methodName])
|
|
56464
56859
|
return _.noop;
|