@bagelink/auth 1.4.56 → 1.4.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,6 +10,7 @@ A framework-agnostic authentication composable with Vue support.
10
10
  - Complete authentication flow (login, signup, password recovery, etc.)
11
11
  - Error handling
12
12
  - User management
13
+ - Event listeners for authentication state changes
13
14
 
14
15
  ## Installation
15
16
 
@@ -22,12 +23,23 @@ bun add @bagelink/auth
22
23
  ### Basic Setup
23
24
 
24
25
  ```typescript
25
- import { initAuth } from '@bagelink/auth'
26
+ import { initAuth, AuthState } from '@bagelink/auth'
26
27
 
27
28
  // Initialize with axios instance
28
29
  const auth = initAuth({
29
30
  axios: axiosInstance,
30
- errorHandler: (error) => console.error(error)
31
+ baseURL: 'https://api.example.com'
32
+ })
33
+
34
+ // Add event listeners for authentication state changes
35
+ auth.on(AuthState.LOGIN, () => {
36
+ console.log('User logged in!')
37
+ // Redirect to dashboard, update UI, etc.
38
+ })
39
+
40
+ auth.on(AuthState.LOGOUT, () => {
41
+ console.log('User logged out!')
42
+ // Redirect to login page, clear sensitive data, etc.
31
43
  })
32
44
  ```
33
45
 
@@ -73,6 +85,64 @@ export default {
73
85
  }
74
86
  ```
75
87
 
88
+ ### Event Listeners
89
+
90
+ You can listen to authentication state changes using the event system:
91
+
92
+ ```typescript
93
+ import { initAuth, AuthState } from '@bagelink/auth'
94
+
95
+ const auth = initAuth({
96
+ axios: axiosInstance,
97
+ baseURL: 'https://api.example.com'
98
+ })
99
+
100
+ // Listen to login events
101
+ auth.on(AuthState.LOGIN, () => {
102
+ router.push('/dashboard')
103
+ })
104
+
105
+ // Listen to logout events
106
+ auth.on(AuthState.LOGOUT, () => {
107
+ router.push('/login')
108
+ })
109
+
110
+ // Listen to signup events
111
+ auth.on(AuthState.SIGNUP, () => {
112
+ router.push('/welcome')
113
+ })
114
+
115
+ // Listen to password reset events
116
+ auth.on(AuthState.PASSWORD_RESET, () => {
117
+ console.log('Password has been reset')
118
+ })
119
+
120
+ // Listen to profile update events
121
+ auth.on(AuthState.PROFILE_UPDATE, () => {
122
+ console.log('Profile updated successfully')
123
+ })
124
+
125
+ // Listen to auth check events (when user is authenticated)
126
+ auth.on(AuthState.AUTH_CHECK, () => {
127
+ console.log('User authentication verified')
128
+ })
129
+ ```
130
+
131
+ #### Event Methods
132
+
133
+ - `auth.on(event, handler)`: Add an event listener
134
+ - `auth.off(event, handler)`: Remove a specific event listener
135
+ - `auth.removeAllListeners(event?)`: Remove all listeners for an event (or all events if no event specified)
136
+
137
+ #### Available Events
138
+
139
+ - `AuthState.LOGIN`: Fired when user successfully logs in
140
+ - `AuthState.LOGOUT`: Fired when user logs out
141
+ - `AuthState.SIGNUP`: Fired when user successfully signs up
142
+ - `AuthState.PASSWORD_RESET`: Fired when password is reset or updated
143
+ - `AuthState.PROFILE_UPDATE`: Fired when user profile is updated
144
+ - `AuthState.AUTH_CHECK`: Fired when user authentication is verified
145
+
76
146
  ### Vanilla JavaScript
77
147
 
78
148
  ```typescript
@@ -80,7 +150,7 @@ import { initAuth } from '@bagelink/auth'
80
150
 
81
151
  const auth = initAuth({
82
152
  axios: axiosInstance,
83
- errorHandler: (error) => console.error(error)
153
+ baseURL: 'https://api.example.com'
84
154
  })
