UNPKG

@graphql-hive/gateway-runtime

Version:
1,297 lines (1,274 loc) • 73.1 kB
'use strict'; var disableIntrospection = require('@envelop/disable-introspection'); var genericAuth = require('@envelop/generic-auth'); var core$1 = require('@graphql-hive/core'); var fusionRuntime = require('@graphql-mesh/fusion-runtime'); var hmacUpstreamSignature = require('@graphql-mesh/hmac-upstream-signature'); var useMeshHive = require('@graphql-mesh/plugin-hive'); var useMeshResponseCache = require('@graphql-mesh/plugin-response-cache'); var utils = require('@graphql-mesh/utils'); var batchDelegate = require('@graphql-tools/batch-delegate'); var delegate = require('@graphql-tools/delegate'); var executorCommon = require('@graphql-tools/executor-common'); var utils$1 = require('@graphql-tools/utils'); var wrap = require('@graphql-tools/wrap'); var pluginCsrfPrevention = require('@graphql-yoga/plugin-csrf-prevention'); var pluginDeferStream = require('@graphql-yoga/plugin-defer-stream'); var pluginPersistedOperations = require('@graphql-yoga/plugin-persisted-operations'); var disposablestack = require('@whatwg-node/disposablestack'); var graphql = require('graphql'); var graphqlYoga = require('graphql-yoga'); var federation = require('@graphql-tools/federation'); var pluginApolloUsageReport = require('@graphql-yoga/plugin-apollo-usage-report'); var server = require('@whatwg-node/server'); var transportCommon = require('@graphql-mesh/transport-common'); var gatewayAbortSignalAny = require('@graphql-hive/gateway-abort-signal-any'); var core = require('@envelop/core'); var crossHelpers = require('@graphql-mesh/cross-helpers'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } var useMeshHive__default = /*#__PURE__*/_interopDefault(useMeshHive); var useMeshResponseCache__default = /*#__PURE__*/_interopDefault(useMeshResponseCache); function checkIfDataSatisfiesSelectionSet(selectionSet, data) { if (Array.isArray(data)) { return data.every( (item) => checkIfDataSatisfiesSelectionSet(selectionSet, item) ); } for (const selection of selectionSet.selections) { if (selection.kind === "Field") { const field = selection; const responseKey = field.alias?.value || field.name.value; if (data[responseKey] != null) { if (field.selectionSet) { if (!checkIfDataSatisfiesSelectionSet( field.selectionSet, data[field.name.value] )) { return false; } } } else { return false; } } else if (selection.kind === "InlineFragment") { const inlineFragment = selection; if (!checkIfDataSatisfiesSelectionSet(inlineFragment.selectionSet, data)) { return false; } } } return true; } const defaultQueryText = ( /* GraphQL */ ` # Welcome to GraphiQL # GraphiQL is an in-browser tool for writing, validating, # and testing GraphQL queries. # # Type queries into this side of the screen, and you will # see intelligent typeaheads aware of the current GraphQL # type schema and live syntax and validation errors # highlighted within the text. # # GraphQL queries typically start with a "{" character. # Lines that start with a # are ignored. # # An example GraphQL query might look like: # # { # field(arg: "value") { # subField # } # } # ` ); function delayInMs(ms, signal) { return new Promise((resolve, reject) => { globalThis.setTimeout(resolve, ms); }); } const defaultLoadedPlacePrefix = "GraphOS Managed Federation"; function createGraphOSFetcher({ graphosOpts, configContext }) { let lastSeenId; let lastSupergraphSdl; let nextFetchTime; const uplinksParam = graphosOpts.upLink || process.env["APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT"]; const uplinks = uplinksParam?.split(",").map((uplink) => uplink.trim()) || federation.DEFAULT_UPLINKS; const graphosLogger = configContext.logger.child("GraphOS"); graphosLogger.info("Using Managed Federation with uplinks: ", ...uplinks); const maxRetries = graphosOpts.maxRetries || Math.max(3, uplinks.length); let supergraphLoadedPlace = defaultLoadedPlacePrefix; if (graphosOpts.graphRef) { supergraphLoadedPlace += ` <br>${graphosOpts.graphRef}`; } return { supergraphLoadedPlace, unifiedGraphFetcher(transportContext) { const uplinksToUse = []; let retries = maxRetries; function fetchSupergraphWithDelay() { if (nextFetchTime) { const currentTime = Date.now(); if (nextFetchTime >= currentTime) { const delay = nextFetchTime - currentTime; graphosLogger.info(`Fetching supergraph with delay: ${delay}ms`); nextFetchTime = 0; return delayInMs(delay).then(fetchSupergraph); } } return fetchSupergraph(); } function fetchSupergraph() { if (uplinksToUse.length === 0) { uplinksToUse.push(...uplinks); } retries--; const uplinkToUse = uplinksToUse.pop(); const attemptLogger = graphosLogger.child( `Attempt ${maxRetries - retries} - UpLink: ${uplinkToUse}` ); attemptLogger.debug(`Fetching supergraph`); return federation.fetchSupergraphSdlFromManagedFederation({ graphRef: graphosOpts.graphRef, apiKey: graphosOpts.apiKey, upLink: uplinkToUse, lastSeenId, // @ts-expect-error TODO: Fetch types mismatch fetch: transportContext.fetch || configContext.fetch, loggerByMessageLevel: { ERROR(message) { attemptLogger.error(message); }, INFO(message) { attemptLogger.info(message); }, WARN(message) { attemptLogger.warn(message); } } }).then( (result) => { if (result.minDelaySeconds) { attemptLogger.debug( `Setting min delay to ${result.minDelaySeconds}s` ); nextFetchTime = Date.now() + result.minDelaySeconds * 1e3; } if ("error" in result) { attemptLogger.error(result.error.code, result.error.message); } if ("id" in result) { lastSeenId = result.id; } if ("supergraphSdl" in result) { attemptLogger.info( `Fetched the new supergraph ${lastSeenId ? `with id ${lastSeenId}` : ""}` ); lastSupergraphSdl = result.supergraphSdl; } if (!lastSupergraphSdl) { if (retries > 0) { return fetchSupergraphWithDelay(); } throw new Error("Failed to fetch supergraph SDL"); } return lastSupergraphSdl; }, (err) => { attemptLogger.error(err); if (retries > 0) { return fetchSupergraphWithDelay(); } return lastSupergraphSdl; } ); } return fetchSupergraphWithDelay(); } }; } function getProxyExecutor({ config, configContext, getSchema, onSubgraphExecuteHooks, transportExecutorStack }) { const fakeTransportEntryMap = {}; let subgraphName = "upstream"; const onSubgraphExecute = fusionRuntime.getOnSubgraphExecute({ onSubgraphExecuteHooks, transportEntryMap: new Proxy(fakeTransportEntryMap, { get(fakeTransportEntryMap2, subgraphNameProp) { if (!fakeTransportEntryMap2[subgraphNameProp]) { subgraphName = subgraphNameProp; fakeTransportEntryMap2[subgraphNameProp] = { kind: "http", subgraph: subgraphName.toString(), location: config.proxy?.endpoint, headers: config.proxy?.headers, options: config.proxy }; } return fakeTransportEntryMap2[subgraphNameProp]; } }), transportContext: configContext, getSubgraphSchema: getSchema, transportExecutorStack, transports: config.transports }); return function proxyExecutor(executionRequest) { return onSubgraphExecute(subgraphName, executionRequest); }; } function getReportingPlugin(config, configContext) { if (config.reporting?.type === "hive") { return { name: "Hive", plugin: useMeshHive__default.default({ ...configContext, logger: configContext.logger.child("Hive"), ...config.reporting, ...config.persistedDocuments && "type" in config.persistedDocuments && config.persistedDocuments?.type === "hive" ? { experimental__persistedDocuments: { cdn: { endpoint: config.persistedDocuments.endpoint, accessToken: config.persistedDocuments.token }, allowArbitraryDocuments: !!config.persistedDocuments.allowArbitraryDocuments } } : {} }) }; } else if (config.reporting?.type === "graphos" || !config.reporting && "supergraph" in config && typeof config.supergraph === "object" && "type" in config.supergraph && config.supergraph.type === "graphos") { if ("supergraph" in config && typeof config.supergraph === "object" && "type" in config.supergraph && config.supergraph.type === "graphos") { if (!config.reporting) { config.reporting = { type: "graphos", apiKey: config.supergraph.apiKey, graphRef: config.supergraph.graphRef }; } else { config.reporting.apiKey ||= config.supergraph.apiKey; config.reporting.graphRef ||= config.supergraph.graphRef; } } return { name: "GraphOS", // @ts-expect-error - TODO: Fix types plugin: pluginApolloUsageReport.useApolloUsageReport(config.reporting) }; } return { plugin: {} }; } function handleUnifiedGraphConfig(config, configContext) { return utils$1.mapMaybePromise( typeof config === "function" ? config(configContext) : config, (schema) => handleUnifiedGraphSchema(schema, configContext) ); } function handleUnifiedGraphSchema(unifiedGraphSchema, configContext) { if (typeof unifiedGraphSchema === "string" && (utils$1.isValidPath(unifiedGraphSchema) || utils.isUrl(unifiedGraphSchema))) { return utils.readFileOrUrl(unifiedGraphSchema, { fetch: configContext.fetch, cwd: configContext.cwd, logger: configContext.logger, allowUnknownExtensions: true, importFn: utils.defaultImportFn }); } return unifiedGraphSchema; } var landingPageHtml = "<!doctype html><html lang=en><head><meta charset=utf-8><title>Welcome to __PRODUCT_NAME__</title><link rel=icon href=https://the-guild.dev/favicon.ico><style>body,html{padding:0;margin:0;height:100%;font-family:Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Oxygen,Ubuntu,Cantarell,'Fira Sans','Droid Sans','Helvetica Neue',sans-serif;color:#fff;background-color:#000}main>section.hero{display:flex;height:50vh;justify-content:center;align-items:center;flex-direction:column}.logo{display:flex;align-items:center}.buttons{margin-top:24px}h1{font-size:80px}h2{color:#888;max-width:50%;margin-top:0;text-align:center}a{color:#fff;text-decoration:none;margin-left:10px;margin-right:10px;font-weight:700;transition:color .3s ease;padding:4px;overflow:visible}a.graphiql:hover{color:rgba(255,0,255,.7)}a.docs:hover{color:rgba(28,200,238,.7)}a.tutorial:hover{color:rgba(125,85,245,.7)}svg{margin-right:24px}.supergraph-information>*{margin-left:auto;margin-right:auto;text-align:center;max-width:50%}.not-what-your-looking-for{margin-top:5vh}.not-what-your-looking-for>*{margin-left:auto;margin-right:auto}.not-what-your-looking-for>p{text-align:center}.not-what-your-looking-for>h2{color:#464646}.not-what-your-looking-for>p{max-width:600px;line-height:1.3em}.not-what-your-looking-for>pre{max-width:300px}</style></head><body id=body><main><section class=hero><div class=logo><div>__PRODUCT_LOGO__</div><h1>__PRODUCT_NAME__</h1></div><h2>__PRODUCT_DESCRIPTION__</h2><div class=buttons><a href=__PRODUCT_LINK__ class=docs>Read the Docs</a> <a href=__GRAPHIQL_LINK__ class=graphiql>Visit GraphiQL</a></div></section>__SUBGRAPH_HTML__<section class=not-what-your-looking-for><h2>Not the page you are looking for? \u{1F440}</h2><p>This page is shown be default whenever a 404 is hit.<br>You can disable this by behavior via the <code>landingPage</code> option.</p><pre>\n <code>\n// gateway.config.ts\n\nimport { defineConfig } from '__PRODUCT_PACKAGE_NAME__';\n\nexport const gatewayConfig = defineConfig({\n landingPage: false,\n});\n </code>\n </pre><p>If you expected this page to be the GraphQL route, you need to configure Hive Gateway. Currently, the GraphQL route is configured to be on <code>__GRAPHIQL_LINK__</code>.</p><pre>\n <code>\n// gateway.config.ts\n\nimport { defineConfig } from '__PRODUCT_PACKAGE_NAME__';\n\nexport const gatewayConfig = defineConfig({\n graphqlEndpoint: '__REQUEST_PATH__',\n});\n </code>\n </pre></section></main></body></html>"; function useContentEncoding({ subgraphs } = {}) { if (!subgraphs?.length) { return server.useContentEncoding(); } const compressionAlgorithm = "gzip"; let fetchAPI; const execReqWithContentEncoding = /* @__PURE__ */ new WeakSet(); return { onYogaInit({ yoga }) { fetchAPI = yoga.fetchAPI; }, onPluginInit({ addPlugin }) { addPlugin(server.useContentEncoding()); }, onSubgraphExecute({ subgraphName, executionRequest }) { if (subgraphs.includes(subgraphName) || subgraphs.includes("*")) { execReqWithContentEncoding.add(executionRequest); } }, onFetch({ executionRequest, options, setOptions }) { if (options.body && !options.headers?.["Content-Encoding"] && executionRequest && execReqWithContentEncoding.has(executionRequest) && fetchAPI.CompressionStream) { const compressionStream = new fetchAPI.CompressionStream( compressionAlgorithm ); let bodyStream; if (options.body instanceof fetchAPI.ReadableStream) { bodyStream = options.body; } else { bodyStream = new fetchAPI.Response(options.body).body; } setOptions({ ...options, headers: { "Accept-Encoding": "gzip, deflate", ...options.headers, "Content-Encoding": compressionAlgorithm }, body: bodyStream.pipeThrough(compressionStream) }); } } }; } function useCustomAgent(agentFactory) { return { onFetch(payload) { const agent = agentFactory(payload); if (agent != null) { payload.setOptions({ ...payload.options, // @ts-expect-error - `agent` is there agent }); } } }; } function useDelegationPlanDebug(opts) { let fetchAPI; const stageExecuteLogById = /* @__PURE__ */ new WeakMap(); return { onYogaInit({ yoga }) { fetchAPI = yoga.fetchAPI; }, onDelegationPlan({ subgraph, typeName, variables, fragments, fieldNodes, info, logger = opts.logger }) { logger = logger.child("delegation-plan"); const planId = fetchAPI.crypto.randomUUID(); logger.debug("start", () => { const logObj = { planId, subgraph, typeName }; if (variables && Object.keys(variables).length) { logObj["variables"] = JSON.stringify(variables); } if (fragments && Object.keys(fragments).length) { logObj["fragments"] = Object.fromEntries( Object.entries(fragments).map(([name, fragment]) => [ name, graphql.print(fragment) ]) ); } if (fieldNodes && fieldNodes.length) { logObj["fieldNodes"] = fieldNodes.map( (fieldNode) => graphql.print(fieldNode) ); } if (info?.path) { logObj["path"] = utils$1.pathToArray(info.path).join(" | "); } return logObj; }); const start = performance.now(); return ({ delegationPlan }) => { logger.debug("done", () => ({ planId, plan: delegationPlan.map((plan) => { const planObj = {}; for (const [subschema, selectionSet] of plan) { if (subschema.name) { planObj[subschema.name] = graphql.print(selectionSet); } } return planObj; }), duration: performance.now() - start })); }; }, onDelegationStageExecute({ object, info, context, subgraph, selectionSet, key, typeName, logger = opts.logger }) { logger = logger.child("delegation-stage-execute"); let stageId; let contextLog = stageExecuteLogById.get(context); if (!contextLog) { contextLog = /* @__PURE__ */ new Set(); stageExecuteLogById.set(context, contextLog); } const log = { subgraph, typeName, key: JSON.stringify(key), object: JSON.stringify(object), selectionSet: graphql.print(selectionSet) }; const logStr = JSON.stringify(log); if (contextLog.has(logStr)) { return; } contextLog.add(logStr); stageId = fetchAPI.crypto.randomUUID(); logger.debug("start", () => { return { stageId, ...log, path: utils$1.pathToArray(info.path).join(" | ") }; }); const start = performance.now(); return ({ result }) => { logger.debug("result", () => ({ stageId, result: JSON.stringify(result, null), duration: performance.now() - start })); }; } }; } function useFetchDebug(opts) { let fetchAPI; return { onYogaInit({ yoga }) { fetchAPI = yoga.fetchAPI; }, onFetch({ url, options, logger = opts.logger }) { logger = logger.child("fetch"); const fetchId = fetchAPI.crypto.randomUUID(); logger.debug("request", () => ({ fetchId, url, ...options || {}, body: options?.body && JSON.stringify(options.body), headers: options?.headers && JSON.stringify(options.headers, null, 2) })); const start = performance.now(); return function onFetchDone({ response }) { logger.debug("response", () => ({ fetchId, status: response.status, headers: JSON.stringify( Object.fromEntries(response.headers.entries()) ), duration: performance.now() - start })); }; } }; } function usePropagateHeaders(opts) { const resHeadersByRequest = /* @__PURE__ */ new WeakMap(); return { onFetch({ executionRequest, context, options, setOptions }) { const request = context?.request || executionRequest?.context?.request; if (request) { const subgraphName = executionRequest && fusionRuntime.subgraphNameByExecutionRequest.get(executionRequest); let job; if (opts.fromClientToSubgraphs) { job = utils$1.mapMaybePromise( opts.fromClientToSubgraphs({ request, subgraphName }), (headers) => setOptions({ ...options, // @ts-expect-error TODO: headers can contain null and undefined values. the types are incorrect headers: { ...headers, ...options.headers } }) ); } return utils$1.mapMaybePromise(job, () => { if (opts.fromSubgraphsToClient) { return function onFetchDone({ response }) { return utils$1.mapMaybePromise( opts.fromSubgraphsToClient?.({ response, subgraphName }), (headers) => { if (headers && request) { let existingHeaders = resHeadersByRequest.get(request); if (!existingHeaders) { existingHeaders = {}; resHeadersByRequest.set(request, existingHeaders); } for (const key in headers) { const value = headers[key]; if (value) { const headerAsArray = Array.isArray(value) ? value : [value]; if (existingHeaders[key]) { existingHeaders[key].push(...headerAsArray); } else { existingHeaders[key] = headerAsArray; } } } } } ); }; } }); } }, onResponse({ response, request }) { const headers = resHeadersByRequest.get(request); if (headers) { for (const key in headers) { const value = headers[key]; if (value) { for (const v of value) { response.headers.append(key, v); } } } } } }; } function useRequestId() { return { onRequest({ request, fetchAPI }) { const requestId = request.headers.get("x-request-id") || fetchAPI.crypto.randomUUID(); utils.requestIdByRequest.set(request, requestId); }, onContextBuilding({ context }) { if (context?.request) { const requestId = utils.requestIdByRequest.get(context.request); if (requestId && context.logger) { context.logger = context.logger.child(requestId); } } }, onFetch({ context, options, setOptions }) { if (context?.request) { const requestId = utils.requestIdByRequest.get(context.request); if (requestId) { setOptions({ ...options || {}, headers: { ...options.headers || {}, "x-request-id": requestId } }); } } }, onResponse({ request, response }) { const requestId = utils.requestIdByRequest.get(request); if (requestId) { response.headers.set("x-request-id", requestId); } } }; } function useSubgraphExecuteDebug(opts) { let fetchAPI; return { onYogaInit({ yoga }) { fetchAPI = yoga.fetchAPI; }, onSubgraphExecute({ executionRequest, logger = opts.logger }) { const subgraphExecuteId = fetchAPI.crypto.randomUUID(); logger = logger.child("subgraph-execute"); if (executionRequest) { logger.debug("start", () => ({ subgraphExecuteId, query: executionRequest.document && transportCommon.defaultPrintFn(executionRequest.document), variables: executionRequest.variables && JSON.stringify(executionRequest.variables) })); } const start = performance.now(); return function onSubgraphExecuteDone({ result }) { if (graphqlYoga.isAsyncIterable(result)) { return { onNext({ result: result2 }) { logger.debug("next", () => ({ subgraphExecuteId, result: JSON.stringify(result2) })); }, onEnd() { logger.debug("end", () => ({ subgraphExecuteId, duration: performance.now() - start })); } }; } logger.debug("result", () => ({ subgraphExecuteId, result: JSON.stringify(result) })); return undefined; }; } }; } function useUpstreamCancel() { return { onFetch({ context, options, executionRequest, info }) { const signals = []; if (context?.request?.signal) { signals.push(context.request.signal); } const execRequestSignal = executionRequest?.signal || executionRequest?.info?.signal; if (execRequestSignal) { signals.push(execRequestSignal); } const signalInInfo = info?.signal; if (signalInInfo) { signals.push(signalInInfo); } if (gatewayAbortSignalAny.isAbortSignalFromAny(options.signal)) { options.signal.addSignals(signals); } else { if (options.signal) { signals.push(options.signal); } options.signal = gatewayAbortSignalAny.abortSignalAny(signals); } }, onSubgraphExecute({ executionRequest }) { const signals = []; if (executionRequest.info?.signal) { signals.push(executionRequest.info.signal); } if (executionRequest.context?.request?.signal) { signals.push(executionRequest.context.request.signal); } if (gatewayAbortSignalAny.isAbortSignalFromAny(executionRequest.signal)) { executionRequest.signal.addSignals(signals); } else { if (executionRequest.signal) { signals.push(executionRequest.signal); } executionRequest.signal = gatewayAbortSignalAny.abortSignalAny(signals); } } }; } function useUpstreamRetry(opts) { const timeouts = /* @__PURE__ */ new Set(); const retryOptions = typeof opts === "function" ? opts : () => opts; const executionRequestResponseMap = /* @__PURE__ */ new WeakMap(); return { onSubgraphExecute({ subgraphName, executionRequest, executor, setExecutor }) { const optsForReq = retryOptions({ subgraphName, executionRequest }); if (optsForReq) { const { maxRetries, retryDelay = 1e3, retryDelayFactor = 1.25, shouldRetry = ({ response, executionResult }) => { if (response) { if (response.status >= 500 || response.status === 429 || response.headers.get("Retry-After")) { return true; } } if (!executionResult || !utils$1.isAsyncIterable(executionResult) && executionResult.errors?.length && executionResult.errors.some((e) => !core.isOriginalGraphQLError(e))) { return true; } return false; } } = optsForReq; if (maxRetries > 0) { setExecutor(function(executionRequest2) { let attemptsLeft = maxRetries + 1; let executionResult; let currRetryDelay = retryDelay; function retry() { try { if (attemptsLeft <= 0) { return executionResult; } const requestTime = Date.now(); attemptsLeft--; return utils$1.mapMaybePromise( executor(executionRequest2), (currRes) => { executionResult = currRes; let retryAfterSecondsFromHeader; const response = executionRequestResponseMap.get(executionRequest2); executionRequestResponseMap.delete(executionRequest2); const retryAfterHeader = response?.headers.get("Retry-After"); if (retryAfterHeader) { retryAfterSecondsFromHeader = parseInt(retryAfterHeader) * 1e3; if (isNaN(retryAfterSecondsFromHeader)) { const dateTime = new Date(retryAfterHeader).getTime(); if (!isNaN(dateTime)) { retryAfterSecondsFromHeader = dateTime - requestTime; } } } currRetryDelay = retryAfterSecondsFromHeader || currRetryDelay * retryDelayFactor; if (shouldRetry({ executionRequest: executionRequest2, executionResult, response })) { return new Promise((resolve) => { const timeout = setTimeout(() => { timeouts.delete(timeout); resolve(retry()); }, currRetryDelay); timeouts.add(timeout); }); } return executionResult; }, (e) => { if (attemptsLeft <= 0) { throw e; } return retry(); } ); } catch (e) { if (attemptsLeft <= 0) { throw e; } return retry(); } } return retry(); }); } } }, onFetch({ executionRequest }) { if (executionRequest) { return function onFetchDone({ response }) { executionRequestResponseMap.set(executionRequest, response); }; } return undefined; }, [disposablestack.DisposableSymbols.dispose]() { for (const timeout of timeouts) { clearTimeout(timeout); timeouts.delete(timeout); } } }; } function useUpstreamTimeout(opts) { const timeoutFactory = typeof opts === "function" ? opts : () => opts; const timeoutSignalsByExecutionRequest = /* @__PURE__ */ new WeakMap(); const errorExtensionsByExecRequest = /* @__PURE__ */ new WeakMap(); return { onSubgraphExecute({ subgraphName, executionRequest, executor, setExecutor }) { const timeout = timeoutFactory({ subgraphName, executionRequest }); if (timeout) { setExecutor(function timeoutExecutor(executionRequest2) { let timeoutSignal = timeoutSignalsByExecutionRequest.get(executionRequest2); if (!timeoutSignal) { timeoutSignal = AbortSignal.timeout(timeout); } timeoutSignalsByExecutionRequest.set(executionRequest2, timeoutSignal); const timeout$ = utils$1.getAbortPromise(timeoutSignal); let finalSignal = timeoutSignal; const signals = /* @__PURE__ */ new Set(); signals.add(timeoutSignal); if (executionRequest2.signal) { signals.add(executionRequest2.signal); finalSignal = gatewayAbortSignalAny.abortSignalAny(signals); } return Promise.race([ timeout$, executor({ ...executionRequest2, signal: finalSignal }) ]).then((result) => { if (utils$1.isAsyncIterable(result)) { return { [Symbol.asyncIterator]() { const iterator = result[Symbol.asyncIterator](); if (iterator.return) { utils$1.registerAbortSignalListener( timeoutSignal, () => iterator.return?.(timeoutSignal.reason) ); } return iterator; } }; } return result; }).catch((e) => { if (e === timeoutSignal.reason) { const upstreamErrorExtensions = errorExtensionsByExecRequest.get(executionRequest2); throw utils$1.createGraphQLError(e.message, { originalError: e, extensions: upstreamErrorExtensions }); } throw e; }).finally(() => { errorExtensionsByExecRequest.delete(executionRequest2); timeoutSignalsByExecutionRequest.delete(executionRequest2); }); }); } return undefined; }, onFetch({ url, executionRequest, options, setOptions }) { const subgraphName = executionRequest && fusionRuntime.subgraphNameByExecutionRequest.get(executionRequest); if (!executionRequest || !timeoutSignalsByExecutionRequest.has(executionRequest)) { const timeout = timeoutFactory({ subgraphName, executionRequest }); if (timeout) { let timeoutSignal; if (executionRequest) { timeoutSignal = timeoutSignalsByExecutionRequest.get(executionRequest); if (!timeoutSignal) { timeoutSignal = AbortSignal.timeout(timeout); timeoutSignalsByExecutionRequest.set( executionRequest, timeoutSignal ); } } else { timeoutSignal = AbortSignal.timeout(timeout); } const signals = /* @__PURE__ */ new Set(); signals.add(timeoutSignal); if (options.signal) { signals.add(options.signal); setOptions({ ...options, signal: gatewayAbortSignalAny.abortSignalAny(signals) }); } } } if (executionRequest) { const upstreamErrorExtensions = { subgraph: subgraphName, request: { url, method: options.method, body: options.body } }; errorExtensionsByExecRequest.set( executionRequest, upstreamErrorExtensions ); return function onFetchDone({ response }) { timeoutSignalsByExecutionRequest.delete(executionRequest); upstreamErrorExtensions.response = { status: response.status, statusText: response.statusText, headers: utils.getHeadersObj(response.headers) }; }; } return undefined; } }; } function useWebhooks({ pubsub, logger }) { if (!pubsub) { throw new Error(`You must provide a pubsub instance to webhooks feature! Example: export const gatewayConfig = defineConfig({ pubsub: new PubSub(), webhooks: true, }) See documentation: https://the-guild.dev/docs/mesh/pubsub`); } return { onRequest({ request, url, endResponse, fetchAPI }) { for (const eventName of pubsub.getEventNames()) { if (eventName === `webhook:${request.method.toLowerCase()}:${url.pathname}`) { logger?.debug(`Received webhook request for ${url.pathname}`); return request.text().then((body) => { logger?.debug(`Emitted webhook request for ${url.pathname}`, body); pubsub.publish( eventName, request.headers.get("content-type") === "application/json" ? JSON.parse(body) : body ); endResponse( new fetchAPI.Response(null, { status: 204, statusText: "OK" }) ); }); } } } }; } const defaultProductLogo = ( /* HTML */ ` <svg height="150" viewBox="0 0 57 61" fill="none" xmlns="http://www.w3.org/2000/svg" > <path fill-rule="evenodd" clip-rule="evenodd" d="M0 16.1C0 17.7999 0.900024 19.2999 2.30005 20.1C2.45459 20.203 2.63562 20.2794 2.81592 20.343C2.9856 20.403 3.15454 20.4515 3.30005 20.5H3.40002C3.45203 20.552 3.53125 20.577 3.60925 20.589C3.68115 20.6 3.75208 20.6 3.80005 20.6H3.90002H4.5H5H5.1001C5.15015 20.6 5.2251 20.575 5.30005 20.55C5.375 20.525 5.44995 20.5 5.5 20.5H5.6001C5.65015 20.5 5.7251 20.475 5.80005 20.45C5.875 20.425 5.94995 20.4 6 20.4C7.69995 19.7001 9 18.1 9 16.1C9 15.8784 8.94543 15.7114 8.91699 15.5186C8.90686 15.4502 8.90002 15.3785 8.90002 15.3L27.7 4.5C28 4.40002 28.3 4.30005 28.6001 4.30005C28.9 4.30005 29.2 4.30005 29.5 4.5L45.4 13.7C46 12.2999 46.9 11.1001 48.1001 10.3L31.7 0.800049C30.8 0.300049 29.8 0 28.7 0C27.7 0 26.6 0.300049 25.7 0.800049L6.40002 11.9C5.90002 11.6 5.30005 11.5 4.6001 11.5C2.1001 11.5 0 13.6 0 16.1ZM55.1001 19.5C56.1001 18.7 56.7 17.5 56.7 16.1C56.7 13.6 54.6 11.5 52 11.5C51.5 11.5 51 11.6 50.6001 11.7C50.4 11.7999 50.3 11.8 50.1001 11.9C50.1001 12 50 12 50 12C49.9236 12.0382 49.8618 12.0618 49.8091 12.0819C49.7236 12.1146 49.6619 12.1382 49.6001 12.2C49.6001 12.2999 49.5 12.3 49.5 12.3C49.2 12.5 49 12.7 48.7 13C48.6499 13.05 48.6 13.125 48.55 13.2C48.5 13.275 48.45 13.3501 48.4 13.4C47.8 14.2001 47.5 15.1 47.5 16.1C47.5 18.2 48.9 19.9 50.8 20.5V41.2C50.8 41.8999 50.5 42.5 49.9 42.8L34.3 51.8C35.2 52.9 35.7 54.4 35.7 55.9L52.1001 46.5C53.9 45.4 55.1001 43.3999 55.1001 41.2V19.5ZM33.295 56.3519C33.3 56.261 33.3 56.1737 33.3 56.1C33.3 55.9 33.3 55.8 33.4 55.8V55.6V55.3V55.1C33.3425 55.0425 33.3181 55.0181 33.3077 54.9886C33.3 54.9669 33.3 54.9425 33.3 54.9C33.3 54.8 33.2 54.7999 33.2 54.7C33.1 54.6 33.1001 54.6 33.1001 54.5C33.1001 54.5 33.0583 54.4583 33.0288 54.4019C33.0126 54.3708 33 54.3354 33 54.3C32.9 54.3 32.9 54.2 32.9 54.2C32.9 54.2 32.8 54.1 32.8 54C32.7 54 32.7 53.9 32.7 53.9C32.6499 53.8501 32.625 53.8 32.6 53.75C32.575 53.7 32.55 53.6499 32.5 53.6V53.5C31.6 52.3 30.3 51.6 28.8 51.6C27.4 51.6 26.1 52.2 25.3 53.2L7.5 42.9C7 42.6 6.6001 42 6.6001 41.3V22.9C6 23.1 5.30005 23.2 4.6001 23.2C3.90002 23.2 3.09998 23 2.40002 22.8V41.3C2.40002 43.5 3.5 45.5 5.40002 46.6L24.4 57.6C25 59.4 26.7 60.7 28.7 60.7C31 60.7 32.8 59 33.2 56.9V56.8C33.2632 56.6738 33.2865 56.5076 33.295 56.3519ZM43.8319 24.9475L41.345 29.2535L43.8319 33.5596C44.056 33.9475 44.056 34.4257 43.8319 34.8137L40.9828 39.7468C40.7588 40.1349 40.3446 40.3739 39.8964 40.3739H34.9225L32.4355 44.6801C32.2114 45.068 31.7972 45.3071 31.349 45.3071H25.651C25.2028 45.3071 24.7886 45.068 24.5645 44.6801L22.0775 40.374H17.1036C16.6554 40.374 16.2412 40.135 16.0172 39.7469L13.1681 34.8137C12.944 34.4257 12.944 33.9476 13.1681 33.5596L15.655 29.2535L13.1681 24.9475C12.944 24.5594 12.944 24.0814 13.1681 23.6934L16.0172 18.7603C16.2412 18.3722 16.6554 18.1331 17.1036 18.1331H22.0775L24.5645 13.827C24.7886 13.439 25.2028 13.2 25.651 13.2H31.349C31.7972 13.2 32.2114 13.439 32.4355 13.827L34.9225 18.1331H39.8964C40.3446 18.1331 40.7588 18.3722 40.9828 18.7603L43.8319 23.6932C44.056 24.0813 44.056 24.5594 43.8319 24.9475ZM17.828 37.8656H22.0775L24.2023 34.1866L22.0775 30.5077H17.828L15.7032 34.1866L17.828 37.8656ZM17.828 27.9994H22.0775L24.2023 24.3203L22.0775 20.6414H17.828L15.7032 24.3203L17.828 27.9994ZM26.3754 42.7987H30.6248L32.7495 39.1199L30.6248 35.4409H26.3754L24.2505 39.1199L26.3754 42.7987ZM26.3752 25.5746L24.2505 29.2535L26.3752 32.9325H30.6248L32.7495 29.2535L30.6248 25.5746H26.3752ZM26.3754 23.0662H30.6248L32.7495 19.3872L30.6246 15.7084H26.3752L24.2505 19.3872L26.3754 23.0662ZM34.9225 37.8656H39.172L41.2968 34.1866L39.172 30.5077H34.9225L32.7977 34.1866L34.9225 37.8656ZM34.9225 27.9994H39.172L41.2968 24.3204L39.172 20.6415H34.9225L32.7977 24.3204L34.9225 27.9994Z" fill="#eab308" /> </svg> ` ); function createGatewayRuntime(config) { let fetchAPI = config.fetchAPI; let logger; if (config.logging == null) { logger = new utils.DefaultLogger(); } else if (typeof config.logging === "boolean") { logger = config.logging ? new utils.DefaultLogger() : new utils.DefaultLogger("", utils.LogLevel.silent); } else if (typeof config.logging === "number") { logger = new utils.DefaultLogger(undefined, config.logging); } else { logger = config.logging; } const onFetchHooks = []; const wrappedFetchFn = utils.wrapFetchWithHooks(onFetchHooks); const configContext = { fetch: wrappedFetchFn, logger, cwd: config.cwd || (typeof process !== "undefined" ? process.cwd() : ""), cache: config.cache, pubsub: config.pubsub }; let unifiedGraphPlugin; const readinessCheckEndpoint = config.readinessCheckEndpoint || "/readiness"; const onSubgraphExecuteHooks = []; const onDelegateHooks = []; const onDelegationPlanHooks = []; const onDelegationStageExecuteHooks = []; let unifiedGraph; let schemaInvalidator; let getSchema = () => unifiedGraph; let contextBuilder; let readinessChecker; const { name: reportingTarget, plugin: registryPlugin } = getReportingPlugin( config, configContext ); let persistedDocumentsPlugin = {}; if (config.reporting?.type !== "hive" && config.persistedDocuments && "type" in config.persistedDocuments && config.persistedDocuments?.type === "hive") { persistedDocumentsPlugin = useMeshHive__default.default({ ...configContext, logger: configContext.logger.child("Hive"), experimental__persistedDocuments: { cdn: { endpoint: config.persistedDocuments.endpoint, accessToken: config.persistedDocuments.token }, allowArbitraryDocuments: !!config.persistedDocuments.allowArbitraryDocuments } }); } else if (config.persistedDocuments && "getPersistedOperation" in config.persistedDocuments) { persistedDocumentsPlugin = pluginPersistedOperations.usePersistedOperations({ ...configContext, ...config.persistedDocuments }); } let subgraphInformationHTMLRenderer = () => ""; if ("proxy" in config) { let createExecuteFnFromExecutor2 = function(executor) { return function executeFn2(args) { return executor({ rootValue: args.rootValue, document: args.document, ...args.operationName ? { operationName: args.operationName } : {}, ...args.variableValues ? { variables: args.variableValues } : {}, ...args.contextValue ? { context: args.contextValue } : {} }); }; }, continuePolling2 = function() { if (currentTimeout) { clearTimeout(currentTimeout); } if (pollingInterval) { currentTimeout = setTimeout(schemaFetcher, pollingInterval); } }, pausePolling2 = function() { if (currentTimeout) { clearTimeout(currentTimeout); } }; const transportExecutorStack = new disposablestack.AsyncDisposableStack(); const proxyExecutor = getProxyExecutor({ config, configContext, getSchema() { return unifiedGraph; }, onSubgraphExecuteHooks, transportExecutorStack }); const executeFn = createExecuteFnFromExecutor2(proxyExecutor); let currentTimeout; const pollingInterval = config.pollingInterval; let initialFetch$; let schemaFetcher; if (config.schema && typeof config.schema === "object" && "type" in config.schema) { const { endpoint, key } = config.schema; const fetcher = core$1.createSchemaFetcher({ endpoint, key, logger: configContext.logger.child("Hive CDN") }); schemaFetcher = function fetchSchemaFromCDN() { pausePolling2(); initialFetch$ = utils$1.mapMaybePromise(fetcher(), ({ sdl }) => { { unifiedGraph = graphql.buildSchema(sdl, { assumeValid: true, assumeValidSDL: true }); } continuePolling2(); return true; }); return initialFetch$; }; } else if (config.schema) { if (!isDynamicUnifiedGraphSchema(config.schema)) { delete config.pollingInterval; } schemaFetcher = function fetchSchema() { pausePolling2(); initialFetch$ = utils$1.mapMaybePromise( handleUnifiedGraphConfig( // @ts-expect-error TODO: what's up with type narrowing config.schema, configContext ), (schema) => { if (graphql.isSchema(schema)) { unifiedGraph = schema; } else if (utils$1.isDocumentNode(schema)) { unifiedGraph = graphql.buildASTSchema(schema, { assumeValid: true, assumeValidSDL: true }); } else { unifiedGraph = graphql.buildSchema(schema, { noLocation: true, assumeValid: true, assumeValidSDL: true }); } continuePolling2(); return true; } ); return initialFetch$; }; } else { schemaFetcher = function fetchSchemaWithExecutor() { pausePolling2(); return utils$1.mapMaybePromise( wrap.schemaFromExecutor(proxyExecutor, configContext, { assumeValid: true }), (schema) => { unifiedGraph = schema; continuePolling2(); return true; }, (err) => { configContext.logger.warn(`Failed to introspect schema`, err); return true; } ); }; } getSchema = () => { if (unifiedGraph != null) { return unifiedGraph; } if (initialFetch$ != null) { return utils$1.mapMaybePromise(initialFetch$, () => unifiedGraph); } return utils$1.mapMaybePromise(schemaFetcher(), () => unifiedGraph); }; const shouldSkipValidation = "skipValidation" in config ? config.skipValidation : false; const executorPlugin = { onExecute({ setExecuteFn }) { setExecuteFn(executeFn); }, onSubscribe({ setSubscribeFn }) { setSubscribeFn(executeFn); }, onValidate({ params, setResult }) { if (shouldSkipValidation || !params.schema) { setResult([]); } }, [disposablestack.DisposableSymbols.asyncDispose]() { pausePolling2(); return transportExecutorStack.disposeAsync(); } }; unifiedGraphPlugin = executorPlugin; readinessChecker = () => { const res$ = proxyExecutor({ document: graphql.parse(`query ReadinessCheck { __typename }`) }); return utils$1.mapMaybePromise( res$, (res) => !graphqlYoga.isAsyncIterable(res) && !!res.data?.__typename ); }; schemaInvalidator = () => { unifiedGraph = undefined; initialFetch$ = schemaFetcher(); }; subgraphInformationHTMLRenderer = () => { const endpoint = config.proxy.endpoint; const htmlParts = []; htmlParts.push(`<section class="supergraph-information">`); htmlParts.push(`<h3>Proxy: <a href="${endpoint}">${endpoint}</a></h3>`); if (config.schema) { if (typeof config.schema === "object" && "type" in config.schema) { htmlParts.push( `<p><strong>Source: </strong> <i>${config.schema.type === "hive" ? "Hive" : "Unknown"} CDN</i></p>` ); } else if (utils$1.isValidPath(config.schema) || utils.isUrl(String(config.schema))) { htmlParts.push( `<p><strong>Source: </strong> <i>${config.schema}</i></p>` ); } else { htmlParts.push( `<p><strong>Source: </strong> <i>GraphQL schema in config</i></p>` ); } } if (reportingTarget) { htmlParts.push( `<p><strong>Usage Reporting: </strong> <i>${reportingTarget}</i></p>` ); } htmlParts.push(`</section>`); return htmlParts.join(""); }; } else if ("subgraph" in config) { let getSubschemaConfig2 = function() { if (getSubschemaConfig$ == null) { getSubschemaConfig$ = utils$1.mapMaybePromise( handleUnifiedGraphConfig(subgraphInConfig, configContext), (newUnifiedGraph) => { if (graphql.isSchema(newUnifiedGraph)) { unifiedGraph = newUnifiedGraph; } else if (utils$1.isDocumentNode(newUnifiedGraph)) { unifiedGraph = graphql.buildASTSchema(newUnifiedGraph, { assumeValid: true, assumeValidSDL: true }); } else { unifiedGraph = graphql.buildSchema(newUnifiedGraph, { noLocation: true, assumeValid: true, assumeValidSDL: true }); } unifiedGraph = fusionRuntime.restoreExtraDirectives(unifiedGraph); subschemaConfig = { name: utils$1.getDirectiveExtensions(unifiedGraph)?.["transport"]?.[0]?.["subgraph"], schema: unifiedGraph }; const transportEntryMap = {}; const additionalTypeDefs = []; const stitchingDirectivesTransformer = fusionRuntime.getStitchingDirectivesTransformerForSubschema(); const onSubgraphExecute = fusionRuntime.getOnSubgraphExecute({ onSubgraphExecuteHooks, ...config.transports ? { transports: config.transports } : {}, transportContext: configContext, transportEntryMap, getSubgraphSchema() { return unifiedGraph; }, transportExecutorStack }); subschemaConfig = fusionRuntime.handleFederationSubschema({ subschemaConfig, transportEntryMap, additionalTypeDefs, stitchingDirectivesTransformer, onSubgraphExecute }); unifiedGraph = wrap.wrapSchema(subschemaConfig); const entities = Object.keys(subschemaConfig.merge || {}); let entitiesDef = "union _Entity"; if (entities.length) { entitiesDef += ` = ${entities.join(" | ")}`; } const additionalResolvers = utils$1.asArray( "additionalResolvers" in config ? config.additionalResolvers : [] ).filter((r) => r != null); const finalTypeDefs = fusionRuntime.handleResolveToDirectives( graphql.parse( /* GraphQL */ ` type Query { _entities(representations: [_Any!]!): [_Entity]! _service: _Service! } scalar _Any ${entitiesDef} type _Service { sdl: String } ` ), additi