@ada-support/embed2 1.6.53 → 1.7.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.
@@ -0,0 +1,23 @@
1
+ import type { StartOptions } from "@ada-support/embed-types";
2
+ import { Client } from "common/models/client";
3
+ export declare const PRIMARY_BOT_ASSIGNMENT_LABEL = "primary";
4
+ export declare const ALTERNATIVE_BOT_ASSIGNMENT_LABEL = "alternative";
5
+ export declare const BOT_ASSIGNMENT_STORAGE_KEY = "botAssignment";
6
+ export declare function getBotOverrideHandleFromWebpageURL(): string;
7
+ /**
8
+ * Uses the alternative rollout value to determine whether a primary or alternative bot should be assigned to a chatter.
9
+ *
10
+ * If an override is provided in the URL, then it should be used but without impact to the bot assignment in storage.
11
+ *
12
+ * If a value of 0 is used for the alternative rollout value, then the primary bot should be used without impact to the bot assignment in storage.
13
+ *
14
+ * If a value of 1 is used for the alternative rollout value, then the alternative bot should be used without impact to the bot assignment in storage.
15
+ *
16
+ * @param primaryBotClientObject
17
+ * @param alternativeBot
18
+ * @param adaSettings
19
+ */
20
+ export declare function fetchClientUsingAlternativeBotRollout(primaryBotClientObject: Client, alternativeBot: null | {
21
+ handle: string;
22
+ rollout: number;
23
+ }, adaSettings: StartOptions): Promise<Client | null>;
@@ -18,11 +18,11 @@ export interface CSInterface {
18
18
  isEmbedFramed?: boolean;
19
19
  }
20
20
  export declare function getClientCacheUrl(handle: string, cluster?: AdaCluster, domain?: string): string;
21
- export declare function getEmbedURL({ frameName, handle, cluster, domain, }: GetEmbedUrlParams): string;
21
+ export declare function getEmbedURL({ frameName, handle, cluster, domain }: GetEmbedUrlParams): string;
22
22
  /**
23
23
  * Generate the Chat / API URL
24
24
  */
