@nestia/fetcher
Version:
Fetcher library of Nestia SDK
181 lines (178 loc) • 6.69 kB
JavaScript
import { HttpError } from '@typia/utils';
/** @internal */
var FetcherBase;
(function (FetcherBase) {
FetcherBase.request = (props) => async (connection, route, input, stringify) => {
const result = await _Propagate("fetch")(props)(connection, route, input, stringify);
if (result.success === false)
throw new HttpError(route.method, route.path, result.status, result.headers, result.data);
return result.data;
};
FetcherBase.propagate = (props) => async (connection, route, input, stringify) => _Propagate("propagate")(props)(connection, route, input, stringify);
/** @internal */
const _Propagate = (method) => (props) => async (connection, route, input, stringify) => {
//----
// REQUEST MESSAGE
//----
// METHOD & HEADERS
const headers = {
...(connection.headers ?? {}),
};
if (input !== undefined) {
if (route.request?.type === undefined)
throw new Error(`Error on ${props.className}.fetch(): no content-type being configured.`);
else if (route.request.type !== "multipart/form-data")
headers["Content-Type"] = route.request.type;
}
else if (input === undefined && headers["Content-Type"] !== undefined)
delete headers["Content-Type"];
// INIT REQUEST DATA
const init = {
...(connection.options ?? {}),
method: route.method,
headers: (() => {
const output = [];
for (const [key, value] of Object.entries(headers))
if (value === undefined)
continue;
else if (Array.isArray(value))
for (const v of value)
output.push([key, String(v)]);
else
output.push([key, String(value)]);
return output;
})(),
};
// CONSTRUCT BODY DATA
if (input !== undefined)
init.body = props.encode(
// BODY TRANSFORM
route.request?.type === "application/x-www-form-urlencoded"
? request_query_body(input)
: route.request?.type === "multipart/form-data"
? request_form_data_body(input)
: route.request?.type !== "text/plain"
? (stringify ?? JSON.stringify)(input)
: input, headers);
//----
// RESPONSE MESSAGE
//----
// URL SPECIFICATION
const path = connection.host[connection.host.length - 1] !== "/" &&
route.path[0] !== "/"
? `/${route.path}`
: route.path;
const url = new URL(`${connection.host}${path}`);
// DO FETCH
const event = {
route,
path,
status: null,
input,
output: undefined,
started_at: new Date(),
respond_at: null,
completed_at: null,
};
try {
// TRY FETCH
const response = await (connection.fetch ?? fetch)(url.href, init);
event.respond_at = new Date();
event.status = response.status;
// CONSTRUCT RESULT DATA
const result = {
success: response.status === 200 ||
response.status === 201 ||
response.status === route.status,
status: response.status,
headers: response_headers_to_object(response.headers),
data: undefined,
};
if (result.success === false) {
// WHEN FAILED
result.data = await response.text();
const type = response.headers.get("content-type");
if (method !== "fetch" &&
type &&
type.indexOf("application/json") !== -1)
try {
result.data = JSON.parse(result.data);
}
catch { }
}
else {
// WHEN SUCCESS
if (route.method === "HEAD")
result.data = undefined;
else if (route.response?.type === "application/json") {
const text = await response.text();
result.data = text.length ? JSON.parse(text) : undefined;
}
else if (route.response?.type === "application/x-www-form-urlencoded") {
const query = new URLSearchParams(await response.text());
result.data = route.parseQuery ? route.parseQuery(query) : query;
}
else
result.data = props.decode(await response.text(), result.headers);
}
event.output = result.data;
return result;
}
catch (exp) {
throw exp;
}
finally {
event.completed_at = new Date();
if (connection.logger)
try {
await connection.logger(event);
}
catch { }
}
};
})(FetcherBase || (FetcherBase = {}));
/** @internal */
const request_query_body = (input) => {
const q = new URLSearchParams();
for (const [key, value] of Object.entries(input))
if (value === undefined)
continue;
else if (Array.isArray(value))
value.forEach((elem) => q.append(key, String(elem)));
else
q.set(key, String(value));
return q;
};
/** @internal */
const request_form_data_body = (input) => {
const encoded = new FormData();
const append = (key) => (value) => {
if (value === undefined)
return;
else if (typeof File === "function" && value instanceof File)
encoded.append(key, value, value.name);
else
encoded.append(key, value);
};
for (const [key, value] of Object.entries(input))
if (Array.isArray(value))
value.map(append(key));
else
append(key)(value);
return encoded;
};
/** @internal */
const response_headers_to_object = (headers) => {
const output = {};
headers.forEach((value, key) => {
if (key === "set-cookie") {
output[key] ??= [];
output[key].push(...value.split(";").map((str) => str.trim()));
}
else
output[key] = value;
});
return output;
};
export { FetcherBase };
//# sourceMappingURL=FetcherBase.mjs.map