UNPKG

@graphql-hive/gateway

Version:
1,634 lines 56.3 kB
'use strict';

var cluster = require('node:cluster');
var module$1 = require('node:module');
var node_os = require('node:os');
var node_path = require('node:path');
var extraTypings = require('@commander-js/extra-typings');
var logger = require('@graphql-hive/logger');
var renderGraphiql = require('@graphql-yoga/render-graphiql');
var gatewayRuntime = require('@graphql-hive/gateway-runtime');
var pubsub = require('@graphql-hive/pubsub');
var utils = require('@graphql-mesh/utils');
var promises = require('node:fs/promises');
var node_url = require('node:url');
var node_fs = require('node:fs');
var node_http = require('node:http');
var node_https = require('node:https');
var server = require('@whatwg-node/server');
var sdkTraceBase = require('@opentelemetry/sdk-trace-base');
var utils$1 = require('@graphql-tools/utils');
var codeFileLoader = require('@graphql-tools/code-file-loader');
var graphqlFileLoader = require('@graphql-tools/graphql-file-loader');
var load = require('@graphql-tools/load');

function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }

var cluster__default = /*#__PURE__*/_interopDefault(cluster);
var module__default = /*#__PURE__*/_interopDefault(module$1);

function getEnvStr(key, opts = {}) {
  const globalThat = opts.globalThis ?? globalThis;
  let variable = globalThat.process?.env?.[key] || // @ts-expect-error can exist in wrangler and maybe other runtimes
  globalThat.env?.[key] || // @ts-expect-error can exist in deno
  globalThat.Deno?.env?.get(key) || // @ts-expect-error could be
  globalThat[key];
  if (variable != null) {
    variable += "";
  } else {
    variable = void 0;
  }
  return variable?.trim();
}
function getEnvBool(key, opts = {}) {
  return strToBool(getEnvStr(key, opts));
}
function getNodeEnv(opts = {}) {
  return getEnvStr("NODE_ENV", opts);
}
function strToBool(str) {
  return ["1", "t", "true", "y", "yes", "on", "enabled"].includes(
    (str || "").toLowerCase()
  );
}
function isDebug() {
  return getEnvBool("DEBUG");
}
function isNode() {
  return typeof process !== "undefined" && process.versions && process.versions.node && typeof Bun === "undefined";
}

const unit = Object.create(null);
const m = 60000, h = m * 60, d = h * 24, y = d * 365.25;

unit.year = unit.yr = unit.y = y;
unit.month = unit.mo = unit.mth = y / 12;
unit.week = unit.wk = unit.w = d * 7;
unit.day = unit.d = d;
unit.hour = unit.hr = unit.h = h;
unit.minute = unit.min = unit.m = m;
unit.second = unit.sec = unit.s = 1000;
unit.millisecond = unit.millisec = unit.ms = 1;
unit.microsecond = unit.microsec =  unit.us = unit.µs = 1e-3;
unit.nanosecond = unit.nanosec = unit.ns = 1e-6;

unit.group = ',';
unit.decimal = '.';
unit.placeholder = ' _';

const durationRE = /((?:\d{1,16}(?:\.\d{1,16})?|\.\d{1,16})(?:[eE][-+]?\d{1,4})?)\s?([\p{L}]{0,14})/gu;

parse.unit = unit;

/**
 * convert `str` to ms
 *
 * @param {string} str
 * @param {string} format
 * @return {number|null}
 */
function parse(str = '', format = 'ms') {
  let result = null, prevUnits;

  String(str)
    .replace(new RegExp(`(\\d)[${parse.unit.placeholder}${parse.unit.group}](\\d)`, 'g'), '$1$2')  // clean up group separators / placeholders
    .replace(parse.unit.decimal, '.') // normalize decimal separator
    .replace(durationRE, (_, n, units) => {
    // if no units, find next smallest units or fall back to format value
    // eg. 1h30 -> 1h30m
    if (!units) {
      if (prevUnits) {
        for (const u in parse.unit) if (parse.unit[u] < prevUnits) { units = u; break }
      }
      else units = format;
    }
    else units = units.toLowerCase();

    prevUnits = units = parse.unit[units] || parse.unit[units.replace(/s$/, '')];

    if (units) result = (result || 0) + n * units;
  });

  return result && ((result / (parse.unit[format] || 1)) * (str[0] === '-' ? -1 : 1))
}

const defaultConfigExtensions = [
  ".ts",
  ".mts",
  ".cts",
  ".js",
  ".mjs",
  ".cjs"
];
const defaultConfigFileName = "gateway.config";
function createDefaultConfigPaths(configFileName) {
  return defaultConfigExtensions.map((ext) => `${configFileName}${ext}`);
}
async function loadConfig(opts) {
  let importedConfig = null;
  if (!opts.configPath) {
    !opts.quiet && opts.log.debug(`Searching for default config files`);
    const configPaths = [
      ...createDefaultConfigPaths(defaultConfigFileName),
      ...createDefaultConfigPaths(opts.configFileName),
      // For backwards compatibility of Mesh Compose users
      ...createDefaultConfigPaths("mesh.config")
    ];
    for (const configPath of configPaths) {
      const absoluteConfigPath = node_path.join(process.cwd(), configPath);
      const exists = await promises.lstat(absoluteConfigPath).then(() => true).catch(() => false);
      if (exists) {
        !opts.quiet && opts.log.info(`Found default config file ${absoluteConfigPath}`);
        const importUrl = node_url.pathToFileURL(absoluteConfigPath).toString();
        const module = await import(importUrl);
        importedConfig = Object(module).gatewayConfig || null;
        if (!importedConfig && !configPath.includes("mesh.config")) {
          !opts.quiet && opts.log.warn(
            `No "gatewayConfig" exported from config file at ${absoluteConfigPath}`
          );
        }
        break;
      }
    }
  } else {
    const configPath = node_path.isAbsolute(opts.configPath) ? opts.configPath : node_path.join(process.cwd(), opts.configPath);
    !opts.quiet && opts.log.info(`Loading config file at path ${configPath}`);
    const exists = await promises.lstat(configPath).then(() => true).catch(() => false);
    if (!exists) {
      throw new Error(`Cannot find config file at ${configPath}`);
    }
    const importUrl = node_url.pathToFileURL(configPath).toString();
    const module = await import(importUrl);
    importedConfig = Object(module).gatewayConfig || null;
    if (!importedConfig) {
      throw new Error(
        `No "gatewayConfig" exported from config file at ${configPath}`
      );
    }
  }
  if (importedConfig) {
    !opts.quiet && opts.log.info("Loaded config");
  } else {
    !opts.quiet && opts.log.debug("No config loaded");
  }
  return importedConfig || {};
}
async function getBuiltinPluginsFromConfig(config, ctx) {
  const plugins = [];
  if (config.jwt) {
    const { useJWT } = await import('@graphql-mesh/plugin-jwt-auth');
    plugins.push(useJWT(config.jwt));
  }
  if (config.prometheus) {
    const { default: useMeshPrometheus } = await import('@graphql-mesh/plugin-prometheus');
    plugins.push(useMeshPrometheus({ ...config.prometheus, log: ctx.log }));
  }
  if (config.openTelemetry) {
    const { useOpenTelemetry } = await import('@graphql-hive/plugin-opentelemetry');
    plugins.push(useOpenTelemetry({ ...config.openTelemetry, log: ctx.log }));
  }
  if (config.rateLimiting) {
    const { default: useMeshRateLimit } = await import('@graphql-mesh/plugin-rate-limit');
    plugins.push(
      useMeshRateLimit({
        config: Array.isArray(config.rateLimiting) ? config.rateLimiting : typeof config.rateLimiting === "object" ? config.rateLimiting.config : [],
        cache: ctx.cache
      })
    );
  }
  if (config.jit) {
    const { useJIT } = await import('@graphql-mesh/plugin-jit');
    plugins.push(useJIT());
  }
  if (config.awsSigv4) {
    const { useAWSSigv4 } = await import('@graphql-hive/plugin-aws-sigv4');
    plugins.push(useAWSSigv4(config.awsSigv4));
  }
  if (config.maxTokens) {
    const { maxTokensPlugin: useMaxTokens } = await import('@escape.tech/graphql-armor-max-tokens');
    const maxTokensPlugin = useMaxTokens({
      n: typeof config.maxTokens === "number" ? config.maxTokens : 1e3
    });
    plugins.push(
      // @ts-expect-error the armor plugin does not inherit the context
      maxTokensPlugin
    );
  }
  if (config.maxDepth) {
    const { maxDepthPlugin: useMaxDepth } = await import('@escape.tech/graphql-armor-max-depth');
    const maxDepthPlugin = useMaxDepth({
      n: typeof config.maxDepth === "number" ? config.maxDepth : 6
    });
    plugins.push(
      // @ts-expect-error the armor plugin does not inherit the context
      maxDepthPlugin
    );
  }
  if (config.blockFieldSuggestions) {
    const { blockFieldSuggestionsPlugin: useBlockFieldSuggestions } = await import('@escape.tech/graphql-armor-block-field-suggestions');
    const blockFieldSuggestionsPlugin = useBlockFieldSuggestions();
    plugins.push(
      // @ts-expect-error the armor plugin does not inherit the context
      blockFieldSuggestionsPlugin
    );
  }
  return plugins;
}
async function getCacheInstanceFromConfig(config, ctx) {
  if (typeof config.cache === "function") {
    return config.cache(ctx);
  }
  if (config.cache && "type" in config.cache) {
    switch (config.cache.type) {
      case "redis": {
        const { default: RedisCache } = await import('@graphql-mesh/cache-redis');
        return new RedisCache({
          ...ctx,
          ...config.cache,
          // TODO: use new logger
          logger: logger.LegacyLogger.from(ctx.log)
        });
      }
      case "cfw-kv": {
        const { default: CloudflareKVCacheStorage } = await import('@graphql-mesh/cache-cfw-kv');
        return new CloudflareKVCacheStorage({
          ...ctx,
          ...config.cache
        });
      }
      case "upstash-redis": {
        const { default: UpstashRedisCache } = await import('@graphql-mesh/cache-upstash-redis');
        return new UpstashRedisCache({
          ...ctx,
          ...config.cache
        });
      }
    }
    if (config.cache.type !== "localforage") {
      ctx.log.warn(
        "Unknown cache type, falling back to localforage",
        config.cache
      );
    }
    const { default: LocalforageCache2 } = await import('@graphql-mesh/cache-localforage');
    return new LocalforageCache2({
      ...ctx,
      ...config.cache
    });
  }
  if (config.cache) {
    return config.cache;
  }
  const { default: LocalforageCache } = await import('@graphql-mesh/cache-localforage');
  return new LocalforageCache(ctx);
}

