@strands.gg/accui 2.17.55 → 2.17.56
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/{StrandsUIPlugin-fvlbvZ-P.cjs.js → StrandsUIPlugin-CiAAY2sN.cjs.js} +3 -3
- package/dist/StrandsUIPlugin-Dar_Az2V.es.js +20430 -0
- package/dist/StrandsUIPlugin-fHS0GmiX.cjs.js +143 -0
- package/dist/{StrandsUIPlugin-CSsUxobq.es.js → StrandsUIPlugin-hYC1FxeG.es.js} +950 -931
- package/dist/accui.css +1 -1
- package/dist/index.cjs.js +5 -5
- package/dist/index.es.js +6574 -5585
- package/dist/nuxt/module.cjs.js +2 -2
- package/dist/nuxt/module.es.js +21 -9
- package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js +1 -1
- package/dist/nuxt/runtime/composables/useStrandsAuth.d.ts +45 -0
- package/dist/nuxt/runtime/composables/useStrandsAuth.es.js +79 -19
- package/dist/nuxt/runtime/composables/useStrandsOAuth.d.ts +8 -0
- package/dist/nuxt/runtime/plugin.client.cjs.js +1 -1
- package/dist/nuxt/runtime/plugin.client.es.js +2 -2
- package/dist/nuxt/runtime/plugin.server.cjs.js +1 -1
- package/dist/nuxt/runtime/plugin.server.es.js +1 -1
- package/dist/nuxt/runtime/plugins/auth-interceptor.client.cjs.js +1 -1
- package/dist/nuxt/runtime/plugins/auth-interceptor.client.d.ts +7 -0
- package/dist/nuxt/runtime/plugins/auth-interceptor.client.es.js +21 -30
- package/dist/types/index.d.ts +8 -0
- package/dist/types/oauth.d.ts +420 -0
- package/dist/useDarkMode-BdG1G2mj.es.js +102 -0
- package/dist/{useDarkMode-Cew-NWjS.cjs.js → useDarkMode-BvBnG5kF.cjs.js} +1 -1
- package/dist/useDarkMode-Dd0Q5Nnz.cjs.js +1 -0
- package/dist/{useDarkMode-CvinMR9O.es.js → useDarkMode-FTkHLBsX.es.js} +15 -15
- package/dist/useDarkMode-OZ_A8-6H.es.js +102 -0
- package/dist/useStrandsAuth-CetpoBNk.cjs.js +1 -0
- package/dist/useStrandsAuth-DEWj1bFC.cjs.js +1 -0
- package/dist/useStrandsAuth-DTJ5fn33.es.js +666 -0
- package/dist/useStrandsAuth-Z7P9ujqQ.es.js +666 -0
- package/dist/useStrandsAuth-wzl2F9DE.es.js +702 -0
- package/dist/{useStrandsConfig-CKLyNlSd.es.js → useStrandsConfig-B2UXxIbT.es.js} +46 -40
- package/dist/useStrandsConfig-BGJg1LlC.es.js +213 -0
- package/dist/useStrandsConfig-BYOAEt9d.cjs.js +1 -0
- package/dist/vite.cjs.js +1 -1
- package/dist/vite.es.js +2 -2
- package/dist/vue/components/index.d.ts +2 -0
- package/dist/vue/composables/useAuthenticatedFetch.d.ts +3 -1
- package/dist/vue/composables/useDarkMode.d.ts +6 -1
- package/dist/vue/composables/useStrandsOAuth.d.ts +10 -0
- package/dist/vue/ui/UiMegaMenu/index.d.ts +4 -0
- package/dist/vue/ui/index.d.ts +8 -0
- package/package.json +1 -1
- package/dist/index.d.ts +0 -7
- package/dist/nuxt/module.d.ts +0 -4
- package/dist/nuxt/runtime/composables/useAuthenticatedFetch.d.ts +0 -20
- package/dist/nuxt/runtime/middleware/auth.d.ts +0 -6
- package/dist/nuxt/runtime/middleware/auth.global.d.ts +0 -2
- package/dist/nuxt/runtime/middleware/guest.d.ts +0 -6
- package/dist/nuxt/runtime/plugin.client.d.ts +0 -2
- package/dist/nuxt/runtime/plugin.server.d.ts +0 -2
- package/dist/nuxt.d.ts +0 -4
- package/dist/useStrandsAuth-CmAxFCQ_.cjs.js +0 -1
- package/dist/useStrandsAuth-DWsqp9WI.es.js +0 -661
- package/dist/useStrandsConfig-nOR0oGdb.cjs.js +0 -1
- package/dist/utils/colors.d.ts +0 -10
- package/dist/utils/index.d.ts +0 -2
- package/dist/utils/slots.d.ts +0 -1
- package/dist/utils/validation.d.ts +0 -12
- package/dist/vite/index.d.ts +0 -2
- package/dist/vite/plugin.d.ts +0 -68
- package/dist/vue/components/StrandsNav/index.d.ts +0 -8
- package/dist/vue/components/StrandsNav/types.d.ts +0 -12
- package/dist/vue/components/icons/index.d.ts +0 -2
- package/dist/vue/composables/useFloatingPosition.d.ts +0 -22
- package/dist/vue/composables/useModalStack.d.ts +0 -86
- package/dist/vue/composables/useOAuthProviders.d.ts +0 -73
- package/dist/vue/composables/useStrandsAuth.d.ts +0 -2
- package/dist/vue/composables/useStrandsConfig.d.ts +0 -5
- package/dist/vue/composables/useStrandsMfa.d.ts +0 -36
- package/dist/vue/composables/useTheme.d.ts +0 -18
- package/dist/vue/index.d.ts +0 -16
- package/dist/vue/plugins/StrandsUIPlugin.d.ts +0 -20
- package/dist/vue/utils/contrast.d.ts +0 -75
- package/dist/vue/utils/debounce.d.ts +0 -12
- package/dist/vue/utils/fontPreloader.d.ts +0 -11
- package/dist/vue/utils/iconProps.d.ts +0 -9
- package/dist/vue/utils/lazyComponents.d.ts +0 -4
- package/dist/vue/utils/levels.d.ts +0 -27
- package/dist/vue/utils/modalStack.d.ts +0 -34
- package/dist/vue/utils/performanceInit.d.ts +0 -40
- package/dist/vue/utils/requestCache.d.ts +0 -49
- package/dist/vue/utils/slots.d.ts +0 -9
- package/dist/vue/utils/sounds.d.ts +0 -57
- package/dist/webcomponents/define-element.d.ts +0 -25
- package/dist/webcomponents/entries/strands-alert.d.ts +0 -12
- package/dist/webcomponents/entries/strands-button.d.ts +0 -12
- package/dist/webcomponents/entries/strands-card.d.ts +0 -12
- package/dist/webcomponents/entries/strands-checkbox-group.d.ts +0 -11
- package/dist/webcomponents/entries/strands-divider.d.ts +0 -12
- package/dist/webcomponents/entries/strands-input.d.ts +0 -14
- package/dist/webcomponents/entries/strands-link.d.ts +0 -12
- package/dist/webcomponents/entries/strands-loader-spinner.d.ts +0 -10
- package/dist/webcomponents/entries/strands-modal.d.ts +0 -16
- package/dist/webcomponents/entries/strands-pill.d.ts +0 -11
- package/dist/webcomponents/entries/strands-radio-group.d.ts +0 -11
- package/dist/webcomponents/entries/strands-slider.d.ts +0 -10
- package/dist/webcomponents/entries/strands-table.d.ts +0 -13
- package/dist/webcomponents/entries/strands-tabs.d.ts +0 -12
- package/dist/webcomponents/entries/strands-theme-toggle.d.ts +0 -10
- package/dist/webcomponents/entries/strands-toggle.d.ts +0 -10
- package/dist/webcomponents/entries/strands-tooltip.d.ts +0 -12
- package/dist/webcomponents/index.d.ts +0 -8
- package/dist/webcomponents/loader.d.ts +0 -73
- package/dist/webcomponents/registry.d.ts +0 -41
|
@@ -0,0 +1,666 @@
|
|
|
1
|
+
import { computed as u, ref as y, getCurrentInstance as ve, onUnmounted as we } from "vue";
|
|
2
|
+
import { u as ye } from "./useStrandsConfig-BGJg1LlC.es.js";
|
|
3
|
+
class me {
|
|
4
|
+
cache = /* @__PURE__ */ new Map();
|
|
5
|
+
DEFAULT_TTL = 300 * 1e3;
|
|
6
|
+
// 5 minutes
|
|
7
|
+
/**
|
|
8
|
+
* Memoized fetch - prevents duplicate requests and caches results
|
|
9
|
+
*/
|
|
10
|
+
async fetch(d, v, h = this.DEFAULT_TTL) {
|
|
11
|
+
const x = Date.now(), s = this.cache.get(d);
|
|
12
|
+
if (s && x - s.timestamp < s.ttl)
|
|
13
|
+
return s.promise;
|
|
14
|
+
this.cleanExpired();
|
|
15
|
+
const c = v().finally(() => {
|
|
16
|
+
setTimeout(() => {
|
|
17
|
+
this.cache.delete(d);
|
|
18
|
+
}, h);
|
|
19
|
+
});
|
|
20
|
+
return this.cache.set(d, {
|
|
21
|
+
promise: c,
|
|
22
|
+
timestamp: x,
|
|
23
|
+
ttl: h
|
|
24
|
+
}), c;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Clear all cached entries
|
|
28
|
+
*/
|
|
29
|
+
clear() {
|
|
30
|
+
this.cache.clear();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Remove a specific cache entry
|
|
34
|
+
*/
|
|
35
|
+
invalidate(d) {
|
|
36
|
+
this.cache.delete(d);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Clean expired cache entries
|
|
40
|
+
*/
|
|
41
|
+
cleanExpired() {
|
|
42
|
+
const d = Date.now();
|
|
43
|
+
for (const [v, h] of this.cache.entries())
|
|
44
|
+
d - h.timestamp > h.ttl && this.cache.delete(v);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get cache statistics (for debugging)
|
|
48
|
+
*/
|
|
49
|
+
getStats() {
|
|
50
|
+
return {
|
|
51
|
+
size: this.cache.size,
|
|
52
|
+
entries: Array.from(this.cache.keys())
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const w = new me();
|
|
57
|
+
function pe() {
|
|
58
|
+
return {
|
|
59
|
+
fetch: w.fetch.bind(w),
|
|
60
|
+
clear: w.clear.bind(w),
|
|
61
|
+
invalidate: w.invalidate.bind(w),
|
|
62
|
+
getStats: w.getStats.bind(w)
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
function Te(a, d) {
|
|
66
|
+
let v = null;
|
|
67
|
+
return (...h) => {
|
|
68
|
+
v && clearTimeout(v), v = setTimeout(() => {
|
|
69
|
+
a(...h);
|
|
70
|
+
}, d);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
const N = Te((a, d) => {
|
|
74
|
+
typeof window < "u" && localStorage.setItem(a, d);
|
|
75
|
+
}, 300), k = (a) => ({
|
|
76
|
+
id: a.id,
|
|
77
|
+
email: a.email,
|
|
78
|
+
firstName: a.first_name || a.firstName || "",
|
|
79
|
+
lastName: a.last_name || a.lastName || "",
|
|
80
|
+
avatar: a.avatar_url || a.avatar,
|
|
81
|
+
mfaEnabled: a.mfa_enabled ?? a.mfaEnabled ?? !1,
|
|
82
|
+
emailVerified: a.email_verified ?? a.emailVerified ?? !1,
|
|
83
|
+
passwordUpdatedAt: a.password_updated_at || a.passwordUpdatedAt,
|
|
84
|
+
settings: a.settings || {},
|
|
85
|
+
xp: a.xp || 0,
|
|
86
|
+
level: a.level || 1,
|
|
87
|
+
next_level_xp: a.next_level_xp || a.next_level_xp || 4,
|
|
88
|
+
username: a.username,
|
|
89
|
+
usernameLastChangedAt: a.username_last_changed_at || a.usernameLastChangedAt,
|
|
90
|
+
createdAt: a.created_at || a.createdAt,
|
|
91
|
+
updatedAt: a.updated_at || a.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
|
|
92
|
+
}), Se = {
|
|
93
|
+
currentUser: y(null),
|
|
94
|
+
currentSession: y(null),
|
|
95
|
+
loadingStates: y({
|
|
96
|
+
initializing: !0,
|
|
97
|
+
signingIn: !1,
|
|
98
|
+
signingUp: !1,
|
|
99
|
+
signingOut: !1,
|
|
100
|
+
refreshingToken: !1,
|
|
101
|
+
sendingMfaEmail: !1,
|
|
102
|
+
verifyingMfa: !1,
|
|
103
|
+
loadingProfile: !1
|
|
104
|
+
}),
|
|
105
|
+
isInitialized: y(!1),
|
|
106
|
+
mfaRequired: y(!1),
|
|
107
|
+
mfaSessionId: y(null),
|
|
108
|
+
availableMfaMethods: y([])
|
|
109
|
+
};
|
|
110
|
+
let A = null, m = null, O = null;
|
|
111
|
+
function Ae() {
|
|
112
|
+
const { getUrl: a, config: d } = ye(), { fetch: v, clear: h, invalidate: x } = pe(), { currentUser: s, currentSession: c, loadingStates: i, isInitialized: p, mfaRequired: T, mfaSessionId: f, availableMfaMethods: S } = Se, C = () => {
|
|
113
|
+
if (s.value = null, c.value = null, T.value = !1, f.value = null, S.value = [], E(), m = null, h(), typeof window < "u" && d.value?.onSignOutUrl) {
|
|
114
|
+
const t = window.location.pathname + window.location.search, e = d.value.onSignOutUrl;
|
|
115
|
+
t !== e && (window.location.href = e);
|
|
116
|
+
}
|
|
117
|
+
}, F = u(() => i.value.initializing), z = u(() => i.value.signingIn), J = u(() => i.value.signingUp), R = u(() => i.value.signingOut), L = u(() => i.value.refreshingToken), U = u(() => i.value.sendingMfaEmail), V = u(() => i.value.verifyingMfa);
|
|
118
|
+
u(() => i.value.loadingProfile);
|
|
119
|
+
const j = u(
|
|
120
|
+
() => i.value.signingIn || i.value.signingUp || i.value.signingOut || i.value.refreshingToken || i.value.sendingMfaEmail || i.value.verifyingMfa || i.value.loadingProfile
|
|
121
|
+
), q = u(() => i.value.initializing || j.value), G = u(() => {
|
|
122
|
+
const t = i.value;
|
|
123
|
+
return t.initializing ? "Checking authentication..." : t.signingIn ? "Signing you in..." : t.signingUp ? "Creating your account..." : t.signingOut ? "Signing you out..." : t.refreshingToken ? "Refreshing session..." : t.sendingMfaEmail ? "Sending verification code..." : t.verifyingMfa ? "Verifying code..." : t.loadingProfile ? "Loading profile..." : "Loading...";
|
|
124
|
+
}), K = () => ({
|
|
125
|
+
"Content-Type": "application/json"
|
|
126
|
+
}), H = async () => {
|
|
127
|
+
try {
|
|
128
|
+
const t = await fetch(a("authStatus"), {
|
|
129
|
+
method: "GET",
|
|
130
|
+
credentials: "include"
|
|
131
|
+
});
|
|
132
|
+
if (!t.ok)
|
|
133
|
+
return !1;
|
|
134
|
+
const e = await t.json();
|
|
135
|
+
return e.authenticated && e.user ? (s.value = k(e.user), c.value = {
|
|
136
|
+
accessToken: "",
|
|
137
|
+
// Token is in HttpOnly cookie
|
|
138
|
+
refreshToken: "",
|
|
139
|
+
// Token is in HttpOnly cookie
|
|
140
|
+
expiresAt: new Date(Date.now() + 300 * 1e3),
|
|
141
|
+
// Assume 5 min expiry
|
|
142
|
+
userId: e.user.id
|
|
143
|
+
}, !0) : !1;
|
|
144
|
+
} catch {
|
|
145
|
+
return !1;
|
|
146
|
+
}
|
|
147
|
+
}, W = async (t, e, n) => {
|
|
148
|
+
const r = await fetch(a("mfaHardwareCompleteRegistration"), {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: {
|
|
151
|
+
"Content-Type": "application/json"
|
|
152
|
+
},
|
|
153
|
+
credentials: "include",
|
|
154
|
+
body: JSON.stringify({
|
|
155
|
+
device_id: t,
|
|
156
|
+
credential: e
|
|
157
|
+
})
|
|
158
|
+
});
|
|
159
|
+
if (!r.ok) {
|
|
160
|
+
const l = await r.text();
|
|
161
|
+
let o = "Failed to complete hardware key registration";
|
|
162
|
+
try {
|
|
163
|
+
const g = JSON.parse(l);
|
|
164
|
+
o = g.message || g.error || l;
|
|
165
|
+
} catch {
|
|
166
|
+
o = l || "Failed to complete hardware key registration";
|
|
167
|
+
}
|
|
168
|
+
throw new Error(o);
|
|
169
|
+
}
|
|
170
|
+
return r.json();
|
|
171
|
+
}, B = async (t, e, n = "hardware") => {
|
|
172
|
+
const r = await fetch(a("mfaHardwareStartRegistration"), {
|
|
173
|
+
method: "POST",
|
|
174
|
+
headers: {
|
|
175
|
+
"Content-Type": "application/json"
|
|
176
|
+
},
|
|
177
|
+
credentials: "include",
|
|
178
|
+
body: JSON.stringify({
|
|
179
|
+
device_name: t,
|
|
180
|
+
device_type: n
|
|
181
|
+
})
|
|
182
|
+
});
|
|
183
|
+
if (!r.ok) {
|
|
184
|
+
const l = await r.text();
|
|
185
|
+
let o = "Failed to start hardware key registration";
|
|
186
|
+
try {
|
|
187
|
+
const g = JSON.parse(l);
|
|
188
|
+
o = g.message || g.error || l;
|
|
189
|
+
} catch {
|
|
190
|
+
o = l || "Failed to start hardware key registration";
|
|
191
|
+
}
|
|
192
|
+
throw new Error(o);
|
|
193
|
+
}
|
|
194
|
+
return r.json();
|
|
195
|
+
}, Y = u(() => s.value !== null), Q = async (t) => {
|
|
196
|
+
i.value.signingIn = !0;
|
|
197
|
+
try {
|
|
198
|
+
T.value = !1, f.value = null, S.value = [];
|
|
199
|
+
const e = {
|
|
200
|
+
"Content-Type": "application/json"
|
|
201
|
+
};
|
|
202
|
+
typeof window < "u" && window.location && (e.Origin = window.location.origin);
|
|
203
|
+
const n = await fetch(a("signIn"), {
|
|
204
|
+
method: "POST",
|
|
205
|
+
headers: e,
|
|
206
|
+
credentials: "include",
|
|
207
|
+
// Include cookies for auth
|
|
208
|
+
body: JSON.stringify(t)
|
|
209
|
+
});
|
|
210
|
+
if (!n.ok)
|
|
211
|
+
throw n.status === 401 ? new Error("Invalid email or password") : n.status === 403 ? new Error("Please verify your email address before signing in") : new Error(`Sign in failed: ${n.status} ${n.statusText}`);
|
|
212
|
+
const r = await n.json();
|
|
213
|
+
if (r.mfa_required) {
|
|
214
|
+
T.value = !0, f.value = r.mfa_session_id || null;
|
|
215
|
+
const l = (r.available_mfa_methods || []).map((o) => {
|
|
216
|
+
let g = `${o.device_type.charAt(0).toUpperCase() + o.device_type.slice(1)} Authentication`;
|
|
217
|
+
return o.device_type === "hardware" ? g = o.device_name || "Security Key" : o.device_type === "totp" ? g = o.device_name || "Authenticator App" : o.device_type === "email" && (g = o.device_name || "Email Verification"), {
|
|
218
|
+
id: o.device_id,
|
|
219
|
+
device_type: o.device_type,
|
|
220
|
+
device_name: o.device_name || g,
|
|
221
|
+
is_active: !0,
|
|
222
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
223
|
+
last_used_at: o.last_used_at,
|
|
224
|
+
// Pass through additional metadata if available
|
|
225
|
+
credential_id: o.credential_id,
|
|
226
|
+
device_info: o.device_info
|
|
227
|
+
};
|
|
228
|
+
});
|
|
229
|
+
return S.value = l, i.value.signingIn = !1, r;
|
|
230
|
+
}
|
|
231
|
+
return await P(r), r;
|
|
232
|
+
} catch (e) {
|
|
233
|
+
throw e;
|
|
234
|
+
} finally {
|
|
235
|
+
i.value.signingIn = !1;
|
|
236
|
+
}
|
|
237
|
+
}, X = async (t) => {
|
|
238
|
+
i.value.signingUp = !0;
|
|
239
|
+
try {
|
|
240
|
+
throw new Error("Sign up not implemented - please integrate with auth SDK");
|
|
241
|
+
} finally {
|
|
242
|
+
i.value.signingUp = !1;
|
|
243
|
+
}
|
|
244
|
+
}, Z = async () => {
|
|
245
|
+
i.value.signingOut = !0;
|
|
246
|
+
try {
|
|
247
|
+
await fetch(a("signOut"), {
|
|
248
|
+
method: "POST",
|
|
249
|
+
credentials: "include"
|
|
250
|
+
// Cookies will be cleared by server response
|
|
251
|
+
}), E(), m = null, h(), s.value = null, c.value = null, T.value = !1, f.value = null, S.value = [], typeof window < "u" && d.value?.onSignOutUrl && (window.location.href = d.value.onSignOutUrl);
|
|
252
|
+
} finally {
|
|
253
|
+
i.value.signingOut = !1;
|
|
254
|
+
}
|
|
255
|
+
}, b = async () => {
|
|
256
|
+
if (m)
|
|
257
|
+
return await m;
|
|
258
|
+
m = (async () => {
|
|
259
|
+
i.value.refreshingToken = !0;
|
|
260
|
+
try {
|
|
261
|
+
const e = await fetch(a("refresh"), {
|
|
262
|
+
method: "POST",
|
|
263
|
+
headers: {
|
|
264
|
+
"Content-Type": "application/json"
|
|
265
|
+
},
|
|
266
|
+
credentials: "include"
|
|
267
|
+
// Refresh token sent via cookie
|
|
268
|
+
});
|
|
269
|
+
if (!e.ok) {
|
|
270
|
+
if (e.status === 401)
|
|
271
|
+
return C(), !1;
|
|
272
|
+
throw new Error(`Token refresh failed: ${e.status} ${e.statusText}`);
|
|
273
|
+
}
|
|
274
|
+
const n = await e.json();
|
|
275
|
+
n.user && (s.value = k(n.user));
|
|
276
|
+
const r = {
|
|
277
|
+
accessToken: "",
|
|
278
|
+
// Token is in HttpOnly cookie
|
|
279
|
+
refreshToken: "",
|
|
280
|
+
// Token is in HttpOnly cookie
|
|
281
|
+
expiresAt: new Date(Date.now() + 300 * 1e3),
|
|
282
|
+
// 5 minutes from now
|
|
283
|
+
userId: n.user?.id || s.value?.id
|
|
284
|
+
};
|
|
285
|
+
return c.value = r, _(), x(`sessions:${s.value?.id || "unknown"}`), !0;
|
|
286
|
+
} catch {
|
|
287
|
+
return C(), !1;
|
|
288
|
+
} finally {
|
|
289
|
+
i.value.refreshingToken = !1;
|
|
290
|
+
}
|
|
291
|
+
})();
|
|
292
|
+
const t = await m;
|
|
293
|
+
return m = null, t;
|
|
294
|
+
}, ee = async () => {
|
|
295
|
+
const t = `profile:${c.value.accessToken.slice(0, 20)}`;
|
|
296
|
+
i.value.loadingProfile = !0;
|
|
297
|
+
try {
|
|
298
|
+
return await v(t, async () => {
|
|
299
|
+
const e = await fetch(a("profile"), {
|
|
300
|
+
method: "GET",
|
|
301
|
+
headers: {
|
|
302
|
+
"Content-Type": "application/json",
|
|
303
|
+
Authorization: `Bearer ${c.value?.accessToken}`
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
if (!e.ok)
|
|
307
|
+
throw e.status === 401 ? new Error("Authentication expired. Please sign in again.") : new Error(`Failed to fetch profile: ${e.status} ${e.statusText}`);
|
|
308
|
+
const n = await e.json();
|
|
309
|
+
return s.value = k(n), s.value && typeof window < "u" && localStorage.setItem("strands_auth_user", JSON.stringify(s.value)), s.value;
|
|
310
|
+
});
|
|
311
|
+
} finally {
|
|
312
|
+
i.value.loadingProfile = !1;
|
|
313
|
+
}
|
|
314
|
+
}, te = async (t) => {
|
|
315
|
+
i.value.loadingProfile = !0;
|
|
316
|
+
try {
|
|
317
|
+
const e = await fetch(a("profile"), {
|
|
318
|
+
method: "POST",
|
|
319
|
+
headers: {
|
|
320
|
+
"Content-Type": "application/json",
|
|
321
|
+
Authorization: `Bearer ${c.value.accessToken}`
|
|
322
|
+
},
|
|
323
|
+
body: JSON.stringify({
|
|
324
|
+
first_name: t.firstName,
|
|
325
|
+
last_name: t.lastName
|
|
326
|
+
})
|
|
327
|
+
});
|
|
328
|
+
if (!e.ok)
|
|
329
|
+
throw e.status === 401 ? new Error("Authentication expired. Please sign in again.") : new Error(`Profile update failed: ${e.status} ${e.statusText}`);
|
|
330
|
+
const n = await e.json();
|
|
331
|
+
return s.value = k(n), s.value && N("strands_auth_user", JSON.stringify(s.value)), s.value;
|
|
332
|
+
} finally {
|
|
333
|
+
i.value.loadingProfile = !1;
|
|
334
|
+
}
|
|
335
|
+
}, ae = async (t) => {
|
|
336
|
+
i.value.loadingProfile = !0;
|
|
337
|
+
try {
|
|
338
|
+
const e = await fetch(a("settings"), {
|
|
339
|
+
method: "POST",
|
|
340
|
+
headers: {
|
|
341
|
+
"Content-Type": "application/json",
|
|
342
|
+
Authorization: `Bearer ${c.value.accessToken}`
|
|
343
|
+
},
|
|
344
|
+
body: JSON.stringify({
|
|
345
|
+
settings: t
|
|
346
|
+
})
|
|
347
|
+
});
|
|
348
|
+
if (!e.ok)
|
|
349
|
+
throw e.status === 401 ? new Error("Authentication expired. Please sign in again.") : new Error(`Settings update failed: ${e.status} ${e.statusText}`);
|
|
350
|
+
const n = await e.json();
|
|
351
|
+
return s.value = k(n), s.value && N("strands_auth_user", JSON.stringify(s.value)), s.value;
|
|
352
|
+
} finally {
|
|
353
|
+
i.value.loadingProfile = !1;
|
|
354
|
+
}
|
|
355
|
+
}, ne = async (t, e) => {
|
|
356
|
+
i.value.loadingProfile = !0;
|
|
357
|
+
try {
|
|
358
|
+
const n = await fetch(a("changeEmail"), {
|
|
359
|
+
method: "POST",
|
|
360
|
+
headers: {
|
|
361
|
+
"Content-Type": "application/json",
|
|
362
|
+
Authorization: `Bearer ${c.value.accessToken}`
|
|
363
|
+
},
|
|
364
|
+
body: JSON.stringify({
|
|
365
|
+
new_email: t,
|
|
366
|
+
password: e
|
|
367
|
+
})
|
|
368
|
+
});
|
|
369
|
+
if (!n.ok) {
|
|
370
|
+
if (n.status === 401)
|
|
371
|
+
throw new Error("Authentication expired. Please sign in again.");
|
|
372
|
+
{
|
|
373
|
+
const l = await n.json().catch(() => ({}));
|
|
374
|
+
throw new Error(l.message || `Email change failed: ${n.status} ${n.statusText}`);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
const r = await n.json();
|
|
378
|
+
return s.value && (s.value = {
|
|
379
|
+
...s.value,
|
|
380
|
+
email: t,
|
|
381
|
+
emailVerified: !1,
|
|
382
|
+
// Email needs to be verified again
|
|
383
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
384
|
+
}, typeof window < "u" && localStorage.setItem("strands_auth_user", JSON.stringify(s.value))), r;
|
|
385
|
+
} finally {
|
|
386
|
+
i.value.loadingProfile = !1;
|
|
387
|
+
}
|
|
388
|
+
}, ie = async (t, e, n = !1) => {
|
|
389
|
+
if (!f.value)
|
|
390
|
+
throw new Error("No MFA session available");
|
|
391
|
+
i.value.verifyingMfa = !0;
|
|
392
|
+
try {
|
|
393
|
+
const r = a(n ? "mfaBackupCodeVerify" : "mfaSigninVerify"), l = n ? { mfa_session_id: f.value, backup_code: e } : { mfa_session_id: f.value, device_id: t, code: e }, o = await fetch(r, {
|
|
394
|
+
method: "POST",
|
|
395
|
+
headers: { "Content-Type": "application/json" },
|
|
396
|
+
credentials: "include",
|
|
397
|
+
body: JSON.stringify(l)
|
|
398
|
+
});
|
|
399
|
+
if (!o.ok) {
|
|
400
|
+
const I = await o.text();
|
|
401
|
+
let $ = "MFA verification failed";
|
|
402
|
+
try {
|
|
403
|
+
const D = JSON.parse(I);
|
|
404
|
+
$ = D.message || D.error || I;
|
|
405
|
+
} catch {
|
|
406
|
+
$ = I || "MFA verification failed";
|
|
407
|
+
}
|
|
408
|
+
throw new Error($);
|
|
409
|
+
}
|
|
410
|
+
const g = await o.json();
|
|
411
|
+
return T.value = !1, f.value = null, S.value = [], await P(g), g;
|
|
412
|
+
} finally {
|
|
413
|
+
i.value.verifyingMfa = !1;
|
|
414
|
+
}
|
|
415
|
+
}, se = async (t) => {
|
|
416
|
+
if (!f.value)
|
|
417
|
+
throw new Error("No MFA session available");
|
|
418
|
+
i.value.sendingMfaEmail = !0;
|
|
419
|
+
try {
|
|
420
|
+
const e = await fetch(a("mfaSigninSendEmail"), {
|
|
421
|
+
method: "POST",
|
|
422
|
+
headers: { "Content-Type": "application/json" },
|
|
423
|
+
credentials: "include",
|
|
424
|
+
body: JSON.stringify({
|
|
425
|
+
mfa_session_id: f.value,
|
|
426
|
+
device_id: t
|
|
427
|
+
})
|
|
428
|
+
});
|
|
429
|
+
if (!e.ok) {
|
|
430
|
+
const r = await e.text();
|
|
431
|
+
let l = "Failed to send MFA email code";
|
|
432
|
+
try {
|
|
433
|
+
const o = JSON.parse(r);
|
|
434
|
+
l = o.message || o.error || r;
|
|
435
|
+
} catch {
|
|
436
|
+
l = r || "Failed to send MFA email code";
|
|
437
|
+
}
|
|
438
|
+
throw new Error(l);
|
|
439
|
+
}
|
|
440
|
+
return await e.json();
|
|
441
|
+
} finally {
|
|
442
|
+
i.value.sendingMfaEmail = !1;
|
|
443
|
+
}
|
|
444
|
+
}, re = async (t) => {
|
|
445
|
+
if (!f.value)
|
|
446
|
+
throw new Error("No MFA session available");
|
|
447
|
+
const e = await fetch(a("mfaWebAuthnChallenge"), {
|
|
448
|
+
method: "POST",
|
|
449
|
+
headers: { "Content-Type": "application/json" },
|
|
450
|
+
credentials: "include",
|
|
451
|
+
body: JSON.stringify({
|
|
452
|
+
mfa_session_id: f.value,
|
|
453
|
+
device_id: t
|
|
454
|
+
})
|
|
455
|
+
});
|
|
456
|
+
if (!e.ok) {
|
|
457
|
+
const n = await e.text();
|
|
458
|
+
let r = "Failed to get WebAuthn challenge";
|
|
459
|
+
try {
|
|
460
|
+
const l = JSON.parse(n);
|
|
461
|
+
r = l.message || l.error || n;
|
|
462
|
+
} catch {
|
|
463
|
+
r = n || r;
|
|
464
|
+
}
|
|
465
|
+
throw new Error(r);
|
|
466
|
+
}
|
|
467
|
+
return e.json();
|
|
468
|
+
}, P = async (t) => {
|
|
469
|
+
try {
|
|
470
|
+
t.user && (s.value = k(t.user));
|
|
471
|
+
const e = {
|
|
472
|
+
accessToken: "",
|
|
473
|
+
// Token is in HttpOnly cookie
|
|
474
|
+
refreshToken: "",
|
|
475
|
+
// Token is in HttpOnly cookie
|
|
476
|
+
expiresAt: new Date(Date.now() + 300 * 1e3),
|
|
477
|
+
// 5 minutes from now (matching API token expiry)
|
|
478
|
+
userId: s.value?.id || t.user?.id
|
|
479
|
+
};
|
|
480
|
+
c.value = e, _();
|
|
481
|
+
} catch {
|
|
482
|
+
}
|
|
483
|
+
}, _ = () => {
|
|
484
|
+
if (A && clearTimeout(A), !c.value || typeof document < "u" && document.visibilityState === "hidden")
|
|
485
|
+
return;
|
|
486
|
+
const t = /* @__PURE__ */ new Date(), n = c.value.expiresAt.getTime() - t.getTime() - 60 * 1e3;
|
|
487
|
+
if (n <= 0) {
|
|
488
|
+
b();
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
A = setTimeout(async () => {
|
|
492
|
+
(typeof document > "u" || document.visibilityState === "visible") && await b() && _();
|
|
493
|
+
}, n);
|
|
494
|
+
}, E = () => {
|
|
495
|
+
A && (clearTimeout(A), A = null);
|
|
496
|
+
}, M = async () => {
|
|
497
|
+
if (p.value) {
|
|
498
|
+
i.value.initializing = !1;
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
return O || (O = (async () => {
|
|
502
|
+
i.value.initializing = !0;
|
|
503
|
+
try {
|
|
504
|
+
typeof window < "u" && (await Promise.race([
|
|
505
|
+
H(),
|
|
506
|
+
new Promise((n) => setTimeout(() => n(!1), 5e3))
|
|
507
|
+
]) ? _() : (s.value = null, c.value = null)), p.value = !0, await new Promise((t) => setTimeout(t, 50));
|
|
508
|
+
} catch {
|
|
509
|
+
} finally {
|
|
510
|
+
i.value.initializing = !1, O = null;
|
|
511
|
+
}
|
|
512
|
+
})(), O);
|
|
513
|
+
}, oe = async (t) => {
|
|
514
|
+
i.value.loadingProfile = !0;
|
|
515
|
+
try {
|
|
516
|
+
const e = await fetch(a("changeUsername"), {
|
|
517
|
+
method: "POST",
|
|
518
|
+
headers: {
|
|
519
|
+
"Content-Type": "application/json"
|
|
520
|
+
},
|
|
521
|
+
credentials: "include",
|
|
522
|
+
body: JSON.stringify({
|
|
523
|
+
username: t
|
|
524
|
+
})
|
|
525
|
+
});
|
|
526
|
+
if (!e.ok) {
|
|
527
|
+
const r = await e.json().catch(() => ({}));
|
|
528
|
+
throw e.status === 409 ? new Error("Username is already taken") : r.cooldown_end ? new Error(`You can only change your username once every 30 days. You can change it again on ${new Date(r.cooldown_end).toLocaleDateString()}`) : new Error(r.message || `Username change failed: ${e.status} ${e.statusText}`);
|
|
529
|
+
}
|
|
530
|
+
const n = await e.json();
|
|
531
|
+
return s.value && (s.value = {
|
|
532
|
+
...s.value,
|
|
533
|
+
username: t,
|
|
534
|
+
usernameLastChangedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
535
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
536
|
+
}), n;
|
|
537
|
+
} finally {
|
|
538
|
+
i.value.loadingProfile = !1;
|
|
539
|
+
}
|
|
540
|
+
}, le = async () => {
|
|
541
|
+
const t = await fetch(a("usernameCooldown"), {
|
|
542
|
+
method: "GET",
|
|
543
|
+
credentials: "include"
|
|
544
|
+
});
|
|
545
|
+
if (!t.ok)
|
|
546
|
+
throw new Error(`Failed to get username cooldown: ${t.status} ${t.statusText}`);
|
|
547
|
+
return t.json();
|
|
548
|
+
}, ue = async (t) => {
|
|
549
|
+
const e = a("checkUsernameAvailability").replace("{username}", encodeURIComponent(t)), n = await fetch(e, {
|
|
550
|
+
method: "GET",
|
|
551
|
+
headers: {
|
|
552
|
+
"Content-Type": "application/json"
|
|
553
|
+
}
|
|
554
|
+
});
|
|
555
|
+
if (!n.ok)
|
|
556
|
+
throw new Error(`Failed to check username availability: ${n.status} ${n.statusText}`);
|
|
557
|
+
return n.json();
|
|
558
|
+
}, ce = async () => {
|
|
559
|
+
const t = `sessions:${s.value?.id || "no-user"}`;
|
|
560
|
+
try {
|
|
561
|
+
return await v(t, async () => {
|
|
562
|
+
const e = await fetch(a("sessions"), {
|
|
563
|
+
method: "GET",
|
|
564
|
+
credentials: "include"
|
|
565
|
+
});
|
|
566
|
+
if (!e.ok)
|
|
567
|
+
throw await e.text(), new Error(`Failed to get user sessions: ${e.status} ${e.statusText}`);
|
|
568
|
+
return e.json();
|
|
569
|
+
}, 120 * 1e3);
|
|
570
|
+
} catch (e) {
|
|
571
|
+
throw e;
|
|
572
|
+
}
|
|
573
|
+
}, de = async () => {
|
|
574
|
+
const t = await fetch(a("sessionsStats"), {
|
|
575
|
+
method: "GET",
|
|
576
|
+
credentials: "include"
|
|
577
|
+
});
|
|
578
|
+
if (!t.ok)
|
|
579
|
+
throw new Error(`Failed to get session stats: ${t.status} ${t.statusText}`);
|
|
580
|
+
return t.json();
|
|
581
|
+
}, fe = async (t) => {
|
|
582
|
+
const e = a("sessionRevoke").replace("{session_id}", encodeURIComponent(t)), n = await fetch(e, {
|
|
583
|
+
method: "POST",
|
|
584
|
+
credentials: "include"
|
|
585
|
+
});
|
|
586
|
+
if (!n.ok)
|
|
587
|
+
throw new Error(`Failed to revoke session: ${n.status} ${n.statusText}`);
|
|
588
|
+
return n.status === 200;
|
|
589
|
+
}, ge = async () => {
|
|
590
|
+
const t = await fetch(a("sessionsRevokeAll"), {
|
|
591
|
+
method: "POST",
|
|
592
|
+
credentials: "include"
|
|
593
|
+
});
|
|
594
|
+
if (!t.ok)
|
|
595
|
+
throw new Error(`Failed to revoke all other sessions: ${t.status} ${t.statusText}`);
|
|
596
|
+
return t.status === 200;
|
|
597
|
+
};
|
|
598
|
+
typeof document < "u" && document.addEventListener("visibilitychange", () => {
|
|
599
|
+
document.visibilityState === "visible" && c.value ? _() : document.visibilityState === "hidden" && E();
|
|
600
|
+
});
|
|
601
|
+
const he = () => {
|
|
602
|
+
E(), h();
|
|
603
|
+
};
|
|
604
|
+
try {
|
|
605
|
+
ve() && we(he);
|
|
606
|
+
} catch {
|
|
607
|
+
}
|
|
608
|
+
return p.value || M(), {
|
|
609
|
+
// State
|
|
610
|
+
user: u(() => s.value),
|
|
611
|
+
currentUser: u(() => s.value),
|
|
612
|
+
currentSession: u(() => c.value),
|
|
613
|
+
isAuthenticated: Y,
|
|
614
|
+
isLoading: u(() => q.value || !p.value),
|
|
615
|
+
loading: j,
|
|
616
|
+
loadingMessage: G,
|
|
617
|
+
// Specific loading states
|
|
618
|
+
isInitializing: F,
|
|
619
|
+
isInitialized: u(() => p.value),
|
|
620
|
+
isSigningIn: z,
|
|
621
|
+
isSigningUp: J,
|
|
622
|
+
isSigningOut: R,
|
|
623
|
+
isRefreshingToken: L,
|
|
624
|
+
isSendingMfaEmail: U,
|
|
625
|
+
isVerifyingMfa: V,
|
|
626
|
+
// MFA State
|
|
627
|
+
mfaRequired: u(() => T.value),
|
|
628
|
+
mfaSessionId: u(() => f.value),
|
|
629
|
+
availableMfaMethods: u(() => S.value),
|
|
630
|
+
// Methods
|
|
631
|
+
signIn: Q,
|
|
632
|
+
signUp: X,
|
|
633
|
+
signOut: Z,
|
|
634
|
+
refreshToken: b,
|
|
635
|
+
fetchProfile: ee,
|
|
636
|
+
updateProfile: te,
|
|
637
|
+
updateUserSettings: ae,
|
|
638
|
+
changeEmail: ne,
|
|
639
|
+
changeUsername: oe,
|
|
640
|
+
getUsernameCooldown: le,
|
|
641
|
+
checkUsernameAvailability: ue,
|
|
642
|
+
// Session management
|
|
643
|
+
getUserSessions: ce,
|
|
644
|
+
getSessionStats: de,
|
|
645
|
+
revokeSession: fe,
|
|
646
|
+
revokeAllOtherSessions: ge,
|
|
647
|
+
initialize: M,
|
|
648
|
+
setAuthData: P,
|
|
649
|
+
verifyMfa: ie,
|
|
650
|
+
sendMfaEmailCode: se,
|
|
651
|
+
getMfaWebAuthnChallenge: re,
|
|
652
|
+
registerHardwareKey: B,
|
|
653
|
+
completeHardwareKeyRegistration: W,
|
|
654
|
+
// Token management
|
|
655
|
+
startTokenRefreshTimer: _,
|
|
656
|
+
stopTokenRefreshTimer: E,
|
|
657
|
+
getAuthHeaders: K,
|
|
658
|
+
// Force re-initialization (useful for testing or navigation)
|
|
659
|
+
forceReInit: () => {
|
|
660
|
+
p.value = !1, i.value.initializing = !0, M();
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
export {
|
|
665
|
+
Ae as u
|
|
666
|
+
};
|