Skip to content

Commit

Permalink
🏭 Add progress addon
Browse files Browse the repository at this point in the history
Partially solves #154
  • Loading branch information
elbywan committed Dec 3, 2022
1 parent 23ba7b1 commit 2bae524
Show file tree
Hide file tree
Showing 2 changed files with 144 additions and 0 deletions.
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,17 @@ Used to construct and append the query string part of the URL from an object.

```js
import QueryStringAddon from "wretch/addons/queryString"

let w = wretch("http://example.com").addon(QueryStringAddon);
// url is http://example.com
w = w.query({ a: 1, b: 2 });
// url is now http://example.com?a=1&b=2
w = w.query({ c: 3, d: [4, 5] });
// url is now http://example.com?a=1&b=2c=3&d=4&d=5
w = w.query("five&six&seven=eight");
// url is now http://example.com?a=1&b=2c=3&d=4&d=5&five&six&seven=eight
w = w.query({ reset: true }, true);
// url is now http://example.com?reset=true
```

### [FormData 🔗](https://elbywan.github.io/wretch/api/interfaces/addons_formData.FormDataAddon.html)
Expand All @@ -517,6 +528,23 @@ Adds a helper method to serialize a `multipart/form-data` body from an object.

```js
import FormDataAddon from "wretch/addons/formData"

const form = {
duck: "Muscovy",
duckProperties: {
beak: {
color: "yellow",
},
legs: 2,
},
ignored: {
key: 0,
},
};

// Will append the following keys to the FormData payload:
// "duck", "duckProperties[beak][color]", "duckProperties[legs]"
wretch("...").addons(FormDataAddon).formData(form, ["ignored"]).post();
```

### [FormUrl 🔗](https://elbywan.github.io/wretch/api/interfaces/addons_formUrl.FormUrlAddon.html)
Expand All @@ -525,6 +553,13 @@ Adds a method to serialize a `application/x-www-form-urlencoded` body from an ob

```js
import FormUrlAddon from "wretch/addons/formUrl"

const form = { a: 1, b: { c: 2 } };
const alreadyEncodedForm = "a=1&b=%7B%22c%22%3A2%7D";

// Automatically sets the content-type header to "application/x-www-form-urlencoded"
wretch("...").addon(FormUrlAddon).formUrl(form).post();
wretch("...").addon(FormUrlAddon).formUrl(alreadyEncodedForm).post();
```

### [Abort 🔗](https://elbywan.github.io/wretch/api/modules/addons_abort.html)
Expand Down Expand Up @@ -573,6 +608,26 @@ wretch("...").addon(AbortAddon()).get().setTimeout(1000).json(_ =>
)
```

### [Progress 🔗](https://elbywan.github.io/wretch/api/modules/addons_progress.html)

Adds the ability to monitor progress when downloading a response.

_Compatible with all platforms implementing the [TransformStream WebAPI](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream#browser_compatibility)._


```js
import ProgressAddon from "wretch/addons/progress"

wretch("some_url")
.addon(ProgressAddon())
// Called with the number of bytes loaded and the total number of bytes to load
.progress((loaded, total) => {
console.log(`${(loaded / total * 100).toFixed(0)}%`)
})
.get()
.json()
```

### [Performance 🔗](https://elbywan.github.io/wretch/api/modules/addons_perfs.html)

Adds the ability to measure requests using the Performance Timings API.
Expand Down
89 changes: 89 additions & 0 deletions src/addons/progress.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import type { ConfiguredMiddleware, WretchAddon, WretchResponseChain } from "../types.js"

export interface ProgressResolver {
/**
* Provides a way to register a callback to be invoked one or multiple times during the download.
* The callback receives the current progress as two arguments, the number of bytes loaded and the total number of bytes to load.
*
* _Under the hood: this method adds a middleware to the chain that will intercept the response and replace the body with a new one that will emit the progress event._
*
* ```js
* import ProgressAddon from "wretch/addons/progress"
*
* wretch("some_url")
* // Register the addon
* .addon(ProgressAddon())
* .get()
* // Log the progress as a percentage of completion
* .progress((loaded, total) => console.log(`${(loaded / total * 100).toFixed(0)}%`))
* ```
*
* @param onProgress - A callback that will be called one or multiple times with the number of bytes loaded and the total number of bytes to load.
*/
progress: <T, C extends ProgressResolver, R>(
this: C & WretchResponseChain<T, C, R>,
onProgress: (loaded: number, total: number) => void
) => this
}

/**
* Adds the ability to monitor progress when downloading a response.
*
* _Compatible with all platforms implementing the [TransformStream WebAPI](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream#browser_compatibility)._
*
* ```js
* import ProgressAddon from "wretch/addons/progress"
*
* wretch("some_url")
* // Register the addon
* .addon(ProgressAddon())
* .get()
* // Log the progress as a percentage of completion
* .progress((loaded, total) => console.log(`${(loaded / total * 100).toFixed(0)}%`))
* ```
*/
const progress: () => WretchAddon<unknown, ProgressResolver> = () => {
const cb = {
ref: null
}

const transformMiddleware: ConfiguredMiddleware = next => (url, opts) => {
let loaded = 0
let total = 0
return next(url, opts).then(response => {
try {
const contentLength = response.headers.get("content-length")
total = contentLength ? +contentLength : null
const transform = new TransformStream({
transform(chunk, controller) {
loaded += chunk.length
if (total < loaded) {
total = loaded
}
if (cb.ref) {
cb.ref(loaded, total)
}
controller.enqueue(chunk)
}
})
return new Response(response.body.pipeThrough(transform), response)
} catch (e) {
return response
}
})
}

return {
beforeRequest(wretch) {
return wretch._middlewares.push(transformMiddleware)
},
resolver: {
progress(onProgress: (loaded: number, total: number) => void) {
cb.ref = onProgress
return this
}
},
}
}

export default progress

0 comments on commit 2bae524

Please sign in to comment.