async function startBunServer(gwRuntime, opts) {
  const serverOptions = {
    fetch: gwRuntime,
    port: opts.port || defaultOptions.port,
    hostname: opts.host || defaultOptions.host,
    reusePort: true,
    idleTimeout: opts.requestTimeout
  };
  if (opts.sslCredentials) {
    const tlsOptions = {};
    if (opts.sslCredentials.ca_file_name) {
      tlsOptions.ca = Bun.file(opts.sslCredentials.ca_file_name);
    }
    if (opts.sslCredentials.cert_file_name) {
      tlsOptions.cert = Bun.file(opts.sslCredentials.cert_file_name);
    }
    if (opts.sslCredentials.dh_params_file_name) {
      tlsOptions.dhParamsFile = opts.sslCredentials.dh_params_file_name;
    }
    if (opts.sslCredentials.key_file_name) {
      tlsOptions.key = Bun.file(opts.sslCredentials.key_file_name);
    }
    if (opts.sslCredentials.passphrase) {
      tlsOptions.passphrase = opts.sslCredentials.passphrase;
    }
    if (opts.sslCredentials.ssl_ciphers) ;
    if (opts.sslCredentials.ssl_prefer_low_memory_usage) {
      tlsOptions.lowMemoryMode = opts.sslCredentials.ssl_prefer_low_memory_usage;
    }
    serverOptions.tls = tlsOptions;
  }
  if (!opts.disableWebsockets) {
    const { makeHandler } = await import('graphql-ws/use/bun');
    serverOptions.websocket = makeHandler(
      gatewayRuntime.getGraphQLWSOptions(gwRuntime, (ctx) => ({
        socket: ctx.extra.socket,
        ...ctx.extra.socket.data
      }))
    );
    serverOptions.fetch = function(request, server2) {
      if (request.headers.has("Sec-WebSocket-Key") && server2.upgrade(request, {
        data: {
          request
        }
      })) {
        return void 0;
      }
      return gwRuntime.handleRequest(request, server2);
    };
  }
  const server = Bun.serve(serverOptions);
  opts.log.info(`Listening on ${server.url}`);
  gwRuntime.disposableStack.defer(() => server[gatewayRuntime.DisposableSymbols.dispose]());
}

async function startNodeHttpServer(gwRuntime, opts) {
  const {
    log,
    host = defaultOptions.host,
    port = defaultOptions.port,
    sslCredentials,
    maxHeaderSize,
    disableWebsockets,
    requestTimeout
  } = opts;
  let server$1;
  let protocol;
  if (sslCredentials) {
    protocol = "https";
    const sslOptionsForNodeHttp = {};
    if (sslCredentials.ca_file_name) {
      sslOptionsForNodeHttp.ca = await node_fs.promises.readFile(
        sslCredentials.ca_file_name
      );
    }
    if (sslCredentials.cert_file_name) {
      sslOptionsForNodeHttp.cert = await node_fs.promises.readFile(
        sslCredentials.cert_file_name
      );
    }
    if (sslCredentials.dh_params_file_name) {
      sslOptionsForNodeHttp.dhparam = await node_fs.promises.readFile(
        sslCredentials.dh_params_file_name
      );
    }
    if (sslCredentials.key_file_name) {
      sslOptionsForNodeHttp.key = await node_fs.promises.readFile(
        sslCredentials.key_file_name
      );
    }
    if (sslCredentials.passphrase) {
      sslOptionsForNodeHttp.passphrase = sslCredentials.passphrase;
    }
    if (sslCredentials.ssl_ciphers) {
      sslOptionsForNodeHttp.ciphers = sslCredentials.ssl_ciphers;
    }
    if (sslCredentials.ssl_prefer_low_memory_usage) {
      sslOptionsForNodeHttp.honorCipherOrder = true;
    }
    server$1 = node_https.createServer(
      {
        ...sslOptionsForNodeHttp,
        maxHeaderSize,
        requestTimeout
      },
      gwRuntime
    );
  } else {
    protocol = "http";
    server$1 = node_http.createServer(
      {
        maxHeaderSize,
        requestTimeout
      },
      gwRuntime
    );
  }
  const url = `${protocol}://${host}:${port}`.replace("0.0.0.0", "localhost");
  log.debug(`Starting server on ${url}`);
  if (!disableWebsockets) {
    log.debug("Setting up WebSocket server");
    const { WebSocketServer } = await import('ws');
    const wsServer = new WebSocketServer({
      path: gwRuntime.graphqlEndpoint,
      server: server$1
    });
    const { useServer } = await import('graphql-ws/use/ws');
    useServer(
      gatewayRuntime.getGraphQLWSOptions(gwRuntime, (ctx) => ({
        req: ctx.extra.request,
        socket: ctx.extra.socket,
        request: server.normalizeNodeRequest(ctx.extra.request, gwRuntime.fetchAPI)
      })),
      wsServer
    );
    gwRuntime.disposableStack.defer(
      () => new Promise((resolve, reject) => {
        log.info("Stopping the WebSocket server");
        wsServer.close((err) => {
          if (err) {
            return reject(err);
          }
          log.info("Stopped the WebSocket server successfully");
          return resolve();
        });
      })
    );
  }
  return new Promise((resolve, reject) => {
    server$1.once("error", reject);
    server$1.listen(port, host, () => {
      log.info(`Listening on ${url}`);
      gwRuntime.disposableStack.defer(
        () => new Promise((resolve2) => {
          process.stderr.write("\n");
          log.info("Stopping the server");
          server$1.closeAllConnections();
          server$1.close(() => {
            log.info("Stopped the server successfully");
            return resolve2();
          });
        })
      );
      return resolve();
    });
  });
}

