@rovela-ai/sdk 0.1.26 → 0.1.27

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 (72) hide show
  1. package/dist/admin/components/CategoryForm.d.ts.map +1 -1
  2. package/dist/admin/components/CategoryForm.js +2 -2
  3. package/dist/admin/components/CategoryForm.js.map +1 -1
  4. package/dist/admin/components/ProductForm.d.ts.map +1 -1
  5. package/dist/admin/components/ProductForm.js +3 -35
  6. package/dist/admin/components/ProductForm.js.map +1 -1
  7. package/dist/core/db/queries.d.ts +7 -7
  8. package/dist/media/api/delete.d.ts +44 -0
  9. package/dist/media/api/delete.d.ts.map +1 -0
  10. package/dist/media/api/delete.js +134 -0
  11. package/dist/media/api/delete.js.map +1 -0
  12. package/dist/media/api/index.d.ts +17 -0
  13. package/dist/media/api/index.d.ts.map +1 -0
  14. package/dist/media/api/index.js +17 -0
  15. package/dist/media/api/index.js.map +1 -0
  16. package/dist/media/api/presign.d.ts +39 -0
  17. package/dist/media/api/presign.d.ts.map +1 -0
  18. package/dist/media/api/presign.js +138 -0
  19. package/dist/media/api/presign.js.map +1 -0
  20. package/dist/media/components/DropZone.d.ts +18 -0
  21. package/dist/media/components/DropZone.d.ts.map +1 -0
  22. package/dist/media/components/DropZone.js +112 -0
  23. package/dist/media/components/DropZone.js.map +1 -0
  24. package/dist/media/components/ImageGalleryUpload.d.ts +18 -0
  25. package/dist/media/components/ImageGalleryUpload.d.ts.map +1 -0
  26. package/dist/media/components/ImageGalleryUpload.js +156 -0
  27. package/dist/media/components/ImageGalleryUpload.js.map +1 -0
  28. package/dist/media/components/ImageUpload.d.ts +17 -0
  29. package/dist/media/components/ImageUpload.d.ts.map +1 -0
  30. package/dist/media/components/ImageUpload.js +95 -0
  31. package/dist/media/components/ImageUpload.js.map +1 -0
  32. package/dist/media/components/index.d.ts +10 -0
  33. package/dist/media/components/index.d.ts.map +1 -0
  34. package/dist/media/components/index.js +9 -0
  35. package/dist/media/components/index.js.map +1 -0
  36. package/dist/media/config.d.ts +57 -0
  37. package/dist/media/config.d.ts.map +1 -0
  38. package/dist/media/config.js +142 -0
  39. package/dist/media/config.js.map +1 -0
  40. package/dist/media/hooks/index.d.ts +8 -0
  41. package/dist/media/hooks/index.d.ts.map +1 -0
  42. package/dist/media/hooks/index.js +7 -0
  43. package/dist/media/hooks/index.js.map +1 -0
  44. package/dist/media/hooks/useUpload.d.ts +32 -0
  45. package/dist/media/hooks/useUpload.d.ts.map +1 -0
  46. package/dist/media/hooks/useUpload.js +258 -0
  47. package/dist/media/hooks/useUpload.js.map +1 -0
  48. package/dist/media/index.d.ts +57 -0
  49. package/dist/media/index.d.ts.map +1 -0
  50. package/dist/media/index.js +68 -0
  51. package/dist/media/index.js.map +1 -0
  52. package/dist/media/server/delete.d.ts +59 -0
  53. package/dist/media/server/delete.d.ts.map +1 -0
  54. package/dist/media/server/delete.js +176 -0
  55. package/dist/media/server/delete.js.map +1 -0
  56. package/dist/media/server/index.d.ts +10 -0
  57. package/dist/media/server/index.d.ts.map +1 -0
  58. package/dist/media/server/index.js +13 -0
  59. package/dist/media/server/index.js.map +1 -0
  60. package/dist/media/server/presign.d.ts +57 -0
  61. package/dist/media/server/presign.d.ts.map +1 -0
  62. package/dist/media/server/presign.js +112 -0
  63. package/dist/media/server/presign.js.map +1 -0
  64. package/dist/media/server/r2-client.d.ts +30 -0
  65. package/dist/media/server/r2-client.d.ts.map +1 -0
  66. package/dist/media/server/r2-client.js +76 -0
  67. package/dist/media/server/r2-client.js.map +1 -0
  68. package/dist/media/types.d.ts +271 -0
  69. package/dist/media/types.d.ts.map +1 -0
  70. package/dist/media/types.js +52 -0
  71. package/dist/media/types.js.map +1 -0
  72. package/package.json +15 -1
