@oxyhq/services 5.5.0 → 5.5.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 (36) hide show
  1. package/lib/commonjs/core/index.js +8 -0
  2. package/lib/commonjs/core/index.js.map +1 -1
  3. package/lib/commonjs/index.js +15 -19
  4. package/lib/commonjs/index.js.map +1 -1
  5. package/lib/commonjs/ui/hooks/index.js +7 -0
  6. package/lib/commonjs/ui/hooks/index.js.map +1 -1
  7. package/lib/commonjs/ui/hooks/useAuthFetch.js +197 -0
  8. package/lib/commonjs/ui/hooks/useAuthFetch.js.map +1 -0
  9. package/lib/commonjs/ui/index.js +8 -0
  10. package/lib/commonjs/ui/index.js.map +1 -1
  11. package/lib/module/core/index.js +8 -0
  12. package/lib/module/core/index.js.map +1 -1
  13. package/lib/module/index.js +4 -5
  14. package/lib/module/index.js.map +1 -1
  15. package/lib/module/ui/hooks/index.js +1 -0
  16. package/lib/module/ui/hooks/index.js.map +1 -1
  17. package/lib/module/ui/hooks/useAuthFetch.js +192 -0
  18. package/lib/module/ui/hooks/useAuthFetch.js.map +1 -0
  19. package/lib/module/ui/index.js +1 -0
  20. package/lib/module/ui/index.js.map +1 -1
  21. package/lib/typescript/core/index.d.ts +5 -0
  22. package/lib/typescript/core/index.d.ts.map +1 -1
  23. package/lib/typescript/index.d.ts +3 -4
  24. package/lib/typescript/index.d.ts.map +1 -1
  25. package/lib/typescript/ui/hooks/index.d.ts +1 -0
  26. package/lib/typescript/ui/hooks/index.d.ts.map +1 -1
  27. package/lib/typescript/ui/hooks/useAuthFetch.d.ts +33 -0
  28. package/lib/typescript/ui/hooks/useAuthFetch.d.ts.map +1 -0
  29. package/lib/typescript/ui/index.d.ts +1 -0
  30. package/lib/typescript/ui/index.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/core/index.ts +8 -0
  33. package/src/index.ts +4 -4
  34. package/src/ui/hooks/index.ts +2 -1
  35. package/src/ui/hooks/useAuthFetch.ts +215 -0
  36. package/src/ui/index.ts +1 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/services",
3
- "version": "5.5.0",
3
+ "version": "5.5.2",
4
4
  "description": "Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀",
5
5
  "main": "lib/commonjs/node/index.js",
6
6
  "module": "lib/module/node/index.js",
package/src/core/index.ts CHANGED
@@ -191,6 +191,14 @@ export class OxyServices {
191
191
  return this.accessToken !== null;
192
192
  }
193
193
 
