@@ -26,13 +26,13 @@ import { existsSync, promises as fs } from 'node:fs'
26
26
import os from 'node:os'
27
27
import path from 'node:path'
28
28
29
+ import { downloadWithLock } from '@socketsecurity/registry/lib/download-lock'
29
30
import { readJson , remove } from '@socketsecurity/registry/lib/fs'
30
31
import { getSocketDlxDir } from '@socketsecurity/registry/lib/paths'
31
32
import { spawn } from '@socketsecurity/registry/lib/spawn'
32
33
33
34
import constants from '../constants.mts'
34
35
import { InputError } from './errors.mts'
35
- import { httpRequest } from './http.mts'
36
36
37
37
import type {
38
38
SpawnExtra ,
@@ -111,39 +111,33 @@ async function isCacheValid(
111
111
}
112
112
113
113
/**
114
- * Download a file from a URL with integrity checking.
114
+ * Download a file from a URL with integrity checking and download locking.
115
+ * Uses registry's downloadWithLock to prevent concurrent downloads.
115
116
*/
116
117
async function downloadBinary (
117
118
url : string ,
118
119
destPath : string ,
119
120
checksum ?: string ,
120
121
) : Promise < string > {
121
- const result = await httpRequest ( url )
122
-
123
- if ( ! result . ok ) {
124
- throw new InputError ( `Failed to download binary: ${ result . message } ` )
125
- }
126
-
127
- const response = result . data !
128
-
129
- if ( ! response . ok ) {
130
- throw new InputError (
131
- `Failed to download binary: ${ response . status } ${ response . statusText } ` ,
132
- )
133
- }
134
-
135
- // Create a temporary file first.
122
+ // Create a temporary file first for integrity checking.
136
123
const tempPath = `${ destPath } .download`
137
- const hasher = createHash ( 'sha256' )
138
124
139
125
try {
140
126
// Ensure directory exists.
141
127
await fs . mkdir ( path . dirname ( destPath ) , { recursive : true } )
142
128
143
- // Get the response as a buffer and compute hash.
144
- const buffer = response . body
145
-
146
- // Compute hash.
129
+ // Download with locking and automatic retries.
130
+ // This prevents concurrent downloads and provides retry logic.
131
+ await downloadWithLock ( url , tempPath , {
132
+ lockTimeout : 120_000 , // Wait up to 2 minutes for concurrent downloads
133
+ retries : 3 , // Retry up to 3 times with exponential backoff
134
+ retryDelay : 1000 , // Start with 1 second delay
135
+ timeout : 300_000 , // 5 minute timeout per attempt
136
+ } )
137
+
138
+ // Read file for checksum verification.
139
+ const buffer = await fs . readFile ( tempPath )
140
+ const hasher = createHash ( 'sha256' )
147
141
hasher . update ( buffer )
148
142
const actualChecksum = hasher . digest ( 'hex' )
149
143
@@ -154,9 +148,6 @@ async function downloadBinary(
154
148
)
155
149
}
156
150
157
- // Write to temp file.
158
- await fs . writeFile ( tempPath , buffer )
159
-
160
151
// Make executable on POSIX systems.
161
152
if ( os . platform ( ) !== 'win32' ) {
162
153
await fs . chmod ( tempPath , 0o755 )
@@ -173,7 +164,9 @@ async function downloadBinary(
173
164
} catch {
174
165
// Ignore cleanup errors.
175
166
}
176
- throw error
167
+ throw error instanceof Error
168
+ ? error
169
+ : new InputError ( `Failed to download binary: ${ String ( error ) } ` )
177
170
}
178
171
}
179
172
0 commit comments