UNPKG

sensai

Version:

Because even AI needs a master

130 lines (129 loc) 5.58 kB
"use strict"; 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); } };