.chain((config) => { const serviceDefs = config.services || {}; const registerFutures = Object.keys(serviceDefs).map((serviceName) => { const serviceDef = serviceDefs[serviceName]; const { path, options = {}, } = serviceDef; debug('service def: ', serviceName, serviceDef); if (!path) { // TODO: this should probably error return Future.of(null); } const requirePath = resolvePath(path); const Service = require(requirePath); const service = Service(serviceName); return this['service:register']({service}, this.source) .chain(() => this.ask(service.id, 'configure', options)) ; }); return Future.parallel(10, registerFutures); })
.chain((services) => { debug('found services', services.map((s) => `${s.type}/${s.name}/${s.id}`)); // If a response is expected, we just send the message // and wait for the future to resolve with the data. // Otherwise, we return early and send the message on to // its destination, forcing the future to execute // TODO: watchers and interceptors const futures = services.map( (service) => wrapFuture(service._inbox({ source, action, data, })) // here we are resolving with null in case of an error so // that the rest of the discovery can complete succesfully. // we filter the null values out after everything is reported. // TODO: report the error .chainRej((err) => { debug('discover resulted in an error', err); return Future.of(null); }) ); // TODO: make number of simultaneous message sends configurable // at the kernel layer const sendFuture = Future.parallel(10, futures).map((results) => { // remove null values here... // TODO: should also filter non-valid results and report problems return results.filter((result) => !!result); }); if (expectResponse) { return sendFuture; } else { sendFuture.fork(noop, noop); return Future.of(null); } })
// branch to simply resolve with the error. // TODO: consider reporting any errors somehow return this.findService(subscription.source.id) .chain((service) => wrapFuture(service._inbox({ source, action: subscription.action, data, }))) .chainRej((err) => Future.of(err)) ; }); // We don't return the broadcast future since we don't want to bubble // any errors back up to the caller. const broadcast = Future.parallel(5, futures); broadcast.fork(noop, noop); return Future.of(null); }, sendPointToPoint(destination, action, data, source, expectResponse) { debug('send p2p', destination, action); return this.findService(destination) .chain((service) => { // If a response is expected, we just send the message // and wait for the future to resolve with the data. // Otherwise we return early and send the message on to // its destination, forcing the future to execute
module.exports = exports = function createUI(options, definitions) { const { rooturl = 'http://localhost:8081', apiurl, basepath = '/', title = 'Couer Application', manageMenuTitle = 'Manage', clientPaths = {}, themepath = resolve(__dirname, '../client/src/css/themes/couer/theme.css'), } = options; const ui = { title, basepath: R.last(basepath) === '/' ? R.init(basepath) : basepath, apiurl, definitions, }; definitions.forEach((def) => { Object.defineProperty(def, 'ui', { get: function() { return ui; }, enumerable: true, }); }); const viewdefs = definitions.reduce((res, def) => { const data = def.definition(ui); return res.concat(Array.isArray(data) ? data : [data]); }, []); const viewpaths = Object.assign({ table: resolve(__dirname, '../client/src/js/views/TableView'), // display: resolve(__dirname, '../client/src/js/views/DisplayView.js'), form: resolve(__dirname, '../client/src/js/views/FormView.js'), // dashboard: resolve(__dirname, '../client/src/js/views/DashboardView.js'), // 404: resolve(__dirname, '../client/src/js/views/NotFoundView.js'), // login: resolve(__dirname, '../client/src/js/views/LoginView.js'), }, clientPaths); const menulinks = [[manageMenuTitle, viewdefs.reduce((res, def) => { if (!def.menuTitle) { return res; } return res.concat([[def.menuTitle, def.path, def.menuIcon || '']]); }, [])]]; const loginview = viewdefs.reduce((res, def) => { if (!res && def.islogin) { return def; } return res; }, null); const contextmenus = viewdefs.reduce((res, def) => { if (def.contextKey) { if (!res[def.contextKey]) { res[def.contextKey] = [def.contextKey, []]; } res[def.contextKey][1].push([def.contextTitle, def.path, def.contextIcon || '']); } return res; }, {}); const data = {views: viewdefs, menulinks, contextmenus, title, loginview, themepath}; const tmpd = resolve(tmpdir(), `./${process.pid}-${Date.now()}`); const tmpjsd = resolve(tmpd, './js'); const dest = resolve(cwd(), './client'); const destjsd = resolve(dest, './js'); const destcssd = resolve(dest, './css'); const destjsfile = resolve(destjsd, './index.js'); const destcssfile = resolve(destcssd, './index.css'); const tmpjs = resolve(tmpjsd, './index.js'); const destassets = resolve(dest, './assets'); const js = genjs(data, viewpaths); const mkdirs = Future.parallel(1, [ Future.node((done) => mkdirp(destjsd, done)), Future.node((done) => mkdirp(destcssd, done)), Future.node((done) => mkdirp(destassets, done)), Future.node((done) => mkdirp(tmpjsd, done)), ]); let buildjs = mkdirs.chain(() => Future.node((done) => fs.writeFile(tmpjs, js, done))).chain(() => { const browserify = require('browserify'); const b = browserify({ // builtins, }); b.add(tmpjs); const cssModulesify = require('css-modulesify'); const FileSystemLoader = require('css-modulesify/file-system-loader'); const originalFetch = FileSystemLoader.prototype.fetch; FileSystemLoader.prototype.fetch = function(_newPath, relativeTo, _trace) { if (_newPath.replace(/^["']|["']$/g, '') === '!theme') { _newPath = themepath; } return originalFetch.call(this, _newPath, relativeTo, _trace); }; // const assets = require('postcss-assets'); b.plugin(cssModulesify, { rootDir: '/', output: destcssfile, generateScopedName: cssModulesify.generateShortName, before: [ 'postcss-import', // 'postcss-mixins', // 'postcss-simple-vars', 'postcss-nested', ], after: [ // 'postcss-conditionals', 'postcss-calc', 'postcss-color-function', // assets({ // loadPaths: [resolve(__dirname, '../client/src/assets')], // }), // 'lost', ], }); return Future.node(done => b.bundle(done)) .chain((buffer) => { return Future.node((done) => fs.writeFile(destjsfile, buffer, done)); }) ; }) ; buildjs = Future.cache(buildjs); const staticServer = router.static('/', dest); return router.scope(basepath, Pipeline.build([ router.scope('/_resource', Pipeline.build([ (req, res) => buildjs.chain(() => staticServer(req, res)), ])), (req, res) => { // no post routes here if (!req.ismethod('GET')) { return next(req, res); } const root = trimTrailingSlash(`${rooturl}${basepath}`); debug('root:', root); const data = { title, root, }; const html = genhtml(data); return done(req, res.html(html)); }, ])); };