@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 +56 -2
- package/dist/index.d.cts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +58 -3
- package/package.json +2 -2
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 {
|
|
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.
|
|
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.
|
|
50
|
+
"@suss/behavioral-ir": "0.26.0",
|
|
51
51
|
"graphql": "^16.8.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|