function startServerForRuntime(runtime, {
  log,
  host = defaultOptions.host,
  port = defaultOptions.port,
  sslCredentials,
  maxHeaderSize = 16384,
  disableWebsockets = false
}) {
  process.on("message", (message) => {
    if (message === "invalidateUnifiedGraph") {
      log.info("Invalidating Supergraph");
      runtime.invalidateUnifiedGraph();
    }
  });
  const serverOpts = {
    log,
    host,
    port,
    maxHeaderSize,
    disableWebsockets,
    ...sslCredentials ? { sslCredentials } : {}
  };
  const startServer = globalThis.Bun ? startBunServer : startNodeHttpServer;
  return startServer(runtime, serverOpts);
}

function handleFork(log, config) {
  try {
    if (cluster__default.default.isPrimary && config.fork && config.fork > 1) {
      const workers = /* @__PURE__ */ new Set();
      let expectedToExit = false;
      log.debug(`Forking ${config.fork} workers`);
      for (let i = 0; i < config.fork; i++) {
        const worker = cluster__default.default.fork();
        const workerLogger = log.child({ worker: worker.id });
        worker.once("exit", (code, signal) => {
          const logData = {
            signal
          };
          if (code != null) {
            logData["code"] = code;
          }
          if (expectedToExit) {
            workerLogger.debug(logData, "exited");
          } else {
            workerLogger.error(
              logData,
              "Exited unexpectedly. A restart is recommended to ensure the stability of the service"
            );
          }
          workers.delete(worker);
          if (!expectedToExit && workers.size === 0) {
            log.error(logData, "All workers exited unexpectedly. Exiting...");
            process.exit(1);
          }
        });
        workers.add(worker);
      }
      utils.registerTerminateHandler((signal) => {
        log.info(`Killing workers on ${signal}`);
        expectedToExit = true;
        workers.forEach((w) => {
          w.kill(signal);
        });
      });
      return true;
    }
  } catch (e) {
    log.error(
      // @ts-expect-error very likely an instanceof error
      e,
      "Error while forking workers"
    );
  }
  return false;
}

async function handleOpenTelemetryCLIOpts(ctx, cliOpts) {
  const accessToken = cliOpts.hiveTraceAccessToken;
  const traceEndpoint = cliOpts.hiveTraceEndpoint;
  const target = cliOpts.hiveTarget;
  const openTelemetry = cliOpts.openTelemetry;
  const exporterType = cliOpts.openTelemetryExporterType ?? "otlp-http";
  const log = ctx.log.child("[OpenTelemetry] ");
  if (openTelemetry || accessToken) {
    log.debug(
      { openTelemetry, exporterType, target, traceEndpoint },
      "Initializing OpenTelemetry SDK"
    );
    const { openTelemetrySetup, HiveTracingSpanProcessor } = await import('@graphql-hive/plugin-opentelemetry/setup');
    const processors = [];
    const logAttributes = {
      traceEndpoints: [],
      contextManager: false
    };
    let integration;
    if (openTelemetry) {
      const otelEndpoint = typeof openTelemetry === "string" ? openTelemetry : getEnvStr("OTEL_EXPORTER_OTLP_ENDPOINT");
      log.debug({ exporterType, otelEndpoint }, "Setting up OTLP Exporter");
      integration = {
        name: "OpenTelemetry",
        source: { flag: "--opentelemetry", env: "OPENTELEMETRY" }
      };
      logAttributes.traceEndpoints.push({
        url: otelEndpoint ?? null,
        type: exporterType
      });
      log.debug({ type: exporterType }, "Loading OpenTelemetry exporter");
      const { OTLPTraceExporter } = await import(`@opentelemetry/exporter-trace-${exporterType}`);
      processors.push(
        new sdkTraceBase.BatchSpanProcessor(new OTLPTraceExporter({ url: otelEndpoint }))
      );
    }
    if (accessToken) {
      log.debug({ target, traceEndpoint }, "Setting up Hive Tracing");
      integration ??= {
        name: "Hive Tracing",
        source: {
          flag: "--hive-trace-access-token",
          env: "HIVE_TRACE_ACCESS_TOKEN"
        }
      };
      if (!target) {
        ctx.log.error(
          'Hive tracing needs a target. Please provide it through "--hive-target <target>"'
        );
        process.exit(1);
      }
      logAttributes.traceEndpoints.push({
        url: traceEndpoint,
        type: "hive tracing",
        target
      });
      processors.push(
        new HiveTracingSpanProcessor({
          accessToken,
          target,
          endpoint: traceEndpoint
        })
      );
    }
    log.debug("Trying to load AsyncLocalStorage based Context Manager");
    const contextManager = await import('@opentelemetry/context-async-hooks').then((module) => {
      logAttributes.contextManager = true;
      return new module.AsyncLocalStorageContextManager();
    }).catch(() => null);
    openTelemetrySetup({
      log,
      traces: { processors },
      resource: await detectResource().catch((err) => {
        if (err && typeof err === "object" && "code" in err && err.code === "ERR_MODULE_NOT_FOUND") {
          ctx.log.warn(
            err,
            `NodeJS modules necessary for environment detection is missing, please install it to auto-detect the environment`
          );
          return void 0;
        }
        throw err;
      }),
      contextManager,
      _initialization: {
        name: integration.name,
        source: `cli flag (${integration.source.flag}) or environment variables (${integration.source.env})`,
        logAttributes
      }
    });
    return true;
  }
  return false;
}
async function detectResource() {
  if (isNode()) {
    const autoInstrumentationsNodeName = "@opentelemetry/auto-instrumentations-node";
    const { getResourceDetectors } = await import(autoInstrumentationsNodeName);
    const resourcesName = "@opentelemetry/resources";
    const { detectResources } = await import(resourcesName);
    return detectResources({ detectors: getResourceDetectors() });
  }
  return void 0;
}

