1- import type { ConfiguredMiddleware , Wretch , WretchAddon , WretchResponseChain } from "../types.js"
1+ import type { ConfiguredMiddleware , Wretch , WretchAddon , WretchOptions , WretchResponseChain } from "../types.js"
22
33export type ProgressCallback = ( loaded : number , total : number ) => void
44
@@ -105,6 +105,22 @@ function toStream<T extends Request | Response>(requestOrResponse: T, bodySize:
105105 }
106106}
107107
108+ const defaultGetUploadTotal = async ( url : string , opts : WretchOptions ) : Promise < number > => {
109+ let total =
110+ opts . body instanceof ArrayBuffer ? + opts . body . byteLength :
111+ opts . body instanceof Blob ? + opts . body . size :
112+ 0
113+ try {
114+ // Try to determine body size by reading it as a blob
115+ total ||= ( await new Request ( url , opts ) . blob ( ) ) . size
116+ } catch {
117+ // Cannot determine body size
118+ }
119+
120+ return total
121+ }
122+
123+
108124/**
109125 * Adds the ability to monitor progress when downloading a response.
110126 *
@@ -121,7 +137,22 @@ function toStream<T extends Request | Response>(requestOrResponse: T, bodySize:
121137 * .progress((loaded, total) => console.log(`${(loaded / total * 100).toFixed(0)}%`))
122138 * ```
123139 */
124- const progress : ( ) => WretchAddon < ProgressAddon , ProgressResolver > = ( ) => {
140+ const progress : ( options ?: {
141+ /**
142+ * Function used to determine the total upload size before streaming the request body.
143+ * Receives the final request URL and options, returns the total byte size (sync or async). Defaults to trying the `byteLength` property
144+ * for `ArrayBuffer` and the `.size` property for `Blob` (e.g., `FormData` or `File`), then falling back to `Request#blob()` when available.
145+ *
146+ * _Note_: The fallback of using `Request#blob()` is memory consuming as it loads the entire body into memory.
147+ *
148+ * @param url The request URL
149+ * @param opts The request options
150+ * @returns The total upload size in bytes
151+ */
152+ getUploadTotal ?: ( url : string , opts : WretchOptions ) => number | Promise < number >
153+ } ) => WretchAddon < ProgressAddon , ProgressResolver > = ( {
154+ getUploadTotal = defaultGetUploadTotal
155+ } = { } ) => {
125156 function downloadMiddleware ( state : Record < any , any > ) : ConfiguredMiddleware {
126157 return next => ( url , opts ) => {
127158 return next ( url , opts ) . then ( response => {
@@ -141,16 +172,11 @@ const progress: () => WretchAddon<ProgressAddon, ProgressResolver> = () => {
141172 return next ( url , opts )
142173 }
143174
144- let bodySize : number = 0
145-
146- try {
147- bodySize = ( await new Request ( "a:" , { method : "POST" , body } ) . blob ( ) ) . size
148- } catch {
149- // Unable to determine body size
150- }
151-
152- const request = toStream ( new Request ( url , opts ) , bodySize , state . upload )
153- return next ( request . url , request )
175+ const streameableRequest = toStream (
176+ new Request ( url , opts ) ,
177+ await getUploadTotal ( url , opts ) , state . upload
178+ )
179+ return next ( url , streameableRequest )
154180 }
155181 }
156182
0 commit comments