UNPKG

resolve-runtime

Version:
365 lines (325 loc) 10.5 kB
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, 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; } import 'source-map-support/register'; import { Server } from 'http'; import express from 'express'; import MqttConnection from 'mqtt-connection'; import path from 'path'; import wrapApiHandler from 'resolve-api-handler-express'; import createCommandExecutor from 'resolve-command'; import createEventStore from 'resolve-es'; import createQueryExecutor, { constants as queryConstants } from 'resolve-query'; import createSocketServer from 'socket.io'; import uuid from 'uuid/v4'; import Url from 'url'; import getWebSocketStream from 'websocket-stream'; import { Server as WebSocketServer } from 'ws'; import createPubsubManager from './utils/create_pubsub_manager'; import getRootBasedUrl from './utils/get_root_based_url'; import startExpressServer from './utils/start_express_server'; import sagaRunnerExpress from './utils/saga_runner_express'; import mainHandler from './handlers/main_handler'; const initEventStore = async ({ storageAdapter: createStorageAdapter, busAdapter: createBusAdapter }, resolve) => { Object.assign(resolve, { eventStore: createEventStore({ storage: createStorageAdapter(), bus: createBusAdapter() }) }); }; const initExpress = async resolve => { const app = express(); const server = new Server(app); Object.defineProperties(resolve, { app: { value: app }, server: { value: server } }); }; const getMqttTopic = (appId, { topicName, topicId }) => { return `${appId}/${topicName === '*' ? '+' : topicName}/${topicId === '*' ? '+' : topicId}`; }; const createServerMqttHandler = (pubsubManager, appId, qos) => ws => { const stream = getWebSocketStream(ws); const client = new MqttConnection(stream); let messageId = 1; const publisher = (topicName, topicId, event) => new Promise((resolve, reject) => { client.publish({ topic: getMqttTopic(appId, { topicName, topicId }), payload: JSON.stringify(event), messageId: messageId++, qos }, error => error ? reject(error) : resolve()); }); client.on('connect', () => { client.connack({ returnCode: 0 }); }); client.on('pingreq', () => client.pingresp()); client.on('subscribe', packet => { try { for (const subscription of packet.subscriptions) { const [, topicName, topicId] = (subscription.topic || subscription).split('/'); pubsubManager.subscribe({ client: publisher, topicName, topicId }); } client.suback({ granted: [packet.qos], messageId: packet.messageId }); } catch (error) { resolveLog('warn', 'MQTT subscription failed', packet, error); } }); client.on('unsubscribe', packet => { try { for (const unsubscription of packet.unsubscriptions) { const [, topicName, topicId] = (unsubscription.topic || unsubscription).split('/'); pubsubManager.unsubscribe({ client: publisher, topicName, topicId }); } client.unsuback({ granted: [packet.qos], messageId: packet.messageId }); } catch (error) { resolveLog('warn', 'MQTT unsubscription failed', packet, error); } }); const dispose = () => { pubsubManager.unsubscribeClient(publisher); client.destroy(); }; client.on('close', dispose); client.on('error', dispose); client.on('disconnect', dispose); }; const sanitizeWildcardTopic = topic => topic === '*' ? '+' : topic; const createServerSocketIOHandler = pubsubManager => socket => { const publisher = (topicName, topicId, event) => new Promise(resolve => { socket.emit('message', JSON.stringify({ topicName, topicId, payload: event }), resolve); }); socket.on('subscribe', packet => { const subscriptions = JSON.parse(packet); for (const _ref of subscriptions) { const { topicName, topicId } = _ref; pubsubManager.subscribe({ client: publisher, topicName: sanitizeWildcardTopic(topicName), topicId: sanitizeWildcardTopic(topicId) }); } }); socket.on('unsubscribe', packet => { const unsubscriptions = JSON.parse(packet); for (const _ref2 of unsubscriptions) { const { topicName, topicId } = _ref2; pubsubManager.unsubscribe({ client: publisher, topicName: sanitizeWildcardTopic(topicName), topicId: sanitizeWildcardTopic(topicId) }); } }); const dispose = () => { pubsubManager.unsubscribeClient(publisher); }; socket.on('error', dispose); socket.on('disconnect', dispose); }; const initSubscribeAdapter = async resolve => { const pubsubManager = createPubsubManager(); const appId = resolve.applicationName; const qos = 1; try { const handler = createServerSocketIOHandler(pubsubManager); const socketIOServer = createSocketServer(resolve.server, { path: getRootBasedUrl(resolve.rootPath, '/api/socket-io/'), serveClient: false, transports: ['polling'] }); socketIOServer.on('connection', handler); } catch (error) { resolveLog('warn', 'Cannot init Socket.IO server socket: ', error); } try { const socketMqttServer = new WebSocketServer({ server: resolve.server, path: getRootBasedUrl(resolve.rootPath, '/api/mqtt') }); const handler = createServerMqttHandler(pubsubManager, appId, qos); socketMqttServer.on('connection', handler); } catch (error) { resolveLog('warn', 'Cannot init MQTT server socket: ', error); } Object.defineProperties(resolve, { pubsubManager: { value: pubsubManager } }); }; const initHMR = async resolve => { const HMR_ID = uuid(); const HMRSocketHandler = socket => { socket.emit('hotModuleReload', HMR_ID); }; const HMRSocketServer = createSocketServer(resolve.server, { path: getRootBasedUrl(resolve.rootPath, '/api/hmr/'), serveClient: false }); HMRSocketServer.on('connection', HMRSocketHandler); }; const emptyWorker = async () => { throw new Error('Guard exception: worker should not be invoked on non-cloud environment'); }; const initDomain = async ({ snapshotAdapter: createSnapshotAdapter, readModelAdapters: readModelAdaptersCreators }, resolve) => { const { eventStore, aggregates, readModels, viewModels } = resolve; const snapshotAdapter = createSnapshotAdapter(); const readModelAdapters = {}; for (const _ref3 of readModelAdaptersCreators) { const { name, factory } = _ref3; readModelAdapters[name] = factory(); } const executeCommand = createCommandExecutor({ eventStore, aggregates, snapshotAdapter }); const executeQuery = createQueryExecutor({ eventStore, viewModels, readModels, readModelAdapters, snapshotAdapter }); Object.assign(resolve, { executeCommand, executeQuery }); Object.defineProperties(resolve, { readModelAdapters: { value: readModelAdapters }, snapshotAdapter: { value: snapshotAdapter } }); }; const initEventLoop = async resolve => { const executors = resolve.executeQuery.getExecutors(queryConstants.modelTypes.readModel); const unsubscribe = await resolve.eventStore.loadEvents({ skipStorage: true }, async event => { resolve.pubsubManager.dispatch({ topicName: event.type, topicId: event.aggregateId, event }); const applicationPromises = []; for (const executor of executors) { applicationPromises.push(executor.updateByEvents([event])); } await Promise.all(applicationPromises); }); Object.defineProperty(resolve, 'unsubscribe', { value: unsubscribe }); }; const getSubscribeAdapterOptions = async (resolve, origin, adapterName) => { if (adapterName !== 'mqtt' && adapterName !== 'socket.io') { return null; } const { protocol, hostname, port } = Url.parse(origin); const isMqtt = adapterName === 'mqtt'; const isSecure = /^https/.test(protocol); const targetProtocol = ['http', 'https', 'ws', 'wss'][isMqtt * 2 + isSecure]; const targetPath = isMqtt ? '/api/mqtt' : '/api/socket-io/'; const targetPort = port == null ? [80, 443][+isSecure] : port; const url = `${targetProtocol}://${hostname}:${targetPort}${getRootBasedUrl(resolve.rootPath, targetPath)}`; return { appId: resolve.applicationName, url }; }; const localEntry = async ({ assemblies, constants, domain, redux, routes }) => { try { const resolve = _objectSpread({ aggregateActions: assemblies.aggregateActions, seedClientEnvs: assemblies.seedClientEnvs }, constants, domain, { redux, routes }); resolve.getSubscribeAdapterOptions = getSubscribeAdapterOptions.bind(null, resolve); await initEventStore(assemblies, resolve); await initExpress(resolve); await initSubscribeAdapter(resolve); await initHMR(resolve); await initDomain(assemblies, resolve); await initEventLoop(resolve); const getCustomParameters = async () => ({ resolve }); const executor = wrapApiHandler(mainHandler, getCustomParameters); resolve.app.use(getRootBasedUrl(resolve.rootPath, `/${resolve.staticPath}`), express.static(path.join(process.cwd(), resolve.distDir, './client'))); resolve.app.use(executor); await sagaRunnerExpress(resolve, assemblies.sagas); await startExpressServer(resolve); resolveLog('debug', 'Local entry point cold start success', resolve); return emptyWorker; } catch (error) { resolveLog('error', 'Local entry point cold start failure', error); } }; export default localEntry; //# sourceMappingURL=local_entry.js.map