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