25
- export declare function getURL({ name, handle, cluster, domain, route, qp, }: CSInterface): string;
25
+ export declare function getURL({ name, handle, cluster, domain, route, qp }: CSInterface): string;
26
26
  export declare function getChatURL({ handle, version, cluster, domain, qp, }: {
27
27
  handle: string;
28
28
  version?: string;
@@ -8,6 +8,10 @@ export declare class Client {
8
8
  intro?: CurrentIntro;
9
9
  handle: string;
10
10
  rollout: number;
11
+ alternative_bot: null | {
12
+ handle: string;
13
+ rollout: number;
14
+ };
11
15
  language: LanguageKey;
12
16
  privacy: boolean;
13
17
  features: Features;
@@ -23,6 +23,10 @@ export interface Features {
23
23
  export interface ClientObject {
24
24
  handle: string;
25
25
  rollout: number;
26
+ alternative_bot: null | {
27
+ handle: string;
28
+ rollout: number;
29
+ };
26
30
  chat: boolean;
27
31
  persistence: string;
28
32
  language: LanguageKey;
@@ -38,6 +42,10 @@ export interface ClientObject {
38
42
  export interface CachedClientObject {
39
43
  handle: string;
40
44
  rollout: number;
45
+ alternative_bot: null | {
46
+ handle: string;
47
+ rollout: number;
48
+ };
41
49
  chat: boolean;
42
50
  persistence: string;
43
51
  language: LanguageKey;
@@ -32,7 +32,7 @@ var createStorage = function (storageType) {
32
32
  var storage = {
33
33
  setItem: function (key, value) {
34
34
  try {
35
- storageProvider === null || storageProvider === void 0 ? void 0 : storageProvider.setItem(key, JSON.stringify(value));
35
+ storageProvider.setItem(key, JSON.stringify(value));
36
36
  }
37
37
  catch (e) {
38
38
  console.warn(WEB_STORAGE_ACCESS_ERROR_MESSAGE);
@@ -40,7 +40,7 @@ var createStorage = function (storageType) {
40
40
  },
41
41
  getItem: function (key) {
42
42
  try {
43
- var rawStorageValue = storageProvider === null || storageProvider === void 0 ? void 0 : storageProvider.getItem(key);
43
+ var rawStorageValue = storageProvider.getItem(key);
44
44
  return rawStorageValue && JSON.parse(rawStorageValue);
45
45
  }
46
46
  catch (e) {
@@ -50,7 +50,7 @@ var createStorage = function (storageType) {
50
50
  },
51
51
  removeItem: function (key) {
52
52
  try {
53
- storageProvider === null || storageProvider === void 0 ? void 0 : storageProvider.removeItem(key);
53
+ storageProvider.removeItem(key);
54
54
  }
55
55
  catch (e) {
56
56
  console.warn(WEB_STORAGE_ACCESS_ERROR_MESSAGE);
@@ -58,7 +58,7 @@ var createStorage = function (storageType) {
58
58
  },
59
59
  clear: function () {
60
60
  try {
61
- storageProvider === null || storageProvider === void 0 ? void 0 : storageProvider.clear();
61
+ storageProvider.clear();
62
62
  }
63
63
  catch (e) {
64
64
  console.warn(WEB_STORAGE_ACCESS_ERROR_MESSAGE);
@@ -13260,7 +13260,7 @@ const client = new error_tracker_BrowserClient({
13260
13260
  return event;
13261
13261
  },
13262
13262
  environment: "production",
13263
- release: "1.6.53-508e85e",
13263
+ release: "1.7.0-b0e29e3",
13264
13264
  sampleRate: 0.25,
13265
13265
  autoSessionTracking: false,
13266
13266
  // Integrations don't seem to work with Sentry: https://github.com/getsentry/sentry-javascript/issues/2541
@@ -13830,9 +13830,9 @@ function getEmbedURL(_ref) {
13830
13830
  if (isProduction) {
13831
13831
  host = `https://${handle}${clusterString}.${domainString}.support`;
13832
13832
  } else {
13833
- host = `http://${window.location.hostname}:9001`;
13833
+ host = `http://${handle}.localhost:9001`;
13834
13834
  }
13835
- return `${host}/embed/${frameName}/${"508e85e"}/index.html`;
13835
+ return `${host}/embed/${frameName}/${"b0e29e3"}/index.html`;
13836
13836
  }
13837
13837
  function constructQueryString(query) {
13838
13838
  return Object.keys(query).map(key => {
@@ -13867,7 +13867,7 @@ function getURL(_ref2) {
13867
13867
  }
13868
13868
  if (window.location.hostname.includes("localhost") || handle === "e2ereference") {
13869
13869
  const domainName = window.location.hostname.includes("localhost") ? "localhost" : "e2ereference";
13870
- const host = `${window.location.hostname}:${ports[domainName].api}`;
13870
+ const host = `${handle}.${domainName}:${ports[domainName].api}`;
13871
13871
  return `http://${host}/${routeString}${questionSym}${queryString}`;
13872
13872
  }
13873
13873
  return prodUrl;
@@ -14785,6 +14785,401 @@ const getCampaignToTrigger = (adaSettings, marketingCampaigns, options, onlyBasi
14785
14785
  return conditionsMet && doesPassFrequencyCheck;
14786
14786
  });
14787
14787
  };
14788
+ ;// CONCATENATED MODULE: ./src/common/helpers/fetch-client.ts
14789
+
14790
+
14791
+
14792
+
14793
+ function isClientObject(client) {
14794
+ // We require the following properties defined
14795
+ return ["handle", "rollout", "chat", "features"].every(key => client[key] !== undefined);
14796
+ }
14797
+
14798
+ /**
14799
+ * Tries fetching the client from cache. If this fails, tries fetching it from API.
14800
+ * If this also fails, returns null.
14801
+ */
14802
+ async function fetchClient(handle, cluster, domain, language) {
14803
+ try {
14804
+ const cachedClient = await httpRequest({
14805
+ url: getClientCacheUrl(handle, cluster, domain)
14806
+ });
14807
+
14808
+ // We may not have "rollout" data cached for all clients
14809
+ // In this case, don't return here and instead try to get it from API below
14810
+ // Also fall back to API if the cached client does not match the expected format
14811
+ if (isClientObject(cachedClient)) {
14812
+ return {
14813
+ client: cachedClient
14814
+ };
14815
+ }
14816
+ } catch (e) {
14817
+ if (e instanceof Error) {
14818
+ error_tracker.trackException(e);
14819
+ }
14820
+ }
14821
+
14822
+ // If we failed to get the client from cache, try getting it directly from API
14823
+ try {
14824
+ return await httpRequest({
14825
+ url: getURL({
14826
+ name: "api",
14827
+ handle,
14828
+ cluster,
14829
+ domain,
14830
+ qp: {
14831
+ url: window.location.href,
14832
+ language
14833
+ }
14834
+ })
14835
+ });
14836
+ } catch (e) {
14837
+ // Do not trackException here because this can fail a lot due to poor network connections
14838
+ warn("Failed to fetch client from API.");
14839
+ }
14840
+ return null;
14841
+ }
14842
+ // EXTERNAL MODULE: ./node_modules/@babel/runtime-corejs3/core-js/object/get-own-property-symbols.js
14843
+ var get_own_property_symbols = __webpack_require__(3263);
14844
+ // EXTERNAL MODULE: ./node_modules/@babel/runtime-corejs3/core-js/instance/index-of.js
14845
+ var index_of = __webpack_require__(5683);
14846
+ // EXTERNAL MODULE: ./node_modules/@babel/runtime-corejs3/core-js/object/keys.js
14847
+ var keys = __webpack_require__(9356);
14848
+ ;// CONCATENATED MODULE: ./node_modules/@babel/runtime-corejs3/helpers/esm/objectWithoutPropertiesLoose.js
14849
+
14850
+
14851
+ function _objectWithoutPropertiesLoose(source, excluded) {
14852
+ if (source == null) return {};
14853
+ var target = {};
14854
+
14855
+ var sourceKeys = keys(source);
14856
+
14857
+ var key, i;
14858
+
14859
+ for (i = 0; i < sourceKeys.length; i++) {
14860
+ key = sourceKeys[i];
14861
+ if (index_of(excluded).call(excluded, key) >= 0) continue;
14862
+ target[key] = source[key];
14863
+ }
14864
+
14865
+ return target;
14866
+ }
14867
+ ;// CONCATENATED MODULE: ./node_modules/@babel/runtime-corejs3/helpers/esm/objectWithoutProperties.js
14868
+
14869
+
14870
+
14871
+ function _objectWithoutProperties(source, excluded) {
14872
+ if (source == null) return {};
14873
+ var target = _objectWithoutPropertiesLoose(source, excluded);
14874
+ var key, i;
14875
+
14876
+ if (get_own_property_symbols) {
14877
+ var sourceSymbolKeys = get_own_property_symbols(source);
14878
+
14879
+ for (i = 0; i < sourceSymbolKeys.length; i++) {
14880
+ key = sourceSymbolKeys[i];
14881
+ if (index_of(excluded).call(excluded, key) >= 0) continue;
14882
+ if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
14883
+ target[key] = source[key];
14884
+ }
14885
+ }
14886
+
14887
+ return target;
14888
+ }
14889
+ ;// CONCATENATED MODULE: ./src/common/models/client/index.ts
14890
+
14891
+
14892
+ const _excluded = ["chat", "chat_button", "features", "handle", "language", "persistence", "privacy", "rollout", "alternative_bot", "tint", "translated_languages"];
14893
+
14894
+ class Client {
14895
+ constructor(clientResponse) {
14896
+ _defineProperty(this, "tint", void 0);
14897
+ _defineProperty(this, "chat", void 0);
14898
+ _defineProperty(this, "intro", void 0);
14899
+ _defineProperty(this, "handle", void 0);
14900
+ _defineProperty(this, "rollout", void 0);
14901
+ _defineProperty(this, "alternative_bot", void 0);
14902
+ _defineProperty(this, "language", void 0);
14903
+ _defineProperty(this, "privacy", void 0);
14904
+ _defineProperty(this, "features", void 0);
14905
+ _defineProperty(this, "persistence", void 0);
14906
+ _defineProperty(this, "chat_button", void 0);
14907
+ _defineProperty(this, "business_events", void 0);
14908
+ _defineProperty(this, "marketing_campaigns_order", void 0);
14909
+ _defineProperty(this, "marketing_campaigns", void 0);
14910
+ _defineProperty(this, "ui_settings", void 0);
14911
+ _defineProperty(this, "translated_languages", void 0);
14912
+ const {
14913
+ chat,
14914
+ chat_button: chatButton,
14915
+ features,
14916
+ handle,
14917
+ language,
14918
+ persistence,
14919
+ privacy,
14920
+ rollout,
14921
+ // eslint-disable-next-line @typescript-eslint/naming-convention
14922
+ alternative_bot,
14923
+ tint,
14924
+ // eslint-disable-next-line @typescript-eslint/naming-convention
14925
+ translated_languages
14926
+ } = clientResponse,
14927
+ rest = _objectWithoutProperties(clientResponse, _excluded);
14928
+ this.chat = chat;
14929
+ this.chat_button = chatButton;
14930
+ this.features = features;
14931
+ this.handle = handle;
14932
+ this.language = language;
14933
+ this.persistence = persistence;
14934
+ this.privacy = privacy;
14935
+ this.rollout = rollout;
14936
+ this.tint = tint;
14937
+ this.translated_languages = translated_languages;
14938
+ this.alternative_bot = alternative_bot;
14939
+
14940
+ /**
14941
+ * Even if some properties are not used by Embed2, they may be used by Chat.
14942
+ * Chat can "pull" the client object from Embed2 in order to reduce an API
14943
+ * request. Embed2 should thus not modify keys.
14944
+ */
14945
+ Object.assign(this, rest);
14946
+ }
14947
+ }
14948
+ function getButtonText(client) {
14949
+ var _client$ui_settings, _client$ui_settings$e;
14950
+ const buttonTextMap = client === null || client === void 0 ? void 0 : (_client$ui_settings = client.ui_settings) === null || _client$ui_settings === void 0 ? void 0 : (_client$ui_settings$e = _client$ui_settings.embed) === null || _client$ui_settings$e === void 0 ? void 0 : _client$ui_settings$e.button_text;
14951
+ const languageKey = get_browser_language();
14952
+ if (!buttonTextMap || !buttonTextMap[languageKey]) {
14953
+ return "Chat";
14954
+ }
14955
+ return buttonTextMap[languageKey];
14956
+ }
14957
+ // EXTERNAL MODULE: ./node_modules/lodash.memoize/index.js
14958
+ var lodash_memoize = __webpack_require__(773);
14959
+ var lodash_memoize_default = /*#__PURE__*/__webpack_require__.n(lodash_memoize);
14960
+ ;// CONCATENATED MODULE: ./src/services/logger/index.ts
14961
+
14962
+ function logger_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
14963
+ function logger_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? logger_ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : logger_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
14964
+
14965
+
14966
+ const DEFAULT_LOG_SAMPLE_RATE = 0.01; // 1% of logs will go through
14967
+
14968
+ const DD_BASE_URL = "https://browser-http-intake.logs.datadoghq.com/v1/input/";
14969
+ const DD_TOKEN = "pubfe23baedd2ea322bebb5ed2020fa2fa1";
14970
+
14971
+ // We need memoization to ensure consistency of logs
14972
+ const shouldLog = lodash_memoize_default()(sampleRate => {
14973
+ if (!DD_TOKEN || sampleRate === 0) {
14974
+ return false;
14975
+ }
14976
+ return Math.random() < (sampleRate || DEFAULT_LOG_SAMPLE_RATE);
14977
+ });
14978
+
14979
+ /**
14980
+ * Sends log to Datadog
14981
+ */
14982
+ async function log(message, extra, options) {
14983
+ if (!shouldLog(options === null || options === void 0 ? void 0 : options.sampleRate)) {
14984
+ return;
14985
+ }
14986
+ await httpRequest({
14987
+ url: `${DD_BASE_URL}${DD_TOKEN}?ddsource=browser&ddtags=version:1.5.0,env:${"production"}`,
14988
+ method: "POST",
14989
+ body: JSON.stringify(logger_objectSpread(logger_objectSpread({
14990
+ message
14991
+ }, extra), {}, {
14992
+ sampleRate: (options === null || options === void 0 ? void 0 : options.sampleRate) || DEFAULT_LOG_SAMPLE_RATE,
14993
+ service: "embed",
14994
+ env: "production",
14995
+ embedVersion: 2,
14996
+ version: "1.7.0",
14997
+ isNpm: true,
14998
+ commitHash: "b0e29e3"
14999
+ }))
15000
+ });
15001
+ }
15002
+ ;// CONCATENATED MODULE: ./src/client/helpers/alternative-bot-rollout/index.ts
15003
+
15004
+ function alternative_bot_rollout_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
15005
+ function alternative_bot_rollout_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? alternative_bot_rollout_ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : alternative_bot_rollout_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
15006
+
15007
+
15008
+
15009
+
15010
+
15011
+ const PRIMARY_BOT_ASSIGNMENT_LABEL = "primary";
15012
+ const ALTERNATIVE_BOT_ASSIGNMENT_LABEL = "alternative";
15013
+ const BOT_ASSIGNMENT_STORAGE_KEY = "botAssignment";
15014
+ /**
15015
+ * Using the alternative bot rollout value from the client object, determines whether
15016
+ * the primary or alternative bot should be assigned to the chatter
15017
+ * using a random value.
15018
+ *
15019
+ * @param alternativeRolloutValue
15020
+ */
15021
+ function getAssignmentUsingAlternativeBotRolloutValue(alternativeRolloutValue) {
15022
+ // rollout values use a maximum of 2 decimal digits
15023
+ const randomValue = parseFloat(Math.random().toFixed(2));
15024
+ if (randomValue <= alternativeRolloutValue) {
15025
+ return ALTERNATIVE_BOT_ASSIGNMENT_LABEL;
15026
+ }
15027
+ return PRIMARY_BOT_ASSIGNMENT_LABEL;
15028
+ }
15029
+
15030
+ /**
15031
+ * Using the primary client handle (the default bot that is set on the page),
15032
+ * retrieves the bot assignment (primary or alternative) from the browser storage.
15033
+ *
15034
+ * @param primaryClientHandle
15035
+ */
15036
+ function getBotAssignmentFromBrowserStorage(primaryClientHandle) {
15037
+ const botAssignment = dist/* adaLocalStorage.getFnItem */.wG.getFnItem(BOT_ASSIGNMENT_STORAGE_KEY);
15038
+
15039
+ // the chatter has never been assigned a bot
15040
+ if (!botAssignment) {
15041
+ return null;
15042
+ }
15043
+
15044
+ // the chatter has been assigned an alternative bot
15045
+ if (botAssignment[primaryClientHandle] === ALTERNATIVE_BOT_ASSIGNMENT_LABEL) {
15046
+ return ALTERNATIVE_BOT_ASSIGNMENT_LABEL;
15047
+ }
15048
+
15049
+ // the chatter has been assigned a primary bot
15050
+ if (botAssignment[primaryClientHandle] === PRIMARY_BOT_ASSIGNMENT_LABEL) {
15051
+ return PRIMARY_BOT_ASSIGNMENT_LABEL;
15052
+ }
15053
+
15054
+ // the value in browser storage for bot assignment is not one we recognize
15055
+ return null;
15056
+ }
15057
+
15058
+ /**
15059
+ * Returns the primaryBotClientObject if botAssignment is primary.
15060
+ *
15061
+ * Else fetches the alternative bot's client object when botAssignment is alternative.
15062
+ *
15063
+ * Returns null if the request for the alternative bot's client fails.
15064
+ *
15065
+ * @param botAssignment
15066
+ * @param primaryBotClientObject
15067
+ * @param alternativeBotHandle
15068
+ * @param adaSettings
15069
+ */
15070
+ async function getClientObjectUsingBotAssignment(botAssignment, primaryBotClientObject, alternativeBotHandle, adaSettings) {
15071
+ const {
15072
+ cluster,
15073
+ domain,
15074
+ language
15075
+ } = adaSettings;
15076
+ if (botAssignment === PRIMARY_BOT_ASSIGNMENT_LABEL) {
15077
+ return primaryBotClientObject;
15078
+ }
15079
+ if (botAssignment === ALTERNATIVE_BOT_ASSIGNMENT_LABEL) {
15080
+ const alternativeBotClientResponse = await fetchClient(alternativeBotHandle, cluster, domain, language || get_browser_language());
15081
+ if (alternativeBotClientResponse) {
15082
+ return new Client(alternativeBotClientResponse.client);
15083
+ }
15084
+
15085
+ // if a chatter was assigned the alternative bot, but we couldn't load it
15086
+ // then we won't show any bot
15087
+ return null;
15088
+ }
15089
+ return null;
15090
+ }
15091
+
15092
+ /**
15093
+ * Stores the bot assigned in browser storage for retrieval later
15094
+ * when the chatter navigates to a new page.
15095
+ *
15096
+ * @param botAssignment
15097
+ * @param primaryBotHandle
15098
+ */
15099
+ function setBotAssignmentInBrowserStorage(botAssignment, primaryBotHandle) {
15100
+ const storedBotAssignments = dist/* adaLocalStorage.getFnItem */.wG.getFnItem(BOT_ASSIGNMENT_STORAGE_KEY) || {};
15101
+ dist/* adaLocalStorage.setFnItem */.wG.setFnItem(BOT_ASSIGNMENT_STORAGE_KEY, alternative_bot_rollout_objectSpread(alternative_bot_rollout_objectSpread({}, storedBotAssignments), {}, {
15102
+ [primaryBotHandle]: botAssignment
15103
+ }));
15104
+ }
15105
+
15106
+ /**
15107
+ * Returns the client object that should be used for initialization.
15108
+ *
15109
+ * If there is a bot assignment in browser storage, then this should be used.
15110
+ *
15111
+ * Otherwise, assign a bot and use it.
15112
+ *
15113
+ * @param primaryBotClientObject
15114
+ * @param alternativeBotHandle
15115
+ * @param alternativeBotRolloutValue
15116
+ * @param adaSettings
15117
+ */
15118
+ async function getClientObjectForEmbedInitialization(primaryBotClientObject, alternativeBotHandle, alternativeBotRolloutValue, adaSettings) {
15119
+ const botAssignmentFromStorage = getBotAssignmentFromBrowserStorage(primaryBotClientObject.handle);
15120
+ if (botAssignmentFromStorage) {
15121
+ return getClientObjectUsingBotAssignment(botAssignmentFromStorage, primaryBotClientObject, alternativeBotHandle, adaSettings);
15122
+ }
15123
+ const newBotAssignment = getAssignmentUsingAlternativeBotRolloutValue(alternativeBotRolloutValue);
15124
+ setBotAssignmentInBrowserStorage(newBotAssignment, primaryBotClientObject.handle);
15125
+ if (newBotAssignment === ALTERNATIVE_BOT_ASSIGNMENT_LABEL) {
15126
+ log("Alternative Bot Rollout", {
15127
+ primaryBot: primaryBotClientObject.handle,
15128
+ alternativeBotRollout: alternativeBotRolloutValue,
15129
+ alternativeBot: alternativeBotHandle
15130
+ }, {
15131
+ sampleRate: 1
15132
+ });
15133
+ }
15134
+ return getClientObjectUsingBotAssignment(newBotAssignment, primaryBotClientObject, alternativeBotHandle, adaSettings);
15135
+ }
15136
+ function getBotOverrideHandleFromWebpageURL() {
15137
+ const webpageURL = new URL(window.location.href);
15138
+ return webpageURL.searchParams.get("adaBotHandleOverride");
15139
+ }
15140
+
15141
+ /**
15142
+ * Uses the alternative rollout value to determine whether a primary or alternative bot should be assigned to a chatter.
15143
+ *
15144
+ * If an override is provided in the URL, then it should be used but without impact to the bot assignment in storage.
15145
+ *
15146
+ * If a value of 0 is used for the alternative rollout value, then the primary bot should be used without impact to the bot assignment in storage.
15147
+ *
15148
+ * If a value of 1 is used for the alternative rollout value, then the alternative bot should be used without impact to the bot assignment in storage.
15149
+ *
15150
+ * @param primaryBotClientObject
15151
+ * @param alternativeBot
15152
+ * @param adaSettings
15153
+ */
15154
+ async function fetchClientUsingAlternativeBotRollout(primaryBotClientObject, alternativeBot, adaSettings) {
15155
+ const adaBotHandleOverride = getBotOverrideHandleFromWebpageURL();
15156
+ const {
15157
+ domain,
15158
+ cluster,
15159
+ language
15160
+ } = adaSettings;
15161
+ if (adaBotHandleOverride) {
15162
+ const overrideBotClientObject = await fetchClient(adaBotHandleOverride, cluster, domain, language || get_browser_language());
15163
+ if (!overrideBotClientObject) {
15164
+ return null;
15165
+ }
15166
+ return new Client(overrideBotClientObject.client);
15167
+ }
15168
+ if (alternativeBot) {
15169
+ const {
15170
+ handle: alternativeBotHandle,
15171
+ rollout: alternativeBotRolloutValue
15172
+ } = alternativeBot;
15173
+ if (alternativeBotRolloutValue === 0) {
15174
+ return getClientObjectUsingBotAssignment(PRIMARY_BOT_ASSIGNMENT_LABEL, primaryBotClientObject, alternativeBotHandle, adaSettings);
15175
+ }
15176
+ if (alternativeBotRolloutValue === 1) {
15177
+ return getClientObjectUsingBotAssignment(ALTERNATIVE_BOT_ASSIGNMENT_LABEL, primaryBotClientObject, alternativeBotHandle, adaSettings);
15178
+ }
15179
+ return getClientObjectForEmbedInitialization(primaryBotClientObject, alternativeBotHandle, alternativeBotRolloutValue, adaSettings);
15180
+ }
15181
+ return primaryBotClientObject;
15182
+ }
14788
15183
  ;// CONCATENATED MODULE: ./src/client/helpers/location-change/index.ts
14789
15184
 
14790
15185
  function dispatchEvent(eventName) {
@@ -15033,60 +15428,6 @@ function connect(mapStateToProps, mapDispatchToProps) {
15033
15428
  function bindActionCreators(action, dispatch) {
15034
15429
  return payload => dispatch(action, payload);
15035
15430
  }
15036
- ;// CONCATENATED MODULE: ./src/common/helpers/fetch-client.ts
15037
-
15038
-
15039
-
15040
-
15041
- function isClientObject(client) {
15042
- // We require the following properties defined
15043
- return ["handle", "rollout", "chat", "features"].every(key => client[key] !== undefined);
15044
- }
15045
-
15046
- /**
15047
- * Tries fetching the client from cache. If this fails, tries fetching it from API.
15048
- * If this also fails, returns null.
15049
- */
15050
- async function fetchClient(handle, cluster, domain, language) {
15051
- try {
15052
- const cachedClient = await httpRequest({
15053
- url: getClientCacheUrl(handle, cluster, domain)
15054
- });
15055
-
15056
- // We may not have "rollout" data cached for all clients
15057
- // In this case, don't return here and instead try to get it from API below
15058
- // Also fall back to API if the cached client does not match the expected format
15059
- if (isClientObject(cachedClient)) {
15060
- return {
15061
- client: cachedClient
15062
- };
15063
- }
15064
- } catch (e) {
15065
- if (e instanceof Error) {
15066
- error_tracker.trackException(e);
15067
- }
15068
- }
15069
-
15070
- // If we failed to get the client from cache, try getting it directly from API
15071
- try {
15072
- return await httpRequest({
15073
- url: getURL({
15074
- name: "api",
15075
- handle,
15076
- cluster,
15077
- domain,
15078
- qp: {
15079
- url: window.location.href,
15080
- language
15081
- }
15082
- })
15083
- });
15084
- } catch (e) {
15085
- // Do not trackException here because this can fail a lot due to poor network connections
15086
- warn("Failed to fetch client from API.");
15087
- }
15088
- return null;
15089
- }
15090
15431
  ;// CONCATENATED MODULE: ./src/common/helpers/isRTL.ts
15091
15432
 
15092
15433
  const RTLLanguages = ["ar", "he"];
@@ -15113,117 +15454,6 @@ function wait(ms) {
15113
15454
  setTimeout(resolve, ms);
15114
15455
  });
15115
15456
  }
15116
- // EXTERNAL MODULE: ./node_modules/@babel/runtime-corejs3/core-js/object/get-own-property-symbols.js
15117
- var get_own_property_symbols = __webpack_require__(3263);
15118
- // EXTERNAL MODULE: ./node_modules/@babel/runtime-corejs3/core-js/instance/index-of.js
15119
- var index_of = __webpack_require__(5683);
15120
- // EXTERNAL MODULE: ./node_modules/@babel/runtime-corejs3/core-js/object/keys.js
15121
- var keys = __webpack_require__(9356);
15122
- ;// CONCATENATED MODULE: ./node_modules/@babel/runtime-corejs3/helpers/esm/objectWithoutPropertiesLoose.js
15123
-
15124
-
15125
- function _objectWithoutPropertiesLoose(source, excluded) {
15126
- if (source == null) return {};
15127
- var target = {};
15128
-
15129
- var sourceKeys = keys(source);
15130
-
15131
- var key, i;
15132
-
15133
- for (i = 0; i < sourceKeys.length; i++) {
15134
- key = sourceKeys[i];
15135
- if (index_of(excluded).call(excluded, key) >= 0) continue;
15136
- target[key] = source[key];
15137
- }
15138
-
15139
- return target;
15140
- }
15141
- ;// CONCATENATED MODULE: ./node_modules/@babel/runtime-corejs3/helpers/esm/objectWithoutProperties.js
15142
-
15143
-
15144
-
15145
- function _objectWithoutProperties(source, excluded) {
15146
- if (source == null) return {};
15147
- var target = _objectWithoutPropertiesLoose(source, excluded);
15148
- var key, i;
15149
-
15150
- if (get_own_property_symbols) {
15151
- var sourceSymbolKeys = get_own_property_symbols(source);
15152
-
15153
- for (i = 0; i < sourceSymbolKeys.length; i++) {
15154
- key = sourceSymbolKeys[i];
15155
- if (index_of(excluded).call(excluded, key) >= 0) continue;
15156
- if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue;
15157
- target[key] = source[key];
15158
- }
15159
- }
15160
-
15161
- return target;
15162
- }
15163
- ;// CONCATENATED MODULE: ./src/common/models/client/index.ts
15164
-
15165
-
15166
- const _excluded = ["chat", "chat_button", "features", "handle", "language", "persistence", "privacy", "rollout", "tint", "translated_languages"];
15167
-
15168
- class Client {
15169
- constructor(clientResponse) {
15170
- _defineProperty(this, "tint", void 0);
15171
- _defineProperty(this, "chat", void 0);
15172
- _defineProperty(this, "intro", void 0);
15173
- _defineProperty(this, "handle", void 0);
15174
- _defineProperty(this, "rollout", void 0);
15175
- _defineProperty(this, "language", void 0);
15176
- _defineProperty(this, "privacy", void 0);
15177
- _defineProperty(this, "features", void 0);
15178
- _defineProperty(this, "persistence", void 0);
15179
- _defineProperty(this, "chat_button", void 0);
15180
- _defineProperty(this, "business_events", void 0);
15181
- _defineProperty(this, "marketing_campaigns_order", void 0);
15182
- _defineProperty(this, "marketing_campaigns", void 0);
15183
- _defineProperty(this, "ui_settings", void 0);
15184
- _defineProperty(this, "translated_languages", void 0);
15185
- const {
15186
- chat,
15187
- chat_button: chatButton,
15188
- features,
15189
- handle,
15190
- language,
15191
- persistence,
15192
- privacy,
15193
- rollout,
15194
- tint,
15195
- // eslint-disable-next-line @typescript-eslint/naming-convention
15196
- translated_languages
15197
- } = clientResponse,
15198
- rest = _objectWithoutProperties(clientResponse, _excluded);
15199
- this.chat = chat;
15200
- this.chat_button = chatButton;
15201
- this.features = features;
15202
- this.handle = handle;
15203
- this.language = language;
15204
- this.persistence = persistence;
15205
- this.privacy = privacy;
15206
- this.rollout = rollout;
15207
- this.tint = tint;
15208
- this.translated_languages = translated_languages;
15209
-
15210
- /**
15211
- * Even if some properties are not used by Embed2, they may be used by Chat.
15212
- * Chat can "pull" the client object from Embed2 in order to reduce an API
15213
- * request. Embed2 should thus not modify keys.
15214
- */
15215
- Object.assign(this, rest);
15216
- }
15217
- }
15218
- function getButtonText(client) {
15219
- var _client$ui_settings, _client$ui_settings$e;
15220
- const buttonTextMap = client === null || client === void 0 ? void 0 : (_client$ui_settings = client.ui_settings) === null || _client$ui_settings === void 0 ? void 0 : (_client$ui_settings$e = _client$ui_settings.embed) === null || _client$ui_settings$e === void 0 ? void 0 : _client$ui_settings$e.button_text;
15221
- const languageKey = get_browser_language();
15222
- if (!buttonTextMap || !buttonTextMap[languageKey]) {
15223
- return "Chat";
15224
- }
15225
- return buttonTextMap[languageKey];
15226
- }
15227
15457
  ;// CONCATENATED MODULE: ./src/common/types/modules.ts
15228
15458
  const CHAT_IFRAME = "chat";
15229
15459
  const ENTRY_SCRIPT = "entry";
@@ -15264,51 +15494,6 @@ function setBrowserStorageItem(key, value, persistenceSetting) {
15264
15494
  dist/* adaSessionStorage.setItem */.Dp.setItem(key, value);
15265
15495
  }
15266
15496
  }
15267
- // EXTERNAL MODULE: ./node_modules/lodash.memoize/index.js
15268
- var lodash_memoize = __webpack_require__(773);
15269
- var lodash_memoize_default = /*#__PURE__*/__webpack_require__.n(lodash_memoize);
15270
- ;// CONCATENATED MODULE: ./src/services/logger/index.ts
15271
-
15272
- function logger_ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
15273
- function logger_objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? logger_ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : logger_ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
15274
-
15275
-
15276
- const DEFAULT_LOG_SAMPLE_RATE = 0.01; // 1% of logs will go through
15277
-
15278
- const DD_BASE_URL = "https://browser-http-intake.logs.datadoghq.com/v1/input/";
15279
- const DD_TOKEN = "pubfe23baedd2ea322bebb5ed2020fa2fa1";
15280
-
15281
- // We need memoization to ensure consistency of logs
15282
- const shouldLog = lodash_memoize_default()(sampleRate => {
15283
- if (!DD_TOKEN || sampleRate === 0) {
15284
- return false;
15285
- }
15286
- return Math.random() < (sampleRate || DEFAULT_LOG_SAMPLE_RATE);
15287
- });
15288
-
15289
- /**
15290
- * Sends log to Datadog
15291
- */
15292
- async function log(message, extra, options) {
15293
- if (!shouldLog(options === null || options === void 0 ? void 0 : options.sampleRate)) {
15294
- return;
15295
- }
15296
- await httpRequest({
15297
- url: `${DD_BASE_URL}${DD_TOKEN}?ddsource=browser&ddtags=version:1.5.0,env:${"production"}`,
15298
- method: "POST",
15299
- body: JSON.stringify(logger_objectSpread(logger_objectSpread({
15300
- message
15301
- }, extra), {}, {
15302
- sampleRate: (options === null || options === void 0 ? void 0 : options.sampleRate) || DEFAULT_LOG_SAMPLE_RATE,
15303
- service: "embed",
15304
- env: "production",
15305
- embedVersion: 2,
15306
- version: "1.6.53",
15307
- isNpm: true,
15308
- commitHash: "508e85e"
15309
- }))
15310
- });
15311
- }
15312
15497
  ;// CONCATENATED MODULE: ./src/services/chat-versioning/manifest.ts
15313
15498
 
15314
15499
 
@@ -15831,7 +16016,7 @@ class ChatFrame extends d {
15831
16016
  const hostPageUrlParams = new URL(window.location.href).searchParams;
15832
16017
  const smsToken = hostPageUrlParams.get("adaSMSToken");
15833
16018
  const queryParams = {
15834
- embedVersion: "508e85e".slice(0, 7),
16019
+ embedVersion: "b0e29e3".slice(0, 7),
15835
16020
  greeting,
15836
16021
  language,
15837
16022
  skipGreeting,
@@ -16684,6 +16869,7 @@ function Container_objectSpread(e) { for (var r = 1; r < arguments.length; r++)
16684
16869
 
16685
16870
 
16686
16871
 
16872
+
16687
16873
 
16688
16874
 
16689
16875
  class Container extends d {
@@ -17388,78 +17574,90 @@ class Container extends d {
17388
17574
  this.unmount();
17389
17575
  return;
17390
17576
  }
17391
- const newClient = new Client(clientResponse.client);
17577
+ const primaryBotClient = new Client(clientResponse.client);
17578
+ const shouldHideEmbedButton = !checkRollout(rolloutOverride || primaryBotClient.rollout, primaryBotClient.handle);
17579
+
17580
+ // if there is a bot override handle in the URL, we should ignore the primary rollout which determines whether any embed button is shown
17581
+ if (shouldHideEmbedButton && !getBotOverrideHandleFromWebpageURL()) {
17582
+ warn("User is in the wrong rollout group.");
17583
+ if (adaReadyCallback) {
17584
+ adaReadyCallback({
17585
+ isRolledOut: false
17586
+ });
17587
+ }
17588
+ this.unmount();
17589
+ return;
17590
+ }
17591
+ const alternativeBot = primaryBotClient.alternative_bot;
17592
+ const assignedClient = await fetchClientUsingAlternativeBotRollout(primaryBotClient, alternativeBot, adaSettings);
17593
+ if (!assignedClient) {
17594
+ this.unmount();
17595
+ return;
17596
+ }
17392
17597
  const baseApiUrl = getURL({
17393
17598
  name: "api",
17394
- handle,
17599
+ handle: assignedClient.handle,
17395
17600
  cluster: cluster,
17396
17601
  domain
17397
17602
  });
17398
17603
 
17399
17604
  // fetch business events if they're not part of the cached client
17400
- if (newClient.features.afm_business_events && !newClient.business_events) {
17605
+ if (assignedClient.features.afm_business_events && !assignedClient.business_events) {
17401
17606
  try {
17402
17607
  const businessEvents = await httpRequest({
17403
17608
  url: `${baseApiUrl}business_events/`
17404
17609
  });
17405
- newClient.business_events = businessEvents.business_events;
17610
+ assignedClient.business_events = businessEvents.business_events;
17406
17611
  } catch (e) {
17407
- newClient.business_events = [];
17612
+ assignedClient.business_events = [];
17408
17613
  warn("Failed to fetch events from API.");
17409
17614
  }
17410
17615
  }
17411
17616
 
17412
17617
  // Fetch marketing campaigns if they're not part of the cached client
17413
- if (!newClient.marketing_campaigns) {
17618
+ if (!assignedClient.marketing_campaigns) {
17414
17619
  try {
17415
17620
  const marketingCampaigns = await httpRequest({
17416
17621
  url: `${baseApiUrl}campaigns/`
17417
17622
  });
17418
- newClient.marketing_campaigns = marketingCampaigns.campaigns;
17623
+ assignedClient.marketing_campaigns = marketingCampaigns.campaigns;
17419
17624
  } catch (e) {
17420
- newClient.marketing_campaigns = [];
17421
- newClient.marketing_campaigns_order = [];
17625
+ assignedClient.marketing_campaigns = [];
17626
+ assignedClient.marketing_campaigns_order = [];
17422
17627
  warn("Failed to fetch campaigns from API.");
17423
17628
  }
17424
17629
  }
17425
17630
 
17426
17631
  // Sort the campaigns by marketing_campaigns_order
17427
- if (newClient.marketing_campaigns_order) {
17428
- newClient.marketing_campaigns = newClient.marketing_campaigns_order.map(campaignId => {
17429
- var _newClient$marketing_;
17430
- return (_newClient$marketing_ = newClient.marketing_campaigns) === null || _newClient$marketing_ === void 0 ? void 0 : _newClient$marketing_.find(c => c._id === campaignId);
17632
+ if (assignedClient.marketing_campaigns_order) {
17633
+ assignedClient.marketing_campaigns = assignedClient.marketing_campaigns_order.map(campaignId => {
17634
+ var _assignedClient$marke;
17635
+ return (_assignedClient$marke = assignedClient.marketing_campaigns) === null || _assignedClient$marke === void 0 ? void 0 : _assignedClient$marke.find(c => c._id === campaignId);
17431
17636
  })
17432
17637
  // remove undefined campaigns, shouldn't happen but just in case
17433
17638
  // for example this happened once when we manually deleted a malformed campaign from the db
17434
17639
  .filter(campaign => Boolean(campaign));
17435
17640
  }
17436
17641
  const {
17437
- chat: chatIsOn,
17438
- rollout
17439
- } = newClient;
17642
+ chat: chatIsOn
17643
+ } = assignedClient;
17440
17644
  if (!chatIsOn) {
17441
17645
  warn("Sorry, please turn on the web chat integration in your bot's settings.");
17442
17646
  this.unmount();
17443
17647
  return;
17444
17648
  }
17445
- if (!checkRollout(rolloutOverride || rollout, handle)) {
17446
- warn("User is in the wrong rollout group.");
17447
- if (adaReadyCallback) {
17448
- adaReadyCallback({
17449
- isRolledOut: false
17450
- });
17451
- }
17452
- this.unmount();
17453
- return;
17454
- }
17455
- const chatterInLiveChat = retrieveStorage(IN_LIVE_CHAT_STORAGE_KEY, newClient, privateMode);
17649
+ const chatterInLiveChat = retrieveStorage(IN_LIVE_CHAT_STORAGE_KEY, assignedClient, privateMode);
17456
17650
  try {
17457
17651
  await setGlobalState(Container_objectSpread(Container_objectSpread({
17458
- client: newClient,
17459
- enabledLanguages: newClient.features.translations ? ["en"].concat(newClient.translated_languages) : ["en"],
17652
+ client: assignedClient,
17653
+ enabledLanguages: assignedClient.features.translations ? ["en"].concat(assignedClient.translated_languages) : ["en"],
17460
17654
  initialURL: window.location.href,
17461
17655
  isIntroShown: false
17462
17656
  }, adaSettingsWithoutFunctions), {}, {
17657
+ handle: assignedClient.handle,
17658
+ adaSettings: Container_objectSpread(Container_objectSpread({}, adaSettings), {}, {
17659
+ handle: assignedClient.handle
17660
+ }),
17463
17661
  chatterInLiveChat
17464
17662
  }));
17465
17663
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ada-support/embed2",
3
- "version": "1.6.53",
3
+ "version": "1.7.0",
4
4
  "description": "",
5
5
  "main": "dist/npm-entry",
6
6
  "typings": "dist/npm-entry/index-npm.d.ts",
@@ -79,7 +79,7 @@
79
79
  },
80
80
  "dependencies": {
81
81
  "@ada-support/embed-types": "^1.6.5",
82
- "@ada-support/web-storage": "^0.14.0",
82
+ "@ada-support/web-storage": "^1.2.6",
83
83
  "@babel/core": "^7.23.2",
84
84
  "@babel/plugin-proposal-class-properties": "^7.16.7",
85
85
  "@babel/plugin-proposal-numeric-separator": "^7.16.7",