@suss/contract-graphql 0.24.4 → 0.26.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/dist/index.cjs CHANGED
@@ -31,6 +31,8 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  SCALAR_SHAPES: () => SCALAR_SHAPES,
34
+ describesOperations: () => describesOperations,
35
+ describesTypes: () => describesTypes,
34
36
  graphqlDocumentFilesToSummaries: () => graphqlDocumentFilesToSummaries,
35
37
  graphqlDocumentsPathToSummaries: () => graphqlDocumentsPathToSummaries,
36
38
  graphqlDocumentsToSummaries: () => graphqlDocumentsToSummaries,
@@ -65,6 +67,51 @@ function scalarOrRef(node) {
65
67
  const name = node.name.value;
66
68
  return SCALAR_SHAPES[name] ?? { type: "ref", name };
67
69
  }
70
+ function typeDefinitionsIn(doc) {
71
+ const definitions = {};
72
+ for (const node of doc.definitions) {
73
+ if (node.kind !== import_graphql.Kind.OBJECT_TYPE_DEFINITION && node.kind !== import_graphql.Kind.OBJECT_TYPE_EXTENSION && node.kind !== import_graphql.Kind.INTERFACE_TYPE_DEFINITION && node.kind !== import_graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION) {
74
+ continue;
75
+ }
76
+ const properties = {
77
+ ...definitions[node.name.value]?.type === "record" ? definitions[node.name.value].properties : {}
78
+ };
79
+ for (const field of node.fields ?? []) {
80
+ properties[field.name.value] = typeNodeToShape(field.type);
81
+ }
82
+ definitions[node.name.value] = { type: "record", properties };
83
+ }
84
+ return definitions;
85
+ }
86
+ var SCHEMA_KINDS = /* @__PURE__ */ new Set([
87
+ import_graphql.Kind.SCHEMA_DEFINITION,
88
+ import_graphql.Kind.SCHEMA_EXTENSION,
89
+ import_graphql.Kind.OBJECT_TYPE_DEFINITION,
90
+ import_graphql.Kind.OBJECT_TYPE_EXTENSION,
91
+ import_graphql.Kind.INTERFACE_TYPE_DEFINITION,
92
+ import_graphql.Kind.INTERFACE_TYPE_EXTENSION,
93
+ import_graphql.Kind.INPUT_OBJECT_TYPE_DEFINITION,
94
+ import_graphql.Kind.INPUT_OBJECT_TYPE_EXTENSION,
95
+ import_graphql.Kind.ENUM_TYPE_DEFINITION,
96
+ import_graphql.Kind.ENUM_TYPE_EXTENSION,
97
+ import_graphql.Kind.UNION_TYPE_DEFINITION,
98
+ import_graphql.Kind.UNION_TYPE_EXTENSION,
99
+ import_graphql.Kind.SCALAR_TYPE_DEFINITION,
100
+ import_graphql.Kind.SCALAR_TYPE_EXTENSION
101
+ ]);
102
+ function describesTypes(text) {
103
+ return definitionKinds(text).some((kind) => SCHEMA_KINDS.has(kind));
104
+ }
105
+ function describesOperations(text) {
106
+ return definitionKinds(text).includes(import_graphql.Kind.OPERATION_DEFINITION);
107
+ }
108
+ function definitionKinds(text) {
109
+ try {
110
+ return (0, import_graphql.parse)(text).definitions.map((definition) => definition.kind);
111
+ } catch {
112
+ return [];
113
+ }
114
+ }
68
115
 
69
116
  // src/documents.ts
70
117
  var import_node_fs = __toESM(require("fs"), 1);
@@ -456,9 +503,10 @@ function graphqlSdlToSummaries(sdl, options = {}) {
456
503
  if (rootFields.length === 0) {
457
504
  return [];
458
505
  }
506
+ const definitions = typeDefinitionsIn(doc);
459
507
  const out = [buildSchemaDocumentSummary(settings)];
460
508
  for (const { rootType, field } of rootFields) {
461
- out.push(buildResolverSummary(rootType, field, settings));
509
+ out.push(buildResolverSummary(rootType, field, settings, definitions));
462
510
  }
463
511
  return out;
464
512
  }
