1+ import { SupportedFileTypes , InputFile , BulkProcessingOptions , BulkProcessingResult , DocumentConversionOptions } from './types' ;
2+
3+ /**
4+ * Supported file types and their extensions
5+ */
6+ export const SUPPORTED_FILE_TYPES : SupportedFileTypes = {
7+ images : {
8+ 'image/jpeg' : [ '.jpg' , '.jpeg' ] ,
9+ 'image/png' : [ '.png' ] ,
10+ 'image/webp' : [ '.webp' ] ,
11+ 'image/tiff' : [ '.tiff' , '.tif' ] ,
12+ 'image/heic' : [ '.heic' ] ,
13+ 'image/heif' : [ '.heif' ] ,
14+ } ,
15+ pdf : {
16+ 'application/pdf' : [ '.pdf' ] ,
17+ } ,
18+ documents : {
19+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : [ '.docx' ] ,
20+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' : [ '.xlsx' ] ,
21+ 'application/vnd.ms-excel' : [ '.xls' ] ,
22+ 'text/csv' : [ '.csv' ] ,
23+ } ,
24+ } ;
25+
26+ /**
27+ * Get file type from MIME type or filename
28+ */
29+ export function getFileType ( file : File | string ) : InputFile [ 'type' ] {
30+ const mimeType = typeof file === 'string' ? getMimeTypeFromFilename ( file ) : file . type ;
31+ const filename = typeof file === 'string' ? file : file . name ;
32+
33+ // Check image formats
34+ for ( const [ mime , extensions ] of Object . entries ( SUPPORTED_FILE_TYPES . images ) ) {
35+ if ( mimeType === mime || extensions . some ( ext => filename . toLowerCase ( ) . endsWith ( ext ) ) ) {
36+ if ( mime === 'image/heic' || mime === 'image/heif' ) return 'heic' ;
37+ if ( mime === 'image/webp' ) return 'webp' ;
38+ if ( mime === 'image/tiff' ) return 'tiff' ;
39+ return 'image' ;
40+ }
41+ }
42+
43+ // Check PDF
44+ if ( mimeType === 'application/pdf' || filename . toLowerCase ( ) . endsWith ( '.pdf' ) ) {
45+ return 'pdf' ;
46+ }
47+
48+ // Check documents
49+ for ( const [ mime , extensions ] of Object . entries ( SUPPORTED_FILE_TYPES . documents ) ) {
50+ if ( mimeType === mime || extensions . some ( ext => filename . toLowerCase ( ) . endsWith ( ext ) ) ) {
51+ return 'document' ;
52+ }
53+ }
54+
55+ // Default to image for unknown types
56+ return 'image' ;
57+ }
58+
59+ /**
60+ * Get MIME type from filename extension
61+ */
62+ function getMimeTypeFromFilename ( filename : string ) : string {
63+ const ext = filename . toLowerCase ( ) . split ( '.' ) . pop ( ) ;
64+
65+ const mimeMap : Record < string , string > = {
66+ jpg : 'image/jpeg' ,
67+ jpeg : 'image/jpeg' ,
68+ png : 'image/png' ,
69+ webp : 'image/webp' ,
70+ tiff : 'image/tiff' ,
71+ tif : 'image/tiff' ,
72+ heic : 'image/heic' ,
73+ heif : 'image/heif' ,
74+ pdf : 'application/pdf' ,
75+ docx : 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ,
76+ xlsx : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ,
77+ xls : 'application/vnd.ms-excel' ,
78+ csv : 'text/csv' ,
79+ } ;
80+
81+ return mimeMap [ ext || '' ] || 'application/octet-stream' ;
82+ }
83+
84+ /**
85+ * Check if a file type is supported
86+ */
87+ export function isSupportedFile ( file : File | string ) : boolean {
88+ const type = getFileType ( file ) ;
89+ return [ 'image' , 'pdf' , 'heic' , 'webp' , 'tiff' , 'document' ] . includes ( type ) ;
90+ }
91+
92+ /**
93+ * Get supported file extensions as a string for file input accept attribute
94+ */
95+ export function getSupportedExtensions ( ) : string {
96+ const allExtensions : string [ ] = [ ] ;
97+
98+ // Add image extensions
99+ Object . values ( SUPPORTED_FILE_TYPES . images ) . forEach ( extensions => {
100+ allExtensions . push ( ...extensions ) ;
101+ } ) ;
102+
103+ // Add PDF extensions
104+ Object . values ( SUPPORTED_FILE_TYPES . pdf ) . forEach ( extensions => {
105+ allExtensions . push ( ...extensions ) ;
106+ } ) ;
107+
108+ // Add document extensions
109+ Object . values ( SUPPORTED_FILE_TYPES . documents ) . forEach ( extensions => {
110+ allExtensions . push ( ...extensions ) ;
111+ } ) ;
112+
113+ return allExtensions . join ( ',' ) ;
114+ }
115+
116+ /**
117+ * Convert HEIC/HEIF to JPEG for processing
118+ */
119+ export async function convertHeicToJpeg ( file : File ) : Promise < File > {
120+ // Note: This would require a HEIC decoder library like libheif-js or heic2any
121+ // For now, return a placeholder implementation
122+ console . warn ( 'HEIC conversion not yet implemented - requires libheif-js or similar' ) ;
123+
124+ // Placeholder: return original file (will likely fail processing)
125+ return new File ( [ file ] , file . name . replace ( / \. ( h e i c | h e i f ) $ / i, '.jpg' ) , {
126+ type : 'image/jpeg'
127+ } ) ;
128+ }
129+
130+ /**
131+ * Convert WebP to PNG/JPEG for better compatibility
132+ */
133+ export async function convertWebPToStandard ( file : File , targetFormat : 'png' | 'jpeg' = 'png' ) : Promise < File > {
134+ return new Promise ( ( resolve , reject ) => {
135+ const canvas = document . createElement ( 'canvas' ) ;
136+ const ctx = canvas . getContext ( '2d' ) ;
137+ const img = new Image ( ) ;
138+
139+ img . onload = ( ) => {
140+ canvas . width = img . width ;
141+ canvas . height = img . height ;
142+
143+ if ( ! ctx ) {
144+ reject ( new Error ( 'Canvas context not available' ) ) ;
145+ return ;
146+ }
147+
148+ ctx . drawImage ( img , 0 , 0 ) ;
149+
150+ canvas . toBlob ( ( blob ) => {
151+ if ( ! blob ) {
152+ reject ( new Error ( 'Failed to convert WebP' ) ) ;
153+ return ;
154+ }
155+
156+ const convertedFile = new File ( [ blob ] ,
157+ file . name . replace ( / \. w e b p $ / i, `.${ targetFormat } ` ) , {
158+ type : `image/${ targetFormat } `
159+ } ) ;
160+
161+ resolve ( convertedFile ) ;
162+ } , `image/${ targetFormat } ` , 0.9 ) ;
163+ } ;
164+
165+ img . onerror = ( ) => reject ( new Error ( 'Failed to load WebP image' ) ) ;
166+ img . src = URL . createObjectURL ( file ) ;
167+ } ) ;
168+ }
169+
170+ /**
171+ * Convert TIFF to PNG for processing
172+ */
173+ export async function convertTiffToPng ( file : File ) : Promise < File > {
174+ // Note: This would require a TIFF decoder library like tiff.js
175+ console . warn ( 'TIFF conversion not yet implemented - requires tiff.js or similar' ) ;
176+
177+ // Placeholder: return as PNG type but original data (will likely fail)
178+ return new File ( [ file ] , file . name . replace ( / \. t i f f ? $ / i, '.png' ) , {
179+ type : 'image/png'
180+ } ) ;
181+ }
182+
183+ /**
184+ * Process bulk files with progress tracking
185+ */
186+ export async function processBulkFiles (
187+ files : InputFile [ ] ,
188+ analyzeFn : ( file : InputFile ) => Promise < any > ,
189+ applyFn : ( file : InputFile , result : any ) => Promise < any > ,
190+ options : BulkProcessingOptions = { }
191+ ) : Promise < BulkProcessingResult > {
192+ const {
193+ maxConcurrency = 3 ,
194+ onProgress,
195+ onFileComplete,
196+ stopOnError = false
197+ } = options ;
198+
199+ const startTime = Date . now ( ) ;
200+ const results : ( any | null ) [ ] = [ ] ;
201+ const errors : ( Error | null ) [ ] = [ ] ;
202+ let successful = 0 ;
203+ let failed = 0 ;
204+
205+ // Process files in batches to control concurrency
206+ for ( let i = 0 ; i < files . length ; i += maxConcurrency ) {
207+ const batch = files . slice ( i , i + maxConcurrency ) ;
208+
209+ const batchPromises = batch . map ( async ( file , batchIndex ) => {
210+ const fileIndex = i + batchIndex ;
211+
212+ try {
213+ onProgress ?.( fileIndex , files . length , file . name || `file-${ fileIndex } ` ) ;
214+
215+ // Analyze file
216+ const analyzeResult = await analyzeFn ( file ) ;
217+
218+ // Apply redactions
219+ const applyResult = await applyFn ( file , analyzeResult ) ;
220+
221+ results [ fileIndex ] = applyResult ;
222+ errors [ fileIndex ] = null ;
223+ successful ++ ;
224+
225+ onFileComplete ?.( file , applyResult ) ;
226+
227+ return applyResult ;
228+ } catch ( error ) {
229+ const err = error instanceof Error ? error : new Error ( String ( error ) ) ;
230+ results [ fileIndex ] = null ;
231+ errors [ fileIndex ] = err ;
232+ failed ++ ;
233+
234+ onFileComplete ?.( file , null , err ) ;
235+
236+ if ( stopOnError ) {
237+ throw err ;
238+ }
239+
240+ return null ;
241+ }
242+ } ) ;
243+
244+ try {
245+ await Promise . all ( batchPromises ) ;
246+ } catch ( error ) {
247+ if ( stopOnError ) {
248+ break ;
249+ }
250+ }
251+ }
252+
253+ const duration = Date . now ( ) - startTime ;
254+
255+ return {
256+ successful,
257+ failed,
258+ total : files . length ,
259+ results,
260+ errors,
261+ duration
262+ } ;
263+ }
264+
265+ /**
266+ * Convert document to PDF for processing
267+ */
268+ export async function convertDocumentToPdf (
269+ file : File ,
270+ options : DocumentConversionOptions = { targetFormat : 'pdf' }
271+ ) : Promise < File > {
272+ // Note: This would require document conversion libraries
273+ // For DOCX: mammoth.js + html-to-pdf
274+ // For XLSX: xlsx + html-to-pdf
275+
276+ console . warn ( `Document conversion not yet implemented for ${ file . type } ` ) ;
277+ console . log ( 'Would convert:' , file . name , 'with options:' , options ) ;
278+
279+ // Placeholder: return original file
280+ return file ;
281+ }
0 commit comments