@returningai/widget-sdk 1.6.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  }
@@ -12157,534 +11608,1150 @@ function createDOMPurify() {
12157
11608
  _neutralizeRoot(dirty);
12158
11609
  throw error;
12159
11610
  }
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;
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({});
11703
+ }
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;
11711
+ }
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];
11718
+ }
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;
11826
+ }
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%");
11931
+ }
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"
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)
12234
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;
12215
+ }
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
+ }
12362
12227
  }
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}`;
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;
12367
12237
  }
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;
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
+ );
12645
12526
  });
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
12527
  }
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();
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 assertCallbackName(callbackName) {
12546
+ if (!callbackName || typeof callbackName !== "string") {
12547
+ throw new TypeError("ReturningAI callback name must be a non-empty string");
12548
+ }
12549
+ }
12550
+ function assertCallback(callback) {
12551
+ if (typeof callback !== "function") {
12552
+ throw new TypeError("ReturningAI callback must be a function");
12553
+ }
12554
+ }
12555
+ class WidgetCallbackRegistry {
12556
+ constructor() {
12557
+ __publicField(this, "registeredCallbacks", {});
12558
+ __publicField(this, "locked", false);
12559
+ }
12560
+ get isLocked() {
12561
+ return this.locked;
12562
+ }
12563
+ registerCallback(callbackName, callback) {
12564
+ if (this.locked) {
12565
+ return;
12658
12566
  }
12659
- } catch {
12567
+ assertCallbackName(callbackName);
12568
+ assertCallback(callback);
12569
+ this.registeredCallbacks = {
12570
+ ...this.registeredCallbacks,
12571
+ [callbackName]: callback
12572
+ };
12573
+ }
12574
+ lock() {
12575
+ if (this.locked) {
12576
+ return;
12577
+ }
12578
+ this.locked = true;
12579
+ }
12580
+ read() {
12581
+ return { ...this.registeredCallbacks };
12660
12582
  }
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
12583
  }
12667
- function getOrCreateVisitorId(communityId, storagePrefix) {
12668
- const key = `${storagePrefix}-${VISITOR_KEY}-${communityId}`;
12584
+ const STORE_CALLBACK_FIELD_OPTIONS_SCOPE = "store.callback_field_options";
12585
+ const STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME = "callbackFieldOptions";
12586
+ const STORE_CALLBACK_FIELD_OPTIONS_TIMEOUT_MS = 15e3;
12587
+ const STORE_PURCHASE_SUCCESS_SCOPE = "store.purchase_success";
12588
+ const STORE_PURCHASE_SUCCESS_CALLBACK_NAME = "storePurchaseSuccess";
12589
+ function isSupportedStoreHostCallback(callbackName, payload) {
12590
+ if (!payload) return false;
12591
+ 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;
12592
+ }
12593
+ async function invokeStoreHostCallback(callbacks, callbackName, payload) {
12594
+ if (!isSupportedStoreHostCallback(callbackName, payload)) {
12595
+ throw new Error("Host callback is only supported for store widgets");
12596
+ }
12597
+ const callback = callbacks[callbackName];
12598
+ if (!callback) throw new Error("Host callback is not registered");
12599
+ let value;
12669
12600
  try {
12670
- let id = localStorage.getItem(key);
12671
- if (!id) {
12672
- id = uuid();
12673
- localStorage.setItem(key, id);
12674
- }
12675
- return id;
12601
+ value = await callback(payload);
12676
12602
  } catch {
12677
- return uuid();
12603
+ throw new Error("Host callback failed");
12678
12604
  }
12605
+ return callbackName === STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME ? value : void 0;
12679
12606
  }
12680
- function fireTracking(url) {
12681
- if (!url) return;
12607
+ function debounce(fn, wait) {
12608
+ let timer = null;
12609
+ return {
12610
+ call(h) {
12611
+ if (timer) clearTimeout(timer);
12612
+ timer = setTimeout(() => {
12613
+ fn(h);
12614
+ timer = null;
12615
+ }, wait);
12616
+ },
12617
+ cancel() {
12618
+ if (timer) {
12619
+ clearTimeout(timer);
12620
+ timer = null;
12621
+ }
12622
+ }
12623
+ };
12624
+ }
12625
+ function sendTokenToWidget(config, state, iframe) {
12626
+ var _a;
12627
+ if (!state.accessToken) return;
12682
12628
  try {
12683
- const img = new Image();
12684
- img.src = url;
12629
+ (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
12630
+ {
12631
+ type: "RETURNINGAI_WIDGET_TOKEN",
12632
+ value: {
12633
+ widgetId: config.widgetId,
12634
+ token: state.accessToken,
12635
+ ...config.customData !== void 0 && { customData: config.customData }
12636
+ }
12637
+ },
12638
+ config.widgetDomain
12639
+ );
12685
12640
  } catch {
12686
12641
  }
12687
12642
  }
12643
+ function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled, onLogout, hideLoader, emit, getCallbacks) {
12644
+ const heightSetter = debounce((h) => {
12645
+ iframe.style.height = `${h}px`;
12646
+ emit == null ? void 0 : emit("rai-height-change", { height: h });
12647
+ }, config.heightDebounce ?? 100);
12648
+ const handler = async (event) => {
12649
+ var _a;
12650
+ if (event.origin !== config.widgetDomain) return;
12651
+ if (!event.data || typeof event.data.type !== "string") return;
12652
+ const { type, containerId, widgetId, payload } = event.data;
12653
+ switch (type) {
12654
+ case "RETURNINGAI_WIDGET_REQUEST_TOKEN": {
12655
+ try {
12656
+ const token = await getValidToken(config, state, onRefreshScheduled);
12657
+ (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
12658
+ {
12659
+ type: "RETURNINGAI_WIDGET_TOKEN",
12660
+ value: {
12661
+ token,
12662
+ widgetId: config.widgetId,
12663
+ ...config.customData !== void 0 && { customData: config.customData }
12664
+ }
12665
+ },
12666
+ config.widgetDomain
12667
+ );
12668
+ } catch {
12669
+ }
12670
+ break;
12671
+ }
12672
+ case "WIDGET_HEIGHT_UPDATE": {
12673
+ const h = Number(payload == null ? void 0 : payload.height);
12674
+ if (Number.isFinite(h) && h > 0) {
12675
+ heightSetter.call(h);
12676
+ }
12677
+ break;
12678
+ }
12679
+ case "WIDGET_READY": {
12680
+ if (widgetId !== void 0 && widgetId !== config.widgetId) break;
12681
+ if (containerId !== void 0 && containerId !== config.container) break;
12682
+ iframe.classList.add("loaded");
12683
+ if (hideLoader) hideLoader();
12684
+ emit == null ? void 0 : emit("rai-ready");
12685
+ break;
12686
+ }
12687
+ case "WIDGET_ERROR": {
12688
+ if (hideLoader) hideLoader();
12689
+ break;
12690
+ }
12691
+ case "WIDGET_LOGOUT": {
12692
+ if (onLogout) await onLogout();
12693
+ break;
12694
+ }
12695
+ case "RETURNINGAI_WIDGET_ACTION": {
12696
+ if (event.source !== iframe.contentWindow) break;
12697
+ const message = event.data;
12698
+ if (config.widgetType !== "store" && config.widgetType !== "custom") break;
12699
+ if (message.widgetType !== "store") break;
12700
+ if (message.action !== "store:host-callback") break;
12701
+ if (!isSupportedStoreHostCallback(message.callbackName, message.payload)) break;
12702
+ if (message.widgetId !== void 0 && message.widgetId !== config.widgetId) break;
12703
+ if (message.containerId !== void 0 && message.containerId !== config.container) break;
12704
+ const respond = (result) => {
12705
+ var _a2;
12706
+ (_a2 = iframe.contentWindow) == null ? void 0 : _a2.postMessage(result, config.widgetDomain);
12707
+ };
12708
+ const callbacks = (getCallbacks == null ? void 0 : getCallbacks()) ?? {};
12709
+ if (!callbacks[message.callbackName]) {
12710
+ respond({
12711
+ type: "RETURNINGAI_WIDGET_ACTION_RESULT",
12712
+ requestId: message.requestId,
12713
+ action: "store:host-callback",
12714
+ ok: false,
12715
+ status: "failed",
12716
+ error: { message: "Host callback is not registered" }
12717
+ });
12718
+ break;
12719
+ }
12720
+ try {
12721
+ const value = await invokeStoreHostCallback(
12722
+ callbacks,
12723
+ message.callbackName,
12724
+ message.payload
12725
+ );
12726
+ const result = {
12727
+ type: "RETURNINGAI_WIDGET_ACTION_RESULT",
12728
+ requestId: message.requestId,
12729
+ action: "store:host-callback",
12730
+ ok: true,
12731
+ status: "success",
12732
+ ...value !== void 0 ? { value } : {}
12733
+ };
12734
+ respond(result);
12735
+ } catch {
12736
+ respond({
12737
+ type: "RETURNINGAI_WIDGET_ACTION_RESULT",
12738
+ requestId: message.requestId,
12739
+ action: "store:host-callback",
12740
+ ok: false,
12741
+ status: "failed",
12742
+ error: { message: "Host callback failed" }
12743
+ });
12744
+ }
12745
+ break;
12746
+ }
12747
+ }
12748
+ };
12749
+ window.addEventListener("message", handler);
12750
+ return () => {
12751
+ window.removeEventListener("message", handler);
12752
+ heightSetter.cancel();
12753
+ };
12754
+ }
12688
12755
  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
12756
  const DEFINED_ATTRS = /* @__PURE__ */ new Set([
12690
12757
  "community-id",
@@ -12817,9 +12884,10 @@ function ensureGoogleFontLoaded(family) {
12817
12884
  const DANGEROUS_ELEMENTS = ["script", "iframe", "object", "embed"];
12818
12885
  const URL_ATTRS = ["src", "href", "xlink:href"];
12819
12886
  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) => {
12887
+ const template = document.createElement("template");
12888
+ template.innerHTML = html2;
12889
+ template.content.querySelectorAll(DANGEROUS_ELEMENTS.join(",")).forEach((el2) => el2.remove());
12890
+ template.content.querySelectorAll("*").forEach((el2) => {
12823
12891
  Array.from(el2.attributes).forEach((attr) => {
12824
12892
  const name = attr.name.toLowerCase();
12825
12893
  if (name.startsWith("on")) {
@@ -12832,7 +12900,9 @@ function sanitizeLoaderHtml(html2) {
12832
12900
  }
12833
12901
  });
12834
12902
  });
12835
- return doc.body.innerHTML;
12903
+ const container = document.createElement("div");
12904
+ container.appendChild(template.content.cloneNode(true));
12905
+ return container.innerHTML;
12836
12906
  }
12837
12907
  function readConfig(el2, existingId) {
12838
12908
  const get = (name) => el2.getAttribute(name) ?? el2.getAttribute(`data-${name}`) ?? "";
@@ -13177,15 +13247,13 @@ class BaseWidget extends HTMLElement {
13177
13247
  }
13178
13248
  /**
13179
13249
  * 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.
13250
+ * and renders the configured animation above the loading text.
13183
13251
  *
13184
13252
  * Stale-while-revalidate:
13185
13253
  * - Cache hit → apply palette & render animation synchronously, fire
13186
13254
  * background refresh.
13187
- * - Cache miss → text remains visible; await network; apply palette
13188
- * + render animation when response arrives.
13255
+ * - Cache miss → retain the initial shell; await network; apply palette +
13256
+ * render the resolved animation or fallback.
13189
13257
  */
13190
13258
  async fetchAndRenderLoader() {
13191
13259
  var _a;
@@ -13299,48 +13367,35 @@ class BaseWidget extends HTMLElement {
13299
13367
  }
13300
13368
  hideLoader() {
13301
13369
  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);
13370
+ const loader = this.loaderEl;
13371
+ loader.classList.add("fade-out");
13372
+ setTimeout(() => loader.remove(), 300);
13307
13373
  this.loaderEl = null;
13308
13374
  }
13309
13375
  /**
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.
13376
+ * Resolve after a legacy bundle's first rendered child has reached a paint.
13377
+ * This remains the fallback for bundles that do not expose a deep-ready
13378
+ * handle from mount(). It intentionally does not hide the loader itself so
13379
+ * a stronger nested-MFE readiness signal can take ownership of the handoff.
13324
13380
  */
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) {
13381
+ waitForFirstBundlePaint(container) {
13382
+ return new Promise((resolve) => {
13383
+ let settled = false;
13384
+ let timeoutId;
13385
+ const observer = new MutationObserver(() => {
13386
+ if (container.children.length > 0) finish();
13387
+ });
13388
+ const finish = () => {
13389
+ if (settled) return;
13390
+ settled = true;
13335
13391
  observer.disconnect();
13336
- requestAnimationFrame(remove);
13337
- }
13392
+ clearTimeout(timeoutId);
13393
+ requestAnimationFrame(() => resolve());
13394
+ };
13395
+ observer.observe(container, { childList: true });
13396
+ timeoutId = setTimeout(finish, 2e3);
13397
+ if (container.children.length > 0) finish();
13338
13398
  });
13339
- observer.observe(container, { childList: true });
13340
- setTimeout(() => {
13341
- observer.disconnect();
13342
- remove();
13343
- }, 2e3);
13344
13399
  }
13345
13400
  showError() {
13346
13401
  this.hideLoader();
@@ -13418,7 +13473,8 @@ class BaseWidget extends HTMLElement {
13418
13473
  }
13419
13474
  removeBanner() {
13420
13475
  var _a;
13421
- if ((_a = this.bannerNode) == null ? void 0 : _a.parentNode) this.bannerNode.parentNode.removeChild(this.bannerNode);
13476
+ if ((_a = this.bannerNode) == null ? void 0 : _a.parentNode)
13477
+ this.bannerNode.parentNode.removeChild(this.bannerNode);
13422
13478
  this.bannerNode = null;
13423
13479
  }
13424
13480
  /**
@@ -13431,7 +13487,8 @@ class BaseWidget extends HTMLElement {
13431
13487
  var _a, _b;
13432
13488
  try {
13433
13489
  if (this.bannerNode) return;
13434
- if (this.getAttribute("banner") === "off" || this.hasAttribute("no-banner")) return;
13490
+ if (this.getAttribute("banner") === "off" || this.hasAttribute("no-banner"))
13491
+ return;
13435
13492
  const apiUrl = this.resolveBannerApiUrl();
13436
13493
  const isCustom = this.config.widgetType === "custom";
13437
13494
  let communityId;
@@ -13445,13 +13502,17 @@ class BaseWidget extends HTMLElement {
13445
13502
  decoded = "";
13446
13503
  }
13447
13504
  if (isObjectId(decoded)) widgetId = decoded;
13448
- else if (isObjectId(this.config.communityId)) widgetId = this.config.communityId;
13505
+ else if (isObjectId(this.config.communityId))
13506
+ widgetId = this.config.communityId;
13449
13507
  if (!widgetId) return;
13450
13508
  } else {
13451
13509
  communityId = this.config.communityId;
13452
13510
  }
13453
13511
  const sessionKey = this.config.communityId || widgetId || "rai";
13454
- const visitorId = getOrCreateVisitorId(sessionKey, this.config.storagePrefix);
13512
+ const visitorId = getOrCreateVisitorId(
13513
+ sessionKey,
13514
+ this.config.storagePrefix
13515
+ );
13455
13516
  const w = window.innerWidth;
13456
13517
  const device = w < 640 ? "mobile" : w < 1024 ? "tablet" : "desktop";
13457
13518
  const result = await fetchEligibleBanner(apiUrl, {
@@ -13467,13 +13528,18 @@ class BaseWidget extends HTMLElement {
13467
13528
  hostname: window.location.hostname,
13468
13529
  path: window.location.pathname
13469
13530
  },
13470
- viewport: { width: window.innerWidth, height: window.innerHeight, device },
13531
+ viewport: {
13532
+ width: window.innerWidth,
13533
+ height: window.innerHeight,
13534
+ device
13535
+ },
13471
13536
  identifier: { type: "anonymous_session", value: visitorId }
13472
13537
  });
13473
13538
  if (!this.isConnected) return;
13474
13539
  const banner = result.banner;
13475
13540
  if (!banner) return;
13476
- if (((_a = banner.placement) == null ? void 0 : _a.mobileBehavior) === "hide" && device === "mobile") return;
13541
+ if (((_a = banner.placement) == null ? void 0 : _a.mobileBehavior) === "hide" && device === "mobile")
13542
+ return;
13477
13543
  const node = renderBanner(banner, {
13478
13544
  onCtaClick: () => {
13479
13545
  var _a2, _b2, _c;
@@ -13481,7 +13547,8 @@ class BaseWidget extends HTMLElement {
13481
13547
  this.emit("rai-banner-click", { bannerId: banner.id });
13482
13548
  const url = (_b2 = banner.cta) == null ? void 0 : _b2.url;
13483
13549
  if (!url) return;
13484
- if ((_c = banner.cta) == null ? void 0 : _c.openInNewTab) window.open(url, "_blank", "noopener,noreferrer");
13550
+ if ((_c = banner.cta) == null ? void 0 : _c.openInNewTab)
13551
+ window.open(url, "_blank", "noopener,noreferrer");
13485
13552
  else window.location.href = url;
13486
13553
  },
13487
13554
  onDismiss: () => {
@@ -13527,7 +13594,7 @@ class BaseWidget extends HTMLElement {
13527
13594
  this.addEventListener("rai-ready", mountBannerOnce);
13528
13595
  bannerGateTimer = window.setTimeout(mountBannerOnce, 2500);
13529
13596
  if (this.config.bundleUrl) {
13530
- this.mountWidget();
13597
+ void this.mountWidget(initId);
13531
13598
  } else {
13532
13599
  this.createIframe();
13533
13600
  }
@@ -13650,8 +13717,9 @@ class BaseWidget extends HTMLElement {
13650
13717
  );
13651
13718
  }
13652
13719
  // ── Bundle mode (no-iframe) ──────────────────────────────────────────
13653
- async mountWidget() {
13720
+ async mountWidget(initId) {
13654
13721
  var _a;
13722
+ let container = null;
13655
13723
  try {
13656
13724
  this.teardownMountedWidget();
13657
13725
  const bundleApiUrl = this.config.v2ApiUrl || resolveV2ApiUrl(this.config.domainKey);
@@ -13677,13 +13745,15 @@ class BaseWidget extends HTMLElement {
13677
13745
  if (!((_a = window[globalName]) == null ? void 0 : _a.mount)) {
13678
13746
  await loadScript(this.config.bundleUrl);
13679
13747
  }
13748
+ if (!this.isCurrentInit(initId)) return;
13680
13749
  const widgetGlobal = window[globalName];
13681
13750
  if (!(widgetGlobal == null ? void 0 : widgetGlobal.mount)) {
13682
13751
  throw new Error(
13683
13752
  `window.${globalName}.mount not found after loading bundle`
13684
13753
  );
13685
13754
  }
13686
- const container = document.createElement("div");
13755
+ container = document.createElement("div");
13756
+ const hideNestedBundleUntilReady = this.config.widgetType === "custom";
13687
13757
  container.className = "rai-widget-root";
13688
13758
  container.setAttribute("data-rai-widget-root", "true");
13689
13759
  container.style.cssText = [
@@ -13691,16 +13761,21 @@ class BaseWidget extends HTMLElement {
13691
13761
  "width: 100%",
13692
13762
  "height: 100%",
13693
13763
  "color: var(--text-1)",
13694
- "background: var(--background-1)",
13695
- "font-family: var(--rai-loader-font, 'Roboto', system-ui, -apple-system, sans-serif)"
13764
+ "background: transparent",
13765
+ "font-family: var(--rai-loader-font, 'Roboto', system-ui, -apple-system, sans-serif)",
13766
+ // Custom loaders may intentionally use a transparent backdrop. Keep
13767
+ // their nested bundle fallbacks out of the visual tree until deep
13768
+ // readiness so they cannot show through that transparency. Other
13769
+ // widget types preserve their existing first-paint behavior.
13770
+ `visibility: ${hideNestedBundleUntilReady ? "hidden" : "visible"}`
13696
13771
  ].join("; ");
13697
13772
  this.appendChild(container);
13698
13773
  if (!this.shadow.querySelector("slot")) {
13699
13774
  const slot = document.createElement("slot");
13700
13775
  this.shadow.appendChild(slot);
13701
13776
  }
13702
- this.deferLoaderRemovalUntilReactPaints(container);
13703
- widgetGlobal.mount(container, {
13777
+ const firstPaint = this.waitForFirstBundlePaint(container);
13778
+ const mountResult = widgetGlobal.mount(container, {
13704
13779
  widgetType: this.config.widgetType,
13705
13780
  communityId: this.config.communityId,
13706
13781
  channelId: this.config.channelId,
@@ -13721,16 +13796,31 @@ class BaseWidget extends HTMLElement {
13721
13796
  isOpenWidget: !this.state.accessToken,
13722
13797
  defaultColorSchema: this.config.theme === "dark" ? "dark" : "light",
13723
13798
  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);
13799
+ if (this.config.widgetType !== "store" && this.config.widgetType !== "custom") {
13800
+ throw new Error(
13801
+ "Host callback is only supported for store widgets"
13802
+ );
13803
+ }
13804
+ return invokeStoreHostCallback(
13805
+ this.readRuntimeCallbacks(),
13806
+ callbackName,
13807
+ payload
13808
+ );
13730
13809
  }
13731
13810
  });
13811
+ await waitForBundleMountReadiness(mountResult, firstPaint);
13812
+ if (!this.isCurrentInit(initId) || !container.isConnected) {
13813
+ container.remove();
13814
+ return;
13815
+ }
13816
+ if (hideNestedBundleUntilReady) container.style.visibility = "visible";
13817
+ this.hideLoader();
13732
13818
  this.emit("rai-mounted");
13733
13819
  } catch (err) {
13820
+ if (!this.isCurrentInit(initId)) {
13821
+ container == null ? void 0 : container.remove();
13822
+ return;
13823
+ }
13734
13824
  console.error("[rai-widget] Bundle mount failed:", err);
13735
13825
  this.showError();
13736
13826
  }
@@ -13917,7 +14007,7 @@ class CustomWidget extends BaseWidget {
13917
14007
  }
13918
14008
  }
13919
14009
  }
13920
- console.log(`[rai-widget] v${"1.6.2"}`);
14010
+ console.log(`[rai-widget] v${"1.7.0"}`);
13921
14011
  class StoreWidgetCompat extends StoreWidget {
13922
14012
  }
13923
14013
  const WIDGET_REGISTRY = [
@@ -13981,7 +14071,7 @@ function exposePublicApi() {
13981
14071
  })();
13982
14072
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
13983
14073
  window.ReturningAIWidget = {
13984
- version: "1.6.2",
14074
+ version: "1.7.0",
13985
14075
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
13986
14076
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
13987
14077
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
@@ -14002,6 +14092,8 @@ export {
14002
14092
  STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME,
14003
14093
  STORE_CALLBACK_FIELD_OPTIONS_SCOPE,
14004
14094
  STORE_CALLBACK_FIELD_OPTIONS_TIMEOUT_MS,
14095
+ STORE_PURCHASE_SUCCESS_CALLBACK_NAME,
14096
+ STORE_PURCHASE_SUCCESS_SCOPE,
14005
14097
  SocialWidget,
14006
14098
  StoreWidget
14007
14099
  };