@pendo/agent 2.334.1 → 2.335.1

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.
@@ -4029,8 +4029,8 @@ let SERVER = '';
4029
4029
  let ASSET_HOST = '';
4030
4030
  let ASSET_PATH = '';
4031
4031
  let DESIGNER_SERVER = '';
4032
- let VERSION = '2.334.1_';
4033
- let PACKAGE_VERSION = '2.334.1';
4032
+ let VERSION = '2.335.1_';
4033
+ let PACKAGE_VERSION = '2.335.1';
4034
4034
  let LOADER = 'xhr';
4035
4035
  /* eslint-enable web-sdk-eslint-rules/no-gulp-env-references */
4036
4036
  /**
@@ -11704,7 +11704,7 @@ function trimSurrogate(s) {
11704
11704
  class ElementGetter {
11705
11705
  constructor(cssSelector) {
11706
11706
  this.listeners = {}; // callbacks to be called when an event of type occurs
11707
- this.events = []; // event types this element listens for
11707
+ this.events = []; // [{ type, capture }] events this element listens for
11708
11708
  this.cssSelector = cssSelector;
11709
11709
  }
11710
11710
  get() {
@@ -11719,8 +11719,8 @@ class ElementGetter {
11719
11719
  if (!el) {
11720
11720
  return undefined;
11721
11721
  }
11722
- _.each(this.events, (evtType) => {
11723
- el.addEventListener(evtType, (e) => this.onEvent(e));
11722
+ _.each(this.events, ({ type, capture }) => {
11723
+ el.addEventListener(type, (e) => this.onEvent(e), capture);
11724
11724
  });
11725
11725
  this.elRef = new WeakRef(el);
11726
11726
  return el;
@@ -11729,12 +11729,13 @@ class ElementGetter {
11729
11729
  // XXX not sure about size limit
11730
11730
  return getTextValue(this.get(), limit);
11731
11731
  }
11732
- addEventListener(event, callback) {
11732
+ addEventListener(event, callback, capture = false) {
11733
11733
  const el = this.get();
11734
- if (this.events.indexOf(event) < 0) {
11735
- this.events.push(event);
11734
+ const alreadyRegistered = _.find(this.events, (e) => e.type === event && e.capture === capture);
11735
+ if (!alreadyRegistered) {
11736
+ this.events.push({ type: event, capture });
11736
11737
  if (el) {
11737
- el.addEventListener(event, (e) => this.onEvent(e));
11738
+ el.addEventListener(event, (e) => this.onEvent(e), capture);
11738
11739
  }
11739
11740
  }
11740
11741
  this.listeners[event] = this.listeners[event] || [];
@@ -11746,7 +11747,7 @@ class ElementGetter {
11746
11747
  }
11747
11748
  teardown(el = this.get()) {
11748
11749
  if (el) {
11749
- _.each(this.events, (evtType) => el.removeEventListener(evtType, this.onEvent));
11750
+ _.each(this.events, ({ type, capture }) => el.removeEventListener(type, this.onEvent, capture));
11750
11751
  }
11751
11752
  }
11752
11753
  }
@@ -13925,6 +13926,27 @@ function calculateScrollAmount(combinedPos, screenDim, tooltipLayoutDir, bodyOve
13925
13926
  }
13926
13927
  return scrollAmt;
13927
13928
  }
13929
+ var VALID_UI_ELEMENT_TYPES = ['BUTTON', 'A', 'IMG'];
13930
+ var VALID_TRACK_ACTIONS = [
13931
+ 'advanceGuide',
13932
+ 'previousStep',
13933
+ 'dismissGuide',
13934
+ 'openLink',
13935
+ 'showGuide',
13936
+ 'launchGuide',
13937
+ 'guideSnoozed',
13938
+ 'goToStep',
13939
+ 'submitPoll',
13940
+ 'submitPollAndGoToStep',
13941
+ 'confirmation'
13942
+ ];
13943
+ function normalizeUiElementType(elementType) {
13944
+ var type = _.isString(elementType) ? elementType.toUpperCase() : '';
13945
+ return _.contains(VALID_UI_ELEMENT_TYPES, type) ? type : 'BUTTON';
13946
+ }
13947
+ function isValidTrackedAction(action) {
13948
+ return _.isObject(action) && _.isString(action.action) && _.contains(VALID_TRACK_ACTIONS, action.action);
13949
+ }
13928
13950
  function sizzleAttachPointInterceptor(Sizzle, selection, context, results, seed) {
13929
13951
  var args = _.toArray(arguments).slice(1);
13930
13952
  var resultList = Sizzle.apply(this, args);
@@ -15278,7 +15300,7 @@ var BuildingBlockTemplates = (function () {
15278
15300
  function generateResourceCenterModuleList(templateJson, step, guides, buildNodeFromJSON) {
15279
15301
  var children = templateJson.templateChildren;
15280
15302
  return _.reduce(children, function (acc, childConfig, index) {
15281
- var homeView = BuildingBlockResourceCenter.findResourceCenterHomeView(guides);
15303
+ var homeView = pendo$1.BuildingBlocks.BuildingBlockResourceCenter.findResourceCenterHomeView(guides);
15282
15304
  var guide = _.find(guides, function (singleGuide) {
15283
15305
  return singleGuide.id === childConfig.id;
15284
15306
  });
@@ -15463,7 +15485,7 @@ var BuildingBlockTemplates = (function () {
15463
15485
  });
15464
15486
  recursivelyPopulateTemplateChildrenForTaskList(templateJson, children);
15465
15487
  }
15466
- BuildingBlockResourceCenter.updatePseudoStyleSelectors(templateJson);
15488
+ pendo$1.BuildingBlocks.BuildingBlockResourceCenter.updatePseudoStyleSelectors(templateJson);
15467
15489
  const taskListBlocks = buildNodeFromJSON(templateJson, step, guides);
15468
15490
  return [taskListBlocks];
15469
15491
  }
@@ -15583,7 +15605,7 @@ var BuildingBlockTemplates = (function () {
15583
15605
  });
15584
15606
  if (!allAnnouncementsHaveContent)
15585
15607
  return [];
15586
- var resourceCenter = BuildingBlockResourceCenter.getResourceCenter();
15608
+ var resourceCenter = pendo$1.BuildingBlocks.BuildingBlockResourceCenter.getResourceCenter();
15587
15609
  var bubbleConfig = _.get(resourceCenter, 'attributes.notificationBubble');
15588
15610
  if (bubbleConfig) {
15589
15611
  delete bubbleConfig.dir;
@@ -15641,7 +15663,7 @@ var BuildingBlockTemplates = (function () {
15641
15663
  var guideTopPadding = parseInt(containerDomJson.props.style['padding-top'], 10);
15642
15664
  miniBubble.props.style.top = guideTopPadding + containerTopPadding + 5 + 'px';
15643
15665
  }
15644
- if (!BuildingBlockResourceCenter.hasAnnouncementBeenSeen(guide) && miniBubble) {
15666
+ if (!pendo$1.BuildingBlocks.BuildingBlockResourceCenter.hasAnnouncementBeenSeen(guide) && miniBubble) {
15645
15667
  listItem.props.class += ' pendo-unseen-announcement';
15646
15668
  listItem.children.unshift(miniBubble);
15647
15669
  }
@@ -15667,7 +15689,7 @@ var BuildingBlockTemplates = (function () {
15667
15689
  miniBubble = generateUnreadAnnouncementMiniBubble(bubbleConfig);
15668
15690
  miniBubble.props.style.top = '20px';
15669
15691
  }
15670
- if (!BuildingBlockResourceCenter.hasAnnouncementBeenSeen(guide) && miniBubble) {
15692
+ if (!pendo$1.BuildingBlocks.BuildingBlockResourceCenter.hasAnnouncementBeenSeen(guide) && miniBubble) {
15671
15693
  listItem.props.class += ' pendo-unseen-announcement';
15672
15694
  listItem.children = [miniBubble];
15673
15695
  }
@@ -19829,13 +19851,13 @@ var BuildingBlockResourceCenter = (function () {
19829
19851
  // If a module is currently shown, dismiss that first.
19830
19852
  if (shownModule) {
19831
19853
  var firstModuleStep = _.first(shownModule.steps);
19832
- onGuideDismissed(firstModuleStep);
19854
+ pendo$1.onGuideDismissed(firstModuleStep);
19833
19855
  }
19834
19856
  // Finally, dismiss the homeView
19835
19857
  var resourceCenterHomeView = _.get(guideToDismiss, 'attributes.resourceCenter.isTopLevel', false)
19836
19858
  ? guideToDismiss : BuildingBlockResourceCenter.findResourceCenterHomeView(getLocalActiveGuides());
19837
19859
  var homeViewFirstStep = resourceCenterHomeView.steps[0];
19838
- return onGuideDismissed(homeViewFirstStep);
19860
+ return pendo$1.onGuideDismissed(homeViewFirstStep);
19839
19861
  }
19840
19862
  function repositionResourceCenter() {
19841
19863
  var resourceCenterContainer = BuildingBlockResourceCenter.getResourceCenterContainer();
@@ -24508,7 +24530,9 @@ var loadGuideJs = function (apiKey, params) {
24508
24530
  if (treatAsAdoptPartner()) {
24509
24531
  addAccountIdParams(queryString, params.accountId, ConfigReader.get('oemAccountId'));
24510
24532
  }
24511
- if (isDebuggingEnabled()) {
24533
+ // Also check persisted storage: the store's debuggingEnabled flag is only
24534
+ // committed after leader election, which the first guide request usually beats.
24535
+ if (isDebuggingEnabled() || getDebuggingStorage().enabled === true) {
24512
24536
  // Include debug info from server
24513
24537
  queryString.debug = true;
24514
24538
  }
@@ -25667,10 +25691,13 @@ function connectChannelToStore(channel, store) {
25667
25691
  var closed = false;
25668
25692
  const waitingRequests = [];
25669
25693
  channel.addEventListener('message', function (message) {
25694
+ // connect-channel only routes inbound messages to the store; it never
25695
+ // re-runs the guide loop. Anything that changes what guides display
25696
+ // re-arms the loop from the specific store action that makes the change
25697
+ // (see scheduleGuideUpdate in frames.module). Re-running on every
25698
+ // inbound message instead made two framed guide loops wake each other
25699
+ // forever even when idle (APP-162302).
25670
25700
  messageHandler(message.data);
25671
- if (store.state.guideUpdate.useObserver) {
25672
- store.dispatch('guideUpdate/documentChanged');
25673
- }
25674
25701
  });
25675
25702
  return {
25676
25703
  postMessage,
@@ -33302,6 +33329,59 @@ function GuideStep(guide) {
33302
33329
  return;
33303
33330
  onGuideDismissed(this);
33304
33331
  };
33332
+ /**
33333
+ * Records a `guideActivity` analytics event for this Step. Intended for
33334
+ * code-block guides to report UI interactions (button clicks,
33335
+ * link opens, poll submissions) that the automatic click-capture path cannot
33336
+ * resolve from `domJson`. This only reports analytics;
33337
+ *
33338
+ * Because the details originate from customer-authored guide content, they are
33339
+ * validated before staging: a missing or non-string `elementId` drops the event,
33340
+ * an unrecognized `elementType` falls back to `'BUTTON'`, and malformed action
33341
+ * entries are filtered out (no event when none remain valid).
33342
+ *
33343
+ * @access public
33344
+ * @category Guides
33345
+ * @example
33346
+ * step.trackAction({
33347
+ * elementId: 'pendo-button-advance-btn',
33348
+ * elementType: 'BUTTON',
33349
+ * actions: [{ action: 'advanceGuide' }]
33350
+ * })
33351
+ * @param {Object} details
33352
+ * @param {string} details.elementId DOM id of the interacted element. Required; a missing or
33353
+ * non-string value drops the event.
33354
+ * @param {string} [details.elementType] Element type/tag. Coerced to one of BUTTON/A/IMG,
33355
+ * defaulting to 'BUTTON' when missing or unrecognized.
33356
+ * @param {Array<Object>} details.actions Action objects, e.g. `[{ action: 'advanceGuide' }]`. Entries
33357
+ * whose `action` is not a recognized guideActivity action name are dropped.
33358
+ * @returns {void}
33359
+ */
33360
+ this.trackAction = function (details) {
33361
+ details = details || {};
33362
+ if (!details.elementId || !_.isString(details.elementId))
33363
+ return;
33364
+ var actions = _.isArray(details.actions)
33365
+ ? details.actions
33366
+ : (details.action ? [_.omit(details, 'elementId', 'elementType', 'actions')] : []);
33367
+ actions = _.filter(actions, isValidTrackedAction);
33368
+ actions = _.map(actions, function (action) {
33369
+ if (action.action === 'openLink' && action.url) {
33370
+ return _.extend({}, action, { url: sanitizeUrl(action.url) });
33371
+ }
33372
+ return action;
33373
+ });
33374
+ var eventProps = {
33375
+ type: 'guideActivity',
33376
+ guideId: guide.id,
33377
+ stepId: this.id,
33378
+ ui_element_id: details.elementId,
33379
+ ui_element_type: normalizeUiElementType(details.elementType),
33380
+ ui_element_actions: actions,
33381
+ language: guide.language
33382
+ };
33383
+ stageGuideEvent(createGuideEvent(eventProps));
33384
+ };
33305
33385
  this.isPoweredByEnabled = function () {
33306
33386
  return this.hideCredits !== true;
33307
33387
  };
