@suss/contract-graphql 0.33.1 → 0.34.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/README.md CHANGED
@@ -1,48 +1,48 @@
1
1
  # @suss/contract-graphql
2
2
 
3
- Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and says where the two disagree.
3
+ Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and reports where the two disagree.
4
4
 
5
- Generate suss `BehavioralSummary[]` from GraphQL files a repository already commits. Two readers ship in this package, one for each side of a GraphQL boundary:
5
+ This package builds suss `BehavioralSummary[]` from the GraphQL files a repository already commits. It has two readers, one for each side of a GraphQL boundary:
6
6
 
7
7
  | Reader | Input | Summaries |
8
8
  |--------|-------|-----------|
9
9
  | `suss contract --from graphql` | a plain SDL schema file | one `resolver`-kind summary per root field (the provider side) |
10
10
  | `suss contract --from graphql-documents` | `.graphql` / `.gql` operation documents | one `client`-kind summary per operation (the consumer side) |
11
11
 
12
- Either side pairs against the other, and against summaries extracted from code by the Apollo and NestJS packs. Nothing has to be traced through TypeScript first.
12
+ Each side pairs with the other, and with summaries the Apollo and NestJS packs extract from code. Nothing has to be traced through TypeScript first.
13
13
 
14
14
  ## Reading a schema
15
15
 
16
- `graphqlSdlFileToSummaries` parses the SDL and walks every field on `Query`, `Mutation`, and `Subscription`, including fields added through `extend type Query { ... }`. Each field becomes one summary:
16
+ `graphqlSdlFileToSummaries` parses the SDL and walks every field on `Query`, `Mutation` and `Subscription`, including fields added with `extend type Query { ... }`. Each field becomes one summary:
17
17
 
18
- - `identity.name` is `Type.field`, with a `graphql-resolver` boundary binding that records `transport: "http-graphql"`, `recognition: "graphql"`, and the type and field names. Both defaults are overridable, which is what you want when the same SDL backs several deployments and findings should tell them apart.
18
+ - `identity.name` is `Type.field`, with a `graphql-resolver` boundary binding that records `transport: "http-graphql"`, `recognition: "graphql"`, and the type and field names. You can override both defaults, which helps when the same SDL backs several deployments and the findings should tell them apart.
19
19
  - Inputs come from the field's arguments, each with `role: "args"`.
20
- - Two transitions: a default success returning the field's declared shape, and a `GraphQLError` throw for the `errors[]` path.
21
- - `metadata.graphql.declaredContract` records the return type, the arguments with a `required` flag per argument, and the framework tag. Its provenance is `"derived"`, because the contract and the transitions come from the same field declaration and comparing one against the other would say nothing.
20
+ - Two transitions: a default success that returns the field's declared shape, and a `GraphQLError` throw for the `errors[]` path.
21
+ - `metadata.graphql.declaredContract` records the return type, the arguments with a `required` flag on each, and the framework tag. Its provenance is `"derived"`, because the contract and the transitions come from the same field declaration, and comparing one with the other would find nothing.
22
22
 
23
- One extra `library`-kind summary contains the SDL text itself, under `metadata.graphql.schemaSdl`. Type definitions belong to the schema rather than to any one field, so every resolver points at that document through a source-document label, and the checker follows the label to resolve a consumer's nested selections against a resolver's return type. That summary binds to no boundary, so pairing records it as taking no part.
23
+ One extra `library`-kind summary contains the SDL text itself, under `metadata.graphql.schemaSdl`. Type definitions belong to the schema as a whole, so every resolver points at that document through a source-document label. The checker follows the label to resolve a consumer's nested selections against a resolver's return type. That summary does not bind to a boundary, so pairing records it as taking no part.
24
24
 
