@oxyhq/services 5.16.33 → 5.16.35

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 (240) hide show
  1. package/README.md +26 -8
  2. package/lib/commonjs/core/OxyServices.base.js.map +1 -1
  3. package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -1
  4. package/lib/commonjs/core/mixins/OxyServices.utility.js.map +1 -1
  5. package/lib/commonjs/core/services/AuthService.js +156 -0
  6. package/lib/commonjs/core/services/AuthService.js.map +1 -0
  7. package/lib/commonjs/core/services/SessionService.js +1 -2
  8. package/lib/commonjs/core/services/SessionService.js.map +1 -1
  9. package/lib/commonjs/core/services/SessionTransportService.js +64 -0
  10. package/lib/commonjs/core/services/SessionTransportService.js.map +1 -0
  11. package/lib/commonjs/core/services/TokenService.js +9 -17
  12. package/lib/commonjs/core/services/TokenService.js.map +1 -1
  13. package/lib/commonjs/core/services/UserService.js +123 -0
  14. package/lib/commonjs/core/services/UserService.js.map +1 -0
  15. package/lib/commonjs/core/services/index.js +34 -0
  16. package/lib/commonjs/core/services/index.js.map +1 -0
  17. package/lib/commonjs/crypto/index.js.map +1 -1
  18. package/lib/commonjs/crypto/keyManager.js +3 -2
  19. package/lib/commonjs/crypto/keyManager.js.map +1 -1
  20. package/lib/commonjs/crypto/signatureService.js +28 -122
  21. package/lib/commonjs/crypto/signatureService.js.map +1 -1
  22. package/lib/commonjs/index.js +12 -0
  23. package/lib/commonjs/index.js.map +1 -1
  24. package/lib/commonjs/models/interfaces.js +11 -11
  25. package/lib/commonjs/models/interfaces.js.map +1 -1
  26. package/lib/commonjs/shared/crypto/messageBuilders.js +79 -0
  27. package/lib/commonjs/shared/crypto/messageBuilders.js.map +1 -0
  28. package/lib/commonjs/shared/crypto/platform.js +118 -0
  29. package/lib/commonjs/shared/crypto/platform.js.map +1 -0
  30. package/lib/commonjs/shared/crypto/signature.js +191 -0
  31. package/lib/commonjs/shared/crypto/signature.js.map +1 -0
  32. package/lib/commonjs/shared/index.js +94 -0
  33. package/lib/commonjs/shared/index.js.map +1 -0
  34. package/lib/commonjs/shared/models/index.js +2 -0
  35. package/lib/commonjs/shared/models/index.js.map +1 -0
  36. package/lib/commonjs/shared/transport/index.js +260 -0
  37. package/lib/commonjs/shared/transport/index.js.map +1 -0
  38. package/lib/commonjs/shared/utils/index.js +82 -0
  39. package/lib/commonjs/shared/utils/index.js.map +1 -0
  40. package/lib/commonjs/ui/context/OxyContext.js +4 -40
  41. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  42. package/lib/commonjs/ui/context/hooks/useAuthOperations.js +23 -61
  43. package/lib/commonjs/ui/context/hooks/useAuthOperations.js.map +1 -1
  44. package/lib/commonjs/ui/context/hooks/useLanguageManagement.js.map +1 -1
  45. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js +4 -12
  46. package/lib/commonjs/ui/hooks/queries/useServicesQueries.js.map +1 -1
  47. package/lib/commonjs/ui/hooks/useLanguageManagement.js.map +1 -1
  48. package/lib/commonjs/ui/hooks/useSessionManagement.js +0 -8
  49. package/lib/commonjs/ui/hooks/useSessionManagement.js.map +1 -1
  50. package/lib/commonjs/ui/index.js +2 -0
  51. package/lib/commonjs/ui/index.js.map +1 -1
  52. package/lib/commonjs/ui/screens/AccountSettingsScreen.js.map +1 -1
  53. package/lib/commonjs/ui/screens/OxyAuthScreen.js +2 -11
  54. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  55. package/lib/commonjs/ui/utils/sessionHelpers.js +11 -26
  56. package/lib/commonjs/ui/utils/sessionHelpers.js.map +1 -1
  57. package/lib/commonjs/utils/sessionUtils.js +1 -8
  58. package/lib/commonjs/utils/sessionUtils.js.map +1 -1
  59. package/lib/module/core/OxyServices.base.js.map +1 -1
  60. package/lib/module/core/mixins/OxyServices.user.js.map +1 -1
  61. package/lib/module/core/mixins/OxyServices.utility.js.map +1 -1
  62. package/lib/module/core/services/AuthService.js +151 -0
  63. package/lib/module/core/services/AuthService.js.map +1 -0
  64. package/lib/module/core/services/SessionService.js +1 -2
  65. package/lib/module/core/services/SessionService.js.map +1 -1
  66. package/lib/module/core/services/SessionTransportService.js +59 -0
  67. package/lib/module/core/services/SessionTransportService.js.map +1 -0
  68. package/lib/module/core/services/TokenService.js +9 -17
  69. package/lib/module/core/services/TokenService.js.map +1 -1
  70. package/lib/module/core/services/UserService.js +118 -0
  71. package/lib/module/core/services/UserService.js.map +1 -0
  72. package/lib/module/core/services/index.js +16 -0
  73. package/lib/module/core/services/index.js.map +1 -0
  74. package/lib/module/crypto/index.js +9 -0
  75. package/lib/module/crypto/index.js.map +1 -1
  76. package/lib/module/crypto/keyManager.js +3 -2
  77. package/lib/module/crypto/keyManager.js.map +1 -1
  78. package/lib/module/crypto/signatureService.js +26 -122
  79. package/lib/module/crypto/signatureService.js.map +1 -1
  80. package/lib/module/index.js +2 -0
  81. package/lib/module/index.js.map +1 -1
  82. package/lib/module/models/interfaces.js +11 -11
  83. package/lib/module/models/interfaces.js.map +1 -1
  84. package/lib/module/shared/crypto/messageBuilders.js +70 -0
  85. package/lib/module/shared/crypto/messageBuilders.js.map +1 -0
  86. package/lib/module/shared/crypto/platform.js +112 -0
  87. package/lib/module/shared/crypto/platform.js.map +1 -0
  88. package/lib/module/shared/crypto/signature.js +186 -0
  89. package/lib/module/shared/crypto/signature.js.map +1 -0
  90. package/lib/module/shared/index.js +30 -0
  91. package/lib/module/shared/index.js.map +1 -0
  92. package/lib/module/shared/models/index.js +2 -0
  93. package/lib/module/shared/models/index.js.map +1 -0
  94. package/lib/module/shared/transport/index.js +254 -0
  95. package/lib/module/shared/transport/index.js.map +1 -0
  96. package/lib/module/shared/utils/index.js +74 -0
  97. package/lib/module/shared/utils/index.js.map +1 -0
  98. package/lib/module/ui/context/OxyContext.js +4 -40
  99. package/lib/module/ui/context/OxyContext.js.map +1 -1
  100. package/lib/module/ui/context/hooks/useAuthOperations.js +23 -61
  101. package/lib/module/ui/context/hooks/useAuthOperations.js.map +1 -1
  102. package/lib/module/ui/context/hooks/useLanguageManagement.js.map +1 -1
  103. package/lib/module/ui/hooks/queries/useServicesQueries.js +5 -13
  104. package/lib/module/ui/hooks/queries/useServicesQueries.js.map +1 -1
  105. package/lib/module/ui/hooks/useLanguageManagement.js.map +1 -1
  106. package/lib/module/ui/hooks/useSessionManagement.js +0 -8
  107. package/lib/module/ui/hooks/useSessionManagement.js.map +1 -1
  108. package/lib/module/ui/index.js +1 -0
  109. package/lib/module/ui/index.js.map +1 -1
  110. package/lib/module/ui/screens/AccountSettingsScreen.js.map +1 -1
  111. package/lib/module/ui/screens/OxyAuthScreen.js +2 -11
  112. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  113. package/lib/module/ui/utils/sessionHelpers.js +11 -26
  114. package/lib/module/ui/utils/sessionHelpers.js.map +1 -1
  115. package/lib/module/utils/sessionUtils.js +1 -8
  116. package/lib/module/utils/sessionUtils.js.map +1 -1
  117. package/lib/typescript/core/OxyServices.base.d.ts.map +1 -1
  118. package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -1
  119. package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -1
  120. package/lib/typescript/core/mixins/OxyServices.auth.d.ts +1 -1
  121. package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -1
  122. package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -1
  123. package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -1
  124. package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -1
  125. package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -1
  126. package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -1
  127. package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -1
  128. package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -1
  129. package/lib/typescript/core/mixins/OxyServices.security.d.ts.map +1 -1
  130. package/lib/typescript/core/mixins/OxyServices.user.d.ts +2 -1
  131. package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -1
  132. package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -1
  133. package/lib/typescript/core/mixins/index.d.ts +13 -13
  134. package/lib/typescript/core/mixins/index.d.ts.map +1 -1
  135. package/lib/typescript/core/services/AuthService.d.ts +50 -0
  136. package/lib/typescript/core/services/AuthService.d.ts.map +1 -0
  137. package/lib/typescript/core/services/SessionService.d.ts +3 -5
  138. package/lib/typescript/core/services/SessionService.d.ts.map +1 -1
  139. package/lib/typescript/core/services/SessionTransportService.d.ts +31 -0
  140. package/lib/typescript/core/services/SessionTransportService.d.ts.map +1 -0
  141. package/lib/typescript/core/services/TokenService.d.ts +3 -8
  142. package/lib/typescript/core/services/TokenService.d.ts.map +1 -1
  143. package/lib/typescript/core/services/UserService.d.ts +39 -0
  144. package/lib/typescript/core/services/UserService.d.ts.map +1 -0
  145. package/lib/typescript/core/services/index.d.ts +13 -0
  146. package/lib/typescript/core/services/index.d.ts.map +1 -0
  147. package/lib/typescript/crypto/index.d.ts +9 -0
  148. package/lib/typescript/crypto/index.d.ts.map +1 -1
  149. package/lib/typescript/crypto/keyManager.d.ts.map +1 -1
  150. package/lib/typescript/crypto/signatureService.d.ts +10 -13
  151. package/lib/typescript/crypto/signatureService.d.ts.map +1 -1
  152. package/lib/typescript/index.d.ts +2 -1
  153. package/lib/typescript/index.d.ts.map +1 -1
  154. package/lib/typescript/models/interfaces.d.ts +15 -69
  155. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  156. package/lib/typescript/models/session.d.ts +2 -4
  157. package/lib/typescript/models/session.d.ts.map +1 -1
  158. package/lib/typescript/shared/crypto/messageBuilders.d.ts +38 -0
  159. package/lib/typescript/shared/crypto/messageBuilders.d.ts.map +1 -0
  160. package/lib/typescript/shared/crypto/platform.d.ts +54 -0
  161. package/lib/typescript/shared/crypto/platform.d.ts.map +1 -0
  162. package/lib/typescript/shared/crypto/signature.d.ts +72 -0
  163. package/lib/typescript/shared/crypto/signature.d.ts.map +1 -0
  164. package/lib/typescript/shared/index.d.ts +20 -0
  165. package/lib/typescript/shared/index.d.ts.map +1 -0
  166. package/lib/typescript/shared/models/index.d.ts +163 -0
  167. package/lib/typescript/shared/models/index.d.ts.map +1 -0
  168. package/lib/typescript/shared/transport/index.d.ts +73 -0
  169. package/lib/typescript/shared/transport/index.d.ts.map +1 -0
  170. package/lib/typescript/shared/utils/index.d.ts +28 -0
  171. package/lib/typescript/shared/utils/index.d.ts.map +1 -0
  172. package/lib/typescript/ui/context/OxyContext.d.ts +2 -1
  173. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  174. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts +2 -1
  175. package/lib/typescript/ui/context/hooks/useAuthOperations.d.ts.map +1 -1
  176. package/lib/typescript/ui/context/hooks/useLanguageManagement.d.ts +2 -1
  177. package/lib/typescript/ui/context/hooks/useLanguageManagement.d.ts.map +1 -1
  178. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts +1 -1
  179. package/lib/typescript/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  180. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts +1 -1
  181. package/lib/typescript/ui/hooks/queries/useAccountQueries.d.ts.map +1 -1
  182. package/lib/typescript/ui/hooks/queries/useServicesQueries.d.ts.map +1 -1
  183. package/lib/typescript/ui/hooks/useLanguageManagement.d.ts +2 -1
  184. package/lib/typescript/ui/hooks/useLanguageManagement.d.ts.map +1 -1
  185. package/lib/typescript/ui/hooks/useSessionManagement.d.ts +2 -1
  186. package/lib/typescript/ui/hooks/useSessionManagement.d.ts.map +1 -1
  187. package/lib/typescript/ui/index.d.ts +1 -1
  188. package/lib/typescript/ui/index.d.ts.map +1 -1
  189. package/lib/typescript/ui/screens/OxyAuthScreen.d.ts.map +1 -1
  190. package/lib/typescript/ui/stores/authStore.d.ts +1 -1
  191. package/lib/typescript/ui/stores/authStore.d.ts.map +1 -1
  192. package/lib/typescript/ui/utils/avatarUtils.d.ts +1 -1
  193. package/lib/typescript/ui/utils/avatarUtils.d.ts.map +1 -1
  194. package/lib/typescript/ui/utils/sessionHelpers.d.ts +2 -6
  195. package/lib/typescript/ui/utils/sessionHelpers.d.ts.map +1 -1
  196. package/lib/typescript/utils/sessionUtils.d.ts.map +1 -1
  197. package/package.json +1 -1
  198. package/src/core/OxyServices.base.ts +2 -1
  199. package/src/core/mixins/OxyServices.auth.ts +1 -1
  200. package/src/core/mixins/OxyServices.user.ts +2 -1
  201. package/src/core/mixins/OxyServices.utility.ts +2 -1
  202. package/src/core/services/AuthService.ts +153 -0
  203. package/src/core/services/SessionService.ts +3 -5
  204. package/src/core/services/SessionTransportService.ts +69 -0
  205. package/src/core/services/TokenService.ts +10 -18
  206. package/src/core/services/UserService.ts +125 -0
  207. package/src/core/services/index.ts +14 -0
  208. package/src/crypto/index.ts +9 -0
  209. package/src/crypto/keyManager.ts +3 -2
  210. package/src/crypto/signatureService.ts +43 -142
  211. package/src/index.ts +3 -2
  212. package/src/models/interfaces.ts +21 -74
  213. package/src/models/session.ts +3 -5
  214. package/src/shared/crypto/messageBuilders.ts +89 -0
  215. package/src/shared/crypto/platform.ts +140 -0
  216. package/src/shared/crypto/signature.ts +235 -0
  217. package/src/shared/index.ts +28 -0
  218. package/src/shared/models/index.ts +173 -0
  219. package/src/shared/transport/index.ts +349 -0
  220. package/src/shared/utils/index.ts +73 -0
  221. package/src/ui/context/OxyContext.tsx +22 -57
  222. package/src/ui/context/hooks/useAuthOperations.ts +33 -65
  223. package/src/ui/context/hooks/useLanguageManagement.ts +2 -1
  224. package/src/ui/hooks/auth/index.ts +0 -2
  225. package/src/ui/hooks/mutations/useAccountMutations.ts +1 -1
  226. package/src/ui/hooks/mutations/useServicesMutations.ts +1 -1
  227. package/src/ui/hooks/queries/useAccountQueries.ts +1 -1
  228. package/src/ui/hooks/queries/useServicesQueries.ts +3 -8
  229. package/src/ui/hooks/useLanguageManagement.ts +2 -1
  230. package/src/ui/hooks/useSessionManagement.ts +3 -9
  231. package/src/ui/index.ts +2 -1
  232. package/src/ui/screens/AccountSettingsScreen.tsx +6 -6
  233. package/src/ui/screens/AccountSwitcherScreen.tsx +1 -1
  234. package/src/ui/screens/OxyAuthScreen.tsx +2 -11
  235. package/src/ui/screens/ProfileScreen.tsx +1 -1
  236. package/src/ui/stores/authStore.ts +1 -1
  237. package/src/ui/types/navigation.ts +1 -1
  238. package/src/ui/utils/avatarUtils.ts +1 -1
  239. package/src/ui/utils/sessionHelpers.ts +15 -32
  240. package/src/utils/sessionUtils.ts +1 -8
