|
|
|
const Promise = require("bluebird");
|
|
|
|
const pathToRegexp = require("path-to-regexp");
|
|
|
|
const url = require("url");
|
|
|
|
const xtend = require("xtend");
|
|
|
|
const defaultValue = require("default-value");
|
|
|
|
|
|
|
|
module.exports = function() {
|
|
|
|
let routes = [];
|
|
|
|
|
|
|
|
function addRoute(method, path, handler) {
|
|
|
|
// Mutable arguments? WTF.
|
|
|
|
let keys = [];
|
|
|
|
let regex = pathToRegexp(path, keys);
|
|
|
|
|
|
|
|
routes.push({ method, path, regex, keys, handler });
|
|
|
|
}
|
|
|
|
|
|
|
|
function getRoute(method, path) {
|
|
|
|
let matches;
|
|
|
|
let matchingRoute = routes.find((route) => route.method === method && (matches = route.regex.exec(path)));
|
|
|
|
|
|
|
|
if (matchingRoute == null) {
|
|
|
|
throw new Error("No matching routes found");
|
|
|
|
} else {
|
|
|
|
let params = {};
|
|
|
|
|
|
|
|
matchingRoute.keys.forEach((key, i) => {
|
|
|
|
params[key] = matches[i + 1];
|
|
|
|
});
|
|
|
|
|
|
|
|
return {
|
|
|
|
handler: matchingRoute.handler,
|
|
|
|
params: params
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handle(method, uri, data) {
|
|
|
|
return Promise.try(() => {
|
|
|
|
let {path, query} = url.parse(uri, true);
|
|
|
|
let route = getRoute(method, path);
|
|
|
|
let tasks = [];
|
|
|
|
|
|
|
|
let req = {
|
|
|
|
path: path,
|
|
|
|
query: query,
|
|
|
|
body: data,
|
|
|
|
params: route.params,
|
|
|
|
pass: function(options = {}) {
|
|
|
|
// FIXME: window.fetch passthrough
|
|
|
|
},
|
|
|
|
passRender: function(viewName, options = {}) {
|
|
|
|
return Promise.try(() => {
|
|
|
|
return this.pass(options);
|
|
|
|
}).then((response) => {
|
|
|
|
let locals = defaultValue(options.locals, {});
|
|
|
|
let combinedLocals = xtend(locals, response.body);
|
|
|
|
|
|
|
|
res.render(viewName, combinedLocals, options);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let res = {
|
|
|
|
render: function(viewName, locals = {}, options = {}) {
|
|
|
|
tasks.push({
|
|
|
|
type: "render",
|
|
|
|
viewName, locals, options
|
|
|
|
});
|
|
|
|
},
|
|
|
|
open: function(path, options = {}) {
|
|
|
|
tasks.push({
|
|
|
|
type: "open",
|
|
|
|
path, options
|
|
|
|
});
|
|
|
|
},
|
|
|
|
close: function(options = {}) {
|
|
|
|
tasks.push({
|
|
|
|
type: "close",
|
|
|
|
options
|
|
|
|
});
|
|
|
|
},
|
|
|
|
notify: function(message, options = {}) {
|
|
|
|
tasks.push({
|
|
|
|
type: "notify",
|
|
|
|
message, options
|
|
|
|
});
|
|
|
|
},
|
|
|
|
error: function(error, context = {}) {
|
|
|
|
tasks.push({
|
|
|
|
type: "error",
|
|
|
|
error, context
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.try(() => {
|
|
|
|
return route.handler(req, res);
|
|
|
|
}).then((result) => {
|
|
|
|
return {
|
|
|
|
result: result,
|
|
|
|
actions: tasks
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let api = {
|
|
|
|
get: addRoute.bind(api, "get"),
|
|
|
|
post: addRoute.bind(api, "post"),
|
|
|
|
put: addRoute.bind(api, "put"),
|
|
|
|
delete: addRoute.bind(api, "delete"),
|
|
|
|
head: addRoute.bind(api, "head"),
|
|
|
|
patch: addRoute.bind(api, "patch"),
|
|
|
|
addRoute: addRoute,
|
|
|
|
handle: handle
|
|
|
|
}
|
|
|
|
|
|
|
|
return api;
|
|
|
|
}
|