@returningai/widget-sdk 1.8.2 → 1.8.3

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.
@@ -12418,8 +12418,12 @@ async function authenticateServerless(config, state, onRefreshScheduled) {
12418
12418
  setTokens(config, state, data, onRefreshScheduled);
12419
12419
  state.isAuthenticated = true;
12420
12420
  return true;
12421
- } catch {
12421
+ } catch (err) {
12422
12422
  if (is4xx || attempt === maxRetries) {
12423
+ state.lastAuthError ?? (state.lastAuthError = {
12424
+ status: 0,
12425
+ error: err instanceof Error ? err.message : "Authentication failed"
12426
+ });
12423
12427
  state.isAuthenticated = false;
12424
12428
  return false;
12425
12429
  }
@@ -12438,6 +12442,7 @@ async function authenticateViaProxy(config, state, onRefreshScheduled) {
12438
12442
  );
12439
12443
  }
12440
12444
  let is4xx = false;
12445
+ let status = 0;
12441
12446
  try {
12442
12447
  const response = await fetch(config.authUrl, {
12443
12448
  method: "POST",
@@ -12445,6 +12450,7 @@ async function authenticateViaProxy(config, state, onRefreshScheduled) {
12445
12450
  headers: { "Content-Type": "application/json" }
12446
12451
  });
12447
12452
  if (!response.ok) {
12453
+ status = response.status;
12448
12454
  is4xx = response.status >= 400 && response.status < 500;
12449
12455
  throw new Error(`HTTP ${response.status}`);
12450
12456
  }
@@ -12459,12 +12465,17 @@ async function authenticateViaProxy(config, state, onRefreshScheduled) {
12459
12465
  state.accessTokenExpiry = Date.now() + ttl * 1e3;
12460
12466
  state.refreshTokenExpiry = null;
12461
12467
  state.isAuthenticated = true;
12468
+ state.lastAuthError = null;
12462
12469
  if (config.autoRefresh && onRefreshScheduled) {
12463
12470
  onRefreshScheduled();
12464
12471
  }
12465
12472
  return true;
12466
- } catch {
12473
+ } catch (err) {
12467
12474
  if (is4xx || attempt === maxRetries) {
12475
+ state.lastAuthError = {
12476
+ status,
12477
+ error: err instanceof Error ? err.message : "Authentication failed"
12478
+ };
12468
12479
  state.isAuthenticated = false;
12469
12480
  return false;
12470
12481
  }
@@ -12513,6 +12524,7 @@ async function refreshAccessToken(config, state, onRefreshScheduled, sendToken)
12513
12524
  throw new Error("Invalid refresh response");
12514
12525
  }
12515
12526
  setTokens(config, state, data, onRefreshScheduled);
12527
+ state.isAuthenticated = true;
12516
12528
  if (sendToken) sendToken();
12517
12529
  return true;
12518
12530
  } catch {
@@ -14847,13 +14859,15 @@ class BaseWidget extends HTMLElement {
14847
14859
  () => this.scheduleRefresh()
14848
14860
  );
14849
14861
  if (ok) pushToken();
14862
+ else this.handleSessionExpired();
14850
14863
  } else {
14851
- await refreshAccessToken(
14864
+ const ok = await refreshAccessToken(
14852
14865
  this.config,
14853
14866
  this.state,
14854
14867
  () => this.scheduleRefresh(),
14855
14868
  pushToken
14856
14869
  );
14870
+ if (!ok) this.handleSessionExpired();
14857
14871
  }
14858
14872
  };
14859
14873
  if (delay > 0) {
@@ -14869,6 +14883,35 @@ class BaseWidget extends HTMLElement {
14869
14883
  sendTokenToWidget(this.config, this.state, iframe);
14870
14884
  }, 12e4);
14871
14885
  }
14886
+ /**
14887
+ * A background refresh exhausted every recovery path — /auth/refresh failed
14888
+ * and the re-auth that follows it failed too, so `state.isAuthenticated` is
14889
+ * now false and there is no live token.
14890
+ *
14891
+ * Every startup auth path already calls showError() on failure; this one is
14892
+ * reached from a timer, so without it the widget stays mounted showing stale
14893
+ * content while isAuthenticated() reports false to the host page — which is
14894
+ * how a signed-in user ends up looking at content meant for signed-out users.
14895
+ * The event gives hosts a signal to re-mint credentials instead of polling.
14896
+ */
14897
+ handleSessionExpired() {
14898
+ var _a;
14899
+ if (this.state.refreshTimer) {
14900
+ clearTimeout(this.state.refreshTimer);
14901
+ this.state.refreshTimer = null;
14902
+ }
14903
+ (_a = this.state).lastAuthError ?? (_a.lastAuthError = {
14904
+ status: 401,
14905
+ error: "Session expired",
14906
+ hint: "The widget session could not be renewed. Reload to sign in again."
14907
+ });
14908
+ this.emit("rai-session-expired", {
14909
+ communityId: this.config.communityId,
14910
+ widgetType: this.config.widgetType,
14911
+ lastAuthError: this.state.lastAuthError ?? void 0
14912
+ });
14913
+ this.showError();
14914
+ }
14872
14915
  // ── Logout helper ─────────────────────────────────────────────────────
