@better-s3/react 2.0.0 → 2.2.0
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/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/types/download.d.ts +2 -14
- package/dist/use-download.d.ts +6 -9
- package/dist/use-fetch-download.d.ts +33 -0
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -6,5 +6,6 @@ export { useUpload, type UseUploadOptions, type UseUploadState, type UseUploadRe
|
|
|
6
6
|
export { useMultiUpload, type UseMultiUploadOptions, type UseMultiUploadState, type UseMultiUploadReturn, } from "./use-multi-upload";
|
|
7
7
|
export { useUploadControls, type UseUploadControlsOptions, type UseUploadControlsReturn, } from "./use-upload-controls";
|
|
8
8
|
export { useMultiUploadControls, type UseMultiUploadControlsOptions, type UseMultiUploadControlsReturn, } from "./use-multi-upload-controls";
|
|
9
|
-
export { useDownload, type UseDownloadOptions, type UseDownloadState, type UseDownloadReturn, } from "./use-download";
|
|
9
|
+
export { useDownload, type DownloadPhase, type DownloadHooks, type UseDownloadOptions, type UseDownloadState, type UseDownloadReturn, } from "./use-download";
|
|
10
|
+
export { useFetchDownload, type FetchDownloadPhase, type FetchDownloadProgress, type FetchDownloadHooks, type UseFetchDownloadOptions, type UseFetchDownloadState, type UseFetchDownloadReturn, } from "./use-fetch-download";
|
|
10
11
|
export { useDelete, type UseDeleteOptions, type UseDeleteState, type UseDeleteReturn, } from "./use-delete";
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {validateFile}from'@better-s3/server';export{createPresignApi,validateFile}from'@better-s3/server';import {useState,useRef,useCallback}from'react';function Z(i){if(i===0)return "0 B";let p=["B","KB","MB","GB","TB"],e=Math.floor(Math.log(i)/Math.log(1024));return `${(i/Math.pow(1024,e)).toFixed(e===0?0:1)} ${p[e]}`}async function M(i,p,e){let r;for(let s=0;s<=p;s++)try{return await i()}catch(u){if(u.name==="AbortError")throw u;if(r=u,s<p){let l=1e3*2**s;if(await new Promise(c=>setTimeout(c,l)),e?.aborted)throw new DOMException("Upload aborted","AbortError")}}throw r}function K(i,p,e,r){return new Promise((s,u)=>{let l=new XMLHttpRequest,c=()=>{l.abort(),u(new DOMException("Upload aborted","AbortError"));};r?.addEventListener("abort",c,{once:true}),l.upload.addEventListener("progress",t=>{t.lengthComputable&&e?.({loaded:t.loaded,total:t.total,percent:Math.round(t.loaded/t.total*100)});}),l.addEventListener("load",()=>{if(r?.removeEventListener("abort",c),l.status>=200&&l.status<300){e?.({loaded:i.size,total:i.size,percent:100});let t=l.getResponseHeader("ETag")?.replace(/"/g,"");s(t??void 0);}else u(new Error(`Upload failed: ${l.status} ${l.statusText}`));}),l.addEventListener("error",()=>{r?.removeEventListener("abort",c),u(new Error("Upload failed: network error"));}),l.addEventListener("abort",()=>{r?.removeEventListener("abort",c),u(new DOMException("Upload aborted","AbortError"));}),l.open("PUT",p),l.setRequestHeader("Content-Type",i.type||"application/octet-stream"),l.send(i);})}function q(i,p,e,r,s,u){return new Promise((l,c)=>{let t=new XMLHttpRequest,b=()=>{t.abort(),c(new DOMException("Upload aborted","AbortError"));};u?.addEventListener("abort",b,{once:true}),t.upload.addEventListener("progress",U=>{U.lengthComputable&&(e.bytes=U.loaded,s());}),t.addEventListener("load",()=>{if(u?.removeEventListener("abort",b),t.status>=200&&t.status<300){e.bytes=i.size,s();let U=t.getResponseHeader("ETag")??"";l(U);}else c(new Error(`Part upload failed: ${t.status}`));}),t.addEventListener("error",()=>{u?.removeEventListener("abort",b),c(new Error("Part upload failed: network error"));}),t.addEventListener("abort",()=>{u?.removeEventListener("abort",b),c(new DOMException("Upload aborted","AbortError"));}),t.open("PUT",p),t.send(i);})}async function $(i,p,e,r,s,u,l,c){let t=c?.contentType??p.type,{uploadId:b,key:U}=await i.multipart.init({key:e,contentType:t,metadata:c?.metadata,bucket:c?.bucket}),o=Math.ceil(p.size/r),g=[],n=Array.from({length:o},()=>({bytes:0})),m=()=>{let d=n.reduce((a,y)=>a+y.bytes,0);u?.({loaded:d,total:p.size,percent:Math.round(d/p.size*100)});};try{for(let d=0;d<o;d+=s){if(l?.aborted)throw new DOMException("Upload aborted","AbortError");let a=Math.min(d+s,o),y=[];for(let f=d;f<a;f++){let P=f*r,R=Math.min(P+r,p.size),E=p.slice(P,R),h=f+1;y.push(M(async()=>{let{presignedUrl:v}=await i.multipart.signPart({key:U,uploadId:b,partNumber:h,bucket:c?.bucket});n[f].bytes=0;let k=await q(E,v,n[f],p.size,m,l);return {partNumber:h,eTag:k.replace(/"/g,"")}},3,l));}let w=await Promise.all(y);g.push(...w);}g.sort((d,a)=>d.partNumber-a.partNumber),await i.multipart.complete({key:U,uploadId:b,parts:g,bucket:c?.bucket}),u?.({loaded:p.size,total:p.size,percent:100});}catch(d){throw i.multipart.abort({key:U,uploadId:b,bucket:c?.bucket}).catch(()=>{}),d}}async function S(i,p,e,r={},s={},u,l){let c=r.multipartThreshold??52428800,t=r.multipart===true&&p.size>=c,b=r.concurrentParts??3,U=l?.contentType??p.type,o;return t?await $(i,p,e,10485760,b,s.onProgress,u,l):(o=await M(async()=>{let g=await i.upload({key:e,contentType:U,metadata:l?.metadata,bucket:l?.bucket});return K(p,g.url,s.onProgress,u)},3,u),await i.confirm({key:e,bucket:l?.bucket})),{key:e,eTag:o}}async function A(i,p,e={},r={},s,u){let l=p.map(g=>({...g,status:"pending",progress:{loaded:0,total:g.file.size,percent:0},result:null,error:null})),c=()=>{let g=l.reduce((m,d)=>m+d.progress.loaded,0),n=l.reduce((m,d)=>m+d.progress.total,0);r.onTotalProgress?.({loaded:g,total:n,percent:n>0?Math.round(g/n*100):0});},t=0,b=async()=>{for(;t<l.length;){if(s?.aborted)return;let g=t++,n=l[g];n.status="uploading";try{let m=await S(i,n.file,n.objectKey,e,{onProgress:d=>{n.progress=d,r.onFileProgress?.(n.id,d),c();}},s,u?.(n.file));n.status="success",n.result=m,n.progress={loaded:n.file.size,total:n.file.size,percent:100},r.onFileSuccess?.(n.id,m),c();}catch(m){if(m.name==="AbortError"){n.status="error",n.error="Upload cancelled";return}let d=m instanceof Error?m.message:"Upload failed";n.status="error",n.error=d,r.onFileError?.(n.id,d),c();}}},U=e.concurrentFiles??2,o=Array.from({length:Math.min(U,p.length)},()=>b());return await Promise.all(o),l}var W={loaded:0,total:0,percent:0},F={phase:"idle",progress:W,error:null,result:null,fileName:null,fileSize:null};function O(i){let[p,e]=useState(F),r=useRef(i);r.current=i;let s=useRef(null),u=useCallback(async(t,b,U)=>{e({...F,phase:"validating",fileName:t.name,fileSize:t.size});let o=r.current,g=validateFile(t,{accept:o.accept,maxFileSize:o.maxFileSize});if(g){e(m=>({...m,phase:"error",error:g})),o.onError?.(t,new Error(g),"validating");return}if(o.beforeUpload&&!await o.beforeUpload(t)){e(d=>({...d,phase:"error",error:"Upload blocked by beforeUpload hook"})),o.onError?.(t,new Error("blocked"),"validating");return}e(m=>({...m,phase:"uploading"})),o.onUploadStart?.(t,b);let n=new AbortController;s.current=n;try{let m=await S(o.presignApi,t,b,{multipart:o.multipart,multipartThreshold:o.multipartThreshold,concurrentParts:o.concurrentParts},{onProgress:d=>{e(a=>({...a,progress:d})),o.onProgress?.(t,d);}},n.signal,U);e(d=>({...d,phase:"success",result:m,progress:{loaded:t.size,total:t.size,percent:100}})),await o.onSuccess?.(t,m);}catch(m){if(m.name==="AbortError"){o.onCancel?.(t),e(F);return}let d=m instanceof Error?m.message:"Upload failed";e(a=>({...a,phase:"error",error:d})),o.onError?.(t,m,"uploading");}finally{s.current=null;}},[]),l=useCallback(()=>{s.current?.abort(),e(F);},[]),c=useCallback(()=>{s.current?.abort(),e(F);},[]);return {...p,upload:u,cancel:l,reset:c}}var oe={loaded:0,total:0,percent:0},T={phase:"idle",files:[],totalProgress:oe,error:null},re=0;function se(){return `file-${++re}`}function z(i){let[p,e]=useState(T),r=useRef(i);r.current=i;let s=useRef(null),u=useRef(new Map),l=useCallback(async(b,U)=>{let o=r.current,g=[],n=[],m=new Map;if(e(a=>({...a,phase:"validating",error:null})),o.maxFiles&&b.length>o.maxFiles){let a=`Too many files. Maximum is ${o.maxFiles}.`;e(y=>({...y,phase:"error",error:a})),o.onError?.(new Error(a));return}for(let a of b){let y=validateFile(a,{accept:o.accept,maxFileSize:o.maxFileSize});if(y){let w=`${a.name}: ${y}`;e(f=>({...f,phase:"error",error:w})),o.onError?.(new Error(w));return}}if(o.beforeUpload&&!await o.beforeUpload(b)){e(y=>({...y,phase:"error",error:"Upload blocked by beforeUpload hook"})),o.onError?.(new Error("blocked"));return}for(let a of b){let y=se(),w=U(a);g.push({id:y,file:a,objectKey:w}),m.set(y,a),n.push({id:y,fileName:a.name,fileSize:a.size,status:"pending",progress:{loaded:0,total:a.size,percent:0},error:null});}u.current=m,e({phase:"uploading",files:n,totalProgress:{loaded:0,total:b.reduce((a,y)=>a+y.size,0),percent:0},error:null}),o.onUploadStart?.(b);let d=new AbortController;s.current=d;try{let a=await A(o.presignApi,g,{multipart:o.multipart,multipartThreshold:o.multipartThreshold,concurrentParts:o.concurrentParts,concurrentFiles:o.concurrentFiles},{onFileProgress:(f,P)=>{e(E=>({...E,files:E.files.map(h=>h.id===f?{...h,status:"uploading",progress:P}:h)}));let R=m.get(f);R&&o.onFileProgress?.(R,P);},onFileSuccess:(f,P)=>{e(E=>({...E,files:E.files.map(h=>h.id===f?{...h,status:"success",progress:{loaded:h.fileSize,total:h.fileSize,percent:100}}:h)}));let R=m.get(f);R&&o.onFileSuccess?.(R,P);},onFileError:(f,P)=>{e(E=>({...E,files:E.files.map(h=>h.id===f?{...h,status:"error",error:P}:h)}));let R=m.get(f);R&&o.onFileError?.(R,P);},onTotalProgress:f=>{e(P=>({...P,totalProgress:f})),o.onProgress?.(f);}},d.signal,f=>{let P=o.getUploadOptions?.(f);return o.uploadOptions?{...o.uploadOptions,...P}:P??{}}),y=a.some(f=>f.status==="error"),w=a.filter(f=>f.result!==null).map(f=>f.result);e(f=>({...f,phase:y?"error":"success",error:y?`${a.filter(P=>P.status==="error").length} file(s) failed`:null,totalProgress:y?f.totalProgress:{loaded:f.totalProgress.total,total:f.totalProgress.total,percent:100}})),y||await o.onSuccess?.(w);}catch(a){if(a.name==="AbortError"){o.onCancel?.(),e(T);return}let y=a instanceof Error?a.message:"Upload failed";e(w=>({...w,phase:"error",error:y})),o.onError?.(a);}finally{s.current=null;}},[]),c=useCallback(()=>{s.current?.abort(),e(T);},[]),t=useCallback(()=>{s.current?.abort(),e(T);},[]);return {...p,upload:l,cancel:c,reset:t}}function ae(i){let{objectKey:p,getUploadOptions:e,...r}=i,s=O(r),u=useRef(null),[l,c]=useState(null),t=g=>typeof p=="function"?p(g):p,b=async g=>{let n=g?.[0];n&&(c({name:n.name,size:n.size}),await s.upload(n,t(n),e?.(n)));},U=()=>u.current?.click(),o=s.phase==="uploading";return {phase:s.phase,progress:s.progress,error:s.error,fileInfo:l,isUploading:o,handleFiles:b,openFilePicker:U,cancel:s.cancel,reset:()=>{s.reset(),c(null);},inputProps:{ref:u,type:"file",accept:r.accept?.join(","),hidden:true,onChange:g=>{b(g.target.files),g.target.value="";}},dropHandlers:{onDragOver:g=>{g.preventDefault(),g.stopPropagation();},onDrop:g=>{g.preventDefault(),g.stopPropagation(),o||b(g.dataTransfer.files);}}}}function pe(i){let{objectKey:p,...e}=i,r=z(e),s=useRef(null),u=async t=>{t?.length&&await r.upload(Array.from(t),p);},l=()=>s.current?.click(),c=r.phase==="uploading";return {phase:r.phase,files:r.files,totalProgress:r.totalProgress,error:r.error,isUploading:c,handleFiles:u,openFilePicker:l,cancel:r.cancel,reset:r.reset,inputProps:{ref:s,type:"file",multiple:true,accept:e.accept?.join(","),hidden:true,onChange:t=>{u(t.target.files),t.target.value="";}},dropHandlers:{onDragOver:t=>{t.preventDefault(),t.stopPropagation();},onDrop:t=>{t.preventDefault(),t.stopPropagation(),c||u(t.dataTransfer.files);}}}}var G={loaded:0,total:0,percent:0},D={phase:"idle",progress:G,error:null,fileName:null,fileSize:null};function ue(i){let[p,e]=useState(D),r=useRef(i);r.current=i;let s=useRef(null),u=useCallback(async(t,b)=>{let U=b??t.split("/").pop()??t,o=r.current,g=o.mode??"native";if(o.beforeDownload&&!await o.beforeDownload(t)){e(m=>({...m,phase:"error",error:"Download blocked by beforeDownload hook"})),o.onError?.(t,new Error("blocked"),"presigning");return}e({phase:"presigning",progress:G,error:null,fileName:U,fileSize:null});try{let{url:n}=await o.presignApi.download(t,{fileName:U,bucket:o.bucket});if(g==="native"){let h=document.createElement("a");h.href=n,h.download=U,h.click(),e(v=>({...v,phase:"success"})),await o.onSuccess?.(t),e(D);return}e(h=>({...h,phase:"downloading"})),o.onDownloadStart?.(t);let m=new AbortController;s.current=m;let d=await fetch(n,{signal:m.signal});if(!d.ok)throw new Error(`Download failed: ${d.statusText}`);let a=Number(d.headers.get("content-length")||0);e(h=>({...h,fileSize:a||null}));let y=d.body?.getReader();if(!y)throw new Error("ReadableStream not supported");let w=[],f=0;for(;;){let{done:h,value:v}=await y.read();if(h)break;w.push(v),f+=v.byteLength;let k=a>0?Math.round(f/a*100):0,_={loaded:f,total:a,percent:k};e(Y=>({...Y,progress:_})),o.onProgress?.(t,_);}let P=new Blob(w),R=URL.createObjectURL(P),E=document.createElement("a");E.href=R,E.download=U,E.click(),URL.revokeObjectURL(R),e(h=>({...h,phase:"success",fileSize:P.size,progress:{loaded:P.size,total:P.size,percent:100}})),await o.onSuccess?.(t);}catch(n){if(n.name==="AbortError"){o.onCancel?.(t),e(D);return}let m=n instanceof Error?n.message:"Download failed";e(d=>({...d,phase:"error",error:m})),o.onError?.(t,n,"downloading");}finally{s.current=null;}},[]),l=useCallback(()=>{s.current?.abort(),e(D);},[]),c=useCallback(()=>{s.current?.abort(),e(D);},[]);return {...p,download:u,cancel:l,reset:c}}var N={phase:"idle",error:null};function ge(i){let[p,e]=useState(N),[r,s]=useState(null),u=useRef(i);u.current=i;let l=useCallback(U=>{s(U),e({phase:"confirming",error:null});},[]),c=useCallback(async()=>{if(!r)return;let U=u.current;if(U.beforeDelete&&!await U.beforeDelete(r)){e({phase:"error",error:"Delete blocked by beforeDelete hook"}),U.onError?.(r,new Error("blocked"),"confirming"),s(null);return}e({phase:"deleting",error:null}),U.onDeleteStart?.(r);try{await U.presignApi.delete(r,{bucket:U.bucket}),e({phase:"success",error:null}),await U.onSuccess?.(r),s(null);}catch(o){let g=o instanceof Error?o.message:"Delete failed";e({phase:"error",error:g}),U.onError?.(r,o,"deleting");}},[r]),t=useCallback(()=>{s(null),e(N);},[]),b=useCallback(()=>{s(null),e(N);},[]);return {...p,pendingKey:r,requestDelete:l,confirmDelete:c,cancelDelete:t,reset:b}}export{Z as formatFileSize,S as uploadFile,A as uploadFiles,ge as useDelete,ue as useDownload,z as useMultiUpload,pe as useMultiUploadControls,O as useUpload,ae as useUploadControls};//# sourceMappingURL=index.js.map
|
|
1
|
+
import {validateFile}from'@better-s3/server';export{createPresignApi,validateFile}from'@better-s3/server';import {useState,useRef,useCallback}from'react';function J(i){if(i===0)return "0 B";let d=["B","KB","MB","GB","TB"],e=Math.floor(Math.log(i)/Math.log(1024));return `${(i/Math.pow(1024,e)).toFixed(e===0?0:1)} ${d[e]}`}async function S(i,d,e){let r;for(let n=0;n<=d;n++)try{return await i()}catch(c){if(c.name==="AbortError")throw c;if(r=c,n<d){let s=1e3*2**n;if(await new Promise(u=>setTimeout(u,s)),e?.aborted)throw new DOMException("Upload aborted","AbortError")}}throw r}function K(i,d,e,r){return new Promise((n,c)=>{let s=new XMLHttpRequest,u=()=>{s.abort(),c(new DOMException("Upload aborted","AbortError"));};r?.addEventListener("abort",u,{once:true}),s.upload.addEventListener("progress",t=>{t.lengthComputable&&e?.({loaded:t.loaded,total:t.total,percent:Math.round(t.loaded/t.total*100)});}),s.addEventListener("load",()=>{if(r?.removeEventListener("abort",u),s.status>=200&&s.status<300){e?.({loaded:i.size,total:i.size,percent:100});let t=s.getResponseHeader("ETag")?.replace(/"/g,"");n(t??void 0);}else c(new Error(`Upload failed: ${s.status} ${s.statusText}`));}),s.addEventListener("error",()=>{r?.removeEventListener("abort",u),c(new Error("Upload failed: network error"));}),s.addEventListener("abort",()=>{r?.removeEventListener("abort",u),c(new DOMException("Upload aborted","AbortError"));}),s.open("PUT",d),s.setRequestHeader("Content-Type",i.type||"application/octet-stream"),s.send(i);})}function $(i,d,e,r,n,c){return new Promise((s,u)=>{let t=new XMLHttpRequest,f=()=>{t.abort(),u(new DOMException("Upload aborted","AbortError"));};c?.addEventListener("abort",f,{once:true}),t.upload.addEventListener("progress",g=>{g.lengthComputable&&(e.bytes=g.loaded,n());}),t.addEventListener("load",()=>{if(c?.removeEventListener("abort",f),t.status>=200&&t.status<300){e.bytes=i.size,n();let g=t.getResponseHeader("ETag")??"";s(g);}else u(new Error(`Part upload failed: ${t.status}`));}),t.addEventListener("error",()=>{c?.removeEventListener("abort",f),u(new Error("Part upload failed: network error"));}),t.addEventListener("abort",()=>{c?.removeEventListener("abort",f),u(new DOMException("Upload aborted","AbortError"));}),t.open("PUT",d),t.send(i);})}async function q(i,d,e,r,n,c,s,u){let t=u?.contentType??d.type,{uploadId:f,key:g}=await i.multipart.init({key:e,contentType:t,metadata:u?.metadata,bucket:u?.bucket}),o=Math.ceil(d.size/r),a=[],l=Array.from({length:o},()=>({bytes:0})),p=()=>{let U=l.reduce((m,h)=>m+h.bytes,0);c?.({loaded:U,total:d.size,percent:Math.round(U/d.size*100)});};try{for(let U=0;U<o;U+=n){if(s?.aborted)throw new DOMException("Upload aborted","AbortError");let m=Math.min(U+n,o),h=[];for(let b=U;b<m;b++){let w=b*r,D=Math.min(w+r,d.size),P=d.slice(w,D),y=b+1;h.push(S(async()=>{let{presignedUrl:T}=await i.multipart.signPart({key:g,uploadId:f,partNumber:y,bucket:u?.bucket});l[b].bytes=0;let v=await $(P,T,l[b],d.size,p,s);return {partNumber:y,eTag:v.replace(/"/g,"")}},3,s));}let E=await Promise.all(h);a.push(...E);}a.sort((U,m)=>U.partNumber-m.partNumber),await i.multipart.complete({key:g,uploadId:f,parts:a,bucket:u?.bucket}),c?.({loaded:d.size,total:d.size,percent:100});}catch(U){throw i.multipart.abort({key:g,uploadId:f,bucket:u?.bucket}).catch(()=>{}),U}}async function F(i,d,e,r={},n={},c,s){let u=r.multipartThreshold??52428800,t=r.multipart===true&&d.size>=u,f=r.concurrentParts??3,g=s?.contentType??d.type,o;return t?await q(i,d,e,10485760,f,n.onProgress,c,s):(o=await S(async()=>{let a=await i.upload({key:e,contentType:g,metadata:s?.metadata,bucket:s?.bucket});return K(d,a.url,n.onProgress,c)},3,c),await i.confirm({key:e,bucket:s?.bucket})),{key:e,eTag:o}}async function M(i,d,e={},r={},n,c){let s=d.map(a=>({...a,status:"pending",progress:{loaded:0,total:a.file.size,percent:0},result:null,error:null})),u=()=>{let a=s.reduce((p,U)=>p+U.progress.loaded,0),l=s.reduce((p,U)=>p+U.progress.total,0);r.onTotalProgress?.({loaded:a,total:l,percent:l>0?Math.round(a/l*100):0});},t=0,f=async()=>{for(;t<s.length;){if(n?.aborted)return;let a=t++,l=s[a];l.status="uploading";try{let p=await F(i,l.file,l.objectKey,e,{onProgress:U=>{l.progress=U,r.onFileProgress?.(l.id,U),u();}},n,c?.(l.file));l.status="success",l.result=p,l.progress={loaded:l.file.size,total:l.file.size,percent:100},r.onFileSuccess?.(l.id,p),u();}catch(p){if(p.name==="AbortError"){l.status="error",l.error="Upload cancelled";return}let U=p instanceof Error?p.message:"Upload failed";l.status="error",l.error=U,r.onFileError?.(l.id,U),u();}}},g=e.concurrentFiles??2,o=Array.from({length:Math.min(g,d.length)},()=>f());return await Promise.all(o),s}var ee={loaded:0,total:0,percent:0},R={phase:"idle",progress:ee,error:null,result:null,fileName:null,fileSize:null};function O(i){let[d,e]=useState(R),r=useRef(i);r.current=i;let n=useRef(null),c=useCallback(async(t,f,g)=>{e({...R,phase:"validating",fileName:t.name,fileSize:t.size});let o=r.current,a=validateFile(t,{accept:o.accept,maxFileSize:o.maxFileSize});if(a){e(p=>({...p,phase:"error",error:a})),o.onError?.(t,new Error(a),"validating");return}if(o.beforeUpload&&!await o.beforeUpload(t)){e(U=>({...U,phase:"error",error:"Upload blocked by beforeUpload hook"})),o.onError?.(t,new Error("blocked"),"validating");return}e(p=>({...p,phase:"uploading"})),o.onUploadStart?.(t,f);let l=new AbortController;n.current=l;try{let p=await F(o.presignApi,t,f,{multipart:o.multipart,multipartThreshold:o.multipartThreshold,concurrentParts:o.concurrentParts},{onProgress:U=>{e(m=>({...m,progress:U})),o.onProgress?.(t,U);}},l.signal,g);e(U=>({...U,phase:"success",result:p,progress:{loaded:t.size,total:t.size,percent:100}})),await o.onSuccess?.(t,p);}catch(p){if(p.name==="AbortError"){o.onCancel?.(t),e(R);return}let U=p instanceof Error?p.message:"Upload failed";e(m=>({...m,phase:"error",error:U})),o.onError?.(t,p,"uploading");}finally{n.current=null;}},[]),s=useCallback(()=>{n.current?.abort(),e(R);},[]),u=useCallback(()=>{n.current?.abort(),e(R);},[]);return {...d,upload:c,cancel:s,reset:u}}var re={loaded:0,total:0,percent:0},k={phase:"idle",files:[],totalProgress:re,error:null},se=0;function ne(){return `file-${++se}`}function z(i){let[d,e]=useState(k),r=useRef(i);r.current=i;let n=useRef(null),c=useRef(new Map),s=useCallback(async(f,g)=>{let o=r.current,a=[],l=[],p=new Map;if(e(m=>({...m,phase:"validating",error:null})),o.maxFiles&&f.length>o.maxFiles){let m=`Too many files. Maximum is ${o.maxFiles}.`;e(h=>({...h,phase:"error",error:m})),o.onError?.(new Error(m));return}for(let m of f){let h=validateFile(m,{accept:o.accept,maxFileSize:o.maxFileSize});if(h){let E=`${m.name}: ${h}`;e(b=>({...b,phase:"error",error:E})),o.onError?.(new Error(E));return}}if(o.beforeUpload&&!await o.beforeUpload(f)){e(h=>({...h,phase:"error",error:"Upload blocked by beforeUpload hook"})),o.onError?.(new Error("blocked"));return}for(let m of f){let h=ne(),E=g(m);a.push({id:h,file:m,objectKey:E}),p.set(h,m),l.push({id:h,fileName:m.name,fileSize:m.size,status:"pending",progress:{loaded:0,total:m.size,percent:0},error:null});}c.current=p,e({phase:"uploading",files:l,totalProgress:{loaded:0,total:f.reduce((m,h)=>m+h.size,0),percent:0},error:null}),o.onUploadStart?.(f);let U=new AbortController;n.current=U;try{let m=await M(o.presignApi,a,{multipart:o.multipart,multipartThreshold:o.multipartThreshold,concurrentParts:o.concurrentParts,concurrentFiles:o.concurrentFiles},{onFileProgress:(b,w)=>{e(P=>({...P,files:P.files.map(y=>y.id===b?{...y,status:"uploading",progress:w}:y)}));let D=p.get(b);D&&o.onFileProgress?.(D,w);},onFileSuccess:(b,w)=>{e(P=>({...P,files:P.files.map(y=>y.id===b?{...y,status:"success",progress:{loaded:y.fileSize,total:y.fileSize,percent:100}}:y)}));let D=p.get(b);D&&o.onFileSuccess?.(D,w);},onFileError:(b,w)=>{e(P=>({...P,files:P.files.map(y=>y.id===b?{...y,status:"error",error:w}:y)}));let D=p.get(b);D&&o.onFileError?.(D,w);},onTotalProgress:b=>{e(w=>({...w,totalProgress:b})),o.onProgress?.(b);}},U.signal,b=>{let w=o.getUploadOptions?.(b);return o.uploadOptions?{...o.uploadOptions,...w}:w??{}}),h=m.some(b=>b.status==="error"),E=m.filter(b=>b.result!==null).map(b=>b.result);e(b=>({...b,phase:h?"error":"success",error:h?`${m.filter(w=>w.status==="error").length} file(s) failed`:null,totalProgress:h?b.totalProgress:{loaded:b.totalProgress.total,total:b.totalProgress.total,percent:100}})),h||await o.onSuccess?.(E);}catch(m){if(m.name==="AbortError"){o.onCancel?.(),e(k);return}let h=m instanceof Error?m.message:"Upload failed";e(E=>({...E,phase:"error",error:h})),o.onError?.(m);}finally{n.current=null;}},[]),u=useCallback(()=>{n.current?.abort(),e(k);},[]),t=useCallback(()=>{n.current?.abort(),e(k);},[]);return {...d,upload:s,cancel:u,reset:t}}function ie(i){let{objectKey:d,getUploadOptions:e,...r}=i,n=O(r),c=useRef(null),[s,u]=useState(null),t=a=>typeof d=="function"?d(a):d,f=async a=>{let l=a?.[0];l&&(u({name:l.name,size:l.size}),await n.upload(l,t(l),e?.(l)));},g=()=>c.current?.click(),o=n.phase==="uploading";return {phase:n.phase,progress:n.progress,error:n.error,fileInfo:s,isUploading:o,handleFiles:f,openFilePicker:g,cancel:n.cancel,reset:()=>{n.reset(),u(null);},inputProps:{ref:c,type:"file",accept:r.accept?.join(","),hidden:true,onChange:a=>{f(a.target.files),a.target.value="";}},dropHandlers:{onDragOver:a=>{a.preventDefault(),a.stopPropagation();},onDrop:a=>{a.preventDefault(),a.stopPropagation(),o||f(a.dataTransfer.files);}}}}function de(i){let{objectKey:d,...e}=i,r=z(e),n=useRef(null),c=async t=>{t?.length&&await r.upload(Array.from(t),d);},s=()=>n.current?.click(),u=r.phase==="uploading";return {phase:r.phase,files:r.files,totalProgress:r.totalProgress,error:r.error,isUploading:u,handleFiles:c,openFilePicker:s,cancel:r.cancel,reset:r.reset,inputProps:{ref:n,type:"file",multiple:true,accept:e.accept?.join(","),hidden:true,onChange:t=>{c(t.target.files),t.target.value="";}},dropHandlers:{onDragOver:t=>{t.preventDefault(),t.stopPropagation();},onDrop:t=>{t.preventDefault(),t.stopPropagation(),u||c(t.dataTransfer.files);}}}}var N={phase:"idle",error:null,fileName:null};function ge(i){let[d,e]=useState(N),r=useRef(i);r.current=i;let n=useCallback(async(s,u)=>{let t=u??s.split("/").pop()??s,f=r.current;if(f.beforeDownload&&!await f.beforeDownload(s)){e({phase:"error",error:"Download blocked by beforeDownload hook",fileName:t}),f.onError?.(s,new Error("blocked"));return}e({phase:"downloading",error:null,fileName:t});try{let{url:g}=await f.presignApi.download(s,{fileName:t,bucket:f.bucket}),o=await fetch(g);if(!o.ok)throw new Error(o.status===404?"File not found":`Download failed (${o.status})`);let a=await o.blob(),l=URL.createObjectURL(a),p=document.createElement("a");p.href=l,p.download=t,p.click(),URL.revokeObjectURL(l),e({phase:"success",error:null,fileName:t}),await f.onSuccess?.(s),e(N);}catch(g){let o=g instanceof Error?g.message:"Download failed";e({phase:"error",error:o,fileName:t}),f.onError?.(s,g);}},[]),c=useCallback(()=>{e(N);},[]);return {...d,download:n,reset:c}}var X={loaded:0,total:0,percent:0},x={phase:"idle",progress:X,error:null,fileName:null,fileSize:null};function fe(i){let[d,e]=useState(x),r=useRef(i);r.current=i;let n=useRef(null),c=useCallback(async(t,f)=>{let g=f??t.split("/").pop()??t,o=r.current;if(o.beforeDownload&&!await o.beforeDownload(t)){e(l=>({...l,phase:"error",error:"Download blocked by beforeDownload hook"})),o.onError?.(t,new Error("blocked"),"presigning");return}e({phase:"presigning",progress:X,error:null,fileName:g,fileSize:null});try{let{url:a}=await o.presignApi.download(t,{fileName:g,bucket:o.bucket});e(P=>({...P,phase:"downloading"})),o.onDownloadStart?.(t);let l=new AbortController;n.current=l;let p=await fetch(a,{signal:l.signal});if(!p.ok)throw new Error(p.status===404?"File not found":`Download failed (${p.status})`);let U=Number(p.headers.get("content-length")||0);e(P=>({...P,fileSize:U||null}));let m=p.body?.getReader();if(!m)throw new Error("ReadableStream not supported");let h=[],E=0;for(;;){let{done:P,value:y}=await m.read();if(P)break;h.push(y),E+=y.byteLength;let T=U>0?Math.round(E/U*100):0,v={loaded:E,total:U,percent:T};e(Z=>({...Z,progress:v})),o.onProgress?.(t,v);}let b=new Blob(h),w=URL.createObjectURL(b),D=document.createElement("a");D.href=w,D.download=g,D.click(),URL.revokeObjectURL(w),e(P=>({...P,phase:"success",fileSize:b.size,progress:{loaded:b.size,total:b.size,percent:100}})),await o.onSuccess?.(t);}catch(a){if(a.name==="AbortError"){o.onCancel?.(t),e(x);return}let l=a instanceof Error?a.message:"Download failed";e(p=>({...p,phase:"error",error:l})),o.onError?.(t,a,"downloading");}finally{n.current=null;}},[]),s=useCallback(()=>{n.current?.abort(),e(x);},[]),u=useCallback(()=>{n.current?.abort(),e(x);},[]);return {...d,download:c,cancel:s,reset:u}}var _={phase:"idle",error:null};function be(i){let[d,e]=useState(_),[r,n]=useState(null),c=useRef(i);c.current=i;let s=useCallback(g=>{n(g),e({phase:"confirming",error:null});},[]),u=useCallback(async()=>{if(!r)return;let g=c.current;if(g.beforeDelete&&!await g.beforeDelete(r)){e({phase:"error",error:"Delete blocked by beforeDelete hook"}),g.onError?.(r,new Error("blocked"),"confirming"),n(null);return}e({phase:"deleting",error:null}),g.onDeleteStart?.(r);try{await g.presignApi.delete(r,{bucket:g.bucket}),e({phase:"success",error:null}),await g.onSuccess?.(r),n(null);}catch(o){let a=o instanceof Error?o.message:"Delete failed";e({phase:"error",error:a}),g.onError?.(r,o,"deleting");}},[r]),t=useCallback(()=>{n(null),e(_);},[]),f=useCallback(()=>{n(null),e(_);},[]);return {...d,pendingKey:r,requestDelete:s,confirmDelete:u,cancelDelete:t,reset:f}}export{J as formatFileSize,F as uploadFile,M as uploadFiles,be as useDelete,ge as useDownload,fe as useFetchDownload,z as useMultiUpload,de as useMultiUploadControls,O as useUpload,ie as useUploadControls};//# sourceMappingURL=index.js.map
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/helpers.ts","../src/upload/retry.ts","../src/upload/simple.ts","../src/upload/part.ts","../src/upload/multipart.ts","../src/upload/upload-file.ts","../src/upload/upload-files.ts","../src/use-upload.ts","../src/use-multi-upload.ts","../src/use-upload-controls.ts","../src/use-multi-upload-controls.ts","../src/use-download.ts","../src/use-delete.ts"],"names":["formatFileSize","bytes","units","i","withRetry","fn","retries","signal","lastError","attempt","err","delay","r","uploadSimple","file","presignedUrl","onProgress","resolve","reject","xhr","onAbort","e","eTag","uploadPart","blob","partLoaded","totalSize","reportProgress","uploadMultipart","presignApi","objectKey","partSize","concurrentParts","requestOptions","contentType","uploadId","key","totalParts","parts","partProgress","loaded","sum","p","batchStart","batchEnd","batch","start","end","partNumber","batchResults","a","b","uploadFile","config","callbacks","threshold","useMultipart","presign","uploadFiles","items","getRequestOptions","results","item","reportTotalProgress","total","nextIndex","processNext","idx","result","progress","message","concurrentFiles","workers","INITIAL_PROGRESS","INITIAL_STATE","useUpload","options","state","setState","useState","optionsRef","useRef","abortRef","upload","useCallback","opts","validationError","validateFile","s","controller","cancel","reset","nextId","generateId","useMultiUpload","fileMapRef","files","resolveKey","fileStates","fileMap","msg","id","f","error","perFile","hasErrors","successResults","useUploadControls","getUploadOptions","hookOptions","ctx","inputRef","fileInfo","setFileInfo","handleFiles","openFilePicker","isUploading","useMultiUploadControls","useDownload","download","downloadName","name","mode","url","anchor","res","contentLength","reader","chunks","done","value","percent","blobUrl","useDelete","pendingKey","setPendingKey","requestDelete","confirmDelete","cancelDelete"],"mappings":"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","file":"index.js","sourcesContent":["export function formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n const size = bytes / Math.pow(1024, i);\n return `${size.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;\n}\n","import { RETRY_BASE_DELAY } from \"./constants\";\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n retries: number,\n signal?: AbortSignal,\n): Promise<T> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n if ((err as Error).name === \"AbortError\") throw err;\n lastError = err;\n if (attempt < retries) {\n const delay = RETRY_BASE_DELAY * 2 ** attempt;\n await new Promise((r) => setTimeout(r, delay));\n if (signal?.aborted)\n throw new DOMException(\"Upload aborted\", \"AbortError\");\n }\n }\n }\n throw lastError;\n}\n","import type { UploadProgress } from \"../types\";\n\nexport function uploadSimple(\n file: File,\n presignedUrl: string,\n onProgress?: (progress: UploadProgress) => void,\n signal?: AbortSignal,\n): Promise<string | undefined> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n const onAbort = () => {\n xhr.abort();\n reject(new DOMException(\"Upload aborted\", \"AbortError\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n xhr.upload.addEventListener(\"progress\", (e) => {\n if (e.lengthComputable) {\n onProgress?.({\n loaded: e.loaded,\n total: e.total,\n percent: Math.round((e.loaded / e.total) * 100),\n });\n }\n });\n\n xhr.addEventListener(\"load\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n if (xhr.status >= 200 && xhr.status < 300) {\n onProgress?.({ loaded: file.size, total: file.size, percent: 100 });\n const eTag = xhr.getResponseHeader(\"ETag\")?.replace(/\"/g, \"\");\n resolve(eTag ?? undefined);\n } else {\n reject(new Error(`Upload failed: ${xhr.status} ${xhr.statusText}`));\n }\n });\n\n xhr.addEventListener(\"error\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(new Error(\"Upload failed: network error\"));\n });\n\n xhr.addEventListener(\"abort\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(new DOMException(\"Upload aborted\", \"AbortError\"));\n });\n\n xhr.open(\"PUT\", presignedUrl);\n xhr.setRequestHeader(\n \"Content-Type\",\n file.type || \"application/octet-stream\",\n );\n xhr.send(file);\n });\n}\n","export function uploadPart(\n blob: Blob,\n presignedUrl: string,\n partLoaded: { bytes: number },\n totalSize: number,\n reportProgress: () => void,\n signal?: AbortSignal,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n const onAbort = () => {\n xhr.abort();\n reject(new DOMException(\"Upload aborted\", \"AbortError\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n xhr.upload.addEventListener(\"progress\", (e) => {\n if (e.lengthComputable) {\n partLoaded.bytes = e.loaded;\n reportProgress();\n }\n });\n\n xhr.addEventListener(\"load\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n if (xhr.status >= 200 && xhr.status < 300) {\n partLoaded.bytes = blob.size;\n reportProgress();\n const eTag = xhr.getResponseHeader(\"ETag\") ?? \"\";\n resolve(eTag);\n } else {\n reject(new Error(`Part upload failed: ${xhr.status}`));\n }\n });\n\n xhr.addEventListener(\"error\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(new Error(\"Part upload failed: network error\"));\n });\n\n xhr.addEventListener(\"abort\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(new DOMException(\"Upload aborted\", \"AbortError\"));\n });\n\n xhr.open(\"PUT\", presignedUrl);\n xhr.send(blob);\n });\n}\n","import type { UploadProgress, UploadRequestOptions } from \"../types\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport { MAX_RETRIES } from \"./constants\";\nimport { withRetry } from \"./retry\";\nimport { uploadPart } from \"./part\";\n\nexport async function uploadMultipart(\n presignApi: PresignApi,\n file: File,\n objectKey: string,\n partSize: number,\n concurrentParts: number,\n onProgress?: (progress: UploadProgress) => void,\n signal?: AbortSignal,\n requestOptions?: UploadRequestOptions,\n): Promise<void> {\n const contentType = requestOptions?.contentType ?? file.type;\n const { uploadId, key } = await presignApi.multipart.init({\n key: objectKey,\n contentType,\n metadata: requestOptions?.metadata,\n bucket: requestOptions?.bucket,\n });\n\n const totalParts = Math.ceil(file.size / partSize);\n const parts: Array<{ partNumber: number; eTag: string }> = [];\n\n const partProgress: Array<{ bytes: number }> = Array.from(\n { length: totalParts },\n () => ({ bytes: 0 }),\n );\n\n const reportProgress = () => {\n const loaded = partProgress.reduce((sum, p) => sum + p.bytes, 0);\n onProgress?.({\n loaded,\n total: file.size,\n percent: Math.round((loaded / file.size) * 100),\n });\n };\n\n try {\n for (\n let batchStart = 0;\n batchStart < totalParts;\n batchStart += concurrentParts\n ) {\n if (signal?.aborted) {\n throw new DOMException(\"Upload aborted\", \"AbortError\");\n }\n\n const batchEnd = Math.min(batchStart + concurrentParts, totalParts);\n const batch: Array<Promise<{ partNumber: number; eTag: string }>> = [];\n\n for (let i = batchStart; i < batchEnd; i++) {\n const start = i * partSize;\n const end = Math.min(start + partSize, file.size);\n const blob = file.slice(start, end);\n const partNumber = i + 1;\n\n batch.push(\n withRetry(\n async () => {\n const { presignedUrl } = await presignApi.multipart.signPart({\n key,\n uploadId,\n partNumber,\n bucket: requestOptions?.bucket,\n });\n\n partProgress[i].bytes = 0;\n\n const eTag = await uploadPart(\n blob,\n presignedUrl,\n partProgress[i],\n file.size,\n reportProgress,\n signal,\n );\n\n return { partNumber, eTag: eTag.replace(/\"/g, \"\") };\n },\n MAX_RETRIES,\n signal,\n ),\n );\n }\n\n const batchResults = await Promise.all(batch);\n parts.push(...batchResults);\n }\n\n parts.sort((a, b) => a.partNumber - b.partNumber);\n\n await presignApi.multipart.complete({\n key,\n uploadId,\n parts,\n bucket: requestOptions?.bucket,\n });\n onProgress?.({ loaded: file.size, total: file.size, percent: 100 });\n } catch (err) {\n presignApi.multipart\n .abort({ key, uploadId, bucket: requestOptions?.bucket })\n .catch(() => {});\n throw err;\n }\n}\n","import type {\n UploadConfig,\n UploadProgress,\n UploadResult,\n UploadRequestOptions,\n} from \"../types\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport {\n DEFAULT_MULTIPART_THRESHOLD,\n DEFAULT_CONCURRENT_PARTS,\n DEFAULT_PART_SIZE,\n MAX_RETRIES,\n} from \"./constants\";\nimport { withRetry } from \"./retry\";\nimport { uploadSimple } from \"./simple\";\nimport { uploadMultipart } from \"./multipart\";\n\nexport type UploadEngineCallbacks = {\n onProgress?: (progress: UploadProgress) => void;\n};\n\nexport async function uploadFile(\n presignApi: PresignApi,\n file: File,\n objectKey: string,\n config: UploadConfig = {},\n callbacks: UploadEngineCallbacks = {},\n signal?: AbortSignal,\n requestOptions?: UploadRequestOptions,\n): Promise<UploadResult> {\n const threshold = config.multipartThreshold ?? DEFAULT_MULTIPART_THRESHOLD;\n const useMultipart = config.multipart === true && file.size >= threshold;\n const concurrentParts = config.concurrentParts ?? DEFAULT_CONCURRENT_PARTS;\n const contentType = requestOptions?.contentType ?? file.type;\n\n let eTag: string | undefined;\n\n if (useMultipart) {\n await uploadMultipart(\n presignApi,\n file,\n objectKey,\n DEFAULT_PART_SIZE,\n concurrentParts,\n callbacks.onProgress,\n signal,\n requestOptions,\n );\n } else {\n eTag = await withRetry(\n async () => {\n const presign = await presignApi.upload({\n key: objectKey,\n contentType,\n metadata: requestOptions?.metadata,\n bucket: requestOptions?.bucket,\n });\n return uploadSimple(file, presign.url, callbacks.onProgress, signal);\n },\n MAX_RETRIES,\n signal,\n );\n\n await presignApi.confirm({\n key: objectKey,\n bucket: requestOptions?.bucket,\n });\n }\n\n return { key: objectKey, eTag };\n}\n","import type {\n UploadConfig,\n UploadProgress,\n UploadResult,\n UploadRequestOptions,\n} from \"../types\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport { DEFAULT_CONCURRENT_FILES } from \"./constants\";\nimport { uploadFile } from \"./upload-file\";\n\nexport type FileItemStatus = \"pending\" | \"uploading\" | \"success\" | \"error\";\n\nexport type FileItem = {\n id: string;\n file: File;\n objectKey: string;\n status: FileItemStatus;\n progress: UploadProgress;\n result: UploadResult | null;\n error: string | null;\n};\n\nexport type MultiUploadCallbacks = {\n onFileProgress?: (id: string, progress: UploadProgress) => void;\n onFileSuccess?: (id: string, result: UploadResult) => void;\n onFileError?: (id: string, error: string) => void;\n onTotalProgress?: (progress: UploadProgress) => void;\n};\n\nexport async function uploadFiles(\n presignApi: PresignApi,\n items: Array<{ id: string; file: File; objectKey: string }>,\n config: UploadConfig = {},\n callbacks: MultiUploadCallbacks = {},\n signal?: AbortSignal,\n getRequestOptions?: (file: File) => UploadRequestOptions,\n): Promise<FileItem[]> {\n const results: FileItem[] = items.map((item) => ({\n ...item,\n status: \"pending\" as FileItemStatus,\n progress: { loaded: 0, total: item.file.size, percent: 0 },\n result: null,\n error: null,\n }));\n\n const reportTotalProgress = () => {\n const loaded = results.reduce((sum, r) => sum + r.progress.loaded, 0);\n const total = results.reduce((sum, r) => sum + r.progress.total, 0);\n callbacks.onTotalProgress?.({\n loaded,\n total,\n percent: total > 0 ? Math.round((loaded / total) * 100) : 0,\n });\n };\n\n let nextIndex = 0;\n\n const processNext = async (): Promise<void> => {\n while (nextIndex < results.length) {\n if (signal?.aborted) return;\n const idx = nextIndex++;\n const item = results[idx];\n\n item.status = \"uploading\";\n\n try {\n const result = await uploadFile(\n presignApi,\n item.file,\n item.objectKey,\n config,\n {\n onProgress: (progress) => {\n item.progress = progress;\n callbacks.onFileProgress?.(item.id, progress);\n reportTotalProgress();\n },\n },\n signal,\n getRequestOptions?.(item.file),\n );\n item.status = \"success\";\n item.result = result;\n item.progress = {\n loaded: item.file.size,\n total: item.file.size,\n percent: 100,\n };\n callbacks.onFileSuccess?.(item.id, result);\n reportTotalProgress();\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n item.status = \"error\";\n item.error = \"Upload cancelled\";\n return;\n }\n const message = err instanceof Error ? err.message : \"Upload failed\";\n item.status = \"error\";\n item.error = message;\n callbacks.onFileError?.(item.id, message);\n reportTotalProgress();\n }\n }\n };\n\n const concurrentFiles = config.concurrentFiles ?? DEFAULT_CONCURRENT_FILES;\n const workers = Array.from(\n { length: Math.min(concurrentFiles, items.length) },\n () => processNext(),\n );\n await Promise.all(workers);\n\n return results;\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport { validateFile } from \"@better-s3/server\";\nimport type {\n UploadConfig,\n UploadHooks,\n UploadPhase,\n UploadProgress,\n UploadResult,\n UploadRequestOptions,\n} from \"./types\";\nimport { uploadFile } from \"./upload\";\n\nexport type UseUploadOptions = UploadConfig &\n UploadHooks & {\n presignApi: PresignApi;\n };\n\nexport type UseUploadState = {\n phase: UploadPhase;\n progress: UploadProgress;\n error: string | null;\n result: UploadResult | null;\n fileName: string | null;\n fileSize: number | null;\n};\n\nexport type UseUploadReturn = UseUploadState & {\n upload: (\n file: File,\n objectKey: string,\n requestOptions?: UploadRequestOptions,\n ) => Promise<void>;\n cancel: () => void;\n reset: () => void;\n};\n\nconst INITIAL_PROGRESS: UploadProgress = { loaded: 0, total: 0, percent: 0 };\n\nconst INITIAL_STATE: UseUploadState = {\n phase: \"idle\",\n progress: INITIAL_PROGRESS,\n error: null,\n result: null,\n fileName: null,\n fileSize: null,\n};\n\nexport function useUpload(options: UseUploadOptions): UseUploadReturn {\n const [state, setState] = useState<UseUploadState>(INITIAL_STATE);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const abortRef = useRef<AbortController | null>(null);\n\n const upload = useCallback(\n async (\n file: File,\n objectKey: string,\n requestOptions?: UploadRequestOptions,\n ) => {\n setState({\n ...INITIAL_STATE,\n phase: \"validating\",\n fileName: file.name,\n fileSize: file.size,\n });\n const opts = optionsRef.current;\n\n const validationError = validateFile(file, {\n accept: opts.accept,\n maxFileSize: opts.maxFileSize,\n });\n if (validationError) {\n setState((s) => ({ ...s, phase: \"error\", error: validationError }));\n opts.onError?.(file, new Error(validationError), \"validating\");\n return;\n }\n\n if (opts.beforeUpload) {\n const allowed = await opts.beforeUpload(file);\n if (!allowed) {\n setState((s) => ({\n ...s,\n phase: \"error\",\n error: \"Upload blocked by beforeUpload hook\",\n }));\n opts.onError?.(file, new Error(\"blocked\"), \"validating\");\n return;\n }\n }\n\n setState((s) => ({ ...s, phase: \"uploading\" }));\n opts.onUploadStart?.(file, objectKey);\n\n const controller = new AbortController();\n abortRef.current = controller;\n\n try {\n const result = await uploadFile(\n opts.presignApi,\n file,\n objectKey,\n {\n multipart: opts.multipart,\n multipartThreshold: opts.multipartThreshold,\n concurrentParts: opts.concurrentParts,\n },\n {\n onProgress: (progress) => {\n setState((s) => ({ ...s, progress }));\n opts.onProgress?.(file, progress);\n },\n },\n controller.signal,\n requestOptions,\n );\n\n setState((s) => ({\n ...s,\n phase: \"success\",\n result,\n progress: { loaded: file.size, total: file.size, percent: 100 },\n }));\n await opts.onSuccess?.(file, result);\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n opts.onCancel?.(file);\n setState(INITIAL_STATE);\n return;\n }\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState((s) => ({ ...s, phase: \"error\", error: message }));\n opts.onError?.(file, err, \"uploading\");\n } finally {\n abortRef.current = null;\n }\n },\n [],\n );\n\n const cancel = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n return { ...state, upload, cancel, reset };\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport { validateFile } from \"@better-s3/server\";\nimport type {\n UploadConfig,\n UploadProgress,\n UploadResult,\n UploadRequestOptions,\n MultiUploadPhase,\n MultiUploadFileState,\n MultiUploadHooks,\n} from \"./types\";\nimport { uploadFiles } from \"./upload\";\n\nexport type UseMultiUploadOptions = UploadConfig &\n MultiUploadHooks & {\n presignApi: PresignApi;\n /** Static request options applied to all files */\n uploadOptions?: UploadRequestOptions;\n /** Per-file request options (overrides uploadOptions) */\n getUploadOptions?: (file: File) => UploadRequestOptions;\n };\n\nexport type UseMultiUploadState = {\n phase: MultiUploadPhase;\n files: MultiUploadFileState[];\n totalProgress: UploadProgress;\n error: string | null;\n};\n\nexport type UseMultiUploadReturn = UseMultiUploadState & {\n upload: (files: File[], resolveKey: (file: File) => string) => Promise<void>;\n cancel: () => void;\n reset: () => void;\n};\n\nconst INITIAL_PROGRESS: UploadProgress = { loaded: 0, total: 0, percent: 0 };\n\nconst INITIAL_STATE: UseMultiUploadState = {\n phase: \"idle\",\n files: [],\n totalProgress: INITIAL_PROGRESS,\n error: null,\n};\n\nlet nextId = 0;\nfunction generateId() {\n return `file-${++nextId}`;\n}\n\nexport function useMultiUpload(\n options: UseMultiUploadOptions,\n): UseMultiUploadReturn {\n const [state, setState] = useState<UseMultiUploadState>(INITIAL_STATE);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const abortRef = useRef<AbortController | null>(null);\n const fileMapRef = useRef<Map<string, File>>(new Map());\n\n const upload = useCallback(\n async (files: File[], resolveKey: (file: File) => string) => {\n const opts = optionsRef.current;\n\n const items: Array<{\n id: string;\n file: File;\n objectKey: string;\n }> = [];\n const fileStates: MultiUploadFileState[] = [];\n const fileMap = new Map<string, File>();\n\n setState((s) => ({ ...s, phase: \"validating\", error: null }));\n\n if (opts.maxFiles && files.length > opts.maxFiles) {\n const msg = `Too many files. Maximum is ${opts.maxFiles}.`;\n setState((s) => ({ ...s, phase: \"error\", error: msg }));\n opts.onError?.(new Error(msg));\n return;\n }\n\n for (const file of files) {\n const validationError = validateFile(file, {\n accept: opts.accept,\n maxFileSize: opts.maxFileSize,\n });\n if (validationError) {\n const msg = `${file.name}: ${validationError}`;\n setState((s) => ({ ...s, phase: \"error\", error: msg }));\n opts.onError?.(new Error(msg));\n return;\n }\n }\n\n if (opts.beforeUpload) {\n const allowed = await opts.beforeUpload(files);\n if (!allowed) {\n setState((s) => ({\n ...s,\n phase: \"error\",\n error: \"Upload blocked by beforeUpload hook\",\n }));\n opts.onError?.(new Error(\"blocked\"));\n return;\n }\n }\n\n for (const file of files) {\n const id = generateId();\n const objectKey = resolveKey(file);\n items.push({ id, file, objectKey });\n fileMap.set(id, file);\n fileStates.push({\n id,\n fileName: file.name,\n fileSize: file.size,\n status: \"pending\",\n progress: { loaded: 0, total: file.size, percent: 0 },\n error: null,\n });\n }\n\n fileMapRef.current = fileMap;\n\n setState({\n phase: \"uploading\",\n files: fileStates,\n totalProgress: {\n loaded: 0,\n total: files.reduce((s, f) => s + f.size, 0),\n percent: 0,\n },\n error: null,\n });\n\n opts.onUploadStart?.(files);\n\n const controller = new AbortController();\n abortRef.current = controller;\n\n try {\n const results = await uploadFiles(\n opts.presignApi,\n items,\n {\n multipart: opts.multipart,\n multipartThreshold: opts.multipartThreshold,\n concurrentParts: opts.concurrentParts,\n concurrentFiles: opts.concurrentFiles,\n },\n {\n onFileProgress: (id, progress) => {\n setState((s) => ({\n ...s,\n files: s.files.map((f) =>\n f.id === id ? { ...f, status: \"uploading\", progress } : f,\n ),\n }));\n const file = fileMap.get(id);\n if (file) opts.onFileProgress?.(file, progress);\n },\n onFileSuccess: (id, result) => {\n setState((s) => ({\n ...s,\n files: s.files.map((f) =>\n f.id === id\n ? {\n ...f,\n status: \"success\",\n progress: {\n loaded: f.fileSize,\n total: f.fileSize,\n percent: 100,\n },\n }\n : f,\n ),\n }));\n const file = fileMap.get(id);\n if (file) opts.onFileSuccess?.(file, result);\n },\n onFileError: (id, error) => {\n setState((s) => ({\n ...s,\n files: s.files.map((f) =>\n f.id === id ? { ...f, status: \"error\", error } : f,\n ),\n }));\n const file = fileMap.get(id);\n if (file) opts.onFileError?.(file, error);\n },\n onTotalProgress: (progress) => {\n setState((s) => ({ ...s, totalProgress: progress }));\n opts.onProgress?.(progress);\n },\n },\n controller.signal,\n (file) => {\n const perFile = opts.getUploadOptions?.(file);\n if (!opts.uploadOptions) return perFile ?? {};\n return { ...opts.uploadOptions, ...perFile };\n },\n );\n\n const hasErrors = results.some((r) => r.status === \"error\");\n const successResults = results\n .filter((r) => r.result !== null)\n .map((r) => r.result!);\n\n setState((s) => ({\n ...s,\n phase: hasErrors ? \"error\" : \"success\",\n error: hasErrors\n ? `${results.filter((r) => r.status === \"error\").length} file(s) failed`\n : null,\n totalProgress: hasErrors\n ? s.totalProgress\n : {\n loaded: s.totalProgress.total,\n total: s.totalProgress.total,\n percent: 100,\n },\n }));\n\n if (!hasErrors) {\n await opts.onSuccess?.(successResults);\n }\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n opts.onCancel?.();\n setState(INITIAL_STATE);\n return;\n }\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState((s) => ({ ...s, phase: \"error\", error: message }));\n opts.onError?.(err);\n } finally {\n abortRef.current = null;\n }\n },\n [],\n );\n\n const cancel = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n return { ...state, upload, cancel, reset };\n}\n","\"use client\";\n\nimport { useRef, useState } from \"react\";\nimport type {\n UploadPhase,\n UploadProgress,\n UploadRequestOptions,\n} from \"./types\";\nimport { useUpload, type UseUploadOptions } from \"./use-upload\";\n\nexport type UseUploadControlsOptions = UseUploadOptions & {\n objectKey: string | ((file: File) => string);\n /** Per-file request options (metadata, bucket, contentType) */\n getUploadOptions?: (file: File) => UploadRequestOptions;\n};\n\nexport type UseUploadControlsReturn = {\n /** Current upload phase */\n phase: UploadPhase;\n /** Upload progress (loaded, total, percent) */\n progress: UploadProgress;\n /** Error message if upload failed */\n error: string | null;\n /** Info about the selected file */\n fileInfo: { name: string; size: number } | null;\n /** Whether an upload is currently in progress */\n isUploading: boolean;\n /** Trigger upload from a FileList (e.g. from a file input or drop event) */\n handleFiles: (files: FileList | null) => void;\n /** Open the native file picker dialog */\n openFilePicker: () => void;\n /** Cancel the current upload */\n cancel: () => void;\n /** Reset to idle state */\n reset: () => void;\n /** Spread on a hidden `<input>` element */\n inputProps: {\n ref: React.RefObject<HTMLInputElement | null>;\n type: \"file\";\n accept?: string;\n hidden: true;\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\n };\n /** Spread on a container to enable drag-and-drop */\n dropHandlers: {\n onDragOver: (e: React.DragEvent) => void;\n onDrop: (e: React.DragEvent) => void;\n };\n};\n\nexport function useUploadControls(\n options: UseUploadControlsOptions,\n): UseUploadControlsReturn {\n const { objectKey, getUploadOptions, ...hookOptions } = options;\n const ctx = useUpload(hookOptions);\n const inputRef = useRef<HTMLInputElement>(null);\n const [fileInfo, setFileInfo] = useState<{\n name: string;\n size: number;\n } | null>(null);\n\n const resolveKey = (file: File): string =>\n typeof objectKey === \"function\" ? objectKey(file) : objectKey;\n\n const handleFiles = async (files: FileList | null) => {\n const file = files?.[0];\n if (!file) return;\n setFileInfo({ name: file.name, size: file.size });\n await ctx.upload(file, resolveKey(file), getUploadOptions?.(file));\n };\n\n const openFilePicker = () => inputRef.current?.click();\n\n const isUploading = ctx.phase === \"uploading\";\n\n return {\n phase: ctx.phase,\n progress: ctx.progress,\n error: ctx.error,\n fileInfo,\n isUploading,\n handleFiles,\n openFilePicker,\n cancel: ctx.cancel,\n reset: () => {\n ctx.reset();\n setFileInfo(null);\n },\n inputProps: {\n ref: inputRef,\n type: \"file\",\n accept: hookOptions.accept?.join(\",\"),\n hidden: true,\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\n handleFiles(e.target.files);\n e.target.value = \"\";\n },\n },\n dropHandlers: {\n onDragOver: (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n },\n onDrop: (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (!isUploading) handleFiles(e.dataTransfer.files);\n },\n },\n };\n}\n","\"use client\";\n\nimport { useRef } from \"react\";\nimport type {\n UploadProgress,\n MultiUploadPhase,\n MultiUploadFileState,\n} from \"./types\";\nimport { useMultiUpload, type UseMultiUploadOptions } from \"./use-multi-upload\";\n\nexport type UseMultiUploadControlsOptions = UseMultiUploadOptions & {\n objectKey: (file: File) => string;\n};\n\nexport type UseMultiUploadControlsReturn = {\n /** Current upload phase */\n phase: MultiUploadPhase;\n /** Per-file upload state */\n files: MultiUploadFileState[];\n /** Aggregated progress across all files */\n totalProgress: UploadProgress;\n /** Error message if upload failed */\n error: string | null;\n /** Whether an upload is currently in progress */\n isUploading: boolean;\n /** Trigger upload from a FileList (e.g. from a file input or drop event) */\n handleFiles: (files: FileList | null) => void;\n /** Open the native file picker dialog */\n openFilePicker: () => void;\n /** Cancel all uploads */\n cancel: () => void;\n /** Reset to idle state */\n reset: () => void;\n /** Spread on a hidden `<input>` element */\n inputProps: {\n ref: React.RefObject<HTMLInputElement | null>;\n type: \"file\";\n multiple: true;\n accept?: string;\n hidden: true;\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\n };\n /** Spread on a container to enable drag-and-drop */\n dropHandlers: {\n onDragOver: (e: React.DragEvent) => void;\n onDrop: (e: React.DragEvent) => void;\n };\n};\n\nexport function useMultiUploadControls(\n options: UseMultiUploadControlsOptions,\n): UseMultiUploadControlsReturn {\n const { objectKey, ...hookOptions } = options;\n const ctx = useMultiUpload(hookOptions);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const handleFiles = async (files: FileList | null) => {\n if (!files?.length) return;\n await ctx.upload(Array.from(files), objectKey);\n };\n\n const openFilePicker = () => inputRef.current?.click();\n\n const isUploading = ctx.phase === \"uploading\";\n\n return {\n phase: ctx.phase,\n files: ctx.files,\n totalProgress: ctx.totalProgress,\n error: ctx.error,\n isUploading,\n handleFiles,\n openFilePicker,\n cancel: ctx.cancel,\n reset: ctx.reset,\n inputProps: {\n ref: inputRef,\n type: \"file\",\n multiple: true,\n accept: hookOptions.accept?.join(\",\"),\n hidden: true,\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\n handleFiles(e.target.files);\n e.target.value = \"\";\n },\n },\n dropHandlers: {\n onDragOver: (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n },\n onDrop: (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (!isUploading) handleFiles(e.dataTransfer.files);\n },\n },\n };\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport type { DownloadPhase, DownloadProgress, DownloadHooks } from \"./types\";\n\nexport type UseDownloadOptions = DownloadHooks & {\n presignApi: PresignApi;\n /**\n * `\"native\"` — browser handles download natively via presigned URL (default)\n * `\"fetch\"` — streams via fetch, enables in-app progress tracking\n */\n mode?: \"native\" | \"fetch\";\n /** Target bucket (overrides server default) */\n bucket?: string;\n};\n\nexport type UseDownloadState = {\n phase: DownloadPhase;\n progress: DownloadProgress;\n error: string | null;\n fileName: string | null;\n fileSize: number | null;\n};\n\nexport type UseDownloadReturn = UseDownloadState & {\n download: (key: string, downloadName?: string) => Promise<void>;\n cancel: () => void;\n reset: () => void;\n};\n\nconst INITIAL_PROGRESS: DownloadProgress = { loaded: 0, total: 0, percent: 0 };\n\nconst INITIAL_STATE: UseDownloadState = {\n phase: \"idle\",\n progress: INITIAL_PROGRESS,\n error: null,\n fileName: null,\n fileSize: null,\n};\n\nexport function useDownload(options: UseDownloadOptions): UseDownloadReturn {\n const [state, setState] = useState<UseDownloadState>(INITIAL_STATE);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const abortRef = useRef<AbortController | null>(null);\n\n const download = useCallback(async (key: string, downloadName?: string) => {\n const name = downloadName ?? key.split(\"/\").pop() ?? key;\n const opts = optionsRef.current;\n const mode = opts.mode ?? \"native\";\n\n if (opts.beforeDownload) {\n const allowed = await opts.beforeDownload(key);\n if (!allowed) {\n setState((s) => ({\n ...s,\n phase: \"error\",\n error: \"Download blocked by beforeDownload hook\",\n }));\n opts.onError?.(key, new Error(\"blocked\"), \"presigning\");\n return;\n }\n }\n\n setState({\n phase: \"presigning\",\n progress: INITIAL_PROGRESS,\n error: null,\n fileName: name,\n fileSize: null,\n });\n\n try {\n const { url } = await opts.presignApi.download(key, {\n fileName: name,\n bucket: opts.bucket,\n });\n\n if (mode === \"native\") {\n const anchor = document.createElement(\"a\");\n anchor.href = url;\n anchor.download = name;\n anchor.click();\n setState((s) => ({ ...s, phase: \"success\" }));\n await opts.onSuccess?.(key);\n setState(INITIAL_STATE);\n return;\n }\n\n // ── Fetch mode ───────────────────────────────────────────────\n setState((s) => ({ ...s, phase: \"downloading\" }));\n opts.onDownloadStart?.(key);\n\n const controller = new AbortController();\n abortRef.current = controller;\n\n const res = await fetch(url, { signal: controller.signal });\n if (!res.ok) throw new Error(`Download failed: ${res.statusText}`);\n\n const contentLength = Number(res.headers.get(\"content-length\") || 0);\n setState((s) => ({ ...s, fileSize: contentLength || null }));\n\n const reader = res.body?.getReader();\n if (!reader) throw new Error(\"ReadableStream not supported\");\n\n const chunks: BlobPart[] = [];\n let loaded = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n loaded += value.byteLength;\n const percent =\n contentLength > 0 ? Math.round((loaded / contentLength) * 100) : 0;\n const progress: DownloadProgress = {\n loaded,\n total: contentLength,\n percent,\n };\n setState((s) => ({ ...s, progress }));\n opts.onProgress?.(key, progress);\n }\n\n const blob = new Blob(chunks);\n const blobUrl = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = blobUrl;\n anchor.download = name;\n anchor.click();\n URL.revokeObjectURL(blobUrl);\n\n setState((s) => ({\n ...s,\n phase: \"success\",\n fileSize: blob.size,\n progress: { loaded: blob.size, total: blob.size, percent: 100 },\n }));\n await opts.onSuccess?.(key);\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n opts.onCancel?.(key);\n setState(INITIAL_STATE);\n return;\n }\n const message = err instanceof Error ? err.message : \"Download failed\";\n setState((s) => ({ ...s, phase: \"error\", error: message }));\n opts.onError?.(key, err, \"downloading\");\n } finally {\n abortRef.current = null;\n }\n }, []);\n\n const cancel = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n return { ...state, download, cancel, reset };\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport type { DeletePhase, DeleteHooks } from \"./types\";\n\nexport type UseDeleteOptions = DeleteHooks & {\n presignApi: PresignApi;\n /** Target bucket (overrides server default) */\n bucket?: string;\n};\n\nexport type UseDeleteState = {\n phase: DeletePhase;\n error: string | null;\n};\n\nexport type UseDeleteReturn = UseDeleteState & {\n requestDelete: (key: string) => void;\n confirmDelete: () => Promise<void>;\n cancelDelete: () => void;\n reset: () => void;\n pendingKey: string | null;\n};\n\nconst INITIAL_STATE: UseDeleteState = {\n phase: \"idle\",\n error: null,\n};\n\nexport function useDelete(options: UseDeleteOptions): UseDeleteReturn {\n const [state, setState] = useState<UseDeleteState>(INITIAL_STATE);\n const [pendingKey, setPendingKey] = useState<string | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const requestDelete = useCallback((key: string) => {\n setPendingKey(key);\n setState({ phase: \"confirming\", error: null });\n }, []);\n\n const confirmDelete = useCallback(async () => {\n if (!pendingKey) return;\n const opts = optionsRef.current;\n\n if (opts.beforeDelete) {\n const allowed = await opts.beforeDelete(pendingKey);\n if (!allowed) {\n setState({\n phase: \"error\",\n error: \"Delete blocked by beforeDelete hook\",\n });\n opts.onError?.(pendingKey, new Error(\"blocked\"), \"confirming\");\n setPendingKey(null);\n return;\n }\n }\n\n setState({ phase: \"deleting\", error: null });\n opts.onDeleteStart?.(pendingKey);\n\n try {\n await opts.presignApi.delete(pendingKey, { bucket: opts.bucket });\n\n setState({ phase: \"success\", error: null });\n await opts.onSuccess?.(pendingKey);\n setPendingKey(null);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Delete failed\";\n setState({ phase: \"error\", error: message });\n opts.onError?.(pendingKey, err, \"deleting\");\n }\n }, [pendingKey]);\n\n const cancelDelete = useCallback(() => {\n setPendingKey(null);\n setState(INITIAL_STATE);\n }, []);\n\n const reset = useCallback(() => {\n setPendingKey(null);\n setState(INITIAL_STATE);\n }, []);\n\n return {\n ...state,\n pendingKey,\n requestDelete,\n confirmDelete,\n cancelDelete,\n reset,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/helpers.ts","../src/upload/retry.ts","../src/upload/simple.ts","../src/upload/part.ts","../src/upload/multipart.ts","../src/upload/upload-file.ts","../src/upload/upload-files.ts","../src/use-upload.ts","../src/use-multi-upload.ts","../src/use-upload-controls.ts","../src/use-multi-upload-controls.ts","../src/use-download.ts","../src/use-fetch-download.ts","../src/use-delete.ts"],"names":["formatFileSize","bytes","units","i","withRetry","fn","retries","signal","lastError","attempt","err","delay","r","uploadSimple","file","presignedUrl","onProgress","resolve","reject","xhr","onAbort","e","eTag","uploadPart","blob","partLoaded","totalSize","reportProgress","uploadMultipart","presignApi","objectKey","partSize","concurrentParts","requestOptions","contentType","uploadId","key","totalParts","parts","partProgress","loaded","sum","p","batchStart","batchEnd","batch","start","end","partNumber","batchResults","a","b","uploadFile","config","callbacks","threshold","useMultipart","presign","uploadFiles","items","getRequestOptions","results","item","reportTotalProgress","total","nextIndex","processNext","idx","result","progress","message","concurrentFiles","workers","INITIAL_PROGRESS","INITIAL_STATE","useUpload","options","state","setState","useState","optionsRef","useRef","abortRef","upload","useCallback","opts","validationError","validateFile","s","controller","cancel","reset","nextId","generateId","useMultiUpload","fileMapRef","files","resolveKey","fileStates","fileMap","msg","id","f","error","perFile","hasErrors","successResults","useUploadControls","getUploadOptions","hookOptions","ctx","inputRef","fileInfo","setFileInfo","handleFiles","openFilePicker","isUploading","useMultiUploadControls","useDownload","download","downloadName","name","url","res","blobUrl","anchor","useFetchDownload","contentLength","reader","chunks","done","value","percent","useDelete","pendingKey","setPendingKey","requestDelete","confirmDelete","cancelDelete"],"mappings":"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","file":"index.js","sourcesContent":["export function formatFileSize(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n const units = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(1024));\n const size = bytes / Math.pow(1024, i);\n return `${size.toFixed(i === 0 ? 0 : 1)} ${units[i]}`;\n}\n","import { RETRY_BASE_DELAY } from \"./constants\";\n\nexport async function withRetry<T>(\n fn: () => Promise<T>,\n retries: number,\n signal?: AbortSignal,\n): Promise<T> {\n let lastError: unknown;\n for (let attempt = 0; attempt <= retries; attempt++) {\n try {\n return await fn();\n } catch (err) {\n if ((err as Error).name === \"AbortError\") throw err;\n lastError = err;\n if (attempt < retries) {\n const delay = RETRY_BASE_DELAY * 2 ** attempt;\n await new Promise((r) => setTimeout(r, delay));\n if (signal?.aborted)\n throw new DOMException(\"Upload aborted\", \"AbortError\");\n }\n }\n }\n throw lastError;\n}\n","import type { UploadProgress } from \"../types\";\n\nexport function uploadSimple(\n file: File,\n presignedUrl: string,\n onProgress?: (progress: UploadProgress) => void,\n signal?: AbortSignal,\n): Promise<string | undefined> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n const onAbort = () => {\n xhr.abort();\n reject(new DOMException(\"Upload aborted\", \"AbortError\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n xhr.upload.addEventListener(\"progress\", (e) => {\n if (e.lengthComputable) {\n onProgress?.({\n loaded: e.loaded,\n total: e.total,\n percent: Math.round((e.loaded / e.total) * 100),\n });\n }\n });\n\n xhr.addEventListener(\"load\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n if (xhr.status >= 200 && xhr.status < 300) {\n onProgress?.({ loaded: file.size, total: file.size, percent: 100 });\n const eTag = xhr.getResponseHeader(\"ETag\")?.replace(/\"/g, \"\");\n resolve(eTag ?? undefined);\n } else {\n reject(new Error(`Upload failed: ${xhr.status} ${xhr.statusText}`));\n }\n });\n\n xhr.addEventListener(\"error\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(new Error(\"Upload failed: network error\"));\n });\n\n xhr.addEventListener(\"abort\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(new DOMException(\"Upload aborted\", \"AbortError\"));\n });\n\n xhr.open(\"PUT\", presignedUrl);\n xhr.setRequestHeader(\n \"Content-Type\",\n file.type || \"application/octet-stream\",\n );\n xhr.send(file);\n });\n}\n","export function uploadPart(\n blob: Blob,\n presignedUrl: string,\n partLoaded: { bytes: number },\n totalSize: number,\n reportProgress: () => void,\n signal?: AbortSignal,\n): Promise<string> {\n return new Promise((resolve, reject) => {\n const xhr = new XMLHttpRequest();\n\n const onAbort = () => {\n xhr.abort();\n reject(new DOMException(\"Upload aborted\", \"AbortError\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n\n xhr.upload.addEventListener(\"progress\", (e) => {\n if (e.lengthComputable) {\n partLoaded.bytes = e.loaded;\n reportProgress();\n }\n });\n\n xhr.addEventListener(\"load\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n if (xhr.status >= 200 && xhr.status < 300) {\n partLoaded.bytes = blob.size;\n reportProgress();\n const eTag = xhr.getResponseHeader(\"ETag\") ?? \"\";\n resolve(eTag);\n } else {\n reject(new Error(`Part upload failed: ${xhr.status}`));\n }\n });\n\n xhr.addEventListener(\"error\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(new Error(\"Part upload failed: network error\"));\n });\n\n xhr.addEventListener(\"abort\", () => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(new DOMException(\"Upload aborted\", \"AbortError\"));\n });\n\n xhr.open(\"PUT\", presignedUrl);\n xhr.send(blob);\n });\n}\n","import type { UploadProgress, UploadRequestOptions } from \"../types\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport { MAX_RETRIES } from \"./constants\";\nimport { withRetry } from \"./retry\";\nimport { uploadPart } from \"./part\";\n\nexport async function uploadMultipart(\n presignApi: PresignApi,\n file: File,\n objectKey: string,\n partSize: number,\n concurrentParts: number,\n onProgress?: (progress: UploadProgress) => void,\n signal?: AbortSignal,\n requestOptions?: UploadRequestOptions,\n): Promise<void> {\n const contentType = requestOptions?.contentType ?? file.type;\n const { uploadId, key } = await presignApi.multipart.init({\n key: objectKey,\n contentType,\n metadata: requestOptions?.metadata,\n bucket: requestOptions?.bucket,\n });\n\n const totalParts = Math.ceil(file.size / partSize);\n const parts: Array<{ partNumber: number; eTag: string }> = [];\n\n const partProgress: Array<{ bytes: number }> = Array.from(\n { length: totalParts },\n () => ({ bytes: 0 }),\n );\n\n const reportProgress = () => {\n const loaded = partProgress.reduce((sum, p) => sum + p.bytes, 0);\n onProgress?.({\n loaded,\n total: file.size,\n percent: Math.round((loaded / file.size) * 100),\n });\n };\n\n try {\n for (\n let batchStart = 0;\n batchStart < totalParts;\n batchStart += concurrentParts\n ) {\n if (signal?.aborted) {\n throw new DOMException(\"Upload aborted\", \"AbortError\");\n }\n\n const batchEnd = Math.min(batchStart + concurrentParts, totalParts);\n const batch: Array<Promise<{ partNumber: number; eTag: string }>> = [];\n\n for (let i = batchStart; i < batchEnd; i++) {\n const start = i * partSize;\n const end = Math.min(start + partSize, file.size);\n const blob = file.slice(start, end);\n const partNumber = i + 1;\n\n batch.push(\n withRetry(\n async () => {\n const { presignedUrl } = await presignApi.multipart.signPart({\n key,\n uploadId,\n partNumber,\n bucket: requestOptions?.bucket,\n });\n\n partProgress[i].bytes = 0;\n\n const eTag = await uploadPart(\n blob,\n presignedUrl,\n partProgress[i],\n file.size,\n reportProgress,\n signal,\n );\n\n return { partNumber, eTag: eTag.replace(/\"/g, \"\") };\n },\n MAX_RETRIES,\n signal,\n ),\n );\n }\n\n const batchResults = await Promise.all(batch);\n parts.push(...batchResults);\n }\n\n parts.sort((a, b) => a.partNumber - b.partNumber);\n\n await presignApi.multipart.complete({\n key,\n uploadId,\n parts,\n bucket: requestOptions?.bucket,\n });\n onProgress?.({ loaded: file.size, total: file.size, percent: 100 });\n } catch (err) {\n presignApi.multipart\n .abort({ key, uploadId, bucket: requestOptions?.bucket })\n .catch(() => {});\n throw err;\n }\n}\n","import type {\n UploadConfig,\n UploadProgress,\n UploadResult,\n UploadRequestOptions,\n} from \"../types\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport {\n DEFAULT_MULTIPART_THRESHOLD,\n DEFAULT_CONCURRENT_PARTS,\n DEFAULT_PART_SIZE,\n MAX_RETRIES,\n} from \"./constants\";\nimport { withRetry } from \"./retry\";\nimport { uploadSimple } from \"./simple\";\nimport { uploadMultipart } from \"./multipart\";\n\nexport type UploadEngineCallbacks = {\n onProgress?: (progress: UploadProgress) => void;\n};\n\nexport async function uploadFile(\n presignApi: PresignApi,\n file: File,\n objectKey: string,\n config: UploadConfig = {},\n callbacks: UploadEngineCallbacks = {},\n signal?: AbortSignal,\n requestOptions?: UploadRequestOptions,\n): Promise<UploadResult> {\n const threshold = config.multipartThreshold ?? DEFAULT_MULTIPART_THRESHOLD;\n const useMultipart = config.multipart === true && file.size >= threshold;\n const concurrentParts = config.concurrentParts ?? DEFAULT_CONCURRENT_PARTS;\n const contentType = requestOptions?.contentType ?? file.type;\n\n let eTag: string | undefined;\n\n if (useMultipart) {\n await uploadMultipart(\n presignApi,\n file,\n objectKey,\n DEFAULT_PART_SIZE,\n concurrentParts,\n callbacks.onProgress,\n signal,\n requestOptions,\n );\n } else {\n eTag = await withRetry(\n async () => {\n const presign = await presignApi.upload({\n key: objectKey,\n contentType,\n metadata: requestOptions?.metadata,\n bucket: requestOptions?.bucket,\n });\n return uploadSimple(file, presign.url, callbacks.onProgress, signal);\n },\n MAX_RETRIES,\n signal,\n );\n\n await presignApi.confirm({\n key: objectKey,\n bucket: requestOptions?.bucket,\n });\n }\n\n return { key: objectKey, eTag };\n}\n","import type {\n UploadConfig,\n UploadProgress,\n UploadResult,\n UploadRequestOptions,\n} from \"../types\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport { DEFAULT_CONCURRENT_FILES } from \"./constants\";\nimport { uploadFile } from \"./upload-file\";\n\nexport type FileItemStatus = \"pending\" | \"uploading\" | \"success\" | \"error\";\n\nexport type FileItem = {\n id: string;\n file: File;\n objectKey: string;\n status: FileItemStatus;\n progress: UploadProgress;\n result: UploadResult | null;\n error: string | null;\n};\n\nexport type MultiUploadCallbacks = {\n onFileProgress?: (id: string, progress: UploadProgress) => void;\n onFileSuccess?: (id: string, result: UploadResult) => void;\n onFileError?: (id: string, error: string) => void;\n onTotalProgress?: (progress: UploadProgress) => void;\n};\n\nexport async function uploadFiles(\n presignApi: PresignApi,\n items: Array<{ id: string; file: File; objectKey: string }>,\n config: UploadConfig = {},\n callbacks: MultiUploadCallbacks = {},\n signal?: AbortSignal,\n getRequestOptions?: (file: File) => UploadRequestOptions,\n): Promise<FileItem[]> {\n const results: FileItem[] = items.map((item) => ({\n ...item,\n status: \"pending\" as FileItemStatus,\n progress: { loaded: 0, total: item.file.size, percent: 0 },\n result: null,\n error: null,\n }));\n\n const reportTotalProgress = () => {\n const loaded = results.reduce((sum, r) => sum + r.progress.loaded, 0);\n const total = results.reduce((sum, r) => sum + r.progress.total, 0);\n callbacks.onTotalProgress?.({\n loaded,\n total,\n percent: total > 0 ? Math.round((loaded / total) * 100) : 0,\n });\n };\n\n let nextIndex = 0;\n\n const processNext = async (): Promise<void> => {\n while (nextIndex < results.length) {\n if (signal?.aborted) return;\n const idx = nextIndex++;\n const item = results[idx];\n\n item.status = \"uploading\";\n\n try {\n const result = await uploadFile(\n presignApi,\n item.file,\n item.objectKey,\n config,\n {\n onProgress: (progress) => {\n item.progress = progress;\n callbacks.onFileProgress?.(item.id, progress);\n reportTotalProgress();\n },\n },\n signal,\n getRequestOptions?.(item.file),\n );\n item.status = \"success\";\n item.result = result;\n item.progress = {\n loaded: item.file.size,\n total: item.file.size,\n percent: 100,\n };\n callbacks.onFileSuccess?.(item.id, result);\n reportTotalProgress();\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n item.status = \"error\";\n item.error = \"Upload cancelled\";\n return;\n }\n const message = err instanceof Error ? err.message : \"Upload failed\";\n item.status = \"error\";\n item.error = message;\n callbacks.onFileError?.(item.id, message);\n reportTotalProgress();\n }\n }\n };\n\n const concurrentFiles = config.concurrentFiles ?? DEFAULT_CONCURRENT_FILES;\n const workers = Array.from(\n { length: Math.min(concurrentFiles, items.length) },\n () => processNext(),\n );\n await Promise.all(workers);\n\n return results;\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport { validateFile } from \"@better-s3/server\";\nimport type {\n UploadConfig,\n UploadHooks,\n UploadPhase,\n UploadProgress,\n UploadResult,\n UploadRequestOptions,\n} from \"./types\";\nimport { uploadFile } from \"./upload\";\n\nexport type UseUploadOptions = UploadConfig &\n UploadHooks & {\n presignApi: PresignApi;\n };\n\nexport type UseUploadState = {\n phase: UploadPhase;\n progress: UploadProgress;\n error: string | null;\n result: UploadResult | null;\n fileName: string | null;\n fileSize: number | null;\n};\n\nexport type UseUploadReturn = UseUploadState & {\n upload: (\n file: File,\n objectKey: string,\n requestOptions?: UploadRequestOptions,\n ) => Promise<void>;\n cancel: () => void;\n reset: () => void;\n};\n\nconst INITIAL_PROGRESS: UploadProgress = { loaded: 0, total: 0, percent: 0 };\n\nconst INITIAL_STATE: UseUploadState = {\n phase: \"idle\",\n progress: INITIAL_PROGRESS,\n error: null,\n result: null,\n fileName: null,\n fileSize: null,\n};\n\nexport function useUpload(options: UseUploadOptions): UseUploadReturn {\n const [state, setState] = useState<UseUploadState>(INITIAL_STATE);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const abortRef = useRef<AbortController | null>(null);\n\n const upload = useCallback(\n async (\n file: File,\n objectKey: string,\n requestOptions?: UploadRequestOptions,\n ) => {\n setState({\n ...INITIAL_STATE,\n phase: \"validating\",\n fileName: file.name,\n fileSize: file.size,\n });\n const opts = optionsRef.current;\n\n const validationError = validateFile(file, {\n accept: opts.accept,\n maxFileSize: opts.maxFileSize,\n });\n if (validationError) {\n setState((s) => ({ ...s, phase: \"error\", error: validationError }));\n opts.onError?.(file, new Error(validationError), \"validating\");\n return;\n }\n\n if (opts.beforeUpload) {\n const allowed = await opts.beforeUpload(file);\n if (!allowed) {\n setState((s) => ({\n ...s,\n phase: \"error\",\n error: \"Upload blocked by beforeUpload hook\",\n }));\n opts.onError?.(file, new Error(\"blocked\"), \"validating\");\n return;\n }\n }\n\n setState((s) => ({ ...s, phase: \"uploading\" }));\n opts.onUploadStart?.(file, objectKey);\n\n const controller = new AbortController();\n abortRef.current = controller;\n\n try {\n const result = await uploadFile(\n opts.presignApi,\n file,\n objectKey,\n {\n multipart: opts.multipart,\n multipartThreshold: opts.multipartThreshold,\n concurrentParts: opts.concurrentParts,\n },\n {\n onProgress: (progress) => {\n setState((s) => ({ ...s, progress }));\n opts.onProgress?.(file, progress);\n },\n },\n controller.signal,\n requestOptions,\n );\n\n setState((s) => ({\n ...s,\n phase: \"success\",\n result,\n progress: { loaded: file.size, total: file.size, percent: 100 },\n }));\n await opts.onSuccess?.(file, result);\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n opts.onCancel?.(file);\n setState(INITIAL_STATE);\n return;\n }\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState((s) => ({ ...s, phase: \"error\", error: message }));\n opts.onError?.(file, err, \"uploading\");\n } finally {\n abortRef.current = null;\n }\n },\n [],\n );\n\n const cancel = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n return { ...state, upload, cancel, reset };\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport { validateFile } from \"@better-s3/server\";\nimport type {\n UploadConfig,\n UploadProgress,\n UploadResult,\n UploadRequestOptions,\n MultiUploadPhase,\n MultiUploadFileState,\n MultiUploadHooks,\n} from \"./types\";\nimport { uploadFiles } from \"./upload\";\n\nexport type UseMultiUploadOptions = UploadConfig &\n MultiUploadHooks & {\n presignApi: PresignApi;\n /** Static request options applied to all files */\n uploadOptions?: UploadRequestOptions;\n /** Per-file request options (overrides uploadOptions) */\n getUploadOptions?: (file: File) => UploadRequestOptions;\n };\n\nexport type UseMultiUploadState = {\n phase: MultiUploadPhase;\n files: MultiUploadFileState[];\n totalProgress: UploadProgress;\n error: string | null;\n};\n\nexport type UseMultiUploadReturn = UseMultiUploadState & {\n upload: (files: File[], resolveKey: (file: File) => string) => Promise<void>;\n cancel: () => void;\n reset: () => void;\n};\n\nconst INITIAL_PROGRESS: UploadProgress = { loaded: 0, total: 0, percent: 0 };\n\nconst INITIAL_STATE: UseMultiUploadState = {\n phase: \"idle\",\n files: [],\n totalProgress: INITIAL_PROGRESS,\n error: null,\n};\n\nlet nextId = 0;\nfunction generateId() {\n return `file-${++nextId}`;\n}\n\nexport function useMultiUpload(\n options: UseMultiUploadOptions,\n): UseMultiUploadReturn {\n const [state, setState] = useState<UseMultiUploadState>(INITIAL_STATE);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const abortRef = useRef<AbortController | null>(null);\n const fileMapRef = useRef<Map<string, File>>(new Map());\n\n const upload = useCallback(\n async (files: File[], resolveKey: (file: File) => string) => {\n const opts = optionsRef.current;\n\n const items: Array<{\n id: string;\n file: File;\n objectKey: string;\n }> = [];\n const fileStates: MultiUploadFileState[] = [];\n const fileMap = new Map<string, File>();\n\n setState((s) => ({ ...s, phase: \"validating\", error: null }));\n\n if (opts.maxFiles && files.length > opts.maxFiles) {\n const msg = `Too many files. Maximum is ${opts.maxFiles}.`;\n setState((s) => ({ ...s, phase: \"error\", error: msg }));\n opts.onError?.(new Error(msg));\n return;\n }\n\n for (const file of files) {\n const validationError = validateFile(file, {\n accept: opts.accept,\n maxFileSize: opts.maxFileSize,\n });\n if (validationError) {\n const msg = `${file.name}: ${validationError}`;\n setState((s) => ({ ...s, phase: \"error\", error: msg }));\n opts.onError?.(new Error(msg));\n return;\n }\n }\n\n if (opts.beforeUpload) {\n const allowed = await opts.beforeUpload(files);\n if (!allowed) {\n setState((s) => ({\n ...s,\n phase: \"error\",\n error: \"Upload blocked by beforeUpload hook\",\n }));\n opts.onError?.(new Error(\"blocked\"));\n return;\n }\n }\n\n for (const file of files) {\n const id = generateId();\n const objectKey = resolveKey(file);\n items.push({ id, file, objectKey });\n fileMap.set(id, file);\n fileStates.push({\n id,\n fileName: file.name,\n fileSize: file.size,\n status: \"pending\",\n progress: { loaded: 0, total: file.size, percent: 0 },\n error: null,\n });\n }\n\n fileMapRef.current = fileMap;\n\n setState({\n phase: \"uploading\",\n files: fileStates,\n totalProgress: {\n loaded: 0,\n total: files.reduce((s, f) => s + f.size, 0),\n percent: 0,\n },\n error: null,\n });\n\n opts.onUploadStart?.(files);\n\n const controller = new AbortController();\n abortRef.current = controller;\n\n try {\n const results = await uploadFiles(\n opts.presignApi,\n items,\n {\n multipart: opts.multipart,\n multipartThreshold: opts.multipartThreshold,\n concurrentParts: opts.concurrentParts,\n concurrentFiles: opts.concurrentFiles,\n },\n {\n onFileProgress: (id, progress) => {\n setState((s) => ({\n ...s,\n files: s.files.map((f) =>\n f.id === id ? { ...f, status: \"uploading\", progress } : f,\n ),\n }));\n const file = fileMap.get(id);\n if (file) opts.onFileProgress?.(file, progress);\n },\n onFileSuccess: (id, result) => {\n setState((s) => ({\n ...s,\n files: s.files.map((f) =>\n f.id === id\n ? {\n ...f,\n status: \"success\",\n progress: {\n loaded: f.fileSize,\n total: f.fileSize,\n percent: 100,\n },\n }\n : f,\n ),\n }));\n const file = fileMap.get(id);\n if (file) opts.onFileSuccess?.(file, result);\n },\n onFileError: (id, error) => {\n setState((s) => ({\n ...s,\n files: s.files.map((f) =>\n f.id === id ? { ...f, status: \"error\", error } : f,\n ),\n }));\n const file = fileMap.get(id);\n if (file) opts.onFileError?.(file, error);\n },\n onTotalProgress: (progress) => {\n setState((s) => ({ ...s, totalProgress: progress }));\n opts.onProgress?.(progress);\n },\n },\n controller.signal,\n (file) => {\n const perFile = opts.getUploadOptions?.(file);\n if (!opts.uploadOptions) return perFile ?? {};\n return { ...opts.uploadOptions, ...perFile };\n },\n );\n\n const hasErrors = results.some((r) => r.status === \"error\");\n const successResults = results\n .filter((r) => r.result !== null)\n .map((r) => r.result!);\n\n setState((s) => ({\n ...s,\n phase: hasErrors ? \"error\" : \"success\",\n error: hasErrors\n ? `${results.filter((r) => r.status === \"error\").length} file(s) failed`\n : null,\n totalProgress: hasErrors\n ? s.totalProgress\n : {\n loaded: s.totalProgress.total,\n total: s.totalProgress.total,\n percent: 100,\n },\n }));\n\n if (!hasErrors) {\n await opts.onSuccess?.(successResults);\n }\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n opts.onCancel?.();\n setState(INITIAL_STATE);\n return;\n }\n const message = err instanceof Error ? err.message : \"Upload failed\";\n setState((s) => ({ ...s, phase: \"error\", error: message }));\n opts.onError?.(err);\n } finally {\n abortRef.current = null;\n }\n },\n [],\n );\n\n const cancel = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n return { ...state, upload, cancel, reset };\n}\n","\"use client\";\n\nimport { useRef, useState } from \"react\";\nimport type {\n UploadPhase,\n UploadProgress,\n UploadRequestOptions,\n} from \"./types\";\nimport { useUpload, type UseUploadOptions } from \"./use-upload\";\n\nexport type UseUploadControlsOptions = UseUploadOptions & {\n objectKey: string | ((file: File) => string);\n /** Per-file request options (metadata, bucket, contentType) */\n getUploadOptions?: (file: File) => UploadRequestOptions;\n};\n\nexport type UseUploadControlsReturn = {\n /** Current upload phase */\n phase: UploadPhase;\n /** Upload progress (loaded, total, percent) */\n progress: UploadProgress;\n /** Error message if upload failed */\n error: string | null;\n /** Info about the selected file */\n fileInfo: { name: string; size: number } | null;\n /** Whether an upload is currently in progress */\n isUploading: boolean;\n /** Trigger upload from a FileList (e.g. from a file input or drop event) */\n handleFiles: (files: FileList | null) => void;\n /** Open the native file picker dialog */\n openFilePicker: () => void;\n /** Cancel the current upload */\n cancel: () => void;\n /** Reset to idle state */\n reset: () => void;\n /** Spread on a hidden `<input>` element */\n inputProps: {\n ref: React.RefObject<HTMLInputElement | null>;\n type: \"file\";\n accept?: string;\n hidden: true;\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\n };\n /** Spread on a container to enable drag-and-drop */\n dropHandlers: {\n onDragOver: (e: React.DragEvent) => void;\n onDrop: (e: React.DragEvent) => void;\n };\n};\n\nexport function useUploadControls(\n options: UseUploadControlsOptions,\n): UseUploadControlsReturn {\n const { objectKey, getUploadOptions, ...hookOptions } = options;\n const ctx = useUpload(hookOptions);\n const inputRef = useRef<HTMLInputElement>(null);\n const [fileInfo, setFileInfo] = useState<{\n name: string;\n size: number;\n } | null>(null);\n\n const resolveKey = (file: File): string =>\n typeof objectKey === \"function\" ? objectKey(file) : objectKey;\n\n const handleFiles = async (files: FileList | null) => {\n const file = files?.[0];\n if (!file) return;\n setFileInfo({ name: file.name, size: file.size });\n await ctx.upload(file, resolveKey(file), getUploadOptions?.(file));\n };\n\n const openFilePicker = () => inputRef.current?.click();\n\n const isUploading = ctx.phase === \"uploading\";\n\n return {\n phase: ctx.phase,\n progress: ctx.progress,\n error: ctx.error,\n fileInfo,\n isUploading,\n handleFiles,\n openFilePicker,\n cancel: ctx.cancel,\n reset: () => {\n ctx.reset();\n setFileInfo(null);\n },\n inputProps: {\n ref: inputRef,\n type: \"file\",\n accept: hookOptions.accept?.join(\",\"),\n hidden: true,\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\n handleFiles(e.target.files);\n e.target.value = \"\";\n },\n },\n dropHandlers: {\n onDragOver: (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n },\n onDrop: (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (!isUploading) handleFiles(e.dataTransfer.files);\n },\n },\n };\n}\n","\"use client\";\n\nimport { useRef } from \"react\";\nimport type {\n UploadProgress,\n MultiUploadPhase,\n MultiUploadFileState,\n} from \"./types\";\nimport { useMultiUpload, type UseMultiUploadOptions } from \"./use-multi-upload\";\n\nexport type UseMultiUploadControlsOptions = UseMultiUploadOptions & {\n objectKey: (file: File) => string;\n};\n\nexport type UseMultiUploadControlsReturn = {\n /** Current upload phase */\n phase: MultiUploadPhase;\n /** Per-file upload state */\n files: MultiUploadFileState[];\n /** Aggregated progress across all files */\n totalProgress: UploadProgress;\n /** Error message if upload failed */\n error: string | null;\n /** Whether an upload is currently in progress */\n isUploading: boolean;\n /** Trigger upload from a FileList (e.g. from a file input or drop event) */\n handleFiles: (files: FileList | null) => void;\n /** Open the native file picker dialog */\n openFilePicker: () => void;\n /** Cancel all uploads */\n cancel: () => void;\n /** Reset to idle state */\n reset: () => void;\n /** Spread on a hidden `<input>` element */\n inputProps: {\n ref: React.RefObject<HTMLInputElement | null>;\n type: \"file\";\n multiple: true;\n accept?: string;\n hidden: true;\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;\n };\n /** Spread on a container to enable drag-and-drop */\n dropHandlers: {\n onDragOver: (e: React.DragEvent) => void;\n onDrop: (e: React.DragEvent) => void;\n };\n};\n\nexport function useMultiUploadControls(\n options: UseMultiUploadControlsOptions,\n): UseMultiUploadControlsReturn {\n const { objectKey, ...hookOptions } = options;\n const ctx = useMultiUpload(hookOptions);\n const inputRef = useRef<HTMLInputElement>(null);\n\n const handleFiles = async (files: FileList | null) => {\n if (!files?.length) return;\n await ctx.upload(Array.from(files), objectKey);\n };\n\n const openFilePicker = () => inputRef.current?.click();\n\n const isUploading = ctx.phase === \"uploading\";\n\n return {\n phase: ctx.phase,\n files: ctx.files,\n totalProgress: ctx.totalProgress,\n error: ctx.error,\n isUploading,\n handleFiles,\n openFilePicker,\n cancel: ctx.cancel,\n reset: ctx.reset,\n inputProps: {\n ref: inputRef,\n type: \"file\",\n multiple: true,\n accept: hookOptions.accept?.join(\",\"),\n hidden: true,\n onChange: (e: React.ChangeEvent<HTMLInputElement>) => {\n handleFiles(e.target.files);\n e.target.value = \"\";\n },\n },\n dropHandlers: {\n onDragOver: (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n },\n onDrop: (e: React.DragEvent) => {\n e.preventDefault();\n e.stopPropagation();\n if (!isUploading) handleFiles(e.dataTransfer.files);\n },\n },\n };\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\nimport type { PresignApi } from \"@better-s3/server\";\n\nexport type DownloadPhase = \"idle\" | \"downloading\" | \"success\" | \"error\";\n\nexport type DownloadHooks = {\n beforeDownload?: (key: string) => Promise<boolean> | boolean;\n onSuccess?: (key: string) => Promise<void> | void;\n onError?: (key: string, error: unknown) => void;\n};\n\nexport type UseDownloadOptions = DownloadHooks & {\n presignApi: PresignApi;\n /** Target bucket (overrides server default) */\n bucket?: string;\n};\n\nexport type UseDownloadState = {\n phase: DownloadPhase;\n error: string | null;\n fileName: string | null;\n};\n\nexport type UseDownloadReturn = UseDownloadState & {\n download: (key: string, downloadName?: string) => Promise<void>;\n reset: () => void;\n};\n\nconst INITIAL_STATE: UseDownloadState = {\n phase: \"idle\",\n error: null,\n fileName: null,\n};\n\nexport function useDownload(options: UseDownloadOptions): UseDownloadReturn {\n const [state, setState] = useState<UseDownloadState>(INITIAL_STATE);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const download = useCallback(async (key: string, downloadName?: string) => {\n const name = downloadName ?? key.split(\"/\").pop() ?? key;\n const opts = optionsRef.current;\n\n if (opts.beforeDownload) {\n const allowed = await opts.beforeDownload(key);\n if (!allowed) {\n setState({\n phase: \"error\",\n error: \"Download blocked by beforeDownload hook\",\n fileName: name,\n });\n opts.onError?.(key, new Error(\"blocked\"));\n return;\n }\n }\n\n setState({ phase: \"downloading\", error: null, fileName: name });\n\n try {\n const { url } = await opts.presignApi.download(key, {\n fileName: name,\n bucket: opts.bucket,\n });\n\n // Fetch as blob so the download attribute works correctly\n // (cross-origin presigned URLs ignore anchor.download).\n const res = await fetch(url);\n if (!res.ok) {\n throw new Error(\n res.status === 404\n ? \"File not found\"\n : `Download failed (${res.status})`,\n );\n }\n\n const blob = await res.blob();\n const blobUrl = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = blobUrl;\n anchor.download = name;\n anchor.click();\n URL.revokeObjectURL(blobUrl);\n\n setState({ phase: \"success\", error: null, fileName: name });\n await opts.onSuccess?.(key);\n setState(INITIAL_STATE);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Download failed\";\n setState({ phase: \"error\", error: message, fileName: name });\n opts.onError?.(key, err);\n }\n }, []);\n\n const reset = useCallback(() => {\n setState(INITIAL_STATE);\n }, []);\n\n return { ...state, download, reset };\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\nimport type { PresignApi } from \"@better-s3/server\";\n\nexport type FetchDownloadPhase =\n | \"idle\"\n | \"presigning\"\n | \"downloading\"\n | \"success\"\n | \"error\";\n\nexport type FetchDownloadProgress = {\n loaded: number;\n total: number;\n percent: number;\n};\n\nexport type FetchDownloadHooks = {\n beforeDownload?: (key: string) => Promise<boolean> | boolean;\n onDownloadStart?: (key: string) => void;\n onProgress?: (key: string, progress: FetchDownloadProgress) => void;\n onSuccess?: (key: string) => Promise<void> | void;\n onError?: (key: string, error: unknown, phase: FetchDownloadPhase) => void;\n onCancel?: (key: string) => void;\n};\n\nexport type UseFetchDownloadOptions = FetchDownloadHooks & {\n presignApi: PresignApi;\n /** Target bucket (overrides server default) */\n bucket?: string;\n};\n\nexport type UseFetchDownloadState = {\n phase: FetchDownloadPhase;\n progress: FetchDownloadProgress;\n error: string | null;\n fileName: string | null;\n fileSize: number | null;\n};\n\nexport type UseFetchDownloadReturn = UseFetchDownloadState & {\n download: (key: string, downloadName?: string) => Promise<void>;\n cancel: () => void;\n reset: () => void;\n};\n\nconst INITIAL_PROGRESS: FetchDownloadProgress = {\n loaded: 0,\n total: 0,\n percent: 0,\n};\n\nconst INITIAL_STATE: UseFetchDownloadState = {\n phase: \"idle\",\n progress: INITIAL_PROGRESS,\n error: null,\n fileName: null,\n fileSize: null,\n};\n\nexport function useFetchDownload(\n options: UseFetchDownloadOptions,\n): UseFetchDownloadReturn {\n const [state, setState] = useState<UseFetchDownloadState>(INITIAL_STATE);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n const abortRef = useRef<AbortController | null>(null);\n\n const download = useCallback(async (key: string, downloadName?: string) => {\n const name = downloadName ?? key.split(\"/\").pop() ?? key;\n const opts = optionsRef.current;\n\n if (opts.beforeDownload) {\n const allowed = await opts.beforeDownload(key);\n if (!allowed) {\n setState((s) => ({\n ...s,\n phase: \"error\",\n error: \"Download blocked by beforeDownload hook\",\n }));\n opts.onError?.(key, new Error(\"blocked\"), \"presigning\");\n return;\n }\n }\n\n setState({\n phase: \"presigning\",\n progress: INITIAL_PROGRESS,\n error: null,\n fileName: name,\n fileSize: null,\n });\n\n try {\n const { url } = await opts.presignApi.download(key, {\n fileName: name,\n bucket: opts.bucket,\n });\n\n setState((s) => ({ ...s, phase: \"downloading\" }));\n opts.onDownloadStart?.(key);\n\n const controller = new AbortController();\n abortRef.current = controller;\n\n const res = await fetch(url, { signal: controller.signal });\n if (!res.ok) {\n throw new Error(\n res.status === 404\n ? \"File not found\"\n : `Download failed (${res.status})`,\n );\n }\n\n const contentLength = Number(res.headers.get(\"content-length\") || 0);\n setState((s) => ({ ...s, fileSize: contentLength || null }));\n\n const reader = res.body?.getReader();\n if (!reader) throw new Error(\"ReadableStream not supported\");\n\n const chunks: BlobPart[] = [];\n let loaded = 0;\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n chunks.push(value);\n loaded += value.byteLength;\n const percent =\n contentLength > 0 ? Math.round((loaded / contentLength) * 100) : 0;\n const progress: FetchDownloadProgress = {\n loaded,\n total: contentLength,\n percent,\n };\n setState((s) => ({ ...s, progress }));\n opts.onProgress?.(key, progress);\n }\n\n const blob = new Blob(chunks);\n const blobUrl = URL.createObjectURL(blob);\n const anchor = document.createElement(\"a\");\n anchor.href = blobUrl;\n anchor.download = name;\n anchor.click();\n URL.revokeObjectURL(blobUrl);\n\n setState((s) => ({\n ...s,\n phase: \"success\",\n fileSize: blob.size,\n progress: { loaded: blob.size, total: blob.size, percent: 100 },\n }));\n await opts.onSuccess?.(key);\n } catch (err) {\n if ((err as Error).name === \"AbortError\") {\n opts.onCancel?.(key);\n setState(INITIAL_STATE);\n return;\n }\n const message = err instanceof Error ? err.message : \"Download failed\";\n setState((s) => ({ ...s, phase: \"error\", error: message }));\n opts.onError?.(key, err, \"downloading\");\n } finally {\n abortRef.current = null;\n }\n }, []);\n\n const cancel = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n const reset = useCallback(() => {\n abortRef.current?.abort();\n setState(INITIAL_STATE);\n }, []);\n\n return { ...state, download, cancel, reset };\n}\n","\"use client\";\n\nimport { useCallback, useRef, useState } from \"react\";\nimport type { PresignApi } from \"@better-s3/server\";\nimport type { DeletePhase, DeleteHooks } from \"./types\";\n\nexport type UseDeleteOptions = DeleteHooks & {\n presignApi: PresignApi;\n /** Target bucket (overrides server default) */\n bucket?: string;\n};\n\nexport type UseDeleteState = {\n phase: DeletePhase;\n error: string | null;\n};\n\nexport type UseDeleteReturn = UseDeleteState & {\n requestDelete: (key: string) => void;\n confirmDelete: () => Promise<void>;\n cancelDelete: () => void;\n reset: () => void;\n pendingKey: string | null;\n};\n\nconst INITIAL_STATE: UseDeleteState = {\n phase: \"idle\",\n error: null,\n};\n\nexport function useDelete(options: UseDeleteOptions): UseDeleteReturn {\n const [state, setState] = useState<UseDeleteState>(INITIAL_STATE);\n const [pendingKey, setPendingKey] = useState<string | null>(null);\n const optionsRef = useRef(options);\n optionsRef.current = options;\n\n const requestDelete = useCallback((key: string) => {\n setPendingKey(key);\n setState({ phase: \"confirming\", error: null });\n }, []);\n\n const confirmDelete = useCallback(async () => {\n if (!pendingKey) return;\n const opts = optionsRef.current;\n\n if (opts.beforeDelete) {\n const allowed = await opts.beforeDelete(pendingKey);\n if (!allowed) {\n setState({\n phase: \"error\",\n error: \"Delete blocked by beforeDelete hook\",\n });\n opts.onError?.(pendingKey, new Error(\"blocked\"), \"confirming\");\n setPendingKey(null);\n return;\n }\n }\n\n setState({ phase: \"deleting\", error: null });\n opts.onDeleteStart?.(pendingKey);\n\n try {\n await opts.presignApi.delete(pendingKey, { bucket: opts.bucket });\n\n setState({ phase: \"success\", error: null });\n await opts.onSuccess?.(pendingKey);\n setPendingKey(null);\n } catch (err) {\n const message = err instanceof Error ? err.message : \"Delete failed\";\n setState({ phase: \"error\", error: message });\n opts.onError?.(pendingKey, err, \"deleting\");\n }\n }, [pendingKey]);\n\n const cancelDelete = useCallback(() => {\n setPendingKey(null);\n setState(INITIAL_STATE);\n }, []);\n\n const reset = useCallback(() => {\n setPendingKey(null);\n setState(INITIAL_STATE);\n }, []);\n\n return {\n ...state,\n pendingKey,\n requestDelete,\n confirmDelete,\n cancelDelete,\n reset,\n };\n}\n"]}
|
package/dist/types/download.d.ts
CHANGED
|
@@ -1,14 +1,2 @@
|
|
|
1
|
-
export type DownloadPhase
|
|
2
|
-
export type
|
|
3
|
-
loaded: number;
|
|
4
|
-
total: number;
|
|
5
|
-
percent: number;
|
|
6
|
-
};
|
|
7
|
-
export type DownloadHooks = {
|
|
8
|
-
beforeDownload?: (key: string) => Promise<boolean> | boolean;
|
|
9
|
-
onDownloadStart?: (key: string) => void;
|
|
10
|
-
onProgress?: (key: string, progress: DownloadProgress) => void;
|
|
11
|
-
onSuccess?: (key: string) => Promise<void> | void;
|
|
12
|
-
onError?: (key: string, error: unknown, phase: DownloadPhase) => void;
|
|
13
|
-
onCancel?: (key: string) => void;
|
|
14
|
-
};
|
|
1
|
+
export type { DownloadPhase, DownloadHooks } from "../use-download";
|
|
2
|
+
export type { FetchDownloadPhase, FetchDownloadProgress, FetchDownloadHooks, } from "../use-fetch-download";
|
package/dist/use-download.d.ts
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import type { PresignApi } from "@better-s3/server";
|
|
2
|
-
|
|
2
|
+
export type DownloadPhase = "idle" | "downloading" | "success" | "error";
|
|
3
|
+
export type DownloadHooks = {
|
|
4
|
+
beforeDownload?: (key: string) => Promise<boolean> | boolean;
|
|
5
|
+
onSuccess?: (key: string) => Promise<void> | void;
|
|
6
|
+
onError?: (key: string, error: unknown) => void;
|
|
7
|
+
};
|
|
3
8
|
export type UseDownloadOptions = DownloadHooks & {
|
|
4
9
|
presignApi: PresignApi;
|
|
5
|
-
/**
|
|
6
|
-
* `"native"` — browser handles download natively via presigned URL (default)
|
|
7
|
-
* `"fetch"` — streams via fetch, enables in-app progress tracking
|
|
8
|
-
*/
|
|
9
|
-
mode?: "native" | "fetch";
|
|
10
10
|
/** Target bucket (overrides server default) */
|
|
11
11
|
bucket?: string;
|
|
12
12
|
};
|
|
13
13
|
export type UseDownloadState = {
|
|
14
14
|
phase: DownloadPhase;
|
|
15
|
-
progress: DownloadProgress;
|
|
16
15
|
error: string | null;
|
|
17
16
|
fileName: string | null;
|
|
18
|
-
fileSize: number | null;
|
|
19
17
|
};
|
|
20
18
|
export type UseDownloadReturn = UseDownloadState & {
|
|
21
19
|
download: (key: string, downloadName?: string) => Promise<void>;
|
|
22
|
-
cancel: () => void;
|
|
23
20
|
reset: () => void;
|
|
24
21
|
};
|
|
25
22
|
export declare function useDownload(options: UseDownloadOptions): UseDownloadReturn;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { PresignApi } from "@better-s3/server";
|
|
2
|
+
export type FetchDownloadPhase = "idle" | "presigning" | "downloading" | "success" | "error";
|
|
3
|
+
export type FetchDownloadProgress = {
|
|
4
|
+
loaded: number;
|
|
5
|
+
total: number;
|
|
6
|
+
percent: number;
|
|
7
|
+
};
|
|
8
|
+
export type FetchDownloadHooks = {
|
|
9
|
+
beforeDownload?: (key: string) => Promise<boolean> | boolean;
|
|
10
|
+
onDownloadStart?: (key: string) => void;
|
|
11
|
+
onProgress?: (key: string, progress: FetchDownloadProgress) => void;
|
|
12
|
+
onSuccess?: (key: string) => Promise<void> | void;
|
|
13
|
+
onError?: (key: string, error: unknown, phase: FetchDownloadPhase) => void;
|
|
14
|
+
onCancel?: (key: string) => void;
|
|
15
|
+
};
|
|
16
|
+
export type UseFetchDownloadOptions = FetchDownloadHooks & {
|
|
17
|
+
presignApi: PresignApi;
|
|
18
|
+
/** Target bucket (overrides server default) */
|
|
19
|
+
bucket?: string;
|
|
20
|
+
};
|
|
21
|
+
export type UseFetchDownloadState = {
|
|
22
|
+
phase: FetchDownloadPhase;
|
|
23
|
+
progress: FetchDownloadProgress;
|
|
24
|
+
error: string | null;
|
|
25
|
+
fileName: string | null;
|
|
26
|
+
fileSize: number | null;
|
|
27
|
+
};
|
|
28
|
+
export type UseFetchDownloadReturn = UseFetchDownloadState & {
|
|
29
|
+
download: (key: string, downloadName?: string) => Promise<void>;
|
|
30
|
+
cancel: () => void;
|
|
31
|
+
reset: () => void;
|
|
32
|
+
};
|
|
33
|
+
export declare function useFetchDownload(options: UseFetchDownloadOptions): UseFetchDownloadReturn;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-s3/react",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.2.0",
|
|
4
4
|
"description": "React hooks for S3-compatible file uploads, downloads, and deletes",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"s3",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"dist"
|
|
34
34
|
],
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@better-s3/server": "2.
|
|
36
|
+
"@better-s3/server": "2.2.0"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"react": ">=18"
|