function handleReportingConfig(ctx, loadedConfig, cliOpts) {
  const confOpts = {
    ...loadedConfig.reporting?.type === "hive" ? {
      hiveRegistryToken: loadedConfig.reporting.token,
      hiveTarget: loadedConfig.reporting.target,
      hiveUsageAccessToken: loadedConfig.reporting.token
    } : {},
    ...loadedConfig.reporting?.type === "graphos" ? {
      apolloGraphRef: loadedConfig.reporting.graphRef,
      apolloKey: loadedConfig.reporting.apiKey
    } : {}
  };
  const opts = {
    ...confOpts,
    ...Object.entries(cliOpts).reduce((acc, [key, val]) => {
      if (val != null) {
        return { ...acc, [key]: val };
      }
      return acc;
    }, {}),
    hiveTarget: (
      // cli arguments always take precedence over config
      confOpts.hiveTarget ?? cliOpts.hiveTarget ?? cliOpts.hiveUsageTarget
    )
  };
  if (cliOpts.hiveRegistryToken && cliOpts.hiveUsageAccessToken) {
    ctx.log.error(
      'Cannot use "--hive-registry-token" with "--hive-usage-access-token". Please use "--hive-usage-target" and "--hive-usage-access-token" or the config instead.'
    );
    process.exit(1);
  }
  if (cliOpts.hiveUsageTarget && cliOpts.hiveTarget) {
    ctx.log.error(
      'Cannot use "--hive-usage-target" with "--hive-target". Please only use "--hive-target"'
    );
    process.exit(1);
  }
  if (cliOpts.hiveRegistryToken && opts.hiveTarget) {
    ctx.log.error(
      'Cannot use "--hive-registry-token" with a target. Please use "--hive-usage-target" and "--hive-usage-access-token" or the config instead.'
    );
    process.exit(1);
  }
  if (opts.hiveTarget && !opts.hiveAccessToken && !opts.hiveUsageAccessToken && !opts.hiveTraceAccessToken) {
    ctx.log.error(
      'Hive usage target needs an access token. Please provide it through "--hive-access-token <token>", or specific "--hive-usage-access-token <token>" and "--hive-trace-access-token" options, or the config.'
    );
    process.exit(1);
  }
  if (!opts.hiveRegistryToken && (opts.hiveAccessToken || opts.hiveUsageAccessToken || opts.hiveTraceAccessToken) && !opts.hiveTarget) {
    ctx.log.error(
      'Hive access token needs a target. Please provide it through the "--hive-target <target>" option or the config.'
    );
    process.exit(1);
  }
  const hiveUsageAccessToken = opts.hiveAccessToken || opts.hiveUsageAccessToken || opts.hiveRegistryToken;
  if (hiveUsageAccessToken) {
    if (opts.hiveUsageTarget) {
      ctx.log.info("Configuring Hive usage reporting");
    } else {
      ctx.log.info("Configuring Hive registry reporting");
    }
    return {
      ...loadedConfig.reporting,
      type: "hive",
      token: hiveUsageAccessToken,
      target: opts.hiveTarget
    };
  }
  if (opts.apolloKey) {
    ctx.log.info("Configuring Apollo GraphOS registry reporting");
    if (!opts.apolloGraphRef?.includes("@")) {
      ctx.log.error(
        `Apollo GraphOS requires a graph ref in the format <graph-id>@<graph-variant>. Please provide a valid graph ref ${opts.apolloGraphRef ? `not ${opts.apolloGraphRef}` : ""}.`
      );
      process.exit(1);
    }
    return {
      ...loadedConfig.reporting,
      type: "graphos",
      apiKey: opts.apolloKey,
      graphRef: opts.apolloGraphRef
    };
  }
  return null;
}

const addCommand$2 = (ctx, cli) => cli.command("proxy").description(
  "serve a proxy to a GraphQL API and add additional features such as monitoring/tracing, caching, rate limiting, security, and more"
).argument("[endpoint]", "URL of the endpoint GraphQL API to proxy").option(
  "--schema <schemaPathOrUrl>",
  "path to the GraphQL schema file or a url from where to pull the schema"
).action(async function proxy(endpoint) {
  const {
    opentelemetry,
    opentelemetryExporterType,
    hiveCdnEndpoint,
    hiveCdnKey,
    hiveRegistryToken,
    hiveTarget,
    hiveUsageTarget,
    hiveAccessToken,
    hiveUsageAccessToken,
    hiveTraceAccessToken,
    hiveTraceEndpoint,
    maskedErrors,
    hivePersistedDocumentsEndpoint,
    hivePersistedDocumentsToken,
    ...opts
  } = this.optsWithGlobals();
  ctx.log.info(`Starting ${ctx.productName} ${ctx.version} in proxy mode`);
  const openTelemetryEnabledByCLI = await handleOpenTelemetryCLIOpts(ctx, {
    openTelemetry: opentelemetry,
    openTelemetryExporterType: opentelemetryExporterType,
    hiveTarget,
    hiveTraceAccessToken,
    hiveTraceEndpoint
  });
  const loadedConfig = await loadConfig({
    log: ctx.log,
    configPath: opts.configPath,
    quiet: !cluster__default.default.isPrimary,
    configFileName: ctx.configFileName
  });
  let proxy2;
  if (endpoint) {
    proxy2 = { endpoint };
  } else if ("proxy" in loadedConfig) {
    proxy2 = loadedConfig.proxy;
  }
  if (!proxy2) {
    ctx.log.error(
      "Proxy endpoint not defined. Please provide it in the [endpoint] argument or in the config file."
    );
    process.exit(1);
  }
  let schema;
  const hiveCdnEndpointOpt = (
    // TODO: take schema from optsWithGlobals once https://github.com/commander-js/extra-typings/pull/76 is merged
    this.opts().schema || hiveCdnEndpoint
  );
  if (hiveCdnEndpointOpt) {
    if (hiveCdnKey) {
      if (!utils.isUrl(hiveCdnEndpointOpt)) {
        ctx.log.error(
          "Endpoint must be a URL when providing --hive-cdn-key but got " + hiveCdnEndpointOpt
        );
        process.exit(1);
      }
      schema = {
        type: "hive",
        endpoint: hiveCdnEndpointOpt,
        // see validation above
        key: hiveCdnKey
      };
    } else {
      schema = this.opts().schema;
    }
  } else if ("schema" in loadedConfig) {
    schema = loadedConfig.schema;
  }
  if (hiveCdnKey && !schema) {
    process.stderr.write(
      `error: option '--schema <schemaPathOrUrl>' is required when providing '--hive-cdn-key <key>'
`
    );
    process.exit(1);
  }
  const registryConfig = {};
  const reporting = handleReportingConfig(ctx, loadedConfig, {
    hiveRegistryToken,
    hiveTarget,
    hiveUsageTarget,
    hiveAccessToken,
    hiveUsageAccessToken,
    hiveTraceAccessToken,
    // proxy can only do reporting to hive registry
    apolloGraphRef: void 0,
    apolloKey: void 0
  });
  if (reporting) {
    registryConfig.reporting = reporting;
  }
  const pubsub$1 = loadedConfig.pubsub || new pubsub.MemPubSub();
  const cwd = loadedConfig.cwd || process.cwd();
  if (loadedConfig.logging != null) {
    ctx.log = gatewayRuntime.createLoggerFromLogging(loadedConfig.logging);
  }
  const cache = await getCacheInstanceFromConfig(loadedConfig, {
    pubsub: pubsub$1,
    log: ctx.log,
    cwd
  });
  const builtinPlugins = await getBuiltinPluginsFromConfig(
    {
      ...loadedConfig,
      ...opts,
      openTelemetry: openTelemetryEnabledByCLI ? { ...loadedConfig.openTelemetry, traces: true } : loadedConfig.openTelemetry
    },
    {
      log: ctx.log,
      cache}
  );
  const config = {
    ...defaultOptions,
    ...loadedConfig,
    ...opts,
    pollingInterval: opts.polling || ("pollingInterval" in loadedConfig ? loadedConfig.pollingInterval : void 0) || defaultOptions.pollingInterval,
    ...registryConfig,
    proxy: proxy2,
    schema,
    logging: ctx.log,
    productName: ctx.productName,
    productDescription: ctx.productDescription,
    productPackageName: ctx.productPackageName,
    productLink: ctx.productLink,
    ...ctx.productLogo ? { productLogo: ctx.productLogo } : {},
    pubsub: pubsub$1,
    cache,
    plugins(ctx2) {
      const userPlugins = loadedConfig.plugins?.(ctx2) ?? [];
      return [...builtinPlugins, ...userPlugins];
    }
  };
  if (hivePersistedDocumentsEndpoint) {
    const token = hivePersistedDocumentsToken || loadedConfig.persistedDocuments && "token" in loadedConfig.persistedDocuments && loadedConfig.persistedDocuments.token;
    if (!token) {
      ctx.log.error(
        `Hive persisted documents needs a CDN token. Please provide it through the "--hive-persisted-documents-token <token>" option or the config.`
      );
      process.exit(1);
    }
    config.persistedDocuments = {
      ...loadedConfig.persistedDocuments,
      type: "hive",
      endpoint: hivePersistedDocumentsEndpoint,
      token
    };
  }
  if (maskedErrors != null) {
    config.maskedErrors = maskedErrors;
  }
  if (typeof config.pollingInterval === "number" && config.pollingInterval < 1e4) {
    process.stderr.write(
      `error: polling interval duration too short ${config.pollingInterval}, use at least 10 seconds
`
    );
    process.exit(1);
  }
  return runProxy(ctx, config);
});
async function runProxy({ log }, config) {
  if (handleFork(log, config)) {
    return;
  }
  const runtime = gatewayRuntime.createGatewayRuntime(config);
  log.info({ endpoint: config.proxy.endpoint }, "Loading schema");
  await runtime.getSchema();
  log.info({ endpoint: config.proxy.endpoint }, "Proxying requests");
  await startServerForRuntime(runtime, {
    ...config,
    log
  });
}

