@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,412 @@
1
+ import type { AccountStorageUsageResponse, AssetUrlResponse, AssetVariant } from '../models/interfaces';
2
+ import type { OxyServicesBase } from '../OxyServices.base';
3
+
4
+ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T) {
5
+ return class extends Base {
6
+ constructor(...args: any[]) {
7
+ super(...(args as [any]));
8
+ }
9
+
10
+ /**
11
+ * Delete file
12
+ */
13
+ async deleteFile(fileId: string): Promise<any> {
14
+ try {
15
+ return await this.makeRequest('DELETE', `/api/assets/${encodeURIComponent(fileId)}`, undefined, { cache: false });
16
+ } catch (error) {
17
+ throw this.handleError(error);
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Get file download URL (synchronous - uses stream endpoint for images to avoid ORB blocking)
23
+ */
24
+ getFileDownloadUrl(fileId: string, variant?: string, expiresIn?: number): string {
25
+ const base = this.getBaseURL();
26
+ const params = new URLSearchParams();
27
+ if (variant) params.set('variant', variant);
28
+ if (expiresIn) params.set('expiresIn', String(expiresIn));
29
+ params.set('fallback', 'placeholderVisible');
30
+ const token = this.getClient().getAccessToken();
31
+ if (token) params.set('token', token);
32
+
33
+ const qs = params.toString();
34
+ return `${base}/api/assets/${encodeURIComponent(fileId)}/stream${qs ? `?${qs}` : ''}`;
35
+ }
36
+
37
+ /**
38
+ * Get file download URL asynchronously (returns signed URL directly from CDN)
39
+ */
40
+ async getFileDownloadUrlAsync(fileId: string, variant?: string, expiresIn?: number): Promise<string> {
41
+ try {
42
+ const url = await this.fetchAssetDownloadUrl(
43
+ fileId,
44
+ variant,
45
+ this.getAssetUrlCacheTTL(expiresIn),
46
+ expiresIn
47
+ );
48
+
49
+ return url || this.getFileDownloadUrl(fileId, variant, expiresIn);
50
+ } catch (error) {
51
+ return this.getFileDownloadUrl(fileId, variant, expiresIn);
52
+ }
53
+ }
54
+
55
+ /**
56
+ * List user files
57
+ */
58
+ async listUserFiles(limit?: number, offset?: number): Promise<{ files: any[]; total: number; hasMore: boolean }> {
59
+ try {
60
+ const paramsObj: any = {};
61
+ if (limit) paramsObj.limit = limit;
62
+ if (offset) paramsObj.offset = offset;
63
+ return await this.makeRequest('GET', '/api/assets', paramsObj, {
64
+ cache: false,
65
+ });
66
+ } catch (error) {
67
+ throw this.handleError(error);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Get account storage usage (server-side usage aggregated from assets)
73
+ */
74
+ async getAccountStorageUsage(): Promise<AccountStorageUsageResponse> {
75
+ try {
76
+ return await this.makeRequest<AccountStorageUsageResponse>('GET', '/api/storage/usage', undefined, {
77
+ cache: false,
78
+ });
79
+ } catch (error) {
80
+ throw this.handleError(error);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Get file content as text
86
+ */
87
+ async getFileContentAsText(fileId: string, variant?: string): Promise<string> {
88
+ try {
89
+ const downloadUrl = await this.fetchAssetDownloadUrl(
90
+ fileId,
91
+ variant,
92
+ this.getAssetUrlCacheTTL()
93
+ );
94
+
95
+ if (!downloadUrl) {
96
+ throw new Error('No download URL returned for asset');
97
+ }
98
+
99
+ return await this.fetchAssetContent(downloadUrl, 'text');
100
+ } catch (error) {
101
+ throw this.handleError(error);
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Get file content as blob
107
+ */
108
+ async getFileContentAsBlob(fileId: string, variant?: string): Promise<Blob> {
109
+ try {
110
+ const downloadUrl = await this.fetchAssetDownloadUrl(
111
+ fileId,
112
+ variant,
113
+ this.getAssetUrlCacheTTL()
114
+ );
115
+
116
+ if (!downloadUrl) {
117
+ throw new Error('No download URL returned for asset');
118
+ }
119
+
120
+ return await this.fetchAssetContent(downloadUrl, 'blob');
121
+ } catch (error) {
122
+ throw this.handleError(error);
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Get batch access to multiple files
128
+ */
129
+ async getBatchFileAccess(fileIds: string[], context?: string): Promise<Record<string, any>> {
130
+ try {
131
+ return await this.makeRequest('POST', '/api/assets/batch-access', {
132
+ fileIds,
133
+ context
134
+ });
135
+ } catch (error) {
136
+ throw this.handleError(error);
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Get download URLs for multiple files efficiently
142
+ */
143
+ async getFileDownloadUrls(fileIds: string[], context?: string): Promise<Record<string, string>> {
144
+ const response: any = await this.getBatchFileAccess(fileIds, context);
145
+ const urls: Record<string, string> = {};
146
+ const results = response.results || {};
147
+ for (const [id, result] of Object.entries(results as Record<string, any>)) {
148
+ if (result.allowed && result.url) {
149
+ urls[id] = result.url;
150
+ }
151
+ }
152
+ return urls;
153
+ }
154
+
155
+ /**
156
+ * Upload raw file data
157
+ */
158
+ async uploadRawFile(file: File | Blob, visibility?: 'private' | 'public' | 'unlisted', metadata?: Record<string, any>): Promise<any> {
159
+ return this.assetUpload(file as File, visibility, metadata);
160
+ }
161
+
162
+ /**
163
+ * Upload file using Central Asset Service
164
+ */
165
+ async assetUpload(file: File, visibility?: 'private' | 'public' | 'unlisted', metadata?: Record<string, any>, onProgress?: (progress: number) => void): Promise<any> {
166
+ const fileName = file.name || 'unknown';
167
+ const fileSize = file.size;
168
+
169
+ try {
170
+ const formData = new FormData();
171
+ // Convert File to Blob to avoid read-only 'name' property error in Expo 54
172
+ // This is a known issue in Expo SDK 52+ where FormData tries to set the read-only 'name' property
173
+ let fileBlob: Blob;
174
+ if (file instanceof Blob) {
175
+ // Already a Blob, use directly
176
+ fileBlob = file;
177
+ } else if (typeof (file as any).blob === 'function') {
178
+ // Use async blob() method if available (Expo 54+ recommended approach)
179
+ fileBlob = await (file as any).blob();
180
+ } else {
181
+ // Fallback: create Blob from File (works in all environments)
182
+ fileBlob = new Blob([file], { type: (file as any).type || 'application/octet-stream' });
183
+ }
184
+ formData.append('file', fileBlob, fileName);
185
+ if (visibility) {
186
+ formData.append('visibility', visibility);
187
+ }
188
+ if (metadata) {
189
+ formData.append('metadata', JSON.stringify(metadata));
190
+ }
191
+
192
+ const response = await this.getClient().request<{ file: any }>({
193
+ method: 'POST',
194
+ url: '/api/assets/upload',
195
+ data: formData,
196
+ cache: false,
197
+ });
198
+
199
+ if (onProgress && response) {
200
+ onProgress(100);
201
+ }
202
+
203
+ return response;
204
+ } catch (error) {
205
+ console.error('File upload error:', error);
206
+
207
+ let errorMessage = 'File upload failed';
208
+
209
+ if (error instanceof Error) {
210
+ errorMessage = error.message || errorMessage;
211
+ } else if (error && typeof error === 'object') {
212
+ if ('message' in error) {
213
+ errorMessage = String((error as any).message) || errorMessage;
214
+ } else if ('error' in error && typeof (error as any).error === 'string') {
215
+ errorMessage = (error as any).error;
216
+ } else if ('data' in error && (error as any).data?.message) {
217
+ errorMessage = String((error as any).data.message);
218
+ }
219
+ } else if (error) {
220
+ errorMessage = String(error) || errorMessage;
221
+ }
222
+
223
+ const contextError = error as Error & { fileContext?: Record<string, unknown> };
224
+ if (!contextError.fileContext) {
225
+ contextError.fileContext = {
226
+ fileName,
227
+ fileSize,
228
+ };
229
+ }
230
+
231
+ if (error instanceof Error && error.message) {
232
+ const handledError = this.handleError(contextError);
233
+ if (!handledError.message || handledError.message.trim() === 'An unexpected error occurred') {
234
+ handledError.message = errorMessage;
235
+ }
236
+ throw handledError;
237
+ }
238
+
239
+ const newError = new Error(errorMessage);
240
+ (newError as any).fileContext = contextError.fileContext;
241
+ throw this.handleError(newError);
242
+ }
243
+ }
244
+
245
+ /**
246
+ * Link asset to an entity
247
+ */
248
+ async assetLink(fileId: string, app: string, entityType: string, entityId: string, visibility?: 'private' | 'public' | 'unlisted', webhookUrl?: string): Promise<any> {
249
+ try {
250
+ const body: any = { app, entityType, entityId };
251
+ if (visibility) body.visibility = visibility;
252
+ if (webhookUrl) body.webhookUrl = webhookUrl;
253
+ return await this.makeRequest('POST', `/api/assets/${fileId}/links`, body, { cache: false });
254
+ } catch (error) {
255
+ throw this.handleError(error);
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Unlink asset from an entity
261
+ */
262
+ async assetUnlink(fileId: string, app: string, entityType: string, entityId: string): Promise<any> {
263
+ try {
264
+ return await this.makeRequest('DELETE', `/api/assets/${fileId}/links`, {
265
+ app,
266
+ entityType,
267
+ entityId
268
+ }, { cache: false });
269
+ } catch (error) {
270
+ throw this.handleError(error);
271
+ }
272
+ }
273
+
274
+ /**
275
+ * Get asset metadata
276
+ */
277
+ async assetGet(fileId: string): Promise<any> {
278
+ try {
279
+ return await this.makeRequest('GET', `/api/assets/${fileId}`, undefined, {
280
+ cache: true,
281
+ cacheTTL: 5 * 60 * 1000,
282
+ });
283
+ } catch (error) {
284
+ throw this.handleError(error);
285
+ }
286
+ }
287
+
288
+ /**
289
+ * Get asset URL (CDN or signed URL)
290
+ */
291
+ async assetGetUrl(fileId: string, variant?: string, expiresIn?: number): Promise<AssetUrlResponse> {
292
+ try {
293
+ const params: any = {};
294
+ if (variant) params.variant = variant;
295
+ if (expiresIn) params.expiresIn = expiresIn;
296
+
297
+ return await this.makeRequest<AssetUrlResponse>('GET', `/api/assets/${fileId}/url`, params, {
298
+ cache: true,
299
+ cacheTTL: 10 * 60 * 1000,
300
+ });
301
+ } catch (error) {
302
+ throw this.handleError(error);
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Restore asset from trash
308
+ */
309
+ async assetRestore(fileId: string): Promise<any> {
310
+ try {
311
+ return await this.makeRequest('POST', `/api/assets/${fileId}/restore`, undefined, { cache: false });
312
+ } catch (error) {
313
+ throw this.handleError(error);
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Delete asset with optional force
319
+ */
320
+ async assetDelete(fileId: string, force: boolean = false): Promise<any> {
321
+ try {
322
+ const params: any = force ? { force: 'true' } : undefined;
323
+ return await this.makeRequest('DELETE', `/api/assets/${fileId}`, params, { cache: false });
324
+ } catch (error) {
325
+ throw this.handleError(error);
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Get list of available variants for an asset
331
+ */
332
+ async assetGetVariants(fileId: string): Promise<AssetVariant[]> {
333
+ try {
334
+ const assetData = await this.assetGet(fileId);
335
+ return assetData.file?.variants || [];
336
+ } catch (error) {
337
+ throw this.handleError(error);
338
+ }
339
+ }
340
+
341
+ /**
342
+ * Update asset visibility
343
+ */
344
+ async assetUpdateVisibility(fileId: string, visibility: 'private' | 'public' | 'unlisted'): Promise<any> {
345
+ try {
346
+ return await this.makeRequest('PATCH', `/api/assets/${fileId}/visibility`, {
347
+ visibility
348
+ }, { cache: false });
349
+ } catch (error) {
350
+ throw this.handleError(error);
351
+ }
352
+ }
353
+
354
+ async uploadAvatar(file: File, userId: string, app: string = 'profiles'): Promise<any> {
355
+ try {
356
+ const asset = await this.assetUpload(file, 'public');
357
+ await this.assetLink(asset.file.id, app, 'avatar', userId, 'public');
358
+ return asset;
359
+ } catch (error) {
360
+ throw this.handleError(error);
361
+ }
362
+ }
363
+
364
+ async uploadProfileBanner(file: File, userId: string, app: string = 'profiles'): Promise<any> {
365
+ try {
366
+ const asset = await this.assetUpload(file, 'public');
367
+ await this.assetLink(asset.file.id, app, 'profile-banner', userId, 'public');
368
+ return asset;
369
+ } catch (error) {
370
+ throw this.handleError(error);
371
+ }
372
+ }
373
+
374
+ public getAssetUrlCacheTTL(expiresIn?: number) {
375
+ const desiredTtlMs = (expiresIn ?? 3600) * 1000;
376
+ return Math.min(desiredTtlMs, 10 * 60 * 1000);
377
+ }
378
+
379
+ public async fetchAssetDownloadUrl(
380
+ fileId: string,
381
+ variant?: string,
382
+ cacheTTL?: number,
383
+ expiresIn?: number
384
+ ): Promise<string | null> {
385
+ const params: any = {};
386
+ if (variant) params.variant = variant;
387
+ if (expiresIn) params.expiresIn = expiresIn;
388
+
389
+ const urlRes = await this.makeRequest<{ url: string }>(
390
+ 'GET',
391
+ `/api/assets/${encodeURIComponent(fileId)}/url`,
392
+ Object.keys(params).length ? params : undefined,
393
+ {
394
+ cache: true,
395
+ cacheTTL: cacheTTL ?? 10 * 60 * 1000,
396
+ }
397
+ );
398
+
399
+ return urlRes?.url || null;
400
+ }
401
+
402
+ public async fetchAssetContent(url: string, type: 'text'): Promise<string>;
403
+ public async fetchAssetContent(url: string, type: 'blob'): Promise<Blob>;
404
+ public async fetchAssetContent(url: string, type: 'text' | 'blob') {
405
+ const response = await fetch(url, { credentials: 'include' });
406
+ if (!response?.ok) {
407
+ throw new Error(`Failed to fetch asset content (status ${response?.status})`);
408
+ }
409
+ return type === 'text' ? response.text() : response.blob();
410
+ }
411
+ };
412
+ }