wretch
Version:
A tiny wrapper built around fetch with an intuitive syntax.
1 lines • 17.5 kB
Source Map (JSON)
{"version":3,"file":"wretch.min.mjs","names":[],"sources":["../../src/utils.ts","../../src/constants.ts","../../src/middleware.ts","../../src/resolver.ts","../../src/core.ts","../../src/index.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 { core } from \"./core.js\"\nimport { WretchError } from \"./resolver.js\"\nimport type { Wretch, WretchOptions } from \"./types.js\"\n\nexport type {\n Wretch,\n ConfiguredMiddleware,\n FetchLike,\n Middleware,\n WretchResponseChain,\n WretchOptions,\n WretchError,\n WretchErrorCallback,\n WretchResponse,\n WretchDeferredCallback,\n WretchAddon\n} from \"./types.js\"\n\n/**\n * Creates a new wretch instance with a base url and base\n * [fetch options](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch).\n *\n * ```ts\n * import wretch from \"wretch\"\n *\n * // Reusable instance\n * const w = wretch(\"https://domain.com\", { mode: \"cors\" })\n * ```\n *\n * @param _url The base url\n * @param _options The base fetch options\n * @returns A fresh wretch instance\n */\nconst factory = (_url = \"\", _options: WretchOptions = {}): Wretch =>\n ({ ...core, _url, _options })\n\nfactory[\"default\"] = factory\nfactory.WretchError = WretchError\n\nexport default factory\n"],"mappings":"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"}