14873
14916
  async logoutAndClear() {
14874
14917
  await logout(this.config, this.state);
@@ -15024,7 +15067,7 @@ class CustomWidget extends BaseWidget {
15024
15067
  }
15025
15068
  }
15026
15069
  }
15027
- console.log(`[rai-widget] v${"1.8.2"}`);
15070
+ console.log(`[rai-widget] v${"1.8.3"}`);
15028
15071
  class StoreWidgetCompat extends StoreWidget {
15029
15072
  }
15030
15073
  const WIDGET_REGISTRY = [
@@ -15088,7 +15131,7 @@ function exposePublicApi() {
15088
15131
  })();
15089
15132
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
15090
15133
  window.ReturningAIWidget = {
15091
- version: "1.8.2",
15134
+ version: "1.8.3",
15092
15135
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
15093
15136
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
15094
15137
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
@@ -110,6 +110,18 @@ export declare abstract class BaseWidget extends HTMLElement {
110
110
  private mountWidget;
111
111
  private scheduleRefresh;
112
112
  private schedulePeriodicSync;
113
+ /**
114
+ * A background refresh exhausted every recovery path — /auth/refresh failed
115
+ * and the re-auth that follows it failed too, so `state.isAuthenticated` is
116
+ * now false and there is no live token.
117
+ *
118
+ * Every startup auth path already calls showError() on failure; this one is
119
+ * reached from a timer, so without it the widget stays mounted showing stale
120
+ * content while isAuthenticated() reports false to the host page — which is
121
+ * how a signed-in user ends up looking at content meant for signed-out users.
122
+ * The event gives hosts a signal to re-mint credentials instead of polling.
123
+ */
124
+ private handleSessionExpired;
113
125
  private logoutAndClear;
114
126
  reload(): Promise<void>;
115
127
  logoutPublic(): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@returningai/widget-sdk",
3
- "version": "1.8.2",
3
+ "version": "1.8.3",
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",
@@ -25,12 +25,14 @@
25
25
  "build:all": "npm run build && npm run build:banner",
26
26
  "build:all:min": "npm run build:min && npm run build:banner:min",
27
27
  "types": "tsc --emitDeclarationOnly --outDir dist/types && cp src/jsx.d.ts dist/types/jsx.d.ts",
28
- "test": "npm run test:host-callback && npm run test:bundle-readiness && npm run test:bundle-runtime-cache && npm run test:bundle-token-sync && npm run test:auth-bootstrap && npm run test:startup-performance && npm run test:jsx-types",
28
+ "test": "npm run test:host-callback && npm run test:bundle-readiness && npm run test:bundle-runtime-cache && npm run test:bundle-token-sync && npm run test:auth-bootstrap && npm run test:auth-error-reporting && npm run test:auth-refresh-state && npm run test:startup-performance && npm run test:jsx-types",
29
29
  "test:host-callback": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/postmessage-host-callback.test.js",
30
30
  "test:bundle-readiness": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/bundle-readiness.test.js",
31
31
  "test:bundle-runtime-cache": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/bundle-runtime-cache.test.js",
32
32
  "test:bundle-token-sync": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/bundle-token-sync.test.js",
33
33
  "test:auth-bootstrap": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/auth-bootstrap.test.js",
34
+ "test:auth-error-reporting": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/auth-error-reporting.test.js",
35
+ "test:auth-refresh-state": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/auth-refresh-state.test.js",
34
36
  "test:startup-performance": "rm -rf test/.tmp && tsc -p test/tsconfig.json && node test/.tmp/test/startup-performance.test.js",
35
37
  "test:jsx-types": "tsc --noEmit --strict --lib ES2020,DOM --skipLibCheck false src/jsx.d.ts",
36
38
  "coverage:repowise": "rm -rf coverage && npm run coverage:host-callback && npm run coverage:bundle-readiness && npm run test:jsx-types",
@@ -48,4 +50,4 @@
48
50
  "dompurify": "^3.4.12",
49
51
  "lottie-web": "^5.13.0"
50
52
  }
51
- }
53
+ }