@@ -69,69 +69,110 @@ export async function extractTarGz(
6969 } | null = null
7070
7171 return new Promise ( ( resolve , reject ) => {
72+ let isProcessing = false
73+ const chunks : Buffer [ ] = [ ]
74+
7275 const processChunk = async ( chunk : Buffer ) => {
73- buffer = Buffer . concat ( [ buffer , chunk ] )
76+ if ( isProcessing ) {
77+ chunks . push ( chunk )
78+ return
79+ }
7480
75- while ( buffer . length >= 512 ) {
76- if ( ! currentFile ) {
77- // Parse header
78- const header = parseTarHeader ( buffer . subarray ( 0 , 512 ) )
79- if ( ! header ) {
80- // End of archive or invalid header
81- buffer = buffer . subarray ( 512 )
82- continue
83- }
81+ isProcessing = true
82+ buffer = Buffer . concat ( [ buffer , chunk ] )
8483
85- if ( header . type === 'file' && header . size > 0 ) {
86- currentFile = { name : header . name , size : header . size }
87- const outputPath = join ( resolvedOutputDir , header . name )
84+ // Process any queued chunks
85+ while ( chunks . length > 0 ) {
86+ const nextChunk = chunks . shift ( )
87+ if ( nextChunk ) {
88+ buffer = Buffer . concat ( [ buffer , nextChunk ] )
89+ }
90+ }
8891
89- // Ensure directory exists
90- await fs . mkdir ( dirname ( outputPath ) , { recursive : true } )
91- currentFile . stream = createWriteStream ( outputPath )
92- }
92+ try {
93+ while ( buffer . length >= 512 ) {
94+ if ( ! currentFile ) {
95+ // Parse header
96+ const header = parseTarHeader ( buffer . subarray ( 0 , 512 ) )
97+ if ( ! header ) {
98+ // End of archive or invalid header
99+ buffer = buffer . subarray ( 512 )
100+ continue
101+ }
93102
94- buffer = buffer . subarray ( 512 ) // Skip header
95- } else {
96- // Extract file content
97- const remainingSize = currentFile . size
98- const availableData = Math . min ( remainingSize , buffer . length )
103+ if ( header . type === 'file' && header . size > 0 ) {
104+ currentFile = { name : header . name , size : header . size }
105+ const outputPath = join ( resolvedOutputDir , header . name )
99106
100- if ( availableData > 0 && currentFile . stream ) {
101- currentFile . stream . write ( buffer . subarray ( 0 , availableData ) )
102- currentFile . size -= availableData
103- buffer = buffer . subarray ( availableData )
104- }
107+ // Ensure directory exists
108+ await fs . mkdir ( dirname ( outputPath ) , { recursive : true } )
109+ currentFile . stream = createWriteStream ( outputPath )
110+ }
105111
106- if ( currentFile . size === 0 ) {
107- // File complete
108- if ( currentFile . stream ) {
109- currentFile . stream . end ( )
112+ buffer = buffer . subarray ( 512 ) // Skip header
113+ } else {
114+ // Extract file content
115+ const remainingSize = currentFile . size
116+ const availableData = Math . min ( remainingSize , buffer . length )
117+
118+ if ( availableData > 0 && currentFile . stream ) {
119+ currentFile . stream . write ( buffer . subarray ( 0 , availableData ) )
120+ currentFile . size -= availableData
121+ buffer = buffer . subarray ( availableData )
110122 }
111- currentFile = null
112123
113- // Skip padding to 512-byte boundary
114- const paddingSize = ( 512 - ( availableData % 512 ) ) % 512
115- if ( buffer . length >= paddingSize ) {
116- buffer = buffer . subarray ( paddingSize )
124+ if ( currentFile . size === 0 ) {
125+ // File complete - wait for stream to finish
126+ if ( currentFile . stream ) {
127+ await new Promise < void > ( ( streamResolve , streamReject ) => {
128+ currentFile ?. stream ?. once ( 'finish' , streamResolve )
129+ currentFile ?. stream ?. once ( 'error' , streamReject )
130+ currentFile ?. stream ?. end ( )
131+ } )
132+ }
133+ currentFile = null
134+
135+ // Skip padding to 512-byte boundary
136+ const paddingSize = ( 512 - ( availableData % 512 ) ) % 512
137+ if ( buffer . length >= paddingSize ) {
138+ buffer = buffer . subarray ( paddingSize )
139+ }
140+ } else if ( buffer . length < remainingSize ) {
141+ // Need more data
142+ break
117143 }
118- } else if ( buffer . length < remainingSize ) {
119- // Need more data
120- break
121144 }
122145 }
146+ } catch ( error ) {
147+ reject ( error )
148+ return
123149 }
150+
151+ isProcessing = false
124152 }
125153
126154 gunzip . on ( 'data' , ( chunk : Buffer ) => {
127155 processChunk ( chunk ) . catch ( reject )
128156 } )
129157
130- gunzip . on ( 'end' , ( ) => {
131- if ( currentFile ?. stream ) {
132- currentFile . stream . end ( )
158+ gunzip . on ( 'end' , async ( ) => {
159+ try {
160+ // Wait for any pending processing
161+ while ( isProcessing ) {
162+ await new Promise ( ( resolve ) => setTimeout ( resolve , 1 ) )
163+ }
164+
165+ if ( currentFile ?. stream ) {
166+ await new Promise < void > ( ( streamResolve , streamReject ) => {
167+ currentFile ?. stream ?. once ( 'finish' , streamResolve )
168+ currentFile ?. stream ?. once ( 'error' , streamReject )
169+ currentFile ?. stream ?. end ( )
170+ } )
171+ }
172+ resolve ( )
173+ } catch ( error ) {
174+ reject ( error )
133175 }
134- resolve ( )
135176 } )
136177
137178 gunzip . on ( 'error' , reject )
0 commit comments