@igorvaryvoda/sirv-upload-widget 0.3.1 → 0.3.2

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.
package/README.md CHANGED
@@ -57,8 +57,7 @@ export default function UploadPage() {
57
57
 
58
58
  | Prop | Type | Default | Description |
59
59
  |------|------|---------|-------------|
60
- | `proxyEndpoint` | `string` | - | URL of your upload proxy (Cloudflare Worker) |
61
- | `presignEndpoint` | `string` | - | Alternative: endpoint for presigned URLs |
60
+ | `proxyEndpoint` | `string` | **required** | URL of your upload proxy that forwards to Sirv REST API |
62
61
  | `folder` | `string` | `"/"` | Default upload folder |
63
62
  | `onUpload` | `(files: SirvFile[]) => void` | - | Callback when files are uploaded |
64
63
  | `onError` | `(error: string, file?: SirvFile) => void` | - | Callback on upload errors |
package/dist/index.d.mts CHANGED
@@ -32,64 +32,51 @@ interface ConflictInfo {
32
32
  suggestedPath: string;
33
33
  }
34
34
  /**
35
- * For presigned URL mode, user's backend only needs ONE endpoint.
36
- * The widget uploads directly to Sirv's S3 endpoint.
35
+ * The widget uploads files through a proxy endpoint to Sirv's REST API.
36
+ * Your backend receives the file and forwards it to Sirv.
37
37
  *
38
38
  * Example backend implementation (Next.js):
39
39
  * ```typescript
40
- * import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3'
41
- * import { getSignedUrl } from '@aws-sdk/s3-request-presigner'
40
+ * // app/api/sirv/upload/route.ts
41
+ * export async function POST(req: Request) {
42
+ * const url = new URL(req.url)
43
+ * const filename = url.searchParams.get('filename')!
44
+ * const folder = url.searchParams.get('folder') || '/'
42
45
  *
43
- * const s3 = new S3Client({
44
- * endpoint: 'https://s3.sirv.com',
45
- * region: 'us-east-1',
46
- * credentials: {
47
- * accessKeyId: process.env.SIRV_S3_KEY!,
48
- * secretAccessKey: process.env.SIRV_S3_SECRET!,
49
- * },
50
- * forcePathStyle: true,
51
- * })
46
+ * // Get Sirv access token (you should cache this)
47
+ * const tokenRes = await fetch('https://api.sirv.com/v2/token', {
48
+ * method: 'POST',
49
+ * headers: { 'Content-Type': 'application/json' },
50
+ * body: JSON.stringify({
51
+ * clientId: process.env.SIRV_CLIENT_ID,
52
+ * clientSecret: process.env.SIRV_CLIENT_SECRET,
53
+ * }),
54
+ * })
55
+ * const { token } = await tokenRes.json()
52
56
  *
53
- * export async function POST(req: Request) {
54
- * const { filename, contentType, folder } = await req.json()
55
- * const key = `${folder}/${filename}`.replace(/^\/+/, '')
57
+ * // Upload file to Sirv REST API
58
+ * const path = `${folder}/${filename}`.replace(/\/+/g, '/')
59
+ * const uploadRes = await fetch(
60
+ * `https://api.sirv.com/v2/files/upload?filename=${encodeURIComponent(path)}`,
61
+ * {
62
+ * method: 'POST',
63
+ * headers: {
64
+ * 'Authorization': `Bearer ${token}`,
65
+ * 'Content-Type': req.headers.get('Content-Type') || 'application/octet-stream',
66
+ * },
67
+ * body: req.body,
68
+ * }
69
+ * )
56
70
  *
57
- * const uploadUrl = await getSignedUrl(s3, new PutObjectCommand({
58
- * Bucket: process.env.SIRV_BUCKET!,
59
- * Key: key,
60
- * ContentType: contentType,
61
- * }), { expiresIn: 300 })
71
+ * if (!uploadRes.ok) {
72
+ * return Response.json({ success: false, error: 'Upload failed' }, { status: 500 })
73
+ * }
62
74
  *
63
- * const publicUrl = `https://${process.env.SIRV_BUCKET}.sirv.com/${key}`
64
- * return Response.json({ uploadUrl, publicUrl, path: '/' + key })
75
+ * const publicUrl = `https://${process.env.SIRV_ACCOUNT}.sirv.com${path}`
76
+ * return Response.json({ success: true, url: publicUrl, path })
65
77
  * }
66
78
  * ```
67
79
  */
68
- /** POST {presignEndpoint} - Get a presigned upload URL */
69
- interface PresignRequest {
70
- /** Target filename */
71
- filename: string;
72
- /** Content type of the file */
73
- contentType: string;
74
- /** Target folder path (e.g., "/uploads/2024") */
75
- folder?: string;
76
- /** File size in bytes (for validation) */
77
- size?: number;
78
- }
79
- interface PresignResponse {
80
- /** Presigned URL to upload directly to Sirv S3 */
81
- uploadUrl: string;
82
- /** Public CDN URL where file will be accessible */
83
- publicUrl: string;
84
- /** Path on Sirv (e.g., "/uploads/2024/image.jpg") */
85
- path: string;
86
- /** Error message if failed */
87
- error?: string;
88
- }
89
- /**
90
- * For proxy mode, user's backend handles all Sirv operations.
91
- * Use this if you can't use presigned URLs or need more control.
92
- */
93
80
  /** POST {endpoint}/upload - Upload a file to Sirv */
94
81
  interface UploadRequest {
95
82
  /** Base64-encoded file data OR a URL to fetch */
@@ -182,27 +169,19 @@ interface GoogleDriveConfig {
182
169
  }
183
170
  interface SirvUploaderProps {
184
171
  /**
185
- * RECOMMENDED: Endpoint to get presigned upload URLs.
186
- * Widget will POST { filename, contentType, folder } and expect { uploadUrl, publicUrl, path }
187
- * Then upload directly to Sirv's S3 endpoint.
172
+ * Proxy endpoint URL for uploading files to Sirv.
173
+ * Your backend should forward the file to Sirv's REST API.
188
174
  *
189
- * Your backend just needs to call AWS SDK's getSignedUrl with Sirv's S3 endpoint.
190
- */
191
- presignEndpoint?: string;
192
- /**
193
- * ALTERNATIVE: Base URL for full proxy endpoint.
194
- * Use this if you can't use presigned URLs.
195
175
  * The widget will call:
196
- * - POST {endpoint}/upload (with file data)
197
- * - GET {endpoint}/browse
176
+ * - POST {endpoint}/upload?filename=...&folder=... (with file binary in body)
177
+ *
178
+ * Expected response: { success: true, url: "...", path: "..." }
179
+ *
180
+ * Optional endpoints for file browsing (if you want to enable Sirv file picker):
181
+ * - GET {endpoint}/browse?path=/folder
198
182
  * - DELETE {endpoint}/delete
199
183
  */
200
- proxyEndpoint?: string;
201
- /**
202
- * Sirv account/bucket name (e.g., "myaccount" for myaccount.sirv.com)
203
- * Required for file picker when using presigned URLs.
204
- */
205
- sirvAccount?: string;
184
+ proxyEndpoint: string;
206
185
  /**
207
186
  * Default folder to upload files to.
208
187
  * @default "/"
@@ -232,14 +211,14 @@ interface SirvUploaderProps {
232
211
  batch?: boolean;
233
212
  /** Enable CSV/Excel import tab. @default true */
234
213
  csvImport?: boolean;
235
- /** Enable Sirv file picker. @default true */
236
- filePicker?: boolean;
237
214
  /** Enable drag and drop. @default true */
238
215
  dragDrop?: boolean;
239
216
  /** Enable clipboard paste. @default true */
240
217
  paste?: boolean;
241
218
  /** Accept all asset types (images, videos, 3D, PDF). @default false */
242
219
  allAssets?: boolean;
220
+ /** Enable built-in image editor for staged files. @default true */
221
+ imageEditor?: boolean;
243
222
  };
244
223
  /**
245
224
  * Dropbox integration configuration.
@@ -343,21 +322,35 @@ interface SirvUploaderLabels {
343
322
  conflictMessage: string;
344
323
  filesSelected: string;
345
324
  }
325
+ /**
326
+ * @deprecated Import from hooks/useSirvUpload.ts instead
327
+ */
346
328
  interface UseSirvUploadOptions$1 {
347
- endpoint: string;
329
+ /** Proxy endpoint URL for uploading files to Sirv */
330
+ proxyEndpoint: string;
331
+ /** Default upload folder */
348
332
  folder: string;
333
+ /** Conflict resolution strategy */
349
334
  onConflict: ConflictResolution | 'ask';
335
+ /** Max concurrent uploads */
350
336
  concurrency: number;
337
+ /** Auto-upload on file add */
351
338
  autoUpload: boolean;
339
+ /** Max file size */
352
340
  maxFileSize: number;
341
+ /** Callback on successful uploads */
353
342
  onUpload?: (files: SirvFile[]) => void;
343
+ /** Callback on errors */
354
344
  onError?: (error: string, file?: SirvFile) => void;
355
345
  }
346
+ /**
347
+ * @deprecated Import from hooks/useSirvUpload.ts instead
348
+ */
356
349
  interface UseSirvUploadReturn$1 {
357
350
  /** Current files in the queue */
358
351
  files: SirvFile[];
359
- /** Add files to the queue */
360
- addFiles: (files: File[]) => void;
352
+ /** Add SirvFile objects to the queue */
353
+ addFiles: (files: SirvFile[]) => void;
361
354
  /** Add URLs (from CSV/spreadsheet) */
362
355
  addUrls: (urls: string[]) => void;
363
356
  /** Remove a file from the queue */
@@ -372,10 +365,6 @@ interface UseSirvUploadReturn$1 {
372
365
  retryFile: (id: string) => Promise<void>;
373
366
  /** Cancel an in-progress upload */
374
367
  cancelUpload: (id: string) => void;
375
- /** Resolve a conflict */
376
- resolveConflict: (id: string, resolution: ConflictResolution) => void;
377
- /** Current conflict needing resolution (if onConflict='ask') */
378
- currentConflict: ConflictInfo | null;
379
368
  /** Overall upload progress (0-100) */
380
369
  progress: number;
381
370
  /** True if any uploads are in progress */
@@ -383,34 +372,6 @@ interface UseSirvUploadReturn$1 {
383
372
  /** True if all files have been uploaded */
384
373
  isComplete: boolean;
385
374
  }
386
- interface UseFilePickerOptions {
387
- endpoint: string;
388
- fileType?: 'image' | 'video' | 'all';
389
- }
390
- interface UseFilePickerReturn {
391
- /** Current folder path */
392
- currentPath: string;
393
- /** Items in current folder */
394
- items: BrowseItem[];
395
- /** Loading state */
396
- isLoading: boolean;
397
- /** Error message */
398
- error: string | null;
399
- /** Navigate to a folder */
400
- navigateTo: (path: string) => void;
401
- /** Go up one folder */
402
- goUp: () => void;
403
- /** Refresh current folder */
404
- refresh: () => void;
405
- /** Search within current folder */
406
- search: (query: string) => void;
407
- /** Select a file */
408
- selectFile: (item: BrowseItem) => void;
409
- /** Selected files */
410
- selectedFiles: BrowseItem[];
411
- /** Clear selection */
412
- clearSelection: () => void;
413
- }
414
375
  interface ParsedUrl {
415
376
  url: string;
416
377
  path: string;
@@ -433,7 +394,7 @@ interface CsvParseResult {
433
394
  invalidCount: number;
434
395
  }
435
396
 
436
- declare function SirvUploader({ presignEndpoint, proxyEndpoint, sirvAccount, folder, onUpload, onError, onSelect, onRemove, features, dropbox, googleDrive, maxFiles, maxFileSize, accept, onConflict, autoUpload, concurrency, className, disabled, compact, theme, labels: customLabels, children, }: SirvUploaderProps): react_jsx_runtime.JSX.Element;
397
+ declare function SirvUploader({ proxyEndpoint, folder, onUpload, onError, onSelect, onRemove, features, dropbox, googleDrive, maxFiles, maxFileSize, accept, onConflict, autoUpload, concurrency, className, disabled, compact, theme, labels: customLabels, children, }: SirvUploaderProps): react_jsx_runtime.JSX.Element;
437
398
 
438
399
  interface DropZoneProps {
439
400
  onFiles: (files: SirvFile[]) => void;
@@ -484,11 +445,13 @@ interface StagedFilesGridProps {
484
445
  files: SirvFile[];
485
446
  onRemove: (id: string) => void;
486
447
  onEdit?: (file: SirvFile) => void;
448
+ onFileEdited?: (id: string, editedFile: File, previewUrl: string) => void;
487
449
  onAddMore?: (files: SirvFile[]) => void;
488
450
  maxFiles?: number;
489
451
  accept?: string;
490
452
  disabled?: boolean;
491
453
  showFilenames?: boolean;
454
+ enableEditor?: boolean;
492
455
  className?: string;
493
456
  labels?: {
494
457
  addMore?: string;
@@ -496,28 +459,7 @@ interface StagedFilesGridProps {
496
459
  remove?: string;
497
460
  };
498
461
  }
499
- declare function StagedFilesGrid({ files, onRemove, onEdit, onAddMore, maxFiles, accept, disabled, showFilenames, className, labels, }: StagedFilesGridProps): react_jsx_runtime.JSX.Element;
500
-
501
- interface FilePickerProps {
502
- endpoint: string;
503
- isOpen: boolean;
504
- onClose: () => void;
505
- onSelect: (items: BrowseItem[]) => void;
506
- fileType?: 'image' | 'video' | 'all';
507
- multiple?: boolean;
508
- initialPath?: string;
509
- className?: string;
510
- labels?: {
511
- title?: string;
512
- select?: string;
513
- cancel?: string;
514
- search?: string;
515
- empty?: string;
516
- loading?: string;
517
- error?: string;
518
- };
519
- }
520
- declare function FilePicker({ endpoint, isOpen, onClose, onSelect, fileType, multiple, initialPath, className, labels, }: FilePickerProps): react_jsx_runtime.JSX.Element | null;
462
+ declare function StagedFilesGrid({ files, onRemove, onEdit, onFileEdited, onAddMore, maxFiles, accept, disabled, showFilenames, enableEditor, className, labels, }: StagedFilesGridProps): react_jsx_runtime.JSX.Element;
521
463
 
522
464
  interface SpreadsheetImportProps {
523
465
  onUrls: (urls: string[]) => void;
@@ -533,11 +475,39 @@ interface SpreadsheetImportProps {
533
475
  }
534
476
  declare function SpreadsheetImport({ onUrls, className, labels, }: SpreadsheetImportProps): react_jsx_runtime.JSX.Element;
535
477
 
478
+ interface ImageEditorProps {
479
+ file: File;
480
+ previewUrl: string;
481
+ onApply: (editedFile: File, previewUrl: string) => void;
482
+ onCancel: () => void;
483
+ labels?: {
484
+ title?: string;
485
+ apply?: string;
486
+ cancel?: string;
487
+ reset?: string;
488
+ rotateLeft?: string;
489
+ rotateRight?: string;
490
+ flipHorizontal?: string;
491
+ flipVertical?: string;
492
+ crop?: string;
493
+ transform?: string;
494
+ aspectRatio?: string;
495
+ aspectFree?: string;
496
+ };
497
+ }
498
+ declare function ImageEditor({ file, previewUrl, onApply, onCancel, labels, }: ImageEditorProps): react_jsx_runtime.JSX.Element;
499
+
536
500
  interface UseSirvUploadOptions {
537
- /** Endpoint to get presigned URLs (recommended) */
538
- presignEndpoint?: string;
539
- /** Full proxy endpoint (alternative) */
540
- proxyEndpoint?: string;
501
+ /**
502
+ * Proxy endpoint URL for uploading files to Sirv.
503
+ * Your backend should forward the file to Sirv's REST API.
504
+ *
505
+ * The widget will POST to: {proxyEndpoint}/upload?filename=...&folder=...
506
+ * with the file binary in the request body.
507
+ *
508
+ * Expected response: { success: true, url: "...", path: "..." }
509
+ */
510
+ proxyEndpoint: string;
541
511
  /** Default upload folder */
542
512
  folder: string;
543
513
  /** Conflict resolution strategy */
@@ -750,6 +720,55 @@ declare function useGoogleDrivePicker({ clientId, apiKey, appId, onSelect, onCan
750
720
  clearSession: () => void;
751
721
  };
752
722
 
723
+ type AspectRatio = 'free' | '1:1' | '4:3' | '3:4' | '16:9' | '9:16';
724
+ interface CropArea {
725
+ x: number;
726
+ y: number;
727
+ width: number;
728
+ height: number;
729
+ }
730
+ interface EditorState {
731
+ rotation: 0 | 90 | 180 | 270;
732
+ flipH: boolean;
733
+ flipV: boolean;
734
+ crop: CropArea | null;
735
+ zoom: number;
736
+ }
737
+ interface UseImageEditorOptions {
738
+ file: File;
739
+ previewUrl: string;
740
+ onApply: (editedFile: File, previewUrl: string) => void;
741
+ onCancel: () => void;
742
+ maxCanvasSize?: number;
743
+ }
744
+ interface UseImageEditorReturn {
745
+ canvasRef: React.RefObject<HTMLCanvasElement | null>;
746
+ state: EditorState;
747
+ isLoading: boolean;
748
+ imageLoaded: boolean;
749
+ canvasSize: {
750
+ width: number;
751
+ height: number;
752
+ };
753
+ imageSize: {
754
+ width: number;
755
+ height: number;
756
+ };
757
+ hasChanges: boolean;
758
+ isApplying: boolean;
759
+ aspectRatio: AspectRatio;
760
+ rotateLeft: () => void;
761
+ rotateRight: () => void;
762
+ flipHorizontal: () => void;
763
+ flipVertical: () => void;
764
+ setCrop: (crop: CropArea | null) => void;
765
+ setAspectRatio: (ratio: AspectRatio) => void;
766
+ setZoom: (zoom: number) => void;
767
+ reset: () => void;
768
+ apply: () => Promise<void>;
769
+ }
770
+ declare function useImageEditor({ file, previewUrl, onApply, onCancel, maxCanvasSize, }: UseImageEditorOptions): UseImageEditorReturn;
771
+
753
772
  /**
754
773
  * File utility functions for the Sirv Upload Widget
755
774
  */
@@ -790,6 +809,45 @@ declare function getImageDimensions(file: File): Promise<{
790
809
  } | null>;
791
810
  declare function formatFileSize(bytes: number): string;
792
811
  declare function getMimeType(file: File): string;
812
+ /**
813
+ * Options for creating a SirvFile object
814
+ */
815
+ interface CreateSirvFileOptions {
816
+ file: File;
817
+ /** Override the filename (defaults to file.name) */
818
+ filename?: string;
819
+ /** Skip generating preview URL (for non-image files) */
820
+ skipPreview?: boolean;
821
+ /** Skip getting image dimensions */
822
+ skipDimensions?: boolean;
823
+ /** Initial status (defaults to 'pending') */
824
+ status?: 'pending' | 'error';
825
+ /** Error message if status is 'error' */
826
+ error?: string;
827
+ }
828
+ /**
829
+ * Result of creating a SirvFile
830
+ */
831
+ interface CreateSirvFileResult {
832
+ id: string;
833
+ file: File;
834
+ filename: string;
835
+ previewUrl: string;
836
+ dimensions?: {
837
+ width: number;
838
+ height: number;
839
+ };
840
+ size: number;
841
+ fileCategory: 'image' | 'video' | '3d' | 'pdf' | 'other';
842
+ status: 'pending' | 'error';
843
+ progress: number;
844
+ error?: string;
845
+ }
846
+ /**
847
+ * Create a SirvFile object from a File with proper preview URL and dimensions
848
+ * This is the canonical way to create SirvFile objects to ensure consistency.
849
+ */
850
+ declare function createSirvFile(options: CreateSirvFileOptions): Promise<CreateSirvFileResult>;
793
851
 
794
852
  declare const DELIMITERS: readonly [",", "\t", ";", "|"];
795
853
  type CsvDelimiter = (typeof DELIMITERS)[number];
@@ -797,6 +855,12 @@ type CsvDelimiter = (typeof DELIMITERS)[number];
797
855
  * Detect the delimiter used in CSV content by analyzing the first few lines
798
856
  */
799
857
  declare function detectDelimiter(csvContent: string): CsvDelimiter;
858
+ /**
859
+ * Auto-detect the likely URL column index by checking:
860
+ * 1. Common URL-related header names
861
+ * 2. Cells that start with 'http'
862
+ */
863
+ declare function detectUrlColumnIndex(headers: string[], getCellValue: (rowIndex: number, colIndex: number) => string, rowCount: number): number;
800
864
  interface ParsedUrlItem {
801
865
  url: string;
802
866
  path: string;
@@ -840,4 +904,4 @@ declare function parseExcelClient(arrayBuffer: ArrayBuffer, options?: ParseOptio
840
904
  */
841
905
  declare function isSpreadsheetFile(file: File): boolean;
842
906
 
843
- export { ACCEPTED_3D_FORMATS, ACCEPTED_ALL_FORMATS, ACCEPTED_IMAGE_FORMATS, ACCEPTED_VIDEO_FORMATS, type BrowseItem, type BrowseRequest, type BrowseResponse, type CheckRequest, type CheckResponse, type ClientParseResult, type ConflictInfo, type ConflictResolution, type CsvParseOptions, type CsvParseResult, DEFAULT_MAX_FILE_SIZE, type DeleteRequest, type DeleteResponse, DropZone, type DropZoneProps, type DropboxConfig, type DropboxFile, type FileCategory, FileList, type FileListProps, FileListSummary, FilePicker, type FilePickerProps, type GoogleDriveConfig, type GoogleDriveFile, type ImageDimensions, type ParsedUrl, type ParsedUrlItem, type PresignRequest, type PresignResponse, type SirvFile, SirvUploader, type SirvUploaderLabels, type SirvUploaderProps, SpreadsheetImport, type SpreadsheetImportProps, StagedFilesGrid, type StagedFilesGridProps, type UploadRequest, type UploadResponse, type UploadStatus, type UrlValidator, type UseDropboxChooserOptions, type UseFilePickerOptions, type UseFilePickerReturn, type UseGoogleDrivePickerOptions, type UseSirvUploadOptions$1 as UseSirvUploadOptions, type UseSirvUploadReturn$1 as UseSirvUploadReturn, canPreviewFile, convertHeicWithFallback, defaultUrlValidator, detectDelimiter, formatFileSize, generateId, getFileCategory, getImageDimensions, getMimeType, is3DModelFile, isHeifFile, isImageFile, isPdfFile, isSpreadsheetFile, isSvgFile, isVideoFile, parseCsvClient, parseExcelClient, sirvUrlValidator, useDropboxChooser, useGoogleDrivePicker, useSirvUpload, validateFileSize };
907
+ export { ACCEPTED_3D_FORMATS, ACCEPTED_ALL_FORMATS, ACCEPTED_IMAGE_FORMATS, ACCEPTED_VIDEO_FORMATS, type AspectRatio, type BrowseItem, type BrowseRequest, type BrowseResponse, type CheckRequest, type CheckResponse, type ClientParseResult, type ConflictInfo, type ConflictResolution, type CreateSirvFileOptions, type CreateSirvFileResult, type CropArea, type CsvParseOptions, type CsvParseResult, DEFAULT_MAX_FILE_SIZE, type DeleteRequest, type DeleteResponse, DropZone, type DropZoneProps, type DropboxConfig, type DropboxFile, type EditorState, type FileCategory, FileList, type FileListProps, FileListSummary, type GoogleDriveConfig, type GoogleDriveFile, type ImageDimensions, ImageEditor, type ImageEditorProps, type ParsedUrl, type ParsedUrlItem, type SirvFile, SirvUploader, type SirvUploaderLabels, type SirvUploaderProps, SpreadsheetImport, type SpreadsheetImportProps, StagedFilesGrid, type StagedFilesGridProps, type UploadRequest, type UploadResponse, type UploadStatus, type UrlValidator, type UseDropboxChooserOptions, type UseGoogleDrivePickerOptions, type UseImageEditorOptions, type UseImageEditorReturn, type UseSirvUploadOptions$1 as UseSirvUploadOptions, type UseSirvUploadReturn$1 as UseSirvUploadReturn, canPreviewFile, convertHeicWithFallback, createSirvFile, defaultUrlValidator, detectDelimiter, detectUrlColumnIndex, formatFileSize, generateId, getFileCategory, getImageDimensions, getMimeType, is3DModelFile, isHeifFile, isImageFile, isPdfFile, isSpreadsheetFile, isSvgFile, isVideoFile, parseCsvClient, parseExcelClient, sirvUrlValidator, useDropboxChooser, useGoogleDrivePicker, useImageEditor, useSirvUpload, validateFileSize };