@effect-gql/core 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.
Files changed (145) hide show
  1. package/LICENSE +7 -0
  2. package/dist/analyzer-extension.d.ts +105 -0
  3. package/dist/analyzer-extension.d.ts.map +1 -0
  4. package/dist/analyzer-extension.js +137 -0
  5. package/dist/analyzer-extension.js.map +1 -0
  6. package/dist/builder/execute.d.ts +26 -0
  7. package/dist/builder/execute.d.ts.map +1 -0
  8. package/dist/builder/execute.js +104 -0
  9. package/dist/builder/execute.js.map +1 -0
  10. package/dist/builder/field-builders.d.ts +30 -0
  11. package/dist/builder/field-builders.d.ts.map +1 -0
  12. package/dist/builder/field-builders.js +200 -0
  13. package/dist/builder/field-builders.js.map +1 -0
  14. package/dist/builder/index.d.ts +7 -0
  15. package/dist/builder/index.d.ts.map +1 -0
  16. package/dist/builder/index.js +31 -0
  17. package/dist/builder/index.js.map +1 -0
  18. package/dist/builder/pipe-api.d.ts +231 -0
  19. package/dist/builder/pipe-api.d.ts.map +1 -0
  20. package/dist/builder/pipe-api.js +151 -0
  21. package/dist/builder/pipe-api.js.map +1 -0
  22. package/dist/builder/schema-builder.d.ts +301 -0
  23. package/dist/builder/schema-builder.d.ts.map +1 -0
  24. package/dist/builder/schema-builder.js +566 -0
  25. package/dist/builder/schema-builder.js.map +1 -0
  26. package/dist/builder/type-registry.d.ts +80 -0
  27. package/dist/builder/type-registry.d.ts.map +1 -0
  28. package/dist/builder/type-registry.js +505 -0
  29. package/dist/builder/type-registry.js.map +1 -0
  30. package/dist/builder/types.d.ts +283 -0
  31. package/dist/builder/types.d.ts.map +1 -0
  32. package/dist/builder/types.js +3 -0
  33. package/dist/builder/types.js.map +1 -0
  34. package/dist/cli/generate-schema.d.ts +29 -0
  35. package/dist/cli/generate-schema.d.ts.map +1 -0
  36. package/dist/cli/generate-schema.js +233 -0
  37. package/dist/cli/generate-schema.js.map +1 -0
  38. package/dist/cli/index.d.ts +19 -0
  39. package/dist/cli/index.d.ts.map +1 -0
  40. package/dist/cli/index.js +24 -0
  41. package/dist/cli/index.js.map +1 -0
  42. package/dist/context.d.ts +18 -0
  43. package/dist/context.d.ts.map +1 -0
  44. package/dist/context.js +11 -0
  45. package/dist/context.js.map +1 -0
  46. package/dist/error.d.ts +45 -0
  47. package/dist/error.d.ts.map +1 -0
  48. package/dist/error.js +29 -0
  49. package/dist/error.js.map +1 -0
  50. package/dist/extensions.d.ts +130 -0
  51. package/dist/extensions.d.ts.map +1 -0
  52. package/dist/extensions.js +78 -0
  53. package/dist/extensions.js.map +1 -0
  54. package/dist/index.d.ts +12 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +47 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/loader.d.ts +169 -0
  59. package/dist/loader.d.ts.map +1 -0
  60. package/dist/loader.js +237 -0
  61. package/dist/loader.js.map +1 -0
  62. package/dist/resolver-context.d.ts +154 -0
  63. package/dist/resolver-context.d.ts.map +1 -0
  64. package/dist/resolver-context.js +184 -0
  65. package/dist/resolver-context.js.map +1 -0
  66. package/dist/schema-mapping.d.ts +30 -0
  67. package/dist/schema-mapping.d.ts.map +1 -0
  68. package/dist/schema-mapping.js +280 -0
  69. package/dist/schema-mapping.js.map +1 -0
  70. package/dist/server/cache-control.d.ts +96 -0
  71. package/dist/server/cache-control.d.ts.map +1 -0
  72. package/dist/server/cache-control.js +308 -0
  73. package/dist/server/cache-control.js.map +1 -0
  74. package/dist/server/complexity.d.ts +165 -0
  75. package/dist/server/complexity.d.ts.map +1 -0
  76. package/dist/server/complexity.js +433 -0
  77. package/dist/server/complexity.js.map +1 -0
  78. package/dist/server/config.d.ts +66 -0
  79. package/dist/server/config.d.ts.map +1 -0
  80. package/dist/server/config.js +104 -0
  81. package/dist/server/config.js.map +1 -0
  82. package/dist/server/graphiql.d.ts +5 -0
  83. package/dist/server/graphiql.d.ts.map +1 -0
  84. package/dist/server/graphiql.js +43 -0
  85. package/dist/server/graphiql.js.map +1 -0
  86. package/dist/server/index.d.ts +18 -0
  87. package/dist/server/index.d.ts.map +1 -0
  88. package/dist/server/index.js +48 -0
  89. package/dist/server/index.js.map +1 -0
  90. package/dist/server/router.d.ts +79 -0
  91. package/dist/server/router.d.ts.map +1 -0
  92. package/dist/server/router.js +232 -0
  93. package/dist/server/router.js.map +1 -0
  94. package/dist/server/schema-builder-extensions.d.ts +42 -0
  95. package/dist/server/schema-builder-extensions.d.ts.map +1 -0
  96. package/dist/server/schema-builder-extensions.js +48 -0
  97. package/dist/server/schema-builder-extensions.js.map +1 -0
  98. package/dist/server/sse-adapter.d.ts +64 -0
  99. package/dist/server/sse-adapter.d.ts.map +1 -0
  100. package/dist/server/sse-adapter.js +227 -0
  101. package/dist/server/sse-adapter.js.map +1 -0
  102. package/dist/server/sse-types.d.ts +192 -0
  103. package/dist/server/sse-types.d.ts.map +1 -0
  104. package/dist/server/sse-types.js +63 -0
  105. package/dist/server/sse-types.js.map +1 -0
  106. package/dist/server/ws-adapter.d.ts +39 -0
  107. package/dist/server/ws-adapter.d.ts.map +1 -0
  108. package/dist/server/ws-adapter.js +247 -0
  109. package/dist/server/ws-adapter.js.map +1 -0
  110. package/dist/server/ws-types.d.ts +169 -0
  111. package/dist/server/ws-types.d.ts.map +1 -0
  112. package/dist/server/ws-types.js +11 -0
  113. package/dist/server/ws-types.js.map +1 -0
  114. package/dist/server/ws-utils.d.ts +42 -0
  115. package/dist/server/ws-utils.d.ts.map +1 -0
  116. package/dist/server/ws-utils.js +99 -0
  117. package/dist/server/ws-utils.js.map +1 -0
  118. package/package.json +61 -0
  119. package/src/analyzer-extension.ts +254 -0
  120. package/src/builder/execute.ts +153 -0
  121. package/src/builder/field-builders.ts +322 -0
  122. package/src/builder/index.ts +48 -0
  123. package/src/builder/pipe-api.ts +312 -0
  124. package/src/builder/schema-builder.ts +970 -0
  125. package/src/builder/type-registry.ts +670 -0
  126. package/src/builder/types.ts +305 -0
  127. package/src/context.ts +23 -0
  128. package/src/error.ts +32 -0
  129. package/src/extensions.ts +240 -0
  130. package/src/index.ts +32 -0
  131. package/src/loader.ts +363 -0
  132. package/src/resolver-context.ts +253 -0
  133. package/src/schema-mapping.ts +307 -0
  134. package/src/server/cache-control.ts +590 -0
  135. package/src/server/complexity.ts +774 -0
  136. package/src/server/config.ts +174 -0
  137. package/src/server/graphiql.ts +38 -0
  138. package/src/server/index.ts +96 -0
  139. package/src/server/router.ts +432 -0
  140. package/src/server/schema-builder-extensions.ts +51 -0
  141. package/src/server/sse-adapter.ts +327 -0
  142. package/src/server/sse-types.ts +234 -0
  143. package/src/server/ws-adapter.ts +355 -0
  144. package/src/server/ws-types.ts +192 -0
  145. package/src/server/ws-utils.ts +136 -0
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.makeGraphQLWSHandler = void 0;
4
+ const effect_1 = require("effect");
5
+ const graphql_1 = require("graphql");
6
+ const graphql_ws_1 = require("graphql-ws");
7
+ const complexity_1 = require("./complexity");
8
+ /**
9
+ * Create a ConnectionContext from WSExtra for use in lifecycle hooks.
10
+ */
11
+ const createConnectionContext = (extra) => ({
12
+ runtime: extra.runtime,
13
+ connectionParams: extra.connectionParams,
14
+ socket: extra.socket,
15
+ });
16
+ /**
17
+ * Create the onConnect handler for graphql-ws.
18
+ */
19
+ const makeOnConnectHandler = (options) => {
20
+ if (!options?.onConnect)
21
+ return undefined;
22
+ return async (ctx) => {
23
+ const extra = ctx.extra;
24
+ try {
25
+ const result = await effect_1.Runtime.runPromise(extra.runtime)(options.onConnect(ctx.connectionParams ?? {}));
26
+ if (typeof result === "object" && result !== null) {
27
+ Object.assign(extra.connectionParams, result);
28
+ }
29
+ return result !== false;
30
+ }
31
+ catch {
32
+ return false;
33
+ }
34
+ };
35
+ };
36
+ /**
37
+ * Create the onDisconnect handler for graphql-ws.
38
+ */
39
+ const makeOnDisconnectHandler = (options) => {
40
+ if (!options?.onDisconnect)
41
+ return undefined;
42
+ return async (ctx) => {
43
+ const extra = ctx.extra;
44
+ await effect_1.Runtime.runPromise(extra.runtime)(options.onDisconnect(createConnectionContext(extra))).catch(() => {
45
+ // Ignore cleanup errors
46
+ });
47
+ };
48
+ };
49
+ /**
50
+ * Create the onSubscribe handler for graphql-ws with complexity validation.
51
+ */
52
+ const makeOnSubscribeHandler = (options, schema, complexityConfig, fieldComplexities) => {
53
+ // graphql-ws 6.0: signature changed from (ctx, msg) to (ctx, id, payload)
54
+ return async (ctx, id, payload) => {
55
+ const extra = ctx.extra;
56
+ const connectionCtx = createConnectionContext(extra);
57
+ // Validate complexity if configured
58
+ if (complexityConfig) {
59
+ const validationEffect = (0, complexity_1.validateComplexity)(payload.query, payload.operationName ?? undefined, payload.variables ?? undefined, schema, fieldComplexities, complexityConfig).pipe(effect_1.Effect.catchAll((error) => {
60
+ if (error._tag === "ComplexityLimitExceededError") {
61
+ throw new graphql_1.GraphQLError(error.message, {
62
+ extensions: {
63
+ code: "COMPLEXITY_LIMIT_EXCEEDED",
64
+ limitType: error.limitType,
65
+ limit: error.limit,
66
+ actual: error.actual,
67
+ },
68
+ });
69
+ }
70
+ return effect_1.Effect.logWarning("Complexity analysis failed for subscription", error);
71
+ }));
72
+ await effect_1.Effect.runPromise(validationEffect);
73
+ }
74
+ // Call user's onSubscribe hook if provided
75
+ if (options?.onSubscribe) {
76
+ await effect_1.Runtime.runPromise(extra.runtime)(options.onSubscribe(connectionCtx, {
77
+ id,
78
+ payload: {
79
+ query: payload.query,
80
+ variables: payload.variables ?? undefined,
81
+ operationName: payload.operationName ?? undefined,
82
+ extensions: payload.extensions ?? undefined,
83
+ },
84
+ }));
85
+ }
86
+ };
87
+ };
88
+ /**
89
+ * Create the onComplete handler for graphql-ws.
90
+ */
91
+ const makeOnCompleteHandler = (options) => {
92
+ if (!options?.onComplete)
93
+ return undefined;
94
+ // graphql-ws 6.0: signature changed from (ctx, msg) to (ctx, id, payload)
95
+ return async (ctx, id, _payload) => {
96
+ const extra = ctx.extra;
97
+ await effect_1.Runtime.runPromise(extra.runtime)(options.onComplete(createConnectionContext(extra), { id })).catch(() => {
98
+ // Ignore cleanup errors
99
+ });
100
+ };
101
+ };
102
+ /**
103
+ * Create the onError handler for graphql-ws.
104
+ */
105
+ const makeOnErrorHandler = (options) => {
106
+ if (!options?.onError)
107
+ return undefined;
108
+ // graphql-ws 6.0: signature changed from (ctx, msg, errors) to (ctx, id, payload, errors)
109
+ return async (ctx, _id, _payload, errors) => {
110
+ const extra = ctx.extra;
111
+ await effect_1.Runtime.runPromise(extra.runtime)(options.onError(createConnectionContext(extra), errors)).catch(() => {
112
+ // Ignore error handler errors
113
+ });
114
+ };
115
+ };
116
+ /**
117
+ * Create a graphql-ws compatible socket adapter from an EffectWebSocket.
118
+ */
119
+ const createGraphqlWsSocketAdapter = (socket, runtime) => {
120
+ let messageCallback = null;
121
+ return {
122
+ adapter: {
123
+ protocol: socket.protocol,
124
+ send: (data) => effect_1.Runtime.runPromise(runtime)(socket
125
+ .send(data)
126
+ .pipe(effect_1.Effect.catchAll((error) => effect_1.Effect.logError("WebSocket send error", error)))),
127
+ close: (code, reason) => {
128
+ effect_1.Runtime.runPromise(runtime)(socket.close(code, reason)).catch(() => {
129
+ // Ignore close errors
130
+ });
131
+ },
132
+ onMessage: (cb) => {
133
+ messageCallback = cb;
134
+ },
135
+ onPong: (_payload) => {
136
+ // Pong handling - can be used for keepalive
137
+ },
138
+ },
139
+ dispatchMessage: async (message) => {
140
+ if (messageCallback) {
141
+ await messageCallback(message);
142
+ }
143
+ },
144
+ };
145
+ };
146
+ /**
147
+ * Run the connection lifecycle - manages message queue, fibers, and cleanup.
148
+ */
149
+ const runConnectionLifecycle = (socket, wsServer, extra) => effect_1.Effect.gen(function* () {
150
+ // Create message queue for bridging Stream to callback
151
+ const messageQueue = yield* effect_1.Queue.unbounded();
152
+ const closedDeferred = yield* effect_1.Deferred.make();
153
+ // Fork fiber to consume socket messages and push to queue
154
+ const messageFiber = yield* effect_1.Effect.fork(effect_1.Stream.runForEach(socket.messages, (msg) => effect_1.Queue.offer(messageQueue, msg)).pipe(effect_1.Effect.catchAll((error) => effect_1.Deferred.fail(closedDeferred, error))));
155
+ // Fork fiber to handle socket close
156
+ const closeFiber = yield* effect_1.Effect.fork(socket.closed.pipe(effect_1.Effect.tap((event) => effect_1.Deferred.succeed(closedDeferred, event)), effect_1.Effect.catchAll((error) => effect_1.Deferred.fail(closedDeferred, error))));
157
+ // Create the graphql-ws socket adapter
158
+ const { adapter, dispatchMessage } = createGraphqlWsSocketAdapter(socket, extra.runtime);
159
+ // Open the connection with graphql-ws
160
+ const closedHandler = wsServer.opened(adapter, extra);
161
+ // Fork fiber to process messages from queue
162
+ const processMessagesFiber = yield* effect_1.Effect.fork(effect_1.Effect.gen(function* () {
163
+ while (true) {
164
+ const message = yield* effect_1.Queue.take(messageQueue);
165
+ yield* effect_1.Effect.tryPromise({
166
+ try: () => dispatchMessage(message),
167
+ catch: (error) => error,
168
+ }).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.void));
169
+ }
170
+ }));
171
+ // Wait for connection to close
172
+ yield* effect_1.Deferred.await(closedDeferred).pipe(effect_1.Effect.catchAll(() => effect_1.Effect.succeed({ code: 1000, reason: "Error" })));
173
+ // Cleanup
174
+ closedHandler(1000, "Connection closed");
175
+ yield* effect_1.Fiber.interrupt(messageFiber);
176
+ yield* effect_1.Fiber.interrupt(closeFiber);
177
+ yield* effect_1.Fiber.interrupt(processMessagesFiber);
178
+ yield* effect_1.Queue.shutdown(messageQueue);
179
+ }).pipe(effect_1.Effect.catchAllCause(() => effect_1.Effect.void), effect_1.Effect.scoped);
180
+ /**
181
+ * Create a WebSocket handler for GraphQL subscriptions using the graphql-ws protocol.
182
+ *
183
+ * This function creates a handler that can be used with any WebSocket implementation
184
+ * that conforms to the EffectWebSocket interface. Platform packages (node, bun, express)
185
+ * provide adapters that convert their native WebSocket to EffectWebSocket.
186
+ *
187
+ * The handler:
188
+ * - Uses the graphql-ws protocol for client communication
189
+ * - Creates an Effect runtime from the provided layer for each connection
190
+ * - Executes subscriptions using GraphQL's subscribe() function
191
+ * - Properly cleans up resources when connections close
192
+ *
193
+ * @param schema - The GraphQL schema with subscription definitions
194
+ * @param layer - Effect layer providing services required by resolvers
195
+ * @param options - Optional lifecycle hooks for connection/subscription events
196
+ * @returns A function that handles individual WebSocket connections
197
+ *
198
+ * @example
199
+ * ```typescript
200
+ * import { makeGraphQLWSHandler } from "@effect-gql/core"
201
+ *
202
+ * const handler = makeGraphQLWSHandler(schema, serviceLayer, {
203
+ * onConnect: (params) => Effect.gen(function* () {
204
+ * const user = yield* AuthService.validateToken(params.authToken)
205
+ * return { user }
206
+ * }),
207
+ * })
208
+ *
209
+ * // In platform-specific code:
210
+ * const effectSocket = toEffectWebSocket(rawWebSocket)
211
+ * await Effect.runPromise(handler(effectSocket))
212
+ * ```
213
+ */
214
+ const makeGraphQLWSHandler = (schema, layer, options) => {
215
+ const complexityConfig = options?.complexity;
216
+ const fieldComplexities = options?.fieldComplexities ?? new Map();
217
+ // Build server options using extracted handler factories
218
+ const serverOptions = {
219
+ schema,
220
+ context: async (ctx) => {
221
+ const extra = ctx.extra;
222
+ return {
223
+ runtime: extra.runtime,
224
+ ...extra.connectionParams,
225
+ };
226
+ },
227
+ subscribe: async (args) => (0, graphql_1.subscribe)(args),
228
+ onConnect: makeOnConnectHandler(options),
229
+ onDisconnect: makeOnDisconnectHandler(options),
230
+ onSubscribe: makeOnSubscribeHandler(options, schema, complexityConfig, fieldComplexities),
231
+ onComplete: makeOnCompleteHandler(options),
232
+ onError: makeOnErrorHandler(options),
233
+ };
234
+ const wsServer = (0, graphql_ws_1.makeServer)(serverOptions);
235
+ // Return the connection handler
236
+ return (socket) => effect_1.Effect.gen(function* () {
237
+ const runtime = yield* effect_1.Effect.provide(effect_1.Effect.runtime(), layer);
238
+ const extra = {
239
+ socket,
240
+ runtime,
241
+ connectionParams: {},
242
+ };
243
+ yield* runConnectionLifecycle(socket, wsServer, extra);
244
+ }).pipe(effect_1.Effect.catchAllCause(() => effect_1.Effect.void), effect_1.Effect.scoped);
245
+ };
246
+ exports.makeGraphQLWSHandler = makeGraphQLWSHandler;
247
+ //# sourceMappingURL=ws-adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-adapter.js","sourceRoot":"","sources":["../../src/server/ws-adapter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,169 @@
1
+ import { Effect, Runtime, Stream } from "effect";
2
+ import type { ComplexityConfig, FieldComplexityMap } from "./complexity";
3
+ declare const WebSocketError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => import("effect/Cause").YieldableError & {
4
+ readonly _tag: "WebSocketError";
5
+ } & Readonly<A>;
6
+ /**
7
+ * Error type for WebSocket operations
8
+ */
9
+ export declare class WebSocketError extends WebSocketError_base<{
10
+ readonly cause: unknown;
11
+ }> {
12
+ }
13
+ /**
14
+ * WebSocket close event information
15
+ */
16
+ export interface CloseEvent {
17
+ readonly code: number;
18
+ readonly reason: string;
19
+ }
20
+ /**
21
+ * Platform-neutral WebSocket interface using Effect types.
22
+ *
23
+ * This interface abstracts WebSocket operations across different platforms
24
+ * (Node.js ws, Bun built-in, browser WebSocket). Platform packages implement
25
+ * this interface to bridge their specific WebSocket implementations.
26
+ */
27
+ export interface EffectWebSocket {
28
+ /**
29
+ * Send a message to the client.
30
+ * Returns an Effect that completes when the message is sent.
31
+ */
32
+ readonly send: (data: string) => Effect.Effect<void, WebSocketError>;
33
+ /**
34
+ * Close the WebSocket connection.
35
+ * @param code - Optional close code (default: 1000)
36
+ * @param reason - Optional close reason
37
+ */
38
+ readonly close: (code?: number, reason?: string) => Effect.Effect<void, WebSocketError>;
39
+ /**
40
+ * Stream of incoming messages from the client.
41
+ * The stream completes when the connection closes.
42
+ */
43
+ readonly messages: Stream.Stream<string, WebSocketError>;
44
+ /**
45
+ * Effect that completes with CloseEvent when the connection closes.
46
+ * Use this to detect client disconnection.
47
+ */
48
+ readonly closed: Effect.Effect<CloseEvent, WebSocketError>;
49
+ /**
50
+ * The WebSocket subprotocol negotiated during handshake.
51
+ * For GraphQL subscriptions, this should be "graphql-transport-ws".
52
+ */
53
+ readonly protocol: string;
54
+ }
55
+ /**
56
+ * Context available during a WebSocket connection.
57
+ * This is passed to lifecycle hooks.
58
+ */
59
+ export interface ConnectionContext<R> {
60
+ /**
61
+ * The Effect runtime for this connection.
62
+ * Use this to run Effects within the connection scope.
63
+ */
64
+ readonly runtime: Runtime.Runtime<R>;
65
+ /**
66
+ * Connection parameters sent by the client during CONNECTION_INIT.
67
+ * Often used for authentication tokens.
68
+ */
69
+ readonly connectionParams: Record<string, unknown>;
70
+ /**
71
+ * The underlying WebSocket for this connection.
72
+ */
73
+ readonly socket: EffectWebSocket;
74
+ }
75
+ /**
76
+ * Options for configuring the GraphQL WebSocket handler.
77
+ *
78
+ * @template R - Service requirements for lifecycle hooks
79
+ */
80
+ export interface GraphQLWSOptions<R> {
81
+ /**
82
+ * Query complexity limiting configuration.
83
+ * When provided, subscriptions are validated against complexity limits
84
+ * before execution begins.
85
+ */
86
+ readonly complexity?: ComplexityConfig;
87
+ /**
88
+ * Field complexity definitions from the schema builder.
89
+ * If using the platform serve() functions with subscriptions config,
90
+ * this is typically passed automatically.
91
+ */
92
+ readonly fieldComplexities?: FieldComplexityMap;
93
+ /**
94
+ * Called when a client initiates a connection (CONNECTION_INIT message).
95
+ *
96
+ * Use this for authentication. Return:
97
+ * - `true` to accept the connection
98
+ * - `false` to reject the connection
99
+ * - An object to accept and provide additional context
100
+ *
101
+ * The returned object (or true) is merged into the GraphQL context.
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * onConnect: (params) => Effect.gen(function* () {
106
+ * const token = params.authToken as string
107
+ * const user = yield* AuthService.validateToken(token)
108
+ * return { user } // Available in GraphQL context
109
+ * })
110
+ * ```
111
+ */
112
+ readonly onConnect?: (params: Record<string, unknown>) => Effect.Effect<boolean | Record<string, unknown>, unknown, R>;
113
+ /**
114
+ * Called when a client disconnects.
115
+ * Use this for cleanup (e.g., removing user from active connections).
116
+ */
117
+ readonly onDisconnect?: (ctx: ConnectionContext<R>) => Effect.Effect<void, never, R>;
118
+ /**
119
+ * Called when a client starts a subscription (SUBSCRIBE message).
120
+ * Use this for per-subscription authorization or logging.
121
+ *
122
+ * Note: If complexity validation is enabled, it runs before this hook.
123
+ * Throw an error to reject the subscription.
124
+ */
125
+ readonly onSubscribe?: (ctx: ConnectionContext<R>, message: SubscribeMessage) => Effect.Effect<void, unknown, R>;
126
+ /**
127
+ * Called when a subscription completes or is stopped.
128
+ */
129
+ readonly onComplete?: (ctx: ConnectionContext<R>, message: CompleteMessage) => Effect.Effect<void, never, R>;
130
+ /**
131
+ * Called when an error occurs during subscription execution.
132
+ */
133
+ readonly onError?: (ctx: ConnectionContext<R>, error: unknown) => Effect.Effect<void, never, R>;
134
+ }
135
+ /**
136
+ * GraphQL WebSocket SUBSCRIBE message payload
137
+ */
138
+ export interface SubscribeMessage {
139
+ readonly id: string;
140
+ readonly payload: {
141
+ readonly query: string;
142
+ readonly variables?: Record<string, unknown>;
143
+ readonly operationName?: string;
144
+ readonly extensions?: Record<string, unknown>;
145
+ };
146
+ }
147
+ /**
148
+ * GraphQL WebSocket COMPLETE message payload
149
+ */
150
+ export interface CompleteMessage {
151
+ readonly id: string;
152
+ }
153
+ /**
154
+ * Configuration for the WebSocket endpoint
155
+ */
156
+ export interface GraphQLWSConfig {
157
+ /**
158
+ * Path for WebSocket connections.
159
+ * @default "/graphql"
160
+ */
161
+ readonly path?: string;
162
+ /**
163
+ * How long to wait for CONNECTION_INIT message before closing.
164
+ * @default 5000 (5 seconds)
165
+ */
166
+ readonly connectionInitWaitTimeout?: number;
167
+ }
168
+ export {};
169
+ //# sourceMappingURL=ws-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-types.d.ts","sourceRoot":"","sources":["../../src/server/ws-types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebSocketError = void 0;
4
+ const effect_1 = require("effect");
5
+ /**
6
+ * Error type for WebSocket operations
7
+ */
8
+ class WebSocketError extends effect_1.Data.TaggedError("WebSocketError") {
9
+ }
10
+ exports.WebSocketError = WebSocketError;
11
+ //# sourceMappingURL=ws-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-types.js","sourceRoot":"","sources":["../../src/server/ws-types.ts"],"names":[],"mappings":";;;AAAA,mCAAsD;AAGtD;;GAEG;AACH,MAAa,cAAe,SAAQ,aAAI,CAAC,WAAW,CAAC,gBAAgB,CAEnE;CAAG;AAFL,wCAEK"}
@@ -0,0 +1,42 @@
1
+ import type { EffectWebSocket } from "./ws-types";
2
+ /**
3
+ * Interface for the 'ws' library WebSocket.
4
+ * This allows type-safe usage without requiring core to depend on 'ws'.
5
+ */
6
+ export interface WsWebSocket {
7
+ readonly protocol: string;
8
+ readonly readyState: number;
9
+ send(data: string, callback?: (error?: Error) => void): void;
10
+ close(code?: number, reason?: string): void;
11
+ on(event: "message", listener: (data: Buffer | string) => void): void;
12
+ on(event: "error", listener: (error: Error) => void): void;
13
+ on(event: "close", listener: (code: number, reason: Buffer) => void): void;
14
+ removeListener(event: string, listener: (...args: any[]) => void): void;
15
+ }
16
+ /** WebSocket.CLOSED constant from 'ws' library */
17
+ export declare const WS_CLOSED = 3;
18
+ /**
19
+ * Convert a WebSocket from the 'ws' library to an EffectWebSocket.
20
+ *
21
+ * This creates an Effect-based wrapper around the ws WebSocket instance,
22
+ * providing a Stream for incoming messages and Effect-based send/close operations.
23
+ *
24
+ * This utility is used by platform packages (node, express) that integrate
25
+ * with the 'ws' library for WebSocket support.
26
+ *
27
+ * @param ws - The WebSocket instance from the 'ws' library
28
+ * @returns An EffectWebSocket that can be used with makeGraphQLWSHandler
29
+ *
30
+ * @example
31
+ * ```typescript
32
+ * import { toEffectWebSocketFromWs } from "@effect-gql/core"
33
+ * import { WebSocket } from "ws"
34
+ *
35
+ * wss.on("connection", (ws: WebSocket) => {
36
+ * const effectSocket = toEffectWebSocketFromWs(ws)
37
+ * Effect.runPromise(handler(effectSocket))
38
+ * })
39
+ * ```
40
+ */
41
+ export declare const toEffectWebSocketFromWs: (ws: WsWebSocket) => EffectWebSocket;
42
+ //# sourceMappingURL=ws-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-utils.d.ts","sourceRoot":"","sources":["../../src/server/ws-utils.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAc,MAAM,YAAY,CAAA;AAG7D;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAA;IAC5D,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3C,EAAE,CAAC,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,KAAK,IAAI,GAAG,IAAI,CAAA;IACrE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI,CAAA;IAC1D,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI,CAAA;IAC1E,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,IAAI,CAAA;CACxE;AAED,kDAAkD;AAClD,eAAO,MAAM,SAAS,IAAI,CAAA;AAE1B;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,uBAAuB,GAAI,IAAI,WAAW,KAAG,eA0FzD,CAAA"}
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toEffectWebSocketFromWs = exports.WS_CLOSED = void 0;
4
+ const effect_1 = require("effect");
5
+ const ws_types_1 = require("./ws-types");
6
+ /** WebSocket.CLOSED constant from 'ws' library */
7
+ exports.WS_CLOSED = 3;
8
+ /**
9
+ * Convert a WebSocket from the 'ws' library to an EffectWebSocket.
10
+ *
11
+ * This creates an Effect-based wrapper around the ws WebSocket instance,
12
+ * providing a Stream for incoming messages and Effect-based send/close operations.
13
+ *
14
+ * This utility is used by platform packages (node, express) that integrate
15
+ * with the 'ws' library for WebSocket support.
16
+ *
17
+ * @param ws - The WebSocket instance from the 'ws' library
18
+ * @returns An EffectWebSocket that can be used with makeGraphQLWSHandler
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * import { toEffectWebSocketFromWs } from "@effect-gql/core"
23
+ * import { WebSocket } from "ws"
24
+ *
25
+ * wss.on("connection", (ws: WebSocket) => {
26
+ * const effectSocket = toEffectWebSocketFromWs(ws)
27
+ * Effect.runPromise(handler(effectSocket))
28
+ * })
29
+ * ```
30
+ */
31
+ const toEffectWebSocketFromWs = (ws) => {
32
+ // Create the message stream using a queue
33
+ const messagesEffect = effect_1.Effect.gen(function* () {
34
+ const queue = yield* effect_1.Queue.unbounded();
35
+ const closed = yield* effect_1.Deferred.make();
36
+ // Set up message listener
37
+ ws.on("message", (data) => {
38
+ const message = data.toString();
39
+ effect_1.Effect.runPromise(effect_1.Queue.offer(queue, message)).catch(() => {
40
+ // Queue might be shutdown
41
+ });
42
+ });
43
+ // Set up error listener
44
+ ws.on("error", (error) => {
45
+ effect_1.Effect.runPromise(effect_1.Deferred.fail(closed, new ws_types_1.WebSocketError({ cause: error }))).catch(() => {
46
+ // Already completed
47
+ });
48
+ });
49
+ // Set up close listener
50
+ ws.on("close", (code, reason) => {
51
+ effect_1.Effect.runPromise(effect_1.Queue.shutdown(queue).pipe(effect_1.Effect.andThen(effect_1.Deferred.succeed(closed, { code, reason: reason.toString() })))).catch(() => {
52
+ // Already completed
53
+ });
54
+ });
55
+ return { queue, closed };
56
+ });
57
+ // Create the message stream
58
+ const messages = effect_1.Stream.unwrap(messagesEffect.pipe(effect_1.Effect.map(({ queue }) => effect_1.Stream.fromQueue(queue).pipe(effect_1.Stream.catchAll(() => effect_1.Stream.empty)))));
59
+ return {
60
+ protocol: ws.protocol || "graphql-transport-ws",
61
+ send: (data) => effect_1.Effect.async((resume) => {
62
+ ws.send(data, (error) => {
63
+ if (error) {
64
+ resume(effect_1.Effect.fail(new ws_types_1.WebSocketError({ cause: error })));
65
+ }
66
+ else {
67
+ resume(effect_1.Effect.succeed(undefined));
68
+ }
69
+ });
70
+ }),
71
+ close: (code, reason) => effect_1.Effect.sync(() => {
72
+ ws.close(code ?? 1000, reason ?? "");
73
+ }),
74
+ messages,
75
+ closed: effect_1.Effect.async((resume) => {
76
+ if (ws.readyState === exports.WS_CLOSED) {
77
+ resume(effect_1.Effect.succeed({ code: 1000, reason: "" }));
78
+ return;
79
+ }
80
+ const onClose = (code, reason) => {
81
+ cleanup();
82
+ resume(effect_1.Effect.succeed({ code, reason: reason.toString() }));
83
+ };
84
+ const onError = (error) => {
85
+ cleanup();
86
+ resume(effect_1.Effect.fail(new ws_types_1.WebSocketError({ cause: error })));
87
+ };
88
+ const cleanup = () => {
89
+ ws.removeListener("close", onClose);
90
+ ws.removeListener("error", onError);
91
+ };
92
+ ws.on("close", onClose);
93
+ ws.on("error", onError);
94
+ return effect_1.Effect.sync(cleanup);
95
+ }),
96
+ };
97
+ };
98
+ exports.toEffectWebSocketFromWs = toEffectWebSocketFromWs;
99
+ //# sourceMappingURL=ws-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ws-utils.js","sourceRoot":"","sources":["../../src/server/ws-utils.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@effect-gql/core",
3
+ "version": "0.1.0",
4
+ "description": "Core GraphQL framework for Effect-TS - schema building, type mapping, and execution",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./builder": {
13
+ "types": "./dist/builder/index.d.ts",
14
+ "default": "./dist/builder/index.js"
15
+ },
16
+ "./server": {
17
+ "types": "./dist/server/index.d.ts",
18
+ "default": "./dist/server/index.js"
19
+ }
20
+ },
21
+ "files": [
22
+ "dist",
23
+ "src"
24
+ ],
25
+ "peerDependencies": {
26
+ "@effect/platform": "^0.94.0",
27
+ "effect": "^3.19.0",
28
+ "graphql": "^16.0.0",
29
+ "graphql-ws": "^6.0.0"
30
+ },
31
+ "peerDependenciesMeta": {
32
+ "graphql-ws": {
33
+ "optional": true
34
+ }
35
+ },
36
+ "dependencies": {
37
+ "dataloader": "^2.2.3",
38
+ "reflect-metadata": "^0.2.2"
39
+ },
40
+ "devDependencies": {
41
+ "@effect/platform": "^0.94.0",
42
+ "effect": "^3.19.13",
43
+ "graphql": "^16.0.0",
44
+ "graphql-ws": "^6.0.6"
45
+ },
46
+ "keywords": [
47
+ "effect",
48
+ "graphql",
49
+ "typescript",
50
+ "schema",
51
+ "functional-programming"
52
+ ],
53
+ "license": "MIT",
54
+ "scripts": {
55
+ "build": "tsc",
56
+ "dev": "tsc --watch",
57
+ "clean": "rm -rf dist",
58
+ "test": "vitest run",
59
+ "test:watch": "vitest"
60
+ }
61
+ }