UNPKG

@netlify/content-engine

Version:
320 lines 13 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.contentEngine = void 0; const graphql_1 = require("../graphql"); const index_1 = require("./../datastore/index"); const express_1 = __importDefault(require("express")); const inspector_1 = __importDefault(require("inspector")); const opentracing_1 = require("opentracing"); const reporter_1 = __importDefault(require("../reporter")); const tracer_1 = require("../utils/tracer"); const detect_port_in_use_and_prompt_1 = require("../utils/detect-port-in-use-and-prompt"); const _1 = require("."); const redux_1 = require("../redux"); const datastore_1 = require("../datastore"); const print_instructions_1 = require("../utils/print-instructions"); const __1 = require(".."); const context_1 = __importDefault(require("../schema/context")); const framework_hooks_1 = require("../framework-hooks"); const synchronize_1 = require("../utils/synchronize"); const tracer = (0, opentracing_1.globalTracer)(); const openDebuggerPort = (debugInfo) => { if (inspector_1.default.url() !== undefined) { return; // fixes #26708 } if (debugInfo.break) { inspector_1.default.open(debugInfo.port, undefined, true); // eslint-disable-next-line no-debugger debugger; } else { inspector_1.default.open(debugInfo.port); } }; // Return a user-supplied port otherwise the default Node.js debugging port const getDebugPort = (port) => port ?? 9229; const getDebugInfo = (program) => { if (Object.prototype.hasOwnProperty.call(program, `inspect`)) { return { port: getDebugPort(program.inspect), break: false, }; } else if (Object.prototype.hasOwnProperty.call(program, `inspectBrk`)) { return { port: getDebugPort(program.inspectBrk), break: true, }; } else { return null; } }; const contentEngine = ({ verbose, openTracingConfigFile, host, port, directory, engineConfig, frameworkHooks: frameworkHooksPath, } = { directory: `` }) => { if (frameworkHooksPath) { require(frameworkHooksPath); } framework_hooks_1.frameworkHooks.startup.before(); directory ||= process.cwd(); // provide global Gatsby object global.__GATSBY = process.env.GATSBY_NODE_GLOBALS ? JSON.parse(process.env.GATSBY_NODE_GLOBALS) : {}; const hostname = `0.0.0.0`; const program = { directory, sitePackageJson: require(`${directory}/package.json`), port: (typeof port === `string` ? parseInt(port, 10) : port) || 8000, hostname, host: host || `localhost`, store: redux_1.store, reporter: reporter_1.default, openTracingConfigFile: ``, debugInfo: null, }; program.debugInfo = getDebugInfo(program); console.info(`[content-engine] starting "${program.sitePackageJson.name || `unknown`}" engine`); program.verbose = verbose || false; reporter_1.default.setVerbose(program.verbose); if (program.debugInfo) { openDebuggerPort(program.debugInfo); } if (openTracingConfigFile) { (0, tracer_1.initTracer)(openTracingConfigFile); } async function loadData({ webhookBody, parentSpan, shouldBuildSchema = true, connector, } = { shouldBuildSchema: true, }) { const activity = reporter_1.default.activityTimer(`update extension data`); activity.start(); try { await framework_hooks_1.frameworkHooks.customizeSchema.before(); await (0, _1.customizeSchema)({ parentSpan, refresh: true, }); await framework_hooks_1.frameworkHooks.customizeSchema.after(); await framework_hooks_1.frameworkHooks.sourceNodes.before(); await (0, _1.sourceNodes)({ parentSpan, webhookBody, store: redux_1.store, webhookSourcePluginName: connector, }); await framework_hooks_1.frameworkHooks.sourceNodes.after(); await framework_hooks_1.frameworkHooks.buildSchema.before(); if (shouldBuildSchema) { await (0, _1.buildSchema)({ parentSpan, }); } await framework_hooks_1.frameworkHooks.buildSchema.after(); (0, redux_1.saveState)(); } finally { activity.end(); } } let app; let server; let serverShouldBeRunning = false; let initialized = false; async function createExpressApp() { const appExists = Boolean(app); if (!appExists && serverShouldBeRunning) { try { program.port = await (0, detect_port_in_use_and_prompt_1.detectPortInUseAndPrompt)(program.port, hostname); } catch (e) { if (e.message === `USER_REJECTED`) { process.exit(0); } throw e; } app = (0, express_1.default)(); app.use((req, res, next) => { try { decodeURIComponent(req.path); } catch (e) { return res.status(500).send(`URI malformatted`); } return next(); }); } } const publicInitialize = async ({ parentSpan }) => { await (0, datastore_1.getDataStore)().ready(); await createExpressApp(); if (!initialized) { console.info(`[content-engine] initializing`); await (0, _1.initialize)({ parentSpan, program, reporter: reporter_1.default, app, engineConfig, }); initialized = true; } }; framework_hooks_1.frameworkHooks.startup.after(); async function startGraphQlServerWithArgs() { serverShouldBeRunning = true; await createExpressApp(); return (0, _1.startGraphQLServer)({ parentSpan: undefined, program, app, store: redux_1.store, reporter: reporter_1.default, loadData, }); } return { config: async () => { throw new Error(`contentEngine().config() can only be called when contentEngine({ runInSubProcess: true }) is set.`); }, onStdOut: () => { throw new Error(`contentEngine().onStdOut() can only be called when contentEngine({ runInSubProcess: true }) is set.`); }, onStdErr: () => { throw new Error(`contentEngine().onStdErr() can only be called when contentEngine({ runInSubProcess: true }) is set.`); }, onMessage: () => { throw new Error(`contentEngine().onMessage() can only be called when contentEngine({ runInSubProcess: true }) is set.`); }, sendMessage: () => { throw new Error(`contentEngine().sendMessage() can only be called when contentEngine({ runInSubProcess: true }) is set.`); }, clearListeners: () => { throw new Error(`contentEngine().clearListeners() can only be called when contentEngine({ runInSubProcess: true }) is set.`); }, initialize: async (args) => { const parentSpan = tracer.startSpan(`initialize`); await publicInitialize({ ...args, parentSpan }); parentSpan.finish(); }, startGraphQLServer: async () => { serverShouldBeRunning = true; await publicInitialize({ parentSpan: undefined }); await startGraphQlServerWithArgs(); }, sync: async ({ runServer, webhookBody, buildSchema = true, connector } = { buildSchema: true, }) => { console.log(`[content-engine] sync started`); await framework_hooks_1.frameworkHooks.sync.before(); if (typeof runServer !== `undefined`) { serverShouldBeRunning = runServer; } const parentSpan = tracer.startSpan(`sync-data`); await publicInitialize({ parentSpan, }); try { await loadData({ parentSpan, webhookBody, shouldBuildSchema: buildSchema, connector, }); } catch (e) { console.error(`[content-engine] error loading data`, e); throw e; } (0, redux_1.savePartialStateToDisk)([`inferenceMetadata`]); if (serverShouldBeRunning && !server) { server = await startGraphQlServerWithArgs(); } if (server && !serverShouldBeRunning) { server.close(); } await (0, datastore_1.getDataStore)().ready(); parentSpan.finish(); if (serverShouldBeRunning && server) { (0, print_instructions_1.printInstructions)(program); } await framework_hooks_1.frameworkHooks.sync.after(); return {}; }, stop: () => { throw new Error(`contentEngine().stop() is only available when running in a subprocess with contentEngine({ runInSubProcess: true }) set.`); }, restart: () => { throw new Error(`contentEngine().restart() is only available when running in a subprocess with contentEngine({ runInSubProcess: true }) set.`); }, test: (0, __1.throwOutsideTestEnv)({ getNodes: index_1.getNodes, getNodesByType: index_1.getNodesByType, getNode: index_1.getNode, query: async (query, variables) => { if (!initialized) { throw new Error(`contentEngine().query() called before first contentEngine().sync() completed.\nSchema customization has not run yet so the GraphQL API cannot be queried.`); } const { schema } = redux_1.store.getState(); const result = await (0, graphql_1.graphql)({ schema, source: query, rootValue: {}, contextValue: (0, context_1.default)({ schema: redux_1.store.getState().schema, schemaComposer: redux_1.store.getState().schemaCustomization.composer, context: {}, customContext: redux_1.store.getState().schemaCustomization.context, }), variableValues: variables, }); return result; }, }), query: async (query, variables, querySettings) => { if (!initialized) { throw new Error(`contentEngine().query() called before first contentEngine().sync() completed.\nSchema customization has not run yet so the GraphQL API cannot be queried.`); } const { schema } = redux_1.store.getState(); const cacheTags = new Set(); const schemaComposer = redux_1.store.getState().schemaCustomization.composer; const context = querySettings?.context ? { ...querySettings.context, cacheTags, schemaComposer } : { cacheTags, schemaComposer }; const result = await (0, graphql_1.graphql)({ schema, source: query, rootValue: {}, contextValue: (0, context_1.default)({ schema: redux_1.store.getState().schema, schemaComposer, context, customContext: redux_1.store.getState().schemaCustomization.context, }), variableValues: variables, }); result.extensions ||= {}; result.extensions.cacheTags = Array.from(cacheTags); return result; }, getProcess() { throw new Error(`contentEngine().process is only available when running in a subprocess with contentEngine({ runInSubProcess: true }) set.`); }, syncLedger: async (args) => { return (0, synchronize_1.syncLedger)({ directory: program.directory, ...args, }); }, async buildSchema() { await framework_hooks_1.frameworkHooks.buildSchema.before(); await (0, _1.buildSchema)({ parentSpan: undefined, }); await framework_hooks_1.frameworkHooks.buildSchema.after(); }, }; }; exports.contentEngine = contentEngine; //# sourceMappingURL=content-engine.js.map