UNPKG

bananas-commerce

Version:

A client for bananas-commerce with support for TypeScript

203 lines (202 loc) 6.33 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Fetcher = void 0; const types_js_1 = require("./types.js"); const sendBody = (method) => method === "post" || method === "put" || method === "patch" || method === "delete"; function getPath(pathTemplate, pathParams) { if (pathParams) { return pathTemplate.replace(/\{([^}]+)\}/g, (_, key) => { if (!(key in pathParams)) { throw new Error(`Expected path key ${key} doesnt exist in payload: ${JSON.stringify(pathParams)}`); } return encodeURIComponent(pathParams[key]); }); } return pathTemplate; } function objectToSearchParams(obj, searchParams, parent) { const params = searchParams || new URLSearchParams(); for (const key of Object.keys(obj)) { const propName = parent ? parent + "." + key : key; const leaf = obj[key]; if (Array.isArray(leaf)) { leaf.forEach((v) => { params.append(propName, String(v)); }); } else if (typeof leaf === "object") { if (leaf instanceof Date) { params.set(propName, leaf.toISOString()); } else if (leaf !== null) { objectToSearchParams(leaf, params, propName); } } else { params.set(propName, String(leaf)); } } return params; } function getQuery(params) { if (!params) { return ""; } const queryString = objectToSearchParams(params).toString(); if (queryString.length > 0) { return "?" + queryString; } return queryString; } function getHeaders(init, mediaType) { const headers = new Headers(init); if (!headers.has("Content-Type") && !mediaType || mediaType === "application/json") { headers.append("Content-Type", "application/json"); } if (!headers.has("Accept")) { headers.append("Accept", "application/json"); } return headers; } function getBody(method, payload, mediaType) { if (sendBody(method)) { if (mediaType === "application/x-www-form-urlencoded") { return new URLSearchParams(payload); } else if (mediaType === "multipart/form-data") { const body = new FormData(); Object.entries(payload).forEach(([k, v]) => { body.append(k, v); }); return body; } return JSON.stringify(payload); } } function mergeRequestInit(first, second) { const headers = new Headers(first?.headers); const other = new Headers(second?.headers); for (const key of other.keys()) { const value = other.get(key); if (value != null) { headers.set(key, value); } } return { ...first, ...second, headers }; } function getFetchParams(request) { const path = getPath(request.path, request.payload.path); const query = getQuery(request.payload.query); const headers = getHeaders(request.init?.headers, request.mediaType); const url = request.baseUrl + path + query; const init = { ...request.init, method: request.method.toUpperCase(), headers, body: getBody(request.method, request.payload.body, request.mediaType), }; return { url, init }; } async function getResponseData(response) { const contentType = response.headers.get("content-type"); if (contentType && contentType.indexOf("application/json") !== -1) { return await response.json(); } return await response.text(); } async function fetchJson(url, init) { const response = await fetch(url, init); const data = await getResponseData(response); const result = { headers: response.headers, url: response.url, ok: response.ok, status: response.status, statusText: response.statusText, data, }; if (result.ok) { return result; } throw new types_js_1.ApiError(result); } function wrapMiddlewares(middlewares, fetch) { const handler = async (index, url, init) => { if (middlewares == null || index === middlewares.length) { return fetch(url, init); } const current = middlewares[index]; init = init || { headers: getHeaders() }; return await current(url, init, (nextUrl, nextInit) => handler(index + 1, nextUrl, nextInit)); }; return (url, init) => handler(0, url, init); } async function fetchUrl(request) { const { url, init } = getFetchParams(request); const response = await request.fetch(url, init); return response; } function createFetch(fetch) { const fun = async (payload, init) => { try { return await fetch(payload, init); } catch (err) { if (err instanceof types_js_1.ApiError) { throw new fun.Error(err); } throw err; } }; fun.Error = class extends types_js_1.ApiError { constructor(error) { super(error); Object.setPrototypeOf(this, new.target.prototype); } typed() { return { status: this.status, data: this.data, }; } }; return fun; } function fetcher() { let baseUrl = ""; let defaultInit = {}; const middlewares = []; const fetch = wrapMiddlewares(middlewares, fetchJson); const api = { configure: (config) => { baseUrl = config.baseUrl || ""; defaultInit = config.init || {}; middlewares.splice(0); middlewares.push(...(config.use || [])); return api; }, use: (mw) => { middlewares.push(mw); return api; }, endpoint: (path) => ({ method: (method, mediaType) => createFetch((payload, init) => fetchUrl({ baseUrl: baseUrl || "", path: path, method: method, mediaType: mediaType, payload: payload, init: mergeRequestInit(defaultInit, init), fetch, })), }), }; return api; } exports.Fetcher = { for: () => fetcher(), };