@effect-gql/bun 0.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/LICENSE +7 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -0
- package/dist/serve.d.ts +73 -0
- package/dist/serve.d.ts.map +1 -0
- package/dist/serve.js +145 -0
- package/dist/serve.js.map +1 -0
- package/dist/sse.d.ts +89 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +135 -0
- package/dist/sse.js.map +1 -0
- package/dist/ws.d.ts +78 -0
- package/dist/ws.d.ts.map +1 -0
- package/dist/ws.js +160 -0
- package/dist/ws.js.map +1 -0
- package/package.json +52 -0
- package/src/index.ts +7 -0
- package/src/serve.ts +182 -0
- package/src/sse.ts +182 -0
- package/src/ws.ts +247 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
Copyright 2025 Nick Fisher
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
4
|
+
|
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { serve, type ServeOptions, type SubscriptionsConfig } from "./serve";
|
|
2
|
+
export { createBunWSHandlers, toBunEffectWebSocket, type BunWSOptions } from "./ws";
|
|
3
|
+
export { createBunSSEHandler, createBunSSEHandlers, type BunSSEOptions } from "./sse";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,YAAY,EAAE,KAAK,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAG5E,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,KAAK,YAAY,EAAE,MAAM,MAAM,CAAA;AAGnF,OAAO,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,KAAK,aAAa,EAAE,MAAM,OAAO,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createBunSSEHandlers = exports.createBunSSEHandler = exports.toBunEffectWebSocket = exports.createBunWSHandlers = exports.serve = void 0;
|
|
4
|
+
var serve_1 = require("./serve");
|
|
5
|
+
Object.defineProperty(exports, "serve", { enumerable: true, get: function () { return serve_1.serve; } });
|
|
6
|
+
// WebSocket subscription support
|
|
7
|
+
var ws_1 = require("./ws");
|
|
8
|
+
Object.defineProperty(exports, "createBunWSHandlers", { enumerable: true, get: function () { return ws_1.createBunWSHandlers; } });
|
|
9
|
+
Object.defineProperty(exports, "toBunEffectWebSocket", { enumerable: true, get: function () { return ws_1.toBunEffectWebSocket; } });
|
|
10
|
+
// SSE (Server-Sent Events) subscription support
|
|
11
|
+
var sse_1 = require("./sse");
|
|
12
|
+
Object.defineProperty(exports, "createBunSSEHandler", { enumerable: true, get: function () { return sse_1.createBunSSEHandler; } });
|
|
13
|
+
Object.defineProperty(exports, "createBunSSEHandlers", { enumerable: true, get: function () { return sse_1.createBunSSEHandlers; } });
|
|
14
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,iCAA4E;AAAnE,8FAAA,KAAK,OAAA;AAEd,iCAAiC;AACjC,2BAAmF;AAA1E,yGAAA,mBAAmB,OAAA;AAAE,0GAAA,oBAAoB,OAAA;AAElD,gDAAgD;AAChD,6BAAqF;AAA5E,0GAAA,mBAAmB,OAAA;AAAE,2GAAA,oBAAoB,OAAA"}
|
package/dist/serve.d.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { HttpRouter } from "@effect/platform";
|
|
3
|
+
import type { GraphQLSchema } from "graphql";
|
|
4
|
+
import type { GraphQLWSOptions } from "@effect-gql/core";
|
|
5
|
+
/**
|
|
6
|
+
* Configuration for WebSocket subscriptions
|
|
7
|
+
*/
|
|
8
|
+
export interface SubscriptionsConfig<R> extends GraphQLWSOptions<R> {
|
|
9
|
+
/**
|
|
10
|
+
* The GraphQL schema (required for subscriptions).
|
|
11
|
+
* Must be the same schema used to create the router.
|
|
12
|
+
*/
|
|
13
|
+
readonly schema: GraphQLSchema;
|
|
14
|
+
/**
|
|
15
|
+
* Path for WebSocket connections.
|
|
16
|
+
* @default "/graphql"
|
|
17
|
+
*/
|
|
18
|
+
readonly path?: string;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Options for the Bun GraphQL server
|
|
22
|
+
*/
|
|
23
|
+
export interface ServeOptions<R = never> {
|
|
24
|
+
/** Port to listen on (default: 4000) */
|
|
25
|
+
readonly port?: number;
|
|
26
|
+
/** Hostname to bind to (default: "0.0.0.0") */
|
|
27
|
+
readonly host?: string;
|
|
28
|
+
/** Callback when server starts */
|
|
29
|
+
readonly onStart?: (url: string) => void;
|
|
30
|
+
/**
|
|
31
|
+
* Enable WebSocket subscriptions.
|
|
32
|
+
* When provided, the server will handle WebSocket upgrade requests
|
|
33
|
+
* for GraphQL subscriptions using the graphql-ws protocol.
|
|
34
|
+
*/
|
|
35
|
+
readonly subscriptions?: SubscriptionsConfig<R>;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Start a Bun HTTP server with the given router.
|
|
39
|
+
*
|
|
40
|
+
* This is the main entry point for running a GraphQL server on Bun.
|
|
41
|
+
* It handles all the Effect runtime setup and server lifecycle.
|
|
42
|
+
*
|
|
43
|
+
* @param router - The HttpRouter to serve (typically from makeGraphQLRouter or toRouter)
|
|
44
|
+
* @param layer - Layer providing the router's service dependencies
|
|
45
|
+
* @param options - Server configuration options
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* import { makeGraphQLRouter } from "@effect-gql/core"
|
|
50
|
+
* import { serve } from "@effect-gql/bun"
|
|
51
|
+
*
|
|
52
|
+
* const schema = GraphQLSchemaBuilder.empty
|
|
53
|
+
* .query("hello", { type: S.String, resolve: () => Effect.succeed("world") })
|
|
54
|
+
* .buildSchema()
|
|
55
|
+
*
|
|
56
|
+
* const router = makeGraphQLRouter(schema, Layer.empty, { graphiql: true })
|
|
57
|
+
*
|
|
58
|
+
* // Without subscriptions
|
|
59
|
+
* serve(router, serviceLayer, {
|
|
60
|
+
* port: 4000,
|
|
61
|
+
* onStart: (url) => console.log(`Server running at ${url}`)
|
|
62
|
+
* })
|
|
63
|
+
*
|
|
64
|
+
* // With subscriptions
|
|
65
|
+
* serve(router, serviceLayer, {
|
|
66
|
+
* port: 4000,
|
|
67
|
+
* subscriptions: { schema },
|
|
68
|
+
* onStart: (url) => console.log(`Server running at ${url}`)
|
|
69
|
+
* })
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export declare const serve: <E, R, RE>(router: HttpRouter.HttpRouter<E, R>, layer: Layer.Layer<R, RE>, options?: ServeOptions<R>) => void;
|
|
73
|
+
//# sourceMappingURL=serve.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAE,MAAM,QAAQ,CAAA;AACtC,OAAO,EAAW,UAAU,EAAc,MAAM,kBAAkB,CAAA;AAElE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAC5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAA;AAExD;;GAEG;AACH,MAAM,WAAW,mBAAmB,CAAC,CAAC,CAAE,SAAQ,gBAAgB,CAAC,CAAC,CAAC;IACjE;;;OAGG;IACH,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,KAAK;IACrC,wCAAwC;IACxC,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,kCAAkC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAA;CAChD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,KAAK,GAAI,CAAC,EAAE,CAAC,EAAE,EAAE,EAC5B,QAAQ,UAAU,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,EACnC,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EACzB,UAAS,YAAY,CAAC,CAAC,CAAM,KAC5B,IAsBF,CAAA"}
|
package/dist/serve.js
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.serve = void 0;
|
|
37
|
+
const effect_1 = require("effect");
|
|
38
|
+
const platform_1 = require("@effect/platform");
|
|
39
|
+
const platform_bun_1 = require("@effect/platform-bun");
|
|
40
|
+
/**
|
|
41
|
+
* Start a Bun HTTP server with the given router.
|
|
42
|
+
*
|
|
43
|
+
* This is the main entry point for running a GraphQL server on Bun.
|
|
44
|
+
* It handles all the Effect runtime setup and server lifecycle.
|
|
45
|
+
*
|
|
46
|
+
* @param router - The HttpRouter to serve (typically from makeGraphQLRouter or toRouter)
|
|
47
|
+
* @param layer - Layer providing the router's service dependencies
|
|
48
|
+
* @param options - Server configuration options
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* import { makeGraphQLRouter } from "@effect-gql/core"
|
|
53
|
+
* import { serve } from "@effect-gql/bun"
|
|
54
|
+
*
|
|
55
|
+
* const schema = GraphQLSchemaBuilder.empty
|
|
56
|
+
* .query("hello", { type: S.String, resolve: () => Effect.succeed("world") })
|
|
57
|
+
* .buildSchema()
|
|
58
|
+
*
|
|
59
|
+
* const router = makeGraphQLRouter(schema, Layer.empty, { graphiql: true })
|
|
60
|
+
*
|
|
61
|
+
* // Without subscriptions
|
|
62
|
+
* serve(router, serviceLayer, {
|
|
63
|
+
* port: 4000,
|
|
64
|
+
* onStart: (url) => console.log(`Server running at ${url}`)
|
|
65
|
+
* })
|
|
66
|
+
*
|
|
67
|
+
* // With subscriptions
|
|
68
|
+
* serve(router, serviceLayer, {
|
|
69
|
+
* port: 4000,
|
|
70
|
+
* subscriptions: { schema },
|
|
71
|
+
* onStart: (url) => console.log(`Server running at ${url}`)
|
|
72
|
+
* })
|
|
73
|
+
* ```
|
|
74
|
+
*/
|
|
75
|
+
const serve = (router, layer, options = {}) => {
|
|
76
|
+
const { port = 4000, host = "0.0.0.0", onStart, subscriptions } = options;
|
|
77
|
+
if (subscriptions) {
|
|
78
|
+
// With WebSocket subscriptions - use Bun.serve() directly
|
|
79
|
+
serveWithSubscriptions(router, layer, port, host, subscriptions, onStart);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
// Without subscriptions - use the standard Effect approach
|
|
83
|
+
const app = router.pipe(effect_1.Effect.catchAllCause((cause) => effect_1.Effect.die(cause)), platform_1.HttpServer.serve());
|
|
84
|
+
const serverLayer = platform_bun_1.BunHttpServer.layer({ port });
|
|
85
|
+
const fullLayer = effect_1.Layer.merge(serverLayer, layer);
|
|
86
|
+
if (onStart) {
|
|
87
|
+
onStart(`http://${host === "0.0.0.0" ? "localhost" : host}:${port}`);
|
|
88
|
+
}
|
|
89
|
+
platform_bun_1.BunRuntime.runMain(effect_1.Layer.launch(effect_1.Layer.provide(app, fullLayer)));
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
exports.serve = serve;
|
|
93
|
+
/**
|
|
94
|
+
* Internal implementation for serving with WebSocket subscriptions.
|
|
95
|
+
* Uses Bun.serve() directly to enable WebSocket support.
|
|
96
|
+
*/
|
|
97
|
+
function serveWithSubscriptions(router, layer, port, host, subscriptions, onStart) {
|
|
98
|
+
// Dynamically import ws module to keep it optional
|
|
99
|
+
const importWs = effect_1.Effect.tryPromise({
|
|
100
|
+
try: () => Promise.resolve().then(() => __importStar(require("./ws"))),
|
|
101
|
+
catch: (error) => error,
|
|
102
|
+
});
|
|
103
|
+
effect_1.Effect.runPromise(importWs.pipe(effect_1.Effect.catchAll((error) => effect_1.Effect.logError("Failed to load WebSocket support", error).pipe(effect_1.Effect.andThen(effect_1.Effect.sync(() => process.exit(1))), effect_1.Effect.andThen(effect_1.Effect.fail(error)))))).then(({ createBunWSHandlers }) => {
|
|
104
|
+
// Create the web handler from the Effect router
|
|
105
|
+
const { handler } = platform_1.HttpApp.toWebHandlerLayer(router, layer);
|
|
106
|
+
// Create WebSocket handlers
|
|
107
|
+
const { upgrade, websocket } = createBunWSHandlers(subscriptions.schema, layer, {
|
|
108
|
+
path: subscriptions.path,
|
|
109
|
+
complexity: subscriptions.complexity,
|
|
110
|
+
fieldComplexities: subscriptions.fieldComplexities,
|
|
111
|
+
onConnect: subscriptions.onConnect,
|
|
112
|
+
onDisconnect: subscriptions.onDisconnect,
|
|
113
|
+
onSubscribe: subscriptions.onSubscribe,
|
|
114
|
+
onComplete: subscriptions.onComplete,
|
|
115
|
+
onError: subscriptions.onError,
|
|
116
|
+
});
|
|
117
|
+
// Start Bun server with WebSocket support
|
|
118
|
+
const server = Bun.serve({
|
|
119
|
+
port,
|
|
120
|
+
hostname: host,
|
|
121
|
+
fetch: async (request, server) => {
|
|
122
|
+
// Try WebSocket upgrade first
|
|
123
|
+
if (upgrade(request, server)) {
|
|
124
|
+
return new Response(null, { status: 101 });
|
|
125
|
+
}
|
|
126
|
+
// Handle HTTP requests
|
|
127
|
+
return handler(request);
|
|
128
|
+
},
|
|
129
|
+
websocket,
|
|
130
|
+
});
|
|
131
|
+
if (onStart) {
|
|
132
|
+
onStart(`http://${host === "0.0.0.0" ? "localhost" : host}:${port}`);
|
|
133
|
+
}
|
|
134
|
+
// Handle shutdown
|
|
135
|
+
process.on("SIGINT", () => {
|
|
136
|
+
server.stop();
|
|
137
|
+
process.exit(0);
|
|
138
|
+
});
|
|
139
|
+
process.on("SIGTERM", () => {
|
|
140
|
+
server.stop();
|
|
141
|
+
process.exit(0);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=serve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serve.js","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"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"}
|
package/dist/sse.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { Layer } from "effect";
|
|
2
|
+
import { GraphQLSchema } from "graphql";
|
|
3
|
+
import { type GraphQLSSEOptions } from "@effect-gql/core";
|
|
4
|
+
/**
|
|
5
|
+
* Options for Bun SSE handler
|
|
6
|
+
*/
|
|
7
|
+
export interface BunSSEOptions<R> extends GraphQLSSEOptions<R> {
|
|
8
|
+
/**
|
|
9
|
+
* Path for SSE connections.
|
|
10
|
+
* @default "/graphql/stream"
|
|
11
|
+
*/
|
|
12
|
+
readonly path?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Create an SSE handler for Bun.serve().
|
|
16
|
+
*
|
|
17
|
+
* This function creates a handler that returns a streaming Response for SSE
|
|
18
|
+
* subscription requests. It's designed to integrate with Bun.serve()'s fetch handler.
|
|
19
|
+
*
|
|
20
|
+
* @param schema - The GraphQL schema with subscription definitions
|
|
21
|
+
* @param layer - Effect layer providing services required by resolvers
|
|
22
|
+
* @param options - Optional lifecycle hooks and configuration
|
|
23
|
+
* @returns A function that handles SSE requests and returns a Response
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const sseHandler = createBunSSEHandler(schema, serviceLayer, {
|
|
28
|
+
* path: "/graphql/stream",
|
|
29
|
+
* })
|
|
30
|
+
*
|
|
31
|
+
* Bun.serve({
|
|
32
|
+
* port: 4000,
|
|
33
|
+
* fetch(req, server) {
|
|
34
|
+
* const url = new URL(req.url)
|
|
35
|
+
*
|
|
36
|
+
* // Handle SSE subscriptions
|
|
37
|
+
* if (url.pathname === "/graphql/stream" && req.method === "POST") {
|
|
38
|
+
* return sseHandler(req)
|
|
39
|
+
* }
|
|
40
|
+
*
|
|
41
|
+
* // Handle other requests...
|
|
42
|
+
* },
|
|
43
|
+
* })
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export declare const createBunSSEHandler: <R>(schema: GraphQLSchema, layer: Layer.Layer<R>, options?: BunSSEOptions<R>) => ((request: Request) => Promise<Response>);
|
|
47
|
+
/**
|
|
48
|
+
* Create SSE handlers that integrate with Bun.serve().
|
|
49
|
+
*
|
|
50
|
+
* This returns an object with methods to check if a request should be
|
|
51
|
+
* handled as SSE and to handle it.
|
|
52
|
+
*
|
|
53
|
+
* @param schema - The GraphQL schema with subscription definitions
|
|
54
|
+
* @param layer - Effect layer providing services required by resolvers
|
|
55
|
+
* @param options - Optional lifecycle hooks and configuration
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```typescript
|
|
59
|
+
* const { upgrade: wsUpgrade, websocket } = createBunWSHandlers(schema, layer)
|
|
60
|
+
* const sse = createBunSSEHandlers(schema, layer)
|
|
61
|
+
*
|
|
62
|
+
* Bun.serve({
|
|
63
|
+
* port: 4000,
|
|
64
|
+
* fetch(req, server) {
|
|
65
|
+
* // Try WebSocket upgrade first
|
|
66
|
+
* if (wsUpgrade(req, server)) {
|
|
67
|
+
* return
|
|
68
|
+
* }
|
|
69
|
+
*
|
|
70
|
+
* // Try SSE subscriptions
|
|
71
|
+
* if (sse.shouldHandle(req)) {
|
|
72
|
+
* return sse.handle(req)
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* // Handle other requests...
|
|
76
|
+
* },
|
|
77
|
+
* websocket,
|
|
78
|
+
* })
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
export declare const createBunSSEHandlers: <R>(schema: GraphQLSchema, layer: Layer.Layer<R>, options?: BunSSEOptions<R>) => {
|
|
82
|
+
/** Path this SSE handler responds to */
|
|
83
|
+
readonly path: string;
|
|
84
|
+
/** Check if a request should be handled as SSE */
|
|
85
|
+
shouldHandle: (request: Request) => boolean;
|
|
86
|
+
/** Handle an SSE request */
|
|
87
|
+
handle: (request: Request) => Promise<Response>;
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=sse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,EAAU,MAAM,QAAQ,CAAA;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAIL,KAAK,iBAAiB,EAEvB,MAAM,kBAAkB,CAAA;AAEzB;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,CAAE,SAAQ,iBAAiB,CAAC,CAAC,CAAC;IAC5D;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,EACnC,QAAQ,aAAa,EACrB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EACrB,UAAU,aAAa,CAAC,CAAC,CAAC,KACzB,CAAC,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAgE1C,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,EACpC,QAAQ,aAAa,EACrB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EACrB,UAAU,aAAa,CAAC,CAAC,CAAC,KACzB;IACD,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,kDAAkD;IAClD,YAAY,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAA;IAC3C,4BAA4B;IAC5B,MAAM,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;CAchD,CAAA"}
|
package/dist/sse.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createBunSSEHandlers = exports.createBunSSEHandler = void 0;
|
|
4
|
+
const effect_1 = require("effect");
|
|
5
|
+
const core_1 = require("@effect-gql/core");
|
|
6
|
+
/**
|
|
7
|
+
* Create an SSE handler for Bun.serve().
|
|
8
|
+
*
|
|
9
|
+
* This function creates a handler that returns a streaming Response for SSE
|
|
10
|
+
* subscription requests. It's designed to integrate with Bun.serve()'s fetch handler.
|
|
11
|
+
*
|
|
12
|
+
* @param schema - The GraphQL schema with subscription definitions
|
|
13
|
+
* @param layer - Effect layer providing services required by resolvers
|
|
14
|
+
* @param options - Optional lifecycle hooks and configuration
|
|
15
|
+
* @returns A function that handles SSE requests and returns a Response
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const sseHandler = createBunSSEHandler(schema, serviceLayer, {
|
|
20
|
+
* path: "/graphql/stream",
|
|
21
|
+
* })
|
|
22
|
+
*
|
|
23
|
+
* Bun.serve({
|
|
24
|
+
* port: 4000,
|
|
25
|
+
* fetch(req, server) {
|
|
26
|
+
* const url = new URL(req.url)
|
|
27
|
+
*
|
|
28
|
+
* // Handle SSE subscriptions
|
|
29
|
+
* if (url.pathname === "/graphql/stream" && req.method === "POST") {
|
|
30
|
+
* return sseHandler(req)
|
|
31
|
+
* }
|
|
32
|
+
*
|
|
33
|
+
* // Handle other requests...
|
|
34
|
+
* },
|
|
35
|
+
* })
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
const createBunSSEHandler = (schema, layer, options) => {
|
|
39
|
+
const sseHandler = (0, core_1.makeGraphQLSSEHandler)(schema, layer, options);
|
|
40
|
+
return async (request) => {
|
|
41
|
+
// Check Accept header for SSE support
|
|
42
|
+
const accept = request.headers.get("accept") ?? "";
|
|
43
|
+
if (!accept.includes("text/event-stream") && !accept.includes("*/*")) {
|
|
44
|
+
return new Response(JSON.stringify({
|
|
45
|
+
errors: [{ message: "Client must accept text/event-stream" }],
|
|
46
|
+
}), { status: 406, headers: { "Content-Type": "application/json" } });
|
|
47
|
+
}
|
|
48
|
+
// Read and parse the request body
|
|
49
|
+
let subscriptionRequest;
|
|
50
|
+
try {
|
|
51
|
+
const body = (await request.json());
|
|
52
|
+
if (typeof body.query !== "string") {
|
|
53
|
+
throw new Error("Missing query");
|
|
54
|
+
}
|
|
55
|
+
subscriptionRequest = {
|
|
56
|
+
query: body.query,
|
|
57
|
+
variables: body.variables,
|
|
58
|
+
operationName: body.operationName,
|
|
59
|
+
extensions: body.extensions,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return new Response(JSON.stringify({
|
|
64
|
+
errors: [{ message: "Invalid GraphQL request body" }],
|
|
65
|
+
}), { status: 400, headers: { "Content-Type": "application/json" } });
|
|
66
|
+
}
|
|
67
|
+
// Get the event stream
|
|
68
|
+
const eventStream = sseHandler(subscriptionRequest, request.headers);
|
|
69
|
+
// Create a ReadableStream from the Effect Stream
|
|
70
|
+
const readableStream = new ReadableStream({
|
|
71
|
+
async start(controller) {
|
|
72
|
+
const encoder = new TextEncoder();
|
|
73
|
+
await effect_1.Effect.runPromise(effect_1.Stream.runForEach(eventStream, (event) => effect_1.Effect.sync(() => {
|
|
74
|
+
const message = (0, core_1.formatSSEMessage)(event);
|
|
75
|
+
controller.enqueue(encoder.encode(message));
|
|
76
|
+
})).pipe(effect_1.Effect.catchAll((error) => effect_1.Effect.logWarning("SSE stream error", error)), effect_1.Effect.ensuring(effect_1.Effect.sync(() => controller.close()))));
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
return new Response(readableStream, {
|
|
80
|
+
status: 200,
|
|
81
|
+
headers: core_1.SSE_HEADERS,
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
exports.createBunSSEHandler = createBunSSEHandler;
|
|
86
|
+
/**
|
|
87
|
+
* Create SSE handlers that integrate with Bun.serve().
|
|
88
|
+
*
|
|
89
|
+
* This returns an object with methods to check if a request should be
|
|
90
|
+
* handled as SSE and to handle it.
|
|
91
|
+
*
|
|
92
|
+
* @param schema - The GraphQL schema with subscription definitions
|
|
93
|
+
* @param layer - Effect layer providing services required by resolvers
|
|
94
|
+
* @param options - Optional lifecycle hooks and configuration
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const { upgrade: wsUpgrade, websocket } = createBunWSHandlers(schema, layer)
|
|
99
|
+
* const sse = createBunSSEHandlers(schema, layer)
|
|
100
|
+
*
|
|
101
|
+
* Bun.serve({
|
|
102
|
+
* port: 4000,
|
|
103
|
+
* fetch(req, server) {
|
|
104
|
+
* // Try WebSocket upgrade first
|
|
105
|
+
* if (wsUpgrade(req, server)) {
|
|
106
|
+
* return
|
|
107
|
+
* }
|
|
108
|
+
*
|
|
109
|
+
* // Try SSE subscriptions
|
|
110
|
+
* if (sse.shouldHandle(req)) {
|
|
111
|
+
* return sse.handle(req)
|
|
112
|
+
* }
|
|
113
|
+
*
|
|
114
|
+
* // Handle other requests...
|
|
115
|
+
* },
|
|
116
|
+
* websocket,
|
|
117
|
+
* })
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
const createBunSSEHandlers = (schema, layer, options) => {
|
|
121
|
+
const path = options?.path ?? "/graphql/stream";
|
|
122
|
+
const handler = (0, exports.createBunSSEHandler)(schema, layer, options);
|
|
123
|
+
return {
|
|
124
|
+
path,
|
|
125
|
+
shouldHandle: (request) => {
|
|
126
|
+
if (request.method !== "POST")
|
|
127
|
+
return false;
|
|
128
|
+
const url = new URL(request.url);
|
|
129
|
+
return url.pathname === path;
|
|
130
|
+
},
|
|
131
|
+
handle: handler,
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
exports.createBunSSEHandlers = createBunSSEHandlers;
|
|
135
|
+
//# sourceMappingURL=sse.js.map
|
package/dist/sse.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sse.js","sourceRoot":"","sources":["../src/sse.ts"],"names":[],"mappings":"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"}
|
package/dist/ws.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Effect, Queue, Deferred, Layer } from "effect";
|
|
2
|
+
import { GraphQLSchema } from "graphql";
|
|
3
|
+
import { type EffectWebSocket, type GraphQLWSOptions, WebSocketError, type CloseEvent } from "@effect-gql/core";
|
|
4
|
+
import type { Server, ServerWebSocket } from "bun";
|
|
5
|
+
/**
|
|
6
|
+
* Data attached to each WebSocket connection
|
|
7
|
+
*/
|
|
8
|
+
interface WebSocketData {
|
|
9
|
+
messageQueue: Queue.Queue<string>;
|
|
10
|
+
closedDeferred: Deferred.Deferred<CloseEvent, WebSocketError>;
|
|
11
|
+
effectSocket: EffectWebSocket;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Options for Bun WebSocket server
|
|
15
|
+
*/
|
|
16
|
+
export interface BunWSOptions<R> extends GraphQLWSOptions<R> {
|
|
17
|
+
/**
|
|
18
|
+
* Path for WebSocket connections.
|
|
19
|
+
* @default "/graphql"
|
|
20
|
+
*/
|
|
21
|
+
readonly path?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Create WebSocket handlers for Bun.serve().
|
|
25
|
+
*
|
|
26
|
+
* Bun has built-in WebSocket support that's configured as part of Bun.serve().
|
|
27
|
+
* This function returns the handlers needed to integrate GraphQL subscriptions.
|
|
28
|
+
*
|
|
29
|
+
* @param schema - The GraphQL schema with subscription definitions
|
|
30
|
+
* @param layer - Effect layer providing services required by resolvers
|
|
31
|
+
* @param options - Optional configuration and lifecycle hooks
|
|
32
|
+
* @returns Object containing upgrade check and WebSocket handlers
|
|
33
|
+
*
|
|
34
|
+
* @example
|
|
35
|
+
* ```typescript
|
|
36
|
+
* const { upgrade, websocket } = createBunWSHandlers(schema, serviceLayer)
|
|
37
|
+
*
|
|
38
|
+
* Bun.serve({
|
|
39
|
+
* port: 4000,
|
|
40
|
+
* fetch(req, server) {
|
|
41
|
+
* // Try WebSocket upgrade first
|
|
42
|
+
* if (upgrade(req, server)) {
|
|
43
|
+
* return // Upgraded to WebSocket
|
|
44
|
+
* }
|
|
45
|
+
* // Handle HTTP requests...
|
|
46
|
+
* },
|
|
47
|
+
* websocket,
|
|
48
|
+
* })
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export declare const createBunWSHandlers: <R>(schema: GraphQLSchema, layer: Layer.Layer<R>, options?: BunWSOptions<R>) => {
|
|
52
|
+
/**
|
|
53
|
+
* Check if request should upgrade to WebSocket and perform upgrade.
|
|
54
|
+
* Returns true if upgraded, false otherwise.
|
|
55
|
+
*/
|
|
56
|
+
upgrade: (request: Request, server: Server<WebSocketData>) => boolean;
|
|
57
|
+
/**
|
|
58
|
+
* WebSocket event handlers for Bun.serve()
|
|
59
|
+
*/
|
|
60
|
+
websocket: {
|
|
61
|
+
open: (ws: ServerWebSocket<WebSocketData>) => void;
|
|
62
|
+
message: (ws: ServerWebSocket<WebSocketData>, message: string | Buffer) => void;
|
|
63
|
+
close: (ws: ServerWebSocket<WebSocketData>, code: number, reason: string) => void;
|
|
64
|
+
error: (ws: ServerWebSocket<WebSocketData>, error: Error) => void;
|
|
65
|
+
};
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* Convert a Bun ServerWebSocket to an EffectWebSocket.
|
|
69
|
+
*
|
|
70
|
+
* This is a lower-level utility for custom WebSocket handling.
|
|
71
|
+
* Most users should use createBunWSHandlers() instead.
|
|
72
|
+
*
|
|
73
|
+
* @param ws - The Bun ServerWebSocket instance
|
|
74
|
+
* @returns An EffectWebSocket that can be used with makeGraphQLWSHandler
|
|
75
|
+
*/
|
|
76
|
+
export declare const toBunEffectWebSocket: (ws: ServerWebSocket<WebSocketData>) => Effect.Effect<EffectWebSocket, never, never>;
|
|
77
|
+
export {};
|
|
78
|
+
//# sourceMappingURL=ws.d.ts.map
|
package/dist/ws.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws.d.ts","sourceRoot":"","sources":["../src/ws.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/D,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AACvC,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,gBAAgB,EACrB,cAAc,EACd,KAAK,UAAU,EAChB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,KAAK,CAAA;AAElD;;GAEG;AACH,UAAU,aAAa;IACrB,YAAY,EAAE,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;IACjC,cAAc,EAAE,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC,CAAA;IAC7D,YAAY,EAAE,eAAe,CAAA;CAC9B;AAED;;GAEG;AACH,MAAM,WAAW,YAAY,CAAC,CAAC,CAAE,SAAQ,gBAAgB,CAAC,CAAC,CAAC;IAC1D;;;OAGG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,EACnC,QAAQ,aAAa,EACrB,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EACrB,UAAU,YAAY,CAAC,CAAC,CAAC,KACxB;IACD;;;OAGG;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,aAAa,CAAC,KAAK,OAAO,CAAA;IACrE;;OAEG;IACH,SAAS,EAAE;QACT,IAAI,EAAE,CAAC,EAAE,EAAE,eAAe,CAAC,aAAa,CAAC,KAAK,IAAI,CAAA;QAClD,OAAO,EAAE,CAAC,EAAE,EAAE,eAAe,CAAC,aAAa,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,CAAA;QAC/E,KAAK,EAAE,CAAC,EAAE,EAAE,eAAe,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;QACjF,KAAK,EAAE,CAAC,EAAE,EAAE,eAAe,CAAC,aAAa,CAAC,EAAE,KAAK,EAAE,KAAK,KAAK,IAAI,CAAA;KAClE,CAAA;CA0HF,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAC/B,IAAI,eAAe,CAAC,aAAa,CAAC,KACjC,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,KAAK,EAAE,KAAK,CAkC1C,CAAA"}
|