@oxyhq/services 5.9.2 → 5.9.3

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 (202) hide show
  1. package/README.md +1 -33
  2. package/lib/commonjs/core/OxyServices.js +159 -0
  3. package/lib/commonjs/core/OxyServices.js.map +1 -0
  4. package/lib/commonjs/core/OxyServicesMain.js +51 -0
  5. package/lib/commonjs/core/OxyServicesMain.js.map +1 -0
  6. package/lib/commonjs/core/analytics/AnalyticsService.js +67 -0
  7. package/lib/commonjs/core/analytics/AnalyticsService.js.map +1 -0
  8. package/lib/commonjs/core/auth/AuthService.js +526 -0
  9. package/lib/commonjs/core/auth/AuthService.js.map +1 -0
  10. package/lib/commonjs/core/devices/DeviceService.js +61 -0
  11. package/lib/commonjs/core/devices/DeviceService.js.map +1 -0
  12. package/lib/commonjs/core/files/FileService.js +176 -0
  13. package/lib/commonjs/core/files/FileService.js.map +1 -0
  14. package/lib/commonjs/core/index.js +103 -1701
  15. package/lib/commonjs/core/index.js.map +1 -1
  16. package/lib/commonjs/core/karma/KarmaService.js +100 -0
  17. package/lib/commonjs/core/karma/KarmaService.js.map +1 -0
  18. package/lib/commonjs/core/locations/LocationService.js +131 -0
  19. package/lib/commonjs/core/locations/LocationService.js.map +1 -0
  20. package/lib/commonjs/core/payments/PaymentService.js +124 -0
  21. package/lib/commonjs/core/payments/PaymentService.js.map +1 -0
  22. package/lib/commonjs/core/users/UserService.js +234 -0
  23. package/lib/commonjs/core/users/UserService.js.map +1 -0
  24. package/lib/commonjs/index.js +164 -3
  25. package/lib/commonjs/index.js.map +1 -1
  26. package/lib/commonjs/models/session.js +2 -0
  27. package/lib/commonjs/{types/middleware.js.map → models/session.js.map} +1 -1
  28. package/lib/commonjs/ui/context/OxyContext.js +28 -24
  29. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  30. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +2 -2
  31. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  32. package/lib/commonjs/ui/screens/FileManagementScreen.js +12 -12
  33. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  34. package/lib/commonjs/ui/screens/ProfileScreen.js +2 -2
  35. package/lib/commonjs/ui/screens/ProfileScreen.js.map +1 -1
  36. package/lib/commonjs/ui/screens/SessionManagementScreen.js.map +1 -1
  37. package/lib/commonjs/ui/screens/SignInScreen.js +1 -1
  38. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  39. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js +1 -1
  40. package/lib/commonjs/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  41. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js +1 -1
  42. package/lib/commonjs/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  43. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js +1 -1
  44. package/lib/commonjs/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  45. package/lib/commonjs/ui/stores/followStore.js +4 -4
  46. package/lib/commonjs/ui/stores/followStore.js.map +1 -1
  47. package/lib/commonjs/utils/apiUtils.js +93 -0
  48. package/lib/commonjs/utils/apiUtils.js.map +1 -0
  49. package/lib/commonjs/utils/asyncUtils.js +219 -0
  50. package/lib/commonjs/utils/asyncUtils.js.map +1 -0
  51. package/lib/commonjs/utils/errorUtils.js +148 -0
  52. package/lib/commonjs/utils/errorUtils.js.map +1 -0
  53. package/lib/commonjs/utils/hookUtils.js +399 -0
  54. package/lib/commonjs/utils/hookUtils.js.map +1 -0
  55. package/lib/commonjs/utils/loggerUtils.js +160 -0
  56. package/lib/commonjs/utils/loggerUtils.js.map +1 -0
  57. package/lib/commonjs/utils/validationUtils.js +174 -0
  58. package/lib/commonjs/utils/validationUtils.js.map +1 -0
  59. package/lib/module/core/OxyServices.js +153 -0
  60. package/lib/module/core/OxyServices.js.map +1 -0
  61. package/lib/module/core/OxyServicesMain.js +47 -0
  62. package/lib/module/core/OxyServicesMain.js.map +1 -0
  63. package/lib/module/core/analytics/AnalyticsService.js +62 -0
  64. package/lib/module/core/analytics/AnalyticsService.js.map +1 -0
  65. package/lib/module/core/auth/AuthService.js +521 -0
  66. package/lib/module/core/auth/AuthService.js.map +1 -0
  67. package/lib/module/core/devices/DeviceService.js +57 -0
  68. package/lib/module/core/devices/DeviceService.js.map +1 -0
  69. package/lib/module/core/files/FileService.js +171 -0
  70. package/lib/module/core/files/FileService.js.map +1 -0
  71. package/lib/module/core/index.js +25 -1690
  72. package/lib/module/core/index.js.map +1 -1
  73. package/lib/module/core/karma/KarmaService.js +95 -0
  74. package/lib/module/core/karma/KarmaService.js.map +1 -0
  75. package/lib/module/core/locations/LocationService.js +127 -0
  76. package/lib/module/core/locations/LocationService.js.map +1 -0
  77. package/lib/module/core/payments/PaymentService.js +119 -0
  78. package/lib/module/core/payments/PaymentService.js.map +1 -0
  79. package/lib/module/core/users/UserService.js +230 -0
  80. package/lib/module/core/users/UserService.js.map +1 -0
  81. package/lib/module/index.js +8 -4
  82. package/lib/module/index.js.map +1 -1
  83. package/lib/module/models/session.js +2 -0
  84. package/lib/module/{types/middleware.js.map → models/session.js.map} +1 -1
  85. package/lib/module/ui/context/OxyContext.js +28 -24
  86. package/lib/module/ui/context/OxyContext.js.map +1 -1
  87. package/lib/module/ui/screens/AccountSwitcherScreen.js +2 -2
  88. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  89. package/lib/module/ui/screens/FileManagementScreen.js +12 -12
  90. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  91. package/lib/module/ui/screens/ProfileScreen.js +2 -2
  92. package/lib/module/ui/screens/ProfileScreen.js.map +1 -1
  93. package/lib/module/ui/screens/SessionManagementScreen.js.map +1 -1
  94. package/lib/module/ui/screens/SignInScreen.js +1 -1
  95. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  96. package/lib/module/ui/screens/karma/KarmaCenterScreen.js +1 -1
  97. package/lib/module/ui/screens/karma/KarmaCenterScreen.js.map +1 -1
  98. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js +1 -1
  99. package/lib/module/ui/screens/karma/KarmaLeaderboardScreen.js.map +1 -1
  100. package/lib/module/ui/screens/karma/KarmaRulesScreen.js +1 -1
  101. package/lib/module/ui/screens/karma/KarmaRulesScreen.js.map +1 -1
  102. package/lib/module/ui/stores/followStore.js +4 -4
  103. package/lib/module/ui/stores/followStore.js.map +1 -1
  104. package/lib/module/utils/apiUtils.js +85 -0
  105. package/lib/module/utils/apiUtils.js.map +1 -0
  106. package/lib/module/utils/asyncUtils.js +202 -0
  107. package/lib/module/utils/asyncUtils.js.map +1 -0
  108. package/lib/module/utils/errorUtils.js +139 -0
  109. package/lib/module/utils/errorUtils.js.map +1 -0
  110. package/lib/module/utils/hookUtils.js +381 -0
  111. package/lib/module/utils/hookUtils.js.map +1 -0
  112. package/lib/module/utils/loggerUtils.js +149 -0
  113. package/lib/module/utils/loggerUtils.js.map +1 -0
  114. package/lib/module/utils/validationUtils.js +154 -0
  115. package/lib/module/utils/validationUtils.js.map +1 -0
  116. package/lib/typescript/core/OxyServices.d.ts +64 -0
  117. package/lib/typescript/core/OxyServices.d.ts.map +1 -0
  118. package/lib/typescript/core/OxyServicesMain.d.ts +33 -0
  119. package/lib/typescript/core/OxyServicesMain.d.ts.map +1 -0
  120. package/lib/typescript/core/analytics/AnalyticsService.d.ts +26 -0
  121. package/lib/typescript/core/analytics/AnalyticsService.d.ts.map +1 -0
  122. package/lib/typescript/core/auth/AuthService.d.ts +165 -0
  123. package/lib/typescript/core/auth/AuthService.d.ts.map +1 -0
  124. package/lib/typescript/core/devices/DeviceService.d.ts +20 -0
  125. package/lib/typescript/core/devices/DeviceService.d.ts.map +1 -0
  126. package/lib/typescript/core/files/FileService.d.ts +59 -0
  127. package/lib/typescript/core/files/FileService.d.ts.map +1 -0
  128. package/lib/typescript/core/index.d.ts +19 -656
  129. package/lib/typescript/core/index.d.ts.map +1 -1
  130. package/lib/typescript/core/karma/KarmaService.d.ts +50 -0
  131. package/lib/typescript/core/karma/KarmaService.d.ts.map +1 -0
  132. package/lib/typescript/core/locations/LocationService.d.ts +39 -0
  133. package/lib/typescript/core/locations/LocationService.d.ts.map +1 -0
  134. package/lib/typescript/core/payments/PaymentService.d.ts +50 -0
  135. package/lib/typescript/core/payments/PaymentService.d.ts.map +1 -0
  136. package/lib/typescript/core/users/UserService.d.ts +111 -0
  137. package/lib/typescript/core/users/UserService.d.ts.map +1 -0
  138. package/lib/typescript/index.d.ts +7 -3
  139. package/lib/typescript/index.d.ts.map +1 -1
  140. package/lib/typescript/models/{secureSession.d.ts → session.d.ts} +4 -4
  141. package/lib/typescript/models/session.d.ts.map +1 -0
  142. package/lib/typescript/ui/context/OxyContext.d.ts +2 -2
  143. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  144. package/lib/typescript/utils/apiUtils.d.ts +61 -0
  145. package/lib/typescript/utils/apiUtils.d.ts.map +1 -0
  146. package/lib/typescript/utils/asyncUtils.d.ts +64 -0
  147. package/lib/typescript/utils/asyncUtils.d.ts.map +1 -0
  148. package/lib/typescript/utils/errorUtils.d.ts +45 -0
  149. package/lib/typescript/utils/errorUtils.d.ts.map +1 -0
  150. package/lib/typescript/utils/hookUtils.d.ts +102 -0
  151. package/lib/typescript/utils/hookUtils.d.ts.map +1 -0
  152. package/lib/typescript/utils/loggerUtils.d.ts +49 -0
  153. package/lib/typescript/utils/loggerUtils.d.ts.map +1 -0
  154. package/lib/typescript/utils/validationUtils.d.ts +80 -0
  155. package/lib/typescript/utils/validationUtils.d.ts.map +1 -0
  156. package/package.json +2 -8
  157. package/src/core/OxyServices.ts +168 -0
  158. package/src/core/OxyServicesMain.ts +57 -0
  159. package/src/core/analytics/AnalyticsService.ts +64 -0
  160. package/src/core/auth/AuthService.ts +544 -0
  161. package/src/core/devices/DeviceService.ts +55 -0
  162. package/src/core/files/FileService.ts +194 -0
  163. package/src/core/index.ts +27 -1765
  164. package/src/core/karma/KarmaService.ts +104 -0
  165. package/src/core/locations/LocationService.ts +141 -0
  166. package/src/core/payments/PaymentService.ts +133 -0
  167. package/src/core/users/UserService.ts +241 -0
  168. package/src/index.ts +29 -8
  169. package/src/models/{secureSession.ts → session.ts} +5 -5
  170. package/src/ui/context/OxyContext.tsx +34 -30
  171. package/src/ui/screens/AccountSwitcherScreen.tsx +4 -4
  172. package/src/ui/screens/FileManagementScreen.tsx +12 -12
  173. package/src/ui/screens/ProfileScreen.tsx +3 -3
  174. package/src/ui/screens/SessionManagementScreen.tsx +2 -2
  175. package/src/ui/screens/SignInScreen.tsx +1 -1
  176. package/src/ui/screens/karma/KarmaCenterScreen.tsx +2 -2
  177. package/src/ui/screens/karma/KarmaLeaderboardScreen.tsx +3 -3
  178. package/src/ui/screens/karma/KarmaRulesScreen.tsx +3 -3
  179. package/src/ui/stores/followStore.ts +4 -4
  180. package/src/utils/apiUtils.ts +102 -0
  181. package/src/utils/asyncUtils.ts +265 -0
  182. package/src/utils/errorUtils.ts +172 -0
  183. package/src/utils/hookUtils.ts +397 -0
  184. package/src/utils/loggerUtils.ts +153 -0
  185. package/src/utils/validationUtils.ts +158 -0
  186. package/lib/commonjs/middleware.js +0 -17
  187. package/lib/commonjs/middleware.js.map +0 -1
  188. package/lib/commonjs/models/secureSession.js +0 -2
  189. package/lib/commonjs/models/secureSession.js.map +0 -1
  190. package/lib/commonjs/types/middleware.js +0 -6
  191. package/lib/module/middleware.js +0 -12
  192. package/lib/module/middleware.js.map +0 -1
  193. package/lib/module/models/secureSession.js +0 -2
  194. package/lib/module/models/secureSession.js.map +0 -1
  195. package/lib/module/types/middleware.js +0 -4
  196. package/lib/typescript/middleware.d.ts +0 -9
  197. package/lib/typescript/middleware.d.ts.map +0 -1
  198. package/lib/typescript/models/secureSession.d.ts.map +0 -1
  199. package/lib/typescript/types/middleware.d.ts +0 -19
  200. package/lib/typescript/types/middleware.d.ts.map +0 -1
  201. package/src/middleware.ts +0 -11
  202. package/src/types/middleware.ts +0 -20
