@oxyhq/services 5.13.1 → 5.13.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 (204) hide show
  1. package/README.md +71 -0
  2. package/lib/commonjs/core/HttpClient.js +238 -0
  3. package/lib/commonjs/core/HttpClient.js.map +1 -0
  4. package/lib/commonjs/core/OxyServices.js +538 -332
  5. package/lib/commonjs/core/OxyServices.js.map +1 -1
  6. package/lib/commonjs/core/RequestManager.js +199 -0
  7. package/lib/commonjs/core/RequestManager.js.map +1 -0
  8. package/lib/commonjs/core/index.js +38 -1
  9. package/lib/commonjs/core/index.js.map +1 -1
  10. package/lib/commonjs/index.js +36 -0
  11. package/lib/commonjs/index.js.map +1 -1
  12. package/lib/commonjs/ui/components/Avatar.js +94 -27
  13. package/lib/commonjs/ui/components/Avatar.js.map +1 -1
  14. package/lib/commonjs/ui/components/FollowButton.js +1 -0
  15. package/lib/commonjs/ui/components/FollowButton.js.map +1 -1
  16. package/lib/commonjs/ui/components/internal/TextField.js +13 -8
  17. package/lib/commonjs/ui/components/internal/TextField.js.map +1 -1
  18. package/lib/commonjs/ui/context/OxyContext.js +183 -224
  19. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  20. package/lib/commonjs/ui/hooks/useSessionSocket.js +80 -22
  21. package/lib/commonjs/ui/hooks/useSessionSocket.js.map +1 -1
  22. package/lib/commonjs/ui/index.js +4 -1
  23. package/lib/commonjs/ui/index.js.map +1 -1
  24. package/lib/commonjs/ui/screens/AccountSettingsScreen.js +32 -2
  25. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  26. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js +101 -59
  27. package/lib/commonjs/ui/screens/AccountSwitcherScreen.js.map +1 -1
  28. package/lib/commonjs/ui/screens/FileManagementScreen.js +3 -2
  29. package/lib/commonjs/ui/screens/FileManagementScreen.js.map +1 -1
  30. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js +75 -117
  31. package/lib/commonjs/ui/screens/LanguageSelectorScreen.js.map +1 -1
  32. package/lib/commonjs/ui/screens/SignInScreen.js +0 -11
  33. package/lib/commonjs/ui/screens/SignInScreen.js.map +1 -1
  34. package/lib/commonjs/ui/screens/SignUpScreen.js +14 -16
  35. package/lib/commonjs/ui/screens/SignUpScreen.js.map +1 -1
  36. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js +50 -18
  37. package/lib/commonjs/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  38. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js +10 -10
  39. package/lib/commonjs/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  40. package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js +16 -26
  41. package/lib/commonjs/ui/screens/steps/SignInPasswordStep.js.map +1 -1
  42. package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js +104 -212
  43. package/lib/commonjs/ui/screens/steps/SignInUsernameStep.js.map +1 -1
  44. package/lib/commonjs/ui/stores/accountStore.js +237 -0
  45. package/lib/commonjs/ui/stores/accountStore.js.map +1 -0
  46. package/lib/commonjs/ui/stores/authStore.js +2 -1
  47. package/lib/commonjs/ui/stores/authStore.js.map +1 -1
  48. package/lib/commonjs/ui/styles/authStyles.js +14 -7
  49. package/lib/commonjs/ui/styles/authStyles.js.map +1 -1
  50. package/lib/commonjs/utils/asyncUtils.js +9 -22
  51. package/lib/commonjs/utils/asyncUtils.js.map +1 -1
  52. package/lib/commonjs/utils/cache.js +259 -0
  53. package/lib/commonjs/utils/cache.js.map +1 -0
  54. package/lib/commonjs/utils/index.js +99 -0
  55. package/lib/commonjs/utils/index.js.map +1 -1
  56. package/lib/commonjs/utils/languageUtils.js +159 -0
  57. package/lib/commonjs/utils/languageUtils.js.map +1 -0
  58. package/lib/commonjs/utils/requestUtils.js +217 -0
  59. package/lib/commonjs/utils/requestUtils.js.map +1 -0
  60. package/lib/commonjs/utils/sessionUtils.js +191 -0
  61. package/lib/commonjs/utils/sessionUtils.js.map +1 -0
  62. package/lib/module/core/HttpClient.js +232 -0
  63. package/lib/module/core/HttpClient.js.map +1 -0
  64. package/lib/module/core/OxyServices.js +536 -326
  65. package/lib/module/core/OxyServices.js.map +1 -1
  66. package/lib/module/core/RequestManager.js +194 -0
  67. package/lib/module/core/RequestManager.js.map +1 -0
  68. package/lib/module/core/index.js +2 -0
  69. package/lib/module/core/index.js.map +1 -1
  70. package/lib/module/index.js +2 -0
  71. package/lib/module/index.js.map +1 -1
  72. package/lib/module/ui/components/Avatar.js +94 -27
  73. package/lib/module/ui/components/Avatar.js.map +1 -1
  74. package/lib/module/ui/components/FollowButton.js +1 -0
  75. package/lib/module/ui/components/FollowButton.js.map +1 -1
  76. package/lib/module/ui/components/internal/TextField.js +13 -8
  77. package/lib/module/ui/components/internal/TextField.js.map +1 -1
  78. package/lib/module/ui/context/OxyContext.js +182 -223
  79. package/lib/module/ui/context/OxyContext.js.map +1 -1
  80. package/lib/module/ui/hooks/useSessionSocket.js +80 -22
  81. package/lib/module/ui/hooks/useSessionSocket.js.map +1 -1
  82. package/lib/module/ui/index.js +4 -2
  83. package/lib/module/ui/index.js.map +1 -1
  84. package/lib/module/ui/screens/AccountSettingsScreen.js +33 -2
  85. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  86. package/lib/module/ui/screens/AccountSwitcherScreen.js +102 -60
  87. package/lib/module/ui/screens/AccountSwitcherScreen.js.map +1 -1
  88. package/lib/module/ui/screens/FileManagementScreen.js +3 -2
  89. package/lib/module/ui/screens/FileManagementScreen.js.map +1 -1
  90. package/lib/module/ui/screens/LanguageSelectorScreen.js +73 -117
  91. package/lib/module/ui/screens/LanguageSelectorScreen.js.map +1 -1
  92. package/lib/module/ui/screens/SignInScreen.js +0 -11
  93. package/lib/module/ui/screens/SignInScreen.js.map +1 -1
  94. package/lib/module/ui/screens/SignUpScreen.js +14 -16
  95. package/lib/module/ui/screens/SignUpScreen.js.map +1 -1
  96. package/lib/module/ui/screens/WelcomeNewUserScreen.js +50 -18
  97. package/lib/module/ui/screens/WelcomeNewUserScreen.js.map +1 -1
  98. package/lib/module/ui/screens/internal/SignInPasswordStep.js +10 -10
  99. package/lib/module/ui/screens/internal/SignInPasswordStep.js.map +1 -1
  100. package/lib/module/ui/screens/steps/SignInPasswordStep.js +16 -26
  101. package/lib/module/ui/screens/steps/SignInPasswordStep.js.map +1 -1
  102. package/lib/module/ui/screens/steps/SignInUsernameStep.js +105 -214
  103. package/lib/module/ui/screens/steps/SignInUsernameStep.js.map +1 -1
  104. package/lib/module/ui/stores/accountStore.js +229 -0
  105. package/lib/module/ui/stores/accountStore.js.map +1 -0
  106. package/lib/module/ui/stores/authStore.js +2 -1
  107. package/lib/module/ui/stores/authStore.js.map +1 -1
  108. package/lib/module/ui/styles/authStyles.js +14 -7
  109. package/lib/module/ui/styles/authStyles.js.map +1 -1
  110. package/lib/module/utils/asyncUtils.js +10 -22
  111. package/lib/module/utils/asyncUtils.js.map +1 -1
  112. package/lib/module/utils/cache.js +250 -0
  113. package/lib/module/utils/cache.js.map +1 -0
  114. package/lib/module/utils/index.js +7 -0
  115. package/lib/module/utils/index.js.map +1 -1
  116. package/lib/module/utils/languageUtils.js +151 -0
  117. package/lib/module/utils/languageUtils.js.map +1 -0
  118. package/lib/module/utils/requestUtils.js +210 -0
  119. package/lib/module/utils/requestUtils.js.map +1 -0
  120. package/lib/module/utils/sessionUtils.js +180 -0
  121. package/lib/module/utils/sessionUtils.js.map +1 -0
  122. package/lib/typescript/core/HttpClient.d.ts +64 -0
  123. package/lib/typescript/core/HttpClient.d.ts.map +1 -0
  124. package/lib/typescript/core/OxyServices.d.ts +88 -71
  125. package/lib/typescript/core/OxyServices.d.ts.map +1 -1
  126. package/lib/typescript/core/RequestManager.d.ts +67 -0
  127. package/lib/typescript/core/RequestManager.d.ts.map +1 -0
  128. package/lib/typescript/core/index.d.ts +2 -0
  129. package/lib/typescript/core/index.d.ts.map +1 -1
  130. package/lib/typescript/index.d.ts +2 -0
  131. package/lib/typescript/index.d.ts.map +1 -1
  132. package/lib/typescript/models/interfaces.d.ts +15 -0
  133. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  134. package/lib/typescript/models/session.d.ts +1 -0
  135. package/lib/typescript/models/session.d.ts.map +1 -1
  136. package/lib/typescript/ui/components/Avatar.d.ts +6 -7
  137. package/lib/typescript/ui/components/Avatar.d.ts.map +1 -1
  138. package/lib/typescript/ui/components/FollowButton.d.ts.map +1 -1
  139. package/lib/typescript/ui/components/internal/TextField.d.ts.map +1 -1
  140. package/lib/typescript/ui/context/OxyContext.d.ts +4 -0
  141. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  142. package/lib/typescript/ui/hooks/useSessionSocket.d.ts.map +1 -1
  143. package/lib/typescript/ui/index.d.ts +2 -2
  144. package/lib/typescript/ui/index.d.ts.map +1 -1
  145. package/lib/typescript/ui/screens/AccountSettingsScreen.d.ts.map +1 -1
  146. package/lib/typescript/ui/screens/AccountSwitcherScreen.d.ts.map +1 -1
  147. package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts +3 -3
  148. package/lib/typescript/ui/screens/LanguageSelectorScreen.d.ts.map +1 -1
  149. package/lib/typescript/ui/screens/SignInScreen.d.ts.map +1 -1
  150. package/lib/typescript/ui/screens/SignUpScreen.d.ts.map +1 -1
  151. package/lib/typescript/ui/screens/WelcomeNewUserScreen.d.ts.map +1 -1
  152. package/lib/typescript/ui/screens/internal/SignInPasswordStep.d.ts.map +1 -1
  153. package/lib/typescript/ui/screens/steps/SignInPasswordStep.d.ts.map +1 -1
  154. package/lib/typescript/ui/screens/steps/SignInUsernameStep.d.ts.map +1 -1
  155. package/lib/typescript/ui/stores/accountStore.d.ts +34 -0
  156. package/lib/typescript/ui/stores/accountStore.d.ts.map +1 -0
  157. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  158. package/lib/typescript/ui/styles/authStyles.d.ts +18 -2
  159. package/lib/typescript/ui/styles/authStyles.d.ts.map +1 -1
  160. package/lib/typescript/utils/asyncUtils.d.ts +2 -0
  161. package/lib/typescript/utils/asyncUtils.d.ts.map +1 -1
  162. package/lib/typescript/utils/cache.d.ts +128 -0
  163. package/lib/typescript/utils/cache.d.ts.map +1 -0
  164. package/lib/typescript/utils/index.d.ts +4 -0
  165. package/lib/typescript/utils/index.d.ts.map +1 -1
  166. package/lib/typescript/utils/languageUtils.d.ts +38 -0
  167. package/lib/typescript/utils/languageUtils.d.ts.map +1 -0
  168. package/lib/typescript/utils/requestUtils.d.ts +122 -0
  169. package/lib/typescript/utils/requestUtils.d.ts.map +1 -0
  170. package/lib/typescript/utils/sessionUtils.d.ts +55 -0
  171. package/lib/typescript/utils/sessionUtils.d.ts.map +1 -0
  172. package/package.json +1 -1
  173. package/src/core/HttpClient.ts +277 -0
  174. package/src/core/OxyServices.ts +466 -351
  175. package/src/core/RequestManager.ts +240 -0
  176. package/src/core/index.ts +10 -0
  177. package/src/index.ts +10 -0
  178. package/src/models/interfaces.ts +19 -0
  179. package/src/models/session.ts +1 -1
  180. package/src/ui/components/Avatar.tsx +151 -35
  181. package/src/ui/components/FollowButton.tsx +1 -0
  182. package/src/ui/components/internal/TextField.tsx +7 -6
  183. package/src/ui/context/OxyContext.tsx +213 -217
  184. package/src/ui/hooks/useSessionSocket.ts +72 -18
  185. package/src/ui/index.ts +4 -1
  186. package/src/ui/screens/AccountSettingsScreen.tsx +34 -2
  187. package/src/ui/screens/AccountSwitcherScreen.tsx +102 -68
  188. package/src/ui/screens/FileManagementScreen.tsx +1 -1
  189. package/src/ui/screens/LanguageSelectorScreen.tsx +86 -143
  190. package/src/ui/screens/SignInScreen.tsx +0 -7
  191. package/src/ui/screens/SignUpScreen.tsx +14 -15
  192. package/src/ui/screens/WelcomeNewUserScreen.tsx +52 -15
  193. package/src/ui/screens/internal/SignInPasswordStep.tsx +4 -6
  194. package/src/ui/screens/steps/SignInPasswordStep.tsx +4 -8
  195. package/src/ui/screens/steps/SignInUsernameStep.tsx +110 -256
  196. package/src/ui/stores/accountStore.ts +285 -0
  197. package/src/ui/stores/authStore.ts +2 -1
  198. package/src/ui/styles/authStyles.ts +14 -7
  199. package/src/utils/asyncUtils.ts +10 -24
  200. package/src/utils/cache.ts +264 -0
  201. package/src/utils/index.ts +19 -0
  202. package/src/utils/languageUtils.ts +174 -0
  203. package/src/utils/requestUtils.ts +234 -0
  204. package/src/utils/sessionUtils.ts +206 -0
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Request utilities for HTTP clients
3
+ *
4
+ * Provides reusable components for request deduplication, queuing, and logging
5
+ */
6
+ /**
7
+ * Request deduplication - prevents duplicate concurrent requests
8
+ *
9
+ * When multiple requests with the same key are made simultaneously,
10
+ * only one request is executed and all callers receive the same result.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const deduplicator = new RequestDeduplicator();
15
+ *
16
+ * // Multiple calls with same key will share the same promise
17
+ * const promise1 = deduplicator.deduplicate('user-123', () => fetchUser('123'));
18
+ * const promise2 = deduplicator.deduplicate('user-123', () => fetchUser('123'));
19
+ * // promise1 === promise2, only one API call is made
20
+ * ```
21
+ */
22
+ export declare class RequestDeduplicator {
23
+ private pendingRequests;
24
+ /**
25
+ * Deduplicate a request by key
26
+ * @param key Unique key for the request
27
+ * @param requestFn Function that returns a promise
28
+ * @returns Promise that will be shared if key already exists
29
+ */
30
+ deduplicate<T>(key: string, requestFn: () => Promise<T>): Promise<T>;
31
+ /**
32
+ * Clear all pending requests
33
+ */
34
+ clear(): void;
35
+ /**
36
+ * Get number of pending requests
37
+ */
38
+ size(): number;
39
+ }
40
+ /**
41
+ * Request queue with concurrency control
42
+ *
43
+ * Limits the number of concurrent requests and queues excess requests.
44
+ * Useful for rate limiting and preventing request flooding.
45
+ *
46
+ * @example
47
+ * ```typescript
48
+ * const queue = new RequestQueue(5, 100); // Max 5 concurrent, queue up to 100
49
+ *
50
+ * // All requests will be queued and processed with max 5 concurrent
51
+ * await queue.enqueue(() => fetchUser('1'));
52
+ * await queue.enqueue(() => fetchUser('2'));
53
+ * // ...
54
+ * ```
55
+ */
56
+ export declare class RequestQueue {
57
+ private queue;
58
+ private running;
59
+ private maxConcurrent;
60
+ private maxQueueSize;
61
+ /**
62
+ * Create a new request queue
63
+ * @param maxConcurrent Maximum number of concurrent requests (default: 10)
64
+ * @param maxQueueSize Maximum queue size (default: 100)
65
+ */
66
+ constructor(maxConcurrent?: number, maxQueueSize?: number);
67
+ /**
68
+ * Enqueue a request
69
+ * @param requestFn Function that returns a promise
70
+ * @returns Promise that resolves when request completes
71
+ */
72
+ enqueue<T>(requestFn: () => Promise<T>): Promise<T>;
73
+ /**
74
+ * Process queued requests
75
+ */
76
+ private process;
77
+ /**
78
+ * Clear all queued requests
79
+ */
80
+ clear(): void;
81
+ /**
82
+ * Get queue size
83
+ */
84
+ size(): number;
85
+ /**
86
+ * Get number of currently running requests
87
+ */
88
+ runningCount(): number;
89
+ }
90
+ /**
91
+ * Simple logger with level support
92
+ *
93
+ * Lightweight logger for HTTP clients and utilities.
94
+ * For more advanced logging, use loggerUtils.ts
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * const logger = new SimpleLogger(true, 'debug');
99
+ * logger.debug('Debug message');
100
+ * logger.info('Info message');
101
+ * logger.error('Error message');
102
+ * ```
103
+ */
104
+ export declare class SimpleLogger {
105
+ private enabled;
106
+ private level;
107
+ private prefix;
108
+ /**
109
+ * Create a new simple logger
110
+ * @param enabled Whether logging is enabled
111
+ * @param level Minimum log level
112
+ * @param prefix Prefix for log messages (default: '')
113
+ */
114
+ constructor(enabled?: boolean, level?: string, prefix?: string);
115
+ private shouldLog;
116
+ private formatMessage;
117
+ error(...args: any[]): void;
118
+ warn(...args: any[]): void;
119
+ info(...args: any[]): void;
120
+ debug(...args: any[]): void;
121
+ }
122
+ //# sourceMappingURL=requestUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requestUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/requestUtils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,eAAe,CAAmC;IAE1D;;;;;OAKG;IACG,WAAW,CAAC,CAAC,EACjB,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC,CAAC,CAAC;IAeb;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,IAAI,IAAI,MAAM;CAGf;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,YAAY,CAAS;IAE7B;;;;OAIG;gBACS,aAAa,GAAE,MAAW,EAAE,YAAY,GAAE,MAAY;IAKlE;;;;OAIG;IACG,OAAO,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAmBzD;;OAEG;YACW,OAAO;IAmBrB;;OAEG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACH,IAAI,IAAI,MAAM;IAId;;OAEG;IACH,YAAY,IAAI,MAAM;CAGvB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,KAAK,CAA+C;IAC5D,OAAO,CAAC,MAAM,CAAS;IAEvB;;;;;OAKG;gBAED,OAAO,GAAE,OAAe,EACxB,KAAK,GAAE,MAAgB,EACvB,MAAM,GAAE,MAAW;IAOrB,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,aAAa;IAIrB,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;IAM3B,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;IAM1B,IAAI,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;IAM1B,KAAK,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI;CAK5B"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Session management utilities
3
+ *
4
+ * Provides consistent session normalization, deduplication, and sorting
5
+ * to ensure sessions are always displayed in a predictable order.
6
+ */
7
+ import type { ClientSession } from '../models/session';
8
+ /**
9
+ * Normalize a session to ensure all required fields are present
10
+ */
11
+ export declare function normalizeSession(session: Partial<ClientSession> & {
12
+ sessionId: string;
13
+ }): ClientSession;
14
+ /**
15
+ * Compare two sessions for equality
16
+ */
17
+ export declare function sessionsEqual(a: ClientSession, b: ClientSession): boolean;
18
+ /**
19
+ * Sort sessions by lastActive (most recent first), then by sessionId for stability
20
+ */
21
+ export declare function sortSessions(sessions: ClientSession[]): ClientSession[];
22
+ /**
23
+ * Deduplicate sessions by sessionId, keeping the most recent version
24
+ */
25
+ export declare function deduplicateSessions(sessions: ClientSession[]): ClientSession[];
26
+ /**
27
+ * Deduplicate sessions by userId, keeping only one session per user
28
+ * Priority: 1) Active session (if provided), 2) Most recent session
29
+ * This prevents showing duplicate accounts for the same user
30
+ */
31
+ export declare function deduplicateSessionsByUserId(sessions: ClientSession[], activeSessionId?: string | null): ClientSession[];
32
+ /**
33
+ * Normalize, deduplicate, and sort sessions
34
+ * This ensures consistent session ordering across the application
35
+ *
36
+ * @param sessions - Array of sessions to normalize
37
+ * @param activeSessionId - Optional active session ID to prioritize
38
+ * @param deduplicateByUserId - If true, deduplicate by userId (one account per user). Default: true
39
+ */
40
+ export declare function normalizeAndSortSessions(sessions: ClientSession[], activeSessionId?: string | null, deduplicateByUserId?: boolean): ClientSession[];
41
+ /**
42
+ * Merge two session arrays, prioritizing newer data
43
+ * Returns normalized, deduplicated, and sorted sessions
44
+ *
45
+ * @param existing - Existing sessions array
46
+ * @param incoming - New sessions to merge in
47
+ * @param activeSessionId - Optional active session ID to prioritize
48
+ * @param deduplicateByUserId - If true, deduplicate by userId (one account per user). Default: true
49
+ */
50
+ export declare function mergeSessions(existing: ClientSession[], incoming: ClientSession[], activeSessionId?: string | null, deduplicateByUserId?: boolean): ClientSession[];
51
+ /**
52
+ * Check if two session arrays are equal (same sessionIds in same order)
53
+ */
54
+ export declare function sessionsArraysEqual(a: ClientSession[], b: ClientSession[]): boolean;
55
+ //# sourceMappingURL=sessionUtils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessionUtils.d.ts","sourceRoot":"","sources":["../../../src/utils/sessionUtils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG,aAAa,CASvG;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAEzE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAWvE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAkB9E;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,aAAa,EAAE,EACzB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,GAC9B,aAAa,EAAE,CAiCjB;AAED;;;;;;;GAOG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,aAAa,EAAE,EACzB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,EAC/B,mBAAmB,GAAE,OAAc,GAClC,aAAa,EAAE,CAgBjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,aAAa,EAAE,EACzB,QAAQ,EAAE,aAAa,EAAE,EACzB,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,EAC/B,mBAAmB,GAAE,OAAc,GAClC,aAAa,EAAE,CAgCjB;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,aAAa,EAAE,EAAE,CAAC,EAAE,aAAa,EAAE,GAAG,OAAO,CAWnF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxyhq/services",
3
- "version": "5.13.1",
3
+ "version": "5.13.3",
4
4
  "description": "Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀",