25
- Named types convert to `TypeShape` as follows: `String` and `ID` become `text`, `Int` and `Float` become `number`, `Boolean` becomes `boolean`, and any other named type becomes a `ref` with that name. A list becomes an `array`, and a non-null wrapper is dropped because `TypeShape` treats nullability as the absence of null in a union.
25
+ Named types convert to `TypeShape` like this: `String` and `ID` become `text`, `Int` and `Float` become `number`, `Boolean` becomes `boolean`, and any other named type becomes a `ref` with that name. A list becomes an `array`. A non-null wrapper is dropped, because in a `TypeShape` a value is non-null when its union does not include null.
26
26
 
27
27
  ## Reading operation documents
28
28
 
29
- `graphqlDocumentsPathToSummaries` accepts a single file or a directory walked recursively for `.graphql` and `.gql` files (`node_modules` is skipped). Each query, mutation, and subscription definition becomes one summary:
29
+ `graphqlDocumentsPathToSummaries` accepts a single file, or a directory that it searches recursively for `.graphql` and `.gql` files, skipping `node_modules`. Each query, mutation and subscription definition becomes one summary:
30
30
 
31
- - A `graphql-operation` boundary binding with the operation type and, when the operation is named, its name. The default recognition tag is `"graphql-documents"`, so findings distinguish these from operations traced at a call site.
32
- - Inputs from the operation's variable definitions, each with `role: "variable"`, the same role the TypeScript adapter stamps on `$variables` it recovers from a tagged template literal.
33
- - The full document text at `metadata.graphql.document`, the same place the TypeScript adapter puts documents it finds in code, so the checker's GraphQL pairing pass reads both alike.
34
- - A response shape approximated from the selection set. Field names become record properties and leaves become `unknown`, since a document does not declare field types. A field selected twice, once directly and once through a fragment, merges into one property the way a server would merge it.
31
+ - A `graphql-operation` boundary binding with the operation type and, when the operation is named, its name. The default recognition tag is `"graphql-documents"`, so findings can tell these apart from operations found at a call site.
32
+ - Inputs from the operation's variable definitions, each with `role: "variable"`. That is the same role the TypeScript adapter gives the `$variables` it finds in a tagged template literal.
33
+ - The full document text at `metadata.graphql.document`, the same place the TypeScript adapter puts documents it finds in code, so the checker's GraphQL pairing pass reads both the same way.
34
+ - A response shape estimated from the selection set. Field names become record properties and leaves become `unknown`, since a document does not declare field types. A field selected twice, once directly and once through a fragment, merges into one property, the way a server would merge it.
35
35
 
36
- Fragment spreads are resolved against every fragment definition in the read set and inlined into the stored document, so the pairing pass sees the selected fields directly. A spread the reader cannot expand stays in the document as written and becomes a gap on the summary rather than an error: the fragment may be missing from the read set, part of a cycle, or defined in more than one file. In the ambiguous case the first definition in read order wins and the gap says which files competed.
36
+ Fragment spreads are resolved against every fragment definition in the files read, and inlined into the stored document, so the pairing pass sees the selected fields directly. A spread the reader cannot expand stays in the document as written, and becomes a gap on the summary instead of an error. The fragment may be missing from the files read, part of a cycle, or defined in more than one file. When it is defined more than once, the first definition in read order wins, and the gap lists which files had one.
37
37
 
38
- An anonymous operation is named after its path relative to the directory you passed, plus its operation type, so the name is the same on every machine that checks the repo out. Repeated names get a `#2` suffix, because transition ids are built from the name and a repeated id would make two operations look like one.
38
+ An anonymous operation is named after its path relative to the directory you passed, plus its operation type, so the name is the same on every machine that checks out the repo. A repeated name gets a `#2` suffix, because transition ids are built from the name, and a repeated id would make two operations look like one.
39
39
 
40
40
  ## What neither reader does
41
41
 
42
- - **Non-root resolvers.** A schema reader summary is emitted for `Query`, `Mutation`, and `Subscription` fields. A field resolver on `Order.customer` is not a boundary summary today.
42
+ - **Non-root resolvers.** The schema reader only produces summaries for `Query`, `Mutation` and `Subscription` fields. A field resolver on `Order.customer` does not get a boundary summary today.
43
43
  - **Directives, unions and interface resolution, and custom scalars** beyond the `ref` fallback are not modeled.