@@ -508,7 +556,7 @@ function collectRootFields(doc) {
508
556
  function isRootType(name) {
509
557
  return ROOT_TYPES.includes(name);
510
558
  }
511
- function buildResolverSummary(rootType, field, settings) {
559
+ function buildResolverSummary(rootType, field, settings, definitions) {
512
560
  const { source, recognition, transport } = settings;
513
561
  const fieldName = field.name.value;
514
562
  const ownerKey = `${rootType}.${fieldName}`;
@@ -531,6 +579,10 @@ function buildResolverSummary(rootType, field, settings) {
531
579
  },
532
580
  inputs: buildInputs(field),
533
581
  transitions: buildTransitions2(ownerKey, field),
582
+ // The record behind each named type this field mentions, so a reader
583
+ // comparing the field against an implementation has structure on
584
+ // both sides rather than a name on one.
585
+ ...Object.keys(definitions).length > 0 ? { definitions } : {},
534
586
  gaps: [],
535
587
  confidence: { source: "derived", level: "high" },
536
588
  // Declared contract: checker pairs against any other source
@@ -628,6 +680,8 @@ function buildTransitions2(ownerKey, field) {
628
680
  // Annotate the CommonJS export names for ESM import in node:
629
681
  0 && (module.exports = {
630
682
  SCALAR_SHAPES,
683
+ describesOperations,
684
+ describesTypes,
631
685
  graphqlDocumentFilesToSummaries,
632
686
  graphqlDocumentsPathToSummaries,
633
687
  graphqlDocumentsToSummaries,
package/dist/index.d.cts CHANGED
@@ -52,6 +52,10 @@ declare function graphqlDocumentsPathToSummaries(specPath: string, options?: Gra
52
52
  * its own spelling can build on this table instead of restating it.
53
53
  */
54
54
  declare const SCALAR_SHAPES: Record<string, TypeShape>;
55
+ /** Whether a document declares types. A document that does is a schema. */
56
+ declare function describesTypes(text: string): boolean;
57
+ /** Whether a document has an operation to read, rather than fragments alone. */
58
+ declare function describesOperations(text: string): boolean;
55
59
 
56
60
  interface GraphqlContractOptions {
57
61
  /**
@@ -95,4 +99,4 @@ declare function graphqlSdlFileToSummaries(filepath: string, options?: GraphqlCo
95
99
  */
96
100
  declare function loadSdlFile(filepath: string): string | null;
97
101
 
98
- export { type GraphqlContractOptions, type GraphqlDocumentsOptions, SCALAR_SHAPES, graphqlDocumentFilesToSummaries, graphqlDocumentsPathToSummaries, graphqlDocumentsToSummaries, graphqlSdlFileToSummaries, graphqlSdlToSummaries, loadSdlFile };
102
+ export { type GraphqlContractOptions, type GraphqlDocumentsOptions, SCALAR_SHAPES, describesOperations, describesTypes, graphqlDocumentFilesToSummaries, graphqlDocumentsPathToSummaries, graphqlDocumentsToSummaries, graphqlSdlFileToSummaries, graphqlSdlToSummaries, loadSdlFile };
package/dist/index.d.ts CHANGED
@@ -52,6 +52,10 @@ declare function graphqlDocumentsPathToSummaries(specPath: string, options?: Gra
52
52
  * its own spelling can build on this table instead of restating it.
53
53
  */
54
54
  declare const SCALAR_SHAPES: Record<string, TypeShape>;
55
+ /** Whether a document declares types. A document that does is a schema. */
56
+ declare function describesTypes(text: string): boolean;
57
+ /** Whether a document has an operation to read, rather than fragments alone. */
58
+ declare function describesOperations(text: string): boolean;
55
59
 
56
60
  interface GraphqlContractOptions {
57
61
  /**
@@ -95,4 +99,4 @@ declare function graphqlSdlFileToSummaries(filepath: string, options?: GraphqlCo
95
99
  */
96
100
  declare function loadSdlFile(filepath: string): string | null;
97
101
 
98
- export { type GraphqlContractOptions, type GraphqlDocumentsOptions, SCALAR_SHAPES, graphqlDocumentFilesToSummaries, graphqlDocumentsPathToSummaries, graphqlDocumentsToSummaries, graphqlSdlFileToSummaries, graphqlSdlToSummaries, loadSdlFile };
102
+ export { type GraphqlContractOptions, type GraphqlDocumentsOptions, SCALAR_SHAPES, describesOperations, describesTypes, graphqlDocumentFilesToSummaries, graphqlDocumentsPathToSummaries, graphqlDocumentsToSummaries, graphqlSdlFileToSummaries, graphqlSdlToSummaries, loadSdlFile };
package/dist/index.js CHANGED
@@ -11,7 +11,10 @@ import {
11
11
  } from "@suss/behavioral-ir";
12
12
 
13
13
  // src/typeShape.ts
14
- import { Kind } from "graphql";
14
+ import {
15
+ parse as graphqlParse,
16
+ Kind
17
+ } from "graphql";
15
18
  var SCALAR_SHAPES = {
16
19
  String: { type: "text" },
17
20
  ID: { type: "text" },
@@ -32,6 +35,51 @@ function scalarOrRef(node) {
32
35
  const name = node.name.value;
33
36
  return SCALAR_SHAPES[name] ?? { type: "ref", name };
34
37
  }
38
+ function typeDefinitionsIn(doc) {
39
+ const definitions = {};
40
+ for (const node of doc.definitions) {
41
+ if (node.kind !== Kind.OBJECT_TYPE_DEFINITION && node.kind !== Kind.OBJECT_TYPE_EXTENSION && node.kind !== Kind.INTERFACE_TYPE_DEFINITION && node.kind !== Kind.INPUT_OBJECT_TYPE_DEFINITION) {
42
+ continue;
43
+ }
44
+ const properties = {
45
+ ...definitions[node.name.value]?.type === "record" ? definitions[node.name.value].properties : {}
46
+ };
47
+ for (const field of node.fields ?? []) {
48
+ properties[field.name.value] = typeNodeToShape(field.type);
49
+ }
50
+ definitions[node.name.value] = { type: "record", properties };
51
+ }
52
+ return definitions;
53
+ }
54
+ var SCHEMA_KINDS = /* @__PURE__ */ new Set([
55
+ Kind.SCHEMA_DEFINITION,
56
+ Kind.SCHEMA_EXTENSION,
57
+ Kind.OBJECT_TYPE_DEFINITION,
58
+ Kind.OBJECT_TYPE_EXTENSION,
59
+ Kind.INTERFACE_TYPE_DEFINITION,
60
+ Kind.INTERFACE_TYPE_EXTENSION,
61
+ Kind.INPUT_OBJECT_TYPE_DEFINITION,
62
+ Kind.INPUT_OBJECT_TYPE_EXTENSION,
63
+ Kind.ENUM_TYPE_DEFINITION,
64
+ Kind.ENUM_TYPE_EXTENSION,
65
+ Kind.UNION_TYPE_DEFINITION,
66
+ Kind.UNION_TYPE_EXTENSION,
67
+ Kind.SCALAR_TYPE_DEFINITION,
68
+ Kind.SCALAR_TYPE_EXTENSION
69
+ ]);
70
+ function describesTypes(text) {
71
+ return definitionKinds(text).some((kind) => SCHEMA_KINDS.has(kind));
72
+ }
73
+ function describesOperations(text) {
74
+ return definitionKinds(text).includes(Kind.OPERATION_DEFINITION);
75
+ }
76
+ function definitionKinds(text) {
77
+ try {
78
+ return graphqlParse(text).definitions.map((definition) => definition.kind);
79
+ } catch {
80
+ return [];
81
+ }
82
+ }
35
83
 
36
84
  // src/documents.ts
37
85
  import fs from "fs";
@@ -430,9 +478,10 @@ function graphqlSdlToSummaries(sdl, options = {}) {
430
478
  if (rootFields.length === 0) {
431
479
  return [];
432
480
  }
481
+ const definitions = typeDefinitionsIn(doc);
433
482
  const out = [buildSchemaDocumentSummary(settings)];
434
483
  for (const { rootType, field } of rootFields) {
435
- out.push(buildResolverSummary(rootType, field, settings));
484
+ out.push(buildResolverSummary(rootType, field, settings, definitions));
436
485
  }
437
486
  return out;
438
487
  }
@@ -482,7 +531,7 @@ function collectRootFields(doc) {
482
531
  function isRootType(name) {
483
532
  return ROOT_TYPES.includes(name);
484
533
  }
485
- function buildResolverSummary(rootType, field, settings) {
534
+ function buildResolverSummary(rootType, field, settings, definitions) {
486
535
  const { source, recognition, transport } = settings;
487
536
  const fieldName = field.name.value;
488
537
  const ownerKey = `${rootType}.${fieldName}`;
@@ -505,6 +554,10 @@ function buildResolverSummary(rootType, field, settings) {
505
554
  },
506
555
  inputs: buildInputs(field),
507
556
  transitions: buildTransitions2(ownerKey, field),
557
+ // The record behind each named type this field mentions, so a reader
558
+ // comparing the field against an implementation has structure on
559
+ // both sides rather than a name on one.
560
+ ...Object.keys(definitions).length > 0 ? { definitions } : {},
508
561
  gaps: [],
509
562
  confidence: { source: "derived", level: "high" },
510
563
  // Declared contract: checker pairs against any other source
@@ -601,6 +654,8 @@ function buildTransitions2(ownerKey, field) {
601
654
  }
602
655
  export {
603
656
  SCALAR_SHAPES,
657
+ describesOperations,
658
+ describesTypes,
604
659
  graphqlDocumentFilesToSummaries,
605
660
  graphqlDocumentsPathToSummaries,
606
661
  graphqlDocumentsToSummaries,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/contract-graphql",
3
- "version": "0.24.4",
3
+ "version": "0.26.0",
4
4
  "type": "module",
5
5
  "description": "Turn a GraphQL schema or a committed operation document into a suss contract and check it against the resolvers and clients on either side.",
6
6
  "keywords": [
@@ -47,7 +47,7 @@
47
47
  "typecheck": "tsc --noEmit"
48
48
  },
49
49
  "dependencies": {
50
- "@suss/behavioral-ir": "0.24.4",
50
+ "@suss/behavioral-ir": "0.26.0",
51
51
  "graphql": "^16.8.0"
52
52
  },
53
53
  "devDependencies": {