const addCommand$1 = (ctx, cli) => cli.command("subgraph").description(
  "serve a Federation subgraph that can be used with any Federation compatible router like Apollo Router/Gateway"
).argument(
  "[schemaPathOrUrl]",
  'path to the subgraph schema file or a url from where to pull the subgraph schema (default: "subgraph.graphql")'
).action(async function subgraph(schemaPathOrUrl) {
  const {
    opentelemetry,
    opentelemetryExporterType,
    maskedErrors,
    hiveRegistryToken,
    hiveTarget,
    hiveUsageTarget,
    hiveAccessToken,
    hiveUsageAccessToken,
    hiveTraceAccessToken,
    hiveTraceEndpoint,
    hivePersistedDocumentsEndpoint,
    hivePersistedDocumentsToken,
    ...opts
  } = this.optsWithGlobals();
  ctx.log.info(`Starting ${ctx.productName} ${ctx.version} as subgraph`);
  const openTelemetryEnabledByCLI = await handleOpenTelemetryCLIOpts(ctx, {
    openTelemetry: opentelemetry,
    openTelemetryExporterType: opentelemetryExporterType,
    hiveTarget,
    hiveTraceAccessToken,
    hiveTraceEndpoint
  });
  const loadedConfig = await loadConfig({
    log: ctx.log,
    configPath: opts.configPath,
    quiet: !cluster__default.default.isPrimary,
    configFileName: ctx.configFileName
  });
  let subgraph2 = "subgraph.graphql";
  if (schemaPathOrUrl) {
    subgraph2 = schemaPathOrUrl;
  } else if ("subgraph" in loadedConfig) {
    subgraph2 = loadedConfig.subgraph;
  }
  const registryConfig = {};
  const reporting = handleReportingConfig(ctx, loadedConfig, {
    hiveRegistryToken,
    hiveTarget,
    hiveUsageTarget,
    hiveAccessToken,
    hiveUsageAccessToken,
    hiveTraceAccessToken,
    // subgraph can only do reporting to hive registry
    apolloGraphRef: void 0,
    apolloKey: void 0
  });
  if (reporting) {
    registryConfig.reporting = reporting;
  }
  const pubsub$1 = loadedConfig.pubsub || new pubsub.MemPubSub();
  const cwd = loadedConfig.cwd || process.cwd();
  if (loadedConfig.logging != null) {
    ctx.log = gatewayRuntime.createLoggerFromLogging(loadedConfig.logging);
  }
  const cache = await getCacheInstanceFromConfig(loadedConfig, {
    pubsub: pubsub$1,
    log: ctx.log,
    cwd
  });
  const builtinPlugins = await getBuiltinPluginsFromConfig(
    {
      ...loadedConfig,
      ...opts,
      openTelemetry: openTelemetryEnabledByCLI ? { ...loadedConfig.openTelemetry, traces: true } : loadedConfig.openTelemetry
    },
    {
      log: ctx.log,
      cache}
  );
  const config = {
    ...defaultOptions,
    ...loadedConfig,
    ...opts,
    pollingInterval: opts.polling || ("pollingInterval" in loadedConfig ? loadedConfig.pollingInterval : void 0) || defaultOptions.pollingInterval,
    ...registryConfig,
    subgraph: subgraph2,
    logging: loadedConfig.logging ?? ctx.log,
    productName: ctx.productName,
    productDescription: ctx.productDescription,
    productPackageName: ctx.productPackageName,
    productLink: ctx.productLink,
    productLogo: ctx.productLogo,
    pubsub: pubsub$1,
    cache,
    plugins(ctx2) {
      const userPlugins = loadedConfig.plugins?.(ctx2) ?? [];
      return [...builtinPlugins, ...userPlugins];
    }
  };
  if (hivePersistedDocumentsEndpoint) {
    const token = hivePersistedDocumentsToken || loadedConfig.persistedDocuments && "token" in loadedConfig.persistedDocuments && loadedConfig.persistedDocuments.token;
    if (!token) {
      ctx.log.error(
        `Hive persisted documents needs a CDN token. Please provide it through the "--hive-persisted-documents-token <token>" option or the config.`
      );
      process.exit(1);
    }
    config.persistedDocuments = {
      ...loadedConfig.persistedDocuments,
      type: "hive",
      endpoint: hivePersistedDocumentsEndpoint,
      token
    };
  }
  if (maskedErrors != null) {
    config.maskedErrors = maskedErrors;
  }
  if (typeof config.pollingInterval === "number" && config.pollingInterval < 1e4) {
    process.stderr.write(
      `error: polling interval duration too short ${config.pollingInterval}, use at least 10 seconds
`
    );
    process.exit(1);
  }
  return runSubgraph(ctx, config);
});
async function runSubgraph({ log }, config) {
  let absSchemaPath = null;
  if (typeof config.subgraph === "string" && utils$1.isValidPath(config.subgraph) && !utils.isUrl(config.subgraph)) {
    const subgraphPath = config.subgraph;
    absSchemaPath = node_path.isAbsolute(subgraphPath) ? String(subgraphPath) : node_path.resolve(process.cwd(), subgraphPath);
    try {
      await promises.lstat(absSchemaPath);
    } catch {
      throw new Error(`Subgraph schema at ${absSchemaPath} does not exist`);
    }
  }
  if (handleFork(log, config)) {
    return;
  }
  const runtime = gatewayRuntime.createGatewayRuntime(config);
  if (absSchemaPath) {
    log.info(`Loading local subgraph from ${absSchemaPath}`);
  } else if (utils.isUrl(String(config.subgraph))) {
    log.info(`Loading remote subgraph from ${config.subgraph}`);
  } else {
    log.info("Loading subgraph from config");
  }
  await runtime.getSchema();
  await startServerForRuntime(runtime, {
    ...config,
    log
  });
}

