UNPKG

fusion-plugin-http-router

Version:

Registers http routes and handlers on the server.

268 lines (223 loc) 9.33 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; } var fusionCore = require('fusion-core'); var pathToRegexp = require('path-to-regexp'); var bodyParser = _interopDefault(require('koa-body')); const HttpRouterToken = fusionCore.createToken('HttpRouter'); const HttpHandlersToken = fusionCore.createToken('HttpHandlersToken'); const BodyParserOptionsToken = fusionCore.createToken('BodyParserOptionsToken'); function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function matchUri(url, patternedPath) { const { path, keys, pattern } = patternedPath; const match = pattern.exec(url); if (!match) { return { isExact: false, params: {}, path }; } const values = match.slice(1); const params = keys.reduce((final, name, index) => _objectSpread({}, final, { [name]: values[index] }), {}); return { isExact: true, params, path }; } function matchPath(url, paths) { if (!(paths instanceof Array)) { return matchUri(url, paths); } return paths.reduce((final, path) => { const match = matchUri(url, path); if (match.isExact && !final.isExact) { return match; } return final; }, { isExact: false, params: {}, path: '' }); } function getPatternedPaths(paths) { return paths.map(path => { const keys = []; const pattern = pathToRegexp.pathToRegexp(path, keys); return { path, pattern, keys: keys.map(({ name }) => name) }; }); } function ownKeys$1(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread$1(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$1(Object(source), true).forEach(function (key) { _defineProperty$1(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$1(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty$1(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } const methods = new Set(['CONNECT', 'DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT', 'TRACE']); function dive(currentKey, into, target) { Object.keys(into).forEach(key => { if (methods.has(key.toUpperCase())) { target[currentKey] = Object.keys(into).reduce((final, key) => { if (methods.has(key.toUpperCase())) { return _objectSpread$1({}, final, { [key.toUpperCase()]: into[key] }); } return final; }, _objectSpread$1({}, target[currentKey])); } else { let newKey = key; const newVal = into[key]; if (currentKey.length > 0) { newKey = currentKey + key; } if (newVal instanceof Object) { dive(newKey, newVal, target); } else { target[newKey] = newVal; } } }); } function flattenHandlers(handlers) { const flatHandlers = {}; dive('', handlers, flatHandlers); return flatHandlers; } function getHandler(ctx, paths, handlers) { const match = matchPath(ctx.path, paths); const handler = handlers[match.path] && handlers[match.path][ctx.method]; return match.isExact ? { handler, match } : { match: {} }; } function findInvalidPath(handlers) { function findInvalidMethod(path) { return Object.keys(handlers[path]).find(method => typeof handlers[path][method] !== 'function'); } const paths = Object.keys(handlers); const invalidPath = paths.find(path => { if (handlers[path] instanceof Object) { return findInvalidMethod(path); } return true; }); if (invalidPath) { const invalidMethod = findInvalidMethod(invalidPath); return invalidMethod ? `method "${invalidMethod}" of path "${invalidPath}"` : `path "${invalidPath}"`; } return ''; } function ownKeys$2(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } function _objectSpread$2(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys$2(Object(source), true).forEach(function (key) { _defineProperty$2(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys$2(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _defineProperty$2(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /* eslint-env node */ function comparator(a, b) { const aLength = a.split(':').length; const bLength = b.split(':').length; return aLength < bLength ? -1 : 1; } const plugin = fusionCore.createPlugin({ deps: { bodyParserOptions: BodyParserOptionsToken.optional, handlers: HttpHandlersToken }, provides: deps => { const { handlers, bodyParserOptions = {} } = deps; if (!handlers || typeof handlers !== 'object') { throw new Error('Missing/incorrect handlers registered to HttpHandlersToken'); } const flatHandlers = flattenHandlers(handlers); const paths = Object.keys(flatHandlers).sort(comparator); // eslint-disable-next-line no-console console.log('\x1b[36m\x1b[1m', 'Registered Paths:\x1b[0m'); paths.forEach((path, index) => { const methods = Object.keys(flatHandlers[path]).join(', '); // eslint-disable-next-line no-console console.log('\x1b[36m', `${index + 1}. ${path} -> { ${methods} }`, '\x1b[0m'); }); const invalidPath = findInvalidPath(flatHandlers); if (invalidPath) { throw new Error(`Missing/incorrect handler registered against ${invalidPath}.`); } const parseBody = bodyParser(_objectSpread$2({}, bodyParserOptions, { multipart: true })); const patternedPaths = getPatternedPaths(paths); const from = fusionCore.memoize(async ctx => { const { query } = ctx; const { handler, match } = getHandler(ctx, patternedPaths, flatHandlers); if (typeof handler !== 'function') { return { handler: null, match }; } await parseBody(ctx, () => Promise.resolve()); const { body, files = {} } = ctx.request; return { handler: (...args) => handler({ params: match.params, query, body, files }, ...args), match }; }); return { from }; }, middleware: (deps, service) => async (ctx, next) => { try { const { handler } = await service.from(ctx); if (typeof handler === 'function') { ctx.body = await handler(ctx); } } catch (error) { ctx.body = { status: 'failure', data: { code: error.code, message: error.message, stack: error.stack, meta: error.meta } }; } return next(); } }); var index = plugin; exports.default = index; exports.BodyParserOptionsToken = BodyParserOptionsToken; exports.HttpHandlersToken = HttpHandlersToken; exports.HttpRouterToken = HttpRouterToken;