@strands.gg/accui 2.7.1 → 2.7.2

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 (59) hide show
  1. package/dist/accui.css +1 -0
  2. package/dist/index.cjs.js +1 -0
  3. package/dist/index.d.ts +7 -0
  4. package/dist/index.es.js +40821 -0
  5. package/dist/nuxt/module.cjs.js +1 -0
  6. package/dist/nuxt/module.d.ts +5 -0
  7. package/dist/nuxt/module.es.js +193 -0
  8. package/dist/nuxt/runtime/composables/useAuthenticatedFetch.cjs.js +1 -0
  9. package/dist/nuxt/runtime/composables/useAuthenticatedFetch.es.js +134 -0
  10. package/dist/nuxt/runtime/composables/useStrandsAuth.cjs.js +1 -0
  11. package/dist/nuxt/runtime/composables/useStrandsAuth.es.js +56 -0
  12. package/dist/nuxt/runtime/middleware/auth.global.cjs.js +1 -0
  13. package/dist/nuxt/runtime/middleware/auth.global.es.js +54 -0
  14. package/dist/nuxt/runtime/plugin.client.cjs.js +1 -0
  15. package/dist/nuxt/runtime/plugin.client.es.js +26 -0
  16. package/dist/nuxt/runtime/plugin.server.cjs.js +1 -0
  17. package/dist/nuxt/runtime/plugin.server.es.js +17 -0
  18. package/dist/nuxt/runtime/plugins/auth-interceptor.client.cjs.js +1 -0
  19. package/dist/nuxt/runtime/plugins/auth-interceptor.client.es.js +77 -0
  20. package/dist/nuxt.cjs.js +1 -0
  21. package/dist/nuxt.d.ts +2 -0
  22. package/dist/nuxt.es.js +11 -0
  23. package/dist/types/index.d.ts +292 -1
  24. package/dist/useStrandsAuth-BqNW7g0R.cjs.js +1 -0
  25. package/dist/useStrandsAuth-GX6lPddh.es.js +913 -0
  26. package/dist/useStrandsConfig-BUmt8HfH.cjs.js +1 -0
  27. package/dist/useStrandsConfig-f200kXFG.es.js +238 -0
  28. package/package.json +1 -1
  29. package/dist/shared/defaults.d.ts +0 -1
  30. package/dist/utils/colors.d.ts +0 -1
  31. package/dist/utils/index.d.ts +0 -1
  32. package/dist/utils/slots.d.ts +0 -1
  33. package/dist/utils/validation.d.ts +0 -1
  34. package/dist/vue/components/StrandsNav/index.d.ts +0 -1
  35. package/dist/vue/components/icons/index.d.ts +0 -1
  36. package/dist/vue/components/index.d.ts +0 -1
  37. package/dist/vue/composables/useAuthenticatedFetch.d.ts +0 -1
  38. package/dist/vue/composables/useDarkMode.d.ts +0 -1
  39. package/dist/vue/composables/useFloatingPosition.d.ts +0 -1
  40. package/dist/vue/composables/useModalStack.d.ts +0 -1
  41. package/dist/vue/composables/useOAuthProviders.d.ts +0 -1
  42. package/dist/vue/composables/useStrandsAuth.d.ts +0 -1
  43. package/dist/vue/composables/useStrandsConfig.d.ts +0 -1
  44. package/dist/vue/composables/useStrandsMfa.d.ts +0 -1
  45. package/dist/vue/composables/useTheme.d.ts +0 -1
  46. package/dist/vue/index.d.ts +0 -1
  47. package/dist/vue/plugins/StrandsUIPlugin.d.ts +0 -1
  48. package/dist/vue/ui/UiButton/index.d.ts +0 -1
  49. package/dist/vue/ui/index.d.ts +0 -1
  50. package/dist/vue/utils/contrast.d.ts +0 -1
  51. package/dist/vue/utils/debounce.d.ts +0 -1
  52. package/dist/vue/utils/fontPreloader.d.ts +0 -1
  53. package/dist/vue/utils/iconProps.d.ts +0 -1
  54. package/dist/vue/utils/lazyComponents.d.ts +0 -1
  55. package/dist/vue/utils/levels.d.ts +0 -1
  56. package/dist/vue/utils/modalStack.d.ts +0 -1
  57. package/dist/vue/utils/performanceInit.d.ts +0 -1
  58. package/dist/vue/utils/requestCache.d.ts +0 -1
  59. package/dist/vue/utils/sounds.d.ts +0 -1
