@returningai/widget-sdk 1.2.11 → 1.2.13

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.
package/README.md CHANGED
@@ -1,300 +1,391 @@
1
- # ReturningAI Widget SDK
1
+ # @returningai/widget-sdk
2
2
 
3
- A Web Component SDK for embedding ReturningAI widgets on any customer website. Supports both **iframe mode** (Shadow DOM isolation) and **bundle mode** (direct DOM rendering). Drop-in replacement for the legacy `widget-loader.js`.
3
+ Embed ReturningAI widgets on any website — React, Vue, Angular, or plain `<script>` tag. No external dependencies.
4
4
 
5
5
  ---
6
6
 
7
- ## Table of Contents
7
+ ## Quick Start
8
8
 
9
- - [Architecture](#architecture)
10
- - [Embed Modes](#embed-modes)
11
- - [Usage](#usage)
12
- - [Script Tag (Legacy Embed)](#script-tag-legacy-embed)
13
- - [Web Component](#web-component)
14
- - [Bundle Mode (Non-Iframe)](#bundle-mode-non-iframe)
15
- - [Configuration Attributes](#configuration-attributes)
16
- - [Public API](#public-api)
17
- - [Build Process](#build-process)
18
- - [Project Structure](#project-structure)
9
+ ### 1. Get your credentials
10
+
11
+ Log in to your **ReturningAI dashboard** and go to **Community Settings > Integrations > SDK Access**. You will find:
12
+
13
+ - **Widget ID** — identifies your widget
14
+ - **API URL** — the base URL for widget authentication
15
+ - **Widget URL** — the URL that loads the widget content
16
+
17
+ ### 2. Add the SDK to your page
18
+
19
+ ```html
20
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
21
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
22
+ data-widget-id="YOUR_WIDGET_ID"
23
+ data-widget-type="store"
24
+ data-theme="dark"
25
+ data-email="user@example.com"
26
+ api-url="YOUR_API_URL"
27
+ widget-url="YOUR_WIDGET_URL"
28
+ ></script>
29
+ ```
30
+
31
+ That's it. The widget will authenticate and render automatically.
19
32
 
20
33
  ---
21
34
 
22
- ## Architecture
35
+ ## Embed Modes
23
36
 
24
- The SDK renders widgets in one of two modes, chosen automatically by the presence of the `bundle-url` attribute:
37
+ Choose an embed mode based on your setup:
25
38
 
26
- ### Iframe Mode (default)
39
+ | | Public Embed | Access Key Embed | Authenticated Embed |
40
+ |---|---|---|---|
41
+ | **Backend required** | **No** | Yes | Yes |
42
+ | **Security** | Basic | High | Highest |
43
+ | **Best for** | Quick setup, demos | Production websites | Apps with user login |
27
44
 
28
- ```
29
- Customer page DOM
30
- └── <rai-store-widget> ← custom element (or any type-specific tag)
31
- └── Shadow Root [closed]
32
- ├── <style> ← all CSS scoped here, never leaks
33
- ├── <div class="rai-loader"> ← animated loader (hidden after ready)
34
- ├── <div class="rai-error"> ← error state (hidden until error)
35
- └── <iframe src="widget-url"> ← actual widget content
36
- ```
45
+ ### Public Embed (no backend needed)
37
46
 
38
- ### Bundle Mode (when `bundle-url` is set)
47
+ The simplest way to embed a widget. The SDK handles everything — no server-side code required.
39
48
 
40
- ```
41
- Customer page DOM
42
- └── <rai-store-widget> ← custom element (or any type-specific tag)
43
- ├── Shadow Root [closed]
44
- │ ├── <style> ← SDK loader/error CSS
45
- │ └── <slot> ← renders light-DOM children through shadow
46
- └── <div> (light DOM) ← widget mounts here, CSS cascade works normally
47
- └── [React app from IIFE bundle]
49
+ ```html
50
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
51
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
52
+ data-widget-id="YOUR_WIDGET_ID"
53
+ data-widget-type="store"
54
+ data-theme="dark"
55
+ data-email="user@example.com"
56
+ api-url="YOUR_API_URL"
57
+ widget-url="YOUR_WIDGET_URL"
58
+ ></script>
48
59
  ```
49
60
 
50
- Bundle mode loads the widget's JavaScript bundle directly into the page (no iframe). CSS cascades from `html[data-theme]` into the widget naturally, making theme integration seamless.
61
+ The `data-email` attribute identifies the user. The SDK authenticates directly with the ReturningAI backend on the client side.
51
62
 
52
- ### Module Responsibilities
63
+ **When to use:** Demos, internal tools, or any scenario where you don't have a backend server.
53
64
 
54
- | Module | File | Purpose |
55
- |--------|------|---------|
56
- | Base Web Component | `src/BaseWidget.ts` | Abstract `HTMLElement` subclass; owns Shadow Root, auth lifecycle, iframe/bundle rendering, lazy loading, DOM events, public API |
57
- | Store Widget | `src/StoreWidget.ts` | Extends `BaseWidget`; implements `buildWidgetUrl()` for the store micro-frontend |
58
- | Channel Widget | `src/ChannelWidget.ts` | Extends `BaseWidget`; appends encoded channel ID to URL |
59
- | Milestone Widget | `src/MilestoneWidget.ts` | Extends `BaseWidget`; same URL strategy as `ChannelWidget` |
60
- | Social Widget | `src/SocialWidget.ts` | Extends `BaseWidget`; same URL strategy as `ChannelWidget` |
61
- | Currency Widget | `src/CurrencyWidget.ts` | Extends `BaseWidget`; same URL strategy as `ChannelWidget` |
62
- | Custom Widget | `src/CustomWidget.ts` | Extends `BaseWidget`; same URL strategy as `ChannelWidget` |
63
- | Auth | `src/core/auth.ts` | Serverless auth with exponential backoff retry, proxy auth (authenticated embed), embed token validation (access key embed), token refresh (concurrency lock), logout, error settings fetch |
64
- | Storage | `src/core/storage.ts` | `localStorage` helpers scoped to `{prefix}-{communityId}-*`; access token never written to disk |
65
- | postMessage | `src/core/postmessage.ts` | Sends token (+ optional `customData`) to iframe; debounced height updates; emits DOM events; handles `WIDGET_READY`, `WIDGET_HEIGHT_UPDATE`, `WIDGET_LOGOUT`, `RETURNINGAI_WIDGET_REQUEST_TOKEN` |
66
- | Styles | `src/styles/widget.css` | Loader animation, error state, retry button, iframe fade-in — all scoped inside Shadow DOM |
67
- | Entry | `src/index.ts` | Registers all 7 custom element tags (+ 1 deprecated alias); bootstraps from `<script>` tag for legacy embeds; routes by `widget-type`; exposes `window.ReturningAIWidget` |
65
+ ### Access Key Embed (recommended for production)
68
66
 
69
- ### Auth Flow
67
+ Your access credentials stay on your server. Your server exchanges them for a **short-lived embed token (15 min)** and injects only that token into the page. No secret ever reaches the browser.
70
68
 
71
- ```
72
- Page load
73
- │
74
- ├─ embed-token present? (Access Key Embed)
75
- │ Yes ──► validateEmbedToken()
76
- │ ├─ valid ──► continue below
77
- │ └─ invalid ──► showError() — stop
78
- │ No ──► skip validation, continue below
79
- │
80
- ├─ auth-url present? (Authenticated Embed)
81
- │ Yes ──► authenticateViaProxy() ──► launch widget
82
- │ No ──► loadFromStorage()
83
- │ ├─ refresh token found ──► refreshAccessToken() ──► launch widget
84
- │ └─ no token ──► authenticateServerless() ──► launch widget
85
- │
86
- └─ launch widget
87
- ├─ bundle-url present? ──► mountWidget() (load IIFE, render in light DOM)
88
- └─ no bundle-url ──► createIframe() (standard iframe mode)
89
- ```
69
+ **Step 1 — Create an access key pair** (one-time, from your dashboard):
90
70
 
91
- **Access Key Embed flow** (credential exchange happens entirely server-side):
71
+ Go to **Community Settings > Integrations > SDK Access** and click **Create SDK Key**. Enter a name and optional allowed origins.
92
72
 
93
- ```
94
- Customer server
95
- │ POST /v2/api/widget-access-keys/token
96
- │ Body: { accessId, accessKey } ← stored in env vars, never in HTML
97
- ▼
98
- RAI backend — validates credentials, signs 15-min JWT
99
- │ Response: { embedToken, expiresIn: 900 }
100
- ▼
101
- Customer server injects embedToken into HTML
102
- │ <rai-store-widget embed-token="eyJ...">
103
- ▼
104
- Browser loads page — SDK reads embed-token attribute
105
- │ POST /v2/api/widget-access-keys/validate
106
- │ Body: { embedToken }
107
- ▼
108
- RAI backend — verifies JWT signature + checks key not revoked
109
- ├─ valid ──► widget initialises normally
110
- └─ invalid ──► showError()
111
- ```
73
+ The dashboard will display your `accessId` and `accessKey`. The `accessKey` is shown **once only** — save it securely.
112
74
 
113
- **Token storage strategy**
75
+ Store both values in your server environment variables.
114
76
 
115
- | Token | Location | TTL |
116
- |-------|----------|-----|
117
- | Access token | Memory only (`WidgetState.accessToken`) | ~5 min |
118
- | Refresh token | `localStorage` | 7 days |
77
+ **Step 2 — Exchange credentials for an embed token on every page load** (Node.js example):
119
78
 
120
- The access token is never written to `localStorage`. On every page load the refresh token is exchanged for a fresh access token before the widget mounts.
79
+ ```js
80
+ // Runs on YOUR SERVER — never in the browser
81
+ const response = await fetch('https://YOUR_API_URL/v2/api/widget-access-keys/token', {
82
+ method: 'POST',
83
+ headers: { 'Content-Type': 'application/json' },
84
+ body: JSON.stringify({
85
+ accessId: process.env.RAI_ACCESS_ID,
86
+ accessKey: process.env.RAI_ACCESS_KEY,
87
+ }),
88
+ })
89
+ const { data } = await response.json()
90
+ // data.embedToken expires in 15 minutes
91
+ ```
121
92
 
122
- ### postMessage Protocol (iframe mode only)
93
+ **Step 3 — Embed** (only the short-lived token appears in HTML):
123
94
 
124
- Messages the SDK sends **to** the widget iframe:
95
+ ```html
96
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
97
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
98
+ data-widget-id="YOUR_WIDGET_ID"
99
+ data-widget-type="store"
100
+ data-theme="dark"
101
+ data-email="user@example.com"
102
+ data-embed-token="eyJ..."
103
+ api-url="YOUR_API_URL"
104
+ widget-url="YOUR_WIDGET_URL"
105
+ ></script>
106
+ ```
125
107
 
126
- | Type | Payload | When |
127
- |------|---------|------|
128
- | `RETURNINGAI_WIDGET_TOKEN` | `{ widgetId, token, customData? }` | After iframe load, on refresh, every 2 min |
108
+ The SDK validates the token on widget load. If the token is expired or invalid, the widget shows an error screen. Tokens expire after 15 minutes — regenerate on each page render.
129
109
 
130
- Messages the SDK **receives** from the widget iframe:
110
+ ### Authenticated Embed (custom backend auth)
131
111
 
132
- | Type | Action |
133
- |------|--------|
134
- | `WIDGET_READY` | Hides loader, fades in iframe |
135
- | `WIDGET_HEIGHT_UPDATE` | Resizes iframe to `payload.height` |
136
- | `WIDGET_ERROR` | Hides loader |
137
- | `WIDGET_LOGOUT` | Calls logout endpoint, removes iframe |
138
- | `RETURNINGAI_WIDGET_REQUEST_TOKEN` | Responds immediately with a fresh token |
112
+ Your backend holds the API key. The SDK calls your endpoint to get a token — the API key never touches the browser.
139
113
 
140
- All messages are origin-validated against `config.widgetDomain`.
114
+ **Step 1 — Create a backend endpoint** (Node.js example):
141
115
 
142
- ---
116
+ ```js
117
+ app.post('/api/widget-auth', async (req, res) => {
118
+ const response = await fetch('https://YOUR_API_URL/{community_id}/auth/signin', {
119
+ method: 'POST',
120
+ headers: {
121
+ 'returningai-api-key': process.env.WIDGET_API_KEY,
122
+ 'email': req.user.email,
123
+ 'Content-Type': 'application/json',
124
+ },
125
+ })
126
+ const data = await response.json()
127
+ res.json({ token: data.token }) // never forward the API key
128
+ })
129
+ ```
143
130
 
144
- ## Embed Modes
131
+ The endpoint must return `{ token }`. Optionally include `expiresIn` (seconds) and the SDK will schedule a re-auth before expiry — otherwise it defaults to 300 seconds.
145
132
 
146
- The SDK supports three embed modes depending on your security requirements.
133
+ **Step 2 — Embed**:
147
134
 
148
- | | Public Embed | Access Key Embed | Authenticated Embed |
149
- |---|---|---|---|
150
- | Credentials in HTML | None | Short-lived token only | None |
151
- | Backend required | **No** | Yes — one call per page load | Yes — one endpoint |
152
- | Who generates auth | SDK calls RAI directly | Your server calls RAI, then token in HTML | Your server calls RAI with API key |
153
- | `embed-token` attribute | Absent | **Required** | Absent |
154
- | `auth-url` attribute | Absent | Absent | **Required** |
135
+ ```html
136
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
137
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
138
+ data-widget-id="YOUR_WIDGET_ID"
139
+ data-widget-type="store"
140
+ data-theme="dark"
141
+ data-auth-url="/api/widget-auth"
142
+ api-url="YOUR_API_URL"
143
+ widget-url="YOUR_WIDGET_URL"
144
+ ></script>
145
+ ```
155
146
 
156
- ---
147
+ No `data-email` needed — user identity is resolved on your backend.
157
148
 
158
- ## Shadow DOM Implementation
149
+ ---
159
150
 
160
- In iframe mode, the SDK uses a **closed** Shadow Root (`mode: 'closed'`), which means:
151
+ ## Install
161
152
 
162
- - Customer page CSS cannot reach any element inside the widget
163
- - The widget's loader and error styles are fully encapsulated
164
- - No class name collisions with customer frameworks (Tailwind, Bootstrap, etc.)
153
+ ### CDN (no install needed)
165
154
 
166
- In bundle mode, widget content renders in the **light DOM** (through a `<slot>`) so that `html[data-theme]` and Tailwind CSS cascade into the widget naturally.
155
+ ```html
156
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"></script>
157
+ ```
167
158
 
168
- ### Theme variables (iframe mode)
159
+ ### npm / yarn / pnpm
169
160
 
170
- | Variable | Light | Dark |
171
- |----------|-------|------|
172
- | `--rai-accent` | `#000000` | `#ffffff` |
173
- | `--rai-text4` | `#6b7280` | `#9ca3af` |
174
- | `--rai-loader-bg` | `#ffffff` | `#1a1a1a` |
175
- | `--rai-error-bg` | `#ffffff` | `#1a1a1a` |
161
+ ```bash
162
+ npm install @returningai/widget-sdk
163
+ ```
176
164
 
177
165
  ---
178
166
 
179
- ## Dependencies
167
+ ## Widget Types
180
168
 
181
- ### Runtime
169
+ Set the `data-widget-type` attribute to choose which widget to display. The Widget ID and Widget URL for each type are available in your dashboard.
182
170
 
183
- None. The SDK ships as a single self-contained IIFE with no external runtime dependencies.
171
+ | `data-widget-type` | Description |
172
+ |---------------------|-------------|
173
+ | `store` | Store / rewards widget |
174
+ | `channel` | Channel widget |
175
+ | `milestone` | Milestone widget |
176
+ | `social` | Social widget |
177
+ | `currency-view` | Currency widget |
178
+ | `referral-conditions` | Referral widget |
179
+ | `custom` | Custom widget |
184
180
 
185
- ### Dev / Build
181
+ ### Store widget
186
182
 
187
- | Package | Version | Purpose |
188
- |---------|---------|---------|
189
- | `vite` | ^5.1 | Bundles TypeScript + inlines CSS into a single IIFE |
190
- | `typescript` | ^5.3 | Type checking and compilation |
191
- | `terser` | ^5.46 | Minification for production builds |
183
+ ```html
184
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
185
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
186
+ data-widget-id="YOUR_WIDGET_ID"
187
+ data-widget-type="store"
188
+ data-theme="dark"
189
+ data-email="user@example.com"
190
+ api-url="YOUR_API_URL"
191
+ widget-url="YOUR_WIDGET_URL"
192
+ ></script>
193
+ ```
192
194
 
193
- ### Browser requirements
195
+ ### Channel widget
194
196
 
195
- | Feature | Minimum |
196
- |---------|---------|
197
- | Custom Elements v1 | Chrome 67, Firefox 63, Safari 13 |
198
- | Shadow DOM v1 | Chrome 53, Firefox 63, Safari 10 |
199
- | `crypto.randomUUID()` | Chrome 92, Firefox 95, Safari 15.4 |
200
- | `IntersectionObserver` | Chrome 58, Firefox 55, Safari 12.1 |
201
- | `localStorage` | All modern browsers |
197
+ ```html
198
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
199
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
200
+ data-widget-id="YOUR_WIDGET_ID"
201
+ data-widget-type="channel"
202
+ data-theme="dark"
203
+ data-email="user@example.com"
204
+ api-url="YOUR_API_URL"
205
+ widget-url="YOUR_WIDGET_URL"
206
+ ></script>
207
+ ```
202
208
 
203
- ---
209
+ > **Note:** `YOUR_WIDGET_ID` for channel widgets is the base64-encoded channel ID — find it under **Community Settings > Channels** in your dashboard. Alternatively, use the [custom element approach](#framework-usage-custom-elements) with separate `community-id` and `channel-id` attributes.
204
210
 
205
- ## Usage
211
+ ### Social widget
206
212
 
207
- ### Script Tag (Legacy Embed)
213
+ ```html
214
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
215
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
216
+ data-widget-id="YOUR_WIDGET_ID"
217
+ data-widget-type="social"
218
+ data-theme="dark"
219
+ data-email="user@example.com"
220
+ api-url="YOUR_API_URL"
221
+ widget-url="YOUR_WIDGET_URL"
222
+ ></script>
223
+ ```
208
224
 
209
- Zero changes required to existing embed HTML. Point `src` at the SDK and keep all `data-*` attributes as-is:
225
+ ### Milestone widget
210
226
 
211
227
  ```html
212
- <!-- 1. Container div (unchanged from current embed) -->
213
- <div
214
- id="returning-ai-widget-YOUR_COMMUNITY_ID"
215
- style="width: 100%; height: 600px;"
216
- ></div>
217
-
218
- <!-- 2. SDK script tag -->
219
- <script
220
- src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
221
- data-community-id="YOUR_COMMUNITY_ID"
222
- data-widget-type="store"
223
- data-container="returning-ai-widget-YOUR_COMMUNITY_ID"
228
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
229
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
230
+ data-widget-id="YOUR_WIDGET_ID"
231
+ data-widget-type="milestone"
232
+ data-theme="dark"
233
+ data-email="user@example.com"
234
+ api-url="YOUR_API_URL"
235
+ widget-url="YOUR_WIDGET_URL"
236
+ ></script>
237
+ ```
238
+
239
+ ### Custom widget
240
+
241
+ ```html
242
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
243
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
244
+ data-widget-id="YOUR_WIDGET_ID"
245
+ data-widget-type="custom"
224
246
  data-theme="dark"
225
- data-width="100%"
226
- data-height="600px"
227
- data-api-url="YOUR_API_URL"
228
- data-widget-url="YOUR_WIDGET_URL"
229
- data-auto-refresh="true"
230
247
  data-email="user@example.com"
248
+ api-url="YOUR_API_URL"
249
+ widget-url="YOUR_WIDGET_URL"
231
250
  ></script>
232
251
  ```
233
252
 
234
- The SDK scans for the loader `<script>` tag, reads its `data-*` attributes, creates the appropriate widget element, and mounts it inside the container div.
253
+ ---
235
254
 
236
- ### Access Key Embed
255
+ ## Attributes
237
256
 
238
- Credentials live only in your server environment. Your server calls RAI to get a short-lived embed token and injects it into the page response — no secret ever reaches the browser.
257
+ There are two ways to identify your widget:
239
258
 
240
- **Create an access key pair:** Go to **Community Settings > Integrations > SDK Access** in your ReturningAI dashboard and click **Create SDK Key**. The `accessKey` is shown once — save it securely.
259
+ 1. **Script-tag embed:** Use `data-widget-id` (a pre-encoded ID from your dashboard) on the `<script>` tag
260
+ 2. **Custom element embed:** Use `community-id` (and `channel-id` for channel widgets) on the custom element tag
241
261
 
242
- ```
243
- Your server env:
244
- RAI_ACCESS_ID = rai_...
245
- RAI_ACCESS_KEY = pk_... ← never in HTML
262
+ All attributes can be provided with or without the `data-` prefix — both work.
246
263
 
247
- Per-request (server-side):
248
- POST /v2/api/widget-access-keys/token
249
- → { embedToken, expiresIn: 900 }
264
+ ### Script-tag attributes
250
265
 
251
- Page HTML:
252
- <rai-store-widget embed-token="eyJ..." ...>
266
+ | Attribute | Required | Default | Description |
267
+ |-----------|----------|---------|-------------|
268
+ | `data-widget-id` | **Yes** | — | Your widget ID (from dashboard) |
269
+ | `data-widget-type` | **Yes** | `store` | Widget type (see table above) |
270
+ | `api-url` | **Yes**\* | — | Auth API base URL (from dashboard) |
271
+ | `widget-url` | **Yes**\* | — | Widget URL (from dashboard). \*Not required when using `data-bundle-url` |
272
+ | `data-bundle-url` | No | — | URL to the widget IIFE bundle — triggers [bundle mode](#bundle-mode) (non-iframe) |
273
+ | `data-theme` | No | `light` | `light` or `dark` |
274
+ | `data-email` | Recommended | — | User's email for identification |
275
+ | `data-embed-token` | Access Key only | — | Short-lived token from your server |
276
+ | `data-auth-url` | Auth Embed only | — | Your backend auth endpoint |
277
+ | `data-locale` | No | — | BCP 47 locale tag (e.g. `fr-FR`) |
278
+ | `data-eager` | No | — | Load immediately instead of waiting until visible |
279
+ | `data-auto-refresh` | No | `true` | Auto-refresh access token before expiry |
280
+ | `data-debug` | No | `false` | Verbose console logging |
281
+ | `data-custom-data` | No | — | JSON object forwarded to the widget |
282
+
283
+ ### Custom element attributes
284
+
285
+ | Attribute | Required | Default | Description |
286
+ |-----------|----------|---------|-------------|
287
+ | `community-id` | **Yes**\* | — | Your community ID (raw ObjectId from dashboard). \*Custom widgets use `widget-id` instead |
288
+ | `channel-id` | **Channel only** | — | Channel ID (raw ObjectId) — required for `<rai-channel-widget>` |
289
+ | `api-url` | **Yes** | — | Auth API base URL (from dashboard) |
290
+ | `widget-url` | **Yes**\* | — | Widget URL (from dashboard). \*Not required when using `bundle-url` |
291
+ | `bundle-url` | No | — | URL to the widget IIFE bundle — triggers [bundle mode](#bundle-mode) (non-iframe) |
292
+ | `theme` | No | `light` | `light` or `dark` |
293
+ | `data-email` | Recommended | — | User's email for identification |
294
+ | `embed-token` | Access Key only | — | Short-lived token from your server |
295
+ | `auth-url` | Auth Embed only | — | Your backend auth endpoint |
296
+ | `width` | No | `100%` | CSS width |
297
+ | `height` | No | `600px` | CSS height |
298
+ | `locale` | No | — | BCP 47 locale tag (e.g. `fr-FR`) |
299
+ | `eager` | No | — | Load immediately instead of waiting until visible |
300
+ | `auto-refresh` | No | `true` | Auto-refresh access token before expiry |
301
+ | `debug` | No | `false` | Verbose console logging |
302
+ | `custom-data` | No | — | JSON object forwarded to the widget |
303
+
304
+ > **`widget-id` vs `community-id`:** The script-tag method uses `data-widget-id` which is a pre-encoded identifier (base64). The custom element method uses raw ObjectIds — `community-id` for most widget types, plus `channel-id` for channel widgets. The SDK handles the encoding internally.
305
+
306
+ ### Container `<div>` (script-tag method only)
307
+
308
+ The SDK looks for a `<div>` with `id="returning-ai-widget-{YOUR_WIDGET_ID}"`. Size the div to control the widget dimensions:
309
+
310
+ ```html
311
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
253
312
  ```
254
313
 
255
- Node.js / Express example:
314
+ Custom elements don't need a container div — they size themselves using `width` and `height` attributes (defaults: `100%` and `600px`).
256
315
 
257
- ```js
258
- app.get('/page', async (req, res) => {
259
- const { data } = await fetch(`${process.env.RAI_API_URL}/v2/api/widget-access-keys/token`, {
260
- method: 'POST',
261
- headers: { 'Content-Type': 'application/json' },
262
- body: JSON.stringify({
263
- accessId: process.env.RAI_ACCESS_ID,
264
- accessKey: process.env.RAI_ACCESS_KEY,
265
- }),
266
- }).then(r => r.json())
267
-
268
- res.render('page', { embedToken: data.embedToken })
269
- })
316
+ ### User Identifier Attributes
317
+
318
+ Pass any `data-*` attribute to identify the current user. Which identifiers are available is configured per community in your dashboard.
319
+
320
+ ```html
321
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
322
+ data-widget-id="YOUR_WIDGET_ID"
323
+ data-widget-type="store"
324
+ data-email="user@example.com"
325
+ data-user-id="12345"
326
+ data-account-id="abc"
327
+ api-url="YOUR_API_URL"
328
+ widget-url="YOUR_WIDGET_URL"
329
+ ></script>
270
330
  ```
271
331
 
332
+ Any `data-*` attribute not reserved by the SDK is forwarded to the auth API as a user identifier.
333
+
334
+ ---
335
+
336
+ ## Bundle Mode
337
+
338
+ When you set the `bundle-url` attribute, the widget renders directly in the page DOM instead of inside an iframe. This gives full CSS cascade — `html[data-theme]` and Tailwind classes apply to the widget content naturally.
339
+
340
+ `widget-url` is not needed in bundle mode.
341
+
272
342
  ```html
273
- <!-- page.html -->
343
+ <!-- Script-tag approach -->
344
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:100vh"></div>
345
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
346
+ data-widget-id="YOUR_WIDGET_ID"
347
+ data-widget-type="store"
348
+ data-theme="dark"
349
+ data-email="user@example.com"
350
+ data-bundle-url="YOUR_BUNDLE_URL"
351
+ api-url="YOUR_API_URL"
352
+ data-eager
353
+ ></script>
354
+
355
+ <!-- Custom element approach -->
274
356
  <rai-store-widget
275
357
  community-id="YOUR_COMMUNITY_ID"
276
358
  api-url="YOUR_API_URL"
277
- embed-token="<%= embedToken %>"
359
+ bundle-url="YOUR_BUNDLE_URL"
278
360
  data-email="user@example.com"
361
+ theme="dark"
362
+ width="100%"
363
+ height="100vh"
364
+ eager
279
365
  ></rai-store-widget>
280
366
  ```
281
367
 
282
- The SDK validates the token on load. If expired (> 15 min since page render), the widget shows the error screen — users should refresh the page.
368
+ Each widget type has its own IIFE bundle and global name:
283
369
 
284
- ### Web Component
370
+ | Widget type | Bundle global | Custom element tag |
371
+ |-------------|--------------|-------------------|
372
+ | `store` | `RaiStoreWidget` | `<rai-store-widget>` |
373
+ | `channel` | `RaiChannelWidget` | `<rai-channel-widget>` |
374
+ | `social` | `RaiSocialWidget` | `<rai-social-widget>` |
375
+ | `milestone` | `RaiMilestoneWidget` | `<rai-milestone-widget>` |
376
+ | `currency-view` | `RaiCurrencyWidget` | `<rai-currency-widget>` |
377
+ | `referral-conditions` | `RaiReferralWidget` | `<rai-referral-widget>` |
378
+ | `custom` | `RaiCustomWidget` | `<rai-custom-widget>` |
285
379
 
286
- For customers using a JavaScript framework (React, Vue, Angular), import the SDK once and use the custom element directly. Each widget type has its own tag:
380
+ The bundle must export a `mount(container, config)` function on its global (e.g. `window.RaiStoreWidget.mount`).
287
381
 
288
- | Tag | Widget type |
289
- |-----|-------------|
290
- | `<rai-store-widget>` | `store` |
291
- | `<rai-channel-widget>` | `channel` |
292
- | `<rai-milestone-widget>` | `milestone` |
293
- | `<rai-social-widget>` | `social` |
294
- | `<rai-currency-widget>` | `currency-view` |
295
- | `<rai-referral-widget>` | `referral-conditions` |
296
- | `<rai-custom-widget>` | `custom` |
297
- | `<rai-widget>` | `store` *(deprecated alias — use `<rai-store-widget>`)* |
382
+ ---
383
+
384
+ ## Framework Usage (Custom Elements)
385
+
386
+ When using a frontend framework (React, Vue, Angular), you can use custom HTML element tags instead of the script-tag method. Custom elements use raw IDs (`community-id`, `channel-id`) instead of the pre-encoded `data-widget-id`.
387
+
388
+ ### Plain HTML
298
389
 
299
390
  ```html
300
391
  <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"></script>
@@ -303,124 +394,132 @@ For customers using a JavaScript framework (React, Vue, Angular), import the SDK
303
394
  <rai-store-widget
304
395
  community-id="YOUR_COMMUNITY_ID"
305
396
  theme="dark"
306
- width="100%"
307
- height="600px"
397
+ data-email="user@example.com"
308
398
  api-url="YOUR_API_URL"
309
399
  widget-url="YOUR_WIDGET_URL"
310
- data-email="user@example.com"
311
400
  ></rai-store-widget>
312
401
 
313
- <!-- Channel widget (requires channel-id) -->
402
+ <!-- Channel widget — requires both community-id AND channel-id -->
314
403
  <rai-channel-widget
315
404
  community-id="YOUR_COMMUNITY_ID"
316
405
  channel-id="YOUR_CHANNEL_ID"
317
406
  theme="dark"
318
- api-url="YOUR_API_URL"
319
- widget-url="YOUR_WIDGET_URL"
320
407
  data-email="user@example.com"
321
- ></rai-channel-widget>
322
-
323
- <!-- Custom widget (uses widget-id instead of community-id) -->
324
- <rai-custom-widget
325
- widget-id="BASE64_ENCODED_WIDGET_ID"
326
- theme="dark"
327
408
  api-url="YOUR_API_URL"
328
409
  widget-url="YOUR_WIDGET_URL"
329
- data-email="user@example.com"
330
- ></rai-custom-widget>
410
+ ></rai-channel-widget>
331
411
  ```
332
412
 
333
- > **Note:** Custom widgets use `widget-id` (base64 of the CustomWidget MongoDB `_id`) instead of `community-id`. Do not pass both — the SDK ignores `widget-id` when `community-id` is present.
413
+ ### React (TypeScript)
334
414
 
335
- In React (TypeScript):
415
+ Types are included — no manual JSX declarations needed.
336
416
 
337
417
  ```tsx
338
- // JSX types are included in the package — no manual declarations needed
339
418
  import '@returningai/widget-sdk'
340
419
 
341
- export function WidgetEmbed() {
420
+ export function StoreEmbed({ email }: { email: string }) {
342
421
  return (
343
422
  <rai-store-widget
344
423
  community-id="YOUR_COMMUNITY_ID"
345
424
  theme="dark"
346
- height="600px"
425
+ data-email={email}
426
+ api-url="YOUR_API_URL"
427
+ widget-url="YOUR_WIDGET_URL"
428
+ />
429
+ )
430
+ }
431
+
432
+ export function ChannelEmbed({ email }: { email: string }) {
433
+ return (
434
+ <rai-channel-widget
435
+ community-id="YOUR_COMMUNITY_ID"
436
+ channel-id="YOUR_CHANNEL_ID"
437
+ theme="dark"
438
+ data-email={email}
347
439
  api-url="YOUR_API_URL"
348
440
  widget-url="YOUR_WIDGET_URL"
349
- data-email={currentUser.email}
350
441
  />
351
442
  )
352
443
  }
353
444
  ```
354
445
 
355
- ### Bundle Mode (Non-Iframe)
446
+ ### Vue 3
356
447
 
357
- When you set the `bundle-url` attribute, the widget renders directly in the page DOM instead of inside an iframe. This gives full CSS cascade — `html[data-theme]` and Tailwind classes apply to the widget content naturally.
448
+ ```vue
449
+ <script setup>
450
+ import '@returningai/widget-sdk'
451
+ </script>
452
+
453
+ <template>
454
+ <!-- Store widget -->
455
+ <rai-store-widget
456
+ community-id="YOUR_COMMUNITY_ID"
457
+ theme="dark"
458
+ :data-email="user.email"
459
+ api-url="YOUR_API_URL"
460
+ widget-url="YOUR_WIDGET_URL"
461
+ />
462
+
463
+ <!-- Channel widget -->
464
+ <rai-channel-widget
465
+ community-id="YOUR_COMMUNITY_ID"
466
+ channel-id="YOUR_CHANNEL_ID"
467
+ theme="dark"
468
+ :data-email="user.email"
469
+ api-url="YOUR_API_URL"
470
+ widget-url="YOUR_WIDGET_URL"
471
+ />
472
+ </template>
473
+ ```
474
+
475
+ ### Angular
476
+
477
+ ```ts
478
+ // app.module.ts
479
+ import '@returningai/widget-sdk'
480
+ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
481
+
482
+ @NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA] })
483
+ ```
358
484
 
359
485
  ```html
486
+ <!-- Store widget -->
360
487
  <rai-store-widget
361
488
  community-id="YOUR_COMMUNITY_ID"
362
- api-url="YOUR_API_URL"
363
- bundle-url="/path/to/store-widget.js"
364
- embed-token="eyJ..."
365
- data-email="user@example.com"
366
489
  theme="dark"
367
- width="100%"
368
- height="100vh"
369
- eager
490
+ [attr.data-email]="user.email"
491
+ api-url="YOUR_API_URL"
492
+ widget-url="YOUR_WIDGET_URL"
370
493
  ></rai-store-widget>
371
- ```
372
494
 
373
- Each widget type has its own IIFE bundle and global name:
495
+ <!-- Channel widget -->
496
+ <rai-channel-widget
497
+ community-id="YOUR_COMMUNITY_ID"
498
+ channel-id="YOUR_CHANNEL_ID"
499
+ theme="dark"
500
+ [attr.data-email]="user.email"
501
+ api-url="YOUR_API_URL"
502
+ widget-url="YOUR_WIDGET_URL"
503
+ ></rai-channel-widget>
504
+ ```
374
505
 
375
- | Widget type | Bundle global |
376
- |-------------|--------------|
377
- | `store` | `RaiStoreWidget` |
378
- | `channel` | `RaiChannelWidget` |
379
- | `social` | `RaiSocialWidget` |
380
- | `milestone` | `RaiMilestoneWidget` |
381
- | `currency-view` | `RaiCurrencyWidget` |
382
- | `referral-conditions` | `RaiReferralWidget` |
383
- | `custom` | `RaiCustomWidget` |
506
+ ### Available custom element tags
384
507
 
385
- The bundle must export a `mount(container, config)` function on its global (e.g. `window.RaiStoreWidget.mount`).
508
+ | Tag | Required attributes | Widget type |
509
+ |-----|-------------------|-------------|
510
+ | `<rai-store-widget>` | `community-id` | Store |
511
+ | `<rai-channel-widget>` | `community-id` + `channel-id` | Channel |
512
+ | `<rai-milestone-widget>` | `community-id` | Milestone |
513
+ | `<rai-social-widget>` | `community-id` | Social |
514
+ | `<rai-currency-widget>` | `community-id` | Currency |
515
+ | `<rai-referral-widget>` | `community-id` | Referral |
516
+ | `<rai-custom-widget>` | `widget-id` (not `community-id`) | Custom |
386
517
 
387
- ### Configuration Attributes
518
+ ---
388
519
 
389
- All attributes can be provided with or without the `data-` prefix.
520
+ ## DOM Events
390
521
 
391
- | Attribute | Required | Default | Description |
392
- |-----------|----------|---------|-------------|
393
- | `community-id` | **Yes**\* | — | Your community ID — from Community Settings. \*Custom widgets use `widget-id` instead |
394
- | `channel-id` | Channel only | — | Channel ID — required when `widget-type` is `channel` |
395
- | `widget-type` | No | `store` | `store`, `channel`, `milestone`, `social`, `currency-view`, `referral`, `custom` |
396
- | `theme` | No | `light` | `light` or `dark` |
397
- | `container` | No | `returning-ai-widget-{id}` | ID of the container element |
398
- | `width` | No | `100%` | CSS width |
399
- | `height` | No | `600px` | Initial CSS height (auto-resized by `WIDGET_HEIGHT_UPDATE` in iframe mode) |
400
- | `api-url` | No | — | Auth API base URL — from Community Settings |
401
- | `widget-url` | No | — | URL served inside the iframe — from Community Settings (iframe mode only) |
402
- | `bundle-url` | No | — | URL to the widget IIFE bundle — triggers bundle mode (non-iframe) |
403
- | `embed-token` | Access Key only | — | Short-lived JWT from your server. Required for Access Key Embed; absent for Public and Authenticated Embed |
404
- | `v2-api-url` | No | — | Base URL for the V2 API (embed token validation). Only needed when V2 API lives on a different host than `api-url` |
405
- | `auth-url` | Auth Embed only | — | Your backend auth endpoint — enables Authenticated Embed mode |
406
- | `auto-refresh` | No | `true` | Automatically refresh access token before expiry |
407
- | `debug` | No | `false` | Enable verbose console logging |
408
- | `eager` | No | — | Boolean — skip `IntersectionObserver`, init immediately on mount |
409
- | `locale` | No | — | BCP 47 tag appended as `?locale=` to the widget URL (e.g. `fr-FR`) |
410
- | `max-retries` | No | `3` | Max auth retry attempts on network error or 5xx |
411
- | `retry-delay` | No | `500` | Base backoff delay in ms; doubles each attempt |
412
- | `height-debounce` | No | `100` | Debounce window in ms for `WIDGET_HEIGHT_UPDATE` |
413
- | `storage-prefix` | No | `returning-ai-widget` | `localStorage` key prefix — set per tenant to avoid collisions |
414
- | `retry-label` | No | `Retry` | Text for the retry button on the error screen |
415
- | `custom-data` | No | — | JSON string forwarded as `customData` in the token postMessage |
416
- | `data-email` | No | — | User identifier passed to auth (Public Embed only) |
417
- | `data-*` | No | — | Any additional `data-*` attributes are forwarded as `userIdentifiers` to the auth API |
418
-
419
- > **Note:** The legacy `widget-id` attribute is still supported as a deprecated alias for `community-id`.
420
-
421
- ### DOM Events
422
-
423
- All events bubble and are `composed: true` (cross the Shadow DOM boundary).
522
+ Listen for widget lifecycle events:
424
523
 
425
524
  | Event | `detail` | Fired when |
426
525
  |-------|----------|-----------|
@@ -432,120 +531,42 @@ All events bubble and are `composed: true` (cross the Shadow DOM boundary).
432
531
  | `rai-height-change` | `{ height }` | iframe resized (after debounce) |
433
532
 
434
533
  ```js
435
- document.querySelector('rai-channel-widget')
436
- .addEventListener('rai-error', (e) => showToast(e.detail.message))
437
- ```
438
-
439
- ### Public API
440
-
441
- After the SDK loads, `window.ReturningAIWidget` is available:
442
-
443
- ```javascript
444
- // Check the loaded version
445
- window.ReturningAIWidget.version // e.g. "1.1.2"
446
-
447
- // Reload the widget (re-runs auth flow)
448
- await window.ReturningAIWidget.reload()
449
-
450
- // Log out and remove the iframe
451
- await window.ReturningAIWidget.logout()
452
-
453
- // Check authentication state
454
- window.ReturningAIWidget.isAuthenticated() // boolean
455
-
456
- // Inspect token metadata (no token values exposed)
457
- window.ReturningAIWidget.getTokenInfo()
458
- // {
459
- // hasAccessToken: true,
460
- // hasRefreshToken: true,
461
- // accessTokenExpiry: Date,
462
- // refreshTokenExpiry: Date,
463
- // isAccessTokenValid: true,
464
- // isRefreshTokenValid: true
465
- // }
534
+ // Script-tag embed
535
+ const widget = document.querySelector('[data-widget-id]')
536
+
537
+ // Custom element embed
538
+ const widget = document.querySelector('rai-store-widget')
539
+
540
+ widget.addEventListener('rai-authenticated', () => { /* auth succeeded */ })
541
+ widget.addEventListener('rai-ready', () => { /* widget loaded and visible (iframe mode) */ })
542
+ widget.addEventListener('rai-mounted', () => { /* widget mounted (bundle mode) */ })
543
+ widget.addEventListener('rai-error', (e) => console.error(e.detail.message))
544
+ widget.addEventListener('rai-logout', () => { /* user logged out */ })
545
+ widget.addEventListener('rai-height-change', (e) => console.log(e.detail.height))
466
546
  ```
467
547
 
468
548
  ---
469
549
 
470
- ## Build Process
471
-
472
- ### Prerequisites
473
-
474
- ```bash
475
- node >= 18
476
- npm >= 9
477
- ```
478
-
479
- ### Install
480
-
481
- ```bash
482
- cd rai-widget-sdks
483
- npm install
484
- ```
485
-
486
- ### Development build (unminified, fast)
487
-
488
- ```bash
489
- npm run build
490
- # → dist/rai-widget.iife.js (~17 kB, IIFE for CDN/script-tag)
491
- # → dist/rai-widget.js (~17 kB, ESM for bundlers/npm)
492
- ```
493
-
494
- ### Production build (minified with Terser)
550
+ ## Public API
495
551
 
496
- ```bash
497
- npm run build:min
498
- # → dist/rai-widget.iife.js (~6 kB gzip, IIFE for CDN/script-tag)
499
- # → dist/rai-widget.js (~6 kB gzip, ESM for bundlers/npm)
500
- ```
501
-
502
- ### Local dev server
552
+ After the SDK loads, `window.ReturningAIWidget` is available:
503
553
 
504
- ```bash
505
- npm run dev
506
- # Opens Vite dev server — open test/index.html via the dev server URL
554
+ ```js
555
+ window.ReturningAIWidget.version // current SDK version
556
+ await window.ReturningAIWidget.reload() // re-runs auth flow and reloads widget
557
+ await window.ReturningAIWidget.logout() // clears tokens and removes widget
558
+ window.ReturningAIWidget.isAuthenticated() // boolean
559
+ window.ReturningAIWidget.getTokenInfo() // token metadata (no token values exposed)
507
560
  ```
508
561
 
509
- ### How the build works
510
-
511
- 1. **Entry**: `src/index.ts` — imports all widget subclasses, registers the custom elements, runs the bootstrap
512
- 2. **CSS inlining**: `src/styles/widget.css` is imported with Vite's `?inline` suffix, which converts it to a JavaScript string at build time — no separate CSS file is emitted
513
- 3. **Output formats**: `iife` (Immediately Invoked Function Expression) for CDN/script-tag embeds → `dist/rai-widget.iife.js`; `es` (ES module) for bundlers and npm consumers → `dist/rai-widget.js`
514
- 4. **Version injection**: `vite.config.ts` reads `version` from `package.json` and replaces the `__WIDGET_VERSION__` placeholder at build time
515
-
516
- To release a new version, bump `version` in `package.json`, rebuild, and upload `dist/rai-widget.iife.js` to the CDN under the new version path. Run `npm publish` to push the ESM build to the npm registry (`prepublishOnly` handles the rebuild automatically).
517
-
518
562
  ---
519
563
 
520
- ## Project Structure
564
+ ## Browser Support
521
565
 
522
- ```
523
- rai-widget-sdks/
524
- ├── src/
525
- │ ├── types.ts # WidgetConfig, WidgetState, TokenData interfaces
526
- │ ├── BaseWidget.ts # Abstract HTMLElement subclass — Shadow Root, auth, iframe/bundle
527
- │ ├── StoreWidget.ts # Extends BaseWidget; store micro-frontend URL builder
528
- │ ├── ChannelWidget.ts # Extends BaseWidget; channel URL builder
529
- │ ├── MilestoneWidget.ts # Extends BaseWidget; milestone URL builder
530
- │ ├── SocialWidget.ts # Extends BaseWidget; social URL builder
531
- │ ├── CurrencyWidget.ts # Extends BaseWidget; currency-view URL builder
532
- │ ├── CustomWidget.ts # Extends BaseWidget; custom widget URL builder
533
- │ ├── index.ts # Registers all custom elements + IIFE bootstrap
534
- │ ├── jsx.d.ts # React JSX IntrinsicElements + Vue GlobalComponents type shims
535
- │ ├── vite-env.d.ts # Type declarations for ?inline imports + __WIDGET_VERSION__
536
- │ ├── core/
537
- │ │ ├── auth.ts # Serverless auth, refresh, logout, error settings
538
- │ │ ├── storage.ts # localStorage helpers (refresh token persistence)
539
- │ │ └── postmessage.ts # iframe token delivery + message listener
540
- │ └── styles/
541
- │ └── widget.css # All loader/error/iframe CSS (inlined into Shadow DOM)
542
- ├── test/
543
- │ └── index.html # Local test page with config panel and verification checklist
544
- ├── dist/ # Built output (gitignored)
545
- │ ├── rai-widget.iife.js # IIFE bundle for CDN / <script src> embeds
546
- │ ├── rai-widget.js # ESM bundle for bundlers / npm consumers
547
- │ └── types/ # TypeScript declarations (generated by tsc)
548
- ├── package.json
549
- ├── tsconfig.json
550
- └── vite.config.ts
551
- ```
566
+ Requires Custom Elements v1 + Shadow DOM v1.
567
+
568
+ | Browser | Minimum version |
569
+ |---------|----------------|
570
+ | Chrome | 67 |
571
+ | Firefox | 63 |
572
+ | Safari | 13 |
@@ -1 +1 @@
1
- var RaiWidget=function(e){"use strict";var t=Object.defineProperty,r=(e,r,i)=>((e,r,i)=>r in e?t(e,r,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[r]=i)(e,"symbol"!=typeof r?r+"":r,i);function i(e){return`${e.storagePrefix}-${e.widgetId}-auth`}function a(e){return`${e.storagePrefix}-${e.widgetId}-error-settings`}function n(e){try{localStorage.removeItem(i(e))}catch{}}function o(e){return!e||Date.now()>=e-6e4}function s(e,t,r,a){t.accessToken=r.accessToken,t.refreshToken=r.refreshToken,t.tokenFamily=r.tokenFamily??null;const n=Date.now();t.accessTokenExpiry=n+1e3*r.accessTokenTTL,t.refreshTokenExpiry=n+1e3*r.refreshTokenTTL,function(e,t){try{const r={refreshToken:t.refreshToken,tokenFamily:t.tokenFamily,refreshTokenExpiry:t.refreshTokenExpiry};localStorage.setItem(i(e),JSON.stringify(r))}catch{}}(e,t),e.autoRefresh&&a&&a()}async function l(e,t,r){const i=crypto.randomUUID(),a=Date.now(),n=e.maxRetries??3,o=e.retryDelay??500;for(let l=0;l<=n;l++){l>0&&await new Promise(e=>setTimeout(e,o*(1<<l-1)));let c=!1;try{const n=await fetch(`${e.apiUrl}/${e.widgetId}/auth/serverless`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({nonce:i,timestamp:a,widgetType:e.widgetType,userIdentifiers:e.userIdentifiers}),credentials:"include"});if(!n.ok){c=n.status>=400&&n.status<500;const e=await n.json().catch(()=>({error:"Authentication failed"}));throw new Error(e.error||`HTTP ${n.status}`)}const o=await n.json();if(!o.accessToken||!o.refreshToken)throw new Error("Invalid authentication response");return s(e,t,o,r),t.isAuthenticated=!0,!0}catch{if(c||l===n)return t.isAuthenticated=!1,!1}}return t.isAuthenticated=!1,!1}async function c(e,t,r){const i=e.maxRetries??3,a=e.retryDelay??500;for(let n=0;n<=i;n++){n>0&&await new Promise(e=>setTimeout(e,a*(1<<n-1)));let o=!1;try{const i=await fetch(e.authUrl,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"}});if(!i.ok)throw o=i.status>=400&&i.status<500,new Error(`HTTP ${i.status}`);const a=await i.json();if(!a.token)throw new Error("Invalid proxy auth response");const n=a.expiresIn??300;return t.accessToken=a.token,t.refreshToken=null,t.tokenFamily=null,t.accessTokenExpiry=Date.now()+1e3*n,t.refreshTokenExpiry=null,t.isAuthenticated=!0,e.autoRefresh&&r&&r(),!0}catch{if(o||n===i)return t.isAuthenticated=!1,!1}}return t.isAuthenticated=!1,!1}async function d(e,t,r,i){return t.isRefreshing?t.refreshPromise:!!t.refreshToken&&(t.isRefreshing=!0,t.refreshPromise=(async()=>{try{const a=await fetch(`${e.apiUrl}/${e.widgetId}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.refreshToken}`},body:JSON.stringify({widgetType:e.widgetType})});if(!a.ok){if(401===a.status||403===a.status)return u(t),n(e),l(e,t,r);const i=await a.json().catch(()=>({error:"Token refresh failed"}));throw new Error(i.error||`HTTP ${a.status}`)}const o=await a.json();if(!o.accessToken||!o.refreshToken)throw new Error("Invalid refresh response");return s(e,t,o,r),i&&i(),!0}catch{return u(t),n(e),l(e,t,r)}finally{t.isRefreshing=!1,t.refreshPromise=null}})(),t.refreshPromise)}async function h(e,t){const r=function(e){try{const t=localStorage.getItem(a(e));if(!t)return null;const r=JSON.parse(t);return r.cachedAt&&Date.now()-r.cachedAt<36e5?r:(localStorage.removeItem(a(e)),null)}catch{return null}}(e);if(r)return r.configured&&(t.errorSettings={errorMessage:r.errorMessage,modalColor:r.modalColor,backgroundImage:r.backgroundImage}),r.loadingAnimation&&(t.loadingAnimation=r.loadingAnimation),t.errorSettings;try{const r=await fetch(`${e.apiUrl}/${e.widgetId}/auth/error-settings?widgetType=${e.widgetType}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!r.ok)return null;const i=await r.json();return function(e,t){try{localStorage.setItem(a(e),JSON.stringify({...t,cachedAt:Date.now()}))}catch{}}(e,i),i.configured&&(t.errorSettings={errorMessage:i.errorMessage,modalColor:i.modalColor,backgroundImage:i.backgroundImage}),i.loadingAnimation&&(t.loadingAnimation=i.loadingAnimation),t.errorSettings}catch{return null}}function u(e){e.accessToken=null,e.refreshToken=null,e.tokenFamily=null,e.accessTokenExpiry=null,e.refreshTokenExpiry=null,e.isAuthenticated=!1,e.isRefreshing=!1,e.refreshPromise=null,e.refreshTimer&&(clearTimeout(e.refreshTimer),e.refreshTimer=null),e.syncTimer&&(clearInterval(e.syncTimer),e.syncTimer=null)}function m(e,t,r){var i;if(t.accessToken)try{null==(i=r.contentWindow)||i.postMessage({type:"RETURNINGAI_WIDGET_TOKEN",value:{widgetId:e.widgetId,token:t.accessToken,...void 0!==e.customData&&{customData:e.customData}}},e.widgetDomain)}catch{}}function p(e,t,r,i,a,n,s,h){const u=function(e,t){let r=null;return{call(i){r&&clearTimeout(r),r=setTimeout(()=>{e(i),r=null},t)},cancel(){r&&(clearTimeout(r),r=null)}}}(e=>{i.style.height=`${e}px`,null==h||h("rai-height-change",{height:e})},e.heightDebounce??100),m=async r=>{var m;if(r.origin!==e.widgetDomain)return;if(!r.data||"string"!=typeof r.data.type)return;const{type:p,containerId:g,widgetId:f,payload:w}=r.data;switch(p){case"RETURNINGAI_WIDGET_REQUEST_TOKEN":try{const r=await async function(e,t,r){if(t.accessToken&&!o(t.accessTokenExpiry))return t.accessToken;if(e.authUrl){if(await c(e,t,r)&&t.accessToken)return t.accessToken;throw new Error("Unable to obtain valid token")}if(await d(e,t,r)&&t.accessToken)return t.accessToken;if(await l(e,t,r)&&t.accessToken)return t.accessToken;throw new Error("Unable to obtain valid token")}(e,t,a);null==(m=i.contentWindow)||m.postMessage({type:"RETURNINGAI_WIDGET_TOKEN",value:{token:r,widgetId:e.widgetId,...void 0!==e.customData&&{customData:e.customData}}},e.widgetDomain)}catch{}break;case"WIDGET_HEIGHT_UPDATE":{const e=Number(null==w?void 0:w.height);Number.isFinite(e)&&e>0&&u.call(e);break}case"WIDGET_READY":if(void 0!==f&&f!==e.widgetId)break;if(void 0!==g&&g!==e.container)break;i.classList.add("loaded"),s&&s(),null==h||h("rai-ready");break;case"WIDGET_ERROR":s&&s();break;case"WIDGET_LOGOUT":n&&await n()}};return window.addEventListener("message",m),()=>{window.removeEventListener("message",m),u.cancel()}}const g=new Set(["community-id","channel-id","widget-id","widget-type","theme","container","width","height","api-url","widget-url","auto-refresh","debug","storage-prefix","max-retries","retry-delay","height-debounce","locale","custom-data","retry-label","auth-url","bundle-url","embed-token","v2-api-url"]),f={store:"RaiStoreWidget",channel:"RaiChannelWidget",social:"RaiSocialWidget",milestone:"RaiMilestoneWidget","currency-view":"RaiCurrencyWidget","referral-conditions":"RaiReferralWidget",custom:"RaiCustomWidget"},w={"RAI-STORE-WIDGET":"store","RAI-CHANNEL-WIDGET":"channel","RAI-MILESTONE-WIDGET":"milestone","RAI-SOCIAL-WIDGET":"social","RAI-CURRENCY-WIDGET":"currency-view","RAI-REFERRAL-WIDGET":"referral-conditions","RAI-CUSTOM-WIDGET":"custom","RAI-WIDGET":"store"};class y extends HTMLElement{constructor(){super(),r(this,"shadow"),r(this,"config"),r(this,"state",{accessToken:null,refreshToken:null,tokenFamily:null,accessTokenExpiry:null,refreshTokenExpiry:null,refreshTimer:null,syncTimer:null,iframe:null,isAuthenticated:!1,isRefreshing:!1,refreshPromise:null,errorSettings:null,loadingAnimation:null}),r(this,"loaderEl",null),r(this,"errorEl",null),r(this,"msgEl",null),r(this,"cleanupListener"),r(this,"intersectionObserver"),r(this,"themeObserver"),this.shadow=this.attachShadow({mode:"closed"})}connectedCallback(){this.config=function(e,t){const r=t=>e.getAttribute(t)??e.getAttribute(`data-${t}`)??"",i=(e,t)=>{const i=parseInt(r(e),10);return Number.isFinite(i)&&i>=0?i:t},a=!!r("community-id"),n=r("community-id")||r("widget-id")||t||"";if(n&&!/^[a-zA-Z0-9_\-=]{8,}$/.test(n))throw new Error(`[rai-widget] Invalid community-id: "${n}"`);const o=r("channel-id")||void 0;if(o&&!/^[a-zA-Z0-9_\-=]{8,}$/.test(o))throw new Error(`[rai-widget] Invalid channel-id: "${o}"`);const s=r("widget-type")||w[e.tagName]||"store";let l;l=a?"store"===s?n:"channel"===s?o?btoa(o):n:n?btoa(n):n:n;let c,d=r("widget-url")||"https://widget.returningai.com";d.endsWith("store-widget")&&(d=`${d}/${n}/open-widget`);try{c=new URL(d).origin}catch{c=d}if(d.includes("store-widget")){const e=new URL(d);e.searchParams.set("color",r("theme")||"light"),d=e.toString()}const h=r("container")||r("data-container")||`returning-ai-widget-${l}`,u={};Array.from(e.attributes).forEach(e=>{const t=e.name.toLowerCase();if(!t.startsWith("data-"))return;const r=t.slice(5);g.has(r)||(u[t]=e.value)});const m=(()=>{const e=r("custom-data");if(e)try{return JSON.parse(e)}catch{return}})();return{communityId:n,channelId:o,widgetId:l,widgetType:s,theme:r("theme")||"light",container:h,width:r("width")||"100%",height:r("height")||"600px",apiUrl:r("api-url")||"https://sgtr-eks-widgets.genesiv.org",widgetUrl:d,widgetDomain:c,autoRefresh:"false"!==r("auto-refresh"),debug:"true"===r("debug"),storagePrefix:r("storage-prefix")||"returning-ai-widget",userIdentifiers:u,maxRetries:i("max-retries",3),retryDelay:i("retry-delay",500),heightDebounce:i("height-debounce",100),locale:r("locale")||void 0,customData:m,authUrl:r("auth-url")||void 0,bundleUrl:r("bundle-url")||void 0,embedToken:r("embed-token")||void 0,v2ApiUrl:r("v2-api-url")||void 0}}(this,void 0);const e=document.createElement("style");e.textContent=':host{display:block;position:relative;width:100%;height:100%}.rai-loader{position:absolute;top:0;left:0;display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--rai-loader-bg, #ffffff);border-radius:8px;z-index:10;transition:opacity .3s ease-out}.rai-loader.fade-out{opacity:0;pointer-events:none}.rai-error{display:none;position:absolute;top:0;left:0;width:100%;height:100%;align-items:center;justify-content:center;flex-direction:column;gap:12px;background:var(--rai-error-bg, #1a1a1a);border-radius:8px;padding:24px;box-sizing:border-box;text-align:center;color:var(--rai-error-text, #9ca3af);font-family:system-ui,-apple-system,sans-serif;z-index:10}.rai-error.visible{display:flex}iframe{display:block;border:none;opacity:0;transition:opacity .3s ease-in}iframe.loaded{opacity:1}.rai-lottie-loader{width:100px;height:100px}.rai-lottie-loader svg{width:100%!important;height:100%!important}.rai-lottie-spinner{width:32px;height:32px;margin:34px auto;border:3px solid var(--rai-text4, #6b7280);border-top-color:var(--rai-accent, #000000);border-radius:50%;animation:rai-spin .7s linear infinite}@keyframes rai-spin{to{transform:rotate(360deg)}}.rai-sdk-loader{position:relative;width:75px;height:100px}.rai-sdk-loader__bar{position:absolute;bottom:0;width:10px;height:50%;background:var(--rai-accent, #000000);transform-origin:center bottom;box-shadow:1px 1px #0003}.rai-sdk-loader__bar:nth-child(1){left:0;transform:scaleY(.2);animation:rai-sdk-barUp1 4s infinite}.rai-sdk-loader__bar:nth-child(2){left:15px;transform:scaleY(.4);animation:rai-sdk-barUp2 4s infinite}.rai-sdk-loader__bar:nth-child(3){left:30px;transform:scaleY(.6);animation:rai-sdk-barUp3 4s infinite}.rai-sdk-loader__bar:nth-child(4){left:45px;transform:scaleY(.8);animation:rai-sdk-barUp4 4s infinite}.rai-sdk-loader__bar:nth-child(5){left:60px;transform:scale(1);animation:rai-sdk-barUp5 4s infinite}.rai-sdk-loader__ball{position:absolute;bottom:10px;left:0;width:10px;height:10px;background:var(--rai-accent, #000000);border-radius:50%;animation:rai-sdk-ball 4s infinite}@keyframes rai-sdk-ball{0%{transform:translate(0)}5%{transform:translate(8px,-14px)}10%{transform:translate(15px,-10px)}17%{transform:translate(23px,-24px)}20%{transform:translate(30px,-20px)}27%{transform:translate(38px,-34px)}30%{transform:translate(45px,-30px)}37%{transform:translate(53px,-44px)}40%{transform:translate(60px,-40px)}50%{transform:translate(60px)}57%{transform:translate(53px,-14px)}60%{transform:translate(45px,-10px)}67%{transform:translate(37px,-24px)}70%{transform:translate(30px,-20px)}77%{transform:translate(22px,-34px)}80%{transform:translate(15px,-30px)}87%{transform:translate(7px,-44px)}90%{transform:translateY(-40px)}to{transform:translate(0)}}@keyframes rai-sdk-barUp1{0%{transform:scaleY(.2)}40%{transform:scaleY(.2)}50%{transform:scale(1)}90%{transform:scale(1)}to{transform:scaleY(.2)}}@keyframes rai-sdk-barUp2{0%{transform:scaleY(.4)}40%{transform:scaleY(.4)}50%{transform:scaleY(.8)}90%{transform:scaleY(.8)}to{transform:scaleY(.4)}}@keyframes rai-sdk-barUp3{0%{transform:scaleY(.6)}to{transform:scaleY(.6)}}@keyframes rai-sdk-barUp4{0%{transform:scaleY(.8)}40%{transform:scaleY(.8)}50%{transform:scaleY(.4)}90%{transform:scaleY(.4)}to{transform:scaleY(.8)}}@keyframes rai-sdk-barUp5{0%{transform:scale(1)}40%{transform:scale(1)}50%{transform:scaleY(.2)}90%{transform:scaleY(.2)}to{transform:scale(1)}}#loading-square{width:75px;aspect-ratio:1;display:flex;color:var(--rai-accent, #000000);background:linear-gradient(currentColor 0 0) right / 51% 100%,linear-gradient(currentColor 0 0) bottom / 100% 51%;background-repeat:no-repeat;animation:l16-0 2s infinite linear .25s}#loading-square>div{width:50%;height:50%;background:currentColor;animation:l16-1 .5s infinite linear}@keyframes l16-0{0%,12.49%{transform:rotate(0)}12.5%,37.49%{transform:rotate(90deg)}37.5%,62.49%{transform:rotate(180deg)}62.5%,87.49%{transform:rotate(270deg)}87.5%,to{transform:rotate(360deg)}}@keyframes l16-1{0%{transform:perspective(80px) rotate3d(-1,-1,0,0)}80%,to{transform:perspective(80px) rotate3d(-1,-1,0,-180deg)}}#loading-circle{width:75px;aspect-ratio:1;display:grid;grid:50%/50%;color:var(--rai-accent, #000000);border-radius:50%;--_g: no-repeat linear-gradient(currentColor 0 0);background:var(--_g),var(--_g),var(--_g);background-size:50.1% 50.1%;animation:l9-0 1.5s infinite steps(1) alternate,l9-0-0 3s infinite steps(1) alternate}#loading-circle>div{background:var(--rai-text4, #6b7280);border-top-left-radius:100px;transform:perspective(150px) rotateY(0) rotateX(0);transform-origin:bottom right;animation:l9-1 1.5s infinite linear alternate}@keyframes l9-0{0%{background-position:0 100%,100% 100%,100% 0}33%{background-position:100% 100%,100% 100%,100% 0}66%{background-position:100% 0,100% 0,100% 0}}@keyframes l9-0-0{0%{transform:scaleX(1) rotate(0)}50%{transform:scaleX(-1) rotate(-90deg)}}@keyframes l9-1{16.5%{transform:perspective(150px) rotateX(-90deg) rotateY(0) rotateX(0);filter:grayscale(.8)}33%{transform:perspective(150px) rotateX(-180deg) rotateY(0) rotateX(0)}66%{transform:perspective(150px) rotateX(-180deg) rotateY(-180deg) rotateX(0)}to{transform:perspective(150px) rotateX(-180deg) rotateY(-180deg) rotateX(-180deg);filter:grayscale(.8)}}.rai-custom-error{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.tiptap strong.tw-font-bold,.tiptap b.tw-font-bold{font-weight:700}.tiptap em.tw-font-italic,.tiptap i.tw-font-italic{font-style:italic}.tiptap a.tiptap-rich-text-link{color:#3b82f6;text-decoration:underline;position:relative;display:inline-block;width:fit-content}.tiptap a.tiptap-rich-text-link:hover{color:#3b82f6cc;text-decoration:none}.tiptap a.tiptap-rich-text-link:not([title=""]):before{content:attr(title);position:absolute;top:-2rem;left:50%;transform:translate(-50%);white-space:nowrap;border-radius:.25rem;background-color:#000;padding:.25rem .5rem;color:#fff;opacity:0;transition:opacity .2s;pointer-events:none}.tiptap a.tiptap-rich-text-link:not([title=""]):hover:before{opacity:1}.tiptap p[style*="text-align: left"]{text-align:left}.tiptap p[style*="text-align: center"]{text-align:center}.tiptap p[style*="text-align: right"]{text-align:right}.tiptap p[style*="text-align: justify"]{text-align:justify}.tiptap u{text-decoration:underline}.tiptap.ProseMirror{background-color:transparent;height:fit-content!important;word-wrap:break-word;white-space:pre-wrap;white-space:break-spaces;-webkit-font-variant-ligatures:none;font-variant-ligatures:none;font-feature-settings:"liga" 0;padding:0;width:100%;overflow-y:auto}.tiptap{line-height:1.6;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.tiptap p{margin:.5em 0}.tiptap p:first-child{margin-top:0}.tiptap p:last-child{margin-bottom:0}.tiptap img{max-width:100%;height:auto;display:block}.tiptap p[style*="text-align: left"] img{margin-left:0;margin-right:auto}.tiptap p[style*="text-align: center"] img{margin-left:auto;margin-right:auto}.tiptap p[style*="text-align: right"] img{margin-left:auto;margin-right:0}.rai-retry-btn{padding:8px 20px;border:none;border-radius:6px;background:var(--rai-accent, #000000);color:var(--rai-error-bg, #ffffff);font-size:14px;font-family:system-ui,-apple-system,sans-serif;cursor:pointer}.rai-retry-btn:hover{opacity:.85}',this.shadow.appendChild(e);const t=this.config.theme,r="dark"===t?"#ffffff":"#000000",i="dark"===t?"#9ca3af":"#6b7280",a="dark"===t?"#1a1a1a":"#ffffff";this.style.width=this.config.width,this.style.height=this.config.height,this.style.setProperty("--rai-accent",r),this.style.setProperty("--rai-text4",i),this.style.setProperty("--rai-loader-bg",a),this.style.setProperty("--rai-error-bg",a),this.renderShell(),this.hasAttribute("eager")?this.init():(this.intersectionObserver=new IntersectionObserver(e=>{e[0].isIntersecting&&(this.intersectionObserver.disconnect(),this.intersectionObserver=void 0,this.init())}),this.intersectionObserver.observe(this))}disconnectedCallback(){var e,t;null==(e=this.intersectionObserver)||e.disconnect(),null==(t=this.themeObserver)||t.disconnect(),this.cleanupListener&&this.cleanupListener(),u(this.state)}emit(e,t){this.dispatchEvent(new CustomEvent(e,{bubbles:!0,composed:!0,detail:t??{}}))}renderShell(){this.loaderEl=document.createElement("div"),this.loaderEl.className="rai-loader",this.shadow.appendChild(this.loaderEl),this.fetchAndRenderLoader(),this.errorEl=document.createElement("div"),this.errorEl.className="rai-error",this.msgEl=document.createElement("span"),this.msgEl.className="rai-error-msg",this.msgEl.textContent="Authentication failed. Please try again later.",this.errorEl.appendChild(this.msgEl);const e=this.getAttribute("retry-label")||this.getAttribute("data-retry-label")||"Retry",t=document.createElement("button");t.className="rai-retry-btn",t.textContent=e,t.addEventListener("click",()=>this.reload()),this.errorEl.appendChild(t),this.shadow.appendChild(this.errorEl)}async fetchAndRenderLoader(){var e;const t=document.createElement("div");t.className="rai-lottie-spinner",null==(e=this.loaderEl)||e.appendChild(t);try{await h(this.config,this.state)}catch{}if(!this.loaderEl)return;this.loaderEl.innerHTML="";const r=this.state.loadingAnimation,i="dark"===this.config.theme?"dark":"light";if("custom"===(null==r?void 0:r.type)&&r.custom){const e=r.custom[i]||r.custom.light||r.custom.dark;if(e){if("lottie"===e.type&&e.lottieData)return void this.renderLottieLoader(e.lottieData);if("custom"===e.type&&e.html){const t=document.createElement("div");return t.innerHTML=e.html,void this.loaderEl.appendChild(t)}}}"preset"===(null==r?void 0:r.type)&&r.preset?this.loaderEl.appendChild(this.createPresetLoader(r.preset)):this.loaderEl.appendChild(this.createDefaultLoader())}renderLottieLoader(e){const t=document.createElement("div");t.className="rai-lottie-loader",this.loaderEl.appendChild(t);const r=window;if(void 0!==r.lottie)r.lottie.loadAnimation({container:t,renderer:"svg",loop:!0,autoplay:!0,animationData:e});else{const r=document.createElement("div");r.className="rai-lottie-spinner",t.appendChild(r);const i=document.createElement("script");i.src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js",i.onload=()=>{window.lottie.loadAnimation({container:t,renderer:"svg",loop:!0,autoplay:!0,animationData:e})},document.head.appendChild(i)}}createDefaultLoader(){const e=document.createElement("div");e.className="rai-sdk-loader";for(let r=0;r<5;r++){const t=document.createElement("div");t.className="rai-sdk-loader__bar",e.appendChild(t)}const t=document.createElement("div");return t.className="rai-sdk-loader__ball",e.appendChild(t),e}createPresetLoader(e){if("square"===e){const e=document.createElement("div");e.id="loading-square";const t=document.createElement("div");return e.appendChild(t),e}if("circle"===e){const e=document.createElement("div");e.id="loading-circle";const t=document.createElement("div");return e.appendChild(t),e}return this.createDefaultLoader()}hideLoader(){this.loaderEl&&(this.loaderEl.classList.add("fade-out"),setTimeout(()=>{var e;return null==(e=this.loaderEl)?void 0:e.remove()},300),this.loaderEl=null)}showError(){this.hideLoader();const e=this.state.errorSettings;(null==e?void 0:e.errorMessage)?this.renderCustomError(e):this.errorEl&&this.errorEl.classList.add("visible"),this.emit("rai-error",{message:(null==e?void 0:e.errorMessage)??"Authentication failed"})}renderCustomError(e){var t,r;null==(t=this.errorEl)||t.remove(),this.errorEl=null;const i=(null==(r=e.backgroundImage)?void 0:r.trim())?`background-image: url('${e.backgroundImage}'); background-size: cover; background-position: center; background-repeat: no-repeat;`:"",a=document.createElement("div");a.className="rai-custom-error",a.style.cssText=`\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: auto;\n width: 100%;\n height: 100%;\n ${i}\n `;const n=document.createElement("div");n.className="rai-rich-text",n.style.cssText=`\n min-width: 50%;\n min-height: 50%;\n margin: 0 auto;\n padding: 20px;\n width: auto;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background-color: ${e.modalColor||"transparent"};\n `;const o=document.createElement("div");o.className="tiptap ProseMirror",o.innerHTML=e.errorMessage||"",n.appendChild(o),a.appendChild(n),this.shadow.appendChild(a)}async init(){if(!this.config.communityId)return void this.showError();if(await h(this.config,this.state),this.config.embedToken){if(!(await async function(e){try{const t=e.v2ApiUrl||e.apiUrl;return(await fetch(`${t}/v2/api/widget-access-keys/validate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({embedToken:e.embedToken})})).ok}catch{return!1}}(this.config)))return void this.showError()}const e=()=>{this.config.bundleUrl?this.mountWidget():this.createIframe()};if(this.config.authUrl){return void(await c(this.config,this.state,()=>this.scheduleRefresh())?(this.emit("rai-authenticated"),e()):this.showError())}if(function(e,t){try{const r=localStorage.getItem(i(e));if(!r)return!1;const a=JSON.parse(r);return a.refreshToken&&!o(a.refreshTokenExpiry)?(t.refreshToken=a.refreshToken,t.tokenFamily=a.tokenFamily,t.refreshTokenExpiry=a.refreshTokenExpiry,!0):(n(e),!1)}catch{return!1}}(this.config,this.state)){return await d(this.config,this.state,()=>this.scheduleRefresh())?(this.state.isAuthenticated=!0,this.emit("rai-authenticated"),void e()):void this.showError()}await l(this.config,this.state,()=>this.scheduleRefresh())?(this.emit("rai-authenticated"),e()):this.showError()}createIframe(){const e=document.createElement("iframe");e.src=this.buildWidgetUrl(this.config),e.allow="clipboard-write",e.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"),e.frameBorder="0",e.scrolling="no",e.style.width=this.config.width,e.style.height=this.config.height,e.style.border="none",e.style.display="block",e.onload=()=>{m(this.config,this.state,e),this.schedulePeriodicSync(e)},e.onerror=()=>{this.showError()},this.state.iframe=e,this.shadow.appendChild(e),this.cleanupListener=p(this.config,this.state,this.shadow,e,()=>this.scheduleRefresh(),()=>this.logoutAndClear(),()=>this.hideLoader(),(e,t)=>this.emit(e,t))}async mountWidget(){try{const t=window,r=["api_url","base_url","auth_api","socket_path_v2","socket_path","channel_api"];for(const e of r)t[e]||(t[e]=this.config.apiUrl);window.__IS_WIDGET__=!0,window.guest_token=this.state.accessToken,await(e=this.config.bundleUrl,new Promise((t,r)=>{const i=document.createElement("script");i.src=e,i.onload=()=>t(),i.onerror=()=>r(new Error(`Failed to load ${e}`)),document.head.appendChild(i)}));const i=f[this.config.widgetType],a=window[i];if(!(null==a?void 0:a.mount))throw new Error(`window.${i}.mount not found after loading bundle`);const n=document.createElement("div");if(n.style.cssText="position: relative; width: 100%; height: 100%;",this.appendChild(n),!this.shadow.querySelector("slot")){const e=document.createElement("slot");this.shadow.appendChild(e)}this.loaderEl&&(this.loaderEl.remove(),this.loaderEl=null),a.mount(n,{widgetType:this.config.widgetType,communityId:this.config.communityId,channelId:this.config.channelId,widgetId:this.config.communityId,apiUrl:this.config.apiUrl,token:this.state.accessToken,basePath:"/",isPreview:!1,isOpenWidget:!0,defaultColorSchema:"dark"===this.config.theme?"dark":"light"}),this.emit("rai-mounted")}catch(t){console.error("[rai-widget] Bundle mount failed:",t),this.showError()}var e}scheduleRefresh(){if(this.state.refreshTimer&&clearTimeout(this.state.refreshTimer),!this.state.accessTokenExpiry)return;const e=this.state.accessTokenExpiry-Date.now()-6e4,t=()=>{this.state.iframe&&m(this.config,this.state,this.state.iframe),this.config.bundleUrl&&this.state.accessToken&&(window.guest_token=this.state.accessToken)},r=async()=>{if(this.config.authUrl){await c(this.config,this.state,()=>this.scheduleRefresh())&&t()}else await d(this.config,this.state,()=>this.scheduleRefresh(),t)};e>0?this.state.refreshTimer=setTimeout(r,e):r()}schedulePeriodicSync(e){this.state.syncTimer&&clearInterval(this.state.syncTimer),this.state.syncTimer=setInterval(()=>{this.state.accessToken&&m(this.config,this.state,e)},12e4)}async logoutAndClear(){await async function(e,t){if(t.accessToken)try{await fetch(`${e.apiUrl}/${e.widgetId}/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.accessToken}`},body:JSON.stringify({refreshToken:t.refreshToken})})}catch{}u(t),n(e)}(this.config,this.state),this.shadow.querySelectorAll("iframe").forEach(e=>e.remove()),this.emit("rai-logout")}async reload(){u(this.state),this.shadow.querySelectorAll("iframe").forEach(e=>e.remove()),await this.init()}async logoutPublic(){await this.logoutAndClear()}isAuthenticated(){return this.state.isAuthenticated&&null!==this.state.accessToken}getTokenInfo(){return{hasAccessToken:!!this.state.accessToken,hasRefreshToken:!!this.state.refreshToken,accessTokenExpiry:this.state.accessTokenExpiry?new Date(this.state.accessTokenExpiry):null,refreshTokenExpiry:this.state.refreshTokenExpiry?new Date(this.state.refreshTokenExpiry):null,isAccessTokenValid:!!this.state.accessToken&&!o(this.state.accessTokenExpiry),isRefreshTokenValid:!!this.state.refreshToken&&!o(this.state.refreshTokenExpiry)}}}class b extends y{buildWidgetUrl(e){const t=new URL(e.widgetUrl);return t.searchParams.set("color",e.theme),t.searchParams.set("containerId",e.container),t.searchParams.set("connectType","simple"),t.searchParams.set("mode","private"),e.locale&&t.searchParams.set("locale",e.locale),t.toString()}}class k extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("channel-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}class T extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("milestone-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}class v extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("social-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}class x extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("currency-overview-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}class E extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("referral-conditions-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}class I extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("custom-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}console.log("[rai-widget] v1.2.11");const U=[["rai-store-widget",b],["rai-channel-widget",k],["rai-milestone-widget",T],["rai-social-widget",v],["rai-currency-widget",x],["rai-referral-widget",E],["rai-custom-widget",I],["rai-widget",class extends b{}]];for(const[$,D]of U)customElements.get($)||customElements.define($,D);const A={store:b,channel:k,milestone:T,social:v,"currency-view":x,"referral-conditions":E,custom:I},R=U.map(([e])=>e).join(", ");function C(){var e,t;const r=((null==(e=document.currentScript)?void 0:e.hasAttribute("data-widget-id"))||(null==(t=document.currentScript)?void 0:t.hasAttribute("data-community-id"))?document.currentScript:null)||document.querySelector("script[data-widget-id], script[data-community-id]");if(!r)return;const i=r.getAttribute("data-community-id")||r.getAttribute("data-widget-id");if(!i)return;const a=r.getAttribute("data-container")||`returning-ai-widget-${i}`,n=document.getElementById(a);if(!n)return;"static"===getComputedStyle(n).position&&(n.style.position="relative");const o=r.getAttribute("data-widget-type")??"store",s=new(A[o]??b);Array.from(r.attributes).forEach(e=>{s.setAttribute(e.name,e.value)}),n.appendChild(s)}function S(){const e=(()=>{var e,t;const r=((null==(e=document.currentScript)?void 0:e.hasAttribute("data-widget-id"))||(null==(t=document.currentScript)?void 0:t.hasAttribute("data-community-id"))?document.currentScript:null)||document.querySelector("script[data-widget-id], script[data-community-id]");if(!r)return null;const i=r.getAttribute("data-container")||`returning-ai-widget-${r.getAttribute("data-community-id")||r.getAttribute("data-widget-id")}`;return document.getElementById(i)})(),t=null==e?void 0:e.querySelector(R);window.ReturningAIWidget={version:"1.2.11",reload:()=>(null==t?void 0:t.reload())??Promise.resolve(),logout:()=>(null==t?void 0:t.logoutPublic())??Promise.resolve(),isAuthenticated:()=>(null==t?void 0:t.isAuthenticated())??!1,getTokenInfo:()=>(null==t?void 0:t.getTokenInfo())??{}}}return"loading"===document.readyState?document.addEventListener("DOMContentLoaded",C):C(),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",S):S(),e.ChannelWidget=k,e.CurrencyWidget=x,e.CustomWidget=I,e.MilestoneWidget=T,e.ReferralWidget=E,e.SocialWidget=v,e.StoreWidget=b,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),e}({});
1
+ var RaiWidget=function(e){"use strict";var t=Object.defineProperty,r=(e,r,i)=>((e,r,i)=>r in e?t(e,r,{enumerable:!0,configurable:!0,writable:!0,value:i}):e[r]=i)(e,"symbol"!=typeof r?r+"":r,i);function i(e){return`${e.storagePrefix}-${e.widgetId}-auth`}function a(e){return`${e.storagePrefix}-${e.widgetId}-error-settings`}function n(e){try{localStorage.removeItem(i(e))}catch{}}function o(e){return!e||Date.now()>=e-6e4}function s(e,t,r,a){t.accessToken=r.accessToken,t.refreshToken=r.refreshToken,t.tokenFamily=r.tokenFamily??null;const n=Date.now();t.accessTokenExpiry=n+1e3*r.accessTokenTTL,t.refreshTokenExpiry=n+1e3*r.refreshTokenTTL,function(e,t){try{const r={refreshToken:t.refreshToken,tokenFamily:t.tokenFamily,refreshTokenExpiry:t.refreshTokenExpiry};localStorage.setItem(i(e),JSON.stringify(r))}catch{}}(e,t),e.autoRefresh&&a&&a()}async function l(e,t,r){const i=crypto.randomUUID(),a=Date.now(),n=e.maxRetries??3,o=e.retryDelay??500;for(let l=0;l<=n;l++){l>0&&await new Promise(e=>setTimeout(e,o*(1<<l-1)));let c=!1;try{const n=await fetch(`${e.apiUrl}/${e.widgetId}/auth/serverless`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({nonce:i,timestamp:a,widgetType:e.widgetType,userIdentifiers:e.userIdentifiers}),credentials:"include"});if(!n.ok){c=n.status>=400&&n.status<500;const e=await n.json().catch(()=>({error:"Authentication failed"}));throw new Error(e.error||`HTTP ${n.status}`)}const o=await n.json();if(!o.accessToken||!o.refreshToken)throw new Error("Invalid authentication response");return s(e,t,o,r),t.isAuthenticated=!0,!0}catch{if(c||l===n)return t.isAuthenticated=!1,!1}}return t.isAuthenticated=!1,!1}async function c(e,t,r){const i=e.maxRetries??3,a=e.retryDelay??500;for(let n=0;n<=i;n++){n>0&&await new Promise(e=>setTimeout(e,a*(1<<n-1)));let o=!1;try{const i=await fetch(e.authUrl,{method:"POST",credentials:"include",headers:{"Content-Type":"application/json"}});if(!i.ok)throw o=i.status>=400&&i.status<500,new Error(`HTTP ${i.status}`);const a=await i.json();if(!a.token)throw new Error("Invalid proxy auth response");const n=a.expiresIn??300;return t.accessToken=a.token,t.refreshToken=null,t.tokenFamily=null,t.accessTokenExpiry=Date.now()+1e3*n,t.refreshTokenExpiry=null,t.isAuthenticated=!0,e.autoRefresh&&r&&r(),!0}catch{if(o||n===i)return t.isAuthenticated=!1,!1}}return t.isAuthenticated=!1,!1}async function d(e,t,r,i){return t.isRefreshing?t.refreshPromise:!!t.refreshToken&&(t.isRefreshing=!0,t.refreshPromise=(async()=>{try{const a=await fetch(`${e.apiUrl}/${e.widgetId}/auth/refresh`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.refreshToken}`},body:JSON.stringify({widgetType:e.widgetType})});if(!a.ok){if(401===a.status||403===a.status)return u(t),n(e),l(e,t,r);const i=await a.json().catch(()=>({error:"Token refresh failed"}));throw new Error(i.error||`HTTP ${a.status}`)}const o=await a.json();if(!o.accessToken||!o.refreshToken)throw new Error("Invalid refresh response");return s(e,t,o,r),i&&i(),!0}catch{return u(t),n(e),l(e,t,r)}finally{t.isRefreshing=!1,t.refreshPromise=null}})(),t.refreshPromise)}async function h(e,t){const r=function(e){try{const t=localStorage.getItem(a(e));if(!t)return null;const r=JSON.parse(t);return r.cachedAt&&Date.now()-r.cachedAt<36e5?r:(localStorage.removeItem(a(e)),null)}catch{return null}}(e);if(r)return r.configured&&(t.errorSettings={errorMessage:r.errorMessage,modalColor:r.modalColor,backgroundImage:r.backgroundImage}),r.loadingAnimation&&(t.loadingAnimation=r.loadingAnimation),t.errorSettings;try{const r=await fetch(`${e.apiUrl}/${e.widgetId}/auth/error-settings?widgetType=${e.widgetType}`,{method:"GET",headers:{"Content-Type":"application/json"}});if(!r.ok)return null;const i=await r.json();return function(e,t){try{localStorage.setItem(a(e),JSON.stringify({...t,cachedAt:Date.now()}))}catch{}}(e,i),i.configured&&(t.errorSettings={errorMessage:i.errorMessage,modalColor:i.modalColor,backgroundImage:i.backgroundImage}),i.loadingAnimation&&(t.loadingAnimation=i.loadingAnimation),t.errorSettings}catch{return null}}function u(e){e.accessToken=null,e.refreshToken=null,e.tokenFamily=null,e.accessTokenExpiry=null,e.refreshTokenExpiry=null,e.isAuthenticated=!1,e.isRefreshing=!1,e.refreshPromise=null,e.refreshTimer&&(clearTimeout(e.refreshTimer),e.refreshTimer=null),e.syncTimer&&(clearInterval(e.syncTimer),e.syncTimer=null)}function m(e,t,r){var i;if(t.accessToken)try{null==(i=r.contentWindow)||i.postMessage({type:"RETURNINGAI_WIDGET_TOKEN",value:{widgetId:e.widgetId,token:t.accessToken,...void 0!==e.customData&&{customData:e.customData}}},e.widgetDomain)}catch{}}function g(e,t,r,i,a,n,s,h){const u=function(e,t){let r=null;return{call(i){r&&clearTimeout(r),r=setTimeout(()=>{e(i),r=null},t)},cancel(){r&&(clearTimeout(r),r=null)}}}(e=>{i.style.height=`${e}px`,null==h||h("rai-height-change",{height:e})},e.heightDebounce??100),m=async r=>{var m;if(r.origin!==e.widgetDomain)return;if(!r.data||"string"!=typeof r.data.type)return;const{type:g,containerId:f,widgetId:p,payload:w}=r.data;switch(g){case"RETURNINGAI_WIDGET_REQUEST_TOKEN":try{const r=await async function(e,t,r){if(t.accessToken&&!o(t.accessTokenExpiry))return t.accessToken;if(e.authUrl){if(await c(e,t,r)&&t.accessToken)return t.accessToken;throw new Error("Unable to obtain valid token")}if(await d(e,t,r)&&t.accessToken)return t.accessToken;if(await l(e,t,r)&&t.accessToken)return t.accessToken;throw new Error("Unable to obtain valid token")}(e,t,a);null==(m=i.contentWindow)||m.postMessage({type:"RETURNINGAI_WIDGET_TOKEN",value:{token:r,widgetId:e.widgetId,...void 0!==e.customData&&{customData:e.customData}}},e.widgetDomain)}catch{}break;case"WIDGET_HEIGHT_UPDATE":{const e=Number(null==w?void 0:w.height);Number.isFinite(e)&&e>0&&u.call(e);break}case"WIDGET_READY":if(void 0!==p&&p!==e.widgetId)break;if(void 0!==f&&f!==e.container)break;i.classList.add("loaded"),s&&s(),null==h||h("rai-ready");break;case"WIDGET_ERROR":s&&s();break;case"WIDGET_LOGOUT":n&&await n()}};return window.addEventListener("message",m),()=>{window.removeEventListener("message",m),u.cancel()}}const f=new Set(["community-id","channel-id","widget-id","widget-type","theme","container","width","height","api-url","widget-url","auto-refresh","debug","storage-prefix","max-retries","retry-delay","height-debounce","locale","custom-data","retry-label","auth-url","bundle-url","embed-token","v2-api-url","domain-key"]),p={store:"RaiStoreWidget",channel:"RaiChannelWidget",social:"RaiSocialWidget",milestone:"RaiMilestoneWidget","currency-view":"RaiCurrencyWidget","referral-conditions":"RaiReferralWidget",custom:"RaiCustomWidget"},w={"RAI-STORE-WIDGET":"store","RAI-CHANNEL-WIDGET":"channel","RAI-MILESTONE-WIDGET":"milestone","RAI-SOCIAL-WIDGET":"social","RAI-CURRENCY-WIDGET":"currency-view","RAI-REFERRAL-WIDGET":"referral-conditions","RAI-CUSTOM-WIDGET":"custom","RAI-WIDGET":"store"};class y extends HTMLElement{constructor(){super(),r(this,"shadow"),r(this,"config"),r(this,"state",{accessToken:null,refreshToken:null,tokenFamily:null,accessTokenExpiry:null,refreshTokenExpiry:null,refreshTimer:null,syncTimer:null,iframe:null,isAuthenticated:!1,isRefreshing:!1,refreshPromise:null,errorSettings:null,loadingAnimation:null}),r(this,"loaderEl",null),r(this,"errorEl",null),r(this,"msgEl",null),r(this,"cleanupListener"),r(this,"intersectionObserver"),r(this,"themeObserver"),this.shadow=this.attachShadow({mode:"closed"})}connectedCallback(){this.config=function(e,t){const r=t=>e.getAttribute(t)??e.getAttribute(`data-${t}`)??"",i=(e,t)=>{const i=parseInt(r(e),10);return Number.isFinite(i)&&i>=0?i:t},a=!!r("community-id"),n=r("community-id")||r("widget-id")||t||"";if(n&&!/^[a-zA-Z0-9_\-=]{8,}$/.test(n))throw new Error(`[rai-widget] Invalid community-id: "${n}"`);const o=r("channel-id")||void 0;if(o&&!/^[a-zA-Z0-9_\-=]{8,}$/.test(o))throw new Error(`[rai-widget] Invalid channel-id: "${o}"`);const s=r("widget-type")||w[e.tagName]||"store";let l;l=a?"store"===s?n:"channel"===s?o?btoa(o):n:n?btoa(n):n:n;let c,d=r("widget-url")||"https://widget.returningai.com";d.endsWith("store-widget")&&(d=`${d}/${n}/open-widget`);try{c=new URL(d).origin}catch{c=d}if(d.includes("store-widget")){const e=new URL(d);e.searchParams.set("color",r("theme")||"light"),d=e.toString()}const h=r("container")||r("data-container")||`returning-ai-widget-${l}`,u={};Array.from(e.attributes).forEach(e=>{const t=e.name.toLowerCase();if(!t.startsWith("data-"))return;const r=t.slice(5);f.has(r)||(u[t]=e.value)});const m=(()=>{const e=r("custom-data");if(e)try{return JSON.parse(e)}catch{return}})();return{communityId:n,channelId:o,widgetId:l,widgetType:s,theme:r("theme")||"light",container:h,width:r("width")||"100%",height:r("height")||"600px",apiUrl:r("api-url")||"https://sgtr-eks-widgets.genesiv.org",widgetUrl:d,widgetDomain:c,autoRefresh:"false"!==r("auto-refresh"),debug:"true"===r("debug"),storagePrefix:r("storage-prefix")||"returning-ai-widget",userIdentifiers:u,maxRetries:i("max-retries",3),retryDelay:i("retry-delay",500),heightDebounce:i("height-debounce",100),locale:r("locale")||void 0,customData:m,authUrl:r("auth-url")||void 0,bundleUrl:r("bundle-url")||void 0,embedToken:r("embed-token")||void 0,v2ApiUrl:r("v2-api-url")||void 0,domainKey:r("domain-key")||void 0}}(this,void 0);const e=document.createElement("style");e.textContent=':host{display:block;position:relative;width:100%;height:100%}.rai-loader{position:absolute;top:0;left:0;display:flex;align-items:center;justify-content:center;width:100%;height:100%;background:var(--rai-loader-bg, #ffffff);border-radius:8px;z-index:10;transition:opacity .3s ease-out}.rai-loader.fade-out{opacity:0;pointer-events:none}.rai-error{display:none;position:absolute;top:0;left:0;width:100%;height:100%;align-items:center;justify-content:center;flex-direction:column;gap:12px;background:var(--rai-error-bg, #1a1a1a);border-radius:8px;padding:24px;box-sizing:border-box;text-align:center;color:var(--rai-error-text, #9ca3af);font-family:system-ui,-apple-system,sans-serif;z-index:10}.rai-error.visible{display:flex}iframe{display:block;border:none;opacity:0;transition:opacity .3s ease-in}iframe.loaded{opacity:1}.rai-lottie-loader{width:100px;height:100px}.rai-lottie-loader svg{width:100%!important;height:100%!important}.rai-lottie-spinner{width:32px;height:32px;margin:34px auto;border:3px solid var(--rai-text4, #6b7280);border-top-color:var(--rai-accent, #000000);border-radius:50%;animation:rai-spin .7s linear infinite}@keyframes rai-spin{to{transform:rotate(360deg)}}.rai-sdk-loader{position:relative;width:75px;height:100px}.rai-sdk-loader__bar{position:absolute;bottom:0;width:10px;height:50%;background:var(--rai-accent, #000000);transform-origin:center bottom;box-shadow:1px 1px #0003}.rai-sdk-loader__bar:nth-child(1){left:0;transform:scaleY(.2);animation:rai-sdk-barUp1 4s infinite}.rai-sdk-loader__bar:nth-child(2){left:15px;transform:scaleY(.4);animation:rai-sdk-barUp2 4s infinite}.rai-sdk-loader__bar:nth-child(3){left:30px;transform:scaleY(.6);animation:rai-sdk-barUp3 4s infinite}.rai-sdk-loader__bar:nth-child(4){left:45px;transform:scaleY(.8);animation:rai-sdk-barUp4 4s infinite}.rai-sdk-loader__bar:nth-child(5){left:60px;transform:scale(1);animation:rai-sdk-barUp5 4s infinite}.rai-sdk-loader__ball{position:absolute;bottom:10px;left:0;width:10px;height:10px;background:var(--rai-accent, #000000);border-radius:50%;animation:rai-sdk-ball 4s infinite}@keyframes rai-sdk-ball{0%{transform:translate(0)}5%{transform:translate(8px,-14px)}10%{transform:translate(15px,-10px)}17%{transform:translate(23px,-24px)}20%{transform:translate(30px,-20px)}27%{transform:translate(38px,-34px)}30%{transform:translate(45px,-30px)}37%{transform:translate(53px,-44px)}40%{transform:translate(60px,-40px)}50%{transform:translate(60px)}57%{transform:translate(53px,-14px)}60%{transform:translate(45px,-10px)}67%{transform:translate(37px,-24px)}70%{transform:translate(30px,-20px)}77%{transform:translate(22px,-34px)}80%{transform:translate(15px,-30px)}87%{transform:translate(7px,-44px)}90%{transform:translateY(-40px)}to{transform:translate(0)}}@keyframes rai-sdk-barUp1{0%{transform:scaleY(.2)}40%{transform:scaleY(.2)}50%{transform:scale(1)}90%{transform:scale(1)}to{transform:scaleY(.2)}}@keyframes rai-sdk-barUp2{0%{transform:scaleY(.4)}40%{transform:scaleY(.4)}50%{transform:scaleY(.8)}90%{transform:scaleY(.8)}to{transform:scaleY(.4)}}@keyframes rai-sdk-barUp3{0%{transform:scaleY(.6)}to{transform:scaleY(.6)}}@keyframes rai-sdk-barUp4{0%{transform:scaleY(.8)}40%{transform:scaleY(.8)}50%{transform:scaleY(.4)}90%{transform:scaleY(.4)}to{transform:scaleY(.8)}}@keyframes rai-sdk-barUp5{0%{transform:scale(1)}40%{transform:scale(1)}50%{transform:scaleY(.2)}90%{transform:scaleY(.2)}to{transform:scale(1)}}#loading-square{width:75px;aspect-ratio:1;display:flex;color:var(--rai-accent, #000000);background:linear-gradient(currentColor 0 0) right / 51% 100%,linear-gradient(currentColor 0 0) bottom / 100% 51%;background-repeat:no-repeat;animation:l16-0 2s infinite linear .25s}#loading-square>div{width:50%;height:50%;background:currentColor;animation:l16-1 .5s infinite linear}@keyframes l16-0{0%,12.49%{transform:rotate(0)}12.5%,37.49%{transform:rotate(90deg)}37.5%,62.49%{transform:rotate(180deg)}62.5%,87.49%{transform:rotate(270deg)}87.5%,to{transform:rotate(360deg)}}@keyframes l16-1{0%{transform:perspective(80px) rotate3d(-1,-1,0,0)}80%,to{transform:perspective(80px) rotate3d(-1,-1,0,-180deg)}}#loading-circle{width:75px;aspect-ratio:1;display:grid;grid:50%/50%;color:var(--rai-accent, #000000);border-radius:50%;--_g: no-repeat linear-gradient(currentColor 0 0);background:var(--_g),var(--_g),var(--_g);background-size:50.1% 50.1%;animation:l9-0 1.5s infinite steps(1) alternate,l9-0-0 3s infinite steps(1) alternate}#loading-circle>div{background:var(--rai-text4, #6b7280);border-top-left-radius:100px;transform:perspective(150px) rotateY(0) rotateX(0);transform-origin:bottom right;animation:l9-1 1.5s infinite linear alternate}@keyframes l9-0{0%{background-position:0 100%,100% 100%,100% 0}33%{background-position:100% 100%,100% 100%,100% 0}66%{background-position:100% 0,100% 0,100% 0}}@keyframes l9-0-0{0%{transform:scaleX(1) rotate(0)}50%{transform:scaleX(-1) rotate(-90deg)}}@keyframes l9-1{16.5%{transform:perspective(150px) rotateX(-90deg) rotateY(0) rotateX(0);filter:grayscale(.8)}33%{transform:perspective(150px) rotateX(-180deg) rotateY(0) rotateX(0)}66%{transform:perspective(150px) rotateX(-180deg) rotateY(-180deg) rotateX(0)}to{transform:perspective(150px) rotateX(-180deg) rotateY(-180deg) rotateX(-180deg);filter:grayscale(.8)}}.rai-custom-error{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.tiptap strong.tw-font-bold,.tiptap b.tw-font-bold{font-weight:700}.tiptap em.tw-font-italic,.tiptap i.tw-font-italic{font-style:italic}.tiptap a.tiptap-rich-text-link{color:#3b82f6;text-decoration:underline;position:relative;display:inline-block;width:fit-content}.tiptap a.tiptap-rich-text-link:hover{color:#3b82f6cc;text-decoration:none}.tiptap a.tiptap-rich-text-link:not([title=""]):before{content:attr(title);position:absolute;top:-2rem;left:50%;transform:translate(-50%);white-space:nowrap;border-radius:.25rem;background-color:#000;padding:.25rem .5rem;color:#fff;opacity:0;transition:opacity .2s;pointer-events:none}.tiptap a.tiptap-rich-text-link:not([title=""]):hover:before{opacity:1}.tiptap p[style*="text-align: left"]{text-align:left}.tiptap p[style*="text-align: center"]{text-align:center}.tiptap p[style*="text-align: right"]{text-align:right}.tiptap p[style*="text-align: justify"]{text-align:justify}.tiptap u{text-decoration:underline}.tiptap.ProseMirror{background-color:transparent;height:fit-content!important;word-wrap:break-word;white-space:pre-wrap;white-space:break-spaces;-webkit-font-variant-ligatures:none;font-variant-ligatures:none;font-feature-settings:"liga" 0;padding:0;width:100%;overflow-y:auto}.tiptap{line-height:1.6;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif}.tiptap p{margin:.5em 0}.tiptap p:first-child{margin-top:0}.tiptap p:last-child{margin-bottom:0}.tiptap img{max-width:100%;height:auto;display:block}.tiptap p[style*="text-align: left"] img{margin-left:0;margin-right:auto}.tiptap p[style*="text-align: center"] img{margin-left:auto;margin-right:auto}.tiptap p[style*="text-align: right"] img{margin-left:auto;margin-right:0}.rai-retry-btn{padding:8px 20px;border:none;border-radius:6px;background:var(--rai-accent, #000000);color:var(--rai-error-bg, #ffffff);font-size:14px;font-family:system-ui,-apple-system,sans-serif;cursor:pointer}.rai-retry-btn:hover{opacity:.85}',this.shadow.appendChild(e);const t=this.config.theme,r="dark"===t?"#ffffff":"#000000",i="dark"===t?"#9ca3af":"#6b7280",a="dark"===t?"#1a1a1a":"#ffffff";this.style.width=this.config.width,this.style.height=this.config.height,this.style.setProperty("--rai-accent",r),this.style.setProperty("--rai-text4",i),this.style.setProperty("--rai-loader-bg",a),this.style.setProperty("--rai-error-bg",a),this.renderShell(),this.hasAttribute("eager")?this.init():(this.intersectionObserver=new IntersectionObserver(e=>{e[0].isIntersecting&&(this.intersectionObserver.disconnect(),this.intersectionObserver=void 0,this.init())}),this.intersectionObserver.observe(this))}disconnectedCallback(){var e,t;null==(e=this.intersectionObserver)||e.disconnect(),null==(t=this.themeObserver)||t.disconnect(),this.cleanupListener&&this.cleanupListener(),u(this.state)}emit(e,t){this.dispatchEvent(new CustomEvent(e,{bubbles:!0,composed:!0,detail:t??{}}))}renderShell(){this.loaderEl=document.createElement("div"),this.loaderEl.className="rai-loader",this.shadow.appendChild(this.loaderEl),this.fetchAndRenderLoader(),this.errorEl=document.createElement("div"),this.errorEl.className="rai-error",this.msgEl=document.createElement("span"),this.msgEl.className="rai-error-msg",this.msgEl.textContent="Authentication failed. Please try again later.",this.errorEl.appendChild(this.msgEl);const e=this.getAttribute("retry-label")||this.getAttribute("data-retry-label")||"Retry",t=document.createElement("button");t.className="rai-retry-btn",t.textContent=e,t.addEventListener("click",()=>this.reload()),this.errorEl.appendChild(t),this.shadow.appendChild(this.errorEl)}async fetchAndRenderLoader(){var e;null==(e=this.loaderEl)||e.appendChild(this.createDefaultLoader());try{await h(this.config,this.state)}catch{}if(!this.loaderEl)return;this.loaderEl.innerHTML="";const t=this.state.loadingAnimation,r="dark"===this.config.theme?"dark":"light";if("custom"===(null==t?void 0:t.type)&&t.custom){const e=t.custom[r]||t.custom.light||t.custom.dark;if(e){if("lottie"===e.type&&e.lottieData)return void this.renderLottieLoader(e.lottieData);if("custom"===e.type&&e.html){const t=document.createElement("div");return t.innerHTML=e.html,void this.loaderEl.appendChild(t)}}}"preset"===(null==t?void 0:t.type)&&t.preset?this.loaderEl.appendChild(this.createPresetLoader(t.preset)):this.loaderEl.appendChild(this.createDefaultLoader())}renderLottieLoader(e){const t=document.createElement("div");t.className="rai-lottie-loader",this.loaderEl.appendChild(t);const r=window;if(void 0!==r.lottie)r.lottie.loadAnimation({container:t,renderer:"svg",loop:!0,autoplay:!0,animationData:e});else{const r=document.createElement("script");r.src="https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js",r.onload=()=>{window.lottie.loadAnimation({container:t,renderer:"svg",loop:!0,autoplay:!0,animationData:e})},document.head.appendChild(r)}}createDefaultLoader(){const e=document.createElement("div");e.className="rai-sdk-loader";for(let r=0;r<5;r++){const t=document.createElement("div");t.className="rai-sdk-loader__bar",e.appendChild(t)}const t=document.createElement("div");return t.className="rai-sdk-loader__ball",e.appendChild(t),e}createPresetLoader(e){if("square"===e){const e=document.createElement("div");e.id="loading-square";const t=document.createElement("div");return e.appendChild(t),e}if("circle"===e){const e=document.createElement("div");e.id="loading-circle";const t=document.createElement("div");return e.appendChild(t),e}return this.createDefaultLoader()}hideLoader(){this.loaderEl&&(this.loaderEl.classList.add("fade-out"),setTimeout(()=>{var e;return null==(e=this.loaderEl)?void 0:e.remove()},300),this.loaderEl=null)}showError(){this.hideLoader();const e=this.state.errorSettings;(null==e?void 0:e.errorMessage)?this.renderCustomError(e):this.errorEl&&this.errorEl.classList.add("visible"),this.emit("rai-error",{message:(null==e?void 0:e.errorMessage)??"Authentication failed"})}renderCustomError(e){var t,r;null==(t=this.errorEl)||t.remove(),this.errorEl=null;const i=(null==(r=e.backgroundImage)?void 0:r.trim())?`background-image: url('${e.backgroundImage}'); background-size: cover; background-position: center; background-repeat: no-repeat;`:"",a=document.createElement("div");a.className="rai-custom-error",a.style.cssText=`\n display: flex;\n align-items: center;\n justify-content: center;\n overflow: auto;\n width: 100%;\n height: 100%;\n ${i}\n `;const n=document.createElement("div");n.className="rai-rich-text",n.style.cssText=`\n min-width: 50%;\n min-height: 50%;\n margin: 0 auto;\n padding: 20px;\n width: auto;\n height: auto;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background-color: ${e.modalColor||"transparent"};\n `;const o=document.createElement("div");o.className="tiptap ProseMirror",o.innerHTML=e.errorMessage||"",n.appendChild(o),a.appendChild(n),this.shadow.appendChild(a)}async init(){if(!this.config.communityId)return void this.showError();if(await h(this.config,this.state),this.config.embedToken){if(!(await async function(e){try{const t=e.v2ApiUrl||e.apiUrl;return(await fetch(`${t}/v2/api/widget-access-keys/validate`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({embedToken:e.embedToken})})).ok}catch{return!1}}(this.config)))return void this.showError()}const e=()=>{this.config.bundleUrl?this.mountWidget():this.createIframe()};if(this.config.authUrl){return void(await c(this.config,this.state,()=>this.scheduleRefresh())?(this.emit("rai-authenticated"),e()):this.showError())}if(function(e,t){try{const r=localStorage.getItem(i(e));if(!r)return!1;const a=JSON.parse(r);return a.refreshToken&&!o(a.refreshTokenExpiry)?(t.refreshToken=a.refreshToken,t.tokenFamily=a.tokenFamily,t.refreshTokenExpiry=a.refreshTokenExpiry,!0):(n(e),!1)}catch{return!1}}(this.config,this.state)){return await d(this.config,this.state,()=>this.scheduleRefresh())?(this.state.isAuthenticated=!0,this.emit("rai-authenticated"),void e()):void this.showError()}await l(this.config,this.state,()=>this.scheduleRefresh())?(this.emit("rai-authenticated"),e()):this.showError()}createIframe(){const e=document.createElement("iframe");e.src=this.buildWidgetUrl(this.config),e.allow="clipboard-write",e.setAttribute("sandbox","allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"),e.frameBorder="0",e.scrolling="no",e.style.width=this.config.width,e.style.height=this.config.height,e.style.border="none",e.style.display="block",e.onload=()=>{m(this.config,this.state,e),this.schedulePeriodicSync(e)},e.onerror=()=>{this.showError()},this.state.iframe=e,this.shadow.appendChild(e),this.cleanupListener=g(this.config,this.state,this.shadow,e,()=>this.scheduleRefresh(),()=>this.logoutAndClear(),()=>this.hideLoader(),(e,t)=>this.emit(e,t))}async mountWidget(){try{const t=window;!t.DomainKey&&this.config.domainKey&&(t.DomainKey=this.config.domainKey),window.__IS_WIDGET__=!0,window.guest_token=this.state.accessToken,await(e=this.config.bundleUrl,new Promise((t,r)=>{const i=document.createElement("script");i.src=e,i.onload=()=>t(),i.onerror=()=>r(new Error(`Failed to load ${e}`)),document.head.appendChild(i)}));const r=p[this.config.widgetType],i=window[r];if(!(null==i?void 0:i.mount))throw new Error(`window.${r}.mount not found after loading bundle`);const a=document.createElement("div");if(a.style.cssText="position: relative; width: 100%; height: 100%;",this.appendChild(a),!this.shadow.querySelector("slot")){const e=document.createElement("slot");this.shadow.appendChild(e)}this.loaderEl&&(this.loaderEl.remove(),this.loaderEl=null),i.mount(a,{widgetType:this.config.widgetType,communityId:this.config.communityId,channelId:this.config.channelId,widgetId:this.config.communityId,apiUrl:this.config.apiUrl,token:this.state.accessToken,basePath:"/",isPreview:!1,isOpenWidget:!0,defaultColorSchema:"dark"===this.config.theme?"dark":"light"}),this.emit("rai-mounted")}catch(t){console.error("[rai-widget] Bundle mount failed:",t),this.showError()}var e}scheduleRefresh(){if(this.state.refreshTimer&&clearTimeout(this.state.refreshTimer),!this.state.accessTokenExpiry)return;const e=this.state.accessTokenExpiry-Date.now()-6e4,t=()=>{this.state.iframe&&m(this.config,this.state,this.state.iframe),this.config.bundleUrl&&this.state.accessToken&&(window.guest_token=this.state.accessToken)},r=async()=>{if(this.config.authUrl){await c(this.config,this.state,()=>this.scheduleRefresh())&&t()}else await d(this.config,this.state,()=>this.scheduleRefresh(),t)};e>0?this.state.refreshTimer=setTimeout(r,e):r()}schedulePeriodicSync(e){this.state.syncTimer&&clearInterval(this.state.syncTimer),this.state.syncTimer=setInterval(()=>{this.state.accessToken&&m(this.config,this.state,e)},12e4)}async logoutAndClear(){await async function(e,t){if(t.accessToken)try{await fetch(`${e.apiUrl}/${e.widgetId}/auth/logout`,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${t.accessToken}`},body:JSON.stringify({refreshToken:t.refreshToken})})}catch{}u(t),n(e)}(this.config,this.state),this.shadow.querySelectorAll("iframe").forEach(e=>e.remove()),this.emit("rai-logout")}async reload(){u(this.state),this.shadow.querySelectorAll("iframe").forEach(e=>e.remove()),await this.init()}async logoutPublic(){await this.logoutAndClear()}isAuthenticated(){return this.state.isAuthenticated&&null!==this.state.accessToken}getTokenInfo(){return{hasAccessToken:!!this.state.accessToken,hasRefreshToken:!!this.state.refreshToken,accessTokenExpiry:this.state.accessTokenExpiry?new Date(this.state.accessTokenExpiry):null,refreshTokenExpiry:this.state.refreshTokenExpiry?new Date(this.state.refreshTokenExpiry):null,isAccessTokenValid:!!this.state.accessToken&&!o(this.state.accessTokenExpiry),isRefreshTokenValid:!!this.state.refreshToken&&!o(this.state.refreshTokenExpiry)}}}class b extends y{buildWidgetUrl(e){const t=new URL(e.widgetUrl);return t.searchParams.set("color",e.theme),t.searchParams.set("containerId",e.container),t.searchParams.set("connectType","simple"),t.searchParams.set("mode","private"),e.locale&&t.searchParams.set("locale",e.locale),t.toString()}}class k extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("channel-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}class T extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("milestone-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}class v extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("social-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}class x extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("currency-overview-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}class E extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("referral-conditions-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}class I extends y{buildWidgetUrl(e){const t=e.widgetUrl.endsWith("custom-widget")?`${e.widgetUrl}/${e.widgetId}`:e.widgetUrl;if(!e.locale)return t;try{const r=new URL(t);return r.searchParams.set("locale",e.locale),r.toString()}catch{return`${t}?locale=${encodeURIComponent(e.locale)}`}}}console.log("[rai-widget] v1.2.13");const U=[["rai-store-widget",b],["rai-channel-widget",k],["rai-milestone-widget",T],["rai-social-widget",v],["rai-currency-widget",x],["rai-referral-widget",E],["rai-custom-widget",I],["rai-widget",class extends b{}]];for(const[$,D]of U)customElements.get($)||customElements.define($,D);const A={store:b,channel:k,milestone:T,social:v,"currency-view":x,"referral-conditions":E,custom:I},R=U.map(([e])=>e).join(", ");function C(){var e,t;const r=((null==(e=document.currentScript)?void 0:e.hasAttribute("data-widget-id"))||(null==(t=document.currentScript)?void 0:t.hasAttribute("data-community-id"))?document.currentScript:null)||document.querySelector("script[data-widget-id], script[data-community-id]");if(!r)return;const i=r.getAttribute("data-community-id")||r.getAttribute("data-widget-id");if(!i)return;const a=r.getAttribute("data-container")||`returning-ai-widget-${i}`,n=document.getElementById(a);if(!n)return;"static"===getComputedStyle(n).position&&(n.style.position="relative");const o=r.getAttribute("data-widget-type")??"store",s=new(A[o]??b);Array.from(r.attributes).forEach(e=>{s.setAttribute(e.name,e.value)}),n.appendChild(s)}function S(){const e=(()=>{var e,t;const r=((null==(e=document.currentScript)?void 0:e.hasAttribute("data-widget-id"))||(null==(t=document.currentScript)?void 0:t.hasAttribute("data-community-id"))?document.currentScript:null)||document.querySelector("script[data-widget-id], script[data-community-id]");if(!r)return null;const i=r.getAttribute("data-container")||`returning-ai-widget-${r.getAttribute("data-community-id")||r.getAttribute("data-widget-id")}`;return document.getElementById(i)})(),t=null==e?void 0:e.querySelector(R);window.ReturningAIWidget={version:"1.2.13",reload:()=>(null==t?void 0:t.reload())??Promise.resolve(),logout:()=>(null==t?void 0:t.logoutPublic())??Promise.resolve(),isAuthenticated:()=>(null==t?void 0:t.isAuthenticated())??!1,getTokenInfo:()=>(null==t?void 0:t.getTokenInfo())??{}}}return"loading"===document.readyState?document.addEventListener("DOMContentLoaded",C):C(),"loading"===document.readyState?document.addEventListener("DOMContentLoaded",S):S(),e.ChannelWidget=k,e.CurrencyWidget=x,e.CustomWidget=I,e.MilestoneWidget=T,e.ReferralWidget=E,e.SocialWidget=v,e.StoreWidget=b,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),e}({});
@@ -437,7 +437,8 @@ const DEFINED_ATTRS = /* @__PURE__ */ new Set([
437
437
  "auth-url",
438
438
  "bundle-url",
439
439
  "embed-token",
440
- "v2-api-url"
440
+ "v2-api-url",
441
+ "domain-key"
441
442
  ]);
442
443
  const WIDGET_GLOBALS = {
443
444
  store: "RaiStoreWidget",
@@ -565,8 +566,10 @@ function readConfig(el, existingId) {
565
566
  // bundle mode
566
567
  embedToken: get("embed-token") || void 0,
567
568
  // required — issued by customer's server
568
- v2ApiUrl: get("v2-api-url") || void 0
569
+ v2ApiUrl: get("v2-api-url") || void 0,
569
570
  // separate V2 API host for embed-token validation
571
+ domainKey: get("domain-key") || void 0
572
+ // SGTR, STG, GEN — controls IIFE environment resolution
570
573
  };
571
574
  }
572
575
  function createInitialState() {
@@ -679,9 +682,7 @@ class BaseWidget extends HTMLElement {
679
682
  */
680
683
  async fetchAndRenderLoader() {
681
684
  var _a;
682
- const spinner = document.createElement("div");
683
- spinner.className = "rai-lottie-spinner";
684
- (_a = this.loaderEl) == null ? void 0 : _a.appendChild(spinner);
685
+ (_a = this.loaderEl) == null ? void 0 : _a.appendChild(this.createDefaultLoader());
685
686
  try {
686
687
  await fetchErrorSettings(this.config, this.state);
687
688
  } catch {
@@ -725,9 +726,6 @@ class BaseWidget extends HTMLElement {
725
726
  animationData
726
727
  });
727
728
  } else {
728
- const spinner = document.createElement("div");
729
- spinner.className = "rai-lottie-spinner";
730
- lottieContainer.appendChild(spinner);
731
729
  const script = document.createElement("script");
732
730
  script.src = "https://cdnjs.cloudflare.com/ajax/libs/lottie-web/5.12.2/lottie.min.js";
733
731
  script.onload = () => {
@@ -929,16 +927,8 @@ class BaseWidget extends HTMLElement {
929
927
  async mountWidget() {
930
928
  try {
931
929
  const w = window;
932
- const endpointVars = [
933
- "api_url",
934
- "base_url",
935
- "auth_api",
936
- "socket_path_v2",
937
- "socket_path",
938
- "channel_api"
939
- ];
940
- for (const v of endpointVars) {
941
- if (!w[v]) w[v] = this.config.apiUrl;
930
+ if (!w.DomainKey && this.config.domainKey) {
931
+ w.DomainKey = this.config.domainKey;
942
932
  }
943
933
  ;
944
934
  window.__IS_WIDGET__ = true;
@@ -1129,7 +1119,7 @@ class CustomWidget extends BaseWidget {
1129
1119
  }
1130
1120
  }
1131
1121
  }
1132
- console.log(`[rai-widget] v${"1.2.11"}`);
1122
+ console.log(`[rai-widget] v${"1.2.13"}`);
1133
1123
  class StoreWidgetCompat extends StoreWidget {
1134
1124
  }
1135
1125
  const WIDGET_REGISTRY = [
@@ -1193,7 +1183,7 @@ function exposePublicApi() {
1193
1183
  })();
1194
1184
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
1195
1185
  window.ReturningAIWidget = {
1196
- version: "1.2.11",
1186
+ version: "1.2.13",
1197
1187
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
1198
1188
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
1199
1189
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
@@ -30,6 +30,7 @@ export interface WidgetConfig {
30
30
  bundleUrl?: string;
31
31
  embedToken?: string;
32
32
  v2ApiUrl?: string;
33
+ domainKey?: string;
33
34
  }
34
35
  export interface WidgetState {
35
36
  accessToken: string | null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@returningai/widget-sdk",
3
- "version": "1.2.11",
3
+ "version": "1.2.13",
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",