@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,55 @@
1
+ /**
2
+ * Utility functions for common API patterns
3
+ */
4
+ /**
5
+ * Build URL search parameters from an object
6
+ * @param params Object with parameter key-value pairs
7
+ * @returns URLSearchParams instance
8
+ */
9
+ export function buildSearchParams(params) {
10
+ const searchParams = new URLSearchParams();
11
+ for (const [key, value] of Object.entries(params)) {
12
+ if (value !== undefined && value !== null) {
13
+ searchParams.append(key, value.toString());
14
+ }
15
+ }
16
+ return searchParams;
17
+ }
18
+ /**
19
+ * Build URL with search parameters
20
+ * @param baseUrl Base URL
21
+ * @param params Object with parameter key-value pairs
22
+ * @returns Complete URL with search parameters
23
+ */
24
+ export function buildUrl(baseUrl, params) {
25
+ if (!params)
26
+ return baseUrl;
27
+ const searchParams = buildSearchParams(params);
28
+ const queryString = searchParams.toString();
29
+ return queryString ? `${baseUrl}?${queryString}` : baseUrl;
30
+ }
31
+ /**
32
+ * Build pagination search parameters
33
+ * @param params Pagination parameters
34
+ * @returns URLSearchParams with pagination
35
+ */
36
+ export function buildPaginationParams(params) {
37
+ return buildSearchParams(params);
38
+ }
39
+ /**
40
+ * Safe JSON parsing with error handling
41
+ * @param data Data to parse
42
+ * @param fallback Fallback value if parsing fails
43
+ * @returns Parsed data or fallback
44
+ */
45
+ export function safeJsonParse(data, fallback) {
46
+ if (typeof data === 'string') {
47
+ try {
48
+ return JSON.parse(data);
49
+ }
50
+ catch {
51
+ return fallback;
52
+ }
53
+ }
54
+ return data;
55
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * Async utilities for common asynchronous patterns and error handling
3
+ */
4
+ import { logger } from './loggerUtils';
5
+ /**
6
+ * Wrapper for async operations with automatic error handling
7
+ * Returns null on error instead of throwing
8
+ */
9
+ export async function withErrorHandling(operation, errorHandler, context) {
10
+ try {
11
+ return await operation();
12
+ }
13
+ catch (error) {
14
+ if (errorHandler) {
15
+ errorHandler(error);
16
+ }
17
+ else {
18
+ logger.error(`Error in ${context || 'operation'}`, error instanceof Error ? error : new Error(String(error)), {
19
+ component: 'asyncUtils',
20
+ method: 'withErrorHandling',
21
+ });
22
+ }
23
+ return null;
24
+ }
25
+ }
26
+ /**
27
+ * Execute multiple async operations in parallel with error handling
28
+ */
29
+ export async function parallelWithErrorHandling(operations, errorHandler) {
30
+ const results = await Promise.allSettled(operations.map((op, index) => withErrorHandling(op, error => errorHandler?.(error, index))));
31
+ return results.map(result => result.status === 'fulfilled' ? result.value : null);
32
+ }
33
+ /**
34
+ * Retry an async operation with exponential backoff
35
+ *
36
+ * By default, does not retry on 4xx errors (client errors).
37
+ * Use shouldRetry callback to customize retry behavior.
38
+ */
39
+ export async function retryAsync(operation, maxRetries = 3, baseDelay = 1000, shouldRetry) {
40
+ let lastError;
41
+ // Default shouldRetry: don't retry on 4xx errors
42
+ const defaultShouldRetry = (error) => {
43
+ // Don't retry on 4xx errors (client errors)
44
+ if (error?.response?.status >= 400 && error?.response?.status < 500) {
45
+ return false;
46
+ }
47
+ return true;
48
+ };
49
+ const retryCheck = shouldRetry || defaultShouldRetry;
50
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
51
+ try {
52
+ return await operation();
53
+ }
54
+ catch (error) {
55
+ lastError = error;
56
+ if (attempt === maxRetries) {
57
+ break;
58
+ }
59
+ if (!retryCheck(error)) {
60
+ break;
61
+ }
62
+ // Calculate delay with exponential backoff and jitter
63
+ const delay = baseDelay * Math.pow(2, attempt) + Math.random() * 1000;
64
+ await new Promise(resolve => setTimeout(resolve, delay));
65
+ }
66
+ }
67
+ throw lastError;
68
+ }
69
+ /**
70
+ * Debounce async function calls
71
+ */
72
+ export function debounceAsync(func, delay) {
73
+ let timeoutId;
74
+ const lastPromise = null;
75
+ return (...args) => {
76
+ return new Promise((resolve, reject) => {
77
+ clearTimeout(timeoutId);
78
+ timeoutId = setTimeout(async () => {
79
+ try {
80
+ const result = await func(...args);
81
+ resolve(result);
82
+ }
83
+ catch (error) {
84
+ reject(error);
85
+ }
86
+ }, delay);
87
+ });
88
+ };
89
+ }
90
+ /**
91
+ * Throttle async function calls
92
+ */
93
+ export function throttleAsync(func, limit, interval) {
94
+ let inThrottle = false;
95
+ let lastPromise = null;
96
+ return (...args) => {
97
+ if (inThrottle) {
98
+ return lastPromise;
99
+ }
100
+ inThrottle = true;
101
+ lastPromise = func(...args);
102
+ setTimeout(() => {
103
+ inThrottle = false;
104
+ }, interval);
105
+ return lastPromise;
106
+ };
107
+ }
108
+ /**
109
+ * Execute async operations sequentially with progress tracking
110
+ */
111
+ export async function sequentialWithProgress(operations, onProgress) {
112
+ const results = [];
113
+ for (let i = 0; i < operations.length; i++) {
114
+ const result = await operations[i]();
115
+ results.push(result);
116
+ onProgress?.(i + 1, operations.length);
117
+ }
118
+ return results;
119
+ }
120
+ /**
121
+ * Batch async operations
122
+ */
123
+ export async function batchAsync(items, batchSize, processor) {
124
+ for (let i = 0; i < items.length; i += batchSize) {
125
+ const batch = items.slice(i, i + batchSize);
126
+ await processor(batch);
127
+ }
128
+ }
129
+ /**
130
+ * Create a cancellable async operation
131
+ */
132
+ export function createCancellableAsync(operation) {
133
+ let abortController = null;
134
+ return {
135
+ execute: async () => {
136
+ abortController = new AbortController();
137
+ return await operation(abortController.signal);
138
+ },
139
+ cancel: () => {
140
+ abortController?.abort();
141
+ }
142
+ };
143
+ }
144
+ /**
145
+ * Timeout wrapper for async operations
146
+ */
147
+ export async function withTimeout(operation, timeoutMs, timeoutMessage) {
148
+ const timeoutPromise = new Promise((_, reject) => {
149
+ setTimeout(() => {
150
+ reject(new Error(timeoutMessage || `Operation timed out after ${timeoutMs}ms`));
151
+ }, timeoutMs);
152
+ });
153
+ return Promise.race([operation, timeoutPromise]);
154
+ }
155
+ /**
156
+ * Execute async operation with loading state
157
+ */
158
+ export async function withLoadingState(operation, setLoading) {
159
+ setLoading(true);
160
+ try {
161
+ return await operation();
162
+ }
163
+ finally {
164
+ setLoading(false);
165
+ }
166
+ }
167
+ /**
168
+ * Create a promise that resolves after a delay
169
+ */
170
+ export const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
171
+ /**
172
+ * Execute async operation with retry on specific errors
173
+ */
174
+ export async function retryOnError(operation, retryableErrors, maxRetries = 3) {
175
+ return retryAsync(operation, maxRetries, 1000, (error) => {
176
+ const errorCode = error?.code || error?.status || error?.message;
177
+ return retryableErrors.includes(errorCode);
178
+ });
179
+ }
@@ -0,0 +1,218 @@
1
+ /**
2
+ * Centralized cache utility with TTL support
3
+ *
4
+ * This is a production-ready cache implementation used across the codebase
5
+ * for consistent caching behavior and performance optimization.
6
+ */
7
+ /**
8
+ * TTL-based cache implementation
9
+ *
10
+ * Features:
11
+ * - Automatic expiration based on TTL
12
+ * - Manual cleanup of expired entries
13
+ * - Statistics tracking (hits, misses, hit rate)
14
+ * - Type-safe generic interface
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const cache = new TTLCache<string>(5 * 60 * 1000); // 5 minutes
19
+ *
20
+ * // Set with default TTL
21
+ * cache.set('key', 'value');
22
+ *
23
+ * // Set with custom TTL
24
+ * cache.set('key', 'value', 10 * 60 * 1000); // 10 minutes
25
+ *
26
+ * // Get value
27
+ * const value = cache.get('key');
28
+ *
29
+ * // Get statistics
30
+ * const stats = cache.getStats();
31
+ * ```
32
+ */
33
+ export class TTLCache {
34
+ /**
35
+ * Create a new TTL cache
36
+ * @param defaultTTL Default TTL in milliseconds (default: 5 minutes)
37
+ */
38
+ constructor(defaultTTL = 5 * 60 * 1000) {
39
+ this.cache = new Map();
40
+ this.hits = 0;
41
+ this.misses = 0;
42
+ this.defaultTTL = defaultTTL;
43
+ }
44
+ /**
45
+ * Get a value from cache
46
+ * @param key Cache key
47
+ * @returns Cached value or null if not found or expired
48
+ */
49
+ get(key) {
50
+ const entry = this.cache.get(key);
51
+ if (!entry) {
52
+ this.misses++;
53
+ return null;
54
+ }
55
+ const now = Date.now();
56
+ if (now > entry.expiresAt) {
57
+ this.cache.delete(key);
58
+ this.misses++;
59
+ return null;
60
+ }
61
+ this.hits++;
62
+ return entry.data;
63
+ }
64
+ /**
65
+ * Set a value in cache
66
+ * @param key Cache key
67
+ * @param data Data to cache
68
+ * @param ttl Optional TTL override (uses default if not provided)
69
+ */
70
+ set(key, data, ttl) {
71
+ const now = Date.now();
72
+ const expiresAt = now + (ttl || this.defaultTTL);
73
+ this.cache.set(key, { data, timestamp: now, expiresAt });
74
+ }
75
+ /**
76
+ * Delete a specific cache entry
77
+ * @param key Cache key
78
+ * @returns true if entry was deleted, false if not found
79
+ */
80
+ delete(key) {
81
+ return this.cache.delete(key);
82
+ }
83
+ /**
84
+ * Clear all cache entries
85
+ */
86
+ clear() {
87
+ this.cache.clear();
88
+ this.hits = 0;
89
+ this.misses = 0;
90
+ }
91
+ /**
92
+ * Check if a key exists and is not expired
93
+ * @param key Cache key
94
+ * @returns true if key exists and is valid
95
+ */
96
+ has(key) {
97
+ const entry = this.cache.get(key);
98
+ if (!entry)
99
+ return false;
100
+ if (Date.now() > entry.expiresAt) {
101
+ this.cache.delete(key);
102
+ return false;
103
+ }
104
+ return true;
105
+ }
106
+ /**
107
+ * Get all valid cache keys
108
+ * @returns Array of valid cache keys
109
+ */
110
+ keys() {
111
+ const now = Date.now();
112
+ const validKeys = [];
113
+ for (const [key, entry] of this.cache.entries()) {
114
+ if (now <= entry.expiresAt) {
115
+ validKeys.push(key);
116
+ }
117
+ else {
118
+ this.cache.delete(key);
119
+ }
120
+ }
121
+ return validKeys;
122
+ }
123
+ /**
124
+ * Clean up expired entries
125
+ * @returns Number of entries removed
126
+ */
127
+ cleanup() {
128
+ const now = Date.now();
129
+ let removed = 0;
130
+ for (const [key, entry] of this.cache.entries()) {
131
+ if (now > entry.expiresAt) {
132
+ this.cache.delete(key);
133
+ removed++;
134
+ }
135
+ }
136
+ return removed;
137
+ }
138
+ /**
139
+ * Get cache size (number of entries)
140
+ */
141
+ size() {
142
+ return this.cache.size;
143
+ }
144
+ /**
145
+ * Get cache statistics
146
+ */
147
+ getStats() {
148
+ const total = this.hits + this.misses;
149
+ return {
150
+ size: this.cache.size,
151
+ hits: this.hits,
152
+ misses: this.misses,
153
+ hitRate: total > 0 ? this.hits / total : 0,
154
+ };
155
+ }
156
+ /**
157
+ * Reset statistics (keeps cache entries)
158
+ */
159
+ resetStats() {
160
+ this.hits = 0;
161
+ this.misses = 0;
162
+ }
163
+ }
164
+ /**
165
+ * Create a TTL cache instance (convenience function)
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * const cache = createCache<string>(5 * 60 * 1000);
170
+ * ```
171
+ */
172
+ export function createCache(ttl = 5 * 60 * 1000) {
173
+ return new TTLCache(ttl);
174
+ }
175
+ /**
176
+ * Global cache cleanup interval (runs every minute)
177
+ * This helps prevent memory leaks from expired cache entries
178
+ */
179
+ let cleanupInterval = null;
180
+ const activeCaches = new Set();
181
+ /**
182
+ * Register a cache for automatic cleanup
183
+ * @param cache Cache instance to register
184
+ */
185
+ export function registerCacheForCleanup(cache) {
186
+ activeCaches.add(cache);
187
+ // Start cleanup interval if not already running
188
+ if (!cleanupInterval) {
189
+ cleanupInterval = setInterval(() => {
190
+ for (const cache of activeCaches) {
191
+ cache.cleanup();
192
+ }
193
+ }, 60000); // Every minute
194
+ }
195
+ }
196
+ /**
197
+ * Unregister a cache from automatic cleanup
198
+ * @param cache Cache instance to unregister
199
+ */
200
+ export function unregisterCacheFromCleanup(cache) {
201
+ activeCaches.delete(cache);
202
+ // Stop cleanup interval if no caches are registered
203
+ if (activeCaches.size === 0 && cleanupInterval) {
204
+ clearInterval(cleanupInterval);
205
+ cleanupInterval = null;
206
+ }
207
+ }
208
+ /**
209
+ * Stop all cleanup intervals (useful for testing)
210
+ * This will clear the interval and unregister all caches
211
+ */
212
+ export function stopAllCleanupIntervals() {
213
+ if (cleanupInterval) {
214
+ clearInterval(cleanupInterval);
215
+ cleanupInterval = null;
216
+ }
217
+ activeCaches.clear();
218
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * Client-side device management utility
3
+ * Handles persistent device identification across app sessions
4
+ */
5
+ export class DeviceManager {
6
+ /**
7
+ * Check if we're in React Native environment
8
+ */
9
+ static isReactNative() {
10
+ return typeof navigator !== 'undefined' && navigator.product === 'ReactNative';
11
+ }
12
+ /**
13
+ * Get appropriate storage for the platform
14
+ */
15
+ static async getStorage() {
16
+ if (this.isReactNative()) {
17
+ try {
18
+ const asyncStorageModule = await import('@react-native-async-storage/async-storage');
19
+ const storage = asyncStorageModule.default;
20
+ return {
21
+ getItem: storage.getItem.bind(storage),
22
+ setItem: storage.setItem.bind(storage),
23
+ removeItem: storage.removeItem.bind(storage),
24
+ };
25
+ }
26
+ catch (error) {
27
+ console.error('AsyncStorage not available in React Native:', error);
28
+ throw new Error('AsyncStorage is required in React Native environment');
29
+ }
30
+ }
31
+ else {
32
+ // Use localStorage for web
33
+ return {
34
+ getItem: async (key) => localStorage.getItem(key),
35
+ setItem: async (key, value) => localStorage.setItem(key, value),
36
+ removeItem: async (key) => localStorage.removeItem(key)
37
+ };
38
+ }
39
+ }
40
+ /**
41
+ * Get or create device fingerprint for current device
42
+ */
43
+ static getDeviceFingerprint() {
44
+ const fingerprint = {
45
+ userAgent: typeof navigator !== 'undefined' ? navigator.userAgent : 'unknown',
46
+ platform: typeof navigator !== 'undefined' ? navigator.platform : 'unknown',
47
+ language: typeof navigator !== 'undefined' ? navigator.language : undefined,
48
+ timezone: typeof Intl !== 'undefined' ? Intl.DateTimeFormat().resolvedOptions().timeZone : undefined,
49
+ };
50
+ // Add screen info if available
51
+ if (typeof screen !== 'undefined') {
52
+ fingerprint.screen = {
53
+ width: screen.width,
54
+ height: screen.height,
55
+ colorDepth: screen.colorDepth
56
+ };
57
+ }
58
+ return fingerprint;
59
+ }
60
+ /**
61
+ * Get stored device info or create new one
62
+ */
63
+ static async getDeviceInfo() {
64
+ try {
65
+ const storage = await this.getStorage();
66
+ const stored = await storage.getItem(this.DEVICE_KEY);
67
+ if (stored) {
68
+ const deviceInfo = JSON.parse(stored);
69
+ // Update last used timestamp
70
+ deviceInfo.lastUsed = new Date().toISOString();
71
+ await this.saveDeviceInfo(deviceInfo);
72
+ return deviceInfo;
73
+ }
74
+ // Create new device info
75
+ return await this.createNewDeviceInfo();
76
+ }
77
+ catch (error) {
78
+ console.error('Error getting device info:', error);
79
+ return await this.createNewDeviceInfo();
80
+ }
81
+ }
82
+ /**
83
+ * Create new device info and store it
84
+ */
85
+ static async createNewDeviceInfo() {
86
+ const deviceInfo = {
87
+ deviceId: this.generateDeviceId(),
88
+ fingerprint: JSON.stringify(this.getDeviceFingerprint()),
89
+ createdAt: new Date().toISOString(),
90
+ lastUsed: new Date().toISOString()
91
+ };
92
+ await this.saveDeviceInfo(deviceInfo);
93
+ return deviceInfo;
94
+ }
95
+ /**
96
+ * Save device info to storage
97
+ */
98
+ static async saveDeviceInfo(deviceInfo) {
99
+ try {
100
+ const storage = await this.getStorage();
101
+ await storage.setItem(this.DEVICE_KEY, JSON.stringify(deviceInfo));
102
+ }
103
+ catch (error) {
104
+ console.error('Error saving device info:', error);
105
+ }
106
+ }
107
+ /**
108
+ * Update device name
109
+ */
110
+ static async updateDeviceName(deviceName) {
111
+ try {
112
+ const deviceInfo = await this.getDeviceInfo();
113
+ deviceInfo.deviceName = deviceName;
114
+ await this.saveDeviceInfo(deviceInfo);
115
+ }
116
+ catch (error) {
117
+ console.error('Error updating device name:', error);
118
+ }
119
+ }
120
+ /**
121
+ * Clear stored device info (useful for testing or reset)
122
+ */
123
+ static async clearDeviceInfo() {
124
+ try {
125
+ const storage = await this.getStorage();
126
+ await storage.removeItem(this.DEVICE_KEY);
127
+ }
128
+ catch (error) {
129
+ console.error('Error clearing device info:', error);
130
+ }
131
+ }
132
+ /**
133
+ * Generate a unique device ID
134
+ */
135
+ static generateDeviceId() {
136
+ // Use crypto.getRandomValues if available, otherwise fallback to Math.random
137
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
138
+ const array = new Uint8Array(32);
139
+ crypto.getRandomValues(array);
140
+ return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
141
+ }
142
+ else {
143
+ // Fallback for environments without crypto.getRandomValues
144
+ return 'device_' + Date.now().toString(36) + Math.random().toString(36).substr(2);
145
+ }
146
+ }
147
+ /**
148
+ * Get a user-friendly device name based on platform
149
+ */
150
+ static getDefaultDeviceName() {
151
+ const fingerprint = this.getDeviceFingerprint();
152
+ const platform = (fingerprint.platform || '').toLowerCase();
153
+ if (platform.includes('win'))
154
+ return 'Windows Computer';
155
+ if (platform.includes('mac'))
156
+ return 'Mac Computer';
157
+ if (platform.includes('linux'))
158
+ return 'Linux Computer';
159
+ if (platform.includes('iphone'))
160
+ return 'iPhone';
161
+ if (platform.includes('ipad'))
162
+ return 'iPad';
163
+ if (platform.includes('android'))
164
+ return 'Android Device';
165
+ return 'Unknown Device';
166
+ }
167
+ }
168
+ DeviceManager.DEVICE_KEY = 'oxy_device_info';