85
155
 
86
156
  const { login, currentUser, getIsLoggedIn } = auth.useAuth()
package/dist/index.cjs CHANGED
@@ -16,6 +16,36 @@ function createAxiosInstance(baseURL = "") {
16
16
  }
17
17
  });
18
18
  }
19
+ class EventEmitter {
20
+ listeners = /* @__PURE__ */ new Map();
21
+ on(event, handler) {
22
+ if (!this.listeners.has(event)) {
23
+ this.listeners.set(event, /* @__PURE__ */ new Set());
24
+ }
25
+ this.listeners.get(event).add(handler);
26
+ }
27
+ off(event, handler) {
28
+ const eventListeners = this.listeners.get(event);
29
+ if (eventListeners) {
30
+ eventListeners.delete(handler);
31
+ }
32
+ }
33
+ emit(event) {
34
+ const eventListeners = this.listeners.get(event);
35
+ if (eventListeners) {
36
+ eventListeners.forEach((handler) => {
37
+ handler();
38
+ });
39
+ }
40
+ }
41
+ removeAllListeners(event) {
42
+ if (event) {
43
+ this.listeners.delete(event);
44
+ } else {
45
+ this.listeners.clear();
46
+ }
47
+ }
48
+ }
19
49
 
20
50
  class AuthApi {
21
51
  api;
@@ -96,7 +126,18 @@ class AuthApi {
96
126
  }
97
127
  }
98
128
 
129
+ var AuthState = /* @__PURE__ */ ((AuthState2) => {
130
+ AuthState2["LOGIN"] = "login";
131
+ AuthState2["LOGOUT"] = "logout";
132
+ AuthState2["SIGNUP"] = "signup";
133
+ AuthState2["PASSWORD_RESET"] = "password_reset";
134
+ AuthState2["PROFILE_UPDATE"] = "profile_update";
135
+ AuthState2["AUTH_CHECK"] = "auth_check";
136
+ return AuthState2;
137
+ })(AuthState || {});
138
+
99
139
  let authApi = null;