@@ -33655,6 +33735,7 @@ var FramesModule = (function () {
33655
33735
  context.commit('setGuidesLoaded');
33656
33736
  }
33657
33737
  updateMasterGuideList(context.state);
33738
+ scheduleGuideUpdate();
33658
33739
  },
33659
33740
  join(context) {
33660
33741
  if (context.state.disabled)
@@ -33898,6 +33979,7 @@ var FramesModule = (function () {
33898
33979
  stepId: payload.stepId,
33899
33980
  shown: payload.isShown
33900
33981
  });
33982
+ scheduleGuideUpdate();
33901
33983
  },
33902
33984
  hideGuides(context, guideIdsToHide) {
33903
33985
  guideIdsToHide = _.isArray(guideIdsToHide) ? guideIdsToHide : [];
@@ -33918,6 +34000,7 @@ var FramesModule = (function () {
33918
34000
  hideGuides();
33919
34001
  }
33920
34002
  context.commit('hideGuides');
34003
+ scheduleGuideUpdate();
33921
34004
  },
33922
34005
  guideStepHiddenInFrame(context, payload) {
33923
34006
  var wasShown = context.getters.isShownInFrame()(context.state.frameId, { id: payload.stepId });
@@ -33944,6 +34027,7 @@ var FramesModule = (function () {
33944
34027
  stepId: payload.stepId,
33945
34028
  shown: false
33946
34029
  });
34030
+ scheduleGuideUpdate();
33947
34031
  },
33948
34032
  fixDoubleDisplay(context, payload) {
33949
34033
  _.each(context.state.frames, function (frame, frameId) {
@@ -33967,6 +34051,7 @@ var FramesModule = (function () {
33967
34051
  if (!step)
33968
34052
  return;
33969
34053
  step.hide();
34054
+ scheduleGuideUpdate();
33970
34055
  },
33971
34056
  changeLastGuideStepSeen(context, lastGuideStepSeen) {
33972
34057
  crossFrameChannel.postMessage({
@@ -34099,6 +34184,7 @@ var FramesModule = (function () {
34099
34184
  },
34100
34185
  receiveFrameVisibility(context, message) {
34101
34186
  context.commit('setFrameVisibility', message);
34187
+ scheduleGuideUpdate();
34102
34188
  },
34103
34189
  addSelectionModeToFrames(context, message) {
34104
34190
  var options = message.options;
@@ -34504,6 +34590,18 @@ var FramesModule = (function () {
34504
34590
  return _.map(guides, cloneGuide);
34505
34591
  }
34506
34592
  })();
34593
+ // Nudge the guide loop after a cross-frame change that affects what can
34594
+ // display. Guarded on useObserver: when the mutation observer drives the loop
34595
+ // it idles while the page is quiet and needs an explicit nudge to react to
34596
+ // cross-frame state; when the observer is off the loop already polls on its
34597
+ // own. This is called only from the frames actions that actually change
34598
+ // display state (not from every inbound message), which is what lets two
34599
+ // framed guide loops go fully idle instead of pinging each other (APP-162302).
34600
+ function scheduleGuideUpdate() {
34601
+ if (store.state.guideUpdate.useObserver) {
34602
+ store.dispatch('guideUpdate/documentChanged');
34603
+ }
34604
+ }
34507
34605
  function updateMasterGuideList(state) {
34508
34606
  var guidesInThisFrame = _.indexBy(_.filter(getActiveGuides(), function (guide) {
34509
34607
  return !guide.isFrameProxy;
@@ -35918,6 +36016,11 @@ function startDebuggingModuleIfEnabled() {
35918
36016
  if (!debugging.enabled || !isLeader())
35919
36017
  return;
35920
36018
  store.commit('debugger/debuggingEnabled', true);
36019
+ if (pendo$1.guides && _.isUndefined(pendo$1.excludedGuides)) {
36020
+ // The current guide payload was requested without debug=true (e.g. debugging
36021
+ // was just enabled), so it lacks exclusion data; re-fetch to include it.
36022
+ forceGuideReload();
36023
+ }
35921
36024
  addDebuggingFunctions();
35922
36025
  if (dom('#pendo-client-debugger').length)
35923
36026
  return;
@@ -36248,10 +36351,19 @@ var DesignerConnect = (function () {
36248
36351
  }
36249
36352
  function launchDesignerListener(data, msg) {
36250
36353
  var token = msg.data.token;
36251
- if (!token)
36354
+ if (!token) {
36355
+ globalPendo.log('Designer launch request received with no token. Ignoring launch request.');
36356
+ postLaunchError(msg, 'missingToken');
36252
36357
  return;
36358
+ }
36253
36359
  if (token.publicAppId && token.publicAppId !== globalPendo.publicAppId) {
36254
36360
  globalPendo.log(`Designer launch information received was for public app ID ${token.publicAppId}, which does not match the current app ID ${globalPendo.publicAppId}. Ignoring launch request.`);
36361
+ postLaunchError(msg, 'appIdMismatch');
36362
+ return;
36363
+ }
36364
+ if (token.preferredAgentInstallType && token.preferredAgentInstallType !== getInstallType()) {
36365
+ globalPendo.log(`Designer launch information received was for install type ${token.preferredAgentInstallType}, which does not match the current install type ${getInstallType()}. Ignoring launch request.`);
36366
+ postLaunchError(msg, 'installTypeMismatch');
36255
36367
  return;
36256
36368
  }
36257
36369
  tempLaunchToken = token;
@@ -36293,6 +36405,11 @@ var DesignerConnect = (function () {
36293
36405
  pluginAPI.store.dispatch('frames/startPreview');
36294
36406
  }
36295
36407
  }
36408
+ function postLaunchError(msg, reason) {
36409
+ if (msg.source && globalPendo._.isFunction(msg.source.postMessage)) {
36410
+ msg.source.postMessage({ type: 'pendo-designer-launch::error', reason }, msg.origin);
36411
+ }
36412
+ }
36296
36413
  function removeListeners() {
36297
36414
  if (window.removeEventListener) {
36298
36415
  window.removeEventListener('message', messageReceiver, false);
@@ -39128,30 +39245,6 @@ function doSearch(guides, srchTxt) {
39128
39245
  .value();
39129
39246
  }
39130
39247
 
39131
- var lastLauncherHash;
39132
- var launcherProc = function () {
39133
- const guidesList = getActiveGuides();
39134
- if (isLeader()) {
39135
- if (!pendo$1.guideWidget || !pendo$1.guideWidget.enabled)
39136
- return;
39137
- // filter list down to launcher based guides
39138
- var launcherGuides = getLauncherGuideList(guidesList);
39139
- // add all launcher based guides
39140
- updateLauncher(launcherGuides, true);
39141
- }
39142
- else {
39143
- hideLauncher();
39144
- // Check for changes to what should display in the launcher
39145
- var frameGuides = frameLocalGuides(guidesList);
39146
- var launchers = getLauncherGuideList(frameGuides);
39147
- var launcherHash = computeLauncherHash(launchers);
39148
- if (launcherHash !== lastLauncherHash) {
39149
- store.dispatch('frames/changeGuideList', frameGuides);
39150
- }
39151
- lastLauncherHash = launcherHash;
39152
- }
39153
- };
39154
-
39155
39248
  var LAUNCHER_DEFAULT_WIDTH = 330;
39156
39249
  var LAUNCHER_DEFAULT_HEIGHT = 310;
39157
39250
  var LAUNCHER_CLOSED = 'launcher-closed';
@@ -39954,6 +40047,29 @@ var debuggerLaunched = function () {
39954
40047
  var loadLauncherContentHandler = function (activeGuides) {
39955
40048
  return loadLauncherContent(upgradeLauncher(pendo$1.guideWidget, activeGuides));
39956
40049
  };
40050
+ var lastLauncherHash;
40051
+ var launcherProc = function () {
40052
+ const guidesList = getActiveGuides();
40053
+ if (isLeader()) {
40054
+ if (!pendo$1.guideWidget || !pendo$1.guideWidget.enabled)
40055
+ return;
40056
+ // filter list down to launcher based guides
40057
+ var launcherGuides = getLauncherGuideList(guidesList);
40058
+ // add all launcher based guides
40059
+ updateLauncher(launcherGuides, true);
40060
+ }
40061
+ else {
40062
+ hideLauncher();
40063
+ // Check for changes to what should display in the launcher
40064
+ var frameGuides = frameLocalGuides(guidesList);
40065
+ var launchers = getLauncherGuideList(frameGuides);
40066
+ var launcherHash = computeLauncherHash(launchers);
40067
+ if (launcherHash !== lastLauncherHash) {
40068
+ store.dispatch('frames/changeGuideList', frameGuides);
40069
+ }
40070
+ lastLauncherHash = launcherHash;
40071
+ }
40072
+ };
39957
40073
  var initLauncherPlugin = function (pendo, PluginAPI) {
39958
40074
  loadLauncherPublicFunctions(pendo);
39959
40075
  PluginAPI.Events.on('guidesLoaded', guidesLoaded);
@@ -40584,7 +40700,7 @@ class DOMPrompt {
40584
40700
  return evt.isComposing !== undefined ? evt.isComposing : isComposingFromState;
40585
40701
  }
40586
40702
  isEnterKey(evt) {
40587
- return evt.code === 'Enter' || evt.key === 'Enter' || evt.keyCode === 13;
40703
+ return (evt.code === 'Enter' || evt.key === 'Enter' || evt.keyCode === 13) && !evt.shiftKey;
40588
40704
  }
40589
40705
  setupInputListeners() {
40590
40706
  this._.each(this.inputEls, (inputEl) => {
@@ -52348,6 +52464,7 @@ function maskSensitiveFields({ string, contentType, _ }) {
52348
52464
  }
52349
52465
 
52350
52466
  const DEV_LOG_TYPE = 'devlog';
52467
+ const DEV_LOG_SEND_INTERVAL = 5000;
52351
52468
  function createDevLogEnvelope(pluginAPI, globalPendo) {
52352
52469
  return {
52353
52470
  browser_time: pluginAPI.util.getNow(),
@@ -52576,7 +52693,7 @@ function ConsoleCapture() {
52576
52693
  if (!sendQueue.failed()) {
52577
52694
  send();
52578
52695
  }
52579
- }, pluginAPI.constants.SEND_INTERVAL);
52696
+ }, DEV_LOG_SEND_INTERVAL);
52580
52697
  sendQueue.start();
52581
52698
  pluginAPI.Events.ready.on(readyHandler);
52582
52699
  pluginAPI.Events['recording:unpaused'].on(recordingStarted);
@@ -52864,7 +52981,7 @@ function NetworkCapture() {
52864
52981
  if (!sendQueue.failed()) {
52865
52982
  send();
52866
52983
  }
52867
- }, pluginAPI.constants.SEND_INTERVAL);
52984
+ }, DEV_LOG_SEND_INTERVAL);
52868
52985
  sendQueue.start();
52869
52986
  pluginAPI.Events.ready.on(startCapture);
52870
52987
  pluginAPI.Events['recording:unpaused'].on(recordingStarted);
@@ -55214,6 +55331,46 @@ var _ = /*@__PURE__*/getDefaultExportFromCjs(underscoreExports);
55214
55331
 
55215
55332
  var encodeURIComponent = _.isFunction(window.encodeURIComponent) ? window.encodeURIComponent : _.identity;
55216
55333
 
55334
+ const WIDGET_SETTINGS_API = '/external/widget/settings';
55335
+ class PredictApiService {
55336
+ constructor({ baseUrl, token }) {
55337
+ this.performRequest = (url, options) => __awaiter(this, void 0, void 0, function* () {
55338
+ const response = yield fetch(url, options);
55339
+ const body = response.ok ? yield response.json() : null;
55340
+ return { ok: response.ok, status: response.status, body };
55341
+ });
55342
+ this.getWidgetSettingsById = (id, log) => __awaiter(this, void 0, void 0, function* () {
55343
+ var _a, _b;
55344
+ if (!id) {
55345
+ log('[predict] no id provided');
55346
+ return null;
55347
+ }
55348
+ try {
55349
+ const { ok, status, body } = yield this.performRequest(`${this.baseUrl}${WIDGET_SETTINGS_API}/${id}`, {
55350
+ method: 'GET',
55351
+ headers: { 'Content-Type': 'application/json', 'Authorization': this.token }
55352
+ });
55353
+ if (!ok) {
55354
+ log(`[predict] error getting widget settings by id: ${status}`);
55355
+ return null;
55356
+ }
55357
+ const data = body;
55358
+ if ((_a = data === null || data === void 0 ? void 0 : data.errors) === null || _a === void 0 ? void 0 : _a.length) {
55359
+ log(`[predict] error getting widget settings by id: ${data.errors}`);
55360
+ return null;
55361
+ }
55362
+ return (_b = data === null || data === void 0 ? void 0 : data.data) !== null && _b !== void 0 ? _b : null;
55363
+ }
55364
+ catch (error) {
55365
+ log(`[predict] error getting widget settings by id: ${error}`);
55366
+ return null;
55367
+ }
55368
+ });
55369
+ this.baseUrl = baseUrl;
55370
+ this.token = token;
55371
+ }
55372
+ }
55373
+
55217
55374
  const PREDICT_STEP_REGEX = /\$\$predict\$\$/;
55218
55375
  const DRAG_THRESHOLD = 5;
55219
55376
  const STYLE_ID = 'pendo-predict-plugin-styles';
@@ -55221,6 +55378,7 @@ const GUIDE_BUTTON_IDENTIFIER = 'button:contains("token")';
55221
55378
  const pluginVersion = '1.0.1';
55222
55379
  const PREDICT_SANDBOX_PAGE = 'predict-sandbox.html';
55223
55380
  const getPredictBaseUrl = (designerServer) => designerServer.replace('app', 'predict');
55381
+ const getPredictApiBaseUrl = (predictBaseUrl) => predictBaseUrl.replace('predict', 'predict-api');
55224
55382
  const EXTENSION_ORIGIN_REGEX = /^(chrome|moz|safari-web)-extension:\/\//;
55225
55383
  /**
55226
55384
  * When running inside the Pendo Launcher extension, a host page's CSP `frame-src`
@@ -55324,6 +55482,50 @@ const getRecordIdFromUrl = (recordRegex, log = () => { }) => {
55324
55482
  log(`[predict] recordId found: ${recordId}`);
55325
55483
  return recordId;
55326
55484
  };
55485
+ const escapeRegexKeyword = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
55486
+ // Builds the generalized keyword matcher: a simple keyword (single- or
55487
+ // multi-segment, e.g. "Company" or "lightning/r/Opportunity") that works
55488
+ // whether the id lives in the path or the query string. The keyword is escaped
55489
+ // so metacharacters are treated literally, a boundary avoids substring hijacks,
55490
+ // and the id ends naturally at any delimiter.
55491
+ const buildRecordRegex = (keyword) => new RegExp(`(?:^|[/?&=.#])${escapeRegexKeyword(keyword)}[/.=:_-]([a-zA-Z0-9-]+)`, 'i');
55492
+ // Resolves the record id from the current URL using three tiers, returning the
55493
+ // first non-null result:
55494
+ // 1. recordRegexPattern - an advanced, author-supplied full regex.
55495
+ // 2. recordRegexName - the generalized keyword matcher (path + query).
55496
+ // 3. legacy fallback - the original strict `/name/id` template, so every
55497
+ // pre-existing config keeps working with no migration.
55498
+ const resolveRecordId = (configuration, log = () => { }) => {
55499
+ var _a, _b, _c, _d;
55500
+ const { recordRegexPattern, recordRegexName } = configuration;
55501
+ // Tier 1: advanced full pattern (matched against the full href).
55502
+ if (recordRegexPattern) {
55503
+ try {
55504
+ const advancedId = (_b = (_a = window.location.href.match(new RegExp(recordRegexPattern))) === null || _a === void 0 ? void 0 : _a[1]) !== null && _b !== void 0 ? _b : null;
55505
+ if (advancedId) {
55506
+ log(`[predict] recordId found via recordRegexPattern: ${advancedId}`);
55507
+ return advancedId;
55508
+ }
55509
+ }
55510
+ catch (error) {
55511
+ log(`[predict] invalid recordRegexPattern, skipping: ${error}`);
55512
+ }
55513
+ }
55514
+ if (!recordRegexName) {
55515
+ return null;
55516
+ }
55517
+ // Tier 2: generalized keyword matcher against path + query + hash only
55518
+ // (host excluded to avoid subdomain false matches).
55519
+ const { pathname, search, hash } = window.location;
55520
+ const keywordId = (_d = (_c = `${pathname}${search}${hash}`.match(buildRecordRegex(recordRegexName))) === null || _c === void 0 ? void 0 : _c[1]) !== null && _d !== void 0 ? _d : null;
55521
+ if (keywordId) {
55522
+ log(`[predict] recordId found via recordRegexName: ${keywordId}`);
55523
+ return keywordId;
55524
+ }
55525
+ // Tier 3: legacy template fallback (preserves pre-existing configs).
55526
+ const legacyRegex = new RegExp(`/${recordRegexName}/([a-zA-Z0-9]+)(?:/|$)`);
55527
+ return getRecordIdFromUrl(legacyRegex, log);
55528
+ };
55327
55529
  const safeStringify = (value, fallback) => {
55328
55530
  try {
55329
55531
  return JSON.stringify(value);
@@ -55341,13 +55543,25 @@ const safeParse = (value, fallback, log = () => { }) => {
55341
55543
  return fallback;
55342
55544
  }
55343
55545
  };
55546
+ const getAvailableParams = (params) => {
55547
+ // No need to pass the full configuration when widgetId is present
55548
+ if (params.widgetId) {
55549
+ return {
55550
+ widgetId: params.widgetId,
55551
+ pipeVersion: params.pipeVersion,
55552
+ forceAccountId: params.forceAccountId
55553
+ };
55554
+ }
55555
+ return params;
55556
+ };
55344
55557
  const buildQueryParams = (params) => {
55345
55558
  const urlParams = new URLSearchParams();
55346
- const entries = Object.entries(params);
55559
+ const availableParams = getAvailableParams(params);
55560
+ const entries = Object.entries(availableParams);
55347
55561
  urlParams.set('pluginVersion', pluginVersion);
55348
55562
  for (let i = 0; i < entries.length; i++) {
55349
55563
  const [key, value] = entries[i];
55350
- if (value != null) {
55564
+ if (value !== undefined && value !== null) {
55351
55565
  urlParams.set(key, typeof value === 'object' ? safeStringify(value) : String(value));
55352
55566
  }
55353
55567
  }
@@ -55453,10 +55667,16 @@ const createExplanationIframe = ({ analysisId, recordId, configuration, predictB
55453
55667
  };
55454
55668
  // ----- Floating Modal Creation -----
55455
55669
  const createFloatingModal = ({ recordId, configuration, predictBaseUrl, wrapperBase = '' }) => {
55456
- var _a;
55457
- const flatIds = (_a = configuration.analysisIds) === null || _a === void 0 ? void 0 : _a.flat(Infinity);
55670
+ var _a, _b;
55671
+ const flatIds = ((_b = (_a = configuration.analysisIds) === null || _a === void 0 ? void 0 : _a.flat(Infinity)) !== null && _b !== void 0 ? _b : []);
55458
55672
  for (let i = 0; i < flatIds.length; i++) {
55459
- createExplanationIframe({ analysisId: flatIds[i], recordId, configuration, predictBaseUrl, wrapperBase });
55673
+ createExplanationIframe({
55674
+ analysisId: flatIds[i],
55675
+ recordId,
55676
+ configuration,
55677
+ predictBaseUrl,
55678
+ wrapperBase
55679
+ });
55460
55680
  }
55461
55681
  const container = createElement('div', { id: 'floatingModalContainer', className: 'floating-modal-container' }, document.body);
55462
55682
  const dragHandle = createElement('div', {
@@ -55530,7 +55750,14 @@ const setupMessageListener = ({ token, forceAccountId, cleanup, pendo, predictBa
55530
55750
  }
55531
55751
  const visitorId = (_a = pendo === null || pendo === void 0 ? void 0 : pendo.getVisitorId) === null || _a === void 0 ? void 0 : _a.call(pendo);
55532
55752
  const accountId = forceAccountId !== null && forceAccountId !== void 0 ? forceAccountId : (_b = pendo === null || pendo === void 0 ? void 0 : pendo.getAccountId) === null || _b === void 0 ? void 0 : _b.call(pendo);
55533
- (_c = iframe.contentWindow) === null || _c === void 0 ? void 0 : _c.postMessage({ type: 'TOKEN_RECEIVED', token, idToken, extensionAPIKeys, visitorId, accountId }, frameTargetOrigin);
55753
+ (_c = iframe.contentWindow) === null || _c === void 0 ? void 0 : _c.postMessage({
55754
+ type: 'TOKEN_RECEIVED',
55755
+ token,
55756
+ idToken,
55757
+ extensionAPIKeys,
55758
+ visitorId,
55759
+ accountId
55760
+ }, frameTargetOrigin);
55534
55761
  }
55535
55762
  },
55536
55763
  OPEN_EXPLANATION: () => {
@@ -55578,8 +55805,24 @@ const setupMessageListener = ({ token, forceAccountId, cleanup, pendo, predictBa
55578
55805
  window.addEventListener('message', messageHandler);
55579
55806
  return () => window.removeEventListener('message', messageHandler);
55580
55807
  };
55581
- const predictGuidesScript = ({ step, pendo, cleanupArray, cleanup, log, designerServer }) => {
55582
- var _a;
55808
+ const mapAnalysisIds = (analysesHierarchy) => analysesHierarchy === null || analysesHierarchy === void 0 ? void 0 : analysesHierarchy.map(hierarchy => hierarchy.map(item => item.analysisId));
55809
+ // Loads the widget's stored settings from the predict API, which override
55810
+ // whatever was inlined in the guide button. Any failure logs and returns the inline configuration.
55811
+ const fetchWidgetConfiguration = (_a) => __awaiter(void 0, void 0, void 0, function* () {
55812
+ var _b;
55813
+ var { token, predictBaseUrl, log } = _a, inlineConfiguration = __rest(_a, ["token", "predictBaseUrl", "log"]);
55814
+ const predictApiService = new PredictApiService({
55815
+ baseUrl: getPredictApiBaseUrl(predictBaseUrl),
55816
+ token
55817
+ });
55818
+ const widgetSettings = yield predictApiService.getWidgetSettingsById((_b = inlineConfiguration.widgetId) !== null && _b !== void 0 ? _b : '', log);
55819
+ if (!widgetSettings) {
55820
+ return inlineConfiguration;
55821
+ }
55822
+ return Object.assign(Object.assign({}, inlineConfiguration), { analysisIds: mapAnalysisIds(widgetSettings.analysesHierarchy), recordRegexPattern: widgetSettings.recordRegexPattern, recordRegexName: widgetSettings.recordRegexKeyword });
55823
+ });
55824
+ const predictGuidesScript = ({ step, pendo, cleanupArray, cleanup, log, designerServer }) => __awaiter(void 0, void 0, void 0, function* () {
55825
+ var _c;
55583
55826
  log('[predict] initializing');
55584
55827
  // Before anything else, inject styles so that the guide will be hidden
55585
55828
  const pendoContainerId = step === null || step === void 0 ? void 0 : step.containerId;
@@ -55588,33 +55831,47 @@ const predictGuidesScript = ({ step, pendo, cleanupArray, cleanup, log, designer
55588
55831
  log('[predict] cleanupArray is not empty');
55589
55832
  return;
55590
55833
  }
55834
+ if (!designerServer) {
55835
+ log('[predict] no designerServer found, aborting');
55836
+ return;
55837
+ }
55838
+ const predictBaseUrl = getPredictBaseUrl(designerServer);
55591
55839
  // ----- Extract Configuration -----
55592
- const guideButton = (_a = step.guideElement.find(GUIDE_BUTTON_IDENTIFIER)) === null || _a === void 0 ? void 0 : _a[0];
55840
+ const guideButton = (_c = step.guideElement.find(GUIDE_BUTTON_IDENTIFIER)) === null || _c === void 0 ? void 0 : _c[0];
55593
55841
  if (!guideButton) {
55594
55842
  log('[predict] no guide button found, aborting');
55595
55843
  return;
55596
55844
  }
55597
- const _b = safeParse(guideButton.textContent, {}, log), { token } = _b, configuration = __rest(_b, ["token"]);
55598
- if (!configuration || !token) {
55845
+ const _d = safeParse(guideButton.textContent, {}, log), { token } = _d, inlineConfiguration = __rest(_d, ["token"]);
55846
+ if (!inlineConfiguration || !token) {
55599
55847
  log('[predict] no configuration found, aborting');
55600
55848
  return;
55601
55849
  }
55602
- const recordRegex = new RegExp(`/${configuration.recordRegexName}/([a-zA-Z0-9]+)(?:/|$)`);
55603
- const recordId = getRecordIdFromUrl(recordRegex, log);
55850
+ // Widget-backed guides load their settings from the predict API; those
55851
+ // override whatever was inlined in the guide button. Non-widget guides use
55852
+ // the inlined configuration as-is.
55853
+ const configuration = (inlineConfiguration === null || inlineConfiguration === void 0 ? void 0 : inlineConfiguration.widgetId)
55854
+ ? yield fetchWidgetConfiguration(Object.assign(Object.assign({}, inlineConfiguration), { token,
55855
+ predictBaseUrl,
55856
+ log }))
55857
+ : inlineConfiguration;
55858
+ const recordId = resolveRecordId(configuration, log);
55604
55859
  if (!recordId) {
55605
55860
  log('[predict] no recordId found in URL, aborting');
55606
55861
  return;
55607
55862
  }
55608
- if (!designerServer) {
55609
- log('[predict] no designerServer found, aborting');
55610
- return;
55611
- }
55612
- const predictBaseUrl = getPredictBaseUrl(designerServer);
55613
55863
  const wrapperBase = getFrameWrapperBase(pendo);
55614
55864
  log(`[predict] predictBaseUrl: ${predictBaseUrl}${wrapperBase ? `, wrapping via ${wrapperBase}` : ''}`);
55615
- cleanupArray.push(setupMessageListener({ token, forceAccountId: configuration === null || configuration === void 0 ? void 0 : configuration.forceAccountId, cleanup, pendo, predictBaseUrl, wrapperBase }));
55865
+ cleanupArray.push(setupMessageListener({
55866
+ token,
55867
+ forceAccountId: configuration.forceAccountId,
55868
+ cleanup,
55869
+ pendo,
55870
+ predictBaseUrl,
55871
+ wrapperBase
55872
+ }));
55616
55873
  cleanupArray.push(createFloatingModal({ recordId, configuration, predictBaseUrl, wrapperBase }));
55617
- };
55874
+ });
55618
55875
 
55619
55876
  const PredictGuides = () => {
55620
55877
  let pluginApiRef = null;
@@ -55649,7 +55906,10 @@ const PredictGuides = () => {
55649
55906
  var _a, _b;
55650
55907
  const designerServer = ((_a = pendo === null || pendo === void 0 ? void 0 : pendo.getConfigValue) === null || _a === void 0 ? void 0 : _a.call(pendo, 'designerServer')) ||
55651
55908
  ((_b = pendo === null || pendo === void 0 ? void 0 : pendo.getConfigValue) === null || _b === void 0 ? void 0 : _b.call(pendo, 'server'));
55652
- predictGuidesScript({ step, pendo, cleanupArray, cleanup, log, designerServer });
55909
+ Promise$2.resolve(predictGuidesScript({ step, pendo, cleanupArray, cleanup, log, designerServer })).catch((error) => {
55910
+ log(`[predict] predictGuidesScript crashed, tearing down: ${error}`);
55911
+ cleanup();
55912
+ });
55653
55913
  this.on('unmounted', (evt) => {
55654
55914
  if (evt.reason !== 'hidden')
55655
55915
  cleanup();