@oxyhq/services 5.13.15 → 5.13.17

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 (138) hide show
  1. package/README.md +10 -0
  2. package/lib/commonjs/core/OxyServices.base.js +271 -0
  3. package/lib/commonjs/core/OxyServices.base.js.map +1 -0
  4. package/lib/commonjs/core/OxyServices.errors.js +26 -0
  5. package/lib/commonjs/core/OxyServices.errors.js.map +1 -0
  6. package/lib/commonjs/core/OxyServices.js +58 -2168
  7. package/lib/commonjs/core/OxyServices.js.map +1 -1
  8. package/lib/commonjs/core/mixins/OxyServices.analytics.js +60 -0
  9. package/lib/commonjs/core/mixins/OxyServices.analytics.js.map +1 -0
  10. package/lib/commonjs/core/mixins/OxyServices.assets.js +424 -0
  11. package/lib/commonjs/core/mixins/OxyServices.assets.js.map +1 -0
  12. package/lib/commonjs/core/mixins/OxyServices.auth.js +303 -0
  13. package/lib/commonjs/core/mixins/OxyServices.auth.js.map +1 -0
  14. package/lib/commonjs/core/mixins/OxyServices.developer.js +115 -0
  15. package/lib/commonjs/core/mixins/OxyServices.developer.js.map +1 -0
  16. package/lib/commonjs/core/mixins/OxyServices.devices.js +119 -0
  17. package/lib/commonjs/core/mixins/OxyServices.devices.js.map +1 -0
  18. package/lib/commonjs/core/mixins/OxyServices.karma.js +117 -0
  19. package/lib/commonjs/core/mixins/OxyServices.karma.js.map +1 -0
  20. package/lib/commonjs/core/mixins/OxyServices.language.js +124 -0
  21. package/lib/commonjs/core/mixins/OxyServices.language.js.map +1 -0
  22. package/lib/commonjs/core/mixins/OxyServices.location.js +55 -0
  23. package/lib/commonjs/core/mixins/OxyServices.location.js.map +1 -0
  24. package/lib/commonjs/core/mixins/OxyServices.payment.js +66 -0
  25. package/lib/commonjs/core/mixins/OxyServices.payment.js.map +1 -0
  26. package/lib/commonjs/core/mixins/OxyServices.privacy.js +174 -0
  27. package/lib/commonjs/core/mixins/OxyServices.privacy.js.map +1 -0
  28. package/lib/commonjs/core/mixins/OxyServices.totp.js +53 -0
  29. package/lib/commonjs/core/mixins/OxyServices.totp.js.map +1 -0
  30. package/lib/commonjs/core/mixins/OxyServices.user.js +388 -0
  31. package/lib/commonjs/core/mixins/OxyServices.user.js.map +1 -0
  32. package/lib/commonjs/core/mixins/OxyServices.utility.js +161 -0
  33. package/lib/commonjs/core/mixins/OxyServices.utility.js.map +1 -0
  34. package/lib/commonjs/core/mixins/index.js +39 -0
  35. package/lib/commonjs/core/mixins/index.js.map +1 -0
  36. package/lib/commonjs/core/mixins/mixinHelpers.js +62 -0
  37. package/lib/commonjs/core/mixins/mixinHelpers.js.map +1 -0
  38. package/lib/commonjs/ui/context/OxyContext.js +27 -2
  39. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  40. package/lib/module/core/OxyServices.base.js +265 -0
  41. package/lib/module/core/OxyServices.base.js.map +1 -0
  42. package/lib/module/core/OxyServices.errors.js +20 -0
  43. package/lib/module/core/OxyServices.errors.js.map +1 -0
  44. package/lib/module/core/OxyServices.js +43 -2164
  45. package/lib/module/core/OxyServices.js.map +1 -1
  46. package/lib/module/core/mixins/OxyServices.analytics.js +56 -0
  47. package/lib/module/core/mixins/OxyServices.analytics.js.map +1 -0
  48. package/lib/module/core/mixins/OxyServices.assets.js +420 -0
  49. package/lib/module/core/mixins/OxyServices.assets.js.map +1 -0
  50. package/lib/module/core/mixins/OxyServices.auth.js +299 -0
  51. package/lib/module/core/mixins/OxyServices.auth.js.map +1 -0
  52. package/lib/module/core/mixins/OxyServices.developer.js +111 -0
  53. package/lib/module/core/mixins/OxyServices.developer.js.map +1 -0
  54. package/lib/module/core/mixins/OxyServices.devices.js +115 -0
  55. package/lib/module/core/mixins/OxyServices.devices.js.map +1 -0
  56. package/lib/module/core/mixins/OxyServices.karma.js +113 -0
  57. package/lib/module/core/mixins/OxyServices.karma.js.map +1 -0
  58. package/lib/module/core/mixins/OxyServices.language.js +120 -0
  59. package/lib/module/core/mixins/OxyServices.language.js.map +1 -0
  60. package/lib/module/core/mixins/OxyServices.location.js +51 -0
  61. package/lib/module/core/mixins/OxyServices.location.js.map +1 -0
  62. package/lib/module/core/mixins/OxyServices.payment.js +62 -0
  63. package/lib/module/core/mixins/OxyServices.payment.js.map +1 -0
  64. package/lib/module/core/mixins/OxyServices.privacy.js +170 -0
  65. package/lib/module/core/mixins/OxyServices.privacy.js.map +1 -0
  66. package/lib/module/core/mixins/OxyServices.totp.js +49 -0
  67. package/lib/module/core/mixins/OxyServices.totp.js.map +1 -0
  68. package/lib/module/core/mixins/OxyServices.user.js +384 -0
  69. package/lib/module/core/mixins/OxyServices.user.js.map +1 -0
  70. package/lib/module/core/mixins/OxyServices.utility.js +156 -0
  71. package/lib/module/core/mixins/OxyServices.utility.js.map +1 -0
  72. package/lib/module/core/mixins/index.js +36 -0
  73. package/lib/module/core/mixins/index.js.map +1 -0
  74. package/lib/module/core/mixins/mixinHelpers.js +56 -0
  75. package/lib/module/core/mixins/mixinHelpers.js.map +1 -0
  76. package/lib/module/ui/context/OxyContext.js +27 -2
  77. package/lib/module/ui/context/OxyContext.js.map +1 -1
  78. package/lib/typescript/core/OxyServices.base.d.ts +123 -0
  79. package/lib/typescript/core/OxyServices.base.d.ts.map +1 -0
  80. package/lib/typescript/core/OxyServices.d.ts +970 -746
  81. package/lib/typescript/core/OxyServices.d.ts.map +1 -1
  82. package/lib/typescript/core/OxyServices.errors.d.ts +12 -0
  83. package/lib/typescript/core/OxyServices.errors.d.ts.map +1 -0
  84. package/lib/typescript/core/mixins/OxyServices.analytics.d.ts +70 -0
  85. package/lib/typescript/core/mixins/OxyServices.analytics.d.ts.map +1 -0
  86. package/lib/typescript/core/mixins/OxyServices.assets.d.ts +166 -0
  87. package/lib/typescript/core/mixins/OxyServices.assets.d.ts.map +1 -0
  88. package/lib/typescript/core/mixins/OxyServices.auth.d.ts +168 -0
  89. package/lib/typescript/core/mixins/OxyServices.auth.d.ts.map +1 -0
  90. package/lib/typescript/core/mixins/OxyServices.developer.d.ts +103 -0
  91. package/lib/typescript/core/mixins/OxyServices.developer.d.ts.map +1 -0
  92. package/lib/typescript/core/mixins/OxyServices.devices.d.ts +93 -0
  93. package/lib/typescript/core/mixins/OxyServices.devices.d.ts.map +1 -0
  94. package/lib/typescript/core/mixins/OxyServices.karma.d.ts +89 -0
  95. package/lib/typescript/core/mixins/OxyServices.karma.d.ts.map +1 -0
  96. package/lib/typescript/core/mixins/OxyServices.language.d.ts +85 -0
  97. package/lib/typescript/core/mixins/OxyServices.language.d.ts.map +1 -0
  98. package/lib/typescript/core/mixins/OxyServices.location.d.ts +68 -0
  99. package/lib/typescript/core/mixins/OxyServices.location.d.ts.map +1 -0
  100. package/lib/typescript/core/mixins/OxyServices.payment.d.ts +74 -0
  101. package/lib/typescript/core/mixins/OxyServices.payment.d.ts.map +1 -0
  102. package/lib/typescript/core/mixins/OxyServices.privacy.d.ts +126 -0
  103. package/lib/typescript/core/mixins/OxyServices.privacy.d.ts.map +1 -0
  104. package/lib/typescript/core/mixins/OxyServices.totp.d.ts +69 -0
  105. package/lib/typescript/core/mixins/OxyServices.totp.d.ts.map +1 -0
  106. package/lib/typescript/core/mixins/OxyServices.user.d.ts +189 -0
  107. package/lib/typescript/core/mixins/OxyServices.user.d.ts.map +1 -0
  108. package/lib/typescript/core/mixins/OxyServices.utility.d.ts +97 -0
  109. package/lib/typescript/core/mixins/OxyServices.utility.d.ts.map +1 -0
  110. package/lib/typescript/core/mixins/index.d.ts +899 -0
  111. package/lib/typescript/core/mixins/index.d.ts.map +1 -0
  112. package/lib/typescript/core/mixins/mixinHelpers.d.ts +32 -0
  113. package/lib/typescript/core/mixins/mixinHelpers.d.ts.map +1 -0
  114. package/lib/typescript/models/interfaces.d.ts +10 -0
  115. package/lib/typescript/models/interfaces.d.ts.map +1 -1
  116. package/lib/typescript/ui/context/OxyContext.d.ts +2 -0
  117. package/lib/typescript/ui/context/OxyContext.d.ts.map +1 -1
  118. package/package.json +1 -1
  119. package/src/core/OxyServices.base.ts +311 -0
  120. package/src/core/OxyServices.errors.ts +26 -0
  121. package/src/core/OxyServices.ts +43 -2199
  122. package/src/core/mixins/OxyServices.analytics.ts +53 -0
  123. package/src/core/mixins/OxyServices.assets.ts +410 -0
  124. package/src/core/mixins/OxyServices.auth.ts +275 -0
  125. package/src/core/mixins/OxyServices.developer.ts +114 -0
  126. package/src/core/mixins/OxyServices.devices.ts +103 -0
  127. package/src/core/mixins/OxyServices.karma.ts +111 -0
  128. package/src/core/mixins/OxyServices.language.ts +127 -0
  129. package/src/core/mixins/OxyServices.location.ts +46 -0
  130. package/src/core/mixins/OxyServices.payment.ts +59 -0
  131. package/src/core/mixins/OxyServices.privacy.ts +182 -0
  132. package/src/core/mixins/OxyServices.totp.ts +36 -0
  133. package/src/core/mixins/OxyServices.user.ts +384 -0
  134. package/src/core/mixins/OxyServices.utility.ts +187 -0
  135. package/src/core/mixins/index.ts +58 -0
  136. package/src/core/mixins/mixinHelpers.ts +69 -0
  137. package/src/models/interfaces.ts +12 -0
  138. package/src/ui/context/OxyContext.tsx +36 -0
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Analytics Methods Mixin
3
+ *
4
+ * Provides methods for analytics tracking and data retrieval
5
+ */
6
+ import type { OxyServicesBase } from '../OxyServices.base';
7
+ import { CACHE_TIMES } from './mixinHelpers';
8
+
9
+ export function OxyServicesAnalyticsMixin<T extends typeof OxyServicesBase>(Base: T) {
10
+ return class extends Base {
11
+ constructor(...args: any[]) {
12
+ super(...(args as [any]));
13
+ }
14
+
15
+ /**
16
+ * Track an analytics event
17
+ * @param eventName - Name of the event to track
18
+ * @param properties - Optional event properties
19
+ */
20
+ async trackEvent(eventName: string, properties?: Record<string, any>): Promise<void> {
21
+ try {
22
+ await this.makeRequest('POST', '/api/analytics/events', {
23
+ event: eventName,
24
+ properties
25
+ }, { cache: false, retry: false }); // Don't retry analytics events
26
+ } catch (error) {
27
+ throw this.handleError(error);
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Get analytics data for a date range
33
+ * @param startDate - Optional start date (ISO string)
34
+ * @param endDate - Optional end date (ISO string)
35
+ * @returns Analytics data
36
+ */
37
+ async getAnalytics(startDate?: string, endDate?: string): Promise<any> {
38
+ try {
39
+ const params: any = {};
40
+ if (startDate) params.startDate = startDate;
41
+ if (endDate) params.endDate = endDate;
42
+
43
+ return await this.makeRequest('GET', '/api/analytics', params, {
44
+ cache: true,
45
+ cacheTTL: CACHE_TIMES.LONG,
46
+ });
47
+ } catch (error) {
48
+ throw this.handleError(error);
49
+ }
50
+ }
51
+ };
52
+ }
53
+
@@ -0,0 +1,410 @@
1
+ /**
2
+ * Asset & File Methods Mixin
3
+ */
4
+ import type { AssetInitResponse, AssetUrlResponse, AssetVariant } from '../../models/interfaces';
5
+ import type { OxyServicesBase } from '../OxyServices.base';
6
+
7
+ export function OxyServicesAssetsMixin<T extends typeof OxyServicesBase>(Base: T) {
8
+ return class extends Base {
9
+ constructor(...args: any[]) {
10
+ super(...(args as [any]));
11
+ }
12
+ // ============================================================================
13
+ // FILE METHODS (LEGACY - Using Asset Service)
14
+ // ============================================================================
15
+
16
+ /**
17
+ * Delete file
18
+ */
19
+ async deleteFile(fileId: string): Promise<any> {
20
+ try {
21
+ // Central Asset Service delete with force=true behavior controlled by caller via assetDelete
22
+ return await this.makeRequest('DELETE', `/api/assets/${encodeURIComponent(fileId)}`, undefined, { cache: false });
23
+ } catch (error) {
24
+ throw this.handleError(error);
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Get file download URL (synchronous - uses download endpoint that redirects to signed URLs)
30
+ * For better performance, use getFileDownloadUrlAsync when possible
31
+ */
32
+ getFileDownloadUrl(fileId: string, variant?: string, expiresIn?: number): string {
33
+ const base = this.getBaseURL();
34
+ const params = new URLSearchParams();
35
+ if (variant) params.set('variant', variant);
36
+ if (expiresIn) params.set('expiresIn', String(expiresIn));
37
+ const token = this.getClient().getAccessToken();
38
+ if (token) params.set('token', token);
39
+
40
+ // Use download endpoint which redirects to signed URLs (better for CDN caching)
41
+ // Fallback to stream if download doesn't work
42
+ const qs = params.toString();
43
+ return `${base}/api/assets/${encodeURIComponent(fileId)}/download${qs ? `?${qs}` : ''}`;
44
+ }
45
+
46
+ /**
47
+ * Get file download URL asynchronously (returns signed URL directly from CDN)
48
+ * This is more efficient than the synchronous version as it avoids redirects
49
+ * Use this when you can handle async operations (e.g., in useEffect, useMemo with async)
50
+ */
51
+ async getFileDownloadUrlAsync(fileId: string, variant?: string, expiresIn?: number): Promise<string> {
52
+ try {
53
+ const params: any = {};
54
+ if (variant) params.variant = variant;
55
+ if (expiresIn) params.expiresIn = expiresIn;
56
+
57
+ const urlRes = await this.makeRequest<{ url: string }>('GET', `/api/assets/${encodeURIComponent(fileId)}/url`, params, {
58
+ cache: true,
59
+ cacheTTL: Math.min((expiresIn || 3600) * 1000, 10 * 60 * 1000), // Cache for up to 10 minutes or expiresIn, whichever is shorter
60
+ });
61
+
62
+ return urlRes?.url || this.getFileDownloadUrl(fileId, variant, expiresIn);
63
+ } catch (error) {
64
+ // Fallback to synchronous method on error
65
+ return this.getFileDownloadUrl(fileId, variant, expiresIn);
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Get file stream URL (direct Oxy Cloud/CDN URL, no token)
71
+ */
72
+ getFileStreamUrl(fileId: string): string {
73
+ return `${this.getCloudURL()}/files/${fileId}/stream`;
74
+ }
75
+
76
+ /**
77
+ * List user files
78
+ */
79
+ async listUserFiles(limit?: number, offset?: number): Promise<{ files: any[]; total: number; hasMore: boolean }> {
80
+ try {
81
+ const paramsObj: any = {};
82
+ if (limit) paramsObj.limit = limit;
83
+ if (offset) paramsObj.offset = offset;
84
+ return await this.makeRequest('GET', '/api/assets', paramsObj, {
85
+ cache: false, // Don't cache file lists - always get fresh data
86
+ });
87
+ } catch (error) {
88
+ throw this.handleError(error);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Get file content as text
94
+ */
95
+ async getFileContentAsText(fileId: string, variant?: string): Promise<string> {
96
+ try {
97
+ const params: any = variant ? { variant } : undefined;
98
+ const urlRes = await this.makeRequest<{ url: string }>('GET', `/api/assets/${encodeURIComponent(fileId)}/url`, params, {
99
+ cache: true,
100
+ cacheTTL: 10 * 60 * 1000, // 10 minutes cache for URLs
101
+ });
102
+ const downloadUrl = urlRes?.url;
103
+ const response = await fetch(downloadUrl);
104
+ return await response.text();
105
+ } catch (error) {
106
+ throw this.handleError(error);
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Get file content as blob
112
+ */
113
+ async getFileContentAsBlob(fileId: string, variant?: string): Promise<Blob> {
114
+ try {
115
+ const params: any = variant ? { variant } : undefined;
116
+ const urlRes = await this.makeRequest<{ url: string }>('GET', `/api/assets/${encodeURIComponent(fileId)}/url`, params, {
117
+ cache: true,
118
+ cacheTTL: 10 * 60 * 1000, // 10 minutes cache for URLs
119
+ });
120
+ const downloadUrl = urlRes?.url;
121
+ const response = await fetch(downloadUrl);
122
+ return await response.blob();
123
+ } catch (error) {
124
+ throw this.handleError(error);
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Upload raw file data
130
+ */
131
+ async uploadRawFile(file: File | Blob, visibility?: 'private' | 'public' | 'unlisted', metadata?: Record<string, any>): Promise<any> {
132
+ // Switch to Central Asset Service upload flow
133
+ return this.assetUpload(file as File, visibility, metadata);
134
+ }
135
+
136
+ // ============================================================================
137
+ // CENTRAL ASSET SERVICE METHODS
138
+ // ============================================================================
139
+
140
+ /**
141
+ * Calculate SHA256 hash of file content
142
+ */
143
+ async calculateSHA256(file: File | Blob): Promise<string> {
144
+ const buffer = await file.arrayBuffer();
145
+ const hashBuffer = await crypto.subtle.digest('SHA-256', buffer);
146
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
147
+ return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
148
+ }
149
+
150
+ /**
151
+ * Initialize asset upload - returns pre-signed URL and file ID
152
+ */
153
+ async assetInit(sha256: string, size: number, mime: string): Promise<AssetInitResponse> {
154
+ try {
155
+ return await this.makeRequest<AssetInitResponse>('POST', '/api/assets/init', {
156
+ sha256,
157
+ size,
158
+ mime
159
+ }, { cache: false });
160
+ } catch (error) {
161
+ throw this.handleError(error);
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Complete asset upload - commit metadata and trigger variant generation
167
+ */
168
+ async assetComplete(fileId: string, originalName: string, size: number, mime: string, visibility?: 'private' | 'public' | 'unlisted', metadata?: Record<string, any>): Promise<any> {
169
+ try {
170
+ return await this.makeRequest('POST', '/api/assets/complete', {
171
+ fileId,
172
+ originalName,
173
+ size,
174
+ mime,
175
+ visibility,
176
+ metadata
177
+ }, { cache: false });
178
+ } catch (error) {
179
+ throw this.handleError(error);
180
+ }
181
+ }
182
+
183
+ /**
184
+ * Upload file using Central Asset Service
185
+ */
186
+ async assetUpload(file: File, visibility?: 'private' | 'public' | 'unlisted', metadata?: Record<string, any>, onProgress?: (progress: number) => void): Promise<any> {
187
+ try {
188
+ // Calculate SHA256
189
+ const sha256 = await this.calculateSHA256(file);
190
+
191
+ // Initialize upload
192
+ const initResponse = await this.assetInit(sha256, file.size, file.type);
193
+
194
+ // Try presigned URL first
195
+ try {
196
+ await this.uploadToPresignedUrl(initResponse.uploadUrl, file, onProgress);
197
+ } catch (e) {
198
+ // Fallback: direct upload via API to avoid CORS issues
199
+ const fd = new FormData();
200
+ fd.append('file', file);
201
+ // Use httpClient directly for FormData uploads (bypasses RequestManager for special handling)
202
+ await this.getClient().request({
203
+ method: 'POST',
204
+ url: `/api/assets/${encodeURIComponent(initResponse.fileId)}/upload-direct`,
205
+ data: fd,
206
+ });
207
+ }
208
+
209
+ // Complete upload
210
+ return await this.assetComplete(
211
+ initResponse.fileId,
212
+ file.name,
213
+ file.size,
214
+ file.type,
215
+ visibility,
216
+ metadata
217
+ );
218
+ } catch (error) {
219
+ throw this.handleError(error);
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Upload file to pre-signed URL
225
+ */
226
+ public async uploadToPresignedUrl(url: string, file: File, onProgress?: (progress: number) => void): Promise<void> {
227
+ return new Promise((resolve, reject) => {
228
+ const xhr = new XMLHttpRequest();
229
+
230
+ xhr.upload.addEventListener('progress', (event) => {
231
+ if (event.lengthComputable && onProgress) {
232
+ const progress = (event.loaded / event.total) * 100;
233
+ onProgress(progress);
234
+ }
235
+ });
236
+
237
+ xhr.addEventListener('load', () => {
238
+ if (xhr.status >= 200 && xhr.status < 300) {
239
+ resolve();
240
+ } else {
241
+ reject(new Error(`Upload failed with status ${xhr.status}`));
242
+ }
243
+ });
244
+
245
+ xhr.addEventListener('error', () => {
246
+ reject(new Error('Upload failed'));
247
+ });
248
+
249
+ xhr.open('PUT', url);
250
+ xhr.setRequestHeader('Content-Type', file.type);
251
+ xhr.send(file);
252
+ });
253
+ }
254
+
255
+ /**
256
+ * Link asset to an entity
257
+ */
258
+ async assetLink(fileId: string, app: string, entityType: string, entityId: string, visibility?: 'private' | 'public' | 'unlisted', webhookUrl?: string): Promise<any> {
259
+ try {
260
+ const body: any = { app, entityType, entityId };
261
+ if (visibility) body.visibility = visibility;
262
+ if (webhookUrl) body.webhookUrl = webhookUrl;
263
+ return await this.makeRequest('POST', `/api/assets/${fileId}/links`, body, { cache: false });
264
+ } catch (error) {
265
+ throw this.handleError(error);
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Unlink asset from an entity
271
+ */
272
+ async assetUnlink(fileId: string, app: string, entityType: string, entityId: string): Promise<any> {
273
+ try {
274
+ return await this.makeRequest('DELETE', `/api/assets/${fileId}/links`, {
275
+ app,
276
+ entityType,
277
+ entityId
278
+ }, { cache: false });
279
+ } catch (error) {
280
+ throw this.handleError(error);
281
+ }
282
+ }
283
+
284
+ /**
285
+ * Get asset metadata
286
+ */
287
+ async assetGet(fileId: string): Promise<any> {
288
+ try {
289
+ return await this.makeRequest('GET', `/api/assets/${fileId}`, undefined, {
290
+ cache: true,
291
+ cacheTTL: 5 * 60 * 1000, // 5 minutes cache
292
+ });
293
+ } catch (error) {
294
+ throw this.handleError(error);
295
+ }
296
+ }
297
+
298
+ /**
299
+ * Get asset URL (CDN or signed URL)
300
+ */
301
+ async assetGetUrl(fileId: string, variant?: string, expiresIn?: number): Promise<AssetUrlResponse> {
302
+ try {
303
+ const params: any = {};
304
+ if (variant) params.variant = variant;
305
+ if (expiresIn) params.expiresIn = expiresIn;
306
+
307
+ return await this.makeRequest<AssetUrlResponse>('GET', `/api/assets/${fileId}/url`, params, {
308
+ cache: true,
309
+ cacheTTL: 10 * 60 * 1000, // 10 minutes cache for URLs
310
+ });
311
+ } catch (error) {
312
+ throw this.handleError(error);
313
+ }
314
+ }
315
+
316
+ /**
317
+ * Restore asset from trash
318
+ */
319
+ async assetRestore(fileId: string): Promise<any> {
320
+ try {
321
+ return await this.makeRequest('POST', `/api/assets/${fileId}/restore`, undefined, { cache: false });
322
+ } catch (error) {
323
+ throw this.handleError(error);
324
+ }
325
+ }
326
+
327
+ /**
328
+ * Delete asset with optional force
329
+ */
330
+ async assetDelete(fileId: string, force: boolean = false): Promise<any> {
331
+ try {
332
+ const params: any = force ? { force: 'true' } : undefined;
333
+ return await this.makeRequest('DELETE', `/api/assets/${fileId}`, params, { cache: false });
334
+ } catch (error) {
335
+ throw this.handleError(error);
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Get list of available variants for an asset
341
+ */
342
+ async assetGetVariants(fileId: string): Promise<AssetVariant[]> {
343
+ try {
344
+ const assetData = await this.assetGet(fileId);
345
+ return assetData.file?.variants || [];
346
+ } catch (error) {
347
+ throw this.handleError(error);
348
+ }
349
+ }
350
+
351
+ /**
352
+ * Update asset visibility
353
+ * @param fileId - The file ID
354
+ * @param visibility - New visibility level ('private', 'public', or 'unlisted')
355
+ * @returns Updated asset information
356
+ */
357
+ async assetUpdateVisibility(fileId: string, visibility: 'private' | 'public' | 'unlisted'): Promise<any> {
358
+ try {
359
+ return await this.makeRequest('PATCH', `/api/assets/${fileId}/visibility`, {
360
+ visibility
361
+ }, { cache: false });
362
+ } catch (error) {
363
+ throw this.handleError(error);
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Helper: Upload and link avatar with automatic public visibility
369
+ * @param file - The avatar file
370
+ * @param userId - User ID to link to
371
+ * @param app - App name (defaults to 'profiles')
372
+ * @returns The uploaded and linked asset
373
+ */
374
+ async uploadAvatar(file: File, userId: string, app: string = 'profiles'): Promise<any> {
375
+ try {
376
+ // Upload as public
377
+ const asset = await this.assetUpload(file, 'public');
378
+
379
+ // Link to user profile as avatar
380
+ await this.assetLink(asset.file.id, app, 'avatar', userId, 'public');
381
+
382
+ return asset;
383
+ } catch (error) {
384
+ throw this.handleError(error);
385
+ }
386
+ }
387
+
388
+ /**
389
+ * Helper: Upload and link profile banner with automatic public visibility
390
+ * @param file - The banner file
391
+ * @param userId - User ID to link to
392
+ * @param app - App name (defaults to 'profiles')
393
+ * @returns The uploaded and linked asset
394
+ */
395
+ async uploadProfileBanner(file: File, userId: string, app: string = 'profiles'): Promise<any> {
396
+ try {
397
+ // Upload as public
398
+ const asset = await this.assetUpload(file, 'public');
399
+
400
+ // Link to user profile as banner
401
+ await this.assetLink(asset.file.id, app, 'profile-banner', userId, 'public');
402
+
403
+ return asset;
404
+ } catch (error) {
405
+ throw this.handleError(error);
406
+ }
407
+ }
408
+ };
409
+ }
410
+