@effect-gql/core 1.0.0 → 1.1.1

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.
@@ -1,6 +1,6 @@
1
1
  import { Config, Layer, Cause, Effect, Stream, Runtime } from 'effect';
2
- import { j as ComplexityConfig, y as CacheControlConfig, F as FieldComplexityMap, w as CacheHintMap, G as GraphQLExtension, i as GraphQLSchemaBuilder } from '../schema-builder-Cvdq7Kz_.cjs';
3
- export { J as CacheControlConfigFromEnv, x as CachePolicy, z as CachePolicyAnalysisInfo, q as ComplexityAnalysisError, l as ComplexityAnalysisInfo, n as ComplexityCalculator, u as ComplexityConfigFromEnv, m as ComplexityExceededInfo, p as ComplexityLimitExceededError, k as ComplexityResult, o as FieldComplexity, t as combineCalculators, A as computeCachePolicy, B as computeCachePolicyFromQuery, r as defaultComplexityCalculator, s as depthOnlyCalculator, H as toCacheControlHeader, v as validateComplexity } from '../schema-builder-Cvdq7Kz_.cjs';
2
+ import { j as ComplexityConfig, y as CacheControlConfig, F as FieldComplexityMap, w as CacheHintMap, G as GraphQLExtension, i as GraphQLSchemaBuilder } from '../schema-builder-DKvkzU_M.cjs';
3
+ export { J as CacheControlConfigFromEnv, x as CachePolicy, z as CachePolicyAnalysisInfo, q as ComplexityAnalysisError, l as ComplexityAnalysisInfo, n as ComplexityCalculator, u as ComplexityConfigFromEnv, m as ComplexityExceededInfo, p as ComplexityLimitExceededError, k as ComplexityResult, o as FieldComplexity, t as combineCalculators, A as computeCachePolicy, B as computeCachePolicyFromQuery, r as defaultComplexityCalculator, s as depthOnlyCalculator, H as toCacheControlHeader, v as validateComplexity } from '../schema-builder-DKvkzU_M.cjs';
4
4
  import { HttpServerResponse, HttpRouter } from '@effect/platform';
5
5
  import { GraphQLSchema, ExecutionResult } from 'graphql';
6
6
  import * as effect_Cause from 'effect/Cause';
@@ -15,6 +15,8 @@ interface GraphiQLConfig {
15
15
  readonly path: string;
16
16
  /** URL where GraphiQL sends requests (default: same as graphql path) */
17
17
  readonly endpoint: string;
18
+ /** WebSocket URL for subscriptions (default: same as endpoint) */
19
+ readonly subscriptionEndpoint?: string;
18
20
  }
19
21
  /**
20
22
  * Configuration for the GraphQL router
@@ -73,7 +75,7 @@ declare const GraphQLRouterConfigFromEnv: Config.Config<GraphQLRouterConfig>;
73
75
  /**
74
76
  * Generate HTML for GraphiQL IDE, loading dependencies from CDN
75
77
  */
76
- declare const graphiqlHtml: (endpoint: string) => string;
78
+ declare const graphiqlHtml: (endpoint: string, subscriptionEndpoint?: string) => string;
77
79
 
78
80
  /**
79
81
  * Error handler function type for handling uncaught errors during GraphQL execution.
package/server/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Config, Layer, Cause, Effect, Stream, Runtime } from 'effect';
2
- import { j as ComplexityConfig, y as CacheControlConfig, F as FieldComplexityMap, w as CacheHintMap, G as GraphQLExtension, i as GraphQLSchemaBuilder } from '../schema-builder-Cvdq7Kz_.js';
3
- export { J as CacheControlConfigFromEnv, x as CachePolicy, z as CachePolicyAnalysisInfo, q as ComplexityAnalysisError, l as ComplexityAnalysisInfo, n as ComplexityCalculator, u as ComplexityConfigFromEnv, m as ComplexityExceededInfo, p as ComplexityLimitExceededError, k as ComplexityResult, o as FieldComplexity, t as combineCalculators, A as computeCachePolicy, B as computeCachePolicyFromQuery, r as defaultComplexityCalculator, s as depthOnlyCalculator, H as toCacheControlHeader, v as validateComplexity } from '../schema-builder-Cvdq7Kz_.js';
2
+ import { j as ComplexityConfig, y as CacheControlConfig, F as FieldComplexityMap, w as CacheHintMap, G as GraphQLExtension, i as GraphQLSchemaBuilder } from '../schema-builder-DKvkzU_M.js';
3
+ export { J as CacheControlConfigFromEnv, x as CachePolicy, z as CachePolicyAnalysisInfo, q as ComplexityAnalysisError, l as ComplexityAnalysisInfo, n as ComplexityCalculator, u as ComplexityConfigFromEnv, m as ComplexityExceededInfo, p as ComplexityLimitExceededError, k as ComplexityResult, o as FieldComplexity, t as combineCalculators, A as computeCachePolicy, B as computeCachePolicyFromQuery, r as defaultComplexityCalculator, s as depthOnlyCalculator, H as toCacheControlHeader, v as validateComplexity } from '../schema-builder-DKvkzU_M.js';
4
4
  import { HttpServerResponse, HttpRouter } from '@effect/platform';
5
5
  import { GraphQLSchema, ExecutionResult } from 'graphql';
6
6
  import * as effect_Cause from 'effect/Cause';
@@ -15,6 +15,8 @@ interface GraphiQLConfig {
15
15
  readonly path: string;
16
16
  /** URL where GraphiQL sends requests (default: same as graphql path) */
