const path = require('path'); const dotenv = require('dotenv'); const cwd = process.cwd(); ['.common.env', '.dev.env', '.env'].forEach((filename) => { dotenv.config({path: path.join(cwd, filename)}); }); const {fork} = require('child_process'); const chalk = require('chalk'); const {servicesList} = require('@truss/core'); const services = servicesList(); const longestServiceNameLength = services.reduce((l, r) => { return (r.length > l.length) ? r : l; }, '').length; let color = 1; const colors = [ 'redBright', 'greenBright', 'yellowBright', 'blueBright', 'magentaBright', 'cyanBright', 'whiteBright', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white', 'gray', ]; const children = []; const childrenOfService = {}; const bundleServices = (process.env.BUNDLE_SERVICES || '').split(','); const serviceCounts = bundleServices.reduce((l, r) => { const [service, count = 1] = r.split(':'); if (service in l) { l[service] = count; } return l; }, services.reduce((l, r) => { l[r] = 1; return l; }, {})); services.forEach((service) => { for (let i = 0; i < serviceCounts[service]; ++i) { forkService(service); } }); function forkService(service) { const servicePath = path.join(cwd, 'dist', 'production', service); // const servicePath = path.join(cwd, 'services', service); let index; const colorizer = chalk[colors[color++]]; const dresser = (line) => { const prefix = colorizer(`${(index + '').padStart(2, '0')} ยป ${ service.padEnd(longestServiceNameLength) } |`); console.error(`${prefix} ${line}`); }; const logger = (message) => { message.split('\n').forEach(dresser); }; const child = fork(servicePath, [], { env: Object.assign({ SOURCE_PATH: servicePath, }, process.env), execArgv: [], stdio: 'pipe', }); const forwardStream = (data) => { logger(data.toString('utf8').replace(/\n$/, '')); }; child.stdout.on('data', forwardStream); child.stderr.on('data', forwardStream); childrenOfService[service] = childrenOfService[service] || []; childrenOfService[service].push(child); index = children.length; logger(`forking...`); children.push(child); const createChildActions = (child) => { const actions = createActions(child); return (action) => { actions[action.type](action); }; }; child.on('message', createChildActions(child)); } function createActions (child) { return { listening: () => (child.listening = true), connect: ({payload: {clientId, service}}) => { const candidate = randomService(service); if (candidate) { candidate.send({ type: 'connection_from', payload: { clientId, from: children.indexOf(child), }, }); child.send({ type: 'connection_to', payload: { clientId, to: children.indexOf(candidate), }, }); } else { child.send({ type: 'connection_error', payload: { clientId, }, }); } }, client_send: ({payload}) => { children[payload.to].send({ type: 'server_recv', payload: { clientId: payload.clientId, data: payload.data, }, }); }, server_send: ({payload}) => { children[payload.to].send({ type: 'client_recv', payload: { clientId: payload.clientId, data: payload.data, }, }); }, }; } function randomService(service) { const candidates = [...childrenOfService[service]]; while (candidates.length > 0) { const index = Math.floor(Math.random() * candidates.length); const [candidate] = candidates.splice(index, 1); if (candidate.listening) { return candidate; } } }