44
- - **Schema validation.** An SDL file that fails to parse comes back as an empty array rather than an error, and the documents reader skips a file it cannot parse so one bad file does not lose the rest.
45
- - **Cross-checking documents against a schema.** The documents reader never resolves a selected field's type; that pairing happens later in the checker, against the schema document summary.
44
+ - **Schema validation.** An SDL file that fails to parse comes back as an empty array, with no error. The documents reader skips a file it cannot parse, so one bad file does not lose the rest.
45
+ - **Checking documents against a schema.** The documents reader never resolves a selected field's type. The checker does that pairing later, against the schema document summary.
46
46
 
47
47
  ## Worked example
48
48
 
@@ -65,7 +65,7 @@ suss contract --from graphql-documents src/queries -o summaries/operations.json
65
65
  suss check summaries/schema.json summaries/operations.json
66
66
  ```
67
67
 
68
- Or programmatically:
68
+ Or from code:
69
69
 
70
70
  ```ts
71
71
  import {
@@ -79,7 +79,7 @@ const consumer = graphqlDocumentsPathToSummaries("src/queries");
79
79
 
80
80
  ## Where it fits in suss
81
81
 
82
- Depends only on `@suss/behavioral-ir` (for the IR types it produces) and `graphql` (for parsing and printing). It does not extract from source code and is independent of the language adapters. `@suss/contract-appsync` reuses this package's SDL loader and type conversion for AppSync schemas declared in CloudFormation.
82
+ The package depends only on `@suss/behavioral-ir`, for the IR types it produces, and `graphql`, for parsing and printing. It does not extract from source code and does not use the language adapters. `@suss/contract-appsync` reuses this package's SDL loader and type conversion for AppSync schemas declared in CloudFormation.
83
83
 
84
84
  ## More
85
85
 
package/dist/index.cjs CHANGED
@@ -287,8 +287,8 @@ function buildVariableInputs(op) {
287
287
  type: "parameter",
288
288
  name: variable.variable.name.value,
289
289
  position: index,
290
- // Same role the TypeScript adapter stamps on operation-header
291
- // `$variables`, so downstream consumers treat both alike.
290
+ // The TypeScript adapter gives an operation's `$variables` the same
291
+ // role, so the checker treats both alike.
292
292
  role: "variable",
293
293
  shape: typeNodeToShape(variable.type)
294
294
  }));