194
+ /**
195
+ * Get the current access token
196
+ * @returns Current access token or null if not authenticated
197
+ */
198
+ public getAccessToken(): string | null {
199
+ return this.accessToken;
200
+ }
201
+
194
202
  /**
195
203
  * Sets authentication tokens directly (useful for initializing from storage)
196
204
  * @param accessToken - JWT access token
package/src/index.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  OxyProvider,
19
19
  OxyContextProvider,
20
20
  useOxy,
21
+ useAuthFetch,
21
22
 
22
23
  // Components
23
24
  OxySignInButton,
@@ -39,9 +40,8 @@ import { OxyContextState, OxyContextProviderProps } from './ui/context/OxyContex
39
40
  import * as Models from './models/interfaces';
40
41
 
41
42
  // ------------- Core Exports -------------
42
- export default OxyCore; // Default export for backward compatibility
43
- export { OxyServices };
44
- export * from './core';
43
+ export { OxyCore, OxyServices };
44
+ export default OxyServices; // Default export for backward compatibility
45
45
 
46
46
  // ------------- Utility Exports -------------
47
47
  export { DeviceManager } from './utils';
@@ -57,6 +57,7 @@ export {
57
57
  OxyProvider,
58
58
  OxyContextProvider,
59
59
  useOxy,
60
+ useAuthFetch,
60
61
 
61
62
  // Components
62
63
  OxySignInButton,
@@ -65,7 +66,6 @@ export {
65
66
  FollowButton,
66
67
  FontLoader,
67
68
  OxyIcon,
68
- useOxyFollow,
69
69
  useFollow,
70
70
  ProfileScreen,
71
71
  OxyRouter,
@@ -1 +1,2 @@
1
- export { useOxyFollow, useFollow } from './useOxyFollow';
1
+ export { useOxyFollow, useFollow } from './useOxyFollow';
2
+ export { useAuthFetch } from './useAuthFetch';
@@ -0,0 +1,215 @@
1
+ /**
2
+ * Zero Config Authenticated Fetch Hook
3
+ *
4
+ * Simple hook that provides fetch-like API with automatic authentication
5
+ * Leverages the existing useOxy hook and OxyProvider infrastructure
6
+ *
7
+ * Usage:
8
+ * const authFetch = useAuthFetch();
9
+ * const response = await authFetch('/api/protected');
10
+ * const data = await authFetch.get('/api/users');
11
+ */
12
+
13
+ import { useCallback } from 'react';
14
+ import { useOxy } from '../context/OxyContext';
15
+
16
+ export interface AuthFetchOptions extends Omit<RequestInit, 'body'> {
17
+ body?: any; // Allow any type for body, we'll JSON.stringify if needed
18
+ }
19
+
20
+ export interface AuthFetchAPI {
21
+ // Main fetch function (drop-in replacement)
22
+ (input: RequestInfo | URL, init?: AuthFetchOptions): Promise<Response>;
23
+
24
+ // Convenience methods for JSON APIs
25
+ get: (endpoint: string, options?: AuthFetchOptions) => Promise<any>;
26
+ post: (endpoint: string, data?: any, options?: AuthFetchOptions) => Promise<any>;
27
+ put: (endpoint: string, data?: any, options?: AuthFetchOptions) => Promise<any>;
28
+ delete: (endpoint: string, options?: AuthFetchOptions) => Promise<any>;
29
+
30
+ // Access to auth state and methods
31
+ isAuthenticated: boolean;
32
+ user: any;
33
+ login: (username: string, password: string) => Promise<any>;
34
+ logout: () => Promise<void>;
35
+ signUp: (username: string, email: string, password: string) => Promise<any>;
36
+ }
37
+
38
+ /**
39
+ * Hook that provides authenticated fetch functionality
40
+ * Uses the existing OxyServices instance from useOxy context
41
+ */
42
+ export function useAuthFetch(): AuthFetchAPI {
43
+ const { oxyServices, isAuthenticated, user, login, logout, signUp, activeSessionId } = useOxy();
44
+
45
+ // Main fetch function with automatic auth headers
46
+ const authFetch = useCallback(async (input: RequestInfo | URL, init?: AuthFetchOptions): Promise<Response> => {
47
+ const url = resolveURL(input, oxyServices.getBaseURL());
48
+ const options = await addAuthHeaders(init, oxyServices, activeSessionId || undefined);
49
+
50
+ try {
51
+ let response = await fetch(url, options);
52
+
53
+ // Handle token expiry and automatic refresh
54
+ if (response.status === 401 && oxyServices.getCurrentUserId()) {
55
+ // Try to refresh token and retry
56
+ try {
57
+ await oxyServices.refreshTokens();
58
+ const retryOptions = await addAuthHeaders(init, oxyServices, activeSessionId || undefined);
59
+ response = await fetch(url, retryOptions);
60
+ } catch (refreshError) {
61
+ // Refresh failed, user needs to login again
62
+ console.warn('Token refresh failed, user needs to re-authenticate');
63
+ throw new Error('Authentication expired. Please login again.');
64
+ }
65
+ }
66
+
67
+ return response;
68
+ } catch (error) {
69
+ console.error('AuthFetch error:', error);
70
+ throw error;
71
+ }
72
+ }, [oxyServices, activeSessionId]);
73
+
74
+ // JSON convenience methods
75
+ const get = useCallback(async (endpoint: string, options?: AuthFetchOptions) => {
76
+ const response = await authFetch(endpoint, { ...options, method: 'GET' });
77
+ return handleJsonResponse(response);
78
+ }, [authFetch]);
79
+
80
+ const post = useCallback(async (endpoint: string, data?: any, options?: AuthFetchOptions) => {
81
+ const response = await authFetch(endpoint, {
82
+ ...options,
83
+ method: 'POST',
84
+ headers: {
85
+ 'Content-Type': 'application/json',
86
+ ...options?.headers
87
+ },
88
+ body: data ? JSON.stringify(data) : undefined
89
+ });
90
+ return handleJsonResponse(response);
91
+ }, [authFetch]);
92
+
93
+ const put = useCallback(async (endpoint: string, data?: any, options?: AuthFetchOptions) => {
94
+ const response = await authFetch(endpoint, {
95
+ ...options,
96
+ method: 'PUT',
97
+ headers: {
98
+ 'Content-Type': 'application/json',
99
+ ...options?.headers
100
+ },
101
+ body: data ? JSON.stringify(data) : undefined
102
+ });
103
+ return handleJsonResponse(response);
104
+ }, [authFetch]);
105
+
106
+ const del = useCallback(async (endpoint: string, options?: AuthFetchOptions) => {
107
+ const response = await authFetch(endpoint, { ...options, method: 'DELETE' });
108
+ return handleJsonResponse(response);
109
+ }, [authFetch]);
110
+
111
+ // Attach convenience methods and auth state to the main function
112
+ const fetchWithMethods = authFetch as AuthFetchAPI;
113
+ fetchWithMethods.get = get;
114
+ fetchWithMethods.post = post;
115
+ fetchWithMethods.put = put;
116
+ fetchWithMethods.delete = del;
117
+ fetchWithMethods.isAuthenticated = isAuthenticated;
118
+ fetchWithMethods.user = user;
119
+ fetchWithMethods.login = login;
120
+ fetchWithMethods.logout = logout;
121
+ fetchWithMethods.signUp = signUp;
122
+
123
+ return fetchWithMethods;
124
+ }
125
+
126
+ /**
127
+ * Helper functions
128
+ */
129
+
130
+ function resolveURL(input: RequestInfo | URL, baseURL: string): string {
131
+ const url = input.toString();
132
+
133
+ // If it's already a full URL, return as is
134
+ if (url.startsWith('http://') || url.startsWith('https://')) {
135
+ return url;
136
+ }
137
+
138
+ // If it starts with /, it's relative to base URL
139
+ if (url.startsWith('/')) {
140
+ return `${baseURL}${url}`;
141
+ }
142
+
143
+ // Otherwise, append to base URL with /
144
+ return `${baseURL}/${url}`;
145
+ }
146
+
147
+ async function addAuthHeaders(init?: AuthFetchOptions, oxyServices?: any, activeSessionId?: string): Promise<RequestInit> {
148
+ const headers = new Headers(init?.headers);
149
+
150
+ // Add auth header if user is authenticated
151
+ if (oxyServices?.getCurrentUserId() && !headers.has('Authorization')) {
152
+ try {
153
+ // First try to get regular JWT access token
154
+ let accessToken = oxyServices.getAccessToken?.();
155
+
156
+ // If no JWT token but we have a secure session, try to get token from session
157
+ if (!accessToken && activeSessionId) {
158
+ console.log('[Auth API] No JWT token, trying to get token from secure session:', activeSessionId);
159
+ try {
160
+ const tokenData = await oxyServices.getTokenBySession(activeSessionId);
161
+ accessToken = tokenData.accessToken;
162
+ console.log('[Auth API] Got token from session successfully');
163
+ } catch (error) {
164
+ console.warn('[Auth API] Failed to get token from session:', error);
165
+ }
166
+ }
167
+
168
+ if (accessToken) {
169
+ headers.set('Authorization', `Bearer ${accessToken}`);
170
+ } else {
171
+ console.warn('[Auth API] No authentication token available - JWT token:', !!oxyServices.getAccessToken?.(), 'activeSessionId:', activeSessionId);
172
+ }
173
+ } catch (error) {
174
+ console.error('[Auth API] Error getting access token:', error);
175
+ }
176
+ }
177
+
178
+ const body = init?.body;
179
+ const processedBody = body && typeof body === 'object' && !(body instanceof FormData) && !(body instanceof URLSearchParams)
180
+ ? JSON.stringify(body)
181
+ : body;
182
+
183
+ return {
184
+ ...init,
185
+ headers,
186
+ body: processedBody
187
+ };
188
+ }
189
+
190
+ async function handleJsonResponse(response: Response): Promise<any> {
191
+ if (!response.ok) {
192
+ let errorMessage = `HTTP ${response.status}: ${response.statusText}`;
193
+
194
+ try {
195
+ const errorData = await response.json();
196
+ errorMessage = errorData.message || errorData.error || errorMessage;
197
+ } catch {
198
+ // Ignore JSON parsing errors
199
+ }
200
+
201
+ const error = new Error(errorMessage) as any;
202
+ error.status = response.status;
203
+ error.response = response;
204
+ throw error;
205
+ }
206
+
207
+ try {
208
+ return await response.json();
209
+ } catch {
210
+ // If response isn't JSON, return the response itself
211
+ return response;
212
+ }
213
+ }
214
+
215
+ export default useAuthFetch;
package/src/ui/index.ts CHANGED
@@ -64,6 +64,7 @@ export * from './navigation/types';
64
64
 
65
65
  // Hooks
66
66
  export { useOxyFollow, useFollow } from './hooks';
67
+ export { default as useAuthFetch } from './hooks/useAuthFetch';
67
68
 
68
69
  // Screens
69
70
  export { default as ProfileScreen } from './screens/ProfileScreen';