const addCommand = (ctx, cli) => cli.command("supergraph").description(
  "serve a Federation supergraph provided by a compliant composition tool such as Mesh Compose or Apollo Rover"
).argument(
  "[schemaPathOrUrl]",
  'path to the composed supergraph schema file or a url from where to pull the supergraph schema (default: "supergraph.graphql")'
).addOption(
  new extraTypings.Option(
    "--apollo-uplink <uplink>",
    "The URL of the managed federation up link. When retrying after a failure, you should cycle through the default up links using this option."
  ).env("APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT")
).addOption(
  new extraTypings.Option(
    "--hive-router-runtime",
    "Use the Hive Router runtime for query planning and execution (env: HIVE_ROUTER_RUNTIME)"
  ).env("HIVE_ROUTER_RUNTIME")
).on("optionEnv:hive-router-runtime", function() {
  this.setOptionValueWithSource(
    "hiveRouterRuntime",
    // must be camelCase
    getEnvBool("HIVE_ROUTER_RUNTIME"),
    "env"
  );
}).action(async function supergraph(schemaPathOrUrl) {
  const {
    opentelemetry,
    opentelemetryExporterType,
    hiveCdnEndpoint,
    hiveCdnKey,
    hiveRegistryToken,
    hiveUsageTarget,
    hiveTarget,
    hiveAccessToken,
    hiveUsageAccessToken,
    hiveTraceAccessToken,
    hiveTraceEndpoint,
    maskedErrors,
    apolloGraphRef,
    apolloKey,
    hivePersistedDocumentsEndpoint,
    hivePersistedDocumentsToken,
    hivePersistedDocumentsCacheTtl,
    hivePersistedDocumentsCacheNotFoundTtl,
    ...opts
  } = this.optsWithGlobals();
  const { apolloUplink } = this.opts();
  ctx.log.info(
    `Starting ${ctx.productName} ${ctx.version} with supergraph`
  );
  const openTelemetryEnabledByCLI = await handleOpenTelemetryCLIOpts(ctx, {
    openTelemetry: opentelemetry,
    openTelemetryExporterType: opentelemetryExporterType,
    hiveTarget,
    hiveTraceAccessToken,
    hiveTraceEndpoint
  });
  const loadedConfig = await loadConfig({
    log: ctx.log,
    configPath: opts.configPath,
    quiet: !cluster__default.default.isPrimary,
    configFileName: ctx.configFileName
  });
  let supergraph2 = "./supergraph.graphql";
  if (schemaPathOrUrl) {
    ctx.log.info(`Supergraph will be loaded from "${schemaPathOrUrl}"`);
    if (hiveCdnKey) {
      ctx.log.info("Using Hive CDN key");
      if (!utils.isUrl(schemaPathOrUrl)) {
        ctx.log.error(
          `Hive CDN endpoint must be a URL when providing --hive-cdn-key but got "${schemaPathOrUrl}"`
        );
        process.exit(1);
      }
      supergraph2 = {
        type: "hive",
        endpoint: schemaPathOrUrl,
        key: hiveCdnKey
      };
    } else if (apolloKey) {
      ctx.log.info("Using GraphOS API key");
      if (!schemaPathOrUrl.includes("@")) {
        ctx.log.error(
          `Apollo GraphOS requires a graph ref in the format <graph-id>@<graph-variant> when providing --apollo-key. Please provide a valid graph ref not "${schemaPathOrUrl}".`
        );
        process.exit(1);
      }
      supergraph2 = {
        type: "graphos",
        apiKey: apolloKey,
        graphRef: schemaPathOrUrl,
        ...apolloUplink ? { upLink: apolloUplink } : {}
      };
    } else {
      supergraph2 = schemaPathOrUrl;
    }
  } else if (hiveCdnEndpoint) {
    if (!utils.isUrl(hiveCdnEndpoint)) {
      ctx.log.error(
        `Hive CDN endpoint must be a valid URL but got ${hiveCdnEndpoint}. Please provide a valid URL.`
      );
      process.exit(1);
    }
    if (!hiveCdnKey) {
      ctx.log.error(
        `Hive CDN requires an API key. Please provide an API key using the --hive-cdn-key option. Learn more at https://the-guild.dev/graphql/hive/docs/features/high-availability-cdn#cdn-access-tokens`
      );
      process.exit(1);
    }
    ctx.log.info(`Using Hive CDN endpoint ${hiveCdnEndpoint}`);
    supergraph2 = {
      type: "hive",
      endpoint: hiveCdnEndpoint,
      key: hiveCdnKey
    };
  } else if (apolloGraphRef) {
    if (!apolloGraphRef.includes("@")) {
      ctx.log.error(
        `Apollo GraphOS requires a graph ref in the format <graph-id>@<graph-variant>. Please provide a valid graph ref not ${apolloGraphRef}.`
      );
      process.exit(1);
    }
    if (!apolloKey) {
      ctx.log.error(
        "Apollo GraphOS requires an API key. Please provide an API key using the --apollo-key option."
      );
      process.exit(1);
    }
    ctx.log.info(`Using Apollo Graph Ref ${apolloGraphRef}`);
    supergraph2 = {
      type: "graphos",
      apiKey: apolloKey,
      graphRef: apolloGraphRef,
      upLink: apolloUplink
    };
  } else if ("supergraph" in loadedConfig) {
    supergraph2 = loadedConfig.supergraph;
  } else {
    ctx.log.info(`Using default supergraph location "${supergraph2}"`);
  }
  const registryConfig = {};
  const reporting = handleReportingConfig(ctx, loadedConfig, {
    hiveTarget,
    hiveAccessToken,
    hiveTraceAccessToken,
    hiveRegistryToken,
    hiveUsageTarget,
    hiveUsageAccessToken,
    apolloGraphRef: apolloGraphRef || schemaPathOrUrl,
    apolloKey
  });
  if (reporting) {
    registryConfig.reporting = reporting;
  }
  const pubsub$1 = loadedConfig.pubsub || new pubsub.MemPubSub();
  const cwd = loadedConfig.cwd || process.cwd();
  if (loadedConfig.logging != null) {
    ctx.log = gatewayRuntime.createLoggerFromLogging(loadedConfig.logging);
  }
  const cache = await getCacheInstanceFromConfig(loadedConfig, {
    pubsub: pubsub$1,
    log: ctx.log,
    cwd
  });
  const builtinPlugins = await getBuiltinPluginsFromConfig(
    {
      ...loadedConfig,
      ...opts,
      openTelemetry: openTelemetryEnabledByCLI ? { ...loadedConfig.openTelemetry, traces: true } : loadedConfig.openTelemetry
    },
    {
      log: ctx.log,
      cache}
  );
  const config = {
    ...defaultOptions,
    ...loadedConfig,
    ...opts,
    pollingInterval: opts.polling || ("pollingInterval" in loadedConfig ? loadedConfig.pollingInterval : void 0) || defaultOptions.pollingInterval,
    ...registryConfig,
    supergraph: supergraph2,
    logging: ctx.log,
    productName: ctx.productName,
    productDescription: ctx.productDescription,
    productPackageName: ctx.productPackageName,
    productLink: ctx.productLink,
    productLogo: ctx.productLogo,
    pubsub: pubsub$1,
    cache,
    plugins(ctx2) {
      const userPlugins = loadedConfig.plugins?.(ctx2) ?? [];
      return [...builtinPlugins, ...userPlugins];
    }
  };
  if (opts.hiveRouterRuntime && !config.unifiedGraphHandler) {
    ctx.log.warn("Using Hive Router Runtime");
    try {
      ctx.log.debug("Loading @graphql-hive/router-runtime package");
      const moduleName = "@graphql-hive/router-runtime";
      const { unifiedGraphHandler } = await import(moduleName);
      config.unifiedGraphHandler ||= unifiedGraphHandler;
    } catch (e) {
      ctx.log.warn(
        "Could not load the @graphql-hive/router-runtime package. Please install it to use the Router Runtime. Falling back to the default runtime."
      );
    }
  }
  if (hivePersistedDocumentsEndpoint) {
    const token = hivePersistedDocumentsToken || loadedConfig.persistedDocuments && "token" in loadedConfig.persistedDocuments && loadedConfig.persistedDocuments.token;
    if (!token) {
      ctx.log.error(
        'Hive persisted documents needs a CDN token. Please provide it through the "--hive-persisted-documents-token <token>" option or the config.'
      );
      process.exit(1);
    }
    config.persistedDocuments = {
      ...loadedConfig.persistedDocuments,
      type: "hive",
      endpoint: hivePersistedDocumentsEndpoint,
      token,
      // Apply cache options from CLI (CLI takes precedence over config)
      ...hivePersistedDocumentsCacheTtl != null ? { cacheTtlSeconds: hivePersistedDocumentsCacheTtl } : {},
      ...hivePersistedDocumentsCacheNotFoundTtl != null ? {
        cacheNotFoundTtlSeconds: hivePersistedDocumentsCacheNotFoundTtl
      } : {}
    };
  }
  if (maskedErrors != null) {
    config.maskedErrors = maskedErrors;
  }
  if (typeof config.pollingInterval === "number" && config.pollingInterval < 1e4) {
    process.stderr.write(
      `error: polling interval duration too short ${config.pollingInterval}, use at least 10 seconds
`
    );
    process.exit(1);
  }
  return runSupergraph(ctx, config);
}).allowUnknownOption(getNodeEnv() === "test").allowExcessArguments(getNodeEnv() === "test");
async function runSupergraph({ log }, config) {
  let absSchemaPath = null;
  if (typeof config.supergraph === "string" && utils$1.isValidPath(config.supergraph) && !utils.isUrl(config.supergraph)) {
    const supergraphPath = config.supergraph;
    absSchemaPath = node_path.isAbsolute(supergraphPath) ? String(supergraphPath) : node_path.resolve(process.cwd(), supergraphPath);
    try {
      await promises.lstat(absSchemaPath);
    } catch (err) {
      log.error(
        { path: absSchemaPath, err },
        "Could not find supergraph. Make sure the file exists."
      );
      process.exit(1);
    }
  }
  if (absSchemaPath) {
    delete config.pollingInterval;
    if (cluster__default.default.isPrimary) {
      log.info({ path: absSchemaPath }, "Watching supergraph file for changes");
      const ctrl = new AbortController();
      utils.registerTerminateHandler((signal) => {
        log.info(
          { path: absSchemaPath },
          `Closing watcher for supergraph on ${signal}`
        );
        return ctrl.abort(`Process terminated on ${signal}`);
      });
      (async function watcher() {
        for await (const f of promises.watch(absSchemaPath, {
          signal: ctrl.signal
        })) {
          if (f.eventType === "rename") {
            throw new Error(`Supergraph file was renamed to "${f.filename}"`);
          }
          log.info(
            { path: absSchemaPath },
            "Supergraph changed. Invalidating..."
          );
          if (config.fork && config.fork > 1) {
            for (const workerId in cluster__default.default.workers) {
              cluster__default.default.workers[workerId].send("invalidateUnifiedGraph");
            }
          } else {
            runtime.invalidateUnifiedGraph();
          }
        }
      })().catch((e) => {
        if (e.name === "AbortError") return;
        log.error(
          { path: absSchemaPath, err: e },
          "Supergraph watcher closed with an error"
        );
      }).then(() => {
        log.info(
          { path: absSchemaPath },
          "Supergraph watcher successfuly closed"
        );
      });
    }
  }
  if (handleFork(log, config)) {
    return;
  }
  if (config.additionalTypeDefs) {
    const loaders = [new graphqlFileLoader.GraphQLFileLoader(), new codeFileLoader.CodeFileLoader()];
    const additionalTypeDefsArr = utils$1.asArray(config.additionalTypeDefs);
    config.additionalTypeDefs = await Promise.all(
      additionalTypeDefsArr.flatMap(async (ptr) => {
        if (typeof ptr === "string" && ptr.length <= 255 && utils$1.isValidPath(ptr)) {
          const sources = await load.loadTypedefs(ptr, {
            loaders
          });
          return sources.map((source) => {
            const typeSource = source.document || source.rawSDL || source.schema;
            if (!typeSource) {
              throw new Error(`Invalid source ${source.location || ptr}`);
            }
            return typeSource;
          });
        }
        return ptr;
      })
    );
  }
  const runtime = gatewayRuntime.createGatewayRuntime(config);
  if (absSchemaPath) {
    log.info({ path: absSchemaPath }, "Loading local supergraph");
  } else if (utils.isUrl(String(config.supergraph))) {
    log.info({ url: config.supergraph }, "Loading remote supergraph");
  } else if (typeof config.supergraph === "object" && "type" in config.supergraph && config.supergraph.type === "hive") {
    log.info(
      { endpoint: config.supergraph.endpoint },
      "Loading supergraph from Hive CDN"
    );
  } else {
    log.info("Loading supergraph from config");
  }
  await runtime.getSchema();
  await startServerForRuntime(runtime, {
    ...config,
    log
  });
}

