import { ApolloClient } from '@apollo/client'; import { SaveToFilesSource } from './SaveToFilesButton'; import { SaveToFilesRetryNotice } from './uploadBlob'; export interface SavedFile { id: string; name: string; folderId: string | null; } export interface UseSaveToFilesResult { save: (params: { source: SaveToFilesSource; folderPath: string; /** * Fired before each retry wait. Purely informational — the save is still in * flight. Exists so a caller rendering its own chip can say why a cold start is * taking a minute instead of showing a spinner that looks stuck. */ onRetry?: (notice: SaveToFilesRetryNotice) => void; }) => Promise; isPending: boolean; error: Error | null; progress: number; /** * The retry currently being waited for or run — `{ step, attempt, maxAttempts, * delayMs }`. Null on the first attempt of every step, and always cleared when the * save settles either way, so it can never be mistaken for a result. Render it to * explain a long pause; it is not an error and must not be shown as one. */ retry: SaveToFilesRetryNotice | null; } /** The Apollo surface `resolveFolderIdByPath` needs — kept narrow so it is trivial to fake. */ type FolderMutatingClient = Pick; export interface ResolveFolderOptions { onRetry?: (notice: SaveToFilesRetryNotice) => void; /** * Test seam for the retry ladder's clock and waits. Production callers never set * these — without them the real ladder (tens of seconds) runs, which is the point. */ now?: () => number; sleep?: (ms: number) => Promise; } /** * Resolve (creating if needed) the destination folder for a path, returning its id. * * Split out of the hook so it is unit-testable the same way `uploadBlobToFiles` * is: pure modulo the injected Apollo client. * * `ensureFolderByPath` is declared `Folder!` in wspace-files-svc, and its * resolver creates any missing ancestors and collapses concurrent calls for the * same path onto a single row (partial unique index on * `(workspace_id, parent_folder_id, name)`). It therefore never resolves to * null: a null here means the operation FAILED and GraphQL non-null propagation * blanked the field. * * That distinction is the whole point of this function. The shared Apollo client * runs `errorPolicy: 'all'`, so those failures come back IN-BAND on * `result.error` rather than being thrown — exactly as `uploadBlobToFiles` * already documents for initiateUpload/completeUpload. Reading only `data` and * blaming the path turned a gateway OPERATION_TIMEOUT into "Could not resolve * folder for path: /HealthyBowl/AI Assistant/Attachments", which reads like a * bad path or a missing folder and sent people looking at file-storage quota * they had plenty of. Surface the real reason; keep the path as a last resort * for the genuinely inexplicable case. * * A gateway timeout is now also *waited out* rather than only reported, because this * is the hop that was measured failing on a cold container. Only a cold-start-class * failure is retried — a quota rejection, an auth failure or any other 4xx answer * still comes straight back, unchanged and immediately. */ export declare function resolveFolderIdByPath(client: FolderMutatingClient, folderPath: string, options?: ResolveFolderOptions): Promise; export declare function useSaveToFiles(): UseSaveToFilesResult; export {}; //# sourceMappingURL=useSaveToFiles.d.ts.map