17
17
  readonly endpoint: string;
18
+ /** WebSocket URL for subscriptions (default: same as endpoint) */
19
+ readonly subscriptionEndpoint?: string;
18
20
  }
19
21
  /**
20
22
  * Configuration for the GraphQL router
@@ -73,7 +75,7 @@ declare const GraphQLRouterConfigFromEnv: Config.Config<GraphQLRouterConfig>;
73
75
  /**
74
76
  * Generate HTML for GraphiQL IDE, loading dependencies from CDN
75
77
  */
76
- declare const graphiqlHtml: (endpoint: string) => string;
78
+ declare const graphiqlHtml: (endpoint: string, subscriptionEndpoint?: string) => string;
77
79
 
78
80
  /**
79
81
  * Error handler function type for handling uncaught errors during GraphQL execution.
package/server/index.js CHANGED
@@ -1,7 +1,6 @@
1
- import { Config, Option, Data, Context, Effect, Queue, Deferred, Stream, Ref, Runtime, Fiber } from 'effect';
2
- import { HttpServerResponse, HttpServerRequest, HttpRouter } from '@effect/platform';
3
- import { Kind, GraphQLObjectType, parse, specifiedRules, NoSchemaIntrospectionCustomRule, validate, subscribe, GraphQLError, GraphQLNonNull, GraphQLList, GraphQLSchema, GraphQLScalarType, GraphQLEnumType, execute } from 'graphql';
4
- import { makeServer } from 'graphql-ws';
1
+ import { Config, Option, Data, Context, Schema, Effect, Queue, Deferred, Stream, Ref, Fiber, Runtime } from 'effect';
2
+ import { HttpIncomingMessage, HttpServerResponse, HttpServerRequest, HttpRouter } from '@effect/platform';
3
+ import { Kind, GraphQLObjectType, parse, specifiedRules, NoSchemaIntrospectionCustomRule, validate, GraphQLError, subscribe, GraphQLNonNull, GraphQLList, GraphQLSchema, GraphQLScalarType, GraphQLEnumType, execute } from 'graphql';
5
4
 
6
5
  // src/server/config.ts
7
6
  var defaultConfig = {
@@ -96,7 +95,7 @@ var GraphQLRouterConfigFromEnv = Config.all({
96
95
  );
97
96
 
98
97
  // src/server/graphiql.ts
99
- var graphiqlHtml = (endpoint) => `<!DOCTYPE html>
98
+ var graphiqlHtml = (endpoint, subscriptionEndpoint) => `<!DOCTYPE html>
100
99
  <html lang="en">
101
100
  <head>
102
101
  <meta charset="utf-8" />
@@ -124,6 +123,7 @@ var graphiqlHtml = (endpoint) => `<!DOCTYPE html>
124
123
  <script>
125
124
  const fetcher = GraphiQL.createFetcher({
126
125
  url: ${JSON.stringify(endpoint)},
126
+ subscriptionUrl: ${JSON.stringify(subscriptionEndpoint ?? endpoint)},
127
127
  });
128
128
  ReactDOM.createRoot(document.getElementById('graphiql')).render(
129
129
  React.createElement(GraphiQL, { fetcher })
@@ -848,19 +848,32 @@ var defaultErrorHandler = (cause) => (process.env.NODE_ENV !== "production" ? Ef
848
848
  ).pipe(Effect.orDie)
849
849
  )
850
850
  );
851
- var parseGraphQLQuery = (query, extensionsService) => Effect.gen(function* () {
851
+ var GraphQLRequestBodySchema = Schema.Struct({
852
+ query: Schema.String,
853
+ variables: Schema.optionalWith(Schema.Record({ key: Schema.String, value: Schema.Unknown }), {
854
+ as: "Option"
855
+ }),
856
+ operationName: Schema.optionalWith(Schema.String, { as: "Option" })
857
+ });
858
+ var decodeRequestBody = HttpIncomingMessage.schemaBodyJson(GraphQLRequestBodySchema);
859
+ var parseGraphQLQuery = (query, extensionsService) => {
852
860
  try {
853
861
  const document = parse(query);
854
- return { ok: true, document };
862
+ return Effect.succeed({ ok: true, document });
855
863
  } catch (parseError) {
856
- const extensionData = yield* extensionsService.get();
857
- const response = yield* HttpServerResponse.json({
858
- errors: [{ message: String(parseError) }],
859
- extensions: Object.keys(extensionData).length > 0 ? extensionData : void 0
860
- }).pipe(Effect.orDie);
861
- return { ok: false, response };
864
+ return extensionsService.get().pipe(
865
+ Effect.flatMap(
866
+ (extensionData) => HttpServerResponse.json({
867
+ errors: [{ message: String(parseError) }],
868
+ extensions: Object.keys(extensionData).length > 0 ? extensionData : void 0
869
+ }).pipe(
870
+ Effect.orDie,
871
+ Effect.map((response) => ({ ok: false, response }))
872
+ )
873
+ )
874
+ );
862
875
  }
863
- });
876
+ };
864
877
  var runComplexityValidation = (body, schema, fieldComplexities, complexityConfig) => {
865
878
  if (!complexityConfig) {
866
879
  return Effect.void;
@@ -880,8 +893,9 @@ var runComplexityValidation = (body, schema, fieldComplexities, complexityConfig
880
893
  )
881
894
  );
882
895
  };
883
- var executeGraphQLQuery = (schema, document, variables, operationName, runtime) => Effect.gen(function* () {
884
- const executeResult = yield* Effect.try({
896
+ var isPromiseLike = (value) => value !== null && typeof value === "object" && "then" in value && typeof value.then === "function";
897
+ var executeGraphQLQuery = (schema, document, variables, operationName, runtime) => {
898
+ const tryExecute = Effect.try({
885
899
  try: () => execute({
886
900
  schema,
887
901
  document,
@@ -891,33 +905,34 @@ var executeGraphQLQuery = (schema, document, variables, operationName, runtime)
891
905
  }),
892
906
  catch: (error) => new Error(String(error))
893
907
  });
894
- if (executeResult && typeof executeResult === "object" && "then" in executeResult) {
895
- return yield* Effect.promise(
896
- () => executeResult
897
- );
898
- }
899
- return executeResult;
900
- });
901
- var computeCacheControlHeader = (document, operationName, schema, cacheHints, cacheControlConfig) => Effect.gen(function* () {
908
+ return tryExecute.pipe(
909
+ Effect.flatMap((executeResult) => {
910
+ if (isPromiseLike(executeResult)) {
911
+ return Effect.promise(() => executeResult);
912
+ }
913
+ return Effect.succeed(executeResult);
914
+ })
915
+ );
916
+ };
917
+ var computeCacheControlHeader = (document, operationName, schema, cacheHints, cacheControlConfig) => {
902
918
  if (cacheControlConfig?.enabled === false || cacheControlConfig?.calculateHttpHeaders === false) {
903
- return void 0;
919
+ return Effect.succeed(void 0);
904
920
  }
905
921
  const operations = document.definitions.filter(
906
922
  (d) => d.kind === Kind.OPERATION_DEFINITION
907
923
  );
908
924
  const operation = operationName ? operations.find((o) => o.name?.value === operationName) : operations[0];
909
925
  if (!operation || operation.operation === "mutation") {
910
- return void 0;
926
+ return Effect.succeed(void 0);
911
927
  }
912
- const cachePolicy = yield* computeCachePolicy({
928
+ return computeCachePolicy({
913
929
  document,
914
930
  operation,
915
931
  schema,
916
932
  cacheHints,
917
933
  config: cacheControlConfig ?? {}
918
- });
919
- return toCacheControlHeader(cachePolicy);
920
- });
934
+ }).pipe(Effect.map(toCacheControlHeader));
935
+ };
921
936
  var buildGraphQLResponse = (result, extensionData, cacheControlHeader) => {
922
937
  const finalResult = Object.keys(extensionData).length > 0 ? {
923
938
  ...result,
@@ -955,7 +970,12 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
955
970
  const extensionsService = yield* makeExtensionsService();
956
971
  const runtime = yield* Effect.runtime();
957
972
  const request = yield* HttpServerRequest.HttpServerRequest;
958
- const body = yield* request.json;
973
+ const parsedBody = yield* decodeRequestBody(request);
974
+ const body = {
975
+ query: parsedBody.query,
976
+ variables: parsedBody.variables._tag === "Some" ? parsedBody.variables.value : void 0,
977
+ operationName: parsedBody.operationName._tag === "Some" ? parsedBody.operationName.value : void 0
978
+ };
959
979
  const parseResult = yield* parseGraphQLQuery(body.query, extensionsService);
960
980
  if (!parseResult.ok) {
961
981
  return parseResult.response;
@@ -964,7 +984,7 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
964
984
  yield* runParseHooks(extensions, body.query, document).pipe(
965
985
  Effect.provideService(ExtensionsService, extensionsService)
966
986
  );
967
- const validationRules = resolvedConfig.introspection ? void 0 : [...specifiedRules, NoSchemaIntrospectionCustomRule];
987
+ const validationRules = resolvedConfig.introspection ? void 0 : specifiedRules.concat(NoSchemaIntrospectionCustomRule);
968
988
  const validationErrors = validate(schema, document, validationRules);
969
989
  yield* runValidateHooks(extensions, document, validationErrors).pipe(
970
990
  Effect.provideService(ExtensionsService, extensionsService)
@@ -1025,9 +1045,12 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
1025
1045
  HttpRouter.post(resolvedConfig.path, graphqlHandler)
1026
1046
  );
1027
1047
  if (resolvedConfig.graphiql) {
1028
- const { path, endpoint } = resolvedConfig.graphiql;
1048
+ const { path, endpoint, subscriptionEndpoint } = resolvedConfig.graphiql;
1029
1049
  router = router.pipe(
1030
- HttpRouter.get(path, HttpServerResponse.html(graphiqlHtml(endpoint)))
1050
+ HttpRouter.get(
1051
+ path,
1052
+ HttpServerResponse.html(graphiqlHtml(endpoint, subscriptionEndpoint))
1053
+ )
1031
1054
  );
1032
1055
  }
1033
1056
  return router;
@@ -1202,27 +1225,45 @@ var runConnectionLifecycle = (socket, wsServer, extra) => Effect.gen(function* (
1202
1225
  Effect.catchAllCause(() => Effect.void),
1203
1226
  Effect.scoped
1204
1227
  );
1228
+ var importGraphqlWs = Effect.tryPromise({
1229
+ try: () => import('graphql-ws'),
1230
+ catch: () => new Error(
1231
+ "graphql-ws is required for WebSocket subscriptions. Install it with: npm install graphql-ws"
1232
+ )
1233
+ });
1205
1234
  var makeGraphQLWSHandler = (schema, layer, options) => {
1206
1235
  const complexityConfig = options?.complexity;
1207
1236
  const fieldComplexities = options?.fieldComplexities ?? /* @__PURE__ */ new Map();
