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; }