wretch
Version:
A tiny wrapper built around fetch with an intuitive syntax.
1 lines • 50.7 kB
Source Map (JSON)
{"version":3,"file":"wretch.all.min.mjs","names":["Addons.abortAddon","Addons.basicAuthAddon","Addons.formDataAddon","Addons.formUrlAddon","Addons.perfsAddon","Addons.queryStringAddon","Addons.progressAddon"],"sources":["../../src/utils.ts","../../src/constants.ts","../../src/middleware.ts","../../src/resolver.ts","../../src/core.ts","../../src/addons/abort.ts","../../src/addons/basicAuth.ts","../../src/addons/formData.ts","../../src/addons/formUrl.ts","../../src/addons/perfs.ts","../../src/addons/queryString.ts","../../src/addons/progress.ts","../../src/index.all.ts"],"sourcesContent":["export function extractContentType(headers: HeadersInit = {}): string | undefined {\n const normalizedHeaders = headers instanceof Array ? Object.fromEntries(headers) : headers\n for (const k in normalizedHeaders) {\n if (k.toLowerCase() === \"content-type\") return normalizedHeaders[k]\n }\n}\n\nexport function isLikelyJsonMime(value: string): boolean {\n return /^application\\/.*json/.test(value)\n}\n\nexport const mix = (one: object, two: object, mergeArrays = false) => {\n const acc = { ...one }\n for (const key in two) {\n if (!Object.prototype.hasOwnProperty.call(two, key)) continue\n const value = one[key]\n const newValue = two[key]\n acc[key] = Array.isArray(value) && Array.isArray(newValue) ?\n mergeArrays ? [...value, ...newValue] : newValue :\n typeof value === \"object\" && typeof newValue === \"object\" ?\n mix(value, newValue, mergeArrays) :\n newValue\n }\n return acc\n}\n","export const JSON_MIME = \"application/json\"\nexport const FETCH_ERROR = Symbol()\nexport const CATCHER_FALLBACK = Symbol()\n","import type { ConfiguredMiddleware, FetchLike } from \"./types.js\"\n\n/**\n * @private @internal\n */\nexport const middlewareHelper = (middlewares: ConfiguredMiddleware[]) => (fetchFunction: FetchLike): FetchLike =>\n middlewares.reduceRight((acc, curr) => curr(acc), fetchFunction)\n","import { middlewareHelper } from \"./middleware.js\"\nimport type { Wretch, WretchResponse, WretchResponseChain, WretchError as WretchErrorType } from \"./types.js\"\nimport { CATCHER_FALLBACK, FETCH_ERROR } from \"./constants.js\"\n\n/**\n * This class inheriting from Error is thrown when the fetch response is not \"ok\".\n * It extends Error and adds status, text and body fields.\n */\nexport class WretchError extends Error implements WretchErrorType {\n status: number\n response: WretchResponse\n url: string\n}\n\nexport const resolver = <T, Chain, R, E>(wretch: T & Wretch<T, Chain, R, E>) => {\n const sharedState = Object.create(null)\n\n wretch = wretch._addons.reduce((w, addon) =>\n addon.beforeRequest &&\n addon.beforeRequest(w, wretch._options, sharedState)\n || w,\n wretch)\n\n const {\n _url: url,\n _options: opts,\n _fetch: customFetch,\n _errorTransformer: errorTransformer,\n _catchers: _catchers,\n _resolvers: resolvers,\n _middlewares: middlewares,\n _addons: addons\n } = wretch\n\n const catchers = new Map(_catchers)\n const finalOptions = opts\n\n // The generated fetch request\n let finalUrl = url\n const _fetchReq = middlewareHelper(middlewares)((url, options) => {\n finalUrl = url\n const fetchImpl = customFetch || fetch\n return fetchImpl(url, options)\n })(url, finalOptions)\n // Throws on an http error\n const referenceError = new Error()\n const throwingPromise: Promise<void | WretchResponse> = _fetchReq\n .then(async response => {\n if (!response.ok) {\n const err = new WretchError()\n err[\"cause\"] = referenceError\n err.stack += \"\\nCAUSE: \" + referenceError.stack\n err.response = response\n err.status = response.status\n err.url = finalUrl\n\n if (response.type === \"opaque\" || errorTransformer) {\n err.message = response.statusText\n } else {\n try {\n err.message = await response.clone().text()\n } catch {\n err.message = response.statusText\n }\n }\n\n throw err\n }\n return response\n })\n // Wraps the Promise in order to dispatch the error to a matching catcher\n const catchersWrapper = <T>(promise: Promise<T>): Promise<void | T> =>\n promise.catch(async error => {\n\n const catcher =\n catchers.get(error?.status) ||\n catchers.get(error?.name) ||\n (!(error instanceof WretchError) && catchers.get(FETCH_ERROR)) ||\n catchers.get(CATCHER_FALLBACK)\n\n if(error.response && errorTransformer) {\n error = await errorTransformer(error, error.response, wretch)\n }\n\n if (catcher)\n return catcher(error, wretch)\n\n throw error\n })\n // Enforces the proper promise type when a body parsing method is called.\n type BodyParser =\n <Type>(funName: \"json\" | \"blob\" | \"formData\" | \"arrayBuffer\" | \"text\" | null)\n => <Result = void>(cb?: (type: Type) => Result)\n => Promise<Awaited<Result>>\n const bodyParser: BodyParser = funName => cb => {\n const promise = funName ?\n // If a callback is provided, then callback with the body result otherwise return the parsed body itself.\n throwingPromise.then(_ => _?.[funName]()) :\n // No body parsing method - return the response\n throwingPromise\n return catchersWrapper(cb ? promise.then(cb) : promise)\n }\n\n const responseChain: WretchResponseChain<T, Chain, R, E> = {\n _wretchReq: wretch,\n _fetchReq,\n _sharedState: sharedState,\n res: bodyParser<WretchResponse>(null),\n json: bodyParser<any>(\"json\"),\n blob: bodyParser<Blob>(\"blob\"),\n formData: bodyParser<FormData>(\"formData\"),\n arrayBuffer: bodyParser<ArrayBuffer>(\"arrayBuffer\"),\n text: bodyParser<string>(\"text\"),\n error(errorId, cb) {\n catchers.set(errorId, cb)\n return this\n },\n badRequest(cb) { return this.error(400, cb) },\n unauthorized(cb) { return this.error(401, cb) },\n forbidden(cb) { return this.error(403, cb) },\n notFound(cb) { return this.error(404, cb) },\n timeout(cb) { return this.error(408, cb) },\n internalError(cb) { return this.error(500, cb) },\n fetchError(cb) { return this.error(FETCH_ERROR, cb) },\n }\n\n const enhancedResponseChain: R extends undefined ? Chain & WretchResponseChain<T, Chain, undefined> : R = addons.reduce((chain, addon) => ({\n ...chain,\n ...(typeof addon.resolver === \"function\" ? (addon.resolver as (_: WretchResponseChain<T, Chain, R, E>) => any)(chain) : addon.resolver)\n }), responseChain)\n\n return resolvers.reduce((chain, r) => r(chain, wretch), enhancedResponseChain)\n}\n","import { mix, extractContentType, isLikelyJsonMime } from \"./utils.js\"\nimport { JSON_MIME, CATCHER_FALLBACK } from \"./constants.js\"\nimport { resolver, WretchError } from \"./resolver.js\"\nimport type { Wretch, WretchOptions } from \"./types.js\"\n\nexport const core: Wretch = {\n _url: \"\",\n _options: {},\n _catchers: new Map(),\n _resolvers: [],\n _deferred: [],\n _middlewares: [],\n _addons: [],\n addon(addon) {\n const addons = Array.isArray(addon) ? addon : [addon]\n const wretchProps = addons.reduce((acc, a) => ({ ...acc, ...a.wretch }), {})\n return { ...this, _addons: [...this._addons, ...addons], ...wretchProps }\n },\n fetchPolyfill(fetchImpl) {\n return { ...this, _fetch: fetchImpl }\n },\n url(_url, replace = false) {\n if (replace)\n return { ...this, _url }\n const idx = this._url.indexOf(\"?\")\n return {\n ...this,\n _url: idx > -1 ?\n this._url.slice(0, idx) + _url + this._url.slice(idx) :\n this._url + _url\n }\n },\n options(options, replace = false) {\n return { ...this, _options: replace ? options : mix(this._options, options) }\n },\n headers(headerValues) {\n const headers =\n !headerValues ? {} :\n Array.isArray(headerValues) ? Object.fromEntries(headerValues) :\n \"entries\" in headerValues ? Object.fromEntries((headerValues as Headers).entries()) :\n headerValues\n return { ...this, _options: mix(this._options, { headers }) }\n },\n accept(headerValue) {\n return this.headers({ Accept: headerValue })\n },\n content(headerValue) {\n return this.headers({ \"Content-Type\": headerValue })\n },\n auth(headerValue) {\n return this.headers({ Authorization: headerValue })\n },\n catcher(ids, catcher) {\n const newMap = new Map(this._catchers)\n const errorIds = Array.isArray(ids) ? ids : [ids]\n for (const errorId of errorIds) {\n newMap.set(errorId, catcher)\n }\n return { ...this, _catchers: newMap }\n },\n catcherFallback(catcher) {\n return this.catcher(CATCHER_FALLBACK, catcher)\n },\n customError(transformer) {\n return { ...this, _errorTransformer: transformer } as any\n },\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n resolve<R = unknown>(resolver, clear: boolean = false) {\n return { ...this, _resolvers: clear ? [resolver] : [...this._resolvers, resolver] }\n },\n defer(callback, clear: boolean = false) {\n return {\n ...this,\n _deferred: clear ? [callback] : [...this._deferred, callback]\n }\n },\n middlewares(middlewares, clear = false) {\n return {\n ...this,\n _middlewares: clear ? middlewares : [...this._middlewares, ...middlewares]\n }\n },\n fetch(method: string = this._options.method, url = \"\", body = null) {\n let base = this.url(url).options({ method })\n // \"Jsonify\" the body if it is an object and if it is likely that the content type targets json.\n const contentType = extractContentType(base._options.headers)\n const jsonify =\n typeof body === \"object\" &&\n !(body instanceof FormData) &&\n (!base._options.headers || !contentType || isLikelyJsonMime(contentType))\n base =\n !body ? base :\n jsonify ? base.json(body, contentType) :\n base.body(body)\n return resolver(\n base\n ._deferred\n .reduce((acc: Wretch, curr) => curr(acc, acc._url, acc._options), base)\n )\n },\n get(url = \"\") {\n return this.fetch(\"GET\", url)\n },\n delete(url = \"\") {\n return this.fetch(\"DELETE\", url)\n },\n put(body, url = \"\") {\n return this.fetch(\"PUT\", url, body)\n },\n post(body, url = \"\") {\n return this.fetch(\"POST\", url, body)\n },\n patch(body, url = \"\") {\n return this.fetch(\"PATCH\", url, body)\n },\n head(url = \"\") {\n return this.fetch(\"HEAD\", url)\n },\n opts(url = \"\") {\n return this.fetch(\"OPTIONS\", url)\n },\n body(contents) {\n return { ...this, _options: { ...this._options, body: contents } }\n },\n json(jsObject, contentType) {\n const currentContentType = extractContentType(this._options.headers)\n return this.content(\n contentType ||\n isLikelyJsonMime(currentContentType) && currentContentType ||\n JSON_MIME\n ).body(JSON.stringify(jsObject))\n },\n toFetch() {\n return (fetchUrl, fetchOptions: WretchOptions = {}) => {\n return this\n .url(fetchUrl)\n .options(fetchOptions)\n .catcherFallback(error => {\n if(error instanceof WretchError) { return error.response }\n else { throw error }\n })\n .fetch()\n .res()\n }\n }\n}\n","import type { Wretch, WretchAddon, WretchErrorCallback, WretchResponseChain } from \"../types.js\"\n\nexport interface AbortWretch {\n /**\n * Associates a custom controller with the request.\n *\n * Useful when you need to use\n * your own AbortController, otherwise wretch will create a new controller itself.\n *\n * ```js\n * const controller = new AbortController()\n *\n * // Associates the same controller with multiple requests\n * wretch(\"url1\")\n * .addon(AbortAddon())\n * .signal(controller)\n * .get()\n * .json()\n * wretch(\"url2\")\n * .addon(AbortAddon())\n * .signal(controller)\n * .get()\n * .json()\n *\n * // Aborts both requests\n * controller.abort()\n * ```\n *\n * @param controller - An instance of AbortController\n */\n signal: <T extends AbortWretch, C, R, E>(this: T & Wretch<T, C, R, E>, controller: AbortController) => this\n}\n\n/**\n * Options for the setTimeout method.\n */\nexport type SetTimeoutOptions = {\n /**\n * A custom AbortController to use for aborting the request.\n * If not provided, the controller associated with the request will be used.\n */\n controller?: AbortController\n}\n\nexport interface AbortResolver {\n /**\n * Aborts the request after a fixed time.\n *\n * If you use a custom AbortController associated with the request, pass it in the options object.\n *\n * ```js\n * // 1 second timeout\n * wretch(\"...\").addon(AbortAddon()).get().setTimeout(1000).json(_ =>\n * // will not be called if the request timeouts\n * )\n *\n * // With custom controller\n * wretch(\"...\").addon(AbortAddon()).get().setTimeout(1000, { controller }).json()\n * ```\n *\n * @param time - Time in milliseconds\n * @param options - Optional configuration object\n */\n setTimeout: <T, C extends AbortResolver, R, E>(this: C & WretchResponseChain<T, C, R, E>, time: number, options?: SetTimeoutOptions) => this\n /**\n * Returns the provided or generated AbortController plus the wretch response chain as a pair.\n *\n * ```js\n * // We need the controller outside the chain\n * const [c, w] = wretch(\"url\")\n * .addon(AbortAddon())\n * .get()\n * .controller()\n *\n * // Resume with the chain\n * w.onAbort(_ => console.log(\"ouch\")).json()\n *\n * // Later on…\n * c.abort()\n * ```\n */\n controller: <T, C extends AbortResolver, R, E>(this: C & WretchResponseChain<T, C, R, E>) => [any, this]\n /**\n * Catches an AbortError and performs a callback.\n */\n onAbort: <T, C extends AbortResolver, R, E>(this: C & WretchResponseChain<T, C, R, E>, cb: WretchErrorCallback<T, C, R, E>) => this\n}\n\n/**\n * Adds the ability to abort requests using AbortController and signals under the hood.\n *\n * ```js\n * import AbortAddon from \"wretch/addons/abort\"\n *\n * const [c, w] = wretch(\"...\")\n * .addon(AbortAddon())\n * .get()\n * .onAbort((_) => console.log(\"Aborted !\"))\n * .controller();\n *\n * w.text((_) => console.log(\"should never be called\"));\n * c.abort();\n *\n * // Or :\n *\n * const controller = new AbortController();\n *\n * wretch(\"...\")\n * .addon(AbortAddon())\n * .signal(controller)\n * .get()\n * .onAbort((_) => console.log(\"Aborted !\"))\n * .text((_) => console.log(\"should never be called\"));\n *\n * controller.abort();\n * ```\n */\nconst abort: () => WretchAddon<AbortWretch, AbortResolver> = () => {\n return {\n beforeRequest(wretch, options, state) {\n const fetchController = new AbortController()\n if (!options[\"signal\"]) {\n options[\"signal\"] = fetchController.signal\n }\n const timeout = {\n ref: null,\n clear() {\n if (timeout.ref) {\n clearTimeout(timeout.ref)\n timeout.ref = null\n }\n }\n }\n state.abort = {\n timeout,\n fetchController\n }\n return wretch\n },\n wretch: {\n signal(controller) {\n return { ...this, _options: { ...this._options, signal: controller.signal } }\n },\n },\n resolver: {\n setTimeout(time, options = {}) {\n const controller = options.controller ?? this._sharedState.abort.fetchController\n const { timeout } = this._sharedState.abort\n timeout.clear()\n timeout.ref = setTimeout(() => controller.abort(), time)\n return this\n },\n controller() { return [this._sharedState.abort.fetchController, this] },\n onAbort(cb) { return this.error(\"AbortError\", cb) }\n },\n }\n}\n\nexport default abort\n","import type { ConfiguredMiddleware, Wretch, WretchAddon } from \"../types.js\"\n\nfunction utf8ToBase64(input: string) {\n const utf8Bytes = new TextEncoder().encode(input)\n return btoa(String.fromCharCode(...utf8Bytes))\n}\n\nexport interface BasicAuthAddon {\n /**\n * Sets the `Authorization` header to `Basic ` + <base64 encoded credentials>.\n * Additionally, allows using URLs with credentials in them.\n *\n * ```js\n * const user = \"user\"\n * const pass = \"pass\"\n *\n * // Automatically sets the Authorization header to \"Basic \" + <base64 encoded credentials>\n * wretch(\"...\").addon(BasicAuthAddon).basicAuth(user, pass).get()\n *\n * // Allows using URLs with credentials in them\n * wretch(`https://${user}:${pass}@...`).addon(BasicAuthAddon).get()\n * ```\n *\n * @param username - Username to use for basic auth\n * @param password - Password to use for basic auth\n */\n basicAuth<T extends BasicAuthAddon, C, R, E>(\n this: T & Wretch<T, C, R, E>,\n username: string,\n password: string\n ): this\n}\n\nconst makeBasicAuthMiddleware: () => ConfiguredMiddleware = () => next => (url, opts) => {\n let parsedUrl: URL | null\n try {\n parsedUrl = new URL(url)\n } catch {\n parsedUrl = null\n }\n\n if (parsedUrl?.username || parsedUrl?.password) {\n const basicAuthBase64 = utf8ToBase64(\n `${decodeURIComponent(parsedUrl.username)}:${decodeURIComponent(parsedUrl.password)}`,\n )\n opts.headers = {\n ...opts.headers,\n Authorization: `Basic ${basicAuthBase64}`,\n }\n parsedUrl.username = \"\"\n parsedUrl.password = \"\"\n url = parsedUrl.toString()\n }\n\n return next(url, opts)\n}\n\n\n/**\n * Adds the ability to use basic auth with the `Authorization` header.\n *\n * ```js\n * import BasicAuthAddon from \"wretch/addons/basicAuth\"\n *\n * wretch().addon(BasicAuthAddon)\n * ```\n */\nconst basicAuth: WretchAddon<BasicAuthAddon> = {\n beforeRequest(wretch) {\n return wretch.middlewares([makeBasicAuthMiddleware()])\n },\n wretch: {\n basicAuth(username, password) {\n const basicAuthBase64 = utf8ToBase64(`${username}:${password}`)\n return this.auth(`Basic ${basicAuthBase64}`)\n },\n },\n}\n\nexport default basicAuth\n","import type { Wretch, WretchAddon } from \"../types.js\"\n\n/**\n * Options for the formData method.\n */\nexport type FormDataOptions = {\n /**\n * Enable recursion through nested objects to produce `object[key]` keys.\n * When set to `true`, all nested objects will be recursively converted.\n * When set to an array of strings, the specified keys will be excluded from recursion.\n */\n recursive?: string[] | boolean\n}\n\nfunction convertFormData(\n formObject: object,\n recursive: string[] | boolean = false,\n formData = new FormData(),\n ancestors = [] as string[],\n) {\n Object.entries(formObject).forEach(([key, value]) => {\n let formKey = ancestors.reduce((acc, ancestor) => (\n acc ? `${acc}[${ancestor}]` : ancestor\n ), null)\n formKey = formKey ? `${formKey}[${key}]` : key\n if (value instanceof Array || (globalThis.FileList && value instanceof FileList)) {\n for (const item of value as File[])\n formData.append(formKey, item)\n } else if (\n recursive &&\n typeof value === \"object\" &&\n (\n !(recursive instanceof Array) ||\n !recursive.includes(key)\n )\n ) {\n if (value !== null) {\n convertFormData(value, recursive, formData, [...ancestors, key])\n }\n } else {\n formData.append(formKey, value)\n }\n })\n\n return formData\n}\n\nexport interface FormDataAddon {\n /**\n * Converts the javascript object to a FormData and sets the request body.\n *\n * ```js\n * const form = {\n * hello: \"world\",\n * duck: \"Muscovy\",\n * };\n *\n * wretch(\"...\").addons(FormDataAddon).formData(form).post();\n * ```\n *\n * The `recursive` option when set to `true` will enable recursion through all\n * nested objects and produce `object[key]` keys. It can be set to an array of\n * string to exclude specific keys.\n *\n * > Warning: Be careful to exclude `Blob` instances in the Browser, and\n * > `ReadableStream` and `Buffer` instances when using the node.js compatible\n * > `form-data` package.\n *\n * ```js\n * const form = {\n * duck: \"Muscovy\",\n * duckProperties: {\n * beak: {\n * color: \"yellow\",\n * },\n * legs: 2,\n * },\n * ignored: {\n * key: 0,\n * },\n * };\n *\n * // Will append the following keys to the FormData payload:\n * // \"duck\", \"duckProperties[beak][color]\", \"duckProperties[legs]\"\n * wretch(\"...\").addons(FormDataAddon).formData(form, { recursive: [\"ignored\"] }).post();\n * ```\n *\n * > Note: This addon does not support specifying a custom `filename`.\n * > If you need to do so, you can use the `body` method directly:\n * > ```js\n * > const form = new FormData();\n * > form.append(\"hello\", \"world\", \"hello.txt\");\n * > wretch(\"...\").body(form).post();\n * > ```\n * > See: https://developer.mozilla.org/en-US/docs/Web/API/FormData/append#example\n *\n * @param formObject - An object which will be converted to a FormData\n * @param options - Optional configuration object\n */\n formData<T extends FormDataAddon, C, R, E>(this: T & Wretch<T, C, R, E>, formObject: object, options?: FormDataOptions): this\n}\n\n/**\n * Adds the ability to convert a an object to a FormData and use it as a request body.\n *\n * ```js\n * import FormDataAddon from \"wretch/addons/formData\"\n *\n * wretch().addon(FormDataAddon)\n * ```\n */\nconst formData: WretchAddon<FormDataAddon> = {\n wretch: {\n formData(formObject, options = {}) {\n return this.body(convertFormData(formObject, options.recursive ?? false))\n }\n }\n}\n\nexport default formData\n","import type { Wretch, WretchAddon } from \"../types.js\"\n\nfunction encodeQueryValue(key: string, value: unknown) {\n return encodeURIComponent(key) +\n \"=\" +\n encodeURIComponent(\n typeof value === \"object\" ?\n JSON.stringify(value) :\n \"\" + value\n )\n}\nfunction convertFormUrl(formObject: object) {\n return Object.keys(formObject)\n .map(key => {\n const value = formObject[key]\n if (value instanceof Array) {\n return value.map(v => encodeQueryValue(key, v)).join(\"&\")\n }\n return encodeQueryValue(key, value)\n })\n .join(\"&\")\n}\n\nexport interface FormUrlAddon {\n /**\n * Converts the input parameter to an url encoded string and sets the content-type\n * header and body. If the input argument is already a string, skips the conversion\n * part.\n *\n * ```js\n * const form = { a: 1, b: { c: 2 } };\n * const alreadyEncodedForm = \"a=1&b=%7B%22c%22%3A2%7D\";\n *\n * // Automatically sets the content-type header to \"application/x-www-form-urlencoded\"\n * wretch(\"...\").addon(FormUrlAddon).formUrl(form).post();\n * wretch(\"...\").addon(FormUrlAddon).formUrl(alreadyEncodedForm).post();\n * ```\n *\n * @param input - An object to convert into an url encoded string or an already encoded string\n */\n formUrl<T extends FormUrlAddon, C, R, E>(this: T & Wretch<T, C, R, E>, input: (object | string)): this\n}\n\n/**\n * Adds the ability to convert a an object to a FormUrl and use it as a request body.\n *\n * ```js\n * import FormUrlAddon from \"wretch/addons/formUrl\"\n *\n * wretch().addon(FormUrlAddon)\n * ```\n */\nconst formUrl: WretchAddon<FormUrlAddon> = {\n wretch: {\n formUrl(input) {\n return this\n .body(typeof input === \"string\" ? input : convertFormUrl(input))\n .content(\"application/x-www-form-urlencoded\")\n }\n }\n}\n\nexport default formUrl\n","import type { WretchResponseChain, WretchAddon } from \"../types.js\"\n\nexport type PerfCallback = (timing: any) => void\n\nexport interface PerfsAddon {\n /**\n * Performs a callback on the API performance timings of the request.\n *\n * Warning: Still experimental on browsers and node.js\n */\n perfs: <T, C extends PerfsAddon, R>(this: C & WretchResponseChain<T, C, R>, cb?: PerfCallback) => this,\n}\n\n/**\n * Adds the ability to measure requests using the Performance Timings API.\n *\n * Uses the Performance API\n * ([browsers](https://developer.mozilla.org/en-US/docs/Web/API/Performance_API) &\n * [node.js](https://nodejs.org/api/perf_hooks.html)) to expose timings related to\n * the underlying request.\n *\n * Browser timings are very accurate, node.js only contains raw measures.\n *\n * ```js\n * import PerfsAddon from \"wretch/addons/perfs\"\n *\n * // Use perfs() before the response types (text, json, ...)\n * wretch(\"...\")\n * .addon(PerfsAddon())\n * .get()\n * .perfs((timings) => {\n * // Will be called when the timings are ready.\n * console.log(timings.startTime);\n * })\n * .res();\n *\n * ```\n */\nconst perfs: () => WretchAddon<unknown, PerfsAddon> = () => {\n const callbacks = new Map<string, PerfCallback>()\n let observer /*: PerformanceObserver | null*/ = null\n\n const onMatch = (\n entries /*: PerformanceObserverEntryList */,\n name: string,\n callback: PerfCallback,\n performance: typeof globalThis.performance\n ) => {\n if (!entries.getEntriesByName)\n return false\n const matches = entries.getEntriesByName(name)\n if (matches && matches.length > 0) {\n callback(matches.reverse()[0])\n if (performance.clearMeasures)\n performance.clearMeasures(name)\n callbacks.delete(name)\n\n if (callbacks.size < 1) {\n observer.disconnect()\n if (performance.clearResourceTimings) {\n performance.clearResourceTimings()\n }\n }\n return true\n }\n return false\n }\n\n const initObserver = (\n performance: (typeof globalThis.performance) | null | undefined,\n performanceObserver /*: (typeof PerformanceObserver) | null | undefined */\n ) => {\n if (!observer && performance && performanceObserver) {\n observer = new performanceObserver(entries => {\n callbacks.forEach((callback, name) => {\n onMatch(entries, name, callback, performance)\n })\n })\n if (performance.clearResourceTimings) {\n performance.clearResourceTimings()\n }\n }\n\n return observer\n }\n\n const monitor = (\n name: string | null | undefined,\n callback: PerfCallback | null | undefined,\n ) => {\n if (!name || !callback)\n return\n\n if (!initObserver(performance, PerformanceObserver))\n return\n\n if (!onMatch(performance, name, callback, performance)) {\n if (callbacks.size < 1)\n observer.observe({ entryTypes: [\"resource\", \"measure\"] })\n callbacks.set(name, callback)\n }\n }\n\n return {\n resolver: {\n perfs(cb) {\n this._fetchReq\n .then(() =>\n monitor(this._wretchReq._url, cb)\n )\n .catch(() => {/* swallow */ })\n return this\n },\n }\n }\n}\n\nexport default perfs\n","import type { Wretch, WretchAddon } from \"../types.js\"\n\n/**\n * Options for the query method.\n */\nexport type QueryStringOptions = {\n /**\n * Replace existing query parameters instead of appending to them.\n */\n replace?: boolean\n /**\n * Completely omit key=value pairs for undefined or null values.\n */\n omitUndefinedOrNullValues?: boolean\n}\n\nconst appendQueryParams = (url: string, qp: object | string, replace: boolean, omitUndefinedOrNullValues: boolean) => {\n let queryString: string\n\n if (typeof qp === \"string\") {\n queryString = qp\n } else {\n const usp = new URLSearchParams()\n for (const key in qp) {\n const value = qp[key]\n if (omitUndefinedOrNullValues && (value === null || value === undefined)) continue\n if (Array.isArray(value)) {\n for (const val of value)\n usp.append(key, val ?? \"\")\n } else {\n usp.append(key, value ?? \"\")\n }\n }\n queryString = usp.toString()\n }\n\n const split = url.split(\"?\")\n\n if (!queryString)\n return replace ? split[0] : url\n\n if (replace || split.length < 2)\n return split[0] + \"?\" + queryString\n\n return url + \"&\" + queryString\n}\n\nexport interface QueryStringAddon {\n /**\n * Converts a javascript object to query parameters, then appends this query string\n * to the current url. String values are used as the query string verbatim.\n *\n * Set `replace` to `true` in the options to replace existing query parameters.\n * Set `omitUndefinedOrNullValues` to `true` in the options to completely omit the key=value pair for undefined or null values.\n *\n * ```\n * import QueryAddon from \"wretch/addons/queryString\"\n *\n * let w = wretch(\"http://example.com\").addon(QueryStringAddon);\n * // url is http://example.com\n * w = w.query({ a: 1, b: 2 });\n * // url is now http://example.com?a=1&b=2\n * w = w.query({ c: 3, d: [4, 5] });\n * // url is now http://example.com?a=1&b=2c=3&d=4&d=5\n * w = w.query(\"five&six&seven=eight\");\n * // url is now http://example.com?a=1&b=2c=3&d=4&d=5&five&six&seven=eight\n * w = w.query({ reset: true }, { replace: true });\n * // url is now http://example.com?reset=true\n * ```\n *\n * ##### **Note that .query is not meant to handle complex cases with nested objects.**\n *\n * For this kind of usage, you can use `wretch` in conjunction with other libraries\n * (like [`qs`](https://github.com/ljharb/qs)).\n *\n * ```js\n * // Using wretch with qs\n *\n * const queryObject = { some: { nested: \"objects\" } };\n * const w = wretch(\"https://example.com/\").addon(QueryStringAddon)\n *\n * // Use .qs inside .query :\n *\n * w.query(qs.stringify(queryObject));\n *\n * // Use .defer :\n *\n * const qsWretch = w.defer((w, url, { qsQuery, qsOptions }) => (\n * qsQuery ? w.query(qs.stringify(qsQuery, qsOptions)) : w\n * ));\n *\n * qsWretch\n * .url(\"https://example.com/\")\n * .options({ qs: { query: queryObject } });\n * ```\n *\n * @param qp - An object which will be converted, or a string which will be used verbatim.\n * @param options - Optional configuration object\n */\n query<T extends QueryStringAddon, C, R, E>(this: T & Wretch<T, C, R, E>, qp: object | string, options?: QueryStringOptions): this\n}\n\n/**\n * Adds the ability to append query parameters from a javascript object.\n *\n * ```js\n * import QueryAddon from \"wretch/addons/queryString\"\n *\n * wretch().addon(QueryAddon)\n * ```\n */\nconst queryString: WretchAddon<QueryStringAddon> = {\n wretch: {\n query(qp, options = {}) {\n return { ...this, _url: appendQueryParams(this._url, qp, options.replace ?? false, options.omitUndefinedOrNullValues ?? false) }\n }\n }\n}\n\nexport default queryString\n","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","import { core } from \"./core.js\"\nimport * as Addons from \"./addons/index.js\"\nimport { WretchError } from \"./resolver.js\"\n\nconst factory = (_url = \"\", _options = {}) =>\n ({ ...core, _url, _options })\n .addon([\n Addons.abortAddon(),\n Addons.basicAuthAddon,\n Addons.formDataAddon,\n Addons.formUrlAddon,\n Addons.perfsAddon(),\n Addons.queryStringAddon,\n Addons.progressAddon()\n ])\n\nfactory[\"default\"] = factory\nfactory.WretchError = WretchError\n\nexport default factory\n"],"mappings":"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