1- import { createWriteStream } from 'fs' ;
1+ import { createWriteStream , unlinkSync } from 'fs' ;
22import { createProgressBar } from '../output/progress' ;
3+ import { CLIError } from '../errors/base' ;
4+ import { ExitCode } from '../errors/codes' ;
35
46export async function downloadFile (
57 url : string ,
@@ -9,33 +11,53 @@ export async function downloadFile(
911 const res = await fetch ( url ) ;
1012
1113 if ( ! res . ok ) {
12- throw new Error ( `Download failed: HTTP ${ res . status } ` ) ;
14+ throw new CLIError ( `Download failed: HTTP ${ res . status } ` , ExitCode . GENERAL ) ;
1315 }
1416
1517 const contentLength = Number ( res . headers . get ( 'content-length' ) || 0 ) ;
1618 const reader = res . body ?. getReader ( ) ;
17- if ( ! reader ) throw new Error ( 'No response body' ) ;
19+ if ( ! reader ) throw new CLIError ( 'No response body' , ExitCode . GENERAL ) ;
1820
1921 const writer = createWriteStream ( destPath ) ;
2022 const progress = contentLength > 0 && ! opts ?. quiet
2123 ? createProgressBar ( contentLength , 'Downloading' )
2224 : null ;
2325
2426 let received = 0 ;
27+ let completed = false ;
2528
2629 try {
30+ const writeError = new Promise < never > ( ( _ , reject ) => {
31+ writer . on ( 'error' , reject ) ;
32+ } ) ;
33+
2734 while ( true ) {
28- const { done, value } = await reader . read ( ) ;
35+ const { done, value } = await Promise . race ( [
36+ reader . read ( ) ,
37+ writeError ,
38+ ] ) as ReadableStreamReadResult < Uint8Array > ;
2939 if ( done ) break ;
3040
31- writer . write ( value ) ;
41+ const ok = writer . write ( value ) ;
42+ if ( ! ok ) await new Promise ( r => writer . once ( 'drain' , r ) ) ;
43+
3244 received += value . byteLength ;
3345 progress ?. update ( received ) ;
3446 }
47+ completed = true ;
3548 } finally {
3649 reader . releaseLock ( ) ;
37- writer . end ( ) ;
3850 progress ?. finish ( ) ;
51+
52+ await new Promise < void > ( ( resolve , reject ) => {
53+ writer . on ( 'finish' , resolve ) ;
54+ writer . on ( 'error' , reject ) ;
55+ writer . end ( ) ;
56+ } ) ;
57+
58+ if ( ! completed ) {
59+ try { unlinkSync ( destPath ) ; } catch { /* best effort */ }
60+ }
3961 }
4062
4163 return { size : received } ;
0 commit comments