@strands.gg/accui 2.18.1 → 2.19.1

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 (74) hide show
  1. package/dist/StrandsUIPlugin-5AOCDOcM.cjs.js +143 -0
  2. package/dist/StrandsUIPlugin-AI0SbvpH.es.js +20708 -0
  3. package/dist/StrandsUIPlugin-B0HUf-tm.es.js +20437 -0
  4. package/dist/StrandsUIPlugin-B98LOdrQ.cjs.js +143 -0
  5. package/dist/StrandsUIPlugin-BbeOau1G.cjs.js +143 -0
  6. package/dist/StrandsUIPlugin-BcARSDQF.cjs.js +143 -0
  7. package/dist/StrandsUIPlugin-CCRrewS9.es.js +20587 -0
  8. package/dist/StrandsUIPlugin-CQk56uZ2.es.js +19805 -0
  9. package/dist/StrandsUIPlugin-Cgmu6cTA.es.js +20440 -0
  10. package/dist/StrandsUIPlugin-CpBH0QFc.cjs.js +272 -0
  11. package/dist/StrandsUIPlugin-Ct91SLTy.es.js +20587 -0
  12. package/dist/StrandsUIPlugin-DHhawL07.cjs.js +143 -0
  13. package/dist/StrandsUIPlugin-DY7VCw7G.es.js +20437 -0
  14. package/dist/StrandsUIPlugin-DZU1IjwD.cjs.js +143 -0
  15. package/dist/StrandsUIPlugin-Tpi8IoJc.cjs.js +143 -0
  16. package/dist/StrandsUIPlugin-lRGsfjf5.es.js +20440 -0
  17. package/dist/index.cjs.js +5 -5
  18. package/dist/index.es.js +2902 -2806
  19. package/dist/nuxt/module.cjs.js +6 -6
  20. package/dist/nuxt/module.es.js +17 -17
  21. package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js +1 -1
  22. package/dist/nuxt/runtime/composables/useStrandsAuth.d.ts +7 -1
  23. package/dist/nuxt/runtime/composables/useStrandsAuth.es.js +24 -19
  24. package/dist/nuxt/runtime/plugin.client.cjs.js +1 -1
  25. package/dist/nuxt/runtime/plugin.client.es.js +9 -14
  26. package/dist/nuxt/runtime/plugin.server.cjs.js +1 -1
  27. package/dist/nuxt/runtime/plugin.server.es.js +1 -1
  28. package/dist/types/composables.d.ts +6 -2
  29. package/dist/types/index.d.ts +14 -0
  30. package/dist/useDarkMode-Cew-NWjS.cjs.js +1 -0
  31. package/dist/useDarkMode-CvinMR9O.es.js +102 -0
  32. package/dist/useDarkMode-OZ_A8-6H.es.js +102 -0
  33. package/dist/useStrandsAuth-B7Fqq-Qp.es.js +717 -0
  34. package/dist/useStrandsAuth-BDTnh_c_.es.js +702 -0
  35. package/dist/useStrandsAuth-CEQ4UsKz.cjs.js +1 -0
  36. package/dist/useStrandsAuth-CboHAkjH.es.js +717 -0
  37. package/dist/useStrandsAuth-CetpoBNk.cjs.js +1 -0
  38. package/dist/useStrandsAuth-D2HvStMt.es.js +787 -0
  39. package/dist/useStrandsAuth-DSLFMDTj.es.js +717 -0
  40. package/dist/useStrandsAuth-Db5ZGvxL.cjs.js +1 -0
  41. package/dist/useStrandsAuth-Dz3Grglt.cjs.js +1 -0
  42. package/dist/useStrandsAuth-FVoni53L.es.js +666 -0
  43. package/dist/useStrandsAuth-Yp5OpRHU.cjs.js +1 -0
  44. package/dist/useStrandsConfig-48gZP9xV.es.js +209 -0
  45. package/dist/useStrandsConfig-B8h5hDy7.es.js +219 -0
  46. package/dist/useStrandsConfig-BGJg1LlC.es.js +213 -0
  47. package/dist/useStrandsConfig-BY_IIwK8.cjs.js +1 -0
  48. package/dist/useStrandsConfig-DRGldIzy.cjs.js +1 -0
  49. package/dist/vite.cjs.js +1 -1
  50. package/dist/vite.es.js +1 -1
  51. package/dist/vue/components/SignedIn.vue.d.ts +3 -1
  52. package/dist/vue/components/VirtualList.vue.d.ts +36 -0
  53. package/dist/vue/composables/useKnownAccounts.d.ts +33 -0
  54. package/dist/vue/index.d.ts +1 -0
  55. package/dist/vue/ui/UiBunnyFontPicker.vue.d.ts +23 -0
  56. package/dist/vue/ui/UiInput/UiInput.Select.vue.d.ts +806 -0
  57. package/dist/webcomponents/entries/strands-alert.d.ts +2 -0
  58. package/dist/webcomponents/entries/strands-button.d.ts +2 -0
  59. package/dist/webcomponents/entries/strands-card.d.ts +2 -0
  60. package/dist/webcomponents/entries/strands-checkbox-group.d.ts +2 -0
  61. package/dist/webcomponents/entries/strands-divider.d.ts +2 -0
  62. package/dist/webcomponents/entries/strands-input.d.ts +2 -0
  63. package/dist/webcomponents/entries/strands-link.d.ts +2 -0
  64. package/dist/webcomponents/entries/strands-loader-spinner.d.ts +2 -0
  65. package/dist/webcomponents/entries/strands-modal.d.ts +2 -0
  66. package/dist/webcomponents/entries/strands-pill.d.ts +2 -0
  67. package/dist/webcomponents/entries/strands-radio-group.d.ts +2 -0
  68. package/dist/webcomponents/entries/strands-slider.d.ts +2 -0
  69. package/dist/webcomponents/entries/strands-table.d.ts +2 -0
  70. package/dist/webcomponents/entries/strands-tabs.d.ts +2 -0
  71. package/dist/webcomponents/entries/strands-theme-toggle.d.ts +2 -0
  72. package/dist/webcomponents/entries/strands-toggle.d.ts +2 -0
  73. package/dist/webcomponents/entries/strands-tooltip.d.ts +2 -0
  74. package/package.json +16 -16
