@returningai/widget-sdk 1.4.0 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -10336,6 +10336,9 @@ var lottie_light = { exports: {} };
10336
10336
  var lottie_lightExports = lottie_light.exports;
10337
10337
  const lottie = /* @__PURE__ */ getDefaultExportFromCjs(lottie_lightExports);
10338
10338
  function storageKey(config) {
10339
+ return `${config.storagePrefix}-${config.widgetId}-${config.container}-auth`;
10340
+ }
10341
+ function legacyStorageKey(config) {
10339
10342
  return `${config.storagePrefix}-${config.widgetId}-auth`;
10340
10343
  }
10341
10344
  function errorSettingsKey(config) {
@@ -10354,13 +10357,21 @@ function saveToStorage(config, state) {
10354
10357
  }
10355
10358
  function loadFromStorage(config, state) {
10356
10359
  try {
10357
- const raw = localStorage.getItem(storageKey(config));
10360
+ let raw = localStorage.getItem(storageKey(config));
10361
+ const loadedLegacyKey = !raw;
10362
+ if (!raw) {
10363
+ raw = localStorage.getItem(legacyStorageKey(config));
10364
+ }
10358
10365
  if (!raw) return false;
10359
10366
  const parsed = JSON.parse(raw);
10360
10367
  if (parsed.refreshToken && !isTokenExpired(parsed.refreshTokenExpiry)) {
10361
10368
  state.refreshToken = parsed.refreshToken;
10362
10369
  state.tokenFamily = parsed.tokenFamily;
10363
10370
  state.refreshTokenExpiry = parsed.refreshTokenExpiry;
10371
+ if (loadedLegacyKey) {
10372
+ saveToStorage(config, state);
10373
+ localStorage.removeItem(legacyStorageKey(config));
10374
+ }
10364
10375
  return true;
10365
10376
  }
10366
10377
  clearStorage(config);
@@ -10372,6 +10383,7 @@ function loadFromStorage(config, state) {
10372
10383
  function clearStorage(config) {
10373
10384
  try {
10374
10385
  localStorage.removeItem(storageKey(config));
10386
+ localStorage.removeItem(legacyStorageKey(config));
10375
10387
  } catch {
10376
10388
  }
10377
10389
  }
@@ -10412,6 +10424,16 @@ function setTokens(config, state, data, onRefreshScheduled) {
10412
10424
  onRefreshScheduled();
10413
10425
  }
10414
10426
  }
10427
+ function buildIdentityProof(config) {
10428
+ const proof = {};
10429
+ if (config.embedToken) {
10430
+ proof.embedToken = config.embedToken;
10431
+ }
10432
+ if (config.userIdentifiers && Object.keys(config.userIdentifiers).length > 0) {
10433
+ proof.userIdentifiers = config.userIdentifiers;
10434
+ }
10435
+ return proof;
10436
+ }
10415
10437
  async function authenticateServerless(config, state, onRefreshScheduled) {
10416
10438
  const nonce = crypto.randomUUID();
10417
10439
  const timestamp = Date.now();
@@ -10428,14 +10450,9 @@ async function authenticateServerless(config, state, onRefreshScheduled) {
10428
10450
  const body = {
10429
10451
  nonce,
10430
10452
  timestamp,
10431
- widgetType: config.widgetType
10453
+ widgetType: config.widgetType,
10454
+ ...buildIdentityProof(config)
10432
10455
  };
10433
- if (config.embedToken) {
10434
- body.embedToken = config.embedToken;
10435
- }
10436
- if (config.userIdentifiers && Object.keys(config.userIdentifiers).length > 0) {
10437
- body.userIdentifiers = config.userIdentifiers;
10438
- }
10439
10456
  const response = await fetch(`${config.apiUrl}/${config.widgetId}/auth/serverless`, {
10440
10457
  method: "POST",
10441
10458
  headers: { "Content-Type": "application/json" },
@@ -10531,6 +10548,13 @@ async function refreshAccessToken(config, state, onRefreshScheduled, sendToken)
10531
10548
  }
10532
10549
  state.isRefreshing = true;
10533
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
+ };
10534
10558
  try {
10535
10559
  const response = await fetch(`${config.apiUrl}/${config.widgetId}/auth/refresh`, {
10536
10560
  method: "POST",
@@ -10538,13 +10562,14 @@ async function refreshAccessToken(config, state, onRefreshScheduled, sendToken)
10538
10562
  "Content-Type": "application/json",
10539
10563
  Authorization: `Bearer ${state.refreshToken}`
10540
10564
  },
10541
- body: JSON.stringify({ widgetType: config.widgetType })
10565
+ body: JSON.stringify({
10566
+ widgetType: config.widgetType,
10567
+ ...buildIdentityProof(config)
10568
+ })
10542
10569
  });
10543
10570
  if (!response.ok) {
10544
- if (response.status === 401 || response.status === 403) {
10545
- clearState(state);
10546
- clearStorage(config);
10547
- return authenticateServerless(config, state, onRefreshScheduled);
10571
+ if (response.status === 400 || response.status === 401 || response.status === 403) {
10572
+ return reauthenticateAfterRefreshFailure();
10548
10573
  }
10549
10574
  const error = await response.json().catch(() => ({ error: "Token refresh failed" }));
10550
10575
  throw new Error(error.error || `HTTP ${response.status}`);
@@ -10557,9 +10582,7 @@ async function refreshAccessToken(config, state, onRefreshScheduled, sendToken)
10557
10582
  if (sendToken) sendToken();
10558
10583
  return true;
10559
10584
  } catch {
10560
- clearState(state);
10561
- clearStorage(config);
10562
- return authenticateServerless(config, state, onRefreshScheduled);
10585
+ return reauthenticateAfterRefreshFailure();
10563
10586
  } finally {
10564
10587
  state.isRefreshing = false;
10565
10588
  state.refreshPromise = null;
@@ -11030,6 +11053,13 @@ function readConfig(el, existingId) {
11030
11053
  firstPaintFont: get("font") || void 0
11031
11054
  };
11032
11055
  }
11056
+ function identitySignatureForConfig(config) {
11057
+ const identifiers = Object.keys(config.userIdentifiers || {}).sort().map((key) => [key, config.userIdentifiers[key]]);
11058
+ return JSON.stringify({
11059
+ embedToken: config.embedToken || "",
11060
+ userIdentifiers: identifiers
11061
+ });
11062
+ }
11033
11063
  function createInitialState() {
11034
11064
  return {
11035
11065
  accessToken: null,
@@ -11064,11 +11094,15 @@ class BaseWidget extends HTMLElement {
11064
11094
  __publicField(this, "intersectionObserver");
11065
11095
  // #3
11066
11096
  __publicField(this, "themeObserver");
11097
+ __publicField(this, "authAttributeObserver");
11098
+ __publicField(this, "identitySignature", "");
11067
11099
  __publicField(this, "lottieInstances", []);
11068
11100
  this.shadow = this.attachShadow({ mode: "closed" });
11069
11101
  }
11070
11102
  connectedCallback() {
11071
11103
  this.config = readConfig(this, void 0);
11104
+ this.identitySignature = identitySignatureForConfig(this.config);
11105
+ this.setupAuthAttributeObserver();
11072
11106
  const style = document.createElement("style");
11073
11107
  style.textContent = widgetCSS;
11074
11108
  this.shadow.appendChild(style);
@@ -11098,13 +11132,36 @@ class BaseWidget extends HTMLElement {
11098
11132
  }
11099
11133
  }
11100
11134
  disconnectedCallback() {
11101
- var _a, _b;
11135
+ var _a, _b, _c;
11102
11136
  (_a = this.intersectionObserver) == null ? void 0 : _a.disconnect();
11103
11137
  (_b = this.themeObserver) == null ? void 0 : _b.disconnect();
11138
+ (_c = this.authAttributeObserver) == null ? void 0 : _c.disconnect();
11104
11139
  if (this.cleanupListener) this.cleanupListener();
11105
11140
  this.clearLottieInstances();
11106
11141
  clearState(this.state);
11107
11142
  }
11143
+ setupAuthAttributeObserver() {
11144
+ var _a;
11145
+ (_a = this.authAttributeObserver) == null ? void 0 : _a.disconnect();
11146
+ this.authAttributeObserver = new MutationObserver(() => {
11147
+ var _a2;
11148
+ const nextConfig = readConfig(this, (_a2 = this.config) == null ? void 0 : _a2.communityId);
11149
+ const nextIdentitySignature = identitySignatureForConfig(nextConfig);
11150
+ if (nextIdentitySignature === this.identitySignature) {
11151
+ this.config = nextConfig;
11152
+ return;
11153
+ }
11154
+ const previousConfig = this.config;
11155
+ this.config = nextConfig;
11156
+ this.identitySignature = nextIdentitySignature;
11157
+ clearStorage(previousConfig);
11158
+ clearStorage(this.config);
11159
+ if (this.state.isAuthenticated || this.state.accessToken || this.state.refreshToken || this.state.iframe) {
11160
+ void this.reload();
11161
+ }
11162
+ });
11163
+ this.authAttributeObserver.observe(this, { attributes: true });
11164
+ }
11108
11165
  // ── DOM event helper (#1) ─────────────────────────────────────────────
11109
11166
  emit(type, detail) {
11110
11167
  this.dispatchEvent(
@@ -11645,6 +11702,14 @@ class BaseWidget extends HTMLElement {
11645
11702
  }
11646
11703
  // ── Public API ────────────────────────────────────────────────────────
11647
11704
  async reload() {
11705
+ var _a;
11706
+ const previousConfig = this.config;
11707
+ this.config = readConfig(this, (_a = this.config) == null ? void 0 : _a.communityId);
11708
+ this.identitySignature = identitySignatureForConfig(this.config);
11709
+ this.style.width = this.config.width;
11710
+ this.style.height = this.config.height;
11711
+ clearStorage(previousConfig);
11712
+ clearStorage(this.config);
11648
11713
  clearState(this.state);
11649
11714
  this.shadow.querySelectorAll("iframe").forEach((el) => el.remove());
11650
11715
  await this.init();
@@ -11767,7 +11832,7 @@ class CustomWidget extends BaseWidget {
11767
11832
  }
11768
11833
  }
11769
11834
  }
11770
- console.log(`[rai-widget] v${"1.4.0"}`);
11835
+ console.log(`[rai-widget] v${"1.4.1"}`);
11771
11836
  class StoreWidgetCompat extends StoreWidget {
11772
11837
  }
11773
11838
  const WIDGET_REGISTRY = [
@@ -11831,7 +11896,7 @@ function exposePublicApi() {
11831
11896
  })();
11832
11897
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
11833
11898
  window.ReturningAIWidget = {
11834
- version: "1.4.0",
11899
+ version: "1.4.1",
11835
11900
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
11836
11901
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
11837
11902
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
@@ -10,11 +10,14 @@ export declare abstract class BaseWidget extends HTMLElement {
10
10
  private cleanupListener?;
11
11
  private intersectionObserver?;
12
12
  private themeObserver?;
13
+ private authAttributeObserver?;
14
+ private identitySignature;
13
15
  private lottieInstances;
14
16
  constructor();
15
17
  protected abstract buildWidgetUrl(config: WidgetConfig): string;
16
18
  connectedCallback(): void;
17
19
  disconnectedCallback(): void;
20
+ private setupAuthAttributeObserver;
18
21
  private emit;
19
22
  /**
20
23
  * Apply widget-defined CSS colors before first paint so the loader uses
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@returningai/widget-sdk",
3
- "version": "1.4.0",
3
+ "version": "1.4.1",
4
4
  "description": "Shadow DOM isolated widget SDK for ReturningAI",
5
5
  "main": "dist/rai-widget.iife.js",
6
6
  "module": "dist/rai-widget.js",
@@ -13,7 +13,9 @@
13
13
  }
14
14
  },
15
15
  "files": [
16
- "dist"
16
+ "dist",
17
+ "CHANGELOG.md",
18
+ "README.md"
17
19
  ],
18
20
  "scripts": {
19
21
  "dev": "vite",