@@ -1 +1,292 @@
1
- export * from '../../../src/types/index.ts';
1
+ // Re-export types from shared-types when available
2
+ // export * from '@strands.gg/auth-types'
3
+
4
+ // Core authentication types
5
+ export interface User {
6
+ id: string
7
+ email: string
8
+ firstName: string
9
+ lastName: string
10
+ avatar?: string
11
+ mfaEnabled: boolean
12
+ emailVerified: boolean
13
+ passwordUpdatedAt?: string | Date
14
+ settings?: any
15
+ xp: number
16
+ level: number
17
+ next_level_xp: number
18
+ username?: string
19
+ usernameLastChangedAt?: string | Date
20
+ createdAt: string | Date
21
+ updatedAt: string | Date
22
+ }
23
+
24
+ // Authenticated fetch types
25
+ export interface AuthenticatedFetchOptions extends RequestInit {
26
+ /**
27
+ * Whether to automatically refresh token if the request fails with 401
28
+ * @default true
29
+ */
30
+ autoRefresh?: boolean
31
+
32
+ /**
33
+ * Whether to throw an error if user is not authenticated
34
+ * @default true
35
+ */
36
+ requireAuth?: boolean
37
+
38
+ /**
39
+ * Base URL for the API requests
40
+ * If not provided, will use the current origin
41
+ */
42
+ baseURL?: string
43
+ }
44
+
45
+ export interface Session {
46
+ id?: string
47
+ userId?: string
48
+ accessToken: string
49
+ refreshToken: string
50
+ expiresAt: Date
51
+ createdAt?: Date
52
+ }
53
+
54
+ // Enhanced session response type for session management
55
+ export interface SessionInfo {
56
+ id: string
57
+ device_name?: string
58
+ device_type?: string
59
+ ip_address?: string
60
+ country?: string
61
+ city?: string
62
+ application_domain?: string
63
+ application_name?: string
64
+ created_at: string | Date
65
+ last_activity_at?: string | Date
66
+ is_current: boolean
67
+ }
68
+
69
+ export interface SessionStats {
70
+ total_sessions: number
71
+ active_sessions: number
72
+ devices_by_type: Record<string, number>
73
+ unique_locations: string[]
74
+ }
75
+
76
+ export interface AuthConfig {
77
+ apiUrl: string
78
+ applicationId: string
79
+ publicKey: string
80
+ autoRefresh?: boolean
81
+ redirectUrl?: string
82
+ }
83
+
84
+ // Component prop types
85
+ export interface SignInCredentials {
86
+ email: string
87
+ password: string
88
+ }
89
+
90
+ export interface SignUpData {
91
+ email: string
92
+ password: string
93
+ firstName: string
94
+ lastName: string
95
+ }
96
+
97
+ // OAuth provider types
98
+ export interface OAuthProvider {
99
+ id: string
100
+ name: string
101
+ enabled: boolean
102
+ clientId?: string // Optional - may still be used for OAuth provider configuration
103
+ scopes?: string[]
104
+ }
105
+
106
+ // Configuration types for Nuxt modules
107
+ export interface StrandsAuthConfig {
108
+ /**
109
+ * Base URL for the Strands Auth API
110
+ * @default 'https://your-api.example.com'
111
+ */
112
+ baseUrl?: string
113
+
114
+ /**
115
+ * Client ID for authentication (optional - authentication is now based on domain)
116
+ */
117
+ clientId?: string
118
+
119
+ /**
120
+ * Primary accent color for the auth components
121
+ * @default '#EA00A8'
122
+ */
123
+ accentColor?: string
124
+
125
+ /**
126
+ * Default redirect URL after successful authentication
127
+ * @default '/'
128
+ */
129
+ redirectUrl?: string
130
+
131
+ /**
132
+ * URL to redirect to after successful sign in
133
+ * @default '/dashboard'
134
+ */
135
+ onSignInUrl?: string
136
+
137
+ /**
138
+ * URL to redirect to after successful sign out
139
+ * @default '/'
140
+ */
141
+ onSignOutUrl?: string
142
+
143
+ /**
144
+ * Enable automatic token refresh
145
+ * @default true
146
+ */
147
+ autoRefresh?: boolean
148
+
149
+ /**
150
+ * Token refresh interval in minutes
151
+ * @default 4
152
+ */
153
+ refreshInterval?: number
154
+
155
+ /**
156
+ * Pages that should redirect to sign in if user is not authenticated
157
+ * @default []
158
+ */
159
+ protectedRoutes?: string[]
160
+
161
+ /**
162
+ * Pages that should redirect away if user IS authenticated
163
+ * @default ['/auth', '/login', '/register']
164
+ */
165
+ guestOnlyRoutes?: string[]
166
+
167
+ /**
168
+ * Enable development mode (shows debug info)
169
+ * @default false
170
+ */
171
+ devMode?: boolean
172
+
173
+ /**
174
+ * Support email address for contact links
175
+ * @optional
176
+ */
177
+ supportEmail?: string
178
+
179
+ /**
180
+ * OAuth2 redirect URL for OAuth providers
181
+ * If not specified, will use {origin}/auth/callback
182
+ * @optional
183
+ */
184
+ oauth2RedirectUrl?: string
185
+
186
+ /**
187
+ * Automatically import CSS styles
188
+ * Set to false if you want to manually import styles or use custom styling
189
+ * @default true
190
+ */
191
+ styles?: boolean
192
+
193
+ /**
194
+ * Custom API endpoints
195
+ */
196
+ endpoints?: Partial<StrandsAuthEndpoints>
197
+
198
+ /**
199
+ * Enable squircle corners for UI components
200
+ * @default true
201
+ */
202
+ useSquircle?: boolean
203
+ }
204
+
205
+ export interface StrandsAuthEndpoints {
206
+ signIn: string
207
+ signUp: string
208
+ signOut: string
209
+ refresh: string
210
+ passwordReset: string
211
+ passwordResetConfirm: string
212
+ completeRegistration: string
213
+ profile: string
214
+ verifyEmail: string
215
+ oauthProviders: string
216
+ oauthProvider: string
217
+ changeEmail: string
218
+ avatar: string
219
+ settings: string
220
+ // Username endpoints
221
+ changeUsername: string
222
+ usernameCooldown: string
223
+ checkUsernameAvailability: string
224
+ // MFA endpoints
225
+ mfaDevices: string
226
+ mfaTotpSetup: string
227
+ mfaTotpVerify: string
228
+ mfaEmailSetup: string
229
+ mfaEmailSend: string
230
+ mfaEmailVerify: string
231
+ mfaDeviceDisable: string
232
+ mfaBackupCodes: string
233
+ // Hardware key endpoints
234
+ mfaHardwareStartRegistration: string
235
+ mfaHardwareCompleteRegistration: string
236
+ // MFA sign-in specific endpoints
237
+ mfaSigninSendEmail: string
238
+ mfaSigninVerify: string
239
+ mfaBackupCodeVerify: string
240
+ mfaWebAuthnChallenge: string
241
+ // Session management endpoints
242
+ sessions: string
243
+ sessionsStats: string
244
+ sessionRevoke: string
245
+ sessionsRevokeAll: string
246
+ }
247
+
248
+ // API Response types
249
+ export interface AuthResponse {
250
+ access_token: string
251
+ refresh_token: string
252
+ user: User
253
+ mfa_required?: boolean
254
+ mfa_session_id?: string
255
+ available_mfa_methods?: MfaDevice[]
256
+ }
257
+
258
+ // MFA Types
259
+ export type MfaDeviceType = 'totp' | 'email' | 'hardware' | 'passkey'
260
+
261
+ export interface MfaDevice {
262
+ id: string
263
+ device_type: MfaDeviceType
264
+ device_name: string
265
+ is_active: boolean
266
+ last_used_at?: string | Date
267
+ created_at: string | Date
268
+ }
269
+
270
+ export interface MfaDevicesResponse {
271
+ devices: MfaDevice[]
272
+ mfa_enabled: boolean
273
+ }
274
+
275
+ export interface TotpSetupResponse {
276
+ device_id: string
277
+ secret: string
278
+ qr_code_url: string
279
+ backup_codes: string[]
280
+ }
281
+
282
+ export interface BackupCodesResponse {
283
+ backup_codes: string[]
284
+ }
285
+
286
+ // MFA Error Response types
287
+ export interface MfaErrorResponse {
288
+ code: string
289
+ message: string
290
+ mfa_session_id?: string
291
+ available_methods?: string[]
292
+ }
@@ -0,0 +1 @@
1
+ "use strict";const e=require("vue"),t=require("./useStrandsConfig-BUmt8HfH.cjs.js"),n=new class{cache=new Map;DEFAULT_TTL=3e5;async fetch(e,t,n=this.DEFAULT_TTL){const a=Date.now(),i=this.cache.get(e);if(i&&a-i.timestamp<i.ttl)return i.promise;this.cleanExpired();const r=t().finally(()=>{setTimeout(()=>{this.cache.delete(e)},n)});return this.cache.set(e,{promise:r,timestamp:a,ttl:n}),r}clear(){this.cache.clear()}invalidate(e){this.cache.delete(e)}cleanExpired(){const e=Date.now();for(const[t,n]of this.cache.entries())e-n.timestamp>n.ttl&&this.cache.delete(t)}getStats(){return{size:this.cache.size,entries:Array.from(this.cache.keys())}}},a=function(){let e=null;return(...t)=>{e&&clearTimeout(e),e=setTimeout(()=>{((e,t)=>{"undefined"!=typeof window&&localStorage.setItem(e,t)})(...t)},300)}}(),i=e=>({id:e.id,email:e.email,firstName:e.first_name||e.firstName||"",lastName:e.last_name||e.lastName||"",avatar:e.avatar_url||e.avatar,mfaEnabled:e.mfa_enabled??e.mfaEnabled??0,emailVerified:e.email_verified??e.emailVerified??0,passwordUpdatedAt:e.password_updated_at||e.passwordUpdatedAt,settings:e.settings||{},xp:e.xp||0,level:e.level||1,next_level_xp:e.next_level_xp||e.next_level_xp||4,username:e.username,usernameLastChangedAt:e.username_last_changed_at||e.usernameLastChangedAt,createdAt:e.created_at||e.createdAt,updatedAt:e.updated_at||e.updatedAt||(new Date).toISOString()}),r={currentUser:e.ref(null),currentSession:e.ref(null),loadingStates:e.ref({initializing:1,signingIn:0,signingUp:0,signingOut:0,refreshingToken:0,sendingMfaEmail:0,verifyingMfa:0,loadingProfile:0}),isInitialized:e.ref(0),mfaRequired:e.ref(0),mfaSessionId:e.ref(null),availableMfaMethods:e.ref([])};let o=null,s=null;exports.useStrandsAuth=function(){const{getUrl:d,config:c}=t.useStrandsConfig(),{fetch:l,clear:u,invalidate:h}={fetch:n.fetch.bind(n),clear:n.clear.bind(n),invalidate:n.invalidate.bind(n),getStats:n.getStats.bind(n)},{currentUser:f,currentSession:w,loadingStates:y,isInitialized:g,mfaRequired:m,mfaSessionId:p,availableMfaMethods:S}=r,_=()=>{if(f.value=null,w.value=null,m.value=0,p.value=null,S.value=[],"undefined"!=typeof window&&(localStorage.removeItem("strands_auth_session"),localStorage.removeItem("strands_auth_user")),D(),s=null,u(),"undefined"!=typeof window&&c.value?.onSignOutUrl){const e=window.location.pathname+window.location.search,t=c.value.onSignOutUrl;e!==t&&(window.location.href=t)}},T=e.computed(()=>y.value.initializing),E=e.computed(()=>y.value.signingIn),O=e.computed(()=>y.value.signingUp),v=e.computed(()=>y.value.signingOut),A=e.computed(()=>y.value.refreshingToken),$=e.computed(()=>y.value.sendingMfaEmail),N=e.computed(()=>y.value.verifyingMfa);e.computed(()=>y.value.loadingProfile);const b=e.computed(()=>y.value.signingIn||y.value.signingUp||y.value.signingOut||y.value.refreshingToken||y.value.sendingMfaEmail||y.value.verifyingMfa||y.value.loadingProfile),k=e.computed(()=>y.value.initializing||b.value),C=e.computed(()=>{const e=y.value;return e.initializing?"Checking authentication...":e.signingIn?"Signing you in...":e.signingUp?"Creating your account...":e.signingOut?"Signing you out...":e.refreshingToken?"Refreshing session...":e.sendingMfaEmail?"Sending verification code...":e.verifyingMfa?"Verifying code...":e.loadingProfile?"Loading profile...":"Loading..."}),J=()=>{const e={};return w.value?.accessToken&&(e.Authorization=`Bearer ${w.value.accessToken}`),w.value?.refreshToken&&(e["x-refresh-token"]=w.value.refreshToken),e},P=e.computed(()=>null!==f.value),F=async()=>{if(!w.value?.refreshToken)return 0;if(s)return await s;s=(async()=>{y.value.refreshingToken=1;try{const e=await fetch(d("refresh"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:w.value.refreshToken})});if(!e.ok){if(401===e.status)return _(),0;throw new Error(`Token refresh failed: ${e.status} ${e.statusText}`)}const t=await e.json();t.user&&(f.value=i(t.user),f.value&&"undefined"!=typeof window&&localStorage.setItem("strands_auth_user",JSON.stringify(f.value)));const n={accessToken:t.access_token,refreshToken:t.refresh_token,expiresAt:new Date(Date.now()+3e5),userId:t.user?.id||f.value?.id};return w.value=n,"undefined"!=typeof window&&localStorage.setItem("strands_auth_session",JSON.stringify(n)),U(),h(`sessions:${w.value.accessToken.slice(0,20)}`),1}catch(e){return _(),0}finally{y.value.refreshingToken=0}})();const e=await s;return s=null,e},M=async e=>{try{e.user&&(f.value=i(e.user));const t={accessToken:e.access_token,refreshToken:e.refresh_token,expiresAt:new Date(Date.now()+3e5),userId:f.value?.id||e.user?.id};w.value=t,"undefined"!=typeof window&&(localStorage.setItem("strands_auth_session",JSON.stringify(t)),f.value&&localStorage.setItem("strands_auth_user",JSON.stringify(f.value))),U()}catch(t){}},U=()=>{if(o&&clearTimeout(o),!w.value)return;if("undefined"!=typeof document&&"hidden"===document.visibilityState)return;const e=new Date,t=w.value.expiresAt.getTime()-e.getTime()-6e4;t<=0?F():o=setTimeout(async()=>{"undefined"!=typeof document&&"visible"!==document.visibilityState||await F()&&U()},t)},D=()=>{o&&(clearTimeout(o),o=null)},I=async()=>{if(!g.value){y.value.initializing=1;try{if("undefined"!=typeof window){const t=localStorage.getItem("strands_auth_session"),n=localStorage.getItem("strands_auth_user");if(t&&n)try{const e=JSON.parse(t),a=JSON.parse(n);e.expiresAt=new Date(e.expiresAt),e.expiresAt<=new Date&&e.refreshToken?(w.value=e,f.value=a,await F()||_()):e.expiresAt>new Date?(w.value=e,f.value=a,U()):(localStorage.removeItem("strands_auth_session"),localStorage.removeItem("strands_auth_user"))}catch(e){localStorage.removeItem("strands_auth_session"),localStorage.removeItem("strands_auth_user")}}g.value=1,await new Promise(e=>setTimeout(e,50))}catch(e){}finally{y.value.initializing=0}}};"undefined"!=typeof document&&document.addEventListener("visibilitychange",()=>{"visible"===document.visibilityState&&w.value?(U(),j()):"hidden"===document.visibilityState&&D()}),"undefined"!=typeof window&&window.addEventListener("storage",e=>{"strands_auth_session"!==e.key&&"strands_auth_user"!==e.key||!e.newValue&&f.value&&_()});const j=()=>{if("undefined"!=typeof window&&f.value&&w.value){const e=localStorage.getItem("strands_auth_session"),t=localStorage.getItem("strands_auth_user");e&&t||_()}},R=()=>{D(),u()};try{e.onUnmounted(R)}catch(z){}return g.value||I(),{user:e.computed(()=>f.value),currentUser:e.computed(()=>f.value),currentSession:e.computed(()=>w.value),isAuthenticated:P,isLoading:e.computed(()=>k.value||!g.value),loading:e.computed(()=>b.value),loadingMessage:C,isInitializing:T,isSigningIn:E,isSigningUp:O,isSigningOut:v,isRefreshingToken:A,isSendingMfaEmail:$,isVerifyingMfa:N,mfaRequired:e.computed(()=>m.value),mfaSessionId:e.computed(()=>p.value),availableMfaMethods:e.computed(()=>S.value),signIn:async e=>{y.value.signingIn=1;try{m.value=0,p.value=null,S.value=[];const t={"Content-Type":"application/json"};"undefined"!=typeof window&&window.location&&(t.Origin=window.location.origin);const n=await fetch(d("signIn"),{method:"POST",headers:t,body:JSON.stringify(e)});if(!n.ok)throw 401===n.status?new Error("Invalid email or password"):403===n.status?new Error("Please verify your email address before signing in"):new Error(`Sign in failed: ${n.status} ${n.statusText}`);const a=await n.json();if(a.mfa_required){m.value=1,p.value=a.mfa_session_id||null;const e=(a.available_mfa_methods||[]).map(e=>{let t=`${e.device_type.charAt(0).toUpperCase()+e.device_type.slice(1)} Authentication`;return"hardware"===e.device_type?t=e.device_name||"Security Key":"totp"===e.device_type?t=e.device_name||"Authenticator App":"email"===e.device_type&&(t=e.device_name||"Email Verification"),{id:e.device_id,device_type:e.device_type,device_name:e.device_name||t,is_active:1,created_at:(new Date).toISOString(),last_used_at:e.last_used_at,credential_id:e.credential_id,device_info:e.device_info}});return S.value=e,y.value.signingIn=0,a}return await M(a),a}catch(t){throw t}finally{y.value.signingIn=0}},signUp:async e=>{y.value.signingUp=1;try{throw new Error("Sign up not implemented - please integrate with auth SDK")}finally{y.value.signingUp=0}},signOut:async()=>{y.value.signingOut=1;try{D(),s=null,u(),f.value=null,w.value=null,m.value=0,p.value=null,S.value=[],"undefined"!=typeof window&&(localStorage.removeItem("strands_auth_session"),localStorage.removeItem("strands_auth_user"))}finally{y.value.signingOut=0}},refreshToken:F,fetchProfile:async()=>{const e=`profile:${w.value.accessToken.slice(0,20)}`;y.value.loadingProfile=1;try{return await l(e,async()=>{const e=await fetch(d("profile"),{method:"GET",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value?.accessToken}`}});if(!e.ok)throw 401===e.status?new Error("Authentication expired. Please sign in again."):new Error(`Failed to fetch profile: ${e.status} ${e.statusText}`);const t=await e.json();return f.value=i(t),f.value&&"undefined"!=typeof window&&localStorage.setItem("strands_auth_user",JSON.stringify(f.value)),f.value})}finally{y.value.loadingProfile=0}},updateProfile:async e=>{y.value.loadingProfile=1;try{const t=await fetch(d("profile"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({first_name:e.firstName,last_name:e.lastName})});if(!t.ok)throw 401===t.status?new Error("Authentication expired. Please sign in again."):new Error(`Profile update failed: ${t.status} ${t.statusText}`);const n=await t.json();return f.value=i(n),f.value&&a("strands_auth_user",JSON.stringify(f.value)),f.value}finally{y.value.loadingProfile=0}},updateUserSettings:async e=>{y.value.loadingProfile=1;try{const t=await fetch(d("settings"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({settings:e})});if(!t.ok)throw 401===t.status?new Error("Authentication expired. Please sign in again."):new Error(`Settings update failed: ${t.status} ${t.statusText}`);const n=await t.json();return f.value=i(n),f.value&&a("strands_auth_user",JSON.stringify(f.value)),f.value}finally{y.value.loadingProfile=0}},changeEmail:async(e,t)=>{y.value.loadingProfile=1;try{const n=await fetch(d("changeEmail"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({new_email:e,password:t})});if(!n.ok){if(401===n.status)throw new Error("Authentication expired. Please sign in again.");{const e=await n.json().catch(()=>({}));throw new Error(e.message||`Email change failed: ${n.status} ${n.statusText}`)}}const a=await n.json();return f.value&&(f.value={...f.value,email:e,emailVerified:0,updatedAt:(new Date).toISOString()},"undefined"!=typeof window&&localStorage.setItem("strands_auth_user",JSON.stringify(f.value))),a}finally{y.value.loadingProfile=0}},changeUsername:async e=>{y.value.loadingProfile=1;try{const t=await fetch(d("changeUsername"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({username:e})});if(!t.ok){const e=await t.json().catch(()=>({}));if(409===t.status)throw new Error("Username is already taken");if(e.cooldown_end)throw new Error(`You can only change your username once every 30 days. You can change it again on ${new Date(e.cooldown_end).toLocaleDateString()}`);throw new Error(e.message||`Username change failed: ${t.status} ${t.statusText}`)}const n=await t.json();return f.value&&(f.value={...f.value,username:e,usernameLastChangedAt:(new Date).toISOString(),updatedAt:(new Date).toISOString()},"undefined"!=typeof window&&localStorage.setItem("strands_auth_user",JSON.stringify(f.value))),n}finally{y.value.loadingProfile=0}},getUsernameCooldown:async()=>{const e=await fetch(d("usernameCooldown"),{method:"GET",headers:{Authorization:`Bearer ${w.value.accessToken}`}});if(!e.ok)throw new Error(`Failed to get username cooldown: ${e.status} ${e.statusText}`);return e.json()},checkUsernameAvailability:async e=>{const t=d("checkUsernameAvailability").replace("{username}",encodeURIComponent(e)),n=await fetch(t,{method:"GET",headers:{"Content-Type":"application/json"}});if(!n.ok)throw new Error(`Failed to check username availability: ${n.status} ${n.statusText}`);return n.json()},getUserSessions:async()=>{const e=`sessions:${w.value?.accessToken?.slice(0,20)||"no-token"}`;try{return await l(e,async()=>{const e=J(),t=await fetch(d("sessions"),{method:"GET",headers:e});if(!t.ok)throw await t.text(),new Error(`Failed to get user sessions: ${t.status} ${t.statusText}`);return t.json()},12e4)}catch(t){throw t}},getSessionStats:async()=>{const e=await fetch(d("sessionsStats"),{method:"GET",headers:J()});if(!e.ok)throw new Error(`Failed to get session stats: ${e.status} ${e.statusText}`);return e.json()},revokeSession:async e=>{const t=d("sessionRevoke").replace("{session_id}",encodeURIComponent(e)),n=await fetch(t,{method:"POST",headers:J()});if(!n.ok)throw new Error(`Failed to revoke session: ${n.status} ${n.statusText}`);return 200===n.status},revokeAllOtherSessions:async()=>{const e=await fetch(d("sessionsRevokeAll"),{method:"POST",headers:J()});if(!e.ok)throw new Error(`Failed to revoke all other sessions: ${e.status} ${e.statusText}`);return 200===e.status},initialize:I,setAuthData:M,verifyMfa:async(e,t,n=0)=>{if(!p.value)throw new Error("No MFA session available");y.value.verifyingMfa=1;try{const a=d(n?"mfaBackupCodeVerify":"mfaSigninVerify"),i=n?{mfa_session_id:p.value,backup_code:t}:{mfa_session_id:p.value,device_id:e,code:t},r=await fetch(a,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(i)});if(!r.ok){const e=await r.text();let t="MFA verification failed";try{const n=JSON.parse(e);t=n.message||n.error||e}catch{t=e||"MFA verification failed"}throw new Error(t)}const o=await r.json();return m.value=0,p.value=null,S.value=[],await M(o),o}finally{y.value.verifyingMfa=0}},sendMfaEmailCode:async e=>{if(!p.value)throw new Error("No MFA session available");y.value.sendingMfaEmail=1;try{const t=await fetch(d("mfaSigninSendEmail"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mfa_session_id:p.value,device_id:e})});if(!t.ok){const e=await t.text();let n="Failed to send MFA email code";try{const t=JSON.parse(e);n=t.message||t.error||e}catch{n=e||"Failed to send MFA email code"}throw new Error(n)}return await t.json()}finally{y.value.sendingMfaEmail=0}},getMfaWebAuthnChallenge:async e=>{if(!p.value)throw new Error("No MFA session available");const t=await fetch(d("mfaWebAuthnChallenge"),{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({mfa_session_id:p.value,device_id:e})});if(!t.ok){const e=await t.text();let n="Failed to get WebAuthn challenge";try{const t=JSON.parse(e);n=t.message||t.error||e}catch{n=e||n}throw new Error(n)}return t.json()},registerHardwareKey:async(e,t,n="hardware")=>{const a=await fetch(d("mfaHardwareStartRegistration"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({device_name:e,device_type:n})});if(!a.ok){const e=await a.text();let t="Failed to start hardware key registration";try{const n=JSON.parse(e);t=n.message||n.error||e}catch{t=e||"Failed to start hardware key registration"}throw new Error(t)}return a.json()},completeHardwareKeyRegistration:async(e,t,n)=>{const a=await fetch(d("mfaHardwareCompleteRegistration"),{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${w.value.accessToken}`},body:JSON.stringify({device_id:e,credential:t})});if(!a.ok){const e=await a.text();let t="Failed to complete hardware key registration";try{const n=JSON.parse(e);t=n.message||n.error||e}catch{t=e||"Failed to complete hardware key registration"}throw new Error(t)}return a.json()},startTokenRefreshTimer:U,stopTokenRefreshTimer:D,getAuthHeaders:J,forceReInit:()=>{g.value=0,y.value.initializing=1,I()}}};