@pendo/agent 2.337.3 → 2.338.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dom.esm.js +20 -22
- package/dist/pendo.debugger.min.js +13 -8
- package/dist/pendo.module.js +224 -76
- package/dist/pendo.module.min.js +10 -10
- package/dist/pendo.preview.min.js +5 -5
- package/dist/replay.worker.min.js +1 -1
- package/dist/servers.json +8 -8
- package/package.json +1 -1
package/dist/pendo.module.js
CHANGED
|
@@ -430,6 +430,12 @@ function getPolicy(pendo) {
|
|
|
430
430
|
return policy;
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
+
function escapeRegExp(string) {
|
|
434
|
+
if (!string)
|
|
435
|
+
return;
|
|
436
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
437
|
+
}
|
|
438
|
+
|
|
433
439
|
var STAGING_SERVER_HASHES = 'stagingServerHashes';
|
|
434
440
|
var STAGING_DOMAINS = 'stagingDomains';
|
|
435
441
|
var pendo$1;
|
|
@@ -515,7 +521,9 @@ function urlMatchesPatterns(url, patterns = [], hashes = []) {
|
|
|
515
521
|
}
|
|
516
522
|
}
|
|
517
523
|
if (typeof pattern === 'string') {
|
|
518
|
-
|
|
524
|
+
// String entries are literal origins/hosts, not patterns. Escape
|
|
525
|
+
// them so a '.' can't act as a wildcard and match a look-alike domain.
|
|
526
|
+
pattern = new RegExp('^' + escapeRegExp(pattern) + '$');
|
|
519
527
|
}
|
|
520
528
|
if (pattern instanceof RegExp && pattern.test(url)) {
|
|
521
529
|
return true;
|
|
@@ -649,10 +657,13 @@ function getPendoConfigValue(key) {
|
|
|
649
657
|
}
|
|
650
658
|
|
|
651
659
|
// Resolve the global object without relying on a bare `window` identifier.
|
|
652
|
-
// `window` is only a bound global in a normal browser realm;
|
|
653
|
-
//
|
|
654
|
-
//
|
|
655
|
-
//
|
|
660
|
+
// `window` is only a bound global in a normal browser realm; the compat builds
|
|
661
|
+
// bind it via their `(function(window, document){...})` wrapper, but the npm
|
|
662
|
+
// module build has no wrapper, so its module scope can be evaluated in realms
|
|
663
|
+
// where `window` is undeclared (a Web Worker, or a customer bundler re-chunking
|
|
664
|
+
// the module into worker-reachable code) and a bare reference throws a
|
|
665
|
+
// ReferenceError at module-load. `globalThis` resolves in every realm; the
|
|
666
|
+
// `self`/`window` fallbacks cover engines that predate it. APP-160669, APP-165809.
|
|
656
667
|
function getGlobalScope() {
|
|
657
668
|
if (typeof globalThis !== 'undefined') {
|
|
658
669
|
return globalThis;
|
|
@@ -3916,7 +3927,13 @@ function isMobileUserAgent() {
|
|
|
3916
3927
|
return (/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i).test(getUA());
|
|
3917
3928
|
}
|
|
3918
3929
|
function isSafari(userAgent = getUA()) {
|
|
3919
|
-
return userAgent.indexOf('Safari') >= 0 && !userAgent
|
|
3930
|
+
return userAgent.indexOf('Safari') >= 0 && !isChromium(userAgent);
|
|
3931
|
+
}
|
|
3932
|
+
// Every Chromium UA carries a trailing Safari/ token, so Chrome, Edge, Opera,
|
|
3933
|
+
// Brave and Electron all have to be excluded from isSafari(). Chromium builds
|
|
3934
|
+
// report Chromium/ without a Chrome/ token, hence both checks.
|
|
3935
|
+
function isChromium(userAgent = getUA()) {
|
|
3936
|
+
return userAgent.indexOf('Chrome') >= 0 || userAgent.indexOf('Chromium') >= 0;
|
|
3920
3937
|
}
|
|
3921
3938
|
const sniffer = {
|
|
3922
3939
|
supportsHistoryApi: shouldWrapNativeHistory,
|
|
@@ -4029,8 +4046,8 @@ let SERVER = '';
|
|
|
4029
4046
|
let ASSET_HOST = '';
|
|
4030
4047
|
let ASSET_PATH = '';
|
|
4031
4048
|
let DESIGNER_SERVER = '';
|
|
4032
|
-
let VERSION = '2.
|
|
4033
|
-
let PACKAGE_VERSION = '2.
|
|
4049
|
+
let VERSION = '2.338.2_';
|
|
4050
|
+
let PACKAGE_VERSION = '2.338.2';
|
|
4034
4051
|
let LOADER = 'xhr';
|
|
4035
4052
|
/* eslint-enable web-sdk-eslint-rules/no-gulp-env-references */
|
|
4036
4053
|
/**
|
|
@@ -4335,9 +4352,9 @@ function unwrapFirefoxObject(obj) {
|
|
|
4335
4352
|
return obj;
|
|
4336
4353
|
}
|
|
4337
4354
|
|
|
4338
|
-
var decodeURIComponent = _.isFunction(
|
|
4355
|
+
var decodeURIComponent = _.isFunction(globalScope.decodeURIComponent) ? globalScope.decodeURIComponent : _.identity;
|
|
4339
4356
|
|
|
4340
|
-
var encodeURIComponent = _.isFunction(
|
|
4357
|
+
var encodeURIComponent = _.isFunction(globalScope.encodeURIComponent) ? globalScope.encodeURIComponent : _.identity;
|
|
4341
4358
|
|
|
4342
4359
|
class EventTarget {
|
|
4343
4360
|
constructor() {
|
|
@@ -4447,12 +4464,6 @@ class LogEmitter extends EventTarget {
|
|
|
4447
4464
|
}
|
|
4448
4465
|
const log = new LogEmitter();
|
|
4449
4466
|
|
|
4450
|
-
function escapeRegExp(string) {
|
|
4451
|
-
if (!string)
|
|
4452
|
-
return;
|
|
4453
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
4454
|
-
}
|
|
4455
|
-
|
|
4456
4467
|
const allowKeys = function () {
|
|
4457
4468
|
var allowKeys = ConfigReader.get('storage.allowKeys') || '*';
|
|
4458
4469
|
return _.isArray(allowKeys) ? _.indexBy(allowKeys) : allowKeys;
|
|
@@ -4919,18 +4930,6 @@ var agentStorage = (function () {
|
|
|
4919
4930
|
};
|
|
4920
4931
|
})();
|
|
4921
4932
|
|
|
4922
|
-
/*
|
|
4923
|
-
* @license MIT - https://github.com/taylorhakes/promise-polyfill
|
|
4924
|
-
*/
|
|
4925
|
-
// Modified to not affect global environment: the `var` keeps the binding module-local
|
|
4926
|
-
// so the polyfill never assigns to the real global `Promise`.
|
|
4927
|
-
//
|
|
4928
|
-
// Main-thread only. Reading `window` here is safe because rollup's `inject` is skipped
|
|
4929
|
-
// for the nested worker build (see `skipPlugins` on webWorkerLoader in
|
|
4930
|
-
// rollup/web-sdk.config.mjs): a worker realm has a native Promise and no `window`, so
|
|
4931
|
-
// injecting this polyfill there was both unreachable and fatal — it threw
|
|
4932
|
-
// `ReferenceError: window is not defined` at worker startup and stopped session replay
|
|
4933
|
-
// with a WORKER_ERROR. APP-163474.
|
|
4934
4933
|
var Promise$1;
|
|
4935
4934
|
|
|
4936
4935
|
Promise$1 = (function (global, factory) {
|
|
@@ -4940,7 +4939,7 @@ Promise$1 = (function (global, factory) {
|
|
|
4940
4939
|
}
|
|
4941
4940
|
|
|
4942
4941
|
return nativePromise;
|
|
4943
|
-
}(
|
|
4942
|
+
}(globalScope, (function () {
|
|
4944
4943
|
/**
|
|
4945
4944
|
* @this {Promise}
|
|
4946
4945
|
*/
|
|
@@ -6269,6 +6268,7 @@ function wasSessionCleared(evt) {
|
|
|
6269
6268
|
//
|
|
6270
6269
|
// NOTE: This, for now, at least is identified as an instanceof HTMLAnchorElement
|
|
6271
6270
|
|
|
6271
|
+
|
|
6272
6272
|
function URLPolyfillFactory(doc) {
|
|
6273
6273
|
return function URL(str) {
|
|
6274
6274
|
var a = doc.createElement('a');
|
|
@@ -6300,11 +6300,11 @@ function URLPolyfillFactory(doc) {
|
|
|
6300
6300
|
var URL = (function(global, factory) {
|
|
6301
6301
|
var nativeURL = global.URL;
|
|
6302
6302
|
if (typeof nativeURL !== 'function' || !/native/.test(nativeURL)) {
|
|
6303
|
-
return factory(document);
|
|
6303
|
+
return factory(global.document);
|
|
6304
6304
|
}
|
|
6305
6305
|
|
|
6306
6306
|
return nativeURL;
|
|
6307
|
-
})(
|
|
6307
|
+
})(globalScope, URLPolyfillFactory);
|
|
6308
6308
|
|
|
6309
6309
|
function hasComposedPath(evt) {
|
|
6310
6310
|
return evt && typeof evt.composedPath === 'function';
|
|
@@ -10079,6 +10079,26 @@ var writeMessage = function (msg) {
|
|
|
10079
10079
|
});
|
|
10080
10080
|
return q.resolve(writeImgTag(url)).catch(_.noop);
|
|
10081
10081
|
};
|
|
10082
|
+
/**
|
|
10083
|
+
* Releases a fetch response body we are never going to read. An undrained body
|
|
10084
|
+
* holds its stream, the underlying data pipe and the socket open until the
|
|
10085
|
+
* {Response} is collected, which may never happen in a long-lived embedded
|
|
10086
|
+
* renderer. Cancelling does not affect keepalive delivery.
|
|
10087
|
+
*
|
|
10088
|
+
* @access private
|
|
10089
|
+
* @param {Response} response to discard the body of
|
|
10090
|
+
* @returns {Response} the same response, for chaining
|
|
10091
|
+
*/
|
|
10092
|
+
function cancelResponseBody(response) {
|
|
10093
|
+
try {
|
|
10094
|
+
if (response && response.body && _.isFunction(response.body.cancel)) {
|
|
10095
|
+
response.body.cancel().catch(_.noop);
|
|
10096
|
+
}
|
|
10097
|
+
}
|
|
10098
|
+
catch (e) {
|
|
10099
|
+
}
|
|
10100
|
+
return response;
|
|
10101
|
+
}
|
|
10082
10102
|
/**
|
|
10083
10103
|
* @access private
|
|
10084
10104
|
* @param {string} msg to send
|
|
@@ -10107,7 +10127,9 @@ function writeErrorPOST(msg) {
|
|
|
10107
10127
|
else {
|
|
10108
10128
|
promise = ajax.postJSON(url, errorData);
|
|
10109
10129
|
}
|
|
10110
|
-
return promise.then(function () {
|
|
10130
|
+
return promise.then(function (response) {
|
|
10131
|
+
cancelResponseBody(response);
|
|
10132
|
+
}, function (err) { log.debug('error writing error:' + err); });
|
|
10111
10133
|
}
|
|
10112
10134
|
catch (e) {
|
|
10113
10135
|
log.info('Failed to write error to server using POST endpoint: ' + e);
|
|
@@ -10129,6 +10151,10 @@ function writeMetricsPOST(payload) {
|
|
|
10129
10151
|
keepalive: true,
|
|
10130
10152
|
body: JSON.stringify(payload),
|
|
10131
10153
|
headers: { 'Content-Type': 'application/json' }
|
|
10154
|
+
})
|
|
10155
|
+
.then(cancelResponseBody)
|
|
10156
|
+
.catch(function (err) {
|
|
10157
|
+
log.debug(`Failed to write metrics payload to server: ${err}`);
|
|
10132
10158
|
});
|
|
10133
10159
|
}
|
|
10134
10160
|
else {
|
|
@@ -10156,13 +10182,23 @@ function writeImgTag(src) {
|
|
|
10156
10182
|
image.src = src;
|
|
10157
10183
|
return deferred.promise;
|
|
10158
10184
|
}
|
|
10185
|
+
/**
|
|
10186
|
+
* Sends a fire-and-forget beacon request. The response body is cancelled before
|
|
10187
|
+
* the promise resolves, so callers get {Response.status}, {Response.ok} and the
|
|
10188
|
+
* headers but cannot read the body. Use {fetch} directly if you need to read one.
|
|
10189
|
+
*
|
|
10190
|
+
* @access private
|
|
10191
|
+
* @param {string} url to request
|
|
10192
|
+
* @param {Object} options passed to {fetch}, defaulting to a keepalive GET
|
|
10193
|
+
* @returns {Promise<Response>} with its body already cancelled
|
|
10194
|
+
*/
|
|
10159
10195
|
function fetchKeepalive(url, options = {}) {
|
|
10160
10196
|
const defaults = {
|
|
10161
10197
|
method: 'GET',
|
|
10162
10198
|
keepalive: true
|
|
10163
10199
|
};
|
|
10164
10200
|
const fetchOptions = _.extend({}, defaults, options);
|
|
10165
|
-
return fetch(url, fetchOptions);
|
|
10201
|
+
return fetch(url, fetchOptions).then(cancelResponseBody);
|
|
10166
10202
|
}
|
|
10167
10203
|
fetchKeepalive.supported = function () {
|
|
10168
10204
|
try {
|
|
@@ -11614,7 +11650,7 @@ var WeakRef = (function(global, factory) {
|
|
|
11614
11650
|
}
|
|
11615
11651
|
|
|
11616
11652
|
return nativeWeakRef;
|
|
11617
|
-
})(
|
|
11653
|
+
})(globalScope, WeakRefFactory);
|
|
11618
11654
|
|
|
11619
11655
|
const trimString = (str, limit) => {
|
|
11620
11656
|
const len = str.length;
|
|
@@ -11788,6 +11824,13 @@ _.extend(dom, {
|
|
|
11788
11824
|
});
|
|
11789
11825
|
_.extend(dom.prototype, DomEvent.$, DomQuery.$);
|
|
11790
11826
|
|
|
11827
|
+
// Domains that Pendo owns, will be swapped in by build patches
|
|
11828
|
+
const pendoDomainPatterns = [
|
|
11829
|
+
/^https:\/\/((adopt|oem)\.)?((us1)\.)?(app|via|adopt|cdn|oem|novus-api|novus)(\.(au|eu|gov|hsbc|jpn|tv))?\.pendo\.io$/,
|
|
11830
|
+
/^https:\/\/([0-9]{8}t[0-9]{4}-dot-)(pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)\.appspot\.com$/,
|
|
11831
|
+
/^https:\/\/hotfix-(ops|app)-([0-9]+-dot-)(pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)\.appspot\.com$/,
|
|
11832
|
+
/^https:\/\/(pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)-static\.storage\.googleapis\.com$/
|
|
11833
|
+
]; // eslint-disable-line web-sdk-eslint-rules/no-gulp-env-references
|
|
11791
11834
|
function isTrustedOrigin(host) {
|
|
11792
11835
|
if (!host)
|
|
11793
11836
|
return false;
|
|
@@ -11801,13 +11844,6 @@ function isTrustedOrigin(host) {
|
|
|
11801
11844
|
return true;
|
|
11802
11845
|
if (host === getContentHost())
|
|
11803
11846
|
return true;
|
|
11804
|
-
// Domains that Pendo owns, will be swapped in by build patches
|
|
11805
|
-
const patterns = [
|
|
11806
|
-
/^https:\/\/((adopt|oem)\.)?((us1)\.)?(app|via|adopt|cdn|oem|novus-api|novus)(\.(au|eu|gov|hsbc|jpn|tv))?\.pendo\.io$/,
|
|
11807
|
-
/^https:\/\/([0-9]{8}t[0-9]{4}-dot-)(pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)\.appspot\.com$/,
|
|
11808
|
-
/^https:\/\/hotfix-(ops|app)-([0-9]+-dot-)(pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)\.appspot\.com$/,
|
|
11809
|
-
/^https:\/\/(pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)-static\.storage\.googleapis\.com$/
|
|
11810
|
-
]; // eslint-disable-line web-sdk-eslint-rules/no-gulp-env-references
|
|
11811
11847
|
var adoptHost = ConfigReader.get('adoptHost');
|
|
11812
11848
|
if (adoptHost) {
|
|
11813
11849
|
var fullHostname = 'https://' + adoptHost;
|
|
@@ -11817,10 +11853,17 @@ function isTrustedOrigin(host) {
|
|
|
11817
11853
|
}
|
|
11818
11854
|
if (urlMatchesPatterns(host, ConfigReader.get('allowedOriginServers', []), ConfigReader.get('allowedOriginServerHashes', [])))
|
|
11819
11855
|
return true;
|
|
11820
|
-
return _.any(
|
|
11856
|
+
return _.any(pendoDomainPatterns, function (pattern) {
|
|
11821
11857
|
return pattern.test(host);
|
|
11822
11858
|
});
|
|
11823
11859
|
}
|
|
11860
|
+
function isPendoDomain(origin) {
|
|
11861
|
+
if (!origin)
|
|
11862
|
+
return false;
|
|
11863
|
+
return _.any(pendoDomainPatterns, function (pattern) {
|
|
11864
|
+
return pattern.test(origin);
|
|
11865
|
+
});
|
|
11866
|
+
}
|
|
11824
11867
|
function hasAllowedOriginServers() {
|
|
11825
11868
|
const allowedOriginServers = ConfigReader.get('allowedOriginServers', []);
|
|
11826
11869
|
return allowedOriginServers && allowedOriginServers.length > 0;
|
|
@@ -13158,6 +13201,7 @@ function sendWithImg(apiKey, silo) {
|
|
|
13158
13201
|
return writeImgTag(buildGetRequestUrl(apiKey, silo));
|
|
13159
13202
|
}
|
|
13160
13203
|
function handleFetchResponse(response) {
|
|
13204
|
+
cancelResponseBody(response);
|
|
13161
13205
|
if (response.status < 200 || response.status >= 300) {
|
|
13162
13206
|
return q.reject(new Error(`received status code ${response.status}: ${response.statusText}`));
|
|
13163
13207
|
}
|
|
@@ -26801,10 +26845,23 @@ var DesignerV2 = (function () {
|
|
|
26801
26845
|
}
|
|
26802
26846
|
return validTarget;
|
|
26803
26847
|
}
|
|
26848
|
+
function hasSameOriginOpener() {
|
|
26849
|
+
if (!window.opener)
|
|
26850
|
+
return false;
|
|
26851
|
+
try {
|
|
26852
|
+
return window.opener.location.origin === window.location.origin;
|
|
26853
|
+
}
|
|
26854
|
+
catch (e) {
|
|
26855
|
+
return false;
|
|
26856
|
+
}
|
|
26857
|
+
}
|
|
26804
26858
|
function launchOnToken(url) {
|
|
26805
26859
|
if (isDesignerFrame(window.name)) {
|
|
26806
26860
|
return;
|
|
26807
26861
|
}
|
|
26862
|
+
if (isPendoDomain(window.location.origin) && !hasSameOriginOpener()) {
|
|
26863
|
+
return;
|
|
26864
|
+
}
|
|
26808
26865
|
var tokenParamTest = /pendo-designer=([A-Za-z0-9-]+)/;
|
|
26809
26866
|
if (!tokenParamTest.test(url))
|
|
26810
26867
|
return;
|
|
@@ -26817,7 +26874,7 @@ var DesignerV2 = (function () {
|
|
|
26817
26874
|
// clear stored designer state if we're launching from a token
|
|
26818
26875
|
window.localStorage.removeItem('pendo-navigation-state');
|
|
26819
26876
|
window.localStorage.removeItem('pendo-designer-mode');
|
|
26820
|
-
launchInAppDesigner({
|
|
26877
|
+
DesignerV2.launchInAppDesigner({
|
|
26821
26878
|
target: validTarget,
|
|
26822
26879
|
host: validAssetHost,
|
|
26823
26880
|
preloader: false,
|
|
@@ -29734,7 +29791,11 @@ var BuildingBlockGuides = (function () {
|
|
|
29734
29791
|
// position and calculate its height before making it visible.
|
|
29735
29792
|
// In the designer, changing visibility like this causes the guide to "flicker"
|
|
29736
29793
|
// between re-renders
|
|
29737
|
-
|
|
29794
|
+
// Only hide the first match. Resource center module previews nest the module's
|
|
29795
|
+
// pendo-g- inside the home view's pendo-g- under the same id (via
|
|
29796
|
+
// correctGuideContainerId), and imageNodeHandler only un-hides the first match,
|
|
29797
|
+
// so hiding every match left the module content hidden forever.
|
|
29798
|
+
dom(guideContainer[0]).css({ visibility: 'hidden' });
|
|
29738
29799
|
guideToAppend.css({ visibility: 'hidden' });
|
|
29739
29800
|
const embedConfig = _.get(guide, 'attributes.embedConfig', null);
|
|
29740
29801
|
var watermark = BuildingBlockWatermark.buildWatermark({
|
|
@@ -42841,10 +42902,13 @@ function GuideMarkdown() {
|
|
|
42841
42902
|
}
|
|
42842
42903
|
|
|
42843
42904
|
// Resolve the global object without relying on a bare `window` identifier.
|
|
42844
|
-
// `window` is only a bound global in a normal browser realm;
|
|
42845
|
-
//
|
|
42846
|
-
//
|
|
42847
|
-
//
|
|
42905
|
+
// `window` is only a bound global in a normal browser realm; the compat builds
|
|
42906
|
+
// bind it via their `(function(window, document){...})` wrapper, but the npm
|
|
42907
|
+
// module build has no wrapper, so its module scope can be evaluated in realms
|
|
42908
|
+
// where `window` is undeclared (a Web Worker, or a customer bundler re-chunking
|
|
42909
|
+
// the module into worker-reachable code) and a bare reference throws a
|
|
42910
|
+
// ReferenceError at module-load. `globalThis` resolves in every realm; the
|
|
42911
|
+
// `self`/`window` fallbacks cover engines that predate it. APP-160669, APP-165809.
|
|
42848
42912
|
function getGlobalScope() {
|
|
42849
42913
|
if (typeof globalThis !== 'undefined') {
|
|
42850
42914
|
return globalThis;
|
|
@@ -42892,6 +42956,7 @@ var setTimeout$1 = setTimeout;
|
|
|
42892
42956
|
//
|
|
42893
42957
|
// NOTE: This, for now, at least is identified as an instanceof HTMLAnchorElement
|
|
42894
42958
|
|
|
42959
|
+
|
|
42895
42960
|
function URLPolyfillFactory(doc) {
|
|
42896
42961
|
return function URL(str) {
|
|
42897
42962
|
var a = doc.createElement('a');
|
|
@@ -42923,24 +42988,12 @@ function URLPolyfillFactory(doc) {
|
|
|
42923
42988
|
var URL$1 = (function(global, factory) {
|
|
42924
42989
|
var nativeURL = global.URL;
|
|
42925
42990
|
if (typeof nativeURL !== 'function' || !/native/.test(nativeURL)) {
|
|
42926
|
-
return factory(document);
|
|
42991
|
+
return factory(global.document);
|
|
42927
42992
|
}
|
|
42928
42993
|
|
|
42929
42994
|
return nativeURL;
|
|
42930
|
-
})(
|
|
42995
|
+
})(globalScope, URLPolyfillFactory);
|
|
42931
42996
|
|
|
42932
|
-
/*
|
|
42933
|
-
* @license MIT - https://github.com/taylorhakes/promise-polyfill
|
|
42934
|
-
*/
|
|
42935
|
-
// Modified to not affect global environment: the `var` keeps the binding module-local
|
|
42936
|
-
// so the polyfill never assigns to the real global `Promise`.
|
|
42937
|
-
//
|
|
42938
|
-
// Main-thread only. Reading `window` here is safe because rollup's `inject` is skipped
|
|
42939
|
-
// for the nested worker build (see `skipPlugins` on webWorkerLoader in
|
|
42940
|
-
// rollup/web-sdk.config.mjs): a worker realm has a native Promise and no `window`, so
|
|
42941
|
-
// injecting this polyfill there was both unreachable and fatal — it threw
|
|
42942
|
-
// `ReferenceError: window is not defined` at worker startup and stopped session replay
|
|
42943
|
-
// with a WORKER_ERROR. APP-163474.
|
|
42944
42997
|
var Promise$1;
|
|
42945
42998
|
|
|
42946
42999
|
Promise$1 = (function (global, factory) {
|
|
@@ -42950,7 +43003,7 @@ Promise$1 = (function (global, factory) {
|
|
|
42950
43003
|
}
|
|
42951
43004
|
|
|
42952
43005
|
return nativePromise;
|
|
42953
|
-
}(
|
|
43006
|
+
}(globalScope, (function () {
|
|
42954
43007
|
/**
|
|
42955
43008
|
* @this {Promise}
|
|
42956
43009
|
*/
|
|
@@ -50694,6 +50747,7 @@ const RECORDING_CONFIG_ON_RECORDING_STOP = 'recording.onRecordingStop';
|
|
|
50694
50747
|
const RECORDING_CONFIG_WORKER_OVERRIDE = 'recording.workerOverride';
|
|
50695
50748
|
const RECORDING_CONFIG_TREAT_IFRAME_AS_ROOT = 'recording.treatIframeAsRoot';
|
|
50696
50749
|
const RECORDING_CONFIG_DISABLE_UNLOAD = 'recording.disableUnload';
|
|
50750
|
+
const RECORDING_CONFIG_RECORD_CROSS_ORIGIN_IFRAMES = 'recording.recordCrossOriginIframes';
|
|
50697
50751
|
const RESOURCE_CACHING = 'resourceCaching';
|
|
50698
50752
|
const ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;
|
|
50699
50753
|
const ONE_MINUTE_IN_MILLISECONDS = 60 * 1000;
|
|
@@ -50805,6 +50859,26 @@ class SessionRecorder {
|
|
|
50805
50859
|
* @type {boolean}
|
|
50806
50860
|
*/
|
|
50807
50861
|
cf.addOption(RECORDING_CONFIG_TREAT_IFRAME_AS_ROOT, [cf.sources.SNIPPET_SRC], undefined);
|
|
50862
|
+
/**
|
|
50863
|
+
* Controls whether content embedded from another domain is included in replay. When enabled, the
|
|
50864
|
+
* embedded content shows up as part of the page. When disabled, it appears as a blank box.
|
|
50865
|
+
*
|
|
50866
|
+
* This only applies to embedded content that also has Pendo installed. Embeds from other companies
|
|
50867
|
+
* (Stripe, YouTube) are never captured either way — turning this off just stops Pendo from
|
|
50868
|
+
* messaging those frames at all.
|
|
50869
|
+
*
|
|
50870
|
+
* To capture embedded content as its own separate replay instead of part of the page it sits in,
|
|
50871
|
+
* see recording.treatIframeAsRoot.
|
|
50872
|
+
*
|
|
50873
|
+
* @access public
|
|
50874
|
+
* @category Config/Replay
|
|
50875
|
+
* @name recording.recordCrossOriginIframes
|
|
50876
|
+
* @default true
|
|
50877
|
+
* @type {boolean}
|
|
50878
|
+
*/
|
|
50879
|
+
// Like recording.enabled, this defaults to undefined so that it only overrides the
|
|
50880
|
+
// value returned by the backend when it is explicitly set to true/false
|
|
50881
|
+
cf.addOption(RECORDING_CONFIG_RECORD_CROSS_ORIGIN_IFRAMES, [cf.sources.SNIPPET_SRC], undefined);
|
|
50808
50882
|
cf.addOption(RESOURCE_CACHING, [cf.sources.PENDO_CONFIG_SRC], undefined);
|
|
50809
50883
|
}
|
|
50810
50884
|
initialize(pendo, PluginAPI) {
|
|
@@ -51024,6 +51098,9 @@ class SessionRecorder {
|
|
|
51024
51098
|
});
|
|
51025
51099
|
const blockedSelectors = ['.pendo-ignore', '.pendo-sr-ignore'].concat(recordingOptions.blockedSelectors || []);
|
|
51026
51100
|
const hiddenSelectors = ['.pendo-sr-hide'].concat(recordingOptions.hiddenSelectors || []);
|
|
51101
|
+
// The snippet config value should take precedence over the value from the visitor config
|
|
51102
|
+
const snippetValue = this.api.ConfigReader.get(RECORDING_CONFIG_RECORD_CROSS_ORIGIN_IFRAMES);
|
|
51103
|
+
const recordCrossOriginIframes = this.pendo._.isBoolean(snippetValue) ? snippetValue : recordingOptions.recordCrossOriginIframes;
|
|
51027
51104
|
const config = {
|
|
51028
51105
|
maskInputFn: this.pendo._.partial(maskInput, maskOptions),
|
|
51029
51106
|
maskTextFn: this.pendo._.partial(maskText, maskOptions),
|
|
@@ -51034,7 +51111,7 @@ class SessionRecorder {
|
|
|
51034
51111
|
// we need to concat hidden selectors to re-use block selector logic
|
|
51035
51112
|
blockSelector: blockedSelectors.concat(hiddenSelectors).join(','),
|
|
51036
51113
|
hideSelector: hiddenSelectors.join(','),
|
|
51037
|
-
recordCrossOriginIframes:
|
|
51114
|
+
recordCrossOriginIframes: recordCrossOriginIframes != null ? recordCrossOriginIframes : true,
|
|
51038
51115
|
emitFromIframe: !!this.api.ConfigReader.get(RECORDING_CONFIG_TREAT_IFRAME_AS_ROOT)
|
|
51039
51116
|
};
|
|
51040
51117
|
return config;
|
|
@@ -51790,6 +51867,20 @@ var WorkerFactory = /*#__PURE__*/createInlineWorkerFactory(/* rollup-plugin-web-
|
|
|
51790
51867
|
}
|
|
51791
51868
|
|
|
51792
51869
|
const ONE_HUNDRED_MB_IN_BYTES = 100 * 1024 * 1024;
|
|
51870
|
+
// An undrained response body holds its stream, the underlying data pipe and the
|
|
51871
|
+
// socket open until the response is collected, which in a worker that lives for
|
|
51872
|
+
// the whole session may never happen. Deliberately duplicated from
|
|
51873
|
+
// cancelResponseBody in src/transmit.js instead of imported: this is its own
|
|
51874
|
+
// bundle, and importing transmit would pull the rest of its dependencies in.
|
|
51875
|
+
function cancelResponseBody(response) {
|
|
51876
|
+
try {
|
|
51877
|
+
if (response && response.body && typeof response.body.cancel === 'function') {
|
|
51878
|
+
response.body.cancel().catch(function () { });
|
|
51879
|
+
}
|
|
51880
|
+
}
|
|
51881
|
+
catch (e) {
|
|
51882
|
+
}
|
|
51883
|
+
}
|
|
51793
51884
|
function matchHostedResources(recordingEvent, stringifiedRecordingPayload) {
|
|
51794
51885
|
const fontURLRegex = /https:\/\/[^"\\\s]+?\.(woff2?|ttf|otf|eot)(\?[^"\\\s]*)?\b/g;
|
|
51795
51886
|
const matches = stringifiedRecordingPayload.match(fontURLRegex);
|
|
@@ -51864,6 +51955,7 @@ var WorkerFactory = /*#__PURE__*/createInlineWorkerFactory(/* rollup-plugin-web-
|
|
|
51864
51955
|
method: 'POST',
|
|
51865
51956
|
body
|
|
51866
51957
|
}).then(function (response) {
|
|
51958
|
+
cancelResponseBody(response);
|
|
51867
51959
|
if (response.status < 200 || response.status >= 300) {
|
|
51868
51960
|
postMessage({
|
|
51869
51961
|
error: new Error(`received status code ${response.status}: ${response.statusText}`),
|
|
@@ -52623,7 +52715,10 @@ function ConsoleCapture() {
|
|
|
52623
52715
|
let transport;
|
|
52624
52716
|
let isPtmPaused;
|
|
52625
52717
|
let isCapturingConsoleLogs = false;
|
|
52718
|
+
let alwaysCaptureErrors = false;
|
|
52719
|
+
let isRecordingActive = false;
|
|
52626
52720
|
const CAPTURE_CONSOLE_CONFIG = 'captureConsoleLogs';
|
|
52721
|
+
const ALWAYS_CAPTURE_ERRORS_CONFIG = 'alwaysCaptureDevLogErrors';
|
|
52627
52722
|
const CONSOLE_METHODS = ['log', 'warn', 'error', 'info'];
|
|
52628
52723
|
const DEV_LOG_SUB_TYPE = 'console';
|
|
52629
52724
|
// deduplicate logs
|
|
@@ -52662,9 +52757,11 @@ function ConsoleCapture() {
|
|
|
52662
52757
|
pluginAPI = PluginAPI;
|
|
52663
52758
|
const { ConfigReader } = pluginAPI;
|
|
52664
52759
|
ConfigReader.addOption(CAPTURE_CONSOLE_CONFIG, [ConfigReader.sources.PENDO_CONFIG_SRC], false);
|
|
52760
|
+
ConfigReader.addOption(ALWAYS_CAPTURE_ERRORS_CONFIG, [ConfigReader.sources.PENDO_CONFIG_SRC], false);
|
|
52665
52761
|
const captureConsoleEnabled = ConfigReader.get(CAPTURE_CONSOLE_CONFIG);
|
|
52666
52762
|
if (!captureConsoleEnabled)
|
|
52667
52763
|
return;
|
|
52764
|
+
alwaysCaptureErrors = !!ConfigReader.get(ALWAYS_CAPTURE_ERRORS_CONFIG);
|
|
52668
52765
|
globalPendo = pendo;
|
|
52669
52766
|
buffer = new DevlogBuffer(pendo, pluginAPI);
|
|
52670
52767
|
transport = new DevlogTransport(pendo, pluginAPI);
|
|
@@ -52708,11 +52805,19 @@ function ConsoleCapture() {
|
|
|
52708
52805
|
pluginAPI.log.info(`[ConsoleCapture] Console log capture ${shouldCapture ? 'started' : 'stopped'}${reason ? `: ${reason}` : ''}`);
|
|
52709
52806
|
}
|
|
52710
52807
|
function recordingStarted() {
|
|
52808
|
+
isRecordingActive = true;
|
|
52711
52809
|
setCaptureState({ shouldCapture: true, reason: 'recording started' });
|
|
52712
52810
|
}
|
|
52713
52811
|
function recordingStopped() {
|
|
52812
|
+
isRecordingActive = false;
|
|
52813
|
+
if (alwaysCaptureErrors)
|
|
52814
|
+
return; // capture isn't tied to the recording lifecycle
|
|
52714
52815
|
setCaptureState({ shouldCapture: false, reason: 'recording stopped' });
|
|
52715
52816
|
}
|
|
52817
|
+
// ingestion only keeps error logs when there's no recording, so don't send the rest
|
|
52818
|
+
function isErrorOnlyCapture() {
|
|
52819
|
+
return alwaysCaptureErrors && !isRecordingActive;
|
|
52820
|
+
}
|
|
52716
52821
|
function readyHandler() {
|
|
52717
52822
|
try {
|
|
52718
52823
|
addIntercepts();
|
|
@@ -52721,6 +52826,10 @@ function ConsoleCapture() {
|
|
|
52721
52826
|
catch (error) {
|
|
52722
52827
|
pluginAPI.log.error('Error setting up console capture intercepts: ', error);
|
|
52723
52828
|
teardown();
|
|
52829
|
+
return;
|
|
52830
|
+
}
|
|
52831
|
+
if (alwaysCaptureErrors) {
|
|
52832
|
+
setCaptureState({ shouldCapture: true, reason: 'always capture dev log errors' });
|
|
52724
52833
|
}
|
|
52725
52834
|
}
|
|
52726
52835
|
function addIntercepts() {
|
|
@@ -52756,6 +52865,8 @@ function ConsoleCapture() {
|
|
|
52756
52865
|
function createConsoleEvent(args, methodName, { skipStackTrace = false, skipScrubPII = false } = {}) {
|
|
52757
52866
|
if (!isCapturingConsoleLogs || !args || args.length === 0 || !_.contains(CONSOLE_METHODS, methodName))
|
|
52758
52867
|
return;
|
|
52868
|
+
if (isErrorOnlyCapture() && methodName !== 'error')
|
|
52869
|
+
return;
|
|
52759
52870
|
// stringify args
|
|
52760
52871
|
let message = _.compact(_.map(args, arg => {
|
|
52761
52872
|
let stringifiedArg;
|
|
@@ -52869,8 +52980,11 @@ function NetworkCapture() {
|
|
|
52869
52980
|
let responseBodyCb;
|
|
52870
52981
|
let pendoDevlogBaseUrl;
|
|
52871
52982
|
let isCapturingNetworkLogs = false;
|
|
52983
|
+
let alwaysCaptureErrors = false;
|
|
52984
|
+
let isRecordingActive = false;
|
|
52872
52985
|
let excludeRequestUrls = [];
|
|
52873
52986
|
const CAPTURE_NETWORK_CONFIG = 'captureNetworkRequests';
|
|
52987
|
+
const ALWAYS_CAPTURE_ERRORS_CONFIG = 'alwaysCaptureDevLogErrors';
|
|
52874
52988
|
const NETWORK_SUB_TYPE = 'network';
|
|
52875
52989
|
const NETWORK_LOGS_CONFIG = 'networkLogs';
|
|
52876
52990
|
const NETWORK_LOGS_CONFIG_ALLOWED_REQUEST_HEADERS = 'networkLogs.allowedRequestHeaders';
|
|
@@ -52946,9 +53060,11 @@ function NetworkCapture() {
|
|
|
52946
53060
|
globalPendo = pendo;
|
|
52947
53061
|
const { ConfigReader } = pluginAPI;
|
|
52948
53062
|
ConfigReader.addOption(CAPTURE_NETWORK_CONFIG, [ConfigReader.sources.PENDO_CONFIG_SRC], false);
|
|
53063
|
+
ConfigReader.addOption(ALWAYS_CAPTURE_ERRORS_CONFIG, [ConfigReader.sources.PENDO_CONFIG_SRC], false);
|
|
52949
53064
|
const captureNetworkEnabled = ConfigReader.get(CAPTURE_NETWORK_CONFIG);
|
|
52950
53065
|
if (!captureNetworkEnabled)
|
|
52951
53066
|
return;
|
|
53067
|
+
alwaysCaptureErrors = !!ConfigReader.get(ALWAYS_CAPTURE_ERRORS_CONFIG);
|
|
52952
53068
|
buffer = new DevlogBuffer(pendo, pluginAPI);
|
|
52953
53069
|
transport = new DevlogTransport(pendo, pluginAPI);
|
|
52954
53070
|
sendQueue = new pluginAPI.SendQueue(transport.sendRequest.bind(transport));
|
|
@@ -53008,7 +53124,9 @@ function NetworkCapture() {
|
|
|
53008
53124
|
*/
|
|
53009
53125
|
ConfigReader.addOption(NETWORK_LOGS_CONFIG_CAPTURE_REQUEST_BODY, [ConfigReader.sources.SNIPPET_SRC], undefined);
|
|
53010
53126
|
/**
|
|
53011
|
-
* Callback function to process/modify response bodies before capturing them. If not provided, the response body
|
|
53127
|
+
* Callback function to process/modify response bodies before capturing them. If not provided, the response body
|
|
53128
|
+
* will not be captured, unless `alwaysCaptureDevLogErrors` is enabled: error response bodies are then captured
|
|
53129
|
+
* as-is until a callback is provided.
|
|
53012
53130
|
*
|
|
53013
53131
|
* @access public
|
|
53014
53132
|
* @category Config/Network Logs
|
|
@@ -53053,6 +53171,14 @@ function NetworkCapture() {
|
|
|
53053
53171
|
responseBodyCb = config.captureResponseBody;
|
|
53054
53172
|
}
|
|
53055
53173
|
}
|
|
53174
|
+
// error bodies are captured by default here; processBody still masks and truncates them
|
|
53175
|
+
function getResponseBodyCb(response) {
|
|
53176
|
+
if (responseBodyCb)
|
|
53177
|
+
return responseBodyCb;
|
|
53178
|
+
if (alwaysCaptureErrors && response.status >= 400)
|
|
53179
|
+
return (body) => body;
|
|
53180
|
+
return undefined;
|
|
53181
|
+
}
|
|
53056
53182
|
function buildExcludeRequestUrls() {
|
|
53057
53183
|
const requestUrls = pluginAPI.ConfigReader.get(NETWORK_LOGS_CONFIG_EXCLUDE_REQUEST_URLS);
|
|
53058
53184
|
if (!requestUrls || requestUrls.length === 0)
|
|
@@ -53100,13 +53226,24 @@ function NetworkCapture() {
|
|
|
53100
53226
|
pluginAPI.log.info(`[NetworkCapture] Network request capture ${shouldCapture ? 'started' : 'stopped'}${reason ? `: ${reason}` : ''}`);
|
|
53101
53227
|
}
|
|
53102
53228
|
function recordingStarted() {
|
|
53229
|
+
isRecordingActive = true;
|
|
53103
53230
|
return setCaptureState({ shouldCapture: true, reason: 'recording started' });
|
|
53104
53231
|
}
|
|
53105
53232
|
function recordingStopped() {
|
|
53233
|
+
isRecordingActive = false;
|
|
53234
|
+
if (alwaysCaptureErrors)
|
|
53235
|
+
return; // capture isn't tied to the recording lifecycle
|
|
53106
53236
|
return setCaptureState({ shouldCapture: false, reason: 'recording stopped' });
|
|
53107
53237
|
}
|
|
53238
|
+
// ingestion only keeps error responses when there's no recording, so don't send the rest
|
|
53239
|
+
function isErrorOnlyCapture() {
|
|
53240
|
+
return alwaysCaptureErrors && !isRecordingActive;
|
|
53241
|
+
}
|
|
53108
53242
|
function startCapture() {
|
|
53109
53243
|
pluginAPI.NetworkRequest.on({ request: handleRequest, response: handleResponse, error: handleError });
|
|
53244
|
+
if (alwaysCaptureErrors) {
|
|
53245
|
+
setCaptureState({ shouldCapture: true, reason: 'always capture dev log errors' });
|
|
53246
|
+
}
|
|
53110
53247
|
}
|
|
53111
53248
|
function teardown() {
|
|
53112
53249
|
if (sendInterval) {
|
|
@@ -53154,6 +53291,10 @@ function NetworkCapture() {
|
|
|
53154
53291
|
delete requestMap[response.requestId];
|
|
53155
53292
|
return;
|
|
53156
53293
|
}
|
|
53294
|
+
if (isErrorOnlyCapture() && !(response.status >= 400)) {
|
|
53295
|
+
delete requestMap[response.requestId];
|
|
53296
|
+
return;
|
|
53297
|
+
}
|
|
53157
53298
|
if (!buffer.hasTokenAvailable()) {
|
|
53158
53299
|
delete requestMap[response.requestId];
|
|
53159
53300
|
return;
|
|
@@ -53212,11 +53353,11 @@ function NetworkCapture() {
|
|
|
53212
53353
|
return '[Failed to process request body]';
|
|
53213
53354
|
}
|
|
53214
53355
|
}
|
|
53215
|
-
function processResponseBody({ response }) {
|
|
53216
|
-
if (!response || !response.body || !
|
|
53356
|
+
function processResponseBody({ response, bodyCb = responseBodyCb }) {
|
|
53357
|
+
if (!response || !response.body || !bodyCb)
|
|
53217
53358
|
return '';
|
|
53218
53359
|
try {
|
|
53219
|
-
const body =
|
|
53360
|
+
const body = bodyCb(response.body, { response });
|
|
53220
53361
|
const contentType = globalPendo._.get(response, 'headers.content-type', '');
|
|
53221
53362
|
return processBody(body, contentType);
|
|
53222
53363
|
}
|
|
@@ -53233,8 +53374,9 @@ function NetworkCapture() {
|
|
|
53233
53374
|
if (requestBodyCb) {
|
|
53234
53375
|
networkEvent.devLogRequestBody = processRequestBody({ request });
|
|
53235
53376
|
}
|
|
53236
|
-
|
|
53237
|
-
|
|
53377
|
+
const effectiveResponseBodyCb = getResponseBodyCb(response);
|
|
53378
|
+
if (effectiveResponseBodyCb) {
|
|
53379
|
+
networkEvent.devLogResponseBody = processResponseBody({ response, bodyCb: effectiveResponseBodyCb });
|
|
53238
53380
|
}
|
|
53239
53381
|
return networkEvent;
|
|
53240
53382
|
}
|
|
@@ -55309,18 +55451,24 @@ var underscore = {exports: {}};
|
|
|
55309
55451
|
var underscoreExports = underscore.exports;
|
|
55310
55452
|
var _ = /*@__PURE__*/getDefaultExportFromCjs(underscoreExports);
|
|
55311
55453
|
|
|
55312
|
-
var encodeURIComponent = _.isFunction(
|
|
55454
|
+
var encodeURIComponent = _.isFunction(globalScope.encodeURIComponent) ? globalScope.encodeURIComponent : _.identity;
|
|
55313
55455
|
|
|
55314
55456
|
const WIDGET_SETTINGS_API = '/external/widget/settings';
|
|
55315
55457
|
class PredictApiService {
|
|
55316
55458
|
constructor({ baseUrl, token }) {
|
|
55317
55459
|
this.performRequest = (url, options) => __awaiter(this, void 0, void 0, function* () {
|
|
55460
|
+
var _a;
|
|
55318
55461
|
const response = yield fetch(url, options);
|
|
55319
|
-
|
|
55320
|
-
|
|
55462
|
+
if (!response.ok) {
|
|
55463
|
+
// nothing reads an error body, and leaving it undrained holds its
|
|
55464
|
+
// stream and socket open until the response is collected
|
|
55465
|
+
(_a = response.body) === null || _a === void 0 ? void 0 : _a.cancel().catch(() => { });
|
|
55466
|
+
return { ok: false, status: response.status, body: null };
|
|
55467
|
+
}
|
|
55468
|
+
return { ok: true, status: response.status, body: yield response.json() };
|
|
55321
55469
|
});
|
|
55322
55470
|
this.getWidgetSettingsById = (id, log) => __awaiter(this, void 0, void 0, function* () {
|
|
55323
|
-
var
|
|
55471
|
+
var _b, _c;
|
|
55324
55472
|
if (!id) {
|
|
55325
55473
|
log('[predict] no id provided');
|
|
55326
55474
|
return null;
|
|
@@ -55335,11 +55483,11 @@ class PredictApiService {
|
|
|
55335
55483
|
return null;
|
|
55336
55484
|
}
|
|
55337
55485
|
const data = body;
|
|
55338
|
-
if ((
|
|
55486
|
+
if ((_b = data === null || data === void 0 ? void 0 : data.errors) === null || _b === void 0 ? void 0 : _b.length) {
|
|
55339
55487
|
log(`[predict] error getting widget settings by id: ${data.errors}`);
|
|
55340
55488
|
return null;
|
|
55341
55489
|
}
|
|
55342
|
-
return (
|
|
55490
|
+
return (_c = data === null || data === void 0 ? void 0 : data.data) !== null && _c !== void 0 ? _c : null;
|
|
55343
55491
|
}
|
|
55344
55492
|
catch (error) {
|
|
55345
55493
|
log(`[predict] error getting widget settings by id: ${error}`);
|