bananas-commerce
Version:
A client for bananas-commerce with support for TypeScript
203 lines (202 loc) • 6.33 kB
JavaScript
;
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(),
};