1208
- const serverOptions = {
1209
- schema,
1210
- context: async (ctx) => {
1211
- const extra = ctx.extra;
1212
- return {
1213
- runtime: extra.runtime,
1214
- ...extra.connectionParams
1215
- };
1216
- },
1217
- subscribe: async (args) => subscribe(args),
1218
- onConnect: makeOnConnectHandler(options),
1219
- onDisconnect: makeOnDisconnectHandler(options),
1220
- onSubscribe: makeOnSubscribeHandler(options, schema, complexityConfig, fieldComplexities),
1221
- onComplete: makeOnCompleteHandler(options),
1222
- onError: makeOnErrorHandler(options)
1237
+ let wsServerPromise = null;
1238
+ const getOrCreateServer = async () => {
1239
+ if (!wsServerPromise) {
1240
+ wsServerPromise = Effect.runPromise(importGraphqlWs).then(({ makeServer }) => {
1241
+ const serverOptions = {
1242
+ schema,
1243
+ context: async (ctx) => {
1244
+ const extra = ctx.extra;
1245
+ return {
1246
+ runtime: extra.runtime,
1247
+ ...extra.connectionParams
1248
+ };
1249
+ },
1250
+ subscribe: async (args) => subscribe(args),
1251
+ onConnect: makeOnConnectHandler(options),
1252
+ onDisconnect: makeOnDisconnectHandler(options),
1253
+ onSubscribe: makeOnSubscribeHandler(options, schema, complexityConfig, fieldComplexities),
1254
+ onComplete: makeOnCompleteHandler(options),
1255
+ onError: makeOnErrorHandler(options)
1256
+ };
1257
+ return makeServer(serverOptions);
1258
+ });
1259
+ }
1260
+ return wsServerPromise;
1223
1261
  };
1224
- const wsServer = makeServer(serverOptions);
1225
1262
  return (socket) => Effect.gen(function* () {
1263
+ const wsServer = yield* Effect.tryPromise({
1264
+ try: () => getOrCreateServer(),
1265
+ catch: (error) => error
1266
+ });
1226
1267
  const runtime = yield* Effect.provide(Effect.runtime(), layer);
1227
1268
  const extra = {
1228
1269
  socket,