@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.
Files changed (106) hide show
  1. package/dist/{StrandsUIPlugin-fvlbvZ-P.cjs.js → StrandsUIPlugin-CiAAY2sN.cjs.js} +3 -3
  2. package/dist/StrandsUIPlugin-Dar_Az2V.es.js +20430 -0
  3. package/dist/StrandsUIPlugin-fHS0GmiX.cjs.js +143 -0
  4. package/dist/{StrandsUIPlugin-CSsUxobq.es.js → StrandsUIPlugin-hYC1FxeG.es.js} +950 -931
  5. package/dist/accui.css +1 -1
  6. package/dist/index.cjs.js +5 -5
  7. package/dist/index.es.js +6574 -5585
  8. package/dist/nuxt/module.cjs.js +2 -2
  9. package/dist/nuxt/module.es.js +21 -9
  10. package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js +1 -1
  11. package/dist/nuxt/runtime/composables/useStrandsAuth.d.ts +45 -0
  12. package/dist/nuxt/runtime/composables/useStrandsAuth.es.js +79 -19
  13. package/dist/nuxt/runtime/composables/useStrandsOAuth.d.ts +8 -0
  14. package/dist/nuxt/runtime/plugin.client.cjs.js +1 -1
  15. package/dist/nuxt/runtime/plugin.client.es.js +2 -2
  16. package/dist/nuxt/runtime/plugin.server.cjs.js +1 -1
  17. package/dist/nuxt/runtime/plugin.server.es.js +1 -1
  18. package/dist/nuxt/runtime/plugins/auth-interceptor.client.cjs.js +1 -1
  19. package/dist/nuxt/runtime/plugins/auth-interceptor.client.d.ts +7 -0
  20. package/dist/nuxt/runtime/plugins/auth-interceptor.client.es.js +21 -30
  21. package/dist/types/index.d.ts +8 -0
  22. package/dist/types/oauth.d.ts +420 -0
  23. package/dist/useDarkMode-BdG1G2mj.es.js +102 -0
  24. package/dist/{useDarkMode-Cew-NWjS.cjs.js → useDarkMode-BvBnG5kF.cjs.js} +1 -1
  25. package/dist/useDarkMode-Dd0Q5Nnz.cjs.js +1 -0
  26. package/dist/{useDarkMode-CvinMR9O.es.js → useDarkMode-FTkHLBsX.es.js} +15 -15
  27. package/dist/useDarkMode-OZ_A8-6H.es.js +102 -0
  28. package/dist/useStrandsAuth-CetpoBNk.cjs.js +1 -0
  29. package/dist/useStrandsAuth-DEWj1bFC.cjs.js +1 -0
  30. package/dist/useStrandsAuth-DTJ5fn33.es.js +666 -0
  31. package/dist/useStrandsAuth-Z7P9ujqQ.es.js +666 -0
  32. package/dist/useStrandsAuth-wzl2F9DE.es.js +702 -0
  33. package/dist/{useStrandsConfig-CKLyNlSd.es.js → useStrandsConfig-B2UXxIbT.es.js} +46 -40
  34. package/dist/useStrandsConfig-BGJg1LlC.es.js +213 -0
  35. package/dist/useStrandsConfig-BYOAEt9d.cjs.js +1 -0
  36. package/dist/vite.cjs.js +1 -1
  37. package/dist/vite.es.js +2 -2
  38. package/dist/vue/components/index.d.ts +2 -0
  39. package/dist/vue/composables/useAuthenticatedFetch.d.ts +3 -1
  40. package/dist/vue/composables/useDarkMode.d.ts +6 -1
  41. package/dist/vue/composables/useStrandsOAuth.d.ts +10 -0
  42. package/dist/vue/ui/UiMegaMenu/index.d.ts +4 -0
  43. package/dist/vue/ui/index.d.ts +8 -0
  44. package/package.json +1 -1
  45. package/dist/index.d.ts +0 -7
  46. package/dist/nuxt/module.d.ts +0 -4
  47. package/dist/nuxt/runtime/composables/useAuthenticatedFetch.d.ts +0 -20
  48. package/dist/nuxt/runtime/middleware/auth.d.ts +0 -6
  49. package/dist/nuxt/runtime/middleware/auth.global.d.ts +0 -2
  50. package/dist/nuxt/runtime/middleware/guest.d.ts +0 -6
  51. package/dist/nuxt/runtime/plugin.client.d.ts +0 -2
  52. package/dist/nuxt/runtime/plugin.server.d.ts +0 -2
  53. package/dist/nuxt.d.ts +0 -4
  54. package/dist/useStrandsAuth-CmAxFCQ_.cjs.js +0 -1
  55. package/dist/useStrandsAuth-DWsqp9WI.es.js +0 -661
  56. package/dist/useStrandsConfig-nOR0oGdb.cjs.js +0 -1
  57. package/dist/utils/colors.d.ts +0 -10
  58. package/dist/utils/index.d.ts +0 -2
  59. package/dist/utils/slots.d.ts +0 -1
  60. package/dist/utils/validation.d.ts +0 -12
  61. package/dist/vite/index.d.ts +0 -2
  62. package/dist/vite/plugin.d.ts +0 -68
  63. package/dist/vue/components/StrandsNav/index.d.ts +0 -8
  64. package/dist/vue/components/StrandsNav/types.d.ts +0 -12
  65. package/dist/vue/components/icons/index.d.ts +0 -2
  66. package/dist/vue/composables/useFloatingPosition.d.ts +0 -22
  67. package/dist/vue/composables/useModalStack.d.ts +0 -86
  68. package/dist/vue/composables/useOAuthProviders.d.ts +0 -73
  69. package/dist/vue/composables/useStrandsAuth.d.ts +0 -2
  70. package/dist/vue/composables/useStrandsConfig.d.ts +0 -5
  71. package/dist/vue/composables/useStrandsMfa.d.ts +0 -36
  72. package/dist/vue/composables/useTheme.d.ts +0 -18
  73. package/dist/vue/index.d.ts +0 -16
  74. package/dist/vue/plugins/StrandsUIPlugin.d.ts +0 -20
  75. package/dist/vue/utils/contrast.d.ts +0 -75
  76. package/dist/vue/utils/debounce.d.ts +0 -12
  77. package/dist/vue/utils/fontPreloader.d.ts +0 -11
  78. package/dist/vue/utils/iconProps.d.ts +0 -9
  79. package/dist/vue/utils/lazyComponents.d.ts +0 -4
  80. package/dist/vue/utils/levels.d.ts +0 -27
  81. package/dist/vue/utils/modalStack.d.ts +0 -34
  82. package/dist/vue/utils/performanceInit.d.ts +0 -40
  83. package/dist/vue/utils/requestCache.d.ts +0 -49
  84. package/dist/vue/utils/slots.d.ts +0 -9
  85. package/dist/vue/utils/sounds.d.ts +0 -57
  86. package/dist/webcomponents/define-element.d.ts +0 -25
  87. package/dist/webcomponents/entries/strands-alert.d.ts +0 -12
  88. package/dist/webcomponents/entries/strands-button.d.ts +0 -12
  89. package/dist/webcomponents/entries/strands-card.d.ts +0 -12
  90. package/dist/webcomponents/entries/strands-checkbox-group.d.ts +0 -11
  91. package/dist/webcomponents/entries/strands-divider.d.ts +0 -12
  92. package/dist/webcomponents/entries/strands-input.d.ts +0 -14
  93. package/dist/webcomponents/entries/strands-link.d.ts +0 -12
  94. package/dist/webcomponents/entries/strands-loader-spinner.d.ts +0 -10
  95. package/dist/webcomponents/entries/strands-modal.d.ts +0 -16
  96. package/dist/webcomponents/entries/strands-pill.d.ts +0 -11
  97. package/dist/webcomponents/entries/strands-radio-group.d.ts +0 -11
  98. package/dist/webcomponents/entries/strands-slider.d.ts +0 -10
  99. package/dist/webcomponents/entries/strands-table.d.ts +0 -13
  100. package/dist/webcomponents/entries/strands-tabs.d.ts +0 -12
  101. package/dist/webcomponents/entries/strands-theme-toggle.d.ts +0 -10
  102. package/dist/webcomponents/entries/strands-toggle.d.ts +0 -10
  103. package/dist/webcomponents/entries/strands-tooltip.d.ts +0 -12
  104. package/dist/webcomponents/index.d.ts +0 -8
  105. package/dist/webcomponents/loader.d.ts +0 -73
  106. 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
+ };