@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
JavaScript
;
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;