@strands.gg/accui 1.8.0 → 1.10.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.
Files changed (49) hide show
  1. package/dist/accui.css +1 -5802
  2. package/dist/nuxt/module.cjs.js +1 -109
  3. package/dist/nuxt/module.cjs.js.map +1 -1
  4. package/dist/nuxt/module.es.js +0 -1
  5. package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js +1 -57
  6. package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js.map +1 -1
  7. package/dist/nuxt/runtime/composables/useStrandsAuth.es.js +1 -2
  8. package/dist/nuxt/runtime/middleware/auth.global.cjs.js +1 -42
  9. package/dist/nuxt/runtime/middleware/auth.global.cjs.js.map +1 -1
  10. package/dist/nuxt/runtime/middleware/auth.global.es.js +0 -1
  11. package/dist/nuxt/runtime/plugin.client.cjs.js +1 -26
  12. package/dist/nuxt/runtime/plugin.client.cjs.js.map +1 -1
  13. package/dist/nuxt/runtime/plugin.client.es.js +2 -1
  14. package/dist/nuxt/runtime/plugin.client.es.js.map +1 -1
  15. package/dist/nuxt/runtime/plugin.server.cjs.js +1 -17
  16. package/dist/nuxt/runtime/plugin.server.cjs.js.map +1 -1
  17. package/dist/nuxt/runtime/plugin.server.es.js +0 -1
  18. package/dist/nuxt-v4/module.cjs.js +1 -119
  19. package/dist/nuxt-v4/module.cjs.js.map +1 -1
  20. package/dist/nuxt-v4/module.es.js +0 -1
  21. package/dist/nuxt-v4/runtime/composables/useStrandsAuth.cjs.js +1 -70
  22. package/dist/nuxt-v4/runtime/composables/useStrandsAuth.cjs.js.map +1 -1
  23. package/dist/nuxt-v4/runtime/composables/useStrandsAuth.es.js +1 -2
  24. package/dist/nuxt-v4/runtime/middleware/auth.global.cjs.js +1 -42
  25. package/dist/nuxt-v4/runtime/middleware/auth.global.cjs.js.map +1 -1
  26. package/dist/nuxt-v4/runtime/middleware/auth.global.es.js +0 -1
  27. package/dist/nuxt-v4/runtime/plugin.client.cjs.js +1 -26
  28. package/dist/nuxt-v4/runtime/plugin.client.cjs.js.map +1 -1
  29. package/dist/nuxt-v4/runtime/plugin.client.es.js +2 -1
  30. package/dist/nuxt-v4/runtime/plugin.client.es.js.map +1 -1
  31. package/dist/nuxt-v4/runtime/plugin.server.cjs.js +1 -22
  32. package/dist/nuxt-v4/runtime/plugin.server.cjs.js.map +1 -1
  33. package/dist/nuxt-v4/runtime/plugin.server.es.js +0 -1
  34. package/dist/strands-auth-ui.cjs.js +1 -9840
  35. package/dist/strands-auth-ui.cjs.js.map +1 -1
  36. package/dist/strands-auth-ui.es.js +1398 -808
  37. package/dist/strands-auth-ui.es.js.map +1 -1
  38. package/dist/useStrandsAuth-Co9ekmXB.cjs +1 -0
  39. package/dist/useStrandsAuth-Co9ekmXB.cjs.map +1 -0
  40. package/dist/{useStrandsAuth-Beee__9G.js → useStrandsAuth-kWUOoMm-.js} +314 -172
  41. package/dist/useStrandsAuth-kWUOoMm-.js.map +1 -0
  42. package/dist/useStrandsConfig-Cxb360Os.js +0 -1
  43. package/dist/useStrandsConfig-Z9_36OcV.cjs +1 -0
  44. package/dist/{useStrandsConfig-Dms13Zd0.cjs.map → useStrandsConfig-Z9_36OcV.cjs.map} +1 -1
  45. package/package.json +1 -1
  46. package/dist/useStrandsAuth-Beee__9G.js.map +0 -1
  47. package/dist/useStrandsAuth-JfbGH2c-.cjs +0 -755
  48. package/dist/useStrandsAuth-JfbGH2c-.cjs.map +0 -1
  49. package/dist/useStrandsConfig-Dms13Zd0.cjs +0 -179
