import { EventTarget } from 'event-target-shim'; import { parseBody, parseHeaders, ProgressData, Request, RequestResult, Response } from 'koajax'; import { Blob, fetch, Headers } from 'taro-fetch-polyfill'; import { ReadableStream } from 'web-streams-polyfill'; import { fromAsync } from './Event'; import { emitStreamProgress, streamFromProgress } from './utility'; export * from './utility'; export function request({ path, method, headers, withCredentials, body, signal, timeout, responseType }: Request): RequestResult { const signals = [signal, timeout && AbortSignal.timeout(timeout)].filter( Boolean ); headers = headers instanceof Headers ? Object.fromEntries(headers.entries()) : headers instanceof Array ? Object.fromEntries(headers) : headers; headers = responseType === 'text' ? { ...headers, Accept: 'text/plain' } : responseType === 'json' ? { ...headers, Accept: 'application/json' } : responseType === 'document' ? { ...headers, Accept: 'text/html, application/xhtml+xml, application/xml' } : responseType === 'arraybuffer' || responseType === 'blob' ? { ...headers, Accept: 'application/octet-stream' } : headers; const isStream = body instanceof ReadableStream; var upload: AsyncGenerator | undefined; if (isStream) { const uploadProgress = new EventTarget(); body = ReadableStream.from( emitStreamProgress( body as ReadableStream, +headers['Content-Length'], uploadProgress ) ) as ReadableStream; upload = streamFromProgress(uploadProgress); } const downloadProgress = new EventTarget(); const response = fetch(path + '', { method, headers, credentials: withCredentials ? 'include' : 'omit', body, signal: signals[0] && AbortSignal.any(signals), // @ts-expect-error https://developer.chrome.com/docs/capabilities/web-apis/fetch-streaming-requests duplex: isStream ? 'half' : undefined }).then(response => parseResponse(response, responseType, downloadProgress) ); return { response, upload, download: streamFromProgress(downloadProgress) }; } export async function parseResponse( { status, statusText, headers, body }: import('taro-fetch-polyfill').Response, responseType: Request['responseType'], downloadProgress: EventTarget ): Promise> { const stream = ReadableStream.from( emitStreamProgress( body as ReadableStream, +headers.get('Content-Length'), downloadProgress ) ) as ReadableStream; const contentType = headers.get('Content-Type') || ''; const header = parseHeaders( [...headers].map(([key, value]) => `${key}: ${value}`).join('\n') ); const rBody = status === 204 ? undefined : await parseFetchBody(stream, contentType, responseType); return { status, statusText, headers: header, body: rBody }; } export async function parseFetchBody( stream: ReadableStream, contentType: string, responseType: Request['responseType'] ) { const blob = new Blob(await fromAsync(stream), { type: contentType }); if (responseType === 'blob') return blob as B; if (responseType === 'arraybuffer') return blob.arrayBuffer() as B; const text = await blob.text(); if (responseType === 'text') return text as B; return parseBody(text, contentType); }