140
+ let eventEmitter = null;
100
141
  const currentUser = vue.ref({
101
142
  id: "",
102
143
  email: "",
@@ -112,7 +153,20 @@ function initAuth({
112
153
  if (!authApi) {
113
154
  authApi = new AuthApi(axios, baseURL);
114
155
  }
156
+ if (!eventEmitter) {
157
+ eventEmitter = new EventEmitter();
158
+ }
115
159
  return {
160
+ // Event listener methods
161
+ on(event, handler) {
162
+ eventEmitter.on(event, handler);
163
+ },
164
+ off(event, handler) {
165
+ eventEmitter.off(event, handler);
166
+ },
167
+ removeAllListeners(event) {
168
+ eventEmitter.removeAllListeners(event);
169
+ },
116
170
  install(app) {
117
171
  app.config.globalProperties.$auth = useAuth();
118
172
  }
@@ -127,6 +181,15 @@ function useAuth() {
127
181
  async function logout() {
128
182
  try {
129
183
  await authApi.logout();
184
+ currentUser.value = {
185
+ id: "",
186
+ email: "",
187
+ first_name: "",
188
+ last_name: "",
189
+ is_superuser: false,
190
+ is_active: false
191
+ };
192
+ eventEmitter.emit(AuthState.LOGOUT);
130
193
  } catch (error) {
131
194
  throw error;
132
195
  }
@@ -138,6 +201,7 @@ function useAuth() {
138
201
  credentials.password
139
202
  );
140
203
  await checkAuth();
204
+ eventEmitter.emit(AuthState.LOGIN);
141
205
  } catch (error) {
142
206
  throw error;
143
207
  }
@@ -147,6 +211,9 @@ function useAuth() {
147
211
  if (!getIsLoggedIn()) {
148
212
  const { data } = await authApi.getCurrentUser();
149
213
  currentUser.value = data;
214
+ if (getIsLoggedIn()) {
215
+ eventEmitter.emit(AuthState.AUTH_CHECK);
216
+ }
150
217
  }
151
218
  } catch (error) {
152
219
  return false;
@@ -160,6 +227,7 @@ function useAuth() {
160
227
  }
161
228
  const { data } = await authApi.signup(user);
162
229
  currentUser.value = data;
230
+ eventEmitter.emit(AuthState.SIGNUP);
163
231
  } catch (error) {
164
232
  throw error;
165
233
  }
@@ -174,6 +242,7 @@ function useAuth() {
174
242
  async function resetPassword(newPassword) {
175
243
  try {
176
244
  await authApi.resetPassword(newPassword);
245
+ eventEmitter.emit(AuthState.PASSWORD_RESET);
177
246
  } catch (error) {
178
247
  throw error;
179
248
  }
@@ -184,6 +253,7 @@ function useAuth() {
184
253
  throw new Error("Passwords do not match");
185
254
  }
186
255
  await authApi.updatePassword(form);
256
+ eventEmitter.emit(AuthState.PASSWORD_RESET);
187
257
  } catch (error) {
188
258
  throw error;
189
259
  }
@@ -192,6 +262,7 @@ function useAuth() {
192
262
  try {
193
263
  const { data } = await authApi.updateUserProfile(user);
194
264
  currentUser.value = { ...currentUser.value, ...data };
265
+ eventEmitter.emit(AuthState.PROFILE_UPDATE);
195
266
  } catch (error) {
196
267
  throw error;
197
268
  }
@@ -231,5 +302,6 @@ function useAuth() {
231
302
  }
232
303
 
233
304
  exports.AuthApi = AuthApi;
305
+ exports.AuthState = AuthState;
234
306
  exports.initAuth = initAuth;
235
307
  exports.useAuth = useAuth;
package/dist/index.d.cts CHANGED
@@ -24,6 +24,23 @@ interface UpdatePasswordForm {
24
24
  new_password: string;
25
25
  confirmNewPassword: string;
26
26
  }
27
+ declare enum AuthState {
28
+ LOGIN = "login",
29
+ LOGOUT = "logout",
30
+ SIGNUP = "signup",
31
+ PASSWORD_RESET = "password_reset",
32
+ PROFILE_UPDATE = "profile_update",
33
+ AUTH_CHECK = "auth_check"
34
+ }
35
+ type AuthEventHandler = () => void;
36
+ interface AuthEventMap {
37
+ [AuthState.LOGIN]: AuthEventHandler;
38
+ [AuthState.LOGOUT]: AuthEventHandler;
39
+ [AuthState.SIGNUP]: AuthEventHandler;
40
+ [AuthState.PASSWORD_RESET]: AuthEventHandler;
41
+ [AuthState.PROFILE_UPDATE]: AuthEventHandler;
42
+ [AuthState.AUTH_CHECK]: AuthEventHandler;
43
+ }
27
44
  interface Token {
28
45
  access_token: string;
29
46
  }
@@ -105,6 +122,9 @@ declare function initAuth({ axios, baseURL, }: {
105
122
  axios: AxiosInstance;
106
123
  baseURL?: string;
107
124
  }): {
125
+ on<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
126
+ off<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
127
+ removeAllListeners<K extends AuthState>(event?: K): void;
108
128
  install(app: App): void;
109
129
  };
110
130
  declare function useAuth(): {
@@ -140,5 +160,5 @@ declare function useAuth(): {
140
160
  deleteUser: (userId: string) => Promise<void>;
141
161
  };
142
162
 
143
- export { AuthApi, initAuth, useAuth };
144
- export type { CreateUserResponse, DeleteUserResponse, GetMeResponse, GetUserResponse, GetUsersResponse, LoginResponse, NewPassword, NewUser, PasswordRecovery, PasswordRecoveryResponse, ResetPasswordResponse, SanitizedUserList, SanitizedUserOut, SignupResponse, Token, UpdateMeResponse, UpdatePassword, UpdatePasswordForm, UpdatePasswordResponse, UpdateUserResponse, User, UserCreate, UserRegister, UserUpdate, UserUpdateMe };
163
+ export { AuthApi, AuthState, initAuth, useAuth };
164
+ export type { AuthEventHandler, AuthEventMap, CreateUserResponse, DeleteUserResponse, GetMeResponse, GetUserResponse, GetUsersResponse, LoginResponse, NewPassword, NewUser, PasswordRecovery, PasswordRecoveryResponse, ResetPasswordResponse, SanitizedUserList, SanitizedUserOut, SignupResponse, Token, UpdateMeResponse, UpdatePassword, UpdatePasswordForm, UpdatePasswordResponse, UpdateUserResponse, User, UserCreate, UserRegister, UserUpdate, UserUpdateMe };
package/dist/index.d.mts CHANGED
@@ -24,6 +24,23 @@ interface UpdatePasswordForm {
24
24
  new_password: string;
25
25
  confirmNewPassword: string;
26
26
  }
27
+ declare enum AuthState {
28
+ LOGIN = "login",
29
+ LOGOUT = "logout",
30
+ SIGNUP = "signup",
31
+ PASSWORD_RESET = "password_reset",
32
+ PROFILE_UPDATE = "profile_update",
33
+ AUTH_CHECK = "auth_check"
34
+ }
35
+ type AuthEventHandler = () => void;
36
+ interface AuthEventMap {
37
+ [AuthState.LOGIN]: AuthEventHandler;
38
+ [AuthState.LOGOUT]: AuthEventHandler;
39
+ [AuthState.SIGNUP]: AuthEventHandler;
40
+ [AuthState.PASSWORD_RESET]: AuthEventHandler;
41
+ [AuthState.PROFILE_UPDATE]: AuthEventHandler;
42
+ [AuthState.AUTH_CHECK]: AuthEventHandler;
43
+ }
27
44
  interface Token {
28
45
  access_token: string;
29
46
  }
@@ -105,6 +122,9 @@ declare function initAuth({ axios, baseURL, }: {
105
122
  axios: AxiosInstance;
106
123
  baseURL?: string;
107
124
  }): {
125
+ on<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
126
+ off<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
127
+ removeAllListeners<K extends AuthState>(event?: K): void;
108
128
  install(app: App): void;
109
129
  };
110
130
  declare function useAuth(): {
@@ -140,5 +160,5 @@ declare function useAuth(): {
140
160
  deleteUser: (userId: string) => Promise<void>;
141
161
  };
142
162
 
143
- export { AuthApi, initAuth, useAuth };
144
- export type { CreateUserResponse, DeleteUserResponse, GetMeResponse, GetUserResponse, GetUsersResponse, LoginResponse, NewPassword, NewUser, PasswordRecovery, PasswordRecoveryResponse, ResetPasswordResponse, SanitizedUserList, SanitizedUserOut, SignupResponse, Token, UpdateMeResponse, UpdatePassword, UpdatePasswordForm, UpdatePasswordResponse, UpdateUserResponse, User, UserCreate, UserRegister, UserUpdate, UserUpdateMe };
163
+ export { AuthApi, AuthState, initAuth, useAuth };
164
+ export type { AuthEventHandler, AuthEventMap, CreateUserResponse, DeleteUserResponse, GetMeResponse, GetUserResponse, GetUsersResponse, LoginResponse, NewPassword, NewUser, PasswordRecovery, PasswordRecoveryResponse, ResetPasswordResponse, SanitizedUserList, SanitizedUserOut, SignupResponse, Token, UpdateMeResponse, UpdatePassword, UpdatePasswordForm, UpdatePasswordResponse, UpdateUserResponse, User, UserCreate, UserRegister, UserUpdate, UserUpdateMe };
package/dist/index.d.ts CHANGED
@@ -24,6 +24,23 @@ interface UpdatePasswordForm {
24
24
  new_password: string;
25
25
  confirmNewPassword: string;
26
26
  }
27
+ declare enum AuthState {
28
+ LOGIN = "login",
29
+ LOGOUT = "logout",
30
+ SIGNUP = "signup",
31
+ PASSWORD_RESET = "password_reset",
32
+ PROFILE_UPDATE = "profile_update",
33
+ AUTH_CHECK = "auth_check"
34
+ }
35
+ type AuthEventHandler = () => void;
36
+ interface AuthEventMap {
37
+ [AuthState.LOGIN]: AuthEventHandler;
38
+ [AuthState.LOGOUT]: AuthEventHandler;
39
+ [AuthState.SIGNUP]: AuthEventHandler;
40
+ [AuthState.PASSWORD_RESET]: AuthEventHandler;
41
+ [AuthState.PROFILE_UPDATE]: AuthEventHandler;
42
+ [AuthState.AUTH_CHECK]: AuthEventHandler;
43
+ }
27
44
  interface Token {
28
45
  access_token: string;
29
46
  }
@@ -105,6 +122,9 @@ declare function initAuth({ axios, baseURL, }: {
105
122
  axios: AxiosInstance;
106
123
  baseURL?: string;
107
124
  }): {
125
+ on<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
126
+ off<K extends AuthState>(event: K, handler: AuthEventMap[K]): void;
127
+ removeAllListeners<K extends AuthState>(event?: K): void;
108
128
  install(app: App): void;
109
129
  };
110
130
  declare function useAuth(): {
@@ -140,5 +160,5 @@ declare function useAuth(): {
140
160
  deleteUser: (userId: string) => Promise<void>;
141
161
  };
142
162
 
143
- export { AuthApi, initAuth, useAuth };
144
- export type { CreateUserResponse, DeleteUserResponse, GetMeResponse, GetUserResponse, GetUsersResponse, LoginResponse, NewPassword, NewUser, PasswordRecovery, PasswordRecoveryResponse, ResetPasswordResponse, SanitizedUserList, SanitizedUserOut, SignupResponse, Token, UpdateMeResponse, UpdatePassword, UpdatePasswordForm, UpdatePasswordResponse, UpdateUserResponse, User, UserCreate, UserRegister, UserUpdate, UserUpdateMe };
163
+ export { AuthApi, AuthState, initAuth, useAuth };
164
+ export type { AuthEventHandler, AuthEventMap, CreateUserResponse, DeleteUserResponse, GetMeResponse, GetUserResponse, GetUsersResponse, LoginResponse, NewPassword, NewUser, PasswordRecovery, PasswordRecoveryResponse, ResetPasswordResponse, SanitizedUserList, SanitizedUserOut, SignupResponse, Token, UpdateMeResponse, UpdatePassword, UpdatePasswordForm, UpdatePasswordResponse, UpdateUserResponse, User, UserCreate, UserRegister, UserUpdate, UserUpdateMe };
package/dist/index.mjs CHANGED
@@ -10,6 +10,36 @@ function createAxiosInstance(baseURL = "") {
10
10
  }
11
11
  });
12
12
  }
13
+ class EventEmitter {
14
+ listeners = /* @__PURE__ */ new Map();
15
+ on(event, handler) {
16
+ if (!this.listeners.has(event)) {
17
+ this.listeners.set(event, /* @__PURE__ */ new Set());
18
+ }
19
+ this.listeners.get(event).add(handler);
20
+ }
21
+ off(event, handler) {
22
+ const eventListeners = this.listeners.get(event);
23
+ if (eventListeners) {
24
+ eventListeners.delete(handler);
25
+ }
26
+ }
27
+ emit(event) {
28
+ const eventListeners = this.listeners.get(event);
29
+ if (eventListeners) {
30
+ eventListeners.forEach((handler) => {
31
+ handler();
32
+ });
33
+ }
34
+ }
35
+ removeAllListeners(event) {
36
+ if (event) {
37
+ this.listeners.delete(event);
38
+ } else {
39
+ this.listeners.clear();
40
+ }
41
+ }
42
+ }
13
43
 
14
44
  class AuthApi {
15
45
  api;
@@ -90,7 +120,18 @@ class AuthApi {
90
120
  }
91
121
  }
92
122
 
123
+ var AuthState = /* @__PURE__ */ ((AuthState2) => {
124
+ AuthState2["LOGIN"] = "login";
125
+ AuthState2["LOGOUT"] = "logout";
126
+ AuthState2["SIGNUP"] = "signup";
127
+ AuthState2["PASSWORD_RESET"] = "password_reset";
128
+ AuthState2["PROFILE_UPDATE"] = "profile_update";
129
+ AuthState2["AUTH_CHECK"] = "auth_check";
130
+ return AuthState2;
131
+ })(AuthState || {});
132
+
93
133
  let authApi = null;
134
+ let eventEmitter = null;
94
135
  const currentUser = ref({
95
136
  id: "",
96
137
  email: "",
@@ -106,7 +147,20 @@ function initAuth({
106
147
  if (!authApi) {
107
148
  authApi = new AuthApi(axios, baseURL);
108
149
  }
150
+ if (!eventEmitter) {
151
+ eventEmitter = new EventEmitter();
152
+ }
109
153
  return {
154
+ // Event listener methods
155
+ on(event, handler) {
156
+ eventEmitter.on(event, handler);
157
+ },
158
+ off(event, handler) {
159
+ eventEmitter.off(event, handler);
160
+ },
161
+ removeAllListeners(event) {
162
+ eventEmitter.removeAllListeners(event);
163
+ },
110
164
  install(app) {
111
165
  app.config.globalProperties.$auth = useAuth();
112
166
  }
@@ -121,6 +175,15 @@ function useAuth() {
121
175
  async function logout() {
122
176
  try {
123
177
  await authApi.logout();
178
+ currentUser.value = {
179
+ id: "",
180
+ email: "",
181
+ first_name: "",
182
+ last_name: "",
183
+ is_superuser: false,
184
+ is_active: false
185
+ };
186
+ eventEmitter.emit(AuthState.LOGOUT);
124
187
  } catch (error) {
125
188
  throw error;
126
189
  }
@@ -132,6 +195,7 @@ function useAuth() {
132
195
  credentials.password
133
196
  );
134
197
  await checkAuth();
198
+ eventEmitter.emit(AuthState.LOGIN);
135
199
  } catch (error) {
136
200
  throw error;
137
201
  }
@@ -141,6 +205,9 @@ function useAuth() {
141
205
  if (!getIsLoggedIn()) {
142
206
  const { data } = await authApi.getCurrentUser();
143
207
  currentUser.value = data;
208
+ if (getIsLoggedIn()) {
209
+ eventEmitter.emit(AuthState.AUTH_CHECK);
210
+ }
144
211
  }
145
212
  } catch (error) {
146
213
  return false;
@@ -154,6 +221,7 @@ function useAuth() {
154
221
  }
155
222
  const { data } = await authApi.signup(user);
156
223
  currentUser.value = data;
224
+ eventEmitter.emit(AuthState.SIGNUP);
157
225
  } catch (error) {
158
226
  throw error;
159
227
  }
@@ -168,6 +236,7 @@ function useAuth() {
168
236
  async function resetPassword(newPassword) {
169
237
  try {
170
238
  await authApi.resetPassword(newPassword);
239
+ eventEmitter.emit(AuthState.PASSWORD_RESET);
171
240
  } catch (error) {
172
241
  throw error;
173
242
  }
@@ -178,6 +247,7 @@ function useAuth() {
178
247
  throw new Error("Passwords do not match");
179
248
  }
180
249
  await authApi.updatePassword(form);
250
+ eventEmitter.emit(AuthState.PASSWORD_RESET);
181
251
  } catch (error) {
182
252
  throw error;
183
253
  }
@@ -186,6 +256,7 @@ function useAuth() {
186
256
  try {
187
257
  const { data } = await authApi.updateUserProfile(user);
188
258
  currentUser.value = { ...currentUser.value, ...data };
259
+ eventEmitter.emit(AuthState.PROFILE_UPDATE);
189
260
  } catch (error) {
190
261
  throw error;
191
262
  }
@@ -224,4 +295,4 @@ function useAuth() {
224
295
  };
225
296
  }
226
297
 
227
- export { AuthApi, initAuth, useAuth };
298
+ export { AuthApi, AuthState, initAuth, useAuth };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/auth",
3
3
  "type": "module",
4
- "version": "1.4.56",
4
+ "version": "1.4.64",
5
5
  "description": "Bagelink auth package",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export * from './api'
2
2
  export * from './types'
3
+ export { AuthState } from './types'
3
4
  export * from './useAuth'
package/src/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ /* eslint-disable no-unused-vars */
1
2
  import type { AxiosResponse } from 'axios'
2
3
 
3
4
  export interface User {
@@ -26,6 +27,25 @@ export interface UpdatePasswordForm {
26
27
  confirmNewPassword: string
27
28
  }
28
29
 
30
+ export enum AuthState {
31
+ LOGIN = 'login',
32
+ LOGOUT = 'logout',
33
+ SIGNUP = 'signup',
34
+ PASSWORD_RESET = 'password_reset',
35
+ PROFILE_UPDATE = 'profile_update',
36
+ AUTH_CHECK = 'auth_check',
37
+ }
38
+
39
+ export type AuthEventHandler = () => void
40
+ export interface AuthEventMap {
41
+ [AuthState.LOGIN]: AuthEventHandler
42
+ [AuthState.LOGOUT]: AuthEventHandler
43
+ [AuthState.SIGNUP]: AuthEventHandler
44
+ [AuthState.PASSWORD_RESET]: AuthEventHandler
45
+ [AuthState.PROFILE_UPDATE]: AuthEventHandler
46
+ [AuthState.AUTH_CHECK]: AuthEventHandler
47
+ }
48
+
29
49
  // API Response Types
30
50
  export interface Token {
31
51
  access_token: string
package/src/useAuth.ts CHANGED
@@ -1,11 +1,14 @@
1
1
  import type { AxiosInstance } from 'axios'
2
2
  import type { App } from 'vue'
3
- import type { User, NewUser, UpdatePasswordForm } from './types'
3
+ import type { User, NewUser, UpdatePasswordForm, AuthEventMap } from './types'
4
4
  import { ref } from 'vue'
5
5
  import { AuthApi } from './api'
6
+ import { AuthState } from './types'
7
+ import { EventEmitter } from './utils'
6
8
 
7
9
  // Global state
8
10
  let authApi: AuthApi | null = null
11
+ let eventEmitter: EventEmitter | null = null
9
12
  const currentUser = ref<User>({
10
13
  id: '',
11
14
  email: '',
@@ -27,7 +30,24 @@ export function initAuth({
27
30
  authApi = new AuthApi(axios, baseURL)
28
31
  }
29
32
 
33
+ if (!eventEmitter) {
34
+ eventEmitter = new EventEmitter()
35
+ }
36
+
30
37
  return {
38
+ // Event listener methods
39
+ on<K extends AuthState>(event: K, handler: AuthEventMap[K]): void {
40
+ eventEmitter!.on(event, handler)
41
+ },
42
+
43
+ off<K extends AuthState>(event: K, handler: AuthEventMap[K]): void {
44
+ eventEmitter!.off(event, handler)
45
+ },
46
+
47
+ removeAllListeners<K extends AuthState>(event?: K): void {
48
+ eventEmitter!.removeAllListeners(event)
49
+ },
50
+
31
51
  install(app: App) {
32
52
  // Make auth available globally
33
53
  app.config.globalProperties.$auth = useAuth()
@@ -49,6 +69,16 @@ export function useAuth() {
49
69
  async function logout() {
50
70
  try {
51
71
  await authApi!.logout()
72
+ // Reset current user
73
+ currentUser.value = {
74
+ id: '',
75
+ email: '',
76
+ first_name: '',
77
+ last_name: '',
78
+ is_superuser: false,
79
+ is_active: false,
80
+ }
81
+ eventEmitter!.emit(AuthState.LOGOUT)
52
82
  } catch (error) {
53
83
  throw error
54
84
  }
@@ -61,6 +91,7 @@ export function useAuth() {
61
91
  credentials.password
62
92
  )
63
93
  await checkAuth()
94
+ eventEmitter!.emit(AuthState.LOGIN)
64
95
  } catch (error) {
65
96
  throw error
66
97
  }
@@ -71,6 +102,9 @@ export function useAuth() {
71
102
  if (!getIsLoggedIn()) {
72
103
  const { data } = await authApi!.getCurrentUser()
73
104
  currentUser.value = data
105
+ if (getIsLoggedIn()) {
106
+ eventEmitter!.emit(AuthState.AUTH_CHECK)
107
+ }
74
108
  }
75
109
  } catch (error) {
76
110
  return false
@@ -85,6 +119,7 @@ export function useAuth() {
85
119
  }
86
120
  const { data } = await authApi!.signup(user)
87
121
  currentUser.value = data
122
+ eventEmitter!.emit(AuthState.SIGNUP)
88
123
  } catch (error) {
89
124
  throw error
90
125
  }
@@ -101,6 +136,7 @@ export function useAuth() {
101
136
  async function resetPassword(newPassword: string) {
102
137
  try {
103
138
  await authApi!.resetPassword(newPassword)
139
+ eventEmitter!.emit(AuthState.PASSWORD_RESET)
104
140
  } catch (error) {
105
141
  throw error
106
142
  }
@@ -112,6 +148,7 @@ export function useAuth() {
112
148
  throw new Error('Passwords do not match')
113
149
  }
114
150
  await authApi!.updatePassword(form)
151
+ eventEmitter!.emit(AuthState.PASSWORD_RESET)
115
152
  } catch (error) {
116
153
  throw error
117
154
  }
@@ -121,6 +158,7 @@ export function useAuth() {
121
158
  try {
122
159
  const { data } = await authApi!.updateUserProfile(user)
123
160
  currentUser.value = { ...currentUser.value, ...data }
161
+ eventEmitter!.emit(AuthState.PROFILE_UPDATE)
124
162
  } catch (error) {
125
163
  throw error
126
164
  }
package/src/utils.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import type { AxiosInstance } from 'axios'
2
+ import type { AuthEventMap, AuthState } from './types'
2
3
  import axios from 'axios'
3
4
 
4
5
  export function createAxiosInstance(baseURL: string = ''): AxiosInstance {
@@ -10,3 +11,36 @@ export function createAxiosInstance(baseURL: string = ''): AxiosInstance {
10
11
  },
11
12
  })
12
13
  }
14
+
15
+ export class EventEmitter {
16
+ private listeners: Map<string, Set<() => void>> = new Map()
17
+
18
+ on<K extends AuthState>(event: K, handler: AuthEventMap[K]): void {
19
+ if (!this.listeners.has(event)) {
20
+ this.listeners.set(event, new Set())
21
+ }
22
+ this.listeners.get(event)!.add(handler)
23
+ }
24
+
25
+ off<K extends AuthState>(event: K, handler: AuthEventMap[K]): void {
26
+ const eventListeners = this.listeners.get(event)
27
+ if (eventListeners) {
28
+ eventListeners.delete(handler)
29
+ }
30
+ }
31
+
32
+ emit<K extends AuthState>(event: K): void {
33
+ const eventListeners = this.listeners.get(event)
34
+ if (eventListeners) {
35
+ eventListeners.forEach((handler) => { handler() })
36
+ }
37
+ }
38
+
39
+ removeAllListeners<K extends AuthState>(event?: K): void {
40
+ if (event) {
41
+ this.listeners.delete(event)
42
+ } else {
43
+ this.listeners.clear()
44
+ }
45
+ }
46
+ }