@returningai/widget-sdk 1.2.15 → 1.2.16

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,527 +1,477 @@
1
- # @returningai/widget-sdk
2
-
3
- Embed ReturningAI widgets on any website — React, Vue, Angular, or plain `<script>` tag. No external dependencies.
4
-
5
- ---
6
-
7
- ## Quick Start
8
-
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.
32
-
33
- ---
34
-
35
- ## Authentication
36
-
37
- ### Public Embed (no backend needed)
38
-
39
- The simplest way to embed a widget. The SDK handles everything — no server-side code required.
40
-
41
- ```html
42
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
43
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
44
- data-widget-id="YOUR_WIDGET_ID"
45
- data-widget-type="store"
46
- data-theme="dark"
47
- data-email="user@example.com"
48
- api-url="YOUR_API_URL"
49
- widget-url="YOUR_WIDGET_URL"
50
- ></script>
51
- ```
52
-
53
- The `data-email` attribute identifies the user. The SDK authenticates directly with the ReturningAI backend on the client side.
54
-
55
- **When to use:** Demos, internal tools, or any scenario where you don't have a backend server.
56
-
57
- ### Access Key Embed (recommended for production)
58
-
59
- 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.
60
-
61
- **Step 1 — Create an access key pair** (one-time, from your dashboard):
62
-
63
- Go to **Community Settings > Integrations > SDK Access** and click **Create SDK Key**. Enter a name and optional allowed origins.
64
-
65
- The dashboard will display your `accessId` and `accessKey`. The `accessKey` is shown **once only** — save it securely.
66
-
67
- Store both values in your server environment variables.
68
-
69
- **Step 2 — Exchange credentials for an embed token on every page load** (Node.js example):
70
-
71
- ```js
72
- // Runs on YOUR SERVER — never in the browser
73
- const response = await fetch('https://YOUR_API_URL/v2/api/widget-access-keys/token', {
74
- method: 'POST',
75
- headers: { 'Content-Type': 'application/json' },
76
- body: JSON.stringify({
77
- accessId: process.env.RAI_ACCESS_ID,
78
- accessKey: process.env.RAI_ACCESS_KEY,
79
- }),
80
- })
81
- const { data } = await response.json()
82
- // data.embedToken expires in 15 minutes
83
- ```
84
-
85
- **Step 3 — Embed** (only the short-lived token appears in HTML):
86
-
87
- ```html
88
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
89
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
90
- data-widget-id="YOUR_WIDGET_ID"
91
- data-widget-type="store"
92
- data-theme="dark"
93
- data-email="user@example.com"
94
- data-embed-token="eyJ..."
95
- api-url="YOUR_API_URL"
96
- widget-url="YOUR_WIDGET_URL"
97
- ></script>
98
- ```
99
-
100
- 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.
101
-
102
- ---
103
-
104
- ## Install
105
-
106
- ### CDN (no install needed)
107
-
108
- ```html
109
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"></script>
110
- ```
111
-
112
- ### npm / yarn / pnpm
113
-
114
- ```bash
115
- npm install @returningai/widget-sdk
116
- ```
117
-
118
- ---
119
-
120
- ## Widget Types
121
-
122
- 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.
123
-
124
- | `data-widget-type` | Description |
125
- |---------------------|-------------|
126
- | `store` | Store / rewards widget |
127
- | `channel` | Channel widget |
128
- | `milestone` | Milestone widget |
129
- | `social` | Social widget |
130
- | `currency-view` | Currency widget |
131
- | `referral-conditions` | Referral widget |
132
- | `custom` | Custom widget |
133
-
134
- ### Store widget
135
-
136
- ```html
137
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
138
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
139
- data-widget-id="YOUR_WIDGET_ID"
140
- data-widget-type="store"
141
- data-theme="dark"
142
- data-email="user@example.com"
143
- api-url="YOUR_API_URL"
144
- widget-url="YOUR_WIDGET_URL"
145
- ></script>
146
- ```
147
-
148
- ### Channel widget
149
-
150
- ```html
151
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
152
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
153
- data-widget-id="YOUR_WIDGET_ID"
154
- data-widget-type="channel"
155
- data-theme="dark"
156
- data-email="user@example.com"
157
- api-url="YOUR_API_URL"
158
- widget-url="YOUR_WIDGET_URL"
159
- ></script>
160
- ```
161
-
162
- > **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.
163
-
164
- ### Social widget
165
-
166
- ```html
167
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
168
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
169
- data-widget-id="YOUR_WIDGET_ID"
170
- data-widget-type="social"
171
- data-theme="dark"
172
- data-email="user@example.com"
173
- api-url="YOUR_API_URL"
174
- widget-url="YOUR_WIDGET_URL"
175
- ></script>
176
- ```
177
-
178
- ### Milestone widget
179
-
180
- ```html
181
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
182
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
183
- data-widget-id="YOUR_WIDGET_ID"
184
- data-widget-type="milestone"
185
- data-theme="dark"
186
- data-email="user@example.com"
187
- api-url="YOUR_API_URL"
188
- widget-url="YOUR_WIDGET_URL"
189
- ></script>
190
- ```
191
-
192
- ### Custom widget
193
-
194
- ```html
195
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
196
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
197
- data-widget-id="YOUR_WIDGET_ID"
198
- data-widget-type="custom"
199
- data-theme="dark"
200
- data-email="user@example.com"
201
- api-url="YOUR_API_URL"
202
- widget-url="YOUR_WIDGET_URL"
203
- ></script>
204
- ```
205
-
206
- ---
207
-
208
- ## Attributes
209
-
210
- There are two ways to identify your widget:
211
-
212
- 1. **Script-tag embed:** Use `data-widget-id` (a pre-encoded ID from your dashboard) on the `<script>` tag
213
- 2. **Custom element embed:** Use `community-id` (and `channel-id` for channel widgets) on the custom element tag
214
-
215
- All attributes can be provided with or without the `data-` prefix — both work.
216
-
217
- ### Script-tag attributes
218
-
219
- | Attribute | Required | Default | Description |
220
- |-----------|----------|---------|-------------|
221
- | `data-widget-id` | **Yes** | — | Your widget ID (from dashboard) |
222
- | `data-widget-type` | **Yes** | `store` | Widget type (see table above) |
223
- | `api-url` | **Yes**\* | — | Auth API base URL (from dashboard) |
224
- | `widget-url` | **Yes**\* | — | Widget URL (from dashboard). \*Not required when using `data-bundle-url` |
225
- | `data-bundle-url` | No | — | URL to the widget IIFE bundle — triggers [bundle mode](#bundle-mode) (non-iframe) |
226
- | `data-domain-key` | Bundle mode | — | Environment key: `SGTR`, `STG`, or `PROD`. Required for bundle mode on external sites |
227
- | `data-theme` | No | `light` | `light` or `dark` |
228
- | `data-email` | Recommended | — | User's email for identification |
229
- | `data-embed-token` | Access Key only | — | Short-lived token from your server |
230
- | `data-locale` | No | — | BCP 47 locale tag (e.g. `fr-FR`) |
231
- | `data-eager` | No | — | Load immediately instead of waiting until visible |
232
- | `data-auto-refresh` | No | `true` | Auto-refresh access token before expiry |
233
- | `data-debug` | No | `false` | Verbose console logging |
234
- | `data-custom-data` | No | — | JSON object forwarded to the widget |
235
-
236
- ### Custom element attributes
237
-
238
- | Attribute | Required | Default | Description |
239
- |-----------|----------|---------|-------------|
240
- | `community-id` | **Yes**\* | — | Your community ID (raw ObjectId from dashboard). \*Custom widgets use `widget-id` instead |
241
- | `channel-id` | **Channel only** | — | Channel ID (raw ObjectId) — required for `<rai-channel-widget>` |
242
- | `api-url` | **Yes** | — | Auth API base URL (from dashboard) |
243
- | `widget-url` | **Yes**\* | — | Widget URL (from dashboard). \*Not required when using `bundle-url` |
244
- | `bundle-url` | No | — | URL to the widget IIFE bundle — triggers [bundle mode](#bundle-mode) (non-iframe) |
245
- | `domain-key` | Bundle mode | — | Environment key: `SGTR`, `STG`, or `PROD`. Required for bundle mode on external sites |
246
- | `theme` | No | `light` | `light` or `dark` |
247
- | `data-email` | Recommended | — | User's email for identification |
248
- | `embed-token` | Access Key only | — | Short-lived token from your server |
249
- | `width` | No | `100%` | CSS width |
250
- | `height` | No | `600px` | CSS height |
251
- | `locale` | No | — | BCP 47 locale tag (e.g. `fr-FR`) |
252
- | `eager` | No | — | Load immediately instead of waiting until visible |
253
- | `auto-refresh` | No | `true` | Auto-refresh access token before expiry |
254
- | `debug` | No | `false` | Verbose console logging |
255
- | `custom-data` | No | — | JSON object forwarded to the widget |
256
-
257
- > **`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.
258
-
259
- ### Container `<div>` (script-tag method only)
260
-
261
- The SDK looks for a `<div>` with `id="returning-ai-widget-{YOUR_WIDGET_ID}"`. Size the div to control the widget dimensions:
262
-
263
- ```html
264
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
265
- ```
266
-
267
- Custom elements don't need a container div — they size themselves using `width` and `height` attributes (defaults: `100%` and `600px`).
268
-
269
- ### User Identifier Attributes
270
-
271
- Pass any `data-*` attribute to identify the current user. Which identifiers are available is configured per community in your dashboard.
272
-
273
- ```html
274
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
275
- data-widget-id="YOUR_WIDGET_ID"
276
- data-widget-type="store"
277
- data-email="user@example.com"
278
- data-user-id="12345"
279
- data-account-id="abc"
280
- api-url="YOUR_API_URL"
281
- widget-url="YOUR_WIDGET_URL"
282
- ></script>
283
- ```
284
-
285
- Any `data-*` attribute not reserved by the SDK is forwarded to the auth API as a user identifier.
286
-
287
- ---
288
-
289
- ## Bundle Mode
290
-
291
- 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.
292
-
293
- `widget-url` is not needed in bundle mode. `domain-key` is required so the widget resolves the correct environment URLs.
294
-
295
- ```html
296
- <!-- Script-tag approach -->
297
- <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:100vh"></div>
298
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
299
- data-widget-id="YOUR_WIDGET_ID"
300
- data-widget-type="store"
301
- data-theme="dark"
302
- data-email="user@example.com"
303
- data-bundle-url="YOUR_BUNDLE_URL"
304
- data-domain-key="YOUR_DOMAIN_KEY"
305
- api-url="YOUR_API_URL"
306
- data-eager
307
- ></script>
308
-
309
- <!-- Custom element approach -->
310
- <rai-store-widget
311
- community-id="YOUR_COMMUNITY_ID"
312
- api-url="YOUR_API_URL"
313
- bundle-url="YOUR_BUNDLE_URL"
314
- domain-key="YOUR_DOMAIN_KEY"
315
- data-email="user@example.com"
316
- theme="dark"
317
- width="100%"
318
- height="100vh"
319
- eager
320
- ></rai-store-widget>
321
- ```
322
-
323
- Each widget type has its own IIFE bundle and global name:
324
-
325
- | Widget type | Bundle global | Custom element tag |
326
- |-------------|--------------|-------------------|
327
- | `store` | `RaiStoreWidget` | `<rai-store-widget>` |
328
- | `channel` | `RaiChannelWidget` | `<rai-channel-widget>` |
329
- | `social` | `RaiSocialWidget` | `<rai-social-widget>` |
330
- | `milestone` | `RaiMilestoneWidget` | `<rai-milestone-widget>` |
331
- | `currency-view` | `RaiCurrencyWidget` | `<rai-currency-widget>` |
332
- | `referral-conditions` | `RaiReferralWidget` | `<rai-referral-widget>` |
333
- | `custom` | `RaiCustomWidget` | `<rai-custom-widget>` |
334
-
335
- The bundle must export a `mount(container, config)` function on its global (e.g. `window.RaiStoreWidget.mount`).
336
-
337
- ---
338
-
339
- ## Framework Usage (Custom Elements)
340
-
341
- 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`.
342
-
343
- ### Plain HTML
344
-
345
- ```html
346
- <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"></script>
347
-
348
- <!-- Store widget -->
349
- <rai-store-widget
350
- community-id="YOUR_COMMUNITY_ID"
351
- theme="dark"
352
- data-email="user@example.com"
353
- api-url="YOUR_API_URL"
354
- widget-url="YOUR_WIDGET_URL"
355
- ></rai-store-widget>
356
-
357
- <!-- Channel widget — requires both community-id AND channel-id -->
358
- <rai-channel-widget
359
- community-id="YOUR_COMMUNITY_ID"
360
- channel-id="YOUR_CHANNEL_ID"
361
- theme="dark"
362
- data-email="user@example.com"
363
- api-url="YOUR_API_URL"
364
- widget-url="YOUR_WIDGET_URL"
365
- ></rai-channel-widget>
366
- ```
367
-
368
- ### React (TypeScript)
369
-
370
- Types are included — no manual JSX declarations needed.
371
-
372
- ```tsx
373
- import '@returningai/widget-sdk'
374
-
375
- export function StoreEmbed({ email }: { email: string }) {
376
- return (
377
- <rai-store-widget
378
- community-id="YOUR_COMMUNITY_ID"
379
- theme="dark"
380
- data-email={email}
381
- api-url="YOUR_API_URL"
382
- widget-url="YOUR_WIDGET_URL"
383
- />
384
- )
385
- }
386
-
387
- export function ChannelEmbed({ email }: { email: string }) {
388
- return (
389
- <rai-channel-widget
390
- community-id="YOUR_COMMUNITY_ID"
391
- channel-id="YOUR_CHANNEL_ID"
392
- theme="dark"
393
- data-email={email}
394
- api-url="YOUR_API_URL"
395
- widget-url="YOUR_WIDGET_URL"
396
- />
397
- )
398
- }
399
- ```
400
-
401
- ### Vue 3
402
-
403
- ```vue
404
- <script setup>
405
- import '@returningai/widget-sdk'
406
- </script>
407
-
408
- <template>
409
- <!-- Store widget -->
410
- <rai-store-widget
411
- community-id="YOUR_COMMUNITY_ID"
412
- theme="dark"
413
- :data-email="user.email"
414
- api-url="YOUR_API_URL"
415
- widget-url="YOUR_WIDGET_URL"
416
- />
417
-
418
- <!-- Channel widget -->
419
- <rai-channel-widget
420
- community-id="YOUR_COMMUNITY_ID"
421
- channel-id="YOUR_CHANNEL_ID"
422
- theme="dark"
423
- :data-email="user.email"
424
- api-url="YOUR_API_URL"
425
- widget-url="YOUR_WIDGET_URL"
426
- />
427
- </template>
428
- ```
429
-
430
- ### Angular
431
-
432
- ```ts
433
- // app.module.ts
434
- import '@returningai/widget-sdk'
435
- import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
436
-
437
- @NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA] })
438
- ```
439
-
440
- ```html
441
- <!-- Store widget -->
442
- <rai-store-widget
443
- community-id="YOUR_COMMUNITY_ID"
444
- theme="dark"
445
- [attr.data-email]="user.email"
446
- api-url="YOUR_API_URL"
447
- widget-url="YOUR_WIDGET_URL"
448
- ></rai-store-widget>
449
-
450
- <!-- Channel widget -->
451
- <rai-channel-widget
452
- community-id="YOUR_COMMUNITY_ID"
453
- channel-id="YOUR_CHANNEL_ID"
454
- theme="dark"
455
- [attr.data-email]="user.email"
456
- api-url="YOUR_API_URL"
457
- widget-url="YOUR_WIDGET_URL"
458
- ></rai-channel-widget>
459
- ```
460
-
461
- ### Available custom element tags
462
-
463
- | Tag | Required attributes | Widget type |
464
- |-----|-------------------|-------------|
465
- | `<rai-store-widget>` | `community-id` | Store |
466
- | `<rai-channel-widget>` | `community-id` + `channel-id` | Channel |
467
- | `<rai-milestone-widget>` | `community-id` | Milestone |
468
- | `<rai-social-widget>` | `community-id` | Social |
469
- | `<rai-currency-widget>` | `community-id` | Currency |
470
- | `<rai-referral-widget>` | `community-id` | Referral |
471
- | `<rai-custom-widget>` | `widget-id` (not `community-id`) | Custom |
472
-
473
- ---
474
-
475
- ## DOM Events
476
-
477
- Listen for widget lifecycle events:
478
-
479
- | Event | `detail` | Fired when |
480
- |-------|----------|-----------|
481
- | `rai-authenticated` | `{}` | Auth succeeded, before widget mounts |
482
- | `rai-ready` | `{}` | `WIDGET_READY` received, loader hidden (iframe mode) |
483
- | `rai-mounted` | `{}` | Widget bundle mounted successfully (bundle mode) |
484
- | `rai-error` | `{ message }` | Auth failed after all retries |
485
- | `rai-logout` | `{}` | Widget logged out |
486
- | `rai-height-change` | `{ height }` | iframe resized (after debounce) |
487
-
488
- ```js
489
- // Script-tag embed
490
- const widget = document.querySelector('[data-widget-id]')
491
-
492
- // Custom element embed
493
- const widget = document.querySelector('rai-store-widget')
494
-
495
- widget.addEventListener('rai-authenticated', () => { /* auth succeeded */ })
496
- widget.addEventListener('rai-ready', () => { /* widget loaded and visible (iframe mode) */ })
497
- widget.addEventListener('rai-mounted', () => { /* widget mounted (bundle mode) */ })
498
- widget.addEventListener('rai-error', (e) => console.error(e.detail.message))
499
- widget.addEventListener('rai-logout', () => { /* user logged out */ })
500
- widget.addEventListener('rai-height-change', (e) => console.log(e.detail.height))
501
- ```
502
-
503
- ---
504
-
505
- ## Public API
506
-
507
- After the SDK loads, `window.ReturningAIWidget` is available:
508
-
509
- ```js
510
- window.ReturningAIWidget.version // current SDK version
511
- await window.ReturningAIWidget.reload() // re-runs auth flow and reloads widget
512
- await window.ReturningAIWidget.logout() // clears tokens and removes widget
513
- window.ReturningAIWidget.isAuthenticated() // boolean
514
- window.ReturningAIWidget.getTokenInfo() // token metadata (no token values exposed)
515
- ```
516
-
517
- ---
518
-
519
- ## Browser Support
520
-
521
- Requires Custom Elements v1 + Shadow DOM v1.
522
-
523
- | Browser | Minimum version |
524
- |---------|----------------|
525
- | Chrome | 67 |
526
- | Firefox | 63 |
527
- | Safari | 13 |
1
+ # @returningai/widget-sdk
2
+
3
+ Embed ReturningAI widgets on any website — React, Vue, Angular, or plain `<script>` tag. No external dependencies.
4
+
5
+ ---
6
+
7
+ ## Quick Start
8
+
9
+ 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.
10
+
11
+ **Step 1 — Create an access key pair** (one-time, from your dashboard):
12
+
13
+ Go to **Community Settings > Integrations > SDK Access** and click **Create SDK Key**. Enter a name and optional allowed origins.
14
+
15
+ The dashboard will display your `accessId` and `accessKey`. The `accessKey` is shown **once only** — save it securely.
16
+
17
+ Store both values in your server environment variables.
18
+
19
+ **Step 2 — Exchange credentials for an embed token on every page load** (Node.js example):
20
+
21
+ ```js
22
+ // Runs on YOUR SERVER — never in the browser
23
+ const response = await fetch('https://YOUR_API_URL/v2/api/widget-access-keys/token', {
24
+ method: 'POST',
25
+ headers: { 'Content-Type': 'application/json' },
26
+ body: JSON.stringify({
27
+ accessId: process.env.RAI_ACCESS_ID,
28
+ accessKey: process.env.RAI_ACCESS_KEY,
29
+ }),
30
+ })
31
+ const { data } = await response.json()
32
+ // data.embedToken expires in 15 minutes
33
+ ```
34
+
35
+ **Step 3 — Embed** (only the short-lived token appears in HTML):
36
+
37
+ ```html
38
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
39
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
40
+ data-widget-id="YOUR_WIDGET_ID"
41
+ data-widget-type="store"
42
+ data-theme="dark"
43
+ data-email="user@example.com"
44
+ data-embed-token="eyJ..."
45
+ api-url="YOUR_API_URL"
46
+ widget-url="YOUR_WIDGET_URL"
47
+ ></script>
48
+ ```
49
+
50
+ 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.
51
+
52
+ ---
53
+
54
+ ## Install
55
+
56
+ ### CDN (no install needed)
57
+
58
+ ```html
59
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"></script>
60
+ ```
61
+
62
+ ### npm / yarn / pnpm
63
+
64
+ ```bash
65
+ npm install @returningai/widget-sdk
66
+ ```
67
+
68
+ ---
69
+
70
+ ## Widget Types
71
+
72
+ 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.
73
+
74
+ | `data-widget-type` | Description |
75
+ |---------------------|-------------|
76
+ | `store` | Store / rewards widget |
77
+ | `channel` | Channel widget |
78
+ | `milestone` | Milestone widget |
79
+ | `social` | Social widget |
80
+ | `currency-view` | Currency widget |
81
+ | `referral-conditions` | Referral widget |
82
+ | `custom` | Custom widget |
83
+
84
+ ### Store widget
85
+
86
+ ```html
87
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
88
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
89
+ data-widget-id="YOUR_WIDGET_ID"
90
+ data-widget-type="store"
91
+ data-theme="dark"
92
+ data-email="user@example.com"
93
+ api-url="YOUR_API_URL"
94
+ widget-url="YOUR_WIDGET_URL"
95
+ ></script>
96
+ ```
97
+
98
+ ### Channel widget
99
+
100
+ ```html
101
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
102
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
103
+ data-widget-id="YOUR_WIDGET_ID"
104
+ data-widget-type="channel"
105
+ data-theme="dark"
106
+ data-email="user@example.com"
107
+ api-url="YOUR_API_URL"
108
+ widget-url="YOUR_WIDGET_URL"
109
+ ></script>
110
+ ```
111
+
112
+ > **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.
113
+
114
+ ### Social widget
115
+
116
+ ```html
117
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
118
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
119
+ data-widget-id="YOUR_WIDGET_ID"
120
+ data-widget-type="social"
121
+ data-theme="dark"
122
+ data-email="user@example.com"
123
+ api-url="YOUR_API_URL"
124
+ widget-url="YOUR_WIDGET_URL"
125
+ ></script>
126
+ ```
127
+
128
+ ### Milestone widget
129
+
130
+ ```html
131
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
132
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
133
+ data-widget-id="YOUR_WIDGET_ID"
134
+ data-widget-type="milestone"
135
+ data-theme="dark"
136
+ data-email="user@example.com"
137
+ api-url="YOUR_API_URL"
138
+ widget-url="YOUR_WIDGET_URL"
139
+ ></script>
140
+ ```
141
+
142
+ ### Custom widget
143
+
144
+ ```html
145
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
146
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
147
+ data-widget-id="YOUR_WIDGET_ID"
148
+ data-widget-type="custom"
149
+ data-theme="dark"
150
+ data-email="user@example.com"
151
+ api-url="YOUR_API_URL"
152
+ widget-url="YOUR_WIDGET_URL"
153
+ ></script>
154
+ ```
155
+
156
+ ---
157
+
158
+ ## Attributes
159
+
160
+ There are two ways to identify your widget:
161
+
162
+ 1. **Script-tag embed:** Use `data-widget-id` (a pre-encoded ID from your dashboard) on the `<script>` tag
163
+ 2. **Custom element embed:** Use `community-id` (and `channel-id` for channel widgets) on the custom element tag
164
+
165
+ All attributes can be provided with or without the `data-` prefix — both work.
166
+
167
+ ### Script-tag attributes
168
+
169
+ | Attribute | Required | Default | Description |
170
+ |-----------|----------|---------|-------------|
171
+ | `data-widget-id` | **Yes** | — | Your widget ID (from dashboard) |
172
+ | `data-widget-type` | **Yes** | `store` | Widget type (see table above) |
173
+ | `api-url` | **Yes**\* | — | Auth API base URL (from dashboard) |
174
+ | `widget-url` | **Yes**\* | — | Widget URL (from dashboard). \*Not required when using `data-bundle-url` |
175
+ | `data-bundle-url` | No | — | URL to the widget IIFE bundle — triggers [bundle mode](#bundle-mode) (non-iframe) |
176
+ | `data-domain-key` | Bundle mode | — | Environment key: `SGTR`, `STG`, or `PROD`. Required for bundle mode on external sites |
177
+ | `data-theme` | No | `light` | `light` or `dark` |
178
+ | `data-email` | Recommended | — | User's email for identification |
179
+ | `data-embed-token` | Access Key only | — | Short-lived token from your server |
180
+ | `data-locale` | No | — | BCP 47 locale tag (e.g. `fr-FR`) |
181
+ | `data-eager` | No | — | Load immediately instead of waiting until visible |
182
+ | `data-auto-refresh` | No | `true` | Auto-refresh access token before expiry |
183
+ | `data-debug` | No | `false` | Verbose console logging |
184
+ | `data-custom-data` | No | — | JSON object forwarded to the widget |
185
+
186
+ ### Custom element attributes
187
+
188
+ | Attribute | Required | Default | Description |
189
+ |-----------|----------|---------|-------------|
190
+ | `community-id` | **Yes**\* | — | Your community ID (raw ObjectId from dashboard). \*Custom widgets use `widget-id` instead |
191
+ | `channel-id` | **Channel only** | — | Channel ID (raw ObjectId) — required for `<rai-channel-widget>` |
192
+ | `api-url` | **Yes** | — | Auth API base URL (from dashboard) |
193
+ | `widget-url` | **Yes**\* | — | Widget URL (from dashboard). \*Not required when using `bundle-url` |
194
+ | `bundle-url` | No | — | URL to the widget IIFE bundle — triggers [bundle mode](#bundle-mode) (non-iframe) |
195
+ | `domain-key` | Bundle mode | — | Environment key: `SGTR`, `STG`, or `PROD`. Required for bundle mode on external sites |
196
+ | `theme` | No | `light` | `light` or `dark` |
197
+ | `data-email` | Recommended | — | User's email for identification |
198
+ | `embed-token` | Access Key only | — | Short-lived token from your server |
199
+ | `width` | No | `100%` | CSS width |
200
+ | `height` | No | `600px` | CSS height |
201
+ | `locale` | No | — | BCP 47 locale tag (e.g. `fr-FR`) |
202
+ | `eager` | No | — | Load immediately instead of waiting until visible |
203
+ | `auto-refresh` | No | `true` | Auto-refresh access token before expiry |
204
+ | `debug` | No | `false` | Verbose console logging |
205
+ | `custom-data` | No | — | JSON object forwarded to the widget |
206
+
207
+ > **`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.
208
+
209
+ ### Container `<div>` (script-tag method only)
210
+
211
+ The SDK looks for a `<div>` with `id="returning-ai-widget-{YOUR_WIDGET_ID}"`. Size the div to control the widget dimensions:
212
+
213
+ ```html
214
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:600px"></div>
215
+ ```
216
+
217
+ Custom elements don't need a container div — they size themselves using `width` and `height` attributes (defaults: `100%` and `600px`).
218
+
219
+ ### User Identifier Attributes
220
+
221
+ Pass any `data-*` attribute to identify the current user. Which identifiers are available is configured per community in your dashboard.
222
+
223
+ ```html
224
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
225
+ data-widget-id="YOUR_WIDGET_ID"
226
+ data-widget-type="store"
227
+ data-email="user@example.com"
228
+ data-user-id="12345"
229
+ data-account-id="abc"
230
+ api-url="YOUR_API_URL"
231
+ widget-url="YOUR_WIDGET_URL"
232
+ ></script>
233
+ ```
234
+
235
+ Any `data-*` attribute not reserved by the SDK is forwarded to the auth API as a user identifier.
236
+
237
+ ---
238
+
239
+ ## Bundle Mode
240
+
241
+ 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.
242
+
243
+ `widget-url` is not needed in bundle mode. `domain-key` is required so the widget resolves the correct environment URLs.
244
+
245
+ ```html
246
+ <!-- Script-tag approach -->
247
+ <div id="returning-ai-widget-YOUR_WIDGET_ID" style="width:100%;height:100vh"></div>
248
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"
249
+ data-widget-id="YOUR_WIDGET_ID"
250
+ data-widget-type="store"
251
+ data-theme="dark"
252
+ data-email="user@example.com"
253
+ data-bundle-url="YOUR_BUNDLE_URL"
254
+ data-domain-key="YOUR_DOMAIN_KEY"
255
+ api-url="YOUR_API_URL"
256
+ data-eager
257
+ ></script>
258
+
259
+ <!-- Custom element approach -->
260
+ <rai-store-widget
261
+ community-id="YOUR_COMMUNITY_ID"
262
+ api-url="YOUR_API_URL"
263
+ bundle-url="YOUR_BUNDLE_URL"
264
+ domain-key="YOUR_DOMAIN_KEY"
265
+ data-email="user@example.com"
266
+ theme="dark"
267
+ width="100%"
268
+ height="100vh"
269
+ eager
270
+ ></rai-store-widget>
271
+ ```
272
+
273
+ Each widget type has its own IIFE bundle and global name:
274
+
275
+ | Widget type | Bundle global | Custom element tag |
276
+ |-------------|--------------|-------------------|
277
+ | `store` | `RaiStoreWidget` | `<rai-store-widget>` |
278
+ | `channel` | `RaiChannelWidget` | `<rai-channel-widget>` |
279
+ | `social` | `RaiSocialWidget` | `<rai-social-widget>` |
280
+ | `milestone` | `RaiMilestoneWidget` | `<rai-milestone-widget>` |
281
+ | `currency-view` | `RaiCurrencyWidget` | `<rai-currency-widget>` |
282
+ | `referral-conditions` | `RaiReferralWidget` | `<rai-referral-widget>` |
283
+ | `custom` | `RaiCustomWidget` | `<rai-custom-widget>` |
284
+
285
+ The bundle must export a `mount(container, config)` function on its global (e.g. `window.RaiStoreWidget.mount`).
286
+
287
+ ---
288
+
289
+ ## Framework Usage (Custom Elements)
290
+
291
+ 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`.
292
+
293
+ ### Plain HTML
294
+
295
+ ```html
296
+ <script src="https://unpkg.com/@returningai/widget-sdk/dist/rai-widget.iife.js"></script>
297
+
298
+ <!-- Store widget -->
299
+ <rai-store-widget
300
+ community-id="YOUR_COMMUNITY_ID"
301
+ theme="dark"
302
+ data-email="user@example.com"
303
+ api-url="YOUR_API_URL"
304
+ widget-url="YOUR_WIDGET_URL"
305
+ ></rai-store-widget>
306
+
307
+ <!-- Channel widget — requires both community-id AND channel-id -->
308
+ <rai-channel-widget
309
+ community-id="YOUR_COMMUNITY_ID"
310
+ channel-id="YOUR_CHANNEL_ID"
311
+ theme="dark"
312
+ data-email="user@example.com"
313
+ api-url="YOUR_API_URL"
314
+ widget-url="YOUR_WIDGET_URL"
315
+ ></rai-channel-widget>
316
+ ```
317
+
318
+ ### React (TypeScript)
319
+
320
+ Types are included — no manual JSX declarations needed.
321
+
322
+ ```tsx
323
+ import '@returningai/widget-sdk'
324
+
325
+ export function StoreEmbed({ email }: { email: string }) {
326
+ return (
327
+ <rai-store-widget
328
+ community-id="YOUR_COMMUNITY_ID"
329
+ theme="dark"
330
+ data-email={email}
331
+ api-url="YOUR_API_URL"
332
+ widget-url="YOUR_WIDGET_URL"
333
+ />
334
+ )
335
+ }
336
+
337
+ export function ChannelEmbed({ email }: { email: string }) {
338
+ return (
339
+ <rai-channel-widget
340
+ community-id="YOUR_COMMUNITY_ID"
341
+ channel-id="YOUR_CHANNEL_ID"
342
+ theme="dark"
343
+ data-email={email}
344
+ api-url="YOUR_API_URL"
345
+ widget-url="YOUR_WIDGET_URL"
346
+ />
347
+ )
348
+ }
349
+ ```
350
+
351
+ ### Vue 3
352
+
353
+ ```vue
354
+ <script setup>
355
+ import '@returningai/widget-sdk'
356
+ </script>
357
+
358
+ <template>
359
+ <!-- Store widget -->
360
+ <rai-store-widget
361
+ community-id="YOUR_COMMUNITY_ID"
362
+ theme="dark"
363
+ :data-email="user.email"
364
+ api-url="YOUR_API_URL"
365
+ widget-url="YOUR_WIDGET_URL"
366
+ />
367
+
368
+ <!-- Channel widget -->
369
+ <rai-channel-widget
370
+ community-id="YOUR_COMMUNITY_ID"
371
+ channel-id="YOUR_CHANNEL_ID"
372
+ theme="dark"
373
+ :data-email="user.email"
374
+ api-url="YOUR_API_URL"
375
+ widget-url="YOUR_WIDGET_URL"
376
+ />
377
+ </template>
378
+ ```
379
+
380
+ ### Angular
381
+
382
+ ```ts
383
+ // app.module.ts
384
+ import '@returningai/widget-sdk'
385
+ import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'
386
+
387
+ @NgModule({ schemas: [CUSTOM_ELEMENTS_SCHEMA] })
388
+ ```
389
+
390
+ ```html
391
+ <!-- Store widget -->
392
+ <rai-store-widget
393
+ community-id="YOUR_COMMUNITY_ID"
394
+ theme="dark"
395
+ [attr.data-email]="user.email"
396
+ api-url="YOUR_API_URL"
397
+ widget-url="YOUR_WIDGET_URL"
398
+ ></rai-store-widget>
399
+
400
+ <!-- Channel widget -->
401
+ <rai-channel-widget
402
+ community-id="YOUR_COMMUNITY_ID"
403
+ channel-id="YOUR_CHANNEL_ID"
404
+ theme="dark"
405
+ [attr.data-email]="user.email"
406
+ api-url="YOUR_API_URL"
407
+ widget-url="YOUR_WIDGET_URL"
408
+ ></rai-channel-widget>
409
+ ```
410
+
411
+ ### Available custom element tags
412
+
413
+ | Tag | Required attributes | Widget type |
414
+ |-----|-------------------|-------------|
415
+ | `<rai-store-widget>` | `community-id` | Store |
416
+ | `<rai-channel-widget>` | `community-id` + `channel-id` | Channel |
417
+ | `<rai-milestone-widget>` | `community-id` | Milestone |
418
+ | `<rai-social-widget>` | `community-id` | Social |
419
+ | `<rai-currency-widget>` | `community-id` | Currency |
420
+ | `<rai-referral-widget>` | `community-id` | Referral |
421
+ | `<rai-custom-widget>` | `widget-id` (not `community-id`) | Custom |
422
+
423
+ ---
424
+
425
+ ## DOM Events
426
+
427
+ Listen for widget lifecycle events:
428
+
429
+ | Event | `detail` | Fired when |
430
+ |-------|----------|-----------|
431
+ | `rai-authenticated` | `{}` | Auth succeeded, before widget mounts |
432
+ | `rai-ready` | `{}` | `WIDGET_READY` received, loader hidden (iframe mode) |
433
+ | `rai-mounted` | `{}` | Widget bundle mounted successfully (bundle mode) |
434
+ | `rai-error` | `{ message }` | Auth failed after all retries |
435
+ | `rai-logout` | `{}` | Widget logged out |
436
+ | `rai-height-change` | `{ height }` | iframe resized (after debounce) |
437
+
438
+ ```js
439
+ // Script-tag embed
440
+ const widget = document.querySelector('[data-widget-id]')
441
+
442
+ // Custom element embed
443
+ const widget = document.querySelector('rai-store-widget')
444
+
445
+ widget.addEventListener('rai-authenticated', () => { /* auth succeeded */ })
446
+ widget.addEventListener('rai-ready', () => { /* widget loaded and visible (iframe mode) */ })
447
+ widget.addEventListener('rai-mounted', () => { /* widget mounted (bundle mode) */ })
448
+ widget.addEventListener('rai-error', (e) => console.error(e.detail.message))
449
+ widget.addEventListener('rai-logout', () => { /* user logged out */ })
450
+ widget.addEventListener('rai-height-change', (e) => console.log(e.detail.height))
451
+ ```
452
+
453
+ ---
454
+
455
+ ## Public API
456
+
457
+ After the SDK loads, `window.ReturningAIWidget` is available:
458
+
459
+ ```js
460
+ window.ReturningAIWidget.version // current SDK version
461
+ await window.ReturningAIWidget.reload() // re-runs auth flow and reloads widget
462
+ await window.ReturningAIWidget.logout() // clears tokens and removes widget
463
+ window.ReturningAIWidget.isAuthenticated() // boolean
464
+ window.ReturningAIWidget.getTokenInfo() // token metadata (no token values exposed)
465
+ ```
466
+
467
+ ---
468
+
469
+ ## Browser Support
470
+
471
+ Requires Custom Elements v1 + Shadow DOM v1.
472
+
473
+ | Browser | Minimum version |
474
+ |---------|----------------|
475
+ | Chrome | 67 |
476
+ | Firefox | 63 |
477
+ | 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 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="PROD"===this.config.domainKey?"GEN":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.15");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.15",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="PROD"===this.config.domainKey?"GEN":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.16");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.16",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}({});
@@ -569,7 +569,7 @@ function readConfig(el, existingId) {
569
569
  v2ApiUrl: get("v2-api-url") || void 0,
570
570
  // separate V2 API host for embed-token validation
571
571
  domainKey: get("domain-key") || void 0
572
- // SGTR, STG, PROD — controls IIFE environment resolution (PROD maps to GEN internally)
572
+ // SGTR, STG, PROD — controls IIFE environment resolution
573
573
  };
574
574
  }
575
575
  function createInitialState() {
@@ -1119,7 +1119,7 @@ class CustomWidget extends BaseWidget {
1119
1119
  }
1120
1120
  }
1121
1121
  }
1122
- console.log(`[rai-widget] v${"1.2.15"}`);
1122
+ console.log(`[rai-widget] v${"1.2.16"}`);
1123
1123
  class StoreWidgetCompat extends StoreWidget {
1124
1124
  }
1125
1125
  const WIDGET_REGISTRY = [
@@ -1183,7 +1183,7 @@ function exposePublicApi() {
1183
1183
  })();
1184
1184
  const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
1185
1185
  window.ReturningAIWidget = {
1186
- version: "1.2.15",
1186
+ version: "1.2.16",
1187
1187
  reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
1188
1188
  logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
1189
1189
  isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@returningai/widget-sdk",
3
- "version": "1.2.15",
3
+ "version": "1.2.16",
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",