@pendo/agent 2.337.2 → 2.338.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/dom.esm.js +9 -2
- package/dist/pendo.debugger.min.js +13 -8
- package/dist/pendo.module.js +198 -38
- package/dist/pendo.module.min.js +9 -9
- 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;
|
|
@@ -3916,7 +3924,13 @@ function isMobileUserAgent() {
|
|
|
3916
3924
|
return (/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i).test(getUA());
|
|
3917
3925
|
}
|
|
3918
3926
|
function isSafari(userAgent = getUA()) {
|
|
3919
|
-
return userAgent.indexOf('Safari') >= 0 && !userAgent
|
|
3927
|
+
return userAgent.indexOf('Safari') >= 0 && !isChromium(userAgent);
|
|
3928
|
+
}
|
|
3929
|
+
// Every Chromium UA carries a trailing Safari/ token, so Chrome, Edge, Opera,
|
|
3930
|
+
// Brave and Electron all have to be excluded from isSafari(). Chromium builds
|
|
3931
|
+
// report Chromium/ without a Chrome/ token, hence both checks.
|
|
3932
|
+
function isChromium(userAgent = getUA()) {
|
|
3933
|
+
return userAgent.indexOf('Chrome') >= 0 || userAgent.indexOf('Chromium') >= 0;
|
|
3920
3934
|
}
|
|
3921
3935
|
const sniffer = {
|
|
3922
3936
|
supportsHistoryApi: shouldWrapNativeHistory,
|
|
@@ -4029,8 +4043,8 @@ let SERVER = '';
|
|
|
4029
4043
|
let ASSET_HOST = '';
|
|
4030
4044
|
let ASSET_PATH = '';
|
|
4031
4045
|
let DESIGNER_SERVER = '';
|
|
4032
|
-
let VERSION = '2.
|
|
4033
|
-
let PACKAGE_VERSION = '2.
|
|
4046
|
+
let VERSION = '2.338.0_';
|
|
4047
|
+
let PACKAGE_VERSION = '2.338.0';
|
|
4034
4048
|
let LOADER = 'xhr';
|
|
4035
4049
|
/* eslint-enable web-sdk-eslint-rules/no-gulp-env-references */
|
|
4036
4050
|
/**
|
|
@@ -4447,12 +4461,6 @@ class LogEmitter extends EventTarget {
|
|
|
4447
4461
|
}
|
|
4448
4462
|
const log = new LogEmitter();
|
|
4449
4463
|
|
|
4450
|
-
function escapeRegExp(string) {
|
|
4451
|
-
if (!string)
|
|
4452
|
-
return;
|
|
4453
|
-
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
4454
|
-
}
|
|
4455
|
-
|
|
4456
4464
|
const allowKeys = function () {
|
|
4457
4465
|
var allowKeys = ConfigReader.get('storage.allowKeys') || '*';
|
|
4458
4466
|
return _.isArray(allowKeys) ? _.indexBy(allowKeys) : allowKeys;
|
|
@@ -10079,6 +10087,26 @@ var writeMessage = function (msg) {
|
|
|
10079
10087
|
});
|
|
10080
10088
|
return q.resolve(writeImgTag(url)).catch(_.noop);
|
|
10081
10089
|
};
|
|
10090
|
+
/**
|
|
10091
|
+
* Releases a fetch response body we are never going to read. An undrained body
|
|
10092
|
+
* holds its stream, the underlying data pipe and the socket open until the
|
|
10093
|
+
* {Response} is collected, which may never happen in a long-lived embedded
|
|
10094
|
+
* renderer. Cancelling does not affect keepalive delivery.
|
|
10095
|
+
*
|
|
10096
|
+
* @access private
|
|
10097
|
+
* @param {Response} response to discard the body of
|
|
10098
|
+
* @returns {Response} the same response, for chaining
|
|
10099
|
+
*/
|
|
10100
|
+
function cancelResponseBody(response) {
|
|
10101
|
+
try {
|
|
10102
|
+
if (response && response.body && _.isFunction(response.body.cancel)) {
|
|
10103
|
+
response.body.cancel().catch(_.noop);
|
|
10104
|
+
}
|
|
10105
|
+
}
|
|
10106
|
+
catch (e) {
|
|
10107
|
+
}
|
|
10108
|
+
return response;
|
|
10109
|
+
}
|
|
10082
10110
|
/**
|
|
10083
10111
|
* @access private
|
|
10084
10112
|
* @param {string} msg to send
|
|
@@ -10107,7 +10135,9 @@ function writeErrorPOST(msg) {
|
|
|
10107
10135
|
else {
|
|
10108
10136
|
promise = ajax.postJSON(url, errorData);
|
|
10109
10137
|
}
|
|
10110
|
-
return promise.then(function () {
|
|
10138
|
+
return promise.then(function (response) {
|
|
10139
|
+
cancelResponseBody(response);
|
|
10140
|
+
}, function (err) { log.debug('error writing error:' + err); });
|
|
10111
10141
|
}
|
|
10112
10142
|
catch (e) {
|
|
10113
10143
|
log.info('Failed to write error to server using POST endpoint: ' + e);
|
|
@@ -10129,6 +10159,10 @@ function writeMetricsPOST(payload) {
|
|
|
10129
10159
|
keepalive: true,
|
|
10130
10160
|
body: JSON.stringify(payload),
|
|
10131
10161
|
headers: { 'Content-Type': 'application/json' }
|
|
10162
|
+
})
|
|
10163
|
+
.then(cancelResponseBody)
|
|
10164
|
+
.catch(function (err) {
|
|
10165
|
+
log.debug(`Failed to write metrics payload to server: ${err}`);
|
|
10132
10166
|
});
|
|
10133
10167
|
}
|
|
10134
10168
|
else {
|
|
@@ -10156,13 +10190,23 @@ function writeImgTag(src) {
|
|
|
10156
10190
|
image.src = src;
|
|
10157
10191
|
return deferred.promise;
|
|
10158
10192
|
}
|
|
10193
|
+
/**
|
|
10194
|
+
* Sends a fire-and-forget beacon request. The response body is cancelled before
|
|
10195
|
+
* the promise resolves, so callers get {Response.status}, {Response.ok} and the
|
|
10196
|
+
* headers but cannot read the body. Use {fetch} directly if you need to read one.
|
|
10197
|
+
*
|
|
10198
|
+
* @access private
|
|
10199
|
+
* @param {string} url to request
|
|
10200
|
+
* @param {Object} options passed to {fetch}, defaulting to a keepalive GET
|
|
10201
|
+
* @returns {Promise<Response>} with its body already cancelled
|
|
10202
|
+
*/
|
|
10159
10203
|
function fetchKeepalive(url, options = {}) {
|
|
10160
10204
|
const defaults = {
|
|
10161
10205
|
method: 'GET',
|
|
10162
10206
|
keepalive: true
|
|
10163
10207
|
};
|
|
10164
10208
|
const fetchOptions = _.extend({}, defaults, options);
|
|
10165
|
-
return fetch(url, fetchOptions);
|
|
10209
|
+
return fetch(url, fetchOptions).then(cancelResponseBody);
|
|
10166
10210
|
}
|
|
10167
10211
|
fetchKeepalive.supported = function () {
|
|
10168
10212
|
try {
|
|
@@ -11788,6 +11832,13 @@ _.extend(dom, {
|
|
|
11788
11832
|
});
|
|
11789
11833
|
_.extend(dom.prototype, DomEvent.$, DomQuery.$);
|
|
11790
11834
|
|
|
11835
|
+
// Domains that Pendo owns, will be swapped in by build patches
|
|
11836
|
+
const pendoDomainPatterns = [
|
|
11837
|
+
/^https:\/\/((adopt|oem)\.)?((us1)\.)?(app|via|adopt|cdn|oem|novus-api|novus)(\.(au|eu|gov|hsbc|jpn|tv))?\.pendo\.io$/,
|
|
11838
|
+
/^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$/,
|
|
11839
|
+
/^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$/,
|
|
11840
|
+
/^https:\/\/(pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)-static\.storage\.googleapis\.com$/
|
|
11841
|
+
]; // eslint-disable-line web-sdk-eslint-rules/no-gulp-env-references
|
|
11791
11842
|
function isTrustedOrigin(host) {
|
|
11792
11843
|
if (!host)
|
|
11793
11844
|
return false;
|
|
@@ -11801,17 +11852,6 @@ function isTrustedOrigin(host) {
|
|
|
11801
11852
|
return true;
|
|
11802
11853
|
if (host === getContentHost())
|
|
11803
11854
|
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
|
-
/^https:\/\/([a-zA-Z0-9-]+\.)*pendo-dev\.com$/,
|
|
11811
|
-
/^https:\/\/([a-zA-Z0-9-]+-dot-)?(pendo-apollo|pendo-armada|pendo-atlas|pendo-batman|pendo-calypso|pendo-dap|pendo-dev|pendo-dr-us-east1|pendo-eu-dev|pendo-freeze|pendo-helix|pendo-link|pendo-magic|pendo-mango|pendo-mcfly|pendo-ml|pendo-mobile-fbi|pendo-mobile-guides|pendo-mobile-hummus|pendo-mobile-plat|pendo-perfserf|pendo-voc|pendo-wildlings|pendo-ionchef|pendo-scrum-ops|pendo-security|pendo-test|pendo-uat|pendo-au|pendo-eu|pendo-govramp|pendo-hsbc|pendo-io|pendo-jp-prod|pendo-tv|pendo-us1)\.appspot\.com$/,
|
|
11812
|
-
/^https:\/\/(via|adopt|local)\.pendo\.(local|io):\d{4}$/,
|
|
11813
|
-
/^https:\/\/(pendo-apollo|pendo-armada|pendo-atlas|pendo-batman|pendo-calypso|pendo-dap|pendo-dev|pendo-dr-us-east1|pendo-eu-dev|pendo-freeze|pendo-helix|pendo-link|pendo-magic|pendo-mango|pendo-mcfly|pendo-ml|pendo-mobile-fbi|pendo-mobile-guides|pendo-mobile-hummus|pendo-mobile-plat|pendo-perfserf|pendo-voc|pendo-wildlings|pendo-ionchef|pendo-scrum-ops|pendo-security|pendo-test|pendo-uat)-static\.storage\.googleapis\.com$/
|
|
11814
|
-
]; // eslint-disable-line web-sdk-eslint-rules/no-gulp-env-references
|
|
11815
11855
|
var adoptHost = ConfigReader.get('adoptHost');
|
|
11816
11856
|
if (adoptHost) {
|
|
11817
11857
|
var fullHostname = 'https://' + adoptHost;
|
|
@@ -11821,10 +11861,17 @@ function isTrustedOrigin(host) {
|
|
|
11821
11861
|
}
|
|
11822
11862
|
if (urlMatchesPatterns(host, ConfigReader.get('allowedOriginServers', []), ConfigReader.get('allowedOriginServerHashes', [])))
|
|
11823
11863
|
return true;
|
|
11824
|
-
return _.any(
|
|
11864
|
+
return _.any(pendoDomainPatterns, function (pattern) {
|
|
11825
11865
|
return pattern.test(host);
|
|
11826
11866
|
});
|
|
11827
11867
|
}
|
|
11868
|
+
function isPendoDomain(origin) {
|
|
11869
|
+
if (!origin)
|
|
11870
|
+
return false;
|
|
11871
|
+
return _.any(pendoDomainPatterns, function (pattern) {
|
|
11872
|
+
return pattern.test(origin);
|
|
11873
|
+
});
|
|
11874
|
+
}
|
|
11828
11875
|
function hasAllowedOriginServers() {
|
|
11829
11876
|
const allowedOriginServers = ConfigReader.get('allowedOriginServers', []);
|
|
11830
11877
|
return allowedOriginServers && allowedOriginServers.length > 0;
|
|
@@ -13162,6 +13209,7 @@ function sendWithImg(apiKey, silo) {
|
|
|
13162
13209
|
return writeImgTag(buildGetRequestUrl(apiKey, silo));
|
|
13163
13210
|
}
|
|
13164
13211
|
function handleFetchResponse(response) {
|
|
13212
|
+
cancelResponseBody(response);
|
|
13165
13213
|
if (response.status < 200 || response.status >= 300) {
|
|
13166
13214
|
return q.reject(new Error(`received status code ${response.status}: ${response.statusText}`));
|
|
13167
13215
|
}
|
|
@@ -26805,10 +26853,23 @@ var DesignerV2 = (function () {
|
|
|
26805
26853
|
}
|
|
26806
26854
|
return validTarget;
|
|
26807
26855
|
}
|
|
26856
|
+
function hasSameOriginOpener() {
|
|
26857
|
+
if (!window.opener)
|
|
26858
|
+
return false;
|
|
26859
|
+
try {
|
|
26860
|
+
return window.opener.location.origin === window.location.origin;
|
|
26861
|
+
}
|
|
26862
|
+
catch (e) {
|
|
26863
|
+
return false;
|
|
26864
|
+
}
|
|
26865
|
+
}
|
|
26808
26866
|
function launchOnToken(url) {
|
|
26809
26867
|
if (isDesignerFrame(window.name)) {
|
|
26810
26868
|
return;
|
|
26811
26869
|
}
|
|
26870
|
+
if (isPendoDomain(window.location.origin) && !hasSameOriginOpener()) {
|
|
26871
|
+
return;
|
|
26872
|
+
}
|
|
26812
26873
|
var tokenParamTest = /pendo-designer=([A-Za-z0-9-]+)/;
|
|
26813
26874
|
if (!tokenParamTest.test(url))
|
|
26814
26875
|
return;
|
|
@@ -26821,7 +26882,7 @@ var DesignerV2 = (function () {
|
|
|
26821
26882
|
// clear stored designer state if we're launching from a token
|
|
26822
26883
|
window.localStorage.removeItem('pendo-navigation-state');
|
|
26823
26884
|
window.localStorage.removeItem('pendo-designer-mode');
|
|
26824
|
-
launchInAppDesigner({
|
|
26885
|
+
DesignerV2.launchInAppDesigner({
|
|
26825
26886
|
target: validTarget,
|
|
26826
26887
|
host: validAssetHost,
|
|
26827
26888
|
preloader: false,
|
|
@@ -29738,7 +29799,11 @@ var BuildingBlockGuides = (function () {
|
|
|
29738
29799
|
// position and calculate its height before making it visible.
|
|
29739
29800
|
// In the designer, changing visibility like this causes the guide to "flicker"
|
|
29740
29801
|
// between re-renders
|
|
29741
|
-
|
|
29802
|
+
// Only hide the first match. Resource center module previews nest the module's
|
|
29803
|
+
// pendo-g- inside the home view's pendo-g- under the same id (via
|
|
29804
|
+
// correctGuideContainerId), and imageNodeHandler only un-hides the first match,
|
|
29805
|
+
// so hiding every match left the module content hidden forever.
|
|
29806
|
+
dom(guideContainer[0]).css({ visibility: 'hidden' });
|
|
29742
29807
|
guideToAppend.css({ visibility: 'hidden' });
|
|
29743
29808
|
const embedConfig = _.get(guide, 'attributes.embedConfig', null);
|
|
29744
29809
|
var watermark = BuildingBlockWatermark.buildWatermark({
|
|
@@ -50698,6 +50763,7 @@ const RECORDING_CONFIG_ON_RECORDING_STOP = 'recording.onRecordingStop';
|
|
|
50698
50763
|
const RECORDING_CONFIG_WORKER_OVERRIDE = 'recording.workerOverride';
|
|
50699
50764
|
const RECORDING_CONFIG_TREAT_IFRAME_AS_ROOT = 'recording.treatIframeAsRoot';
|
|
50700
50765
|
const RECORDING_CONFIG_DISABLE_UNLOAD = 'recording.disableUnload';
|
|
50766
|
+
const RECORDING_CONFIG_RECORD_CROSS_ORIGIN_IFRAMES = 'recording.recordCrossOriginIframes';
|
|
50701
50767
|
const RESOURCE_CACHING = 'resourceCaching';
|
|
50702
50768
|
const ONE_DAY_IN_MILLISECONDS = 24 * 60 * 60 * 1000;
|
|
50703
50769
|
const ONE_MINUTE_IN_MILLISECONDS = 60 * 1000;
|
|
@@ -50809,6 +50875,26 @@ class SessionRecorder {
|
|
|
50809
50875
|
* @type {boolean}
|
|
50810
50876
|
*/
|
|
50811
50877
|
cf.addOption(RECORDING_CONFIG_TREAT_IFRAME_AS_ROOT, [cf.sources.SNIPPET_SRC], undefined);
|
|
50878
|
+
/**
|
|
50879
|
+
* Controls whether content embedded from another domain is included in replay. When enabled, the
|
|
50880
|
+
* embedded content shows up as part of the page. When disabled, it appears as a blank box.
|
|
50881
|
+
*
|
|
50882
|
+
* This only applies to embedded content that also has Pendo installed. Embeds from other companies
|
|
50883
|
+
* (Stripe, YouTube) are never captured either way — turning this off just stops Pendo from
|
|
50884
|
+
* messaging those frames at all.
|
|
50885
|
+
*
|
|
50886
|
+
* To capture embedded content as its own separate replay instead of part of the page it sits in,
|
|
50887
|
+
* see recording.treatIframeAsRoot.
|
|
50888
|
+
*
|
|
50889
|
+
* @access public
|
|
50890
|
+
* @category Config/Replay
|
|
50891
|
+
* @name recording.recordCrossOriginIframes
|
|
50892
|
+
* @default true
|
|
50893
|
+
* @type {boolean}
|
|
50894
|
+
*/
|
|
50895
|
+
// Like recording.enabled, this defaults to undefined so that it only overrides the
|
|
50896
|
+
// value returned by the backend when it is explicitly set to true/false
|
|
50897
|
+
cf.addOption(RECORDING_CONFIG_RECORD_CROSS_ORIGIN_IFRAMES, [cf.sources.SNIPPET_SRC], undefined);
|
|
50812
50898
|
cf.addOption(RESOURCE_CACHING, [cf.sources.PENDO_CONFIG_SRC], undefined);
|
|
50813
50899
|
}
|
|
50814
50900
|
initialize(pendo, PluginAPI) {
|
|
@@ -51028,6 +51114,9 @@ class SessionRecorder {
|
|
|
51028
51114
|
});
|
|
51029
51115
|
const blockedSelectors = ['.pendo-ignore', '.pendo-sr-ignore'].concat(recordingOptions.blockedSelectors || []);
|
|
51030
51116
|
const hiddenSelectors = ['.pendo-sr-hide'].concat(recordingOptions.hiddenSelectors || []);
|
|
51117
|
+
// The snippet config value should take precedence over the value from the visitor config
|
|
51118
|
+
const snippetValue = this.api.ConfigReader.get(RECORDING_CONFIG_RECORD_CROSS_ORIGIN_IFRAMES);
|
|
51119
|
+
const recordCrossOriginIframes = this.pendo._.isBoolean(snippetValue) ? snippetValue : recordingOptions.recordCrossOriginIframes;
|
|
51031
51120
|
const config = {
|
|
51032
51121
|
maskInputFn: this.pendo._.partial(maskInput, maskOptions),
|
|
51033
51122
|
maskTextFn: this.pendo._.partial(maskText, maskOptions),
|
|
@@ -51038,7 +51127,7 @@ class SessionRecorder {
|
|
|
51038
51127
|
// we need to concat hidden selectors to re-use block selector logic
|
|
51039
51128
|
blockSelector: blockedSelectors.concat(hiddenSelectors).join(','),
|
|
51040
51129
|
hideSelector: hiddenSelectors.join(','),
|
|
51041
|
-
recordCrossOriginIframes:
|
|
51130
|
+
recordCrossOriginIframes: recordCrossOriginIframes != null ? recordCrossOriginIframes : true,
|
|
51042
51131
|
emitFromIframe: !!this.api.ConfigReader.get(RECORDING_CONFIG_TREAT_IFRAME_AS_ROOT)
|
|
51043
51132
|
};
|
|
51044
51133
|
return config;
|
|
@@ -51794,6 +51883,20 @@ var WorkerFactory = /*#__PURE__*/createInlineWorkerFactory(/* rollup-plugin-web-
|
|
|
51794
51883
|
}
|
|
51795
51884
|
|
|
51796
51885
|
const ONE_HUNDRED_MB_IN_BYTES = 100 * 1024 * 1024;
|
|
51886
|
+
// An undrained response body holds its stream, the underlying data pipe and the
|
|
51887
|
+
// socket open until the response is collected, which in a worker that lives for
|
|
51888
|
+
// the whole session may never happen. Deliberately duplicated from
|
|
51889
|
+
// cancelResponseBody in src/transmit.js instead of imported: this is its own
|
|
51890
|
+
// bundle, and importing transmit would pull the rest of its dependencies in.
|
|
51891
|
+
function cancelResponseBody(response) {
|
|
51892
|
+
try {
|
|
51893
|
+
if (response && response.body && typeof response.body.cancel === 'function') {
|
|
51894
|
+
response.body.cancel().catch(function () { });
|
|
51895
|
+
}
|
|
51896
|
+
}
|
|
51897
|
+
catch (e) {
|
|
51898
|
+
}
|
|
51899
|
+
}
|
|
51797
51900
|
function matchHostedResources(recordingEvent, stringifiedRecordingPayload) {
|
|
51798
51901
|
const fontURLRegex = /https:\/\/[^"\\\s]+?\.(woff2?|ttf|otf|eot)(\?[^"\\\s]*)?\b/g;
|
|
51799
51902
|
const matches = stringifiedRecordingPayload.match(fontURLRegex);
|
|
@@ -51868,6 +51971,7 @@ var WorkerFactory = /*#__PURE__*/createInlineWorkerFactory(/* rollup-plugin-web-
|
|
|
51868
51971
|
method: 'POST',
|
|
51869
51972
|
body
|
|
51870
51973
|
}).then(function (response) {
|
|
51974
|
+
cancelResponseBody(response);
|
|
51871
51975
|
if (response.status < 200 || response.status >= 300) {
|
|
51872
51976
|
postMessage({
|
|
51873
51977
|
error: new Error(`received status code ${response.status}: ${response.statusText}`),
|
|
@@ -52627,7 +52731,10 @@ function ConsoleCapture() {
|
|
|
52627
52731
|
let transport;
|
|
52628
52732
|
let isPtmPaused;
|
|
52629
52733
|
let isCapturingConsoleLogs = false;
|
|
52734
|
+
let alwaysCaptureErrors = false;
|
|
52735
|
+
let isRecordingActive = false;
|
|
52630
52736
|
const CAPTURE_CONSOLE_CONFIG = 'captureConsoleLogs';
|
|
52737
|
+
const ALWAYS_CAPTURE_ERRORS_CONFIG = 'alwaysCaptureDevLogErrors';
|
|
52631
52738
|
const CONSOLE_METHODS = ['log', 'warn', 'error', 'info'];
|
|
52632
52739
|
const DEV_LOG_SUB_TYPE = 'console';
|
|
52633
52740
|
// deduplicate logs
|
|
@@ -52666,9 +52773,11 @@ function ConsoleCapture() {
|
|
|
52666
52773
|
pluginAPI = PluginAPI;
|
|
52667
52774
|
const { ConfigReader } = pluginAPI;
|
|
52668
52775
|
ConfigReader.addOption(CAPTURE_CONSOLE_CONFIG, [ConfigReader.sources.PENDO_CONFIG_SRC], false);
|
|
52776
|
+
ConfigReader.addOption(ALWAYS_CAPTURE_ERRORS_CONFIG, [ConfigReader.sources.PENDO_CONFIG_SRC], false);
|
|
52669
52777
|
const captureConsoleEnabled = ConfigReader.get(CAPTURE_CONSOLE_CONFIG);
|
|
52670
52778
|
if (!captureConsoleEnabled)
|
|
52671
52779
|
return;
|
|
52780
|
+
alwaysCaptureErrors = !!ConfigReader.get(ALWAYS_CAPTURE_ERRORS_CONFIG);
|
|
52672
52781
|
globalPendo = pendo;
|
|
52673
52782
|
buffer = new DevlogBuffer(pendo, pluginAPI);
|
|
52674
52783
|
transport = new DevlogTransport(pendo, pluginAPI);
|
|
@@ -52712,11 +52821,19 @@ function ConsoleCapture() {
|
|
|
52712
52821
|
pluginAPI.log.info(`[ConsoleCapture] Console log capture ${shouldCapture ? 'started' : 'stopped'}${reason ? `: ${reason}` : ''}`);
|
|
52713
52822
|
}
|
|
52714
52823
|
function recordingStarted() {
|
|
52824
|
+
isRecordingActive = true;
|
|
52715
52825
|
setCaptureState({ shouldCapture: true, reason: 'recording started' });
|
|
52716
52826
|
}
|
|
52717
52827
|
function recordingStopped() {
|
|
52828
|
+
isRecordingActive = false;
|
|
52829
|
+
if (alwaysCaptureErrors)
|
|
52830
|
+
return; // capture isn't tied to the recording lifecycle
|
|
52718
52831
|
setCaptureState({ shouldCapture: false, reason: 'recording stopped' });
|
|
52719
52832
|
}
|
|
52833
|
+
// ingestion only keeps error logs when there's no recording, so don't send the rest
|
|
52834
|
+
function isErrorOnlyCapture() {
|
|
52835
|
+
return alwaysCaptureErrors && !isRecordingActive;
|
|
52836
|
+
}
|
|
52720
52837
|
function readyHandler() {
|
|
52721
52838
|
try {
|
|
52722
52839
|
addIntercepts();
|
|
@@ -52725,6 +52842,10 @@ function ConsoleCapture() {
|
|
|
52725
52842
|
catch (error) {
|
|
52726
52843
|
pluginAPI.log.error('Error setting up console capture intercepts: ', error);
|
|
52727
52844
|
teardown();
|
|
52845
|
+
return;
|
|
52846
|
+
}
|
|
52847
|
+
if (alwaysCaptureErrors) {
|
|
52848
|
+
setCaptureState({ shouldCapture: true, reason: 'always capture dev log errors' });
|
|
52728
52849
|
}
|
|
52729
52850
|
}
|
|
52730
52851
|
function addIntercepts() {
|
|
@@ -52760,6 +52881,8 @@ function ConsoleCapture() {
|
|
|
52760
52881
|
function createConsoleEvent(args, methodName, { skipStackTrace = false, skipScrubPII = false } = {}) {
|
|
52761
52882
|
if (!isCapturingConsoleLogs || !args || args.length === 0 || !_.contains(CONSOLE_METHODS, methodName))
|
|
52762
52883
|
return;
|
|
52884
|
+
if (isErrorOnlyCapture() && methodName !== 'error')
|
|
52885
|
+
return;
|
|
52763
52886
|
// stringify args
|
|
52764
52887
|
let message = _.compact(_.map(args, arg => {
|
|
52765
52888
|
let stringifiedArg;
|
|
@@ -52873,8 +52996,11 @@ function NetworkCapture() {
|
|
|
52873
52996
|
let responseBodyCb;
|
|
52874
52997
|
let pendoDevlogBaseUrl;
|
|
52875
52998
|
let isCapturingNetworkLogs = false;
|
|
52999
|
+
let alwaysCaptureErrors = false;
|
|
53000
|
+
let isRecordingActive = false;
|
|
52876
53001
|
let excludeRequestUrls = [];
|
|
52877
53002
|
const CAPTURE_NETWORK_CONFIG = 'captureNetworkRequests';
|
|
53003
|
+
const ALWAYS_CAPTURE_ERRORS_CONFIG = 'alwaysCaptureDevLogErrors';
|
|
52878
53004
|
const NETWORK_SUB_TYPE = 'network';
|
|
52879
53005
|
const NETWORK_LOGS_CONFIG = 'networkLogs';
|
|
52880
53006
|
const NETWORK_LOGS_CONFIG_ALLOWED_REQUEST_HEADERS = 'networkLogs.allowedRequestHeaders';
|
|
@@ -52950,9 +53076,11 @@ function NetworkCapture() {
|
|
|
52950
53076
|
globalPendo = pendo;
|
|
52951
53077
|
const { ConfigReader } = pluginAPI;
|
|
52952
53078
|
ConfigReader.addOption(CAPTURE_NETWORK_CONFIG, [ConfigReader.sources.PENDO_CONFIG_SRC], false);
|
|
53079
|
+
ConfigReader.addOption(ALWAYS_CAPTURE_ERRORS_CONFIG, [ConfigReader.sources.PENDO_CONFIG_SRC], false);
|
|
52953
53080
|
const captureNetworkEnabled = ConfigReader.get(CAPTURE_NETWORK_CONFIG);
|
|
52954
53081
|
if (!captureNetworkEnabled)
|
|
52955
53082
|
return;
|
|
53083
|
+
alwaysCaptureErrors = !!ConfigReader.get(ALWAYS_CAPTURE_ERRORS_CONFIG);
|
|
52956
53084
|
buffer = new DevlogBuffer(pendo, pluginAPI);
|
|
52957
53085
|
transport = new DevlogTransport(pendo, pluginAPI);
|
|
52958
53086
|
sendQueue = new pluginAPI.SendQueue(transport.sendRequest.bind(transport));
|
|
@@ -53012,7 +53140,9 @@ function NetworkCapture() {
|
|
|
53012
53140
|
*/
|
|
53013
53141
|
ConfigReader.addOption(NETWORK_LOGS_CONFIG_CAPTURE_REQUEST_BODY, [ConfigReader.sources.SNIPPET_SRC], undefined);
|
|
53014
53142
|
/**
|
|
53015
|
-
* Callback function to process/modify response bodies before capturing them. If not provided, the response body
|
|
53143
|
+
* Callback function to process/modify response bodies before capturing them. If not provided, the response body
|
|
53144
|
+
* will not be captured, unless `alwaysCaptureDevLogErrors` is enabled: error response bodies are then captured
|
|
53145
|
+
* as-is until a callback is provided.
|
|
53016
53146
|
*
|
|
53017
53147
|
* @access public
|
|
53018
53148
|
* @category Config/Network Logs
|
|
@@ -53057,6 +53187,14 @@ function NetworkCapture() {
|
|
|
53057
53187
|
responseBodyCb = config.captureResponseBody;
|
|
53058
53188
|
}
|
|
53059
53189
|
}
|
|
53190
|
+
// error bodies are captured by default here; processBody still masks and truncates them
|
|
53191
|
+
function getResponseBodyCb(response) {
|
|
53192
|
+
if (responseBodyCb)
|
|
53193
|
+
return responseBodyCb;
|
|
53194
|
+
if (alwaysCaptureErrors && response.status >= 400)
|
|
53195
|
+
return (body) => body;
|
|
53196
|
+
return undefined;
|
|
53197
|
+
}
|
|
53060
53198
|
function buildExcludeRequestUrls() {
|
|
53061
53199
|
const requestUrls = pluginAPI.ConfigReader.get(NETWORK_LOGS_CONFIG_EXCLUDE_REQUEST_URLS);
|
|
53062
53200
|
if (!requestUrls || requestUrls.length === 0)
|
|
@@ -53104,13 +53242,24 @@ function NetworkCapture() {
|
|
|
53104
53242
|
pluginAPI.log.info(`[NetworkCapture] Network request capture ${shouldCapture ? 'started' : 'stopped'}${reason ? `: ${reason}` : ''}`);
|
|
53105
53243
|
}
|
|
53106
53244
|
function recordingStarted() {
|
|
53245
|
+
isRecordingActive = true;
|
|
53107
53246
|
return setCaptureState({ shouldCapture: true, reason: 'recording started' });
|
|
53108
53247
|
}
|
|
53109
53248
|
function recordingStopped() {
|
|
53249
|
+
isRecordingActive = false;
|
|
53250
|
+
if (alwaysCaptureErrors)
|
|
53251
|
+
return; // capture isn't tied to the recording lifecycle
|
|
53110
53252
|
return setCaptureState({ shouldCapture: false, reason: 'recording stopped' });
|
|
53111
53253
|
}
|
|
53254
|
+
// ingestion only keeps error responses when there's no recording, so don't send the rest
|
|
53255
|
+
function isErrorOnlyCapture() {
|
|
53256
|
+
return alwaysCaptureErrors && !isRecordingActive;
|
|
53257
|
+
}
|
|
53112
53258
|
function startCapture() {
|
|
53113
53259
|
pluginAPI.NetworkRequest.on({ request: handleRequest, response: handleResponse, error: handleError });
|
|
53260
|
+
if (alwaysCaptureErrors) {
|
|
53261
|
+
setCaptureState({ shouldCapture: true, reason: 'always capture dev log errors' });
|
|
53262
|
+
}
|
|
53114
53263
|
}
|
|
53115
53264
|
function teardown() {
|
|
53116
53265
|
if (sendInterval) {
|
|
@@ -53158,6 +53307,10 @@ function NetworkCapture() {
|
|
|
53158
53307
|
delete requestMap[response.requestId];
|
|
53159
53308
|
return;
|
|
53160
53309
|
}
|
|
53310
|
+
if (isErrorOnlyCapture() && !(response.status >= 400)) {
|
|
53311
|
+
delete requestMap[response.requestId];
|
|
53312
|
+
return;
|
|
53313
|
+
}
|
|
53161
53314
|
if (!buffer.hasTokenAvailable()) {
|
|
53162
53315
|
delete requestMap[response.requestId];
|
|
53163
53316
|
return;
|
|
@@ -53216,11 +53369,11 @@ function NetworkCapture() {
|
|
|
53216
53369
|
return '[Failed to process request body]';
|
|
53217
53370
|
}
|
|
53218
53371
|
}
|
|
53219
|
-
function processResponseBody({ response }) {
|
|
53220
|
-
if (!response || !response.body || !
|
|
53372
|
+
function processResponseBody({ response, bodyCb = responseBodyCb }) {
|
|
53373
|
+
if (!response || !response.body || !bodyCb)
|
|
53221
53374
|
return '';
|
|
53222
53375
|
try {
|
|
53223
|
-
const body =
|
|
53376
|
+
const body = bodyCb(response.body, { response });
|
|
53224
53377
|
const contentType = globalPendo._.get(response, 'headers.content-type', '');
|
|
53225
53378
|
return processBody(body, contentType);
|
|
53226
53379
|
}
|
|
@@ -53237,8 +53390,9 @@ function NetworkCapture() {
|
|
|
53237
53390
|
if (requestBodyCb) {
|
|
53238
53391
|
networkEvent.devLogRequestBody = processRequestBody({ request });
|
|
53239
53392
|
}
|
|
53240
|
-
|
|
53241
|
-
|
|
53393
|
+
const effectiveResponseBodyCb = getResponseBodyCb(response);
|
|
53394
|
+
if (effectiveResponseBodyCb) {
|
|
53395
|
+
networkEvent.devLogResponseBody = processResponseBody({ response, bodyCb: effectiveResponseBodyCb });
|
|
53242
53396
|
}
|
|
53243
53397
|
return networkEvent;
|
|
53244
53398
|
}
|
|
@@ -55319,12 +55473,18 @@ const WIDGET_SETTINGS_API = '/external/widget/settings';
|
|
|
55319
55473
|
class PredictApiService {
|
|
55320
55474
|
constructor({ baseUrl, token }) {
|
|
55321
55475
|
this.performRequest = (url, options) => __awaiter(this, void 0, void 0, function* () {
|
|
55476
|
+
var _a;
|
|
55322
55477
|
const response = yield fetch(url, options);
|
|
55323
|
-
|
|
55324
|
-
|
|
55478
|
+
if (!response.ok) {
|
|
55479
|
+
// nothing reads an error body, and leaving it undrained holds its
|
|
55480
|
+
// stream and socket open until the response is collected
|
|
55481
|
+
(_a = response.body) === null || _a === void 0 ? void 0 : _a.cancel().catch(() => { });
|
|
55482
|
+
return { ok: false, status: response.status, body: null };
|
|
55483
|
+
}
|
|
55484
|
+
return { ok: true, status: response.status, body: yield response.json() };
|
|
55325
55485
|
});
|
|
55326
55486
|
this.getWidgetSettingsById = (id, log) => __awaiter(this, void 0, void 0, function* () {
|
|
55327
|
-
var
|
|
55487
|
+
var _b, _c;
|
|
55328
55488
|
if (!id) {
|
|
55329
55489
|
log('[predict] no id provided');
|
|
55330
55490
|
return null;
|
|
@@ -55339,11 +55499,11 @@ class PredictApiService {
|
|
|
55339
55499
|
return null;
|
|
55340
55500
|
}
|
|
55341
55501
|
const data = body;
|
|
55342
|
-
if ((
|
|
55502
|
+
if ((_b = data === null || data === void 0 ? void 0 : data.errors) === null || _b === void 0 ? void 0 : _b.length) {
|
|
55343
55503
|
log(`[predict] error getting widget settings by id: ${data.errors}`);
|
|
55344
55504
|
return null;
|
|
55345
55505
|
}
|
|
55346
|
-
return (
|
|
55506
|
+
return (_c = data === null || data === void 0 ? void 0 : data.data) !== null && _c !== void 0 ? _c : null;
|
|
55347
55507
|
}
|
|
55348
55508
|
catch (error) {
|
|
55349
55509
|
log(`[predict] error getting widget settings by id: ${error}`);
|