@8wave/ai-elements 0.98.0-beta.4 → 0.98.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/dist/_chunks/{Document-CjCQXPDF.js → Document-DtFcn7Mq.js} +2 -2
- package/dist/_chunks/Document-DtFcn7Mq.js.map +1 -0
- package/dist/_chunks/{PkToolRequestConfirm-BGt2J3e5.js → PkToolRequestConfirm-FQ9nDFXy.js} +2 -2
- package/dist/_chunks/{PkToolRequestConfirm-BGt2J3e5.js.map → PkToolRequestConfirm-FQ9nDFXy.js.map} +1 -1
- package/dist/_chunks/{PkToolRequestGeolocation-D9kfr6SV.js → PkToolRequestGeolocation-BJ9NJZIg.js} +2 -2
- package/dist/_chunks/{PkToolRequestGeolocation-D9kfr6SV.js.map → PkToolRequestGeolocation-BJ9NJZIg.js.map} +1 -1
- package/dist/_chunks/{PkToolRequestOAuthConnection-BH2tXr0j.js → PkToolRequestOAuthConnection-DdvJ6n1K.js} +2 -2
- package/dist/_chunks/{PkToolRequestOAuthConnection-BH2tXr0j.js.map → PkToolRequestOAuthConnection-DdvJ6n1K.js.map} +1 -1
- package/dist/_chunks/{PkToolShowCalendarEvent-DZwH1Run.js → PkToolShowCalendarEvent-BYnhl2er.js} +2 -2
- package/dist/_chunks/{PkToolShowCalendarEvent-DZwH1Run.js.map → PkToolShowCalendarEvent-BYnhl2er.js.map} +1 -1
- package/dist/_chunks/{PkToolShowContactForm-DEQBTzf4.js → PkToolShowContactForm-upUNZtId.js} +3 -3
- package/dist/_chunks/{PkToolShowContactForm-DEQBTzf4.js.map → PkToolShowContactForm-upUNZtId.js.map} +1 -1
- package/dist/_chunks/{PkToolShowDiagram-siwsx1K7.js → PkToolShowDiagram-Jq_4KWiK.js} +2 -2
- package/dist/_chunks/{PkToolShowDiagram-siwsx1K7.js.map → PkToolShowDiagram-Jq_4KWiK.js.map} +1 -1
- package/dist/_chunks/{PkToolShowForm-AZwrmmM9.js → PkToolShowForm-BNV4Ja6g.js} +2 -2
- package/dist/_chunks/{PkToolShowForm-AZwrmmM9.js.map → PkToolShowForm-BNV4Ja6g.js.map} +1 -1
- package/dist/_chunks/{PkToolShowSources-FnMkkdm6.js → PkToolShowSources-DwB6VXrK.js} +2 -2
- package/dist/_chunks/{PkToolShowSources-FnMkkdm6.js.map → PkToolShowSources-DwB6VXrK.js.map} +1 -1
- package/dist/_chunks/{PkToolShowWeather-COyrzQ7m.js → PkToolShowWeather-C2rPCQ_K.js} +2 -2
- package/dist/_chunks/{PkToolShowWeather-COyrzQ7m.js.map → PkToolShowWeather-C2rPCQ_K.js.map} +1 -1
- package/dist/_chunks/{index.es-eLFDkB2Y.js → index.es-u1dF1xg2.js} +2 -2
- package/dist/_chunks/{index.es-eLFDkB2Y.js.map → index.es-u1dF1xg2.js.map} +1 -1
- package/dist/_chunks/{vue-i18n-BBwABYBg.js → vue-i18n-kZs9Hfey.js} +650 -640
- package/dist/_chunks/vue-i18n-kZs9Hfey.js.map +1 -0
- package/dist/ai-elements.es.js +150 -121
- package/dist/ai-elements.es.js.map +1 -1
- package/dist-vue/PkChatbot.js +1 -1
- package/dist-vue/PkChatbotMessages.js +1 -1
- package/dist-vue/PkChatbotViewChat.js +1 -1
- package/dist-vue/PkChatbotViewConversations.js +1 -1
- package/dist-vue/PkChatbotViewFiles.js +1 -1
- package/dist-vue/PkChatbotViewProfile.js +1 -1
- package/dist-vue/_chunks/{Document-B8gv0e2O.js → Document-CSdfKj_B.js} +2 -2
- package/dist-vue/_chunks/{Document-B8gv0e2O.js.map → Document-CSdfKj_B.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbot-CxpAotKB.js → PkChatbot-DHkhT0Jf.js} +6 -6
- package/dist-vue/_chunks/{PkChatbot-CxpAotKB.js.map → PkChatbot-DHkhT0Jf.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotMessages-DEzcxcqc.js → PkChatbotMessages-B4qfTNOJ.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotMessages-DEzcxcqc.js.map → PkChatbotMessages-B4qfTNOJ.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewChat-QzWSymIg.js → PkChatbotViewChat-C08TsXiL.js} +4 -4
- package/dist-vue/_chunks/{PkChatbotViewChat-QzWSymIg.js.map → PkChatbotViewChat-C08TsXiL.js.map} +1 -1
- package/dist-vue/_chunks/PkChatbotViewConversations-BQZi5kSC.js +192 -0
- package/dist-vue/_chunks/PkChatbotViewConversations-BQZi5kSC.js.map +1 -0
- package/dist-vue/_chunks/{PkChatbotViewFiles-RHMwvWUS.js → PkChatbotViewFiles-TLKE_tRw.js} +3 -3
- package/dist-vue/_chunks/{PkChatbotViewFiles-RHMwvWUS.js.map → PkChatbotViewFiles-TLKE_tRw.js.map} +1 -1
- package/dist-vue/_chunks/{PkChatbotViewProfile-Bka6CUAa.js → PkChatbotViewProfile-B6ANZY96.js} +2 -2
- package/dist-vue/_chunks/{PkChatbotViewProfile-Bka6CUAa.js.map → PkChatbotViewProfile-B6ANZY96.js.map} +1 -1
- package/dist-vue/_chunks/{PkToolShowSources-rGZz65c2.js → PkToolShowSources-CFh-i60o.js} +2 -2
- package/dist-vue/_chunks/{PkToolShowSources-rGZz65c2.js.map → PkToolShowSources-CFh-i60o.js.map} +1 -1
- package/dist-vue/_chunks/{constants-BSe0uxSf.js → constants-BCJ_gKeL.js} +2 -2
- package/dist-vue/_chunks/{constants-BSe0uxSf.js.map → constants-BCJ_gKeL.js.map} +1 -1
- package/dist-vue/_chunks/{createChatbotApiClient-Bf5DbqVH.js → createChatbotApiClient-BOBdBxmf.js} +2 -2
- package/dist-vue/_chunks/{createChatbotApiClient-Bf5DbqVH.js.map → createChatbotApiClient-BOBdBxmf.js.map} +1 -1
- package/dist-vue/_chunks/{useChatbotStore-DeHwEN8W.js → useChatbotStore-BJ-EDmND.js} +3 -3
- package/dist-vue/_chunks/useChatbotStore-BJ-EDmND.js.map +1 -0
- package/dist-vue/api.js +1 -1
- package/dist-vue/apps/web-component/src/components/EmbeddedChatWidget.ce.d.ts +7 -0
- package/dist-vue/apps/web-component/src/components/FloatingChatWidget.ce.d.ts +7 -0
- package/dist-vue/apps/web-component/src/composables/useChatbotAuth.d.ts +11 -0
- package/dist-vue/apps/web-component/src/lib.d.ts +6 -0
- package/dist-vue/composables.js +118 -103
- package/dist-vue/composables.js.map +1 -1
- package/dist-vue/index.js +12 -10
- package/dist-vue/index.js.map +1 -1
- package/dist-vue/packages/components/src/chat/PkChatbotMessages.d.ts +3 -134
- package/dist-vue/packages/components/src/chat/PkMessageParts.d.ts +3 -134
- package/dist-vue/packages/components/src/chat/constants.d.ts +1 -1
- package/dist-vue/packages/composable/src/chatbot/useChatbotStore.d.ts +24 -24
- package/dist-vue/packages/models/src/schema/constants.d.ts +2 -0
- package/package.json +1 -1
- package/dist/_chunks/Document-CjCQXPDF.js.map +0 -1
- package/dist/_chunks/vue-i18n-BBwABYBg.js.map +0 -1
- package/dist-vue/_chunks/PkChatbotViewConversations-O-wm_nu1.js +0 -192
- package/dist-vue/_chunks/PkChatbotViewConversations-O-wm_nu1.js.map +0 -1
- package/dist-vue/_chunks/useChatbotStore-DeHwEN8W.js.map +0 -1
package/dist-vue/api.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { t as e } from "./_chunks/createChatbotApiClient-
|
|
1
|
+
import { t as e } from "./_chunks/createChatbotApiClient-BOBdBxmf.js";
|
|
2
2
|
export { e as createChatbotApiClient };
|
|
@@ -6,6 +6,12 @@ type __VLS_Props = {
|
|
|
6
6
|
externalToken?: string;
|
|
7
7
|
/** Boolean or attribute string ("true"/"false") when set via HTML attribute. */
|
|
8
8
|
awaitExternalToken?: boolean | string;
|
|
9
|
+
/**
|
|
10
|
+
* Host-session mode: the widget skips its own sign-in and uses
|
|
11
|
+
* the host platform's better-auth session from the cookie.
|
|
12
|
+
* Boolean or attribute string ("true"/"false").
|
|
13
|
+
*/
|
|
14
|
+
useHostSession?: boolean | string;
|
|
9
15
|
/** Comma-separated list of extra slot names to forward to PkChatbot (e.g. "tool-showArtifact,subagent"). */
|
|
10
16
|
forwardedSlots?: string;
|
|
11
17
|
customCss?: string;
|
|
@@ -40,6 +46,7 @@ declare const __VLS_base: DefineComponent<__VLS_Props, {
|
|
|
40
46
|
systemTheme: "light" | "dark";
|
|
41
47
|
externalToken: string;
|
|
42
48
|
awaitExternalToken: boolean | string;
|
|
49
|
+
useHostSession: boolean | string;
|
|
43
50
|
forwardedSlots: string;
|
|
44
51
|
appinsightsConnectionString: string;
|
|
45
52
|
cookieName: string;
|
|
@@ -6,6 +6,12 @@ type __VLS_Props = {
|
|
|
6
6
|
externalToken?: string;
|
|
7
7
|
/** Boolean or attribute string ("true"/"false") when set via HTML attribute. */
|
|
8
8
|
awaitExternalToken?: boolean | string;
|
|
9
|
+
/**
|
|
10
|
+
* Host-session mode: the widget skips its own sign-in and uses
|
|
11
|
+
* the host platform's better-auth session from the cookie.
|
|
12
|
+
* Boolean or attribute string ("true"/"false").
|
|
13
|
+
*/
|
|
14
|
+
useHostSession?: boolean | string;
|
|
9
15
|
floatingButtonPosition?: 'left' | 'right';
|
|
10
16
|
customCss?: string;
|
|
11
17
|
appinsightsConnectionString?: string;
|
|
@@ -34,6 +40,7 @@ declare const __VLS_export: DefineComponent<__VLS_Props, {
|
|
|
34
40
|
systemTheme: "light" | "dark";
|
|
35
41
|
externalToken: string;
|
|
36
42
|
awaitExternalToken: boolean | string;
|
|
43
|
+
useHostSession: boolean | string;
|
|
37
44
|
appinsightsConnectionString: string;
|
|
38
45
|
cookieName: string;
|
|
39
46
|
}, {}, {}, {}, string, ComponentProvideOptions, false, {}, any>;
|
|
@@ -78,6 +78,17 @@ export declare function useChatbotAuth(options?: {
|
|
|
78
78
|
basePath?: string;
|
|
79
79
|
externalToken?: MaybeRefOrGetter<string | undefined>;
|
|
80
80
|
awaitExternalToken?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Host-session mode: the widget never signs in on its own and sends
|
|
83
|
+
* no Authorization header; the session comes from the host
|
|
84
|
+
* platform's better-auth cookie (sent thanks to
|
|
85
|
+
* `credentials: 'include'`). The session belongs to the host, so
|
|
86
|
+
* account management is hidden and `signOut` is a no-op. Intended
|
|
87
|
+
* for the widget embedded in the platform itself (Backoffice
|
|
88
|
+
* copilot, spec platform-copilot-session-bridge.md); it only works
|
|
89
|
+
* where the host cookie context works, not on third-party embeds.
|
|
90
|
+
*/
|
|
91
|
+
useHostSession?: MaybeRefOrGetter<boolean | undefined>;
|
|
81
92
|
isActive?: MaybeRefOrGetter<boolean>;
|
|
82
93
|
cookieName?: string;
|
|
83
94
|
/** Called when a session is established or the user changes. */
|
|
@@ -7,6 +7,7 @@ export declare const FloatingChatWidgetCe: VueElementConstructor<{
|
|
|
7
7
|
baseUrl?: string;
|
|
8
8
|
externalToken?: string;
|
|
9
9
|
awaitExternalToken?: boolean | string;
|
|
10
|
+
useHostSession?: boolean | string;
|
|
10
11
|
floatingButtonPosition?: "left" | "right";
|
|
11
12
|
customCss?: string;
|
|
12
13
|
appinsightsConnectionString?: string;
|
|
@@ -19,6 +20,7 @@ export declare const EmbeddedChatWidgetCe: VueElementConstructor<{
|
|
|
19
20
|
baseUrl?: string;
|
|
20
21
|
externalToken?: string;
|
|
21
22
|
awaitExternalToken?: boolean | string;
|
|
23
|
+
useHostSession?: boolean | string;
|
|
22
24
|
forwardedSlots?: string;
|
|
23
25
|
customCss?: string;
|
|
24
26
|
appinsightsConnectionString?: string;
|
|
@@ -38,6 +40,8 @@ export interface FloatingChatWidgetElement extends HTMLElement {
|
|
|
38
40
|
baseUrl?: string;
|
|
39
41
|
externalToken?: string;
|
|
40
42
|
awaitExternalToken?: boolean;
|
|
43
|
+
/** Host-session mode: use the host platform's better-auth session from the cookie (no widget sign-in). */
|
|
44
|
+
useHostSession?: boolean;
|
|
41
45
|
floatingButtonPosition?: 'left' | 'right';
|
|
42
46
|
customCss?: string;
|
|
43
47
|
appinsightsConnectionString?: string;
|
|
@@ -59,6 +63,8 @@ export interface EmbeddedChatWidgetElement extends HTMLElement {
|
|
|
59
63
|
baseUrl?: string;
|
|
60
64
|
externalToken?: string;
|
|
61
65
|
awaitExternalToken?: boolean;
|
|
66
|
+
/** Host-session mode: use the host platform's better-auth session from the cookie (no widget sign-in). */
|
|
67
|
+
useHostSession?: boolean;
|
|
62
68
|
forwardedSlots?: string;
|
|
63
69
|
customCss?: string;
|
|
64
70
|
appinsightsConnectionString?: string;
|
package/dist-vue/composables.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as e } from "./_chunks/createChatbotApiClient-
|
|
2
|
-
import { t } from "./_chunks/useChatbotStore-
|
|
1
|
+
import { t as e } from "./_chunks/createChatbotApiClient-BOBdBxmf.js";
|
|
2
|
+
import { t } from "./_chunks/useChatbotStore-BJ-EDmND.js";
|
|
3
3
|
import { t as n } from "./_chunks/src-8QXYVWIx.js";
|
|
4
4
|
import { t as r } from "./_chunks/useLocalizedString-PfaKjIQY.js";
|
|
5
5
|
import { d as i } from "./_chunks/utils-BMa8jwJS.js";
|
|
@@ -133,12 +133,12 @@ function j() {
|
|
|
133
133
|
};
|
|
134
134
|
}
|
|
135
135
|
function M(e = {}) {
|
|
136
|
-
let t = l(x(e.cookieName)), n = a({
|
|
136
|
+
let t = l(x(e.cookieName)), n = a(() => u(e.useHostSession) ?? !1), r = a({
|
|
137
137
|
get: () => t.value,
|
|
138
138
|
set: (n) => {
|
|
139
139
|
b(n, e.cookieName), t.value = n;
|
|
140
140
|
}
|
|
141
|
-
}),
|
|
141
|
+
}), i = a(() => !n.value && r.value ? { Authorization: `Bearer ${r.value}` } : void 0), o = f({
|
|
142
142
|
baseURL: e.baseUrl ?? void 0,
|
|
143
143
|
basePath: e.basePath ?? "auth",
|
|
144
144
|
plugins: [
|
|
@@ -150,189 +150,204 @@ function M(e = {}) {
|
|
|
150
150
|
],
|
|
151
151
|
fetchOptions: {
|
|
152
152
|
onSuccess: (e) => {
|
|
153
|
+
if (n.value) return;
|
|
153
154
|
let t = e.response.headers.get("set-auth-token");
|
|
154
|
-
t && (
|
|
155
|
+
t && (r.value = t);
|
|
155
156
|
},
|
|
156
157
|
auth: {
|
|
157
158
|
type: "Bearer",
|
|
158
|
-
token: () => n.value
|
|
159
|
-
}
|
|
159
|
+
token: () => n.value ? "" : r.value
|
|
160
|
+
},
|
|
161
|
+
customFetchImpl: (e, t) => fetch(e, {
|
|
162
|
+
...t,
|
|
163
|
+
credentials: n.value ? "include" : t?.credentials
|
|
164
|
+
})
|
|
160
165
|
}
|
|
161
|
-
}),
|
|
162
|
-
function
|
|
163
|
-
let t =
|
|
164
|
-
return
|
|
166
|
+
}), s = o.useSession(), c = l(!1), h = a(() => u(e.externalToken)), g = a(() => u(e.isActive) ?? !0), _ = l(h.value), v = a(() => !!h.value || !!e.awaitExternalToken || !!_.value || n.value), y = Promise.resolve();
|
|
167
|
+
function T(e) {
|
|
168
|
+
let t = y.then(e, e);
|
|
169
|
+
return y = t, t;
|
|
165
170
|
}
|
|
166
|
-
d(() =>
|
|
171
|
+
d(() => s.value.data?.user, (t, n) => {
|
|
167
172
|
t && t.id !== n?.id && e.onSignedIn?.({
|
|
168
173
|
userId: t.id,
|
|
169
174
|
isAnonymous: !!t.isAnonymous
|
|
170
175
|
}), !t && n && e.onSignedOut?.();
|
|
171
176
|
});
|
|
172
|
-
async function T() {
|
|
173
|
-
let { data: t } = await i.signIn.anonymous();
|
|
174
|
-
t?.token && (n.value = t.token, t.user?.id && S(t.user.id, e.cookieName));
|
|
175
|
-
}
|
|
176
177
|
async function E() {
|
|
178
|
+
let { data: t } = await o.signIn.anonymous();
|
|
179
|
+
t?.token && (r.value = t.token, t.user?.id && S(t.user.id, e.cookieName));
|
|
180
|
+
}
|
|
181
|
+
async function D() {
|
|
177
182
|
let t = C(e.cookieName);
|
|
178
183
|
if (!t) return !1;
|
|
179
184
|
try {
|
|
180
|
-
let { data: e } = await
|
|
181
|
-
if (e?.token) return
|
|
185
|
+
let { data: e } = await o.signIn.anonymousReauth({ userId: t });
|
|
186
|
+
if (e?.token) return r.value = e.token, await s.value.refetch(), !0;
|
|
182
187
|
} catch {}
|
|
183
188
|
return !1;
|
|
184
189
|
}
|
|
185
|
-
async function
|
|
186
|
-
await
|
|
190
|
+
async function O() {
|
|
191
|
+
await D() || await E();
|
|
187
192
|
}
|
|
188
|
-
async function
|
|
189
|
-
|
|
193
|
+
async function M(t) {
|
|
194
|
+
_.value === t && (e.onExternalAuthExpired?.(), !e.awaitExternalToken && await O());
|
|
190
195
|
}
|
|
191
|
-
async function
|
|
192
|
-
|
|
196
|
+
async function N(e) {
|
|
197
|
+
c.value = !0;
|
|
193
198
|
try {
|
|
194
|
-
let { data: t } = await
|
|
195
|
-
if (
|
|
199
|
+
let { data: t } = await o.signIn.externalToken({ token: e });
|
|
200
|
+
if (_.value !== e) return;
|
|
196
201
|
if (t?.token) {
|
|
197
|
-
|
|
202
|
+
r.value = t.token, await s.value.refetch();
|
|
198
203
|
return;
|
|
199
204
|
}
|
|
200
|
-
await
|
|
205
|
+
await M(e);
|
|
201
206
|
} catch {
|
|
202
|
-
await
|
|
207
|
+
await M(e);
|
|
203
208
|
} finally {
|
|
204
|
-
|
|
209
|
+
c.value = !1;
|
|
205
210
|
}
|
|
206
211
|
}
|
|
207
|
-
let
|
|
208
|
-
async function
|
|
209
|
-
if (!(!e ||
|
|
210
|
-
|
|
212
|
+
let P, F, I = !1;
|
|
213
|
+
async function L(e) {
|
|
214
|
+
if (!(!e || P === e)) {
|
|
215
|
+
_.value = e, F = e, P = e;
|
|
211
216
|
try {
|
|
212
|
-
await
|
|
213
|
-
|
|
217
|
+
await T(async () => {
|
|
218
|
+
_.value === e && await N(e);
|
|
214
219
|
});
|
|
215
220
|
} finally {
|
|
216
|
-
|
|
221
|
+
P = void 0;
|
|
217
222
|
}
|
|
218
223
|
}
|
|
219
224
|
}
|
|
220
|
-
async function
|
|
221
|
-
|
|
222
|
-
if (!
|
|
223
|
-
|
|
225
|
+
async function R() {
|
|
226
|
+
c.value || await T(async () => {
|
|
227
|
+
if (!(_.value || n.value)) {
|
|
228
|
+
c.value = !0;
|
|
224
229
|
try {
|
|
225
|
-
await
|
|
230
|
+
await O();
|
|
226
231
|
} finally {
|
|
227
|
-
|
|
232
|
+
c.value = !1;
|
|
228
233
|
}
|
|
229
234
|
}
|
|
230
235
|
});
|
|
231
236
|
}
|
|
232
|
-
d([
|
|
237
|
+
d([g, s], ([t, r]) => {
|
|
233
238
|
if (!t) return;
|
|
234
|
-
if (!
|
|
235
|
-
if (
|
|
236
|
-
|
|
239
|
+
if (!r.isPending && !r.isRefetching && !c.value && !r.data) {
|
|
240
|
+
if (n.value) return;
|
|
241
|
+
if (_.value) {
|
|
242
|
+
L(_.value);
|
|
237
243
|
return;
|
|
238
244
|
}
|
|
239
245
|
if (e.awaitExternalToken) return;
|
|
240
|
-
|
|
246
|
+
R();
|
|
241
247
|
return;
|
|
242
248
|
}
|
|
243
|
-
let
|
|
244
|
-
if (n.
|
|
245
|
-
if (
|
|
246
|
-
|
|
247
|
-
|
|
249
|
+
let i = r.data?.user;
|
|
250
|
+
if (!n.value) {
|
|
251
|
+
if (r.data?.session?.hardExpiresAt && !I) {
|
|
252
|
+
if (I = !0, !_.value) {
|
|
253
|
+
G();
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (F !== _.value) {
|
|
257
|
+
L(_.value);
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
248
260
|
}
|
|
249
|
-
if (
|
|
250
|
-
|
|
261
|
+
if (i?.isAnonymous && _.value && F !== _.value) {
|
|
262
|
+
L(_.value);
|
|
251
263
|
return;
|
|
252
264
|
}
|
|
265
|
+
i?.isAnonymous && i.id && !C(e.cookieName) && S(i.id, e.cookieName);
|
|
253
266
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
r?.isAnonymous && r.id && !C(e.cookieName) && S(r.id, e.cookieName);
|
|
259
|
-
}, { immediate: !0 }), d(c, (e, t) => {
|
|
267
|
+
}, { immediate: !0 }), d(n, () => {
|
|
268
|
+
s.value.refetch();
|
|
269
|
+
}), d(h, (e, t) => {
|
|
260
270
|
if (e) {
|
|
261
|
-
|
|
271
|
+
_.value = e, g.value && L(e);
|
|
262
272
|
return;
|
|
263
273
|
}
|
|
264
|
-
t &&
|
|
274
|
+
t && _.value === t && G();
|
|
265
275
|
});
|
|
266
|
-
function
|
|
276
|
+
function z(t) {
|
|
267
277
|
if (t.status === 401) {
|
|
268
|
-
if (
|
|
269
|
-
|
|
278
|
+
if (n.value) {
|
|
279
|
+
s.value.refetch();
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (_.value) {
|
|
283
|
+
L(_.value);
|
|
270
284
|
return;
|
|
271
285
|
}
|
|
272
|
-
e.awaitExternalToken ||
|
|
286
|
+
e.awaitExternalToken || R();
|
|
273
287
|
}
|
|
274
288
|
}
|
|
275
|
-
let
|
|
276
|
-
async function
|
|
277
|
-
let
|
|
289
|
+
let B = a(() => s.value.data?.user?.id);
|
|
290
|
+
async function V(t, n, i) {
|
|
291
|
+
let a = await o.signIn.email({
|
|
278
292
|
email: t,
|
|
279
|
-
password:
|
|
280
|
-
rememberMe:
|
|
293
|
+
password: n,
|
|
294
|
+
rememberMe: i
|
|
281
295
|
});
|
|
282
|
-
return
|
|
296
|
+
return a.data?.token && (r.value = a.data.token, w(e.cookieName)), a;
|
|
283
297
|
}
|
|
284
|
-
function
|
|
285
|
-
return
|
|
298
|
+
function H(e) {
|
|
299
|
+
return o.signUp.email(e);
|
|
286
300
|
}
|
|
287
|
-
function
|
|
288
|
-
return
|
|
301
|
+
function U(e, t) {
|
|
302
|
+
return o.requestPasswordReset({
|
|
289
303
|
email: e,
|
|
290
304
|
redirectTo: t
|
|
291
305
|
});
|
|
292
306
|
}
|
|
293
|
-
async function
|
|
294
|
-
let
|
|
307
|
+
async function W(t, n) {
|
|
308
|
+
let i = e.baseUrl ?? void 0, a = `${i}/${e.basePath ?? "auth"}/widget-oauth-callback`, c = o.signIn.socialRedirect({
|
|
295
309
|
provider: t,
|
|
296
|
-
callbackURL:
|
|
310
|
+
callbackURL: a
|
|
297
311
|
}), l = window.open(c, "eightwave-oauth", "width=600,height=700,menubar=no,toolbar=no");
|
|
298
312
|
return l ? new Promise((t) => {
|
|
299
|
-
let
|
|
313
|
+
let a = new URL(i).origin, o = setInterval(() => {
|
|
300
314
|
l?.closed && c();
|
|
301
315
|
}, 500);
|
|
302
316
|
function c() {
|
|
303
|
-
window.removeEventListener("message", u), clearInterval(
|
|
317
|
+
window.removeEventListener("message", u), clearInterval(o), t({ popupBlocked: !1 });
|
|
304
318
|
}
|
|
305
319
|
function u(t) {
|
|
306
|
-
t.origin ===
|
|
320
|
+
t.origin === a && t.data?.type === "eightwave-oauth" && (l?.close(), t.data.token && (r.value = t.data.token, w(e.cookieName)), s.value.refetch().then(() => n?.()), c());
|
|
307
321
|
}
|
|
308
322
|
window.addEventListener("message", u);
|
|
309
323
|
}) : { popupBlocked: !0 };
|
|
310
324
|
}
|
|
311
|
-
async function
|
|
312
|
-
|
|
313
|
-
|
|
325
|
+
async function G() {
|
|
326
|
+
if (n.value) return;
|
|
327
|
+
_.value = void 0;
|
|
328
|
+
let e = r.value;
|
|
314
329
|
try {
|
|
315
|
-
await
|
|
330
|
+
await o.signOut();
|
|
316
331
|
} finally {
|
|
317
|
-
|
|
332
|
+
r.value === e && (r.value = ""), await s.value.refetch();
|
|
318
333
|
}
|
|
319
334
|
}
|
|
320
335
|
return {
|
|
321
|
-
client:
|
|
322
|
-
session:
|
|
323
|
-
isAuthenticating:
|
|
324
|
-
isExternalAuth:
|
|
325
|
-
token:
|
|
326
|
-
headers:
|
|
327
|
-
onError:
|
|
328
|
-
userId:
|
|
329
|
-
signInWithEmail:
|
|
330
|
-
signUpWithEmail:
|
|
331
|
-
requestPasswordReset:
|
|
332
|
-
signInSocial:
|
|
333
|
-
signInWithSocialPopup:
|
|
334
|
-
signInWithExternalToken:
|
|
335
|
-
signOut:
|
|
336
|
+
client: o,
|
|
337
|
+
session: s,
|
|
338
|
+
isAuthenticating: c,
|
|
339
|
+
isExternalAuth: v,
|
|
340
|
+
token: r,
|
|
341
|
+
headers: i,
|
|
342
|
+
onError: z,
|
|
343
|
+
userId: B,
|
|
344
|
+
signInWithEmail: V,
|
|
345
|
+
signUpWithEmail: H,
|
|
346
|
+
requestPasswordReset: U,
|
|
347
|
+
signInSocial: o.signIn.social,
|
|
348
|
+
signInWithSocialPopup: W,
|
|
349
|
+
signInWithExternalToken: L,
|
|
350
|
+
signOut: G
|
|
336
351
|
};
|
|
337
352
|
}
|
|
338
353
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composables.js","names":[],"sources":["../src/utils/cookie.ts","../src/utils/index.ts","../src/composables/useChatbotAgent.ts","../../../packages/auth/src/social-redirect-client-plugin.ts","../src/composables/useChatbotAuth.ts","../src/composables/useFocusTrapBypass.ts"],"sourcesContent":["import { logger } from 'utils'\n\nconst DEFAULT_COOKIE_NAME = 'eight-wave-ai'\nconst DEFAULT_USER_ID_COOKIE_SUFFIX = '-uid'\nconst MAX_AGE_ONE_YEAR = 31536000\n\nfunction setCookie(name: string, value: string, maxAge: number) {\n try {\n // A past `expires` accompanies `max-age=0` on deletion: some cookie\n // implementations (older browsers, happy-dom) only honor the former.\n const expires =\n maxAge > 0 ? '' : '; expires=Thu, 01 Jan 1970 00:00:00 GMT'\n document.cookie = `${name}=${value}; path=/; max-age=${maxAge}${expires}; SameSite=Lax; Secure`\n } catch (error) {\n logger.warn(\n '[cookie] setCookie blocked (cross-origin/tracking protection)',\n error,\n )\n }\n}\n\nfunction getCookie(name: string): string {\n try {\n // Only split on the FIRST \"=\" — session tokens are signed with a\n // base64 signature that can contain \"=\" padding, so splitting on every\n // \"=\" would truncate the value and corrupt the token.\n const prefix = `${name}=`\n const row = document.cookie\n .split('; ')\n .find((entry) => entry.startsWith(prefix))\n return row ? row.slice(prefix.length) : ''\n } catch (error) {\n logger.warn(\n '[cookie] getCookie blocked (cross-origin/tracking protection)',\n error,\n )\n return ''\n }\n}\n\n// create first-party session cookie for authentication with 1 year expiry;\n// an empty token (sign-out) deletes the cookie instead of storing an empty value\nexport function setSessionCookie(\n token: string,\n cookieName: string = DEFAULT_COOKIE_NAME,\n) {\n setCookie(cookieName, token, token ? MAX_AGE_ONE_YEAR : 0)\n}\n\n// retrieve first-party session cookie\nexport function getSessionCookie(cookieName: string = DEFAULT_COOKIE_NAME) {\n return getCookie(cookieName)\n}\n\n// persist anonymous userId alongside session token to enable re-auth after session expiry\nexport function setAnonymousUserIdCookie(\n userId: string,\n cookieName: string = DEFAULT_COOKIE_NAME,\n) {\n setCookie(\n `${cookieName}${DEFAULT_USER_ID_COOKIE_SUFFIX}`,\n userId,\n MAX_AGE_ONE_YEAR,\n )\n}\n\n// retrieve persisted anonymous userId\nexport function getAnonymousUserIdCookie(\n cookieName: string = DEFAULT_COOKIE_NAME,\n) {\n return getCookie(`${cookieName}${DEFAULT_USER_ID_COOKIE_SUFFIX}`)\n}\n\n// remove the persisted anonymous userId once the anonymous user links a real\n// account (the linked anonymous user is deleted server-side, so it's stale)\nexport function clearAnonymousUserIdCookie(\n cookieName: string = DEFAULT_COOKIE_NAME,\n) {\n setCookie(`${cookieName}${DEFAULT_USER_ID_COOKIE_SUFFIX}`, '', 0)\n}\n","export * from './cookie'\n\nexport const BACKEND_URL: string = import.meta.env.VITE_BACKEND_URL\nexport const FRONTEND_URL: string = import.meta.env.VITE_FRONTEND_URL\nexport const AUTH_BASE_PATH = 'auth'\n","import type { MaybeRefOrGetter } from 'vue'\nimport { computed, ref, toValue, watch } from 'vue'\nimport type { AgentBranding, AgentPublic, AuthMethods } from 'models'\nimport { createChatbotApiClient } from 'composables'\nimport { resolveContrastColor } from 'components/src/chat/utils'\nimport { BACKEND_URL } from '~/utils'\n\nexport function useChatbotAgent(options: {\n baseUrl?: string\n basePath?: string\n organizationSlug?: MaybeRefOrGetter<string>\n agentId?: MaybeRefOrGetter<string>\n headers?: MaybeRefOrGetter<Record<string, string> | undefined>\n /**\n * Host-provided color scheme override, applied when the agent theme is\n * `auto`. Mirrors the precedence used by the chatbot store: agent theme,\n * then this override, then the OS `prefers-color-scheme`.\n */\n systemTheme?: MaybeRefOrGetter<'light' | 'dark' | undefined>\n}) {\n const agent = ref<AgentPublic>()\n const branding = ref<AgentBranding>()\n const authMethods = ref<AuthMethods>()\n const requiresAuth = ref(false)\n const notFound = ref(false)\n const isLoading = ref(false)\n\n async function load() {\n const orgSlug = toValue(options.organizationSlug)\n const agentId = toValue(options.agentId)\n if (!orgSlug || !agentId) {\n return\n }\n const apiClient = createChatbotApiClient({\n baseUrl: options.baseUrl ?? BACKEND_URL,\n basePath: options.basePath,\n headers: () => toValue(options.headers),\n })\n isLoading.value = true\n try {\n const result = await apiClient.getAgentAccess(orgSlug, agentId)\n authMethods.value = result.authMethods\n if (result.requiresAuth) {\n requiresAuth.value = true\n branding.value = result.branding ?? undefined\n agent.value = undefined\n } else {\n requiresAuth.value = false\n branding.value = undefined\n agent.value = result.agent ?? undefined\n }\n notFound.value = false\n } catch {\n notFound.value = true\n agent.value = undefined\n branding.value = undefined\n requiresAuth.value = false\n } finally {\n isLoading.value = false\n }\n }\n\n // Reload when org/agent changes\n watch(\n [\n () => toValue(options.organizationSlug),\n () => toValue(options.agentId),\n ],\n () => {\n load()\n },\n { immediate: true },\n )\n\n // Reload when headers change meaningfully (e.g. after login) and auth is required\n watch(\n () => toValue(options.headers),\n () => {\n if (requiresAuth.value) {\n load()\n }\n },\n )\n\n // Branding-aware theming: fall back to branding when agent is not available\n const source = computed(\n () => agent.value?.interface ?? branding.value?.interface,\n )\n\n const contrastColor = computed(() =>\n resolveContrastColor(source.value?.textColor, source.value?.mainColor),\n )\n const mainColor = computed(() => source.value?.mainColor)\n\n const themeClass = computed(() => {\n const theme = source.value?.theme\n if (theme && theme !== 'auto') {\n return ['theme', `theme--${theme}`]\n }\n const systemTheme = toValue(options.systemTheme)\n if (systemTheme) {\n return ['theme', `theme--${systemTheme}`]\n }\n return ['theme']\n })\n\n const themeStyle = computed(() =>\n source.value\n ? {\n '--chatbot-main-color': mainColor.value,\n '--chatbot-contrast-color': contrastColor.value,\n }\n : undefined,\n )\n\n return {\n agent,\n branding,\n authMethods,\n requiresAuth,\n notFound,\n isLoading,\n reload: load,\n contrastColor,\n mainColor,\n themeClass,\n themeStyle,\n }\n}\n","import type { BetterAuthClientPlugin } from 'better-auth/client'\nimport type { socialRedirect } from './social-redirect-plugin'\n\n/**\n * Client counterpart of the server `socialRedirect()` plugin.\n *\n * Exposes `authClient.signIn.socialRedirect(...)`, which builds the URL of the\n * backend `/sign-in/social-redirect` endpoint from the client's configured\n * `baseURL`/`basePath` and returns it as a string.\n *\n * It intentionally does NOT navigate: the caller decides how to use the URL —\n * the main app does a top-level `window.location` navigation, the embeddable\n * widget opens it in a popup. Building the URL here (instead of a cross-origin\n * `signIn.social` XHR) keeps the OAuth `state` cookie first-party on the backend\n * origin and avoids the `state_mismatch` failure.\n */\nexport function socialRedirectClient() {\n return {\n id: 'social-redirect',\n $InferServerPlugin: {} as ReturnType<typeof socialRedirect>,\n getActions: (_$fetch, _$store, options) => ({\n signIn: {\n socialRedirect: (opts: {\n provider: string\n callbackURL: string\n errorCallbackURL?: string\n }): string => {\n const baseURL = options?.baseURL ?? ''\n const basePath = options?.basePath ?? 'auth'\n const params = new URLSearchParams({\n provider: opts.provider,\n callbackURL: opts.callbackURL,\n })\n if (opts.errorCallbackURL) {\n params.set('errorCallbackURL', opts.errorCallbackURL)\n }\n return `${baseURL}/${basePath}/sign-in/social-redirect?${params}`\n },\n },\n }),\n } satisfies BetterAuthClientPlugin\n}\n","import type { MaybeRefOrGetter } from 'vue'\nimport type { BetterAuthClientPlugin } from 'better-auth/client'\nimport type { auth } from 'auth'\nimport { socialRedirectClient } from 'auth/src/social-redirect-client-plugin'\nimport { computed, ref, watch, toValue } from 'vue'\nimport { createAuthClient } from 'better-auth/vue'\nimport {\n inferAdditionalFields,\n anonymousClient,\n} from 'better-auth/client/plugins'\nimport {\n getSessionCookie,\n setSessionCookie,\n getAnonymousUserIdCookie,\n setAnonymousUserIdCookie,\n clearAnonymousUserIdCookie,\n BACKEND_URL,\n AUTH_BASE_PATH,\n} from '~/utils'\n\nfunction anonymousReauthClient() {\n return {\n id: 'anonymous-reauth' as const,\n getActions: ($fetch) => ({\n signIn: {\n anonymousReauth: async (data: { userId: string }) => {\n return $fetch<{\n token: string\n user: Record<string, unknown>\n }>('/sign-in/anonymous-reauth', {\n method: 'POST',\n body: data,\n })\n },\n },\n }),\n pathMethods: {\n '/sign-in/anonymous-reauth': 'POST' as const,\n },\n } satisfies BetterAuthClientPlugin\n}\n\nfunction externalAuthClient() {\n return {\n id: 'external-auth' as const,\n getActions: ($fetch) => ({\n signIn: {\n externalToken: async (data: { token: string }) => {\n return $fetch<{\n token: string\n user: Record<string, unknown>\n }>('/sign-in/external-token', {\n method: 'POST',\n body: data,\n })\n },\n },\n }),\n pathMethods: {\n '/sign-in/external-token': 'POST' as const,\n },\n atomListeners: [\n {\n matcher: (path: string) => path === '/sign-in/external-token',\n signal: '$sessionSignal' as const,\n },\n ],\n } satisfies BetterAuthClientPlugin\n}\n\ntype AuthClientOptions = {\n baseURL?: string\n basePath?: string\n plugins: [\n ReturnType<typeof inferAdditionalFields<typeof auth>>,\n ReturnType<typeof anonymousClient>,\n ReturnType<typeof anonymousReauthClient>,\n ReturnType<typeof externalAuthClient>,\n ReturnType<typeof socialRedirectClient>,\n ]\n}\n\nexport type AuthClient = ReturnType<typeof createAuthClient<AuthClientOptions>>\n\nexport function useChatbotAuth(\n options: {\n baseUrl?: string\n basePath?: string\n externalToken?: MaybeRefOrGetter<string | undefined>\n awaitExternalToken?: boolean\n isActive?: MaybeRefOrGetter<boolean>\n cookieName?: string\n /** Called when a session is established or the user changes. */\n onSignedIn?: (detail: { userId: string; isAnonymous: boolean }) => void\n /** Called when the session ends without an immediate replacement. */\n onSignedOut?: () => void\n /**\n * Called when an external token sign-in fails (expired token, invalid\n * signature or disabled config): the host should provide a fresh token.\n */\n onExternalAuthExpired?: () => void\n } = {},\n) {\n const _token = ref(getSessionCookie(options.cookieName))\n\n const token = computed({\n get: () => _token.value,\n set: (token: string) => {\n setSessionCookie(token, options.cookieName)\n _token.value = token\n },\n })\n\n const headers = computed(() =>\n token.value ? { Authorization: `Bearer ${token.value}` } : undefined,\n )\n\n const client = createAuthClient({\n baseURL: options.baseUrl ?? BACKEND_URL,\n basePath: options.basePath ?? AUTH_BASE_PATH,\n plugins: [\n inferAdditionalFields<typeof auth>(),\n anonymousClient(),\n anonymousReauthClient(),\n externalAuthClient(),\n socialRedirectClient(),\n ],\n fetchOptions: {\n onSuccess: (ctx) => {\n const newTokenFromHeader =\n ctx.response.headers.get('set-auth-token')\n if (newTokenFromHeader) {\n token.value = newTokenFromHeader\n }\n },\n auth: {\n type: 'Bearer',\n token: () => token.value,\n },\n },\n }) as AuthClient\n\n const session = client.useSession()\n const isAuthenticating = ref(false)\n const externalToken = computed(() => toValue(options.externalToken))\n const isActive = computed(() => toValue(options.isActive) ?? true)\n\n /**\n * External token currently authorized for automatic (re-)authentication.\n * Follows the `externalToken` option and imperative sign-in calls; cleared\n * by `signOut`, so the watchers never re-login with a token the host has\n * withdrawn.\n */\n const activeExternalToken = ref(externalToken.value)\n\n // External token mode: the host application owns the auth lifecycle,\n // so the widget must not offer account management (sign-out, profile).\n const isExternalAuth = computed(\n () =>\n !!externalToken.value ||\n !!options.awaitExternalToken ||\n !!activeExternalToken.value,\n )\n\n /**\n * Serializes sign-in operations: a request arriving while another is in\n * flight runs right after it instead of being dropped. Each task re-checks\n * state when it starts, so superseded requests no-op.\n */\n let authChain: Promise<void> = Promise.resolve()\n function runExclusiveAuth(task: () => Promise<void>): Promise<void> {\n const run = authChain.then(task, task)\n authChain = run\n return run\n }\n\n // Notify the host on session transitions, whatever flow caused them\n // (anonymous, external token, email, social).\n watch(\n () => session.value.data?.user,\n (user, previousUser) => {\n if (user && user.id !== previousUser?.id) {\n options.onSignedIn?.({\n userId: user.id,\n isAnonymous: !!user.isAnonymous,\n })\n }\n if (!user && previousUser) {\n options.onSignedOut?.()\n }\n },\n )\n\n async function signInAnonymously(): Promise<void> {\n const { data } = await client.signIn.anonymous()\n if (data?.token) {\n token.value = data.token\n if (data.user?.id) {\n setAnonymousUserIdCookie(\n data.user.id as string,\n options.cookieName,\n )\n }\n }\n }\n\n /**\n * Attempts to create a new session for an existing anonymous user.\n * Returns true if re-auth succeeded, false otherwise.\n */\n async function tryAnonymousReauth(): Promise<boolean> {\n const storedUserId = getAnonymousUserIdCookie(options.cookieName)\n if (!storedUserId) {\n return false\n }\n try {\n const { data } = await client.signIn.anonymousReauth({\n userId: storedUserId,\n })\n if (data?.token) {\n token.value = data.token\n await session.value.refetch()\n return true\n }\n } catch {\n // user record may have been deleted — fall back to creating a new one\n }\n return false\n }\n\n /** Re-authenticates the stored anonymous user, or creates a fresh one. */\n async function reauthOrSignInAnonymously(): Promise<void> {\n const reauthed = await tryAnonymousReauth()\n if (!reauthed) {\n await signInAnonymously()\n }\n }\n\n /**\n * The external token was rejected (expired, invalid or config disabled):\n * notify the host so it can provide a fresh one, then apply the fallback\n * (keep waiting in `awaitExternalToken` mode, anonymous otherwise).\n */\n async function handleExternalTokenFailure(\n failedExternalToken: string,\n ): Promise<void> {\n if (activeExternalToken.value !== failedExternalToken) {\n // Withdrawn (sign-out) or replaced while the request was in flight\n return\n }\n options.onExternalAuthExpired?.()\n if (options.awaitExternalToken) {\n return\n }\n await reauthOrSignInAnonymously()\n }\n\n /** Raw token exchange; must run inside `runExclusiveAuth`. */\n async function exchangeExternalToken(\n newExternalToken: string,\n ): Promise<void> {\n isAuthenticating.value = true\n try {\n const { data } = await client.signIn.externalToken({\n token: newExternalToken,\n })\n if (activeExternalToken.value !== newExternalToken) {\n // Withdrawn (sign-out) or replaced while the request was in flight\n return\n }\n if (data?.token) {\n token.value = data.token\n await session.value.refetch()\n return\n }\n await handleExternalTokenFailure(newExternalToken)\n } catch {\n await handleExternalTokenFailure(newExternalToken)\n } finally {\n isAuthenticating.value = false\n }\n }\n\n /** External token currently queued or in flight, to drop duplicate requests. */\n let pendingExternalToken: string | undefined\n\n /**\n * Last external token an exchange was attempted with, whatever the\n * trigger. The session watcher consults it so a token that was already\n * tried is never auto-retried: a rejected token must not loop between\n * exchange and fallback. Deliberate retries (prop change, imperative\n * `signIn`, 401 handler) bypass it.\n */\n let attemptedExternalToken: string | undefined\n\n /** The cookie-restored external session was reconciled with the host state. */\n let externalSessionReconciled = false\n\n async function signInWithExternalToken(\n newExternalToken: string,\n ): Promise<void> {\n if (!newExternalToken || pendingExternalToken === newExternalToken) {\n return\n }\n activeExternalToken.value = newExternalToken\n attemptedExternalToken = newExternalToken\n pendingExternalToken = newExternalToken\n try {\n await runExclusiveAuth(async () => {\n if (activeExternalToken.value !== newExternalToken) {\n // Superseded while waiting for the previous operation\n return\n }\n await exchangeExternalToken(newExternalToken)\n })\n } finally {\n pendingExternalToken = undefined\n }\n }\n\n async function signInAnonymouslyWithFallback(): Promise<void> {\n if (isAuthenticating.value) {\n // The `[isActive, session]` watcher retries on the next session change\n return\n }\n await runExclusiveAuth(async () => {\n if (activeExternalToken.value) {\n // An external sign-in was requested in the meantime\n return\n }\n isAuthenticating.value = true\n try {\n await reauthOrSignInAnonymously()\n } finally {\n isAuthenticating.value = false\n }\n })\n }\n\n watch(\n [isActive, session],\n ([newIsActive, newSession]) => {\n if (!newIsActive) {\n return\n }\n if (\n !newSession.isPending &&\n !newSession.isRefetching &&\n !isAuthenticating.value &&\n !newSession.data\n ) {\n if (activeExternalToken.value) {\n signInWithExternalToken(activeExternalToken.value)\n return\n }\n if (options.awaitExternalToken) {\n return\n }\n signInAnonymouslyWithFallback()\n return\n }\n\n const user = newSession.data?.user\n\n // A session created by an external token exchange (the only kind\n // with `hardExpiresAt`) may have been restored from the cookie\n // after a page navigation, where prop changes cannot be observed:\n // reconcile it once with the auth state the host declares now.\n if (\n newSession.data?.session?.hardExpiresAt &&\n !externalSessionReconciled\n ) {\n externalSessionReconciled = true\n if (!activeExternalToken.value) {\n // The host no longer provides a token (it logged out\n // before this page load): drop the stale session. The\n // usual fallback rules apply (keep waiting in\n // `awaitExternalToken` mode, anonymous otherwise).\n signOut()\n return\n }\n if (attemptedExternalToken !== activeExternalToken.value) {\n // Re-exchange the current token, so a token minted for a\n // different user replaces the restored session and an\n // expired one raises `external-auth-expired` right away.\n signInWithExternalToken(activeExternalToken.value)\n return\n }\n }\n\n // Upgrade an anonymous session restored from the cookie when an\n // external token is available (e.g. the user logged into the host\n // app between page loads). Attempted once per token.\n if (\n user?.isAnonymous &&\n activeExternalToken.value &&\n attemptedExternalToken !== activeExternalToken.value\n ) {\n signInWithExternalToken(activeExternalToken.value)\n return\n }\n\n // Backfill userId cookie for existing anonymous sessions\n if (\n user?.isAnonymous &&\n user.id &&\n !getAnonymousUserIdCookie(options.cookieName)\n ) {\n setAnonymousUserIdCookie(user.id as string, options.cookieName)\n }\n },\n { immediate: true },\n )\n\n watch(externalToken, (newToken, previousToken) => {\n if (newToken) {\n // Track the intent even when inactive: the `[isActive, session]`\n // watcher signs in with it once the widget becomes active.\n activeExternalToken.value = newToken\n if (isActive.value) {\n signInWithExternalToken(newToken)\n }\n return\n }\n // The host withdrew the token (logged out): sign out without\n // re-authenticating; the fallback rules apply (keep waiting in\n // `awaitExternalToken` mode, anonymous otherwise).\n if (previousToken && activeExternalToken.value === previousToken) {\n signOut()\n }\n })\n\n function onError(error: { type: string; title: string; status: number }) {\n if (error.status !== 401) {\n return\n }\n if (activeExternalToken.value) {\n signInWithExternalToken(activeExternalToken.value)\n return\n }\n if (options.awaitExternalToken) {\n return\n }\n signInAnonymouslyWithFallback()\n }\n\n const userId = computed(() => session.value.data?.user?.id)\n\n // Auth methods for widget login screen\n async function signInWithEmail(\n email: string,\n password: string,\n rememberMe?: boolean,\n ) {\n const result = await client.signIn.email({\n email,\n password,\n rememberMe,\n })\n // Capture the session token from the response body. Relying solely on\n // the `set-auth-token` header is unreliable here, so we mirror the\n // anonymous/social flows and store the token explicitly.\n if (result.data?.token) {\n token.value = result.data.token\n // Signing in from an anonymous session links (and deletes) the\n // anonymous user server-side, so its stored userId can no longer\n // be re-authenticated — drop it so a later sign-out falls straight\n // back to a fresh anonymous session.\n clearAnonymousUserIdCookie(options.cookieName)\n }\n return result\n }\n\n function signUpWithEmail(params: {\n email: string\n password: string\n name: string\n }) {\n return client.signUp.email(params)\n }\n\n function requestPasswordReset(email: string, redirectTo: string) {\n return client.requestPasswordReset({ email, redirectTo })\n }\n\n /**\n * Opens a popup for social OAuth sign-in.\n *\n * The popup performs a top-level navigation to the backend `social-redirect`\n * endpoint, so the OAuth `state` cookie is set first-party on the backend\n * origin and survives the round-trip (a cross-origin XHR `signIn.social`\n * would set it as a third-party cookie that browsers drop, causing\n * `state_mismatch`). After the flow completes, the backend callback page\n * posts back the session Bearer token, which we store before refetching.\n */\n async function signInWithSocialPopup(\n provider: 'google' | 'microsoft',\n onAuthSuccess?: () => void,\n ): Promise<{ popupBlocked: boolean }> {\n const baseUrl = options.baseUrl ?? BACKEND_URL\n const basePath = options.basePath ?? AUTH_BASE_PATH\n const callbackURL = `${baseUrl}/${basePath}/widget-oauth-callback`\n const redirectUrl = client.signIn.socialRedirect({\n provider,\n callbackURL,\n })\n\n const popup = window.open(\n redirectUrl,\n 'eightwave-oauth',\n 'width=600,height=700,menubar=no,toolbar=no',\n )\n\n if (!popup) {\n return { popupBlocked: true }\n }\n\n return new Promise((resolve) => {\n const allowedOrigin = new URL(baseUrl).origin\n // Resolve too if the user closes the popup without completing OAuth.\n const closedPollId = setInterval(() => {\n if (popup?.closed) {\n finish()\n }\n }, 500)\n\n function finish() {\n window.removeEventListener('message', onOAuthMessage)\n clearInterval(closedPollId)\n resolve({ popupBlocked: false })\n }\n\n function onOAuthMessage(event: MessageEvent) {\n const isTrustedOAuthMessage =\n event.origin === allowedOrigin &&\n event.data?.type === 'eightwave-oauth'\n if (!isTrustedOAuthMessage) {\n return\n }\n popup?.close()\n if (event.data.token) {\n token.value = event.data.token\n // Same as the email flow: the anonymous user was linked\n // and deleted server-side, its stored userId is stale.\n clearAnonymousUserIdCookie(options.cookieName)\n }\n session.value.refetch().then(() => onAuthSuccess?.())\n finish()\n }\n\n window.addEventListener('message', onOAuthMessage)\n })\n }\n\n /**\n * Signs the current user out and clears the locally stored session token.\n * Withdraws the active external token first, so the watchers never\n * re-authenticate with it: in external mode the host signs the widget back\n * in (prop change or `signInWithExternalToken`), while in the other modes\n * the `[isActive, session]` watcher re-establishes an anonymous session\n * (re-using the stored anonymous userId cookie when present).\n */\n async function signOut(): Promise<void> {\n activeExternalToken.value = undefined\n const revokedToken = token.value\n try {\n await client.signOut()\n } finally {\n // A concurrent exchange may have stored a fresh session token\n // while the revoke request was in flight (e.g. a host `signIn`\n // racing the mount-time reconcile): only clear our own token,\n // never the newer one.\n if (token.value === revokedToken) {\n token.value = ''\n }\n await session.value.refetch()\n }\n }\n\n return {\n client,\n session,\n isAuthenticating,\n isExternalAuth,\n token,\n headers,\n onError,\n userId,\n signInWithEmail,\n signUpWithEmail,\n requestPasswordReset,\n signInSocial: client.signIn.social,\n signInWithSocialPopup,\n signInWithExternalToken,\n signOut,\n }\n}\n","import { getCurrentInstance, onBeforeUnmount, onMounted } from 'vue'\n\n/**\n * Prevents external focus-trapping libraries (e.g. Bootstrap 5 modals) from\n * stealing focus away from elements inside this custom element's shadow root.\n *\n * Bootstrap modals attach a `focusin` listener on `document` that pulls focus\n * back whenever it leaves the modal subtree. Because our custom element lives\n * outside the modal in the DOM, every focus event that escapes the shadow root\n * is treated as a \"focus escape\" and gets redirected back into the modal.\n *\n * Fix: intercept `focusin` at the shadow root boundary in the **bubble phase**\n * and stop its propagation. The event still reaches every internal element\n * normally (capture → target → bubble up through the shadow tree), but is\n * stopped at the shadow root before it can re-target and continue to `document`.\n *\n * Must be called inside a `.ce.vue` root component's `<script setup>`.\n */\nexport function useFocusTrapBypass() {\n let shadowRoot: ShadowRoot | null = null\n\n const stopFocusPropagation = (event: Event) => {\n event.stopPropagation()\n }\n\n onMounted(() => {\n const instance = getCurrentInstance()\n const rootNode = instance?.proxy?.$el?.getRootNode()\n\n if (!(rootNode instanceof ShadowRoot)) {\n return\n }\n\n shadowRoot = rootNode\n shadowRoot.addEventListener('focusin', stopFocusPropagation)\n })\n\n onBeforeUnmount(() => {\n if (!shadowRoot) {\n return\n }\n\n shadowRoot.removeEventListener('focusin', stopFocusPropagation)\n shadowRoot = null\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"composables.js","names":[],"sources":["../src/utils/cookie.ts","../src/utils/index.ts","../src/composables/useChatbotAgent.ts","../../../packages/auth/src/social-redirect-client-plugin.ts","../src/composables/useChatbotAuth.ts","../src/composables/useFocusTrapBypass.ts"],"sourcesContent":["import { logger } from 'utils'\n\nconst DEFAULT_COOKIE_NAME = 'eight-wave-ai'\nconst DEFAULT_USER_ID_COOKIE_SUFFIX = '-uid'\nconst MAX_AGE_ONE_YEAR = 31536000\n\nfunction setCookie(name: string, value: string, maxAge: number) {\n try {\n // A past `expires` accompanies `max-age=0` on deletion: some cookie\n // implementations (older browsers, happy-dom) only honor the former.\n const expires =\n maxAge > 0 ? '' : '; expires=Thu, 01 Jan 1970 00:00:00 GMT'\n document.cookie = `${name}=${value}; path=/; max-age=${maxAge}${expires}; SameSite=Lax; Secure`\n } catch (error) {\n logger.warn(\n '[cookie] setCookie blocked (cross-origin/tracking protection)',\n error,\n )\n }\n}\n\nfunction getCookie(name: string): string {\n try {\n // Only split on the FIRST \"=\" — session tokens are signed with a\n // base64 signature that can contain \"=\" padding, so splitting on every\n // \"=\" would truncate the value and corrupt the token.\n const prefix = `${name}=`\n const row = document.cookie\n .split('; ')\n .find((entry) => entry.startsWith(prefix))\n return row ? row.slice(prefix.length) : ''\n } catch (error) {\n logger.warn(\n '[cookie] getCookie blocked (cross-origin/tracking protection)',\n error,\n )\n return ''\n }\n}\n\n// create first-party session cookie for authentication with 1 year expiry;\n// an empty token (sign-out) deletes the cookie instead of storing an empty value\nexport function setSessionCookie(\n token: string,\n cookieName: string = DEFAULT_COOKIE_NAME,\n) {\n setCookie(cookieName, token, token ? MAX_AGE_ONE_YEAR : 0)\n}\n\n// retrieve first-party session cookie\nexport function getSessionCookie(cookieName: string = DEFAULT_COOKIE_NAME) {\n return getCookie(cookieName)\n}\n\n// persist anonymous userId alongside session token to enable re-auth after session expiry\nexport function setAnonymousUserIdCookie(\n userId: string,\n cookieName: string = DEFAULT_COOKIE_NAME,\n) {\n setCookie(\n `${cookieName}${DEFAULT_USER_ID_COOKIE_SUFFIX}`,\n userId,\n MAX_AGE_ONE_YEAR,\n )\n}\n\n// retrieve persisted anonymous userId\nexport function getAnonymousUserIdCookie(\n cookieName: string = DEFAULT_COOKIE_NAME,\n) {\n return getCookie(`${cookieName}${DEFAULT_USER_ID_COOKIE_SUFFIX}`)\n}\n\n// remove the persisted anonymous userId once the anonymous user links a real\n// account (the linked anonymous user is deleted server-side, so it's stale)\nexport function clearAnonymousUserIdCookie(\n cookieName: string = DEFAULT_COOKIE_NAME,\n) {\n setCookie(`${cookieName}${DEFAULT_USER_ID_COOKIE_SUFFIX}`, '', 0)\n}\n","export * from './cookie'\n\nexport const BACKEND_URL: string = import.meta.env.VITE_BACKEND_URL\nexport const FRONTEND_URL: string = import.meta.env.VITE_FRONTEND_URL\nexport const AUTH_BASE_PATH = 'auth'\n","import type { MaybeRefOrGetter } from 'vue'\nimport { computed, ref, toValue, watch } from 'vue'\nimport type { AgentBranding, AgentPublic, AuthMethods } from 'models'\nimport { createChatbotApiClient } from 'composables'\nimport { resolveContrastColor } from 'components/src/chat/utils'\nimport { BACKEND_URL } from '~/utils'\n\nexport function useChatbotAgent(options: {\n baseUrl?: string\n basePath?: string\n organizationSlug?: MaybeRefOrGetter<string>\n agentId?: MaybeRefOrGetter<string>\n headers?: MaybeRefOrGetter<Record<string, string> | undefined>\n /**\n * Host-provided color scheme override, applied when the agent theme is\n * `auto`. Mirrors the precedence used by the chatbot store: agent theme,\n * then this override, then the OS `prefers-color-scheme`.\n */\n systemTheme?: MaybeRefOrGetter<'light' | 'dark' | undefined>\n}) {\n const agent = ref<AgentPublic>()\n const branding = ref<AgentBranding>()\n const authMethods = ref<AuthMethods>()\n const requiresAuth = ref(false)\n const notFound = ref(false)\n const isLoading = ref(false)\n\n async function load() {\n const orgSlug = toValue(options.organizationSlug)\n const agentId = toValue(options.agentId)\n if (!orgSlug || !agentId) {\n return\n }\n const apiClient = createChatbotApiClient({\n baseUrl: options.baseUrl ?? BACKEND_URL,\n basePath: options.basePath,\n headers: () => toValue(options.headers),\n })\n isLoading.value = true\n try {\n const result = await apiClient.getAgentAccess(orgSlug, agentId)\n authMethods.value = result.authMethods\n if (result.requiresAuth) {\n requiresAuth.value = true\n branding.value = result.branding ?? undefined\n agent.value = undefined\n } else {\n requiresAuth.value = false\n branding.value = undefined\n agent.value = result.agent ?? undefined\n }\n notFound.value = false\n } catch {\n notFound.value = true\n agent.value = undefined\n branding.value = undefined\n requiresAuth.value = false\n } finally {\n isLoading.value = false\n }\n }\n\n // Reload when org/agent changes\n watch(\n [\n () => toValue(options.organizationSlug),\n () => toValue(options.agentId),\n ],\n () => {\n load()\n },\n { immediate: true },\n )\n\n // Reload when headers change meaningfully (e.g. after login) and auth is required\n watch(\n () => toValue(options.headers),\n () => {\n if (requiresAuth.value) {\n load()\n }\n },\n )\n\n // Branding-aware theming: fall back to branding when agent is not available\n const source = computed(\n () => agent.value?.interface ?? branding.value?.interface,\n )\n\n const contrastColor = computed(() =>\n resolveContrastColor(source.value?.textColor, source.value?.mainColor),\n )\n const mainColor = computed(() => source.value?.mainColor)\n\n const themeClass = computed(() => {\n const theme = source.value?.theme\n if (theme && theme !== 'auto') {\n return ['theme', `theme--${theme}`]\n }\n const systemTheme = toValue(options.systemTheme)\n if (systemTheme) {\n return ['theme', `theme--${systemTheme}`]\n }\n return ['theme']\n })\n\n const themeStyle = computed(() =>\n source.value\n ? {\n '--chatbot-main-color': mainColor.value,\n '--chatbot-contrast-color': contrastColor.value,\n }\n : undefined,\n )\n\n return {\n agent,\n branding,\n authMethods,\n requiresAuth,\n notFound,\n isLoading,\n reload: load,\n contrastColor,\n mainColor,\n themeClass,\n themeStyle,\n }\n}\n","import type { BetterAuthClientPlugin } from 'better-auth/client'\nimport type { socialRedirect } from './social-redirect-plugin'\n\n/**\n * Client counterpart of the server `socialRedirect()` plugin.\n *\n * Exposes `authClient.signIn.socialRedirect(...)`, which builds the URL of the\n * backend `/sign-in/social-redirect` endpoint from the client's configured\n * `baseURL`/`basePath` and returns it as a string.\n *\n * It intentionally does NOT navigate: the caller decides how to use the URL —\n * the main app does a top-level `window.location` navigation, the embeddable\n * widget opens it in a popup. Building the URL here (instead of a cross-origin\n * `signIn.social` XHR) keeps the OAuth `state` cookie first-party on the backend\n * origin and avoids the `state_mismatch` failure.\n */\nexport function socialRedirectClient() {\n return {\n id: 'social-redirect',\n $InferServerPlugin: {} as ReturnType<typeof socialRedirect>,\n getActions: (_$fetch, _$store, options) => ({\n signIn: {\n socialRedirect: (opts: {\n provider: string\n callbackURL: string\n errorCallbackURL?: string\n }): string => {\n const baseURL = options?.baseURL ?? ''\n const basePath = options?.basePath ?? 'auth'\n const params = new URLSearchParams({\n provider: opts.provider,\n callbackURL: opts.callbackURL,\n })\n if (opts.errorCallbackURL) {\n params.set('errorCallbackURL', opts.errorCallbackURL)\n }\n return `${baseURL}/${basePath}/sign-in/social-redirect?${params}`\n },\n },\n }),\n } satisfies BetterAuthClientPlugin\n}\n","import type { MaybeRefOrGetter } from 'vue'\nimport type { BetterAuthClientPlugin } from 'better-auth/client'\nimport type { auth } from 'auth'\nimport { socialRedirectClient } from 'auth/src/social-redirect-client-plugin'\nimport { computed, ref, watch, toValue } from 'vue'\nimport { createAuthClient } from 'better-auth/vue'\nimport {\n inferAdditionalFields,\n anonymousClient,\n} from 'better-auth/client/plugins'\nimport {\n getSessionCookie,\n setSessionCookie,\n getAnonymousUserIdCookie,\n setAnonymousUserIdCookie,\n clearAnonymousUserIdCookie,\n BACKEND_URL,\n AUTH_BASE_PATH,\n} from '~/utils'\n\nfunction anonymousReauthClient() {\n return {\n id: 'anonymous-reauth' as const,\n getActions: ($fetch) => ({\n signIn: {\n anonymousReauth: async (data: { userId: string }) => {\n return $fetch<{\n token: string\n user: Record<string, unknown>\n }>('/sign-in/anonymous-reauth', {\n method: 'POST',\n body: data,\n })\n },\n },\n }),\n pathMethods: {\n '/sign-in/anonymous-reauth': 'POST' as const,\n },\n } satisfies BetterAuthClientPlugin\n}\n\nfunction externalAuthClient() {\n return {\n id: 'external-auth' as const,\n getActions: ($fetch) => ({\n signIn: {\n externalToken: async (data: { token: string }) => {\n return $fetch<{\n token: string\n user: Record<string, unknown>\n }>('/sign-in/external-token', {\n method: 'POST',\n body: data,\n })\n },\n },\n }),\n pathMethods: {\n '/sign-in/external-token': 'POST' as const,\n },\n atomListeners: [\n {\n matcher: (path: string) => path === '/sign-in/external-token',\n signal: '$sessionSignal' as const,\n },\n ],\n } satisfies BetterAuthClientPlugin\n}\n\ntype AuthClientOptions = {\n baseURL?: string\n basePath?: string\n plugins: [\n ReturnType<typeof inferAdditionalFields<typeof auth>>,\n ReturnType<typeof anonymousClient>,\n ReturnType<typeof anonymousReauthClient>,\n ReturnType<typeof externalAuthClient>,\n ReturnType<typeof socialRedirectClient>,\n ]\n}\n\nexport type AuthClient = ReturnType<typeof createAuthClient<AuthClientOptions>>\n\nexport function useChatbotAuth(\n options: {\n baseUrl?: string\n basePath?: string\n externalToken?: MaybeRefOrGetter<string | undefined>\n awaitExternalToken?: boolean\n /**\n * Host-session mode: the widget never signs in on its own and sends\n * no Authorization header; the session comes from the host\n * platform's better-auth cookie (sent thanks to\n * `credentials: 'include'`). The session belongs to the host, so\n * account management is hidden and `signOut` is a no-op. Intended\n * for the widget embedded in the platform itself (Backoffice\n * copilot, spec platform-copilot-session-bridge.md); it only works\n * where the host cookie context works, not on third-party embeds.\n */\n useHostSession?: MaybeRefOrGetter<boolean | undefined>\n isActive?: MaybeRefOrGetter<boolean>\n cookieName?: string\n /** Called when a session is established or the user changes. */\n onSignedIn?: (detail: { userId: string; isAnonymous: boolean }) => void\n /** Called when the session ends without an immediate replacement. */\n onSignedOut?: () => void\n /**\n * Called when an external token sign-in fails (expired token, invalid\n * signature or disabled config): the host should provide a fresh token.\n */\n onExternalAuthExpired?: () => void\n } = {},\n) {\n const _token = ref(getSessionCookie(options.cookieName))\n const useHostSession = computed(\n () => toValue(options.useHostSession) ?? false,\n )\n\n const token = computed({\n get: () => _token.value,\n set: (token: string) => {\n setSessionCookie(token, options.cookieName)\n _token.value = token\n },\n })\n\n const headers = computed(() =>\n !useHostSession.value && token.value\n ? { Authorization: `Bearer ${token.value}` }\n : undefined,\n )\n\n const client = createAuthClient({\n baseURL: options.baseUrl ?? BACKEND_URL,\n basePath: options.basePath ?? AUTH_BASE_PATH,\n plugins: [\n inferAdditionalFields<typeof auth>(),\n anonymousClient(),\n anonymousReauthClient(),\n externalAuthClient(),\n socialRedirectClient(),\n ],\n fetchOptions: {\n onSuccess: (ctx) => {\n if (useHostSession.value) {\n // The host session token must never be persisted as the\n // widget's own Bearer: it would keep impersonating the\n // host after the mode is turned off.\n return\n }\n const newTokenFromHeader =\n ctx.response.headers.get('set-auth-token')\n if (newTokenFromHeader) {\n token.value = newTokenFromHeader\n }\n },\n auth: {\n type: 'Bearer',\n // Host-session mode relies on the cookie alone: the widget's\n // stored Bearer (e.g. a previous anonymous session) would\n // shadow it.\n token: () => (useHostSession.value ? '' : token.value),\n },\n // Only host-session mode may exchange cookies: with credentials\n // on, an anonymous sign-in's Set-Cookie would REPLACE the user's\n // platform session cookie on the backend origin.\n customFetchImpl: (input, init) =>\n fetch(input, {\n ...init,\n credentials: useHostSession.value\n ? 'include'\n : init?.credentials,\n }),\n },\n }) as AuthClient\n\n const session = client.useSession()\n const isAuthenticating = ref(false)\n const externalToken = computed(() => toValue(options.externalToken))\n const isActive = computed(() => toValue(options.isActive) ?? true)\n\n /**\n * External token currently authorized for automatic (re-)authentication.\n * Follows the `externalToken` option and imperative sign-in calls; cleared\n * by `signOut`, so the watchers never re-login with a token the host has\n * withdrawn.\n */\n const activeExternalToken = ref(externalToken.value)\n\n // External token / host-session mode: the host application owns the auth\n // lifecycle, so the widget must not offer account management (sign-out,\n // profile).\n const isExternalAuth = computed(\n () =>\n !!externalToken.value ||\n !!options.awaitExternalToken ||\n !!activeExternalToken.value ||\n useHostSession.value,\n )\n\n /**\n * Serializes sign-in operations: a request arriving while another is in\n * flight runs right after it instead of being dropped. Each task re-checks\n * state when it starts, so superseded requests no-op.\n */\n let authChain: Promise<void> = Promise.resolve()\n function runExclusiveAuth(task: () => Promise<void>): Promise<void> {\n const run = authChain.then(task, task)\n authChain = run\n return run\n }\n\n // Notify the host on session transitions, whatever flow caused them\n // (anonymous, external token, email, social).\n watch(\n () => session.value.data?.user,\n (user, previousUser) => {\n if (user && user.id !== previousUser?.id) {\n options.onSignedIn?.({\n userId: user.id,\n isAnonymous: !!user.isAnonymous,\n })\n }\n if (!user && previousUser) {\n options.onSignedOut?.()\n }\n },\n )\n\n async function signInAnonymously(): Promise<void> {\n const { data } = await client.signIn.anonymous()\n if (data?.token) {\n token.value = data.token\n if (data.user?.id) {\n setAnonymousUserIdCookie(\n data.user.id as string,\n options.cookieName,\n )\n }\n }\n }\n\n /**\n * Attempts to create a new session for an existing anonymous user.\n * Returns true if re-auth succeeded, false otherwise.\n */\n async function tryAnonymousReauth(): Promise<boolean> {\n const storedUserId = getAnonymousUserIdCookie(options.cookieName)\n if (!storedUserId) {\n return false\n }\n try {\n const { data } = await client.signIn.anonymousReauth({\n userId: storedUserId,\n })\n if (data?.token) {\n token.value = data.token\n await session.value.refetch()\n return true\n }\n } catch {\n // user record may have been deleted — fall back to creating a new one\n }\n return false\n }\n\n /** Re-authenticates the stored anonymous user, or creates a fresh one. */\n async function reauthOrSignInAnonymously(): Promise<void> {\n const reauthed = await tryAnonymousReauth()\n if (!reauthed) {\n await signInAnonymously()\n }\n }\n\n /**\n * The external token was rejected (expired, invalid or config disabled):\n * notify the host so it can provide a fresh one, then apply the fallback\n * (keep waiting in `awaitExternalToken` mode, anonymous otherwise).\n */\n async function handleExternalTokenFailure(\n failedExternalToken: string,\n ): Promise<void> {\n if (activeExternalToken.value !== failedExternalToken) {\n // Withdrawn (sign-out) or replaced while the request was in flight\n return\n }\n options.onExternalAuthExpired?.()\n if (options.awaitExternalToken) {\n return\n }\n await reauthOrSignInAnonymously()\n }\n\n /** Raw token exchange; must run inside `runExclusiveAuth`. */\n async function exchangeExternalToken(\n newExternalToken: string,\n ): Promise<void> {\n isAuthenticating.value = true\n try {\n const { data } = await client.signIn.externalToken({\n token: newExternalToken,\n })\n if (activeExternalToken.value !== newExternalToken) {\n // Withdrawn (sign-out) or replaced while the request was in flight\n return\n }\n if (data?.token) {\n token.value = data.token\n await session.value.refetch()\n return\n }\n await handleExternalTokenFailure(newExternalToken)\n } catch {\n await handleExternalTokenFailure(newExternalToken)\n } finally {\n isAuthenticating.value = false\n }\n }\n\n /** External token currently queued or in flight, to drop duplicate requests. */\n let pendingExternalToken: string | undefined\n\n /**\n * Last external token an exchange was attempted with, whatever the\n * trigger. The session watcher consults it so a token that was already\n * tried is never auto-retried: a rejected token must not loop between\n * exchange and fallback. Deliberate retries (prop change, imperative\n * `signIn`, 401 handler) bypass it.\n */\n let attemptedExternalToken: string | undefined\n\n /** The cookie-restored external session was reconciled with the host state. */\n let externalSessionReconciled = false\n\n async function signInWithExternalToken(\n newExternalToken: string,\n ): Promise<void> {\n if (!newExternalToken || pendingExternalToken === newExternalToken) {\n return\n }\n activeExternalToken.value = newExternalToken\n attemptedExternalToken = newExternalToken\n pendingExternalToken = newExternalToken\n try {\n await runExclusiveAuth(async () => {\n if (activeExternalToken.value !== newExternalToken) {\n // Superseded while waiting for the previous operation\n return\n }\n await exchangeExternalToken(newExternalToken)\n })\n } finally {\n pendingExternalToken = undefined\n }\n }\n\n async function signInAnonymouslyWithFallback(): Promise<void> {\n if (isAuthenticating.value) {\n // The `[isActive, session]` watcher retries on the next session change\n return\n }\n await runExclusiveAuth(async () => {\n if (activeExternalToken.value || useHostSession.value) {\n // A host-owned sign-in was requested in the meantime\n return\n }\n isAuthenticating.value = true\n try {\n await reauthOrSignInAnonymously()\n } finally {\n isAuthenticating.value = false\n }\n })\n }\n\n watch(\n [isActive, session],\n ([newIsActive, newSession]) => {\n if (!newIsActive) {\n return\n }\n if (\n !newSession.isPending &&\n !newSession.isRefetching &&\n !isAuthenticating.value &&\n !newSession.data\n ) {\n if (useHostSession.value) {\n // Host-session mode: the session comes from the host\n // cookie, the widget never signs in on its own. No\n // session means the host is not (or no longer) logged in.\n return\n }\n if (activeExternalToken.value) {\n signInWithExternalToken(activeExternalToken.value)\n return\n }\n if (options.awaitExternalToken) {\n return\n }\n signInAnonymouslyWithFallback()\n return\n }\n\n const user = newSession.data?.user\n\n if (useHostSession.value) {\n // Host-session mode: the session is read from (and owned by)\n // the host cookie, none of the widget-session housekeeping\n // below applies.\n return\n }\n\n // A session created by an external token exchange (the only kind\n // with `hardExpiresAt`) may have been restored from the cookie\n // after a page navigation, where prop changes cannot be observed:\n // reconcile it once with the auth state the host declares now.\n if (\n newSession.data?.session?.hardExpiresAt &&\n !externalSessionReconciled\n ) {\n externalSessionReconciled = true\n if (!activeExternalToken.value) {\n // The host no longer provides a token (it logged out\n // before this page load): drop the stale session. The\n // usual fallback rules apply (keep waiting in\n // `awaitExternalToken` mode, anonymous otherwise).\n signOut()\n return\n }\n if (attemptedExternalToken !== activeExternalToken.value) {\n // Re-exchange the current token, so a token minted for a\n // different user replaces the restored session and an\n // expired one raises `external-auth-expired` right away.\n signInWithExternalToken(activeExternalToken.value)\n return\n }\n }\n\n // Upgrade an anonymous session restored from the cookie when an\n // external token is available (e.g. the user logged into the host\n // app between page loads). Attempted once per token.\n if (\n user?.isAnonymous &&\n activeExternalToken.value &&\n attemptedExternalToken !== activeExternalToken.value\n ) {\n signInWithExternalToken(activeExternalToken.value)\n return\n }\n\n // Backfill userId cookie for existing anonymous sessions\n if (\n user?.isAnonymous &&\n user.id &&\n !getAnonymousUserIdCookie(options.cookieName)\n ) {\n setAnonymousUserIdCookie(user.id as string, options.cookieName)\n }\n },\n { immediate: true },\n )\n\n // Host-session mode toggle: on entry re-read the session from the host\n // cookie (the widget Bearer is already suppressed by the mode); on exit\n // re-read without the cookie, so the `[isActive, session]` watcher\n // re-establishes the widget's own flow (anonymous or external token).\n watch(useHostSession, () => {\n session.value.refetch()\n })\n\n watch(externalToken, (newToken, previousToken) => {\n if (newToken) {\n // Track the intent even when inactive: the `[isActive, session]`\n // watcher signs in with it once the widget becomes active.\n activeExternalToken.value = newToken\n if (isActive.value) {\n signInWithExternalToken(newToken)\n }\n return\n }\n // The host withdrew the token (logged out): sign out without\n // re-authenticating; the fallback rules apply (keep waiting in\n // `awaitExternalToken` mode, anonymous otherwise).\n if (previousToken && activeExternalToken.value === previousToken) {\n signOut()\n }\n })\n\n function onError(error: { type: string; title: string; status: number }) {\n if (error.status !== 401) {\n return\n }\n if (useHostSession.value) {\n // The widget cannot recover a host session on its own (a 401\n // here means the host session expired): re-read it so the UI\n // reflects the signed-out state.\n session.value.refetch()\n return\n }\n if (activeExternalToken.value) {\n signInWithExternalToken(activeExternalToken.value)\n return\n }\n if (options.awaitExternalToken) {\n return\n }\n signInAnonymouslyWithFallback()\n }\n\n const userId = computed(() => session.value.data?.user?.id)\n\n // Auth methods for widget login screen\n async function signInWithEmail(\n email: string,\n password: string,\n rememberMe?: boolean,\n ) {\n const result = await client.signIn.email({\n email,\n password,\n rememberMe,\n })\n // Capture the session token from the response body. Relying solely on\n // the `set-auth-token` header is unreliable here, so we mirror the\n // anonymous/social flows and store the token explicitly.\n if (result.data?.token) {\n token.value = result.data.token\n // Signing in from an anonymous session links (and deletes) the\n // anonymous user server-side, so its stored userId can no longer\n // be re-authenticated — drop it so a later sign-out falls straight\n // back to a fresh anonymous session.\n clearAnonymousUserIdCookie(options.cookieName)\n }\n return result\n }\n\n function signUpWithEmail(params: {\n email: string\n password: string\n name: string\n }) {\n return client.signUp.email(params)\n }\n\n function requestPasswordReset(email: string, redirectTo: string) {\n return client.requestPasswordReset({ email, redirectTo })\n }\n\n /**\n * Opens a popup for social OAuth sign-in.\n *\n * The popup performs a top-level navigation to the backend `social-redirect`\n * endpoint, so the OAuth `state` cookie is set first-party on the backend\n * origin and survives the round-trip (a cross-origin XHR `signIn.social`\n * would set it as a third-party cookie that browsers drop, causing\n * `state_mismatch`). After the flow completes, the backend callback page\n * posts back the session Bearer token, which we store before refetching.\n */\n async function signInWithSocialPopup(\n provider: 'google' | 'microsoft',\n onAuthSuccess?: () => void,\n ): Promise<{ popupBlocked: boolean }> {\n const baseUrl = options.baseUrl ?? BACKEND_URL\n const basePath = options.basePath ?? AUTH_BASE_PATH\n const callbackURL = `${baseUrl}/${basePath}/widget-oauth-callback`\n const redirectUrl = client.signIn.socialRedirect({\n provider,\n callbackURL,\n })\n\n const popup = window.open(\n redirectUrl,\n 'eightwave-oauth',\n 'width=600,height=700,menubar=no,toolbar=no',\n )\n\n if (!popup) {\n return { popupBlocked: true }\n }\n\n return new Promise((resolve) => {\n const allowedOrigin = new URL(baseUrl).origin\n // Resolve too if the user closes the popup without completing OAuth.\n const closedPollId = setInterval(() => {\n if (popup?.closed) {\n finish()\n }\n }, 500)\n\n function finish() {\n window.removeEventListener('message', onOAuthMessage)\n clearInterval(closedPollId)\n resolve({ popupBlocked: false })\n }\n\n function onOAuthMessage(event: MessageEvent) {\n const isTrustedOAuthMessage =\n event.origin === allowedOrigin &&\n event.data?.type === 'eightwave-oauth'\n if (!isTrustedOAuthMessage) {\n return\n }\n popup?.close()\n if (event.data.token) {\n token.value = event.data.token\n // Same as the email flow: the anonymous user was linked\n // and deleted server-side, its stored userId is stale.\n clearAnonymousUserIdCookie(options.cookieName)\n }\n session.value.refetch().then(() => onAuthSuccess?.())\n finish()\n }\n\n window.addEventListener('message', onOAuthMessage)\n })\n }\n\n /**\n * Signs the current user out and clears the locally stored session token.\n * Withdraws the active external token first, so the watchers never\n * re-authenticate with it: in external mode the host signs the widget back\n * in (prop change or `signInWithExternalToken`), while in the other modes\n * the `[isActive, session]` watcher re-establishes an anonymous session\n * (re-using the stored anonymous userId cookie when present).\n */\n async function signOut(): Promise<void> {\n if (useHostSession.value) {\n // The session belongs to the host application (cookie): revoking\n // it from the widget would log the user out of the host too.\n // Leaving host-session mode is the host's move (prop change).\n return\n }\n activeExternalToken.value = undefined\n const revokedToken = token.value\n try {\n await client.signOut()\n } finally {\n // A concurrent exchange may have stored a fresh session token\n // while the revoke request was in flight (e.g. a host `signIn`\n // racing the mount-time reconcile): only clear our own token,\n // never the newer one.\n if (token.value === revokedToken) {\n token.value = ''\n }\n await session.value.refetch()\n }\n }\n\n return {\n client,\n session,\n isAuthenticating,\n isExternalAuth,\n token,\n headers,\n onError,\n userId,\n signInWithEmail,\n signUpWithEmail,\n requestPasswordReset,\n signInSocial: client.signIn.social,\n signInWithSocialPopup,\n signInWithExternalToken,\n signOut,\n }\n}\n","import { getCurrentInstance, onBeforeUnmount, onMounted } from 'vue'\n\n/**\n * Prevents external focus-trapping libraries (e.g. Bootstrap 5 modals) from\n * stealing focus away from elements inside this custom element's shadow root.\n *\n * Bootstrap modals attach a `focusin` listener on `document` that pulls focus\n * back whenever it leaves the modal subtree. Because our custom element lives\n * outside the modal in the DOM, every focus event that escapes the shadow root\n * is treated as a \"focus escape\" and gets redirected back into the modal.\n *\n * Fix: intercept `focusin` at the shadow root boundary in the **bubble phase**\n * and stop its propagation. The event still reaches every internal element\n * normally (capture → target → bubble up through the shadow tree), but is\n * stopped at the shadow root before it can re-target and continue to `document`.\n *\n * Must be called inside a `.ce.vue` root component's `<script setup>`.\n */\nexport function useFocusTrapBypass() {\n let shadowRoot: ShadowRoot | null = null\n\n const stopFocusPropagation = (event: Event) => {\n event.stopPropagation()\n }\n\n onMounted(() => {\n const instance = getCurrentInstance()\n const rootNode = instance?.proxy?.$el?.getRootNode()\n\n if (!(rootNode instanceof ShadowRoot)) {\n return\n }\n\n shadowRoot = rootNode\n shadowRoot.addEventListener('focusin', stopFocusPropagation)\n })\n\n onBeforeUnmount(() => {\n if (!shadowRoot) {\n return\n }\n\n shadowRoot.removeEventListener('focusin', stopFocusPropagation)\n shadowRoot = null\n })\n}\n"],"mappings":"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"}
|