@cranberry-money/shared-services 1.0.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.
- package/README.md +288 -0
- package/dist/adapters/MobileApiClient.d.ts +68 -0
- package/dist/adapters/MobileApiClient.d.ts.map +1 -0
- package/dist/adapters/MobileApiClient.js +240 -0
- package/dist/adapters/MobileTokenStorage.d.ts +43 -0
- package/dist/adapters/MobileTokenStorage.d.ts.map +1 -0
- package/dist/adapters/MobileTokenStorage.js +128 -0
- package/dist/adapters/WebApiClient.d.ts +28 -0
- package/dist/adapters/WebApiClient.d.ts.map +1 -0
- package/dist/adapters/WebApiClient.js +119 -0
- package/dist/adapters/WebTokenStorage.d.ts +38 -0
- package/dist/adapters/WebTokenStorage.d.ts.map +1 -0
- package/dist/adapters/WebTokenStorage.js +86 -0
- package/dist/auth/AuthManager.d.ts +81 -0
- package/dist/auth/AuthManager.d.ts.map +1 -0
- package/dist/auth/AuthManager.js +223 -0
- package/dist/auth/createAuthManager.d.ts +63 -0
- package/dist/auth/createAuthManager.d.ts.map +1 -0
- package/dist/auth/createAuthManager.js +103 -0
- package/dist/auth/useAuthManager.d.ts +66 -0
- package/dist/auth/useAuthManager.d.ts.map +1 -0
- package/dist/auth/useAuthManager.js +133 -0
- package/dist/core/BaseApiClient.d.ts +82 -0
- package/dist/core/BaseApiClient.d.ts.map +1 -0
- package/dist/core/BaseApiClient.js +89 -0
- package/dist/core/TokenStorage.d.ts +45 -0
- package/dist/core/TokenStorage.d.ts.map +1 -0
- package/dist/core/TokenStorage.js +23 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/query/QueryClient.d.ts +82 -0
- package/dist/query/QueryClient.d.ts.map +1 -0
- package/dist/query/QueryClient.js +136 -0
- package/dist/query/useAuth.d.ts +64 -0
- package/dist/query/useAuth.d.ts.map +1 -0
- package/dist/query/useAuth.js +144 -0
- package/dist/query/usePortfolios.d.ts +79 -0
- package/dist/query/usePortfolios.d.ts.map +1 -0
- package/dist/query/usePortfolios.js +172 -0
- package/dist/services/AuthService.d.ts +75 -0
- package/dist/services/AuthService.d.ts.map +1 -0
- package/dist/services/AuthService.js +83 -0
- package/dist/services/BaseService.d.ts +48 -0
- package/dist/services/BaseService.d.ts.map +1 -0
- package/dist/services/BaseService.js +51 -0
- package/dist/services/PortfolioService.d.ts +100 -0
- package/dist/services/PortfolioService.d.ts.map +1 -0
- package/dist/services/PortfolioService.js +68 -0
- package/package.json +56 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform Authentication Manager
|
|
3
|
+
*
|
|
4
|
+
* Centralizes authentication logic and state management across web and mobile platforms.
|
|
5
|
+
* Handles token storage, automatic refresh, and authentication state persistence.
|
|
6
|
+
*/
|
|
7
|
+
import { AuthService } from '../services/AuthService';
|
|
8
|
+
export class AuthManager {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
this.currentState = {
|
|
11
|
+
isAuthenticated: false,
|
|
12
|
+
isLoading: false,
|
|
13
|
+
user: null,
|
|
14
|
+
error: null,
|
|
15
|
+
};
|
|
16
|
+
this.apiClient = config.apiClient;
|
|
17
|
+
this.tokenStorage = config.tokenStorage;
|
|
18
|
+
this.authService = new AuthService(config.apiClient);
|
|
19
|
+
this.onAuthStateChange = config.onAuthStateChange;
|
|
20
|
+
this.autoRefreshEnabled = config.autoRefreshEnabled ?? true;
|
|
21
|
+
// Set token storage on API client
|
|
22
|
+
this.apiClient.setTokenStorage(this.tokenStorage);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get current authentication state
|
|
26
|
+
*/
|
|
27
|
+
getState() {
|
|
28
|
+
return { ...this.currentState };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Initialize authentication manager
|
|
32
|
+
* Checks for existing tokens and validates authentication state
|
|
33
|
+
*/
|
|
34
|
+
async initialize() {
|
|
35
|
+
this.updateState({ isLoading: true, error: null });
|
|
36
|
+
try {
|
|
37
|
+
const hasTokens = await this.tokenStorage.hasTokens();
|
|
38
|
+
if (!hasTokens) {
|
|
39
|
+
this.updateState({
|
|
40
|
+
isAuthenticated: false,
|
|
41
|
+
isLoading: false,
|
|
42
|
+
user: null
|
|
43
|
+
});
|
|
44
|
+
return this.currentState;
|
|
45
|
+
}
|
|
46
|
+
// Validate tokens by fetching user profile
|
|
47
|
+
const user = await this.authService.getProfile();
|
|
48
|
+
this.updateState({
|
|
49
|
+
isAuthenticated: true,
|
|
50
|
+
isLoading: false,
|
|
51
|
+
user,
|
|
52
|
+
error: null,
|
|
53
|
+
});
|
|
54
|
+
// Start auto-refresh if enabled
|
|
55
|
+
if (this.autoRefreshEnabled) {
|
|
56
|
+
this.startAutoRefresh();
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
// Tokens are invalid, clear them
|
|
61
|
+
await this.tokenStorage.clearTokens();
|
|
62
|
+
this.updateState({
|
|
63
|
+
isAuthenticated: false,
|
|
64
|
+
isLoading: false,
|
|
65
|
+
user: null,
|
|
66
|
+
error: error instanceof Error ? error.message : 'Authentication failed',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return this.currentState;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Sign in user with credentials
|
|
73
|
+
*/
|
|
74
|
+
async signin(credentials) {
|
|
75
|
+
this.updateState({ isLoading: true, error: null });
|
|
76
|
+
try {
|
|
77
|
+
const response = await this.authService.signin(credentials);
|
|
78
|
+
// Store tokens if provided in response (mobile)
|
|
79
|
+
if (response && typeof response === 'object' && 'access' in response && 'refresh' in response) {
|
|
80
|
+
await this.tokenStorage.storeTokens(response);
|
|
81
|
+
}
|
|
82
|
+
// Fetch user profile
|
|
83
|
+
const user = await this.authService.getProfile();
|
|
84
|
+
this.updateState({
|
|
85
|
+
isAuthenticated: true,
|
|
86
|
+
isLoading: false,
|
|
87
|
+
user,
|
|
88
|
+
error: null,
|
|
89
|
+
});
|
|
90
|
+
// Start auto-refresh if enabled
|
|
91
|
+
if (this.autoRefreshEnabled) {
|
|
92
|
+
this.startAutoRefresh();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
this.updateState({
|
|
97
|
+
isAuthenticated: false,
|
|
98
|
+
isLoading: false,
|
|
99
|
+
user: null,
|
|
100
|
+
error: error instanceof Error ? error.message : 'Sign in failed',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return this.currentState;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Sign out user
|
|
107
|
+
*/
|
|
108
|
+
async signout() {
|
|
109
|
+
this.updateState({ isLoading: true, error: null });
|
|
110
|
+
try {
|
|
111
|
+
// Call sign out endpoint
|
|
112
|
+
await this.authService.signout();
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
// Log error but continue with local cleanup
|
|
116
|
+
console.warn('Sign out API call failed:', error);
|
|
117
|
+
}
|
|
118
|
+
// Always clear local state regardless of API call result
|
|
119
|
+
await this.tokenStorage.clearTokens();
|
|
120
|
+
this.stopAutoRefresh();
|
|
121
|
+
this.updateState({
|
|
122
|
+
isAuthenticated: false,
|
|
123
|
+
isLoading: false,
|
|
124
|
+
user: null,
|
|
125
|
+
error: null,
|
|
126
|
+
});
|
|
127
|
+
return this.currentState;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Sign up new user
|
|
131
|
+
*/
|
|
132
|
+
async signup(userData) {
|
|
133
|
+
this.updateState({ isLoading: true, error: null });
|
|
134
|
+
try {
|
|
135
|
+
await this.authService.signup(userData);
|
|
136
|
+
this.updateState({ isLoading: false, error: null });
|
|
137
|
+
return { success: true };
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
this.updateState({
|
|
141
|
+
isLoading: false,
|
|
142
|
+
error: error instanceof Error ? error.message : 'Sign up failed',
|
|
143
|
+
});
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
error: error instanceof Error ? error.message : 'Sign up failed'
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Refresh authentication tokens
|
|
152
|
+
*/
|
|
153
|
+
async refreshTokens() {
|
|
154
|
+
try {
|
|
155
|
+
const tokens = await this.tokenStorage.retrieveTokens();
|
|
156
|
+
if (!tokens?.refresh) {
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
const newTokens = await this.authService.refreshToken({ refresh: tokens.refresh });
|
|
160
|
+
await this.tokenStorage.storeTokens(newTokens);
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
console.error('Token refresh failed:', error);
|
|
165
|
+
// Clear invalid tokens
|
|
166
|
+
await this.tokenStorage.clearTokens();
|
|
167
|
+
this.updateState({
|
|
168
|
+
isAuthenticated: false,
|
|
169
|
+
user: null,
|
|
170
|
+
error: 'Session expired',
|
|
171
|
+
});
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Update user profile in state
|
|
177
|
+
*/
|
|
178
|
+
async refreshProfile() {
|
|
179
|
+
if (!this.currentState.isAuthenticated)
|
|
180
|
+
return;
|
|
181
|
+
try {
|
|
182
|
+
const user = await this.authService.getProfile();
|
|
183
|
+
this.updateState({ user });
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
console.error('Profile refresh failed:', error);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Start automatic token refresh
|
|
191
|
+
*/
|
|
192
|
+
startAutoRefresh() {
|
|
193
|
+
this.stopAutoRefresh();
|
|
194
|
+
// Check every 5 minutes
|
|
195
|
+
this.refreshTimer = setInterval(async () => {
|
|
196
|
+
if (this.currentState.isAuthenticated) {
|
|
197
|
+
await this.refreshTokens();
|
|
198
|
+
}
|
|
199
|
+
}, 5 * 60 * 1000);
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Stop automatic token refresh
|
|
203
|
+
*/
|
|
204
|
+
stopAutoRefresh() {
|
|
205
|
+
if (this.refreshTimer) {
|
|
206
|
+
clearInterval(this.refreshTimer);
|
|
207
|
+
this.refreshTimer = undefined;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Update authentication state and notify listeners
|
|
212
|
+
*/
|
|
213
|
+
updateState(updates) {
|
|
214
|
+
this.currentState = { ...this.currentState, ...updates };
|
|
215
|
+
this.onAuthStateChange?.(this.currentState);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Clean up resources
|
|
219
|
+
*/
|
|
220
|
+
dispose() {
|
|
221
|
+
this.stopAutoRefresh();
|
|
222
|
+
}
|
|
223
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory functions to create platform-specific AuthManager instances
|
|
3
|
+
*
|
|
4
|
+
* Provides convenient setup for web and mobile authentication with
|
|
5
|
+
* appropriate API clients and token storage implementations.
|
|
6
|
+
*/
|
|
7
|
+
import { AuthManager, type AuthManagerConfig } from './AuthManager';
|
|
8
|
+
import { type SecureStoreInterface } from '../adapters/MobileTokenStorage';
|
|
9
|
+
export interface WebAuthManagerConfig {
|
|
10
|
+
apiBaseUrl: string;
|
|
11
|
+
onAuthStateChange?: AuthManagerConfig['onAuthStateChange'];
|
|
12
|
+
autoRefreshEnabled?: boolean;
|
|
13
|
+
apiTimeout?: number;
|
|
14
|
+
}
|
|
15
|
+
export interface MobileAuthManagerConfig {
|
|
16
|
+
apiBaseUrl: string;
|
|
17
|
+
secureStore: SecureStoreInterface;
|
|
18
|
+
onAuthStateChange?: AuthManagerConfig['onAuthStateChange'];
|
|
19
|
+
autoRefreshEnabled?: boolean;
|
|
20
|
+
retryAttempts?: number;
|
|
21
|
+
offlineQueueEnabled?: boolean;
|
|
22
|
+
apiTimeout?: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Create AuthManager for web applications
|
|
26
|
+
* Uses cookie-based authentication with fetch API
|
|
27
|
+
*/
|
|
28
|
+
export declare function createWebAuthManager(config: WebAuthManagerConfig): AuthManager;
|
|
29
|
+
/**
|
|
30
|
+
* Create AuthManager for mobile applications
|
|
31
|
+
* Uses secure storage with token-based authentication
|
|
32
|
+
*/
|
|
33
|
+
export declare function createMobileAuthManager(config: MobileAuthManagerConfig): AuthManager;
|
|
34
|
+
/**
|
|
35
|
+
* Create Expo-specific AuthManager
|
|
36
|
+
* Convenience function for Expo applications using SecureStore
|
|
37
|
+
*/
|
|
38
|
+
export declare function createExpoAuthManager(config: Omit<MobileAuthManagerConfig, 'secureStore'> & {
|
|
39
|
+
expoSecureStore: SecureStoreInterface;
|
|
40
|
+
}): AuthManager;
|
|
41
|
+
/**
|
|
42
|
+
* AuthManager factory for different platforms
|
|
43
|
+
*/
|
|
44
|
+
export declare const authManagerFactory: {
|
|
45
|
+
readonly web: typeof createWebAuthManager;
|
|
46
|
+
readonly mobile: typeof createMobileAuthManager;
|
|
47
|
+
readonly expo: typeof createExpoAuthManager;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Platform detection utility
|
|
51
|
+
*/
|
|
52
|
+
export declare function detectPlatform(): 'web' | 'mobile';
|
|
53
|
+
/**
|
|
54
|
+
* Auto-create AuthManager based on platform
|
|
55
|
+
*/
|
|
56
|
+
export declare function createAuthManagerForPlatform(config: {
|
|
57
|
+
apiBaseUrl: string;
|
|
58
|
+
onAuthStateChange?: AuthManagerConfig['onAuthStateChange'];
|
|
59
|
+
secureStore?: SecureStoreInterface;
|
|
60
|
+
retryAttempts?: number;
|
|
61
|
+
offlineQueueEnabled?: boolean;
|
|
62
|
+
}): AuthManager;
|
|
63
|
+
//# sourceMappingURL=createAuthManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAuthManager.d.ts","sourceRoot":"","sources":["../../src/auth/createAuthManager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAIpE,OAAO,EAAsB,KAAK,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAE/F,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC,mBAAmB,CAAC,CAAC;IAC3D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,oBAAoB,CAAC;IAClC,iBAAiB,CAAC,EAAE,iBAAiB,CAAC,mBAAmB,CAAC,CAAC;IAC3D,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,oBAAoB,GAAG,WAAW,CAe9E;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,uBAAuB,GAAG,WAAW,CAgBpF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,IAAI,CAAC,uBAAuB,EAAE,aAAa,CAAC,GAAG;IACrD,eAAe,EAAE,oBAAoB,CAAC;CACvC,GACA,WAAW,CAKb;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;CAIrB,CAAC;AAEX;;GAEG;AACH,wBAAgB,cAAc,IAAI,KAAK,GAAG,QAAQ,CAajD;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,iBAAiB,CAAC,mBAAmB,CAAC,CAAC;IAE3D,WAAW,CAAC,EAAE,oBAAoB,CAAC;IACnC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mBAAmB,CAAC,EAAE,OAAO,CAAC;CAC/B,GAAG,WAAW,CAqBd"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory functions to create platform-specific AuthManager instances
|
|
3
|
+
*
|
|
4
|
+
* Provides convenient setup for web and mobile authentication with
|
|
5
|
+
* appropriate API clients and token storage implementations.
|
|
6
|
+
*/
|
|
7
|
+
import { AuthManager } from './AuthManager';
|
|
8
|
+
import { WebApiClient } from '../adapters/WebApiClient';
|
|
9
|
+
import { WebTokenStorage } from '../adapters/WebTokenStorage';
|
|
10
|
+
import { MobileApiClient } from '../adapters/MobileApiClient';
|
|
11
|
+
import { MobileTokenStorage } from '../adapters/MobileTokenStorage';
|
|
12
|
+
/**
|
|
13
|
+
* Create AuthManager for web applications
|
|
14
|
+
* Uses cookie-based authentication with fetch API
|
|
15
|
+
*/
|
|
16
|
+
export function createWebAuthManager(config) {
|
|
17
|
+
const apiClient = new WebApiClient({
|
|
18
|
+
baseURL: config.apiBaseUrl,
|
|
19
|
+
withCredentials: true, // Enable cookies
|
|
20
|
+
timeout: config.apiTimeout || 30000,
|
|
21
|
+
});
|
|
22
|
+
const tokenStorage = new WebTokenStorage();
|
|
23
|
+
return new AuthManager({
|
|
24
|
+
apiClient,
|
|
25
|
+
tokenStorage,
|
|
26
|
+
onAuthStateChange: config.onAuthStateChange,
|
|
27
|
+
autoRefreshEnabled: config.autoRefreshEnabled ?? true,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create AuthManager for mobile applications
|
|
32
|
+
* Uses secure storage with token-based authentication
|
|
33
|
+
*/
|
|
34
|
+
export function createMobileAuthManager(config) {
|
|
35
|
+
const apiClient = new MobileApiClient({
|
|
36
|
+
baseURL: config.apiBaseUrl,
|
|
37
|
+
timeout: config.apiTimeout || 30000,
|
|
38
|
+
retryAttempts: config.retryAttempts || 3,
|
|
39
|
+
offlineQueueEnabled: config.offlineQueueEnabled ?? true,
|
|
40
|
+
});
|
|
41
|
+
const tokenStorage = new MobileTokenStorage(config.secureStore);
|
|
42
|
+
return new AuthManager({
|
|
43
|
+
apiClient,
|
|
44
|
+
tokenStorage,
|
|
45
|
+
onAuthStateChange: config.onAuthStateChange,
|
|
46
|
+
autoRefreshEnabled: config.autoRefreshEnabled ?? true,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Create Expo-specific AuthManager
|
|
51
|
+
* Convenience function for Expo applications using SecureStore
|
|
52
|
+
*/
|
|
53
|
+
export function createExpoAuthManager(config) {
|
|
54
|
+
return createMobileAuthManager({
|
|
55
|
+
...config,
|
|
56
|
+
secureStore: config.expoSecureStore,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* AuthManager factory for different platforms
|
|
61
|
+
*/
|
|
62
|
+
export const authManagerFactory = {
|
|
63
|
+
web: createWebAuthManager,
|
|
64
|
+
mobile: createMobileAuthManager,
|
|
65
|
+
expo: createExpoAuthManager,
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Platform detection utility
|
|
69
|
+
*/
|
|
70
|
+
export function detectPlatform() {
|
|
71
|
+
// Simple platform detection
|
|
72
|
+
if (typeof window !== 'undefined' && window.document) {
|
|
73
|
+
return 'web';
|
|
74
|
+
}
|
|
75
|
+
// Check for React Native environment
|
|
76
|
+
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
|
77
|
+
return 'mobile';
|
|
78
|
+
}
|
|
79
|
+
// Default to web for SSR environments
|
|
80
|
+
return 'web';
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Auto-create AuthManager based on platform
|
|
84
|
+
*/
|
|
85
|
+
export function createAuthManagerForPlatform(config) {
|
|
86
|
+
const platform = detectPlatform();
|
|
87
|
+
if (platform === 'mobile') {
|
|
88
|
+
if (!config.secureStore) {
|
|
89
|
+
throw new Error('SecureStore is required for mobile platform');
|
|
90
|
+
}
|
|
91
|
+
return createMobileAuthManager({
|
|
92
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
93
|
+
secureStore: config.secureStore,
|
|
94
|
+
onAuthStateChange: config.onAuthStateChange,
|
|
95
|
+
retryAttempts: config.retryAttempts,
|
|
96
|
+
offlineQueueEnabled: config.offlineQueueEnabled,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return createWebAuthManager({
|
|
100
|
+
apiBaseUrl: config.apiBaseUrl,
|
|
101
|
+
onAuthStateChange: config.onAuthStateChange,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* React hook for AuthManager integration
|
|
3
|
+
*
|
|
4
|
+
* Provides a React-friendly interface to the AuthManager with
|
|
5
|
+
* automatic state synchronization and React Query integration.
|
|
6
|
+
*/
|
|
7
|
+
import React from 'react';
|
|
8
|
+
import type { AuthManager, AuthState } from './AuthManager';
|
|
9
|
+
import type { SigninPayload, SignupPayload } from '../services/AuthService';
|
|
10
|
+
export interface UseAuthManagerOptions {
|
|
11
|
+
authManager: AuthManager;
|
|
12
|
+
enableQueryInvalidation?: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function useAuthManager({ authManager, enableQueryInvalidation }: UseAuthManagerOptions): {
|
|
15
|
+
getState: () => AuthState;
|
|
16
|
+
clearError: () => void;
|
|
17
|
+
signin: (credentials: SigninPayload) => Promise<AuthState>;
|
|
18
|
+
signout: () => Promise<AuthState>;
|
|
19
|
+
signup: (userData: SignupPayload) => Promise<{
|
|
20
|
+
success: boolean;
|
|
21
|
+
error?: string;
|
|
22
|
+
}>;
|
|
23
|
+
refreshTokens: () => Promise<boolean>;
|
|
24
|
+
refreshProfile: () => Promise<void>;
|
|
25
|
+
isSignedIn: boolean;
|
|
26
|
+
isSignedOut: boolean;
|
|
27
|
+
hasUser: boolean;
|
|
28
|
+
hasError: boolean;
|
|
29
|
+
isAuthenticated: boolean;
|
|
30
|
+
isLoading: boolean;
|
|
31
|
+
user: import("..").UserProfile | null;
|
|
32
|
+
error: string | null;
|
|
33
|
+
};
|
|
34
|
+
/**
|
|
35
|
+
* Higher-order component for AuthManager integration
|
|
36
|
+
*/
|
|
37
|
+
export declare function withAuthManager<P extends object>(Component: React.ComponentType<P & {
|
|
38
|
+
authManager: ReturnType<typeof useAuthManager>;
|
|
39
|
+
}>, authManager: AuthManager): (props: P) => import("react/jsx-runtime").JSX.Element;
|
|
40
|
+
export declare function AuthManagerProvider({ children, authManager }: {
|
|
41
|
+
children: React.ReactNode;
|
|
42
|
+
authManager: AuthManager;
|
|
43
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
44
|
+
export declare function useAuthManagerContext(options?: {
|
|
45
|
+
enableQueryInvalidation?: boolean;
|
|
46
|
+
}): {
|
|
47
|
+
getState: () => AuthState;
|
|
48
|
+
clearError: () => void;
|
|
49
|
+
signin: (credentials: SigninPayload) => Promise<AuthState>;
|
|
50
|
+
signout: () => Promise<AuthState>;
|
|
51
|
+
signup: (userData: SignupPayload) => Promise<{
|
|
52
|
+
success: boolean;
|
|
53
|
+
error?: string;
|
|
54
|
+
}>;
|
|
55
|
+
refreshTokens: () => Promise<boolean>;
|
|
56
|
+
refreshProfile: () => Promise<void>;
|
|
57
|
+
isSignedIn: boolean;
|
|
58
|
+
isSignedOut: boolean;
|
|
59
|
+
hasUser: boolean;
|
|
60
|
+
hasError: boolean;
|
|
61
|
+
isAuthenticated: boolean;
|
|
62
|
+
isLoading: boolean;
|
|
63
|
+
user: import("..").UserProfile | null;
|
|
64
|
+
error: string | null;
|
|
65
|
+
};
|
|
66
|
+
//# sourceMappingURL=useAuthManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAuthManager.d.ts","sourceRoot":"","sources":["../../src/auth/useAuthManager.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAA+E,MAAM,OAAO,CAAC;AAEpG,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAG5E,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,WAAW,CAAC;IACzB,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,wBAAgB,cAAc,CAAC,EAAE,WAAW,EAAE,uBAA8B,EAAE,EAAE,qBAAqB;;;0BA4DrE,aAAa;;uBAYhB,aAAa;;;;;;;;;;;;;;EAsCzC;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,MAAM,EAC9C,SAAS,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC,GAAG;IAAE,WAAW,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAA;CAAE,CAAC,EACtF,WAAW,EAAE,WAAW,IAEW,OAAO,CAAC,6CAK5C;AAOD,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,WAAW,EACZ,EAAE;IACD,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,WAAW,EAAE,WAAW,CAAC;CAC1B,2CAMA;AAED,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE;IAAE,uBAAuB,CAAC,EAAE,OAAO,CAAA;CAAE;;;0BArFrD,aAAa;;uBAYhB,aAAa;;;;;;;;;;;;;;EAoFzC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* React hook for AuthManager integration
|
|
4
|
+
*
|
|
5
|
+
* Provides a React-friendly interface to the AuthManager with
|
|
6
|
+
* automatic state synchronization and React Query integration.
|
|
7
|
+
*/
|
|
8
|
+
import { useEffect, useState, useCallback, useMemo, createContext, useContext } from 'react';
|
|
9
|
+
import { useQueryClient } from '@tanstack/react-query';
|
|
10
|
+
import { queryKeys } from '../query/QueryClient';
|
|
11
|
+
export function useAuthManager({ authManager, enableQueryInvalidation = true }) {
|
|
12
|
+
const queryClient = useQueryClient();
|
|
13
|
+
const [authState, setAuthState] = useState(() => authManager.getState());
|
|
14
|
+
// Initialize auth manager on mount
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
let mounted = true;
|
|
17
|
+
const initialize = async () => {
|
|
18
|
+
try {
|
|
19
|
+
await authManager.initialize();
|
|
20
|
+
if (mounted) {
|
|
21
|
+
setAuthState(authManager.getState());
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
console.error('AuthManager initialization failed:', error);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
initialize();
|
|
29
|
+
return () => {
|
|
30
|
+
mounted = false;
|
|
31
|
+
};
|
|
32
|
+
}, [authManager]);
|
|
33
|
+
// Subscribe to auth state changes
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const unsubscribe = (newState) => {
|
|
36
|
+
setAuthState(newState);
|
|
37
|
+
// Invalidate auth-related queries when authentication state changes
|
|
38
|
+
if (enableQueryInvalidation) {
|
|
39
|
+
if (newState.isAuthenticated && !authState.isAuthenticated) {
|
|
40
|
+
// User signed in - invalidate all queries to refresh data
|
|
41
|
+
queryClient.invalidateQueries();
|
|
42
|
+
}
|
|
43
|
+
else if (!newState.isAuthenticated && authState.isAuthenticated) {
|
|
44
|
+
// User signed out - clear all queries
|
|
45
|
+
queryClient.clear();
|
|
46
|
+
}
|
|
47
|
+
else if (newState.user && newState.user !== authState.user) {
|
|
48
|
+
// User profile changed - invalidate auth queries
|
|
49
|
+
queryClient.invalidateQueries({ queryKey: queryKeys.auth.profile() });
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
// Set up state change listener
|
|
54
|
+
const originalOnChange = authManager['onAuthStateChange'];
|
|
55
|
+
authManager['onAuthStateChange'] = (state) => {
|
|
56
|
+
unsubscribe(state);
|
|
57
|
+
originalOnChange?.(state);
|
|
58
|
+
};
|
|
59
|
+
return () => {
|
|
60
|
+
authManager['onAuthStateChange'] = originalOnChange;
|
|
61
|
+
};
|
|
62
|
+
}, [authManager, authState, queryClient, enableQueryInvalidation]);
|
|
63
|
+
// Memoized action functions
|
|
64
|
+
const actions = useMemo(() => ({
|
|
65
|
+
signin: async (credentials) => {
|
|
66
|
+
const result = await authManager.signin(credentials);
|
|
67
|
+
setAuthState(result);
|
|
68
|
+
return result;
|
|
69
|
+
},
|
|
70
|
+
signout: async () => {
|
|
71
|
+
const result = await authManager.signout();
|
|
72
|
+
setAuthState(result);
|
|
73
|
+
return result;
|
|
74
|
+
},
|
|
75
|
+
signup: async (userData) => {
|
|
76
|
+
return authManager.signup(userData);
|
|
77
|
+
},
|
|
78
|
+
refreshTokens: async () => {
|
|
79
|
+
return authManager.refreshTokens();
|
|
80
|
+
},
|
|
81
|
+
refreshProfile: async () => {
|
|
82
|
+
await authManager.refreshProfile();
|
|
83
|
+
setAuthState(authManager.getState());
|
|
84
|
+
},
|
|
85
|
+
}), [authManager]);
|
|
86
|
+
// Convenience getters
|
|
87
|
+
const derived = useMemo(() => ({
|
|
88
|
+
isSignedIn: authState.isAuthenticated,
|
|
89
|
+
isSignedOut: !authState.isAuthenticated && !authState.isLoading,
|
|
90
|
+
hasUser: !!authState.user,
|
|
91
|
+
hasError: !!authState.error,
|
|
92
|
+
}), [authState]);
|
|
93
|
+
return {
|
|
94
|
+
// State
|
|
95
|
+
...authState,
|
|
96
|
+
...derived,
|
|
97
|
+
// Actions
|
|
98
|
+
...actions,
|
|
99
|
+
// Utilities
|
|
100
|
+
getState: useCallback(() => authManager.getState(), [authManager]),
|
|
101
|
+
clearError: useCallback(() => {
|
|
102
|
+
if (authState.error) {
|
|
103
|
+
setAuthState((prev) => ({ ...prev, error: null }));
|
|
104
|
+
}
|
|
105
|
+
}, [authState.error]),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Higher-order component for AuthManager integration
|
|
110
|
+
*/
|
|
111
|
+
export function withAuthManager(Component, authManager) {
|
|
112
|
+
return function AuthManagerWrapper(props) {
|
|
113
|
+
const authManagerHook = useAuthManager({ authManager });
|
|
114
|
+
return _jsx(Component, { ...props, authManager: authManagerHook });
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Context provider for AuthManager (optional pattern)
|
|
119
|
+
*/
|
|
120
|
+
const AuthManagerContext = createContext(null);
|
|
121
|
+
export function AuthManagerProvider({ children, authManager }) {
|
|
122
|
+
return (_jsx(AuthManagerContext.Provider, { value: authManager, children: children }));
|
|
123
|
+
}
|
|
124
|
+
export function useAuthManagerContext(options) {
|
|
125
|
+
const authManager = useContext(AuthManagerContext);
|
|
126
|
+
if (!authManager) {
|
|
127
|
+
throw new Error('useAuthManagerContext must be used within an AuthManagerProvider');
|
|
128
|
+
}
|
|
129
|
+
return useAuthManager({
|
|
130
|
+
authManager,
|
|
131
|
+
enableQueryInvalidation: options?.enableQueryInvalidation
|
|
132
|
+
});
|
|
133
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base API Client abstraction for cross-platform HTTP requests
|
|
3
|
+
*
|
|
4
|
+
* This provides a platform-agnostic interface for making HTTP requests
|
|
5
|
+
* with consistent error handling, authentication, and response processing.
|
|
6
|
+
*/
|
|
7
|
+
import type { TokenStorage } from './TokenStorage';
|
|
8
|
+
export interface RequestConfig {
|
|
9
|
+
url: string;
|
|
10
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
|
|
11
|
+
data?: unknown;
|
|
12
|
+
params?: Record<string, unknown>;
|
|
13
|
+
headers?: Record<string, string>;
|
|
14
|
+
timeout?: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ApiResponse<T = unknown> {
|
|
17
|
+
data: T;
|
|
18
|
+
status: number;
|
|
19
|
+
statusText: string;
|
|
20
|
+
headers: Record<string, string>;
|
|
21
|
+
}
|
|
22
|
+
export interface ApiError {
|
|
23
|
+
message: string;
|
|
24
|
+
status?: number;
|
|
25
|
+
code?: string;
|
|
26
|
+
data?: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface ApiClientConfig {
|
|
29
|
+
baseURL: string;
|
|
30
|
+
timeout?: number;
|
|
31
|
+
defaultHeaders?: Record<string, string>;
|
|
32
|
+
withCredentials?: boolean;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Abstract base class for API clients
|
|
36
|
+
* Platform-specific implementations should extend this class
|
|
37
|
+
*/
|
|
38
|
+
export declare abstract class BaseApiClient {
|
|
39
|
+
protected baseURL: string;
|
|
40
|
+
protected timeout: number;
|
|
41
|
+
protected defaultHeaders: Record<string, string>;
|
|
42
|
+
protected tokenStorage?: TokenStorage;
|
|
43
|
+
protected withCredentials: boolean;
|
|
44
|
+
constructor(config: ApiClientConfig);
|
|
45
|
+
/**
|
|
46
|
+
* Set token storage implementation
|
|
47
|
+
*/
|
|
48
|
+
setTokenStorage(storage: TokenStorage): void;
|
|
49
|
+
/**
|
|
50
|
+
* Make HTTP request - must be implemented by platform-specific clients
|
|
51
|
+
*/
|
|
52
|
+
abstract request<T = unknown>(config: RequestConfig): Promise<ApiResponse<T>>;
|
|
53
|
+
/**
|
|
54
|
+
* Convenience methods for common HTTP operations
|
|
55
|
+
*/
|
|
56
|
+
get<T = unknown>(url: string, params?: Record<string, unknown>): Promise<ApiResponse<T>>;
|
|
57
|
+
post<T = unknown>(url: string, data?: unknown): Promise<ApiResponse<T>>;
|
|
58
|
+
put<T = unknown>(url: string, data?: unknown): Promise<ApiResponse<T>>;
|
|
59
|
+
patch<T = unknown>(url: string, data?: unknown): Promise<ApiResponse<T>>;
|
|
60
|
+
delete<T = unknown>(url: string): Promise<ApiResponse<T>>;
|
|
61
|
+
/**
|
|
62
|
+
* Build full URL from relative path
|
|
63
|
+
*/
|
|
64
|
+
protected buildUrl(url: string): string;
|
|
65
|
+
/**
|
|
66
|
+
* Merge headers with defaults and authentication
|
|
67
|
+
*/
|
|
68
|
+
protected buildHeaders(headers?: Record<string, string>): Promise<Record<string, string>>;
|
|
69
|
+
/**
|
|
70
|
+
* Handle API errors consistently
|
|
71
|
+
*/
|
|
72
|
+
protected createError(message: string, status?: number, code?: string, data?: unknown): ApiError;
|
|
73
|
+
/**
|
|
74
|
+
* Check if error is authentication related
|
|
75
|
+
*/
|
|
76
|
+
protected isAuthError(error: ApiError): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Handle token refresh logic (to be implemented by platform adapters)
|
|
79
|
+
*/
|
|
80
|
+
protected handleTokenRefresh(): Promise<boolean>;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=BaseApiClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BaseApiClient.d.ts","sourceRoot":"","sources":["../../src/core/BaseApiClient.ts"],"names":[],"mappings":"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"}
|