(url: string, destPath: string, name: string)
| 168 | // ============================================================================ |
| 169 | |
| 170 | async function downloadBinary(url: string, destPath: string, name: string): Promise<void> { |
| 171 | const { net } = await import("electron") |
| 172 | |
| 173 | logger.info(`[Binaries] Downloading ${name} from ${url}`) |
| 174 | |
| 175 | const response = await net.fetch(url, { redirect: "follow" }) |
| 176 | if (!response.ok) { |
| 177 | throw new Error(`Download failed: HTTP ${response.status} ${response.statusText}`) |
| 178 | } |
| 179 | |
| 180 | const body = response.body |
| 181 | if (!body) throw new Error("No response body") |
| 182 | |
| 183 | fs.mkdirSync(path.dirname(destPath), { recursive: true }) |
| 184 | |
| 185 | const fileStream = createWriteStream(destPath) |
| 186 | let bytesDownloaded = 0 |
| 187 | |
| 188 | const reader = body.getReader() |
| 189 | try { |
| 190 | while (true) { |
| 191 | const { done, value } = await reader.read() |
| 192 | if (done) break |
| 193 | fileStream.write(value) |
| 194 | bytesDownloaded += value.byteLength |
| 195 | } |
| 196 | } finally { |
| 197 | reader.releaseLock() |
| 198 | } |
| 199 | |
| 200 | fileStream.end() |
| 201 | await new Promise<void>((resolve, reject) => { |
| 202 | fileStream.on("finish", resolve) |
| 203 | fileStream.on("error", reject) |
| 204 | }) |
| 205 | |
| 206 | if (process.platform !== "win32") { |
| 207 | fs.chmodSync(destPath, 0o755) |
| 208 | } |
| 209 | |
| 210 | logger.info(`[Binaries] Downloaded ${name}: ${bytesDownloaded} bytes`) |
| 211 | } |
| 212 | |
| 213 | // ============================================================================ |
| 214 | // Core API |
no test coverage detected