UNPKG

@teclone/r-server

Version:

A lightweight, extensible web-server with inbuilt routing-engine, static file server, file upload handler, request body parser, middleware support and lots more

329 lines (312 loc) 13.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var _asyncToGenerator = require('@babel/runtime/helpers/asyncToGenerator'); var _slicedToArray = require('@babel/runtime/helpers/slicedToArray'); var _classCallCheck = require('@babel/runtime/helpers/classCallCheck'); var _createClass = require('@babel/runtime/helpers/createClass'); var _defineProperty = require('@babel/runtime/helpers/defineProperty'); var _regeneratorRuntime = require('@babel/runtime/regenerator'); var utils = require('@teclone/utils'); var regex = require('@teclone/regex'); var Constants = require('./Constants'); function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var _asyncToGenerator__default = /*#__PURE__*/_interopDefaultLegacy(_asyncToGenerator); var _slicedToArray__default = /*#__PURE__*/_interopDefaultLegacy(_slicedToArray); var _classCallCheck__default = /*#__PURE__*/_interopDefaultLegacy(_classCallCheck); var _createClass__default = /*#__PURE__*/_interopDefaultLegacy(_createClass); var _defineProperty__default = /*#__PURE__*/_interopDefaultLegacy(_defineProperty); var _regeneratorRuntime__default = /*#__PURE__*/_interopDefaultLegacy(_regeneratorRuntime); function _createForOfIteratorHelper(o, allowArrayLike) { var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"]; if (!it) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = it.call(o); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it["return"] != null) it["return"](); } finally { if (didErr) throw err; } } }; } function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); } function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } var generateNext = function generateNext() { var status = false; var next = function next() { return status = true; }; next.status = function () { return status; }; next.reset = function () { status = false; }; return next; }; var Engine = /*#__PURE__*/function () { function Engine(path, method, request, response) { _classCallCheck__default["default"](this, Engine); _defineProperty__default["default"](this, "resolved", false); _defineProperty__default["default"](this, "request", void 0); _defineProperty__default["default"](this, "response", void 0); _defineProperty__default["default"](this, "method", void 0); _defineProperty__default["default"](this, "path", void 0); this.resolved = false; this.request = request; this.response = response; this.path = utils.stripSlashes(path); this.method = method; } /** * capture route parameters */ _createClass__default["default"](Engine, [{ key: "captureParameter", value: function captureParameter(routeToken, pathToken, parameters) { var processToken = function processToken(token, value) { var _ref = token.indexOf(':') > -1 ? token.split(':') : ['string', token], _ref2 = _slicedToArray__default["default"](_ref, 2), dataType = _ref2[0], name = _ref2[1]; var result = value; switch (dataType.toLowerCase()) { case 'int': result = Number.parseInt(value); break; case 'float': case 'number': case 'numeric': result = Number.parseFloat(value); break; case 'bool': case 'boolean': result = result.toLowerCase(); result = !['0', 'false', '', 'null', 'nil', 'undefined', 'no', 'none'].includes(value); break; } if (Number.isNaN(result)) { result = 0; } return { name: name, dataType: dataType, value: result }; }; if (Constants.DOUBLE_TOKEN_REGEX.test(routeToken)) { var token1 = RegExp.$1; var separator = RegExp.$2; var token2 = RegExp.$3; var values = pathToken ? pathToken.split(separator) : ['', '']; parameters.push(processToken(token1, values[0])); parameters.push(processToken(token2, values[1])); } else if (Constants.SINGLE_TOKEN_REGEX.test(routeToken)) { var token = RegExp.$1; parameters.push(processToken(token, pathToken)); } return parameters; } /** * runs through the route, and captures parameters */ }, { key: "captureParameters", value: function captureParameters(routePath) { routePath = utils.stripSlashes(routePath); var parameters = []; //split the tokens. var routeTokens = routePath.split('/'); var pathTokens = this.path.split('/'); // if the route tokens is greater than the path tokens, fill with empty string var len = routeTokens.length; utils.fillArray(pathTokens, len, ''); var i = -1; while (++i < len) { var pathToken = pathTokens[i]; var routeToken = routeTokens[i]; // if it is final route token, store remaining url if (routeToken === '*' && i + 1 === len) { parameters.push({ name: '__rest', dataType: 'string', value: pathTokens.slice(i).join('/') }); break; } else { this.captureParameter(routeToken, pathToken, parameters); } } return parameters.reduce(function (acc, current) { acc[current.name] = current.value; return acc; }, {}); } /** * check if route url matches request url */ }, { key: "matchPath", value: function matchPath(routePath) { routePath = utils.stripSlashes(routePath); /* create matching regular expression */ var tokens = routePath ? routePath.split('/') : []; var max = tokens.length - 1; var pattern = tokens.map(function (token, index) { // if it is the last token and it is optional, put it inside an optional regex pattern if (index === max && /[^)]\?$/.test(token)) { token = "(".concat(token.substring(0, token.length - 1), ")?"); } if (token === '*') { return '.*'; } else { token = regex.replace(Constants.DOUBLE_TOKEN_REGEX, '[^/]+$:2[^/]+', token); token = regex.replace(Constants.SINGLE_TOKEN_REGEX, '[^/]+', token); } return token; }).join('/'); var regex$1 = new RegExp('^' + pattern + '$', 'i'); //regex is case insensitive // the second test is necessary to capture optional path return regex$1.test(this.path) || regex$1.test(this.path + '/'); } /** * processes the route */ }, { key: "process", value: function () { var _process = _asyncToGenerator__default["default"]( /*#__PURE__*/_regeneratorRuntime__default["default"].mark(function _callee(routeInstances) { var _this = this; var middlewareInstances, method, routeInstance, _routeInstance, routePath, routeCallback, routeMiddlewares, matchingMiddlewares, next, _iterator, _step, _step$value, middlewarePath, middlewares, pathParams, _iterator2, _step2, middleware, routePathParams, _args = arguments; return _regeneratorRuntime__default["default"].wrap(function _callee$(_context) { while (1) switch (_context.prev = _context.next) { case 0: middlewareInstances = _args.length > 1 && _args[1] !== undefined ? _args[1] : []; method = this.method; if (!this.resolved) { _context.next = 4; break; } return _context.abrupt("return", true); case 4: routeInstance = routeInstances.find(function (routeInstance) { return _this.matchPath(routeInstance[1]); }); if (routeInstance) { _context.next = 7; break; } return _context.abrupt("return", false); case 7: _routeInstance = _slicedToArray__default["default"](routeInstance, 4), routePath = _routeInstance[1], routeCallback = _routeInstance[2], routeMiddlewares = _routeInstance[3]; matchingMiddlewares = middlewareInstances.filter(function (_ref3) { var _ref4 = _slicedToArray__default["default"](_ref3, 4), middlewarePath = _ref4[1], methods = _ref4[3]; return methods.has(method) && _this.matchPath(middlewarePath); }).map(function (_ref5) { var _ref6 = _slicedToArray__default["default"](_ref5, 3), middlewarePath = _ref6[1], middleware = _ref6[2]; return [middlewarePath, middleware]; }); if (routeMiddlewares.length) { matchingMiddlewares.push([routePath, routeMiddlewares]); } // execute middlewares next = generateNext(); _iterator = _createForOfIteratorHelper(matchingMiddlewares); _context.prev = 12; _iterator.s(); case 14: if ((_step = _iterator.n()).done) { _context.next = 41; break; } _step$value = _slicedToArray__default["default"](_step.value, 2), middlewarePath = _step$value[0], middlewares = _step$value[1]; pathParams = this.captureParameters(middlewarePath); _iterator2 = _createForOfIteratorHelper(middlewares); _context.prev = 18; _iterator2.s(); case 20: if ((_step2 = _iterator2.n()).done) { _context.next = 29; break; } middleware = _step2.value; next.reset(); _context.next = 25; return middleware(this.request, this.response, next, { pathParams: pathParams }); case 25: if (next.status()) { _context.next = 27; break; } return _context.abrupt("break", 29); case 27: _context.next = 20; break; case 29: _context.next = 34; break; case 31: _context.prev = 31; _context.t0 = _context["catch"](18); _iterator2.e(_context.t0); case 34: _context.prev = 34; _iterator2.f(); return _context.finish(34); case 37: if (next.status()) { _context.next = 39; break; } return _context.abrupt("return", true); case 39: _context.next = 14; break; case 41: _context.next = 46; break; case 43: _context.prev = 43; _context.t1 = _context["catch"](12); _iterator.e(_context.t1); case 46: _context.prev = 46; _iterator.f(); return _context.finish(46); case 49: routePathParams = this.captureParameters(routePath); _context.next = 52; return routeCallback(this.request, this.response, { pathParams: routePathParams }); case 52: return _context.abrupt("return", true); case 53: case "end": return _context.stop(); } }, _callee, this, [[12, 43, 46, 49], [18, 31, 34, 37]]); })); function process(_x) { return _process.apply(this, arguments); } return process; }() }]); return Engine; }(); exports.Engine = Engine; //# sourceMappingURL=Engine.js.map