import { net } from 'electron' import { Readable } from 'node:stream' import { byteStringResponseHeaders } from './response-headers.js' export const fetchNetwork: typeof fetch = async (input, init) => { const request = new Request(input, init) if (request.redirect !== 'manual') return net.fetch(request) if (request.method !== 'GET') throw new Error('Ручные переходы доступны только для загрузки файлов') // net.fetch rejects manual redirects before it can return their status and headers. return new Promise((resolve, reject) => { const client = net.request({ url: request.url, method: 'GET', redirect: 'manual', credentials: request.credentials, }) let body: Readable | null = null const cleanup = () => request.signal.removeEventListener('abort', abort) const fail = (error: unknown) => { cleanup() reject(error) client.abort() } const abort = () => { const error = request.signal.reason body?.destroy(error instanceof Error ? error : new Error('Загрузка отменена')) fail(error) } const headersOf = (values: Record) => { const headers = new Headers() for (const [name, entries] of Object.entries(byteStringResponseHeaders(values))) for (const value of entries) headers.append(name, value) return headers } client.on('error', fail) client.on('redirect', (status, _method, url, values) => { try { const headers = headersOf(values) headers.set('Location', new URL(url).href) resolve(new Response(null, { status, headers })) cleanup() client.abort() } catch (error) { fail(error) } }) client.on('response', (response) => { try { body = response as unknown as Readable body.once('end', cleanup) body.once('close', () => { cleanup() if (!body!.readableEnded) client.abort() }) const empty = [204, 205, 304].includes(response.statusCode) const stream = empty ? null : Readable.toWeb(body) as ReadableStream resolve(new Response(stream, { status: response.statusCode, headers: headersOf(response.headers), })) if (empty) body.resume() } catch (error) { fail(error) } }) try { for (const [name, value] of request.headers) client.setHeader(name, value) request.signal.addEventListener('abort', abort, { once: true }) if (request.signal.aborted) abort() else client.end() } catch (error) { fail(error) } }) }