wretch
Version:
A tiny wrapper built around fetch with an intuitive syntax.
99 lines • 4.26 kB
JavaScript
import { middlewareHelper } from "./middleware.js";
import { CATCHER_FALLBACK, FETCH_ERROR } from "./constants.js";
/**
* This class inheriting from Error is thrown when the fetch response is not "ok".
* It extends Error and adds status, text and body fields.
*/
export class WretchError extends Error {
}
export const resolver = (wretch) => {
const sharedState = Object.create(null);
wretch = wretch._addons.reduce((w, addon) => addon.beforeRequest &&
addon.beforeRequest(w, wretch._options, sharedState)
|| w, wretch);
const { _url: url, _options: opts, _fetch: customFetch, _errorTransformer: errorTransformer, _catchers: _catchers, _resolvers: resolvers, _middlewares: middlewares, _addons: addons } = wretch;
const catchers = new Map(_catchers);
const finalOptions = opts;
// The generated fetch request
let finalUrl = url;
const _fetchReq = middlewareHelper(middlewares)((url, options) => {
finalUrl = url;
const fetchImpl = customFetch || fetch;
return fetchImpl(url, options);
})(url, finalOptions);
// Throws on an http error
const referenceError = new Error();
const throwingPromise = _fetchReq
.then(async (response) => {
if (!response.ok) {
const err = new WretchError();
err["cause"] = referenceError;
err.stack += "\nCAUSE: " + referenceError.stack;
err.response = response;
err.status = response.status;
err.url = finalUrl;
if (response.type === "opaque" || errorTransformer) {
err.message = response.statusText;
}
else {
try {
err.message = await response.clone().text();
}
catch (_a) {
err.message = response.statusText;
}
}
throw err;
}
return response;
});
// Wraps the Promise in order to dispatch the error to a matching catcher
const catchersWrapper = (promise) => promise.catch(async (error) => {
const catcher = catchers.get(error === null || error === void 0 ? void 0 : error.status) ||
catchers.get(error === null || error === void 0 ? void 0 : error.name) ||
(!(error instanceof WretchError) && catchers.get(FETCH_ERROR)) ||
catchers.get(CATCHER_FALLBACK);
if (error.response && errorTransformer) {
error = await errorTransformer(error, error.response, wretch);
}
if (catcher)
return catcher(error, wretch);
throw error;
});
const bodyParser = funName => cb => {
const promise = funName ?
// If a callback is provided, then callback with the body result otherwise return the parsed body itself.
throwingPromise.then(_ => _ === null || _ === void 0 ? void 0 : _[funName]()) :
// No body parsing method - return the response
throwingPromise;
return catchersWrapper(cb ? promise.then(cb) : promise);
};
const responseChain = {
_wretchReq: wretch,
_fetchReq,
_sharedState: sharedState,
res: bodyParser(null),
json: bodyParser("json"),
blob: bodyParser("blob"),
formData: bodyParser("formData"),
arrayBuffer: bodyParser("arrayBuffer"),
text: bodyParser("text"),
error(errorId, cb) {
catchers.set(errorId, cb);
return this;
},
badRequest(cb) { return this.error(400, cb); },
unauthorized(cb) { return this.error(401, cb); },
forbidden(cb) { return this.error(403, cb); },
notFound(cb) { return this.error(404, cb); },
timeout(cb) { return this.error(408, cb); },
internalError(cb) { return this.error(500, cb); },
fetchError(cb) { return this.error(FETCH_ERROR, cb); },
};
const enhancedResponseChain = addons.reduce((chain, addon) => ({
...chain,
...(typeof addon.resolver === "function" ? addon.resolver(chain) : addon.resolver)
}), responseChain);
return resolvers.reduce((chain, r) => r(chain, wretch), enhancedResponseChain);
};
//# sourceMappingURL=resolver.js.map