@@ -1,38 +1,162 @@
1
- import { ref, computed } from "vue";
1
+ import { ref, computed, onUnmounted } from "vue";
2
2
  import { u as useStrandsConfig } from "./useStrandsConfig-Cxb360Os.js";
3
- const { getUrl } = useStrandsConfig();
4
- const currentUser = ref(null);
5
- const currentSession = ref(null);
6
- const isInitializing = ref(true);
7
- const isSigningIn = ref(false);
8
- const isSigningUp = ref(false);
9
- const isSigningOut = ref(false);
10
- const isRefreshingToken = ref(false);
11
- const isSendingMfaEmail = ref(false);
12
- const isVerifyingMfa = ref(false);
13
- const isLoadingProfile = ref(false);
14
- const loading = computed(() => isSigningIn.value || isSigningUp.value || isSigningOut.value || isRefreshingToken.value || isSendingMfaEmail.value || isVerifyingMfa.value || isLoadingProfile.value);
15
- const isLoading = computed(() => isInitializing.value || loading.value);
16
- const loadingMessage = computed(() => {
17
- if (isInitializing.value) return "Checking authentication...";
18
- if (isSigningIn.value) return "Signing you in...";
19
- if (isSigningUp.value) return "Creating your account...";
20
- if (isSigningOut.value) return "Signing you out...";
21
- if (isRefreshingToken.value) return "Refreshing session...";
22
- if (isSendingMfaEmail.value) return "Sending verification code...";
23
- if (isVerifyingMfa.value) return "Verifying code...";
24
- return "Loading...";
25
- });
26
- const isInitialized = ref(false);
27
- const mfaRequired = ref(false);
28
- const mfaSessionId = ref(null);
29
- const availableMfaMethods = ref([]);
3
+ class RequestCache {
4
+ cache = /* @__PURE__ */ new Map();
5
+ DEFAULT_TTL = 5 * 60 * 1e3;
6
+ // 5 minutes
7
+ /**
8
+ * Memoized fetch - prevents duplicate requests and caches results
9
+ */
10
+ async fetch(key, fetcher, ttl = this.DEFAULT_TTL) {
11
+ const now = Date.now();
12
+ const entry = this.cache.get(key);
13
+ if (entry && now - entry.timestamp < entry.ttl) {
14
+ return entry.promise;
15
+ }
16
+ this.cleanExpired();
17
+ const promise = fetcher().finally(() => {
18
+ setTimeout(() => {
19
+ this.cache.delete(key);
20
+ }, ttl);
21
+ });
22
+ this.cache.set(key, {
23
+ promise,
24
+ timestamp: now,
25
+ ttl
26
+ });
27
+ return promise;
28
+ }
29
+ /**
30
+ * Clear all cached entries
31
+ */
32
+ clear() {
33
+ this.cache.clear();
34
+ }
35
+ /**
36
+ * Remove a specific cache entry
37
+ */
38
+ invalidate(key) {
39
+ this.cache.delete(key);
40
+ }
41
+ /**
42
+ * Clean expired cache entries
43
+ */
44
+ cleanExpired() {
45
+ const now = Date.now();
46
+ for (const [key, entry] of this.cache.entries()) {
47
+ if (now - entry.timestamp > entry.ttl) {
48
+ this.cache.delete(key);
49
+ }
50
+ }
51
+ }
52
+ /**
53
+ * Get cache statistics (for debugging)
54
+ */
55
+ getStats() {
56
+ return {
57
+ size: this.cache.size,
58
+ entries: Array.from(this.cache.keys())
59
+ };
60
+ }
61
+ }
62
+ const requestCache = new RequestCache();
63
+ function useRequestCache() {
64
+ return {
65
+ fetch: requestCache.fetch.bind(requestCache),
66
+ clear: requestCache.clear.bind(requestCache),
67
+ invalidate: requestCache.invalidate.bind(requestCache),
68
+ getStats: requestCache.getStats.bind(requestCache)
69
+ };
70
+ }
71
+ function debounce(func, wait) {
72
+ let timeout = null;
73
+ return (...args) => {
74
+ if (timeout) {
75
+ clearTimeout(timeout);
76
+ }
77
+ timeout = setTimeout(() => {
78
+ func(...args);
79
+ }, wait);
80
+ };
81
+ }
82
+ const debouncedSetItem = debounce((key, value) => {
83
+ if (typeof window !== "undefined") {
84
+ localStorage.setItem(key, value);
85
+ }
86
+ }, 300);
87
+ const mapApiUserToFrontend = (apiUser) => {
88
+ return {
89
+ id: apiUser.id,
90
+ email: apiUser.email,
91
+ firstName: apiUser.first_name || apiUser.firstName || "",
92
+ lastName: apiUser.last_name || apiUser.lastName || "",
93
+ avatar: apiUser.avatar_url || apiUser.avatar,
94
+ mfaEnabled: apiUser.mfa_enabled ?? apiUser.mfaEnabled ?? false,
95
+ emailVerified: apiUser.email_verified ?? apiUser.emailVerified ?? false,
96
+ passwordUpdatedAt: apiUser.password_updated_at || apiUser.passwordUpdatedAt,
97
+ settings: apiUser.settings || {},
98
+ xp: apiUser.xp || 0,
99
+ level: apiUser.level || 1,
100
+ next_level_xp: apiUser.next_level_xp || apiUser.next_level_xp || 4,
101
+ username: apiUser.username,
102
+ usernameLastChangedAt: apiUser.username_last_changed_at || apiUser.usernameLastChangedAt,
103
+ createdAt: apiUser.created_at || apiUser.createdAt,
104
+ updatedAt: apiUser.updated_at || apiUser.updatedAt || (/* @__PURE__ */ new Date()).toISOString()
105
+ };
106
+ };
107
+ const globalState = {
108
+ currentUser: ref(null),
109
+ currentSession: ref(null),
110
+ loadingStates: ref({
111
+ initializing: true,
112
+ signingIn: false,
113
+ signingUp: false,
114
+ signingOut: false,
115
+ refreshingToken: false,
116
+ sendingMfaEmail: false,
117
+ verifyingMfa: false,
118
+ loadingProfile: false
119
+ }),
120
+ isInitialized: ref(false),
121
+ mfaRequired: ref(false),
122
+ mfaSessionId: ref(null),
123
+ availableMfaMethods: ref([])
124
+ };
30
125
  let refreshTimer = null;