const addCommands = (ctx, cli) => {
  addCommand(ctx, cli);
  addCommand$1(ctx, cli);
  addCommand$2(ctx, cli);
};

function getFreeMemInGb() {
  return node_os.freemem() / 1024 ** 2;
}
function getMaxConcurrencyPerMem() {
  return parseInt(String(getFreeMemInGb()));
}
function getMaxConcurrencyPerCpu() {
  return node_os.availableParallelism();
}
function getMaxConcurrency() {
  const result = Math.min(getMaxConcurrencyPerMem(), getMaxConcurrencyPerCpu());
  if (result < 1) {
    return 1;
  }
  return result;
}

function defineConfig(config) {
  return config;
}
const defaultOptions = {
  fork: 1,
  host: node_os.platform().toLowerCase() === "win32" || // is WSL?
  node_os.release().toLowerCase().includes("microsoft") ? "127.0.0.1" : "0.0.0.0",
  port: 4e3,
  pollingInterval: 1e4,
  renderGraphiQL: renderGraphiql.renderGraphiQL
};
let cli = new extraTypings.Command().configureHelp({
  // will print help of global options for each command
  showGlobalOptions: true
}).addOption(
  new extraTypings.Option(
    "--fork <number>",
    `number of workers to spawn. (default: ${defaultOptions.fork})`
  ).env("FORK").argParser((v) => {
    const number = parseInt(v);
    if (isNaN(number)) {
      throw new extraTypings.InvalidArgumentError("not a number.");
    }
    const maxConcurrency = getMaxConcurrency();
    if (number > maxConcurrency) {
      throw new extraTypings.InvalidArgumentError(
        `exceedes number of available concurrency "${maxConcurrency}".`
      );
    }
    return number;
  })
).addOption(
  new extraTypings.Option(
    "-c, --config-path <path>",
    `path to the configuration file. defaults to the following files respectively in the current working directory: ${createDefaultConfigPaths("gateway").join(", ")}`
  ).env("CONFIG_PATH")
).addOption(
  new extraTypings.Option(
    "-h, --host <hostname>",
    `host to use for serving (default: ${defaultOptions.host})`
  )
).addOption(
  new extraTypings.Option(
    "-p, --port <number>",
    `port to use for serving (default: ${defaultOptions.port})`
  ).env("PORT").argParser((v) => {
    const port = parseInt(v);
    if (isNaN(port)) {
      throw new extraTypings.InvalidArgumentError("not a number.");
    }
    return port;
  })
).addOption(
  new extraTypings.Option(
    "--polling <duration>",
    `schema polling interval in human readable duration (default: 10s)`
  ).env("POLLING").argParser((v) => {
    const interval = parse(v);
    if (!interval) {
      throw new extraTypings.InvalidArgumentError("not a duration.");
    }
    return interval;
  })
).option("--no-masked-errors", "don't mask unexpected errors in responses").option(
  "--masked-errors",
  "mask unexpected errors in responses (default: true)",
  // we use "null" intentionally so that we know when the user provided the flag vs when not
  // see here https://github.com/tj/commander.js/blob/970ecae402b253de691e6a9066fea22f38fe7431/lib/command.js#L655
  // @ts-expect-error
  null
).addOption(
  new extraTypings.Option(
    "--opentelemetry [exporter-endpoint]",
    `Enable OpenTelemetry integration with an exporter using this option's value as endpoint. By default, it uses OTLP HTTP, use "--opentelemetry-exporter-type" to change the default.`
  ).env("OPENTELEMETRY")
).addOption(
  new extraTypings.Option(
    "--opentelemetry-exporter-type <type>",
    `OpenTelemetry exporter type to use when setting up OpenTelemetry integration. Requires "--opentelemetry" to set the endpoint.`
  ).choices(["otlp-http", "otlp-grpc"]).default("otlp-http").env("OPENTELEMETRY_EXPORTER_TYPE")
).addOption(
  new extraTypings.Option(
    "--hive-registry-token <token>",
    '[DEPRECATED] please use "--hive-target" and "--hive-access-token"'
  ).env("HIVE_REGISTRY_TOKEN")
).addOption(
  new extraTypings.Option(
    "--hive-usage-target <target>",
    "[DEPRECATED] please use --hive-target instead."
  ).env("HIVE_USAGE_TARGET")
).addOption(
  new extraTypings.Option(
    "--hive-target <target>",
    'Hive registry target to which the usage and tracing data should be reported to. Requires either "--hive-access-token <token>", "--hive-usage-access-token <token>" or "--hive-trace-access-token" option'
  ).env("HIVE_TARGET")
).addOption(
  new extraTypings.Option(
    "--hive-access-token <token>",
    'Hive registry access token for usage metrics reporting and tracing. Enables both usage reporting and tracing. Requires the "--hive-target <target>" option'
  ).env("HIVE_ACCESS_TOKEN")
).addOption(
  new extraTypings.Option(
    "--hive-usage-access-token <token>",
    `Hive registry access token for usage reporting. Enables Hive usage report. Requires the "--hive-target <target>" option. It can't be used together with "--hive-access-token"`
  ).env("HIVE_USAGE_ACCESS_TOKEN")
).addOption(
  new extraTypings.Option(
    "--hive-trace-access-token <token>",
    `Hive registry access token for tracing. Enables Hive tracing. Requires the "--hive-target <target>" option. It can't be used together with "--hive-access-token"`
  ).env("HIVE_TRACE_ACCESS_TOKEN")
).addOption(
  new extraTypings.Option(
    "--hive-trace-endpoint <endpoint>",
    `Hive registry tracing endpoint.`
  ).env("HIVE_TRACE_ENDPOINT").default(`https://api.graphql-hive.com/otel/v1/traces`)
).option(
  "--hive-persisted-documents-endpoint <endpoint>",
  '[EXPERIMENTAL] Hive CDN endpoint for fetching the persisted documents. Requires the "--hive-persisted-documents-token <token>" option'
).option(
  "--hive-persisted-documents-token <token>",
  '[EXPERIMENTAL] Hive persisted documents CDN endpoint token. Requires the "--hive-persisted-documents-endpoint <endpoint>" option'
).option(
  "--hive-persisted-documents-cache-ttl <seconds>",
  "[EXPERIMENTAL] TTL in seconds for cached persisted documents",
  (v) => {
    const ttl = parseInt(v);
    if (isNaN(ttl) || ttl < 0) {
      throw new extraTypings.InvalidArgumentError(
        "TTL must be a non-negative number of seconds."
      );
    }
    return ttl;
  }
).option(
  "--hive-persisted-documents-cache-not-found-ttl <seconds>",
  "[EXPERIMENTAL] TTL in seconds for negative cache entries (default: 60)",
  (v) => {
    const ttl = parseInt(v);
    if (isNaN(ttl) || ttl < 0) {
      throw new extraTypings.InvalidArgumentError(
        "TTL must be a non-negative number of seconds."
      );
    }
    return ttl;
  }
).addOption(
  new extraTypings.Option(
    "--hive-cdn-endpoint <endpoint>",
    "Hive CDN endpoint for fetching the schema"
  ).env("HIVE_CDN_ENDPOINT")
).addOption(
  new extraTypings.Option(
    "--hive-cdn-key <key>",
    'Hive CDN API key for fetching the schema. implies that the "schemaPathOrUrl" argument is a url'
  ).env("HIVE_CDN_KEY")
).addOption(
  new extraTypings.Option(
    "--apollo-graph-ref <graphRef>",
    "Apollo graph ref of the managed federation graph (<YOUR_GRAPH_ID>@<VARIANT>)"
  ).env("APOLLO_GRAPH_REF")
).addOption(
  new extraTypings.Option(
    "--apollo-key <apiKey>",
    "Apollo API key to use to authenticate with the managed federation up link"
  ).env("APOLLO_KEY")
).option("--disable-websockets", "Disable WebSockets support").addOption(
  new extraTypings.Option(
    "--jit",
    "Enable Just-In-Time compilation of GraphQL documents (env: JIT)"
  ).env("JIT")
).on("optionEnv:jit", function() {
  this.setOptionValueWithSource("jit", getEnvBool("JIT"), "env");
});
async function run(userCtx) {
  const ctx = {
    log: userCtx.log || new logger.Logger(),
    productName: "Hive Gateway",
    productDescription: "Unify and accelerate your data graph across diverse services with Hive Gateway, which seamlessly integrates with Apollo Federation.",
    productPackageName: "@graphql-hive/gateway",
    productLink: "https://the-guild.dev/graphql/hive/docs/gateway",
    binName: "hive-gateway",
    configFileName: "gateway.config",
    version: globalThis.__VERSION__ || "unknown",
    ...userCtx
  };
  const { binName, productDescription, version } = ctx;
  cli = cli.name(binName).description(productDescription).version(version);
  if (cluster__default.default.worker?.id) {
    ctx.log = ctx.log.child({ worker: cluster__default.default.worker.id });
  }
  addCommands(ctx, cli);
  return cli.parseAsync();
}
function handleNodeWarnings() {
  const originalProcessEmitWarning = process.emitWarning.bind(process);
  process.emitWarning = function gatewayEmitWarning(warning, ...opts) {
    if (isDebug()) {
      originalProcessEmitWarning(warning, ...opts);
    }
  };
}
function enableModuleCachingIfPossible() {
  let cacheDir;
  if (globalThis.__PACKED_DEPS_PATH__) {
    cacheDir = node_path.join(globalThis.__PACKED_DEPS_PATH__, "node-compile-cache");
  }
  if (module__default.default.enableCompileCache) {
    module__default.default.enableCompileCache(cacheDir);
  }
}

exports.defaultOptions = defaultOptions;
exports.defineConfig = defineConfig;
exports.enableModuleCachingIfPossible = enableModuleCachingIfPossible;
exports.getBuiltinPluginsFromConfig = getBuiltinPluginsFromConfig;
exports.getCacheInstanceFromConfig = getCacheInstanceFromConfig;
exports.handleNodeWarnings = handleNodeWarnings;
exports.run = run;