@effect-gql/node 0.1.0 → 1.1.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.
Files changed (49) hide show
  1. package/README.md +100 -0
  2. package/index.cjs +315 -0
  3. package/index.cjs.map +1 -0
  4. package/index.d.cts +270 -0
  5. package/index.d.ts +270 -0
  6. package/index.js +310 -0
  7. package/index.js.map +1 -0
  8. package/package.json +14 -31
  9. package/dist/config.d.ts +0 -43
  10. package/dist/config.d.ts.map +0 -1
  11. package/dist/config.js +0 -52
  12. package/dist/config.js.map +0 -1
  13. package/dist/graphiql.d.ts +0 -5
  14. package/dist/graphiql.d.ts.map +0 -1
  15. package/dist/graphiql.js +0 -43
  16. package/dist/graphiql.js.map +0 -1
  17. package/dist/http-utils.d.ts +0 -20
  18. package/dist/http-utils.d.ts.map +0 -1
  19. package/dist/http-utils.js +0 -36
  20. package/dist/http-utils.js.map +0 -1
  21. package/dist/index.d.ts +0 -5
  22. package/dist/index.d.ts.map +0 -1
  23. package/dist/index.js +0 -18
  24. package/dist/index.js.map +0 -1
  25. package/dist/router.d.ts +0 -32
  26. package/dist/router.d.ts.map +0 -1
  27. package/dist/router.js +0 -77
  28. package/dist/router.js.map +0 -1
  29. package/dist/schema-builder-extensions.d.ts +0 -31
  30. package/dist/schema-builder-extensions.d.ts.map +0 -1
  31. package/dist/schema-builder-extensions.js +0 -35
  32. package/dist/schema-builder-extensions.js.map +0 -1
  33. package/dist/serve.d.ts +0 -73
  34. package/dist/serve.d.ts.map +0 -1
  35. package/dist/serve.js +0 -176
  36. package/dist/serve.js.map +0 -1
  37. package/dist/sse.d.ts +0 -86
  38. package/dist/sse.d.ts.map +0 -1
  39. package/dist/sse.js +0 -175
  40. package/dist/sse.js.map +0 -1
  41. package/dist/ws.d.ts +0 -97
  42. package/dist/ws.d.ts.map +0 -1
  43. package/dist/ws.js +0 -137
  44. package/dist/ws.js.map +0 -1
  45. package/src/http-utils.ts +0 -32
  46. package/src/index.ts +0 -15
  47. package/src/serve.ts +0 -217
  48. package/src/sse.ts +0 -234
  49. package/src/ws.ts +0 -183
