ky
Version:
Tiny and elegant HTTP client based on the Fetch API
121 lines • 4.68 kB
JavaScript
import { usualFormBoundarySize } from '../core/constants.js';
// eslint-disable-next-line @typescript-eslint/ban-types
export const getBodySize = (body) => {
if (!body) {
return 0;
}
if (body instanceof FormData) {
// This is an approximation, as FormData size calculation is not straightforward
let size = 0;
for (const [key, value] of body) {
size += usualFormBoundarySize;
size += new TextEncoder().encode(`Content-Disposition: form-data; name="${key}"`).length;
size += typeof value === 'string'
? new TextEncoder().encode(value).length
: value.size;
}
return size;
}
if (body instanceof Blob) {
return body.size;
}
if (body instanceof ArrayBuffer) {
return body.byteLength;
}
if (typeof body === 'string') {
return new TextEncoder().encode(body).length;
}
if (body instanceof URLSearchParams) {
return new TextEncoder().encode(body.toString()).length;
}
if ('byteLength' in body) {
return (body).byteLength;
}
if (typeof body === 'object' && body !== null) {
try {
const jsonString = JSON.stringify(body);
return new TextEncoder().encode(jsonString).length;
}
catch {
return 0;
}
}
return 0; // Default case, unable to determine size
};
export const streamResponse = (response, onDownloadProgress) => {
const totalBytes = Number(response.headers.get('content-length')) || 0;
let transferredBytes = 0;
if (response.status === 204) {
if (onDownloadProgress) {
onDownloadProgress({ percent: 1, totalBytes, transferredBytes }, new Uint8Array());
}
return new Response(null, {
status: response.status,
statusText: response.statusText,
headers: response.headers,
});
}
return new Response(new ReadableStream({
async start(controller) {
const reader = response.body.getReader();
if (onDownloadProgress) {
onDownloadProgress({ percent: 0, transferredBytes: 0, totalBytes }, new Uint8Array());
}
async function read() {
const { done, value } = await reader.read();
if (done) {
controller.close();
return;
}
if (onDownloadProgress) {
transferredBytes += value.byteLength;
const percent = totalBytes === 0 ? 0 : transferredBytes / totalBytes;
onDownloadProgress({ percent, transferredBytes, totalBytes }, value);
}
controller.enqueue(value);
await read();
}
await read();
},
}), {
status: response.status,
statusText: response.statusText,
headers: response.headers,
});
};
export const streamRequest = (request, onUploadProgress) => {
const totalBytes = getBodySize(request.body);
let transferredBytes = 0;
return new Request(request, {
// @ts-expect-error - Types are outdated.
duplex: 'half',
body: new ReadableStream({
async start(controller) {
const reader = request.body instanceof ReadableStream ? request.body.getReader() : new Response('').body.getReader();
async function read() {
const { done, value } = await reader.read();
if (done) {
// Ensure 100% progress is reported when the upload is complete
if (onUploadProgress) {
onUploadProgress({ percent: 1, transferredBytes, totalBytes: Math.max(totalBytes, transferredBytes) }, new Uint8Array());
}
controller.close();
return;
}
transferredBytes += value.byteLength;
let percent = totalBytes === 0 ? 0 : transferredBytes / totalBytes;
if (totalBytes < transferredBytes || percent === 1) {
percent = 0.99;
}
if (onUploadProgress) {
onUploadProgress({ percent: Number(percent.toFixed(2)), transferredBytes, totalBytes }, value);
}
controller.enqueue(value);
await read();
}
await read();
},
}),
});
};
//# sourceMappingURL=body.js.map