@@ -0,0 +1,349 @@
1
+ /**
2
+ * Transport Abstraction
3
+ *
4
+ * Unified transport layer for WebSocket, SSE, and polling.
5
+ * Provides automatic fallback and unified configuration.
6
+ */
7
+
8
+ export type TransportType = 'websocket' | 'sse' | 'polling';
9
+
10
+ export interface TransportConfig {
11
+ baseURL: string;
12
+ namespace?: string;
13
+ sessionToken?: string;
14
+ accessToken?: string;
15
+ pollingInterval?: number; // milliseconds
16
+ reconnectAttempts?: number;
17
+ reconnectDelay?: number; // milliseconds
18
+ timeout?: number; // milliseconds
19
+ }
20
+
21
+ export interface TransportUpdate {
22
+ status: 'authorized' | 'cancelled' | 'expired' | 'pending';
23
+ sessionId?: string;
24
+ publicKey?: string;
25
+ userId?: string;
26
+ username?: string;
27
+ [key: string]: unknown;
28
+ }
29
+
30
+ export interface TransportCallbacks {
31
+ onUpdate?: (update: TransportUpdate) => void;
32
+ onConnect?: () => void;
33
+ onDisconnect?: () => void;
34
+ onError?: (error: Error) => void;
35
+ }
36
+
37
+ /**
38
+ * Transport Interface
39
+ * All transport implementations must implement this
40
+ */
41
+ export interface Transport {
42
+ /**
43
+ * Connect to the transport
44
+ */
45
+ connect(): Promise<void>;
46
+
47
+ /**
48
+ * Disconnect from the transport
49
+ */
50
+ disconnect(): void;
51
+
52
+ /**
53
+ * Check if currently connected
54
+ */
55
+ isConnected(): boolean;
56
+
57
+ /**
58
+ * Get the transport type
59
+ */
60
+ getType(): TransportType;
61
+ }
62
+
63
+ /**
64
+ * Transport Factory
65
+ * Creates the appropriate transport based on availability and configuration
66
+ */
67
+ export class TransportFactory {
68
+ /**
69
+ * Create a transport with automatic fallback
70
+ * Tries WebSocket first, then SSE, then polling
71
+ */
72
+ static async create(
73
+ config: TransportConfig,
74
+ callbacks: TransportCallbacks
75
+ ): Promise<Transport> {
76
+ // Try WebSocket first (best for real-time)
77
+ if (await this.isWebSocketAvailable()) {
78
+ try {
79
+ return new WebSocketTransport(config, callbacks);
80
+ } catch (error) {
81
+ // Fall through to SSE
82
+ }
83
+ }
84
+
85
+ // Try SSE second (good for one-way updates)
86
+ if (await this.isSSEAvailable()) {
87
+ try {
88
+ return new SSETransport(config, callbacks);
89
+ } catch (error) {
90
+ // Fall through to polling
91
+ }
92
+ }
93
+
94
+ // Fall back to polling (always available)
95
+ return new PollingTransport(config, callbacks);
96
+ }
97
+
98
+ /**
99
+ * Check if WebSocket is available
100
+ */
101
+ private static async isWebSocketAvailable(): Promise<boolean> {
102
+ // WebSocket is available in browsers and Node.js with socket.io-client
103
+ return typeof window !== 'undefined' || typeof process !== 'undefined';
104
+ }
105
+
106
+ /**
107
+ * Check if SSE is available
108
+ */
109
+ private static async isSSEAvailable(): Promise<boolean> {
110
+ // SSE is available in browsers via EventSource
111
+ // In Node.js, would need a polyfill or library
112
+ return typeof EventSource !== 'undefined' ||
113
+ (typeof window !== 'undefined' && 'EventSource' in window);
114
+ }
115
+ }
116
+
117
+ /**
118
+ * WebSocket Transport Implementation
119
+ * Uses socket.io-client for WebSocket connections
120
+ */
121
+ class WebSocketTransport implements Transport {
122
+ private socket: any = null;
123
+ private config: TransportConfig;
124
+ private callbacks: TransportCallbacks;
125
+ private connected = false;
126
+
127
+ constructor(config: TransportConfig, callbacks: TransportCallbacks) {
128
+ this.config = config;
129
+ this.callbacks = callbacks;
130
+ }
131
+
132
+ async connect(): Promise<void> {
133
+ // Dynamic import to avoid bundling socket.io-client if not needed
134
+ let io: any;
135
+ try {
136
+ io = (await import('socket.io-client')).default;
137
+ } catch (error) {
138
+ throw new Error('socket.io-client is required for WebSocket transport. Install it as a dependency.');
139
+ }
140
+
141
+ const url = this.config.namespace
142
+ ? `${this.config.baseURL}/${this.config.namespace}`
143
+ : this.config.baseURL;
144
+
145
+ const socketOptions: any = {
146
+ transports: ['websocket', 'polling'],
147
+ reconnection: true,
148
+ reconnectionAttempts: this.config.reconnectAttempts ?? 3,
149
+ reconnectionDelay: this.config.reconnectDelay ?? 1000,
150
+ };
151
+
152
+ if (this.config.accessToken) {
153
+ socketOptions.auth = { token: this.config.accessToken };
154
+ }
155
+
156
+ this.socket = io(url, socketOptions);
157
+
158
+ this.socket.on('connect', () => {
159
+ this.connected = true;
160
+ if (this.config.sessionToken) {
161
+ this.socket.emit('join', this.config.sessionToken);
162
+ }
163
+ this.callbacks.onConnect?.();
164
+ });
165
+
166
+ this.socket.on('auth_update', (payload: TransportUpdate) => {
167
+ this.callbacks.onUpdate?.(payload);
168
+ });
169
+
170
+ this.socket.on('connect_error', (error: Error) => {
171
+ this.callbacks.onError?.(error);
172
+ });
173
+
174
+ this.socket.on('disconnect', () => {
175
+ this.connected = false;
176
+ this.callbacks.onDisconnect?.();
177
+ });
178
+ }
179
+
180
+ disconnect(): void {
181
+ if (this.socket) {
182
+ this.socket.disconnect();
183
+ this.socket = null;
184
+ this.connected = false;
185
+ }
186
+ }
187
+
188
+ isConnected(): boolean {
189
+ return this.connected && this.socket?.connected === true;
190
+ }
191
+
192
+ getType(): TransportType {
193
+ return 'websocket';
194
+ }
195
+ }
196
+
197
+ /**
198
+ * SSE Transport Implementation
199
+ * Uses EventSource for Server-Sent Events
200
+ */
201
+ class SSETransport implements Transport {
202
+ private eventSource: EventSource | null = null;
203
+ private config: TransportConfig;
204
+ private callbacks: TransportCallbacks;
205
+ private connected = false;
206
+
207
+ constructor(config: TransportConfig, callbacks: TransportCallbacks) {
208
+ this.config = config;
209
+ this.callbacks = callbacks;
210
+ }
211
+
212
+ async connect(): Promise<void> {
213
+ if (typeof EventSource === 'undefined') {
214
+ throw new Error('EventSource is not available in this environment');
215
+ }
216
+
217
+ const url = this.config.sessionToken
218
+ ? `${this.config.baseURL}/auth/session/stream/${this.config.sessionToken}`
219
+ : `${this.config.baseURL}/auth/session/stream`;
220
+
221
+ this.eventSource = new EventSource(url);
222
+
223
+ this.eventSource.onopen = () => {
224
+ this.connected = true;
225
+ this.callbacks.onConnect?.();
226
+ };
227
+
228
+ this.eventSource.onmessage = (event) => {
229
+ try {
230
+ const update = JSON.parse(event.data) as TransportUpdate;
231
+ this.callbacks.onUpdate?.(update);
232
+ } catch (error) {
233
+ this.callbacks.onError?.(error as Error);
234
+ }
235
+ };
236
+
237
+ this.eventSource.onerror = (error) => {
238
+ this.callbacks.onError?.(new Error('SSE connection error'));
239
+ };
240
+ }
241
+
242
+ disconnect(): void {
243
+ if (this.eventSource) {
244
+ this.eventSource.close();
245
+ this.eventSource = null;
246
+ this.connected = false;
247
+ }
248
+ }
249
+
250
+ isConnected(): boolean {
251
+ return this.connected && this.eventSource?.readyState === EventSource.OPEN;
252
+ }
253
+
254
+ getType(): TransportType {
255
+ return 'sse';
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Polling Transport Implementation
261
+ * Uses HTTP polling as fallback
262
+ */
263
+ class PollingTransport implements Transport {
264
+ private intervalId: ReturnType<typeof setInterval> | null = null;
265
+ private config: TransportConfig;
266
+ private callbacks: TransportCallbacks;
267
+ private connected = false;
268
+ private abortController: AbortController | null = null;
269
+
270
+ constructor(config: TransportConfig, callbacks: TransportCallbacks) {
271
+ this.config = config;
272
+ this.callbacks = callbacks;
273
+ }
274
+
275
+ async connect(): Promise<void> {
276
+ this.connected = true;
277
+ this.callbacks.onConnect?.();
278
+
279
+ const poll = async () => {
280
+ if (!this.config.sessionToken) {
281
+ return;
282
+ }
283
+
284
+ try {
285
+ this.abortController = new AbortController();
286
+ const response = await fetch(
287
+ `${this.config.baseURL}/api/auth/session/status/${this.config.sessionToken}`,
288
+ {
289
+ signal: this.abortController.signal,
290
+ headers: this.config.accessToken
291
+ ? { Authorization: `Bearer ${this.config.accessToken}` }
292
+ : {},
293
+ }
294
+ );
295
+
296
+ if (!response.ok) {
297
+ throw new Error(`Polling failed: ${response.statusText}`);
298
+ }
299
+
300
+ const data = await response.json();
301
+
302
+ if (data.authorized && data.sessionId) {
303
+ this.callbacks.onUpdate?.({
304
+ status: 'authorized',
305
+ sessionId: data.sessionId,
306
+ publicKey: data.publicKey,
307
+ });
308
+ } else if (data.status === 'expired') {
309
+ this.callbacks.onUpdate?.({ status: 'expired' });
310
+ } else if (data.status === 'cancelled') {
311
+ this.callbacks.onUpdate?.({ status: 'cancelled' });
312
+ }
313
+ } catch (error) {
314
+ if (error instanceof Error && error.name !== 'AbortError') {
315
+ this.callbacks.onError?.(error);
316
+ }
317
+ }
318
+ };
319
+
320
+ // Poll immediately, then at intervals
321
+ await poll();
322
+ this.intervalId = setInterval(
323
+ poll,
324
+ this.config.pollingInterval ?? 3000
325
+ );
326
+ }
327
+
328
+ disconnect(): void {
329
+ if (this.intervalId) {
330
+ clearInterval(this.intervalId);
331
+ this.intervalId = null;
332
+ }
333
+ if (this.abortController) {
334
+ this.abortController.abort();
335
+ this.abortController = null;
336
+ }
337
+ this.connected = false;
338
+ this.callbacks.onDisconnect?.();
339
+ }
340
+
341
+ isConnected(): boolean {
342
+ return this.connected && this.intervalId !== null;
343
+ }
344
+
345
+ getType(): TransportType {
346
+ return 'polling';
347
+ }
348
+ }
349
+
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Shared Utilities
3
+ *
4
+ * Common utility functions used across Accounts, Services SDK, and API packages.
5
+ */
6
+
7
+ /**
8
+ * Get a shortened display version of a public key
9
+ * Format: first 8 chars ... last 8 chars
10
+ */
11
+ export function shortenPublicKey(publicKey: string): string {
12
+ if (publicKey.length <= 16) return publicKey;
13
+ return `${publicKey.slice(0, 8)}...${publicKey.slice(-8)}`;
14
+ }
15
+
16
+ /**
17
+ * Generate a secure random session token
18
+ * Uses crypto.randomBytes (Node) or Web Crypto API (Browser/RN)
19
+ */
20
+ export async function generateSessionToken(size: number = 32): Promise<string> {
21
+ // Use platform-appropriate random bytes
22
+ if (typeof window !== 'undefined' && window.crypto) {
23
+ // Web platform
24
+ const array = new Uint8Array(size);
25
+ window.crypto.getRandomValues(array);
26
+ return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
27
+ }
28
+
29
+ if (typeof process !== 'undefined' && process.versions?.node) {
30
+ // Node.js platform
31
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
32
+ const getCrypto = new Function('return require("crypto")');
33
+ const crypto = getCrypto();
34
+ return crypto.randomBytes(size).toString('hex');
35
+ }
36
+
37
+ // React Native - will be handled by platform adapter
38
+ // For now, throw an error if we can't determine platform
39
+ throw new Error('Unable to generate session token: no crypto implementation available');
40
+ }
41
+
42
+ /**
43
+ * Generate a session token synchronously (Node.js only)
44
+ */
45
+ export function generateSessionTokenSync(size: number = 32): string {
46
+ if (typeof process === 'undefined' || !process.versions?.node) {
47
+ throw new Error('generateSessionTokenSync can only be used in Node.js');
48
+ }
49
+
50
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
51
+ const getCrypto = new Function('return require("crypto")');
52
+ const crypto = getCrypto();
53
+ return crypto.randomBytes(size).toString('hex');
54
+ }
55
+
56
+ /**
57
+ * Convert bytes to hex string
58
+ */
59
+ export function bytesToHex(bytes: Uint8Array): string {
60
+ return Array.from(bytes, byte => byte.toString(16).padStart(2, '0')).join('');
61
+ }
62
+
63
+ /**
64
+ * Convert hex string to bytes
65
+ */
66
+ export function hexToBytes(hex: string): Uint8Array {
67
+ const bytes = new Uint8Array(hex.length / 2);
68
+ for (let i = 0; i < hex.length; i += 2) {
69
+ bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
70
+ }
71
+ return bytes;
72
+ }
73
+
@@ -11,7 +11,8 @@ import {
11
11
  } from 'react';
12
12
  import { Platform } from 'react-native';
13
13
  import { OxyServices } from '../../core';
14
- import type { User, ApiError } from '../../models/interfaces';
14
+ import type { ApiError } from '../../models/interfaces';
15
+ import type { User } from '../../shared';
15
16
  import type { ClientSession } from '../../models/session';
16
17
  import { toast } from '../../lib/sonner';
17
18
  import { useAuthStore, type AuthState } from '../stores/authStore';
@@ -222,7 +223,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
222
223
  // CRITICAL: Invalidate cache on app startup to ensure fresh state check
223
224
  // This prevents stale cache from previous session from showing incorrect state
224
225
  KeyManager.invalidateCache();
225
-
226
+
226
227
  // Check if identity exists and verify integrity
227
228
  const hasIdentity = await KeyManager.hasIdentity();
228
229
  if (hasIdentity) {
@@ -421,7 +422,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
421
422
  if (pendingTransfers.length > 0) {
422
423
  const activeTransferId = getActiveTransferId();
423
424
  const hasActiveTransfer = activeTransferId && pendingTransfers.some((t: { transferId: string; data: any }) => t.transferId === activeTransferId);
424
-
425
+
425
426
  if (hasActiveTransfer) {
426
427
  throw new Error(
427
428
  'Cannot delete identity: An active identity transfer is in progress. ' +
@@ -485,11 +486,11 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
485
486
  if (wasOffline) {
486
487
  const now = Date.now();
487
488
  const timeSinceLastLog = now - lastReconnectionLog;
488
-
489
+
489
490
  if (timeSinceLastLog >= RECONNECTION_LOG_DEBOUNCE_MS) {
490
491
  logger('Network reconnected, checking identity sync...');
491
492
  lastReconnectionLog = now;
492
-
493
+
493
494
  // Sync identity first (if not synced)
494
495
  try {
495
496
  const hasIdentityValue = await hasIdentity();
@@ -520,7 +521,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
520
521
  // This is handled by useCheckPendingTransfers hook which runs automatically
521
522
  // when authenticated and online
522
523
  }
523
-
524
+
524
525
  // TanStack Query will automatically retry pending mutations
525
526
  // Reset flag immediately after processing (whether logged or not)
526
527
  wasOffline = false;
@@ -580,22 +581,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
580
581
  setTokenReady(false);
581
582
 
582
583
  try {
583
- // CRITICAL: Get current local identity (publicKey) before restoring sessions
584
- // Sessions must belong to the current local identity stored in Accounts app
585
- let currentPublicKey: string | null = null;
586
- try {
587
- if (Platform.OS !== 'web') {
588
- // Only check identity on native platforms (web doesn't have local identity)
589
- // KeyManager is already imported at the top of the file
590
- currentPublicKey = await KeyManager.getPublicKey();
591
- }
592
- } catch (identityError) {
593
- if (__DEV__) {
594
- logger('Failed to get current local identity during session restoration', identityError);
595
- }
596
- // Continue without identity check if it fails (e.g., on web platform)
597
- }
598
-
599
584
  const storedSessionIdsJson = await storage.getItem(storageKeys.sessionIds);
600
585
  const storedSessionIds: string[] = storedSessionIdsJson ? JSON.parse(storedSessionIdsJson) : [];
601
586
  const storedActiveSessionId = await storage.getItem(storageKeys.activeSessionId);
@@ -608,25 +593,12 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
608
593
  const validation = await oxyServices.validateSession(sessionId, { useHeaderValidation: true });
609
594
  if (validation?.valid && validation.user) {
610
595
  const now = new Date();
611
- // user.id is now publicKey (canonical identity)
612
- const sessionPublicKey = validation.user.publicKey || validation.user.id || '';
613
-
614
- // IDENTITY BINDING: Only restore sessions that match current local identity
615
- // If we have a current local identity, filter out sessions that don't match
616
- if (currentPublicKey && sessionPublicKey !== currentPublicKey) {
617
- if (__DEV__) {
618
- logger(`Skipping session ${sessionId.substring(0, 8)}... - belongs to different identity (${sessionPublicKey.substring(0, 16)}... vs ${currentPublicKey.substring(0, 16)}...)`);
619
- }
620
- continue; // Skip this session - it belongs to a different identity
621
- }
622
-
623
596
  validSessions.push({
624
597
  sessionId,
625
598
  deviceId: '',
626
599
  expiresAt: new Date(now.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString(),
627
600
  lastActive: now.toISOString(),
628
- publicKey: sessionPublicKey, // Canonical user identity
629
- userId: validation.user.id?.toString(), // Optional MongoDB ObjectId for backward compatibility
601
+ userId: validation.user.id?.toString() ?? '',
630
602
  isCurrent: sessionId === storedActiveSessionId,
631
603
  });
632
604
  }
@@ -644,13 +616,6 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
644
616
 
645
617
  if (validSessions.length > 0) {
646
618
  updateSessions(validSessions, { merge: false });
647
- } else if (currentPublicKey && storedSessionIds.length > 0) {
648
- // All sessions were filtered out due to identity mismatch - clear stored sessions
649
- if (__DEV__) {
650
- logger('All stored sessions belong to different identity - clearing session storage');
651
- }
652
- await storage.removeItem(storageKeys.sessionIds);
653
- await storage.removeItem(storageKeys.activeSessionId);
654
619
  }
655
620
  }
656
621
 
@@ -762,9 +727,9 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
762
727
  return isAuthenticatedFromStore || isAuthenticatedFromSessions;
763
728
  }, [isAuthenticatedFromStore, isAuthenticatedFromSessions]);
764
729
 
765
- // Get userId from JWT token for socket room matching
766
- // Note: JWT token may contain MongoDB ObjectId internally, but user.id is publicKey (canonical identity)
767
- // Socket rooms may need internal mapping from publicKey to MongoDB ObjectId if backend requires it
730
+ // Get userId from JWT token (MongoDB ObjectId) for socket room matching
731
+ // user.id is set to publicKey for compatibility, but socket rooms use MongoDB ObjectId
732
+ // The JWT token's userId field contains the MongoDB ObjectId
768
733
  const userId = oxyServices.getCurrentUserId() || user?.id;
769
734
 
770
735
  // Use Zustand store for transfer state management
@@ -784,16 +749,16 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
784
749
  // Load transfer codes
785
750
  const storedCodes = await storage.getItem(TRANSFER_CODES_STORAGE_KEY);
786
751
  const storedActiveTransferId = await storage.getItem(ACTIVE_TRANSFER_STORAGE_KEY);
787
-
752
+
788
753
  const parsedCodes = storedCodes ? JSON.parse(storedCodes) : {};
789
754
  const activeTransferId = storedActiveTransferId || null;
790
-
755
+
791
756
  // Restore to Zustand store (store handles validation and expiration)
792
757
  restoreFromStorage(parsedCodes, activeTransferId);
793
758
  markRestored();
794
-
759
+
795
760
  if (__DEV__ && Object.keys(parsedCodes).length > 0) {
796
- logger('Restored transfer codes from storage', {
761
+ logger('Restored transfer codes from storage', {
797
762
  count: Object.keys(parsedCodes).length,
798
763
  hasActiveTransfer: !!activeTransferId,
799
764
  });
@@ -818,7 +783,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
818
783
  const persistTransferCodes = async () => {
819
784
  try {
820
785
  await storage.setItem(TRANSFER_CODES_STORAGE_KEY, JSON.stringify(transferCodes));
821
-
786
+
822
787
  if (activeTransferId) {
823
788
  await storage.setItem(ACTIVE_TRANSFER_STORAGE_KEY, activeTransferId);
824
789
  } else {
@@ -846,7 +811,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
846
811
  // Transfer code management functions using Zustand store
847
812
  const storeTransferCode = useCallback(async (transferId: string, code: string, sourceDeviceId: string | null, publicKey: string) => {
848
813
  storeTransferCodeStore(transferId, code, sourceDeviceId, publicKey);
849
-
814
+
850
815
  if (__DEV__) {
851
816
  logger('Stored transfer code', { transferId, sourceDeviceId, publicKey: publicKey.substring(0, 16) + '...' });
852
817
  }
@@ -858,7 +823,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
858
823
 
859
824
  const updateTransferState = useCallback(async (transferId: string, state: 'pending' | 'completed' | 'failed') => {
860
825
  updateTransferStateStore(transferId, state);
861
-
826
+
862
827
  if (__DEV__) {
863
828
  logger('Updated transfer state', { transferId, state });
864
829
  }
@@ -866,7 +831,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
866
831
 
867
832
  const clearTransferCode = useCallback(async (transferId: string) => {
868
833
  clearTransferCodeStore(transferId);
869
-
834
+
870
835
  if (__DEV__) {
871
836
  logger('Cleared transfer code', { transferId });
872
837
  }
@@ -908,7 +873,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
908
873
  const storedTransfer = getTransferCode(data.transferId);
909
874
 
910
875
  if (!storedTransfer) {
911
- logger('Transfer code not found for transferId', {
876
+ logger('Transfer code not found for transferId', {
912
877
  transferId: data.transferId,
913
878
  });
914
879
  toast.error('Transfer verification failed: Code not found. Identity will not be deleted.');
@@ -930,7 +895,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
930
895
  // Verify deviceId matches - very lenient since publicKey is the critical check
931
896
  // If publicKey matches, we allow deletion even if deviceId doesn't match exactly
932
897
  // This handles cases where deviceId might not be available or slightly different
933
- const deviceIdMatches =
898
+ const deviceIdMatches =
934
899
  // Exact match
935
900
  (data.sourceDeviceId && data.sourceDeviceId === currentDeviceId) ||
936
901
  // Stored sourceDeviceId matches current deviceId
@@ -1010,7 +975,7 @@ export const OxyProvider: React.FC<OxyContextProviderProps> = ({
1010
975
  }
1011
976
 
1012
977
  await deleteIdentityAndClearAccount(false, false, true);
1013
-
978
+
1014
979
  // Verify identity was actually deleted
1015
980
  const identityDeleted = !(await KeyManager.hasIdentity());
1016
981
  if (!identityDeleted) {