package/src/serve.ts DELETED
@@ -1,217 +0,0 @@
1
- import { Effect, Layer } from "effect"
2
- import { HttpApp, HttpRouter, HttpServer } from "@effect/platform"
3
- import { NodeHttpServer, NodeRuntime } from "@effect/platform-node"
4
- import { createServer } from "node:http"
5
- import type { GraphQLSchema } from "graphql"
6
- import type { GraphQLWSOptions } from "@effect-gql/core"
7
- import { toWebHeaders } from "./http-utils"
8
-
9
- /**
10
- * Configuration for WebSocket subscriptions
11
- */
12
- export interface SubscriptionsConfig<R> extends GraphQLWSOptions<R> {
13
- /**
14
- * The GraphQL schema (required for subscriptions).
15
- * Must be the same schema used to create the router.
16
- */
17
- readonly schema: GraphQLSchema
18
- /**
19
- * Path for WebSocket connections.
20
- * @default "/graphql"
21
- */
22
- readonly path?: string
23
- }
24
-
25
- /**
26
- * Options for the Node.js GraphQL server
27
- */
28
- export interface ServeOptions<R = never> {
29
- /** Port to listen on (default: 4000) */
30
- readonly port?: number
31
- /** Hostname to bind to (default: "0.0.0.0") */
32
- readonly host?: string
33
- /** Callback when server starts */
34
- readonly onStart?: (url: string) => void
35
- /**
36
- * Enable WebSocket subscriptions.
37
- * When provided, the server will handle WebSocket upgrade requests
38
- * for GraphQL subscriptions using the graphql-ws protocol.
39
- */
40
- readonly subscriptions?: SubscriptionsConfig<R>
41
- }
42
-
43
- /**
44
- * Start a Node.js HTTP server with the given router.
45
- *
46
- * This is the main entry point for running a GraphQL server on Node.js.
47
- * It handles all the Effect runtime setup and server lifecycle.
48
- *
49
- * @param router - The HttpRouter to serve (typically from makeGraphQLRouter or toRouter)
50
- * @param layer - Layer providing the router's service dependencies
51
- * @param options - Server configuration options
52
- *
53
- * @example
54
- * ```typescript
55
- * import { makeGraphQLRouter } from "@effect-gql/core"
56
- * import { serve } from "@effect-gql/node"
57
- *
58
- * const schema = GraphQLSchemaBuilder.empty
59
- * .query("hello", { type: S.String, resolve: () => Effect.succeed("world") })
60
- * .buildSchema()
61
- *
62
- * const router = makeGraphQLRouter(schema, Layer.empty, { graphiql: true })
63
- *
64
- * // Without subscriptions
65
- * serve(router, serviceLayer, {
66
- * port: 4000,
67
- * onStart: (url) => console.log(`Server running at ${url}`)
68
- * })
69
- *
70
- * // With subscriptions
71
- * serve(router, serviceLayer, {
72
- * port: 4000,
73
- * subscriptions: { schema },
74
- * onStart: (url) => console.log(`Server running at ${url}`)
75
- * })
76
- * ```
77
- */
78
- export const serve = <E, R, RE>(
79
- router: HttpRouter.HttpRouter<E, R>,
80
- layer: Layer.Layer<R, RE>,
81
- options: ServeOptions<R> = {}
82
- ): void => {
83
- const { port = 4000, host = "0.0.0.0", onStart, subscriptions } = options
84
-
85
- if (subscriptions) {
86
- // With WebSocket subscriptions - we need to manage the HTTP server ourselves
87
- serveWithSubscriptions(router, layer, port, host, subscriptions, onStart)
88
- } else {
89
- // Without subscriptions - use the standard Effect approach
90
- const app = router.pipe(
91
- Effect.catchAllCause((cause) => Effect.die(cause)),
92
- HttpServer.serve()
93
- )
94
-
95
- const serverLayer = NodeHttpServer.layer(() => createServer(), { port })
96
- const fullLayer = Layer.merge(serverLayer, layer)
97
-
98
- if (onStart) {
99
- onStart(`http://${host === "0.0.0.0" ? "localhost" : host}:${port}`)
100
- }
101
-
102
- NodeRuntime.runMain(Layer.launch(Layer.provide(app, fullLayer)))
103
- }
104
- }
105
-
106
- /**
107
- * Internal implementation for serving with WebSocket subscriptions.
108
- * Uses a custom HTTP server setup to enable WebSocket upgrade handling.
109
- */
110
- function serveWithSubscriptions<E, R, RE>(
111
- router: HttpRouter.HttpRouter<E, R>,
112
- layer: Layer.Layer<R, RE>,
113
- port: number,
114
- host: string,
115
- subscriptions: SubscriptionsConfig<R>,
116
- onStart?: (url: string) => void
117
- ): void {
118
- // Dynamically import ws module to keep it optional
119
- const importWs = Effect.tryPromise({
120
- try: () => import("./ws"),
121
- catch: (error) => error as Error,
122
- })
123
-
124
- Effect.runPromise(
125
- importWs.pipe(
126
- Effect.catchAll((error) =>
127
- Effect.logError("Failed to load WebSocket support", error).pipe(
128
- Effect.andThen(Effect.logError("Make sure 'ws' package is installed: npm install ws")),
129
- Effect.andThen(Effect.sync(() => process.exit(1))),
130
- Effect.andThen(Effect.fail(error))
131
- )
132
- )
133
- )
134
- ).then(({ createGraphQLWSServer }) => {
135
- // Create the web handler from the Effect router
136
- const { handler } = HttpApp.toWebHandlerLayer(router, layer)
137
-
138
- // Create the HTTP server
139
- const httpServer = createServer(async (req, res) => {
140
- try {
141
- // Collect request body
142
- const chunks: Buffer[] = []
143
- for await (const chunk of req) {
144
- chunks.push(chunk as Buffer)
145
- }
146
- const body = Buffer.concat(chunks).toString()
147
-
148
- // Convert Node.js request to web standard Request
149
- // Use URL constructor for safe URL parsing (avoids injection via req.url)
150
- const baseUrl = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`
151
- const url = new URL(req.url || "/", baseUrl).href
152
- const headers = toWebHeaders(req.headers)
153
-
154
- const webRequest = new Request(url, {
155
- method: req.method,
156
- headers,
157
- body: ["GET", "HEAD"].includes(req.method!) ? undefined : body,
158
- })
159
-
160
- // Process through Effect handler
161
- const webResponse = await handler(webRequest)
162
-
163
- // Write response
164
- res.statusCode = webResponse.status
165
- webResponse.headers.forEach((value, key) => {
166
- res.setHeader(key, value)
167
- })
168
- const responseBody = await webResponse.text()
169
- res.end(responseBody)
170
- } catch (error) {
171
- res.statusCode = 500
172
- res.end(JSON.stringify({ error: String(error) }))
173
- }
174
- })
175
-
176
- // Create WebSocket server for subscriptions
177
- const { handleUpgrade, close: closeWS } = createGraphQLWSServer(
178
- subscriptions.schema,
179
- layer as Layer.Layer<R>,
180
- {
181
- path: subscriptions.path,
182
- complexity: subscriptions.complexity,
183
- fieldComplexities: subscriptions.fieldComplexities,
184
- onConnect: subscriptions.onConnect,
185
- onDisconnect: subscriptions.onDisconnect,
186
- onSubscribe: subscriptions.onSubscribe,
187
- onComplete: subscriptions.onComplete,
188
- onError: subscriptions.onError,
189
- }
190
- )
191
-
192
- // Attach WebSocket upgrade handler
193
- httpServer.on("upgrade", (request, socket, head) => {
194
- handleUpgrade(request, socket, head)
195
- })
196
-
197
- // Handle shutdown
198
- process.on("SIGINT", async () => {
199
- await closeWS()
200
- httpServer.close()
201
- process.exit(0)
202
- })
203
-
204
- process.on("SIGTERM", async () => {
205
- await closeWS()
206
- httpServer.close()
207
- process.exit(0)
208
- })
209
-
210
- // Start listening
211
- httpServer.listen(port, host, () => {
212
- if (onStart) {
213
- onStart(`http://${host === "0.0.0.0" ? "localhost" : host}:${port}`)
214
- }
215
- })
216
- })
217
- }
package/src/sse.ts DELETED
@@ -1,234 +0,0 @@
1
- import { Effect, Layer, Stream, Deferred } from "effect"
2
- import type { IncomingMessage, ServerResponse } from "node:http"
3
- import { GraphQLSchema } from "graphql"
4
- import {
5
- makeGraphQLSSEHandler,
6
- formatSSEMessage,
7
- SSE_HEADERS,
8
- type GraphQLSSEOptions,
9
- type SSESubscriptionRequest,
10
- SSEError,
11
- } from "@effect-gql/core"
12
- import { toWebHeaders } from "./http-utils"
13
-
14
- /**
15
- * Options for Node.js SSE handler
16
- */
17
- export interface NodeSSEOptions<R> extends GraphQLSSEOptions<R> {
18
- /**
19
- * Path for SSE connections.
20
- * @default "/graphql/stream"
21
- */
22
- readonly path?: string
23
- }
24
-
25
- /**
26
- * Create an SSE handler for Node.js HTTP server.
27
- *
28
- * This function creates a handler that can process SSE subscription requests.
29
- * It handles:
30
- * - Parsing the GraphQL subscription request from the HTTP body
31
- * - Setting up the SSE connection with proper headers
32
- * - Streaming subscription events to the client
33
- * - Detecting client disconnection and cleaning up
34
- *
35
- * @param schema - The GraphQL schema with subscription definitions
36
- * @param layer - Effect layer providing services required by resolvers
37
- * @param options - Optional lifecycle hooks and configuration
38
- * @returns A request handler function
39
- *
40
- * @example
41
- * ```typescript
42
- * import { createServer } from "node:http"
43
- * import { createSSEHandler } from "@effect-gql/node"
44
- *
45
- * const sseHandler = createSSEHandler(schema, serviceLayer, {
46
- * path: "/graphql/stream",
47
- * onConnect: (request, headers) => Effect.gen(function* () {
48
- * const user = yield* AuthService.validateToken(headers.get("authorization"))
49
- * return { user }
50
- * }),
51
- * })
52
- *
53
- * const server = createServer((req, res) => {
54
- * const url = new URL(req.url, `http://${req.headers.host}`)
55
- * if (url.pathname === "/graphql/stream" && req.method === "POST") {
56
- * sseHandler(req, res)
57
- * } else {
58
- * // Handle other requests...
59
- * }
60
- * })
61
- *
62
- * server.listen(4000)
63
- * ```
64
- */
65
- export const createSSEHandler = <R>(
66
- schema: GraphQLSchema,
67
- layer: Layer.Layer<R>,
68
- options?: NodeSSEOptions<R>
69
- ): ((req: IncomingMessage, res: ServerResponse) => Promise<void>) => {
70
- const sseHandler = makeGraphQLSSEHandler(schema, layer, options)
71
-
72
- return async (req: IncomingMessage, res: ServerResponse): Promise<void> => {
73
- // Check Accept header for SSE support
74
- const accept = req.headers.accept ?? ""
75
- if (!accept.includes("text/event-stream") && !accept.includes("*/*")) {
76
- res.statusCode = 406
77
- res.end(
78
- JSON.stringify({
79
- errors: [{ message: "Client must accept text/event-stream" }],
80
- })
81
- )
82
- return
83
- }
84
-
85
- // Read the request body
86
- let body: string
87
- try {
88
- body = await readBody(req)
89
- } catch {
90
- res.statusCode = 400
91
- res.end(
92
- JSON.stringify({
93
- errors: [{ message: "Failed to read request body" }],
94
- })
95
- )
96
- return
97
- }
98
-
99
- // Parse the GraphQL request
100
- let request: SSESubscriptionRequest
101
- try {
102
- const parsed = JSON.parse(body)
103
- if (typeof parsed.query !== "string") {
104
- throw new Error("Missing query")
105
- }
106
- request = {
107
- query: parsed.query,
108
- variables: parsed.variables,
109
- operationName: parsed.operationName,
110
- extensions: parsed.extensions,
111
- }
112
- } catch {
113
- res.statusCode = 400
114
- res.end(
115
- JSON.stringify({
116
- errors: [{ message: "Invalid GraphQL request body" }],
117
- })
118
- )
119
- return
120
- }
121
-
122
- // Convert Node.js headers to web Headers
123
- const headers = toWebHeaders(req.headers)
124
-
125
- // Set SSE headers
126
- res.writeHead(200, SSE_HEADERS)
127
-
128
- // Get the event stream
129
- const eventStream = sseHandler(request, headers)
130
-
131
- // Create the streaming effect
132
- const streamEffect = Effect.gen(function* () {
133
- // Track client disconnection
134
- const clientDisconnected = yield* Deferred.make<void, SSEError>()
135
-
136
- req.on("close", () => {
137
- Effect.runPromise(Deferred.succeed(clientDisconnected, undefined)).catch(() => {})
138
- })
139
-
140
- req.on("error", (error) => {
141
- Effect.runPromise(Deferred.fail(clientDisconnected, new SSEError({ cause: error }))).catch(
142
- () => {}
143
- )
144
- })
145
-
146
- // Stream events to the client
147
- const runStream = Stream.runForEach(eventStream, (event) =>
148
- Effect.async<void, SSEError>((resume) => {
149
- const message = formatSSEMessage(event)
150
- res.write(message, (error) => {
151
- if (error) {
152
- resume(Effect.fail(new SSEError({ cause: error })))
153
- } else {
154
- resume(Effect.succeed(undefined))
155
- }
156
- })
157
- })
158
- )
159
-
160
- // Race between stream completion and client disconnection
161
- yield* Effect.race(
162
- runStream.pipe(Effect.catchAll((error) => Effect.logWarning("SSE stream error", error))),
163
- Deferred.await(clientDisconnected)
164
- )
165
- })
166
-
167
- await Effect.runPromise(
168
- streamEffect.pipe(
169
- Effect.ensuring(Effect.sync(() => res.end())),
170
- Effect.catchAll(() => Effect.void)
171
- )
172
- )
173
- }
174
- }
175
-
176
- /**
177
- * Read the request body as a string.
178
- */
179
- function readBody(req: IncomingMessage): Promise<string> {
180
- return new Promise((resolve, reject) => {
181
- const chunks: Buffer[] = []
182
- req.on("data", (chunk: Buffer) => chunks.push(chunk))
183
- req.on("end", () => resolve(Buffer.concat(chunks).toString()))
184
- req.on("error", reject)
185
- })
186
- }
187
-
188
- /**
189
- * Create SSE middleware that can be used with the serve() function.
190
- *
191
- * This returns an object that can be used to integrate SSE subscriptions
192
- * with the HTTP server when using the custom subscription mode.
193
- *
194
- * @param schema - The GraphQL schema with subscription definitions
195
- * @param layer - Effect layer providing services required by resolvers
196
- * @param options - Optional lifecycle hooks and configuration
197
- *
198
- * @example
199
- * ```typescript
200
- * // In serve.ts with custom HTTP server setup
201
- * const sseServer = createSSEServer(schema, layer, { path: "/graphql/stream" })
202
- *
203
- * httpServer.on("request", (req, res) => {
204
- * if (sseServer.shouldHandle(req)) {
205
- * sseServer.handle(req, res)
206
- * }
207
- * })
208
- * ```
209
- */
210
- export const createSSEServer = <R>(
211
- schema: GraphQLSchema,
212
- layer: Layer.Layer<R>,
213
- options?: NodeSSEOptions<R>
214
- ): {
215
- /** Path this SSE server handles */
216
- readonly path: string
217
- /** Check if a request should be handled by this SSE server */
218
- shouldHandle: (req: IncomingMessage) => boolean
219
- /** Handle an SSE request */
220
- handle: (req: IncomingMessage, res: ServerResponse) => Promise<void>
221
- } => {
222
- const path = options?.path ?? "/graphql/stream"
223
- const handler = createSSEHandler(schema, layer, options)
224
-
225
- return {
226
- path,
227
- shouldHandle: (req: IncomingMessage) => {
228
- if (req.method !== "POST") return false
229
- const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`)
230
- return url.pathname === path
231
- },
232
- handle: handler,
233
- }
234
- }
package/src/ws.ts DELETED
@@ -1,183 +0,0 @@
1
- import { Effect, Layer } from "effect"
2
- import type { IncomingMessage, Server } from "node:http"
3
- import type { Duplex } from "node:stream"
4
- import { WebSocket, WebSocketServer } from "ws"
5
- import { GraphQLSchema } from "graphql"
6
- import {
7
- makeGraphQLWSHandler,
8
- toEffectWebSocketFromWs,
9
- type EffectWebSocket,
10
- type GraphQLWSOptions,
11
- } from "@effect-gql/core"
12
-
13
- /**
14
- * Options for Node.js WebSocket server
15
- */
16
- export interface NodeWSOptions<R> extends GraphQLWSOptions<R> {
17
- /**
18
- * Path for WebSocket connections.
19
- * @default "/graphql"
20
- */
21
- readonly path?: string
22
- }
23
-
24
- /**
25
- * Convert a Node.js WebSocket (from 'ws' library) to an EffectWebSocket.
26
- *
27
- * This creates an Effect-based wrapper around the ws WebSocket instance,
28
- * providing a Stream for incoming messages and Effect-based send/close operations.
29
- *
30
- * @param ws - The WebSocket instance from the 'ws' library
31
- * @returns An EffectWebSocket that can be used with makeGraphQLWSHandler
32
- *
33
- * @example
34
- * ```typescript
35
- * wss.on("connection", (ws, req) => {
36
- * const effectSocket = toEffectWebSocket(ws)
37
- * Effect.runPromise(handler(effectSocket))
38
- * })
39
- * ```
40
- */
41
- export const toEffectWebSocket = (ws: WebSocket): EffectWebSocket => toEffectWebSocketFromWs(ws)
42
-
43
- /**
44
- * Create a WebSocket server that handles GraphQL subscriptions.
45
- *
46
- * This function creates a WebSocketServer and returns utilities for
47
- * integrating it with an HTTP server via the upgrade event.
48
- *
49
- * @param schema - The GraphQL schema with subscription definitions
50
- * @param layer - Effect layer providing services required by resolvers
51
- * @param options - Optional configuration and lifecycle hooks
52
- * @returns Object containing the WebSocketServer and handlers
53
- *
54
- * @example
55
- * ```typescript
56
- * const httpServer = createServer(requestHandler)
57
- * const { wss, handleUpgrade } = createGraphQLWSServer(schema, serviceLayer)
58
- *
59
- * httpServer.on("upgrade", (request, socket, head) => {
60
- * if (request.url === "/graphql") {
61
- * handleUpgrade(request, socket, head)
62
- * }
63
- * })
64
- *
65
- * httpServer.listen(4000)
66
- * ```
67
- */
68
- export const createGraphQLWSServer = <R>(
69
- schema: GraphQLSchema,
70
- layer: Layer.Layer<R>,
71
- options?: NodeWSOptions<R>
72
- ): {
73
- /** The underlying WebSocketServer instance */
74
- wss: WebSocketServer
75
- /** Handle HTTP upgrade requests */
76
- handleUpgrade: (request: IncomingMessage, socket: Duplex, head: Buffer) => void
77
- /** Close the WebSocket server */
78
- close: () => Promise<void>
79
- } => {
80
- const wss = new WebSocketServer({ noServer: true })
81
- const path = options?.path ?? "/graphql"
82
-
83
- // Create the handler from core
84
- const handler = makeGraphQLWSHandler(schema, layer, options)
85
-
86
- // Track active connections for cleanup
87
- const activeConnections = new Set<WebSocket>()
88
-
89
- wss.on("connection", (ws, _request) => {
90
- activeConnections.add(ws)
91
-
92
- const effectSocket = toEffectWebSocket(ws)
93
-
94
- // Run the handler
95
- Effect.runPromise(
96
- handler(effectSocket).pipe(
97
- Effect.catchAll((error) => Effect.logError("GraphQL WebSocket handler error", error))
98
- )
99
- ).finally(() => {
100
- activeConnections.delete(ws)
101
- })
102
- })
103
-
104
- const handleUpgrade = (request: IncomingMessage, socket: Duplex, head: Buffer) => {
105
- // Check if this is the GraphQL WebSocket path
106
- const url = new URL(request.url ?? "/", `http://${request.headers.host}`)
107
- if (url.pathname !== path) {
108
- socket.destroy()
109
- return
110
- }
111
-
112
- // Check for correct WebSocket subprotocol
113
- const protocol = request.headers["sec-websocket-protocol"]
114
- if (!protocol?.includes("graphql-transport-ws")) {
115
- socket.write("HTTP/1.1 400 Bad Request\r\n\r\n")
116
- socket.destroy()
117
- return
118
- }
119
-
120
- wss.handleUpgrade(request, socket, head, (ws) => {
121
- wss.emit("connection", ws, request)
122
- })
123
- }
124
-
125
- const close = async () => {
126
- // Close all active connections
127
- for (const ws of activeConnections) {
128
- ws.close(1001, "Server shutting down")
129
- }
130
- activeConnections.clear()
131
-
132
- // Close the WebSocket server
133
- return new Promise<void>((resolve, reject) => {
134
- wss.close((error) => {
135
- if (error) reject(error)
136
- else resolve()
137
- })
138
- })
139
- }
140
-
141
- return { wss, handleUpgrade, close }
142
- }
143
-
144
- /**
145
- * Attach WebSocket subscription support to an existing HTTP server.
146
- *
147
- * This is a convenience function that creates a GraphQL WebSocket server
148
- * and attaches it to an HTTP server's upgrade event.
149
- *
150
- * @param server - The HTTP server to attach to
151
- * @param schema - The GraphQL schema with subscription definitions
152
- * @param layer - Effect layer providing services required by resolvers
153
- * @param options - Optional configuration and lifecycle hooks
154
- * @returns Cleanup function to close the WebSocket server
155
- *
156
- * @example
157
- * ```typescript
158
- * const httpServer = createServer(requestHandler)
159
- *
160
- * const cleanup = attachWebSocketToServer(httpServer, schema, serviceLayer, {
161
- * path: "/graphql",
162
- * })
163
- *
164
- * httpServer.listen(4000)
165
- *
166
- * // Later, to cleanup:
167
- * await cleanup()
168
- * ```
169
- */
170
- export const attachWebSocketToServer = <R>(
171
- server: Server,
172
- schema: GraphQLSchema,
173
- layer: Layer.Layer<R>,
174
- options?: NodeWSOptions<R>
175
- ): { close: () => Promise<void> } => {
176
- const { handleUpgrade, close } = createGraphQLWSServer(schema, layer, options)
177
-
178
- server.on("upgrade", (request, socket, head) => {
179
- handleUpgrade(request, socket as Duplex, head)
180
- })
181
-
182
- return { close }
183
- }