@@ -0,0 +1,13 @@
1
+ /**
2
+ * @rovela-ai/sdk/media/server
3
+ *
4
+ * Server-side media utilities.
5
+ * Only import this in server components and API routes.
6
+ */
7
+ // R2 Client
8
+ export { getR2Client, clearR2ClientCache, getBucketName, getPublicUrlBase, getStoreId } from './r2-client';
9
+ // Presigned URLs
10
+ export { generatePresignedUrl, generatePresignedUrls, isR2Url, validateStoreUrl, } from './presign';
11
+ // File Deletion
12
+ export { deleteFile, deleteFileByKey, deleteFiles, safeDeleteFile } from './delete';
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/media/server/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY;AACZ,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,aAAa,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE1G,iBAAiB;AACjB,OAAO,EACL,oBAAoB,EACpB,qBAAqB,EACrB,OAAO,EACP,gBAAgB,GAEjB,MAAM,WAAW,CAAA;AAElB,gBAAgB;AAChB,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,57 @@
1
+ /**
2
+ * @rovela-ai/sdk/media/server/presign
3
+ *
4
+ * Presigned URL generation for direct browser uploads to R2.
5
+ * Server-side only - do not import in client components.
6
+ */
7
+ import type { PresignedUrlRequest, PresignedUrlResponse, MediaStorageConfig } from '../types';
8
+ /**
9
+ * Options for generating a presigned URL
10
+ */
11
+ export interface GeneratePresignedUrlOptions {
12
+ /** URL expiration time in seconds (default: 300 = 5 minutes) */
13
+ expiresIn?: number;
14
+ /** Optional storage config override */
15
+ config?: MediaStorageConfig;
16
+ }
17
+ /**
18
+ * Generate a presigned URL for uploading a file to R2.
19
+ *
20
+ * The presigned URL allows the browser to upload directly to R2
21
+ * without the file passing through your server.
22
+ *
23
+ * @param request - Upload request details
24
+ * @param options - Optional configuration
25
+ * @returns Presigned URL response with upload URL and final public URL
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const result = await generatePresignedUrl({
30
+ * filename: 'product-image.jpg',
31
+ * contentType: 'image/jpeg',
32
+ * folder: 'products',
33
+ * entityId: 'prod_abc123',
34
+ * });
35
+ *
36
+ * // Client uploads directly to result.uploadUrl
37
+ * // After upload, image is accessible at result.publicUrl
38
+ * ```
39
+ */
40
+ export declare function generatePresignedUrl(request: PresignedUrlRequest, options?: GeneratePresignedUrlOptions): Promise<PresignedUrlResponse>;
41
+ /**
42
+ * Generate presigned URLs for multiple files at once.
43
+ *
44
+ * @param requests - Array of upload request details
45
+ * @param options - Optional configuration
46
+ * @returns Array of presigned URL responses
47
+ */
48
+ export declare function generatePresignedUrls(requests: PresignedUrlRequest[], options?: GeneratePresignedUrlOptions): Promise<PresignedUrlResponse[]>;
49
+ /**
50
+ * Check if a URL is from this store's R2 storage
51
+ */
52
+ export declare function isR2Url(url: string, config?: MediaStorageConfig): boolean;
53
+ /**
54
+ * Validate that a URL belongs to this store's storage prefix
55
+ */
56
+ export declare function validateStoreUrl(url: string, config?: MediaStorageConfig): boolean;
57
+ //# sourceMappingURL=presign.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presign.d.ts","sourceRoot":"","sources":["../../../src/media/server/presign.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,UAAU,CAAA;AAcjB;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,uCAAuC;IACvC,MAAM,CAAC,EAAE,kBAAkB,CAAA;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,mBAAmB,EAC5B,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,oBAAoB,CAAC,CAwC/B;AAED;;;;;;GAMG;AACH,wBAAsB,qBAAqB,CACzC,QAAQ,EAAE,mBAAmB,EAAE,EAC/B,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAEjC;AAMD;;GAEG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAGzE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAiBlF"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * @rovela-ai/sdk/media/server/presign
3
+ *
4
+ * Presigned URL generation for direct browser uploads to R2.
5
+ * Server-side only - do not import in client components.
6
+ */
7
+ import { PutObjectCommand } from '@aws-sdk/client-s3';
8
+ import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
9
+ import { generateUniqueFilename } from '../types';
10
+ import { buildStorageKey, buildPublicUrl, requireMediaConfig } from '../config';
11
+ import { getR2Client, getBucketName, getPublicUrlBase, getStoreId } from './r2-client';
12
+ // =============================================================================
13
+ // Presigned URL Generation
14
+ // =============================================================================
15
+ /**
16
+ * Default presigned URL expiration time in seconds (5 minutes)
17
+ */
18
+ const DEFAULT_EXPIRES_IN = 5 * 60;
19
+ /**
20
+ * Generate a presigned URL for uploading a file to R2.
21
+ *
22
+ * The presigned URL allows the browser to upload directly to R2
23
+ * without the file passing through your server.
24
+ *
25
+ * @param request - Upload request details
26
+ * @param options - Optional configuration
27
+ * @returns Presigned URL response with upload URL and final public URL
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const result = await generatePresignedUrl({
32
+ * filename: 'product-image.jpg',
33
+ * contentType: 'image/jpeg',
34
+ * folder: 'products',
35
+ * entityId: 'prod_abc123',
36
+ * });
37
+ *
38
+ * // Client uploads directly to result.uploadUrl
39
+ * // After upload, image is accessible at result.publicUrl
40
+ * ```
41
+ */
42
+ export async function generatePresignedUrl(request, options = {}) {
43
+ const { expiresIn = DEFAULT_EXPIRES_IN, config } = options;
44
+ // Get configuration
45
+ const resolvedConfig = config || requireMediaConfig();
46
+ const client = getR2Client(resolvedConfig);
47
+ const bucketName = getBucketName(resolvedConfig);
48
+ const publicUrlBase = getPublicUrlBase(resolvedConfig);
49
+ const storeId = getStoreId(resolvedConfig);
50
+ // Generate unique filename to prevent overwrites
51
+ const uniqueFilename = generateUniqueFilename(request.filename);
52
+ // Build the storage key (path)
53
+ const key = buildStorageKey(storeId, request.folder, uniqueFilename, request.entityId);
54
+ // Create the PutObject command
55
+ const command = new PutObjectCommand({
56
+ Bucket: bucketName,
57
+ Key: key,
58
+ ContentType: request.contentType,
59
+ });
60
+ // Generate presigned URL
61
+ const uploadUrl = await getSignedUrl(client, command, {
62
+ expiresIn,
63
+ });
64
+ // Calculate expiration time
65
+ const expiresAt = new Date(Date.now() + expiresIn * 1000);
66
+ // Build public URL
67
+ const publicUrl = buildPublicUrl(publicUrlBase, key);
68
+ return {
69
+ uploadUrl,
70
+ publicUrl,
71
+ key,
72
+ expiresAt,
73
+ };
74
+ }
75
+ /**
76
+ * Generate presigned URLs for multiple files at once.
77
+ *
78
+ * @param requests - Array of upload request details
79
+ * @param options - Optional configuration
80
+ * @returns Array of presigned URL responses
81
+ */
82
+ export async function generatePresignedUrls(requests, options = {}) {
83
+ return Promise.all(requests.map((request) => generatePresignedUrl(request, options)));
84
+ }
85
+ // =============================================================================
86
+ // URL Validation
87
+ // =============================================================================
88
+ /**
89
+ * Check if a URL is from this store's R2 storage
90
+ */
91
+ export function isR2Url(url, config) {
92
+ const resolvedConfig = config || requireMediaConfig();
93
+ return url.startsWith(resolvedConfig.publicUrl);
94
+ }
95
+ /**
96
+ * Validate that a URL belongs to this store's storage prefix
97
+ */
98
+ export function validateStoreUrl(url, config) {
99
+ const resolvedConfig = config || requireMediaConfig();
100
+ if (!isR2Url(url, resolvedConfig)) {
101
+ return false;
102
+ }
103
+ // Extract key from URL and check prefix
104
+ const baseUrl = resolvedConfig.publicUrl.endsWith('/')
105
+ ? resolvedConfig.publicUrl.slice(0, -1)
106
+ : resolvedConfig.publicUrl;
107
+ const key = url.slice(baseUrl.length + 1);
108
+ // Key should start with stores/{storeId}/
109
+ const expectedPrefix = `stores/${resolvedConfig.storeId}/`;
110
+ return key.startsWith(expectedPrefix);
111
+ }
112
+ //# sourceMappingURL=presign.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presign.js","sourceRoot":"","sources":["../../../src/media/server/presign.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * @rovela-ai/sdk/media/server/r2-client
3
+ *
4
+ * S3-compatible client for Cloudflare R2 storage.
5
+ * Server-side only - do not import in client components.
6
+ */
7
+ import { S3Client } from '@aws-sdk/client-s3';
8
+ import type { MediaStorageConfig } from '../types';
9
+ /**
10
+ * Get or create the S3 client for R2.
11
+ * Caches the client instance for reuse.
12
+ */
13
+ export declare function getR2Client(config?: MediaStorageConfig): S3Client;
14
+ /**
15
+ * Clear the cached client (useful for testing)
16
+ */
17
+ export declare function clearR2ClientCache(): void;
18
+ /**
19
+ * Get the configured bucket name
20
+ */
21
+ export declare function getBucketName(config?: MediaStorageConfig): string;
22
+ /**
23
+ * Get the public URL base for the bucket
24
+ */
25
+ export declare function getPublicUrlBase(config?: MediaStorageConfig): string;
26
+ /**
27
+ * Get the store ID for path prefixing
28
+ */
29
+ export declare function getStoreId(config?: MediaStorageConfig): string;
30
+ //# sourceMappingURL=r2-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-client.d.ts","sourceRoot":"","sources":["../../../src/media/server/r2-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAoBlD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,QAAQ,CAsBjE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,IAAI,CAGzC;AAMD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAGjE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAGpE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,CAAC,EAAE,kBAAkB,GAAG,MAAM,CAG9D"}
@@ -0,0 +1,76 @@
1
+ /**
2
+ * @rovela-ai/sdk/media/server/r2-client
3
+ *
4
+ * S3-compatible client for Cloudflare R2 storage.
5
+ * Server-side only - do not import in client components.
6
+ */
7
+ import { S3Client } from '@aws-sdk/client-s3';
8
+ import { requireMediaConfig } from '../config';
9
+ // =============================================================================
10
+ // Client Instance Management
11
+ // =============================================================================
12
+ /** Cached S3 client instance */
13
+ let cachedClient = null;
14
+ /** Cached config used to create the client */
15
+ let cachedConfigHash = null;
16
+ /**
17
+ * Create a hash of the config for cache invalidation
18
+ */
19
+ function hashConfig(config) {
20
+ return `${config.accountId}:${config.bucketName}:${config.accessKeyId.slice(0, 8)}`;
21
+ }
22
+ /**
23
+ * Get or create the S3 client for R2.
24
+ * Caches the client instance for reuse.
25
+ */
26
+ export function getR2Client(config) {
27
+ const resolvedConfig = config || requireMediaConfig();
28
+ const configHash = hashConfig(resolvedConfig);
29
+ // Return cached client if config hasn't changed
30
+ if (cachedClient && cachedConfigHash === configHash) {
31
+ return cachedClient;
32
+ }
33
+ // Create new client
34
+ cachedClient = new S3Client({
35
+ region: 'auto',
36
+ endpoint: `https://${resolvedConfig.accountId}.r2.cloudflarestorage.com`,
37
+ credentials: {
38
+ accessKeyId: resolvedConfig.accessKeyId,
39
+ secretAccessKey: resolvedConfig.secretAccessKey,
40
+ },
41
+ });
42
+ cachedConfigHash = configHash;
43
+ return cachedClient;
44
+ }
45
+ /**
46
+ * Clear the cached client (useful for testing)
47
+ */
48
+ export function clearR2ClientCache() {
49
+ cachedClient = null;
50
+ cachedConfigHash = null;
51
+ }
52
+ // =============================================================================
53
+ // Bucket Operations
54
+ // =============================================================================
55
+ /**
56
+ * Get the configured bucket name
57
+ */
58
+ export function getBucketName(config) {
59
+ const resolvedConfig = config || requireMediaConfig();
60
+ return resolvedConfig.bucketName;
61
+ }
62
+ /**
63
+ * Get the public URL base for the bucket
64
+ */
65
+ export function getPublicUrlBase(config) {
66
+ const resolvedConfig = config || requireMediaConfig();
67
+ return resolvedConfig.publicUrl;
68
+ }
69
+ /**
70
+ * Get the store ID for path prefixing
71
+ */
72
+ export function getStoreId(config) {
73
+ const resolvedConfig = config || requireMediaConfig();
74
+ return resolvedConfig.storeId;
75
+ }
76
+ //# sourceMappingURL=r2-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"r2-client.js","sourceRoot":"","sources":["../../../src/media/server/r2-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAE7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAE9C,gFAAgF;AAChF,6BAA6B;AAC7B,gFAAgF;AAEhF,gCAAgC;AAChC,IAAI,YAAY,GAAoB,IAAI,CAAA;AAExC,8CAA8C;AAC9C,IAAI,gBAAgB,GAAkB,IAAI,CAAA;AAE1C;;GAEG;AACH,SAAS,UAAU,CAAC,MAA0B;IAC5C,OAAO,GAAG,MAAM,CAAC,SAAS,IAAI,MAAM,CAAC,UAAU,IAAI,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAA;AACrF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,MAA2B;IACrD,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IACrD,MAAM,UAAU,GAAG,UAAU,CAAC,cAAc,CAAC,CAAA;IAE7C,gDAAgD;IAChD,IAAI,YAAY,IAAI,gBAAgB,KAAK,UAAU,EAAE,CAAC;QACpD,OAAO,YAAY,CAAA;IACrB,CAAC;IAED,oBAAoB;IACpB,YAAY,GAAG,IAAI,QAAQ,CAAC;QAC1B,MAAM,EAAE,MAAM;QACd,QAAQ,EAAE,WAAW,cAAc,CAAC,SAAS,2BAA2B;QACxE,WAAW,EAAE;YACX,WAAW,EAAE,cAAc,CAAC,WAAW;YACvC,eAAe,EAAE,cAAc,CAAC,eAAe;SAChD;KACF,CAAC,CAAA;IAEF,gBAAgB,GAAG,UAAU,CAAA;IAE7B,OAAO,YAAY,CAAA;AACrB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,YAAY,GAAG,IAAI,CAAA;IACnB,gBAAgB,GAAG,IAAI,CAAA;AACzB,CAAC;AAED,gFAAgF;AAChF,oBAAoB;AACpB,gFAAgF;AAEhF;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,MAA2B;IACvD,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IACrD,OAAO,cAAc,CAAC,UAAU,CAAA;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,MAA2B;IAC1D,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IACrD,OAAO,cAAc,CAAC,SAAS,CAAA;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,UAAU,CAAC,MAA2B;IACpD,MAAM,cAAc,GAAG,MAAM,IAAI,kBAAkB,EAAE,CAAA;IACrD,OAAO,cAAc,CAAC,OAAO,CAAA;AAC/B,CAAC"}
@@ -0,0 +1,271 @@
1
+ /**
2
+ * @rovela-ai/sdk/media/types
3
+ *
4
+ * TypeScript type definitions for media upload functionality.
5
+ * Supports product images, category covers, and variant swatches.
6
+ */
7
+ /**
8
+ * R2/S3 storage configuration
9
+ */
10
+ export interface MediaStorageConfig {
11
+ /** Cloudflare account ID (for R2) */
12
+ accountId: string;
13
+ /** S3-compatible access key */
14
+ accessKeyId: string;
15
+ /** S3-compatible secret key */
16
+ secretAccessKey: string;
17
+ /** Bucket name */
18
+ bucketName: string;
19
+ /** Public URL for accessing uploaded files */
20
+ publicUrl: string;
21
+ /** Store ID for path prefixing */
22
+ storeId: string;
23
+ }
24
+ /**
25
+ * Upload constraints configuration
26
+ */
27
+ export interface UploadConfig {
28
+ /** Maximum file size in bytes (default: 10MB) */
29
+ maxSizeBytes: number;
30
+ /** Allowed MIME types */
31
+ allowedTypes: string[];
32
+ /** Maximum files for batch uploads */
33
+ maxFiles: number;
34
+ }
35
+ /**
36
+ * Default upload configuration
37
+ */
38
+ export declare const DEFAULT_UPLOAD_CONFIG: UploadConfig;
39
+ /**
40
+ * Folder types for organizing uploads
41
+ */
42
+ export type MediaFolder = 'products' | 'categories' | 'variants' | 'general';
43
+ /**
44
+ * Request to generate a presigned upload URL
45
+ */
46
+ export interface PresignedUrlRequest {
47
+ /** Original filename (used for extension) */
48
+ filename: string;
49
+ /** MIME type of the file */
50
+ contentType: string;
51
+ /** Target folder for organization */
52
+ folder: MediaFolder;
53
+ /** Optional entity ID (productId, categoryId, etc.) */
54
+ entityId?: string;
55
+ }
56
+ /**
57
+ * Response containing presigned URL details
58
+ */
59
+ export interface PresignedUrlResponse {
60
+ /** Presigned PUT URL for uploading */
61
+ uploadUrl: string;
62
+ /** Final public URL after upload completes */
63
+ publicUrl: string;
64
+ /** Generated unique key/path in storage */
65
+ key: string;
66
+ /** URL expiration timestamp */
67
+ expiresAt: Date;
68
+ }
69
+ /**
70
+ * Upload progress information
71
+ */
72
+ export interface UploadProgress {
73
+ /** Bytes uploaded so far */
74
+ loaded: number;
75
+ /** Total bytes to upload */
76
+ total: number;
77
+ /** Progress percentage (0-100) */
78
+ percentage: number;
79
+ }
80
+ /**
81
+ * Status of an upload operation
82
+ */
83
+ export type UploadStatus = 'idle' | 'preparing' | 'uploading' | 'success' | 'error';
84
+ /**
85
+ * Result of a completed upload
86
+ */
87
+ export interface UploadResult {
88
+ /** Whether upload succeeded */
89
+ success: boolean;
90
+ /** Final public URL (only if success) */
91
+ url?: string;
92
+ /** Original filename */
93
+ filename: string;
94
+ /** File size in bytes */
95
+ size: number;
96
+ /** MIME type */
97
+ contentType: string;
98
+ /** Error message (only if failed) */
99
+ error?: string;
100
+ }
101
+ /**
102
+ * State for tracking an upload in progress
103
+ */
104
+ export interface UploadState {
105
+ /** Current status */
106
+ status: UploadStatus;
107
+ /** Progress information (during upload) */
108
+ progress: UploadProgress | null;
109
+ /** Result (after completion) */
110
+ result: UploadResult | null;
111
+ /** Error message (if failed) */
112
+ error: string | null;
113
+ }
114
+ /**
115
+ * API request body for presign endpoint
116
+ */
117
+ export interface PresignApiRequest {
118
+ filename: string;
119
+ contentType: string;
120
+ folder: MediaFolder;
121
+ entityId?: string;
122
+ }
123
+ /**
124
+ * API response from presign endpoint
125
+ */
126
+ export interface PresignApiResponse {
127
+ success: boolean;
128
+ data?: PresignedUrlResponse;
129
+ error?: string;
130
+ }
131
+ /**
132
+ * API request body for delete endpoint
133
+ */
134
+ export interface DeleteApiRequest {
135
+ /** URL or key of the file to delete */
136
+ url: string;
137
+ }
138
+ /**
139
+ * API response from delete endpoint
140
+ */
141
+ export interface DeleteApiResponse {
142
+ success: boolean;
143
+ error?: string;
144
+ }
145
+ /**
146
+ * Standard API error response
147
+ */
148
+ export interface MediaApiError {
149
+ success: false;
150
+ error: string;
151
+ code?: string;
152
+ }
153
+ /**
154
+ * Options for useUpload hook
155
+ */
156
+ export interface UseUploadOptions {
157
+ /** Target folder for uploads */
158
+ folder: MediaFolder;
159
+ /** Associated entity ID */
160
+ entityId?: string;
161
+ /** Upload configuration overrides */
162
+ config?: Partial<UploadConfig>;
163
+ /** Callback when upload completes */
164
+ onSuccess?: (result: UploadResult) => void;
165
+ /** Callback when upload fails */
166
+ onError?: (error: string) => void;
167
+ /** Callback for progress updates */
168
+ onProgress?: (progress: UploadProgress) => void;
169
+ }
170
+ /**
171
+ * Return type of useUpload hook
172
+ */
173
+ export interface UseUploadReturn {
174
+ /** Upload a single file */
175
+ upload: (file: File) => Promise<UploadResult>;
176
+ /** Upload multiple files */
177
+ uploadMultiple: (files: File[]) => Promise<UploadResult[]>;
178
+ /** Current upload status */
179
+ status: UploadStatus;
180
+ /** Current progress (if uploading) */
181
+ progress: UploadProgress | null;
182
+ /** Whether currently uploading */
183
+ isUploading: boolean;
184
+ /** Current error (if any) */
185
+ error: string | null;
186
+ /** Reset state to idle */
187
+ reset: () => void;
188
+ }
189
+ /**
190
+ * Props for DropZone component
191
+ */
192
+ export interface DropZoneProps {
193
+ /** Called when files are dropped/selected */
194
+ onFiles: (files: File[]) => void;
195
+ /** Whether to accept multiple files */
196
+ multiple?: boolean;
197
+ /** Accepted file types (MIME) */
198
+ accept?: string[];
199
+ /** Maximum file size in bytes */
200
+ maxSize?: number;
201
+ /** Whether the dropzone is disabled */
202
+ disabled?: boolean;
203
+ /** Additional CSS classes */
204
+ className?: string;
205
+ /** Custom content inside dropzone */
206
+ children?: React.ReactNode;
207
+ }
208
+ /**
209
+ * Props for ImageUpload component
210
+ */
211
+ export interface ImageUploadProps {
212
+ /** Current image URL (null if no image) */
213
+ value: string | null;
214
+ /** Called when image changes */
215
+ onChange: (url: string | null) => void;
216
+ /** Target folder for uploads */
217
+ folder: MediaFolder;
218
+ /** Associated entity ID */
219
+ entityId?: string;
220
+ /** Maximum file size in MB */
221
+ maxSizeMB?: number;
222
+ /** Whether upload is disabled */
223
+ disabled?: boolean;
224
+ /** Additional CSS classes */
225
+ className?: string;
226
+ /** Placeholder text when no image */
227
+ placeholder?: string;
228
+ /** Aspect ratio for preview (e.g., "1/1", "16/9") */
229
+ aspectRatio?: string;
230
+ }
231
+ /**
232
+ * Props for ImageGalleryUpload component
233
+ */
234
+ export interface ImageGalleryUploadProps {
235
+ /** Current array of image URLs */
236
+ value: string[];
237
+ /** Called when images change */
238
+ onChange: (urls: string[]) => void;
239
+ /** Target folder for uploads */
240
+ folder: MediaFolder;
241
+ /** Associated entity ID */
242
+ entityId?: string;
243
+ /** Maximum number of images allowed */
244
+ maxImages?: number;
245
+ /** Maximum file size per image in MB */
246
+ maxSizeMB?: number;
247
+ /** Whether upload is disabled */
248
+ disabled?: boolean;
249
+ /** Additional CSS classes */
250
+ className?: string;
251
+ }
252
+ /**
253
+ * File validation result
254
+ */
255
+ export interface FileValidation {
256
+ valid: boolean;
257
+ error?: string;
258
+ }
259
+ /**
260
+ * Validate a file against upload constraints
261
+ */
262
+ export declare function validateFile(file: File, config?: UploadConfig): FileValidation;
263
+ /**
264
+ * Get file extension from filename
265
+ */
266
+ export declare function getFileExtension(filename: string): string;
267
+ /**
268
+ * Generate a unique filename with timestamp and random suffix
269
+ */
270
+ export declare function generateUniqueFilename(originalFilename: string): string;
271
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/media/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * @rovela-ai/sdk/media/types
3
+ *
4
+ * TypeScript type definitions for media upload functionality.
5
+ * Supports product images, category covers, and variant swatches.
6
+ */
7
+ /**
8
+ * Default upload configuration
9
+ */
10
+ export const DEFAULT_UPLOAD_CONFIG = {
11
+ maxSizeBytes: 10 * 1024 * 1024, // 10MB
12
+ allowedTypes: ['image/jpeg', 'image/png', 'image/webp', 'image/gif'],
13
+ maxFiles: 10,
14
+ };
15
+ /**
16
+ * Validate a file against upload constraints
17
+ */
18
+ export function validateFile(file, config = DEFAULT_UPLOAD_CONFIG) {
19
+ // Check file size
20
+ if (file.size > config.maxSizeBytes) {
21
+ const maxMB = Math.round(config.maxSizeBytes / (1024 * 1024));
22
+ return {
23
+ valid: false,
24
+ error: `File size exceeds ${maxMB}MB limit`,
25
+ };
26
+ }
27
+ // Check file type
28
+ if (!config.allowedTypes.includes(file.type)) {
29
+ return {
30
+ valid: false,
31
+ error: `File type ${file.type} is not allowed`,
32
+ };
33
+ }
34
+ return { valid: true };
35
+ }
36
+ /**
37
+ * Get file extension from filename
38
+ */
39
+ export function getFileExtension(filename) {
40
+ const parts = filename.split('.');
41
+ return parts.length > 1 ? parts.pop().toLowerCase() : '';
42
+ }
43
+ /**
44
+ * Generate a unique filename with timestamp and random suffix
45
+ */
46
+ export function generateUniqueFilename(originalFilename) {
47
+ const ext = getFileExtension(originalFilename);
48
+ const timestamp = Date.now();
49
+ const random = Math.random().toString(36).substring(2, 8);
50
+ return ext ? `${timestamp}_${random}.${ext}` : `${timestamp}_${random}`;
51
+ }
52
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/media/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAoCH;;GAEG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAiB;IACjD,YAAY,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI,EAAE,OAAO;IACvC,YAAY,EAAE,CAAC,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,CAAC;IACpE,QAAQ,EAAE,EAAE;CACb,CAAA;AAwQD;;GAEG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAU,EACV,SAAuB,qBAAqB;IAE5C,kBAAkB;IAClB,IAAI,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,YAAY,EAAE,CAAC;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,CAAA;QAC7D,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,qBAAqB,KAAK,UAAU;SAC5C,CAAA;IACH,CAAC;IAED,kBAAkB;IAClB,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QAC7C,OAAO;YACL,KAAK,EAAE,KAAK;YACZ,KAAK,EAAE,aAAa,IAAI,CAAC,IAAI,iBAAiB;SAC/C,CAAA;IACH,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,CAAA;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAgB;IAC/C,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IACjC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAG,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAA;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,gBAAwB;IAC7D,MAAM,GAAG,GAAG,gBAAgB,CAAC,gBAAgB,CAAC,CAAA;IAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;IAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;IACzD,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,SAAS,IAAI,MAAM,EAAE,CAAA;AACzE,CAAC"}