@@ -0,0 +1,397 @@
1
+ /**
2
+ * React hook utilities for common patterns and state management
3
+ */
4
+
5
+ import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
6
+
7
+ /**
8
+ * Hook for managing async operations with loading, error, and data states
9
+ */
10
+ export function useAsync<T>(
11
+ asyncFn: () => Promise<T>,
12
+ deps: any[] = []
13
+ ) {
14
+ const [data, setData] = useState<T | null>(null);
15
+ const [loading, setLoading] = useState(false);
16
+ const [error, setError] = useState<Error | null>(null);
17
+
18
+ const execute = useCallback(async () => {
19
+ setLoading(true);
20
+ setError(null);
21
+
22
+ try {
23
+ const result = await asyncFn();
24
+ setData(result);
25
+ return result;
26
+ } catch (err) {
27
+ const error = err instanceof Error ? err : new Error(String(err));
28
+ setError(error);
29
+ throw error;
30
+ } finally {
31
+ setLoading(false);
32
+ }
33
+ }, deps);
34
+
35
+ return { data, loading, error, execute };
36
+ }
37
+
38
+ /**
39
+ * Hook for managing async operations that execute on mount
40
+ */
41
+ export function useAsyncEffect<T>(
42
+ asyncFn: () => Promise<T>,
43
+ deps: any[] = []
44
+ ) {
45
+ const [data, setData] = useState<T | null>(null);
46
+ const [loading, setLoading] = useState(true);
47
+ const [error, setError] = useState<Error | null>(null);
48
+
49
+ useEffect(() => {
50
+ let mounted = true;
51
+
52
+ const execute = async () => {
53
+ try {
54
+ const result = await asyncFn();
55
+ if (mounted) {
56
+ setData(result);
57
+ setLoading(false);
58
+ }
59
+ } catch (err) {
60
+ if (mounted) {
61
+ const error = err instanceof Error ? err : new Error(String(err));
62
+ setError(error);
63
+ setLoading(false);
64
+ }
65
+ }
66
+ };
67
+
68
+ execute();
69
+
70
+ return () => {
71
+ mounted = false;
72
+ };
73
+ }, deps);
74
+
75
+ return { data, loading, error };
76
+ }
77
+
78
+ /**
79
+ * Hook for debounced values
80
+ */
81
+ export function useDebounce<T>(value: T, delay: number): T {
82
+ const [debouncedValue, setDebouncedValue] = useState<T>(value);
83
+
84
+ useEffect(() => {
85
+ const handler = setTimeout(() => {
86
+ setDebouncedValue(value);
87
+ }, delay);
88
+
89
+ return () => {
90
+ clearTimeout(handler);
91
+ };
92
+ }, [value, delay]);
93
+
94
+ return debouncedValue;
95
+ }
96
+
97
+ /**
98
+ * Hook for throttled values
99
+ */
100
+ export function useThrottle<T>(value: T, delay: number): T {
101
+ const [throttledValue, setThrottledValue] = useState<T>(value);
102
+ const lastRun = useRef(Date.now());
103
+
104
+ useEffect(() => {
105
+ const handler = setTimeout(() => {
106
+ if (Date.now() - lastRun.current >= delay) {
107
+ setThrottledValue(value);
108
+ lastRun.current = Date.now();
109
+ }
110
+ }, delay - (Date.now() - lastRun.current));
111
+
112
+ return () => {
113
+ clearTimeout(handler);
114
+ };
115
+ }, [value, delay]);
116
+
117
+ return throttledValue;
118
+ }
119
+
120
+ /**
121
+ * Hook for previous value
122
+ */
123
+ export function usePrevious<T>(value: T): T | undefined {
124
+ const ref = useRef<T>();
125
+
126
+ useEffect(() => {
127
+ ref.current = value;
128
+ });
129
+
130
+ return ref.current;
131
+ }
132
+
133
+ /**
134
+ * Hook for boolean state with toggle
135
+ */
136
+ export function useToggle(initialValue: boolean = false) {
137
+ const [value, setValue] = useState(initialValue);
138
+
139
+ const toggle = useCallback(() => setValue(v => !v), []);
140
+ const setTrue = useCallback(() => setValue(true), []);
141
+ const setFalse = useCallback(() => setValue(false), []);
142
+
143
+ return { value, toggle, setTrue, setFalse, setValue };
144
+ }
145
+
146
+ /**
147
+ * Hook for counter state
148
+ */
149
+ export function useCounter(initialValue: number = 0) {
150
+ const [count, setCount] = useState(initialValue);
151
+
152
+ const increment = useCallback(() => setCount(c => c + 1), []);
153
+ const decrement = useCallback(() => setCount(c => c - 1), []);
154
+ const reset = useCallback(() => setCount(initialValue), [initialValue]);
155
+ const setValue = useCallback((value: number) => setCount(value), []);
156
+
157
+ return { count, increment, decrement, reset, setValue };
158
+ }
159
+
160
+ /**
161
+ * Hook for local storage
162
+ */
163
+ export function useLocalStorage<T>(key: string, initialValue: T) {
164
+ const [storedValue, setStoredValue] = useState<T>(() => {
165
+ try {
166
+ const item = window.localStorage.getItem(key);
167
+ return item ? JSON.parse(item) : initialValue;
168
+ } catch (error) {
169
+ console.error(`Error reading localStorage key "${key}":`, error);
170
+ return initialValue;
171
+ }
172
+ });
173
+
174
+ const setValue = useCallback((value: T | ((val: T) => T)) => {
175
+ try {
176
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
177
+ setStoredValue(valueToStore);
178
+ window.localStorage.setItem(key, JSON.stringify(valueToStore));
179
+ } catch (error) {
180
+ console.error(`Error setting localStorage key "${key}":`, error);
181
+ }
182
+ }, [key, storedValue]);
183
+
184
+ return [storedValue, setValue] as const;
185
+ }
186
+
187
+ /**
188
+ * Hook for session storage
189
+ */
190
+ export function useSessionStorage<T>(key: string, initialValue: T) {
191
+ const [storedValue, setStoredValue] = useState<T>(() => {
192
+ try {
193
+ const item = window.sessionStorage.getItem(key);
194
+ return item ? JSON.parse(item) : initialValue;
195
+ } catch (error) {
196
+ console.error(`Error reading sessionStorage key "${key}":`, error);
197
+ return initialValue;
198
+ }
199
+ });
200
+
201
+ const setValue = useCallback((value: T | ((val: T) => T)) => {
202
+ try {
203
+ const valueToStore = value instanceof Function ? value(storedValue) : value;
204
+ setStoredValue(valueToStore);
205
+ window.sessionStorage.setItem(key, JSON.stringify(valueToStore));
206
+ } catch (error) {
207
+ console.error(`Error setting sessionStorage key "${key}":`, error);
208
+ }
209
+ }, [key, storedValue]);
210
+
211
+ return [storedValue, setValue] as const;
212
+ }
213
+
214
+ /**
215
+ * Hook for window size
216
+ */
217
+ export function useWindowSize() {
218
+ const [windowSize, setWindowSize] = useState({
219
+ width: window.innerWidth,
220
+ height: window.innerHeight,
221
+ });
222
+
223
+ useEffect(() => {
224
+ const handleResize = () => {
225
+ setWindowSize({
226
+ width: window.innerWidth,
227
+ height: window.innerHeight,
228
+ });
229
+ };
230
+
231
+ window.addEventListener('resize', handleResize);
232
+ return () => window.removeEventListener('resize', handleResize);
233
+ }, []);
234
+
235
+ return windowSize;
236
+ }
237
+
238
+ /**
239
+ * Hook for scroll position
240
+ */
241
+ export function useScrollPosition() {
242
+ const [scrollPosition, setScrollPosition] = useState(0);
243
+
244
+ useEffect(() => {
245
+ const handleScroll = () => {
246
+ setScrollPosition(window.pageYOffset);
247
+ };
248
+
249
+ window.addEventListener('scroll', handleScroll);
250
+ return () => window.removeEventListener('scroll', handleScroll);
251
+ }, []);
252
+
253
+ return scrollPosition;
254
+ }
255
+
256
+ /**
257
+ * Hook for online/offline status
258
+ */
259
+ export function useOnlineStatus() {
260
+ const [isOnline, setIsOnline] = useState(navigator.onLine);
261
+
262
+ useEffect(() => {
263
+ const handleOnline = () => setIsOnline(true);
264
+ const handleOffline = () => setIsOnline(false);
265
+
266
+ window.addEventListener('online', handleOnline);
267
+ window.addEventListener('offline', handleOffline);
268
+
269
+ return () => {
270
+ window.removeEventListener('online', handleOnline);
271
+ window.removeEventListener('offline', handleOffline);
272
+ };
273
+ }, []);
274
+
275
+ return isOnline;
276
+ }
277
+
278
+ /**
279
+ * Hook for media queries
280
+ */
281
+ export function useMediaQuery(query: string): boolean {
282
+ const [matches, setMatches] = useState(false);
283
+
284
+ useEffect(() => {
285
+ const media = window.matchMedia(query);
286
+ if (media.matches !== matches) {
287
+ setMatches(media.matches);
288
+ }
289
+
290
+ const listener = () => setMatches(media.matches);
291
+ media.addEventListener('change', listener);
292
+
293
+ return () => media.removeEventListener('change', listener);
294
+ }, [matches, query]);
295
+
296
+ return matches;
297
+ }
298
+
299
+ /**
300
+ * Hook for keyboard events
301
+ */
302
+ export function useKeyPress(targetKey: string): boolean {
303
+ const [keyPressed, setKeyPressed] = useState(false);
304
+
305
+ useEffect(() => {
306
+ const downHandler = ({ key }: KeyboardEvent) => {
307
+ if (key === targetKey) {
308
+ setKeyPressed(true);
309
+ }
310
+ };
311
+
312
+ const upHandler = ({ key }: KeyboardEvent) => {
313
+ if (key === targetKey) {
314
+ setKeyPressed(false);
315
+ }
316
+ };
317
+
318
+ window.addEventListener('keydown', downHandler);
319
+ window.addEventListener('keyup', upHandler);
320
+
321
+ return () => {
322
+ window.removeEventListener('keydown', downHandler);
323
+ window.removeEventListener('keyup', upHandler);
324
+ };
325
+ }, [targetKey]);
326
+
327
+ return keyPressed;
328
+ }
329
+
330
+ /**
331
+ * Hook for click outside detection
332
+ */
333
+ export function useClickOutside(ref: React.RefObject<HTMLElement>, handler: () => void) {
334
+ useEffect(() => {
335
+ const listener = (event: MouseEvent | TouchEvent) => {
336
+ if (!ref.current || ref.current.contains(event.target as Node)) {
337
+ return;
338
+ }
339
+ handler();
340
+ };
341
+
342
+ document.addEventListener('mousedown', listener);
343
+ document.addEventListener('touchstart', listener);
344
+
345
+ return () => {
346
+ document.removeEventListener('mousedown', listener);
347
+ document.removeEventListener('touchstart', listener);
348
+ };
349
+ }, [ref, handler]);
350
+ }
351
+
352
+ /**
353
+ * Hook for form validation
354
+ */
355
+ export function useFormValidation<T extends Record<string, any>>(
356
+ initialValues: T,
357
+ validationSchema: (values: T) => Partial<Record<keyof T, string>>
358
+ ) {
359
+ const [values, setValues] = useState<T>(initialValues);
360
+ const [errors, setErrors] = useState<Partial<Record<keyof T, string>>>({});
361
+ const [touched, setTouched] = useState<Partial<Record<keyof T, boolean>>>({});
362
+
363
+ const validate = useCallback((valuesToValidate: T) => {
364
+ return validationSchema(valuesToValidate);
365
+ }, [validationSchema]);
366
+
367
+ const setValue = useCallback((field: keyof T, value: any) => {
368
+ setValues(prev => ({ ...prev, [field]: value }));
369
+ if (touched[field]) {
370
+ const newErrors = validate({ ...values, [field]: value });
371
+ setErrors(prev => ({ ...prev, [field]: newErrors[field] }));
372
+ }
373
+ }, [values, touched, validate]);
374
+
375
+ const setTouchedField = useCallback((field: keyof T) => {
376
+ setTouched(prev => ({ ...prev, [field]: true }));
377
+ const newErrors = validate(values);
378
+ setErrors(prev => ({ ...prev, [field]: newErrors[field] }));
379
+ }, [values, validate]);
380
+
381
+ const isValid = useMemo(() => {
382
+ const validationErrors = validate(values);
383
+ return Object.keys(validationErrors).length === 0;
384
+ }, [values, validate]);
385
+
386
+ return {
387
+ values,
388
+ errors,
389
+ touched,
390
+ isValid,
391
+ setValue,
392
+ setTouchedField,
393
+ setValues,
394
+ setErrors,
395
+ setTouched
396
+ };
397
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Centralized logging utilities for consistent logging across the application
3
+ */
4
+
5
+ export enum LogLevel {
6
+ DEBUG = 0,
7
+ INFO = 1,
8
+ WARN = 2,
9
+ ERROR = 3,
10
+ NONE = 4
11
+ }
12
+
13
+ export interface LogContext {
14
+ component?: string;
15
+ method?: string;
16
+ userId?: string;
17
+ sessionId?: string;
18
+ requestId?: string;
19
+ [key: string]: any;
20
+ }
21
+
22
+ class Logger {
23
+ private level: LogLevel = LogLevel.INFO;
24
+ private isDevelopment: boolean = process.env.NODE_ENV === 'development';
25
+
26
+ setLevel(level: LogLevel): void {
27
+ this.level = level;
28
+ }
29
+
30
+ private shouldLog(level: LogLevel): boolean {
31
+ return level >= this.level;
32
+ }
33
+
34
+ private formatMessage(level: string, message: string, context?: LogContext): string {
35
+ const timestamp = new Date().toISOString();
36
+ const contextStr = context ? ` [${Object.entries(context).map(([k, v]) => `${k}:${v}`).join(', ')}]` : '';
37
+ return `[${timestamp}] ${level}${contextStr}: ${message}`;
38
+ }
39
+
40
+ debug(message: string, context?: LogContext, ...args: any[]): void {
41
+ if (this.shouldLog(LogLevel.DEBUG)) {
42
+ const formattedMessage = this.formatMessage('DEBUG', message, context);
43
+ if (this.isDevelopment) {
44
+ console.log(formattedMessage, ...args);
45
+ }
46
+ }
47
+ }
48
+
49
+ info(message: string, context?: LogContext, ...args: any[]): void {
50
+ if (this.shouldLog(LogLevel.INFO)) {
51
+ const formattedMessage = this.formatMessage('INFO', message, context);
52
+ console.log(formattedMessage, ...args);
53
+ }
54
+ }
55
+
56
+ warn(message: string, context?: LogContext, ...args: any[]): void {
57
+ if (this.shouldLog(LogLevel.WARN)) {
58
+ const formattedMessage = this.formatMessage('WARN', message, context);
59
+ console.warn(formattedMessage, ...args);
60
+ }
61
+ }
62
+
63
+ error(message: string, error?: any, context?: LogContext, ...args: any[]): void {
64
+ if (this.shouldLog(LogLevel.ERROR)) {
65
+ const formattedMessage = this.formatMessage('ERROR', message, context);
66
+ if (error) {
67
+ console.error(formattedMessage, error, ...args);
68
+ } else {
69
+ console.error(formattedMessage, ...args);
70
+ }
71
+ }
72
+ }
73
+
74
+ // Specialized logging methods for common patterns
75
+ auth(message: string, context?: LogContext, ...args: any[]): void {
76
+ this.info(`🔐 ${message}`, { ...context, category: 'auth' }, ...args);
77
+ }
78
+
79
+ api(message: string, context?: LogContext, ...args: any[]): void {
80
+ this.info(`🌐 ${message}`, { ...context, category: 'api' }, ...args);
81
+ }
82
+
83
+ session(message: string, context?: LogContext, ...args: any[]): void {
84
+ this.info(`📱 ${message}`, { ...context, category: 'session' }, ...args);
85
+ }
86
+
87
+ user(message: string, context?: LogContext, ...args: any[]): void {
88
+ this.info(`👤 ${message}`, { ...context, category: 'user' }, ...args);
89
+ }
90
+
91
+ device(message: string, context?: LogContext, ...args: any[]): void {
92
+ this.info(`📱 ${message}`, { ...context, category: 'device' }, ...args);
93
+ }
94
+
95
+ payment(message: string, context?: LogContext, ...args: any[]): void {
96
+ this.info(`💳 ${message}`, { ...context, category: 'payment' }, ...args);
97
+ }
98
+
99
+ // Performance logging
100
+ performance(operation: string, duration: number, context?: LogContext): void {
101
+ const level = duration > 1000 ? LogLevel.WARN : LogLevel.INFO;
102
+ const message = `⏱️ ${operation} completed in ${duration}ms`;
103
+ if (level === LogLevel.WARN) {
104
+ this.warn(message, { ...context, category: 'performance', duration });
105
+ } else {
106
+ this.info(message, { ...context, category: 'performance', duration });
107
+ }
108
+ }
109
+
110
+ // Error logging with stack trace
111
+ errorWithStack(message: string, error: Error, context?: LogContext): void {
112
+ this.error(message, error, { ...context, stack: error.stack });
113
+ }
114
+
115
+ // Group related log messages
116
+ group(label: string, fn: () => void): void {
117
+ if (this.isDevelopment && this.shouldLog(LogLevel.DEBUG)) {
118
+ console.group(label);
119
+ fn();
120
+ console.groupEnd();
121
+ } else {
122
+ fn();
123
+ }
124
+ }
125
+ }
126
+
127
+ // Create singleton instance
128
+ export const logger = new Logger();
129
+
130
+ // Convenience functions for common logging patterns
131
+ export const logAuth = (message: string, context?: LogContext, ...args: any[]) =>
132
+ logger.auth(message, context, ...args);
133
+
134
+ export const logApi = (message: string, context?: LogContext, ...args: any[]) =>
135
+ logger.api(message, context, ...args);
136
+
137
+ export const logSession = (message: string, context?: LogContext, ...args: any[]) =>
138
+ logger.session(message, context, ...args);
139
+
140
+ export const logUser = (message: string, context?: LogContext, ...args: any[]) =>
141
+ logger.user(message, context, ...args);
142
+
143
+ export const logDevice = (message: string, context?: LogContext, ...args: any[]) =>
144
+ logger.device(message, context, ...args);
145
+
146
+ export const logPayment = (message: string, context?: LogContext, ...args: any[]) =>
147
+ logger.payment(message, context, ...args);
148
+
149
+ export const logError = (message: string, error?: any, context?: LogContext, ...args: any[]) =>
150
+ logger.error(message, error, context, ...args);
151
+
152
+ export const logPerformance = (operation: string, duration: number, context?: LogContext) =>
153
+ logger.performance(operation, duration, context);
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Validation utilities for common data validation patterns
3
+ */
4
+
5
+ /**
6
+ * Email validation regex
7
+ */
8
+ export const EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
9
+
10
+ /**
11
+ * Username validation regex (alphanumeric, underscore, dash, 3-30 chars)
12
+ */
13
+ export const USERNAME_REGEX = /^[a-zA-Z0-9_-]{3,30}$/;
14
+
15
+ /**
16
+ * Password validation regex (at least 8 chars, 1 uppercase, 1 lowercase, 1 number)
17
+ */
18
+ export const PASSWORD_REGEX = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d@$!%*?&]{8,}$/;
19
+
20
+ /**
21
+ * Validate email format
22
+ */
23
+ export function isValidEmail(email: string): boolean {
24
+ return EMAIL_REGEX.test(email);
25
+ }
26
+
27
+ /**
28
+ * Validate username format
29
+ */
30
+ export function isValidUsername(username: string): boolean {
31
+ return USERNAME_REGEX.test(username);
32
+ }
33
+
34
+ /**
35
+ * Validate password strength
36
+ */
37
+ export function isValidPassword(password: string): boolean {
38
+ return PASSWORD_REGEX.test(password);
39
+ }
40
+
41
+ /**
42
+ * Validate required string
43
+ */
44
+ export function isRequiredString(value: any): boolean {
45
+ return typeof value === 'string' && value.trim().length > 0;
46
+ }
47
+
48
+ /**
49
+ * Validate required number
50
+ */
51
+ export function isRequiredNumber(value: any): boolean {
52
+ return typeof value === 'number' && !isNaN(value);
53
+ }
54
+
55
+ /**
56
+ * Validate required boolean
57
+ */
58
+ export function isRequiredBoolean(value: any): boolean {
59
+ return typeof value === 'boolean';
60
+ }
61
+
62
+ /**
63
+ * Validate array
64
+ */
65
+ export function isValidArray(value: any): boolean {
66
+ return Array.isArray(value);
67
+ }
68
+
69
+ /**
70
+ * Validate object
71
+ */
72
+ export function isValidObject(value: any): boolean {
73
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
74
+ }
75
+
76
+ /**
77
+ * Validate UUID format
78
+ */
79
+ export function isValidUUID(uuid: string): boolean {
80
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
81
+ return UUID_REGEX.test(uuid);
82
+ }
83
+
84
+ /**
85
+ * Validate URL format
86
+ */
87
+ export function isValidURL(url: string): boolean {
88
+ try {
89
+ new URL(url);
90
+ return true;
91
+ } catch {
92
+ return false;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * Validate date string
98
+ */
99
+ export function isValidDate(dateString: string): boolean {
100
+ const date = new Date(dateString);
101
+ return !isNaN(date.getTime());
102
+ }
103
+
104
+ /**
105
+ * Validate file size (in bytes)
106
+ */
107
+ export function isValidFileSize(size: number, maxSize: number): boolean {
108
+ return size > 0 && size <= maxSize;
109
+ }
110
+
111
+ /**
112
+ * Validate file type
113
+ */
114
+ export function isValidFileType(filename: string, allowedTypes: string[]): boolean {
115
+ const extension = filename.split('.').pop()?.toLowerCase();
116
+ return extension ? allowedTypes.includes(extension) : false;
117
+ }
118
+
119
+ /**
120
+ * Sanitize string input
121
+ */
122
+ export function sanitizeString(input: string): string {
123
+ return input.trim().replace(/[<>]/g, '');
124
+ }
125
+
126
+ /**
127
+ * Sanitize HTML input
128
+ */
129
+ export function sanitizeHTML(input: string): string {
130
+ return input
131
+ .replace(/&/g, '&amp;')
132
+ .replace(/</g, '&lt;')
133
+ .replace(/>/g, '&gt;')
134
+ .replace(/"/g, '&quot;')
135
+ .replace(/'/g, '&#x27;');
136
+ }
137
+
138
+ /**
139
+ * Validate and sanitize user input
140
+ */
141
+ export function validateAndSanitizeUserInput(input: any, type: 'string' | 'email' | 'username'): string | null {
142
+ if (typeof input !== 'string') {
143
+ return null;
144
+ }
145
+
146
+ const sanitized = sanitizeString(input);
147
+
148
+ switch (type) {
149
+ case 'email':
150
+ return isValidEmail(sanitized) ? sanitized : null;
151
+ case 'username':
152
+ return isValidUsername(sanitized) ? sanitized : null;
153
+ case 'string':
154
+ return isRequiredString(sanitized) ? sanitized : null;
155
+ default:
156
+ return null;
157
+ }
158
+ }