@@ -335,9 +335,8 @@ function unexpandedSpreadGaps(unexpanded, fragments) {
335
335
  const gaps = [];
336
336
  for (const name of [...unexpanded.missing].sort()) {
337
337
  gaps.push(
338
- // A statement about the reading, not the code: the fragment
339
- // likely exists somewhere outside the files handed to this
340
- // reader.
338
+ // The fragment is probably defined in a file this reader was not
339
+ // given.
341
340
  readingGap(
342
341
  `Fragment spread "...${name}" has no matching fragment definition in the read set; its selections are not part of this summary.`
343
342
  )
@@ -579,17 +578,13 @@ function buildResolverSummary(rootType, field, settings, definitions) {
579
578
  },
580
579
  inputs: buildInputs(field),
581
580
  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.
581
+ // Every type record in the document, so a comparison with the
582
+ // implementation has a structure to compare and not only a type name.
585
583
  ...Object.keys(definitions).length > 0 ? { definitions } : {},
586
584
  gaps: [],
587
585
  confidence: { source: "derived", level: "high" },
588
- // Declared contract: checker pairs against any other source
589
- // declaring a contract for the same gql:Type.field key. Provenance
590
- // is "derived" because both this metadata and the summary's
591
- // transitions come from the same SDL field declaration;
592
- // self-comparison would be tautological.
586
+ // Provenance is "derived" because the transitions come from the same
587
+ // field declaration, so comparing the contract with them finds nothing.
593
588
  metadata: (0, import_behavioral_ir2.withGraphqlMetadata)(
594
589
  (0, import_behavioral_ir2.withSourceDocumentMetadata)(void 0, { label: source }),
595
590
  {
package/dist/index.d.cts CHANGED
@@ -1,101 +1,109 @@
1
1
  import { BehavioralSummary, TypeShape } from '@suss/behavioral-ir';
2
2
 
3
+ /**
4
+ * Reads committed `.graphql` and `.gql` operation documents and writes one
5
+ * client summary per operation, with no call site to trace. The document
6
+ * text goes where the TypeScript adapter puts documents it finds in code,
7
+ * so the checker's GraphQL pairing pass reads both the same way.
8
+ *
9
+ * Fragment spreads are inlined from every file read. The README describes
10
+ * what happens to a spread that cannot be expanded.
11
+ */
12
+
3
13
  interface GraphqlDocumentsOptions {
4
14
  /**
5
15
  * Recognition tag for the operation binding. Defaults to
6
- * `"graphql-documents"` so findings distinguish document-derived
7
- * operations from call-site-traced ones.
16
+ * `"graphql-documents"`, so findings can tell these operations apart
17
+ * from operations found at a call site.
8
18
  */
9
19
  recognition?: string;
10
20
  /**
11
- * Transport to record on the boundary binding. Defaults to
12
- * `"http-graphql"` to match the schema reader's resolver side.
21
+ * Transport recorded on the boundary binding. Defaults to
22
+ * `"http-graphql"`, the same as the schema reader.
13
23
  */
14
24
  transport?: string;
15
25
  /**
16
- * Directory the document paths hang off. An anonymous operation is
17
- * named after its path relative to this root, so the name stays the
18
- * same on every machine that checks the repo out. Without it the
19
- * path is used as given.
26
+ * The directory an anonymous operation's name is relative to, so the
27
+ * name is the same on every machine. Without it the path is used as is.
20
28
  */
21
29
  rootDir?: string;
22
30
  }
23
- /** One parsed source document, tagged with where it came from. */
31
+ /** One document's text and the path it came from. */
24
32
  interface DocumentSource {
25
33
  /** Path recorded on each summary's `location.file`. */
26
34
  path: string;
27
- /** Raw document text. */
28
35
  text: string;
29
36
  }
30
37
  /**
31
- * Convert in-memory operation documents into summaries. Fragments are
32
- * resolved across the whole set, so a fragment-only document
33
- * contributes definitions without producing summaries of its own.
38
+ * Converts documents already in memory into one summary per operation.
39
+ * Fragments resolve across the whole set, so a file of fragments alone
40
+ * contributes definitions but does not produce a summary.
34
41
  */
35
42
  declare function graphqlDocumentsToSummaries(sources: DocumentSource[], options?: GraphqlDocumentsOptions): BehavioralSummary[];
36
43
  /**
37
- * Read `.graphql` / `.gql` files from disk and convert them to
38
- * summaries. Unreadable files are skipped, so one bad path does not
39
- * lose the rest of the set.
44
+ * Reads `.graphql` and `.gql` files and converts them. A file that cannot
45
+ * be read is skipped, so one bad path does not lose the rest.
40
46
  */
41
47
  declare function graphqlDocumentFilesToSummaries(filepaths: string[], options?: GraphqlDocumentsOptions): BehavioralSummary[];
42
48
  /**
43
- * Accept either a single document file or a directory to walk
44
- * recursively for `.graphql` / `.gql` files. Entry point for the
45
- * CLI's `suss contract --from graphql-documents <path>`.
49
+ * Reads one document file, or every `.graphql` and `.gql` file under a
50
+ * directory, skipping `node_modules`. Throws when nothing exists at the
51
+ * path. `suss contract --from graphql-documents <path>` calls this.
46
52
  */
47
53
  declare function graphqlDocumentsPathToSummaries(specPath: string, options?: GraphqlDocumentsOptions): BehavioralSummary[];
48
54
 
49
55
  /**
50
- * The five standard GraphQL scalars, mapped to the shapes they read
51
- * as. Exported so packs whose library exposes the same scalars under
52
- * its own spelling can build on this table instead of restating it.
56
+ * The TypeShape for each of the five standard GraphQL scalars. A pack
57
+ * whose library spells the same scalars its own way can build on this
58
+ * table instead of copying it.
53
59
  */
54
60
  declare const SCALAR_SHAPES: Record<string, TypeShape>;
55
- /** Whether a document declares types. A document that does is a schema. */
61
+ /** True when the document declares a type, which makes it a schema. */
56
62
  declare function describesTypes(text: string): boolean;
57
- /** Whether a document has an operation to read, rather than fragments alone. */
63
+ /** True when the document has at least one operation. A file of fragments has none. */
58
64
  declare function describesOperations(text: string): boolean;
59
65
 
66
+ /**
67
+ * Reads a plain GraphQL SDL file and writes one resolver summary for each
68
+ * field on Query, Mutation and Subscription. @suss/contract-appsync does
69
+ * the same for AppSync schemas declared in CloudFormation. The README
70
+ * describes what each summary contains.
71
+ */
72
+
60
73
  interface GraphqlContractOptions {
61
74
  /**
62
- * Logical source path recorded on each summary's `location.file`.
63
- * Defaults to `"graphql"` so summaries are identifiable even when
64
- * the SDL came from a non-file source (in-memory string).
75
+ * The path recorded on each summary's `location.file`. Defaults to
76
+ * `"graphql"`, for SDL passed in as a string with no file behind it.
65
77
  */
66
78
  source?: string;
67
79
  /**
68
80
  * Recognition tag for the resolver binding. Defaults to `"graphql"`.
69
- * Override when the same SDL is used by multiple deployments
70
- * (`apollo-prod`, `apollo-staging`) and you want findings to
71
- * distinguish them.
81
+ * When several deployments serve the same SDL, give each its own tag
82
+ * (`apollo-prod`, `apollo-staging`) so findings tell them apart.
72
83
  */
73
84
  recognition?: string;
74
85
  /**
75
- * Transport to record on the boundary binding. Defaults to
76
- * `"http-graphql"`: most GraphQL servers run over HTTPS.
86
+ * Transport recorded on the boundary binding. Defaults to
87
+ * `"http-graphql"`, since most GraphQL servers are served over HTTP.
77
88
  */
78
89
  transport?: string;
79
90
  }
80
91
  /**
81
- * Convert an SDL string into resolver-kind summaries. Used directly
82
- * when the caller has the SDL in memory; tests and the file-based
83
- * entry point share this code path.
92
+ * Converts SDL text into one resolver summary per root field, plus one
93
+ * summary for the schema document. Returns an empty array when the SDL
94
+ * does not parse or has no root fields.
84
95
  */
85
96
  declare function graphqlSdlToSummaries(sdl: string, options?: GraphqlContractOptions): BehavioralSummary[];
86
97
  /**
87
- * Read an SDL file from disk and convert it to summaries. Convenience
88
- * wrapper for the CLI's `suss contract --from graphql <file>` path.
98
+ * Reads an SDL file and converts it, recording the file path as the
99
+ * source. `suss contract --from graphql <file>` calls this.
89
100
  */
90
101
  declare function graphqlSdlFileToSummaries(filepath: string, options?: GraphqlContractOptions): BehavioralSummary[];
91
102
  /**
92
- * Read an SDL file from disk and return its text, or `null` when the
93
- * file can't be read. Shared entry point for callers that need the raw
94
- * SDL rather than the derived summaries, e.g. @suss/contract-appsync
95
- * resolving an external schema file referenced by a CloudFormation /
96
- * SAM template's `DefinitionS3Location` / `SchemaUri`. Reading fails
97
- * best-effort (missing file, permission error) so the caller can record
98
- * an unresolved-schema gap instead of throwing.
103
+ * Reads an SDL file and returns its text, or `null` when the file cannot
104
+ * be read. @suss/contract-appsync uses it for the schema file a template
105
+ * points at through `DefinitionS3Location` or `SchemaUri`, and records a
106
+ * gap on `null` so a missing file does not fail the run.
99
107
  */
100
108
  declare function loadSdlFile(filepath: string): string | null;
101
109
 
package/dist/index.d.ts CHANGED
@@ -1,101 +1,109 @@
1
1
  import { BehavioralSummary, TypeShape } from '@suss/behavioral-ir';
2
2
 
3
+ /**
4
+ * Reads committed `.graphql` and `.gql` operation documents and writes one
5
+ * client summary per operation, with no call site to trace. The document
6
+ * text goes where the TypeScript adapter puts documents it finds in code,
7
+ * so the checker's GraphQL pairing pass reads both the same way.
8
+ *
9
+ * Fragment spreads are inlined from every file read. The README describes
10
+ * what happens to a spread that cannot be expanded.
11
+ */
12
+
3
13
  interface GraphqlDocumentsOptions {
4
14
  /**
5
15
  * Recognition tag for the operation binding. Defaults to
6
- * `"graphql-documents"` so findings distinguish document-derived
7
- * operations from call-site-traced ones.
16
+ * `"graphql-documents"`, so findings can tell these operations apart
17
+ * from operations found at a call site.
8
18
  */
9
19
  recognition?: string;
10
20
  /**
11
- * Transport to record on the boundary binding. Defaults to
12
- * `"http-graphql"` to match the schema reader's resolver side.
21
+ * Transport recorded on the boundary binding. Defaults to
22
+ * `"http-graphql"`, the same as the schema reader.
13
23
  */
14
24
  transport?: string;
15
25
  /**
16
- * Directory the document paths hang off. An anonymous operation is
17
- * named after its path relative to this root, so the name stays the
18
- * same on every machine that checks the repo out. Without it the
19
- * path is used as given.
26
+ * The directory an anonymous operation's name is relative to, so the
27
+ * name is the same on every machine. Without it the path is used as is.
20
28
  */
21
29
  rootDir?: string;
22
30
  }
23
- /** One parsed source document, tagged with where it came from. */
31
+ /** One document's text and the path it came from. */
24
32
  interface DocumentSource {
25
33
  /** Path recorded on each summary's `location.file`. */
26
34
  path: string;
27
- /** Raw document text. */
28
35
  text: string;
29
36
  }
30
37
  /**
31
- * Convert in-memory operation documents into summaries. Fragments are
32
- * resolved across the whole set, so a fragment-only document
33
- * contributes definitions without producing summaries of its own.
38
+ * Converts documents already in memory into one summary per operation.
39
+ * Fragments resolve across the whole set, so a file of fragments alone
40
+ * contributes definitions but does not produce a summary.
34
41
  */
35
42
  declare function graphqlDocumentsToSummaries(sources: DocumentSource[], options?: GraphqlDocumentsOptions): BehavioralSummary[];
36
43
  /**
37
- * Read `.graphql` / `.gql` files from disk and convert them to
38
- * summaries. Unreadable files are skipped, so one bad path does not
39
- * lose the rest of the set.
44
+ * Reads `.graphql` and `.gql` files and converts them. A file that cannot
45
+ * be read is skipped, so one bad path does not lose the rest.
40
46
  */
41
47
  declare function graphqlDocumentFilesToSummaries(filepaths: string[], options?: GraphqlDocumentsOptions): BehavioralSummary[];
42
48
  /**
43
- * Accept either a single document file or a directory to walk
44
- * recursively for `.graphql` / `.gql` files. Entry point for the
45
- * CLI's `suss contract --from graphql-documents <path>`.
49
+ * Reads one document file, or every `.graphql` and `.gql` file under a
50
+ * directory, skipping `node_modules`. Throws when nothing exists at the
51
+ * path. `suss contract --from graphql-documents <path>` calls this.
46
52
  */
47
53
  declare function graphqlDocumentsPathToSummaries(specPath: string, options?: GraphqlDocumentsOptions): BehavioralSummary[];
48
54
 
49
55
  /**
50
- * The five standard GraphQL scalars, mapped to the shapes they read
51
- * as. Exported so packs whose library exposes the same scalars under
52
- * its own spelling can build on this table instead of restating it.
56
+ * The TypeShape for each of the five standard GraphQL scalars. A pack
57
+ * whose library spells the same scalars its own way can build on this
58
+ * table instead of copying it.
53
59
  */
54
60
  declare const SCALAR_SHAPES: Record<string, TypeShape>;
55
- /** Whether a document declares types. A document that does is a schema. */
61
+ /** True when the document declares a type, which makes it a schema. */
56
62
  declare function describesTypes(text: string): boolean;
57
- /** Whether a document has an operation to read, rather than fragments alone. */
63
+ /** True when the document has at least one operation. A file of fragments has none. */
58
64
  declare function describesOperations(text: string): boolean;
59
65
 
66
+ /**
67
+ * Reads a plain GraphQL SDL file and writes one resolver summary for each
68
+ * field on Query, Mutation and Subscription. @suss/contract-appsync does
69
+ * the same for AppSync schemas declared in CloudFormation. The README
70
+ * describes what each summary contains.
71
+ */
72
+
60
73
  interface GraphqlContractOptions {
61
74
  /**
62
- * Logical source path recorded on each summary's `location.file`.
63
- * Defaults to `"graphql"` so summaries are identifiable even when
64
- * the SDL came from a non-file source (in-memory string).
75
+ * The path recorded on each summary's `location.file`. Defaults to
76
+ * `"graphql"`, for SDL passed in as a string with no file behind it.
65
77
  */
66
78
  source?: string;
67
79
  /**
68
80
  * Recognition tag for the resolver binding. Defaults to `"graphql"`.
69
- * Override when the same SDL is used by multiple deployments
70
- * (`apollo-prod`, `apollo-staging`) and you want findings to
71
- * distinguish them.
81
+ * When several deployments serve the same SDL, give each its own tag
82
+ * (`apollo-prod`, `apollo-staging`) so findings tell them apart.
72
83
  */
73
84
  recognition?: string;
74
85
  /**
75
- * Transport to record on the boundary binding. Defaults to
76
- * `"http-graphql"`: most GraphQL servers run over HTTPS.
86
+ * Transport recorded on the boundary binding. Defaults to
87
+ * `"http-graphql"`, since most GraphQL servers are served over HTTP.
77
88
  */
78
89
  transport?: string;
79
90
  }
80
91
  /**
81
- * Convert an SDL string into resolver-kind summaries. Used directly
82
- * when the caller has the SDL in memory; tests and the file-based
83
- * entry point share this code path.
92
+ * Converts SDL text into one resolver summary per root field, plus one
93
+ * summary for the schema document. Returns an empty array when the SDL
94
+ * does not parse or has no root fields.
84
95
  */
85
96
  declare function graphqlSdlToSummaries(sdl: string, options?: GraphqlContractOptions): BehavioralSummary[];
86
97
  /**
87
- * Read an SDL file from disk and convert it to summaries. Convenience
88
- * wrapper for the CLI's `suss contract --from graphql <file>` path.
98
+ * Reads an SDL file and converts it, recording the file path as the
99
+ * source. `suss contract --from graphql <file>` calls this.
89
100
  */
90
101
  declare function graphqlSdlFileToSummaries(filepath: string, options?: GraphqlContractOptions): BehavioralSummary[];
91
102
  /**
92
- * Read an SDL file from disk and return its text, or `null` when the
93
- * file can't be read. Shared entry point for callers that need the raw
94
- * SDL rather than the derived summaries, e.g. @suss/contract-appsync
95
- * resolving an external schema file referenced by a CloudFormation /
96
- * SAM template's `DefinitionS3Location` / `SchemaUri`. Reading fails
97
- * best-effort (missing file, permission error) so the caller can record
98
- * an unresolved-schema gap instead of throwing.
103
+ * Reads an SDL file and returns its text, or `null` when the file cannot
104
+ * be read. @suss/contract-appsync uses it for the schema file a template
105
+ * points at through `DefinitionS3Location` or `SchemaUri`, and records a
106
+ * gap on `null` so a missing file does not fail the run.
99
107
  */
100
108
  declare function loadSdlFile(filepath: string): string | null;
101
109
 
package/dist/index.js CHANGED
@@ -262,8 +262,8 @@ function buildVariableInputs(op) {
262
262
  type: "parameter",
263
263
  name: variable.variable.name.value,
264
264
  position: index,
265
- // Same role the TypeScript adapter stamps on operation-header
266
- // `$variables`, so downstream consumers treat both alike.
265
+ // The TypeScript adapter gives an operation's `$variables` the same
266
+ // role, so the checker treats both alike.
267
267
  role: "variable",
268
268
  shape: typeNodeToShape(variable.type)
269
269
  }));
@@ -310,9 +310,8 @@ function unexpandedSpreadGaps(unexpanded, fragments) {
310
310
  const gaps = [];
311
311
  for (const name of [...unexpanded.missing].sort()) {
312
312
  gaps.push(
313
- // A statement about the reading, not the code: the fragment
314
- // likely exists somewhere outside the files handed to this
315
- // reader.
313
+ // The fragment is probably defined in a file this reader was not
314
+ // given.
316
315
  readingGap(
317
316
  `Fragment spread "...${name}" has no matching fragment definition in the read set; its selections are not part of this summary.`
318
317
  )
@@ -554,17 +553,13 @@ function buildResolverSummary(rootType, field, settings, definitions) {
554
553
  },
555
554
  inputs: buildInputs(field),
556
555
  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.
556
+ // Every type record in the document, so a comparison with the
557
+ // implementation has a structure to compare and not only a type name.
560
558
  ...Object.keys(definitions).length > 0 ? { definitions } : {},
561
559
  gaps: [],
562
560
  confidence: { source: "derived", level: "high" },
563
- // Declared contract: checker pairs against any other source
564
- // declaring a contract for the same gql:Type.field key. Provenance
565
- // is "derived" because both this metadata and the summary's
566
- // transitions come from the same SDL field declaration;
567
- // self-comparison would be tautological.
561
+ // Provenance is "derived" because the transitions come from the same
562
+ // field declaration, so comparing the contract with them finds nothing.
568
563
  metadata: withGraphqlMetadata2(
569
564
  withSourceDocumentMetadata(void 0, { label: source }),
570
565
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suss/contract-graphql",
3
- "version": "0.33.1",
3
+ "version": "0.34.1",
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": [
@@ -14,7 +14,7 @@
14
14
  ],
15
15
  "license": "Apache-2.0",
16
16
  "author": "Nimbus Cloud AI LLC",
17
- "homepage": "https://github.com/nimbuscloud-ai/suss/tree/main/packages/contract/graphql#readme",
17
+ "homepage": "https://suss.sh",
18
18
  "repository": {
19
19
  "type": "git",
20
20
  "url": "git+https://github.com/nimbuscloud-ai/suss.git",
@@ -47,7 +47,7 @@
47
47
  "typecheck": "tsc --noEmit"
48
48
  },
49
49
  "dependencies": {
50
- "@suss/behavioral-ir": "0.33.1",
50
+ "@suss/behavioral-ir": "0.34.1",
51
51
  "graphql": "^16.8.0"
52
52
  },
53
53
  "devDependencies": {