126
+ let refreshPromise = null;
31
127
  function useStrandsAuth() {
128
+ const { getUrl } = useStrandsConfig();
129
+ const { fetch: cachedFetch, clear: clearCache, invalidate } = useRequestCache();
130
+ const { currentUser, currentSession, loadingStates, isInitialized, mfaRequired, mfaSessionId, availableMfaMethods } = globalState;
131
+ const isInitializing = computed(() => loadingStates.value.initializing);
132
+ const isSigningIn = computed(() => loadingStates.value.signingIn);
133
+ const isSigningUp = computed(() => loadingStates.value.signingUp);
134
+ const isSigningOut = computed(() => loadingStates.value.signingOut);
135
+ const isRefreshingToken = computed(() => loadingStates.value.refreshingToken);
136
+ const isSendingMfaEmail = computed(() => loadingStates.value.sendingMfaEmail);
137
+ const isVerifyingMfa = computed(() => loadingStates.value.verifyingMfa);
138
+ computed(() => loadingStates.value.loadingProfile);
139
+ const loading = computed(
140
+ () => loadingStates.value.signingIn || loadingStates.value.signingUp || loadingStates.value.signingOut || loadingStates.value.refreshingToken || loadingStates.value.sendingMfaEmail || loadingStates.value.verifyingMfa || loadingStates.value.loadingProfile
141
+ );
142
+ const isLoading = computed(() => loadingStates.value.initializing || loading.value);
143
+ const loadingMessage = computed(() => {
144
+ const states = loadingStates.value;
145
+ if (states.initializing) return "Checking authentication...";
146
+ if (states.signingIn) return "Signing you in...";
147
+ if (states.signingUp) return "Creating your account...";
148
+ if (states.signingOut) return "Signing you out...";
149
+ if (states.refreshingToken) return "Refreshing session...";
150
+ if (states.sendingMfaEmail) return "Sending verification code...";
151
+ if (states.verifyingMfa) return "Verifying code...";
152
+ if (states.loadingProfile) return "Loading profile...";
153
+ return "Loading...";
154
+ });
32
155
  const getAuthHeaders = () => {
33
156
  if (!currentSession.value?.accessToken) {
34
157
  throw new Error("No access token available");
35
158
  }
159
+ currentSession.value.expiresAt;
36
160
  const headers = {
37
161
  "Authorization": `Bearer ${currentSession.value.accessToken}`
38
162
  };
@@ -99,16 +223,20 @@ function useStrandsAuth() {
99
223
  };
100
224
  const isAuthenticated = computed(() => currentUser.value !== null);
101
225
  const signIn = async (credentials) => {
102
- isSigningIn.value = true;
226
+ loadingStates.value.signingIn = true;
103
227
  try {
104
228
  mfaRequired.value = false;
105
229
  mfaSessionId.value = null;
106
230
  availableMfaMethods.value = [];
231
+ const headers = {
232
+ "Content-Type": "application/json"
233
+ };
234
+ if (typeof window !== "undefined" && window.location) {
235
+ headers["Origin"] = window.location.origin;
236
+ }
107
237
  const response = await fetch(getUrl("signIn"), {
108
238
  method: "POST",
109
- headers: {
110
- "Content-Type": "application/json"
111
- },
239
+ headers,
112
240
  body: JSON.stringify(credentials)
113
241
  });
114
242
  if (!response.ok) {
@@ -146,29 +274,31 @@ function useStrandsAuth() {
146
274
  };
147
275
  });
148
276
  availableMfaMethods.value = methods;
149
- isSigningIn.value = false;
277
+ loadingStates.value.signingIn = false;
150
278
  return authData;
151
279
  }
152
- setAuthData(authData);
280
+ await setAuthData(authData);
153
281
  return authData;
154
282
  } catch (error) {
155
283
  throw error;
156
284
  } finally {
157
- isSigningIn.value = false;
285
+ loadingStates.value.signingIn = false;
158
286
  }
159
287
  };
