@effect-gql/core 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.cjs CHANGED
@@ -837,7 +837,14 @@ var GraphQLSchemaBuilder = class _GraphQLSchemaBuilder {
837
837
  * Register an object type from a schema
838
838
  */
839
839
  objectType(config) {
840
- const { schema, implements: implementsInterfaces, directives, cacheControl, fields } = config;
840
+ const {
841
+ schema,
842
+ description,
843
+ implements: implementsInterfaces,
844
+ directives,
845
+ cacheControl,
846
+ fields
847
+ } = config;
841
848
  const name = config.name ?? getSchemaName(schema);
842
849
  if (!name) {
843
850
  throw new Error(
@@ -845,7 +852,14 @@ var GraphQLSchemaBuilder = class _GraphQLSchemaBuilder {
845
852
  );
846
853
  }
847
854
  const newTypes = new Map(this.state.types);
848
- newTypes.set(name, { name, schema, implements: implementsInterfaces, directives, cacheControl });
855
+ newTypes.set(name, {
856
+ name,
857
+ schema,
858
+ description,
859
+ implements: implementsInterfaces,
860
+ directives,
861
+ cacheControl
862
+ });
849
863
  let newObjectFields = this.state.objectFields;
850
864
  if (fields) {
851
865
  newObjectFields = new Map(this.state.objectFields);
@@ -1214,6 +1228,7 @@ var GraphQLSchemaBuilder = class _GraphQLSchemaBuilder {
1214
1228
  const implementedInterfaces = typeReg.implements?.map((name) => interfaceRegistry.get(name)).filter(Boolean) ?? [];
1215
1229
  const graphqlType = new graphql.GraphQLObjectType({
1216
1230
  name: typeName,
1231
+ description: typeReg.description,
1217
1232
  fields: () => {
1218
1233
  const baseFields = schemaToFields(typeReg.schema, sharedCtx);
1219
1234
  const additionalFields = this.state.objectFields.get(typeName);
@@ -1489,7 +1504,8 @@ var LoaderRegistry = class {
1489
1504
  this.Service,
1490
1505
  effect.Effect.gen(function* () {
1491
1506
  const instances = {};
1492
- for (const [name, def] of Object.entries(self.definitions)) {
1507
+ for (const name of Object.keys(self.definitions)) {
1508
+ const def = self.definitions[name];
1493
1509
  instances[name] = yield* createDataLoader(def);
1494
1510
  }
1495
1511
  return instances;
@@ -1509,6 +1525,9 @@ var LoaderRegistry = class {
1509
1525
  /**
1510
1526
  * Load a single value by key.
1511
1527
  * This is the most common operation in resolvers.
1528
+ *
1529
+ * @internal The internal cast is safe because LoaderInstances<Defs> guarantees
1530
+ * that loaders[name] is a DataLoader with the correct key/value types.
1512
1531
  */
1513
1532
  load(name, key) {
1514
1533
  const self = this;
@@ -1521,6 +1540,9 @@ var LoaderRegistry = class {
1521
1540
  /**
1522
1541
  * Load multiple values by keys.
1523
1542
  * All keys are batched into a single request.
1543
+ *
1544
+ * @internal The internal cast is safe because LoaderInstances<Defs> guarantees
1545
+ * that loaders[name] is a DataLoader with the correct key/value types.
1524
1546
  */
1525
1547
  loadMany(name, keys) {
1526
1548
  const self = this;
@@ -1537,35 +1559,39 @@ var LoaderRegistry = class {
1537
1559
  });
1538
1560
  }
1539
1561
  };
1562
+ function createSingleDataLoader(def, context) {
1563
+ return new DataLoader__default.default(async (keys) => {
1564
+ const items = await effect.Effect.runPromise(def.batch(keys).pipe(effect.Effect.provide(context)));
1565
+ return keys.map((key) => {
1566
+ const item = items.find((i) => def.key(i) === key);
1567
+ if (!item) return new Error(`Not found: ${key}`);
1568
+ return item;
1569
+ });
1570
+ });
1571
+ }
1572
+ function createGroupedDataLoader(def, context) {
1573
+ return new DataLoader__default.default(async (keys) => {
1574
+ const items = await effect.Effect.runPromise(def.batch(keys).pipe(effect.Effect.provide(context)));
1575
+ const map = /* @__PURE__ */ new Map();
1576
+ for (const item of items) {
1577
+ const key = def.groupBy(item);
1578
+ let arr = map.get(key);
1579
+ if (!arr) {
1580
+ arr = [];
1581
+ map.set(key, arr);
1582
+ }
1583
+ arr.push(item);
1584
+ }
1585
+ return keys.map((key) => map.get(key) ?? []);
1586
+ });
1587
+ }
1540
1588
  function createDataLoader(def) {
1541
1589
  return effect.Effect.gen(function* () {
1542
1590
  const context = yield* effect.Effect.context();
1543
1591
  if (def._tag === "single") {
1544
- const loader = new DataLoader__default.default(async (keys) => {
1545
- const items = await effect.Effect.runPromise(def.batch(keys).pipe(effect.Effect.provide(context)));
1546
- return keys.map((key) => {
1547
- const item = items.find((i) => def.key(i) === key);
1548
- if (!item) return new Error(`Not found: ${key}`);
1549
- return item;
1550
- });
1551
- });
1552
- return loader;
1592
+ return createSingleDataLoader(def, context);
1553
1593
  } else {
1554
- const loader = new DataLoader__default.default(async (keys) => {
1555
- const items = await effect.Effect.runPromise(def.batch(keys).pipe(effect.Effect.provide(context)));
1556
- const map = /* @__PURE__ */ new Map();
1557
- for (const item of items) {
1558
- const key = def.groupBy(item);
1559
- let arr = map.get(key);
1560
- if (!arr) {
1561
- arr = [];
1562
- map.set(key, arr);
1563
- }
1564
- arr.push(item);
1565
- }
1566
- return keys.map((key) => map.get(key) ?? []);
1567
- });
1568
- return loader;
1594
+ return createGroupedDataLoader(def, context);
1569
1595
  }
1570
1596
  });
1571
1597
  }
@@ -1789,7 +1815,7 @@ var GraphQLRouterConfigFromEnv = effect.Config.all({
1789
1815
  );
1790
1816
 
1791
1817
  // src/server/graphiql.ts
1792
- var graphiqlHtml = (endpoint) => `<!DOCTYPE html>
1818
+ var graphiqlHtml = (endpoint, subscriptionEndpoint) => `<!DOCTYPE html>
1793
1819
  <html lang="en">
1794
1820
  <head>
1795
1821
  <meta charset="utf-8" />
@@ -1817,6 +1843,7 @@ var graphiqlHtml = (endpoint) => `<!DOCTYPE html>
1817
1843
  <script>
1818
1844
  const fetcher = GraphiQL.createFetcher({
1819
1845
  url: ${JSON.stringify(endpoint)},
1846
+ subscriptionUrl: ${JSON.stringify(subscriptionEndpoint ?? endpoint)},
1820
1847
  });
1821
1848
  ReactDOM.createRoot(document.getElementById('graphiql')).render(
1822
1849
  React.createElement(GraphiQL, { fetcher })
@@ -2492,19 +2519,32 @@ var defaultErrorHandler = (cause) => (process.env.NODE_ENV !== "production" ? ef
2492
2519
  ).pipe(effect.Effect.orDie)
2493
2520
  )
2494
2521
  );
2495
- var parseGraphQLQuery = (query2, extensionsService) => effect.Effect.gen(function* () {
2522
+ var GraphQLRequestBodySchema = effect.Schema.Struct({
2523
+ query: effect.Schema.String,
2524
+ variables: effect.Schema.optionalWith(effect.Schema.Record({ key: effect.Schema.String, value: effect.Schema.Unknown }), {
2525
+ as: "Option"
2526
+ }),
2527
+ operationName: effect.Schema.optionalWith(effect.Schema.String, { as: "Option" })
2528
+ });
2529
+ var decodeRequestBody = platform.HttpIncomingMessage.schemaBodyJson(GraphQLRequestBodySchema);
2530
+ var parseGraphQLQuery = (query2, extensionsService) => {
2496
2531
  try {
2497
2532
  const document = graphql.parse(query2);
2498
- return { ok: true, document };
2533
+ return effect.Effect.succeed({ ok: true, document });
2499
2534
  } catch (parseError) {
2500
- const extensionData = yield* extensionsService.get();
2501
- const response = yield* platform.HttpServerResponse.json({
2502
- errors: [{ message: String(parseError) }],
2503
- extensions: Object.keys(extensionData).length > 0 ? extensionData : void 0
2504
- }).pipe(effect.Effect.orDie);
2505
- return { ok: false, response };
2535
+ return extensionsService.get().pipe(
2536
+ effect.Effect.flatMap(
2537
+ (extensionData) => platform.HttpServerResponse.json({
2538
+ errors: [{ message: String(parseError) }],
2539
+ extensions: Object.keys(extensionData).length > 0 ? extensionData : void 0
2540
+ }).pipe(
2541
+ effect.Effect.orDie,
2542
+ effect.Effect.map((response) => ({ ok: false, response }))
2543
+ )
2544
+ )
2545
+ );
2506
2546
  }
2507
- });
2547
+ };
2508
2548
  var runComplexityValidation = (body, schema, fieldComplexities, complexityConfig) => {
2509
2549
  if (!complexityConfig) {
2510
2550
  return effect.Effect.void;
@@ -2524,8 +2564,9 @@ var runComplexityValidation = (body, schema, fieldComplexities, complexityConfig
2524
2564
  )
2525
2565
  );
2526
2566
  };
2527
- var executeGraphQLQuery = (schema, document, variables, operationName, runtime) => effect.Effect.gen(function* () {
2528
- const executeResult = yield* effect.Effect.try({
2567
+ var isPromiseLike = (value) => value !== null && typeof value === "object" && "then" in value && typeof value.then === "function";
2568
+ var executeGraphQLQuery = (schema, document, variables, operationName, runtime) => {
2569
+ const tryExecute = effect.Effect.try({
2529
2570
  try: () => graphql.execute({
2530
2571
  schema,
2531
2572
  document,
@@ -2535,33 +2576,34 @@ var executeGraphQLQuery = (schema, document, variables, operationName, runtime)
2535
2576
  }),
2536
2577
  catch: (error) => new Error(String(error))
2537
2578
  });
2538
- if (executeResult && typeof executeResult === "object" && "then" in executeResult) {
2539
- return yield* effect.Effect.promise(
2540
- () => executeResult
2541
- );
2542
- }
2543
- return executeResult;
2544
- });
2545
- var computeCacheControlHeader = (document, operationName, schema, cacheHints, cacheControlConfig) => effect.Effect.gen(function* () {
2579
+ return tryExecute.pipe(
2580
+ effect.Effect.flatMap((executeResult) => {
2581
+ if (isPromiseLike(executeResult)) {
2582
+ return effect.Effect.promise(() => executeResult);
2583
+ }
2584
+ return effect.Effect.succeed(executeResult);
2585
+ })
2586
+ );
2587
+ };
2588
+ var computeCacheControlHeader = (document, operationName, schema, cacheHints, cacheControlConfig) => {
2546
2589
  if (cacheControlConfig?.enabled === false || cacheControlConfig?.calculateHttpHeaders === false) {
2547
- return void 0;
2590
+ return effect.Effect.succeed(void 0);
2548
2591
  }
2549
2592
  const operations = document.definitions.filter(
2550
2593
  (d) => d.kind === graphql.Kind.OPERATION_DEFINITION
2551
2594
  );
2552
2595
  const operation = operationName ? operations.find((o) => o.name?.value === operationName) : operations[0];
2553
2596
  if (!operation || operation.operation === "mutation") {
2554
- return void 0;
2597
+ return effect.Effect.succeed(void 0);
2555
2598
  }
2556
- const cachePolicy = yield* computeCachePolicy({
2599
+ return computeCachePolicy({
2557
2600
  document,
2558
2601
  operation,
2559
2602
  schema,
2560
2603
  cacheHints,
2561
2604
  config: cacheControlConfig ?? {}
2562
- });
2563
- return toCacheControlHeader(cachePolicy);
2564
- });
2605
+ }).pipe(effect.Effect.map(toCacheControlHeader));
2606
+ };
2565
2607
  var buildGraphQLResponse = (result, extensionData, cacheControlHeader) => {
2566
2608
  const finalResult = Object.keys(extensionData).length > 0 ? {
2567
2609
  ...result,
@@ -2599,7 +2641,12 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
2599
2641
  const extensionsService = yield* makeExtensionsService();
2600
2642
  const runtime = yield* effect.Effect.runtime();
2601
2643
  const request = yield* platform.HttpServerRequest.HttpServerRequest;
2602
- const body = yield* request.json;
2644
+ const parsedBody = yield* decodeRequestBody(request);
2645
+ const body = {
2646
+ query: parsedBody.query,
2647
+ variables: parsedBody.variables._tag === "Some" ? parsedBody.variables.value : void 0,
2648
+ operationName: parsedBody.operationName._tag === "Some" ? parsedBody.operationName.value : void 0
2649
+ };
2603
2650
  const parseResult = yield* parseGraphQLQuery(body.query, extensionsService);
2604
2651
  if (!parseResult.ok) {
2605
2652
  return parseResult.response;
@@ -2608,7 +2655,7 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
2608
2655
  yield* runParseHooks(extensions, body.query, document).pipe(
2609
2656
  effect.Effect.provideService(ExtensionsService, extensionsService)
2610
2657
  );
2611
- const validationRules = resolvedConfig.introspection ? void 0 : [...graphql.specifiedRules, graphql.NoSchemaIntrospectionCustomRule];
2658
+ const validationRules = resolvedConfig.introspection ? void 0 : graphql.specifiedRules.concat(graphql.NoSchemaIntrospectionCustomRule);
2612
2659
  const validationErrors = graphql.validate(schema, document, validationRules);
2613
2660
  yield* runValidateHooks(extensions, document, validationErrors).pipe(
2614
2661
  effect.Effect.provideService(ExtensionsService, extensionsService)
@@ -2669,9 +2716,12 @@ var makeGraphQLRouter = (schema, layer, options = {}) => {
2669
2716
  platform.HttpRouter.post(resolvedConfig.path, graphqlHandler)
2670
2717
  );
2671
2718
  if (resolvedConfig.graphiql) {
2672
- const { path, endpoint } = resolvedConfig.graphiql;
2719
+ const { path, endpoint, subscriptionEndpoint } = resolvedConfig.graphiql;
2673
2720
  router = router.pipe(
2674
- platform.HttpRouter.get(path, platform.HttpServerResponse.html(graphiqlHtml(endpoint)))
2721
+ platform.HttpRouter.get(
2722
+ path,
2723
+ platform.HttpServerResponse.html(graphiqlHtml(endpoint, subscriptionEndpoint))
2724
+ )
2675
2725
  );
2676
2726
  }
2677
2727
  return router;