UNPKG

fets

Version:

TypeScript HTTP Framework focusing on e2e type-safety, easy setup, performance & great developer experience

197 lines (196 loc) • 9.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ClientValidationError = void 0; exports.createClient = createClient; const qs_1 = require("qs"); const fetch_1 = require("@whatwg-node/fetch"); const server_1 = require("@whatwg-node/server"); const utils_js_1 = require("../plugins/utils.js"); const utils_js_2 = require("../utils.js"); const clientResponse_js_1 = require("./clientResponse.js"); const qsOptions = { indices: false, arrayFormat: 'repeat', }; class ClientValidationError extends Error { constructor(path, method, errors, response) { super(`Validation failed for ${method} ${path}`); this.path = path; this.method = method; this.errors = errors; this.response = response; } [Symbol.iterator]() { return this.errors[Symbol.iterator](); } } exports.ClientValidationError = ClientValidationError; function useValidationErrors() { return { onResponse({ path, method, response }) { if (response.status === 400 && response.headers.get('x-error-type') === 'validation') { return response.json().then(resJson => { if (resJson.errors) { throw new ClientValidationError(path, method, resJson.errors, response); } }); } }, }; } function createClient({ endpoint, fetchFn = fetch_1.fetch, plugins = [], globalParams, }) { plugins.unshift(useValidationErrors()); const onRequestInitHooks = []; const onFetchHooks = []; const onResponseHooks = []; for (const plugin of plugins) { if (plugin.onRequestInit) { onRequestInitHooks.push(plugin.onRequestInit); } if (plugin.onFetch) { onFetchHooks.push(plugin.onFetch); } if (plugin.onResponse) { onResponseHooks.push(plugin.onResponse); } } return new Proxy(utils_js_1.EMPTY_OBJECT, { get(_target, path) { return new Proxy(utils_js_1.EMPTY_OBJECT, { get(_target, method) { async function clientMethod(requestParams = {}) { // Merge globalParams with the current requestParams if (globalParams?.headers) { requestParams.headers = { ...globalParams.headers, ...requestParams.headers, }; } if (globalParams?.query) { requestParams.query = { ...globalParams.query, ...requestParams.query, }; } if (globalParams?.params) { requestParams.params = { ...globalParams.params, ...requestParams.params, }; } if (globalParams?.json) { requestParams.json = { ...globalParams.json, ...requestParams.json, }; } if (globalParams?.formData) { requestParams.formData = { ...globalParams.formData, ...requestParams.formData, }; } if (globalParams?.formUrlEncoded) { requestParams.formUrlEncoded = { ...globalParams.formUrlEncoded, ...requestParams.formUrlEncoded, }; } const { headers = {}, params: paramsBody, json: jsonBody, formData: formDataBody, formUrlEncoded: formUrlEncodedBody, query: queryBody, ...requestInitByUser } = requestParams; if (paramsBody) { for (const pathParamKey in paramsBody) { const value = paramsBody[pathParamKey]; if (value) { const encodedValue = encodeURIComponent(value); path = path .replace(`{${pathParamKey}}`, encodedValue) .replace(`:${pathParamKey}`, encodedValue); } } } if (!path.startsWith('/') && !path.startsWith('http')) { path = `/${path}`; } const requestInit = { ...requestInitByUser, method: method.toUpperCase(), headers, }; if (jsonBody) { requestInit.body = JSON.stringify(jsonBody); headers['Content-Type'] = 'application/json'; } if (formDataBody) { requestInit.body = new fetch_1.FormData(); for (const key in formDataBody) { const value = formDataBody[key]; if (value != null) { requestInit.body.append(key, (0, utils_js_2.isBlob)(value) ? value : value.toString()); } } } if (formUrlEncodedBody) { requestInit.body = (0, qs_1.stringify)(formUrlEncodedBody, qsOptions); requestInit.headers['Content-Type'] = 'application/x-www-form-urlencoded'; } let response; if (onRequestInitHooks.length) { const onRequestParamsHookPayload = { path, method, requestParams, requestInit, endResponse(res) { response = res; }, }; await (0, server_1.iterateAsyncVoid)(onRequestInitHooks, onRequestParamsHook => onRequestParamsHook(onRequestParamsHookPayload)); } if (response == null) { let finalUrl = path; if (endpoint && !path.startsWith('http')) { finalUrl = `${endpoint}${path}`; } if (queryBody) { const searchParams = (0, qs_1.stringify)(queryBody, qsOptions); if (finalUrl.includes('?')) { finalUrl += '&' + searchParams; } else { finalUrl += '?' + searchParams; } } let currentFetchFn = fetchFn; if (onFetchHooks.length > 0) { const onFetchHookPayload = { url: finalUrl, init: requestInit, fetchFn: currentFetchFn, setFetchFn(newFetchFn) { currentFetchFn = newFetchFn; }, }; await (0, server_1.iterateAsyncVoid)(onFetchHooks, onFetchHook => onFetchHook(onFetchHookPayload)); } response = await currentFetchFn(finalUrl, requestInit); } if (onResponseHooks.length > 0) { const onResponsePayload = { path, method, requestParams, requestInit, response, }; await (0, server_1.iterateAsyncVoid)(onResponseHooks, onResponseHook => onResponseHook(onResponsePayload)); } return response; } return function wrappedClientMethod(requestParams = {}) { return (0, clientResponse_js_1.createClientTypedResponsePromise)(clientMethod(requestParams)); }; }, }); }, }); }