{"version":3,"file":"progress.min.cjs","names":[],"sources":["../../../src/addons/progress.ts"],"sourcesContent":["import type { ConfiguredMiddleware, Wretch, WretchAddon, WretchOptions, WretchResponseChain } from \"../types.js\"\n\nexport type ProgressCallback = (loaded: number, total: number) => void\n\nexport interface ProgressResolver {\n  /**\n   * Provides a way to register a callback to be invoked one or multiple times during the download.\n   * The callback receives the current progress as two arguments, the number of bytes downloaded and the total number of bytes to download.\n   *\n   * _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._\n   *\n   * ```js\n   * import ProgressAddon from \"wretch/addons/progress\"\n   *\n   * wretch(\"some_url\")\n   *   .addon(ProgressAddon())\n   *   .get()\n   *   .progress((loaded, total) => console.log(`${(loaded / total * 100).toFixed(0)}%`))\n   * ```\n   *\n   * @param onProgress - A callback function for download progress\n   */\n  progress: <T, C extends ProgressResolver, R>(\n    this: C & WretchResponseChain<T, C, R>,\n    onProgress: ProgressCallback\n  ) => this\n}\n\nexport interface ProgressAddon {\n  /**\n   * Provides a way to register a callback to be invoked one or multiple times during the upload.\n   * The callback receives the current progress as two arguments, the number of bytes uploaded and the total number of bytes to upload.\n   *\n   * **Browser Limitations:**\n   * - **Firefox**: Does not support request body streaming (request.body is not readable). Upload progress monitoring will not work.\n   * - **Chrome/Chromium**: Requires HTTPS connections (HTTP/2). Will fail with `ERR_ALPN_NEGOTIATION_FAILED` on HTTP servers.\n   * - **Node.js**: Full support for both HTTP and HTTPS.\n   *\n   * _Compatible with platforms implementing the [TransformStream WebAPI](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream#browser_compatibility) and supporting request body streaming._\n   *\n   * ```js\n   * import ProgressAddon from \"wretch/addons/progress\"\n   *\n   * wretch(\"https://example.com/upload\") // Note: HTTPS required for Chrome\n   *   .addon(ProgressAddon())\n   *   .onUpload((loaded, total) => console.log(`Upload: ${(loaded / total * 100).toFixed(0)}%`))\n   *   .post(formData)\n   *   .res()\n   * ```\n   *\n   * @param onUpload - A callback that will be called one or multiple times with the number of bytes uploaded and the total number of bytes to upload.\n   */\n  onUpload<T extends ProgressAddon, C, R, E>(this: T & Wretch<T, C, R, E>, onUpload: (loaded: number, total: number) => void): this\n\n  /**\n   * Provides a way to register a callback to be invoked one or multiple times during the download.\n   * The callback receives the current progress as two arguments, the number of bytes downloaded and the total number of bytes to download.\n   *\n   * _Compatible with all platforms implementing the [TransformStream WebAPI](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream#browser_compatibility)._\n   *\n   * ```js\n   * import ProgressAddon from \"wretch/addons/progress\"\n   *\n   * wretch(\"some_url\")\n   *   .addon(ProgressAddon())\n   *   .onDownload((loaded, total) => console.log(`Download: ${(loaded / total * 100).toFixed(0)}%`))\n   *   .get()\n   *   .res()\n   * ```\n   *\n   * @param onDownload - A callback that will be called one or multiple times with the number of bytes downloaded and the total number of bytes to download.\n   */\n  onDownload<T extends ProgressAddon, C, R, E>(this: T & Wretch<T, C, R, E>, onDownload: (loaded: number, total: number) => void): this\n}\n\nfunction toStream<T extends Request | Response>(requestOrResponse: T, bodySize: number, callback: ProgressCallback | undefined): T {\n  try {\n    const contentLength = requestOrResponse.headers.get(\"content-length\")\n    let total = bodySize || (contentLength ? +contentLength : 0)\n    let loaded = 0\n    const transform = new TransformStream({\n      start() {\n        callback?.(loaded, total)\n      },\n      transform(chunk, controller) {\n        loaded += chunk.length\n        if (total < loaded) {\n          total = loaded\n        }\n        callback?.(loaded, total)\n        controller.enqueue(chunk)\n      }\n    })\n\n    const stream = requestOrResponse.body.pipeThrough(transform)\n\n    if(\"status\" in requestOrResponse) {\n      return new Response(stream, requestOrResponse) as T\n    } else {\n      // @ts-expect-error RequestInit does not yet include duplex\n      return new Request(requestOrResponse, { body: stream, duplex: \"half\" }) as T\n    }\n  } catch {\n    return requestOrResponse\n  }\n}\n\nconst defaultGetUploadTotal = async (url: string, opts: WretchOptions): Promise<number> => {\n  let total =\n          opts.body instanceof ArrayBuffer ? +opts.body.byteLength :\n            opts.body instanceof Blob ? +opts.body.size :\n              0\n  try {\n    // Try to determine body size by reading it as a blob\n    total ||= (await new Request(url, opts).blob()).size\n  } catch {\n    // Cannot determine body size\n  }\n\n  return total\n}\n\n\n/**\n * Adds the ability to monitor progress when downloading a response.\n *\n * _Compatible with all platforms implementing the [TransformStream WebAPI](https://developer.mozilla.org/en-US/docs/Web/API/TransformStream#browser_compatibility)._\n *\n * ```js\n * import ProgressAddon from \"wretch/addons/progress\"\n *\n * wretch(\"some_url\")\n *   // Register the addon\n *   .addon(ProgressAddon())\n *   .get()\n *   // Log the progress as a percentage of completion\n *   .progress((loaded, total) => console.log(`${(loaded / total * 100).toFixed(0)}%`))\n * ```\n */\nconst progress: (options?: {\n  /**\n   * Function used to determine the total upload size before streaming the request body.\n   * Receives the final request URL and options, returns the total byte size (sync or async). Defaults to trying the `byteLength` property\n   * for `ArrayBuffer` and the `.size` property for `Blob` (e.g., `FormData` or `File`), then falling back to `Request#blob()` when available.\n   *\n   * _Note_: The fallback of using `Request#blob()` is memory consuming as it loads the entire body into memory.\n   *\n   * @param url The request URL\n   * @param opts The request options\n   * @returns The total upload size in bytes\n   */\n  getUploadTotal?: (url: string, opts: WretchOptions) => number | Promise<number>\n}) => WretchAddon<ProgressAddon, ProgressResolver> = ({\n  getUploadTotal = defaultGetUploadTotal\n} = {}) => {\n  function downloadMiddleware(state: Record<any, any>) : ConfiguredMiddleware {\n    return next => (url, opts) => {\n      return next(url, opts).then(response => {\n        if (!state.progress) {\n          return response\n        }\n        return toStream(response, 0, state.progress)\n      })\n    }\n  }\n\n  function uploadMiddleware(state: Record<any, any>): ConfiguredMiddleware {\n    return next => async (url, opts) => {\n      const body = opts.body\n\n      if (!body || !state.upload) {\n        return next(url, opts)\n      }\n\n      const streameableRequest = toStream(\n        new Request(url, opts),\n        await getUploadTotal(url, opts), state.upload\n      )\n      return next(url, streameableRequest)\n    }\n  }\n\n  return {\n    beforeRequest(wretch, options, state) {\n      const middlewares = []\n      if (options.__uploadProgressCallback) {\n        state.upload = options.__uploadProgressCallback\n        delete options.__uploadProgressCallback\n      }\n      if (options.__downloadProgressCallback) {\n        state.progress = options.__downloadProgressCallback\n        delete options.__downloadProgressCallback\n      }\n      middlewares.push(uploadMiddleware(state))\n      middlewares.push(downloadMiddleware(state))\n      return wretch.middlewares(middlewares)\n    },\n    wretch: {\n      onUpload(onUpload: (loaded: number, total: number) => void) {\n        return this.options({ __uploadProgressCallback: onUpload })\n      },\n      onDownload(onDownload: (loaded: number, total: number) => void) {\n        return this.options({ __downloadProgressCallback: onDownload })\n      }\n    },\n    resolver: {\n      progress(onProgress: ProgressCallback) {\n        this._sharedState.progress = onProgress\n        return this\n      }\n    },\n  }\n}\n\nexport default progress\n"],"mappings":"AA2EA,SAAS,EAAuC,EAAsB,EAAkB,EAA2C,CACjI,GAAI,CACF,IAAM,EAAgB,EAAkB,QAAQ,IAAI,iBAAiB,CACjE,EAAQ,IAAa,EAAgB,CAAC,EAAgB,GACtD,EAAS,EACP,EAAY,IAAI,gBAAgB,CACpC,OAAQ,CACN,IAAW,EAAQ,EAAM,EAE3B,UAAU,EAAO,EAAY,CAC3B,GAAU,EAAM,OACZ,EAAQ,IACV,EAAQ,GAEV,IAAW,EAAQ,EAAM,CACzB,EAAW,QAAQ,EAAM,EAE5B,CAAC,CAEI,EAAS,EAAkB,KAAK,YAAY,EAAU,CAM1D,MAJC,WAAY,EACN,IAAI,SAAS,EAAQ,EAAkB,CAGvC,IAAI,QAAQ,EAAmB,CAAE,KAAM,EAAQ,OAAQ,OAAQ,CAAC,MAEnE,CACN,OAAO,GAIX,MAAM,EAAwB,MAAO,EAAa,IAAyC,CACzF,IAAI,EACI,EAAK,gBAAgB,YAAc,CAAC,EAAK,KAAK,WAC5C,EAAK,gBAAgB,KAAO,CAAC,EAAK,KAAK,KACrC,EACZ,GAAI,CAEF,KAAW,MAAM,IAAI,QAAQ,EAAK,EAAK,CAAC,MAAM,EAAE,UAC1C,EAIR,OAAO,GAoBH,GAagD,CACpD,iBAAiB,GACf,EAAE,GAAK,CACT,SAAS,EAAmB,EAAgD,CAC1E,MAAO,KAAS,EAAK,IACZ,EAAK,EAAK,EAAK,CAAC,KAAK,GACrB,EAAM,SAGJ,EAAS,EAAU,EAAG,EAAM,SAAS,CAFnC,EAGT,CAIN,SAAS,EAAiB,EAA+C,CACvE,MAAO,IAAQ,MAAO,EAAK,IAGrB,CAFS,EAAK,MAEL,CAAC,EAAM,OACX,EAAK,EAAK,EAAK,CAOjB,EAAK,EAJe,EACzB,IAAI,QAAQ,EAAK,EAAK,CACtB,MAAM,EAAe,EAAK,EAAK,CAAE,EAAM,OACxC,CACmC,CAIxC,MAAO,CACL,cAAc,EAAQ,EAAS,EAAO,CACpC,IAAM,EAAc,EAAE,CAWtB,OAVI,EAAQ,2BACV,EAAM,OAAS,EAAQ,yBACvB,OAAO,EAAQ,0BAEb,EAAQ,6BACV,EAAM,SAAW,EAAQ,2BACzB,OAAO,EAAQ,4BAEjB,EAAY,KAAK,EAAiB,EAAM,CAAC,CACzC,EAAY,KAAK,EAAmB,EAAM,CAAC,CACpC,EAAO,YAAY,EAAY,EAExC,OAAQ,CACN,SAAS,EAAmD,CAC1D,OAAO,KAAK,QAAQ,CAAE,yBAA0B,EAAU,CAAC,EAE7D,WAAW,EAAqD,CAC9D,OAAO,KAAK,QAAQ,CAAE,2BAA4B,EAAY,CAAC,EAElE,CACD,SAAU,CACR,SAAS,EAA8B,CAErC,MADA,MAAK,aAAa,SAAW,EACtB,MAEV,CACF"}