@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
JavaScript
'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