UNPKG

@bennetgallein/mande

Version:

Some awesome description

167 lines (165 loc) 5.65 kB
/*! * @bennetgallein/mande v3.0.0-rc5 * (c) 2024 Eduardo San Martin Morote * @license MIT */ function stringifyQuery(query) { let searchParams = Object.keys(query) .map((k) => [k, query[k]].map(encodeURIComponent).join('=')) .join('&'); return searchParams ? '?' + searchParams : ''; } let trailingSlashRE = /\/+$/; let leadingSlashRE = /^\/+/; function joinURL(base, url) { return (base.replace(trailingSlashRE, '') + '/' + url.replace(leadingSlashRE, '')); } function removeNullishValues(headers) { return Object.keys(headers).reduce((newHeaders, headerName) => { if (headers[headerName] != null) { // @ts-ignore newHeaders[headerName] = headers[headerName]; } return newHeaders; }, {}); } /** * Global default options as {@link Options} that are applied to **all** mande * instances. Always contain an initialized `headers` property with the default * headers: * - Accept: 'application/json' * - 'Content-Type': 'application/json' */ const defaults = { responseAs: 'json', headers: { Accept: 'application/json', 'Content-Type': 'application/json', }, }; /** * Create a Mande instance * * @example * ```js * const users = mande('/api/users') * users.get('2').then(user => { * // do something * }) * ``` * @param baseURL - absolute url * @param instanceOptions - optional options that will be applied to every * other request for this instance */ function mande(baseURL, passedInstanceOptions = {}, fetchPolyfill) { function _fetch(method, urlOrData, dataOrOptions, localOptions = {}) { let url; let data; if (typeof urlOrData === 'object') { url = ''; data = urlOrData; localOptions = dataOrOptions || {}; } else { url = urlOrData; data = dataOrOptions; } let mergedOptions = { ...defaults, ...instanceOptions, method, ...localOptions, // we need to ditch nullish headers headers: removeNullishValues({ ...defaults.headers, ...instanceOptions.headers, ...localOptions.headers, }), }; let query = { ...defaults.query, ...instanceOptions.query, ...localOptions.query, }; let { responseAs } = mergedOptions; url = joinURL(baseURL, typeof url === 'number' ? '' + url : url || ''); // TODO: warn about multiple queries provided not supported // if (__DEV__ && query && urlInstance.search) url += stringifyQuery(query); if (data) mergedOptions.body = JSON.stringify(data); return localFetch(url, mergedOptions) .then((response) => Promise.all([ response, responseAs === 'response' ? response : response[responseAs]().catch(() => null), ])) .then(([response, data]) => { if (response.status >= 200 && response.status < 300) { // data is a raw response when responseAs is response return responseAs !== 'response' && response.status == 204 ? null : data; } let err = new Error(response.statusText); err.response = response; err.body = data; throw err; }); } const localFetch = typeof fetch != 'undefined' ? fetch : fetchPolyfill; if (!localFetch) { throw new Error('No fetch function exists. Make sure to include a polyfill on Node.js.'); } const instanceOptions = { query: {}, headers: {}, ...passedInstanceOptions, }; return { options: instanceOptions, post: _fetch.bind(null, 'POST'), put: _fetch.bind(null, 'PUT'), patch: _fetch.bind(null, 'PATCH'), // these two have no body get: (url, options) => _fetch('GET', url, null, options), delete: (url, options) => _fetch('DELETE', url, null, options), }; } /** * Creates an Nuxt SSR compatible function that automatically proxies cookies * to requests and works transparently on the server and client (it still * requires a fetch polyfill on Node). * @example * ```js * import { mande, nuxtWrap } from 'mande' * * const fetchPolyfill = process.server ? require('node-fetch') : fetch * const users = mande(BASE_URL + '/api/users', {}, fetchPolyfill) * * export const getUserById = nuxtWrap(users, (api, id: string) => api.get(id)) * ``` * * @param api - Mande instance to wrap * @param fn - function to be wrapped */ function nuxtWrap(api, fn) { // args for the api call + 1 because of api parameter const argsAmount = fn.length; const wrappedCall = function _wrappedCall() { let apiInstance = api; let args = Array.from(arguments); // call from nuxt server with a function to augment the api instance if (arguments.length === argsAmount) { apiInstance = { ...api }; // remove the first argument const [augmentApiInstance] = args.splice(0, 1); // let the caller augment the instance augmentApiInstance(apiInstance); } return fn.call(null, apiInstance, ...args); }; return wrappedCall; } export { defaults, mande, nuxtWrap };