@effect-gql/bun 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.
package/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # Effect GraphQL
2
+
3
+ A GraphQL framework for Effect-TS that brings full type safety, composability, and functional programming to your GraphQL servers.
4
+
5
+ > **Note:** This is an experimental prototype exploring the integration between Effect Schema, Effect's service system, and GraphQL.
6
+
7
+ ## Features
8
+
9
+ - **Type-Safe End-to-End** - Define schemas once with Effect Schema, get TypeScript types and GraphQL types automatically
10
+ - **Effect-Powered Resolvers** - Resolvers are Effect programs with built-in error handling and service injection
11
+ - **Immutable Builder** - Fluent, pipe-able API for composing schemas from reusable parts
12
+ - **Service Integration** - Use Effect's Layer system for dependency injection
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @effect-gql/core effect graphql
18
+ ```
19
+
20
+ ## Quick Start
21
+
22
+ ```typescript
23
+ import { Effect, Layer } from "effect"
24
+ import * as S from "effect/Schema"
25
+ import { GraphQLSchemaBuilder, execute } from "@effect-gql/core"
26
+
27
+ // Define your schema with Effect Schema
28
+ const UserSchema = S.Struct({
29
+ id: S.String,
30
+ name: S.String,
31
+ email: S.String,
32
+ })
33
+
34
+ // Build your GraphQL schema
35
+ const schema = GraphQLSchemaBuilder.empty
36
+ .objectType({ name: "User", schema: UserSchema })
37
+ .query("users", {
38
+ type: S.Array(UserSchema),
39
+ resolve: () => Effect.succeed([
40
+ { id: "1", name: "Alice", email: "alice@example.com" },
41
+ { id: "2", name: "Bob", email: "bob@example.com" },
42
+ ]),
43
+ })
44
+ .query("user", {
45
+ type: UserSchema,
46
+ args: S.Struct({ id: S.String }),
47
+ resolve: (args) => Effect.succeed({
48
+ id: args.id,
49
+ name: "Alice",
50
+ email: "alice@example.com",
51
+ }),
52
+ })
53
+ .buildSchema()
54
+
55
+ // Execute a query
56
+ const result = await Effect.runPromise(
57
+ execute(schema, Layer.empty)(`
58
+ query {
59
+ users { id name email }
60
+ }
61
+ `)
62
+ )
63
+ ```
64
+
65
+ ## Documentation
66
+
67
+ For full documentation, guides, and API reference, visit the [documentation site](https://nrf110.github.io/effect-gql/).
68
+
69
+ ## Development
70
+
71
+ ```bash
72
+ # Install dependencies
73
+ npm install
74
+
75
+ # Build the project
76
+ npm run build
77
+
78
+ # Run tests
79
+ npm test
80
+
81
+ # Development mode with watch
82
+ npm run dev
83
+ ```
84
+
85
+ ## Contributing
86
+
87
+ Contributions are welcome! Here's how you can help:
88
+
89
+ 1. **Report bugs** - Open an issue describing the problem and steps to reproduce
90
+ 2. **Suggest features** - Open an issue describing your idea
91
+ 3. **Submit PRs** - Fork the repo, make your changes, and open a pull request
92
+
93
+ Please ensure your code:
94
+ - Passes all existing tests (`npm test`)
95
+ - Includes tests for new functionality
96
+ - Follows the existing code style
97
+
98
+ ## License
99
+
100
+ MIT
package/index.cjs ADDED
@@ -0,0 +1,291 @@
1
+ 'use strict';
2
+
3
+ var effect = require('effect');
4
+ var core = require('@effect-gql/core');
5
+ var platform = require('@effect/platform');
6
+ var platformBun = require('@effect/platform-bun');
7
+
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropNames = Object.getOwnPropertyNames;
10
+ var __esm = (fn, res) => function __init() {
11
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
12
+ };
13
+ var __export = (target, all) => {
14
+ for (var name in all)
15
+ __defProp(target, name, { get: all[name], enumerable: true });
16
+ };
17
+
18
+ // src/ws.ts
19
+ var ws_exports = {};
20
+ __export(ws_exports, {
21
+ createBunWSHandlers: () => exports.createBunWSHandlers,
22
+ toBunEffectWebSocket: () => exports.toBunEffectWebSocket
23
+ });
24
+ exports.createBunWSHandlers = void 0; exports.toBunEffectWebSocket = void 0;
25
+ var init_ws = __esm({
26
+ "src/ws.ts"() {
27
+ exports.createBunWSHandlers = (schema, layer, options) => {
28
+ const path = options?.path ?? "/graphql";
29
+ const handler = core.makeGraphQLWSHandler(schema, layer, options);
30
+ const activeHandlers = /* @__PURE__ */ new Map();
31
+ const upgrade = (request, server) => {
32
+ const url = new URL(request.url);
33
+ if (url.pathname !== path) {
34
+ return false;
35
+ }
36
+ const upgradeHeader = request.headers.get("upgrade");
37
+ if (upgradeHeader?.toLowerCase() !== "websocket") {
38
+ return false;
39
+ }
40
+ const protocol = request.headers.get("sec-websocket-protocol");
41
+ if (!protocol?.includes("graphql-transport-ws")) {
42
+ return false;
43
+ }
44
+ const success = server.upgrade(request, {
45
+ data: {}
46
+ // Will be populated in open handler
47
+ });
48
+ return success;
49
+ };
50
+ const websocket = {
51
+ open: (ws) => {
52
+ const setupEffect = effect.Effect.gen(function* () {
53
+ const messageQueue = yield* effect.Queue.unbounded();
54
+ const closedDeferred = yield* effect.Deferred.make();
55
+ const effectSocket = {
56
+ protocol: ws.data?.effectSocket?.protocol || "graphql-transport-ws",
57
+ send: (data) => effect.Effect.try({
58
+ try: () => {
59
+ ws.send(data);
60
+ },
61
+ catch: (error) => new core.WebSocketError({ cause: error })
62
+ }),
63
+ close: (code, reason) => effect.Effect.sync(() => {
64
+ ws.close(code ?? 1e3, reason ?? "");
65
+ }),
66
+ messages: effect.Stream.fromQueue(messageQueue).pipe(effect.Stream.catchAll(() => effect.Stream.empty)),
67
+ closed: effect.Deferred.await(closedDeferred)
68
+ };
69
+ ws.data = {
70
+ messageQueue,
71
+ closedDeferred,
72
+ effectSocket
73
+ };
74
+ return effectSocket;
75
+ });
76
+ const handlerPromise = effect.Effect.runPromise(
77
+ setupEffect.pipe(
78
+ effect.Effect.flatMap((effectSocket) => handler(effectSocket)),
79
+ effect.Effect.catchAllCause(() => effect.Effect.void)
80
+ )
81
+ );
82
+ activeHandlers.set(ws, handlerPromise);
83
+ },
84
+ message: (ws, message) => {
85
+ const data = ws.data;
86
+ if (data?.messageQueue) {
87
+ const messageStr = typeof message === "string" ? message : message.toString();
88
+ effect.Effect.runPromise(effect.Queue.offer(data.messageQueue, messageStr)).catch(() => {
89
+ });
90
+ }
91
+ },
92
+ close: (ws, code, reason) => {
93
+ const data = ws.data;
94
+ if (data) {
95
+ effect.Effect.runPromise(
96
+ effect.Effect.all([
97
+ effect.Queue.shutdown(data.messageQueue),
98
+ effect.Deferred.succeed(data.closedDeferred, { code, reason })
99
+ ])
100
+ ).catch(() => {
101
+ });
102
+ }
103
+ activeHandlers.delete(ws);
104
+ },
105
+ error: (ws, error) => {
106
+ const data = ws.data;
107
+ if (data) {
108
+ effect.Effect.runPromise(
109
+ effect.Deferred.fail(data.closedDeferred, new core.WebSocketError({ cause: error }))
110
+ ).catch(() => {
111
+ });
112
+ }
113
+ }
114
+ };
115
+ return { upgrade, websocket };
116
+ };
117
+ exports.toBunEffectWebSocket = (ws) => effect.Effect.gen(function* () {
118
+ const messageQueue = yield* effect.Queue.unbounded();
119
+ const closedDeferred = yield* effect.Deferred.make();
120
+ const effectSocket = {
121
+ protocol: "graphql-transport-ws",
122
+ send: (data) => effect.Effect.try({
123
+ try: () => {
124
+ ws.send(data);
125
+ },
126
+ catch: (error) => new core.WebSocketError({ cause: error })
127
+ }),
128
+ close: (code, reason) => effect.Effect.sync(() => {
129
+ ws.close(code ?? 1e3, reason ?? "");
130
+ }),
131
+ messages: effect.Stream.fromQueue(messageQueue).pipe(effect.Stream.catchAll(() => effect.Stream.empty)),
132
+ closed: effect.Deferred.await(closedDeferred)
133
+ };
134
+ ws.data = {
135
+ messageQueue,
136
+ closedDeferred,
137
+ effectSocket
138
+ };
139
+ return effectSocket;
140
+ });
141
+ }
142
+ });
143
+ var serve = (router, layer, options = {}) => {
144
+ const { port = 4e3, host = "0.0.0.0", onStart, subscriptions } = options;
145
+ if (subscriptions) {
146
+ serveWithSubscriptions(router, layer, port, host, subscriptions, onStart);
147
+ } else {
148
+ const app = router.pipe(
149
+ effect.Effect.catchAllCause((cause) => effect.Effect.die(cause)),
150
+ platform.HttpServer.serve()
151
+ );
152
+ const serverLayer = platformBun.BunHttpServer.layer({ port });
153
+ const fullLayer = effect.Layer.merge(serverLayer, layer);
154
+ if (onStart) {
155
+ onStart(`http://${host === "0.0.0.0" ? "localhost" : host}:${port}`);
156
+ }
157
+ platformBun.BunRuntime.runMain(effect.Layer.launch(effect.Layer.provide(app, fullLayer)));
158
+ }
159
+ };
160
+ function serveWithSubscriptions(router, layer, port, host, subscriptions, onStart) {
161
+ const importWs = effect.Effect.tryPromise({
162
+ try: () => Promise.resolve().then(() => (init_ws(), ws_exports)),
163
+ catch: (error) => error
164
+ });
165
+ effect.Effect.runPromise(
166
+ importWs.pipe(
167
+ effect.Effect.catchAll(
168
+ (error) => effect.Effect.logError("Failed to load WebSocket support", error).pipe(
169
+ effect.Effect.andThen(effect.Effect.sync(() => process.exit(1))),
170
+ effect.Effect.andThen(effect.Effect.fail(error))
171
+ )
172
+ )
173
+ )
174
+ ).then(({ createBunWSHandlers: createBunWSHandlers2 }) => {
175
+ const { handler } = platform.HttpApp.toWebHandlerLayer(router, layer);
176
+ const { upgrade, websocket } = createBunWSHandlers2(
177
+ subscriptions.schema,
178
+ layer,
179
+ {
180
+ path: subscriptions.path,
181
+ complexity: subscriptions.complexity,
182
+ fieldComplexities: subscriptions.fieldComplexities,
183
+ onConnect: subscriptions.onConnect,
184
+ onDisconnect: subscriptions.onDisconnect,
185
+ onSubscribe: subscriptions.onSubscribe,
186
+ onComplete: subscriptions.onComplete,
187
+ onError: subscriptions.onError
188
+ }
189
+ );
190
+ const server = Bun.serve({
191
+ port,
192
+ hostname: host,
193
+ fetch: async (request, server2) => {
194
+ if (upgrade(request, server2)) {
195
+ return new Response(null, { status: 101 });
196
+ }
197
+ return handler(request);
198
+ },
199
+ websocket
200
+ });
201
+ if (onStart) {
202
+ onStart(`http://${host === "0.0.0.0" ? "localhost" : host}:${port}`);
203
+ }
204
+ process.on("SIGINT", () => {
205
+ server.stop();
206
+ process.exit(0);
207
+ });
208
+ process.on("SIGTERM", () => {
209
+ server.stop();
210
+ process.exit(0);
211
+ });
212
+ });
213
+ }
214
+
215
+ // src/index.ts
216
+ init_ws();
217
+ var createBunSSEHandler = (schema, layer, options) => {
218
+ const sseHandler = core.makeGraphQLSSEHandler(schema, layer, options);
219
+ return async (request) => {
220
+ const accept = request.headers.get("accept") ?? "";
221
+ if (!accept.includes("text/event-stream") && !accept.includes("*/*")) {
222
+ return new Response(
223
+ JSON.stringify({
224
+ errors: [{ message: "Client must accept text/event-stream" }]
225
+ }),
226
+ { status: 406, headers: { "Content-Type": "application/json" } }
227
+ );
228
+ }
229
+ let subscriptionRequest;
230
+ try {
231
+ const body = await request.json();
232
+ if (typeof body.query !== "string") {
233
+ throw new Error("Missing query");
234
+ }
235
+ subscriptionRequest = {
236
+ query: body.query,
237
+ variables: body.variables,
238
+ operationName: body.operationName,
239
+ extensions: body.extensions
240
+ };
241
+ } catch {
242
+ return new Response(
243
+ JSON.stringify({
244
+ errors: [{ message: "Invalid GraphQL request body" }]
245
+ }),
246
+ { status: 400, headers: { "Content-Type": "application/json" } }
247
+ );
248
+ }
249
+ const eventStream = sseHandler(subscriptionRequest, request.headers);
250
+ const readableStream = new ReadableStream({
251
+ async start(controller) {
252
+ const encoder = new TextEncoder();
253
+ await effect.Effect.runPromise(
254
+ effect.Stream.runForEach(
255
+ eventStream,
256
+ (event) => effect.Effect.sync(() => {
257
+ const message = core.formatSSEMessage(event);
258
+ controller.enqueue(encoder.encode(message));
259
+ })
260
+ ).pipe(
261
+ effect.Effect.catchAll((error) => effect.Effect.logWarning("SSE stream error", error)),
262
+ effect.Effect.ensuring(effect.Effect.sync(() => controller.close()))
263
+ )
264
+ );
265
+ }
266
+ });
267
+ return new Response(readableStream, {
268
+ status: 200,
269
+ headers: core.SSE_HEADERS
270
+ });
271
+ };
272
+ };
273
+ var createBunSSEHandlers = (schema, layer, options) => {
274
+ const path = options?.path ?? "/graphql/stream";
275
+ const handler = createBunSSEHandler(schema, layer, options);
276
+ return {
277
+ path,
278
+ shouldHandle: (request) => {
279
+ if (request.method !== "POST") return false;
280
+ const url = new URL(request.url);
281
+ return url.pathname === path;
282
+ },
283
+ handle: handler
284
+ };
285
+ };
286
+
287
+ exports.createBunSSEHandler = createBunSSEHandler;
288
+ exports.createBunSSEHandlers = createBunSSEHandlers;
289
+ exports.serve = serve;
290
+ //# sourceMappingURL=index.cjs.map
291
+ //# sourceMappingURL=index.cjs.map
package/index.cjs.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ws.ts","../src/serve.ts","../src/index.ts","../src/sse.ts"],"names":["createBunWSHandlers","toBunEffectWebSocket","makeGraphQLWSHandler","Effect","Queue","Deferred","WebSocketError","Stream","HttpServer","BunHttpServer","Layer","BunRuntime","HttpApp","server","makeGraphQLSSEHandler","formatSSEMessage","SSE_HEADERS"],"mappings":"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","file":"index.cjs","sourcesContent":["import { Effect, Stream, Queue, Deferred, Layer } from \"effect\"\nimport { GraphQLSchema } from \"graphql\"\nimport {\n makeGraphQLWSHandler,\n type EffectWebSocket,\n type GraphQLWSOptions,\n WebSocketError,\n type CloseEvent,\n} from \"@effect-gql/core\"\nimport type { Server, ServerWebSocket } from \"bun\"\n\n/**\n * Data attached to each WebSocket connection\n */\ninterface WebSocketData {\n messageQueue: Queue.Queue<string>\n closedDeferred: Deferred.Deferred<CloseEvent, WebSocketError>\n effectSocket: EffectWebSocket\n}\n\n/**\n * Options for Bun WebSocket server\n */\nexport interface BunWSOptions<R> extends GraphQLWSOptions<R> {\n /**\n * Path for WebSocket connections.\n * @default \"/graphql\"\n */\n readonly path?: string\n}\n\n/**\n * Create WebSocket handlers for Bun.serve().\n *\n * Bun has built-in WebSocket support that's configured as part of Bun.serve().\n * This function returns the handlers needed to integrate GraphQL subscriptions.\n *\n * @param schema - The GraphQL schema with subscription definitions\n * @param layer - Effect layer providing services required by resolvers\n * @param options - Optional configuration and lifecycle hooks\n * @returns Object containing upgrade check and WebSocket handlers\n *\n * @example\n * ```typescript\n * const { upgrade, websocket } = createBunWSHandlers(schema, serviceLayer)\n *\n * Bun.serve({\n * port: 4000,\n * fetch(req, server) {\n * // Try WebSocket upgrade first\n * if (upgrade(req, server)) {\n * return // Upgraded to WebSocket\n * }\n * // Handle HTTP requests...\n * },\n * websocket,\n * })\n * ```\n */\nexport const createBunWSHandlers = <R>(\n schema: GraphQLSchema,\n layer: Layer.Layer<R>,\n options?: BunWSOptions<R>\n): {\n /**\n * Check if request should upgrade to WebSocket and perform upgrade.\n * Returns true if upgraded, false otherwise.\n */\n upgrade: (request: Request, server: Server<WebSocketData>) => boolean\n /**\n * WebSocket event handlers for Bun.serve()\n */\n websocket: {\n open: (ws: ServerWebSocket<WebSocketData>) => void\n message: (ws: ServerWebSocket<WebSocketData>, message: string | Buffer) => void\n close: (ws: ServerWebSocket<WebSocketData>, code: number, reason: string) => void\n error: (ws: ServerWebSocket<WebSocketData>, error: Error) => void\n }\n} => {\n const path = options?.path ?? \"/graphql\"\n const handler = makeGraphQLWSHandler(schema, layer, options)\n\n // Track active connection handlers for cleanup\n const activeHandlers = new Map<ServerWebSocket<WebSocketData>, Promise<void>>()\n\n const upgrade = (request: Request, server: Server<WebSocketData>): boolean => {\n const url = new URL(request.url)\n\n // Check if this is a WebSocket upgrade request for the GraphQL path\n if (url.pathname !== path) {\n return false\n }\n\n const upgradeHeader = request.headers.get(\"upgrade\")\n if (upgradeHeader?.toLowerCase() !== \"websocket\") {\n return false\n }\n\n // Check for correct subprotocol\n const protocol = request.headers.get(\"sec-websocket-protocol\")\n if (!protocol?.includes(\"graphql-transport-ws\")) {\n return false\n }\n\n // Perform upgrade - data will be set in open handler\n const success = server.upgrade(request, {\n data: {} as WebSocketData, // Will be populated in open handler\n })\n\n return success\n }\n\n const websocket = {\n open: (ws: ServerWebSocket<WebSocketData>) => {\n // Create Effect-based socket wrapper\n const setupEffect = Effect.gen(function* () {\n const messageQueue = yield* Queue.unbounded<string>()\n const closedDeferred = yield* Deferred.make<CloseEvent, WebSocketError>()\n\n const effectSocket: EffectWebSocket = {\n protocol: ws.data?.effectSocket?.protocol || \"graphql-transport-ws\",\n\n send: (data: string) =>\n Effect.try({\n try: () => {\n ws.send(data)\n },\n catch: (error) => new WebSocketError({ cause: error }),\n }),\n\n close: (code?: number, reason?: string) =>\n Effect.sync(() => {\n ws.close(code ?? 1000, reason ?? \"\")\n }),\n\n messages: Stream.fromQueue(messageQueue).pipe(Stream.catchAll(() => Stream.empty)),\n\n closed: Deferred.await(closedDeferred),\n }\n\n // Store in WebSocket data\n ws.data = {\n messageQueue,\n closedDeferred,\n effectSocket,\n }\n\n return effectSocket\n })\n\n // Run setup and handler\n const handlerPromise = Effect.runPromise(\n setupEffect.pipe(\n Effect.flatMap((effectSocket) => handler(effectSocket)),\n Effect.catchAllCause(() => Effect.void)\n )\n )\n\n activeHandlers.set(ws, handlerPromise)\n },\n\n message: (ws: ServerWebSocket<WebSocketData>, message: string | Buffer) => {\n const data = ws.data as WebSocketData | undefined\n if (data?.messageQueue) {\n const messageStr = typeof message === \"string\" ? message : message.toString()\n Effect.runPromise(Queue.offer(data.messageQueue, messageStr)).catch(() => {\n // Queue might be shutdown\n })\n }\n },\n\n close: (ws: ServerWebSocket<WebSocketData>, code: number, reason: string) => {\n const data = ws.data as WebSocketData | undefined\n if (data) {\n Effect.runPromise(\n Effect.all([\n Queue.shutdown(data.messageQueue),\n Deferred.succeed(data.closedDeferred, { code, reason }),\n ])\n ).catch(() => {\n // Already completed\n })\n }\n activeHandlers.delete(ws)\n },\n\n error: (ws: ServerWebSocket<WebSocketData>, error: Error) => {\n const data = ws.data as WebSocketData | undefined\n if (data) {\n Effect.runPromise(\n Deferred.fail(data.closedDeferred, new WebSocketError({ cause: error }))\n ).catch(() => {\n // Already completed\n })\n }\n },\n }\n\n return { upgrade, websocket }\n}\n\n/**\n * Convert a Bun ServerWebSocket to an EffectWebSocket.\n *\n * This is a lower-level utility for custom WebSocket handling.\n * Most users should use createBunWSHandlers() instead.\n *\n * @param ws - The Bun ServerWebSocket instance\n * @returns An EffectWebSocket that can be used with makeGraphQLWSHandler\n */\nexport const toBunEffectWebSocket = (\n ws: ServerWebSocket<WebSocketData>\n): Effect.Effect<EffectWebSocket, never, never> =>\n Effect.gen(function* () {\n const messageQueue = yield* Queue.unbounded<string>()\n const closedDeferred = yield* Deferred.make<CloseEvent, WebSocketError>()\n\n const effectSocket: EffectWebSocket = {\n protocol: \"graphql-transport-ws\",\n\n send: (data: string) =>\n Effect.try({\n try: () => {\n ws.send(data)\n },\n catch: (error) => new WebSocketError({ cause: error }),\n }),\n\n close: (code?: number, reason?: string) =>\n Effect.sync(() => {\n ws.close(code ?? 1000, reason ?? \"\")\n }),\n\n messages: Stream.fromQueue(messageQueue).pipe(Stream.catchAll(() => Stream.empty)),\n\n closed: Deferred.await(closedDeferred),\n }\n\n // Store in WebSocket data for event handlers\n ws.data = {\n messageQueue,\n closedDeferred,\n effectSocket,\n }\n\n return effectSocket\n })\n","import { Effect, Layer } from \"effect\"\nimport { HttpApp, HttpRouter, HttpServer } from \"@effect/platform\"\nimport { BunHttpServer, BunRuntime } from \"@effect/platform-bun\"\nimport type { GraphQLSchema } from \"graphql\"\nimport type { GraphQLWSOptions } from \"@effect-gql/core\"\n\n/**\n * Configuration for WebSocket subscriptions\n */\nexport interface SubscriptionsConfig<R> extends GraphQLWSOptions<R> {\n /**\n * The GraphQL schema (required for subscriptions).\n * Must be the same schema used to create the router.\n */\n readonly schema: GraphQLSchema\n /**\n * Path for WebSocket connections.\n * @default \"/graphql\"\n */\n readonly path?: string\n}\n\n/**\n * Options for the Bun GraphQL server\n */\nexport interface ServeOptions<R = never> {\n /** Port to listen on (default: 4000) */\n readonly port?: number\n /** Hostname to bind to (default: \"0.0.0.0\") */\n readonly host?: string\n /** Callback when server starts */\n readonly onStart?: (url: string) => void\n /**\n * Enable WebSocket subscriptions.\n * When provided, the server will handle WebSocket upgrade requests\n * for GraphQL subscriptions using the graphql-ws protocol.\n */\n readonly subscriptions?: SubscriptionsConfig<R>\n}\n\n/**\n * Start a Bun HTTP server with the given router.\n *\n * This is the main entry point for running a GraphQL server on Bun.\n * It handles all the Effect runtime setup and server lifecycle.\n *\n * @param router - The HttpRouter to serve (typically from makeGraphQLRouter or toRouter)\n * @param layer - Layer providing the router's service dependencies\n * @param options - Server configuration options\n *\n * @example\n * ```typescript\n * import { makeGraphQLRouter } from \"@effect-gql/core\"\n * import { serve } from \"@effect-gql/bun\"\n *\n * const schema = GraphQLSchemaBuilder.empty\n * .query(\"hello\", { type: S.String, resolve: () => Effect.succeed(\"world\") })\n * .buildSchema()\n *\n * const router = makeGraphQLRouter(schema, Layer.empty, { graphiql: true })\n *\n * // Without subscriptions\n * serve(router, serviceLayer, {\n * port: 4000,\n * onStart: (url) => console.log(`Server running at ${url}`)\n * })\n *\n * // With subscriptions\n * serve(router, serviceLayer, {\n * port: 4000,\n * subscriptions: { schema },\n * onStart: (url) => console.log(`Server running at ${url}`)\n * })\n * ```\n */\nexport const serve = <E, R, RE>(\n router: HttpRouter.HttpRouter<E, R>,\n layer: Layer.Layer<R, RE>,\n options: ServeOptions<R> = {}\n): void => {\n const { port = 4000, host = \"0.0.0.0\", onStart, subscriptions } = options\n\n if (subscriptions) {\n // With WebSocket subscriptions - use Bun.serve() directly\n serveWithSubscriptions(router, layer, port, host, subscriptions, onStart)\n } else {\n // Without subscriptions - use the standard Effect approach\n const app = router.pipe(\n Effect.catchAllCause((cause) => Effect.die(cause)),\n HttpServer.serve()\n )\n\n const serverLayer = BunHttpServer.layer({ port })\n const fullLayer = Layer.merge(serverLayer, layer)\n\n if (onStart) {\n onStart(`http://${host === \"0.0.0.0\" ? \"localhost\" : host}:${port}`)\n }\n\n BunRuntime.runMain(Layer.launch(Layer.provide(app, fullLayer)))\n }\n}\n\n/**\n * Internal implementation for serving with WebSocket subscriptions.\n * Uses Bun.serve() directly to enable WebSocket support.\n */\nfunction serveWithSubscriptions<E, R, RE>(\n router: HttpRouter.HttpRouter<E, R>,\n layer: Layer.Layer<R, RE>,\n port: number,\n host: string,\n subscriptions: SubscriptionsConfig<R>,\n onStart?: (url: string) => void\n): void {\n // Dynamically import ws module to keep it optional\n const importWs = Effect.tryPromise({\n try: () => import(\"./ws\"),\n catch: (error) => error as Error,\n })\n\n Effect.runPromise(\n importWs.pipe(\n Effect.catchAll((error) =>\n Effect.logError(\"Failed to load WebSocket support\", error).pipe(\n Effect.andThen(Effect.sync(() => process.exit(1))),\n Effect.andThen(Effect.fail(error))\n )\n )\n )\n ).then(({ createBunWSHandlers }) => {\n // Create the web handler from the Effect router\n const { handler } = HttpApp.toWebHandlerLayer(router, layer)\n\n // Create WebSocket handlers\n const { upgrade, websocket } = createBunWSHandlers(\n subscriptions.schema,\n layer as Layer.Layer<R>,\n {\n path: subscriptions.path,\n complexity: subscriptions.complexity,\n fieldComplexities: subscriptions.fieldComplexities,\n onConnect: subscriptions.onConnect,\n onDisconnect: subscriptions.onDisconnect,\n onSubscribe: subscriptions.onSubscribe,\n onComplete: subscriptions.onComplete,\n onError: subscriptions.onError,\n }\n )\n\n // Start Bun server with WebSocket support\n const server = Bun.serve({\n port,\n hostname: host,\n fetch: async (request, server) => {\n // Try WebSocket upgrade first\n if (upgrade(request, server)) {\n return new Response(null, { status: 101 })\n }\n\n // Handle HTTP requests\n return handler(request)\n },\n websocket,\n })\n\n if (onStart) {\n onStart(`http://${host === \"0.0.0.0\" ? \"localhost\" : host}:${port}`)\n }\n\n // Handle shutdown\n process.on(\"SIGINT\", () => {\n server.stop()\n process.exit(0)\n })\n\n process.on(\"SIGTERM\", () => {\n server.stop()\n process.exit(0)\n })\n })\n}\n","export { serve, type ServeOptions, type SubscriptionsConfig } from \"./serve\"\n\n// WebSocket subscription support\nexport { createBunWSHandlers, toBunEffectWebSocket, type BunWSOptions } from \"./ws\"\n\n// SSE (Server-Sent Events) subscription support\nexport { createBunSSEHandler, createBunSSEHandlers, type BunSSEOptions } from \"./sse\"\n","import { Effect, Layer, Stream } from \"effect\"\nimport { GraphQLSchema } from \"graphql\"\nimport {\n makeGraphQLSSEHandler,\n formatSSEMessage,\n SSE_HEADERS,\n type GraphQLSSEOptions,\n type SSESubscriptionRequest,\n} from \"@effect-gql/core\"\n\n/**\n * Options for Bun SSE handler\n */\nexport interface BunSSEOptions<R> extends GraphQLSSEOptions<R> {\n /**\n * Path for SSE connections.\n * @default \"/graphql/stream\"\n */\n readonly path?: string\n}\n\n/**\n * Create an SSE handler for Bun.serve().\n *\n * This function creates a handler that returns a streaming Response for SSE\n * subscription requests. It's designed to integrate with Bun.serve()'s fetch handler.\n *\n * @param schema - The GraphQL schema with subscription definitions\n * @param layer - Effect layer providing services required by resolvers\n * @param options - Optional lifecycle hooks and configuration\n * @returns A function that handles SSE requests and returns a Response\n *\n * @example\n * ```typescript\n * const sseHandler = createBunSSEHandler(schema, serviceLayer, {\n * path: \"/graphql/stream\",\n * })\n *\n * Bun.serve({\n * port: 4000,\n * fetch(req, server) {\n * const url = new URL(req.url)\n *\n * // Handle SSE subscriptions\n * if (url.pathname === \"/graphql/stream\" && req.method === \"POST\") {\n * return sseHandler(req)\n * }\n *\n * // Handle other requests...\n * },\n * })\n * ```\n */\nexport const createBunSSEHandler = <R>(\n schema: GraphQLSchema,\n layer: Layer.Layer<R>,\n options?: BunSSEOptions<R>\n): ((request: Request) => Promise<Response>) => {\n const sseHandler = makeGraphQLSSEHandler(schema, layer, options)\n\n return async (request: Request): Promise<Response> => {\n // Check Accept header for SSE support\n const accept = request.headers.get(\"accept\") ?? \"\"\n if (!accept.includes(\"text/event-stream\") && !accept.includes(\"*/*\")) {\n return new Response(\n JSON.stringify({\n errors: [{ message: \"Client must accept text/event-stream\" }],\n }),\n { status: 406, headers: { \"Content-Type\": \"application/json\" } }\n )\n }\n\n // Read and parse the request body\n let subscriptionRequest: SSESubscriptionRequest\n try {\n const body = (await request.json()) as Record<string, unknown>\n if (typeof body.query !== \"string\") {\n throw new Error(\"Missing query\")\n }\n subscriptionRequest = {\n query: body.query,\n variables: body.variables as Record<string, unknown> | undefined,\n operationName: body.operationName as string | undefined,\n extensions: body.extensions as Record<string, unknown> | undefined,\n }\n } catch {\n return new Response(\n JSON.stringify({\n errors: [{ message: \"Invalid GraphQL request body\" }],\n }),\n { status: 400, headers: { \"Content-Type\": \"application/json\" } }\n )\n }\n\n // Get the event stream\n const eventStream = sseHandler(subscriptionRequest, request.headers)\n\n // Create a ReadableStream from the Effect Stream\n const readableStream = new ReadableStream({\n async start(controller) {\n const encoder = new TextEncoder()\n\n await Effect.runPromise(\n Stream.runForEach(eventStream, (event) =>\n Effect.sync(() => {\n const message = formatSSEMessage(event)\n controller.enqueue(encoder.encode(message))\n })\n ).pipe(\n Effect.catchAll((error) => Effect.logWarning(\"SSE stream error\", error)),\n Effect.ensuring(Effect.sync(() => controller.close()))\n )\n )\n },\n })\n\n return new Response(readableStream, {\n status: 200,\n headers: SSE_HEADERS,\n })\n }\n}\n\n/**\n * Create SSE handlers that integrate with Bun.serve().\n *\n * This returns an object with methods to check if a request should be\n * handled as SSE and to handle it.\n *\n * @param schema - The GraphQL schema with subscription definitions\n * @param layer - Effect layer providing services required by resolvers\n * @param options - Optional lifecycle hooks and configuration\n *\n * @example\n * ```typescript\n * const { upgrade: wsUpgrade, websocket } = createBunWSHandlers(schema, layer)\n * const sse = createBunSSEHandlers(schema, layer)\n *\n * Bun.serve({\n * port: 4000,\n * fetch(req, server) {\n * // Try WebSocket upgrade first\n * if (wsUpgrade(req, server)) {\n * return\n * }\n *\n * // Try SSE subscriptions\n * if (sse.shouldHandle(req)) {\n * return sse.handle(req)\n * }\n *\n * // Handle other requests...\n * },\n * websocket,\n * })\n * ```\n */\nexport const createBunSSEHandlers = <R>(\n schema: GraphQLSchema,\n layer: Layer.Layer<R>,\n options?: BunSSEOptions<R>\n): {\n /** Path this SSE handler responds to */\n readonly path: string\n /** Check if a request should be handled as SSE */\n shouldHandle: (request: Request) => boolean\n /** Handle an SSE request */\n handle: (request: Request) => Promise<Response>\n} => {\n const path = options?.path ?? \"/graphql/stream\"\n const handler = createBunSSEHandler(schema, layer, options)\n\n return {\n path,\n shouldHandle: (request: Request) => {\n if (request.method !== \"POST\") return false\n const url = new URL(request.url)\n return url.pathname === path\n },\n handle: handler,\n }\n}\n"]}