@returningai/widget-sdk 1.8.13 → 1.8.14

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.
@@ -12275,7 +12275,12 @@ function legacyStorageKey(config) {
12275
12275
  function errorSettingsKey(config) {
12276
12276
  return `${config.storagePrefix}-${config.widgetId}-error-settings`;
12277
12277
  }
12278
+ const retiredStates = /* @__PURE__ */ new WeakSet();
12279
+ function retireWidgetState(state) {
12280
+ retiredStates.add(state);
12281
+ }
12278
12282
  function saveToStorage(config, state) {
12283
+ if (retiredStates.has(state)) return;
12279
12284
  try {
12280
12285
  const data = {
12281
12286
  refreshToken: state.refreshToken,
@@ -12311,7 +12316,8 @@ function loadFromStorage(config, state) {
12311
12316
  return false;
12312
12317
  }
12313
12318
  }
12314
- function clearStorage(config) {
12319
+ function clearStorage(config, state) {
12320
+ if (state && retiredStates.has(state)) return;
12315
12321
  try {
12316
12322
  localStorage.removeItem(storageKey(config));
12317
12323
  localStorage.removeItem(legacyStorageKey(config));
@@ -12446,6 +12452,363 @@ function createAuthRenewalRecoveryController(retry, environment = createDefaultE
12446
12452
  cancel: reset
12447
12453
  };
12448
12454
  }
12455
+ const STORE_CALLBACK_FIELD_OPTIONS_SCOPE = "store.callback_field_options";
12456
+ const STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME = "callbackFieldOptions";
12457
+ const STORE_CALLBACK_FIELD_OPTIONS_TIMEOUT_MS = 15e3;
12458
+ const STORE_PURCHASE_SUCCESS_SCOPE = "store.purchase_success";
12459
+ const STORE_PURCHASE_SUCCESS_CALLBACK_NAME = "storePurchaseSuccess";
12460
+ const MILESTONE_CTA_CLICK_SCOPE = "milestone.cta_click";
12461
+ const MILESTONE_CTA_CLICK_CALLBACK_NAME = "milestoneCtaClick";
12462
+ const MILESTONE_CTA_CLICK_EVENT = "milestone_cta_clicked";
12463
+ function isRecord$1(value) {
12464
+ return typeof value === "object" && value !== null;
12465
+ }
12466
+ function isStringRecord$1(value) {
12467
+ return isRecord$1(value) && Object.values(value).every((item) => typeof item === "string");
12468
+ }
12469
+ function readEmbedTokenClaims(embedToken) {
12470
+ if (!embedToken) return null;
12471
+ const encodedPayload = embedToken.split(".")[1];
12472
+ if (!encodedPayload) return null;
12473
+ try {
12474
+ const normalizedPayload = encodedPayload.replace(/-/g, "+").replace(/_/g, "/");
12475
+ const paddedPayload = normalizedPayload.padEnd(
12476
+ Math.ceil(normalizedPayload.length / 4) * 4,
12477
+ "="
12478
+ );
12479
+ const bytes = Uint8Array.from(atob(paddedPayload), (character) => character.charCodeAt(0));
12480
+ const claims = JSON.parse(new TextDecoder().decode(bytes));
12481
+ return isRecord$1(claims) ? claims : null;
12482
+ } catch {
12483
+ return null;
12484
+ }
12485
+ }
12486
+ function readEmbedTokenUserIdentifiers(embedToken) {
12487
+ const claims = readEmbedTokenClaims(embedToken);
12488
+ if (!claims || !isStringRecord$1(claims.userIdentifiers)) return null;
12489
+ return { ...claims.userIdentifiers };
12490
+ }
12491
+ function resolveHostUserIdentifiers(config) {
12492
+ return readEmbedTokenUserIdentifiers(config.embedToken) ?? { ...config.userIdentifiers };
12493
+ }
12494
+ function hasCallbackContract(payload, callbackName, scope) {
12495
+ return isRecord$1(payload) && payload.callbackName === callbackName && payload.scope === scope;
12496
+ }
12497
+ function isMilestoneCtaClickPayload(payload) {
12498
+ if (!hasCallbackContract(
12499
+ payload,
12500
+ MILESTONE_CTA_CLICK_CALLBACK_NAME,
12501
+ MILESTONE_CTA_CLICK_SCOPE
12502
+ )) return false;
12503
+ const context = payload.context;
12504
+ if (!isRecord$1(context)) return false;
12505
+ const community = context.community;
12506
+ const milestone = context.milestone;
12507
+ const stage = context.stage;
12508
+ const button = context.button;
12509
+ const user = context.user;
12510
+ return context.event === MILESTONE_CTA_CLICK_EVENT && isRecord$1(community) && typeof community.id === "string" && community.id.length > 0 && (community.name === void 0 || community.name === null || typeof community.name === "string") && isRecord$1(milestone) && typeof milestone.id === "string" && milestone.id.length > 0 && typeof milestone.name === "string" && milestone.name.length > 0 && isRecord$1(stage) && typeof stage.id === "string" && stage.id.length > 0 && typeof stage.name === "string" && stage.name.length > 0 && isRecord$1(button) && typeof button.name === "string" && button.name.length > 0 && isRecord$1(user) && isStringRecord$1(user.identifiers) && typeof context.clickedAt === "string" && context.clickedAt.length > 0 && !Number.isNaN(Date.parse(context.clickedAt));
12511
+ }
12512
+ function enrichHostCallbackPayload(payload, userIdentifiers) {
12513
+ if (!userIdentifiers || Object.keys(userIdentifiers).length === 0) return payload;
12514
+ if (!isRecord$1(payload)) return payload;
12515
+ const context = payload.context;
12516
+ if (!isRecord$1(context)) return payload;
12517
+ const user = context.user;
12518
+ if (!isRecord$1(user)) return payload;
12519
+ return {
12520
+ ...payload,
12521
+ context: {
12522
+ ...context,
12523
+ user: {
12524
+ ...user,
12525
+ identifiers: { ...userIdentifiers }
12526
+ }
12527
+ }
12528
+ };
12529
+ }
12530
+ const HOST_CALLBACK_DEFINITIONS = {
12531
+ [STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME]: {
12532
+ callbackName: STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME,
12533
+ scope: STORE_CALLBACK_FIELD_OPTIONS_SCOPE,
12534
+ sourceWidgetType: "store",
12535
+ allowedHostWidgetTypes: ["store", "custom"],
12536
+ completionMode: "result",
12537
+ missingHandler: "reject",
12538
+ legacyActions: ["store:host-callback"],
12539
+ validatePayload: (payload) => hasCallbackContract(
12540
+ payload,
12541
+ STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME,
12542
+ STORE_CALLBACK_FIELD_OPTIONS_SCOPE
12543
+ )
12544
+ },
12545
+ [STORE_PURCHASE_SUCCESS_CALLBACK_NAME]: {
12546
+ callbackName: STORE_PURCHASE_SUCCESS_CALLBACK_NAME,
12547
+ scope: STORE_PURCHASE_SUCCESS_SCOPE,
12548
+ sourceWidgetType: "store",
12549
+ allowedHostWidgetTypes: ["store", "custom"],
12550
+ completionMode: "acknowledged",
12551
+ missingHandler: "reject",
12552
+ legacyActions: ["store:host-callback"],
12553
+ validatePayload: (payload) => hasCallbackContract(
12554
+ payload,
12555
+ STORE_PURCHASE_SUCCESS_CALLBACK_NAME,
12556
+ STORE_PURCHASE_SUCCESS_SCOPE
12557
+ )
12558
+ },
12559
+ [MILESTONE_CTA_CLICK_CALLBACK_NAME]: {
12560
+ callbackName: MILESTONE_CTA_CLICK_CALLBACK_NAME,
12561
+ scope: MILESTONE_CTA_CLICK_SCOPE,
12562
+ sourceWidgetType: "milestone",
12563
+ allowedHostWidgetTypes: ["milestone", "custom"],
12564
+ completionMode: "detached",
12565
+ missingHandler: "ignore",
12566
+ legacyActions: [],
12567
+ validatePayload: isMilestoneCtaClickPayload
12568
+ }
12569
+ };
12570
+ function createInvocationId() {
12571
+ return `host_callback_${Date.now()}_${Math.random().toString(36).slice(2)}`;
12572
+ }
12573
+ function observe(observer, event) {
12574
+ if (!observer) return;
12575
+ try {
12576
+ observer(event);
12577
+ } catch {
12578
+ }
12579
+ }
12580
+ function getHostCallbackDefinition(callbackName, payload) {
12581
+ const definition = HOST_CALLBACK_DEFINITIONS[callbackName];
12582
+ return (definition == null ? void 0 : definition.validatePayload(payload)) ? definition : void 0;
12583
+ }
12584
+ function getSupportedHostCallbacks(callbacks, hostWidgetType) {
12585
+ return Object.values(HOST_CALLBACK_DEFINITIONS).filter(
12586
+ (definition) => definition.allowedHostWidgetTypes.includes(hostWidgetType) && Object.prototype.hasOwnProperty.call(callbacks, definition.callbackName) && typeof callbacks[definition.callbackName] === "function"
12587
+ ).map((definition) => definition.callbackName);
12588
+ }
12589
+ function isHostCallbackMessageSupported(definition, action, sourceWidgetType, hostWidgetType) {
12590
+ const actionSupported = action === "widget:host-callback" || definition.legacyActions.includes(action);
12591
+ return actionSupported && sourceWidgetType === definition.sourceWidgetType && definition.allowedHostWidgetTypes.includes(hostWidgetType);
12592
+ }
12593
+ function dispatchHostCallback(input) {
12594
+ const definition = getHostCallbackDefinition(input.callbackName, input.payload);
12595
+ if (!definition || !definition.allowedHostWidgetTypes.includes(input.hostWidgetType)) {
12596
+ throw new Error("Host callback is not supported for this widget");
12597
+ }
12598
+ const invocationId = input.invocationId || createInvocationId();
12599
+ const eventBase = {
12600
+ invocationId,
12601
+ callbackName: definition.callbackName,
12602
+ scope: definition.scope,
12603
+ sourceWidgetType: definition.sourceWidgetType,
12604
+ hostWidgetType: input.hostWidgetType,
12605
+ completionMode: definition.completionMode,
12606
+ transport: input.transport
12607
+ };
12608
+ observe(input.observer, { type: "callback.received", ...eventBase });
12609
+ const callback = input.callbacks[definition.callbackName];
12610
+ if (!callback) {
12611
+ observe(input.observer, { type: "callback.handler_missing", ...eventBase });
12612
+ if (definition.missingHandler === "ignore") return;
12613
+ return Promise.reject(new Error("Host callback is not registered"));
12614
+ }
12615
+ observe(input.observer, { type: "callback.handler_started", ...eventBase });
12616
+ let result;
12617
+ try {
12618
+ result = callback(input.payload);
12619
+ } catch (error) {
12620
+ observe(input.observer, {
12621
+ type: "callback.handler_rejected",
12622
+ ...eventBase,
12623
+ error
12624
+ });
12625
+ if (definition.completionMode === "detached") return;
12626
+ return Promise.reject(new Error("Host callback failed"));
12627
+ }
12628
+ if (definition.completionMode === "detached") {
12629
+ if (result && typeof result.then === "function") {
12630
+ void Promise.resolve(result).then(
12631
+ () => observe(input.observer, { type: "callback.handler_resolved", ...eventBase }),
12632
+ (error) => observe(input.observer, {
12633
+ type: "callback.handler_rejected",
12634
+ ...eventBase,
12635
+ error
12636
+ })
12637
+ );
12638
+ } else {
12639
+ observe(input.observer, { type: "callback.handler_resolved", ...eventBase });
12640
+ }
12641
+ return;
12642
+ }
12643
+ return Promise.resolve(result).then(
12644
+ (value) => {
12645
+ observe(input.observer, { type: "callback.handler_resolved", ...eventBase });
12646
+ return definition.completionMode === "result" ? value : void 0;
12647
+ },
12648
+ (error) => {
12649
+ observe(input.observer, {
12650
+ type: "callback.handler_rejected",
12651
+ ...eventBase,
12652
+ error
12653
+ });
12654
+ throw new Error("Host callback failed");
12655
+ }
12656
+ );
12657
+ }
12658
+ const EMBED_TOKEN_REJECTED_ERROR = "Invalid or expired embed token";
12659
+ const EMBED_TOKEN_EXPIRY_WINDOW_MS = 3e4;
12660
+ const EMBED_TOKEN_REQUEST_TIMEOUT_MS = 1e4;
12661
+ const EMBED_TOKEN_REQUEST_COOLDOWN_MS = 3e4;
12662
+ const EMBED_TOKEN_CACHE_MARGIN_MS = 6e4;
12663
+ const defaultClock = {
12664
+ now: () => Date.now(),
12665
+ setTimeout: (callback, ms) => setTimeout(callback, ms),
12666
+ clearTimeout: (handle) => clearTimeout(handle)
12667
+ };
12668
+ function isRecord(value) {
12669
+ return typeof value === "object" && value !== null && !Array.isArray(value);
12670
+ }
12671
+ function isStringRecord(value) {
12672
+ return isRecord(value) && Object.values(value).every((item) => typeof item === "string");
12673
+ }
12674
+ function decodeEmbedTokenClaims(embedToken) {
12675
+ const payload = readEmbedTokenClaims(embedToken ?? void 0);
12676
+ if (!payload) return null;
12677
+ return {
12678
+ accessId: typeof payload.accessId === "string" ? payload.accessId : void 0,
12679
+ communityId: typeof payload.communityId === "string" ? payload.communityId : void 0,
12680
+ userIdentifiers: isStringRecord(payload.userIdentifiers) ? { ...payload.userIdentifiers } : void 0,
12681
+ exp: typeof payload.exp === "number" && Number.isFinite(payload.exp) ? payload.exp : void 0
12682
+ };
12683
+ }
12684
+ function sortedIdentifiers(identifiers) {
12685
+ return Object.keys(identifiers || {}).sort().map((key) => [key, identifiers[key]]);
12686
+ }
12687
+ function hasIdentifiers(identifiers) {
12688
+ return !!identifiers && Object.keys(identifiers).length > 0;
12689
+ }
12690
+ function embedIdentityKey(embedToken) {
12691
+ if (!embedToken) return "";
12692
+ const claims = decodeEmbedTokenClaims(embedToken);
12693
+ if (!claims || !hasIdentifiers(claims.userIdentifiers)) return embedToken;
12694
+ return JSON.stringify({
12695
+ accessId: claims.accessId ?? "",
12696
+ communityId: claims.communityId ?? "",
12697
+ userIdentifiers: sortedIdentifiers(claims.userIdentifiers)
12698
+ });
12699
+ }
12700
+ function identitySignatureForConfig(config) {
12701
+ return JSON.stringify({
12702
+ embedToken: embedIdentityKey(config.embedToken),
12703
+ userIdentifiers: sortedIdentifiers(config.userIdentifiers)
12704
+ });
12705
+ }
12706
+ function isEmbedTokenRejected(error) {
12707
+ return !!error && error.status === 401 && error.error === EMBED_TOKEN_REJECTED_ERROR;
12708
+ }
12709
+ function isEmbedTokenExpiring(embedToken, now = Date.now()) {
12710
+ var _a;
12711
+ const exp = (_a = decodeEmbedTokenClaims(embedToken)) == null ? void 0 : _a.exp;
12712
+ if (exp === void 0) return false;
12713
+ return exp * 1e3 <= now + EMBED_TOKEN_EXPIRY_WINDOW_MS;
12714
+ }
12715
+ function readEmbedTokenProvider(callbacks) {
12716
+ const provider = callbacks == null ? void 0 : callbacks.embedTokenRequest;
12717
+ return typeof provider === "function" ? provider : null;
12718
+ }
12719
+ function sameIdentifiers(left, right) {
12720
+ return JSON.stringify(sortedIdentifiers(left)) === JSON.stringify(sortedIdentifiers(right));
12721
+ }
12722
+ function acceptRenewedEmbedToken(staleToken, result, now = Date.now()) {
12723
+ if (!isRecord(result)) return null;
12724
+ const candidate = result.embedToken;
12725
+ if (typeof candidate !== "string" || !candidate || candidate === staleToken) return null;
12726
+ const stale = decodeEmbedTokenClaims(staleToken);
12727
+ const next = decodeEmbedTokenClaims(candidate);
12728
+ if (!stale || !next) return null;
12729
+ if (next.exp === void 0 || next.exp * 1e3 <= now + EMBED_TOKEN_EXPIRY_WINDOW_MS) return null;
12730
+ if (!stale.communityId || stale.communityId !== next.communityId) return null;
12731
+ if (hasIdentifiers(stale.userIdentifiers)) {
12732
+ if (!sameIdentifiers(stale.userIdentifiers, next.userIdentifiers)) return null;
12733
+ } else if (hasIdentifiers(next.userIdentifiers)) {
12734
+ return null;
12735
+ }
12736
+ return candidate;
12737
+ }
12738
+ const REGISTRY_SYMBOL$1 = Symbol.for("returningai.widget-sdk.embed-token-renewal.v1");
12739
+ function registry$1() {
12740
+ const root = globalThis;
12741
+ root[REGISTRY_SYMBOL$1] ?? (root[REGISTRY_SYMBOL$1] = /* @__PURE__ */ new Map());
12742
+ return root[REGISTRY_SYMBOL$1];
12743
+ }
12744
+ function registryEntry(key) {
12745
+ const entries2 = registry$1();
12746
+ let entry = entries2.get(key);
12747
+ if (!entry) {
12748
+ entry = { inFlight: null, cachedToken: null, cachedUntil: 0, cooldownUntil: 0 };
12749
+ entries2.set(key, entry);
12750
+ }
12751
+ return entry;
12752
+ }
12753
+ function markRenewedEmbedTokenRefused(staleToken, refusedToken, clock = defaultClock) {
12754
+ const cooldownUntil = clock.now() + EMBED_TOKEN_REQUEST_COOLDOWN_MS;
12755
+ for (const token of /* @__PURE__ */ new Set([staleToken, refusedToken])) {
12756
+ const entry = registryEntry(embedIdentityKey(token));
12757
+ if (entry.cachedToken === refusedToken) {
12758
+ entry.cachedToken = null;
12759
+ entry.cachedUntil = 0;
12760
+ }
12761
+ entry.cooldownUntil = Math.max(entry.cooldownUntil, cooldownUntil);
12762
+ }
12763
+ }
12764
+ function renewEmbedToken(request) {
12765
+ const clock = request.clock ?? defaultClock;
12766
+ const entry = registryEntry(embedIdentityKey(request.staleToken));
12767
+ const now = clock.now();
12768
+ if (entry.cachedToken && entry.cachedToken !== request.staleToken && now < entry.cachedUntil) {
12769
+ return Promise.resolve(entry.cachedToken);
12770
+ }
12771
+ if (entry.inFlight) return entry.inFlight;
12772
+ if (now < entry.cooldownUntil) return Promise.resolve(null);
12773
+ const current = entry;
12774
+ const inFlight = (async () => {
12775
+ var _a;
12776
+ let timer;
12777
+ const timeout = new Promise((resolve) => {
12778
+ timer = clock.setTimeout(() => resolve(void 0), EMBED_TOKEN_REQUEST_TIMEOUT_MS);
12779
+ });
12780
+ let accepted = null;
12781
+ try {
12782
+ const answer = await Promise.race([
12783
+ Promise.resolve().then(() => request.provider({ reason: request.reason })),
12784
+ timeout
12785
+ ]);
12786
+ accepted = acceptRenewedEmbedToken(request.staleToken, answer, clock.now());
12787
+ } catch {
12788
+ accepted = null;
12789
+ } finally {
12790
+ clock.clearTimeout(timer);
12791
+ }
12792
+ if (accepted) {
12793
+ const exp = ((_a = decodeEmbedTokenClaims(accepted)) == null ? void 0 : _a.exp) ?? 0;
12794
+ current.cachedToken = accepted;
12795
+ current.cachedUntil = exp * 1e3 - EMBED_TOKEN_CACHE_MARGIN_MS;
12796
+ current.cooldownUntil = 0;
12797
+ } else {
12798
+ current.cooldownUntil = clock.now() + EMBED_TOKEN_REQUEST_COOLDOWN_MS;
12799
+ }
12800
+ current.inFlight = null;
12801
+ return accepted;
12802
+ })();
12803
+ current.inFlight = inFlight;
12804
+ return inFlight;
12805
+ }
12806
+ function decideEmbedTokenChange(input) {
12807
+ if (!input.hasAttemptedInit) return "update";
12808
+ if (!input.sameSignature) return "reload";
12809
+ if (!input.tokenChanged) return "update";
12810
+ return input.healthy && input.hasProvider ? "swap" : "reload";
12811
+ }
12449
12812
  function setTokens(config, state, data, onRefreshScheduled) {
12450
12813
  state.accessToken = data.accessToken;
12451
12814
  state.refreshToken = data.refreshToken;
@@ -12471,13 +12834,62 @@ function buildIdentityProof(config) {
12471
12834
  }
12472
12835
  return proof;
12473
12836
  }
12837
+ function canReauthenticateAfterTransientFailure(config) {
12838
+ var _a;
12839
+ if (!config.embedToken) return true;
12840
+ const exp = (_a = readEmbedTokenClaims(config.embedToken)) == null ? void 0 : _a.exp;
12841
+ return typeof exp === "number" && exp * 1e3 > Date.now();
12842
+ }
12474
12843
  function calculateRefreshDelay(accessTokenExpiry, now = Date.now()) {
12475
12844
  const remainingLifetime = accessTokenExpiry - now;
12476
12845
  if (remainingLifetime <= 0) return 0;
12477
12846
  const refreshLeadTime = Math.min(6e4, remainingLifetime / 2);
12478
12847
  return Math.max(0, Math.floor(remainingLifetime - refreshLeadTime));
12479
12848
  }
12480
- async function authenticateServerless(config, state, onRefreshScheduled) {
12849
+ async function authenticateServerless(config, state, renewal, onRefreshScheduled) {
12850
+ var _a;
12851
+ const provider = ((_a = renewal.getEmbedTokenProvider) == null ? void 0 : _a.call(renewal)) ?? null;
12852
+ const isCurrent = () => !renewal.isCurrent || renewal.isCurrent();
12853
+ let renewalAttempted = false;
12854
+ let renewed = null;
12855
+ let presentedToken;
12856
+ const renewEmbedTokenOnce = async () => {
12857
+ const staleToken = config.embedToken;
12858
+ if (!provider || !staleToken || renewalAttempted) return false;
12859
+ renewalAttempted = true;
12860
+ const fresh = await renewEmbedToken({
12861
+ staleToken,
12862
+ reason: renewal.reason ?? "expired",
12863
+ provider
12864
+ });
12865
+ if (!fresh) return false;
12866
+ if (config.embedToken === staleToken) config.embedToken = fresh;
12867
+ renewed = { from: staleToken, to: fresh };
12868
+ return true;
12869
+ };
12870
+ const signIn = async () => {
12871
+ const presented = config.embedToken;
12872
+ presentedToken = presented;
12873
+ const authed = await signInOnce(config, state, onRefreshScheduled);
12874
+ if (!authed && renewed && presented === renewed.to && isEmbedTokenRejected(state.lastAuthError)) {
12875
+ markRenewedEmbedTokenRefused(renewed.from, renewed.to);
12876
+ }
12877
+ return authed;
12878
+ };
12879
+ if (provider && config.embedToken && isEmbedTokenExpiring(config.embedToken)) {
12880
+ await renewEmbedTokenOnce();
12881
+ }
12882
+ if (await signIn()) return true;
12883
+ if (!isEmbedTokenRejected(state.lastAuthError)) return false;
12884
+ if (provider && config.embedToken && config.embedToken !== presentedToken) {
12885
+ if (!isCurrent()) return false;
12886
+ return signIn();
12887
+ }
12888
+ if (!await renewEmbedTokenOnce() || !isCurrent()) return false;
12889
+ return signIn();
12890
+ }
12891
+ async function signInOnce(config, state, onRefreshScheduled) {
12892
+ state.lastAuthError = null;
12481
12893
  const nonce = crypto.randomUUID();
12482
12894
  const timestamp = Date.now();
12483
12895
  const maxRetries = config.maxRetries ?? 3;
@@ -12610,7 +13022,7 @@ async function authenticateViaProxy(config, state, onRefreshScheduled) {
12610
13022
  state.isAuthenticated = false;
12611
13023
  return false;
12612
13024
  }
12613
- async function refreshAccessToken(config, state, onRefreshScheduled, sendToken) {
13025
+ async function refreshAccessToken(config, state, renewal, onRefreshScheduled, sendToken) {
12614
13026
  if (state.isRefreshing) {
12615
13027
  return state.refreshPromise;
12616
13028
  }
@@ -12626,50 +13038,81 @@ async function refreshAccessToken(config, state, onRefreshScheduled, sendToken)
12626
13038
  };
12627
13039
  state.isRefreshing = true;
12628
13040
  state.refreshPromise = (async () => {
12629
- const reauthenticateAfterRefreshFailure = async () => {
13041
+ const adoptNewerGeneration = () => {
12630
13042
  if (state.refreshToken && state.refreshToken !== presentedRefreshToken && state.accessToken && state.accessTokenExpiry && Date.now() < state.accessTokenExpiry) {
12631
13043
  state.isAuthenticated = true;
12632
13044
  if (sendToken) sendToken();
12633
13045
  return true;
12634
13046
  }
12635
- const authed = await authenticateServerless(config, state, onRefreshScheduled);
13047
+ return false;
13048
+ };
13049
+ const reauthenticateAfterRefreshFailure = async () => {
13050
+ if (adoptNewerGeneration()) return true;
13051
+ const authed = await authenticateServerless(
13052
+ config,
13053
+ state,
13054
+ { ...renewal, reason: "session-expired" },
13055
+ onRefreshScheduled
13056
+ );
12636
13057
  if (authed && sendToken) sendToken();
12637
13058
  if (!authed && isPermanentAuthFailure(state.lastAuthError)) {
12638
13059
  const permanentError = state.lastAuthError;
12639
13060
  clearAuthenticationData(state);
12640
13061
  state.lastAuthError = permanentError;
12641
- clearStorage(config);
13062
+ clearStorage(config, state);
12642
13063
  } else if (!authed) {
12643
13064
  Object.assign(state, previousAuthentication);
12644
13065
  }
12645
13066
  return authed;
12646
13067
  };
13068
+ const retainSessionAfterTransientFailure = async (failure) => {
13069
+ if (adoptNewerGeneration()) return true;
13070
+ if (canReauthenticateAfterTransientFailure(config)) {
13071
+ return reauthenticateAfterRefreshFailure();
13072
+ }
13073
+ Object.assign(state, previousAuthentication);
13074
+ state.lastAuthError = failure;
13075
+ return false;
13076
+ };
12647
13077
  try {
12648
13078
  if (!presentedRefreshToken) {
12649
13079
  return reauthenticateAfterRefreshFailure();
12650
13080
  }
12651
- const response = await fetch(`${config.apiUrl}/${config.widgetId}/auth/refresh`, {
12652
- method: "POST",
12653
- headers: {
12654
- "Content-Type": "application/json",
12655
- Authorization: `Bearer ${presentedRefreshToken}`
12656
- },
12657
- body: JSON.stringify({
12658
- widgetType: config.widgetType,
12659
- ...buildIdentityProof(config)
12660
- })
12661
- });
13081
+ let response;
13082
+ try {
13083
+ response = await fetch(`${config.apiUrl}/${config.widgetId}/auth/refresh`, {
13084
+ method: "POST",
13085
+ headers: {
13086
+ "Content-Type": "application/json",
13087
+ Authorization: `Bearer ${presentedRefreshToken}`
13088
+ },
13089
+ body: JSON.stringify({
13090
+ widgetType: config.widgetType,
13091
+ ...buildIdentityProof(config)
13092
+ })
13093
+ });
13094
+ } catch (err) {
13095
+ return retainSessionAfterTransientFailure({
13096
+ status: 0,
13097
+ error: err instanceof Error ? err.message : "Token refresh failed"
13098
+ });
13099
+ }
12662
13100
  if (!response.ok) {
12663
- if (response.status === 400 || response.status === 401 || response.status === 403) {
13101
+ const error = await response.json().catch(() => ({ error: "Token refresh failed" }));
13102
+ const failure = {
13103
+ status: response.status,
13104
+ error: (error == null ? void 0 : error.error) || `HTTP ${response.status}`
13105
+ };
13106
+ if (isPermanentAuthFailure(failure)) {
12664
13107
  return reauthenticateAfterRefreshFailure();
12665
13108
  }
12666
- const error = await response.json().catch(() => ({ error: "Token refresh failed" }));
12667
- throw new Error(error.error || `HTTP ${response.status}`);
13109
+ return retainSessionAfterTransientFailure(failure);
12668
13110
  }
12669
13111
  const data = await response.json();
12670
13112
  if (!data.accessToken || !data.refreshToken) {
12671
13113
  throw new Error("Invalid refresh response");
12672
13114
  }
13115
+ state.lastAuthError = null;
12673
13116
  setTokens(config, state, data, onRefreshScheduled);
12674
13117
  state.isAuthenticated = true;
12675
13118
  if (sendToken) sendToken();
@@ -12683,19 +13126,17 @@ async function refreshAccessToken(config, state, onRefreshScheduled, sendToken)
12683
13126
  })();
12684
13127
  return state.refreshPromise;
12685
13128
  }
12686
- async function getValidToken(config, state, onRefreshScheduled) {
13129
+ async function getValidToken(config, state, renewal, onRefreshScheduled) {
12687
13130
  if (state.accessToken && !isTokenExpired(state.accessTokenExpiry)) {
12688
13131
  return state.accessToken;
12689
13132
  }
12690
13133
  if (config.authUrl) {
12691
- const authed2 = await authenticateViaProxy(config, state, onRefreshScheduled);
12692
- if (authed2 && state.accessToken) return state.accessToken;
13134
+ const authed = await authenticateViaProxy(config, state, onRefreshScheduled);
13135
+ if (authed && state.accessToken) return state.accessToken;
12693
13136
  throw new Error("Unable to obtain valid token");
12694
13137
  }
12695
- const refreshed = await refreshAccessToken(config, state, onRefreshScheduled);
13138
+ const refreshed = await refreshAccessToken(config, state, renewal, onRefreshScheduled);
12696
13139
  if (refreshed && state.accessToken) return state.accessToken;
12697
- const authed = await authenticateServerless(config, state, onRefreshScheduled);
12698
- if (authed && state.accessToken) return state.accessToken;
12699
13140
  throw new Error("Unable to obtain valid token");
12700
13141
  }
12701
13142
  async function logout(config, state) {
@@ -12713,7 +13154,7 @@ async function logout(config, state) {
12713
13154
  }
12714
13155
  }
12715
13156
  clearState(state);
12716
- clearStorage(config);
13157
+ clearStorage(config, state);
12717
13158
  }
12718
13159
  const inflightErrorSettings = /* @__PURE__ */ new Map();
12719
13160
  function applyErrorSettingsToState(state, data) {
@@ -12882,7 +13323,7 @@ function applySnapshot(subscriber, snapshot, notify = true) {
12882
13323
  var _a;
12883
13324
  Object.assign(subscriber.state, snapshot);
12884
13325
  if (snapshot.refreshToken) saveToStorage(subscriber.config, subscriber.state);
12885
- else clearStorage(subscriber.config);
13326
+ else clearStorage(subscriber.config, subscriber.state);
12886
13327
  if (!notify) return;
12887
13328
  try {
12888
13329
  (_a = subscriber.onSnapshot) == null ? void 0 : _a.call(subscriber);
@@ -12944,10 +13385,9 @@ function subscribeAuthSession(config, state, onSnapshot) {
12944
13385
  return inFlight;
12945
13386
  },
12946
13387
  publish() {
12947
- if (!released) publishSnapshot(session, state);
13388
+ publishSnapshot(session, state);
12948
13389
  },
12949
13390
  clear() {
12950
- if (released) return;
12951
13391
  state.accessToken = null;
12952
13392
  state.refreshToken = null;
12953
13393
  state.tokenFamily = null;
@@ -13327,321 +13767,127 @@ function syncBundleAccessToken(config, state, runtime = window) {
13327
13767
  return true;
13328
13768
  }
13329
13769
  function assertCallbackName(callbackName) {
13330
- if (!callbackName || typeof callbackName !== "string") {
13331
- throw new TypeError("ReturningAI callback name must be a non-empty string");
13332
- }
13333
- }
13334
- function assertCallback(callback) {
13335
- if (typeof callback !== "function") {
13336
- throw new TypeError("ReturningAI callback must be a function");
13337
- }
13338
- }
13339
- class WidgetCallbackRegistry {
13340
- constructor() {
13341
- __publicField(this, "registeredCallbacks", {});
13342
- __publicField(this, "locked", false);
13343
- }
13344
- get isLocked() {
13345
- return this.locked;
13346
- }
13347
- registerCallback(callbackName, callback) {
13348
- if (this.locked) {
13349
- return;
13350
- }
13351
- assertCallbackName(callbackName);
13352
- assertCallback(callback);
13353
- this.registeredCallbacks = {
13354
- ...this.registeredCallbacks,
13355
- [callbackName]: callback
13356
- };
13357
- }
13358
- lock() {
13359
- if (this.locked) {
13360
- return;
13361
- }
13362
- this.locked = true;
13363
- }
13364
- read() {
13365
- return { ...this.registeredCallbacks };
13366
- }
13367
- }
13368
- const loadedGoogleFontFamilies = /* @__PURE__ */ new Set();
13369
- const LOCAL_OR_SYSTEM_FONT_FAMILIES = /* @__PURE__ */ new Set([
13370
- "aeonik",
13371
- "courier",
13372
- "courier new",
13373
- "monaco",
13374
- "monospace",
13375
- "sans-serif",
13376
- "serif",
13377
- "system-ui"
13378
- ]);
13379
- const SERIF_FONT_FAMILIES = /* @__PURE__ */ new Set(["lora", "playfair display", "prata"]);
13380
- const GOOGLE_FONT_VARIANTS = ":ital,wght@0,300;0,400;0,500;0,600;0,700;1,400;1,700";
13381
- function normalizeFontFamily(family) {
13382
- return family.trim() === "Noto" ? "Noto Sans" : family.trim();
13383
- }
13384
- function getFontStack(family) {
13385
- const normalizedFamily = normalizeFontFamily(family);
13386
- if (normalizedFamily.includes(",")) return normalizedFamily;
13387
- const normalizedKey = normalizedFamily.toLowerCase();
13388
- switch (normalizedKey) {
13389
- case "monospace":
13390
- return "monospace";
13391
- case "courier":
13392
- case "courier new":
13393
- return '"Courier New", Courier, monospace';
13394
- case "monaco":
13395
- return "Monaco, Menlo, Consolas, monospace";
13396
- case "system-ui":
13397
- return "system-ui, -apple-system, sans-serif";
13398
- case "sans-serif":
13399
- case "serif":
13400
- return normalizedKey;
13401
- default:
13402
- return `'${normalizedFamily.replace(/[\\']/g, "\\\\$&")}', ${SERIF_FONT_FAMILIES.has(normalizedKey) ? "Georgia, 'Times New Roman', serif" : "system-ui, -apple-system, sans-serif"}`;
13403
- }
13404
- }
13405
- function ensureGoogleFontPreconnect() {
13406
- if (!document.getElementById("rai-fonts-preconnect")) {
13407
- const link = document.createElement("link");
13408
- link.id = "rai-fonts-preconnect";
13409
- link.rel = "preconnect";
13410
- link.href = "https://fonts.googleapis.com";
13411
- document.head.appendChild(link);
13412
- }
13413
- if (!document.getElementById("rai-fonts-static-preconnect")) {
13414
- const link = document.createElement("link");
13415
- link.id = "rai-fonts-static-preconnect";
13416
- link.rel = "preconnect";
13417
- link.href = "https://fonts.gstatic.com";
13418
- link.crossOrigin = "anonymous";
13419
- document.head.appendChild(link);
13420
- }
13421
- }
13422
- function ensureGoogleFontLoaded(family) {
13423
- const normalizedFamily = normalizeFontFamily(family);
13424
- const fontKey = normalizedFamily.toLowerCase();
13425
- if (!normalizedFamily || normalizedFamily.includes(",") || LOCAL_OR_SYSTEM_FONT_FAMILIES.has(fontKey)) {
13426
- return;
13427
- }
13428
- if (loadedGoogleFontFamilies.has(fontKey)) return;
13429
- ensureGoogleFontPreconnect();
13430
- const linkId = `rai-font-${normalizedFamily.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
13431
- const fontUrl = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(
13432
- normalizedFamily
13433
- )}${GOOGLE_FONT_VARIANTS}&display=swap`;
13434
- let link = document.getElementById(linkId);
13435
- if (link && link.href !== fontUrl) {
13436
- link.remove();
13437
- link = null;
13438
- }
13439
- if (!link) {
13440
- link = document.createElement("link");
13441
- link.id = linkId;
13442
- link.rel = "stylesheet";
13443
- link.href = fontUrl;
13444
- document.head.appendChild(link);
13445
- }
13446
- loadedGoogleFontFamilies.add(fontKey);
13447
- link.onerror = () => {
13448
- loadedGoogleFontFamilies.delete(fontKey);
13449
- link == null ? void 0 : link.remove();
13450
- };
13451
- }
13452
- const STORE_CALLBACK_FIELD_OPTIONS_SCOPE = "store.callback_field_options";
13453
- const STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME = "callbackFieldOptions";
13454
- const STORE_CALLBACK_FIELD_OPTIONS_TIMEOUT_MS = 15e3;
13455
- const STORE_PURCHASE_SUCCESS_SCOPE = "store.purchase_success";
13456
- const STORE_PURCHASE_SUCCESS_CALLBACK_NAME = "storePurchaseSuccess";
13457
- const MILESTONE_CTA_CLICK_SCOPE = "milestone.cta_click";
13458
- const MILESTONE_CTA_CLICK_CALLBACK_NAME = "milestoneCtaClick";
13459
- const MILESTONE_CTA_CLICK_EVENT = "milestone_cta_clicked";
13460
- function isRecord(value) {
13461
- return typeof value === "object" && value !== null;
13462
- }
13463
- function isStringRecord(value) {
13464
- return isRecord(value) && Object.values(value).every((item) => typeof item === "string");
13465
- }
13466
- function readEmbedTokenUserIdentifiers(embedToken) {
13467
- if (!embedToken) return null;
13468
- const encodedPayload = embedToken.split(".")[1];
13469
- if (!encodedPayload) return null;
13470
- try {
13471
- const normalizedPayload = encodedPayload.replace(/-/g, "+").replace(/_/g, "/");
13472
- const paddedPayload = normalizedPayload.padEnd(
13473
- Math.ceil(normalizedPayload.length / 4) * 4,
13474
- "="
13475
- );
13476
- const bytes = Uint8Array.from(atob(paddedPayload), (character) => character.charCodeAt(0));
13477
- const claims = JSON.parse(new TextDecoder().decode(bytes));
13478
- if (!isRecord(claims) || !isStringRecord(claims.userIdentifiers)) return null;
13479
- return { ...claims.userIdentifiers };
13480
- } catch {
13481
- return null;
13482
- }
13483
- }
13484
- function resolveHostUserIdentifiers(config) {
13485
- return readEmbedTokenUserIdentifiers(config.embedToken) ?? { ...config.userIdentifiers };
13486
- }
13487
- function hasCallbackContract(payload, callbackName, scope) {
13488
- return isRecord(payload) && payload.callbackName === callbackName && payload.scope === scope;
13489
- }
13490
- function isMilestoneCtaClickPayload(payload) {
13491
- if (!hasCallbackContract(
13492
- payload,
13493
- MILESTONE_CTA_CLICK_CALLBACK_NAME,
13494
- MILESTONE_CTA_CLICK_SCOPE
13495
- )) return false;
13496
- const context = payload.context;
13497
- if (!isRecord(context)) return false;
13498
- const community = context.community;
13499
- const milestone = context.milestone;
13500
- const stage = context.stage;
13501
- const button = context.button;
13502
- const user = context.user;
13503
- return context.event === MILESTONE_CTA_CLICK_EVENT && isRecord(community) && typeof community.id === "string" && community.id.length > 0 && (community.name === void 0 || community.name === null || typeof community.name === "string") && isRecord(milestone) && typeof milestone.id === "string" && milestone.id.length > 0 && typeof milestone.name === "string" && milestone.name.length > 0 && isRecord(stage) && typeof stage.id === "string" && stage.id.length > 0 && typeof stage.name === "string" && stage.name.length > 0 && isRecord(button) && typeof button.name === "string" && button.name.length > 0 && isRecord(user) && isStringRecord(user.identifiers) && typeof context.clickedAt === "string" && context.clickedAt.length > 0 && !Number.isNaN(Date.parse(context.clickedAt));
13504
- }
13505
- function enrichHostCallbackPayload(payload, userIdentifiers) {
13506
- if (!userIdentifiers || Object.keys(userIdentifiers).length === 0) return payload;
13507
- if (!isRecord(payload)) return payload;
13508
- const context = payload.context;
13509
- if (!isRecord(context)) return payload;
13510
- const user = context.user;
13511
- if (!isRecord(user)) return payload;
13512
- return {
13513
- ...payload,
13514
- context: {
13515
- ...context,
13516
- user: {
13517
- ...user,
13518
- identifiers: { ...userIdentifiers }
13519
- }
13520
- }
13521
- };
13522
- }
13523
- const HOST_CALLBACK_DEFINITIONS = {
13524
- [STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME]: {
13525
- callbackName: STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME,
13526
- scope: STORE_CALLBACK_FIELD_OPTIONS_SCOPE,
13527
- sourceWidgetType: "store",
13528
- allowedHostWidgetTypes: ["store", "custom"],
13529
- completionMode: "result",
13530
- missingHandler: "reject",
13531
- legacyActions: ["store:host-callback"],
13532
- validatePayload: (payload) => hasCallbackContract(
13533
- payload,
13534
- STORE_CALLBACK_FIELD_OPTIONS_CALLBACK_NAME,
13535
- STORE_CALLBACK_FIELD_OPTIONS_SCOPE
13536
- )
13537
- },
13538
- [STORE_PURCHASE_SUCCESS_CALLBACK_NAME]: {
13539
- callbackName: STORE_PURCHASE_SUCCESS_CALLBACK_NAME,
13540
- scope: STORE_PURCHASE_SUCCESS_SCOPE,
13541
- sourceWidgetType: "store",
13542
- allowedHostWidgetTypes: ["store", "custom"],
13543
- completionMode: "acknowledged",
13544
- missingHandler: "reject",
13545
- legacyActions: ["store:host-callback"],
13546
- validatePayload: (payload) => hasCallbackContract(
13547
- payload,
13548
- STORE_PURCHASE_SUCCESS_CALLBACK_NAME,
13549
- STORE_PURCHASE_SUCCESS_SCOPE
13550
- )
13551
- },
13552
- [MILESTONE_CTA_CLICK_CALLBACK_NAME]: {
13553
- callbackName: MILESTONE_CTA_CLICK_CALLBACK_NAME,
13554
- scope: MILESTONE_CTA_CLICK_SCOPE,
13555
- sourceWidgetType: "milestone",
13556
- allowedHostWidgetTypes: ["milestone", "custom"],
13557
- completionMode: "detached",
13558
- missingHandler: "ignore",
13559
- legacyActions: [],
13560
- validatePayload: isMilestoneCtaClickPayload
13770
+ if (!callbackName || typeof callbackName !== "string") {
13771
+ throw new TypeError("ReturningAI callback name must be a non-empty string");
13561
13772
  }
13562
- };
13563
- function createInvocationId() {
13564
- return `host_callback_${Date.now()}_${Math.random().toString(36).slice(2)}`;
13565
13773
  }
13566
- function observe(observer, event) {
13567
- if (!observer) return;
13568
- try {
13569
- observer(event);
13570
- } catch {
13774
+ function assertCallback(callback) {
13775
+ if (typeof callback !== "function") {
13776
+ throw new TypeError("ReturningAI callback must be a function");
13571
13777
  }
13572
13778
  }
13573
- function getHostCallbackDefinition(callbackName, payload) {
13574
- const definition = HOST_CALLBACK_DEFINITIONS[callbackName];
13575
- return (definition == null ? void 0 : definition.validatePayload(payload)) ? definition : void 0;
13779
+ class WidgetCallbackRegistry {
13780
+ constructor() {
13781
+ __publicField(this, "registeredCallbacks", {});
13782
+ __publicField(this, "locked", false);
13783
+ }
13784
+ get isLocked() {
13785
+ return this.locked;
13786
+ }
13787
+ registerCallback(callbackName, callback) {
13788
+ if (this.locked) {
13789
+ return;
13790
+ }
13791
+ assertCallbackName(callbackName);
13792
+ assertCallback(callback);
13793
+ this.registeredCallbacks = {
13794
+ ...this.registeredCallbacks,
13795
+ [callbackName]: callback
13796
+ };
13797
+ }
13798
+ lock() {
13799
+ if (this.locked) {
13800
+ return;
13801
+ }
13802
+ this.locked = true;
13803
+ }
13804
+ read() {
13805
+ return { ...this.registeredCallbacks };
13806
+ }
13576
13807
  }
13577
- function isHostCallbackMessageSupported(definition, action, sourceWidgetType, hostWidgetType) {
13578
- const actionSupported = action === "widget:host-callback" || definition.legacyActions.includes(action);
13579
- return actionSupported && sourceWidgetType === definition.sourceWidgetType && definition.allowedHostWidgetTypes.includes(hostWidgetType);
13808
+ const loadedGoogleFontFamilies = /* @__PURE__ */ new Set();
13809
+ const LOCAL_OR_SYSTEM_FONT_FAMILIES = /* @__PURE__ */ new Set([
13810
+ "aeonik",
13811
+ "courier",
13812
+ "courier new",
13813
+ "monaco",
13814
+ "monospace",
13815
+ "sans-serif",
13816
+ "serif",
13817
+ "system-ui"
13818
+ ]);
13819
+ const SERIF_FONT_FAMILIES = /* @__PURE__ */ new Set(["lora", "playfair display", "prata"]);
13820
+ const GOOGLE_FONT_VARIANTS = ":ital,wght@0,300;0,400;0,500;0,600;0,700;1,400;1,700";
13821
+ function normalizeFontFamily(family) {
13822
+ return family.trim() === "Noto" ? "Noto Sans" : family.trim();
13580
13823
  }
13581
- function dispatchHostCallback(input) {
13582
- const definition = getHostCallbackDefinition(input.callbackName, input.payload);
13583
- if (!definition || !definition.allowedHostWidgetTypes.includes(input.hostWidgetType)) {
13584
- throw new Error("Host callback is not supported for this widget");
13824
+ function getFontStack(family) {
13825
+ const normalizedFamily = normalizeFontFamily(family);
13826
+ if (normalizedFamily.includes(",")) return normalizedFamily;
13827
+ const normalizedKey = normalizedFamily.toLowerCase();
13828
+ switch (normalizedKey) {
13829
+ case "monospace":
13830
+ return "monospace";
13831
+ case "courier":
13832
+ case "courier new":
13833
+ return '"Courier New", Courier, monospace';
13834
+ case "monaco":
13835
+ return "Monaco, Menlo, Consolas, monospace";
13836
+ case "system-ui":
13837
+ return "system-ui, -apple-system, sans-serif";
13838
+ case "sans-serif":
13839
+ case "serif":
13840
+ return normalizedKey;
13841
+ default:
13842
+ return `'${normalizedFamily.replace(/[\\']/g, "\\\\$&")}', ${SERIF_FONT_FAMILIES.has(normalizedKey) ? "Georgia, 'Times New Roman', serif" : "system-ui, -apple-system, sans-serif"}`;
13585
13843
  }
13586
- const invocationId = input.invocationId || createInvocationId();
13587
- const eventBase = {
13588
- invocationId,
13589
- callbackName: definition.callbackName,
13590
- scope: definition.scope,
13591
- sourceWidgetType: definition.sourceWidgetType,
13592
- hostWidgetType: input.hostWidgetType,
13593
- completionMode: definition.completionMode,
13594
- transport: input.transport
13595
- };
13596
- observe(input.observer, { type: "callback.received", ...eventBase });
13597
- const callback = input.callbacks[definition.callbackName];
13598
- if (!callback) {
13599
- observe(input.observer, { type: "callback.handler_missing", ...eventBase });
13600
- if (definition.missingHandler === "ignore") return;
13601
- return Promise.reject(new Error("Host callback is not registered"));
13844
+ }
13845
+ function ensureGoogleFontPreconnect() {
13846
+ if (!document.getElementById("rai-fonts-preconnect")) {
13847
+ const link = document.createElement("link");
13848
+ link.id = "rai-fonts-preconnect";
13849
+ link.rel = "preconnect";
13850
+ link.href = "https://fonts.googleapis.com";
13851
+ document.head.appendChild(link);
13602
13852
  }
13603
- observe(input.observer, { type: "callback.handler_started", ...eventBase });
13604
- let result;
13605
- try {
13606
- result = callback(input.payload);
13607
- } catch (error) {
13608
- observe(input.observer, {
13609
- type: "callback.handler_rejected",
13610
- ...eventBase,
13611
- error
13612
- });
13613
- if (definition.completionMode === "detached") return;
13614
- return Promise.reject(new Error("Host callback failed"));
13853
+ if (!document.getElementById("rai-fonts-static-preconnect")) {
13854
+ const link = document.createElement("link");
13855
+ link.id = "rai-fonts-static-preconnect";
13856
+ link.rel = "preconnect";
13857
+ link.href = "https://fonts.gstatic.com";
13858
+ link.crossOrigin = "anonymous";
13859
+ document.head.appendChild(link);
13615
13860
  }
13616
- if (definition.completionMode === "detached") {
13617
- if (result && typeof result.then === "function") {
13618
- void Promise.resolve(result).then(
13619
- () => observe(input.observer, { type: "callback.handler_resolved", ...eventBase }),
13620
- (error) => observe(input.observer, {
13621
- type: "callback.handler_rejected",
13622
- ...eventBase,
13623
- error
13624
- })
13625
- );
13626
- } else {
13627
- observe(input.observer, { type: "callback.handler_resolved", ...eventBase });
13628
- }
13861
+ }
13862
+ function ensureGoogleFontLoaded(family) {
13863
+ const normalizedFamily = normalizeFontFamily(family);
13864
+ const fontKey = normalizedFamily.toLowerCase();
13865
+ if (!normalizedFamily || normalizedFamily.includes(",") || LOCAL_OR_SYSTEM_FONT_FAMILIES.has(fontKey)) {
13629
13866
  return;
13630
13867
  }
13631
- return Promise.resolve(result).then(
13632
- (value) => {
13633
- observe(input.observer, { type: "callback.handler_resolved", ...eventBase });
13634
- return definition.completionMode === "result" ? value : void 0;
13635
- },
13636
- (error) => {
13637
- observe(input.observer, {
13638
- type: "callback.handler_rejected",
13639
- ...eventBase,
13640
- error
13641
- });
13642
- throw new Error("Host callback failed");
13643
- }
13644
- );
13868
+ if (loadedGoogleFontFamilies.has(fontKey)) return;
13869
+ ensureGoogleFontPreconnect();
13870
+ const linkId = `rai-font-${normalizedFamily.toLowerCase().replace(/[^a-z0-9]+/g, "-")}`;
13871
+ const fontUrl = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(
13872
+ normalizedFamily
13873
+ )}${GOOGLE_FONT_VARIANTS}&display=swap`;
13874
+ let link = document.getElementById(linkId);
13875
+ if (link && link.href !== fontUrl) {
13876
+ link.remove();
13877
+ link = null;
13878
+ }
13879
+ if (!link) {
13880
+ link = document.createElement("link");
13881
+ link.id = linkId;
13882
+ link.rel = "stylesheet";
13883
+ link.href = fontUrl;
13884
+ document.head.appendChild(link);
13885
+ }
13886
+ loadedGoogleFontFamilies.add(fontKey);
13887
+ link.onerror = () => {
13888
+ loadedGoogleFontFamilies.delete(fontKey);
13889
+ link == null ? void 0 : link.remove();
13890
+ };
13645
13891
  }
13646
13892
  const SUPPORTED_WIDGET_LANGUAGES = [
13647
13893
  "en",
@@ -13779,15 +14025,36 @@ function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled
13779
14025
  emit == null ? void 0 : emit("rai-height-change", { height: h });
13780
14026
  }, config.heightDebounce ?? 100);
13781
14027
  const handler = async (event) => {
13782
- var _a;
14028
+ var _a, _b;
13783
14029
  if (event.origin !== config.widgetDomain) return;
13784
14030
  if (!event.data || typeof event.data.type !== "string") return;
13785
14031
  const { type, containerId, widgetId, payload } = event.data;
13786
14032
  switch (type) {
14033
+ case "RETURNINGAI_WIDGET_REQUEST_CALLBACK_CAPABILITIES": {
14034
+ if (event.source !== iframe.contentWindow) break;
14035
+ if (widgetId !== void 0 && widgetId !== config.widgetId) break;
14036
+ if (containerId !== void 0 && containerId !== config.container) break;
14037
+ const requestId = event.data.requestId;
14038
+ if (typeof requestId !== "string" || !requestId || requestId.length > 256) break;
14039
+ (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage({
14040
+ type: "RETURNINGAI_WIDGET_CALLBACK_CAPABILITIES",
14041
+ requestId,
14042
+ supportedCallbacks: getSupportedHostCallbacks((getCallbacks == null ? void 0 : getCallbacks()) ?? {}, config.widgetType)
14043
+ }, config.widgetDomain);
14044
+ break;
14045
+ }
13787
14046
  case "RETURNINGAI_WIDGET_REQUEST_TOKEN": {
13788
14047
  try {
13789
- const token = await getValidToken(config, state, onRefreshScheduled);
13790
- (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
14048
+ const token = await getValidToken(
14049
+ config,
14050
+ state,
14051
+ {
14052
+ getEmbedTokenProvider: () => readEmbedTokenProvider(getCallbacks == null ? void 0 : getCallbacks()),
14053
+ isCurrent: () => iframe.isConnected
14054
+ },
14055
+ onRefreshScheduled
14056
+ );
14057
+ (_b = iframe.contentWindow) == null ? void 0 : _b.postMessage(
13791
14058
  {
13792
14059
  type: "RETURNINGAI_WIDGET_TOKEN",
13793
14060
  value: {
@@ -14238,13 +14505,6 @@ function readConfig(el2, existingId) {
14238
14505
  firstPaintFont: get("font") || void 0
14239
14506
  };
14240
14507
  }
14241
- function identitySignatureForConfig(config) {
14242
- const identifiers = Object.keys(config.userIdentifiers || {}).sort().map((key) => [key, config.userIdentifiers[key]]);
14243
- return JSON.stringify({
14244
- embedToken: config.embedToken || "",
14245
- userIdentifiers: identifiers
14246
- });
14247
- }
14248
14508
  function createInitialState() {
14249
14509
  return {
14250
14510
  accessToken: null,
@@ -14268,6 +14528,17 @@ function createInitialState() {
14268
14528
  widgetData: null
14269
14529
  };
14270
14530
  }
14531
+ function forkWidgetState(previous) {
14532
+ return {
14533
+ ...createInitialState(),
14534
+ errorSettings: previous.errorSettings ?? null,
14535
+ loadingAnimation: previous.loadingAnimation ?? null,
14536
+ loadingSettings: previous.loadingSettings ?? null,
14537
+ themePalette: previous.themePalette ?? null,
14538
+ font: previous.font ?? null,
14539
+ defaultColorSchema: previous.defaultColorSchema ?? null
14540
+ };
14541
+ }
14271
14542
  class BaseWidget extends HTMLElement {
14272
14543
  constructor() {
14273
14544
  super();
@@ -14285,6 +14556,14 @@ class BaseWidget extends HTMLElement {
14285
14556
  __publicField(this, "themeObserver");
14286
14557
  __publicField(this, "identityAttributeObserver");
14287
14558
  __publicField(this, "identitySignature", "");
14559
+ /** The `embed-token` attribute as last read; `config.embedToken` may hold a renewed pass instead. */
14560
+ __publicField(this, "lastEmbedTokenAttribute");
14561
+ /** The init generation whose widget launched; part of the silent-swap health check. */
14562
+ __publicField(this, "launchedInitId", 0);
14563
+ /** Set when the expired-pass error was shown with no `embedTokenRequest` registered. */
14564
+ __publicField(this, "lateEmbedTokenRegistrationReload", false);
14565
+ /** An error screen is showing for the current init (any cause); clears on the next launch. */
14566
+ __publicField(this, "errorScreenShown", false);
14288
14567
  __publicField(this, "hasAttemptedInit", false);
14289
14568
  __publicField(this, "initGeneration", 0);
14290
14569
  __publicField(this, "lottieInstances", []);
@@ -14314,6 +14593,10 @@ class BaseWidget extends HTMLElement {
14314
14593
  }
14315
14594
  registerCallback(callbackName, callback) {
14316
14595
  this.callbackRegistry.registerCallback(callbackName, callback);
14596
+ if (callbackName === "embedTokenRequest" && this.lateEmbedTokenRegistrationReload && !this.callbackRegistry.isLocked && this.readEmbedTokenProvider() && isEmbedTokenRejected(this.state.lastAuthError) && this.isConnected) {
14597
+ this.lateEmbedTokenRegistrationReload = false;
14598
+ void this.reload();
14599
+ }
14317
14600
  }
14318
14601
  lockCallbacks() {
14319
14602
  this.callbackRegistry.lock();
@@ -14321,6 +14604,7 @@ class BaseWidget extends HTMLElement {
14321
14604
  connectedCallback() {
14322
14605
  this.config = readConfig(this, void 0);
14323
14606
  this.identitySignature = identitySignatureForConfig(this.config);
14607
+ this.lastEmbedTokenAttribute = this.config.embedToken;
14324
14608
  this.setupIdentityAttributeObserver();
14325
14609
  ensureWidgetPreconnects(
14326
14610
  resolveWidgetPreconnectOrigins(this.config, document.baseURI)
@@ -14394,7 +14678,22 @@ class BaseWidget extends HTMLElement {
14394
14678
  if (!this.isConnected || !this.config) return;
14395
14679
  const nextConfig = readConfig(this, this.config.communityId);
14396
14680
  const nextIdentitySignature = identitySignatureForConfig(nextConfig);
14397
- if (nextIdentitySignature === this.identitySignature) {
14681
+ const tokenChanged = nextConfig.embedToken !== this.lastEmbedTokenAttribute;
14682
+ const sameSignature = nextIdentitySignature === this.identitySignature;
14683
+ const decision = decideEmbedTokenChange({
14684
+ tokenChanged,
14685
+ sameSignature,
14686
+ hasAttemptedInit: this.hasAttemptedInit,
14687
+ healthy: this.isHealthyForEmbedTokenSwap(),
14688
+ hasProvider: !!this.readEmbedTokenProvider()
14689
+ });
14690
+ this.lastEmbedTokenAttribute = nextConfig.embedToken;
14691
+ if (decision === "swap") {
14692
+ this.config.embedToken = nextConfig.embedToken;
14693
+ return;
14694
+ }
14695
+ if (decision === "update" && sameSignature) {
14696
+ if (!tokenChanged) nextConfig.embedToken = this.config.embedToken;
14398
14697
  this.config = nextConfig;
14399
14698
  return;
14400
14699
  }
@@ -14403,10 +14702,28 @@ class BaseWidget extends HTMLElement {
14403
14702
  this.identitySignature = nextIdentitySignature;
14404
14703
  clearStorage(previousConfig);
14405
14704
  clearStorage(this.config);
14406
- if (this.hasAttemptedInit) {
14705
+ if (decision === "reload") {
14407
14706
  void this.reload();
14408
14707
  }
14409
14708
  }
14709
+ readEmbedTokenProvider() {
14710
+ return readEmbedTokenProvider(this.callbackRegistry.read());
14711
+ }
14712
+ /**
14713
+ * Renewal options for auth calls started by init `initId`; the provider is
14714
+ * read at call time. A disconnect or a newer init stops the retry. Writes
14715
+ * from this call land only in the state object init `initId` owns.
14716
+ */
14717
+ embedTokenRenewal(initId) {
14718
+ return {
14719
+ getEmbedTokenProvider: () => this.readEmbedTokenProvider(),
14720
+ isCurrent: () => this.isCurrentInit(initId)
14721
+ };
14722
+ }
14723
+ /** Authenticated, no error state or error screen, and launched for the current init. */
14724
+ isHealthyForEmbedTokenSwap() {
14725
+ return this.state.isAuthenticated && !this.state.lastAuthError && !this.errorScreenShown && this.launchedInitId !== 0 && this.launchedInitId === this.initGeneration;
14726
+ }
14410
14727
  // ── DOM event helper (#1) ─────────────────────────────────────────────
14411
14728
  emit(type, detail) {
14412
14729
  this.dispatchEvent(
@@ -14596,10 +14913,15 @@ class BaseWidget extends HTMLElement {
14596
14913
  this.renderLoaderAnimation();
14597
14914
  return;
14598
14915
  }
14916
+ const target = this.state;
14599
14917
  try {
14600
- await fetchErrorSettings(this.config, this.state);
14918
+ await fetchErrorSettings(this.config, target);
14601
14919
  } catch {
14602
14920
  }
14921
+ if (target !== this.state) {
14922
+ const fetched = loadErrorSettingsFromStorage(this.config);
14923
+ if (fetched) applyErrorSettingsToState(this.state, fetched);
14924
+ }
14603
14925
  if (!loaderEl || loaderEl !== this.loaderEl || !this.isConnected) return;
14604
14926
  if (!this.config.themeExplicit && (this.state.defaultColorSchema === "light" || this.state.defaultColorSchema === "dark") && this.state.defaultColorSchema !== this.config.theme) {
14605
14927
  this.config.theme = this.state.defaultColorSchema;
@@ -14808,6 +15130,8 @@ class BaseWidget extends HTMLElement {
14808
15130
  }
14809
15131
  showError(failureKind = "authentication") {
14810
15132
  this.hideLoader();
15133
+ this.errorScreenShown = true;
15134
+ this.lateEmbedTokenRegistrationReload = failureKind === "authentication" && isEmbedTokenRejected(this.state.lastAuthError) && !this.readEmbedTokenProvider();
14811
15135
  const settings = this.state.errorSettings;
14812
15136
  const presentation = selectWidgetErrorPresentation({
14813
15137
  failureKind,
@@ -14978,29 +15302,61 @@ class BaseWidget extends HTMLElement {
14978
15302
  }
14979
15303
  }
14980
15304
  // ── Initialization ─────────────────────────────────────────────────────
15305
+ /**
15306
+ * Starts a new init: it owns a fresh state object and its own auth-session
15307
+ * subscription. The previous init's in-flight work keeps the retired state,
15308
+ * so it can finish but never writes this init's state or storage. It still
15309
+ * publishes to the session it started on; after a same-member reload that is
15310
+ * the same shared session this init joined, which is intended (siblings and
15311
+ * this init get the result).
15312
+ */
15313
+ beginInit() {
15314
+ const initId = ++this.initGeneration;
15315
+ const retired = this.state;
15316
+ if (retired.refreshTimer) clearTimeout(retired.refreshTimer);
15317
+ if (retired.syncTimer) clearInterval(retired.syncTimer);
15318
+ retired.refreshTimer = null;
15319
+ retired.syncTimer = null;
15320
+ retireWidgetState(retired);
15321
+ const state = forkWidgetState(retired);
15322
+ this.state = state;
15323
+ const previousSession = this.authSession;
15324
+ this.authSession = void 0;
15325
+ if (this.config.communityId && !this.config.authUrl) {
15326
+ let subscribing = true;
15327
+ this.authSession = subscribeAuthSession(this.config, state, () => {
15328
+ if (this.state !== state) return;
15329
+ if (state.isAuthenticated && !state.lastAuthError) {
15330
+ this.authRenewalRecovery.succeeded();
15331
+ }
15332
+ if (this.config.autoRefresh && !state.lastAuthError) {
15333
+ if (!subscribing) {
15334
+ this.scheduleRefresh();
15335
+ } else if (state.accessTokenExpiry && calculateRefreshDelay(state.accessTokenExpiry) > 0) {
15336
+ this.scheduleRefresh();
15337
+ }
15338
+ }
15339
+ if (state.iframe) {
15340
+ sendTokenToWidget(this.config, state, state.iframe);
15341
+ }
15342
+ syncBundleAccessToken(this.config, state);
15343
+ });
15344
+ subscribing = false;
15345
+ }
15346
+ previousSession == null ? void 0 : previousSession.release();
15347
+ return initId;
15348
+ }
14981
15349
  async init() {
14982
15350
  var _a, _b;
14983
15351
  (_a = this.cancelBundleForegroundRecovery) == null ? void 0 : _a.call(this);
14984
15352
  this.cancelBundleForegroundRecovery = void 0;
14985
15353
  this.authRenewalRecovery.cancel();
14986
- const initId = ++this.initGeneration;
15354
+ const initId = this.beginInit();
14987
15355
  this.hasAttemptedInit = true;
14988
15356
  if (!this.config.communityId) {
14989
15357
  this.showError();
14990
15358
  return;
14991
15359
  }
14992
- if (!this.config.authUrl && !this.authSession) {
14993
- this.authSession = subscribeAuthSession(this.config, this.state, () => {
14994
- if (this.state.isAuthenticated && !this.state.lastAuthError) {
14995
- this.authRenewalRecovery.succeeded();
14996
- }
14997
- if (this.config.autoRefresh) this.scheduleRefresh();
14998
- if (this.state.iframe) {
14999
- sendTokenToWidget(this.config, this.state, this.state.iframe);
15000
- }
15001
- syncBundleAccessToken(this.config, this.state);
15002
- });
15003
- }
15004
15360
  if (this.config.bundleUrl) {
15005
15361
  this.prepareBundleRuntimeEnvironment();
15006
15362
  warmBundleRuntime(
@@ -15008,6 +15364,7 @@ class BaseWidget extends HTMLElement {
15008
15364
  this.config.bundleUrl
15009
15365
  );
15010
15366
  }
15367
+ const renewal = this.embedTokenRenewal(initId);
15011
15368
  const finishStartup = (authentication) => resolveStartupAuthentication(
15012
15369
  authentication,
15013
15370
  (options) => fetchErrorSettings(this.config, this.state, options)
@@ -15015,6 +15372,9 @@ class BaseWidget extends HTMLElement {
15015
15372
  const launchWidget = () => {
15016
15373
  var _a2;
15017
15374
  if (!this.isCurrentInit(initId)) return;
15375
+ this.launchedInitId = initId;
15376
+ this.lateEmbedTokenRegistrationReload = false;
15377
+ this.errorScreenShown = false;
15018
15378
  (_a2 = this.errorEl) == null ? void 0 : _a2.classList.remove("visible");
15019
15379
  this.shadow.querySelectorAll(".rai-custom-error").forEach((el2) => el2.remove());
15020
15380
  let bannerGated = false;
@@ -15038,11 +15398,9 @@ class BaseWidget extends HTMLElement {
15038
15398
  };
15039
15399
  if (this.config.authUrl) {
15040
15400
  const authed2 = await finishStartup(
15041
- authenticateViaProxy(
15042
- this.config,
15043
- this.state,
15044
- () => this.scheduleRefresh()
15045
- )
15401
+ authenticateViaProxy(this.config, this.state, () => {
15402
+ if (initId === this.initGeneration) this.scheduleRefresh();
15403
+ })
15046
15404
  );
15047
15405
  if (!this.isCurrentInit(initId)) return;
15048
15406
  if (authed2) {
@@ -15059,10 +15417,11 @@ class BaseWidget extends HTMLElement {
15059
15417
  const authed2 = await finishStartup(
15060
15418
  this.authSession ? this.authSession.run(
15061
15419
  "authenticate",
15062
- () => authenticateServerless(this.config, this.state)
15420
+ () => authenticateServerless(this.config, this.state, renewal)
15063
15421
  ) : authenticateServerless(
15064
15422
  this.config,
15065
15423
  this.state,
15424
+ renewal,
15066
15425
  () => this.scheduleRefresh()
15067
15426
  )
15068
15427
  );
@@ -15089,10 +15448,11 @@ class BaseWidget extends HTMLElement {
15089
15448
  const refreshed = await finishStartup(
15090
15449
  this.authSession ? this.authSession.run(
15091
15450
  "refresh",
15092
- () => refreshAccessToken(this.config, this.state)
15451
+ () => refreshAccessToken(this.config, this.state, renewal)
15093
15452
  ) : refreshAccessToken(
15094
15453
  this.config,
15095
15454
  this.state,
15455
+ renewal,
15096
15456
  () => this.scheduleRefresh()
15097
15457
  )
15098
15458
  );
@@ -15109,10 +15469,11 @@ class BaseWidget extends HTMLElement {
15109
15469
  const authed = await finishStartup(
15110
15470
  this.authSession ? this.authSession.run(
15111
15471
  "authenticate",
15112
- () => authenticateServerless(this.config, this.state)
15472
+ () => authenticateServerless(this.config, this.state, renewal)
15113
15473
  ) : authenticateServerless(
15114
15474
  this.config,
15115
15475
  this.state,
15476
+ renewal,
15116
15477
  () => this.scheduleRefresh()
15117
15478
  )
15118
15479
  );
@@ -15154,6 +15515,8 @@ class BaseWidget extends HTMLElement {
15154
15515
  }
15155
15516
  createIframe() {
15156
15517
  this.teardownMountedWidget();
15518
+ const session = this.authSession;
15519
+ const initId = this.initGeneration;
15157
15520
  const iframe = document.createElement("iframe");
15158
15521
  iframe.src = this.buildWidgetUrl(this.config);
15159
15522
  iframe.allow = "clipboard-write";
@@ -15181,7 +15544,14 @@ class BaseWidget extends HTMLElement {
15181
15544
  this.state,
15182
15545
  this.shadow,
15183
15546
  iframe,
15184
- () => this.scheduleRefresh(),
15547
+ // A token the iframe asks for can renew the session outside the shared
15548
+ // auth session. Publish it: the next session refresh would otherwise
15549
+ // copy the old snapshot back and present a rotated-away refresh token,
15550
+ // which the server treats as reuse once its 60 s grace has passed.
15551
+ () => {
15552
+ if (session) session.publish();
15553
+ else if (initId === this.initGeneration) this.scheduleRefresh();
15554
+ },
15185
15555
  () => this.logoutAndClear(),
15186
15556
  () => this.hideLoader(),
15187
15557
  (type, detail) => this.emit(type, detail),
@@ -15286,7 +15656,7 @@ class BaseWidget extends HTMLElement {
15286
15656
  // host page. Bundle consumers use this explicit contract instead of
15287
15657
  // inferring the SDK host element from their rendered DOM.
15288
15658
  autoHeight,
15289
- requestHostCallback: (callbackName, payload) => dispatchHostCallback({
15659
+ requestHostCallback: Object.defineProperty((callbackName, payload) => dispatchHostCallback({
15290
15660
  callbacks: this.readRuntimeCallbacks(),
15291
15661
  callbackName,
15292
15662
  payload: enrichHostCallbackPayload(
@@ -15295,6 +15665,8 @@ class BaseWidget extends HTMLElement {
15295
15665
  ),
15296
15666
  hostWidgetType: this.config.widgetType,
15297
15667
  transport: "bundle"
15668
+ }), "supportedCallbacks", {
15669
+ get: () => getSupportedHostCallbacks(this.readRuntimeCallbacks(), this.config.widgetType)
15298
15670
  })
15299
15671
  });
15300
15672
  const resolvedMountResult = await resolveBundleMountResult(mountResult);
@@ -15361,30 +15733,29 @@ class BaseWidget extends HTMLElement {
15361
15733
  }
15362
15734
  async performScheduledRefresh() {
15363
15735
  this.state.refreshTimer = null;
15736
+ const initId = this.initGeneration;
15737
+ const { state, config, authSession } = this;
15738
+ const current = () => initId === this.initGeneration;
15364
15739
  const pushToken = () => {
15365
- if (this.state.iframe)
15366
- sendTokenToWidget(this.config, this.state, this.state.iframe);
15367
- syncBundleAccessToken(this.config, this.state);
15740
+ if (!current()) return;
15741
+ if (state.iframe) sendTokenToWidget(config, state, state.iframe);
15742
+ syncBundleAccessToken(config, state);
15743
+ };
15744
+ const rearm = () => {
15745
+ if (current()) this.scheduleRefresh();
15368
15746
  };
15369
15747
  let ok;
15370
- if (this.config.authUrl) {
15371
- ok = await authenticateViaProxy(
15372
- this.config,
15373
- this.state,
15374
- () => this.scheduleRefresh()
15375
- );
15748
+ if (config.authUrl) {
15749
+ ok = await authenticateViaProxy(config, state, rearm);
15376
15750
  if (ok) pushToken();
15377
15751
  } else {
15378
- ok = this.authSession ? await this.authSession.run(
15752
+ const renewal = this.embedTokenRenewal(initId);
15753
+ ok = authSession ? await authSession.run(
15379
15754
  "refresh",
15380
- () => refreshAccessToken(this.config, this.state)
15381
- ) : await refreshAccessToken(
15382
- this.config,
15383
- this.state,
15384
- () => this.scheduleRefresh(),
15385
- pushToken
15386
- );
15755
+ () => refreshAccessToken(config, state, renewal)
15756
+ ) : await refreshAccessToken(config, state, renewal, rearm, pushToken);
15387
15757
  }
15758
+ if (initId !== this.initGeneration) return;
15388
15759
  if (ok) {
15389
15760
  this.authRenewalRecovery.succeeded();
15390
15761
  } else if (isPermanentAuthFailure(this.state.lastAuthError)) {
@@ -15427,12 +15798,15 @@ class BaseWidget extends HTMLElement {
15427
15798
  }
15428
15799
  // ── Logout helper ─────────────────────────────────────────────────────
15429
15800
  async logoutAndClear() {
15430
- var _a;
15801
+ const initId = this.initGeneration;
15802
+ const session = this.authSession;
15431
15803
  this.authRenewalRecovery.cancel();
15432
15804
  await logout(this.config, this.state);
15433
- (_a = this.authSession) == null ? void 0 : _a.clear();
15434
- this.teardownMountedWidget();
15435
- this.removeBanner();
15805
+ session == null ? void 0 : session.clear();
15806
+ if (initId === this.initGeneration) {
15807
+ this.teardownMountedWidget();
15808
+ this.removeBanner();
15809
+ }
15436
15810
  this.emit("rai-logout");
15437
15811
  }
15438
15812
  // ── Public API ────────────────────────────────────────────────────────
@@ -15444,13 +15818,13 @@ class BaseWidget extends HTMLElement {
15444
15818
  this.authSession = void 0;
15445
15819
  this.config = readConfig(this, (_b = this.config) == null ? void 0 : _b.communityId);
15446
15820
  this.identitySignature = identitySignatureForConfig(this.config);
15821
+ this.lastEmbedTokenAttribute = this.config.embedToken;
15447
15822
  this.style.width = this.config.width;
15448
15823
  this.style.height = this.config.height;
15449
15824
  clearStorage(previousConfig);
15450
15825
  clearStorage(this.config);
15451
15826
  this.teardownMountedWidget();
15452
15827
  this.removeBanner();
15453
- clearState(this.state);
15454
15828
  this.applyFirstPaintColors();
15455
15829
  this.showLoader();
15456
15830
  await this.init();
@@ -15588,7 +15962,7 @@ class CustomWidget extends BaseWidget {
15588
15962
  }
15589
15963
  }
15590
15964
  }
15591
- console.log(`[rai-widget] v${"1.8.13"}`);
15965
+ console.log(`[rai-widget] v${"1.8.14"}`);
15592
15966
  class StoreWidgetCompat extends StoreWidget {
15593
15967
  }
15594
15968
  const WIDGET_REGISTRY = [
@@ -15652,7 +16026,7 @@ function exposePublicApi() {
15652
16026
  })();
15653
16027
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
15654
16028
  window.ReturningAIWidget = {
15655
- version: "1.8.13",
16029
+ version: "1.8.14",
15656
16030
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
15657
16031
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
15658
16032
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,