{"version":3,"file":"adapter-gateway-apollo-CxV_hMTv.mjs","sources":["../src/graphql/gateway/adapter-gateway-apollo.ts"],"sourcesContent":["import type {\n  GetDataSourceFunction,\n  GraphQLDataSourceProcessOptions,\n  ServiceDefinition,\n  SubgraphHealthCheckFunction,\n  SupergraphSdlUpdateFunction,\n} from \"@apollo/gateway\";\nimport {\n  ApolloGateway,\n  LocalCompose,\n  RemoteGraphQLDataSource,\n} from \"@apollo/gateway\";\nimport { ApolloServer, HeaderMap } from \"@apollo/server\";\nimport { ApolloServerPluginInlineTraceDisabled } from \"@apollo/server/plugin/disabled\";\nimport { ApolloServerPluginDrainHttpServer } from \"@apollo/server/plugin/drainHttpServer\";\nimport { ApolloServerPluginLandingPageLocalDefault } from \"@apollo/server/plugin/landingPage/default\";\nimport { buildSubgraphSchema } from \"@apollo/subgraph\";\nimport type { ILogger } from \"document-model\";\nimport type { GraphQLSchema } from \"graphql\";\nimport type http from \"node:http\";\nimport type { WebSocketServer } from \"ws\";\nimport type { Context } from \"../types.js\";\nimport { useServer } from \"../websocket.js\";\nimport type {\n  FetchHandler,\n  GatewayContextFactory,\n  IGatewayAdapter,\n  SubgraphDefinition,\n  WsContextFactory,\n  WsDisposer,\n} from \"./types.js\";\n\n/**\n * Drop subgraphs whose typeDefs cannot be built into a valid subgraph schema in\n * isolation — e.g. a duplicate type definition within a single subgraph (Sentry\n * #917) — so one broken subgraph is excluded instead of failing the build for\n * everyone. Each excluded subgraph is logged; the rest are composed.\n *\n * Scope: this only catches per-subgraph build failures. Errors that surface only\n * when subgraphs are composed together (cross-subgraph type or `@key` conflicts)\n * are still thrown by `LocalCompose.initialize()` and are NOT handled here.\n */\nexport function filterComposableSubgraphs(\n  serviceList: ServiceDefinition[],\n  logger?: Pick<ILogger, \"error\">,\n): ServiceDefinition[] {\n  return serviceList.filter((service) => {\n    try {\n      buildSubgraphSchema({ typeDefs: service.typeDefs });\n      return true;\n    } catch (error) {\n      logger?.error(\n        `Excluding subgraph \"${service.name}\" from supergraph composition: ${\n          error instanceof Error ? error.message : String(error)\n        }`,\n      );\n      return false;\n    }\n  });\n}\n\n// Forwards the incoming authorization header to federated subgraph requests.\nclass AuthenticatedDataSource extends RemoteGraphQLDataSource {\n  willSendRequest(options: GraphQLDataSourceProcessOptions) {\n    const { authorization } = options.context.headers as {\n      authorization: string;\n    };\n    if (authorization && options?.request.http) {\n      options.request.http.headers.set(\"authorization\", authorization);\n    }\n  }\n}\n\nexport class ApolloGatewayAdapter implements IGatewayAdapter<Context> {\n  readonly #logger: ILogger;\n  readonly #servers: ApolloServer<Context>[] = [];\n\n  #supergraphServer: ApolloServer<Context> | null = null;\n  #gatewayOptions: {\n    update: SupergraphSdlUpdateFunction;\n    healthCheck: SubgraphHealthCheckFunction;\n    getDataSource: GetDataSourceFunction;\n  } | null = null;\n  #getSubgraphs: (() => SubgraphDefinition[]) | null = null;\n\n  constructor(logger: ILogger) {\n    this.#logger = logger;\n  }\n\n  async start(_httpServer: http.Server): Promise<void> {\n    // Per-subgraph Apollo servers start lazily in createHandler.\n    // Nothing to do here.\n  }\n\n  async createHandler(\n    schema: GraphQLSchema,\n    contextFactory: GatewayContextFactory<Context>,\n  ): Promise<FetchHandler> {\n    const server = new ApolloServer<Context>({\n      schema,\n      logger: this.#logger,\n      introspection: true,\n      // Each ApolloServer otherwise registers its own SIGINT/SIGTERM handler\n      // that re-emits the signal via process.kill after server.stop() resolves.\n      // With many subgraph servers that re-emit lands while the reactor's\n      // graceful-shutdown chain is mid-flight, looking like a \"second SIGINT\"\n      // to the reactor and aborting cleanup. The reactor drives our shutdown\n      // end-to-end (see ReactorBuilder.attachSignalHandlers), so opt out.\n      stopOnTerminationSignals: false,\n      plugins: [\n        ApolloServerPluginInlineTraceDisabled(),\n        ApolloServerPluginLandingPageLocalDefault(),\n      ],\n    });\n    await server.start();\n    this.#servers.push(server);\n    return createApolloFetchHandler(server, contextFactory);\n  }\n\n  async createSupergraphHandler(\n    getSubgraphs: () => SubgraphDefinition[],\n    httpServer: http.Server,\n    contextFactory: GatewayContextFactory<Context>,\n  ): Promise<FetchHandler> {\n    if (this.#supergraphServer) {\n      throw new Error(\"Supergraph server is already running\");\n    }\n\n    this.#getSubgraphs = getSubgraphs;\n\n    const gateway = new ApolloGateway({\n      supergraphSdl: async (options) => {\n        this.#gatewayOptions = options;\n        return await this.#buildSupergraphSdl();\n      },\n      buildService: (serviceConfig) =>\n        new AuthenticatedDataSource(serviceConfig),\n    });\n\n    this.#supergraphServer = new ApolloServer<Context>({\n      gateway,\n      logger: this.#logger,\n      introspection: true,\n      stopOnTerminationSignals: false,\n      plugins: [\n        ApolloServerPluginDrainHttpServer({ httpServer }),\n        ApolloServerPluginInlineTraceDisabled(),\n        ApolloServerPluginLandingPageLocalDefault(),\n      ],\n    });\n\n    await this.#supergraphServer.start();\n    return createApolloFetchHandler(this.#supergraphServer, contextFactory);\n  }\n\n  async updateSupergraph(): Promise<void> {\n    if (!this.#gatewayOptions || !this.#getSubgraphs) {\n      // Not yet initialized - no-op.\n      return;\n    }\n    const { supergraphSdl } = await this.#buildSupergraphSdl();\n    this.#gatewayOptions.update(supergraphSdl);\n  }\n\n  async #buildSupergraphSdl() {\n    if (!this.#gatewayOptions || !this.#getSubgraphs) {\n      throw new Error(\"Gateway is not ready\");\n    }\n    const serviceList: ServiceDefinition[] = this.#getSubgraphs().map((s) => ({\n      name: s.name,\n      typeDefs: s.typeDefs,\n      url: s.url,\n    }));\n    const composableServiceList = filterComposableSubgraphs(\n      serviceList,\n      this.#logger,\n    );\n    const localCompose = new LocalCompose({\n      localServiceList: composableServiceList,\n    });\n    return localCompose.initialize(this.#gatewayOptions);\n  }\n\n  attachWebSocket(\n    wsServer: WebSocketServer,\n    schema: GraphQLSchema,\n    contextFactory: WsContextFactory<Context>,\n  ): WsDisposer {\n    return useServer(\n      {\n        schema,\n        context: async (ctx: {\n          connectionParams?: Record<string, unknown>;\n        }) => {\n          const connectionParams = (ctx.connectionParams ?? {}) as Record<\n            string,\n            unknown\n          >;\n          return contextFactory(connectionParams);\n        },\n      },\n      wsServer,\n    );\n  }\n\n  async stop(): Promise<void> {\n    await Promise.all(this.#servers.map((s) => s.stop()));\n    this.#servers.length = 0;\n\n    if (this.#supergraphServer) {\n      await this.#supergraphServer.stop();\n      this.#supergraphServer = null;\n    }\n    this.#gatewayOptions = null;\n    this.#getSubgraphs = null;\n  }\n}\n\n/**\n * Wrap an existing ApolloServer as a Fetch API handler.\n * Does not manage server lifecycle; caller is responsible for start/stop.\n */\nexport function createApolloFetchHandler(\n  server: ApolloServer<Context>,\n  contextFactory: GatewayContextFactory<Context>,\n): FetchHandler {\n  return async (request: Request): Promise<Response> => {\n    const url = new URL(request.url);\n    const headers = new HeaderMap();\n    request.headers.forEach((value, key) => headers.set(key, value));\n\n    let body: unknown = undefined;\n    if (request.method !== \"GET\" && request.method !== \"HEAD\") {\n      try {\n        body = (await request.json()) as unknown;\n      } catch {\n        // body may be empty for some requests\n      }\n    }\n\n    const result = await server.executeHTTPGraphQLRequest({\n      httpGraphQLRequest: {\n        method: request.method.toUpperCase(),\n        headers,\n        search: url.search,\n        body,\n      },\n      context: () => contextFactory(request),\n    });\n\n    const responseHeaders = new Headers();\n    for (const [key, value] of result.headers) {\n      responseHeaders.set(key, value);\n    }\n\n    let responseBody: string;\n    if (result.body.kind === \"complete\") {\n      responseBody = result.body.string;\n    } else {\n      const chunks: string[] = [];\n      for await (const chunk of result.body.asyncIterator) {\n        chunks.push(chunk);\n      }\n      responseBody = chunks.join(\"\");\n    }\n\n    return new Response(responseBody, {\n      status: result.status ?? 200,\n      headers: responseHeaders,\n    });\n  };\n}\n"],"names":["#logger","#servers","#supergraphServer","#getSubgraphs","#gatewayOptions","#buildSupergraphSdl"],"mappings":"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","debug_id":"75bd4780-abf6-5deb-836d-0da65e8050b8"}