@returningai/widget-sdk 1.6.2 → 1.7.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.
@@ -10335,794 +10335,245 @@ var lottie_light = { exports: {} };
10335
10335
  })(lottie_light, lottie_light.exports);
10336
10336
  var lottie_lightExports = lottie_light.exports;
10337
10337
  const lottie = /* @__PURE__ */ getDefaultExportFromCjs(lottie_lightExports);
10338
- function storageKey(config) {
10339
- return `${config.storagePrefix}-${config.widgetId}-${config.container}-auth`;
10340
- }
10341
- function legacyStorageKey(config) {
10342
- return `${config.storagePrefix}-${config.widgetId}-auth`;
10343
- }
10344
- function errorSettingsKey(config) {
10345
- return `${config.storagePrefix}-${config.widgetId}-error-settings`;
10346
- }
10347
- function saveToStorage(config, state) {
10338
+ async function fetchEligibleBanner(apiUrl, request) {
10348
10339
  try {
10349
- const data = {
10350
- refreshToken: state.refreshToken,
10351
- tokenFamily: state.tokenFamily,
10352
- refreshTokenExpiry: state.refreshTokenExpiry
10353
- };
10354
- localStorage.setItem(storageKey(config), JSON.stringify(data));
10340
+ const res = await fetch(`${apiUrl}/v2/api/banner-sdk/eligible`, {
10341
+ method: "POST",
10342
+ headers: { "Content-Type": "application/json" },
10343
+ body: JSON.stringify(request)
10344
+ });
10345
+ const json = await res.json();
10346
+ if (json && json.meta && json.meta.status === "success") {
10347
+ return json.data || { banner: null };
10348
+ }
10349
+ return { banner: null, reason: json && json.detail || "request_failed" };
10355
10350
  } catch {
10351
+ return { banner: null, reason: "network_error" };
10356
10352
  }
10357
10353
  }
10358
- function loadFromStorage(config, state) {
10359
- try {
10360
- let raw = localStorage.getItem(storageKey(config));
10361
- const loadedLegacyKey = !raw;
10362
- if (!raw) {
10363
- raw = localStorage.getItem(legacyStorageKey(config));
10364
- }
10365
- if (!raw) return false;
10366
- const parsed = JSON.parse(raw);
10367
- if (parsed.refreshToken && !isTokenExpired(parsed.refreshTokenExpiry)) {
10368
- state.refreshToken = parsed.refreshToken;
10369
- state.tokenFamily = parsed.tokenFamily;
10370
- state.refreshTokenExpiry = parsed.refreshTokenExpiry;
10371
- if (loadedLegacyKey) {
10372
- saveToStorage(config, state);
10373
- localStorage.removeItem(legacyStorageKey(config));
10354
+ /*! @license DOMPurify 3.4.9 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.9/LICENSE */
10355
+ function _arrayLikeToArray(r, a) {
10356
+ (null == a || a > r.length) && (a = r.length);
10357
+ for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
10358
+ return n;
10359
+ }
10360
+ function _arrayWithHoles(r) {
10361
+ if (Array.isArray(r)) return r;
10362
+ }
10363
+ function _iterableToArrayLimit(r, l) {
10364
+ var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
10365
+ if (null != t) {
10366
+ var e, n, i, u, a = [], f = true, o = false;
10367
+ try {
10368
+ if (i = (t = t.call(r)).next, 0 === l) ;
10369
+ else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = true) ;
10370
+ } catch (r2) {
10371
+ o = true, n = r2;
10372
+ } finally {
10373
+ try {
10374
+ if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
10375
+ } finally {
10376
+ if (o) throw n;
10374
10377
  }
10375
- return true;
10376
10378
  }
10377
- clearStorage(config);
10378
- return false;
10379
- } catch {
10380
- return false;
10379
+ return a;
10381
10380
  }
10382
10381
  }
10383
- function clearStorage(config) {
10384
- try {
10385
- localStorage.removeItem(storageKey(config));
10386
- localStorage.removeItem(legacyStorageKey(config));
10387
- } catch {
10382
+ function _nonIterableRest() {
10383
+ throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
10384
+ }
10385
+ function _slicedToArray(r, e) {
10386
+ return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
10387
+ }
10388
+ function _unsupportedIterableToArray(r, a) {
10389
+ if (r) {
10390
+ if ("string" == typeof r) return _arrayLikeToArray(r, a);
10391
+ var t = {}.toString.call(r).slice(8, -1);
10392
+ return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
10388
10393
  }
10389
10394
  }
10390
- const ERROR_SETTINGS_CACHE_TTL = 36e5;
10391
- function loadErrorSettingsFromStorage(config) {
10392
- try {
10393
- const raw = localStorage.getItem(errorSettingsKey(config));
10394
- if (!raw) return null;
10395
- const parsed = JSON.parse(raw);
10396
- if (parsed.cachedAt && Date.now() - parsed.cachedAt < ERROR_SETTINGS_CACHE_TTL) {
10397
- return parsed;
10395
+ const entries = Object.entries, setPrototypeOf = Object.setPrototypeOf, isFrozen = Object.isFrozen, getPrototypeOf = Object.getPrototypeOf, getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
10396
+ let freeze = Object.freeze, seal = Object.seal, create = Object.create;
10397
+ let _ref = typeof Reflect !== "undefined" && Reflect, apply = _ref.apply, construct = _ref.construct;
10398
+ if (!freeze) {
10399
+ freeze = function freeze2(x) {
10400
+ return x;
10401
+ };
10402
+ }
10403
+ if (!seal) {
10404
+ seal = function seal2(x) {
10405
+ return x;
10406
+ };
10407
+ }
10408
+ if (!apply) {
10409
+ apply = function apply2(func, thisArg) {
10410
+ for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
10411
+ args[_key - 2] = arguments[_key];
10398
10412
  }
10399
- localStorage.removeItem(errorSettingsKey(config));
10400
- return null;
10401
- } catch {
10402
- return null;
10403
- }
10413
+ return func.apply(thisArg, args);
10414
+ };
10404
10415
  }
10405
- function saveErrorSettingsToStorage(config, settings) {
10406
- try {
10407
- localStorage.setItem(errorSettingsKey(config), JSON.stringify({ ...settings, cachedAt: Date.now() }));
10408
- } catch {
10409
- }
10416
+ if (!construct) {
10417
+ construct = function construct2(Func) {
10418
+ for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
10419
+ args[_key2 - 1] = arguments[_key2];
10420
+ }
10421
+ return new Func(...args);
10422
+ };
10410
10423
  }
10411
- function isTokenExpired(expiry) {
10412
- if (!expiry) return true;
10413
- return Date.now() >= expiry - 6e4;
10424
+ const arrayForEach = unapply(Array.prototype.forEach);
10425
+ const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
10426
+ const arrayPop = unapply(Array.prototype.pop);
10427
+ const arrayPush = unapply(Array.prototype.push);
10428
+ const arraySplice = unapply(Array.prototype.splice);
10429
+ const arrayIsArray = Array.isArray;
10430
+ const stringToLowerCase = unapply(String.prototype.toLowerCase);
10431
+ const stringToString = unapply(String.prototype.toString);
10432
+ const stringMatch = unapply(String.prototype.match);
10433
+ const stringReplace = unapply(String.prototype.replace);
10434
+ const stringIndexOf = unapply(String.prototype.indexOf);
10435
+ const stringTrim = unapply(String.prototype.trim);
10436
+ const numberToString = unapply(Number.prototype.toString);
10437
+ const booleanToString = unapply(Boolean.prototype.toString);
10438
+ const bigintToString = typeof BigInt === "undefined" ? null : unapply(BigInt.prototype.toString);
10439
+ const symbolToString = typeof Symbol === "undefined" ? null : unapply(Symbol.prototype.toString);
10440
+ const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
10441
+ const objectToString = unapply(Object.prototype.toString);
10442
+ const regExpTest = unapply(RegExp.prototype.test);
10443
+ const typeErrorCreate = unconstruct(TypeError);
10444
+ function unapply(func) {
10445
+ return function(thisArg) {
10446
+ if (thisArg instanceof RegExp) {
10447
+ thisArg.lastIndex = 0;
10448
+ }
10449
+ for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
10450
+ args[_key3 - 1] = arguments[_key3];
10451
+ }
10452
+ return apply(func, thisArg, args);
10453
+ };
10414
10454
  }
10415
- function setTokens(config, state, data, onRefreshScheduled) {
10416
- state.accessToken = data.accessToken;
10417
- state.refreshToken = data.refreshToken;
10418
- state.tokenFamily = data.tokenFamily ?? null;
10419
- const now = Date.now();
10420
- state.accessTokenExpiry = now + data.accessTokenTTL * 1e3;
10421
- state.refreshTokenExpiry = now + data.refreshTokenTTL * 1e3;
10422
- saveToStorage(config, state);
10423
- if (config.autoRefresh && onRefreshScheduled) {
10424
- onRefreshScheduled();
10455
+ function unconstruct(Func) {
10456
+ return function() {
10457
+ for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
10458
+ args[_key4] = arguments[_key4];
10459
+ }
10460
+ return construct(Func, args);
10461
+ };
10462
+ }
10463
+ function addToSet(set, array) {
10464
+ let transformCaseFunc = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : stringToLowerCase;
10465
+ if (setPrototypeOf) {
10466
+ setPrototypeOf(set, null);
10467
+ }
10468
+ if (!arrayIsArray(array)) {
10469
+ return set;
10470
+ }
10471
+ let l = array.length;
10472
+ while (l--) {
10473
+ let element = array[l];
10474
+ if (typeof element === "string") {
10475
+ const lcElement = transformCaseFunc(element);
10476
+ if (lcElement !== element) {
10477
+ if (!isFrozen(array)) {
10478
+ array[l] = lcElement;
10479
+ }
10480
+ element = lcElement;
10481
+ }
10482
+ }
10483
+ set[element] = true;
10425
10484
  }
10485
+ return set;
10426
10486
  }
10427
- function buildIdentityProof(config) {
10428
- const proof = {};
10429
- if (config.embedToken) {
10430
- proof.embedToken = config.embedToken;
10487
+ function cleanArray(array) {
10488
+ for (let index = 0; index < array.length; index++) {
10489
+ const isPropertyExist = objectHasOwnProperty(array, index);
10490
+ if (!isPropertyExist) {
10491
+ array[index] = null;
10492
+ }
10431
10493
  }
10432
- if (config.userIdentifiers && Object.keys(config.userIdentifiers).length > 0) {
10433
- proof.userIdentifiers = config.userIdentifiers;
10494
+ return array;
10495
+ }
10496
+ function clone(object) {
10497
+ const newObject = create(null);
10498
+ for (const _ref2 of entries(object)) {
10499
+ var _ref3 = _slicedToArray(_ref2, 2);
10500
+ const property = _ref3[0];
10501
+ const value = _ref3[1];
10502
+ const isPropertyExist = objectHasOwnProperty(object, property);
10503
+ if (isPropertyExist) {
10504
+ if (arrayIsArray(value)) {
10505
+ newObject[property] = cleanArray(value);
10506
+ } else if (value && typeof value === "object" && value.constructor === Object) {
10507
+ newObject[property] = clone(value);
10508
+ } else {
10509
+ newObject[property] = value;
10510
+ }
10511
+ }
10434
10512
  }
10435
- return proof;
10513
+ return newObject;
10436
10514
  }
10437
- async function authenticateServerless(config, state, onRefreshScheduled) {
10438
- const nonce = crypto.randomUUID();
10439
- const timestamp = Date.now();
10440
- const maxRetries = config.maxRetries ?? 3;
10441
- const retryDelay = config.retryDelay ?? 500;
10442
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
10443
- if (attempt > 0) {
10444
- await new Promise(
10445
- (resolve) => setTimeout(resolve, retryDelay * (1 << attempt - 1))
10446
- );
10515
+ function stringifyValue(value) {
10516
+ switch (typeof value) {
10517
+ case "string": {
10518
+ return value;
10447
10519
  }
10448
- let is4xx = false;
10449
- try {
10450
- const body = {
10451
- nonce,
10452
- timestamp,
10453
- widgetType: config.widgetType,
10454
- ...buildIdentityProof(config)
10455
- };
10456
- const response = await fetch(`${config.apiUrl}/${config.widgetId}/auth/serverless`, {
10457
- method: "POST",
10458
- headers: { "Content-Type": "application/json" },
10459
- body: JSON.stringify(body),
10460
- credentials: "include"
10461
- });
10462
- if (!response.ok) {
10463
- is4xx = response.status >= 400 && response.status < 500;
10464
- const errorBody = await response.json().catch(() => ({ error: "Authentication failed" }));
10465
- if (is4xx) {
10466
- state.lastAuthError = {
10467
- status: response.status,
10468
- error: errorBody == null ? void 0 : errorBody.error,
10469
- hint: errorBody == null ? void 0 : errorBody.hint
10470
- };
10471
- console.error(
10472
- `[rai-widget] /auth/serverless ${response.status}:`,
10473
- (errorBody == null ? void 0 : errorBody.error) || "Authentication failed",
10474
- (errorBody == null ? void 0 : errorBody.hint) ? `— ${errorBody.hint}` : ""
10475
- );
10476
- }
10477
- throw new Error((errorBody == null ? void 0 : errorBody.error) || `HTTP ${response.status}`);
10478
- }
10479
- state.lastAuthError = null;
10480
- const data = await response.json();
10481
- if (!data.accessToken || !data.refreshToken) {
10482
- throw new Error("Invalid authentication response");
10483
- }
10484
- setTokens(config, state, data, onRefreshScheduled);
10485
- state.isAuthenticated = true;
10486
- return true;
10487
- } catch {
10488
- if (is4xx || attempt === maxRetries) {
10489
- state.isAuthenticated = false;
10490
- return false;
10491
- }
10520
+ case "number": {
10521
+ return numberToString(value);
10492
10522
  }
10493
- }
10494
- state.isAuthenticated = false;
10495
- return false;
10496
- }
10497
- async function authenticateViaProxy(config, state, onRefreshScheduled) {
10498
- const maxRetries = config.maxRetries ?? 3;
10499
- const retryDelay = config.retryDelay ?? 500;
10500
- for (let attempt = 0; attempt <= maxRetries; attempt++) {
10501
- if (attempt > 0) {
10502
- await new Promise(
10503
- (resolve) => setTimeout(resolve, retryDelay * (1 << attempt - 1))
10504
- );
10523
+ case "boolean": {
10524
+ return booleanToString(value);
10505
10525
  }
10506
- let is4xx = false;
10507
- try {
10508
- const response = await fetch(config.authUrl, {
10509
- method: "POST",
10510
- credentials: "include",
10511
- headers: { "Content-Type": "application/json" }
10512
- });
10513
- if (!response.ok) {
10514
- is4xx = response.status >= 400 && response.status < 500;
10515
- throw new Error(`HTTP ${response.status}`);
10516
- }
10517
- const data = await response.json();
10518
- if (!data.token) {
10519
- throw new Error("Invalid proxy auth response");
10520
- }
10521
- const ttl = data.expiresIn ?? 300;
10522
- state.accessToken = data.token;
10523
- state.refreshToken = null;
10524
- state.tokenFamily = null;
10525
- state.accessTokenExpiry = Date.now() + ttl * 1e3;
10526
- state.refreshTokenExpiry = null;
10527
- state.isAuthenticated = true;
10528
- if (config.autoRefresh && onRefreshScheduled) {
10529
- onRefreshScheduled();
10526
+ case "bigint": {
10527
+ return bigintToString ? bigintToString(value) : "0";
10528
+ }
10529
+ case "symbol": {
10530
+ return symbolToString ? symbolToString(value) : "Symbol()";
10531
+ }
10532
+ case "undefined": {
10533
+ return objectToString(value);
10534
+ }
10535
+ case "function":
10536
+ case "object": {
10537
+ if (value === null) {
10538
+ return objectToString(value);
10530
10539
  }
10531
- return true;
10532
- } catch {
10533
- if (is4xx || attempt === maxRetries) {
10534
- state.isAuthenticated = false;
10535
- return false;
10540
+ const valueAsRecord = value;
10541
+ const valueToString = lookupGetter(valueAsRecord, "toString");
10542
+ if (typeof valueToString === "function") {
10543
+ const stringified = valueToString(valueAsRecord);
10544
+ return typeof stringified === "string" ? stringified : objectToString(stringified);
10536
10545
  }
10546
+ return objectToString(value);
10547
+ }
10548
+ default: {
10549
+ return objectToString(value);
10537
10550
  }
10538
10551
  }
10539
- state.isAuthenticated = false;
10540
- return false;
10541
10552
  }
10542
- async function refreshAccessToken(config, state, onRefreshScheduled, sendToken) {
10543
- if (state.isRefreshing) {
10544
- return state.refreshPromise;
10545
- }
10546
- if (!state.refreshToken) {
10547
- return false;
10548
- }
10549
- state.isRefreshing = true;
10550
- state.refreshPromise = (async () => {
10551
- const reauthenticateAfterRefreshFailure = async () => {
10552
- clearState(state);
10553
- clearStorage(config);
10554
- const authed = await authenticateServerless(config, state, onRefreshScheduled);
10555
- if (authed && sendToken) sendToken();
10556
- return authed;
10557
- };
10558
- try {
10559
- const response = await fetch(`${config.apiUrl}/${config.widgetId}/auth/refresh`, {
10560
- method: "POST",
10561
- headers: {
10562
- "Content-Type": "application/json",
10563
- Authorization: `Bearer ${state.refreshToken}`
10564
- },
10565
- body: JSON.stringify({
10566
- widgetType: config.widgetType,
10567
- ...buildIdentityProof(config)
10568
- })
10569
- });
10570
- if (!response.ok) {
10571
- if (response.status === 400 || response.status === 401 || response.status === 403) {
10572
- return reauthenticateAfterRefreshFailure();
10573
- }
10574
- const error = await response.json().catch(() => ({ error: "Token refresh failed" }));
10575
- throw new Error(error.error || `HTTP ${response.status}`);
10553
+ function lookupGetter(object, prop) {
10554
+ while (object !== null) {
10555
+ const desc = getOwnPropertyDescriptor(object, prop);
10556
+ if (desc) {
10557
+ if (desc.get) {
10558
+ return unapply(desc.get);
10576
10559
  }
10577
- const data = await response.json();
10578
- if (!data.accessToken || !data.refreshToken) {
10579
- throw new Error("Invalid refresh response");
10560
+ if (typeof desc.value === "function") {
10561
+ return unapply(desc.value);
10580
10562
  }
10581
- setTokens(config, state, data, onRefreshScheduled);
10582
- if (sendToken) sendToken();
10583
- return true;
10584
- } catch {
10585
- return reauthenticateAfterRefreshFailure();
10586
- } finally {
10587
- state.isRefreshing = false;
10588
- state.refreshPromise = null;
10589
10563
  }
10590
- })();
10591
- return state.refreshPromise;
10592
- }
10593
- async function getValidToken(config, state, onRefreshScheduled) {
10594
- if (state.accessToken && !isTokenExpired(state.accessTokenExpiry)) {
10595
- return state.accessToken;
10596
- }
10597
- if (config.authUrl) {
10598
- const authed2 = await authenticateViaProxy(config, state, onRefreshScheduled);
10599
- if (authed2 && state.accessToken) return state.accessToken;
10600
- throw new Error("Unable to obtain valid token");
10564
+ object = getPrototypeOf(object);
10601
10565
  }
10602
- const refreshed = await refreshAccessToken(config, state, onRefreshScheduled);
10603
- if (refreshed && state.accessToken) return state.accessToken;
10604
- const authed = await authenticateServerless(config, state, onRefreshScheduled);
10605
- if (authed && state.accessToken) return state.accessToken;
10606
- throw new Error("Unable to obtain valid token");
10607
- }
10608
- async function logout(config, state) {
10609
- if (state.accessToken) {
10610
- try {
10611
- await fetch(`${config.apiUrl}/${config.widgetId}/auth/logout`, {
10612
- method: "POST",
10613
- headers: {
10614
- "Content-Type": "application/json",
10615
- Authorization: `Bearer ${state.accessToken}`
10616
- },
10617
- body: JSON.stringify({ refreshToken: state.refreshToken })
10618
- });
10619
- } catch {
10620
- }
10566
+ function fallbackValue() {
10567
+ return null;
10621
10568
  }
10622
- clearState(state);
10623
- clearStorage(config);
10569
+ return fallbackValue;
10624
10570
  }
10625
- const inflightErrorSettings = /* @__PURE__ */ new Map();
10626
- function applyErrorSettingsToState(state, data) {
10627
- if (data.configured) {
10628
- state.errorSettings = {
10629
- errorMessage: data.errorMessage,
10630
- modalColor: data.modalColor,
10631
- backgroundImage: data.backgroundImage
10632
- };
10633
- }
10634
- if (data.loadingAnimation) {
10635
- state.loadingAnimation = data.loadingAnimation;
10636
- }
10637
- if (data.themePalette) {
10638
- state.themePalette = data.themePalette;
10639
- }
10640
- if (typeof data.font === "string" && data.font) {
10641
- state.font = data.font;
10642
- }
10643
- if (data.defaultColorSchema === "light" || data.defaultColorSchema === "dark") {
10644
- state.defaultColorSchema = data.defaultColorSchema;
10645
- }
10646
- }
10647
- async function fetchErrorSettings(config, state, opts = {}) {
10648
- if (!opts.skipCache) {
10649
- const cached = loadErrorSettingsFromStorage(config);
10650
- if (cached) {
10651
- applyErrorSettingsToState(state, cached);
10652
- return state.errorSettings;
10653
- }
10654
- }
10655
- const url = `${config.apiUrl}/${config.widgetId}/auth/error-settings?widgetType=${config.widgetType}`;
10656
- let promise = inflightErrorSettings.get(url);
10657
- if (!promise) {
10658
- promise = (async () => {
10659
- try {
10660
- const response = await fetch(url, {
10661
- method: "GET",
10662
- headers: { "Content-Type": "application/json" }
10663
- });
10664
- if (!response.ok) return null;
10665
- const data2 = await response.json();
10666
- saveErrorSettingsToStorage(config, data2);
10667
- return data2;
10668
- } catch {
10669
- return null;
10670
- } finally {
10671
- inflightErrorSettings.delete(url);
10672
- }
10673
- })();
10674
- inflightErrorSettings.set(url, promise);
10675
- }
10676
- const data = await promise;
10677
- if (!data) return null;
10678
- applyErrorSettingsToState(state, data);
10679
- return state.errorSettings;
10680
- }
10681
- function clearState(state) {
10682
- state.accessToken = null;
10683
- state.refreshToken = null;
10684
- state.tokenFamily = null;
10685
- state.accessTokenExpiry = null;
10686
- state.refreshTokenExpiry = null;
10687
- state.isAuthenticated = false;
10688
- state.isRefreshing = false;
10689
- state.refreshPromise = null;
10690
- state.lastAuthError = null;
10691
- if (state.refreshTimer) {
10692
- clearTimeout(state.refreshTimer);
10693
- state.refreshTimer = null;
10694
- }
10695
- if (state.syncTimer) {
10696
- clearInterval(state.syncTimer);
10697
- state.syncTimer = null;
10698
- }
10699
- }
10700
- const STORE_CALLBACK_FIELD_OPTIONS_SCOPE = "store.callback_field_options";
10701
- const STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME = "callbackFieldOptions";
10702
- const STORE_CALLBACK_FIELD_OPTIONS_TIMEOUT_MS = 15e3;
10703
- function debounce(fn, wait) {
10704
- let timer = null;
10705
- return {
10706
- call(h) {
10707
- if (timer) clearTimeout(timer);
10708
- timer = setTimeout(() => {
10709
- fn(h);
10710
- timer = null;
10711
- }, wait);
10712
- },
10713
- cancel() {
10714
- if (timer) {
10715
- clearTimeout(timer);
10716
- timer = null;
10717
- }
10718
- }
10719
- };
10720
- }
10721
- function sendTokenToWidget(config, state, iframe) {
10722
- var _a;
10723
- if (!state.accessToken) return;
10724
- try {
10725
- (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
10726
- {
10727
- type: "RETURNINGAI_WIDGET_TOKEN",
10728
- value: {
10729
- widgetId: config.widgetId,
10730
- token: state.accessToken,
10731
- ...config.customData !== void 0 && { customData: config.customData }
10732
- }
10733
- },
10734
- config.widgetDomain
10735
- );
10736
- } catch {
10737
- }
10738
- }
10739
- function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled, onLogout, hideLoader, emit, getCallbacks) {
10740
- const heightSetter = debounce((h) => {
10741
- iframe.style.height = `${h}px`;
10742
- emit == null ? void 0 : emit("rai-height-change", { height: h });
10743
- }, config.heightDebounce ?? 100);
10744
- const handler = async (event) => {
10745
- var _a, _b;
10746
- if (event.origin !== config.widgetDomain) return;
10747
- if (!event.data || typeof event.data.type !== "string") return;
10748
- const { type, containerId, widgetId, payload } = event.data;
10749
- switch (type) {
10750
- case "RETURNINGAI_WIDGET_REQUEST_TOKEN": {
10751
- try {
10752
- const token = await getValidToken(config, state, onRefreshScheduled);
10753
- (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
10754
- {
10755
- type: "RETURNINGAI_WIDGET_TOKEN",
10756
- value: {
10757
- token,
10758
- widgetId: config.widgetId,
10759
- ...config.customData !== void 0 && { customData: config.customData }
10760
- }
10761
- },
10762
- config.widgetDomain
10763
- );
10764
- } catch {
10765
- }
10766
- break;
10767
- }
10768
- case "WIDGET_HEIGHT_UPDATE": {
10769
- const h = Number(payload == null ? void 0 : payload.height);
10770
- if (Number.isFinite(h) && h > 0) {
10771
- heightSetter.call(h);
10772
- }
10773
- break;
10774
- }
10775
- case "WIDGET_READY": {
10776
- if (widgetId !== void 0 && widgetId !== config.widgetId) break;
10777
- if (containerId !== void 0 && containerId !== config.container) break;
10778
- iframe.classList.add("loaded");
10779
- if (hideLoader) hideLoader();
10780
- emit == null ? void 0 : emit("rai-ready");
10781
- break;
10782
- }
10783
- case "WIDGET_ERROR": {
10784
- if (hideLoader) hideLoader();
10785
- break;
10786
- }
10787
- case "WIDGET_LOGOUT": {
10788
- if (onLogout) await onLogout();
10789
- break;
10790
- }
10791
- case "RETURNINGAI_WIDGET_ACTION": {
10792
- if (event.source !== iframe.contentWindow) break;
10793
- const message = event.data;
10794
- if (config.widgetType !== "store" && config.widgetType !== "custom") break;
10795
- if (message.widgetType !== "store") break;
10796
- if (message.action !== "store:host-callback") break;
10797
- if (message.callbackName !== STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME) break;
10798
- if (((_b = message.payload) == null ? void 0 : _b.scope) !== STORE_CALLBACK_FIELD_OPTIONS_SCOPE) break;
10799
- if (message.widgetId !== void 0 && message.widgetId !== config.widgetId) break;
10800
- if (message.containerId !== void 0 && message.containerId !== config.container) break;
10801
- const respond = (result) => {
10802
- var _a2;
10803
- (_a2 = iframe.contentWindow) == null ? void 0 : _a2.postMessage(result, config.widgetDomain);
10804
- };
10805
- const callbacks = (getCallbacks == null ? void 0 : getCallbacks()) ?? {};
10806
- const callback = callbacks[message.callbackName];
10807
- if (!callback) {
10808
- respond({
10809
- type: "RETURNINGAI_WIDGET_ACTION_RESULT",
10810
- requestId: message.requestId,
10811
- action: "store:host-callback",
10812
- ok: false,
10813
- status: "failed",
10814
- error: { message: "Host callback is not registered" }
10815
- });
10816
- break;
10817
- }
10818
- try {
10819
- const value = await callback(message.payload);
10820
- respond({
10821
- type: "RETURNINGAI_WIDGET_ACTION_RESULT",
10822
- requestId: message.requestId,
10823
- action: "store:host-callback",
10824
- ok: true,
10825
- status: "success",
10826
- value
10827
- });
10828
- } catch {
10829
- respond({
10830
- type: "RETURNINGAI_WIDGET_ACTION_RESULT",
10831
- requestId: message.requestId,
10832
- action: "store:host-callback",
10833
- ok: false,
10834
- status: "failed",
10835
- error: { message: "Host callback failed" }
10836
- });
10837
- }
10838
- break;
10839
- }
10840
- }
10841
- };
10842
- window.addEventListener("message", handler);
10843
- return () => {
10844
- window.removeEventListener("message", handler);
10845
- heightSetter.cancel();
10846
- };
10847
- }
10848
- function assertCallbackName(callbackName) {
10849
- if (!callbackName || typeof callbackName !== "string") {
10850
- throw new TypeError("ReturningAI callback name must be a non-empty string");
10851
- }
10852
- }
10853
- function assertCallback(callback) {
10854
- if (typeof callback !== "function") {
10855
- throw new TypeError("ReturningAI callback must be a function");
10856
- }
10857
- }
10858
- class WidgetCallbackRegistry {
10859
- constructor() {
10860
- __publicField(this, "registeredCallbacks", {});
10861
- __publicField(this, "locked", false);
10862
- }
10863
- get isLocked() {
10864
- return this.locked;
10865
- }
10866
- registerCallback(callbackName, callback) {
10867
- if (this.locked) {
10868
- return;
10869
- }
10870
- assertCallbackName(callbackName);
10871
- assertCallback(callback);
10872
- this.registeredCallbacks = {
10873
- ...this.registeredCallbacks,
10874
- [callbackName]: callback
10875
- };
10876
- }
10877
- lock() {
10878
- if (this.locked) {
10879
- return;
10880
- }
10881
- this.locked = true;
10882
- }
10883
- read() {
10884
- return { ...this.registeredCallbacks };
10885
- }
10886
- }
10887
- async function fetchEligibleBanner(apiUrl, request) {
10888
- try {
10889
- const res = await fetch(`${apiUrl}/v2/api/banner-sdk/eligible`, {
10890
- method: "POST",
10891
- headers: { "Content-Type": "application/json" },
10892
- body: JSON.stringify(request)
10893
- });
10894
- const json = await res.json();
10895
- if (json && json.meta && json.meta.status === "success") {
10896
- return json.data || { banner: null };
10897
- }
10898
- return { banner: null, reason: json && json.detail || "request_failed" };
10899
- } catch {
10900
- return { banner: null, reason: "network_error" };
10901
- }
10902
- }
10903
- /*! @license DOMPurify 3.4.9 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.9/LICENSE */
10904
- function _arrayLikeToArray(r, a) {
10905
- (null == a || a > r.length) && (a = r.length);
10906
- for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e];
10907
- return n;
10908
- }
10909
- function _arrayWithHoles(r) {
10910
- if (Array.isArray(r)) return r;
10911
- }
10912
- function _iterableToArrayLimit(r, l) {
10913
- var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"];
10914
- if (null != t) {
10915
- var e, n, i, u, a = [], f = true, o = false;
10916
- try {
10917
- if (i = (t = t.call(r)).next, 0 === l) ;
10918
- else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = true) ;
10919
- } catch (r2) {
10920
- o = true, n = r2;
10921
- } finally {
10922
- try {
10923
- if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return;
10924
- } finally {
10925
- if (o) throw n;
10926
- }
10927
- }
10928
- return a;
10929
- }
10930
- }
10931
- function _nonIterableRest() {
10932
- throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
10933
- }
10934
- function _slicedToArray(r, e) {
10935
- return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest();
10936
- }
10937
- function _unsupportedIterableToArray(r, a) {
10938
- if (r) {
10939
- if ("string" == typeof r) return _arrayLikeToArray(r, a);
10940
- var t = {}.toString.call(r).slice(8, -1);
10941
- return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0;
10942
- }
10943
- }
10944
- const entries = Object.entries, setPrototypeOf = Object.setPrototypeOf, isFrozen = Object.isFrozen, getPrototypeOf = Object.getPrototypeOf, getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
10945
- let freeze = Object.freeze, seal = Object.seal, create = Object.create;
10946
- let _ref = typeof Reflect !== "undefined" && Reflect, apply = _ref.apply, construct = _ref.construct;
10947
- if (!freeze) {
10948
- freeze = function freeze2(x) {
10949
- return x;
10950
- };
10951
- }
10952
- if (!seal) {
10953
- seal = function seal2(x) {
10954
- return x;
10955
- };
10956
- }
10957
- if (!apply) {
10958
- apply = function apply2(func, thisArg) {
10959
- for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
10960
- args[_key - 2] = arguments[_key];
10961
- }
10962
- return func.apply(thisArg, args);
10963
- };
10964
- }
10965
- if (!construct) {
10966
- construct = function construct2(Func) {
10967
- for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
10968
- args[_key2 - 1] = arguments[_key2];
10969
- }
10970
- return new Func(...args);
10971
- };
10972
- }
10973
- const arrayForEach = unapply(Array.prototype.forEach);
10974
- const arrayLastIndexOf = unapply(Array.prototype.lastIndexOf);
10975
- const arrayPop = unapply(Array.prototype.pop);
10976
- const arrayPush = unapply(Array.prototype.push);
10977
- const arraySplice = unapply(Array.prototype.splice);
10978
- const arrayIsArray = Array.isArray;
10979
- const stringToLowerCase = unapply(String.prototype.toLowerCase);
10980
- const stringToString = unapply(String.prototype.toString);
10981
- const stringMatch = unapply(String.prototype.match);
10982
- const stringReplace = unapply(String.prototype.replace);
10983
- const stringIndexOf = unapply(String.prototype.indexOf);
10984
- const stringTrim = unapply(String.prototype.trim);
10985
- const numberToString = unapply(Number.prototype.toString);
10986
- const booleanToString = unapply(Boolean.prototype.toString);
10987
- const bigintToString = typeof BigInt === "undefined" ? null : unapply(BigInt.prototype.toString);
10988
- const symbolToString = typeof Symbol === "undefined" ? null : unapply(Symbol.prototype.toString);
10989
- const objectHasOwnProperty = unapply(Object.prototype.hasOwnProperty);
10990
- const objectToString = unapply(Object.prototype.toString);
10991
- const regExpTest = unapply(RegExp.prototype.test);
10992
- const typeErrorCreate = unconstruct(TypeError);
10993
- function unapply(func) {
10994
- return function(thisArg) {
10995
- if (thisArg instanceof RegExp) {
10996
- thisArg.lastIndex = 0;
10997
- }
10998
- for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
10999
- args[_key3 - 1] = arguments[_key3];
11000
- }
11001
- return apply(func, thisArg, args);
11002
- };
11003
- }
11004
- function unconstruct(Func) {
11005
- return function() {
11006
- for (var _len4 = arguments.length, args = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
11007
- args[_key4] = arguments[_key4];
11008
- }
11009
- return construct(Func, args);
11010
- };
11011
- }
11012
- function addToSet(set, array) {
11013
- let transformCaseFunc = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : stringToLowerCase;
11014
- if (setPrototypeOf) {
11015
- setPrototypeOf(set, null);
11016
- }
11017
- if (!arrayIsArray(array)) {
11018
- return set;
11019
- }
11020
- let l = array.length;
11021
- while (l--) {
11022
- let element = array[l];
11023
- if (typeof element === "string") {
11024
- const lcElement = transformCaseFunc(element);
11025
- if (lcElement !== element) {
11026
- if (!isFrozen(array)) {
11027
- array[l] = lcElement;
11028
- }
11029
- element = lcElement;
11030
- }
11031
- }
11032
- set[element] = true;
11033
- }
11034
- return set;
11035
- }
11036
- function cleanArray(array) {
11037
- for (let index = 0; index < array.length; index++) {
11038
- const isPropertyExist = objectHasOwnProperty(array, index);
11039
- if (!isPropertyExist) {
11040
- array[index] = null;
11041
- }
11042
- }
11043
- return array;
11044
- }
11045
- function clone(object) {
11046
- const newObject = create(null);
11047
- for (const _ref2 of entries(object)) {
11048
- var _ref3 = _slicedToArray(_ref2, 2);
11049
- const property = _ref3[0];
11050
- const value = _ref3[1];
11051
- const isPropertyExist = objectHasOwnProperty(object, property);
11052
- if (isPropertyExist) {
11053
- if (arrayIsArray(value)) {
11054
- newObject[property] = cleanArray(value);
11055
- } else if (value && typeof value === "object" && value.constructor === Object) {
11056
- newObject[property] = clone(value);
11057
- } else {
11058
- newObject[property] = value;
11059
- }
11060
- }
11061
- }
11062
- return newObject;
11063
- }
11064
- function stringifyValue(value) {
11065
- switch (typeof value) {
11066
- case "string": {
11067
- return value;
11068
- }
11069
- case "number": {
11070
- return numberToString(value);
11071
- }
11072
- case "boolean": {
11073
- return booleanToString(value);
11074
- }
11075
- case "bigint": {
11076
- return bigintToString ? bigintToString(value) : "0";
11077
- }
11078
- case "symbol": {
11079
- return symbolToString ? symbolToString(value) : "Symbol()";
11080
- }
11081
- case "undefined": {
11082
- return objectToString(value);
11083
- }
11084
- case "function":
11085
- case "object": {
11086
- if (value === null) {
11087
- return objectToString(value);
11088
- }
11089
- const valueAsRecord = value;
11090
- const valueToString = lookupGetter(valueAsRecord, "toString");
11091
- if (typeof valueToString === "function") {
11092
- const stringified = valueToString(valueAsRecord);
11093
- return typeof stringified === "string" ? stringified : objectToString(stringified);
11094
- }
11095
- return objectToString(value);
11096
- }
11097
- default: {
11098
- return objectToString(value);
11099
- }
11100
- }
11101
- }
11102
- function lookupGetter(object, prop) {
11103
- while (object !== null) {
11104
- const desc = getOwnPropertyDescriptor(object, prop);
11105
- if (desc) {
11106
- if (desc.get) {
11107
- return unapply(desc.get);
11108
- }
11109
- if (typeof desc.value === "function") {
11110
- return unapply(desc.value);
11111
- }
11112
- }
11113
- object = getPrototypeOf(object);
11114
- }
11115
- function fallbackValue() {
11116
- return null;
11117
- }
11118
- return fallbackValue;
11119
- }
11120
- function isRegex(value) {
11121
- try {
11122
- regExpTest(value, "");
11123
- return true;
11124
- } catch (_unused) {
11125
- return false;
10571
+ function isRegex(value) {
10572
+ try {
10573
+ regExpTest(value, "");
10574
+ return true;
10575
+ } catch (_unused) {
10576
+ return false;
11126
10577
  }
11127
10578
  }
11128
10579
  const html$1 = freeze(["a", "abbr", "acronym", "address", "area", "article", "aside", "audio", "b", "bdi", "bdo", "big", "blink", "blockquote", "body", "br", "button", "canvas", "caption", "center", "cite", "code", "col", "colgroup", "content", "data", "datalist", "dd", "decorator", "del", "details", "dfn", "dialog", "dir", "div", "dl", "dt", "element", "em", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6", "head", "header", "hgroup", "hr", "html", "i", "img", "input", "ins", "kbd", "label", "legend", "li", "main", "map", "mark", "marquee", "menu", "menuitem", "meter", "nav", "nobr", "ol", "optgroup", "option", "output", "p", "picture", "pre", "progress", "q", "rp", "rt", "ruby", "s", "samp", "search", "section", "select", "shadow", "slot", "small", "source", "spacer", "span", "strike", "strong", "style", "sub", "summary", "sup", "table", "tbody", "td", "template", "textarea", "tfoot", "th", "thead", "time", "tr", "track", "tt", "u", "ul", "var", "video", "wbr"]);
@@ -11225,7 +10676,7 @@ function createDOMPurify() {
11225
10676
  const HTMLTemplateElement = window2.HTMLTemplateElement, Node = window2.Node, Element = window2.Element, NodeFilter = window2.NodeFilter, _window$NamedNodeMap = window2.NamedNodeMap;
11226
10677
  _window$NamedNodeMap === void 0 ? window2.NamedNodeMap || window2.MozNamedAttrMap : _window$NamedNodeMap;
11227
10678
  window2.HTMLFormElement;
11228
- const DOMParser2 = window2.DOMParser, trustedTypes = window2.trustedTypes;
10679
+ const DOMParser = window2.DOMParser, trustedTypes = window2.trustedTypes;
11229
10680
  const ElementPrototype = Element.prototype;
11230
10681
  const cloneNode = lookupGetter(ElementPrototype, "cloneNode");
11231
10682
  const remove = lookupGetter(ElementPrototype, "remove");
@@ -11729,7 +11180,7 @@ function createDOMPurify() {
11729
11180
  const dirtyPayload = trustedTypesPolicy ? _createTrustedHTML(dirty) : dirty;
11730
11181
  if (NAMESPACE === HTML_NAMESPACE) {
11731
11182
  try {
11732
- doc = new DOMParser2().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
11183
+ doc = new DOMParser().parseFromString(dirtyPayload, PARSER_MEDIA_TYPE);
11733
11184
  } catch (_) {
11734
11185
  }
11735
11186
  }
@@ -12116,575 +11567,1265 @@ function createDOMPurify() {
12116
11567
  }
12117
11568
  }
12118
11569
  };
12119
- DOMPurify.sanitize = function(dirty) {
12120
- let cfg = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
12121
- let body = null;
12122
- let importedNode = null;
12123
- let currentNode = null;
12124
- let returnNode = null;
12125
- IS_EMPTY_INPUT = !dirty;
12126
- if (IS_EMPTY_INPUT) {
12127
- dirty = "<!-->";
11570
+ DOMPurify.sanitize = function(dirty) {
11571
+ let cfg = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
11572
+ let body = null;
11573
+ let importedNode = null;
11574
+ let currentNode = null;
11575
+ let returnNode = null;
11576
+ IS_EMPTY_INPUT = !dirty;
11577
+ if (IS_EMPTY_INPUT) {
11578
+ dirty = "<!-->";
11579
+ }
11580
+ if (typeof dirty !== "string" && !_isNode(dirty)) {
11581
+ dirty = stringifyValue(dirty);
11582
+ if (typeof dirty !== "string") {
11583
+ throw typeErrorCreate("dirty is not a string, aborting");
11584
+ }
11585
+ }
11586
+ if (!DOMPurify.isSupported) {
11587
+ return dirty;
11588
+ }
11589
+ if (!SET_CONFIG) {
11590
+ _parseConfig(cfg);
11591
+ }
11592
+ DOMPurify.removed = [];
11593
+ const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
11594
+ if (inPlace) {
11595
+ const nn = getNodeName ? getNodeName(dirty) : dirty.nodeName;
11596
+ if (typeof nn === "string") {
11597
+ const tagName = transformCaseFunc(nn);
11598
+ if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
11599
+ throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place");
11600
+ }
11601
+ }
11602
+ if (_isClobbered(dirty)) {
11603
+ throw typeErrorCreate("root node is clobbered and cannot be sanitized in-place");
11604
+ }
11605
+ try {
11606
+ _sanitizeAttachedShadowRoots(dirty);
11607
+ } catch (error) {
11608
+ _neutralizeRoot(dirty);
11609
+ throw error;
11610
+ }
11611
+ } else if (_isNode(dirty)) {
11612
+ body = _initDocument("<!---->");
11613
+ importedNode = body.ownerDocument.importNode(dirty, true);
11614
+ if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === "BODY") {
11615
+ body = importedNode;
11616
+ } else if (importedNode.nodeName === "HTML") {
11617
+ body = importedNode;
11618
+ } else {
11619
+ body.appendChild(importedNode);
11620
+ }
11621
+ _sanitizeAttachedShadowRoots(importedNode);
11622
+ } else {
11623
+ if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
11624
+ dirty.indexOf("<") === -1) {
11625
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
11626
+ }
11627
+ body = _initDocument(dirty);
11628
+ if (!body) {
11629
+ return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : "";
11630
+ }
11631
+ }
11632
+ if (body && FORCE_BODY) {
11633
+ _forceRemove(body.firstChild);
11634
+ }
11635
+ const nodeIterator = _createNodeIterator(inPlace ? dirty : body);
11636
+ try {
11637
+ while (currentNode = nodeIterator.nextNode()) {
11638
+ _sanitizeElements(currentNode);
11639
+ _sanitizeAttributes(currentNode);
11640
+ if (_isDocumentFragment(currentNode.content)) {
11641
+ _sanitizeShadowDOM2(currentNode.content);
11642
+ }
11643
+ }
11644
+ } catch (error) {
11645
+ if (inPlace) {
11646
+ _neutralizeRoot(dirty);
11647
+ }
11648
+ throw error;
11649
+ }
11650
+ if (inPlace) {
11651
+ arrayForEach(DOMPurify.removed, (entry) => {
11652
+ if (entry.element) {
11653
+ _neutralizeSubtree(entry.element);
11654
+ }
11655
+ });
11656
+ if (SAFE_FOR_TEMPLATES) {
11657
+ _scrubTemplateExpressions2(dirty);
11658
+ }
11659
+ return dirty;
11660
+ }
11661
+ if (RETURN_DOM) {
11662
+ if (SAFE_FOR_TEMPLATES) {
11663
+ _scrubTemplateExpressions2(body);
11664
+ }
11665
+ if (RETURN_DOM_FRAGMENT) {
11666
+ returnNode = createDocumentFragment.call(body.ownerDocument);
11667
+ while (body.firstChild) {
11668
+ returnNode.appendChild(body.firstChild);
11669
+ }
11670
+ } else {
11671
+ returnNode = body;
11672
+ }
11673
+ if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
11674
+ returnNode = importNode.call(originalDocument, returnNode, true);
11675
+ }
11676
+ return returnNode;
11677
+ }
11678
+ let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
11679
+ if (WHOLE_DOCUMENT && ALLOWED_TAGS["!doctype"] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
11680
+ serializedHTML = "<!DOCTYPE " + body.ownerDocument.doctype.name + ">\n" + serializedHTML;
11681
+ }
11682
+ if (SAFE_FOR_TEMPLATES) {
11683
+ arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => {
11684
+ serializedHTML = stringReplace(serializedHTML, expr, " ");
11685
+ });
11686
+ }
11687
+ return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
11688
+ };
11689
+ DOMPurify.setConfig = function() {
11690
+ let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
11691
+ _parseConfig(cfg);
11692
+ SET_CONFIG = true;
11693
+ };
11694
+ DOMPurify.clearConfig = function() {
11695
+ CONFIG = null;
11696
+ SET_CONFIG = false;
11697
+ trustedTypesPolicy = defaultTrustedTypesPolicy;
11698
+ emptyHTML = "";
11699
+ };
11700
+ DOMPurify.isValidAttribute = function(tag, attr, value) {
11701
+ if (!CONFIG) {
11702
+ _parseConfig({});
12128
11703
  }
12129
- if (typeof dirty !== "string" && !_isNode(dirty)) {
12130
- dirty = stringifyValue(dirty);
12131
- if (typeof dirty !== "string") {
12132
- throw typeErrorCreate("dirty is not a string, aborting");
12133
- }
11704
+ const lcTag = transformCaseFunc(tag);
11705
+ const lcName = transformCaseFunc(attr);
11706
+ return _isValidAttribute(lcTag, lcName, value);
11707
+ };
11708
+ DOMPurify.addHook = function(entryPoint, hookFunction) {
11709
+ if (typeof hookFunction !== "function") {
11710
+ return;
12134
11711
  }
12135
- if (!DOMPurify.isSupported) {
12136
- return dirty;
11712
+ arrayPush(hooks[entryPoint], hookFunction);
11713
+ };
11714
+ DOMPurify.removeHook = function(entryPoint, hookFunction) {
11715
+ if (hookFunction !== void 0) {
11716
+ const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
11717
+ return index === -1 ? void 0 : arraySplice(hooks[entryPoint], index, 1)[0];
12137
11718
  }
12138
- if (!SET_CONFIG) {
12139
- _parseConfig(cfg);
11719
+ return arrayPop(hooks[entryPoint]);
11720
+ };
11721
+ DOMPurify.removeHooks = function(entryPoint) {
11722
+ hooks[entryPoint] = [];
11723
+ };
11724
+ DOMPurify.removeAllHooks = function() {
11725
+ hooks = _createHooksMap();
11726
+ };
11727
+ return DOMPurify;
11728
+ }
11729
+ var purify = createDOMPurify();
11730
+ purify.addHook("afterSanitizeAttributes", (node) => {
11731
+ if (node.tagName === "A" && node.getAttribute("target") === "_blank") {
11732
+ node.setAttribute("rel", "noopener noreferrer");
11733
+ }
11734
+ });
11735
+ function sanitizeBannerHtml(html2) {
11736
+ if (!html2) return "";
11737
+ return purify.sanitize(html2, {
11738
+ ALLOWED_TAGS: ["p", "strong", "em", "u", "img", "span", "div", "a", "br"],
11739
+ ALLOWED_ATTR: ["style", "src", "class", "href", "target", "alt", "width", "height"],
11740
+ ADD_ATTR: ["target"],
11741
+ ALLOW_DATA_ATTR: false
11742
+ });
11743
+ }
11744
+ function resolveCssToken(token) {
11745
+ const normalizedToken = token.startsWith("--") ? token : `--${token}`;
11746
+ const candidates = [
11747
+ document.documentElement,
11748
+ document.body,
11749
+ ...Array.from(
11750
+ document.querySelectorAll(
11751
+ [
11752
+ "rai-widget",
11753
+ "rai-custom-widget",
11754
+ "rai-channel-widget",
11755
+ "rai-store-widget",
11756
+ "rai-milestone-widget",
11757
+ "rai-social-widget",
11758
+ "rai-currency-widget",
11759
+ "rai-referral-widget",
11760
+ ".rai-widget-root",
11761
+ '[data-rai-widget-root="true"]'
11762
+ ].join(",")
11763
+ )
11764
+ )
11765
+ ].filter(Boolean);
11766
+ for (const node of candidates) {
11767
+ const value = getComputedStyle(node).getPropertyValue(normalizedToken).trim();
11768
+ if (value) return value;
11769
+ }
11770
+ for (const node of Array.from(document.querySelectorAll("*"))) {
11771
+ const value = getComputedStyle(node).getPropertyValue(normalizedToken).trim();
11772
+ if (value) return value;
11773
+ }
11774
+ return void 0;
11775
+ }
11776
+ const safeColor = (v) => {
11777
+ if (!v) return void 0;
11778
+ const value = v.trim();
11779
+ if (/^(#|rgb|hsl)/i.test(value)) return value;
11780
+ if (/^(--)?[a-z0-9-]+$/i.test(value)) {
11781
+ const resolved = resolveCssToken(value);
11782
+ return resolved;
11783
+ }
11784
+ const varMatch = value.match(/^var\((--[a-z0-9-]+)\)$/i);
11785
+ if (varMatch) {
11786
+ const token = varMatch[1];
11787
+ const resolved = resolveCssToken(token);
11788
+ return resolved;
11789
+ }
11790
+ return void 0;
11791
+ };
11792
+ function el(tag, styles, attrs) {
11793
+ const node = document.createElement(tag);
11794
+ Object.assign(node.style, styles);
11795
+ if (attrs) {
11796
+ for (const k of Object.keys(attrs)) node.setAttribute(k, attrs[k]);
11797
+ }
11798
+ return node;
11799
+ }
11800
+ function ctaAlign(pos) {
11801
+ if (pos === "left") return "flex-start";
11802
+ if (pos === "right") return "flex-end";
11803
+ return "center";
11804
+ }
11805
+ function isImageBanner(banner) {
11806
+ var _a, _b;
11807
+ return banner.type === "image" && Boolean(((_a = banner.image) == null ? void 0 : _a.desktopUrl) || ((_b = banner.image) == null ? void 0 : _b.mobileUrl));
11808
+ }
11809
+ function imageAlign(pos) {
11810
+ if (pos === "left" || pos === "top") return "flex-start";
11811
+ if (pos === "right" || pos === "bottom") return "flex-end";
11812
+ return "center";
11813
+ }
11814
+ function imageObjectPosition(horizontal, vertical) {
11815
+ const x = horizontal === "left" ? "left" : horizontal === "right" ? "right" : "center";
11816
+ const y = vertical === "top" ? "top" : vertical === "bottom" ? "bottom" : "center";
11817
+ return `${x} ${y}`;
11818
+ }
11819
+ function getCtaStyleValue(banner, keys) {
11820
+ var _a;
11821
+ const sources = [(_a = banner.cta) == null ? void 0 : _a.style, banner.cta, banner.ctaStyle];
11822
+ for (const source of sources) {
11823
+ for (const key of keys) {
11824
+ const value = source == null ? void 0 : source[key];
11825
+ if (value) return value;
12140
11826
  }
12141
- DOMPurify.removed = [];
12142
- const inPlace = IN_PLACE && typeof dirty !== "string" && _isNode(dirty);
12143
- if (inPlace) {
12144
- const nn = getNodeName ? getNodeName(dirty) : dirty.nodeName;
12145
- if (typeof nn === "string") {
12146
- const tagName = transformCaseFunc(nn);
12147
- if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
12148
- throw typeErrorCreate("root node is forbidden and cannot be sanitized in-place");
12149
- }
12150
- }
12151
- if (_isClobbered(dirty)) {
12152
- throw typeErrorCreate("root node is clobbered and cannot be sanitized in-place");
12153
- }
12154
- try {
12155
- _sanitizeAttachedShadowRoots(dirty);
12156
- } catch (error) {
12157
- _neutralizeRoot(dirty);
12158
- throw error;
11827
+ }
11828
+ return void 0;
11829
+ }
11830
+ function styleRichText(container) {
11831
+ container.querySelectorAll("p").forEach((p) => {
11832
+ p.style.margin = "0";
11833
+ p.style.whiteSpace = "normal";
11834
+ p.style.overflowWrap = "anywhere";
11835
+ });
11836
+ container.querySelectorAll("strong").forEach((strong) => {
11837
+ strong.style.fontWeight = "700";
11838
+ strong.style.whiteSpace = "normal";
11839
+ });
11840
+ container.querySelectorAll("em").forEach((em) => {
11841
+ em.style.fontStyle = "italic";
11842
+ em.style.whiteSpace = "normal";
11843
+ });
11844
+ container.querySelectorAll("u").forEach((u) => {
11845
+ u.style.textDecoration = "underline";
11846
+ u.style.whiteSpace = "normal";
11847
+ });
11848
+ container.querySelectorAll("a").forEach((a) => {
11849
+ a.style.color = "#3b82f6";
11850
+ a.style.textDecoration = "underline";
11851
+ a.style.whiteSpace = "normal";
11852
+ a.style.overflowWrap = "anywhere";
11853
+ if (a.getAttribute("target") === "_blank") {
11854
+ a.setAttribute("rel", "noopener noreferrer");
11855
+ }
11856
+ });
11857
+ container.querySelectorAll("img").forEach((img) => {
11858
+ img.style.maxWidth = "100%";
11859
+ img.style.height = "auto";
11860
+ });
11861
+ }
11862
+ function readTextAlign(container) {
11863
+ const aligned = container.querySelector(
11864
+ '[style*="text-align"]'
11865
+ );
11866
+ const value = ((aligned == null ? void 0 : aligned.style.textAlign) || container.style.textAlign || "").toLowerCase().trim();
11867
+ if (value === "center" || value === "right" || value === "justify") return value;
11868
+ return "left";
11869
+ }
11870
+ function justifyFromTextAlign(align) {
11871
+ if (align === "center") return "center";
11872
+ if (align === "right") return "flex-end";
11873
+ return "flex-start";
11874
+ }
11875
+ function renderBanner(banner, handlers) {
11876
+ var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
11877
+ const placement = banner.placement || {};
11878
+ const mode = placement.mode || "overlay";
11879
+ const position = placement.position || "top";
11880
+ const zIndex = String(placement.zIndex || 2147483e3);
11881
+ const imageBanner = isImageBanner(banner);
11882
+ const bg = imageBanner ? "transparent" : safeColor((_a = banner.style) == null ? void 0 : _a.backgroundColor) || "#1f2024";
11883
+ const color = safeColor((_b = banner.style) == null ? void 0 : _b.color) || "#ffffff";
11884
+ const isCard = mode === "overlay" || mode === "popup";
11885
+ const ctaPosition = getCtaStyleValue(banner, ["position"]) || "center";
11886
+ const imageHorizontal = imageAlign((_c = banner.image) == null ? void 0 : _c.horizontalAlignment);
11887
+ const imageVertical = imageAlign((_d = banner.image) == null ? void 0 : _d.verticalAlignment);
11888
+ const imageHorizontalPosition = ((_e = banner.image) == null ? void 0 : _e.horizontalAlignment) || "center";
11889
+ const imageVerticalPosition = ((_f = banner.image) == null ? void 0 : _f.verticalAlignment) || "center";
11890
+ const closeColor = imageBanner ? "#ffffff" : color;
11891
+ const root = el("div", {
11892
+ boxSizing: "border-box",
11893
+ fontFamily: "Roboto, sans-serif",
11894
+ fontSize: "14px",
11895
+ lineHeight: "1.35",
11896
+ background: bg,
11897
+ color,
11898
+ borderRadius: isCard && !imageBanner ? "6px" : "0",
11899
+ boxShadow: isCard && !imageBanner ? "0 8px 24px rgba(0,0,0,0.22)" : "none",
11900
+ padding: imageBanner ? "0" : isCard ? "12px 42px 12px 24px" : "8px 42px 8px 16px",
11901
+ display: "flex",
11902
+ alignItems: imageBanner ? imageVertical : "flex-start",
11903
+ justifyContent: "center",
11904
+ gap: imageBanner ? "0" : "16px"
11905
+ });
11906
+ root.setAttribute("role", isCard ? "dialog" : "region");
11907
+ root.setAttribute("aria-label", "community banner");
11908
+ root.setAttribute("data-renderer-version", "sdk-banner-preview-layout");
11909
+ if (isCard) {
11910
+ Object.assign(root.style, {
11911
+ position: "fixed",
11912
+ left: "50%",
11913
+ width: "fit-content",
11914
+ maxWidth: imageBanner ? "70vw" : "min(700px, 80vw)",
11915
+ height: "auto",
11916
+ minHeight: imageBanner ? "0" : "80px",
11917
+ maxHeight: imageBanner ? "70vh" : "calc(100vh - 300px)",
11918
+ overflowY: imageBanner ? "visible" : "auto",
11919
+ zIndex
11920
+ });
11921
+ if (imageBanner) {
11922
+ const translate = [];
11923
+ if (imageHorizontalPosition === "left") {
11924
+ root.style.left = "24px";
11925
+ } else if (imageHorizontalPosition === "right") {
11926
+ root.style.left = "";
11927
+ root.style.right = "24px";
11928
+ } else {
11929
+ root.style.left = "50%";
11930
+ translate.push("-50%");
12159
11931
  }
12160
- } else if (_isNode(dirty)) {
12161
- body = _initDocument("<!---->");
12162
- importedNode = body.ownerDocument.importNode(dirty, true);
12163
- if (importedNode.nodeType === NODE_TYPE.element && importedNode.nodeName === "BODY") {
12164
- body = importedNode;
12165
- } else if (importedNode.nodeName === "HTML") {
12166
- body = importedNode;
11932
+ if (imageVerticalPosition === "top") {
11933
+ root.style.top = "10px";
11934
+ } else if (imageVerticalPosition === "bottom") {
11935
+ root.style.bottom = "24px";
12167
11936
  } else {
12168
- body.appendChild(importedNode);
11937
+ root.style.top = "50%";
11938
+ translate.push("-50%");
12169
11939
  }
12170
- _sanitizeAttachedShadowRoots(importedNode);
11940
+ if (translate.length === 2) root.style.transform = `translate(${translate[0]}, ${translate[1]})`;
11941
+ else if (translate.length === 1 && imageHorizontalPosition === "center") root.style.transform = `translateX(${translate[0]})`;
11942
+ else if (translate.length === 1) root.style.transform = `translateY(${translate[0]})`;
11943
+ else root.style.transform = "";
11944
+ } else if (position === "bottom") {
11945
+ root.style.bottom = "24px";
11946
+ root.style.transform = "translateX(-50%)";
11947
+ } else if (position === "center") {
11948
+ root.style.top = "50%";
11949
+ root.style.transform = "translate(-50%, -50%)";
12171
11950
  } else {
12172
- if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && // eslint-disable-next-line unicorn/prefer-includes
12173
- dirty.indexOf("<") === -1) {
12174
- return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(dirty) : dirty;
12175
- }
12176
- body = _initDocument(dirty);
12177
- if (!body) {
12178
- return RETURN_DOM ? null : RETURN_TRUSTED_TYPE ? emptyHTML : "";
12179
- }
12180
- }
12181
- if (body && FORCE_BODY) {
12182
- _forceRemove(body.firstChild);
12183
- }
12184
- const nodeIterator = _createNodeIterator(inPlace ? dirty : body);
12185
- try {
12186
- while (currentNode = nodeIterator.nextNode()) {
12187
- _sanitizeElements(currentNode);
12188
- _sanitizeAttributes(currentNode);
12189
- if (_isDocumentFragment(currentNode.content)) {
12190
- _sanitizeShadowDOM2(currentNode.content);
12191
- }
12192
- }
12193
- } catch (error) {
12194
- if (inPlace) {
12195
- _neutralizeRoot(dirty);
12196
- }
12197
- throw error;
11951
+ root.style.top = "10px";
11952
+ root.style.transform = "translateX(-50%)";
12198
11953
  }
12199
- if (inPlace) {
12200
- arrayForEach(DOMPurify.removed, (entry) => {
12201
- if (entry.element) {
12202
- _neutralizeSubtree(entry.element);
11954
+ } else {
11955
+ Object.assign(root.style, { position: "relative", width: "100%", zIndex });
11956
+ }
11957
+ const contentWrap = el("div", {
11958
+ display: "flex",
11959
+ width: imageBanner || isCard ? "auto" : "100%",
11960
+ maxWidth: "100%",
11961
+ flexDirection: "column",
11962
+ gap: imageBanner ? "0" : "12px",
11963
+ overflow: "hidden"
11964
+ });
11965
+ const contentRow = el("div", {
11966
+ display: "flex",
11967
+ width: imageBanner || isCard ? "auto" : "100%",
11968
+ justifyContent: imageBanner ? imageHorizontal : "space-between",
11969
+ alignItems: imageBanner ? imageVertical : "flex-start",
11970
+ gap: "12px"
11971
+ });
11972
+ const content = el("div", {
11973
+ width: imageBanner || isCard ? "auto" : "100%",
11974
+ maxWidth: imageBanner ? "70vw" : isCard ? "calc(min(700px, 80vw) - 66px)" : "100%",
11975
+ overflow: "hidden",
11976
+ wordBreak: "normal",
11977
+ overflowWrap: "anywhere",
11978
+ whiteSpace: "normal",
11979
+ color
11980
+ });
11981
+ const imageUrl = ((_g = banner.image) == null ? void 0 : _g.desktopUrl) || ((_h = banner.image) == null ? void 0 : _h.mobileUrl);
11982
+ if (imageBanner && imageUrl) {
11983
+ const imageLinkUrl = (_i = banner.image) == null ? void 0 : _i.linkUrl;
11984
+ const imageOpenInNewTab = (_j = banner.image) == null ? void 0 : _j.openInNewTab;
11985
+ const img = el(
11986
+ "img",
11987
+ {
11988
+ display: "block",
11989
+ maxWidth: "70vw",
11990
+ maxHeight: "70vh",
11991
+ width: "auto",
11992
+ height: "auto",
11993
+ objectFit: "contain",
11994
+ objectPosition: imageObjectPosition(
11995
+ (_k = banner.image) == null ? void 0 : _k.horizontalAlignment,
11996
+ (_l = banner.image) == null ? void 0 : _l.verticalAlignment
11997
+ ),
11998
+ cursor: imageLinkUrl ? "pointer" : "",
11999
+ borderRadius: ((_m = banner.image) == null ? void 0 : _m.cornerRadius) === "rounded" ? "8px" : "0"
12000
+ },
12001
+ { src: imageUrl, alt: banner.title || "banner" }
12002
+ );
12003
+ if (imageLinkUrl) {
12004
+ img.addEventListener("click", () => {
12005
+ if (imageOpenInNewTab) {
12006
+ window.open(imageLinkUrl, "_blank", "noopener,noreferrer");
12007
+ } else {
12008
+ window.location.href = imageLinkUrl;
12203
12009
  }
12204
12010
  });
12205
- if (SAFE_FOR_TEMPLATES) {
12206
- _scrubTemplateExpressions2(dirty);
12207
- }
12208
- return dirty;
12209
- }
12210
- if (RETURN_DOM) {
12211
- if (SAFE_FOR_TEMPLATES) {
12212
- _scrubTemplateExpressions2(body);
12213
- }
12214
- if (RETURN_DOM_FRAGMENT) {
12215
- returnNode = createDocumentFragment.call(body.ownerDocument);
12216
- while (body.firstChild) {
12217
- returnNode.appendChild(body.firstChild);
12218
- }
12219
- } else {
12220
- returnNode = body;
12221
- }
12222
- if (ALLOWED_ATTR.shadowroot || ALLOWED_ATTR.shadowrootmode) {
12223
- returnNode = importNode.call(originalDocument, returnNode, true);
12224
- }
12225
- return returnNode;
12226
12011
  }
12227
- let serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
12228
- if (WHOLE_DOCUMENT && ALLOWED_TAGS["!doctype"] && body.ownerDocument && body.ownerDocument.doctype && body.ownerDocument.doctype.name && regExpTest(DOCTYPE_NAME, body.ownerDocument.doctype.name)) {
12229
- serializedHTML = "<!DOCTYPE " + body.ownerDocument.doctype.name + ">\n" + serializedHTML;
12012
+ content.appendChild(img);
12013
+ } else {
12014
+ content.innerHTML = sanitizeBannerHtml(banner.text);
12015
+ styleRichText(content);
12016
+ const textAlign = readTextAlign(content);
12017
+ content.style.textAlign = textAlign;
12018
+ if (isCard) {
12019
+ contentRow.style.justifyContent = justifyFromTextAlign(textAlign);
12230
12020
  }
12231
- if (SAFE_FOR_TEMPLATES) {
12232
- arrayForEach([MUSTACHE_EXPR$1, ERB_EXPR$1, TMPLIT_EXPR$1], (expr) => {
12233
- serializedHTML = stringReplace(serializedHTML, expr, " ");
12021
+ }
12022
+ if (!imageBanner && ((_n = banner.cta) == null ? void 0 : _n.enabled)) {
12023
+ const ctaBg = safeColor(
12024
+ getCtaStyleValue(banner, ["backgroundColor", "buttonColor", "aColor"])
12025
+ ) || "#2f6df6";
12026
+ const ctaColor = safeColor(getCtaStyleValue(banner, ["textColor", "color"])) || "#ffffff";
12027
+ const btn = el("button", {
12028
+ cursor: "pointer",
12029
+ border: "none",
12030
+ borderRadius: "6px",
12031
+ height: "36px",
12032
+ minHeight: "36px",
12033
+ padding: "0 16px",
12034
+ fontFamily: "Roboto, sans-serif",
12035
+ fontSize: "12px",
12036
+ fontWeight: "600",
12037
+ lineHeight: "20px",
12038
+ backgroundColor: ctaBg,
12039
+ color: ctaColor,
12040
+ boxSizing: "border-box",
12041
+ display: "inline-flex",
12042
+ alignItems: "center",
12043
+ justifyContent: "center"
12044
+ });
12045
+ btn.type = "button";
12046
+ btn.textContent = banner.cta.label || "CTA Button";
12047
+ btn.addEventListener("click", handlers.onCtaClick);
12048
+ if (ctaPosition === "inline") {
12049
+ contentRow.appendChild(content);
12050
+ const inlineWrap = el("div", {
12051
+ display: "flex",
12052
+ flexDirection: "column",
12053
+ alignItems: "flex-end",
12054
+ justifyContent: "center",
12055
+ flexShrink: "0"
12234
12056
  });
12057
+ inlineWrap.appendChild(btn);
12058
+ contentRow.appendChild(inlineWrap);
12059
+ contentWrap.appendChild(contentRow);
12060
+ } else {
12061
+ contentRow.appendChild(content);
12062
+ contentWrap.appendChild(contentRow);
12063
+ const wrap = el("div", {
12064
+ display: "flex",
12065
+ width: "100%",
12066
+ justifyContent: ctaAlign(ctaPosition)
12067
+ });
12068
+ wrap.appendChild(btn);
12069
+ contentWrap.appendChild(wrap);
12235
12070
  }
12236
- return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? _createTrustedHTML(serializedHTML) : serializedHTML;
12237
- };
12238
- DOMPurify.setConfig = function() {
12239
- let cfg = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : {};
12240
- _parseConfig(cfg);
12241
- SET_CONFIG = true;
12242
- };
12243
- DOMPurify.clearConfig = function() {
12244
- CONFIG = null;
12245
- SET_CONFIG = false;
12246
- trustedTypesPolicy = defaultTrustedTypesPolicy;
12247
- emptyHTML = "";
12248
- };
12249
- DOMPurify.isValidAttribute = function(tag, attr, value) {
12250
- if (!CONFIG) {
12251
- _parseConfig({});
12252
- }
12253
- const lcTag = transformCaseFunc(tag);
12254
- const lcName = transformCaseFunc(attr);
12255
- return _isValidAttribute(lcTag, lcName, value);
12256
- };
12257
- DOMPurify.addHook = function(entryPoint, hookFunction) {
12258
- if (typeof hookFunction !== "function") {
12259
- return;
12260
- }
12261
- arrayPush(hooks[entryPoint], hookFunction);
12262
- };
12263
- DOMPurify.removeHook = function(entryPoint, hookFunction) {
12264
- if (hookFunction !== void 0) {
12265
- const index = arrayLastIndexOf(hooks[entryPoint], hookFunction);
12266
- return index === -1 ? void 0 : arraySplice(hooks[entryPoint], index, 1)[0];
12267
- }
12268
- return arrayPop(hooks[entryPoint]);
12269
- };
12270
- DOMPurify.removeHooks = function(entryPoint) {
12271
- hooks[entryPoint] = [];
12272
- };
12273
- DOMPurify.removeAllHooks = function() {
12274
- hooks = _createHooksMap();
12275
- };
12276
- return DOMPurify;
12071
+ } else {
12072
+ contentRow.appendChild(content);
12073
+ contentWrap.appendChild(contentRow);
12074
+ }
12075
+ root.appendChild(contentWrap);
12076
+ const close = el("button", {
12077
+ position: isCard ? "absolute" : "",
12078
+ top: isCard ? imageBanner ? "16px" : "12px" : "",
12079
+ right: isCard ? imageBanner ? "16px" : "12px" : "",
12080
+ marginTop: isCard ? "0" : "8px",
12081
+ width: "20px",
12082
+ minWidth: "20px",
12083
+ height: "20px",
12084
+ cursor: "pointer",
12085
+ border: "none",
12086
+ borderRadius: "50%",
12087
+ background: "rgba(0,0,0,0.25)",
12088
+ color: closeColor,
12089
+ fontSize: "16px",
12090
+ lineHeight: "1",
12091
+ display: "flex",
12092
+ alignItems: "center",
12093
+ justifyContent: "center",
12094
+ padding: "0",
12095
+ flexShrink: "0"
12096
+ });
12097
+ close.type = "button";
12098
+ close.setAttribute("aria-label", "dismiss banner");
12099
+ close.innerHTML = "&times;";
12100
+ close.addEventListener("click", handlers.onDismiss);
12101
+ root.appendChild(close);
12102
+ return root;
12277
12103
  }
12278
- var purify = createDOMPurify();
12279
- purify.addHook("afterSanitizeAttributes", (node) => {
12280
- if (node.tagName === "A" && node.getAttribute("target") === "_blank") {
12281
- node.setAttribute("rel", "noopener noreferrer");
12104
+ const VISITOR_KEY = "rai-banner-visitor";
12105
+ function uuid() {
12106
+ try {
12107
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
12108
+ return crypto.randomUUID();
12109
+ }
12110
+ } catch {
12282
12111
  }
12283
- });
12284
- function sanitizeBannerHtml(html2) {
12285
- if (!html2) return "";
12286
- return purify.sanitize(html2, {
12287
- ALLOWED_TAGS: ["p", "strong", "em", "u", "img", "span", "div", "a", "br"],
12288
- ALLOWED_ATTR: ["style", "src", "class", "href", "target", "alt", "width", "height"],
12289
- ADD_ATTR: ["target"],
12290
- ALLOW_DATA_ATTR: false
12112
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
12113
+ const r = Math.random() * 16 | 0;
12114
+ const v = c === "x" ? r : r & 3 | 8;
12115
+ return v.toString(16);
12291
12116
  });
12292
12117
  }
12293
- function resolveCssToken(token) {
12294
- const normalizedToken = token.startsWith("--") ? token : `--${token}`;
12295
- const candidates = [
12296
- document.documentElement,
12297
- document.body,
12298
- ...Array.from(
12299
- document.querySelectorAll(
12300
- [
12301
- "rai-widget",
12302
- "rai-custom-widget",
12303
- "rai-channel-widget",
12304
- "rai-store-widget",
12305
- "rai-milestone-widget",
12306
- "rai-social-widget",
12307
- "rai-currency-widget",
12308
- "rai-referral-widget",
12309
- ".rai-widget-root",
12310
- '[data-rai-widget-root="true"]'
12311
- ].join(",")
12312
- )
12313
- )
12314
- ].filter(Boolean);
12315
- for (const node of candidates) {
12316
- const value = getComputedStyle(node).getPropertyValue(normalizedToken).trim();
12317
- if (value) return value;
12118
+ function getOrCreateVisitorId(communityId, storagePrefix) {
12119
+ const key = `${storagePrefix}-${VISITOR_KEY}-${communityId}`;
12120
+ try {
12121
+ let id = localStorage.getItem(key);
12122
+ if (!id) {
12123
+ id = uuid();
12124
+ localStorage.setItem(key, id);
12125
+ }
12126
+ return id;
12127
+ } catch {
12128
+ return uuid();
12318
12129
  }
12319
- for (const node of Array.from(document.querySelectorAll("*"))) {
12320
- const value = getComputedStyle(node).getPropertyValue(normalizedToken).trim();
12321
- if (value) return value;
12130
+ }
12131
+ function fireTracking(url) {
12132
+ if (!url) return;
12133
+ try {
12134
+ const img = new Image();
12135
+ img.src = url;
12136
+ } catch {
12322
12137
  }
12323
- return void 0;
12324
12138
  }
12325
- const safeColor = (v) => {
12326
- if (!v) return void 0;
12327
- const value = v.trim();
12328
- if (/^(#|rgb|hsl)/i.test(value)) return value;
12329
- if (/^(--)?[a-z0-9-]+$/i.test(value)) {
12330
- const resolved = resolveCssToken(value);
12331
- return resolved;
12139
+ function storageKey(config) {
12140
+ return `${config.storagePrefix}-${config.widgetId}-${config.container}-auth`;
12141
+ }
12142
+ function legacyStorageKey(config) {
12143
+ return `${config.storagePrefix}-${config.widgetId}-auth`;
12144
+ }
12145
+ function errorSettingsKey(config) {
12146
+ return `${config.storagePrefix}-${config.widgetId}-error-settings`;
12147
+ }
12148
+ function saveToStorage(config, state) {
12149
+ try {
12150
+ const data = {
12151
+ refreshToken: state.refreshToken,
12152
+ tokenFamily: state.tokenFamily,
12153
+ refreshTokenExpiry: state.refreshTokenExpiry
12154
+ };
12155
+ localStorage.setItem(storageKey(config), JSON.stringify(data));
12156
+ } catch {
12332
12157
  }
12333
- const varMatch = value.match(/^var\((--[a-z0-9-]+)\)$/i);
12334
- if (varMatch) {
12335
- const token = varMatch[1];
12336
- const resolved = resolveCssToken(token);
12337
- return resolved;
12158
+ }
12159
+ function loadFromStorage(config, state) {
12160
+ try {
12161
+ let raw = localStorage.getItem(storageKey(config));
12162
+ const loadedLegacyKey = !raw;
12163
+ if (!raw) {
12164
+ raw = localStorage.getItem(legacyStorageKey(config));
12165
+ }
12166
+ if (!raw) return false;
12167
+ const parsed = JSON.parse(raw);
12168
+ if (parsed.refreshToken && !isTokenExpired(parsed.refreshTokenExpiry)) {
12169
+ state.refreshToken = parsed.refreshToken;
12170
+ state.tokenFamily = parsed.tokenFamily;
12171
+ state.refreshTokenExpiry = parsed.refreshTokenExpiry;
12172
+ if (loadedLegacyKey) {
12173
+ saveToStorage(config, state);
12174
+ localStorage.removeItem(legacyStorageKey(config));
12175
+ }
12176
+ return true;
12177
+ }
12178
+ clearStorage(config);
12179
+ return false;
12180
+ } catch {
12181
+ return false;
12338
12182
  }
12339
- return void 0;
12340
- };
12341
- function el(tag, styles, attrs) {
12342
- const node = document.createElement(tag);
12343
- Object.assign(node.style, styles);
12344
- if (attrs) {
12345
- for (const k of Object.keys(attrs)) node.setAttribute(k, attrs[k]);
12183
+ }
12184
+ function clearStorage(config) {
12185
+ try {
12186
+ localStorage.removeItem(storageKey(config));
12187
+ localStorage.removeItem(legacyStorageKey(config));
12188
+ } catch {
12346
12189
  }
12347
- return node;
12348
12190
  }
12349
- function ctaAlign(pos) {
12350
- if (pos === "left") return "flex-start";
12351
- if (pos === "right") return "flex-end";
12352
- return "center";
12191
+ const ERROR_SETTINGS_CACHE_TTL = 36e5;
12192
+ function loadErrorSettingsFromStorage(config) {
12193
+ try {
12194
+ const raw = localStorage.getItem(errorSettingsKey(config));
12195
+ if (!raw) return null;
12196
+ const parsed = JSON.parse(raw);
12197
+ if (parsed.cachedAt && Date.now() - parsed.cachedAt < ERROR_SETTINGS_CACHE_TTL) {
12198
+ return parsed;
12199
+ }
12200
+ localStorage.removeItem(errorSettingsKey(config));
12201
+ return null;
12202
+ } catch {
12203
+ return null;
12204
+ }
12353
12205
  }
12354
- function isImageBanner(banner) {
12355
- var _a, _b;
12356
- return banner.type === "image" && Boolean(((_a = banner.image) == null ? void 0 : _a.desktopUrl) || ((_b = banner.image) == null ? void 0 : _b.mobileUrl));
12206
+ function saveErrorSettingsToStorage(config, settings) {
12207
+ try {
12208
+ localStorage.setItem(errorSettingsKey(config), JSON.stringify({ ...settings, cachedAt: Date.now() }));
12209
+ } catch {
12210
+ }
12357
12211
  }
12358
- function imageAlign(pos) {
12359
- if (pos === "left" || pos === "top") return "flex-start";
12360
- if (pos === "right" || pos === "bottom") return "flex-end";
12361
- return "center";
12212
+ function isTokenExpired(expiry) {
12213
+ if (!expiry) return true;
12214
+ return Date.now() >= expiry - 6e4;
12362
12215
  }
12363
- function imageObjectPosition(horizontal, vertical) {
12364
- const x = horizontal === "left" ? "left" : horizontal === "right" ? "right" : "center";
12365
- const y = vertical === "top" ? "top" : vertical === "bottom" ? "bottom" : "center";
12366
- return `${x} ${y}`;
12216
+ function setTokens(config, state, data, onRefreshScheduled) {
12217
+ state.accessToken = data.accessToken;
12218
+ state.refreshToken = data.refreshToken;
12219
+ state.tokenFamily = data.tokenFamily ?? null;
12220
+ const now = Date.now();
12221
+ state.accessTokenExpiry = now + data.accessTokenTTL * 1e3;
12222
+ state.refreshTokenExpiry = now + data.refreshTokenTTL * 1e3;
12223
+ saveToStorage(config, state);
12224
+ if (config.autoRefresh && onRefreshScheduled) {
12225
+ onRefreshScheduled();
12226
+ }
12367
12227
  }
12368
- function getCtaStyleValue(banner, keys) {
12369
- var _a;
12370
- const sources = [(_a = banner.cta) == null ? void 0 : _a.style, banner.cta, banner.ctaStyle];
12371
- for (const source of sources) {
12372
- for (const key of keys) {
12373
- const value = source == null ? void 0 : source[key];
12374
- if (value) return value;
12228
+ function buildIdentityProof(config) {
12229
+ const proof = {};
12230
+ if (config.embedToken) {
12231
+ proof.embedToken = config.embedToken;
12232
+ }
12233
+ if (config.userIdentifiers && Object.keys(config.userIdentifiers).length > 0) {
12234
+ proof.userIdentifiers = config.userIdentifiers;
12235
+ }
12236
+ return proof;
12237
+ }
12238
+ async function authenticateServerless(config, state, onRefreshScheduled) {
12239
+ const nonce = crypto.randomUUID();
12240
+ const timestamp = Date.now();
12241
+ const maxRetries = config.maxRetries ?? 3;
12242
+ const retryDelay = config.retryDelay ?? 500;
12243
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
12244
+ if (attempt > 0) {
12245
+ await new Promise(
12246
+ (resolve) => setTimeout(resolve, retryDelay * (1 << attempt - 1))
12247
+ );
12248
+ }
12249
+ let is4xx = false;
12250
+ try {
12251
+ const body = {
12252
+ nonce,
12253
+ timestamp,
12254
+ widgetType: config.widgetType,
12255
+ ...buildIdentityProof(config)
12256
+ };
12257
+ const response = await fetch(`${config.apiUrl}/${config.widgetId}/auth/serverless`, {
12258
+ method: "POST",
12259
+ headers: { "Content-Type": "application/json" },
12260
+ body: JSON.stringify(body),
12261
+ credentials: "include"
12262
+ });
12263
+ if (!response.ok) {
12264
+ is4xx = response.status >= 400 && response.status < 500;
12265
+ const errorBody = await response.json().catch(() => ({ error: "Authentication failed" }));
12266
+ if (is4xx) {
12267
+ state.lastAuthError = {
12268
+ status: response.status,
12269
+ error: errorBody == null ? void 0 : errorBody.error,
12270
+ hint: errorBody == null ? void 0 : errorBody.hint
12271
+ };
12272
+ console.error(
12273
+ `[rai-widget] /auth/serverless ${response.status}:`,
12274
+ (errorBody == null ? void 0 : errorBody.error) || "Authentication failed",
12275
+ (errorBody == null ? void 0 : errorBody.hint) ? `— ${errorBody.hint}` : ""
12276
+ );
12277
+ }
12278
+ throw new Error((errorBody == null ? void 0 : errorBody.error) || `HTTP ${response.status}`);
12279
+ }
12280
+ state.lastAuthError = null;
12281
+ const data = await response.json();
12282
+ if (!data.accessToken || !data.refreshToken) {
12283
+ throw new Error("Invalid authentication response");
12284
+ }
12285
+ setTokens(config, state, data, onRefreshScheduled);
12286
+ state.isAuthenticated = true;
12287
+ return true;
12288
+ } catch {
12289
+ if (is4xx || attempt === maxRetries) {
12290
+ state.isAuthenticated = false;
12291
+ return false;
12292
+ }
12375
12293
  }
12376
12294
  }
12377
- return void 0;
12295
+ state.isAuthenticated = false;
12296
+ return false;
12378
12297
  }
12379
- function styleRichText(container) {
12380
- container.querySelectorAll("p").forEach((p) => {
12381
- p.style.margin = "0";
12382
- p.style.whiteSpace = "normal";
12383
- p.style.overflowWrap = "anywhere";
12384
- });
12385
- container.querySelectorAll("strong").forEach((strong) => {
12386
- strong.style.fontWeight = "700";
12387
- strong.style.whiteSpace = "normal";
12388
- });
12389
- container.querySelectorAll("em").forEach((em) => {
12390
- em.style.fontStyle = "italic";
12391
- em.style.whiteSpace = "normal";
12392
- });
12393
- container.querySelectorAll("u").forEach((u) => {
12394
- u.style.textDecoration = "underline";
12395
- u.style.whiteSpace = "normal";
12396
- });
12397
- container.querySelectorAll("a").forEach((a) => {
12398
- a.style.color = "#3b82f6";
12399
- a.style.textDecoration = "underline";
12400
- a.style.whiteSpace = "normal";
12401
- a.style.overflowWrap = "anywhere";
12402
- if (a.getAttribute("target") === "_blank") {
12403
- a.setAttribute("rel", "noopener noreferrer");
12298
+ async function authenticateViaProxy(config, state, onRefreshScheduled) {
12299
+ const maxRetries = config.maxRetries ?? 3;
12300
+ const retryDelay = config.retryDelay ?? 500;
12301
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
12302
+ if (attempt > 0) {
12303
+ await new Promise(
12304
+ (resolve) => setTimeout(resolve, retryDelay * (1 << attempt - 1))
12305
+ );
12404
12306
  }
12405
- });
12406
- container.querySelectorAll("img").forEach((img) => {
12407
- img.style.maxWidth = "100%";
12408
- img.style.height = "auto";
12409
- });
12410
- }
12411
- function readTextAlign(container) {
12412
- const aligned = container.querySelector(
12413
- '[style*="text-align"]'
12414
- );
12415
- const value = ((aligned == null ? void 0 : aligned.style.textAlign) || container.style.textAlign || "").toLowerCase().trim();
12416
- if (value === "center" || value === "right" || value === "justify") return value;
12417
- return "left";
12418
- }
12419
- function justifyFromTextAlign(align) {
12420
- if (align === "center") return "center";
12421
- if (align === "right") return "flex-end";
12422
- return "flex-start";
12307
+ let is4xx = false;
12308
+ try {
12309
+ const response = await fetch(config.authUrl, {
12310
+ method: "POST",
12311
+ credentials: "include",
12312
+ headers: { "Content-Type": "application/json" }
12313
+ });
12314
+ if (!response.ok) {
12315
+ is4xx = response.status >= 400 && response.status < 500;
12316
+ throw new Error(`HTTP ${response.status}`);
12317
+ }
12318
+ const data = await response.json();
12319
+ if (!data.token) {
12320
+ throw new Error("Invalid proxy auth response");
12321
+ }
12322
+ const ttl = data.expiresIn ?? 300;
12323
+ state.accessToken = data.token;
12324
+ state.refreshToken = null;
12325
+ state.tokenFamily = null;
12326
+ state.accessTokenExpiry = Date.now() + ttl * 1e3;
12327
+ state.refreshTokenExpiry = null;
12328
+ state.isAuthenticated = true;
12329
+ if (config.autoRefresh && onRefreshScheduled) {
12330
+ onRefreshScheduled();
12331
+ }
12332
+ return true;
12333
+ } catch {
12334
+ if (is4xx || attempt === maxRetries) {
12335
+ state.isAuthenticated = false;
12336
+ return false;
12337
+ }
12338
+ }
12339
+ }
12340
+ state.isAuthenticated = false;
12341
+ return false;
12423
12342
  }
12424
- function renderBanner(banner, handlers) {
12425
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n;
12426
- const placement = banner.placement || {};
12427
- const mode = placement.mode || "overlay";
12428
- const position = placement.position || "top";
12429
- const zIndex = String(placement.zIndex || 2147483e3);
12430
- const imageBanner = isImageBanner(banner);
12431
- const bg = imageBanner ? "transparent" : safeColor((_a = banner.style) == null ? void 0 : _a.backgroundColor) || "#1f2024";
12432
- const color = safeColor((_b = banner.style) == null ? void 0 : _b.color) || "#ffffff";
12433
- const isCard = mode === "overlay" || mode === "popup";
12434
- const ctaPosition = getCtaStyleValue(banner, ["position"]) || "center";
12435
- const imageHorizontal = imageAlign((_c = banner.image) == null ? void 0 : _c.horizontalAlignment);
12436
- const imageVertical = imageAlign((_d = banner.image) == null ? void 0 : _d.verticalAlignment);
12437
- const imageHorizontalPosition = ((_e = banner.image) == null ? void 0 : _e.horizontalAlignment) || "center";
12438
- const imageVerticalPosition = ((_f = banner.image) == null ? void 0 : _f.verticalAlignment) || "center";
12439
- const closeColor = imageBanner ? "#ffffff" : color;
12440
- const root = el("div", {
12441
- boxSizing: "border-box",
12442
- fontFamily: "Roboto, sans-serif",
12443
- fontSize: "14px",
12444
- lineHeight: "1.35",
12445
- background: bg,
12446
- color,
12447
- borderRadius: isCard && !imageBanner ? "6px" : "0",
12448
- boxShadow: isCard && !imageBanner ? "0 8px 24px rgba(0,0,0,0.22)" : "none",
12449
- padding: imageBanner ? "0" : isCard ? "12px 42px 12px 24px" : "8px 42px 8px 16px",
12450
- display: "flex",
12451
- alignItems: imageBanner ? imageVertical : "flex-start",
12452
- justifyContent: "center",
12453
- gap: imageBanner ? "0" : "16px"
12454
- });
12455
- root.setAttribute("role", isCard ? "dialog" : "region");
12456
- root.setAttribute("aria-label", "community banner");
12457
- root.setAttribute("data-renderer-version", "sdk-banner-preview-layout");
12458
- if (isCard) {
12459
- Object.assign(root.style, {
12460
- position: "fixed",
12461
- left: "50%",
12462
- width: "fit-content",
12463
- maxWidth: imageBanner ? "70vw" : "min(700px, 80vw)",
12464
- height: "auto",
12465
- minHeight: imageBanner ? "0" : "80px",
12466
- maxHeight: imageBanner ? "70vh" : "calc(100vh - 300px)",
12467
- overflowY: imageBanner ? "visible" : "auto",
12468
- zIndex
12469
- });
12470
- if (imageBanner) {
12471
- const translate = [];
12472
- if (imageHorizontalPosition === "left") {
12473
- root.style.left = "24px";
12474
- } else if (imageHorizontalPosition === "right") {
12475
- root.style.left = "";
12476
- root.style.right = "24px";
12477
- } else {
12478
- root.style.left = "50%";
12479
- translate.push("-50%");
12343
+ async function refreshAccessToken(config, state, onRefreshScheduled, sendToken) {
12344
+ if (state.isRefreshing) {
12345
+ return state.refreshPromise;
12346
+ }
12347
+ if (!state.refreshToken) {
12348
+ return false;
12349
+ }
12350
+ state.isRefreshing = true;
12351
+ state.refreshPromise = (async () => {
12352
+ const reauthenticateAfterRefreshFailure = async () => {
12353
+ clearState(state);
12354
+ clearStorage(config);
12355
+ const authed = await authenticateServerless(config, state, onRefreshScheduled);
12356
+ if (authed && sendToken) sendToken();
12357
+ return authed;
12358
+ };
12359
+ try {
12360
+ const response = await fetch(`${config.apiUrl}/${config.widgetId}/auth/refresh`, {
12361
+ method: "POST",
12362
+ headers: {
12363
+ "Content-Type": "application/json",
12364
+ Authorization: `Bearer ${state.refreshToken}`
12365
+ },
12366
+ body: JSON.stringify({
12367
+ widgetType: config.widgetType,
12368
+ ...buildIdentityProof(config)
12369
+ })
12370
+ });
12371
+ if (!response.ok) {
12372
+ if (response.status === 400 || response.status === 401 || response.status === 403) {
12373
+ return reauthenticateAfterRefreshFailure();
12374
+ }
12375
+ const error = await response.json().catch(() => ({ error: "Token refresh failed" }));
12376
+ throw new Error(error.error || `HTTP ${response.status}`);
12480
12377
  }
12481
- if (imageVerticalPosition === "top") {
12482
- root.style.top = "10px";
12483
- } else if (imageVerticalPosition === "bottom") {
12484
- root.style.bottom = "24px";
12485
- } else {
12486
- root.style.top = "50%";
12487
- translate.push("-50%");
12378
+ const data = await response.json();
12379
+ if (!data.accessToken || !data.refreshToken) {
12380
+ throw new Error("Invalid refresh response");
12488
12381
  }
12489
- if (translate.length === 2) root.style.transform = `translate(${translate[0]}, ${translate[1]})`;
12490
- else if (translate.length === 1 && imageHorizontalPosition === "center") root.style.transform = `translateX(${translate[0]})`;
12491
- else if (translate.length === 1) root.style.transform = `translateY(${translate[0]})`;
12492
- else root.style.transform = "";
12493
- } else if (position === "bottom") {
12494
- root.style.bottom = "24px";
12495
- root.style.transform = "translateX(-50%)";
12496
- } else if (position === "center") {
12497
- root.style.top = "50%";
12498
- root.style.transform = "translate(-50%, -50%)";
12499
- } else {
12500
- root.style.top = "10px";
12501
- root.style.transform = "translateX(-50%)";
12382
+ setTokens(config, state, data, onRefreshScheduled);
12383
+ if (sendToken) sendToken();
12384
+ return true;
12385
+ } catch {
12386
+ return reauthenticateAfterRefreshFailure();
12387
+ } finally {
12388
+ state.isRefreshing = false;
12389
+ state.refreshPromise = null;
12502
12390
  }
12503
- } else {
12504
- Object.assign(root.style, { position: "relative", width: "100%", zIndex });
12391
+ })();
12392
+ return state.refreshPromise;
12393
+ }
12394
+ async function getValidToken(config, state, onRefreshScheduled) {
12395
+ if (state.accessToken && !isTokenExpired(state.accessTokenExpiry)) {
12396
+ return state.accessToken;
12505
12397
  }
12506
- const contentWrap = el("div", {
12507
- display: "flex",
12508
- width: imageBanner || isCard ? "auto" : "100%",
12509
- maxWidth: "100%",
12510
- flexDirection: "column",
12511
- gap: imageBanner ? "0" : "12px",
12512
- overflow: "hidden"
12513
- });
12514
- const contentRow = el("div", {
12515
- display: "flex",
12516
- width: imageBanner || isCard ? "auto" : "100%",
12517
- justifyContent: imageBanner ? imageHorizontal : "space-between",
12518
- alignItems: imageBanner ? imageVertical : "flex-start",
12519
- gap: "12px"
12520
- });
12521
- const content = el("div", {
12522
- width: imageBanner || isCard ? "auto" : "100%",
12523
- maxWidth: imageBanner ? "70vw" : isCard ? "calc(min(700px, 80vw) - 66px)" : "100%",
12524
- overflow: "hidden",
12525
- wordBreak: "normal",
12526
- overflowWrap: "anywhere",
12527
- whiteSpace: "normal",
12528
- color
12529
- });
12530
- const imageUrl = ((_g = banner.image) == null ? void 0 : _g.desktopUrl) || ((_h = banner.image) == null ? void 0 : _h.mobileUrl);
12531
- if (imageBanner && imageUrl) {
12532
- const imageLinkUrl = (_i = banner.image) == null ? void 0 : _i.linkUrl;
12533
- const imageOpenInNewTab = (_j = banner.image) == null ? void 0 : _j.openInNewTab;
12534
- const img = el(
12535
- "img",
12536
- {
12537
- display: "block",
12538
- maxWidth: "70vw",
12539
- maxHeight: "70vh",
12540
- width: "auto",
12541
- height: "auto",
12542
- objectFit: "contain",
12543
- objectPosition: imageObjectPosition(
12544
- (_k = banner.image) == null ? void 0 : _k.horizontalAlignment,
12545
- (_l = banner.image) == null ? void 0 : _l.verticalAlignment
12546
- ),
12547
- cursor: imageLinkUrl ? "pointer" : "",
12548
- borderRadius: ((_m = banner.image) == null ? void 0 : _m.cornerRadius) === "rounded" ? "8px" : "0"
12549
- },
12550
- { src: imageUrl, alt: banner.title || "banner" }
12551
- );
12552
- if (imageLinkUrl) {
12553
- img.addEventListener("click", () => {
12554
- if (imageOpenInNewTab) {
12555
- window.open(imageLinkUrl, "_blank", "noopener,noreferrer");
12556
- } else {
12557
- window.location.href = imageLinkUrl;
12558
- }
12398
+ if (config.authUrl) {
12399
+ const authed2 = await authenticateViaProxy(config, state, onRefreshScheduled);
12400
+ if (authed2 && state.accessToken) return state.accessToken;
12401
+ throw new Error("Unable to obtain valid token");
12402
+ }
12403
+ const refreshed = await refreshAccessToken(config, state, onRefreshScheduled);
12404
+ if (refreshed && state.accessToken) return state.accessToken;
12405
+ const authed = await authenticateServerless(config, state, onRefreshScheduled);
12406
+ if (authed && state.accessToken) return state.accessToken;
12407
+ throw new Error("Unable to obtain valid token");
12408
+ }
12409
+ async function logout(config, state) {
12410
+ if (state.accessToken) {
12411
+ try {
12412
+ await fetch(`${config.apiUrl}/${config.widgetId}/auth/logout`, {
12413
+ method: "POST",
12414
+ headers: {
12415
+ "Content-Type": "application/json",
12416
+ Authorization: `Bearer ${state.accessToken}`
12417
+ },
12418
+ body: JSON.stringify({ refreshToken: state.refreshToken })
12559
12419
  });
12560
- }
12561
- content.appendChild(img);
12562
- } else {
12563
- content.innerHTML = sanitizeBannerHtml(banner.text);
12564
- styleRichText(content);
12565
- const textAlign = readTextAlign(content);
12566
- content.style.textAlign = textAlign;
12567
- if (isCard) {
12568
- contentRow.style.justifyContent = justifyFromTextAlign(textAlign);
12420
+ } catch {
12569
12421
  }
12570
12422
  }
12571
- if (!imageBanner && ((_n = banner.cta) == null ? void 0 : _n.enabled)) {
12572
- const ctaBg = safeColor(
12573
- getCtaStyleValue(banner, ["backgroundColor", "buttonColor", "aColor"])
12574
- ) || "#2f6df6";
12575
- const ctaColor = safeColor(getCtaStyleValue(banner, ["textColor", "color"])) || "#ffffff";
12576
- const btn = el("button", {
12577
- cursor: "pointer",
12578
- border: "none",
12579
- borderRadius: "6px",
12580
- height: "36px",
12581
- minHeight: "36px",
12582
- padding: "0 16px",
12583
- fontFamily: "Roboto, sans-serif",
12584
- fontSize: "12px",
12585
- fontWeight: "600",
12586
- lineHeight: "20px",
12587
- backgroundColor: ctaBg,
12588
- color: ctaColor,
12589
- boxSizing: "border-box",
12590
- display: "inline-flex",
12591
- alignItems: "center",
12592
- justifyContent: "center"
12593
- });
12594
- btn.type = "button";
12595
- btn.textContent = banner.cta.label || "CTA Button";
12596
- btn.addEventListener("click", handlers.onCtaClick);
12597
- if (ctaPosition === "inline") {
12598
- contentRow.appendChild(content);
12599
- const inlineWrap = el("div", {
12600
- display: "flex",
12601
- flexDirection: "column",
12602
- alignItems: "flex-end",
12603
- justifyContent: "center",
12604
- flexShrink: "0"
12605
- });
12606
- inlineWrap.appendChild(btn);
12607
- contentRow.appendChild(inlineWrap);
12608
- contentWrap.appendChild(contentRow);
12609
- } else {
12610
- contentRow.appendChild(content);
12611
- contentWrap.appendChild(contentRow);
12612
- const wrap = el("div", {
12613
- display: "flex",
12614
- width: "100%",
12615
- justifyContent: ctaAlign(ctaPosition)
12616
- });
12617
- wrap.appendChild(btn);
12618
- contentWrap.appendChild(wrap);
12423
+ clearState(state);
12424
+ clearStorage(config);
12425
+ }
12426
+ const inflightErrorSettings = /* @__PURE__ */ new Map();
12427
+ function applyErrorSettingsToState(state, data) {
12428
+ if (data.configured) {
12429
+ state.errorSettings = {
12430
+ errorMessage: data.errorMessage,
12431
+ modalColor: data.modalColor,
12432
+ backgroundImage: data.backgroundImage
12433
+ };
12434
+ }
12435
+ if (data.loadingAnimation) {
12436
+ state.loadingAnimation = data.loadingAnimation;
12437
+ }
12438
+ if (data.themePalette) {
12439
+ state.themePalette = data.themePalette;
12440
+ }
12441
+ if (typeof data.font === "string" && data.font) {
12442
+ state.font = data.font;
12443
+ }
12444
+ if (data.defaultColorSchema === "light" || data.defaultColorSchema === "dark") {
12445
+ state.defaultColorSchema = data.defaultColorSchema;
12446
+ }
12447
+ }
12448
+ async function fetchErrorSettings(config, state, opts = {}) {
12449
+ if (!opts.skipCache) {
12450
+ const cached = loadErrorSettingsFromStorage(config);
12451
+ if (cached) {
12452
+ applyErrorSettingsToState(state, cached);
12453
+ return state.errorSettings;
12619
12454
  }
12620
- } else {
12621
- contentRow.appendChild(content);
12622
- contentWrap.appendChild(contentRow);
12623
12455
  }
12624
- root.appendChild(contentWrap);
12625
- const close = el("button", {
12626
- position: isCard ? "absolute" : "",
12627
- top: isCard ? imageBanner ? "16px" : "12px" : "",
12628
- right: isCard ? imageBanner ? "16px" : "12px" : "",
12629
- marginTop: isCard ? "0" : "8px",
12630
- width: "20px",
12631
- minWidth: "20px",
12632
- height: "20px",
12633
- cursor: "pointer",
12634
- border: "none",
12635
- borderRadius: "50%",
12636
- background: "rgba(0,0,0,0.25)",
12637
- color: closeColor,
12638
- fontSize: "16px",
12639
- lineHeight: "1",
12640
- display: "flex",
12641
- alignItems: "center",
12642
- justifyContent: "center",
12643
- padding: "0",
12644
- flexShrink: "0"
12456
+ const url = `${config.apiUrl}/${config.widgetId}/auth/error-settings?widgetType=${config.widgetType}`;
12457
+ let promise = inflightErrorSettings.get(url);
12458
+ if (!promise) {
12459
+ promise = (async () => {
12460
+ try {
12461
+ const response = await fetch(url, {
12462
+ method: "GET",
12463
+ headers: { "Content-Type": "application/json" }
12464
+ });
12465
+ if (!response.ok) return null;
12466
+ const data2 = await response.json();
12467
+ saveErrorSettingsToStorage(config, data2);
12468
+ return data2;
12469
+ } catch {
12470
+ return null;
12471
+ } finally {
12472
+ inflightErrorSettings.delete(url);
12473
+ }
12474
+ })();
12475
+ inflightErrorSettings.set(url, promise);
12476
+ }
12477
+ const data = await promise;
12478
+ if (!data) return null;
12479
+ applyErrorSettingsToState(state, data);
12480
+ return state.errorSettings;
12481
+ }
12482
+ function clearState(state) {
12483
+ state.accessToken = null;
12484
+ state.refreshToken = null;
12485
+ state.tokenFamily = null;
12486
+ state.accessTokenExpiry = null;
12487
+ state.refreshTokenExpiry = null;
12488
+ state.isAuthenticated = false;
12489
+ state.isRefreshing = false;
12490
+ state.refreshPromise = null;
12491
+ state.lastAuthError = null;
12492
+ if (state.refreshTimer) {
12493
+ clearTimeout(state.refreshTimer);
12494
+ state.refreshTimer = null;
12495
+ }
12496
+ if (state.syncTimer) {
12497
+ clearInterval(state.syncTimer);
12498
+ state.syncTimer = null;
12499
+ }
12500
+ }
12501
+ const BUNDLE_READY_TIMEOUT_MS = 3e4;
12502
+ function isPromiseLike(value) {
12503
+ return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.then === "function";
12504
+ }
12505
+ function isReadyHandle(value) {
12506
+ if (typeof value !== "object" || value === null || !("ready" in value)) {
12507
+ return false;
12508
+ }
12509
+ return isPromiseLike(value.ready);
12510
+ }
12511
+ function waitWithTimeout(promise, timeoutMs, timeoutMessage) {
12512
+ return new Promise((resolve, reject) => {
12513
+ const timeoutId = setTimeout(() => {
12514
+ reject(new Error(timeoutMessage));
12515
+ }, timeoutMs);
12516
+ Promise.resolve(promise).then(
12517
+ (value) => {
12518
+ clearTimeout(timeoutId);
12519
+ resolve(value);
12520
+ },
12521
+ (error) => {
12522
+ clearTimeout(timeoutId);
12523
+ reject(error);
12524
+ }
12525
+ );
12526
+ });
12527
+ }
12528
+ async function waitForBundleMountReadiness(mountResult, firstPaint, timeoutMs = BUNDLE_READY_TIMEOUT_MS) {
12529
+ const resolvedMountResult = isPromiseLike(mountResult) ? await waitWithTimeout(
12530
+ mountResult,
12531
+ timeoutMs,
12532
+ `Widget bundle mount did not resolve within ${timeoutMs}ms`
12533
+ ) : mountResult;
12534
+ if (!isReadyHandle(resolvedMountResult)) {
12535
+ await firstPaint;
12536
+ return "first-paint";
12537
+ }
12538
+ await waitWithTimeout(
12539
+ resolvedMountResult.ready,
12540
+ timeoutMs,
12541
+ `Widget bundle did not report deep readiness within ${timeoutMs}ms`
12542
+ );
12543
+ return "deep-ready";
12544
+ }
12545
+ function isWidgetBundleRuntime(value) {
12546
+ return (typeof value === "object" || typeof value === "function") && value !== null && typeof value.mount === "function";
12547
+ }
12548
+ function normalizeBundleUrl(url, baseUrl) {
12549
+ const normalized = new URL(url, baseUrl);
12550
+ normalized.hash = "";
12551
+ return normalized.href;
12552
+ }
12553
+ function createBundleRuntimeLoader({
12554
+ loadScript,
12555
+ readGlobal,
12556
+ baseUrl
12557
+ }) {
12558
+ const runtimePromises = /* @__PURE__ */ new Map();
12559
+ const globalLoadTails = /* @__PURE__ */ new Map();
12560
+ return async function getBundleRuntime(globalName, bundleUrl) {
12561
+ const normalizedUrl = normalizeBundleUrl(bundleUrl, baseUrl());
12562
+ const cacheKey = `${globalName}:${normalizedUrl}`;
12563
+ const cachedRuntime = runtimePromises.get(cacheKey);
12564
+ if (cachedRuntime) return cachedRuntime;
12565
+ const previousLoad = globalLoadTails.get(globalName) || Promise.resolve();
12566
+ const runtimePromise = previousLoad.then(async () => {
12567
+ await loadScript(normalizedUrl);
12568
+ const runtime = readGlobal(globalName);
12569
+ if (!isWidgetBundleRuntime(runtime)) {
12570
+ throw new Error(
12571
+ `window.${globalName}.mount not found after loading ${normalizedUrl}`
12572
+ );
12573
+ }
12574
+ const mount = runtime.mount;
12575
+ return {
12576
+ mount(container, config) {
12577
+ return mount.call(runtime, container, config);
12578
+ }
12579
+ };
12580
+ });
12581
+ runtimePromises.set(cacheKey, runtimePromise);
12582
+ const loadTail = runtimePromise.then(
12583
+ () => void 0,
12584
+ () => void 0
12585
+ );
12586
+ globalLoadTails.set(globalName, loadTail);
12587
+ void loadTail.then(() => {
12588
+ if (globalLoadTails.get(globalName) === loadTail) {
12589
+ globalLoadTails.delete(globalName);
12590
+ }
12591
+ });
12592
+ try {
12593
+ return await runtimePromise;
12594
+ } catch (error) {
12595
+ if (runtimePromises.get(cacheKey) === runtimePromise) {
12596
+ runtimePromises.delete(cacheKey);
12597
+ }
12598
+ throw error;
12599
+ }
12600
+ };
12601
+ }
12602
+ function appendScript(url) {
12603
+ return new Promise((resolve, reject) => {
12604
+ const script = document.createElement("script");
12605
+ script.src = url;
12606
+ script.onload = () => resolve();
12607
+ script.onerror = () => reject(new Error(`Failed to load ${url}`));
12608
+ document.head.appendChild(script);
12645
12609
  });
12646
- close.type = "button";
12647
- close.setAttribute("aria-label", "dismiss banner");
12648
- close.innerHTML = "&times;";
12649
- close.addEventListener("click", handlers.onDismiss);
12650
- root.appendChild(close);
12651
- return root;
12652
12610
  }
12653
- const VISITOR_KEY = "rai-banner-visitor";
12654
- function uuid() {
12655
- try {
12656
- if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
12657
- return crypto.randomUUID();
12611
+ const loadBrowserBundleRuntime = createBundleRuntimeLoader({
12612
+ loadScript: appendScript,
12613
+ readGlobal: (globalName) => window[globalName],
12614
+ baseUrl: () => document.baseURI
12615
+ });
12616
+ function getOrLoadBundleRuntime(globalName, bundleUrl) {
12617
+ return loadBrowserBundleRuntime(globalName, bundleUrl);
12618
+ }
12619
+ function assertCallbackName(callbackName) {
12620
+ if (!callbackName || typeof callbackName !== "string") {
12621
+ throw new TypeError("ReturningAI callback name must be a non-empty string");
12622
+ }
12623
+ }
12624
+ function assertCallback(callback) {
12625
+ if (typeof callback !== "function") {
12626
+ throw new TypeError("ReturningAI callback must be a function");
12627
+ }
12628
+ }
12629
+ class WidgetCallbackRegistry {
12630
+ constructor() {
12631
+ __publicField(this, "registeredCallbacks", {});
12632
+ __publicField(this, "locked", false);
12633
+ }
12634
+ get isLocked() {
12635
+ return this.locked;
12636
+ }
12637
+ registerCallback(callbackName, callback) {
12638
+ if (this.locked) {
12639
+ return;
12658
12640
  }
12659
- } catch {
12641
+ assertCallbackName(callbackName);
12642
+ assertCallback(callback);
12643
+ this.registeredCallbacks = {
12644
+ ...this.registeredCallbacks,
12645
+ [callbackName]: callback
12646
+ };
12647
+ }
12648
+ lock() {
12649
+ if (this.locked) {
12650
+ return;
12651
+ }
12652
+ this.locked = true;
12653
+ }
12654
+ read() {
12655
+ return { ...this.registeredCallbacks };
12660
12656
  }
12661
- return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
12662
- const r = Math.random() * 16 | 0;
12663
- const v = c === "x" ? r : r & 3 | 8;
12664
- return v.toString(16);
12665
- });
12666
12657
  }
12667
- function getOrCreateVisitorId(communityId, storagePrefix) {
12668
- const key = `${storagePrefix}-${VISITOR_KEY}-${communityId}`;
12658
+ const STORE_CALLBACK_FIELD_OPTIONS_SCOPE = "store.callback_field_options";
12659
+ const STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME = "callbackFieldOptions";
12660
+ const STORE_CALLBACK_FIELD_OPTIONS_TIMEOUT_MS = 15e3;
12661
+ const STORE_PURCHASE_SUCCESS_SCOPE = "store.purchase_success";
12662
+ const STORE_PURCHASE_SUCCESS_CALLBACK_NAME = "storePurchaseSuccess";
12663
+ function isSupportedStoreHostCallback(callbackName, payload) {
12664
+ if (!payload) return false;
12665
+ return callbackName === STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME && payload.callbackName === STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME && payload.scope === STORE_CALLBACK_FIELD_OPTIONS_SCOPE || callbackName === STORE_PURCHASE_SUCCESS_CALLBACK_NAME && payload.callbackName === STORE_PURCHASE_SUCCESS_CALLBACK_NAME && payload.scope === STORE_PURCHASE_SUCCESS_SCOPE;
12666
+ }
12667
+ async function invokeStoreHostCallback(callbacks, callbackName, payload) {
12668
+ if (!isSupportedStoreHostCallback(callbackName, payload)) {
12669
+ throw new Error("Host callback is only supported for store widgets");
12670
+ }
12671
+ const callback = callbacks[callbackName];
12672
+ if (!callback) throw new Error("Host callback is not registered");
12673
+ let value;
12669
12674
  try {
12670
- let id = localStorage.getItem(key);
12671
- if (!id) {
12672
- id = uuid();
12673
- localStorage.setItem(key, id);
12674
- }
12675
- return id;
12675
+ value = await callback(payload);
12676
12676
  } catch {
12677
- return uuid();
12677
+ throw new Error("Host callback failed");
12678
12678
  }
12679
+ return callbackName === STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME ? value : void 0;
12679
12680
  }
12680
- function fireTracking(url) {
12681
- if (!url) return;
12681
+ function debounce(fn, wait) {
12682
+ let timer = null;
12683
+ return {
12684
+ call(h) {
12685
+ if (timer) clearTimeout(timer);
12686
+ timer = setTimeout(() => {
12687
+ fn(h);
12688
+ timer = null;
12689
+ }, wait);
12690
+ },
12691
+ cancel() {
12692
+ if (timer) {
12693
+ clearTimeout(timer);
12694
+ timer = null;
12695
+ }
12696
+ }
12697
+ };
12698
+ }
12699
+ function sendTokenToWidget(config, state, iframe) {
12700
+ var _a;
12701
+ if (!state.accessToken) return;
12682
12702
  try {
12683
- const img = new Image();
12684
- img.src = url;
12703
+ (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
12704
+ {
12705
+ type: "RETURNINGAI_WIDGET_TOKEN",
12706
+ value: {
12707
+ widgetId: config.widgetId,
12708
+ token: state.accessToken,
12709
+ ...config.customData !== void 0 && { customData: config.customData }
12710
+ }
12711
+ },
12712
+ config.widgetDomain
12713
+ );
12685
12714
  } catch {
12686
12715
  }
12687
12716
  }
12717
+ function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled, onLogout, hideLoader, emit, getCallbacks) {
12718
+ const heightSetter = debounce((h) => {
12719
+ iframe.style.height = `${h}px`;
12720
+ emit == null ? void 0 : emit("rai-height-change", { height: h });
12721
+ }, config.heightDebounce ?? 100);
12722
+ const handler = async (event) => {
12723
+ var _a;
12724
+ if (event.origin !== config.widgetDomain) return;
12725
+ if (!event.data || typeof event.data.type !== "string") return;
12726
+ const { type, containerId, widgetId, payload } = event.data;
12727
+ switch (type) {
12728
+ case "RETURNINGAI_WIDGET_REQUEST_TOKEN": {
12729
+ try {
12730
+ const token = await getValidToken(config, state, onRefreshScheduled);
12731
+ (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
12732
+ {
12733
+ type: "RETURNINGAI_WIDGET_TOKEN",
12734
+ value: {
12735
+ token,
12736
+ widgetId: config.widgetId,
12737
+ ...config.customData !== void 0 && { customData: config.customData }
12738
+ }
12739
+ },
12740
+ config.widgetDomain
12741
+ );
12742
+ } catch {
12743
+ }
12744
+ break;
12745
+ }
12746
+ case "WIDGET_HEIGHT_UPDATE": {
12747
+ const h = Number(payload == null ? void 0 : payload.height);
12748
+ if (Number.isFinite(h) && h > 0) {
12749
+ heightSetter.call(h);
12750
+ }
12751
+ break;
12752
+ }
12753
+ case "WIDGET_READY": {
12754
+ if (widgetId !== void 0 && widgetId !== config.widgetId) break;
12755
+ if (containerId !== void 0 && containerId !== config.container) break;
12756
+ iframe.classList.add("loaded");
12757
+ if (hideLoader) hideLoader();
12758
+ emit == null ? void 0 : emit("rai-ready");
12759
+ break;
12760
+ }
12761
+ case "WIDGET_ERROR": {
12762
+ if (hideLoader) hideLoader();
12763
+ break;
12764
+ }
12765
+ case "WIDGET_LOGOUT": {
12766
+ if (onLogout) await onLogout();
12767
+ break;
12768
+ }
12769
+ case "RETURNINGAI_WIDGET_ACTION": {
12770
+ if (event.source !== iframe.contentWindow) break;
12771
+ const message = event.data;
12772
+ if (config.widgetType !== "store" && config.widgetType !== "custom") break;
12773
+ if (message.widgetType !== "store") break;
12774
+ if (message.action !== "store:host-callback") break;
12775
+ if (!isSupportedStoreHostCallback(message.callbackName, message.payload)) break;
12776
+ if (message.widgetId !== void 0 && message.widgetId !== config.widgetId) break;
12777
+ if (message.containerId !== void 0 && message.containerId !== config.container) break;
12778
+ const respond = (result) => {
12779
+ var _a2;
12780
+ (_a2 = iframe.contentWindow) == null ? void 0 : _a2.postMessage(result, config.widgetDomain);
12781
+ };
12782
+ const callbacks = (getCallbacks == null ? void 0 : getCallbacks()) ?? {};
12783
+ if (!callbacks[message.callbackName]) {
12784
+ respond({
12785
+ type: "RETURNINGAI_WIDGET_ACTION_RESULT",
12786
+ requestId: message.requestId,
12787
+ action: "store:host-callback",
12788
+ ok: false,
12789
+ status: "failed",
12790
+ error: { message: "Host callback is not registered" }
12791
+ });
12792
+ break;
12793
+ }
12794
+ try {
12795
+ const value = await invokeStoreHostCallback(
12796
+ callbacks,
12797
+ message.callbackName,
12798
+ message.payload
12799
+ );
12800
+ const result = {
12801
+ type: "RETURNINGAI_WIDGET_ACTION_RESULT",
12802
+ requestId: message.requestId,
12803
+ action: "store:host-callback",
12804
+ ok: true,
12805
+ status: "success",
12806
+ ...value !== void 0 ? { value } : {}
12807
+ };
12808
+ respond(result);
12809
+ } catch {
12810
+ respond({
12811
+ type: "RETURNINGAI_WIDGET_ACTION_RESULT",
12812
+ requestId: message.requestId,
12813
+ action: "store:host-callback",
12814
+ ok: false,
12815
+ status: "failed",
12816
+ error: { message: "Host callback failed" }
12817
+ });
12818
+ }
12819
+ break;
12820
+ }
12821
+ }
12822
+ };
12823
+ window.addEventListener("message", handler);
12824
+ return () => {
12825
+ window.removeEventListener("message", handler);
12826
+ heightSetter.cancel();
12827
+ };
12828
+ }
12688
12829
  const widgetCSS = ':host{display:block;position:relative;width:100%;height:100%}.rai-loader{position:absolute;top:0;left:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.75rem;width:100%;height:100%;background:var(--background-5, var(--rai-loader-bg, #ffffff));border-radius:8px;z-index:10;transition:opacity .3s ease-out;font-family:var(--rai-loader-font, "Roboto", system-ui, -apple-system, sans-serif)}.rai-loader.fade-out{opacity:0;pointer-events:none}.rai-loader-text{margin:.5rem 0 0;font-size:1.125rem;line-height:1.75rem;font-weight:400;letter-spacing:normal;color:var(--accent-1, var(--rai-accent, #000000));text-align:center}@media (min-width: 768px){.rai-loader-text{font-size:1.5rem;line-height:2rem}}.rai-error{display:none;position:absolute;top:0;left:0;width:100%;height:100%;align-items:center;justify-content:center;flex-direction:column;gap:12px;background:var(--rai-error-bg, #1a1a1a);border-radius:8px;padding:24px;box-sizing:border-box;text-align:center;color:var(--rai-error-text, #9ca3af);font-family:system-ui,-apple-system,sans-serif;z-index:10}.rai-error.visible{display:flex}iframe{display:block;border:none;opacity:0;transition:opacity .3s ease-in}iframe.loaded{opacity:1}.rai-lottie-loader{width:100px;height:100px}.rai-lottie-loader svg{width:100%!important;height:100%!important}.rai-lottie-spinner{width:32px;height:32px;margin:34px auto;border:3px solid var(--rai-text4, #6b7280);border-top-color:var(--rai-accent, #000000);border-radius:50%;animation:rai-spin .7s linear infinite}@keyframes rai-spin{to{transform:rotate(360deg)}}.rai-sdk-loader{position:relative;width:75px;height:100px}.rai-sdk-loader__bar{position:absolute;bottom:0;width:10px;height:50%;background:var(--rai-accent, #000000);transform-origin:center bottom;box-shadow:1px 1px #0003}.rai-sdk-loader__bar:nth-child(1){left:0;transform:scaleY(.2);animation:rai-sdk-barUp1 4s infinite}.rai-sdk-loader__bar:nth-child(2){left:15px;transform:scaleY(.4);animation:rai-sdk-barUp2 4s infinite}.rai-sdk-loader__bar:nth-child(3){left:30px;transform:scaleY(.6);animation:rai-sdk-barUp3 4s infinite}.rai-sdk-loader__bar:nth-child(4){left:45px;transform:scaleY(.8);animation:rai-sdk-barUp4 4s infinite}.rai-sdk-loader__bar:nth-child(5){left:60px;transform:scale(1);animation:rai-sdk-barUp5 4s infinite}.rai-sdk-loader__ball{position:absolute;bottom:10px;left:0;width:10px;height:10px;background:var(--rai-accent, #000000);border-radius:50%;animation:rai-sdk-ball 4s infinite}@keyframes rai-sdk-ball{0%{transform:translate(0)}5%{transform:translate(8px,-14px)}10%{transform:translate(15px,-10px)}17%{transform:translate(23px,-24px)}20%{transform:translate(30px,-20px)}27%{transform:translate(38px,-34px)}30%{transform:translate(45px,-30px)}37%{transform:translate(53px,-44px)}40%{transform:translate(60px,-40px)}50%{transform:translate(60px)}57%{transform:translate(53px,-14px)}60%{transform:translate(45px,-10px)}67%{transform:translate(37px,-24px)}70%{transform:translate(30px,-20px)}77%{transform:translate(22px,-34px)}80%{transform:translate(15px,-30px)}87%{transform:translate(7px,-44px)}90%{transform:translateY(-40px)}to{transform:translate(0)}}@keyframes rai-sdk-barUp1{0%{transform:scaleY(.2)}40%{transform:scaleY(.2)}50%{transform:scale(1)}90%{transform:scale(1)}to{transform:scaleY(.2)}}@keyframes rai-sdk-barUp2{0%{transform:scaleY(.4)}40%{transform:scaleY(.4)}50%{transform:scaleY(.8)}90%{transform:scaleY(.8)}to{transform:scaleY(.4)}}@keyframes rai-sdk-barUp3{0%{transform:scaleY(.6)}to{transform:scaleY(.6)}}@keyframes rai-sdk-barUp4{0%{transform:scaleY(.8)}40%{transform:scaleY(.8)}50%{transform:scaleY(.4)}90%{transform:scaleY(.4)}to{transform:scaleY(.8)}}@keyframes rai-sdk-barUp5{0%{transform:scale(1)}40%{transform:scale(1)}50%{transform:scaleY(.2)}90%{transform:scaleY(.2)}to{transform:scale(1)}}#loading-square{width:75px;aspect-ratio:1;display:flex;color:var(--rai-accent, #000000);background:linear-gradient(currentColor 0 0) right / 51% 100%,linear-gradient(currentColor 0 0) bottom / 100% 51%;background-repeat:no-repeat;animation:l16-0 2s infinite linear .25s}#loading-square>div{width:50%;height:50%;background:currentColor;animation:l16-1 .5s infinite linear}@keyframes l16-0{0%,12.49%{transform:rotate(0)}12.5%,37.49%{transform:rotate(90deg)}37.5%,62.49%{transform:rotate(180deg)}62.5%,87.49%{transform:rotate(270deg)}87.5%,to{transform:rotate(360deg)}}@keyframes l16-1{0%{transform:perspective(80px) rotate3d(-1,-1,0,0)}80%,to{transform:perspective(80px) rotate3d(-1,-1,0,-180deg)}}#loading-circle{width:75px;aspect-ratio:1;display:grid;grid:50%/50%;color:var(--rai-accent, #000000);border-radius:50%;--_g: no-repeat linear-gradient(currentColor 0 0);background:var(--_g),var(--_g),var(--_g);background-size:50.1% 50.1%;animation:l9-0 1.5s infinite steps(1) alternate,l9-0-0 3s infinite steps(1) alternate}#loading-circle>div{background:var(--rai-text4, #6b7280);border-top-left-radius:100px;transform:perspective(150px) rotateY(0) rotateX(0);transform-origin:bottom right;animation:l9-1 1.5s infinite linear alternate}@keyframes l9-0{0%{background-position:0 100%,100% 100%,100% 0}33%{background-position:100% 100%,100% 100%,100% 0}66%{background-position:100% 0,100% 0,100% 0}}@keyframes l9-0-0{0%{transform:scaleX(1) rotate(0)}50%{transform:scaleX(-1) rotate(-90deg)}}@keyframes l9-1{16.5%{transform:perspective(150px) rotateX(-90deg) rotateY(0) rotateX(0);filter:grayscale(.8)}33%{transform:perspective(150px) rotateX(-180deg) rotateY(0) rotateX(0)}66%{transform:perspective(150px) rotateX(-180deg) rotateY(-180deg) rotateX(0)}to{transform:perspective(150px) rotateX(-180deg) rotateY(-180deg) rotateX(-180deg);filter:grayscale(.8)}}.rai-custom-error{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.tiptap strong.tw-font-bold,.tiptap b.tw-font-bold{font-weight:700}.tiptap em.tw-font-italic,.tiptap i.tw-font-italic{font-style:italic}.tiptap a.tiptap-rich-text-link{color:#3b82f6;text-decoration:underline;position:relative;display:inline-block;width:fit-content}.tiptap a.tiptap-rich-text-link:hover{color:#3b82f6cc;text-decoration:none}.tiptap a.tiptap-rich-text-link:not([title=""]):before{content:attr(title);position:absolute;top:-2rem;left:50%;transform:translate(-50%);white-space:nowrap;border-radius:.25rem;background-color:#000;padding:.25rem .5rem;color:#fff;opacity:0;transition:opacity .2s;pointer-events:none}.tiptap a.tiptap-rich-text-link:not([title=""]):hover:before{opacity:1}.tiptap p[style*="text-align: left"]{text-align:left}.tiptap p[style*="text-align: center"]{text-align:center}.tiptap p[style*="text-align: right"]{text-align:right}.tiptap p[style*="text-align: justify"]{text-align:justify}.tiptap u{text-decoration:underline}.tiptap.ProseMirror{background-color:transparent;height:fit-content!important;word-wrap:break-word;white-space:pre-wrap;white-space:break-spaces;-webkit-font-variant-ligatures:none;font-variant-ligatures:none;font-feature-settings:"liga" 0;padding:0;width:100%;overflow-y:auto}.tiptap{line-height:1.6;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.tiptap p{margin:.5em 0}.tiptap p:first-child{margin-top:0}.tiptap p:last-child{margin-bottom:0}.tiptap img{max-width:100%;height:auto;display:block}.tiptap p[style*="text-align: left"] img{margin-left:0;margin-right:auto}.tiptap p[style*="text-align: center"] img{margin-left:auto;margin-right:auto}.tiptap p[style*="text-align: right"] img{margin-left:auto;margin-right:0}.rai-retry-btn{padding:8px 20px;border:none;border-radius:6px;background:var(--rai-accent, #000000);color:var(--rai-error-bg, #ffffff);font-size:14px;font-family:system-ui,-apple-system,sans-serif;cursor:pointer}.rai-retry-btn:hover{opacity:.85}';
12689
12830
  const DEFINED_ATTRS = /* @__PURE__ */ new Set([
12690
12831
  "community-id",
@@ -12793,15 +12934,6 @@ function resolveWidgetServerUrl(domainKey) {
12793
12934
  function resolveV2ApiUrl(domainKey) {
12794
12935
  return DOMAIN_KEY_TO_V2_API_URL[domainKey] || DOMAIN_KEY_TO_V2_API_URL.PROD;
12795
12936
  }
12796
- function loadScript(url) {
12797
- return new Promise((resolve, reject) => {
12798
- const s = document.createElement("script");
12799
- s.src = url;
12800
- s.onload = () => resolve();
12801
- s.onerror = () => reject(new Error(`Failed to load ${url}`));
12802
- document.head.appendChild(s);
12803
- });
12804
- }
12805
12937
  const loadedFonts = /* @__PURE__ */ new Set();
12806
12938
  function ensureGoogleFontLoaded(family) {
12807
12939
  if (!family || loadedFonts.has(family)) return;
@@ -12817,9 +12949,10 @@ function ensureGoogleFontLoaded(family) {
12817
12949
  const DANGEROUS_ELEMENTS = ["script", "iframe", "object", "embed"];
12818
12950
  const URL_ATTRS = ["src", "href", "xlink:href"];
12819
12951
  function sanitizeLoaderHtml(html2) {
12820
- const doc = new DOMParser().parseFromString(html2, "text/html");
12821
- doc.body.querySelectorAll(DANGEROUS_ELEMENTS.join(",")).forEach((el2) => el2.remove());
12822
- doc.body.querySelectorAll("*").forEach((el2) => {
12952
+ const template = document.createElement("template");
12953
+ template.innerHTML = html2;
12954
+ template.content.querySelectorAll(DANGEROUS_ELEMENTS.join(",")).forEach((el2) => el2.remove());
12955
+ template.content.querySelectorAll("*").forEach((el2) => {
12823
12956
  Array.from(el2.attributes).forEach((attr) => {
12824
12957
  const name = attr.name.toLowerCase();
12825
12958
  if (name.startsWith("on")) {
@@ -12832,7 +12965,9 @@ function sanitizeLoaderHtml(html2) {
12832
12965
  }
12833
12966
  });
12834
12967
  });
12835
- return doc.body.innerHTML;
12968
+ const container = document.createElement("div");
12969
+ container.appendChild(template.content.cloneNode(true));
12970
+ return container.innerHTML;
12836
12971
  }
12837
12972
  function readConfig(el2, existingId) {
12838
12973
  const get = (name) => el2.getAttribute(name) ?? el2.getAttribute(`data-${name}`) ?? "";
@@ -13177,15 +13312,13 @@ class BaseWidget extends HTMLElement {
13177
13312
  }
13178
13313
  /**
13179
13314
  * Loads the community loading animation + theme palette from cache or API
13180
- * and renders the animation above the "Loading ..." text. The text is
13181
- * already painted by renderShell so this method only ADDS the animation;
13182
- * users see two states at most: text-only → text + animation.
13315
+ * and renders the configured animation above the loading text.
13183
13316
  *
13184
13317
  * Stale-while-revalidate:
13185
13318
  * - Cache hit → apply palette & render animation synchronously, fire
13186
13319
  * background refresh.
13187
- * - Cache miss → text remains visible; await network; apply palette
13188
- * + render animation when response arrives.
13320
+ * - Cache miss → retain the initial shell; await network; apply palette +
13321
+ * render the resolved animation or fallback.
13189
13322
  */
13190
13323
  async fetchAndRenderLoader() {
13191
13324
  var _a;
@@ -13299,48 +13432,35 @@ class BaseWidget extends HTMLElement {
13299
13432
  }
13300
13433
  hideLoader() {
13301
13434
  if (!this.loaderEl) return;
13302
- this.loaderEl.classList.add("fade-out");
13303
- setTimeout(() => {
13304
- var _a;
13305
- return (_a = this.loaderEl) == null ? void 0 : _a.remove();
13306
- }, 300);
13435
+ const loader = this.loaderEl;
13436
+ loader.classList.add("fade-out");
13437
+ setTimeout(() => loader.remove(), 300);
13307
13438
  this.loaderEl = null;
13308
13439
  }
13309
13440
  /**
13310
- * Bundle mode: keep the SDK loader visible until React has actually
13311
- * painted into `container`, eliminating the brief bg-color gap that
13312
- * happens when the loader is removed before the React app's first frame.
13313
- *
13314
- * Strategy:
13315
- * 1. MutationObserver fires when React commits any DOM mutation into
13316
- * the container — synchronous inside mount(), but the callback runs
13317
- * as a microtask, so it observes the first child addition.
13318
- * 2. requestAnimationFrame waits one paint cycle so the React content
13319
- * is actually visible before the SDK loader fades out.
13320
- * 3. 2s fallback covers silent React mount errors so the loader doesn't
13321
- * hang forever.
13322
- *
13323
- * Calls hideLoader() (with its 300ms fade-out) so the transition is smooth.
13441
+ * Resolve after a legacy bundle's first rendered child has reached a paint.
13442
+ * This remains the fallback for bundles that do not expose a deep-ready
13443
+ * handle from mount(). It intentionally does not hide the loader itself so
13444
+ * a stronger nested-MFE readiness signal can take ownership of the handoff.
13324
13445
  */
13325
- deferLoaderRemovalUntilReactPaints(container) {
13326
- if (!this.loaderEl) return;
13327
- let removed = false;
13328
- const remove = () => {
13329
- if (removed) return;
13330
- removed = true;
13331
- this.hideLoader();
13332
- };
13333
- const observer = new MutationObserver(() => {
13334
- if (container.children.length > 0) {
13446
+ waitForFirstBundlePaint(container) {
13447
+ return new Promise((resolve) => {
13448
+ let settled = false;
13449
+ let timeoutId;
13450
+ const observer = new MutationObserver(() => {
13451
+ if (container.children.length > 0) finish();
13452
+ });
13453
+ const finish = () => {
13454
+ if (settled) return;
13455
+ settled = true;
13335
13456
  observer.disconnect();
13336
- requestAnimationFrame(remove);
13337
- }
13457
+ clearTimeout(timeoutId);
13458
+ requestAnimationFrame(() => resolve());
13459
+ };
13460
+ observer.observe(container, { childList: true });
13461
+ timeoutId = setTimeout(finish, 2e3);
13462
+ if (container.children.length > 0) finish();
13338
13463
  });
13339
- observer.observe(container, { childList: true });
13340
- setTimeout(() => {
13341
- observer.disconnect();
13342
- remove();
13343
- }, 2e3);
13344
13464
  }
13345
13465
  showError() {
13346
13466
  this.hideLoader();
@@ -13418,7 +13538,8 @@ class BaseWidget extends HTMLElement {
13418
13538
  }
13419
13539
  removeBanner() {
13420
13540
  var _a;
13421
- if ((_a = this.bannerNode) == null ? void 0 : _a.parentNode) this.bannerNode.parentNode.removeChild(this.bannerNode);
13541
+ if ((_a = this.bannerNode) == null ? void 0 : _a.parentNode)
13542
+ this.bannerNode.parentNode.removeChild(this.bannerNode);
13422
13543
  this.bannerNode = null;
13423
13544
  }
13424
13545
  /**
@@ -13431,7 +13552,8 @@ class BaseWidget extends HTMLElement {
13431
13552
  var _a, _b;
13432
13553
  try {
13433
13554
  if (this.bannerNode) return;
13434
- if (this.getAttribute("banner") === "off" || this.hasAttribute("no-banner")) return;
13555
+ if (this.getAttribute("banner") === "off" || this.hasAttribute("no-banner"))
13556
+ return;
13435
13557
  const apiUrl = this.resolveBannerApiUrl();
13436
13558
  const isCustom = this.config.widgetType === "custom";
13437
13559
  let communityId;
@@ -13445,13 +13567,17 @@ class BaseWidget extends HTMLElement {
13445
13567
  decoded = "";
13446
13568
  }
13447
13569
  if (isObjectId(decoded)) widgetId = decoded;
13448
- else if (isObjectId(this.config.communityId)) widgetId = this.config.communityId;
13570
+ else if (isObjectId(this.config.communityId))
13571
+ widgetId = this.config.communityId;
13449
13572
  if (!widgetId) return;
13450
13573
  } else {
13451
13574
  communityId = this.config.communityId;
13452
13575
  }
13453
13576
  const sessionKey = this.config.communityId || widgetId || "rai";
13454
- const visitorId = getOrCreateVisitorId(sessionKey, this.config.storagePrefix);
13577
+ const visitorId = getOrCreateVisitorId(
13578
+ sessionKey,
13579
+ this.config.storagePrefix
13580
+ );
13455
13581
  const w = window.innerWidth;
13456
13582
  const device = w < 640 ? "mobile" : w < 1024 ? "tablet" : "desktop";
13457
13583
  const result = await fetchEligibleBanner(apiUrl, {
@@ -13467,13 +13593,18 @@ class BaseWidget extends HTMLElement {
13467
13593
  hostname: window.location.hostname,
13468
13594
  path: window.location.pathname
13469
13595
  },
13470
- viewport: { width: window.innerWidth, height: window.innerHeight, device },
13596
+ viewport: {
13597
+ width: window.innerWidth,
13598
+ height: window.innerHeight,
13599
+ device
13600
+ },
13471
13601
  identifier: { type: "anonymous_session", value: visitorId }
13472
13602
  });
13473
13603
  if (!this.isConnected) return;
13474
13604
  const banner = result.banner;
13475
13605
  if (!banner) return;
13476
- if (((_a = banner.placement) == null ? void 0 : _a.mobileBehavior) === "hide" && device === "mobile") return;
13606
+ if (((_a = banner.placement) == null ? void 0 : _a.mobileBehavior) === "hide" && device === "mobile")
13607
+ return;
13477
13608
  const node = renderBanner(banner, {
13478
13609
  onCtaClick: () => {
13479
13610
  var _a2, _b2, _c;
@@ -13481,7 +13612,8 @@ class BaseWidget extends HTMLElement {
13481
13612
  this.emit("rai-banner-click", { bannerId: banner.id });
13482
13613
  const url = (_b2 = banner.cta) == null ? void 0 : _b2.url;
13483
13614
  if (!url) return;
13484
- if ((_c = banner.cta) == null ? void 0 : _c.openInNewTab) window.open(url, "_blank", "noopener,noreferrer");
13615
+ if ((_c = banner.cta) == null ? void 0 : _c.openInNewTab)
13616
+ window.open(url, "_blank", "noopener,noreferrer");
13485
13617
  else window.location.href = url;
13486
13618
  },
13487
13619
  onDismiss: () => {
@@ -13527,7 +13659,7 @@ class BaseWidget extends HTMLElement {
13527
13659
  this.addEventListener("rai-ready", mountBannerOnce);
13528
13660
  bannerGateTimer = window.setTimeout(mountBannerOnce, 2500);
13529
13661
  if (this.config.bundleUrl) {
13530
- this.mountWidget();
13662
+ void this.mountWidget(initId);
13531
13663
  } else {
13532
13664
  this.createIframe();
13533
13665
  }
@@ -13650,8 +13782,8 @@ class BaseWidget extends HTMLElement {
13650
13782
  );
13651
13783
  }
13652
13784
  // ── Bundle mode (no-iframe) ──────────────────────────────────────────
13653
- async mountWidget() {
13654
- var _a;
13785
+ async mountWidget(initId) {
13786
+ let container = null;
13655
13787
  try {
13656
13788
  this.teardownMountedWidget();
13657
13789
  const bundleApiUrl = this.config.v2ApiUrl || resolveV2ApiUrl(this.config.domainKey);
@@ -13674,16 +13806,13 @@ class BaseWidget extends HTMLElement {
13674
13806
  window.loadingAnimation = this.state.loadingAnimation;
13675
13807
  }
13676
13808
  const globalName = WIDGET_GLOBALS[this.config.widgetType];
13677
- if (!((_a = window[globalName]) == null ? void 0 : _a.mount)) {
13678
- await loadScript(this.config.bundleUrl);
13679
- }
13680
- const widgetGlobal = window[globalName];
13681
- if (!(widgetGlobal == null ? void 0 : widgetGlobal.mount)) {
13682
- throw new Error(
13683
- `window.${globalName}.mount not found after loading bundle`
13684
- );
13685
- }
13686
- const container = document.createElement("div");
13809
+ const widgetGlobal = await getOrLoadBundleRuntime(
13810
+ globalName,
13811
+ this.config.bundleUrl
13812
+ );
13813
+ if (!this.isCurrentInit(initId)) return;
13814
+ container = document.createElement("div");
13815
+ const hideNestedBundleUntilReady = this.config.widgetType === "custom";
13687
13816
  container.className = "rai-widget-root";
13688
13817
  container.setAttribute("data-rai-widget-root", "true");
13689
13818
  container.style.cssText = [
@@ -13691,16 +13820,21 @@ class BaseWidget extends HTMLElement {
13691
13820
  "width: 100%",
13692
13821
  "height: 100%",
13693
13822
  "color: var(--text-1)",
13694
- "background: var(--background-1)",
13695
- "font-family: var(--rai-loader-font, 'Roboto', system-ui, -apple-system, sans-serif)"
13823
+ "background: transparent",
13824
+ "font-family: var(--rai-loader-font, 'Roboto', system-ui, -apple-system, sans-serif)",
13825
+ // Custom loaders may intentionally use a transparent backdrop. Keep
13826
+ // their nested bundle fallbacks out of the visual tree until deep
13827
+ // readiness so they cannot show through that transparency. Other
13828
+ // widget types preserve their existing first-paint behavior.
13829
+ `visibility: ${hideNestedBundleUntilReady ? "hidden" : "visible"}`
13696
13830
  ].join("; ");
13697
13831
  this.appendChild(container);
13698
13832
  if (!this.shadow.querySelector("slot")) {
13699
13833
  const slot = document.createElement("slot");
13700
13834
  this.shadow.appendChild(slot);
13701
13835
  }
13702
- this.deferLoaderRemovalUntilReactPaints(container);
13703
- widgetGlobal.mount(container, {
13836
+ const firstPaint = this.waitForFirstBundlePaint(container);
13837
+ const mountResult = widgetGlobal.mount(container, {
13704
13838
  widgetType: this.config.widgetType,
13705
13839
  communityId: this.config.communityId,
13706
13840
  channelId: this.config.channelId,
@@ -13721,16 +13855,31 @@ class BaseWidget extends HTMLElement {
13721
13855
  isOpenWidget: !this.state.accessToken,
13722
13856
  defaultColorSchema: this.config.theme === "dark" ? "dark" : "light",
13723
13857
  requestHostCallback: async (callbackName, payload) => {
13724
- if (this.config.widgetType !== "store" && this.config.widgetType !== "custom" || callbackName !== STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME || payload.scope !== STORE_CALLBACK_FIELD_OPTIONS_SCOPE) {
13725
- throw new Error("Host callback is only supported for store widgets");
13726
- }
13727
- const callback = this.readRuntimeCallbacks()[callbackName];
13728
- if (!callback) throw new Error("Host callback is not registered");
13729
- return callback(payload);
13858
+ if (this.config.widgetType !== "store" && this.config.widgetType !== "custom") {
13859
+ throw new Error(
13860
+ "Host callback is only supported for store widgets"
13861
+ );
13862
+ }
13863
+ return invokeStoreHostCallback(
13864
+ this.readRuntimeCallbacks(),
13865
+ callbackName,
13866
+ payload
13867
+ );
13730
13868
  }
13731
13869
  });
13870
+ await waitForBundleMountReadiness(mountResult, firstPaint);
13871
+ if (!this.isCurrentInit(initId) || !container.isConnected) {
13872
+ container.remove();
13873
+ return;
13874
+ }
13875
+ if (hideNestedBundleUntilReady) container.style.visibility = "visible";
13876
+ this.hideLoader();
13732
13877
  this.emit("rai-mounted");
13733
13878
  } catch (err) {
13879
+ if (!this.isCurrentInit(initId)) {
13880
+ container == null ? void 0 : container.remove();
13881
+ return;
13882
+ }
13734
13883
  console.error("[rai-widget] Bundle mount failed:", err);
13735
13884
  this.showError();
13736
13885
  }
@@ -13917,7 +14066,7 @@ class CustomWidget extends BaseWidget {
13917
14066
  }
13918
14067
  }
13919
14068
  }
13920
- console.log(`[rai-widget] v${"1.6.2"}`);
14069
+ console.log(`[rai-widget] v${"1.7.1"}`);
13921
14070
  class StoreWidgetCompat extends StoreWidget {
13922
14071
  }
13923
14072
  const WIDGET_REGISTRY = [
@@ -13981,7 +14130,7 @@ function exposePublicApi() {
13981
14130
  })();
13982
14131
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
13983
14132
  window.ReturningAIWidget = {
13984
- version: "1.6.2",
14133
+ version: "1.7.1",
13985
14134
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
13986
14135
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
13987
14136
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
@@ -14002,6 +14151,8 @@ export {
14002
14151
  STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME,
14003
14152
  STORE_CALLBACK_FIELD_OPTIONS_SCOPE,
14004
14153
  STORE_CALLBACK_FIELD_OPTIONS_TIMEOUT_MS,
14154
+ STORE_PURCHASE_SUCCESS_CALLBACK_NAME,
14155
+ STORE_PURCHASE_SUCCESS_SCOPE,
14005
14156
  SocialWidget,
14006
14157
  StoreWidget
14007
14158
  };