fusion-plugin-http-router
Version:
Registers http routes and handlers on the server.
268 lines (223 loc) • 9.33 kB
JavaScript
;
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;