UNPKG

@graphql-mesh/serve-cli

Version:
183 lines (182 loc) 8.68 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.runServeCLI = void 0; const tslib_1 = require("tslib"); /* eslint-disable import/no-nodejs-modules */ const cluster_1 = tslib_1.__importDefault(require("cluster")); const os_1 = require("os"); const path_1 = require("path"); const uWebSockets_js_1 = require("uWebSockets.js"); const serve_runtime_1 = require("@graphql-mesh/serve-runtime"); // eslint-disable-next-line import/no-extraneous-dependencies const utils_1 = require("@graphql-mesh/utils"); const git_loader_1 = require("@graphql-tools/git-loader"); const github_loader_1 = require("@graphql-tools/github-loader"); const graphql_file_loader_1 = require("@graphql-tools/graphql-file-loader"); const load_1 = require("@graphql-tools/load"); const url_loader_1 = require("@graphql-tools/url-loader"); async function runServeCLI(processExit = (exitCode) => process.exit(exitCode)) { const prefix = cluster_1.default.worker?.id ? `🕸️ Mesh Worker#${cluster_1.default.worker.id}` : `🕸️ Mesh`; const workerLogger = new utils_1.DefaultLogger(prefix); workerLogger.info(`Starting`); const meshServeCLIConfigFileName = process.env.MESH_SERVE_CONFIG_FILE_NAME || 'mesh.config.ts'; const meshServeCLIConfigFilePath = process.env.MESH_SERVE_CONFIG_FILE_PATH || (0, path_1.join)(process.cwd(), meshServeCLIConfigFileName); const meshServeCLIConfigRelativePath = (0, path_1.relative)(process.cwd(), meshServeCLIConfigFilePath); workerLogger.info(`Loading configuration from ${meshServeCLIConfigRelativePath}`); const loadedConfig = await Promise.resolve(`${meshServeCLIConfigFilePath}`).then(s => tslib_1.__importStar(require(s))).catch(e => { workerLogger.error(`Failed to load configuration from ${meshServeCLIConfigRelativePath}`, e); return processExit(1); }); const meshServeCLIConfig = loadedConfig.serveConfig || { fusiongraph: './fusiongraph.graphql', }; workerLogger.info(`Loaded configuration from ${meshServeCLIConfigRelativePath}`); let unifiedGraphPath; let spec; if ('fusiongraph' in meshServeCLIConfig) { unifiedGraphPath = meshServeCLIConfig.fusiongraph; spec = 'fusion'; } else if ('supergraph' in meshServeCLIConfig) { unifiedGraphPath = meshServeCLIConfig.supergraph; spec = 'federation'; } else if (!('http' in meshServeCLIConfig)) { unifiedGraphPath = './fusiongraph.graphql'; } const unifiedGraphName = spec === 'fusion' ? 'fusiongraph' : 'supergraph'; if (cluster_1.default.isPrimary) { let forkNum; if (!process.env.FORK || process.env.FORK === 'true') { forkNum = process.env.NODE_ENV === 'production' ? (0, os_1.availableParallelism)() : 1; } else if (process.env.FORK === 'false' || process.env.FORK === '0' || process.env.FORK === '1') { forkNum = 1; } else if (!isNaN(parseInt(process.env.FORK))) { forkNum = parseInt(process.env.FORK); } if (typeof unifiedGraphPath === 'string' && !unifiedGraphPath.includes('://')) { const parcelWatcher$ = Promise.resolve().then(() => tslib_1.__importStar(require('@parcel/watcher'))); parcelWatcher$ .catch(e => { httpHandler.logger.error(`If you want to enable hot reloading on ${unifiedGraphPath}, install "@parcel/watcher"`, e); }) .then(parcelWatcher => { if (parcelWatcher) { const absoluteUnifiedGraphPath = (0, path_1.isAbsolute)(unifiedGraphPath) ? unifiedGraphPath : (0, path_1.join)(process.cwd(), unifiedGraphPath); const unifiedGraphDir = (0, path_1.dirname)(absoluteUnifiedGraphPath); return parcelWatcher .subscribe(unifiedGraphDir, (err, events) => { if (err) { workerLogger.error(err); return; } if (events.some(event => event.path === absoluteUnifiedGraphPath)) { workerLogger.info(`${unifiedGraphName} changed`); if (forkNum > 1) { for (const workerId in cluster_1.default.workers) { cluster_1.default.workers[workerId].send('invalidateUnifiedGraph'); } } else { httpHandler.invalidateUnifiedGraph(); } } }) .then(subscription => { registerTerminateHandler(eventName => { workerLogger.info(`Closing watcher for ${absoluteUnifiedGraphPath} for ${eventName}`); return subscription.unsubscribe(); }); }); } return null; }) .catch(e => { workerLogger.error(`Failed to watch ${unifiedGraphPath}`, e); }); } if (forkNum > 1) { workerLogger.info(`Forking ${forkNum} Mesh Workers`); for (let i = 0; i < forkNum; i++) { workerLogger.info(`Forking Mesh Worker #${i}`); const worker = cluster_1.default.fork(); registerTerminateHandler(eventName => { workerLogger.info(`Closing Mesh Worker #${i} for ${eventName}`); worker.kill(eventName); workerLogger.info(`Closed Mesh Worker #${i} for ${eventName}`); }); workerLogger.info(`Forked Mesh Worker #${i}`); } workerLogger.info(`Forked ${forkNum} Mesh Workers`); return; } } const port = meshServeCLIConfig.port || 4000; const host = meshServeCLIConfig.host || 'localhost'; const httpHandler = (0, serve_runtime_1.createServeRuntime)({ logging: workerLogger, ...meshServeCLIConfig, [unifiedGraphName]() { workerLogger.info(`Loading ${unifiedGraphName} from ${unifiedGraphPath}`); return (0, load_1.loadSchema)(unifiedGraphPath, { loaders: [new graphql_file_loader_1.GraphQLFileLoader(), new url_loader_1.UrlLoader(), new github_loader_1.GithubLoader(), new git_loader_1.GitLoader()], assumeValid: true, assumeValidSDL: true, }) .then(supergraph => { workerLogger.info(`Loaded ${unifiedGraphName} from ${unifiedGraphPath}`); return supergraph; }) .catch(e => { workerLogger.error(`Failed to load Supergraph from ${unifiedGraphPath}`, e); throw e; }); }, }); process.on('message', message => { if (message === 'invalidateUnifiedGraph') { workerLogger.info(`Invalidating ${unifiedGraphName}`); httpHandler.invalidateUnifiedGraph(); } }); const app = meshServeCLIConfig.sslCredentials ? (0, uWebSockets_js_1.SSLApp)(meshServeCLIConfig.sslCredentials) : (0, uWebSockets_js_1.App)(); const protocol = meshServeCLIConfig.sslCredentials ? 'https' : 'http'; app.any('/*', httpHandler); workerLogger.info(`Starting server on ${protocol}://${host}:${port}`); app.listen(host, port, function listenCallback(listenSocket) { if (listenSocket) { workerLogger.info(`Started server on ${protocol}://${host}:${port}`); registerTerminateHandler(eventName => { workerLogger.info(`Closing ${protocol}://${host}:${port} for ${eventName}`); app.close(); }); } else { workerLogger.error(`Failed to start server on ${protocol}://${host}:${port}`); processExit(1); } }); } exports.runServeCLI = runServeCLI; const terminateEvents = ['SIGINT', 'SIGTERM']; const terminateHandlers = new Set(); for (const eventName of terminateEvents) { process.once(eventName, () => { for (const handler of terminateHandlers) { handler(eventName); terminateHandlers.delete(handler); } }); } function registerTerminateHandler(callback) { terminateHandlers.add(callback); return () => { terminateHandlers.delete(callback); }; }