diff --git a/frontend/src/core/hooks/tools/shared/useToolApiCalls.ts b/frontend/src/core/hooks/tools/shared/useToolApiCalls.ts index a8e6a88a02..1ab8925f14 100644 --- a/frontend/src/core/hooks/tools/shared/useToolApiCalls.ts +++ b/frontend/src/core/hooks/tools/shared/useToolApiCalls.ts @@ -4,6 +4,7 @@ import apiClient from '@app/services/apiClient'; // Our configured instance import { processResponse, ResponseHandler } from '@app/utils/toolResponseProcessor'; import { isEmptyOutput } from '@app/services/errorUtils'; import type { ProcessingProgress } from '@app/hooks/tools/shared/useToolState'; +import type { StirlingFile, FileId } from '@app/types/fileContext'; export interface ApiCallsConfig { endpoint: string | ((params: TParams) => string); @@ -18,14 +19,14 @@ export const useToolApiCalls = () => { const processFiles = useCallback(async ( params: TParams, - validFiles: File[], + validFiles: StirlingFile[], config: ApiCallsConfig, onProgress: (progress: ProcessingProgress) => void, onStatus: (status: string) => void, - markFileError?: (fileId: string) => void, - ): Promise<{ outputFiles: File[]; successSourceIds: string[] }> => { + markFileError?: (fileId: FileId) => void, + ): Promise<{ outputFiles: File[]; successSourceIds: FileId[] }> => { const processedFiles: File[] = []; - const successSourceIds: string[] = []; + const successSourceIds: FileId[] = []; const failedFiles: string[] = []; const total = validFiles.length; @@ -35,7 +36,7 @@ export const useToolApiCalls = () => { for (let i = 0; i < validFiles.length; i++) { const file = validFiles[i]; - console.debug('[processFiles] Start', { index: i, total, name: file.name, fileId: (file as any).fileId }); + console.debug('[processFiles] Start', { index: i, total, name: file.name, fileId: file.fileId }); onProgress({ current: i + 1, total, currentFileName: file.name }); onStatus(`Processing ${file.name} (${i + 1}/${total})`); @@ -47,7 +48,7 @@ export const useToolApiCalls = () => { responseType: 'blob', cancelToken: cancelTokenRef.current?.token, }); - console.debug('[processFiles] Response OK', { name: file.name, status: (response as any)?.status }); + console.debug('[processFiles] Response OK', { name: file.name, status: response.status }); // Forward to shared response processor (uses tool-specific responseHandler if provided) const responseFiles = await processResponse( @@ -63,7 +64,7 @@ export const useToolApiCalls = () => { console.warn('[processFiles] Empty output treated as failure', { name: file.name }); failedFiles.push(file.name); try { - (markFileError as any)?.((file as any).fileId); + markFileError?.(file.fileId); } catch (e) { console.debug('markFileError', e); } @@ -71,7 +72,7 @@ export const useToolApiCalls = () => { } processedFiles.push(...responseFiles); // record source id as successful - successSourceIds.push((file as any).fileId); + successSourceIds.push(file.fileId); console.debug('[processFiles] Success', { name: file.name, produced: responseFiles.length }); } catch (error) { @@ -82,7 +83,7 @@ export const useToolApiCalls = () => { failedFiles.push(file.name); // mark errored file so UI can highlight try { - (markFileError as any)?.((file as any).fileId); + markFileError?.(file.fileId); } catch (e) { console.debug('markFileError', e); } diff --git a/frontend/src/core/hooks/tools/shared/useToolOperation.ts b/frontend/src/core/hooks/tools/shared/useToolOperation.ts index e9eccdc95f..4ef803efb5 100644 --- a/frontend/src/core/hooks/tools/shared/useToolOperation.ts +++ b/frontend/src/core/hooks/tools/shared/useToolOperation.ts @@ -190,17 +190,17 @@ export const useToolOperation = ( } // Handle zero-byte inputs explicitly: mark as error and continue with others - const zeroByteFiles = selectedFiles.filter(file => (file as any)?.size === 0); + const zeroByteFiles = selectedFiles.filter(file => file.size === 0); if (zeroByteFiles.length > 0) { try { for (const f of zeroByteFiles) { - (fileActions.markFileError as any)((f as any).fileId); + fileActions.markFileError(f.fileId); } } catch (e) { console.log('markFileError', e); } } - const validFiles = selectedFiles.filter(file => (file as any)?.size > 0); + const validFiles: StirlingFile[] = selectedFiles.filter(file => file.size > 0); if (validFiles.length === 0) { actions.setError(t('noValidFiles', 'No valid files to process')); return; @@ -233,7 +233,7 @@ export const useToolOperation = ( try { let processedFiles: File[]; - let successSourceIds: string[] = []; + let successSourceIds: FileId[] = []; // Use original files directly (no PDF metadata injection - history stored in IndexedDB) const filesForAPI = extractFiles(validFiles); @@ -251,14 +251,14 @@ export const useToolOperation = ( console.debug('[useToolOperation] Multi-file start', { count: filesForAPI.length }); const result = await processFiles( params, - filesForAPI, + validFiles, apiCallsConfig, actions.setProgress, actions.setStatus, - fileActions.markFileError as any + fileActions.markFileError ); processedFiles = result.outputFiles; - successSourceIds = result.successSourceIds as any; + successSourceIds = result.successSourceIds; console.debug('[useToolOperation] Multi-file results', { outputFiles: processedFiles.length, successSources: result.successSourceIds.length }); break; } @@ -286,7 +286,7 @@ export const useToolOperation = ( processedFiles = await extractZipFiles(response.data); } // Assume all inputs succeeded together unless server provided an error earlier - successSourceIds = validFiles.map(f => (f as any).fileId) as any; + successSourceIds = validFiles.map(f => f.fileId); break; } @@ -300,15 +300,15 @@ export const useToolOperation = ( // If consumedAllInputs flag is set, mark all inputs as successful // (used for operations that combine N inputs into fewer outputs) if (consumedAllInputs) { - successSourceIds = validFiles.map(f => (f as any).fileId) as any; + successSourceIds = validFiles.map(f => f.fileId); } else { // Try to map outputs back to inputs by filename (before extension) - const inputBaseNames = new Map(); + const inputBaseNames = new Map(); for (const f of validFiles) { const base = (f.name || '').replace(/\.[^.]+$/, '').toLowerCase(); - inputBaseNames.set(base, (f as any).fileId); + inputBaseNames.set(base, f.fileId); } - const mappedSuccess: string[] = []; + const mappedSuccess: FileId[] = []; for (const out of processedFiles) { const base = (out.name || '').replace(/\.[^.]+$/, '').toLowerCase(); const id = inputBaseNames.get(base); @@ -316,9 +316,9 @@ export const useToolOperation = ( } // Fallback to naive alignment if names don't match if (mappedSuccess.length === 0) { - successSourceIds = validFiles.slice(0, processedFiles.length).map(f => (f as any).fileId) as any; + successSourceIds = validFiles.slice(0, processedFiles.length).map(f => f.fileId); } else { - successSourceIds = mappedSuccess as any; + successSourceIds = mappedSuccess; } } break; @@ -327,16 +327,16 @@ export const useToolOperation = ( // Normalize error flags across tool types: mark failures, clear successes try { - const allInputIds = validFiles.map(f => (f as any).fileId) as unknown as string[]; - const okSet = new Set((successSourceIds as unknown as string[]) || []); + const allInputIds = validFiles.map(f => f.fileId); + const okSet = new Set(successSourceIds); // Clear errors on successes for (const okId of okSet) { - try { (fileActions.clearFileError as any)(okId); } catch (_e) { void _e; } + try { fileActions.clearFileError(okId); } catch (_e) { void _e; } } // Mark errors on inputs that didn't succeed for (const id of allInputIds) { if (!okSet.has(id)) { - try { (fileActions.markFileError as any)(id); } catch (_e) { void _e; } + try { fileActions.markFileError(id); } catch (_e) { void _e; } } } } catch (_e) { void _e; } @@ -344,12 +344,12 @@ export const useToolOperation = ( if (externalErrorFileIds.length > 0) { // If backend told us which sources failed, prefer that mapping successSourceIds = validFiles - .map(f => (f as any).fileId) - .filter(id => !externalErrorFileIds.includes(id)) as any; + .map(f => f.fileId) + .filter(id => !externalErrorFileIds.includes(id)); // Also mark failed IDs immediately try { for (const badId of externalErrorFileIds) { - (fileActions.markFileError as any)(badId); + fileActions.markFileError(badId as FileId); } } catch (_e) { void _e; } } @@ -398,7 +398,7 @@ export const useToolOperation = ( ); // Always create child stubs linking back to the successful source inputs const successInputStubs = successSourceIds - .map((id) => selectors.getStirlingFileStub(id as any)) + .map((id) => selectors.getStirlingFileStub(id)) .filter(Boolean) as StirlingFileStub[]; if (successInputStubs.length !== processedFiles.length) { @@ -424,7 +424,7 @@ export const useToolOperation = ( return createStirlingFile(file, childStub.id); }); // Build consumption arrays aligned to the successful source IDs - const toConsumeInputIds = successSourceIds.filter((id: string) => inputFileIds.includes(id as any)) as unknown as FileId[]; + const toConsumeInputIds = successSourceIds.filter((id) => inputFileIds.includes(id)); // Outputs and stubs are already ordered by success sequence console.debug('[useToolOperation] Consuming files', { inputCount: inputFileIds.length, toConsume: toConsumeInputIds.length }); const outputFileIds = await consumeFiles(toConsumeInputIds, outputStirlingFiles, outputStirlingFileStubs); @@ -441,25 +441,27 @@ export const useToolOperation = ( } catch (error: any) { // Centralized 422 handler: mark provided IDs in errorFileIds try { - const status = (error?.response?.status as number | undefined); - if (status === 422) { + const status = error?.response?.status; + if (typeof status === 'number' && status === 422) { const payload = error?.response?.data; - let parsed: any = payload; + let parsed: unknown = payload; if (typeof payload === 'string') { try { parsed = JSON.parse(payload); } catch { parsed = payload; } - } else if (payload && typeof (payload as any).text === 'function') { + } else if (payload && typeof (payload as Blob).text === 'function') { // Blob or Response-like object from axios when responseType='blob' const text = await (payload as Blob).text(); try { parsed = JSON.parse(text); } catch { parsed = text; } } - let ids: string[] | undefined = Array.isArray(parsed?.errorFileIds) ? parsed.errorFileIds : undefined; + let ids: string[] | undefined = Array.isArray((parsed as { errorFileIds?: unknown })?.errorFileIds) + ? (parsed as { errorFileIds: string[] }).errorFileIds + : undefined; if (!ids && typeof parsed === 'string') { const match = parsed.match(/[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/g); if (match && match.length > 0) ids = Array.from(new Set(match)); } if (ids && ids.length > 0) { for (const badId of ids) { - try { (fileActions.markFileError as any)(badId); } catch (_e) { void _e; } + try { fileActions.markFileError(badId as FileId); } catch (_e) { void _e; } } actions.setStatus('Process failed due to invalid/corrupted file(s)'); // Avoid duplicating toast messaging here