@oxyhq/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/README.md +50 -0
  2. package/dist/cjs/AuthManager.js +361 -0
  3. package/dist/cjs/CrossDomainAuth.js +258 -0
  4. package/dist/cjs/HttpService.js +618 -0
  5. package/dist/cjs/OxyServices.base.js +263 -0
  6. package/dist/cjs/OxyServices.errors.js +22 -0
  7. package/dist/cjs/OxyServices.js +63 -0
  8. package/dist/cjs/constants/version.js +16 -0
  9. package/dist/cjs/crypto/index.js +20 -0
  10. package/dist/cjs/crypto/keyManager.js +887 -0
  11. package/dist/cjs/crypto/polyfill.js +64 -0
  12. package/dist/cjs/crypto/recoveryPhrase.js +169 -0
  13. package/dist/cjs/crypto/signatureService.js +296 -0
  14. package/dist/cjs/i18n/index.js +73 -0
  15. package/dist/cjs/i18n/locales/ar-SA.json +120 -0
  16. package/dist/cjs/i18n/locales/ca-ES.json +120 -0
  17. package/dist/cjs/i18n/locales/de-DE.json +120 -0
  18. package/dist/cjs/i18n/locales/en-US.json +956 -0
  19. package/dist/cjs/i18n/locales/es-ES.json +944 -0
  20. package/dist/cjs/i18n/locales/fr-FR.json +120 -0
  21. package/dist/cjs/i18n/locales/it-IT.json +120 -0
  22. package/dist/cjs/i18n/locales/ja-JP.json +119 -0
  23. package/dist/cjs/i18n/locales/ko-KR.json +120 -0
  24. package/dist/cjs/i18n/locales/locales/ar-SA.json +120 -0
  25. package/dist/cjs/i18n/locales/locales/ca-ES.json +120 -0
  26. package/dist/cjs/i18n/locales/locales/de-DE.json +120 -0
  27. package/dist/cjs/i18n/locales/locales/en-US.json +956 -0
  28. package/dist/cjs/i18n/locales/locales/es-ES.json +944 -0
  29. package/dist/cjs/i18n/locales/locales/fr-FR.json +120 -0
  30. package/dist/cjs/i18n/locales/locales/it-IT.json +120 -0
  31. package/dist/cjs/i18n/locales/locales/ja-JP.json +119 -0
  32. package/dist/cjs/i18n/locales/locales/ko-KR.json +120 -0
  33. package/dist/cjs/i18n/locales/locales/pt-PT.json +120 -0
  34. package/dist/cjs/i18n/locales/locales/zh-CN.json +120 -0
  35. package/dist/cjs/i18n/locales/pt-PT.json +120 -0
  36. package/dist/cjs/i18n/locales/zh-CN.json +120 -0
  37. package/dist/cjs/index.js +153 -0
  38. package/dist/cjs/mixins/OxyServices.analytics.js +49 -0
  39. package/dist/cjs/mixins/OxyServices.assets.js +380 -0
  40. package/dist/cjs/mixins/OxyServices.auth.js +259 -0
  41. package/dist/cjs/mixins/OxyServices.developer.js +97 -0
  42. package/dist/cjs/mixins/OxyServices.devices.js +116 -0
  43. package/dist/cjs/mixins/OxyServices.features.js +309 -0
  44. package/dist/cjs/mixins/OxyServices.fedcm.js +435 -0
  45. package/dist/cjs/mixins/OxyServices.karma.js +108 -0
  46. package/dist/cjs/mixins/OxyServices.language.js +154 -0
  47. package/dist/cjs/mixins/OxyServices.location.js +43 -0
  48. package/dist/cjs/mixins/OxyServices.payment.js +158 -0
  49. package/dist/cjs/mixins/OxyServices.popup.js +371 -0
  50. package/dist/cjs/mixins/OxyServices.privacy.js +162 -0
  51. package/dist/cjs/mixins/OxyServices.redirect.js +345 -0
  52. package/dist/cjs/mixins/OxyServices.security.js +81 -0
  53. package/dist/cjs/mixins/OxyServices.user.js +355 -0
  54. package/dist/cjs/mixins/OxyServices.utility.js +156 -0
  55. package/dist/cjs/mixins/index.js +79 -0
  56. package/dist/cjs/mixins/mixinHelpers.js +53 -0
  57. package/dist/cjs/models/interfaces.js +20 -0
  58. package/dist/cjs/models/session.js +2 -0
  59. package/dist/cjs/shared/index.js +70 -0
  60. package/dist/cjs/shared/utils/colorUtils.js +153 -0
  61. package/dist/cjs/shared/utils/debugUtils.js +73 -0
  62. package/dist/cjs/shared/utils/errorUtils.js +183 -0
  63. package/dist/cjs/shared/utils/index.js +49 -0
  64. package/dist/cjs/shared/utils/networkUtils.js +183 -0
  65. package/dist/cjs/shared/utils/themeUtils.js +106 -0
  66. package/dist/cjs/utils/apiUtils.js +61 -0
  67. package/dist/cjs/utils/asyncUtils.js +194 -0
  68. package/dist/cjs/utils/cache.js +226 -0
  69. package/dist/cjs/utils/deviceManager.js +205 -0
  70. package/dist/cjs/utils/errorUtils.js +154 -0
  71. package/dist/cjs/utils/index.js +26 -0
  72. package/dist/cjs/utils/languageUtils.js +165 -0
  73. package/dist/cjs/utils/loggerUtils.js +126 -0
  74. package/dist/cjs/utils/platform.js +144 -0
  75. package/dist/cjs/utils/requestUtils.js +209 -0
  76. package/dist/cjs/utils/sessionUtils.js +181 -0
  77. package/dist/cjs/utils/validationUtils.js +173 -0
  78. package/dist/esm/AuthManager.js +356 -0
  79. package/dist/esm/CrossDomainAuth.js +253 -0
  80. package/dist/esm/HttpService.js +614 -0
  81. package/dist/esm/OxyServices.base.js +259 -0
  82. package/dist/esm/OxyServices.errors.js +17 -0
  83. package/dist/esm/OxyServices.js +59 -0
  84. package/dist/esm/constants/version.js +13 -0
  85. package/dist/esm/crypto/index.js +13 -0
  86. package/dist/esm/crypto/keyManager.js +850 -0
  87. package/dist/esm/crypto/polyfill.js +61 -0
  88. package/dist/esm/crypto/recoveryPhrase.js +132 -0
  89. package/dist/esm/crypto/signatureService.js +259 -0
  90. package/dist/esm/i18n/index.js +69 -0
  91. package/dist/esm/i18n/locales/ar-SA.json +120 -0
  92. package/dist/esm/i18n/locales/ca-ES.json +120 -0
  93. package/dist/esm/i18n/locales/de-DE.json +120 -0
  94. package/dist/esm/i18n/locales/en-US.json +956 -0
  95. package/dist/esm/i18n/locales/es-ES.json +944 -0
  96. package/dist/esm/i18n/locales/fr-FR.json +120 -0
  97. package/dist/esm/i18n/locales/it-IT.json +120 -0
  98. package/dist/esm/i18n/locales/ja-JP.json +119 -0
  99. package/dist/esm/i18n/locales/ko-KR.json +120 -0
  100. package/dist/esm/i18n/locales/locales/ar-SA.json +120 -0
  101. package/dist/esm/i18n/locales/locales/ca-ES.json +120 -0
  102. package/dist/esm/i18n/locales/locales/de-DE.json +120 -0
  103. package/dist/esm/i18n/locales/locales/en-US.json +956 -0
  104. package/dist/esm/i18n/locales/locales/es-ES.json +944 -0
  105. package/dist/esm/i18n/locales/locales/fr-FR.json +120 -0
  106. package/dist/esm/i18n/locales/locales/it-IT.json +120 -0
  107. package/dist/esm/i18n/locales/locales/ja-JP.json +119 -0
  108. package/dist/esm/i18n/locales/locales/ko-KR.json +120 -0
  109. package/dist/esm/i18n/locales/locales/pt-PT.json +120 -0
  110. package/dist/esm/i18n/locales/locales/zh-CN.json +120 -0
  111. package/dist/esm/i18n/locales/pt-PT.json +120 -0
  112. package/dist/esm/i18n/locales/zh-CN.json +120 -0
  113. package/dist/esm/index.js +55 -0
  114. package/dist/esm/mixins/OxyServices.analytics.js +46 -0
  115. package/dist/esm/mixins/OxyServices.assets.js +377 -0
  116. package/dist/esm/mixins/OxyServices.auth.js +256 -0
  117. package/dist/esm/mixins/OxyServices.developer.js +94 -0
  118. package/dist/esm/mixins/OxyServices.devices.js +113 -0
  119. package/dist/esm/mixins/OxyServices.features.js +306 -0
  120. package/dist/esm/mixins/OxyServices.fedcm.js +433 -0
  121. package/dist/esm/mixins/OxyServices.karma.js +105 -0
  122. package/dist/esm/mixins/OxyServices.language.js +118 -0
  123. package/dist/esm/mixins/OxyServices.location.js +40 -0
  124. package/dist/esm/mixins/OxyServices.payment.js +155 -0
  125. package/dist/esm/mixins/OxyServices.popup.js +369 -0
  126. package/dist/esm/mixins/OxyServices.privacy.js +159 -0
  127. package/dist/esm/mixins/OxyServices.redirect.js +343 -0
  128. package/dist/esm/mixins/OxyServices.security.js +78 -0
  129. package/dist/esm/mixins/OxyServices.user.js +352 -0
  130. package/dist/esm/mixins/OxyServices.utility.js +153 -0
  131. package/dist/esm/mixins/index.js +76 -0
  132. package/dist/esm/mixins/mixinHelpers.js +48 -0
  133. package/dist/esm/models/interfaces.js +17 -0
  134. package/dist/esm/models/session.js +1 -0
  135. package/dist/esm/shared/index.js +31 -0
  136. package/dist/esm/shared/utils/colorUtils.js +143 -0
  137. package/dist/esm/shared/utils/debugUtils.js +65 -0
  138. package/dist/esm/shared/utils/errorUtils.js +170 -0
  139. package/dist/esm/shared/utils/index.js +15 -0
  140. package/dist/esm/shared/utils/networkUtils.js +173 -0
  141. package/dist/esm/shared/utils/themeUtils.js +98 -0
  142. package/dist/esm/utils/apiUtils.js +55 -0
  143. package/dist/esm/utils/asyncUtils.js +179 -0
  144. package/dist/esm/utils/cache.js +218 -0
  145. package/dist/esm/utils/deviceManager.js +168 -0
  146. package/dist/esm/utils/errorUtils.js +146 -0
  147. package/dist/esm/utils/index.js +7 -0
  148. package/dist/esm/utils/languageUtils.js +158 -0
  149. package/dist/esm/utils/loggerUtils.js +115 -0
  150. package/dist/esm/utils/platform.js +102 -0
  151. package/dist/esm/utils/requestUtils.js +203 -0
  152. package/dist/esm/utils/sessionUtils.js +171 -0
  153. package/dist/esm/utils/validationUtils.js +153 -0
  154. package/dist/types/AuthManager.d.ts +143 -0
  155. package/dist/types/CrossDomainAuth.d.ts +160 -0
  156. package/dist/types/HttpService.d.ts +163 -0
  157. package/dist/types/OxyServices.base.d.ts +126 -0
  158. package/dist/types/OxyServices.d.ts +81 -0
  159. package/dist/types/OxyServices.errors.d.ts +11 -0
  160. package/dist/types/constants/version.d.ts +13 -0
  161. package/dist/types/crypto/index.d.ts +11 -0
  162. package/dist/types/crypto/keyManager.d.ts +189 -0
  163. package/dist/types/crypto/polyfill.d.ts +11 -0
  164. package/dist/types/crypto/recoveryPhrase.d.ts +58 -0
  165. package/dist/types/crypto/signatureService.d.ts +86 -0
  166. package/dist/types/i18n/index.d.ts +3 -0
  167. package/dist/types/index.d.ts +50 -0
  168. package/dist/types/mixins/OxyServices.analytics.d.ts +66 -0
  169. package/dist/types/mixins/OxyServices.assets.d.ts +135 -0
  170. package/dist/types/mixins/OxyServices.auth.d.ts +186 -0
  171. package/dist/types/mixins/OxyServices.developer.d.ts +99 -0
  172. package/dist/types/mixins/OxyServices.devices.d.ts +96 -0
  173. package/dist/types/mixins/OxyServices.features.d.ts +228 -0
  174. package/dist/types/mixins/OxyServices.fedcm.d.ts +200 -0
  175. package/dist/types/mixins/OxyServices.karma.d.ts +85 -0
  176. package/dist/types/mixins/OxyServices.language.d.ts +81 -0
  177. package/dist/types/mixins/OxyServices.location.d.ts +64 -0
  178. package/dist/types/mixins/OxyServices.payment.d.ts +111 -0
  179. package/dist/types/mixins/OxyServices.popup.d.ts +205 -0
  180. package/dist/types/mixins/OxyServices.privacy.d.ts +122 -0
  181. package/dist/types/mixins/OxyServices.redirect.d.ts +245 -0
  182. package/dist/types/mixins/OxyServices.security.d.ts +78 -0
  183. package/dist/types/mixins/OxyServices.user.d.ts +182 -0
  184. package/dist/types/mixins/OxyServices.utility.d.ts +93 -0
  185. package/dist/types/mixins/index.d.ts +30 -0
  186. package/dist/types/mixins/mixinHelpers.d.ts +31 -0
  187. package/dist/types/models/interfaces.d.ts +415 -0
  188. package/dist/types/models/session.d.ts +27 -0
  189. package/dist/types/shared/index.d.ts +28 -0
  190. package/dist/types/shared/utils/colorUtils.d.ts +104 -0
  191. package/dist/types/shared/utils/debugUtils.d.ts +48 -0
  192. package/dist/types/shared/utils/errorUtils.d.ts +97 -0
  193. package/dist/types/shared/utils/index.d.ts +13 -0
  194. package/dist/types/shared/utils/networkUtils.d.ts +139 -0
  195. package/dist/types/shared/utils/themeUtils.d.ts +90 -0
  196. package/dist/types/utils/apiUtils.d.ts +53 -0
  197. package/dist/types/utils/asyncUtils.d.ts +58 -0
  198. package/dist/types/utils/cache.d.ts +127 -0
  199. package/dist/types/utils/deviceManager.d.ts +65 -0
  200. package/dist/types/utils/errorUtils.d.ts +46 -0
  201. package/dist/types/utils/index.d.ts +6 -0
  202. package/dist/types/utils/languageUtils.d.ts +37 -0
  203. package/dist/types/utils/loggerUtils.d.ts +48 -0
  204. package/dist/types/utils/platform.d.ts +40 -0
  205. package/dist/types/utils/requestUtils.d.ts +123 -0
  206. package/dist/types/utils/sessionUtils.d.ts +54 -0
  207. package/dist/types/utils/validationUtils.d.ts +85 -0
  208. package/package.json +84 -0
  209. package/src/AuthManager.ts +436 -0
  210. package/src/CrossDomainAuth.ts +307 -0
  211. package/src/HttpService.ts +752 -0
  212. package/src/OxyServices.base.ts +334 -0
  213. package/src/OxyServices.errors.ts +26 -0
  214. package/src/OxyServices.ts +129 -0
  215. package/src/constants/version.ts +15 -0
  216. package/src/crypto/index.ts +25 -0
  217. package/src/crypto/keyManager.ts +962 -0
  218. package/src/crypto/polyfill.ts +70 -0
  219. package/src/crypto/recoveryPhrase.ts +166 -0
  220. package/src/crypto/signatureService.ts +323 -0
  221. package/src/i18n/index.ts +75 -0
  222. package/src/i18n/locales/ar-SA.json +120 -0
  223. package/src/i18n/locales/ca-ES.json +120 -0
  224. package/src/i18n/locales/de-DE.json +120 -0
  225. package/src/i18n/locales/en-US.json +956 -0
  226. package/src/i18n/locales/es-ES.json +944 -0
  227. package/src/i18n/locales/fr-FR.json +120 -0
  228. package/src/i18n/locales/it-IT.json +120 -0
  229. package/src/i18n/locales/ja-JP.json +119 -0
  230. package/src/i18n/locales/ko-KR.json +120 -0
  231. package/src/i18n/locales/pt-PT.json +120 -0
  232. package/src/i18n/locales/zh-CN.json +120 -0
  233. package/src/index.ts +153 -0
  234. package/src/mixins/OxyServices.analytics.ts +53 -0
  235. package/src/mixins/OxyServices.assets.ts +412 -0
  236. package/src/mixins/OxyServices.auth.ts +358 -0
  237. package/src/mixins/OxyServices.developer.ts +114 -0
  238. package/src/mixins/OxyServices.devices.ts +119 -0
  239. package/src/mixins/OxyServices.features.ts +428 -0
  240. package/src/mixins/OxyServices.fedcm.ts +494 -0
  241. package/src/mixins/OxyServices.karma.ts +111 -0
  242. package/src/mixins/OxyServices.language.ts +127 -0
  243. package/src/mixins/OxyServices.location.ts +46 -0
  244. package/src/mixins/OxyServices.payment.ts +163 -0
  245. package/src/mixins/OxyServices.popup.ts +443 -0
  246. package/src/mixins/OxyServices.privacy.ts +182 -0
  247. package/src/mixins/OxyServices.redirect.ts +397 -0
  248. package/src/mixins/OxyServices.security.ts +103 -0
  249. package/src/mixins/OxyServices.user.ts +392 -0
  250. package/src/mixins/OxyServices.utility.ts +191 -0
  251. package/src/mixins/index.ts +91 -0
  252. package/src/mixins/mixinHelpers.ts +69 -0
  253. package/src/models/interfaces.ts +511 -0
  254. package/src/models/session.ts +30 -0
  255. package/src/shared/index.ts +82 -0
  256. package/src/shared/utils/colorUtils.ts +155 -0
  257. package/src/shared/utils/debugUtils.ts +73 -0
  258. package/src/shared/utils/errorUtils.ts +181 -0
  259. package/src/shared/utils/index.ts +59 -0
  260. package/src/shared/utils/networkUtils.ts +248 -0
  261. package/src/shared/utils/themeUtils.ts +115 -0
  262. package/src/types/bip39.d.ts +32 -0
  263. package/src/types/buffer.d.ts +97 -0
  264. package/src/types/color.d.ts +20 -0
  265. package/src/types/elliptic.d.ts +62 -0
  266. package/src/utils/apiUtils.ts +88 -0
  267. package/src/utils/asyncUtils.ts +252 -0
  268. package/src/utils/cache.ts +264 -0
  269. package/src/utils/deviceManager.ts +198 -0
  270. package/src/utils/errorUtils.ts +216 -0
  271. package/src/utils/index.ts +21 -0
  272. package/src/utils/languageUtils.ts +174 -0
  273. package/src/utils/loggerUtils.ts +153 -0
  274. package/src/utils/platform.ts +117 -0
  275. package/src/utils/requestUtils.ts +237 -0
  276. package/src/utils/sessionUtils.ts +206 -0
  277. package/src/utils/validationUtils.ts +174 -0
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ /**
3
+ * Session management utilities
4
+ *
5
+ * Provides consistent session normalization, deduplication, and sorting
6
+ * to ensure sessions are always displayed in a predictable order.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.normalizeSession = normalizeSession;
10
+ exports.sessionsEqual = sessionsEqual;
11
+ exports.sortSessions = sortSessions;
12
+ exports.deduplicateSessions = deduplicateSessions;
13
+ exports.deduplicateSessionsByUserId = deduplicateSessionsByUserId;
14
+ exports.normalizeAndSortSessions = normalizeAndSortSessions;
15
+ exports.mergeSessions = mergeSessions;
16
+ exports.sessionsArraysEqual = sessionsArraysEqual;
17
+ /**
18
+ * Normalize a session to ensure all required fields are present
19
+ */
20
+ function normalizeSession(session) {
21
+ const now = new Date().toISOString();
22
+ return {
23
+ sessionId: session.sessionId,
24
+ deviceId: session.deviceId || '',
25
+ expiresAt: session.expiresAt || now,
26
+ lastActive: session.lastActive || now,
27
+ userId: session.userId || '',
28
+ };
29
+ }
30
+ /**
31
+ * Compare two sessions for equality
32
+ */
33
+ function sessionsEqual(a, b) {
34
+ return a.sessionId === b.sessionId;
35
+ }
36
+ /**
37
+ * Sort sessions by lastActive (most recent first), then by sessionId for stability
38
+ */
39
+ function sortSessions(sessions) {
40
+ return [...sessions].sort((a, b) => {
41
+ // Sort by lastActive descending (most recent first)
42
+ const timeA = new Date(a.lastActive).getTime();
43
+ const timeB = new Date(b.lastActive).getTime();
44
+ if (timeA !== timeB) {
45
+ return timeB - timeA; // Descending order
46
+ }
47
+ // If lastActive is the same, sort by sessionId for stability
48
+ return a.sessionId.localeCompare(b.sessionId);
49
+ });
50
+ }
51
+ /**
52
+ * Deduplicate sessions by sessionId, keeping the most recent version
53
+ */
54
+ function deduplicateSessions(sessions) {
55
+ const sessionMap = new Map();
56
+ for (const session of sessions) {
57
+ const existing = sessionMap.get(session.sessionId);
58
+ if (!existing) {
59
+ sessionMap.set(session.sessionId, session);
60
+ }
61
+ else {
62
+ // Keep the one with more recent lastActive
63
+ const existingTime = new Date(existing.lastActive).getTime();
64
+ const currentTime = new Date(session.lastActive).getTime();
65
+ if (currentTime > existingTime) {
66
+ sessionMap.set(session.sessionId, session);
67
+ }
68
+ }
69
+ }
70
+ return Array.from(sessionMap.values());
71
+ }
72
+ /**
73
+ * Deduplicate sessions by userId, keeping only one session per user
74
+ * Priority: 1) Active session (if provided), 2) Most recent session
75
+ * This prevents showing duplicate accounts for the same user
76
+ */
77
+ function deduplicateSessionsByUserId(sessions, activeSessionId) {
78
+ if (!sessions.length)
79
+ return [];
80
+ const userSessionMap = new Map();
81
+ for (const session of sessions) {
82
+ if (!session.userId)
83
+ continue; // Skip sessions without userId
84
+ const existing = userSessionMap.get(session.userId);
85
+ if (!existing) {
86
+ userSessionMap.set(session.userId, session);
87
+ }
88
+ else {
89
+ // Prioritize active session
90
+ const isCurrentActive = activeSessionId && session.sessionId === activeSessionId;
91
+ const isExistingActive = activeSessionId && existing.sessionId === activeSessionId;
92
+ if (isCurrentActive && !isExistingActive) {
93
+ userSessionMap.set(session.userId, session);
94
+ }
95
+ else if (!isCurrentActive && isExistingActive) {
96
+ // Keep existing (active) session
97
+ continue;
98
+ }
99
+ else {
100
+ // Neither is active, keep the one with more recent lastActive
101
+ const existingTime = new Date(existing.lastActive).getTime();
102
+ const currentTime = new Date(session.lastActive).getTime();
103
+ if (currentTime > existingTime) {
104
+ userSessionMap.set(session.userId, session);
105
+ }
106
+ }
107
+ }
108
+ }
109
+ return Array.from(userSessionMap.values());
110
+ }
111
+ /**
112
+ * Normalize, deduplicate, and sort sessions
113
+ * This ensures consistent session ordering across the application
114
+ *
115
+ * @param sessions - Array of sessions to normalize
116
+ * @param activeSessionId - Optional active session ID to prioritize
117
+ * @param deduplicateByUserId - If true, deduplicate by userId (one account per user). Default: true
118
+ */
119
+ function normalizeAndSortSessions(sessions, activeSessionId, deduplicateByUserId = true) {
120
+ if (!sessions.length)
121
+ return [];
122
+ // Normalize all sessions
123
+ const normalized = sessions.map(normalizeSession);
124
+ // First deduplicate by sessionId (exact duplicates)
125
+ const deduplicatedBySessionId = deduplicateSessions(normalized);
126
+ // Then deduplicate by userId if requested (one account per user)
127
+ const finalSessions = deduplicateByUserId
128
+ ? deduplicateSessionsByUserId(deduplicatedBySessionId, activeSessionId)
129
+ : deduplicatedBySessionId;
130
+ // Sort consistently
131
+ return sortSessions(finalSessions);
132
+ }
133
+ /**
134
+ * Merge two session arrays, prioritizing newer data
135
+ * Returns normalized, deduplicated, and sorted sessions
136
+ *
137
+ * @param existing - Existing sessions array
138
+ * @param incoming - New sessions to merge in
139
+ * @param activeSessionId - Optional active session ID to prioritize
140
+ * @param deduplicateByUserId - If true, deduplicate by userId (one account per user). Default: true
141
+ */
142
+ function mergeSessions(existing, incoming, activeSessionId, deduplicateByUserId = true) {
143
+ if (!existing.length && !incoming.length)
144
+ return [];
145
+ if (!existing.length)
146
+ return normalizeAndSortSessions(incoming, activeSessionId, deduplicateByUserId);
147
+ if (!incoming.length)
148
+ return normalizeAndSortSessions(existing, activeSessionId, deduplicateByUserId);
149
+ // Normalize both arrays
150
+ const normalizedExisting = existing.map(normalizeSession);
151
+ const normalizedIncoming = incoming.map(normalizeSession);
152
+ // Create a map with existing sessions (by sessionId)
153
+ const sessionMap = new Map();
154
+ // Add existing sessions first
155
+ for (const session of normalizedExisting) {
156
+ sessionMap.set(session.sessionId, session);
157
+ }
158
+ // Merge incoming sessions - backend data always replaces existing
159
+ for (const session of normalizedIncoming) {
160
+ sessionMap.set(session.sessionId, session);
161
+ }
162
+ // Convert to array
163
+ const merged = Array.from(sessionMap.values());
164
+ // Apply userId deduplication if requested
165
+ const finalSessions = deduplicateByUserId
166
+ ? deduplicateSessionsByUserId(merged, activeSessionId)
167
+ : merged;
168
+ // Sort consistently
169
+ return sortSessions(finalSessions);
170
+ }
171
+ /**
172
+ * Check if two session arrays are equal (same sessionIds in same order)
173
+ */
174
+ function sessionsArraysEqual(a, b) {
175
+ if (a.length !== b.length) {
176
+ return false;
177
+ }
178
+ const sortedA = sortSessions(a);
179
+ const sortedB = sortSessions(b);
180
+ return sortedA.every((session, index) => sessionsEqual(session, sortedB[index]));
181
+ }
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ /**
3
+ * Validation utilities for common data validation patterns
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PASSWORD_REGEX = exports.USERNAME_REGEX = exports.EMAIL_REGEX = void 0;
7
+ exports.isValidEmail = isValidEmail;
8
+ exports.isValidUsername = isValidUsername;
9
+ exports.isValidPassword = isValidPassword;
10
+ exports.isRequiredString = isRequiredString;
11
+ exports.isRequiredNumber = isRequiredNumber;
12
+ exports.isRequiredBoolean = isRequiredBoolean;
13
+ exports.isValidArray = isValidArray;
14
+ exports.isValidObject = isValidObject;
15
+ exports.isValidUUID = isValidUUID;
16
+ exports.isValidURL = isValidURL;
17
+ exports.isValidDate = isValidDate;
18
+ exports.isValidFileSize = isValidFileSize;
19
+ exports.isValidFileType = isValidFileType;
20
+ exports.sanitizeString = sanitizeString;
21
+ exports.sanitizeHTML = sanitizeHTML;
22
+ exports.isValidObjectId = isValidObjectId;
23
+ exports.validateAndSanitizeUserInput = validateAndSanitizeUserInput;
24
+ /**
25
+ * Email validation regex
26
+ */
27
+ exports.EMAIL_REGEX = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
28
+ /**
29
+ * Username validation regex (alphanumeric, underscores, and hyphens, 3-30 chars)
30
+ */
31
+ exports.USERNAME_REGEX = /^[a-zA-Z0-9_-]{3,30}$/;
32
+ /**
33
+ * Password validation regex (at least 8 chars, 1 uppercase, 1 lowercase, 1 number)
34
+ */
35
+ // At least 8 characters (tests expect len>=8 without complexity requirements)
36
+ exports.PASSWORD_REGEX = /^.{8,}$/;
37
+ /**
38
+ * Validate email format
39
+ */
40
+ function isValidEmail(email) {
41
+ return exports.EMAIL_REGEX.test(email);
42
+ }
43
+ /**
44
+ * Validate username format
45
+ */
46
+ function isValidUsername(username) {
47
+ return exports.USERNAME_REGEX.test(username);
48
+ }
49
+ /**
50
+ * Validate password strength
51
+ */
52
+ function isValidPassword(password) {
53
+ return exports.PASSWORD_REGEX.test(password);
54
+ }
55
+ /**
56
+ * Validate required string
57
+ */
58
+ function isRequiredString(value) {
59
+ return typeof value === 'string' && value.trim().length > 0;
60
+ }
61
+ /**
62
+ * Validate required number
63
+ */
64
+ function isRequiredNumber(value) {
65
+ return typeof value === 'number' && !Number.isNaN(value);
66
+ }
67
+ /**
68
+ * Validate required boolean
69
+ */
70
+ function isRequiredBoolean(value) {
71
+ return typeof value === 'boolean';
72
+ }
73
+ /**
74
+ * Validate array
75
+ */
76
+ function isValidArray(value) {
77
+ return Array.isArray(value);
78
+ }
79
+ /**
80
+ * Validate object
81
+ */
82
+ function isValidObject(value) {
83
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
84
+ }
85
+ /**
86
+ * Validate UUID format
87
+ */
88
+ function isValidUUID(uuid) {
89
+ 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;
90
+ return UUID_REGEX.test(uuid);
91
+ }
92
+ /**
93
+ * Validate URL format
94
+ */
95
+ function isValidURL(url) {
96
+ try {
97
+ new URL(url);
98
+ return true;
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ }
104
+ /**
105
+ * Validate date string
106
+ */
107
+ function isValidDate(dateString) {
108
+ const date = new Date(dateString);
109
+ return !Number.isNaN(date.getTime());
110
+ }
111
+ /**
112
+ * Validate file size (in bytes)
113
+ */
114
+ function isValidFileSize(size, maxSize) {
115
+ return size > 0 && size <= maxSize;
116
+ }
117
+ /**
118
+ * Validate file type
119
+ */
120
+ function isValidFileType(filename, allowedTypes) {
121
+ const extension = filename.split('.').pop()?.toLowerCase();
122
+ return extension ? allowedTypes.includes(extension) : false;
123
+ }
124
+ /**
125
+ * Sanitize string input
126
+ */
127
+ function sanitizeString(input) {
128
+ // Remove HTML tags entirely and trim whitespace
129
+ return input.trim().replace(/<[^>]*>/g, '');
130
+ }
131
+ /**
132
+ * Sanitize HTML input
133
+ */
134
+ function sanitizeHTML(input) {
135
+ return input
136
+ .replace(/&/g, '&amp;')
137
+ .replace(/</g, '&lt;')
138
+ .replace(/>/g, '&gt;')
139
+ .replace(/"/g, '&quot;')
140
+ .replace(/'/g, '&#x27;');
141
+ }
142
+ /**
143
+ * Validate MongoDB ObjectId format
144
+ * Note: This is a basic format check. For full validation, use mongoose.Types.ObjectId.isValid()
145
+ * This function works in environments where mongoose may not be available (e.g., client-side)
146
+ */
147
+ function isValidObjectId(id) {
148
+ if (typeof id !== 'string') {
149
+ return false;
150
+ }
151
+ // MongoDB ObjectId is 24 hex characters
152
+ const OBJECT_ID_REGEX = /^[0-9a-fA-F]{24}$/;
153
+ return OBJECT_ID_REGEX.test(id);
154
+ }
155
+ /**
156
+ * Validate and sanitize user input
157
+ */
158
+ function validateAndSanitizeUserInput(input, type) {
159
+ if (typeof input !== 'string') {
160
+ return null;
161
+ }
162
+ const sanitized = sanitizeString(input);
163
+ switch (type) {
164
+ case 'email':
165
+ return isValidEmail(sanitized) ? sanitized : null;
166
+ case 'username':
167
+ return isValidUsername(sanitized) ? sanitized : null;
168
+ case 'string':
169
+ return isRequiredString(sanitized) ? sanitized : null;
170
+ default:
171
+ return null;
172
+ }
173
+ }
@@ -0,0 +1,356 @@
1
+ /**
2
+ * AuthManager - Centralized Authentication Manager
3
+ *
4
+ * Provides a unified authentication interface for all platforms.
5
+ * Handles token storage, session management, and auth state changes.
6
+ *
7
+ * @module core/AuthManager
8
+ */
9
+ /**
10
+ * Storage keys used by AuthManager.
11
+ */
12
+ const STORAGE_KEYS = {
13
+ ACCESS_TOKEN: 'oxy_access_token',
14
+ REFRESH_TOKEN: 'oxy_refresh_token',
15
+ SESSION: 'oxy_session',
16
+ USER: 'oxy_user',
17
+ AUTH_METHOD: 'oxy_auth_method',
18
+ };
19
+ /**
20
+ * Default in-memory storage for non-browser environments.
21
+ */
22
+ class MemoryStorage {
23
+ constructor() {
24
+ this.store = new Map();
25
+ }
26
+ getItem(key) {
27
+ return this.store.get(key) ?? null;
28
+ }
29
+ setItem(key, value) {
30
+ this.store.set(key, value);
31
+ }
32
+ removeItem(key) {
33
+ this.store.delete(key);
34
+ }
35
+ }
36
+ /**
37
+ * Browser localStorage adapter.
38
+ */
39
+ class LocalStorageAdapter {
40
+ getItem(key) {
41
+ if (typeof window === 'undefined')
42
+ return null;
43
+ try {
44
+ return localStorage.getItem(key);
45
+ }
46
+ catch {
47
+ return null;
48
+ }
49
+ }
50
+ setItem(key, value) {
51
+ if (typeof window === 'undefined')
52
+ return;
53
+ try {
54
+ localStorage.setItem(key, value);
55
+ }
56
+ catch {
57
+ // Storage full or blocked
58
+ }
59
+ }
60
+ removeItem(key) {
61
+ if (typeof window === 'undefined')
62
+ return;
63
+ try {
64
+ localStorage.removeItem(key);
65
+ }
66
+ catch {
67
+ // Ignore errors
68
+ }
69
+ }
70
+ }
71
+ /**
72
+ * AuthManager - Centralized authentication management.
73
+ *
74
+ * Provides a single point of control for:
75
+ * - Token storage and retrieval
76
+ * - Session management
77
+ * - Auth state change notifications
78
+ * - Multiple auth method support
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * const authManager = new AuthManager(oxyServices);
83
+ *
84
+ * // Listen for auth changes
85
+ * authManager.onAuthStateChange((user) => {
86
+ * console.log('Auth state changed:', user);
87
+ * });
88
+ *
89
+ * // Handle successful auth
90
+ * await authManager.handleAuthSuccess(session);
91
+ *
92
+ * // Sign out
93
+ * await authManager.signOut();
94
+ * ```
95
+ */
96
+ export class AuthManager {
97
+ constructor(oxyServices, config = {}) {
98
+ this.listeners = new Set();
99
+ this.currentUser = null;
100
+ this.refreshTimer = null;
101
+ this.oxyServices = oxyServices;
102
+ this.config = {
103
+ storage: config.storage ?? this.getDefaultStorage(),
104
+ autoRefresh: config.autoRefresh ?? true,
105
+ refreshBuffer: config.refreshBuffer ?? 5 * 60 * 1000, // 5 minutes
106
+ };
107
+ this.storage = this.config.storage;
108
+ }
109
+ /**
110
+ * Get default storage based on environment.
111
+ */
112
+ getDefaultStorage() {
113
+ if (typeof window !== 'undefined' && window.localStorage) {
114
+ return new LocalStorageAdapter();
115
+ }
116
+ return new MemoryStorage();
117
+ }
118
+ /**
119
+ * Subscribe to auth state changes.
120
+ *
121
+ * @param callback - Function called when auth state changes
122
+ * @returns Unsubscribe function
123
+ */
124
+ onAuthStateChange(callback) {
125
+ this.listeners.add(callback);
126
+ // Call immediately with current state
127
+ callback(this.currentUser);
128
+ return () => {
129
+ this.listeners.delete(callback);
130
+ };
131
+ }
132
+ /**
133
+ * Notify all listeners of auth state change.
134
+ */
135
+ notifyListeners() {
136
+ for (const listener of this.listeners) {
137
+ try {
138
+ listener(this.currentUser);
139
+ }
140
+ catch {
141
+ // Ignore listener errors
142
+ }
143
+ }
144
+ }
145
+ /**
146
+ * Handle successful authentication.
147
+ *
148
+ * @param session - Session response from auth
149
+ * @param method - Auth method used
150
+ */
151
+ async handleAuthSuccess(session, method = 'credentials') {
152
+ // Store tokens
153
+ if (session.accessToken) {
154
+ await this.storage.setItem(STORAGE_KEYS.ACCESS_TOKEN, session.accessToken);
155
+ this.oxyServices.httpService.setTokens(session.accessToken);
156
+ }
157
+ // Store refresh token if available
158
+ if (session.refreshToken) {
159
+ await this.storage.setItem(STORAGE_KEYS.REFRESH_TOKEN, session.refreshToken);
160
+ }
161
+ // Store session info
162
+ await this.storage.setItem(STORAGE_KEYS.SESSION, JSON.stringify({
163
+ sessionId: session.sessionId,
164
+ deviceId: session.deviceId,
165
+ expiresAt: session.expiresAt,
166
+ }));
167
+ // Store user only if it has valid required fields (not an empty placeholder)
168
+ if (session.user && typeof session.user.id === 'string' && session.user.id.length > 0) {
169
+ await this.storage.setItem(STORAGE_KEYS.USER, JSON.stringify(session.user));
170
+ this.currentUser = session.user;
171
+ }
172
+ // Store auth method
173
+ await this.storage.setItem(STORAGE_KEYS.AUTH_METHOD, method);
174
+ // Setup auto-refresh if enabled
175
+ if (this.config.autoRefresh && session.expiresAt) {
176
+ this.setupTokenRefresh(session.expiresAt);
177
+ }
178
+ // Notify listeners
179
+ this.notifyListeners();
180
+ }
181
+ /**
182
+ * Setup automatic token refresh.
183
+ */
184
+ setupTokenRefresh(expiresAt) {
185
+ if (this.refreshTimer) {
186
+ clearTimeout(this.refreshTimer);
187
+ }
188
+ const expiresAtMs = new Date(expiresAt).getTime();
189
+ const now = Date.now();
190
+ const refreshAt = expiresAtMs - this.config.refreshBuffer;
191
+ const delay = Math.max(0, refreshAt - now);
192
+ if (delay > 0) {
193
+ this.refreshTimer = setTimeout(() => {
194
+ this.refreshToken().catch(() => {
195
+ // Refresh failed, user will need to re-auth
196
+ });
197
+ }, delay);
198
+ }
199
+ }
200
+ /**
201
+ * Refresh the access token.
202
+ */
203
+ async refreshToken() {
204
+ const refreshToken = await this.storage.getItem(STORAGE_KEYS.REFRESH_TOKEN);
205
+ if (!refreshToken) {
206
+ return false;
207
+ }
208
+ try {
209
+ // Cast httpService to proper type (needed due to mixin composition)
210
+ const httpService = this.oxyServices.httpService;
211
+ const response = await httpService.request({
212
+ method: 'POST',
213
+ url: '/api/auth/refresh',
214
+ data: { refreshToken },
215
+ cache: false,
216
+ });
217
+ await this.handleAuthSuccess(response, 'credentials');
218
+ return true;
219
+ }
220
+ catch {
221
+ // Refresh failed, clear session and update state
222
+ await this.clearSession();
223
+ this.currentUser = null;
224
+ this.notifyListeners();
225
+ return false;
226
+ }
227
+ }
228
+ /**
229
+ * Sign out and clear all auth data.
230
+ */
231
+ async signOut() {
232
+ // Clear refresh timer
233
+ if (this.refreshTimer) {
234
+ clearTimeout(this.refreshTimer);
235
+ this.refreshTimer = null;
236
+ }
237
+ // Revoke FedCM credential if supported
238
+ try {
239
+ const services = this.oxyServices;
240
+ if (services.revokeFedCMCredential) {
241
+ await services.revokeFedCMCredential();
242
+ }
243
+ }
244
+ catch {
245
+ // Ignore FedCM errors
246
+ }
247
+ // Clear HTTP client tokens
248
+ this.oxyServices.httpService.setTokens('');
249
+ // Clear storage
250
+ await this.clearSession();
251
+ // Update state and notify
252
+ this.currentUser = null;
253
+ this.notifyListeners();
254
+ }
255
+ /**
256
+ * Clear session data from storage.
257
+ */
258
+ async clearSession() {
259
+ await this.storage.removeItem(STORAGE_KEYS.ACCESS_TOKEN);
260
+ await this.storage.removeItem(STORAGE_KEYS.REFRESH_TOKEN);
261
+ await this.storage.removeItem(STORAGE_KEYS.SESSION);
262
+ await this.storage.removeItem(STORAGE_KEYS.USER);
263
+ await this.storage.removeItem(STORAGE_KEYS.AUTH_METHOD);
264
+ }
265
+ /**
266
+ * Get current user.
267
+ */
268
+ getCurrentUser() {
269
+ return this.currentUser;
270
+ }
271
+ /**
272
+ * Check if user is authenticated.
273
+ */
274
+ isAuthenticated() {
275
+ return this.currentUser !== null;
276
+ }
277
+ /**
278
+ * Get stored access token.
279
+ */
280
+ async getAccessToken() {
281
+ return this.storage.getItem(STORAGE_KEYS.ACCESS_TOKEN);
282
+ }
283
+ /**
284
+ * Get the auth method used for current session.
285
+ */
286
+ async getAuthMethod() {
287
+ const method = await this.storage.getItem(STORAGE_KEYS.AUTH_METHOD);
288
+ return method;
289
+ }
290
+ /**
291
+ * Initialize auth state from storage.
292
+ *
293
+ * Call this on app startup to restore previous session.
294
+ */
295
+ async initialize() {
296
+ try {
297
+ // Try to restore user from storage
298
+ const userJson = await this.storage.getItem(STORAGE_KEYS.USER);
299
+ if (userJson) {
300
+ this.currentUser = JSON.parse(userJson);
301
+ }
302
+ // Restore token to HTTP client
303
+ const token = await this.storage.getItem(STORAGE_KEYS.ACCESS_TOKEN);
304
+ if (token) {
305
+ this.oxyServices.httpService.setTokens(token);
306
+ }
307
+ // Check session expiry
308
+ const sessionJson = await this.storage.getItem(STORAGE_KEYS.SESSION);
309
+ if (sessionJson) {
310
+ const session = JSON.parse(sessionJson);
311
+ if (session.expiresAt) {
312
+ const expiresAt = new Date(session.expiresAt).getTime();
313
+ if (expiresAt <= Date.now()) {
314
+ // Session expired, try refresh
315
+ const refreshed = await this.refreshToken();
316
+ if (!refreshed) {
317
+ await this.clearSession();
318
+ this.currentUser = null;
319
+ }
320
+ }
321
+ else if (this.config.autoRefresh) {
322
+ // Setup refresh timer
323
+ this.setupTokenRefresh(session.expiresAt);
324
+ }
325
+ }
326
+ }
327
+ return this.currentUser;
328
+ }
329
+ catch {
330
+ // Failed to restore, start fresh
331
+ await this.clearSession();
332
+ this.currentUser = null;
333
+ return null;
334
+ }
335
+ }
336
+ /**
337
+ * Destroy the auth manager and clean up resources.
338
+ */
339
+ destroy() {
340
+ if (this.refreshTimer) {
341
+ clearTimeout(this.refreshTimer);
342
+ this.refreshTimer = null;
343
+ }
344
+ this.listeners.clear();
345
+ }
346
+ }
347
+ /**
348
+ * Create an AuthManager instance.
349
+ *
350
+ * @param oxyServices - OxyServices instance
351
+ * @param config - Optional configuration
352
+ * @returns AuthManager instance
353
+ */
354
+ export function createAuthManager(oxyServices, config) {
355
+ return new AuthManager(oxyServices, config);
356
+ }