@powerhousedao/reactor-api 6.2.3-dev.3 → 6.2.3-dev.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{adapter-gateway-apollo-Cn9E96GF.mjs → adapter-gateway-apollo-B54G_cXT.mjs} +18 -17
- package/dist/adapter-gateway-apollo-B54G_cXT.mjs.map +1 -0
- package/dist/{adapter-gateway-apollo-C_RdohOv.mjs → adapter-gateway-apollo-D2DNlPY0.mjs} +3 -3
- package/dist/{adapter-gateway-mercurius-CjaUcMgX.mjs → adapter-gateway-mercurius-rbKlwF9m.mjs} +7 -6
- package/dist/adapter-gateway-mercurius-rbKlwF9m.mjs.map +1 -0
- package/dist/{adapter-gateway-stitching-HmWjRMhm.mjs → adapter-gateway-stitching-Dzv10u8D.mjs} +18 -14
- package/dist/adapter-gateway-stitching-Dzv10u8D.mjs.map +1 -0
- package/dist/{adapter-http-express-B39rPhqH.mjs → adapter-http-express-CWqziB1p.mjs} +7 -7
- package/dist/adapter-http-express-CWqziB1p.mjs.map +1 -0
- package/dist/{adapter-http-fastify-BwXO6rqE.mjs → adapter-http-fastify-CYZQ6FFv.mjs} +10 -9
- package/dist/adapter-http-fastify-CYZQ6FFv.mjs.map +1 -0
- package/dist/index.d.mts +863 -453
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1000 -258
- package/dist/index.mjs.map +1 -1
- package/dist/{utils-XWVecaNn.mjs → pieces-BzaJGkRl.mjs} +130 -9
- package/dist/pieces-BzaJGkRl.mjs.map +1 -0
- package/dist/rolldown-runtime-8H4AJuhK.mjs +16 -0
- package/dist/run-node-handler-CQjyjOuN.mjs +52 -0
- package/dist/run-node-handler-CQjyjOuN.mjs.map +1 -0
- package/dist/src/packages/https-hooks.d.mts +4 -4
- package/dist/src/packages/https-hooks.d.mts.map +1 -1
- package/dist/src/packages/https-hooks.mjs +2 -2
- package/dist/src/packages/https-hooks.mjs.map +1 -1
- package/dist/src/packages/vite-loader.d.mts +10 -6
- package/dist/src/packages/vite-loader.d.mts.map +1 -1
- package/dist/src/packages/vite-loader.mjs +71 -9
- package/dist/src/packages/vite-loader.mjs.map +1 -1
- package/dist/{types-DcYyNcHJ.d.mts → types-D4Qnqo_D.d.mts} +26 -3
- package/dist/types-D4Qnqo_D.d.mts.map +1 -0
- package/dist/{websocket-DncAB9XK.mjs → websocket-_SH5aBcy.mjs} +3 -3
- package/dist/{websocket-DncAB9XK.mjs.map → websocket-_SH5aBcy.mjs.map} +1 -1
- package/package.json +18 -18
- package/dist/adapter-gateway-apollo-Cn9E96GF.mjs.map +0 -1
- package/dist/adapter-gateway-mercurius-CjaUcMgX.mjs.map +0 -1
- package/dist/adapter-gateway-stitching-HmWjRMhm.mjs.map +0 -1
- package/dist/adapter-http-express-B39rPhqH.mjs.map +0 -1
- package/dist/adapter-http-fastify-BwXO6rqE.mjs.map +0 -1
- package/dist/path-normalize-BOQwY9wR.mjs +0 -23
- package/dist/path-normalize-BOQwY9wR.mjs.map +0 -1
- package/dist/types-DcYyNcHJ.d.mts.map +0 -1
- package/dist/utils-XWVecaNn.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
-
import { t as useServer } from "./websocket-
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="cec74315-a3ad-5b30-a06a-f13108285db4")}catch(e){}}();
|
|
3
|
+
import { t as useServer } from "./websocket-_SH5aBcy.mjs";
|
|
4
4
|
import { buildSubgraphSchema } from "@apollo/subgraph";
|
|
5
5
|
import { ApolloGateway, LocalCompose, RemoteGraphQLDataSource } from "@apollo/gateway";
|
|
6
6
|
import { ApolloServer, HeaderMap } from "@apollo/server";
|
|
@@ -66,14 +66,15 @@ var ApolloGatewayAdapter = class {
|
|
|
66
66
|
async createSupergraphHandler(getSubgraphs, httpServer, contextFactory) {
|
|
67
67
|
if (this.#supergraphServer) throw new Error("Supergraph server is already running");
|
|
68
68
|
this.#getSubgraphs = getSubgraphs;
|
|
69
|
+
const gateway = new ApolloGateway({
|
|
70
|
+
supergraphSdl: async (options) => {
|
|
71
|
+
this.#gatewayOptions = options;
|
|
72
|
+
return await this.#buildSupergraphSdl();
|
|
73
|
+
},
|
|
74
|
+
buildService: (serviceConfig) => new AuthenticatedDataSource(serviceConfig)
|
|
75
|
+
});
|
|
69
76
|
this.#supergraphServer = new ApolloServer({
|
|
70
|
-
gateway
|
|
71
|
-
supergraphSdl: async (options) => {
|
|
72
|
-
this.#gatewayOptions = options;
|
|
73
|
-
return await this.#buildSupergraphSdl();
|
|
74
|
-
},
|
|
75
|
-
buildService: (serviceConfig) => new AuthenticatedDataSource(serviceConfig)
|
|
76
|
-
}),
|
|
77
|
+
gateway,
|
|
77
78
|
logger: this.#logger,
|
|
78
79
|
introspection: true,
|
|
79
80
|
stopOnTerminationSignals: false,
|
|
@@ -93,18 +94,18 @@ var ApolloGatewayAdapter = class {
|
|
|
93
94
|
}
|
|
94
95
|
async #buildSupergraphSdl() {
|
|
95
96
|
if (!this.#gatewayOptions || !this.#getSubgraphs) throw new Error("Gateway is not ready");
|
|
96
|
-
|
|
97
|
+
const composableServiceList = filterComposableSubgraphs(this.#getSubgraphs().map((s) => ({
|
|
97
98
|
name: s.name,
|
|
98
99
|
typeDefs: s.typeDefs,
|
|
99
100
|
url: s.url
|
|
100
|
-
})), this.#logger)
|
|
101
|
+
})), this.#logger);
|
|
102
|
+
return new LocalCompose({ localServiceList: composableServiceList }).initialize(this.#gatewayOptions);
|
|
101
103
|
}
|
|
102
|
-
attachWebSocket(wsServer, schema,
|
|
104
|
+
attachWebSocket(wsServer, schema, handlers) {
|
|
103
105
|
return useServer({
|
|
104
106
|
schema,
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
107
|
+
onConnect: (ctx) => handlers.onConnect(ctx.connectionParams ?? {}, ctx.extra),
|
|
108
|
+
context: (ctx) => handlers.context(ctx.connectionParams ?? {}, ctx.extra)
|
|
108
109
|
}, wsServer);
|
|
109
110
|
}
|
|
110
111
|
async stop() {
|
|
@@ -158,5 +159,5 @@ function createApolloFetchHandler(server, contextFactory) {
|
|
|
158
159
|
//#endregion
|
|
159
160
|
export { createApolloFetchHandler as n, filterComposableSubgraphs as r, ApolloGatewayAdapter as t };
|
|
160
161
|
|
|
161
|
-
//# sourceMappingURL=adapter-gateway-apollo-
|
|
162
|
-
//# debugId=
|
|
162
|
+
//# sourceMappingURL=adapter-gateway-apollo-B54G_cXT.mjs.map
|
|
163
|
+
//# debugId=cec74315-a3ad-5b30-a06a-f13108285db4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-gateway-apollo-B54G_cXT.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 WsDisposer,\n WsHandlers,\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 // Array form: @apollo/subgraph 2.15 dropped the bare-module overload.\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 constructor(\n config?: ConstructorParameters<typeof RemoteGraphQLDataSource>[0],\n ) {\n // Node's fetch retires a pooled connection before the subgraph server's advertised keep-alive\n // timeout; Apollo's default make-fetch-happen agent holds it until the server closes it under us.\n super({\n fetcher: fetch as RemoteGraphQLDataSource[\"fetcher\"],\n ...config,\n });\n }\n\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 handlers: WsHandlers<Context>,\n ): WsDisposer {\n return useServer(\n {\n schema,\n onConnect: (ctx) =>\n handlers.onConnect(ctx.connectionParams ?? {}, ctx.extra as object),\n context: (ctx) =>\n handlers.context(ctx.connectionParams ?? {}, ctx.extra as object),\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":[],"mappings":"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","debug_id":"cec74315-a3ad-5b30-a06a-f13108285db4"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as ApolloGatewayAdapter } from "./adapter-gateway-apollo-
|
|
1
|
+
import { t as ApolloGatewayAdapter } from "./adapter-gateway-apollo-B54G_cXT.mjs";
|
|
2
2
|
export { ApolloGatewayAdapter };
|
|
3
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
4
|
-
//# debugId=
|
|
3
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="65868ada-1705-55c6-bedd-92c2ef113956")}catch(e){}}();
|
|
4
|
+
//# debugId=65868ada-1705-55c6-bedd-92c2ef113956
|
package/dist/{adapter-gateway-mercurius-CjaUcMgX.mjs → adapter-gateway-mercurius-rbKlwF9m.mjs}
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
-
import { t as useServer } from "./websocket-
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="6ddd7998-f0f6-54a1-b9e8-fa189ecfedc9")}catch(e){}}();
|
|
3
|
+
import { t as useServer } from "./websocket-_SH5aBcy.mjs";
|
|
4
4
|
import mercuriusGateway from "@mercuriusjs/gateway";
|
|
5
5
|
import Fastify from "fastify";
|
|
6
6
|
import mercurius from "mercurius";
|
|
@@ -47,10 +47,11 @@ var MercuriusGatewayAdapter = class {
|
|
|
47
47
|
this.#supergraphApp = newApp;
|
|
48
48
|
if (oldApp) await oldApp.close();
|
|
49
49
|
}
|
|
50
|
-
attachWebSocket(wsServer, schema,
|
|
50
|
+
attachWebSocket(wsServer, schema, handlers) {
|
|
51
51
|
return useServer({
|
|
52
52
|
schema,
|
|
53
|
-
|
|
53
|
+
onConnect: (ctx) => handlers.onConnect(ctx.connectionParams ?? {}, ctx.extra),
|
|
54
|
+
context: (ctx) => handlers.context(ctx.connectionParams ?? {}, ctx.extra)
|
|
54
55
|
}, wsServer);
|
|
55
56
|
}
|
|
56
57
|
async stop() {
|
|
@@ -128,5 +129,5 @@ async function injectRequest(app, request) {
|
|
|
128
129
|
//#endregion
|
|
129
130
|
export { MercuriusGatewayAdapter };
|
|
130
131
|
|
|
131
|
-
//# sourceMappingURL=adapter-gateway-mercurius-
|
|
132
|
-
//# debugId=
|
|
132
|
+
//# sourceMappingURL=adapter-gateway-mercurius-rbKlwF9m.mjs.map
|
|
133
|
+
//# debugId=6ddd7998-f0f6-54a1-b9e8-fa189ecfedc9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-gateway-mercurius-rbKlwF9m.mjs","sources":["../src/graphql/gateway/adapter-gateway-mercurius.ts"],"sourcesContent":["import type { MercuriusGatewayOptions } from \"@mercuriusjs/gateway\";\nimport mercuriusGateway from \"@mercuriusjs/gateway\";\nimport Fastify from \"fastify\";\nimport type {\n FastifyInstance,\n FastifyPluginCallback,\n FastifyReply,\n FastifyRequest,\n} from \"fastify\";\nimport mercurius from \"mercurius\";\nimport type { MercuriusOptions } from \"mercurius\";\nimport type { ILogger } from \"document-model\";\nimport type { GraphQLSchema } from \"graphql\";\nimport type http from \"node:http\";\nimport { AsyncLocalStorage } from \"node:async_hooks\";\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 WsDisposer,\n WsHandlers,\n} from \"./types.js\";\n\n/**\n * Threads the original Fetch API Request through Mercurius's internal\n * fastify.inject() call so that contextFactory (which uses the auth WeakMap\n * from auth-middleware.ts) receives the same Request object that auth\n * middleware populated before calling the handler.\n */\nconst requestAls = new AsyncLocalStorage<Request>();\n\n// @mercuriusjs/gateway exports a plain `(instance, opts) => void` rather than\n// a typed FastifyPluginCallback — cast once here.\n// The gateway plugin's accepted options are the intersection of the base\n// MercuriusOptions and MercuriusGatewayOptions (mirroring the unexported\n// MercuriusFederationOptions type in the package).\ntype GatewayPluginOptions = MercuriusOptions & MercuriusGatewayOptions;\nconst mercuriusGatewayPlugin =\n mercuriusGateway as unknown as FastifyPluginCallback<GatewayPluginOptions>;\n\nexport class MercuriusGatewayAdapter implements IGatewayAdapter<Context> {\n readonly #logger: ILogger;\n readonly #subgraphApps: FastifyInstance[] = [];\n\n #supergraphApp: FastifyInstance | null = null;\n #getSubgraphs: (() => SubgraphDefinition[]) | null = null;\n #supergraphContextFactory: GatewayContextFactory<Context> | null = null;\n\n constructor(logger: ILogger) {\n this.#logger = logger;\n }\n\n async start(_httpServer: http.Server): Promise<void> {\n // Mercurius instances are started lazily in createHandler /\n // createSupergraphHandler — nothing to do here.\n }\n\n async createHandler(\n schema: GraphQLSchema,\n contextFactory: GatewayContextFactory<Context>,\n ): Promise<FetchHandler> {\n const app = await buildMercuriusApp(schema, contextFactory, this.#logger);\n this.#subgraphApps.push(app);\n return buildFetchHandler(app);\n }\n\n async createSupergraphHandler(\n getSubgraphs: () => SubgraphDefinition[],\n _httpServer: http.Server,\n contextFactory: GatewayContextFactory<Context>,\n ): Promise<FetchHandler> {\n if (this.#supergraphApp) {\n throw new Error(\"Supergraph is already running\");\n }\n this.#getSubgraphs = getSubgraphs;\n this.#supergraphContextFactory = contextFactory;\n this.#supergraphApp = await buildGatewayApp(\n getSubgraphs(),\n contextFactory,\n this.#logger,\n );\n\n // Capture `this` so the returned handler always delegates to the *current*\n // #supergraphApp, allowing updateSupergraph() to swap it atomically.\n // eslint-disable-next-line @typescript-eslint/no-this-alias\n const adapter = this;\n return (request: Request): Promise<Response> => {\n if (!adapter.#supergraphApp) {\n return Promise.resolve(\n new Response(\"Gateway not ready\", { status: 503 }),\n );\n }\n return requestAls.run(request, () =>\n injectRequest(adapter.#supergraphApp!, request),\n );\n };\n }\n\n async updateSupergraph(): Promise<void> {\n if (!this.#getSubgraphs || !this.#supergraphContextFactory) return;\n const newApp = await buildGatewayApp(\n this.#getSubgraphs(),\n this.#supergraphContextFactory,\n this.#logger,\n );\n const oldApp = this.#supergraphApp;\n // Swap atomically — in-flight requests on the old app finish normally.\n this.#supergraphApp = newApp;\n if (oldApp) await oldApp.close();\n }\n\n attachWebSocket(\n wsServer: WebSocketServer,\n schema: GraphQLSchema,\n handlers: WsHandlers<Context>,\n ): WsDisposer {\n // Use graphql-ws directly; Mercurius's own subscription transport is\n // Fastify-specific and not applicable here.\n return useServer(\n {\n schema,\n onConnect: (ctx) =>\n handlers.onConnect(ctx.connectionParams ?? {}, ctx.extra as object),\n context: (ctx) =>\n handlers.context(ctx.connectionParams ?? {}, ctx.extra as object),\n },\n wsServer,\n );\n }\n\n async stop(): Promise<void> {\n await Promise.all(this.#subgraphApps.map((app) => app.close()));\n this.#subgraphApps.length = 0;\n if (this.#supergraphApp) {\n await this.#supergraphApp.close();\n this.#supergraphApp = null;\n }\n this.#getSubgraphs = null;\n this.#supergraphContextFactory = null;\n }\n}\n\n// ── Fastify instance factories ────────────────────────────────────────────────\n\nfunction makeContextFn(\n contextFactory: GatewayContextFactory<Context>,\n logger: ILogger,\n) {\n return (_req: FastifyRequest, _reply: FastifyReply) => {\n const request = requestAls.getStore();\n if (!request) {\n logger.error(\"[mercurius] No Fetch Request in AsyncLocalStorage\");\n throw new Error(\"No Fetch Request in AsyncLocalStorage\");\n }\n return contextFactory(request);\n };\n}\n\nasync function buildMercuriusApp(\n schema: GraphQLSchema,\n contextFactory: GatewayContextFactory<Context>,\n logger: ILogger,\n): Promise<FastifyInstance> {\n const app = Fastify({ logger: false });\n\n await app.register(mercurius, {\n schema,\n graphiql: false,\n context: makeContextFn(contextFactory, logger),\n // Override _Service.sdl to rewrite \"type Query/Mutation/Subscription {\"\n // as \"extend type … {\" so that @mercuriusjs/gateway v5 (which follows the\n // Federation v1 convention of using extensionTypeMap) correctly maps root\n // operation fields to this service during query planning.\n resolvers: {\n _Service: {\n sdl: (parent: { sdl?: string }) =>\n (parent.sdl ?? \"\").replace(\n /\\btype\\s+(Query|Mutation|Subscription)\\s*\\{/g,\n \"extend type $1 {\",\n ),\n },\n },\n } satisfies MercuriusOptions);\n\n await app.ready();\n return app;\n}\n\n/**\n * Builds a Mercurius federation gateway that composes the given subgraph\n * services. Each service URL must be reachable so that the gateway can fetch\n * its SDL via `_service { sdl }` (Apollo Federation protocol).\n */\nasync function buildGatewayApp(\n subgraphs: SubgraphDefinition[],\n contextFactory: GatewayContextFactory<Context>,\n logger: ILogger,\n): Promise<FastifyInstance> {\n const app = Fastify({ logger: false });\n\n await app.register(mercuriusGatewayPlugin, {\n gateway: {\n services: subgraphs.map((s) => ({ name: s.name, url: s.url })),\n },\n graphiql: false,\n context: makeContextFn(contextFactory, logger),\n });\n\n await app.ready();\n return app;\n}\n\n// ── Fetch API bridge ──────────────────────────────────────────────────────────\n\nfunction buildFetchHandler(app: FastifyInstance): FetchHandler {\n return (request: Request): Promise<Response> =>\n requestAls.run(request, () => injectRequest(app, request));\n}\n\nasync function injectRequest(\n app: FastifyInstance,\n request: Request,\n): Promise<Response> {\n const body =\n request.method !== \"GET\" && request.method !== \"HEAD\"\n ? await request.text()\n : undefined;\n\n const headers: Record<string, string> = {};\n request.headers.forEach((value, key) => {\n headers[key] = value;\n });\n\n const response = await app.inject({\n method: request.method as\n | \"DELETE\"\n | \"GET\"\n | \"HEAD\"\n | \"OPTIONS\"\n | \"PATCH\"\n | \"POST\"\n | \"PUT\",\n url: \"/graphql\",\n headers,\n payload: body,\n });\n\n const responseHeaders: Record<string, string> = {};\n for (const [key, value] of Object.entries(response.headers)) {\n if (value !== undefined) responseHeaders[key] = String(value);\n }\n\n return new Response(response.payload, {\n status: response.statusCode,\n headers: responseHeaders,\n });\n}\n"],"names":[],"mappings":"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","debug_id":"6ddd7998-f0f6-54a1-b9e8-fa189ecfedc9"}
|
package/dist/{adapter-gateway-stitching-HmWjRMhm.mjs → adapter-gateway-stitching-Dzv10u8D.mjs}
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
-
import { t as useServer } from "./websocket-
|
|
4
|
-
import { n as createApolloFetchHandler } from "./adapter-gateway-apollo-
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="86579686-a510-5b69-bb5b-7db68995aac8")}catch(e){}}();
|
|
3
|
+
import { t as useServer } from "./websocket-_SH5aBcy.mjs";
|
|
4
|
+
import { n as createApolloFetchHandler } from "./adapter-gateway-apollo-B54G_cXT.mjs";
|
|
5
5
|
import { Kind, buildASTSchema, isTypeDefinitionNode, isTypeExtensionNode, parse, print, validateSchema } from "graphql";
|
|
6
6
|
import { ApolloServer } from "@apollo/server";
|
|
7
7
|
import { ApolloServerPluginInlineTraceDisabled } from "@apollo/server/plugin/disabled";
|
|
@@ -70,10 +70,12 @@ function filterBuildableSubgraphs(subgraphs, logger) {
|
|
|
70
70
|
for (const def of subgraph.typeDefs.definitions) if (isTypeDefinitionNode(def)) own.add(def.name.value);
|
|
71
71
|
else if (def.kind === Kind.DIRECTIVE_DEFINITION) ownDirectives.add(def.name.value);
|
|
72
72
|
const stubs = [];
|
|
73
|
-
for (const name of new Set([...owners.keys(), ...ROOT_TYPE_NAMES])) if (!own.has(name)) stubs.push(stubDeclaration(name, owners.get(name)));
|
|
73
|
+
for (const name of /* @__PURE__ */ new Set([...owners.keys(), ...ROOT_TYPE_NAMES])) if (!own.has(name)) stubs.push(stubDeclaration(name, owners.get(name)));
|
|
74
74
|
for (const [name, def] of directiveDefs) if (!ownDirectives.has(name)) stubs.push(print(def));
|
|
75
75
|
try {
|
|
76
|
-
const
|
|
76
|
+
const documents = stubs.length > 0 ? [subgraph.typeDefs, parse(stubs.join("\n"))] : [subgraph.typeDefs];
|
|
77
|
+
const merged = mergeTypeDefs(documents, { useSchemaDefinition: false });
|
|
78
|
+
const errors = validateSchema(buildASTSchema(merged));
|
|
77
79
|
if (errors.length > 0) throw new Error(errors.map((error) => error.message).join("; "));
|
|
78
80
|
return true;
|
|
79
81
|
} catch (error) {
|
|
@@ -167,7 +169,8 @@ function mergeSubgraphTypeDefs(subgraphs, policy) {
|
|
|
167
169
|
* subgraph's resolver ran.
|
|
168
170
|
*/
|
|
169
171
|
function mergeSubgraphResolvers(subgraphs, policy) {
|
|
170
|
-
|
|
172
|
+
const ordered = policy === "first" ? [...subgraphs].reverse() : subgraphs;
|
|
173
|
+
return mergeResolvers(ordered.map((s) => s.resolvers));
|
|
171
174
|
}
|
|
172
175
|
/**
|
|
173
176
|
* Drops resolver entries that have no matching type or field in the merged
|
|
@@ -207,9 +210,10 @@ function filterResolversToSchema(typeMap, resolvers, logger) {
|
|
|
207
210
|
* resolvers (shared by the adapter and the pure merge helper).
|
|
208
211
|
*/
|
|
209
212
|
function buildMergedSchema(typeDefs, resolvers, logger) {
|
|
213
|
+
const filtered = filterResolversToSchema(buildASTSchema(typeDefs, { assumeValid: true }).getTypeMap(), resolvers, logger);
|
|
210
214
|
return makeExecutableSchema({
|
|
211
215
|
typeDefs,
|
|
212
|
-
resolvers:
|
|
216
|
+
resolvers: filtered
|
|
213
217
|
});
|
|
214
218
|
}
|
|
215
219
|
/**
|
|
@@ -257,12 +261,11 @@ var StitchingGatewayAdapter = class {
|
|
|
257
261
|
this.#installSupergraphServer(server);
|
|
258
262
|
if (old) await old.stop();
|
|
259
263
|
}
|
|
260
|
-
attachWebSocket(wsServer, schema,
|
|
264
|
+
attachWebSocket(wsServer, schema, handlers) {
|
|
261
265
|
return useServer({
|
|
262
266
|
schema,
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
267
|
+
onConnect: (ctx) => handlers.onConnect(ctx.connectionParams ?? {}, ctx.extra),
|
|
268
|
+
context: (ctx) => handlers.context(ctx.connectionParams ?? {}, ctx.extra)
|
|
266
269
|
}, wsServer);
|
|
267
270
|
}
|
|
268
271
|
async stop() {
|
|
@@ -306,12 +309,13 @@ var StitchingGatewayAdapter = class {
|
|
|
306
309
|
#installSupergraphServer(server) {
|
|
307
310
|
if (this.#contextFactory === null) throw new Error("Supergraph context factory not set");
|
|
308
311
|
this.#supergraphServer = server;
|
|
309
|
-
|
|
312
|
+
const handler = createApolloFetchHandler(server, this.#contextFactory);
|
|
313
|
+
this.#handler = handler;
|
|
310
314
|
return (request) => this.#handler(request);
|
|
311
315
|
}
|
|
312
316
|
};
|
|
313
317
|
//#endregion
|
|
314
318
|
export { StitchingGatewayAdapter };
|
|
315
319
|
|
|
316
|
-
//# sourceMappingURL=adapter-gateway-stitching-
|
|
317
|
-
//# debugId=
|
|
320
|
+
//# sourceMappingURL=adapter-gateway-stitching-Dzv10u8D.mjs.map
|
|
321
|
+
//# debugId=86579686-a510-5b69-bb5b-7db68995aac8
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-gateway-stitching-Dzv10u8D.mjs","sources":["../src/graphql/gateway/adapter-gateway-stitching.ts"],"sourcesContent":["import { ApolloServer } from \"@apollo/server\";\nimport { ApolloServerPluginInlineTraceDisabled } from \"@apollo/server/plugin/disabled\";\nimport { ApolloServerPluginLandingPageLocalDefault } from \"@apollo/server/plugin/landingPage/default\";\nimport { mergeResolvers, mergeTypeDefs } from \"@graphql-tools/merge\";\nimport { makeExecutableSchema } from \"@graphql-tools/schema\";\nimport type { IResolvers } from \"@graphql-tools/utils\";\nimport {\n buildASTSchema,\n type DefinitionNode,\n type DocumentNode,\n isTypeDefinitionNode,\n isTypeExtensionNode,\n Kind,\n parse,\n print,\n type GraphQLNamedType,\n type GraphQLSchema,\n type TypeDefinitionNode,\n validateSchema,\n} from \"graphql\";\nimport type http from \"node:http\";\nimport type { WebSocketServer } from \"ws\";\nimport type { ILogger } from \"document-model\";\nimport type { Context } from \"../types.js\";\nimport { useServer } from \"../websocket.js\";\nimport { createApolloFetchHandler } from \"./adapter-gateway-apollo.js\";\nimport type {\n FetchHandler,\n GatewayContextFactory,\n IGatewayAdapter,\n SubgraphDefinition,\n WsDisposer,\n WsHandlers,\n} from \"./types.js\";\n\n/**\n * How to resolve two subgraphs that define the same field with incompatible\n * types (a name clash, the #1565 scenario).\n *\n * - `\"last\"` (default): the later-registered subgraph's field definition wins.\n * - `\"first\"`: the earlier-registered subgraph's field definition wins.\n *\n * Every conflict is logged either way; unlike federation composition, the\n * supergraph is still built. The policy applies to field type clashes only:\n * a type-KIND clash (the same type name declared as an object by one subgraph\n * and as an enum/input/interface/union by another) is resolved by\n * `mergeTypeDefs` itself, which always keeps the last declaration. Those are\n * detected separately and reported through the same channel so they are\n * logged rather than dropped in silence.\n */\nexport type StitchConflictPolicy = \"first\" | \"last\";\n\nexport type StitchingGatewayOptions = {\n onFieldTypeConflict?: StitchConflictPolicy;\n};\n\n/** Root operation types, which a subgraph may extend without defining. */\nconst ROOT_TYPE_NAMES = [\"Query\", \"Mutation\", \"Subscription\"];\n\n/**\n * A placeholder declaration for a type name a subgraph borrows from another\n * subgraph. Same kind as the real declaration, so the borrowing subgraph's\n * SDL validates the same way it does in the merged supergraph:\n *\n * - scalar/enum/input/object: a minimal declaration of that kind.\n * - interface: the real declaration, because a subgraph that implements the\n * interface has to satisfy its field set.\n * - union: a self-contained union over one placeholder member, so nothing the\n * real declaration references has to be resolvable here.\n */\nfunction stubDeclaration(\n name: string,\n owner: TypeDefinitionNode | undefined,\n): string {\n switch (owner?.kind) {\n case Kind.SCALAR_TYPE_DEFINITION:\n return `scalar ${name}`;\n case Kind.ENUM_TYPE_DEFINITION:\n return `enum ${name} { _STUB }`;\n case Kind.INPUT_OBJECT_TYPE_DEFINITION:\n return `input ${name} { _stub: Boolean }`;\n case Kind.INTERFACE_TYPE_DEFINITION:\n return print(owner);\n case Kind.UNION_TYPE_DEFINITION:\n return `type ${name}_Stub { _stub: Boolean } union ${name} = ${name}_Stub`;\n default:\n return `type ${name} { _stub: Boolean }`;\n }\n}\n\n/**\n * Drops subgraphs whose own typeDefs cannot be built into a valid schema, so\n * one malformed subgraph is excluded instead of failing the whole supergraph\n * build (which, since GraphQLManager.init() does not guard the supergraph\n * gateway, would be a boot failure). Each excluded subgraph is logged; the\n * rest are merged. This is the stitching counterpart of the federation path's\n * `filterComposableSubgraphs`.\n *\n * A stitching subgraph is not self-contained the way a federation subgraph is:\n * it may extend a root type it never defines (`analytics` only does\n * `extend type Query`) and reference types another subgraph owns (in raw form\n * `analytics` uses `DateTime`, declared by `auth`/`reactor`/`packages`).\n * Building it alone would therefore reject perfectly good subgraphs. So the isolation build adds\n * a placeholder declaration for every type name declared by some *other*\n * subgraph in the set, plus the root types, plus every directive definition in\n * the set. What is left to fail is the subgraph's own SDL: a dangling type\n * reference no subgraph declares, a duplicated field, an invalid implements\n * clause.\n *\n * Scope: like the federation filter, this only catches per-subgraph failures.\n * Errors that appear only once subgraphs are merged (a field type or type kind\n * clash) are reported by `mergeSubgraphTypeDefs`, not here.\n */\nexport function filterBuildableSubgraphs(\n subgraphs: SubgraphDefinition[],\n logger?: Pick<ILogger, \"error\">,\n): SubgraphDefinition[] {\n // First declaration of each type name across the whole set, for stub kinds.\n const owners = new Map<string, TypeDefinitionNode>();\n const directiveDefs = new Map<string, DefinitionNode>();\n for (const subgraph of subgraphs) {\n for (const def of subgraph.typeDefs.definitions) {\n if (isTypeDefinitionNode(def) && !owners.has(def.name.value)) {\n owners.set(def.name.value, def);\n } else if (\n def.kind === Kind.DIRECTIVE_DEFINITION &&\n !directiveDefs.has(def.name.value)\n ) {\n directiveDefs.set(def.name.value, def);\n }\n }\n }\n\n return subgraphs.filter((subgraph) => {\n const own = new Set<string>();\n const ownDirectives = new Set<string>();\n for (const def of subgraph.typeDefs.definitions) {\n if (isTypeDefinitionNode(def)) {\n own.add(def.name.value);\n } else if (def.kind === Kind.DIRECTIVE_DEFINITION) {\n ownDirectives.add(def.name.value);\n }\n }\n\n const stubs: string[] = [];\n for (const name of new Set([...owners.keys(), ...ROOT_TYPE_NAMES])) {\n if (!own.has(name)) {\n stubs.push(stubDeclaration(name, owners.get(name)));\n }\n }\n for (const [name, def] of directiveDefs) {\n if (!ownDirectives.has(name)) {\n stubs.push(print(def));\n }\n }\n\n try {\n // Merge the same way the supergraph does, so a subgraph that declares a\n // type twice within itself is normalized here too, then build and\n // validate: buildASTSchema asserts the SDL, validateSchema the result.\n const documents =\n stubs.length > 0\n ? [subgraph.typeDefs, parse(stubs.join(\"\\n\"))]\n : [subgraph.typeDefs];\n const merged = mergeTypeDefs(documents, { useSchemaDefinition: false });\n const errors = validateSchema(buildASTSchema(merged));\n if (errors.length > 0) {\n throw new Error(errors.map((error) => error.message).join(\"; \"));\n }\n return true;\n } catch (error) {\n logger?.error(\n `Stitching gateway: excluding subgraph \"${subgraph.name}\" from the supergraph: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return false;\n }\n });\n}\n\n/**\n * The SDL keyword for a type definition or extension node, or undefined for a\n * node that does not declare a named type (schema definitions, directive\n * definitions, executable definitions). Extensions map to the same keyword as\n * their definition: `extend type Query` and `type Query` are the same kind.\n */\nfunction typeKeyword(def: DefinitionNode): string | undefined {\n switch (def.kind) {\n case Kind.OBJECT_TYPE_DEFINITION:\n case Kind.OBJECT_TYPE_EXTENSION:\n return \"type\";\n case Kind.INTERFACE_TYPE_DEFINITION:\n case Kind.INTERFACE_TYPE_EXTENSION:\n return \"interface\";\n case Kind.UNION_TYPE_DEFINITION:\n case Kind.UNION_TYPE_EXTENSION:\n return \"union\";\n case Kind.ENUM_TYPE_DEFINITION:\n case Kind.ENUM_TYPE_EXTENSION:\n return \"enum\";\n case Kind.INPUT_OBJECT_TYPE_DEFINITION:\n case Kind.INPUT_OBJECT_TYPE_EXTENSION:\n return \"input\";\n case Kind.SCALAR_TYPE_DEFINITION:\n case Kind.SCALAR_TYPE_EXTENSION:\n return \"scalar\";\n default:\n return undefined;\n }\n}\n\n/**\n * Finds type names that two subgraphs declare with different kinds (object vs\n * enum, input, interface or union). `mergeTypeDefs` does not call\n * `onFieldTypeConflict` for these - it keeps the last declaration and drops\n * the other subgraph's type entirely, with no diagnostic - so they are\n * scanned for before the merge and reported alongside field conflicts.\n */\nfunction scanTypeKindClashes(subgraphs: SubgraphDefinition[]): string[] {\n const clashes: string[] = [];\n const declaredBy = new Map<string, { keyword: string; subgraph: string }>();\n for (const subgraph of subgraphs) {\n for (const def of subgraph.typeDefs.definitions) {\n if (!isTypeDefinitionNode(def) && !isTypeExtensionNode(def)) {\n continue;\n }\n const keyword = typeKeyword(def);\n if (keyword === undefined) {\n continue;\n }\n const name = def.name.value;\n const previous = declaredBy.get(name);\n if (previous === undefined) {\n declaredBy.set(name, { keyword, subgraph: subgraph.name });\n continue;\n }\n if (previous.keyword === keyword) {\n continue;\n }\n clashes.push(\n `type kind clash on ${name}: declared as ${previous.keyword} by ` +\n `subgraph \"${previous.subgraph}\" and as ${keyword} by subgraph ` +\n `\"${subgraph.name}\"; mergeTypeDefs keeps the last declaration ` +\n `(${keyword}) and drops the other, whatever the conflict policy`,\n );\n // Track what the merge actually keeps, so a third declaration is\n // compared against the surviving kind.\n declaredBy.set(name, { keyword, subgraph: subgraph.name });\n }\n }\n return clashes;\n}\n\n/**\n * Merges all subgraph type definitions into one schema, applying the field\n * conflict policy. Exported so tests and the benchmark can exercise the merge\n * without a full adapter.\n */\nexport function mergeSubgraphTypeDefs(\n subgraphs: SubgraphDefinition[],\n policy: StitchConflictPolicy,\n): { typeDefs: DocumentNode; conflicts: string[] } {\n const conflicts: string[] = scanTypeKindClashes(subgraphs);\n const typeDefs = mergeTypeDefs(\n subgraphs.map((s) => s.typeDefs),\n {\n // Never emit a `schema { ... }` block: the merged root types are\n // determined by whichever subgraphs define Query/Mutation/Subscription.\n useSchemaDefinition: false,\n onFieldTypeConflict: (existing, other, type) => {\n // Identical definitions are normal stitching (shared types across\n // subgraphs); only differing definitions are conflicts.\n if (print(existing) === print(other)) {\n return existing;\n }\n const winner = policy === \"first\" ? existing : other;\n const loser = policy === \"first\" ? other : existing;\n conflicts.push(\n `field type clash on ${type.name.value}.${existing.name.value}: kept ` +\n `${print(winner.type)} (${\n policy === \"first\" ? \"first\" : \"last\"\n } subgraph), dropped ${print(loser.type)}`,\n );\n return winner;\n },\n },\n );\n return { typeDefs, conflicts };\n}\n\n/**\n * Merges the subgraph resolver maps in the order the conflict policy implies.\n * `mergeResolvers` is unconditionally last-wins, so under `\"first\"` the array\n * is reversed: the first-registered subgraph's resolver ends up applied last\n * and wins, matching the field type the merged schema advertises. Without\n * this the schema would advertise one subgraph's type while another\n * subgraph's resolver ran.\n */\nfunction mergeSubgraphResolvers(\n subgraphs: SubgraphDefinition[],\n policy: StitchConflictPolicy,\n): IResolvers {\n const ordered = policy === \"first\" ? [...subgraphs].reverse() : subgraphs;\n return mergeResolvers(\n ordered.map((s) => s.resolvers as IResolvers | undefined),\n );\n}\n\n/**\n * Drops resolver entries that have no matching type or field in the merged\n * schema. Document-model subgraphs generate resolver entries for operations\n * their SDL does not declare (the federation runtime silently ignores those);\n * makeExecutableSchema rejects them, so they are filtered before the build.\n */\nfunction filterResolversToSchema(\n typeMap: Record<string, GraphQLNamedType | undefined>,\n resolvers: Record<string, unknown>,\n logger?: Pick<ILogger, \"debug\">,\n): Record<string, unknown> {\n const filtered: Record<string, unknown> = {};\n for (const [typeName, entry] of Object.entries(resolvers)) {\n const type = typeMap[typeName];\n if (type === undefined) {\n // Type not part of the merged schema.\n logger?.debug(\n `Stitching gateway: dropped resolvers for type \"${typeName}\": the merged schema has no such type`,\n );\n continue;\n }\n const fields =\n type instanceof Object && \"getFields\" in type\n ? (type as { getFields(): Record<string, unknown> }).getFields()\n : undefined;\n if (fields === undefined) {\n // Scalar, enum, or union resolver: keep as-is.\n filtered[typeName] = entry;\n continue;\n }\n if (entry === null || typeof entry !== \"object\") {\n // Not a resolver map: nothing to keep.\n logger?.debug(\n `Stitching gateway: dropped resolver entry for type \"${typeName}\": expected a field map, got ${entry === null ? \"null\" : typeof entry}`,\n );\n continue;\n }\n const map = entry as { [key: string]: unknown };\n const kept: Record<string, unknown> = {};\n const dropped: string[] = [];\n for (const [key, value] of Object.entries(map)) {\n if (key === \"__resolveReference\" || fields[key] !== undefined) {\n kept[key] = value;\n } else {\n dropped.push(key);\n }\n }\n if (dropped.length > 0) {\n logger?.debug(\n `Stitching gateway: dropped resolvers ${dropped\n .map((field) => `${typeName}.${field}`)\n .join(\", \")}: the merged schema declares no such field(s)`,\n );\n }\n if (Object.keys(kept).length > 0) {\n filtered[typeName] = kept;\n }\n }\n return filtered;\n}\n\n/**\n * Builds the executable supergraph schema from merged type definitions and\n * resolvers (shared by the adapter and the pure merge helper).\n */\nfunction buildMergedSchema(\n typeDefs: DocumentNode,\n resolvers: IResolvers,\n logger?: Pick<ILogger, \"debug\">,\n): GraphQLSchema {\n // A type-only build to get the merged type map; assumeValid skips SDL\n // validation, which makeExecutableSchema performs on the real build.\n const typeMap = buildASTSchema(typeDefs, { assumeValid: true }).getTypeMap();\n // The filter works on the raw resolver shape; the result is a valid\n // IResolvers because it only removes entries from one.\n const filtered = filterResolversToSchema(\n typeMap,\n resolvers as Record<string, unknown>,\n logger,\n );\n return makeExecutableSchema({ typeDefs, resolvers: filtered as IResolvers });\n}\n\n/**\n * Merges multiple subgraphs into a single in-process executable schema.\n * This is the \"Guild\" (graphql-tools) composition path: instead of federation\n * (which hard-fails on cross-subgraph type conflicts and executes subgraph\n * fields over loopback HTTP), the type definitions are merged with an\n * explicit conflict policy and the resolvers run in-process.\n */\nexport function mergeSubgraphSchemas(\n subgraphs: SubgraphDefinition[],\n policy: StitchConflictPolicy,\n): GraphQLSchema {\n if (subgraphs.length === 0) {\n throw new Error(\"Cannot merge zero subgraphs\");\n }\n const { typeDefs } = mergeSubgraphTypeDefs(subgraphs, policy);\n const resolvers = mergeSubgraphResolvers(subgraphs, policy);\n return buildMergedSchema(typeDefs, resolvers);\n}\n\n/**\n * A stitching gateway adapter: the supergraph is one merged schema executed\n * in-process. Subgraph routes (`/graphql/<name>`) are served exactly like the\n * Apollo adapter, so direct subgraph access is unchanged.\n *\n * Note: unlike the Apollo adapter, the supergraph ApolloServer is NOT given\n * ApolloServerPluginDrainHttpServer. The plugin closes the (shared) HTTP\n * server on stop, which would kill every mounted subgraph route when\n * updateSupergraph() swaps the supergraph server. The shared server is drained\n * explicitly in stop() instead.\n */\nexport class StitchingGatewayAdapter implements IGatewayAdapter<Context> {\n readonly #logger: ILogger;\n readonly #policy: StitchConflictPolicy;\n\n readonly #servers: ApolloServer<Context>[] = [];\n #supergraphServer: ApolloServer<Context> | null = null;\n #handler: FetchHandler | null = null;\n #httpServer: http.Server | null = null;\n #getSubgraphs: (() => SubgraphDefinition[]) | null = null;\n #contextFactory: GatewayContextFactory<Context> | null = null;\n\n constructor(logger: ILogger, options: StitchingGatewayOptions = {}) {\n this.#logger = logger;\n this.#policy = options.onFieldTypeConflict ?? \"last\";\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 = await this.#makeServer(schema);\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 this.#httpServer = httpServer;\n this.#contextFactory = contextFactory;\n\n const server = await this.#buildSupergraphServer(getSubgraphs());\n return this.#installSupergraphServer(server);\n }\n\n async updateSupergraph(): Promise<void> {\n if (!this.#getSubgraphs || !this.#contextFactory) {\n // Not yet initialized - no-op.\n return;\n }\n const server = await this.#buildSupergraphServer(this.#getSubgraphs());\n const old = this.#supergraphServer;\n this.#installSupergraphServer(server);\n // The handler swap above is atomic: new requests use the new server,\n // in-flight requests on the old one finish normally.\n if (old) {\n await old.stop();\n }\n }\n\n attachWebSocket(\n wsServer: WebSocketServer,\n schema: GraphQLSchema,\n handlers: WsHandlers<Context>,\n ): WsDisposer {\n return useServer(\n {\n schema,\n onConnect: (ctx) =>\n handlers.onConnect(ctx.connectionParams ?? {}, ctx.extra as object),\n context: (ctx) =>\n handlers.context(ctx.connectionParams ?? {}, ctx.extra as object),\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.#handler = null;\n // Same as the Apollo adapter: clearing these makes a later\n // updateSupergraph() a no-op instead of resurrecting a stopped adapter.\n this.#getSubgraphs = null;\n this.#contextFactory = null;\n\n // Drain the shared HTTP server ourselves (the Apollo adapter gets this\n // from ApolloServerPluginDrainHttpServer, which we cannot use here - see\n // class comment). Only close a server that is still accepting connections\n // so a later close by the owner is a no-op instead of an error.\n const httpServer = this.#httpServer;\n this.#httpServer = null;\n if (httpServer?.listening) {\n await new Promise<void>((resolve) => httpServer.close(() => resolve()));\n }\n }\n\n // ── internals ──────────────────────────────────────────────────────────────\n\n async #makeServer(schema: GraphQLSchema): Promise<ApolloServer<Context>> {\n const server = new ApolloServer<Context>({\n schema,\n logger: this.#logger,\n introspection: true,\n // Same rationale as the Apollo adapter: the reactor drives shutdown\n // end-to-end; per-subgraph servers must not re-emit SIGINT.\n stopOnTerminationSignals: false,\n plugins: [\n ApolloServerPluginInlineTraceDisabled(),\n ApolloServerPluginLandingPageLocalDefault(),\n ],\n });\n await server.start();\n return server;\n }\n\n async #buildSupergraphServer(\n all: SubgraphDefinition[],\n ): Promise<ApolloServer<Context>> {\n let subgraphs = filterBuildableSubgraphs(all, this.#logger);\n if (subgraphs.length === 0 && all.length > 0) {\n // Excluding every subgraph would trade one subgraph's error for a\n // supergraph with no root type at all; keep the original set so the\n // build fails with the underlying error instead.\n this.#logger.error(\n \"Stitching gateway: every subgraph failed its isolation build; merging the unfiltered set so the underlying error surfaces\",\n );\n subgraphs = all;\n }\n const { typeDefs, conflicts } = mergeSubgraphTypeDefs(\n subgraphs,\n this.#policy,\n );\n for (const conflict of conflicts) {\n this.#logger.warn(\n `Stitching gateway (policy \"${this.#policy}\"): ${conflict}`,\n );\n }\n if (conflicts.length > 0) {\n this.#logger.warn(\n `Stitching gateway: merged ${subgraphs.length} subgraphs with ${conflicts.length} schema conflict(s); each resolution is logged above`,\n );\n } else {\n this.#logger.debug(\n `Stitching gateway: merged ${subgraphs.length} subgraphs without conflicts`,\n );\n }\n const resolvers = mergeSubgraphResolvers(subgraphs, this.#policy);\n const schema = buildMergedSchema(typeDefs, resolvers, this.#logger);\n return this.#makeServer(schema);\n }\n\n #installSupergraphServer(server: ApolloServer<Context>): FetchHandler {\n if (this.#contextFactory === null) {\n throw new Error(\"Supergraph context factory not set\");\n }\n this.#supergraphServer = server;\n const handler = createApolloFetchHandler(server, this.#contextFactory);\n this.#handler = handler;\n // The returned handler always delegates to the *current* server so\n // updateSupergraph() swaps are picked up without re-mounting the route.\n return (request: Request) => this.#handler!(request);\n }\n}\n"],"names":[],"mappings":"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","debug_id":"86579686-a510-5b69-bb5b-7db68995aac8"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
-
import {
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="82a67d04-4ad0-51b0-a91b-1d4c7e52a077")}catch(e){}}();
|
|
3
|
+
import { n as normalizePath, t as runNodeHandler } from "./run-node-handler-CQjyjOuN.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { match } from "path-to-regexp";
|
|
6
6
|
import { Readable } from "node:stream";
|
|
@@ -93,7 +93,7 @@ var ExpressHttpAdapter = class {
|
|
|
93
93
|
const matched = entry.matcher(pathname);
|
|
94
94
|
if (!matched) continue;
|
|
95
95
|
req.params = matched.params;
|
|
96
|
-
entry.handler
|
|
96
|
+
runNodeHandler(entry.handler, req, res, entry.rawBody ? void 0 : req.body);
|
|
97
97
|
return true;
|
|
98
98
|
}
|
|
99
99
|
next();
|
|
@@ -135,7 +135,7 @@ var ExpressHttpAdapter = class {
|
|
|
135
135
|
this.#routes.delete(handle);
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
|
-
async listen(port, tls) {
|
|
138
|
+
async listen(port, tls, host) {
|
|
139
139
|
let server;
|
|
140
140
|
if (tls === true) {
|
|
141
141
|
const { cert, key } = await devcert.certificateFor("localhost");
|
|
@@ -157,7 +157,7 @@ var ExpressHttpAdapter = class {
|
|
|
157
157
|
else server = http.createServer(this.#app);
|
|
158
158
|
return new Promise((resolve, reject) => {
|
|
159
159
|
server.once("error", reject);
|
|
160
|
-
server.listen(port, () => {
|
|
160
|
+
server.listen(port, host, () => {
|
|
161
161
|
server.off("error", reject);
|
|
162
162
|
resolve(server);
|
|
163
163
|
});
|
|
@@ -235,5 +235,5 @@ function createExpressHttpAdapter(existingApp) {
|
|
|
235
235
|
//#endregion
|
|
236
236
|
export { createExpressHttpAdapter };
|
|
237
237
|
|
|
238
|
-
//# sourceMappingURL=adapter-http-express-
|
|
239
|
-
//# debugId=
|
|
238
|
+
//# sourceMappingURL=adapter-http-express-CWqziB1p.mjs.map
|
|
239
|
+
//# debugId=82a67d04-4ad0-51b0-a91b-1d4c7e52a077
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-http-express-CWqziB1p.mjs","sources":["../src/graphql/gateway/adapter-http-express.ts"],"sourcesContent":["import bodyParser from \"body-parser\";\nimport cors from \"cors\";\nimport type { CorsOptions } from \"cors\";\nimport devcert from \"devcert\";\nimport type express from \"express\";\nimport type { Express } from \"express\";\nimport { Router } from \"express\";\nimport expressLib from \"express\";\nimport type { IRouter } from \"express\";\nimport fs from \"node:fs\";\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport path from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport type { ReadableStream as WebReadableStream } from \"node:stream/web\";\nimport { match, type MatchFunction, type ParamData } from \"path-to-regexp\";\nimport type {\n FetchHandler,\n HttpMethod,\n IHttpAdapter,\n NodeRouteOptions,\n AdapterRouteHandle,\n TlsOptions,\n} from \"./types.js\";\nimport { normalizePath } from \"./path-normalize.js\";\nimport { runNodeHandler, type NodeHandler } from \"./run-node-handler.js\";\n\ntype GetHandler = (r: Request) => Response | Promise<Response>;\n\ntype RouteEntry =\n | {\n kind: \"fetch\";\n path: string;\n matcher: MatchFunction<ParamData>;\n prefix: boolean;\n handler: FetchHandler;\n }\n | {\n kind: \"get\";\n path: string;\n matcher: MatchFunction<ParamData>;\n handler: GetHandler;\n }\n | {\n kind: \"node\";\n method: string;\n path: string;\n matcher: MatchFunction<ParamData>;\n handler: NodeHandler;\n /** Dispatched ahead of the body parsers, with the stream unread. */\n rawBody: boolean;\n };\n\nexport class ExpressHttpAdapter implements IHttpAdapter {\n readonly #app: Express;\n readonly #router: IRouter;\n\n /**\n * Every route lives in this registry and is served by one\n * permanently-registered dispatcher (installed by setupMiddleware), because\n * Express 4 has no route-removal API: routes registered directly on the\n * app or router can never be taken back. Entries iterate in registration\n * order; mounting a path that already holds a route of the same kind (and\n * method, for node routes) replaces the old entry, so re-mounting is\n * last-write-wins without accumulating.\n */\n readonly #routes = new Map<number, RouteEntry>();\n #nextHandle = 0;\n\n constructor(existingApp?: Express) {\n this.#app = existingApp ?? expressLib();\n this.#router = Router();\n this.#app.use(this.#router);\n }\n\n setupSentryErrorHandler(sentry: object): void {\n const s = sentry as {\n setupExpressErrorHandler(app: Express): void;\n };\n s.setupExpressErrorHandler(this.#app);\n }\n\n get handle(): unknown {\n return this.#app;\n }\n\n mountRawMiddleware(middleware: unknown): void {\n // Connect/Express middleware shape; the caller owns its typing.\n this.#app.use(middleware as express.RequestHandler);\n }\n\n mountNodeRoute(\n method: HttpMethod,\n path: string,\n handler: (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n body?: unknown,\n ) => void | Promise<void>,\n { rawBody = false, prefix = false }: NodeRouteOptions = {},\n ): AdapterRouteHandle {\n this.#replaceDuplicates(\"node\", path, method.toUpperCase());\n const handle = this.#nextHandle++;\n this.#routes.set(handle, {\n kind: \"node\",\n method: method.toUpperCase(),\n path,\n matcher: match(normalizePath(path), { end: !prefix }),\n rawBody,\n handler,\n });\n return { dispose: () => this.#routes.delete(handle) };\n }\n\n setupMiddleware({\n corsOptions,\n bodyLimit = \"50mb\",\n }: {\n corsOptions?: CorsOptions;\n bodyLimit?: string;\n }): void {\n this.#router.use(cors(corsOptions));\n\n // Ahead of the parsers: a raw route's whole point is that nothing has read\n // the stream. Behind them, `req` is a consumed stream and only a\n // re-encoded `req.body` survives — which cannot carry a signature over the\n // octets the client actually sent.\n this.#router.use((req, res, next) => {\n this.#dispatchNodeRoute(req, res, next, true);\n });\n\n this.#router.use(bodyParser.json({ limit: bodyLimit }));\n this.#router.use(\n bodyParser.urlencoded({ extended: true, limit: bodyLimit }),\n );\n\n // The single dispatch point for every registered route, registered\n // AFTER the body parsers so node routes receive a parsed req.body.\n // A request matching no route falls through to app-level middleware\n // (mountRawMiddleware) and then Express's default 404.\n this.#router.use((req, res, next) => {\n const pathname = req.path;\n\n // Node routes first, then GET routes, then Fetch mounts — the same\n // order the Fastify adapter uses. A node route is the most specific\n // registration there is (method plus exact path), while a Fetch mount is\n // often a prefix; letting the prefix win would silently shadow an exact\n // route, and would do it in only one of the two adapters.\n if (this.#dispatchNodeRoute(req, res, () => undefined, false)) return;\n\n if (req.method === \"GET\" || req.method === \"HEAD\") {\n for (const entry of this.#routes.values()) {\n if (entry.kind !== \"get\" || !entry.matcher(pathname)) continue;\n this.#serveGetEntry(entry, req, res);\n return;\n }\n }\n\n for (const entry of this.#routes.values()) {\n if (entry.kind !== \"fetch\" || !entry.matcher(pathname)) continue;\n this.#serveFetchHandler(entry.handler, req, res, next);\n return;\n }\n\n next();\n });\n }\n\n /**\n * Serves the first node route matching this request, or calls `next()`.\n * `raw` selects which half of the registry to consider, so the two passes —\n * one before the body parsers, one after — never serve the same route twice.\n */\n #dispatchNodeRoute(\n req: express.Request,\n res: express.Response,\n next: express.NextFunction,\n raw: boolean,\n ): boolean {\n const pathname = req.path;\n for (const entry of this.#routes.values()) {\n if (entry.kind !== \"node\") continue;\n if (entry.rawBody !== raw) continue;\n if (entry.method !== req.method) continue;\n const matched = entry.matcher(pathname);\n if (!matched) continue;\n req.params = matched.params as Record<string, string>;\n runNodeHandler(\n entry.handler,\n req,\n res,\n entry.rawBody ? undefined : req.body,\n );\n return true;\n }\n next();\n return false;\n }\n\n mount(\n path: string,\n handler: FetchHandler,\n { prefix, exact }: { prefix?: boolean; exact?: boolean } = {},\n ): AdapterRouteHandle {\n // `exact` never meant exact: it selected prefix matching. Kept as an alias\n // so existing callers keep working while the name is retired.\n const asPrefix = prefix ?? exact ?? false;\n this.#replaceDuplicates(\"fetch\", path);\n const handle = this.#nextHandle++;\n this.#routes.set(handle, {\n kind: \"fetch\",\n path,\n matcher: match(normalizePath(path), { end: !asPrefix }),\n prefix: asPrefix,\n handler,\n });\n return { dispose: () => this.#routes.delete(handle) };\n }\n\n getRoute(\n path: string,\n handler: (request: Request) => Response | Promise<Response>,\n ): AdapterRouteHandle {\n this.#replaceDuplicates(\"get\", path);\n const handle = this.#nextHandle++;\n this.#routes.set(handle, {\n kind: \"get\",\n path,\n matcher: match(normalizePath(path)),\n handler,\n });\n return { dispose: () => this.#routes.delete(handle) };\n }\n\n /**\n * Drop any existing entry the new registration would duplicate (same kind\n * and path, plus method for node routes) so a re-mount replaces instead of\n * shadowing. Map iteration tolerates concurrent deletion.\n */\n #replaceDuplicates(\n kind: RouteEntry[\"kind\"],\n path: string,\n method?: string,\n ): void {\n for (const [handle, entry] of this.#routes) {\n if (entry.kind !== kind || entry.path !== path) continue;\n if (kind === \"node\" && entry.kind === \"node\" && entry.method !== method) {\n continue;\n }\n this.#routes.delete(handle);\n }\n }\n\n async listen(\n port: number,\n tls?: TlsOptions,\n host?: string,\n ): Promise<http.Server> {\n let server: http.Server;\n\n if (tls === true) {\n const { cert, key } = (await devcert.certificateFor(\"localhost\")) as {\n cert: Buffer;\n key: Buffer;\n };\n if (!cert || !key) {\n throw new Error(\"Invalid certificate generated\");\n }\n server = https.createServer({ cert, key }, this.#app);\n } else if (tls && \"keyPath\" in tls) {\n const currentDir = process.cwd();\n server = https.createServer(\n {\n key: fs.readFileSync(path.join(currentDir, tls.keyPath)),\n cert: fs.readFileSync(path.join(currentDir, tls.certPath)),\n },\n this.#app,\n );\n } else if (tls && \"cert\" in tls) {\n server = https.createServer({ cert: tls.cert, key: tls.key }, this.#app);\n } else {\n server = http.createServer(this.#app);\n }\n\n return new Promise<http.Server>((resolve, reject) => {\n server.once(\"error\", reject);\n server.listen(port, host, () => {\n server.off(\"error\", reject);\n resolve(server);\n });\n });\n }\n\n #serveFetchHandler(\n handler: FetchHandler,\n req: express.Request,\n res: express.Response,\n next: express.NextFunction,\n ): void {\n // Build the full URL for the Fetch Request\n const protocol = req.protocol;\n const host = req.get(\"host\") ?? \"localhost\";\n const url = `${protocol}://${host}${req.originalUrl}`;\n\n // Convert Node.js incoming headers to Fetch Headers\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers.set(key, value);\n } else if (Array.isArray(value)) {\n headers.set(key, value.join(\", \"));\n }\n }\n\n // bodyParser has already run, so req.body is a parsed JS value.\n // Re-serialize it so the Fetch Request body stream is readable.\n let body: string | undefined;\n if (\n req.method !== \"GET\" &&\n req.method !== \"HEAD\" &&\n req.body !== undefined\n ) {\n body = JSON.stringify(req.body);\n }\n\n const fetchRequest = new Request(url, {\n method: req.method,\n headers,\n body,\n });\n\n Promise.resolve(handler(fetchRequest))\n .then(async (response) => {\n res.status(response.status);\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n if (response.body === null) {\n res.send(await response.text());\n return;\n }\n // Stream the body to the socket instead of awaiting .text(): a\n // stream-backed body (e.g. graphql-sse) only closes when the\n // subscription completes, so awaiting would hang the request.\n // `response.body` is typed against the global (lib) ReadableStream\n // declaration, while Readable.fromWeb wants node:stream/web's; at\n // runtime they are the same stream, so the cast is safe.\n const bodyStream = response.body as WebReadableStream;\n const nodeStream = Readable.fromWeb(bodyStream);\n req.on(\"close\", () => {\n // The client went away mid-stream; stop pulling from the source.\n nodeStream.destroy();\n });\n nodeStream.on(\"error\", (err) => {\n if (res.headersSent) {\n // The response has already started; nothing left to say.\n res.destroy();\n } else {\n next(err);\n }\n });\n nodeStream.pipe(res);\n })\n .catch(next);\n }\n\n #serveGetEntry(\n entry: Extract<RouteEntry, { kind: \"get\" }>,\n req: express.Request,\n res: express.Response,\n ): void {\n const protocol = req.protocol;\n const host = req.get(\"host\") ?? \"localhost\";\n const url = `${protocol}://${host}${req.originalUrl}`;\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers.set(key, value);\n } else if (Array.isArray(value)) {\n headers.set(key, value.join(\", \"));\n }\n }\n // GET is forced (matching the pre-refactor behavior, which also\n // normalized HEAD requests through the GET route handler).\n const fetchRequest = new Request(url, { method: \"GET\", headers });\n Promise.resolve(entry.handler(fetchRequest))\n .then(async (response) => {\n res.status(response.status);\n response.headers.forEach((value, key) => {\n res.setHeader(key, value);\n });\n if (response.body === null) {\n res.send(await response.text());\n return;\n }\n // Stream the body to the socket instead of awaiting .text(): a\n // stream-backed body (e.g. graphql-sse) only closes when the\n // subscription completes, so awaiting would hang the request.\n // `response.body` is typed against the global (lib) ReadableStream\n // declaration, while Readable.fromWeb wants node:stream/web's; at\n // runtime they are the same stream, so the cast is safe.\n const bodyStream = response.body as WebReadableStream;\n const nodeStream = Readable.fromWeb(bodyStream);\n req.on(\"close\", () => {\n // The client went away mid-stream; stop pulling from the source.\n nodeStream.destroy();\n });\n nodeStream.on(\"error\", (err) => {\n if (res.headersSent) {\n // The response has already started; nothing left to say.\n res.destroy();\n } else {\n res.status(500).send(String(err));\n }\n });\n nodeStream.pipe(res);\n })\n .catch((err: unknown) => {\n res.status(500).send(String(err));\n });\n }\n}\n\nexport function createExpressHttpAdapter(existingApp?: Express): {\n adapter: IHttpAdapter;\n} {\n return { adapter: new ExpressHttpAdapter(existingApp) };\n}\n"],"names":[],"mappings":"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","debug_id":"82a67d04-4ad0-51b0-a91b-1d4c7e52a077"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
|
-
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="
|
|
3
|
-
import {
|
|
2
|
+
!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof globalThis?globalThis:"undefined"!=typeof self?self:{},n=(new e.Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="3202957f-be6f-596c-8dfa-4dfb9f7f88b2")}catch(e){}}();
|
|
3
|
+
import { n as normalizePath, t as runNodeHandler } from "./run-node-handler-CQjyjOuN.mjs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { match } from "path-to-regexp";
|
|
6
6
|
import { Readable } from "node:stream";
|
|
@@ -87,7 +87,7 @@ var FastifyHttpAdapter = class {
|
|
|
87
87
|
if (!this.#instance) return;
|
|
88
88
|
sentry.setupFastifyErrorHandler(this.#instance);
|
|
89
89
|
}
|
|
90
|
-
async listen(port, tls) {
|
|
90
|
+
async listen(port, tls, host) {
|
|
91
91
|
let httpServer;
|
|
92
92
|
if (tls === true) {
|
|
93
93
|
const { cert, key } = await devcert.certificateFor("localhost");
|
|
@@ -107,12 +107,13 @@ var FastifyHttpAdapter = class {
|
|
|
107
107
|
key: tls.key
|
|
108
108
|
});
|
|
109
109
|
else httpServer = http.createServer();
|
|
110
|
+
const bodyLimit = this.#setupOps.find((op) => op.kind === "cors")?.bodyLimit ?? 52428800;
|
|
110
111
|
const instance = Fastify({
|
|
111
112
|
serverFactory: (handler) => {
|
|
112
113
|
httpServer.on("request", handler);
|
|
113
114
|
return httpServer;
|
|
114
115
|
},
|
|
115
|
-
bodyLimit
|
|
116
|
+
bodyLimit,
|
|
116
117
|
logger: false
|
|
117
118
|
});
|
|
118
119
|
this.#instance = instance;
|
|
@@ -128,7 +129,7 @@ var FastifyHttpAdapter = class {
|
|
|
128
129
|
}
|
|
129
130
|
req.raw.params = claimed.params;
|
|
130
131
|
reply.hijack();
|
|
131
|
-
claimed.route.handler
|
|
132
|
+
runNodeHandler(claimed.route.handler, req.raw, reply.raw, void 0);
|
|
132
133
|
});
|
|
133
134
|
instance.route({
|
|
134
135
|
method: [
|
|
@@ -145,7 +146,7 @@ var FastifyHttpAdapter = class {
|
|
|
145
146
|
await instance.ready();
|
|
146
147
|
return new Promise((resolve, reject) => {
|
|
147
148
|
httpServer.once("error", reject);
|
|
148
|
-
httpServer.listen(port, () => {
|
|
149
|
+
httpServer.listen(port, host, () => {
|
|
149
150
|
httpServer.off("error", reject);
|
|
150
151
|
resolve(httpServer);
|
|
151
152
|
});
|
|
@@ -176,7 +177,7 @@ var FastifyHttpAdapter = class {
|
|
|
176
177
|
if (node) {
|
|
177
178
|
req.raw.params = node.params;
|
|
178
179
|
reply.hijack();
|
|
179
|
-
node.route.handler
|
|
180
|
+
runNodeHandler(node.route.handler, req.raw, reply.raw, req.body);
|
|
180
181
|
return;
|
|
181
182
|
}
|
|
182
183
|
if (method === "GET") {
|
|
@@ -239,5 +240,5 @@ function createFastifyHttpAdapter() {
|
|
|
239
240
|
//#endregion
|
|
240
241
|
export { createFastifyHttpAdapter };
|
|
241
242
|
|
|
242
|
-
//# sourceMappingURL=adapter-http-fastify-
|
|
243
|
-
//# debugId=
|
|
243
|
+
//# sourceMappingURL=adapter-http-fastify-CYZQ6FFv.mjs.map
|
|
244
|
+
//# debugId=3202957f-be6f-596c-8dfa-4dfb9f7f88b2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-http-fastify-CYZQ6FFv.mjs","sources":["../src/graphql/gateway/adapter-http-fastify.ts"],"sourcesContent":["import fastifyCors from \"@fastify/cors\";\nimport fastifyFormbody from \"@fastify/formbody\";\nimport fastifyMiddie from \"@fastify/middie\";\nimport type { CorsOptions } from \"cors\";\nimport devcert from \"devcert\";\nimport Fastify from \"fastify\";\nimport type { FastifyInstance, FastifyReply, FastifyRequest } from \"fastify\";\nimport fs from \"node:fs\";\nimport http from \"node:http\";\nimport https from \"node:https\";\nimport nodePath from \"node:path\";\nimport { Readable } from \"node:stream\";\nimport type { ReadableStream as WebReadableStream } from \"node:stream/web\";\nimport { match, type MatchFunction, type ParamData } from \"path-to-regexp\";\nimport type {\n FetchHandler,\n HttpMethod,\n IHttpAdapter,\n NodeRouteOptions,\n AdapterRouteHandle,\n TlsOptions,\n} from \"./types.js\";\nimport { normalizePath } from \"./path-normalize.js\";\nimport { runNodeHandler, type NodeHandler } from \"./run-node-handler.js\";\n\n/** Parses body-limit strings like \"50mb\" to bytes. */\nfunction parseBodyLimit(limit: string): number {\n const m = /^(\\d+(?:\\.\\d+)?)\\s*(b|kb|mb|gb)?$/i.exec(limit.trim());\n if (!m) return 52_428_800; // 50 MB fallback\n const n = parseFloat(m[1]);\n const units: Record<string, number> = {\n b: 1,\n kb: 1_024,\n mb: 1_048_576,\n gb: 1_073_741_824,\n };\n return Math.round(n * (units[(m[2] ?? \"b\").toLowerCase()] ?? 1));\n}\n\ntype FetchEntry = {\n path: string;\n handler: FetchHandler;\n matcher: MatchFunction<ParamData>;\n prefix: boolean;\n};\n\ntype GetEntry = {\n path: string;\n handler: (r: Request) => Response | Promise<Response>;\n matcher: MatchFunction<ParamData>;\n};\n\ntype NodeEntry = {\n path: string;\n method: HttpMethod;\n matcher: MatchFunction<ParamData>;\n handler: NodeHandler;\n /** Claimed in the onRequest hook, before any body parsing. */\n rawBody: boolean;\n};\n\n// Pre-listen configuration ops (need the Fastify instance to apply).\ntype SetupOp =\n | { kind: \"cors\"; options?: CorsOptions; bodyLimit: number }\n | { kind: \"middie\"; middleware: unknown };\n\nexport class FastifyHttpAdapter implements IHttpAdapter {\n // Dispatch maps, keyed by route handle and iterated in registration\n // order — populated at any time (before or after listen). Mounting a\n // path that already holds a route of the same kind (and method, for\n // node routes) replaces the old entry, so re-mounting is last-write-\n // wins without accumulating; a handle's dispose() removes its own entry.\n readonly #fetchRoutes = new Map<number, FetchEntry>();\n readonly #getRoutes = new Map<number, GetEntry>();\n readonly #nodeRoutes = new Map<number, NodeEntry>();\n #nextHandle = 0;\n\n // Ops that need the Fastify instance (CORS config, Connect middleware).\n readonly #setupOps: SetupOp[] = [];\n\n #instance: FastifyInstance | undefined;\n\n get handle(): unknown {\n return this.#instance;\n }\n\n setupMiddleware({\n corsOptions,\n bodyLimit = \"50mb\",\n }: {\n corsOptions?: CorsOptions;\n bodyLimit?: string;\n }): void {\n this.#setupOps.push({\n kind: \"cors\",\n options: corsOptions,\n bodyLimit: parseBodyLimit(bodyLimit),\n });\n }\n\n mount(\n path: string,\n handler: FetchHandler,\n { prefix, exact }: { prefix?: boolean; exact?: boolean } = {},\n ): AdapterRouteHandle {\n // `exact` never meant exact: it selected prefix matching. Kept as an alias\n // so existing callers keep working while the name is retired.\n const asPrefix = prefix ?? exact ?? false;\n for (const [handle, entry] of this.#fetchRoutes) {\n if (entry.path === path) this.#fetchRoutes.delete(handle);\n }\n const handle = this.#nextHandle++;\n this.#fetchRoutes.set(handle, {\n path,\n handler,\n matcher: match(normalizePath(path), { end: !asPrefix }),\n prefix: asPrefix,\n });\n return { dispose: () => this.#fetchRoutes.delete(handle) };\n }\n\n getRoute(\n path: string,\n handler: (r: Request) => Response | Promise<Response>,\n ): AdapterRouteHandle {\n for (const [handle, entry] of this.#getRoutes) {\n if (entry.path === path) this.#getRoutes.delete(handle);\n }\n const handle = this.#nextHandle++;\n this.#getRoutes.set(handle, {\n path,\n handler,\n matcher: match(normalizePath(path)),\n });\n return { dispose: () => this.#getRoutes.delete(handle) };\n }\n\n mountNodeRoute(\n method: HttpMethod,\n path: string,\n handler: NodeHandler,\n { rawBody = false, prefix = false }: NodeRouteOptions = {},\n ): AdapterRouteHandle {\n for (const [handle, entry] of this.#nodeRoutes) {\n if (entry.path === path && entry.method === method) {\n this.#nodeRoutes.delete(handle);\n }\n }\n const handle = this.#nextHandle++;\n this.#nodeRoutes.set(handle, {\n path,\n method,\n matcher: match(normalizePath(path), { end: !prefix }),\n handler,\n rawBody,\n });\n return { dispose: () => this.#nodeRoutes.delete(handle) };\n }\n\n mountRawMiddleware(middleware: unknown): void {\n if (this.#instance) {\n // @fastify/middie is always registered, so .use() is available post-listen.\n (\n this.#instance as FastifyInstance & {\n use(middleware: unknown): FastifyInstance;\n }\n ).use(middleware);\n } else {\n this.#setupOps.push({ kind: \"middie\", middleware });\n }\n }\n\n setupSentryErrorHandler(sentry: object): void {\n if (!this.#instance) return;\n const s = sentry as {\n setupFastifyErrorHandler(app: FastifyInstance): void;\n };\n s.setupFastifyErrorHandler(this.#instance);\n }\n\n async listen(\n port: number,\n tls?: TlsOptions,\n host?: string,\n ): Promise<http.Server> {\n let httpServer: http.Server;\n\n if (tls === true) {\n const { cert, key } = (await devcert.certificateFor(\"localhost\")) as {\n cert: Buffer;\n key: Buffer;\n };\n if (!cert || !key) throw new Error(\"Invalid certificate generated\");\n httpServer = https.createServer({ cert, key });\n } else if (tls && \"keyPath\" in tls) {\n const cwd = process.cwd();\n httpServer = https.createServer({\n key: fs.readFileSync(nodePath.join(cwd, tls.keyPath)),\n cert: fs.readFileSync(nodePath.join(cwd, tls.certPath)),\n });\n } else if (tls && \"cert\" in tls) {\n httpServer = https.createServer({ cert: tls.cert, key: tls.key });\n } else {\n httpServer = http.createServer();\n }\n\n const corsOp = this.#setupOps.find(\n (op): op is Extract<SetupOp, { kind: \"cors\" }> => op.kind === \"cors\",\n );\n const bodyLimit = corsOp?.bodyLimit ?? 52_428_800;\n\n const instance = Fastify({\n serverFactory: (handler) => {\n httpServer.on(\"request\", handler);\n return httpServer;\n },\n bodyLimit,\n logger: false,\n });\n\n this.#instance = instance;\n\n // Always register middie first so .use() is available post-listen.\n await instance.register(fastifyMiddie);\n await instance.register(fastifyFormbody);\n\n for (const op of this.#setupOps) {\n if (op.kind === \"cors\") {\n await instance.register(\n fastifyCors,\n op.options as Parameters<typeof fastifyCors>[1],\n );\n } else {\n (\n instance as FastifyInstance & {\n use(middleware: unknown): FastifyInstance;\n }\n ).use(op.middleware);\n }\n }\n\n // Raw routes are claimed here, in the earliest hook there is: Fastify\n // parses the body before the handler runs, and it 415s an unknown content\n // type outright, so a route that needs the octets as sent cannot wait for\n // dispatch. `hijack()` takes the reply out of Fastify's lifecycle and the\n // handler owns the socket from here.\n instance.addHook(\"onRequest\", (req, reply, done) => {\n const claimed = this.#matchNodeRoute(req, true);\n if (!claimed) {\n done();\n return;\n }\n (req.raw as http.IncomingMessage & { params?: ParamData }).params =\n claimed.params;\n reply.hijack();\n runNodeHandler(claimed.route.handler, req.raw, reply.raw, undefined);\n });\n\n // Single catch-all route — all dispatching is done via the Maps above so\n // that routes registered after listen() are picked up automatically.\n // OPTIONS is excluded because @fastify/cors registers its own OPTIONS /*\n // handler for preflight; including it here would cause a duplicate-route\n // conflict at startup.\n instance.route({\n method: [\"DELETE\", \"GET\", \"HEAD\", \"PATCH\", \"POST\", \"PUT\"],\n url: \"/*\",\n handler: (req, reply) => this.#dispatch(req, reply),\n });\n\n await instance.ready();\n\n return new Promise<http.Server>((resolve, reject) => {\n httpServer.once(\"error\", reject);\n httpServer.listen(port, host, () => {\n httpServer.off(\"error\", reject);\n resolve(httpServer);\n });\n });\n }\n\n /**\n * Finds a node route for this request. `raw` selects which half of the\n * registry to consider: raw routes are claimed in the onRequest hook, the\n * rest in the catch-all handler once the body is parsed.\n */\n #matchNodeRoute(\n req: FastifyRequest,\n raw: boolean,\n ): { route: NodeEntry; params: ParamData } | undefined {\n const pathname = new URL(req.url, \"http://localhost\").pathname;\n const method = req.method.toUpperCase();\n for (const entry of this.#nodeRoutes.values()) {\n if (entry.rawBody !== raw) continue;\n if (entry.method !== method) continue;\n const result = entry.matcher(pathname);\n if (result) return { route: entry, params: result.params };\n }\n return undefined;\n }\n\n #dispatch(\n req: FastifyRequest,\n reply: FastifyReply,\n ): void | Promise<FastifyReply> {\n const pathname = new URL(req.url, \"http://localhost\").pathname;\n const method = req.method.toUpperCase() as\n | \"DELETE\"\n | \"GET\"\n | \"HEAD\"\n | \"PATCH\"\n | \"POST\"\n | \"PUT\";\n\n // 1. Node routes — path-to-regexp match + method, handler manages raw\n // response. Attach decoded params onto `req.raw` so downstream handlers\n // can read them via the same `req.params` API the Express adapter exposes.\n const node = this.#matchNodeRoute(req, false);\n if (node) {\n (req.raw as http.IncomingMessage & { params?: ParamData }).params =\n node.params;\n reply.hijack();\n runNodeHandler(node.route.handler, req.raw, reply.raw, req.body);\n return;\n }\n\n // 2. GET-specific routes (health, explorer, etc.).\n if (method === \"GET\") {\n for (const entry of this.#getRoutes.values()) {\n if (entry.matcher(pathname)) {\n return this.#serveGetEntry(entry, req, reply);\n }\n }\n }\n\n // 3. Fetch routes (GraphQL handlers, SSE, etc.).\n // Iterate in reverse so that the last-mounted handler wins when the same\n // path is mounted more than once (e.g. supergraph remount after reload).\n for (const entry of [...this.#fetchRoutes.values()].reverse()) {\n if (entry.matcher(pathname)) {\n return serveFetchHandler(entry.handler, req, reply);\n }\n }\n\n void reply.status(404).send({\n message: `Route ${req.method}:${pathname} not found`,\n error: \"Not Found\",\n statusCode: 404,\n });\n }\n\n async #serveGetEntry(\n entry: GetEntry,\n req: FastifyRequest,\n reply: FastifyReply,\n ): Promise<FastifyReply> {\n const url = buildUrl(req);\n const headers = buildHeaders(req);\n const fetchReq = new Request(url, { method: \"GET\", headers });\n const response = await entry.handler(fetchReq);\n writeResponse(reply, response);\n if (response.body === null) {\n return reply.send(await response.text());\n }\n // Stream the body instead of awaiting .text(): a stream-backed body\n // (e.g. graphql-sse) only closes when the subscription completes, so\n // awaiting would hang the request. Fastify tears the source down on\n // client disconnect and routes pre-header stream errors to the\n // error handler.\n // `response.body` is typed against the global (lib) ReadableStream\n // declaration, while Readable.fromWeb wants node:stream/web's; at\n // runtime they are the same stream, so the cast is safe.\n const bodyStream = response.body as WebReadableStream;\n return reply.send(Readable.fromWeb(bodyStream));\n }\n}\n\nasync function serveFetchHandler(\n handler: FetchHandler,\n req: FastifyRequest,\n reply: FastifyReply,\n): Promise<FastifyReply> {\n const url = buildUrl(req);\n const headers = buildHeaders(req);\n let body: string | undefined;\n if (req.method !== \"GET\" && req.method !== \"HEAD\" && req.body !== undefined) {\n body = JSON.stringify(req.body);\n }\n const response = await handler(\n new Request(url, { method: req.method, headers, body }),\n );\n writeResponse(reply, response);\n if (response.body === null) {\n return reply.send(await response.text());\n }\n // Stream the body instead of awaiting .text(): a stream-backed body\n // (e.g. graphql-sse) only closes when the subscription completes, so\n // awaiting would hang the request. Fastify tears the source down on\n // client disconnect and routes pre-header stream errors to the\n // error handler.\n // `response.body` is typed against the global (lib) ReadableStream\n // declaration, while Readable.fromWeb wants node:stream/web's; at\n // runtime they are the same stream, so the cast is safe.\n const bodyStream = response.body as WebReadableStream;\n return reply.send(Readable.fromWeb(bodyStream));\n}\n\nfunction buildUrl(req: FastifyRequest): string {\n const protocol = req.protocol;\n const host = req.headers.host ?? \"localhost\";\n return `${protocol}://${host}${req.url}`;\n}\n\nfunction buildHeaders(req: FastifyRequest): Headers {\n const headers = new Headers();\n for (const [key, value] of Object.entries(req.headers)) {\n if (typeof value === \"string\") {\n headers.set(key, value);\n } else if (Array.isArray(value)) {\n headers.set(key, value.join(\", \"));\n }\n }\n return headers;\n}\n\nfunction writeResponse(reply: FastifyReply, response: Response): void {\n reply.statusCode = response.status;\n response.headers.forEach((value, key) => {\n reply.header(key, value);\n });\n}\n\nexport function createFastifyHttpAdapter(): { adapter: IHttpAdapter } {\n return { adapter: new FastifyHttpAdapter() };\n}\n"],"names":["nodePath"],"mappings":"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","debug_id":"3202957f-be6f-596c-8dfa-4dfb9f7f88b2"}
|