5
5
  "main": "lib/commonjs/index.js",
6
6
  "module": "lib/module/index.js",
@@ -0,0 +1,277 @@
1
+ /**
2
+ * HTTP Client Service
3
+ *
4
+ * Handles all HTTP communication with authentication, interceptors, and error handling.
5
+ * This is the single source of truth for making authenticated HTTP requests.
6
+ */
7
+
8
+ import axios, { type AxiosInstance, type InternalAxiosRequestConfig, type AxiosResponse } from 'axios';
9
+ import { jwtDecode } from 'jwt-decode';
10
+ import type { OxyConfig } from '../models/interfaces';
11
+ import { handleHttpError } from '../utils/errorUtils';
12
+ import { SimpleLogger } from '../utils/requestUtils';
13
+
14
+ interface JwtPayload {
15
+ exp?: number;
16
+ userId?: string;
17
+ id?: string;
18
+ sessionId?: string;
19
+ [key: string]: any;
20
+ }
21
+
22
+ /**
23
+ * Token store for authentication
24
+ */
25
+ class TokenStore {
26
+ private static instance: TokenStore;
27
+ private accessToken: string | null = null;
28
+ private refreshToken: string | null = null;
29
+
30
+ private constructor() {}
31
+
32
+ static getInstance(): TokenStore {
33
+ if (!TokenStore.instance) {
34
+ TokenStore.instance = new TokenStore();
35
+ }
36
+ return TokenStore.instance;
37
+ }
38
+
39
+ setTokens(accessToken: string, refreshToken = ''): void {
40
+ this.accessToken = accessToken;
41
+ this.refreshToken = refreshToken;
42
+ }
43
+
44
+ getAccessToken(): string | null {
45
+ return this.accessToken;
46
+ }
47
+
48
+ getRefreshToken(): string | null {
49
+ return this.refreshToken;
50
+ }
51
+
52
+ clearTokens(): void {
53
+ this.accessToken = null;
54
+ this.refreshToken = null;
55
+ }
56
+
57
+ hasAccessToken(): boolean {
58
+ return !!this.accessToken;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * HTTP Client Service
64
+ *
65
+ * Manages Axios instance with authentication interceptors.
66
+ * All HTTP requests should go through this service to ensure authentication.
67
+ */
68
+ export class HttpClient {
69
+ private client: AxiosInstance;
70
+ private tokenStore: TokenStore;
71
+ private logger: SimpleLogger;
72
+ private baseURL: string;
73
+
74
+ constructor(config: OxyConfig) {
75
+ this.baseURL = config.baseURL;
76
+ this.tokenStore = TokenStore.getInstance();
77
+ this.logger = new SimpleLogger(
78
+ config.enableLogging || false,
79
+ config.logLevel || 'error',
80
+ 'HttpClient'
81
+ );
82
+
83
+ const timeout = config.requestTimeout || 5000;
84
+
85
+ // Create Axios instance with optimized configuration
86
+ this.client = axios.create({
87
+ baseURL: config.baseURL,
88
+ timeout,
89
+ headers: {
90
+ 'Accept': 'application/json',
91
+ },
92
+ // Enable HTTP keep-alive for connection reuse (Node.js only)
93
+ ...(typeof process !== 'undefined' &&
94
+ process.env &&
95
+ typeof window === 'undefined' &&
96
+ typeof require !== 'undefined' ? {
97
+ httpAgent: new (require('http').Agent)({
98
+ keepAlive: true,
99
+ keepAliveMsecs: 1000,
100
+ maxSockets: 50
101
+ }),
102
+ httpsAgent: new (require('https').Agent)({
103
+ keepAlive: true,
104
+ keepAliveMsecs: 1000,
105
+ maxSockets: 50
106
+ }),
107
+ } : {}),
108
+ });
109
+
110
+ this.setupInterceptors();
111
+ }
112
+
113
+ /**
114
+ * Setup axios interceptors for authentication and error handling
115
+ */
116
+ private setupInterceptors(): void {
117
+ // Request interceptor: Add authentication header
118
+ this.client.interceptors.request.use(
119
+ async (req: InternalAxiosRequestConfig) => {
120
+ const accessToken = this.tokenStore.getAccessToken();
121
+ if (!accessToken) {
122
+ return req;
123
+ }
124
+
125
+ try {
126
+ const decoded = jwtDecode<JwtPayload>(accessToken);
127
+ const currentTime = Math.floor(Date.now() / 1000);
128
+
129
+ // If token expires in less than 60 seconds, refresh it
130
+ if (decoded.exp && decoded.exp - currentTime < 60) {
131
+ if (decoded.sessionId) {
132
+ try {
133
+ // Create a new axios instance to avoid interceptor recursion
134
+ const refreshClient = axios.create({
135
+ baseURL: this.client.defaults.baseURL,
136
+ timeout: this.client.defaults.timeout,
137
+ });
138
+ const res = await refreshClient.get(`/api/session/token/${decoded.sessionId}`);
139
+ this.tokenStore.setTokens(res.data.accessToken);
140
+ req.headers.Authorization = `Bearer ${res.data.accessToken}`;
141
+ this.logger.debug('Token refreshed');
142
+ } catch (refreshError) {
143
+ // If refresh fails, use current token anyway
144
+ req.headers.Authorization = `Bearer ${accessToken}`;
145
+ this.logger.warn('Token refresh failed, using current token');
146
+ }
147
+ } else {
148
+ req.headers.Authorization = `Bearer ${accessToken}`;
149
+ }
150
+ } else {
151
+ req.headers.Authorization = `Bearer ${accessToken}`;
152
+ }
153
+ } catch (error) {
154
+ this.logger.error('Error processing token:', error);
155
+ // Even if there's an error, still try to use the token
156
+ req.headers.Authorization = `Bearer ${accessToken}`;
157
+ }
158
+
159
+ return req;
160
+ },
161
+ (error) => {
162
+ this.logger.error('Request interceptor error:', error);
163
+ return Promise.reject(error);
164
+ }
165
+ );
166
+
167
+ // Response interceptor: Handle auth errors
168
+ this.client.interceptors.response.use(
169
+ (response) => response,
170
+ (error) => {
171
+ if (error.response?.status === 401) {
172
+ this.logger.warn('401 Unauthorized, clearing tokens');
173
+ this.tokenStore.clearTokens();
174
+ }
175
+ return Promise.reject(error);
176
+ }
177
+ );
178
+ }
179
+
180
+ /**
181
+ * Get the underlying Axios instance
182
+ * Use this only when you need direct access to Axios features
183
+ */
184
+ getAxiosInstance(): AxiosInstance {
185
+ return this.client;
186
+ }
187
+
188
+ /**
189
+ * Make a raw HTTP request (no caching, deduplication, etc.)
190
+ * Use this for requests that need to bypass performance features
191
+ */
192
+ async request<T = any>(config: {
193
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
194
+ url: string;
195
+ data?: any;
196
+ params?: any;
197
+ timeout?: number;
198
+ signal?: AbortSignal;
199
+ }): Promise<T> {
200
+ try {
201
+ const response = await this.client.request<T>({
202
+ method: config.method,
203
+ url: config.url,
204
+ data: config.data,
205
+ params: config.params,
206
+ timeout: config.timeout,
207
+ signal: config.signal,
208
+ });
209
+
210
+ // Unwrap standardized API response format: { data: ... }
211
+ // This handles responses from sendSuccess() and sendPaginated() helpers
212
+ const responseData = response.data as any;
213
+
214
+ // Handle paginated responses: { data: [...], pagination: {...} }
215
+ // Return the data array directly - the calling method will wrap it appropriately
216
+ if (responseData && typeof responseData === 'object' && 'data' in responseData && 'pagination' in responseData) {
217
+ // For paginated responses, return the data array directly
218
+ // The calling methods like getUserFollowers/getUserFollowing will handle wrapping
219
+ // We return the whole response so methods can access both data and pagination
220
+ return responseData as T;
221
+ }
222
+
223
+ // Handle regular success responses: { data: ... }
224
+ if (responseData && typeof responseData === 'object' && 'data' in responseData) {
225
+ return responseData.data as T;
226
+ }
227
+
228
+ // Return as-is for responses that don't use sendSuccess wrapper
229
+ return responseData as T;
230
+ } catch (error) {
231
+ throw handleHttpError(error);
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Get base URL
237
+ */
238
+ getBaseURL(): string {
239
+ return this.baseURL;
240
+ }
241
+
242
+ /**
243
+ * Set authentication tokens
244
+ */
245
+ setTokens(accessToken: string, refreshToken = ''): void {
246
+ this.tokenStore.setTokens(accessToken, refreshToken);
247
+ }
248
+
249
+ /**
250
+ * Clear authentication tokens
251
+ */
252
+ clearTokens(): void {
253
+ this.tokenStore.clearTokens();
254
+ }
255
+
256
+ /**
257
+ * Get access token
258
+ */
259
+ getAccessToken(): string | null {
260
+ return this.tokenStore.getAccessToken();
261
+ }
262
+
263
+ /**
264
+ * Check if has access token
265
+ */
266
+ hasAccessToken(): boolean {
267
+ return this.tokenStore.hasAccessToken();
268
+ }
269
+
270
+ // Test-only utility to reset global tokens between jest tests
271
+ static __resetTokensForTests(): void {
272
+ try {
273
+ TokenStore.getInstance().clearTokens();
274
+ } catch {}
275
+ }
276
+ }
277
+