160
288
  const signUp = async (userData) => {
161
- isSigningUp.value = true;
289
+ loadingStates.value.signingUp = true;
162
290
  try {
163
291
  throw new Error("Sign up not implemented - please integrate with auth SDK");
164
292
  } finally {
165
- isSigningUp.value = false;
293
+ loadingStates.value.signingUp = false;
166
294
  }
167
295
  };
168
296
  const signOut = async () => {
169
- isSigningOut.value = true;
297
+ loadingStates.value.signingOut = true;
170
298
  try {
171
299
  stopTokenRefreshTimer();
300
+ refreshPromise = null;
301
+ clearCache();
172
302
  currentUser.value = null;
173
303
  currentSession.value = null;
174
304
  mfaRequired.value = false;
@@ -179,102 +309,106 @@ function useStrandsAuth() {
179
309
  localStorage.removeItem("strands_auth_user");
180
310
  }
181
311
  } finally {
182
- isSigningOut.value = false;
312
+ loadingStates.value.signingOut = false;
183
313
  }
184
314
  };
185
315
  const refreshToken = async () => {
186
316
  if (!currentSession.value?.refreshToken) {
187
317
  return false;
188
318
  }
189
- isRefreshingToken.value = true;
190
- try {
191
- const response = await fetch(getUrl("refresh"), {
192
- method: "POST",
193
- headers: {
194
- "Content-Type": "application/json"
195
- },
196
- body: JSON.stringify({
197
- refresh_token: currentSession.value.refreshToken
198
- })
199
- });
200
- if (!response.ok) {
201
- if (response.status === 401) {
202
- await signOut();
203
- return false;
319
+ if (refreshPromise) {
320
+ console.log("Token refresh already in progress, waiting for completion...");
321
+ return await refreshPromise;
322
+ }
323
+ refreshPromise = (async () => {
324
+ loadingStates.value.refreshingToken = true;
325
+ try {
326
+ const response = await fetch(getUrl("refresh"), {
327
+ method: "POST",
328
+ headers: {
329
+ "Content-Type": "application/json"
330
+ },
331
+ body: JSON.stringify({
332
+ refresh_token: currentSession.value.refreshToken
333
+ })
334
+ });
335
+ if (!response.ok) {
336
+ if (response.status === 401) {
337
+ await signOut();
338
+ return false;
339
+ }
340
+ throw new Error(`Token refresh failed: ${response.status} ${response.statusText}`);
204
341
  }
205
- throw new Error(`Token refresh failed: ${response.status} ${response.statusText}`);
206
- }
207
- const tokenData = await response.json();
208
- const newSession = {
209
- accessToken: tokenData.access_token,
210
- refreshToken: tokenData.refresh_token,
211
- expiresAt: new Date(Date.now() + 5 * 60 * 1e3),
212
- // 5 minutes from now
213
- userId: currentUser.value?.id
214
- };
215
- currentSession.value = newSession;
216
- if (typeof window !== "undefined") {
217
- localStorage.setItem("strands_auth_session", JSON.stringify(newSession));
342
+ const tokenData = await response.json();
343
+ if (tokenData.user) {
344
+ currentUser.value = mapApiUserToFrontend(tokenData.user);
345
+ if (currentUser.value && typeof window !== "undefined") {
346
+ localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
347
+ }
348
+ }
349
+ const newSession = {
350
+ accessToken: tokenData.access_token,
351
+ refreshToken: tokenData.refresh_token,
352
+ expiresAt: new Date(Date.now() + 5 * 60 * 1e3),
353
+ // 5 minutes from now
354
+ userId: tokenData.user?.id || currentUser.value?.id
355
+ };
356
+ currentSession.value = newSession;
357
+ if (typeof window !== "undefined") {
358
+ localStorage.setItem("strands_auth_session", JSON.stringify(newSession));
359
+ }
360
+ startTokenRefreshTimer();
361
+ invalidate(`sessions:${currentSession.value.accessToken.slice(0, 20)}`);
362
+ return true;
363
+ } catch (error) {
364
+ await signOut();
365
+ return false;
366
+ } finally {
367
+ loadingStates.value.refreshingToken = false;
218
368
  }
219
- startTokenRefreshTimer();
220
- return true;
221
- } catch (error) {
222
- await signOut();
223
- return false;
224
- }
369
+ })();
370
+ const result = await refreshPromise;
371
+ refreshPromise = null;
372
+ return result;
225
373
  };
226
374
  const fetchProfile = async () => {
227
375
  if (!currentSession.value?.accessToken) {
228
376
  throw new Error("No access token available");
229
377
  }
230
- isLoadingProfile.value = true;
378
+ const cacheKey = `profile:${currentSession.value.accessToken.slice(0, 20)}`;
379
+ loadingStates.value.loadingProfile = true;
231
380
  try {
232
- const response = await fetch(getUrl("profile"), {
233
- method: "GET",
234
- headers: {
235
- "Content-Type": "application/json",
236
- "Authorization": `Bearer ${currentSession.value.accessToken}`
381
+ return await cachedFetch(cacheKey, async () => {
382
+ const response = await fetch(getUrl("profile"), {
383
+ method: "GET",
384
+ headers: {
385
+ "Content-Type": "application/json",
386
+ "Authorization": `Bearer ${currentSession.value?.accessToken}`
387
+ }
388
+ });
389
+ if (!response.ok) {
390
+ if (response.status === 401) {
391
+ throw new Error("Authentication expired. Please sign in again.");
392
+ } else {
393
+ throw new Error(`Failed to fetch profile: ${response.status} ${response.statusText}`);
394
+ }
237
395
  }
238
- });
239
- if (!response.ok) {
240
- if (response.status === 401) {
241
- throw new Error("Authentication expired. Please sign in again.");
242
- } else {
243
- throw new Error(`Failed to fetch profile: ${response.status} ${response.statusText}`);
396
+ const userData = await response.json();
397
+ currentUser.value = mapApiUserToFrontend(userData);
398
+ if (currentUser.value && typeof window !== "undefined") {
399
+ localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
244
400
  }
245
- }
246
- const userData = await response.json();
247
- currentUser.value = {
248
- id: userData.id,
249
- email: userData.email,
250
- firstName: userData.first_name,
251
- lastName: userData.last_name,
252
- avatar: userData.avatar_url,
253
- mfaEnabled: userData.mfaEnabled || false,
254
- emailVerified: userData.email_verified,
255
- passwordUpdatedAt: userData.password_updated_at,
256
- settings: userData.settings || {},
257
- xp: userData.xp || 0,
258
- level: userData.level || 1,
259
- next_level_xp: userData.next_level_xp || 4,
260
- username: userData.username,
261
- usernameLastChangedAt: userData.username_last_changed_at,
262
- createdAt: userData.created_at,
263
- updatedAt: userData.updated_at || (/* @__PURE__ */ new Date()).toISOString()
264
- };
265
- if (typeof window !== "undefined") {
266
- localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
267
- }
268
- return currentUser.value;
401
+ return currentUser.value;
402
+ });
269
403
  } finally {
270
- isLoadingProfile.value = false;
404
+ loadingStates.value.loadingProfile = false;
271
405
  }
272
406
  };
273
407
  const updateProfile = async (profileData) => {
274
408
  if (!currentSession.value?.accessToken) {
275
409
  throw new Error("No access token available");
276
410
  }
277
- isLoadingProfile.value = true;
411
+ loadingStates.value.loadingProfile = true;
278
412
  try {
279
413
  const response = await fetch(getUrl("profile"), {
280
414
  method: "POST",
@@ -295,42 +429,20 @@ function useStrandsAuth() {
295
429
  }
296
430
  }
297
431
  const updatedUserData = await response.json();
432
+ currentUser.value = mapApiUserToFrontend(updatedUserData);
298
433
  if (currentUser.value) {
299
- currentUser.value = {
300
- ...currentUser.value,
301
- // Update with API response data, preserving the API field names
302
- id: updatedUserData.id,
303
- email: updatedUserData.email,
304
- firstName: updatedUserData.first_name,
305
- lastName: updatedUserData.last_name,
306
- emailVerified: updatedUserData.email_verified,
307
- avatar: updatedUserData.avatar_url,
308
- mfaEnabled: updatedUserData.mfa_enabled,
309
- xp: updatedUserData.xp,
310
- level: updatedUserData.level,
311
- next_level_xp: updatedUserData.next_level_xp,
312
- username: updatedUserData.username,
313
- // This was missing and causing the bug!
314
- usernameLastChangedAt: updatedUserData.username_last_changed_at,
315
- createdAt: updatedUserData.created_at,
316
- updatedAt: updatedUserData.updated_at,
317
- settings: updatedUserData.settings,
318
- passwordUpdatedAt: updatedUserData.password_updated_at
319
- };
320
- if (typeof window !== "undefined") {
321
- localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
322
- }
434
+ debouncedSetItem("strands_auth_user", JSON.stringify(currentUser.value));
323
435
  }
324
436
  return currentUser.value;
325
437
  } finally {
326
- isLoadingProfile.value = false;
438
+ loadingStates.value.loadingProfile = false;
327
439
  }
328
440
  };
329
441
  const updateUserSettings = async (settings) => {
330
442
  if (!currentSession.value?.accessToken) {
331
443
  throw new Error("No access token available");
332
444
  }
333
- isLoadingProfile.value = true;
445
+ loadingStates.value.loadingProfile = true;
334
446
  try {
335
447
  const response = await fetch(getUrl("settings"), {
336
448
  method: "POST",
@@ -350,26 +462,20 @@ function useStrandsAuth() {
350
462
  }
351
463
  }
352
464
  const updatedUserData = await response.json();
465
+ currentUser.value = mapApiUserToFrontend(updatedUserData);
353
466
  if (currentUser.value) {
354
- currentUser.value = {
355
- ...currentUser.value,
356
- settings: updatedUserData.settings,
357
- updatedAt: updatedUserData.updated_at || (/* @__PURE__ */ new Date()).toISOString()
358
- };
359
- if (typeof window !== "undefined") {
360
- localStorage.setItem("strands_auth_user", JSON.stringify(currentUser.value));
361
- }
467
+ debouncedSetItem("strands_auth_user", JSON.stringify(currentUser.value));
362
468
  }
363
469
  return currentUser.value;
364
470
  } finally {
365
- isLoadingProfile.value = false;
471
+ loadingStates.value.loadingProfile = false;
366
472
  }
367
473
  };
368
474
  const changeEmail = async (newEmail, password) => {
369
475
  if (!currentSession.value?.accessToken) {
370
476
  throw new Error("No access token available");
371
477
  }
372
- isLoadingProfile.value = true;
478
+ loadingStates.value.loadingProfile = true;
373
479
  try {
374
480
  const response = await fetch(getUrl("changeEmail"), {
375
481
  method: "POST",
@@ -405,14 +511,14 @@ function useStrandsAuth() {
405
511
  }
406
512
  return result;
407
513
  } finally {
408
- isLoadingProfile.value = false;
514
+ loadingStates.value.loadingProfile = false;
409
515
  }
410
516
  };
411
517
  const verifyMfa = async (deviceId, code, isBackupCode = false) => {
412
518
  if (!mfaSessionId.value) {
413
519
  throw new Error("No MFA session available");
414
520
  }
415
- isVerifyingMfa.value = true;
521
+ loadingStates.value.verifyingMfa = true;
416
522
  try {
417
523
  const endpoint = isBackupCode ? getUrl("mfaBackupCodeVerify") : getUrl("mfaSigninVerify");
418
524
  const body = isBackupCode ? { mfa_session_id: mfaSessionId.value, backup_code: code } : { mfa_session_id: mfaSessionId.value, device_id: deviceId, code };
@@ -436,17 +542,17 @@ function useStrandsAuth() {
436
542
  mfaRequired.value = false;
437
543
  mfaSessionId.value = null;
438
544
  availableMfaMethods.value = [];
439
- setAuthData(authData);
545
+ await setAuthData(authData);
440
546
  return authData;
441
547
  } finally {
442
- isVerifyingMfa.value = false;
548
+ loadingStates.value.verifyingMfa = false;
443
549
  }
444
550
  };
445
551
  const sendMfaEmailCode = async (deviceId) => {
446
552
  if (!mfaSessionId.value) {
447
553
  throw new Error("No MFA session available");
448
554
  }
449
- isSendingMfaEmail.value = true;
555
+ loadingStates.value.sendingMfaEmail = true;
450
556
  try {
451
557
  const response = await fetch(getUrl("mfaSigninSendEmail"), {
452
558
  method: "POST",
@@ -470,7 +576,7 @@ function useStrandsAuth() {
470
576
  const result = await response.json();
471
577
  return result;
472
578
  } finally {
473
- isSendingMfaEmail.value = false;
579
+ loadingStates.value.sendingMfaEmail = false;
474
580
  }
475
581
  };
476
582
  const getMfaWebAuthnChallenge = async (deviceId) => {
@@ -498,10 +604,10 @@ function useStrandsAuth() {
498
604
  }
499
605
  return response.json();
500
606
  };
501
- const setAuthData = (authResponse) => {
607
+ const setAuthData = async (authResponse) => {
502
608
  try {
503
609
  if (authResponse.user) {
504
- currentUser.value = authResponse.user;
610
+ currentUser.value = mapApiUserToFrontend(authResponse.user);
505
611
  }
506
612
  const session = {
507
613
  accessToken: authResponse.access_token,
@@ -519,7 +625,6 @@ function useStrandsAuth() {
519
625
  }
520
626
  startTokenRefreshTimer();
521
627
  } catch (error) {
522
- console.error("Error setting auth data:", error);
523
628
  }
524
629
  };
525
630
  const startTokenRefreshTimer = () => {
@@ -527,6 +632,9 @@ function useStrandsAuth() {
527
632
  clearTimeout(refreshTimer);
528
633
  }
529
634
  if (!currentSession.value) return;
635
+ if (typeof document !== "undefined" && document.visibilityState === "hidden") {
636
+ return;
637
+ }
530
638
  const now = /* @__PURE__ */ new Date();
531
639
  const expiresAt = currentSession.value.expiresAt;
532
640
  const timeUntilRefresh = expiresAt.getTime() - now.getTime() - 1 * 60 * 1e3;
@@ -535,9 +643,11 @@ function useStrandsAuth() {
535
643
  return;
536
644
  }
537
645
  refreshTimer = setTimeout(async () => {
538
- const success = await refreshToken();
539
- if (success) {
540
- startTokenRefreshTimer();
646
+ if (typeof document === "undefined" || document.visibilityState === "visible") {
647
+ const success = await refreshToken();
648
+ if (success) {
649
+ startTokenRefreshTimer();
650
+ }
541
651
  }
542
652
  }, timeUntilRefresh);
543
653
  };
@@ -549,7 +659,7 @@ function useStrandsAuth() {
549
659
  };
550
660
  const initialize = async () => {
551
661
  if (isInitialized.value) return;
552
- isInitializing.value = true;
662
+ loadingStates.value.initializing = true;
553
663
  try {
554
664
  if (typeof window !== "undefined") {
555
665
  const storedSession = localStorage.getItem("strands_auth_session");
@@ -577,14 +687,14 @@ function useStrandsAuth() {
577
687
  await new Promise((resolve) => setTimeout(resolve, 50));
578
688
  } catch (error) {
579
689
  } finally {
580
- isInitializing.value = false;
690
+ loadingStates.value.initializing = false;
581
691
  }
582
692
  };
583
693
  const changeUsername = async (newUsername) => {
584
694
  if (!currentSession.value?.accessToken) {
585
695
  throw new Error("No access token available");
586
696
  }
587
- isLoadingProfile.value = true;
697
+ loadingStates.value.loadingProfile = true;
588
698
  try {
589
699
  const response = await fetch(getUrl("changeUsername"), {
590
700
  method: "POST",
@@ -619,7 +729,7 @@ function useStrandsAuth() {
619
729
  }
620
730
  return result;
621
731
  } finally {
622
- isLoadingProfile.value = false;
732
+ loadingStates.value.loadingProfile = false;
623
733
  }
624
734
  };
625
735
  const getUsernameCooldown = async () => {
@@ -651,14 +761,26 @@ function useStrandsAuth() {
651
761
  return response.json();
652
762
  };
653
763
  const getUserSessions = async () => {
654
- const response = await fetch(getUrl("sessions"), {
655
- method: "GET",
656
- headers: getAuthHeaders()
657
- });
658
- if (!response.ok) {
659
- throw new Error(`Failed to get user sessions: ${response.status} ${response.statusText}`);
764
+ if (!currentSession.value?.accessToken) {
765
+ throw new Error("No access token available");
766
+ }
767
+ const cacheKey = `sessions:${currentSession.value.accessToken.slice(0, 20)}`;
768
+ try {
769
+ return await cachedFetch(cacheKey, async () => {
770
+ const headers = getAuthHeaders();
771
+ const response = await fetch(getUrl("sessions"), {
772
+ method: "GET",
773
+ headers
774
+ });
775
+ if (!response.ok) {
776
+ const errorText = await response.text();
777
+ throw new Error(`Failed to get user sessions: ${response.status} ${response.statusText}`);
778
+ }
779
+ return response.json();
780
+ }, 2 * 60 * 1e3);
781
+ } catch (error) {
782
+ throw error;
660
783
  }
661
- return response.json();
662
784
  };
663
785
  const getSessionStats = async () => {
664
786
  const response = await fetch(getUrl("sessionsStats"), {
@@ -691,6 +813,27 @@ function useStrandsAuth() {
691
813
  }
692
814
  return response.status === 200;
693
815
  };
816
+ if (typeof document !== "undefined") {
817
+ document.addEventListener("visibilitychange", () => {
818
+ if (document.visibilityState === "visible" && currentSession.value) {
819
+ startTokenRefreshTimer();
820
+ } else if (document.visibilityState === "hidden") {
821
+ stopTokenRefreshTimer();
822
+ }
823
+ });
824
+ }
825
+ const cleanup = () => {
826
+ stopTokenRefreshTimer();
827
+ clearCache();
828
+ if (typeof document !== "undefined") {
829
+ document.removeEventListener("visibilitychange", () => {
830
+ });
831
+ }
832
+ };
833
+ try {
834
+ onUnmounted(cleanup);
835
+ } catch (e) {
836
+ }
694
837
  if (!isInitialized.value) {
695
838
  initialize();
696
839
  }
@@ -745,7 +888,7 @@ function useStrandsAuth() {
745
888
  // Force re-initialization (useful for testing or navigation)
746
889
  forceReInit: () => {
747
890
  isInitialized.value = false;
748
- isInitializing.value = true;
891
+ loadingStates.value.initializing = true;
749
892
  initialize();
750
893
  }
751
894
  };
@@ -753,4 +896,3 @@ function useStrandsAuth() {
753
896
  export {
754
897
  useStrandsAuth as u
755
898
  };
756
- //# sourceMappingURL=useStrandsAuth-Beee__9G.js.map