@powerhousedao/reactor-api 6.0.2-staging.8 → 6.1.0-dev.0
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-IZuGzFaY.mjs +134 -0
- package/dist/adapter-gateway-apollo-IZuGzFaY.mjs.map +1 -0
- package/dist/adapter-gateway-mercurius-CLmTtNJC.mjs +132 -0
- package/dist/adapter-gateway-mercurius-CLmTtNJC.mjs.map +1 -0
- package/dist/adapter-http-express-DO4ui7AV.mjs +135 -0
- package/dist/adapter-http-express-DO4ui7AV.mjs.map +1 -0
- package/dist/adapter-http-fastify-B248RJtm.mjs +208 -0
- package/dist/adapter-http-fastify-B248RJtm.mjs.map +1 -0
- package/dist/index.d.mts +68 -17
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +268 -733
- package/dist/index.mjs.map +1 -1
- package/dist/src/packages/vite-loader.d.mts +1 -1
- package/dist/src/packages/vite-loader.mjs +1 -1
- package/dist/{types-Do4QTfT3.d.mts → types-DnHgkahK.d.mts} +1 -1
- package/dist/{types-Do4QTfT3.d.mts.map → types-DnHgkahK.d.mts.map} +1 -1
- package/dist/{utils-CHCRSWig.mjs → utils-BFkbSO_H.mjs} +13 -6
- package/dist/utils-BFkbSO_H.mjs.map +1 -0
- package/dist/websocket-DncAB9XK.mjs +18 -0
- package/dist/websocket-DncAB9XK.mjs.map +1 -0
- package/package.json +53 -21
- package/dist/utils-CHCRSWig.mjs.map +0 -1
|
@@ -0,0 +1,134 @@
|
|
|
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]="7f597280-1b64-50b2-9735-754051857add")}catch(e){}}();
|
|
3
|
+
import { t as useServer } from "./websocket-DncAB9XK.mjs";
|
|
4
|
+
import { ApolloGateway, LocalCompose, RemoteGraphQLDataSource } from "@apollo/gateway";
|
|
5
|
+
import { ApolloServer, HeaderMap } from "@apollo/server";
|
|
6
|
+
import { ApolloServerPluginInlineTraceDisabled } from "@apollo/server/plugin/disabled";
|
|
7
|
+
import { ApolloServerPluginDrainHttpServer } from "@apollo/server/plugin/drainHttpServer";
|
|
8
|
+
import { ApolloServerPluginLandingPageLocalDefault } from "@apollo/server/plugin/landingPage/default";
|
|
9
|
+
//#region src/graphql/gateway/adapter-gateway-apollo.ts
|
|
10
|
+
var AuthenticatedDataSource = class extends RemoteGraphQLDataSource {
|
|
11
|
+
willSendRequest(options) {
|
|
12
|
+
const { authorization } = options.context.headers;
|
|
13
|
+
if (authorization && options?.request.http) options.request.http.headers.set("authorization", authorization);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
var ApolloGatewayAdapter = class {
|
|
17
|
+
#logger;
|
|
18
|
+
#servers = [];
|
|
19
|
+
#supergraphServer = null;
|
|
20
|
+
#gatewayOptions = null;
|
|
21
|
+
#getSubgraphs = null;
|
|
22
|
+
constructor(logger) {
|
|
23
|
+
this.#logger = logger;
|
|
24
|
+
}
|
|
25
|
+
async start(_httpServer) {}
|
|
26
|
+
async createHandler(schema, contextFactory) {
|
|
27
|
+
const server = new ApolloServer({
|
|
28
|
+
schema,
|
|
29
|
+
logger: this.#logger,
|
|
30
|
+
introspection: true,
|
|
31
|
+
stopOnTerminationSignals: false,
|
|
32
|
+
plugins: [ApolloServerPluginInlineTraceDisabled(), ApolloServerPluginLandingPageLocalDefault()]
|
|
33
|
+
});
|
|
34
|
+
await server.start();
|
|
35
|
+
this.#servers.push(server);
|
|
36
|
+
return createApolloFetchHandler(server, contextFactory);
|
|
37
|
+
}
|
|
38
|
+
async createSupergraphHandler(getSubgraphs, httpServer, contextFactory) {
|
|
39
|
+
if (this.#supergraphServer) throw new Error("Supergraph server is already running");
|
|
40
|
+
this.#getSubgraphs = getSubgraphs;
|
|
41
|
+
this.#supergraphServer = new ApolloServer({
|
|
42
|
+
gateway: new ApolloGateway({
|
|
43
|
+
supergraphSdl: async (options) => {
|
|
44
|
+
this.#gatewayOptions = options;
|
|
45
|
+
return await this.#buildSupergraphSdl();
|
|
46
|
+
},
|
|
47
|
+
buildService: (serviceConfig) => new AuthenticatedDataSource(serviceConfig)
|
|
48
|
+
}),
|
|
49
|
+
logger: this.#logger,
|
|
50
|
+
introspection: true,
|
|
51
|
+
stopOnTerminationSignals: false,
|
|
52
|
+
plugins: [
|
|
53
|
+
ApolloServerPluginDrainHttpServer({ httpServer }),
|
|
54
|
+
ApolloServerPluginInlineTraceDisabled(),
|
|
55
|
+
ApolloServerPluginLandingPageLocalDefault()
|
|
56
|
+
]
|
|
57
|
+
});
|
|
58
|
+
await this.#supergraphServer.start();
|
|
59
|
+
return createApolloFetchHandler(this.#supergraphServer, contextFactory);
|
|
60
|
+
}
|
|
61
|
+
async updateSupergraph() {
|
|
62
|
+
if (!this.#gatewayOptions || !this.#getSubgraphs) return;
|
|
63
|
+
const { supergraphSdl } = await this.#buildSupergraphSdl();
|
|
64
|
+
this.#gatewayOptions.update(supergraphSdl);
|
|
65
|
+
}
|
|
66
|
+
async #buildSupergraphSdl() {
|
|
67
|
+
if (!this.#gatewayOptions || !this.#getSubgraphs) throw new Error("Gateway is not ready");
|
|
68
|
+
return new LocalCompose({ localServiceList: this.#getSubgraphs().map((s) => ({
|
|
69
|
+
name: s.name,
|
|
70
|
+
typeDefs: s.typeDefs,
|
|
71
|
+
url: s.url
|
|
72
|
+
})) }).initialize(this.#gatewayOptions);
|
|
73
|
+
}
|
|
74
|
+
attachWebSocket(wsServer, schema, contextFactory) {
|
|
75
|
+
return useServer({
|
|
76
|
+
schema,
|
|
77
|
+
context: async (ctx) => {
|
|
78
|
+
return contextFactory(ctx.connectionParams ?? {});
|
|
79
|
+
}
|
|
80
|
+
}, wsServer);
|
|
81
|
+
}
|
|
82
|
+
async stop() {
|
|
83
|
+
await Promise.all(this.#servers.map((s) => s.stop()));
|
|
84
|
+
this.#servers.length = 0;
|
|
85
|
+
if (this.#supergraphServer) {
|
|
86
|
+
await this.#supergraphServer.stop();
|
|
87
|
+
this.#supergraphServer = null;
|
|
88
|
+
}
|
|
89
|
+
this.#gatewayOptions = null;
|
|
90
|
+
this.#getSubgraphs = null;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Wrap an existing ApolloServer as a Fetch API handler.
|
|
95
|
+
* Does not manage server lifecycle; caller is responsible for start/stop.
|
|
96
|
+
*/
|
|
97
|
+
function createApolloFetchHandler(server, contextFactory) {
|
|
98
|
+
return async (request) => {
|
|
99
|
+
const url = new URL(request.url);
|
|
100
|
+
const headers = new HeaderMap();
|
|
101
|
+
request.headers.forEach((value, key) => headers.set(key, value));
|
|
102
|
+
let body = void 0;
|
|
103
|
+
if (request.method !== "GET" && request.method !== "HEAD") try {
|
|
104
|
+
body = await request.json();
|
|
105
|
+
} catch {}
|
|
106
|
+
const result = await server.executeHTTPGraphQLRequest({
|
|
107
|
+
httpGraphQLRequest: {
|
|
108
|
+
method: request.method.toUpperCase(),
|
|
109
|
+
headers,
|
|
110
|
+
search: url.search,
|
|
111
|
+
body
|
|
112
|
+
},
|
|
113
|
+
context: () => contextFactory(request)
|
|
114
|
+
});
|
|
115
|
+
const responseHeaders = new Headers();
|
|
116
|
+
for (const [key, value] of result.headers) responseHeaders.set(key, value);
|
|
117
|
+
let responseBody;
|
|
118
|
+
if (result.body.kind === "complete") responseBody = result.body.string;
|
|
119
|
+
else {
|
|
120
|
+
const chunks = [];
|
|
121
|
+
for await (const chunk of result.body.asyncIterator) chunks.push(chunk);
|
|
122
|
+
responseBody = chunks.join("");
|
|
123
|
+
}
|
|
124
|
+
return new Response(responseBody, {
|
|
125
|
+
status: result.status ?? 200,
|
|
126
|
+
headers: responseHeaders
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//#endregion
|
|
131
|
+
export { ApolloGatewayAdapter };
|
|
132
|
+
|
|
133
|
+
//# sourceMappingURL=adapter-gateway-apollo-IZuGzFaY.mjs.map
|
|
134
|
+
//# debugId=7f597280-1b64-50b2-9735-754051857add
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-gateway-apollo-IZuGzFaY.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 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// 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 localCompose = new LocalCompose({ localServiceList: serviceList });\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":"7f597280-1b64-50b2-9735-754051857add"}
|
|
@@ -0,0 +1,132 @@
|
|
|
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]="b5ea3d3a-7b76-5891-97bf-dc76e50f3387")}catch(e){}}();
|
|
3
|
+
import { t as useServer } from "./websocket-DncAB9XK.mjs";
|
|
4
|
+
import mercuriusGateway from "@mercuriusjs/gateway";
|
|
5
|
+
import Fastify from "fastify";
|
|
6
|
+
import mercurius from "mercurius";
|
|
7
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
8
|
+
//#region src/graphql/gateway/adapter-gateway-mercurius.ts
|
|
9
|
+
/**
|
|
10
|
+
* Threads the original Fetch API Request through Mercurius's internal
|
|
11
|
+
* fastify.inject() call so that contextFactory (which uses the auth WeakMap
|
|
12
|
+
* from auth-middleware.ts) receives the same Request object that auth
|
|
13
|
+
* middleware populated before calling the handler.
|
|
14
|
+
*/
|
|
15
|
+
const requestAls = new AsyncLocalStorage();
|
|
16
|
+
const mercuriusGatewayPlugin = mercuriusGateway;
|
|
17
|
+
var MercuriusGatewayAdapter = class {
|
|
18
|
+
#logger;
|
|
19
|
+
#subgraphApps = [];
|
|
20
|
+
#supergraphApp = null;
|
|
21
|
+
#getSubgraphs = null;
|
|
22
|
+
#supergraphContextFactory = null;
|
|
23
|
+
constructor(logger) {
|
|
24
|
+
this.#logger = logger;
|
|
25
|
+
}
|
|
26
|
+
async start(_httpServer) {}
|
|
27
|
+
async createHandler(schema, contextFactory) {
|
|
28
|
+
const app = await buildMercuriusApp(schema, contextFactory, this.#logger);
|
|
29
|
+
this.#subgraphApps.push(app);
|
|
30
|
+
return buildFetchHandler(app);
|
|
31
|
+
}
|
|
32
|
+
async createSupergraphHandler(getSubgraphs, _httpServer, contextFactory) {
|
|
33
|
+
if (this.#supergraphApp) throw new Error("Supergraph is already running");
|
|
34
|
+
this.#getSubgraphs = getSubgraphs;
|
|
35
|
+
this.#supergraphContextFactory = contextFactory;
|
|
36
|
+
this.#supergraphApp = await buildGatewayApp(getSubgraphs(), contextFactory, this.#logger);
|
|
37
|
+
const adapter = this;
|
|
38
|
+
return (request) => {
|
|
39
|
+
if (!adapter.#supergraphApp) return Promise.resolve(new Response("Gateway not ready", { status: 503 }));
|
|
40
|
+
return requestAls.run(request, () => injectRequest(adapter.#supergraphApp, request));
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
async updateSupergraph() {
|
|
44
|
+
if (!this.#getSubgraphs || !this.#supergraphContextFactory) return;
|
|
45
|
+
const newApp = await buildGatewayApp(this.#getSubgraphs(), this.#supergraphContextFactory, this.#logger);
|
|
46
|
+
const oldApp = this.#supergraphApp;
|
|
47
|
+
this.#supergraphApp = newApp;
|
|
48
|
+
if (oldApp) await oldApp.close();
|
|
49
|
+
}
|
|
50
|
+
attachWebSocket(wsServer, schema, contextFactory) {
|
|
51
|
+
return useServer({
|
|
52
|
+
schema,
|
|
53
|
+
context: async (ctx) => contextFactory(ctx.connectionParams ?? {})
|
|
54
|
+
}, wsServer);
|
|
55
|
+
}
|
|
56
|
+
async stop() {
|
|
57
|
+
await Promise.all(this.#subgraphApps.map((app) => app.close()));
|
|
58
|
+
this.#subgraphApps.length = 0;
|
|
59
|
+
if (this.#supergraphApp) {
|
|
60
|
+
await this.#supergraphApp.close();
|
|
61
|
+
this.#supergraphApp = null;
|
|
62
|
+
}
|
|
63
|
+
this.#getSubgraphs = null;
|
|
64
|
+
this.#supergraphContextFactory = null;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
function makeContextFn(contextFactory, logger) {
|
|
68
|
+
return (_req, _reply) => {
|
|
69
|
+
const request = requestAls.getStore();
|
|
70
|
+
if (!request) {
|
|
71
|
+
logger.error("[mercurius] No Fetch Request in AsyncLocalStorage");
|
|
72
|
+
throw new Error("No Fetch Request in AsyncLocalStorage");
|
|
73
|
+
}
|
|
74
|
+
return contextFactory(request);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
async function buildMercuriusApp(schema, contextFactory, logger) {
|
|
78
|
+
const app = Fastify({ logger: false });
|
|
79
|
+
await app.register(mercurius, {
|
|
80
|
+
schema,
|
|
81
|
+
graphiql: false,
|
|
82
|
+
context: makeContextFn(contextFactory, logger),
|
|
83
|
+
resolvers: { _Service: { sdl: (parent) => (parent.sdl ?? "").replace(/\btype\s+(Query|Mutation|Subscription)\s*\{/g, "extend type $1 {") } }
|
|
84
|
+
});
|
|
85
|
+
await app.ready();
|
|
86
|
+
return app;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Builds a Mercurius federation gateway that composes the given subgraph
|
|
90
|
+
* services. Each service URL must be reachable so that the gateway can fetch
|
|
91
|
+
* its SDL via `_service { sdl }` (Apollo Federation protocol).
|
|
92
|
+
*/
|
|
93
|
+
async function buildGatewayApp(subgraphs, contextFactory, logger) {
|
|
94
|
+
const app = Fastify({ logger: false });
|
|
95
|
+
await app.register(mercuriusGatewayPlugin, {
|
|
96
|
+
gateway: { services: subgraphs.map((s) => ({
|
|
97
|
+
name: s.name,
|
|
98
|
+
url: s.url
|
|
99
|
+
})) },
|
|
100
|
+
graphiql: false,
|
|
101
|
+
context: makeContextFn(contextFactory, logger)
|
|
102
|
+
});
|
|
103
|
+
await app.ready();
|
|
104
|
+
return app;
|
|
105
|
+
}
|
|
106
|
+
function buildFetchHandler(app) {
|
|
107
|
+
return (request) => requestAls.run(request, () => injectRequest(app, request));
|
|
108
|
+
}
|
|
109
|
+
async function injectRequest(app, request) {
|
|
110
|
+
const body = request.method !== "GET" && request.method !== "HEAD" ? await request.text() : void 0;
|
|
111
|
+
const headers = {};
|
|
112
|
+
request.headers.forEach((value, key) => {
|
|
113
|
+
headers[key] = value;
|
|
114
|
+
});
|
|
115
|
+
const response = await app.inject({
|
|
116
|
+
method: request.method,
|
|
117
|
+
url: "/graphql",
|
|
118
|
+
headers,
|
|
119
|
+
payload: body
|
|
120
|
+
});
|
|
121
|
+
const responseHeaders = {};
|
|
122
|
+
for (const [key, value] of Object.entries(response.headers)) if (value !== void 0) responseHeaders[key] = String(value);
|
|
123
|
+
return new Response(response.payload, {
|
|
124
|
+
status: response.statusCode,
|
|
125
|
+
headers: responseHeaders
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
129
|
+
export { MercuriusGatewayAdapter };
|
|
130
|
+
|
|
131
|
+
//# sourceMappingURL=adapter-gateway-mercurius-CLmTtNJC.mjs.map
|
|
132
|
+
//# debugId=b5ea3d3a-7b76-5891-97bf-dc76e50f3387
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-gateway-mercurius-CLmTtNJC.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 WsContextFactory,\n WsDisposer,\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 contextFactory: WsContextFactory<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 context: async (ctx: { connectionParams?: Record<string, unknown> }) =>\n contextFactory(ctx.connectionParams ?? {}),\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":["#logger","#subgraphApps","#supergraphApp","#getSubgraphs","#supergraphContextFactory"],"mappings":"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","debug_id":"b5ea3d3a-7b76-5891-97bf-dc76e50f3387"}
|
|
@@ -0,0 +1,135 @@
|
|
|
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]="c2b574f7-cae6-5b39-aba7-5ea03cd99a0b")}catch(e){}}();
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { match } from "path-to-regexp";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import http from "node:http";
|
|
7
|
+
import https from "node:https";
|
|
8
|
+
import bodyParser from "body-parser";
|
|
9
|
+
import cors from "cors";
|
|
10
|
+
import devcert from "devcert";
|
|
11
|
+
import expressLib, { Router } from "express";
|
|
12
|
+
//#region src/graphql/gateway/adapter-http-express.ts
|
|
13
|
+
var ExpressHttpAdapter = class {
|
|
14
|
+
#app;
|
|
15
|
+
#router;
|
|
16
|
+
#handlers = /* @__PURE__ */ new Map();
|
|
17
|
+
constructor(existingApp) {
|
|
18
|
+
this.#app = existingApp ?? expressLib();
|
|
19
|
+
this.#router = Router();
|
|
20
|
+
this.#app.use(this.#router);
|
|
21
|
+
}
|
|
22
|
+
setupSentryErrorHandler(sentry) {
|
|
23
|
+
sentry.setupExpressErrorHandler(this.#app);
|
|
24
|
+
}
|
|
25
|
+
get handle() {
|
|
26
|
+
return this.#app;
|
|
27
|
+
}
|
|
28
|
+
mountRawMiddleware(middleware) {
|
|
29
|
+
this.#app.use(middleware);
|
|
30
|
+
}
|
|
31
|
+
mountNodeRoute(method, path, handler) {
|
|
32
|
+
const m = method.toLowerCase();
|
|
33
|
+
this.#app[m](path, (req, res) => handler(req, res, req.body));
|
|
34
|
+
}
|
|
35
|
+
setupMiddleware({ corsOptions, bodyLimit = "50mb" }) {
|
|
36
|
+
this.#router.use(cors(corsOptions));
|
|
37
|
+
this.#router.use(bodyParser.json({ limit: bodyLimit }));
|
|
38
|
+
this.#router.use(bodyParser.urlencoded({
|
|
39
|
+
extended: true,
|
|
40
|
+
limit: bodyLimit
|
|
41
|
+
}));
|
|
42
|
+
this.#router.use((req, res, next) => {
|
|
43
|
+
for (const { handler, matcher } of this.#handlers.values()) if (matcher(req.path)) {
|
|
44
|
+
this.#serveFetchHandler(handler, req, res, next);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
next();
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
mount(path, handler, { exact = false } = {}) {
|
|
51
|
+
if (exact) this.#router.use(path, (req, res, next) => this.#serveFetchHandler(handler, req, res, next));
|
|
52
|
+
else this.#handlers.set(path, {
|
|
53
|
+
handler,
|
|
54
|
+
matcher: match(path)
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
getRoute(routePath, handler) {
|
|
58
|
+
this.#app.get(routePath, (req, res) => {
|
|
59
|
+
const url = `${req.protocol}://${req.get("host") ?? "localhost"}${req.originalUrl}`;
|
|
60
|
+
const headers = new Headers();
|
|
61
|
+
for (const [key, value] of Object.entries(req.headers)) if (typeof value === "string") headers.set(key, value);
|
|
62
|
+
else if (Array.isArray(value)) headers.set(key, value.join(", "));
|
|
63
|
+
const fetchRequest = new Request(url, {
|
|
64
|
+
method: "GET",
|
|
65
|
+
headers
|
|
66
|
+
});
|
|
67
|
+
Promise.resolve(handler(fetchRequest)).then(async (response) => {
|
|
68
|
+
res.status(response.status);
|
|
69
|
+
response.headers.forEach((value, key) => {
|
|
70
|
+
res.setHeader(key, value);
|
|
71
|
+
});
|
|
72
|
+
res.send(await response.text());
|
|
73
|
+
}).catch((err) => {
|
|
74
|
+
res.status(500).send(String(err));
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
async listen(port, tls) {
|
|
79
|
+
let server;
|
|
80
|
+
if (tls === true) {
|
|
81
|
+
const { cert, key } = await devcert.certificateFor("localhost");
|
|
82
|
+
if (!cert || !key) throw new Error("Invalid certificate generated");
|
|
83
|
+
server = https.createServer({
|
|
84
|
+
cert,
|
|
85
|
+
key
|
|
86
|
+
}, this.#app);
|
|
87
|
+
} else if (tls && "keyPath" in tls) {
|
|
88
|
+
const currentDir = process.cwd();
|
|
89
|
+
server = https.createServer({
|
|
90
|
+
key: fs.readFileSync(path.join(currentDir, tls.keyPath)),
|
|
91
|
+
cert: fs.readFileSync(path.join(currentDir, tls.certPath))
|
|
92
|
+
}, this.#app);
|
|
93
|
+
} else if (tls && "cert" in tls) server = https.createServer({
|
|
94
|
+
cert: tls.cert,
|
|
95
|
+
key: tls.key
|
|
96
|
+
}, this.#app);
|
|
97
|
+
else server = http.createServer(this.#app);
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
server.once("error", reject);
|
|
100
|
+
server.listen(port, () => {
|
|
101
|
+
server.off("error", reject);
|
|
102
|
+
resolve(server);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
#serveFetchHandler(handler, req, res, next) {
|
|
107
|
+
const url = `${req.protocol}://${req.get("host") ?? "localhost"}${req.originalUrl}`;
|
|
108
|
+
const headers = new Headers();
|
|
109
|
+
for (const [key, value] of Object.entries(req.headers)) if (typeof value === "string") headers.set(key, value);
|
|
110
|
+
else if (Array.isArray(value)) headers.set(key, value.join(", "));
|
|
111
|
+
let body;
|
|
112
|
+
if (req.method !== "GET" && req.method !== "HEAD" && req.body !== void 0) body = JSON.stringify(req.body);
|
|
113
|
+
const fetchRequest = new Request(url, {
|
|
114
|
+
method: req.method,
|
|
115
|
+
headers,
|
|
116
|
+
body
|
|
117
|
+
});
|
|
118
|
+
Promise.resolve(handler(fetchRequest)).then(async (response) => {
|
|
119
|
+
res.status(response.status);
|
|
120
|
+
response.headers.forEach((value, key) => {
|
|
121
|
+
res.setHeader(key, value);
|
|
122
|
+
});
|
|
123
|
+
const responseBody = await response.text();
|
|
124
|
+
res.send(responseBody);
|
|
125
|
+
}).catch(next);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
function createExpressHttpAdapter(existingApp) {
|
|
129
|
+
return { adapter: new ExpressHttpAdapter(existingApp) };
|
|
130
|
+
}
|
|
131
|
+
//#endregion
|
|
132
|
+
export { createExpressHttpAdapter };
|
|
133
|
+
|
|
134
|
+
//# sourceMappingURL=adapter-http-express-DO4ui7AV.mjs.map
|
|
135
|
+
//# debugId=c2b574f7-cae6-5b39-aba7-5ea03cd99a0b
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter-http-express-DO4ui7AV.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 { match, type MatchFunction, type ParamData } from \"path-to-regexp\";\nimport type { FetchHandler, IHttpAdapter, TlsOptions } from \"./types.js\";\n\nexport class ExpressHttpAdapter implements IHttpAdapter {\n readonly #app: Express;\n readonly #router: IRouter;\n readonly #handlers = new Map<\n string,\n { handler: FetchHandler; matcher: MatchFunction<ParamData> }\n >();\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 // eslint-disable-next-line @typescript-eslint/no-explicit-any\n this.#app.use(middleware as any);\n }\n\n mountNodeRoute(\n method: \"DELETE\" | \"GET\" | \"HEAD\" | \"POST\" | \"PUT\",\n path: string,\n handler: (\n req: http.IncomingMessage,\n res: http.ServerResponse,\n body?: unknown,\n ) => void,\n ): void {\n const m = method.toLowerCase() as\n | \"delete\"\n | \"get\"\n | \"head\"\n | \"post\"\n | \"put\";\n this.#app[m](path, (req: express.Request, res: express.Response) =>\n handler(req, res, req.body as unknown),\n );\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 this.#router.use(bodyParser.json({ limit: bodyLimit }));\n this.#router.use(\n bodyParser.urlencoded({ extended: true, limit: bodyLimit }),\n );\n\n // Dispatcher registered AFTER bodyParser so req.body is populated when it fires.\n this.#router.use((req, res, next) => {\n for (const { handler, matcher } of this.#handlers.values()) {\n if (matcher(req.path)) {\n this.#serveFetchHandler(handler, req, res, next);\n return;\n }\n }\n next();\n });\n }\n\n mount(\n path: string,\n handler: FetchHandler,\n { exact = false }: { exact?: boolean } = {},\n ): void {\n if (exact) {\n this.#router.use(path, (req, res, next) =>\n this.#serveFetchHandler(handler, req, res, next),\n );\n } else {\n // Exact match - stored in the internal dispatch map\n this.#handlers.set(path, {\n handler,\n matcher: match(path),\n });\n }\n }\n\n getRoute(\n routePath: string,\n handler: (request: Request) => Response | Promise<Response>,\n ): void {\n this.#app.get(routePath, (req, res) => {\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 const fetchRequest = new Request(url, { method: \"GET\", headers });\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 res.send(await response.text());\n })\n .catch((err: unknown) => {\n res.status(500).send(String(err));\n });\n });\n }\n\n async listen(port: number, tls?: TlsOptions): 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, () => {\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 const responseBody = await response.text();\n res.send(responseBody);\n })\n .catch(next);\n }\n}\n\nexport function createExpressHttpAdapter(existingApp?: Express): {\n adapter: IHttpAdapter;\n} {\n return { adapter: new ExpressHttpAdapter(existingApp) };\n}\n"],"names":["#app","#router","#handlers","#serveFetchHandler"],"mappings":"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","debug_id":"c2b574f7-cae6-5b39-aba7-5ea03cd99a0b"}
|