@returningai/widget-sdk 1.4.1 → 1.4.2

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.
@@ -10827,6 +10827,11 @@ const DEFINED_ATTRS = /* @__PURE__ */ new Set([
10827
10827
  "accent",
10828
10828
  "font"
10829
10829
  ]);
10830
+ function isUserIdentifierAttribute(name) {
10831
+ const normalized = name.toLowerCase();
10832
+ if (!normalized.startsWith("data-")) return false;
10833
+ return !DEFINED_ATTRS.has(normalized.slice(5));
10834
+ }
10830
10835
  const WIDGET_GLOBALS = {
10831
10836
  store: "RaiStoreWidget",
10832
10837
  channel: "RaiChannelWidget",
@@ -11094,15 +11099,20 @@ class BaseWidget extends HTMLElement {
11094
11099
  __publicField(this, "intersectionObserver");
11095
11100
  // #3
11096
11101
  __publicField(this, "themeObserver");
11097
- __publicField(this, "authAttributeObserver");
11102
+ __publicField(this, "identityAttributeObserver");
11098
11103
  __publicField(this, "identitySignature", "");
11104
+ __publicField(this, "hasAttemptedInit", false);
11105
+ __publicField(this, "initGeneration", 0);
11099
11106
  __publicField(this, "lottieInstances", []);
11100
11107
  this.shadow = this.attachShadow({ mode: "closed" });
11101
11108
  }
11109
+ static get observedAttributes() {
11110
+ return ["embed-token", "data-embed-token"];
11111
+ }
11102
11112
  connectedCallback() {
11103
11113
  this.config = readConfig(this, void 0);
11104
11114
  this.identitySignature = identitySignatureForConfig(this.config);
11105
- this.setupAuthAttributeObserver();
11115
+ this.setupIdentityAttributeObserver();
11106
11116
  const style = document.createElement("style");
11107
11117
  style.textContent = widgetCSS;
11108
11118
  this.shadow.appendChild(style);
@@ -11135,32 +11145,45 @@ class BaseWidget extends HTMLElement {
11135
11145
  var _a, _b, _c;
11136
11146
  (_a = this.intersectionObserver) == null ? void 0 : _a.disconnect();
11137
11147
  (_b = this.themeObserver) == null ? void 0 : _b.disconnect();
11138
- (_c = this.authAttributeObserver) == null ? void 0 : _c.disconnect();
11139
- if (this.cleanupListener) this.cleanupListener();
11148
+ (_c = this.identityAttributeObserver) == null ? void 0 : _c.disconnect();
11149
+ this.teardownMountedWidget();
11140
11150
  this.clearLottieInstances();
11141
11151
  clearState(this.state);
11142
11152
  }
11143
- setupAuthAttributeObserver() {
11153
+ attributeChangedCallback(name, oldValue, newValue) {
11154
+ if (oldValue === newValue) return;
11155
+ if (name !== "embed-token" && name !== "data-embed-token") return;
11156
+ this.handleIdentityConfigChange();
11157
+ }
11158
+ setupIdentityAttributeObserver() {
11144
11159
  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();
11160
+ (_a = this.identityAttributeObserver) == null ? void 0 : _a.disconnect();
11161
+ this.identityAttributeObserver = new MutationObserver((mutations) => {
11162
+ const hasIdentityAttributeChange = mutations.some(
11163
+ (mutation) => mutation.type === "attributes" && !!mutation.attributeName && isUserIdentifierAttribute(mutation.attributeName)
11164
+ );
11165
+ if (hasIdentityAttributeChange) {
11166
+ this.handleIdentityConfigChange();
11161
11167
  }
11162
11168
  });
11163
- this.authAttributeObserver.observe(this, { attributes: true });
11169
+ this.identityAttributeObserver.observe(this, { attributes: true });
11170
+ }
11171
+ handleIdentityConfigChange() {
11172
+ if (!this.isConnected || !this.config) return;
11173
+ const nextConfig = readConfig(this, this.config.communityId);
11174
+ const nextIdentitySignature = identitySignatureForConfig(nextConfig);
11175
+ if (nextIdentitySignature === this.identitySignature) {
11176
+ this.config = nextConfig;
11177
+ return;
11178
+ }
11179
+ const previousConfig = this.config;
11180
+ this.config = nextConfig;
11181
+ this.identitySignature = nextIdentitySignature;
11182
+ clearStorage(previousConfig);
11183
+ clearStorage(this.config);
11184
+ if (this.hasAttemptedInit) {
11185
+ void this.reload();
11186
+ }
11164
11187
  }
11165
11188
  // ── DOM event helper (#1) ─────────────────────────────────────────────
11166
11189
  emit(type, detail) {
@@ -11473,12 +11496,19 @@ class BaseWidget extends HTMLElement {
11473
11496
  }
11474
11497
  // ── Initialization ─────────────────────────────────────────────────────
11475
11498
  async init() {
11499
+ const initId = ++this.initGeneration;
11500
+ this.hasAttemptedInit = true;
11476
11501
  if (!this.config.communityId) {
11477
11502
  this.showError();
11478
11503
  return;
11479
11504
  }
11480
11505
  await fetchErrorSettings(this.config, this.state);
11506
+ if (!this.isCurrentInit(initId)) return;
11481
11507
  const launchWidget = () => {
11508
+ var _a;
11509
+ if (!this.isCurrentInit(initId)) return;
11510
+ (_a = this.errorEl) == null ? void 0 : _a.classList.remove("visible");
11511
+ this.shadow.querySelectorAll(".rai-custom-error").forEach((el) => el.remove());
11482
11512
  if (this.config.bundleUrl) {
11483
11513
  this.mountWidget();
11484
11514
  } else {
@@ -11491,6 +11521,7 @@ class BaseWidget extends HTMLElement {
11491
11521
  this.state,
11492
11522
  () => this.scheduleRefresh()
11493
11523
  );
11524
+ if (!this.isCurrentInit(initId)) return;
11494
11525
  if (authed2) {
11495
11526
  this.emit("rai-authenticated");
11496
11527
  launchWidget();
@@ -11507,6 +11538,7 @@ class BaseWidget extends HTMLElement {
11507
11538
  this.state,
11508
11539
  () => this.scheduleRefresh()
11509
11540
  );
11541
+ if (!this.isCurrentInit(initId)) return;
11510
11542
  if (authed2) {
11511
11543
  this.emit("rai-authenticated");
11512
11544
  launchWidget();
@@ -11522,6 +11554,7 @@ class BaseWidget extends HTMLElement {
11522
11554
  this.state,
11523
11555
  () => this.scheduleRefresh()
11524
11556
  );
11557
+ if (!this.isCurrentInit(initId)) return;
11525
11558
  if (!refreshed) {
11526
11559
  this.showError();
11527
11560
  return;
@@ -11536,6 +11569,7 @@ class BaseWidget extends HTMLElement {
11536
11569
  this.state,
11537
11570
  () => this.scheduleRefresh()
11538
11571
  );
11572
+ if (!this.isCurrentInit(initId)) return;
11539
11573
  if (authed) {
11540
11574
  this.emit("rai-authenticated");
11541
11575
  launchWidget();
@@ -11543,7 +11577,26 @@ class BaseWidget extends HTMLElement {
11543
11577
  this.showError();
11544
11578
  }
11545
11579
  }
11580
+ isCurrentInit(initId) {
11581
+ return this.isConnected && initId === this.initGeneration;
11582
+ }
11583
+ teardownMountedWidget() {
11584
+ if (this.cleanupListener) {
11585
+ this.cleanupListener();
11586
+ this.cleanupListener = void 0;
11587
+ }
11588
+ if (this.state.syncTimer) {
11589
+ clearInterval(this.state.syncTimer);
11590
+ this.state.syncTimer = null;
11591
+ }
11592
+ this.state.iframe = null;
11593
+ this.shadow.querySelectorAll("iframe").forEach((el) => el.remove());
11594
+ this.querySelectorAll('[data-rai-widget-root="true"]').forEach(
11595
+ (el) => el.remove()
11596
+ );
11597
+ }
11546
11598
  createIframe() {
11599
+ this.teardownMountedWidget();
11547
11600
  const iframe = document.createElement("iframe");
11548
11601
  iframe.src = this.buildWidgetUrl(this.config);
11549
11602
  iframe.allow = "clipboard-write";
@@ -11581,6 +11634,7 @@ class BaseWidget extends HTMLElement {
11581
11634
  // ── Bundle mode (no-iframe) ──────────────────────────────────────────
11582
11635
  async mountWidget() {
11583
11636
  try {
11637
+ this.teardownMountedWidget();
11584
11638
  const bundleApiUrl = this.config.v2ApiUrl || resolveV2ApiUrl(this.config.domainKey);
11585
11639
  const w = window;
11586
11640
  if (!w.DomainKey) {
@@ -11697,7 +11751,7 @@ class BaseWidget extends HTMLElement {
11697
11751
  // ── Logout helper ─────────────────────────────────────────────────────
11698
11752
  async logoutAndClear() {
11699
11753
  await logout(this.config, this.state);
11700
- this.shadow.querySelectorAll("iframe").forEach((el) => el.remove());
11754
+ this.teardownMountedWidget();
11701
11755
  this.emit("rai-logout");
11702
11756
  }
11703
11757
  // ── Public API ────────────────────────────────────────────────────────
@@ -11710,8 +11764,8 @@ class BaseWidget extends HTMLElement {
11710
11764
  this.style.height = this.config.height;
11711
11765
  clearStorage(previousConfig);
11712
11766
  clearStorage(this.config);
11767
+ this.teardownMountedWidget();
11713
11768
  clearState(this.state);
11714
- this.shadow.querySelectorAll("iframe").forEach((el) => el.remove());
11715
11769
  await this.init();
11716
11770
  }
11717
11771
  async logoutPublic() {
@@ -11832,7 +11886,7 @@ class CustomWidget extends BaseWidget {
11832
11886
  }
11833
11887
  }
11834
11888
  }
11835
- console.log(`[rai-widget] v${"1.4.1"}`);
11889
+ console.log(`[rai-widget] v${"1.4.2"}`);
11836
11890
  class StoreWidgetCompat extends StoreWidget {
11837
11891
  }
11838
11892
  const WIDGET_REGISTRY = [
@@ -11896,7 +11950,7 @@ function exposePublicApi() {
11896
11950
  })();
11897
11951
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
11898
11952
  window.ReturningAIWidget = {
11899
- version: "1.4.1",
11953
+ version: "1.4.2",
11900
11954
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
11901
11955
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
11902
11956
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
@@ -1,6 +1,7 @@
1
1
  import type { WidgetConfig } from "./types";
2
2
  export declare function readConfig(el: Element, existingId?: string): WidgetConfig;
3
3
  export declare abstract class BaseWidget extends HTMLElement {
4
+ static get observedAttributes(): string[];
4
5
  protected shadow: ShadowRoot;
5
6
  protected config: WidgetConfig;
6
7
  private state;
@@ -10,14 +11,18 @@ export declare abstract class BaseWidget extends HTMLElement {
10
11
  private cleanupListener?;
11
12
  private intersectionObserver?;
12
13
  private themeObserver?;
13
- private authAttributeObserver?;
14
+ private identityAttributeObserver?;
14
15
  private identitySignature;
16
+ private hasAttemptedInit;
17
+ private initGeneration;
15
18
  private lottieInstances;
16
19
  constructor();
17
20
  protected abstract buildWidgetUrl(config: WidgetConfig): string;
18
21
  connectedCallback(): void;
19
22
  disconnectedCallback(): void;
20
- private setupAuthAttributeObserver;
23
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
24
+ private setupIdentityAttributeObserver;
25
+ private handleIdentityConfigChange;
21
26
  private emit;
22
27
  /**
23
28
  * Apply widget-defined CSS colors before first paint so the loader uses
@@ -80,6 +85,8 @@ export declare abstract class BaseWidget extends HTMLElement {
80
85
  private showError;
81
86
  private renderCustomError;
82
87
  private init;
88
+ private isCurrentInit;
89
+ private teardownMountedWidget;
83
90
  private createIframe;
84
91
  private mountWidget;
85
92
  private scheduleRefresh;
package/package.json CHANGED
@@ -1,35 +1,35 @@
1
- {
2
- "name": "@returningai/widget-sdk",
3
- "version": "1.4.1",
4
- "description": "Shadow DOM isolated widget SDK for ReturningAI",
5
- "main": "dist/rai-widget.iife.js",
6
- "module": "dist/rai-widget.js",
7
- "types": "dist/types/index.d.ts",
8
- "sideEffects": true,
9
- "exports": {
10
- ".": {
11
- "import": "./dist/rai-widget.js",
12
- "default": "./dist/rai-widget.iife.js"
13
- }
14
- },
15
- "files": [
16
- "dist",
17
- "CHANGELOG.md",
18
- "README.md"
19
- ],
20
- "scripts": {
21
- "dev": "vite",
22
- "build": "vite build",
23
- "build:min": "vite build --mode production",
24
- "types": "tsc --emitDeclarationOnly --outDir dist/types",
25
- "prepublishOnly": "npm run build:min && npm run types"
26
- },
27
- "devDependencies": {
28
- "terser": "^5.46.0",
29
- "typescript": "^5.3.3",
30
- "vite": "^5.1.0"
31
- },
32
- "dependencies": {
33
- "lottie-web": "^5.13.0"
34
- }
35
- }
1
+ {
2
+ "name": "@returningai/widget-sdk",
3
+ "version": "1.4.2",
4
+ "description": "Shadow DOM isolated widget SDK for ReturningAI",
5
+ "main": "dist/rai-widget.iife.js",
6
+ "module": "dist/rai-widget.js",
7
+ "types": "dist/types/index.d.ts",
8
+ "sideEffects": true,
9
+ "exports": {
10
+ ".": {
11
+ "import": "./dist/rai-widget.js",
12
+ "default": "./dist/rai-widget.iife.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "CHANGELOG.md",
18
+ "README.md"
19
+ ],
20
+ "scripts": {
21
+ "dev": "vite",
22
+ "build": "vite build",
23
+ "build:min": "vite build --mode production",
24
+ "types": "tsc --emitDeclarationOnly --outDir dist/types",
25
+ "prepublishOnly": "npm run build:min && npm run types"
26
+ },
27
+ "devDependencies": {
28
+ "terser": "^5.46.0",
29
+ "typescript": "^5.3.3",
30
+ "vite": "^5.1.0"
31
+ },
32
+ "dependencies": {
33
+ "lottie-web": "^5.13.0"
34
+ }
35
+ }