@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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-gql/core",
3
- "version": "1.0.0",
3
+ "version": "1.1.1",
4
4
  "description": "Core GraphQL framework for Effect-TS - schema building, type mapping, and execution",
5
5
  "repository": {
6
6
  "url": "https://github.com/nrf110/effect-gql"
@@ -237,6 +237,7 @@ interface FieldRegistration<Args = any, A = any, E = any, R = any> {
237
237
  interface TypeRegistration {
238
238
  name: string;
239
239
  schema: S.Schema<any, any, any>;
240
+ description?: string;
240
241
  implements?: readonly string[];
241
242
  directives?: readonly DirectiveApplication[];
242
243
  /**
@@ -773,6 +774,7 @@ declare class GraphQLSchemaBuilder<R = never> implements Pipeable.Pipeable {
773
774
  objectType<A, R2 = never>(config: {
774
775
  name?: string;
775
776
  schema: S.Schema<A, any, any>;
777
+ description?: string;
776
778
  implements?: readonly string[];
777
779
  directives?: readonly DirectiveApplication[];
778
780
  /**
@@ -237,6 +237,7 @@ interface FieldRegistration<Args = any, A = any, E = any, R = any> {
237
237
  interface TypeRegistration {
238
238
  name: string;
239
239
  schema: S.Schema<any, any, any>;
240
+ description?: string;
240
241
  implements?: readonly string[];
241
242
  directives?: readonly DirectiveApplication[];
242
243
  /**
@@ -773,6 +774,7 @@ declare class GraphQLSchemaBuilder<R = never> implements Pipeable.Pipeable {
773
774
  objectType<A, R2 = never>(config: {
774
775
  name?: string;
775
776
  schema: S.Schema<A, any, any>;
777
+ description?: string;
776
778
  implements?: readonly string[];
777
779
  directives?: readonly DirectiveApplication[];
778
780
  /**
package/server/index.cjs CHANGED
@@ -3,7 +3,6 @@
3
3
  var effect = require('effect');
4
4
  var platform = require('@effect/platform');
5
5
  var graphql = require('graphql');
6
- var graphqlWs = require('graphql-ws');
7
6
 
8
7
  // src/server/config.ts
9
8
  var defaultConfig = {
@@ -98,7 +97,7 @@ var GraphQLRouterConfigFromEnv = effect.Config.all({
98
97
  );
99
98
 
100
99
  // src/server/graphiql.ts
101
- var graphiqlHtml = (endpoint) => `<!DOCTYPE html>
100
+ var graphiqlHtml = (endpoint, subscriptionEndpoint) => `<!DOCTYPE html>
102
101
  <html lang="en">
103
102
  <head>
104
103
  <meta charset="utf-8" />
@@ -126,6 +125,7 @@ var graphiqlHtml = (endpoint) => `<!DOCTYPE html>
126
125
  <script>
127
126
  const fetcher = GraphiQL.createFetcher({
128
127
  url: ${JSON.stringify(endpoint)},
128
+ subscriptionUrl: ${JSON.stringify(subscriptionEndpoint ?? endpoint)},
129
129
  });
130
130
  ReactDOM.createRoot(document.getElementById('graphiql')).render(
131
131
  React.createElement(GraphiQL, { fetcher })
@@ -850,19 +850,32 @@ var defaultErrorHandler = (cause) => (process.env.NODE_ENV !== "production" ? ef
850
850
  ).pipe(effect.Effect.orDie)
851
851
  )
852
852
  );
853
- var parseGraphQLQuery = (query, extensionsService) => effect.Effect.gen(function* () {
853
+ var GraphQLRequestBodySchema = effect.Schema.Struct({
854
+ query: effect.Schema.String,
855
+ variables: effect.Schema.optionalWith(effect.Schema.Record({ key: effect.Schema.String, value: effect.Schema.Unknown }), {
856
+ as: "Option"
857
+ }),
858
+ operationName: effect.Schema.optionalWith(effect.Schema.String, { as: "Option" })
859
+ });
860
+ var decodeRequestBody = platform.HttpIncomingMessage.schemaBodyJson(GraphQLRequestBodySchema);
861
+ var parseGraphQLQuery = (query, extensionsService) => {
854
862
  try {
855
863
  const document = graphql.parse(query);
856
- return { ok: true, document };
864
+ return effect.Effect.succeed({ ok: true, document });
857
865
  } catch (parseError) {
858
- const extensionData = yield* extensionsService.get();
859
- const response = yield* platform.HttpServerResponse.json({
860
- errors: [{ message: String(parseError) }],
861
- extensions: Object.keys(extensionData).length > 0 ? extensionData : void 0
862
- }).pipe(effect.Effect.orDie);
863
- return { ok: false, response };
866
+ return extensionsService.get().pipe(
867
+ effect.Effect.flatMap(
868
+ (extensionData) => platform.HttpServerResponse.json({
869
+ errors: [{ message: String(parseError) }],
870
+ extensions: Object.keys(extensionData).length > 0 ? extensionData : void 0
871
+ }).pipe(
872
+ effect.Effect.orDie,
873
+ effect.Effect.map((response) => ({ ok: false, response }))
874
+ )
875
+ )
876
+ );
864
877
  }
865
- });
878
+ };
866
879
  var runComplexityValidation = (body, schema, fieldComplexities, complexityConfig) => {
867
880
  if (!complexityConfig) {
868
881
  return effect.Effect.void;
@@ -882,8 +895,9 @@ var runComplexityValidation = (body, schema, fieldComplexities, complexityConfig
882
895
  )
883
896
  );
884
897
  };
885
- var executeGraphQLQuery = (schema, document, variables, operationName, runtime) => effect.Effect.gen(function* () {
886
- const executeResult = yield* effect.Effect.try({
898
+ var isPromiseLike = (value) => value !== null && typeof value === "object" && "then" in value && typeof value.then === "function";
899
+ var executeGraphQLQuery = (schema, document, variables, operationName, runtime) => {
900
+ const tryExecute = effect.Effect.try({
887
901
  try: () => graphql.execute({
888
902
  schema,
889
903
  document,
@@ -893,33 +907,34 @@ var executeGraphQLQuery = (schema, document, variables, operationName, runtime)
893
907
  }),
894
908
  catch: (error) => new Error(String(error))
895
909
  });
896
- if (executeResult && typeof executeResult === "object" && "then" in executeResult) {
897
- return yield* effect.Effect.promise(
898
- () => executeResult
899
- );
900
- }
901
- return executeResult;
902
- });
903
- var computeCacheControlHeader = (document, operationName, schema, cacheHints, cacheControlConfig) => effect.Effect.gen(function* () {
910
+ return tryExecute.pipe(
911
+ effect.Effect.flatMap((executeResult) => {
912
+ if (isPromiseLike(executeResult)) {
913
+ return effect.Effect.promise(() => executeResult);
914
+ }
915
+ return effect.Effect.succeed(executeResult);
916
+ })
917
+ );
918
+ };
919
+ var computeCacheControlHeader = (document, operationName, schema, cacheHints, cacheControlConfig) => {
904
920
  if (cacheControlConfig?.enabled === false || cacheControlConfig?.calculateHttpHeaders === false) {
905
- return void 0;
921
+ return effect.Effect.succeed(void 0);
906
922
  }
907
923
  const operations = document.definitions.filter(
908
924
  (d) => d.kind === graphql.Kind.OPERATION_DEFINITION
909
925
  );
910
926
  const operation = operationName ? operations.find((o) => o.name?.value === operationName) : operations[0];
911
927
  if (!operation || operation.operation === "mutation") {
912
- return void 0;
928
+ return effect.Effect.succeed(void 0);
913
929
  }
914
- const cachePolicy = yield* computeCachePolicy({
930
+ return computeCachePolicy({
915
931
  document,
916
932
  operation,
917
933
  schema,
918
934
  cacheHints,
919
935
  config: cacheControlConfig ?? {}
920
- });
921
- return toCacheControlHeader(cachePolicy);
922
- });
936
+ }).pipe(effect.Effect.map(toCacheControlHeader));
937
+ };
923
938
  var buildGraphQLResponse = (result, extensionData, cacheControlHeader) => {
924
939
  const finalResult = Object.keys(extensionData).length > 0 ? {
925
940
  ...result,
@@ -957,7 +972,12 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
957
972
  const extensionsService = yield* makeExtensionsService();
958
973
  const runtime = yield* effect.Effect.runtime();
959
974
  const request = yield* platform.HttpServerRequest.HttpServerRequest;
960
- const body = yield* request.json;
975
+ const parsedBody = yield* decodeRequestBody(request);
976
+ const body = {
977
+ query: parsedBody.query,
978
+ variables: parsedBody.variables._tag === "Some" ? parsedBody.variables.value : void 0,
979
+ operationName: parsedBody.operationName._tag === "Some" ? parsedBody.operationName.value : void 0
980
+ };
961
981
  const parseResult = yield* parseGraphQLQuery(body.query, extensionsService);
962
982
  if (!parseResult.ok) {
963
983
  return parseResult.response;
@@ -966,7 +986,7 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
966
986
  yield* runParseHooks(extensions, body.query, document).pipe(
967
987
  effect.Effect.provideService(ExtensionsService, extensionsService)
968
988
  );
969
- const validationRules = resolvedConfig.introspection ? void 0 : [...graphql.specifiedRules, graphql.NoSchemaIntrospectionCustomRule];
989
+ const validationRules = resolvedConfig.introspection ? void 0 : graphql.specifiedRules.concat(graphql.NoSchemaIntrospectionCustomRule);
970
990
  const validationErrors = graphql.validate(schema, document, validationRules);
971
991
  yield* runValidateHooks(extensions, document, validationErrors).pipe(
972
992
  effect.Effect.provideService(ExtensionsService, extensionsService)
@@ -1027,9 +1047,12 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
1027
1047
  platform.HttpRouter.post(resolvedConfig.path, graphqlHandler)
1028
1048
  );
1029
1049
  if (resolvedConfig.graphiql) {
1030
- const { path, endpoint } = resolvedConfig.graphiql;
1050
+ const { path, endpoint, subscriptionEndpoint } = resolvedConfig.graphiql;
1031
1051
  router = router.pipe(
1032
- platform.HttpRouter.get(path, platform.HttpServerResponse.html(graphiqlHtml(endpoint)))
1052
+ platform.HttpRouter.get(
1053
+ path,
1054
+ platform.HttpServerResponse.html(graphiqlHtml(endpoint, subscriptionEndpoint))
1055
+ )
1033
1056
  );
1034
1057
  }
1035
1058
  return router;
@@ -1204,27 +1227,45 @@ var runConnectionLifecycle = (socket, wsServer, extra) => effect.Effect.gen(func
1204
1227
  effect.Effect.catchAllCause(() => effect.Effect.void),
1205
1228
  effect.Effect.scoped
1206
1229
  );
1230
+ var importGraphqlWs = effect.Effect.tryPromise({
1231
+ try: () => import('graphql-ws'),
1232
+ catch: () => new Error(
1233
+ "graphql-ws is required for WebSocket subscriptions. Install it with: npm install graphql-ws"
1234
+ )
1235
+ });
1207
1236
  var makeGraphQLWSHandler = (schema, layer, options) => {
1208
1237
  const complexityConfig = options?.complexity;
1209
1238
  const fieldComplexities = options?.fieldComplexities ?? /* @__PURE__ */ new Map();
1210
- const serverOptions = {
1211
- schema,
1212
- context: async (ctx) => {
1213
- const extra = ctx.extra;
1214
- return {
1215
- runtime: extra.runtime,
1216
- ...extra.connectionParams
1217
- };
1218
- },
1219
- subscribe: async (args) => graphql.subscribe(args),
1220
- onConnect: makeOnConnectHandler(options),
1221
- onDisconnect: makeOnDisconnectHandler(options),
1222
- onSubscribe: makeOnSubscribeHandler(options, schema, complexityConfig, fieldComplexities),
1223
- onComplete: makeOnCompleteHandler(options),
1224
- onError: makeOnErrorHandler(options)
1239
+ let wsServerPromise = null;
1240
+ const getOrCreateServer = async () => {
1241
+ if (!wsServerPromise) {
1242
+ wsServerPromise = effect.Effect.runPromise(importGraphqlWs).then(({ makeServer }) => {
1243
+ const serverOptions = {
1244
+ schema,
1245
+ context: async (ctx) => {
1246
+ const extra = ctx.extra;
1247
+ return {
1248
+ runtime: extra.runtime,
1249
+ ...extra.connectionParams
1250
+ };
1251
+ },
1252
+ subscribe: async (args) => graphql.subscribe(args),
1253
+ onConnect: makeOnConnectHandler(options),
1254
+ onDisconnect: makeOnDisconnectHandler(options),
1255
+ onSubscribe: makeOnSubscribeHandler(options, schema, complexityConfig, fieldComplexities),
1256
+ onComplete: makeOnCompleteHandler(options),
1257
+ onError: makeOnErrorHandler(options)
1258
+ };
1259
+ return makeServer(serverOptions);
1260
+ });
1261
+ }
1262
+ return wsServerPromise;
1225
1263
  };
1226
- const wsServer = graphqlWs.makeServer(serverOptions);
1227
1264
  return (socket) => effect.Effect.gen(function* () {
1265
+ const wsServer = yield* effect.Effect.tryPromise({
1266
+ try: () => getOrCreateServer(),
1267
+ catch: (error) => error
1268
+ });
1228
1269
  const runtime = yield* effect.Effect.provide(effect.Effect.runtime(), layer);
1229
1270
  const extra = {
1230
1271
  socket,