UNPKG

@spec2ts/openapi-client

Version:

Utility to convert OpenAPI v3 specifications to Typescript HTTP client using TypeScript native compiler

205 lines (204 loc) 7.15 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpError = exports.http = exports.QS = exports._ = exports.servers = exports.defaults = void 0; exports.defaults = { baseUrl: "/", }; exports.servers = {}; /** Utilities functions */ exports._ = { // Encode param names and values as URIComponent encodeReserved: [encodeURI, encodeURIComponent], allowReserved: [encodeURI, encodeURI], /** Deeply remove all properties with undefined values. */ stripUndefined(obj) { return obj && JSON.parse(JSON.stringify(obj)); }, isEmpty(v) { return typeof v === "object" && !!v ? Object.keys(v).length === 0 && v.constructor === Object : v === undefined; }, /** Creates a tag-function to encode template strings with the given encoders. */ encode(encoders, delimiter = ",") { return (strings, ...values) => { return strings.reduce((prev, s, i) => { var _a; return `${prev}${s}${q((_a = values[i]) !== null && _a !== void 0 ? _a : "", i)}`; }, ""); }; function q(v, i) { const encoder = encoders[i % encoders.length]; if (typeof v === "object") { if (Array.isArray(v)) { return v.map(encoder).join(delimiter); } const flat = Object.entries(v).reduce((flat, entry) => [...flat, ...entry], []); return flat.map(encoder).join(delimiter); } return encoder(String(v)); } }, /** Separate array values by the given delimiter. */ delimited(delimiter = ",") { return (params, encoders = exports._.encodeReserved) => Object.entries(params) .filter(([, value]) => !exports._.isEmpty(value)) .map(([name, value]) => exports._.encode(encoders, delimiter) `${name}=${value}`) .join("&"); }, /** Join URLs parts. */ joinUrl(...parts) { return parts .filter(Boolean) .join("/") .replace(/([^:]\/)\/+/, "$1"); } }; /** Functions to serialize query parameters in different styles. */ exports.QS = { /** Join params using an ampersand and prepends a questionmark if not empty. */ query(...params) { const s = params.filter(p => !!p).join("&"); return s && `?${s}`; }, /** * Serializes nested objects according to the `deepObject` style specified in * https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#style-values */ deep(params, [k, v] = exports._.encodeReserved) { const qk = exports._.encode([(s) => s, k]); const qv = exports._.encode([(s) => s, v]); // don't add index to arrays // https://github.com/expressjs/body-parser/issues/289 const visit = (obj, prefix = "") => Object.entries(obj) .filter(([, v]) => !exports._.isEmpty(v)) .map(([prop, v]) => { const isValueObject = typeof v === "object"; const index = Array.isArray(obj) && !isValueObject ? "" : prop; const key = prefix ? qk `${prefix}[${index}]` : prop; if (isValueObject) { return visit(v, key); } return qv `${key}=${v}`; }) .join("&"); return visit(params); }, /** * Property values of type array or object generate separate parameters * for each value of the array, or key-value-pair of the map. * For other types of properties this property has no effect. * See https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#encoding-object */ explode(params, encoders = exports._.encodeReserved) { const q = exports._.encode(encoders); return Object.entries(params) .filter(([, value]) => typeof value !== "undefined") .map(([name, value]) => { if (Array.isArray(value)) { return value.map((v) => q `${name}=${v}`).join("&"); } if (typeof value === "object") { return exports.QS.explode(value, encoders); } return q `${name}=${value}`; }) .join("&"); }, form: exports._.delimited(), pipe: exports._.delimited("|"), space: exports._.delimited("%20"), }; /** Http request base methods. */ exports.http = { async fetch(url, req) { const { baseUrl, headers, fetch: customFetch, ...init } = { ...exports.defaults, ...req }; const href = exports._.joinUrl(baseUrl, url); const res = await (customFetch || fetch)(href, { ...init, headers: exports._.stripUndefined({ ...exports.defaults.headers, ...headers }), }); let text; try { text = await res.text(); } catch (err) { /* ok */ } if (!res.ok) { throw new HttpError(res.status, res.statusText, href, res.headers, text); } return { status: res.status, statusText: res.statusText, headers: exports.http.headers(res.headers), data: text }; }, async fetchJson(url, req = {}) { const res = await exports.http.fetch(url, { ...req, headers: { ...req.headers, Accept: "application/json", }, }); res.data = res.data && JSON.parse(res.data); return res; }, async fetchVoid(url, req = {}) { const res = await exports.http.fetch(url, { ...req, headers: { ...req.headers, Accept: "application/json", }, }); return res; }, json({ body, headers, ...req }) { return { ...req, body: JSON.stringify(body), headers: { ...headers, "Content-Type": "application/json", }, }; }, form({ body, headers, ...req }) { return { ...req, body: exports.QS.form(body), headers: { ...headers, "Content-Type": "application/x-www-form-urlencoded", }, }; }, multipart({ body, ...req }) { const data = new FormData(); Object.entries(body).forEach(([name, value]) => { data.append(name, value); }); return { ...req, body: data, }; }, headers(headers) { const res = {}; headers.forEach((value, key) => res[key] = value); return res; } }; class HttpError extends Error { constructor(status, statusText, url, headers, text) { super(`${url} - ${statusText} (${status})`); this.status = status; this.statusText = statusText; this.headers = exports.http.headers(headers); if (text) { try { this.data = JSON.parse(text); } catch (err) { /* ok */ } } } } exports.HttpError = HttpError;