@@ -0,0 +1,787 @@
1
+ import { computed as u, ref as g, getCurrentInstance as Ae, onUnmounted as ke } from "vue";
2
+ import { u as Ee } from "./useStrandsConfig-B8h5hDy7.es.js";
3
+ const j = "strands_known_accounts";
4
+ function U() {
5
+ if (typeof document > "u") return [];
6
+ try {
7
+ const n = document.cookie.split(";").map((p) => p.trim()).find((p) => p.startsWith(`${j}=`));
8
+ if (!n) return [];
9
+ const d = n.slice(j.length + 1), m = decodeURIComponent(d), h = JSON.parse(m);
10
+ return Array.isArray(h) ? h : [];
11
+ } catch {
12
+ return [];
13
+ }
14
+ }
15
+ const E = g(U());
16
+ function xe() {
17
+ function n() {
18
+ E.value = U();
19
+ }
20
+ const d = u(
21
+ () => E.value.find((s) => s.active) ?? null
22
+ ), m = u(
23
+ () => E.value.filter((s) => !s.active)
24
+ ), h = u(() => E.value.length > 1), p = u(() => E.value.length > 0);
25
+ return {
26
+ accounts: E,
27
+ activeAccount: d,
28
+ otherAccounts: m,
29
+ hasMultipleAccounts: h,
30
+ hasKnownAccounts: p,
31
+ refreshFromCookie: n
32
+ };
33
+ }
34
+ class Oe {
35
+ cache = /* @__PURE__ */ new Map();
36
+ DEFAULT_TTL = 300 * 1e3;
37
+ // 5 minutes
38
+ /**
39
+ * Memoized fetch - prevents duplicate requests and caches results
40
+ */
41
+ async fetch(d, m, h = this.DEFAULT_TTL) {
42
+ const p = Date.now(), s = this.cache.get(d);
43
+ if (s && p - s.timestamp < s.ttl)
44
+ return s.promise;
45
+ this.cleanExpired();
46
+ const f = m().finally(() => {
47
+ setTimeout(() => {
48
+ this.cache.delete(d);
49
+ }, h);
50
+ });
51
+ return this.cache.set(d, {
52
+ promise: f,
53
+ timestamp: p,
54
+ ttl: h
55
+ }), f;
56
+ }
57
+ /**
58
+ * Clear all cached entries
59
+ */
60
+ clear() {
61
+ this.cache.clear();
62
+ }
63
+ /**
64
+ * Remove a specific cache entry
65
+ */
66
+ invalidate(d) {
67
+ this.cache.delete(d);
68
+ }
69
+ /**
70
+ * Clean expired cache entries
71
+ */
72
+ cleanExpired() {
73
+ const d = Date.now();
74
+ for (const [m, h] of this.cache.entries())
75
+ d - h.timestamp > h.ttl && this.cache.delete(m);
76
+ }
77
+ /**
78
+ * Get cache statistics (for debugging)
79
+ */
80
+ getStats() {
81
+ return {
82
+ size: this.cache.size,
83
+ entries: Array.from(this.cache.keys())
84
+ };
85
+ }
86
+ }
87
+ const y = new Oe();
88
+ function be() {
89
+ return {
90
+ fetch: y.fetch.bind(y),
91
+ clear: y.clear.bind(y),
92
+ invalidate: y.invalidate.bind(y),
93
+ getStats: y.getStats.bind(y)
94
+ };
95
+ }
96
+ function Ie(n, d) {
97
+ let m = null;
98
+ return (...h) => {
99
+ m && clearTimeout(m), m = setTimeout(() => {
100
+ n(...h);
101
+ }, d);
102
+ };
103
+ }
104
+ const R = Ie((n, d) => {
105
+ typeof window < "u" && localStorage.setItem(n, d);
106
+ }, 300), x = (n) => ({
107
+ id: n.id,
108
+ email: n.email,
109
+ firstName: n.first_name || n.firstName || "",
110
+ lastName: n.last_name || n.lastName || "",
111
+ avatar: n.avatar_url || n.avatar,
112
+ mfaEnabled: n.mfa_enabled ?? n.mfaEnabled ?? !1,
113
+ emailVerified: n.email_verified ?? n.emailVerified ?? !1,
114
+ passwordUpdatedAt: n.password_updated_at || n.passwordUpdatedAt,
115
+ settings: n.settings || {},
116
+ xp: n.xp || 0,
117
+ level: n.level || 1,
118
+ next_level_xp: n.next_level_xp || n.next_level_xp || 4,
119
+ username: n.username,
120
+ usernameLastChangedAt: n.username_last_changed_at || n.usernameLastChangedAt,
121
+ createdAt: n.created_at || n.createdAt,
122
+ updatedAt: n.updated_at || n.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
123
+ }), J = () => {
124
+ if (typeof window > "u")
125
+ return {
126
+ currentUser: g(null),
127
+ currentSession: g(null),
128
+ loadingStates: g({
129
+ initializing: !0,
130
+ signingIn: !1,
131
+ signingUp: !1,
132
+ signingOut: !1,
133
+ refreshingToken: !1,
134
+ sendingMfaEmail: !1,
135
+ verifyingMfa: !1,
136
+ loadingProfile: !1
137
+ }),
138
+ isInitialized: g(!1),
139
+ mfaRequired: g(!1),
140
+ mfaSessionId: g(null),
141
+ availableMfaMethods: g([]),
142
+ promise: null,
143
+ refreshTimer: null,
144
+ heartbeatTimer: null,
145
+ refreshPromise: null,
146
+ refreshTokenFn: null,
147
+ startTimerFn: null,
148
+ stopTimerFn: null
149
+ };
150
+ const n = window;
151
+ return n.__STRANDS_AUTH_STATE__ || (n.__STRANDS_AUTH_STATE__ = {
152
+ currentUser: g(null),
153
+ currentSession: g(null),
154
+ loadingStates: g({
155
+ initializing: !0,
156
+ signingIn: !1,
157
+ signingUp: !1,
158
+ signingOut: !1,
159
+ refreshingToken: !1,
160
+ sendingMfaEmail: !1,
161
+ verifyingMfa: !1,
162
+ loadingProfile: !1
163
+ }),
164
+ isInitialized: g(!1),
165
+ mfaRequired: g(!1),
166
+ mfaSessionId: g(null),
167
+ availableMfaMethods: g([]),
168
+ promise: null,
169
+ refreshTimer: null,
170
+ heartbeatTimer: null,
171
+ refreshPromise: null,
172
+ refreshTokenFn: null,
173
+ startTimerFn: null,
174
+ stopTimerFn: null
175
+ }), n.__STRANDS_AUTH_STATE__;
176
+ }, l = J();
177
+ function Fe() {
178
+ const { getUrl: n, config: d } = Ee(), { fetch: m, clear: h, invalidate: p } = be(), { currentUser: s, currentSession: f, loadingStates: i, isInitialized: T, mfaRequired: _, mfaSessionId: v, availableMfaMethods: A } = l, { refreshFromCookie: I } = xe(), M = () => {
179
+ if (s.value = null, f.value = null, _.value = !1, v.value = null, A.value = [], k(), l.refreshPromise = null, h(), typeof window < "u" && d.value?.onSignOutUrl) {
180
+ const t = window.location.pathname + window.location.search, e = d.value.onSignOutUrl;
181
+ if (t !== e && !t.startsWith(e)) {
182
+ const a = encodeURIComponent(t), r = e.includes("?") ? "&" : "?";
183
+ window.location.href = `${e}${r}redirect=${a}`;
184
+ }
185
+ }
186
+ }, z = u(() => i.value.initializing), L = u(() => i.value.signingIn), H = u(() => i.value.signingUp), K = u(() => i.value.signingOut), V = u(() => i.value.refreshingToken), q = u(() => i.value.sendingMfaEmail), B = u(() => i.value.verifyingMfa);
187
+ u(() => i.value.loadingProfile);
188
+ const C = u(
189
+ () => i.value.signingIn || i.value.signingUp || i.value.signingOut || i.value.refreshingToken || i.value.sendingMfaEmail || i.value.verifyingMfa || i.value.loadingProfile
190
+ ), G = u(() => i.value.initializing || C.value), W = u(() => {
191
+ const t = i.value;
192
+ 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...";
193
+ }), Y = () => ({
194
+ "Content-Type": "application/json"
195
+ }), Q = async () => {
196
+ try {
197
+ const t = await fetch(n("authStatus"), {
198
+ method: "GET",
199
+ credentials: "include"
200
+ });
201
+ if (!t.ok)
202
+ return !1;
203
+ const e = await t.json();
204
+ if (e.authenticated && e.user) {
205
+ s.value = x(e.user);
206
+ const a = e.expires_at ? new Date(e.expires_at * 1e3) : new Date(Date.now() + 480 * 60 * 1e3);
207
+ return f.value = {
208
+ accessToken: "",
209
+ // Token is in HttpOnly cookie
210
+ refreshToken: "",
211
+ // Token is in HttpOnly cookie
212
+ expiresAt: a,
213
+ userId: e.user.id
214
+ }, !0;
215
+ }
216
+ return !1;
217
+ } catch {
218
+ return !1;
219
+ }
220
+ }, X = async (t, e, a) => {
221
+ const r = await fetch(n("mfaHardwareCompleteRegistration"), {
222
+ method: "POST",
223
+ headers: {
224
+ "Content-Type": "application/json"
225
+ },
226
+ credentials: "include",
227
+ body: JSON.stringify({
228
+ device_id: t,
229
+ credential: e
230
+ })
231
+ });
232
+ if (!r.ok) {
233
+ const c = await r.text();
234
+ let o = "Failed to complete hardware key registration";
235
+ try {
236
+ const w = JSON.parse(c);
237
+ o = w.message || w.error || c;
238
+ } catch {
239
+ o = c || "Failed to complete hardware key registration";
240
+ }
241
+ throw new Error(o);
242
+ }
243
+ return r.json();
244
+ }, Z = async (t, e, a = "hardware") => {
245
+ const r = await fetch(n("mfaHardwareStartRegistration"), {
246
+ method: "POST",
247
+ headers: {
248
+ "Content-Type": "application/json"
249
+ },
250
+ credentials: "include",
251
+ body: JSON.stringify({
252
+ device_name: t,
253
+ device_type: a
254
+ })
255
+ });
256
+ if (!r.ok) {
257
+ const c = await r.text();
258
+ let o = "Failed to start hardware key registration";
259
+ try {
260
+ const w = JSON.parse(c);
261
+ o = w.message || w.error || c;
262
+ } catch {
263
+ o = c || "Failed to start hardware key registration";
264
+ }
265
+ throw new Error(o);
266
+ }
267
+ return r.json();
268
+ }, ee = u(() => s.value !== null), te = async (t) => {
269
+ i.value.signingIn = !0;
270
+ try {
271
+ _.value = !1, v.value = null, A.value = [];
272
+ const e = {
273
+ "Content-Type": "application/json"
274
+ };
275
+ typeof window < "u" && window.location && (e.Origin = window.location.origin);
276
+ const a = await fetch(n("signIn"), {
277
+ method: "POST",
278
+ headers: e,
279
+ credentials: "include",
280
+ // Include cookies for auth
281
+ body: JSON.stringify(t)
282
+ });
283
+ if (!a.ok)
284
+ throw a.status === 401 ? new Error("Invalid email or password") : a.status === 403 ? new Error("Please verify your email address before signing in") : new Error(`Sign in failed: ${a.status} ${a.statusText}`);
285
+ const r = await a.json();
286
+ if (r.mfa_required) {
287
+ _.value = !0, v.value = r.mfa_session_id || null;
288
+ const c = (r.available_mfa_methods || []).map((o) => {
289
+ let w = `${o.device_type.charAt(0).toUpperCase() + o.device_type.slice(1)} Authentication`;
290
+ return o.device_type === "hardware" ? w = o.device_name || "Security Key" : o.device_type === "totp" ? w = o.device_name || "Authenticator App" : o.device_type === "email" && (w = o.device_name || "Email Verification"), {
291
+ id: o.device_id,
292
+ device_type: o.device_type,
293
+ device_name: o.device_name || w,
294
+ is_active: !0,
295
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
296
+ last_used_at: o.last_used_at,
297
+ // Pass through additional metadata if available
298
+ credential_id: o.credential_id,
299
+ device_info: o.device_info
300
+ };
301
+ });
302
+ return A.value = c, i.value.signingIn = !1, r;
303
+ }
304
+ return await b(r), r;
305
+ } catch (e) {
306
+ throw e;
307
+ } finally {
308
+ i.value.signingIn = !1;
309
+ }
310
+ }, ne = async (t) => {
311
+ i.value.signingUp = !0;
312
+ try {
313
+ throw new Error("Sign up not implemented - please integrate with auth SDK");
314
+ } finally {
315
+ i.value.signingUp = !1;
316
+ }
317
+ }, ae = async (t) => {
318
+ i.value.signingOut = !0;
319
+ try {
320
+ const e = t?.all ? `${n("signOut")}?all=true` : n("signOut");
321
+ await (await fetch(e, {
322
+ method: "POST",
323
+ credentials: "include"
324
+ // Cookies will be cleared by server response
325
+ })).text(), k(), l.refreshPromise = null, h(), s.value = null, f.value = null, _.value = !1, v.value = null, A.value = [], T.value = !1, I(), typeof window < "u" && d.value?.onSignOutUrl && (window.location.href = d.value.onSignOutUrl);
326
+ } finally {
327
+ i.value.signingOut = !1;
328
+ }
329
+ }, O = async () => {
330
+ if (l.refreshPromise)
331
+ return await l.refreshPromise;
332
+ l.refreshPromise = (async () => {
333
+ i.value.refreshingToken = !0;
334
+ try {
335
+ const e = await fetch(n("refresh"), {
336
+ method: "POST",
337
+ headers: {
338
+ "Content-Type": "application/json"
339
+ },
340
+ credentials: "include"
341
+ // Refresh token sent via cookie
342
+ });
343
+ if (!e.ok) {
344
+ if (e.status === 401)
345
+ return M(), !1;
346
+ throw new Error(`Token refresh failed: ${e.status} ${e.statusText}`);
347
+ }
348
+ const a = await e.json();
349
+ a.user && (s.value = x(a.user));
350
+ const c = {
351
+ accessToken: "",
352
+ // Token is in HttpOnly cookie
353
+ refreshToken: "",
354
+ // Token is in HttpOnly cookie
355
+ expiresAt: a.expires_at ? new Date(a.expires_at * 1e3) : new Date(Date.now() + 480 * 60 * 1e3),
356
+ userId: a.user?.id || s.value?.id
357
+ };
358
+ return f.value = c, S(), p(`sessions:${s.value?.id || "unknown"}`), !0;
359
+ } catch {
360
+ return M(), !1;
361
+ } finally {
362
+ i.value.refreshingToken = !1;
363
+ }
364
+ })();
365
+ const t = await l.refreshPromise;
366
+ return l.refreshPromise = null, t;
367
+ }, ie = async () => {
368
+ const t = `profile:${f.value.accessToken.slice(0, 20)}`;
369
+ i.value.loadingProfile = !0;
370
+ try {
371
+ return await m(t, async () => {
372
+ const e = await fetch(n("profile"), {
373
+ method: "GET",
374
+ headers: {
375
+ "Content-Type": "application/json",
376
+ Authorization: `Bearer ${f.value?.accessToken}`
377
+ }
378
+ });
379
+ if (!e.ok)
380
+ throw e.status === 401 ? new Error("Authentication expired. Please sign in again.") : new Error(`Failed to fetch profile: ${e.status} ${e.statusText}`);
381
+ const a = await e.json();
382
+ return s.value = x(a), s.value && typeof window < "u" && localStorage.setItem("strands_auth_user", JSON.stringify(s.value)), s.value;
383
+ });
384
+ } finally {
385
+ i.value.loadingProfile = !1;
386
+ }
387
+ }, se = async (t) => {
388
+ i.value.loadingProfile = !0;
389
+ try {
390
+ const e = await fetch(n("profile"), {
391
+ method: "POST",
392
+ headers: {
393
+ "Content-Type": "application/json",
394
+ Authorization: `Bearer ${f.value.accessToken}`
395
+ },
396
+ body: JSON.stringify({
397
+ first_name: t.firstName,
398
+ last_name: t.lastName
399
+ })
400
+ });
401
+ if (!e.ok)
402
+ throw e.status === 401 ? new Error("Authentication expired. Please sign in again.") : new Error(`Profile update failed: ${e.status} ${e.statusText}`);
403
+ const a = await e.json();
404
+ return s.value = x(a), s.value && R("strands_auth_user", JSON.stringify(s.value)), s.value;
405
+ } finally {
406
+ i.value.loadingProfile = !1;
407
+ }
408
+ }, re = async (t) => {
409
+ i.value.loadingProfile = !0;
410
+ try {
411
+ const e = await fetch(n("settings"), {
412
+ method: "POST",
413
+ headers: {
414
+ "Content-Type": "application/json",
415
+ Authorization: `Bearer ${f.value.accessToken}`
416
+ },
417
+ body: JSON.stringify({
418
+ settings: t
419
+ })
420
+ });
421
+ if (!e.ok)
422
+ throw e.status === 401 ? new Error("Authentication expired. Please sign in again.") : new Error(`Settings update failed: ${e.status} ${e.statusText}`);
423
+ const a = await e.json();
424
+ return s.value = x(a), s.value && R("strands_auth_user", JSON.stringify(s.value)), s.value;
425
+ } finally {
426
+ i.value.loadingProfile = !1;
427
+ }
428
+ }, oe = async (t, e) => {
429
+ i.value.loadingProfile = !0;
430
+ try {
431
+ const a = await fetch(n("changeEmail"), {
432
+ method: "POST",
433
+ headers: {
434
+ "Content-Type": "application/json",
435
+ Authorization: `Bearer ${f.value.accessToken}`
436
+ },
437
+ body: JSON.stringify({
438
+ new_email: t,
439
+ password: e
440
+ })
441
+ });
442
+ if (!a.ok) {
443
+ if (a.status === 401)
444
+ throw new Error("Authentication expired. Please sign in again.");
445
+ {
446
+ const c = await a.json().catch(() => ({}));
447
+ throw new Error(c.message || `Email change failed: ${a.status} ${a.statusText}`);
448
+ }
449
+ }
450
+ const r = await a.json();
451
+ return s.value && (s.value = {
452
+ ...s.value,
453
+ email: t,
454
+ emailVerified: !1,
455
+ // Email needs to be verified again
456
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
457
+ }, typeof window < "u" && localStorage.setItem("strands_auth_user", JSON.stringify(s.value))), r;
458
+ } finally {
459
+ i.value.loadingProfile = !1;
460
+ }
461
+ }, le = async (t, e, a = !1) => {
462
+ if (!v.value)
463
+ throw new Error("No MFA session available");
464
+ i.value.verifyingMfa = !0;
465
+ try {
466
+ const r = n(a ? "mfaBackupCodeVerify" : "mfaSigninVerify"), c = a ? { mfa_session_id: v.value, backup_code: e } : { mfa_session_id: v.value, device_id: t, code: e }, o = await fetch(r, {
467
+ method: "POST",
468
+ headers: { "Content-Type": "application/json" },
469
+ credentials: "include",
470
+ body: JSON.stringify(c)
471
+ });
472
+ if (!o.ok) {
473
+ const D = await o.text();
474
+ let F = "MFA verification failed";
475
+ try {
476
+ const $ = JSON.parse(D);
477
+ F = $.message || $.error || D;
478
+ } catch {
479
+ F = D || "MFA verification failed";
480
+ }
481
+ throw new Error(F);
482
+ }
483
+ const w = await o.json();
484
+ return _.value = !1, v.value = null, A.value = [], await b(w), w;
485
+ } finally {
486
+ i.value.verifyingMfa = !1;
487
+ }
488
+ }, ce = async (t) => {
489
+ if (!v.value)
490
+ throw new Error("No MFA session available");
491
+ i.value.sendingMfaEmail = !0;
492
+ try {
493
+ const e = await fetch(n("mfaSigninSendEmail"), {
494
+ method: "POST",
495
+ headers: { "Content-Type": "application/json" },
496
+ credentials: "include",
497
+ body: JSON.stringify({
498
+ mfa_session_id: v.value,
499
+ device_id: t
500
+ })
501
+ });
502
+ if (!e.ok) {
503
+ const r = await e.text();
504
+ let c = "Failed to send MFA email code";
505
+ try {
506
+ const o = JSON.parse(r);
507
+ c = o.message || o.error || r;
508
+ } catch {
509
+ c = r || "Failed to send MFA email code";
510
+ }
511
+ throw new Error(c);
512
+ }
513
+ return await e.json();
514
+ } finally {
515
+ i.value.sendingMfaEmail = !1;
516
+ }
517
+ }, ue = async (t) => {
518
+ if (!v.value)
519
+ throw new Error("No MFA session available");
520
+ const e = await fetch(n("mfaWebAuthnChallenge"), {
521
+ method: "POST",
522
+ headers: { "Content-Type": "application/json" },
523
+ credentials: "include",
524
+ body: JSON.stringify({
525
+ mfa_session_id: v.value,
526
+ device_id: t
527
+ })
528
+ });
529
+ if (!e.ok) {
530
+ const a = await e.text();
531
+ let r = "Failed to get WebAuthn challenge";
532
+ try {
533
+ const c = JSON.parse(a);
534
+ r = c.message || c.error || a;
535
+ } catch {
536
+ r = a || r;
537
+ }
538
+ throw new Error(r);
539
+ }
540
+ return e.json();
541
+ }, b = async (t) => {
542
+ try {
543
+ t.user && (s.value = x(t.user));
544
+ const a = {
545
+ accessToken: "",
546
+ // Token is in HttpOnly cookie
547
+ refreshToken: "",
548
+ // Token is in HttpOnly cookie
549
+ expiresAt: t.expires_at ? new Date(t.expires_at * 1e3) : new Date(Date.now() + 480 * 60 * 1e3),
550
+ userId: s.value?.id || t.user?.id
551
+ };
552
+ f.value = a, S(), I();
553
+ } catch {
554
+ }
555
+ }, fe = 240 * 1e3, de = () => {
556
+ l.heartbeatTimer && (clearInterval(l.heartbeatTimer), l.heartbeatTimer = null);
557
+ };
558
+ let N;
559
+ const S = () => {
560
+ if (l.refreshTimer && clearTimeout(l.refreshTimer), !f.value || typeof document < "u" && document.visibilityState === "hidden")
561
+ return;
562
+ const t = /* @__PURE__ */ new Date(), a = f.value.expiresAt.getTime() - t.getTime() - 60 * 1e3;
563
+ if (a <= 0) {
564
+ O();
565
+ return;
566
+ }
567
+ l.refreshTimer = setTimeout(async () => {
568
+ (typeof document > "u" || document.visibilityState === "visible") && await O() && S();
569
+ }, a), N();
570
+ }, k = () => {
571
+ l.refreshTimer && (clearTimeout(l.refreshTimer), l.refreshTimer = null), de();
572
+ };
573
+ N = () => {
574
+ l.heartbeatTimer || (l.heartbeatTimer = setInterval(async () => {
575
+ if (typeof document < "u" && document.visibilityState === "hidden" || !f.value) return;
576
+ const t = Date.now();
577
+ f.value.expiresAt.getTime() - t <= 300 * 1e3 && await O(), !l.refreshTimer && f.value && S();
578
+ }, fe));
579
+ }, l.refreshTokenFn = O, l.startTimerFn = S, l.stopTimerFn = k;
580
+ const P = async () => {
581
+ if (T.value) {
582
+ i.value.initializing = !1;
583
+ return;
584
+ }
585
+ if (l.promise) {
586
+ await l.promise;
587
+ return;
588
+ }
589
+ i.value.initializing = !0;
590
+ const t = async () => {
591
+ try {
592
+ typeof window < "u" && (await Promise.race([
593
+ Q(),
594
+ new Promise((r) => setTimeout(() => r(!1), 5e3))
595
+ ]) ? S() : (s.value = null, f.value = null)), T.value = !0, await new Promise((e) => setTimeout(e, 50));
596
+ } catch {
597
+ } finally {
598
+ i.value.initializing = !1, l.promise = null;
599
+ }
600
+ };
601
+ return l.promise = t(), l.promise;
602
+ }, he = async (t) => {
603
+ i.value.loadingProfile = !0;
604
+ try {
605
+ const e = await fetch(n("changeUsername"), {
606
+ method: "POST",
607
+ headers: {
608
+ "Content-Type": "application/json"
609
+ },
610
+ credentials: "include",
611
+ body: JSON.stringify({
612
+ username: t
613
+ })
614
+ });
615
+ if (!e.ok) {
616
+ const r = await e.json().catch(() => ({}));
617
+ 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}`);
618
+ }
619
+ const a = await e.json();
620
+ return s.value && (s.value = {
621
+ ...s.value,
622
+ username: t,
623
+ usernameLastChangedAt: (/* @__PURE__ */ new Date()).toISOString(),
624
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
625
+ }), a;
626
+ } finally {
627
+ i.value.loadingProfile = !1;
628
+ }
629
+ }, ge = async () => {
630
+ const t = await fetch(n("usernameCooldown"), {
631
+ method: "GET",
632
+ credentials: "include"
633
+ });
634
+ if (!t.ok)
635
+ throw new Error(`Failed to get username cooldown: ${t.status} ${t.statusText}`);
636
+ return t.json();
637
+ }, me = async (t) => {
638
+ const e = n("checkUsernameAvailability").replace("{username}", encodeURIComponent(t)), a = await fetch(e, {
639
+ method: "GET",
640
+ headers: {
641
+ "Content-Type": "application/json"
642
+ }
643
+ });
644
+ if (!a.ok)
645
+ throw new Error(`Failed to check username availability: ${a.status} ${a.statusText}`);
646
+ return a.json();
647
+ }, ve = async () => {
648
+ const t = `sessions:${s.value?.id || "no-user"}`;
649
+ try {
650
+ return await m(t, async () => {
651
+ const e = await fetch(n("sessions"), {
652
+ method: "GET",
653
+ credentials: "include"
654
+ });
655
+ if (!e.ok)
656
+ throw await e.text(), new Error(`Failed to get user sessions: ${e.status} ${e.statusText}`);
657
+ return e.json();
658
+ }, 120 * 1e3);
659
+ } catch (e) {
660
+ throw e;
661
+ }
662
+ }, we = async () => {
663
+ const t = await fetch(n("sessionsStats"), {
664
+ method: "GET",
665
+ credentials: "include"
666
+ });
667
+ if (!t.ok)
668
+ throw new Error(`Failed to get session stats: ${t.status} ${t.statusText}`);
669
+ return t.json();
670
+ }, pe = async (t) => {
671
+ const e = n("sessionRevoke").replace("{session_id}", encodeURIComponent(t)), a = await fetch(e, {
672
+ method: "POST",
673
+ credentials: "include"
674
+ });
675
+ if (!a.ok)
676
+ throw new Error(`Failed to revoke session: ${a.status} ${a.statusText}`);
677
+ return a.status === 200;
678
+ }, ye = async () => {
679
+ const t = await fetch(n("sessionsRevokeAll"), {
680
+ method: "POST",
681
+ credentials: "include"
682
+ });
683
+ if (!t.ok)
684
+ throw new Error(`Failed to revoke all other sessions: ${t.status} ${t.statusText}`);
685
+ return t.status === 200;
686
+ }, Te = async (t) => {
687
+ const e = await fetch(n("switchAccount"), {
688
+ method: "POST",
689
+ headers: { "Content-Type": "application/json" },
690
+ credentials: "include",
691
+ body: JSON.stringify({ user_id: t })
692
+ });
693
+ if (!e.ok) {
694
+ const r = await e.json().catch(() => ({})), c = new Error(r.message || "Failed to switch account");
695
+ throw c.code = r.code, c.email = r.email, c;
696
+ }
697
+ const a = await e.json();
698
+ return await b(a), a;
699
+ }, Se = async (t) => {
700
+ if (!(await fetch(n("removeKnownAccount"), {
701
+ method: "POST",
702
+ headers: { "Content-Type": "application/json" },
703
+ credentials: "include",
704
+ body: JSON.stringify({ user_id: t })
705
+ })).ok)
706
+ throw new Error("Failed to remove known account");
707
+ I(), s.value?.id === t && (s.value = null, f.value = null, k(), h());
708
+ };
709
+ if (typeof document < "u" && typeof window < "u") {
710
+ const t = window;
711
+ t.__STRANDS_VISIBILITY_LISTENER__ || (t.__STRANDS_VISIBILITY_LISTENER__ = !0, document.addEventListener("visibilitychange", async () => {
712
+ const e = J();
713
+ if (document.visibilityState === "visible" && e.currentSession.value) {
714
+ const a = /* @__PURE__ */ new Date();
715
+ e.currentSession.value.expiresAt.getTime() - a.getTime() <= 120 * 1e3 ? e.refreshTokenFn && await e.refreshTokenFn() : e.startTimerFn && e.startTimerFn();
716
+ } else document.visibilityState === "hidden" && e.stopTimerFn && e.stopTimerFn();
717
+ }));
718
+ }
719
+ const _e = () => {
720
+ k(), h();
721
+ };
722
+ try {
723
+ Ae() && ke(_e);
724
+ } catch {
725
+ }
726
+ return T.value || P(), {
727
+ // State
728
+ user: u(() => s.value),
729
+ currentUser: u(() => s.value),
730
+ currentSession: u(() => f.value),
731
+ isAuthenticated: ee,
732
+ isLoading: u(() => G.value || !T.value),
733
+ loading: C,
734
+ loadingMessage: W,
735
+ // Specific loading states
736
+ isInitializing: z,
737
+ isInitialized: u(() => T.value),
738
+ isSigningIn: L,
739
+ isSigningUp: H,
740
+ isSigningOut: K,
741
+ isRefreshingToken: V,
742
+ isSendingMfaEmail: q,
743
+ isVerifyingMfa: B,
744
+ // MFA State
745
+ mfaRequired: u(() => _.value),
746
+ mfaSessionId: u(() => v.value),
747
+ availableMfaMethods: u(() => A.value),
748
+ // Methods
749
+ signIn: te,
750
+ signUp: ne,
751
+ signOut: ae,
752
+ refreshToken: O,
753
+ fetchProfile: ie,
754
+ updateProfile: se,
755
+ updateUserSettings: re,
756
+ changeEmail: oe,
757
+ changeUsername: he,
758
+ getUsernameCooldown: ge,
759
+ checkUsernameAvailability: me,
760
+ // Session management
761
+ getUserSessions: ve,
762
+ getSessionStats: we,
763
+ revokeSession: pe,
764
+ revokeAllOtherSessions: ye,
765
+ initialize: P,
766
+ setAuthData: b,
767
+ verifyMfa: le,
768
+ sendMfaEmailCode: ce,
769
+ getMfaWebAuthnChallenge: ue,
770
+ registerHardwareKey: Z,
771
+ completeHardwareKeyRegistration: X,
772
+ // Multi-account
773
+ switchAccount: Te,
774
+ removeKnownAccount: Se,
775
+ // Token management
776
+ startTokenRefreshTimer: S,
777
+ stopTokenRefreshTimer: k,
778
+ getAuthHeaders: Y,
779
+ // Force re-initialization (useful for testing or navigation)
780
+ forceReInit: () => {
781
+ T.value = !1, i.value.initializing = !0, P();
782
+ }
783
+ };
784
+ }
785
+ export {
786
+ Fe as u
787
+ };