Skip to content

Commit 0b18ce4

Browse files
committed
feat: added progress for request and response with json
1 parent 314077e commit 0b18ce4

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

src/fetch.ts

+55-1
Original file line numberDiff line numberDiff line change
@@ -66,12 +66,14 @@ export interface FetchOptions<R extends ResponseType = ResponseType>
6666
onRequestError?(
6767
context: FetchContext & { error: Error }
6868
): Promise<void> | void;
69+
onRequestProgress?(progress: number): Promise<void> | void;
6970
onResponse?(
7071
context: FetchContext & { response: FetchResponse<R> }
7172
): Promise<void> | void;
7273
onResponseError?(
7374
context: FetchContext & { response: FetchResponse<R> }
7475
): Promise<void> | void;
76+
onResponseProgress?(progress: number): Promise<void> | void;
7577
}
7678

7779
export interface $Fetch {
@@ -196,6 +198,28 @@ export function createFetch(globalOptions: CreateFetchOptions = {}): $Fetch {
196198
? context.options.body
197199
: JSON.stringify(context.options.body);
198200

201+
if (context.options.onRequestProgress) {
202+
const { readable, writable } = new TransformStream();
203+
const _writer = writable.getWriter();
204+
const _encoder = new TextEncoder();
205+
const contentLength = _encoder.encode(
206+
context.options.body
207+
).byteLength;
208+
let loaded = 0;
209+
210+
for (const char of context.options.body) {
211+
const chunk = _encoder.encode(char);
212+
loaded += chunk.byteLength;
213+
_writer.write(chunk);
214+
context.options.onRequestProgress(
215+
Math.round((loaded / contentLength) * 100)
216+
);
217+
}
218+
219+
context.options.body = readable;
220+
context.options.duplex = "half";
221+
}
222+
199223
// Set Content-Type and Accept headers to application/json by default
200224
// for JSON serializable request bodies.
201225
// Pass empty object as older browsers don't support undefined.
@@ -255,7 +279,37 @@ export function createFetch(globalOptions: CreateFetchOptions = {}): $Fetch {
255279
// We override the `.json()` method to parse the body more securely with `destr`
256280
switch (responseType) {
257281
case "json": {
258-
const data = await context.response.text();
282+
const data = await (async function () {
283+
/* Custom response.text() function to retrieve text from response and get progress values */
284+
let loaded = 0;
285+
const contentLength =
286+
context.response!.headers.get("content-length")!;
287+
const _reader = context.response!.body!.getReader();
288+
const _decoder = new TextDecoder();
289+
const _chunks: string[] = [];
290+
291+
async function read(): Promise<string> {
292+
const { done, value } = await _reader.read();
293+
294+
if (done) {
295+
return _chunks.join("");
296+
}
297+
298+
loaded += value.byteLength;
299+
300+
if (context.options.onResponseProgress) {
301+
context.options.onResponseProgress(
302+
Math.round((loaded / Number.parseInt(contentLength)) * 100)
303+
);
304+
}
305+
306+
const chunk = _decoder.decode(value, { stream: true });
307+
_chunks.push(chunk);
308+
return await read(); // read the next chunk
309+
}
310+
311+
return await read();
312+
})();
259313
const parseFunction = context.options.parseResponse || destr;
260314
context.response._data = parseFunction(data);
261315
break;

test/index.test.ts

+30
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,36 @@ describe("ofetch", () => {
323323
expect(race).to.equal("timeout");
324324
});
325325

326+
it("return progress in onResponseProgress", async () => {
327+
let loaded = 0;
328+
await $fetch(getURL("post"), {
329+
method: "post",
330+
body: JSON.stringify({
331+
key: "test",
332+
json: true,
333+
}),
334+
onResponseProgress: (progress) => {
335+
loaded = progress;
336+
},
337+
});
338+
expect(loaded).to.equal(100);
339+
});
340+
341+
it("return progress in onRequestProgress", async () => {
342+
let loaded = 0;
343+
await $fetch(getURL("post"), {
344+
method: "post",
345+
body: JSON.stringify({
346+
key: "test",
347+
json: true,
348+
}),
349+
onRequestProgress: (progress) => {
350+
loaded = progress;
351+
},
352+
});
353+
expect(loaded).to.equal(100);
354+
});
355+
326356
it("deep merges defaultOptions", async () => {
327357
const _customFetch = $fetch.create({
328358
query: {

0 commit comments

Comments
 (0)