@powership/server
Version:
260 lines (257 loc) • 7.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.isHttpError = exports.HttpError = exports.BaseRequestHandler = exports.BaseRequest = void 0;
var _utils = require("@powership/utils");
var _httpErrors = _interopRequireDefault(require("http-errors"));
var _httpStatusCodes = require("http-status-codes");
var _qs = _interopRequireDefault(require("qs"));
var _ServerLogs = require("./ServerLogs.cjs");
var _Symbol = require("./Symbol.cjs");
var _routeMatch = require("./routeMatch.cjs");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
const {
HttpError,
InternalServerError,
isHttpError,
PreconditionFailed
} = _httpErrors.default;
exports.isHttpError = isHttpError;
exports.HttpError = HttpError;
class BaseRequest {
constructor(init) {
this.replace(init);
}
replace = init => {
this.input = init;
const {
//
query,
method,
host,
headers,
pathname,
port,
urlObject
} = this.parseInit(init);
this.query = query;
this.method = method;
this.headers = headers;
this.host = host;
this.pathname = pathname;
this.port = port;
this.urlObject = urlObject;
return this;
};
parseInit = init => {
const headers = BaseRequest.parseHeaders(init.headers);
const host = headers.get('host') || 'UNKNOWN';
const method = (init.method || 'UNKNOWN').toUpperCase();
const urlObject = new URL(init.url || '', `http://${host}` // fixme get protocol
);
const query = _qs.default.parse(urlObject.search);
const pathname = urlObject.pathname;
const port = urlObject.port;
return {
headers,
host,
method,
query,
pathname,
port,
urlObject
};
};
static parseHeaders = input => {
if (input === undefined) return new Headers();
if (input instanceof Headers) return input;
if (input && typeof input === 'object') {
const headers = new Headers();
Object.entries(input).forEach(([name, v]) => {
//
if (Array.isArray(v)) {
v.forEach(el => {
if (typeof el !== 'string') {
throw new Error(`INVALID_HEADER_TYPE: ${typeof el}`);
}
headers.append(name, `${v}`);
});
return;
}
switch (typeof v) {
case 'boolean':
case 'string':
{
headers.append(name, `${v}`);
break;
}
default:
{
throw new Error(`INVALID_HEADER_TYPE: ${typeof v}`);
}
}
});
return headers;
}
throw new Error('INVALID_HEADERS_CONFIG');
};
}
exports.BaseRequest = BaseRequest;
class BaseRequestHandler extends BaseRequest {
constructor(input = {}) {
super(input);
const {
body = _Symbol.UnhandledSymbol,
headers,
statusCode = 404
} = input;
this.statusCode = statusCode;
this.body = body;
this.headers = BaseRequestHandler.parseHeaders(headers);
}
graphQLData() {
const {
data,
errors
} = this.graphQLResponse();
if (errors?.length) {
throw new PreconditionFailed(errors.map(el => el.message).join('. '));
}
return data;
}
graphQLResponse() {
try {
const response = BaseRequestHandler.jsonBody(this.body);
const {
errors,
data
} = response;
if (!errors && !('data' in response)) {
return {
errors: [{
message: 'Empty response.',
path: ''
}],
data: {}
};
}
return {
errors,
data
};
} catch (e) {
return {
errors: [{
message: e.message,
path: ''
}],
data: {}
};
}
}
toHttpResponse = () => {
return BaseRequestHandler.toHttp(this, 'RESPONSE');
};
toHttpRequest = () => {
return BaseRequestHandler.toHttp(this, 'REQUEST');
};
/**
* Return the matched routePattern parameters or null
* if the route has no parameters, an empty object is returned
* https://github.com/snd/url-pattern
* @param routePattern
*/
testRoute = routePattern => {
return BaseRequestHandler.createRouteMatcher(routePattern).match(this.urlObject.pathname.replace(/\/$/, ''));
};
static createRouteMatcher(routePattern) {
return (0, _routeMatch.createRouteMatcher)(routePattern);
}
static httpStatusCode(statusCode) {
if (statusCode in _httpStatusCodes.StatusCodes) {
if (statusCode?.toString().match(/^\d*$/)) {
return +statusCode;
}
return +_httpStatusCodes.StatusCodes[statusCode];
}
return _httpStatusCodes.StatusCodes.INTERNAL_SERVER_ERROR;
}
static httpResponseBody(body) {
if (typeof body === 'string') return body;
if (body === _Symbol.UnhandledSymbol) return '';
if (!body) return '';
if (isStream(body)) {
return body;
}
if (typeof body === 'object') {
try {
return JSON.stringify(body);
} catch (e) {
_ServerLogs.ServerLogs.fatal(e);
throw new InternalServerError();
}
}
return '';
}
static jsonBody(body) {
const constructorName = (0, _utils.getTypeName)(body);
if (constructorName === 'String') {
try {
return JSON.parse(body);
} catch (e) {
console.error(`Failed to parse json.`);
if (!(0, _utils.isProduction)()) {
console.error((0, _utils.inspectObject)(e));
}
}
}
if (constructorName === 'Object') {
return body;
}
_ServerLogs.ServerLogs.fatal(`Invalid body`, (0, _utils.inspectObject)(body));
return {};
}
static headersNamed(headers) {
const parsed = BaseRequestHandler.parseHeaders(headers);
// @ts-ignore
return [...parsed.entries()].reduce((acc, [name, value]) => {
return [...acc, {
name,
value
}];
}, []);
}
static httpHeaders(headers) {
return BaseRequestHandler.headersNamed(headers).reduce((acc, {
name,
value
}) => ({
...acc,
[name]: value
}), {});
}
static toHttp(input, type) {
const req = new BaseRequest(input);
const {
body: bodyInit
} = input;
const body = type === 'RESPONSE' ? this.httpResponseBody(bodyInit ?? _Symbol.UnhandledSymbol) : bodyInit === _Symbol.UnhandledSymbol ? '' : bodyInit ?? '';
return {
body,
streamBody: isStream(body) ? body : undefined,
headers: this.httpHeaders(input.headers),
statusCode: this.httpStatusCode(input.statusCode ?? 404),
headersNamed: this.headersNamed(input.headers),
payload: {},
method: req.method,
query: req.query,
type
};
}
}
exports.BaseRequestHandler = BaseRequestHandler;
function isStream(value) {
return typeof value?.pipe === 'function' && typeof value?.abort === 'function' && typeof value?.on === 'function';
}
//# sourceMappingURL=BaseRequestHandler.cjs.map