sensai
Version:
Because even AI needs a master
130 lines (129 loc) • 5.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return _default;
}
});
const _constants = require("../../constants");
const _context = /*#__PURE__*/ _interop_require_default(require("./context"));
const _body = /*#__PURE__*/ _interop_require_default(require("./body"));
const _querystring = require("../../utils/querystring");
const _url = require("../../utils/url");
const _request = require("../../utils/request");
const _nodestream = require("node:stream");
const _error = /*#__PURE__*/ _interop_require_default(require("./error"));
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
const _default = (router)=>{
return async (request, response)=>{
const { method = _constants.HTTP_DEFAULT_METHOD, headers, url: requestUrl } = request;
const { url, searchParams } = (0, _url.parseUrl)(requestUrl);
const { middlewares = [], resource, params } = router.lookup(url) || {};
const requestId = (0, _request.getUniqueRequestId)();
const serverHeaders = {
"X-Request-Id": requestId
};
if (resource) {
const isHead = method === _constants.HTTP_HEAD;
const endpoint = resource[isHead ? _constants.HTTP_GET : method] || resource[_constants.HTTP_ANY];
if (endpoint) {
const route = endpoint[_constants.VERSION_DEFAULT]; // TODO check from header X-Api-Version or Accept?
if (route) {
const { route: routePath, type } = route;
const data = await getRequestData(request, searchParams, params);
try {
const status = {
code: _constants.HTTP_STATUS.OK
};
let output = await _context.default.run({
headers,
type,
requestId,
status
}, async ()=>{
return await [
...middlewares,
routePath
].reduce((prev, next)=>{
const { default: handler } = require(next);
return prev.then(handler.bind({
request,
response
}));
}, Promise.resolve({
...data,
...params
}));
});
write(response, status.code, serverHeaders, output, isHead);
} catch (error) {
// TODO log errors properly in dev mode
console.error(error);
if (error instanceof _error.default) {
// TODO provide a way to obfuscate and manage errors
const { code, message } = error;
const payload = message ? {
code,
message
} : undefined;
write(response, error.code, serverHeaders, payload);
} else {
console.error(error);
write(response, _constants.HTTP_STATUS.INTERNAL_ERROR, serverHeaders);
}
}
} else {
// status code when using content negotiation with the Accept header.
write(response, _constants.HTTP_STATUS.NOT_ACCEPTABLE, serverHeaders);
}
} else {
write(response, _constants.HTTP_STATUS.NOT_ALLOWED, {
...serverHeaders,
Allow: Object.keys(resource)
});
}
} else {
write(response, _constants.HTTP_STATUS.NOT_FOUND, serverHeaders);
}
};
};
/**
* Extract data from request.
* @notes
* - the query parameters and URL path parameters are passed separately for speed.
* - we could merge query and body strings (ir url encoded) for faster parsing (it will also facilitate picking properties later on)
*/ const getRequestData = async (request, searchParams, params)=>{
const body = await (0, _body.default)(request);
return {
...(0, _querystring.parse)(searchParams),
...body,
...params
};
};
/**
* Write or pipe payload to server response.
*/ const write = (response, code, headers = {}, payload, shouldNotReturnPayload)=>{
// TODO The content type of the response should match what the client expects based on the context of the API and the Accept header.
if (payload instanceof _nodestream.Stream) {
response.writeHead(code, {
...headers,
"Transfer-Encoding": "chunked"
});
payload.pipe(response);
} else {
const output = payload == null ? "" : JSON.stringify(payload);
response.writeHead(code, {
...headers,
"Content-Type": _constants.MIME_TYPE.JSON,
"Content-Length": Buffer.byteLength(output)
});
response.end(shouldNotReturnPayload ? null : output);
}
};