@returningai/widget-sdk 1.0.2 → 1.1.0
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 +225 -89
- package/dist/rai-widget.iife.js +1 -1
- package/dist/rai-widget.js +435 -92
- package/dist/types/BaseWidget.d.ts +5 -0
- package/dist/types/core/auth.d.ts +2 -0
- package/dist/types/core/postmessage.d.ts +1 -1
- package/dist/types/types.d.ts +17 -0
- package/package.json +1 -1
package/dist/rai-widget.js
CHANGED
|
@@ -81,33 +81,93 @@ function setTokens(config, state, data, onRefreshScheduled) {
|
|
|
81
81
|
async function authenticateServerless(config, state, onRefreshScheduled) {
|
|
82
82
|
const nonce = crypto.randomUUID();
|
|
83
83
|
const timestamp = Date.now();
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
widgetType: config.widgetType,
|
|
92
|
-
userIdentifiers: config.userIdentifiers
|
|
93
|
-
}),
|
|
94
|
-
credentials: "include"
|
|
95
|
-
});
|
|
96
|
-
if (!response.ok) {
|
|
97
|
-
const error = await response.json().catch(() => ({ error: "Authentication failed" }));
|
|
98
|
-
throw new Error(error.error || `HTTP ${response.status}`);
|
|
84
|
+
const maxRetries = config.maxRetries ?? 3;
|
|
85
|
+
const retryDelay = config.retryDelay ?? 500;
|
|
86
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
87
|
+
if (attempt > 0) {
|
|
88
|
+
await new Promise(
|
|
89
|
+
(resolve) => setTimeout(resolve, retryDelay * (1 << attempt - 1))
|
|
90
|
+
);
|
|
99
91
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
92
|
+
let is4xx = false;
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch(`${config.apiUrl}/${config.widgetId}/auth/serverless`, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: { "Content-Type": "application/json" },
|
|
97
|
+
body: JSON.stringify({
|
|
98
|
+
nonce,
|
|
99
|
+
timestamp,
|
|
100
|
+
widgetType: config.widgetType,
|
|
101
|
+
userIdentifiers: config.userIdentifiers
|
|
102
|
+
}),
|
|
103
|
+
credentials: "include"
|
|
104
|
+
});
|
|
105
|
+
if (!response.ok) {
|
|
106
|
+
is4xx = response.status >= 400 && response.status < 500;
|
|
107
|
+
const error = await response.json().catch(() => ({ error: "Authentication failed" }));
|
|
108
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
109
|
+
}
|
|
110
|
+
const data = await response.json();
|
|
111
|
+
if (!data.accessToken || !data.refreshToken) {
|
|
112
|
+
throw new Error("Invalid authentication response");
|
|
113
|
+
}
|
|
114
|
+
setTokens(config, state, data, onRefreshScheduled);
|
|
115
|
+
state.isAuthenticated = true;
|
|
116
|
+
return true;
|
|
117
|
+
} catch {
|
|
118
|
+
if (is4xx || attempt === maxRetries) {
|
|
119
|
+
state.isAuthenticated = false;
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
103
122
|
}
|
|
104
|
-
setTokens(config, state, data, onRefreshScheduled);
|
|
105
|
-
state.isAuthenticated = true;
|
|
106
|
-
return true;
|
|
107
|
-
} catch {
|
|
108
|
-
state.isAuthenticated = false;
|
|
109
|
-
return false;
|
|
110
123
|
}
|
|
124
|
+
state.isAuthenticated = false;
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
async function authenticateViaProxy(config, state, onRefreshScheduled) {
|
|
128
|
+
const maxRetries = config.maxRetries ?? 3;
|
|
129
|
+
const retryDelay = config.retryDelay ?? 500;
|
|
130
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
131
|
+
if (attempt > 0) {
|
|
132
|
+
await new Promise(
|
|
133
|
+
(resolve) => setTimeout(resolve, retryDelay * (1 << attempt - 1))
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
let is4xx = false;
|
|
137
|
+
try {
|
|
138
|
+
const response = await fetch(config.authUrl, {
|
|
139
|
+
method: "POST",
|
|
140
|
+
credentials: "include",
|
|
141
|
+
headers: { "Content-Type": "application/json" }
|
|
142
|
+
});
|
|
143
|
+
if (!response.ok) {
|
|
144
|
+
is4xx = response.status >= 400 && response.status < 500;
|
|
145
|
+
throw new Error(`HTTP ${response.status}`);
|
|
146
|
+
}
|
|
147
|
+
const data = await response.json();
|
|
148
|
+
if (!data.token) {
|
|
149
|
+
throw new Error("Invalid proxy auth response");
|
|
150
|
+
}
|
|
151
|
+
const ttl = data.expiresIn ?? 300;
|
|
152
|
+
state.accessToken = data.token;
|
|
153
|
+
state.refreshToken = null;
|
|
154
|
+
state.tokenFamily = null;
|
|
155
|
+
state.accessTokenExpiry = Date.now() + ttl * 1e3;
|
|
156
|
+
state.refreshTokenExpiry = null;
|
|
157
|
+
state.isAuthenticated = true;
|
|
158
|
+
if (config.autoRefresh && onRefreshScheduled) {
|
|
159
|
+
onRefreshScheduled();
|
|
160
|
+
}
|
|
161
|
+
return true;
|
|
162
|
+
} catch {
|
|
163
|
+
if (is4xx || attempt === maxRetries) {
|
|
164
|
+
state.isAuthenticated = false;
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
state.isAuthenticated = false;
|
|
170
|
+
return false;
|
|
111
171
|
}
|
|
112
172
|
async function refreshAccessToken(config, state, onRefreshScheduled, sendToken) {
|
|
113
173
|
if (state.isRefreshing) {
|
|
@@ -158,6 +218,11 @@ async function getValidToken(config, state, onRefreshScheduled) {
|
|
|
158
218
|
if (state.accessToken && !isTokenExpired(state.accessTokenExpiry)) {
|
|
159
219
|
return state.accessToken;
|
|
160
220
|
}
|
|
221
|
+
if (config.authUrl) {
|
|
222
|
+
const authed2 = await authenticateViaProxy(config, state, onRefreshScheduled);
|
|
223
|
+
if (authed2 && state.accessToken) return state.accessToken;
|
|
224
|
+
throw new Error("Unable to obtain valid token");
|
|
225
|
+
}
|
|
161
226
|
const refreshed = await refreshAccessToken(config, state, onRefreshScheduled);
|
|
162
227
|
if (refreshed && state.accessToken) return state.accessToken;
|
|
163
228
|
const authed = await authenticateServerless(config, state, onRefreshScheduled);
|
|
@@ -181,6 +246,18 @@ async function logout(config, state) {
|
|
|
181
246
|
clearState(state);
|
|
182
247
|
clearStorage(config);
|
|
183
248
|
}
|
|
249
|
+
async function validateEmbedToken(config) {
|
|
250
|
+
try {
|
|
251
|
+
const res = await fetch(`${config.apiUrl}/v2/api/widget-access-keys/validate`, {
|
|
252
|
+
method: "POST",
|
|
253
|
+
headers: { "Content-Type": "application/json" },
|
|
254
|
+
body: JSON.stringify({ embedToken: config.embedToken })
|
|
255
|
+
});
|
|
256
|
+
return res.ok;
|
|
257
|
+
} catch {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
184
261
|
async function fetchErrorSettings(config, state) {
|
|
185
262
|
const cached = loadErrorSettingsFromStorage(config);
|
|
186
263
|
if (cached) {
|
|
@@ -231,6 +308,24 @@ function clearState(state) {
|
|
|
231
308
|
state.syncTimer = null;
|
|
232
309
|
}
|
|
233
310
|
}
|
|
311
|
+
function debounce(fn, wait) {
|
|
312
|
+
let timer = null;
|
|
313
|
+
return {
|
|
314
|
+
call(h) {
|
|
315
|
+
if (timer) clearTimeout(timer);
|
|
316
|
+
timer = setTimeout(() => {
|
|
317
|
+
fn(h);
|
|
318
|
+
timer = null;
|
|
319
|
+
}, wait);
|
|
320
|
+
},
|
|
321
|
+
cancel() {
|
|
322
|
+
if (timer) {
|
|
323
|
+
clearTimeout(timer);
|
|
324
|
+
timer = null;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
};
|
|
328
|
+
}
|
|
234
329
|
function sendTokenToWidget(config, state, iframe) {
|
|
235
330
|
var _a;
|
|
236
331
|
if (!state.accessToken) return;
|
|
@@ -240,7 +335,8 @@ function sendTokenToWidget(config, state, iframe) {
|
|
|
240
335
|
type: "RETURNINGAI_WIDGET_TOKEN",
|
|
241
336
|
value: {
|
|
242
337
|
widgetId: config.widgetId,
|
|
243
|
-
token: state.accessToken
|
|
338
|
+
token: state.accessToken,
|
|
339
|
+
...config.customData !== void 0 && { customData: config.customData }
|
|
244
340
|
}
|
|
245
341
|
},
|
|
246
342
|
config.widgetDomain
|
|
@@ -248,7 +344,11 @@ function sendTokenToWidget(config, state, iframe) {
|
|
|
248
344
|
} catch {
|
|
249
345
|
}
|
|
250
346
|
}
|
|
251
|
-
function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled, onLogout, hideLoader) {
|
|
347
|
+
function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled, onLogout, hideLoader, emit) {
|
|
348
|
+
const heightSetter = debounce((h) => {
|
|
349
|
+
iframe.style.height = `${h}px`;
|
|
350
|
+
emit == null ? void 0 : emit("rai-height-change", { height: h });
|
|
351
|
+
}, config.heightDebounce ?? 100);
|
|
252
352
|
const handler = async (event) => {
|
|
253
353
|
var _a;
|
|
254
354
|
if (event.origin !== config.widgetDomain) return;
|
|
@@ -261,7 +361,11 @@ function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled
|
|
|
261
361
|
(_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
|
|
262
362
|
{
|
|
263
363
|
type: "RETURNINGAI_WIDGET_TOKEN",
|
|
264
|
-
value: {
|
|
364
|
+
value: {
|
|
365
|
+
token,
|
|
366
|
+
widgetId: config.widgetId,
|
|
367
|
+
...config.customData !== void 0 && { customData: config.customData }
|
|
368
|
+
}
|
|
265
369
|
},
|
|
266
370
|
config.widgetDomain
|
|
267
371
|
);
|
|
@@ -272,7 +376,7 @@ function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled
|
|
|
272
376
|
case "WIDGET_HEIGHT_UPDATE": {
|
|
273
377
|
const h = Number(payload == null ? void 0 : payload.height);
|
|
274
378
|
if (Number.isFinite(h) && h > 0) {
|
|
275
|
-
|
|
379
|
+
heightSetter.call(h);
|
|
276
380
|
}
|
|
277
381
|
break;
|
|
278
382
|
}
|
|
@@ -281,6 +385,7 @@ function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled
|
|
|
281
385
|
if (containerId !== void 0 && containerId !== config.container) break;
|
|
282
386
|
iframe.classList.add("loaded");
|
|
283
387
|
if (hideLoader) hideLoader();
|
|
388
|
+
emit == null ? void 0 : emit("rai-ready");
|
|
284
389
|
break;
|
|
285
390
|
}
|
|
286
391
|
case "WIDGET_ERROR": {
|
|
@@ -294,11 +399,17 @@ function setupMessageListener(config, state, _shadow, iframe, onRefreshScheduled
|
|
|
294
399
|
}
|
|
295
400
|
};
|
|
296
401
|
window.addEventListener("message", handler);
|
|
297
|
-
return () =>
|
|
402
|
+
return () => {
|
|
403
|
+
window.removeEventListener("message", handler);
|
|
404
|
+
heightSetter.cancel();
|
|
405
|
+
};
|
|
298
406
|
}
|
|
299
|
-
const widgetCSS = ":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}.loader{position:relative;width:75px;height:100px}.loader__bar{position:absolute;bottom:0;width:10px;height:50%;background:var(--rai-accent, #000000);transform-origin:center bottom;box-shadow:1px 1px #0003}.loader__bar:nth-child(1){left:0;transform:scaleY(.2);animation:barUp1 4s infinite}.loader__bar:nth-child(2){left:15px;transform:scaleY(.4);animation:barUp2 4s infinite}.loader__bar:nth-child(3){left:30px;transform:scaleY(.6);animation:barUp3 4s infinite}.loader__bar:nth-child(4){left:45px;transform:scaleY(.8);animation:barUp4 4s infinite}.loader__bar:nth-child(5){left:60px;transform:scale(1);animation:barUp5 4s infinite}.loader__ball{position:absolute;bottom:10px;left:0;width:10px;height:10px;background:var(--rai-accent, #000000);border-radius:50%;animation:ball 4s infinite}@keyframes 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 barUp1{0%{transform:scaleY(.2)}40%{transform:scaleY(.2)}50%{transform:scale(1)}90%{transform:scale(1)}to{transform:scaleY(.2)}}@keyframes barUp2{0%{transform:scaleY(.4)}40%{transform:scaleY(.4)}50%{transform:scaleY(.8)}90%{transform:scaleY(.8)}to{transform:scaleY(.4)}}@keyframes barUp3{0%{transform:scaleY(.6)}to{transform:scaleY(.6)}}@keyframes barUp4{0%{transform:scaleY(.8)}40%{transform:scaleY(.8)}50%{transform:scaleY(.4)}90%{transform:scaleY(.4)}to{transform:scaleY(.8)}}@keyframes 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)}}";
|
|
407
|
+
const widgetCSS = ":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-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-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}";
|
|
300
408
|
const DEFINED_ATTRS = /* @__PURE__ */ new Set([
|
|
409
|
+
"community-id",
|
|
410
|
+
"channel-id",
|
|
301
411
|
"widget-id",
|
|
412
|
+
// deprecated — 'community-id' is the canonical name
|
|
302
413
|
"widget-type",
|
|
303
414
|
"theme",
|
|
304
415
|
"container",
|
|
@@ -307,14 +418,76 @@ const DEFINED_ATTRS = /* @__PURE__ */ new Set([
|
|
|
307
418
|
"api-url",
|
|
308
419
|
"widget-url",
|
|
309
420
|
"auto-refresh",
|
|
310
|
-
"debug"
|
|
421
|
+
"debug",
|
|
422
|
+
// New attrs — excluded from userIdentifiers
|
|
423
|
+
"storage-prefix",
|
|
424
|
+
"max-retries",
|
|
425
|
+
"retry-delay",
|
|
426
|
+
"height-debounce",
|
|
427
|
+
"locale",
|
|
428
|
+
"custom-data",
|
|
429
|
+
"retry-label",
|
|
430
|
+
"auth-url",
|
|
431
|
+
"bundle-url",
|
|
432
|
+
"embed-token"
|
|
311
433
|
]);
|
|
434
|
+
const WIDGET_GLOBALS = {
|
|
435
|
+
store: "RaiStoreWidget",
|
|
436
|
+
channel: "RaiChannelWidget",
|
|
437
|
+
social: "RaiSocialWidget",
|
|
438
|
+
milestone: "RaiMilestoneWidget",
|
|
439
|
+
"currency-view": "RaiCurrencyWidget"
|
|
440
|
+
};
|
|
441
|
+
const TAG_TO_TYPE = {
|
|
442
|
+
"RAI-STORE-WIDGET": "store",
|
|
443
|
+
"RAI-CHANNEL-WIDGET": "channel",
|
|
444
|
+
"RAI-MILESTONE-WIDGET": "milestone",
|
|
445
|
+
"RAI-SOCIAL-WIDGET": "social",
|
|
446
|
+
"RAI-CURRENCY-WIDGET": "currency-view",
|
|
447
|
+
"RAI-WIDGET": "store"
|
|
448
|
+
// deprecated alias
|
|
449
|
+
};
|
|
450
|
+
function loadScript(url) {
|
|
451
|
+
return new Promise((resolve, reject) => {
|
|
452
|
+
const s = document.createElement("script");
|
|
453
|
+
s.src = url;
|
|
454
|
+
s.onload = () => resolve();
|
|
455
|
+
s.onerror = () => reject(new Error(`Failed to load ${url}`));
|
|
456
|
+
document.head.appendChild(s);
|
|
457
|
+
});
|
|
458
|
+
}
|
|
312
459
|
function readConfig(el, existingId) {
|
|
313
460
|
const get = (name) => el.getAttribute(name) ?? el.getAttribute(`data-${name}`) ?? "";
|
|
461
|
+
const num = (name, def) => {
|
|
462
|
+
const v = parseInt(get(name), 10);
|
|
463
|
+
return Number.isFinite(v) && v >= 0 ? v : def;
|
|
464
|
+
};
|
|
465
|
+
const usesExplicitIds = !!get("community-id");
|
|
466
|
+
const communityId = get("community-id") || get("widget-id") || existingId || "";
|
|
467
|
+
if (communityId && !/^[a-zA-Z0-9_\-=]{8,}$/.test(communityId)) {
|
|
468
|
+
throw new Error(`[rai-widget] Invalid community-id: "${communityId}"`);
|
|
469
|
+
}
|
|
470
|
+
const channelId = get("channel-id") || void 0;
|
|
471
|
+
if (channelId && !/^[a-zA-Z0-9_\-=]{8,}$/.test(channelId)) {
|
|
472
|
+
throw new Error(`[rai-widget] Invalid channel-id: "${channelId}"`);
|
|
473
|
+
}
|
|
474
|
+
const widgetType = get("widget-type") || TAG_TO_TYPE[el.tagName] || "store";
|
|
475
|
+
let widgetId;
|
|
476
|
+
if (usesExplicitIds) {
|
|
477
|
+
if (widgetType === "store") {
|
|
478
|
+
widgetId = communityId;
|
|
479
|
+
} else if (widgetType === "channel") {
|
|
480
|
+
widgetId = channelId ? btoa(channelId) : communityId;
|
|
481
|
+
} else {
|
|
482
|
+
widgetId = communityId ? btoa(communityId) : communityId;
|
|
483
|
+
}
|
|
484
|
+
} else {
|
|
485
|
+
widgetId = communityId;
|
|
486
|
+
}
|
|
314
487
|
const rawWidgetUrl = get("widget-url") || "https://widget.returningai.com";
|
|
315
488
|
let widgetUrl = rawWidgetUrl;
|
|
316
489
|
if (widgetUrl.endsWith("store-widget")) {
|
|
317
|
-
widgetUrl = `${widgetUrl}/${
|
|
490
|
+
widgetUrl = `${widgetUrl}/${communityId}/open-widget`;
|
|
318
491
|
}
|
|
319
492
|
let widgetDomain;
|
|
320
493
|
try {
|
|
@@ -327,11 +500,7 @@ function readConfig(el, existingId) {
|
|
|
327
500
|
u.searchParams.set("color", get("theme") || "light");
|
|
328
501
|
widgetUrl = u.toString();
|
|
329
502
|
}
|
|
330
|
-
const
|
|
331
|
-
if (widgetId && !/^[a-zA-Z0-9_\-=]{8,}$/.test(widgetId)) {
|
|
332
|
-
throw new Error(`[rai-widget] Invalid widget-id format: "${widgetId}"`);
|
|
333
|
-
}
|
|
334
|
-
const container = get("container") || get("data-container") || `returning-ai-widget-${widgetId}`;
|
|
503
|
+
const container = get("container") || get("data-container") || `returning-ai-widget-${communityId}`;
|
|
335
504
|
const userIdentifiers = {};
|
|
336
505
|
Array.from(el.attributes).forEach((attr) => {
|
|
337
506
|
const name = attr.name.toLowerCase();
|
|
@@ -341,9 +510,21 @@ function readConfig(el, existingId) {
|
|
|
341
510
|
userIdentifiers[name] = attr.value;
|
|
342
511
|
}
|
|
343
512
|
});
|
|
513
|
+
const customData = (() => {
|
|
514
|
+
const raw = get("custom-data");
|
|
515
|
+
if (!raw) return void 0;
|
|
516
|
+
try {
|
|
517
|
+
return JSON.parse(raw);
|
|
518
|
+
} catch {
|
|
519
|
+
return void 0;
|
|
520
|
+
}
|
|
521
|
+
})();
|
|
344
522
|
return {
|
|
523
|
+
communityId,
|
|
524
|
+
channelId,
|
|
345
525
|
widgetId,
|
|
346
|
-
|
|
526
|
+
// auth token — base64-encoded per widget type for auth URLs + iframe URL building
|
|
527
|
+
widgetType,
|
|
347
528
|
theme: get("theme") || "light",
|
|
348
529
|
container,
|
|
349
530
|
width: get("width") || "100%",
|
|
@@ -353,8 +534,25 @@ function readConfig(el, existingId) {
|
|
|
353
534
|
widgetDomain,
|
|
354
535
|
autoRefresh: get("auto-refresh") !== "false",
|
|
355
536
|
debug: get("debug") === "true",
|
|
356
|
-
storagePrefix: "returning-ai-widget",
|
|
357
|
-
|
|
537
|
+
storagePrefix: get("storage-prefix") || "returning-ai-widget",
|
|
538
|
+
// #7
|
|
539
|
+
userIdentifiers,
|
|
540
|
+
maxRetries: num("max-retries", 3),
|
|
541
|
+
// #2
|
|
542
|
+
retryDelay: num("retry-delay", 500),
|
|
543
|
+
// #2
|
|
544
|
+
heightDebounce: num("height-debounce", 100),
|
|
545
|
+
// #4
|
|
546
|
+
locale: get("locale") || void 0,
|
|
547
|
+
// #5
|
|
548
|
+
customData,
|
|
549
|
+
// #8
|
|
550
|
+
authUrl: get("auth-url") || void 0,
|
|
551
|
+
// authenticated embed
|
|
552
|
+
bundleUrl: get("bundle-url") || void 0,
|
|
553
|
+
// bundle mode
|
|
554
|
+
embedToken: get("embed-token") || void 0
|
|
555
|
+
// required — issued by customer's server
|
|
358
556
|
};
|
|
359
557
|
}
|
|
360
558
|
function createInitialState() {
|
|
@@ -381,7 +579,11 @@ class BaseWidget extends HTMLElement {
|
|
|
381
579
|
__publicField(this, "state", createInitialState());
|
|
382
580
|
__publicField(this, "loaderEl", null);
|
|
383
581
|
__publicField(this, "errorEl", null);
|
|
582
|
+
__publicField(this, "msgEl", null);
|
|
384
583
|
__publicField(this, "cleanupListener");
|
|
584
|
+
__publicField(this, "intersectionObserver");
|
|
585
|
+
// #3
|
|
586
|
+
__publicField(this, "themeObserver");
|
|
385
587
|
this.shadow = this.attachShadow({ mode: "closed" });
|
|
386
588
|
}
|
|
387
589
|
connectedCallback() {
|
|
@@ -393,17 +595,43 @@ class BaseWidget extends HTMLElement {
|
|
|
393
595
|
const accentColor = theme === "dark" ? "#ffffff" : "#000000";
|
|
394
596
|
const text4Color = theme === "dark" ? "#9ca3af" : "#6b7280";
|
|
395
597
|
const bgColor = theme === "dark" ? "#1a1a1a" : "#ffffff";
|
|
598
|
+
this.style.width = this.config.width;
|
|
599
|
+
this.style.height = this.config.height;
|
|
396
600
|
this.style.setProperty("--rai-accent", accentColor);
|
|
397
601
|
this.style.setProperty("--rai-text4", text4Color);
|
|
398
602
|
this.style.setProperty("--rai-loader-bg", bgColor);
|
|
399
603
|
this.style.setProperty("--rai-error-bg", bgColor);
|
|
400
604
|
this.renderShell();
|
|
401
|
-
this.
|
|
605
|
+
if (this.hasAttribute("eager")) {
|
|
606
|
+
this.init();
|
|
607
|
+
} else {
|
|
608
|
+
this.intersectionObserver = new IntersectionObserver((entries) => {
|
|
609
|
+
if (entries[0].isIntersecting) {
|
|
610
|
+
this.intersectionObserver.disconnect();
|
|
611
|
+
this.intersectionObserver = void 0;
|
|
612
|
+
this.init();
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
this.intersectionObserver.observe(this);
|
|
616
|
+
}
|
|
402
617
|
}
|
|
403
618
|
disconnectedCallback() {
|
|
619
|
+
var _a, _b;
|
|
620
|
+
(_a = this.intersectionObserver) == null ? void 0 : _a.disconnect();
|
|
621
|
+
(_b = this.themeObserver) == null ? void 0 : _b.disconnect();
|
|
404
622
|
if (this.cleanupListener) this.cleanupListener();
|
|
405
623
|
clearState(this.state);
|
|
406
624
|
}
|
|
625
|
+
// ── DOM event helper (#1) ─────────────────────────────────────────────
|
|
626
|
+
emit(type, detail) {
|
|
627
|
+
this.dispatchEvent(
|
|
628
|
+
new CustomEvent(type, {
|
|
629
|
+
bubbles: true,
|
|
630
|
+
composed: true,
|
|
631
|
+
detail: detail ?? {}
|
|
632
|
+
})
|
|
633
|
+
);
|
|
634
|
+
}
|
|
407
635
|
// ── Rendering ─────────────────────────────────────────────────────────
|
|
408
636
|
renderShell() {
|
|
409
637
|
this.loaderEl = document.createElement("div");
|
|
@@ -412,19 +640,28 @@ class BaseWidget extends HTMLElement {
|
|
|
412
640
|
this.shadow.appendChild(this.loaderEl);
|
|
413
641
|
this.errorEl = document.createElement("div");
|
|
414
642
|
this.errorEl.className = "rai-error";
|
|
415
|
-
this.
|
|
643
|
+
this.msgEl = document.createElement("span");
|
|
644
|
+
this.msgEl.className = "rai-error-msg";
|
|
645
|
+
this.msgEl.textContent = "Authentication failed. Please try again later.";
|
|
646
|
+
this.errorEl.appendChild(this.msgEl);
|
|
647
|
+
const retryLabel = this.getAttribute("retry-label") || this.getAttribute("data-retry-label") || "Retry";
|
|
648
|
+
const btn = document.createElement("button");
|
|
649
|
+
btn.className = "rai-retry-btn";
|
|
650
|
+
btn.textContent = retryLabel;
|
|
651
|
+
btn.addEventListener("click", () => this.reload());
|
|
652
|
+
this.errorEl.appendChild(btn);
|
|
416
653
|
this.shadow.appendChild(this.errorEl);
|
|
417
654
|
}
|
|
418
655
|
createDefaultLoader() {
|
|
419
656
|
const loader = document.createElement("div");
|
|
420
|
-
loader.className = "loader";
|
|
657
|
+
loader.className = "rai-sdk-loader";
|
|
421
658
|
for (let i = 0; i < 5; i++) {
|
|
422
659
|
const bar = document.createElement("div");
|
|
423
|
-
bar.className = "loader__bar";
|
|
660
|
+
bar.className = "rai-sdk-loader__bar";
|
|
424
661
|
loader.appendChild(bar);
|
|
425
662
|
}
|
|
426
663
|
const ball = document.createElement("div");
|
|
427
|
-
ball.className = "loader__ball";
|
|
664
|
+
ball.className = "rai-sdk-loader__ball";
|
|
428
665
|
loader.appendChild(ball);
|
|
429
666
|
return loader;
|
|
430
667
|
}
|
|
@@ -438,22 +675,51 @@ class BaseWidget extends HTMLElement {
|
|
|
438
675
|
this.loaderEl = null;
|
|
439
676
|
}
|
|
440
677
|
showError() {
|
|
441
|
-
var _a;
|
|
678
|
+
var _a, _b;
|
|
442
679
|
this.hideLoader();
|
|
443
680
|
if (this.errorEl) {
|
|
444
|
-
if ((_a = this.state.errorSettings) == null ? void 0 : _a.errorMessage) {
|
|
445
|
-
this.
|
|
681
|
+
if (((_a = this.state.errorSettings) == null ? void 0 : _a.errorMessage) && this.msgEl) {
|
|
682
|
+
this.msgEl.textContent = this.state.errorSettings.errorMessage;
|
|
446
683
|
}
|
|
447
684
|
this.errorEl.classList.add("visible");
|
|
448
685
|
}
|
|
686
|
+
this.emit("rai-error", { message: ((_b = this.msgEl) == null ? void 0 : _b.textContent) ?? "Authentication failed" });
|
|
449
687
|
}
|
|
450
688
|
// ── Initialization ─────────────────────────────────────────────────────
|
|
451
689
|
async init() {
|
|
452
|
-
if (!this.config.
|
|
690
|
+
if (!this.config.communityId) {
|
|
453
691
|
this.showError();
|
|
454
692
|
return;
|
|
455
693
|
}
|
|
456
694
|
await fetchErrorSettings(this.config, this.state);
|
|
695
|
+
if (this.config.embedToken) {
|
|
696
|
+
const valid = await validateEmbedToken(this.config);
|
|
697
|
+
if (!valid) {
|
|
698
|
+
this.showError();
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
const launchWidget = () => {
|
|
703
|
+
if (this.config.bundleUrl) {
|
|
704
|
+
this.mountWidget();
|
|
705
|
+
} else {
|
|
706
|
+
this.createIframe();
|
|
707
|
+
}
|
|
708
|
+
};
|
|
709
|
+
if (this.config.authUrl) {
|
|
710
|
+
const authed2 = await authenticateViaProxy(
|
|
711
|
+
this.config,
|
|
712
|
+
this.state,
|
|
713
|
+
() => this.scheduleRefresh()
|
|
714
|
+
);
|
|
715
|
+
if (authed2) {
|
|
716
|
+
this.emit("rai-authenticated");
|
|
717
|
+
launchWidget();
|
|
718
|
+
} else {
|
|
719
|
+
this.showError();
|
|
720
|
+
}
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
457
723
|
const hasStored = loadFromStorage(this.config, this.state);
|
|
458
724
|
if (hasStored) {
|
|
459
725
|
const refreshed = await refreshAccessToken(
|
|
@@ -466,7 +732,8 @@ class BaseWidget extends HTMLElement {
|
|
|
466
732
|
return;
|
|
467
733
|
}
|
|
468
734
|
this.state.isAuthenticated = true;
|
|
469
|
-
this.
|
|
735
|
+
this.emit("rai-authenticated");
|
|
736
|
+
launchWidget();
|
|
470
737
|
return;
|
|
471
738
|
}
|
|
472
739
|
const authed = await authenticateServerless(
|
|
@@ -475,7 +742,8 @@ class BaseWidget extends HTMLElement {
|
|
|
475
742
|
() => this.scheduleRefresh()
|
|
476
743
|
);
|
|
477
744
|
if (authed) {
|
|
478
|
-
this.
|
|
745
|
+
this.emit("rai-authenticated");
|
|
746
|
+
launchWidget();
|
|
479
747
|
} else {
|
|
480
748
|
this.showError();
|
|
481
749
|
}
|
|
@@ -507,34 +775,83 @@ class BaseWidget extends HTMLElement {
|
|
|
507
775
|
iframe,
|
|
508
776
|
() => this.scheduleRefresh(),
|
|
509
777
|
() => this.logoutAndClear(),
|
|
510
|
-
() => this.hideLoader()
|
|
778
|
+
() => this.hideLoader(),
|
|
779
|
+
(type, detail) => this.emit(type, detail)
|
|
780
|
+
// #1
|
|
511
781
|
);
|
|
512
782
|
}
|
|
783
|
+
// ── Bundle mode (no-iframe) ──────────────────────────────────────────
|
|
784
|
+
async mountWidget() {
|
|
785
|
+
try {
|
|
786
|
+
const w = window;
|
|
787
|
+
const endpointVars = [
|
|
788
|
+
"api_url",
|
|
789
|
+
"base_url",
|
|
790
|
+
"auth_api",
|
|
791
|
+
"socket_path_v2",
|
|
792
|
+
"socket_path",
|
|
793
|
+
"channel_api"
|
|
794
|
+
];
|
|
795
|
+
for (const v of endpointVars) {
|
|
796
|
+
if (!w[v]) w[v] = this.config.apiUrl;
|
|
797
|
+
}
|
|
798
|
+
await loadScript(this.config.bundleUrl);
|
|
799
|
+
const globalName = WIDGET_GLOBALS[this.config.widgetType];
|
|
800
|
+
const widgetGlobal = window[globalName];
|
|
801
|
+
if (!(widgetGlobal == null ? void 0 : widgetGlobal.mount)) {
|
|
802
|
+
throw new Error(`window.${globalName}.mount not found after loading bundle`);
|
|
803
|
+
}
|
|
804
|
+
const container = document.createElement("div");
|
|
805
|
+
container.style.cssText = "position: relative; width: 100%; height: 100%;";
|
|
806
|
+
this.appendChild(container);
|
|
807
|
+
if (!this.shadow.querySelector("slot")) {
|
|
808
|
+
const slot = document.createElement("slot");
|
|
809
|
+
this.shadow.appendChild(slot);
|
|
810
|
+
}
|
|
811
|
+
if (this.loaderEl) {
|
|
812
|
+
this.loaderEl.remove();
|
|
813
|
+
this.loaderEl = null;
|
|
814
|
+
}
|
|
815
|
+
widgetGlobal.mount(container, {
|
|
816
|
+
widgetType: this.config.widgetType,
|
|
817
|
+
communityId: this.config.communityId,
|
|
818
|
+
channelId: this.config.channelId,
|
|
819
|
+
// channel widget only; others receive undefined and ignore it
|
|
820
|
+
widgetId: this.config.communityId,
|
|
821
|
+
// deprecated alias kept for widget main.tsx compatibility
|
|
822
|
+
apiUrl: this.config.apiUrl,
|
|
823
|
+
token: this.state.accessToken,
|
|
824
|
+
basePath: "/",
|
|
825
|
+
isPreview: false,
|
|
826
|
+
isOpenWidget: true,
|
|
827
|
+
defaultColorSchema: this.config.theme === "dark" ? "dark" : "light"
|
|
828
|
+
});
|
|
829
|
+
this.emit("rai-mounted");
|
|
830
|
+
} catch (err) {
|
|
831
|
+
console.error("[rai-widget] Bundle mount failed:", err);
|
|
832
|
+
this.showError();
|
|
833
|
+
}
|
|
834
|
+
}
|
|
513
835
|
// ── Token scheduling ──────────────────────────────────────────────────
|
|
514
836
|
scheduleRefresh() {
|
|
515
837
|
if (this.state.refreshTimer) clearTimeout(this.state.refreshTimer);
|
|
516
838
|
if (!this.state.accessTokenExpiry) return;
|
|
517
839
|
const delay = this.state.accessTokenExpiry - Date.now() - 6e4;
|
|
840
|
+
const pushToken = () => {
|
|
841
|
+
if (this.state.iframe) sendTokenToWidget(this.config, this.state, this.state.iframe);
|
|
842
|
+
};
|
|
843
|
+
const doRefresh = async () => {
|
|
844
|
+
if (this.config.authUrl) {
|
|
845
|
+
const ok = await authenticateViaProxy(this.config, this.state, () => this.scheduleRefresh());
|
|
846
|
+
if (ok) pushToken();
|
|
847
|
+
} else {
|
|
848
|
+
await refreshAccessToken(this.config, this.state, () => this.scheduleRefresh(), pushToken);
|
|
849
|
+
}
|
|
850
|
+
};
|
|
518
851
|
if (delay > 0) {
|
|
519
|
-
this.state.refreshTimer = setTimeout(
|
|
520
|
-
await refreshAccessToken(
|
|
521
|
-
this.config,
|
|
522
|
-
this.state,
|
|
523
|
-
() => this.scheduleRefresh(),
|
|
524
|
-
() => {
|
|
525
|
-
if (this.state.iframe) sendTokenToWidget(this.config, this.state, this.state.iframe);
|
|
526
|
-
}
|
|
527
|
-
);
|
|
528
|
-
}, delay);
|
|
852
|
+
this.state.refreshTimer = setTimeout(doRefresh, delay);
|
|
529
853
|
} else {
|
|
530
|
-
|
|
531
|
-
this.config,
|
|
532
|
-
this.state,
|
|
533
|
-
() => this.scheduleRefresh(),
|
|
534
|
-
() => {
|
|
535
|
-
if (this.state.iframe) sendTokenToWidget(this.config, this.state, this.state.iframe);
|
|
536
|
-
}
|
|
537
|
-
);
|
|
854
|
+
doRefresh();
|
|
538
855
|
}
|
|
539
856
|
}
|
|
540
857
|
schedulePeriodicSync(iframe) {
|
|
@@ -547,6 +864,7 @@ class BaseWidget extends HTMLElement {
|
|
|
547
864
|
async logoutAndClear() {
|
|
548
865
|
await logout(this.config, this.state);
|
|
549
866
|
this.shadow.querySelectorAll("iframe").forEach((el) => el.remove());
|
|
867
|
+
this.emit("rai-logout");
|
|
550
868
|
}
|
|
551
869
|
// ── Public API ────────────────────────────────────────────────────────
|
|
552
870
|
async reload() {
|
|
@@ -578,48 +896,73 @@ class StoreWidget extends BaseWidget {
|
|
|
578
896
|
url.searchParams.set("containerId", config.container);
|
|
579
897
|
url.searchParams.set("connectType", "simple");
|
|
580
898
|
url.searchParams.set("mode", "private");
|
|
899
|
+
if (config.locale) url.searchParams.set("locale", config.locale);
|
|
581
900
|
return url.toString();
|
|
582
901
|
}
|
|
583
902
|
}
|
|
584
903
|
class ChannelWidget extends BaseWidget {
|
|
585
904
|
buildWidgetUrl(config) {
|
|
586
|
-
|
|
587
|
-
|
|
905
|
+
const base = config.widgetUrl.endsWith("channel-widget") ? `${config.widgetUrl}/${config.widgetId}` : config.widgetUrl;
|
|
906
|
+
if (!config.locale) return base;
|
|
907
|
+
try {
|
|
908
|
+
const u = new URL(base);
|
|
909
|
+
u.searchParams.set("locale", config.locale);
|
|
910
|
+
return u.toString();
|
|
911
|
+
} catch {
|
|
912
|
+
return `${base}?locale=${encodeURIComponent(config.locale)}`;
|
|
588
913
|
}
|
|
589
|
-
return config.widgetUrl;
|
|
590
914
|
}
|
|
591
915
|
}
|
|
592
916
|
class MilestoneWidget extends BaseWidget {
|
|
593
917
|
buildWidgetUrl(config) {
|
|
594
|
-
|
|
595
|
-
|
|
918
|
+
const base = config.widgetUrl.endsWith("milestone-widget") ? `${config.widgetUrl}/${config.widgetId}` : config.widgetUrl;
|
|
919
|
+
if (!config.locale) return base;
|
|
920
|
+
try {
|
|
921
|
+
const u = new URL(base);
|
|
922
|
+
u.searchParams.set("locale", config.locale);
|
|
923
|
+
return u.toString();
|
|
924
|
+
} catch {
|
|
925
|
+
return `${base}?locale=${encodeURIComponent(config.locale)}`;
|
|
596
926
|
}
|
|
597
|
-
return config.widgetUrl;
|
|
598
927
|
}
|
|
599
928
|
}
|
|
600
929
|
class SocialWidget extends BaseWidget {
|
|
601
930
|
buildWidgetUrl(config) {
|
|
602
|
-
|
|
603
|
-
|
|
931
|
+
const base = config.widgetUrl.endsWith("social-widget") ? `${config.widgetUrl}/${config.widgetId}` : config.widgetUrl;
|
|
932
|
+
if (!config.locale) return base;
|
|
933
|
+
try {
|
|
934
|
+
const u = new URL(base);
|
|
935
|
+
u.searchParams.set("locale", config.locale);
|
|
936
|
+
return u.toString();
|
|
937
|
+
} catch {
|
|
938
|
+
return `${base}?locale=${encodeURIComponent(config.locale)}`;
|
|
604
939
|
}
|
|
605
|
-
return config.widgetUrl;
|
|
606
940
|
}
|
|
607
941
|
}
|
|
608
942
|
class CurrencyWidget extends BaseWidget {
|
|
609
943
|
buildWidgetUrl(config) {
|
|
610
|
-
|
|
611
|
-
|
|
944
|
+
const base = config.widgetUrl.endsWith("currency-overview-widget") ? `${config.widgetUrl}/${config.widgetId}` : config.widgetUrl;
|
|
945
|
+
if (!config.locale) return base;
|
|
946
|
+
try {
|
|
947
|
+
const u = new URL(base);
|
|
948
|
+
u.searchParams.set("locale", config.locale);
|
|
949
|
+
return u.toString();
|
|
950
|
+
} catch {
|
|
951
|
+
return `${base}?locale=${encodeURIComponent(config.locale)}`;
|
|
612
952
|
}
|
|
613
|
-
return config.widgetUrl;
|
|
614
953
|
}
|
|
615
954
|
}
|
|
616
|
-
console.log(`[rai-widget] v${"1.0
|
|
955
|
+
console.log(`[rai-widget] v${"1.1.0"}`);
|
|
956
|
+
class StoreWidgetCompat extends StoreWidget {
|
|
957
|
+
}
|
|
617
958
|
const WIDGET_REGISTRY = [
|
|
618
|
-
["rai-widget", StoreWidget],
|
|
959
|
+
["rai-store-widget", StoreWidget],
|
|
619
960
|
["rai-channel-widget", ChannelWidget],
|
|
620
961
|
["rai-milestone-widget", MilestoneWidget],
|
|
621
962
|
["rai-social-widget", SocialWidget],
|
|
622
|
-
["rai-currency-widget", CurrencyWidget]
|
|
963
|
+
["rai-currency-widget", CurrencyWidget],
|
|
964
|
+
["rai-widget", StoreWidgetCompat]
|
|
965
|
+
// deprecated alias — use <rai-store-widget> instead
|
|
623
966
|
];
|
|
624
967
|
for (const [tag, cls] of WIDGET_REGISTRY) {
|
|
625
968
|
if (!customElements.get(tag)) {
|
|
@@ -635,10 +978,10 @@ const WIDGET_CLASS_MAP = {
|
|
|
635
978
|
};
|
|
636
979
|
const ALL_WIDGET_SELECTOR = WIDGET_REGISTRY.map(([tag]) => tag).join(", ");
|
|
637
980
|
function bootstrapFromScriptTag() {
|
|
638
|
-
var _a;
|
|
639
|
-
const script = (((_a = document.currentScript) == null ? void 0 : _a.hasAttribute("data-widget-id")) ? document.currentScript : null) || document.querySelector("script[data-widget-id]");
|
|
981
|
+
var _a, _b;
|
|
982
|
+
const script = (((_a = document.currentScript) == null ? void 0 : _a.hasAttribute("data-widget-id")) || ((_b = document.currentScript) == null ? void 0 : _b.hasAttribute("data-community-id")) ? document.currentScript : null) || document.querySelector("script[data-widget-id], script[data-community-id]");
|
|
640
983
|
if (!script) return;
|
|
641
|
-
const widgetId = script.getAttribute("data-widget-id");
|
|
984
|
+
const widgetId = script.getAttribute("data-community-id") || script.getAttribute("data-widget-id");
|
|
642
985
|
if (!widgetId) return;
|
|
643
986
|
const containerId = script.getAttribute("data-container") || `returning-ai-widget-${widgetId}`;
|
|
644
987
|
const container = document.getElementById(containerId);
|
|
@@ -661,15 +1004,15 @@ if (document.readyState === "loading") {
|
|
|
661
1004
|
}
|
|
662
1005
|
function exposePublicApi() {
|
|
663
1006
|
const container = (() => {
|
|
664
|
-
var _a;
|
|
665
|
-
const script = (((_a = document.currentScript) == null ? void 0 : _a.hasAttribute("data-widget-id")) ? document.currentScript : null) || document.querySelector("script[data-widget-id]");
|
|
1007
|
+
var _a, _b;
|
|
1008
|
+
const script = (((_a = document.currentScript) == null ? void 0 : _a.hasAttribute("data-widget-id")) || ((_b = document.currentScript) == null ? void 0 : _b.hasAttribute("data-community-id")) ? document.currentScript : null) || document.querySelector("script[data-widget-id], script[data-community-id]");
|
|
666
1009
|
if (!script) return null;
|
|
667
|
-
const id = script.getAttribute("data-container") || `returning-ai-widget-${script.getAttribute("data-widget-id")}`;
|
|
1010
|
+
const id = script.getAttribute("data-container") || `returning-ai-widget-${script.getAttribute("data-community-id") || script.getAttribute("data-widget-id")}`;
|
|
668
1011
|
return document.getElementById(id);
|
|
669
1012
|
})();
|
|
670
1013
|
const widget = container == null ? void 0 : container.querySelector(ALL_WIDGET_SELECTOR);
|
|
671
1014
|
window.ReturningAIWidget = {
|
|
672
|
-
version: "1.0
|
|
1015
|
+
version: "1.1.0",
|
|
673
1016
|
reload: () => (widget == null ? void 0 : widget.reload()) ?? Promise.resolve(),
|
|
674
1017
|
logout: () => (widget == null ? void 0 : widget.logoutPublic()) ?? Promise.resolve(),
|
|
675
1018
|
isAuthenticated: () => (widget == null ? void 0 : widget.isAuthenticated()) ?? false,
|